@damiro/stylo 0.17.0 → 0.19.0
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/README.md +5 -3
- package/dist/{InPlaceView-CRb5AkfX.js → InPlaceView-D2GIV1A-.js} +692 -613
- package/dist/InPlaceView-D2GIV1A-.js.map +1 -0
- package/dist/{Preview-5lm2uUVj.js → Preview-BRA_R_Gp.js} +3 -3
- package/dist/{Preview-5lm2uUVj.js.map → Preview-BRA_R_Gp.js.map} +1 -1
- package/dist/Stylo.d.ts.map +1 -1
- package/dist/{embed-DKKeCE1Y.js → embed-DJAydn-v.js} +2 -2
- package/dist/{embed-DKKeCE1Y.js.map → embed-DJAydn-v.js.map} +1 -1
- package/dist/{embed-cache-CcBfVrc9.js → embed-cache-BU9Smn-h.js} +24 -17
- package/dist/embed-cache-BU9Smn-h.js.map +1 -0
- package/dist/inplace/InPlaceView.d.ts.map +1 -1
- package/dist/inplace/config.d.ts +8 -1
- package/dist/inplace/config.d.ts.map +1 -1
- package/dist/inplace/context-menu-actions.d.ts +4 -4
- package/dist/inplace/context-menu-actions.d.ts.map +1 -1
- package/dist/inplace/context-menu-types.d.ts +4 -0
- package/dist/inplace/context-menu-types.d.ts.map +1 -1
- package/dist/inplace/context-menu.d.ts.map +1 -1
- package/dist/inplace/extension.d.ts +7 -1
- package/dist/inplace/extension.d.ts.map +1 -1
- package/dist/inplace/host-cell.d.ts +10 -0
- package/dist/inplace/host-cell.d.ts.map +1 -0
- package/dist/inplace/host-items.d.ts +19 -0
- package/dist/inplace/host-items.d.ts.map +1 -0
- package/dist/inplace/menu-plugin.d.ts +9 -1
- package/dist/inplace/menu-plugin.d.ts.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/stylo.js +391 -166
- package/dist/stylo.js.map +1 -1
- package/dist/toolbar/OverflowBar.d.ts +19 -0
- package/dist/toolbar/OverflowBar.d.ts.map +1 -0
- package/dist/toolbar/OverflowMenu.d.ts +24 -0
- package/dist/toolbar/OverflowMenu.d.ts.map +1 -0
- package/dist/toolbar/Toolbar.d.ts +5 -1
- package/dist/toolbar/Toolbar.d.ts.map +1 -1
- package/dist/toolbar/overflow-fit.d.ts +27 -0
- package/dist/toolbar/overflow-fit.d.ts.map +1 -0
- package/dist/toolbar/toolbar-model.d.ts +31 -0
- package/dist/toolbar/toolbar-model.d.ts.map +1 -0
- package/dist/toolbar/use-toolbar-overflow.d.ts +17 -0
- package/dist/toolbar/use-toolbar-overflow.d.ts.map +1 -0
- package/dist/types/inplace.d.ts +48 -5
- package/dist/types/inplace.d.ts.map +1 -1
- package/dist/types/toolbar.d.ts +16 -0
- package/dist/types/toolbar.d.ts.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/{useCodeMirror-B07wRexo.js → useCodeMirror-Fn6qEfMx.js} +2 -2
- package/dist/{useCodeMirror-B07wRexo.js.map → useCodeMirror-Fn6qEfMx.js.map} +1 -1
- package/package.json +1 -1
- package/dist/InPlaceView-CRb5AkfX.js.map +0 -1
- package/dist/embed-cache-CcBfVrc9.js.map +0 -1
package/dist/stylo.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stylo.js","names":[],"sources":["../src/editor/SourceView.tsx","../src/inplace/lazyInPlace.ts","../src/render/lazyPreview.ts","../src/SplitView.tsx","../src/toolbar/floating-watchdog.ts","../src/toolbar/keyboard-inset.ts","../src/toolbar/Toolbar.tsx","../src/Stylo.tsx"],"sourcesContent":["import { useRef, useState, useSyncExternalStore } from \"react\"\nimport type { ReactNode } from \"react\"\nimport { createPortal } from \"react-dom\"\nimport { showPanel } from \"@codemirror/view\"\nimport type { Extension } from \"@codemirror/state\"\nimport type { EditorView } from \"@codemirror/view\"\nimport styles from \"../styles/stylo.module.css\"\nimport type { CodeLanguages, ResolveErrorInfo, TagSource, WikiLinkSource } from \"../types\"\nimport { CanvasHeaderHost } from \"./canvas-header-panel\"\nimport { useCodeMirror } from \"./useCodeMirror\"\n\nconst noopSubscribe = () => () => {}\nconst noopSnapshot = () => null\n\nexport interface SourceViewProps {\n value: string\n onChange: (next: string) => void\n readOnly?: boolean\n placeholder?: string\n /** Fenced-code grammars, forwarded to the Markdown language. Read once. */\n codeLanguages?: CodeLanguages\n /** `[[wikilink]]` autocomplete source. Read once. */\n wikiLinkSource?: WikiLinkSource\n /** `#tag` autocomplete source. Read once. */\n tagSource?: TagSource\n /** Notified when `wikiLinkSource` or `tagSource` rejects. */\n onResolveError?: (error: unknown, info: ResolveErrorInfo) => void\n /** Called with the doc string on `Mod-s`. */\n onSave?: (value: string) => void\n /** Called with the `EditorView` once created, and with `null` on teardown. */\n onViewChange?: (view: EditorView | null) => void\n /** Host CodeMirror extensions, reconfigured in place when the array changes. */\n hostExtensions?: readonly Extension[]\n /** Host content docked after the search panel, before the document. Read once. */\n canvasHeader?: (ctx: { view: EditorView | null }) => ReactNode\n}\n\n/** Raw CodeMirror 6 Markdown surface bound to the canonical string. */\nexport function SourceView({\n value,\n onChange,\n readOnly,\n placeholder,\n codeLanguages,\n wikiLinkSource,\n tagSource,\n onResolveError,\n onSave,\n onViewChange,\n canvasHeader,\n hostExtensions,\n}: SourceViewProps) {\n const [editorView, setEditorView] = useState<EditorView | null>(null)\n const onViewChangeRef = useRef(onViewChange)\n onViewChangeRef.current = onViewChange\n const handleViewChange = useRef((view: EditorView | null) => {\n setEditorView(view)\n onViewChangeRef.current?.(view)\n }).current\n\n // `canvasHeader` itself is read through a ref (a changed render prop needs\n // no editor rebuild); whether it was passed at all is read once, at mount,\n // to decide whether the panel extension exists at all.\n const canvasHeaderRef = useRef(canvasHeader)\n canvasHeaderRef.current = canvasHeader\n const [headerHost] = useState(() => (canvasHeader ? new CanvasHeaderHost() : null))\n const headerDom = useSyncExternalStore(\n headerHost?.subscribe ?? noopSubscribe,\n headerHost?.getSnapshot ?? noopSnapshot,\n )\n const [extensions] = useState(() => (headerHost ? [showPanel.of(headerHost.panel)] : []))\n\n const ref = useCodeMirror({\n value,\n onChange,\n readOnly,\n placeholder,\n codeLanguages,\n wikiLinkSource,\n tagSource,\n onResolveError,\n onSave,\n onViewChange: handleViewChange,\n hostExtensions,\n extensions,\n })\n return (\n <div className={styles.source} ref={ref}>\n {headerDom &&\n canvasHeaderRef.current &&\n createPortal(canvasHeaderRef.current({ view: editorView }), headerDom)}\n </div>\n )\n}\n","import { lazy } from \"react\"\n\n/**\n * The in-place canvas carries the decoration plugin (and, in later increments,\n * its KaTeX widgets). It loads only when `mode` resolves to `in-place`, so\n * `source` consumers never fetch it.\n */\nexport const LazyInPlaceView = lazy(async () => ({\n default: (await import(\"./InPlaceView\")).InPlaceView,\n}))\n","import { lazy } from \"react\"\n\n/**\n * Preview pulls in react-markdown + remark/rehype + KaTeX. It is loaded only\n * when a rendered mode (`preview`, `split`) is actually used, so `mode=\"source\"`\n * consumers never pay for it. Both callers share this one wrapper, so the render\n * pipeline stays a single lazy chunk.\n */\nexport const LazyPreview = lazy(async () => ({\n default: (await import(\"./Preview\")).Preview,\n}))\n","import { Suspense, useEffect, useRef, useState } from \"react\"\nimport type { ReactNode } from \"react\"\nimport type { Extension } from \"@codemirror/state\"\nimport type { EditorView } from \"@codemirror/view\"\nimport { SourceView } from \"./editor/SourceView\"\nimport { LazyPreview } from \"./render/lazyPreview\"\nimport styles from \"./styles/stylo.module.css\"\nimport type {\n CodeLanguages,\n EmbedSource,\n FrontmatterDisplay,\n ResolveErrorInfo,\n TagSource,\n TaskToggleInfo,\n WikiLinkSource,\n} from \"./types\"\n\nexport interface SplitViewProps {\n value: string\n onChange: (next: string) => void\n onWikiLinkClick?: (target: string) => void\n readOnly?: boolean\n placeholder?: string\n codeLanguages?: CodeLanguages\n wikiLinkSource?: WikiLinkSource\n tagSource?: TagSource\n embedSource?: EmbedSource\n onResolveError?: (error: unknown, info: ResolveErrorInfo) => void\n frontmatter?: FrontmatterDisplay\n softBreaks?: boolean\n onTaskToggle?: (info: TaskToggleInfo) => void\n /** Called with the doc string on `Mod-s`. */\n onSave?: (value: string) => void\n /** Forwarded the source pane's `EditorView` for the shared toolbar. */\n onViewChange?: (view: EditorView | null) => void\n /** Host CodeMirror extensions for the source pane. */\n hostExtensions?: readonly Extension[]\n /** Host content docked in the source pane, after its search panel. Read once. */\n canvasHeader?: (ctx: { view: EditorView | null }) => ReactNode\n}\n\n/**\n * Source and preview side by side. Both panes are a pure function of the same\n * string; the only link between them is a proportional scroll sync. Expects the\n * Stylo root to have a bounded height — otherwise both panes grow and the page\n * scrolls instead.\n */\nexport function SplitView({\n value,\n onChange,\n onWikiLinkClick,\n readOnly,\n placeholder,\n codeLanguages,\n wikiLinkSource,\n tagSource,\n embedSource,\n onResolveError,\n frontmatter,\n softBreaks,\n onTaskToggle,\n onSave,\n onViewChange,\n canvasHeader,\n hostExtensions,\n}: SplitViewProps) {\n const [view, setView] = useState<EditorView | null>(null)\n const previewPane = useRef<HTMLDivElement | null>(null)\n const syncing = useRef(false)\n\n const handleView = (next: EditorView | null) => {\n setView(next)\n onViewChange?.(next)\n }\n\n useEffect(() => {\n const source = view?.scrollDOM\n const preview = previewPane.current\n if (!source || !preview) return\n\n const sync = (from: HTMLElement, to: HTMLElement) => {\n if (syncing.current) return\n const range = from.scrollHeight - from.clientHeight\n if (range <= 0) return\n syncing.current = true\n to.scrollTop = (from.scrollTop / range) * (to.scrollHeight - to.clientHeight)\n requestAnimationFrame(() => {\n syncing.current = false\n })\n }\n\n const fromSource = () => sync(source, preview)\n const fromPreview = () => sync(preview, source)\n source.addEventListener(\"scroll\", fromSource, { passive: true })\n preview.addEventListener(\"scroll\", fromPreview, { passive: true })\n return () => {\n source.removeEventListener(\"scroll\", fromSource)\n preview.removeEventListener(\"scroll\", fromPreview)\n }\n }, [view])\n\n return (\n <div className={styles.split}>\n <div className={styles.splitPane}>\n <SourceView\n value={value}\n onChange={onChange}\n readOnly={readOnly}\n placeholder={placeholder}\n codeLanguages={codeLanguages}\n wikiLinkSource={wikiLinkSource}\n tagSource={tagSource}\n onResolveError={onResolveError}\n onSave={onSave}\n onViewChange={handleView}\n canvasHeader={canvasHeader}\n hostExtensions={hostExtensions}\n />\n </div>\n <div className={styles.splitPane} ref={previewPane}>\n <Suspense fallback={<div className={styles.preview} aria-busy=\"true\" />}>\n <LazyPreview\n value={value}\n onWikiLinkClick={onWikiLinkClick}\n embedSource={embedSource}\n onResolveError={onResolveError}\n frontmatter={frontmatter}\n codeLanguages={codeLanguages}\n softBreaks={softBreaks}\n onTaskToggle={onTaskToggle}\n />\n </Suspense>\n </div>\n </div>\n )\n}\n","import { useEffect, type RefObject } from \"react\"\n\n/**\n * Continuously re-asserts a `transform` on a `position: fixed` element,\n * alternating an imperceptible sub-pixel jitter every animation frame,\n * instead of trusting the browser to keep it correctly composited on its\n * own. Several rounds of on-device testing found iOS Safari losing track of\n * this exact kind of element — across its own address-bar animation, and in\n * at least one report tied to the keyboard opening — in ways a one-time\n * static fix (a plain `translateZ(0)`) did not reliably prevent. A value\n * that never changes gives the browser nothing to recompute; alternating it\n * every frame forces a fresh compositor pass regardless of what triggered\n * the drift, so it can't stay wrong for more than one frame (~16ms).\n *\n * Writes to the DOM directly rather than through React state, so this never\n * causes a re-render — the transform is the only thing touched.\n */\nexport function useFloatingWatchdog(ref: RefObject<HTMLElement | null>, enabled: boolean): void {\n useEffect(() => {\n if (!enabled) return\n let raf = 0\n let jitter = false\n const tick = () => {\n const el = ref.current\n if (el) {\n jitter = !jitter\n el.style.transform = jitter ? \"translate3d(0, 0.01px, 0)\" : \"translate3d(0, 0, 0)\"\n }\n raf = requestAnimationFrame(tick)\n }\n raf = requestAnimationFrame(tick)\n return () => cancelAnimationFrame(raf)\n }, [ref, enabled])\n}\n","import { useEffect, useState } from \"react\"\n\n/**\n * How far the on-screen keyboard eats into the window, tracked through the\n * `visualViewport` API. 0 when `enabled` is false or the browser has no\n * `visualViewport` (older Safari, SSR, most desktop browsers). A mobile\n * keyboard shrinks the *visual* viewport, not the layout viewport a plain\n * `position: fixed` bottom offset is computed against — this is what lets the\n * sticky toolbar ride up above the keyboard instead of hiding behind it.\n */\nexport function useKeyboardInset(enabled: boolean): number {\n const [inset, setInset] = useState(0)\n\n useEffect(() => {\n const vv = typeof window === \"undefined\" ? null : window.visualViewport\n if (!enabled || !vv) {\n setInset(0)\n return\n }\n const update = () => setInset(Math.max(0, window.innerHeight - vv.height - vv.offsetTop))\n update()\n vv.addEventListener(\"resize\", update)\n vv.addEventListener(\"scroll\", update)\n return () => {\n vv.removeEventListener(\"resize\", update)\n vv.removeEventListener(\"scroll\", update)\n }\n }, [enabled])\n\n return inset\n}\n","import { useEffect, useReducer, useRef, useState } from \"react\"\nimport type { ReactNode } from \"react\"\nimport type { EditorState } from \"@codemirror/state\"\nimport type { EditorView } from \"@codemirror/view\"\nimport styles from \"../styles/stylo.module.css\"\nimport type { ToolbarCommandId } from \"../types\"\nimport { activeTableCell } from \"./cell-inline\"\nimport { resolveForCell } from \"./command-helpers\"\nimport { BUILTIN_BY_ID } from \"./commands\"\nimport type { ToolbarItem } from \"./config\"\nimport { useFloatingWatchdog } from \"./floating-watchdog\"\nimport { DEFAULT_ICONS } from \"./icons\"\nimport { useKeyboardInset } from \"./keyboard-inset\"\n\nexport interface ToolbarProps {\n /** The surface the commands act on. `null` while a lazy view is mounting. */\n view: EditorView | null\n /** Ordered items to render; `\"|\"` is a separator. */\n items: ToolbarItem[]\n /** Per-id glyph overrides; any id left out keeps its built-in icon. */\n icons?: Partial<Record<ToolbarCommandId, ReactNode>>\n /** Render every button inert (e.g. a read-only surface). */\n disabled?: boolean\n /** Fix the bar to a window edge (normalised from `ToolbarConfig.sticky`; the\n * caller resolves `true` to `\"bottom\"`). */\n sticky?: \"top\" | \"bottom\" | false\n /** Fade the bar out while the editing surface is unfocused (`ToolbarConfig.stickyVisibility`). */\n stickyVisibility?: \"consistent\" | \"dynamic\"\n}\n\n/** A button to render, normalised from a built-in id or a custom item. */\ninterface Btn {\n key: string\n icon: ReactNode\n title: string\n run: (view: EditorView) => unknown\n isActive?: (state: EditorState) => boolean\n disabled?: (state: EditorState) => boolean\n}\n\n/**\n * Splits `items` into the runs a `\"|\"` already delimits, keeping each\n * separator as its own entry. A run renders as one flex child so wrapping\n * (on narrow hosts) breaks between groups, never in the middle of one — the\n * default bar's `undo, redo | h1, h2, h3 | ...` shape becomes the wrap unit\n * for free, no separate grouping config needed.\n */\nfunction groupItems(items: ToolbarItem[]): (Exclude<ToolbarItem, \"|\">[] | \"|\")[] {\n const groups: (Exclude<ToolbarItem, \"|\">[] | \"|\")[] = []\n let run: Exclude<ToolbarItem, \"|\">[] = []\n for (const item of items) {\n if (item !== \"|\") {\n run.push(item)\n continue\n }\n if (run.length) groups.push(run)\n run = []\n groups.push(\"|\")\n }\n if (run.length) groups.push(run)\n return groups\n}\n\n/** Resolve one non-separator item to a renderable button, or `null` to skip it. */\nfunction toButton(item: Exclude<ToolbarItem, \"|\">, icons: ToolbarProps[\"icons\"]): Btn | null {\n if (typeof item !== \"string\") {\n const { id, icon, title, run, isActive, disabled } = item\n return { key: id, icon, title, run, isActive, disabled }\n }\n const cmd = BUILTIN_BY_ID[item]\n if (!cmd) return null\n return {\n key: item,\n icon: icons?.[item] ?? DEFAULT_ICONS[item],\n title: cmd.title,\n run: cmd.run,\n isActive: cmd.isActive,\n disabled: cmd.disabled,\n }\n}\n\n/**\n * Formatting bar above the editing surface. It holds no document state — each\n * button runs a command against the live `EditorView`. Pressed states are read\n * back from the view whenever the selection, keys, or pointer move. Built-in\n * ids and consumer-supplied {@link ToolbarCustomItem}s render through the same\n * button path.\n */\nexport function Toolbar({ view, items, icons, disabled, sticky, stickyVisibility }: ToolbarProps) {\n const [, refresh] = useReducer((n: number) => n + 1, 0)\n const [focused, setFocused] = useState(false)\n // Only \"bottom\" needs keyboard tracking — nothing eats into the top of the\n // screen the way a keyboard eats the bottom.\n const keyboardInset = useKeyboardInset(sticky === \"bottom\")\n const barRef = useRef<HTMLDivElement>(null)\n useFloatingWatchdog(barRef, sticky === \"top\")\n\n useEffect(() => {\n if (!view) return\n const el = view.contentDOM\n setFocused(view.hasFocus)\n const onFocus = () => {\n setFocused(true)\n refresh()\n }\n const onBlur = () => {\n setFocused(false)\n refresh()\n }\n const events = [\"keyup\", \"mouseup\", \"input\"] as const\n for (const ev of events) el.addEventListener(ev, refresh)\n el.addEventListener(\"focus\", onFocus)\n el.addEventListener(\"blur\", onBlur)\n return () => {\n for (const ev of events) el.removeEventListener(ev, refresh)\n el.removeEventListener(\"focus\", onFocus)\n el.removeEventListener(\"blur\", onBlur)\n }\n }, [view])\n\n // \"dynamic\" fades the bar out while nothing is focused, so it doesn't sit\n // over the content while the caret is elsewhere (e.g. scrolling to read).\n const dynamicHidden = Boolean(sticky) && stickyVisibility === \"dynamic\" && !focused\n\n // `state.selection` never follows the caret into an editable table cell's\n // `contentEditable` DOM (see `extension.ts`'s mousedown handler), so a\n // built-in command's own `disabled` / `isActive` would otherwise read a\n // selection that may point anywhere else in the document while the user is\n // actually typing in a cell. `resolveForCell` below re-derives both from the\n // real DOM focus in that case instead.\n const inCell = view ? Boolean(activeTableCell(view)) : false\n\n const className = [\n styles.toolbar,\n sticky && styles.toolbarSticky,\n sticky === \"top\" && styles.toolbarStickyTop,\n sticky === \"bottom\" && styles.toolbarStickyBottom,\n dynamicHidden && styles.toolbarStickyHidden,\n ]\n .filter(Boolean)\n .join(\" \")\n // `transform`, not `bottom` — a `position: fixed` element repositioned via\n // `bottom` doesn't reliably repaint in step with the keyboard animation on\n // iOS Safari; `translateY` forces a compositor update on every\n // `visualViewport` event instead. The bar's resting position (keyboard\n // closed) is `bottom: 0` in CSS; this only nudges it up. `\"top\"` needs no\n // offset at all.\n const stickyStyle =\n sticky === \"bottom\" ? { transform: `translateY(-${keyboardInset}px)` } : undefined\n\n return (\n <div\n ref={barRef}\n className={className}\n role=\"toolbar\"\n aria-label=\"Formatting\"\n aria-hidden={dynamicHidden || undefined}\n style={stickyStyle}\n >\n {groupItems(items).map((group, i) => {\n if (group === \"|\") {\n return <span key={`sep-${i}`} className={styles.toolbarSep} aria-hidden=\"true\" />\n }\n return (\n <div key={`group-${i}`} className={styles.toolbarGroup}>\n {group.map((item) => {\n const btn = toButton(item, icons)\n if (!btn) return null\n // Only a built-in command is cell-aware — a host's own\n // `ToolbarCustomItem` keeps reading `state` exactly as before;\n // it has no notion of a table cell to degrade into.\n const builtin = typeof item === \"string\" ? BUILTIN_BY_ID[item] : undefined\n const resolved = view\n ? builtin\n ? resolveForCell(view.state, builtin, inCell)\n : {\n disabled: Boolean(btn.disabled?.(view.state)),\n active: Boolean(btn.isActive?.(view.state)),\n }\n : { disabled: true, active: false }\n const off = disabled || !view || resolved.disabled\n const active = !off && resolved.active\n return (\n <button\n key={btn.key}\n type=\"button\"\n className={styles.toolbarButton}\n data-command={btn.key}\n title={btn.title}\n aria-label={btn.title}\n aria-pressed={btn.isActive ? active : undefined}\n data-active={active ? \"\" : undefined}\n disabled={off}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n if (!view) return\n btn.run(view)\n refresh()\n }}\n >\n {btn.icon}\n </button>\n )\n })}\n </div>\n )\n })}\n </div>\n )\n}\n","import { forwardRef, Suspense, useEffect, useImperativeHandle, useRef, useState } from \"react\"\nimport { EditorView } from \"@codemirror/view\"\nimport { SourceView } from \"./editor/SourceView\"\nimport { LazyInPlaceView } from \"./inplace/lazyInPlace\"\nimport { LazyPreview } from \"./render/lazyPreview\"\nimport { SplitView } from \"./SplitView\"\nimport styles from \"./styles/stylo.module.css\"\nimport { Toolbar } from \"./toolbar/Toolbar\"\nimport { resolveToolbarItems } from \"./toolbar/config\"\nimport { splitFrontmatter } from \"./frontmatter\"\nimport { invalidateEmbed } from \"./render/embed-cache\"\nimport \"./styles/tokens.css\"\nimport type { ResolveErrorInfo, StyloHandle, StyloProps } from \"./types\"\n\n/**\n * Plain-text-first Markdown editor. `value` is the canonical Markdown string;\n * every view is a pure function of it.\n *\n * The default `mode` is `in-place`, the live decoration canvas (ADR-002 §1,\n * ADR-004). Pass `mode=\"source\"` for the plain surface with no lazy render\n * chunk. `preview` and `split` are also available.\n *\n * A formatting `toolbar` sits above every editing surface (all modes but\n * `preview`); pass `toolbar={false}` to drop it or a `ToolbarConfig` to trim it.\n */\nexport const Stylo = forwardRef<StyloHandle, StyloProps>(function Stylo(\n {\n value,\n onChange,\n mode = \"in-place\",\n onSave,\n onFrontmatter,\n onWikiLinkClick,\n onLinkClick,\n readOnly,\n placeholder,\n className,\n inPlace,\n codeLanguages,\n wikiLinkSource,\n tagSource,\n embedSource,\n onResolveError,\n toolbar,\n icons,\n frontmatter,\n softBreaks,\n onTaskToggle,\n canvasHeader,\n extensions,\n },\n ref,\n) {\n const resolved = mode === \"preview\" || mode === \"split\" || mode === \"in-place\" ? mode : \"source\"\n\n const [view, setView] = useState<EditorView | null>(null)\n const toolbarItems = resolved === \"preview\" ? null : resolveToolbarItems(toolbar)\n const toolbarConfig = toolbar && typeof toolbar === \"object\" ? toolbar : undefined\n const toolbarRender = toolbarConfig?.render\n const stickyToolbar: \"top\" | \"bottom\" | false =\n toolbarConfig?.sticky === \"top\" ? \"top\" : toolbarConfig?.sticky ? \"bottom\" : false\n const stickyVisibility = toolbarConfig?.stickyVisibility\n\n // Report the raw frontmatter block on mount and whenever it changes. Stylo\n // does not parse it — the host passes `raw` to its own YAML parser.\n const onFrontmatterRef = useRef(onFrontmatter)\n onFrontmatterRef.current = onFrontmatter\n\n // Stable wrapper so a changed `onResolveError` needs no editor rebuild and\n // does not thrash the embed effect. Passed to every surface.\n const onResolveErrorRef = useRef(onResolveError)\n onResolveErrorRef.current = onResolveError\n const resolveError = useRef((error: unknown, info: ResolveErrorInfo) => {\n onResolveErrorRef.current?.(error, info)\n }).current\n\n // Read by `invalidateEmbed` below — kept current without needing the\n // imperative handle itself to be rebuilt when `embedSource` changes.\n const embedSourceRef = useRef(embedSource)\n embedSourceRef.current = embedSource\n const lastFrontmatter = useRef<string | null | undefined>(undefined)\n useEffect(() => {\n const raw = splitFrontmatter(value)?.frontmatter ?? null\n if (raw === lastFrontmatter.current) return\n lastFrontmatter.current = raw\n onFrontmatterRef.current?.(raw)\n }, [value])\n\n useImperativeHandle(\n ref,\n () => ({\n focus: () => view?.focus(),\n getView: () => view,\n insertAtCursor: (md) => {\n if (!view) return\n view.dispatch(view.state.replaceSelection(md))\n view.focus()\n },\n scrollToHeading: (text) => {\n if (!view) return false\n const target = text.trim().toLowerCase()\n const { doc } = view.state\n for (let n = 1; n <= doc.lines; n++) {\n const line = doc.line(n)\n const m = /^ {0,3}#{1,6}[ \\t]+(.*?)(?:[ \\t]+#+)?[ \\t]*$/.exec(line.text)\n if (!m || m[1] === undefined) continue\n if (m[1].trim().toLowerCase() !== target) continue\n view.dispatch({\n selection: { anchor: line.from },\n effects: EditorView.scrollIntoView(line.from, { y: \"start\" }),\n })\n view.focus()\n return true\n }\n return false\n },\n invalidateEmbed: (ref) => {\n const source = embedSourceRef.current\n if (source) invalidateEmbed(source, ref)\n },\n }),\n [view],\n )\n\n const rootClass = [\n styles.root,\n stickyToolbar === \"bottom\" && styles.stickyToolbarRootBottom,\n stickyToolbar === \"top\" && styles.stickyToolbarRootTop,\n \"stylo\",\n className,\n ]\n .filter(Boolean)\n .join(\" \")\n\n return (\n <div className={rootClass} data-stylo-mode={resolved}>\n {toolbarItems &&\n (() => {\n const bar = (\n <Toolbar\n view={view}\n items={toolbarItems}\n icons={icons}\n disabled={readOnly}\n sticky={stickyToolbar}\n stickyVisibility={stickyVisibility}\n />\n )\n return toolbarRender ? toolbarRender(bar, { view }) : bar\n })()}\n\n {resolved === \"source\" && (\n <SourceView\n value={value}\n onChange={onChange}\n readOnly={readOnly}\n placeholder={placeholder}\n codeLanguages={codeLanguages}\n wikiLinkSource={wikiLinkSource}\n tagSource={tagSource}\n onResolveError={resolveError}\n onSave={onSave}\n onViewChange={setView}\n canvasHeader={canvasHeader}\n hostExtensions={extensions}\n />\n )}\n\n {resolved === \"preview\" && (\n <Suspense fallback={<div className={styles.preview} aria-busy=\"true\" />}>\n <LazyPreview\n value={value}\n onWikiLinkClick={onWikiLinkClick}\n embedSource={embedSource}\n onResolveError={resolveError}\n frontmatter={frontmatter}\n codeLanguages={codeLanguages}\n softBreaks={softBreaks}\n onTaskToggle={onTaskToggle}\n />\n </Suspense>\n )}\n\n {resolved === \"split\" && (\n <SplitView\n value={value}\n onChange={onChange}\n onWikiLinkClick={onWikiLinkClick}\n readOnly={readOnly}\n placeholder={placeholder}\n codeLanguages={codeLanguages}\n wikiLinkSource={wikiLinkSource}\n tagSource={tagSource}\n embedSource={embedSource}\n onResolveError={resolveError}\n frontmatter={frontmatter}\n softBreaks={softBreaks}\n onTaskToggle={onTaskToggle}\n onSave={onSave}\n onViewChange={setView}\n canvasHeader={canvasHeader}\n hostExtensions={extensions}\n />\n )}\n\n {resolved === \"in-place\" && (\n <Suspense fallback={<div className={styles.inplace} aria-busy=\"true\" />}>\n <LazyInPlaceView\n value={value}\n onChange={onChange}\n readOnly={readOnly}\n placeholder={placeholder}\n onWikiLinkClick={onWikiLinkClick}\n onLinkClick={onLinkClick}\n inPlace={inPlace}\n codeLanguages={codeLanguages}\n wikiLinkSource={wikiLinkSource}\n tagSource={tagSource}\n embedSource={embedSource}\n onResolveError={resolveError}\n onSave={onSave}\n onViewChange={setView}\n canvasHeader={canvasHeader}\n hostExtensions={extensions}\n />\n </Suspense>\n )}\n </div>\n )\n})\n\nStylo.displayName = \"Stylo\"\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stylo.js","names":[],"sources":["../src/editor/SourceView.tsx","../src/inplace/lazyInPlace.ts","../src/render/lazyPreview.ts","../src/SplitView.tsx","../src/toolbar/floating-watchdog.ts","../src/toolbar/keyboard-inset.ts","../src/toolbar/OverflowMenu.tsx","../src/toolbar/overflow-fit.ts","../src/toolbar/toolbar-model.ts","../src/toolbar/use-toolbar-overflow.ts","../src/toolbar/OverflowBar.tsx","../src/toolbar/Toolbar.tsx","../src/Stylo.tsx"],"sourcesContent":["import { useRef, useState, useSyncExternalStore } from \"react\"\nimport type { ReactNode } from \"react\"\nimport { createPortal } from \"react-dom\"\nimport { showPanel } from \"@codemirror/view\"\nimport type { Extension } from \"@codemirror/state\"\nimport type { EditorView } from \"@codemirror/view\"\nimport styles from \"../styles/stylo.module.css\"\nimport type { CodeLanguages, ResolveErrorInfo, TagSource, WikiLinkSource } from \"../types\"\nimport { CanvasHeaderHost } from \"./canvas-header-panel\"\nimport { useCodeMirror } from \"./useCodeMirror\"\n\nconst noopSubscribe = () => () => {}\nconst noopSnapshot = () => null\n\nexport interface SourceViewProps {\n value: string\n onChange: (next: string) => void\n readOnly?: boolean\n placeholder?: string\n /** Fenced-code grammars, forwarded to the Markdown language. Read once. */\n codeLanguages?: CodeLanguages\n /** `[[wikilink]]` autocomplete source. Read once. */\n wikiLinkSource?: WikiLinkSource\n /** `#tag` autocomplete source. Read once. */\n tagSource?: TagSource\n /** Notified when `wikiLinkSource` or `tagSource` rejects. */\n onResolveError?: (error: unknown, info: ResolveErrorInfo) => void\n /** Called with the doc string on `Mod-s`. */\n onSave?: (value: string) => void\n /** Called with the `EditorView` once created, and with `null` on teardown. */\n onViewChange?: (view: EditorView | null) => void\n /** Host CodeMirror extensions, reconfigured in place when the array changes. */\n hostExtensions?: readonly Extension[]\n /** Host content docked after the search panel, before the document. Read once. */\n canvasHeader?: (ctx: { view: EditorView | null }) => ReactNode\n}\n\n/** Raw CodeMirror 6 Markdown surface bound to the canonical string. */\nexport function SourceView({\n value,\n onChange,\n readOnly,\n placeholder,\n codeLanguages,\n wikiLinkSource,\n tagSource,\n onResolveError,\n onSave,\n onViewChange,\n canvasHeader,\n hostExtensions,\n}: SourceViewProps) {\n const [editorView, setEditorView] = useState<EditorView | null>(null)\n const onViewChangeRef = useRef(onViewChange)\n onViewChangeRef.current = onViewChange\n const handleViewChange = useRef((view: EditorView | null) => {\n setEditorView(view)\n onViewChangeRef.current?.(view)\n }).current\n\n // `canvasHeader` itself is read through a ref (a changed render prop needs\n // no editor rebuild); whether it was passed at all is read once, at mount,\n // to decide whether the panel extension exists at all.\n const canvasHeaderRef = useRef(canvasHeader)\n canvasHeaderRef.current = canvasHeader\n const [headerHost] = useState(() => (canvasHeader ? new CanvasHeaderHost() : null))\n const headerDom = useSyncExternalStore(\n headerHost?.subscribe ?? noopSubscribe,\n headerHost?.getSnapshot ?? noopSnapshot,\n )\n const [extensions] = useState(() => (headerHost ? [showPanel.of(headerHost.panel)] : []))\n\n const ref = useCodeMirror({\n value,\n onChange,\n readOnly,\n placeholder,\n codeLanguages,\n wikiLinkSource,\n tagSource,\n onResolveError,\n onSave,\n onViewChange: handleViewChange,\n hostExtensions,\n extensions,\n })\n return (\n <div className={styles.source} ref={ref}>\n {headerDom &&\n canvasHeaderRef.current &&\n createPortal(canvasHeaderRef.current({ view: editorView }), headerDom)}\n </div>\n )\n}\n","import { lazy } from \"react\"\n\n/**\n * The in-place canvas carries the decoration plugin (and, in later increments,\n * its KaTeX widgets). It loads only when `mode` resolves to `in-place`, so\n * `source` consumers never fetch it.\n */\nexport const LazyInPlaceView = lazy(async () => ({\n default: (await import(\"./InPlaceView\")).InPlaceView,\n}))\n","import { lazy } from \"react\"\n\n/**\n * Preview pulls in react-markdown + remark/rehype + KaTeX. It is loaded only\n * when a rendered mode (`preview`, `split`) is actually used, so `mode=\"source\"`\n * consumers never pay for it. Both callers share this one wrapper, so the render\n * pipeline stays a single lazy chunk.\n */\nexport const LazyPreview = lazy(async () => ({\n default: (await import(\"./Preview\")).Preview,\n}))\n","import { Suspense, useEffect, useRef, useState } from \"react\"\nimport type { ReactNode } from \"react\"\nimport type { Extension } from \"@codemirror/state\"\nimport type { EditorView } from \"@codemirror/view\"\nimport { SourceView } from \"./editor/SourceView\"\nimport { LazyPreview } from \"./render/lazyPreview\"\nimport styles from \"./styles/stylo.module.css\"\nimport type {\n CodeLanguages,\n EmbedSource,\n FrontmatterDisplay,\n ResolveErrorInfo,\n TagSource,\n TaskToggleInfo,\n WikiLinkSource,\n} from \"./types\"\n\nexport interface SplitViewProps {\n value: string\n onChange: (next: string) => void\n onWikiLinkClick?: (target: string) => void\n readOnly?: boolean\n placeholder?: string\n codeLanguages?: CodeLanguages\n wikiLinkSource?: WikiLinkSource\n tagSource?: TagSource\n embedSource?: EmbedSource\n onResolveError?: (error: unknown, info: ResolveErrorInfo) => void\n frontmatter?: FrontmatterDisplay\n softBreaks?: boolean\n onTaskToggle?: (info: TaskToggleInfo) => void\n /** Called with the doc string on `Mod-s`. */\n onSave?: (value: string) => void\n /** Forwarded the source pane's `EditorView` for the shared toolbar. */\n onViewChange?: (view: EditorView | null) => void\n /** Host CodeMirror extensions for the source pane. */\n hostExtensions?: readonly Extension[]\n /** Host content docked in the source pane, after its search panel. Read once. */\n canvasHeader?: (ctx: { view: EditorView | null }) => ReactNode\n}\n\n/**\n * Source and preview side by side. Both panes are a pure function of the same\n * string; the only link between them is a proportional scroll sync. Expects the\n * Stylo root to have a bounded height — otherwise both panes grow and the page\n * scrolls instead.\n */\nexport function SplitView({\n value,\n onChange,\n onWikiLinkClick,\n readOnly,\n placeholder,\n codeLanguages,\n wikiLinkSource,\n tagSource,\n embedSource,\n onResolveError,\n frontmatter,\n softBreaks,\n onTaskToggle,\n onSave,\n onViewChange,\n canvasHeader,\n hostExtensions,\n}: SplitViewProps) {\n const [view, setView] = useState<EditorView | null>(null)\n const previewPane = useRef<HTMLDivElement | null>(null)\n const syncing = useRef(false)\n\n const handleView = (next: EditorView | null) => {\n setView(next)\n onViewChange?.(next)\n }\n\n useEffect(() => {\n const source = view?.scrollDOM\n const preview = previewPane.current\n if (!source || !preview) return\n\n const sync = (from: HTMLElement, to: HTMLElement) => {\n if (syncing.current) return\n const range = from.scrollHeight - from.clientHeight\n if (range <= 0) return\n syncing.current = true\n to.scrollTop = (from.scrollTop / range) * (to.scrollHeight - to.clientHeight)\n requestAnimationFrame(() => {\n syncing.current = false\n })\n }\n\n const fromSource = () => sync(source, preview)\n const fromPreview = () => sync(preview, source)\n source.addEventListener(\"scroll\", fromSource, { passive: true })\n preview.addEventListener(\"scroll\", fromPreview, { passive: true })\n return () => {\n source.removeEventListener(\"scroll\", fromSource)\n preview.removeEventListener(\"scroll\", fromPreview)\n }\n }, [view])\n\n return (\n <div className={styles.split}>\n <div className={styles.splitPane}>\n <SourceView\n value={value}\n onChange={onChange}\n readOnly={readOnly}\n placeholder={placeholder}\n codeLanguages={codeLanguages}\n wikiLinkSource={wikiLinkSource}\n tagSource={tagSource}\n onResolveError={onResolveError}\n onSave={onSave}\n onViewChange={handleView}\n canvasHeader={canvasHeader}\n hostExtensions={hostExtensions}\n />\n </div>\n <div className={styles.splitPane} ref={previewPane}>\n <Suspense fallback={<div className={styles.preview} aria-busy=\"true\" />}>\n <LazyPreview\n value={value}\n onWikiLinkClick={onWikiLinkClick}\n embedSource={embedSource}\n onResolveError={onResolveError}\n frontmatter={frontmatter}\n codeLanguages={codeLanguages}\n softBreaks={softBreaks}\n onTaskToggle={onTaskToggle}\n />\n </Suspense>\n </div>\n </div>\n )\n}\n","import { useEffect, type RefObject } from \"react\"\n\n/**\n * Continuously re-asserts a `transform` on a `position: fixed` element,\n * alternating an imperceptible sub-pixel jitter every animation frame,\n * instead of trusting the browser to keep it correctly composited on its\n * own. Several rounds of on-device testing found iOS Safari losing track of\n * this exact kind of element — across its own address-bar animation, and in\n * at least one report tied to the keyboard opening — in ways a one-time\n * static fix (a plain `translateZ(0)`) did not reliably prevent. A value\n * that never changes gives the browser nothing to recompute; alternating it\n * every frame forces a fresh compositor pass regardless of what triggered\n * the drift, so it can't stay wrong for more than one frame (~16ms).\n *\n * Writes to the DOM directly rather than through React state, so this never\n * causes a re-render — the transform is the only thing touched.\n */\nexport function useFloatingWatchdog(ref: RefObject<HTMLElement | null>, enabled: boolean): void {\n useEffect(() => {\n if (!enabled) return\n let raf = 0\n let jitter = false\n const tick = () => {\n const el = ref.current\n if (el) {\n jitter = !jitter\n el.style.transform = jitter ? \"translate3d(0, 0.01px, 0)\" : \"translate3d(0, 0, 0)\"\n }\n raf = requestAnimationFrame(tick)\n }\n raf = requestAnimationFrame(tick)\n return () => cancelAnimationFrame(raf)\n }, [ref, enabled])\n}\n","import { useEffect, useState } from \"react\"\n\n/**\n * How far the on-screen keyboard eats into the window, tracked through the\n * `visualViewport` API. 0 when `enabled` is false or the browser has no\n * `visualViewport` (older Safari, SSR, most desktop browsers). A mobile\n * keyboard shrinks the *visual* viewport, not the layout viewport a plain\n * `position: fixed` bottom offset is computed against — this is what lets the\n * sticky toolbar ride up above the keyboard instead of hiding behind it.\n */\nexport function useKeyboardInset(enabled: boolean): number {\n const [inset, setInset] = useState(0)\n\n useEffect(() => {\n const vv = typeof window === \"undefined\" ? null : window.visualViewport\n if (!enabled || !vv) {\n setInset(0)\n return\n }\n const update = () => setInset(Math.max(0, window.innerHeight - vv.height - vv.offsetTop))\n update()\n vv.addEventListener(\"resize\", update)\n vv.addEventListener(\"scroll\", update)\n return () => {\n vv.removeEventListener(\"resize\", update)\n vv.removeEventListener(\"scroll\", update)\n }\n }, [enabled])\n\n return inset\n}\n","import { useEffect, useRef, useState } from \"react\"\nimport type { ReactNode } from \"react\"\nimport styles from \"../styles/stylo.module.css\"\n\nexport interface OverflowEntry {\n key: string\n title: string\n icon: ReactNode\n disabled: boolean\n /** Pressed state of a toggle command; `undefined` when it is not a toggle. */\n active?: boolean\n run: () => void\n}\n\nconst MORE_ICON = (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\">\n <circle cx=\"5\" cy=\"12\" r=\"1.8\" />\n <circle cx=\"12\" cy=\"12\" r=\"1.8\" />\n <circle cx=\"19\" cy=\"12\" r=\"1.8\" />\n </svg>\n)\n\n/** The glyph used for the \"more\" button when the host passes no `overflowIcon`. */\nexport const moreGlyph = (icon: ReactNode) => icon ?? MORE_ICON\n\n/**\n * The trailing \"more\" button and the menu it opens. A button keeps the icon,\n * label, disabled state and action it had in the row. Mouse presses keep the\n * editor focused, like the row's buttons; from the keyboard (Enter / Space on\n * the button) focus moves into the menu: arrows step, Escape closes.\n */\nexport function OverflowMenu({\n entries,\n icon,\n}: {\n /** Menu entries in order; `\"|\"` is a divider between groups. */\n entries: (OverflowEntry | \"|\")[]\n icon?: ReactNode\n}) {\n const [open, setOpen] = useState(false)\n const rootRef = useRef<HTMLDivElement>(null)\n const moreRef = useRef<HTMLButtonElement>(null)\n\n useEffect(() => {\n if (!open) return\n const onDown = (e: MouseEvent) => {\n if (!rootRef.current?.contains(e.target as Node)) setOpen(false)\n }\n // Document-level: after a mouse open, focus is still in the editor.\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== \"Escape\") return\n const inside = rootRef.current?.contains(document.activeElement)\n setOpen(false)\n if (inside) moreRef.current?.focus()\n }\n document.addEventListener(\"mousedown\", onDown)\n document.addEventListener(\"keydown\", onKey)\n return () => {\n document.removeEventListener(\"mousedown\", onDown)\n document.removeEventListener(\"keydown\", onKey)\n }\n }, [open])\n\n const items = () =>\n Array.from(\n rootRef.current?.querySelectorAll<HTMLButtonElement>(\"[role^=menuitem]:enabled\") ?? [],\n )\n\n const onKeyDown = (e: React.KeyboardEvent) => {\n if ((e.key === \"ArrowDown\" || e.key === \"ArrowUp\") && open) {\n e.preventDefault()\n const list = items()\n const at = list.indexOf(document.activeElement as HTMLButtonElement)\n const down = e.key === \"ArrowDown\"\n // Nothing focused yet: Down starts at the first entry, Up at the last.\n const next =\n at < 0 ? (down ? 0 : list.length - 1) : (at + (down ? 1 : -1) + list.length) % list.length\n list[next]?.focus()\n }\n }\n\n return (\n <div\n ref={rootRef}\n className={styles.toolbarMore}\n data-stylo-overflow=\"\"\n onKeyDown={onKeyDown}\n onBlur={(e) => {\n // Tabbing out of the open menu closes it.\n if (open && !rootRef.current?.contains(e.relatedTarget as Node | null)) setOpen(false)\n }}\n >\n <button\n ref={moreRef}\n type=\"button\"\n className={styles.toolbarButton}\n title=\"More\"\n aria-label=\"More\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n onMouseDown={(e) => e.preventDefault()}\n onClick={(e) => {\n const next = !open\n setOpen(next)\n // detail 0 = keyboard activation: move into the menu once it renders.\n if (next && e.detail === 0) requestAnimationFrame(() => items()[0]?.focus())\n }}\n >\n {moreGlyph(icon)}\n </button>\n {open && (\n <div className={styles.toolbarMenu} role=\"menu\" data-stylo-overflow-menu=\"\">\n {entries.map((entry, i) =>\n entry === \"|\" ? (\n <div key={`d-${i}`} className={styles.toolbarMenuDivider} role=\"separator\" />\n ) : (\n <button\n key={entry.key}\n type=\"button\"\n role={entry.active === undefined ? \"menuitem\" : \"menuitemcheckbox\"}\n aria-checked={entry.active}\n data-active={entry.active ? \"\" : undefined}\n className={styles.toolbarMenuItem}\n data-command={entry.key}\n disabled={entry.disabled}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n // Keyboard users keep their place: the focused entry is about to unmount.\n if (rootRef.current?.contains(document.activeElement)) moreRef.current?.focus()\n setOpen(false)\n entry.run()\n }}\n >\n {entry.icon}\n <span>{entry.title}</span>\n </button>\n ),\n )}\n </div>\n )}\n </div>\n )\n}\n","/** One slot of the flattened bar, as the overflow fit sees it. */\nexport interface FitEntry {\n sep: boolean\n pinned: boolean\n /** Measured width, margins included, gap excluded. */\n width: number\n}\n\n/** Drops separators that would lead, trail, or sit next to another. */\nexport function trimSeps<T extends { sep: boolean }>(list: T[]): T[] {\n const out: T[] = []\n for (const e of list) {\n if (e.sep && (out.length === 0 || out[out.length - 1]!.sep)) continue\n out.push(e)\n }\n while (out.length && out[out.length - 1]!.sep) out.pop()\n return out\n}\n\n/**\n * The slots the overflow menu lists: every folded one, plus each separator\n * with a folded slot on both sides, so the menu keeps the bar's groups.\n */\nexport function menuSlots<T extends { sep: boolean; folded: boolean }>(list: T[]): T[] {\n const firstAt = list.findIndex((e) => e.folded)\n const lastAt = list.map((e) => e.folded).lastIndexOf(true)\n return trimSeps(list.filter((e, i) => e.folded || (e.sep && i > firstAt && i < lastAt)))\n}\n\n/**\n * Which entries fold into the overflow menu so the rest fit in `available`\n * pixels. Folds from the end, unpinned buttons first; pinned ones only once\n * nothing else is left. `moreWidth` (and one `gap` before it) is reserved once\n * anything has folded; `gap` also separates the slots that stay.\n */\nexport function fitEntries(\n entries: FitEntry[],\n available: number,\n moreWidth: number,\n gap = 0,\n): boolean[] {\n const folded = entries.map(() => false)\n const fits = () => {\n const kept = trimSeps(entries.filter((_, i) => !folded[i]))\n const row = kept.reduce((sum, e) => sum + e.width, 0) + gap * Math.max(0, kept.length - 1)\n const more = folded.some(Boolean) ? moreWidth + (kept.length ? gap : 0) : 0\n return row + more <= available\n }\n const lastOpen = (pinned: boolean) => {\n for (let i = entries.length - 1; i >= 0; i--) {\n const e = entries[i]!\n if (!folded[i] && !e.sep && e.pinned === pinned) return i\n }\n return -1\n }\n while (!fits()) {\n const i = lastOpen(false) >= 0 ? lastOpen(false) : lastOpen(true)\n if (i < 0) break\n folded[i] = true\n }\n return folded\n}\n","import type { ReactNode } from \"react\"\nimport type { EditorState } from \"@codemirror/state\"\nimport type { EditorView } from \"@codemirror/view\"\nimport type { ToolbarCommandId } from \"../types\"\nimport { BUILTIN_BY_ID } from \"./commands\"\nimport type { ToolbarItem } from \"./config\"\nimport { DEFAULT_ICONS } from \"./icons\"\n\n/** A button to render, normalised from a built-in id or a custom item. */\nexport interface Btn {\n key: string\n icon: ReactNode\n title: string\n run: (view: EditorView) => unknown\n isActive?: (state: EditorState) => boolean\n disabled?: (state: EditorState) => boolean\n}\n\n/**\n * Splits `items` into the runs a `\"|\"` already delimits, keeping each\n * separator as its own entry. A run renders as one flex child so wrapping\n * (on narrow hosts) breaks between groups, never in the middle of one — the\n * default bar's `undo, redo | h1, h2, h3 | ...` shape becomes the wrap unit\n * for free, no separate grouping config needed.\n */\nexport function groupItems(items: ToolbarItem[]): (Exclude<ToolbarItem, \"|\">[] | \"|\")[] {\n const groups: (Exclude<ToolbarItem, \"|\">[] | \"|\")[] = []\n let run: Exclude<ToolbarItem, \"|\">[] = []\n for (const item of items) {\n if (item !== \"|\") {\n run.push(item)\n continue\n }\n if (run.length) groups.push(run)\n run = []\n groups.push(\"|\")\n }\n if (run.length) groups.push(run)\n return groups\n}\n\n/** Resolve one non-separator item to a renderable button, or `null` to skip it. */\nexport function toButton(\n item: Exclude<ToolbarItem, \"|\">,\n icons: Partial<Record<ToolbarCommandId, ReactNode>> | undefined,\n): Btn | null {\n if (typeof item !== \"string\") {\n const { id, icon, title, run, isActive, disabled } = item\n return { key: id, icon, title, run, isActive, disabled }\n }\n const cmd = BUILTIN_BY_ID[item]\n if (!cmd) return null\n return {\n key: item,\n icon: icons?.[item] ?? DEFAULT_ICONS[item],\n title: cmd.title,\n run: cmd.run,\n isActive: cmd.isActive,\n disabled: cmd.disabled,\n }\n}\n\n/** A slot's button plus its live disabled / pressed state. */\nexport interface Resolved {\n btn: Btn\n off: boolean\n active: boolean\n}\n","import { useCallback, useLayoutEffect, useState } from \"react\"\nimport type { RefObject } from \"react\"\nimport { fitEntries } from \"./overflow-fit\"\n\nconst px = (v: string) => parseFloat(v) || 0\n\n/** Border-box width plus horizontal margins (a separator's width is mostly margin). */\nfunction outerWidth(el: HTMLElement): number {\n const cs = getComputedStyle(el)\n return el.getBoundingClientRect().width + px(cs.marginLeft) + px(cs.marginRight)\n}\n\n/**\n * Decides which slots fold into the overflow menu. `measureRef` is a hidden\n * row inside the bar holding one copy of every slot (same order as `slots`)\n * followed by the \"more\" button; their rendered widths give the real fit, so\n * it follows fonts, custom glyphs and the sticky bar's larger buttons. Widths\n * come from layout rects, not offsets, so right-to-left pages measure the same.\n * Re-measured before paint when `slots` change, and whenever the bar or the\n * measuring row resizes (a host widening the panel, a glyph or font changing).\n * Returns one flag per slot.\n */\nexport function useToolbarOverflow(\n measureRef: RefObject<HTMLElement | null>,\n slots: { key: string; sep: boolean; pinned: boolean }[],\n): boolean[] {\n const [folded, setFolded] = useState<boolean[]>([])\n const sig = slots.map((s) => `${s.key}${s.sep ? \"|\" : \"\"}${s.pinned ? \"!\" : \"\"}`).join(\"\\0\")\n\n const measure = useCallback(() => {\n const row = measureRef.current\n const bar = row?.closest<HTMLElement>(\"[role=toolbar]\")\n if (!bar || !row) return\n const kids = Array.from(row.children) as HTMLElement[]\n const more = kids.pop()\n if (!more || kids.length !== slots.length) return\n const cs = getComputedStyle(bar)\n const available = bar.clientWidth - px(cs.paddingLeft) - px(cs.paddingRight)\n const next = fitEntries(\n slots.map((s, i) => ({ sep: s.sep, pinned: s.pinned, width: outerWidth(kids[i]!) })),\n available,\n outerWidth(more),\n px(getComputedStyle(row).columnGap),\n )\n setFolded((cur) =>\n cur.length === next.length && cur.every((f, i) => f === next[i]) ? cur : next,\n )\n // `sig` stands in for `slots`: a fresh array each render must not re-measure.\n }, [measureRef, sig])\n\n useLayoutEffect(() => {\n // The bar is found from the row: a child's layout effect runs before the\n // parent's `ref` is attached, so a bar ref would still be null here.\n const row = measureRef.current\n const bar = row?.closest<HTMLElement>(\"[role=toolbar]\")\n if (!bar || !row) return\n measure()\n if (typeof ResizeObserver === \"undefined\") return\n const ro = new ResizeObserver(measure)\n ro.observe(bar)\n ro.observe(row)\n return () => ro.disconnect()\n }, [measureRef, measure])\n\n return folded\n}\n","import { useRef } from \"react\"\nimport type { ReactNode } from \"react\"\nimport styles from \"../styles/stylo.module.css\"\nimport type { ToolbarCommandId } from \"../types\"\nimport type { ToolbarItem } from \"./config\"\nimport { OverflowMenu, moreGlyph } from \"./OverflowMenu\"\nimport type { OverflowEntry } from \"./OverflowMenu\"\nimport { menuSlots, trimSeps } from \"./overflow-fit\"\nimport { toButton } from \"./toolbar-model\"\nimport type { Btn, Resolved } from \"./toolbar-model\"\nimport { useToolbarOverflow } from \"./use-toolbar-overflow\"\n\nconst Sep = () => <span className={styles.toolbarSep} aria-hidden=\"true\" />\n\n/**\n * The bar's contents in `overflow: \"menu\"` mode. Slots lay out flat (no group\n * wrappers) so single buttons can fold; the ones that do not fit go to a\n * trailing menu. A hidden copy of every slot, rendered alongside, is what the\n * fit measures.\n */\nexport function OverflowBar({\n items,\n icons,\n overflowIcon,\n resolve,\n run,\n renderItem,\n}: {\n items: ToolbarItem[]\n icons?: Partial<Record<ToolbarCommandId, ReactNode>>\n overflowIcon?: ReactNode\n resolve: (item: Exclude<ToolbarItem, \"|\">) => Resolved | null\n run: (btn: Btn) => void\n renderItem: (item: Exclude<ToolbarItem, \"|\">) => ReactNode\n}) {\n const measureRef = useRef<HTMLDivElement>(null)\n // Unknown ids are dropped up front so the slots match the measured DOM.\n const flat = items.filter((it) => it === \"|\" || toButton(it, icons) !== null)\n const folded = useToolbarOverflow(\n measureRef,\n flat.map((it, i) => ({\n key: it === \"|\" ? `sep-${i}` : typeof it === \"string\" ? it : it.id,\n sep: it === \"|\",\n pinned: typeof it === \"object\" && Boolean(it.pinned),\n })),\n )\n const marked = flat.map((it, i) => ({ it, sep: it === \"|\", folded: Boolean(folded[i]) }))\n const row = trimSeps(marked.filter((s) => !s.folded))\n const menu = menuSlots(marked)\n\n const entries = menu.map((s): OverflowEntry | \"|\" => {\n const r = s.it === \"|\" ? null : resolve(s.it)\n if (!r) return \"|\"\n const { btn, off, active } = r\n return {\n key: btn.key,\n title: btn.title,\n icon: btn.icon,\n disabled: off,\n active: btn.isActive ? active : undefined,\n run: () => run(btn),\n }\n })\n\n return (\n <>\n {row.map((s, i) => (s.it === \"|\" ? <Sep key={`sep-${i}`} /> : renderItem(s.it)))}\n {menu.length > 0 && <OverflowMenu entries={entries} icon={overflowIcon} />}\n <div className={styles.toolbarMeasureClip} aria-hidden=\"true\">\n <div ref={measureRef} className={styles.toolbarMeasure}>\n {flat.map((it, i) =>\n it === \"|\" ? (\n <Sep key={i} />\n ) : (\n <button key={i} type=\"button\" tabIndex={-1} className={styles.toolbarButton}>\n {toButton(it, icons)!.icon}\n </button>\n ),\n )}\n <button type=\"button\" tabIndex={-1} className={styles.toolbarButton}>\n {moreGlyph(overflowIcon)}\n </button>\n </div>\n </div>\n </>\n )\n}\n","import { useEffect, useReducer, useRef, useState } from \"react\"\nimport type { ReactNode } from \"react\"\nimport type { EditorView } from \"@codemirror/view\"\nimport styles from \"../styles/stylo.module.css\"\nimport type { ToolbarCommandId } from \"../types\"\nimport { activeTableCell } from \"./cell-inline\"\nimport { resolveForCell } from \"./command-helpers\"\nimport { BUILTIN_BY_ID } from \"./commands\"\nimport type { ToolbarItem } from \"./config\"\nimport { useFloatingWatchdog } from \"./floating-watchdog\"\nimport { useKeyboardInset } from \"./keyboard-inset\"\nimport { OverflowBar } from \"./OverflowBar\"\nimport { groupItems, toButton } from \"./toolbar-model\"\nimport type { Btn, Resolved } from \"./toolbar-model\"\n\nexport interface ToolbarProps {\n /** The surface the commands act on. `null` while a lazy view is mounting. */\n view: EditorView | null\n /** Ordered items to render; `\"|\"` is a separator. */\n items: ToolbarItem[]\n /** Per-id glyph overrides; any id left out keeps its built-in icon. */\n icons?: Partial<Record<ToolbarCommandId, ReactNode>>\n /** Render every button inert (e.g. a read-only surface). */\n disabled?: boolean\n /** Fix the bar to a window edge (normalised from `ToolbarConfig.sticky`; the\n * caller resolves `true` to `\"bottom\"`). */\n sticky?: \"top\" | \"bottom\" | false\n /** Fade the bar out while the editing surface is unfocused (`ToolbarConfig.stickyVisibility`). */\n stickyVisibility?: \"consistent\" | \"dynamic\"\n /** `\"menu\"` folds buttons that do not fit into a trailing menu instead of wrapping. */\n overflow?: \"wrap\" | \"menu\"\n /** Glyph for the overflow menu's button. */\n overflowIcon?: ReactNode\n}\n\n/**\n * Formatting bar above the editing surface. It holds no document state — each\n * button runs a command against the live `EditorView`. Pressed states are read\n * back from the view whenever the selection, keys, or pointer move. Built-in\n * ids and consumer-supplied {@link ToolbarCustomItem}s render through the same\n * button path.\n */\nexport function Toolbar({\n view,\n items,\n icons,\n disabled,\n sticky,\n stickyVisibility,\n overflow,\n overflowIcon,\n}: ToolbarProps) {\n const [, refresh] = useReducer((n: number) => n + 1, 0)\n const [focused, setFocused] = useState(false)\n // Only \"bottom\" needs keyboard tracking — nothing eats into the top of the\n // screen the way a keyboard eats the bottom.\n const keyboardInset = useKeyboardInset(sticky === \"bottom\")\n const barRef = useRef<HTMLDivElement>(null)\n useFloatingWatchdog(barRef, sticky === \"top\")\n\n useEffect(() => {\n if (!view) return\n const el = view.contentDOM\n setFocused(view.hasFocus)\n const onFocus = () => {\n setFocused(true)\n refresh()\n }\n const onBlur = () => {\n setFocused(false)\n refresh()\n }\n const events = [\"keyup\", \"mouseup\", \"input\"] as const\n for (const ev of events) el.addEventListener(ev, refresh)\n el.addEventListener(\"focus\", onFocus)\n el.addEventListener(\"blur\", onBlur)\n return () => {\n for (const ev of events) el.removeEventListener(ev, refresh)\n el.removeEventListener(\"focus\", onFocus)\n el.removeEventListener(\"blur\", onBlur)\n }\n }, [view])\n\n // \"dynamic\" fades the bar out while nothing is focused, so it doesn't sit\n // over the content while the caret is elsewhere (e.g. scrolling to read).\n const dynamicHidden = Boolean(sticky) && stickyVisibility === \"dynamic\" && !focused\n\n // `state.selection` never follows the caret into an editable table cell's\n // `contentEditable` DOM (see `extension.ts`'s mousedown handler), so a\n // built-in command's own `disabled` / `isActive` would otherwise read a\n // selection that may point anywhere else in the document while the user is\n // actually typing in a cell. `resolveForCell` below re-derives both from the\n // real DOM focus in that case instead.\n const inCell = view ? Boolean(activeTableCell(view)) : false\n\n const menuMode = overflow === \"menu\"\n\n const className = [\n styles.toolbar,\n sticky && styles.toolbarSticky,\n sticky === \"top\" && styles.toolbarStickyTop,\n sticky === \"bottom\" && styles.toolbarStickyBottom,\n dynamicHidden && styles.toolbarStickyHidden,\n menuMode && styles.toolbarOverflow,\n ]\n .filter(Boolean)\n .join(\" \")\n // `transform`, not `bottom` — a `position: fixed` element repositioned via\n // `bottom` doesn't reliably repaint in step with the keyboard animation on\n // iOS Safari; `translateY` forces a compositor update on every\n // `visualViewport` event instead. The bar's resting position (keyboard\n // closed) is `bottom: 0` in CSS; this only nudges it up. `\"top\"` needs no\n // offset at all.\n const stickyStyle =\n sticky === \"bottom\" ? { transform: `translateY(-${keyboardInset}px)` } : undefined\n\n /** A slot's button plus its live disabled / pressed state, or `null` to skip it. */\n const resolveItem = (item: Exclude<ToolbarItem, \"|\">): Resolved | null => {\n const btn = toButton(item, icons)\n if (!btn) return null\n // Only a built-in command is cell-aware — a host's own `ToolbarCustomItem`\n // keeps reading `state` exactly as before; it has no notion of a table\n // cell to degrade into.\n const builtin = typeof item === \"string\" ? BUILTIN_BY_ID[item] : undefined\n const resolved = view\n ? builtin\n ? resolveForCell(view.state, builtin, inCell)\n : {\n disabled: Boolean(btn.disabled?.(view.state)),\n active: Boolean(btn.isActive?.(view.state)),\n }\n : { disabled: true, active: false }\n const off = Boolean(disabled || !view || resolved.disabled)\n return { btn, off, active: !off && resolved.active }\n }\n\n const run = (btn: Btn) => {\n if (!view) return\n btn.run(view)\n refresh()\n }\n\n const renderItem = (item: Exclude<ToolbarItem, \"|\">) => {\n const r = resolveItem(item)\n if (!r) return null\n const { btn, off, active } = r\n return (\n <button\n key={btn.key}\n type=\"button\"\n className={styles.toolbarButton}\n data-command={btn.key}\n title={btn.title}\n aria-label={btn.title}\n aria-pressed={btn.isActive ? active : undefined}\n data-active={active ? \"\" : undefined}\n disabled={off}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => run(btn)}\n >\n {btn.icon}\n </button>\n )\n }\n\n const content = menuMode ? (\n <OverflowBar\n items={items}\n icons={icons}\n overflowIcon={overflowIcon}\n resolve={resolveItem}\n run={run}\n renderItem={renderItem}\n />\n ) : (\n groupItems(items).map((group, i) =>\n group === \"|\" ? (\n <span key={`sep-${i}`} className={styles.toolbarSep} aria-hidden=\"true\" />\n ) : (\n <div key={`group-${i}`} className={styles.toolbarGroup}>\n {group.map(renderItem)}\n </div>\n ),\n )\n )\n\n return (\n <div\n ref={barRef}\n className={className}\n role=\"toolbar\"\n aria-label=\"Formatting\"\n aria-hidden={dynamicHidden || undefined}\n style={stickyStyle}\n >\n {content}\n </div>\n )\n}\n","import { forwardRef, Suspense, useEffect, useImperativeHandle, useRef, useState } from \"react\"\nimport { EditorView } from \"@codemirror/view\"\nimport { SourceView } from \"./editor/SourceView\"\nimport { LazyInPlaceView } from \"./inplace/lazyInPlace\"\nimport { LazyPreview } from \"./render/lazyPreview\"\nimport { SplitView } from \"./SplitView\"\nimport styles from \"./styles/stylo.module.css\"\nimport { Toolbar } from \"./toolbar/Toolbar\"\nimport { resolveToolbarItems } from \"./toolbar/config\"\nimport { splitFrontmatter } from \"./frontmatter\"\nimport { invalidateEmbed } from \"./render/embed-cache\"\nimport \"./styles/tokens.css\"\nimport type { ResolveErrorInfo, StyloHandle, StyloProps } from \"./types\"\n\n/**\n * Plain-text-first Markdown editor. `value` is the canonical Markdown string;\n * every view is a pure function of it.\n *\n * The default `mode` is `in-place`, the live decoration canvas (ADR-002 §1,\n * ADR-004). Pass `mode=\"source\"` for the plain surface with no lazy render\n * chunk. `preview` and `split` are also available.\n *\n * A formatting `toolbar` sits above every editing surface (all modes but\n * `preview`); pass `toolbar={false}` to drop it or a `ToolbarConfig` to trim it.\n */\nexport const Stylo = forwardRef<StyloHandle, StyloProps>(function Stylo(\n {\n value,\n onChange,\n mode = \"in-place\",\n onSave,\n onFrontmatter,\n onWikiLinkClick,\n onLinkClick,\n readOnly,\n placeholder,\n className,\n inPlace,\n codeLanguages,\n wikiLinkSource,\n tagSource,\n embedSource,\n onResolveError,\n toolbar,\n icons,\n frontmatter,\n softBreaks,\n onTaskToggle,\n canvasHeader,\n extensions,\n },\n ref,\n) {\n const resolved = mode === \"preview\" || mode === \"split\" || mode === \"in-place\" ? mode : \"source\"\n\n const [view, setView] = useState<EditorView | null>(null)\n const toolbarItems = resolved === \"preview\" ? null : resolveToolbarItems(toolbar)\n const toolbarConfig = toolbar && typeof toolbar === \"object\" ? toolbar : undefined\n const toolbarRender = toolbarConfig?.render\n const stickyToolbar: \"top\" | \"bottom\" | false =\n toolbarConfig?.sticky === \"top\" ? \"top\" : toolbarConfig?.sticky ? \"bottom\" : false\n const stickyVisibility = toolbarConfig?.stickyVisibility\n\n // Report the raw frontmatter block on mount and whenever it changes. Stylo\n // does not parse it — the host passes `raw` to its own YAML parser.\n const onFrontmatterRef = useRef(onFrontmatter)\n onFrontmatterRef.current = onFrontmatter\n\n // Stable wrapper so a changed `onResolveError` needs no editor rebuild and\n // does not thrash the embed effect. Passed to every surface.\n const onResolveErrorRef = useRef(onResolveError)\n onResolveErrorRef.current = onResolveError\n const resolveError = useRef((error: unknown, info: ResolveErrorInfo) => {\n onResolveErrorRef.current?.(error, info)\n }).current\n\n // Read by `invalidateEmbed` below — kept current without needing the\n // imperative handle itself to be rebuilt when `embedSource` changes.\n const embedSourceRef = useRef(embedSource)\n embedSourceRef.current = embedSource\n const lastFrontmatter = useRef<string | null | undefined>(undefined)\n useEffect(() => {\n const raw = splitFrontmatter(value)?.frontmatter ?? null\n if (raw === lastFrontmatter.current) return\n lastFrontmatter.current = raw\n onFrontmatterRef.current?.(raw)\n }, [value])\n\n useImperativeHandle(\n ref,\n () => ({\n focus: () => view?.focus(),\n getView: () => view,\n insertAtCursor: (md) => {\n if (!view) return\n view.dispatch(view.state.replaceSelection(md))\n view.focus()\n },\n scrollToHeading: (text) => {\n if (!view) return false\n const target = text.trim().toLowerCase()\n const { doc } = view.state\n for (let n = 1; n <= doc.lines; n++) {\n const line = doc.line(n)\n const m = /^ {0,3}#{1,6}[ \\t]+(.*?)(?:[ \\t]+#+)?[ \\t]*$/.exec(line.text)\n if (!m || m[1] === undefined) continue\n if (m[1].trim().toLowerCase() !== target) continue\n view.dispatch({\n selection: { anchor: line.from },\n effects: EditorView.scrollIntoView(line.from, { y: \"start\" }),\n })\n view.focus()\n return true\n }\n return false\n },\n invalidateEmbed: (ref) => {\n const source = embedSourceRef.current\n if (source) invalidateEmbed(source, ref)\n },\n }),\n [view],\n )\n\n const rootClass = [\n styles.root,\n stickyToolbar === \"bottom\" && styles.stickyToolbarRootBottom,\n stickyToolbar === \"top\" && styles.stickyToolbarRootTop,\n \"stylo\",\n className,\n ]\n .filter(Boolean)\n .join(\" \")\n\n return (\n <div className={rootClass} data-stylo-mode={resolved}>\n {toolbarItems &&\n (() => {\n const bar = (\n <Toolbar\n view={view}\n items={toolbarItems}\n icons={icons}\n disabled={readOnly}\n sticky={stickyToolbar}\n stickyVisibility={stickyVisibility}\n overflow={toolbarConfig?.overflow}\n overflowIcon={toolbarConfig?.overflowIcon}\n />\n )\n return toolbarRender ? toolbarRender(bar, { view }) : bar\n })()}\n\n {resolved === \"source\" && (\n <SourceView\n value={value}\n onChange={onChange}\n readOnly={readOnly}\n placeholder={placeholder}\n codeLanguages={codeLanguages}\n wikiLinkSource={wikiLinkSource}\n tagSource={tagSource}\n onResolveError={resolveError}\n onSave={onSave}\n onViewChange={setView}\n canvasHeader={canvasHeader}\n hostExtensions={extensions}\n />\n )}\n\n {resolved === \"preview\" && (\n <Suspense fallback={<div className={styles.preview} aria-busy=\"true\" />}>\n <LazyPreview\n value={value}\n onWikiLinkClick={onWikiLinkClick}\n embedSource={embedSource}\n onResolveError={resolveError}\n frontmatter={frontmatter}\n codeLanguages={codeLanguages}\n softBreaks={softBreaks}\n onTaskToggle={onTaskToggle}\n />\n </Suspense>\n )}\n\n {resolved === \"split\" && (\n <SplitView\n value={value}\n onChange={onChange}\n onWikiLinkClick={onWikiLinkClick}\n readOnly={readOnly}\n placeholder={placeholder}\n codeLanguages={codeLanguages}\n wikiLinkSource={wikiLinkSource}\n tagSource={tagSource}\n embedSource={embedSource}\n onResolveError={resolveError}\n frontmatter={frontmatter}\n softBreaks={softBreaks}\n onTaskToggle={onTaskToggle}\n onSave={onSave}\n onViewChange={setView}\n canvasHeader={canvasHeader}\n hostExtensions={extensions}\n />\n )}\n\n {resolved === \"in-place\" && (\n <Suspense fallback={<div className={styles.inplace} aria-busy=\"true\" />}>\n <LazyInPlaceView\n value={value}\n onChange={onChange}\n readOnly={readOnly}\n placeholder={placeholder}\n onWikiLinkClick={onWikiLinkClick}\n onLinkClick={onLinkClick}\n inPlace={inPlace}\n codeLanguages={codeLanguages}\n wikiLinkSource={wikiLinkSource}\n tagSource={tagSource}\n embedSource={embedSource}\n onResolveError={resolveError}\n onSave={onSave}\n onViewChange={setView}\n canvasHeader={canvasHeader}\n hostExtensions={extensions}\n />\n </Suspense>\n )}\n </div>\n )\n})\n\nStylo.displayName = \"Stylo\"\n"],"mappings":";;;;;;;;AAWA,IAAM,gBAA4B,CAAC,GAC7B,UAAqB;AA0B3B,SAAgB,EAAW,EACzB,UACA,aACA,aACA,gBACA,kBACA,mBACA,cACA,mBACA,WACA,iBACA,iBACA,qBACkB;CAClB,IAAM,CAAC,GAAY,KAAiB,EAA4B,IAAI,GAC9D,IAAkB,EAAO,CAAY;CAC3C,EAAgB,UAAU;CAC1B,IAAM,IAAmB,GAAQ,MAA4B;EAE3D,AADA,EAAc,CAAI,GAClB,EAAgB,UAAU,CAAI;CAChC,CAAC,CAAC,CAAC,SAKG,IAAkB,EAAO,CAAY;CAC3C,EAAgB,UAAU;CAC1B,IAAM,CAAC,KAAc,QAAgB,IAAe,IAAI,EAAiB,IAAI,IAAK,GAC5E,IAAY,EAChB,GAAY,aAAa,GACzB,GAAY,eAAe,CAC7B,GACM,CAAC,KAAc,QAAgB,IAAa,CAAC,EAAU,GAAG,EAAW,KAAK,CAAC,IAAI,CAAC,CAAE,GAElF,IAAM,EAAc;EACxB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,cAAc;EACd;EACA;CACF,CAAC;CACD,OACE,kBAAC,OAAD;EAAK,WAAW,EAAO;EAAa;EACjC,UAAA,KACC,EAAgB,WAChB,EAAa,EAAgB,QAAQ,EAAE,MAAM,EAAW,CAAC,GAAG,CAAS;CACpE,CAAA;AAET;;;ACtFA,IAAa,IAAkB,EAAK,aAAa,EAC/C,UAAU,MAAM,OAAO,6BAAA,CAAkB,YAC3C,EAAE,GCDW,IAAc,EAAK,aAAa,EAC3C,UAAU,MAAM,OAAO,yBAAA,CAAc,QACvC,EAAE;;;ACqCF,SAAgB,EAAU,EACxB,UACA,aACA,oBACA,aACA,gBACA,kBACA,mBACA,cACA,gBACA,mBACA,gBACA,eACA,iBACA,WACA,iBACA,iBACA,qBACiB;CACjB,IAAM,CAAC,GAAM,KAAW,EAA4B,IAAI,GAClD,IAAc,EAA8B,IAAI,GAChD,IAAU,EAAO,EAAK;CAiC5B,OA1BA,QAAgB;EACd,IAAM,IAAS,GAAM,WACf,IAAU,EAAY;EAC5B,IAAI,CAAC,KAAU,CAAC,GAAS;EAEzB,IAAM,KAAQ,GAAmB,MAAoB;GACnD,IAAI,EAAQ,SAAS;GACrB,IAAM,IAAQ,EAAK,eAAe,EAAK;GACnC,KAAS,MACb,EAAQ,UAAU,IAClB,EAAG,YAAa,EAAK,YAAY,KAAU,EAAG,eAAe,EAAG,eAChE,4BAA4B;IAC1B,EAAQ,UAAU;GACpB,CAAC;EACH,GAEM,UAAmB,EAAK,GAAQ,CAAO,GACvC,UAAoB,EAAK,GAAS,CAAM;EAG9C,OAFA,EAAO,iBAAiB,UAAU,GAAY,EAAE,SAAS,GAAK,CAAC,GAC/D,EAAQ,iBAAiB,UAAU,GAAa,EAAE,SAAS,GAAK,CAAC,SACpD;GAEX,AADA,EAAO,oBAAoB,UAAU,CAAU,GAC/C,EAAQ,oBAAoB,UAAU,CAAW;EACnD;CACF,GAAG,CAAC,CAAI,CAAC,GAGP,kBAAC,OAAD;EAAK,WAAW,EAAO;EAAvB,UAAA,CACE,kBAAC,OAAD;GAAK,WAAW,EAAO;GACrB,UAAA,kBAAC,GAAD;IACS;IACG;IACA;IACG;IACE;IACC;IACL;IACK;IACR;IACR,eA5CY,MAA4B;KAE9C,AADA,EAAQ,CAAI,GACZ,IAAe,CAAI;IACrB;IA0CsB;IACE;GACjB,CAAA;EACE,CAAA,GACL,kBAAC,OAAD;GAAK,WAAW,EAAO;GAAW,KAAK;GACrC,UAAA,kBAAC,GAAD;IAAU,UAAU,kBAAC,OAAD;KAAK,WAAW,EAAO;KAAS,aAAU;IAAQ,CAAA;IACpE,UAAA,kBAAC,GAAD;KACS;KACU;KACJ;KACG;KACH;KACE;KACH;KACE;IACf,CAAA;GACO,CAAA;EACP,CAAA,CACF;;AAET;;;ACtHA,SAAgB,EAAoB,GAAoC,GAAwB;CAC9F,QAAgB;EACd,IAAI,CAAC,GAAS;EACd,IAAI,IAAM,GACN,IAAS,IACP,UAAa;GACjB,IAAM,IAAK,EAAI;GAKf,AAJI,MACF,IAAS,CAAC,GACV,EAAG,MAAM,YAAY,IAAS,8BAA8B,yBAE9D,IAAM,sBAAsB,CAAI;EAClC;EAEA,OADA,IAAM,sBAAsB,CAAI,SACnB,qBAAqB,CAAG;CACvC,GAAG,CAAC,GAAK,CAAO,CAAC;AACnB;;;ACvBA,SAAgB,EAAiB,GAA0B;CACzD,IAAM,CAAC,GAAO,KAAY,EAAS,CAAC;CAkBpC,OAhBA,QAAgB;EACd,IAAM,IAAK,OAAO,SAAW,MAAc,OAAO,OAAO;EACzD,IAAI,CAAC,KAAW,CAAC,GAAI;GACnB,EAAS,CAAC;GACV;EACF;EACA,IAAM,UAAe,EAAS,KAAK,IAAI,GAAG,OAAO,cAAc,EAAG,SAAS,EAAG,SAAS,CAAC;EAIxF,OAHA,EAAO,GACP,EAAG,iBAAiB,UAAU,CAAM,GACpC,EAAG,iBAAiB,UAAU,CAAM,SACvB;GAEX,AADA,EAAG,oBAAoB,UAAU,CAAM,GACvC,EAAG,oBAAoB,UAAU,CAAM;EACzC;CACF,GAAG,CAAC,CAAO,CAAC,GAEL;AACT;;;AChBA,IAAM,IACJ,kBAAC,OAAD;CAAK,SAAQ;CAAY,MAAK;CAAe,QAAO;CAApD,UAAA;EACE,kBAAC,UAAD;GAAQ,IAAG;GAAI,IAAG;GAAK,GAAE;EAAO,CAAA;EAChC,kBAAC,UAAD;GAAQ,IAAG;GAAK,IAAG;GAAK,GAAE;EAAO,CAAA;EACjC,kBAAC,UAAD;GAAQ,IAAG;GAAK,IAAG;GAAK,GAAE;EAAO,CAAA;CAC9B;IAIM,KAAa,MAAoB,KAAQ;AAQtD,SAAgB,EAAa,EAC3B,YACA,WAKC;CACD,IAAM,CAAC,GAAM,KAAW,EAAS,EAAK,GAChC,IAAU,EAAuB,IAAI,GACrC,IAAU,EAA0B,IAAI;CAE9C,QAAgB;EACd,IAAI,CAAC,GAAM;EACX,IAAM,KAAU,MAAkB;GAChC,AAAK,EAAQ,SAAS,SAAS,EAAE,MAAc,KAAG,EAAQ,EAAK;EACjE,GAEM,KAAS,MAAqB;GAClC,IAAI,EAAE,QAAQ,UAAU;GACxB,IAAM,IAAS,EAAQ,SAAS,SAAS,SAAS,aAAa;GAE/D,AADA,EAAQ,EAAK,GACT,KAAQ,EAAQ,SAAS,MAAM;EACrC;EAGA,OAFA,SAAS,iBAAiB,aAAa,CAAM,GAC7C,SAAS,iBAAiB,WAAW,CAAK,SAC7B;GAEX,AADA,SAAS,oBAAoB,aAAa,CAAM,GAChD,SAAS,oBAAoB,WAAW,CAAK;EAC/C;CACF,GAAG,CAAC,CAAI,CAAC;CAET,IAAM,UACJ,MAAM,KACJ,EAAQ,SAAS,iBAAoC,0BAA0B,KAAK,CAAC,CACvF;CAeF,OACE,kBAAC,OAAD;EACE,KAAK;EACL,WAAW,EAAO;EAClB,uBAAoB;EACT,YAlBI,MAA2B;GAC5C,KAAK,EAAE,QAAQ,eAAe,EAAE,QAAQ,cAAc,GAAM;IAC1D,EAAE,eAAe;IACjB,IAAM,IAAO,EAAM,GACb,IAAK,EAAK,QAAQ,SAAS,aAAkC,GAC7D,IAAO,EAAE,QAAQ;IAIvB,EADE,IAAK,IAAK,IAAO,IAAI,EAAK,SAAS,KAAM,KAAM,IAAO,IAAI,MAAM,EAAK,UAAU,EAAK,OAC5E,EAAE,MAAM;GACpB;EACF;EAQI,SAAS,MAAM;GAEb,AAAI,KAAQ,CAAC,EAAQ,SAAS,SAAS,EAAE,aAA4B,KAAG,EAAQ,EAAK;EACvF;EARF,UAAA,CAUE,kBAAC,UAAD;GACE,KAAK;GACL,MAAK;GACL,WAAW,EAAO;GAClB,OAAM;GACN,cAAW;GACX,iBAAc;GACd,iBAAe;GACf,cAAc,MAAM,EAAE,eAAe;GACrC,UAAU,MAAM;IACd,IAAM,IAAO,CAAC;IAGd,AAFA,EAAQ,CAAI,GAER,KAAQ,EAAE,WAAW,KAAG,4BAA4B,EAAM,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC;GAC7E;GAEC,UAAA,EAAU,CAAI;EACT,CAAA,GACP,KACC,kBAAC,OAAD;GAAK,WAAW,EAAO;GAAa,MAAK;GAAO,4BAAyB;GACtE,UAAA,EAAQ,KAAK,GAAO,MACnB,MAAU,MACR,kBAAC,OAAD;IAAoB,WAAW,EAAO;IAAoB,MAAK;GAAa,GAAlE,KAAK,GAA6D,IAE5E,kBAAC,UAAD;IAEE,MAAK;IACL,MAAM,EAAM,WAAW,KAAA,IAAY,aAAa;IAChD,gBAAc,EAAM;IACpB,eAAa,EAAM,SAAS,KAAK,KAAA;IACjC,WAAW,EAAO;IAClB,gBAAc,EAAM;IACpB,UAAU,EAAM;IAChB,cAAc,MAAM,EAAE,eAAe;IACrC,eAAe;KAIb,AAFI,EAAQ,SAAS,SAAS,SAAS,aAAa,KAAG,EAAQ,SAAS,MAAM,GAC9E,EAAQ,EAAK,GACb,EAAM,IAAI;IACZ;IAfF,UAAA,CAiBG,EAAM,MACP,kBAAC,QAAD,EAAA,UAAO,EAAM,MAAY,CAAA,CACnB;GAlBD,GAAA,EAAM,GAkBL,CAEZ;EACG,CAAA,CAEJ;;AAET;;;ACrIA,SAAgB,EAAqC,GAAgB;CACnE,IAAM,IAAW,CAAC;CAClB,KAAK,IAAM,KAAK,GACV,EAAE,QAAQ,EAAI,WAAW,KAAK,EAAI,EAAI,SAAS,EAAE,CAAE,QACvD,EAAI,KAAK,CAAC;CAEZ,OAAO,EAAI,UAAU,EAAI,EAAI,SAAS,EAAE,CAAE,MAAK,EAAI,IAAI;CACvD,OAAO;AACT;AAMA,SAAgB,EAAuD,GAAgB;CACrF,IAAM,IAAU,EAAK,WAAW,MAAM,EAAE,MAAM,GACxC,IAAS,EAAK,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC,YAAY,EAAI;CACzD,OAAO,EAAS,EAAK,QAAQ,GAAG,MAAM,EAAE,UAAW,EAAE,OAAO,IAAI,KAAW,IAAI,CAAO,CAAC;AACzF;AAQA,SAAgB,EACd,GACA,GACA,GACA,IAAM,GACK;CACX,IAAM,IAAS,EAAQ,UAAU,EAAK,GAChC,UAAa;EACjB,IAAM,IAAO,EAAS,EAAQ,QAAQ,GAAG,MAAM,CAAC,EAAO,EAAE,CAAC;EAG1D,OAFY,EAAK,QAAQ,GAAK,MAAM,IAAM,EAAE,OAAO,CAAC,IAAI,IAAM,KAAK,IAAI,GAAG,EAAK,SAAS,CAAC,KAC5E,EAAO,KAAK,OAAO,IAAI,KAAa,EAAK,SAAS,IAAM,KAAK,MACrD;CACvB,GACM,KAAY,MAAoB;EACpC,KAAK,IAAI,IAAI,EAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;GAC5C,IAAM,IAAI,EAAQ;GAClB,IAAI,CAAC,EAAO,MAAM,CAAC,EAAE,OAAO,EAAE,WAAW,GAAQ,OAAO;EAC1D;EACA,OAAO;CACT;CACA,OAAO,CAAC,EAAK,IAAG;EACd,IAAM,IAAI,EAAS,EAAK,KAAK,IAAI,EAAS,EAAK,IAAI,EAAS,EAAI;EAChE,IAAI,IAAI,GAAG;EACX,EAAO,KAAK;CACd;CACA,OAAO;AACT;;;ACpCA,SAAgB,EAAW,GAA6D;CACtF,IAAM,IAAgD,CAAC,GACnD,IAAmC,CAAC;CACxC,KAAK,IAAM,KAAQ,GAAO;EACxB,IAAI,MAAS,KAAK;GAChB,EAAI,KAAK,CAAI;GACb;EACF;EAGA,AAFI,EAAI,UAAQ,EAAO,KAAK,CAAG,GAC/B,IAAM,CAAC,GACP,EAAO,KAAK,GAAG;CACjB;CAEA,OADI,EAAI,UAAQ,EAAO,KAAK,CAAG,GACxB;AACT;AAGA,SAAgB,EACd,GACA,GACY;CACZ,IAAI,OAAO,KAAS,UAAU;EAC5B,IAAM,EAAE,OAAI,SAAM,UAAO,QAAK,aAAU,gBAAa;EACrD,OAAO;GAAE,KAAK;GAAI;GAAM;GAAO;GAAK;GAAU;EAAS;CACzD;CACA,IAAM,IAAM,EAAc;CAE1B,OADK,IACE;EACL,KAAK;EACL,MAAM,IAAQ,MAAS,EAAc;EACrC,OAAO,EAAI;EACX,KAAK,EAAI;EACT,UAAU,EAAI;EACd,UAAU,EAAI;CAChB,IARiB;AASnB;;;ACxDA,IAAM,KAAM,MAAc,WAAW,CAAC,KAAK;AAG3C,SAAS,EAAW,GAAyB;CAC3C,IAAM,IAAK,iBAAiB,CAAE;CAC9B,OAAO,EAAG,sBAAsB,CAAC,CAAC,QAAQ,EAAG,EAAG,UAAU,IAAI,EAAG,EAAG,WAAW;AACjF;AAYA,SAAgB,EACd,GACA,GACW;CACX,IAAM,CAAC,GAAQ,KAAa,EAAoB,CAAC,CAAC,GAC5C,IAAM,EAAM,KAAK,MAAM,GAAG,EAAE,MAAM,EAAE,MAAM,MAAM,KAAK,EAAE,SAAS,MAAM,IAAI,CAAC,CAAC,KAAK,IAAI,GAErF,IAAU,QAAkB;EAChC,IAAM,IAAM,EAAW,SACjB,IAAM,GAAK,QAAqB,gBAAgB;EACtD,IAAI,CAAC,KAAO,CAAC,GAAK;EAClB,IAAM,IAAO,MAAM,KAAK,EAAI,QAAQ,GAC9B,IAAO,EAAK,IAAI;EACtB,IAAI,CAAC,KAAQ,EAAK,WAAW,EAAM,QAAQ;EAC3C,IAAM,IAAK,iBAAiB,CAAG,GACzB,IAAY,EAAI,cAAc,EAAG,EAAG,WAAW,IAAI,EAAG,EAAG,YAAY,GACrE,IAAO,EACX,EAAM,KAAK,GAAG,OAAO;GAAE,KAAK,EAAE;GAAK,QAAQ,EAAE;GAAQ,OAAO,EAAW,EAAK,EAAG;EAAE,EAAE,GACnF,GACA,EAAW,CAAI,GACf,EAAG,iBAAiB,CAAG,CAAC,CAAC,SAAS,CACpC;EACA,GAAW,MACT,EAAI,WAAW,EAAK,UAAU,EAAI,OAAO,GAAG,MAAM,MAAM,EAAK,EAAE,IAAI,IAAM,CAC3E;CAEF,GAAG,CAAC,GAAY,CAAG,CAAC;CAgBpB,OAdA,QAAsB;EAGpB,IAAM,IAAM,EAAW,SACjB,IAAM,GAAK,QAAqB,gBAAgB;EAGtD,IAFI,CAAC,KAAO,CAAC,MACb,EAAQ,GACJ,OAAO,iBAAmB,MAAa;EAC3C,IAAM,IAAK,IAAI,eAAe,CAAO;EAGrC,OAFA,EAAG,QAAQ,CAAG,GACd,EAAG,QAAQ,CAAG,SACD,EAAG,WAAW;CAC7B,GAAG,CAAC,GAAY,CAAO,CAAC,GAEjB;AACT;;;ACrDA,IAAM,UAAY,kBAAC,QAAD;CAAM,WAAW,EAAO;CAAY,eAAY;AAAQ,CAAA;AAQ1E,SAAgB,EAAY,EAC1B,UACA,UACA,iBACA,YACA,QACA,iBAQC;CACD,IAAM,IAAa,EAAuB,IAAI,GAExC,IAAO,EAAM,QAAQ,MAAO,MAAO,OAAO,EAAS,GAAI,CAAK,MAAM,IAAI,GACtE,IAAS,EACb,GACA,EAAK,KAAK,GAAI,OAAO;EACnB,KAAK,MAAO,MAAM,OAAO,MAAM,OAAO,KAAO,WAAW,IAAK,EAAG;EAChE,KAAK,MAAO;EACZ,QAAQ,OAAO,KAAO,YAAY,EAAQ,EAAG;CAC/C,EAAE,CACJ,GACM,IAAS,EAAK,KAAK,GAAI,OAAO;EAAE;EAAI,KAAK,MAAO;EAAK,QAAQ,EAAQ,EAAO;CAAI,EAAE,GAClF,IAAM,EAAS,EAAO,QAAQ,MAAM,CAAC,EAAE,MAAM,CAAC,GAC9C,IAAO,EAAU,CAAM,GAEvB,IAAU,EAAK,KAAK,MAA2B;EACnD,IAAM,IAAI,EAAE,OAAO,MAAM,OAAO,EAAQ,EAAE,EAAE;EAC5C,IAAI,CAAC,GAAG,OAAO;EACf,IAAM,EAAE,QAAK,QAAK,cAAW;EAC7B,OAAO;GACL,KAAK,EAAI;GACT,OAAO,EAAI;GACX,MAAM,EAAI;GACV,UAAU;GACV,QAAQ,EAAI,WAAW,IAAS,KAAA;GAChC,WAAW,EAAI,CAAG;EACpB;CACF,CAAC;CAED,OACE,kBAAA,GAAA,EAAA,UAAA;EACG,EAAI,KAAK,GAAG,MAAO,EAAE,OAAO,MAAM,kBAAC,GAAD,CAAuB,GAAb,OAAO,GAAM,IAAI,EAAW,EAAE,EAAE,CAAE;EAC9E,EAAK,SAAS,KAAK,kBAAC,GAAD;GAAuB;GAAS,MAAM;EAAe,CAAA;EACzE,kBAAC,OAAD;GAAK,WAAW,EAAO;GAAoB,eAAY;GACrD,UAAA,kBAAC,OAAD;IAAK,KAAK;IAAY,WAAW,EAAO;IAAxC,UAAA,CACG,EAAK,KAAK,GAAI,MACb,MAAO,MACL,kBAAC,GAAD,CAAc,GAAJ,CAAI,IAEd,kBAAC,UAAD;KAAgB,MAAK;KAAS,UAAU;KAAI,WAAW,EAAO;KAC3D,UAAA,EAAS,GAAI,CAAK,CAAC,CAAE;IAChB,GAFK,CAEL,CAEZ,GACA,kBAAC,UAAD;KAAQ,MAAK;KAAS,UAAU;KAAI,WAAW,EAAO;KACnD,UAAA,EAAU,CAAY;IACjB,CAAA,CACL;;EACF,CAAA;CACL,EAAA,CAAA;AAEN;;;AC5CA,SAAgB,EAAQ,EACtB,SACA,UACA,UACA,aACA,WACA,qBACA,aACA,mBACe;CACf,IAAM,GAAG,KAAW,GAAY,MAAc,IAAI,GAAG,CAAC,GAChD,CAAC,GAAS,KAAc,EAAS,EAAK,GAGtC,IAAgB,EAAiB,MAAW,QAAQ,GACpD,IAAS,EAAuB,IAAI;CAG1C,AAFA,EAAoB,GAAQ,MAAW,KAAK,GAE5C,QAAgB;EACd,IAAI,CAAC,GAAM;EACX,IAAM,IAAK,EAAK;EAChB,EAAW,EAAK,QAAQ;EACxB,IAAM,UAAgB;GAEpB,AADA,EAAW,EAAI,GACf,EAAQ;EACV,GACM,UAAe;GAEnB,AADA,EAAW,EAAK,GAChB,EAAQ;EACV,GACM,IAAS;GAAC;GAAS;GAAW;EAAO;EAC3C,KAAK,IAAM,KAAM,GAAQ,EAAG,iBAAiB,GAAI,CAAO;EAGxD,OAFA,EAAG,iBAAiB,SAAS,CAAO,GACpC,EAAG,iBAAiB,QAAQ,CAAM,SACrB;GACX,KAAK,IAAM,KAAM,GAAQ,EAAG,oBAAoB,GAAI,CAAO;GAE3D,AADA,EAAG,oBAAoB,SAAS,CAAO,GACvC,EAAG,oBAAoB,QAAQ,CAAM;EACvC;CACF,GAAG,CAAC,CAAI,CAAC;CAIT,IAAM,IAAgB,EAAQ,KAAW,MAAqB,aAAa,CAAC,GAQtE,IAAS,IAAO,EAAQ,EAAgB,CAAI,IAAK,IAEjD,IAAW,MAAa,QAExB,IAAY;EAChB,EAAO;EACP,KAAU,EAAO;EACjB,MAAW,SAAS,EAAO;EAC3B,MAAW,YAAY,EAAO;EAC9B,KAAiB,EAAO;EACxB,KAAY,EAAO;CACrB,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,GAOL,IACJ,MAAW,WAAW,EAAE,WAAW,eAAe,EAAc,KAAK,IAAI,KAAA,GAGrE,KAAe,MAAqD;EACxE,IAAM,IAAM,EAAS,GAAM,CAAK;EAChC,IAAI,CAAC,GAAK,OAAO;EAIjB,IAAM,IAAU,OAAO,KAAS,WAAW,EAAc,KAAQ,KAAA,GAC3D,IAAW,IACb,IACE,EAAe,EAAK,OAAO,GAAS,CAAM,IAC1C;GACE,UAAU,EAAQ,EAAI,WAAW,EAAK,KAAK;GAC3C,QAAQ,EAAQ,EAAI,WAAW,EAAK,KAAK;EAC3C,IACF;GAAE,UAAU;GAAM,QAAQ;EAAM,GAC9B,IAAM,GAAQ,KAAY,CAAC,KAAQ,EAAS;EAClD,OAAO;GAAE;GAAK;GAAK,QAAQ,CAAC,KAAO,EAAS;EAAO;CACrD,GAEM,KAAO,MAAa;EACnB,MACL,EAAI,IAAI,CAAI,GACZ,EAAQ;CACV,GAEM,KAAc,MAAoC;EACtD,IAAM,IAAI,EAAY,CAAI;EAC1B,IAAI,CAAC,GAAG,OAAO;EACf,IAAM,EAAE,QAAK,QAAK,cAAW;EAC7B,OACE,kBAAC,UAAD;GAEE,MAAK;GACL,WAAW,EAAO;GAClB,gBAAc,EAAI;GAClB,OAAO,EAAI;GACX,cAAY,EAAI;GAChB,gBAAc,EAAI,WAAW,IAAS,KAAA;GACtC,eAAa,IAAS,KAAK,KAAA;GAC3B,UAAU;GACV,cAAc,MAAM,EAAE,eAAe;GACrC,eAAe,EAAI,CAAG;GAErB,UAAA,EAAI;EACC,GAbD,EAAI,GAaH;CAEZ,GAEM,IAAU,IACd,kBAAC,GAAD;EACS;EACA;EACO;EACd,SAAS;EACJ;EACO;CACb,CAAA,IAED,EAAW,CAAK,CAAC,CAAC,KAAK,GAAO,MAC5B,MAAU,MACR,kBAAC,QAAD;EAAuB,WAAW,EAAO;EAAY,eAAY;CAAQ,GAA9D,OAAO,GAAuD,IAEzE,kBAAC,OAAD;EAAwB,WAAW,EAAO;EACvC,UAAA,EAAM,IAAI,CAAU;CAClB,GAFK,SAAS,GAEd,CAET;CAGF,OACE,kBAAC,OAAD;EACE,KAAK;EACM;EACX,MAAK;EACL,cAAW;EACX,eAAa,KAAiB,KAAA;EAC9B,OAAO;EAEN,UAAA;CACE,CAAA;AAET;;;AC7KA,IAAa,IAAQ,EAAoC,SACvD,EACE,UACA,aACA,UAAO,YACP,WACA,kBACA,oBACA,gBACA,aACA,gBACA,cACA,YACA,kBACA,mBACA,cACA,gBACA,mBACA,YACA,UACA,gBACA,eACA,iBACA,iBACA,iBAEF,GACA;CACA,IAAM,IAAW,MAAS,aAAa,MAAS,WAAW,MAAS,aAAa,IAAO,UAElF,CAAC,GAAM,KAAW,EAA4B,IAAI,GAClD,IAAe,MAAa,YAAY,OAAO,EAAoB,CAAO,GAC1E,IAAgB,KAAW,OAAO,KAAY,WAAW,IAAU,KAAA,GACnE,IAAgB,GAAe,QAC/B,IACJ,GAAe,WAAW,QAAQ,QAAQ,GAAe,SAAS,WAAW,IACzE,IAAmB,GAAe,kBAIlC,IAAmB,EAAO,CAAa;CAC7C,EAAiB,UAAU;CAI3B,IAAM,IAAoB,EAAO,CAAc;CAC/C,EAAkB,UAAU;CAC5B,IAAM,IAAe,GAAQ,GAAgB,MAA2B;EACtE,EAAkB,UAAU,GAAO,CAAI;CACzC,CAAC,CAAC,CAAC,SAIG,IAAiB,EAAO,CAAW;CACzC,EAAe,UAAU;CACzB,IAAM,IAAkB,EAAkC,KAAA,CAAS;CAQnE,AAPA,QAAgB;EACd,IAAM,IAAM,EAAiB,CAAK,CAAC,EAAE,eAAe;EAChD,MAAQ,EAAgB,YAC5B,EAAgB,UAAU,GAC1B,EAAiB,UAAU,CAAG;CAChC,GAAG,CAAC,CAAK,CAAC,GAEV,EACE,UACO;EACL,aAAa,GAAM,MAAM;EACzB,eAAe;EACf,iBAAiB,MAAO;GACjB,MACL,EAAK,SAAS,EAAK,MAAM,iBAAiB,CAAE,CAAC,GAC7C,EAAK,MAAM;EACb;EACA,kBAAkB,MAAS;GACzB,IAAI,CAAC,GAAM,OAAO;GAClB,IAAM,IAAS,EAAK,KAAK,CAAC,CAAC,YAAY,GACjC,EAAE,WAAQ,EAAK;GACrB,KAAK,IAAI,IAAI,GAAG,KAAK,EAAI,OAAO,KAAK;IACnC,IAAM,IAAO,EAAI,KAAK,CAAC,GACjB,IAAI,+CAA+C,KAAK,EAAK,IAAI;IACnE,OAAC,KAAK,EAAE,OAAO,KAAA,MACf,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,YAAY,MAAM,GAMlC,OALA,EAAK,SAAS;KACZ,WAAW,EAAE,QAAQ,EAAK,KAAK;KAC/B,SAAS,EAAW,eAAe,EAAK,MAAM,EAAE,GAAG,QAAQ,CAAC;IAC9D,CAAC,GACD,EAAK,MAAM,GACJ;GACT;GACA,OAAO;EACT;EACA,kBAAkB,MAAQ;GACxB,IAAM,IAAS,EAAe;GAC9B,AAAI,KAAQ,EAAgB,GAAQ,CAAG;EACzC;CACF,IACA,CAAC,CAAI,CACP;CAEA,IAAM,KAAY;EAChB,EAAO;EACP,MAAkB,YAAY,EAAO;EACrC,MAAkB,SAAS,EAAO;EAClC;EACA;CACF,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CAEX,OACE,kBAAC,OAAD;EAAK,WAAW;EAAW,mBAAiB;EAA5C,UAAA;GACG,YACQ;IACL,IAAM,IACJ,kBAAC,GAAD;KACQ;KACN,OAAO;KACA;KACP,UAAU;KACV,QAAQ;KACU;KAClB,UAAU,GAAe;KACzB,cAAc,GAAe;IAC9B,CAAA;IAEH,OAAO,IAAgB,EAAc,GAAK,EAAE,QAAK,CAAC,IAAI;GACxD,EAAA,CAAG;GAEJ,MAAa,YACZ,kBAAC,GAAD;IACS;IACG;IACA;IACG;IACE;IACC;IACL;IACX,gBAAgB;IACR;IACR,cAAc;IACA;IACd,gBAAgB;GACjB,CAAA;GAGF,MAAa,aACZ,kBAAC,GAAD;IAAU,UAAU,kBAAC,OAAD;KAAK,WAAW,EAAO;KAAS,aAAU;IAAQ,CAAA;IACpE,UAAA,kBAAC,GAAD;KACS;KACU;KACJ;KACb,gBAAgB;KACH;KACE;KACH;KACE;IACf,CAAA;GACO,CAAA;GAGX,MAAa,WACZ,kBAAC,GAAD;IACS;IACG;IACO;IACP;IACG;IACE;IACC;IACL;IACE;IACb,gBAAgB;IACH;IACD;IACE;IACN;IACR,cAAc;IACA;IACd,gBAAgB;GACjB,CAAA;GAGF,MAAa,cACZ,kBAAC,GAAD;IAAU,UAAU,kBAAC,OAAD;KAAK,WAAW,EAAO;KAAS,aAAU;IAAQ,CAAA;IACpE,UAAA,kBAAC,GAAD;KACS;KACG;KACA;KACG;KACI;KACJ;KACJ;KACM;KACC;KACL;KACE;KACb,gBAAgB;KACR;KACR,cAAc;KACA;KACd,gBAAgB;IACjB,CAAA;GACO,CAAA;EAET;;AAET,CAAC;AAED,EAAM,cAAc"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { ToolbarCommandId } from "../types";
|
|
3
|
+
import type { ToolbarItem } from "./config";
|
|
4
|
+
import type { Btn, Resolved } from "./toolbar-model";
|
|
5
|
+
/**
|
|
6
|
+
* The bar's contents in `overflow: "menu"` mode. Slots lay out flat (no group
|
|
7
|
+
* wrappers) so single buttons can fold; the ones that do not fit go to a
|
|
8
|
+
* trailing menu. A hidden copy of every slot, rendered alongside, is what the
|
|
9
|
+
* fit measures.
|
|
10
|
+
*/
|
|
11
|
+
export declare function OverflowBar({ items, icons, overflowIcon, resolve, run, renderItem, }: {
|
|
12
|
+
items: ToolbarItem[];
|
|
13
|
+
icons?: Partial<Record<ToolbarCommandId, ReactNode>>;
|
|
14
|
+
overflowIcon?: ReactNode;
|
|
15
|
+
resolve: (item: Exclude<ToolbarItem, "|">) => Resolved | null;
|
|
16
|
+
run: (btn: Btn) => void;
|
|
17
|
+
renderItem: (item: Exclude<ToolbarItem, "|">) => ReactNode;
|
|
18
|
+
}): import("react").JSX.Element;
|
|
19
|
+
//# sourceMappingURL=OverflowBar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OverflowBar.d.ts","sourceRoot":"","sources":["../../src/toolbar/OverflowBar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAK3C,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAKpD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,KAAK,EACL,YAAY,EACZ,OAAO,EACP,GAAG,EACH,UAAU,GACX,EAAE;IACD,KAAK,EAAE,WAAW,EAAE,CAAA;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC,CAAA;IACpD,YAAY,CAAC,EAAE,SAAS,CAAA;IACxB,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,KAAK,QAAQ,GAAG,IAAI,CAAA;IAC7D,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IACvB,UAAU,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,KAAK,SAAS,CAAA;CAC3D,+BAoDA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export interface OverflowEntry {
|
|
3
|
+
key: string;
|
|
4
|
+
title: string;
|
|
5
|
+
icon: ReactNode;
|
|
6
|
+
disabled: boolean;
|
|
7
|
+
/** Pressed state of a toggle command; `undefined` when it is not a toggle. */
|
|
8
|
+
active?: boolean;
|
|
9
|
+
run: () => void;
|
|
10
|
+
}
|
|
11
|
+
/** The glyph used for the "more" button when the host passes no `overflowIcon`. */
|
|
12
|
+
export declare const moreGlyph: (icon: ReactNode) => string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined>;
|
|
13
|
+
/**
|
|
14
|
+
* The trailing "more" button and the menu it opens. A button keeps the icon,
|
|
15
|
+
* label, disabled state and action it had in the row. Mouse presses keep the
|
|
16
|
+
* editor focused, like the row's buttons; from the keyboard (Enter / Space on
|
|
17
|
+
* the button) focus moves into the menu: arrows step, Escape closes.
|
|
18
|
+
*/
|
|
19
|
+
export declare function OverflowMenu({ entries, icon, }: {
|
|
20
|
+
/** Menu entries in order; `"|"` is a divider between groups. */
|
|
21
|
+
entries: (OverflowEntry | "|")[];
|
|
22
|
+
icon?: ReactNode;
|
|
23
|
+
}): import("react").JSX.Element;
|
|
24
|
+
//# sourceMappingURL=OverflowMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OverflowMenu.d.ts","sourceRoot":"","sources":["../../src/toolbar/OverflowMenu.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,SAAS,CAAA;IACf,QAAQ,EAAE,OAAO,CAAA;IACjB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,MAAM,IAAI,CAAA;CAChB;AAUD,mFAAmF;AACnF,eAAO,MAAM,SAAS,SAAU,SAAS,6SAAsB,CAAA;AAE/D;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,IAAI,GACL,EAAE;IACD,gEAAgE;IAChE,OAAO,EAAE,CAAC,aAAa,GAAG,GAAG,CAAC,EAAE,CAAA;IAChC,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB,+BAwGA"}
|
|
@@ -16,6 +16,10 @@ export interface ToolbarProps {
|
|
|
16
16
|
sticky?: "top" | "bottom" | false;
|
|
17
17
|
/** Fade the bar out while the editing surface is unfocused (`ToolbarConfig.stickyVisibility`). */
|
|
18
18
|
stickyVisibility?: "consistent" | "dynamic";
|
|
19
|
+
/** `"menu"` folds buttons that do not fit into a trailing menu instead of wrapping. */
|
|
20
|
+
overflow?: "wrap" | "menu";
|
|
21
|
+
/** Glyph for the overflow menu's button. */
|
|
22
|
+
overflowIcon?: ReactNode;
|
|
19
23
|
}
|
|
20
24
|
/**
|
|
21
25
|
* Formatting bar above the editing surface. It holds no document state — each
|
|
@@ -24,5 +28,5 @@ export interface ToolbarProps {
|
|
|
24
28
|
* ids and consumer-supplied {@link ToolbarCustomItem}s render through the same
|
|
25
29
|
* button path.
|
|
26
30
|
*/
|
|
27
|
-
export declare function Toolbar({ view, items, icons, disabled, sticky, stickyVisibility }: ToolbarProps): import("react").JSX.Element;
|
|
31
|
+
export declare function Toolbar({ view, items, icons, disabled, sticky, stickyVisibility, overflow, overflowIcon, }: ToolbarProps): import("react").JSX.Element;
|
|
28
32
|
//# sourceMappingURL=Toolbar.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/toolbar/Toolbar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/toolbar/Toolbar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAIhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAO3C,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,IAAI,EAAE,UAAU,GAAG,IAAI,CAAA;IACvB,qDAAqD;IACrD,KAAK,EAAE,WAAW,EAAE,CAAA;IACpB,uEAAuE;IACvE,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC,CAAA;IACpD,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;iDAC6C;IAC7C,MAAM,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,KAAK,CAAA;IACjC,kGAAkG;IAClG,gBAAgB,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;IAC3C,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC1B,4CAA4C;IAC5C,YAAY,CAAC,EAAE,SAAS,CAAA;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,EACtB,IAAI,EACJ,KAAK,EACL,KAAK,EACL,QAAQ,EACR,MAAM,EACN,gBAAgB,EAChB,QAAQ,EACR,YAAY,GACb,EAAE,YAAY,+BAmJd"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** One slot of the flattened bar, as the overflow fit sees it. */
|
|
2
|
+
export interface FitEntry {
|
|
3
|
+
sep: boolean;
|
|
4
|
+
pinned: boolean;
|
|
5
|
+
/** Measured width, margins included, gap excluded. */
|
|
6
|
+
width: number;
|
|
7
|
+
}
|
|
8
|
+
/** Drops separators that would lead, trail, or sit next to another. */
|
|
9
|
+
export declare function trimSeps<T extends {
|
|
10
|
+
sep: boolean;
|
|
11
|
+
}>(list: T[]): T[];
|
|
12
|
+
/**
|
|
13
|
+
* The slots the overflow menu lists: every folded one, plus each separator
|
|
14
|
+
* with a folded slot on both sides, so the menu keeps the bar's groups.
|
|
15
|
+
*/
|
|
16
|
+
export declare function menuSlots<T extends {
|
|
17
|
+
sep: boolean;
|
|
18
|
+
folded: boolean;
|
|
19
|
+
}>(list: T[]): T[];
|
|
20
|
+
/**
|
|
21
|
+
* Which entries fold into the overflow menu so the rest fit in `available`
|
|
22
|
+
* pixels. Folds from the end, unpinned buttons first; pinned ones only once
|
|
23
|
+
* nothing else is left. `moreWidth` (and one `gap` before it) is reserved once
|
|
24
|
+
* anything has folded; `gap` also separates the slots that stay.
|
|
25
|
+
*/
|
|
26
|
+
export declare function fitEntries(entries: FitEntry[], available: number, moreWidth: number, gap?: number): boolean[];
|
|
27
|
+
//# sourceMappingURL=overflow-fit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overflow-fit.d.ts","sourceRoot":"","sources":["../../src/toolbar/overflow-fit.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,OAAO,CAAA;IACZ,MAAM,EAAE,OAAO,CAAA;IACf,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAA;CACd;AAED,uEAAuE;AACvE,wBAAgB,QAAQ,CAAC,CAAC,SAAS;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAQnE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAIrF;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,QAAQ,EAAE,EACnB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,GAAG,SAAI,GACN,OAAO,EAAE,CAqBX"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { EditorState } from "@codemirror/state";
|
|
3
|
+
import type { EditorView } from "@codemirror/view";
|
|
4
|
+
import type { ToolbarCommandId } from "../types";
|
|
5
|
+
import type { ToolbarItem } from "./config";
|
|
6
|
+
/** A button to render, normalised from a built-in id or a custom item. */
|
|
7
|
+
export interface Btn {
|
|
8
|
+
key: string;
|
|
9
|
+
icon: ReactNode;
|
|
10
|
+
title: string;
|
|
11
|
+
run: (view: EditorView) => unknown;
|
|
12
|
+
isActive?: (state: EditorState) => boolean;
|
|
13
|
+
disabled?: (state: EditorState) => boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Splits `items` into the runs a `"|"` already delimits, keeping each
|
|
17
|
+
* separator as its own entry. A run renders as one flex child so wrapping
|
|
18
|
+
* (on narrow hosts) breaks between groups, never in the middle of one — the
|
|
19
|
+
* default bar's `undo, redo | h1, h2, h3 | ...` shape becomes the wrap unit
|
|
20
|
+
* for free, no separate grouping config needed.
|
|
21
|
+
*/
|
|
22
|
+
export declare function groupItems(items: ToolbarItem[]): (Exclude<ToolbarItem, "|">[] | "|")[];
|
|
23
|
+
/** Resolve one non-separator item to a renderable button, or `null` to skip it. */
|
|
24
|
+
export declare function toButton(item: Exclude<ToolbarItem, "|">, icons: Partial<Record<ToolbarCommandId, ReactNode>> | undefined): Btn | null;
|
|
25
|
+
/** A slot's button plus its live disabled / pressed state. */
|
|
26
|
+
export interface Resolved {
|
|
27
|
+
btn: Btn;
|
|
28
|
+
off: boolean;
|
|
29
|
+
active: boolean;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=toolbar-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolbar-model.d.ts","sourceRoot":"","sources":["../../src/toolbar/toolbar-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAG3C,0EAA0E;AAC1E,MAAM,WAAW,GAAG;IAClB,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,SAAS,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAA;IAClC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAA;IAC1C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAA;CAC3C;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,EAAE,CActF;AAED,mFAAmF;AACnF,wBAAgB,QAAQ,CACtB,IAAI,EAAE,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,EAC/B,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC,GAAG,SAAS,GAC9D,GAAG,GAAG,IAAI,CAeZ;AAED,8DAA8D;AAC9D,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,GAAG,CAAA;IACR,GAAG,EAAE,OAAO,CAAA;IACZ,MAAM,EAAE,OAAO,CAAA;CAChB"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { RefObject } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Decides which slots fold into the overflow menu. `measureRef` is a hidden
|
|
4
|
+
* row inside the bar holding one copy of every slot (same order as `slots`)
|
|
5
|
+
* followed by the "more" button; their rendered widths give the real fit, so
|
|
6
|
+
* it follows fonts, custom glyphs and the sticky bar's larger buttons. Widths
|
|
7
|
+
* come from layout rects, not offsets, so right-to-left pages measure the same.
|
|
8
|
+
* Re-measured before paint when `slots` change, and whenever the bar or the
|
|
9
|
+
* measuring row resizes (a host widening the panel, a glyph or font changing).
|
|
10
|
+
* Returns one flag per slot.
|
|
11
|
+
*/
|
|
12
|
+
export declare function useToolbarOverflow(measureRef: RefObject<HTMLElement | null>, slots: {
|
|
13
|
+
key: string;
|
|
14
|
+
sep: boolean;
|
|
15
|
+
pinned: boolean;
|
|
16
|
+
}[]): boolean[];
|
|
17
|
+
//# sourceMappingURL=use-toolbar-overflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-toolbar-overflow.d.ts","sourceRoot":"","sources":["../../src/toolbar/use-toolbar-overflow.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAWtC;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EACzC,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,EAAE,GACtD,OAAO,EAAE,CAwCX"}
|
package/dist/types/inplace.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { EditorState } from "@codemirror/state";
|
|
2
|
+
import type { EditorView } from "@codemirror/view";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
1
4
|
import type { ToolbarCommandId } from "./toolbar";
|
|
2
5
|
/**
|
|
3
6
|
* Per-construct on/off switches for the in-place canvas. Each key defaults to
|
|
@@ -48,16 +51,56 @@ export type SelectionUI = "menu" | "bar" | "none";
|
|
|
48
51
|
/**
|
|
49
52
|
* The right-click menu's top-level groups. `link` is the internal / external
|
|
50
53
|
* link field rows; `format` the inline-mark submenu; `paragraph` the block-type
|
|
51
|
-
* submenu; `insert` the new-block submenu; `clipboard` cut / copy / paste
|
|
54
|
+
* submenu; `insert` the new-block submenu; `clipboard` cut / copy / paste;
|
|
55
|
+
* `host` the entries the host passes as `contextMenu.items`.
|
|
52
56
|
*/
|
|
53
|
-
export type MenuGroupId = "link" | "format" | "paragraph" | "insert" | "clipboard";
|
|
57
|
+
export type MenuGroupId = "host" | "link" | "format" | "paragraph" | "insert" | "clipboard";
|
|
58
|
+
/**
|
|
59
|
+
* A host-supplied right-click entry (`contextMenu.items`), the menu's parallel
|
|
60
|
+
* of a custom toolbar button. `run` gets the live view with the selection as it
|
|
61
|
+
* was when the menu opened — inside a table cell too, where the selected text
|
|
62
|
+
* is mapped back to its range in the document first.
|
|
63
|
+
*/
|
|
64
|
+
export interface ContextMenuItem {
|
|
65
|
+
/** Stable identity; exposed on the row as `data-menu-item`. */
|
|
66
|
+
id: string;
|
|
67
|
+
/** The row's label. */
|
|
68
|
+
title: string;
|
|
69
|
+
/**
|
|
70
|
+
* A leading glyph: stroke-path data like the built-in rows (`|` separates
|
|
71
|
+
* paths), or any element that renders to an SVG. An element is rendered once
|
|
72
|
+
* and copied as static markup, so it cannot hold state or handlers.
|
|
73
|
+
*/
|
|
74
|
+
icon?: string | ReactNode;
|
|
75
|
+
run: (view: EditorView) => void;
|
|
76
|
+
/** Greyed and not selectable while true. Read each time the menu opens. */
|
|
77
|
+
disabled?: (state: EditorState) => boolean;
|
|
78
|
+
/**
|
|
79
|
+
* `"selection"` shows the item only with a non-empty selection, `"no-selection"`
|
|
80
|
+
* only without one. Defaults to `"always"`.
|
|
81
|
+
*/
|
|
82
|
+
when?: "selection" | "no-selection" | "always";
|
|
83
|
+
/**
|
|
84
|
+
* Offer the item in a read-only note. Without it the item is hidden there,
|
|
85
|
+
* like every built-in row. Use it only for an item that does not edit the
|
|
86
|
+
* document (a comment kept apart from the text).
|
|
87
|
+
*/
|
|
88
|
+
readOnlySafe?: boolean;
|
|
89
|
+
}
|
|
54
90
|
export interface ContextMenuConfig {
|
|
55
91
|
/**
|
|
56
|
-
* Which top-level groups the menu shows, in order. Omit for all
|
|
57
|
-
* default order. `link` and `format` still yield to
|
|
58
|
-
* not `"menu"` (they move to the floating bar /
|
|
92
|
+
* Which top-level groups the menu shows, in order. Omit for all six in their
|
|
93
|
+
* default order (`host` first). `link` and `format` still yield to
|
|
94
|
+
* `selectionUI` when it is not `"menu"` (they move to the floating bar /
|
|
95
|
+
* toolbar). A list without `"host"` hides the host items.
|
|
59
96
|
*/
|
|
60
97
|
groups?: MenuGroupId[];
|
|
98
|
+
/**
|
|
99
|
+
* The host's own entries, drawn as the `host` group. Unlike the rest of
|
|
100
|
+
* `inPlace`, read each time the menu opens, so a re-render's `run` closures
|
|
101
|
+
* apply.
|
|
102
|
+
*/
|
|
103
|
+
items?: ContextMenuItem[];
|
|
61
104
|
}
|
|
62
105
|
export interface InPlaceConfig {
|
|
63
106
|
/** Which decoration types the in-place canvas renders. Read once, at mount. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inplace.d.ts","sourceRoot":"","sources":["../../src/types/inplace.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAEjD;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,OAAO,CAAA;AAE7C;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,OAAO,CAAA;AAE1C;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAA;AAEjD
|
|
1
|
+
{"version":3,"file":"inplace.d.ts","sourceRoot":"","sources":["../../src/types/inplace.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAEjD;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,OAAO,CAAA;AAE7C;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,OAAO,CAAA;AAE1C;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAA;AAEjD;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAA;AAE3F;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,EAAE,EAAE,MAAM,CAAA;IACV,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,GAAG,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAA;IAC/B,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAA;IAC1C;;;OAGG;IACH,IAAI,CAAC,EAAE,WAAW,GAAG,cAAc,GAAG,QAAQ,CAAA;IAC9C;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,WAAW,EAAE,CAAA;IACtB;;;;OAIG;IACH,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,+EAA+E;IAC/E,WAAW,CAAC,EAAE,wBAAwB,CAAA;IACtC,oEAAoE;IACpE,KAAK,CAAC,EAAE,YAAY,CAAA;IACpB;;;OAGG;IACH,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,iBAAiB,CAAA;IACzC;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,gBAAgB,EAAE,CAAA;CACvC"}
|
package/dist/types/toolbar.d.ts
CHANGED
|
@@ -32,6 +32,12 @@ export interface ToolbarCustomItem {
|
|
|
32
32
|
isActive?: (state: EditorState) => boolean;
|
|
33
33
|
/** When true, the button is rendered `disabled`. */
|
|
34
34
|
disabled?: (state: EditorState) => boolean;
|
|
35
|
+
/**
|
|
36
|
+
* With `toolbar.overflow: "menu"`, a pinned item is the last to fold into the
|
|
37
|
+
* overflow menu — for the buttons a user must always reach (Save). Ignored
|
|
38
|
+
* in the default wrapping mode.
|
|
39
|
+
*/
|
|
40
|
+
pinned?: boolean;
|
|
35
41
|
}
|
|
36
42
|
/** One rendered slot: a built-in id, a `"|"` separator, or a custom button. */
|
|
37
43
|
export type ToolbarItem = ToolbarCommandId | "|" | ToolbarCustomItem;
|
|
@@ -68,5 +74,15 @@ export interface ToolbarConfig {
|
|
|
68
74
|
* content while the caret is elsewhere. Ignored when `sticky` is off.
|
|
69
75
|
*/
|
|
70
76
|
stickyVisibility?: "consistent" | "dynamic";
|
|
77
|
+
/**
|
|
78
|
+
* What the bar does when its buttons do not fit. `"wrap"` (the default)
|
|
79
|
+
* breaks onto further lines between groups. `"menu"` keeps one row: the
|
|
80
|
+
* buttons that fit stay, the rest fold in order into a trailing "more"
|
|
81
|
+
* button that opens them as a menu. Re-measured whenever the bar's width or
|
|
82
|
+
* its items change.
|
|
83
|
+
*/
|
|
84
|
+
overflow?: "wrap" | "menu";
|
|
85
|
+
/** The "more" button's glyph when `overflow` is `"menu"`. Defaults to `⋯`. */
|
|
86
|
+
overflowIcon?: ReactNode;
|
|
71
87
|
}
|
|
72
88
|
//# sourceMappingURL=toolbar.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolbar.d.ts","sourceRoot":"","sources":["../../src/types/toolbar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,MAAM,GACN,MAAM,GACN,QAAQ,GACR,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,MAAM,GACN,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,MAAM,GACN,WAAW,GACX,MAAM,GACN,UAAU,GACV,OAAO,GACP,YAAY,GACZ,aAAa,GACb,MAAM,GACN,IAAI,GACJ,aAAa,GACb,OAAO,GACP,MAAM,GACN,WAAW,CAAA;AAEf;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,EAAE,EAAE,MAAM,CAAA;IACV,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,IAAI,EAAE,SAAS,CAAA;IACf,8DAA8D;IAC9D,GAAG,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAA;IAC/B,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAA;IAC1C,oDAAoD;IACpD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"toolbar.d.ts","sourceRoot":"","sources":["../../src/types/toolbar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,MAAM,GACN,MAAM,GACN,QAAQ,GACR,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,MAAM,GACN,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,MAAM,GACN,WAAW,GACX,MAAM,GACN,UAAU,GACV,OAAO,GACP,YAAY,GACZ,aAAa,GACb,MAAM,GACN,IAAI,GACJ,aAAa,GACb,OAAO,GACP,MAAM,GACN,WAAW,CAAA;AAEf;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,EAAE,EAAE,MAAM,CAAA;IACV,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,IAAI,EAAE,SAAS,CAAA;IACf,8DAA8D;IAC9D,GAAG,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAA;IAC/B,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAA;IAC1C,oDAAoD;IACpD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAA;IAC1C;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,+EAA+E;AAC/E,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG,GAAG,GAAG,iBAAiB,CAAA;AAEpE,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,KAAK,CAAC,EAAE,WAAW,EAAE,CAAA;IACrB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE;QAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAAA;KAAE,KAAK,SAAS,CAAA;IACxE;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAA;IACnC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC1B,8EAA8E;IAC9E,YAAY,CAAC,EAAE,SAAS,CAAA;CACzB"}
|
package/dist/types.d.ts
CHANGED
|
@@ -9,5 +9,5 @@ export type { StyloMode, FrontmatterDisplay, CodeLanguages, TaskToggleInfo, Styl
|
|
|
9
9
|
export type { ToolbarCommandId, ToolbarCustomItem, ToolbarItem, ToolbarConfig, } from "./types/toolbar";
|
|
10
10
|
export type { WikiLinkCompletion, WikiLinkSource, TagCompletion, TagSource } from "./types/sources";
|
|
11
11
|
export type { EmbedSource, ResolveErrorInfo } from "./types/sources";
|
|
12
|
-
export type { InPlaceDecorationToggles, TableEditing, RevealMode, SelectionUI, MenuGroupId, ContextMenuConfig, InPlaceConfig, } from "./types/inplace";
|
|
12
|
+
export type { InPlaceDecorationToggles, TableEditing, RevealMode, SelectionUI, MenuGroupId, ContextMenuConfig, ContextMenuItem, InPlaceConfig, } from "./types/inplace";
|
|
13
13
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,YAAY,EACV,SAAS,EACT,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,UAAU,EACV,WAAW,GACZ,MAAM,cAAc,CAAA;AACrB,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,aAAa,GACd,MAAM,iBAAiB,CAAA;AACxB,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AACnG,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACpE,YAAY,EACV,wBAAwB,EACxB,YAAY,EACZ,UAAU,EACV,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,aAAa,GACd,MAAM,iBAAiB,CAAA"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,YAAY,EACV,SAAS,EACT,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,UAAU,EACV,WAAW,GACZ,MAAM,cAAc,CAAA;AACrB,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,aAAa,GACd,MAAM,iBAAiB,CAAA;AACxB,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AACnG,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACpE,YAAY,EACV,wBAAwB,EACxB,YAAY,EACZ,UAAU,EACV,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,aAAa,GACd,MAAM,iBAAiB,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { l as e, o as t, s as n } from "./embed-cache-
|
|
1
|
+
import { l as e, o as t, s as n } from "./embed-cache-BU9Smn-h.js";
|
|
2
2
|
import { useEffect as r, useRef as i } from "react";
|
|
3
3
|
import { EditorView as a, keymap as o, placeholder as s, runScopeHandlers as c } from "@codemirror/view";
|
|
4
4
|
import { Annotation as l, ChangeSet as u, Compartment as d, EditorSelection as f, EditorState as p, Facet as ee, Prec as m } from "@codemirror/state";
|
|
@@ -1590,4 +1590,4 @@ function cn({ value: e, onChange: t, readOnly: n = !1, placeholder: s, onSave: c
|
|
|
1590
1590
|
//#endregion
|
|
1591
1591
|
export { je as C, he as D, me as E, fe as O, Pe as S, ke as T, Re as _, X as a, M as b, wt as c, B as d, V as f, Fe as g, I as h, St as i, de as k, Ot as l, He as m, qt as n, Ct as o, Ke as p, Mt as r, xt as s, cn as t, ft as u, De as v, Oe as w, Ae as x, Me as y };
|
|
1592
1592
|
|
|
1593
|
-
//# sourceMappingURL=useCodeMirror-
|
|
1593
|
+
//# sourceMappingURL=useCodeMirror-Fn6qEfMx.js.map
|