@lalternative/editor 0.8.6 → 0.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -23,8 +23,11 @@ function placeToolbar({
23
23
  selection.bottom + clearance,
24
24
  // Never past the bottom edge: a selection low on the page would put
25
25
  // the toolbar off-screen once the callout's height is added.
26
- Math.max(0, viewport.height - toolbar.height - GAP)
27
- ) : Math.max(0, selection.top - GAP - toolbar.height);
26
+ Math.max(selection.top, viewport.height - toolbar.height - GAP)
27
+ ) : Math.max(
28
+ Math.min(GAP, selection.top),
29
+ selection.top - GAP - toolbar.height
30
+ );
28
31
  const centred = selection.x - toolbar.width / 2;
29
32
  const left = Math.max(0, Math.min(centred, viewport.width - toolbar.width));
30
33
  return { left, top, below };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/placement.ts","../src/SelectionToolbar.tsx","../src/useViewport.ts","../src/viewport.ts","../src/useSelectionToolbar.ts","../src/formats.ts","../src/EditorScreen.tsx","../src/slash.ts","../src/SlashMenu.tsx","../src/SlashCommands.ts","../src/InlineSuggestions.ts","../src/passage.ts","../src/markdown.ts"],"sourcesContent":["export interface SelectionRect {\n /** Horizontal centre of the selection. */\n x: number;\n top: number;\n bottom: number;\n}\n\nexport interface Size {\n width: number;\n height: number;\n}\n\nexport interface PlaceToolbarInput {\n selection: SelectionRect;\n toolbar: Size;\n viewport: Size;\n /** iOS renders its own callout above a selection, which covers a toolbar\n * placed there, so the caller asks for below on that platform. */\n preferBelow?: boolean;\n /** Height of the system's own selection callout — Cut/Copy/Paste — which is\n * drawn over the page and cannot be measured from it. Clearing it is what\n * keeps the two from overlapping. Zero where there is no such callout. */\n calloutHeight?: number;\n}\n\nexport interface ToolbarPlacement {\n left: number;\n top: number;\n below: boolean;\n}\n\nconst GAP = 8;\n\n/** iOS draws its Cut/Copy/Paste callout roughly this tall, below a selection\n * the page cannot see. Measured from the platform rather than derived: it is\n * a system surface, so nothing in the document reports its size. */\nexport const IOS_CALLOUT_HEIGHT = 44;\n\n/**\n * placeToolbar positions a floating toolbar against a selection.\n *\n * Kept apart from the component because it is the part that is wrong in subtle\n * ways — off-screen at the edges, hidden under a system callout — and the part\n * a test can pin down without a browser.\n */\nexport function placeToolbar({\n selection,\n toolbar,\n viewport,\n preferBelow = false,\n calloutHeight = 0,\n}: PlaceToolbarInput): ToolbarPlacement {\n const clearance = GAP + calloutHeight;\n const fitsAbove = selection.top - GAP - toolbar.height >= 0;\n const fitsBelow = selection.bottom + clearance + toolbar.height <= viewport.height;\n\n const below = preferBelow ? fitsBelow || !fitsAbove : !fitsAbove && fitsBelow;\n\n const top = below\n ? Math.min(\n selection.bottom + clearance,\n // Never past the bottom edge: a selection low on the page would put\n // the toolbar off-screen once the callout's height is added.\n Math.max(0, viewport.height - toolbar.height - GAP),\n )\n : Math.max(0, selection.top - GAP - toolbar.height);\n\n const centred = selection.x - toolbar.width / 2;\n const left = Math.max(0, Math.min(centred, viewport.width - toolbar.width));\n\n return { left, top, below };\n}\n","import { useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { placeToolbar, IOS_CALLOUT_HEIGHT, type SelectionRect } from \"./placement\";\nimport { useViewport } from \"./useViewport\";\n\nexport interface ToolbarAction {\n id: string;\n label: string;\n icon?: React.ReactNode;\n onSelect: (selectedText: string) => void;\n}\n\nexport interface BlockFormat {\n id: string;\n label: string;\n shortcut?: string;\n isActive?: boolean;\n onSelect: () => void;\n}\n\nexport interface SelectionToolbarProps {\n selectedText: string;\n selection: SelectionRect;\n actions?: ToolbarAction[];\n /** Heading levels and list kinds, rendered behind a single trigger. */\n formats?: BlockFormat[];\n formatsLabel?: string;\n /** iOS draws its own callout above a selection; the host detects it. */\n preferBelow?: boolean;\n container?: HTMLElement | null;\n}\n\nconst FALLBACK = { width: 320, height: 40 };\n\n// Whether the width used to place the bar is the one it actually has. The\n// fallback is a guess, and placing on a guess puts the bar off-centre by\n// however far the guess is wrong — which is invisible while the bar happens to\n// be 320px wide and obvious as soon as a host adds another action to it.\ntype Measured = { width: number; height: number; real: boolean };\n\nexport function SelectionToolbar({\n selectedText,\n selection,\n actions = [],\n formats = [],\n formatsLabel = \"Format\",\n preferBelow = false,\n container,\n}: SelectionToolbarProps) {\n const ref = useRef<HTMLDivElement>(null);\n const [size, setSize] = useState<Measured>({ ...FALLBACK, real: false });\n const [formatsOpen, setFormatsOpen] = useState(false);\n const viewport = useViewport();\n\n // Measured after paint rather than assumed: the toolbar's width depends on\n // labels the host supplies, and guessing it puts the toolbar off-centre.\n //\n // Observed rather than recomputed from a dependency list: the width follows\n // the rendered labels, and a list naming the things that happen to change it\n // today misses the next one — a longer label, a font that loads late.\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n const measure = () => {\n const rect = el.getBoundingClientRect();\n if (rect.width > 0)\n setSize((prev) =>\n prev.real && prev.width === rect.width && prev.height === rect.height\n ? prev\n : { width: rect.width, height: rect.height, real: true },\n );\n };\n measure();\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(measure);\n observer.observe(el);\n return () => observer.disconnect();\n }, []);\n\n useEffect(() => {\n if (!formatsOpen) return;\n const close = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) setFormatsOpen(false);\n };\n document.addEventListener(\"mousedown\", close);\n return () => document.removeEventListener(\"mousedown\", close);\n }, [formatsOpen]);\n\n if (typeof document === \"undefined\") return null;\n\n const place = placeToolbar({\n selection,\n toolbar: size,\n viewport: { width: viewport.width, height: viewport.height },\n preferBelow,\n // preferBelow is set by the same platform check that puts the callout\n // there, so it is what says whether there is one to clear.\n calloutHeight: preferBelow ? IOS_CALLOUT_HEIGHT : 0,\n });\n\n return createPortal(\n <div\n ref={ref}\n role=\"toolbar\"\n className=\"lalt-toolbar\"\n // Custom properties rather than left/top directly: under the mobile\n // breakpoint the stylesheet docks the toolbar to the bottom of the\n // screen and ignores these, which inline left/top would override.\n //\n // The inset is what the docked bar sits on top of. `bottom: 0` means the\n // bottom of the layout viewport, which the on-screen keyboard covers\n // without shrinking — so the bar has to be lifted by the strip the\n // keyboard occupies to stay in sight.\n style={\n {\n \"--lalt-toolbar-left\": `${place.left}px`,\n \"--lalt-toolbar-top\": `${place.top}px`,\n \"--lalt-keyboard-inset\": `${viewport.bottomInset}px`,\n // Hidden for the one frame between mounting and being measured:\n // that first paint places the bar on the fallback width, and showing\n // it means showing the bar jump once the real width arrives.\n // Hidden rather than unmounted — it has to be in the document to\n // have a width at all.\n visibility: size.real ? undefined : \"hidden\",\n } as React.CSSProperties\n }\n >\n {actions.map((action) => (\n <button\n key={action.id}\n type=\"button\"\n className=\"lalt-toolbar__button\"\n // The label is what the narrow layout hides, so it has to stay\n // reachable to anyone not reading the icon.\n aria-label={action.icon ? action.label : undefined}\n data-has-icon={action.icon ? \"\" : undefined}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => action.onSelect(selectedText)}\n >\n {action.icon}\n <span className=\"lalt-toolbar__label\">{action.label}</span>\n </button>\n ))}\n\n {formats.length > 0 && (\n <div className=\"lalt-toolbar__formats\">\n <button\n type=\"button\"\n className=\"lalt-toolbar__button\"\n aria-haspopup=\"menu\"\n aria-expanded={formatsOpen}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => setFormatsOpen((open) => !open)}\n >\n {formatsLabel}\n </button>\n {formatsOpen && (\n <div role=\"menu\" className=\"lalt-toolbar__menu\">\n {formats.map((format) => (\n <button\n key={format.id}\n type=\"button\"\n role=\"menuitem\"\n className=\"lalt-toolbar__item\"\n aria-current={format.isActive}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n format.onSelect();\n setFormatsOpen(false);\n }}\n >\n <span>{format.label}</span>\n {format.shortcut && (\n <kbd className=\"lalt-toolbar__shortcut\">{format.shortcut}</kbd>\n )}\n </button>\n ))}\n </div>\n )}\n </div>\n )}\n </div>,\n container ?? document.body,\n );\n}\n","import { useEffect, useState } from \"react\";\n\nimport { readViewport, type ViewportMetrics } from \"./viewport\";\n\nconst SERVER: ViewportMetrics = { width: 0, height: 0, offsetTop: 0, bottomInset: 0 };\n\nfunction read(): ViewportMetrics {\n if (typeof window === \"undefined\") return SERVER;\n return readViewport({\n innerWidth: window.innerWidth,\n innerHeight: window.innerHeight,\n visual: window.visualViewport,\n });\n}\n\n/**\n * useViewport tracks the area a fixed element can occupy, keyboard included.\n *\n * The visual viewport's own events are the only ones a raised keyboard fires:\n * `window.resize` does not run on iOS when the keyboard opens, so a component\n * listening to it alone keeps the measurements it took before the keys\n * appeared.\n */\nexport function useViewport(): ViewportMetrics {\n const [metrics, setMetrics] = useState<ViewportMetrics>(read);\n\n useEffect(() => {\n const update = () => setMetrics(read());\n update();\n\n const visual = window.visualViewport;\n visual?.addEventListener(\"resize\", update);\n // iOS scrolls the visual viewport rather than the document to keep the\n // caret above the keyboard, and reports it here and nowhere else.\n visual?.addEventListener(\"scroll\", update);\n window.addEventListener(\"resize\", update);\n window.addEventListener(\"orientationchange\", update);\n\n return () => {\n visual?.removeEventListener(\"resize\", update);\n visual?.removeEventListener(\"scroll\", update);\n window.removeEventListener(\"resize\", update);\n window.removeEventListener(\"orientationchange\", update);\n };\n }, []);\n\n return metrics;\n}\n","export interface VisualViewportLike {\n width: number;\n height: number;\n offsetTop: number;\n}\n\nexport interface ViewportMetrics {\n width: number;\n height: number;\n offsetTop: number;\n /** How much of the layout viewport's bottom is covered, the on-screen\n * keyboard included. Zero on a desktop browser and wherever the API is\n * missing. */\n bottomInset: number;\n}\n\nexport interface ReadViewportInput {\n innerWidth: number;\n innerHeight: number;\n visual?: VisualViewportLike | null;\n}\n\n/**\n * readViewport reports the area a fixed element can actually occupy.\n *\n * The distinction the on-screen keyboard turns on: `innerHeight` measures the\n * layout viewport, which the keyboard covers without shrinking, so an element\n * pinned to its bottom edge ends up underneath the keys. Only `visualViewport`\n * narrows, and the difference is the inset a docked toolbar has to clear.\n */\nexport function readViewport({\n innerWidth,\n innerHeight,\n visual,\n}: ReadViewportInput): ViewportMetrics {\n if (!visual) {\n return { width: innerWidth, height: innerHeight, offsetTop: 0, bottomInset: 0 };\n }\n\n // offsetTop is what iOS scrolls the visual viewport by; without it the\n // covered strip reads as smaller than it is once the page is scrolled under\n // a raised keyboard. Never negative: a viewport reported as taller than the\n // layout one — Android during a rotation, a browser mid-resize — would\n // otherwise push the toolbar down off the screen.\n const covered = innerHeight - visual.height - visual.offsetTop;\n\n return {\n width: visual.width,\n height: visual.height,\n offsetTop: visual.offsetTop,\n bottomInset: Math.max(0, Math.round(covered)),\n };\n}\n","import { useEffect, useState } from \"react\";\nimport type { Editor } from \"@tiptap/react\";\n\nimport type { SelectionRect } from \"./placement\";\n\nexport interface SelectionInfo {\n text: string;\n rect: SelectionRect;\n}\n\n/**\n * useSelectionToolbar reports the current non-empty selection of an editor.\n *\n * Returns null whenever there is nothing to act on, so the caller renders the\n * toolbar only while it has a target.\n */\nexport function useSelectionToolbar(editor: Editor | null): SelectionInfo | null {\n const [selection, setSelection] = useState<SelectionInfo | null>(null);\n\n useEffect(() => {\n if (!editor) return;\n\n const read = () => {\n const { state } = editor;\n const { from, to, empty } = state.selection;\n if (empty || !editor.isFocused) {\n setSelection(null);\n return;\n }\n\n const text = state.doc.textBetween(from, to, \" \").trim();\n if (text === \"\") {\n setSelection(null);\n return;\n }\n\n const start = editor.view.coordsAtPos(from);\n const end = editor.view.coordsAtPos(to);\n setSelection({\n text,\n rect: {\n x: (start.left + end.right) / 2,\n top: Math.min(start.top, end.top),\n bottom: Math.max(start.bottom, end.bottom),\n },\n });\n };\n\n read();\n editor.on(\"selectionUpdate\", read);\n editor.on(\"blur\", read);\n editor.on(\"focus\", read);\n return () => {\n editor.off(\"selectionUpdate\", read);\n editor.off(\"blur\", read);\n editor.off(\"focus\", read);\n };\n }, [editor]);\n\n return selection;\n}\n","import type { Editor } from \"@tiptap/react\";\n\n// Type-only: these commands are contributed by StarterKit's extensions, and\n// without the reference TypeScript does not know they exist on a chain. No\n// runtime import, so the host stays in charge of which extensions it mounts —\n// blockFormats simply returns entries whose commands no-op if one is missing.\nimport type {} from \"@tiptap/starter-kit\";\n\nimport type { BlockFormat } from \"./SelectionToolbar\";\n\nexport interface FormatLabels {\n paragraph: string;\n heading1: string;\n heading2: string;\n heading3: string;\n orderedList: string;\n bulletList: string;\n}\n\nexport const defaultFormatLabels: FormatLabels = {\n paragraph: \"Texte\",\n heading1: \"Titre 1\",\n heading2: \"Titre 2\",\n heading3: \"Titre 3\",\n orderedList: \"Liste numérotée\",\n bulletList: \"Liste à puces\",\n};\n\n/**\n * blockFormats is the set a writer reaches for on a selected passage.\n *\n * Labels are passed in rather than translated here: the package carries no\n * i18n runtime, so the host names things in whatever language it speaks.\n */\nexport function blockFormats(\n editor: Editor | null,\n labels: FormatLabels = defaultFormatLabels,\n): BlockFormat[] {\n if (!editor) return [];\n\n // Shortcuts are the ones TipTap's own extensions bind, read from their\n // source rather than invented: heading binds Mod-Alt-<level>, the list\n // extensions bind Mod-Shift-7/8, and setParagraph binds nothing at all.\n // A label promising a shortcut that does nothing is worse than no label.\n const mod = isApple() ? \"⌘\" : \"Ctrl\";\n\n return [\n {\n id: \"paragraph\",\n label: labels.paragraph,\n isActive: editor.isActive(\"paragraph\"),\n onSelect: () => editor.chain().focus().setParagraph().run(),\n },\n ...([1, 2, 3] as const).map((level) => ({\n id: `heading${level}`,\n label: labels[`heading${level}` as keyof FormatLabels],\n shortcut: `${mod}+Alt+${level}`,\n isActive: editor.isActive(\"heading\", { level }),\n onSelect: () => editor.chain().focus().toggleHeading({ level }).run(),\n })),\n {\n id: \"orderedList\",\n label: labels.orderedList,\n shortcut: `${mod}+Shift+7`,\n isActive: editor.isActive(\"orderedList\"),\n onSelect: () => editor.chain().focus().toggleOrderedList().run(),\n },\n {\n id: \"bulletList\",\n label: labels.bulletList,\n shortcut: `${mod}+Shift+8`,\n isActive: editor.isActive(\"bulletList\"),\n onSelect: () => editor.chain().focus().toggleBulletList().run(),\n },\n ];\n}\n\nfunction isApple(): boolean {\n if (typeof navigator === \"undefined\") return false;\n return /Mac|iPad|iPhone|iPod/.test(navigator.platform || navigator.userAgent);\n}\n","import { useViewport } from \"./useViewport\";\n\nexport type SaveState = \"idle\" | \"saving\" | \"saved\" | \"dirty\";\n\nexport interface EditorScreenProps {\n /** The writing surface. Mounted by the host, which owns the TipTap instance\n * and whichever extensions it needs. */\n children: React.ReactNode;\n title?: string;\n onTitleChange?: (title: string) => void;\n titlePlaceholder?: string;\n titleLabel?: string;\n saveState?: SaveState;\n saveLabels?: SaveLabels;\n /** Drawn at the far left of the bar; a back control belongs here. */\n lead?: React.ReactNode;\n /** Drawn at the far right. Keep it to what a thumb can reach on a phone —\n * one primary control and an overflow, not a row of five. */\n actions?: React.ReactNode;\n /** Sits between the save dot and the actions: a character count, a badge. */\n status?: React.ReactNode;\n /** Between the title and the writing surface — a dictation control, a hint. */\n aside?: React.ReactNode;\n /** Below the writing surface, inside the scrolling column. */\n footer?: React.ReactNode;\n /** Above the writing surface and below the bar, pinned while the page\n * scrolls: tabs over variants of one document belong here. */\n subnav?: React.ReactNode;\n /** Fills its container instead of the viewport, for a host whose shell\n * already owns the page height. */\n fill?: boolean;\n className?: string;\n}\n\nexport interface SaveLabels {\n saving: string;\n saved: string;\n dirty: string;\n}\n\nexport const defaultSaveLabels: SaveLabels = {\n saving: \"Enregistrement en cours\",\n saved: \"Enregistré\",\n dirty: \"Modifications non enregistrées\",\n};\n\n/**\n * EditorScreen is the page a writing surface sits in: a command bar, a title,\n * one column of prose, and slots for whatever the host puts around it.\n *\n * It renders and measures; it never fetches, saves or routes. `saveState` is\n * displayed, not computed — the host owns the debounce and the request, so the\n * same screen serves an autosaving draft and a form with an explicit button.\n *\n * Commands sit at the top rather than in a bar at the bottom. Below the fold\n * they are gone the moment the document runs past one screen, and pinned to\n * the bottom they eat a sixth of what the on-screen keyboard leaves.\n */\nexport function EditorScreen({\n children,\n title,\n onTitleChange,\n titlePlaceholder = \"Titre\",\n titleLabel = \"Titre\",\n saveState,\n saveLabels = defaultSaveLabels,\n lead,\n actions,\n status,\n aside,\n footer,\n subnav,\n fill = false,\n className,\n}: EditorScreenProps) {\n const { bottomInset } = useViewport();\n const withTitle = onTitleChange !== undefined;\n\n return (\n <div\n className={[\n \"lalt-screen\",\n fill ? \"lalt-screen--fill\" : \"\",\n className ?? \"\",\n ]\n .filter(Boolean)\n .join(\" \")}\n // The keyboard covers the layout viewport without shrinking it, so the\n // column's last lines sit under the keys unless the strip is padded out.\n style={{ \"--lalt-keyboard\": `${bottomInset}px` } as React.CSSProperties}\n >\n <header className=\"lalt-screen__bar\">\n {lead}\n {saveState && (\n <SaveDot state={saveState} labels={saveLabels} />\n )}\n {status && <div className=\"lalt-screen__status\">{status}</div>}\n {actions && <div className=\"lalt-screen__actions\">{actions}</div>}\n </header>\n\n {subnav && <div className=\"lalt-screen__subnav\">{subnav}</div>}\n\n <div className=\"lalt-screen__scroll\">\n <div className=\"lalt-screen__column\">\n {withTitle && (\n <input\n className=\"lalt-screen__title\"\n value={title ?? \"\"}\n onChange={(event) => onTitleChange(event.target.value)}\n placeholder={titlePlaceholder}\n aria-label={titleLabel}\n />\n )}\n {aside && <div className=\"lalt-screen__aside\">{aside}</div>}\n {children}\n {footer && <div className=\"lalt-screen__footer\">{footer}</div>}\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * A dot rather than a word: the state is worth a glance, never a read, and a\n * label that appears and vanishes leaves the author wondering what it meant\n * once it is gone. Screen readers get the sentence instead.\n */\nfunction SaveDot({ state, labels }: { state: SaveState; labels: SaveLabels }) {\n if (state === \"idle\") return <span className=\"lalt-screen__save\" />;\n\n const label =\n state === \"saving\" ? labels.saving : state === \"dirty\" ? labels.dirty : labels.saved;\n\n return (\n <span className=\"lalt-screen__save\" role=\"status\" aria-live=\"polite\">\n <span className={`lalt-screen__dot lalt-screen__dot--${state}`} aria-hidden />\n <span className=\"lalt-screen__sr\">{label}</span>\n </span>\n );\n}\n","import type { Editor } from \"@tiptap/react\";\nimport type { Range } from \"@tiptap/core\";\n\n// Type-only, as in formats.ts: StarterKit contributes these commands to a\n// chain, and without the reference TypeScript does not know they exist. The\n// host stays in charge of which extensions it actually mounts.\nimport type {} from \"@tiptap/starter-kit\";\n\n/**\n * SlashItem is one entry of the `/` menu.\n *\n * icon is a node rather than a component type: the package carries no icon\n * library, so the host renders whichever one it already ships.\n */\nexport interface SlashItem {\n id: string;\n title: string;\n description?: string;\n shortcut?: string;\n icon?: React.ReactNode;\n command: (args: { editor: Editor; range: Range }) => void;\n}\n\nexport interface SlashLabels {\n heading1: string;\n heading2: string;\n bulletList: string;\n orderedList: string;\n blockquote: string;\n codeBlock: string;\n horizontalRule: string;\n}\n\nexport const defaultSlashLabels: SlashLabels = {\n heading1: \"Titre 1\",\n heading2: \"Titre 2\",\n bulletList: \"Liste à puces\",\n orderedList: \"Liste numérotée\",\n blockquote: \"Citation\",\n codeBlock: \"Bloc de code\",\n horizontalRule: \"Séparateur\",\n};\n\nexport interface SlashDescriptions {\n heading1?: string;\n heading2?: string;\n bulletList?: string;\n orderedList?: string;\n blockquote?: string;\n codeBlock?: string;\n horizontalRule?: string;\n}\n\nexport interface DefaultSlashItemsOptions {\n labels?: SlashLabels;\n descriptions?: SlashDescriptions;\n icons?: Partial<Record<DefaultSlashId, React.ReactNode>>;\n}\n\nexport type DefaultSlashId =\n | \"heading1\"\n | \"heading2\"\n | \"bulletList\"\n | \"orderedList\"\n | \"blockquote\"\n | \"codeBlock\"\n | \"horizontalRule\";\n\n/**\n * defaultSlashItems is the block set every writing surface shares.\n *\n * Returned as data rather than rendered behind flags: a host adds, removes or\n * reorders entries by composing the array, so a surface that needs one more\n * command does not push another boolean into this signature.\n */\nexport function defaultSlashItems({\n labels = defaultSlashLabels,\n descriptions = {},\n icons = {},\n}: DefaultSlashItemsOptions = {}): SlashItem[] {\n const item = (\n id: DefaultSlashId,\n shortcut: string,\n run: (editor: Editor) => void,\n ): SlashItem => ({\n id,\n title: labels[id],\n description: descriptions[id],\n shortcut,\n icon: icons[id],\n command: ({ editor, range }) => {\n editor.chain().focus().deleteRange(range).run();\n run(editor);\n },\n });\n\n return [\n item(\"heading1\", \"#\", (editor) =>\n editor.chain().focus().setNode(\"heading\", { level: 1 }).run(),\n ),\n item(\"heading2\", \"##\", (editor) =>\n editor.chain().focus().setNode(\"heading\", { level: 2 }).run(),\n ),\n item(\"bulletList\", \"-\", (editor) =>\n editor.chain().focus().toggleBulletList().run(),\n ),\n item(\"orderedList\", \"1.\", (editor) =>\n editor.chain().focus().toggleOrderedList().run(),\n ),\n item(\"blockquote\", \">\", (editor) =>\n editor.chain().focus().toggleBlockquote().run(),\n ),\n item(\"codeBlock\", \"```\", (editor) =>\n editor.chain().focus().toggleCodeBlock().run(),\n ),\n item(\"horizontalRule\", \"---\", (editor) =>\n editor.chain().focus().setHorizontalRule().run(),\n ),\n ];\n}\n\n/** filterSlashItems matches the query against titles the way the menu does. */\nexport function filterSlashItems(items: SlashItem[], query: string): SlashItem[] {\n const q = query.trim().toLowerCase();\n if (q === \"\") return items;\n return items.filter((item) => item.title.toLowerCase().includes(q));\n}\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\n\nimport type { SlashItem } from \"./slash\";\n\nexport interface SlashMenuHandle {\n onKeyDown: (props: { event: KeyboardEvent }) => boolean;\n}\n\nexport interface SlashMenuProps {\n items: SlashItem[];\n command: (item: SlashItem) => void;\n emptyLabel?: string;\n}\n\nexport const SlashMenu = forwardRef<SlashMenuHandle, SlashMenuProps>(\n function SlashMenu({ items, command, emptyLabel = \"Aucune commande\" }, ref) {\n const [selectedIndex, setSelectedIndex] = useState(0);\n\n // The suggestion plugin rebuilds these props on every keystroke, so the\n // imperative handle reads refs rather than a captured render closure —\n // otherwise Enter runs the command the menu showed one keystroke ago.\n const itemsRef = useRef(items);\n itemsRef.current = items;\n const commandRef = useRef(command);\n commandRef.current = command;\n const selectedRef = useRef(selectedIndex);\n selectedRef.current = selectedIndex;\n\n const ids = items.map((item) => item.id).join(\" \");\n useEffect(() => {\n setSelectedIndex(0);\n selectedRef.current = 0;\n }, [ids]);\n\n const selectItem = useCallback((index: number) => {\n const item = itemsRef.current[index];\n if (item) commandRef.current(item);\n }, []);\n\n const move = useCallback((delta: number) => {\n const count = itemsRef.current.length;\n if (count === 0) return;\n const next = (selectedRef.current + delta + count) % count;\n selectedRef.current = next;\n setSelectedIndex(next);\n }, []);\n\n useImperativeHandle(ref, () => ({\n onKeyDown: ({ event }) => {\n if (event.key === \"ArrowUp\") {\n move(-1);\n return true;\n }\n if (event.key === \"ArrowDown\") {\n move(1);\n return true;\n }\n if (event.key === \"Enter\" || event.key === \"Tab\") {\n if (itemsRef.current.length === 0) return false;\n selectItem(selectedRef.current);\n return true;\n }\n return false;\n },\n }));\n\n if (items.length === 0) {\n return <div className=\"lalt-slash lalt-slash--empty\">{emptyLabel}</div>;\n }\n\n return (\n <div className=\"lalt-slash\" role=\"listbox\">\n {items.map((item, index) => (\n <button\n key={item.id}\n type=\"button\"\n role=\"option\"\n aria-selected={index === selectedIndex}\n className={\n index === selectedIndex\n ? \"lalt-slash__item lalt-slash__item--active\"\n : \"lalt-slash__item\"\n }\n // Losing editor focus tears the suggestion plugin down before a\n // click lands, so the item would never run its command.\n onMouseDown={(event) => {\n event.preventDefault();\n selectItem(index);\n }}\n onMouseEnter={() => {\n selectedRef.current = index;\n setSelectedIndex(index);\n }}\n >\n {item.icon && (\n <span className=\"lalt-slash__icon\" aria-hidden>\n {item.icon}\n </span>\n )}\n <span className=\"lalt-slash__text\">\n <span className=\"lalt-slash__title\">{item.title}</span>\n {item.description && (\n <span className=\"lalt-slash__description\">{item.description}</span>\n )}\n </span>\n {item.shortcut && (\n <kbd className=\"lalt-slash__shortcut\">{item.shortcut}</kbd>\n )}\n </button>\n ))}\n </div>\n );\n },\n);\n","import { Extension } from \"@tiptap/core\";\nimport { ReactRenderer } from \"@tiptap/react\";\nimport { PluginKey } from \"@tiptap/pm/state\";\nimport Suggestion, { type SuggestionOptions, type SuggestionProps } from \"@tiptap/suggestion\";\n\nimport { SlashMenu, type SlashMenuHandle, type SlashMenuProps } from \"./SlashMenu\";\nimport { filterSlashItems, type SlashItem } from \"./slash\";\n\nexport interface SlashCommandsOptions {\n /** Read on every keystroke, so a host can swap the set without remounting\n * TipTap — rebuilding the extensions array wipes the text being typed. */\n items: () => SlashItem[];\n char: string;\n emptyLabel?: string;\n /** Distinguishes concurrently mounted editors: two surfaces sharing one key\n * would answer each other's suggestions. */\n pluginKey: PluginKey;\n}\n\nconst OFFSET = 6;\n\nexport const SlashCommands = Extension.create<SlashCommandsOptions>({\n name: \"laSlashCommands\",\n\n addOptions() {\n return {\n items: () => [],\n char: \"/\",\n pluginKey: new PluginKey(\"laSlashCommands\"),\n };\n },\n\n addProseMirrorPlugins() {\n const { items, char, emptyLabel, pluginKey } = this.options;\n\n const suggestion: Omit<SuggestionOptions<SlashItem>, \"editor\"> = {\n char,\n pluginKey,\n startOfLine: false,\n items: ({ query }) => filterSlashItems(items(), query),\n render: () => {\n let component: ReactRenderer<SlashMenuHandle, SlashMenuProps> | null = null;\n let container: HTMLDivElement | null = null;\n\n const place = (clientRect: SuggestionProps<SlashItem>[\"clientRect\"]) => {\n if (!container || !clientRect) return;\n const rect = clientRect();\n if (!rect) return;\n container.style.top = `${rect.bottom + OFFSET}px`;\n container.style.left = `${rect.left}px`;\n };\n\n const close = () => {\n container?.remove();\n container = null;\n component?.destroy();\n component = null;\n };\n\n return {\n onStart: (props) => {\n component = new ReactRenderer(SlashMenu, {\n props: { ...props, emptyLabel },\n editor: props.editor,\n });\n container = document.createElement(\"div\");\n container.className = \"lalt-slash-anchor\";\n container.appendChild(component.element);\n document.body.appendChild(container);\n place(props.clientRect);\n },\n onUpdate: (props) => {\n component?.updateProps({ ...props, emptyLabel });\n place(props.clientRect);\n },\n onKeyDown: (props) => {\n if (props.event.key === \"Escape\") {\n close();\n return true;\n }\n return component?.ref?.onKeyDown(props) ?? false;\n },\n onExit: close,\n };\n },\n };\n\n return [\n Suggestion({\n editor: this.editor,\n ...suggestion,\n command: ({ editor, range, props }) => props.command({ editor, range }),\n }),\n ];\n },\n});\n","import { Extension } from \"@tiptap/core\";\nimport { Plugin, PluginKey } from \"@tiptap/pm/state\";\nimport { Decoration, DecorationSet } from \"@tiptap/pm/view\";\nimport type { Node as ProseMirrorNode } from \"@tiptap/pm/model\";\n\nexport interface InlineSuggestion {\n id: string;\n /** Document positions of the passage this replaces. */\n from: number;\n to: number;\n after: string;\n}\n\nexport interface SuggestionActions {\n onAccept: (id: string) => void;\n onReject: (id: string) => void;\n acceptLabel: string;\n rejectLabel: string;\n}\n\nexport const inlineSuggestionsKey = new PluginKey<DecorationSet>(\"lalt-inline-suggestions\");\n\n/**\n * InlineSuggestions renders proposed edits where they belong: over the passage\n * they replace, with the new text right after it.\n *\n * Decorations, never document changes. The editor's content stays exactly what\n * the author wrote until they accept, so the draft is saveable mid-review and\n * a rejected suggestion leaves nothing behind — which a document-level insert\n * could not promise.\n */\nexport const InlineSuggestions = Extension.create<{ actions: SuggestionActions | null }>({\n name: \"lalt-inline-suggestions\",\n\n addOptions() {\n return { actions: null };\n },\n\n addProseMirrorPlugins() {\n const options = this.options;\n\n return [\n new Plugin<DecorationSet>({\n key: inlineSuggestionsKey,\n\n state: {\n init: () => DecorationSet.empty,\n apply(tr, old) {\n const set = tr.getMeta(inlineSuggestionsKey) as InlineSuggestion[] | undefined;\n if (set) return build(tr.doc, set, options.actions);\n // Positions follow the document as the author keeps typing around\n // a pending suggestion.\n return old.map(tr.mapping, tr.doc);\n },\n },\n\n props: {\n decorations(state) {\n return inlineSuggestionsKey.getState(state) ?? DecorationSet.empty;\n },\n },\n }),\n ];\n },\n});\n\nfunction build(\n doc: ProseMirrorNode,\n suggestions: InlineSuggestion[],\n actions: SuggestionActions | null,\n): DecorationSet {\n const decorations: Decoration[] = [];\n\n for (const s of suggestions) {\n if (s.from < 0 || s.to > doc.content.size || s.from >= s.to) continue;\n\n decorations.push(\n Decoration.inline(s.from, s.to, { class: \"lalt-suggestion__old\" }),\n Decoration.widget(s.to, () => renderProposal(s, actions), {\n // The widget belongs to the suggestion that precedes it, so typing at\n // the boundary does not push it around.\n side: 1,\n ignoreSelection: true,\n }),\n );\n }\n return DecorationSet.create(doc, decorations);\n}\n\nfunction renderProposal(s: InlineSuggestion, actions: SuggestionActions | null): HTMLElement {\n const wrap = document.createElement(\"span\");\n wrap.className = \"lalt-suggestion\";\n wrap.setAttribute(\"contenteditable\", \"false\");\n\n const text = document.createElement(\"span\");\n text.className = \"lalt-suggestion__new\";\n text.textContent = s.after;\n wrap.appendChild(text);\n\n if (actions) {\n wrap.appendChild(button(\"reject\", actions.rejectLabel, \"✕\", () => actions.onReject(s.id)));\n wrap.appendChild(button(\"accept\", actions.acceptLabel, \"✓\", () => actions.onAccept(s.id)));\n }\n return wrap;\n}\n\nfunction button(kind: string, label: string, glyph: string, onClick: () => void): HTMLElement {\n const el = document.createElement(\"button\");\n el.type = \"button\";\n el.className = `lalt-suggestion__action lalt-suggestion__action--${kind}`;\n el.setAttribute(\"aria-label\", label);\n el.textContent = glyph;\n // mousedown would move the selection into the widget before the click lands.\n el.addEventListener(\"mousedown\", (e) => e.preventDefault());\n el.addEventListener(\"click\", (e) => {\n e.preventDefault();\n onClick();\n });\n return el;\n}\n","export interface TextRange {\n from: number;\n to: number;\n}\n\nexport interface RevisionPart {\n before: string;\n after: string;\n}\n\n// Letters, digits, and the marks that belong inside a French word.\nconst WORD = /[\\p{L}\\p{N}'’-]/u;\n\n/**\n * expandToWords grows a range out to whole words.\n *\n * A selection dragged across a line lands mid-word more often than not, and a\n * model handed \"ns de production\" answers for a fragment — which then reads as\n * a truncated diff the author has to decode.\n */\nexport function expandToWords(text: string, from: number, to: number): TextRange {\n if (from >= to) return { from, to };\n\n let start = from;\n while (start > 0 && WORD.test(text[start - 1]) && WORD.test(text[start])) start--;\n\n let end = to;\n while (end < text.length && WORD.test(text[end]) && WORD.test(text[end - 1])) end++;\n\n return { from: start, to: end };\n}\n\n/**\n * splitRevision turns one model answer into the decisions it really contains.\n *\n * A three-paragraph rewrite accepted or rejected as a whole forces the author\n * to keep a weak paragraph for a strong one, so each paragraph becomes its own\n * decision — but only when both sides line up. When the model merged or split\n * paragraphs, position no longer maps them, and pairing by index would show a\n * diff that never existed.\n */\nexport function splitRevision(before: string, after: string): RevisionPart[] {\n if (before.trim() === after.trim()) return [];\n\n const olds = paragraphs(before);\n const news = paragraphs(after);\n\n if (olds.length !== news.length || olds.length === 0) {\n return [{ before: before.trim(), after: after.trim() }];\n }\n\n const parts: RevisionPart[] = [];\n for (let i = 0; i < olds.length; i++) {\n if (olds[i] !== news[i]) parts.push({ before: olds[i], after: news[i] });\n }\n return parts;\n}\n\nfunction paragraphs(s: string): string[] {\n return s\n .split(/\\n\\s*\\n/)\n .map((p) => p.trim())\n .filter((p) => p !== \"\");\n}\n","import type { RichDoc, RichNode } from \"./document\";\n\ninterface Mark {\n type?: string;\n attrs?: Record<string, unknown>;\n}\n\n/**\n * docToMarkdown renders a TipTap document as the markdown a blog accepts.\n *\n * Written here rather than taken from a library: the editor only ever produces\n * the handful of nodes StarterKit gives it, and a general markdown serializer\n * brings a dependency and its escaping rules for output a human is about to\n * paste into their own site.\n */\nexport function docToMarkdown(doc: RichDoc): string {\n return blocks(doc.content ?? [])\n .filter((b) => b !== \"\")\n .join(\"\\n\\n\");\n}\n\nfunction blocks(nodes: RichNode[]): string[] {\n return nodes.map(block);\n}\n\nfunction block(node: RichNode): string {\n switch (node.type) {\n case \"heading\": {\n const level = Number(node.attrs?.level ?? 2);\n return `${\"#\".repeat(Math.min(6, Math.max(1, level)))} ${inline(node.content)}`;\n }\n case \"blockquote\":\n return blocks(node.content ?? [])\n .filter((b) => b !== \"\")\n .map((b) =>\n b\n .split(\"\\n\")\n .map((line) => `> ${line}`)\n .join(\"\\n\"),\n )\n .join(\"\\n>\\n\");\n case \"codeBlock\": {\n const language = String(node.attrs?.language ?? \"\");\n return `\\`\\`\\`${language}\\n${inline(node.content)}\\n\\`\\`\\``;\n }\n case \"bulletList\":\n return listItems(node).map((item) => `- ${item}`).join(\"\\n\");\n case \"orderedList\":\n return listItems(node)\n .map((item, i) => `${i + 1}. ${item}`)\n .join(\"\\n\");\n case \"horizontalRule\":\n return \"---\";\n default:\n return inline(node.content);\n }\n}\n\nfunction listItems(list: RichNode): string[] {\n return (list.content ?? []).map((item) =>\n blocks(item.content ?? [])\n .filter((b) => b !== \"\")\n // Continuation lines are indented so the item stays one item.\n .join(\"\\n\\n\")\n .split(\"\\n\")\n .join(\"\\n \"),\n );\n}\n\nfunction inline(nodes: RichNode[] | undefined): string {\n return (nodes ?? []).map(text).join(\"\");\n}\n\nfunction text(node: RichNode): string {\n if (node.type === \"hardBreak\") return \"\\n\";\n if (typeof node.text !== \"string\") return inline(node.content);\n\n let out = node.text;\n // Innermost first, so bold inside a link wraps the text and not the URL.\n for (const mark of ((node.marks as Mark[] | undefined) ?? []).slice().reverse()) {\n switch (mark.type) {\n case \"bold\":\n out = `**${out}**`;\n break;\n case \"italic\":\n out = `*${out}*`;\n break;\n case \"strike\":\n out = `~~${out}~~`;\n break;\n case \"code\":\n out = `\\`${out}\\``;\n break;\n case \"link\":\n out = `[${out}](${String(mark.attrs?.href ?? \"\")})`;\n break;\n }\n }\n return out;\n}\n"],"mappings":";;;;;;;;AA+BA,IAAM,MAAM;AAKL,IAAM,qBAAqB;AAS3B,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,gBAAgB;AAClB,GAAwC;AACtC,QAAM,YAAY,MAAM;AACxB,QAAM,YAAY,UAAU,MAAM,MAAM,QAAQ,UAAU;AAC1D,QAAM,YAAY,UAAU,SAAS,YAAY,QAAQ,UAAU,SAAS;AAE5E,QAAM,QAAQ,cAAc,aAAa,CAAC,YAAY,CAAC,aAAa;AAEpE,QAAM,MAAM,QACR,KAAK;AAAA,IACH,UAAU,SAAS;AAAA;AAAA;AAAA,IAGnB,KAAK,IAAI,GAAG,SAAS,SAAS,QAAQ,SAAS,GAAG;AAAA,EACpD,IACA,KAAK,IAAI,GAAG,UAAU,MAAM,MAAM,QAAQ,MAAM;AAEpD,QAAM,UAAU,UAAU,IAAI,QAAQ,QAAQ;AAC9C,QAAM,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,SAAS,SAAS,QAAQ,QAAQ,KAAK,CAAC;AAE1E,SAAO,EAAE,MAAM,KAAK,MAAM;AAC5B;;;ACvEA,SAAS,aAAAA,YAAW,iBAAiB,QAAQ,YAAAC,iBAAgB;AAC7D,SAAS,oBAAoB;;;ACD7B,SAAS,WAAW,gBAAgB;;;AC8B7B,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AACF,GAAuC;AACrC,MAAI,CAAC,QAAQ;AACX,WAAO,EAAE,OAAO,YAAY,QAAQ,aAAa,WAAW,GAAG,aAAa,EAAE;AAAA,EAChF;AAOA,QAAM,UAAU,cAAc,OAAO,SAAS,OAAO;AAErD,SAAO;AAAA,IACL,OAAO,OAAO;AAAA,IACd,QAAQ,OAAO;AAAA,IACf,WAAW,OAAO;AAAA,IAClB,aAAa,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,CAAC;AAAA,EAC9C;AACF;;;ADhDA,IAAM,SAA0B,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,aAAa,EAAE;AAEpF,SAAS,OAAwB;AAC/B,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAO,aAAa;AAAA,IAClB,YAAY,OAAO;AAAA,IACnB,aAAa,OAAO;AAAA,IACpB,QAAQ,OAAO;AAAA,EACjB,CAAC;AACH;AAUO,SAAS,cAA+B;AAC7C,QAAM,CAAC,SAAS,UAAU,IAAI,SAA0B,IAAI;AAE5D,YAAU,MAAM;AACd,UAAM,SAAS,MAAM,WAAW,KAAK,CAAC;AACtC,WAAO;AAEP,UAAM,SAAS,OAAO;AACtB,YAAQ,iBAAiB,UAAU,MAAM;AAGzC,YAAQ,iBAAiB,UAAU,MAAM;AACzC,WAAO,iBAAiB,UAAU,MAAM;AACxC,WAAO,iBAAiB,qBAAqB,MAAM;AAEnD,WAAO,MAAM;AACX,cAAQ,oBAAoB,UAAU,MAAM;AAC5C,cAAQ,oBAAoB,UAAU,MAAM;AAC5C,aAAO,oBAAoB,UAAU,MAAM;AAC3C,aAAO,oBAAoB,qBAAqB,MAAM;AAAA,IACxD;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO;AACT;;;ADkFQ,SAYE,KAZF;AAhGR,IAAM,WAAW,EAAE,OAAO,KAAK,QAAQ,GAAG;AAQnC,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA,UAAU,CAAC;AAAA,EACX,UAAU,CAAC;AAAA,EACX,eAAe;AAAA,EACf,cAAc;AAAA,EACd;AACF,GAA0B;AACxB,QAAM,MAAM,OAAuB,IAAI;AACvC,QAAM,CAAC,MAAM,OAAO,IAAIC,UAAmB,EAAE,GAAG,UAAU,MAAM,MAAM,CAAC;AACvE,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,KAAK;AACpD,QAAM,WAAW,YAAY;AAQ7B,kBAAgB,MAAM;AACpB,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AACT,UAAM,UAAU,MAAM;AACpB,YAAM,OAAO,GAAG,sBAAsB;AACtC,UAAI,KAAK,QAAQ;AACf;AAAA,UAAQ,CAAC,SACP,KAAK,QAAQ,KAAK,UAAU,KAAK,SAAS,KAAK,WAAW,KAAK,SAC3D,OACA,EAAE,OAAO,KAAK,OAAO,QAAQ,KAAK,QAAQ,MAAM,KAAK;AAAA,QAC3D;AAAA,IACJ;AACA,YAAQ;AACR,QAAI,OAAO,mBAAmB,YAAa;AAC3C,UAAM,WAAW,IAAI,eAAe,OAAO;AAC3C,aAAS,QAAQ,EAAE;AACnB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,CAAC;AAEL,EAAAC,WAAU,MAAM;AACd,QAAI,CAAC,YAAa;AAClB,UAAM,QAAQ,CAAC,MAAkB;AAC/B,UAAI,IAAI,WAAW,CAAC,IAAI,QAAQ,SAAS,EAAE,MAAc,EAAG,gBAAe,KAAK;AAAA,IAClF;AACA,aAAS,iBAAiB,aAAa,KAAK;AAC5C,WAAO,MAAM,SAAS,oBAAoB,aAAa,KAAK;AAAA,EAC9D,GAAG,CAAC,WAAW,CAAC;AAEhB,MAAI,OAAO,aAAa,YAAa,QAAO;AAE5C,QAAM,QAAQ,aAAa;AAAA,IACzB;AAAA,IACA,SAAS;AAAA,IACT,UAAU,EAAE,OAAO,SAAS,OAAO,QAAQ,SAAS,OAAO;AAAA,IAC3D;AAAA;AAAA;AAAA,IAGA,eAAe,cAAc,qBAAqB;AAAA,EACpD,CAAC;AAED,SAAO;AAAA,IACL;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,WAAU;AAAA,QASV,OACE;AAAA,UACE,uBAAuB,GAAG,MAAM,IAAI;AAAA,UACpC,sBAAsB,GAAG,MAAM,GAAG;AAAA,UAClC,yBAAyB,GAAG,SAAS,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAMhD,YAAY,KAAK,OAAO,SAAY;AAAA,QACtC;AAAA,QAGD;AAAA,kBAAQ,IAAI,CAAC,WACZ;AAAA,YAAC;AAAA;AAAA,cAEC,MAAK;AAAA,cACL,WAAU;AAAA,cAGV,cAAY,OAAO,OAAO,OAAO,QAAQ;AAAA,cACzC,iBAAe,OAAO,OAAO,KAAK;AAAA,cAClC,aAAa,CAAC,MAAM,EAAE,eAAe;AAAA,cACrC,SAAS,MAAM,OAAO,SAAS,YAAY;AAAA,cAE1C;AAAA,uBAAO;AAAA,gBACR,oBAAC,UAAK,WAAU,uBAAuB,iBAAO,OAAM;AAAA;AAAA;AAAA,YAX/C,OAAO;AAAA,UAYd,CACD;AAAA,UAEA,QAAQ,SAAS,KAChB,qBAAC,SAAI,WAAU,yBACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,iBAAc;AAAA,gBACd,iBAAe;AAAA,gBACf,aAAa,CAAC,MAAM,EAAE,eAAe;AAAA,gBACrC,SAAS,MAAM,eAAe,CAAC,SAAS,CAAC,IAAI;AAAA,gBAE5C;AAAA;AAAA,YACH;AAAA,YACC,eACC,oBAAC,SAAI,MAAK,QAAO,WAAU,sBACxB,kBAAQ,IAAI,CAAC,WACZ;AAAA,cAAC;AAAA;AAAA,gBAEC,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,gBAAc,OAAO;AAAA,gBACrB,aAAa,CAAC,MAAM,EAAE,eAAe;AAAA,gBACrC,SAAS,MAAM;AACb,yBAAO,SAAS;AAChB,iCAAe,KAAK;AAAA,gBACtB;AAAA,gBAEA;AAAA,sCAAC,UAAM,iBAAO,OAAM;AAAA,kBACnB,OAAO,YACN,oBAAC,SAAI,WAAU,0BAA0B,iBAAO,UAAS;AAAA;AAAA;AAAA,cAbtD,OAAO;AAAA,YAed,CACD,GACH;AAAA,aAEJ;AAAA;AAAA;AAAA,IAEJ;AAAA,IACA,aAAa,SAAS;AAAA,EACxB;AACF;;;AGzLA,SAAS,aAAAC,YAAW,YAAAC,iBAAgB;AAgB7B,SAAS,oBAAoB,QAA6C;AAC/E,QAAM,CAAC,WAAW,YAAY,IAAIA,UAA+B,IAAI;AAErE,EAAAD,WAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AAEb,UAAME,QAAO,MAAM;AACjB,YAAM,EAAE,MAAM,IAAI;AAClB,YAAM,EAAE,MAAM,IAAI,MAAM,IAAI,MAAM;AAClC,UAAI,SAAS,CAAC,OAAO,WAAW;AAC9B,qBAAa,IAAI;AACjB;AAAA,MACF;AAEA,YAAMC,QAAO,MAAM,IAAI,YAAY,MAAM,IAAI,GAAG,EAAE,KAAK;AACvD,UAAIA,UAAS,IAAI;AACf,qBAAa,IAAI;AACjB;AAAA,MACF;AAEA,YAAM,QAAQ,OAAO,KAAK,YAAY,IAAI;AAC1C,YAAM,MAAM,OAAO,KAAK,YAAY,EAAE;AACtC,mBAAa;AAAA,QACX,MAAAA;AAAA,QACA,MAAM;AAAA,UACJ,IAAI,MAAM,OAAO,IAAI,SAAS;AAAA,UAC9B,KAAK,KAAK,IAAI,MAAM,KAAK,IAAI,GAAG;AAAA,UAChC,QAAQ,KAAK,IAAI,MAAM,QAAQ,IAAI,MAAM;AAAA,QAC3C;AAAA,MACF,CAAC;AAAA,IACH;AAEA,IAAAD,MAAK;AACL,WAAO,GAAG,mBAAmBA,KAAI;AACjC,WAAO,GAAG,QAAQA,KAAI;AACtB,WAAO,GAAG,SAASA,KAAI;AACvB,WAAO,MAAM;AACX,aAAO,IAAI,mBAAmBA,KAAI;AAClC,aAAO,IAAI,QAAQA,KAAI;AACvB,aAAO,IAAI,SAASA,KAAI;AAAA,IAC1B;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO;AACT;;;ACzCO,IAAM,sBAAoC;AAAA,EAC/C,WAAW;AAAA,EACX,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,aAAa;AAAA,EACb,YAAY;AACd;AAQO,SAAS,aACd,QACA,SAAuB,qBACR;AACf,MAAI,CAAC,OAAQ,QAAO,CAAC;AAMrB,QAAM,MAAM,QAAQ,IAAI,WAAM;AAE9B,SAAO;AAAA,IACL;AAAA,MACE,IAAI;AAAA,MACJ,OAAO,OAAO;AAAA,MACd,UAAU,OAAO,SAAS,WAAW;AAAA,MACrC,UAAU,MAAM,OAAO,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI;AAAA,IAC5D;AAAA,IACA,GAAI,CAAC,GAAG,GAAG,CAAC,EAAY,IAAI,CAAC,WAAW;AAAA,MACtC,IAAI,UAAU,KAAK;AAAA,MACnB,OAAO,OAAO,UAAU,KAAK,EAAwB;AAAA,MACrD,UAAU,GAAG,GAAG,QAAQ,KAAK;AAAA,MAC7B,UAAU,OAAO,SAAS,WAAW,EAAE,MAAM,CAAC;AAAA,MAC9C,UAAU,MAAM,OAAO,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,IAAI;AAAA,IACtE,EAAE;AAAA,IACF;AAAA,MACE,IAAI;AAAA,MACJ,OAAO,OAAO;AAAA,MACd,UAAU,GAAG,GAAG;AAAA,MAChB,UAAU,OAAO,SAAS,aAAa;AAAA,MACvC,UAAU,MAAM,OAAO,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,IAAI;AAAA,IACjE;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,OAAO,OAAO;AAAA,MACd,UAAU,GAAG,GAAG;AAAA,MAChB,UAAU,OAAO,SAAS,YAAY;AAAA,MACtC,UAAU,MAAM,OAAO,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI;AAAA,IAChE;AAAA,EACF;AACF;AAEA,SAAS,UAAmB;AAC1B,MAAI,OAAO,cAAc,YAAa,QAAO;AAC7C,SAAO,uBAAuB,KAAK,UAAU,YAAY,UAAU,SAAS;AAC9E;;;ACWM,SAGI,OAAAE,MAHJ,QAAAC,aAAA;AAnDC,IAAM,oBAAgC;AAAA,EAC3C,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,OAAO;AACT;AAcO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA,mBAAmB;AAAA,EACnB,aAAa;AAAA,EACb;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AACF,GAAsB;AACpB,QAAM,EAAE,YAAY,IAAI,YAAY;AACpC,QAAM,YAAY,kBAAkB;AAEpC,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,OAAO,sBAAsB;AAAA,QAC7B,aAAa;AAAA,MACf,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,MAGX,OAAO,EAAE,mBAAmB,GAAG,WAAW,KAAK;AAAA,MAE/C;AAAA,wBAAAA,MAAC,YAAO,WAAU,oBACf;AAAA;AAAA,UACA,aACC,gBAAAD,KAAC,WAAQ,OAAO,WAAW,QAAQ,YAAY;AAAA,UAEhD,UAAU,gBAAAA,KAAC,SAAI,WAAU,uBAAuB,kBAAO;AAAA,UACvD,WAAW,gBAAAA,KAAC,SAAI,WAAU,wBAAwB,mBAAQ;AAAA,WAC7D;AAAA,QAEC,UAAU,gBAAAA,KAAC,SAAI,WAAU,uBAAuB,kBAAO;AAAA,QAExD,gBAAAA,KAAC,SAAI,WAAU,uBACb,0BAAAC,MAAC,SAAI,WAAU,uBACZ;AAAA,uBACC,gBAAAD;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,OAAO,SAAS;AAAA,cAChB,UAAU,CAAC,UAAU,cAAc,MAAM,OAAO,KAAK;AAAA,cACrD,aAAa;AAAA,cACb,cAAY;AAAA;AAAA,UACd;AAAA,UAED,SAAS,gBAAAA,KAAC,SAAI,WAAU,sBAAsB,iBAAM;AAAA,UACpD;AAAA,UACA,UAAU,gBAAAA,KAAC,SAAI,WAAU,uBAAuB,kBAAO;AAAA,WAC1D,GACF;AAAA;AAAA;AAAA,EACF;AAEJ;AAOA,SAAS,QAAQ,EAAE,OAAO,OAAO,GAA6C;AAC5E,MAAI,UAAU,OAAQ,QAAO,gBAAAA,KAAC,UAAK,WAAU,qBAAoB;AAEjE,QAAM,QACJ,UAAU,WAAW,OAAO,SAAS,UAAU,UAAU,OAAO,QAAQ,OAAO;AAEjF,SACE,gBAAAC,MAAC,UAAK,WAAU,qBAAoB,MAAK,UAAS,aAAU,UAC1D;AAAA,oBAAAD,KAAC,UAAK,WAAW,sCAAsC,KAAK,IAAI,eAAW,MAAC;AAAA,IAC5E,gBAAAA,KAAC,UAAK,WAAU,mBAAmB,iBAAM;AAAA,KAC3C;AAEJ;;;AC1GO,IAAM,qBAAkC;AAAA,EAC7C,UAAU;AAAA,EACV,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,gBAAgB;AAClB;AAkCO,SAAS,kBAAkB;AAAA,EAChC,SAAS;AAAA,EACT,eAAe,CAAC;AAAA,EAChB,QAAQ,CAAC;AACX,IAA8B,CAAC,GAAgB;AAC7C,QAAM,OAAO,CACX,IACA,UACA,SACe;AAAA,IACf;AAAA,IACA,OAAO,OAAO,EAAE;AAAA,IAChB,aAAa,aAAa,EAAE;AAAA,IAC5B;AAAA,IACA,MAAM,MAAM,EAAE;AAAA,IACd,SAAS,CAAC,EAAE,QAAQ,MAAM,MAAM;AAC9B,aAAO,MAAM,EAAE,MAAM,EAAE,YAAY,KAAK,EAAE,IAAI;AAC9C,UAAI,MAAM;AAAA,IACZ;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,MAAK;AAAA,MAAY;AAAA,MAAK,CAAC,WACrB,OAAO,MAAM,EAAE,MAAM,EAAE,QAAQ,WAAW,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI;AAAA,IAC9D;AAAA,IACA;AAAA,MAAK;AAAA,MAAY;AAAA,MAAM,CAAC,WACtB,OAAO,MAAM,EAAE,MAAM,EAAE,QAAQ,WAAW,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI;AAAA,IAC9D;AAAA,IACA;AAAA,MAAK;AAAA,MAAc;AAAA,MAAK,CAAC,WACvB,OAAO,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI;AAAA,IAChD;AAAA,IACA;AAAA,MAAK;AAAA,MAAe;AAAA,MAAM,CAAC,WACzB,OAAO,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,IAAI;AAAA,IACjD;AAAA,IACA;AAAA,MAAK;AAAA,MAAc;AAAA,MAAK,CAAC,WACvB,OAAO,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI;AAAA,IAChD;AAAA,IACA;AAAA,MAAK;AAAA,MAAa;AAAA,MAAO,CAAC,WACxB,OAAO,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,IAAI;AAAA,IAC/C;AAAA,IACA;AAAA,MAAK;AAAA,MAAkB;AAAA,MAAO,CAAC,WAC7B,OAAO,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,IAAI;AAAA,IACjD;AAAA,EACF;AACF;AAGO,SAAS,iBAAiB,OAAoB,OAA4B;AAC/E,QAAM,IAAI,MAAM,KAAK,EAAE,YAAY;AACnC,MAAI,MAAM,GAAI,QAAO;AACrB,SAAO,MAAM,OAAO,CAAC,SAAS,KAAK,MAAM,YAAY,EAAE,SAAS,CAAC,CAAC;AACpE;;;AC9HA;AAAA,EACE;AAAA,EACA;AAAA,EACA,aAAAE;AAAA,EACA;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,OACK;AAmEM,gBAAAC,MAgCD,QAAAC,aAhCC;AArDN,IAAM,YAAY;AAAA,EACvB,SAASC,WAAU,EAAE,OAAO,SAAS,aAAa,kBAAkB,GAAG,KAAK;AAC1E,UAAM,CAAC,eAAe,gBAAgB,IAAIH,UAAS,CAAC;AAKpD,UAAM,WAAWD,QAAO,KAAK;AAC7B,aAAS,UAAU;AACnB,UAAM,aAAaA,QAAO,OAAO;AACjC,eAAW,UAAU;AACrB,UAAM,cAAcA,QAAO,aAAa;AACxC,gBAAY,UAAU;AAEtB,UAAM,MAAM,MAAM,IAAI,CAAC,SAAS,KAAK,EAAE,EAAE,KAAK,GAAG;AACjD,IAAAD,WAAU,MAAM;AACd,uBAAiB,CAAC;AAClB,kBAAY,UAAU;AAAA,IACxB,GAAG,CAAC,GAAG,CAAC;AAER,UAAM,aAAa,YAAY,CAAC,UAAkB;AAChD,YAAM,OAAO,SAAS,QAAQ,KAAK;AACnC,UAAI,KAAM,YAAW,QAAQ,IAAI;AAAA,IACnC,GAAG,CAAC,CAAC;AAEL,UAAM,OAAO,YAAY,CAAC,UAAkB;AAC1C,YAAM,QAAQ,SAAS,QAAQ;AAC/B,UAAI,UAAU,EAAG;AACjB,YAAM,QAAQ,YAAY,UAAU,QAAQ,SAAS;AACrD,kBAAY,UAAU;AACtB,uBAAiB,IAAI;AAAA,IACvB,GAAG,CAAC,CAAC;AAEL,wBAAoB,KAAK,OAAO;AAAA,MAC9B,WAAW,CAAC,EAAE,MAAM,MAAM;AACxB,YAAI,MAAM,QAAQ,WAAW;AAC3B,eAAK,EAAE;AACP,iBAAO;AAAA,QACT;AACA,YAAI,MAAM,QAAQ,aAAa;AAC7B,eAAK,CAAC;AACN,iBAAO;AAAA,QACT;AACA,YAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,OAAO;AAChD,cAAI,SAAS,QAAQ,WAAW,EAAG,QAAO;AAC1C,qBAAW,YAAY,OAAO;AAC9B,iBAAO;AAAA,QACT;AACA,eAAO;AAAA,MACT;AAAA,IACF,EAAE;AAEF,QAAI,MAAM,WAAW,GAAG;AACtB,aAAO,gBAAAG,KAAC,SAAI,WAAU,gCAAgC,sBAAW;AAAA,IACnE;AAEA,WACE,gBAAAA,KAAC,SAAI,WAAU,cAAa,MAAK,WAC9B,gBAAM,IAAI,CAAC,MAAM,UAChB,gBAAAC;AAAA,MAAC;AAAA;AAAA,QAEC,MAAK;AAAA,QACL,MAAK;AAAA,QACL,iBAAe,UAAU;AAAA,QACzB,WACE,UAAU,gBACN,8CACA;AAAA,QAIN,aAAa,CAAC,UAAU;AACtB,gBAAM,eAAe;AACrB,qBAAW,KAAK;AAAA,QAClB;AAAA,QACA,cAAc,MAAM;AAClB,sBAAY,UAAU;AACtB,2BAAiB,KAAK;AAAA,QACxB;AAAA,QAEC;AAAA,eAAK,QACJ,gBAAAD,KAAC,UAAK,WAAU,oBAAmB,eAAW,MAC3C,eAAK,MACR;AAAA,UAEF,gBAAAC,MAAC,UAAK,WAAU,oBACd;AAAA,4BAAAD,KAAC,UAAK,WAAU,qBAAqB,eAAK,OAAM;AAAA,YAC/C,KAAK,eACJ,gBAAAA,KAAC,UAAK,WAAU,2BAA2B,eAAK,aAAY;AAAA,aAEhE;AAAA,UACC,KAAK,YACJ,gBAAAA,KAAC,SAAI,WAAU,wBAAwB,eAAK,UAAS;AAAA;AAAA;AAAA,MAhClD,KAAK;AAAA,IAkCZ,CACD,GACH;AAAA,EAEJ;AACF;;;ACxHA,SAAS,iBAAiB;AAC1B,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB;AAC1B,OAAO,gBAAkE;AAgBzE,IAAM,SAAS;AAER,IAAM,gBAAgB,UAAU,OAA6B;AAAA,EAClE,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,OAAO,MAAM,CAAC;AAAA,MACd,MAAM;AAAA,MACN,WAAW,IAAI,UAAU,iBAAiB;AAAA,IAC5C;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,UAAM,EAAE,OAAO,MAAM,YAAY,UAAU,IAAI,KAAK;AAEpD,UAAM,aAA2D;AAAA,MAC/D;AAAA,MACA;AAAA,MACA,aAAa;AAAA,MACb,OAAO,CAAC,EAAE,MAAM,MAAM,iBAAiB,MAAM,GAAG,KAAK;AAAA,MACrD,QAAQ,MAAM;AACZ,YAAI,YAAmE;AACvE,YAAI,YAAmC;AAEvC,cAAM,QAAQ,CAAC,eAAyD;AACtE,cAAI,CAAC,aAAa,CAAC,WAAY;AAC/B,gBAAM,OAAO,WAAW;AACxB,cAAI,CAAC,KAAM;AACX,oBAAU,MAAM,MAAM,GAAG,KAAK,SAAS,MAAM;AAC7C,oBAAU,MAAM,OAAO,GAAG,KAAK,IAAI;AAAA,QACrC;AAEA,cAAM,QAAQ,MAAM;AAClB,qBAAW,OAAO;AAClB,sBAAY;AACZ,qBAAW,QAAQ;AACnB,sBAAY;AAAA,QACd;AAEA,eAAO;AAAA,UACL,SAAS,CAAC,UAAU;AAClB,wBAAY,IAAI,cAAc,WAAW;AAAA,cACvC,OAAO,EAAE,GAAG,OAAO,WAAW;AAAA,cAC9B,QAAQ,MAAM;AAAA,YAChB,CAAC;AACD,wBAAY,SAAS,cAAc,KAAK;AACxC,sBAAU,YAAY;AACtB,sBAAU,YAAY,UAAU,OAAO;AACvC,qBAAS,KAAK,YAAY,SAAS;AACnC,kBAAM,MAAM,UAAU;AAAA,UACxB;AAAA,UACA,UAAU,CAAC,UAAU;AACnB,uBAAW,YAAY,EAAE,GAAG,OAAO,WAAW,CAAC;AAC/C,kBAAM,MAAM,UAAU;AAAA,UACxB;AAAA,UACA,WAAW,CAAC,UAAU;AACpB,gBAAI,MAAM,MAAM,QAAQ,UAAU;AAChC,oBAAM;AACN,qBAAO;AAAA,YACT;AACA,mBAAO,WAAW,KAAK,UAAU,KAAK,KAAK;AAAA,UAC7C;AAAA,UACA,QAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,WAAW;AAAA,QACT,QAAQ,KAAK;AAAA,QACb,GAAG;AAAA,QACH,SAAS,CAAC,EAAE,QAAQ,OAAO,MAAM,MAAM,MAAM,QAAQ,EAAE,QAAQ,MAAM,CAAC;AAAA,MACxE,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;;;AC/FD,SAAS,aAAAG,kBAAiB;AAC1B,SAAS,QAAQ,aAAAC,kBAAiB;AAClC,SAAS,YAAY,qBAAqB;AAkBnC,IAAM,uBAAuB,IAAIA,WAAyB,yBAAyB;AAWnF,IAAM,oBAAoBD,WAAU,OAA8C;AAAA,EACvF,MAAM;AAAA,EAEN,aAAa;AACX,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AAAA,EAEA,wBAAwB;AACtB,UAAM,UAAU,KAAK;AAErB,WAAO;AAAA,MACL,IAAI,OAAsB;AAAA,QACxB,KAAK;AAAA,QAEL,OAAO;AAAA,UACL,MAAM,MAAM,cAAc;AAAA,UAC1B,MAAM,IAAI,KAAK;AACb,kBAAM,MAAM,GAAG,QAAQ,oBAAoB;AAC3C,gBAAI,IAAK,QAAO,MAAM,GAAG,KAAK,KAAK,QAAQ,OAAO;AAGlD,mBAAO,IAAI,IAAI,GAAG,SAAS,GAAG,GAAG;AAAA,UACnC;AAAA,QACF;AAAA,QAEA,OAAO;AAAA,UACL,YAAY,OAAO;AACjB,mBAAO,qBAAqB,SAAS,KAAK,KAAK,cAAc;AAAA,UAC/D;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;AAED,SAAS,MACP,KACA,aACA,SACe;AACf,QAAM,cAA4B,CAAC;AAEnC,aAAW,KAAK,aAAa;AAC3B,QAAI,EAAE,OAAO,KAAK,EAAE,KAAK,IAAI,QAAQ,QAAQ,EAAE,QAAQ,EAAE,GAAI;AAE7D,gBAAY;AAAA,MACV,WAAW,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,uBAAuB,CAAC;AAAA,MACjE,WAAW,OAAO,EAAE,IAAI,MAAM,eAAe,GAAG,OAAO,GAAG;AAAA;AAAA;AAAA,QAGxD,MAAM;AAAA,QACN,iBAAiB;AAAA,MACnB,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO,cAAc,OAAO,KAAK,WAAW;AAC9C;AAEA,SAAS,eAAe,GAAqB,SAAgD;AAC3F,QAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,OAAK,YAAY;AACjB,OAAK,aAAa,mBAAmB,OAAO;AAE5C,QAAME,QAAO,SAAS,cAAc,MAAM;AAC1C,EAAAA,MAAK,YAAY;AACjB,EAAAA,MAAK,cAAc,EAAE;AACrB,OAAK,YAAYA,KAAI;AAErB,MAAI,SAAS;AACX,SAAK,YAAY,OAAO,UAAU,QAAQ,aAAa,UAAK,MAAM,QAAQ,SAAS,EAAE,EAAE,CAAC,CAAC;AACzF,SAAK,YAAY,OAAO,UAAU,QAAQ,aAAa,UAAK,MAAM,QAAQ,SAAS,EAAE,EAAE,CAAC,CAAC;AAAA,EAC3F;AACA,SAAO;AACT;AAEA,SAAS,OAAO,MAAc,OAAe,OAAe,SAAkC;AAC5F,QAAM,KAAK,SAAS,cAAc,QAAQ;AAC1C,KAAG,OAAO;AACV,KAAG,YAAY,oDAAoD,IAAI;AACvE,KAAG,aAAa,cAAc,KAAK;AACnC,KAAG,cAAc;AAEjB,KAAG,iBAAiB,aAAa,CAAC,MAAM,EAAE,eAAe,CAAC;AAC1D,KAAG,iBAAiB,SAAS,CAAC,MAAM;AAClC,MAAE,eAAe;AACjB,YAAQ;AAAA,EACV,CAAC;AACD,SAAO;AACT;;;AC5GA,IAAM,OAAO;AASN,SAAS,cAAcC,OAAc,MAAc,IAAuB;AAC/E,MAAI,QAAQ,GAAI,QAAO,EAAE,MAAM,GAAG;AAElC,MAAI,QAAQ;AACZ,SAAO,QAAQ,KAAK,KAAK,KAAKA,MAAK,QAAQ,CAAC,CAAC,KAAK,KAAK,KAAKA,MAAK,KAAK,CAAC,EAAG;AAE1E,MAAI,MAAM;AACV,SAAO,MAAMA,MAAK,UAAU,KAAK,KAAKA,MAAK,GAAG,CAAC,KAAK,KAAK,KAAKA,MAAK,MAAM,CAAC,CAAC,EAAG;AAE9E,SAAO,EAAE,MAAM,OAAO,IAAI,IAAI;AAChC;AAWO,SAAS,cAAc,QAAgB,OAA+B;AAC3E,MAAI,OAAO,KAAK,MAAM,MAAM,KAAK,EAAG,QAAO,CAAC;AAE5C,QAAM,OAAO,WAAW,MAAM;AAC9B,QAAM,OAAO,WAAW,KAAK;AAE7B,MAAI,KAAK,WAAW,KAAK,UAAU,KAAK,WAAW,GAAG;AACpD,WAAO,CAAC,EAAE,QAAQ,OAAO,KAAK,GAAG,OAAO,MAAM,KAAK,EAAE,CAAC;AAAA,EACxD;AAEA,QAAM,QAAwB,CAAC;AAC/B,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,QAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAG,OAAM,KAAK,EAAE,QAAQ,KAAK,CAAC,GAAG,OAAO,KAAK,CAAC,EAAE,CAAC;AAAA,EACzE;AACA,SAAO;AACT;AAEA,SAAS,WAAW,GAAqB;AACvC,SAAO,EACJ,MAAM,SAAS,EACf,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,CAAC,MAAM,MAAM,EAAE;AAC3B;;;AChDO,SAAS,cAAc,KAAsB;AAClD,SAAO,OAAO,IAAI,WAAW,CAAC,CAAC,EAC5B,OAAO,CAAC,MAAM,MAAM,EAAE,EACtB,KAAK,MAAM;AAChB;AAEA,SAAS,OAAO,OAA6B;AAC3C,SAAO,MAAM,IAAI,KAAK;AACxB;AAEA,SAAS,MAAM,MAAwB;AACrC,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK,WAAW;AACd,YAAM,QAAQ,OAAO,KAAK,OAAO,SAAS,CAAC;AAC3C,aAAO,GAAG,IAAI,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,OAAO,KAAK,OAAO,CAAC;AAAA,IAC/E;AAAA,IACA,KAAK;AACH,aAAO,OAAO,KAAK,WAAW,CAAC,CAAC,EAC7B,OAAO,CAAC,MAAM,MAAM,EAAE,EACtB;AAAA,QAAI,CAAC,MACJ,EACG,MAAM,IAAI,EACV,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,EACzB,KAAK,IAAI;AAAA,MACd,EACC,KAAK,OAAO;AAAA,IACjB,KAAK,aAAa;AAChB,YAAM,WAAW,OAAO,KAAK,OAAO,YAAY,EAAE;AAClD,aAAO,SAAS,QAAQ;AAAA,EAAK,OAAO,KAAK,OAAO,CAAC;AAAA;AAAA,IACnD;AAAA,IACA,KAAK;AACH,aAAO,UAAU,IAAI,EAAE,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,EAAE,KAAK,IAAI;AAAA,IAC7D,KAAK;AACH,aAAO,UAAU,IAAI,EAClB,IAAI,CAAC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,EACpC,KAAK,IAAI;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO,OAAO,KAAK,OAAO;AAAA,EAC9B;AACF;AAEA,SAAS,UAAU,MAA0B;AAC3C,UAAQ,KAAK,WAAW,CAAC,GAAG;AAAA,IAAI,CAAC,SAC/B,OAAO,KAAK,WAAW,CAAC,CAAC,EACtB,OAAO,CAAC,MAAM,MAAM,EAAE,EAEtB,KAAK,MAAM,EACX,MAAM,IAAI,EACV,KAAK,MAAM;AAAA,EAChB;AACF;AAEA,SAAS,OAAO,OAAuC;AACrD,UAAQ,SAAS,CAAC,GAAG,IAAI,IAAI,EAAE,KAAK,EAAE;AACxC;AAEA,SAAS,KAAK,MAAwB;AACpC,MAAI,KAAK,SAAS,YAAa,QAAO;AACtC,MAAI,OAAO,KAAK,SAAS,SAAU,QAAO,OAAO,KAAK,OAAO;AAE7D,MAAI,MAAM,KAAK;AAEf,aAAW,SAAU,KAAK,SAAgC,CAAC,GAAG,MAAM,EAAE,QAAQ,GAAG;AAC/E,YAAQ,KAAK,MAAM;AAAA,MACjB,KAAK;AACH,cAAM,KAAK,GAAG;AACd;AAAA,MACF,KAAK;AACH,cAAM,IAAI,GAAG;AACb;AAAA,MACF,KAAK;AACH,cAAM,KAAK,GAAG;AACd;AAAA,MACF,KAAK;AACH,cAAM,KAAK,GAAG;AACd;AAAA,MACF,KAAK;AACH,cAAM,IAAI,GAAG,KAAK,OAAO,KAAK,OAAO,QAAQ,EAAE,CAAC;AAChD;AAAA,IACJ;AAAA,EACF;AACA,SAAO;AACT;","names":["useEffect","useState","useState","useEffect","useEffect","useState","read","text","jsx","jsxs","useEffect","useRef","useState","jsx","jsxs","SlashMenu","Extension","PluginKey","text","text"]}
1
+ {"version":3,"sources":["../src/placement.ts","../src/SelectionToolbar.tsx","../src/useViewport.ts","../src/viewport.ts","../src/useSelectionToolbar.ts","../src/formats.ts","../src/EditorScreen.tsx","../src/slash.ts","../src/SlashMenu.tsx","../src/SlashCommands.ts","../src/InlineSuggestions.ts","../src/passage.ts","../src/markdown.ts"],"sourcesContent":["export interface SelectionRect {\n /** Horizontal centre of the selection. */\n x: number;\n top: number;\n bottom: number;\n}\n\nexport interface Size {\n width: number;\n height: number;\n}\n\nexport interface PlaceToolbarInput {\n selection: SelectionRect;\n toolbar: Size;\n viewport: Size;\n /** iOS renders its own callout above a selection, which covers a toolbar\n * placed there, so the caller asks for below on that platform. */\n preferBelow?: boolean;\n /** Height of the system's own selection callout — Cut/Copy/Paste — which is\n * drawn over the page and cannot be measured from it. Clearing it is what\n * keeps the two from overlapping. Zero where there is no such callout. */\n calloutHeight?: number;\n}\n\nexport interface ToolbarPlacement {\n left: number;\n top: number;\n below: boolean;\n}\n\nconst GAP = 8;\n\n/** iOS draws its Cut/Copy/Paste callout roughly this tall, below a selection\n * the page cannot see. Measured from the platform rather than derived: it is\n * a system surface, so nothing in the document reports its size. */\nexport const IOS_CALLOUT_HEIGHT = 44;\n\n/**\n * placeToolbar positions a floating toolbar against a selection.\n *\n * Kept apart from the component because it is the part that is wrong in subtle\n * ways — off-screen at the edges, hidden under a system callout — and the part\n * a test can pin down without a browser.\n */\nexport function placeToolbar({\n selection,\n toolbar,\n viewport,\n preferBelow = false,\n calloutHeight = 0,\n}: PlaceToolbarInput): ToolbarPlacement {\n const clearance = GAP + calloutHeight;\n const fitsAbove = selection.top - GAP - toolbar.height >= 0;\n const fitsBelow = selection.bottom + clearance + toolbar.height <= viewport.height;\n\n const below = preferBelow ? fitsBelow || !fitsAbove : !fitsAbove && fitsBelow;\n\n // Both branches stay within reach of the selection. Clamping to the viewport\n // edge instead would park the toolbar at the top of the screen whenever a\n // raised keyboard shrinks the viewport under the selection's own\n // coordinates — far from the words it acts on, which is worse than a few\n // pixels of overlap.\n const top = below\n ? Math.min(\n selection.bottom + clearance,\n // Never past the bottom edge: a selection low on the page would put\n // the toolbar off-screen once the callout's height is added.\n Math.max(selection.top, viewport.height - toolbar.height - GAP),\n )\n : Math.max(\n Math.min(GAP, selection.top),\n selection.top - GAP - toolbar.height,\n );\n\n const centred = selection.x - toolbar.width / 2;\n const left = Math.max(0, Math.min(centred, viewport.width - toolbar.width));\n\n return { left, top, below };\n}\n","import { useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { placeToolbar, IOS_CALLOUT_HEIGHT, type SelectionRect } from \"./placement\";\nimport { useViewport } from \"./useViewport\";\n\nexport interface ToolbarAction {\n id: string;\n label: string;\n icon?: React.ReactNode;\n onSelect: (selectedText: string) => void;\n}\n\nexport interface BlockFormat {\n id: string;\n label: string;\n shortcut?: string;\n isActive?: boolean;\n onSelect: () => void;\n}\n\nexport interface SelectionToolbarProps {\n selectedText: string;\n selection: SelectionRect;\n actions?: ToolbarAction[];\n /** Heading levels and list kinds, rendered behind a single trigger. */\n formats?: BlockFormat[];\n formatsLabel?: string;\n /** iOS draws its own callout above a selection; the host detects it. */\n preferBelow?: boolean;\n container?: HTMLElement | null;\n}\n\nconst FALLBACK = { width: 320, height: 40 };\n\n// Whether the width used to place the bar is the one it actually has. The\n// fallback is a guess, and placing on a guess puts the bar off-centre by\n// however far the guess is wrong — which is invisible while the bar happens to\n// be 320px wide and obvious as soon as a host adds another action to it.\ntype Measured = { width: number; height: number; real: boolean };\n\nexport function SelectionToolbar({\n selectedText,\n selection,\n actions = [],\n formats = [],\n formatsLabel = \"Format\",\n preferBelow = false,\n container,\n}: SelectionToolbarProps) {\n const ref = useRef<HTMLDivElement>(null);\n const [size, setSize] = useState<Measured>({ ...FALLBACK, real: false });\n const [formatsOpen, setFormatsOpen] = useState(false);\n const viewport = useViewport();\n\n // Measured after paint rather than assumed: the toolbar's width depends on\n // labels the host supplies, and guessing it puts the toolbar off-centre.\n //\n // Observed rather than recomputed from a dependency list: the width follows\n // the rendered labels, and a list naming the things that happen to change it\n // today misses the next one — a longer label, a font that loads late.\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n const measure = () => {\n const rect = el.getBoundingClientRect();\n if (rect.width > 0)\n setSize((prev) =>\n prev.real && prev.width === rect.width && prev.height === rect.height\n ? prev\n : { width: rect.width, height: rect.height, real: true },\n );\n };\n measure();\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(measure);\n observer.observe(el);\n return () => observer.disconnect();\n }, []);\n\n useEffect(() => {\n if (!formatsOpen) return;\n const close = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) setFormatsOpen(false);\n };\n document.addEventListener(\"mousedown\", close);\n return () => document.removeEventListener(\"mousedown\", close);\n }, [formatsOpen]);\n\n if (typeof document === \"undefined\") return null;\n\n const place = placeToolbar({\n selection,\n toolbar: size,\n viewport: { width: viewport.width, height: viewport.height },\n preferBelow,\n // preferBelow is set by the same platform check that puts the callout\n // there, so it is what says whether there is one to clear.\n calloutHeight: preferBelow ? IOS_CALLOUT_HEIGHT : 0,\n });\n\n return createPortal(\n <div\n ref={ref}\n role=\"toolbar\"\n className=\"lalt-toolbar\"\n // Custom properties rather than left/top directly: under the mobile\n // breakpoint the stylesheet docks the toolbar to the bottom of the\n // screen and ignores these, which inline left/top would override.\n //\n // The inset is what the docked bar sits on top of. `bottom: 0` means the\n // bottom of the layout viewport, which the on-screen keyboard covers\n // without shrinking — so the bar has to be lifted by the strip the\n // keyboard occupies to stay in sight.\n style={\n {\n \"--lalt-toolbar-left\": `${place.left}px`,\n \"--lalt-toolbar-top\": `${place.top}px`,\n \"--lalt-keyboard-inset\": `${viewport.bottomInset}px`,\n // Hidden for the one frame between mounting and being measured:\n // that first paint places the bar on the fallback width, and showing\n // it means showing the bar jump once the real width arrives.\n // Hidden rather than unmounted — it has to be in the document to\n // have a width at all.\n visibility: size.real ? undefined : \"hidden\",\n } as React.CSSProperties\n }\n >\n {actions.map((action) => (\n <button\n key={action.id}\n type=\"button\"\n className=\"lalt-toolbar__button\"\n // The label is what the narrow layout hides, so it has to stay\n // reachable to anyone not reading the icon.\n aria-label={action.icon ? action.label : undefined}\n data-has-icon={action.icon ? \"\" : undefined}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => action.onSelect(selectedText)}\n >\n {action.icon}\n <span className=\"lalt-toolbar__label\">{action.label}</span>\n </button>\n ))}\n\n {formats.length > 0 && (\n <div className=\"lalt-toolbar__formats\">\n <button\n type=\"button\"\n className=\"lalt-toolbar__button\"\n aria-haspopup=\"menu\"\n aria-expanded={formatsOpen}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => setFormatsOpen((open) => !open)}\n >\n {formatsLabel}\n </button>\n {formatsOpen && (\n <div role=\"menu\" className=\"lalt-toolbar__menu\">\n {formats.map((format) => (\n <button\n key={format.id}\n type=\"button\"\n role=\"menuitem\"\n className=\"lalt-toolbar__item\"\n aria-current={format.isActive}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n format.onSelect();\n setFormatsOpen(false);\n }}\n >\n <span>{format.label}</span>\n {format.shortcut && (\n <kbd className=\"lalt-toolbar__shortcut\">{format.shortcut}</kbd>\n )}\n </button>\n ))}\n </div>\n )}\n </div>\n )}\n </div>,\n container ?? document.body,\n );\n}\n","import { useEffect, useState } from \"react\";\n\nimport { readViewport, type ViewportMetrics } from \"./viewport\";\n\nconst SERVER: ViewportMetrics = { width: 0, height: 0, offsetTop: 0, bottomInset: 0 };\n\nfunction read(): ViewportMetrics {\n if (typeof window === \"undefined\") return SERVER;\n return readViewport({\n innerWidth: window.innerWidth,\n innerHeight: window.innerHeight,\n visual: window.visualViewport,\n });\n}\n\n/**\n * useViewport tracks the area a fixed element can occupy, keyboard included.\n *\n * The visual viewport's own events are the only ones a raised keyboard fires:\n * `window.resize` does not run on iOS when the keyboard opens, so a component\n * listening to it alone keeps the measurements it took before the keys\n * appeared.\n */\nexport function useViewport(): ViewportMetrics {\n const [metrics, setMetrics] = useState<ViewportMetrics>(read);\n\n useEffect(() => {\n const update = () => setMetrics(read());\n update();\n\n const visual = window.visualViewport;\n visual?.addEventListener(\"resize\", update);\n // iOS scrolls the visual viewport rather than the document to keep the\n // caret above the keyboard, and reports it here and nowhere else.\n visual?.addEventListener(\"scroll\", update);\n window.addEventListener(\"resize\", update);\n window.addEventListener(\"orientationchange\", update);\n\n return () => {\n visual?.removeEventListener(\"resize\", update);\n visual?.removeEventListener(\"scroll\", update);\n window.removeEventListener(\"resize\", update);\n window.removeEventListener(\"orientationchange\", update);\n };\n }, []);\n\n return metrics;\n}\n","export interface VisualViewportLike {\n width: number;\n height: number;\n offsetTop: number;\n}\n\nexport interface ViewportMetrics {\n width: number;\n height: number;\n offsetTop: number;\n /** How much of the layout viewport's bottom is covered, the on-screen\n * keyboard included. Zero on a desktop browser and wherever the API is\n * missing. */\n bottomInset: number;\n}\n\nexport interface ReadViewportInput {\n innerWidth: number;\n innerHeight: number;\n visual?: VisualViewportLike | null;\n}\n\n/**\n * readViewport reports the area a fixed element can actually occupy.\n *\n * The distinction the on-screen keyboard turns on: `innerHeight` measures the\n * layout viewport, which the keyboard covers without shrinking, so an element\n * pinned to its bottom edge ends up underneath the keys. Only `visualViewport`\n * narrows, and the difference is the inset a docked toolbar has to clear.\n */\nexport function readViewport({\n innerWidth,\n innerHeight,\n visual,\n}: ReadViewportInput): ViewportMetrics {\n if (!visual) {\n return { width: innerWidth, height: innerHeight, offsetTop: 0, bottomInset: 0 };\n }\n\n // offsetTop is what iOS scrolls the visual viewport by; without it the\n // covered strip reads as smaller than it is once the page is scrolled under\n // a raised keyboard. Never negative: a viewport reported as taller than the\n // layout one — Android during a rotation, a browser mid-resize — would\n // otherwise push the toolbar down off the screen.\n const covered = innerHeight - visual.height - visual.offsetTop;\n\n return {\n width: visual.width,\n height: visual.height,\n offsetTop: visual.offsetTop,\n bottomInset: Math.max(0, Math.round(covered)),\n };\n}\n","import { useEffect, useState } from \"react\";\nimport type { Editor } from \"@tiptap/react\";\n\nimport type { SelectionRect } from \"./placement\";\n\nexport interface SelectionInfo {\n text: string;\n rect: SelectionRect;\n}\n\n/**\n * useSelectionToolbar reports the current non-empty selection of an editor.\n *\n * Returns null whenever there is nothing to act on, so the caller renders the\n * toolbar only while it has a target.\n */\nexport function useSelectionToolbar(editor: Editor | null): SelectionInfo | null {\n const [selection, setSelection] = useState<SelectionInfo | null>(null);\n\n useEffect(() => {\n if (!editor) return;\n\n const read = () => {\n const { state } = editor;\n const { from, to, empty } = state.selection;\n if (empty || !editor.isFocused) {\n setSelection(null);\n return;\n }\n\n const text = state.doc.textBetween(from, to, \" \").trim();\n if (text === \"\") {\n setSelection(null);\n return;\n }\n\n const start = editor.view.coordsAtPos(from);\n const end = editor.view.coordsAtPos(to);\n setSelection({\n text,\n rect: {\n x: (start.left + end.right) / 2,\n top: Math.min(start.top, end.top),\n bottom: Math.max(start.bottom, end.bottom),\n },\n });\n };\n\n read();\n editor.on(\"selectionUpdate\", read);\n editor.on(\"blur\", read);\n editor.on(\"focus\", read);\n return () => {\n editor.off(\"selectionUpdate\", read);\n editor.off(\"blur\", read);\n editor.off(\"focus\", read);\n };\n }, [editor]);\n\n return selection;\n}\n","import type { Editor } from \"@tiptap/react\";\n\n// Type-only: these commands are contributed by StarterKit's extensions, and\n// without the reference TypeScript does not know they exist on a chain. No\n// runtime import, so the host stays in charge of which extensions it mounts —\n// blockFormats simply returns entries whose commands no-op if one is missing.\nimport type {} from \"@tiptap/starter-kit\";\n\nimport type { BlockFormat } from \"./SelectionToolbar\";\n\nexport interface FormatLabels {\n paragraph: string;\n heading1: string;\n heading2: string;\n heading3: string;\n orderedList: string;\n bulletList: string;\n}\n\nexport const defaultFormatLabels: FormatLabels = {\n paragraph: \"Texte\",\n heading1: \"Titre 1\",\n heading2: \"Titre 2\",\n heading3: \"Titre 3\",\n orderedList: \"Liste numérotée\",\n bulletList: \"Liste à puces\",\n};\n\n/**\n * blockFormats is the set a writer reaches for on a selected passage.\n *\n * Labels are passed in rather than translated here: the package carries no\n * i18n runtime, so the host names things in whatever language it speaks.\n */\nexport function blockFormats(\n editor: Editor | null,\n labels: FormatLabels = defaultFormatLabels,\n): BlockFormat[] {\n if (!editor) return [];\n\n // Shortcuts are the ones TipTap's own extensions bind, read from their\n // source rather than invented: heading binds Mod-Alt-<level>, the list\n // extensions bind Mod-Shift-7/8, and setParagraph binds nothing at all.\n // A label promising a shortcut that does nothing is worse than no label.\n const mod = isApple() ? \"⌘\" : \"Ctrl\";\n\n return [\n {\n id: \"paragraph\",\n label: labels.paragraph,\n isActive: editor.isActive(\"paragraph\"),\n onSelect: () => editor.chain().focus().setParagraph().run(),\n },\n ...([1, 2, 3] as const).map((level) => ({\n id: `heading${level}`,\n label: labels[`heading${level}` as keyof FormatLabels],\n shortcut: `${mod}+Alt+${level}`,\n isActive: editor.isActive(\"heading\", { level }),\n onSelect: () => editor.chain().focus().toggleHeading({ level }).run(),\n })),\n {\n id: \"orderedList\",\n label: labels.orderedList,\n shortcut: `${mod}+Shift+7`,\n isActive: editor.isActive(\"orderedList\"),\n onSelect: () => editor.chain().focus().toggleOrderedList().run(),\n },\n {\n id: \"bulletList\",\n label: labels.bulletList,\n shortcut: `${mod}+Shift+8`,\n isActive: editor.isActive(\"bulletList\"),\n onSelect: () => editor.chain().focus().toggleBulletList().run(),\n },\n ];\n}\n\nfunction isApple(): boolean {\n if (typeof navigator === \"undefined\") return false;\n return /Mac|iPad|iPhone|iPod/.test(navigator.platform || navigator.userAgent);\n}\n","import { useViewport } from \"./useViewport\";\n\nexport type SaveState = \"idle\" | \"saving\" | \"saved\" | \"dirty\";\n\nexport interface EditorScreenProps {\n /** The writing surface. Mounted by the host, which owns the TipTap instance\n * and whichever extensions it needs. */\n children: React.ReactNode;\n title?: string;\n onTitleChange?: (title: string) => void;\n titlePlaceholder?: string;\n titleLabel?: string;\n saveState?: SaveState;\n saveLabels?: SaveLabels;\n /** Drawn at the far left of the bar; a back control belongs here. */\n lead?: React.ReactNode;\n /** Drawn at the far right. Keep it to what a thumb can reach on a phone —\n * one primary control and an overflow, not a row of five. */\n actions?: React.ReactNode;\n /** Sits between the save dot and the actions: a character count, a badge. */\n status?: React.ReactNode;\n /** Between the title and the writing surface — a dictation control, a hint. */\n aside?: React.ReactNode;\n /** Below the writing surface, inside the scrolling column. */\n footer?: React.ReactNode;\n /** Above the writing surface and below the bar, pinned while the page\n * scrolls: tabs over variants of one document belong here. */\n subnav?: React.ReactNode;\n /** Fills its container instead of the viewport, for a host whose shell\n * already owns the page height. */\n fill?: boolean;\n className?: string;\n}\n\nexport interface SaveLabels {\n saving: string;\n saved: string;\n dirty: string;\n}\n\nexport const defaultSaveLabels: SaveLabels = {\n saving: \"Enregistrement en cours\",\n saved: \"Enregistré\",\n dirty: \"Modifications non enregistrées\",\n};\n\n/**\n * EditorScreen is the page a writing surface sits in: a command bar, a title,\n * one column of prose, and slots for whatever the host puts around it.\n *\n * It renders and measures; it never fetches, saves or routes. `saveState` is\n * displayed, not computed — the host owns the debounce and the request, so the\n * same screen serves an autosaving draft and a form with an explicit button.\n *\n * Commands sit at the top rather than in a bar at the bottom. Below the fold\n * they are gone the moment the document runs past one screen, and pinned to\n * the bottom they eat a sixth of what the on-screen keyboard leaves.\n */\nexport function EditorScreen({\n children,\n title,\n onTitleChange,\n titlePlaceholder = \"Titre\",\n titleLabel = \"Titre\",\n saveState,\n saveLabels = defaultSaveLabels,\n lead,\n actions,\n status,\n aside,\n footer,\n subnav,\n fill = false,\n className,\n}: EditorScreenProps) {\n const { bottomInset } = useViewport();\n const withTitle = onTitleChange !== undefined;\n\n return (\n <div\n className={[\n \"lalt-screen\",\n fill ? \"lalt-screen--fill\" : \"\",\n className ?? \"\",\n ]\n .filter(Boolean)\n .join(\" \")}\n // The keyboard covers the layout viewport without shrinking it, so the\n // column's last lines sit under the keys unless the strip is padded out.\n style={{ \"--lalt-keyboard\": `${bottomInset}px` } as React.CSSProperties}\n >\n <header className=\"lalt-screen__bar\">\n {lead}\n {saveState && (\n <SaveDot state={saveState} labels={saveLabels} />\n )}\n {status && <div className=\"lalt-screen__status\">{status}</div>}\n {actions && <div className=\"lalt-screen__actions\">{actions}</div>}\n </header>\n\n {subnav && <div className=\"lalt-screen__subnav\">{subnav}</div>}\n\n <div className=\"lalt-screen__scroll\">\n <div className=\"lalt-screen__column\">\n {withTitle && (\n <input\n className=\"lalt-screen__title\"\n value={title ?? \"\"}\n onChange={(event) => onTitleChange(event.target.value)}\n placeholder={titlePlaceholder}\n aria-label={titleLabel}\n />\n )}\n {aside && <div className=\"lalt-screen__aside\">{aside}</div>}\n {children}\n {footer && <div className=\"lalt-screen__footer\">{footer}</div>}\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * A dot rather than a word: the state is worth a glance, never a read, and a\n * label that appears and vanishes leaves the author wondering what it meant\n * once it is gone. Screen readers get the sentence instead.\n */\nfunction SaveDot({ state, labels }: { state: SaveState; labels: SaveLabels }) {\n if (state === \"idle\") return <span className=\"lalt-screen__save\" />;\n\n const label =\n state === \"saving\" ? labels.saving : state === \"dirty\" ? labels.dirty : labels.saved;\n\n return (\n <span className=\"lalt-screen__save\" role=\"status\" aria-live=\"polite\">\n <span className={`lalt-screen__dot lalt-screen__dot--${state}`} aria-hidden />\n <span className=\"lalt-screen__sr\">{label}</span>\n </span>\n );\n}\n","import type { Editor } from \"@tiptap/react\";\nimport type { Range } from \"@tiptap/core\";\n\n// Type-only, as in formats.ts: StarterKit contributes these commands to a\n// chain, and without the reference TypeScript does not know they exist. The\n// host stays in charge of which extensions it actually mounts.\nimport type {} from \"@tiptap/starter-kit\";\n\n/**\n * SlashItem is one entry of the `/` menu.\n *\n * icon is a node rather than a component type: the package carries no icon\n * library, so the host renders whichever one it already ships.\n */\nexport interface SlashItem {\n id: string;\n title: string;\n description?: string;\n shortcut?: string;\n icon?: React.ReactNode;\n command: (args: { editor: Editor; range: Range }) => void;\n}\n\nexport interface SlashLabels {\n heading1: string;\n heading2: string;\n bulletList: string;\n orderedList: string;\n blockquote: string;\n codeBlock: string;\n horizontalRule: string;\n}\n\nexport const defaultSlashLabels: SlashLabels = {\n heading1: \"Titre 1\",\n heading2: \"Titre 2\",\n bulletList: \"Liste à puces\",\n orderedList: \"Liste numérotée\",\n blockquote: \"Citation\",\n codeBlock: \"Bloc de code\",\n horizontalRule: \"Séparateur\",\n};\n\nexport interface SlashDescriptions {\n heading1?: string;\n heading2?: string;\n bulletList?: string;\n orderedList?: string;\n blockquote?: string;\n codeBlock?: string;\n horizontalRule?: string;\n}\n\nexport interface DefaultSlashItemsOptions {\n labels?: SlashLabels;\n descriptions?: SlashDescriptions;\n icons?: Partial<Record<DefaultSlashId, React.ReactNode>>;\n}\n\nexport type DefaultSlashId =\n | \"heading1\"\n | \"heading2\"\n | \"bulletList\"\n | \"orderedList\"\n | \"blockquote\"\n | \"codeBlock\"\n | \"horizontalRule\";\n\n/**\n * defaultSlashItems is the block set every writing surface shares.\n *\n * Returned as data rather than rendered behind flags: a host adds, removes or\n * reorders entries by composing the array, so a surface that needs one more\n * command does not push another boolean into this signature.\n */\nexport function defaultSlashItems({\n labels = defaultSlashLabels,\n descriptions = {},\n icons = {},\n}: DefaultSlashItemsOptions = {}): SlashItem[] {\n const item = (\n id: DefaultSlashId,\n shortcut: string,\n run: (editor: Editor) => void,\n ): SlashItem => ({\n id,\n title: labels[id],\n description: descriptions[id],\n shortcut,\n icon: icons[id],\n command: ({ editor, range }) => {\n editor.chain().focus().deleteRange(range).run();\n run(editor);\n },\n });\n\n return [\n item(\"heading1\", \"#\", (editor) =>\n editor.chain().focus().setNode(\"heading\", { level: 1 }).run(),\n ),\n item(\"heading2\", \"##\", (editor) =>\n editor.chain().focus().setNode(\"heading\", { level: 2 }).run(),\n ),\n item(\"bulletList\", \"-\", (editor) =>\n editor.chain().focus().toggleBulletList().run(),\n ),\n item(\"orderedList\", \"1.\", (editor) =>\n editor.chain().focus().toggleOrderedList().run(),\n ),\n item(\"blockquote\", \">\", (editor) =>\n editor.chain().focus().toggleBlockquote().run(),\n ),\n item(\"codeBlock\", \"```\", (editor) =>\n editor.chain().focus().toggleCodeBlock().run(),\n ),\n item(\"horizontalRule\", \"---\", (editor) =>\n editor.chain().focus().setHorizontalRule().run(),\n ),\n ];\n}\n\n/** filterSlashItems matches the query against titles the way the menu does. */\nexport function filterSlashItems(items: SlashItem[], query: string): SlashItem[] {\n const q = query.trim().toLowerCase();\n if (q === \"\") return items;\n return items.filter((item) => item.title.toLowerCase().includes(q));\n}\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\n\nimport type { SlashItem } from \"./slash\";\n\nexport interface SlashMenuHandle {\n onKeyDown: (props: { event: KeyboardEvent }) => boolean;\n}\n\nexport interface SlashMenuProps {\n items: SlashItem[];\n command: (item: SlashItem) => void;\n emptyLabel?: string;\n}\n\nexport const SlashMenu = forwardRef<SlashMenuHandle, SlashMenuProps>(\n function SlashMenu({ items, command, emptyLabel = \"Aucune commande\" }, ref) {\n const [selectedIndex, setSelectedIndex] = useState(0);\n\n // The suggestion plugin rebuilds these props on every keystroke, so the\n // imperative handle reads refs rather than a captured render closure —\n // otherwise Enter runs the command the menu showed one keystroke ago.\n const itemsRef = useRef(items);\n itemsRef.current = items;\n const commandRef = useRef(command);\n commandRef.current = command;\n const selectedRef = useRef(selectedIndex);\n selectedRef.current = selectedIndex;\n\n const ids = items.map((item) => item.id).join(\" \");\n useEffect(() => {\n setSelectedIndex(0);\n selectedRef.current = 0;\n }, [ids]);\n\n const selectItem = useCallback((index: number) => {\n const item = itemsRef.current[index];\n if (item) commandRef.current(item);\n }, []);\n\n const move = useCallback((delta: number) => {\n const count = itemsRef.current.length;\n if (count === 0) return;\n const next = (selectedRef.current + delta + count) % count;\n selectedRef.current = next;\n setSelectedIndex(next);\n }, []);\n\n useImperativeHandle(ref, () => ({\n onKeyDown: ({ event }) => {\n if (event.key === \"ArrowUp\") {\n move(-1);\n return true;\n }\n if (event.key === \"ArrowDown\") {\n move(1);\n return true;\n }\n if (event.key === \"Enter\" || event.key === \"Tab\") {\n if (itemsRef.current.length === 0) return false;\n selectItem(selectedRef.current);\n return true;\n }\n return false;\n },\n }));\n\n if (items.length === 0) {\n return <div className=\"lalt-slash lalt-slash--empty\">{emptyLabel}</div>;\n }\n\n return (\n <div className=\"lalt-slash\" role=\"listbox\">\n {items.map((item, index) => (\n <button\n key={item.id}\n type=\"button\"\n role=\"option\"\n aria-selected={index === selectedIndex}\n className={\n index === selectedIndex\n ? \"lalt-slash__item lalt-slash__item--active\"\n : \"lalt-slash__item\"\n }\n // Losing editor focus tears the suggestion plugin down before a\n // click lands, so the item would never run its command.\n onMouseDown={(event) => {\n event.preventDefault();\n selectItem(index);\n }}\n onMouseEnter={() => {\n selectedRef.current = index;\n setSelectedIndex(index);\n }}\n >\n {item.icon && (\n <span className=\"lalt-slash__icon\" aria-hidden>\n {item.icon}\n </span>\n )}\n <span className=\"lalt-slash__text\">\n <span className=\"lalt-slash__title\">{item.title}</span>\n {item.description && (\n <span className=\"lalt-slash__description\">{item.description}</span>\n )}\n </span>\n {item.shortcut && (\n <kbd className=\"lalt-slash__shortcut\">{item.shortcut}</kbd>\n )}\n </button>\n ))}\n </div>\n );\n },\n);\n","import { Extension } from \"@tiptap/core\";\nimport { ReactRenderer } from \"@tiptap/react\";\nimport { PluginKey } from \"@tiptap/pm/state\";\nimport Suggestion, { type SuggestionOptions, type SuggestionProps } from \"@tiptap/suggestion\";\n\nimport { SlashMenu, type SlashMenuHandle, type SlashMenuProps } from \"./SlashMenu\";\nimport { filterSlashItems, type SlashItem } from \"./slash\";\n\nexport interface SlashCommandsOptions {\n /** Read on every keystroke, so a host can swap the set without remounting\n * TipTap — rebuilding the extensions array wipes the text being typed. */\n items: () => SlashItem[];\n char: string;\n emptyLabel?: string;\n /** Distinguishes concurrently mounted editors: two surfaces sharing one key\n * would answer each other's suggestions. */\n pluginKey: PluginKey;\n}\n\nconst OFFSET = 6;\n\nexport const SlashCommands = Extension.create<SlashCommandsOptions>({\n name: \"laSlashCommands\",\n\n addOptions() {\n return {\n items: () => [],\n char: \"/\",\n pluginKey: new PluginKey(\"laSlashCommands\"),\n };\n },\n\n addProseMirrorPlugins() {\n const { items, char, emptyLabel, pluginKey } = this.options;\n\n const suggestion: Omit<SuggestionOptions<SlashItem>, \"editor\"> = {\n char,\n pluginKey,\n startOfLine: false,\n items: ({ query }) => filterSlashItems(items(), query),\n render: () => {\n let component: ReactRenderer<SlashMenuHandle, SlashMenuProps> | null = null;\n let container: HTMLDivElement | null = null;\n\n const place = (clientRect: SuggestionProps<SlashItem>[\"clientRect\"]) => {\n if (!container || !clientRect) return;\n const rect = clientRect();\n if (!rect) return;\n container.style.top = `${rect.bottom + OFFSET}px`;\n container.style.left = `${rect.left}px`;\n };\n\n const close = () => {\n container?.remove();\n container = null;\n component?.destroy();\n component = null;\n };\n\n return {\n onStart: (props) => {\n component = new ReactRenderer(SlashMenu, {\n props: { ...props, emptyLabel },\n editor: props.editor,\n });\n container = document.createElement(\"div\");\n container.className = \"lalt-slash-anchor\";\n container.appendChild(component.element);\n document.body.appendChild(container);\n place(props.clientRect);\n },\n onUpdate: (props) => {\n component?.updateProps({ ...props, emptyLabel });\n place(props.clientRect);\n },\n onKeyDown: (props) => {\n if (props.event.key === \"Escape\") {\n close();\n return true;\n }\n return component?.ref?.onKeyDown(props) ?? false;\n },\n onExit: close,\n };\n },\n };\n\n return [\n Suggestion({\n editor: this.editor,\n ...suggestion,\n command: ({ editor, range, props }) => props.command({ editor, range }),\n }),\n ];\n },\n});\n","import { Extension } from \"@tiptap/core\";\nimport { Plugin, PluginKey } from \"@tiptap/pm/state\";\nimport { Decoration, DecorationSet } from \"@tiptap/pm/view\";\nimport type { Node as ProseMirrorNode } from \"@tiptap/pm/model\";\n\nexport interface InlineSuggestion {\n id: string;\n /** Document positions of the passage this replaces. */\n from: number;\n to: number;\n after: string;\n}\n\nexport interface SuggestionActions {\n onAccept: (id: string) => void;\n onReject: (id: string) => void;\n acceptLabel: string;\n rejectLabel: string;\n}\n\nexport const inlineSuggestionsKey = new PluginKey<DecorationSet>(\"lalt-inline-suggestions\");\n\n/**\n * InlineSuggestions renders proposed edits where they belong: over the passage\n * they replace, with the new text right after it.\n *\n * Decorations, never document changes. The editor's content stays exactly what\n * the author wrote until they accept, so the draft is saveable mid-review and\n * a rejected suggestion leaves nothing behind — which a document-level insert\n * could not promise.\n */\nexport const InlineSuggestions = Extension.create<{ actions: SuggestionActions | null }>({\n name: \"lalt-inline-suggestions\",\n\n addOptions() {\n return { actions: null };\n },\n\n addProseMirrorPlugins() {\n const options = this.options;\n\n return [\n new Plugin<DecorationSet>({\n key: inlineSuggestionsKey,\n\n state: {\n init: () => DecorationSet.empty,\n apply(tr, old) {\n const set = tr.getMeta(inlineSuggestionsKey) as InlineSuggestion[] | undefined;\n if (set) return build(tr.doc, set, options.actions);\n // Positions follow the document as the author keeps typing around\n // a pending suggestion.\n return old.map(tr.mapping, tr.doc);\n },\n },\n\n props: {\n decorations(state) {\n return inlineSuggestionsKey.getState(state) ?? DecorationSet.empty;\n },\n },\n }),\n ];\n },\n});\n\nfunction build(\n doc: ProseMirrorNode,\n suggestions: InlineSuggestion[],\n actions: SuggestionActions | null,\n): DecorationSet {\n const decorations: Decoration[] = [];\n\n for (const s of suggestions) {\n if (s.from < 0 || s.to > doc.content.size || s.from >= s.to) continue;\n\n decorations.push(\n Decoration.inline(s.from, s.to, { class: \"lalt-suggestion__old\" }),\n Decoration.widget(s.to, () => renderProposal(s, actions), {\n // The widget belongs to the suggestion that precedes it, so typing at\n // the boundary does not push it around.\n side: 1,\n ignoreSelection: true,\n }),\n );\n }\n return DecorationSet.create(doc, decorations);\n}\n\nfunction renderProposal(s: InlineSuggestion, actions: SuggestionActions | null): HTMLElement {\n const wrap = document.createElement(\"span\");\n wrap.className = \"lalt-suggestion\";\n wrap.setAttribute(\"contenteditable\", \"false\");\n\n const text = document.createElement(\"span\");\n text.className = \"lalt-suggestion__new\";\n text.textContent = s.after;\n wrap.appendChild(text);\n\n if (actions) {\n wrap.appendChild(button(\"reject\", actions.rejectLabel, \"✕\", () => actions.onReject(s.id)));\n wrap.appendChild(button(\"accept\", actions.acceptLabel, \"✓\", () => actions.onAccept(s.id)));\n }\n return wrap;\n}\n\nfunction button(kind: string, label: string, glyph: string, onClick: () => void): HTMLElement {\n const el = document.createElement(\"button\");\n el.type = \"button\";\n el.className = `lalt-suggestion__action lalt-suggestion__action--${kind}`;\n el.setAttribute(\"aria-label\", label);\n el.textContent = glyph;\n // mousedown would move the selection into the widget before the click lands.\n el.addEventListener(\"mousedown\", (e) => e.preventDefault());\n el.addEventListener(\"click\", (e) => {\n e.preventDefault();\n onClick();\n });\n return el;\n}\n","export interface TextRange {\n from: number;\n to: number;\n}\n\nexport interface RevisionPart {\n before: string;\n after: string;\n}\n\n// Letters, digits, and the marks that belong inside a French word.\nconst WORD = /[\\p{L}\\p{N}'’-]/u;\n\n/**\n * expandToWords grows a range out to whole words.\n *\n * A selection dragged across a line lands mid-word more often than not, and a\n * model handed \"ns de production\" answers for a fragment — which then reads as\n * a truncated diff the author has to decode.\n */\nexport function expandToWords(text: string, from: number, to: number): TextRange {\n if (from >= to) return { from, to };\n\n let start = from;\n while (start > 0 && WORD.test(text[start - 1]) && WORD.test(text[start])) start--;\n\n let end = to;\n while (end < text.length && WORD.test(text[end]) && WORD.test(text[end - 1])) end++;\n\n return { from: start, to: end };\n}\n\n/**\n * splitRevision turns one model answer into the decisions it really contains.\n *\n * A three-paragraph rewrite accepted or rejected as a whole forces the author\n * to keep a weak paragraph for a strong one, so each paragraph becomes its own\n * decision — but only when both sides line up. When the model merged or split\n * paragraphs, position no longer maps them, and pairing by index would show a\n * diff that never existed.\n */\nexport function splitRevision(before: string, after: string): RevisionPart[] {\n if (before.trim() === after.trim()) return [];\n\n const olds = paragraphs(before);\n const news = paragraphs(after);\n\n if (olds.length !== news.length || olds.length === 0) {\n return [{ before: before.trim(), after: after.trim() }];\n }\n\n const parts: RevisionPart[] = [];\n for (let i = 0; i < olds.length; i++) {\n if (olds[i] !== news[i]) parts.push({ before: olds[i], after: news[i] });\n }\n return parts;\n}\n\nfunction paragraphs(s: string): string[] {\n return s\n .split(/\\n\\s*\\n/)\n .map((p) => p.trim())\n .filter((p) => p !== \"\");\n}\n","import type { RichDoc, RichNode } from \"./document\";\n\ninterface Mark {\n type?: string;\n attrs?: Record<string, unknown>;\n}\n\n/**\n * docToMarkdown renders a TipTap document as the markdown a blog accepts.\n *\n * Written here rather than taken from a library: the editor only ever produces\n * the handful of nodes StarterKit gives it, and a general markdown serializer\n * brings a dependency and its escaping rules for output a human is about to\n * paste into their own site.\n */\nexport function docToMarkdown(doc: RichDoc): string {\n return blocks(doc.content ?? [])\n .filter((b) => b !== \"\")\n .join(\"\\n\\n\");\n}\n\nfunction blocks(nodes: RichNode[]): string[] {\n return nodes.map(block);\n}\n\nfunction block(node: RichNode): string {\n switch (node.type) {\n case \"heading\": {\n const level = Number(node.attrs?.level ?? 2);\n return `${\"#\".repeat(Math.min(6, Math.max(1, level)))} ${inline(node.content)}`;\n }\n case \"blockquote\":\n return blocks(node.content ?? [])\n .filter((b) => b !== \"\")\n .map((b) =>\n b\n .split(\"\\n\")\n .map((line) => `> ${line}`)\n .join(\"\\n\"),\n )\n .join(\"\\n>\\n\");\n case \"codeBlock\": {\n const language = String(node.attrs?.language ?? \"\");\n return `\\`\\`\\`${language}\\n${inline(node.content)}\\n\\`\\`\\``;\n }\n case \"bulletList\":\n return listItems(node).map((item) => `- ${item}`).join(\"\\n\");\n case \"orderedList\":\n return listItems(node)\n .map((item, i) => `${i + 1}. ${item}`)\n .join(\"\\n\");\n case \"horizontalRule\":\n return \"---\";\n default:\n return inline(node.content);\n }\n}\n\nfunction listItems(list: RichNode): string[] {\n return (list.content ?? []).map((item) =>\n blocks(item.content ?? [])\n .filter((b) => b !== \"\")\n // Continuation lines are indented so the item stays one item.\n .join(\"\\n\\n\")\n .split(\"\\n\")\n .join(\"\\n \"),\n );\n}\n\nfunction inline(nodes: RichNode[] | undefined): string {\n return (nodes ?? []).map(text).join(\"\");\n}\n\nfunction text(node: RichNode): string {\n if (node.type === \"hardBreak\") return \"\\n\";\n if (typeof node.text !== \"string\") return inline(node.content);\n\n let out = node.text;\n // Innermost first, so bold inside a link wraps the text and not the URL.\n for (const mark of ((node.marks as Mark[] | undefined) ?? []).slice().reverse()) {\n switch (mark.type) {\n case \"bold\":\n out = `**${out}**`;\n break;\n case \"italic\":\n out = `*${out}*`;\n break;\n case \"strike\":\n out = `~~${out}~~`;\n break;\n case \"code\":\n out = `\\`${out}\\``;\n break;\n case \"link\":\n out = `[${out}](${String(mark.attrs?.href ?? \"\")})`;\n break;\n }\n }\n return out;\n}\n"],"mappings":"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","names":["useEffect","useState","useState","useEffect","useEffect","useState","read","text","jsx","jsxs","useEffect","useRef","useState","jsx","jsxs","SlashMenu","Extension","PluginKey","text","text"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lalternative/editor",
3
- "version": "0.8.6",
3
+ "version": "0.8.7",
4
4
  "description": "Shared TipTap editor for L'Alternative apps: rich text, format menu, selection toolbar and inline AI revisions",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",