@ai-matrx/associations 0.5.2 → 0.6.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.
@@ -38,6 +38,8 @@ __export(react_exports, {
38
38
  CommentComposer: () => CommentComposer,
39
39
  CommentThread: () => CommentThread,
40
40
  DefaultEntityIcon: () => DefaultEntityIcon,
41
+ DefaultWindowShell: () => DefaultWindowShell,
42
+ DoorControlsSlot: () => DoorControlsSlot,
41
43
  DoorRef: () => DoorRef,
42
44
  PrimaryEntityProvider: () => PrimaryEntityProvider,
43
45
  UniversalAssociationPicker: () => UniversalAssociationPicker,
@@ -46,8 +48,11 @@ __export(react_exports, {
46
48
  buildCommentTree: () => buildCommentTree,
47
49
  formatRelativeTime: () => formatRelativeTime,
48
50
  getContentRoleMeta: () => getContentRoleMeta,
51
+ lazyPickerOverride: () => lazyPickerOverride,
52
+ lazyWindowShell: () => lazyWindowShell,
49
53
  useAssociationCandidates: () => useAssociationCandidates,
50
54
  useAssociationEntitySelectAdapter: () => useAssociationEntitySelectAdapter,
55
+ useAssociationPickerBridge: () => useAssociationPickerBridge,
51
56
  useAssociations: () => useAssociations,
52
57
  useAssociationsStore: () => useAssociationsStore,
53
58
  useAssociationsUiPorts: () => useAssociationsUiPorts,
@@ -65,12 +70,185 @@ module.exports = __toCommonJS(react_exports);
65
70
 
66
71
  // src/react/context.tsx
67
72
  var import_react = require("react");
73
+
74
+ // src/demanded-rpcs.generated.ts
75
+ var DEMANDED_RPC_NAMES = [
76
+ "assoc_add",
77
+ "assoc_remove",
78
+ "assoc_set_targets",
79
+ "assoc_remove_for_entity",
80
+ "assoc_for_entity",
81
+ "assoc_for_targets",
82
+ "assoc_for_sources",
83
+ "assoc_members_visible",
84
+ "conversation_file_add",
85
+ "conversation_file_remove",
86
+ "conversation_files",
87
+ "agent_resource_add",
88
+ "agent_resource_remove",
89
+ "cat_list",
90
+ "cat_create",
91
+ "cat_update",
92
+ "cat_reparent",
93
+ "cat_delete",
94
+ "ues_set",
95
+ "ues_list",
96
+ "ues_get_bulk",
97
+ "ues_touch",
98
+ "reference_search_candidates",
99
+ "cmt_list",
100
+ "cmt_add",
101
+ "cmt_edit",
102
+ "cmt_delete"
103
+ ];
104
+
105
+ // src/core/assertDemandedSchema.ts
106
+ var BAD_UUID = "__not_a_uuid__";
107
+ var SELF_TEST_MISSING_RPC = "assoc_probe_self_test_missing_fn";
108
+ var PROBE_ARGS = {
109
+ // edges — writes
110
+ assoc_add: {
111
+ p_source_type: "note",
112
+ p_source_id: BAD_UUID,
113
+ p_target_type: "task",
114
+ p_target_id: BAD_UUID
115
+ },
116
+ assoc_remove: {
117
+ p_source_type: "note",
118
+ p_source_id: BAD_UUID,
119
+ p_target_type: "task",
120
+ p_target_id: BAD_UUID
121
+ },
122
+ assoc_set_targets: {
123
+ p_source_type: "note",
124
+ p_source_id: BAD_UUID,
125
+ p_target_type: "task",
126
+ p_target_ids: [BAD_UUID]
127
+ },
128
+ assoc_remove_for_entity: { p_type: "note", p_id: BAD_UUID },
129
+ // edges — reads
130
+ assoc_for_entity: { p_type: "note", p_id: BAD_UUID },
131
+ assoc_for_targets: { p_target_type: "task", p_target_ids: [BAD_UUID] },
132
+ assoc_for_sources: { p_source_type: "note", p_source_ids: [BAD_UUID] },
133
+ assoc_members_visible: { p_target_type: "task", p_target_ids: [BAD_UUID] },
134
+ // conversation files
135
+ conversation_file_add: {
136
+ p_conversation_id: BAD_UUID,
137
+ p_file_id: BAD_UUID
138
+ },
139
+ conversation_file_remove: {
140
+ p_conversation_id: BAD_UUID,
141
+ p_file_id: BAD_UUID
142
+ },
143
+ conversation_files: { p_conversation_id: BAD_UUID },
144
+ // agent resources
145
+ agent_resource_add: {
146
+ p_agent_id: BAD_UUID,
147
+ p_source_type: "note",
148
+ p_source_id: BAD_UUID
149
+ },
150
+ agent_resource_remove: {
151
+ p_agent_id: BAD_UUID,
152
+ p_source_type: "note",
153
+ p_source_id: BAD_UUID
154
+ },
155
+ // categories
156
+ cat_list: {},
157
+ cat_create: { p_dimension: "", p_name: "", p_org_id: BAD_UUID },
158
+ cat_update: { p_category_id: BAD_UUID, p_name: "__probe__" },
159
+ cat_reparent: { p_category_id: BAD_UUID },
160
+ cat_delete: { p_category_id: BAD_UUID },
161
+ // favorites / recents
162
+ ues_set: { p_entity_type: "__probe__", p_entity_id: BAD_UUID },
163
+ ues_list: {},
164
+ ues_get_bulk: { p_entity_type: "__probe__", p_entity_ids: [BAD_UUID] },
165
+ ues_touch: { p_entity_type: "__probe__", p_entity_id: BAD_UUID },
166
+ // candidates
167
+ reference_search_candidates: { p_token: "__probe__", p_limit: 1 },
168
+ // comments (W6) — every fn takes a uuid arg, so the unparseable sentinel
169
+ // guarantees 22P02 before any SECURITY DEFINER body runs (no write occurs).
170
+ cmt_list: { p_entity_type: "__probe__", p_entity_id: BAD_UUID },
171
+ cmt_add: {
172
+ p_entity_type: "__probe__",
173
+ p_entity_id: BAD_UUID,
174
+ p_body: "__probe__"
175
+ },
176
+ cmt_edit: { p_id: BAD_UUID, p_body: "__probe__" },
177
+ cmt_delete: { p_id: BAD_UUID }
178
+ };
179
+ function isMissingFunctionError(error) {
180
+ if (!error || typeof error !== "object") return false;
181
+ return error.code === "PGRST202";
182
+ }
183
+ async function assertDemandedSchema(dataSource, options = {}) {
184
+ const { selfTest = false, concurrency = 6, throwOnViolation = true } = options;
185
+ const names = [...DEMANDED_RPC_NAMES];
186
+ if (selfTest) names.push(SELF_TEST_MISSING_RPC);
187
+ const missing = [];
188
+ const unreachable = [];
189
+ const answered = [];
190
+ let cursor = 0;
191
+ async function worker() {
192
+ while (cursor < names.length) {
193
+ const fn = names[cursor];
194
+ cursor += 1;
195
+ if (!fn) continue;
196
+ const args = fn === SELF_TEST_MISSING_RPC ? {} : PROBE_ARGS[fn];
197
+ try {
198
+ const { error } = await dataSource.rpc(fn, args);
199
+ if (error == null) answered.push(fn);
200
+ else if (isMissingFunctionError(error)) missing.push(fn);
201
+ else answered.push(fn);
202
+ } catch (e) {
203
+ unreachable.push({ fn, error: e });
204
+ }
205
+ }
206
+ }
207
+ await Promise.all(
208
+ Array.from({ length: Math.min(concurrency, names.length) }, worker)
209
+ );
210
+ if (selfTest) {
211
+ const selfTestMissing = missing.includes(SELF_TEST_MISSING_RPC);
212
+ const idx = missing.indexOf(SELF_TEST_MISSING_RPC);
213
+ if (idx >= 0) missing.splice(idx, 1);
214
+ const answeredIdx = answered.indexOf(SELF_TEST_MISSING_RPC);
215
+ if (answeredIdx >= 0) answered.splice(answeredIdx, 1);
216
+ if (!selfTestMissing) {
217
+ throw new Error(
218
+ `assertDemandedSchema self-test FAILED: the fabricated function "${SELF_TEST_MISSING_RPC}" did not come back as missing (PGRST202). This probe cannot currently fail, so its green result proves nothing \u2014 the dataSource is swallowing or faking errors.`
219
+ );
220
+ }
221
+ }
222
+ if (unreachable.length > 0) {
223
+ throw new Error(
224
+ `assertDemandedSchema could not complete: ${unreachable.length} probe call(s) failed to reach the database (${unreachable.map((u) => u.fn).join(", ")}). This is NOT a schema verdict \u2014 fix connectivity and re-run.`
225
+ );
226
+ }
227
+ const ok = missing.length === 0;
228
+ const report = { ok, missing, unreachable, answered };
229
+ if (!ok && throwOnViolation) {
230
+ throw new Error(
231
+ `DEMANDED SCHEMA VIOLATION: the database this dataSource points at cannot answer ${missing.length} demanded @ai-matrx/associations function(s): ${missing.join(", ")}. This package demands the AI Matrx platform schema (README \xA7 The demanded schema); a database without it is not this package's database.`
232
+ );
233
+ }
234
+ return report;
235
+ }
236
+
237
+ // src/react/context.tsx
68
238
  var import_jsx_runtime = require("react/jsx-runtime");
69
239
  var AssociationsReactContext = (0, import_react.createContext)(
70
240
  null
71
241
  );
242
+ function isDevelopmentBuild() {
243
+ try {
244
+ return typeof process !== "undefined" && true;
245
+ } catch {
246
+ return false;
247
+ }
248
+ }
72
249
  function AssociationsProvider({
73
250
  store,
251
+ probeSchema,
74
252
  notifier,
75
253
  windowShell,
76
254
  capture,
@@ -80,6 +258,28 @@ function AssociationsProvider({
80
258
  children
81
259
  }) {
82
260
  const notifierWarned = (0, import_react.useRef)(false);
261
+ const shouldProbe = probeSchema ?? isDevelopmentBuild();
262
+ (0, import_react.useEffect)(() => {
263
+ if (!shouldProbe) return;
264
+ let stale = false;
265
+ void (async () => {
266
+ try {
267
+ await assertDemandedSchema(store.dataSource);
268
+ } catch (error) {
269
+ if (stale) return;
270
+ store.errorSink({
271
+ code: "demanded_schema_violation",
272
+ message: error instanceof Error ? error.message : "assertDemandedSchema failed",
273
+ context: {
274
+ remedy: "Run the platform.associations schema (README \xA7 The demanded schema) on the database this dataSource points at."
275
+ }
276
+ });
277
+ }
278
+ })();
279
+ return () => {
280
+ stale = true;
281
+ };
282
+ }, [shouldProbe, store]);
83
283
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
84
284
  AssociationsReactContext.Provider,
85
285
  {
@@ -1471,7 +1671,11 @@ function usePrimaryEntity() {
1471
1671
  }
1472
1672
 
1473
1673
  // src/react/components/AssociationWindow.tsx
1674
+ var import_react12 = require("react");
1675
+
1676
+ // src/react/components/DefaultWindowShell.tsx
1474
1677
  var import_react11 = require("react");
1678
+ var import_react_dom = require("react-dom");
1475
1679
  var import_design_system = require("@ai-matrx/design-system");
1476
1680
 
1477
1681
  // src/react/icons.tsx
@@ -1590,9 +1794,246 @@ function DefaultEntityIcon(props) {
1590
1794
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M12 13.5V8" })
1591
1795
  ] });
1592
1796
  }
1797
+ function MaximizeIcon(props) {
1798
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { ...base(props), children: [
1799
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M15 3h6v6" }),
1800
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M9 21H3v-6" }),
1801
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M21 3l-7 7" }),
1802
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M3 21l7-7" })
1803
+ ] });
1804
+ }
1805
+ function RestoreIcon(props) {
1806
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { ...base(props), children: [
1807
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M4 14h6v6" }),
1808
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M20 10h-6V4" }),
1809
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M14 10l7-7" }),
1810
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M3 21l7-7" })
1811
+ ] });
1812
+ }
1813
+ function GripResizeIcon(props) {
1814
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { ...base(props), children: [
1815
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M21 15 15 21" }),
1816
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M21 20 20 21" })
1817
+ ] });
1818
+ }
1593
1819
 
1594
- // src/react/components/AssociationWindow.tsx
1820
+ // src/react/components/DefaultWindowShell.tsx
1595
1821
  var import_jsx_runtime4 = require("react/jsx-runtime");
1822
+ var DEFAULT_WIDTH = 440;
1823
+ var DEFAULT_HEIGHT = 580;
1824
+ var MIN_WIDTH = 330;
1825
+ var MIN_HEIGHT = 380;
1826
+ var MOBILE_MAX_WIDTH = 640;
1827
+ var EDGE_KEEP = 48;
1828
+ function viewport() {
1829
+ if (typeof window === "undefined") {
1830
+ return { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT };
1831
+ }
1832
+ return { width: window.innerWidth, height: window.innerHeight };
1833
+ }
1834
+ function clamp(rect) {
1835
+ const { width: vw, height: vh } = viewport();
1836
+ const width = Math.min(Math.max(rect.width, MIN_WIDTH), Math.max(vw, MIN_WIDTH));
1837
+ const height = Math.min(
1838
+ Math.max(rect.height, MIN_HEIGHT),
1839
+ Math.max(vh, MIN_HEIGHT)
1840
+ );
1841
+ return {
1842
+ width,
1843
+ height,
1844
+ x: Math.min(Math.max(rect.x, EDGE_KEEP - width), Math.max(vw - EDGE_KEEP, 0)),
1845
+ y: Math.min(Math.max(rect.y, 0), Math.max(vh - EDGE_KEEP, 0))
1846
+ };
1847
+ }
1848
+ function centred() {
1849
+ const { width: vw, height: vh } = viewport();
1850
+ const width = Math.min(DEFAULT_WIDTH, Math.max(vw - 32, MIN_WIDTH));
1851
+ const height = Math.min(DEFAULT_HEIGHT, Math.max(vh - 32, MIN_HEIGHT));
1852
+ return clamp({
1853
+ width,
1854
+ height,
1855
+ x: Math.round((vw - width) / 2),
1856
+ y: Math.round((vh - height) / 2)
1857
+ });
1858
+ }
1859
+ function DefaultWindowShell({
1860
+ id,
1861
+ title,
1862
+ onClose,
1863
+ icon,
1864
+ children,
1865
+ className
1866
+ }) {
1867
+ const panelRef = (0, import_react11.useRef)(null);
1868
+ const [mounted, setMounted] = (0, import_react11.useState)(false);
1869
+ const [isMobile, setIsMobile] = (0, import_react11.useState)(false);
1870
+ const [rect, setRect] = (0, import_react11.useState)(() => centred());
1871
+ const [maximized, setMaximized] = (0, import_react11.useState)(false);
1872
+ const gesture = (0, import_react11.useRef)(null);
1873
+ (0, import_react11.useLayoutEffect)(() => {
1874
+ setMounted(true);
1875
+ setIsMobile(window.innerWidth < MOBILE_MAX_WIDTH);
1876
+ setRect(centred());
1877
+ }, []);
1878
+ (0, import_react11.useEffect)(() => {
1879
+ if (!mounted) return;
1880
+ const onResize = () => {
1881
+ setIsMobile(window.innerWidth < MOBILE_MAX_WIDTH);
1882
+ setRect((current) => clamp(current));
1883
+ };
1884
+ window.addEventListener("resize", onResize);
1885
+ return () => window.removeEventListener("resize", onResize);
1886
+ }, [mounted]);
1887
+ (0, import_react11.useEffect)(() => {
1888
+ const onKeyDown = (event) => {
1889
+ if (event.key === "Escape") onClose();
1890
+ };
1891
+ window.addEventListener("keydown", onKeyDown);
1892
+ return () => window.removeEventListener("keydown", onKeyDown);
1893
+ }, [onClose]);
1894
+ (0, import_react11.useEffect)(() => {
1895
+ panelRef.current?.focus({ preventScroll: true });
1896
+ }, [mounted]);
1897
+ const beginGesture = (0, import_react11.useCallback)(
1898
+ (mode) => (event) => {
1899
+ if (isMobile || maximized || event.button !== 0) return;
1900
+ event.preventDefault();
1901
+ event.currentTarget.setPointerCapture(event.pointerId);
1902
+ gesture.current = {
1903
+ mode,
1904
+ pointerId: event.pointerId,
1905
+ startX: event.clientX,
1906
+ startY: event.clientY,
1907
+ origin: rect
1908
+ };
1909
+ },
1910
+ [isMobile, maximized, rect]
1911
+ );
1912
+ const onPointerMove = (0, import_react11.useCallback)((event) => {
1913
+ const active = gesture.current;
1914
+ if (!active || active.pointerId !== event.pointerId) return;
1915
+ const dx = event.clientX - active.startX;
1916
+ const dy = event.clientY - active.startY;
1917
+ setRect(
1918
+ clamp(
1919
+ active.mode === "move" ? { ...active.origin, x: active.origin.x + dx, y: active.origin.y + dy } : {
1920
+ ...active.origin,
1921
+ width: active.origin.width + dx,
1922
+ height: active.origin.height + dy
1923
+ }
1924
+ )
1925
+ );
1926
+ }, []);
1927
+ const endGesture = (0, import_react11.useCallback)((event) => {
1928
+ const active = gesture.current;
1929
+ if (!active || active.pointerId !== event.pointerId) return;
1930
+ gesture.current = null;
1931
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
1932
+ event.currentTarget.releasePointerCapture(event.pointerId);
1933
+ }
1934
+ }, []);
1935
+ if (!mounted || typeof document === "undefined") return null;
1936
+ const labelId = `assoc-window-title:${id}`;
1937
+ const style = isMobile ? {} : maximized ? { inset: "1rem", width: "auto", height: "auto" } : {
1938
+ left: rect.x,
1939
+ top: rect.y,
1940
+ width: rect.width,
1941
+ height: rect.height
1942
+ };
1943
+ return (0, import_react_dom.createPortal)(
1944
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1945
+ "div",
1946
+ {
1947
+ ref: panelRef,
1948
+ role: "dialog",
1949
+ "aria-labelledby": labelId,
1950
+ tabIndex: -1,
1951
+ style,
1952
+ className: (0, import_design_system.cn)(
1953
+ // Non-modal by design: no backdrop element exists, so the page
1954
+ // behind keeps every click. `pointer-events-auto` re-asserts our
1955
+ // own subtree in case a Radix surface left <body> inert.
1956
+ "pointer-events-auto fixed z-50 flex flex-col overflow-hidden border border-border bg-card text-card-foreground shadow-xl outline-none",
1957
+ isMobile ? "inset-x-0 bottom-0 max-h-[85dvh] rounded-t-xl" : "rounded-xl",
1958
+ className
1959
+ ),
1960
+ children: [
1961
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1962
+ "header",
1963
+ {
1964
+ onPointerDown: beginGesture("move"),
1965
+ onPointerMove,
1966
+ onPointerUp: endGesture,
1967
+ onPointerCancel: endGesture,
1968
+ onDoubleClick: () => {
1969
+ if (!isMobile) setMaximized((v) => !v);
1970
+ },
1971
+ className: (0, import_design_system.cn)(
1972
+ "flex shrink-0 items-center justify-between gap-2 border-b border-border px-3 py-2 select-none",
1973
+ isMobile ? "" : "cursor-move touch-none"
1974
+ ),
1975
+ children: [
1976
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1977
+ "span",
1978
+ {
1979
+ id: labelId,
1980
+ className: "flex min-w-0 items-center gap-1.5 text-sm font-medium text-foreground",
1981
+ children: [
1982
+ icon,
1983
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "truncate", children: title })
1984
+ ]
1985
+ }
1986
+ ),
1987
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "flex shrink-0 items-center gap-0.5", children: [
1988
+ isMobile ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1989
+ "button",
1990
+ {
1991
+ type: "button",
1992
+ onPointerDown: (event) => event.stopPropagation(),
1993
+ onClick: () => setMaximized((v) => !v),
1994
+ "aria-label": maximized ? "Restore window" : "Maximize window",
1995
+ className: "flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
1996
+ children: maximized ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RestoreIcon, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MaximizeIcon, { className: "h-3.5 w-3.5" })
1997
+ }
1998
+ ),
1999
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2000
+ "button",
2001
+ {
2002
+ type: "button",
2003
+ onPointerDown: (event) => event.stopPropagation(),
2004
+ onClick: onClose,
2005
+ "aria-label": "Close",
2006
+ className: "flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
2007
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(XIcon, { className: "h-3.5 w-3.5" })
2008
+ }
2009
+ )
2010
+ ] })
2011
+ ]
2012
+ }
2013
+ ),
2014
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "min-h-0 flex-1 overflow-hidden", children }),
2015
+ isMobile || maximized ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2016
+ "button",
2017
+ {
2018
+ type: "button",
2019
+ "aria-label": "Resize window",
2020
+ onPointerDown: beginGesture("resize"),
2021
+ onPointerMove,
2022
+ onPointerUp: endGesture,
2023
+ onPointerCancel: endGesture,
2024
+ className: "absolute bottom-0 right-0 flex h-4 w-4 cursor-nwse-resize touch-none items-center justify-center text-muted-foreground/60 hover:text-foreground",
2025
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(GripResizeIcon, { className: "h-3 w-3" })
2026
+ }
2027
+ )
2028
+ ]
2029
+ }
2030
+ ),
2031
+ document.body
2032
+ );
2033
+ }
2034
+
2035
+ // src/react/components/AssociationWindow.tsx
2036
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1596
2037
  function AssociationWindow({
1597
2038
  open,
1598
2039
  onClose,
@@ -1602,81 +2043,54 @@ function AssociationWindow({
1602
2043
  subtitle,
1603
2044
  children
1604
2045
  }) {
1605
- const instanceId = (0, import_react11.useId)();
2046
+ const instanceId = (0, import_react12.useId)();
1606
2047
  const { windowShell } = useAssociationsUiPorts();
1607
2048
  if (!open) return null;
1608
- const body = /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex h-full min-h-0 flex-col gap-2 p-3", children: [
1609
- subtitle ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "shrink-0 text-xs text-muted-foreground", children: subtitle }) : null,
2049
+ const body = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex h-full min-h-0 flex-col gap-2 p-3", children: [
2050
+ subtitle ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "shrink-0 text-xs text-muted-foreground", children: subtitle }) : null,
1610
2051
  children
1611
2052
  ] });
2053
+ const windowId = `association:${scopeId}:${instanceId}`;
1612
2054
  if (windowShell) {
1613
2055
  const HostWindow = windowShell.Window;
1614
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1615
- HostWindow,
1616
- {
1617
- id: `association:${scopeId}:${instanceId}`,
1618
- title,
1619
- onClose,
1620
- children: body
1621
- }
1622
- );
2056
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(HostWindow, { id: windowId, title, onClose, children: body });
1623
2057
  }
1624
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1625
- "div",
2058
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2059
+ DefaultWindowShell,
1626
2060
  {
1627
- role: "dialog",
1628
- "aria-label": title,
1629
- className: (0, import_design_system.cn)(
1630
- "pointer-events-auto fixed left-1/2 top-1/2 z-50 flex max-h-[85dvh] w-[min(440px,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl border border-border bg-card shadow-xl"
1631
- ),
1632
- style: { height: "min(580px, 85dvh)" },
1633
- children: [
1634
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-border px-3 py-2", children: [
1635
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "flex min-w-0 items-center gap-1.5 text-sm font-medium text-foreground", children: [
1636
- icon,
1637
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "truncate", children: title })
1638
- ] }),
1639
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1640
- "button",
1641
- {
1642
- type: "button",
1643
- onClick: onClose,
1644
- "aria-label": "Close",
1645
- className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
1646
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(XIcon, { className: "h-3.5 w-3.5" })
1647
- }
1648
- )
1649
- ] }),
1650
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "min-h-0 flex-1", children: body })
1651
- ]
2061
+ id: windowId,
2062
+ title,
2063
+ onClose,
2064
+ ...icon !== void 0 ? { icon } : {},
2065
+ children: body
1652
2066
  }
1653
2067
  );
1654
2068
  }
1655
2069
 
1656
2070
  // src/react/components/AssociationPicker.tsx
1657
- var import_react12 = require("react");
2071
+ var import_react13 = require("react");
1658
2072
  var import_design_system2 = require("@ai-matrx/design-system");
1659
- var import_jsx_runtime5 = require("react/jsx-runtime");
2073
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1660
2074
  function AssociationPicker(props) {
1661
2075
  const store = useAssociationsStore();
1662
2076
  const { pickerOverrides } = useAssociationsUiPorts();
1663
2077
  const info = store.registry.getEntityInfo(props.token);
1664
2078
  const Icon = info.Icon ?? DefaultEntityIcon;
1665
2079
  const Override = pickerOverrides?.[props.token];
1666
- if (Override) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Override, { ...props });
2080
+ if (Override) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Override, { ...props });
1667
2081
  const title = `Add ${info.labelPlural}`;
1668
2082
  const subtitle = props.containerLabel ? `Attach to ${props.containerLabel}` : "Click an item to attach or detach it";
1669
2083
  if (!props.open) return null;
1670
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2084
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1671
2085
  AssociationWindow,
1672
2086
  {
1673
2087
  open: props.open,
1674
2088
  onClose: () => props.onOpenChange(false),
1675
2089
  scopeId: `picker:${props.token}`,
1676
2090
  title,
1677
- icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Icon, { className: "size-3.5 text-primary" }),
2091
+ icon: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Icon, { className: "size-3.5 text-primary" }),
1678
2092
  subtitle,
1679
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2093
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1680
2094
  AssociationCandidateBody,
1681
2095
  {
1682
2096
  token: props.token,
@@ -1701,7 +2115,7 @@ function AssociationCandidateBody({
1701
2115
  onClose
1702
2116
  }) {
1703
2117
  void onClose;
1704
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2118
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1705
2119
  EntityCandidateList,
1706
2120
  {
1707
2121
  token,
@@ -1723,8 +2137,8 @@ function EntityCandidateList({
1723
2137
  }) {
1724
2138
  const store = useAssociationsStore();
1725
2139
  const notifier = useNotifier();
1726
- const [search, setSearch] = (0, import_react12.useState)("");
1727
- const [busyId, setBusyId] = (0, import_react12.useState)(null);
2140
+ const [search, setSearch] = (0, import_react13.useState)("");
2141
+ const [busyId, setBusyId] = (0, import_react13.useState)(null);
1728
2142
  const info = store.registry.getEntityInfo(token);
1729
2143
  const Icon = info.Icon ?? DefaultEntityIcon;
1730
2144
  const { candidates, loading, error, reload } = useAssociationCandidates({
@@ -1748,10 +2162,10 @@ function EntityCandidateList({
1748
2162
  setBusyId(null);
1749
2163
  }
1750
2164
  };
1751
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1752
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative mb-2", children: [
1753
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SearchIcon, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }),
1754
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2165
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2166
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "relative mb-2", children: [
2167
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SearchIcon, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }),
2168
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1755
2169
  import_design_system2.Input,
1756
2170
  {
1757
2171
  value: search,
@@ -1762,13 +2176,13 @@ function EntityCandidateList({
1762
2176
  }
1763
2177
  )
1764
2178
  ] }),
1765
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex-1 min-h-0 overflow-y-auto -mx-1 px-1", children: loading && candidates.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(ListMessage, { children: [
1766
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SpinnerIcon, { className: "h-4 w-4 animate-spin" }),
2179
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-1 min-h-0 overflow-y-auto -mx-1 px-1", children: loading && candidates.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(ListMessage, { children: [
2180
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SpinnerIcon, { className: "h-4 w-4 animate-spin" }),
1767
2181
  "Loading\u2026"
1768
- ] }) : error ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-[12px] text-destructive", children: [
1769
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "font-medium", children: "Couldn\u2019t load items" }),
1770
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "opacity-80", children: error }),
1771
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2182
+ ] }) : error ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-[12px] text-destructive", children: [
2183
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "font-medium", children: "Couldn\u2019t load items" }),
2184
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "opacity-80", children: error }),
2185
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1772
2186
  "button",
1773
2187
  {
1774
2188
  type: "button",
@@ -1777,10 +2191,10 @@ function EntityCandidateList({
1777
2191
  children: "Retry"
1778
2192
  }
1779
2193
  )
1780
- ] }) : candidates.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ListMessage, { children: canCreate ? `Nothing to attach yet \u2014 create a new ${info.label.toLowerCase()} below.` : "Nothing to attach." }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { className: "space-y-0.5", children: candidates.map((c) => {
2194
+ ] }) : candidates.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ListMessage, { children: canCreate ? `Nothing to attach yet \u2014 create a new ${info.label.toLowerCase()} below.` : "Nothing to attach." }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "space-y-0.5", children: candidates.map((c) => {
1781
2195
  const attached = attachedIds.has(c.id);
1782
2196
  const busy = busyId === c.id;
1783
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2197
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1784
2198
  "button",
1785
2199
  {
1786
2200
  type: "button",
@@ -1792,23 +2206,23 @@ function EntityCandidateList({
1792
2206
  attached && "bg-accent/40"
1793
2207
  ),
1794
2208
  children: [
1795
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
1796
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "flex-1 min-w-0 truncate text-foreground", children: c.title }),
1797
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2209
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
2210
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "flex-1 min-w-0 truncate text-foreground", children: c.title }),
2211
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1798
2212
  "span",
1799
2213
  {
1800
2214
  className: (0, import_design_system2.cn)(
1801
2215
  "flex h-5 w-5 items-center justify-center rounded-full shrink-0",
1802
2216
  attached ? "bg-primary text-primary-foreground" : "border border-border text-muted-foreground group-hover:border-primary/60"
1803
2217
  ),
1804
- children: busy ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SpinnerIcon, { className: "h-3 w-3 animate-spin" }) : attached ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CheckIcon, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PlusIcon, { className: "h-3 w-3" })
2218
+ children: busy ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SpinnerIcon, { className: "h-3 w-3 animate-spin" }) : attached ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CheckIcon, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PlusIcon, { className: "h-3 w-3" })
1805
2219
  }
1806
2220
  )
1807
2221
  ]
1808
2222
  }
1809
2223
  ) }, c.id);
1810
2224
  }) }) }),
1811
- canCreate && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2225
+ canCreate && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1812
2226
  CreateAndAttachFooter,
1813
2227
  {
1814
2228
  token,
@@ -1830,9 +2244,9 @@ function CreateAndAttachFooter({
1830
2244
  const store = useAssociationsStore();
1831
2245
  const notifier = useNotifier();
1832
2246
  const info = store.registry.getEntityInfo(token);
1833
- const [editing, setEditing] = (0, import_react12.useState)(false);
1834
- const [name, setName] = (0, import_react12.useState)("");
1835
- const [busy, setBusy] = (0, import_react12.useState)(false);
2247
+ const [editing, setEditing] = (0, import_react13.useState)(false);
2248
+ const [name, setName] = (0, import_react13.useState)("");
2249
+ const [busy, setBusy] = (0, import_react13.useState)(false);
1836
2250
  const submit = async () => {
1837
2251
  const title = name.trim();
1838
2252
  if (!title || busy) return;
@@ -1867,7 +2281,7 @@ function CreateAndAttachFooter({
1867
2281
  }
1868
2282
  };
1869
2283
  if (!editing) {
1870
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2284
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1871
2285
  "button",
1872
2286
  {
1873
2287
  type: "button",
@@ -1877,10 +2291,10 @@ function CreateAndAttachFooter({
1877
2291
  },
1878
2292
  className: "mt-2 flex min-h-11 w-full shrink-0 items-center justify-center gap-1.5 rounded-md border border-dashed border-border px-3 py-2 text-sm text-muted-foreground transition-colors hover:border-primary/60 hover:text-foreground md:min-h-0",
1879
2293
  children: [
1880
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PlusIcon, { className: "h-4 w-4" }),
2294
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PlusIcon, { className: "h-4 w-4" }),
1881
2295
  "New ",
1882
2296
  info.label,
1883
- seed ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "max-w-40 truncate text-muted-foreground/70", children: [
2297
+ seed ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "max-w-40 truncate text-muted-foreground/70", children: [
1884
2298
  "\u201C",
1885
2299
  seed,
1886
2300
  "\u201D"
@@ -1889,8 +2303,8 @@ function CreateAndAttachFooter({
1889
2303
  }
1890
2304
  );
1891
2305
  }
1892
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mt-2 flex shrink-0 items-center gap-1.5", children: [
1893
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2306
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mt-2 flex shrink-0 items-center gap-1.5", children: [
2307
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1894
2308
  import_design_system2.Input,
1895
2309
  {
1896
2310
  autoFocus: true,
@@ -1909,17 +2323,17 @@ function CreateAndAttachFooter({
1909
2323
  style: { fontSize: 16 }
1910
2324
  }
1911
2325
  ),
1912
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2326
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1913
2327
  "button",
1914
2328
  {
1915
2329
  type: "button",
1916
2330
  disabled: busy || !name.trim(),
1917
2331
  onClick: () => void submit(),
1918
2332
  className: "flex h-11 items-center gap-1 rounded-md bg-primary px-2.5 text-sm font-medium text-primary-foreground transition-opacity disabled:opacity-50 md:h-8",
1919
- children: busy ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SpinnerIcon, { className: "h-3.5 w-3.5 animate-spin" }) : "Create"
2333
+ children: busy ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SpinnerIcon, { className: "h-3.5 w-3.5 animate-spin" }) : "Create"
1920
2334
  }
1921
2335
  ),
1922
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2336
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1923
2337
  "button",
1924
2338
  {
1925
2339
  type: "button",
@@ -1930,38 +2344,122 @@ function CreateAndAttachFooter({
1930
2344
  },
1931
2345
  title: "Cancel",
1932
2346
  className: "flex h-11 w-11 items-center justify-center rounded-md border border-border text-muted-foreground transition-colors hover:text-foreground disabled:opacity-50 md:h-8 md:w-8",
1933
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(XIcon, { className: "h-4 w-4" })
2347
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(XIcon, { className: "h-4 w-4" })
1934
2348
  }
1935
2349
  )
1936
2350
  ] });
1937
2351
  }
1938
2352
  function ListMessage({ children }) {
1939
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex items-center justify-center gap-2 py-8 text-[13px] text-muted-foreground", children });
2353
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex items-center justify-center gap-2 py-8 text-[13px] text-muted-foreground", children });
1940
2354
  }
1941
2355
 
1942
2356
  // src/react/components/UniversalAssociationPicker.tsx
1943
- var import_react13 = require("react");
2357
+ var import_react14 = require("react");
2358
+ var import_design_system4 = require("@ai-matrx/design-system");
2359
+
2360
+ // src/react/components/entityDoors.tsx
1944
2361
  var import_design_system3 = require("@ai-matrx/design-system");
1945
- var import_jsx_runtime6 = require("react/jsx-runtime");
2362
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2363
+ function DoorRef(props) {
2364
+ const { onOpen, ...refProps } = props;
2365
+ const { entityDoors } = useAssociationsUiPorts();
2366
+ const store = useAssociationsStore();
2367
+ const HostRef = entityDoors?.EntityRef;
2368
+ if (HostRef) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HostRef, { ...refProps });
2369
+ const name = refProps.name ?? null;
2370
+ const href = store.registry.tryGetEntityInfo(refProps.token)?.hrefFor?.(refProps.id) ?? null;
2371
+ if (onOpen) {
2372
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2373
+ "button",
2374
+ {
2375
+ type: "button",
2376
+ onClick: onOpen,
2377
+ className: (0, import_design_system3.cn)(
2378
+ "min-w-0 truncate text-left underline-offset-2 hover:underline",
2379
+ refProps.className
2380
+ ),
2381
+ children: name
2382
+ }
2383
+ );
2384
+ }
2385
+ if (href) {
2386
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2387
+ "a",
2388
+ {
2389
+ href,
2390
+ target: refProps.openInNewTab === false ? void 0 : "_blank",
2391
+ rel: "noopener noreferrer",
2392
+ className: (0, import_design_system3.cn)(
2393
+ "inline-flex min-w-0 items-center gap-1 truncate underline-offset-2 hover:underline",
2394
+ refProps.className
2395
+ ),
2396
+ children: [
2397
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "min-w-0 truncate", children: name }),
2398
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ExternalLinkIcon, { className: "h-3 w-3 shrink-0 opacity-0 transition-opacity group-hover:opacity-60" })
2399
+ ]
2400
+ }
2401
+ );
2402
+ }
2403
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: (0, import_design_system3.cn)("min-w-0 truncate", refProps.className), children: name });
2404
+ }
2405
+ function UnresolvedRef({ token, id }) {
2406
+ const { entityDoors } = useAssociationsUiPorts();
2407
+ const HostUnresolved = entityDoors?.UnresolvedRef;
2408
+ if (HostUnresolved) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HostUnresolved, { token, id });
2409
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2410
+ "span",
2411
+ {
2412
+ className: "inline-flex items-center rounded border border-border/60 bg-muted/40 px-1.5 py-0.5 text-[11px] text-muted-foreground",
2413
+ title: `This ${token} could not be read \u2014 it may be deleted or not shared with you.`,
2414
+ children: "Unavailable"
2415
+ }
2416
+ );
2417
+ }
2418
+ function DoorControlsSlot(props) {
2419
+ const { entityDoors } = useAssociationsUiPorts();
2420
+ const store = useAssociationsStore();
2421
+ const HostControls = entityDoors?.DoorControls;
2422
+ if (HostControls) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HostControls, { ...props });
2423
+ const href = store.registry.tryGetEntityInfo(props.token)?.hrefFor?.(props.id) ?? null;
2424
+ if (!href) return null;
2425
+ const label = props.name ? `Open ${props.name}` : `Open this ${props.token}`;
2426
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2427
+ "a",
2428
+ {
2429
+ href,
2430
+ target: "_blank",
2431
+ rel: "noopener noreferrer",
2432
+ "aria-label": label,
2433
+ title: label,
2434
+ className: (0, import_design_system3.cn)(
2435
+ "inline-flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
2436
+ props.className
2437
+ ),
2438
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ExternalLinkIcon, { className: "h-3 w-3" })
2439
+ }
2440
+ );
2441
+ }
2442
+
2443
+ // src/react/components/UniversalAssociationPicker.tsx
2444
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1946
2445
  function attachedKey(token, id) {
1947
2446
  return `${token}:${id}`;
1948
2447
  }
1949
2448
  function UniversalAssociationPicker(props) {
1950
2449
  const { attachedKeys, onAttach, onDetach, ownerId, orgId, className } = props;
1951
2450
  const store = useAssociationsStore();
1952
- const { entityDoors, pickerOverrides } = useAssociationsUiPorts();
2451
+ const { pickerOverrides } = useAssociationsUiPorts();
1953
2452
  const notifier = useNotifier();
1954
2453
  const tokens = props.tokens ?? store.registry.curatedTokens();
1955
- const [query, setQuery] = (0, import_react13.useState)("");
1956
- const [browseToken, setBrowseToken] = (0, import_react13.useState)(null);
1957
- const [busyKey, setBusyKey] = (0, import_react13.useState)(null);
2454
+ const [query, setQuery] = (0, import_react14.useState)("");
2455
+ const [browseToken, setBrowseToken] = (0, import_react14.useState)(null);
2456
+ const [busyKey, setBusyKey] = (0, import_react14.useState)(null);
1958
2457
  const { results, loading, isRecents } = useUniversalEntitySearch({
1959
2458
  query,
1960
2459
  tokens,
1961
2460
  ownerId: ownerId ?? null,
1962
2461
  enabled: browseToken === null
1963
2462
  });
1964
- const DoorControls = entityDoors?.DoorControls;
1965
2463
  const toggle = async (c) => {
1966
2464
  const key = attachedKey(c.token, c.id);
1967
2465
  if (busyKey) return;
@@ -1985,11 +2483,11 @@ function UniversalAssociationPicker(props) {
1985
2483
  groups.set(r.token, list);
1986
2484
  }
1987
2485
  const BrowseOverride = browseToken ? pickerOverrides?.[browseToken] : void 0;
1988
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: (0, import_design_system3.cn)("flex min-h-0 flex-1 flex-col gap-2", className), children: [
1989
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "relative", children: [
1990
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SearchIcon, { className: "absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }),
1991
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1992
- import_design_system3.Input,
2486
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: (0, import_design_system4.cn)("flex min-h-0 flex-1 flex-col gap-2", className), children: [
2487
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "relative", children: [
2488
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SearchIcon, { className: "absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }),
2489
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2490
+ import_design_system4.Input,
1993
2491
  {
1994
2492
  value: query,
1995
2493
  onChange: (e) => {
@@ -2001,23 +2499,23 @@ function UniversalAssociationPicker(props) {
2001
2499
  style: { fontSize: 16 }
2002
2500
  }
2003
2501
  ),
2004
- loading && browseToken === null && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SpinnerIcon, { className: "absolute right-2.5 top-1/2 h-4 w-4 -translate-y-1/2 animate-spin text-muted-foreground" })
2502
+ loading && browseToken === null && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SpinnerIcon, { className: "absolute right-2.5 top-1/2 h-4 w-4 -translate-y-1/2 animate-spin text-muted-foreground" })
2005
2503
  ] }),
2006
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex flex-wrap gap-1", children: tokens.map((t) => {
2504
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex flex-wrap gap-1", children: tokens.map((t) => {
2007
2505
  const info = store.registry.getEntityInfo(t);
2008
2506
  const ChipIcon = info.Icon ?? DefaultEntityIcon;
2009
2507
  const active = browseToken === t;
2010
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2508
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2011
2509
  "button",
2012
2510
  {
2013
2511
  type: "button",
2014
2512
  onClick: () => setBrowseToken(active ? null : t),
2015
- className: (0, import_design_system3.cn)(
2513
+ className: (0, import_design_system4.cn)(
2016
2514
  "inline-flex items-center gap-1 rounded-md border px-1.5 py-0.5 text-[11px] transition-colors",
2017
2515
  active ? "border-primary/50 bg-primary/10 text-foreground" : "border-border text-muted-foreground hover:text-foreground hover:bg-accent"
2018
2516
  ),
2019
2517
  children: [
2020
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChipIcon, { className: "h-3 w-3" }),
2518
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ChipIcon, { className: "h-3 w-3" }),
2021
2519
  info.labelPlural
2022
2520
  ]
2023
2521
  },
@@ -2027,7 +2525,7 @@ function UniversalAssociationPicker(props) {
2027
2525
  browseToken && BrowseOverride ? (
2028
2526
  // The token's canonical host picker (e.g. the stored-files browser)
2029
2527
  // owns per-token browsing when registered — never a plain list twin.
2030
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2528
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2031
2529
  BrowseOverride,
2032
2530
  {
2033
2531
  open: true,
@@ -2043,7 +2541,7 @@ function UniversalAssociationPicker(props) {
2043
2541
  onDetach: (id) => onDetach(browseToken, id)
2044
2542
  }
2045
2543
  )
2046
- ) : browseToken ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex min-h-0 flex-1 flex-col", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2544
+ ) : browseToken ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex min-h-0 flex-1 flex-col", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2047
2545
  AssociationCandidateBody,
2048
2546
  {
2049
2547
  token: browseToken,
@@ -2056,63 +2554,63 @@ function UniversalAssociationPicker(props) {
2056
2554
  onAttach: (id, title) => onAttach(browseToken, id, title),
2057
2555
  onDetach: (id) => onDetach(browseToken, id)
2058
2556
  }
2059
- ) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "-mx-1 min-h-0 flex-1 overflow-y-auto px-1", children: results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "py-6 text-center text-[12px] text-muted-foreground", children: loading ? "Searching\u2026" : query.trim() ? "No matches \u2014 narrow with a type chip to browse." : "No recent items. Type to search, or pick a type to browse." }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-2", children: [
2060
- isRecents && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "px-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/60", children: "Recent" }),
2557
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "-mx-1 min-h-0 flex-1 overflow-y-auto px-1", children: results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "py-6 text-center text-[12px] text-muted-foreground", children: loading ? "Searching\u2026" : query.trim() ? "No matches \u2014 narrow with a type chip to browse." : "No recent items. Type to search, or pick a type to browse." }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-2", children: [
2558
+ isRecents && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "px-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/60", children: "Recent" }),
2061
2559
  [...groups.entries()].map(([token, items]) => {
2062
2560
  const info = store.registry.getEntityInfo(token);
2063
2561
  const GroupIcon = info.Icon ?? DefaultEntityIcon;
2064
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-0.5", children: [
2065
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "flex items-center gap-1 px-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/70", children: [
2066
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(GroupIcon, { className: "h-3 w-3" }),
2562
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-0.5", children: [
2563
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("p", { className: "flex items-center gap-1 px-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/70", children: [
2564
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(GroupIcon, { className: "h-3 w-3" }),
2067
2565
  info.labelPlural
2068
2566
  ] }),
2069
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "space-y-0.5", children: items.map((c) => {
2567
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "space-y-0.5", children: items.map((c) => {
2070
2568
  const key = attachedKey(c.token, c.id);
2071
2569
  const attached = attachedKeys.has(key);
2072
2570
  const busy = busyKey === key;
2073
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2571
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2074
2572
  "li",
2075
2573
  {
2076
- className: (0, import_design_system3.cn)(
2574
+ className: (0, import_design_system4.cn)(
2077
2575
  "group/entity-ref group flex items-center gap-1 rounded-md pr-1.5 transition-colors",
2078
2576
  "hover:bg-accent",
2079
2577
  attached && "bg-accent/40"
2080
2578
  ),
2081
2579
  children: [
2082
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2580
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2083
2581
  "button",
2084
2582
  {
2085
2583
  type: "button",
2086
2584
  disabled: busy,
2087
2585
  onClick: () => toggle(c),
2088
2586
  title: attached ? `Detach "${c.title}"` : `Attach "${c.title}"`,
2089
- className: (0, import_design_system3.cn)(
2587
+ className: (0, import_design_system4.cn)(
2090
2588
  "flex min-w-0 flex-1 items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-sm",
2091
2589
  "disabled:opacity-50"
2092
2590
  ),
2093
2591
  children: [
2094
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "min-w-0 flex-1 truncate text-foreground", children: c.title }),
2095
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2592
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "min-w-0 flex-1 truncate text-foreground", children: c.title }),
2593
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2096
2594
  "span",
2097
2595
  {
2098
- className: (0, import_design_system3.cn)(
2596
+ className: (0, import_design_system4.cn)(
2099
2597
  "flex h-5 w-5 shrink-0 items-center justify-center rounded-full",
2100
2598
  attached ? "bg-primary text-primary-foreground" : "border border-border text-muted-foreground group-hover:border-primary/60"
2101
2599
  ),
2102
- children: busy ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SpinnerIcon, { className: "h-3 w-3 animate-spin" }) : attached ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CheckIcon, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PlusIcon, { className: "h-3 w-3" })
2600
+ children: busy ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SpinnerIcon, { className: "h-3 w-3 animate-spin" }) : attached ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CheckIcon, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PlusIcon, { className: "h-3 w-3" })
2103
2601
  }
2104
2602
  )
2105
2603
  ]
2106
2604
  }
2107
2605
  ),
2108
- DoorControls ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2109
- DoorControls,
2606
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2607
+ DoorControlsSlot,
2110
2608
  {
2111
2609
  token: c.token,
2112
2610
  id: c.id,
2113
2611
  name: c.title
2114
2612
  }
2115
- ) : null
2613
+ )
2116
2614
  ]
2117
2615
  },
2118
2616
  key
@@ -2125,79 +2623,18 @@ function UniversalAssociationPicker(props) {
2125
2623
  }
2126
2624
 
2127
2625
  // src/react/components/AttachedItemsSheet.tsx
2128
- var import_react14 = require("react");
2626
+ var import_react15 = require("react");
2129
2627
  var import_design_system5 = require("@ai-matrx/design-system");
2130
-
2131
- // src/react/components/entityDoors.tsx
2132
- var import_design_system4 = require("@ai-matrx/design-system");
2133
- var import_jsx_runtime7 = require("react/jsx-runtime");
2134
- function DoorRef(props) {
2135
- const { onOpen, ...refProps } = props;
2136
- const { entityDoors } = useAssociationsUiPorts();
2137
- const store = useAssociationsStore();
2138
- const HostRef = entityDoors?.EntityRef;
2139
- if (HostRef) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HostRef, { ...refProps });
2140
- const name = refProps.name ?? null;
2141
- const href = store.registry.tryGetEntityInfo(refProps.token)?.hrefFor?.(refProps.id) ?? null;
2142
- if (onOpen) {
2143
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2144
- "button",
2145
- {
2146
- type: "button",
2147
- onClick: onOpen,
2148
- className: (0, import_design_system4.cn)(
2149
- "min-w-0 truncate text-left underline-offset-2 hover:underline",
2150
- refProps.className
2151
- ),
2152
- children: name
2153
- }
2154
- );
2155
- }
2156
- if (href) {
2157
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2158
- "a",
2159
- {
2160
- href,
2161
- target: refProps.openInNewTab === false ? void 0 : "_blank",
2162
- rel: "noopener noreferrer",
2163
- className: (0, import_design_system4.cn)(
2164
- "inline-flex min-w-0 items-center gap-1 truncate underline-offset-2 hover:underline",
2165
- refProps.className
2166
- ),
2167
- children: [
2168
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "min-w-0 truncate", children: name }),
2169
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ExternalLinkIcon, { className: "h-3 w-3 shrink-0 opacity-0 transition-opacity group-hover:opacity-60" })
2170
- ]
2171
- }
2172
- );
2173
- }
2174
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: (0, import_design_system4.cn)("min-w-0 truncate", refProps.className), children: name });
2175
- }
2176
- function UnresolvedRef({ token, id }) {
2177
- const { entityDoors } = useAssociationsUiPorts();
2178
- const HostUnresolved = entityDoors?.UnresolvedRef;
2179
- if (HostUnresolved) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HostUnresolved, { token, id });
2180
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2181
- "span",
2182
- {
2183
- className: "inline-flex items-center rounded border border-border/60 bg-muted/40 px-1.5 py-0.5 text-[11px] text-muted-foreground",
2184
- title: `This ${token} could not be read \u2014 it may be deleted or not shared with you.`,
2185
- children: "Unavailable"
2186
- }
2187
- );
2188
- }
2189
-
2190
- // src/react/components/AttachedItemsSheet.tsx
2191
- var import_jsx_runtime8 = require("react/jsx-runtime");
2628
+ var import_jsx_runtime9 = require("react/jsx-runtime");
2192
2629
  function AttachedItemsSheet(props) {
2193
2630
  const store = useAssociationsStore();
2194
2631
  const info = store.registry.getEntityInfo(props.token);
2195
2632
  const Icon = info.Icon ?? DefaultEntityIcon;
2196
2633
  const title = info.labelPlural;
2197
2634
  const containerName = props.container?.label ?? props.containerLabel;
2198
- const subtitle = props.container ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "inline-flex min-w-0 max-w-full items-center gap-1", children: [
2635
+ const subtitle = props.container ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "inline-flex min-w-0 max-w-full items-center gap-1", children: [
2199
2636
  "Attached to",
2200
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2637
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2201
2638
  DoorRef,
2202
2639
  {
2203
2640
  token: props.container.type,
@@ -2208,16 +2645,16 @@ function AttachedItemsSheet(props) {
2208
2645
  )
2209
2646
  ] }) : props.containerLabel ? `Attached to ${props.containerLabel}` : "Attached items";
2210
2647
  if (!props.open) return null;
2211
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2648
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2212
2649
  AssociationWindow,
2213
2650
  {
2214
2651
  open: props.open,
2215
2652
  onClose: () => props.onOpenChange(false),
2216
2653
  scopeId: `attached:${props.token}`,
2217
2654
  title,
2218
- icon: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Icon, { className: "size-3.5 text-primary" }),
2655
+ icon: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Icon, { className: "size-3.5 text-primary" }),
2219
2656
  subtitle,
2220
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2657
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2221
2658
  AttachedItemsBody,
2222
2659
  {
2223
2660
  token: props.token,
@@ -2241,11 +2678,11 @@ function AttachedItemsBody({
2241
2678
  const notifier = useNotifier();
2242
2679
  const info = store.registry.getEntityInfo(token);
2243
2680
  const Icon = info.Icon ?? DefaultEntityIcon;
2244
- const [titles, setTitles] = (0, import_react14.useState)(null);
2245
- const [titleError, setTitleError] = (0, import_react14.useState)(null);
2246
- const [busyId, setBusyId] = (0, import_react14.useState)(null);
2681
+ const [titles, setTitles] = (0, import_react15.useState)(null);
2682
+ const [titleError, setTitleError] = (0, import_react15.useState)(null);
2683
+ const [busyId, setBusyId] = (0, import_react15.useState)(null);
2247
2684
  const idKey = links.map((l) => l.resourceId).sort().join(",");
2248
- (0, import_react14.useEffect)(() => {
2685
+ (0, import_react15.useEffect)(() => {
2249
2686
  if (!enabled) return;
2250
2687
  const ids = idKey ? idKey.split(",") : [];
2251
2688
  if (ids.length === 0) {
@@ -2289,30 +2726,30 @@ function AttachedItemsBody({
2289
2726
  setBusyId(null);
2290
2727
  }
2291
2728
  };
2292
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2293
- titleError ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-[12px] text-destructive", children: [
2729
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2730
+ titleError ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "mb-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-[12px] text-destructive", children: [
2294
2731
  "Showing names recorded at attach time \u2014 they may be out of date.",
2295
2732
  " ",
2296
2733
  titleError
2297
2734
  ] }) : null,
2298
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex-1 min-h-0 overflow-y-auto -mx-1 px-1", children: links.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex items-center justify-center py-8 text-[13px] text-muted-foreground", children: "Nothing attached yet." }) : titles === null ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-center gap-2 py-8 text-[13px] text-muted-foreground", children: [
2299
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SpinnerIcon, { className: "h-4 w-4 animate-spin" }),
2735
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex-1 min-h-0 overflow-y-auto -mx-1 px-1", children: links.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex items-center justify-center py-8 text-[13px] text-muted-foreground", children: "Nothing attached yet." }) : titles === null ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-center gap-2 py-8 text-[13px] text-muted-foreground", children: [
2736
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SpinnerIcon, { className: "h-4 w-4 animate-spin" }),
2300
2737
  "Loading\u2026"
2301
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "space-y-0.5", children: links.map((link) => {
2738
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("ul", { className: "space-y-0.5", children: links.map((link) => {
2302
2739
  const resolved = titles.get(link.resourceId);
2303
2740
  const missing = !titles.has(link.resourceId);
2304
2741
  const name = resolved ?? link.label ?? (missing ? null : "Untitled");
2305
2742
  const busy = busyId === link.resourceId;
2306
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2743
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2307
2744
  "li",
2308
2745
  {
2309
2746
  className: "group/entity-ref group flex items-center gap-2 rounded-md px-2.5 py-2 text-sm transition-colors hover:bg-accent",
2310
2747
  children: [
2311
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
2312
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "flex-1 min-w-0 truncate", children: name === null ? (
2748
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
2749
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "flex-1 min-w-0 truncate", children: name === null ? (
2313
2750
  // The X beside the row stays the one-click detach.
2314
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(UnresolvedRef, { token, id: link.resourceId })
2315
- ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2751
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(UnresolvedRef, { token, id: link.resourceId })
2752
+ ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2316
2753
  DoorRef,
2317
2754
  {
2318
2755
  token,
@@ -2322,7 +2759,7 @@ function AttachedItemsBody({
2322
2759
  className: "text-foreground"
2323
2760
  }
2324
2761
  ) }),
2325
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2762
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2326
2763
  "button",
2327
2764
  {
2328
2765
  type: "button",
@@ -2333,7 +2770,7 @@ function AttachedItemsBody({
2333
2770
  "flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors",
2334
2771
  "hover:bg-destructive/10 hover:text-destructive disabled:opacity-50"
2335
2772
  ),
2336
- children: busy ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SpinnerIcon, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(XIcon, { className: "h-3.5 w-3.5" })
2773
+ children: busy ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SpinnerIcon, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(XIcon, { className: "h-3.5 w-3.5" })
2337
2774
  }
2338
2775
  )
2339
2776
  ]
@@ -2341,14 +2778,14 @@ function AttachedItemsBody({
2341
2778
  link.edgeId
2342
2779
  );
2343
2780
  }) }) }),
2344
- onAdd ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2781
+ onAdd ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2345
2782
  "button",
2346
2783
  {
2347
2784
  type: "button",
2348
2785
  onClick: onAdd,
2349
2786
  className: "mt-2 flex w-full items-center justify-center gap-1.5 rounded-md border border-border px-3 py-2 text-sm text-muted-foreground transition-colors hover:border-primary/60 hover:text-foreground",
2350
2787
  children: [
2351
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PlusIcon, { className: "h-4 w-4" }),
2788
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PlusIcon, { className: "h-4 w-4" }),
2352
2789
  "Attach ",
2353
2790
  info.labelPlural.toLowerCase()
2354
2791
  ]
@@ -2358,7 +2795,7 @@ function AttachedItemsBody({
2358
2795
  }
2359
2796
 
2360
2797
  // src/react/components/AssociationCard.tsx
2361
- var import_react15 = require("react");
2798
+ var import_react16 = require("react");
2362
2799
  var import_design_system6 = require("@ai-matrx/design-system");
2363
2800
 
2364
2801
  // src/react/contentRoles.ts
@@ -2409,7 +2846,7 @@ function getContentRoleMeta(role) {
2409
2846
  }
2410
2847
 
2411
2848
  // src/react/components/AssociationCard.tsx
2412
- var import_jsx_runtime9 = require("react/jsx-runtime");
2849
+ var import_jsx_runtime10 = require("react/jsx-runtime");
2413
2850
  function AssociationCard({
2414
2851
  token,
2415
2852
  container: containerProp,
@@ -2418,11 +2855,11 @@ function AssociationCard({
2418
2855
  const store = useAssociationsStore();
2419
2856
  const fromCtx = usePrimaryEntity();
2420
2857
  const container = containerProp ?? fromCtx;
2421
- const [open, setOpen] = (0, import_react15.useState)(false);
2858
+ const [open, setOpen] = (0, import_react16.useState)(false);
2422
2859
  const info = store.registry.getEntityInfo(token);
2423
2860
  const role = getContentRoleMeta(info.contentRole);
2424
2861
  const Icon = info.Icon ?? DefaultEntityIcon;
2425
- const [listOpen, setListOpen] = (0, import_react15.useState)(false);
2862
+ const [listOpen, setListOpen] = (0, import_react16.useState)(false);
2426
2863
  const { status, countFor, attachedIdsFor, linksFor, attach, detach } = useContainerLinks({
2427
2864
  containerType: container?.type ?? "organization",
2428
2865
  containerId: container?.id ?? null,
@@ -2440,8 +2877,8 @@ function AssociationCard({
2440
2877
  const loading = status === "loading" || status === "idle";
2441
2878
  const canAttach = info.canListCandidates;
2442
2879
  const canDrillIn = count > 0;
2443
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2444
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2880
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2881
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2445
2882
  "div",
2446
2883
  {
2447
2884
  className: (0, import_design_system6.cn)(
@@ -2449,7 +2886,7 @@ function AssociationCard({
2449
2886
  className
2450
2887
  ),
2451
2888
  children: [
2452
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2889
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2453
2890
  "span",
2454
2891
  {
2455
2892
  className: (0, import_design_system6.cn)(
@@ -2458,7 +2895,7 @@ function AssociationCard({
2458
2895
  )
2459
2896
  }
2460
2897
  ),
2461
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2898
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2462
2899
  "button",
2463
2900
  {
2464
2901
  type: "button",
@@ -2470,7 +2907,7 @@ function AssociationCard({
2470
2907
  canDrillIn ? "cursor-pointer" : "cursor-default"
2471
2908
  ),
2472
2909
  children: [
2473
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2910
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2474
2911
  "span",
2475
2912
  {
2476
2913
  className: (0, import_design_system6.cn)(
@@ -2478,20 +2915,20 @@ function AssociationCard({
2478
2915
  role.accentBg,
2479
2916
  role.accentText
2480
2917
  ),
2481
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Icon, { className: "h-4 w-4" })
2918
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Icon, { className: "h-4 w-4" })
2482
2919
  }
2483
2920
  ),
2484
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "min-w-0 flex-1", children: [
2485
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "block truncate text-sm font-medium text-foreground", children: info.labelPlural }),
2486
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "block text-[11px] text-muted-foreground tabular-nums", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
2487
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SpinnerIcon, { className: "h-3 w-3 animate-spin" }),
2921
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "min-w-0 flex-1", children: [
2922
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "block truncate text-sm font-medium text-foreground", children: info.labelPlural }),
2923
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "block text-[11px] text-muted-foreground tabular-nums", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
2924
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SpinnerIcon, { className: "h-3 w-3 animate-spin" }),
2488
2925
  "Loading\u2026"
2489
2926
  ] }) : `${count} attached` })
2490
2927
  ] })
2491
2928
  ]
2492
2929
  }
2493
2930
  ),
2494
- canAttach && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2931
+ canAttach && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2495
2932
  "button",
2496
2933
  {
2497
2934
  type: "button",
@@ -2499,13 +2936,13 @@ function AssociationCard({
2499
2936
  "aria-label": `Attach ${info.labelPlural.toLowerCase()}`,
2500
2937
  title: `Attach ${info.labelPlural.toLowerCase()}`,
2501
2938
  className: "mr-2 flex h-11 w-11 shrink-0 items-center justify-center rounded-md border border-border text-muted-foreground transition-colors hover:border-primary/60 hover:text-foreground lg:mr-3 lg:h-7 lg:w-7",
2502
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PlusIcon, { className: "h-4 w-4" })
2939
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PlusIcon, { className: "h-4 w-4" })
2503
2940
  }
2504
2941
  )
2505
2942
  ]
2506
2943
  }
2507
2944
  ),
2508
- listOpen && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2945
+ listOpen && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2509
2946
  AttachedItemsSheet,
2510
2947
  {
2511
2948
  open: listOpen,
@@ -2523,7 +2960,7 @@ function AssociationCard({
2523
2960
  onDetach: (resourceId) => detach(token, resourceId)
2524
2961
  }
2525
2962
  ),
2526
- canAttach && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2963
+ canAttach && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2527
2964
  AssociationPicker,
2528
2965
  {
2529
2966
  open,
@@ -2541,7 +2978,7 @@ function AssociationCard({
2541
2978
 
2542
2979
  // src/react/components/AssociationCardGrid.tsx
2543
2980
  var import_design_system7 = require("@ai-matrx/design-system");
2544
- var import_jsx_runtime10 = require("react/jsx-runtime");
2981
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2545
2982
  function AssociationCardGrid({
2546
2983
  tokens,
2547
2984
  excludeTokens,
@@ -2552,33 +2989,33 @@ function AssociationCardGrid({
2552
2989
  const list = (tokens ?? store.registry.curatedTokens()).filter(
2553
2990
  (token) => !excluded.has(token)
2554
2991
  );
2555
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: (0, import_design_system7.cn)("space-y-5", className), children: CONTENT_ROLES.map((role) => {
2992
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: (0, import_design_system7.cn)("space-y-5", className), children: CONTENT_ROLES.map((role) => {
2556
2993
  const inRole = list.filter(
2557
2994
  (token) => store.registry.getEntityInfo(token).contentRole === role.id
2558
2995
  );
2559
2996
  if (inRole.length === 0) return null;
2560
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("section", { children: [
2561
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "mb-2.5 flex items-baseline gap-3 pl-1.5", children: [
2562
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2", children: [
2563
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2997
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("section", { children: [
2998
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "mb-2.5 flex items-baseline gap-3 pl-1.5", children: [
2999
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-2", children: [
3000
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2564
3001
  "span",
2565
3002
  {
2566
3003
  className: (0, import_design_system7.cn)("h-2.5 w-2.5 rounded-full", role.accentBar)
2567
3004
  }
2568
3005
  ),
2569
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h3", { className: "text-sm font-semibold text-foreground", children: role.title })
3006
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h3", { className: "text-sm font-semibold text-foreground", children: role.title })
2570
3007
  ] }),
2571
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "hidden text-xs text-muted-foreground sm:block", children: role.tagline })
3008
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "hidden text-xs text-muted-foreground sm:block", children: role.tagline })
2572
3009
  ] }),
2573
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-3", children: inRole.map((token) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AssociationCard, { token }, token)) })
3010
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-3", children: inRole.map((token) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AssociationCard, { token }, token)) })
2574
3011
  ] }, role.id);
2575
3012
  }) });
2576
3013
  }
2577
3014
 
2578
3015
  // src/react/components/AssociationList.tsx
2579
- var import_react16 = require("react");
3016
+ var import_react17 = require("react");
2580
3017
  var import_design_system8 = require("@ai-matrx/design-system");
2581
- var import_jsx_runtime11 = require("react/jsx-runtime");
3018
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2582
3019
  function useContainerLinksAdapter(container, tokens) {
2583
3020
  const store = useAssociationsStore();
2584
3021
  const links = useContainerLinks({
@@ -2616,9 +3053,9 @@ function AssociationList(props) {
2616
3053
  const adapter = props.adapter ?? defaultAdapter;
2617
3054
  const variant = props.variant ?? "full";
2618
3055
  const tokenFilter = props.tokens ?? null;
2619
- const [pickerToken, setPickerToken] = (0, import_react16.useState)(null);
2620
- const [showUniversal, setShowUniversal] = (0, import_react16.useState)(false);
2621
- const [removingKeys, setRemovingKeys] = (0, import_react16.useState)(/* @__PURE__ */ new Set());
3056
+ const [pickerToken, setPickerToken] = (0, import_react17.useState)(null);
3057
+ const [showUniversal, setShowUniversal] = (0, import_react17.useState)(false);
3058
+ const [removingKeys, setRemovingKeys] = (0, import_react17.useState)(/* @__PURE__ */ new Set());
2622
3059
  const rows = tokenFilter ? adapter.rows.filter((r) => tokenFilter.includes(r.token)) : adapter.rows;
2623
3060
  const visibleRows = rows.filter((r) => !removingKeys.has(r.key));
2624
3061
  const { titleFor } = useEntityTitles(
@@ -2656,15 +3093,15 @@ function AssociationList(props) {
2656
3093
  const grouped = groupRows(store, visibleRows);
2657
3094
  const isLoading = adapter.status === "loading" || adapter.status === "idle";
2658
3095
  const attachableTokens = tokenFilter ?? store.registry.curatedTokens();
2659
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: (0, import_design_system8.cn)("flex flex-col gap-2 text-foreground", props.className), children: [
2660
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center justify-between gap-2 px-1", children: [
2661
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("h3", { className: "flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: [
2662
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Link2Icon, { className: "h-3.5 w-3.5" }),
3096
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: (0, import_design_system8.cn)("flex flex-col gap-2 text-foreground", props.className), children: [
3097
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center justify-between gap-2 px-1", children: [
3098
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("h3", { className: "flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: [
3099
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Link2Icon, { className: "h-3.5 w-3.5" }),
2663
3100
  "Resources",
2664
- visibleRows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "rounded bg-muted px-1 text-[10px] font-medium text-muted-foreground", children: visibleRows.length })
3101
+ visibleRows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "rounded bg-muted px-1 text-[10px] font-medium text-muted-foreground", children: visibleRows.length })
2665
3102
  ] }),
2666
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-1.5", children: [
2667
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3103
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-1.5", children: [
3104
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2668
3105
  "button",
2669
3106
  {
2670
3107
  type: "button",
@@ -2674,19 +3111,19 @@ function AssociationList(props) {
2674
3111
  showUniversal ? "bg-accent text-foreground" : "text-muted-foreground hover:bg-accent hover:text-foreground"
2675
3112
  ),
2676
3113
  children: [
2677
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PlusIcon, { className: "h-3 w-3" }),
3114
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PlusIcon, { className: "h-3 w-3" }),
2678
3115
  "Add"
2679
3116
  ]
2680
3117
  }
2681
3118
  ),
2682
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3119
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2683
3120
  "button",
2684
3121
  {
2685
3122
  type: "button",
2686
3123
  onClick: () => void adapter.reload(),
2687
3124
  title: "Refresh",
2688
3125
  className: "text-muted-foreground/60 transition-colors hover:text-foreground",
2689
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3126
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2690
3127
  RefreshIcon,
2691
3128
  {
2692
3129
  className: (0, import_design_system8.cn)(
@@ -2699,7 +3136,7 @@ function AssociationList(props) {
2699
3136
  )
2700
3137
  ] })
2701
3138
  ] }),
2702
- showUniversal && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rounded-md border border-border/60 bg-muted/30 p-2 max-h-80 flex flex-col", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3139
+ showUniversal && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rounded-md border border-border/60 bg-muted/30 p-2 max-h-80 flex flex-col", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2703
3140
  UniversalAssociationPicker,
2704
3141
  {
2705
3142
  tokens: attachableTokens,
@@ -2709,9 +3146,9 @@ function AssociationList(props) {
2709
3146
  onDetach: (token, id) => adapter.detach(token, id)
2710
3147
  }
2711
3148
  ) }),
2712
- adapter.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center justify-between gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-2.5 py-2", children: [
2713
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "truncate text-[11px] text-destructive", children: adapter.error || "Failed to load resources" }),
2714
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3149
+ adapter.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center justify-between gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-2.5 py-2", children: [
3150
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "truncate text-[11px] text-destructive", children: adapter.error || "Failed to load resources" }),
3151
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2715
3152
  "button",
2716
3153
  {
2717
3154
  type: "button",
@@ -2721,17 +3158,17 @@ function AssociationList(props) {
2721
3158
  }
2722
3159
  )
2723
3160
  ] }),
2724
- adapter.status !== "error" && isLoading && visibleRows.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "space-y-1.5 px-1", children: [
2725
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_design_system8.Skeleton, { className: "h-3 w-20" }),
2726
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_design_system8.Skeleton, { className: "h-7 w-full rounded-md" }),
2727
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_design_system8.Skeleton, { className: "h-7 w-3/4 rounded-md" })
3161
+ adapter.status !== "error" && isLoading && visibleRows.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "space-y-1.5 px-1", children: [
3162
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_design_system8.Skeleton, { className: "h-3 w-20" }),
3163
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_design_system8.Skeleton, { className: "h-7 w-full rounded-md" }),
3164
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_design_system8.Skeleton, { className: "h-7 w-3/4 rounded-md" })
2728
3165
  ] }),
2729
- adapter.status === "ready" && visibleRows.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "px-1 py-1 text-[11px] text-muted-foreground", children: "Nothing attached yet \u2014 use Add to link anything." }),
2730
- visibleRows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "space-y-2.5", children: grouped.map(({ role, tokens: tokenGroups }) => {
3166
+ adapter.status === "ready" && visibleRows.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "px-1 py-1 text-[11px] text-muted-foreground", children: "Nothing attached yet \u2014 use Add to link anything." }),
3167
+ visibleRows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "space-y-2.5", children: grouped.map(({ role, tokens: tokenGroups }) => {
2731
3168
  const roleMeta = getContentRoleMeta(role);
2732
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "space-y-1.5", children: [
2733
- variant === "full" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-1.5 px-1", children: [
2734
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3169
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "space-y-1.5", children: [
3170
+ variant === "full" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-1.5 px-1", children: [
3171
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2735
3172
  "span",
2736
3173
  {
2737
3174
  className: (0, import_design_system8.cn)(
@@ -2740,7 +3177,7 @@ function AssociationList(props) {
2740
3177
  )
2741
3178
  }
2742
3179
  ),
2743
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3180
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2744
3181
  "p",
2745
3182
  {
2746
3183
  className: (0, import_design_system8.cn)(
@@ -2753,28 +3190,28 @@ function AssociationList(props) {
2753
3190
  ] }),
2754
3191
  tokenGroups.map(({ info, token, rows: tokenRows }) => {
2755
3192
  const TokenIcon = info?.Icon ?? null;
2756
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "space-y-0.5", children: [
2757
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center justify-between gap-1 px-1", children: [
2758
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "flex items-center gap-1 text-[10px] font-medium text-muted-foreground/80", children: [
2759
- TokenIcon ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(TokenIcon, { className: "h-3 w-3" }) : null,
3193
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "space-y-0.5", children: [
3194
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center justify-between gap-1 px-1", children: [
3195
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "flex items-center gap-1 text-[10px] font-medium text-muted-foreground/80", children: [
3196
+ TokenIcon ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TokenIcon, { className: "h-3 w-3" }) : null,
2760
3197
  info?.labelPlural ?? titleize(token),
2761
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-muted-foreground/50", children: tokenRows.length })
3198
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-muted-foreground/50", children: tokenRows.length })
2762
3199
  ] }),
2763
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-1", children: [
3200
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-1", children: [
2764
3201
  props.renderSectionActions && info ? props.renderSectionActions(info.token) : null,
2765
- info?.canListCandidates && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3202
+ info?.canListCandidates && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2766
3203
  "button",
2767
3204
  {
2768
3205
  type: "button",
2769
3206
  onClick: () => setPickerToken(info.token),
2770
3207
  title: `Add ${info.labelPlural}`,
2771
3208
  className: "rounded p-0.5 text-muted-foreground/60 transition-colors hover:bg-accent hover:text-foreground",
2772
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PlusIcon, { className: "h-3 w-3" })
3209
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PlusIcon, { className: "h-3 w-3" })
2773
3210
  }
2774
3211
  )
2775
3212
  ] })
2776
3213
  ] }),
2777
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("ul", { className: "space-y-0.5", children: tokenRows.map((row) => {
3214
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("ul", { className: "space-y-0.5", children: tokenRows.map((row) => {
2778
3215
  const busy = removingKeys.has(row.key);
2779
3216
  const custom = props.renderRow?.(row, {
2780
3217
  title: titleFor({
@@ -2787,9 +3224,9 @@ function AssociationList(props) {
2787
3224
  onOpen: () => openRow(row)
2788
3225
  });
2789
3226
  if (custom != null) {
2790
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("li", { children: custom }, row.key);
3227
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("li", { children: custom }, row.key);
2791
3228
  }
2792
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3229
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2793
3230
  "div",
2794
3231
  {
2795
3232
  className: (0, import_design_system8.cn)(
@@ -2797,7 +3234,7 @@ function AssociationList(props) {
2797
3234
  busy && "opacity-50"
2798
3235
  ),
2799
3236
  children: [
2800
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3237
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2801
3238
  DoorRef,
2802
3239
  {
2803
3240
  token: row.token,
@@ -2813,9 +3250,9 @@ function AssociationList(props) {
2813
3250
  className: "min-w-0 flex-1 text-foreground"
2814
3251
  }
2815
3252
  ),
2816
- row.originRefs && row.originRefs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "flex shrink-0 items-center gap-1 text-[10px] text-muted-foreground/60", children: [
3253
+ row.originRefs && row.originRefs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "flex shrink-0 items-center gap-1 text-[10px] text-muted-foreground/60", children: [
2817
3254
  "via",
2818
- row.originRefs.map((origin) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3255
+ row.originRefs.map((origin) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2819
3256
  DoorRef,
2820
3257
  {
2821
3258
  token: origin.token,
@@ -2827,8 +3264,8 @@ function AssociationList(props) {
2827
3264
  },
2828
3265
  `${origin.token}:${origin.id}`
2829
3266
  ))
2830
- ] }) : row.originNote ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "shrink-0 text-[10px] text-muted-foreground/60", children: row.originNote }) : null,
2831
- adapter.setPinned && info && row.removable && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3267
+ ] }) : row.originNote ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "shrink-0 text-[10px] text-muted-foreground/60", children: row.originNote }) : null,
3268
+ adapter.setPinned && info && row.removable && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2832
3269
  "button",
2833
3270
  {
2834
3271
  type: "button",
@@ -2843,7 +3280,7 @@ function AssociationList(props) {
2843
3280
  "shrink-0 rounded p-0.5 transition-colors hover:!text-foreground",
2844
3281
  row.pinned ? "text-primary" : "text-muted-foreground/0 group-hover:text-muted-foreground"
2845
3282
  ),
2846
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3283
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2847
3284
  PinIcon,
2848
3285
  {
2849
3286
  className: (0, import_design_system8.cn)(
@@ -2854,7 +3291,7 @@ function AssociationList(props) {
2854
3291
  )
2855
3292
  }
2856
3293
  ),
2857
- row.removable && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3294
+ row.removable && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2858
3295
  "button",
2859
3296
  {
2860
3297
  type: "button",
@@ -2863,7 +3300,7 @@ function AssociationList(props) {
2863
3300
  title: "Detach",
2864
3301
  "aria-label": "Detach",
2865
3302
  className: "shrink-0 rounded p-0.5 text-muted-foreground/0 transition-colors group-hover:text-muted-foreground hover:!text-destructive disabled:opacity-50",
2866
- children: busy ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SpinnerIcon, { className: "h-3 w-3 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(XIcon, { className: "h-3 w-3" })
3303
+ children: busy ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SpinnerIcon, { className: "h-3 w-3 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(XIcon, { className: "h-3 w-3" })
2867
3304
  }
2868
3305
  )
2869
3306
  ]
@@ -2874,7 +3311,7 @@ function AssociationList(props) {
2874
3311
  })
2875
3312
  ] }, role);
2876
3313
  }) }),
2877
- pickerToken && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3314
+ pickerToken && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2878
3315
  AssociationPicker,
2879
3316
  {
2880
3317
  open: true,
@@ -2930,9 +3367,9 @@ function titleize(token) {
2930
3367
  }
2931
3368
 
2932
3369
  // src/react/components/AssociationCaptureToolbar.tsx
2933
- var import_react17 = require("react");
3370
+ var import_react18 = require("react");
2934
3371
  var import_design_system9 = require("@ai-matrx/design-system");
2935
- var import_jsx_runtime12 = require("react/jsx-runtime");
3372
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2936
3373
  function AssociationCaptureToolbar({
2937
3374
  attach,
2938
3375
  uploadFolderPath,
@@ -2950,21 +3387,43 @@ function AssociationCaptureToolbar({
2950
3387
  const store = useAssociationsStore();
2951
3388
  const { capture } = useAssociationsUiPorts();
2952
3389
  const notifier = useNotifier();
2953
- const fileInputRef = (0, import_react17.useRef)(null);
2954
- const [isUploading, setIsUploading] = (0, import_react17.useState)(false);
2955
- const [isDragOver, setIsDragOver] = (0, import_react17.useState)(false);
2956
- const [isPicking, setIsPicking] = (0, import_react17.useState)(false);
2957
- const [namingDoc, setNamingDoc] = (0, import_react17.useState)(false);
2958
- const [docName, setDocName] = (0, import_react17.useState)("");
2959
- const [creatingDoc, setCreatingDoc] = (0, import_react17.useState)(false);
3390
+ const fileInputRef = (0, import_react18.useRef)(null);
3391
+ const [isUploading, setIsUploading] = (0, import_react18.useState)(false);
3392
+ const [isDragOver, setIsDragOver] = (0, import_react18.useState)(false);
3393
+ const [isPicking, setIsPicking] = (0, import_react18.useState)(false);
3394
+ const [namingDoc, setNamingDoc] = (0, import_react18.useState)(false);
3395
+ const [docName, setDocName] = (0, import_react18.useState)("");
3396
+ const [creatingDoc, setCreatingDoc] = (0, import_react18.useState)(false);
3397
+ const hostRequestUpload = capture?.requestUpload;
3398
+ const hostUploadFile = capture?.uploadFile;
3399
+ const requestUploadPath = hostRequestUpload ? (opts) => hostRequestUpload(opts) : hostUploadFile ? async (opts) => {
3400
+ const uploaded = [];
3401
+ const failed = [];
3402
+ for (const file of opts.files) {
3403
+ try {
3404
+ const result = await hostUploadFile(file, {
3405
+ folderPath: opts.folderPath,
3406
+ ...opts.visibility !== void 0 ? { visibility: opts.visibility } : {}
3407
+ });
3408
+ if (result.ok) uploaded.push(result.id);
3409
+ else failed.push({ name: file.name, error: result.error });
3410
+ } catch (err) {
3411
+ failed.push({
3412
+ name: file.name,
3413
+ error: err instanceof Error && err.message ? err.message : "Upload failed"
3414
+ });
3415
+ }
3416
+ }
3417
+ return { uploaded, aliased: [], failed };
3418
+ } : void 0;
2960
3419
  const actions = {
2961
- upload: (showActions?.upload ?? true) && Boolean(capture?.requestUpload),
3420
+ upload: (showActions?.upload ?? true) && Boolean(requestUploadPath),
2962
3421
  addFile: (showActions?.addFile ?? true) && Boolean(capture?.openFilePicker),
2963
3422
  newDocument: (showActions?.newDocument ?? true) && Boolean(capture?.createDataTable)
2964
3423
  };
2965
- const canDrop = Boolean(capture?.requestUpload);
3424
+ const canDrop = Boolean(requestUploadPath);
2966
3425
  const uploadAndAttach = async (files) => {
2967
- const requestUpload = capture?.requestUpload;
3426
+ const requestUpload = requestUploadPath;
2968
3427
  if (files.length === 0 || !requestUpload) return;
2969
3428
  setIsUploading(true);
2970
3429
  const watchdog = setTimeout(() => {
@@ -3122,7 +3581,7 @@ function AssociationCaptureToolbar({
3122
3581
  }
3123
3582
  };
3124
3583
  const anyBuiltIn = actions.upload || actions.addFile || actions.newDocument;
3125
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3584
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3126
3585
  "div",
3127
3586
  {
3128
3587
  className: (0, import_design_system9.cn)(
@@ -3143,11 +3602,11 @@ function AssociationCaptureToolbar({
3143
3602
  },
3144
3603
  onDrop: handleDrop,
3145
3604
  children: [
3146
- isDragOver ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "pointer-events-none absolute inset-0 z-10 grid place-items-center bg-background/70", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "flex items-center gap-2 rounded-md border border-primary/40 bg-card px-3 py-1.5 text-xs font-medium text-foreground", children: [
3147
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(UploadIcon, { className: "size-3.5 text-primary" }),
3605
+ isDragOver ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "pointer-events-none absolute inset-0 z-10 grid place-items-center bg-background/70", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "flex items-center gap-2 rounded-md border border-primary/40 bg-card px-3 py-1.5 text-xs font-medium text-foreground", children: [
3606
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(UploadIcon, { className: "size-3.5 text-primary" }),
3148
3607
  "Drop to upload & attach"
3149
3608
  ] }) }) : null,
3150
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3609
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3151
3610
  "input",
3152
3611
  {
3153
3612
  ref: fileInputRef,
@@ -3157,8 +3616,8 @@ function AssociationCaptureToolbar({
3157
3616
  onChange: handleFilesSelected
3158
3617
  }
3159
3618
  ),
3160
- showToolbar && (anyBuiltIn || extraActions) ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex flex-wrap items-center gap-0.5 border-b border-border/60 px-1.5 py-1", children: namingDoc ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-1 items-center gap-1.5 py-0.5", children: [
3161
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3619
+ showToolbar && (anyBuiltIn || extraActions) ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex flex-wrap items-center gap-0.5 border-b border-border/60 px-1.5 py-1", children: namingDoc ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-1 items-center gap-1.5 py-0.5", children: [
3620
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3162
3621
  import_design_system9.Input,
3163
3622
  {
3164
3623
  autoFocus: true,
@@ -3177,17 +3636,17 @@ function AssociationCaptureToolbar({
3177
3636
  style: { fontSize: 16 }
3178
3637
  }
3179
3638
  ),
3180
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3639
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3181
3640
  "button",
3182
3641
  {
3183
3642
  type: "button",
3184
3643
  disabled: creatingDoc || !docName.trim(),
3185
3644
  onClick: () => void handleCreateDoc(),
3186
3645
  className: "flex h-7 items-center rounded-md bg-primary px-2 text-[11px] font-medium text-primary-foreground disabled:opacity-50",
3187
- children: creatingDoc ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SpinnerIcon, { className: "size-3.5 animate-spin" }) : "Create"
3646
+ children: creatingDoc ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SpinnerIcon, { className: "size-3.5 animate-spin" }) : "Create"
3188
3647
  }
3189
3648
  ),
3190
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3649
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3191
3650
  "button",
3192
3651
  {
3193
3652
  type: "button",
@@ -3200,8 +3659,8 @@ function AssociationCaptureToolbar({
3200
3659
  children: "Cancel"
3201
3660
  }
3202
3661
  )
3203
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
3204
- actions.upload ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3662
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
3663
+ actions.upload ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3205
3664
  CaptureToolbarAction,
3206
3665
  {
3207
3666
  icon: isUploading ? SpinnerIcon : UploadIcon,
@@ -3211,7 +3670,7 @@ function AssociationCaptureToolbar({
3211
3670
  disabled: isPicking || isUploading
3212
3671
  }
3213
3672
  ) : null,
3214
- actions.addFile ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3673
+ actions.addFile ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3215
3674
  CaptureToolbarAction,
3216
3675
  {
3217
3676
  icon: isPicking ? SpinnerIcon : FolderOpenIcon,
@@ -3221,8 +3680,8 @@ function AssociationCaptureToolbar({
3221
3680
  disabled: isPicking || isUploading
3222
3681
  }
3223
3682
  ) : null,
3224
- (actions.upload || actions.addFile) && actions.newDocument ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "mx-1 h-4 w-px bg-border" }) : null,
3225
- actions.newDocument ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3683
+ (actions.upload || actions.addFile) && actions.newDocument ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "mx-1 h-4 w-px bg-border" }) : null,
3684
+ actions.newDocument ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3226
3685
  CaptureToolbarAction,
3227
3686
  {
3228
3687
  icon: creatingDoc ? SpinnerIcon : PlusIcon,
@@ -3246,7 +3705,7 @@ function CaptureToolbarAction({
3246
3705
  disabled,
3247
3706
  spinning
3248
3707
  }) {
3249
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3708
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3250
3709
  import_design_system9.Button,
3251
3710
  {
3252
3711
  type: "button",
@@ -3256,17 +3715,147 @@ function CaptureToolbarAction({
3256
3715
  disabled,
3257
3716
  className: "h-7 gap-1 px-2 text-[11px] text-muted-foreground hover:text-foreground",
3258
3717
  children: [
3259
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, { className: (0, import_design_system9.cn)("size-3.5", spinning && "animate-spin") }),
3718
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Icon, { className: (0, import_design_system9.cn)("size-3.5", spinning && "animate-spin") }),
3260
3719
  label
3261
3720
  ]
3262
3721
  }
3263
3722
  );
3264
3723
  }
3265
3724
 
3725
+ // src/react/lazyPorts.tsx
3726
+ var import_react19 = require("react");
3727
+ var import_jsx_runtime14 = require("react/jsx-runtime");
3728
+ function lazyComponent(load) {
3729
+ return (0, import_react19.lazy)(async () => {
3730
+ const loaded = await load();
3731
+ return typeof loaded === "function" ? { default: loaded } : loaded;
3732
+ });
3733
+ }
3734
+ function lazyWindowShell(load) {
3735
+ const Loaded = lazyComponent(load);
3736
+ return function LazyWindowShell(props) {
3737
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react19.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Loaded, { ...props }) });
3738
+ };
3739
+ }
3740
+ function lazyPickerOverride(load) {
3741
+ const Loaded = lazyComponent(load);
3742
+ return function LazyPickerOverride(props) {
3743
+ if (!props.open) return null;
3744
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react19.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Loaded, { ...props }) });
3745
+ };
3746
+ }
3747
+
3748
+ // src/react/hooks/useAssociationPickerBridge.ts
3749
+ var import_react20 = require("react");
3750
+ function useAssociationPickerBridge(props, options = {}) {
3751
+ const store = useAssociationsStore();
3752
+ const notifier = useNotifier();
3753
+ const [busy, setBusy] = (0, import_react20.useState)(false);
3754
+ const { attachedIds, onAttach, onDetach, token } = props;
3755
+ const {
3756
+ createdLocationLabel,
3757
+ openCreatedLocation,
3758
+ itemNoun = "item"
3759
+ } = options;
3760
+ const attachMany = (0, import_react20.useCallback)(
3761
+ async (items) => {
3762
+ if (items.length === 0) return;
3763
+ setBusy(true);
3764
+ const failed = [];
3765
+ try {
3766
+ for (const item of items) {
3767
+ try {
3768
+ const result = await onAttach(item.id, item.name);
3769
+ if (!result.ok) {
3770
+ failed.push({ name: item.name, error: result.error });
3771
+ }
3772
+ } catch (error) {
3773
+ store.errorSink({
3774
+ code: "picker_attach_threw",
3775
+ message: `[associations] attaching ${itemNoun} "${item.name}" threw`,
3776
+ context: { token, id: item.id, detail: error }
3777
+ });
3778
+ failed.push({
3779
+ name: item.name,
3780
+ error: error instanceof Error ? error.message : void 0
3781
+ });
3782
+ }
3783
+ }
3784
+ const attached = items.length - failed.length;
3785
+ if (attached > 0) {
3786
+ notifier.success(
3787
+ attached === 1 ? `Attached 1 ${itemNoun}` : `Attached ${attached} ${itemNoun}s`
3788
+ );
3789
+ }
3790
+ if (failed.length > 0) {
3791
+ const first = failed[0];
3792
+ const detail = first?.error ? ` (${first.error})` : "";
3793
+ notifier.error(
3794
+ failed.length === 1 ? `Couldn't attach "${first?.name ?? itemNoun}"${detail}` : `Couldn't attach ${failed.length} ${itemNoun}s${detail}`,
3795
+ {
3796
+ ...createdLocationLabel ? {
3797
+ description: `The ${failed.length === 1 ? itemNoun + " is" : itemNoun + "s are"} safe in ${createdLocationLabel}. You can retry the association here.`
3798
+ } : {},
3799
+ ...openCreatedLocation ? { action: openCreatedLocation } : {}
3800
+ }
3801
+ );
3802
+ store.errorSink({
3803
+ code: "picker_attach_partial",
3804
+ message: `[associations] ${failed.length} of ${items.length} ${itemNoun}s were not attached`,
3805
+ context: { token, failed }
3806
+ });
3807
+ }
3808
+ } finally {
3809
+ setBusy(false);
3810
+ }
3811
+ },
3812
+ [
3813
+ createdLocationLabel,
3814
+ itemNoun,
3815
+ notifier,
3816
+ onAttach,
3817
+ openCreatedLocation,
3818
+ store,
3819
+ token
3820
+ ]
3821
+ );
3822
+ const toggle = (0, import_react20.useCallback)(
3823
+ async (item) => {
3824
+ setBusy(true);
3825
+ try {
3826
+ const detaching = attachedIds.has(item.id);
3827
+ const result = detaching ? await onDetach(item.id) : await onAttach(item.id, item.name);
3828
+ if (!result.ok) {
3829
+ const verb = detaching ? "detach" : "attach";
3830
+ notifier.error(
3831
+ `Couldn't ${verb} "${item.name}"` + (result.error ? `: ${result.error}` : "")
3832
+ );
3833
+ store.errorSink({
3834
+ code: detaching ? "picker_detach_failed" : "picker_attach_failed",
3835
+ message: `[associations] ${verb} refused for "${item.name}"`,
3836
+ context: { token, id: item.id, error: result.error }
3837
+ });
3838
+ }
3839
+ } catch (error) {
3840
+ store.errorSink({
3841
+ code: "picker_toggle_threw",
3842
+ message: `[associations] toggling ${itemNoun} "${item.name}" threw`,
3843
+ context: { token, id: item.id, detail: error }
3844
+ });
3845
+ notifier.error(`Couldn't update "${item.name}"`);
3846
+ } finally {
3847
+ setBusy(false);
3848
+ }
3849
+ },
3850
+ [attachedIds, itemNoun, notifier, onAttach, onDetach, store, token]
3851
+ );
3852
+ return { attachMany, toggle, busy };
3853
+ }
3854
+
3266
3855
  // src/react/components/AssociationEntitySelect.tsx
3267
- var import_react18 = require("react");
3856
+ var import_react21 = require("react");
3268
3857
  var import_design_system10 = require("@ai-matrx/design-system");
3269
- var import_jsx_runtime13 = require("react/jsx-runtime");
3858
+ var import_jsx_runtime15 = require("react/jsx-runtime");
3270
3859
  function AssociationEntitySelect({
3271
3860
  token,
3272
3861
  adapter,
@@ -3280,19 +3869,17 @@ function AssociationEntitySelect({
3280
3869
  createSlot
3281
3870
  }) {
3282
3871
  const store = useAssociationsStore();
3283
- const { entityDoors } = useAssociationsUiPorts();
3284
3872
  const notifier = useNotifier();
3285
3873
  const info = store.registry.getEntityInfo(token);
3286
3874
  const InfoIcon = info.Icon ?? DefaultEntityIcon;
3287
- const DoorControls = entityDoors?.DoorControls;
3288
3875
  const { items, activeId } = adapter;
3289
3876
  const active = items.find((i) => i.id === activeId) ?? null;
3290
3877
  const activeIndex = active ? items.indexOf(active) : -1;
3291
- const [open, setOpen] = (0, import_react18.useState)(false);
3292
- const [query, setQuery] = (0, import_react18.useState)("");
3293
- const [creating, setCreating] = (0, import_react18.useState)(false);
3294
- const [draftName, setDraftName] = (0, import_react18.useState)("");
3295
- const [busy, setBusy] = (0, import_react18.useState)(false);
3878
+ const [open, setOpen] = (0, import_react21.useState)(false);
3879
+ const [query, setQuery] = (0, import_react21.useState)("");
3880
+ const [creating, setCreating] = (0, import_react21.useState)(false);
3881
+ const [draftName, setDraftName] = (0, import_react21.useState)("");
3882
+ const [busy, setBusy] = (0, import_react21.useState)(false);
3296
3883
  const entityLabel = info.label.toLowerCase();
3297
3884
  const close = () => {
3298
3885
  setOpen(false);
@@ -3309,7 +3896,7 @@ function AssociationEntitySelect({
3309
3896
  if (id) close();
3310
3897
  else notifier.error(`Couldn't create the ${entityLabel}`);
3311
3898
  };
3312
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3899
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3313
3900
  "div",
3314
3901
  {
3315
3902
  className: (0, import_design_system10.cn)(
@@ -3317,7 +3904,7 @@ function AssociationEntitySelect({
3317
3904
  className
3318
3905
  ),
3319
3906
  children: [
3320
- showIcon ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3907
+ showIcon ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3321
3908
  InfoIcon,
3322
3909
  {
3323
3910
  className: (0, import_design_system10.cn)(
@@ -3327,7 +3914,7 @@ function AssociationEntitySelect({
3327
3914
  "aria-hidden": true
3328
3915
  }
3329
3916
  ) : null,
3330
- active ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3917
+ active ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3331
3918
  import_design_system10.EditableLabel,
3332
3919
  {
3333
3920
  value: active.title,
@@ -3346,8 +3933,8 @@ function AssociationEntitySelect({
3346
3933
  inputClassName: "text-xs font-medium"
3347
3934
  }
3348
3935
  ) : null,
3349
- active && DoorControls ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DoorControls, { token, id: active.id, name: active.title }) : null,
3350
- !active ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3936
+ active ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DoorControlsSlot, { token, id: active.id, name: active.title }) : null,
3937
+ !active ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3351
3938
  "span",
3352
3939
  {
3353
3940
  className: (0, import_design_system10.cn)(
@@ -3357,13 +3944,13 @@ function AssociationEntitySelect({
3357
3944
  children: adapter.loading ? "\u2026" : emptyLabel ?? info.labelPlural
3358
3945
  }
3359
3946
  ) : null,
3360
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3947
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3361
3948
  import_design_system10.Popover,
3362
3949
  {
3363
3950
  open,
3364
3951
  onOpenChange: (next) => next ? setOpen(true) : close(),
3365
3952
  children: [
3366
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_design_system10.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3953
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_design_system10.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3367
3954
  "button",
3368
3955
  {
3369
3956
  type: "button",
@@ -3371,17 +3958,17 @@ function AssociationEntitySelect({
3371
3958
  title: `Switch or add ${info.labelPlural.toLowerCase()}`,
3372
3959
  "aria-label": `Switch or add ${info.labelPlural.toLowerCase()}`,
3373
3960
  children: [
3374
- items.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "tabular-nums", children: [
3961
+ items.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "tabular-nums", children: [
3375
3962
  activeIndex >= 0 ? activeIndex + 1 : "\u2014",
3376
3963
  "/",
3377
3964
  items.length
3378
3965
  ] }) : null,
3379
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChevronDownIcon, { className: "size-3 opacity-60" })
3966
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ChevronDownIcon, { className: "size-3 opacity-60" })
3380
3967
  ]
3381
3968
  }
3382
3969
  ) }),
3383
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_design_system10.PopoverContent, { className: "w-60 p-0", align, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_design_system10.Command, { children: [
3384
- items.length > 5 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3970
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_design_system10.PopoverContent, { className: "w-60 p-0", align, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_design_system10.Command, { children: [
3971
+ items.length > 5 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3385
3972
  import_design_system10.CommandInput,
3386
3973
  {
3387
3974
  value: query,
@@ -3389,9 +3976,9 @@ function AssociationEntitySelect({
3389
3976
  placeholder: `Search ${info.labelPlural.toLowerCase()}\u2026`
3390
3977
  }
3391
3978
  ) : null,
3392
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_design_system10.CommandList, { children: [
3393
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_design_system10.CommandEmpty, { children: "No match." }),
3394
- items.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_design_system10.CommandGroup, { children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3979
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_design_system10.CommandList, { children: [
3980
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_design_system10.CommandEmpty, { children: "No match." }),
3981
+ items.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_design_system10.CommandGroup, { children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3395
3982
  import_design_system10.CommandItem,
3396
3983
  {
3397
3984
  value: `${item.title} ${item.id}`,
@@ -3402,7 +3989,7 @@ function AssociationEntitySelect({
3402
3989
  },
3403
3990
  className: "group/entity-ref group gap-2",
3404
3991
  children: [
3405
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3992
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3406
3993
  CheckIcon,
3407
3994
  {
3408
3995
  className: (0, import_design_system10.cn)(
@@ -3411,8 +3998,8 @@ function AssociationEntitySelect({
3411
3998
  )
3412
3999
  }
3413
4000
  ),
3414
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "min-w-0 flex-1 truncate", children: item.title }),
3415
- DoorControls ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4001
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "min-w-0 flex-1 truncate", children: item.title }),
4002
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3416
4003
  "span",
3417
4004
  {
3418
4005
  className: "inline-flex shrink-0 items-center",
@@ -3420,8 +4007,8 @@ function AssociationEntitySelect({
3420
4007
  e.preventDefault();
3421
4008
  e.stopPropagation();
3422
4009
  },
3423
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3424
- DoorControls,
4010
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4011
+ DoorControlsSlot,
3425
4012
  {
3426
4013
  token,
3427
4014
  id: item.id,
@@ -3429,8 +4016,8 @@ function AssociationEntitySelect({
3429
4016
  }
3430
4017
  )
3431
4018
  }
3432
- ) : null,
3433
- adapter.detach && item.id !== activeId ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4019
+ ),
4020
+ adapter.detach && item.id !== activeId ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3434
4021
  "button",
3435
4022
  {
3436
4023
  type: "button",
@@ -3445,7 +4032,7 @@ function AssociationEntitySelect({
3445
4032
  className: "grid size-5 shrink-0 place-items-center rounded text-muted-foreground opacity-0 transition-opacity hover:bg-accent hover:text-destructive group-hover:opacity-100",
3446
4033
  title: `Remove this ${entityLabel} from here (does not delete it)`,
3447
4034
  "aria-label": `Remove ${item.title}`,
3448
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(XIcon, { className: "size-3" })
4035
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(XIcon, { className: "size-3" })
3449
4036
  }
3450
4037
  ) : null
3451
4038
  ]
@@ -3453,9 +4040,9 @@ function AssociationEntitySelect({
3453
4040
  item.id
3454
4041
  )) }) : null
3455
4042
  ] }),
3456
- createSlot !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "border-t border-border p-1", children: typeof createSlot === "function" ? createSlot(close) : createSlot }) : null,
3457
- createSlot === void 0 && adapter.createAndAttach ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "border-t border-border p-1", children: creating ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-1 px-1 py-0.5", children: [
3458
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4043
+ createSlot !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "border-t border-border p-1", children: typeof createSlot === "function" ? createSlot(close) : createSlot }) : null,
4044
+ createSlot === void 0 && adapter.createAndAttach ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "border-t border-border p-1", children: creating ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-1 px-1 py-0.5", children: [
4045
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3459
4046
  "input",
3460
4047
  {
3461
4048
  autoFocus: true,
@@ -3479,17 +4066,17 @@ function AssociationEntitySelect({
3479
4066
  "aria-label": `New ${entityLabel} name`
3480
4067
  }
3481
4068
  ),
3482
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4069
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3483
4070
  "button",
3484
4071
  {
3485
4072
  type: "button",
3486
4073
  disabled: busy || !draftName.trim(),
3487
4074
  onClick: () => void create(draftName),
3488
4075
  className: "inline-flex h-6 shrink-0 items-center rounded-md px-1.5 text-[10px] font-medium text-primary transition-colors hover:bg-accent disabled:opacity-50",
3489
- children: busy ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SpinnerIcon, { className: "size-3 animate-spin" }) : "Create"
4076
+ children: busy ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SpinnerIcon, { className: "size-3 animate-spin" }) : "Create"
3490
4077
  }
3491
4078
  )
3492
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
4079
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3493
4080
  "button",
3494
4081
  {
3495
4082
  type: "button",
@@ -3500,7 +4087,7 @@ function AssociationEntitySelect({
3500
4087
  },
3501
4088
  className: "flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-xs text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
3502
4089
  children: [
3503
- busy ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SpinnerIcon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PlusIcon, { className: "size-3.5" }),
4090
+ busy ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SpinnerIcon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PlusIcon, { className: "size-3.5" }),
3504
4091
  query.trim() ? `Create "${query.trim()}"` : `New ${info.label}`
3505
4092
  ]
3506
4093
  }
@@ -3515,7 +4102,7 @@ function AssociationEntitySelect({
3515
4102
  }
3516
4103
 
3517
4104
  // src/react/components/CategorySelect.tsx
3518
- var import_react19 = require("react");
4105
+ var import_react22 = require("react");
3519
4106
  var import_design_system11 = require("@ai-matrx/design-system");
3520
4107
 
3521
4108
  // src/core/categoryHierarchy.ts
@@ -3583,7 +4170,7 @@ function buildCategoryHierarchy(categories) {
3583
4170
  }
3584
4171
 
3585
4172
  // src/react/components/CategorySelect.tsx
3586
- var import_jsx_runtime14 = require("react/jsx-runtime");
4173
+ var import_jsx_runtime16 = require("react/jsx-runtime");
3587
4174
  var NONE = "__none__";
3588
4175
  var ROOT_PARENT = "__root__";
3589
4176
  function plural(noun) {
@@ -3610,7 +4197,7 @@ function CategorySelect({
3610
4197
  reload
3611
4198
  } = useCategories({ dimension });
3612
4199
  const hierarchy = buildCategoryHierarchy(categories);
3613
- const [createParentId, setCreateParentId] = (0, import_react19.useState)(ROOT_PARENT);
4200
+ const [createParentId, setCreateParentId] = (0, import_react22.useState)(ROOT_PARENT);
3614
4201
  const options = [
3615
4202
  ...allowNone ? [{ value: NONE, label: "None", keywords: "none clear empty" }] : [],
3616
4203
  ...hierarchy.hasHierarchy ? hierarchy.items.map(({ category, depth, parent }) => ({
@@ -3619,9 +4206,9 @@ function CategorySelect({
3619
4206
  // The type-ahead matches the parent's name too, so someone who knows
3620
4207
  // the branch but not the leaf still finds it.
3621
4208
  keywords: parent ? parent.name : "",
3622
- render: depth === 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "font-medium", children: category.name }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "flex items-center gap-1.5 pl-3 text-muted-foreground", children: [
3623
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CornerDownRightIcon, { className: "h-3.5 w-3.5 shrink-0" }),
3624
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-foreground", children: category.name })
4209
+ render: depth === 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium", children: category.name }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "flex items-center gap-1.5 pl-3 text-muted-foreground", children: [
4210
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(CornerDownRightIcon, { className: "h-3.5 w-3.5 shrink-0" }),
4211
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-foreground", children: category.name })
3625
4212
  ] })
3626
4213
  })) : categories.map((category) => ({
3627
4214
  value: category.id,
@@ -3668,8 +4255,8 @@ function CategorySelect({
3668
4255
  );
3669
4256
  return result.id;
3670
4257
  };
3671
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
3672
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4258
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { children: [
4259
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3673
4260
  import_design_system11.CreatablePicker,
3674
4261
  {
3675
4262
  value: value ?? (allowNone ? NONE : null),
@@ -3685,18 +4272,18 @@ function CategorySelect({
3685
4272
  emptyLabel: `No ${noun} matches that.`,
3686
4273
  ...allowCreate ? { onCreate: create } : {},
3687
4274
  ...allowCreate && hierarchy.hasHierarchy ? {
3688
- createExtra: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_design_system11.Select, { value: createParentId, onValueChange: setCreateParentId, children: [
3689
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4275
+ createExtra: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_design_system11.Select, { value: createParentId, onValueChange: setCreateParentId, children: [
4276
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3690
4277
  import_design_system11.SelectTrigger,
3691
4278
  {
3692
4279
  className: "h-7 text-xs",
3693
4280
  "aria-label": `Put the new ${noun} under`,
3694
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_design_system11.SelectValue, {})
4281
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system11.SelectValue, {})
3695
4282
  }
3696
4283
  ),
3697
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_design_system11.SelectContent, { children: [
3698
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_design_system11.SelectItem, { value: ROOT_PARENT, className: "text-xs", children: "Top level" }),
3699
- hierarchy.items.filter((item) => item.depth === 0).map((item) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
4284
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_design_system11.SelectContent, { children: [
4285
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system11.SelectItem, { value: ROOT_PARENT, className: "text-xs", children: "Top level" }),
4286
+ hierarchy.items.filter((item) => item.depth === 0).map((item) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
3700
4287
  import_design_system11.SelectItem,
3701
4288
  {
3702
4289
  value: item.category.id,
@@ -3716,20 +4303,20 @@ function CategorySelect({
3716
4303
  selected === null && value !== null ? (
3717
4304
  // A stored id whose row this reader cannot see is not "nothing chosen":
3718
4305
  // saying so beats a blank box that looks like an unsaved field.
3719
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("p", { className: "mt-1 text-xs text-muted-foreground", children: [
4306
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("p", { className: "mt-1 text-xs text-muted-foreground", children: [
3720
4307
  "This ",
3721
4308
  noun,
3722
4309
  " is no longer available to you."
3723
4310
  ] })
3724
4311
  ) : null,
3725
- error ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "mt-1 text-xs text-destructive", children: error }) : null
4312
+ error ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "mt-1 text-xs text-destructive", children: error }) : null
3726
4313
  ] });
3727
4314
  }
3728
4315
 
3729
4316
  // src/react/components/CategoryTagPicker.tsx
3730
- var import_react20 = require("react");
4317
+ var import_react23 = require("react");
3731
4318
  var import_design_system12 = require("@ai-matrx/design-system");
3732
- var import_jsx_runtime15 = require("react/jsx-runtime");
4319
+ var import_jsx_runtime17 = require("react/jsx-runtime");
3733
4320
  var ROOT_PARENT2 = "__root__";
3734
4321
  function CategoryTagPicker({
3735
4322
  entityType,
@@ -3745,11 +4332,11 @@ function CategoryTagPicker({
3745
4332
  }) {
3746
4333
  const store = useAssociationsStore();
3747
4334
  const notifier = useNotifier();
3748
- const [open, setOpen] = (0, import_react20.useState)(false);
3749
- const [creating, setCreating] = (0, import_react20.useState)(false);
3750
- const [writing, setWriting] = (0, import_react20.useState)(false);
3751
- const [search, setSearch] = (0, import_react20.useState)("");
3752
- const [createParentId, setCreateParentId] = (0, import_react20.useState)(ROOT_PARENT2);
4335
+ const [open, setOpen] = (0, import_react23.useState)(false);
4336
+ const [creating, setCreating] = (0, import_react23.useState)(false);
4337
+ const [writing, setWriting] = (0, import_react23.useState)(false);
4338
+ const [search, setSearch] = (0, import_react23.useState)("");
4339
+ const [createParentId, setCreateParentId] = (0, import_react23.useState)(ROOT_PARENT2);
3753
4340
  const {
3754
4341
  categories,
3755
4342
  create: createCategory,
@@ -3853,44 +4440,44 @@ function CategoryTagPicker({
3853
4440
  const exactMatch = categories.some(
3854
4441
  (c) => c.name.toLowerCase() === search.trim().toLowerCase()
3855
4442
  );
3856
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", children: [
3857
- selected.map((c) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
4443
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", children: [
4444
+ selected.map((c) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3858
4445
  import_design_system12.Badge,
3859
4446
  {
3860
4447
  variant: "secondary",
3861
4448
  className: "gap-1 pr-1 text-xs font-medium",
3862
4449
  children: [
3863
4450
  showHierarchy ? hierarchyById.get(c.id)?.displayName ?? c.name : c.name,
3864
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4451
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3865
4452
  "button",
3866
4453
  {
3867
4454
  type: "button",
3868
4455
  onClick: () => void toggle(c.id),
3869
4456
  className: "ml-0.5 rounded-full p-0.5 hover:bg-muted-foreground/20",
3870
4457
  "aria-label": `Remove ${c.name}`,
3871
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(XIcon, { className: "h-2.5 w-2.5" })
4458
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(XIcon, { className: "h-2.5 w-2.5" })
3872
4459
  }
3873
4460
  )
3874
4461
  ]
3875
4462
  },
3876
4463
  c.id
3877
4464
  )),
3878
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_design_system12.Popover, { open, onOpenChange: setOpen, children: [
3879
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_design_system12.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
4465
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_design_system12.Popover, { open, onOpenChange: setOpen, children: [
4466
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_design_system12.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3880
4467
  import_design_system12.Button,
3881
4468
  {
3882
4469
  variant: "outline",
3883
4470
  size: "sm",
3884
4471
  className: "h-7 gap-1 px-2 text-xs",
3885
4472
  children: [
3886
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { className: "h-3.5 w-3.5" }),
4473
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { className: "h-3.5 w-3.5" }),
3887
4474
  selected.length === 0 ? addLabel : "Edit",
3888
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ChevronsUpDownIcon, { className: "h-3 w-3 opacity-50" })
4475
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ChevronsUpDownIcon, { className: "h-3 w-3 opacity-50" })
3889
4476
  ]
3890
4477
  }
3891
4478
  ) }),
3892
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_design_system12.PopoverContent, { className: "w-56 p-0", align: "start", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_design_system12.Command, { shouldFilter: false, children: [
3893
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4479
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_design_system12.PopoverContent, { className: "w-56 p-0", align: "start", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_design_system12.Command, { shouldFilter: false, children: [
4480
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3894
4481
  import_design_system12.CommandInput,
3895
4482
  {
3896
4483
  placeholder: allowCreate ? "Search or create..." : "Search...",
@@ -3898,9 +4485,9 @@ function CategoryTagPicker({
3898
4485
  onValueChange: setSearch
3899
4486
  }
3900
4487
  ),
3901
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_design_system12.CommandList, { children: [
3902
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_design_system12.CommandEmpty, { className: "px-2 py-3 text-xs text-muted-foreground", children: search.trim() ? "No match." : emptyText }),
3903
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_design_system12.CommandGroup, { children: showHierarchy ? filteredHierarchy.map(({ category, depth, parent }) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
4488
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_design_system12.CommandList, { children: [
4489
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_design_system12.CommandEmpty, { className: "px-2 py-3 text-xs text-muted-foreground", children: search.trim() ? "No match." : emptyText }),
4490
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_design_system12.CommandGroup, { children: showHierarchy ? filteredHierarchy.map(({ category, depth, parent }) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3904
4491
  import_design_system12.CommandItem,
3905
4492
  {
3906
4493
  value: category.id,
@@ -3908,7 +4495,7 @@ function CategoryTagPicker({
3908
4495
  onSelect: () => void toggle(category.id),
3909
4496
  className: (0, import_design_system12.cn)("text-xs", depth === 1 && "pl-6"),
3910
4497
  children: [
3911
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4498
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3912
4499
  CheckIcon,
3913
4500
  {
3914
4501
  className: (0, import_design_system12.cn)(
@@ -3917,19 +4504,19 @@ function CategoryTagPicker({
3917
4504
  )
3918
4505
  }
3919
4506
  ),
3920
- depth === 1 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(CornerDownRightIcon, { className: "mr-1.5 h-3.5 w-3.5 shrink-0 text-muted-foreground" }) : null,
3921
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: (0, import_design_system12.cn)(depth === 0 && "font-medium"), children: category.name })
4507
+ depth === 1 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CornerDownRightIcon, { className: "mr-1.5 h-3.5 w-3.5 shrink-0 text-muted-foreground" }) : null,
4508
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: (0, import_design_system12.cn)(depth === 0 && "font-medium"), children: category.name })
3922
4509
  ]
3923
4510
  },
3924
4511
  category.id
3925
- )) : filtered.map((c) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
4512
+ )) : filtered.map((c) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3926
4513
  import_design_system12.CommandItem,
3927
4514
  {
3928
4515
  value: c.id,
3929
4516
  onSelect: () => void toggle(c.id),
3930
4517
  className: "text-xs",
3931
4518
  children: [
3932
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
4519
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3933
4520
  CheckIcon,
3934
4521
  {
3935
4522
  className: (0, import_design_system12.cn)(
@@ -3943,25 +4530,25 @@ function CategoryTagPicker({
3943
4530
  },
3944
4531
  c.id
3945
4532
  )) }),
3946
- allowCreate && search.trim() && !exactMatch ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_design_system12.CommandGroup, { children: [
3947
- showHierarchy ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "space-y-1.5 px-2 pb-2", children: [
3948
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-[11px] font-medium text-muted-foreground", children: "Place in" }),
3949
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
4533
+ allowCreate && search.trim() && !exactMatch ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_design_system12.CommandGroup, { children: [
4534
+ showHierarchy ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-1.5 px-2 pb-2", children: [
4535
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-[11px] font-medium text-muted-foreground", children: "Place in" }),
4536
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3950
4537
  import_design_system12.Select,
3951
4538
  {
3952
4539
  value: createParentId,
3953
4540
  onValueChange: setCreateParentId,
3954
4541
  children: [
3955
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_design_system12.SelectTrigger, { size: "sm", className: "bg-background", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_design_system12.SelectValue, {}) }),
3956
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_design_system12.SelectContent, { children: [
3957
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_design_system12.SelectItem, { value: ROOT_PARENT2, children: "Top level" }),
3958
- hierarchy.roots.map((root) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_design_system12.SelectItem, { value: root.id, children: root.name }, root.id))
4542
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_design_system12.SelectTrigger, { size: "sm", className: "bg-background", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_design_system12.SelectValue, {}) }),
4543
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_design_system12.SelectContent, { children: [
4544
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_design_system12.SelectItem, { value: ROOT_PARENT2, children: "Top level" }),
4545
+ hierarchy.roots.map((root) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_design_system12.SelectItem, { value: root.id, children: root.name }, root.id))
3959
4546
  ] })
3960
4547
  ]
3961
4548
  }
3962
4549
  )
3963
4550
  ] }) : null,
3964
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
4551
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3965
4552
  import_design_system12.CommandItem,
3966
4553
  {
3967
4554
  value: `__create__${search}`,
@@ -3969,7 +4556,7 @@ function CategoryTagPicker({
3969
4556
  disabled: creating,
3970
4557
  className: "text-xs text-primary",
3971
4558
  children: [
3972
- creating ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SpinnerIcon, { className: "mr-2 h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { className: "mr-2 h-3.5 w-3.5" }),
4559
+ creating ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SpinnerIcon, { className: "mr-2 h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { className: "mr-2 h-3.5 w-3.5" }),
3973
4560
  "Create \u201C",
3974
4561
  search.trim(),
3975
4562
  "\u201D"
@@ -3984,7 +4571,7 @@ function CategoryTagPicker({
3984
4571
  }
3985
4572
 
3986
4573
  // src/react/components/CommentThread.tsx
3987
- var import_react21 = require("react");
4574
+ var import_react24 = require("react");
3988
4575
  var import_design_system13 = require("@ai-matrx/design-system");
3989
4576
 
3990
4577
  // src/react/relativeTime.ts
@@ -4012,7 +4599,7 @@ function formatRelativeTime(iso, now = Date.now()) {
4012
4599
  }
4013
4600
 
4014
4601
  // src/react/components/CommentThread.tsx
4015
- var import_jsx_runtime16 = require("react/jsx-runtime");
4602
+ var import_jsx_runtime18 = require("react/jsx-runtime");
4016
4603
  var AUTHOR_DOOR_TOKEN = "user";
4017
4604
  function resolveAuthor(comment, authorDisplay) {
4018
4605
  const base2 = {
@@ -4031,7 +4618,7 @@ function resolveAuthor(comment, authorDisplay) {
4031
4618
  }
4032
4619
  function AuthorAvatar({ author }) {
4033
4620
  if (author.avatarUrl) {
4034
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4621
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4035
4622
  "img",
4036
4623
  {
4037
4624
  src: author.avatarUrl,
@@ -4040,7 +4627,7 @@ function AuthorAvatar({ author }) {
4040
4627
  }
4041
4628
  );
4042
4629
  }
4043
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4630
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4044
4631
  "span",
4045
4632
  {
4046
4633
  "aria-hidden": true,
@@ -4055,7 +4642,7 @@ function AuthorName({
4055
4642
  }) {
4056
4643
  const DoorRef2 = entityDoors?.EntityRef;
4057
4644
  if (DoorRef2 && author.userId) {
4058
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4645
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4059
4646
  DoorRef2,
4060
4647
  {
4061
4648
  token: AUTHOR_DOOR_TOKEN,
@@ -4066,7 +4653,7 @@ function AuthorName({
4066
4653
  }
4067
4654
  );
4068
4655
  }
4069
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-sm font-medium text-foreground", children: author.name });
4656
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-sm font-medium text-foreground", children: author.name });
4070
4657
  }
4071
4658
  function CommentComposer({
4072
4659
  onSubmit,
@@ -4077,9 +4664,9 @@ function CommentComposer({
4077
4664
  autoFocus = false
4078
4665
  }) {
4079
4666
  const notifier = useNotifier();
4080
- const [body, setBody] = (0, import_react21.useState)(initialValue);
4081
- const [submitting, setSubmitting] = (0, import_react21.useState)(false);
4082
- const [error, setError] = (0, import_react21.useState)(null);
4667
+ const [body, setBody] = (0, import_react24.useState)(initialValue);
4668
+ const [submitting, setSubmitting] = (0, import_react24.useState)(false);
4669
+ const [error, setError] = (0, import_react24.useState)(null);
4083
4670
  async function submit(e) {
4084
4671
  e?.preventDefault();
4085
4672
  const trimmed = body.trim();
@@ -4096,8 +4683,8 @@ function CommentComposer({
4096
4683
  }
4097
4684
  setBody("");
4098
4685
  }
4099
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("form", { onSubmit: submit, className: "flex flex-col gap-1.5", children: [
4100
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4686
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("form", { onSubmit: submit, className: "flex flex-col gap-1.5", children: [
4687
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4101
4688
  "textarea",
4102
4689
  {
4103
4690
  value: body,
@@ -4112,10 +4699,10 @@ function CommentComposer({
4112
4699
  className: "w-full resize-y rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
4113
4700
  }
4114
4701
  ),
4115
- error && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { role: "alert", className: "text-xs text-destructive", children: error }),
4116
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", children: [
4117
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_design_system13.Button, { type: "submit", size: "sm", disabled: !body.trim() || submitting, children: [
4118
- submitting && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SpinnerIcon, { className: "mr-1 h-3 w-3 animate-spin" }),
4702
+ error && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { role: "alert", className: "text-xs text-destructive", children: error }),
4703
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-2", children: [
4704
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_design_system13.Button, { type: "submit", size: "sm", disabled: !body.trim() || submitting, children: [
4705
+ submitting && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SpinnerIcon, { className: "mr-1 h-3 w-3 animate-spin" }),
4119
4706
  submitLabel
4120
4707
  ] }),
4121
4708
  trailing
@@ -4131,10 +4718,10 @@ function CommentItem({
4131
4718
  }) {
4132
4719
  const { authorDisplay, entityDoors } = useAssociationsUiPorts();
4133
4720
  const notifier = useNotifier();
4134
- const [replying, setReplying] = (0, import_react21.useState)(false);
4135
- const [editing, setEditing] = (0, import_react21.useState)(false);
4136
- const [confirmingDelete, setConfirmingDelete] = (0, import_react21.useState)(false);
4137
- const [deleting, setDeleting] = (0, import_react21.useState)(false);
4721
+ const [replying, setReplying] = (0, import_react24.useState)(false);
4722
+ const [editing, setEditing] = (0, import_react24.useState)(false);
4723
+ const [confirmingDelete, setConfirmingDelete] = (0, import_react24.useState)(false);
4724
+ const [deleting, setDeleting] = (0, import_react24.useState)(false);
4138
4725
  const author = resolveAuthor(comment, authorDisplay);
4139
4726
  const isOwn = thread.currentUserId !== null && comment.createdBy === thread.currentUserId;
4140
4727
  const edited = comment.updatedAt !== comment.createdAt;
@@ -4148,7 +4735,7 @@ function CommentItem({
4148
4735
  notifier.error(res.error ?? "Could not delete the comment");
4149
4736
  }
4150
4737
  }
4151
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4738
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4152
4739
  "div",
4153
4740
  {
4154
4741
  "data-comment-id": comment.id,
@@ -4157,10 +4744,10 @@ function CommentItem({
4157
4744
  depth > 0 && "border-l border-border pl-3"
4158
4745
  ),
4159
4746
  children: [
4160
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", children: [
4161
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AuthorAvatar, { author }),
4162
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AuthorName, { author, entityDoors }),
4163
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4747
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-2", children: [
4748
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AuthorAvatar, { author }),
4749
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AuthorName, { author, entityDoors }),
4750
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4164
4751
  "span",
4165
4752
  {
4166
4753
  className: "text-xs text-muted-foreground",
@@ -4168,7 +4755,7 @@ function CommentItem({
4168
4755
  children: formatRelativeTime(comment.createdAt)
4169
4756
  }
4170
4757
  ),
4171
- edited && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4758
+ edited && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4172
4759
  "span",
4173
4760
  {
4174
4761
  className: "text-xs text-muted-foreground",
@@ -4177,7 +4764,7 @@ function CommentItem({
4177
4764
  }
4178
4765
  )
4179
4766
  ] }),
4180
- editing ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "pl-8", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4767
+ editing ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "pl-8", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4181
4768
  CommentComposer,
4182
4769
  {
4183
4770
  initialValue: comment.body,
@@ -4189,7 +4776,7 @@ function CommentItem({
4189
4776
  if (res.ok) setEditing(false);
4190
4777
  return res;
4191
4778
  },
4192
- trailing: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4779
+ trailing: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4193
4780
  import_design_system13.Button,
4194
4781
  {
4195
4782
  type: "button",
@@ -4200,10 +4787,10 @@ function CommentItem({
4200
4787
  }
4201
4788
  )
4202
4789
  }
4203
- ) }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "whitespace-pre-wrap pl-8 text-sm text-foreground", children: comment.body }),
4204
- !editing && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex items-center gap-1 pl-7", children: confirmingDelete ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
4205
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-xs text-destructive", children: "Deletes this comment for everyone." }),
4206
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4790
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "whitespace-pre-wrap pl-8 text-sm text-foreground", children: comment.body }),
4791
+ !editing && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex items-center gap-1 pl-7", children: confirmingDelete ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4792
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-xs text-destructive", children: "Deletes this comment for everyone." }),
4793
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4207
4794
  import_design_system13.Button,
4208
4795
  {
4209
4796
  type: "button",
@@ -4212,12 +4799,12 @@ function CommentItem({
4212
4799
  disabled: deleting,
4213
4800
  onClick: () => void confirmDelete(),
4214
4801
  children: [
4215
- deleting && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SpinnerIcon, { className: "mr-1 h-3 w-3 animate-spin" }),
4802
+ deleting && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SpinnerIcon, { className: "mr-1 h-3 w-3 animate-spin" }),
4216
4803
  "Delete"
4217
4804
  ]
4218
4805
  }
4219
4806
  ),
4220
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4807
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4221
4808
  import_design_system13.Button,
4222
4809
  {
4223
4810
  type: "button",
@@ -4228,8 +4815,8 @@ function CommentItem({
4228
4815
  children: "Cancel"
4229
4816
  }
4230
4817
  )
4231
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
4232
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4818
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4819
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4233
4820
  "button",
4234
4821
  {
4235
4822
  type: "button",
@@ -4238,8 +4825,8 @@ function CommentItem({
4238
4825
  children: "Reply"
4239
4826
  }
4240
4827
  ),
4241
- isOwn && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
4242
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4828
+ isOwn && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4829
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4243
4830
  "button",
4244
4831
  {
4245
4832
  type: "button",
@@ -4248,7 +4835,7 @@ function CommentItem({
4248
4835
  children: "Edit"
4249
4836
  }
4250
4837
  ),
4251
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4838
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4252
4839
  "button",
4253
4840
  {
4254
4841
  type: "button",
@@ -4259,9 +4846,9 @@ function CommentItem({
4259
4846
  )
4260
4847
  ] })
4261
4848
  ] }) }),
4262
- replying && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-start gap-2 pl-8 pt-1", children: [
4263
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(CornerDownRightIcon, { className: "mt-2 h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
4264
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4849
+ replying && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-start gap-2 pl-8 pt-1", children: [
4850
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(CornerDownRightIcon, { className: "mt-2 h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
4851
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4265
4852
  CommentComposer,
4266
4853
  {
4267
4854
  submitLabel: "Reply",
@@ -4275,7 +4862,7 @@ function CommentItem({
4275
4862
  if (res.ok) setReplying(false);
4276
4863
  return res;
4277
4864
  },
4278
- trailing: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4865
+ trailing: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4279
4866
  import_design_system13.Button,
4280
4867
  {
4281
4868
  type: "button",
@@ -4288,7 +4875,7 @@ function CommentItem({
4288
4875
  }
4289
4876
  ) })
4290
4877
  ] }),
4291
- replies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex flex-col gap-3 pl-8 pt-1", children: replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4878
+ replies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex flex-col gap-3 pl-8 pt-1", children: replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4292
4879
  CommentItem,
4293
4880
  {
4294
4881
  comment: reply,
@@ -4328,11 +4915,11 @@ function CommentThread({
4328
4915
  const thread = useComments({ token, id });
4329
4916
  const { roots, childrenOf } = buildCommentTree(thread.comments);
4330
4917
  const loading = (thread.status === "loading" || thread.status === "idle") && thread.comments.length === 0;
4331
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("section", { className: (0, import_design_system13.cn)("flex flex-col gap-3", className), children: [
4332
- showHeader && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("header", { className: "flex items-center gap-2", children: [
4333
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("h3", { className: "text-sm font-semibold text-foreground", children: "Comments" }),
4334
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-xs text-muted-foreground tabular-nums", children: thread.comments.length }),
4335
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4918
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("section", { className: (0, import_design_system13.cn)("flex flex-col gap-3", className), children: [
4919
+ showHeader && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("header", { className: "flex items-center gap-2", children: [
4920
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h3", { className: "text-sm font-semibold text-foreground", children: "Comments" }),
4921
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-xs text-muted-foreground tabular-nums", children: thread.comments.length }),
4922
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4336
4923
  "button",
4337
4924
  {
4338
4925
  type: "button",
@@ -4340,18 +4927,18 @@ function CommentThread({
4340
4927
  "aria-label": "Refresh comments",
4341
4928
  title: "Refresh comments",
4342
4929
  className: "ml-auto flex h-6 w-6 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
4343
- children: thread.status === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SpinnerIcon, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(RefreshIcon, { className: "h-3.5 w-3.5" })
4930
+ children: thread.status === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SpinnerIcon, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(RefreshIcon, { className: "h-3.5 w-3.5" })
4344
4931
  }
4345
4932
  )
4346
4933
  ] }),
4347
- thread.error && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4934
+ thread.error && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4348
4935
  "div",
4349
4936
  {
4350
4937
  role: "alert",
4351
4938
  className: "flex items-center gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive",
4352
4939
  children: [
4353
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "min-w-0 flex-1", children: thread.error }),
4354
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4940
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "min-w-0 flex-1", children: thread.error }),
4941
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4355
4942
  import_design_system13.Button,
4356
4943
  {
4357
4944
  type: "button",
@@ -4365,10 +4952,10 @@ function CommentThread({
4365
4952
  ]
4366
4953
  }
4367
4954
  ),
4368
- loading ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-col gap-3", "aria-hidden": true, "data-testid": "comments-loading", children: [
4369
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.Skeleton, { className: "h-12 w-full" }),
4370
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_design_system13.Skeleton, { className: "h-12 w-4/5" })
4371
- ] }) : roots.length === 0 && !thread.error ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-sm text-muted-foreground", children: "No comments yet." }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex flex-col gap-4", children: roots.map((comment) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4955
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col gap-3", "aria-hidden": true, "data-testid": "comments-loading", children: [
4956
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_design_system13.Skeleton, { className: "h-12 w-full" }),
4957
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_design_system13.Skeleton, { className: "h-12 w-4/5" })
4958
+ ] }) : roots.length === 0 && !thread.error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-sm text-muted-foreground", children: "No comments yet." }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex flex-col gap-4", children: roots.map((comment) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4372
4959
  CommentItem,
4373
4960
  {
4374
4961
  comment,
@@ -4379,7 +4966,7 @@ function CommentThread({
4379
4966
  },
4380
4967
  comment.id
4381
4968
  )) }),
4382
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4969
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4383
4970
  CommentComposer,
4384
4971
  {
4385
4972
  onSubmit: (body) => thread.add(body, { orgId: orgId ?? null })