From c4251c7a16765c043367656eccdec54e84100556 Mon Sep 17 00:00:00 2001 From: ocavue Date: Wed, 14 Jan 2026 17:07:04 +1100 Subject: [PATCH 01/10] add lit and vanilla --- .scripts/fetch-examples.ts | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/.scripts/fetch-examples.ts b/.scripts/fetch-examples.ts index efbcc1d96..272d70a02 100644 --- a/.scripts/fetch-examples.ts +++ b/.scripts/fetch-examples.ts @@ -66,6 +66,34 @@ export default function Editor() { return } ` + + +const litEntry = (story: string) => `import './components/editor/examples/${story}' + +let container = document.querySelector('#example-container') +if (!container) { + container = document.createElement('div') + container.id = 'example-container' + document.body.appendChild(container) +} + +const example = document.createElement('lit-editor-example-${story}') +container.replace(example) +` + +const vanillaEntry = (story: string) => `import { setupVanillaEditor } from './components/editor/examples/${story}' + +let container = document.querySelector('#example-container') +if (!container) { + container = document.createElement('div') + container.id = 'example-container' + document.body.appendChild(container) +} + +const example = setupVanillaEditor().render() +container.replace(example) +` + const createSvelteEntry = (componentsPath: string) => (story: string) => ` + diff --git a/.templates/template-vanilla/main.js b/.templates/template-vanilla/main.js deleted file mode 100644 index 117581fd4..000000000 --- a/.templates/template-vanilla/main.js +++ /dev/null @@ -1,2 +0,0 @@ -import './src/app.css' -import './src/index' diff --git a/.templates/template-vanilla/package.json b/.templates/template-vanilla/package.json index 220a7b927..eb7413ebe 100644 --- a/.templates/template-vanilla/package.json +++ b/.templates/template-vanilla/package.json @@ -16,6 +16,7 @@ "postcss": "^8.5.6", "tailwindcss": "^4.1.18", "tw-animate-css": "^1.4.0", + "typescript": "~5.9.3", "vite": "7.3.1" } } diff --git a/.templates/template-vanilla/src/app.ts b/.templates/template-vanilla/src/app.ts new file mode 100644 index 000000000..6c8a9329f --- /dev/null +++ b/.templates/template-vanilla/src/app.ts @@ -0,0 +1,5 @@ +export const app = { + render: () => { + return document.createElement('div') + }, +} diff --git a/.templates/template-vanilla/src/index.js b/.templates/template-vanilla/src/index.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/.templates/template-vanilla/src/main.ts b/.templates/template-vanilla/src/main.ts new file mode 100644 index 000000000..41422e27c --- /dev/null +++ b/.templates/template-vanilla/src/main.ts @@ -0,0 +1,11 @@ +import './app.css' +import { app } from './app' + +let container = document.querySelector('#app') +if (!container) { + container = document.createElement('div') + container.id = 'app' + document.body.appendChild(container) +} + +container.replaceChildren(app.render()) diff --git a/.templates/template-vanilla/tsconfig.json b/.templates/template-vanilla/tsconfig.json new file mode 100644 index 000000000..4ba8dd95c --- /dev/null +++ b/.templates/template-vanilla/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} From dab9452d48679f4c421296cdcdc3284dd21fe775 Mon Sep 17 00:00:00 2001 From: ocavue Date: Wed, 14 Jan 2026 17:15:56 +1100 Subject: [PATCH 03/10] fix --- .scripts/fetch-examples.ts | 4 +++- .templates/template-lit/src/editor.ts | 8 +++++--- .templates/template-vanilla/src/app.ts | 5 ----- .templates/template-vanilla/src/editor.ts | 3 +++ .templates/template-vanilla/src/main.ts | 4 ++-- 5 files changed, 13 insertions(+), 11 deletions(-) delete mode 100644 .templates/template-vanilla/src/app.ts create mode 100644 .templates/template-vanilla/src/editor.ts diff --git a/.scripts/fetch-examples.ts b/.scripts/fetch-examples.ts index e139fb28b..3635a4db3 100644 --- a/.scripts/fetch-examples.ts +++ b/.scripts/fetch-examples.ts @@ -83,7 +83,9 @@ container.replace(example) const vanillaEntry = (story: string) => `import { setupVanillaEditor } from './components/editor/examples/${story}' -export const app = setupVanillaEditor() +export function renderEditor() { + return setupVanillaEditor().render() +} ` const createSvelteEntry = diff --git a/.templates/template-lit/src/editor.ts b/.templates/template-lit/src/editor.ts index f29e8d372..f18958690 100644 --- a/.templates/template-lit/src/editor.ts +++ b/.templates/template-lit/src/editor.ts @@ -3,9 +3,11 @@ import { customElement } from 'lit/decorators.js' @customElement('my-editor') export class MyEditor extends LitElement { + createRenderRoot() { + return this + } + render() { - return html` -
- ` + return html`
` } } diff --git a/.templates/template-vanilla/src/app.ts b/.templates/template-vanilla/src/app.ts deleted file mode 100644 index 6c8a9329f..000000000 --- a/.templates/template-vanilla/src/app.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const app = { - render: () => { - return document.createElement('div') - }, -} diff --git a/.templates/template-vanilla/src/editor.ts b/.templates/template-vanilla/src/editor.ts new file mode 100644 index 000000000..e5596caad --- /dev/null +++ b/.templates/template-vanilla/src/editor.ts @@ -0,0 +1,3 @@ +export function renderEditor() { + return document.createElement('div') +} diff --git a/.templates/template-vanilla/src/main.ts b/.templates/template-vanilla/src/main.ts index 41422e27c..a4945ae00 100644 --- a/.templates/template-vanilla/src/main.ts +++ b/.templates/template-vanilla/src/main.ts @@ -1,5 +1,5 @@ import './app.css' -import { app } from './app' +import { renderEditor } from './editor' let container = document.querySelector('#app') if (!container) { @@ -8,4 +8,4 @@ if (!container) { document.body.appendChild(container) } -container.replaceChildren(app.render()) +container.replaceChildren(renderEditor()) From 57bb15c037fd04450be11c76c4e1ee1b308cb32a Mon Sep 17 00:00:00 2001 From: ocavue Date: Wed, 14 Jan 2026 17:16:49 +1100 Subject: [PATCH 04/10] fix --- .scripts/fetch-examples.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.scripts/fetch-examples.ts b/.scripts/fetch-examples.ts index 3635a4db3..ff5ac2ec5 100644 --- a/.scripts/fetch-examples.ts +++ b/.scripts/fetch-examples.ts @@ -69,16 +69,19 @@ export default function Editor() { const litEntry = (story: string) => `import './components/editor/examples/${story}' +import { LitElement, html } from 'lit' +import { customElement } from 'lit/decorators.js' -let container = document.querySelector('#example-container') -if (!container) { - container = document.createElement('div') - container.id = 'example-container' - document.body.appendChild(container) -} +@customElement('my-editor') +export class MyEditor extends LitElement { + createRenderRoot() { + return this + } -const example = document.createElement('lit-editor-example-${story}') -container.replace(example) + render() { + return html\`\` + } +} ` const vanillaEntry = (story: string) => `import { setupVanillaEditor } from './components/editor/examples/${story}' From b07a44e8eef3674676df639728b1b8485f2416df Mon Sep 17 00:00:00 2001 From: ocavue Date: Wed, 14 Jan 2026 17:16:58 +1100 Subject: [PATCH 05/10] fix --- .scripts/fetch-examples.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.scripts/fetch-examples.ts b/.scripts/fetch-examples.ts index ff5ac2ec5..c7f06e552 100644 --- a/.scripts/fetch-examples.ts +++ b/.scripts/fetch-examples.ts @@ -149,13 +149,13 @@ const FRAMEWORK_CONFIG = { lit: { template: 'lit', destDir: 'src', - entryFile: 'src/index.ts', + entryFile: 'src/editor.ts', createEntryContent: litEntry, }, vanilla: { template: 'vanilla', destDir: 'src', - entryFile: 'src/app.ts', + entryFile: 'src/editor.ts', createEntryContent: vanillaEntry, }, next: { From e0aeddc08a405d7c6e8bd02d1f98efdbf81c9011 Mon Sep 17 00:00:00 2001 From: ocavue Date: Wed, 14 Jan 2026 17:17:43 +1100 Subject: [PATCH 06/10] fix --- .scripts/fetch-examples.ts | 11 +- .templates/template-lit/src/editor.ts | 4 +- lit-minimal/.gitignore | 4 + lit-minimal/README.md | 15 +++ lit-minimal/index.html | 13 ++ lit-minimal/package.json | 25 ++++ lit-minimal/src/app.css | 13 ++ lit-minimal/src/app.ts | 14 +++ .../editor/examples/minimal/editor.ts | 59 +++++++++ .../editor/examples/minimal/index.ts | 1 + lit-minimal/src/editor.ts | 16 +++ lit-minimal/tsconfig.json | 24 ++++ lit-minimal/vite.config.ts | 6 + lit-slash-menu/.gitignore | 4 + lit-slash-menu/README.md | 15 +++ lit-slash-menu/index.html | 13 ++ lit-slash-menu/package.json | 25 ++++ lit-slash-menu/src/app.css | 13 ++ lit-slash-menu/src/app.ts | 14 +++ .../editor/examples/slash-menu/editor.ts | 75 ++++++++++++ .../editor/examples/slash-menu/extension.ts | 12 ++ .../editor/examples/slash-menu/index.ts | 1 + .../components/editor/ui/slash-menu/index.ts | 3 + .../editor/ui/slash-menu/slash-menu-empty.ts | 21 ++++ .../editor/ui/slash-menu/slash-menu-item.ts | 48 ++++++++ .../editor/ui/slash-menu/slash-menu.ts | 114 ++++++++++++++++++ lit-slash-menu/src/editor.ts | 16 +++ lit-slash-menu/tsconfig.json | 24 ++++ lit-slash-menu/vite.config.ts | 6 + vanilla-minimal/.gitignore | 4 + vanilla-minimal/README.md | 15 +++ vanilla-minimal/index.html | 11 ++ vanilla-minimal/package.json | 24 ++++ vanilla-minimal/src/app.css | 13 ++ .../editor/examples/minimal/editor.ts | 22 ++++ .../editor/examples/minimal/index.ts | 1 + vanilla-minimal/src/editor.ts | 5 + vanilla-minimal/src/main.ts | 11 ++ vanilla-minimal/tsconfig.json | 26 ++++ vanilla-minimal/vite.config.ts | 6 + 40 files changed, 741 insertions(+), 6 deletions(-) create mode 100644 lit-minimal/.gitignore create mode 100644 lit-minimal/README.md create mode 100644 lit-minimal/index.html create mode 100644 lit-minimal/package.json create mode 100644 lit-minimal/src/app.css create mode 100644 lit-minimal/src/app.ts create mode 100644 lit-minimal/src/components/editor/examples/minimal/editor.ts create mode 100644 lit-minimal/src/components/editor/examples/minimal/index.ts create mode 100644 lit-minimal/src/editor.ts create mode 100644 lit-minimal/tsconfig.json create mode 100644 lit-minimal/vite.config.ts create mode 100644 lit-slash-menu/.gitignore create mode 100644 lit-slash-menu/README.md create mode 100644 lit-slash-menu/index.html create mode 100644 lit-slash-menu/package.json create mode 100644 lit-slash-menu/src/app.css create mode 100644 lit-slash-menu/src/app.ts create mode 100644 lit-slash-menu/src/components/editor/examples/slash-menu/editor.ts create mode 100644 lit-slash-menu/src/components/editor/examples/slash-menu/extension.ts create mode 100644 lit-slash-menu/src/components/editor/examples/slash-menu/index.ts create mode 100644 lit-slash-menu/src/components/editor/ui/slash-menu/index.ts create mode 100644 lit-slash-menu/src/components/editor/ui/slash-menu/slash-menu-empty.ts create mode 100644 lit-slash-menu/src/components/editor/ui/slash-menu/slash-menu-item.ts create mode 100644 lit-slash-menu/src/components/editor/ui/slash-menu/slash-menu.ts create mode 100644 lit-slash-menu/src/editor.ts create mode 100644 lit-slash-menu/tsconfig.json create mode 100644 lit-slash-menu/vite.config.ts create mode 100644 vanilla-minimal/.gitignore create mode 100644 vanilla-minimal/README.md create mode 100644 vanilla-minimal/index.html create mode 100644 vanilla-minimal/package.json create mode 100644 vanilla-minimal/src/app.css create mode 100644 vanilla-minimal/src/components/editor/examples/minimal/editor.ts create mode 100644 vanilla-minimal/src/components/editor/examples/minimal/index.ts create mode 100644 vanilla-minimal/src/editor.ts create mode 100644 vanilla-minimal/src/main.ts create mode 100644 vanilla-minimal/tsconfig.json create mode 100644 vanilla-minimal/vite.config.ts diff --git a/.scripts/fetch-examples.ts b/.scripts/fetch-examples.ts index c7f06e552..090093010 100644 --- a/.scripts/fetch-examples.ts +++ b/.scripts/fetch-examples.ts @@ -67,8 +67,9 @@ export default function Editor() { } ` - -const litEntry = (story: string) => `import './components/editor/examples/${story}' +const litEntry = ( + story: string, +) => `import './components/editor/examples/${story}' import { LitElement, html } from 'lit' import { customElement } from 'lit/decorators.js' @@ -84,7 +85,9 @@ export class MyEditor extends LitElement { } ` -const vanillaEntry = (story: string) => `import { setupVanillaEditor } from './components/editor/examples/${story}' +const vanillaEntry = ( + story: string, +) => `import { setupVanillaEditor } from './components/editor/examples/${story}' export function renderEditor() { return setupVanillaEditor().render() @@ -113,8 +116,6 @@ const svelteEntry = createSvelteEntry('./components/editor/examples') const svelteKitEntry = createSvelteEntry('../components/editor/examples') const vueEntry = createVueEntry('./components/editor/examples') - - const FRAMEWORK_CONFIG = { react: { template: 'react', diff --git a/.templates/template-lit/src/editor.ts b/.templates/template-lit/src/editor.ts index f18958690..e7ebe9e34 100644 --- a/.templates/template-lit/src/editor.ts +++ b/.templates/template-lit/src/editor.ts @@ -8,6 +8,8 @@ export class MyEditor extends LitElement { } render() { - return html`
` + return html` +
+ ` } } diff --git a/lit-minimal/.gitignore b/lit-minimal/.gitignore new file mode 100644 index 000000000..5d6225c6d --- /dev/null +++ b/lit-minimal/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.next +.svelte-kit diff --git a/lit-minimal/README.md b/lit-minimal/README.md new file mode 100644 index 000000000..954cc8c15 --- /dev/null +++ b/lit-minimal/README.md @@ -0,0 +1,15 @@ +# lit-minimal + +A [ProseKit](https://prosekit.dev) example. + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/prosekit/examples/tree/master/lit-minimal) +[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/prosekit/examples/tree/master/lit-minimal) + +Run the example locally with: + +```bash +npx degit prosekit/examples/lit-minimal lit-minimal +cd lit-minimal +npm install +npm run dev +``` diff --git a/lit-minimal/index.html b/lit-minimal/index.html new file mode 100644 index 000000000..9637b1626 --- /dev/null +++ b/lit-minimal/index.html @@ -0,0 +1,13 @@ + + + + + + ProseKit + Lit + + + + + + + diff --git a/lit-minimal/package.json b/lit-minimal/package.json new file mode 100644 index 000000000..29be21bd5 --- /dev/null +++ b/lit-minimal/package.json @@ -0,0 +1,25 @@ +{ + "name": "example-lit-minimal", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "tsc && vite build", + "dev": "vite", + "preview": "vite preview" + }, + "dependencies": { + "lit": "^3.3.2", + "prosekit": "^0.17.1" + }, + "devDependencies": { + "@egoist/tailwindcss-icons": "^1.9.0", + "@iconify-json/lucide": "^1.2.84", + "@tailwindcss/vite": "^4.1.18", + "postcss": "^8.5.6", + "tailwindcss": "^4.1.18", + "tw-animate-css": "^1.4.0", + "typescript": "5.9.3", + "vite": "7.3.1" + } +} diff --git a/lit-minimal/src/app.css b/lit-minimal/src/app.css new file mode 100644 index 000000000..5d0567ecf --- /dev/null +++ b/lit-minimal/src/app.css @@ -0,0 +1,13 @@ +@import 'tailwindcss'; +@import 'tw-animate-css'; + +@plugin "@egoist/tailwindcss-icons"; + +body { + height: 100svh; + display: grid; + max-width: 900px; + padding: 16px; + margin-left: auto; + margin-right: auto; +} diff --git a/lit-minimal/src/app.ts b/lit-minimal/src/app.ts new file mode 100644 index 000000000..8f9339335 --- /dev/null +++ b/lit-minimal/src/app.ts @@ -0,0 +1,14 @@ +import './app.css' +import './editor' + +import { LitElement, html } from 'lit' +import { customElement } from 'lit/decorators.js' + +@customElement('my-app') +export class MyApp extends LitElement { + render() { + return html` + + ` + } +} diff --git a/lit-minimal/src/components/editor/examples/minimal/editor.ts b/lit-minimal/src/components/editor/examples/minimal/editor.ts new file mode 100644 index 000000000..122b4ad8f --- /dev/null +++ b/lit-minimal/src/components/editor/examples/minimal/editor.ts @@ -0,0 +1,59 @@ +import 'prosekit/basic/style.css' +import 'prosekit/basic/typography.css' + +import { + html, + LitElement, + type PropertyDeclaration, + type PropertyValues, +} from 'lit' +import { createRef, ref, type Ref } from 'lit/directives/ref.js' +import { defineBasicExtension } from 'prosekit/basic' +import type { Editor } from 'prosekit/core' +import { createEditor } from 'prosekit/core' + +export class LitEditor extends LitElement { + static override properties = { + editor: { + state: true, + attribute: false, + } satisfies PropertyDeclaration, + } + + private editor: Editor + private ref: Ref + + constructor() { + super() + + const extension = defineBasicExtension() + this.editor = createEditor({ extension }) + this.ref = createRef() + } + + override createRenderRoot() { + return this + } + + override updated(changedProperties: PropertyValues) { + super.updated(changedProperties) + this.editor.mount(this.ref.value) + } + + override render() { + return html` +
+ ` + } +} + +export function registerLitEditor() { + if (customElements.get('lit-editor-example-minimal')) return + customElements.define('lit-editor-example-minimal', LitEditor) +} + +declare global { + interface HTMLElementTagNameMap { + 'lit-editor-example-minimal': LitEditor + } +} diff --git a/lit-minimal/src/components/editor/examples/minimal/index.ts b/lit-minimal/src/components/editor/examples/minimal/index.ts new file mode 100644 index 000000000..9eeecbbc5 --- /dev/null +++ b/lit-minimal/src/components/editor/examples/minimal/index.ts @@ -0,0 +1 @@ +export { LitEditor as ExampleEditor, registerLitEditor } from './editor' diff --git a/lit-minimal/src/editor.ts b/lit-minimal/src/editor.ts new file mode 100644 index 000000000..1b968c3fc --- /dev/null +++ b/lit-minimal/src/editor.ts @@ -0,0 +1,16 @@ +import './components/editor/examples/minimal' +import { LitElement, html } from 'lit' +import { customElement } from 'lit/decorators.js' + +@customElement('my-editor') +export class MyEditor extends LitElement { + createRenderRoot() { + return this + } + + render() { + return html` + + ` + } +} diff --git a/lit-minimal/tsconfig.json b/lit-minimal/tsconfig.json new file mode 100644 index 000000000..1de5d357a --- /dev/null +++ b/lit-minimal/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2022", + "experimentalDecorators": true, + "useDefineForClassFields": false, + "module": "ESNext", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/lit-minimal/vite.config.ts b/lit-minimal/vite.config.ts new file mode 100644 index 000000000..fb0cdf00b --- /dev/null +++ b/lit-minimal/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite' +import tailwindcss from '@tailwindcss/vite' + +export default defineConfig({ + plugins: [tailwindcss()], +}) diff --git a/lit-slash-menu/.gitignore b/lit-slash-menu/.gitignore new file mode 100644 index 000000000..5d6225c6d --- /dev/null +++ b/lit-slash-menu/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.next +.svelte-kit diff --git a/lit-slash-menu/README.md b/lit-slash-menu/README.md new file mode 100644 index 000000000..1a4d66003 --- /dev/null +++ b/lit-slash-menu/README.md @@ -0,0 +1,15 @@ +# lit-slash-menu + +A [ProseKit](https://prosekit.dev) example. + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/prosekit/examples/tree/master/lit-slash-menu) +[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/prosekit/examples/tree/master/lit-slash-menu) + +Run the example locally with: + +```bash +npx degit prosekit/examples/lit-slash-menu lit-slash-menu +cd lit-slash-menu +npm install +npm run dev +``` diff --git a/lit-slash-menu/index.html b/lit-slash-menu/index.html new file mode 100644 index 000000000..9637b1626 --- /dev/null +++ b/lit-slash-menu/index.html @@ -0,0 +1,13 @@ + + + + + + ProseKit + Lit + + + + + + + diff --git a/lit-slash-menu/package.json b/lit-slash-menu/package.json new file mode 100644 index 000000000..620651a3a --- /dev/null +++ b/lit-slash-menu/package.json @@ -0,0 +1,25 @@ +{ + "name": "example-lit-slash-menu", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "tsc && vite build", + "dev": "vite", + "preview": "vite preview" + }, + "dependencies": { + "lit": "^3.3.2", + "prosekit": "^0.17.1" + }, + "devDependencies": { + "@egoist/tailwindcss-icons": "^1.9.0", + "@iconify-json/lucide": "^1.2.84", + "@tailwindcss/vite": "^4.1.18", + "postcss": "^8.5.6", + "tailwindcss": "^4.1.18", + "tw-animate-css": "^1.4.0", + "typescript": "5.9.3", + "vite": "7.3.1" + } +} diff --git a/lit-slash-menu/src/app.css b/lit-slash-menu/src/app.css new file mode 100644 index 000000000..5d0567ecf --- /dev/null +++ b/lit-slash-menu/src/app.css @@ -0,0 +1,13 @@ +@import 'tailwindcss'; +@import 'tw-animate-css'; + +@plugin "@egoist/tailwindcss-icons"; + +body { + height: 100svh; + display: grid; + max-width: 900px; + padding: 16px; + margin-left: auto; + margin-right: auto; +} diff --git a/lit-slash-menu/src/app.ts b/lit-slash-menu/src/app.ts new file mode 100644 index 000000000..8f9339335 --- /dev/null +++ b/lit-slash-menu/src/app.ts @@ -0,0 +1,14 @@ +import './app.css' +import './editor' + +import { LitElement, html } from 'lit' +import { customElement } from 'lit/decorators.js' + +@customElement('my-app') +export class MyApp extends LitElement { + render() { + return html` + + ` + } +} diff --git a/lit-slash-menu/src/components/editor/examples/slash-menu/editor.ts b/lit-slash-menu/src/components/editor/examples/slash-menu/editor.ts new file mode 100644 index 000000000..b70613631 --- /dev/null +++ b/lit-slash-menu/src/components/editor/examples/slash-menu/editor.ts @@ -0,0 +1,75 @@ +import 'prosekit/basic/style.css' +import 'prosekit/basic/typography.css' + +import { + html, + LitElement, + type PropertyDeclaration, + type PropertyValues, +} from 'lit' +import { createRef, ref, type Ref } from 'lit/directives/ref.js' +import type { Editor } from 'prosekit/core' +import { createEditor } from 'prosekit/core' + +import { defineExtension } from './extension' + +import '../../ui/slash-menu/index' + +export class LitEditor extends LitElement { + static override properties = { + editor: { + state: true, + attribute: false, + } satisfies PropertyDeclaration, + } + + private editor: Editor + private ref: Ref + + constructor() { + super() + + const extension = defineExtension() + this.editor = createEditor({ extension }) + this.ref = createRef() + } + + override createRenderRoot() { + return this + } + + override updated(changedProperties: PropertyValues) { + super.updated(changedProperties) + this.editor.mount(this.ref.value) + } + + override render() { + return html` +
+
+
+ +
+
+ ` + } +} + +export function registerLitEditor() { + if (customElements.get('lit-editor-example-slash-menu')) return + customElements.define('lit-editor-example-slash-menu', LitEditor) +} + +declare global { + interface HTMLElementTagNameMap { + 'lit-editor-example-slash-menu': LitEditor + } +} diff --git a/lit-slash-menu/src/components/editor/examples/slash-menu/extension.ts b/lit-slash-menu/src/components/editor/examples/slash-menu/extension.ts new file mode 100644 index 000000000..0edda6013 --- /dev/null +++ b/lit-slash-menu/src/components/editor/examples/slash-menu/extension.ts @@ -0,0 +1,12 @@ +import { defineBasicExtension } from 'prosekit/basic' +import { union } from 'prosekit/core' +import { definePlaceholder } from 'prosekit/extensions/placeholder' + +export function defineExtension() { + return union( + defineBasicExtension(), + definePlaceholder({ placeholder: 'Press / for commands...' }), + ) +} + +export type EditorExtension = ReturnType diff --git a/lit-slash-menu/src/components/editor/examples/slash-menu/index.ts b/lit-slash-menu/src/components/editor/examples/slash-menu/index.ts new file mode 100644 index 000000000..9eeecbbc5 --- /dev/null +++ b/lit-slash-menu/src/components/editor/examples/slash-menu/index.ts @@ -0,0 +1 @@ +export { LitEditor as ExampleEditor, registerLitEditor } from './editor' diff --git a/lit-slash-menu/src/components/editor/ui/slash-menu/index.ts b/lit-slash-menu/src/components/editor/ui/slash-menu/index.ts new file mode 100644 index 000000000..6b2bff17a --- /dev/null +++ b/lit-slash-menu/src/components/editor/ui/slash-menu/index.ts @@ -0,0 +1,3 @@ +import './slash-menu' +import './slash-menu-item' +import './slash-menu-empty' diff --git a/lit-slash-menu/src/components/editor/ui/slash-menu/slash-menu-empty.ts b/lit-slash-menu/src/components/editor/ui/slash-menu/slash-menu-empty.ts new file mode 100644 index 000000000..8b31ad9f4 --- /dev/null +++ b/lit-slash-menu/src/components/editor/ui/slash-menu/slash-menu-empty.ts @@ -0,0 +1,21 @@ +import 'prosekit/lit/autocomplete' + +import { html, LitElement } from 'lit' + +class SlashMenuEmptyElement extends LitElement { + override createRenderRoot() { + return this + } + + override render() { + return html` + + No results + + ` + } +} + +customElements.define('lit-editor-slash-menu-empty', SlashMenuEmptyElement) diff --git a/lit-slash-menu/src/components/editor/ui/slash-menu/slash-menu-item.ts b/lit-slash-menu/src/components/editor/ui/slash-menu/slash-menu-item.ts new file mode 100644 index 000000000..a87cd0db3 --- /dev/null +++ b/lit-slash-menu/src/components/editor/ui/slash-menu/slash-menu-item.ts @@ -0,0 +1,48 @@ +import 'prosekit/lit/autocomplete' + +import { html, LitElement } from 'lit' +import type { AutocompleteItemEvents } from 'prosekit/web/autocomplete' + +class SlashMenuItemElement extends LitElement { + static override properties = { + label: { type: String }, + kbd: { type: String }, + } + + label: string + kbd: string + + constructor() { + super() + this.label = '' + this.kbd = '' + } + + override createRenderRoot() { + return this + } + + handleSelect = (event: AutocompleteItemEvents['select']) => { + this.dispatchEvent(new CustomEvent('select', { detail: event.detail })) + } + + override render() { + return html` + + ${this.label} + ${this.kbd + ? html` + + ${this.kbd} + + ` + : ''} + + ` + } +} + +customElements.define('lit-editor-slash-menu-item', SlashMenuItemElement) diff --git a/lit-slash-menu/src/components/editor/ui/slash-menu/slash-menu.ts b/lit-slash-menu/src/components/editor/ui/slash-menu/slash-menu.ts new file mode 100644 index 000000000..6716185a1 --- /dev/null +++ b/lit-slash-menu/src/components/editor/ui/slash-menu/slash-menu.ts @@ -0,0 +1,114 @@ +import 'prosekit/lit/autocomplete' + +import { html, LitElement, type PropertyDeclaration } from 'lit' +import type { BasicExtension } from 'prosekit/basic' +import type { Editor } from 'prosekit/core' +import { canUseRegexLookbehind } from 'prosekit/core' + +// Match inputs like "/", "/table", "/heading 1" etc. Do not match "/ heading". +const regex = canUseRegexLookbehind() ? /(?, + } + + editor?: Editor + + override createRenderRoot() { + return this + } + + override render() { + const editor = this.editor + if (!editor) { + return html`` + } + + return html` + + + editor.commands.setParagraph()} + > + editor.commands.setHeading({ level: 1 })} + > + editor.commands.setHeading({ level: 2 })} + > + editor.commands.setHeading({ level: 3 })} + > + editor.commands.wrapInList({ kind: 'bullet' })} + > + editor.commands.wrapInList({ kind: 'ordered' })} + > + editor.commands.wrapInList({ kind: 'task' })} + > + editor.commands.wrapInList({ kind: 'toggle' })} + > + editor.commands.setBlockquote()} + > + editor.commands.insertTable({ row: 3, col: 3 })} + > + editor.commands.insertHorizontalRule()} + > + editor.commands.setCodeBlock()} + > + + + + ` + } +} + +customElements.define('lit-editor-slash-menu', SlashMenuElement) diff --git a/lit-slash-menu/src/editor.ts b/lit-slash-menu/src/editor.ts new file mode 100644 index 000000000..ef27a8ba7 --- /dev/null +++ b/lit-slash-menu/src/editor.ts @@ -0,0 +1,16 @@ +import './components/editor/examples/slash-menu' +import { LitElement, html } from 'lit' +import { customElement } from 'lit/decorators.js' + +@customElement('my-editor') +export class MyEditor extends LitElement { + createRenderRoot() { + return this + } + + render() { + return html` + + ` + } +} diff --git a/lit-slash-menu/tsconfig.json b/lit-slash-menu/tsconfig.json new file mode 100644 index 000000000..1de5d357a --- /dev/null +++ b/lit-slash-menu/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2022", + "experimentalDecorators": true, + "useDefineForClassFields": false, + "module": "ESNext", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/lit-slash-menu/vite.config.ts b/lit-slash-menu/vite.config.ts new file mode 100644 index 000000000..fb0cdf00b --- /dev/null +++ b/lit-slash-menu/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite' +import tailwindcss from '@tailwindcss/vite' + +export default defineConfig({ + plugins: [tailwindcss()], +}) diff --git a/vanilla-minimal/.gitignore b/vanilla-minimal/.gitignore new file mode 100644 index 000000000..5d6225c6d --- /dev/null +++ b/vanilla-minimal/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.next +.svelte-kit diff --git a/vanilla-minimal/README.md b/vanilla-minimal/README.md new file mode 100644 index 000000000..6937f9ae9 --- /dev/null +++ b/vanilla-minimal/README.md @@ -0,0 +1,15 @@ +# vanilla-minimal + +A [ProseKit](https://prosekit.dev) example. + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/prosekit/examples/tree/master/vanilla-minimal) +[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/prosekit/examples/tree/master/vanilla-minimal) + +Run the example locally with: + +```bash +npx degit prosekit/examples/vanilla-minimal vanilla-minimal +cd vanilla-minimal +npm install +npm run dev +``` diff --git a/vanilla-minimal/index.html b/vanilla-minimal/index.html new file mode 100644 index 000000000..2d9080a6a --- /dev/null +++ b/vanilla-minimal/index.html @@ -0,0 +1,11 @@ + + + + + + ProseKit + Vanilla TypeScript + + + + + diff --git a/vanilla-minimal/package.json b/vanilla-minimal/package.json new file mode 100644 index 000000000..3c3923301 --- /dev/null +++ b/vanilla-minimal/package.json @@ -0,0 +1,24 @@ +{ + "name": "example-vanilla-minimal", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite", + "preview": "vite preview" + }, + "dependencies": { + "prosekit": "^0.17.1" + }, + "devDependencies": { + "@egoist/tailwindcss-icons": "^1.9.0", + "@iconify-json/lucide": "^1.2.84", + "@tailwindcss/vite": "^4.1.18", + "postcss": "^8.5.6", + "tailwindcss": "^4.1.18", + "tw-animate-css": "^1.4.0", + "typescript": "~5.9.3", + "vite": "7.3.1" + } +} diff --git a/vanilla-minimal/src/app.css b/vanilla-minimal/src/app.css new file mode 100644 index 000000000..5d0567ecf --- /dev/null +++ b/vanilla-minimal/src/app.css @@ -0,0 +1,13 @@ +@import 'tailwindcss'; +@import 'tw-animate-css'; + +@plugin "@egoist/tailwindcss-icons"; + +body { + height: 100svh; + display: grid; + max-width: 900px; + padding: 16px; + margin-left: auto; + margin-right: auto; +} diff --git a/vanilla-minimal/src/components/editor/examples/minimal/editor.ts b/vanilla-minimal/src/components/editor/examples/minimal/editor.ts new file mode 100644 index 000000000..a8ca847e5 --- /dev/null +++ b/vanilla-minimal/src/components/editor/examples/minimal/editor.ts @@ -0,0 +1,22 @@ +import 'prosekit/basic/style.css' +import 'prosekit/basic/typography.css' + +import { defineBasicExtension } from 'prosekit/basic' +import { createEditor } from 'prosekit/core' + +export function setupVanillaEditor() { + const extension = defineBasicExtension() + const editor = createEditor({ extension }) + + return { + render: () => { + const element = document.createElement('div') + element.className = 'outline-solid p-4' + editor.mount(element) + return element + }, + destroy: () => { + editor.unmount() + }, + } +} diff --git a/vanilla-minimal/src/components/editor/examples/minimal/index.ts b/vanilla-minimal/src/components/editor/examples/minimal/index.ts new file mode 100644 index 000000000..e54daaa4f --- /dev/null +++ b/vanilla-minimal/src/components/editor/examples/minimal/index.ts @@ -0,0 +1 @@ +export { setupVanillaEditor } from './editor' diff --git a/vanilla-minimal/src/editor.ts b/vanilla-minimal/src/editor.ts new file mode 100644 index 000000000..3ea03142c --- /dev/null +++ b/vanilla-minimal/src/editor.ts @@ -0,0 +1,5 @@ +import { setupVanillaEditor } from './components/editor/examples/minimal' + +export function renderEditor() { + return setupVanillaEditor().render() +} diff --git a/vanilla-minimal/src/main.ts b/vanilla-minimal/src/main.ts new file mode 100644 index 000000000..a4945ae00 --- /dev/null +++ b/vanilla-minimal/src/main.ts @@ -0,0 +1,11 @@ +import './app.css' +import { renderEditor } from './editor' + +let container = document.querySelector('#app') +if (!container) { + container = document.createElement('div') + container.id = 'app' + document.body.appendChild(container) +} + +container.replaceChildren(renderEditor()) diff --git a/vanilla-minimal/tsconfig.json b/vanilla-minimal/tsconfig.json new file mode 100644 index 000000000..4ba8dd95c --- /dev/null +++ b/vanilla-minimal/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/vanilla-minimal/vite.config.ts b/vanilla-minimal/vite.config.ts new file mode 100644 index 000000000..fb0cdf00b --- /dev/null +++ b/vanilla-minimal/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite' +import tailwindcss from '@tailwindcss/vite' + +export default defineConfig({ + plugins: [tailwindcss()], +}) From c616ddfa8e44c62ee22ba1dee628dddf0e3a2632 Mon Sep 17 00:00:00 2001 From: ocavue Date: Wed, 14 Jan 2026 17:20:02 +1100 Subject: [PATCH 07/10] fix --- lit-minimal/src/app.ts | 5 +++++ lit-minimal/src/editor.ts | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lit-minimal/src/app.ts b/lit-minimal/src/app.ts index 8f9339335..e3a45d6d8 100644 --- a/lit-minimal/src/app.ts +++ b/lit-minimal/src/app.ts @@ -6,6 +6,11 @@ import { customElement } from 'lit/decorators.js' @customElement('my-app') export class MyApp extends LitElement { + + createRenderRoot() { + return this + } + render() { return html` diff --git a/lit-minimal/src/editor.ts b/lit-minimal/src/editor.ts index 1b968c3fc..63bc2902d 100644 --- a/lit-minimal/src/editor.ts +++ b/lit-minimal/src/editor.ts @@ -1,7 +1,9 @@ -import './components/editor/examples/minimal' +import { registerLitEditor } from './components/editor/examples/minimal' import { LitElement, html } from 'lit' import { customElement } from 'lit/decorators.js' +registerLitEditor() + @customElement('my-editor') export class MyEditor extends LitElement { createRenderRoot() { From c85625bd876e29b17437f9464d44e423e2e1999e Mon Sep 17 00:00:00 2001 From: ocavue Date: Wed, 14 Jan 2026 17:20:31 +1100 Subject: [PATCH 08/10] fix --- .scripts/fetch-examples.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.scripts/fetch-examples.ts b/.scripts/fetch-examples.ts index 090093010..56dc7536a 100644 --- a/.scripts/fetch-examples.ts +++ b/.scripts/fetch-examples.ts @@ -69,10 +69,12 @@ export default function Editor() { const litEntry = ( story: string, -) => `import './components/editor/examples/${story}' +) => `import { registerLitEditor } from './components/editor/examples/${story}' import { LitElement, html } from 'lit' import { customElement } from 'lit/decorators.js' +registerLitEditor() + @customElement('my-editor') export class MyEditor extends LitElement { createRenderRoot() { From 43504d4a9e2b7831b2a905685d1c7a4aecc8af28 Mon Sep 17 00:00:00 2001 From: ocavue Date: Wed, 14 Jan 2026 17:21:09 +1100 Subject: [PATCH 09/10] fix --- .templates/template-lit/src/app.ts | 4 ++++ lit-minimal/src/app.ts | 5 ----- lit-slash-menu/src/editor.ts | 4 +++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.templates/template-lit/src/app.ts b/.templates/template-lit/src/app.ts index 8f9339335..3cbf14f94 100644 --- a/.templates/template-lit/src/app.ts +++ b/.templates/template-lit/src/app.ts @@ -6,6 +6,10 @@ import { customElement } from 'lit/decorators.js' @customElement('my-app') export class MyApp extends LitElement { + createRenderRoot() { + return this + } + render() { return html` diff --git a/lit-minimal/src/app.ts b/lit-minimal/src/app.ts index e3a45d6d8..8f9339335 100644 --- a/lit-minimal/src/app.ts +++ b/lit-minimal/src/app.ts @@ -6,11 +6,6 @@ import { customElement } from 'lit/decorators.js' @customElement('my-app') export class MyApp extends LitElement { - - createRenderRoot() { - return this - } - render() { return html` diff --git a/lit-slash-menu/src/editor.ts b/lit-slash-menu/src/editor.ts index ef27a8ba7..7ea7548ab 100644 --- a/lit-slash-menu/src/editor.ts +++ b/lit-slash-menu/src/editor.ts @@ -1,7 +1,9 @@ -import './components/editor/examples/slash-menu' +import { registerLitEditor } from './components/editor/examples/slash-menu' import { LitElement, html } from 'lit' import { customElement } from 'lit/decorators.js' +registerLitEditor() + @customElement('my-editor') export class MyEditor extends LitElement { createRenderRoot() { From f0f4213d00b738ef2e25afd0bb8ffa8354e1e85e Mon Sep 17 00:00:00 2001 From: ocavue Date: Wed, 14 Jan 2026 17:21:25 +1100 Subject: [PATCH 10/10] fetch --- lit-minimal/src/app.ts | 4 ++++ lit-slash-menu/src/app.ts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/lit-minimal/src/app.ts b/lit-minimal/src/app.ts index 8f9339335..3cbf14f94 100644 --- a/lit-minimal/src/app.ts +++ b/lit-minimal/src/app.ts @@ -6,6 +6,10 @@ import { customElement } from 'lit/decorators.js' @customElement('my-app') export class MyApp extends LitElement { + createRenderRoot() { + return this + } + render() { return html` diff --git a/lit-slash-menu/src/app.ts b/lit-slash-menu/src/app.ts index 8f9339335..3cbf14f94 100644 --- a/lit-slash-menu/src/app.ts +++ b/lit-slash-menu/src/app.ts @@ -6,6 +6,10 @@ import { customElement } from 'lit/decorators.js' @customElement('my-app') export class MyApp extends LitElement { + createRenderRoot() { + return this + } + render() { return html`