@ai-composer/core 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 (124) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +6 -0
  3. package/dist/commands/builtins.d.ts +41 -0
  4. package/dist/commands/builtins.d.ts.map +1 -0
  5. package/dist/commands/builtins.js +116 -0
  6. package/dist/commands/builtins.js.map +1 -0
  7. package/dist/commands/registry.d.ts +27 -0
  8. package/dist/commands/registry.d.ts.map +1 -0
  9. package/dist/commands/registry.js +29 -0
  10. package/dist/commands/registry.js.map +1 -0
  11. package/dist/editor.d.ts +185 -0
  12. package/dist/editor.d.ts.map +1 -0
  13. package/dist/editor.js +558 -0
  14. package/dist/editor.js.map +1 -0
  15. package/dist/errors.d.ts +15 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +22 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/events/event-bus.d.ts +19 -0
  20. package/dist/events/event-bus.d.ts.map +1 -0
  21. package/dist/events/event-bus.js +51 -0
  22. package/dist/events/event-bus.js.map +1 -0
  23. package/dist/events/events.d.ts +94 -0
  24. package/dist/events/events.d.ts.map +1 -0
  25. package/dist/events/events.js +6 -0
  26. package/dist/events/events.js.map +1 -0
  27. package/dist/factory.d.ts +17 -0
  28. package/dist/factory.d.ts.map +1 -0
  29. package/dist/factory.js +18 -0
  30. package/dist/factory.js.map +1 -0
  31. package/dist/history/history.d.ts +48 -0
  32. package/dist/history/history.d.ts.map +1 -0
  33. package/dist/history/history.js +71 -0
  34. package/dist/history/history.js.map +1 -0
  35. package/dist/index.d.ts +43 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +30 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/model/document.d.ts +54 -0
  40. package/dist/model/document.d.ts.map +1 -0
  41. package/dist/model/document.js +177 -0
  42. package/dist/model/document.js.map +1 -0
  43. package/dist/model/nodes.d.ts +80 -0
  44. package/dist/model/nodes.d.ts.map +1 -0
  45. package/dist/model/nodes.js +64 -0
  46. package/dist/model/nodes.js.map +1 -0
  47. package/dist/model/selection.d.ts +40 -0
  48. package/dist/model/selection.d.ts.map +1 -0
  49. package/dist/model/selection.js +79 -0
  50. package/dist/model/selection.js.map +1 -0
  51. package/dist/nodes/registry.d.ts +39 -0
  52. package/dist/nodes/registry.d.ts.map +1 -0
  53. package/dist/nodes/registry.js +93 -0
  54. package/dist/nodes/registry.js.map +1 -0
  55. package/dist/package.json +21 -0
  56. package/dist/plugins/registry.d.ts +53 -0
  57. package/dist/plugins/registry.d.ts.map +1 -0
  58. package/dist/plugins/registry.js +102 -0
  59. package/dist/plugins/registry.js.map +1 -0
  60. package/dist/serialization/builtins.d.ts +30 -0
  61. package/dist/serialization/builtins.d.ts.map +1 -0
  62. package/dist/serialization/builtins.js +102 -0
  63. package/dist/serialization/builtins.js.map +1 -0
  64. package/dist/serialization/index.d.ts +4 -0
  65. package/dist/serialization/index.d.ts.map +1 -0
  66. package/dist/serialization/index.js +3 -0
  67. package/dist/serialization/index.js.map +1 -0
  68. package/dist/serialization/registry.d.ts +17 -0
  69. package/dist/serialization/registry.d.ts.map +1 -0
  70. package/dist/serialization/registry.js +35 -0
  71. package/dist/serialization/registry.js.map +1 -0
  72. package/dist/serialization/serializers.d.ts +12 -0
  73. package/dist/serialization/serializers.d.ts.map +1 -0
  74. package/dist/serialization/serializers.js +3 -0
  75. package/dist/serialization/serializers.js.map +1 -0
  76. package/dist/state/state.d.ts +54 -0
  77. package/dist/state/state.d.ts.map +1 -0
  78. package/dist/state/state.js +6 -0
  79. package/dist/state/state.js.map +1 -0
  80. package/dist/triggers/engine.d.ts +102 -0
  81. package/dist/triggers/engine.d.ts.map +1 -0
  82. package/dist/triggers/engine.js +235 -0
  83. package/dist/triggers/engine.js.map +1 -0
  84. package/dist/types.d.ts +37 -0
  85. package/dist/types.d.ts.map +1 -0
  86. package/dist/types.js +3 -0
  87. package/dist/types.js.map +1 -0
  88. package/dist/utils/html.d.ts +3 -0
  89. package/dist/utils/html.d.ts.map +1 -0
  90. package/dist/utils/html.js +10 -0
  91. package/dist/utils/html.js.map +1 -0
  92. package/package.json +20 -0
  93. package/project.json +56 -0
  94. package/src/commands/builtins.ts +142 -0
  95. package/src/commands/registry.ts +53 -0
  96. package/src/commands.test.ts +118 -0
  97. package/src/editor.test.ts +311 -0
  98. package/src/editor.ts +826 -0
  99. package/src/errors.ts +25 -0
  100. package/src/events/event-bus.test.ts +62 -0
  101. package/src/events/event-bus.ts +72 -0
  102. package/src/events/events.ts +120 -0
  103. package/src/factory.ts +21 -0
  104. package/src/history/history.ts +108 -0
  105. package/src/index.ts +201 -0
  106. package/src/model/document.test.ts +130 -0
  107. package/src/model/document.ts +207 -0
  108. package/src/model/nodes.ts +151 -0
  109. package/src/model/selection.ts +107 -0
  110. package/src/nodes/registry.ts +142 -0
  111. package/src/plugins/registry.ts +163 -0
  112. package/src/serialization/builtins.ts +123 -0
  113. package/src/serialization/index.ts +20 -0
  114. package/src/serialization/registry.ts +49 -0
  115. package/src/serialization/serializers.ts +15 -0
  116. package/src/state/state.ts +57 -0
  117. package/src/triggers/engine.ts +317 -0
  118. package/src/triggers.test.ts +138 -0
  119. package/src/types.ts +42 -0
  120. package/src/utils/html.ts +9 -0
  121. package/tsconfig.json +15 -0
  122. package/tsconfig.lib.json +15 -0
  123. package/tsconfig.spec.json +16 -0
  124. package/vitest.config.ts +11 -0
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Composer document — the authoritative, framework-neutral value of an editor
3
+ * (model-first architecture, ADR-0001). Documents are treated as immutable:
4
+ * edit helpers return new documents and never mutate node objects.
5
+ */
6
+ import { createNodeKey, createTextNode, isAIComposerNode, } from './nodes';
7
+ import { fromGlobalOffset, toGlobalOffset } from './selection';
8
+ export function createDocument(nodes = [], metadata) {
9
+ const document = { nodes: normalizeNodes(nodes) };
10
+ if (metadata !== undefined)
11
+ document.metadata = metadata;
12
+ return document;
13
+ }
14
+ /** Coerce common inputs into a document. */
15
+ export function coerceDocument(value) {
16
+ if (value === null || value === undefined)
17
+ return createDocument([]);
18
+ if (typeof value === 'string')
19
+ return createDocument(value ? [createTextNode(value)] : []);
20
+ if (Array.isArray(value))
21
+ return createDocument(value);
22
+ return createDocument(value.nodes ?? [], value.metadata);
23
+ }
24
+ /**
25
+ * Merge adjacent text nodes and drop empty text runs. Returns a new array;
26
+ * input nodes are never mutated.
27
+ */
28
+ export function normalizeNodes(nodes) {
29
+ const result = [];
30
+ for (const node of nodes) {
31
+ if (node.type === 'text') {
32
+ if (node.text === '')
33
+ continue;
34
+ const last = result[result.length - 1];
35
+ if (last && last.type === 'text') {
36
+ result[result.length - 1] = { ...last, text: last.text + node.text };
37
+ continue;
38
+ }
39
+ }
40
+ result.push(node);
41
+ }
42
+ return result;
43
+ }
44
+ export function nodeLength(node) {
45
+ return node.type === 'text' ? node.text.length : 1;
46
+ }
47
+ export function totalLength(nodes) {
48
+ return nodes.reduce((sum, node) => sum + nodeLength(node), 0);
49
+ }
50
+ export function documentLength(document) {
51
+ return totalLength(document.nodes);
52
+ }
53
+ /**
54
+ * True when there is nothing meaningful to show or submit: no chips and no
55
+ * text beyond whitespace. ContentEditable hosts keep a layout `<br>` after
56
+ * the user deletes everything — that artifact must read as empty or the
57
+ * placeholder never comes back.
58
+ */
59
+ export function isEmptyDocument(document) {
60
+ let sawChip = false;
61
+ let sawText = false;
62
+ for (const node of document.nodes) {
63
+ if (node.type !== 'text')
64
+ sawChip = true;
65
+ else if (node.text.trim().length > 0)
66
+ sawText = true;
67
+ }
68
+ return !sawChip && !sawText;
69
+ }
70
+ export function findNodeIndex(document, key) {
71
+ return document.nodes.findIndex((node) => node.key === key);
72
+ }
73
+ export function findNode(document, key) {
74
+ const index = findNodeIndex(document, key);
75
+ return index >= 0 ? { node: document.nodes[index], index } : null;
76
+ }
77
+ export function getNodesOfType(document, type) {
78
+ return document.nodes.filter((node) => node.type === type);
79
+ }
80
+ export function cloneDocument(document) {
81
+ return {
82
+ nodes: document.nodes.map((node) => ({ ...node })),
83
+ ...(document.metadata ? { metadata: { ...document.metadata } } : {}),
84
+ };
85
+ }
86
+ export function documentsEqual(a, b) {
87
+ if (a === b)
88
+ return true;
89
+ return JSON.stringify(a.nodes) === JSON.stringify(b.nodes);
90
+ }
91
+ /**
92
+ * Replace unknown/unregistered node types with a readable text node so that
93
+ * documents from external sources (paste, storage, setValue) can never inject
94
+ * unrenderable nodes.
95
+ */
96
+ export function sanitizeDocument(document, isKnownType) {
97
+ const nodes = document.nodes.map((node) => {
98
+ if (isKnownType(node.type) && isAIComposerNode(node))
99
+ return node;
100
+ if (node.type === 'text' && typeof node.text === 'string') {
101
+ return createTextNode(node.text);
102
+ }
103
+ return createTextNode(JSON.stringify(node));
104
+ });
105
+ return createDocument(nodes, document.metadata);
106
+ }
107
+ // ---------------------------------------------------------------------------
108
+ // Edit primitives (pure)
109
+ // ---------------------------------------------------------------------------
110
+ /** Delete `range` (inclusive) and report the caret position after deletion. */
111
+ export function deleteDocumentRange(document, range) {
112
+ const startOffset = toGlobalOffset(document, range.start);
113
+ const endOffset = toGlobalOffset(document, range.end);
114
+ const low = Math.min(startOffset, endOffset);
115
+ const high = Math.max(startOffset, endOffset);
116
+ const nodes = [];
117
+ let cursor = 0;
118
+ for (const node of document.nodes) {
119
+ const length = nodeLength(node);
120
+ const nodeStart = cursor;
121
+ const nodeEnd = cursor + length;
122
+ if (nodeEnd <= low || nodeStart >= high) {
123
+ nodes.push(node);
124
+ cursor = nodeEnd;
125
+ continue;
126
+ }
127
+ if (node.type === 'text') {
128
+ const keepBefore = Math.max(0, low - nodeStart);
129
+ const keepAfter = Math.max(0, nodeEnd - high);
130
+ if (keepBefore > 0)
131
+ nodes.push({ ...node, text: node.text.slice(0, keepBefore) });
132
+ if (keepAfter > 0)
133
+ nodes.push(createTextNode(node.text.slice(node.text.length - keepAfter)));
134
+ }
135
+ // atomic nodes overlapping the range are removed entirely
136
+ cursor = nodeEnd;
137
+ }
138
+ const next = { nodes, ...(document.metadata ? { metadata: document.metadata } : {}) };
139
+ const normalized = { ...next, nodes: normalizeNodes(next.nodes) };
140
+ return { document: normalized, caret: fromGlobalOffset(normalized, low) };
141
+ }
142
+ /** Insert `inserted` at `position` (splitting a text node when needed). */
143
+ export function insertNodesAt(document, position, inserted) {
144
+ if (inserted.length === 0)
145
+ return { document, caret: position };
146
+ const index = Math.min(Math.max(position.nodeIndex, 0), document.nodes.length);
147
+ const target = document.nodes[index];
148
+ const result = [...document.nodes.slice(0, index)];
149
+ if (target && target.type === 'text') {
150
+ const offset = Math.min(Math.max(position.offset, 0), target.text.length);
151
+ if (offset === 0) {
152
+ result.push(...inserted, target);
153
+ }
154
+ else if (offset === target.text.length) {
155
+ result.push(target, ...inserted);
156
+ }
157
+ else {
158
+ result.push({ ...target, text: target.text.slice(0, offset) }, ...inserted, createTextNode(target.text.slice(offset), createNodeKey('text')));
159
+ }
160
+ }
161
+ else if (target) {
162
+ // atomic target: offset 0 = before the chip, 1 = after it
163
+ if (position.offset > 0)
164
+ result.push(target, ...inserted);
165
+ else
166
+ result.push(...inserted, target);
167
+ }
168
+ else {
169
+ result.push(...inserted);
170
+ }
171
+ result.push(...document.nodes.slice(index + 1));
172
+ const next = { nodes: result, ...(document.metadata ? { metadata: document.metadata } : {}) };
173
+ const normalized = { ...next, nodes: normalizeNodes(next.nodes) };
174
+ const caretOffset = toGlobalOffset(document, position) + totalLength(inserted);
175
+ return { document: normalized, caret: fromGlobalOffset(normalized, caretOffset) };
176
+ }
177
+ //# sourceMappingURL=document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.js","sourceRoot":"","sources":["../../src/model/document.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Node model.
3
+ *
4
+ * A composer document is a flat list of nodes (see ADR-0002): text runs and
5
+ * atomic "chip" nodes (mention, command, variable, attachment, custom).
6
+ *
7
+ * Every node carries a stable instance `key`. It is NOT the entity id —
8
+ * {@link MentionNode}.id identifies the mentioned entity, `key` identifies
9
+ * this particular node instance and stays stable across clones, undo/redo
10
+ * and DOM round-trips (React keys, DOM↔model mapping).
11
+ */
12
+ /** Stable node instance key. */
13
+ export type NodeKey = string;
14
+ export interface BaseNode {
15
+ key: NodeKey;
16
+ }
17
+ export interface TextNode extends BaseNode {
18
+ type: 'text';
19
+ text: string;
20
+ }
21
+ export interface MentionNode extends BaseNode {
22
+ type: 'mention';
23
+ /** Entity id (user, ticket, …) — not the node instance key. */
24
+ id: string;
25
+ label: string;
26
+ metadata?: Record<string, unknown>;
27
+ }
28
+ export interface CommandNode extends BaseNode {
29
+ type: 'command';
30
+ id: string;
31
+ label: string;
32
+ metadata?: Record<string, unknown>;
33
+ }
34
+ export interface VariableNode extends BaseNode {
35
+ type: 'variable';
36
+ name: string;
37
+ value?: unknown;
38
+ }
39
+ export interface AttachmentNode extends BaseNode {
40
+ type: 'attachment';
41
+ id: string;
42
+ name: string;
43
+ mimeType: string;
44
+ size?: number;
45
+ metadata?: Record<string, unknown>;
46
+ }
47
+ /** Application/plugin-defined node. Rendering & serialization come from the owning NodeDefinition. */
48
+ export interface CustomNode extends BaseNode {
49
+ type: 'custom';
50
+ plugin: string;
51
+ nodeType: string;
52
+ data: unknown;
53
+ }
54
+ export type AIComposerNode = TextNode | MentionNode | CommandNode | VariableNode | AttachmentNode | CustomNode;
55
+ /** Anything that is not a text run is indivisible (length 1) for edits and selection. */
56
+ export type AtomicNode = Exclude<AIComposerNode, TextNode>;
57
+ export type AIComposerNodeType = AIComposerNode['type'];
58
+ /** Generate a unique node key, e.g. `mention_lx3a_07_f2k9q1`. */
59
+ export declare function createNodeKey(prefix?: string): NodeKey;
60
+ export declare function createTextNode(text: string, key?: NodeKey): TextNode;
61
+ export declare function createMentionNode(input: Omit<MentionNode, 'type' | 'key'>, key?: NodeKey): MentionNode;
62
+ export declare function createCommandNode(input: Omit<CommandNode, 'type' | 'key'>, key?: NodeKey): CommandNode;
63
+ export declare function createVariableNode(input: {
64
+ name: string;
65
+ value?: unknown;
66
+ }, key?: NodeKey): VariableNode;
67
+ export declare function createAttachmentNode(input: Omit<AttachmentNode, 'type' | 'key'>, key?: NodeKey): AttachmentNode;
68
+ export declare function createCustomNode(input: {
69
+ plugin: string;
70
+ nodeType: string;
71
+ data: unknown;
72
+ }, key?: NodeKey): CustomNode;
73
+ /** Assign a key to a hand-built node that lacks one (e.g. from parsed JSON). */
74
+ export declare function ensureNodeKey<T extends AIComposerNode>(node: T): T;
75
+ export declare function isTextNode(node: AIComposerNode): node is TextNode;
76
+ export declare function isAtomicNode(node: AIComposerNode): node is AtomicNode;
77
+ export declare function getNodeType(node: AIComposerNode): AIComposerNodeType;
78
+ /** Lightweight structural validation for documents coming from views / storage. */
79
+ export declare function isAIComposerNode(value: unknown): value is AIComposerNode;
80
+ //# sourceMappingURL=nodes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodes.d.ts","sourceRoot":"","sources":["../../src/model/nodes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Node model.
3
+ *
4
+ * A composer document is a flat list of nodes (see ADR-0002): text runs and
5
+ * atomic "chip" nodes (mention, command, variable, attachment, custom).
6
+ *
7
+ * Every node carries a stable instance `key`. It is NOT the entity id —
8
+ * {@link MentionNode}.id identifies the mentioned entity, `key` identifies
9
+ * this particular node instance and stays stable across clones, undo/redo
10
+ * and DOM round-trips (React keys, DOM↔model mapping).
11
+ */
12
+ let sequence = 0;
13
+ /** Generate a unique node key, e.g. `mention_lx3a_07_f2k9q1`. */
14
+ export function createNodeKey(prefix = 'n') {
15
+ sequence += 1;
16
+ return `${prefix}_${Date.now().toString(36)}_${sequence.toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
17
+ }
18
+ // ---------------------------------------------------------------------------
19
+ // Factories
20
+ // ---------------------------------------------------------------------------
21
+ export function createTextNode(text, key = createNodeKey('text')) {
22
+ return { type: 'text', key, text };
23
+ }
24
+ export function createMentionNode(input, key = createNodeKey('mention')) {
25
+ return { type: 'mention', key, ...input };
26
+ }
27
+ export function createCommandNode(input, key = createNodeKey('command')) {
28
+ return { type: 'command', key, ...input };
29
+ }
30
+ export function createVariableNode(input, key = createNodeKey('variable')) {
31
+ return { type: 'variable', key, name: input.name, ...(input.value !== undefined ? { value: input.value } : {}) };
32
+ }
33
+ export function createAttachmentNode(input, key = createNodeKey('attachment')) {
34
+ return { type: 'attachment', key, ...input };
35
+ }
36
+ export function createCustomNode(input, key = createNodeKey(input.nodeType || 'custom')) {
37
+ return { type: 'custom', key, plugin: input.plugin, nodeType: input.nodeType, data: input.data };
38
+ }
39
+ /** Assign a key to a hand-built node that lacks one (e.g. from parsed JSON). */
40
+ export function ensureNodeKey(node) {
41
+ return node.key ? node : { ...node, key: createNodeKey(node.type) };
42
+ }
43
+ // ---------------------------------------------------------------------------
44
+ // Guards
45
+ // ---------------------------------------------------------------------------
46
+ export function isTextNode(node) {
47
+ return node.type === 'text';
48
+ }
49
+ export function isAtomicNode(node) {
50
+ return node.type !== 'text';
51
+ }
52
+ export function getNodeType(node) {
53
+ return node.type;
54
+ }
55
+ /** Lightweight structural validation for documents coming from views / storage. */
56
+ export function isAIComposerNode(value) {
57
+ if (typeof value !== 'object' || value === null)
58
+ return false;
59
+ const node = value;
60
+ return (typeof node.type === 'string' &&
61
+ typeof node.key === 'string' &&
62
+ (node.type !== 'text' || typeof node.text === 'string'));
63
+ }
64
+ //# sourceMappingURL=nodes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodes.js","sourceRoot":"","sources":["../../src/model/nodes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Selection model — framework-neutral positions against the document model,
3
+ * never raw DOM Range objects (see ADR-0001).
4
+ */
5
+ import type { AIComposerDocument } from './document';
6
+ /** Caret or selection endpoint. For atomic nodes offset is 0 (before) or 1 (after). */
7
+ export interface Position {
8
+ nodeIndex: number;
9
+ offset: number;
10
+ }
11
+ export interface SelectionState {
12
+ anchor: Position;
13
+ focus: Position;
14
+ }
15
+ export interface DocumentRange {
16
+ start: Position;
17
+ end: Position;
18
+ }
19
+ export declare const createPosition: (nodeIndex: number, offset?: number) => Position;
20
+ export declare const createSelection: (anchor?: Position, focus?: Position) => SelectionState;
21
+ export declare function equalsPosition(a: Position, b: Position): boolean;
22
+ export declare function equalsSelection(a: SelectionState, b: SelectionState): boolean;
23
+ /** Total order over positions: by node index, then offset. */
24
+ export declare function comparePositions(a: Position, b: Position): number;
25
+ export declare const isCollapsed: (selection: SelectionState) => boolean;
26
+ /** Anchor→focus ordering regardless of selection direction. */
27
+ export declare function getRange(selection: SelectionState): DocumentRange;
28
+ export declare const getStart: (selection: SelectionState) => Position;
29
+ export declare const getEnd: (selection: SelectionState) => Position;
30
+ /** Convert a model position into a document-wide character offset. */
31
+ export declare function toGlobalOffset(document: AIComposerDocument, position: Position): number;
32
+ /** Convert a document-wide offset back into a node-relative position. */
33
+ export declare function fromGlobalOffset(document: AIComposerDocument, globalOffset: number): Position;
34
+ export declare function clampPosition(document: AIComposerDocument, position: Position): Position;
35
+ export declare function clampSelection(document: AIComposerDocument, selection: SelectionState): SelectionState;
36
+ export declare function globalRange(document: AIComposerDocument, selection: SelectionState): {
37
+ start: number;
38
+ end: number;
39
+ };
40
+ //# sourceMappingURL=selection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../../src/model/selection.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGrD,uFAAuF;AACvF,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,QAAQ,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,QAAQ,CAAC;IAChB,GAAG,EAAE,QAAQ,CAAC;CACf;AAED,eAAO,MAAM,cAAc,GAAI,WAAW,MAAM,EAAE,eAAU,KAAG,QAAmC,CAAC;AAEnG,eAAO,MAAM,eAAe,GAC1B,SAAQ,QAA+B,EACvC,QAAO,QAAiB,KACvB,cAAqC,CAAC;AAEzC,wBAAgB,cAAc,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAEhE;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,cAAc,GAAG,OAAO,CAE7E;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAIjE;AAED,eAAO,MAAM,WAAW,GAAI,WAAW,cAAc,KAAG,OACL,CAAC;AAEpD,+DAA+D;AAC/D,wBAAgB,QAAQ,CAAC,SAAS,EAAE,cAAc,GAAG,aAAa,CAIjE;AAED,eAAO,MAAM,QAAQ,GAAI,WAAW,cAAc,KAAG,QAAqC,CAAC;AAC3F,eAAO,MAAM,MAAM,GAAI,WAAW,cAAc,KAAG,QAAmC,CAAC;AAEvF,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,kBAAkB,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,CASvF;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,GAAG,QAAQ,CAU7F;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,kBAAkB,EAAE,QAAQ,EAAE,QAAQ,GAAG,QAAQ,CAaxF;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,kBAAkB,EAAE,SAAS,EAAE,cAAc,GAAG,cAAc,CAEtG;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,kBAAkB,EAAE,SAAS,EAAE,cAAc,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAGnH"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Selection model — framework-neutral positions against the document model,
3
+ * never raw DOM Range objects (see ADR-0001).
4
+ */
5
+ import { nodeLength } from './document';
6
+ export const createPosition = (nodeIndex, offset = 0) => ({ nodeIndex, offset });
7
+ export const createSelection = (anchor = createPosition(0, 0), focus = anchor) => ({ anchor, focus });
8
+ export function equalsPosition(a, b) {
9
+ return a.nodeIndex === b.nodeIndex && a.offset === b.offset;
10
+ }
11
+ export function equalsSelection(a, b) {
12
+ return equalsPosition(a.anchor, b.anchor) && equalsPosition(a.focus, b.focus);
13
+ }
14
+ /** Total order over positions: by node index, then offset. */
15
+ export function comparePositions(a, b) {
16
+ if (a.nodeIndex !== b.nodeIndex)
17
+ return a.nodeIndex < b.nodeIndex ? -1 : 1;
18
+ if (a.offset === b.offset)
19
+ return 0;
20
+ return a.offset < b.offset ? -1 : 1;
21
+ }
22
+ export const isCollapsed = (selection) => equalsPosition(selection.anchor, selection.focus);
23
+ /** Anchor→focus ordering regardless of selection direction. */
24
+ export function getRange(selection) {
25
+ return comparePositions(selection.anchor, selection.focus) <= 0
26
+ ? { start: selection.anchor, end: selection.focus }
27
+ : { start: selection.focus, end: selection.anchor };
28
+ }
29
+ export const getStart = (selection) => getRange(selection).start;
30
+ export const getEnd = (selection) => getRange(selection).end;
31
+ /** Convert a model position into a document-wide character offset. */
32
+ export function toGlobalOffset(document, position) {
33
+ let offset = 0;
34
+ for (let i = 0; i < position.nodeIndex && i < document.nodes.length; i += 1) {
35
+ offset += nodeLength(document.nodes[i]);
36
+ }
37
+ const node = document.nodes[position.nodeIndex];
38
+ if (!node)
39
+ return offset;
40
+ if (node.type === 'text')
41
+ return offset + Math.min(Math.max(position.offset, 0), node.text.length);
42
+ return offset + (position.offset > 0 ? 1 : 0);
43
+ }
44
+ /** Convert a document-wide offset back into a node-relative position. */
45
+ export function fromGlobalOffset(document, globalOffset) {
46
+ let remaining = Math.max(0, globalOffset);
47
+ for (let i = 0; i < document.nodes.length; i += 1) {
48
+ const length = nodeLength(document.nodes[i]);
49
+ if (remaining < length || (remaining === length && i === document.nodes.length - 1)) {
50
+ return createPosition(i, remaining);
51
+ }
52
+ remaining -= length;
53
+ }
54
+ return createPosition(document.nodes.length, 0);
55
+ }
56
+ export function clampPosition(document, position) {
57
+ const maxIndex = document.nodes.length;
58
+ const nodeIndex = Math.min(Math.max(position.nodeIndex, 0), maxIndex);
59
+ if (nodeIndex === maxIndex) {
60
+ // At (or beyond) the end boundary: clamp onto the last node's end.
61
+ if (maxIndex === 0)
62
+ return createPosition(0, 0);
63
+ const last = document.nodes[maxIndex - 1];
64
+ return createPosition(maxIndex - 1, nodeLength(last));
65
+ }
66
+ const node = document.nodes[nodeIndex];
67
+ if (!node)
68
+ return createPosition(nodeIndex, 0);
69
+ const maxOffset = nodeLength(node);
70
+ return createPosition(nodeIndex, Math.min(Math.max(position.offset, 0), maxOffset));
71
+ }
72
+ export function clampSelection(document, selection) {
73
+ return createSelection(clampPosition(document, selection.anchor), clampPosition(document, selection.focus));
74
+ }
75
+ export function globalRange(document, selection) {
76
+ const { start, end } = getRange(selection);
77
+ return { start: toGlobalOffset(document, start), end: toGlobalOffset(document, end) };
78
+ }
79
+ //# sourceMappingURL=selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection.js","sourceRoot":"","sources":["../../src/model/selection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Node type registry. Core registers the built-in node types; plugins can add
3
+ * custom ones or override serialization for built-ins (ADR-0004).
4
+ */
5
+ import type { Unsubscribe } from '../events/event-bus';
6
+ import type { AIComposerNode } from '../model/nodes';
7
+ export interface NodeDefinition {
8
+ /** Node type identifier, e.g. 'mention'. `custom` nodes also carry `nodeType`. */
9
+ type: string;
10
+ /** Owning plugin id (informational, for custom nodes). */
11
+ plugin?: string;
12
+ /** Atomic nodes are indivisible units with model length 1. */
13
+ isAtomic: boolean;
14
+ /** Plain-text projection used by the `text` serializer and a11y announcements. */
15
+ toText?(node: AIComposerNode): string;
16
+ /** Markdown projection. */
17
+ toMarkdown?(node: AIComposerNode): string;
18
+ /** Safe HTML projection — must escape user content (used for copy/export, never for trusting input). */
19
+ toHtml?(node: AIComposerNode): string;
20
+ /** Short chip label for DOM rendering. */
21
+ toDisplay?(node: AIComposerNode): string;
22
+ }
23
+ export interface NodeRegistry {
24
+ register(definition: NodeDefinition): Unsubscribe;
25
+ get(type: string): NodeDefinition | undefined;
26
+ has(type: string): boolean;
27
+ list(): NodeDefinition[];
28
+ clear(): void;
29
+ }
30
+ export declare function createNodeRegistry(): NodeRegistry;
31
+ export declare const TEXT_NODE_DEFINITION: NodeDefinition;
32
+ export declare const MENTION_NODE_DEFINITION: NodeDefinition;
33
+ export declare const COMMAND_NODE_DEFINITION: NodeDefinition;
34
+ export declare const VARIABLE_NODE_DEFINITION: NodeDefinition;
35
+ export declare const ATTACHMENT_NODE_DEFINITION: NodeDefinition;
36
+ export declare const CUSTOM_NODE_DEFINITION: NodeDefinition;
37
+ /** Registry pre-loaded with all built-in node types. */
38
+ export declare function createDefaultNodeRegistry(): NodeRegistry;
39
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/nodes/registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,OAAO,KAAK,EAKV,cAAc,EAGf,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,QAAQ,EAAE,OAAO,CAAC;IAClB,kFAAkF;IAClF,MAAM,CAAC,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAAC;IACtC,2BAA2B;IAC3B,UAAU,CAAC,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAAC;IAC1C,wGAAwG;IACxG,MAAM,CAAC,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAAC;IACtC,0CAA0C;IAC1C,SAAS,CAAC,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAAC;CAC1C;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,EAAE,cAAc,GAAG,WAAW,CAAC;IAClD,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;IAC9C,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,IAAI,IAAI,cAAc,EAAE,CAAC;IACzB,KAAK,IAAI,IAAI,CAAC;CACf;AAED,wBAAgB,kBAAkB,IAAI,YAAY,CAsBjD;AAQD,eAAO,MAAM,oBAAoB,EAAE,cAOlC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,cAQrC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,cAQrC,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,cAQtC,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,cAQxC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,cAQpC,CAAC;AAEF,wDAAwD;AACxD,wBAAgB,yBAAyB,IAAI,YAAY,CASxD"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Node type registry. Core registers the built-in node types; plugins can add
3
+ * custom ones or override serialization for built-ins (ADR-0004).
4
+ */
5
+ import { escapeHtml } from '../utils/html';
6
+ export function createNodeRegistry() {
7
+ const definitions = new Map();
8
+ return {
9
+ register(definition) {
10
+ definitions.set(definition.type, definition);
11
+ return () => {
12
+ if (definitions.get(definition.type) === definition)
13
+ definitions.delete(definition.type);
14
+ };
15
+ },
16
+ get(type) {
17
+ return definitions.get(type);
18
+ },
19
+ has(type) {
20
+ return definitions.has(type);
21
+ },
22
+ list() {
23
+ return [...definitions.values()];
24
+ },
25
+ clear() {
26
+ definitions.clear();
27
+ },
28
+ };
29
+ }
30
+ // ---------------------------------------------------------------------------
31
+ // Built-in definitions
32
+ // ---------------------------------------------------------------------------
33
+ const as = (node) => node;
34
+ export const TEXT_NODE_DEFINITION = {
35
+ type: 'text',
36
+ isAtomic: false,
37
+ toText: (node) => as(node).text,
38
+ toMarkdown: (node) => as(node).text,
39
+ toHtml: (node) => escapeHtml(as(node).text).replace(/\n/g, '<br>'),
40
+ toDisplay: (node) => as(node).text,
41
+ };
42
+ export const MENTION_NODE_DEFINITION = {
43
+ type: 'mention',
44
+ isAtomic: true,
45
+ toText: (node) => `@${as(node).label}`,
46
+ toMarkdown: (node) => `[@${as(node).label}](mention:${as(node).id})`,
47
+ toHtml: (node) => `<span data-aic-node="mention" data-aic-id="${escapeHtml(as(node).id)}">@${escapeHtml(as(node).label)}</span>`,
48
+ toDisplay: (node) => `@${as(node).label}`,
49
+ };
50
+ export const COMMAND_NODE_DEFINITION = {
51
+ type: 'command',
52
+ isAtomic: true,
53
+ toText: (node) => `/${as(node).label}`,
54
+ toMarkdown: (node) => `[/${as(node).label}](command:${as(node).id})`,
55
+ toHtml: (node) => `<span data-aic-node="command" data-aic-id="${escapeHtml(as(node).id)}">/${escapeHtml(as(node).label)}</span>`,
56
+ toDisplay: (node) => `/${as(node).label}`,
57
+ };
58
+ export const VARIABLE_NODE_DEFINITION = {
59
+ type: 'variable',
60
+ isAtomic: true,
61
+ toText: (node) => `{{${as(node).name}}}`,
62
+ toMarkdown: (node) => `{{${as(node).name}}}`,
63
+ toHtml: (node) => `<span data-aic-node="variable" data-aic-name="${escapeHtml(as(node).name)}">{{${escapeHtml(as(node).name)}}}</span>`,
64
+ toDisplay: (node) => `{{${as(node).name}}}`,
65
+ };
66
+ export const ATTACHMENT_NODE_DEFINITION = {
67
+ type: 'attachment',
68
+ isAtomic: true,
69
+ toText: (node) => as(node).name,
70
+ toMarkdown: (node) => `[${as(node).name}](attachment:${as(node).id})`,
71
+ toHtml: (node) => `<span data-aic-node="attachment" data-aic-id="${escapeHtml(as(node).id)}" data-aic-mime="${escapeHtml(as(node).mimeType)}">${escapeHtml(as(node).name)}</span>`,
72
+ toDisplay: (node) => as(node).name,
73
+ };
74
+ export const CUSTOM_NODE_DEFINITION = {
75
+ type: 'custom',
76
+ isAtomic: true,
77
+ toText: (node) => JSON.stringify(as(node).data),
78
+ toMarkdown: (node) => escapeHtml(JSON.stringify(as(node).data)),
79
+ toHtml: (node) => `<span data-aic-node="custom" data-aic-plugin="${escapeHtml(as(node).plugin)}" data-aic-type="${escapeHtml(as(node).nodeType)}" data-aic-data="${escapeHtml(JSON.stringify(as(node).data))}">${escapeHtml(String(as(node).data ?? ''))}</span>`,
80
+ toDisplay: (node) => String(as(node).data ?? ''),
81
+ };
82
+ /** Registry pre-loaded with all built-in node types. */
83
+ export function createDefaultNodeRegistry() {
84
+ const registry = createNodeRegistry();
85
+ registry.register(TEXT_NODE_DEFINITION);
86
+ registry.register(MENTION_NODE_DEFINITION);
87
+ registry.register(COMMAND_NODE_DEFINITION);
88
+ registry.register(VARIABLE_NODE_DEFINITION);
89
+ registry.register(ATTACHMENT_NODE_DEFINITION);
90
+ registry.register(CUSTOM_NODE_DEFINITION);
91
+ return registry;
92
+ }
93
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/nodes/registry.ts"],"names":[],"mappings":"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"}