From a461d42c1314054ff51583216708683f2fd9f652 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 08:41:31 +0000 Subject: [PATCH 1/2] chore(deps): update dependency prosekit-registry to v0.0.20260519064721 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5a35227fac..d79c4d2e25 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,6 @@ "@types/node": "24.12.4", "prettier": "3.8.3", "prettier-plugin-packagejson": "3.0.2", - "prosekit-registry": "0.0.20260516183323" + "prosekit-registry": "0.0.20260519064721" } } From 1552686c12418255743e5357330005166dd8a8fd Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 08:42:31 +0000 Subject: [PATCH 2/2] [autofix.ci] apply automated fixes --- lit-inline-menu/.gitignore | 4 + lit-inline-menu/README.md | 15 + lit-inline-menu/index.html | 13 + lit-inline-menu/package.json | 24 ++ lit-inline-menu/src/app.css | 12 + lit-inline-menu/src/app.ts | 18 + .../editor/examples/inline-menu/editor.ts | 87 +++++ .../editor/examples/inline-menu/extension.ts | 7 + .../editor/examples/inline-menu/index.ts | 1 + .../editor/sample/sample-doc-inline-menu.ts | 33 ++ .../src/components/editor/ui/button/button.ts | 92 +++++ .../src/components/editor/ui/button/index.ts | 1 + .../components/editor/ui/editor-context.ts | 6 + .../components/editor/ui/inline-menu/index.ts | 1 + .../editor/ui/inline-menu/inline-menu.ts | 320 ++++++++++++++++++ lit-inline-menu/src/editor.ts | 18 + lit-inline-menu/tsconfig.json | 25 ++ lit-inline-menu/vite.config.ts | 6 + preact-font-family/.gitignore | 4 + preact-font-family/README.md | 15 + preact-font-family/index.html | 12 + preact-font-family/package.json | 25 ++ preact-font-family/src/App.tsx | 5 + preact-font-family/src/app.css | 12 + .../editor/examples/font-family/editor.tsx | 37 ++ .../editor/examples/font-family/extension.ts | 9 + .../editor/examples/font-family/index.ts | 1 + .../editor/examples/font-family/toolbar.tsx | 88 +++++ .../editor/sample/sample-doc-font-family.ts | 80 +++++ preact-font-family/src/main.tsx | 5 + preact-font-family/tsconfig.app.json | 33 ++ preact-font-family/tsconfig.json | 7 + preact-font-family/tsconfig.node.json | 26 ++ preact-font-family/vite.config.ts | 8 + react-font-family/.gitignore | 4 + react-font-family/README.md | 15 + react-font-family/index.html | 12 + react-font-family/package.json | 28 ++ react-font-family/src/App.tsx | 5 + react-font-family/src/app.css | 12 + .../editor/examples/font-family/editor.tsx | 39 +++ .../editor/examples/font-family/extension.ts | 9 + .../editor/examples/font-family/index.ts | 1 + .../editor/examples/font-family/toolbar.tsx | 88 +++++ .../editor/sample/sample-doc-font-family.ts | 80 +++++ react-font-family/src/main.tsx | 10 + react-font-family/tsconfig.app.json | 28 ++ react-font-family/tsconfig.json | 7 + react-font-family/tsconfig.node.json | 26 ++ react-font-family/vite.config.ts | 8 + solid-font-family/.gitignore | 4 + solid-font-family/README.md | 15 + solid-font-family/index.html | 12 + solid-font-family/package.json | 25 ++ solid-font-family/src/App.tsx | 5 + solid-font-family/src/app.css | 12 + .../editor/examples/font-family/editor.tsx | 35 ++ .../editor/examples/font-family/extension.ts | 9 + .../editor/examples/font-family/index.ts | 1 + .../editor/examples/font-family/toolbar.tsx | 82 +++++ .../editor/sample/sample-doc-font-family.ts | 80 +++++ solid-font-family/src/index.tsx | 8 + solid-font-family/tsconfig.app.json | 29 ++ solid-font-family/tsconfig.json | 7 + solid-font-family/tsconfig.node.json | 26 ++ solid-font-family/vite.config.ts | 7 + svelte-font-family/.gitignore | 4 + svelte-font-family/README.md | 15 + svelte-font-family/index.html | 12 + svelte-font-family/package.json | 28 ++ svelte-font-family/src/App.svelte | 5 + svelte-font-family/src/app.css | 12 + .../editor/examples/font-family/editor.svelte | 30 ++ .../editor/examples/font-family/extension.ts | 9 + .../editor/examples/font-family/index.ts | 1 + .../examples/font-family/toolbar.svelte | 75 ++++ .../editor/sample/sample-doc-font-family.ts | 80 +++++ svelte-font-family/src/main.ts | 8 + svelte-font-family/src/vite-env.d.ts | 2 + svelte-font-family/svelte.config.js | 7 + svelte-font-family/tsconfig.json | 21 ++ svelte-font-family/tsconfig.node.json | 9 + svelte-font-family/vite.config.ts | 8 + vue-font-family/.gitignore | 4 + vue-font-family/README.md | 15 + vue-font-family/index.html | 12 + vue-font-family/package.json | 27 ++ vue-font-family/src/App.vue | 7 + vue-font-family/src/app.css | 12 + .../editor/examples/font-family/editor.vue | 36 ++ .../editor/examples/font-family/extension.ts | 9 + .../editor/examples/font-family/index.ts | 1 + .../editor/examples/font-family/toolbar.vue | 87 +++++ .../editor/sample/sample-doc-font-family.ts | 80 +++++ vue-font-family/src/main.ts | 5 + vue-font-family/tsconfig.app.json | 16 + vue-font-family/tsconfig.json | 7 + vue-font-family/tsconfig.node.json | 26 ++ vue-font-family/vite.config.ts | 8 + 99 files changed, 2457 insertions(+) create mode 100644 lit-inline-menu/.gitignore create mode 100644 lit-inline-menu/README.md create mode 100644 lit-inline-menu/index.html create mode 100644 lit-inline-menu/package.json create mode 100644 lit-inline-menu/src/app.css create mode 100644 lit-inline-menu/src/app.ts create mode 100644 lit-inline-menu/src/components/editor/examples/inline-menu/editor.ts create mode 100644 lit-inline-menu/src/components/editor/examples/inline-menu/extension.ts create mode 100644 lit-inline-menu/src/components/editor/examples/inline-menu/index.ts create mode 100644 lit-inline-menu/src/components/editor/sample/sample-doc-inline-menu.ts create mode 100644 lit-inline-menu/src/components/editor/ui/button/button.ts create mode 100644 lit-inline-menu/src/components/editor/ui/button/index.ts create mode 100644 lit-inline-menu/src/components/editor/ui/editor-context.ts create mode 100644 lit-inline-menu/src/components/editor/ui/inline-menu/index.ts create mode 100644 lit-inline-menu/src/components/editor/ui/inline-menu/inline-menu.ts create mode 100644 lit-inline-menu/src/editor.ts create mode 100644 lit-inline-menu/tsconfig.json create mode 100644 lit-inline-menu/vite.config.ts create mode 100644 preact-font-family/.gitignore create mode 100644 preact-font-family/README.md create mode 100644 preact-font-family/index.html create mode 100644 preact-font-family/package.json create mode 100644 preact-font-family/src/App.tsx create mode 100644 preact-font-family/src/app.css create mode 100644 preact-font-family/src/components/editor/examples/font-family/editor.tsx create mode 100644 preact-font-family/src/components/editor/examples/font-family/extension.ts create mode 100644 preact-font-family/src/components/editor/examples/font-family/index.ts create mode 100644 preact-font-family/src/components/editor/examples/font-family/toolbar.tsx create mode 100644 preact-font-family/src/components/editor/sample/sample-doc-font-family.ts create mode 100644 preact-font-family/src/main.tsx create mode 100644 preact-font-family/tsconfig.app.json create mode 100644 preact-font-family/tsconfig.json create mode 100644 preact-font-family/tsconfig.node.json create mode 100644 preact-font-family/vite.config.ts create mode 100644 react-font-family/.gitignore create mode 100644 react-font-family/README.md create mode 100644 react-font-family/index.html create mode 100644 react-font-family/package.json create mode 100644 react-font-family/src/App.tsx create mode 100644 react-font-family/src/app.css create mode 100644 react-font-family/src/components/editor/examples/font-family/editor.tsx create mode 100644 react-font-family/src/components/editor/examples/font-family/extension.ts create mode 100644 react-font-family/src/components/editor/examples/font-family/index.ts create mode 100644 react-font-family/src/components/editor/examples/font-family/toolbar.tsx create mode 100644 react-font-family/src/components/editor/sample/sample-doc-font-family.ts create mode 100644 react-font-family/src/main.tsx create mode 100644 react-font-family/tsconfig.app.json create mode 100644 react-font-family/tsconfig.json create mode 100644 react-font-family/tsconfig.node.json create mode 100644 react-font-family/vite.config.ts create mode 100644 solid-font-family/.gitignore create mode 100644 solid-font-family/README.md create mode 100644 solid-font-family/index.html create mode 100644 solid-font-family/package.json create mode 100644 solid-font-family/src/App.tsx create mode 100644 solid-font-family/src/app.css create mode 100644 solid-font-family/src/components/editor/examples/font-family/editor.tsx create mode 100644 solid-font-family/src/components/editor/examples/font-family/extension.ts create mode 100644 solid-font-family/src/components/editor/examples/font-family/index.ts create mode 100644 solid-font-family/src/components/editor/examples/font-family/toolbar.tsx create mode 100644 solid-font-family/src/components/editor/sample/sample-doc-font-family.ts create mode 100644 solid-font-family/src/index.tsx create mode 100644 solid-font-family/tsconfig.app.json create mode 100644 solid-font-family/tsconfig.json create mode 100644 solid-font-family/tsconfig.node.json create mode 100644 solid-font-family/vite.config.ts create mode 100644 svelte-font-family/.gitignore create mode 100644 svelte-font-family/README.md create mode 100644 svelte-font-family/index.html create mode 100644 svelte-font-family/package.json create mode 100644 svelte-font-family/src/App.svelte create mode 100644 svelte-font-family/src/app.css create mode 100644 svelte-font-family/src/components/editor/examples/font-family/editor.svelte create mode 100644 svelte-font-family/src/components/editor/examples/font-family/extension.ts create mode 100644 svelte-font-family/src/components/editor/examples/font-family/index.ts create mode 100644 svelte-font-family/src/components/editor/examples/font-family/toolbar.svelte create mode 100644 svelte-font-family/src/components/editor/sample/sample-doc-font-family.ts create mode 100644 svelte-font-family/src/main.ts create mode 100644 svelte-font-family/src/vite-env.d.ts create mode 100644 svelte-font-family/svelte.config.js create mode 100644 svelte-font-family/tsconfig.json create mode 100644 svelte-font-family/tsconfig.node.json create mode 100644 svelte-font-family/vite.config.ts create mode 100644 vue-font-family/.gitignore create mode 100644 vue-font-family/README.md create mode 100644 vue-font-family/index.html create mode 100644 vue-font-family/package.json create mode 100644 vue-font-family/src/App.vue create mode 100644 vue-font-family/src/app.css create mode 100644 vue-font-family/src/components/editor/examples/font-family/editor.vue create mode 100644 vue-font-family/src/components/editor/examples/font-family/extension.ts create mode 100644 vue-font-family/src/components/editor/examples/font-family/index.ts create mode 100644 vue-font-family/src/components/editor/examples/font-family/toolbar.vue create mode 100644 vue-font-family/src/components/editor/sample/sample-doc-font-family.ts create mode 100644 vue-font-family/src/main.ts create mode 100644 vue-font-family/tsconfig.app.json create mode 100644 vue-font-family/tsconfig.json create mode 100644 vue-font-family/tsconfig.node.json create mode 100644 vue-font-family/vite.config.ts diff --git a/lit-inline-menu/.gitignore b/lit-inline-menu/.gitignore new file mode 100644 index 0000000000..5d6225c6df --- /dev/null +++ b/lit-inline-menu/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.next +.svelte-kit diff --git a/lit-inline-menu/README.md b/lit-inline-menu/README.md new file mode 100644 index 0000000000..5a11896fdd --- /dev/null +++ b/lit-inline-menu/README.md @@ -0,0 +1,15 @@ +# lit-inline-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-inline-menu) +[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/prosekit/examples/tree/master/lit-inline-menu) + +Run the example locally with: + +```bash +npx degit prosekit/examples/lit-inline-menu lit-inline-menu +cd lit-inline-menu +npm install +npm run dev +``` diff --git a/lit-inline-menu/index.html b/lit-inline-menu/index.html new file mode 100644 index 0000000000..9637b16269 --- /dev/null +++ b/lit-inline-menu/index.html @@ -0,0 +1,13 @@ + + + + + + ProseKit + Lit + + + + + + + diff --git a/lit-inline-menu/package.json b/lit-inline-menu/package.json new file mode 100644 index 0000000000..4379618814 --- /dev/null +++ b/lit-inline-menu/package.json @@ -0,0 +1,24 @@ +{ + "name": "example-lit-inline-menu", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "tsc && vite build", + "dev": "vite", + "preview": "vite preview" + }, + "dependencies": { + "@lit/context": "^1.1.6", + "lit": "^3.3.3", + "prosekit": "^0.21.1" + }, + "devDependencies": { + "@egoist/tailwindcss-icons": "^1.9.2", + "@iconify-json/lucide": "^1.2.107", + "@tailwindcss/vite": "^4.3.0", + "tailwindcss": "^4.3.0", + "typescript": "^6.0.3", + "vite": "^8.0.13" + } +} diff --git a/lit-inline-menu/src/app.css b/lit-inline-menu/src/app.css new file mode 100644 index 0000000000..ccad1aefaa --- /dev/null +++ b/lit-inline-menu/src/app.css @@ -0,0 +1,12 @@ +@import 'tailwindcss'; + +@plugin "@egoist/tailwindcss-icons"; + +body { + height: 100svh; + display: grid; + max-width: 900px; + padding: 16px; + margin-left: auto; + margin-right: auto; +} diff --git a/lit-inline-menu/src/app.ts b/lit-inline-menu/src/app.ts new file mode 100644 index 0000000000..3cbf14f94d --- /dev/null +++ b/lit-inline-menu/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-inline-menu/src/components/editor/examples/inline-menu/editor.ts b/lit-inline-menu/src/components/editor/examples/inline-menu/editor.ts new file mode 100644 index 0000000000..b461ac2cb4 --- /dev/null +++ b/lit-inline-menu/src/components/editor/examples/inline-menu/editor.ts @@ -0,0 +1,87 @@ +import 'prosekit/basic/style.css' +import 'prosekit/basic/typography.css' + +import { ContextProvider } from '@lit/context' +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 { sampleContent } from '../../sample/sample-doc-inline-menu' +import { editorContext } from '../../ui/editor-context' +import { registerLitEditorInlineMenu } from '../../ui/inline-menu' + +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, defaultContent: sampleContent }) + this.ref = createRef() + new ContextProvider(this, { + context: editorContext, + initialValue: this.editor, + }) + } + + 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() { + registerLitEditorInlineMenu() + + if (customElements.get('lit-editor-example-inline-menu')) return + customElements.define('lit-editor-example-inline-menu', LitEditor) +} + +declare global { + interface HTMLElementTagNameMap { + 'lit-editor-example-inline-menu': LitEditor + } +} diff --git a/lit-inline-menu/src/components/editor/examples/inline-menu/extension.ts b/lit-inline-menu/src/components/editor/examples/inline-menu/extension.ts new file mode 100644 index 0000000000..15210b5dc0 --- /dev/null +++ b/lit-inline-menu/src/components/editor/examples/inline-menu/extension.ts @@ -0,0 +1,7 @@ +import { defineBasicExtension } from 'prosekit/basic' + +export function defineExtension() { + return defineBasicExtension() +} + +export type EditorExtension = ReturnType diff --git a/lit-inline-menu/src/components/editor/examples/inline-menu/index.ts b/lit-inline-menu/src/components/editor/examples/inline-menu/index.ts new file mode 100644 index 0000000000..9eeecbbc50 --- /dev/null +++ b/lit-inline-menu/src/components/editor/examples/inline-menu/index.ts @@ -0,0 +1 @@ +export { LitEditor as ExampleEditor, registerLitEditor } from './editor' diff --git a/lit-inline-menu/src/components/editor/sample/sample-doc-inline-menu.ts b/lit-inline-menu/src/components/editor/sample/sample-doc-inline-menu.ts new file mode 100644 index 0000000000..62a5984cb0 --- /dev/null +++ b/lit-inline-menu/src/components/editor/sample/sample-doc-inline-menu.ts @@ -0,0 +1,33 @@ +import type { NodeJSON } from 'prosekit/core' + +const loremText = + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Aliquet nec ullamcorper sit amet risus. Nam aliquam sem et tortor consequat id porta. Interdum posuere lorem ipsum dolor sit amet. Lectus sit amet est placerat in egestas erat. Egestas sed tempus urna et pharetra pharetra. Sit amet cursus sit amet dictum sit amet. Porttitor leo a diam sollicitudin. Tellus orci ac auctor augue. Tellus in hac habitasse platea dictumst vestibulum. At elementum eu facilisis sed odio morbi. Dolor magna eget est lorem ipsum. Et malesuada fames ac turpis egestas. Arcu risus quis varius quam quisque id diam. Purus viverra accumsan in nisl nisi scelerisque eu ultrices. Ut tortor pretium viverra suspendisse potenti nullam ac tortor vitae.' + +export const sampleContent: NodeJSON = { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + marks: [ + { + type: 'bold', + }, + ], + text: 'Try to select some text', + }, + ], + }, + ...Array.from({ length: 10 }, () => ({ + type: 'paragraph' as const, + content: [ + { + type: 'text' as const, + text: loremText, + }, + ], + })), + ], +} diff --git a/lit-inline-menu/src/components/editor/ui/button/button.ts b/lit-inline-menu/src/components/editor/ui/button/button.ts new file mode 100644 index 0000000000..aef47e4e1f --- /dev/null +++ b/lit-inline-menu/src/components/editor/ui/button/button.ts @@ -0,0 +1,92 @@ +import { html, LitElement, nothing, type PropertyDeclaration } from 'lit' +import { + registerTooltipPopupElement, + registerTooltipPositionerElement, + registerTooltipRootElement, + registerTooltipTriggerElement, +} from 'prosekit/lit/tooltip' + +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} + + ` + } +} + +export function registerLitEditorButton() { + registerTooltipPopupElement() + registerTooltipPositionerElement() + registerTooltipRootElement() + registerTooltipTriggerElement() + + if (customElements.get('lit-editor-button')) return + customElements.define('lit-editor-button', LitButton) +} + +declare global { + interface HTMLElementTagNameMap { + 'lit-editor-button': LitButton + } +} diff --git a/lit-inline-menu/src/components/editor/ui/button/index.ts b/lit-inline-menu/src/components/editor/ui/button/index.ts new file mode 100644 index 0000000000..02efbeadd2 --- /dev/null +++ b/lit-inline-menu/src/components/editor/ui/button/index.ts @@ -0,0 +1 @@ +export { registerLitEditorButton } from './button' diff --git a/lit-inline-menu/src/components/editor/ui/editor-context.ts b/lit-inline-menu/src/components/editor/ui/editor-context.ts new file mode 100644 index 0000000000..7af6d3a865 --- /dev/null +++ b/lit-inline-menu/src/components/editor/ui/editor-context.ts @@ -0,0 +1,6 @@ +import { createContext } from '@lit/context' +import type { Editor } from 'prosekit/core' + +export const editorContext = createContext( + 'prosekit-editor', +) diff --git a/lit-inline-menu/src/components/editor/ui/inline-menu/index.ts b/lit-inline-menu/src/components/editor/ui/inline-menu/index.ts new file mode 100644 index 0000000000..ea7d96885b --- /dev/null +++ b/lit-inline-menu/src/components/editor/ui/inline-menu/index.ts @@ -0,0 +1 @@ +export { registerLitEditorInlineMenu } from './inline-menu' diff --git a/lit-inline-menu/src/components/editor/ui/inline-menu/inline-menu.ts b/lit-inline-menu/src/components/editor/ui/inline-menu/inline-menu.ts new file mode 100644 index 0000000000..5c17504ceb --- /dev/null +++ b/lit-inline-menu/src/components/editor/ui/inline-menu/inline-menu.ts @@ -0,0 +1,320 @@ +import { ContextConsumer } from '@lit/context' +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 { LinkAttrs } from 'prosekit/extensions/link' +import { + registerInlinePopoverPopupElement, + registerInlinePopoverPositionerElement, + registerInlinePopoverRootElement, + type OpenChangeEvent, +} from 'prosekit/lit/inline-popover' +import type { EditorState } from 'prosekit/pm/state' + +import { registerLitEditorButton } from '../button' +import { editorContext } from '../editor-context' + +function getInlineMenuItems(editor: Editor) { + return { + 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, + link: editor.commands.addLink + ? { + isActive: editor.marks.link.isActive(), + canExec: editor.commands.addLink.canExec({ href: '' }), + command: () => editor.commands.expandLink(), + currentLink: getCurrentLink(editor.state) || '', + } + : undefined, + } +} + +function getCurrentLink(state: EditorState): string | undefined { + const { $from } = state.selection + const marks = $from.marksAcross($from) + if (!marks) { + return + } + for (const mark of marks) { + if (mark.type.name === 'link') { + return (mark.attrs as LinkAttrs).href + } + } +} + +class LitInlineMenu extends LitElement { + static override properties = { + linkMenuOpen: { + state: true, + attribute: false, + } satisfies PropertyDeclaration, + } + + private linkMenuOpen = false + + private editorConsumer = new ContextConsumer(this, { + context: editorContext, + subscribe: true, + }) + + 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) + this.attachEditorListener() + } + + private attachEditorListener() { + this.detachEditorListener() + + const editor = this.editorConsumer.value + if (!editor) return + + this.removeUpdateExtension = editor.use( + defineUpdateHandler(() => this.requestUpdate()), + ) + } + + private detachEditorListener() { + this.removeUpdateExtension?.() + this.removeUpdateExtension = undefined + } + + private handleLinkUpdate(editor: Editor, href?: string) { + if (href) { + editor.commands.addLink({ href }) + } else { + editor.commands.removeLink() + } + + this.linkMenuOpen = false + editor.focus() + } + + override render() { + const editor = this.editorConsumer.value as + | Editor + | undefined + if (!editor) { + return nothing + } + + const items = getInlineMenuItems(editor) + + return html` + { + if (!event.detail) { + this.linkMenuOpen = false + } + }} + > + + + ${items.bold + ? html` + + ` + : nothing} + ${items.italic + ? html` + + ` + : nothing} + ${items.underline + ? html` + + ` + : nothing} + ${items.strike + ? html` + + ` + : nothing} + ${items.code + ? html` + + ` + : nothing} + ${items.link && items.link.canExec + ? html` + { + items.link?.command?.() + this.linkMenuOpen = !this.linkMenuOpen + }} + > + ` + : nothing} + + + + + ${items.link + ? html` + { + this.linkMenuOpen = event.detail + }} + > + + + ${this.linkMenuOpen + ? html` +
{ + event.preventDefault() + const target = + event.target as HTMLFormElement | null + const href = target + ?.querySelector('input') + ?.value?.trim() + this.handleLinkUpdate(editor, href) + }} + > + +
+ ` + : nothing} + ${items.link.isActive + ? html` + + ` + : nothing} +
+
+
+ ` + : nothing} + ` + } +} + +export function registerLitEditorInlineMenu() { + registerLitEditorButton() + registerInlinePopoverRootElement() + registerInlinePopoverPositionerElement() + registerInlinePopoverPopupElement() + + if (customElements.get('lit-editor-inline-menu')) return + customElements.define('lit-editor-inline-menu', LitInlineMenu) +} + +declare global { + interface HTMLElementTagNameMap { + 'lit-editor-inline-menu': LitInlineMenu + } +} diff --git a/lit-inline-menu/src/editor.ts b/lit-inline-menu/src/editor.ts new file mode 100644 index 0000000000..42480bd68a --- /dev/null +++ b/lit-inline-menu/src/editor.ts @@ -0,0 +1,18 @@ +import { registerLitEditor } from './components/editor/examples/inline-menu' +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-inline-menu/tsconfig.json b/lit-inline-menu/tsconfig.json new file mode 100644 index 0000000000..f3ad657d5f --- /dev/null +++ b/lit-inline-menu/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2023", + "experimentalDecorators": true, + "useDefineForClassFields": false, + "module": "esnext", + "lib": ["ES2023", "DOM", "DOM.Iterable"], + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/lit-inline-menu/vite.config.ts b/lit-inline-menu/vite.config.ts new file mode 100644 index 0000000000..fb0cdf00b5 --- /dev/null +++ b/lit-inline-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/preact-font-family/.gitignore b/preact-font-family/.gitignore new file mode 100644 index 0000000000..5d6225c6df --- /dev/null +++ b/preact-font-family/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.next +.svelte-kit diff --git a/preact-font-family/README.md b/preact-font-family/README.md new file mode 100644 index 0000000000..5823f1c75b --- /dev/null +++ b/preact-font-family/README.md @@ -0,0 +1,15 @@ +# preact-font-family + +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/preact-font-family) +[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/prosekit/examples/tree/master/preact-font-family) + +Run the example locally with: + +```bash +npx degit prosekit/examples/preact-font-family preact-font-family +cd preact-font-family +npm install +npm run dev +``` diff --git a/preact-font-family/index.html b/preact-font-family/index.html new file mode 100644 index 0000000000..b4f79f2233 --- /dev/null +++ b/preact-font-family/index.html @@ -0,0 +1,12 @@ + + + + + + ProseKit + Preact + + +
+ + + diff --git a/preact-font-family/package.json b/preact-font-family/package.json new file mode 100644 index 0000000000..bbc9569fed --- /dev/null +++ b/preact-font-family/package.json @@ -0,0 +1,25 @@ +{ + "name": "example-preact-font-family", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "tsc -b && vite build", + "dev": "vite", + "preview": "vite preview" + }, + "dependencies": { + "preact": "^10.29.1", + "prosekit": "^0.21.1" + }, + "devDependencies": { + "@egoist/tailwindcss-icons": "^1.9.2", + "@iconify-json/lucide": "^1.2.107", + "@preact/preset-vite": "^2.10.5", + "@tailwindcss/vite": "^4.3.0", + "@types/node": "^24.12.4", + "tailwindcss": "^4.3.0", + "typescript": "^6.0.3", + "vite": "^8.0.13" + } +} diff --git a/preact-font-family/src/App.tsx b/preact-font-family/src/App.tsx new file mode 100644 index 0000000000..1d065eacb3 --- /dev/null +++ b/preact-font-family/src/App.tsx @@ -0,0 +1,5 @@ +import { ExampleEditor } from './components/editor/examples/font-family' + +export default function App() { + return +} diff --git a/preact-font-family/src/app.css b/preact-font-family/src/app.css new file mode 100644 index 0000000000..ccad1aefaa --- /dev/null +++ b/preact-font-family/src/app.css @@ -0,0 +1,12 @@ +@import 'tailwindcss'; + +@plugin "@egoist/tailwindcss-icons"; + +body { + height: 100svh; + display: grid; + max-width: 900px; + padding: 16px; + margin-left: auto; + margin-right: auto; +} diff --git a/preact-font-family/src/components/editor/examples/font-family/editor.tsx b/preact-font-family/src/components/editor/examples/font-family/editor.tsx new file mode 100644 index 0000000000..5566b9c2e3 --- /dev/null +++ b/preact-font-family/src/components/editor/examples/font-family/editor.tsx @@ -0,0 +1,37 @@ +import 'prosekit/basic/style.css' +import 'prosekit/basic/typography.css' + +import { useMemo } from 'preact/hooks' +import { createEditor, type NodeJSON } from 'prosekit/core' +import { ProseKit } from 'prosekit/preact' + +import { sampleContent } from '../../sample/sample-doc-font-family' + +import { defineExtension } from './extension' +import Toolbar from './toolbar' + +interface EditorProps { + initialContent?: NodeJSON +} + +export default function Editor(props: EditorProps) { + const defaultContent = props.initialContent ?? sampleContent + const editor = useMemo(() => { + const extension = defineExtension() + return createEditor({ extension, defaultContent }) + }, [defaultContent]) + + return ( + +
+ +
+
+
+
+
+ ) +} diff --git a/preact-font-family/src/components/editor/examples/font-family/extension.ts b/preact-font-family/src/components/editor/examples/font-family/extension.ts new file mode 100644 index 0000000000..fc06c6a7c5 --- /dev/null +++ b/preact-font-family/src/components/editor/examples/font-family/extension.ts @@ -0,0 +1,9 @@ +import { defineBasicExtension } from 'prosekit/basic' +import { union } from 'prosekit/core' +import { defineFontFamily } from 'prosekit/extensions/font-family' + +export function defineExtension() { + return union(defineBasicExtension(), defineFontFamily()) +} + +export type EditorExtension = ReturnType diff --git a/preact-font-family/src/components/editor/examples/font-family/index.ts b/preact-font-family/src/components/editor/examples/font-family/index.ts new file mode 100644 index 0000000000..1a07a6017a --- /dev/null +++ b/preact-font-family/src/components/editor/examples/font-family/index.ts @@ -0,0 +1 @@ +export { default as ExampleEditor } from './editor' diff --git a/preact-font-family/src/components/editor/examples/font-family/toolbar.tsx b/preact-font-family/src/components/editor/examples/font-family/toolbar.tsx new file mode 100644 index 0000000000..694fd41983 --- /dev/null +++ b/preact-font-family/src/components/editor/examples/font-family/toolbar.tsx @@ -0,0 +1,88 @@ +import { useEffect } from 'preact/hooks' +import type { Editor } from 'prosekit/core' +import { useEditorDerivedValue } from 'prosekit/preact' + +import type { EditorExtension } from './extension' + +const fonts = [ + { label: 'Arial', family: 'Arial, sans-serif' }, + { label: 'Times New Roman', family: 'Times New Roman, serif' }, + { label: 'Courier New', family: 'Courier New, monospace' }, + { label: 'Georgia', family: 'Georgia, serif' }, + { label: 'Verdana', family: 'Verdana, sans-serif' }, + { label: 'Comic Sans MS', family: 'Comic Sans MS, cursive' }, + { label: 'Impact', family: 'Impact, sans-serif' }, + { label: 'Inter', family: 'Inter, sans-serif', google: true }, + { + label: 'Playfair Display', + family: 'Playfair Display, serif', + google: true, + }, + { label: 'Merriweather', family: 'Merriweather, serif', google: true }, +] + +function loadGoogleFonts() { + const linkId = 'prosekit-google-fonts' + if (document.getElementById(linkId)) return + const link = document.createElement('link') + link.id = linkId + link.rel = 'stylesheet' + link.href = + 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&family=Merriweather:wght@400;700&family=Playfair+Display:wght@400;700&display=swap' + document.head.appendChild(link) +} + +function getToolbarState(editor: Editor) { + let activeFont = '' + for (const font of fonts) { + if (editor.marks.fontFamily.isActive({ family: font.family })) { + activeFont = font.family + break + } + } + + const handleChange = (value: string) => { + if (!value) { + editor.commands.removeFontFamily() + return + } + const font = fonts.find((f) => f.family === value) + if (font?.google) { + loadGoogleFonts() + } + editor.commands.addFontFamily({ family: value }) + } + + return { activeFont, handleChange } +} + +export default function Toolbar() { + const state = useEditorDerivedValue(getToolbarState) + + useEffect(() => { + loadGoogleFonts() + }, []) + + return ( +
+ +
+ ) +} diff --git a/preact-font-family/src/components/editor/sample/sample-doc-font-family.ts b/preact-font-family/src/components/editor/sample/sample-doc-font-family.ts new file mode 100644 index 0000000000..b175bf2b3d --- /dev/null +++ b/preact-font-family/src/components/editor/sample/sample-doc-font-family.ts @@ -0,0 +1,80 @@ +import type { NodeJSON } from 'prosekit/core' + +export const sampleContent: NodeJSON = { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text: 'Select text and choose a font from the inline menu. ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Georgia, serif', + }, + }, + ], + text: 'Georgia', + }, + { + type: 'text', + text: ', ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Courier New, monospace', + }, + }, + ], + text: 'Courier New', + }, + { + type: 'text', + text: ', ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Impact, sans-serif', + }, + }, + ], + text: 'Impact', + }, + { + type: 'text', + text: ', ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Comic Sans MS, cursive', + }, + }, + ], + text: 'Comic Sans', + }, + { + type: 'text', + text: '.', + }, + ], + }, + ], +} diff --git a/preact-font-family/src/main.tsx b/preact-font-family/src/main.tsx new file mode 100644 index 0000000000..9452b5673a --- /dev/null +++ b/preact-font-family/src/main.tsx @@ -0,0 +1,5 @@ +import './app.css' +import { render } from 'preact' +import App from './App.tsx' + +render(, document.getElementById('app')!) diff --git a/preact-font-family/tsconfig.app.json b/preact-font-family/tsconfig.app.json new file mode 100644 index 0000000000..3fe3ab979d --- /dev/null +++ b/preact-font-family/tsconfig.app.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": ["vite/client"], + "skipLibCheck": true, + "paths": { + "react": ["./node_modules/preact/compat/"], + "react-dom": ["./node_modules/preact/compat/"] + }, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + "jsxImportSource": "preact", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/preact-font-family/tsconfig.json b/preact-font-family/tsconfig.json new file mode 100644 index 0000000000..1ffef600d9 --- /dev/null +++ b/preact-font-family/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/preact-font-family/tsconfig.node.json b/preact-font-family/tsconfig.node.json new file mode 100644 index 0000000000..8a67f62f4c --- /dev/null +++ b/preact-font-family/tsconfig.node.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "types": ["node"], + "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": ["vite.config.ts"] +} diff --git a/preact-font-family/vite.config.ts b/preact-font-family/vite.config.ts new file mode 100644 index 0000000000..5e0b3c9e1a --- /dev/null +++ b/preact-font-family/vite.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vite' +import preact from '@preact/preset-vite' +import tailwindcss from '@tailwindcss/vite' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [preact(), tailwindcss()], +}) diff --git a/react-font-family/.gitignore b/react-font-family/.gitignore new file mode 100644 index 0000000000..5d6225c6df --- /dev/null +++ b/react-font-family/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.next +.svelte-kit diff --git a/react-font-family/README.md b/react-font-family/README.md new file mode 100644 index 0000000000..2884947faa --- /dev/null +++ b/react-font-family/README.md @@ -0,0 +1,15 @@ +# react-font-family + +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/react-font-family) +[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/prosekit/examples/tree/master/react-font-family) + +Run the example locally with: + +```bash +npx degit prosekit/examples/react-font-family react-font-family +cd react-font-family +npm install +npm run dev +``` diff --git a/react-font-family/index.html b/react-font-family/index.html new file mode 100644 index 0000000000..a5a78f3bf8 --- /dev/null +++ b/react-font-family/index.html @@ -0,0 +1,12 @@ + + + + + + ProseKit + React + + +
+ + + diff --git a/react-font-family/package.json b/react-font-family/package.json new file mode 100644 index 0000000000..c42ba26f50 --- /dev/null +++ b/react-font-family/package.json @@ -0,0 +1,28 @@ +{ + "name": "example-react-font-family", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "tsc -b && vite build", + "dev": "vite", + "preview": "vite preview" + }, + "dependencies": { + "prosekit": "^0.21.1", + "react": "^19.2.6", + "react-dom": "^19.2.6" + }, + "devDependencies": { + "@egoist/tailwindcss-icons": "^1.9.2", + "@iconify-json/lucide": "^1.2.107", + "@tailwindcss/vite": "^4.3.0", + "@types/node": "^24.12.4", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "tailwindcss": "^4.3.0", + "typescript": "^6.0.3", + "vite": "^8.0.13" + } +} diff --git a/react-font-family/src/App.tsx b/react-font-family/src/App.tsx new file mode 100644 index 0000000000..1d065eacb3 --- /dev/null +++ b/react-font-family/src/App.tsx @@ -0,0 +1,5 @@ +import { ExampleEditor } from './components/editor/examples/font-family' + +export default function App() { + return +} diff --git a/react-font-family/src/app.css b/react-font-family/src/app.css new file mode 100644 index 0000000000..ccad1aefaa --- /dev/null +++ b/react-font-family/src/app.css @@ -0,0 +1,12 @@ +@import 'tailwindcss'; + +@plugin "@egoist/tailwindcss-icons"; + +body { + height: 100svh; + display: grid; + max-width: 900px; + padding: 16px; + margin-left: auto; + margin-right: auto; +} diff --git a/react-font-family/src/components/editor/examples/font-family/editor.tsx b/react-font-family/src/components/editor/examples/font-family/editor.tsx new file mode 100644 index 0000000000..bb1b1a0485 --- /dev/null +++ b/react-font-family/src/components/editor/examples/font-family/editor.tsx @@ -0,0 +1,39 @@ +'use client' + +import 'prosekit/basic/style.css' +import 'prosekit/basic/typography.css' + +import { createEditor, type NodeJSON } from 'prosekit/core' +import { ProseKit } from 'prosekit/react' +import { useMemo } from 'react' + +import { sampleContent } from '../../sample/sample-doc-font-family' + +import { defineExtension } from './extension' +import Toolbar from './toolbar' + +interface EditorProps { + initialContent?: NodeJSON +} + +export default function Editor(props: EditorProps) { + const defaultContent = props.initialContent ?? sampleContent + const editor = useMemo(() => { + const extension = defineExtension() + return createEditor({ extension, defaultContent }) + }, [defaultContent]) + + return ( + +
+ +
+
+
+
+
+ ) +} diff --git a/react-font-family/src/components/editor/examples/font-family/extension.ts b/react-font-family/src/components/editor/examples/font-family/extension.ts new file mode 100644 index 0000000000..fc06c6a7c5 --- /dev/null +++ b/react-font-family/src/components/editor/examples/font-family/extension.ts @@ -0,0 +1,9 @@ +import { defineBasicExtension } from 'prosekit/basic' +import { union } from 'prosekit/core' +import { defineFontFamily } from 'prosekit/extensions/font-family' + +export function defineExtension() { + return union(defineBasicExtension(), defineFontFamily()) +} + +export type EditorExtension = ReturnType diff --git a/react-font-family/src/components/editor/examples/font-family/index.ts b/react-font-family/src/components/editor/examples/font-family/index.ts new file mode 100644 index 0000000000..1a07a6017a --- /dev/null +++ b/react-font-family/src/components/editor/examples/font-family/index.ts @@ -0,0 +1 @@ +export { default as ExampleEditor } from './editor' diff --git a/react-font-family/src/components/editor/examples/font-family/toolbar.tsx b/react-font-family/src/components/editor/examples/font-family/toolbar.tsx new file mode 100644 index 0000000000..967c77d5c8 --- /dev/null +++ b/react-font-family/src/components/editor/examples/font-family/toolbar.tsx @@ -0,0 +1,88 @@ +'use client' + +import type { Editor } from 'prosekit/core' +import { useEditorDerivedValue } from 'prosekit/react' +import { useEffect } from 'react' + +import type { EditorExtension } from './extension' + +const fonts = [ + { label: 'Arial', family: 'Arial, sans-serif' }, + { label: 'Times New Roman', family: 'Times New Roman, serif' }, + { label: 'Courier New', family: 'Courier New, monospace' }, + { label: 'Georgia', family: 'Georgia, serif' }, + { label: 'Verdana', family: 'Verdana, sans-serif' }, + { label: 'Comic Sans MS', family: 'Comic Sans MS, cursive' }, + { label: 'Impact', family: 'Impact, sans-serif' }, + { label: 'Inter', family: 'Inter, sans-serif', google: true }, + { + label: 'Playfair Display', + family: 'Playfair Display, serif', + google: true, + }, + { label: 'Merriweather', family: 'Merriweather, serif', google: true }, +] + +function loadGoogleFonts() { + const linkId = 'prosekit-google-fonts' + if (document.getElementById(linkId)) return + const link = document.createElement('link') + link.id = linkId + link.rel = 'stylesheet' + link.href = + 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&family=Merriweather:wght@400;700&family=Playfair+Display:wght@400;700&display=swap' + document.head.appendChild(link) +} + +function getToolbarState(editor: Editor) { + let activeFont = '' + for (const font of fonts) { + if (editor.marks.fontFamily.isActive({ family: font.family })) { + activeFont = font.family + break + } + } + + const handleChange = (value: string) => { + if (!value) { + editor.commands.removeFontFamily() + return + } + const font = fonts.find((f) => f.family === value) + if (font?.google) { + loadGoogleFonts() + } + editor.commands.addFontFamily({ family: value }) + } + + return { activeFont, handleChange } +} + +export default function Toolbar() { + const state = useEditorDerivedValue(getToolbarState) + + useEffect(() => { + loadGoogleFonts() + }, []) + + return ( +
+ +
+ ) +} diff --git a/react-font-family/src/components/editor/sample/sample-doc-font-family.ts b/react-font-family/src/components/editor/sample/sample-doc-font-family.ts new file mode 100644 index 0000000000..b175bf2b3d --- /dev/null +++ b/react-font-family/src/components/editor/sample/sample-doc-font-family.ts @@ -0,0 +1,80 @@ +import type { NodeJSON } from 'prosekit/core' + +export const sampleContent: NodeJSON = { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text: 'Select text and choose a font from the inline menu. ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Georgia, serif', + }, + }, + ], + text: 'Georgia', + }, + { + type: 'text', + text: ', ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Courier New, monospace', + }, + }, + ], + text: 'Courier New', + }, + { + type: 'text', + text: ', ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Impact, sans-serif', + }, + }, + ], + text: 'Impact', + }, + { + type: 'text', + text: ', ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Comic Sans MS, cursive', + }, + }, + ], + text: 'Comic Sans', + }, + { + type: 'text', + text: '.', + }, + ], + }, + ], +} diff --git a/react-font-family/src/main.tsx b/react-font-family/src/main.tsx new file mode 100644 index 0000000000..87de8eb52b --- /dev/null +++ b/react-font-family/src/main.tsx @@ -0,0 +1,10 @@ +import './app.css' +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.tsx' + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/react-font-family/tsconfig.app.json b/react-font-family/tsconfig.app.json new file mode 100644 index 0000000000..a9b5a59ca6 --- /dev/null +++ b/react-font-family/tsconfig.app.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/react-font-family/tsconfig.json b/react-font-family/tsconfig.json new file mode 100644 index 0000000000..1ffef600d9 --- /dev/null +++ b/react-font-family/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/react-font-family/tsconfig.node.json b/react-font-family/tsconfig.node.json new file mode 100644 index 0000000000..8a67f62f4c --- /dev/null +++ b/react-font-family/tsconfig.node.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "types": ["node"], + "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": ["vite.config.ts"] +} diff --git a/react-font-family/vite.config.ts b/react-font-family/vite.config.ts new file mode 100644 index 0000000000..c90997597d --- /dev/null +++ b/react-font-family/vite.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import tailwindcss from '@tailwindcss/vite' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react(), tailwindcss()], +}) diff --git a/solid-font-family/.gitignore b/solid-font-family/.gitignore new file mode 100644 index 0000000000..5d6225c6df --- /dev/null +++ b/solid-font-family/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.next +.svelte-kit diff --git a/solid-font-family/README.md b/solid-font-family/README.md new file mode 100644 index 0000000000..d841088363 --- /dev/null +++ b/solid-font-family/README.md @@ -0,0 +1,15 @@ +# solid-font-family + +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/solid-font-family) +[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/prosekit/examples/tree/master/solid-font-family) + +Run the example locally with: + +```bash +npx degit prosekit/examples/solid-font-family solid-font-family +cd solid-font-family +npm install +npm run dev +``` diff --git a/solid-font-family/index.html b/solid-font-family/index.html new file mode 100644 index 0000000000..375ea8dbc4 --- /dev/null +++ b/solid-font-family/index.html @@ -0,0 +1,12 @@ + + + + + + ProseKit + Solid + + +
+ + + diff --git a/solid-font-family/package.json b/solid-font-family/package.json new file mode 100644 index 0000000000..85b97cd1f6 --- /dev/null +++ b/solid-font-family/package.json @@ -0,0 +1,25 @@ +{ + "name": "example-solid-font-family", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "tsc -b && vite build", + "dev": "vite", + "preview": "vite preview" + }, + "dependencies": { + "prosekit": "^0.21.1", + "solid-js": "^1.9.13" + }, + "devDependencies": { + "@egoist/tailwindcss-icons": "^1.9.2", + "@iconify-json/lucide": "^1.2.107", + "@tailwindcss/vite": "^4.3.0", + "@types/node": "^24.12.4", + "tailwindcss": "^4.3.0", + "typescript": "^6.0.3", + "vite": "^8.0.13", + "vite-plugin-solid": "^2.11.12" + } +} diff --git a/solid-font-family/src/App.tsx b/solid-font-family/src/App.tsx new file mode 100644 index 0000000000..1d065eacb3 --- /dev/null +++ b/solid-font-family/src/App.tsx @@ -0,0 +1,5 @@ +import { ExampleEditor } from './components/editor/examples/font-family' + +export default function App() { + return +} diff --git a/solid-font-family/src/app.css b/solid-font-family/src/app.css new file mode 100644 index 0000000000..ccad1aefaa --- /dev/null +++ b/solid-font-family/src/app.css @@ -0,0 +1,12 @@ +@import 'tailwindcss'; + +@plugin "@egoist/tailwindcss-icons"; + +body { + height: 100svh; + display: grid; + max-width: 900px; + padding: 16px; + margin-left: auto; + margin-right: auto; +} diff --git a/solid-font-family/src/components/editor/examples/font-family/editor.tsx b/solid-font-family/src/components/editor/examples/font-family/editor.tsx new file mode 100644 index 0000000000..1e300d53c0 --- /dev/null +++ b/solid-font-family/src/components/editor/examples/font-family/editor.tsx @@ -0,0 +1,35 @@ +import 'prosekit/basic/style.css' +import 'prosekit/basic/typography.css' + +import { createEditor, type NodeJSON } from 'prosekit/core' +import { ProseKit } from 'prosekit/solid' +import type { JSX } from 'solid-js' + +import { sampleContent } from '../../sample/sample-doc-font-family' + +import { defineExtension } from './extension' +import Toolbar from './toolbar' + +interface EditorProps { + initialContent?: NodeJSON +} + +export default function Editor(props: EditorProps): JSX.Element { + const defaultContent = props.initialContent ?? sampleContent + const extension = defineExtension() + const editor = createEditor({ extension, defaultContent }) + + return ( + +
+ +
+
+
+
+
+ ) +} diff --git a/solid-font-family/src/components/editor/examples/font-family/extension.ts b/solid-font-family/src/components/editor/examples/font-family/extension.ts new file mode 100644 index 0000000000..fc06c6a7c5 --- /dev/null +++ b/solid-font-family/src/components/editor/examples/font-family/extension.ts @@ -0,0 +1,9 @@ +import { defineBasicExtension } from 'prosekit/basic' +import { union } from 'prosekit/core' +import { defineFontFamily } from 'prosekit/extensions/font-family' + +export function defineExtension() { + return union(defineBasicExtension(), defineFontFamily()) +} + +export type EditorExtension = ReturnType diff --git a/solid-font-family/src/components/editor/examples/font-family/index.ts b/solid-font-family/src/components/editor/examples/font-family/index.ts new file mode 100644 index 0000000000..1a07a6017a --- /dev/null +++ b/solid-font-family/src/components/editor/examples/font-family/index.ts @@ -0,0 +1 @@ +export { default as ExampleEditor } from './editor' diff --git a/solid-font-family/src/components/editor/examples/font-family/toolbar.tsx b/solid-font-family/src/components/editor/examples/font-family/toolbar.tsx new file mode 100644 index 0000000000..c41339a80a --- /dev/null +++ b/solid-font-family/src/components/editor/examples/font-family/toolbar.tsx @@ -0,0 +1,82 @@ +import type { Editor } from 'prosekit/core' +import { useEditorDerivedValue } from 'prosekit/solid' +import { onMount, type JSX } from 'solid-js' + +import type { EditorExtension } from './extension' + +const fonts = [ + { label: 'Arial', family: 'Arial, sans-serif' }, + { label: 'Times New Roman', family: 'Times New Roman, serif' }, + { label: 'Courier New', family: 'Courier New, monospace' }, + { label: 'Georgia', family: 'Georgia, serif' }, + { label: 'Verdana', family: 'Verdana, sans-serif' }, + { label: 'Comic Sans MS', family: 'Comic Sans MS, cursive' }, + { label: 'Impact', family: 'Impact, sans-serif' }, + { label: 'Inter', family: 'Inter, sans-serif', google: true }, + { + label: 'Playfair Display', + family: 'Playfair Display, serif', + google: true, + }, + { label: 'Merriweather', family: 'Merriweather, serif', google: true }, +] + +function loadGoogleFonts() { + const linkId = 'prosekit-google-fonts' + if (document.getElementById(linkId)) return + const link = document.createElement('link') + link.id = linkId + link.rel = 'stylesheet' + link.href = + 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&family=Merriweather:wght@400;700&family=Playfair+Display:wght@400;700&display=swap' + document.head.appendChild(link) +} + +function getToolbarState(editor: Editor) { + let activeFont = '' + for (const font of fonts) { + if (editor.marks.fontFamily.isActive({ family: font.family })) { + activeFont = font.family + break + } + } + + const handleChange = (value: string) => { + if (!value) { + editor.commands.removeFontFamily() + return + } + const font = fonts.find((f) => f.family === value) + if (font?.google) { + loadGoogleFonts() + } + editor.commands.addFontFamily({ family: value }) + } + + return { activeFont, handleChange } +} + +export default function Toolbar(): JSX.Element { + const state = useEditorDerivedValue(getToolbarState) + + onMount(() => { + loadGoogleFonts() + }) + + return ( +
+ +
+ ) +} diff --git a/solid-font-family/src/components/editor/sample/sample-doc-font-family.ts b/solid-font-family/src/components/editor/sample/sample-doc-font-family.ts new file mode 100644 index 0000000000..b175bf2b3d --- /dev/null +++ b/solid-font-family/src/components/editor/sample/sample-doc-font-family.ts @@ -0,0 +1,80 @@ +import type { NodeJSON } from 'prosekit/core' + +export const sampleContent: NodeJSON = { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text: 'Select text and choose a font from the inline menu. ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Georgia, serif', + }, + }, + ], + text: 'Georgia', + }, + { + type: 'text', + text: ', ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Courier New, monospace', + }, + }, + ], + text: 'Courier New', + }, + { + type: 'text', + text: ', ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Impact, sans-serif', + }, + }, + ], + text: 'Impact', + }, + { + type: 'text', + text: ', ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Comic Sans MS, cursive', + }, + }, + ], + text: 'Comic Sans', + }, + { + type: 'text', + text: '.', + }, + ], + }, + ], +} diff --git a/solid-font-family/src/index.tsx b/solid-font-family/src/index.tsx new file mode 100644 index 0000000000..92b7740757 --- /dev/null +++ b/solid-font-family/src/index.tsx @@ -0,0 +1,8 @@ +/* @refresh reload */ +import './app.css' +import { render } from 'solid-js/web' +import App from './App' + +const root = document.getElementById('root') + +render(() => , root!) diff --git a/solid-font-family/tsconfig.app.json b/solid-font-family/tsconfig.app.json new file mode 100644 index 0000000000..c0b480e758 --- /dev/null +++ b/solid-font-family/tsconfig.app.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "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, + "jsx": "preserve", + "jsxImportSource": "solid-js", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/solid-font-family/tsconfig.json b/solid-font-family/tsconfig.json new file mode 100644 index 0000000000..1ffef600d9 --- /dev/null +++ b/solid-font-family/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/solid-font-family/tsconfig.node.json b/solid-font-family/tsconfig.node.json new file mode 100644 index 0000000000..8a67f62f4c --- /dev/null +++ b/solid-font-family/tsconfig.node.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "types": ["node"], + "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": ["vite.config.ts"] +} diff --git a/solid-font-family/vite.config.ts b/solid-font-family/vite.config.ts new file mode 100644 index 0000000000..9cb3cb51fd --- /dev/null +++ b/solid-font-family/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import solid from 'vite-plugin-solid' +import tailwindcss from '@tailwindcss/vite' + +export default defineConfig({ + plugins: [solid(), tailwindcss()], +}) diff --git a/svelte-font-family/.gitignore b/svelte-font-family/.gitignore new file mode 100644 index 0000000000..5d6225c6df --- /dev/null +++ b/svelte-font-family/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.next +.svelte-kit diff --git a/svelte-font-family/README.md b/svelte-font-family/README.md new file mode 100644 index 0000000000..d640c7fa6c --- /dev/null +++ b/svelte-font-family/README.md @@ -0,0 +1,15 @@ +# svelte-font-family + +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/svelte-font-family) +[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/prosekit/examples/tree/master/svelte-font-family) + +Run the example locally with: + +```bash +npx degit prosekit/examples/svelte-font-family svelte-font-family +cd svelte-font-family +npm install +npm run dev +``` diff --git a/svelte-font-family/index.html b/svelte-font-family/index.html new file mode 100644 index 0000000000..58212c2501 --- /dev/null +++ b/svelte-font-family/index.html @@ -0,0 +1,12 @@ + + + + + + ProseKit + Svelte + + +
+ + + diff --git a/svelte-font-family/package.json b/svelte-font-family/package.json new file mode 100644 index 0000000000..5bf3398531 --- /dev/null +++ b/svelte-font-family/package.json @@ -0,0 +1,28 @@ +{ + "name": "example-svelte-font-family", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "svelte-check --tsconfig ./tsconfig.json && vite build", + "dev": "vite", + "preview": "vite preview" + }, + "dependencies": { + "prosekit": "^0.21.1" + }, + "devDependencies": { + "@egoist/tailwindcss-icons": "^1.9.2", + "@iconify-json/lucide": "^1.2.107", + "@sveltejs/vite-plugin-svelte": "^7.1.2", + "@tailwindcss/vite": "^4.3.0", + "@tsconfig/svelte": "^5.0.8", + "postcss": "^8.5.14", + "svelte": "^5.55.7", + "svelte-check": "^4.4.8", + "tailwindcss": "^4.3.0", + "tslib": "^2.8.1", + "typescript": "^6.0.3", + "vite": "^8.0.13" + } +} diff --git a/svelte-font-family/src/App.svelte b/svelte-font-family/src/App.svelte new file mode 100644 index 0000000000..07d0a7c3cc --- /dev/null +++ b/svelte-font-family/src/App.svelte @@ -0,0 +1,5 @@ + + + diff --git a/svelte-font-family/src/app.css b/svelte-font-family/src/app.css new file mode 100644 index 0000000000..ccad1aefaa --- /dev/null +++ b/svelte-font-family/src/app.css @@ -0,0 +1,12 @@ +@import 'tailwindcss'; + +@plugin "@egoist/tailwindcss-icons"; + +body { + height: 100svh; + display: grid; + max-width: 900px; + padding: 16px; + margin-left: auto; + margin-right: auto; +} diff --git a/svelte-font-family/src/components/editor/examples/font-family/editor.svelte b/svelte-font-family/src/components/editor/examples/font-family/editor.svelte new file mode 100644 index 0000000000..bc62906072 --- /dev/null +++ b/svelte-font-family/src/components/editor/examples/font-family/editor.svelte @@ -0,0 +1,30 @@ + + + +
+ +
+
+
+
+
diff --git a/svelte-font-family/src/components/editor/examples/font-family/extension.ts b/svelte-font-family/src/components/editor/examples/font-family/extension.ts new file mode 100644 index 0000000000..fc06c6a7c5 --- /dev/null +++ b/svelte-font-family/src/components/editor/examples/font-family/extension.ts @@ -0,0 +1,9 @@ +import { defineBasicExtension } from 'prosekit/basic' +import { union } from 'prosekit/core' +import { defineFontFamily } from 'prosekit/extensions/font-family' + +export function defineExtension() { + return union(defineBasicExtension(), defineFontFamily()) +} + +export type EditorExtension = ReturnType diff --git a/svelte-font-family/src/components/editor/examples/font-family/index.ts b/svelte-font-family/src/components/editor/examples/font-family/index.ts new file mode 100644 index 0000000000..1933e61970 --- /dev/null +++ b/svelte-font-family/src/components/editor/examples/font-family/index.ts @@ -0,0 +1 @@ +export { default as ExampleEditor } from './editor.svelte' diff --git a/svelte-font-family/src/components/editor/examples/font-family/toolbar.svelte b/svelte-font-family/src/components/editor/examples/font-family/toolbar.svelte new file mode 100644 index 0000000000..4c4467fbc5 --- /dev/null +++ b/svelte-font-family/src/components/editor/examples/font-family/toolbar.svelte @@ -0,0 +1,75 @@ + + +
+ +
diff --git a/svelte-font-family/src/components/editor/sample/sample-doc-font-family.ts b/svelte-font-family/src/components/editor/sample/sample-doc-font-family.ts new file mode 100644 index 0000000000..b175bf2b3d --- /dev/null +++ b/svelte-font-family/src/components/editor/sample/sample-doc-font-family.ts @@ -0,0 +1,80 @@ +import type { NodeJSON } from 'prosekit/core' + +export const sampleContent: NodeJSON = { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text: 'Select text and choose a font from the inline menu. ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Georgia, serif', + }, + }, + ], + text: 'Georgia', + }, + { + type: 'text', + text: ', ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Courier New, monospace', + }, + }, + ], + text: 'Courier New', + }, + { + type: 'text', + text: ', ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Impact, sans-serif', + }, + }, + ], + text: 'Impact', + }, + { + type: 'text', + text: ', ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Comic Sans MS, cursive', + }, + }, + ], + text: 'Comic Sans', + }, + { + type: 'text', + text: '.', + }, + ], + }, + ], +} diff --git a/svelte-font-family/src/main.ts b/svelte-font-family/src/main.ts new file mode 100644 index 0000000000..b4bc565380 --- /dev/null +++ b/svelte-font-family/src/main.ts @@ -0,0 +1,8 @@ +import './app.css' + +import { mount } from 'svelte' +import App from './App.svelte' + +const app = mount(App, { target: document.getElementById('app')! }) + +export default app diff --git a/svelte-font-family/src/vite-env.d.ts b/svelte-font-family/src/vite-env.d.ts new file mode 100644 index 0000000000..4078e7476a --- /dev/null +++ b/svelte-font-family/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/svelte-font-family/svelte.config.js b/svelte-font-family/svelte.config.js new file mode 100644 index 0000000000..b0683fd24d --- /dev/null +++ b/svelte-font-family/svelte.config.js @@ -0,0 +1,7 @@ +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' + +export default { + // Consult https://svelte.dev/docs#compile-time-svelte-preprocess + // for more information about preprocessors + preprocess: vitePreprocess(), +} diff --git a/svelte-font-family/tsconfig.json b/svelte-font-family/tsconfig.json new file mode 100644 index 0000000000..86876593dd --- /dev/null +++ b/svelte-font-family/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "@tsconfig/svelte/tsconfig.json", + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "resolveJsonModule": true, + /** + * Typecheck JS in `.svelte` and `.js` files by default. + * Disable checkJs if you'd like to use dynamic types in JS. + * Note that setting allowJs false does not prevent the use + * of JS in `.svelte` files. + */ + "allowJs": true, + "checkJs": true, + "allowImportingTsExtensions": true, + "isolatedModules": true + }, + "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/svelte-font-family/tsconfig.node.json b/svelte-font-family/tsconfig.node.json new file mode 100644 index 0000000000..494bfe0835 --- /dev/null +++ b/svelte-font-family/tsconfig.node.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler" + }, + "include": ["vite.config.ts"] +} diff --git a/svelte-font-family/vite.config.ts b/svelte-font-family/vite.config.ts new file mode 100644 index 0000000000..11edea7f23 --- /dev/null +++ b/svelte-font-family/vite.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vite' +import { svelte } from '@sveltejs/vite-plugin-svelte' +import tailwindcss from '@tailwindcss/vite' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [svelte(), tailwindcss()], +}) diff --git a/vue-font-family/.gitignore b/vue-font-family/.gitignore new file mode 100644 index 0000000000..5d6225c6df --- /dev/null +++ b/vue-font-family/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.next +.svelte-kit diff --git a/vue-font-family/README.md b/vue-font-family/README.md new file mode 100644 index 0000000000..8ca69b4f41 --- /dev/null +++ b/vue-font-family/README.md @@ -0,0 +1,15 @@ +# vue-font-family + +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/vue-font-family) +[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/prosekit/examples/tree/master/vue-font-family) + +Run the example locally with: + +```bash +npx degit prosekit/examples/vue-font-family vue-font-family +cd vue-font-family +npm install +npm run dev +``` diff --git a/vue-font-family/index.html b/vue-font-family/index.html new file mode 100644 index 0000000000..d8952bb9e6 --- /dev/null +++ b/vue-font-family/index.html @@ -0,0 +1,12 @@ + + + + + + ProseKit + Vue + + +
+ + + diff --git a/vue-font-family/package.json b/vue-font-family/package.json new file mode 100644 index 0000000000..c9989eb3e6 --- /dev/null +++ b/vue-font-family/package.json @@ -0,0 +1,27 @@ +{ + "name": "example-vue-font-family", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "vue-tsc -b && vite build", + "dev": "vite", + "preview": "vite preview" + }, + "dependencies": { + "prosekit": "^0.21.1", + "vue": "^3.5.34" + }, + "devDependencies": { + "@egoist/tailwindcss-icons": "^1.9.2", + "@iconify-json/lucide": "^1.2.107", + "@tailwindcss/vite": "^4.3.0", + "@types/node": "^24.12.4", + "@vitejs/plugin-vue": "^6.0.6", + "@vue/tsconfig": "^0.9.1", + "tailwindcss": "^4.3.0", + "typescript": "^6.0.3", + "vite": "^8.0.13", + "vue-tsc": "^3.2.8" + } +} diff --git a/vue-font-family/src/App.vue b/vue-font-family/src/App.vue new file mode 100644 index 0000000000..a7a2c28ebb --- /dev/null +++ b/vue-font-family/src/App.vue @@ -0,0 +1,7 @@ + + + diff --git a/vue-font-family/src/app.css b/vue-font-family/src/app.css new file mode 100644 index 0000000000..ccad1aefaa --- /dev/null +++ b/vue-font-family/src/app.css @@ -0,0 +1,12 @@ +@import 'tailwindcss'; + +@plugin "@egoist/tailwindcss-icons"; + +body { + height: 100svh; + display: grid; + max-width: 900px; + padding: 16px; + margin-left: auto; + margin-right: auto; +} diff --git a/vue-font-family/src/components/editor/examples/font-family/editor.vue b/vue-font-family/src/components/editor/examples/font-family/editor.vue new file mode 100644 index 0000000000..b855a2e7ab --- /dev/null +++ b/vue-font-family/src/components/editor/examples/font-family/editor.vue @@ -0,0 +1,36 @@ + + + diff --git a/vue-font-family/src/components/editor/examples/font-family/extension.ts b/vue-font-family/src/components/editor/examples/font-family/extension.ts new file mode 100644 index 0000000000..fc06c6a7c5 --- /dev/null +++ b/vue-font-family/src/components/editor/examples/font-family/extension.ts @@ -0,0 +1,9 @@ +import { defineBasicExtension } from 'prosekit/basic' +import { union } from 'prosekit/core' +import { defineFontFamily } from 'prosekit/extensions/font-family' + +export function defineExtension() { + return union(defineBasicExtension(), defineFontFamily()) +} + +export type EditorExtension = ReturnType diff --git a/vue-font-family/src/components/editor/examples/font-family/index.ts b/vue-font-family/src/components/editor/examples/font-family/index.ts new file mode 100644 index 0000000000..dcc4cb6219 --- /dev/null +++ b/vue-font-family/src/components/editor/examples/font-family/index.ts @@ -0,0 +1 @@ +export { default as ExampleEditor } from './editor.vue' diff --git a/vue-font-family/src/components/editor/examples/font-family/toolbar.vue b/vue-font-family/src/components/editor/examples/font-family/toolbar.vue new file mode 100644 index 0000000000..86ff0e069a --- /dev/null +++ b/vue-font-family/src/components/editor/examples/font-family/toolbar.vue @@ -0,0 +1,87 @@ + + + diff --git a/vue-font-family/src/components/editor/sample/sample-doc-font-family.ts b/vue-font-family/src/components/editor/sample/sample-doc-font-family.ts new file mode 100644 index 0000000000..b175bf2b3d --- /dev/null +++ b/vue-font-family/src/components/editor/sample/sample-doc-font-family.ts @@ -0,0 +1,80 @@ +import type { NodeJSON } from 'prosekit/core' + +export const sampleContent: NodeJSON = { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text: 'Select text and choose a font from the inline menu. ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Georgia, serif', + }, + }, + ], + text: 'Georgia', + }, + { + type: 'text', + text: ', ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Courier New, monospace', + }, + }, + ], + text: 'Courier New', + }, + { + type: 'text', + text: ', ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Impact, sans-serif', + }, + }, + ], + text: 'Impact', + }, + { + type: 'text', + text: ', ', + }, + { + type: 'text', + marks: [ + { + type: 'fontFamily', + attrs: { + family: 'Comic Sans MS, cursive', + }, + }, + ], + text: 'Comic Sans', + }, + { + type: 'text', + text: '.', + }, + ], + }, + ], +} diff --git a/vue-font-family/src/main.ts b/vue-font-family/src/main.ts new file mode 100644 index 0000000000..0ed1fdfef8 --- /dev/null +++ b/vue-font-family/src/main.ts @@ -0,0 +1,5 @@ +import './app.css' +import { createApp } from 'vue' +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/vue-font-family/tsconfig.app.json b/vue-font-family/tsconfig.app.json new file mode 100644 index 0000000000..7c0e5ef3a8 --- /dev/null +++ b/vue-font-family/tsconfig.app.json @@ -0,0 +1,16 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "types": ["vite/client"], + "lib": ["ES2022", "DOM", "DOM.Iterable"], + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"] +} diff --git a/vue-font-family/tsconfig.json b/vue-font-family/tsconfig.json new file mode 100644 index 0000000000..1ffef600d9 --- /dev/null +++ b/vue-font-family/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/vue-font-family/tsconfig.node.json b/vue-font-family/tsconfig.node.json new file mode 100644 index 0000000000..8a67f62f4c --- /dev/null +++ b/vue-font-family/tsconfig.node.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "types": ["node"], + "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": ["vite.config.ts"] +} diff --git a/vue-font-family/vite.config.ts b/vue-font-family/vite.config.ts new file mode 100644 index 0000000000..2e9084402d --- /dev/null +++ b/vue-font-family/vite.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import tailwindcss from '@tailwindcss/vite' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue(), tailwindcss()], +})