@inkami/blx 0.17.3
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 +98 -0
- package/dist/attributed-string/attributed-string.d.ts +37 -0
- package/dist/attributed-string/index.d.ts +2 -0
- package/dist/attributed-string/types.d.ts +13 -0
- package/dist/autocomplete/autocomplete-menu.d.ts +24 -0
- package/dist/autocomplete/index.d.ts +2 -0
- package/dist/autocomplete/types.d.ts +29 -0
- package/dist/blob-store/index.d.ts +32 -0
- package/dist/blob-store/types.d.ts +24 -0
- package/dist/block/block-controller.d.ts +205 -0
- package/dist/block/index.d.ts +2 -0
- package/dist/block/types.d.ts +15 -0
- package/dist/block-action-menu/block-action-menu.d.ts +62 -0
- package/dist/block-action-menu/index.d.ts +2 -0
- package/dist/block-context-menu/block-context-menu.d.ts +33 -0
- package/dist/block-context-menu/index.d.ts +2 -0
- package/dist/blx.js +16245 -0
- package/dist/blx.js.map +1 -0
- package/dist/caret/custom-caret.d.ts +30 -0
- package/dist/clipboard/clipboard-manager.d.ts +28 -0
- package/dist/clipboard/index.d.ts +2 -0
- package/dist/clipboard/paste-parser.d.ts +30 -0
- package/dist/commands/command-registry.d.ts +26 -0
- package/dist/commands/index.d.ts +2 -0
- package/dist/crdt/automerge-sync.d.ts +157 -0
- package/dist/crdt/crdt-log.d.ts +57 -0
- package/dist/crdt/index.d.ts +8 -0
- package/dist/crdt/remote-cursor-renderer.d.ts +28 -0
- package/dist/crdt/replay-ui.d.ts +34 -0
- package/dist/crdt/types.d.ts +47 -0
- package/dist/date-picker/agenda-utils.d.ts +37 -0
- package/dist/date-picker/date-parser.d.ts +25 -0
- package/dist/date-picker/date-picker.d.ts +115 -0
- package/dist/date-picker/index.d.ts +6 -0
- package/dist/drag-handle/drag-handle.d.ts +105 -0
- package/dist/drag-handle/index.d.ts +1 -0
- package/dist/editor.d.ts +864 -0
- package/dist/emoji-picker/emoji-data.d.ts +22 -0
- package/dist/emoji-picker/emoji-picker.d.ts +43 -0
- package/dist/emoji-picker/index.d.ts +3 -0
- package/dist/event-bus/event-bus.d.ts +14 -0
- package/dist/event-bus/index.d.ts +2 -0
- package/dist/event-bus/types.d.ts +320 -0
- package/dist/fold/fold-manager.d.ts +27 -0
- package/dist/fold/index.d.ts +2 -0
- package/dist/grapheme.d.ts +28 -0
- package/dist/index.d.ts +62 -0
- package/dist/inline-toolbar/highlight-picker.d.ts +41 -0
- package/dist/inline-toolbar/index.d.ts +5 -0
- package/dist/inline-toolbar/inline-toolbar.d.ts +97 -0
- package/dist/inline-toolbar/link-popover.d.ts +24 -0
- package/dist/keybindings/defaults.d.ts +2 -0
- package/dist/keybindings/index.d.ts +4 -0
- package/dist/keybindings/key-matcher.d.ts +41 -0
- package/dist/keybindings/types.d.ts +6 -0
- package/dist/keybindings/vim.d.ts +2 -0
- package/dist/markdown/block-shortcuts.d.ts +35 -0
- package/dist/markdown/emoticon-substitutions.d.ts +22 -0
- package/dist/markdown/export.d.ts +5 -0
- package/dist/markdown/index.d.ts +4 -0
- package/dist/markdown/inline-shortcuts.d.ts +13 -0
- package/dist/markdown/types.d.ts +29 -0
- package/dist/plugins/blockquote.d.ts +6 -0
- package/dist/plugins/checklist.d.ts +18 -0
- package/dist/plugins/code.d.ts +50 -0
- package/dist/plugins/excalidraw.d.ts +26 -0
- package/dist/plugins/heading.d.ts +20 -0
- package/dist/plugins/horizontal-rule.d.ts +5 -0
- package/dist/plugins/image.d.ts +19 -0
- package/dist/plugins/index.d.ts +28 -0
- package/dist/plugins/list.d.ts +18 -0
- package/dist/plugins/mermaid.d.ts +11 -0
- package/dist/plugins/paragraph.d.ts +18 -0
- package/dist/plugins/prism-loader.d.ts +16 -0
- package/dist/plugins/render-utils.d.ts +43 -0
- package/dist/plugins/table.d.ts +22 -0
- package/dist/plugins/youtube.d.ts +21 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/schema-registry.d.ts +35 -0
- package/dist/schema/types.d.ts +67 -0
- package/dist/search/index.d.ts +4 -0
- package/dist/search/search-engine.d.ts +39 -0
- package/dist/search/search-ui.d.ts +59 -0
- package/dist/selection/index.d.ts +3 -0
- package/dist/selection/selection-manager.d.ts +41 -0
- package/dist/selection/types.d.ts +20 -0
- package/dist/slash-menu/index.d.ts +2 -0
- package/dist/slash-menu/slash-menu.d.ts +27 -0
- package/dist/table-of-contents/index.d.ts +1 -0
- package/dist/table-of-contents/table-of-contents.d.ts +74 -0
- package/dist/transaction/index.d.ts +2 -0
- package/dist/transaction/transaction.d.ts +29 -0
- package/dist/transaction/types.d.ts +40 -0
- package/dist/types.d.ts +21 -0
- package/dist/ui/context-menu.d.ts +103 -0
- package/dist/ui/dismiss.d.ts +33 -0
- package/dist/ui/dropdown.d.ts +81 -0
- package/dist/ui/floating-scroll-manager.d.ts +50 -0
- package/dist/ui/index.d.ts +18 -0
- package/dist/ui/modal.d.ts +109 -0
- package/dist/ui/popover.d.ts +59 -0
- package/dist/ui/position-fixed.d.ts +35 -0
- package/dist/ui/toast.d.ts +60 -0
- package/dist/ui/tooltip.d.ts +64 -0
- package/dist/undo/index.d.ts +5 -0
- package/dist/undo/jump-list.d.ts +37 -0
- package/dist/undo/types.d.ts +49 -0
- package/dist/undo/undo-manager.d.ts +47 -0
- package/dist/util/icons.d.ts +13 -0
- package/dist/util/id.d.ts +10 -0
- package/dist/viewport/block-viewport.d.ts +106 -0
- package/dist/viewport/index.d.ts +2 -0
- package/package.json +66 -0
- package/style.css +4670 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare class CustomCaret {
|
|
2
|
+
private _el;
|
|
3
|
+
private _container;
|
|
4
|
+
private _x;
|
|
5
|
+
private _y;
|
|
6
|
+
private _currentBlock;
|
|
7
|
+
private _suppressNextSnap;
|
|
8
|
+
private _instantNextMove;
|
|
9
|
+
private _enabled;
|
|
10
|
+
private _activeTimer;
|
|
11
|
+
private _trailCleanup;
|
|
12
|
+
private _destroyed;
|
|
13
|
+
private _bound_onSelectionChange;
|
|
14
|
+
private _bound_onKeyDown;
|
|
15
|
+
private _bound_onScroll;
|
|
16
|
+
private _bound_onResize;
|
|
17
|
+
private _resizeObserver;
|
|
18
|
+
constructor(container: HTMLElement);
|
|
19
|
+
hide(): void;
|
|
20
|
+
setEnabled(enabled: boolean): void;
|
|
21
|
+
destroy(): void;
|
|
22
|
+
private _onSelectionChange;
|
|
23
|
+
private _onKeyDown;
|
|
24
|
+
private _onScroll;
|
|
25
|
+
private _onResize;
|
|
26
|
+
private _update;
|
|
27
|
+
private _moveTo;
|
|
28
|
+
private _cancelTrail;
|
|
29
|
+
private _animateSnap;
|
|
30
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clipboard manager: handles copy/cut/paste of blocks.
|
|
3
|
+
*
|
|
4
|
+
* Uses the Clipboard API to write both a custom MIME type
|
|
5
|
+
* (`application/x-blx-blocks+json`) for lossless block round-trips
|
|
6
|
+
* and `text/plain` (Markdown) for interop with other apps.
|
|
7
|
+
*
|
|
8
|
+
* Since the Clipboard API only supports `text/plain` and `text/html`,
|
|
9
|
+
* we embed the JSON payload inside an HTML comment in the `text/html`
|
|
10
|
+
* slot so both rich paste (back into BLX) and plain paste (into other
|
|
11
|
+
* apps) work seamlessly.
|
|
12
|
+
*/
|
|
13
|
+
import type { Block, BlockJSON } from "../types";
|
|
14
|
+
/**
|
|
15
|
+
* Copy an array of blocks to the system clipboard.
|
|
16
|
+
* Writes both `text/plain` (Markdown) and `text/html` (with embedded JSON).
|
|
17
|
+
*/
|
|
18
|
+
export declare function copyBlocksToClipboard(blocks: ReadonlyArray<Block>): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Read BLX block data from the clipboard.
|
|
21
|
+
* Returns the array of BlockJSON if found, or null if the clipboard
|
|
22
|
+
* contains no BLX data.
|
|
23
|
+
*/
|
|
24
|
+
export declare function readBlocksFromClipboard(): Promise<BlockJSON[] | null>;
|
|
25
|
+
/**
|
|
26
|
+
* Extract BLX block JSON from an HTML string (e.g. from ClipboardEvent.clipboardData).
|
|
27
|
+
*/
|
|
28
|
+
export declare function extractBlocksFromHtml(html: string): BlockJSON[] | null;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse external paste content (HTML or plain text) into BlockJSON arrays.
|
|
3
|
+
*
|
|
4
|
+
* When pasting from external apps (browsers, Google Docs, VS Code, etc.),
|
|
5
|
+
* the clipboard may contain HTML structure or multi-paragraph plain text.
|
|
6
|
+
* This module converts that content into BLX blocks so the editor can
|
|
7
|
+
* create proper multi-block structure instead of dumping everything into
|
|
8
|
+
* a single block.
|
|
9
|
+
*/
|
|
10
|
+
import type { BlockJSON } from "../types";
|
|
11
|
+
/**
|
|
12
|
+
* Parse an HTML string from the clipboard into an array of BlockJSON.
|
|
13
|
+
* Returns null if the HTML doesn't contain meaningful block structure
|
|
14
|
+
* (i.e., it's just a single text fragment with no block-level elements).
|
|
15
|
+
*/
|
|
16
|
+
export declare function htmlToBlocks(html: string): BlockJSON[] | null;
|
|
17
|
+
/**
|
|
18
|
+
* Parse a Markdown string into an array of BlockJSON.
|
|
19
|
+
* Handles headings, lists, checklists, code fences, horizontal rules,
|
|
20
|
+
* and inline formatting (**bold**, *italic*, `code`, ~~strikethrough~~, [links](url)).
|
|
21
|
+
* Returns null if the text doesn't look like Markdown (no Markdown-specific
|
|
22
|
+
* syntax found) so the caller can fall back to plain text splitting.
|
|
23
|
+
*/
|
|
24
|
+
export declare function markdownToBlocks(text: string): BlockJSON[] | null;
|
|
25
|
+
/**
|
|
26
|
+
* Split multi-paragraph plain text into BlockJSON arrays.
|
|
27
|
+
* Paragraphs are separated by blank lines (`\n\n`).
|
|
28
|
+
* Returns null if the text is a single paragraph (let default paste handle it).
|
|
29
|
+
*/
|
|
30
|
+
export declare function plainTextToBlocks(text: string): BlockJSON[] | null;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface CommandEntry {
|
|
2
|
+
description: string;
|
|
3
|
+
handler: (args?: string) => void;
|
|
4
|
+
}
|
|
5
|
+
export interface ExternalCommandEntry {
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
description: string;
|
|
9
|
+
execute: (args?: string) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare class CommandRegistry {
|
|
12
|
+
private commands;
|
|
13
|
+
register(name: string, entry: CommandEntry): void;
|
|
14
|
+
unregister(name: string): void;
|
|
15
|
+
execute(name: string, args?: string): void;
|
|
16
|
+
list(): Array<{
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
}>;
|
|
20
|
+
has(name: string): boolean;
|
|
21
|
+
filter(prefix: string): Array<{
|
|
22
|
+
name: string;
|
|
23
|
+
description: string;
|
|
24
|
+
}>;
|
|
25
|
+
listFull(): ExternalCommandEntry[];
|
|
26
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AutomergeSync — bridges an Automerge CRDT document with a BLX Editor.
|
|
3
|
+
*
|
|
4
|
+
* Maintains a parallel Automerge document that mirrors the editor's block
|
|
5
|
+
* model. Local edits are translated into Automerge mutations (splice for
|
|
6
|
+
* text, mark/unmark for rich-text attributes, object changes for metadata).
|
|
7
|
+
* Remote Automerge changes are applied back to the editor.
|
|
8
|
+
*
|
|
9
|
+
* ## Automerge document schema
|
|
10
|
+
*
|
|
11
|
+
* ```
|
|
12
|
+
* {
|
|
13
|
+
* blockOrder: string[], // ordered block IDs
|
|
14
|
+
* blocks: {
|
|
15
|
+
* [blockId]: {
|
|
16
|
+
* type: string,
|
|
17
|
+
* text: string, // Automerge collaborative text
|
|
18
|
+
* metadata: Record<string, any>,
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* Rich-text attributes (bold, italic, etc.) are stored as Automerge marks
|
|
25
|
+
* on the `text` field of each block.
|
|
26
|
+
*/
|
|
27
|
+
import * as Automerge from "@automerge/automerge";
|
|
28
|
+
import type { EventBus } from "../event-bus";
|
|
29
|
+
import type { EditorEventMap } from "../event-bus";
|
|
30
|
+
import type { Block } from "../types";
|
|
31
|
+
import { AttributedString } from "../attributed-string";
|
|
32
|
+
export interface AutomergeBlockData {
|
|
33
|
+
type: string;
|
|
34
|
+
text: string;
|
|
35
|
+
metadata: Record<string, unknown>;
|
|
36
|
+
}
|
|
37
|
+
export interface AutomergeDocSchema {
|
|
38
|
+
/** Ordered list of block IDs (determines document order) */
|
|
39
|
+
blockOrder: string[];
|
|
40
|
+
/** Map of blockId → block data */
|
|
41
|
+
blocks: Record<string, AutomergeBlockData>;
|
|
42
|
+
}
|
|
43
|
+
export interface AutomergeSyncOptions {
|
|
44
|
+
/** Optional hex actor ID for this peer (auto-generated if omitted) */
|
|
45
|
+
actorId?: string;
|
|
46
|
+
}
|
|
47
|
+
export declare class AutomergeSync {
|
|
48
|
+
private doc;
|
|
49
|
+
/** When true, editor events are ignored (we're applying remote changes) */
|
|
50
|
+
private _applying;
|
|
51
|
+
/**
|
|
52
|
+
* Cache of per-block span attribute signatures. Used to detect whether
|
|
53
|
+
* formatting changed on a keystroke so we can skip the expensive
|
|
54
|
+
* unmark-all / re-mark-all cycle when only text was inserted/deleted.
|
|
55
|
+
*/
|
|
56
|
+
private _spanSigCache;
|
|
57
|
+
private _getBlocks;
|
|
58
|
+
private _getBlock;
|
|
59
|
+
private _addBlock;
|
|
60
|
+
private _removeBlock;
|
|
61
|
+
private _moveBlock;
|
|
62
|
+
private _updateBlockContent;
|
|
63
|
+
private _replaceAllBlocks;
|
|
64
|
+
private _bus;
|
|
65
|
+
private _unsubs;
|
|
66
|
+
constructor(editorApi: {
|
|
67
|
+
getBlocks: () => ReadonlyArray<Block>;
|
|
68
|
+
getBlock: (id: string) => Block | undefined;
|
|
69
|
+
addBlock: (block: Block, index?: number) => Block;
|
|
70
|
+
removeBlock: (id: string) => boolean;
|
|
71
|
+
moveBlock: (id: string, toIndex: number) => boolean;
|
|
72
|
+
updateBlockContent: (blockId: string, content: AttributedString, metadata?: Record<string, unknown>, blockType?: string) => void;
|
|
73
|
+
replaceAllBlocks?: (blocks: Block[]) => void;
|
|
74
|
+
bus: EventBus<EditorEventMap>;
|
|
75
|
+
}, options?: AutomergeSyncOptions);
|
|
76
|
+
/** Get the underlying Automerge document (read-only view) */
|
|
77
|
+
getDoc(): Automerge.Doc<AutomergeDocSchema>;
|
|
78
|
+
/** Get the actor ID of this Automerge instance */
|
|
79
|
+
getActorId(): string;
|
|
80
|
+
/** Save the document to a binary blob for persistence */
|
|
81
|
+
save(): Uint8Array;
|
|
82
|
+
/** Load a document from a previously saved binary blob */
|
|
83
|
+
load(data: Uint8Array, actorId?: string): void;
|
|
84
|
+
/**
|
|
85
|
+
* Merge another Automerge document into this one. Useful for
|
|
86
|
+
* initial sync when peers exchange full document state.
|
|
87
|
+
*/
|
|
88
|
+
merge(otherDoc: Automerge.Doc<AutomergeDocSchema>): void;
|
|
89
|
+
/**
|
|
90
|
+
* Generate a sync message to send to a remote peer.
|
|
91
|
+
* Returns [updatedSyncState, message] where message may be null
|
|
92
|
+
* if there's nothing new to send.
|
|
93
|
+
*/
|
|
94
|
+
generateSyncMessage(syncState: Automerge.SyncState): [Automerge.SyncState, Uint8Array | null];
|
|
95
|
+
/**
|
|
96
|
+
* Receive a sync message from a remote peer.
|
|
97
|
+
* Applies remote changes to both the Automerge doc and the editor.
|
|
98
|
+
* Returns the updated sync state.
|
|
99
|
+
*/
|
|
100
|
+
receiveSyncMessage(syncState: Automerge.SyncState, message: Uint8Array): Automerge.SyncState;
|
|
101
|
+
/**
|
|
102
|
+
* Apply raw Automerge changes (from `getChanges` / `getAllChanges`).
|
|
103
|
+
* Useful for replaying history or applying batched changes.
|
|
104
|
+
*/
|
|
105
|
+
applyChanges(changes: Uint8Array[]): void;
|
|
106
|
+
/** Get all changes since the document was created */
|
|
107
|
+
getAllChanges(): Uint8Array[];
|
|
108
|
+
/** Get changes since a set of heads */
|
|
109
|
+
getChanges(heads: Automerge.Heads): Uint8Array[];
|
|
110
|
+
/** Get the current document heads (for change tracking) */
|
|
111
|
+
getHeads(): Automerge.Heads;
|
|
112
|
+
/** Create a new sync state for a peer connection */
|
|
113
|
+
static initSyncState(): Automerge.SyncState;
|
|
114
|
+
/** Fork a copy of the document (for testing / branching) */
|
|
115
|
+
fork(): Automerge.Doc<AutomergeDocSchema>;
|
|
116
|
+
/** Clean up event listeners */
|
|
117
|
+
destroy(): void;
|
|
118
|
+
/** Whether we are currently applying remote changes (suppressing local events) */
|
|
119
|
+
get isApplying(): boolean;
|
|
120
|
+
/** Populate the Automerge doc from the current editor state */
|
|
121
|
+
private initFromEditor;
|
|
122
|
+
/** Listen to editor events and mirror changes to the Automerge doc */
|
|
123
|
+
private setupListeners;
|
|
124
|
+
private handleContentChanged;
|
|
125
|
+
private handleBlockAdded;
|
|
126
|
+
private handleBlockRemoved;
|
|
127
|
+
private handleBlockMoved;
|
|
128
|
+
private handleBlockTypeChanged;
|
|
129
|
+
/**
|
|
130
|
+
* Diff two Automerge document versions and apply the differences
|
|
131
|
+
* to the editor. This is called after receiving remote changes.
|
|
132
|
+
*/
|
|
133
|
+
private diffAndApply;
|
|
134
|
+
/**
|
|
135
|
+
* Full replace: apply the entire Automerge doc to the editor.
|
|
136
|
+
* Used after `load()`.
|
|
137
|
+
*/
|
|
138
|
+
private applyDocToEditor;
|
|
139
|
+
/**
|
|
140
|
+
* Apply Automerge marks from AttributedString spans.
|
|
141
|
+
* Must be called inside an `Automerge.change()` callback.
|
|
142
|
+
*/
|
|
143
|
+
private applyMarksFromSpans;
|
|
144
|
+
/**
|
|
145
|
+
* Convert an Automerge block's text + marks back to an AttributedString.
|
|
146
|
+
*
|
|
147
|
+
* Uses an efficient O(M log M) interval sweep (M = mark count) rather
|
|
148
|
+
* than the previous O(M × N) per-character approach (N = text length).
|
|
149
|
+
*/
|
|
150
|
+
private blockDataToAttributedString;
|
|
151
|
+
}
|
|
152
|
+
/** Simple text diff: find minimal insert/delete to go from `before` to `after` */
|
|
153
|
+
export declare function diffText(before: string, after: string): {
|
|
154
|
+
pos: number;
|
|
155
|
+
del: number;
|
|
156
|
+
ins: string;
|
|
157
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { UndoAction, CursorState } from "../undo/types";
|
|
2
|
+
import type { BlockJSON } from "../types";
|
|
3
|
+
import type { CrdtOperation, CrdtLogSnapshot } from "./types";
|
|
4
|
+
/**
|
|
5
|
+
* Append-only operation log for CRDT-style change tracking.
|
|
6
|
+
*
|
|
7
|
+
* Captures an initial editor snapshot and every subsequent edit as an
|
|
8
|
+
* immutable operation entry. Supports compound grouping (e.g., block split
|
|
9
|
+
* = content change + block add) so multi-action edits replay atomically.
|
|
10
|
+
*
|
|
11
|
+
* Each operation is tagged with a `peerId` to identify which collaborating
|
|
12
|
+
* peer authored the change.
|
|
13
|
+
*/
|
|
14
|
+
export declare class CrdtLog {
|
|
15
|
+
private initialState;
|
|
16
|
+
private operations;
|
|
17
|
+
private nextId;
|
|
18
|
+
/** The local peer ID attached to new operations */
|
|
19
|
+
private _localPeerId;
|
|
20
|
+
private _grouping;
|
|
21
|
+
private _groupActions;
|
|
22
|
+
private _groupBeforeCursor;
|
|
23
|
+
/** Set the local peer ID for subsequent operations */
|
|
24
|
+
setLocalPeerId(peerId: string): void;
|
|
25
|
+
/** Get the current local peer ID */
|
|
26
|
+
getLocalPeerId(): string | null;
|
|
27
|
+
/** Capture the initial editor state */
|
|
28
|
+
setInitialState(blocks: BlockJSON[]): void;
|
|
29
|
+
/** Start grouping actions into a single atomic operation */
|
|
30
|
+
beginGroup(beforeCursor: CursorState | null): void;
|
|
31
|
+
/** Whether a compound group is currently active */
|
|
32
|
+
get isGrouping(): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Record an action. If a compound group is active, the action is
|
|
35
|
+
* buffered until `endGroup()` is called.
|
|
36
|
+
*/
|
|
37
|
+
record(action: UndoAction, beforeCursor: CursorState | null, afterCursor: CursorState | null): void;
|
|
38
|
+
/**
|
|
39
|
+
* Append an externally-built operation (e.g. from a remote peer).
|
|
40
|
+
* Assigns a new sequential ID but preserves the original peerId.
|
|
41
|
+
*/
|
|
42
|
+
appendRemote(op: Omit<CrdtOperation, "id">): void;
|
|
43
|
+
/** End compound group and push as a single atomic operation */
|
|
44
|
+
endGroup(afterCursor: CursorState | null): void;
|
|
45
|
+
/** Total number of operations in the log */
|
|
46
|
+
getOperationCount(): number;
|
|
47
|
+
/** Get a specific operation by index */
|
|
48
|
+
getOperation(index: number): CrdtOperation | undefined;
|
|
49
|
+
/** Get the initial editor state */
|
|
50
|
+
getInitialState(): BlockJSON[];
|
|
51
|
+
/** Get all operations */
|
|
52
|
+
getOperations(): readonly CrdtOperation[];
|
|
53
|
+
/** Clear all recorded operations (keeps initial state) */
|
|
54
|
+
clear(): void;
|
|
55
|
+
/** Serialize the log */
|
|
56
|
+
toJSON(): CrdtLogSnapshot;
|
|
57
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { CrdtLog } from "./crdt-log";
|
|
2
|
+
export { ReplayUI } from "./replay-ui";
|
|
3
|
+
export { renderRemoteCursors, clearRemoteCursors } from "./remote-cursor-renderer";
|
|
4
|
+
export { AutomergeSync, diffText } from "./automerge-sync";
|
|
5
|
+
export type { AutomergeDocSchema, AutomergeBlockData, AutomergeSyncOptions } from "./automerge-sync";
|
|
6
|
+
export type { ReplayUICallbacks } from "./replay-ui";
|
|
7
|
+
export type { CrdtOperation, CrdtLogSnapshot, CrdtPeer, RemoteCursor } from "./types";
|
|
8
|
+
export { PEER_COLORS } from "./types";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote cursor renderer — positions colored caret indicators and selection
|
|
3
|
+
* highlights as absolute overlays above the editor, using getBoundingClientRect
|
|
4
|
+
* to compute positions.
|
|
5
|
+
*
|
|
6
|
+
* Cursors are rendered into a dedicated overlay container that sits outside
|
|
7
|
+
* the contenteditable DOM. This avoids contaminating textContent,
|
|
8
|
+
* Range.toString(), and TreeWalker traversals that the editor relies on
|
|
9
|
+
* for offset calculations.
|
|
10
|
+
*/
|
|
11
|
+
import type { RemoteCursor } from "./types";
|
|
12
|
+
/**
|
|
13
|
+
* Render remote cursors (and optional selection highlights) as absolutely
|
|
14
|
+
* positioned overlays.
|
|
15
|
+
*
|
|
16
|
+
* @param contentEditable - The block's contenteditable element (used for position measurement)
|
|
17
|
+
* @param cursors - Array of remote cursors targeting this block
|
|
18
|
+
*/
|
|
19
|
+
export declare function renderRemoteCursors(contentEditable: HTMLElement, cursors: RemoteCursor[]): void;
|
|
20
|
+
/** Remove all remote cursor elements for a specific block (or all blocks) */
|
|
21
|
+
export declare function clearRemoteCursors(contentEditable: HTMLElement): void;
|
|
22
|
+
/** Remove cursor overlays for a non-text block (uses the block container directly) */
|
|
23
|
+
export declare function clearRemoteCursorsFromContainer(container: HTMLElement): void;
|
|
24
|
+
/**
|
|
25
|
+
* Render a colored left-border indicator on a block to show it's part of
|
|
26
|
+
* a remote peer's multi-block selection.
|
|
27
|
+
*/
|
|
28
|
+
export declare function renderBlockSelectionBorder(container: HTMLElement, cursors: RemoteCursor[]): void;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replay UI — slider + transport controls anchored below the editor.
|
|
3
|
+
*
|
|
4
|
+
* Shows a timeline slider and play/pause/step controls that let the user
|
|
5
|
+
* scrub through the CRDT operation log to see how the document was built.
|
|
6
|
+
*/
|
|
7
|
+
export interface ReplayUICallbacks {
|
|
8
|
+
onSeek(position: number): void;
|
|
9
|
+
onPlay(): void;
|
|
10
|
+
onPause(): void;
|
|
11
|
+
onStepForward(): void;
|
|
12
|
+
onStepBackward(): void;
|
|
13
|
+
onExit(): void;
|
|
14
|
+
getIsPlaying(): boolean;
|
|
15
|
+
getCurrentPosition(): number;
|
|
16
|
+
getTotalOperations(): number;
|
|
17
|
+
}
|
|
18
|
+
export declare class ReplayUI {
|
|
19
|
+
private container;
|
|
20
|
+
private slider;
|
|
21
|
+
private playBtn;
|
|
22
|
+
private stepBackBtn;
|
|
23
|
+
private stepFwdBtn;
|
|
24
|
+
private exitBtn;
|
|
25
|
+
private posLabel;
|
|
26
|
+
private callbacks;
|
|
27
|
+
constructor(editorRoot: HTMLElement, callbacks: ReplayUICallbacks);
|
|
28
|
+
/** Refresh slider range, position, and play/pause icon */
|
|
29
|
+
update(): void;
|
|
30
|
+
private updateLabel;
|
|
31
|
+
private updatePlayIcon;
|
|
32
|
+
destroy(): void;
|
|
33
|
+
private createButton;
|
|
34
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { UndoAction, CursorState } from "../undo/types";
|
|
2
|
+
import type { BlockJSON } from "../types";
|
|
3
|
+
/** Identity and display info for a collaborating peer */
|
|
4
|
+
export interface CrdtPeer {
|
|
5
|
+
/** Unique peer identifier */
|
|
6
|
+
id: string;
|
|
7
|
+
/** Display name shown on cursor labels */
|
|
8
|
+
displayName: string;
|
|
9
|
+
/** CSS color for cursor and selection highlights */
|
|
10
|
+
color: string;
|
|
11
|
+
}
|
|
12
|
+
/** A single CRDT operation entry in the log */
|
|
13
|
+
export interface CrdtOperation {
|
|
14
|
+
/** Sequential operation ID */
|
|
15
|
+
id: number;
|
|
16
|
+
/** Timestamp of the operation */
|
|
17
|
+
timestamp: number;
|
|
18
|
+
/** Peer who authored this operation (null = local before peers were set up) */
|
|
19
|
+
peerId: string | null;
|
|
20
|
+
/** One or more actions forming an atomic unit (compound ops like split) */
|
|
21
|
+
actions: UndoAction[];
|
|
22
|
+
/** Cursor state before the operation */
|
|
23
|
+
beforeCursor: CursorState | null;
|
|
24
|
+
/** Cursor state after the operation */
|
|
25
|
+
afterCursor: CursorState | null;
|
|
26
|
+
}
|
|
27
|
+
/** A remote peer's cursor position, optionally with a selection range */
|
|
28
|
+
export interface RemoteCursor {
|
|
29
|
+
peerId: string;
|
|
30
|
+
displayName: string;
|
|
31
|
+
color: string;
|
|
32
|
+
blockId: string;
|
|
33
|
+
offset: number;
|
|
34
|
+
/** If set, the peer has a selection from offset..endOffset */
|
|
35
|
+
endOffset?: number;
|
|
36
|
+
/** If set, the selection spans into this block (multi-block selection) */
|
|
37
|
+
endBlockId?: string;
|
|
38
|
+
/** Timestamp of last cursor update (ms since epoch) */
|
|
39
|
+
lastUpdated?: number;
|
|
40
|
+
}
|
|
41
|
+
/** Serializable snapshot of a CRDT log */
|
|
42
|
+
export interface CrdtLogSnapshot {
|
|
43
|
+
initialState: BlockJSON[];
|
|
44
|
+
operations: CrdtOperation[];
|
|
45
|
+
}
|
|
46
|
+
/** Default peer colors for multiplayer (soft, distinct hues) */
|
|
47
|
+
export declare const PEER_COLORS: readonly ["#e06c75", "#61afef", "#e5c07b", "#98c379", "#c678dd", "#d19a66", "#56b6c2", "#be5046"];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for agenda date formatting and state classification.
|
|
3
|
+
*
|
|
4
|
+
* All dates are ISO 8601 date-only strings ("YYYY-MM-DD").
|
|
5
|
+
* No time component, no timezone — dates are interpreted in the user's local timezone.
|
|
6
|
+
*/
|
|
7
|
+
/** Agenda item urgency states, used for badge/label styling. */
|
|
8
|
+
export type AgendaState = "overdue" | "today" | "tomorrow" | "upcoming" | "future" | "past" | "ongoing";
|
|
9
|
+
export interface AgendaDisplay {
|
|
10
|
+
/** Human-readable label for the badge/label (e.g. "Tomorrow", "Mar 15", "Overdue: Mar 1"). */
|
|
11
|
+
label: string;
|
|
12
|
+
/** CSS class suffix for styling (e.g. "blx-agenda-overdue"). */
|
|
13
|
+
state: AgendaState;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Parse an ISO date string ("YYYY-MM-DD") into a Date at midnight local time.
|
|
17
|
+
* Returns null for invalid inputs.
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseAgendaDate(iso: string): Date | null;
|
|
20
|
+
/**
|
|
21
|
+
* Format a Date as "YYYY-MM-DD" (ISO 8601 date-only).
|
|
22
|
+
*/
|
|
23
|
+
export declare function formatAgendaDateISO(date: Date): string;
|
|
24
|
+
/** Full month names for calendar header. */
|
|
25
|
+
export declare const MONTH_NAMES_FULL: string[];
|
|
26
|
+
/**
|
|
27
|
+
* Classify an agenda date and produce a display label + state.
|
|
28
|
+
*
|
|
29
|
+
* @param agendaDate - ISO date string ("YYYY-MM-DD")
|
|
30
|
+
* @param now - Reference date (defaults to current date). Pass explicitly for testability.
|
|
31
|
+
* @param role - Date role: "due" (default), "scheduled", or "event". Affects past-date classification.
|
|
32
|
+
*/
|
|
33
|
+
export declare function classifyAgendaDate(agendaDate: string, now?: Date, role?: "due" | "scheduled" | "event"): AgendaDisplay;
|
|
34
|
+
/**
|
|
35
|
+
* CSS class for a given agenda state.
|
|
36
|
+
*/
|
|
37
|
+
export declare function agendaStateClass(state: AgendaState): string;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Natural language date parser for the @ agenda trigger.
|
|
3
|
+
*
|
|
4
|
+
* Parses common date phrases ("tomorrow", "next friday", "march 15") into
|
|
5
|
+
* date suggestions. Designed to be lightweight (no external dependencies)
|
|
6
|
+
* and handle the most common patterns users type.
|
|
7
|
+
*/
|
|
8
|
+
/** A parsed date suggestion shown in the picker. */
|
|
9
|
+
export interface DateSuggestion {
|
|
10
|
+
/** Human-readable label (e.g. "Tomorrow", "Next Friday") */
|
|
11
|
+
label: string;
|
|
12
|
+
/** Formatted date description (e.g. "Wed, Mar 11") */
|
|
13
|
+
description: string;
|
|
14
|
+
/** The resolved Date object */
|
|
15
|
+
date: Date;
|
|
16
|
+
/** ISO date string ("YYYY-MM-DD") */
|
|
17
|
+
isoDate: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Parse a natural language date query into suggestions.
|
|
21
|
+
*
|
|
22
|
+
* Returns up to 5 suggestions sorted by relevance. Empty query returns
|
|
23
|
+
* empty array (the caller should show the default calendar UI).
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseDateQuery(query: string, now?: Date): DateSuggestion[];
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Date picker for task agenda items with role selection, explicit confirmation,
|
|
3
|
+
* and recurrence configuration.
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Role tabs: Due | Scheduled | Event (context-sensitive defaults)
|
|
7
|
+
* - Natural language date suggestions (type "tomorrow", "next friday", etc.)
|
|
8
|
+
* - Mini calendar grid (month view) with keyboard navigation
|
|
9
|
+
* - Explicit confirm/cancel (tick/cross) in footer bar
|
|
10
|
+
* - Repeat configuration (interval + unit + fromCompletion flag)
|
|
11
|
+
* - Remove agenda item button (when editing an existing agenda date)
|
|
12
|
+
*
|
|
13
|
+
* Follows the same lifecycle pattern as EmojiPicker:
|
|
14
|
+
* - `show(anchorRect, options?)` positions and displays the picker
|
|
15
|
+
* - `reposition(anchorRect)` follows the caret on scroll
|
|
16
|
+
* - `hide()` / `visible` / `destroy()` for cleanup
|
|
17
|
+
* - `handleKeyDown(e)` returns true if the event was consumed
|
|
18
|
+
*/
|
|
19
|
+
import type { DateSuggestion } from "./date-parser";
|
|
20
|
+
export type DateRole = "due" | "scheduled" | "event";
|
|
21
|
+
export interface Recurrence {
|
|
22
|
+
interval: number;
|
|
23
|
+
unit: "day" | "week" | "month" | "year";
|
|
24
|
+
anchor?: string;
|
|
25
|
+
fromCompletion?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare class DatePicker {
|
|
28
|
+
private el;
|
|
29
|
+
private onSelect;
|
|
30
|
+
private onRemove;
|
|
31
|
+
private onClose;
|
|
32
|
+
private pendingInteraction;
|
|
33
|
+
private currentMonth;
|
|
34
|
+
private selectedDate;
|
|
35
|
+
private pendingDate;
|
|
36
|
+
private selectedRole;
|
|
37
|
+
private blockType;
|
|
38
|
+
private repeatEnabled;
|
|
39
|
+
private repeatInterval;
|
|
40
|
+
private repeatUnit;
|
|
41
|
+
private fromCompletion;
|
|
42
|
+
private showingRepeatConfig;
|
|
43
|
+
private roleTabBar;
|
|
44
|
+
private calendarGrid;
|
|
45
|
+
private monthLabel;
|
|
46
|
+
private removeBtn;
|
|
47
|
+
private footerBar;
|
|
48
|
+
private tickBtn;
|
|
49
|
+
private repeatToggleBtn;
|
|
50
|
+
private repeatConfigEl;
|
|
51
|
+
private calendarSection;
|
|
52
|
+
private suggestionList;
|
|
53
|
+
private headerEl;
|
|
54
|
+
private dowRow;
|
|
55
|
+
private inSuggestionMode;
|
|
56
|
+
private suggestions;
|
|
57
|
+
private suggestionIndex;
|
|
58
|
+
constructor(onSelect: (isoDate: string, role: DateRole, recurrence?: Recurrence) => void, onRemove: () => void, onClose: () => void);
|
|
59
|
+
get element(): HTMLElement;
|
|
60
|
+
/**
|
|
61
|
+
* Show the picker anchored to the given rect.
|
|
62
|
+
* @param anchorRect - Caret or block bounding rect
|
|
63
|
+
* @param options - Optional initial state for role, date, recurrence, blockType
|
|
64
|
+
*/
|
|
65
|
+
show(anchorRect: DOMRect, options?: {
|
|
66
|
+
initialRole?: DateRole;
|
|
67
|
+
initialDate?: string;
|
|
68
|
+
initialRecurrence?: Recurrence;
|
|
69
|
+
blockType?: string;
|
|
70
|
+
} | string | null): void;
|
|
71
|
+
reposition(anchorRect: DOMRect): void;
|
|
72
|
+
/** Clamp the picker within the viewport, flipping or pinning as needed. */
|
|
73
|
+
private clampToViewport;
|
|
74
|
+
hide(): void;
|
|
75
|
+
get visible(): boolean;
|
|
76
|
+
isInteracting(): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Update displayed date suggestions.
|
|
79
|
+
* Called by the editor on each keystroke while @ search is active.
|
|
80
|
+
* Pass an empty array or null to exit suggestion mode.
|
|
81
|
+
*/
|
|
82
|
+
updateSuggestions(suggestions: DateSuggestion[] | null): void;
|
|
83
|
+
/**
|
|
84
|
+
* Programmatically set the role (due/scheduled/event).
|
|
85
|
+
* Used by inline syntax detection (e.g., @due(...) sets role to "due").
|
|
86
|
+
*/
|
|
87
|
+
setRole(role: DateRole): void;
|
|
88
|
+
/**
|
|
89
|
+
* Programmatically confirm the current suggestion (like pressing Enter).
|
|
90
|
+
* Used by inline syntax auto-confirm (typing the closing paren).
|
|
91
|
+
* If in suggestion mode with suggestions, selects the highlighted one.
|
|
92
|
+
* If a pending date is set, confirms it.
|
|
93
|
+
*/
|
|
94
|
+
confirmSelection(): void;
|
|
95
|
+
/** Handle keyboard events. Returns true if the event was consumed. */
|
|
96
|
+
handleKeyDown(e: KeyboardEvent): boolean;
|
|
97
|
+
destroy(): void;
|
|
98
|
+
private renderRoleTabs;
|
|
99
|
+
private confirmDate;
|
|
100
|
+
private updateConfirmState;
|
|
101
|
+
private formatRecurrenceLabel;
|
|
102
|
+
private updateRepeatLabel;
|
|
103
|
+
private setPendingDate;
|
|
104
|
+
private toggleRepeatConfig;
|
|
105
|
+
private updateRepeatSection;
|
|
106
|
+
/** Format pending date as "Wed, Mar 25" for the repeat config chip. */
|
|
107
|
+
private formatDateChip;
|
|
108
|
+
private buildRepeatConfig;
|
|
109
|
+
private enterSuggestionMode;
|
|
110
|
+
private exitSuggestionMode;
|
|
111
|
+
private renderSuggestions;
|
|
112
|
+
private updateSuggestionSelection;
|
|
113
|
+
private navigateMonth;
|
|
114
|
+
private renderCalendar;
|
|
115
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { DatePicker } from "./date-picker";
|
|
2
|
+
export type { DateRole, Recurrence } from "./date-picker";
|
|
3
|
+
export { classifyAgendaDate, parseAgendaDate, formatAgendaDateISO, agendaStateClass, MONTH_NAMES_FULL } from "./agenda-utils";
|
|
4
|
+
export type { AgendaState, AgendaDisplay } from "./agenda-utils";
|
|
5
|
+
export { parseDateQuery } from "./date-parser";
|
|
6
|
+
export type { DateSuggestion } from "./date-parser";
|