@tensor_1/react 2.17.0 → 2.19.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 (60) hide show
  1. package/README.md +131 -1
  2. package/dist/components/Comments.d.ts +18 -2
  3. package/dist/components/Comments.js +37 -12
  4. package/dist/components/EditorIcons.d.ts +39 -0
  5. package/dist/components/EditorIcons.js +51 -0
  6. package/dist/components/EmojiGlyph.d.ts +10 -0
  7. package/dist/components/EmojiGlyph.js +18 -0
  8. package/dist/components/EmojiPicker.d.ts +60 -0
  9. package/dist/components/EmojiPicker.js +277 -0
  10. package/dist/components/Markdown.d.ts +36 -0
  11. package/dist/components/Markdown.js +104 -0
  12. package/dist/components/ReactionBar.d.ts +57 -0
  13. package/dist/components/ReactionBar.js +81 -0
  14. package/dist/components/RelationshipGraph.d.ts +81 -3
  15. package/dist/components/RelationshipGraph.js +102 -59
  16. package/dist/components/RelationshipGraphLayered.d.ts +8 -0
  17. package/dist/components/RelationshipGraphLayered.js +420 -0
  18. package/dist/components/RelationshipGraphList.d.ts +16 -0
  19. package/dist/components/RelationshipGraphList.js +33 -0
  20. package/dist/components/RichTextEditor.d.ts +128 -0
  21. package/dist/components/RichTextEditor.js +954 -0
  22. package/dist/components/SaveStatus.d.ts +32 -0
  23. package/dist/components/SaveStatus.js +21 -0
  24. package/dist/components/Tabs.d.ts +10 -3
  25. package/dist/components/Tabs.js +5 -3
  26. package/dist/emoji-image-WQLh8ZQP.d.cts +128 -0
  27. package/dist/emoji-image.d.ts +25 -0
  28. package/dist/emoji-image.js +80 -0
  29. package/dist/emoji-model.d.ts +66 -0
  30. package/dist/emoji-model.js +108 -0
  31. package/dist/emoji.cjs +871 -0
  32. package/dist/emoji.d.cts +57 -0
  33. package/dist/emoji.d.ts +6 -0
  34. package/dist/emoji.js +18 -0
  35. package/dist/index.cjs +1503 -540
  36. package/dist/index.d.cts +245 -65
  37. package/dist/index.d.ts +3 -2
  38. package/dist/index.js +11 -2
  39. package/dist/markdown-model.d.ts +75 -0
  40. package/dist/markdown-model.js +438 -0
  41. package/dist/markdown.cjs +585 -0
  42. package/dist/markdown.d.cts +115 -0
  43. package/dist/markdown.d.ts +4 -0
  44. package/dist/markdown.js +9 -0
  45. package/dist/overlay-position-CEGSz6Z2.d.cts +59 -0
  46. package/dist/relationship-graph-model.d.ts +112 -1
  47. package/dist/relationship-graph-model.js +420 -1
  48. package/dist/rich-text/emoji-items.d.ts +14 -0
  49. package/dist/rich-text/emoji-items.js +7 -0
  50. package/dist/rich-text/pipeline.d.ts +91 -0
  51. package/dist/rich-text/pipeline.js +379 -0
  52. package/dist/rich-text/round-trip.d.ts +24 -0
  53. package/dist/rich-text/round-trip.js +11 -0
  54. package/dist/rich-text.cjs +2578 -0
  55. package/dist/rich-text.d.cts +148 -0
  56. package/dist/rich-text.d.ts +8 -0
  57. package/dist/rich-text.js +10 -0
  58. package/dist/url-policy.d.ts +9 -0
  59. package/dist/url-policy.js +11 -0
  60. package/package.json +82 -1
@@ -0,0 +1,4 @@
1
+ export { Markdown, MarkdownProps, defaultMarkdownIsSafeUrl } from './components/Markdown.js';
2
+ export { MarkdownBlock, MarkdownInline, MarkdownList, MarkdownListItem, TableAlign, parseInline, parseMarkdown, plainText } from './markdown-model.js';
3
+ import 'react';
4
+ import './url-policy.js';
@@ -0,0 +1,9 @@
1
+ import { Markdown, defaultMarkdownIsSafeUrl } from "./components/Markdown.js";
2
+ import { parseMarkdown, parseInline, plainText } from "./markdown-model.js";
3
+ export {
4
+ Markdown,
5
+ defaultMarkdownIsSafeUrl,
6
+ parseInline,
7
+ parseMarkdown,
8
+ plainText
9
+ };
@@ -0,0 +1,59 @@
1
+ /**
2
+ * uix overlay positioning — framework-agnostic, dependency-free anchored placement.
3
+ *
4
+ * Pure geometry (no DOM): given the anchor's rect, the floating element's size, and
5
+ * the viewport, compute where to put the floating element with two behaviours:
6
+ * • flip — if the preferred side doesn't fit, use the opposite side (or whichever
7
+ * side has more room);
8
+ * • shift — slide along the cross axis so the element stays within the viewport.
9
+ *
10
+ * The React hook (useAnchoredPosition) and the vanilla styleguide both feed this
11
+ * getBoundingClientRect() values, so anchored overlays land in the same place in every
12
+ * browser — instead of relying on CSS anchor() positioning, which is Chromium-only and
13
+ * silently detaches the overlay from its trigger everywhere else (UIX-FIX-02).
14
+ *
15
+ * Everything here is deterministic and unit-tested (see overlay-position.test.mjs).
16
+ */
17
+ type Side = 'top' | 'bottom' | 'left' | 'right';
18
+ type Align = 'start' | 'center' | 'end';
19
+ type Placement = Side | `${Side}-${Align}`;
20
+ /** A viewport-space rectangle, as returned by getBoundingClientRect(). */
21
+ interface Rect {
22
+ x: number;
23
+ y: number;
24
+ width: number;
25
+ height: number;
26
+ }
27
+ interface Size {
28
+ width: number;
29
+ height: number;
30
+ }
31
+ interface PositionOptions {
32
+ /** Preferred placement. Default `'bottom-start'`; a bare side (e.g. `'top'`) centers on the cross axis. */
33
+ placement?: Placement;
34
+ /** Gap between the anchor and the floating element, in px. Default 6. */
35
+ offset?: number;
36
+ /** Minimum gap kept from each viewport edge when shifting, in px. Default 8. */
37
+ padding?: number;
38
+ /** Flip to the opposite side when the preferred one doesn't fit. Default true. */
39
+ flip?: boolean;
40
+ /** Slide along the cross axis to stay on-screen. Default true. */
41
+ shift?: boolean;
42
+ }
43
+ interface PositionResult {
44
+ /** Viewport-space left, for `position: fixed`. */
45
+ x: number;
46
+ /** Viewport-space top, for `position: fixed`. */
47
+ y: number;
48
+ /** The placement actually used (after any flip). */
49
+ placement: Placement;
50
+ side: Side;
51
+ align: Align;
52
+ }
53
+ /**
54
+ * Compute the floating element's viewport-space position anchored to `anchor`.
55
+ * Coordinates are for `position: fixed` (same space as getBoundingClientRect).
56
+ */
57
+ declare function computePosition(anchor: Rect, floating: Size, viewport: Size, options?: PositionOptions): PositionResult;
58
+
59
+ export { type Align as A, type Placement as P, type Rect as R, type Side as S, type PositionOptions as a, type PositionResult as b, type Size as c, computePosition as d };
@@ -5,13 +5,21 @@ interface RelationshipGraphNode {
5
5
  description?: string;
6
6
  x?: number;
7
7
  y?: number;
8
+ /** Layered layout: the node's column. Omitted → BFS distance from the root along source→target. */
9
+ depth?: number;
8
10
  }
11
+ /** Where an edge's arrowhead sits: at the target (default), at the source, or nowhere. */
12
+ type RelationshipGraphArrow = 'forward' | 'backward' | 'none';
9
13
  interface RelationshipGraphEdge {
10
14
  id: string;
11
15
  source: string;
12
16
  target: string;
13
17
  type?: string;
14
18
  label?: string;
19
+ /** Layered layout: arrowhead placement (default `forward`). */
20
+ arrow?: RelationshipGraphArrow;
21
+ /** Layered layout: always draw this edge's label, whatever the label policy. */
22
+ emphasis?: boolean;
15
23
  }
16
24
  interface PositionedRelationshipNode extends RelationshipGraphNode {
17
25
  x: number;
@@ -27,5 +35,108 @@ declare function boundRelationshipGraph(nodes: RelationshipGraphNode[], edges: R
27
35
  declare function relationshipNeighbors(nodeId: string, edges: RelationshipGraphEdge[]): string[];
28
36
  declare function layoutRelationshipGraph(nodes: RelationshipGraphNode[], edges: RelationshipGraphEdge[], rootId?: string): PositionedRelationshipNode[];
29
37
  declare function traverseRelationshipNode(nodes: RelationshipGraphNode[], currentId: string | undefined, direction: -1 | 1): string | undefined;
38
+ type LayeredEdgeKind = 'forward' | 'lateral' | 'cycle';
39
+ interface LayeredLayoutOptions {
40
+ rootId?: string;
41
+ /** Minimum sibling group size that collapses into a cluster. Default 6; 0 disables. */
42
+ clusterThreshold?: number;
43
+ expandedClusterIds?: ReadonlySet<string>;
44
+ density?: 'compact' | 'standard';
45
+ }
46
+ interface RelationshipGraphCluster {
47
+ id: string;
48
+ parentId: string;
49
+ depth: number;
50
+ type?: string;
51
+ edgeType?: string;
52
+ edgeLabel?: string;
53
+ members: RelationshipGraphNode[];
54
+ }
55
+ interface LayeredItem {
56
+ /** Node id, or cluster id for a collapsed cluster. */
57
+ id: string;
58
+ kind: 'node' | 'cluster';
59
+ node?: RelationshipGraphNode;
60
+ cluster?: RelationshipGraphCluster;
61
+ depth: number;
62
+ /** Position within its column (0-based, top to bottom). */
63
+ index: number;
64
+ x: number;
65
+ y: number;
66
+ width: number;
67
+ height: number;
68
+ /** Forward parents / children as item ids, in column order. */
69
+ parents: string[];
70
+ children: string[];
71
+ /** Set on the first member of an expanded cluster: the group its collapse control belongs to. */
72
+ expandedGroup?: RelationshipGraphCluster;
73
+ /** True when a lateral or cycle edge touches this node. */
74
+ inCycle: boolean;
75
+ /** Edges (any kind) touching this item. */
76
+ degree: number;
77
+ }
78
+ interface RoutedEdge {
79
+ id: string;
80
+ /** Item ids (a cluster id when the edge enters a collapsed cluster). */
81
+ source: string;
82
+ target: string;
83
+ kind: LayeredEdgeKind;
84
+ /** Original edge ids drawn by this path (several for a cluster). */
85
+ edgeIds: string[];
86
+ type?: string;
87
+ label?: string;
88
+ arrow: RelationshipGraphArrow;
89
+ emphasis: boolean;
90
+ path: string;
91
+ labelX: number;
92
+ labelY: number;
93
+ /** Arrowhead tip and the direction it points in (radians, 0 = pointing right). */
94
+ arrowX: number;
95
+ arrowY: number;
96
+ arrowAngle: number;
97
+ /** Label policy `auto` shows this label without interaction. */
98
+ autoLabel: boolean;
99
+ /** The label wrapped to fit the column gap (at most two lines). */
100
+ labelLines: string[];
101
+ }
102
+ interface LayeredColumn {
103
+ depth: number;
104
+ x: number;
105
+ /** Underlying nodes in the column, cluster members included. */
106
+ count: number;
107
+ }
108
+ interface LayeredLayout {
109
+ rootId: string | undefined;
110
+ items: LayeredItem[];
111
+ edges: RoutedEdge[];
112
+ clusters: RelationshipGraphCluster[];
113
+ columns: LayeredColumn[];
114
+ /** Node id → id of the item that currently draws it (itself, or its cluster). */
115
+ itemOf: Record<string, string>;
116
+ width: number;
117
+ height: number;
118
+ nodeWidth: number;
119
+ nodeHeight: number;
120
+ headerHeight: number;
121
+ }
122
+ /** Word-wrap a label into at most two lines that fit `width`; overlong words end in an ellipsis. */
123
+ declare function wrapEdgeLabel(label: string, width: number): string[];
124
+ interface LayeredEdgeClassification {
125
+ depthById: Map<string, number>;
126
+ kindById: Map<string, LayeredEdgeKind>;
127
+ }
128
+ /**
129
+ * Column (depth) for every node and a kind for every drawable edge.
130
+ * Supplied `node.depth` wins; otherwise BFS from the root along source→target.
131
+ * Nodes the BFS cannot reach go to a trailing column. Self-loops and edges with
132
+ * a missing endpoint get no kind and are not drawn.
133
+ */
134
+ declare function classifyLayeredEdges(nodes: RelationshipGraphNode[], edges: RelationshipGraphEdge[], rootId?: string | undefined): LayeredEdgeClassification;
135
+ declare function clusterIdFor(parentId: string, type: string | undefined, edgeType: string | undefined): string;
136
+ /** Layered, clustered, crossing-reduced layout for `RelationshipGraph layout="layered"`. */
137
+ declare function layoutLayeredGraph(nodes: RelationshipGraphNode[], edges: RelationshipGraphEdge[], options?: LayeredLayoutOptions): LayeredLayout;
138
+ type LayeredNavigationKey = 'ArrowUp' | 'ArrowDown' | 'ArrowLeft' | 'ArrowRight' | 'Home' | 'End';
139
+ /** Keyboard traversal over a layered layout: ←/→ along forward edges, ↑/↓ within a column. */
140
+ declare function layeredNeighbor(layout: LayeredLayout, currentId: string | undefined, key: LayeredNavigationKey): string | undefined;
30
141
 
31
- export { type BoundedRelationshipGraph, type PositionedRelationshipNode, type RelationshipGraphEdge, type RelationshipGraphNode, boundRelationshipGraph, layoutRelationshipGraph, relationshipNeighbors, traverseRelationshipNode };
142
+ export { type BoundedRelationshipGraph, type LayeredColumn, type LayeredEdgeClassification, type LayeredEdgeKind, type LayeredItem, type LayeredLayout, type LayeredLayoutOptions, type LayeredNavigationKey, type PositionedRelationshipNode, type RelationshipGraphArrow, type RelationshipGraphCluster, type RelationshipGraphEdge, type RelationshipGraphNode, type RoutedEdge, boundRelationshipGraph, classifyLayeredEdges, clusterIdFor, layeredNeighbor, layoutLayeredGraph, layoutRelationshipGraph, relationshipNeighbors, traverseRelationshipNode, wrapEdgeLabel };
@@ -60,9 +60,428 @@ function traverseRelationshipNode(nodes, currentId, direction) {
60
60
  const current = Math.max(0, nodes.findIndex((node) => node.id === currentId));
61
61
  return nodes[(current + direction + nodes.length) % nodes.length]?.id;
62
62
  }
63
+ const LAYERED_GEOMETRY = {
64
+ standard: { nodeWidth: 176, nodeHeight: 76, columnGap: 96, rowGap: 12 },
65
+ compact: { nodeWidth: 160, nodeHeight: 60, columnGap: 80, rowGap: 8 }
66
+ };
67
+ const LAYERED_PADDING = 16;
68
+ const LAYERED_HEADER = 28;
69
+ const GROUP_HEADER = 28;
70
+ const AUTO_LABEL_MAX_FANOUT = 4;
71
+ const LABEL_CHAR_WIDTH = 5.6;
72
+ const LABEL_MAX_LINES = 2;
73
+ function wrapEdgeLabel(label, width) {
74
+ const max = Math.max(4, Math.floor(width / LABEL_CHAR_WIDTH));
75
+ const clip = (word) => word.length > max ? `${word.slice(0, max - 1)}\u2026` : word;
76
+ const lines = [];
77
+ let current = "";
78
+ for (const word of label.split(/\s+/).filter(Boolean)) {
79
+ const next = current ? `${current} ${word}` : word;
80
+ if (next.length <= max) {
81
+ current = next;
82
+ continue;
83
+ }
84
+ if (current) lines.push(current);
85
+ current = clip(word);
86
+ if (lines.length === LABEL_MAX_LINES) break;
87
+ }
88
+ if (current && lines.length < LABEL_MAX_LINES) lines.push(current);
89
+ if (lines.length === LABEL_MAX_LINES) {
90
+ const consumed = lines.join(" ").replace(/…$/, "").length;
91
+ if (consumed < label.replace(/\s+/g, " ").trim().length) {
92
+ const last = lines[LABEL_MAX_LINES - 1];
93
+ lines[LABEL_MAX_LINES - 1] = last.endsWith("\u2026") ? last : `${last.slice(0, Math.max(1, max - 1))}\u2026`;
94
+ }
95
+ }
96
+ return lines;
97
+ }
98
+ const drawableEdge = (edge, ids) => ids.has(edge.source) && ids.has(edge.target) && edge.source !== edge.target;
99
+ function classifyLayeredEdges(nodes, edges, rootId = nodes[0]?.id) {
100
+ const ids = new Set(nodes.map((node) => node.id));
101
+ const root = rootId !== void 0 && ids.has(rootId) ? rootId : nodes[0]?.id;
102
+ const outgoing = /* @__PURE__ */ new Map();
103
+ for (const edge of edges) {
104
+ if (!drawableEdge(edge, ids)) continue;
105
+ const list = outgoing.get(edge.source);
106
+ if (list) list.push(edge.target);
107
+ else outgoing.set(edge.source, [edge.target]);
108
+ }
109
+ const bfs = /* @__PURE__ */ new Map();
110
+ if (root !== void 0) {
111
+ bfs.set(root, 0);
112
+ const queue = [root];
113
+ for (let head = 0; head < queue.length; head += 1) {
114
+ const current = queue[head];
115
+ for (const next of outgoing.get(current) ?? []) {
116
+ if (!bfs.has(next)) {
117
+ bfs.set(next, bfs.get(current) + 1);
118
+ queue.push(next);
119
+ }
120
+ }
121
+ }
122
+ }
123
+ const depthById = /* @__PURE__ */ new Map();
124
+ let deepest = 0;
125
+ for (const node of nodes) {
126
+ const supplied = node.depth !== void 0 && Number.isFinite(node.depth) ? Math.max(0, Math.floor(node.depth)) : void 0;
127
+ const depth = supplied ?? bfs.get(node.id);
128
+ if (depth === void 0) continue;
129
+ depthById.set(node.id, depth);
130
+ deepest = Math.max(deepest, depth);
131
+ }
132
+ for (const node of nodes) if (!depthById.has(node.id)) depthById.set(node.id, deepest + 1);
133
+ const forwardOut = /* @__PURE__ */ new Map();
134
+ const kindById = /* @__PURE__ */ new Map();
135
+ const backEdges = [];
136
+ for (const edge of edges) {
137
+ if (!drawableEdge(edge, ids)) continue;
138
+ const from = depthById.get(edge.source);
139
+ const to = depthById.get(edge.target);
140
+ if (to === from + 1) {
141
+ kindById.set(edge.id, "forward");
142
+ const list = forwardOut.get(edge.source);
143
+ if (list) list.push(edge.target);
144
+ else forwardOut.set(edge.source, [edge.target]);
145
+ } else if (to < from) {
146
+ backEdges.push(edge);
147
+ } else {
148
+ kindById.set(edge.id, "lateral");
149
+ }
150
+ }
151
+ for (const edge of backEdges) {
152
+ const limit = depthById.get(edge.source);
153
+ const seen = /* @__PURE__ */ new Set([edge.target]);
154
+ const stack = [edge.target];
155
+ let closes = false;
156
+ while (stack.length > 0 && !closes) {
157
+ const current = stack.pop();
158
+ for (const next of forwardOut.get(current) ?? []) {
159
+ if (next === edge.source) {
160
+ closes = true;
161
+ break;
162
+ }
163
+ if (!seen.has(next) && depthById.get(next) < limit) {
164
+ seen.add(next);
165
+ stack.push(next);
166
+ }
167
+ }
168
+ }
169
+ kindById.set(edge.id, closes ? "cycle" : "lateral");
170
+ }
171
+ return { depthById, kindById };
172
+ }
173
+ function clusterIdFor(parentId, type, edgeType) {
174
+ return `cluster:${parentId}:${type ?? ""}:${edgeType ?? ""}`;
175
+ }
176
+ const mean = (values) => values.reduce((sum, value) => sum + value, 0) / values.length;
177
+ const round1 = (value) => Math.round(value * 10) / 10;
178
+ function layoutLayeredGraph(nodes, edges, options = {}) {
179
+ const { nodeWidth, nodeHeight, columnGap, rowGap } = LAYERED_GEOMETRY[options.density ?? "standard"];
180
+ const threshold = options.clusterThreshold ?? 6;
181
+ const expanded = options.expandedClusterIds ?? /* @__PURE__ */ new Set();
182
+ const rootId = options.rootId !== void 0 && nodes.some((node) => node.id === options.rootId) ? options.rootId : nodes[0]?.id;
183
+ if (nodes.length === 0) {
184
+ return {
185
+ rootId,
186
+ items: [],
187
+ edges: [],
188
+ clusters: [],
189
+ columns: [],
190
+ itemOf: {},
191
+ width: 0,
192
+ height: 0,
193
+ nodeWidth,
194
+ nodeHeight,
195
+ headerHeight: LAYERED_HEADER
196
+ };
197
+ }
198
+ const { depthById, kindById } = classifyLayeredEdges(nodes, edges, rootId);
199
+ const drawable = edges.filter((edge) => kindById.has(edge.id));
200
+ const forwardParents = /* @__PURE__ */ new Map();
201
+ const forwardChildCount = /* @__PURE__ */ new Map();
202
+ const inCycle = /* @__PURE__ */ new Set();
203
+ const nodeDegree = /* @__PURE__ */ new Map();
204
+ for (const edge of drawable) {
205
+ nodeDegree.set(edge.source, (nodeDegree.get(edge.source) ?? 0) + 1);
206
+ nodeDegree.set(edge.target, (nodeDegree.get(edge.target) ?? 0) + 1);
207
+ if (kindById.get(edge.id) === "forward") {
208
+ const list = forwardParents.get(edge.target);
209
+ if (list) list.push(edge);
210
+ else forwardParents.set(edge.target, [edge]);
211
+ forwardChildCount.set(edge.source, (forwardChildCount.get(edge.source) ?? 0) + 1);
212
+ } else {
213
+ inCycle.add(edge.source);
214
+ inCycle.add(edge.target);
215
+ }
216
+ }
217
+ const groups = /* @__PURE__ */ new Map();
218
+ if (threshold > 0) {
219
+ for (const node of nodes) {
220
+ if (node.id === rootId || inCycle.has(node.id) || (forwardChildCount.get(node.id) ?? 0) > 0) continue;
221
+ const parents = forwardParents.get(node.id) ?? [];
222
+ if (parents.length !== 1) continue;
223
+ const via = parents[0];
224
+ const id = clusterIdFor(via.source, node.type, via.type);
225
+ const group = groups.get(id);
226
+ if (group) {
227
+ group.members.push(node);
228
+ continue;
229
+ }
230
+ const created = { id, parentId: via.source, depth: depthById.get(node.id), members: [node] };
231
+ if (node.type !== void 0) created.type = node.type;
232
+ if (via.type !== void 0) created.edgeType = via.type;
233
+ const edgeLabel = via.label ?? via.type;
234
+ if (edgeLabel !== void 0) created.edgeLabel = edgeLabel;
235
+ groups.set(id, created);
236
+ }
237
+ }
238
+ const clusterById = /* @__PURE__ */ new Map();
239
+ const itemOf = {};
240
+ const expandedGroupOf = /* @__PURE__ */ new Map();
241
+ for (const group of groups.values()) {
242
+ if (group.members.length < threshold) continue;
243
+ if (expanded.has(group.id)) {
244
+ for (const member of group.members) expandedGroupOf.set(member.id, group);
245
+ continue;
246
+ }
247
+ clusterById.set(group.id, group);
248
+ for (const member of group.members) itemOf[member.id] = group.id;
249
+ }
250
+ for (const node of nodes) if (itemOf[node.id] === void 0) itemOf[node.id] = node.id;
251
+ const columnsByDepth = /* @__PURE__ */ new Map();
252
+ const itemById = /* @__PURE__ */ new Map();
253
+ for (const node of nodes) {
254
+ const id = itemOf[node.id];
255
+ if (itemById.has(id)) continue;
256
+ const cluster = clusterById.get(id);
257
+ const depth = depthById.get(node.id);
258
+ const item = {
259
+ id,
260
+ kind: cluster ? "cluster" : "node",
261
+ depth,
262
+ index: 0,
263
+ x: 0,
264
+ y: 0,
265
+ width: nodeWidth,
266
+ height: nodeHeight,
267
+ parents: [],
268
+ children: [],
269
+ inCycle: cluster ? false : inCycle.has(node.id),
270
+ degree: cluster ? cluster.members.length : nodeDegree.get(node.id) ?? 0
271
+ };
272
+ if (cluster) item.cluster = cluster;
273
+ else item.node = node;
274
+ itemById.set(id, item);
275
+ const column = columnsByDepth.get(depth);
276
+ if (column) column.push(item);
277
+ else columnsByDepth.set(depth, [item]);
278
+ }
279
+ for (const edge of drawable) {
280
+ if (kindById.get(edge.id) !== "forward") continue;
281
+ const from = itemById.get(itemOf[edge.source]);
282
+ const to = itemById.get(itemOf[edge.target]);
283
+ if (!from.children.includes(to.id)) from.children.push(to.id);
284
+ if (!to.parents.includes(from.id)) to.parents.push(from.id);
285
+ }
286
+ const depths = [...columnsByDepth.keys()].sort((a, b) => a - b);
287
+ const position = /* @__PURE__ */ new Map();
288
+ const reindex = (column) => {
289
+ column.forEach((item, index) => {
290
+ item.index = index;
291
+ position.set(item.id, index);
292
+ });
293
+ };
294
+ for (const depth of depths) reindex(columnsByDepth.get(depth));
295
+ const groupOfItem = (item) => item.node ? expandedGroupOf.get(item.node.id) : void 0;
296
+ const sweep = (depth, neighbours) => {
297
+ const keyed = columnsByDepth.get(depth).map((item) => {
298
+ const around = neighbours(item).map((id) => position.get(id)).filter((value) => value !== void 0);
299
+ return { item, key: around.length > 0 ? mean(around) : item.index, tie: item.index };
300
+ });
301
+ keyed.sort((a, b) => a.key - b.key || a.tie - b.tie);
302
+ const ordered = [];
303
+ const placed = /* @__PURE__ */ new Set();
304
+ for (const { item } of keyed) {
305
+ if (placed.has(item.id)) continue;
306
+ const group = groupOfItem(item);
307
+ for (const candidate of group ? keyed.map((entry) => entry.item).filter((other) => groupOfItem(other) === group) : [item]) {
308
+ if (placed.has(candidate.id)) continue;
309
+ ordered.push(candidate);
310
+ placed.add(candidate.id);
311
+ }
312
+ }
313
+ columnsByDepth.set(depth, ordered);
314
+ reindex(ordered);
315
+ };
316
+ const inner = depths.slice(1);
317
+ for (const depth of inner) sweep(depth, (item) => item.parents);
318
+ for (const depth of [...inner].reverse()) sweep(depth, (item) => item.children);
319
+ for (const depth of inner) sweep(depth, (item) => item.parents);
320
+ const top = LAYERED_PADDING + LAYERED_HEADER;
321
+ const columns = [];
322
+ let width = 0;
323
+ let height = 0;
324
+ for (const depth of depths) {
325
+ const column = columnsByDepth.get(depth);
326
+ const x = LAYERED_PADDING + depth * (nodeWidth + columnGap);
327
+ let cursor = top;
328
+ let count = 0;
329
+ const ledGroups = /* @__PURE__ */ new Set();
330
+ for (const item of column) {
331
+ const parentCentres = item.parents.map((id) => itemById.get(id)).filter((parent) => parent.depth < depth).map((parent) => parent.y + parent.height / 2);
332
+ const group = groupOfItem(item);
333
+ const leadsGroup = group !== void 0 && !ledGroups.has(group);
334
+ if (group) ledGroups.add(group);
335
+ const floor = cursor + (leadsGroup ? GROUP_HEADER : 0);
336
+ const target = parentCentres.length > 0 ? mean(parentCentres) - item.height / 2 : floor;
337
+ item.x = x;
338
+ item.y = round1(Math.max(target, floor));
339
+ if (leadsGroup) item.expandedGroup = group;
340
+ cursor = item.y + item.height + rowGap;
341
+ count += item.cluster ? item.cluster.members.length : 1;
342
+ width = Math.max(width, x + item.width);
343
+ height = Math.max(height, item.y + item.height);
344
+ }
345
+ columns.push({ depth, x, count });
346
+ }
347
+ const byPosition = (a, b) => (position.get(a) ?? 0) - (position.get(b) ?? 0);
348
+ for (const item of itemById.values()) {
349
+ item.children.sort(byPosition);
350
+ item.parents.sort(byPosition);
351
+ }
352
+ const routed = /* @__PURE__ */ new Map();
353
+ for (const edge of drawable) {
354
+ const kind = kindById.get(edge.id);
355
+ const from = itemById.get(itemOf[edge.source]);
356
+ const to = itemById.get(itemOf[edge.target]);
357
+ const key = from.kind === "cluster" || to.kind === "cluster" ? `${from.id}->${to.id}` : edge.id;
358
+ const existing = routed.get(key);
359
+ if (existing) {
360
+ existing.edgeIds.push(edge.id);
361
+ continue;
362
+ }
363
+ let startX;
364
+ let startY;
365
+ let endX;
366
+ let endY;
367
+ let path;
368
+ let labelX;
369
+ let labelY;
370
+ let startAngle;
371
+ let endAngle;
372
+ if (kind === "forward") {
373
+ startX = from.x + from.width;
374
+ startY = from.y + from.height / 2;
375
+ endX = to.x;
376
+ endY = to.y + to.height / 2;
377
+ const bend = Math.max(columnGap / 2, 24);
378
+ path = `M${round1(startX)} ${round1(startY)} C${round1(startX + bend)} ${round1(startY)} ${round1(endX - bend)} ${round1(endY)} ${round1(endX)} ${round1(endY)}`;
379
+ labelX = (startX + endX) / 2;
380
+ labelY = (startY + endY) / 2;
381
+ startAngle = Math.PI;
382
+ endAngle = 0;
383
+ } else if (from.depth === to.depth) {
384
+ startX = from.x + from.width;
385
+ startY = from.y + from.height / 2;
386
+ endX = to.x + to.width;
387
+ endY = to.y + to.height / 2;
388
+ const bulge = Math.min(columnGap - 8, 20 + Math.abs(endY - startY) * 0.1);
389
+ path = `M${round1(startX)} ${round1(startY)} C${round1(startX + bulge)} ${round1(startY)} ${round1(endX + bulge)} ${round1(endY)} ${round1(endX)} ${round1(endY)}`;
390
+ labelX = startX + bulge * 0.75;
391
+ labelY = (startY + endY) / 2;
392
+ startAngle = Math.PI;
393
+ endAngle = Math.PI;
394
+ } else {
395
+ startX = from.x + from.width / 2;
396
+ startY = from.y + from.height;
397
+ endX = to.x + to.width / 2;
398
+ endY = to.y + to.height;
399
+ const dip = 28 + Math.abs(endX - startX) * 0.12;
400
+ const low = Math.max(startY, endY) + dip;
401
+ path = `M${round1(startX)} ${round1(startY)} C${round1(startX)} ${round1(low)} ${round1(endX)} ${round1(low)} ${round1(endX)} ${round1(endY)}`;
402
+ labelX = (startX + endX) / 2;
403
+ labelY = low - dip * 0.25;
404
+ startAngle = -Math.PI / 2;
405
+ endAngle = -Math.PI / 2;
406
+ height = Math.max(height, low);
407
+ }
408
+ const arrow = edge.arrow ?? "forward";
409
+ const atStart = arrow === "backward";
410
+ const entry = {
411
+ id: key,
412
+ source: from.id,
413
+ target: to.id,
414
+ kind,
415
+ edgeIds: [edge.id],
416
+ arrow,
417
+ emphasis: edge.emphasis === true,
418
+ path,
419
+ labelX: round1(labelX),
420
+ labelY: round1(labelY),
421
+ arrowX: round1(atStart ? startX : endX),
422
+ arrowY: round1(atStart ? startY : endY),
423
+ arrowAngle: atStart ? startAngle : endAngle,
424
+ labelLines: [],
425
+ autoLabel: to.kind !== "cluster" && (edge.emphasis === true || kind === "forward" && from.children.length <= AUTO_LABEL_MAX_FANOUT)
426
+ };
427
+ if (edge.type !== void 0) entry.type = edge.type;
428
+ const label = edge.label ?? edge.type;
429
+ if (label !== void 0) entry.label = label;
430
+ routed.set(key, entry);
431
+ }
432
+ const seenLabels = /* @__PURE__ */ new Set();
433
+ for (const entry of routed.values()) {
434
+ if (entry.label === void 0) continue;
435
+ entry.labelLines = wrapEdgeLabel(entry.label, columnGap - 8);
436
+ if (!entry.autoLabel || entry.emphasis || entry.kind !== "forward") continue;
437
+ const key = `${entry.source}|${entry.label}`;
438
+ if (seenLabels.has(key)) entry.autoLabel = false;
439
+ else seenLabels.add(key);
440
+ }
441
+ return {
442
+ rootId,
443
+ items: depths.flatMap((depth) => columnsByDepth.get(depth)),
444
+ edges: [...routed.values()],
445
+ clusters: [...clusterById.values()],
446
+ columns,
447
+ itemOf,
448
+ width: round1(width + LAYERED_PADDING),
449
+ height: round1(height + LAYERED_PADDING + 8),
450
+ nodeWidth,
451
+ nodeHeight,
452
+ headerHeight: LAYERED_HEADER
453
+ };
454
+ }
455
+ function layeredNeighbor(layout, currentId, key) {
456
+ const root = layout.rootId !== void 0 ? layout.itemOf[layout.rootId] : layout.items[0]?.id;
457
+ const current = layout.items.find((item) => item.id === currentId);
458
+ if (!current) return root ?? layout.items[0]?.id;
459
+ const column = layout.items.filter((item) => item.depth === current.depth);
460
+ switch (key) {
461
+ case "ArrowUp":
462
+ return column[current.index - 1]?.id;
463
+ case "ArrowDown":
464
+ return column[current.index + 1]?.id;
465
+ case "ArrowRight":
466
+ return current.children[0];
467
+ case "ArrowLeft":
468
+ return current.parents[0];
469
+ case "Home":
470
+ return root;
471
+ case "End":
472
+ return column[column.length - 1]?.id;
473
+ default:
474
+ return void 0;
475
+ }
476
+ }
63
477
  export {
64
478
  boundRelationshipGraph,
479
+ classifyLayeredEdges,
480
+ clusterIdFor,
481
+ layeredNeighbor,
482
+ layoutLayeredGraph,
65
483
  layoutRelationshipGraph,
66
484
  relationshipNeighbors,
67
- traverseRelationshipNode
485
+ traverseRelationshipNode,
486
+ wrapEdgeLabel
68
487
  };
@@ -0,0 +1,14 @@
1
+ import { EmojiItem } from '@tiptap/extension-emoji';
2
+
3
+ /**
4
+ * The emoji list the editor configures `@tiptap/extension-emoji` with (RTE-01, on-prem).
5
+ *
6
+ * Upstream's default list carries a `fallbackImage` on a public CDN for nearly every
7
+ * entry, and its GitHub list adds image-only custom emoji. Either would make a browser
8
+ * request third-party images for emoji the device cannot draw. This list keeps only
9
+ * Unicode emoji and drops every image URL, so the editor never builds a remote URL.
10
+ * Same-origin fallback images are opt-in through `emojiImageBaseUrl`.
11
+ */
12
+ declare const OFFLINE_EMOJI_ITEMS: ReadonlyArray<EmojiItem>;
13
+
14
+ export { OFFLINE_EMOJI_ITEMS };
@@ -0,0 +1,7 @@
1
+ import { emojis } from "@tiptap/extension-emoji";
2
+ const OFFLINE_EMOJI_ITEMS = Object.freeze(
3
+ emojis.filter((item) => typeof item.emoji === "string" && item.emoji !== "").map(({ fallbackImage: _remote, ...item }) => item).filter((item) => !/https?:|\/\//i.test(JSON.stringify(item)))
4
+ );
5
+ export {
6
+ OFFLINE_EMOJI_ITEMS
7
+ };