Fixing problems

This commit is contained in:
2026-01-21 18:46:00 +01:00
parent 67b68f1c8a
commit d32a86fea8
5 changed files with 77 additions and 52 deletions

View File

@@ -40,10 +40,10 @@
editMode = modes[(idx + 1) % modes.length]; editMode = modes[(idx + 1) % modes.length];
} }
let htmlContainer: HTMLDivElement; let htmlContainer = $state<HTMLDivElement>(undefined!);
let cssContainer: HTMLDivElement; let cssContainer = $state<HTMLDivElement>(undefined!);
let sharedContainer: HTMLDivElement; let sharedContainer = $state<HTMLDivElement>(undefined!);
let appContainer: HTMLDivElement; let appContainer = $state<HTMLDivElement>(undefined!);
let htmlEditor: EditorView | null = null; let htmlEditor: EditorView | null = null;
let cssEditor: EditorView | null = null; let cssEditor: EditorView | null = null;

View File

@@ -1,24 +1,24 @@
<script lang="ts"> <script lang="ts">
import { Square, Type, Image, Music, Video, Globe } from 'lucide-svelte'; import { Square, Type, Image, Music, Video, Globe } from 'lucide-svelte';
import { state } from './state.svelte'; import { state as appState } from './state.svelte';
let locked = $derived(state.locked); let locked = $derived(appState.locked);
let imageInput: HTMLInputElement; let imageInput = $state<HTMLInputElement>(undefined!);
let soundInput: HTMLInputElement; let soundInput = $state<HTMLInputElement>(undefined!);
let videoInput: HTMLInputElement; let videoInput = $state<HTMLInputElement>(undefined!);
function getSpawnPosition() { function getSpawnPosition() {
return { return {
x: state.snap(-state.viewport.x / state.viewport.zoom + 400), x: appState.snap(-appState.viewport.x / appState.viewport.zoom + 400),
y: state.snap(-state.viewport.y / state.viewport.zoom + 300) y: appState.snap(-appState.viewport.y / appState.viewport.zoom + 300)
}; };
} }
function addTile() { function addTile() {
const id = crypto.randomUUID(); const id = crypto.randomUUID();
const pos = getSpawnPosition(); const pos = getSpawnPosition();
state.addItem({ appState.addItem({
id, id,
html: '', html: '',
css: '', css: '',
@@ -27,15 +27,15 @@
width: 200, width: 200,
height: 200, height: 200,
rotation: 0, rotation: 0,
zIndex: state.maxZIndex + 1 zIndex: appState.maxZIndex + 1
}); });
state.select(id); appState.select(id);
} }
function addText() { function addText() {
const id = crypto.randomUUID(); const id = crypto.randomUUID();
const pos = getSpawnPosition(); const pos = getSpawnPosition();
state.addItem({ appState.addItem({
id, id,
html: '<p>Text</p>', html: '<p>Text</p>',
css: `p { css: `p {
@@ -48,9 +48,9 @@
width: 200, width: 200,
height: 50, height: 50,
rotation: 0, rotation: 0,
zIndex: state.maxZIndex + 1 zIndex: appState.maxZIndex + 1
}); });
state.select(id); appState.select(id);
} }
function addEmbed() { function addEmbed() {
@@ -58,7 +58,7 @@
if (!url) return; if (!url) return;
const id = crypto.randomUUID(); const id = crypto.randomUUID();
const pos = getSpawnPosition(); const pos = getSpawnPosition();
state.addItem({ appState.addItem({
id, id,
html: `<iframe src="${url}" frameborder="0" allowfullscreen></iframe>`, html: `<iframe src="${url}" frameborder="0" allowfullscreen></iframe>`,
css: `iframe { css: `iframe {
@@ -71,9 +71,9 @@
width: 640, width: 640,
height: 480, height: 480,
rotation: 0, rotation: 0,
zIndex: state.maxZIndex + 1 zIndex: appState.maxZIndex + 1
}); });
state.select(id); appState.select(id);
} }
function handleImageChange(e: Event) { function handleImageChange(e: Event) {
@@ -105,8 +105,8 @@
const img = document.createElement('img'); const img = document.createElement('img');
img.onload = () => { img.onload = () => {
state.addAsset(assetId, { blob: file, url, filename: file.name }); appState.addAsset(assetId, { blob: file, url, filename: file.name });
state.addItem({ appState.addItem({
id, id,
assetId, assetId,
html: `<img src="${url}" alt="" />`, html: `<img src="${url}" alt="" />`,
@@ -120,9 +120,9 @@
width: img.naturalWidth, width: img.naturalWidth,
height: img.naturalHeight, height: img.naturalHeight,
rotation: 0, rotation: 0,
zIndex: state.maxZIndex + 1 zIndex: appState.maxZIndex + 1
}); });
state.select(id); appState.select(id);
}; };
img.src = url; img.src = url;
} }
@@ -133,8 +133,8 @@
const url = URL.createObjectURL(file); const url = URL.createObjectURL(file);
const pos = getSpawnPosition(); const pos = getSpawnPosition();
state.addAsset(assetId, { blob: file, url, filename: file.name }); appState.addAsset(assetId, { blob: file, url, filename: file.name });
state.addItem({ appState.addItem({
id, id,
assetId, assetId,
html: `<audio src="${url}" controls></audio>`, html: `<audio src="${url}" controls></audio>`,
@@ -146,9 +146,9 @@
width: 300, width: 300,
height: 54, height: 54,
rotation: 0, rotation: 0,
zIndex: state.maxZIndex + 1 zIndex: appState.maxZIndex + 1
}); });
state.select(id); appState.select(id);
} }
function addVideoItem(file: File) { function addVideoItem(file: File) {
@@ -159,8 +159,8 @@
const video = document.createElement('video'); const video = document.createElement('video');
video.onloadedmetadata = () => { video.onloadedmetadata = () => {
state.addAsset(assetId, { blob: file, url, filename: file.name }); appState.addAsset(assetId, { blob: file, url, filename: file.name });
state.addItem({ appState.addItem({
id, id,
assetId, assetId,
html: `<video src="${url}" controls></video>`, html: `<video src="${url}" controls></video>`,
@@ -173,9 +173,9 @@
width: video.videoWidth || 640, width: video.videoWidth || 640,
height: video.videoHeight || 360, height: video.videoHeight || 360,
rotation: 0, rotation: 0,
zIndex: state.maxZIndex + 1 zIndex: appState.maxZIndex + 1
}); });
state.select(id); appState.select(id);
}; };
video.src = url; video.src = url;
} }

View File

@@ -102,7 +102,7 @@ export async function importBoard(
} }
} }
state.load(manifest, assets); await state.load(manifest, assets);
return { success: true }; return { success: true };
} catch (e) { } catch (e) {
return { return {
@@ -175,8 +175,7 @@ export async function mergeBoard(
}; };
}); });
state.addAssets(newAssets); await state.mergeData(newAssets, newItems);
state.addItems(newItems);
return { success: true, itemCount: newItems.length }; return { success: true, itemCount: newItems.length };
} catch (e) { } catch (e) {

View File

@@ -93,9 +93,11 @@ function createState() {
: 0, : 0,
); );
async function save() { async function saveNow(): Promise<void> {
if (saveTimeout) clearTimeout(saveTimeout); if (saveTimeout) {
saveTimeout = setTimeout(async () => { clearTimeout(saveTimeout);
saveTimeout = null;
}
const storedAssets: Record<string, StoredAsset> = {}; const storedAssets: Record<string, StoredAsset> = {};
for (const [id, asset] of Object.entries(assets)) { for (const [id, asset] of Object.entries(assets)) {
storedAssets[id] = { storedAssets[id] = {
@@ -109,6 +111,12 @@ function createState() {
} catch { } catch {
// localStorage full or unavailable // localStorage full or unavailable
} }
}
function save() {
if (saveTimeout) clearTimeout(saveTimeout);
saveTimeout = setTimeout(() => {
saveNow();
}, 500); }, 500);
} }
@@ -181,6 +189,12 @@ function createState() {
save(); save();
} }
async function mergeData(newAssets: AssetStore, newItems: Item[]): Promise<void> {
Object.assign(assets, newAssets);
manifest.items.push(...newItems);
await saveNow();
}
function getItem(id: string): Item | undefined { function getItem(id: string): Item | undefined {
return manifest.items.find((i) => i.id === id); return manifest.items.find((i) => i.id === id);
} }
@@ -417,8 +431,16 @@ function createState() {
localStorage.removeItem(STORAGE_KEY); localStorage.removeItem(STORAGE_KEY);
} }
function load(newManifest: Manifest, newAssets: AssetStore) { async function load(newManifest: Manifest, newAssets: AssetStore): Promise<void> {
Object.values(assets).forEach((a) => URL.revokeObjectURL(a.url)); Object.values(assets).forEach((a) => URL.revokeObjectURL(a.url));
for (const item of newManifest.items) {
if (item.assetId && newAssets[item.assetId]) {
const newUrl = newAssets[item.assetId].url;
item.html = item.html.replace(/src="[^"]*"/g, `src="${newUrl}"`);
}
}
manifest = newManifest; manifest = newManifest;
assets = newAssets; assets = newAssets;
viewport = { x: 0, y: 0, zoom: 1 }; viewport = { x: 0, y: 0, zoom: 1 };
@@ -426,7 +448,7 @@ function createState() {
editingId = null; editingId = null;
editingGlobal = false; editingGlobal = false;
focusedId = null; focusedId = null;
save(); await saveNow();
} }
restore(); restore();
@@ -468,6 +490,7 @@ function createState() {
removeItem, removeItem,
addAsset, addAsset,
addAssets, addAssets,
mergeData,
getItem, getItem,
select, select,
toggleSelection, toggleSelection,

View File

@@ -4,4 +4,7 @@ import { svelte } from '@sveltejs/vite-plugin-svelte';
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [svelte()], plugins: [svelte()],
build: {
chunkSizeWarningLimit: 1000,
},
}); });