@ai-matrx/associations 0.7.3 → 0.7.5
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 +22 -0
- package/dist/react/index.cjs +165 -104
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +165 -104
- package/dist/react/index.js.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog — @ai-matrx/associations
|
|
2
2
|
|
|
3
|
+
## 0.7.5 — 2026-09-02 (recents in token-specific attach pickers)
|
|
4
|
+
|
|
5
|
+
- The per-token picker opened by an association card's `+` control now shows
|
|
6
|
+
favorited and recently viewed rows under **Recent**, while retaining the
|
|
7
|
+
deduplicated general candidate list under **All** and the existing search,
|
|
8
|
+
attach/detach, and create-and-attach workflows.
|
|
9
|
+
- A failing-then-passing integration regression opens the exact
|
|
10
|
+
**Attach conversations** path from `AssociationCard` and proves a favorite
|
|
11
|
+
with no `last_viewed_at` surfaces before an ordinary conversation. Full
|
|
12
|
+
package suite: 194 tests.
|
|
13
|
+
|
|
14
|
+
## 0.7.4 — 2026-09-02 (bounded universal-picker results)
|
|
15
|
+
|
|
16
|
+
- The universal picker now keeps type filters in one horizontally scrollable
|
|
17
|
+
rail instead of wrapping the full token cloud through the results viewport.
|
|
18
|
+
- Search results retain a labelled, minimum-height vertical region inside
|
|
19
|
+
bounded desktop and mobile hosts, so a visible candidate remains reachable
|
|
20
|
+
and its attach button can enter the normal attach/detach lifecycle.
|
|
21
|
+
- A failing-then-passing constrained-layout regression searches and activates
|
|
22
|
+
`Canary Usage Confirmation` at 1440×900 and 390×844, then proves the control
|
|
23
|
+
flips from Attach to Detach. Full package suite: 193 tests.
|
|
24
|
+
|
|
3
25
|
## 0.7.3 — 2026-09-01 (favorites surface in universal recents)
|
|
4
26
|
|
|
5
27
|
- Empty-query universal pickers now include a newly favorited entity even when
|
package/dist/react/index.cjs
CHANGED
|
@@ -2183,6 +2183,15 @@ function EntityCandidateList({
|
|
|
2183
2183
|
enabled,
|
|
2184
2184
|
...search.trim() ? { search: search.trim() } : {}
|
|
2185
2185
|
});
|
|
2186
|
+
const { results: recentCandidates, loading: recentsLoading } = useUniversalEntitySearch({
|
|
2187
|
+
query: "",
|
|
2188
|
+
tokens: [token],
|
|
2189
|
+
enabled: enabled && !search.trim()
|
|
2190
|
+
});
|
|
2191
|
+
const visibleRecents = search.trim() ? [] : recentCandidates;
|
|
2192
|
+
const recentIds = new Set(visibleRecents.map((candidate) => candidate.id));
|
|
2193
|
+
const ordinaryCandidates = search.trim() ? candidates : candidates.filter((candidate) => !recentIds.has(candidate.id));
|
|
2194
|
+
const visibleCount = visibleRecents.length + ordinaryCandidates.length;
|
|
2186
2195
|
const canCreate = info.titleColumn !== null && info.listCandidates === null;
|
|
2187
2196
|
const toggle = async (id, title) => {
|
|
2188
2197
|
if (busyId) return;
|
|
@@ -2213,7 +2222,7 @@ function EntityCandidateList({
|
|
|
2213
2222
|
}
|
|
2214
2223
|
)
|
|
2215
2224
|
] }),
|
|
2216
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-1 min-h-0 overflow-y-auto -mx-1 px-1", children: loading &&
|
|
2225
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-1 min-h-0 overflow-y-auto -mx-1 px-1", children: (loading || recentsLoading) && visibleCount === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(ListMessage, { children: [
|
|
2217
2226
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SpinnerIcon, { className: "h-4 w-4 animate-spin" }),
|
|
2218
2227
|
"Loading\u2026"
|
|
2219
2228
|
] }) : error ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-[12px] text-destructive", children: [
|
|
@@ -2228,23 +2237,69 @@ function EntityCandidateList({
|
|
|
2228
2237
|
children: "Retry"
|
|
2229
2238
|
}
|
|
2230
2239
|
)
|
|
2231
|
-
] }) :
|
|
2232
|
-
|
|
2233
|
-
|
|
2240
|
+
] }) : visibleCount === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ListMessage, { children: canCreate ? `Nothing to attach yet \u2014 create a new ${info.label.toLowerCase()} below.` : "Nothing to attach." }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-2", children: [
|
|
2241
|
+
visibleRecents.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2242
|
+
CandidateSection,
|
|
2243
|
+
{
|
|
2244
|
+
label: "Recent",
|
|
2245
|
+
candidates: visibleRecents,
|
|
2246
|
+
attachedIds,
|
|
2247
|
+
busyId,
|
|
2248
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
2249
|
+
onToggle: toggle
|
|
2250
|
+
}
|
|
2251
|
+
),
|
|
2252
|
+
ordinaryCandidates.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2253
|
+
CandidateSection,
|
|
2254
|
+
{
|
|
2255
|
+
label: visibleRecents.length > 0 ? `All ${info.labelPlural}` : null,
|
|
2256
|
+
candidates: ordinaryCandidates,
|
|
2257
|
+
attachedIds,
|
|
2258
|
+
busyId,
|
|
2259
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
2260
|
+
onToggle: toggle
|
|
2261
|
+
}
|
|
2262
|
+
)
|
|
2263
|
+
] }) }),
|
|
2264
|
+
canCreate && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2265
|
+
CreateAndAttachFooter,
|
|
2266
|
+
{
|
|
2267
|
+
token,
|
|
2268
|
+
orgId,
|
|
2269
|
+
seed: search.trim(),
|
|
2270
|
+
onAttach,
|
|
2271
|
+
onCreated: reload
|
|
2272
|
+
}
|
|
2273
|
+
)
|
|
2274
|
+
] });
|
|
2275
|
+
}
|
|
2276
|
+
function CandidateSection({
|
|
2277
|
+
label,
|
|
2278
|
+
candidates,
|
|
2279
|
+
attachedIds,
|
|
2280
|
+
busyId,
|
|
2281
|
+
icon,
|
|
2282
|
+
onToggle
|
|
2283
|
+
}) {
|
|
2284
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "space-y-0.5", children: [
|
|
2285
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "px-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/60", children: label }),
|
|
2286
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "space-y-0.5", children: candidates.map((candidate) => {
|
|
2287
|
+
const attached = attachedIds.has(candidate.id);
|
|
2288
|
+
const busy = busyId === candidate.id;
|
|
2234
2289
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
2235
2290
|
"button",
|
|
2236
2291
|
{
|
|
2237
2292
|
type: "button",
|
|
2238
2293
|
disabled: busy,
|
|
2239
|
-
onClick: () =>
|
|
2294
|
+
onClick: () => void onToggle(candidate.id, candidate.title),
|
|
2240
2295
|
className: (0, import_design_system2.cn)(
|
|
2241
2296
|
"group flex min-h-11 w-full items-center gap-2 rounded-md px-2.5 py-2 text-left text-sm transition-colors md:min-h-0",
|
|
2242
2297
|
"hover:bg-accent disabled:opacity-50",
|
|
2243
2298
|
attached && "bg-accent/40"
|
|
2244
2299
|
),
|
|
2245
2300
|
children: [
|
|
2246
|
-
|
|
2247
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "flex-1 min-w-0 truncate text-foreground", children:
|
|
2301
|
+
icon,
|
|
2302
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "flex-1 min-w-0 truncate text-foreground", children: candidate.title }),
|
|
2248
2303
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2249
2304
|
"span",
|
|
2250
2305
|
{
|
|
@@ -2257,18 +2312,8 @@ function EntityCandidateList({
|
|
|
2257
2312
|
)
|
|
2258
2313
|
]
|
|
2259
2314
|
}
|
|
2260
|
-
) },
|
|
2261
|
-
}) })
|
|
2262
|
-
canCreate && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2263
|
-
CreateAndAttachFooter,
|
|
2264
|
-
{
|
|
2265
|
-
token,
|
|
2266
|
-
orgId,
|
|
2267
|
-
seed: search.trim(),
|
|
2268
|
-
onAttach,
|
|
2269
|
-
onCreated: reload
|
|
2270
|
-
}
|
|
2271
|
-
)
|
|
2315
|
+
) }, candidate.id);
|
|
2316
|
+
}) })
|
|
2272
2317
|
] });
|
|
2273
2318
|
}
|
|
2274
2319
|
function CreateAndAttachFooter({
|
|
@@ -2538,27 +2583,35 @@ function UniversalAssociationPicker(props) {
|
|
|
2538
2583
|
),
|
|
2539
2584
|
loading && browseToken === null && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SpinnerIcon, { className: "absolute right-2.5 top-1/2 h-4 w-4 -translate-y-1/2 animate-spin text-muted-foreground" })
|
|
2540
2585
|
] }),
|
|
2541
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
"
|
|
2547
|
-
{
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2586
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2587
|
+
"div",
|
|
2588
|
+
{
|
|
2589
|
+
role: "group",
|
|
2590
|
+
"aria-label": "Entity type filters",
|
|
2591
|
+
className: "flex shrink-0 flex-nowrap gap-1 overflow-x-auto pb-1",
|
|
2592
|
+
children: tokens.map((t) => {
|
|
2593
|
+
const info = store.registry.getEntityInfo(t);
|
|
2594
|
+
const ChipIcon = info.Icon ?? DefaultEntityIcon;
|
|
2595
|
+
const active = browseToken === t;
|
|
2596
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2597
|
+
"button",
|
|
2598
|
+
{
|
|
2599
|
+
type: "button",
|
|
2600
|
+
onClick: () => setBrowseToken(active ? null : t),
|
|
2601
|
+
className: (0, import_design_system4.cn)(
|
|
2602
|
+
"inline-flex shrink-0 items-center gap-1 rounded-md border px-1.5 py-0.5 text-[11px] transition-colors",
|
|
2603
|
+
active ? "border-primary/50 bg-primary/10 text-foreground" : "border-border text-muted-foreground hover:text-foreground hover:bg-accent"
|
|
2604
|
+
),
|
|
2605
|
+
children: [
|
|
2606
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ChipIcon, { className: "h-3 w-3" }),
|
|
2607
|
+
info.labelPlural
|
|
2608
|
+
]
|
|
2609
|
+
},
|
|
2610
|
+
t
|
|
2611
|
+
);
|
|
2612
|
+
})
|
|
2613
|
+
}
|
|
2614
|
+
),
|
|
2562
2615
|
browseToken && BrowseOverride ? (
|
|
2563
2616
|
// The token's canonical host picker (e.g. the stored-files browser)
|
|
2564
2617
|
// owns per-token browsing when registered — never a plain list twin.
|
|
@@ -2591,71 +2644,79 @@ function UniversalAssociationPicker(props) {
|
|
|
2591
2644
|
onAttach: (id, title) => onAttach(browseToken, id, title),
|
|
2592
2645
|
onDetach: (id) => onDetach(browseToken, id)
|
|
2593
2646
|
}
|
|
2594
|
-
) }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
{
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2647
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2648
|
+
"div",
|
|
2649
|
+
{
|
|
2650
|
+
role: "region",
|
|
2651
|
+
"aria-label": "Association search results",
|
|
2652
|
+
className: "-mx-1 min-h-24 flex-1 overflow-y-auto overscroll-contain px-1",
|
|
2653
|
+
children: results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "py-6 text-center text-[12px] text-muted-foreground", children: loading ? "Searching\u2026" : query.trim() ? "No matches \u2014 narrow with a type chip to browse." : "No recent items. Type to search, or pick a type to browse." }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-2", children: [
|
|
2654
|
+
isRecents && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "px-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/60", children: "Recent" }),
|
|
2655
|
+
[...groups.entries()].map(([token, items]) => {
|
|
2656
|
+
const info = store.registry.getEntityInfo(token);
|
|
2657
|
+
const GroupIcon = info.Icon ?? DefaultEntityIcon;
|
|
2658
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-0.5", children: [
|
|
2659
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("p", { className: "flex items-center gap-1 px-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/70", children: [
|
|
2660
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(GroupIcon, { className: "h-3 w-3" }),
|
|
2661
|
+
info.labelPlural
|
|
2662
|
+
] }),
|
|
2663
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "space-y-0.5", children: items.map((c) => {
|
|
2664
|
+
const key = attachedKey(c.token, c.id);
|
|
2665
|
+
const attached = attachedKeys.has(key);
|
|
2666
|
+
const busy = busyKey === key;
|
|
2667
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2668
|
+
"li",
|
|
2669
|
+
{
|
|
2670
|
+
className: (0, import_design_system4.cn)(
|
|
2671
|
+
"group/entity-ref group flex items-center gap-1 rounded-md pr-1.5 transition-colors",
|
|
2672
|
+
"hover:bg-accent",
|
|
2673
|
+
attached && "bg-accent/40"
|
|
2674
|
+
),
|
|
2675
|
+
children: [
|
|
2676
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2677
|
+
"button",
|
|
2678
|
+
{
|
|
2679
|
+
type: "button",
|
|
2680
|
+
disabled: busy,
|
|
2681
|
+
onClick: () => toggle(c),
|
|
2682
|
+
title: attached ? `Detach "${c.title}"` : `Attach "${c.title}"`,
|
|
2683
|
+
className: (0, import_design_system4.cn)(
|
|
2684
|
+
"flex min-w-0 flex-1 items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-sm",
|
|
2685
|
+
"disabled:opacity-50"
|
|
2686
|
+
),
|
|
2687
|
+
children: [
|
|
2688
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "min-w-0 flex-1 truncate text-foreground", children: c.title }),
|
|
2689
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2690
|
+
"span",
|
|
2691
|
+
{
|
|
2692
|
+
className: (0, import_design_system4.cn)(
|
|
2693
|
+
"flex h-5 w-5 shrink-0 items-center justify-center rounded-full",
|
|
2694
|
+
attached ? "bg-primary text-primary-foreground" : "border border-border text-muted-foreground group-hover:border-primary/60"
|
|
2695
|
+
),
|
|
2696
|
+
children: busy ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SpinnerIcon, { className: "h-3 w-3 animate-spin" }) : attached ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CheckIcon, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PlusIcon, { className: "h-3 w-3" })
|
|
2697
|
+
}
|
|
2698
|
+
)
|
|
2699
|
+
]
|
|
2700
|
+
}
|
|
2627
2701
|
),
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
token: c.token,
|
|
2647
|
-
id: c.id,
|
|
2648
|
-
name: c.title
|
|
2649
|
-
}
|
|
2650
|
-
)
|
|
2651
|
-
]
|
|
2652
|
-
},
|
|
2653
|
-
key
|
|
2654
|
-
);
|
|
2655
|
-
}) })
|
|
2656
|
-
] }, token);
|
|
2657
|
-
})
|
|
2658
|
-
] }) })
|
|
2702
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2703
|
+
DoorControlsSlot,
|
|
2704
|
+
{
|
|
2705
|
+
token: c.token,
|
|
2706
|
+
id: c.id,
|
|
2707
|
+
name: c.title
|
|
2708
|
+
}
|
|
2709
|
+
)
|
|
2710
|
+
]
|
|
2711
|
+
},
|
|
2712
|
+
key
|
|
2713
|
+
);
|
|
2714
|
+
}) })
|
|
2715
|
+
] }, token);
|
|
2716
|
+
})
|
|
2717
|
+
] })
|
|
2718
|
+
}
|
|
2719
|
+
)
|
|
2659
2720
|
] });
|
|
2660
2721
|
}
|
|
2661
2722
|
|