@ai-matrx/associations 0.7.3 → 0.7.4

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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog — @ai-matrx/associations
2
2
 
3
+ ## 0.7.4 — 2026-09-02 (bounded universal-picker results)
4
+
5
+ - The universal picker now keeps type filters in one horizontally scrollable
6
+ rail instead of wrapping the full token cloud through the results viewport.
7
+ - Search results retain a labelled, minimum-height vertical region inside
8
+ bounded desktop and mobile hosts, so a visible candidate remains reachable
9
+ and its attach button can enter the normal attach/detach lifecycle.
10
+ - A failing-then-passing constrained-layout regression searches and activates
11
+ `Canary Usage Confirmation` at 1440×900 and 390×844, then proves the control
12
+ flips from Attach to Detach. Full package suite: 193 tests.
13
+
3
14
  ## 0.7.3 — 2026-09-01 (favorites surface in universal recents)
4
15
 
5
16
  - Empty-query universal pickers now include a newly favorited entity even when
@@ -2538,27 +2538,35 @@ function UniversalAssociationPicker(props) {
2538
2538
  ),
2539
2539
  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
2540
  ] }),
2541
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex flex-wrap gap-1", children: tokens.map((t) => {
2542
- const info = store.registry.getEntityInfo(t);
2543
- const ChipIcon = info.Icon ?? DefaultEntityIcon;
2544
- const active = browseToken === t;
2545
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2546
- "button",
2547
- {
2548
- type: "button",
2549
- onClick: () => setBrowseToken(active ? null : t),
2550
- className: (0, import_design_system4.cn)(
2551
- "inline-flex items-center gap-1 rounded-md border px-1.5 py-0.5 text-[11px] transition-colors",
2552
- active ? "border-primary/50 bg-primary/10 text-foreground" : "border-border text-muted-foreground hover:text-foreground hover:bg-accent"
2553
- ),
2554
- children: [
2555
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ChipIcon, { className: "h-3 w-3" }),
2556
- info.labelPlural
2557
- ]
2558
- },
2559
- t
2560
- );
2561
- }) }),
2541
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2542
+ "div",
2543
+ {
2544
+ role: "group",
2545
+ "aria-label": "Entity type filters",
2546
+ className: "flex shrink-0 flex-nowrap gap-1 overflow-x-auto pb-1",
2547
+ children: tokens.map((t) => {
2548
+ const info = store.registry.getEntityInfo(t);
2549
+ const ChipIcon = info.Icon ?? DefaultEntityIcon;
2550
+ const active = browseToken === t;
2551
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2552
+ "button",
2553
+ {
2554
+ type: "button",
2555
+ onClick: () => setBrowseToken(active ? null : t),
2556
+ className: (0, import_design_system4.cn)(
2557
+ "inline-flex shrink-0 items-center gap-1 rounded-md border px-1.5 py-0.5 text-[11px] transition-colors",
2558
+ active ? "border-primary/50 bg-primary/10 text-foreground" : "border-border text-muted-foreground hover:text-foreground hover:bg-accent"
2559
+ ),
2560
+ children: [
2561
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ChipIcon, { className: "h-3 w-3" }),
2562
+ info.labelPlural
2563
+ ]
2564
+ },
2565
+ t
2566
+ );
2567
+ })
2568
+ }
2569
+ ),
2562
2570
  browseToken && BrowseOverride ? (
2563
2571
  // The token's canonical host picker (e.g. the stored-files browser)
2564
2572
  // owns per-token browsing when registered — never a plain list twin.
@@ -2591,71 +2599,79 @@ function UniversalAssociationPicker(props) {
2591
2599
  onAttach: (id, title) => onAttach(browseToken, id, title),
2592
2600
  onDetach: (id) => onDetach(browseToken, id)
2593
2601
  }
2594
- ) }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "-mx-1 min-h-0 flex-1 overflow-y-auto px-1", children: results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "py-6 text-center text-[12px] text-muted-foreground", children: loading ? "Searching\u2026" : query.trim() ? "No matches \u2014 narrow with a type chip to browse." : "No recent items. Type to search, or pick a type to browse." }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-2", children: [
2595
- isRecents && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "px-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/60", children: "Recent" }),
2596
- [...groups.entries()].map(([token, items]) => {
2597
- const info = store.registry.getEntityInfo(token);
2598
- const GroupIcon = info.Icon ?? DefaultEntityIcon;
2599
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-0.5", children: [
2600
- /* @__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: [
2601
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(GroupIcon, { className: "h-3 w-3" }),
2602
- info.labelPlural
2603
- ] }),
2604
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "space-y-0.5", children: items.map((c) => {
2605
- const key = attachedKey(c.token, c.id);
2606
- const attached = attachedKeys.has(key);
2607
- const busy = busyKey === key;
2608
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2609
- "li",
2610
- {
2611
- className: (0, import_design_system4.cn)(
2612
- "group/entity-ref group flex items-center gap-1 rounded-md pr-1.5 transition-colors",
2613
- "hover:bg-accent",
2614
- attached && "bg-accent/40"
2615
- ),
2616
- children: [
2617
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2618
- "button",
2619
- {
2620
- type: "button",
2621
- disabled: busy,
2622
- onClick: () => toggle(c),
2623
- title: attached ? `Detach "${c.title}"` : `Attach "${c.title}"`,
2624
- className: (0, import_design_system4.cn)(
2625
- "flex min-w-0 flex-1 items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-sm",
2626
- "disabled:opacity-50"
2602
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2603
+ "div",
2604
+ {
2605
+ role: "region",
2606
+ "aria-label": "Association search results",
2607
+ className: "-mx-1 min-h-24 flex-1 overflow-y-auto overscroll-contain px-1",
2608
+ 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: [
2609
+ isRecents && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "px-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/60", children: "Recent" }),
2610
+ [...groups.entries()].map(([token, items]) => {
2611
+ const info = store.registry.getEntityInfo(token);
2612
+ const GroupIcon = info.Icon ?? DefaultEntityIcon;
2613
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-0.5", children: [
2614
+ /* @__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: [
2615
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(GroupIcon, { className: "h-3 w-3" }),
2616
+ info.labelPlural
2617
+ ] }),
2618
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "space-y-0.5", children: items.map((c) => {
2619
+ const key = attachedKey(c.token, c.id);
2620
+ const attached = attachedKeys.has(key);
2621
+ const busy = busyKey === key;
2622
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2623
+ "li",
2624
+ {
2625
+ className: (0, import_design_system4.cn)(
2626
+ "group/entity-ref group flex items-center gap-1 rounded-md pr-1.5 transition-colors",
2627
+ "hover:bg-accent",
2628
+ attached && "bg-accent/40"
2629
+ ),
2630
+ children: [
2631
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2632
+ "button",
2633
+ {
2634
+ type: "button",
2635
+ disabled: busy,
2636
+ onClick: () => toggle(c),
2637
+ title: attached ? `Detach "${c.title}"` : `Attach "${c.title}"`,
2638
+ className: (0, import_design_system4.cn)(
2639
+ "flex min-w-0 flex-1 items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-sm",
2640
+ "disabled:opacity-50"
2641
+ ),
2642
+ children: [
2643
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "min-w-0 flex-1 truncate text-foreground", children: c.title }),
2644
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2645
+ "span",
2646
+ {
2647
+ className: (0, import_design_system4.cn)(
2648
+ "flex h-5 w-5 shrink-0 items-center justify-center rounded-full",
2649
+ attached ? "bg-primary text-primary-foreground" : "border border-border text-muted-foreground group-hover:border-primary/60"
2650
+ ),
2651
+ 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" })
2652
+ }
2653
+ )
2654
+ ]
2655
+ }
2627
2656
  ),
2628
- children: [
2629
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "min-w-0 flex-1 truncate text-foreground", children: c.title }),
2630
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2631
- "span",
2632
- {
2633
- className: (0, import_design_system4.cn)(
2634
- "flex h-5 w-5 shrink-0 items-center justify-center rounded-full",
2635
- attached ? "bg-primary text-primary-foreground" : "border border-border text-muted-foreground group-hover:border-primary/60"
2636
- ),
2637
- 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" })
2638
- }
2639
- )
2640
- ]
2641
- }
2642
- ),
2643
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2644
- DoorControlsSlot,
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
- ] }) })
2657
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2658
+ DoorControlsSlot,
2659
+ {
2660
+ token: c.token,
2661
+ id: c.id,
2662
+ name: c.title
2663
+ }
2664
+ )
2665
+ ]
2666
+ },
2667
+ key
2668
+ );
2669
+ }) })
2670
+ ] }, token);
2671
+ })
2672
+ ] })
2673
+ }
2674
+ )
2659
2675
  ] });
2660
2676
  }
2661
2677