@ai-matrx/associations 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +90 -0
- package/README.md +51 -13
- package/dist/CommentThread-D4kcTbog.d.cts +1905 -0
- package/dist/CommentThread-D4kcTbog.d.ts +1905 -0
- package/dist/core/index.cjs +21 -6
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +18 -5
- package/dist/core/index.d.ts +18 -5
- package/dist/core/index.js +21 -6
- package/dist/core/index.js.map +1 -1
- package/dist/index.d.cts +47 -14
- package/dist/index.d.ts +47 -14
- package/dist/react/index.cjs +1038 -451
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +79 -1854
- package/dist/react/index.d.ts +79 -1854
- package/dist/react/index.js +1063 -465
- package/dist/react/index.js.map +1 -1
- package/dist/react/lazy/index.cjs +89 -0
- package/dist/react/lazy/index.cjs.map +1 -0
- package/dist/react/lazy/index.d.cts +17 -0
- package/dist/react/lazy/index.d.ts +17 -0
- package/dist/react/lazy/index.js +59 -0
- package/dist/react/lazy/index.js.map +1 -0
- package/package.json +18 -4
package/dist/react/index.js
CHANGED
|
@@ -1,13 +1,191 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/react/context.tsx
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
createContext,
|
|
6
|
+
useContext,
|
|
7
|
+
useEffect,
|
|
8
|
+
useRef
|
|
9
|
+
} from "react";
|
|
10
|
+
|
|
11
|
+
// src/demanded-rpcs.generated.ts
|
|
12
|
+
var DEMANDED_RPC_NAMES = [
|
|
13
|
+
"assoc_add",
|
|
14
|
+
"assoc_remove",
|
|
15
|
+
"assoc_set_targets",
|
|
16
|
+
"assoc_remove_for_entity",
|
|
17
|
+
"assoc_for_entity",
|
|
18
|
+
"assoc_for_targets",
|
|
19
|
+
"assoc_for_sources",
|
|
20
|
+
"assoc_members_visible",
|
|
21
|
+
"conversation_file_add",
|
|
22
|
+
"conversation_file_remove",
|
|
23
|
+
"conversation_files",
|
|
24
|
+
"agent_resource_add",
|
|
25
|
+
"agent_resource_remove",
|
|
26
|
+
"cat_list",
|
|
27
|
+
"cat_create",
|
|
28
|
+
"cat_update",
|
|
29
|
+
"cat_reparent",
|
|
30
|
+
"cat_delete",
|
|
31
|
+
"ues_set",
|
|
32
|
+
"ues_list",
|
|
33
|
+
"ues_get_bulk",
|
|
34
|
+
"ues_touch",
|
|
35
|
+
"reference_search_candidates",
|
|
36
|
+
"cmt_list",
|
|
37
|
+
"cmt_add",
|
|
38
|
+
"cmt_edit",
|
|
39
|
+
"cmt_delete"
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
// src/core/assertDemandedSchema.ts
|
|
43
|
+
var BAD_UUID = "__not_a_uuid__";
|
|
44
|
+
var SELF_TEST_MISSING_RPC = "assoc_probe_self_test_missing_fn";
|
|
45
|
+
var PROBE_ARGS = {
|
|
46
|
+
// edges — writes
|
|
47
|
+
assoc_add: {
|
|
48
|
+
p_source_type: "note",
|
|
49
|
+
p_source_id: BAD_UUID,
|
|
50
|
+
p_target_type: "task",
|
|
51
|
+
p_target_id: BAD_UUID
|
|
52
|
+
},
|
|
53
|
+
assoc_remove: {
|
|
54
|
+
p_source_type: "note",
|
|
55
|
+
p_source_id: BAD_UUID,
|
|
56
|
+
p_target_type: "task",
|
|
57
|
+
p_target_id: BAD_UUID
|
|
58
|
+
},
|
|
59
|
+
assoc_set_targets: {
|
|
60
|
+
p_source_type: "note",
|
|
61
|
+
p_source_id: BAD_UUID,
|
|
62
|
+
p_target_type: "task",
|
|
63
|
+
p_target_ids: [BAD_UUID]
|
|
64
|
+
},
|
|
65
|
+
assoc_remove_for_entity: { p_type: "note", p_id: BAD_UUID },
|
|
66
|
+
// edges — reads
|
|
67
|
+
assoc_for_entity: { p_type: "note", p_id: BAD_UUID },
|
|
68
|
+
assoc_for_targets: { p_target_type: "task", p_target_ids: [BAD_UUID] },
|
|
69
|
+
assoc_for_sources: { p_source_type: "note", p_source_ids: [BAD_UUID] },
|
|
70
|
+
assoc_members_visible: { p_target_type: "task", p_target_ids: [BAD_UUID] },
|
|
71
|
+
// conversation files
|
|
72
|
+
conversation_file_add: {
|
|
73
|
+
p_conversation_id: BAD_UUID,
|
|
74
|
+
p_file_id: BAD_UUID
|
|
75
|
+
},
|
|
76
|
+
conversation_file_remove: {
|
|
77
|
+
p_conversation_id: BAD_UUID,
|
|
78
|
+
p_file_id: BAD_UUID
|
|
79
|
+
},
|
|
80
|
+
conversation_files: { p_conversation_id: BAD_UUID },
|
|
81
|
+
// agent resources
|
|
82
|
+
agent_resource_add: {
|
|
83
|
+
p_agent_id: BAD_UUID,
|
|
84
|
+
p_source_type: "note",
|
|
85
|
+
p_source_id: BAD_UUID
|
|
86
|
+
},
|
|
87
|
+
agent_resource_remove: {
|
|
88
|
+
p_agent_id: BAD_UUID,
|
|
89
|
+
p_source_type: "note",
|
|
90
|
+
p_source_id: BAD_UUID
|
|
91
|
+
},
|
|
92
|
+
// categories
|
|
93
|
+
cat_list: {},
|
|
94
|
+
cat_create: { p_dimension: "", p_name: "", p_org_id: BAD_UUID },
|
|
95
|
+
cat_update: { p_category_id: BAD_UUID, p_name: "__probe__" },
|
|
96
|
+
cat_reparent: { p_category_id: BAD_UUID },
|
|
97
|
+
cat_delete: { p_category_id: BAD_UUID },
|
|
98
|
+
// favorites / recents
|
|
99
|
+
ues_set: { p_entity_type: "__probe__", p_entity_id: BAD_UUID },
|
|
100
|
+
ues_list: {},
|
|
101
|
+
ues_get_bulk: { p_entity_type: "__probe__", p_entity_ids: [BAD_UUID] },
|
|
102
|
+
ues_touch: { p_entity_type: "__probe__", p_entity_id: BAD_UUID },
|
|
103
|
+
// candidates
|
|
104
|
+
reference_search_candidates: { p_token: "__probe__", p_limit: 1 },
|
|
105
|
+
// comments (W6) — every fn takes a uuid arg, so the unparseable sentinel
|
|
106
|
+
// guarantees 22P02 before any SECURITY DEFINER body runs (no write occurs).
|
|
107
|
+
cmt_list: { p_entity_type: "__probe__", p_entity_id: BAD_UUID },
|
|
108
|
+
cmt_add: {
|
|
109
|
+
p_entity_type: "__probe__",
|
|
110
|
+
p_entity_id: BAD_UUID,
|
|
111
|
+
p_body: "__probe__"
|
|
112
|
+
},
|
|
113
|
+
cmt_edit: { p_id: BAD_UUID, p_body: "__probe__" },
|
|
114
|
+
cmt_delete: { p_id: BAD_UUID }
|
|
115
|
+
};
|
|
116
|
+
function isMissingFunctionError(error) {
|
|
117
|
+
if (!error || typeof error !== "object") return false;
|
|
118
|
+
return error.code === "PGRST202";
|
|
119
|
+
}
|
|
120
|
+
async function assertDemandedSchema(dataSource, options = {}) {
|
|
121
|
+
const { selfTest = false, concurrency = 6, throwOnViolation = true } = options;
|
|
122
|
+
const names = [...DEMANDED_RPC_NAMES];
|
|
123
|
+
if (selfTest) names.push(SELF_TEST_MISSING_RPC);
|
|
124
|
+
const missing = [];
|
|
125
|
+
const unreachable = [];
|
|
126
|
+
const answered = [];
|
|
127
|
+
let cursor = 0;
|
|
128
|
+
async function worker() {
|
|
129
|
+
while (cursor < names.length) {
|
|
130
|
+
const fn = names[cursor];
|
|
131
|
+
cursor += 1;
|
|
132
|
+
if (!fn) continue;
|
|
133
|
+
const args = fn === SELF_TEST_MISSING_RPC ? {} : PROBE_ARGS[fn];
|
|
134
|
+
try {
|
|
135
|
+
const { error } = await dataSource.rpc(fn, args);
|
|
136
|
+
if (error == null) answered.push(fn);
|
|
137
|
+
else if (isMissingFunctionError(error)) missing.push(fn);
|
|
138
|
+
else answered.push(fn);
|
|
139
|
+
} catch (e) {
|
|
140
|
+
unreachable.push({ fn, error: e });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
await Promise.all(
|
|
145
|
+
Array.from({ length: Math.min(concurrency, names.length) }, worker)
|
|
146
|
+
);
|
|
147
|
+
if (selfTest) {
|
|
148
|
+
const selfTestMissing = missing.includes(SELF_TEST_MISSING_RPC);
|
|
149
|
+
const idx = missing.indexOf(SELF_TEST_MISSING_RPC);
|
|
150
|
+
if (idx >= 0) missing.splice(idx, 1);
|
|
151
|
+
const answeredIdx = answered.indexOf(SELF_TEST_MISSING_RPC);
|
|
152
|
+
if (answeredIdx >= 0) answered.splice(answeredIdx, 1);
|
|
153
|
+
if (!selfTestMissing) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`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.`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (unreachable.length > 0) {
|
|
160
|
+
throw new Error(
|
|
161
|
+
`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.`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
const ok = missing.length === 0;
|
|
165
|
+
const report = { ok, missing, unreachable, answered };
|
|
166
|
+
if (!ok && throwOnViolation) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
`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.`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
return report;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/react/context.tsx
|
|
5
175
|
import { jsx } from "react/jsx-runtime";
|
|
6
176
|
var AssociationsReactContext = createContext(
|
|
7
177
|
null
|
|
8
178
|
);
|
|
179
|
+
function isDevelopmentBuild() {
|
|
180
|
+
try {
|
|
181
|
+
return typeof process !== "undefined" && true;
|
|
182
|
+
} catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
9
186
|
function AssociationsProvider({
|
|
10
187
|
store,
|
|
188
|
+
probeSchema,
|
|
11
189
|
notifier,
|
|
12
190
|
windowShell,
|
|
13
191
|
capture,
|
|
@@ -17,6 +195,28 @@ function AssociationsProvider({
|
|
|
17
195
|
children
|
|
18
196
|
}) {
|
|
19
197
|
const notifierWarned = useRef(false);
|
|
198
|
+
const shouldProbe = probeSchema ?? isDevelopmentBuild();
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
if (!shouldProbe) return;
|
|
201
|
+
let stale = false;
|
|
202
|
+
void (async () => {
|
|
203
|
+
try {
|
|
204
|
+
await assertDemandedSchema(store.dataSource);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
if (stale) return;
|
|
207
|
+
store.errorSink({
|
|
208
|
+
code: "demanded_schema_violation",
|
|
209
|
+
message: error instanceof Error ? error.message : "assertDemandedSchema failed",
|
|
210
|
+
context: {
|
|
211
|
+
remedy: "Run the platform.associations schema (README \xA7 The demanded schema) on the database this dataSource points at."
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
})();
|
|
216
|
+
return () => {
|
|
217
|
+
stale = true;
|
|
218
|
+
};
|
|
219
|
+
}, [shouldProbe, store]);
|
|
20
220
|
return /* @__PURE__ */ jsx(
|
|
21
221
|
AssociationsReactContext.Provider,
|
|
22
222
|
{
|
|
@@ -78,7 +278,7 @@ function useNotifier() {
|
|
|
78
278
|
}
|
|
79
279
|
|
|
80
280
|
// src/react/hooks/useAssociations.ts
|
|
81
|
-
import { useEffect, useRef as useRef2, useSyncExternalStore } from "react";
|
|
281
|
+
import { useEffect as useEffect2, useRef as useRef2, useSyncExternalStore } from "react";
|
|
82
282
|
|
|
83
283
|
// src/results.ts
|
|
84
284
|
function isAssociationsRpcErr(r) {
|
|
@@ -789,7 +989,7 @@ function useAssociations(args) {
|
|
|
789
989
|
() => store.getEdges(type, id ?? "")
|
|
790
990
|
);
|
|
791
991
|
const loadedKey = useRef2(null);
|
|
792
|
-
|
|
992
|
+
useEffect2(() => {
|
|
793
993
|
if (!autoLoad || !type || !id) return;
|
|
794
994
|
const k = associationsKey(type, id);
|
|
795
995
|
if (loadedKey.current === k) return;
|
|
@@ -845,7 +1045,7 @@ var useEntityRelationships = useAssociations;
|
|
|
845
1045
|
// src/react/hooks/useContainerLinks.ts
|
|
846
1046
|
import {
|
|
847
1047
|
useCallback,
|
|
848
|
-
useEffect as
|
|
1048
|
+
useEffect as useEffect3,
|
|
849
1049
|
useLayoutEffect,
|
|
850
1050
|
useRef as useRef3,
|
|
851
1051
|
useState
|
|
@@ -910,7 +1110,7 @@ function useContainerLinks(args) {
|
|
|
910
1110
|
loading: false
|
|
911
1111
|
});
|
|
912
1112
|
}, [containerId, conversationKey, store]);
|
|
913
|
-
|
|
1113
|
+
useEffect3(() => {
|
|
914
1114
|
const timeout = setTimeout(() => {
|
|
915
1115
|
void loadConversationFiles();
|
|
916
1116
|
}, 0);
|
|
@@ -993,7 +1193,7 @@ function useContainerLinks(args) {
|
|
|
993
1193
|
}
|
|
994
1194
|
|
|
995
1195
|
// src/react/hooks/useEntityTitles.ts
|
|
996
|
-
import { useEffect as
|
|
1196
|
+
import { useEffect as useEffect4, useState as useState2 } from "react";
|
|
997
1197
|
function useEntityTitles(refs) {
|
|
998
1198
|
const store = useAssociationsStore();
|
|
999
1199
|
const [resolved, setResolved] = useState2({});
|
|
@@ -1003,7 +1203,7 @@ function useEntityTitles(refs) {
|
|
|
1003
1203
|
(r) => store.registry.tryGetEntityInfo(r.token)?.titleColumn != null && store.titles.get(r.token, r.id) == null && !attempted[entityTitleCacheKey(r.token, r.id)]
|
|
1004
1204
|
);
|
|
1005
1205
|
const neededKey = needed.map((r) => entityTitleCacheKey(r.token, r.id)).sort().join("|");
|
|
1006
|
-
|
|
1206
|
+
useEffect4(() => {
|
|
1007
1207
|
if (!neededKey) return;
|
|
1008
1208
|
let cancelled = false;
|
|
1009
1209
|
setLoading(true);
|
|
@@ -1067,7 +1267,7 @@ function useEntityTitles(refs) {
|
|
|
1067
1267
|
}
|
|
1068
1268
|
|
|
1069
1269
|
// src/react/hooks/useCategories.ts
|
|
1070
|
-
import { useEffect as
|
|
1270
|
+
import { useEffect as useEffect5, useRef as useRef4, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
1071
1271
|
var noopSubscribe2 = () => () => {
|
|
1072
1272
|
};
|
|
1073
1273
|
function useCategories(args) {
|
|
@@ -1079,7 +1279,7 @@ function useCategories(args) {
|
|
|
1079
1279
|
() => store.getCategories(dimension ?? "")
|
|
1080
1280
|
);
|
|
1081
1281
|
const loadedKey = useRef4(null);
|
|
1082
|
-
|
|
1282
|
+
useEffect5(() => {
|
|
1083
1283
|
if (!autoLoad || !dimension) return;
|
|
1084
1284
|
if (loadedKey.current === dimension) return;
|
|
1085
1285
|
loadedKey.current = dimension;
|
|
@@ -1130,7 +1330,7 @@ function useCategories(args) {
|
|
|
1130
1330
|
}
|
|
1131
1331
|
|
|
1132
1332
|
// src/react/hooks/useUniversalEntitySearch.ts
|
|
1133
|
-
import { useEffect as
|
|
1333
|
+
import { useEffect as useEffect6, useRef as useRef5, useState as useState3 } from "react";
|
|
1134
1334
|
var DEBOUNCE_MS = 250;
|
|
1135
1335
|
var RECENTS_LIMIT = 12;
|
|
1136
1336
|
function useUniversalEntitySearch(args) {
|
|
@@ -1148,7 +1348,7 @@ function useUniversalEntitySearch(args) {
|
|
|
1148
1348
|
const [isRecents, setIsRecents] = useState3(true);
|
|
1149
1349
|
const runRef = useRef5(0);
|
|
1150
1350
|
const tokensKey = (tokens ?? []).join(",");
|
|
1151
|
-
|
|
1351
|
+
useEffect6(() => {
|
|
1152
1352
|
if (!enabled) return;
|
|
1153
1353
|
const run = ++runRef.current;
|
|
1154
1354
|
const trimmed = query.trim();
|
|
@@ -1210,7 +1410,7 @@ async function loadRecents(store, tokens) {
|
|
|
1210
1410
|
}
|
|
1211
1411
|
|
|
1212
1412
|
// src/react/hooks/useAssociationCandidates.ts
|
|
1213
|
-
import { useEffect as
|
|
1413
|
+
import { useEffect as useEffect7, useState as useState4 } from "react";
|
|
1214
1414
|
function useAssociationCandidates(args) {
|
|
1215
1415
|
const { token, enabled = true, search, limit } = args;
|
|
1216
1416
|
const store = useAssociationsStore();
|
|
@@ -1219,7 +1419,7 @@ function useAssociationCandidates(args) {
|
|
|
1219
1419
|
const [loading, setLoading] = useState4(false);
|
|
1220
1420
|
const [error, setError] = useState4(null);
|
|
1221
1421
|
const [tick, setTick] = useState4(0);
|
|
1222
|
-
|
|
1422
|
+
useEffect7(() => {
|
|
1223
1423
|
if (!enabled) return void 0;
|
|
1224
1424
|
let cancelled = false;
|
|
1225
1425
|
void (async () => {
|
|
@@ -1260,7 +1460,7 @@ function currentUserIdOrNull(store) {
|
|
|
1260
1460
|
}
|
|
1261
1461
|
|
|
1262
1462
|
// src/react/hooks/useComments.ts
|
|
1263
|
-
import { useEffect as
|
|
1463
|
+
import { useEffect as useEffect8, useRef as useRef6, useSyncExternalStore as useSyncExternalStore3 } from "react";
|
|
1264
1464
|
var noopSubscribe3 = () => () => {
|
|
1265
1465
|
};
|
|
1266
1466
|
function useComments(args) {
|
|
@@ -1273,7 +1473,7 @@ function useComments(args) {
|
|
|
1273
1473
|
() => store.getComments(token, id ?? "")
|
|
1274
1474
|
);
|
|
1275
1475
|
const loadedKey = useRef6(null);
|
|
1276
|
-
|
|
1476
|
+
useEffect8(() => {
|
|
1277
1477
|
if (!autoLoad || !key || !id) return;
|
|
1278
1478
|
if (loadedKey.current === key) return;
|
|
1279
1479
|
loadedKey.current = key;
|
|
@@ -1415,6 +1615,16 @@ function usePrimaryEntity() {
|
|
|
1415
1615
|
|
|
1416
1616
|
// src/react/components/AssociationWindow.tsx
|
|
1417
1617
|
import { useId } from "react";
|
|
1618
|
+
|
|
1619
|
+
// src/react/components/DefaultWindowShell.tsx
|
|
1620
|
+
import {
|
|
1621
|
+
useCallback as useCallback2,
|
|
1622
|
+
useEffect as useEffect9,
|
|
1623
|
+
useLayoutEffect as useLayoutEffect2,
|
|
1624
|
+
useRef as useRef7,
|
|
1625
|
+
useState as useState6
|
|
1626
|
+
} from "react";
|
|
1627
|
+
import { createPortal } from "react-dom";
|
|
1418
1628
|
import { cn } from "@ai-matrx/design-system";
|
|
1419
1629
|
|
|
1420
1630
|
// src/react/icons.tsx
|
|
@@ -1533,9 +1743,246 @@ function DefaultEntityIcon(props) {
|
|
|
1533
1743
|
/* @__PURE__ */ jsx3("path", { d: "M12 13.5V8" })
|
|
1534
1744
|
] });
|
|
1535
1745
|
}
|
|
1746
|
+
function MaximizeIcon(props) {
|
|
1747
|
+
return /* @__PURE__ */ jsxs("svg", { ...base(props), children: [
|
|
1748
|
+
/* @__PURE__ */ jsx3("path", { d: "M15 3h6v6" }),
|
|
1749
|
+
/* @__PURE__ */ jsx3("path", { d: "M9 21H3v-6" }),
|
|
1750
|
+
/* @__PURE__ */ jsx3("path", { d: "M21 3l-7 7" }),
|
|
1751
|
+
/* @__PURE__ */ jsx3("path", { d: "M3 21l7-7" })
|
|
1752
|
+
] });
|
|
1753
|
+
}
|
|
1754
|
+
function RestoreIcon(props) {
|
|
1755
|
+
return /* @__PURE__ */ jsxs("svg", { ...base(props), children: [
|
|
1756
|
+
/* @__PURE__ */ jsx3("path", { d: "M4 14h6v6" }),
|
|
1757
|
+
/* @__PURE__ */ jsx3("path", { d: "M20 10h-6V4" }),
|
|
1758
|
+
/* @__PURE__ */ jsx3("path", { d: "M14 10l7-7" }),
|
|
1759
|
+
/* @__PURE__ */ jsx3("path", { d: "M3 21l7-7" })
|
|
1760
|
+
] });
|
|
1761
|
+
}
|
|
1762
|
+
function GripResizeIcon(props) {
|
|
1763
|
+
return /* @__PURE__ */ jsxs("svg", { ...base(props), children: [
|
|
1764
|
+
/* @__PURE__ */ jsx3("path", { d: "M21 15 15 21" }),
|
|
1765
|
+
/* @__PURE__ */ jsx3("path", { d: "M21 20 20 21" })
|
|
1766
|
+
] });
|
|
1767
|
+
}
|
|
1536
1768
|
|
|
1537
|
-
// src/react/components/
|
|
1769
|
+
// src/react/components/DefaultWindowShell.tsx
|
|
1538
1770
|
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1771
|
+
var DEFAULT_WIDTH = 440;
|
|
1772
|
+
var DEFAULT_HEIGHT = 580;
|
|
1773
|
+
var MIN_WIDTH = 330;
|
|
1774
|
+
var MIN_HEIGHT = 380;
|
|
1775
|
+
var MOBILE_MAX_WIDTH = 640;
|
|
1776
|
+
var EDGE_KEEP = 48;
|
|
1777
|
+
function viewport() {
|
|
1778
|
+
if (typeof window === "undefined") {
|
|
1779
|
+
return { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT };
|
|
1780
|
+
}
|
|
1781
|
+
return { width: window.innerWidth, height: window.innerHeight };
|
|
1782
|
+
}
|
|
1783
|
+
function clamp(rect) {
|
|
1784
|
+
const { width: vw, height: vh } = viewport();
|
|
1785
|
+
const width = Math.min(Math.max(rect.width, MIN_WIDTH), Math.max(vw, MIN_WIDTH));
|
|
1786
|
+
const height = Math.min(
|
|
1787
|
+
Math.max(rect.height, MIN_HEIGHT),
|
|
1788
|
+
Math.max(vh, MIN_HEIGHT)
|
|
1789
|
+
);
|
|
1790
|
+
return {
|
|
1791
|
+
width,
|
|
1792
|
+
height,
|
|
1793
|
+
x: Math.min(Math.max(rect.x, EDGE_KEEP - width), Math.max(vw - EDGE_KEEP, 0)),
|
|
1794
|
+
y: Math.min(Math.max(rect.y, 0), Math.max(vh - EDGE_KEEP, 0))
|
|
1795
|
+
};
|
|
1796
|
+
}
|
|
1797
|
+
function centred() {
|
|
1798
|
+
const { width: vw, height: vh } = viewport();
|
|
1799
|
+
const width = Math.min(DEFAULT_WIDTH, Math.max(vw - 32, MIN_WIDTH));
|
|
1800
|
+
const height = Math.min(DEFAULT_HEIGHT, Math.max(vh - 32, MIN_HEIGHT));
|
|
1801
|
+
return clamp({
|
|
1802
|
+
width,
|
|
1803
|
+
height,
|
|
1804
|
+
x: Math.round((vw - width) / 2),
|
|
1805
|
+
y: Math.round((vh - height) / 2)
|
|
1806
|
+
});
|
|
1807
|
+
}
|
|
1808
|
+
function DefaultWindowShell({
|
|
1809
|
+
id,
|
|
1810
|
+
title,
|
|
1811
|
+
onClose,
|
|
1812
|
+
icon,
|
|
1813
|
+
children,
|
|
1814
|
+
className
|
|
1815
|
+
}) {
|
|
1816
|
+
const panelRef = useRef7(null);
|
|
1817
|
+
const [mounted, setMounted] = useState6(false);
|
|
1818
|
+
const [isMobile, setIsMobile] = useState6(false);
|
|
1819
|
+
const [rect, setRect] = useState6(() => centred());
|
|
1820
|
+
const [maximized, setMaximized] = useState6(false);
|
|
1821
|
+
const gesture = useRef7(null);
|
|
1822
|
+
useLayoutEffect2(() => {
|
|
1823
|
+
setMounted(true);
|
|
1824
|
+
setIsMobile(window.innerWidth < MOBILE_MAX_WIDTH);
|
|
1825
|
+
setRect(centred());
|
|
1826
|
+
}, []);
|
|
1827
|
+
useEffect9(() => {
|
|
1828
|
+
if (!mounted) return;
|
|
1829
|
+
const onResize = () => {
|
|
1830
|
+
setIsMobile(window.innerWidth < MOBILE_MAX_WIDTH);
|
|
1831
|
+
setRect((current) => clamp(current));
|
|
1832
|
+
};
|
|
1833
|
+
window.addEventListener("resize", onResize);
|
|
1834
|
+
return () => window.removeEventListener("resize", onResize);
|
|
1835
|
+
}, [mounted]);
|
|
1836
|
+
useEffect9(() => {
|
|
1837
|
+
const onKeyDown = (event) => {
|
|
1838
|
+
if (event.key === "Escape") onClose();
|
|
1839
|
+
};
|
|
1840
|
+
window.addEventListener("keydown", onKeyDown);
|
|
1841
|
+
return () => window.removeEventListener("keydown", onKeyDown);
|
|
1842
|
+
}, [onClose]);
|
|
1843
|
+
useEffect9(() => {
|
|
1844
|
+
panelRef.current?.focus({ preventScroll: true });
|
|
1845
|
+
}, [mounted]);
|
|
1846
|
+
const beginGesture = useCallback2(
|
|
1847
|
+
(mode) => (event) => {
|
|
1848
|
+
if (isMobile || maximized || event.button !== 0) return;
|
|
1849
|
+
event.preventDefault();
|
|
1850
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
1851
|
+
gesture.current = {
|
|
1852
|
+
mode,
|
|
1853
|
+
pointerId: event.pointerId,
|
|
1854
|
+
startX: event.clientX,
|
|
1855
|
+
startY: event.clientY,
|
|
1856
|
+
origin: rect
|
|
1857
|
+
};
|
|
1858
|
+
},
|
|
1859
|
+
[isMobile, maximized, rect]
|
|
1860
|
+
);
|
|
1861
|
+
const onPointerMove = useCallback2((event) => {
|
|
1862
|
+
const active = gesture.current;
|
|
1863
|
+
if (!active || active.pointerId !== event.pointerId) return;
|
|
1864
|
+
const dx = event.clientX - active.startX;
|
|
1865
|
+
const dy = event.clientY - active.startY;
|
|
1866
|
+
setRect(
|
|
1867
|
+
clamp(
|
|
1868
|
+
active.mode === "move" ? { ...active.origin, x: active.origin.x + dx, y: active.origin.y + dy } : {
|
|
1869
|
+
...active.origin,
|
|
1870
|
+
width: active.origin.width + dx,
|
|
1871
|
+
height: active.origin.height + dy
|
|
1872
|
+
}
|
|
1873
|
+
)
|
|
1874
|
+
);
|
|
1875
|
+
}, []);
|
|
1876
|
+
const endGesture = useCallback2((event) => {
|
|
1877
|
+
const active = gesture.current;
|
|
1878
|
+
if (!active || active.pointerId !== event.pointerId) return;
|
|
1879
|
+
gesture.current = null;
|
|
1880
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) {
|
|
1881
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
1882
|
+
}
|
|
1883
|
+
}, []);
|
|
1884
|
+
if (!mounted || typeof document === "undefined") return null;
|
|
1885
|
+
const labelId = `assoc-window-title:${id}`;
|
|
1886
|
+
const style = isMobile ? {} : maximized ? { inset: "1rem", width: "auto", height: "auto" } : {
|
|
1887
|
+
left: rect.x,
|
|
1888
|
+
top: rect.y,
|
|
1889
|
+
width: rect.width,
|
|
1890
|
+
height: rect.height
|
|
1891
|
+
};
|
|
1892
|
+
return createPortal(
|
|
1893
|
+
/* @__PURE__ */ jsxs2(
|
|
1894
|
+
"div",
|
|
1895
|
+
{
|
|
1896
|
+
ref: panelRef,
|
|
1897
|
+
role: "dialog",
|
|
1898
|
+
"aria-labelledby": labelId,
|
|
1899
|
+
tabIndex: -1,
|
|
1900
|
+
style,
|
|
1901
|
+
className: cn(
|
|
1902
|
+
// Non-modal by design: no backdrop element exists, so the page
|
|
1903
|
+
// behind keeps every click. `pointer-events-auto` re-asserts our
|
|
1904
|
+
// own subtree in case a Radix surface left <body> inert.
|
|
1905
|
+
"pointer-events-auto fixed z-50 flex flex-col overflow-hidden border border-border bg-card text-card-foreground shadow-xl outline-none",
|
|
1906
|
+
isMobile ? "inset-x-0 bottom-0 max-h-[85dvh] rounded-t-xl" : "rounded-xl",
|
|
1907
|
+
className
|
|
1908
|
+
),
|
|
1909
|
+
children: [
|
|
1910
|
+
/* @__PURE__ */ jsxs2(
|
|
1911
|
+
"header",
|
|
1912
|
+
{
|
|
1913
|
+
onPointerDown: beginGesture("move"),
|
|
1914
|
+
onPointerMove,
|
|
1915
|
+
onPointerUp: endGesture,
|
|
1916
|
+
onPointerCancel: endGesture,
|
|
1917
|
+
onDoubleClick: () => {
|
|
1918
|
+
if (!isMobile) setMaximized((v) => !v);
|
|
1919
|
+
},
|
|
1920
|
+
className: cn(
|
|
1921
|
+
"flex shrink-0 items-center justify-between gap-2 border-b border-border px-3 py-2 select-none",
|
|
1922
|
+
isMobile ? "" : "cursor-move touch-none"
|
|
1923
|
+
),
|
|
1924
|
+
children: [
|
|
1925
|
+
/* @__PURE__ */ jsxs2(
|
|
1926
|
+
"span",
|
|
1927
|
+
{
|
|
1928
|
+
id: labelId,
|
|
1929
|
+
className: "flex min-w-0 items-center gap-1.5 text-sm font-medium text-foreground",
|
|
1930
|
+
children: [
|
|
1931
|
+
icon,
|
|
1932
|
+
/* @__PURE__ */ jsx4("span", { className: "truncate", children: title })
|
|
1933
|
+
]
|
|
1934
|
+
}
|
|
1935
|
+
),
|
|
1936
|
+
/* @__PURE__ */ jsxs2("span", { className: "flex shrink-0 items-center gap-0.5", children: [
|
|
1937
|
+
isMobile ? null : /* @__PURE__ */ jsx4(
|
|
1938
|
+
"button",
|
|
1939
|
+
{
|
|
1940
|
+
type: "button",
|
|
1941
|
+
onPointerDown: (event) => event.stopPropagation(),
|
|
1942
|
+
onClick: () => setMaximized((v) => !v),
|
|
1943
|
+
"aria-label": maximized ? "Restore window" : "Maximize window",
|
|
1944
|
+
className: "flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
|
|
1945
|
+
children: maximized ? /* @__PURE__ */ jsx4(RestoreIcon, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx4(MaximizeIcon, { className: "h-3.5 w-3.5" })
|
|
1946
|
+
}
|
|
1947
|
+
),
|
|
1948
|
+
/* @__PURE__ */ jsx4(
|
|
1949
|
+
"button",
|
|
1950
|
+
{
|
|
1951
|
+
type: "button",
|
|
1952
|
+
onPointerDown: (event) => event.stopPropagation(),
|
|
1953
|
+
onClick: onClose,
|
|
1954
|
+
"aria-label": "Close",
|
|
1955
|
+
className: "flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
|
|
1956
|
+
children: /* @__PURE__ */ jsx4(XIcon, { className: "h-3.5 w-3.5" })
|
|
1957
|
+
}
|
|
1958
|
+
)
|
|
1959
|
+
] })
|
|
1960
|
+
]
|
|
1961
|
+
}
|
|
1962
|
+
),
|
|
1963
|
+
/* @__PURE__ */ jsx4("div", { className: "min-h-0 flex-1 overflow-hidden", children }),
|
|
1964
|
+
isMobile || maximized ? null : /* @__PURE__ */ jsx4(
|
|
1965
|
+
"button",
|
|
1966
|
+
{
|
|
1967
|
+
type: "button",
|
|
1968
|
+
"aria-label": "Resize window",
|
|
1969
|
+
onPointerDown: beginGesture("resize"),
|
|
1970
|
+
onPointerMove,
|
|
1971
|
+
onPointerUp: endGesture,
|
|
1972
|
+
onPointerCancel: endGesture,
|
|
1973
|
+
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",
|
|
1974
|
+
children: /* @__PURE__ */ jsx4(GripResizeIcon, { className: "h-3 w-3" })
|
|
1975
|
+
}
|
|
1976
|
+
)
|
|
1977
|
+
]
|
|
1978
|
+
}
|
|
1979
|
+
),
|
|
1980
|
+
document.body
|
|
1981
|
+
);
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
// src/react/components/AssociationWindow.tsx
|
|
1985
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1539
1986
|
function AssociationWindow({
|
|
1540
1987
|
open,
|
|
1541
1988
|
onClose,
|
|
@@ -1548,78 +1995,51 @@ function AssociationWindow({
|
|
|
1548
1995
|
const instanceId = useId();
|
|
1549
1996
|
const { windowShell } = useAssociationsUiPorts();
|
|
1550
1997
|
if (!open) return null;
|
|
1551
|
-
const body = /* @__PURE__ */
|
|
1552
|
-
subtitle ? /* @__PURE__ */
|
|
1998
|
+
const body = /* @__PURE__ */ jsxs3("div", { className: "flex h-full min-h-0 flex-col gap-2 p-3", children: [
|
|
1999
|
+
subtitle ? /* @__PURE__ */ jsx5("div", { className: "shrink-0 text-xs text-muted-foreground", children: subtitle }) : null,
|
|
1553
2000
|
children
|
|
1554
2001
|
] });
|
|
2002
|
+
const windowId = `association:${scopeId}:${instanceId}`;
|
|
1555
2003
|
if (windowShell) {
|
|
1556
2004
|
const HostWindow = windowShell.Window;
|
|
1557
|
-
return /* @__PURE__ */
|
|
1558
|
-
HostWindow,
|
|
1559
|
-
{
|
|
1560
|
-
id: `association:${scopeId}:${instanceId}`,
|
|
1561
|
-
title,
|
|
1562
|
-
onClose,
|
|
1563
|
-
children: body
|
|
1564
|
-
}
|
|
1565
|
-
);
|
|
2005
|
+
return /* @__PURE__ */ jsx5(HostWindow, { id: windowId, title, onClose, children: body });
|
|
1566
2006
|
}
|
|
1567
|
-
return /* @__PURE__ */
|
|
1568
|
-
|
|
2007
|
+
return /* @__PURE__ */ jsx5(
|
|
2008
|
+
DefaultWindowShell,
|
|
1569
2009
|
{
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
style: { height: "min(580px, 85dvh)" },
|
|
1576
|
-
children: [
|
|
1577
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-border px-3 py-2", children: [
|
|
1578
|
-
/* @__PURE__ */ jsxs2("span", { className: "flex min-w-0 items-center gap-1.5 text-sm font-medium text-foreground", children: [
|
|
1579
|
-
icon,
|
|
1580
|
-
/* @__PURE__ */ jsx4("span", { className: "truncate", children: title })
|
|
1581
|
-
] }),
|
|
1582
|
-
/* @__PURE__ */ jsx4(
|
|
1583
|
-
"button",
|
|
1584
|
-
{
|
|
1585
|
-
type: "button",
|
|
1586
|
-
onClick: onClose,
|
|
1587
|
-
"aria-label": "Close",
|
|
1588
|
-
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",
|
|
1589
|
-
children: /* @__PURE__ */ jsx4(XIcon, { className: "h-3.5 w-3.5" })
|
|
1590
|
-
}
|
|
1591
|
-
)
|
|
1592
|
-
] }),
|
|
1593
|
-
/* @__PURE__ */ jsx4("div", { className: "min-h-0 flex-1", children: body })
|
|
1594
|
-
]
|
|
2010
|
+
id: windowId,
|
|
2011
|
+
title,
|
|
2012
|
+
onClose,
|
|
2013
|
+
...icon !== void 0 ? { icon } : {},
|
|
2014
|
+
children: body
|
|
1595
2015
|
}
|
|
1596
2016
|
);
|
|
1597
2017
|
}
|
|
1598
2018
|
|
|
1599
2019
|
// src/react/components/AssociationPicker.tsx
|
|
1600
|
-
import { useState as
|
|
2020
|
+
import { useState as useState7 } from "react";
|
|
1601
2021
|
import { cn as cn2, Input } from "@ai-matrx/design-system";
|
|
1602
|
-
import { Fragment, jsx as
|
|
2022
|
+
import { Fragment, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1603
2023
|
function AssociationPicker(props) {
|
|
1604
2024
|
const store = useAssociationsStore();
|
|
1605
2025
|
const { pickerOverrides } = useAssociationsUiPorts();
|
|
1606
2026
|
const info = store.registry.getEntityInfo(props.token);
|
|
1607
2027
|
const Icon = info.Icon ?? DefaultEntityIcon;
|
|
1608
2028
|
const Override = pickerOverrides?.[props.token];
|
|
1609
|
-
if (Override) return /* @__PURE__ */
|
|
2029
|
+
if (Override) return /* @__PURE__ */ jsx6(Override, { ...props });
|
|
1610
2030
|
const title = `Add ${info.labelPlural}`;
|
|
1611
2031
|
const subtitle = props.containerLabel ? `Attach to ${props.containerLabel}` : "Click an item to attach or detach it";
|
|
1612
2032
|
if (!props.open) return null;
|
|
1613
|
-
return /* @__PURE__ */
|
|
2033
|
+
return /* @__PURE__ */ jsx6(
|
|
1614
2034
|
AssociationWindow,
|
|
1615
2035
|
{
|
|
1616
2036
|
open: props.open,
|
|
1617
2037
|
onClose: () => props.onOpenChange(false),
|
|
1618
2038
|
scopeId: `picker:${props.token}`,
|
|
1619
2039
|
title,
|
|
1620
|
-
icon: /* @__PURE__ */
|
|
2040
|
+
icon: /* @__PURE__ */ jsx6(Icon, { className: "size-3.5 text-primary" }),
|
|
1621
2041
|
subtitle,
|
|
1622
|
-
children: /* @__PURE__ */
|
|
2042
|
+
children: /* @__PURE__ */ jsx6(
|
|
1623
2043
|
AssociationCandidateBody,
|
|
1624
2044
|
{
|
|
1625
2045
|
token: props.token,
|
|
@@ -1644,7 +2064,7 @@ function AssociationCandidateBody({
|
|
|
1644
2064
|
onClose
|
|
1645
2065
|
}) {
|
|
1646
2066
|
void onClose;
|
|
1647
|
-
return /* @__PURE__ */
|
|
2067
|
+
return /* @__PURE__ */ jsx6(
|
|
1648
2068
|
EntityCandidateList,
|
|
1649
2069
|
{
|
|
1650
2070
|
token,
|
|
@@ -1666,8 +2086,8 @@ function EntityCandidateList({
|
|
|
1666
2086
|
}) {
|
|
1667
2087
|
const store = useAssociationsStore();
|
|
1668
2088
|
const notifier = useNotifier();
|
|
1669
|
-
const [search, setSearch] =
|
|
1670
|
-
const [busyId, setBusyId] =
|
|
2089
|
+
const [search, setSearch] = useState7("");
|
|
2090
|
+
const [busyId, setBusyId] = useState7(null);
|
|
1671
2091
|
const info = store.registry.getEntityInfo(token);
|
|
1672
2092
|
const Icon = info.Icon ?? DefaultEntityIcon;
|
|
1673
2093
|
const { candidates, loading, error, reload } = useAssociationCandidates({
|
|
@@ -1691,10 +2111,10 @@ function EntityCandidateList({
|
|
|
1691
2111
|
setBusyId(null);
|
|
1692
2112
|
}
|
|
1693
2113
|
};
|
|
1694
|
-
return /* @__PURE__ */
|
|
1695
|
-
/* @__PURE__ */
|
|
1696
|
-
/* @__PURE__ */
|
|
1697
|
-
/* @__PURE__ */
|
|
2114
|
+
return /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
2115
|
+
/* @__PURE__ */ jsxs4("div", { className: "relative mb-2", children: [
|
|
2116
|
+
/* @__PURE__ */ jsx6(SearchIcon, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }),
|
|
2117
|
+
/* @__PURE__ */ jsx6(
|
|
1698
2118
|
Input,
|
|
1699
2119
|
{
|
|
1700
2120
|
value: search,
|
|
@@ -1705,13 +2125,13 @@ function EntityCandidateList({
|
|
|
1705
2125
|
}
|
|
1706
2126
|
)
|
|
1707
2127
|
] }),
|
|
1708
|
-
/* @__PURE__ */
|
|
1709
|
-
/* @__PURE__ */
|
|
2128
|
+
/* @__PURE__ */ jsx6("div", { className: "flex-1 min-h-0 overflow-y-auto -mx-1 px-1", children: loading && candidates.length === 0 ? /* @__PURE__ */ jsxs4(ListMessage, { children: [
|
|
2129
|
+
/* @__PURE__ */ jsx6(SpinnerIcon, { className: "h-4 w-4 animate-spin" }),
|
|
1710
2130
|
"Loading\u2026"
|
|
1711
|
-
] }) : error ? /* @__PURE__ */
|
|
1712
|
-
/* @__PURE__ */
|
|
1713
|
-
/* @__PURE__ */
|
|
1714
|
-
/* @__PURE__ */
|
|
2131
|
+
] }) : error ? /* @__PURE__ */ jsxs4("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-[12px] text-destructive", children: [
|
|
2132
|
+
/* @__PURE__ */ jsx6("p", { className: "font-medium", children: "Couldn\u2019t load items" }),
|
|
2133
|
+
/* @__PURE__ */ jsx6("p", { className: "opacity-80", children: error }),
|
|
2134
|
+
/* @__PURE__ */ jsx6(
|
|
1715
2135
|
"button",
|
|
1716
2136
|
{
|
|
1717
2137
|
type: "button",
|
|
@@ -1720,10 +2140,10 @@ function EntityCandidateList({
|
|
|
1720
2140
|
children: "Retry"
|
|
1721
2141
|
}
|
|
1722
2142
|
)
|
|
1723
|
-
] }) : candidates.length === 0 ? /* @__PURE__ */
|
|
2143
|
+
] }) : candidates.length === 0 ? /* @__PURE__ */ jsx6(ListMessage, { children: canCreate ? `Nothing to attach yet \u2014 create a new ${info.label.toLowerCase()} below.` : "Nothing to attach." }) : /* @__PURE__ */ jsx6("ul", { className: "space-y-0.5", children: candidates.map((c) => {
|
|
1724
2144
|
const attached = attachedIds.has(c.id);
|
|
1725
2145
|
const busy = busyId === c.id;
|
|
1726
|
-
return /* @__PURE__ */
|
|
2146
|
+
return /* @__PURE__ */ jsx6("li", { children: /* @__PURE__ */ jsxs4(
|
|
1727
2147
|
"button",
|
|
1728
2148
|
{
|
|
1729
2149
|
type: "button",
|
|
@@ -1735,23 +2155,23 @@ function EntityCandidateList({
|
|
|
1735
2155
|
attached && "bg-accent/40"
|
|
1736
2156
|
),
|
|
1737
2157
|
children: [
|
|
1738
|
-
/* @__PURE__ */
|
|
1739
|
-
/* @__PURE__ */
|
|
1740
|
-
/* @__PURE__ */
|
|
2158
|
+
/* @__PURE__ */ jsx6(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
2159
|
+
/* @__PURE__ */ jsx6("span", { className: "flex-1 min-w-0 truncate text-foreground", children: c.title }),
|
|
2160
|
+
/* @__PURE__ */ jsx6(
|
|
1741
2161
|
"span",
|
|
1742
2162
|
{
|
|
1743
2163
|
className: cn2(
|
|
1744
2164
|
"flex h-5 w-5 items-center justify-center rounded-full shrink-0",
|
|
1745
2165
|
attached ? "bg-primary text-primary-foreground" : "border border-border text-muted-foreground group-hover:border-primary/60"
|
|
1746
2166
|
),
|
|
1747
|
-
children: busy ? /* @__PURE__ */
|
|
2167
|
+
children: busy ? /* @__PURE__ */ jsx6(SpinnerIcon, { className: "h-3 w-3 animate-spin" }) : attached ? /* @__PURE__ */ jsx6(CheckIcon, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx6(PlusIcon, { className: "h-3 w-3" })
|
|
1748
2168
|
}
|
|
1749
2169
|
)
|
|
1750
2170
|
]
|
|
1751
2171
|
}
|
|
1752
2172
|
) }, c.id);
|
|
1753
2173
|
}) }) }),
|
|
1754
|
-
canCreate && /* @__PURE__ */
|
|
2174
|
+
canCreate && /* @__PURE__ */ jsx6(
|
|
1755
2175
|
CreateAndAttachFooter,
|
|
1756
2176
|
{
|
|
1757
2177
|
token,
|
|
@@ -1773,9 +2193,9 @@ function CreateAndAttachFooter({
|
|
|
1773
2193
|
const store = useAssociationsStore();
|
|
1774
2194
|
const notifier = useNotifier();
|
|
1775
2195
|
const info = store.registry.getEntityInfo(token);
|
|
1776
|
-
const [editing, setEditing] =
|
|
1777
|
-
const [name, setName] =
|
|
1778
|
-
const [busy, setBusy] =
|
|
2196
|
+
const [editing, setEditing] = useState7(false);
|
|
2197
|
+
const [name, setName] = useState7("");
|
|
2198
|
+
const [busy, setBusy] = useState7(false);
|
|
1779
2199
|
const submit = async () => {
|
|
1780
2200
|
const title = name.trim();
|
|
1781
2201
|
if (!title || busy) return;
|
|
@@ -1810,7 +2230,7 @@ function CreateAndAttachFooter({
|
|
|
1810
2230
|
}
|
|
1811
2231
|
};
|
|
1812
2232
|
if (!editing) {
|
|
1813
|
-
return /* @__PURE__ */
|
|
2233
|
+
return /* @__PURE__ */ jsxs4(
|
|
1814
2234
|
"button",
|
|
1815
2235
|
{
|
|
1816
2236
|
type: "button",
|
|
@@ -1820,10 +2240,10 @@ function CreateAndAttachFooter({
|
|
|
1820
2240
|
},
|
|
1821
2241
|
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",
|
|
1822
2242
|
children: [
|
|
1823
|
-
/* @__PURE__ */
|
|
2243
|
+
/* @__PURE__ */ jsx6(PlusIcon, { className: "h-4 w-4" }),
|
|
1824
2244
|
"New ",
|
|
1825
2245
|
info.label,
|
|
1826
|
-
seed ? /* @__PURE__ */
|
|
2246
|
+
seed ? /* @__PURE__ */ jsxs4("span", { className: "max-w-40 truncate text-muted-foreground/70", children: [
|
|
1827
2247
|
"\u201C",
|
|
1828
2248
|
seed,
|
|
1829
2249
|
"\u201D"
|
|
@@ -1832,8 +2252,8 @@ function CreateAndAttachFooter({
|
|
|
1832
2252
|
}
|
|
1833
2253
|
);
|
|
1834
2254
|
}
|
|
1835
|
-
return /* @__PURE__ */
|
|
1836
|
-
/* @__PURE__ */
|
|
2255
|
+
return /* @__PURE__ */ jsxs4("div", { className: "mt-2 flex shrink-0 items-center gap-1.5", children: [
|
|
2256
|
+
/* @__PURE__ */ jsx6(
|
|
1837
2257
|
Input,
|
|
1838
2258
|
{
|
|
1839
2259
|
autoFocus: true,
|
|
@@ -1852,17 +2272,17 @@ function CreateAndAttachFooter({
|
|
|
1852
2272
|
style: { fontSize: 16 }
|
|
1853
2273
|
}
|
|
1854
2274
|
),
|
|
1855
|
-
/* @__PURE__ */
|
|
2275
|
+
/* @__PURE__ */ jsx6(
|
|
1856
2276
|
"button",
|
|
1857
2277
|
{
|
|
1858
2278
|
type: "button",
|
|
1859
2279
|
disabled: busy || !name.trim(),
|
|
1860
2280
|
onClick: () => void submit(),
|
|
1861
2281
|
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",
|
|
1862
|
-
children: busy ? /* @__PURE__ */
|
|
2282
|
+
children: busy ? /* @__PURE__ */ jsx6(SpinnerIcon, { className: "h-3.5 w-3.5 animate-spin" }) : "Create"
|
|
1863
2283
|
}
|
|
1864
2284
|
),
|
|
1865
|
-
/* @__PURE__ */
|
|
2285
|
+
/* @__PURE__ */ jsx6(
|
|
1866
2286
|
"button",
|
|
1867
2287
|
{
|
|
1868
2288
|
type: "button",
|
|
@@ -1873,38 +2293,122 @@ function CreateAndAttachFooter({
|
|
|
1873
2293
|
},
|
|
1874
2294
|
title: "Cancel",
|
|
1875
2295
|
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",
|
|
1876
|
-
children: /* @__PURE__ */
|
|
2296
|
+
children: /* @__PURE__ */ jsx6(XIcon, { className: "h-4 w-4" })
|
|
1877
2297
|
}
|
|
1878
2298
|
)
|
|
1879
2299
|
] });
|
|
1880
2300
|
}
|
|
1881
2301
|
function ListMessage({ children }) {
|
|
1882
|
-
return /* @__PURE__ */
|
|
2302
|
+
return /* @__PURE__ */ jsx6("div", { className: "flex items-center justify-center gap-2 py-8 text-[13px] text-muted-foreground", children });
|
|
1883
2303
|
}
|
|
1884
2304
|
|
|
1885
2305
|
// src/react/components/UniversalAssociationPicker.tsx
|
|
1886
|
-
import { useState as
|
|
1887
|
-
import { cn as
|
|
1888
|
-
|
|
2306
|
+
import { useState as useState8 } from "react";
|
|
2307
|
+
import { cn as cn4, Input as Input2 } from "@ai-matrx/design-system";
|
|
2308
|
+
|
|
2309
|
+
// src/react/components/entityDoors.tsx
|
|
2310
|
+
import { cn as cn3 } from "@ai-matrx/design-system";
|
|
2311
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2312
|
+
function DoorRef(props) {
|
|
2313
|
+
const { onOpen, ...refProps } = props;
|
|
2314
|
+
const { entityDoors } = useAssociationsUiPorts();
|
|
2315
|
+
const store = useAssociationsStore();
|
|
2316
|
+
const HostRef = entityDoors?.EntityRef;
|
|
2317
|
+
if (HostRef) return /* @__PURE__ */ jsx7(HostRef, { ...refProps });
|
|
2318
|
+
const name = refProps.name ?? null;
|
|
2319
|
+
const href = store.registry.tryGetEntityInfo(refProps.token)?.hrefFor?.(refProps.id) ?? null;
|
|
2320
|
+
if (onOpen) {
|
|
2321
|
+
return /* @__PURE__ */ jsx7(
|
|
2322
|
+
"button",
|
|
2323
|
+
{
|
|
2324
|
+
type: "button",
|
|
2325
|
+
onClick: onOpen,
|
|
2326
|
+
className: cn3(
|
|
2327
|
+
"min-w-0 truncate text-left underline-offset-2 hover:underline",
|
|
2328
|
+
refProps.className
|
|
2329
|
+
),
|
|
2330
|
+
children: name
|
|
2331
|
+
}
|
|
2332
|
+
);
|
|
2333
|
+
}
|
|
2334
|
+
if (href) {
|
|
2335
|
+
return /* @__PURE__ */ jsxs5(
|
|
2336
|
+
"a",
|
|
2337
|
+
{
|
|
2338
|
+
href,
|
|
2339
|
+
target: refProps.openInNewTab === false ? void 0 : "_blank",
|
|
2340
|
+
rel: "noopener noreferrer",
|
|
2341
|
+
className: cn3(
|
|
2342
|
+
"inline-flex min-w-0 items-center gap-1 truncate underline-offset-2 hover:underline",
|
|
2343
|
+
refProps.className
|
|
2344
|
+
),
|
|
2345
|
+
children: [
|
|
2346
|
+
/* @__PURE__ */ jsx7("span", { className: "min-w-0 truncate", children: name }),
|
|
2347
|
+
/* @__PURE__ */ jsx7(ExternalLinkIcon, { className: "h-3 w-3 shrink-0 opacity-0 transition-opacity group-hover:opacity-60" })
|
|
2348
|
+
]
|
|
2349
|
+
}
|
|
2350
|
+
);
|
|
2351
|
+
}
|
|
2352
|
+
return /* @__PURE__ */ jsx7("span", { className: cn3("min-w-0 truncate", refProps.className), children: name });
|
|
2353
|
+
}
|
|
2354
|
+
function UnresolvedRef({ token, id }) {
|
|
2355
|
+
const { entityDoors } = useAssociationsUiPorts();
|
|
2356
|
+
const HostUnresolved = entityDoors?.UnresolvedRef;
|
|
2357
|
+
if (HostUnresolved) return /* @__PURE__ */ jsx7(HostUnresolved, { token, id });
|
|
2358
|
+
return /* @__PURE__ */ jsx7(
|
|
2359
|
+
"span",
|
|
2360
|
+
{
|
|
2361
|
+
className: "inline-flex items-center rounded border border-border/60 bg-muted/40 px-1.5 py-0.5 text-[11px] text-muted-foreground",
|
|
2362
|
+
title: `This ${token} could not be read \u2014 it may be deleted or not shared with you.`,
|
|
2363
|
+
children: "Unavailable"
|
|
2364
|
+
}
|
|
2365
|
+
);
|
|
2366
|
+
}
|
|
2367
|
+
function DoorControlsSlot(props) {
|
|
2368
|
+
const { entityDoors } = useAssociationsUiPorts();
|
|
2369
|
+
const store = useAssociationsStore();
|
|
2370
|
+
const HostControls = entityDoors?.DoorControls;
|
|
2371
|
+
if (HostControls) return /* @__PURE__ */ jsx7(HostControls, { ...props });
|
|
2372
|
+
const href = store.registry.tryGetEntityInfo(props.token)?.hrefFor?.(props.id) ?? null;
|
|
2373
|
+
if (!href) return null;
|
|
2374
|
+
const label = props.name ? `Open ${props.name}` : `Open this ${props.token}`;
|
|
2375
|
+
return /* @__PURE__ */ jsx7(
|
|
2376
|
+
"a",
|
|
2377
|
+
{
|
|
2378
|
+
href,
|
|
2379
|
+
target: "_blank",
|
|
2380
|
+
rel: "noopener noreferrer",
|
|
2381
|
+
"aria-label": label,
|
|
2382
|
+
title: label,
|
|
2383
|
+
className: cn3(
|
|
2384
|
+
"inline-flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
|
|
2385
|
+
props.className
|
|
2386
|
+
),
|
|
2387
|
+
children: /* @__PURE__ */ jsx7(ExternalLinkIcon, { className: "h-3 w-3" })
|
|
2388
|
+
}
|
|
2389
|
+
);
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
// src/react/components/UniversalAssociationPicker.tsx
|
|
2393
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1889
2394
|
function attachedKey(token, id) {
|
|
1890
2395
|
return `${token}:${id}`;
|
|
1891
2396
|
}
|
|
1892
2397
|
function UniversalAssociationPicker(props) {
|
|
1893
2398
|
const { attachedKeys, onAttach, onDetach, ownerId, orgId, className } = props;
|
|
1894
2399
|
const store = useAssociationsStore();
|
|
1895
|
-
const {
|
|
2400
|
+
const { pickerOverrides } = useAssociationsUiPorts();
|
|
1896
2401
|
const notifier = useNotifier();
|
|
1897
2402
|
const tokens = props.tokens ?? store.registry.curatedTokens();
|
|
1898
|
-
const [query, setQuery] =
|
|
1899
|
-
const [browseToken, setBrowseToken] =
|
|
1900
|
-
const [busyKey, setBusyKey] =
|
|
2403
|
+
const [query, setQuery] = useState8("");
|
|
2404
|
+
const [browseToken, setBrowseToken] = useState8(null);
|
|
2405
|
+
const [busyKey, setBusyKey] = useState8(null);
|
|
1901
2406
|
const { results, loading, isRecents } = useUniversalEntitySearch({
|
|
1902
2407
|
query,
|
|
1903
2408
|
tokens,
|
|
1904
2409
|
ownerId: ownerId ?? null,
|
|
1905
2410
|
enabled: browseToken === null
|
|
1906
2411
|
});
|
|
1907
|
-
const DoorControls = entityDoors?.DoorControls;
|
|
1908
2412
|
const toggle = async (c) => {
|
|
1909
2413
|
const key = attachedKey(c.token, c.id);
|
|
1910
2414
|
if (busyKey) return;
|
|
@@ -1928,10 +2432,10 @@ function UniversalAssociationPicker(props) {
|
|
|
1928
2432
|
groups.set(r.token, list);
|
|
1929
2433
|
}
|
|
1930
2434
|
const BrowseOverride = browseToken ? pickerOverrides?.[browseToken] : void 0;
|
|
1931
|
-
return /* @__PURE__ */
|
|
1932
|
-
/* @__PURE__ */
|
|
1933
|
-
/* @__PURE__ */
|
|
1934
|
-
/* @__PURE__ */
|
|
2435
|
+
return /* @__PURE__ */ jsxs6("div", { className: cn4("flex min-h-0 flex-1 flex-col gap-2", className), children: [
|
|
2436
|
+
/* @__PURE__ */ jsxs6("div", { className: "relative", children: [
|
|
2437
|
+
/* @__PURE__ */ jsx8(SearchIcon, { className: "absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }),
|
|
2438
|
+
/* @__PURE__ */ jsx8(
|
|
1935
2439
|
Input2,
|
|
1936
2440
|
{
|
|
1937
2441
|
value: query,
|
|
@@ -1944,23 +2448,23 @@ function UniversalAssociationPicker(props) {
|
|
|
1944
2448
|
style: { fontSize: 16 }
|
|
1945
2449
|
}
|
|
1946
2450
|
),
|
|
1947
|
-
loading && browseToken === null && /* @__PURE__ */
|
|
2451
|
+
loading && browseToken === null && /* @__PURE__ */ jsx8(SpinnerIcon, { className: "absolute right-2.5 top-1/2 h-4 w-4 -translate-y-1/2 animate-spin text-muted-foreground" })
|
|
1948
2452
|
] }),
|
|
1949
|
-
/* @__PURE__ */
|
|
2453
|
+
/* @__PURE__ */ jsx8("div", { className: "flex flex-wrap gap-1", children: tokens.map((t) => {
|
|
1950
2454
|
const info = store.registry.getEntityInfo(t);
|
|
1951
2455
|
const ChipIcon = info.Icon ?? DefaultEntityIcon;
|
|
1952
2456
|
const active = browseToken === t;
|
|
1953
|
-
return /* @__PURE__ */
|
|
2457
|
+
return /* @__PURE__ */ jsxs6(
|
|
1954
2458
|
"button",
|
|
1955
2459
|
{
|
|
1956
2460
|
type: "button",
|
|
1957
2461
|
onClick: () => setBrowseToken(active ? null : t),
|
|
1958
|
-
className:
|
|
2462
|
+
className: cn4(
|
|
1959
2463
|
"inline-flex items-center gap-1 rounded-md border px-1.5 py-0.5 text-[11px] transition-colors",
|
|
1960
2464
|
active ? "border-primary/50 bg-primary/10 text-foreground" : "border-border text-muted-foreground hover:text-foreground hover:bg-accent"
|
|
1961
2465
|
),
|
|
1962
2466
|
children: [
|
|
1963
|
-
/* @__PURE__ */
|
|
2467
|
+
/* @__PURE__ */ jsx8(ChipIcon, { className: "h-3 w-3" }),
|
|
1964
2468
|
info.labelPlural
|
|
1965
2469
|
]
|
|
1966
2470
|
},
|
|
@@ -1970,7 +2474,7 @@ function UniversalAssociationPicker(props) {
|
|
|
1970
2474
|
browseToken && BrowseOverride ? (
|
|
1971
2475
|
// The token's canonical host picker (e.g. the stored-files browser)
|
|
1972
2476
|
// owns per-token browsing when registered — never a plain list twin.
|
|
1973
|
-
/* @__PURE__ */
|
|
2477
|
+
/* @__PURE__ */ jsx8(
|
|
1974
2478
|
BrowseOverride,
|
|
1975
2479
|
{
|
|
1976
2480
|
open: true,
|
|
@@ -1986,7 +2490,7 @@ function UniversalAssociationPicker(props) {
|
|
|
1986
2490
|
onDetach: (id) => onDetach(browseToken, id)
|
|
1987
2491
|
}
|
|
1988
2492
|
)
|
|
1989
|
-
) : browseToken ? /* @__PURE__ */
|
|
2493
|
+
) : browseToken ? /* @__PURE__ */ jsx8("div", { className: "flex min-h-0 flex-1 flex-col", children: /* @__PURE__ */ jsx8(
|
|
1990
2494
|
AssociationCandidateBody,
|
|
1991
2495
|
{
|
|
1992
2496
|
token: browseToken,
|
|
@@ -1999,63 +2503,63 @@ function UniversalAssociationPicker(props) {
|
|
|
1999
2503
|
onAttach: (id, title) => onAttach(browseToken, id, title),
|
|
2000
2504
|
onDetach: (id) => onDetach(browseToken, id)
|
|
2001
2505
|
}
|
|
2002
|
-
) }) : /* @__PURE__ */
|
|
2003
|
-
isRecents && /* @__PURE__ */
|
|
2506
|
+
) }) : /* @__PURE__ */ jsx8("div", { className: "-mx-1 min-h-0 flex-1 overflow-y-auto px-1", children: results.length === 0 ? /* @__PURE__ */ jsx8("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__ */ jsxs6("div", { className: "space-y-2", children: [
|
|
2507
|
+
isRecents && /* @__PURE__ */ jsx8("p", { className: "px-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/60", children: "Recent" }),
|
|
2004
2508
|
[...groups.entries()].map(([token, items]) => {
|
|
2005
2509
|
const info = store.registry.getEntityInfo(token);
|
|
2006
2510
|
const GroupIcon = info.Icon ?? DefaultEntityIcon;
|
|
2007
|
-
return /* @__PURE__ */
|
|
2008
|
-
/* @__PURE__ */
|
|
2009
|
-
/* @__PURE__ */
|
|
2511
|
+
return /* @__PURE__ */ jsxs6("div", { className: "space-y-0.5", children: [
|
|
2512
|
+
/* @__PURE__ */ jsxs6("p", { className: "flex items-center gap-1 px-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/70", children: [
|
|
2513
|
+
/* @__PURE__ */ jsx8(GroupIcon, { className: "h-3 w-3" }),
|
|
2010
2514
|
info.labelPlural
|
|
2011
2515
|
] }),
|
|
2012
|
-
/* @__PURE__ */
|
|
2516
|
+
/* @__PURE__ */ jsx8("ul", { className: "space-y-0.5", children: items.map((c) => {
|
|
2013
2517
|
const key = attachedKey(c.token, c.id);
|
|
2014
2518
|
const attached = attachedKeys.has(key);
|
|
2015
2519
|
const busy = busyKey === key;
|
|
2016
|
-
return /* @__PURE__ */
|
|
2520
|
+
return /* @__PURE__ */ jsxs6(
|
|
2017
2521
|
"li",
|
|
2018
2522
|
{
|
|
2019
|
-
className:
|
|
2523
|
+
className: cn4(
|
|
2020
2524
|
"group/entity-ref group flex items-center gap-1 rounded-md pr-1.5 transition-colors",
|
|
2021
2525
|
"hover:bg-accent",
|
|
2022
2526
|
attached && "bg-accent/40"
|
|
2023
2527
|
),
|
|
2024
2528
|
children: [
|
|
2025
|
-
/* @__PURE__ */
|
|
2529
|
+
/* @__PURE__ */ jsxs6(
|
|
2026
2530
|
"button",
|
|
2027
2531
|
{
|
|
2028
2532
|
type: "button",
|
|
2029
2533
|
disabled: busy,
|
|
2030
2534
|
onClick: () => toggle(c),
|
|
2031
2535
|
title: attached ? `Detach "${c.title}"` : `Attach "${c.title}"`,
|
|
2032
|
-
className:
|
|
2536
|
+
className: cn4(
|
|
2033
2537
|
"flex min-w-0 flex-1 items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-sm",
|
|
2034
2538
|
"disabled:opacity-50"
|
|
2035
2539
|
),
|
|
2036
2540
|
children: [
|
|
2037
|
-
/* @__PURE__ */
|
|
2038
|
-
/* @__PURE__ */
|
|
2541
|
+
/* @__PURE__ */ jsx8("span", { className: "min-w-0 flex-1 truncate text-foreground", children: c.title }),
|
|
2542
|
+
/* @__PURE__ */ jsx8(
|
|
2039
2543
|
"span",
|
|
2040
2544
|
{
|
|
2041
|
-
className:
|
|
2545
|
+
className: cn4(
|
|
2042
2546
|
"flex h-5 w-5 shrink-0 items-center justify-center rounded-full",
|
|
2043
2547
|
attached ? "bg-primary text-primary-foreground" : "border border-border text-muted-foreground group-hover:border-primary/60"
|
|
2044
2548
|
),
|
|
2045
|
-
children: busy ? /* @__PURE__ */
|
|
2549
|
+
children: busy ? /* @__PURE__ */ jsx8(SpinnerIcon, { className: "h-3 w-3 animate-spin" }) : attached ? /* @__PURE__ */ jsx8(CheckIcon, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx8(PlusIcon, { className: "h-3 w-3" })
|
|
2046
2550
|
}
|
|
2047
2551
|
)
|
|
2048
2552
|
]
|
|
2049
2553
|
}
|
|
2050
2554
|
),
|
|
2051
|
-
|
|
2052
|
-
|
|
2555
|
+
/* @__PURE__ */ jsx8(
|
|
2556
|
+
DoorControlsSlot,
|
|
2053
2557
|
{
|
|
2054
2558
|
token: c.token,
|
|
2055
2559
|
id: c.id,
|
|
2056
2560
|
name: c.title
|
|
2057
2561
|
}
|
|
2058
|
-
)
|
|
2562
|
+
)
|
|
2059
2563
|
]
|
|
2060
2564
|
},
|
|
2061
2565
|
key
|
|
@@ -2068,79 +2572,18 @@ function UniversalAssociationPicker(props) {
|
|
|
2068
2572
|
}
|
|
2069
2573
|
|
|
2070
2574
|
// src/react/components/AttachedItemsSheet.tsx
|
|
2071
|
-
import { useEffect as
|
|
2575
|
+
import { useEffect as useEffect10, useState as useState9 } from "react";
|
|
2072
2576
|
import { cn as cn5 } from "@ai-matrx/design-system";
|
|
2073
|
-
|
|
2074
|
-
// src/react/components/entityDoors.tsx
|
|
2075
|
-
import { cn as cn4 } from "@ai-matrx/design-system";
|
|
2076
|
-
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2077
|
-
function DoorRef(props) {
|
|
2078
|
-
const { onOpen, ...refProps } = props;
|
|
2079
|
-
const { entityDoors } = useAssociationsUiPorts();
|
|
2080
|
-
const store = useAssociationsStore();
|
|
2081
|
-
const HostRef = entityDoors?.EntityRef;
|
|
2082
|
-
if (HostRef) return /* @__PURE__ */ jsx7(HostRef, { ...refProps });
|
|
2083
|
-
const name = refProps.name ?? null;
|
|
2084
|
-
const href = store.registry.tryGetEntityInfo(refProps.token)?.hrefFor?.(refProps.id) ?? null;
|
|
2085
|
-
if (onOpen) {
|
|
2086
|
-
return /* @__PURE__ */ jsx7(
|
|
2087
|
-
"button",
|
|
2088
|
-
{
|
|
2089
|
-
type: "button",
|
|
2090
|
-
onClick: onOpen,
|
|
2091
|
-
className: cn4(
|
|
2092
|
-
"min-w-0 truncate text-left underline-offset-2 hover:underline",
|
|
2093
|
-
refProps.className
|
|
2094
|
-
),
|
|
2095
|
-
children: name
|
|
2096
|
-
}
|
|
2097
|
-
);
|
|
2098
|
-
}
|
|
2099
|
-
if (href) {
|
|
2100
|
-
return /* @__PURE__ */ jsxs5(
|
|
2101
|
-
"a",
|
|
2102
|
-
{
|
|
2103
|
-
href,
|
|
2104
|
-
target: refProps.openInNewTab === false ? void 0 : "_blank",
|
|
2105
|
-
rel: "noopener noreferrer",
|
|
2106
|
-
className: cn4(
|
|
2107
|
-
"inline-flex min-w-0 items-center gap-1 truncate underline-offset-2 hover:underline",
|
|
2108
|
-
refProps.className
|
|
2109
|
-
),
|
|
2110
|
-
children: [
|
|
2111
|
-
/* @__PURE__ */ jsx7("span", { className: "min-w-0 truncate", children: name }),
|
|
2112
|
-
/* @__PURE__ */ jsx7(ExternalLinkIcon, { className: "h-3 w-3 shrink-0 opacity-0 transition-opacity group-hover:opacity-60" })
|
|
2113
|
-
]
|
|
2114
|
-
}
|
|
2115
|
-
);
|
|
2116
|
-
}
|
|
2117
|
-
return /* @__PURE__ */ jsx7("span", { className: cn4("min-w-0 truncate", refProps.className), children: name });
|
|
2118
|
-
}
|
|
2119
|
-
function UnresolvedRef({ token, id }) {
|
|
2120
|
-
const { entityDoors } = useAssociationsUiPorts();
|
|
2121
|
-
const HostUnresolved = entityDoors?.UnresolvedRef;
|
|
2122
|
-
if (HostUnresolved) return /* @__PURE__ */ jsx7(HostUnresolved, { token, id });
|
|
2123
|
-
return /* @__PURE__ */ jsx7(
|
|
2124
|
-
"span",
|
|
2125
|
-
{
|
|
2126
|
-
className: "inline-flex items-center rounded border border-border/60 bg-muted/40 px-1.5 py-0.5 text-[11px] text-muted-foreground",
|
|
2127
|
-
title: `This ${token} could not be read \u2014 it may be deleted or not shared with you.`,
|
|
2128
|
-
children: "Unavailable"
|
|
2129
|
-
}
|
|
2130
|
-
);
|
|
2131
|
-
}
|
|
2132
|
-
|
|
2133
|
-
// src/react/components/AttachedItemsSheet.tsx
|
|
2134
|
-
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2577
|
+
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2135
2578
|
function AttachedItemsSheet(props) {
|
|
2136
2579
|
const store = useAssociationsStore();
|
|
2137
2580
|
const info = store.registry.getEntityInfo(props.token);
|
|
2138
2581
|
const Icon = info.Icon ?? DefaultEntityIcon;
|
|
2139
2582
|
const title = info.labelPlural;
|
|
2140
2583
|
const containerName = props.container?.label ?? props.containerLabel;
|
|
2141
|
-
const subtitle = props.container ? /* @__PURE__ */
|
|
2584
|
+
const subtitle = props.container ? /* @__PURE__ */ jsxs7("span", { className: "inline-flex min-w-0 max-w-full items-center gap-1", children: [
|
|
2142
2585
|
"Attached to",
|
|
2143
|
-
/* @__PURE__ */
|
|
2586
|
+
/* @__PURE__ */ jsx9(
|
|
2144
2587
|
DoorRef,
|
|
2145
2588
|
{
|
|
2146
2589
|
token: props.container.type,
|
|
@@ -2151,16 +2594,16 @@ function AttachedItemsSheet(props) {
|
|
|
2151
2594
|
)
|
|
2152
2595
|
] }) : props.containerLabel ? `Attached to ${props.containerLabel}` : "Attached items";
|
|
2153
2596
|
if (!props.open) return null;
|
|
2154
|
-
return /* @__PURE__ */
|
|
2597
|
+
return /* @__PURE__ */ jsx9(
|
|
2155
2598
|
AssociationWindow,
|
|
2156
2599
|
{
|
|
2157
2600
|
open: props.open,
|
|
2158
2601
|
onClose: () => props.onOpenChange(false),
|
|
2159
2602
|
scopeId: `attached:${props.token}`,
|
|
2160
2603
|
title,
|
|
2161
|
-
icon: /* @__PURE__ */
|
|
2604
|
+
icon: /* @__PURE__ */ jsx9(Icon, { className: "size-3.5 text-primary" }),
|
|
2162
2605
|
subtitle,
|
|
2163
|
-
children: /* @__PURE__ */
|
|
2606
|
+
children: /* @__PURE__ */ jsx9(
|
|
2164
2607
|
AttachedItemsBody,
|
|
2165
2608
|
{
|
|
2166
2609
|
token: props.token,
|
|
@@ -2184,11 +2627,11 @@ function AttachedItemsBody({
|
|
|
2184
2627
|
const notifier = useNotifier();
|
|
2185
2628
|
const info = store.registry.getEntityInfo(token);
|
|
2186
2629
|
const Icon = info.Icon ?? DefaultEntityIcon;
|
|
2187
|
-
const [titles, setTitles] =
|
|
2188
|
-
const [titleError, setTitleError] =
|
|
2189
|
-
const [busyId, setBusyId] =
|
|
2630
|
+
const [titles, setTitles] = useState9(null);
|
|
2631
|
+
const [titleError, setTitleError] = useState9(null);
|
|
2632
|
+
const [busyId, setBusyId] = useState9(null);
|
|
2190
2633
|
const idKey = links.map((l) => l.resourceId).sort().join(",");
|
|
2191
|
-
|
|
2634
|
+
useEffect10(() => {
|
|
2192
2635
|
if (!enabled) return;
|
|
2193
2636
|
const ids = idKey ? idKey.split(",") : [];
|
|
2194
2637
|
if (ids.length === 0) {
|
|
@@ -2232,30 +2675,30 @@ function AttachedItemsBody({
|
|
|
2232
2675
|
setBusyId(null);
|
|
2233
2676
|
}
|
|
2234
2677
|
};
|
|
2235
|
-
return /* @__PURE__ */
|
|
2236
|
-
titleError ? /* @__PURE__ */
|
|
2678
|
+
return /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
2679
|
+
titleError ? /* @__PURE__ */ jsxs7("div", { className: "mb-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-[12px] text-destructive", children: [
|
|
2237
2680
|
"Showing names recorded at attach time \u2014 they may be out of date.",
|
|
2238
2681
|
" ",
|
|
2239
2682
|
titleError
|
|
2240
2683
|
] }) : null,
|
|
2241
|
-
/* @__PURE__ */
|
|
2242
|
-
/* @__PURE__ */
|
|
2684
|
+
/* @__PURE__ */ jsx9("div", { className: "flex-1 min-h-0 overflow-y-auto -mx-1 px-1", children: links.length === 0 ? /* @__PURE__ */ jsx9("div", { className: "flex items-center justify-center py-8 text-[13px] text-muted-foreground", children: "Nothing attached yet." }) : titles === null ? /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-center gap-2 py-8 text-[13px] text-muted-foreground", children: [
|
|
2685
|
+
/* @__PURE__ */ jsx9(SpinnerIcon, { className: "h-4 w-4 animate-spin" }),
|
|
2243
2686
|
"Loading\u2026"
|
|
2244
|
-
] }) : /* @__PURE__ */
|
|
2687
|
+
] }) : /* @__PURE__ */ jsx9("ul", { className: "space-y-0.5", children: links.map((link) => {
|
|
2245
2688
|
const resolved = titles.get(link.resourceId);
|
|
2246
2689
|
const missing = !titles.has(link.resourceId);
|
|
2247
2690
|
const name = resolved ?? link.label ?? (missing ? null : "Untitled");
|
|
2248
2691
|
const busy = busyId === link.resourceId;
|
|
2249
|
-
return /* @__PURE__ */
|
|
2692
|
+
return /* @__PURE__ */ jsxs7(
|
|
2250
2693
|
"li",
|
|
2251
2694
|
{
|
|
2252
2695
|
className: "group/entity-ref group flex items-center gap-2 rounded-md px-2.5 py-2 text-sm transition-colors hover:bg-accent",
|
|
2253
2696
|
children: [
|
|
2254
|
-
/* @__PURE__ */
|
|
2255
|
-
/* @__PURE__ */
|
|
2697
|
+
/* @__PURE__ */ jsx9(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
2698
|
+
/* @__PURE__ */ jsx9("span", { className: "flex-1 min-w-0 truncate", children: name === null ? (
|
|
2256
2699
|
// The X beside the row stays the one-click detach.
|
|
2257
|
-
/* @__PURE__ */
|
|
2258
|
-
) : /* @__PURE__ */
|
|
2700
|
+
/* @__PURE__ */ jsx9(UnresolvedRef, { token, id: link.resourceId })
|
|
2701
|
+
) : /* @__PURE__ */ jsx9(
|
|
2259
2702
|
DoorRef,
|
|
2260
2703
|
{
|
|
2261
2704
|
token,
|
|
@@ -2265,7 +2708,7 @@ function AttachedItemsBody({
|
|
|
2265
2708
|
className: "text-foreground"
|
|
2266
2709
|
}
|
|
2267
2710
|
) }),
|
|
2268
|
-
/* @__PURE__ */
|
|
2711
|
+
/* @__PURE__ */ jsx9(
|
|
2269
2712
|
"button",
|
|
2270
2713
|
{
|
|
2271
2714
|
type: "button",
|
|
@@ -2276,7 +2719,7 @@ function AttachedItemsBody({
|
|
|
2276
2719
|
"flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors",
|
|
2277
2720
|
"hover:bg-destructive/10 hover:text-destructive disabled:opacity-50"
|
|
2278
2721
|
),
|
|
2279
|
-
children: busy ? /* @__PURE__ */
|
|
2722
|
+
children: busy ? /* @__PURE__ */ jsx9(SpinnerIcon, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx9(XIcon, { className: "h-3.5 w-3.5" })
|
|
2280
2723
|
}
|
|
2281
2724
|
)
|
|
2282
2725
|
]
|
|
@@ -2284,14 +2727,14 @@ function AttachedItemsBody({
|
|
|
2284
2727
|
link.edgeId
|
|
2285
2728
|
);
|
|
2286
2729
|
}) }) }),
|
|
2287
|
-
onAdd ? /* @__PURE__ */
|
|
2730
|
+
onAdd ? /* @__PURE__ */ jsxs7(
|
|
2288
2731
|
"button",
|
|
2289
2732
|
{
|
|
2290
2733
|
type: "button",
|
|
2291
2734
|
onClick: onAdd,
|
|
2292
2735
|
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",
|
|
2293
2736
|
children: [
|
|
2294
|
-
/* @__PURE__ */
|
|
2737
|
+
/* @__PURE__ */ jsx9(PlusIcon, { className: "h-4 w-4" }),
|
|
2295
2738
|
"Attach ",
|
|
2296
2739
|
info.labelPlural.toLowerCase()
|
|
2297
2740
|
]
|
|
@@ -2301,7 +2744,7 @@ function AttachedItemsBody({
|
|
|
2301
2744
|
}
|
|
2302
2745
|
|
|
2303
2746
|
// src/react/components/AssociationCard.tsx
|
|
2304
|
-
import { useState as
|
|
2747
|
+
import { useState as useState10 } from "react";
|
|
2305
2748
|
import { cn as cn6 } from "@ai-matrx/design-system";
|
|
2306
2749
|
|
|
2307
2750
|
// src/react/contentRoles.ts
|
|
@@ -2352,7 +2795,7 @@ function getContentRoleMeta(role) {
|
|
|
2352
2795
|
}
|
|
2353
2796
|
|
|
2354
2797
|
// src/react/components/AssociationCard.tsx
|
|
2355
|
-
import { Fragment as Fragment3, jsx as
|
|
2798
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2356
2799
|
function AssociationCard({
|
|
2357
2800
|
token,
|
|
2358
2801
|
container: containerProp,
|
|
@@ -2361,11 +2804,11 @@ function AssociationCard({
|
|
|
2361
2804
|
const store = useAssociationsStore();
|
|
2362
2805
|
const fromCtx = usePrimaryEntity();
|
|
2363
2806
|
const container = containerProp ?? fromCtx;
|
|
2364
|
-
const [open, setOpen] =
|
|
2807
|
+
const [open, setOpen] = useState10(false);
|
|
2365
2808
|
const info = store.registry.getEntityInfo(token);
|
|
2366
2809
|
const role = getContentRoleMeta(info.contentRole);
|
|
2367
2810
|
const Icon = info.Icon ?? DefaultEntityIcon;
|
|
2368
|
-
const [listOpen, setListOpen] =
|
|
2811
|
+
const [listOpen, setListOpen] = useState10(false);
|
|
2369
2812
|
const { status, countFor, attachedIdsFor, linksFor, attach, detach } = useContainerLinks({
|
|
2370
2813
|
containerType: container?.type ?? "organization",
|
|
2371
2814
|
containerId: container?.id ?? null,
|
|
@@ -2383,8 +2826,8 @@ function AssociationCard({
|
|
|
2383
2826
|
const loading = status === "loading" || status === "idle";
|
|
2384
2827
|
const canAttach = info.canListCandidates;
|
|
2385
2828
|
const canDrillIn = count > 0;
|
|
2386
|
-
return /* @__PURE__ */
|
|
2387
|
-
/* @__PURE__ */
|
|
2829
|
+
return /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
2830
|
+
/* @__PURE__ */ jsxs8(
|
|
2388
2831
|
"div",
|
|
2389
2832
|
{
|
|
2390
2833
|
className: cn6(
|
|
@@ -2392,7 +2835,7 @@ function AssociationCard({
|
|
|
2392
2835
|
className
|
|
2393
2836
|
),
|
|
2394
2837
|
children: [
|
|
2395
|
-
/* @__PURE__ */
|
|
2838
|
+
/* @__PURE__ */ jsx10(
|
|
2396
2839
|
"span",
|
|
2397
2840
|
{
|
|
2398
2841
|
className: cn6(
|
|
@@ -2401,7 +2844,7 @@ function AssociationCard({
|
|
|
2401
2844
|
)
|
|
2402
2845
|
}
|
|
2403
2846
|
),
|
|
2404
|
-
/* @__PURE__ */
|
|
2847
|
+
/* @__PURE__ */ jsxs8(
|
|
2405
2848
|
"button",
|
|
2406
2849
|
{
|
|
2407
2850
|
type: "button",
|
|
@@ -2413,7 +2856,7 @@ function AssociationCard({
|
|
|
2413
2856
|
canDrillIn ? "cursor-pointer" : "cursor-default"
|
|
2414
2857
|
),
|
|
2415
2858
|
children: [
|
|
2416
|
-
/* @__PURE__ */
|
|
2859
|
+
/* @__PURE__ */ jsx10(
|
|
2417
2860
|
"span",
|
|
2418
2861
|
{
|
|
2419
2862
|
className: cn6(
|
|
@@ -2421,20 +2864,20 @@ function AssociationCard({
|
|
|
2421
2864
|
role.accentBg,
|
|
2422
2865
|
role.accentText
|
|
2423
2866
|
),
|
|
2424
|
-
children: /* @__PURE__ */
|
|
2867
|
+
children: /* @__PURE__ */ jsx10(Icon, { className: "h-4 w-4" })
|
|
2425
2868
|
}
|
|
2426
2869
|
),
|
|
2427
|
-
/* @__PURE__ */
|
|
2428
|
-
/* @__PURE__ */
|
|
2429
|
-
/* @__PURE__ */
|
|
2430
|
-
/* @__PURE__ */
|
|
2870
|
+
/* @__PURE__ */ jsxs8("span", { className: "min-w-0 flex-1", children: [
|
|
2871
|
+
/* @__PURE__ */ jsx10("span", { className: "block truncate text-sm font-medium text-foreground", children: info.labelPlural }),
|
|
2872
|
+
/* @__PURE__ */ jsx10("span", { className: "block text-[11px] text-muted-foreground tabular-nums", children: loading ? /* @__PURE__ */ jsxs8("span", { className: "inline-flex items-center gap-1", children: [
|
|
2873
|
+
/* @__PURE__ */ jsx10(SpinnerIcon, { className: "h-3 w-3 animate-spin" }),
|
|
2431
2874
|
"Loading\u2026"
|
|
2432
2875
|
] }) : `${count} attached` })
|
|
2433
2876
|
] })
|
|
2434
2877
|
]
|
|
2435
2878
|
}
|
|
2436
2879
|
),
|
|
2437
|
-
canAttach && /* @__PURE__ */
|
|
2880
|
+
canAttach && /* @__PURE__ */ jsx10(
|
|
2438
2881
|
"button",
|
|
2439
2882
|
{
|
|
2440
2883
|
type: "button",
|
|
@@ -2442,13 +2885,13 @@ function AssociationCard({
|
|
|
2442
2885
|
"aria-label": `Attach ${info.labelPlural.toLowerCase()}`,
|
|
2443
2886
|
title: `Attach ${info.labelPlural.toLowerCase()}`,
|
|
2444
2887
|
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",
|
|
2445
|
-
children: /* @__PURE__ */
|
|
2888
|
+
children: /* @__PURE__ */ jsx10(PlusIcon, { className: "h-4 w-4" })
|
|
2446
2889
|
}
|
|
2447
2890
|
)
|
|
2448
2891
|
]
|
|
2449
2892
|
}
|
|
2450
2893
|
),
|
|
2451
|
-
listOpen && /* @__PURE__ */
|
|
2894
|
+
listOpen && /* @__PURE__ */ jsx10(
|
|
2452
2895
|
AttachedItemsSheet,
|
|
2453
2896
|
{
|
|
2454
2897
|
open: listOpen,
|
|
@@ -2466,7 +2909,7 @@ function AssociationCard({
|
|
|
2466
2909
|
onDetach: (resourceId) => detach(token, resourceId)
|
|
2467
2910
|
}
|
|
2468
2911
|
),
|
|
2469
|
-
canAttach && /* @__PURE__ */
|
|
2912
|
+
canAttach && /* @__PURE__ */ jsx10(
|
|
2470
2913
|
AssociationPicker,
|
|
2471
2914
|
{
|
|
2472
2915
|
open,
|
|
@@ -2484,7 +2927,7 @@ function AssociationCard({
|
|
|
2484
2927
|
|
|
2485
2928
|
// src/react/components/AssociationCardGrid.tsx
|
|
2486
2929
|
import { cn as cn7 } from "@ai-matrx/design-system";
|
|
2487
|
-
import { jsx as
|
|
2930
|
+
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2488
2931
|
function AssociationCardGrid({
|
|
2489
2932
|
tokens,
|
|
2490
2933
|
excludeTokens,
|
|
@@ -2495,33 +2938,33 @@ function AssociationCardGrid({
|
|
|
2495
2938
|
const list = (tokens ?? store.registry.curatedTokens()).filter(
|
|
2496
2939
|
(token) => !excluded.has(token)
|
|
2497
2940
|
);
|
|
2498
|
-
return /* @__PURE__ */
|
|
2941
|
+
return /* @__PURE__ */ jsx11("div", { className: cn7("space-y-5", className), children: CONTENT_ROLES.map((role) => {
|
|
2499
2942
|
const inRole = list.filter(
|
|
2500
2943
|
(token) => store.registry.getEntityInfo(token).contentRole === role.id
|
|
2501
2944
|
);
|
|
2502
2945
|
if (inRole.length === 0) return null;
|
|
2503
|
-
return /* @__PURE__ */
|
|
2504
|
-
/* @__PURE__ */
|
|
2505
|
-
/* @__PURE__ */
|
|
2506
|
-
/* @__PURE__ */
|
|
2946
|
+
return /* @__PURE__ */ jsxs9("section", { children: [
|
|
2947
|
+
/* @__PURE__ */ jsxs9("div", { className: "mb-2.5 flex items-baseline gap-3 pl-1.5", children: [
|
|
2948
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2", children: [
|
|
2949
|
+
/* @__PURE__ */ jsx11(
|
|
2507
2950
|
"span",
|
|
2508
2951
|
{
|
|
2509
2952
|
className: cn7("h-2.5 w-2.5 rounded-full", role.accentBar)
|
|
2510
2953
|
}
|
|
2511
2954
|
),
|
|
2512
|
-
/* @__PURE__ */
|
|
2955
|
+
/* @__PURE__ */ jsx11("h3", { className: "text-sm font-semibold text-foreground", children: role.title })
|
|
2513
2956
|
] }),
|
|
2514
|
-
/* @__PURE__ */
|
|
2957
|
+
/* @__PURE__ */ jsx11("p", { className: "hidden text-xs text-muted-foreground sm:block", children: role.tagline })
|
|
2515
2958
|
] }),
|
|
2516
|
-
/* @__PURE__ */
|
|
2959
|
+
/* @__PURE__ */ jsx11("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-3", children: inRole.map((token) => /* @__PURE__ */ jsx11(AssociationCard, { token }, token)) })
|
|
2517
2960
|
] }, role.id);
|
|
2518
2961
|
}) });
|
|
2519
2962
|
}
|
|
2520
2963
|
|
|
2521
2964
|
// src/react/components/AssociationList.tsx
|
|
2522
|
-
import { useState as
|
|
2965
|
+
import { useState as useState11 } from "react";
|
|
2523
2966
|
import { cn as cn8, Skeleton } from "@ai-matrx/design-system";
|
|
2524
|
-
import { jsx as
|
|
2967
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2525
2968
|
function useContainerLinksAdapter(container, tokens) {
|
|
2526
2969
|
const store = useAssociationsStore();
|
|
2527
2970
|
const links = useContainerLinks({
|
|
@@ -2559,9 +3002,9 @@ function AssociationList(props) {
|
|
|
2559
3002
|
const adapter = props.adapter ?? defaultAdapter;
|
|
2560
3003
|
const variant = props.variant ?? "full";
|
|
2561
3004
|
const tokenFilter = props.tokens ?? null;
|
|
2562
|
-
const [pickerToken, setPickerToken] =
|
|
2563
|
-
const [showUniversal, setShowUniversal] =
|
|
2564
|
-
const [removingKeys, setRemovingKeys] =
|
|
3005
|
+
const [pickerToken, setPickerToken] = useState11(null);
|
|
3006
|
+
const [showUniversal, setShowUniversal] = useState11(false);
|
|
3007
|
+
const [removingKeys, setRemovingKeys] = useState11(/* @__PURE__ */ new Set());
|
|
2565
3008
|
const rows = tokenFilter ? adapter.rows.filter((r) => tokenFilter.includes(r.token)) : adapter.rows;
|
|
2566
3009
|
const visibleRows = rows.filter((r) => !removingKeys.has(r.key));
|
|
2567
3010
|
const { titleFor } = useEntityTitles(
|
|
@@ -2599,15 +3042,15 @@ function AssociationList(props) {
|
|
|
2599
3042
|
const grouped = groupRows(store, visibleRows);
|
|
2600
3043
|
const isLoading = adapter.status === "loading" || adapter.status === "idle";
|
|
2601
3044
|
const attachableTokens = tokenFilter ?? store.registry.curatedTokens();
|
|
2602
|
-
return /* @__PURE__ */
|
|
2603
|
-
/* @__PURE__ */
|
|
2604
|
-
/* @__PURE__ */
|
|
2605
|
-
/* @__PURE__ */
|
|
3045
|
+
return /* @__PURE__ */ jsxs10("div", { className: cn8("flex flex-col gap-2 text-foreground", props.className), children: [
|
|
3046
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between gap-2 px-1", children: [
|
|
3047
|
+
/* @__PURE__ */ jsxs10("h3", { className: "flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: [
|
|
3048
|
+
/* @__PURE__ */ jsx12(Link2Icon, { className: "h-3.5 w-3.5" }),
|
|
2606
3049
|
"Resources",
|
|
2607
|
-
visibleRows.length > 0 && /* @__PURE__ */
|
|
3050
|
+
visibleRows.length > 0 && /* @__PURE__ */ jsx12("span", { className: "rounded bg-muted px-1 text-[10px] font-medium text-muted-foreground", children: visibleRows.length })
|
|
2608
3051
|
] }),
|
|
2609
|
-
/* @__PURE__ */
|
|
2610
|
-
/* @__PURE__ */
|
|
3052
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1.5", children: [
|
|
3053
|
+
/* @__PURE__ */ jsxs10(
|
|
2611
3054
|
"button",
|
|
2612
3055
|
{
|
|
2613
3056
|
type: "button",
|
|
@@ -2617,19 +3060,19 @@ function AssociationList(props) {
|
|
|
2617
3060
|
showUniversal ? "bg-accent text-foreground" : "text-muted-foreground hover:bg-accent hover:text-foreground"
|
|
2618
3061
|
),
|
|
2619
3062
|
children: [
|
|
2620
|
-
/* @__PURE__ */
|
|
3063
|
+
/* @__PURE__ */ jsx12(PlusIcon, { className: "h-3 w-3" }),
|
|
2621
3064
|
"Add"
|
|
2622
3065
|
]
|
|
2623
3066
|
}
|
|
2624
3067
|
),
|
|
2625
|
-
/* @__PURE__ */
|
|
3068
|
+
/* @__PURE__ */ jsx12(
|
|
2626
3069
|
"button",
|
|
2627
3070
|
{
|
|
2628
3071
|
type: "button",
|
|
2629
3072
|
onClick: () => void adapter.reload(),
|
|
2630
3073
|
title: "Refresh",
|
|
2631
3074
|
className: "text-muted-foreground/60 transition-colors hover:text-foreground",
|
|
2632
|
-
children: /* @__PURE__ */
|
|
3075
|
+
children: /* @__PURE__ */ jsx12(
|
|
2633
3076
|
RefreshIcon,
|
|
2634
3077
|
{
|
|
2635
3078
|
className: cn8(
|
|
@@ -2642,7 +3085,7 @@ function AssociationList(props) {
|
|
|
2642
3085
|
)
|
|
2643
3086
|
] })
|
|
2644
3087
|
] }),
|
|
2645
|
-
showUniversal && /* @__PURE__ */
|
|
3088
|
+
showUniversal && /* @__PURE__ */ jsx12("div", { className: "rounded-md border border-border/60 bg-muted/30 p-2 max-h-80 flex flex-col", children: /* @__PURE__ */ jsx12(
|
|
2646
3089
|
UniversalAssociationPicker,
|
|
2647
3090
|
{
|
|
2648
3091
|
tokens: attachableTokens,
|
|
@@ -2652,9 +3095,9 @@ function AssociationList(props) {
|
|
|
2652
3095
|
onDetach: (token, id) => adapter.detach(token, id)
|
|
2653
3096
|
}
|
|
2654
3097
|
) }),
|
|
2655
|
-
adapter.status === "error" && /* @__PURE__ */
|
|
2656
|
-
/* @__PURE__ */
|
|
2657
|
-
/* @__PURE__ */
|
|
3098
|
+
adapter.status === "error" && /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-2.5 py-2", children: [
|
|
3099
|
+
/* @__PURE__ */ jsx12("span", { className: "truncate text-[11px] text-destructive", children: adapter.error || "Failed to load resources" }),
|
|
3100
|
+
/* @__PURE__ */ jsx12(
|
|
2658
3101
|
"button",
|
|
2659
3102
|
{
|
|
2660
3103
|
type: "button",
|
|
@@ -2664,17 +3107,17 @@ function AssociationList(props) {
|
|
|
2664
3107
|
}
|
|
2665
3108
|
)
|
|
2666
3109
|
] }),
|
|
2667
|
-
adapter.status !== "error" && isLoading && visibleRows.length === 0 && /* @__PURE__ */
|
|
2668
|
-
/* @__PURE__ */
|
|
2669
|
-
/* @__PURE__ */
|
|
2670
|
-
/* @__PURE__ */
|
|
3110
|
+
adapter.status !== "error" && isLoading && visibleRows.length === 0 && /* @__PURE__ */ jsxs10("div", { className: "space-y-1.5 px-1", children: [
|
|
3111
|
+
/* @__PURE__ */ jsx12(Skeleton, { className: "h-3 w-20" }),
|
|
3112
|
+
/* @__PURE__ */ jsx12(Skeleton, { className: "h-7 w-full rounded-md" }),
|
|
3113
|
+
/* @__PURE__ */ jsx12(Skeleton, { className: "h-7 w-3/4 rounded-md" })
|
|
2671
3114
|
] }),
|
|
2672
|
-
adapter.status === "ready" && visibleRows.length === 0 && /* @__PURE__ */
|
|
2673
|
-
visibleRows.length > 0 && /* @__PURE__ */
|
|
3115
|
+
adapter.status === "ready" && visibleRows.length === 0 && /* @__PURE__ */ jsx12("p", { className: "px-1 py-1 text-[11px] text-muted-foreground", children: "Nothing attached yet \u2014 use Add to link anything." }),
|
|
3116
|
+
visibleRows.length > 0 && /* @__PURE__ */ jsx12("div", { className: "space-y-2.5", children: grouped.map(({ role, tokens: tokenGroups }) => {
|
|
2674
3117
|
const roleMeta = getContentRoleMeta(role);
|
|
2675
|
-
return /* @__PURE__ */
|
|
2676
|
-
variant === "full" && /* @__PURE__ */
|
|
2677
|
-
/* @__PURE__ */
|
|
3118
|
+
return /* @__PURE__ */ jsxs10("div", { className: "space-y-1.5", children: [
|
|
3119
|
+
variant === "full" && /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1.5 px-1", children: [
|
|
3120
|
+
/* @__PURE__ */ jsx12(
|
|
2678
3121
|
"span",
|
|
2679
3122
|
{
|
|
2680
3123
|
className: cn8(
|
|
@@ -2683,7 +3126,7 @@ function AssociationList(props) {
|
|
|
2683
3126
|
)
|
|
2684
3127
|
}
|
|
2685
3128
|
),
|
|
2686
|
-
/* @__PURE__ */
|
|
3129
|
+
/* @__PURE__ */ jsx12(
|
|
2687
3130
|
"p",
|
|
2688
3131
|
{
|
|
2689
3132
|
className: cn8(
|
|
@@ -2696,28 +3139,28 @@ function AssociationList(props) {
|
|
|
2696
3139
|
] }),
|
|
2697
3140
|
tokenGroups.map(({ info, token, rows: tokenRows }) => {
|
|
2698
3141
|
const TokenIcon = info?.Icon ?? null;
|
|
2699
|
-
return /* @__PURE__ */
|
|
2700
|
-
/* @__PURE__ */
|
|
2701
|
-
/* @__PURE__ */
|
|
2702
|
-
TokenIcon ? /* @__PURE__ */
|
|
3142
|
+
return /* @__PURE__ */ jsxs10("div", { className: "space-y-0.5", children: [
|
|
3143
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between gap-1 px-1", children: [
|
|
3144
|
+
/* @__PURE__ */ jsxs10("p", { className: "flex items-center gap-1 text-[10px] font-medium text-muted-foreground/80", children: [
|
|
3145
|
+
TokenIcon ? /* @__PURE__ */ jsx12(TokenIcon, { className: "h-3 w-3" }) : null,
|
|
2703
3146
|
info?.labelPlural ?? titleize(token),
|
|
2704
|
-
/* @__PURE__ */
|
|
3147
|
+
/* @__PURE__ */ jsx12("span", { className: "text-muted-foreground/50", children: tokenRows.length })
|
|
2705
3148
|
] }),
|
|
2706
|
-
/* @__PURE__ */
|
|
3149
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1", children: [
|
|
2707
3150
|
props.renderSectionActions && info ? props.renderSectionActions(info.token) : null,
|
|
2708
|
-
info?.canListCandidates && /* @__PURE__ */
|
|
3151
|
+
info?.canListCandidates && /* @__PURE__ */ jsx12(
|
|
2709
3152
|
"button",
|
|
2710
3153
|
{
|
|
2711
3154
|
type: "button",
|
|
2712
3155
|
onClick: () => setPickerToken(info.token),
|
|
2713
3156
|
title: `Add ${info.labelPlural}`,
|
|
2714
3157
|
className: "rounded p-0.5 text-muted-foreground/60 transition-colors hover:bg-accent hover:text-foreground",
|
|
2715
|
-
children: /* @__PURE__ */
|
|
3158
|
+
children: /* @__PURE__ */ jsx12(PlusIcon, { className: "h-3 w-3" })
|
|
2716
3159
|
}
|
|
2717
3160
|
)
|
|
2718
3161
|
] })
|
|
2719
3162
|
] }),
|
|
2720
|
-
/* @__PURE__ */
|
|
3163
|
+
/* @__PURE__ */ jsx12("ul", { className: "space-y-0.5", children: tokenRows.map((row) => {
|
|
2721
3164
|
const busy = removingKeys.has(row.key);
|
|
2722
3165
|
const custom = props.renderRow?.(row, {
|
|
2723
3166
|
title: titleFor({
|
|
@@ -2730,9 +3173,9 @@ function AssociationList(props) {
|
|
|
2730
3173
|
onOpen: () => openRow(row)
|
|
2731
3174
|
});
|
|
2732
3175
|
if (custom != null) {
|
|
2733
|
-
return /* @__PURE__ */
|
|
3176
|
+
return /* @__PURE__ */ jsx12("li", { children: custom }, row.key);
|
|
2734
3177
|
}
|
|
2735
|
-
return /* @__PURE__ */
|
|
3178
|
+
return /* @__PURE__ */ jsx12("li", { children: /* @__PURE__ */ jsxs10(
|
|
2736
3179
|
"div",
|
|
2737
3180
|
{
|
|
2738
3181
|
className: cn8(
|
|
@@ -2740,7 +3183,7 @@ function AssociationList(props) {
|
|
|
2740
3183
|
busy && "opacity-50"
|
|
2741
3184
|
),
|
|
2742
3185
|
children: [
|
|
2743
|
-
/* @__PURE__ */
|
|
3186
|
+
/* @__PURE__ */ jsx12(
|
|
2744
3187
|
DoorRef,
|
|
2745
3188
|
{
|
|
2746
3189
|
token: row.token,
|
|
@@ -2756,9 +3199,9 @@ function AssociationList(props) {
|
|
|
2756
3199
|
className: "min-w-0 flex-1 text-foreground"
|
|
2757
3200
|
}
|
|
2758
3201
|
),
|
|
2759
|
-
row.originRefs && row.originRefs.length > 0 ? /* @__PURE__ */
|
|
3202
|
+
row.originRefs && row.originRefs.length > 0 ? /* @__PURE__ */ jsxs10("span", { className: "flex shrink-0 items-center gap-1 text-[10px] text-muted-foreground/60", children: [
|
|
2760
3203
|
"via",
|
|
2761
|
-
row.originRefs.map((origin) => /* @__PURE__ */
|
|
3204
|
+
row.originRefs.map((origin) => /* @__PURE__ */ jsx12(
|
|
2762
3205
|
DoorRef,
|
|
2763
3206
|
{
|
|
2764
3207
|
token: origin.token,
|
|
@@ -2770,8 +3213,8 @@ function AssociationList(props) {
|
|
|
2770
3213
|
},
|
|
2771
3214
|
`${origin.token}:${origin.id}`
|
|
2772
3215
|
))
|
|
2773
|
-
] }) : row.originNote ? /* @__PURE__ */
|
|
2774
|
-
adapter.setPinned && info && row.removable && /* @__PURE__ */
|
|
3216
|
+
] }) : row.originNote ? /* @__PURE__ */ jsx12("span", { className: "shrink-0 text-[10px] text-muted-foreground/60", children: row.originNote }) : null,
|
|
3217
|
+
adapter.setPinned && info && row.removable && /* @__PURE__ */ jsx12(
|
|
2775
3218
|
"button",
|
|
2776
3219
|
{
|
|
2777
3220
|
type: "button",
|
|
@@ -2786,7 +3229,7 @@ function AssociationList(props) {
|
|
|
2786
3229
|
"shrink-0 rounded p-0.5 transition-colors hover:!text-foreground",
|
|
2787
3230
|
row.pinned ? "text-primary" : "text-muted-foreground/0 group-hover:text-muted-foreground"
|
|
2788
3231
|
),
|
|
2789
|
-
children: /* @__PURE__ */
|
|
3232
|
+
children: /* @__PURE__ */ jsx12(
|
|
2790
3233
|
PinIcon,
|
|
2791
3234
|
{
|
|
2792
3235
|
className: cn8(
|
|
@@ -2797,7 +3240,7 @@ function AssociationList(props) {
|
|
|
2797
3240
|
)
|
|
2798
3241
|
}
|
|
2799
3242
|
),
|
|
2800
|
-
row.removable && /* @__PURE__ */
|
|
3243
|
+
row.removable && /* @__PURE__ */ jsx12(
|
|
2801
3244
|
"button",
|
|
2802
3245
|
{
|
|
2803
3246
|
type: "button",
|
|
@@ -2806,7 +3249,7 @@ function AssociationList(props) {
|
|
|
2806
3249
|
title: "Detach",
|
|
2807
3250
|
"aria-label": "Detach",
|
|
2808
3251
|
className: "shrink-0 rounded p-0.5 text-muted-foreground/0 transition-colors group-hover:text-muted-foreground hover:!text-destructive disabled:opacity-50",
|
|
2809
|
-
children: busy ? /* @__PURE__ */
|
|
3252
|
+
children: busy ? /* @__PURE__ */ jsx12(SpinnerIcon, { className: "h-3 w-3 animate-spin" }) : /* @__PURE__ */ jsx12(XIcon, { className: "h-3 w-3" })
|
|
2810
3253
|
}
|
|
2811
3254
|
)
|
|
2812
3255
|
]
|
|
@@ -2817,7 +3260,7 @@ function AssociationList(props) {
|
|
|
2817
3260
|
})
|
|
2818
3261
|
] }, role);
|
|
2819
3262
|
}) }),
|
|
2820
|
-
pickerToken && /* @__PURE__ */
|
|
3263
|
+
pickerToken && /* @__PURE__ */ jsx12(
|
|
2821
3264
|
AssociationPicker,
|
|
2822
3265
|
{
|
|
2823
3266
|
open: true,
|
|
@@ -2873,9 +3316,9 @@ function titleize(token) {
|
|
|
2873
3316
|
}
|
|
2874
3317
|
|
|
2875
3318
|
// src/react/components/AssociationCaptureToolbar.tsx
|
|
2876
|
-
import { useRef as
|
|
3319
|
+
import { useRef as useRef8, useState as useState12 } from "react";
|
|
2877
3320
|
import { Button, cn as cn9, Input as Input3 } from "@ai-matrx/design-system";
|
|
2878
|
-
import { Fragment as Fragment4, jsx as
|
|
3321
|
+
import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2879
3322
|
function AssociationCaptureToolbar({
|
|
2880
3323
|
attach,
|
|
2881
3324
|
uploadFolderPath,
|
|
@@ -2893,21 +3336,43 @@ function AssociationCaptureToolbar({
|
|
|
2893
3336
|
const store = useAssociationsStore();
|
|
2894
3337
|
const { capture } = useAssociationsUiPorts();
|
|
2895
3338
|
const notifier = useNotifier();
|
|
2896
|
-
const fileInputRef =
|
|
2897
|
-
const [isUploading, setIsUploading] =
|
|
2898
|
-
const [isDragOver, setIsDragOver] =
|
|
2899
|
-
const [isPicking, setIsPicking] =
|
|
2900
|
-
const [namingDoc, setNamingDoc] =
|
|
2901
|
-
const [docName, setDocName] =
|
|
2902
|
-
const [creatingDoc, setCreatingDoc] =
|
|
3339
|
+
const fileInputRef = useRef8(null);
|
|
3340
|
+
const [isUploading, setIsUploading] = useState12(false);
|
|
3341
|
+
const [isDragOver, setIsDragOver] = useState12(false);
|
|
3342
|
+
const [isPicking, setIsPicking] = useState12(false);
|
|
3343
|
+
const [namingDoc, setNamingDoc] = useState12(false);
|
|
3344
|
+
const [docName, setDocName] = useState12("");
|
|
3345
|
+
const [creatingDoc, setCreatingDoc] = useState12(false);
|
|
3346
|
+
const hostRequestUpload = capture?.requestUpload;
|
|
3347
|
+
const hostUploadFile = capture?.uploadFile;
|
|
3348
|
+
const requestUploadPath = hostRequestUpload ? (opts) => hostRequestUpload(opts) : hostUploadFile ? async (opts) => {
|
|
3349
|
+
const uploaded = [];
|
|
3350
|
+
const failed = [];
|
|
3351
|
+
for (const file of opts.files) {
|
|
3352
|
+
try {
|
|
3353
|
+
const result = await hostUploadFile(file, {
|
|
3354
|
+
folderPath: opts.folderPath,
|
|
3355
|
+
...opts.visibility !== void 0 ? { visibility: opts.visibility } : {}
|
|
3356
|
+
});
|
|
3357
|
+
if (result.ok) uploaded.push(result.id);
|
|
3358
|
+
else failed.push({ name: file.name, error: result.error });
|
|
3359
|
+
} catch (err) {
|
|
3360
|
+
failed.push({
|
|
3361
|
+
name: file.name,
|
|
3362
|
+
error: err instanceof Error && err.message ? err.message : "Upload failed"
|
|
3363
|
+
});
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
return { uploaded, aliased: [], failed };
|
|
3367
|
+
} : void 0;
|
|
2903
3368
|
const actions = {
|
|
2904
|
-
upload: (showActions?.upload ?? true) && Boolean(
|
|
3369
|
+
upload: (showActions?.upload ?? true) && Boolean(requestUploadPath),
|
|
2905
3370
|
addFile: (showActions?.addFile ?? true) && Boolean(capture?.openFilePicker),
|
|
2906
3371
|
newDocument: (showActions?.newDocument ?? true) && Boolean(capture?.createDataTable)
|
|
2907
3372
|
};
|
|
2908
|
-
const canDrop = Boolean(
|
|
3373
|
+
const canDrop = Boolean(requestUploadPath);
|
|
2909
3374
|
const uploadAndAttach = async (files) => {
|
|
2910
|
-
const requestUpload =
|
|
3375
|
+
const requestUpload = requestUploadPath;
|
|
2911
3376
|
if (files.length === 0 || !requestUpload) return;
|
|
2912
3377
|
setIsUploading(true);
|
|
2913
3378
|
const watchdog = setTimeout(() => {
|
|
@@ -3065,7 +3530,7 @@ function AssociationCaptureToolbar({
|
|
|
3065
3530
|
}
|
|
3066
3531
|
};
|
|
3067
3532
|
const anyBuiltIn = actions.upload || actions.addFile || actions.newDocument;
|
|
3068
|
-
return /* @__PURE__ */
|
|
3533
|
+
return /* @__PURE__ */ jsxs11(
|
|
3069
3534
|
"div",
|
|
3070
3535
|
{
|
|
3071
3536
|
className: cn9(
|
|
@@ -3086,11 +3551,11 @@ function AssociationCaptureToolbar({
|
|
|
3086
3551
|
},
|
|
3087
3552
|
onDrop: handleDrop,
|
|
3088
3553
|
children: [
|
|
3089
|
-
isDragOver ? /* @__PURE__ */
|
|
3090
|
-
/* @__PURE__ */
|
|
3554
|
+
isDragOver ? /* @__PURE__ */ jsx13("div", { className: "pointer-events-none absolute inset-0 z-10 grid place-items-center bg-background/70", children: /* @__PURE__ */ jsxs11("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: [
|
|
3555
|
+
/* @__PURE__ */ jsx13(UploadIcon, { className: "size-3.5 text-primary" }),
|
|
3091
3556
|
"Drop to upload & attach"
|
|
3092
3557
|
] }) }) : null,
|
|
3093
|
-
/* @__PURE__ */
|
|
3558
|
+
/* @__PURE__ */ jsx13(
|
|
3094
3559
|
"input",
|
|
3095
3560
|
{
|
|
3096
3561
|
ref: fileInputRef,
|
|
@@ -3100,8 +3565,8 @@ function AssociationCaptureToolbar({
|
|
|
3100
3565
|
onChange: handleFilesSelected
|
|
3101
3566
|
}
|
|
3102
3567
|
),
|
|
3103
|
-
showToolbar && (anyBuiltIn || extraActions) ? /* @__PURE__ */
|
|
3104
|
-
/* @__PURE__ */
|
|
3568
|
+
showToolbar && (anyBuiltIn || extraActions) ? /* @__PURE__ */ jsx13("div", { className: "flex flex-wrap items-center gap-0.5 border-b border-border/60 px-1.5 py-1", children: namingDoc ? /* @__PURE__ */ jsxs11("div", { className: "flex flex-1 items-center gap-1.5 py-0.5", children: [
|
|
3569
|
+
/* @__PURE__ */ jsx13(
|
|
3105
3570
|
Input3,
|
|
3106
3571
|
{
|
|
3107
3572
|
autoFocus: true,
|
|
@@ -3120,17 +3585,17 @@ function AssociationCaptureToolbar({
|
|
|
3120
3585
|
style: { fontSize: 16 }
|
|
3121
3586
|
}
|
|
3122
3587
|
),
|
|
3123
|
-
/* @__PURE__ */
|
|
3588
|
+
/* @__PURE__ */ jsx13(
|
|
3124
3589
|
"button",
|
|
3125
3590
|
{
|
|
3126
3591
|
type: "button",
|
|
3127
3592
|
disabled: creatingDoc || !docName.trim(),
|
|
3128
3593
|
onClick: () => void handleCreateDoc(),
|
|
3129
3594
|
className: "flex h-7 items-center rounded-md bg-primary px-2 text-[11px] font-medium text-primary-foreground disabled:opacity-50",
|
|
3130
|
-
children: creatingDoc ? /* @__PURE__ */
|
|
3595
|
+
children: creatingDoc ? /* @__PURE__ */ jsx13(SpinnerIcon, { className: "size-3.5 animate-spin" }) : "Create"
|
|
3131
3596
|
}
|
|
3132
3597
|
),
|
|
3133
|
-
/* @__PURE__ */
|
|
3598
|
+
/* @__PURE__ */ jsx13(
|
|
3134
3599
|
"button",
|
|
3135
3600
|
{
|
|
3136
3601
|
type: "button",
|
|
@@ -3143,8 +3608,8 @@ function AssociationCaptureToolbar({
|
|
|
3143
3608
|
children: "Cancel"
|
|
3144
3609
|
}
|
|
3145
3610
|
)
|
|
3146
|
-
] }) : /* @__PURE__ */
|
|
3147
|
-
actions.upload ? /* @__PURE__ */
|
|
3611
|
+
] }) : /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
3612
|
+
actions.upload ? /* @__PURE__ */ jsx13(
|
|
3148
3613
|
CaptureToolbarAction,
|
|
3149
3614
|
{
|
|
3150
3615
|
icon: isUploading ? SpinnerIcon : UploadIcon,
|
|
@@ -3154,7 +3619,7 @@ function AssociationCaptureToolbar({
|
|
|
3154
3619
|
disabled: isPicking || isUploading
|
|
3155
3620
|
}
|
|
3156
3621
|
) : null,
|
|
3157
|
-
actions.addFile ? /* @__PURE__ */
|
|
3622
|
+
actions.addFile ? /* @__PURE__ */ jsx13(
|
|
3158
3623
|
CaptureToolbarAction,
|
|
3159
3624
|
{
|
|
3160
3625
|
icon: isPicking ? SpinnerIcon : FolderOpenIcon,
|
|
@@ -3164,8 +3629,8 @@ function AssociationCaptureToolbar({
|
|
|
3164
3629
|
disabled: isPicking || isUploading
|
|
3165
3630
|
}
|
|
3166
3631
|
) : null,
|
|
3167
|
-
(actions.upload || actions.addFile) && actions.newDocument ? /* @__PURE__ */
|
|
3168
|
-
actions.newDocument ? /* @__PURE__ */
|
|
3632
|
+
(actions.upload || actions.addFile) && actions.newDocument ? /* @__PURE__ */ jsx13("span", { className: "mx-1 h-4 w-px bg-border" }) : null,
|
|
3633
|
+
actions.newDocument ? /* @__PURE__ */ jsx13(
|
|
3169
3634
|
CaptureToolbarAction,
|
|
3170
3635
|
{
|
|
3171
3636
|
icon: creatingDoc ? SpinnerIcon : PlusIcon,
|
|
@@ -3189,7 +3654,7 @@ function CaptureToolbarAction({
|
|
|
3189
3654
|
disabled,
|
|
3190
3655
|
spinning
|
|
3191
3656
|
}) {
|
|
3192
|
-
return /* @__PURE__ */
|
|
3657
|
+
return /* @__PURE__ */ jsxs11(
|
|
3193
3658
|
Button,
|
|
3194
3659
|
{
|
|
3195
3660
|
type: "button",
|
|
@@ -3199,15 +3664,145 @@ function CaptureToolbarAction({
|
|
|
3199
3664
|
disabled,
|
|
3200
3665
|
className: "h-7 gap-1 px-2 text-[11px] text-muted-foreground hover:text-foreground",
|
|
3201
3666
|
children: [
|
|
3202
|
-
/* @__PURE__ */
|
|
3667
|
+
/* @__PURE__ */ jsx13(Icon, { className: cn9("size-3.5", spinning && "animate-spin") }),
|
|
3203
3668
|
label
|
|
3204
3669
|
]
|
|
3205
3670
|
}
|
|
3206
3671
|
);
|
|
3207
3672
|
}
|
|
3208
3673
|
|
|
3674
|
+
// src/react/lazyPorts.tsx
|
|
3675
|
+
import { Suspense, lazy } from "react";
|
|
3676
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
3677
|
+
function lazyComponent(load) {
|
|
3678
|
+
return lazy(async () => {
|
|
3679
|
+
const loaded = await load();
|
|
3680
|
+
return typeof loaded === "function" ? { default: loaded } : loaded;
|
|
3681
|
+
});
|
|
3682
|
+
}
|
|
3683
|
+
function lazyWindowShell(load) {
|
|
3684
|
+
const Loaded = lazyComponent(load);
|
|
3685
|
+
return function LazyWindowShell(props) {
|
|
3686
|
+
return /* @__PURE__ */ jsx14(Suspense, { fallback: null, children: /* @__PURE__ */ jsx14(Loaded, { ...props }) });
|
|
3687
|
+
};
|
|
3688
|
+
}
|
|
3689
|
+
function lazyPickerOverride(load) {
|
|
3690
|
+
const Loaded = lazyComponent(load);
|
|
3691
|
+
return function LazyPickerOverride(props) {
|
|
3692
|
+
if (!props.open) return null;
|
|
3693
|
+
return /* @__PURE__ */ jsx14(Suspense, { fallback: null, children: /* @__PURE__ */ jsx14(Loaded, { ...props }) });
|
|
3694
|
+
};
|
|
3695
|
+
}
|
|
3696
|
+
|
|
3697
|
+
// src/react/hooks/useAssociationPickerBridge.ts
|
|
3698
|
+
import { useCallback as useCallback3, useState as useState13 } from "react";
|
|
3699
|
+
function useAssociationPickerBridge(props, options = {}) {
|
|
3700
|
+
const store = useAssociationsStore();
|
|
3701
|
+
const notifier = useNotifier();
|
|
3702
|
+
const [busy, setBusy] = useState13(false);
|
|
3703
|
+
const { attachedIds, onAttach, onDetach, token } = props;
|
|
3704
|
+
const {
|
|
3705
|
+
createdLocationLabel,
|
|
3706
|
+
openCreatedLocation,
|
|
3707
|
+
itemNoun = "item"
|
|
3708
|
+
} = options;
|
|
3709
|
+
const attachMany = useCallback3(
|
|
3710
|
+
async (items) => {
|
|
3711
|
+
if (items.length === 0) return;
|
|
3712
|
+
setBusy(true);
|
|
3713
|
+
const failed = [];
|
|
3714
|
+
try {
|
|
3715
|
+
for (const item of items) {
|
|
3716
|
+
try {
|
|
3717
|
+
const result = await onAttach(item.id, item.name);
|
|
3718
|
+
if (!result.ok) {
|
|
3719
|
+
failed.push({ name: item.name, error: result.error });
|
|
3720
|
+
}
|
|
3721
|
+
} catch (error) {
|
|
3722
|
+
store.errorSink({
|
|
3723
|
+
code: "picker_attach_threw",
|
|
3724
|
+
message: `[associations] attaching ${itemNoun} "${item.name}" threw`,
|
|
3725
|
+
context: { token, id: item.id, detail: error }
|
|
3726
|
+
});
|
|
3727
|
+
failed.push({
|
|
3728
|
+
name: item.name,
|
|
3729
|
+
error: error instanceof Error ? error.message : void 0
|
|
3730
|
+
});
|
|
3731
|
+
}
|
|
3732
|
+
}
|
|
3733
|
+
const attached = items.length - failed.length;
|
|
3734
|
+
if (attached > 0) {
|
|
3735
|
+
notifier.success(
|
|
3736
|
+
attached === 1 ? `Attached 1 ${itemNoun}` : `Attached ${attached} ${itemNoun}s`
|
|
3737
|
+
);
|
|
3738
|
+
}
|
|
3739
|
+
if (failed.length > 0) {
|
|
3740
|
+
const first = failed[0];
|
|
3741
|
+
const detail = first?.error ? ` (${first.error})` : "";
|
|
3742
|
+
notifier.error(
|
|
3743
|
+
failed.length === 1 ? `Couldn't attach "${first?.name ?? itemNoun}"${detail}` : `Couldn't attach ${failed.length} ${itemNoun}s${detail}`,
|
|
3744
|
+
{
|
|
3745
|
+
...createdLocationLabel ? {
|
|
3746
|
+
description: `The ${failed.length === 1 ? itemNoun + " is" : itemNoun + "s are"} safe in ${createdLocationLabel}. You can retry the association here.`
|
|
3747
|
+
} : {},
|
|
3748
|
+
...openCreatedLocation ? { action: openCreatedLocation } : {}
|
|
3749
|
+
}
|
|
3750
|
+
);
|
|
3751
|
+
store.errorSink({
|
|
3752
|
+
code: "picker_attach_partial",
|
|
3753
|
+
message: `[associations] ${failed.length} of ${items.length} ${itemNoun}s were not attached`,
|
|
3754
|
+
context: { token, failed }
|
|
3755
|
+
});
|
|
3756
|
+
}
|
|
3757
|
+
} finally {
|
|
3758
|
+
setBusy(false);
|
|
3759
|
+
}
|
|
3760
|
+
},
|
|
3761
|
+
[
|
|
3762
|
+
createdLocationLabel,
|
|
3763
|
+
itemNoun,
|
|
3764
|
+
notifier,
|
|
3765
|
+
onAttach,
|
|
3766
|
+
openCreatedLocation,
|
|
3767
|
+
store,
|
|
3768
|
+
token
|
|
3769
|
+
]
|
|
3770
|
+
);
|
|
3771
|
+
const toggle = useCallback3(
|
|
3772
|
+
async (item) => {
|
|
3773
|
+
setBusy(true);
|
|
3774
|
+
try {
|
|
3775
|
+
const detaching = attachedIds.has(item.id);
|
|
3776
|
+
const result = detaching ? await onDetach(item.id) : await onAttach(item.id, item.name);
|
|
3777
|
+
if (!result.ok) {
|
|
3778
|
+
const verb = detaching ? "detach" : "attach";
|
|
3779
|
+
notifier.error(
|
|
3780
|
+
`Couldn't ${verb} "${item.name}"` + (result.error ? `: ${result.error}` : "")
|
|
3781
|
+
);
|
|
3782
|
+
store.errorSink({
|
|
3783
|
+
code: detaching ? "picker_detach_failed" : "picker_attach_failed",
|
|
3784
|
+
message: `[associations] ${verb} refused for "${item.name}"`,
|
|
3785
|
+
context: { token, id: item.id, error: result.error }
|
|
3786
|
+
});
|
|
3787
|
+
}
|
|
3788
|
+
} catch (error) {
|
|
3789
|
+
store.errorSink({
|
|
3790
|
+
code: "picker_toggle_threw",
|
|
3791
|
+
message: `[associations] toggling ${itemNoun} "${item.name}" threw`,
|
|
3792
|
+
context: { token, id: item.id, detail: error }
|
|
3793
|
+
});
|
|
3794
|
+
notifier.error(`Couldn't update "${item.name}"`);
|
|
3795
|
+
} finally {
|
|
3796
|
+
setBusy(false);
|
|
3797
|
+
}
|
|
3798
|
+
},
|
|
3799
|
+
[attachedIds, itemNoun, notifier, onAttach, onDetach, store, token]
|
|
3800
|
+
);
|
|
3801
|
+
return { attachMany, toggle, busy };
|
|
3802
|
+
}
|
|
3803
|
+
|
|
3209
3804
|
// src/react/components/AssociationEntitySelect.tsx
|
|
3210
|
-
import { useState as
|
|
3805
|
+
import { useState as useState14 } from "react";
|
|
3211
3806
|
import {
|
|
3212
3807
|
cn as cn10,
|
|
3213
3808
|
Command,
|
|
@@ -3221,7 +3816,7 @@ import {
|
|
|
3221
3816
|
PopoverContent,
|
|
3222
3817
|
PopoverTrigger
|
|
3223
3818
|
} from "@ai-matrx/design-system";
|
|
3224
|
-
import { jsx as
|
|
3819
|
+
import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3225
3820
|
function AssociationEntitySelect({
|
|
3226
3821
|
token,
|
|
3227
3822
|
adapter,
|
|
@@ -3235,19 +3830,17 @@ function AssociationEntitySelect({
|
|
|
3235
3830
|
createSlot
|
|
3236
3831
|
}) {
|
|
3237
3832
|
const store = useAssociationsStore();
|
|
3238
|
-
const { entityDoors } = useAssociationsUiPorts();
|
|
3239
3833
|
const notifier = useNotifier();
|
|
3240
3834
|
const info = store.registry.getEntityInfo(token);
|
|
3241
3835
|
const InfoIcon = info.Icon ?? DefaultEntityIcon;
|
|
3242
|
-
const DoorControls = entityDoors?.DoorControls;
|
|
3243
3836
|
const { items, activeId } = adapter;
|
|
3244
3837
|
const active = items.find((i) => i.id === activeId) ?? null;
|
|
3245
3838
|
const activeIndex = active ? items.indexOf(active) : -1;
|
|
3246
|
-
const [open, setOpen] =
|
|
3247
|
-
const [query, setQuery] =
|
|
3248
|
-
const [creating, setCreating] =
|
|
3249
|
-
const [draftName, setDraftName] =
|
|
3250
|
-
const [busy, setBusy] =
|
|
3839
|
+
const [open, setOpen] = useState14(false);
|
|
3840
|
+
const [query, setQuery] = useState14("");
|
|
3841
|
+
const [creating, setCreating] = useState14(false);
|
|
3842
|
+
const [draftName, setDraftName] = useState14("");
|
|
3843
|
+
const [busy, setBusy] = useState14(false);
|
|
3251
3844
|
const entityLabel = info.label.toLowerCase();
|
|
3252
3845
|
const close = () => {
|
|
3253
3846
|
setOpen(false);
|
|
@@ -3264,7 +3857,7 @@ function AssociationEntitySelect({
|
|
|
3264
3857
|
if (id) close();
|
|
3265
3858
|
else notifier.error(`Couldn't create the ${entityLabel}`);
|
|
3266
3859
|
};
|
|
3267
|
-
return /* @__PURE__ */
|
|
3860
|
+
return /* @__PURE__ */ jsxs12(
|
|
3268
3861
|
"div",
|
|
3269
3862
|
{
|
|
3270
3863
|
className: cn10(
|
|
@@ -3272,7 +3865,7 @@ function AssociationEntitySelect({
|
|
|
3272
3865
|
className
|
|
3273
3866
|
),
|
|
3274
3867
|
children: [
|
|
3275
|
-
showIcon ? /* @__PURE__ */
|
|
3868
|
+
showIcon ? /* @__PURE__ */ jsx15(
|
|
3276
3869
|
InfoIcon,
|
|
3277
3870
|
{
|
|
3278
3871
|
className: cn10(
|
|
@@ -3282,7 +3875,7 @@ function AssociationEntitySelect({
|
|
|
3282
3875
|
"aria-hidden": true
|
|
3283
3876
|
}
|
|
3284
3877
|
) : null,
|
|
3285
|
-
active ? /* @__PURE__ */
|
|
3878
|
+
active ? /* @__PURE__ */ jsx15(
|
|
3286
3879
|
EditableLabel,
|
|
3287
3880
|
{
|
|
3288
3881
|
value: active.title,
|
|
@@ -3301,8 +3894,8 @@ function AssociationEntitySelect({
|
|
|
3301
3894
|
inputClassName: "text-xs font-medium"
|
|
3302
3895
|
}
|
|
3303
3896
|
) : null,
|
|
3304
|
-
active
|
|
3305
|
-
!active ? /* @__PURE__ */
|
|
3897
|
+
active ? /* @__PURE__ */ jsx15(DoorControlsSlot, { token, id: active.id, name: active.title }) : null,
|
|
3898
|
+
!active ? /* @__PURE__ */ jsx15(
|
|
3306
3899
|
"span",
|
|
3307
3900
|
{
|
|
3308
3901
|
className: cn10(
|
|
@@ -3312,13 +3905,13 @@ function AssociationEntitySelect({
|
|
|
3312
3905
|
children: adapter.loading ? "\u2026" : emptyLabel ?? info.labelPlural
|
|
3313
3906
|
}
|
|
3314
3907
|
) : null,
|
|
3315
|
-
/* @__PURE__ */
|
|
3908
|
+
/* @__PURE__ */ jsxs12(
|
|
3316
3909
|
Popover,
|
|
3317
3910
|
{
|
|
3318
3911
|
open,
|
|
3319
3912
|
onOpenChange: (next) => next ? setOpen(true) : close(),
|
|
3320
3913
|
children: [
|
|
3321
|
-
/* @__PURE__ */
|
|
3914
|
+
/* @__PURE__ */ jsx15(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs12(
|
|
3322
3915
|
"button",
|
|
3323
3916
|
{
|
|
3324
3917
|
type: "button",
|
|
@@ -3326,17 +3919,17 @@ function AssociationEntitySelect({
|
|
|
3326
3919
|
title: `Switch or add ${info.labelPlural.toLowerCase()}`,
|
|
3327
3920
|
"aria-label": `Switch or add ${info.labelPlural.toLowerCase()}`,
|
|
3328
3921
|
children: [
|
|
3329
|
-
items.length > 1 ? /* @__PURE__ */
|
|
3922
|
+
items.length > 1 ? /* @__PURE__ */ jsxs12("span", { className: "tabular-nums", children: [
|
|
3330
3923
|
activeIndex >= 0 ? activeIndex + 1 : "\u2014",
|
|
3331
3924
|
"/",
|
|
3332
3925
|
items.length
|
|
3333
3926
|
] }) : null,
|
|
3334
|
-
/* @__PURE__ */
|
|
3927
|
+
/* @__PURE__ */ jsx15(ChevronDownIcon, { className: "size-3 opacity-60" })
|
|
3335
3928
|
]
|
|
3336
3929
|
}
|
|
3337
3930
|
) }),
|
|
3338
|
-
/* @__PURE__ */
|
|
3339
|
-
items.length > 5 ? /* @__PURE__ */
|
|
3931
|
+
/* @__PURE__ */ jsx15(PopoverContent, { className: "w-60 p-0", align, children: /* @__PURE__ */ jsxs12(Command, { children: [
|
|
3932
|
+
items.length > 5 ? /* @__PURE__ */ jsx15(
|
|
3340
3933
|
CommandInput,
|
|
3341
3934
|
{
|
|
3342
3935
|
value: query,
|
|
@@ -3344,9 +3937,9 @@ function AssociationEntitySelect({
|
|
|
3344
3937
|
placeholder: `Search ${info.labelPlural.toLowerCase()}\u2026`
|
|
3345
3938
|
}
|
|
3346
3939
|
) : null,
|
|
3347
|
-
/* @__PURE__ */
|
|
3348
|
-
/* @__PURE__ */
|
|
3349
|
-
items.length > 0 ? /* @__PURE__ */
|
|
3940
|
+
/* @__PURE__ */ jsxs12(CommandList, { children: [
|
|
3941
|
+
/* @__PURE__ */ jsx15(CommandEmpty, { children: "No match." }),
|
|
3942
|
+
items.length > 0 ? /* @__PURE__ */ jsx15(CommandGroup, { children: items.map((item) => /* @__PURE__ */ jsxs12(
|
|
3350
3943
|
CommandItem,
|
|
3351
3944
|
{
|
|
3352
3945
|
value: `${item.title} ${item.id}`,
|
|
@@ -3357,7 +3950,7 @@ function AssociationEntitySelect({
|
|
|
3357
3950
|
},
|
|
3358
3951
|
className: "group/entity-ref group gap-2",
|
|
3359
3952
|
children: [
|
|
3360
|
-
/* @__PURE__ */
|
|
3953
|
+
/* @__PURE__ */ jsx15(
|
|
3361
3954
|
CheckIcon,
|
|
3362
3955
|
{
|
|
3363
3956
|
className: cn10(
|
|
@@ -3366,8 +3959,8 @@ function AssociationEntitySelect({
|
|
|
3366
3959
|
)
|
|
3367
3960
|
}
|
|
3368
3961
|
),
|
|
3369
|
-
/* @__PURE__ */
|
|
3370
|
-
|
|
3962
|
+
/* @__PURE__ */ jsx15("span", { className: "min-w-0 flex-1 truncate", children: item.title }),
|
|
3963
|
+
/* @__PURE__ */ jsx15(
|
|
3371
3964
|
"span",
|
|
3372
3965
|
{
|
|
3373
3966
|
className: "inline-flex shrink-0 items-center",
|
|
@@ -3375,8 +3968,8 @@ function AssociationEntitySelect({
|
|
|
3375
3968
|
e.preventDefault();
|
|
3376
3969
|
e.stopPropagation();
|
|
3377
3970
|
},
|
|
3378
|
-
children: /* @__PURE__ */
|
|
3379
|
-
|
|
3971
|
+
children: /* @__PURE__ */ jsx15(
|
|
3972
|
+
DoorControlsSlot,
|
|
3380
3973
|
{
|
|
3381
3974
|
token,
|
|
3382
3975
|
id: item.id,
|
|
@@ -3384,8 +3977,8 @@ function AssociationEntitySelect({
|
|
|
3384
3977
|
}
|
|
3385
3978
|
)
|
|
3386
3979
|
}
|
|
3387
|
-
)
|
|
3388
|
-
adapter.detach && item.id !== activeId ? /* @__PURE__ */
|
|
3980
|
+
),
|
|
3981
|
+
adapter.detach && item.id !== activeId ? /* @__PURE__ */ jsx15(
|
|
3389
3982
|
"button",
|
|
3390
3983
|
{
|
|
3391
3984
|
type: "button",
|
|
@@ -3400,7 +3993,7 @@ function AssociationEntitySelect({
|
|
|
3400
3993
|
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",
|
|
3401
3994
|
title: `Remove this ${entityLabel} from here (does not delete it)`,
|
|
3402
3995
|
"aria-label": `Remove ${item.title}`,
|
|
3403
|
-
children: /* @__PURE__ */
|
|
3996
|
+
children: /* @__PURE__ */ jsx15(XIcon, { className: "size-3" })
|
|
3404
3997
|
}
|
|
3405
3998
|
) : null
|
|
3406
3999
|
]
|
|
@@ -3408,9 +4001,9 @@ function AssociationEntitySelect({
|
|
|
3408
4001
|
item.id
|
|
3409
4002
|
)) }) : null
|
|
3410
4003
|
] }),
|
|
3411
|
-
createSlot !== void 0 ? /* @__PURE__ */
|
|
3412
|
-
createSlot === void 0 && adapter.createAndAttach ? /* @__PURE__ */
|
|
3413
|
-
/* @__PURE__ */
|
|
4004
|
+
createSlot !== void 0 ? /* @__PURE__ */ jsx15("div", { className: "border-t border-border p-1", children: typeof createSlot === "function" ? createSlot(close) : createSlot }) : null,
|
|
4005
|
+
createSlot === void 0 && adapter.createAndAttach ? /* @__PURE__ */ jsx15("div", { className: "border-t border-border p-1", children: creating ? /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1 px-1 py-0.5", children: [
|
|
4006
|
+
/* @__PURE__ */ jsx15(
|
|
3414
4007
|
"input",
|
|
3415
4008
|
{
|
|
3416
4009
|
autoFocus: true,
|
|
@@ -3434,17 +4027,17 @@ function AssociationEntitySelect({
|
|
|
3434
4027
|
"aria-label": `New ${entityLabel} name`
|
|
3435
4028
|
}
|
|
3436
4029
|
),
|
|
3437
|
-
/* @__PURE__ */
|
|
4030
|
+
/* @__PURE__ */ jsx15(
|
|
3438
4031
|
"button",
|
|
3439
4032
|
{
|
|
3440
4033
|
type: "button",
|
|
3441
4034
|
disabled: busy || !draftName.trim(),
|
|
3442
4035
|
onClick: () => void create(draftName),
|
|
3443
4036
|
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",
|
|
3444
|
-
children: busy ? /* @__PURE__ */
|
|
4037
|
+
children: busy ? /* @__PURE__ */ jsx15(SpinnerIcon, { className: "size-3 animate-spin" }) : "Create"
|
|
3445
4038
|
}
|
|
3446
4039
|
)
|
|
3447
|
-
] }) : /* @__PURE__ */
|
|
4040
|
+
] }) : /* @__PURE__ */ jsxs12(
|
|
3448
4041
|
"button",
|
|
3449
4042
|
{
|
|
3450
4043
|
type: "button",
|
|
@@ -3455,7 +4048,7 @@ function AssociationEntitySelect({
|
|
|
3455
4048
|
},
|
|
3456
4049
|
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",
|
|
3457
4050
|
children: [
|
|
3458
|
-
busy ? /* @__PURE__ */
|
|
4051
|
+
busy ? /* @__PURE__ */ jsx15(SpinnerIcon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx15(PlusIcon, { className: "size-3.5" }),
|
|
3459
4052
|
query.trim() ? `Create "${query.trim()}"` : `New ${info.label}`
|
|
3460
4053
|
]
|
|
3461
4054
|
}
|
|
@@ -3470,7 +4063,7 @@ function AssociationEntitySelect({
|
|
|
3470
4063
|
}
|
|
3471
4064
|
|
|
3472
4065
|
// src/react/components/CategorySelect.tsx
|
|
3473
|
-
import { useState as
|
|
4066
|
+
import { useState as useState15 } from "react";
|
|
3474
4067
|
import {
|
|
3475
4068
|
CreatablePicker,
|
|
3476
4069
|
Select,
|
|
@@ -3545,7 +4138,7 @@ function buildCategoryHierarchy(categories) {
|
|
|
3545
4138
|
}
|
|
3546
4139
|
|
|
3547
4140
|
// src/react/components/CategorySelect.tsx
|
|
3548
|
-
import { jsx as
|
|
4141
|
+
import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3549
4142
|
var NONE = "__none__";
|
|
3550
4143
|
var ROOT_PARENT = "__root__";
|
|
3551
4144
|
function plural(noun) {
|
|
@@ -3572,7 +4165,7 @@ function CategorySelect({
|
|
|
3572
4165
|
reload
|
|
3573
4166
|
} = useCategories({ dimension });
|
|
3574
4167
|
const hierarchy = buildCategoryHierarchy(categories);
|
|
3575
|
-
const [createParentId, setCreateParentId] =
|
|
4168
|
+
const [createParentId, setCreateParentId] = useState15(ROOT_PARENT);
|
|
3576
4169
|
const options = [
|
|
3577
4170
|
...allowNone ? [{ value: NONE, label: "None", keywords: "none clear empty" }] : [],
|
|
3578
4171
|
...hierarchy.hasHierarchy ? hierarchy.items.map(({ category, depth, parent }) => ({
|
|
@@ -3581,9 +4174,9 @@ function CategorySelect({
|
|
|
3581
4174
|
// The type-ahead matches the parent's name too, so someone who knows
|
|
3582
4175
|
// the branch but not the leaf still finds it.
|
|
3583
4176
|
keywords: parent ? parent.name : "",
|
|
3584
|
-
render: depth === 0 ? /* @__PURE__ */
|
|
3585
|
-
/* @__PURE__ */
|
|
3586
|
-
/* @__PURE__ */
|
|
4177
|
+
render: depth === 0 ? /* @__PURE__ */ jsx16("span", { className: "font-medium", children: category.name }) : /* @__PURE__ */ jsxs13("span", { className: "flex items-center gap-1.5 pl-3 text-muted-foreground", children: [
|
|
4178
|
+
/* @__PURE__ */ jsx16(CornerDownRightIcon, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
4179
|
+
/* @__PURE__ */ jsx16("span", { className: "text-foreground", children: category.name })
|
|
3587
4180
|
] })
|
|
3588
4181
|
})) : categories.map((category) => ({
|
|
3589
4182
|
value: category.id,
|
|
@@ -3630,8 +4223,8 @@ function CategorySelect({
|
|
|
3630
4223
|
);
|
|
3631
4224
|
return result.id;
|
|
3632
4225
|
};
|
|
3633
|
-
return /* @__PURE__ */
|
|
3634
|
-
/* @__PURE__ */
|
|
4226
|
+
return /* @__PURE__ */ jsxs13("div", { children: [
|
|
4227
|
+
/* @__PURE__ */ jsx16(
|
|
3635
4228
|
CreatablePicker,
|
|
3636
4229
|
{
|
|
3637
4230
|
value: value ?? (allowNone ? NONE : null),
|
|
@@ -3647,18 +4240,18 @@ function CategorySelect({
|
|
|
3647
4240
|
emptyLabel: `No ${noun} matches that.`,
|
|
3648
4241
|
...allowCreate ? { onCreate: create } : {},
|
|
3649
4242
|
...allowCreate && hierarchy.hasHierarchy ? {
|
|
3650
|
-
createExtra: /* @__PURE__ */
|
|
3651
|
-
/* @__PURE__ */
|
|
4243
|
+
createExtra: /* @__PURE__ */ jsxs13(Select, { value: createParentId, onValueChange: setCreateParentId, children: [
|
|
4244
|
+
/* @__PURE__ */ jsx16(
|
|
3652
4245
|
SelectTrigger,
|
|
3653
4246
|
{
|
|
3654
4247
|
className: "h-7 text-xs",
|
|
3655
4248
|
"aria-label": `Put the new ${noun} under`,
|
|
3656
|
-
children: /* @__PURE__ */
|
|
4249
|
+
children: /* @__PURE__ */ jsx16(SelectValue, {})
|
|
3657
4250
|
}
|
|
3658
4251
|
),
|
|
3659
|
-
/* @__PURE__ */
|
|
3660
|
-
/* @__PURE__ */
|
|
3661
|
-
hierarchy.items.filter((item) => item.depth === 0).map((item) => /* @__PURE__ */
|
|
4252
|
+
/* @__PURE__ */ jsxs13(SelectContent, { children: [
|
|
4253
|
+
/* @__PURE__ */ jsx16(SelectItem, { value: ROOT_PARENT, className: "text-xs", children: "Top level" }),
|
|
4254
|
+
hierarchy.items.filter((item) => item.depth === 0).map((item) => /* @__PURE__ */ jsxs13(
|
|
3662
4255
|
SelectItem,
|
|
3663
4256
|
{
|
|
3664
4257
|
value: item.category.id,
|
|
@@ -3678,18 +4271,18 @@ function CategorySelect({
|
|
|
3678
4271
|
selected === null && value !== null ? (
|
|
3679
4272
|
// A stored id whose row this reader cannot see is not "nothing chosen":
|
|
3680
4273
|
// saying so beats a blank box that looks like an unsaved field.
|
|
3681
|
-
/* @__PURE__ */
|
|
4274
|
+
/* @__PURE__ */ jsxs13("p", { className: "mt-1 text-xs text-muted-foreground", children: [
|
|
3682
4275
|
"This ",
|
|
3683
4276
|
noun,
|
|
3684
4277
|
" is no longer available to you."
|
|
3685
4278
|
] })
|
|
3686
4279
|
) : null,
|
|
3687
|
-
error ? /* @__PURE__ */
|
|
4280
|
+
error ? /* @__PURE__ */ jsx16("p", { className: "mt-1 text-xs text-destructive", children: error }) : null
|
|
3688
4281
|
] });
|
|
3689
4282
|
}
|
|
3690
4283
|
|
|
3691
4284
|
// src/react/components/CategoryTagPicker.tsx
|
|
3692
|
-
import { useState as
|
|
4285
|
+
import { useState as useState16 } from "react";
|
|
3693
4286
|
import {
|
|
3694
4287
|
Badge,
|
|
3695
4288
|
Button as Button2,
|
|
@@ -3709,7 +4302,7 @@ import {
|
|
|
3709
4302
|
SelectTrigger as SelectTrigger2,
|
|
3710
4303
|
SelectValue as SelectValue2
|
|
3711
4304
|
} from "@ai-matrx/design-system";
|
|
3712
|
-
import { jsx as
|
|
4305
|
+
import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
3713
4306
|
var ROOT_PARENT2 = "__root__";
|
|
3714
4307
|
function CategoryTagPicker({
|
|
3715
4308
|
entityType,
|
|
@@ -3725,11 +4318,11 @@ function CategoryTagPicker({
|
|
|
3725
4318
|
}) {
|
|
3726
4319
|
const store = useAssociationsStore();
|
|
3727
4320
|
const notifier = useNotifier();
|
|
3728
|
-
const [open, setOpen] =
|
|
3729
|
-
const [creating, setCreating] =
|
|
3730
|
-
const [writing, setWriting] =
|
|
3731
|
-
const [search, setSearch] =
|
|
3732
|
-
const [createParentId, setCreateParentId] =
|
|
4321
|
+
const [open, setOpen] = useState16(false);
|
|
4322
|
+
const [creating, setCreating] = useState16(false);
|
|
4323
|
+
const [writing, setWriting] = useState16(false);
|
|
4324
|
+
const [search, setSearch] = useState16("");
|
|
4325
|
+
const [createParentId, setCreateParentId] = useState16(ROOT_PARENT2);
|
|
3733
4326
|
const {
|
|
3734
4327
|
categories,
|
|
3735
4328
|
create: createCategory,
|
|
@@ -3833,44 +4426,44 @@ function CategoryTagPicker({
|
|
|
3833
4426
|
const exactMatch = categories.some(
|
|
3834
4427
|
(c) => c.name.toLowerCase() === search.trim().toLowerCase()
|
|
3835
4428
|
);
|
|
3836
|
-
return /* @__PURE__ */
|
|
3837
|
-
selected.map((c) => /* @__PURE__ */
|
|
4429
|
+
return /* @__PURE__ */ jsxs14("div", { className: "flex flex-wrap items-center gap-1.5", children: [
|
|
4430
|
+
selected.map((c) => /* @__PURE__ */ jsxs14(
|
|
3838
4431
|
Badge,
|
|
3839
4432
|
{
|
|
3840
4433
|
variant: "secondary",
|
|
3841
4434
|
className: "gap-1 pr-1 text-xs font-medium",
|
|
3842
4435
|
children: [
|
|
3843
4436
|
showHierarchy ? hierarchyById.get(c.id)?.displayName ?? c.name : c.name,
|
|
3844
|
-
/* @__PURE__ */
|
|
4437
|
+
/* @__PURE__ */ jsx17(
|
|
3845
4438
|
"button",
|
|
3846
4439
|
{
|
|
3847
4440
|
type: "button",
|
|
3848
4441
|
onClick: () => void toggle(c.id),
|
|
3849
4442
|
className: "ml-0.5 rounded-full p-0.5 hover:bg-muted-foreground/20",
|
|
3850
4443
|
"aria-label": `Remove ${c.name}`,
|
|
3851
|
-
children: /* @__PURE__ */
|
|
4444
|
+
children: /* @__PURE__ */ jsx17(XIcon, { className: "h-2.5 w-2.5" })
|
|
3852
4445
|
}
|
|
3853
4446
|
)
|
|
3854
4447
|
]
|
|
3855
4448
|
},
|
|
3856
4449
|
c.id
|
|
3857
4450
|
)),
|
|
3858
|
-
/* @__PURE__ */
|
|
3859
|
-
/* @__PURE__ */
|
|
4451
|
+
/* @__PURE__ */ jsxs14(Popover2, { open, onOpenChange: setOpen, children: [
|
|
4452
|
+
/* @__PURE__ */ jsx17(PopoverTrigger2, { asChild: true, children: /* @__PURE__ */ jsxs14(
|
|
3860
4453
|
Button2,
|
|
3861
4454
|
{
|
|
3862
4455
|
variant: "outline",
|
|
3863
4456
|
size: "sm",
|
|
3864
4457
|
className: "h-7 gap-1 px-2 text-xs",
|
|
3865
4458
|
children: [
|
|
3866
|
-
/* @__PURE__ */
|
|
4459
|
+
/* @__PURE__ */ jsx17(Icon, { className: "h-3.5 w-3.5" }),
|
|
3867
4460
|
selected.length === 0 ? addLabel : "Edit",
|
|
3868
|
-
/* @__PURE__ */
|
|
4461
|
+
/* @__PURE__ */ jsx17(ChevronsUpDownIcon, { className: "h-3 w-3 opacity-50" })
|
|
3869
4462
|
]
|
|
3870
4463
|
}
|
|
3871
4464
|
) }),
|
|
3872
|
-
/* @__PURE__ */
|
|
3873
|
-
/* @__PURE__ */
|
|
4465
|
+
/* @__PURE__ */ jsx17(PopoverContent2, { className: "w-56 p-0", align: "start", children: /* @__PURE__ */ jsxs14(Command2, { shouldFilter: false, children: [
|
|
4466
|
+
/* @__PURE__ */ jsx17(
|
|
3874
4467
|
CommandInput2,
|
|
3875
4468
|
{
|
|
3876
4469
|
placeholder: allowCreate ? "Search or create..." : "Search...",
|
|
@@ -3878,9 +4471,9 @@ function CategoryTagPicker({
|
|
|
3878
4471
|
onValueChange: setSearch
|
|
3879
4472
|
}
|
|
3880
4473
|
),
|
|
3881
|
-
/* @__PURE__ */
|
|
3882
|
-
/* @__PURE__ */
|
|
3883
|
-
/* @__PURE__ */
|
|
4474
|
+
/* @__PURE__ */ jsxs14(CommandList2, { children: [
|
|
4475
|
+
/* @__PURE__ */ jsx17(CommandEmpty2, { className: "px-2 py-3 text-xs text-muted-foreground", children: search.trim() ? "No match." : emptyText }),
|
|
4476
|
+
/* @__PURE__ */ jsx17(CommandGroup2, { children: showHierarchy ? filteredHierarchy.map(({ category, depth, parent }) => /* @__PURE__ */ jsxs14(
|
|
3884
4477
|
CommandItem2,
|
|
3885
4478
|
{
|
|
3886
4479
|
value: category.id,
|
|
@@ -3888,7 +4481,7 @@ function CategoryTagPicker({
|
|
|
3888
4481
|
onSelect: () => void toggle(category.id),
|
|
3889
4482
|
className: cn11("text-xs", depth === 1 && "pl-6"),
|
|
3890
4483
|
children: [
|
|
3891
|
-
/* @__PURE__ */
|
|
4484
|
+
/* @__PURE__ */ jsx17(
|
|
3892
4485
|
CheckIcon,
|
|
3893
4486
|
{
|
|
3894
4487
|
className: cn11(
|
|
@@ -3897,19 +4490,19 @@ function CategoryTagPicker({
|
|
|
3897
4490
|
)
|
|
3898
4491
|
}
|
|
3899
4492
|
),
|
|
3900
|
-
depth === 1 ? /* @__PURE__ */
|
|
3901
|
-
/* @__PURE__ */
|
|
4493
|
+
depth === 1 ? /* @__PURE__ */ jsx17(CornerDownRightIcon, { className: "mr-1.5 h-3.5 w-3.5 shrink-0 text-muted-foreground" }) : null,
|
|
4494
|
+
/* @__PURE__ */ jsx17("span", { className: cn11(depth === 0 && "font-medium"), children: category.name })
|
|
3902
4495
|
]
|
|
3903
4496
|
},
|
|
3904
4497
|
category.id
|
|
3905
|
-
)) : filtered.map((c) => /* @__PURE__ */
|
|
4498
|
+
)) : filtered.map((c) => /* @__PURE__ */ jsxs14(
|
|
3906
4499
|
CommandItem2,
|
|
3907
4500
|
{
|
|
3908
4501
|
value: c.id,
|
|
3909
4502
|
onSelect: () => void toggle(c.id),
|
|
3910
4503
|
className: "text-xs",
|
|
3911
4504
|
children: [
|
|
3912
|
-
/* @__PURE__ */
|
|
4505
|
+
/* @__PURE__ */ jsx17(
|
|
3913
4506
|
CheckIcon,
|
|
3914
4507
|
{
|
|
3915
4508
|
className: cn11(
|
|
@@ -3923,25 +4516,25 @@ function CategoryTagPicker({
|
|
|
3923
4516
|
},
|
|
3924
4517
|
c.id
|
|
3925
4518
|
)) }),
|
|
3926
|
-
allowCreate && search.trim() && !exactMatch ? /* @__PURE__ */
|
|
3927
|
-
showHierarchy ? /* @__PURE__ */
|
|
3928
|
-
/* @__PURE__ */
|
|
3929
|
-
/* @__PURE__ */
|
|
4519
|
+
allowCreate && search.trim() && !exactMatch ? /* @__PURE__ */ jsxs14(CommandGroup2, { children: [
|
|
4520
|
+
showHierarchy ? /* @__PURE__ */ jsxs14("div", { className: "space-y-1.5 px-2 pb-2", children: [
|
|
4521
|
+
/* @__PURE__ */ jsx17("p", { className: "text-[11px] font-medium text-muted-foreground", children: "Place in" }),
|
|
4522
|
+
/* @__PURE__ */ jsxs14(
|
|
3930
4523
|
Select2,
|
|
3931
4524
|
{
|
|
3932
4525
|
value: createParentId,
|
|
3933
4526
|
onValueChange: setCreateParentId,
|
|
3934
4527
|
children: [
|
|
3935
|
-
/* @__PURE__ */
|
|
3936
|
-
/* @__PURE__ */
|
|
3937
|
-
/* @__PURE__ */
|
|
3938
|
-
hierarchy.roots.map((root) => /* @__PURE__ */
|
|
4528
|
+
/* @__PURE__ */ jsx17(SelectTrigger2, { size: "sm", className: "bg-background", children: /* @__PURE__ */ jsx17(SelectValue2, {}) }),
|
|
4529
|
+
/* @__PURE__ */ jsxs14(SelectContent2, { children: [
|
|
4530
|
+
/* @__PURE__ */ jsx17(SelectItem2, { value: ROOT_PARENT2, children: "Top level" }),
|
|
4531
|
+
hierarchy.roots.map((root) => /* @__PURE__ */ jsx17(SelectItem2, { value: root.id, children: root.name }, root.id))
|
|
3939
4532
|
] })
|
|
3940
4533
|
]
|
|
3941
4534
|
}
|
|
3942
4535
|
)
|
|
3943
4536
|
] }) : null,
|
|
3944
|
-
/* @__PURE__ */
|
|
4537
|
+
/* @__PURE__ */ jsxs14(
|
|
3945
4538
|
CommandItem2,
|
|
3946
4539
|
{
|
|
3947
4540
|
value: `__create__${search}`,
|
|
@@ -3949,7 +4542,7 @@ function CategoryTagPicker({
|
|
|
3949
4542
|
disabled: creating,
|
|
3950
4543
|
className: "text-xs text-primary",
|
|
3951
4544
|
children: [
|
|
3952
|
-
creating ? /* @__PURE__ */
|
|
4545
|
+
creating ? /* @__PURE__ */ jsx17(SpinnerIcon, { className: "mr-2 h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx17(Icon, { className: "mr-2 h-3.5 w-3.5" }),
|
|
3953
4546
|
"Create \u201C",
|
|
3954
4547
|
search.trim(),
|
|
3955
4548
|
"\u201D"
|
|
@@ -3964,7 +4557,7 @@ function CategoryTagPicker({
|
|
|
3964
4557
|
}
|
|
3965
4558
|
|
|
3966
4559
|
// src/react/components/CommentThread.tsx
|
|
3967
|
-
import { useState as
|
|
4560
|
+
import { useState as useState17 } from "react";
|
|
3968
4561
|
import { Button as Button3, Skeleton as Skeleton2, cn as cn12 } from "@ai-matrx/design-system";
|
|
3969
4562
|
|
|
3970
4563
|
// src/react/relativeTime.ts
|
|
@@ -3992,7 +4585,7 @@ function formatRelativeTime(iso, now = Date.now()) {
|
|
|
3992
4585
|
}
|
|
3993
4586
|
|
|
3994
4587
|
// src/react/components/CommentThread.tsx
|
|
3995
|
-
import { Fragment as Fragment5, jsx as
|
|
4588
|
+
import { Fragment as Fragment5, jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
3996
4589
|
var AUTHOR_DOOR_TOKEN = "user";
|
|
3997
4590
|
function resolveAuthor(comment, authorDisplay) {
|
|
3998
4591
|
const base2 = {
|
|
@@ -4011,7 +4604,7 @@ function resolveAuthor(comment, authorDisplay) {
|
|
|
4011
4604
|
}
|
|
4012
4605
|
function AuthorAvatar({ author }) {
|
|
4013
4606
|
if (author.avatarUrl) {
|
|
4014
|
-
return /* @__PURE__ */
|
|
4607
|
+
return /* @__PURE__ */ jsx18(
|
|
4015
4608
|
"img",
|
|
4016
4609
|
{
|
|
4017
4610
|
src: author.avatarUrl,
|
|
@@ -4020,7 +4613,7 @@ function AuthorAvatar({ author }) {
|
|
|
4020
4613
|
}
|
|
4021
4614
|
);
|
|
4022
4615
|
}
|
|
4023
|
-
return /* @__PURE__ */
|
|
4616
|
+
return /* @__PURE__ */ jsx18(
|
|
4024
4617
|
"span",
|
|
4025
4618
|
{
|
|
4026
4619
|
"aria-hidden": true,
|
|
@@ -4035,7 +4628,7 @@ function AuthorName({
|
|
|
4035
4628
|
}) {
|
|
4036
4629
|
const DoorRef2 = entityDoors?.EntityRef;
|
|
4037
4630
|
if (DoorRef2 && author.userId) {
|
|
4038
|
-
return /* @__PURE__ */
|
|
4631
|
+
return /* @__PURE__ */ jsx18(
|
|
4039
4632
|
DoorRef2,
|
|
4040
4633
|
{
|
|
4041
4634
|
token: AUTHOR_DOOR_TOKEN,
|
|
@@ -4046,7 +4639,7 @@ function AuthorName({
|
|
|
4046
4639
|
}
|
|
4047
4640
|
);
|
|
4048
4641
|
}
|
|
4049
|
-
return /* @__PURE__ */
|
|
4642
|
+
return /* @__PURE__ */ jsx18("span", { className: "text-sm font-medium text-foreground", children: author.name });
|
|
4050
4643
|
}
|
|
4051
4644
|
function CommentComposer({
|
|
4052
4645
|
onSubmit,
|
|
@@ -4057,9 +4650,9 @@ function CommentComposer({
|
|
|
4057
4650
|
autoFocus = false
|
|
4058
4651
|
}) {
|
|
4059
4652
|
const notifier = useNotifier();
|
|
4060
|
-
const [body, setBody] =
|
|
4061
|
-
const [submitting, setSubmitting] =
|
|
4062
|
-
const [error, setError] =
|
|
4653
|
+
const [body, setBody] = useState17(initialValue);
|
|
4654
|
+
const [submitting, setSubmitting] = useState17(false);
|
|
4655
|
+
const [error, setError] = useState17(null);
|
|
4063
4656
|
async function submit(e) {
|
|
4064
4657
|
e?.preventDefault();
|
|
4065
4658
|
const trimmed = body.trim();
|
|
@@ -4076,8 +4669,8 @@ function CommentComposer({
|
|
|
4076
4669
|
}
|
|
4077
4670
|
setBody("");
|
|
4078
4671
|
}
|
|
4079
|
-
return /* @__PURE__ */
|
|
4080
|
-
/* @__PURE__ */
|
|
4672
|
+
return /* @__PURE__ */ jsxs15("form", { onSubmit: submit, className: "flex flex-col gap-1.5", children: [
|
|
4673
|
+
/* @__PURE__ */ jsx18(
|
|
4081
4674
|
"textarea",
|
|
4082
4675
|
{
|
|
4083
4676
|
value: body,
|
|
@@ -4092,10 +4685,10 @@ function CommentComposer({
|
|
|
4092
4685
|
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"
|
|
4093
4686
|
}
|
|
4094
4687
|
),
|
|
4095
|
-
error && /* @__PURE__ */
|
|
4096
|
-
/* @__PURE__ */
|
|
4097
|
-
/* @__PURE__ */
|
|
4098
|
-
submitting && /* @__PURE__ */
|
|
4688
|
+
error && /* @__PURE__ */ jsx18("p", { role: "alert", className: "text-xs text-destructive", children: error }),
|
|
4689
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
|
|
4690
|
+
/* @__PURE__ */ jsxs15(Button3, { type: "submit", size: "sm", disabled: !body.trim() || submitting, children: [
|
|
4691
|
+
submitting && /* @__PURE__ */ jsx18(SpinnerIcon, { className: "mr-1 h-3 w-3 animate-spin" }),
|
|
4099
4692
|
submitLabel
|
|
4100
4693
|
] }),
|
|
4101
4694
|
trailing
|
|
@@ -4111,10 +4704,10 @@ function CommentItem({
|
|
|
4111
4704
|
}) {
|
|
4112
4705
|
const { authorDisplay, entityDoors } = useAssociationsUiPorts();
|
|
4113
4706
|
const notifier = useNotifier();
|
|
4114
|
-
const [replying, setReplying] =
|
|
4115
|
-
const [editing, setEditing] =
|
|
4116
|
-
const [confirmingDelete, setConfirmingDelete] =
|
|
4117
|
-
const [deleting, setDeleting] =
|
|
4707
|
+
const [replying, setReplying] = useState17(false);
|
|
4708
|
+
const [editing, setEditing] = useState17(false);
|
|
4709
|
+
const [confirmingDelete, setConfirmingDelete] = useState17(false);
|
|
4710
|
+
const [deleting, setDeleting] = useState17(false);
|
|
4118
4711
|
const author = resolveAuthor(comment, authorDisplay);
|
|
4119
4712
|
const isOwn = thread.currentUserId !== null && comment.createdBy === thread.currentUserId;
|
|
4120
4713
|
const edited = comment.updatedAt !== comment.createdAt;
|
|
@@ -4128,7 +4721,7 @@ function CommentItem({
|
|
|
4128
4721
|
notifier.error(res.error ?? "Could not delete the comment");
|
|
4129
4722
|
}
|
|
4130
4723
|
}
|
|
4131
|
-
return /* @__PURE__ */
|
|
4724
|
+
return /* @__PURE__ */ jsxs15(
|
|
4132
4725
|
"div",
|
|
4133
4726
|
{
|
|
4134
4727
|
"data-comment-id": comment.id,
|
|
@@ -4137,10 +4730,10 @@ function CommentItem({
|
|
|
4137
4730
|
depth > 0 && "border-l border-border pl-3"
|
|
4138
4731
|
),
|
|
4139
4732
|
children: [
|
|
4140
|
-
/* @__PURE__ */
|
|
4141
|
-
/* @__PURE__ */
|
|
4142
|
-
/* @__PURE__ */
|
|
4143
|
-
/* @__PURE__ */
|
|
4733
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
|
|
4734
|
+
/* @__PURE__ */ jsx18(AuthorAvatar, { author }),
|
|
4735
|
+
/* @__PURE__ */ jsx18(AuthorName, { author, entityDoors }),
|
|
4736
|
+
/* @__PURE__ */ jsx18(
|
|
4144
4737
|
"span",
|
|
4145
4738
|
{
|
|
4146
4739
|
className: "text-xs text-muted-foreground",
|
|
@@ -4148,7 +4741,7 @@ function CommentItem({
|
|
|
4148
4741
|
children: formatRelativeTime(comment.createdAt)
|
|
4149
4742
|
}
|
|
4150
4743
|
),
|
|
4151
|
-
edited && /* @__PURE__ */
|
|
4744
|
+
edited && /* @__PURE__ */ jsx18(
|
|
4152
4745
|
"span",
|
|
4153
4746
|
{
|
|
4154
4747
|
className: "text-xs text-muted-foreground",
|
|
@@ -4157,7 +4750,7 @@ function CommentItem({
|
|
|
4157
4750
|
}
|
|
4158
4751
|
)
|
|
4159
4752
|
] }),
|
|
4160
|
-
editing ? /* @__PURE__ */
|
|
4753
|
+
editing ? /* @__PURE__ */ jsx18("div", { className: "pl-8", children: /* @__PURE__ */ jsx18(
|
|
4161
4754
|
CommentComposer,
|
|
4162
4755
|
{
|
|
4163
4756
|
initialValue: comment.body,
|
|
@@ -4169,7 +4762,7 @@ function CommentItem({
|
|
|
4169
4762
|
if (res.ok) setEditing(false);
|
|
4170
4763
|
return res;
|
|
4171
4764
|
},
|
|
4172
|
-
trailing: /* @__PURE__ */
|
|
4765
|
+
trailing: /* @__PURE__ */ jsx18(
|
|
4173
4766
|
Button3,
|
|
4174
4767
|
{
|
|
4175
4768
|
type: "button",
|
|
@@ -4180,10 +4773,10 @@ function CommentItem({
|
|
|
4180
4773
|
}
|
|
4181
4774
|
)
|
|
4182
4775
|
}
|
|
4183
|
-
) }) : /* @__PURE__ */
|
|
4184
|
-
!editing && /* @__PURE__ */
|
|
4185
|
-
/* @__PURE__ */
|
|
4186
|
-
/* @__PURE__ */
|
|
4776
|
+
) }) : /* @__PURE__ */ jsx18("p", { className: "whitespace-pre-wrap pl-8 text-sm text-foreground", children: comment.body }),
|
|
4777
|
+
!editing && /* @__PURE__ */ jsx18("div", { className: "flex items-center gap-1 pl-7", children: confirmingDelete ? /* @__PURE__ */ jsxs15(Fragment5, { children: [
|
|
4778
|
+
/* @__PURE__ */ jsx18("span", { className: "text-xs text-destructive", children: "Deletes this comment for everyone." }),
|
|
4779
|
+
/* @__PURE__ */ jsxs15(
|
|
4187
4780
|
Button3,
|
|
4188
4781
|
{
|
|
4189
4782
|
type: "button",
|
|
@@ -4192,12 +4785,12 @@ function CommentItem({
|
|
|
4192
4785
|
disabled: deleting,
|
|
4193
4786
|
onClick: () => void confirmDelete(),
|
|
4194
4787
|
children: [
|
|
4195
|
-
deleting && /* @__PURE__ */
|
|
4788
|
+
deleting && /* @__PURE__ */ jsx18(SpinnerIcon, { className: "mr-1 h-3 w-3 animate-spin" }),
|
|
4196
4789
|
"Delete"
|
|
4197
4790
|
]
|
|
4198
4791
|
}
|
|
4199
4792
|
),
|
|
4200
|
-
/* @__PURE__ */
|
|
4793
|
+
/* @__PURE__ */ jsx18(
|
|
4201
4794
|
Button3,
|
|
4202
4795
|
{
|
|
4203
4796
|
type: "button",
|
|
@@ -4208,8 +4801,8 @@ function CommentItem({
|
|
|
4208
4801
|
children: "Cancel"
|
|
4209
4802
|
}
|
|
4210
4803
|
)
|
|
4211
|
-
] }) : /* @__PURE__ */
|
|
4212
|
-
/* @__PURE__ */
|
|
4804
|
+
] }) : /* @__PURE__ */ jsxs15(Fragment5, { children: [
|
|
4805
|
+
/* @__PURE__ */ jsx18(
|
|
4213
4806
|
"button",
|
|
4214
4807
|
{
|
|
4215
4808
|
type: "button",
|
|
@@ -4218,8 +4811,8 @@ function CommentItem({
|
|
|
4218
4811
|
children: "Reply"
|
|
4219
4812
|
}
|
|
4220
4813
|
),
|
|
4221
|
-
isOwn && /* @__PURE__ */
|
|
4222
|
-
/* @__PURE__ */
|
|
4814
|
+
isOwn && /* @__PURE__ */ jsxs15(Fragment5, { children: [
|
|
4815
|
+
/* @__PURE__ */ jsx18(
|
|
4223
4816
|
"button",
|
|
4224
4817
|
{
|
|
4225
4818
|
type: "button",
|
|
@@ -4228,7 +4821,7 @@ function CommentItem({
|
|
|
4228
4821
|
children: "Edit"
|
|
4229
4822
|
}
|
|
4230
4823
|
),
|
|
4231
|
-
/* @__PURE__ */
|
|
4824
|
+
/* @__PURE__ */ jsx18(
|
|
4232
4825
|
"button",
|
|
4233
4826
|
{
|
|
4234
4827
|
type: "button",
|
|
@@ -4239,9 +4832,9 @@ function CommentItem({
|
|
|
4239
4832
|
)
|
|
4240
4833
|
] })
|
|
4241
4834
|
] }) }),
|
|
4242
|
-
replying && /* @__PURE__ */
|
|
4243
|
-
/* @__PURE__ */
|
|
4244
|
-
/* @__PURE__ */
|
|
4835
|
+
replying && /* @__PURE__ */ jsxs15("div", { className: "flex items-start gap-2 pl-8 pt-1", children: [
|
|
4836
|
+
/* @__PURE__ */ jsx18(CornerDownRightIcon, { className: "mt-2 h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
|
|
4837
|
+
/* @__PURE__ */ jsx18("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx18(
|
|
4245
4838
|
CommentComposer,
|
|
4246
4839
|
{
|
|
4247
4840
|
submitLabel: "Reply",
|
|
@@ -4255,7 +4848,7 @@ function CommentItem({
|
|
|
4255
4848
|
if (res.ok) setReplying(false);
|
|
4256
4849
|
return res;
|
|
4257
4850
|
},
|
|
4258
|
-
trailing: /* @__PURE__ */
|
|
4851
|
+
trailing: /* @__PURE__ */ jsx18(
|
|
4259
4852
|
Button3,
|
|
4260
4853
|
{
|
|
4261
4854
|
type: "button",
|
|
@@ -4268,7 +4861,7 @@ function CommentItem({
|
|
|
4268
4861
|
}
|
|
4269
4862
|
) })
|
|
4270
4863
|
] }),
|
|
4271
|
-
replies.length > 0 && /* @__PURE__ */
|
|
4864
|
+
replies.length > 0 && /* @__PURE__ */ jsx18("div", { className: "flex flex-col gap-3 pl-8 pt-1", children: replies.map((reply) => /* @__PURE__ */ jsx18(
|
|
4272
4865
|
CommentItem,
|
|
4273
4866
|
{
|
|
4274
4867
|
comment: reply,
|
|
@@ -4308,11 +4901,11 @@ function CommentThread({
|
|
|
4308
4901
|
const thread = useComments({ token, id });
|
|
4309
4902
|
const { roots, childrenOf } = buildCommentTree(thread.comments);
|
|
4310
4903
|
const loading = (thread.status === "loading" || thread.status === "idle") && thread.comments.length === 0;
|
|
4311
|
-
return /* @__PURE__ */
|
|
4312
|
-
showHeader && /* @__PURE__ */
|
|
4313
|
-
/* @__PURE__ */
|
|
4314
|
-
/* @__PURE__ */
|
|
4315
|
-
/* @__PURE__ */
|
|
4904
|
+
return /* @__PURE__ */ jsxs15("section", { className: cn12("flex flex-col gap-3", className), children: [
|
|
4905
|
+
showHeader && /* @__PURE__ */ jsxs15("header", { className: "flex items-center gap-2", children: [
|
|
4906
|
+
/* @__PURE__ */ jsx18("h3", { className: "text-sm font-semibold text-foreground", children: "Comments" }),
|
|
4907
|
+
/* @__PURE__ */ jsx18("span", { className: "text-xs text-muted-foreground tabular-nums", children: thread.comments.length }),
|
|
4908
|
+
/* @__PURE__ */ jsx18(
|
|
4316
4909
|
"button",
|
|
4317
4910
|
{
|
|
4318
4911
|
type: "button",
|
|
@@ -4320,18 +4913,18 @@ function CommentThread({
|
|
|
4320
4913
|
"aria-label": "Refresh comments",
|
|
4321
4914
|
title: "Refresh comments",
|
|
4322
4915
|
className: "ml-auto flex h-6 w-6 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
|
|
4323
|
-
children: thread.status === "loading" ? /* @__PURE__ */
|
|
4916
|
+
children: thread.status === "loading" ? /* @__PURE__ */ jsx18(SpinnerIcon, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx18(RefreshIcon, { className: "h-3.5 w-3.5" })
|
|
4324
4917
|
}
|
|
4325
4918
|
)
|
|
4326
4919
|
] }),
|
|
4327
|
-
thread.error && /* @__PURE__ */
|
|
4920
|
+
thread.error && /* @__PURE__ */ jsxs15(
|
|
4328
4921
|
"div",
|
|
4329
4922
|
{
|
|
4330
4923
|
role: "alert",
|
|
4331
4924
|
className: "flex items-center gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive",
|
|
4332
4925
|
children: [
|
|
4333
|
-
/* @__PURE__ */
|
|
4334
|
-
/* @__PURE__ */
|
|
4926
|
+
/* @__PURE__ */ jsx18("span", { className: "min-w-0 flex-1", children: thread.error }),
|
|
4927
|
+
/* @__PURE__ */ jsx18(
|
|
4335
4928
|
Button3,
|
|
4336
4929
|
{
|
|
4337
4930
|
type: "button",
|
|
@@ -4345,10 +4938,10 @@ function CommentThread({
|
|
|
4345
4938
|
]
|
|
4346
4939
|
}
|
|
4347
4940
|
),
|
|
4348
|
-
loading ? /* @__PURE__ */
|
|
4349
|
-
/* @__PURE__ */
|
|
4350
|
-
/* @__PURE__ */
|
|
4351
|
-
] }) : roots.length === 0 && !thread.error ? /* @__PURE__ */
|
|
4941
|
+
loading ? /* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-3", "aria-hidden": true, "data-testid": "comments-loading", children: [
|
|
4942
|
+
/* @__PURE__ */ jsx18(Skeleton2, { className: "h-12 w-full" }),
|
|
4943
|
+
/* @__PURE__ */ jsx18(Skeleton2, { className: "h-12 w-4/5" })
|
|
4944
|
+
] }) : roots.length === 0 && !thread.error ? /* @__PURE__ */ jsx18("p", { className: "text-sm text-muted-foreground", children: "No comments yet." }) : /* @__PURE__ */ jsx18("div", { className: "flex flex-col gap-4", children: roots.map((comment) => /* @__PURE__ */ jsx18(
|
|
4352
4945
|
CommentItem,
|
|
4353
4946
|
{
|
|
4354
4947
|
comment,
|
|
@@ -4359,7 +4952,7 @@ function CommentThread({
|
|
|
4359
4952
|
},
|
|
4360
4953
|
comment.id
|
|
4361
4954
|
)) }),
|
|
4362
|
-
/* @__PURE__ */
|
|
4955
|
+
/* @__PURE__ */ jsx18(
|
|
4363
4956
|
CommentComposer,
|
|
4364
4957
|
{
|
|
4365
4958
|
onSubmit: (body) => thread.add(body, { orgId: orgId ?? null })
|
|
@@ -4385,6 +4978,8 @@ export {
|
|
|
4385
4978
|
CommentComposer,
|
|
4386
4979
|
CommentThread,
|
|
4387
4980
|
DefaultEntityIcon,
|
|
4981
|
+
DefaultWindowShell,
|
|
4982
|
+
DoorControlsSlot,
|
|
4388
4983
|
DoorRef,
|
|
4389
4984
|
PrimaryEntityProvider,
|
|
4390
4985
|
UniversalAssociationPicker,
|
|
@@ -4393,8 +4988,11 @@ export {
|
|
|
4393
4988
|
buildCommentTree,
|
|
4394
4989
|
formatRelativeTime,
|
|
4395
4990
|
getContentRoleMeta,
|
|
4991
|
+
lazyPickerOverride,
|
|
4992
|
+
lazyWindowShell,
|
|
4396
4993
|
useAssociationCandidates,
|
|
4397
4994
|
useAssociationEntitySelectAdapter,
|
|
4995
|
+
useAssociationPickerBridge,
|
|
4398
4996
|
useAssociations,
|
|
4399
4997
|
useAssociationsStore,
|
|
4400
4998
|
useAssociationsUiPorts,
|