creo-edit 0.1.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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +169 -0
  3. package/dist/clipboard/drop.d.ts +17 -0
  4. package/dist/clipboard/htmlParser.d.ts +18 -0
  5. package/dist/clipboard/htmlSerializer.d.ts +9 -0
  6. package/dist/commands/imageCommands.d.ts +32 -0
  7. package/dist/commands/insertCommands.d.ts +33 -0
  8. package/dist/commands/listCommands.d.ts +19 -0
  9. package/dist/commands/markCommands.d.ts +21 -0
  10. package/dist/commands/navigationCommands.d.ts +27 -0
  11. package/dist/commands/structuralCommands.d.ts +22 -0
  12. package/dist/commands/textCommands.d.ts +18 -0
  13. package/dist/controller/history.d.ts +31 -0
  14. package/dist/controller/navigation.d.ts +18 -0
  15. package/dist/controller/selection.d.ts +25 -0
  16. package/dist/controller/wordBoundary.d.ts +25 -0
  17. package/dist/createEditor.d.ts +252 -0
  18. package/dist/dom/anchorMap.d.ts +27 -0
  19. package/dist/index.d.ts +34 -0
  20. package/dist/index.js +7833 -0
  21. package/dist/index.js.map +72 -0
  22. package/dist/input/keymap.d.ts +47 -0
  23. package/dist/input/mobile.d.ts +13 -0
  24. package/dist/input/nativeInput.d.ts +27 -0
  25. package/dist/markdown/serialize.d.ts +2 -0
  26. package/dist/model/blockText.d.ts +42 -0
  27. package/dist/model/cellAccess.d.ts +2 -0
  28. package/dist/model/doc.d.ts +45 -0
  29. package/dist/model/fractional.d.ts +57 -0
  30. package/dist/model/rebalance.d.ts +12 -0
  31. package/dist/model/types.d.ts +133 -0
  32. package/dist/plugin/anchorCodec.d.ts +18 -0
  33. package/dist/plugin/atomic.d.ts +2 -0
  34. package/dist/plugin/builtin.d.ts +10 -0
  35. package/dist/plugin/decorations.d.ts +35 -0
  36. package/dist/plugin/htmlCodec.d.ts +8 -0
  37. package/dist/plugin/keymapMatch.d.ts +2 -0
  38. package/dist/plugin/registry.d.ts +29 -0
  39. package/dist/plugin/runsAt.d.ts +9 -0
  40. package/dist/plugin/serializeCodec.d.ts +6 -0
  41. package/dist/plugin/triggers.d.ts +33 -0
  42. package/dist/plugin/types.d.ts +188 -0
  43. package/dist/plugins/add-block/index.d.ts +17 -0
  44. package/dist/plugins/calendar/index.d.ts +7 -0
  45. package/dist/plugins/calendar/view.d.ts +29 -0
  46. package/dist/plugins/cells/codecs.d.ts +6 -0
  47. package/dist/plugins/cells/commands.d.ts +5 -0
  48. package/dist/plugins/cells/controls.d.ts +3 -0
  49. package/dist/plugins/cells/htmlCodec.d.ts +6 -0
  50. package/dist/plugins/cells/index.d.ts +3 -0
  51. package/dist/plugins/cells/views.d.ts +27 -0
  52. package/dist/plugins/drag-handle/index.d.ts +6 -0
  53. package/dist/plugins/infinite-scroll/index.d.ts +44 -0
  54. package/dist/plugins/md-shortcuts/index.d.ts +2 -0
  55. package/dist/plugins/search/engine.d.ts +33 -0
  56. package/dist/plugins/search/highlight.d.ts +13 -0
  57. package/dist/plugins/search/index.d.ts +5 -0
  58. package/dist/plugins/search/navigate.d.ts +15 -0
  59. package/dist/plugins/search/styles.d.ts +1 -0
  60. package/dist/plugins/search/types.d.ts +73 -0
  61. package/dist/plugins/search/ui.d.ts +2 -0
  62. package/dist/plugins/slash/index.d.ts +12 -0
  63. package/dist/plugins/slash/items.d.ts +21 -0
  64. package/dist/plugins/slash/menu.d.ts +17 -0
  65. package/dist/plugins/styles.css +233 -0
  66. package/dist/render/DocView.d.ts +7 -0
  67. package/dist/render/InlineRunsView.d.ts +7 -0
  68. package/dist/render/blocks/CodeBlockView.d.ts +7 -0
  69. package/dist/render/blocks/HeadingView.d.ts +7 -0
  70. package/dist/render/blocks/ImageView.d.ts +8 -0
  71. package/dist/render/blocks/ListItemView.d.ts +7 -0
  72. package/dist/render/blocks/ParagraphView.d.ts +7 -0
  73. package/dist/virtual/VirtualDoc.d.ts +28 -0
  74. package/dist/virtual/heightIndex.d.ts +36 -0
  75. package/package.json +53 -0
@@ -0,0 +1,188 @@
1
+ import type { PublicView, Store } from "creo";
2
+ import type { Anchor, Block, BlockId, BlockSpec, DocState, InlineRun, Selection } from "../model/types";
3
+ export type RunsCtx = {
4
+ runs: InlineRun[];
5
+ setRuns: (newRuns: InlineRun[]) => Block;
6
+ };
7
+ export type DomPoint = {
8
+ node: Node;
9
+ offset: number;
10
+ };
11
+ export type AnchorCodec = {
12
+ /**
13
+ * Given the outer block element + a DOM hit (node, localOffset) inside it,
14
+ * produce an Anchor. Plugins for tables / columns walk into their cell
15
+ * sub-element and extract row/col from data-cell / data-col before
16
+ * computing the visible-character offset.
17
+ */
18
+ domToAnchor(blockEl: HTMLElement, hit: Node, localOffset: number): Anchor | null;
19
+ /**
20
+ * Given an Anchor, locate the (DOM node, offset) within the block's
21
+ * mounted DOM. Returns null when the relevant sub-element isn't currently
22
+ * mounted (e.g. virtualized off-screen).
23
+ */
24
+ anchorToDom(blockEl: HTMLElement, a: Anchor): DomPoint | null;
25
+ /**
26
+ * Return the scope element for IME composition diffing — e.g. the active
27
+ * <td> for a table caret, or the active <div data-col> for columns.
28
+ * Defaults to the block element itself for blocks without sub-scopes.
29
+ */
30
+ domScope?(blockEl: HTMLElement, a: Anchor): HTMLElement | null;
31
+ };
32
+ export type HtmlParseCtx = {
33
+ /** Active inline marks (b/i/u/s/code) collected from ancestor elements. */
34
+ marks: import("../model/types").Mark[];
35
+ };
36
+ export type HtmlBlockCodec = {
37
+ /**
38
+ * HTML tag names this block claims when parsing. The parser walks fragment
39
+ * children, finds the first registered codec whose `matchHTML` includes
40
+ * the tag, and calls `parseHTML`. Order = plugin registration order, so
41
+ * built-ins should register `table` before generic `<div>` parsers.
42
+ */
43
+ matchHTML?: string[];
44
+ parseHTML?(el: HTMLElement, ctx: HtmlParseCtx): BlockSpec | null;
45
+ serializeHTML?(b: Block): string;
46
+ };
47
+ export type SerializeCodec = {
48
+ serialize(b: Block): unknown;
49
+ deserialize(s: unknown, id: BlockId): BlockSpec;
50
+ };
51
+ export type BlockDef<B extends Block = Block> = {
52
+ /** Discriminator — must match block.type. */
53
+ type: B["type"];
54
+ /** Creo view rendering the block. Receives the block + a stable key. */
55
+ view: PublicView<{
56
+ block: B;
57
+ key?: string;
58
+ }, void>;
59
+ /**
60
+ * Resolve the runs slot at `anchor`. Defaults to "block.runs if present,
61
+ * else null" (covers all text-bearing blocks). Override for blocks with
62
+ * nested cells (table, columns).
63
+ */
64
+ runsAt?(b: B, a: Anchor): RunsCtx | null;
65
+ /**
66
+ * Whether the block is "text-bearing" — has a top-level `runs: InlineRun[]`
67
+ * field that text commands operate on directly. Inferred from `runsAt`
68
+ * presence at registration time when omitted; defaults to true if the
69
+ * block exposes a `runs` field at runtime.
70
+ */
71
+ isTextBearing?: boolean;
72
+ /**
73
+ * "Atomic" non-editable block — caret can only sit before (side 0) or
74
+ * after (side 1), never inside. Path encoding is `[side]`. The view should
75
+ * render the outer element with `contenteditable="false"` so the browser
76
+ * places the native caret around the block, not inside it. Backspace /
77
+ * Delete on the block deletes the whole block. Implies `isTextBearing:
78
+ * false` and uses `atomicCodec` by default if no `anchorCodec` is given.
79
+ */
80
+ isAtomic?: boolean;
81
+ /** DOM ↔ anchor mapping. Optional — text-bearing blocks fall back to a
82
+ * shared default that walks visible text by character offset. */
83
+ anchorCodec?: AnchorCodec;
84
+ /** HTML round-trip. Optional — only needed for blocks that survive
85
+ * copy / paste with external apps. */
86
+ htmlCodec?: HtmlBlockCodec;
87
+ /** JSON SerializedBlock round-trip. Required for blocks that should
88
+ * survive `toJSON()` / `setDoc()`. */
89
+ serializeCodec?: SerializeCodec;
90
+ };
91
+ export type CommandCtx = {
92
+ docStore: Store<DocState>;
93
+ selStore: Store<Selection>;
94
+ };
95
+ export type CommandDef<P = unknown> = {
96
+ t: string;
97
+ /** Run the command. Return `false` to signal the command did not apply
98
+ * (e.g. arrow-nav at the table edge); the keymap dispatcher uses this to
99
+ * decide whether to preventDefault. Returning void or true means handled. */
100
+ run(ctx: CommandCtx, payload: P): boolean | void;
101
+ };
102
+ export type KeymapDef = {
103
+ /**
104
+ * Chord string. Modifiers join with "+". The platform-specific Mod token
105
+ * resolves to Cmd on macOS, Ctrl elsewhere. Examples:
106
+ * "Mod+B", "Mod+Shift+S", "Tab", "Shift+Tab", "ArrowLeft".
107
+ */
108
+ chord: string;
109
+ when?(ctx: CommandCtx): boolean;
110
+ /** Command t + payload to dispatch when chord matches. */
111
+ command: {
112
+ t: string;
113
+ payload?: unknown;
114
+ };
115
+ };
116
+ export type TriggerCtx = {
117
+ /** Anchor of the trigger character that fired the match. */
118
+ at: Anchor;
119
+ docStore: Store<DocState>;
120
+ selStore: Store<Selection>;
121
+ /**
122
+ * Dispatch a command. Accepts the typed `Command` shape (preferred for
123
+ * built-ins so payload fields land in the right place) or the open
124
+ * `{ t: string; payload?: unknown }` shape for plugin commands.
125
+ *
126
+ * Two-arg form `(t, payload)` is sugar for `{ t, payload }`.
127
+ */
128
+ dispatch(cmd: {
129
+ t: string;
130
+ [k: string]: unknown;
131
+ }): void;
132
+ dispatch(t: string, payload?: unknown): void;
133
+ /** Element where popover UI should anchor. */
134
+ caretRect(): DOMRect | null;
135
+ /**
136
+ * Request the trigger manager to close this trigger. Idempotent. Call
137
+ * after committing the trigger's action so subsequent keystrokes (e.g.
138
+ * Enter to split a block) flow through to the editor instead of being
139
+ * captured by a stale controller.
140
+ */
141
+ close(): void;
142
+ };
143
+ export type TriggerController = {
144
+ onTextChange?(query: string): void;
145
+ onKey?(e: KeyboardEvent): boolean;
146
+ close(): void;
147
+ };
148
+ export type TriggerDef = {
149
+ /**
150
+ * String prefix or RegExp matched against the most recently inserted
151
+ * characters at the caret. String matches treat the value as a literal
152
+ * trigger char (e.g. "/").
153
+ */
154
+ match: string | RegExp;
155
+ open(ctx: TriggerCtx): TriggerController | null;
156
+ };
157
+ /**
158
+ * DecorationManager passed to mount fns so plugins can read state-without-
159
+ * subscribing-to-doc — e.g. "is this block currently hovered?". Decorations
160
+ * MUST NOT subscribe directly to docStore inside mount; that would re-render
161
+ * on every keystroke. Read live state via this handle on pointer events.
162
+ */
163
+ export type DecorationHandle = {
164
+ hoveredBlock(): import("../model/types").BlockId | null;
165
+ };
166
+ export type DecorationDef = {
167
+ id: string;
168
+ match(b: Block): boolean;
169
+ /**
170
+ * Mount the decoration's UI into the supplied `host` element. Return an
171
+ * optional cleanup fn (called on unmount). The decoration's UI is plain
172
+ * DOM — plugins that want a creo subtree create their own creo app
173
+ * inside `mount` and dispose it in the returned cleanup.
174
+ */
175
+ mount(block: Block, blockEl: HTMLElement, host: HTMLElement, handle: DecorationHandle): (() => void) | void;
176
+ layer: "left" | "right" | "top" | "bottom" | "absolute";
177
+ };
178
+ export type EditorPlugin = {
179
+ name: string;
180
+ blocks?: BlockDef<Block>[];
181
+ commands?: CommandDef<unknown>[];
182
+ keymap?: KeymapDef[];
183
+ triggers?: TriggerDef[];
184
+ decorations?: DecorationDef[];
185
+ /** Tag prefixes whose history snapshots may coalesce. Defaults are
186
+ * ["text:"]; plugins can declare their own (e.g. "myPlugin:typing"). */
187
+ historyCoalescePrefixes?: string[];
188
+ };
@@ -0,0 +1,17 @@
1
+ import type { Block } from "../../model/types";
2
+ import type { EditorPlugin } from "../../plugin/types";
3
+ import { type SlashItem } from "../slash/items";
4
+ export type AddBlockOptions = {
5
+ /** Show on hover only (default). */
6
+ hoverOnly?: boolean;
7
+ /**
8
+ * Custom click handler. When provided, replaces the default behavior of
9
+ * opening the picker menu — useful for hosts that want a different UI
10
+ * or that want to insert a fixed block kind without prompting.
11
+ */
12
+ onClick?: (block: Block, blockEl: HTMLElement) => void;
13
+ /** Replace or extend the items shown in the picker. Defaults to the
14
+ * same set as the slash menu. */
15
+ items?: SlashItem[];
16
+ };
17
+ export declare function addBlockPlugin(opts?: AddBlockOptions): EditorPlugin;
@@ -0,0 +1,7 @@
1
+ import type { EditorPlugin } from "../../plugin/types";
2
+ import type { SlashItem } from "../slash/items";
3
+ /** Slash-menu item — append to your slash items array to expose calendar
4
+ * insertion at the `/` trigger. */
5
+ export declare const calendarSlashItem: SlashItem;
6
+ export declare function calendarPlugin(): EditorPlugin;
7
+ export { CalendarView, DateMarkerView, calendarHelpers } from "./view";
@@ -0,0 +1,29 @@
1
+ import type { CalendarBlock, DateMarkerBlock } from "../../model/types";
2
+ declare function todayIso(): string;
3
+ declare function formatIso(d: Date): string;
4
+ declare function parseIso(s: string): Date;
5
+ declare function addDays(d: Date, n: number): Date;
6
+ export declare const CalendarView: (props: {
7
+ block: CalendarBlock;
8
+ } & {
9
+ key?: import("creo").Key;
10
+ ref?: import("creo").Ref<void> | undefined;
11
+ }, slot?: import("creo").SlotContent) => void;
12
+ /**
13
+ * DateMarkerView — slim non-editable atomic block that renders a single
14
+ * "Wednesday, 2 Sep." line. Used as a separator block in journal /
15
+ * planner UX where the user writes editable paragraphs between days.
16
+ */
17
+ export declare const DateMarkerView: (props: {
18
+ block: DateMarkerBlock;
19
+ } & {
20
+ key?: import("creo").Key;
21
+ ref?: import("creo").Ref<void> | undefined;
22
+ }, slot?: import("creo").SlotContent) => void;
23
+ export declare const calendarHelpers: {
24
+ todayIso: typeof todayIso;
25
+ formatIso: typeof formatIso;
26
+ parseIso: typeof parseIso;
27
+ addDays: typeof addDays;
28
+ };
29
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { Anchor, ColumnsBlock, TableBlock } from "../../model/types";
2
+ import type { AnchorCodec, RunsCtx } from "../../plugin/types";
3
+ export declare function tableRunsAt(b: TableBlock, a: Anchor): RunsCtx | null;
4
+ export declare const tableAnchorCodec: AnchorCodec;
5
+ export declare function columnsRunsAt(b: ColumnsBlock, a: Anchor): RunsCtx | null;
6
+ export declare const columnsAnchorCodec: AnchorCodec;
@@ -0,0 +1,5 @@
1
+ import type { DocState, Selection } from "../../model/types";
2
+ import type { CommandDef } from "../../plugin/types";
3
+ export declare function isInTable(doc: DocState, sel: Selection): boolean;
4
+ export declare function isInColumns(doc: DocState, sel: Selection): boolean;
5
+ export declare const tableCommandDefs: CommandDef<unknown>[];
@@ -0,0 +1,3 @@
1
+ import type { DecorationDef } from "../../plugin/types";
2
+ export declare const tableControlsDecoration: DecorationDef;
3
+ export declare const columnsControlsDecoration: DecorationDef;
@@ -0,0 +1,6 @@
1
+ import type { Block, BlockSpec, Mark } from "../../model/types";
2
+ export declare function parseTableHTML(el: HTMLElement, ctx: {
3
+ marks: Mark[];
4
+ }): BlockSpec | null;
5
+ export declare function serializeTableHTML(b: Block): string;
6
+ export declare function serializeColumnsHTML(b: Block): string;
@@ -0,0 +1,3 @@
1
+ import type { EditorPlugin } from "../../plugin/types";
2
+ export declare const cellsPlugin: EditorPlugin;
3
+ export { isInTable } from "./commands";
@@ -0,0 +1,27 @@
1
+ import type { ColumnsBlock, TableBlock } from "../../model/types";
2
+ /**
3
+ * Render a TableBlock as <table><tbody> with keyed rows + cells.
4
+ *
5
+ * NOTE: intermediate primitives (tbody, tr) get FRESH prop objects each
6
+ * render, not the `_` no-props constant. The reconciler skips descent
7
+ * when an intermediate primitive's props are reference-equal to the
8
+ * previous render — using `_` everywhere causes that shortcut to fire
9
+ * and changes inside cells (e.g. typed text) never make it to the DOM.
10
+ */
11
+ export declare const TableViewPlugin: (props: {
12
+ block: TableBlock;
13
+ } & {
14
+ key?: import("creo").Key;
15
+ ref?: import("creo").Ref<void> | undefined;
16
+ }, slot?: import("creo").SlotContent) => void;
17
+ /**
18
+ * Render a ColumnsBlock as a flex row of equal-width column divs. Each
19
+ * column carries `data-block-id` (so caret-overlay queries find the owner)
20
+ * AND `data-col="<index>"` so pointToAnchor can reconstruct the column.
21
+ */
22
+ export declare const ColumnsViewPlugin: (props: {
23
+ block: ColumnsBlock;
24
+ } & {
25
+ key?: import("creo").Key;
26
+ ref?: import("creo").Ref<void> | undefined;
27
+ }, slot?: import("creo").SlotContent) => void;
@@ -0,0 +1,6 @@
1
+ import type { EditorPlugin } from "../../plugin/types";
2
+ export type DragHandleOptions = {
3
+ /** Show on hover only (default). When `false`, handles are always visible. */
4
+ hoverOnly?: boolean;
5
+ };
6
+ export declare function dragHandlePlugin(opts?: DragHandleOptions): EditorPlugin;
@@ -0,0 +1,44 @@
1
+ import type { BlockId, DistOmit, BlockSpec, DocState, Selection } from "../../model/types";
2
+ import type { EditorPlugin } from "../../plugin/types";
3
+ type Input = DistOmit<BlockSpec, "id"> & {
4
+ id?: BlockId;
5
+ };
6
+ export type InfiniteScrollEditor = {
7
+ docStore: {
8
+ get: () => DocState;
9
+ subscribe: (fn: () => void) => () => void;
10
+ };
11
+ selStore: {
12
+ get: () => Selection;
13
+ };
14
+ appendBlocks: (specs: Input[]) => BlockId[];
15
+ prependBlocks: (specs: Input[]) => BlockId[];
16
+ };
17
+ export type InfiniteScrollOptions = {
18
+ /**
19
+ * The scrolling element to watch. Either an element, or a getter (the
20
+ * element may not exist at editor-mount time). When omitted, the
21
+ * plugin walks up from the editor root looking for the nearest
22
+ * `overflow-y: auto|scroll` ancestor; falls back to `window`.
23
+ */
24
+ scrollContainer?: HTMLElement | (() => HTMLElement | null);
25
+ /** Called when the user scrolls within `threshold` of the bottom. */
26
+ loadAfter?: (editor: InfiniteScrollEditor) => void;
27
+ /**
28
+ * Called when the user scrolls within `threshold` of the top. The
29
+ * plugin re-anchors `scrollTop` after the load so the viewport doesn't
30
+ * jump.
31
+ */
32
+ loadBefore?: (editor: InfiniteScrollEditor) => void;
33
+ /** Pixel distance from an edge that triggers a load. Default: 240. */
34
+ threshold?: number;
35
+ /**
36
+ * Minimum gap between successive triggers in the SAME direction (ms).
37
+ * Prevents back-to-back appends while the user is mid-flick. Default
38
+ * 60ms — short enough to feel responsive while letting the renderer
39
+ * commit a frame in between.
40
+ */
41
+ cooldownMs?: number;
42
+ };
43
+ export declare function infiniteScrollPlugin(opts: InfiniteScrollOptions): EditorPlugin;
44
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { EditorPlugin } from "../../plugin/types";
2
+ export declare function mdShortcutsPlugin(): EditorPlugin;
@@ -0,0 +1,33 @@
1
+ import type { Anchor, Block, BlockId, DocState } from "../../model/types";
2
+ export type SearchMatch = {
3
+ blockId: BlockId;
4
+ start: Anchor;
5
+ end: Anchor;
6
+ /** Slice of the matched text — used for backend results that don't
7
+ * resolve to a live block; engine results include it for parity. */
8
+ snippet?: string;
9
+ };
10
+ export type SearchOpts = {
11
+ caseSensitive: boolean;
12
+ wholeWord: boolean;
13
+ regex: boolean;
14
+ };
15
+ type Slot = {
16
+ text: string;
17
+ /** Anchor path prefix this slot belongs to. Concatenated with charOffset
18
+ * to form the final anchor path. */
19
+ prefix: number[];
20
+ };
21
+ /** Yield every searchable text slot for a block, with the anchor prefix
22
+ * needed to address positions inside it. Skips non-text blocks (img,
23
+ * calendar, date-marker). */
24
+ export declare function slotsOf(block: Block): Generator<Slot>;
25
+ /** Build a sticky-global regex for the query + options. Throws if the user
26
+ * supplied an invalid `regex: true` pattern — caller should catch and show
27
+ * a UI error. */
28
+ export declare function buildMatcher(query: string, opts: SearchOpts): RegExp;
29
+ /** Scan every block in the doc and return all matches, in document order. */
30
+ export declare function searchDoc(doc: DocState, query: string, opts: SearchOpts): SearchMatch[];
31
+ /** Stable key for dedupe across re-scans. */
32
+ export declare function matchKey(m: SearchMatch): string;
33
+ export {};
@@ -0,0 +1,13 @@
1
+ import type { SearchMatch } from "./engine";
2
+ export declare const HL_ALL = "creo-search";
3
+ export declare const HL_CURRENT = "creo-search-current";
4
+ export declare function isHighlightApiSupported(): boolean;
5
+ /**
6
+ * Sync the named highlights with the current match list and active index.
7
+ *
8
+ * Returns the number of matches that landed in DOM (i.e. mounted). The
9
+ * caller can use it for diagnostics — e.g. if `0 < total`, some matches
10
+ * are off-screen and waiting on virtualization to mount their blocks.
11
+ */
12
+ export declare function paintHighlights(root: HTMLElement, matches: readonly SearchMatch[], activeIndex: number): number;
13
+ export declare function clearHighlights(): void;
@@ -0,0 +1,5 @@
1
+ import type { EditorPlugin } from "../../plugin/types";
2
+ import type { SearchOptions } from "./types";
3
+ export type { SearchController, SearchOptions, SearchSource, SearchState, SearchToggle, } from "./types";
4
+ export type { SearchMatch, SearchOpts } from "./engine";
5
+ export declare function searchPlugin(opts?: SearchOptions): EditorPlugin;
@@ -0,0 +1,15 @@
1
+ import type { BlockId, DocState } from "../../model/types";
2
+ import type { SearchMatch } from "./engine";
3
+ import type { SearchSource } from "./types";
4
+ export type EditorScrollHandle = {
5
+ docStore: {
6
+ get(): DocState;
7
+ };
8
+ scrollToBlock(blockId: BlockId, opts?: {
9
+ block?: "start" | "center" | "end" | "nearest";
10
+ behavior?: ScrollBehavior;
11
+ }): void;
12
+ };
13
+ export declare function jumpToMatch(editor: EditorScrollHandle, match: SearchMatch, source?: SearchSource): Promise<void>;
14
+ export declare function nextIndex(current: number, total: number): number;
15
+ export declare function prevIndex(current: number, total: number): number;
@@ -0,0 +1 @@
1
+ export declare function ensureStylesInjected(): void;
@@ -0,0 +1,73 @@
1
+ import type { BlockId } from "../../model/types";
2
+ import type { SearchMatch, SearchOpts } from "./engine";
3
+ export type { SearchMatch, SearchOpts } from "./engine";
4
+ export type SearchToggle = "caseSensitive" | "wholeWord" | "regex";
5
+ export type SearchSource = {
6
+ /** Replace (or augment) the in-doc scan. Async OK. */
7
+ search(query: string, opts: SearchOpts): SearchMatch[] | Promise<SearchMatch[]>;
8
+ /** Called before jump-to-match for a blockId not yet in docStore. The
9
+ * host should load the chunk that contains the block and resolve when
10
+ * the block lands in docStore. */
11
+ ensureLoaded?(blockId: BlockId): Promise<void>;
12
+ };
13
+ export type SearchToggleOpt = {
14
+ initial?: boolean;
15
+ show?: boolean;
16
+ };
17
+ export type SearchOptions = {
18
+ /**
19
+ * When true, the plugin claims `Mod+F` and prevents the browser's find
20
+ * UI from opening. Default: `false` — opt-in to avoid surprising users
21
+ * who expect native Cmd+F.
22
+ */
23
+ interceptBrowserFind?: boolean;
24
+ /** Initial values + UI visibility for each toggle. Omitted toggles use
25
+ * `{ initial: false, show: false }`. */
26
+ toggles?: {
27
+ caseSensitive?: SearchToggleOpt;
28
+ wholeWord?: SearchToggleOpt;
29
+ regex?: SearchToggleOpt;
30
+ };
31
+ /** Backend search source (for infinite-scroll). When omitted, the
32
+ * plugin scans `docStore` only. */
33
+ source?: SearchSource;
34
+ /**
35
+ * Render the UI yourself. When provided, the default panel is NOT
36
+ * mounted — the callback gets a controller and a host element to
37
+ * render whatever it wants. Return a cleanup fn called on plugin
38
+ * teardown.
39
+ */
40
+ renderUI?: (controller: SearchController, host: HTMLElement) => () => void;
41
+ /** Debounce (ms) on input change. Default: 80. */
42
+ debounceMs?: number;
43
+ };
44
+ export type SearchState = {
45
+ isOpen: boolean;
46
+ query: string;
47
+ caseSensitive: boolean;
48
+ wholeWord: boolean;
49
+ regex: boolean;
50
+ matches: readonly SearchMatch[];
51
+ activeIndex: number;
52
+ /** Last engine error (e.g. invalid regex) — UI shows it inline. */
53
+ error: string | null;
54
+ };
55
+ export type SearchController = {
56
+ open(): void;
57
+ close(): void;
58
+ toggleOpen(): void;
59
+ isOpen(): boolean;
60
+ setQuery(q: string): void;
61
+ query(): string;
62
+ setToggle(t: SearchToggle, v: boolean): void;
63
+ toggle(t: SearchToggle): boolean;
64
+ matches(): readonly SearchMatch[];
65
+ activeIndex(): number;
66
+ setActiveIndex(i: number): void;
67
+ next(): void;
68
+ prev(): void;
69
+ /** Subscribe to state changes; returns unsubscribe. */
70
+ subscribe(fn: () => void): () => void;
71
+ /** Snapshot the full state. */
72
+ state(): SearchState;
73
+ };
@@ -0,0 +1,2 @@
1
+ import type { SearchController, SearchOptions } from "./types";
2
+ export declare function mountDefaultPanel(host: HTMLElement, controller: SearchController, options: SearchOptions): () => void;
@@ -0,0 +1,12 @@
1
+ import type { EditorPlugin } from "../../plugin/types";
2
+ import { type SlashItem } from "./items";
3
+ export type SlashOptions = {
4
+ /** Replace or extend the default item list. */
5
+ items?: SlashItem[];
6
+ /** Custom filter — defaults to case-insensitive substring on title+keywords. */
7
+ filter?: (items: SlashItem[], query: string) => SlashItem[];
8
+ };
9
+ export declare function slashCommandsPlugin(opts?: SlashOptions): EditorPlugin;
10
+ export type { SlashItem } from "./items";
11
+ export { defaultSlashItems, defaultFilter } from "./items";
12
+ export { mountSlashMenu, type MenuHandle, type MenuOptions } from "./menu";
@@ -0,0 +1,21 @@
1
+ import type { CommandCtx } from "../../plugin/types";
2
+ import type { DispatchableCommand } from "../../createEditor";
3
+ export type SlashItem = {
4
+ id: string;
5
+ title: string;
6
+ description?: string;
7
+ /** Lowercase keywords used by the default fuzzy filter. */
8
+ keywords?: string[];
9
+ /**
10
+ * Run the action. The slash menu has already removed the trigger char(s)
11
+ * from the document by the time this fires, and the caret is back where
12
+ * the trigger was — this fn just dispatches whatever should happen.
13
+ */
14
+ run(ctx: CommandCtx & {
15
+ dispatch: (cmd: DispatchableCommand) => void;
16
+ }): void;
17
+ };
18
+ export declare const defaultSlashItems: SlashItem[];
19
+ /** Default fuzzy filter — case-insensitive substring match against title +
20
+ * keywords. Plugins can pass a custom matcher via slashCommandsPlugin opts. */
21
+ export declare function defaultFilter(items: SlashItem[], query: string): SlashItem[];
@@ -0,0 +1,17 @@
1
+ import type { SlashItem } from "./items";
2
+ export type MenuOptions = {
3
+ items: SlashItem[];
4
+ filter?: (items: SlashItem[], query: string) => SlashItem[];
5
+ caretRect: DOMRect | null;
6
+ /** Called when the user picks an item (Enter or click). */
7
+ onPick(item: SlashItem): void;
8
+ /** Called when the user hits Escape, clicks away, or otherwise dismisses. */
9
+ onCancel(): void;
10
+ };
11
+ export type MenuHandle = {
12
+ setQuery(q: string): void;
13
+ /** Returns true if the key was consumed (arrow nav / Enter / Esc). */
14
+ handleKey(e: KeyboardEvent): boolean;
15
+ destroy(): void;
16
+ };
17
+ export declare function mountSlashMenu(opts: MenuOptions): MenuHandle;