diff --git a/docs/src/lib/components/EditWithButton.svelte b/docs/src/lib/components/EditWithButton.svelte
new file mode 100644
index 000000000..d86c4683e
--- /dev/null
+++ b/docs/src/lib/components/EditWithButton.svelte
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
diff --git a/docs/src/lib/components/Example.svelte b/docs/src/lib/components/Example.svelte
index f146bb7ed..59fd78645 100644
--- a/docs/src/lib/components/Example.svelte
+++ b/docs/src/lib/components/Example.svelte
@@ -1,16 +1,8 @@
@@ -209,11 +153,7 @@
style:width={containerWidth ? `${containerWidth}px` : undefined}
style:view-transition-name={viewTransitionName}
>
- {#if isVisible}
-
- {:else}
-
- {/if}
+
{#if canResize}
openInStackBlitz(component, name)}
- >
- Edit
-
+
{/if}
-
-
-
-
{/if}
{:else}
@@ -338,14 +253,3 @@
{/if}
-
-{#if svgUnavailable}
-
- (svgUnavailable = false)}
- />
-
-{/if}
diff --git a/docs/src/lib/utils/svelte-repl.ts b/docs/src/lib/utils/svelte-repl.ts
new file mode 100644
index 000000000..76c06b761
--- /dev/null
+++ b/docs/src/lib/utils/svelte-repl.ts
@@ -0,0 +1,228 @@
+interface File {
+ name: string;
+ source: string;
+}
+
+// Lazy-load $lib source files for REPL import resolution (loaded on demand, not eagerly)
+const libModules = import.meta.glob(['/src/lib/**/*.{ts,js,svelte}'], {
+ query: '?raw',
+ import: 'default'
+}) as Record Promise>;
+
+async function readFile(path: string): Promise {
+ const key = `/${path}`;
+ const loader = libModules[key];
+ if (!loader) throw new Error(`File not found: ${path}`);
+ return loader();
+}
+
+/**
+ * Parse local imports from a source file, resolve them using the readFile function,
+ * and return an array of files ready for the Svelte playground (with App.svelte as entry point).
+ */
+export async function accumulateReplFiles(source: string): Promise {
+ // Parse relative, $lib, and absolute path imports
+ const importPattern =
+ /import\s+(?:\{[^}]+\}|\w+)\s+from\s+['"]((?:\.|\.\.|\$lib|\/)\/[^'"]+)['"]/g;
+ const localImports: { importPath: string; fileName: string; resolvedPath: string }[] = [];
+ let match;
+ while ((match = importPattern.exec(source)) !== null) {
+ const importPath = match[1];
+ const fileName = importPath.split('/').pop() ?? importPath;
+
+ let resolvedPath: string;
+ if (importPath.startsWith('$lib/')) {
+ resolvedPath = `src/lib/${importPath.slice('$lib/'.length)}`;
+ } else {
+ resolvedPath = `src/routes/${importPath.replace(/^\.\//, '')}`;
+ }
+
+ localImports.push({ importPath, fileName, resolvedPath });
+ }
+
+ const files: File[] = [];
+ let rewrittenMain = source;
+
+ for (const { importPath, fileName, resolvedPath } of localImports) {
+ // First pass: rewrite import to flat filename
+ rewrittenMain = rewrittenMain.replace(
+ new RegExp(`from\\s+['"]${importPath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}['"]`),
+ `from './${fileName}'`
+ );
+
+ let actualPath = resolvedPath;
+ try {
+ let fileSource: string;
+ try {
+ fileSource = await readFile(resolvedPath);
+ } catch {
+ const hasExtension = /\.\w+$/.test(resolvedPath);
+ if (hasExtension && resolvedPath.endsWith('.js')) {
+ actualPath = resolvedPath.replace(/\.js$/, '.ts');
+ fileSource = await readFile(actualPath);
+ } else if (!hasExtension) {
+ // Try common extensions for extensionless imports
+ let resolved: string | undefined;
+ for (const ext of ['.ts', '.js', '.svelte']) {
+ try {
+ actualPath = resolvedPath + ext;
+ fileSource = await readFile(actualPath);
+ break;
+ } catch {
+ /* try next extension */
+ }
+ }
+ if (!fileSource!) throw new Error(`File not found: ${resolvedPath}`);
+ } else {
+ throw new Error(`File not found: ${resolvedPath}`);
+ }
+ }
+ const actualFileName = actualPath.split('/').pop() ?? actualPath;
+ files.push({ name: actualFileName, source: fileSource });
+
+ // Rewrite import to use actual filename if extension changed
+ if (actualFileName !== fileName) {
+ rewrittenMain = rewrittenMain.replace(`from './${fileName}'`, `from './${actualFileName}'`);
+ }
+ } catch {
+ console.warn(`Could not read import: ${resolvedPath}`);
+ }
+ }
+
+ // App.svelte must be first (it's the entry point)
+ files.unshift({ name: 'App.svelte', source: rewrittenMain });
+
+ return files;
+}
+
+export async function openInSvelteREPL(source: string) {
+ const files = await accumulateReplFiles(source);
+ const url = await createSvelteReplUrl(files);
+ window.open(url, '_blank');
+}
+
+export async function createSvelteReplUrl(files: File[]) {
+ const useTailWind = false;
+ // Temporary: use layerchart@next until layerchart is published as latest
+ const useNext = true;
+ // Temporary: use container until layerchart-docs2 is published to latest
+ const useContainer = true;
+ // Temporary add tailwind notice to the beginning of markup
+ const addTailwindNotice = false;
+
+ const playgroundFiles = files.map((f) => {
+ let contents = f.source;
+ if (f.name === 'App.svelte') {
+ const scriptsection = contents.match(/
-
+
@@ -400,23 +429,14 @@
-
+