@hachej/boring-ui-kit 0.4.57 → 0.4.59
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/bin/boring-ui-blocks.mjs +49 -4
- package/dist/chat-react-conversations.d.ts +7 -1
- package/dist/chat-react-conversations.js +58 -18
- package/package.json +6 -6
- package/r/_shared.json +19 -0
- package/r/agent-target.json +4 -4
- package/r/app-shell.json +17 -17
- package/r/board-viewer.json +10 -10
- package/r/button.json +4 -4
- package/r/data-table.json +21 -0
- package/r/diagram-viewer.json +6 -4
- package/r/dock.json +1 -1
- package/r/documents.json +13 -14
- package/r/drawing-viewer.json +6 -4
- package/r/empty-state.json +22 -0
- package/r/feedback.json +14 -14
- package/r/file-tree.json +4 -4
- package/r/highlights.json +3 -3
- package/r/html-viewer.json +3 -3
- package/r/kpi-stat.json +22 -0
- package/r/list-row.json +21 -0
- package/r/looks-viewer.json +6 -6
- package/r/maquette-viewer.json +7 -6
- package/r/markdown-editor.json +10 -9
- package/r/mockup-viewer.json +5 -5
- package/r/page-header.json +22 -0
- package/r/pi-ambient.json +12 -12
- package/r/pi-app-kinds.json +10 -10
- package/r/pi-app.json +17 -17
- package/r/pi-artifacts.json +4 -4
- package/r/pi-chat-approval.json +4 -4
- package/r/pi-chat-attachments.json +4 -4
- package/r/pi-chat-markdown.json +3 -3
- package/r/pi-chat-mentions.json +4 -4
- package/r/pi-chat-pickers.json +4 -4
- package/r/pi-chat-plugins.json +11 -11
- package/r/pi-chat-question.json +3 -3
- package/r/pi-chat-slash.json +5 -5
- package/r/pi-chat-subagent.json +4 -4
- package/r/pi-chat-voice.json +7 -7
- package/r/pi-chat.json +20 -20
- package/r/pi-workspace.json +3 -3
- package/r/placed-agent.json +7 -7
- package/r/provider-setup.json +4 -4
- package/r/record-viewer.json +5 -3
- package/r/registry.json +494 -280
- package/r/shared-chat.json +11 -11
- package/r/spaces.json +7 -5
- package/r/task-list-viewer.json +28 -3
- package/r/theme.json +115 -50
- package/r/version-history.json +2 -2
- package/r/viewer-code.json +5 -5
- package/r/viewer-frame.json +9 -9
- package/r/viewer-html.json +7 -7
- package/r/viewer-image.json +5 -5
- package/r/viewer-markdown.json +7 -7
- package/r/viewer-pdf.json +6 -6
- package/r/viewers.json +6 -6
- package/r/windows.json +4 -4
package/bin/boring-ui-blocks.mjs
CHANGED
|
@@ -24,8 +24,46 @@ const borderWidth = /(?<![\w-])border(?:-[trblxyse])?(?:-(?:\d+|\[(?:\d|\.)[^\]]
|
|
|
24
24
|
const color = /#[0-9a-fA-F]{3,8}\b|\b(?:rgb|rgba|hsl|hsla|oklch|oklab)\([^)]*\)|\b(?:bg|text|border|ring|fill|stroke|outline|decoration|shadow)-(?:white|black|(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-\d{2,3})(?:\/\d+)?\b/g;
|
|
25
25
|
const font = /\bfont-\[(?!var\(|--)[^\]]+\]|\bfont-family\s*:\s*(?!\s*(?:var\(|inherit\b))[^;\n}]+|\bfontFamily\s*:\s*['"](?!var\()[^'"]+['"]|\bfont\s*:\s*[^;\n}]*\b(?:serif|monospace|system-ui)\b[^;\n}]*/g;
|
|
26
26
|
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* `design-no-x-scroll`: a file that renders a table holds no horizontal scroll container (`overflow-x-auto`, `overflow-x-scroll`, the
|
|
29
|
+
* CSS or React style property). Tables truncate their cells and stack below their container width instead. Lexical and file-scoped: the
|
|
30
|
+
* shadcn `table` wrapper (`overflow-x-auto`) must not be imported by a file that renders a table.
|
|
31
|
+
*/
|
|
32
|
+
const tableMarkup = /<table\b|<Table\b|@\/components\/ui\/table\b|\bDataTable\b|role=["']table["']|\bdisplay\s*:\s*['"]?table\b/;
|
|
33
|
+
const horizontalScroll = /(?<![\w-])overflow-x-(?:auto|scroll)(?![\w-])|\boverflowX\s*:\s*['"]?(?:auto|scroll)\b|\boverflow-x\s*:\s*(?:auto|scroll)\b/g;
|
|
34
|
+
export function noXScroll(source) {
|
|
35
|
+
return tableMarkup.test(source) ? [...source.matchAll(horizontalScroll)].map(match => ({ index: match.index, value: match[0] })) : [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
// The flat card: a Card's own className carries no border, shadow or ring. The shadcn Card's ring lives in its generated file, which blocks never restyle.
|
|
40
|
+
const cardFlat = /<Card\b[^>]*?\bclassName\s*=\s*(?:"[^"]*\b(?:border|shadow|ring)\b[^"]*"|\{[^}\n]*\b(?:border|shadow|ring)\b[^}\n]*\})/g;
|
|
41
|
+
|
|
42
|
+
/** `<Card` tags opened inside another open `<Card>` (a self-closing Card and a Card in a prop are not nesting). Lexical, over JSX tags. */
|
|
43
|
+
function nestedCards(source) {
|
|
44
|
+
const found = [], open = [], tag = /<(\/?)Card\b/g;
|
|
45
|
+
for (let match; (match = tag.exec(source));) {
|
|
46
|
+
if (match[1]) { open.pop(); continue; }
|
|
47
|
+
let depth = 0, quote = null, end = match.index + match[0].length;
|
|
48
|
+
for (; end < source.length; end++) {
|
|
49
|
+
const char = source[end];
|
|
50
|
+
if (quote) { if (char === quote) quote = null; }
|
|
51
|
+
else if (/["'`]/.test(char)) quote = char;
|
|
52
|
+
else if (char === '{') depth++;
|
|
53
|
+
else if (char === '}') depth--;
|
|
54
|
+
else if (char === '>' && depth === 0) break;
|
|
55
|
+
}
|
|
56
|
+
if (open.length) found.push({ index: match.index, value: source.slice(match.index, end + 1).split('\n')[0] });
|
|
57
|
+
if (source[end - 1] !== '/') open.push(match.index);
|
|
58
|
+
tag.lastIndex = end + 1;
|
|
59
|
+
}
|
|
60
|
+
return found;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The repository's rules (registry blocks and kit sources). A rule is a global regex, or a function from the source to `{ index, value }` findings. */
|
|
28
64
|
export const DESIGN_RULES = [
|
|
65
|
+
['design-card-flat', cardFlat],
|
|
66
|
+
['design-no-nested-card', nestedCards],
|
|
29
67
|
['shadow', shadow],
|
|
30
68
|
['radius', radius],
|
|
31
69
|
['border-width', borderWidth],
|
|
@@ -34,6 +72,7 @@ export const DESIGN_RULES = [
|
|
|
34
72
|
['motion', /\b(?:duration|delay)-\d+\b|\b(?:transition(?:-[\w-]+)?|animation(?:-[\w-]+)?|transitionDuration|animationDuration)\s*:\s*[^;\n}]*?\b\d*\.?\d+m?s\b[^;\n}]*/g],
|
|
35
73
|
['style', /\bradix(?:-[\w-]+)?\b/gi],
|
|
36
74
|
['primitive', /\b(?:function|const|class)\s+(?:Button|IconButton|ActionMenu|\w*Popover|\w*Tooltip|\w*Dialog|\w*Menu)\b|<(?:button|input|textarea|select)\b/g],
|
|
75
|
+
['design-no-x-scroll', noXScroll],
|
|
37
76
|
];
|
|
38
77
|
|
|
39
78
|
/**
|
|
@@ -44,6 +83,8 @@ export const DESIGN_RULES = [
|
|
|
44
83
|
*/
|
|
45
84
|
const LENGTH = String.raw`(?<![\w.#-])(?!1px\b)\d*\.?\d+(?:px|rem|em)\b`;
|
|
46
85
|
export const APP_RULES = [
|
|
86
|
+
['design-card-flat', cardFlat],
|
|
87
|
+
['design-no-nested-card', nestedCards],
|
|
47
88
|
['color', color],
|
|
48
89
|
['arbitrary-value', /(?<![\w-])(?:bg|text|border(?:-[trblxyse])?|ring(?:-offset)?|outline|fill|stroke|decoration|shadow|drop-shadow|inset-shadow|rounded(?:-[trblse]{1,2})?|font|tracking|leading|from|via|to|divide(?:-[xy])?|placeholder|caret|accent)-\[(?!var\(|--|#|(?:rgb|rgba|hsl|hsla|oklch|oklab)\()[^\]]+\]/g],
|
|
49
90
|
['border-width', /(?<![\w-])border(?:-[trblxyse])?-(?!0(?![\w.]))\d+(?![\w-])/g],
|
|
@@ -51,15 +92,19 @@ export const APP_RULES = [
|
|
|
51
92
|
['font', /\bfont-family\s*:\s*(?!\s*(?:var\(|inherit\b))[^;\n}]+|\bfontFamily\s*:\s*['"](?!var\()[^'"]+['"]|\bfont\s*:\s*(?!\s*(?:var\(|inherit\b))[^;\n}]+/g],
|
|
52
93
|
['literal-value', /\b(?:border-radius|borderRadius|box-shadow|boxShadow|font-size|fontSize|letter-spacing|letterSpacing)\s*:\s*['"]?(?!\s*(?:var\(|calc\(\s*var\(|inherit\b|initial\b|unset\b|none\b|0(?![\w.])))(?=[^;\n}"']*\d)[^;\n}"']*/g],
|
|
53
94
|
['dark', /(?<![\w-])dark:[^\s"'`]+/g],
|
|
95
|
+
['design-no-x-scroll', noXScroll],
|
|
54
96
|
];
|
|
55
97
|
|
|
56
98
|
/** Findings `{ file, line, column, rule, value }` of `rules` in `text`; CSS block comments are ignored. */
|
|
57
99
|
export function scanDesign(file, text, rules = DESIGN_RULES) {
|
|
58
100
|
const findings = [];
|
|
59
101
|
const source = text.replace(/\/\*[\s\S]*?\*\//g, match => match.replace(/[^\n]/g, ' '));
|
|
60
|
-
for (const [rule, pattern] of rules)
|
|
61
|
-
const
|
|
62
|
-
|
|
102
|
+
for (const [rule, pattern] of rules) {
|
|
103
|
+
const matches = typeof pattern === 'function' ? pattern(source) : [...source.matchAll(pattern)].map(match => ({ index: match.index, value: match[0] }));
|
|
104
|
+
for (const { index, value } of matches) {
|
|
105
|
+
const before = text.slice(0, index), line = before.split('\n').length;
|
|
106
|
+
findings.push({ file, line, column: index - before.lastIndexOf('\n'), rule, value });
|
|
107
|
+
}
|
|
63
108
|
}
|
|
64
109
|
return findings;
|
|
65
110
|
}
|
|
@@ -11,10 +11,16 @@ export interface ConversationsClient {
|
|
|
11
11
|
readonly activeId: string | undefined;
|
|
12
12
|
/** New or fork is on its way: the conversation on screen is about to be left, so it must take no message (`PiChat` disables its composer). */
|
|
13
13
|
readonly creating: boolean;
|
|
14
|
-
/** The conversation New or fork just opened, for up to three seconds: `PiChat` puts the cursor in its box once, then calls `openedSeen`, so a later remount does not. */
|
|
14
|
+
/** The conversation New or fork just opened, for up to three seconds: `PiChat` puts the cursor in its box once, then calls `openedSeen`, so a later remount does not. Undefined while a dead key or IME composition is open in the hidden field, however long it stays open. */
|
|
15
15
|
readonly opened: string | undefined;
|
|
16
16
|
/** Ends `opened` and returns what the person typed since New, for the new box. */
|
|
17
17
|
readonly openedSeen: () => string;
|
|
18
|
+
/** New chat was refused or got no answer in time, in the conversation on screen. `typed`: the person's text was dropped. `PiChat` and `AmbientChat` say so. */
|
|
19
|
+
readonly newFailed: {
|
|
20
|
+
readonly typed: boolean;
|
|
21
|
+
} | undefined;
|
|
22
|
+
/** The person closed that message. */
|
|
23
|
+
readonly newFailedSeen: () => void;
|
|
18
24
|
readonly reload: () => Promise<void>;
|
|
19
25
|
readonly onSelect: (id: string) => void;
|
|
20
26
|
readonly onNew: () => void;
|
|
@@ -11,15 +11,15 @@ const OPENED_MS = 3000;
|
|
|
11
11
|
/**
|
|
12
12
|
* The clicked button keeps the focus until the new box is back, so a Space typed then would press New again, and the box is held, so the start of a message would be lost.
|
|
13
13
|
* The focus goes to a hidden field until then: it outlives the chat's remount, and dead keys, IME and Backspace work there. Not on a phone, where it would open the keyboard.
|
|
14
|
+
* A composition still open must finish there: its accent is only a preview in the value, and the keyboard commits the letter to whatever has the focus.
|
|
14
15
|
*/
|
|
15
|
-
function holdTyping() {
|
|
16
|
+
function holdTyping(onComposing) {
|
|
16
17
|
const page = globalThis.document, focused = page?.activeElement;
|
|
17
18
|
if (!page || focused?.matches('input, textarea, select, [contenteditable]'))
|
|
18
19
|
return undefined;
|
|
19
|
-
const from = focused?.matches('button, [role="button"]') ? focused : undefined;
|
|
20
20
|
focused?.blur();
|
|
21
21
|
if (globalThis.matchMedia?.('(pointer: coarse)').matches)
|
|
22
|
-
return {
|
|
22
|
+
return { field: undefined, composing: false };
|
|
23
23
|
const field = page.createElement('textarea');
|
|
24
24
|
field.dataset.testid = 'new-chat-typing';
|
|
25
25
|
field.tabIndex = -1;
|
|
@@ -27,29 +27,43 @@ function holdTyping() {
|
|
|
27
27
|
field.style.cssText = 'position:fixed;top:0;left:0;width:1px;height:1px;padding:0;border:0;opacity:0;font-size:16px';
|
|
28
28
|
field.addEventListener('keydown', event => { if (event.key === 'Enter')
|
|
29
29
|
event.preventDefault(); });
|
|
30
|
+
const held = { field, composing: false };
|
|
31
|
+
const composing = (value) => { held.composing = value; onComposing(value); };
|
|
32
|
+
field.addEventListener('compositionstart', () => composing(true));
|
|
33
|
+
field.addEventListener('compositionend', () => composing(false));
|
|
34
|
+
field.addEventListener('blur', () => composing(false));
|
|
30
35
|
page.body.append(field);
|
|
31
36
|
field.focus();
|
|
32
|
-
return
|
|
37
|
+
return held;
|
|
33
38
|
}
|
|
34
39
|
export function useConversationsClient({ endpoint, fetch, activeId, onSelect, onChange, pollMs = 4000, openTimeoutMs = 15000 }) {
|
|
35
40
|
const [items, setItems] = useState();
|
|
36
41
|
const [creating, setCreating] = useState(false);
|
|
37
42
|
const [opened, setOpened] = useState();
|
|
43
|
+
const [failedNew, setFailedNew] = useState();
|
|
38
44
|
const typing = useRef(undefined);
|
|
39
|
-
|
|
45
|
+
const [composing, setComposing] = useState(false);
|
|
46
|
+
// What was typed goes nowhere else, so nothing reaches the conversation being left. The focus does not go back to the button: the next Space would press New again, and a create that works would take the rest of the text as a partial message.
|
|
40
47
|
const dropTyping = useCallback(() => {
|
|
41
48
|
const held = typing.current;
|
|
42
49
|
typing.current = undefined;
|
|
43
50
|
held?.field?.remove();
|
|
44
|
-
|
|
45
|
-
held?.from?.focus();
|
|
51
|
+
setComposing(false);
|
|
46
52
|
}, []);
|
|
47
53
|
useEffect(() => dropTyping, [dropTyping]);
|
|
48
54
|
const openedTimer = useRef(undefined);
|
|
49
55
|
const markOpened = useCallback((id) => {
|
|
50
56
|
clearTimeout(openedTimer.current);
|
|
57
|
+
const expire = () => {
|
|
58
|
+
if (typing.current?.composing) {
|
|
59
|
+
openedTimer.current = setTimeout(expire, OPENED_MS);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
dropTyping();
|
|
63
|
+
setOpened(undefined);
|
|
64
|
+
};
|
|
51
65
|
if (id !== undefined)
|
|
52
|
-
openedTimer.current = setTimeout(
|
|
66
|
+
openedTimer.current = setTimeout(expire, OPENED_MS);
|
|
53
67
|
else
|
|
54
68
|
dropTyping();
|
|
55
69
|
setOpened(id);
|
|
@@ -57,9 +71,20 @@ export function useConversationsClient({ endpoint, fetch, activeId, onSelect, on
|
|
|
57
71
|
// The New or fork on its way; forgetting it gives the composer back, and its late answer then moves nobody.
|
|
58
72
|
const opening = useRef(undefined);
|
|
59
73
|
const forget = useCallback(() => { opening.current = undefined; setCreating(false); }, []);
|
|
60
|
-
const latest = useRef({ fetch, onSelect, onChange, activeId, openTimeoutMs });
|
|
61
|
-
useLayoutEffect(() => { latest.current = { fetch, onSelect, onChange, activeId, openTimeoutMs }; });
|
|
62
74
|
const base = endpoint === undefined ? undefined : String(endpoint);
|
|
75
|
+
const latest = useRef({ fetch, onSelect, onChange, activeId, openTimeoutMs, base });
|
|
76
|
+
// Every conversation this person has had open here: a late create's cleanup must never remove one of them.
|
|
77
|
+
const visited = useRef(new Set());
|
|
78
|
+
// Unmounted, it no longer knows what was opened since: its late cleanup removes nothing.
|
|
79
|
+
const mounted = useRef(true);
|
|
80
|
+
useEffect(() => { mounted.current = true; return () => { mounted.current = false; }; }, []);
|
|
81
|
+
useLayoutEffect(() => { latest.current = { fetch, onSelect, onChange, activeId, openTimeoutMs, base }; if (activeId !== undefined)
|
|
82
|
+
visited.current.add(activeId); });
|
|
83
|
+
// The text is not given back: it stays out of the draft being left, and the message says it is gone.
|
|
84
|
+
const failNew = useCallback(() => {
|
|
85
|
+
setFailedNew({ at: latest.current.activeId, typed: Boolean(typing.current?.field?.value) });
|
|
86
|
+
dropTyping();
|
|
87
|
+
}, [dropTyping]);
|
|
63
88
|
const call = useCallback(async (params, body, signal) => {
|
|
64
89
|
if (base === undefined)
|
|
65
90
|
throw new Error('No conversations endpoint');
|
|
@@ -86,7 +111,7 @@ export function useConversationsClient({ endpoint, fetch, activeId, onSelect, on
|
|
|
86
111
|
const reload = useCallback(() => list(), [list]);
|
|
87
112
|
// Another endpoint (another owner's list) starts empty; switching conversations only reloads.
|
|
88
113
|
// eslint-disable-next-line react-hooks/set-state-in-effect -- a new endpoint clears the old owner's list once, after commit
|
|
89
|
-
useEffect(() => { setItems(undefined); markOpened(undefined); forget(); }, [base, forget, markOpened]);
|
|
114
|
+
useEffect(() => { setItems(undefined); setFailedNew(undefined); markOpened(undefined); forget(); }, [base, forget, markOpened]);
|
|
90
115
|
useEffect(() => {
|
|
91
116
|
if (base === undefined)
|
|
92
117
|
return;
|
|
@@ -105,34 +130,48 @@ export function useConversationsClient({ endpoint, fetch, activeId, onSelect, on
|
|
|
105
130
|
const open = async (name, body) => {
|
|
106
131
|
if (opening.current)
|
|
107
132
|
return;
|
|
133
|
+
const gaveUp = name === 'create' ? failNew : dropTyping;
|
|
108
134
|
const run = {};
|
|
109
135
|
opening.current = run;
|
|
110
136
|
setCreating(true);
|
|
111
137
|
const timer = setTimeout(() => { if (opening.current === run) {
|
|
112
138
|
forget();
|
|
113
|
-
|
|
139
|
+
gaveUp();
|
|
114
140
|
} }, latest.current.openTimeoutMs);
|
|
115
141
|
let selected = false;
|
|
116
142
|
try {
|
|
117
143
|
const { conversationId } = await op(name, body);
|
|
118
|
-
|
|
144
|
+
// The conversation exists now: a host refresh that fails does not undo that.
|
|
145
|
+
await changed().catch(() => { });
|
|
119
146
|
if (conversationId !== undefined && opening.current === run) {
|
|
120
147
|
selected = true;
|
|
121
148
|
markOpened(String(conversationId));
|
|
122
149
|
latest.current.onSelect(String(conversationId));
|
|
123
150
|
}
|
|
151
|
+
// Nobody waits for it any more, so it would stay in the list as an empty row. Only that row goes: not one the person has had open here, not one with a message (another tab), not on another owner's list.
|
|
152
|
+
else if (name === 'create' && conversationId !== undefined) {
|
|
153
|
+
const unused = () => mounted.current && latest.current.base === base && !visited.current.has(String(conversationId));
|
|
154
|
+
const row = unused() ? (await call({})).conversations?.find(served => String(served.id) === String(conversationId)) : undefined;
|
|
155
|
+
if (row?.lastMessage === null && unused()) {
|
|
156
|
+
await op('delete', { conversationId });
|
|
157
|
+
await changed();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
124
160
|
}
|
|
125
161
|
finally {
|
|
126
162
|
clearTimeout(timer);
|
|
127
163
|
if (opening.current === run) {
|
|
128
164
|
forget();
|
|
129
165
|
if (!selected)
|
|
130
|
-
|
|
166
|
+
gaveUp();
|
|
131
167
|
}
|
|
132
168
|
}
|
|
133
169
|
};
|
|
134
170
|
return {
|
|
135
|
-
items, activeId, creating,
|
|
171
|
+
items, activeId, creating, reload,
|
|
172
|
+
opened: composing ? undefined : opened,
|
|
173
|
+
newFailed: failedNew && failedNew.at === activeId ? { typed: failedNew.typed } : undefined,
|
|
174
|
+
newFailedSeen: () => setFailedNew(undefined),
|
|
136
175
|
openedSeen: () => {
|
|
137
176
|
const typed = typing.current?.field?.value ?? '';
|
|
138
177
|
typing.current?.field?.remove();
|
|
@@ -140,11 +179,12 @@ export function useConversationsClient({ endpoint, fetch, activeId, onSelect, on
|
|
|
140
179
|
markOpened(undefined);
|
|
141
180
|
return typed;
|
|
142
181
|
},
|
|
143
|
-
onSelect: (id) => { forget(); markOpened(undefined); latest.current.onSelect(id); },
|
|
182
|
+
onSelect: (id) => { forget(); setFailedNew(undefined); markOpened(undefined); latest.current.onSelect(id); },
|
|
144
183
|
onNew: () => {
|
|
145
184
|
if (!opening.current) {
|
|
185
|
+
setFailedNew(undefined);
|
|
146
186
|
markOpened(undefined);
|
|
147
|
-
typing.current = holdTyping();
|
|
187
|
+
typing.current = holdTyping(setComposing);
|
|
148
188
|
}
|
|
149
189
|
else
|
|
150
190
|
typing.current?.field?.focus();
|
|
@@ -161,7 +201,7 @@ export function useConversationsClient({ endpoint, fetch, activeId, onSelect, on
|
|
|
161
201
|
await open('fork', { conversationId: from, at });
|
|
162
202
|
},
|
|
163
203
|
};
|
|
164
|
-
}, [items, activeId, creating, opened, call, reload, forget, markOpened, dropTyping]);
|
|
204
|
+
}, [items, activeId, creating, opened, composing, failedNew, base, call, reload, forget, markOpened, dropTyping, failNew]);
|
|
165
205
|
}
|
|
166
206
|
/** Compatibility spelling retained for one release. */
|
|
167
207
|
export const useConversations = useConversationsClient;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hachej/boring-ui-kit",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.59",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -154,7 +154,7 @@
|
|
|
154
154
|
}
|
|
155
155
|
},
|
|
156
156
|
"peerDependencies": {
|
|
157
|
-
"@hachej/boring-files": "^0.4.
|
|
157
|
+
"@hachej/boring-files": "^0.4.59",
|
|
158
158
|
"@earendil-works/pi-durable": "~1.4.0",
|
|
159
159
|
"react": "^19.0.0",
|
|
160
160
|
"react-dom": "^19.0.0",
|
|
@@ -173,13 +173,13 @@
|
|
|
173
173
|
"@tiptap/extension-floating-menu": "^3.31.4",
|
|
174
174
|
"@earendil-works/chord": "~1.4.0",
|
|
175
175
|
"@earendil-works/pi-ai": "~1.4.0",
|
|
176
|
-
"tldraw": "^5.
|
|
177
|
-
"@tldraw/editor": "^5.
|
|
178
|
-
"@tldraw/store": "^5.
|
|
176
|
+
"tldraw": "^5.6.0",
|
|
177
|
+
"@tldraw/editor": "^5.6.0",
|
|
178
|
+
"@tldraw/store": "^5.6.0",
|
|
179
179
|
"@json-render/core": "^0.21.0",
|
|
180
180
|
"@json-render/react": "^0.21.0",
|
|
181
181
|
"zod": "^4.6.5",
|
|
182
|
-
"@tldraw/tlschema": "^5.
|
|
182
|
+
"@tldraw/tlschema": "^5.6.0",
|
|
183
183
|
"lucide-react": "^1.51.0",
|
|
184
184
|
"@base-ui/react": "1.9.0"
|
|
185
185
|
},
|
package/r/_shared.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "_shared",
|
|
4
|
+
"title": "Kit icons",
|
|
5
|
+
"description": "One fixed semantic icon-symbol map for all kit blocks and bundled primitives. Real Phosphor defaults; hosts may replace this shared module or use existing icon props, independently of theme tokens.",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"@phosphor-icons/react@2.1.10",
|
|
8
|
+
"react@^19.0.0"
|
|
9
|
+
],
|
|
10
|
+
"files": [
|
|
11
|
+
{
|
|
12
|
+
"path": "registry/_shared/icons.ts",
|
|
13
|
+
"content": "'use client';\n\n// Fixed semantic symbols for registry blocks and bundled shadcn primitives. Hosts may swap this module\n// or use existing icon props; theme tokens and DESIGN.md alone do not select an icon library.\nimport { createElement, forwardRef } from 'react';\nimport { CircleIcon as PhosphorCircle } from '@phosphor-icons/react';\nimport type { IconProps } from '@phosphor-icons/react';\n\nexport {\n WarningCircleIcon as AlertCircleIcon,\n ArchiveIcon,\n TrayArrowUpIcon as ArchiveRestoreIcon,\n ArrowDownIcon,\n ArrowLeftIcon,\n ArrowUpIcon,\n ArrowUpRightIcon,\n BellIcon,\n CubeIcon as BoxIcon,\n BrainIcon,\n CameraIcon,\n CheckIcon,\n CaretDownIcon as ChevronDownIcon,\n CaretLeftIcon as ChevronLeftIcon,\n CaretRightIcon as ChevronRightIcon,\n CaretUpIcon as ChevronUpIcon,\n CaretDoubleLeftIcon as ChevronsLeftIcon,\n CaretDoubleRightIcon as ChevronsRightIcon,\n CaretUpDownIcon as ChevronsUpDownIcon,\n WarningCircleIcon as CircleAlertIcon,\n CheckCircleIcon as CircleCheckIcon,\n ProhibitIcon as CircleSlashIcon,\n ClockIcon,\n CodeIcon as CodeXmlIcon,\n CompassIcon,\n CopyIcon,\n ArrowElbowDownRightIcon as CornerDownRightIcon,\n DownloadSimpleIcon as DownloadIcon,\n DotsThreeIcon as EllipsisIcon,\n ArrowSquareOutIcon as ExternalLinkIcon,\n EyeIcon,\n FileAudioIcon,\n FileCodeIcon,\n FileIcon,\n FileXlsIcon as FileSpreadsheetIcon,\n FileTextIcon,\n FolderIcon,\n GitForkIcon,\n GlobeIcon,\n DotsSixIcon as GripIcon,\n DotsSixVerticalIcon as GripVerticalIcon,\n HammerIcon,\n HandIcon,\n ClockCounterClockwiseIcon as HistoryIcon,\n ImageIcon,\n ImageBrokenIcon as ImageOffIcon,\n TrayIcon as InboxIcon,\n InfoIcon,\n SquaresFourIcon as LayoutDashboardIcon,\n LayoutIcon as LayoutTemplateIcon,\n BooksIcon as LibraryIcon,\n QueueIcon as ListEndIcon,\n ListIcon,\n ListChecksIcon as ListTodoIcon,\n CircleNotchIcon as Loader2Icon,\n CircleNotchIcon as LoaderCircleIcon,\n CrosshairIcon as LocateFixedIcon,\n SignOutIcon as LogOutIcon,\n ArrowsOutSimpleIcon as Maximize2Icon,\n ArrowsOutIcon as MaximizeIcon,\n ListIcon as MenuIcon,\n ChatCircleDotsIcon as MessageCircleQuestionIcon,\n ChatIcon as MessageSquareIcon,\n ChatDotsIcon as MessageSquarePlusIcon,\n ChatTextIcon as MessageSquareTextIcon,\n ChatsIcon as MessagesSquareIcon,\n MicrophoneIcon as MicIcon,\n ArrowsInSimpleIcon as Minimize2Icon,\n MinusIcon,\n MoonIcon,\n DotsThreeIcon as MoreHorizontalIcon,\n CursorIcon as MousePointer2Icon,\n XCircleIcon as OctagonXIcon,\n PaletteIcon,\n SquareSplitVerticalIcon as PanelBottomIcon,\n SidebarSimpleIcon as PanelLeftIcon,\n SquareSplitHorizontalIcon as PanelRightIcon,\n AppWindowIcon as PanelsTopLeftIcon,\n PaperclipIcon,\n PauseIcon,\n PenNibIcon as PenToolIcon,\n PencilIcon,\n PencilLineIcon,\n PictureInPictureIcon as PictureInPicture2Icon,\n PushPinIcon as PinIcon,\n PlayIcon,\n PlusIcon,\n PresentationIcon,\n ArrowUUpRightIcon as Redo2Icon,\n ArrowsClockwiseIcon as RefreshCwIcon,\n ArrowBendUpLeftIcon as ReplyIcon,\n ArrowCounterClockwiseIcon as RotateCcwIcon,\n PathIcon as RouteIcon,\n RowsIcon as Rows3Icon,\n FloppyDiskIcon as SaveIcon,\n ScanIcon,\n MagnifyingGlassIcon as SearchCheckIcon,\n MagnifyingGlassIcon as SearchIcon,\n PaperPlaneRightIcon as SendIcon,\n GearIcon as SettingsIcon,\n ShapesIcon,\n ShareNetworkIcon as Share2Icon,\n ShieldWarningIcon as ShieldAlertIcon,\n LineSegmentIcon as SlashIcon,\n SparkleIcon,\n SparkleIcon as SparklesIcon,\n SquareIcon,\n NotePencilIcon as SquarePenIcon,\n NoteIcon as StickyNoteIcon,\n ThumbsDownIcon,\n ThumbsUpIcon,\n TrashIcon as Trash2Icon,\n WarningIcon as TriangleAlertIcon,\n TextTIcon as TypeIcon,\n ArrowUUpLeftIcon as Undo2Icon,\n UserIcon,\n UserMinusIcon,\n FlowArrowIcon as WorkflowIcon,\n WrenchIcon,\n XIcon,\n MagnifyingGlassPlusIcon as ZoomInIcon,\n MagnifyingGlassMinusIcon as ZoomOutIcon,\n ArrowDownIcon as ArrowDown,\n ArrowUpIcon as ArrowUp,\n CaretRightIcon as ChevronRight,\n TrayIcon as Inbox,\n MinusIcon as Minus,\n SquareSplitVerticalIcon as PanelBottom,\n SquareSplitHorizontalIcon as PanelRight,\n SparkleIcon as Sparkles,\n} from '@phosphor-icons/react';\n\n// Every kit CircleIcon is a filled status/recording dot. Phosphor's regular circle is a\n// path outline, so SVG fill/strokeWidth (used by existing callers) cannot fill its centre.\n// Keep the native SVG props and ref, while allowing an explicit native weight override.\nexport const CircleIcon = forwardRef<SVGSVGElement, IconProps>((props, ref) =>\n createElement(PhosphorCircle, { weight: 'fill', ...props, ref }));\n",
|
|
14
|
+
"type": "registry:lib",
|
|
15
|
+
"target": "components/_shared/icons.ts"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"type": "registry:lib"
|
|
19
|
+
}
|
package/r/agent-target.json
CHANGED
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
"title": "Point and dictate on app data",
|
|
5
5
|
"description": "Tap explicitly marked JSON app context, dictate once and show the same app agent’s native reply without opening chat. Host-controlled presentation; no feedback report or second controller.",
|
|
6
6
|
"dependencies": [
|
|
7
|
-
"@hachej/boring-ui-kit@^0.4.
|
|
8
|
-
"@hachej/boring-feedback@^0.4.
|
|
7
|
+
"@hachej/boring-ui-kit@^0.4.59",
|
|
8
|
+
"@hachej/boring-feedback@^0.4.59",
|
|
9
9
|
"@earendil-works/pi-durable@~1.4.0",
|
|
10
10
|
"react@^19.0.0",
|
|
11
11
|
"react-dom@^19.0.0"
|
|
12
12
|
],
|
|
13
13
|
"registryDependencies": [
|
|
14
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
15
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
14
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.59/r/app-shell.json",
|
|
15
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.59/r/pi-chat.json"
|
|
16
16
|
],
|
|
17
17
|
"files": [
|
|
18
18
|
{
|