diff --git a/.templates/template-nuxt/package.json b/.templates/template-nuxt/package.json
index 85434baa0e..44524469a6 100644
--- a/.templates/template-nuxt/package.json
+++ b/.templates/template-nuxt/package.json
@@ -10,7 +10,7 @@
"preview": "nuxt preview"
},
"dependencies": {
- "nuxt": "^4.3.0",
+ "nuxt": "^4.3.1",
"vue": "^3.5.27"
},
"devDependencies": {
diff --git a/lit-minimal/src/components/editor/examples/minimal/editor.ts b/lit-minimal/src/components/editor/examples/minimal/editor.ts
index 122b4ad8f6..5603677c48 100644
--- a/lit-minimal/src/components/editor/examples/minimal/editor.ts
+++ b/lit-minimal/src/components/editor/examples/minimal/editor.ts
@@ -35,6 +35,11 @@ export class LitEditor extends LitElement {
return this
}
+ override disconnectedCallback() {
+ this.editor.unmount()
+ super.disconnectedCallback()
+ }
+
override updated(changedProperties: PropertyValues) {
super.updated(changedProperties)
this.editor.mount(this.ref.value)
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
index 2dd8cc1dc3..eed4cda64b 100644
--- a/lit-slash-menu/src/components/editor/examples/slash-menu/editor.ts
+++ b/lit-slash-menu/src/components/editor/examples/slash-menu/editor.ts
@@ -38,6 +38,11 @@ export class LitEditor extends LitElement {
return this
}
+ override disconnectedCallback() {
+ this.editor.unmount()
+ super.disconnectedCallback()
+ }
+
override updated(changedProperties: PropertyValues) {
super.updated(changedProperties)
this.editor.mount(this.ref.value)
diff --git a/lit-toolbar/.gitignore b/lit-toolbar/.gitignore
new file mode 100644
index 0000000000..5d6225c6df
--- /dev/null
+++ b/lit-toolbar/.gitignore
@@ -0,0 +1,4 @@
+node_modules
+dist
+.next
+.svelte-kit
diff --git a/lit-toolbar/README.md b/lit-toolbar/README.md
new file mode 100644
index 0000000000..6f77188cb3
--- /dev/null
+++ b/lit-toolbar/README.md
@@ -0,0 +1,15 @@
+# lit-toolbar
+
+A [ProseKit](https://prosekit.dev) example.
+
+[](https://stackblitz.com/github/prosekit/examples/tree/master/lit-toolbar)
+[](https://codesandbox.io/p/sandbox/github/prosekit/examples/tree/master/lit-toolbar)
+
+Run the example locally with:
+
+```bash
+npx degit prosekit/examples/lit-toolbar lit-toolbar
+cd lit-toolbar
+npm install
+npm run dev
+```
diff --git a/lit-toolbar/index.html b/lit-toolbar/index.html
new file mode 100644
index 0000000000..9637b16269
--- /dev/null
+++ b/lit-toolbar/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+ ProseKit + Lit
+
+
+
+
+
+
+
diff --git a/lit-toolbar/package.json b/lit-toolbar/package.json
new file mode 100644
index 0000000000..5634ad8db1
--- /dev/null
+++ b/lit-toolbar/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "example-lit-toolbar",
+ "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.2",
+ "@iconify-json/lucide": "^1.2.89",
+ "@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-toolbar/src/app.css b/lit-toolbar/src/app.css
new file mode 100644
index 0000000000..5d0567ecf8
--- /dev/null
+++ b/lit-toolbar/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-toolbar/src/app.ts b/lit-toolbar/src/app.ts
new file mode 100644
index 0000000000..3cbf14f94d
--- /dev/null
+++ b/lit-toolbar/src/app.ts
@@ -0,0 +1,18 @@
+import './app.css'
+import './editor'
+
+import { LitElement, html } from 'lit'
+import { customElement } from 'lit/decorators.js'
+
+@customElement('my-app')
+export class MyApp extends LitElement {
+ createRenderRoot() {
+ return this
+ }
+
+ render() {
+ return html`
+
+ `
+ }
+}
diff --git a/lit-toolbar/src/components/editor/examples/toolbar/editor.ts b/lit-toolbar/src/components/editor/examples/toolbar/editor.ts
new file mode 100644
index 0000000000..64f4418acc
--- /dev/null
+++ b/lit-toolbar/src/components/editor/examples/toolbar/editor.ts
@@ -0,0 +1,82 @@
+import 'prosekit/basic/style.css'
+import 'prosekit/basic/typography.css'
+
+import '../../ui/toolbar/index'
+
+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 { sampleUploader } from '../../sample/sample-uploader'
+
+import { defineExtension } from './extension'
+
+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 disconnectedCallback() {
+ this.editor.unmount()
+ super.disconnectedCallback()
+ }
+
+ 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-toolbar')) return
+ customElements.define('lit-editor-example-toolbar', LitEditor)
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'lit-editor-example-toolbar': LitEditor
+ }
+}
diff --git a/lit-toolbar/src/components/editor/examples/toolbar/extension.ts b/lit-toolbar/src/components/editor/examples/toolbar/extension.ts
new file mode 100644
index 0000000000..1c2b5cbd74
--- /dev/null
+++ b/lit-toolbar/src/components/editor/examples/toolbar/extension.ts
@@ -0,0 +1,8 @@
+import { defineBasicExtension } from 'prosekit/basic'
+import { union } from 'prosekit/core'
+
+export function defineExtension() {
+ return union(defineBasicExtension())
+}
+
+export type EditorExtension = ReturnType
diff --git a/lit-toolbar/src/components/editor/examples/toolbar/index.ts b/lit-toolbar/src/components/editor/examples/toolbar/index.ts
new file mode 100644
index 0000000000..9eeecbbc50
--- /dev/null
+++ b/lit-toolbar/src/components/editor/examples/toolbar/index.ts
@@ -0,0 +1 @@
+export { LitEditor as ExampleEditor, registerLitEditor } from './editor'
diff --git a/lit-toolbar/src/components/editor/sample/sample-uploader.ts b/lit-toolbar/src/components/editor/sample/sample-uploader.ts
new file mode 100644
index 0000000000..8e86d92105
--- /dev/null
+++ b/lit-toolbar/src/components/editor/sample/sample-uploader.ts
@@ -0,0 +1,54 @@
+import type { Uploader } from 'prosekit/extensions/file'
+
+/**
+ * Uploads the given file to https://tmpfiles.org/ and returns the URL of the
+ * uploaded file.
+ *
+ * This function is only for demonstration purposes. All uploaded files will be
+ * deleted by the server after 1 hour.
+ */
+export const sampleUploader: Uploader = ({
+ file,
+ onProgress,
+}): Promise => {
+ return new Promise((resolve, reject) => {
+ const xhr = new XMLHttpRequest()
+ const formData = new FormData()
+ formData.append('file', file)
+
+ xhr.upload.addEventListener('progress', (event) => {
+ if (event.lengthComputable) {
+ onProgress({
+ loaded: event.loaded,
+ total: event.total,
+ })
+ }
+ })
+
+ xhr.addEventListener('load', () => {
+ if (xhr.status === 200) {
+ try {
+ const json = JSON.parse(xhr.responseText) as { data: { url: string } }
+ const url: string = json.data.url.replace(
+ 'tmpfiles.org/',
+ 'tmpfiles.org/dl/',
+ )
+
+ // Simulate a larger delay
+ setTimeout(() => resolve(url), 1000)
+ } catch (error) {
+ reject(new Error('Failed to parse response', { cause: error }))
+ }
+ } else {
+ reject(new Error(`Upload failed with status ${xhr.status}`))
+ }
+ })
+
+ xhr.addEventListener('error', () => {
+ reject(new Error('Upload failed'))
+ })
+
+ xhr.open('POST', 'https://tmpfiles.org/api/v1/upload', true)
+ xhr.send(formData)
+ })
+}
diff --git a/lit-toolbar/src/components/editor/ui/button/button.ts b/lit-toolbar/src/components/editor/ui/button/button.ts
new file mode 100644
index 0000000000..29d27bccff
--- /dev/null
+++ b/lit-toolbar/src/components/editor/ui/button/button.ts
@@ -0,0 +1,76 @@
+import 'prosekit/lit/tooltip'
+
+import { html, LitElement, nothing, type PropertyDeclaration } from 'lit'
+
+class LitButton extends LitElement {
+ static override properties = {
+ pressed: { type: Boolean },
+ disabled: { type: Boolean },
+ tooltip: { type: String },
+ icon: { type: String },
+ } satisfies Record
+
+ pressed = false
+ disabled = false
+ tooltip = ''
+ icon = ''
+
+ override createRenderRoot() {
+ return this
+ }
+
+ override connectedCallback() {
+ super.connectedCallback()
+ this.classList.add('contents')
+ }
+
+ private handleMouseDown = (event: MouseEvent) => {
+ // Prevent the editor from being blurred when the button is clicked
+ event.preventDefault()
+ }
+
+ override render() {
+ const tooltip = this.tooltip
+
+ return html`
+
+
+
+
+ ${tooltip
+ ? html`
+
+ ${tooltip}
+
+ `
+ : nothing}
+
+ `
+ }
+}
+
+customElements.define('lit-editor-button', LitButton)
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'lit-editor-button': LitButton
+ }
+}
diff --git a/lit-toolbar/src/components/editor/ui/button/index.ts b/lit-toolbar/src/components/editor/ui/button/index.ts
new file mode 100644
index 0000000000..ef28ef97fb
--- /dev/null
+++ b/lit-toolbar/src/components/editor/ui/button/index.ts
@@ -0,0 +1 @@
+import './button'
diff --git a/lit-toolbar/src/components/editor/ui/image-upload-popover/image-upload-popover.ts b/lit-toolbar/src/components/editor/ui/image-upload-popover/image-upload-popover.ts
new file mode 100644
index 0000000000..57c1f68f0d
--- /dev/null
+++ b/lit-toolbar/src/components/editor/ui/image-upload-popover/image-upload-popover.ts
@@ -0,0 +1,179 @@
+import 'prosekit/lit/popover'
+import '../button/index'
+
+import { html, LitElement, nothing, type PropertyDeclaration } from 'lit'
+import type { Editor } from 'prosekit/core'
+import type { Uploader } from 'prosekit/extensions/file'
+import type { ImageExtension } from 'prosekit/extensions/image'
+
+let imageUploadId = 0
+
+class LitImageUploadPopover extends LitElement {
+ static override properties = {
+ editor: { attribute: false } satisfies PropertyDeclaration,
+ uploader: { attribute: false } satisfies PropertyDeclaration<
+ Uploader
+ >,
+ tooltip: { type: String },
+ disabled: { type: Boolean },
+ icon: { type: String },
+ }
+
+ editor?: Editor
+ uploader?: Uploader
+ tooltip = ''
+ disabled = false
+ icon = ''
+
+ private open = false
+ private url = ''
+ private file: File | null = null
+ private ariaId = `lit-image-upload-${imageUploadId++}`
+
+ override createRenderRoot() {
+ return this
+ }
+
+ override connectedCallback() {
+ super.connectedCallback()
+ this.classList.add('contents')
+ }
+
+ private handleOpenChange = (event: CustomEvent) => {
+ const isOpen = event.detail
+
+ if (!isOpen) {
+ this.deferResetState()
+ }
+
+ this.open = isOpen
+ this.requestUpdate()
+ }
+
+ private handleFileChange = (event: Event) => {
+ const target = event.target as HTMLInputElement
+ const selectedFile = target.files?.[0]
+
+ if (selectedFile) {
+ this.file = selectedFile
+ this.url = ''
+ } else {
+ this.file = null
+ }
+
+ this.requestUpdate()
+ }
+
+ private handleUrlChange = (event: Event) => {
+ const target = event.target as HTMLInputElement
+ const inputUrl = target.value
+
+ if (inputUrl) {
+ this.url = inputUrl
+ this.file = null
+ } else {
+ this.url = ''
+ }
+
+ this.requestUpdate()
+ }
+
+ private deferResetState() {
+ setTimeout(() => {
+ this.url = ''
+ this.file = null
+ this.requestUpdate()
+ }, 300)
+ }
+
+ private handleSubmit = () => {
+ const editor = this.editor
+ if (!editor) return
+
+ if (this.url) {
+ editor.commands.insertImage({ src: this.url })
+ } else if (this.file && this.uploader) {
+ editor.commands.uploadImage({ file: this.file, uploader: this.uploader })
+ }
+
+ this.open = false
+ this.deferResetState()
+ this.requestUpdate()
+ }
+
+ override render() {
+ return html`
+
+
+
+
+
+
+ ${!this.file
+ ? html`
+
+
+ `
+ : nothing}
+ ${!this.url
+ ? html`
+
+
+ `
+ : nothing}
+ ${this.url
+ ? html`
+
+ `
+ : nothing}
+ ${this.file
+ ? html`
+
+ `
+ : nothing}
+
+
+ `
+ }
+}
+
+customElements.define('lit-editor-image-upload-popover', LitImageUploadPopover)
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'lit-editor-image-upload-popover': LitImageUploadPopover
+ }
+}
diff --git a/lit-toolbar/src/components/editor/ui/image-upload-popover/index.ts b/lit-toolbar/src/components/editor/ui/image-upload-popover/index.ts
new file mode 100644
index 0000000000..cf1b5dfb30
--- /dev/null
+++ b/lit-toolbar/src/components/editor/ui/image-upload-popover/index.ts
@@ -0,0 +1 @@
+import './image-upload-popover'
diff --git a/lit-toolbar/src/components/editor/ui/toolbar/index.ts b/lit-toolbar/src/components/editor/ui/toolbar/index.ts
new file mode 100644
index 0000000000..743d96b731
--- /dev/null
+++ b/lit-toolbar/src/components/editor/ui/toolbar/index.ts
@@ -0,0 +1 @@
+import './toolbar'
diff --git a/lit-toolbar/src/components/editor/ui/toolbar/toolbar.ts b/lit-toolbar/src/components/editor/ui/toolbar/toolbar.ts
new file mode 100644
index 0000000000..ed2034dadb
--- /dev/null
+++ b/lit-toolbar/src/components/editor/ui/toolbar/toolbar.ts
@@ -0,0 +1,456 @@
+import '../button/index'
+import '../image-upload-popover/index'
+
+import {
+ html,
+ LitElement,
+ nothing,
+ type PropertyDeclaration,
+ type PropertyValues,
+} from 'lit'
+import type { BasicExtension } from 'prosekit/basic'
+import { defineUpdateHandler, type Editor } from 'prosekit/core'
+import type { Uploader } from 'prosekit/extensions/file'
+
+function getToolbarItems(editor: Editor) {
+ return {
+ undo: editor.commands.undo
+ ? {
+ isActive: false,
+ canExec: editor.commands.undo.canExec(),
+ command: () => editor.commands.undo(),
+ }
+ : undefined,
+ redo: editor.commands.redo
+ ? {
+ isActive: false,
+ canExec: editor.commands.redo.canExec(),
+ command: () => editor.commands.redo(),
+ }
+ : undefined,
+ bold: editor.commands.toggleBold
+ ? {
+ isActive: editor.marks.bold.isActive(),
+ canExec: editor.commands.toggleBold.canExec(),
+ command: () => editor.commands.toggleBold(),
+ }
+ : undefined,
+ italic: editor.commands.toggleItalic
+ ? {
+ isActive: editor.marks.italic.isActive(),
+ canExec: editor.commands.toggleItalic.canExec(),
+ command: () => editor.commands.toggleItalic(),
+ }
+ : undefined,
+ underline: editor.commands.toggleUnderline
+ ? {
+ isActive: editor.marks.underline.isActive(),
+ canExec: editor.commands.toggleUnderline.canExec(),
+ command: () => editor.commands.toggleUnderline(),
+ }
+ : undefined,
+ strike: editor.commands.toggleStrike
+ ? {
+ isActive: editor.marks.strike.isActive(),
+ canExec: editor.commands.toggleStrike.canExec(),
+ command: () => editor.commands.toggleStrike(),
+ }
+ : undefined,
+ code: editor.commands.toggleCode
+ ? {
+ isActive: editor.marks.code.isActive(),
+ canExec: editor.commands.toggleCode.canExec(),
+ command: () => editor.commands.toggleCode(),
+ }
+ : undefined,
+ codeBlock: editor.commands.insertCodeBlock
+ ? {
+ isActive: editor.nodes.codeBlock.isActive(),
+ canExec: editor.commands.insertCodeBlock.canExec({
+ language: 'javascript',
+ }),
+ command: () =>
+ editor.commands.insertCodeBlock({ language: 'javascript' }),
+ }
+ : undefined,
+ heading1: editor.commands.toggleHeading
+ ? {
+ isActive: editor.nodes.heading.isActive({ level: 1 }),
+ canExec: editor.commands.toggleHeading.canExec({ level: 1 }),
+ command: () => editor.commands.toggleHeading({ level: 1 }),
+ }
+ : undefined,
+ heading2: editor.commands.toggleHeading
+ ? {
+ isActive: editor.nodes.heading.isActive({ level: 2 }),
+ canExec: editor.commands.toggleHeading.canExec({ level: 2 }),
+ command: () => editor.commands.toggleHeading({ level: 2 }),
+ }
+ : undefined,
+ heading3: editor.commands.toggleHeading
+ ? {
+ isActive: editor.nodes.heading.isActive({ level: 3 }),
+ canExec: editor.commands.toggleHeading.canExec({ level: 3 }),
+ command: () => editor.commands.toggleHeading({ level: 3 }),
+ }
+ : undefined,
+ horizontalRule: editor.commands.insertHorizontalRule
+ ? {
+ isActive: editor.nodes.horizontalRule.isActive(),
+ canExec: editor.commands.insertHorizontalRule.canExec(),
+ command: () => editor.commands.insertHorizontalRule(),
+ }
+ : undefined,
+ blockquote: editor.commands.toggleBlockquote
+ ? {
+ isActive: editor.nodes.blockquote.isActive(),
+ canExec: editor.commands.toggleBlockquote.canExec(),
+ command: () => editor.commands.toggleBlockquote(),
+ }
+ : undefined,
+ bulletList: editor.commands.toggleList
+ ? {
+ isActive: editor.nodes.list.isActive({ kind: 'bullet' }),
+ canExec: editor.commands.toggleList.canExec({ kind: 'bullet' }),
+ command: () => editor.commands.toggleList({ kind: 'bullet' }),
+ }
+ : undefined,
+ orderedList: editor.commands.toggleList
+ ? {
+ isActive: editor.nodes.list.isActive({ kind: 'ordered' }),
+ canExec: editor.commands.toggleList.canExec({ kind: 'ordered' }),
+ command: () => editor.commands.toggleList({ kind: 'ordered' }),
+ }
+ : undefined,
+ taskList: editor.commands.toggleList
+ ? {
+ isActive: editor.nodes.list.isActive({ kind: 'task' }),
+ canExec: editor.commands.toggleList.canExec({ kind: 'task' }),
+ command: () => editor.commands.toggleList({ kind: 'task' }),
+ }
+ : undefined,
+ toggleList: editor.commands.toggleList
+ ? {
+ isActive: editor.nodes.list.isActive({ kind: 'toggle' }),
+ canExec: editor.commands.toggleList.canExec({ kind: 'toggle' }),
+ command: () => editor.commands.toggleList({ kind: 'toggle' }),
+ }
+ : undefined,
+ indentList: editor.commands.indentList
+ ? {
+ isActive: false,
+ canExec: editor.commands.indentList.canExec(),
+ command: () => editor.commands.indentList(),
+ }
+ : undefined,
+ dedentList: editor.commands.dedentList
+ ? {
+ isActive: false,
+ canExec: editor.commands.dedentList.canExec(),
+ command: () => editor.commands.dedentList(),
+ }
+ : undefined,
+ insertImage: editor.commands.insertImage
+ ? {
+ isActive: false,
+ canExec: editor.commands.insertImage.canExec(),
+ }
+ : undefined,
+ }
+}
+
+class LitToolbar extends LitElement {
+ static override properties = {
+ editor: { attribute: false } satisfies PropertyDeclaration,
+ uploader: { attribute: false } satisfies PropertyDeclaration<
+ Uploader
+ >,
+ }
+
+ editor?: Editor
+ uploader?: Uploader
+
+ private removeUpdateExtension?: VoidFunction
+
+ override createRenderRoot() {
+ return this
+ }
+
+ override connectedCallback() {
+ super.connectedCallback()
+ this.classList.add('contents')
+ this.attachEditorListener()
+ }
+
+ override disconnectedCallback() {
+ this.detachEditorListener()
+ super.disconnectedCallback()
+ }
+
+ override updated(changedProperties: PropertyValues) {
+ super.updated(changedProperties)
+
+ if (changedProperties.has('editor')) {
+ this.attachEditorListener()
+ }
+ }
+
+ private attachEditorListener() {
+ this.detachEditorListener()
+
+ if (!this.editor) return
+
+ this.removeUpdateExtension = this.editor.use(
+ defineUpdateHandler(() => this.requestUpdate()),
+ )
+ }
+
+ private detachEditorListener() {
+ this.removeUpdateExtension?.()
+ this.removeUpdateExtension = undefined
+ }
+
+ override render() {
+ const editor = this.editor
+ if (!editor) {
+ return nothing
+ }
+
+ const items = getToolbarItems(editor)
+
+ return html`
+
+ ${items.undo
+ ? html`
+
+ `
+ : nothing}
+ ${items.redo
+ ? html`
+
+ `
+ : nothing}
+ ${items.bold
+ ? html`
+
+ `
+ : nothing}
+ ${items.italic
+ ? html`
+
+ `
+ : nothing}
+ ${items.underline
+ ? html`
+
+ `
+ : nothing}
+ ${items.strike
+ ? html`
+
+ `
+ : nothing}
+ ${items.code
+ ? html`
+
+ `
+ : nothing}
+ ${items.codeBlock
+ ? html`
+
+ `
+ : nothing}
+ ${items.heading1
+ ? html`
+
+ `
+ : nothing}
+ ${items.heading2
+ ? html`
+
+ `
+ : nothing}
+ ${items.heading3
+ ? html`
+
+ `
+ : nothing}
+ ${items.horizontalRule
+ ? html`
+
+ `
+ : nothing}
+ ${items.blockquote
+ ? html`
+
+ `
+ : nothing}
+ ${items.bulletList
+ ? html`
+
+ `
+ : nothing}
+ ${items.orderedList
+ ? html`
+
+ `
+ : nothing}
+ ${items.taskList
+ ? html`
+
+ `
+ : nothing}
+ ${items.toggleList
+ ? html`
+
+ `
+ : nothing}
+ ${items.indentList
+ ? html`
+
+ `
+ : nothing}
+ ${items.dedentList
+ ? html`
+
+ `
+ : nothing}
+ ${this.uploader && items.insertImage
+ ? html`
+
+ `
+ : nothing}
+
+ `
+ }
+}
+
+customElements.define('lit-editor-toolbar', LitToolbar)
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'lit-editor-toolbar': LitToolbar
+ }
+}
diff --git a/lit-toolbar/src/editor.ts b/lit-toolbar/src/editor.ts
new file mode 100644
index 0000000000..6eed3b2649
--- /dev/null
+++ b/lit-toolbar/src/editor.ts
@@ -0,0 +1,18 @@
+import { registerLitEditor } from './components/editor/examples/toolbar'
+import { LitElement, html } from 'lit'
+import { customElement } from 'lit/decorators.js'
+
+registerLitEditor()
+
+@customElement('my-editor')
+export class MyEditor extends LitElement {
+ createRenderRoot() {
+ return this
+ }
+
+ render() {
+ return html`
+
+ `
+ }
+}
diff --git a/lit-toolbar/tsconfig.json b/lit-toolbar/tsconfig.json
new file mode 100644
index 0000000000..1de5d357aa
--- /dev/null
+++ b/lit-toolbar/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-toolbar/vite.config.ts b/lit-toolbar/vite.config.ts
new file mode 100644
index 0000000000..fb0cdf00b5
--- /dev/null
+++ b/lit-toolbar/vite.config.ts
@@ -0,0 +1,6 @@
+import { defineConfig } from 'vite'
+import tailwindcss from '@tailwindcss/vite'
+
+export default defineConfig({
+ plugins: [tailwindcss()],
+})
diff --git a/nuxt-full/package.json b/nuxt-full/package.json
index 77e9cdf1a8..aac2aada78 100644
--- a/nuxt-full/package.json
+++ b/nuxt-full/package.json
@@ -10,7 +10,7 @@
"preview": "nuxt preview"
},
"dependencies": {
- "nuxt": "^4.3.0",
+ "nuxt": "^4.3.1",
"prosekit": "^0.17.1",
"vue": "^3.5.27"
},