@tensor_1/react 2.47.0 → 2.49.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 (204) hide show
  1. package/README.md +540 -2
  2. package/dist/{SchedulingCalendar-B6TakO2h.d.ts → SchedulingCalendar-WWyFL3Eo.d.ts} +51 -2
  3. package/dist/announce.d.ts +10 -0
  4. package/dist/announce.js +30 -0
  5. package/dist/chart-analytic.cjs +33 -25
  6. package/dist/chart-preset.cjs +23 -15
  7. package/dist/chart.cjs +23 -15
  8. package/dist/combobox-model.d.ts +10 -1
  9. package/dist/combobox-model.js +15 -0
  10. package/dist/command-palette-model.d.ts +44 -0
  11. package/dist/command-palette-model.js +36 -0
  12. package/dist/components/AccountMenu.d.ts +44 -0
  13. package/dist/components/AccountMenu.js +43 -0
  14. package/dist/components/AppShell.d.ts +45 -2
  15. package/dist/components/AppShell.js +116 -5
  16. package/dist/components/AuditLog.d.ts +128 -0
  17. package/dist/components/AuditLog.js +196 -0
  18. package/dist/components/BrandProfiles.js +4 -3
  19. package/dist/components/Breadcrumbs.d.ts +9 -2
  20. package/dist/components/Breadcrumbs.js +3 -2
  21. package/dist/components/Button.d.ts +12 -5
  22. package/dist/components/Button.js +7 -3
  23. package/dist/components/Card.d.ts +18 -1
  24. package/dist/components/Card.js +5 -1
  25. package/dist/components/ChangeHighlight.d.ts +30 -0
  26. package/dist/components/ChangeHighlight.js +21 -0
  27. package/dist/components/ChartCore.js +2 -1
  28. package/dist/components/Checkbox.d.ts +27 -1
  29. package/dist/components/Checkbox.js +31 -2
  30. package/dist/components/Chip.d.ts +7 -3
  31. package/dist/components/Chip.js +2 -2
  32. package/dist/components/CodeDisplay.d.ts +65 -0
  33. package/dist/components/CodeDisplay.js +124 -0
  34. package/dist/components/Combobox.d.ts +27 -6
  35. package/dist/components/Combobox.js +195 -58
  36. package/dist/components/CommandPalette.d.ts +36 -1
  37. package/dist/components/CommandPalette.js +254 -2
  38. package/dist/components/ContactCard.d.ts +66 -0
  39. package/dist/components/ContactCard.js +74 -0
  40. package/dist/components/Count.d.ts +24 -0
  41. package/dist/components/Count.js +32 -0
  42. package/dist/components/DashboardCanvas.js +14 -0
  43. package/dist/components/DataWorkspace.d.ts +167 -0
  44. package/dist/components/DataWorkspace.js +380 -0
  45. package/dist/components/DateFieldParts.d.ts +2 -1
  46. package/dist/components/DateFieldParts.js +7 -0
  47. package/dist/components/DatePicker.d.ts +9 -2
  48. package/dist/components/DatePicker.js +67 -28
  49. package/dist/components/DateRangePicker.d.ts +12 -2
  50. package/dist/components/DateRangePicker.js +13 -12
  51. package/dist/components/DetailPage.d.ts +7 -1
  52. package/dist/components/DetailPage.js +2 -1
  53. package/dist/components/Dialogs.d.ts +71 -10
  54. package/dist/components/Dialogs.js +130 -30
  55. package/dist/components/DiffViewer.d.ts +23 -3
  56. package/dist/components/DiffViewer.js +26 -12
  57. package/dist/components/DiscardPrompt.js +16 -0
  58. package/dist/components/DisplayPreferences.d.ts +83 -0
  59. package/dist/components/DisplayPreferences.js +185 -0
  60. package/dist/components/EditorialHome.d.ts +23 -3
  61. package/dist/components/EditorialHome.js +16 -11
  62. package/dist/components/EntityPicker.d.ts +20 -3
  63. package/dist/components/EntityPicker.js +32 -13
  64. package/dist/components/FilterEditor.d.ts +18 -4
  65. package/dist/components/FilterEditor.js +141 -19
  66. package/dist/components/FocusOnMount.d.ts +282 -0
  67. package/dist/components/FocusOnMount.js +12 -0
  68. package/dist/components/InfoTip.js +11 -12
  69. package/dist/components/InlineEdit.d.ts +87 -20
  70. package/dist/components/InlineEdit.js +197 -54
  71. package/dist/components/Input.d.ts +12 -2
  72. package/dist/components/Input.js +5 -3
  73. package/dist/components/Kanban.d.ts +14 -3
  74. package/dist/components/Kanban.js +9 -4
  75. package/dist/components/Kbd.d.ts +6 -1
  76. package/dist/components/Kbd.js +6 -6
  77. package/dist/components/Lightbox.d.ts +41 -5
  78. package/dist/components/Lightbox.js +95 -9
  79. package/dist/components/MatchReview.d.ts +40 -1
  80. package/dist/components/MatchReview.js +130 -4
  81. package/dist/components/MetricInput.js +3 -2
  82. package/dist/components/Modal.d.ts +2 -1
  83. package/dist/components/Modal.js +13 -4
  84. package/dist/components/MotionList.d.ts +303 -0
  85. package/dist/components/MotionList.js +175 -0
  86. package/dist/components/NavFavourites.d.ts +5 -0
  87. package/dist/components/NavFavourites.js +7 -7
  88. package/dist/components/OtpInput.d.ts +33 -0
  89. package/dist/components/OtpInput.js +123 -0
  90. package/dist/components/PageHeader.d.ts +7 -1
  91. package/dist/components/PageHeader.js +2 -2
  92. package/dist/components/Pagination.js +6 -5
  93. package/dist/components/PasswordInput.d.ts +34 -0
  94. package/dist/components/PasswordInput.js +101 -0
  95. package/dist/components/Peek.js +3 -3
  96. package/dist/components/Popover.js +11 -0
  97. package/dist/components/PrintLayout.d.ts +63 -0
  98. package/dist/components/PrintLayout.js +114 -0
  99. package/dist/components/QrCode.d.ts +47 -0
  100. package/dist/components/QrCode.js +55 -0
  101. package/dist/components/QrScanner.d.ts +95 -0
  102. package/dist/components/QrScanner.js +266 -0
  103. package/dist/components/RelationshipGraph.d.ts +32 -1
  104. package/dist/components/RelationshipGraph.js +22 -6
  105. package/dist/components/RelationshipGraphLayered.d.ts +1 -1
  106. package/dist/components/RelationshipGraphLayered.js +268 -118
  107. package/dist/components/RelationshipGraphList.js +33 -6
  108. package/dist/components/RuleBuilder.js +39 -7
  109. package/dist/components/SchedulingAgenda.d.ts +3 -1
  110. package/dist/components/SchedulingCalendar.d.ts +3 -1
  111. package/dist/components/SchedulingCalendar.js +43 -4
  112. package/dist/components/SchedulingResourceDay.d.ts +57 -0
  113. package/dist/components/SchedulingResourceDay.js +287 -0
  114. package/dist/components/SchedulingTimeGrid.d.ts +3 -1
  115. package/dist/components/SchedulingTimeline.d.ts +3 -1
  116. package/dist/components/SearchSuggest.d.ts +23 -3
  117. package/dist/components/SearchSuggest.js +56 -18
  118. package/dist/components/Segmented.d.ts +39 -4
  119. package/dist/components/Segmented.js +39 -5
  120. package/dist/components/Select.d.ts +3 -2
  121. package/dist/components/Select.js +7 -1
  122. package/dist/components/ShortcutHint.d.ts +11 -10
  123. package/dist/components/ShortcutHint.js +44 -38
  124. package/dist/components/Sidebar.d.ts +36 -4
  125. package/dist/components/Sidebar.js +37 -12
  126. package/dist/components/SortableList.d.ts +45 -0
  127. package/dist/components/SortableList.js +147 -0
  128. package/dist/components/Stat.d.ts +28 -3
  129. package/dist/components/Stat.js +13 -3
  130. package/dist/components/States.d.ts +33 -1
  131. package/dist/components/States.js +43 -1
  132. package/dist/components/StatusPill.d.ts +14 -3
  133. package/dist/components/StatusPill.js +11 -3
  134. package/dist/components/Steps.js +2 -1
  135. package/dist/components/Table.d.ts +9 -2
  136. package/dist/components/Table.js +4 -2
  137. package/dist/components/TableControls.js +18 -58
  138. package/dist/components/TableOverflowProbe.d.ts +12 -0
  139. package/dist/components/TableOverflowProbe.js +27 -0
  140. package/dist/components/Tabs.d.ts +2 -1
  141. package/dist/components/Tabs.js +10 -2
  142. package/dist/components/TagInput.d.ts +67 -0
  143. package/dist/components/TagInput.js +342 -0
  144. package/dist/components/Textarea.d.ts +5 -0
  145. package/dist/components/Textarea.js +2 -2
  146. package/dist/components/TimePicker.d.ts +80 -0
  147. package/dist/components/TimePicker.js +347 -0
  148. package/dist/components/Toast.d.ts +1 -1
  149. package/dist/components/Toast.js +109 -4
  150. package/dist/components/Tooltip.d.ts +8 -4
  151. package/dist/components/Tooltip.js +86 -28
  152. package/dist/components/Tree.d.ts +8 -0
  153. package/dist/components/Tree.js +11 -5
  154. package/dist/display-preferences-model.d.ts +53 -0
  155. package/dist/display-preferences-model.js +64 -0
  156. package/dist/emoji.cjs +143 -49
  157. package/dist/filter-model.d.ts +59 -3
  158. package/dist/filter-model.js +67 -2
  159. package/dist/hooks/useAnchoredPosition.js +14 -2
  160. package/dist/hooks/useChangeHighlight.d.ts +33 -0
  161. package/dist/hooks/useChangeHighlight.js +44 -0
  162. package/dist/hooks/useDeferredReorder.d.ts +32 -0
  163. package/dist/hooks/useDeferredReorder.js +47 -0
  164. package/dist/hooks/useDialog.d.ts +5 -0
  165. package/dist/hooks/useDialog.js +17 -3
  166. package/dist/hooks/useKeyboardGrab.d.ts +58 -0
  167. package/dist/hooks/useKeyboardGrab.js +118 -0
  168. package/dist/hooks/useOptimisticValue.d.ts +51 -0
  169. package/dist/hooks/useOptimisticValue.js +61 -0
  170. package/dist/hooks/useReducedMotion.d.ts +4 -1
  171. package/dist/hooks/useReducedMotion.js +7 -1
  172. package/dist/hooks/useResolvedFilterLabels.d.ts +15 -0
  173. package/dist/hooks/useResolvedFilterLabels.js +34 -0
  174. package/dist/hooks/useSlidingIndicator.d.ts +18 -0
  175. package/dist/hooks/useSlidingIndicator.js +80 -0
  176. package/dist/hooks/useVirtualRows.d.ts +21 -2
  177. package/dist/hooks/useVirtualRows.js +30 -3
  178. package/dist/icons/Icon.d.ts +1 -1
  179. package/dist/index.cjs +19687 -13423
  180. package/dist/index.d.cts +3419 -651
  181. package/dist/index.d.ts +48 -18
  182. package/dist/index.js +153 -9
  183. package/dist/labels-context.d.ts +93 -3
  184. package/dist/motion.d.ts +11 -0
  185. package/dist/motion.js +35 -0
  186. package/dist/overlay-stack.d.ts +6 -0
  187. package/dist/overlay-stack.js +13 -0
  188. package/dist/qr-model.d.ts +65 -0
  189. package/dist/qr-model.js +323 -0
  190. package/dist/relationship-graph-model.d.ts +92 -3
  191. package/dist/relationship-graph-model.js +134 -15
  192. package/dist/reorder.d.ts +30 -0
  193. package/dist/reorder.js +74 -0
  194. package/dist/rich-text.cjs +46 -4
  195. package/dist/scheduling-resource-model.d.ts +76 -0
  196. package/dist/scheduling-resource-model.js +74 -0
  197. package/dist/search-suggest-model.js +3 -1
  198. package/dist/shell-context.d.ts +11 -0
  199. package/dist/shell-context.js +7 -0
  200. package/dist/time-field-model.d.ts +46 -0
  201. package/dist/time-field-model.js +152 -0
  202. package/dist/toast-store.d.ts +22 -3
  203. package/dist/toast-store.js +26 -9
  204. package/package.json +2 -1
@@ -1,8 +1,15 @@
1
+ /**
2
+ * A colour that a node or an edge shares with a legend item (HAR-1639). Colour is never the only
3
+ * signal: say the same thing in the node's text, the edge's `description` or its `variant`.
4
+ */
5
+ type RelationshipGraphTone = 'neutral' | 'info' | 'success' | 'warning' | 'danger' | 'accent';
1
6
  interface RelationshipGraphNode {
2
7
  id: string;
3
8
  label: string;
4
9
  type?: string;
5
10
  description?: string;
11
+ /** Layered layout: the node's border and edge mark take this tone (HAR-1639). */
12
+ tone?: RelationshipGraphTone;
6
13
  x?: number;
7
14
  y?: number;
8
15
  /** Layered layout: the node's column. Omitted → BFS distance from the root along source→target. */
@@ -20,6 +27,18 @@ interface RelationshipGraphEdge {
20
27
  arrow?: RelationshipGraphArrow;
21
28
  /** Layered layout: always draw this edge's label, whatever the label policy. */
22
29
  emphasis?: boolean;
30
+ /**
31
+ * `dashed` draws the line dashed, e.g. an expected relation that is missing (HAR-1639). Default
32
+ * `solid`; a lateral or cycle edge is dashed either way.
33
+ */
34
+ variant?: 'solid' | 'dashed';
35
+ /** The line and arrowhead take this tone (HAR-1639). */
36
+ tone?: RelationshipGraphTone;
37
+ /**
38
+ * Said to assistive technology with the relationship, on both nodes it joins, e.g. "expected,
39
+ * missing" (HAR-1639). The drawn line itself is decoration; the nodes carry the relationship.
40
+ */
41
+ description?: string;
23
42
  }
24
43
  interface PositionedRelationshipNode extends RelationshipGraphNode {
25
44
  x: number;
@@ -35,7 +54,12 @@ declare function boundRelationshipGraph(nodes: RelationshipGraphNode[], edges: R
35
54
  declare function relationshipNeighbors(nodeId: string, edges: RelationshipGraphEdge[]): string[];
36
55
  declare function layoutRelationshipGraph(nodes: RelationshipGraphNode[], edges: RelationshipGraphEdge[], rootId?: string): PositionedRelationshipNode[];
37
56
  declare function traverseRelationshipNode(nodes: RelationshipGraphNode[], currentId: string | undefined, direction: -1 | 1): string | undefined;
38
- type LayeredEdgeKind = 'forward' | 'lateral' | 'cycle';
57
+ /**
58
+ * `forward` steps one column right; `skip` goes right across more than one column; `lateral`
59
+ * stays in its column; `back` goes left without closing a loop; `cycle` goes left and closes one
60
+ * (HAR-1639: only a cycle is flagged as one, and only lateral and cycle edges are dashed).
61
+ */
62
+ type LayeredEdgeKind = 'forward' | 'skip' | 'lateral' | 'back' | 'cycle';
39
63
  interface LayeredLayoutOptions {
40
64
  rootId?: string;
41
65
  /** Minimum sibling group size that collapses into a cluster. Default 6; 0 disables. */
@@ -70,7 +94,7 @@ interface LayeredItem {
70
94
  children: string[];
71
95
  /** Set on the first member of an expanded cluster: the group its collapse control belongs to. */
72
96
  expandedGroup?: RelationshipGraphCluster;
73
- /** True when a lateral or cycle edge touches this node. */
97
+ /** True when a cycle edge touches this node (HAR-1639: no longer set by skip, lateral or back edges). */
74
98
  inCycle: boolean;
75
99
  /** Edges (any kind) touching this item. */
76
100
  degree: number;
@@ -87,6 +111,10 @@ interface RoutedEdge {
87
111
  label?: string;
88
112
  arrow: RelationshipGraphArrow;
89
113
  emphasis: boolean;
114
+ /** The first underlying edge's `variant`, `tone` and `description`. */
115
+ variant: 'solid' | 'dashed';
116
+ tone?: RelationshipGraphTone;
117
+ description?: string;
90
118
  path: string;
91
119
  labelX: number;
92
120
  labelY: number;
@@ -96,14 +124,32 @@ interface RoutedEdge {
96
124
  arrowAngle: number;
97
125
  /** Label policy `auto` shows this label without interaction. */
98
126
  autoLabel: boolean;
127
+ /**
128
+ * The label is said once by the target column's header (`LayeredColumn.edgeLabel`), so the
129
+ * edge itself never draws it under `edgeLabels="auto"` (HAR-1387: 197 of 200 labels on a
130
+ * 300-node map read "depends on").
131
+ */
132
+ columnLabel: boolean;
133
+ /**
134
+ * One of a busy node's forward edges, drawn as a bus: along a shared trunk in the column gap
135
+ * and a short stub into the target, so a fan-out reads as one line, not a black fan (HAR-1387).
136
+ */
137
+ bus: boolean;
99
138
  /** The label wrapped to fit the column gap (at most two lines). */
100
139
  labelLines: string[];
140
+ /** The widest chip the label may take at its place (map units): the column gap, or a bus stub. */
141
+ labelRoom: number;
101
142
  }
102
143
  interface LayeredColumn {
103
144
  depth: number;
104
145
  x: number;
105
146
  /** Underlying nodes in the column, cluster members included. */
106
147
  count: number;
148
+ /**
149
+ * The relation most forward edges into this column share, when the column takes more than a
150
+ * few of them: the header says it once and those edges draw no label (HAR-1387).
151
+ */
152
+ edgeLabel?: string;
107
153
  }
108
154
  interface LayeredLayout {
109
155
  rootId: string | undefined;
@@ -135,8 +181,51 @@ declare function classifyLayeredEdges(nodes: RelationshipGraphNode[], edges: Rel
135
181
  declare function clusterIdFor(parentId: string, type: string | undefined, edgeType: string | undefined): string;
136
182
  /** Layered, clustered, crossing-reduced layout for `RelationshipGraph layout="layered"`. */
137
183
  declare function layoutLayeredGraph(nodes: RelationshipGraphNode[], edges: RelationshipGraphEdge[], options?: LayeredLayoutOptions): LayeredLayout;
184
+ /** An edge label's box on the stage, for `separateEdgeLabels`. */
185
+ interface EdgeLabelBox {
186
+ id: string;
187
+ /** Centre of the label. */
188
+ x: number;
189
+ y: number;
190
+ width: number;
191
+ height: number;
192
+ /** Placed first when labels compete for room (a toned, emphasised or one-of-a-kind label). */
193
+ priority?: number;
194
+ }
195
+ /** A box an edge label must not cover (a node), by its top-left corner. */
196
+ interface LabelObstacle {
197
+ x: number;
198
+ y: number;
199
+ width: number;
200
+ height: number;
201
+ }
202
+ /** Where a label is drawn after `separateEdgeLabels`, or that it is left out. */
203
+ interface PlacedEdgeLabel {
204
+ x: number;
205
+ y: number;
206
+ hidden: boolean;
207
+ }
208
+ /** The chip a wrapped edge label takes, from its lines (the meta type size, as `wrapEdgeLabel` assumes). */
209
+ declare function edgeLabelSize(lines: readonly string[]): {
210
+ width: number;
211
+ height: number;
212
+ };
213
+ /**
214
+ * Moves edge labels apart where they would be drawn over each other (HAR-1639: two labels on top
215
+ * of each other in a dense layered column). Labels are taken top to bottom; one that overlaps a
216
+ * label already placed moves down just below it. A label that would have to move further than
217
+ * `maxShift` from its edge is left out instead (the relationship is still in the nodes'
218
+ * descriptions and in the list), and so is one that would cover an `obstacle` (a node). Labels with
219
+ * a higher `priority` are placed first, so a repeated relation gives way to a one-of-a-kind or
220
+ * toned one. Deterministic: the same boxes give the same placement.
221
+ */
222
+ declare function separateEdgeLabels(boxes: readonly EdgeLabelBox[], options?: {
223
+ gap?: number;
224
+ maxShift?: number;
225
+ obstacles?: readonly LabelObstacle[];
226
+ }): Map<string, PlacedEdgeLabel>;
138
227
  type LayeredNavigationKey = 'ArrowUp' | 'ArrowDown' | 'ArrowLeft' | 'ArrowRight' | 'Home' | 'End';
139
228
  /** Keyboard traversal over a layered layout: ←/→ along forward edges, ↑/↓ within a column. */
140
229
  declare function layeredNeighbor(layout: LayeredLayout, currentId: string | undefined, key: LayeredNavigationKey): string | undefined;
141
230
 
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 };
231
+ export { type BoundedRelationshipGraph, type EdgeLabelBox, type LabelObstacle, type LayeredColumn, type LayeredEdgeClassification, type LayeredEdgeKind, type LayeredItem, type LayeredLayout, type LayeredLayoutOptions, type LayeredNavigationKey, type PlacedEdgeLabel, type PositionedRelationshipNode, type RelationshipGraphArrow, type RelationshipGraphCluster, type RelationshipGraphEdge, type RelationshipGraphNode, type RelationshipGraphTone, type RoutedEdge, boundRelationshipGraph, classifyLayeredEdges, clusterIdFor, edgeLabelSize, layeredNeighbor, layoutLayeredGraph, layoutRelationshipGraph, relationshipNeighbors, separateEdgeLabels, traverseRelationshipNode, wrapEdgeLabel };
@@ -68,30 +68,37 @@ const LAYERED_PADDING = 16;
68
68
  const LAYERED_HEADER = 28;
69
69
  const GROUP_HEADER = 28;
70
70
  const AUTO_LABEL_MAX_FANOUT = 4;
71
- const LABEL_CHAR_WIDTH = 5.6;
71
+ const LABEL_CHAR_WIDTH = 6.3;
72
+ const LABEL_PAD_X = 6;
73
+ const LABEL_PAD_Y = 3;
74
+ const LABEL_LINE = 15;
75
+ const BUS_INSET = 4;
76
+ const ARROW_LENGTH = 9;
72
77
  const LABEL_MAX_LINES = 2;
73
78
  function wrapEdgeLabel(label, width) {
74
79
  const max = Math.max(4, Math.floor(width / LABEL_CHAR_WIDTH));
75
80
  const clip = (word) => word.length > max ? `${word.slice(0, max - 1)}\u2026` : word;
81
+ const words = label.split(/\s+/).filter(Boolean);
76
82
  const lines = [];
77
83
  let current = "";
78
- for (const word of label.split(/\s+/).filter(Boolean)) {
84
+ let placed = 0;
85
+ for (const word of words) {
79
86
  const next = current ? `${current} ${word}` : word;
80
87
  if (next.length <= max) {
81
88
  current = next;
89
+ placed += 1;
82
90
  continue;
83
91
  }
84
92
  if (current) lines.push(current);
85
- current = clip(word);
93
+ current = "";
86
94
  if (lines.length === LABEL_MAX_LINES) break;
95
+ current = clip(word);
96
+ placed += 1;
87
97
  }
88
98
  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
- }
99
+ if (placed < words.length && lines.length > 0) {
100
+ const last = lines[lines.length - 1];
101
+ if (!last.endsWith("\u2026")) lines[lines.length - 1] = last.length < max ? `${last}\u2026` : `${last.slice(0, max - 1)}\u2026`;
95
102
  }
96
103
  return lines;
97
104
  }
@@ -142,6 +149,8 @@ function classifyLayeredEdges(nodes, edges, rootId = nodes[0]?.id) {
142
149
  const list = forwardOut.get(edge.source);
143
150
  if (list) list.push(edge.target);
144
151
  else forwardOut.set(edge.source, [edge.target]);
152
+ } else if (to > from + 1) {
153
+ kindById.set(edge.id, "skip");
145
154
  } else if (to < from) {
146
155
  backEdges.push(edge);
147
156
  } else {
@@ -166,13 +175,20 @@ function classifyLayeredEdges(nodes, edges, rootId = nodes[0]?.id) {
166
175
  }
167
176
  }
168
177
  }
169
- kindById.set(edge.id, closes ? "cycle" : "lateral");
178
+ kindById.set(edge.id, closes ? "cycle" : "back");
170
179
  }
171
180
  return { depthById, kindById };
172
181
  }
173
182
  function clusterIdFor(parentId, type, edgeType) {
174
183
  return `cluster:${parentId}:${type ?? ""}:${edgeType ?? ""}`;
175
184
  }
185
+ function busPath(startX, startY, trunk, endX, endY) {
186
+ const dy = endY - startY;
187
+ if (Math.abs(dy) < 1) return `M${round1(startX)} ${round1(startY)} H${round1(endX)}`;
188
+ const r = Math.min(8, Math.abs(dy) / 2, trunk - startX, endX - trunk);
189
+ const dir = Math.sign(dy);
190
+ return `M${round1(startX)} ${round1(startY)} H${round1(trunk - r)} Q${round1(trunk)} ${round1(startY)} ${round1(trunk)} ${round1(startY + dir * r)} V${round1(endY - dir * r)} Q${round1(trunk)} ${round1(endY)} ${round1(trunk + r)} ${round1(endY)} H${round1(endX)}`;
191
+ }
176
192
  const mean = (values) => values.reduce((sum, value) => sum + value, 0) / values.length;
177
193
  const round1 = (value) => Math.round(value * 10) / 10;
178
194
  function layoutLayeredGraph(nodes, edges, options = {}) {
@@ -200,6 +216,7 @@ function layoutLayeredGraph(nodes, edges, options = {}) {
200
216
  const forwardParents = /* @__PURE__ */ new Map();
201
217
  const forwardChildCount = /* @__PURE__ */ new Map();
202
218
  const inCycle = /* @__PURE__ */ new Set();
219
+ const crossLinked = /* @__PURE__ */ new Set();
203
220
  const nodeDegree = /* @__PURE__ */ new Map();
204
221
  for (const edge of drawable) {
205
222
  nodeDegree.set(edge.source, (nodeDegree.get(edge.source) ?? 0) + 1);
@@ -210,14 +227,18 @@ function layoutLayeredGraph(nodes, edges, options = {}) {
210
227
  else forwardParents.set(edge.target, [edge]);
211
228
  forwardChildCount.set(edge.source, (forwardChildCount.get(edge.source) ?? 0) + 1);
212
229
  } else {
213
- inCycle.add(edge.source);
214
- inCycle.add(edge.target);
230
+ crossLinked.add(edge.source);
231
+ crossLinked.add(edge.target);
232
+ if (kindById.get(edge.id) === "cycle") {
233
+ inCycle.add(edge.source);
234
+ inCycle.add(edge.target);
235
+ }
215
236
  }
216
237
  }
217
238
  const groups = /* @__PURE__ */ new Map();
218
239
  if (threshold > 0) {
219
240
  for (const node of nodes) {
220
- if (node.id === rootId || inCycle.has(node.id) || (forwardChildCount.get(node.id) ?? 0) > 0) continue;
241
+ if (node.id === rootId || crossLinked.has(node.id) || (forwardChildCount.get(node.id) ?? 0) > 0) continue;
221
242
  const parents = forwardParents.get(node.id) ?? [];
222
243
  if (parents.length !== 1) continue;
223
244
  const via = parents[0];
@@ -369,7 +390,33 @@ function layoutLayeredGraph(nodes, edges, options = {}) {
369
390
  let labelY;
370
391
  let startAngle;
371
392
  let endAngle;
372
- if (kind === "forward") {
393
+ if (kind === "skip") {
394
+ startX = from.x + from.width;
395
+ startY = from.y + from.height / 2;
396
+ endX = to.x;
397
+ endY = to.y + to.height / 2;
398
+ let floor = Math.max(startY, endY);
399
+ for (const item of itemById.values()) if (item.depth > from.depth && item.depth < to.depth) floor = Math.max(floor, item.y + item.height);
400
+ const channel = floor + 18;
401
+ const half = columnGap / 2;
402
+ path = `M${round1(startX)} ${round1(startY)} C${round1(startX + half)} ${round1(startY)} ${round1(startX + half)} ${round1(channel)} ${round1(startX + half)} ${round1(channel)} L${round1(endX - half)} ${round1(channel)} C${round1(endX - half)} ${round1(channel)} ${round1(endX - half)} ${round1(endY)} ${round1(endX)} ${round1(endY)}`;
403
+ labelX = (startX + endX) / 2;
404
+ labelY = channel;
405
+ startAngle = Math.PI;
406
+ endAngle = 0;
407
+ height = Math.max(height, channel + 8);
408
+ } else if (kind === "forward" && from.children.length > AUTO_LABEL_MAX_FANOUT) {
409
+ startX = from.x + from.width;
410
+ startY = from.y + from.height / 2;
411
+ endX = to.x;
412
+ endY = to.y + to.height / 2;
413
+ const trunk = startX + BUS_INSET;
414
+ path = busPath(startX, startY, trunk, endX, endY);
415
+ labelX = (trunk + BUS_INSET + endX - ARROW_LENGTH) / 2;
416
+ labelY = endY;
417
+ startAngle = Math.PI;
418
+ endAngle = 0;
419
+ } else if (kind === "forward") {
373
420
  startX = from.x + from.width;
374
421
  startY = from.y + from.height / 2;
375
422
  endX = to.x;
@@ -415,6 +462,7 @@ function layoutLayeredGraph(nodes, edges, options = {}) {
415
462
  edgeIds: [edge.id],
416
463
  arrow,
417
464
  emphasis: edge.emphasis === true,
465
+ variant: edge.variant === "dashed" ? "dashed" : "solid",
418
466
  path,
419
467
  labelX: round1(labelX),
420
468
  labelY: round1(labelY),
@@ -422,17 +470,55 @@ function layoutLayeredGraph(nodes, edges, options = {}) {
422
470
  arrowY: round1(atStart ? startY : endY),
423
471
  arrowAngle: atStart ? startAngle : endAngle,
424
472
  labelLines: [],
473
+ labelRoom: columnGap - 8,
474
+ columnLabel: false,
475
+ bus: kind === "forward" && from.children.length > AUTO_LABEL_MAX_FANOUT,
425
476
  autoLabel: to.kind !== "cluster" && (edge.emphasis === true || kind === "forward" && from.children.length <= AUTO_LABEL_MAX_FANOUT)
426
477
  };
427
478
  if (edge.type !== void 0) entry.type = edge.type;
479
+ if (edge.tone !== void 0) entry.tone = edge.tone;
480
+ if (edge.description !== void 0) entry.description = edge.description;
428
481
  const label = edge.label ?? edge.type;
429
482
  if (label !== void 0) entry.label = label;
430
483
  routed.set(key, entry);
431
484
  }
485
+ const intoColumn = /* @__PURE__ */ new Map();
486
+ for (const entry of routed.values()) {
487
+ if (entry.kind !== "forward" || entry.label === void 0) continue;
488
+ const depth = itemById.get(entry.target).depth;
489
+ const list = intoColumn.get(depth);
490
+ if (list) list.push(entry);
491
+ else intoColumn.set(depth, [entry]);
492
+ }
493
+ for (const [depth, entries] of intoColumn) {
494
+ if (entries.length <= AUTO_LABEL_MAX_FANOUT) continue;
495
+ const counts = /* @__PURE__ */ new Map();
496
+ for (const entry of entries) counts.set(entry.label, (counts.get(entry.label) ?? 0) + 1);
497
+ let dominant;
498
+ let most = 1;
499
+ for (const [label, count] of counts) if (count > most) {
500
+ dominant = label;
501
+ most = count;
502
+ }
503
+ if (dominant === void 0) continue;
504
+ const column = columns.find((candidate) => candidate.depth === depth);
505
+ if (column) column.edgeLabel = dominant;
506
+ for (const entry of entries) {
507
+ if (entry.emphasis) continue;
508
+ if (entry.label === dominant) {
509
+ entry.columnLabel = true;
510
+ entry.autoLabel = false;
511
+ } else if (itemById.get(entry.target).kind !== "cluster") {
512
+ entry.autoLabel = true;
513
+ }
514
+ }
515
+ }
432
516
  const seenLabels = /* @__PURE__ */ new Set();
433
517
  for (const entry of routed.values()) {
434
518
  if (entry.label === void 0) continue;
435
- entry.labelLines = wrapEdgeLabel(entry.label, columnGap - 8);
519
+ const run = entry.bus ? columnGap - 2 * BUS_INSET - ARROW_LENGTH : columnGap - 8;
520
+ entry.labelRoom = run;
521
+ entry.labelLines = wrapEdgeLabel(entry.label, run - 2 * LABEL_PAD_X);
436
522
  if (!entry.autoLabel || entry.emphasis || entry.kind !== "forward") continue;
437
523
  const key = `${entry.source}|${entry.label}`;
438
524
  if (seenLabels.has(key)) entry.autoLabel = false;
@@ -452,6 +538,37 @@ function layoutLayeredGraph(nodes, edges, options = {}) {
452
538
  headerHeight: LAYERED_HEADER
453
539
  };
454
540
  }
541
+ function edgeLabelSize(lines) {
542
+ const longest = lines.reduce((max, line) => Math.max(max, line.length), 0);
543
+ return { width: longest * LABEL_CHAR_WIDTH + 2 * LABEL_PAD_X, height: Math.max(1, lines.length) * LABEL_LINE + 2 * LABEL_PAD_Y };
544
+ }
545
+ function separateEdgeLabels(boxes, options = {}) {
546
+ const gap = options.gap ?? 2;
547
+ const maxShift = options.maxShift ?? 48;
548
+ const placed = [];
549
+ const result = /* @__PURE__ */ new Map();
550
+ const obstacles = options.obstacles ?? [];
551
+ const ordered = [...boxes].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0) || a.y - b.y || a.x - b.x || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
552
+ for (const box of ordered) {
553
+ const left = box.x - box.width / 2;
554
+ const right = box.x + box.width / 2;
555
+ let top = box.y - box.height / 2;
556
+ for (let guard = 0; guard <= placed.length; guard += 1) {
557
+ const hit = placed.find((other) => left < other.right && other.left < right && top < other.bottom + gap && other.top - gap < top + box.height);
558
+ if (!hit) break;
559
+ top = hit.bottom + gap;
560
+ }
561
+ const shift = top - (box.y - box.height / 2);
562
+ const covers = obstacles.some((o) => left < o.x + o.width && o.x < right && top < o.y + o.height && o.y < top + box.height);
563
+ if (shift > maxShift || covers) {
564
+ result.set(box.id, { x: box.x, y: box.y, hidden: true });
565
+ continue;
566
+ }
567
+ placed.push({ left, right, top, bottom: top + box.height });
568
+ result.set(box.id, { x: box.x, y: round1(top + box.height / 2), hidden: false });
569
+ }
570
+ return result;
571
+ }
455
572
  function layeredNeighbor(layout, currentId, key) {
456
573
  const root = layout.rootId !== void 0 ? layout.itemOf[layout.rootId] : layout.items[0]?.id;
457
574
  const current = layout.items.find((item) => item.id === currentId);
@@ -478,10 +595,12 @@ export {
478
595
  boundRelationshipGraph,
479
596
  classifyLayeredEdges,
480
597
  clusterIdFor,
598
+ edgeLabelSize,
481
599
  layeredNeighbor,
482
600
  layoutLayeredGraph,
483
601
  layoutRelationshipGraph,
484
602
  relationshipNeighbors,
603
+ separateEdgeLabels,
485
604
  traverseRelationshipNode,
486
605
  wrapEdgeLabel
487
606
  };
@@ -0,0 +1,30 @@
1
+ import { PointerEvent } from 'react';
2
+
3
+ /** Move `id` to `index` within `ids`. Returns `ids` itself when nothing moves. */
4
+ declare function placeId(ids: readonly string[], id: string, index: number): readonly string[];
5
+ /** True when two orders hold the same ids in the same order. */
6
+ declare const sameOrder: (a: readonly string[], b: readonly string[]) => boolean;
7
+ /**
8
+ * Where a dragged row lands: how many of the OTHER rows have their middle above the pointer.
9
+ * The rows reorder live while dragging, so counting the dragged row itself would push it one
10
+ * slot past the pointer every time it moves down.
11
+ */
12
+ declare function dropIndex(rows: readonly Element[], dragged: Element | null, clientY: number): number;
13
+ interface RowDragHandlers {
14
+ over: (id: string, index: number) => void;
15
+ drop: (id: string) => void;
16
+ /** The drag was cancelled (Escape, or the browser took the pointer): put the starting order back. */
17
+ cancel?: (id: string) => void;
18
+ }
19
+ /**
20
+ * Pointer drag for a list whose rows reorder live. It listens on the window for the whole drag:
21
+ * React moves the dragged row's DOM node when the order changes, which releases pointer capture on
22
+ * its grip, so after the first move down the grip itself stops receiving move and up events.
23
+ * Nothing animates: the row is placed where the pointer is, frame by frame.
24
+ */
25
+ declare function useRowDrag(rowSelector: string, handlers: RowDragHandlers): {
26
+ dragging: string | null;
27
+ start: (event: PointerEvent<HTMLElement>, id: string) => void;
28
+ };
29
+
30
+ export { type RowDragHandlers, dropIndex, placeId, sameOrder, useRowDrag };
@@ -0,0 +1,74 @@
1
+ "use client";
2
+ import { useEffect, useRef, useState } from "react";
3
+ function placeId(ids, id, index) {
4
+ const from = ids.indexOf(id);
5
+ const to = Math.max(0, Math.min(index, ids.length - 1));
6
+ if (from < 0 || from === to) return ids;
7
+ const next = ids.filter((other) => other !== id);
8
+ next.splice(to, 0, id);
9
+ return next;
10
+ }
11
+ const sameOrder = (a, b) => a.length === b.length && a.every((id, i) => id === b[i]);
12
+ function dropIndex(rows, dragged, clientY) {
13
+ let index = 0;
14
+ for (const row of rows) {
15
+ if (row === dragged) continue;
16
+ const box = row.getBoundingClientRect();
17
+ if (clientY > box.top + box.height / 2) index++;
18
+ }
19
+ return index;
20
+ }
21
+ function useRowDrag(rowSelector, handlers) {
22
+ const [dragging, setDragging] = useState(null);
23
+ const drag = useRef(null);
24
+ const latest = useRef(handlers);
25
+ latest.current = handlers;
26
+ useEffect(() => {
27
+ if (!dragging) return;
28
+ const move = (event) => {
29
+ const current = drag.current;
30
+ if (!current) return;
31
+ latest.current.over(current.id, dropIndex(Array.from(current.list.querySelectorAll(`:scope > ${rowSelector}`)), current.row, event.clientY));
32
+ };
33
+ const finish = (kind) => {
34
+ const current = drag.current;
35
+ drag.current = null;
36
+ setDragging(null);
37
+ if (!current) return;
38
+ if (kind === "drop") latest.current.drop(current.id);
39
+ else latest.current.cancel?.(current.id);
40
+ };
41
+ const end = () => finish("drop");
42
+ const cancel = () => finish("cancel");
43
+ const escape = (event) => {
44
+ if (event.key !== "Escape") return;
45
+ event.preventDefault();
46
+ event.stopPropagation();
47
+ finish("cancel");
48
+ };
49
+ window.addEventListener("pointermove", move);
50
+ window.addEventListener("pointerup", end);
51
+ window.addEventListener("pointercancel", cancel);
52
+ window.addEventListener("keydown", escape, true);
53
+ return () => {
54
+ window.removeEventListener("pointermove", move);
55
+ window.removeEventListener("pointerup", end);
56
+ window.removeEventListener("pointercancel", cancel);
57
+ window.removeEventListener("keydown", escape, true);
58
+ };
59
+ }, [dragging, rowSelector]);
60
+ const start = (event, id) => {
61
+ const row = event.currentTarget.closest("li");
62
+ if (!row?.parentElement) return;
63
+ event.currentTarget.setPointerCapture?.(event.pointerId);
64
+ drag.current = { id, list: row.parentElement, row };
65
+ setDragging(id);
66
+ };
67
+ return { dragging, start };
68
+ }
69
+ export {
70
+ dropIndex,
71
+ placeId,
72
+ sameOrder,
73
+ useRowDrag
74
+ };
@@ -1369,12 +1369,17 @@ function removeAnchorName(el, name) {
1369
1369
  else el.style.removeProperty("anchor-name");
1370
1370
  }
1371
1371
  function translationOf(el) {
1372
- const t = getComputedStyle(el).transform;
1372
+ const style = getComputedStyle(el);
1373
+ const t = style.transform;
1373
1374
  if (!t || t === "none" || typeof DOMMatrixReadOnly === "undefined") return { x: 0, y: 0 };
1374
1375
  try {
1375
1376
  const m = new DOMMatrixReadOnly(t);
1377
+ const [ox = 0, oy = 0] = style.transformOrigin.split(" ").map((v) => parseFloat(v) || 0);
1378
+ const cx2 = el.offsetWidth / 2 - ox;
1379
+ const cy = el.offsetHeight / 2 - oy;
1380
+ const p = m.transformPoint({ x: cx2, y: cy });
1376
1381
  const z = zoomOf(el);
1377
- return { x: m.m41 * z, y: m.m42 * z };
1382
+ return { x: (p.x - cx2) * z, y: (p.y - cy) * z };
1378
1383
  } catch {
1379
1384
  return { x: 0, y: 0 };
1380
1385
  }
@@ -1471,6 +1476,13 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
1471
1476
  const measured = size.width > 0 || size.height > 0;
1472
1477
  if (measured && session.current) side.current = pos.side;
1473
1478
  if (floating.dataset.placement !== pos.placement) floating.dataset.placement = pos.placement;
1479
+ if (measured) {
1480
+ const clamp2 = (v, max) => Math.min(Math.max(v, 0), max);
1481
+ const ox = pos.side === "left" ? size.width : pos.side === "right" ? 0 : clamp2(a.x + a.width / 2 - pos.x, size.width);
1482
+ const oy = pos.side === "top" ? size.height : pos.side === "bottom" ? 0 : clamp2(a.y + a.height / 2 - pos.y, size.height);
1483
+ const origin = `${Math.round(ox / zoom)}px ${Math.round(oy / zoom)}px`;
1484
+ if (s.getPropertyValue("--uix-anchor-origin") !== origin) s.setProperty("--uix-anchor-origin", origin);
1485
+ }
1474
1486
  const writeFixed = (at) => {
1475
1487
  const left2 = `${Math.round(at.x / zoom)}px`;
1476
1488
  const top2 = `${Math.round(at.y / zoom)}px`;
@@ -1567,6 +1579,26 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
1567
1579
  return reposition;
1568
1580
  }
1569
1581
 
1582
+ // src/motion.ts
1583
+ var KEY_OPENED = "data-uix-key-opened";
1584
+ var keyboard = false;
1585
+ var watching = false;
1586
+ function watchInputModality() {
1587
+ if (watching || typeof document === "undefined") return;
1588
+ watching = true;
1589
+ document.addEventListener("keydown", (event) => {
1590
+ if (!["Shift", "Control", "Alt", "Meta"].includes(event.key)) keyboard = true;
1591
+ }, true);
1592
+ document.addEventListener("pointerdown", () => {
1593
+ keyboard = false;
1594
+ }, true);
1595
+ }
1596
+ function markKeyOpened(el) {
1597
+ if (!el) return;
1598
+ if (keyboard) el.setAttribute(KEY_OPENED, "");
1599
+ else el.removeAttribute(KEY_OPENED);
1600
+ }
1601
+
1570
1602
  // src/components/Popover.tsx
1571
1603
  var import_jsx_runtime4 = require("react/jsx-runtime");
1572
1604
  var useIsomorphicLayoutEffect2 = typeof document !== "undefined" ? import_react4.useLayoutEffect : import_react4.useEffect;
@@ -1620,6 +1652,14 @@ function Popover({
1620
1652
  el.hidePopover();
1621
1653
  } : void 0
1622
1654
  });
1655
+ (0, import_react4.useEffect)(() => {
1656
+ watchInputModality();
1657
+ const el = ref.current;
1658
+ if (!el) return;
1659
+ const mark = () => markKeyOpened(el);
1660
+ el.addEventListener("beforetoggle", mark);
1661
+ return () => el.removeEventListener("beforetoggle", mark);
1662
+ }, []);
1623
1663
  (0, import_react4.useEffect)(() => {
1624
1664
  const el = ref.current;
1625
1665
  if (!el || !anchor) return;
@@ -1700,6 +1740,8 @@ function Popover({
1700
1740
  };
1701
1741
  const onKeyDown = (event) => {
1702
1742
  if (event.key !== "Escape" || !isPopoverOpen(el)) return;
1743
+ event.preventDefault();
1744
+ event.stopPropagation();
1703
1745
  window.clearTimeout(timer);
1704
1746
  const hadFocus = el.contains(el.ownerDocument.activeElement);
1705
1747
  try {
@@ -2170,11 +2212,11 @@ function Markdown({
2170
2212
  var import_react6 = require("react");
2171
2213
  var import_jsx_runtime7 = require("react/jsx-runtime");
2172
2214
  var Textarea = (0, import_react6.forwardRef)(
2173
- ({ invalid, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2215
+ ({ invalid, size = "md", className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2174
2216
  "textarea",
2175
2217
  {
2176
2218
  ref,
2177
- className: cx("uix-textarea", className),
2219
+ className: cx("uix-textarea", size === "lg" && "uix-textarea--lg", className),
2178
2220
  "aria-invalid": invalid || void 0,
2179
2221
  ...props
2180
2222
  }