@ai-matrx/design-system 0.86.368 → 0.86.369
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 +12 -2
- package/dist/accents.cjs +2 -2
- package/dist/accents.cjs.map +1 -1
- package/dist/accents.js +2 -2
- package/dist/accents.js.map +1 -1
- package/dist/carousel.js +5 -5
- package/dist/{chunk-SOCDBDFS.js → chunk-2HB65PD6.js} +2 -2
- package/dist/{chunk-KOUHF4JC.js → chunk-345ESUIP.js} +4 -4
- package/dist/{chunk-LS3C7IUV.js → chunk-ASDPMZNP.js} +2 -2
- package/dist/{chunk-WLKHQGX5.js → chunk-CFFRMRWJ.js} +2 -2
- package/dist/{chunk-TBWKBTPM.js → chunk-F7IUBABK.js} +3 -3
- package/dist/{chunk-3JEAXZKX.js → chunk-FLYZSVTR.js} +2 -2
- package/dist/{chunk-UAIBCPMB.js → chunk-RLTTLDWU.js} +4 -4
- package/dist/{chunk-VG765KBC.js → chunk-U3SUPYNT.js} +2 -2
- package/dist/{chunk-QEQDJBNK.js → chunk-WSEZDHLA.js} +2 -2
- package/dist/{chunk-3YTF4MH2.js → chunk-YA3DNAAC.js} +2 -2
- package/dist/clipboard-fallback-dialog.js +2 -2
- package/dist/controls.css +7 -0
- package/dist/controls.js +5 -5
- package/dist/data-table/data-row-inspector.js +5 -5
- package/dist/data-table/data-row-window.js +6 -6
- package/dist/data-table/index.js +8 -8
- package/dist/data-table/pagination.js +3 -3
- package/dist/data-table/uuid-cell.js +3 -3
- package/dist/desktop.js +4 -4
- package/dist/index.cjs +20 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.js +27 -19
- package/dist/index.js.map +1 -1
- package/dist/item.js +3 -3
- package/dist/lite-table.js +2 -2
- package/dist/record-list.js +3 -3
- package/dist/settings.js +3 -3
- package/dist/swipe-deck-tree.cjs +238 -0
- package/dist/swipe-deck-tree.cjs.map +1 -0
- package/dist/swipe-deck-tree.d.cts +202 -0
- package/dist/swipe-deck-tree.d.ts +202 -0
- package/dist/swipe-deck-tree.js +208 -0
- package/dist/swipe-deck-tree.js.map +1 -0
- package/dist/swipe-deck.cjs.map +1 -1
- package/dist/swipe-deck.d.cts +5 -0
- package/dist/swipe-deck.d.ts +5 -0
- package/dist/swipe-deck.js.map +1 -1
- package/dist/text-input-dialog.js +1 -1
- package/dist/tokens.css +23 -23
- package/dist/web-theme.css +20 -20
- package/dist/writing-field.js +3 -3
- package/package.json +12 -2
- /package/dist/{chunk-SOCDBDFS.js.map → chunk-2HB65PD6.js.map} +0 -0
- /package/dist/{chunk-KOUHF4JC.js.map → chunk-345ESUIP.js.map} +0 -0
- /package/dist/{chunk-LS3C7IUV.js.map → chunk-ASDPMZNP.js.map} +0 -0
- /package/dist/{chunk-WLKHQGX5.js.map → chunk-CFFRMRWJ.js.map} +0 -0
- /package/dist/{chunk-TBWKBTPM.js.map → chunk-F7IUBABK.js.map} +0 -0
- /package/dist/{chunk-3JEAXZKX.js.map → chunk-FLYZSVTR.js.map} +0 -0
- /package/dist/{chunk-UAIBCPMB.js.map → chunk-RLTTLDWU.js.map} +0 -0
- /package/dist/{chunk-VG765KBC.js.map → chunk-U3SUPYNT.js.map} +0 -0
- /package/dist/{chunk-QEQDJBNK.js.map → chunk-WSEZDHLA.js.map} +0 -0
- /package/dist/{chunk-3YTF4MH2.js.map → chunk-YA3DNAAC.js.map} +0 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* THE SWIPE DECK MODEL — everything the deck decides, with no DOM and no React.
|
|
5
|
+
*
|
|
6
|
+
* A deck is a list of items a person answers by swiping one of four ways. The deck never stores
|
|
7
|
+
* anything itself: every change is a list of {@link DeckOp}s applied to a {@link DeckState}, and
|
|
8
|
+
* every change carries its exact inverse, so undo is unlimited and covers every kind of change
|
|
9
|
+
* (answers, picks, part edits, categories). A bound deck sends the same ops to its connection.
|
|
10
|
+
*
|
|
11
|
+
* WHERE THE PERSON IS IS DERIVED, never stored: the current step is the first one not yet done.
|
|
12
|
+
* So a deck open on two devices stays in step by sharing state alone, and a reload resumes where
|
|
13
|
+
* the person left off.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
type DeckDirection = "right" | "up" | "left" | "down";
|
|
17
|
+
/** Semantic color of an answer; each names a design-system token pair (`--success` / `--success-foreground`…). */
|
|
18
|
+
type DeckTone = "success" | "info" | "destructive" | "warning" | "primary" | "neutral";
|
|
19
|
+
interface DeckAnswer {
|
|
20
|
+
/** Stable id: what results, callbacks and connections carry. */
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly label: string;
|
|
23
|
+
/**
|
|
24
|
+
* Offered only through the card's own controls (`renderCardActions` / `renderCardBody` call
|
|
25
|
+
* `answer(id)`), never drawn as a button: an answer that needs a choice first ("Related ▸ Partner").
|
|
26
|
+
*/
|
|
27
|
+
readonly hidden?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* "Skip for now": in a bound deck the connection moves the card to the back of the line and keeps no
|
|
30
|
+
* answer (it comes back once the rest are answered); undo brings it back to the front. In a deck with no
|
|
31
|
+
* connection it is kept like any other answer.
|
|
32
|
+
*/
|
|
33
|
+
readonly requeue?: boolean;
|
|
34
|
+
readonly tone: DeckTone;
|
|
35
|
+
/** Higher is better. Decides which answer wins when categories disagree (default by direction). */
|
|
36
|
+
readonly rank?: number;
|
|
37
|
+
/** Replaces the arrow on the edge button and the full-screen wash. */
|
|
38
|
+
readonly icon?: ReactNode;
|
|
39
|
+
/** One key that gives this answer ("Y", "3"), beside the arrows. Case does not matter. */
|
|
40
|
+
readonly shortcut?: string;
|
|
41
|
+
}
|
|
42
|
+
/** The answer on each edge. A missing direction is disabled (two, three or four ways). */
|
|
43
|
+
type DeckKey = Partial<Record<DeckDirection, DeckAnswer>>;
|
|
44
|
+
/** An override: an answer replaces that edge, `null` turns the edge off. */
|
|
45
|
+
type DeckKeyOverride = Partial<Record<DeckDirection, DeckAnswer | null>>;
|
|
46
|
+
interface DeckField {
|
|
47
|
+
readonly label: string;
|
|
48
|
+
readonly value: string;
|
|
49
|
+
}
|
|
50
|
+
/** A part as an item first offers it: by phrase (first match) or by token span. */
|
|
51
|
+
interface DeckPartSeed {
|
|
52
|
+
readonly phrase?: string;
|
|
53
|
+
readonly start?: number;
|
|
54
|
+
readonly end?: number;
|
|
55
|
+
readonly categories?: readonly string[];
|
|
56
|
+
/** The answer this part leans to before anyone answers it. */
|
|
57
|
+
readonly suggested?: string | null;
|
|
58
|
+
readonly answer?: string | null;
|
|
59
|
+
}
|
|
60
|
+
/** A picture on the card: an image by address, or anything the host draws (an avatar with its own fallback). */
|
|
61
|
+
type DeckMedia = {
|
|
62
|
+
readonly src: string;
|
|
63
|
+
readonly alt?: string;
|
|
64
|
+
readonly shape?: "circle" | "square" | "wide";
|
|
65
|
+
} | {
|
|
66
|
+
readonly node: ReactNode;
|
|
67
|
+
readonly shape?: "circle" | "square" | "wide";
|
|
68
|
+
};
|
|
69
|
+
/** One member of a group card ("these four logos"): each can be switched off before the card is answered. */
|
|
70
|
+
interface DeckMember {
|
|
71
|
+
readonly id: string;
|
|
72
|
+
readonly label: string;
|
|
73
|
+
readonly media?: DeckMedia;
|
|
74
|
+
/** Starts switched off. */
|
|
75
|
+
readonly off?: boolean;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* When a card is asked: about one earlier card's answer, or a combination. A leaf names the card (`item`)
|
|
79
|
+
* and, optionally, the answers that count (`is`), the ones that must not (`isNot`), or whether it was
|
|
80
|
+
* answered at all (`answered`, default true). A card only ever depends on cards BEFORE it (no loops);
|
|
81
|
+
* a card that is not asked has no answer as far as later cards are concerned.
|
|
82
|
+
*/
|
|
83
|
+
type DeckCondition = {
|
|
84
|
+
readonly item: string;
|
|
85
|
+
readonly is?: string | readonly string[];
|
|
86
|
+
readonly isNot?: string | readonly string[];
|
|
87
|
+
readonly answered?: boolean;
|
|
88
|
+
} | {
|
|
89
|
+
readonly all: readonly DeckCondition[];
|
|
90
|
+
} | {
|
|
91
|
+
readonly any: readonly DeckCondition[];
|
|
92
|
+
} | {
|
|
93
|
+
readonly not: DeckCondition;
|
|
94
|
+
};
|
|
95
|
+
interface DeckItem {
|
|
96
|
+
readonly id: string;
|
|
97
|
+
/** The main text: fitted to the largest size the screen allows. */
|
|
98
|
+
readonly text: string;
|
|
99
|
+
/** A small label above the text (what kind of item this is). */
|
|
100
|
+
readonly kicker?: string;
|
|
101
|
+
/** A small fact tag beside the kicker. */
|
|
102
|
+
readonly tag?: string;
|
|
103
|
+
/** The suggested answer id: its button is filled and pulses, and the card leans toward it. */
|
|
104
|
+
readonly suggested?: string | null;
|
|
105
|
+
/**
|
|
106
|
+
* The suggestion in finer words than its answer's label, shown as the Suggestion (the card still leans to
|
|
107
|
+
* `suggested`): an answer that merges two ("Sustain or Accept") can show the precise one ("Accept").
|
|
108
|
+
*/
|
|
109
|
+
readonly suggestedLabel?: string;
|
|
110
|
+
/** Shown under the text with the "Reason" label. */
|
|
111
|
+
readonly reason?: string;
|
|
112
|
+
/** More label/value lines under the text. */
|
|
113
|
+
readonly fields?: readonly DeckField[];
|
|
114
|
+
/** Draw the text in quotation marks. */
|
|
115
|
+
readonly quoted?: boolean;
|
|
116
|
+
readonly parts?: readonly DeckPartSeed[];
|
|
117
|
+
/** This item's own key: replaces or turns off edges of the deck's key. */
|
|
118
|
+
readonly answers?: DeckKeyOverride;
|
|
119
|
+
/** Pick-one group: when every item of a pick-one group is answered, the person picks one of its Yes items. */
|
|
120
|
+
readonly group?: string;
|
|
121
|
+
/** The suggested pick of its group. */
|
|
122
|
+
readonly pickSuggested?: boolean;
|
|
123
|
+
/**
|
|
124
|
+
* The right answer(s), by answer id, for a deck that is graded (a quiz). Never shown while she answers;
|
|
125
|
+
* the result says which were right and the end shows the score. Absent: the item is not graded.
|
|
126
|
+
*/
|
|
127
|
+
readonly correct?: string | readonly string[];
|
|
128
|
+
/** Answers off the edges, as a row of buttons on the card; replaces the deck's own `more` for this item. */
|
|
129
|
+
readonly more?: readonly DeckAnswer[];
|
|
130
|
+
/** A picture above the text. */
|
|
131
|
+
readonly media?: DeckMedia;
|
|
132
|
+
/** A group card: its members, each switched on or off; the off ones ride with the answer as `detail.off`. */
|
|
133
|
+
readonly members?: readonly DeckMember[];
|
|
134
|
+
/**
|
|
135
|
+
* Asked only when this holds (see {@link DeckCondition}). Not asked: skipped, and left out of progress,
|
|
136
|
+
* counts, the score and the results; an answer it already had is KEPT but does not apply, and comes
|
|
137
|
+
* back if the card is asked again.
|
|
138
|
+
*/
|
|
139
|
+
readonly showIf?: DeckCondition;
|
|
140
|
+
/**
|
|
141
|
+
* The card this one narrows (a tree: "Too broad" on a parent asks for its children). The deck itself only
|
|
142
|
+
* carries it; the optional `@ai-matrx/design-system/swipe-deck-tree` entry grows, orders and draws the tree.
|
|
143
|
+
*/
|
|
144
|
+
readonly parent?: string;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
declare const TREE_JUST_RIGHT = "just_right";
|
|
148
|
+
declare const TREE_TOO_BROAD = "too_broad";
|
|
149
|
+
declare const TREE_TOO_NARROW = "too_narrow";
|
|
150
|
+
declare const TREE_NOT_USEFUL = "not_useful";
|
|
151
|
+
/** The four answers, on the edges a person expects: right keeps it, up opens it, down folds it, left drops it. */
|
|
152
|
+
declare const TREE_KEY: DeckKey;
|
|
153
|
+
/** The four answers as a list (a table's answer column, a legend). */
|
|
154
|
+
declare const TREE_ANSWERS: readonly DeckAnswer[];
|
|
155
|
+
/** The ancestors of a card, root first (a missing or looping parent ends the walk). */
|
|
156
|
+
declare function treePath(items: readonly DeckItem[], id: string): DeckItem[];
|
|
157
|
+
/** The breadcrumb a narrower card carries as its kicker: "Marketing › Medical marketing". */
|
|
158
|
+
declare function treeBreadcrumb(items: readonly DeckItem[], id: string): string;
|
|
159
|
+
/** The cards directly under one card (null: the roots). */
|
|
160
|
+
declare function treeChildren(items: readonly DeckItem[], id: string | null): DeckItem[];
|
|
161
|
+
/** What the host answers. Each call may take seconds (an agent); the deck keeps going meanwhile. */
|
|
162
|
+
interface DeckTreeConnection {
|
|
163
|
+
/** Narrower cards under `item` (`path` is its ancestors, root first). Their `parent` is set to `item.id`. */
|
|
164
|
+
split(item: DeckItem, path: readonly DeckItem[]): Promise<readonly DeckItem[]>;
|
|
165
|
+
/** One card that combines `items` (all under `parent`); they move under it. Omit to never combine. */
|
|
166
|
+
combine?(items: readonly DeckItem[], parent: DeckItem | null): Promise<DeckItem | null>;
|
|
167
|
+
}
|
|
168
|
+
interface DeckTreeOptions {
|
|
169
|
+
/** How many "Too narrow" cards under one parent ask for a combine. Default 2. */
|
|
170
|
+
readonly combineAt?: number;
|
|
171
|
+
/** Prefix each grown card's kicker with its breadcrumb. Default true. */
|
|
172
|
+
readonly breadcrumbs?: boolean;
|
|
173
|
+
}
|
|
174
|
+
interface DeckTree {
|
|
175
|
+
/** The deck to draw: the seed with every grown card in place (each after the card that was on screen). */
|
|
176
|
+
readonly items: DeckItem[];
|
|
177
|
+
/** Cards whose split or combine is running (for a "finding narrower ones" mark). */
|
|
178
|
+
readonly working: readonly string[];
|
|
179
|
+
/** One sentence per split or combine that failed. */
|
|
180
|
+
readonly problems: readonly string[];
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* The tree, grown from the person's answers. Pass the deck's own answers (a bound deck's `binding.state.answers`,
|
|
184
|
+
* or the last `onChange` result's) and draw `items`.
|
|
185
|
+
*/
|
|
186
|
+
declare function useDeckTree(seed: readonly DeckItem[], answers: Readonly<Record<string, string>>, connection: DeckTreeConnection, options?: DeckTreeOptions): DeckTree;
|
|
187
|
+
/** A tree's shape for a graph: one node per card (with its answer), one edge per parent link. */
|
|
188
|
+
interface DeckTreeGraph {
|
|
189
|
+
readonly nodes: readonly {
|
|
190
|
+
readonly id: string;
|
|
191
|
+
readonly text: string;
|
|
192
|
+
readonly answer: string | null;
|
|
193
|
+
}[];
|
|
194
|
+
readonly edges: readonly {
|
|
195
|
+
readonly from: string;
|
|
196
|
+
readonly to: string;
|
|
197
|
+
}[];
|
|
198
|
+
}
|
|
199
|
+
declare function deckTreeGraph(items: readonly DeckItem[], answers: Readonly<Record<string, string>>): DeckTreeGraph;
|
|
200
|
+
|
|
201
|
+
export { TREE_ANSWERS, TREE_JUST_RIGHT, TREE_KEY, TREE_NOT_USEFUL, TREE_TOO_BROAD, TREE_TOO_NARROW, deckTreeGraph, treeBreadcrumb, treeChildren, treePath, useDeckTree };
|
|
202
|
+
export type { DeckTree, DeckTreeConnection, DeckTreeGraph, DeckTreeOptions };
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/swipe-deck/tree.ts
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
var TREE_JUST_RIGHT = "just_right";
|
|
6
|
+
var TREE_TOO_BROAD = "too_broad";
|
|
7
|
+
var TREE_TOO_NARROW = "too_narrow";
|
|
8
|
+
var TREE_NOT_USEFUL = "not_useful";
|
|
9
|
+
var TREE_KEY = {
|
|
10
|
+
right: { id: TREE_JUST_RIGHT, label: "Just right", tone: "success", rank: 3 },
|
|
11
|
+
up: { id: TREE_TOO_BROAD, label: "Too broad", tone: "info", rank: 2 },
|
|
12
|
+
down: { id: TREE_TOO_NARROW, label: "Too narrow", tone: "warning", rank: 1 },
|
|
13
|
+
left: { id: TREE_NOT_USEFUL, label: "Not useful", tone: "destructive", rank: 0 }
|
|
14
|
+
};
|
|
15
|
+
var TREE_ANSWERS = [TREE_KEY.right, TREE_KEY.up, TREE_KEY.down, TREE_KEY.left];
|
|
16
|
+
function treePath(items, id) {
|
|
17
|
+
const byId = new Map(items.map((i) => [i.id, i]));
|
|
18
|
+
const path = [];
|
|
19
|
+
const seen = /* @__PURE__ */ new Set([id]);
|
|
20
|
+
let parent = byId.get(id)?.parent;
|
|
21
|
+
while (parent && !seen.has(parent)) {
|
|
22
|
+
seen.add(parent);
|
|
23
|
+
const up = byId.get(parent);
|
|
24
|
+
if (!up) break;
|
|
25
|
+
path.unshift(up);
|
|
26
|
+
parent = up.parent;
|
|
27
|
+
}
|
|
28
|
+
return path;
|
|
29
|
+
}
|
|
30
|
+
function treeBreadcrumb(items, id) {
|
|
31
|
+
return treePath(items, id).map((i) => i.text).join(" \u203A ");
|
|
32
|
+
}
|
|
33
|
+
function treeChildren(items, id) {
|
|
34
|
+
const ids = new Set(items.map((i) => i.id));
|
|
35
|
+
return items.filter((i) => id === null ? !i.parent || !ids.has(i.parent) : i.parent === id);
|
|
36
|
+
}
|
|
37
|
+
var messageOf = (e) => e instanceof Error ? e.message : String(e);
|
|
38
|
+
function compose(seed, grown) {
|
|
39
|
+
const out = [];
|
|
40
|
+
const placed = /* @__PURE__ */ new Set();
|
|
41
|
+
const visit = (item) => {
|
|
42
|
+
if (placed.has(item.id)) return;
|
|
43
|
+
placed.add(item.id);
|
|
44
|
+
out.push(item);
|
|
45
|
+
for (const g of grown) if (g.after === item.id) g.items.forEach(visit);
|
|
46
|
+
};
|
|
47
|
+
seed.forEach(visit);
|
|
48
|
+
for (const g of grown) g.items.forEach(visit);
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
function treeOf(seed, grown) {
|
|
52
|
+
const moved = /* @__PURE__ */ new Map();
|
|
53
|
+
for (const g of grown) if ("combined" in g.from) {
|
|
54
|
+
for (const id of g.from.combined) if (g.items[0]) moved.set(id, g.items[0].id);
|
|
55
|
+
}
|
|
56
|
+
return compose(seed, grown).map((i) => moved.has(i.id) ? { ...i, parent: moved.get(i.id) } : i);
|
|
57
|
+
}
|
|
58
|
+
function withBreadcrumbs(items) {
|
|
59
|
+
const byId = new Map(items.map((i) => [i.id, i]));
|
|
60
|
+
const crumbs = /* @__PURE__ */ new Map();
|
|
61
|
+
const crumbOf = (id, seen) => {
|
|
62
|
+
const known = crumbs.get(id);
|
|
63
|
+
if (known !== void 0) return known;
|
|
64
|
+
const parent = byId.get(id)?.parent;
|
|
65
|
+
const up = parent ? byId.get(parent) : void 0;
|
|
66
|
+
const crumb = up && !seen.has(up.id) ? [crumbOf(up.id, seen.add(id)), up.text].filter(Boolean).join(" \u203A ") : "";
|
|
67
|
+
crumbs.set(id, crumb);
|
|
68
|
+
return crumb;
|
|
69
|
+
};
|
|
70
|
+
return items.map((i) => {
|
|
71
|
+
const crumb = crumbOf(i.id, /* @__PURE__ */ new Set());
|
|
72
|
+
if (!crumb) return i;
|
|
73
|
+
return { ...i, kicker: i.kicker ? `${crumb} \xB7 ${i.kicker}` : crumb };
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function useDeckTree(seed, answers, connection, options = {}) {
|
|
77
|
+
const combineAt = Math.max(2, options.combineAt ?? 2);
|
|
78
|
+
const breadcrumbs = options.breadcrumbs ?? true;
|
|
79
|
+
const [grown, setGrown] = React.useState([]);
|
|
80
|
+
const [working, setWorking] = React.useState([]);
|
|
81
|
+
const [problems, setProblems] = React.useState([]);
|
|
82
|
+
const splitCache = React.useRef(/* @__PURE__ */ new Map());
|
|
83
|
+
const asked = React.useRef(/* @__PURE__ */ new Set());
|
|
84
|
+
const minted = React.useRef(/* @__PURE__ */ new Set());
|
|
85
|
+
const unique = (wanted, fallback) => {
|
|
86
|
+
const taken = /* @__PURE__ */ new Set([...compose(live.current.seed, live.current.grown).map((i) => i.id), ...minted.current]);
|
|
87
|
+
let id = wanted && !taken.has(wanted) ? wanted : fallback;
|
|
88
|
+
for (let n = 2; taken.has(id); n += 1) id = `${fallback}~${n}`;
|
|
89
|
+
minted.current.add(id);
|
|
90
|
+
return id;
|
|
91
|
+
};
|
|
92
|
+
const live = React.useRef({ seed, answers, connection, grown });
|
|
93
|
+
React.useLayoutEffect(() => {
|
|
94
|
+
live.current = { seed, answers, connection, grown };
|
|
95
|
+
});
|
|
96
|
+
const raw = treeOf(seed, grown);
|
|
97
|
+
const items = breadcrumbs ? withBreadcrumbs(raw) : raw;
|
|
98
|
+
React.useEffect(() => {
|
|
99
|
+
const raw2 = treeOf(seed, grown);
|
|
100
|
+
const connection2 = live.current.connection;
|
|
101
|
+
const current = () => {
|
|
102
|
+
const { seed: s, grown: g, answers: a } = live.current;
|
|
103
|
+
const all = compose(s, g);
|
|
104
|
+
return all.find((i) => !a[i.id])?.id ?? all.at(-1)?.id ?? "";
|
|
105
|
+
};
|
|
106
|
+
const place = (batch) => setGrown((g) => [...g, batch]);
|
|
107
|
+
for (const item of raw2) {
|
|
108
|
+
if (answers[item.id] !== TREE_TOO_BROAD || asked.current.has(item.id)) continue;
|
|
109
|
+
if (grown.some((g) => "split" in g.from && g.from.split === item.id)) continue;
|
|
110
|
+
asked.current.add(item.id);
|
|
111
|
+
const cached = splitCache.current.get(item.id);
|
|
112
|
+
if (cached) {
|
|
113
|
+
if (cached.length) place({ after: current(), items: cached, from: { split: item.id } });
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
setWorking((w) => [...w, item.id]);
|
|
117
|
+
connection2.split(item, treePath(raw2, item.id)).then((children) => {
|
|
118
|
+
const kids = children.map((c, n) => ({ ...c, id: unique(c.id, `${item.id}/${n + 1}`), parent: item.id }));
|
|
119
|
+
splitCache.current.set(item.id, kids);
|
|
120
|
+
if (!kids.length) {
|
|
121
|
+
setProblems((p) => [...p, `Nothing narrower than \u201C${item.text}\u201D was found.`]);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (live.current.answers[item.id] === TREE_TOO_BROAD) place({ after: current(), items: kids, from: { split: item.id } });
|
|
125
|
+
else asked.current.delete(item.id);
|
|
126
|
+
}).catch((e) => {
|
|
127
|
+
asked.current.delete(item.id);
|
|
128
|
+
console.error("[swipe-deck-tree] split failed:", e);
|
|
129
|
+
setProblems((p) => [...p, `Could not split \u201C${item.text}\u201D: ${messageOf(e)}`]);
|
|
130
|
+
}).finally(() => setWorking((w) => w.filter((x) => x !== item.id)));
|
|
131
|
+
}
|
|
132
|
+
const stale = grown.filter((g) => "split" in g.from && answers[g.from.split] !== TREE_TOO_BROAD);
|
|
133
|
+
if (stale.length) {
|
|
134
|
+
for (const g of stale) if ("split" in g.from) asked.current.delete(g.from.split);
|
|
135
|
+
setGrown(
|
|
136
|
+
(all) => all.flatMap((g) => {
|
|
137
|
+
if (!stale.includes(g)) return [g];
|
|
138
|
+
const kept = g.items.filter((i) => live.current.answers[i.id]);
|
|
139
|
+
return kept.length && "split" in g.from ? [{ after: g.after, items: kept, from: { kept: g.from.split } }] : [];
|
|
140
|
+
})
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
const loose = grown.filter((g) => "combined" in g.from && g.from.combined.some((id) => answers[id] !== TREE_TOO_NARROW));
|
|
144
|
+
if (loose.length) {
|
|
145
|
+
for (const g of loose) if ("combined" in g.from) for (const id of g.from.combined) asked.current.delete(`combine:${id}`);
|
|
146
|
+
setGrown(
|
|
147
|
+
(all) => all.flatMap((g) => {
|
|
148
|
+
if (!loose.includes(g)) return [g];
|
|
149
|
+
const kept = g.items.filter((i) => live.current.answers[i.id]);
|
|
150
|
+
return kept.length ? [{ after: g.after, items: kept, from: { kept: kept[0].id } }] : [];
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
const combine = connection2.combine;
|
|
155
|
+
if (!combine) return;
|
|
156
|
+
const combined = new Set(grown.flatMap((g) => "combined" in g.from ? g.from.combined : []));
|
|
157
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
158
|
+
for (const item of raw2) {
|
|
159
|
+
if (answers[item.id] !== TREE_TOO_NARROW || combined.has(item.id) || asked.current.has(`combine:${item.id}`)) continue;
|
|
160
|
+
const key = item.parent ?? "";
|
|
161
|
+
byParent.set(key, [...byParent.get(key) ?? [], item]);
|
|
162
|
+
}
|
|
163
|
+
for (const [parentId, group] of byParent) {
|
|
164
|
+
if (group.length < combineAt) continue;
|
|
165
|
+
const key = `combine:${parentId}:${group.map((g) => g.id).join(",")}`;
|
|
166
|
+
for (const g of group) asked.current.add(`combine:${g.id}`);
|
|
167
|
+
setWorking((w) => [...w, key]);
|
|
168
|
+
combine(group, raw2.find((i) => i.id === parentId) ?? null).then((made) => {
|
|
169
|
+
if (!made) {
|
|
170
|
+
setProblems((p) => [...p, `Nothing combines ${group.map((g) => `\u201C${g.text}\u201D`).join(" and ")}.`]);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const id = unique(made.id, `${made.id}/combined`);
|
|
174
|
+
place({
|
|
175
|
+
after: current(),
|
|
176
|
+
items: [{ ...made, id, ...parentId ? { parent: parentId } : {} }],
|
|
177
|
+
from: { combined: group.map((g) => g.id) }
|
|
178
|
+
});
|
|
179
|
+
}).catch((e) => {
|
|
180
|
+
for (const g of group) asked.current.delete(`combine:${g.id}`);
|
|
181
|
+
console.error("[swipe-deck-tree] combine failed:", e);
|
|
182
|
+
setProblems((p) => [...p, `Could not combine ${group.length} cards: ${messageOf(e)}`]);
|
|
183
|
+
}).finally(() => setWorking((w) => w.filter((x) => x !== key)));
|
|
184
|
+
}
|
|
185
|
+
}, [answers, seed, grown, combineAt]);
|
|
186
|
+
return { items, working, problems };
|
|
187
|
+
}
|
|
188
|
+
function deckTreeGraph(items, answers) {
|
|
189
|
+
const ids = new Set(items.map((i) => i.id));
|
|
190
|
+
return {
|
|
191
|
+
nodes: items.map((i) => ({ id: i.id, text: i.text, answer: answers[i.id] ?? null })),
|
|
192
|
+
edges: items.filter((i) => i.parent && ids.has(i.parent)).map((i) => ({ from: i.parent, to: i.id }))
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
export {
|
|
196
|
+
TREE_ANSWERS,
|
|
197
|
+
TREE_JUST_RIGHT,
|
|
198
|
+
TREE_KEY,
|
|
199
|
+
TREE_NOT_USEFUL,
|
|
200
|
+
TREE_TOO_BROAD,
|
|
201
|
+
TREE_TOO_NARROW,
|
|
202
|
+
deckTreeGraph,
|
|
203
|
+
treeBreadcrumb,
|
|
204
|
+
treeChildren,
|
|
205
|
+
treePath,
|
|
206
|
+
useDeckTree
|
|
207
|
+
};
|
|
208
|
+
//# sourceMappingURL=swipe-deck-tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/swipe-deck/tree.ts"],"sourcesContent":["\"use client\";\n\n/**\n * @ai-matrx/design-system/swipe-deck-tree — A DECK THAT GROWS INTO A TREE (Arman, 2026-10-24).\n *\n * Each card is answered Just right, Too broad, Too narrow or Not useful. \"Too broad\" asks the host for narrower\n * cards under it (`split`), which join the deck right after the card on screen and get the same treatment, as deep\n * as the person goes. \"Too narrow\" on two or more cards under one parent asks the host for one card that combines\n * them (`combine`); they move under it. The tree is nothing but `DeckItem.parent`, so the deck itself is unchanged\n * and a page that never imports this entry pays nothing for it.\n *\n * The host owns the meaning (an agent, a search, a list): this entry never invents a card. A split or combine that\n * fails is said in `problems`, never dropped. `deckTreeGraph` hands the result to a graph (rich-content's\n * `@ai-matrx/rich-content/swipe-deck-tree` draws it on the one diagram canvas).\n */\n\nimport * as React from \"react\";\nimport type { DeckAnswer, DeckItem, DeckKey } from \"./model\";\n\nexport const TREE_JUST_RIGHT = \"just_right\";\nexport const TREE_TOO_BROAD = \"too_broad\";\nexport const TREE_TOO_NARROW = \"too_narrow\";\nexport const TREE_NOT_USEFUL = \"not_useful\";\n\n/** The four answers, on the edges a person expects: right keeps it, up opens it, down folds it, left drops it. */\nexport const TREE_KEY: DeckKey = {\n right: { id: TREE_JUST_RIGHT, label: \"Just right\", tone: \"success\", rank: 3 },\n up: { id: TREE_TOO_BROAD, label: \"Too broad\", tone: \"info\", rank: 2 },\n down: { id: TREE_TOO_NARROW, label: \"Too narrow\", tone: \"warning\", rank: 1 },\n left: { id: TREE_NOT_USEFUL, label: \"Not useful\", tone: \"destructive\", rank: 0 },\n};\n\n/** The four answers as a list (a table's answer column, a legend). */\nexport const TREE_ANSWERS: readonly DeckAnswer[] = [TREE_KEY.right!, TREE_KEY.up!, TREE_KEY.down!, TREE_KEY.left!];\n\n/** The ancestors of a card, root first (a missing or looping parent ends the walk). */\nexport function treePath(items: readonly DeckItem[], id: string): DeckItem[] {\n const byId = new Map(items.map((i) => [i.id, i]));\n const path: DeckItem[] = [];\n const seen = new Set<string>([id]);\n let parent = byId.get(id)?.parent;\n while (parent && !seen.has(parent)) {\n seen.add(parent);\n const up = byId.get(parent);\n if (!up) break;\n path.unshift(up);\n parent = up.parent;\n }\n return path;\n}\n\n/** The breadcrumb a narrower card carries as its kicker: \"Marketing › Medical marketing\". */\nexport function treeBreadcrumb(items: readonly DeckItem[], id: string): string {\n return treePath(items, id)\n .map((i) => i.text)\n .join(\" › \");\n}\n\n/** The cards directly under one card (null: the roots). */\nexport function treeChildren(items: readonly DeckItem[], id: string | null): DeckItem[] {\n const ids = new Set(items.map((i) => i.id));\n return items.filter((i) => (id === null ? !i.parent || !ids.has(i.parent) : i.parent === id));\n}\n\n/** What the host answers. Each call may take seconds (an agent); the deck keeps going meanwhile. */\nexport interface DeckTreeConnection {\n /** Narrower cards under `item` (`path` is its ancestors, root first). Their `parent` is set to `item.id`. */\n split(item: DeckItem, path: readonly DeckItem[]): Promise<readonly DeckItem[]>;\n /** One card that combines `items` (all under `parent`); they move under it. Omit to never combine. */\n combine?(items: readonly DeckItem[], parent: DeckItem | null): Promise<DeckItem | null>;\n}\n\nexport interface DeckTreeOptions {\n /** How many \"Too narrow\" cards under one parent ask for a combine. Default 2. */\n readonly combineAt?: number;\n /** Prefix each grown card's kicker with its breadcrumb. Default true. */\n readonly breadcrumbs?: boolean;\n}\n\nexport interface DeckTree {\n /** The deck to draw: the seed with every grown card in place (each after the card that was on screen). */\n readonly items: DeckItem[];\n /** Cards whose split or combine is running (for a \"finding narrower ones\" mark). */\n readonly working: readonly string[];\n /** One sentence per split or combine that failed. */\n readonly problems: readonly string[];\n}\n\ninterface Grown {\n /** The card it was placed after. */\n readonly after: string;\n readonly items: readonly DeckItem[];\n /** Why it exists: the split card, the combined cards, or kept (answered children of a split since undone). */\n readonly from: { readonly split: string } | { readonly combined: readonly string[] } | { readonly kept: string };\n}\n\nconst messageOf = (e: unknown) => (e instanceof Error ? e.message : String(e));\n\n/** Seed + grown cards in deck order: each grown batch right after its anchor, recursively. */\nfunction compose(seed: readonly DeckItem[], grown: readonly Grown[]): DeckItem[] {\n const out: DeckItem[] = [];\n const placed = new Set<string>();\n const visit = (item: DeckItem) => {\n if (placed.has(item.id)) return;\n placed.add(item.id);\n out.push(item);\n for (const g of grown) if (g.after === item.id) g.items.forEach(visit);\n };\n seed.forEach(visit);\n // A batch whose anchor is gone (undone away) still shows, at the end.\n for (const g of grown) g.items.forEach(visit);\n return out;\n}\n\n/** The cards with the tree applied: a combined card moves under the card that combines it. */\nfunction treeOf(seed: readonly DeckItem[], grown: readonly Grown[]): DeckItem[] {\n const moved = new Map<string, string>();\n for (const g of grown) if (\"combined\" in g.from) for (const id of g.from.combined) if (g.items[0]) moved.set(id, g.items[0].id);\n return compose(seed, grown).map((i) => (moved.has(i.id) ? { ...i, parent: moved.get(i.id)! } : i));\n}\n\n/** Each narrower card's kicker leads with its path; a card's own kicker stays after it. One pass, memoized paths. */\nfunction withBreadcrumbs(items: readonly DeckItem[]): DeckItem[] {\n const byId = new Map(items.map((i) => [i.id, i]));\n const crumbs = new Map<string, string>();\n const crumbOf = (id: string, seen: Set<string>): string => {\n const known = crumbs.get(id);\n if (known !== undefined) return known;\n const parent = byId.get(id)?.parent;\n const up = parent ? byId.get(parent) : undefined;\n const crumb = up && !seen.has(up.id) ? [crumbOf(up.id, seen.add(id)), up.text].filter(Boolean).join(\" › \") : \"\";\n crumbs.set(id, crumb);\n return crumb;\n };\n return items.map((i) => {\n const crumb = crumbOf(i.id, new Set());\n if (!crumb) return i;\n return { ...i, kicker: i.kicker ? `${crumb} · ${i.kicker}` : crumb };\n });\n}\n\n/**\n * The tree, grown from the person's answers. Pass the deck's own answers (a bound deck's `binding.state.answers`,\n * or the last `onChange` result's) and draw `items`.\n */\nexport function useDeckTree(\n seed: readonly DeckItem[],\n answers: Readonly<Record<string, string>>,\n connection: DeckTreeConnection,\n options: DeckTreeOptions = {},\n): DeckTree {\n const combineAt = Math.max(2, options.combineAt ?? 2);\n const breadcrumbs = options.breadcrumbs ?? true;\n const [grown, setGrown] = React.useState<readonly Grown[]>([]);\n const [working, setWorking] = React.useState<readonly string[]>([]);\n const [problems, setProblems] = React.useState<readonly string[]>([]);\n // Children once fetched are kept: undoing \"Too broad\" and giving it again costs no second call.\n const splitCache = React.useRef(new Map<string, readonly DeckItem[]>());\n const asked = React.useRef(new Set<string>());\n // Every id ever handed out here: two answers landing before a render must never mint the same one.\n const minted = React.useRef(new Set<string>());\n const unique = (wanted: string | undefined, fallback: string) => {\n const taken = new Set([...compose(live.current.seed, live.current.grown).map((i) => i.id), ...minted.current]);\n let id = wanted && !taken.has(wanted) ? wanted : fallback;\n for (let n = 2; taken.has(id); n += 1) id = `${fallback}~${n}`;\n minted.current.add(id);\n return id;\n };\n const live = React.useRef({ seed, answers, connection, grown });\n React.useLayoutEffect(() => {\n live.current = { seed, answers, connection, grown };\n });\n\n const raw = treeOf(seed, grown);\n const items = breadcrumbs ? withBreadcrumbs(raw) : raw;\n\n React.useEffect(() => {\n const raw = treeOf(seed, grown);\n const connection = live.current.connection;\n const current = () => {\n const { seed: s, grown: g, answers: a } = live.current;\n const all = compose(s, g);\n return all.find((i) => !a[i.id])?.id ?? all.at(-1)?.id ?? \"\";\n };\n const place = (batch: Grown) => setGrown((g) => [...g, batch]);\n\n // Too broad: split (once per card; a cached answer comes straight back).\n for (const item of raw) {\n if (answers[item.id] !== TREE_TOO_BROAD || asked.current.has(item.id)) continue;\n if (grown.some((g) => \"split\" in g.from && g.from.split === item.id)) continue;\n asked.current.add(item.id);\n const cached = splitCache.current.get(item.id);\n if (cached) {\n if (cached.length) place({ after: current(), items: cached, from: { split: item.id } });\n continue;\n }\n setWorking((w) => [...w, item.id]);\n connection\n .split(item, treePath(raw, item.id))\n .then((children) => {\n const kids = children.map((c, n) => ({ ...c, id: unique(c.id, `${item.id}/${n + 1}`), parent: item.id }));\n splitCache.current.set(item.id, kids);\n // Nothing narrower: said once, and not asked again for this card (it stays answered Too broad).\n if (!kids.length) {\n setProblems((p) => [...p, `Nothing narrower than “${item.text}” was found.`]);\n return;\n }\n // Still too broad when the answer came back? (It may have been undone meanwhile.)\n if (live.current.answers[item.id] === TREE_TOO_BROAD) place({ after: current(), items: kids, from: { split: item.id } });\n else asked.current.delete(item.id);\n })\n .catch((e: unknown) => {\n asked.current.delete(item.id);\n console.error(\"[swipe-deck-tree] split failed:\", e);\n setProblems((p) => [...p, `Could not split “${item.text}”: ${messageOf(e)}`]);\n })\n .finally(() => setWorking((w) => w.filter((x) => x !== item.id)));\n }\n\n // Undone: a split whose card is no longer Too broad takes back its unanswered children.\n const stale = grown.filter((g) => \"split\" in g.from && answers[g.from.split] !== TREE_TOO_BROAD);\n if (stale.length) {\n for (const g of stale) if (\"split\" in g.from) asked.current.delete(g.from.split);\n setGrown((all) =>\n all.flatMap((g) => {\n if (!stale.includes(g)) return [g];\n const kept = g.items.filter((i) => live.current.answers[i.id]);\n return kept.length && \"split\" in g.from ? [{ after: g.after, items: kept, from: { kept: g.from.split } }] : [];\n }),\n );\n }\n\n // Undone: a combine one of whose cards is no longer Too narrow takes back its card (if unanswered) and lets\n // the others combine again.\n const loose = grown.filter((g) => \"combined\" in g.from && g.from.combined.some((id) => answers[id] !== TREE_TOO_NARROW));\n if (loose.length) {\n for (const g of loose) if (\"combined\" in g.from) for (const id of g.from.combined) asked.current.delete(`combine:${id}`);\n setGrown((all) =>\n all.flatMap((g) => {\n if (!loose.includes(g)) return [g];\n const kept = g.items.filter((i) => live.current.answers[i.id]);\n return kept.length ? [{ after: g.after, items: kept, from: { kept: kept[0]!.id } }] : [];\n }),\n );\n }\n\n // Too narrow: two or more under one parent, not combined yet → one combining card.\n const combine = connection.combine;\n if (!combine) return;\n const combined = new Set(grown.flatMap((g) => (\"combined\" in g.from ? g.from.combined : [])));\n const byParent = new Map<string, DeckItem[]>();\n for (const item of raw) {\n if (answers[item.id] !== TREE_TOO_NARROW || combined.has(item.id) || asked.current.has(`combine:${item.id}`)) continue;\n const key = item.parent ?? \"\";\n byParent.set(key, [...(byParent.get(key) ?? []), item]);\n }\n for (const [parentId, group] of byParent) {\n if (group.length < combineAt) continue;\n const key = `combine:${parentId}:${group.map((g) => g.id).join(\",\")}`;\n for (const g of group) asked.current.add(`combine:${g.id}`);\n setWorking((w) => [...w, key]);\n combine(group, raw.find((i) => i.id === parentId) ?? null)\n .then((made) => {\n if (!made) {\n setProblems((p) => [...p, `Nothing combines ${group.map((g) => `“${g.text}”`).join(\" and \")}.`]);\n return;\n }\n const id = unique(made.id, `${made.id}/combined`);\n place({\n after: current(),\n items: [{ ...made, id, ...(parentId ? { parent: parentId } : {}) }],\n from: { combined: group.map((g) => g.id) },\n });\n })\n .catch((e: unknown) => {\n for (const g of group) asked.current.delete(`combine:${g.id}`);\n console.error(\"[swipe-deck-tree] combine failed:\", e);\n setProblems((p) => [...p, `Could not combine ${group.length} cards: ${messageOf(e)}`]);\n })\n .finally(() => setWorking((w) => w.filter((x) => x !== key)));\n }\n }, [answers, seed, grown, combineAt]);\n\n return { items, working, problems };\n}\n\n/** A tree's shape for a graph: one node per card (with its answer), one edge per parent link. */\nexport interface DeckTreeGraph {\n readonly nodes: readonly { readonly id: string; readonly text: string; readonly answer: string | null }[];\n readonly edges: readonly { readonly from: string; readonly to: string }[];\n}\n\nexport function deckTreeGraph(items: readonly DeckItem[], answers: Readonly<Record<string, string>>): DeckTreeGraph {\n const ids = new Set(items.map((i) => i.id));\n return {\n nodes: items.map((i) => ({ id: i.id, text: i.text, answer: answers[i.id] ?? null })),\n edges: items.filter((i) => i.parent && ids.has(i.parent)).map((i) => ({ from: i.parent!, to: i.id })),\n };\n}\n"],"mappings":"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","names":["raw","connection"]}
|