@arbidocs/blocks 0.4.108 → 0.4.110

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 (174) hide show
  1. package/dist/{NativeDocxEditor-UAZUKZ77.js → NativeDocxEditor-4PY4YZYC.js} +3 -3
  2. package/dist/{NativeDocxEditor-UAZUKZ77.js.map → NativeDocxEditor-4PY4YZYC.js.map} +1 -1
  3. package/dist/{NativeDocxEditor-YX7B7OFH.cjs → NativeDocxEditor-WODZAURG.cjs} +7 -7
  4. package/dist/{NativeDocxEditor-YX7B7OFH.cjs.map → NativeDocxEditor-WODZAURG.cjs.map} +1 -1
  5. package/dist/agents/index.cjs +286 -286
  6. package/dist/agents/index.js +10 -10
  7. package/dist/agents/tasks/ConversationListView.d.ts +5 -1
  8. package/dist/agents/tasks/TaskActivityTimeline.d.ts +5 -1
  9. package/dist/calendar/EventDetails.d.ts +1 -1
  10. package/dist/calendar/index.cjs +13 -0
  11. package/dist/calendar/index.cjs.map +1 -1
  12. package/dist/calendar/index.js +13 -0
  13. package/dist/calendar/index.js.map +1 -1
  14. package/dist/calendar/types.d.ts +3 -0
  15. package/dist/calls/CallSidePanel.d.ts +17 -0
  16. package/dist/calls/CallStage.d.ts +57 -0
  17. package/dist/calls/ControlBar.d.ts +91 -0
  18. package/dist/calls/ControlButton.d.ts +44 -0
  19. package/dist/calls/PreJoin.d.ts +31 -0
  20. package/dist/calls/cards.d.ts +54 -0
  21. package/dist/calls/hooks.d.ts +20 -0
  22. package/dist/calls/index.cjs +1853 -0
  23. package/dist/calls/index.cjs.map +1 -0
  24. package/dist/calls/index.d.ts +20 -0
  25. package/dist/calls/index.js +1737 -0
  26. package/dist/calls/index.js.map +1 -0
  27. package/dist/calls/join-choices.d.ts +11 -0
  28. package/dist/calls/layout.d.ts +106 -0
  29. package/dist/calls/panels.d.ts +55 -0
  30. package/dist/calls/status.d.ts +69 -0
  31. package/dist/calls/tiles.d.ts +59 -0
  32. package/dist/chat/Message.d.ts +17 -2
  33. package/dist/chat/TranscriptLine.d.ts +50 -0
  34. package/dist/chat/index.cjs +147 -126
  35. package/dist/chat/index.d.ts +3 -1
  36. package/dist/chat/index.js +5 -4
  37. package/dist/chat/transcript-runs.d.ts +6 -0
  38. package/dist/{chunk-QT6EGMAJ.cjs → chunk-2NIL2ASF.cjs} +38 -294
  39. package/dist/chunk-2NIL2ASF.cjs.map +1 -0
  40. package/dist/{chunk-XMBYDLZQ.cjs → chunk-2UAJVK3C.cjs} +4 -4
  41. package/dist/{chunk-XMBYDLZQ.cjs.map → chunk-2UAJVK3C.cjs.map} +1 -1
  42. package/dist/{chunk-TTXK62YI.cjs → chunk-54WBIVHV.cjs} +5 -5
  43. package/dist/{chunk-TTXK62YI.cjs.map → chunk-54WBIVHV.cjs.map} +1 -1
  44. package/dist/{chunk-7C35WXYB.js → chunk-5HFPRI3W.js} +5 -5
  45. package/dist/chunk-5HFPRI3W.js.map +1 -0
  46. package/dist/{chunk-S7RHJI5Q.js → chunk-6RHHMBZ5.js} +158 -5
  47. package/dist/chunk-6RHHMBZ5.js.map +1 -0
  48. package/dist/{chunk-3Y6NQIDE.js → chunk-A35KT2II.js} +37 -19
  49. package/dist/chunk-A35KT2II.js.map +1 -0
  50. package/dist/{chunk-PGDL26TQ.js → chunk-AY32GGKU.js} +10 -4
  51. package/dist/{chunk-PGDL26TQ.js.map → chunk-AY32GGKU.js.map} +1 -1
  52. package/dist/{chunk-IXALYQT7.cjs → chunk-C2CWWEHO.cjs} +2 -2
  53. package/dist/chunk-C2CWWEHO.cjs.map +1 -0
  54. package/dist/{chunk-S55VDTX3.js → chunk-CECWAIHL.js} +3 -3
  55. package/dist/{chunk-S55VDTX3.js.map → chunk-CECWAIHL.js.map} +1 -1
  56. package/dist/{chunk-XQDM6T6C.js → chunk-CXZ7FPTQ.js} +3 -3
  57. package/dist/{chunk-XQDM6T6C.js.map → chunk-CXZ7FPTQ.js.map} +1 -1
  58. package/dist/{chunk-C2HRZSAT.cjs → chunk-DJM7DLCT.cjs} +5 -5
  59. package/dist/chunk-DJM7DLCT.cjs.map +1 -0
  60. package/dist/{chunk-XQKGYGCJ.js → chunk-DQCYCEDR.js} +3 -3
  61. package/dist/{chunk-XQKGYGCJ.js.map → chunk-DQCYCEDR.js.map} +1 -1
  62. package/dist/{chunk-ZYSY4L5L.cjs → chunk-ELDUWTRR.cjs} +4 -4
  63. package/dist/{chunk-ZYSY4L5L.cjs.map → chunk-ELDUWTRR.cjs.map} +1 -1
  64. package/dist/{chunk-A2MN2ZLX.js → chunk-GKDWQLVU.js} +13 -274
  65. package/dist/chunk-GKDWQLVU.js.map +1 -0
  66. package/dist/{chunk-24ND4S6Y.cjs → chunk-HBBJYRGI.cjs} +162 -5
  67. package/dist/{chunk-S7RHJI5Q.js.map → chunk-HBBJYRGI.cjs.map} +1 -1
  68. package/dist/{chunk-P2KPDPFF.js → chunk-IXTQYFIF.js} +23 -15
  69. package/dist/chunk-IXTQYFIF.js.map +1 -0
  70. package/dist/{chunk-JQVPGMLU.js → chunk-JB32KYAH.js} +3 -3
  71. package/dist/{chunk-JQVPGMLU.js.map → chunk-JB32KYAH.js.map} +1 -1
  72. package/dist/{chunk-PBS5MFIY.cjs → chunk-JK45LSIH.cjs} +3 -3
  73. package/dist/{chunk-PBS5MFIY.cjs.map → chunk-JK45LSIH.cjs.map} +1 -1
  74. package/dist/chunk-MWDW5UBW.cjs +199 -0
  75. package/dist/chunk-MWDW5UBW.cjs.map +1 -0
  76. package/dist/{chunk-ZPLG7QYF.js → chunk-N5YEF3LC.js} +2 -2
  77. package/dist/chunk-N5YEF3LC.js.map +1 -0
  78. package/dist/{chunk-EWSEQEPL.js → chunk-NRUPUDWB.js} +17 -7
  79. package/dist/chunk-NRUPUDWB.js.map +1 -0
  80. package/dist/{chunk-DDF5GPIJ.js → chunk-P5ST47VY.js} +3 -3
  81. package/dist/{chunk-DDF5GPIJ.js.map → chunk-P5ST47VY.js.map} +1 -1
  82. package/dist/{chunk-EGDWNEAG.js → chunk-PEHLUB52.js} +3 -3
  83. package/dist/{chunk-EGDWNEAG.js.map → chunk-PEHLUB52.js.map} +1 -1
  84. package/dist/{chunk-W7NZRZ54.js → chunk-SQNAJPNR.js} +4 -4
  85. package/dist/{chunk-W7NZRZ54.js.map → chunk-SQNAJPNR.js.map} +1 -1
  86. package/dist/{chunk-DTBSGG36.cjs → chunk-U6MPHCEB.cjs} +24 -16
  87. package/dist/chunk-U6MPHCEB.cjs.map +1 -0
  88. package/dist/{chunk-MP6FK43E.cjs → chunk-VP4KJ4LK.cjs} +17 -6
  89. package/dist/chunk-VP4KJ4LK.cjs.map +1 -0
  90. package/dist/{chunk-LLFW2T6S.cjs → chunk-VZKBI2KJ.cjs} +85 -67
  91. package/dist/chunk-VZKBI2KJ.cjs.map +1 -0
  92. package/dist/{chunk-5YXG342J.cjs → chunk-W7ZDBPJ3.cjs} +4 -4
  93. package/dist/{chunk-5YXG342J.cjs.map → chunk-W7ZDBPJ3.cjs.map} +1 -1
  94. package/dist/{chunk-SKSE4OT6.cjs → chunk-WPVIOGDG.cjs} +5 -5
  95. package/dist/{chunk-SKSE4OT6.cjs.map → chunk-WPVIOGDG.cjs.map} +1 -1
  96. package/dist/{chunk-3US5OIDQ.cjs → chunk-XSUOEJB3.cjs} +9 -3
  97. package/dist/chunk-XSUOEJB3.cjs.map +1 -0
  98. package/dist/chunk-Z3AZ7Q5X.js +177 -0
  99. package/dist/chunk-Z3AZ7Q5X.js.map +1 -0
  100. package/dist/{chunk-Z5VUZ6YD.cjs → chunk-ZV3BKLFY.cjs} +3 -3
  101. package/dist/{chunk-Z5VUZ6YD.cjs.map → chunk-ZV3BKLFY.cjs.map} +1 -1
  102. package/dist/citations/index.cjs +10 -10
  103. package/dist/citations/index.js +1 -1
  104. package/dist/collaboration/index.cjs +57 -57
  105. package/dist/collaboration/index.js +3 -3
  106. package/dist/connect/docx.cjs +6 -6
  107. package/dist/connect/docx.js +4 -4
  108. package/dist/connect/index.cjs +87 -87
  109. package/dist/connect/index.js +16 -16
  110. package/dist/databases/index.cjs +2 -2
  111. package/dist/databases/index.js +1 -1
  112. package/dist/docx/definitions/fragment.d.ts +2 -6
  113. package/dist/docx/definitions/port.d.ts +2 -57
  114. package/dist/docx/index.cjs +46 -46
  115. package/dist/docx/index.js +3 -3
  116. package/dist/docx/references/commands.d.ts +3 -31
  117. package/dist/docx/references/numbering.d.ts +3 -47
  118. package/dist/facts/index.cjs +5 -5
  119. package/dist/facts/index.js +4 -4
  120. package/dist/files/GridView.d.ts +4 -1
  121. package/dist/files/TagPassagePanel.d.ts +2 -0
  122. package/dist/files/index.cjs +89 -79
  123. package/dist/files/index.cjs.map +1 -1
  124. package/dist/files/index.js +88 -78
  125. package/dist/files/index.js.map +1 -1
  126. package/dist/home/index.cjs +17 -17
  127. package/dist/home/index.js +3 -3
  128. package/dist/index.cjs +739 -718
  129. package/dist/index.cjs.map +1 -1
  130. package/dist/index.js +22 -21
  131. package/dist/index.js.map +1 -1
  132. package/dist/retrieval/index.cjs +1 -1
  133. package/dist/retrieval/index.js +1 -1
  134. package/dist/settings/FirmPool.d.ts +45 -0
  135. package/dist/settings/index.cjs +134 -37
  136. package/dist/settings/index.cjs.map +1 -1
  137. package/dist/settings/index.d.ts +1 -0
  138. package/dist/settings/index.js +100 -6
  139. package/dist/settings/index.js.map +1 -1
  140. package/dist/shell/index.cjs +12 -12
  141. package/dist/shell/index.js +4 -4
  142. package/dist/site/index.cjs +5 -5
  143. package/dist/site/index.js +1 -1
  144. package/dist/studio/index.cjs +5 -5
  145. package/dist/studio/index.js +4 -4
  146. package/dist/workflow-canvas/index.cjs +53 -53
  147. package/dist/workflow-canvas/index.js +10 -10
  148. package/dist/workspaces/index.cjs +3 -3
  149. package/dist/workspaces/index.cjs.map +1 -1
  150. package/dist/workspaces/index.js +3 -3
  151. package/dist/workspaces/index.js.map +1 -1
  152. package/node_modules/@arbidocs/coedit/dist/commands.cjs +356 -0
  153. package/node_modules/@arbidocs/coedit/dist/commands.cjs.map +1 -0
  154. package/node_modules/@arbidocs/coedit/dist/commands.d.cts +235 -0
  155. package/node_modules/@arbidocs/coedit/dist/commands.d.ts +235 -0
  156. package/node_modules/@arbidocs/coedit/dist/commands.js +338 -0
  157. package/node_modules/@arbidocs/coedit/dist/commands.js.map +1 -0
  158. package/node_modules/@arbidocs/coedit/package.json +6 -1
  159. package/package.json +15 -5
  160. package/src/calls/calls.css +157 -0
  161. package/dist/chunk-24ND4S6Y.cjs.map +0 -1
  162. package/dist/chunk-3US5OIDQ.cjs.map +0 -1
  163. package/dist/chunk-3Y6NQIDE.js.map +0 -1
  164. package/dist/chunk-7C35WXYB.js.map +0 -1
  165. package/dist/chunk-A2MN2ZLX.js.map +0 -1
  166. package/dist/chunk-C2HRZSAT.cjs.map +0 -1
  167. package/dist/chunk-DTBSGG36.cjs.map +0 -1
  168. package/dist/chunk-EWSEQEPL.js.map +0 -1
  169. package/dist/chunk-IXALYQT7.cjs.map +0 -1
  170. package/dist/chunk-LLFW2T6S.cjs.map +0 -1
  171. package/dist/chunk-MP6FK43E.cjs.map +0 -1
  172. package/dist/chunk-P2KPDPFF.js.map +0 -1
  173. package/dist/chunk-QT6EGMAJ.cjs.map +0 -1
  174. package/dist/chunk-ZPLG7QYF.js.map +0 -1
@@ -0,0 +1,199 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ // src/calls/layout.ts
5
+ var TILE_ASPECT = 16 / 9;
6
+ var NOTICE_MS = 6e3;
7
+ var CAPTION_TTL_MS = 8e3;
8
+ var CAPTION_LINES = 2;
9
+ var MAX_GRID_TILES = 9;
10
+ var MIN_CELL_ASPECT = 0.6;
11
+ var MAX_CELL_ASPECT = 2.4;
12
+ var LANDSCAPE_SLACK = 0.85;
13
+ function gridLayout(count, width, height, gap = 8, aspect = TILE_ASPECT) {
14
+ const n = Math.max(0, Math.floor(count));
15
+ if (n === 0 || width <= 0 || height <= 0) {
16
+ return {
17
+ cols: 0,
18
+ rows: 0,
19
+ rowCounts: [],
20
+ tileWidth: 0,
21
+ tileHeight: 0,
22
+ cellWidth: 0,
23
+ cellHeight: 0
24
+ };
25
+ }
26
+ const options = [];
27
+ for (let cols2 = 1; cols2 <= n; cols2++) {
28
+ const rows2 = Math.ceil(n / cols2);
29
+ if (cols2 > 1 && Math.ceil(n / (cols2 - 1)) === rows2) continue;
30
+ const byWidth = (width - gap * (cols2 - 1)) / cols2;
31
+ const byHeight = (height - gap * (rows2 - 1)) / rows2 * aspect;
32
+ options.push({ cols: cols2, rows: rows2, tileWidth: Math.max(0, Math.min(byWidth, byHeight)) });
33
+ }
34
+ const cellAspect = (o) => (width - gap * (o.cols - 1)) / o.cols / ((height - gap * (o.rows - 1)) / o.rows);
35
+ const sane = options.filter((o) => {
36
+ const a = cellAspect(o);
37
+ return a >= MIN_CELL_ASPECT && a <= MAX_CELL_ASPECT;
38
+ });
39
+ const pool = sane.length ? sane : options;
40
+ const largest = pool.reduce((a, b) => b.tileWidth > a.tileWidth + 0.5 ? b : a);
41
+ const landscape = pool.filter((o) => o.cols >= o.rows && o.tileWidth >= largest.tileWidth * LANDSCAPE_SLACK).reduce((a, b) => !a || b.tileWidth > a.tileWidth ? b : a, null);
42
+ const best = landscape ?? largest;
43
+ const { cols, rows } = best;
44
+ const tileWidth = Math.floor(best.tileWidth);
45
+ return {
46
+ cols,
47
+ rows,
48
+ rowCounts: rowCounts(n, cols),
49
+ tileWidth,
50
+ tileHeight: Math.floor(tileWidth / aspect),
51
+ cellWidth: Math.floor((width - gap * (cols - 1)) / cols),
52
+ cellHeight: Math.floor((height - gap * (rows - 1)) / rows)
53
+ };
54
+ }
55
+ function rowCounts(count, cols) {
56
+ if (count <= 0 || cols <= 0) return [];
57
+ const rows = Math.ceil(count / cols);
58
+ const base = Math.floor(count / rows);
59
+ const extra = count % rows;
60
+ return Array.from({ length: rows }, (_, i) => base + (i < extra ? 1 : 0));
61
+ }
62
+ function gridSlots(items, max = MAX_GRID_TILES) {
63
+ if (items.length <= max) return { shown: items, hidden: 0 };
64
+ return { shown: items.slice(0, max - 1), hidden: items.length - (max - 1) };
65
+ }
66
+ var SPEAKER_ACCENTS = [
67
+ "text-blue-text-strong",
68
+ "text-purple-text-strong",
69
+ "text-green-text-strong",
70
+ "text-yellow-text-strong",
71
+ "text-red-text-strong"
72
+ ];
73
+ function speakerIndex(identity) {
74
+ let h = 0;
75
+ for (let i = 0; i < identity.length; i++) h = h * 31 + identity.charCodeAt(i) >>> 0;
76
+ return h % SPEAKER_ACCENTS.length;
77
+ }
78
+ function speakerAccent(identity) {
79
+ return SPEAKER_ACCENTS[speakerIndex(identity)];
80
+ }
81
+ var SPEAKER_BORDERS = [
82
+ "border-blue-text-strong",
83
+ "border-purple-text-strong",
84
+ "border-green-text-strong",
85
+ "border-yellow-text-strong",
86
+ "border-red-text-strong"
87
+ ];
88
+ function speakerBorder(identity) {
89
+ return SPEAKER_BORDERS[speakerIndex(identity)];
90
+ }
91
+ var SPEAKER_DOTS = [
92
+ "bg-blue-text-strong",
93
+ "bg-purple-text-strong",
94
+ "bg-green-text-strong",
95
+ "bg-yellow-text-strong",
96
+ "bg-red-text-strong"
97
+ ];
98
+ function speakerDot(identity) {
99
+ return SPEAKER_DOTS[speakerIndex(identity)];
100
+ }
101
+ function nearestCorner(x, y, width, height) {
102
+ const top = y < height / 2;
103
+ const left = x < width / 2;
104
+ return `${top ? "top" : "bottom"}-${left ? "left" : "right"}`;
105
+ }
106
+ function formatElapsed(ms) {
107
+ const total = Math.max(0, Math.floor(ms / 1e3));
108
+ const h = Math.floor(total / 3600);
109
+ const m = Math.floor(total % 3600 / 60);
110
+ const s = total % 60;
111
+ const ss = String(s).padStart(2, "0");
112
+ return h > 0 ? `${h}:${String(m).padStart(2, "0")}:${ss}` : `${m}:${ss}`;
113
+ }
114
+ function initialsOf(name) {
115
+ const words = name.trim().split(/\s+/).filter((w) => /[\p{L}\p{N}]/u.test(w));
116
+ if (!words.length) return "?";
117
+ const first = words[0].match(/[\p{L}\p{N}]/u)?.[0] ?? "";
118
+ const last = words.length > 1 ? words[words.length - 1].match(/[\p{L}\p{N}]/u)?.[0] ?? "" : "";
119
+ return (first + last).toUpperCase();
120
+ }
121
+ var INITIAL_SPOTLIGHT = { id: null, candidate: null, since: 0 };
122
+ var SPEAKING_LEVEL = 0.08;
123
+ function nextSpotlight(state, levels, now, { threshold = SPEAKING_LEVEL, holdMs = 800, margin = 1.5, exclude = [] } = {}) {
124
+ let loudest = null;
125
+ let max = threshold;
126
+ for (const [id, level] of Object.entries(levels)) {
127
+ if (exclude.includes(id)) continue;
128
+ if (level >= max) {
129
+ max = level;
130
+ loudest = id;
131
+ }
132
+ }
133
+ if (!loudest || loudest === state.id) {
134
+ return state.candidate ? { ...state, candidate: null } : state;
135
+ }
136
+ if (!state.id) return { id: loudest, candidate: null, since: now };
137
+ const current = levels[state.id] ?? 0;
138
+ if (max < current * margin) return state.candidate ? { ...state, candidate: null } : state;
139
+ if (state.candidate !== loudest) return { ...state, candidate: loudest, since: now };
140
+ return now - state.since >= holdMs ? { id: loudest, candidate: null, since: now } : state;
141
+ }
142
+ function pickSpotlight(people, spotlightId) {
143
+ const spot = people.find((p) => p.id === spotlightId && !p.local);
144
+ if (spot) return spot.id;
145
+ return (people.find((p) => !p.local) ?? people[0])?.id;
146
+ }
147
+ function spotlightBox(width, height, others, narrow, gap = 8, aspect = TILE_ASPECT) {
148
+ if (width <= 0 || height <= 0) return { width: 0, height: 0 };
149
+ let w;
150
+ if (narrow) {
151
+ const rows = Math.ceil(others / 3);
152
+ const stripPerWidth = rows > 0 ? rows / 3 / aspect : 0;
153
+ const fixed = rows > 0 ? gap + rows * gap - rows * 2 * gap / 3 / aspect : 0;
154
+ w = Math.min(width, (height - fixed) / (1 / aspect + stripPerWidth));
155
+ } else {
156
+ const avail = others > 0 ? width - 240 - gap : width;
157
+ w = Math.min(avail, height * aspect);
158
+ }
159
+ w = Math.max(0, Math.floor(w));
160
+ return { width: w, height: Math.floor(w / aspect) };
161
+ }
162
+ function stageContentHeight(view, width, maxHeight, count, gap = 8, aspect = TILE_ASPECT) {
163
+ if (width <= 0 || maxHeight <= 0) return 0;
164
+ if (view === "grid") {
165
+ const g = gridLayout(count, width, maxHeight, gap, aspect);
166
+ return Math.min(maxHeight, g.rows * g.tileHeight + Math.max(0, g.rows - 1) * gap);
167
+ }
168
+ const others = Math.max(0, count - 1);
169
+ const narrow = width < 640;
170
+ const box = spotlightBox(width, maxHeight, others, narrow, gap, aspect);
171
+ if (!narrow || others === 0) return box.height;
172
+ const rows = Math.ceil(others / 3);
173
+ const thumb = (box.width - 2 * gap) / 3 / aspect;
174
+ return Math.min(maxHeight, Math.ceil(box.height + gap + rows * thumb + (rows - 1) * gap));
175
+ }
176
+
177
+ exports.CAPTION_LINES = CAPTION_LINES;
178
+ exports.CAPTION_TTL_MS = CAPTION_TTL_MS;
179
+ exports.INITIAL_SPOTLIGHT = INITIAL_SPOTLIGHT;
180
+ exports.MAX_GRID_TILES = MAX_GRID_TILES;
181
+ exports.NOTICE_MS = NOTICE_MS;
182
+ exports.SPEAKER_ACCENTS = SPEAKER_ACCENTS;
183
+ exports.SPEAKING_LEVEL = SPEAKING_LEVEL;
184
+ exports.TILE_ASPECT = TILE_ASPECT;
185
+ exports.formatElapsed = formatElapsed;
186
+ exports.gridLayout = gridLayout;
187
+ exports.gridSlots = gridSlots;
188
+ exports.initialsOf = initialsOf;
189
+ exports.nearestCorner = nearestCorner;
190
+ exports.nextSpotlight = nextSpotlight;
191
+ exports.pickSpotlight = pickSpotlight;
192
+ exports.rowCounts = rowCounts;
193
+ exports.speakerAccent = speakerAccent;
194
+ exports.speakerBorder = speakerBorder;
195
+ exports.speakerDot = speakerDot;
196
+ exports.spotlightBox = spotlightBox;
197
+ exports.stageContentHeight = stageContentHeight;
198
+ //# sourceMappingURL=chunk-MWDW5UBW.cjs.map
199
+ //# sourceMappingURL=chunk-MWDW5UBW.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/calls/layout.ts"],"names":["cols","rows"],"mappings":"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","file":"chunk-MWDW5UBW.cjs","sourcesContent":["/**\n * Pure layout helpers for the call stage: the adaptive tile grid, the per-speaker caption accent,\n * the picture-in-picture corner and the elapsed-time label. No React, no LiveKit: unit-tested on\n * their own.\n */\n\n/** Tiles are 16:9, like the cameras they show. */\nexport const TILE_ASPECT = 16 / 9\n/** How long a status notice (\"Recording — started by X\") stays spelled out before it folds. */\nexport const NOTICE_MS = 6000\n/** How long a caption stays on screen. */\nexport const CAPTION_TTL_MS = 8000\n/** Caption lines shown at once (two lines, the newest at the bottom). */\nexport const CAPTION_LINES = 2\n/** Tiles shown in the grid before the rest fold into a \"+N\" tile. */\nexport const MAX_GRID_TILES = 9\n\n/** Cell shapes (width / height) a filled tile may take before faces get cropped badly. */\nconst MIN_CELL_ASPECT = 0.6\nconst MAX_CELL_ASPECT = 2.4\n/** A row-first grid may be this much smaller than the largest and still win. */\nconst LANDSCAPE_SLACK = 0.85\n\nexport interface GridLayout {\n cols: number\n rows: number\n /** Tiles per row, top to bottom (a short last row is centred: 3 → [2, 1]). */\n rowCounts: number[]\n tileWidth: number\n tileHeight: number\n /** The cell a tile fills when the grid fills the whole stage (the layout's rows × columns). */\n cellWidth: number\n cellHeight: number\n}\n\n/**\n * The grid that gives `count` tiles of a fixed aspect the largest size inside `width × height`\n * with `gap` between them, preferring a row-first layout when it is nearly as large.\n */\nexport function gridLayout(\n count: number,\n width: number,\n height: number,\n gap = 8,\n aspect = TILE_ASPECT\n): GridLayout {\n const n = Math.max(0, Math.floor(count))\n if (n === 0 || width <= 0 || height <= 0) {\n return {\n cols: 0,\n rows: 0,\n rowCounts: [],\n tileWidth: 0,\n tileHeight: 0,\n cellWidth: 0,\n cellHeight: 0,\n }\n }\n const options: { cols: number; rows: number; tileWidth: number }[] = []\n for (let cols = 1; cols <= n; cols++) {\n const rows = Math.ceil(n / cols)\n // A column count that leaves a whole row empty is never better than one fewer column.\n if (cols > 1 && Math.ceil(n / (cols - 1)) === rows) continue\n const byWidth = (width - gap * (cols - 1)) / cols\n const byHeight = ((height - gap * (rows - 1)) / rows) * aspect\n options.push({ cols, rows, tileWidth: Math.max(0, Math.min(byWidth, byHeight)) })\n }\n // Tiles fill their cells, so a cell far from 16:9 crops faces (a 3:1 strip, a 1:2 column):\n // keep to cells within a sane shape when any layout allows it.\n const cellAspect = (o: { cols: number; rows: number }) =>\n (width - gap * (o.cols - 1)) / o.cols / ((height - gap * (o.rows - 1)) / o.rows)\n const sane = options.filter((o) => {\n const a = cellAspect(o)\n return a >= MIN_CELL_ASPECT && a <= MAX_CELL_ASPECT\n })\n const pool = sane.length ? sane : options\n const largest = pool.reduce((a, b) => (b.tileWidth > a.tileWidth + 0.5 ? b : a))\n // Calls read left to right: on a landscape stage a row-first layout (2 side by side, 3 × 2)\n // beats a slightly larger column-first one.\n const landscape = pool\n .filter((o) => o.cols >= o.rows && o.tileWidth >= largest.tileWidth * LANDSCAPE_SLACK)\n .reduce<typeof largest | null>((a, b) => (!a || b.tileWidth > a.tileWidth ? b : a), null)\n const best = landscape ?? largest\n const { cols, rows } = best\n const tileWidth = Math.floor(best.tileWidth)\n return {\n cols,\n rows,\n rowCounts: rowCounts(n, cols),\n tileWidth,\n tileHeight: Math.floor(tileWidth / aspect),\n cellWidth: Math.floor((width - gap * (cols - 1)) / cols),\n cellHeight: Math.floor((height - gap * (rows - 1)) / rows),\n }\n}\n\n/** Spread `count` tiles over rows of at most `cols`, the fuller rows first: 5 in 3 → [3, 2]. */\nexport function rowCounts(count: number, cols: number): number[] {\n if (count <= 0 || cols <= 0) return []\n const rows = Math.ceil(count / cols)\n // Balanced: 7 in 3 columns → [3, 2, 2], not [3, 3, 1].\n const base = Math.floor(count / rows)\n const extra = count % rows\n return Array.from({ length: rows }, (_, i) => base + (i < extra ? 1 : 0))\n}\n\n/** What the grid shows: up to {@link MAX_GRID_TILES}; past that, one slot becomes \"+N more\". */\nexport function gridSlots<T>(items: T[], max = MAX_GRID_TILES): { shown: T[]; hidden: number } {\n if (items.length <= max) return { shown: items, hidden: 0 }\n return { shown: items.slice(0, max - 1), hidden: items.length - (max - 1) }\n}\n\n/**\n * Speaker accents for captions: semantic text tokens that meet AA contrast on the muted surface\n * in both themes. The same identity always gets the same colour.\n */\nexport const SPEAKER_ACCENTS = [\n 'text-blue-text-strong',\n 'text-purple-text-strong',\n 'text-green-text-strong',\n 'text-yellow-text-strong',\n 'text-red-text-strong',\n] as const\n\nfunction speakerIndex(identity: string): number {\n let h = 0\n for (let i = 0; i < identity.length; i++) h = (h * 31 + identity.charCodeAt(i)) >>> 0\n return h % SPEAKER_ACCENTS.length\n}\n\nexport function speakerAccent(identity: string): (typeof SPEAKER_ACCENTS)[number] {\n return SPEAKER_ACCENTS[speakerIndex(identity)]\n}\n\n/** The same speaker's colour as a border (the live transcript line). */\nexport const SPEAKER_BORDERS = [\n 'border-blue-text-strong',\n 'border-purple-text-strong',\n 'border-green-text-strong',\n 'border-yellow-text-strong',\n 'border-red-text-strong',\n] as const\n\nexport function speakerBorder(identity: string): (typeof SPEAKER_BORDERS)[number] {\n return SPEAKER_BORDERS[speakerIndex(identity)]\n}\n\n/** The speaker's colour as an inset ring (the line being spoken). */\nexport const SPEAKER_RINGS = [\n 'ring-blue-text-strong/40',\n 'ring-purple-text-strong/40',\n 'ring-green-text-strong/40',\n 'ring-yellow-text-strong/40',\n 'ring-red-text-strong/40',\n] as const\n\nexport function speakerRing(identity: string): (typeof SPEAKER_RINGS)[number] {\n return SPEAKER_RINGS[speakerIndex(identity)]\n}\n\n/** The speaker's colour as a fill (the \"is speaking\" dots). */\nexport const SPEAKER_DOTS = [\n 'bg-blue-text-strong',\n 'bg-purple-text-strong',\n 'bg-green-text-strong',\n 'bg-yellow-text-strong',\n 'bg-red-text-strong',\n] as const\n\nexport function speakerDot(identity: string): (typeof SPEAKER_DOTS)[number] {\n return SPEAKER_DOTS[speakerIndex(identity)]\n}\n\nexport type PipCorner = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\n\n/** The viewport corner nearest to a point (where a dragged picture-in-picture card settles). */\nexport function nearestCorner(x: number, y: number, width: number, height: number): PipCorner {\n const top = y < height / 2\n const left = x < width / 2\n return `${top ? 'top' : 'bottom'}-${left ? 'left' : 'right'}` as PipCorner\n}\n\n/** `m:ss`, or `h:mm:ss` past an hour. */\nexport function formatElapsed(ms: number): string {\n const total = Math.max(0, Math.floor(ms / 1000))\n const h = Math.floor(total / 3600)\n const m = Math.floor((total % 3600) / 60)\n const s = total % 60\n const ss = String(s).padStart(2, '0')\n return h > 0 ? `${h}:${String(m).padStart(2, '0')}:${ss}` : `${m}:${ss}`\n}\n\n/** Up to two initials for a camera-off tile: \"Alice Morgan\" → \"AM\", \"usr-1a2b\" → \"U\". */\nexport function initialsOf(name: string): string {\n const words = name\n .trim()\n .split(/\\s+/)\n .filter((w) => /[\\p{L}\\p{N}]/u.test(w))\n if (!words.length) return '?'\n const first = words[0].match(/[\\p{L}\\p{N}]/u)?.[0] ?? ''\n const last = words.length > 1 ? (words[words.length - 1].match(/[\\p{L}\\p{N}]/u)?.[0] ?? '') : ''\n return (first + last).toUpperCase()\n}\n\n/** Who has the spotlight (speaker view), and who is waiting to take it. */\nexport interface SpotlightState {\n id: string | null\n /** A louder voice that must stay dominant for `holdMs` before it takes over. */\n candidate: string | null\n since: number\n}\n\nexport const INITIAL_SPOTLIGHT: SpotlightState = { id: null, candidate: null, since: 0 }\n\n/** A voice at or above this level (0..1) counts as speaking. */\nexport const SPEAKING_LEVEL = 0.08\n\n/**\n * The speaker view's spotlight, with hysteresis: a new voice takes the spotlight only after it has\n * dominated (`margin` × louder than the current one, above `threshold`) for `holdMs`, so a cough\n * does not flick the view; while several talk at once and nobody dominates, it stays put. With\n * nobody in the spotlight yet, the first voice takes it at once. `exclude` (my own identity) never\n * takes the spotlight: nobody needs to watch themselves large (Meet / Zoom behaviour).\n */\nexport function nextSpotlight(\n state: SpotlightState,\n levels: Record<string, number>,\n now: number,\n { threshold = SPEAKING_LEVEL, holdMs = 800, margin = 1.5, exclude = [] as readonly string[] } = {}\n): SpotlightState {\n let loudest: string | null = null\n let max = threshold\n for (const [id, level] of Object.entries(levels)) {\n if (exclude.includes(id)) continue\n if (level >= max) {\n max = level\n loudest = id\n }\n }\n if (!loudest || loudest === state.id) {\n return state.candidate ? { ...state, candidate: null } : state\n }\n if (!state.id) return { id: loudest, candidate: null, since: now }\n const current = levels[state.id] ?? 0\n if (max < current * margin) return state.candidate ? { ...state, candidate: null } : state\n if (state.candidate !== loudest) return { ...state, candidate: loudest, since: now }\n return now - state.since >= holdMs ? { id: loudest, candidate: null, since: now } : state\n}\n\n/**\n * Who fills the speaker view's large slot: the spotlight if it is someone else in the call, else\n * the first other person, else me (alone in the call). Never me while anyone else is there.\n */\nexport function pickSpotlight(\n people: readonly { id: string; local: boolean }[],\n spotlightId: string | null\n): string | undefined {\n const spot = people.find((p) => p.id === spotlightId && !p.local)\n if (spot) return spot.id\n return (people.find((p) => !p.local) ?? people[0])?.id\n}\n\n/**\n * The speaker view's large tile: the biggest 16:9 box that fits, leaving room for the filmstrip\n * (below it on a narrow stage — a row of three 16:9 thumbnails per line — or a 15rem column beside\n * it). It never takes a portrait shape: spare height stays blank under the filmstrip.\n */\nexport function spotlightBox(\n width: number,\n height: number,\n others: number,\n narrow: boolean,\n gap = 8,\n aspect = TILE_ASPECT\n): { width: number; height: number } {\n if (width <= 0 || height <= 0) return { width: 0, height: 0 }\n let w: number\n if (narrow) {\n // Filmstrip rows of three under the tile: each row is (w - 2 gaps) / 3 wide at 16:9.\n const rows = Math.ceil(others / 3)\n const stripPerWidth = rows > 0 ? rows / 3 / aspect : 0\n const fixed = rows > 0 ? gap + rows * gap - (rows * 2 * gap) / 3 / aspect : 0\n // h = w / aspect + w * stripPerWidth + fixed ≤ height\n w = Math.min(width, (height - fixed) / (1 / aspect + stripPerWidth))\n } else {\n const avail = others > 0 ? width - 240 - gap : width\n w = Math.min(avail, height * aspect)\n }\n w = Math.max(0, Math.floor(w))\n return { width: w, height: Math.floor(w / aspect) }\n}\n\n/**\n * How tall the call stage's tiles are, for a stage `width` wide and at most `maxHeight` tall:\n * speaker view (`focus`: the large 16:9 tile, plus the filmstrip under it on a narrow stage) or\n * gallery (`grid`). The panel gives the rest of its height to the participant list.\n */\nexport function stageContentHeight(\n view: 'focus' | 'grid',\n width: number,\n maxHeight: number,\n count: number,\n gap = 8,\n aspect = TILE_ASPECT\n): number {\n if (width <= 0 || maxHeight <= 0) return 0\n if (view === 'grid') {\n const g = gridLayout(count, width, maxHeight, gap, aspect)\n return Math.min(maxHeight, g.rows * g.tileHeight + Math.max(0, g.rows - 1) * gap)\n }\n const others = Math.max(0, count - 1)\n const narrow = width < 640\n const box = spotlightBox(width, maxHeight, others, narrow, gap, aspect)\n if (!narrow || others === 0) return box.height\n const rows = Math.ceil(others / 3)\n const thumb = (box.width - 2 * gap) / 3 / aspect\n return Math.min(maxHeight, Math.ceil(box.height + gap + rows * thumb + (rows - 1) * gap))\n}\n"]}
@@ -536,5 +536,5 @@ function GridView({
536
536
  }
537
537
 
538
538
  export { GridView };
539
- //# sourceMappingURL=chunk-ZPLG7QYF.js.map
540
- //# sourceMappingURL=chunk-ZPLG7QYF.js.map
539
+ //# sourceMappingURL=chunk-N5YEF3LC.js.map
540
+ //# sourceMappingURL=chunk-N5YEF3LC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/files/grid-test-ids.ts","../src/files/GridView.tsx"],"names":[],"mappings":"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","file":"chunk-N5YEF3LC.js","sourcesContent":["/**\n * Test-id hooks for AG Grid, which owns its row and cell DOM. AG Grid stamps every row with a\n * `row-id` attribute and every cell with `col-id`; these helpers turn them into `data-testid`s\n * a host chooses, so a custom UI keeps its own test-id scheme.\n */\n\nexport interface GridTestIdOptions<T> {\n /** Test id of the row for `item` (set on the centre-column row element). */\n getRowTestId?: (item: T) => string\n /** Test id of the cell in column `colId` of `item`. */\n getCellTestId?: (colId: string, item: T) => string\n}\n\n/** Stamp `data-testid`s onto the rows and cells under `root`. Idempotent. */\nexport function applyGridTestIds<T>(\n root: ParentNode,\n itemsById: Map<string, T>,\n { getRowTestId, getCellTestId }: GridTestIdOptions<T>\n): void {\n if (!getRowTestId && !getCellTestId) return\n root.querySelectorAll<HTMLElement>('.ag-center-cols-container .ag-row[row-id]').forEach((row) => {\n const item = itemsById.get(row.getAttribute('row-id') ?? '')\n if (item === undefined) return\n if (getRowTestId) row.setAttribute('data-testid', getRowTestId(item))\n if (getCellTestId) {\n row.querySelectorAll<HTMLElement>('.ag-cell[col-id]').forEach((cell) => {\n cell.setAttribute('data-testid', getCellTestId(cell.getAttribute('col-id') ?? '', item))\n })\n }\n })\n}\n\n/**\n * Keep the ids applied as AG Grid creates and recycles rows (it virtualises). Returns the\n * disconnect function.\n */\nexport function watchGridTestIds<T>(\n root: HTMLElement,\n getItemsById: () => Map<string, T>,\n options: GridTestIdOptions<T>\n): () => void {\n if (!options.getRowTestId && !options.getCellTestId) return () => {}\n const run = () => applyGridTestIds(root, getItemsById(), options)\n run()\n if (typeof MutationObserver === 'undefined') return () => {}\n const observer = new MutationObserver(run)\n observer.observe(root, { childList: true, subtree: true })\n return () => observer.disconnect()\n}\n","/**\n * GridView — a flexible AG Grid presentational core.\n *\n * Pure and app-agnostic: it owns no global state and pulls in no store. Column\n * state persistence (order / width / visibility / sort / filter) is exposed as a\n * controlled/uncontrolled prop pair — `gridState` (initial state, read once at\n * mount) and `onGridStateChange` (called whenever AG Grid's state changes). An\n * app wires those to wherever it keeps the state (Zustand, localStorage, a URL,\n * a server) via a thin adapter; the core stays decoupled.\n *\n * Themed here (colours/fonts/borders via the host CSS variables, so it inherits the host's\n * light/dark theme). The structural helper classes (cell-numeric, multiline clamp, scrollbars,\n * row kebabs) live in `@arbidocs/blocks/grid.css`, which the consuming app imports globally.\n */\n\nimport React, { useRef, useCallback, useMemo, useEffect } from 'react'\nimport { AgGridReact } from 'ag-grid-react'\nimport {\n ColDef,\n GridOptions,\n GridApi,\n IRowNode,\n GridState,\n themeQuartz,\n CellValueChangedEvent,\n GridReadyEvent,\n RowClickedEvent,\n} from 'ag-grid-community'\nimport { AllCommunityModule } from 'ag-grid-community'\nimport { BREAKPOINT_QUERIES, useMediaQuery } from '../lib/use-media-query'\nimport { watchGridTestIds } from './grid-test-ids'\n\n// Create the custom ARBI theme using themeQuartz\n// Colours reference the host CSS variables (hsl(var(--token))), so dark mode follows the app.\n// This enables dark mode support and consistent theming\nconst cssVar = (name: string, alpha?: number): string =>\n alpha === undefined ? `hsl(var(--${name}))` : `hsl(var(--${name}) / ${alpha})`\n\nconst arbiTheme = themeQuartz.withParams({\n // Variant B palette: white canvas, quiet header, muted hover nudge, accent-tint selection.\n // Every colour is a host CSS variable, so light/dark follow the app with no .dark overrides.\n backgroundColor: cssVar('background'),\n foregroundColor: cssVar('foreground'),\n borderColor: cssVar('border'),\n headerBackgroundColor: cssVar('background'),\n headerTextColor: cssVar('muted-foreground'),\n oddRowBackgroundColor: cssVar('background'),\n rowHoverColor: cssVar('muted', 0.6),\n // Checkbox-selected rows: a light accent tint. A row opened elsewhere (e.g. the viewer) may take\n // the full accent, so the two states stay distinguishable.\n selectedRowBackgroundColor: cssVar('accent', 0.6),\n rangeSelectionBackgroundColor: cssVar('accent'),\n accentColor: cssVar('primary'),\n inputFocusBorder: { color: cssVar('primary') },\n headerColumnBorder: false,\n // A faint short tick marks every column edge (so resizing is discoverable without a full\n // divider line); it strengthens while the header is hovered (see grid.css).\n headerColumnResizeHandleColor: cssVar('border'),\n headerColumnResizeHandleHeight: '40%',\n headerColumnResizeHandleWidth: 1,\n\n // Font settings — inherit the host app's font. A googleFont here never\n // loaded under the apps' CSP, so every grid silently fell back to the\n // browser's default SERIF and read as unstyled.\n fontFamily: 'inherit',\n fontSize: 14,\n headerFontSize: 12.5,\n headerFontWeight: 500,\n\n // Sizing\n headerHeight: 44,\n rowHeight: 40,\n cellHorizontalPadding: 16,\n\n // Borders - structural settings only.\n // Row border is a real hairline in the theme border colour (not hardcoded\n // white, which was invisible on the light-mode background and wrong in dark\n // mode) so adjacent rows are clearly separated — important now that rows can\n // be tall and multi-line (e.g. the Abstract column).\n rowBorder: { style: 'solid', width: 1, color: 'hsl(var(--border))' },\n columnBorder: false, // Row hairlines only\n wrapperBorder: false,\n\n // No rounded corners for clean look (except checkboxes)\n borderRadius: 0,\n wrapperBorderRadius: 0,\n checkboxBorderRadius: 2, // Keep checkboxes square\n})\n\n// Selection configuration\nexport interface GridViewSelection<T> {\n mode: 'single' | 'multiple'\n selected: T[]\n onSelectionChange: (items: T[]) => void\n /**\n * Keep the grid's ticked rows equal to `selected` after mount (default: `selected` only seeds the\n * grid). For a host that changes the selection itself, e.g. ticking a parent ticks its sub-tasks.\n */\n controlled?: boolean\n}\n\n// Empty state configuration\nexport interface GridViewEmptyState {\n icon?: React.ReactNode\n title?: string\n message?: string\n action?: React.ReactNode\n}\n\n/** Grid events GridView binds itself; `gridOptions` may not override them. */\ntype GridViewOwnedEvent =\n | 'onGridReady'\n | 'onFirstDataRendered'\n | 'onModelUpdated'\n | 'onSelectionChanged'\n | 'onCellValueChanged'\n | 'onRowClicked'\n | 'onStateUpdated'\n | 'onColumnVisible'\n\n// Generic props to make this grid reusable with different data types\nexport interface GridViewProps<T> {\n // Core data\n items: T[]\n // Column configuration\n columnDefs: ColDef<T>[]\n // Row identity function\n getItemId: (item: T) => string\n // Selection handling\n selection?: GridViewSelection<T>\n // Empty state\n emptyState?: GridViewEmptyState\n // Loading state\n isLoading?: boolean\n loadingMessage?: string\n // Grid options. The events GridView handles itself are left out: they are spread over its own\n // handlers, so passing one here silently replaced them (an `onGridReady` here left the grid api\n // unset and selection never reached `selection.onSelectionChange`). Use the dedicated props.\n gridOptions?: Partial<Omit<GridOptions<T>, GridViewOwnedEvent>>\n // Grid initialization\n onGridReady?: (params: { api: GridApi<T> }) => void\n // Cell value changes\n onCellValueChanged?: (event: CellValueChangedEvent<T>) => void\n // Row click handler\n onRowClick?: (item: T) => void\n // State persistence.\n // The core is decoupled from any store: when `persistState` is on it seeds\n // AG Grid from `gridState` (read once at mount) and reports every subsequent\n // state change through `onGridStateChange`. An app-adapter wires these to its\n // own store. `gridId` is retained as an opaque identity hint for adapters\n // (the core itself does not read it).\n persistState?: boolean\n gridId?: string\n // Persisted column/sort/filter state to restore at mount (controlled input).\n gridState?: GridState\n // Called whenever AG Grid's state changes so an adapter can persist it.\n onGridStateChange?: (state: GridState) => void\n // Initial visible columns (applied when no saved state exists)\n initialVisibleColumns?: string[]\n // Basic styling\n className?: string\n height?: string | number\n // Per-instance row height. Applied via the theme (not just gridOptions) so the\n // theme's calculated line-height tracks it and cell content stays vertically\n // centered. Fixed height — keeps row virtualization on the fast path.\n rowHeight?: number\n testId?: string\n // Pinned rows\n pinnedBottomRowData?: T[]\n pinnedTopRowData?: T[]\n // Pagination (disabled by default)\n pagination?: boolean\n paginationPageSizeSelector?: number[]\n // Test-id hooks: the grid owns its row/cell DOM, so ids are stamped onto it after render.\n // `getRowTestId` names a row; `getCellTestId` a cell (by colId). Without them AG Grid's own\n // `row-id` / `col-id` attributes are still there to select by.\n getRowTestId?: (item: T) => string\n getCellTestId?: (colId: string, item: T) => string\n // Force empty-state UI even when items.length > 0.\n // Used when rows are present but all hidden by an external filter.\n showEmpty?: boolean\n}\n\n/** Minimum row height on coarse pointers: 44px touch target + the 1px row border + breathing room. */\nconst TOUCH_ROW_HEIGHT = 52\n\nexport function GridView<T>({\n items,\n columnDefs,\n getItemId,\n selection,\n emptyState,\n isLoading = false,\n loadingMessage = 'Loading...',\n gridOptions,\n onGridReady,\n onCellValueChanged,\n onRowClick,\n persistState = false,\n gridState,\n onGridStateChange,\n initialVisibleColumns,\n className = 'w-full',\n height = 500,\n rowHeight: rowHeightProp,\n testId = 'grid-view',\n pinnedBottomRowData,\n pinnedTopRowData,\n pagination = false,\n paginationPageSizeSelector,\n getRowTestId,\n getCellTestId,\n showEmpty,\n}: GridViewProps<T>) {\n // Reference to the grid API\n const gridApiRef = useRef<GridApi<T> | null>(null)\n\n // On touch screens rows grow to a 44px-hit-area-friendly height: AG Grid clips cell content\n // (pills, filename buttons, avatar chips) to the row, so a taller hit area needs a taller row.\n const isCoarsePointer = useMediaQuery(BREAKPOINT_QUERIES.coarsePointer)\n const rowHeight = isCoarsePointer\n ? Math.max(rowHeightProp ?? gridOptions?.rowHeight ?? 40, TOUCH_ROW_HEIGHT)\n : rowHeightProp\n\n // Initial row height, captured once so it can live in the (stable) default\n // grid options. rowHeight is NOT passed as a reactive prop — that would make\n // the AgGridReact wrapper re-process grid options on every change and snap\n // manually-resized columns back to their colDef widths. Subsequent changes are\n // applied imperatively (see effect below).\n const initialRowHeightRef = useRef(rowHeight)\n\n // Persisted state to seed the grid with — captured once at mount so a reactive\n // `gridState` prop (e.g. an adapter re-reading its store each render) can never\n // re-initialise the grid mid-session. Mirrors AG Grid's own initialState,\n // which is read once.\n const initialGridStateRef = useRef(gridState)\n\n // Get initial state for the grid (read once at mount)\n const initialState = useMemo(() => {\n if (!persistState) return undefined\n return initialGridStateRef.current\n }, [persistState])\n\n // Save grid state via the adapter using modern AG Grid state API\n const saveGridState = useCallback(() => {\n if (!persistState || !onGridStateChange || !gridApiRef.current) return\n\n // Use AG Grid's comprehensive state API to capture complete state\n // This includes: filters, sort, column order, column visibility, column widths, etc.\n const state = gridApiRef.current.getState()\n\n onGridStateChange(state)\n }, [persistState, onGridStateChange])\n\n // Ensure selection column is visible after grid initialization\n const ensureSelectionColumnVisible = useCallback(() => {\n if (!gridApiRef.current || !selection?.mode) return\n\n // IMPORTANT: Force selection column to be visible after state restoration\n // This ensures even old saved states don't hide the selection column\n // Also handles case where selection column is completely missing from state\n const currentState = gridApiRef.current.getColumnState()\n const selectionColumn = currentState.find((col) => col.colId === 'ag-Grid-SelectionColumn')\n const selectionColumnIndex = currentState.findIndex(\n (col) => col.colId === 'ag-Grid-SelectionColumn'\n )\n\n // Fix if column is hidden OR missing entirely OR not in first position\n if (!selectionColumn || selectionColumn.hide || selectionColumnIndex !== 0) {\n // Use setColumnsVisible for more reliable visibility control\n gridApiRef.current.setColumnsVisible(['ag-Grid-SelectionColumn'], true)\n\n // Also apply state to ensure proper width\n gridApiRef.current.applyColumnState({\n state: [\n {\n colId: 'ag-Grid-SelectionColumn',\n hide: false,\n width: selectionColumn?.width || 48,\n pinned: null, // Ensure not pinned in wrong position\n },\n ],\n defaultState: { hide: undefined },\n applyOrder: false,\n })\n\n // Move selection column to first position if it's not there\n if (selectionColumnIndex !== 0) {\n gridApiRef.current.moveColumns(['ag-Grid-SelectionColumn'], 0)\n }\n }\n }, [selection?.mode])\n\n // Stamp host test ids onto AG Grid's rows and cells (it virtualises, so keep watching).\n const containerRef = useRef<HTMLDivElement>(null)\n const itemsRef = useRef(items)\n itemsRef.current = items\n const getItemIdRef = useRef(getItemId)\n getItemIdRef.current = getItemId\n const hasTestIdHooks = Boolean(getRowTestId || getCellTestId)\n useEffect(() => {\n const root = containerRef.current\n if (!root || !hasTestIdHooks) return\n return watchGridTestIds(\n root,\n () => new Map(itemsRef.current.map((item) => [getItemIdRef.current(item), item])),\n { getRowTestId, getCellTestId }\n )\n }, [hasTestIdHooks, getRowTestId, getCellTestId, isLoading])\n\n // Handle row ID\n const getRowId = useCallback(\n (params: { data: T }) => {\n return getItemId(params.data)\n },\n [getItemId]\n )\n\n // Refs for selection — avoids handleGridReady/handleSelectionChanged recreating\n // when selection object changes (which happens on every selectionConfig recompute)\n const selectionRef = useRef(selection)\n selectionRef.current = selection\n\n const onSelectionChangeRef = useRef(selection?.onSelectionChange)\n onSelectionChangeRef.current = selection?.onSelectionChange\n\n const syncingSelectionRef = useRef(false)\n\n const handleSelectionChanged = useCallback(() => {\n if (syncingSelectionRef.current) return\n if (!onSelectionChangeRef.current || !gridApiRef.current) return\n\n const selectedNodes = gridApiRef.current.getSelectedNodes()\n const selectedData = selectedNodes\n .map((node) => node.data)\n .filter((item): item is T => item !== undefined)\n\n onSelectionChangeRef.current(selectedData)\n }, [])\n\n // Handle row clicks\n // A controlled selection: tick and untick rows so the grid shows exactly `selection.selected`.\n const controlledSelected = selection?.controlled ? selection.selected : undefined\n useEffect(() => {\n const api = gridApiRef.current\n if (!api || api.isDestroyed() || !controlledSelected) return\n const wanted = new Set(controlledSelected.map(getItemId))\n const tick: IRowNode<T>[] = []\n const untick: IRowNode<T>[] = []\n api.forEachNode((node) => {\n if (!node.data) return\n const want = wanted.has(getItemId(node.data))\n if (want && !node.isSelected()) tick.push(node)\n else if (!want && node.isSelected()) untick.push(node)\n })\n syncingSelectionRef.current = true\n try {\n if (tick.length) api.setNodesSelected({ nodes: tick, newValue: true, source: 'api' })\n if (untick.length) api.setNodesSelected({ nodes: untick, newValue: false, source: 'api' })\n } finally {\n syncingSelectionRef.current = false\n }\n }, [controlledSelected, getItemId])\n\n const handleRowClicked = useCallback(\n (event: RowClickedEvent<T>) => {\n if (!onRowClick || !event.data) return\n // Don't fire when the click originated from an interactive element inside the cell\n // (e.g. action buttons). AG Grid fires rowClicked regardless of DOM stopPropagation.\n const target = event.event?.target as HTMLElement | null\n if (target?.closest('button, input, a, select, textarea, [role=\"button\"]')) return\n onRowClick(event.data)\n },\n [onRowClick]\n )\n\n // The visibility the user actually intends, per colId. AG Grid re-processes the\n // column definitions on some React re-renders even when the `columnDefs`\n // reference is unchanged, and that reset restores each colDef's `hide` — so a\n // column the user turned on (e.g. via a fields menu) silently disappears the\n // next time the host re-renders (opening an editor, selecting a row, …). We\n // remember the intended visibility and restore it whenever such a reset fires.\n // A Map (not a Set of hidden ids) so a genuinely NEW column from a real\n // columnDefs change — one we've never seen — is left at its colDef default\n // rather than force-shown.\n const desiredHiddenRef = useRef<Map<string, boolean> | null>(null)\n\n const snapshotDesiredHidden = useCallback(() => {\n const api = gridApiRef.current\n if (!api) return\n desiredHiddenRef.current = new Map(api.getColumnState().map((c) => [c.colId, !!c.hide]))\n }, [])\n\n // Handle grid ready\n const handleGridReady = useCallback(\n (event: GridReadyEvent<T>) => {\n gridApiRef.current = event.api\n\n // Apply initial visible columns when no saved state exists\n // This prevents showing all columns by default for new users\n if (!initialState && initialVisibleColumns?.length) {\n const currentState = event.api.getColumnState()\n const stateMap = new Map(currentState.map((col) => [col.colId, col]))\n const visibleSet = new Set(initialVisibleColumns)\n\n // Build ordered state: selection column first (always visible), then visible columns, then hidden\n const selectionCol = stateMap.get('ag-Grid-SelectionColumn')\n const orderedState = [\n // Selection column first - ALWAYS visible, create if not in state\n selectionCol\n ? { ...selectionCol, hide: false }\n : { colId: 'ag-Grid-SelectionColumn', hide: false, width: 48 },\n // Visible columns in specified order\n ...initialVisibleColumns\n .filter((colId) => colId !== 'ag-Grid-SelectionColumn') // Don't duplicate selection column\n .map((colId) => {\n const col = stateMap.get(colId)\n return col ? { ...col, hide: false } : undefined\n }),\n // Hidden columns (everything else)\n ...currentState\n .filter((col) => col.colId !== 'ag-Grid-SelectionColumn' && !visibleSet.has(col.colId))\n .map((col) => ({ ...col, hide: true })),\n ].filter((col): col is NonNullable<typeof col> => col !== undefined)\n\n event.api.applyColumnState({ state: orderedState, applyOrder: true })\n }\n\n // When restoring persisted state, detect columns from columnDefs that\n // aren't in the saved state (e.g. newly added columns) and append them.\n // AG Grid's maintainColumnOrder adds them but may place them inconsistently;\n // this ensures they appear as hidden at the end so the navigator picks them up.\n if (initialState && initialVisibleColumns?.length) {\n const currentState = event.api.getColumnState()\n const savedColIds = new Set(\n initialState.columnOrder?.orderedColIds ?? currentState.map((c) => c.colId)\n )\n const newCols = currentState.filter(\n (col) => !savedColIds.has(col.colId) && col.colId !== 'ag-Grid-SelectionColumn'\n )\n if (newCols.length > 0) {\n // Make new columns visible if they're in the default visible set\n const defaultVisible = new Set(initialVisibleColumns)\n event.api.applyColumnState({\n state: newCols.map((col) => ({\n ...col,\n hide: !defaultVisible.has(col.colId),\n })),\n })\n }\n }\n\n // Ensure selection column is visible (in case saved state hides it)\n ensureSelectionColumnVisible()\n\n // Seed the intended column visibility from the state AG Grid settled on\n // (colDefs + restored persisted state) so a later colDef-driven reset can\n // be undone rather than silently hiding a user-shown column.\n snapshotDesiredHidden()\n\n // Call the provided onGridReady callback\n if (onGridReady) {\n onGridReady({ api: event.api })\n }\n\n // Restore selection state if needed (read from ref to keep callback stable)\n const sel = selectionRef.current\n if (sel && sel.selected.length > 0) {\n const selectedIds = new Set(sel.selected.map(getItemId))\n event.api.forEachNode((node) => {\n if (node.data && selectedIds.has(getItemId(node.data))) {\n node.setSelected(true)\n }\n })\n }\n },\n [\n onGridReady,\n getItemId,\n ensureSelectionColumnVisible,\n snapshotDesiredHidden,\n initialState,\n initialVisibleColumns,\n ]\n )\n\n // Undo AG Grid's colDef-driven visibility reset; record genuine user changes.\n const handleColumnVisible = useCallback(\n (event: { source?: string }) => {\n const api = gridApiRef.current\n if (!api) return\n // A columnDefs re-apply reports `gridOptionsChanged`; a real toggle reports\n // an interaction source (api, toolPanelUi, columnMenu, …).\n if (event.source === 'gridOptionsChanged') {\n const desired = desiredHiddenRef.current\n if (!desired) return\n // Only correct columns we've seen before (in the snapshot); a brand-new\n // column keeps its colDef default.\n const fixes = api\n .getColumnState()\n .filter(\n (c) =>\n c.colId !== 'ag-Grid-SelectionColumn' &&\n desired.has(c.colId) &&\n desired.get(c.colId) !== !!c.hide\n )\n .map((c) => ({ colId: c.colId, hide: desired.get(c.colId) }))\n if (fixes.length) api.applyColumnState({ state: fixes })\n } else {\n // Genuine change — this is now the intended visibility.\n snapshotDesiredHidden()\n }\n },\n [snapshotDesiredHidden]\n )\n\n // Handle first data rendered - no auto-sizing, let flex columns handle it\n const handleFirstDataRendered = useCallback(() => {\n // Columns with flex will auto-fill, columns with width stay fixed\n // Also ensure selection column is visible as a backup (handles timing issues with initialState)\n ensureSelectionColumnVisible()\n // Seed the intended visibility from the state AG Grid settled on (colDefs +\n // any restored persisted state), so later resets can be undone.\n snapshotDesiredHidden()\n }, [ensureSelectionColumnVisible, snapshotDesiredHidden])\n\n // Track when AG Grid processes row updates\n const handleModelUpdated = useCallback(() => {\n // AG Grid handles model updates internally\n }, [])\n\n // Track row data changes for AG Grid updates\n const prevItemsRef = useRef<T[]>([])\n useEffect(() => {\n prevItemsRef.current = items\n }, [items])\n\n // Handle state updates - save state when it changes (modern AG Grid approach)\n const handleStateUpdated = useCallback(() => {\n saveGridState()\n }, [saveGridState])\n\n // Removed problematic useEffect that caused unnecessary re-renders\n // AG Grid handles selection internally, only sync on initial load\n\n // Default grid options with minimal settings - memoized to prevent re-renders\n const defaultOptions: GridOptions<T> = useMemo(() => {\n return {\n // Initial row height (stable — captured once). Live changes go through the\n // imperative effect, not this option, to avoid column-state churn.\n rowHeight: initialRowHeightRef.current,\n suppressMenuHide: true, // Prevent hiding columns since we don't have enterprise sidebar\n maintainColumnOrder: true, // Preserve column order when columns are added/removed\n\n // Row virtualization: Render rows as needed for performance\n // rowBuffer renders extra rows above/below viewport for smoother scrolling\n rowBuffer: 10, // Render 10 extra rows above/below viewport for smooth scrolling\n suppressColumnVirtualisation: false, // Keep column virtualization enabled\n suppressRowVirtualisation: false, // ENABLE row virtualization - only render visible rows\n animateRows: false, // Disable row animations\n suppressColumnMoveAnimation: true, // Disable column move/resize animations\n\n // Efficient updates with React Query cache\n // getRowId enables change detection - grid only re-renders changed rows\n // Works perfectly with React Query's cache updates\n getRowId: getRowId,\n\n rowSelection: selection?.mode\n ? {\n mode: selection.mode === 'single' ? 'singleRow' : 'multiRow',\n selectAll: 'filtered', // Header checkbox selects all filtered rows (across all pages)\n // In multi-row mode, only checkboxes should toggle selection (clicking row body would replace selection)\n enableClickSelection: selection.mode === 'single',\n checkboxes: true,\n headerCheckbox: selection.mode === 'multiple',\n }\n : undefined,\n selectionColumnDef: selection?.mode\n ? {\n sortable: selection.mode === 'multiple',\n width: 48,\n lockVisible: true, // Cannot be hidden\n suppressMovable: true, // Cannot be moved\n }\n : undefined,\n // Use normal layout for better performance - autoHeight causes forced reflows\n domLayout: 'normal',\n // Stop editing when clicking outside the cell/grid\n stopEditingWhenCellsLoseFocus: true,\n // Additional performance optimizations\n suppressCellFocus: true, // Don't focus cells on click (faster)\n suppressRowHoverHighlight: true, // Disable hover highlight - reduces DOM manipulation during scroll\n suppressScrollOnNewData: true, // Don't scroll when data updates\n suppressAggFuncInHeader: true, // Don't show aggregation functions (faster)\n // Tooltip UX: the AG Grid default show-delay is 2 s which hurts\n // discoverability for cells where tooltips carry the \"reveal full\n // content\" affordance for non-editable users. 300 ms is snappy\n // without being accidental. tooltipInteraction lets users hover\n // onto the tooltip itself (e.g. to select / copy long text).\n // These are AG Grid's native tooltip controls — no React / Radix\n // provider involved, zero per-row cost; tooltip DOM is created on\n // demand when hover-delay elapses and destroyed on mouseout.\n tooltipShowDelay: 300,\n tooltipHideDelay: 4000,\n tooltipInteraction: true,\n // Pagination (disabled by default)\n pagination,\n paginationPageSizeSelector,\n defaultColDef: {\n sortable: true,\n resizable: true,\n unSortIcon: false, // Sort glyph only on the sorted column (every header carrying one was noise)\n sortingOrder: ['asc', 'desc', null], // 3-way sorting: asc → desc → unsorted (AG Grid default)\n wrapText: false, // Disable text wrapping for cleaner display\n autoHeight: false, // Use fixed row height from theme\n wrapHeaderText: false, // Disable header text wrapping for cleaner look\n autoHeaderHeight: false, // Fixed header height\n filterParams: {\n maxNumConditions: 1,\n buttons: ['reset'],\n },\n },\n }\n }, [selection?.mode, getRowId, pagination, paginationPageSizeSelector])\n\n // Combine default options with passed options - memoized to prevent re-renders\n const combinedOptions = useMemo(\n () => ({ ...defaultOptions, ...gridOptions }),\n [defaultOptions, gridOptions]\n )\n\n // Apply fixed height for better performance — autoHeight causes forced\n // reflows by measuring all rows, avoid it.\n //\n // Also derive the geometry the wrapping multiline columns (.cell-multiline —\n // Abstract, Author, custom text fields) need so their overflow clip lands on\n // a WHOLE-line boundary (never mid-line, which reads as a half-line\n // \"bleeding\" into the neighbouring row). All computed once here per\n // row-height change and handed to CSS as variables — zero per-row cost on the\n // million-row grid:\n // --multiline-line-clamp : whole number of lines that fit — drives the CSS\n // line-clamp that hides overflow lines entirely.\n // --multiline-pad-y : vertical cell padding, shrunk on very short rows so\n // at least one whole line always fits.\n // --multiline-max-h : cap of exactly N whole lines + TOP padding + borders.\n // Deliberately excludes the bottom padding: AG Grid\n // forces cell height, so overflow clips at the\n // padding-box edge and any bottom padding becomes a\n // window onto the next (clamped-away) line. Ending the\n // box right at line N's bottom puts the clip on the\n // line boundary; the row supplies the visual gap below.\n const containerStyle = useMemo(() => {\n const style: React.CSSProperties = { height }\n if (rowHeight) {\n const LINE_PX = 14 * 1.35 // grid font-size × multiline line-height (18.9px)\n const BORDER = 2 // multiline cell top + bottom border (1px each, border-box)\n const PREFERRED_PAD = 16 // preferred total vertical padding (8px top + 8px bottom)\n // Don't let padding eat the room a single line needs on short rows.\n const padTotal = Math.max(0, Math.min(PREFERRED_PAD, rowHeight - BORDER - LINE_PX))\n const lines = Math.max(1, Math.floor((rowHeight - BORDER - padTotal) / LINE_PX))\n const vars = style as Record<string, string | number>\n vars['--multiline-line-clamp'] = `${lines}`\n vars['--multiline-pad-y'] = `${padTotal / 2}px`\n vars['--multiline-max-h'] = `${lines * LINE_PX + padTotal / 2 + BORDER}px`\n }\n return style\n }, [height, rowHeight])\n\n // Apply rowHeight changes imperatively via the grid option (not the theme —\n // recreating the theme would re-initialise the grid). This only re-lays-out\n // rows; columns, row virtualization, and the transaction bus are untouched.\n useEffect(() => {\n const api = gridApiRef.current\n if (!api || api.isDestroyed() || !rowHeight) return\n api.setGridOption('rowHeight', rowHeight)\n api.resetRowHeights()\n }, [rowHeight])\n\n // Add professional shadow for 3D effect\n // Add overflow-visible only for autoHeight layout\n const containerClassName = useMemo(() => {\n const baseClass = className || ''\n const shadowClass = 'shadow-sm' // Tailwind shadow for professional look\n // Height-adjustable grids vertically center cell content (CSS) so a\n // one-line cell or a lone pill sits in the middle of the row, and multi-line\n // text (e.g. Abstract) is balanced top-to-bottom — flagged here, applied in\n // grid.css.\n const alignClass = rowHeight ? 'ag-cells-centered' : ''\n return [baseClass, shadowClass, alignClass].filter(Boolean).join(' ')\n }, [className, rowHeight])\n\n // Show loading state — grey skeleton rows so the page transition feels instant\n if (isLoading) {\n const skeletonRowCount = 12\n return (\n <div\n data-testid={`${testId}-loading`}\n className={containerClassName}\n style={containerStyle}\n aria-busy=\"true\"\n aria-label={loadingMessage}\n >\n {/* Header skeleton */}\n <div className=\"flex h-11 items-center gap-4 border-b border-border/40 px-4\">\n <div className=\"h-3 w-6 rounded bg-muted animate-pulse\" />\n <div className=\"h-3 w-32 rounded bg-muted animate-pulse\" />\n <div className=\"h-3 w-24 rounded bg-muted animate-pulse\" />\n <div className=\"h-3 w-20 rounded bg-muted animate-pulse\" />\n <div className=\"h-3 w-28 rounded bg-muted animate-pulse\" />\n <div className=\"h-3 w-16 rounded bg-muted animate-pulse ml-auto\" />\n </div>\n {/* Row skeletons */}\n {Array.from({ length: skeletonRowCount }).map((_, rowIdx) => (\n <div key={rowIdx} className=\"flex h-10 items-center gap-4 border-b border-border/20 px-4\">\n <div className=\"h-4 w-4 rounded-sm bg-muted/70 animate-pulse\" />\n <div\n className=\"h-3 rounded bg-muted/70 animate-pulse\"\n style={{ width: `${20 + ((rowIdx * 17) % 40)}%` }}\n />\n <div\n className=\"h-3 rounded bg-muted/60 animate-pulse\"\n style={{ width: `${8 + ((rowIdx * 11) % 12)}%` }}\n />\n <div\n className=\"h-3 rounded bg-muted/60 animate-pulse\"\n style={{ width: `${6 + ((rowIdx * 7) % 10)}%` }}\n />\n <div\n className=\"h-3 rounded bg-muted/50 animate-pulse ml-auto\"\n style={{ width: `${5 + ((rowIdx * 5) % 8)}%` }}\n />\n </div>\n ))}\n </div>\n )\n }\n\n // Show empty state\n // showEmpty lets callers force this UI (e.g. when an external filter hid\n // every row but the underlying items array is non-empty).\n if ((showEmpty ?? items.length === 0) && emptyState) {\n return (\n <div\n data-testid={`${testId}-empty`}\n // Capped by its container (a landscape phone is shorter than 24rem) and scrollable, with the\n // content centred by auto margins so a tall one still starts at the top.\n className=\"flex h-96 max-h-full flex-col items-center overflow-y-auto p-6 text-center\"\n >\n <div className=\"my-auto flex flex-col items-center\">\n {emptyState.icon && (\n <div className=\"text-muted-foreground/60 mb-4\">{emptyState.icon}</div>\n )}\n {emptyState.title && <h3 className=\"text-xl font-medium mb-2\">{emptyState.title}</h3>}\n {emptyState.message && (\n <p className=\"text-muted-foreground mb-6 max-w-md\">{emptyState.message}</p>\n )}\n {emptyState.action}\n </div>\n </div>\n )\n }\n\n return (\n <div\n ref={containerRef}\n className={containerClassName}\n style={containerStyle}\n data-testid={testId}\n >\n <AgGridReact\n theme={arbiTheme}\n modules={[AllCommunityModule]}\n columnDefs={columnDefs}\n rowData={items}\n pinnedBottomRowData={pinnedBottomRowData}\n pinnedTopRowData={pinnedTopRowData}\n getRowId={getRowId}\n initialState={initialState}\n onGridReady={handleGridReady}\n onFirstDataRendered={handleFirstDataRendered}\n onModelUpdated={handleModelUpdated}\n onSelectionChanged={handleSelectionChanged}\n onCellValueChanged={onCellValueChanged}\n onRowClicked={handleRowClicked}\n onStateUpdated={handleStateUpdated}\n onColumnVisible={handleColumnVisible}\n {...combinedOptions}\n />\n </div>\n )\n}\n"]}
@@ -3,8 +3,8 @@ import { useShellBottomInset, ToolButton, Shell } from './chunk-KZJAG7T3.js';
3
3
  import { useMentionAutocomplete, useElementSize, rootFontSizePx } from './chunk-HOXKR26S.js';
4
4
  import { MentionMenu, toMessageMentions, extractMentions } from './chunk-AA3PT6KR.js';
5
5
  import { observeResize } from './chunk-NPLYHR2E.js';
6
- import { formatCount } from './chunk-OXF2IFXG.js';
7
6
  import { testIdOr } from './chunk-7F6CBP3Z.js';
7
+ import { formatCount } from './chunk-OXF2IFXG.js';
8
8
  import { forwardRef, useState, useRef, useCallback, useEffect, useMemo, useSyncExternalStore, useLayoutEffect } from 'react';
9
9
  import { FileText, ChevronDown, Pencil, Workflow, ChevronRight, Sparkles, ChevronUp, MoreHorizontal, RotateCw, AlertCircle, ArrowLeft, Edit3, Send, Loader2, Square, Mic, ArrowDown, MessagesSquare, Bot, GanttChartSquare, ArrowUpRight, Upload, MessageSquarePlus, Trash2, PanelRight, MessageSquare, X, FileUp } from 'lucide-react';
10
10
  import { cn, Textarea, Button, PopoverTrigger, Popover, PopoverAnchor, PopoverContent, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuItem, Tooltip, TooltipTrigger, TooltipContent, MarkdownRenderer, closeOpenEmphasis, trimOpenCitation } from '@arbidocs/react/ui';
@@ -125,6 +125,7 @@ function MessageRoot({
125
125
  "data-slot": "message",
126
126
  "data-testid": testId,
127
127
  "data-message-role": role,
128
+ "data-tone": tone,
128
129
  className: cn(
129
130
  "group mx-auto mb-1 flex w-full max-w-[46rem] justify-center",
130
131
  tone === "error" ? "rounded-lg bg-red-bg" : tone === "subagent" ? "rounded-lg bg-muted/40" : "bg-background",
@@ -135,9 +136,16 @@ function MessageRoot({
135
136
  }
136
137
  );
137
138
  }
139
+ function MessageAuthor({
140
+ children,
141
+ className
142
+ }) {
143
+ return /* @__PURE__ */ jsx("div", { "data-slot": "message-author", className: cn("mb-1 mt-2 flex items-center", className), children });
144
+ }
138
145
  function MessageContent({
139
146
  role,
140
147
  messageId,
148
+ align = "end",
141
149
  testId = `message-content-${messageId}`,
142
150
  className,
143
151
  children,
@@ -150,7 +158,8 @@ function MessageContent({
150
158
  "data-testid": testId,
151
159
  className: cn(
152
160
  "w-full max-w-full overflow-hidden text-chat",
153
- role === "user" && "flex flex-col items-end",
161
+ role === "user" && "flex flex-col",
162
+ role === "user" && (align === "start" ? "items-start" : "items-end"),
154
163
  className
155
164
  ),
156
165
  ...rest,
@@ -376,7 +385,8 @@ function MessageFooter({
376
385
  steps,
377
386
  className,
378
387
  labels,
379
- stepsToggleTestId
388
+ stepsToggleTestId,
389
+ align = "end"
380
390
  }) {
381
391
  const l = { ...DEFAULT_MESSAGE_LABELS, ...labels };
382
392
  const isUser = role === "user";
@@ -396,7 +406,7 @@ function MessageFooter({
396
406
  ref: measureHeader,
397
407
  className: cn(
398
408
  "flex items-center text-sm",
399
- isUser ? "justify-end gap-2 text-xs" : "justify-between"
409
+ isUser ? cn("gap-2 text-xs", align === "start" ? "justify-start" : "justify-end") : "justify-between"
400
410
  ),
401
411
  children: [
402
412
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
@@ -2444,6 +2454,6 @@ function AssistantDock({
2444
2454
  );
2445
2455
  }
2446
2456
 
2447
- export { AssistantDock, AssistantDockToggle, AssistantModeToggle, ChatDocuments, ChatMessageContent, ChatPage, ChatThreadList, ChatThreadSelect, CitationMark, Composer, ComposerBranchingNotice, ComposerContainer, ComposerDock, ComposerFrame, ComposerInput, ComposerRoot, ComposerSend, ComposerToolbar, ComposerVoiceButton, ContextChip, ConversationEmpty, DEFAULT_ASSISTANT_MODE_TOGGLE_LABELS, DEFAULT_CHAT_PAGE_LABELS, DEFAULT_COMPOSER_LABELS, DEFAULT_CONVERSATION_EMPTY_LABELS, DEFAULT_MESSAGE_ALERT_LABELS, DEFAULT_MESSAGE_LABELS, DEFAULT_MESSAGE_LIST_LABELS, DEFAULT_WORKFLOW_STEP_CHIP_LABELS, Message, MessageActionButton, MessageActionMenu, MessageAlert, MessageBody, MessageBubble, MessageByline, MessageContent, MessageFooter, MessageList, MessageListBanner, MessageListEmpty, MessageListRoot, MessageListRow, MessageListRows, MessageListStatus, MessageListTail, MessageNarration, MessageRoot, MessageSources, MessageSubagentBadge, ScrollToBottom, SourceChip, WorkflowStepChip, toSpeakableText, useAssistantDock, useChatSurface, useRegisterEmbeddedChat, useStreamAnnouncement };
2448
- //# sourceMappingURL=chunk-EWSEQEPL.js.map
2449
- //# sourceMappingURL=chunk-EWSEQEPL.js.map
2457
+ export { AssistantDock, AssistantDockToggle, AssistantModeToggle, ChatDocuments, ChatMessageContent, ChatPage, ChatThreadList, ChatThreadSelect, CitationMark, Composer, ComposerBranchingNotice, ComposerContainer, ComposerDock, ComposerFrame, ComposerInput, ComposerRoot, ComposerSend, ComposerToolbar, ComposerVoiceButton, ContextChip, ConversationEmpty, DEFAULT_ASSISTANT_MODE_TOGGLE_LABELS, DEFAULT_CHAT_PAGE_LABELS, DEFAULT_COMPOSER_LABELS, DEFAULT_CONVERSATION_EMPTY_LABELS, DEFAULT_MESSAGE_ALERT_LABELS, DEFAULT_MESSAGE_LABELS, DEFAULT_MESSAGE_LIST_LABELS, DEFAULT_WORKFLOW_STEP_CHIP_LABELS, Message, MessageActionButton, MessageActionMenu, MessageAlert, MessageAuthor, MessageBody, MessageBubble, MessageByline, MessageContent, MessageFooter, MessageList, MessageListBanner, MessageListEmpty, MessageListRoot, MessageListRow, MessageListRows, MessageListStatus, MessageListTail, MessageNarration, MessageRoot, MessageSources, MessageSubagentBadge, ScrollToBottom, SourceChip, WorkflowStepChip, toSpeakableText, useAssistantDock, useChatSurface, useRegisterEmbeddedChat, useStreamAnnouncement };
2458
+ //# sourceMappingURL=chunk-NRUPUDWB.js.map
2459
+ //# sourceMappingURL=chunk-NRUPUDWB.js.map