Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Every example is typechecked and has a behavior test for the outcome its README

- [Vanilla TypeScript quickstart](https://go.superdoc.dev/examples/vanilla) — open a DOCX, edit it, and export the result.
- [React quickstart](https://go.superdoc.dev/examples/react) — own the editor lifecycle from a React component.
- [SvelteKit quickstart](https://go.superdoc.dev/examples/sveltekit) — own the editor lifecycle from a SvelteKit route.

### Focused workflows

Expand Down
21 changes: 21 additions & 0 deletions examples/sveltekit/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
node_modules

# SvelteKit
/.svelte-kit
/build
/dist

# Logs
*.log

# Env
.env
.env.*
!.env.example

# Misc
.DS_Store

# Test
/test-results
/playwright-report
30 changes: 30 additions & 0 deletions examples/sveltekit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# SvelteKit quickstart

Mount SuperDoc from a SvelteKit route, wait for the document to open, and destroy the editor when the route tears down.
SuperDoc starts inside `$effect`, so it only runs after the route mounts in the browser.

## Run it

Requires Node 22.12 or newer and pnpm 10.

```bash
pnpm install
pnpm dev
```

Open the printed URL. When the document is ready, edit its text and choose **Export DOCX**.

## Verify it

```bash
pnpm typecheck
pnpm build
pnpm browsers
pnpm test
```

The browser test edits a real DOCX, exports it, and verifies the edit in `word/document.xml`.

Inside this monorepo the example is tested against the current workspace build. A standalone copy installs the versions declared in `package.json`.

See the [configuration reference](https://docs.superdoc.dev/editor/superdoc/configuration).
30 changes: 30 additions & 0 deletions examples/sveltekit/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "@superdoc-examples/sveltekit",
"private": true,
"type": "module",
"packageManager": "pnpm@10.25.0",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"typecheck": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"test": "playwright test",
"browsers": "playwright install chromium",
"prepare": "svelte-kit sync || echo ''"
},
"dependencies": {
"superdoc": "^2.4.0"
},
"devDependencies": {
"@playwright/test": "^1.57.0",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.22.0",
"@sveltejs/vite-plugin-svelte": "^6.2.0",
"@types/node": "^22.0.0",
"jszip": "^3.10.1",
"svelte": "^5.25.0",
"svelte-check": "^4.1.0",
"typescript": "^5.9.2",
"vite": "^7.2.7"
}
}
19 changes: 19 additions & 0 deletions examples/sveltekit/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { defineConfig } from '@playwright/test';

export default defineConfig({
testDir: './tests',
testMatch: '**/*.spec.ts',
workers: 1,
retries: 0,
reporter: 'list',
use: {
baseURL: 'http://127.0.0.1:4182',
trace: 'retain-on-failure',
},
webServer: {
command: 'pnpm run build && pnpm run preview --host 127.0.0.1 --port 4182 --strictPort',
url: 'http://127.0.0.1:4182',
reuseExistingServer: false,
timeout: 180_000,
},
});
5 changes: 5 additions & 0 deletions examples/sveltekit/src/app.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
declare global {
namespace App {}
}

export {};
11 changes: 11 additions & 0 deletions examples/sveltekit/src/app.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>
89 changes: 89 additions & 0 deletions examples/sveltekit/src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<script lang="ts">
import { SuperDoc } from 'superdoc';
import 'superdoc/style.css';

let mount = $state<HTMLDivElement | null>(null);
let ready = $state(false);
let exporting = $state(false);

let superdoc: SuperDoc | null = null;
let exportingLock = false;

$effect(() => {
if (!mount) return;

let active = true;
let opened = false;
let loadFailed = false;
ready = false;

const instance = new SuperDoc({
selector: mount,
document: '/sample.docx',
onReady: () => {
if (!active || loadFailed) return;
opened = true;
ready = true;
},
onException: ({ error }) => {
if (!opened) {
loadFailed = true;
if (active) ready = false;
}
console.error('SuperDoc could not open the document.', error);
},
});
superdoc = instance;

return () => {
active = false;
if (superdoc === instance) superdoc = null;
instance.destroy();
};
});

async function exportDocument() {
if (exportingLock) return;
exportingLock = true;
exporting = true;
try {
await superdoc?.export({ exportType: ['docx'], exportedName: 'sample-edited' });
} catch (error) {
console.error('SuperDoc could not export the document.', error);
} finally {
exportingLock = false;
if (superdoc) exporting = false;
}
}
</script>

<svelte:head>
<title>SuperDoc SvelteKit quickstart</title>
</svelte:head>

<main>
<button disabled={!ready || exporting} onclick={() => void exportDocument()} type="button">
Export DOCX
</button>
<div class="editor" bind:this={mount}></div>
</main>

<style>
:global(html),
:global(body) {
min-height: 100%;
margin: 0;
}

main {
min-height: 100vh;
}

button {
margin: 1rem;
}

.editor {
height: calc(100vh - 4rem);
}
</style>
Binary file added examples/sveltekit/static/sample.docx
Binary file not shown.
12 changes: 12 additions & 0 deletions examples/sveltekit/svelte.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter({ fallback: 'index.html' }),
},
};

export default config;
49 changes: 49 additions & 0 deletions examples/sveltekit/tests/quickstart.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { expect, test } from '@playwright/test';
import { readFile } from 'node:fs/promises';
import JSZip from 'jszip';

const edit = 'SVELTEKITQUICKSTARTEDITMARKER';

test('renders on the server, gates readiness, edits, and exports the sample document', async ({ page, request }) => {
test.setTimeout(240_000);
const errors: string[] = [];
page.on('console', (message) => {
if (message.type() === 'error') errors.push(message.text());
});
page.on('pageerror', (error) => errors.push(String(error)));

let releaseDocument = () => {};
const documentHeld = new Promise<void>((resolve) => {
releaseDocument = resolve;
});
await page.route('**/sample.docx', async (route) => {
await documentHeld;
await route.continue();
});

const response = await request.get('/');
expect(await response.text()).toContain('Export DOCX');

await page.goto('/');
const exportButton = page.getByRole('button', { name: 'Export DOCX' });
await expect(exportButton).toBeDisabled();
releaseDocument();
await expect(exportButton).toBeEnabled({ timeout: 120_000 });

const textRun = page.locator('.superdoc-text-run').first();
await expect(textRun).toBeVisible({ timeout: 60_000 });
await textRun.click();
await page.keyboard.type(edit);
await expect(page.locator('.superdoc')).toContainText(edit);

const download = page.waitForEvent('download', { timeout: 120_000 });
await exportButton.click();
const path = await (await download).path();
if (!path) throw new Error('The browser did not save the exported DOCX.');

const zip = await JSZip.loadAsync(await readFile(path));
const documentXml = await zip.file('word/document.xml')?.async('string');
expect(documentXml).toBeTruthy();
expect(documentXml?.replace(/<[^>]+>/g, '')).toContain(edit);
expect(errors).toEqual([]);
});
14 changes: 14 additions & 0 deletions examples/sveltekit/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
}
9 changes: 9 additions & 0 deletions examples/sveltekit/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';

export default defineConfig({
plugins: [sveltekit()],
optimizeDeps: {
exclude: ['@superdoc/docx-engine'],
},
});
2 changes: 2 additions & 0 deletions go-links/links.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"examples/document-modes": { "path": "examples/document-modes" },
"examples/react": { "path": "examples/react" },
"examples/search": { "path": "examples/search" },
"examples/sveltekit": { "path": "examples/sveltekit" },
"examples/vanilla": { "path": "examples/vanilla" },
"examples/version-history": { "path": "examples/version-history" },
"grading-papers": { "path": "go-links/unavailable" },
Expand All @@ -33,6 +34,7 @@
"nuxt": { "path": "go-links/unavailable" },
"react": { "path": "examples/react" },
"solid": { "path": "go-links/unavailable" },
"sveltekit": { "path": "examples/sveltekit" },
"template-builder": {
"repository": "superdoc-dev/demos",
"path": "template-builder"
Expand Down
2 changes: 2 additions & 0 deletions go-links/published-routes.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"examples/document-modes",
"examples/react",
"examples/search",
"examples/sveltekit",
"examples/vanilla",
"examples/version-history",
"grading-papers",
Expand All @@ -26,6 +27,7 @@
"nuxt",
"react",
"solid",
"sveltekit",
"template-builder",
"theming",
"toolbar",
Expand Down
Loading
Loading