@shadow-garden/bapbong-editor 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.
@@ -0,0 +1,207 @@
1
+ import { Schema } from 'prosemirror-model';
2
+ import { type EditorState, type Transaction } from '@shadow-garden/bapbong-input-bridge';
3
+ import type { CaretRect, Command as EditorCommand, EditorChange, EditorPlugin, MeasureMetrics, MeasureText, PagePoint } from '@shadow-garden/bapbong-contracts';
4
+ /** A section boundary surfaced for UI markers. `index` is the break index
5
+ * passed to `removeSectionBreak`; `rect` is a full-page-width line (page-local)
6
+ * at the boundary, ready for `pageToCanvas` — null if off the current layout. */
7
+ export interface SectionBoundary {
8
+ index: number;
9
+ newPage: boolean;
10
+ columns: number;
11
+ pos: number;
12
+ rect: {
13
+ pageIndex: number;
14
+ x: number;
15
+ y: number;
16
+ width: number;
17
+ } | null;
18
+ }
19
+ export type { EditorChange, EditorPlugin, PluginContext } from '@shadow-garden/bapbong-contracts';
20
+ export { RenderCore } from '@shadow-garden/bapbong-view';
21
+ import { Collection } from '@shadow-garden/bapbong-contracts';
22
+ import type { FindPlugin } from './find-plugin';
23
+ import type { TableSelectionPlugin } from './table-selection-plugin';
24
+ export type { TableSelectionPlugin, CellBlock, SelectedCell } from './table-selection-plugin';
25
+ export type { FindPlugin, FindState } from './find-plugin';
26
+ export interface BapbongEditorOptions {
27
+ /** The scroll viewport the page stack lives in (used for virtualization and
28
+ * scroll-into-view). Defaults to `stack.closest('.canvas-wrap')`. */
29
+ viewport?: HTMLElement;
30
+ /** Editor plugins. Their lifecycle/event hooks are invoked by the core; they
31
+ * reach back through the PluginContext handed to `setup`. */
32
+ plugins?: EditorPlugin[];
33
+ /** Engine-independent text measurer (e.g. font-file metrics). Defaults to a
34
+ * canvas-backed one; inject to make wrapping/pagination deterministic across
35
+ * WebView engines. Pair with {@link measureMetrics}. */
36
+ measureText?: MeasureText;
37
+ /** Vertical-metrics provider paired with {@link measureText}. */
38
+ measureMetrics?: MeasureMetrics;
39
+ }
40
+ /**
41
+ * The framework-agnostic core of bapbong: it owns the render → edit loop
42
+ * (import → layout → paint → input → selection → export) and the per-page
43
+ * canvas stack, with no UI chrome of its own. A host wires comment UI / toolbars
44
+ * around it via `onChange`, `caretRect`, `pageToCanvas`, `dispatch`, etc.
45
+ *
46
+ * The render half (layout/paint/scroll/zoom/geometry) lives in a shared
47
+ * {@link RenderCore} (the same one the read-only `BapbongView` uses); this class
48
+ * adds the editing half — a hidden ProseMirror editor as the IME / keyboard
49
+ * sink, pointer-driven caret/selection, plugins, commands and clipboard — and
50
+ * pushes a fresh doc + caret overlay to the core on every transaction.
51
+ */
52
+ export declare class BapbongEditor {
53
+ private readonly stack;
54
+ private readonly core;
55
+ private bridge;
56
+ private lastCaret;
57
+ private lastSelection;
58
+ private caretVisible;
59
+ private blinkTimer;
60
+ private dragAnchor;
61
+ private dragHead;
62
+ private readonly changeListeners;
63
+ private readonly caretPickListeners;
64
+ /** Plugin registry keyed by name — built-ins (internal) + host (external). */
65
+ private readonly plugins;
66
+ private readonly pluginTeardowns;
67
+ private readonly pluginCtx;
68
+ private pointerPlugins;
69
+ private readonly offFonts;
70
+ private guideEl;
71
+ private readonly highlightEls;
72
+ private actionEl;
73
+ private actionHandler;
74
+ private frameEl;
75
+ /** Headless editor commands keyed by name — the surface a toolbar/menubar
76
+ * renders and dispatches against (`editor.commands.get('bold')?.run(...)`).
77
+ * Built from the shared command layer; the same ops run on a backend.
78
+ * (Plugin-contributed commands are an additive follow-up.) */
79
+ readonly commands: Collection<EditorCommand>;
80
+ constructor(stack: HTMLElement, opts?: BapbongEditorOptions);
81
+ /** The controlled surface handed to each plugin (live state + geometry). */
82
+ private makePluginContext;
83
+ /** Current editor state (doc + selection). Throws before a document loads. */
84
+ get state(): EditorState;
85
+ /** Number of laid-out pages (0 before the first document). */
86
+ get pageCount(): number;
87
+ /** The document schema in use (model's base + plugin schema contributions).
88
+ * Hosts serialize/parse comment bodies, previews, etc. against this. */
89
+ get schema(): Schema;
90
+ /** Built-in find-and-replace (always registered; the host renders the bar).
91
+ * Cast is safe by construction — createBuiltins() always registers it. */
92
+ get find(): FindPlugin;
93
+ /** Built-in table cell-range selection (drag across cells). The host opens
94
+ * cell properties from its action icon / right-click via `onAction`. */
95
+ get tableSelection(): TableSelectionPlugin;
96
+ /** Import a .docx, lay it out, and paint the first frame. Resolves with the
97
+ * imported page-chrome keys (the rest of the import rides on the doc model
98
+ * exposed via `state`). */
99
+ loadDocx(bytes: ArrayBuffer): Promise<{
100
+ headerKeys: string[];
101
+ footerKeys: string[];
102
+ }>;
103
+ /** Export the (edited) document back to .docx bytes, carrying the imported
104
+ * source package so unmodelled parts survive the round-trip. */
105
+ exportDocx(): Promise<Uint8Array>;
106
+ /** Apply a transaction (the host builds comment/edit transactions against
107
+ * `state` and dispatches them here). */
108
+ dispatch(tr: Transaction): void;
109
+ /** Subscribe to layout/paint cycles. Returns an unsubscribe fn. */
110
+ onChange(cb: (c: EditorChange) => void): () => void;
111
+ /** Subscribe to caret placement (a pointerdown that placed the caret). The
112
+ * host uses this to e.g. select the comment whose range was clicked. */
113
+ onCaretPick(cb: (pos: number) => void): () => void;
114
+ /** Caret geometry at a doc position (page-local), or null. */
115
+ caretRect(pos: number): CaretRect | null;
116
+ /** Map a page-local point to container (canvas-stack) coordinates, or null. */
117
+ pageToCanvas(p: PagePoint): {
118
+ x: number;
119
+ y: number;
120
+ } | null;
121
+ /** The document's section boundaries (one per section break), for a host to
122
+ * draw clickable markers. `pos` is the doc position at the start of the
123
+ * section after the break — feed it to `caretRect` to place the marker. */
124
+ sectionBoundaries(): SectionBoundary[];
125
+ /** Move the selection (caret if `to` omitted) and anchor the IME. */
126
+ setSelection(from: number, to?: number): void;
127
+ /** Select the word at a doc position. */
128
+ selectWordAt(pos: number): void;
129
+ /** Scroll the viewport so the caret at `pos` sits `topMargin` px from the top. */
130
+ scrollToPos(pos: number, topMargin?: number): void;
131
+ /** Set the zoom factor (1 = 100%) and repaint at the new scale. */
132
+ setZoom(zoom: number): void;
133
+ /** The current zoom factor (1 = 100%). */
134
+ getZoom(): number;
135
+ /** Print the whole document — renders every page (not just the visible ones)
136
+ * and prints one image per sheet. */
137
+ print(): Promise<void>;
138
+ /** Focus the hidden ProseMirror editor (keyboard/IME sink). */
139
+ focus(): void;
140
+ /** Copy the selection to the clipboard (PM serializes the slice). */
141
+ copy(): boolean;
142
+ /** Cut the selection to the clipboard. */
143
+ cut(): boolean;
144
+ /** Paste clipboard content (HTML → parsed by the schema, else plain text). */
145
+ paste(): Promise<void>;
146
+ /** Paste clipboard text as plain text (no formatting). */
147
+ pasteText(): Promise<void>;
148
+ destroy(): void;
149
+ /** Mount the hidden ProseMirror editor on a fresh doc and run the first paint. */
150
+ private mount;
151
+ /** Bridge update hook: relayout (only when the doc changed) → paint → place
152
+ * IME → notify. */
153
+ private refresh;
154
+ /** Compute the caret/selection overlay from `state`, paint it (full content
155
+ * when `contentDirty`, else overlay-only), anchor the IME, and notify. */
156
+ private render;
157
+ /** The caret/selection overlay in the current blink phase. */
158
+ private currentOverlay;
159
+ /** Each plugin's doc-range decorations (the core resolves them to rects). */
160
+ private pluginDecorations;
161
+ private emitChange;
162
+ /** Redraw caret/selection in the current blink phase (overlay-only). */
163
+ private repaintOverlay;
164
+ private stopBlink;
165
+ /** Show the caret solid now, then blink while idle. */
166
+ private restartBlink;
167
+ /** ArrowUp/ArrowDown against the canvas layout (the hidden DOM's own line
168
+ * wrapping is meaningless). With `extend`, Shift+arrow grows the selection. */
169
+ private verticalCmd;
170
+ private onPointerDown;
171
+ /** Offer a key to plugins (before the hidden editor's keymaps). Only when
172
+ * the event targets this editor (the canvas stack / hidden bridge) or has
173
+ * no specific target (body — where focus lands after a claimed pointer
174
+ * gesture), so plugins never steal keys from other inputs on the page. */
175
+ private onKeyDown;
176
+ private onPointerMove;
177
+ /** Overlay-only repaint of the in-progress drag selection. */
178
+ private paintDragSelection;
179
+ private onPointerUp;
180
+ private onContextMenu;
181
+ private onDblClick;
182
+ /** Offer a pointer event to plugins; returns true if one claimed it. `pos` is
183
+ * resolved for down/up/contextmenu only (move fires on every hover). */
184
+ private offerPointer;
185
+ /** Position (or hide, with null) the transient vertical drag guide. Lives in
186
+ * the canvas stack so it scrolls/zooms with the pages. */
187
+ private setGuide;
188
+ /** Position (or hide, with null) the object-selection frame: border, 8
189
+ * resize handles, and a rotate knob above the top edge — all one absolutely
190
+ * positioned container rotated around its center, so a drag updates plain
191
+ * DOM (no canvas repaint). Handle geometry stays constant-size on screen. */
192
+ private setFrame;
193
+ /** Fill (or clear, with null) translucent highlight rects — e.g. a selected
194
+ * table-cell block. Reuses a pool of divs in the canvas stack. */
195
+ private setHighlight;
196
+ /** Show (or hide, with null `at`) a small action button straddling a page
197
+ * point — a touch-friendly trigger. Stops pointer propagation so clicking it
198
+ * doesn't reset the selection underneath. */
199
+ private setActionButton;
200
+ }
201
+ /**
202
+ * Compose the document schema from a base plus each plugin's `schema`
203
+ * contribution (extra marks/nodes appended). Returns `null` when no plugin
204
+ * contributes anything, so callers can keep using the base schema unchanged.
205
+ */
206
+ export declare function composeSchema(base: Schema, plugins: Iterable<EditorPlugin>): Schema | null;
207
+ //# sourceMappingURL=bapbong-editor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bapbong-editor.d.ts","sourceRoot":"","sources":["../../src/lib/bapbong-editor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { EditorPlugin } from '@shadow-garden/bapbong-contracts';
2
+ /**
3
+ * Built-in ("internal") plugins — shipped with the editor, no install needed
4
+ * (unlike external `@shadow-garden/bapbong-*` plugins the host passes via
5
+ * `{ plugins }`). The editor registers these (keyed by `name`) ahead of the
6
+ * host's plugins and exposes the ones with a richer API as typed handles
7
+ * (e.g. `editor.find`).
8
+ *
9
+ * Add a new internal plugin by importing its factory and returning it here.
10
+ * This is a FACTORY, not a shared array: each plugin holds per-editor state, so
11
+ * every editor needs its own fresh instances.
12
+ */
13
+ export declare function createBuiltins(): EditorPlugin[];
14
+ //# sourceMappingURL=built-in-plugins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"built-in-plugins.d.ts","sourceRoot":"","sources":["../../src/lib/built-in-plugins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAOrE;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,IAAI,YAAY,EAAE,CAK/C"}
@@ -0,0 +1,32 @@
1
+ import type { EditorPlugin } from '@shadow-garden/bapbong-contracts';
2
+ export interface FindState {
3
+ query: string;
4
+ /** Total matches. */
5
+ count: number;
6
+ /** 1-based index of the active match (0 when there are none). */
7
+ active: number;
8
+ }
9
+ export interface FindPlugin extends EditorPlugin {
10
+ setQuery(q: string): void;
11
+ next(): void;
12
+ prev(): void;
13
+ clear(): void;
14
+ /** Replace the active match with `text` (one undoable transaction). */
15
+ replaceCurrent(text: string): void;
16
+ /** Replace every match with `text` (one undoable transaction). */
17
+ replaceAll(text: string): void;
18
+ /** Subscribe to query/count/active changes (for the host's find bar). */
19
+ onState(cb: (s: FindState) => void): () => void;
20
+ }
21
+ /**
22
+ * Find-and-replace as a built-in ("internal") editor plugin: the editor
23
+ * instantiates it and exposes it as `editor.find`. Highlighting rides the
24
+ * decoration pipeline (read side); replace dispatches transactions (write side)
25
+ * — exercising both halves of the plugin contract. Matches are recomputed only
26
+ * when the query or the doc changes (cached for cheap per-scroll repaints).
27
+ *
28
+ * Matching is case-insensitive, within a single text node (cross-node matches
29
+ * are out of scope for v1). The host owns the find-bar UI.
30
+ */
31
+ export declare function findPlugin(): FindPlugin;
32
+ //# sourceMappingURL=find-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-plugin.d.ts","sourceRoot":"","sources":["../../src/lib/find-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,YAAY,EAGb,MAAM,kCAAkC,CAAC;AAM1C,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAW,SAAQ,YAAY;IAC9C,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,IAAI,IAAI,IAAI,CAAC;IACb,IAAI,IAAI,IAAI,CAAC;IACb,KAAK,IAAI,IAAI,CAAC;IACd,uEAAuE;IACvE,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,kEAAkE;IAClE,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,yEAAyE;IACzE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACjD;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,IAAI,UAAU,CAuGvC"}
@@ -0,0 +1,12 @@
1
+ import type { EditorPlugin } from '@shadow-garden/bapbong-contracts';
2
+ /**
3
+ * Internal plugin: **Ctrl/Cmd-click a hyperlink to open it** (the Word / Google
4
+ * Docs convention — a plain click still places the caret so links stay
5
+ * editable). Claims the press when a modified primary click lands on a `link`
6
+ * mark, so the editor skips caret placement and the URL opens in a new tab.
7
+ *
8
+ * Pairs with the export hyperlink round-trip (M6) and the accessible mirror
9
+ * (M10), whose `<a href>` elements expose the same links to assistive tech.
10
+ */
11
+ export declare function hyperlinkPlugin(): EditorPlugin;
12
+ //# sourceMappingURL=hyperlink-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hyperlink-plugin.d.ts","sourceRoot":"","sources":["../../src/lib/hyperlink-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,kCAAkC,CAAC;AAuBpF;;;;;;;;GAQG;AACH,wBAAgB,eAAe,IAAI,YAAY,CAgB9C"}
@@ -0,0 +1,38 @@
1
+ import type { EditorPlugin } from '@shadow-garden/bapbong-contracts';
2
+ type Handle = 'nw' | 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w';
3
+ interface Rect {
4
+ x: number;
5
+ y: number;
6
+ width: number;
7
+ height: number;
8
+ }
9
+ /** `point` mapped into the frame's unrotated local space (inverse-rotate
10
+ * around the rect center). Handle/knob hit-tests run in this space. */
11
+ export declare function toLocal(x: number, y: number, rect: Rect, rotation: number): {
12
+ x: number;
13
+ y: number;
14
+ };
15
+ /** Resize cursor for a handle, accounting for the frame's rotation: each
16
+ * handle points at a compass angle (n = 0°, e = 90°, …); adding the rotation
17
+ * and quantizing to 45° picks among the four bidirectional resize cursors. */
18
+ export declare function cursorFor(handle: Handle, rotation: number): string;
19
+ /** Snap: within SNAP_TOL of a cardinal angle → that angle; with Shift → 15°
20
+ * steps. Result normalized to [0, 360). */
21
+ export declare function snapAngle(deg: number, shift: boolean): number;
22
+ /** New rect for dragging `handle` by (dx, dy) from `base`: the opposite
23
+ * edge/corner stays anchored; corners keep the aspect ratio (edges are free)
24
+ * unless Shift flips the mode; both dimensions clamp to MIN_SIZE. */
25
+ export declare function resizeRect(base: Rect, handle: Handle, dx: number, dy: number, shift: boolean): Rect;
26
+ /**
27
+ * Internal plugin: click an image to select it — a frame with 8 resize handles
28
+ * and a rotate knob appears (DOM overlay; the content canvas is untouched).
29
+ * Clicking elsewhere deselects. After a doc change the selection survives only
30
+ * if the same position still holds an image (a resize commit does; edits that
31
+ * shift positions drop it), and the frame re-anchors to the fresh layout.
32
+ *
33
+ * Resize / rotate drags land in later steps (M16 R3/R5); this step is the
34
+ * selection lifecycle the drags build on.
35
+ */
36
+ export declare function imageResizePlugin(): EditorPlugin;
37
+ export {};
38
+ //# sourceMappingURL=image-resize-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-resize-plugin.d.ts","sourceRoot":"","sources":["../../src/lib/image-resize-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAMb,MAAM,kCAAkC,CAAC;AAkB1C,KAAK,MAAM,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,CAAC;AAEhE,UAAU,IAAI;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAqBD;wEACwE;AACxE,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAQpG;AAED;;+EAE+E;AAC/E,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIlE;AAED;4CAC4C;AAC5C,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAU7D;AAiBD;;sEAEsE;AACtE,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAsBnG;AAyDD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,IAAI,YAAY,CAyNhD"}
@@ -0,0 +1,13 @@
1
+ import type { EditorPlugin } from '@shadow-garden/bapbong-contracts';
2
+ /**
3
+ * Internal plugin: drag a table column's interior border to resize it. Hovering
4
+ * a border shows a `col-resize` cursor; during the drag only a vertical guide
5
+ * follows the cursor (no re-layout); on drop it rewrites just the two adjacent
6
+ * columns' `colwidth` (left +Δ, right −Δ) in one transaction — so the table's
7
+ * total width stays fixed and the change is a single undo step.
8
+ *
9
+ * Built on the editor's pointer hook + `ctx.layout`/`setCursor`/`setGuide` — no
10
+ * UI, no schema; geometry + one transaction.
11
+ */
12
+ export declare function tableResizePlugin(): EditorPlugin;
13
+ //# sourceMappingURL=table-resize-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-resize-plugin.d.ts","sourceRoot":"","sources":["../../src/lib/table-resize-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAMb,MAAM,kCAAkC,CAAC;AAgE1C;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,IAAI,YAAY,CA2EhD"}
@@ -0,0 +1,36 @@
1
+ import type { EditorPlugin } from '@shadow-garden/bapbong-contracts';
2
+ /** A selected cell + its position within the block grid (0-based). */
3
+ export interface SelectedCell {
4
+ pos: number;
5
+ row: number;
6
+ col: number;
7
+ }
8
+ /** The selected rectangular cell block as a grid. */
9
+ export interface CellBlock {
10
+ cells: SelectedCell[];
11
+ rows: number;
12
+ cols: number;
13
+ }
14
+ /** Richer handle the editor exposes as `editor.tableSelection`. */
15
+ export interface TableSelectionPlugin extends EditorPlugin {
16
+ /** Subscribe to the action trigger (icon tap / menu); receives the selected
17
+ * block (cells + grid size). Returns an unsubscribe. */
18
+ onAction(cb: (block: CellBlock) => void): () => void;
19
+ /** The current selected block, or null when there's no selection. */
20
+ block(): CellBlock | null;
21
+ /** Clear the block selection + its overlay. */
22
+ clear(): void;
23
+ }
24
+ /**
25
+ * Internal plugin: drag across table cells to select a rectangular block. A
26
+ * drag within one cell stays text selection; crossing into another cell of the
27
+ * same table claims the drag, collapses the text selection, paints the block as
28
+ * a translucent highlight, and (on release) shows a small action button at the
29
+ * block's top-right — a touch-friendly trigger to open cell properties. The
30
+ * host subscribes via `onAction` and reads the selected block via `block()`.
31
+ *
32
+ * Pointer-event based (mouse/touch/pen). The selection is editor overlay state
33
+ * (bapbong's table schema has no ProseMirror CellSelection).
34
+ */
35
+ export declare function tableSelectionPlugin(): TableSelectionPlugin;
36
+ //# sourceMappingURL=table-selection-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-selection-plugin.d.ts","sourceRoot":"","sources":["../../src/lib/table-selection-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAQb,MAAM,kCAAkC,CAAC;AAK1C,sEAAsE;AACtE,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AACD,qDAAqD;AACrD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,mEAAmE;AACnE,MAAM,WAAW,oBAAqB,SAAQ,YAAY;IACxD;6DACyD;IACzD,QAAQ,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACrD,qEAAqE;IACrE,KAAK,IAAI,SAAS,GAAG,IAAI,CAAC;IAC1B,+CAA+C;IAC/C,KAAK,IAAI,IAAI,CAAC;CACf;AA8CD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,IAAI,oBAAoB,CA0G3D"}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@shadow-garden/bapbong-editor",
3
+ "version": "0.1.0",
4
+ "description": "bapbong — canvas-rendered DOCX editor for the browser (work in progress)",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/shadowgarden-app/bapbong.git",
9
+ "directory": "packages/editor"
10
+ },
11
+ "type": "module",
12
+ "main": "./dist/index.cjs",
13
+ "module": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ "./package.json": "./package.json",
17
+ ".": {
18
+ "@shadow-garden/source": "./src/index.ts",
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js",
21
+ "require": "./dist/index.cjs",
22
+ "default": "./dist/index.js"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "!**/*.tsbuildinfo"
28
+ ],
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "nx": {
33
+ "tags": [
34
+ "scope:app"
35
+ ],
36
+ "targets": {
37
+ "build": {
38
+ "executor": "@nx/esbuild:esbuild",
39
+ "outputs": [
40
+ "{options.outputPath}"
41
+ ],
42
+ "options": {
43
+ "outputPath": "packages/editor/dist",
44
+ "main": "packages/editor/src/index.ts",
45
+ "tsConfig": "packages/editor/tsconfig.lib.json",
46
+ "format": [
47
+ "esm",
48
+ "cjs"
49
+ ],
50
+ "declarationRootDir": "packages/editor/src",
51
+ "generatePackageJson": false
52
+ }
53
+ }
54
+ }
55
+ },
56
+ "dependencies": {
57
+ "prosemirror-model": "^1.25.7",
58
+ "@shadow-garden/bapbong-commands": "^0.1.0",
59
+ "@shadow-garden/bapbong-contracts": "^0.1.0",
60
+ "@shadow-garden/bapbong-selection": "^0.1.0",
61
+ "@shadow-garden/bapbong-model": "^0.1.0",
62
+ "@shadow-garden/bapbong-input-bridge": "^0.1.0",
63
+ "@shadow-garden/bapbong-view": "^0.1.0"
64
+ }
65
+ }