@ai-matrx/associations 0.10.7 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ComponentType } from 'react';
3
- import { j as AssociationsNotifier, W as WindowShellPort, k as CapturePort, P as PickerOverrideMap, E as EntityDoorsPort, l as AuthorDisplayPort, m as AssociationsStore, n as CategoryMutationResult, o as CategoryDimension, p as PlatformCategory, q as CategoriesEntry, r as EntityTypeToken, s as UniversalCandidate, t as CandidateRecord, u as PlatformComment, v as CommentsEntry, w as CommentMutationResult, e as AssociationPickerProps, x as EntityDoorProps, y as EntityRefProps, z as ContentRole } from '../CommentThread-DobuYuwz.cjs';
4
- export { B as AssociationCaptureToolbar, A as AssociationCaptureToolbarProps, D as AssociationCard, F as AssociationCardGrid, b as AssociationCardGridProps, a as AssociationCardProps, G as AssociationEntitySelect, H as AssociationEntitySelectAdapter, I as AssociationEntitySelectItem, c as AssociationEntitySelectProps, J as AssociationList, d as AssociationListProps, K as AssociationWindow, f as AssociationWindowProps, L as AssociationWriteResult, M as AttachedItemsSheet, g as AttachedItemsSheetProps, N as CaptureAttach, O as CaptureAttachResult, Q as CaptureToolbarAction, R as CategorySelect, C as CategorySelectProps, S as CategoryTagPicker, h as CategoryTagPickerProps, T as CommentComposer, V as CommentComposerProps, X as CommentThread, i as CommentThreadProps, Y as ContainerLink, Z as ContainerResourceRow, _ as ContainerResourcesAdapter, $ as DefaultEntityIcon, a0 as PrimaryEntity, a1 as PrimaryEntityProvider, a2 as TagPickerIcon, a3 as UniversalAssociationPicker, U as UniversalAssociationPickerProps, a4 as UseAssociationEntitySelectAdapterArgs, a5 as UseAssociationsArgs, a6 as UseAssociationsReturn, a7 as UseContainerLinksArgs, a8 as UseContainerLinksReturn, a9 as attachedKey, aa as buildCommentTree, ab as useAssociationEntitySelectAdapter, ac as useAssociations, ad as useContainerLinks, ae as useContainerLinksAdapter, af as useEntityRelationships, ag as usePrimaryEntity } from '../CommentThread-DobuYuwz.cjs';
3
+ import { j as AssociationsNotifier, W as WindowShellPort, k as CapturePort, P as PickerOverrideMap, E as EntityDoorsPort, l as AuthorDisplayPort, m as AssociationsStore, n as CategoryMutationResult, o as CategoryDimension, p as PlatformCategory, q as CategoriesEntry, r as EntityTypeToken, s as UniversalCandidate, t as CandidateRecord, u as PlatformComment, v as CommentsEntry, w as CommentMutationResult, e as AssociationPickerProps, x as EntityDoorProps, y as EntityRefProps, z as ContentRole } from '../CommentThread-ar5rzifm.cjs';
4
+ export { B as AssociationCaptureToolbar, A as AssociationCaptureToolbarProps, D as AssociationCard, F as AssociationCardGrid, b as AssociationCardGridProps, a as AssociationCardProps, G as AssociationEntitySelect, H as AssociationEntitySelectAdapter, I as AssociationEntitySelectItem, c as AssociationEntitySelectProps, J as AssociationList, d as AssociationListProps, K as AssociationWindow, f as AssociationWindowProps, L as AssociationWriteResult, M as AttachedItemsSheet, g as AttachedItemsSheetProps, N as CaptureAttach, O as CaptureAttachResult, Q as CaptureToolbarAction, R as CategorySelect, C as CategorySelectProps, S as CategoryTagPicker, h as CategoryTagPickerProps, T as CommentComposer, V as CommentComposerProps, X as CommentThread, i as CommentThreadProps, Y as ContainerLink, Z as ContainerResourceRow, _ as ContainerResourcesAdapter, $ as DefaultEntityIcon, a0 as PrimaryContainerEntity, a1 as PrimaryEntity, a2 as PrimaryEntityProvider, a3 as PrimaryFiledEntity, a4 as TagPickerIcon, a5 as UniversalAssociationPicker, U as UniversalAssociationPickerProps, a6 as UseAssociationEntitySelectAdapterArgs, a7 as UseAssociationsArgs, a8 as UseAssociationsReturn, a9 as UseContainerLinksArgs, aa as UseContainerLinksReturn, ab as attachedKey, ac as buildCommentTree, ad as containerLinksArgs, ae as useAssociationEntitySelectAdapter, af as useAssociations, ag as useContainerLinks, ah as useContainerLinksAdapter, ai as useEntityRelationships, aj as usePrimaryEntity } from '../CommentThread-ar5rzifm.cjs';
5
5
 
6
6
  interface AssociationsUiPorts {
7
7
  notifier?: AssociationsNotifier;
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ComponentType } from 'react';
3
- import { j as AssociationsNotifier, W as WindowShellPort, k as CapturePort, P as PickerOverrideMap, E as EntityDoorsPort, l as AuthorDisplayPort, m as AssociationsStore, n as CategoryMutationResult, o as CategoryDimension, p as PlatformCategory, q as CategoriesEntry, r as EntityTypeToken, s as UniversalCandidate, t as CandidateRecord, u as PlatformComment, v as CommentsEntry, w as CommentMutationResult, e as AssociationPickerProps, x as EntityDoorProps, y as EntityRefProps, z as ContentRole } from '../CommentThread-DobuYuwz.js';
4
- export { B as AssociationCaptureToolbar, A as AssociationCaptureToolbarProps, D as AssociationCard, F as AssociationCardGrid, b as AssociationCardGridProps, a as AssociationCardProps, G as AssociationEntitySelect, H as AssociationEntitySelectAdapter, I as AssociationEntitySelectItem, c as AssociationEntitySelectProps, J as AssociationList, d as AssociationListProps, K as AssociationWindow, f as AssociationWindowProps, L as AssociationWriteResult, M as AttachedItemsSheet, g as AttachedItemsSheetProps, N as CaptureAttach, O as CaptureAttachResult, Q as CaptureToolbarAction, R as CategorySelect, C as CategorySelectProps, S as CategoryTagPicker, h as CategoryTagPickerProps, T as CommentComposer, V as CommentComposerProps, X as CommentThread, i as CommentThreadProps, Y as ContainerLink, Z as ContainerResourceRow, _ as ContainerResourcesAdapter, $ as DefaultEntityIcon, a0 as PrimaryEntity, a1 as PrimaryEntityProvider, a2 as TagPickerIcon, a3 as UniversalAssociationPicker, U as UniversalAssociationPickerProps, a4 as UseAssociationEntitySelectAdapterArgs, a5 as UseAssociationsArgs, a6 as UseAssociationsReturn, a7 as UseContainerLinksArgs, a8 as UseContainerLinksReturn, a9 as attachedKey, aa as buildCommentTree, ab as useAssociationEntitySelectAdapter, ac as useAssociations, ad as useContainerLinks, ae as useContainerLinksAdapter, af as useEntityRelationships, ag as usePrimaryEntity } from '../CommentThread-DobuYuwz.js';
3
+ import { j as AssociationsNotifier, W as WindowShellPort, k as CapturePort, P as PickerOverrideMap, E as EntityDoorsPort, l as AuthorDisplayPort, m as AssociationsStore, n as CategoryMutationResult, o as CategoryDimension, p as PlatformCategory, q as CategoriesEntry, r as EntityTypeToken, s as UniversalCandidate, t as CandidateRecord, u as PlatformComment, v as CommentsEntry, w as CommentMutationResult, e as AssociationPickerProps, x as EntityDoorProps, y as EntityRefProps, z as ContentRole } from '../CommentThread-ar5rzifm.js';
4
+ export { B as AssociationCaptureToolbar, A as AssociationCaptureToolbarProps, D as AssociationCard, F as AssociationCardGrid, b as AssociationCardGridProps, a as AssociationCardProps, G as AssociationEntitySelect, H as AssociationEntitySelectAdapter, I as AssociationEntitySelectItem, c as AssociationEntitySelectProps, J as AssociationList, d as AssociationListProps, K as AssociationWindow, f as AssociationWindowProps, L as AssociationWriteResult, M as AttachedItemsSheet, g as AttachedItemsSheetProps, N as CaptureAttach, O as CaptureAttachResult, Q as CaptureToolbarAction, R as CategorySelect, C as CategorySelectProps, S as CategoryTagPicker, h as CategoryTagPickerProps, T as CommentComposer, V as CommentComposerProps, X as CommentThread, i as CommentThreadProps, Y as ContainerLink, Z as ContainerResourceRow, _ as ContainerResourcesAdapter, $ as DefaultEntityIcon, a0 as PrimaryContainerEntity, a1 as PrimaryEntity, a2 as PrimaryEntityProvider, a3 as PrimaryFiledEntity, a4 as TagPickerIcon, a5 as UniversalAssociationPicker, U as UniversalAssociationPickerProps, a6 as UseAssociationEntitySelectAdapterArgs, a7 as UseAssociationsArgs, a8 as UseAssociationsReturn, a9 as UseContainerLinksArgs, aa as UseContainerLinksReturn, ab as attachedKey, ac as buildCommentTree, ad as containerLinksArgs, ae as useAssociationEntitySelectAdapter, af as useAssociations, ag as useContainerLinks, ah as useContainerLinksAdapter, ai as useEntityRelationships, aj as usePrimaryEntity } from '../CommentThread-ar5rzifm.js';
5
5
 
6
6
  interface AssociationsUiPorts {
7
7
  notifier?: AssociationsNotifier;
@@ -1193,6 +1193,77 @@ import {
1193
1193
  useRef as useRef3,
1194
1194
  useState
1195
1195
  } from "react";
1196
+
1197
+ // src/edges.ts
1198
+ var ASSOCIATION_TARGET_TYPES = [
1199
+ "organization",
1200
+ // an org — the top-level container resources attach into
1201
+ "scope",
1202
+ "scope_type",
1203
+ "project",
1204
+ "task",
1205
+ "context_item",
1206
+ "thread",
1207
+ "war_room",
1208
+ "category",
1209
+ "conversation",
1210
+ // a chat conversation a working_document is attached to
1211
+ "fc_set",
1212
+ // a flashcard set (card→set membership)
1213
+ "fc_card",
1214
+ // a flashcard (card→card hierarchy, quiz→card)
1215
+ "study_media",
1216
+ // a study-media artifact (summary/mind_map) — converter lineage target
1217
+ "assessment",
1218
+ // a quiz / practice test — a converter lineage target + origin (assessment→artifact 'source')
1219
+ "note",
1220
+ // a note — the origin a converted study artifact links back to (artifact→note 'source' lineage, P4)
1221
+ "file",
1222
+ // a file (card→file media + source lineage)
1223
+ "quiz_session",
1224
+ // a quiz a card is used in
1225
+ "agent",
1226
+ // an orchestrator agent — the container an Orchestra's member agents attach into (role 'member'); an 'orchestra' self-edge marks the orchestrator
1227
+ "agent_term_list",
1228
+ // a term list — durable file attachments target its vocabulary resource
1229
+ "research_tag",
1230
+ // a research tag a source is filed under (rs_source_tag M2M collapsed into associations)
1231
+ "skill",
1232
+ // a skill — projects link to it (skill→project, role 'member'); code_files attach as resources (code_file→skill, role 'resource')
1233
+ "surface",
1234
+ // a UI surface — agents bind here (agent→surface); context menu reads via agent.menu_surface
1235
+ "plan_entity",
1236
+ // a Content Planning source/media citation — plan_node role edges (about/cites/embeds) target it
1237
+ "plan_node",
1238
+ // a planned page — research topics/tags attach here and follow realization into web_page
1239
+ "web_site",
1240
+ // a web.site — parties attach onto a site's roster (party→web_site role 'writes_for')
1241
+ "seo_topic",
1242
+ // a taxonomy topic — plan_node/plan_entity tag into it (role 'topic')
1243
+ "seo_keyword",
1244
+ // a keyword — plan_node attaches secondaries (role 'secondary_keyword'; the primary is an FK, never an edge)
1245
+ "web_page",
1246
+ // a canonical page — the page workspace attaches notes/tasks/files/keywords onto it (keyword edges: role 'primary'|'supporting')
1247
+ "web_screenshot",
1248
+ // a page capture — per-image attachments on the page workspace
1249
+ "party",
1250
+ // a CRM person/company (crm.party) — the record page attaches tasks/files/notes onto it
1251
+ "crm_outreach_list",
1252
+ // a CRM outreach list (a worked send/dial queue) — parties and resources attach into it
1253
+ "crm_deal",
1254
+ // a CRM deal (crm.deal) — people on it (role 'deal_contact'), tasks/files attach onto it
1255
+ "rulebook",
1256
+ // a Masterwork Rulebook — its Scout interviews attach here (conversation→rulebook, role 'interview')
1257
+ "research_topic",
1258
+ // a research topic — Sources are filed under it (processed_document→research_topic, the landing door's edge)
1259
+ "media_source_library"
1260
+ // a media-catalog Library — Sources are catalogued into it (processed_document→media_source_library)
1261
+ ];
1262
+ function isAssociationTargetType(value) {
1263
+ return typeof value === "string" && ASSOCIATION_TARGET_TYPES.includes(value);
1264
+ }
1265
+
1266
+ // src/react/hooks/useContainerLinks.ts
1196
1267
  function metadataRecord(metadata) {
1197
1268
  if (metadata === null || typeof metadata !== "object" || Array.isArray(metadata)) {
1198
1269
  return metadata == null ? {} : null;
@@ -1201,6 +1272,7 @@ function metadataRecord(metadata) {
1201
1272
  }
1202
1273
  function useContainerLinks(args) {
1203
1274
  const { containerType, containerId, orgId } = args;
1275
+ const outgoing = args.direction === "outgoing";
1204
1276
  const store = useAssociationsStore();
1205
1277
  const {
1206
1278
  edges,
@@ -1211,7 +1283,7 @@ function useContainerLinks(args) {
1211
1283
  type: containerType,
1212
1284
  id: containerId
1213
1285
  });
1214
- const conversationKey = containerType === "conversation" && containerId ? `conversation:${containerId}` : null;
1286
+ const conversationKey = !outgoing && containerType === "conversation" && containerId ? `conversation:${containerId}` : null;
1215
1287
  const activeConversationKey = useRef3(conversationKey);
1216
1288
  const conversationGeneration = useRef3(0);
1217
1289
  const [conversationResult, setConversationResult] = useState(null);
@@ -1267,9 +1339,9 @@ function useContainerLinks(args) {
1267
1339
  }, [loadConversationFiles]);
1268
1340
  const conversationFiles = conversationResult?.key === conversationKey ? conversationResult.files : [];
1269
1341
  const conversationFileError = conversationResult?.key === conversationKey ? conversationResult.error : null;
1270
- const conversationFileStatus = containerType !== "conversation" ? "idle" : conversationResult?.key !== conversationKey ? "loading" : conversationResult.loading ? "loading" : conversationFileError ? "error" : "ready";
1342
+ const conversationFileStatus = outgoing || containerType !== "conversation" ? "idle" : conversationResult?.key !== conversationKey ? "loading" : conversationResult.loading ? "loading" : conversationFileError ? "error" : "ready";
1271
1343
  const incomingGeneric = edges.filter(
1272
- (edge) => edge.direction === "incoming" && !(containerType === "conversation" && edge.otherType === "file")
1344
+ (edge) => outgoing ? edge.direction === "outgoing" : edge.direction === "incoming" && !(containerType === "conversation" && edge.otherType === "file")
1273
1345
  ).map((edge) => ({
1274
1346
  edgeId: edge.id,
1275
1347
  resourceId: edge.otherId,
@@ -1278,7 +1350,29 @@ function useContainerLinks(args) {
1278
1350
  label: edge.label ?? null,
1279
1351
  metadata: edge.metadata ?? {}
1280
1352
  }));
1281
- const incoming = containerType === "conversation" ? [...incomingGeneric, ...conversationFiles] : incomingGeneric;
1353
+ const incoming = !outgoing && containerType === "conversation" ? [...incomingGeneric, ...conversationFiles] : incomingGeneric;
1354
+ const endpoints = (token, resourceId) => {
1355
+ if (!containerId) return { error: "Missing container id" };
1356
+ if (outgoing) {
1357
+ if (!isAssociationTargetType(token)) {
1358
+ return {
1359
+ error: `A ${token.replace(/_/g, " ")} cannot be filed under: it is not a container type.`
1360
+ };
1361
+ }
1362
+ return {
1363
+ sourceType: containerType,
1364
+ sourceId: containerId,
1365
+ targetType: token,
1366
+ targetId: resourceId
1367
+ };
1368
+ }
1369
+ return {
1370
+ sourceType: token,
1371
+ sourceId: resourceId,
1372
+ targetType: containerType,
1373
+ targetId: containerId
1374
+ };
1375
+ };
1282
1376
  const linksFor = (token) => incoming.filter((link) => link.token === token);
1283
1377
  const countFor = (token) => incoming.reduce((n, link) => link.token === token ? n + 1 : n, 0);
1284
1378
  const attachedIdsFor = (token) => {
@@ -1289,39 +1383,36 @@ function useContainerLinks(args) {
1289
1383
  return set;
1290
1384
  };
1291
1385
  const attach = async (token, resourceId, label, metadata, options) => {
1292
- if (!containerId) return { ok: false, error: "Missing container id" };
1386
+ const ends = endpoints(token, resourceId);
1387
+ if ("error" in ends) return { ok: false, error: ends.error };
1293
1388
  const result = await store.add({
1294
- sourceType: token,
1295
- sourceId: resourceId,
1296
- targetType: containerType,
1297
- targetId: containerId,
1389
+ ...ends,
1298
1390
  ...orgId != null ? { orgId } : {},
1299
1391
  ...label !== void 0 ? { label } : {},
1300
1392
  ...metadata !== void 0 ? { metadata } : {},
1301
1393
  ...options?.role !== void 0 ? { role: options.role } : {},
1302
1394
  ...options?.replaceMetadata !== void 0 ? { replaceMetadata: options.replaceMetadata } : {}
1303
1395
  });
1304
- if (result.ok && containerType === "conversation" && token === "file") {
1396
+ if (result.ok && !outgoing && containerType === "conversation" && token === "file") {
1305
1397
  await loadConversationFiles();
1306
1398
  }
1307
1399
  return result;
1308
1400
  };
1309
1401
  const detach = async (token, resourceId, role) => {
1310
- if (!containerId) return { ok: false, error: "Missing container id" };
1402
+ const ends = endpoints(token, resourceId);
1403
+ if ("error" in ends) return { ok: false, error: ends.error };
1311
1404
  const result = await store.remove({
1312
- sourceType: token,
1313
- sourceId: resourceId,
1314
- targetType: containerType,
1315
- targetId: containerId,
1405
+ ...ends,
1316
1406
  ...role !== void 0 ? { role } : {}
1317
1407
  });
1318
- if (result.ok && containerType === "conversation" && token === "file") {
1408
+ if (result.ok && !outgoing && containerType === "conversation" && token === "file") {
1319
1409
  await loadConversationFiles();
1320
1410
  }
1321
1411
  return result;
1322
1412
  };
1323
1413
  const setPinned = async (token, resourceId, pinned, role) => {
1324
- if (!containerId) return { ok: false, error: "Missing container id" };
1414
+ const ends = endpoints(token, resourceId);
1415
+ if ("error" in ends) return { ok: false, error: ends.error };
1325
1416
  const exactRole = role ?? null;
1326
1417
  const link = incoming.find(
1327
1418
  (candidate) => candidate.token === token && candidate.resourceId === resourceId && candidate.role === exactRole
@@ -1335,10 +1426,7 @@ function useContainerLinks(args) {
1335
1426
  };
1336
1427
  }
1337
1428
  return store.add({
1338
- sourceType: token,
1339
- sourceId: resourceId,
1340
- targetType: containerType,
1341
- targetId: containerId,
1429
+ ...ends,
1342
1430
  ...orgId != null ? { orgId } : {},
1343
1431
  ...link.label != null ? { label: link.label } : {},
1344
1432
  ...role !== void 0 ? { role } : {},
@@ -1348,16 +1436,17 @@ function useContainerLinks(args) {
1348
1436
  const reload = async () => {
1349
1437
  await Promise.all([
1350
1438
  reloadGeneric(),
1351
- containerType === "conversation" ? loadConversationFiles() : Promise.resolve()
1439
+ !outgoing && containerType === "conversation" ? loadConversationFiles() : Promise.resolve()
1352
1440
  ]);
1353
1441
  };
1354
- const status = !containerId ? genericStatus : conversationFileStatus === "error" ? "error" : containerType === "conversation" && (conversationFileStatus === "idle" || conversationFileStatus === "loading") ? "loading" : genericStatus;
1442
+ const status = !containerId ? genericStatus : conversationFileStatus === "error" ? "error" : !outgoing && containerType === "conversation" && (conversationFileStatus === "idle" || conversationFileStatus === "loading") ? "loading" : genericStatus;
1355
1443
  const error = conversationFileError ?? genericError;
1356
1444
  return {
1357
1445
  status,
1358
1446
  error,
1359
1447
  reload,
1360
1448
  totalCount: incoming.length,
1449
+ presentTokens: [...new Set(incoming.map((link) => link.token))],
1361
1450
  countFor,
1362
1451
  attachedIdsFor,
1363
1452
  linksFor,
@@ -1366,6 +1455,21 @@ function useContainerLinks(args) {
1366
1455
  setPinned
1367
1456
  };
1368
1457
  }
1458
+ function containerLinksArgs(container) {
1459
+ if (container?.direction === "outgoing") {
1460
+ return {
1461
+ containerType: container.type,
1462
+ containerId: container.id,
1463
+ orgId: container.orgId ?? null,
1464
+ direction: "outgoing"
1465
+ };
1466
+ }
1467
+ return {
1468
+ containerType: container?.type ?? "organization",
1469
+ containerId: container?.id ?? null,
1470
+ orgId: container?.orgId ?? null
1471
+ };
1472
+ }
1369
1473
 
1370
1474
  // src/react/hooks/useEntityTitles.ts
1371
1475
  import { useEffect as useEffect3, useState as useState2 } from "react";
@@ -2206,7 +2310,7 @@ function AssociationPicker(props) {
2206
2310
  const Override = pickerOverrides?.[props.token];
2207
2311
  if (Override) return /* @__PURE__ */ jsx6(Override, { ...props });
2208
2312
  const title = `Add ${info.labelPlural}`;
2209
- const subtitle = props.containerLabel ? `Attach to ${props.containerLabel}` : "Click an item to attach or detach it";
2313
+ const subtitle = props.filing ? `File ${props.containerLabel ?? "this"} under a ${info.label.toLowerCase()}` : props.containerLabel ? `Attach to ${props.containerLabel}` : "Click an item to attach or detach it";
2210
2314
  if (!props.open) return null;
2211
2315
  return /* @__PURE__ */ jsx6(
2212
2316
  AssociationWindow,
@@ -2823,8 +2927,9 @@ function AttachedItemsSheet(props) {
2823
2927
  const Icon = info.Icon ?? DefaultEntityIcon;
2824
2928
  const title = info.labelPlural;
2825
2929
  const containerName = props.container?.label ?? props.containerLabel;
2930
+ const filing = props.container?.direction === "outgoing";
2826
2931
  const subtitle = props.container ? /* @__PURE__ */ jsxs7("span", { className: "inline-flex min-w-0 max-w-full items-center gap-1", children: [
2827
- "Attached to",
2932
+ filing ? "What this is filed under:" : "Attached to",
2828
2933
  /* @__PURE__ */ jsx9(
2829
2934
  DoorRef,
2830
2935
  {
@@ -3100,11 +3205,7 @@ function AssociationCard({
3100
3205
  attach,
3101
3206
  detach,
3102
3207
  setPinned
3103
- } = useContainerLinks({
3104
- containerType: container?.type ?? "organization",
3105
- containerId: container?.id ?? null,
3106
- orgId: container?.orgId ?? null
3107
- });
3208
+ } = useContainerLinks(containerLinksArgs(container));
3108
3209
  if (!container) {
3109
3210
  store.errorSink({
3110
3211
  code: "association_card_no_container",
@@ -3115,7 +3216,8 @@ function AssociationCard({
3115
3216
  }
3116
3217
  const count = countFor(token);
3117
3218
  const loading = status === "loading" || status === "idle";
3118
- const canAttach = info.canListCandidates;
3219
+ const filing = container.direction === "outgoing";
3220
+ const canAttach = info.canListCandidates && (!filing || isAssociationTargetType(token));
3119
3221
  const canDrillIn = count > 0;
3120
3222
  return /* @__PURE__ */ jsxs8(Fragment3, { children: [
3121
3223
  /* @__PURE__ */ jsxs8(
@@ -3163,7 +3265,7 @@ function AssociationCard({
3163
3265
  /* @__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: [
3164
3266
  /* @__PURE__ */ jsx10(SpinnerIcon, { className: "h-3 w-3 animate-spin" }),
3165
3267
  "Loading\u2026"
3166
- ] }) : `${count} attached` })
3268
+ ] }) : `${count} ${container.direction === "outgoing" ? "filed under" : "attached"}` })
3167
3269
  ] })
3168
3270
  ]
3169
3271
  }
@@ -3209,6 +3311,7 @@ function AssociationCard({
3209
3311
  token,
3210
3312
  ...container.label !== void 0 ? { containerLabel: container.label } : {},
3211
3313
  orgId: container.orgId ?? null,
3314
+ ...filing ? { filing: true } : {},
3212
3315
  attachedIds: attachedIdsFor(token),
3213
3316
  onAttach: (resourceId, title) => attach(token, resourceId, title),
3214
3317
  onDetach: (resourceId) => detach(token, resourceId)
@@ -3226,31 +3329,55 @@ function AssociationCardGrid({
3226
3329
  className
3227
3330
  }) {
3228
3331
  const store = useAssociationsStore();
3332
+ const primary = usePrimaryEntity();
3229
3333
  const excluded = new Set(excludeTokens ?? []);
3230
- const list = (tokens ?? store.registry.curatedTokens()).filter(
3231
- (token) => !excluded.has(token)
3334
+ const filing = primary?.direction === "outgoing";
3335
+ const links = useContainerLinks(containerLinksArgs(primary));
3336
+ const base2 = tokens ?? store.registry.curatedTokens();
3337
+ const withPresent = filing ? [
3338
+ ...base2,
3339
+ ...links.presentTokens.filter(
3340
+ (t) => isEntityTypeToken(t) && !base2.includes(t)
3341
+ )
3342
+ ] : base2;
3343
+ const list = withPresent.filter(
3344
+ (token) => !excluded.has(token) && (!filing || isAssociationTargetType(token))
3232
3345
  );
3233
- return /* @__PURE__ */ jsx11("div", { className: cn7("space-y-5", className), children: CONTENT_ROLES.map((role) => {
3234
- const inRole = list.filter(
3235
- (token) => store.registry.getEntityInfo(token).contentRole === role.id
3236
- );
3237
- if (inRole.length === 0) return null;
3238
- return /* @__PURE__ */ jsxs9("section", { children: [
3239
- /* @__PURE__ */ jsxs9("div", { className: "mb-2.5 flex items-baseline gap-3 pl-1.5", children: [
3240
- /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2", children: [
3241
- /* @__PURE__ */ jsx11(
3242
- "span",
3243
- {
3244
- className: cn7("h-2.5 w-2.5 rounded-full", role.accentBar)
3245
- }
3246
- ),
3247
- /* @__PURE__ */ jsx11("h3", { className: "text-sm font-semibold text-foreground", children: role.title })
3346
+ const roleIds = new Set(CONTENT_ROLES.map((r) => r.id));
3347
+ const roleless = filing ? list.filter((token) => {
3348
+ const role = store.registry.getEntityInfo(token).contentRole;
3349
+ return !role || !roleIds.has(role);
3350
+ }) : [];
3351
+ return /* @__PURE__ */ jsxs9("div", { className: cn7("space-y-5", className), children: [
3352
+ CONTENT_ROLES.map((role) => {
3353
+ const inRole = list.filter(
3354
+ (token) => store.registry.getEntityInfo(token).contentRole === role.id
3355
+ );
3356
+ if (inRole.length === 0) return null;
3357
+ return /* @__PURE__ */ jsxs9("section", { children: [
3358
+ /* @__PURE__ */ jsxs9("div", { className: "mb-2.5 flex items-baseline gap-3 pl-1.5", children: [
3359
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2", children: [
3360
+ /* @__PURE__ */ jsx11(
3361
+ "span",
3362
+ {
3363
+ className: cn7("h-2.5 w-2.5 rounded-full", role.accentBar)
3364
+ }
3365
+ ),
3366
+ /* @__PURE__ */ jsx11("h3", { className: "text-sm font-semibold text-foreground", children: role.title })
3367
+ ] }),
3368
+ /* @__PURE__ */ jsx11("p", { className: "hidden text-xs text-muted-foreground sm:block", children: role.tagline })
3248
3369
  ] }),
3249
- /* @__PURE__ */ jsx11("p", { className: "hidden text-xs text-muted-foreground sm:block", children: role.tagline })
3370
+ /* @__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)) })
3371
+ ] }, role.id);
3372
+ }),
3373
+ roleless.length > 0 && /* @__PURE__ */ jsxs9("section", { children: [
3374
+ /* @__PURE__ */ jsxs9("div", { className: "mb-2.5 flex items-baseline gap-3 pl-1.5", children: [
3375
+ /* @__PURE__ */ jsx11("h3", { className: "text-sm font-semibold text-foreground", children: "Filed under" }),
3376
+ /* @__PURE__ */ jsx11("p", { className: "hidden text-xs text-muted-foreground sm:block", children: "Libraries, topics and other places this is kept." })
3250
3377
  ] }),
3251
- /* @__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)) })
3252
- ] }, role.id);
3253
- }) });
3378
+ /* @__PURE__ */ jsx11("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-3", children: roleless.map((token) => /* @__PURE__ */ jsx11(AssociationCard, { token }, token)) })
3379
+ ] })
3380
+ ] });
3254
3381
  }
3255
3382
 
3256
3383
  // src/react/components/AssociationList.tsx
@@ -3262,11 +3389,7 @@ function metadataIsPinned2(metadata) {
3262
3389
  }
3263
3390
  function useContainerLinksAdapter(container, tokens) {
3264
3391
  const store = useAssociationsStore();
3265
- const links = useContainerLinks({
3266
- containerType: container?.type ?? "organization",
3267
- containerId: container?.id ?? null,
3268
- orgId: container?.orgId ?? null
3269
- });
3392
+ const links = useContainerLinks(containerLinksArgs(container));
3270
3393
  const rows = container ? (tokens ?? store.registry.curatedTokens()).flatMap(
3271
3394
  (token) => links.linksFor(token).map((l) => ({
3272
3395
  key: l.edgeId,
@@ -5320,6 +5443,7 @@ export {
5320
5443
  UnresolvedRef,
5321
5444
  attachedKey,
5322
5445
  buildCommentTree,
5446
+ containerLinksArgs,
5323
5447
  getContentRoleMeta,
5324
5448
  lazyPickerOverride,
5325
5449
  lazyWindowShell,