@ai-matrx/associations 0.5.2 → 0.6.1

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