@damiro/stylo 0.15.2 → 0.17.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 +1 -1
- package/dist/{InPlaceView-DVX9qV68.js → InPlaceView-CRb5AkfX.js} +781 -743
- package/dist/InPlaceView-CRb5AkfX.js.map +1 -0
- package/dist/Preview-5lm2uUVj.js +319 -0
- package/dist/Preview-5lm2uUVj.js.map +1 -0
- package/dist/SplitView.d.ts +4 -1
- package/dist/SplitView.d.ts.map +1 -1
- package/dist/Stylo.d.ts.map +1 -1
- package/dist/editor/SourceView.d.ts +4 -1
- package/dist/editor/SourceView.d.ts.map +1 -1
- package/dist/editor/theme.d.ts.map +1 -1
- package/dist/editor/useCodeMirror.d.ts +7 -1
- package/dist/editor/useCodeMirror.d.ts.map +1 -1
- package/dist/embed-DKKeCE1Y.js +109 -0
- package/dist/embed-DKKeCE1Y.js.map +1 -0
- package/dist/{highlight-BLdNzTxQ.js → embed-cache-CcBfVrc9.js} +47 -4
- package/dist/embed-cache-CcBfVrc9.js.map +1 -0
- package/dist/inplace/InPlaceView.d.ts +4 -1
- package/dist/inplace/InPlaceView.d.ts.map +1 -1
- package/dist/inplace/context-menu-helpers.d.ts +3 -4
- package/dist/inplace/context-menu-helpers.d.ts.map +1 -1
- package/dist/inplace/decorate.d.ts.map +1 -1
- package/dist/inplace/edit-line-prefix.d.ts.map +1 -1
- package/dist/inplace/embed.d.ts.map +1 -1
- package/dist/inplace/extension.d.ts.map +1 -1
- package/dist/inplace/inline-md.d.ts.map +1 -1
- package/dist/inplace/link-row.d.ts +14 -13
- package/dist/inplace/link-row.d.ts.map +1 -1
- package/dist/inplace/list-guides.d.ts.map +1 -1
- package/dist/inplace/math.d.ts +18 -3
- package/dist/inplace/math.d.ts.map +1 -1
- package/dist/inplace/table-widget-keys.d.ts +46 -0
- package/dist/inplace/table-widget-keys.d.ts.map +1 -0
- package/dist/inplace/table-widget.d.ts +1 -2
- package/dist/inplace/table-widget.d.ts.map +1 -1
- package/dist/inplace/tables.d.ts.map +1 -1
- package/dist/inplace/wikilinks.d.ts +1 -1
- package/dist/inplace/wikilinks.d.ts.map +1 -1
- package/dist/render/Embed.d.ts.map +1 -1
- package/dist/render/Preview.d.ts.map +1 -1
- package/dist/render/embed-cache.d.ts +13 -0
- package/dist/render/embed-cache.d.ts.map +1 -1
- package/dist/render/preview-components.d.ts +22 -0
- package/dist/render/preview-components.d.ts.map +1 -0
- package/dist/render/remark-wikilink.d.ts.map +1 -1
- package/dist/render/taskCheckbox.d.ts +30 -0
- package/dist/render/taskCheckbox.d.ts.map +1 -1
- package/dist/stylo.js +213 -196
- package/dist/stylo.js.map +1 -1
- package/dist/toolbar/Toolbar.d.ts.map +1 -1
- package/dist/toolbar/command-helpers.d.ts +27 -0
- package/dist/toolbar/command-helpers.d.ts.map +1 -1
- package/dist/toolbar/heading.d.ts +3 -0
- package/dist/toolbar/heading.d.ts.map +1 -1
- package/dist/toolbar/inline-ops.d.ts +6 -0
- package/dist/toolbar/inline-ops.d.ts.map +1 -1
- package/dist/toolbar/table.d.ts.map +1 -1
- package/dist/types/core.d.ts +209 -0
- package/dist/types/core.d.ts.map +1 -0
- package/dist/types/inplace.d.ts +91 -0
- package/dist/types/inplace.d.ts.map +1 -0
- package/dist/types/sources.d.ts +66 -0
- package/dist/types/sources.d.ts.map +1 -0
- package/dist/types/toolbar.d.ts +72 -0
- package/dist/types/toolbar.d.ts.map +1 -0
- package/dist/types.d.ts +11 -406
- package/dist/types.d.ts.map +1 -1
- package/dist/{useCodeMirror-CXtuBlLf.js → useCodeMirror-B07wRexo.js} +317 -268
- package/dist/useCodeMirror-B07wRexo.js.map +1 -0
- package/package.json +4 -4
- package/dist/InPlaceView-DVX9qV68.js.map +0 -1
- package/dist/Preview-BcdSr09-.js +0 -304
- package/dist/Preview-BcdSr09-.js.map +0 -1
- package/dist/embed-Dsu6b-MV.js +0 -131
- package/dist/embed-Dsu6b-MV.js.map +0 -1
- package/dist/highlight-BLdNzTxQ.js.map +0 -1
- package/dist/inplace/table-widget-caret.d.ts +0 -11
- package/dist/inplace/table-widget-caret.d.ts.map +0 -1
- package/dist/useCodeMirror-CXtuBlLf.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 { 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 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}: 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 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 { 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 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}: 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 />\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 { 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 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 const off = disabled || !view || Boolean(btn.disabled?.(view.state))\n const active = Boolean(!off && view && btn.isActive?.(view.state))\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 \"./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 },\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 toolbarRender = toolbar && typeof toolbar === \"object\" ? toolbar.render : undefined\n const stickyConfig = toolbar && typeof toolbar === \"object\" ? toolbar.sticky : undefined\n const stickyToolbar: \"top\" | \"bottom\" | false =\n stickyConfig === \"top\" ? \"top\" : stickyConfig ? \"bottom\" : false\n const stickyVisibility =\n toolbar && typeof toolbar === \"object\" ? toolbar.stickyVisibility : undefined\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 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 }),\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 />\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 />\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 />\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/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 +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;AAEtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,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;AAEtC,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;AAK3C,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;CAC5C;AAqDD;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,EAAE,YAAY,+BAyH/F"}
|
|
@@ -46,6 +46,10 @@ export interface ToolbarCommand {
|
|
|
46
46
|
/** Default key bindings in CodeMirror `key` syntax. */
|
|
47
47
|
keys?: string[];
|
|
48
48
|
}
|
|
49
|
+
/** Any list marker — bullet, ordered, or task — with `[1]` capturing the
|
|
50
|
+
* indent. Ordered accepts both `1.` and `1)`, per CommonMark's actual
|
|
51
|
+
* grammar — shared with `ORDERED.match` below so the two can't drift again. */
|
|
52
|
+
export declare const LIST_MARKER: RegExp;
|
|
49
53
|
export declare const QUOTE: LinePrefixSpec;
|
|
50
54
|
export declare const BULLET: LinePrefixSpec;
|
|
51
55
|
export declare const ORDERED: LinePrefixSpec;
|
|
@@ -53,5 +57,28 @@ export declare const TASK: LinePrefixSpec;
|
|
|
53
57
|
export declare function history(id: "undo" | "redo", title: string, cmd: Command): ToolbarCommand;
|
|
54
58
|
export declare function wrap(id: ToolbarCommandId, title: string, mark: string, keys?: string[]): ToolbarCommand;
|
|
55
59
|
export declare function heading(level: 1 | 2 | 3): ToolbarCommand;
|
|
60
|
+
/**
|
|
61
|
+
* Ids whose `run` already degrades to an inline edit inside a table cell (via
|
|
62
|
+
* `runInlineInCell`) — the only commands still usable while the DOM caret is
|
|
63
|
+
* actually focused in one. Shared by the toolbar and the context menu so a
|
|
64
|
+
* table cell's "what works here" answer can't drift between the two.
|
|
65
|
+
*/
|
|
66
|
+
export declare const CELL_CAPABLE_IDS: ReadonlySet<ToolbarCommandId>;
|
|
67
|
+
/**
|
|
68
|
+
* A command's effective `disabled` / `isActive` for the current DOM focus.
|
|
69
|
+
* `state.selection` never moves into an editable table cell's `contentEditable`
|
|
70
|
+
* DOM — a click there is deliberately left undispatched (see `extension.ts`) —
|
|
71
|
+
* so while the caret is actually inside one, checking `cmd.disabled?.(state)` /
|
|
72
|
+
* `cmd.isActive?.(state)` reads a selection that may point anywhere else in
|
|
73
|
+
* the document. There: a cell-capable command stays live, always reported as
|
|
74
|
+
* neither active nor disabled (`run` degrades to an inline edit on the cell
|
|
75
|
+
* regardless of the model selection, and detecting an existing mark inside the
|
|
76
|
+
* cell's own text isn't implemented); every other command is disabled outright
|
|
77
|
+
* rather than acting on that stale selection.
|
|
78
|
+
*/
|
|
79
|
+
export declare function resolveForCell(state: EditorState, cmd: Pick<ToolbarCommand, "id" | "disabled" | "isActive">, inCell: boolean): {
|
|
80
|
+
disabled: boolean;
|
|
81
|
+
active: boolean;
|
|
82
|
+
};
|
|
56
83
|
export declare function prefix(id: ToolbarCommandId, title: string, spec: LinePrefixSpec, ...extra: ((s: EditorState) => boolean)[]): ToolbarCommand;
|
|
57
84
|
//# sourceMappingURL=command-helpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-helpers.d.ts","sourceRoot":"","sources":["../../src/toolbar/command-helpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,EAAsC,KAAK,cAAc,EAAE,MAAM,SAAS,CAAA;AAUjF,uDAAuD;AACvD,eAAO,MAAM,SAAS,MAAO,WAAW,KAAG,OAC8B,CAAA;AAEzE,oDAAoD;AACpD,eAAO,MAAM,aAAa,MAAO,WAAW,KAAG,OAG9C,CAAA;AAED,yEAAyE;AACzE,eAAO,MAAM,SAAS,MAAO,WAAW,KAAG,OACoB,CAAA;AAE/D;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,SACxB,MAAM,SACT,WAAW,KAAG,OAC4D,CAAA;AAEhF;;;;GAIG;AACH,eAAO,MAAM,aAAa,MAAO,WAAW,KAAG,OACa,CAAA;AAE5D,sEAAsE;AACtE,eAAO,MAAM,YAAY,cACX,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,EAAE,SACvC,WAAW,KAAG,OACQ,CAAA;AAE5B,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,gBAAgB,CAAA;IACpB,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,GAAG,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAA;IAClC,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAA;IAC1C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAA;IAC1C,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAChB;
|
|
1
|
+
{"version":3,"file":"command-helpers.d.ts","sourceRoot":"","sources":["../../src/toolbar/command-helpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,EAAsC,KAAK,cAAc,EAAE,MAAM,SAAS,CAAA;AAUjF,uDAAuD;AACvD,eAAO,MAAM,SAAS,MAAO,WAAW,KAAG,OAC8B,CAAA;AAEzE,oDAAoD;AACpD,eAAO,MAAM,aAAa,MAAO,WAAW,KAAG,OAG9C,CAAA;AAED,yEAAyE;AACzE,eAAO,MAAM,SAAS,MAAO,WAAW,KAAG,OACoB,CAAA;AAE/D;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,SACxB,MAAM,SACT,WAAW,KAAG,OAC4D,CAAA;AAEhF;;;;GAIG;AACH,eAAO,MAAM,aAAa,MAAO,WAAW,KAAG,OACa,CAAA;AAE5D,sEAAsE;AACtE,eAAO,MAAM,YAAY,cACX,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,EAAE,SACvC,WAAW,KAAG,OACQ,CAAA;AAE5B,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,gBAAgB,CAAA;IACpB,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,GAAG,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAA;IAClC,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAA;IAC1C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAA;IAC1C,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAChB;AAED;;gFAEgF;AAChF,eAAO,MAAM,WAAW,QAAkD,CAAA;AAE1E,eAAO,MAAM,KAAK,EAAE,cAAsD,CAAA;AAC1E,eAAO,MAAM,MAAM,EAAE,cAIpB,CAAA;AACD,eAAO,MAAM,OAAO,EAAE,cAIrB,CAAA;AACD,eAAO,MAAM,IAAI,EAAE,cAIlB,CAAA;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,cAAc,CAUxF;AAED,wBAAgB,IAAI,CAClB,EAAE,EAAE,gBAAgB,EACpB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,EAAE,GACd,cAAc,CAUhB;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,cAAc,CAUxD;AAED;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,gBAAgB,CAWzD,CAAA;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,GAAG,UAAU,GAAG,UAAU,CAAC,EACzD,MAAM,EAAE,OAAO,GACd;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAUxC;AAED,wBAAgB,MAAM,CACpB,EAAE,EAAE,gBAAgB,EACpB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,cAAc,EACpB,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,EAAE,GACxC,cAAc,CAQhB"}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { EditorView } from "@codemirror/view";
|
|
2
|
+
/** An ATX heading's `#` … `###### ` prefix — the single source of truth for
|
|
3
|
+
* the toolbar and the in-place canvas's Backspace-unwrap alike. */
|
|
4
|
+
export declare const HEADING_PREFIX: RegExp;
|
|
2
5
|
/**
|
|
3
6
|
* Set, swap, or clear an ATX heading prefix on every line the selection touches.
|
|
4
7
|
* A line already at `level` is cleared; any other level (or none) is set to it.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heading.d.ts","sourceRoot":"","sources":["../../src/toolbar/heading.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAGlD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAmBtE;AAED;iFACiF;AACjF,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAatD"}
|
|
1
|
+
{"version":3,"file":"heading.d.ts","sourceRoot":"","sources":["../../src/toolbar/heading.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAGlD;oEACoE;AACpE,eAAO,MAAM,cAAc,QAAgB,CAAA;AAE3C;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAmBtE;AAED;iFACiF;AACjF,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAatD"}
|
|
@@ -26,6 +26,12 @@ export interface InlineStr {
|
|
|
26
26
|
* to)` of `text`: unwrap a span already wrapped (marks inside or just around the
|
|
27
27
|
* selection), else wrap it; an empty range gets an empty pair. Applying `*` to
|
|
28
28
|
* `**bold**` (or `**` to `*em*`) nests rather than eating a marker.
|
|
29
|
+
*
|
|
30
|
+
* A collapsed `[from, to)` (a bare caret, no selection) sitting inside a word
|
|
31
|
+
* expands to that whole word first — otherwise the two inserts below would
|
|
32
|
+
* land at the very same point and split the word in two (`wo****rd` instead
|
|
33
|
+
* of `**word**`). `nothingToWrap` (`command-helpers.ts`) already disables the
|
|
34
|
+
* button/shortcut when there is no word to expand to.
|
|
29
35
|
*/
|
|
30
36
|
export declare function wrapOp(text: string, from: number, to: number, mark: string): InlineOp;
|
|
31
37
|
/** Apply non-overlapping {@link InlineOp} changes to a string (right to left). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inline-ops.d.ts","sourceRoot":"","sources":["../../src/toolbar/inline-ops.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC9D,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAChE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAE9E,kEAAkE;AAClE,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAErC;AAED,mEAAmE;AACnE,eAAO,MAAM,UAAU,SAAS,CAAA;AAsChC,kFAAkF;AAClF,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACvD,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;CACX;AAED,mEAAmE;AACnE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;CACX;
|
|
1
|
+
{"version":3,"file":"inline-ops.d.ts","sourceRoot":"","sources":["../../src/toolbar/inline-ops.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC9D,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAChE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAE9E,kEAAkE;AAClE,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAErC;AAED,mEAAmE;AACnE,eAAO,MAAM,UAAU,SAAS,CAAA;AAsChC,kFAAkF;AAClF,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACvD,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;CACX;AAED,mEAAmE;AACnE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;CACX;AAiBD;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,CAoErF;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,GACtD,MAAM,CAMR;AAED,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAG1F;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAkB9F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/toolbar/table.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAE3F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAyClD,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAiBrD;AAED,0DAA0D;AAC1D,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAEvD;AA2CD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,EAAE,SAIxB,CAAA;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/toolbar/table.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAE3F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAyClD,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAiBrD;AAED,0DAA0D;AAC1D,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAEvD;AA2CD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,EAAE,SAIxB,CAAA;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,SAyBzB,CAAA"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { Language, LanguageDescription } from "@codemirror/language";
|
|
3
|
+
import type { Extension } from "@codemirror/state";
|
|
4
|
+
import type { EditorView } from "@codemirror/view";
|
|
5
|
+
import type { EmbedSource, ResolveErrorInfo, TagSource, WikiLinkSource } from "./sources";
|
|
6
|
+
import type { InPlaceConfig } from "./inplace";
|
|
7
|
+
import type { ToolbarCommandId, ToolbarConfig } from "./toolbar";
|
|
8
|
+
export type StyloMode = "in-place" | "source" | "preview" | "split";
|
|
9
|
+
/**
|
|
10
|
+
* How `preview` (and the preview pane of `split`) treats the leading `---` YAML
|
|
11
|
+
* block. `"hidden"` (default) drops it from the render; `"code"` renders it as a
|
|
12
|
+
* styled `<pre class="stylo-frontmatter">` a consumer can restyle with its own
|
|
13
|
+
* CSS. A parsed key/value panel is deferred (ADR-001, needs a YAML parser).
|
|
14
|
+
*/
|
|
15
|
+
export type FrontmatterDisplay = "hidden" | "code";
|
|
16
|
+
/**
|
|
17
|
+
* Grammars for fenced-code sub-highlighting. Stylo ships none by default — a
|
|
18
|
+
* consumer opts in with exactly the set they want (`codeLanguages={languages}`
|
|
19
|
+
* from `@codemirror/language-data`, or a hand-built list). Forwarded verbatim
|
|
20
|
+
* to `@codemirror/lang-markdown` for the CodeMirror surfaces (`source`,
|
|
21
|
+
* `split`, `in-place`); `preview` resolves a fence's language the same way
|
|
22
|
+
* (same fuzzy `LanguageDescription` match, or the function form called with
|
|
23
|
+
* the same name) and colours it with the same `--stylo-syntax-*` tokens, so a
|
|
24
|
+
* block reads identically whether it's being read or edited. See the ADR-001
|
|
25
|
+
* amendment and ADR-002 §3's 2026-09-13 amendment.
|
|
26
|
+
*/
|
|
27
|
+
export type CodeLanguages = readonly LanguageDescription[] | ((info: string) => Language | LanguageDescription | null);
|
|
28
|
+
/**
|
|
29
|
+
* Reported by `onTaskToggle` when a `preview` task-list checkbox is clicked.
|
|
30
|
+
* `start`/`end` are raw offsets into `value` bracketing the `[ ]` / `[x]`
|
|
31
|
+
* marker itself (`end - start === 3`); splice in the new marker text to apply
|
|
32
|
+
* the toggle: `value.slice(0, start) + (checked ? "[x]" : "[ ]") +
|
|
33
|
+
* value.slice(end)`. `checked` is the box's new state, not its state before
|
|
34
|
+
* the click.
|
|
35
|
+
*/
|
|
36
|
+
export interface TaskToggleInfo {
|
|
37
|
+
/** Offset of the marker's opening `[` in `value`. */
|
|
38
|
+
start: number;
|
|
39
|
+
/** Offset just past the marker's closing `]` in `value`. */
|
|
40
|
+
end: number;
|
|
41
|
+
/** The checkbox's new state. */
|
|
42
|
+
checked: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface StyloProps {
|
|
45
|
+
/** The canonical Markdown document. Stylo never holds a parsed model of it. */
|
|
46
|
+
value: string;
|
|
47
|
+
/** Called with the complete Markdown string on every edit. */
|
|
48
|
+
onChange: (next: string) => void;
|
|
49
|
+
/**
|
|
50
|
+
* Interaction layout. Defaults to `"in-place"` — the live decoration canvas.
|
|
51
|
+
* `"source"` is the plain surface and avoids loading the render chunk.
|
|
52
|
+
*/
|
|
53
|
+
mode?: StyloMode;
|
|
54
|
+
/**
|
|
55
|
+
* Called with the full Markdown string when `Mod-s` is pressed on any editing
|
|
56
|
+
* surface; the browser's own save dialog is then suppressed. Omit it and
|
|
57
|
+
* `Mod-s` keeps its default browser behaviour. Stylo holds no dirty state —
|
|
58
|
+
* `value` is yours, so compare it against your last-saved copy.
|
|
59
|
+
*/
|
|
60
|
+
onSave?: (value: string) => void;
|
|
61
|
+
/**
|
|
62
|
+
* Called on mount and whenever the leading `---` YAML block changes, with its
|
|
63
|
+
* inner text (no fences), or `null` when there is no block. Stylo does not
|
|
64
|
+
* parse it — pass `raw` to your own YAML parser for a structured panel. The
|
|
65
|
+
* same split is available synchronously as the exported `splitFrontmatter`.
|
|
66
|
+
*/
|
|
67
|
+
onFrontmatter?: (raw: string | null) => void;
|
|
68
|
+
/** Invoked when a `[[wikilink]]` is activated in the preview or in-place canvas. */
|
|
69
|
+
onWikiLinkClick?: (target: string) => void;
|
|
70
|
+
/**
|
|
71
|
+
* Invoked by the in-place link editor's "Open link" action with the link's
|
|
72
|
+
* `href`. Stylo does not navigate on its own.
|
|
73
|
+
*/
|
|
74
|
+
onLinkClick?: (href: string) => void;
|
|
75
|
+
/** Configures the in-place canvas (ADR-005). Applied when it mounts. */
|
|
76
|
+
inPlace?: InPlaceConfig;
|
|
77
|
+
/**
|
|
78
|
+
* How `preview` (and `split`'s preview pane) shows the leading `---` YAML
|
|
79
|
+
* block. `"hidden"` (default) drops it; `"code"` renders it as a styled
|
|
80
|
+
* `<pre class="stylo-frontmatter">`.
|
|
81
|
+
*/
|
|
82
|
+
frontmatter?: FrontmatterDisplay;
|
|
83
|
+
/**
|
|
84
|
+
* Turn a single line ending into a real `<br>` in `preview` (and `split`'s
|
|
85
|
+
* preview pane), instead of CommonMark's default — a blank line required to
|
|
86
|
+
* start a new paragraph, so consecutive lines join into one run. Obsidian's
|
|
87
|
+
* Live Preview reads this way. Off by default: a real, visible change to
|
|
88
|
+
* every existing render, so a host opts in deliberately rather than
|
|
89
|
+
* inheriting it. No effect on `in-place` / `source` — CodeMirror already
|
|
90
|
+
* decorates each source line independently there.
|
|
91
|
+
*/
|
|
92
|
+
softBreaks?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Makes `preview` (and `split`'s preview pane) task-list checkboxes
|
|
95
|
+
* clickable instead of `disabled`. Fired with a {@link TaskToggleInfo} — raw
|
|
96
|
+
* offsets into `value` bracketing the clicked `[ ]` / `[x]` marker, and its
|
|
97
|
+
* new state. Stylo never mutates `value` itself; splice the marker and pass
|
|
98
|
+
* the result to `onChange`, same division of labour as `onWikiLinkClick`.
|
|
99
|
+
* Off by default: every checkbox stays `disabled`, exactly like today,
|
|
100
|
+
* until a host opts in.
|
|
101
|
+
*/
|
|
102
|
+
onTaskToggle?: (info: TaskToggleInfo) => void;
|
|
103
|
+
/**
|
|
104
|
+
* Grammars for fenced-code sub-highlighting on the CodeMirror surfaces
|
|
105
|
+
* (`source`, `split`, `in-place`). None by default. Read once, at mount.
|
|
106
|
+
*/
|
|
107
|
+
codeLanguages?: CodeLanguages;
|
|
108
|
+
/**
|
|
109
|
+
* Enables `[[wikilink]]` autocomplete on the CodeMirror surfaces. Called with
|
|
110
|
+
* the target typed so far; return your index's matches, ordered. Off when
|
|
111
|
+
* omitted. Read once, at mount. See `WikiLinkSource`.
|
|
112
|
+
*/
|
|
113
|
+
wikiLinkSource?: WikiLinkSource;
|
|
114
|
+
/**
|
|
115
|
+
* Enables `#tag` autocomplete on the CodeMirror surfaces. Called with the tag
|
|
116
|
+
* typed so far; return your index's matches, ordered. Off when omitted. Read
|
|
117
|
+
* once, at mount. See `TagSource`.
|
|
118
|
+
*/
|
|
119
|
+
tagSource?: TagSource;
|
|
120
|
+
/**
|
|
121
|
+
* Resolves `![[ref]]` embeds (transclusion) for `preview` and `split`. Called
|
|
122
|
+
* with the raw reference; return a node to render in its place, or `null` to
|
|
123
|
+
* leave it as literal text. May be async. Off when omitted. See
|
|
124
|
+
* {@link EmbedSource}.
|
|
125
|
+
*/
|
|
126
|
+
embedSource?: EmbedSource;
|
|
127
|
+
/**
|
|
128
|
+
* Called when `embedSource`, `wikiLinkSource`, or `tagSource` throws or
|
|
129
|
+
* returns a rejected promise. Purely for observation — logging, a toast — the
|
|
130
|
+
* resolver still falls back (literal `![[ref]]` text, or no completions)
|
|
131
|
+
* either way. A `null` return is a valid result, not an error, and does not
|
|
132
|
+
* fire this. Reactive; see {@link ResolveErrorInfo}.
|
|
133
|
+
*/
|
|
134
|
+
onResolveError?: (error: unknown, info: ResolveErrorInfo) => void;
|
|
135
|
+
/**
|
|
136
|
+
* Formatting toolbar above the editing surface (`source`, `in-place`,
|
|
137
|
+
* `split`; never `preview`). Omit or `true` for the default bar, `false` to
|
|
138
|
+
* hide it, or an object to choose and order the buttons. See ADR-002 §2.
|
|
139
|
+
*/
|
|
140
|
+
toolbar?: boolean | ToolbarConfig;
|
|
141
|
+
/**
|
|
142
|
+
* Replace individual toolbar glyphs, keyed by command id. Any id left out
|
|
143
|
+
* keeps its built-in inline-SVG icon — Stylo ships no icon dependency.
|
|
144
|
+
*/
|
|
145
|
+
icons?: Partial<Record<ToolbarCommandId, ReactNode>>;
|
|
146
|
+
/** Render the source surface read-only. */
|
|
147
|
+
readOnly?: boolean;
|
|
148
|
+
/** Placeholder text shown when the document is empty (source surface). */
|
|
149
|
+
placeholder?: string;
|
|
150
|
+
/** Extra class on the root element, alongside the internal classes. */
|
|
151
|
+
className?: string;
|
|
152
|
+
/**
|
|
153
|
+
* CodeMirror extensions appended after Stylo's own, on every editing surface
|
|
154
|
+
* (`source`, `in-place`, and `split`'s source pane). Reactive: a changed array
|
|
155
|
+
* reconfigures the live view with no remount, so the cursor, undo history and
|
|
156
|
+
* scroll position survive. Compared shallowly — pass a stable identity
|
|
157
|
+
* (`useMemo`) when nothing changed. Ignored in `preview`, which has no editor.
|
|
158
|
+
*/
|
|
159
|
+
extensions?: readonly Extension[];
|
|
160
|
+
/**
|
|
161
|
+
* Host content docked inside the editing surface (`source`, `in-place`,
|
|
162
|
+
* `split`'s source pane; never `preview`) — after the find/replace panel,
|
|
163
|
+
* before the document body. Unlike `toolbar.render`, which wraps content
|
|
164
|
+
* *before* the whole canvas, this reaches the seam between CodeMirror's own
|
|
165
|
+
* top panels and its scroller, so host chrome (e.g. a frontmatter card) can
|
|
166
|
+
* sit under find/replace instead of always above it. `view` is `null` until
|
|
167
|
+
* the surface has mounted. Read once, at mount.
|
|
168
|
+
*/
|
|
169
|
+
canvasHeader?: (ctx: {
|
|
170
|
+
view: EditorView | null;
|
|
171
|
+
}) => ReactNode;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Imperative handle exposed on a `ref` to `<Stylo>`. Every method but
|
|
175
|
+
* `invalidateEmbed` is a no-op — returning `null` / `false` where it has a
|
|
176
|
+
* return value — in `preview` mode or before the editing surface has mounted,
|
|
177
|
+
* since there is no editor then.
|
|
178
|
+
*/
|
|
179
|
+
export interface StyloHandle {
|
|
180
|
+
/** Move keyboard focus into the editing surface. */
|
|
181
|
+
focus(): void;
|
|
182
|
+
/**
|
|
183
|
+
* Put the caret at the start of the first ATX heading (`#` … `######`) whose
|
|
184
|
+
* text matches `text` (trimmed, case-insensitive) and scroll it to the top of
|
|
185
|
+
* the viewport. Returns `true` when a heading matched.
|
|
186
|
+
*/
|
|
187
|
+
scrollToHeading(text: string): boolean;
|
|
188
|
+
/**
|
|
189
|
+
* Replace the current selection — or insert at the caret when the selection is
|
|
190
|
+
* empty — with `md`. No effect on a `readOnly` surface.
|
|
191
|
+
*/
|
|
192
|
+
insertAtCursor(md: string): void;
|
|
193
|
+
/**
|
|
194
|
+
* The underlying CodeMirror `EditorView`, or `null` in `preview` mode or
|
|
195
|
+
* before mount. An escape hatch: not covered by semver.
|
|
196
|
+
*/
|
|
197
|
+
getView(): EditorView | null;
|
|
198
|
+
/**
|
|
199
|
+
* Drops the cached result for an `![[ref]]` embed — or, with no `ref`, every
|
|
200
|
+
* cached embed — so the next render re-invokes `embedSource` instead of
|
|
201
|
+
* showing what it returned before. Call this when you know the content
|
|
202
|
+
* behind a reference changed while the editor is open; Stylo has no way to
|
|
203
|
+
* detect that on its own (see `EmbedSource`). Works in every mode, including
|
|
204
|
+
* `preview`, since it targets the embed cache rather than the editor. A
|
|
205
|
+
* no-op when `embedSource` isn't set.
|
|
206
|
+
*/
|
|
207
|
+
invalidateEmbed(ref?: string): void;
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/types/core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { ToolbarCommandId } from "./toolbar";
|
|
2
|
+
/**
|
|
3
|
+
* Per-construct on/off switches for the in-place canvas. Each key defaults to
|
|
4
|
+
* `true`; setting one `false` leaves that construct as plain source — no
|
|
5
|
+
* decoration, no cursor-reveal behaviour. See ADR-005.
|
|
6
|
+
*/
|
|
7
|
+
export interface InPlaceDecorationToggles {
|
|
8
|
+
headings?: boolean;
|
|
9
|
+
emphasis?: boolean;
|
|
10
|
+
links?: boolean;
|
|
11
|
+
wikilinks?: boolean;
|
|
12
|
+
math?: boolean;
|
|
13
|
+
lists?: boolean;
|
|
14
|
+
tasks?: boolean;
|
|
15
|
+
blockquote?: boolean;
|
|
16
|
+
horizontalRule?: boolean;
|
|
17
|
+
code?: boolean;
|
|
18
|
+
frontmatter?: boolean;
|
|
19
|
+
tables?: boolean;
|
|
20
|
+
/** `![[ref]]` transclusion blocks. Needs `embedSource` set; see ADR-009. */
|
|
21
|
+
embeds?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* How the in-place canvas handles editing a table.
|
|
25
|
+
* `"source"` (default) reveals the aligned pipe source under the caret;
|
|
26
|
+
* `"cells"` keeps the rendered `<table>` and edits its cells in place.
|
|
27
|
+
*/
|
|
28
|
+
export type TableEditing = "source" | "cells";
|
|
29
|
+
/**
|
|
30
|
+
* Whether the in-place canvas shows a construct's Markdown markers when the
|
|
31
|
+
* caret is on its line. `"caret"` (default) reveals them for editing and
|
|
32
|
+
* re-hides them on the way out — Obsidian's Live Preview. `"never"` keeps every
|
|
33
|
+
* inline marker hidden at all times; formatting is changed through the toolbar,
|
|
34
|
+
* the right-click menu, shortcuts, and autoformat-on-type instead. See ADR-007;
|
|
35
|
+
* `"never"` is being rolled out in stages.
|
|
36
|
+
*/
|
|
37
|
+
export type RevealMode = "caret" | "never";
|
|
38
|
+
/**
|
|
39
|
+
* What appears when text is selected in the in-place canvas. `"menu"` (default)
|
|
40
|
+
* puts the inline-formatting group in the right-click menu and shows no floating
|
|
41
|
+
* bar; `"bar"` shows a floating bar above the selection and drops that group
|
|
42
|
+
* from the menu so nothing is doubled; `"none"` shows neither and leaves the
|
|
43
|
+
* main toolbar as the only formatting surface. The toolbar is independent of
|
|
44
|
+
* this setting — it is always available (unless hidden via `toolbar`) and always
|
|
45
|
+
* acts on the selection.
|
|
46
|
+
*/
|
|
47
|
+
export type SelectionUI = "menu" | "bar" | "none";
|
|
48
|
+
/**
|
|
49
|
+
* The right-click menu's top-level groups. `link` is the internal / external
|
|
50
|
+
* link field rows; `format` the inline-mark submenu; `paragraph` the block-type
|
|
51
|
+
* submenu; `insert` the new-block submenu; `clipboard` cut / copy / paste.
|
|
52
|
+
*/
|
|
53
|
+
export type MenuGroupId = "link" | "format" | "paragraph" | "insert" | "clipboard";
|
|
54
|
+
export interface ContextMenuConfig {
|
|
55
|
+
/**
|
|
56
|
+
* Which top-level groups the menu shows, in order. Omit for all five in their
|
|
57
|
+
* default order. `link` and `format` still yield to `selectionUI` when it is
|
|
58
|
+
* not `"menu"` (they move to the floating bar / toolbar).
|
|
59
|
+
*/
|
|
60
|
+
groups?: MenuGroupId[];
|
|
61
|
+
}
|
|
62
|
+
export interface InPlaceConfig {
|
|
63
|
+
/** Which decoration types the in-place canvas renders. Read once, at mount. */
|
|
64
|
+
decorations?: InPlaceDecorationToggles;
|
|
65
|
+
/** Table editing mode (see `TableEditing`). Read once, at mount. */
|
|
66
|
+
table?: TableEditing;
|
|
67
|
+
/**
|
|
68
|
+
* Marker reveal behaviour (see `RevealMode`). Optional, defaults to
|
|
69
|
+
* `"caret"`. Read once, at mount.
|
|
70
|
+
*/
|
|
71
|
+
reveal?: RevealMode;
|
|
72
|
+
/**
|
|
73
|
+
* Right-click a block for a context menu (inline actions on a selection,
|
|
74
|
+
* block + insert actions otherwise). `false` keeps the browser's own menu; an
|
|
75
|
+
* object picks and orders the menu's groups. Defaults to `true`. Read once,
|
|
76
|
+
* at mount.
|
|
77
|
+
*/
|
|
78
|
+
contextMenu?: boolean | ContextMenuConfig;
|
|
79
|
+
/**
|
|
80
|
+
* What a non-empty selection offers (see `SelectionUI`). Defaults to
|
|
81
|
+
* `"menu"`. Read once, at mount.
|
|
82
|
+
*/
|
|
83
|
+
selectionUI?: SelectionUI;
|
|
84
|
+
/**
|
|
85
|
+
* Which buttons the floating selection bar shows (`selectionUI: "bar"`), in
|
|
86
|
+
* order. Any of `bold` / `italic` / `strike` / `code` / `link` / `wikilink` /
|
|
87
|
+
* `math`. Omit for all seven. Read once, at mount.
|
|
88
|
+
*/
|
|
89
|
+
selectionBarItems?: ToolbarCommandId[];
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=inplace.d.ts.map
|
|
@@ -0,0 +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;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAA;AAElF,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,MAAM,CAAC,EAAE,WAAW,EAAE,CAAA;CACvB;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"}
|