@sobree/core 0.1.23 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/editor/index.d.ts +41 -16
- package/dist/editor/types.d.ts +21 -0
- package/dist/editor/view/docRenderer/anchorLayer.d.ts +10 -0
- package/dist/index.js +1535 -1432
- package/dist/index.js.map +1 -1
- package/dist/sobree.d.ts +1 -0
- package/package.json +1 -1
package/dist/editor/index.d.ts
CHANGED
|
@@ -20,22 +20,6 @@ export type { ApiRangeType, BlockInfo, ChangePayload, CommandBus, CommandDefinit
|
|
|
20
20
|
export type { CellRef, InsertAt, InsertColumnOpts, InsertRowOpts, MergeCellsOpts, } from './types';
|
|
21
21
|
export { runsLength } from '../doc/runs';
|
|
22
22
|
export type { RunPropertiesPatch };
|
|
23
|
-
/**
|
|
24
|
-
* Public editor surface.
|
|
25
|
-
*
|
|
26
|
-
* Two entry points for every operation:
|
|
27
|
-
* - Core methods take `BlockRef` / `InlinePosition` / `Range` and
|
|
28
|
-
* return `EditResult`. These are the wire-callable API — same
|
|
29
|
-
* contract for in-process toolbars, headless Y peers (HeadlessSobree),
|
|
30
|
-
* and future MCP wrappers.
|
|
31
|
-
* - "AtSelection" sugar reads the live DOM selection, builds the
|
|
32
|
-
* position/range for you, and delegates to the core. Use these in
|
|
33
|
-
* in-process UI code.
|
|
34
|
-
*
|
|
35
|
-
* Mutations enforce optimistic locking via block `version` numbers.
|
|
36
|
-
* Conflicts return `{ ok: false, error: { code: "optimistic-lock", … } }`
|
|
37
|
-
* rather than throwing.
|
|
38
|
-
*/
|
|
39
23
|
export declare class Editor {
|
|
40
24
|
readonly host: HTMLElement;
|
|
41
25
|
readonly selection: EditorSelection;
|
|
@@ -160,6 +144,21 @@ export declare class Editor {
|
|
|
160
144
|
* and `emitChangeNow` sync only when this flag is set.
|
|
161
145
|
*/
|
|
162
146
|
private domDirty;
|
|
147
|
+
/**
|
|
148
|
+
* Ids of editable textbox frames whose DOM the user has edited since
|
|
149
|
+
* the last sync. Frames live in the floating overlay (outside the
|
|
150
|
+
* body content hosts), so they need their own read-back path —
|
|
151
|
+
* `syncFromDom` re-serialises each dirty frame into
|
|
152
|
+
* `anchoredFrames[id].content.body`.
|
|
153
|
+
*/
|
|
154
|
+
private readonly dirtyFrameIds;
|
|
155
|
+
/**
|
|
156
|
+
* Set by `syncFromDom` when the pending change was a pure live frame
|
|
157
|
+
* keystroke; read (and reset) by `emitChangeNow` into the change
|
|
158
|
+
* payload's `liveFrameEdit`. Lets the host skip the overlay repaint
|
|
159
|
+
* that would clobber the caret, while still repainting on undo/remote.
|
|
160
|
+
*/
|
|
161
|
+
private pendingLiveFrameEdit;
|
|
163
162
|
/**
|
|
164
163
|
* Kernel seam handed to the behaviour modules (`ops/*`, `query`). Built
|
|
165
164
|
* once in the constructor; closes over this instance's privates so the
|
|
@@ -482,6 +481,32 @@ export declare class Editor {
|
|
|
482
481
|
*/
|
|
483
482
|
private ensureCurrent;
|
|
484
483
|
private syncFromDom;
|
|
484
|
+
/**
|
|
485
|
+
* Re-read the DOM of each dirty editable textbox frame into the AST.
|
|
486
|
+
* The frame element IS the serialization host (the block renderer paints
|
|
487
|
+
* its body directly into it), so `serializeHostsToDocument([el])` yields
|
|
488
|
+
* the same `Block[]` shape as a body host. Matched to the AST frame by
|
|
489
|
+
* its stable `data-anchor-id`. Pure body swap — geometry/anchor untouched.
|
|
490
|
+
*/
|
|
491
|
+
private syncFramesFromDom;
|
|
492
|
+
/**
|
|
493
|
+
* The id of the editable textbox frame the caret currently sits in, or
|
|
494
|
+
* null when the selection is in ordinary body flow. Used to route an
|
|
495
|
+
* `input` event to the frame read-back instead of the body read-back.
|
|
496
|
+
*/
|
|
497
|
+
private editedFrameId;
|
|
498
|
+
/**
|
|
499
|
+
* Toggle a mark on the caret inside an editable textbox frame, natively
|
|
500
|
+
* (`document.execCommand`), so the body-selection mark path doesn't have
|
|
501
|
+
* to understand frame coordinates. The resulting `<b>`/`<i>`/`<u>` tags
|
|
502
|
+
* round-trip through the frame read-back (the inline serializer maps them
|
|
503
|
+
* to run properties). Returns false when the caret isn't in a frame, so
|
|
504
|
+
* the mark command falls back to the body path.
|
|
505
|
+
*/
|
|
506
|
+
applyFrameMark(tag: string): boolean;
|
|
507
|
+
/** Active state of `tag` at a frame caret (toolbar highlight), or null
|
|
508
|
+
* when the caret isn't in a frame. */
|
|
509
|
+
frameMarkActive(tag: string): boolean | null;
|
|
485
510
|
/**
|
|
486
511
|
* Schedule a DOM-driven change emit. Called from the `input` listener
|
|
487
512
|
* when the user types — the DOM is the source of truth and we sync the
|
package/dist/editor/types.d.ts
CHANGED
|
@@ -94,6 +94,15 @@ export interface ChangePayload {
|
|
|
94
94
|
document: SobreeDocument;
|
|
95
95
|
revision: number;
|
|
96
96
|
documentVersion: number;
|
|
97
|
+
/**
|
|
98
|
+
* True when this change came from a live keystroke inside an editable
|
|
99
|
+
* textbox frame — the frame's DOM already holds the edit, so the host
|
|
100
|
+
* can skip repainting the floating overlay (which would clobber the
|
|
101
|
+
* caret). Absent / false for body edits, API mutations, undo/redo, and
|
|
102
|
+
* remote (Y.Doc-driven) changes — those re-render from the AST, so the
|
|
103
|
+
* overlay IS stale and must repaint.
|
|
104
|
+
*/
|
|
105
|
+
liveFrameEdit?: boolean;
|
|
97
106
|
}
|
|
98
107
|
/** Summary of a top-level block, for `getBlocks()` and list-style UIs. */
|
|
99
108
|
export interface BlockInfo {
|
|
@@ -171,6 +180,18 @@ export interface EditorLike {
|
|
|
171
180
|
expect?: Record<string, number>;
|
|
172
181
|
}): EditResult<void>;
|
|
173
182
|
readonly selection: EditorSelectionLike;
|
|
183
|
+
/**
|
|
184
|
+
* When the caret sits in an editable textbox frame, toggle a mark
|
|
185
|
+
* (`"strong"`/`"em"`/`"u"`/`"s"`/`"sup"`/`"sub"`) natively on that
|
|
186
|
+
* frame's selection — the frame read-back captures the result. Returns
|
|
187
|
+
* `true` when handled (caret was in a frame), `false` otherwise so the
|
|
188
|
+
* caller falls back to the body mark path. Optional: implemented by the
|
|
189
|
+
* DOM Editor, absent on headless peers (which have no frame DOM).
|
|
190
|
+
*/
|
|
191
|
+
applyFrameMark?(tag: string): boolean;
|
|
192
|
+
/** Active state of `tag` at the frame caret, or `null` when the caret
|
|
193
|
+
* isn't in a frame (caller uses the body `isMarkActive`). */
|
|
194
|
+
frameMarkActive?(tag: string): boolean | null;
|
|
174
195
|
}
|
|
175
196
|
export type EditorEvent = "change" | "selection" | "keydown" | "track-changes-change";
|
|
176
197
|
export type EditorEventPayload = {
|
|
@@ -16,6 +16,16 @@ export interface AnchorLayerContext {
|
|
|
16
16
|
* stacked text (test/headless fallback).
|
|
17
17
|
*/
|
|
18
18
|
renderBody?: (blocks: Block[], host: HTMLElement) => void;
|
|
19
|
+
/**
|
|
20
|
+
* When true, textbox frames become editable islands: the frame turns
|
|
21
|
+
* into its own `contentEditable` host (`pointer-events:auto`) so the
|
|
22
|
+
* user can click in and type. The editor wires the read-back that
|
|
23
|
+
* serialises the frame's DOM into `anchoredFrames[id].content.body`.
|
|
24
|
+
* False / absent → display-only overlay (read-only mode, decorations,
|
|
25
|
+
* headless). Pictures, shapes, and groups stay non-interactive
|
|
26
|
+
* regardless — only textbox prose is editable.
|
|
27
|
+
*/
|
|
28
|
+
editable?: boolean;
|
|
19
29
|
}
|
|
20
30
|
/**
|
|
21
31
|
* Build the per-page anchor layer. Returns a single `<div>` whose
|