@ai-matrx/associations 0.11.4 → 0.12.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.
- package/CHANGELOG.md +32 -0
- package/dist/{CommentThread-DDeqHgTk.d.cts → CommentThread-C78e8433.d.cts} +6 -0
- package/dist/{CommentThread-DDeqHgTk.d.ts → CommentThread-C78e8433.d.ts} +6 -0
- package/dist/react/index.cjs +102 -31
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +13 -4
- package/dist/react/index.d.ts +13 -4
- package/dist/react/index.js +114 -43
- package/dist/react/index.js.map +1 -1
- package/dist/react/lazy/index.d.cts +1 -1
- package/dist/react/lazy/index.d.ts +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Changelog — @ai-matrx/associations
|
|
2
2
|
|
|
3
|
+
## 0.12.1
|
|
4
|
+
|
|
5
|
+
Re-cut of 0.12.0 — same package source. The 0.12.0 tag was cut from a commit whose workspace
|
|
6
|
+
lockfile did not match `shared/messaging/package.json` (ERR_PNPM_OUTDATED_LOCKFILE), so its
|
|
7
|
+
publish could never install; an immutable tag cannot move, so the bytes ship as 0.12.1.
|
|
8
|
+
No consumer action beyond 0.12.0's.
|
|
9
|
+
|
|
10
|
+
## 0.12.0
|
|
11
|
+
|
|
12
|
+
* `UniversalAssociationPicker`: an empty search box with no recents (favorites / last-viewed) now
|
|
13
|
+
**browses candidates across the offered tokens** instead of saying "No recent items" — it was a
|
|
14
|
+
screen that lied for an account with hundreds of records it had never opened. Results carry a
|
|
15
|
+
"Recent" or "Browse" label.
|
|
16
|
+
* `UniversalAssociationPicker` takes `emptyQueryMode?: "recents" | "candidates"` (default
|
|
17
|
+
`"recents"`, which falls back as above).
|
|
18
|
+
* `useUniversalEntitySearch` returns `source: "search" | "recents" | "candidates"` (new
|
|
19
|
+
`UniversalSearchSource` type); `isRecents` is unchanged in meaning.
|
|
20
|
+
* A chip whose token has a `pickerOverrides` component (Files → the host's window) no longer
|
|
21
|
+
leaves the picker body blank: it says the window is open (or closed), offers "Bring window to
|
|
22
|
+
front" / "Reopen window", and "Back to search". Closing the override window keeps the chip
|
|
23
|
+
selected instead of silently dropping back to search.
|
|
24
|
+
|
|
25
|
+
**Consumer action:** none required. Hosts whose override window renders attached state should
|
|
26
|
+
read `AssociationPickerProps.attachedIds` (it has always been passed; matrx-frontend's file
|
|
27
|
+
override did not read it until this release's adoption).
|
|
28
|
+
|
|
29
|
+
## 0.11.5
|
|
30
|
+
|
|
31
|
+
Automatic changed-only republish, cut by the release train while the 0.12.0 source below was
|
|
32
|
+
already on `main` (a checkout sweep committed it before its own version bump) — so 0.11.5 DOES
|
|
33
|
+
carry the 0.12.0 picker changes despite its auto text. Adopt 0.12.0.
|
|
34
|
+
|
|
3
35
|
## 0.11.4
|
|
4
36
|
|
|
5
37
|
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
@@ -1634,6 +1634,12 @@ interface UniversalAssociationPickerProps {
|
|
|
1634
1634
|
ownerId?: string | null;
|
|
1635
1635
|
/** Org stamped onto rows created via the per-token "+ New" footer. */
|
|
1636
1636
|
orgId?: string | null;
|
|
1637
|
+
/**
|
|
1638
|
+
* What an empty search box shows. `"recents"` (default) shows favorites and
|
|
1639
|
+
* last-viewed records and falls back to browsing candidates when there are
|
|
1640
|
+
* none; `"candidates"` always browses the available records.
|
|
1641
|
+
*/
|
|
1642
|
+
emptyQueryMode?: "recents" | "candidates";
|
|
1637
1643
|
className?: string;
|
|
1638
1644
|
}
|
|
1639
1645
|
declare function attachedKey(token: string, id: string): string;
|
|
@@ -1634,6 +1634,12 @@ interface UniversalAssociationPickerProps {
|
|
|
1634
1634
|
ownerId?: string | null;
|
|
1635
1635
|
/** Org stamped onto rows created via the per-token "+ New" footer. */
|
|
1636
1636
|
orgId?: string | null;
|
|
1637
|
+
/**
|
|
1638
|
+
* What an empty search box shows. `"recents"` (default) shows favorites and
|
|
1639
|
+
* last-viewed records and falls back to browsing candidates when there are
|
|
1640
|
+
* none; `"candidates"` always browses the available records.
|
|
1641
|
+
*/
|
|
1642
|
+
emptyQueryMode?: "recents" | "candidates";
|
|
1637
1643
|
className?: string;
|
|
1638
1644
|
}
|
|
1639
1645
|
declare function attachedKey(token: string, id: string): string;
|
package/dist/react/index.cjs
CHANGED
|
@@ -1690,7 +1690,9 @@ function useUniversalEntitySearch(args) {
|
|
|
1690
1690
|
const store = useAssociationsStore();
|
|
1691
1691
|
const [results, setResults] = (0, import_react6.useState)([]);
|
|
1692
1692
|
const [loading, setLoading] = (0, import_react6.useState)(false);
|
|
1693
|
-
const [
|
|
1693
|
+
const [source, setSource] = (0, import_react6.useState)(
|
|
1694
|
+
emptyQueryMode === "candidates" ? "candidates" : "recents"
|
|
1695
|
+
);
|
|
1694
1696
|
const runRef = (0, import_react6.useRef)(0);
|
|
1695
1697
|
const tokensKey = (tokens ?? []).join(",");
|
|
1696
1698
|
(0, import_react6.useEffect)(() => {
|
|
@@ -1701,25 +1703,36 @@ function useUniversalEntitySearch(args) {
|
|
|
1701
1703
|
if (runRef.current !== run) return;
|
|
1702
1704
|
setLoading(true);
|
|
1703
1705
|
const activeTokens = tokensKey ? tokensKey.split(",") : store.registry.listableTokens();
|
|
1706
|
+
const browse = () => store.candidates.searchCandidatesAcrossTokens({
|
|
1707
|
+
tokens: activeTokens,
|
|
1708
|
+
search: trimmed,
|
|
1709
|
+
ownerId: ownerId ?? null,
|
|
1710
|
+
perTokenLimit
|
|
1711
|
+
});
|
|
1704
1712
|
let next = [];
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1713
|
+
let nextSource;
|
|
1714
|
+
if (trimmed) {
|
|
1715
|
+
next = await browse();
|
|
1716
|
+
nextSource = "search";
|
|
1717
|
+
} else if (emptyQueryMode === "candidates") {
|
|
1718
|
+
next = await browse();
|
|
1719
|
+
nextSource = "candidates";
|
|
1712
1720
|
} else {
|
|
1713
1721
|
next = await loadRecents(store, activeTokens);
|
|
1722
|
+
nextSource = "recents";
|
|
1723
|
+
if (next.length === 0 && runRef.current === run) {
|
|
1724
|
+
next = await browse();
|
|
1725
|
+
nextSource = "candidates";
|
|
1726
|
+
}
|
|
1714
1727
|
}
|
|
1715
1728
|
if (runRef.current !== run) return;
|
|
1716
1729
|
setResults(next);
|
|
1717
|
-
|
|
1730
|
+
setSource(nextSource);
|
|
1718
1731
|
setLoading(false);
|
|
1719
1732
|
}, DEBOUNCE_MS);
|
|
1720
1733
|
return () => clearTimeout(timer);
|
|
1721
1734
|
}, [query, tokensKey, ownerId, enabled, perTokenLimit, emptyQueryMode, store]);
|
|
1722
|
-
return { results, loading, isRecents };
|
|
1735
|
+
return { results, loading, isRecents: source === "recents", source };
|
|
1723
1736
|
}
|
|
1724
1737
|
async function loadRecents(store, tokens) {
|
|
1725
1738
|
const res = await store.favorites.list();
|
|
@@ -2782,7 +2795,15 @@ function attachedKey(token, id) {
|
|
|
2782
2795
|
return `${token}:${id}`;
|
|
2783
2796
|
}
|
|
2784
2797
|
function UniversalAssociationPicker(props) {
|
|
2785
|
-
const {
|
|
2798
|
+
const {
|
|
2799
|
+
attachedKeys,
|
|
2800
|
+
onAttach,
|
|
2801
|
+
onDetach,
|
|
2802
|
+
ownerId,
|
|
2803
|
+
orgId,
|
|
2804
|
+
emptyQueryMode = "recents",
|
|
2805
|
+
className
|
|
2806
|
+
} = props;
|
|
2786
2807
|
const store = useAssociationsStore();
|
|
2787
2808
|
const { pickerOverrides } = useAssociationsUiPorts();
|
|
2788
2809
|
const notifier = useNotifier();
|
|
@@ -2790,12 +2811,19 @@ function UniversalAssociationPicker(props) {
|
|
|
2790
2811
|
const [query, setQuery] = (0, import_react14.useState)("");
|
|
2791
2812
|
const [browseToken, setBrowseToken] = (0, import_react14.useState)(null);
|
|
2792
2813
|
const [busyKey, setBusyKey] = (0, import_react14.useState)(null);
|
|
2793
|
-
const
|
|
2814
|
+
const [overrideOpen, setOverrideOpen] = (0, import_react14.useState)(false);
|
|
2815
|
+
const [overrideMount, setOverrideMount] = (0, import_react14.useState)(0);
|
|
2816
|
+
const { results, loading, source } = useUniversalEntitySearch({
|
|
2794
2817
|
query,
|
|
2795
2818
|
tokens,
|
|
2796
2819
|
ownerId: ownerId ?? null,
|
|
2797
|
-
enabled: browseToken === null
|
|
2820
|
+
enabled: browseToken === null,
|
|
2821
|
+
emptyQueryMode
|
|
2798
2822
|
});
|
|
2823
|
+
const selectToken = (next) => {
|
|
2824
|
+
setBrowseToken(next);
|
|
2825
|
+
setOverrideOpen(next !== null && Boolean(pickerOverrides?.[next]));
|
|
2826
|
+
};
|
|
2799
2827
|
const toggle = async (c) => {
|
|
2800
2828
|
const key = attachedKey(c.token, c.id);
|
|
2801
2829
|
if (busyKey) return;
|
|
@@ -2828,7 +2856,7 @@ function UniversalAssociationPicker(props) {
|
|
|
2828
2856
|
value: query,
|
|
2829
2857
|
onChange: (e) => {
|
|
2830
2858
|
setQuery(e.target.value);
|
|
2831
|
-
if (browseToken)
|
|
2859
|
+
if (browseToken) selectToken(null);
|
|
2832
2860
|
},
|
|
2833
2861
|
placeholder: "Search everything\u2026",
|
|
2834
2862
|
className: "pl-8 text-base",
|
|
@@ -2851,7 +2879,8 @@ function UniversalAssociationPicker(props) {
|
|
|
2851
2879
|
"button",
|
|
2852
2880
|
{
|
|
2853
2881
|
type: "button",
|
|
2854
|
-
|
|
2882
|
+
"aria-pressed": active,
|
|
2883
|
+
onClick: () => selectToken(active ? null : t),
|
|
2855
2884
|
className: (0, import_design_system4.cn)(
|
|
2856
2885
|
"inline-flex shrink-0 items-center gap-1 rounded-md border px-1.5 py-0.5 text-[11px] transition-colors",
|
|
2857
2886
|
active ? "border-primary/50 bg-primary/10 text-foreground" : "border-border text-muted-foreground hover:text-foreground hover:bg-accent"
|
|
@@ -2869,20 +2898,62 @@ function UniversalAssociationPicker(props) {
|
|
|
2869
2898
|
browseToken && BrowseOverride ? (
|
|
2870
2899
|
// The token's canonical host picker (e.g. the stored-files browser)
|
|
2871
2900
|
// owns per-token browsing when registered — never a plain list twin.
|
|
2872
|
-
|
|
2873
|
-
|
|
2901
|
+
// It opens in its own window; this body says where it went.
|
|
2902
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2903
|
+
"div",
|
|
2874
2904
|
{
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2905
|
+
role: "region",
|
|
2906
|
+
"aria-label": `${store.registry.getEntityInfo(browseToken).labelPlural} window`,
|
|
2907
|
+
className: "flex flex-1 flex-col items-center justify-center gap-3 px-4 py-8 text-center",
|
|
2908
|
+
children: [
|
|
2909
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-sm text-foreground", children: overrideOpen ? `The ${store.registry.getEntityInfo(browseToken).labelPlural.toLowerCase()} window is open.` : `The ${store.registry.getEntityInfo(browseToken).labelPlural.toLowerCase()} window is closed.` }),
|
|
2910
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "max-w-xs text-[12px] text-muted-foreground", children: overrideOpen ? "Check items there to attach them; uncheck to detach. Changes save as you go." : "Reopen it to pick more, or go back to search everything." }),
|
|
2911
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [
|
|
2912
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2913
|
+
import_design_system4.Button,
|
|
2914
|
+
{
|
|
2915
|
+
type: "button",
|
|
2916
|
+
size: "sm",
|
|
2917
|
+
variant: overrideOpen ? "outline" : "default",
|
|
2918
|
+
onClick: () => {
|
|
2919
|
+
setOverrideOpen(true);
|
|
2920
|
+
setOverrideMount((n) => n + 1);
|
|
2921
|
+
},
|
|
2922
|
+
children: [
|
|
2923
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MaximizeIcon, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
2924
|
+
overrideOpen ? "Bring window to front" : "Reopen window"
|
|
2925
|
+
]
|
|
2926
|
+
}
|
|
2927
|
+
),
|
|
2928
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2929
|
+
import_design_system4.Button,
|
|
2930
|
+
{
|
|
2931
|
+
type: "button",
|
|
2932
|
+
size: "sm",
|
|
2933
|
+
variant: "ghost",
|
|
2934
|
+
onClick: () => selectToken(null),
|
|
2935
|
+
children: "Back to search"
|
|
2936
|
+
}
|
|
2937
|
+
)
|
|
2938
|
+
] }),
|
|
2939
|
+
overrideOpen && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2940
|
+
BrowseOverride,
|
|
2941
|
+
{
|
|
2942
|
+
open: true,
|
|
2943
|
+
onOpenChange: (open) => {
|
|
2944
|
+
if (!open) setOverrideOpen(false);
|
|
2945
|
+
},
|
|
2946
|
+
token: browseToken,
|
|
2947
|
+
orgId: orgId ?? null,
|
|
2948
|
+
attachedIds: new Set(
|
|
2949
|
+
[...attachedKeys].filter((k) => k.startsWith(`${browseToken}:`)).map((k) => k.slice(browseToken.length + 1))
|
|
2950
|
+
),
|
|
2951
|
+
onAttach: (id, title) => onAttach(browseToken, id, title),
|
|
2952
|
+
onDetach: (id) => onDetach(browseToken, id)
|
|
2953
|
+
},
|
|
2954
|
+
overrideMount
|
|
2955
|
+
)
|
|
2956
|
+
]
|
|
2886
2957
|
}
|
|
2887
2958
|
)
|
|
2888
2959
|
) : browseToken ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex min-h-0 flex-1 flex-col", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
@@ -2891,7 +2962,7 @@ function UniversalAssociationPicker(props) {
|
|
|
2891
2962
|
token: browseToken,
|
|
2892
2963
|
enabled: true,
|
|
2893
2964
|
orgId,
|
|
2894
|
-
onClose: () =>
|
|
2965
|
+
onClose: () => selectToken(null),
|
|
2895
2966
|
attachedIds: new Set(
|
|
2896
2967
|
[...attachedKeys].filter((k) => k.startsWith(`${browseToken}:`)).map((k) => k.slice(browseToken.length + 1))
|
|
2897
2968
|
),
|
|
@@ -2904,8 +2975,8 @@ function UniversalAssociationPicker(props) {
|
|
|
2904
2975
|
role: "region",
|
|
2905
2976
|
"aria-label": "Association search results",
|
|
2906
2977
|
className: "-mx-1 min-h-24 flex-1 overflow-y-auto overscroll-contain px-1",
|
|
2907
|
-
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." : "
|
|
2908
|
-
|
|
2978
|
+
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." : "Nothing to attach yet. Type to search, or pick a type to browse." }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-2", children: [
|
|
2979
|
+
source !== "search" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "px-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/60", children: source === "recents" ? "Recent" : "Browse" }),
|
|
2909
2980
|
[...groups.entries()].map(([token, items]) => {
|
|
2910
2981
|
const info = store.registry.getEntityInfo(token);
|
|
2911
2982
|
const GroupIcon = info.Icon ?? DefaultEntityIcon;
|