I am using svelte to build a chrome extension which has the ability to upload images and Post that image to instagram. Here is my project directory structure.
├── .git/
├── .gitignore
├── .npmrc
├── README.md
├── package.json
├── src/
│ ├── app.d.ts
│ ├── app.html
│ ├── lib/
│ │ ├── components/
│ │ │ ├── Button.svelte
│ │ │ ├── CaptionEditor.svelte
│ │ │ ├── HashTagsEditor.svelte
│ │ │ ├── ImagePreview.svelte
│ │ │ ├── ImageUploader.svelte
│ │ │ ├── PostsTab.svelte
│ │ │ ├── ScheduledTab.svelte
│ │ │ ├── StatsTab.svelte
│ │ │ └── Tabs.svelte
│ │ └── index.ts
│ └── routes/
│ ├── +layout.ts
│ └── +page.svelte
├── static/
│ ├── favicon.png
│ ├── manifest.json
│ └── popup.css
├── svelte.config.js
├── tsconfig.json
└── vite.config.ts
I am using pnpm
. Interstingly when i drag and drop the image it is able to show preview, also when the chrome is not fully expanded, even in the case when i click on extension and right click inspect that case also it is able to upload the file and show me the preview. The file size is not the issue as for drag and drop i have uploaded a 1KB
and 1.2MB
file This is my manifest.json
file.{
"manifest_version":3,
"name":"instoma",
"version":"0.0.1",
"description": "A simple instagram automation tool",
"action":{
"default_popup":"index.html"
},
"permissions":[
"storage",
"unlimitedStorage"
],
"web_accessible_resources": [
{
"resources": [
"*.png",
"*.jpg",
"*.jpeg"
],
"matches": [
"<all_urls>"
]
}
],
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'"
}
}
This is my popup.css
file.
body {
width: 450px;
height: 600px;
margin: 0;
padding: 0;
overflow: auto;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
/* Add this to ensure the extension doesn't collapse */
html, body {
min-height: 400px;
max-height: 600px;
}
/* Fix for Chrome fullscreen mode */
@media (min-height: 800px) {
body {
height: 600px;
overflow: hidden;
}
}
/* Add these styles to improve stability */
.plugin-container {
height: 100%;
max-height: 580px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.tab-content {
overflow: auto;
max-height: calc(100% - 100px);
flex: 1;
}
/* Add this to prevent memory issues with images */
img {
max-width: 100%;
height: auto;
object-fit: contain;
}
This is my +page.svelte
file.
<svelte:head>
<link rel="stylesheet" href="popup.css"/>
</svelte:head>
<script>
import { onMount } from 'svelte';
import Tabs from '$lib/components/Tabs.svelte';
import PostsTab from '$lib/components/PostsTab.svelte';
import ScheduledTab from '$lib/components/ScheduledTab.svelte';
import StatsTab from '$lib/components/StatsTab.svelte';
let activeTab = $state('POSTS');
let error = $state(null);
const tabs = [
{ id: 'POSTS', label: 'POSTS' },
{ id: 'SCHEDULED', label: 'SCHEDULED' },
{ id: 'STATS', label: 'STATS' }
];
// Add global error handler
onMount(() => {
window.addEventListener('error', (event) => {
console.error('Global error:', event.error);
error = event.error?.message || 'Unknown error occurred';
return false;
});
window.addEventListener('unhandledrejection', (event) => {
console.error('Unhandled promise rejection:', event.reason);
error = event.reason?.message || 'Promise rejection occurred';
return false;
});
});
</script>
<main>
<div class="plugin-container">
{#if error}
<div class="error-container">
<h3>Something went wrong</h3>
<p>{error}</p>
<button on:click={() => error = null}>Dismiss</button>
</div>
{:else}
<Tabs {tabs} bind:activeTab />
<p class="active-tab-display">The active tab is {activeTab}</p>
<div class="tab-content">
{#if activeTab === 'POSTS'}
<PostsTab />
{:else if activeTab === 'SCHEDULED'}
<ScheduledTab />
{:else if activeTab === 'STATS'}
<StatsTab />
{/if}
</div>
{/if}
</div>
</main>
<style>
main {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
padding: 1rem;
max-width: 100%;
margin: 0 auto;
box-sizing: border-box;
height: 100%;
}
.plugin-container {
background-color: #fff;
border-radius: 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
overflow: hidden;
height: 100%;
display: flex;
flex-direction: column;
}
.tab-content {
flex: 1;
overflow: auto;
padding: 1rem;
}
.active-tab-display {
color: peru;
font-size: 1.5rem;
margin: 0.5rem 0;
text-align: center;
background-color: #f0f0f0;
padding: 0.5rem;
border-radius: 8px;
}
.error-container {
padding: 2rem;
text-align: center;
color: #d32f2f;
}
.error-container button {
margin-top: 1rem;
padding: 0.5rem 1rem;
background-color: #f0f0f0;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
This is my ImageUploader.svelte
file.
<script lang="ts">
import { createEventDispatcher } from "svelte";
let isDragging = $state(false);
let fileInput: HTMLInputElement;
const dispatch = createEventDispatcher<{
upload: { url: string };
}>();
function processFile(file: File) {
// Check file type
if (!file.type.match('image.*')) return;
// Check file size (5MB limit)
if (file.size > 5 * 1024 * 1024) {
alert(`File ${file.name} is too large. Maximum size is 5MB.`);
return;
}
// Use a simple approach that's less likely to cause issues
const reader = new FileReader();
reader.onload = (e) => {
if (typeof e.target?.result !== 'string') return;
dispatch("upload", { url: e.target.result });
};
reader.onerror = () => {
console.error("Error reading file");
};
reader.readAsDataURL(file);
}
function handleFileUpload(event: Event) {
try {
const input = event.target as HTMLInputElement;
const files = input.files;
if (!files || files.length === 0) return;
if (files.length > 3) {
alert("You can only upload up to 3 images at a time.");
return;
}
for (const file of files) {
processFile(file);
}
// Reset the input to allow selecting the same file again
input.value = '';
} catch (error) {
console.error("Error in file upload:", error);
}
}
function handleDragOver(event: DragEvent) {
event.preventDefault();
isDragging = true;
}
function handleDragLeave() {
isDragging = false;
}
function handleDrop(event: DragEvent) {
event.preventDefault();
isDragging = false;
try {
const files = event.dataTransfer?.files;
if(!files || files.length === 0) return;
if (files.length > 3) {
alert("You can only upload up to 3 images at a time.");
return;
}
for (const file of files) {
processFile(file);
}
} catch (error) {
console.error("Error in drop handling:", error);
}
}
function handleClick() {
if (fileInput) {
fileInput.click();
}
}
</script>
<div
class="uploader {isDragging ? 'dragging' : ''}"
role="button"
aria-label="Upload images"
on:click={handleClick}
on:dragover={handleDragOver}
on:dragleave={handleDragLeave}
on:drop={handleDrop}
>
<input
type="file"
hidden
bind:this={fileInput}
on:change={handleFileUpload}
accept=".jpg, .jpeg, .png"
multiple
/>
<div class="upload-content">
<svg
xmlns=";
width="36"
height="36"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="17 8 12 3 7 8"></polyline>
<line x1="12" y1="3" x2="12" y2="15"></line>
</svg>
<p>Drag & drop images here or <span>browse</span></p>
<small>Supports JPG, JPEG, PNG (max 3 images)</small>
</div>
</div>
<style>
.uploader {
border: 2px dashed #ccc;
border-radius: 12px;
padding: 2rem;
text-align: center;
cursor: pointer;
transition: all 0.2s ease;
}
.uploader:hover {
border-color: #007bff;
}
.uploader.dragging {
border-color: #007bff;
background-color: rgba(0, 123, 255, 0.05);
transform: scale(1.01);
}
.upload-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
color: #666;
}
span {
color: #007bff;
font-weight: 500;
}
</style>
This is my ImagePreview.svelte
file.
<script lang="ts">
import CaptionEditor from "./CaptionEditor.svelte";
import HashTagsEditor from "./HashTagsEditor.svelte";
import Button from "./Button.svelte";
import { CircleX } from '@lucide/svelte';
import { createEventDispatcher } from 'svelte';
interface PostResult {
success: boolean | null;
message: string;
}
const { image, index } = $props();
const dispatch = createEventDispatcher();
let showCaption = $state(false);
let showHashtags = $state(false);
let isPosting = $state(false);
let postResult = $state<PostResult>({ success: null, message: '' });
async function shareImage() {
try {
isPosting = true;
postResult = { success: null, message: 'Posting to Instagram...' };
const response = await fetch('http://localhost:8188/api/instagram/post', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
imageUrl: image.url,
caption: image.caption,
hashtags: image.hashtags
})
});
const result = await response.json();
if (result.success) {
postResult = { success: true, message: 'Posted successfully to Instagram!' };
} else {
postResult = { success: false, message: `Error: ${result.message}` };
console.error('Error posting to Instagram:', result.message);
console.log("-----");
console.log(postResult);
}
} catch (error) {
console.error('Error sharing image:', error);
postResult = { success: false, message: `Error: ${error.message}` };
console.log("*********");
console.log(postResult);
} finally {
isPosting = false;
// Clear result message after 5 seconds
setTimeout(() => {
postResult = { success: null, message: '' };
}, 60000);
}
}
function handleRemove() {
dispatch('remove', index);
}
</script>
<div class="image-preview">
<div class="image-container">
<img src={image.url} alt="Demo post preview" />
<button class="close-button" on:click|stopPropagation={handleRemove} aria-label="Remove image">
<CircleX size={24} />
</button>
</div>
<div class="content">
<h3>Image {index + 1}</h3>
<div class="actions">
<button on:click={shareImage} class="action-button" disabled={isPosting}>
<svg
xmlns=";
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path>
<polyline points="16 6 12 2 8 6"></polyline>
<line x1="12" y1="2" x2="12" y2="15"></line>
</svg>
</button>
<button
on:click={() => (showCaption = !showCaption)}
class="action-button"
>
<svg
xmlns=";
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 20h9"></path>
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"
></path>
</svg>
</button>
<button
on:click={() => (showHashtags = !showHashtags)}
class="action-button"
>
<svg
xmlns=";
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"
></path>
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"
></path>
</svg>
</button>
</div>
{#if postResult.message}
<div class="post-result {postResult.success ? 'success' : postResult.success === false ? 'error' : 'info'}">
{postResult.message}
</div>
{/if}
{#if showCaption}
<CaptionEditor {image} />
{/if}
{#if showHashtags}
<HashTagsEditor {image} />
{/if}
</div>
</div>
<style>
.image-preview {
background-color: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
display: flex;
flex-direction: column;
padding: 1rem;
}
.image-container {
position: relative;
width: 100%;
padding-top: 100%; /* 1:1 Aspect Ratio */
overflow: hidden;
}
img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.close-button {
position: absolute;
top: 8px;
right: 8px;
background: rgba(255, 255, 255, 0.7);
border: none;
border-radius: 50%;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
padding: 0;
z-index: 10;
}
.close-button:hover {
background: rgba(255, 255, 255, 0.9);
transform: scale(1.1);
}
.close-button:hover :global(svg) {
color: #ff3333;
}
.content {
padding: 1rem;
}
h3 {
margin: 0 0 1rem 0;
font-size: 1.2rem;
color: #262626;
}
.actions {
display: flex;
gap: 0.75rem;
margin-bottom: 1rem;
}
.action-button {
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.2s;
}
.action-button:hover {
background-color: #f0f0f0;
}
.action-button[disabled] {
opacity: 0.5;
cursor: not-allowed;
}
.post-result {
padding: 0.75rem;
border-radius: 8px;
margin-bottom: 1rem;
font-size: 0.9rem;
}
.post-result.success {
background-color: #e3f7e8;
color: #2e7d32;
}
.post-result.error {
background-color: #fdecea;
color: #d32f2f;
}
.post-result.info {
background-color: #e8f4fd;
color: #0277bd;
}
</style>
I have disabled all other extensions.Still not able to figure out the issue. What am i missing here, are the styles causing conflicts or issues ?
I am using svelte to build a chrome extension which has the ability to upload images and Post that image to instagram. Here is my project directory structure.
├── .git/
├── .gitignore
├── .npmrc
├── README.md
├── package.json
├── src/
│ ├── app.d.ts
│ ├── app.html
│ ├── lib/
│ │ ├── components/
│ │ │ ├── Button.svelte
│ │ │ ├── CaptionEditor.svelte
│ │ │ ├── HashTagsEditor.svelte
│ │ │ ├── ImagePreview.svelte
│ │ │ ├── ImageUploader.svelte
│ │ │ ├── PostsTab.svelte
│ │ │ ├── ScheduledTab.svelte
│ │ │ ├── StatsTab.svelte
│ │ │ └── Tabs.svelte
│ │ └── index.ts
│ └── routes/
│ ├── +layout.ts
│ └── +page.svelte
├── static/
│ ├── favicon.png
│ ├── manifest.json
│ └── popup.css
├── svelte.config.js
├── tsconfig.json
└── vite.config.ts
I am using pnpm
. Interstingly when i drag and drop the image it is able to show preview, also when the chrome is not fully expanded, even in the case when i click on extension and right click inspect that case also it is able to upload the file and show me the preview. The file size is not the issue as for drag and drop i have uploaded a 1KB
and 1.2MB
file This is my manifest.json
file.{
"manifest_version":3,
"name":"instoma",
"version":"0.0.1",
"description": "A simple instagram automation tool",
"action":{
"default_popup":"index.html"
},
"permissions":[
"storage",
"unlimitedStorage"
],
"web_accessible_resources": [
{
"resources": [
"*.png",
"*.jpg",
"*.jpeg"
],
"matches": [
"<all_urls>"
]
}
],
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'"
}
}
This is my popup.css
file.
body {
width: 450px;
height: 600px;
margin: 0;
padding: 0;
overflow: auto;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
/* Add this to ensure the extension doesn't collapse */
html, body {
min-height: 400px;
max-height: 600px;
}
/* Fix for Chrome fullscreen mode */
@media (min-height: 800px) {
body {
height: 600px;
overflow: hidden;
}
}
/* Add these styles to improve stability */
.plugin-container {
height: 100%;
max-height: 580px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.tab-content {
overflow: auto;
max-height: calc(100% - 100px);
flex: 1;
}
/* Add this to prevent memory issues with images */
img {
max-width: 100%;
height: auto;
object-fit: contain;
}
This is my +page.svelte
file.
<svelte:head>
<link rel="stylesheet" href="popup.css"/>
</svelte:head>
<script>
import { onMount } from 'svelte';
import Tabs from '$lib/components/Tabs.svelte';
import PostsTab from '$lib/components/PostsTab.svelte';
import ScheduledTab from '$lib/components/ScheduledTab.svelte';
import StatsTab from '$lib/components/StatsTab.svelte';
let activeTab = $state('POSTS');
let error = $state(null);
const tabs = [
{ id: 'POSTS', label: 'POSTS' },
{ id: 'SCHEDULED', label: 'SCHEDULED' },
{ id: 'STATS', label: 'STATS' }
];
// Add global error handler
onMount(() => {
window.addEventListener('error', (event) => {
console.error('Global error:', event.error);
error = event.error?.message || 'Unknown error occurred';
return false;
});
window.addEventListener('unhandledrejection', (event) => {
console.error('Unhandled promise rejection:', event.reason);
error = event.reason?.message || 'Promise rejection occurred';
return false;
});
});
</script>
<main>
<div class="plugin-container">
{#if error}
<div class="error-container">
<h3>Something went wrong</h3>
<p>{error}</p>
<button on:click={() => error = null}>Dismiss</button>
</div>
{:else}
<Tabs {tabs} bind:activeTab />
<p class="active-tab-display">The active tab is {activeTab}</p>
<div class="tab-content">
{#if activeTab === 'POSTS'}
<PostsTab />
{:else if activeTab === 'SCHEDULED'}
<ScheduledTab />
{:else if activeTab === 'STATS'}
<StatsTab />
{/if}
</div>
{/if}
</div>
</main>
<style>
main {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
padding: 1rem;
max-width: 100%;
margin: 0 auto;
box-sizing: border-box;
height: 100%;
}
.plugin-container {
background-color: #fff;
border-radius: 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
overflow: hidden;
height: 100%;
display: flex;
flex-direction: column;
}
.tab-content {
flex: 1;
overflow: auto;
padding: 1rem;
}
.active-tab-display {
color: peru;
font-size: 1.5rem;
margin: 0.5rem 0;
text-align: center;
background-color: #f0f0f0;
padding: 0.5rem;
border-radius: 8px;
}
.error-container {
padding: 2rem;
text-align: center;
color: #d32f2f;
}
.error-container button {
margin-top: 1rem;
padding: 0.5rem 1rem;
background-color: #f0f0f0;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
This is my ImageUploader.svelte
file.
<script lang="ts">
import { createEventDispatcher } from "svelte";
let isDragging = $state(false);
let fileInput: HTMLInputElement;
const dispatch = createEventDispatcher<{
upload: { url: string };
}>();
function processFile(file: File) {
// Check file type
if (!file.type.match('image.*')) return;
// Check file size (5MB limit)
if (file.size > 5 * 1024 * 1024) {
alert(`File ${file.name} is too large. Maximum size is 5MB.`);
return;
}
// Use a simple approach that's less likely to cause issues
const reader = new FileReader();
reader.onload = (e) => {
if (typeof e.target?.result !== 'string') return;
dispatch("upload", { url: e.target.result });
};
reader.onerror = () => {
console.error("Error reading file");
};
reader.readAsDataURL(file);
}
function handleFileUpload(event: Event) {
try {
const input = event.target as HTMLInputElement;
const files = input.files;
if (!files || files.length === 0) return;
if (files.length > 3) {
alert("You can only upload up to 3 images at a time.");
return;
}
for (const file of files) {
processFile(file);
}
// Reset the input to allow selecting the same file again
input.value = '';
} catch (error) {
console.error("Error in file upload:", error);
}
}
function handleDragOver(event: DragEvent) {
event.preventDefault();
isDragging = true;
}
function handleDragLeave() {
isDragging = false;
}
function handleDrop(event: DragEvent) {
event.preventDefault();
isDragging = false;
try {
const files = event.dataTransfer?.files;
if(!files || files.length === 0) return;
if (files.length > 3) {
alert("You can only upload up to 3 images at a time.");
return;
}
for (const file of files) {
processFile(file);
}
} catch (error) {
console.error("Error in drop handling:", error);
}
}
function handleClick() {
if (fileInput) {
fileInput.click();
}
}
</script>
<div
class="uploader {isDragging ? 'dragging' : ''}"
role="button"
aria-label="Upload images"
on:click={handleClick}
on:dragover={handleDragOver}
on:dragleave={handleDragLeave}
on:drop={handleDrop}
>
<input
type="file"
hidden
bind:this={fileInput}
on:change={handleFileUpload}
accept=".jpg, .jpeg, .png"
multiple
/>
<div class="upload-content">
<svg
xmlns="http://www.w3./2000/svg"
width="36"
height="36"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="17 8 12 3 7 8"></polyline>
<line x1="12" y1="3" x2="12" y2="15"></line>
</svg>
<p>Drag & drop images here or <span>browse</span></p>
<small>Supports JPG, JPEG, PNG (max 3 images)</small>
</div>
</div>
<style>
.uploader {
border: 2px dashed #ccc;
border-radius: 12px;
padding: 2rem;
text-align: center;
cursor: pointer;
transition: all 0.2s ease;
}
.uploader:hover {
border-color: #007bff;
}
.uploader.dragging {
border-color: #007bff;
background-color: rgba(0, 123, 255, 0.05);
transform: scale(1.01);
}
.upload-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
color: #666;
}
span {
color: #007bff;
font-weight: 500;
}
</style>
This is my ImagePreview.svelte
file.
<script lang="ts">
import CaptionEditor from "./CaptionEditor.svelte";
import HashTagsEditor from "./HashTagsEditor.svelte";
import Button from "./Button.svelte";
import { CircleX } from '@lucide/svelte';
import { createEventDispatcher } from 'svelte';
interface PostResult {
success: boolean | null;
message: string;
}
const { image, index } = $props();
const dispatch = createEventDispatcher();
let showCaption = $state(false);
let showHashtags = $state(false);
let isPosting = $state(false);
let postResult = $state<PostResult>({ success: null, message: '' });
async function shareImage() {
try {
isPosting = true;
postResult = { success: null, message: 'Posting to Instagram...' };
const response = await fetch('http://localhost:8188/api/instagram/post', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
imageUrl: image.url,
caption: image.caption,
hashtags: image.hashtags
})
});
const result = await response.json();
if (result.success) {
postResult = { success: true, message: 'Posted successfully to Instagram!' };
} else {
postResult = { success: false, message: `Error: ${result.message}` };
console.error('Error posting to Instagram:', result.message);
console.log("-----");
console.log(postResult);
}
} catch (error) {
console.error('Error sharing image:', error);
postResult = { success: false, message: `Error: ${error.message}` };
console.log("*********");
console.log(postResult);
} finally {
isPosting = false;
// Clear result message after 5 seconds
setTimeout(() => {
postResult = { success: null, message: '' };
}, 60000);
}
}
function handleRemove() {
dispatch('remove', index);
}
</script>
<div class="image-preview">
<div class="image-container">
<img src={image.url} alt="Demo post preview" />
<button class="close-button" on:click|stopPropagation={handleRemove} aria-label="Remove image">
<CircleX size={24} />
</button>
</div>
<div class="content">
<h3>Image {index + 1}</h3>
<div class="actions">
<button on:click={shareImage} class="action-button" disabled={isPosting}>
<svg
xmlns="http://www.w3./2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path>
<polyline points="16 6 12 2 8 6"></polyline>
<line x1="12" y1="2" x2="12" y2="15"></line>
</svg>
</button>
<button
on:click={() => (showCaption = !showCaption)}
class="action-button"
>
<svg
xmlns="http://www.w3./2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 20h9"></path>
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"
></path>
</svg>
</button>
<button
on:click={() => (showHashtags = !showHashtags)}
class="action-button"
>
<svg
xmlns="http://www.w3./2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"
></path>
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"
></path>
</svg>
</button>
</div>
{#if postResult.message}
<div class="post-result {postResult.success ? 'success' : postResult.success === false ? 'error' : 'info'}">
{postResult.message}
</div>
{/if}
{#if showCaption}
<CaptionEditor {image} />
{/if}
{#if showHashtags}
<HashTagsEditor {image} />
{/if}
</div>
</div>
<style>
.image-preview {
background-color: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
display: flex;
flex-direction: column;
padding: 1rem;
}
.image-container {
position: relative;
width: 100%;
padding-top: 100%; /* 1:1 Aspect Ratio */
overflow: hidden;
}
img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.close-button {
position: absolute;
top: 8px;
right: 8px;
background: rgba(255, 255, 255, 0.7);
border: none;
border-radius: 50%;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
padding: 0;
z-index: 10;
}
.close-button:hover {
background: rgba(255, 255, 255, 0.9);
transform: scale(1.1);
}
.close-button:hover :global(svg) {
color: #ff3333;
}
.content {
padding: 1rem;
}
h3 {
margin: 0 0 1rem 0;
font-size: 1.2rem;
color: #262626;
}
.actions {
display: flex;
gap: 0.75rem;
margin-bottom: 1rem;
}
.action-button {
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.2s;
}
.action-button:hover {
background-color: #f0f0f0;
}
.action-button[disabled] {
opacity: 0.5;
cursor: not-allowed;
}
.post-result {
padding: 0.75rem;
border-radius: 8px;
margin-bottom: 1rem;
font-size: 0.9rem;
}
.post-result.success {
background-color: #e3f7e8;
color: #2e7d32;
}
.post-result.error {
background-color: #fdecea;
color: #d32f2f;
}
.post-result.info {
background-color: #e8f4fd;
color: #0277bd;
}
</style>
I have disabled all other extensions.Still not able to figure out the issue. What am i missing here, are the styles causing conflicts or issues ?
Share Improve this question asked Mar 12 at 15:28 Danish XavierDanish Xavier 1,1661 gold badge13 silver badges21 bronze badges 1- what exactly does "crashing" mean in that context? – derpirscher Commented Mar 21 at 12:40
1 Answer
Reset to default -1Try running your extension as a standalone tab or sidebar action instead of a popup:
"action": {
"default_popup": "index.html"
},
"sidebar_action": {
"default_page": "index.html"
}
Use a background script to persist images before the popup closes:
"background": {
"service_worker": "background.js"
}
Add logic to save and retrieve the image from chrome.storage.local
.
<script>
import { onMount } from "svelte";
let imageUrl = "";
// Handle file selection
function handleFileUpload(event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = () => {
imageUrl = reader.result;
chrome.storage.local.set({ uploadedImage: imageUrl });
};
reader.readAsDataURL(file);
}
}
// Retrieve image on mount
onMount(() => {
chrome.storage.local.get("uploadedImage", (data) => {
if (data.uploadedImage) {
imageUrl = data.uploadedImage;
}
});
});
</script>
<input type="file" accept="image/*" on:change={handleFileUpload} />
{#if imageUrl}
<img src={imageUrl} alt="Uploaded Preview" style="max-width: 100%;" />
{/if}
Ensure you have storage permission in manifest.json
:
{
"manifest_version": 3,
"name": "Image Upload Extension",
"version": "1.0",
"permissions": ["storage"]
}