From b4132b54ca67217f706c512c921bf70f86302e31 Mon Sep 17 00:00:00 2001 From: Scott Rhamy Date: Sun, 11 Jan 2026 09:01:17 -0500 Subject: [PATCH 1/2] fix for empty layers toggle showing small circle --- docs/src/routes/docs/components/[name]/+layout.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/routes/docs/components/[name]/+layout.svelte b/docs/src/routes/docs/components/[name]/+layout.svelte index 6d4b92132..cd50a4c2b 100644 --- a/docs/src/routes/docs/components/[name]/+layout.svelte +++ b/docs/src/routes/docs/components/[name]/+layout.svelte @@ -94,7 +94,7 @@ {page.params.example?.replaceAll('-', ' ') ?? metadata.name} - {#if layers} + {#if layers?.length} Date: Thu, 5 Feb 2026 18:01:56 -0500 Subject: [PATCH 2/2] Layerchart Playground - add Save Project Locally functionality. - new imports JSZip + types, file-saver (only imported on use) - add Open in Stackblitz functionality. --- docs/package.json | 6 +- docs/src/routes/docs/playground/+page.svelte | 82 ++++++++++++++++++-- 2 files changed, 80 insertions(+), 8 deletions(-) diff --git a/docs/package.json b/docs/package.json index a229c3f22..2f269648c 100644 --- a/docs/package.json +++ b/docs/package.json @@ -57,7 +57,9 @@ "@types/d3-scale-chromatic": "^3.1.0", "@types/d3-shape": "^3.1.8", "@types/d3-time": "^3.0.4", + "@types/file-saver": "^2.0.7", "@types/hast": "^3.0.4", + "@types/jszip": "^3.4.1", "@types/node": "^25.2.0", "@types/shapefile": "^0.6.4", "@types/sharp": "^0.32.0", @@ -84,6 +86,7 @@ "eslint": "^9.39.2", "eslint-config-prettier": "^10.1.8", "eslint-plugin-svelte": "^3.14.0", + "file-saver": "^2.0.5", "github-slugger": "^2.0.0", "globals": "^17.3.0", "layerchart": "workspace:*", @@ -128,6 +131,7 @@ "@uiw/codemirror-theme-github": "^4.25.4", "@webcontainer/api": "^1.6.1", "ansi_up": "^6.0.6", - "codemirror": "^6.0.2" + "codemirror": "^6.0.2", + "jszip": "^3.10.1" } } diff --git a/docs/src/routes/docs/playground/+page.svelte b/docs/src/routes/docs/playground/+page.svelte index dcdff9a20..f0596a58f 100644 --- a/docs/src/routes/docs/playground/+page.svelte +++ b/docs/src/routes/docs/playground/+page.svelte @@ -176,14 +176,84 @@ await loadFileContent(filepath); } - // Save Project Locally + // Recursively collect all files from the WebContainer as a flat map + async function collectFiles(dir: string = ''): Promise> { + if (!webcontainerInstance) return {}; + + const files: Record = {}; + const entries = await webcontainerInstance.fs.readdir(dir || '/', { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = dir ? `${dir}/${entry.name}` : entry.name; + + // Skip node_modules, .git, and lock files + if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === 'pnpm-lock.yaml') + continue; + + if (entry.isDirectory()) { + Object.assign(files, await collectFiles(fullPath)); + } else { + try { + files[fullPath] = await webcontainerInstance.fs.readFile(fullPath, 'utf-8'); + } catch { + // Skip binary/unreadable files + } + } + } + + return files; + } + + // Save Project Locally (downloads ZIP to Downloads folder) async function saveProject() { - // This needs wired up + if (!webcontainerInstance) return; + + try { + const JSZip = (await import('jszip')).default; + const zip = new JSZip(); + + const now = new Date(); + const timestamp = now.toISOString().replace(/[:.]/g, '-'); + const folderName = `layerchart-${timestamp}`; + const filename = `${folderName}.zip`; + + const rootFolder = zip.folder(folderName)!; + const files = await collectFiles(); + + for (const [path, content] of Object.entries(files)) { + rootFolder.file(path, content); + } + + const { saveAs } = await import('file-saver'); + const blob = await zip.generateAsync({ type: 'blob' }); + saveAs(blob, filename); + } catch (err) { + console.error('Failed to save project:', err); + alert('Failed to save project. Check console for details.'); + } } - // Open Project in StackBlitz async function openInStackBlitz() { - // This needs wired up + if (!webcontainerInstance) return; + + try { + const sdk = (await import('@stackblitz/sdk')).default; + const files = await collectFiles(); + + sdk.openProject( + { + title: 'LayerChart Playground', + files, + template: 'node' + }, + { + newWindow: true, + openFile: 'src/routes/+page.svelte' + } + ); + } catch (err) { + console.error('Failed to open in StackBlitz:', err); + } } // Open Project in REPL @@ -400,12 +470,10 @@