@hachej/boring-ui-kit 0.4.5 → 0.4.6

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/README.md CHANGED
@@ -12,6 +12,8 @@ Lost or malformed acknowledgements stay unknown. `actions.reconcile()` uses reta
12
12
 
13
13
  Historical scaffold tests install/typecheck packed headless UI and UI+files in clean consumers without Pi or an agent package. Candidate runtime installation qualification is recorded separately in the [implementation checkpoint](../../docs/implementation/PARTIAL.md). Import interfaces with `import type`.
14
14
 
15
+ For read-only content without a resource client or controller, select the lightweight [`artifact-viewer`](#read-only-artifactviewer) entry. It needs only React and Marked, not files, Pi, Tiptap, canvas or pdfjs.
16
+
15
17
  See [scaffold guide](../../docs/contracts/SCAFFOLD.md) and [audit](../../docs/contracts/ABSTRACTION-REVIEW.md).
16
18
 
17
19
  ## Exact proposals and commands
@@ -210,6 +212,24 @@ Mounted `tools.propose` accepts the same complete edits as the headless command
210
212
 
211
213
  `test/compatibility/canvas-mounted-native.test.mjs` composes selection and proposals with real native ToolTasks. `npm run canvas:journey:commands` requires `CHROMIUM` and permitted loopback sockets; it checks real desktop/phone viewport geometry with fictional assets. Production fonts, licensing, egress and CSS isolation remain separate qualifications.
212
214
 
215
+ ## Read-only ArtifactViewer
216
+
217
+ ```tsx
218
+ import { ArtifactViewer } from '@hachej/boring-ui-kit/artifact-viewer';
219
+
220
+ <ArtifactViewer name="fictional-guide.md" mimeType="text/markdown" content="# Fictional guide" />;
221
+ ```
222
+
223
+ `ArtifactViewerProps` accepts `name`, `mimeType`, optional `content: string | Uint8Array`, `url`, `className` and `renderPdf`. Install the existing optional React and Marked peers when selecting this entry. It has no ResourceClient, controller, editing, syntax highlighter or agent integration. The host supplies authorized content and owns URL access policy and layout; mounting grants no file or execution authority.
224
+
225
+ Normalized MIME selects Markdown, HTML, images and PDF, with filename fallback. Other text/code is an escaped `<pre>` without highlighting. Byte text requires valid UTF-8 without NUL; leading U+FEFF is preserved. Binary audio/video does not become a text preview. Missing or unreadable content displays “Preview unavailable.” Replace byte arrays when changing content rather than mutating them in place.
226
+
227
+ Markdown uses a private Marked parser, then the same passive HTML allowlist as `HtmlViewer`. Both HTML types render in an empty-sandbox, no-referrer iframe with the fixed restrictive CSP. Links become spans; scripts, attributes, styles, images and external resources are omitted, including Markdown images. The [HTML preview limits](#optional-html-source-and-preview) apply; Markdown input is also limited to 262,144 UTF-16 units before parsing. There is no raw HTML fallback. URL-only Markdown, HTML and text are unavailable: the entry never fetches or embeds them unsanitized.
228
+
229
+ Images use `<img>` and PDFs use the browser's native sandboxed iframe. `content` takes precedence over `url`; content creates an owned Blob URL, revoked on replacement or unmount. Host URLs are borrowed and never revoked. Relative, HTTP(S), blob and matching image/PDF data URLs are supported; other URL schemes are refused. Supply `renderPdf: (props: ArtifactPdfProps) => ReactNode` to use a trusted host renderer instead, receiving the current `{ name, url }`. The entry does not load pdfjs. Browser-native PDF support and sandbox restrictions vary by browser; it provides no page controls or PDF text extraction.
230
+
231
+ `test/packages/ui-artifact-viewer.test.mjs` renders fictional fixtures through the built public entry and checks its browser import graph. `npm run test:artifact-viewer-consumer` installs the kit tarball with only React/Marked and test tooling, checks strict declarations, repeats those DOM tests and bundles without files, controllers, Tiptap, canvas, pdfjs or agent code. DOM structure tests do not qualify browser CSP enforcement, parser inertness, egress or native PDF rendering.
232
+
213
233
  ## Optional HTML source and preview
214
234
 
215
235
  Both `MarkdownEditor` and `HtmlViewer` accept optional `components` bindings for host buttons, inputs, textareas and the selection bubble. Installed registry recipes supply the consumer's unchanged shadcn components. Bindings forward refs and native input events. Omitting them retains unstyled native HTML for direct package consumers; it is not installed-block design qualification. See the [registry theming contract](../../registry/README.md#styling).
@@ -0,0 +1,18 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface ArtifactPdfProps {
3
+ readonly name: string;
4
+ readonly url: string;
5
+ }
6
+ export interface ArtifactViewerProps {
7
+ readonly name: string;
8
+ readonly mimeType: string;
9
+ /** Content takes precedence over a URL. Byte content must be replaced, not mutated in place. */
10
+ readonly content?: string | Uint8Array;
11
+ /** Borrowed host URL; used only for images and PDF, never fetched for text previews. */
12
+ readonly url?: string;
13
+ readonly className?: string;
14
+ /** Trusted host presentation, for example a separately installed pdfjs renderer. */
15
+ readonly renderPdf?: (props: ArtifactPdfProps) => ReactNode;
16
+ }
17
+ export declare function ArtifactViewer(props: ArtifactViewerProps): import("react").JSX.Element;
18
+ //# sourceMappingURL=artifact-viewer.d.ts.map
@@ -0,0 +1,125 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useLayoutEffect, useRef, useState } from 'react';
4
+ import { Marked } from 'marked';
5
+ import { passiveHtml } from './passive-html.js';
6
+ const markdown = new Marked();
7
+ const imageTypes = { png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', webp: 'image/webp', svg: 'image/svg+xml', bmp: 'image/bmp', avif: 'image/avif' };
8
+ function artifactType(name, mimeType) {
9
+ const mime = mimeType.split(';')[0].trim().toLowerCase();
10
+ if (mime === 'text/markdown' || mime === 'text/x-markdown')
11
+ return { kind: 'markdown', mimeType: mime };
12
+ if (mime === 'text/html')
13
+ return { kind: 'html', mimeType: mime };
14
+ if (mime === 'application/pdf')
15
+ return { kind: 'pdf', mimeType: mime };
16
+ if (mime.startsWith('image/'))
17
+ return { kind: 'image', mimeType: mime };
18
+ if (/^(?:audio|video)\//.test(mime))
19
+ return undefined;
20
+ const basename = name.slice(name.lastIndexOf('/') + 1), dot = basename.lastIndexOf('.');
21
+ const extension = dot === -1 ? '' : basename.slice(dot + 1).toLowerCase();
22
+ if (extension === 'md' || extension === 'markdown')
23
+ return { kind: 'markdown', mimeType: 'text/markdown' };
24
+ if (extension === 'html' || extension === 'htm')
25
+ return { kind: 'html', mimeType: 'text/html' };
26
+ if (extension === 'pdf')
27
+ return { kind: 'pdf', mimeType: 'application/pdf' };
28
+ const imageType = Object.hasOwn(imageTypes, extension) ? imageTypes[extension] : undefined;
29
+ if (imageType)
30
+ return { kind: 'image', mimeType: imageType };
31
+ if (/^(?:mp4|webm|mov|m4v|ogg|ogv|mp3|wav)$/.test(extension))
32
+ return undefined;
33
+ return { kind: 'text', mimeType: mime };
34
+ }
35
+ function decodeText(content) {
36
+ if (content === undefined)
37
+ return undefined;
38
+ if (typeof content === 'string')
39
+ return content.includes('\0') ? undefined : content;
40
+ if (content.includes(0))
41
+ return undefined;
42
+ try {
43
+ return new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(content);
44
+ }
45
+ catch {
46
+ return undefined;
47
+ }
48
+ }
49
+ function Unavailable() {
50
+ return _jsx("p", { role: "status", children: "Preview unavailable." });
51
+ }
52
+ function PassiveArtifact({ name, text, kind }) {
53
+ const container = useRef(null);
54
+ const [ready, setReady] = useState(false);
55
+ useLayoutEffect(() => {
56
+ const host = container.current;
57
+ if (!host)
58
+ return;
59
+ host.replaceChildren();
60
+ try {
61
+ if (text.length > 262_144)
62
+ throw new RangeError('Artifact preview source exceeds its limit');
63
+ const source = passiveHtml(kind === 'markdown' ? markdown.parse(text, { async: false }) : text, host.ownerDocument);
64
+ const frame = host.ownerDocument.createElement('iframe');
65
+ frame.setAttribute('sandbox', '');
66
+ frame.referrerPolicy = 'no-referrer';
67
+ frame.title = name;
68
+ frame.srcdoc = source;
69
+ host.replaceChildren(frame);
70
+ setReady(true);
71
+ }
72
+ catch {
73
+ host.replaceChildren();
74
+ setReady(false);
75
+ }
76
+ return () => { host.replaceChildren(); };
77
+ }, [name, text, kind]);
78
+ return _jsxs(_Fragment, { children: [!ready && _jsx(Unavailable, {}), _jsx("div", { ref: container })] });
79
+ }
80
+ function mediaUrl(url, kind) {
81
+ if (!url)
82
+ return undefined;
83
+ try {
84
+ const protocol = new URL(url, 'https://artifact.invalid/').protocol;
85
+ if (['https:', 'http:', 'blob:'].includes(protocol))
86
+ return url;
87
+ if (protocol === 'data:' && (kind === 'image' ? /^data:image\//i : /^data:application\/pdf(?:;|,)/i).test(url))
88
+ return url;
89
+ }
90
+ catch { /* A malformed host URL has no preview. */ }
91
+ return undefined;
92
+ }
93
+ function MediaArtifact({ name, kind, mimeType, content, url, renderPdf }) {
94
+ const [made, setMade] = useState();
95
+ useLayoutEffect(() => {
96
+ if (content === undefined) {
97
+ setMade(undefined);
98
+ return;
99
+ }
100
+ try {
101
+ const blob = new Blob([typeof content === 'string' ? content : new Uint8Array(content)], { type: mimeType });
102
+ const objectUrl = URL.createObjectURL(blob);
103
+ setMade({ content, mimeType, url: objectUrl });
104
+ return () => { URL.revokeObjectURL(objectUrl); };
105
+ }
106
+ catch {
107
+ setMade(undefined);
108
+ }
109
+ }, [content, mimeType]);
110
+ const selectedUrl = content === undefined ? mediaUrl(url, kind) : made?.content === content && made.mimeType === mimeType ? made.url : undefined;
111
+ if (!selectedUrl)
112
+ return _jsx(Unavailable, {});
113
+ if (kind === 'image')
114
+ return _jsx("img", { src: selectedUrl, alt: name, referrerPolicy: "no-referrer" });
115
+ return renderPdf ? renderPdf({ name, url: selectedUrl }) : _jsx("iframe", { src: selectedUrl, title: name, sandbox: "", referrerPolicy: "no-referrer" });
116
+ }
117
+ export function ArtifactViewer(props) {
118
+ const type = artifactType(props.name, props.mimeType);
119
+ const text = type && type.kind !== 'image' && type.kind !== 'pdf' ? decodeText(props.content) : undefined;
120
+ return _jsx("section", { "data-boring": "artifact-viewer", className: props.className, "aria-label": props.name, children: !type ? _jsx(Unavailable, {}) : type.kind === 'image' || type.kind === 'pdf'
121
+ ? _jsx(MediaArtifact, { ...props, kind: type.kind, mimeType: type.mimeType })
122
+ : text === undefined ? _jsx(Unavailable, {}) : type.kind === 'text'
123
+ ? _jsx("pre", { children: text })
124
+ : _jsx(PassiveArtifact, { name: props.name, text: text, kind: type.kind }) });
125
+ }
@@ -1,2 +1,2 @@
1
- export declare function passiveHtml(source: string, document: Document): string;
1
+ export { passiveHtml } from './passive-html.js';
2
2
  //# sourceMappingURL=html-preview.d.ts.map
@@ -1,47 +1 @@
1
- const formatting = new Set(['p', 'div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre', 'code', 'strong', 'em', 'b', 'i', 'u', 's', 'small', 'sub', 'sup', 'kbd', 'samp', 'var', 'mark', 'ul', 'ol', 'li', 'dl', 'dt', 'dd', 'table', 'caption', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td', 'section', 'article', 'header', 'footer', 'main', 'aside', 'figure', 'figcaption', 'br', 'hr']);
2
- const htmlNamespace = 'http://www.w3.org/1999/xhtml';
3
- const prefix = '<!doctype html><html><head><meta http-equiv="Content-Security-Policy" content="default-src \'none\'; base-uri \'none\'; form-action \'none\'"><meta charset="utf-8"><meta name="referrer" content="no-referrer"><meta name="format-detection" content="telephone=no,email=no,address=no"><title>HTML preview</title></head><body>';
4
- export function passiveHtml(source, document) {
5
- if (source.length > 262_144)
6
- throw new RangeError('HTML preview source exceeds its limit');
7
- const template = document.createElement('template');
8
- template.innerHTML = source;
9
- let nodes = 0, length = prefix.length;
10
- const output = [prefix];
11
- function emit(text) {
12
- length += text.length;
13
- if (length > 1_048_576)
14
- throw new RangeError('HTML preview output exceeds its limit');
15
- output.push(text);
16
- }
17
- function visit(node, depth) {
18
- if (++nodes > 20_000 || depth > 128)
19
- throw new RangeError('HTML preview structure exceeds its limit');
20
- if (node.nodeType === 3) {
21
- emit((node.textContent ?? '').replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;'));
22
- return;
23
- }
24
- if (node.nodeType !== 1 || !('namespaceURI' in node) || node.namespaceURI !== htmlNamespace || !('localName' in node) || typeof node.localName !== 'string')
25
- return;
26
- if (node.localName === 'html' || node.localName === 'head' || node.localName === 'body') {
27
- for (let child = node.firstChild; child; child = child.nextSibling)
28
- visit(child, depth + 1);
29
- return;
30
- }
31
- const tag = node.localName === 'a' ? 'span' : node.localName;
32
- if (!formatting.has(tag))
33
- return;
34
- emit('<' + tag + '>');
35
- if (tag === 'br' || tag === 'hr')
36
- return;
37
- if (tag === 'pre')
38
- emit('\n');
39
- for (let child = node.firstChild; child; child = child.nextSibling)
40
- visit(child, depth + 1);
41
- emit('</' + tag + '>');
42
- }
43
- for (let child = template.content.firstChild; child; child = child.nextSibling)
44
- visit(child, 0);
45
- emit('</body></html>');
46
- return output.join('');
47
- }
1
+ export { passiveHtml } from './passive-html.js';
@@ -0,0 +1,2 @@
1
+ export declare function passiveHtml(source: string, document: Document): string;
2
+ //# sourceMappingURL=passive-html.d.ts.map
@@ -0,0 +1,47 @@
1
+ const formatting = new Set(['p', 'div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre', 'code', 'strong', 'em', 'b', 'i', 'u', 's', 'small', 'sub', 'sup', 'kbd', 'samp', 'var', 'mark', 'ul', 'ol', 'li', 'dl', 'dt', 'dd', 'table', 'caption', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td', 'section', 'article', 'header', 'footer', 'main', 'aside', 'figure', 'figcaption', 'br', 'hr']);
2
+ const htmlNamespace = 'http://www.w3.org/1999/xhtml';
3
+ const prefix = '<!doctype html><html><head><meta http-equiv="Content-Security-Policy" content="default-src \'none\'; base-uri \'none\'; form-action \'none\'"><meta charset="utf-8"><meta name="referrer" content="no-referrer"><meta name="format-detection" content="telephone=no,email=no,address=no"><title>HTML preview</title></head><body>';
4
+ export function passiveHtml(source, document) {
5
+ if (source.length > 262_144)
6
+ throw new RangeError('HTML preview source exceeds its limit');
7
+ const template = document.createElement('template');
8
+ template.innerHTML = source;
9
+ let nodes = 0, length = prefix.length;
10
+ const output = [prefix];
11
+ function emit(text) {
12
+ length += text.length;
13
+ if (length > 1_048_576)
14
+ throw new RangeError('HTML preview output exceeds its limit');
15
+ output.push(text);
16
+ }
17
+ function visit(node, depth) {
18
+ if (++nodes > 20_000 || depth > 128)
19
+ throw new RangeError('HTML preview structure exceeds its limit');
20
+ if (node.nodeType === 3) {
21
+ emit((node.textContent ?? '').replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;'));
22
+ return;
23
+ }
24
+ if (node.nodeType !== 1 || !('namespaceURI' in node) || node.namespaceURI !== htmlNamespace || !('localName' in node) || typeof node.localName !== 'string')
25
+ return;
26
+ if (node.localName === 'html' || node.localName === 'head' || node.localName === 'body') {
27
+ for (let child = node.firstChild; child; child = child.nextSibling)
28
+ visit(child, depth + 1);
29
+ return;
30
+ }
31
+ const tag = node.localName === 'a' ? 'span' : node.localName;
32
+ if (!formatting.has(tag))
33
+ return;
34
+ emit('<' + tag + '>');
35
+ if (tag === 'br' || tag === 'hr')
36
+ return;
37
+ if (tag === 'pre')
38
+ emit('\n');
39
+ for (let child = node.firstChild; child; child = child.nextSibling)
40
+ visit(child, depth + 1);
41
+ emit('</' + tag + '>');
42
+ }
43
+ for (let child = template.content.firstChild; child; child = child.nextSibling)
44
+ visit(child, 0);
45
+ emit('</body></html>');
46
+ return output.join('');
47
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hachej/boring-ui-kit",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "license": "MIT",
5
5
  "private": false,
6
6
  "type": "module",
@@ -83,6 +83,10 @@
83
83
  "types": "./dist/html-viewer.d.ts",
84
84
  "import": "./dist/html-viewer.js"
85
85
  },
86
+ "./artifact-viewer": {
87
+ "types": "./dist/artifact-viewer.d.ts",
88
+ "import": "./dist/artifact-viewer.js"
89
+ },
86
90
  "./experience": {
87
91
  "types": "./dist/experience.d.ts",
88
92
  "import": "./dist/experience.js"
@@ -130,7 +134,7 @@
130
134
  }
131
135
  },
132
136
  "peerDependencies": {
133
- "@hachej/boring-files": "^0.4.5",
137
+ "@hachej/boring-files": "^0.4.6",
134
138
  "@earendil-works/pi-durable": "~1.2.0",
135
139
  "react": "^19.0.0",
136
140
  "react-dom": "^19.0.0",
@@ -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.5",
8
- "@hachej/boring-feedback@^0.4.5",
7
+ "@hachej/boring-ui-kit@^0.4.6",
8
+ "@hachej/boring-feedback@^0.4.6",
9
9
  "@earendil-works/pi-durable@~1.2.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.5/r/app-shell.json",
15
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/pi-chat.json",
14
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/app-shell.json",
15
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/pi-chat.json",
16
16
  "button",
17
17
  "card",
18
18
  "message",
package/r/app-shell.json CHANGED
@@ -4,19 +4,19 @@
4
4
  "title": "App shell: layouts, list/detail and agent layer",
5
5
  "description": "AppShell with three layouts. B \"workspace\" puts a ListDetail on a screen (phone: the list, then the detail as a stack step with Back; wide: a 20rem list and the detail side by side, sidebar as an icon rail). C \"chat\" wraps the app's own AgentWorkspace (untouched, passed as the nav item's render; install the pi-app item yourself) in the same sidebar, account menu and header; it takes no shell agent. A \"screen\": one screen at a time, every screen kept mounted. On a phone bottom tabs (at most four; more give three plus a « Plus » sheet) and an avatar button that opens the account sheet; on a wide screen the shadcn sidebar (icon rail, NavUser). One header per screen through useScreenHeader (title, subtitle, back, up to two actions, the rest behind « … »). The account menu holds « Améliorer l'app » (the improve agent), the app's own entries and sign out. AppSheet is the one bottom sheet (one open at a time, Back closes it). It mounts the AgentLayer: one agent's PiChat above the app (phone: a drawer with peek, half and full snap points; wide: a right panel), the improve agent in the same layer, optional push-to-talk and point mode. Also useAgentContext and readAgentContext (the screen on show travels as `context` with each submit) and useBackLayer, the one Back manager. Built on explicitly pinned Base UI drawer and dropdown-menu recipes, plus the sidebar and avatar of the host's own shadcn style.",
6
6
  "dependencies": [
7
- "@hachej/boring-feedback@^0.4.5",
8
- "@hachej/boring-ui-kit@^0.4.5",
7
+ "@hachej/boring-feedback@^0.4.6",
8
+ "@hachej/boring-ui-kit@^0.4.6",
9
9
  "@base-ui/react@^1.9.0",
10
10
  "react@^19.0.0",
11
11
  "react-dom@^19.0.0",
12
12
  "lucide-react@^1.51.0"
13
13
  ],
14
14
  "registryDependencies": [
15
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/pi-chat.json",
16
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/dock.json",
17
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/feedback.json",
18
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/utils.json",
19
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/button.json",
15
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/pi-chat.json",
16
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/dock.json",
17
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/feedback.json",
18
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/utils.json",
19
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/button.json",
20
20
  "drawer",
21
21
  "sidebar",
22
22
  "avatar",
@@ -4,16 +4,16 @@
4
4
  "title": "Board viewer",
5
5
  "description": "A board@1 artifact kind for *.board.json. Lazy native tldraw projection of maquette screens, sandboxed HTML mock-ups, goto arrows and notes. Conditional board-file autosave; phone viewing without accidental frame edits. Hosts supply their resource client, asset URLs and license key.",
6
6
  "dependencies": [
7
- "@hachej/boring-ui-kit@^0.4.5",
8
- "@hachej/boring-files@^0.4.5",
7
+ "@hachej/boring-ui-kit@^0.4.6",
8
+ "@hachej/boring-files@^0.4.6",
9
9
  "react@^19.0.0",
10
10
  "lucide-react@1.51.0",
11
11
  "tldraw@5.5.2"
12
12
  ],
13
13
  "registryDependencies": [
14
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/maquette-viewer.json",
15
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/mockup-viewer.json",
16
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/pi-chat.json",
14
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/maquette-viewer.json",
15
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/mockup-viewer.json",
16
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/pi-chat.json",
17
17
  "button",
18
18
  "alert",
19
19
  "card",
package/r/button.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "lucide-react@^1.51.0"
10
10
  ],
11
11
  "registryDependencies": [
12
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/utils.json",
12
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/utils.json",
13
13
  "dropdown-menu",
14
14
  "button",
15
15
  "toggle",
@@ -8,7 +8,7 @@
8
8
  "lucide-react@^1.51.0"
9
9
  ],
10
10
  "registryDependencies": [
11
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/theme.json",
11
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/theme.json",
12
12
  "alert",
13
13
  "badge"
14
14
  ],
package/r/dock.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "react@^19.0.0"
8
8
  ],
9
9
  "registryDependencies": [
10
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/utils.json"
10
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/utils.json"
11
11
  ],
12
12
  "files": [
13
13
  {
package/r/documents.json CHANGED
@@ -4,20 +4,20 @@
4
4
  "title": "Documents: the Library, a file and its revisions",
5
5
  "description": "The workspace's Library as the list, the chosen file in its viewer and its retained revisions beside it (an older one opens read-only). DocumentSheet opens one document over the app for a host's « Voir la fiche », with an optional « Toutes les fiches ». Every read goes through one revisionProvider binding.",
6
6
  "dependencies": [
7
- "@hachej/boring-ui-kit@^0.4.5",
8
- "@hachej/boring-files@^0.4.5",
7
+ "@hachej/boring-ui-kit@^0.4.6",
8
+ "@hachej/boring-files@^0.4.6",
9
9
  "react@^19.0.0",
10
10
  "lucide-react@^1.51.0"
11
11
  ],
12
12
  "registryDependencies": [
13
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/theme.json",
14
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/utils.json",
15
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/button.json",
16
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/pi-chat.json",
17
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/pi-app.json",
18
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/app-shell.json",
19
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/file-tree.json",
20
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/viewers.json",
13
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/theme.json",
14
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/utils.json",
15
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/button.json",
16
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/pi-chat.json",
17
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/pi-app.json",
18
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/app-shell.json",
19
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/file-tree.json",
20
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/viewers.json",
21
21
  "drawer",
22
22
  "button",
23
23
  "scroll-area",
@@ -4,7 +4,7 @@
4
4
  "title": "Feedback preview",
5
5
  "description": "PreviewBanner: a live, reversible preview of a change in the page (Approve / Discard) over `@hachej/boring-feedback/preview`, whose subagent calls models through a host gateway. Separate from `feedback` so annotation-only apps do not install the model SDK; install `feedback` first (its styles cover the banner).",
6
6
  "dependencies": [
7
- "@hachej/boring-feedback@^0.4.5",
7
+ "@hachej/boring-feedback@^0.4.6",
8
8
  "@earendil-works/pi-ai@~1.2.0",
9
9
  "react@^19.0.0"
10
10
  ],
package/r/feedback.json CHANGED
@@ -4,16 +4,16 @@
4
4
  "title": "Feedback",
5
5
  "description": "Feedback from the chat composer: one Feedback button starts feedback mode on the application page (hover, click to pin a numbered note, hold ⌥ to use the app, optional voice), and one chip carries the notes to the next message. FeedbackBar, NoteBubble, FeedbackChip with its review, useComposerFeedback for the pi-chat composer, and PickerOverlay styles. Optional, not mounted by default: PointButton, AnnotateSheet, FeedbackList and FeedbackReport. Picking, masking, anchoring and placement stay in @hachej/boring-feedback/page and /ui, so restyling cannot weaken them.",
6
6
  "dependencies": [
7
- "@hachej/boring-feedback@^0.4.5",
8
- "@hachej/boring-ui-kit@^0.4.5",
9
- "@hachej/boring-files@^0.4.5",
7
+ "@hachej/boring-feedback@^0.4.6",
8
+ "@hachej/boring-ui-kit@^0.4.6",
9
+ "@hachej/boring-files@^0.4.6",
10
10
  "react@^19.0.0",
11
11
  "react-dom@^19.0.0",
12
12
  "lucide-react@^1.51.0"
13
13
  ],
14
14
  "registryDependencies": [
15
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/pi-chat.json",
16
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/utils.json",
15
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/pi-chat.json",
16
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/utils.json",
17
17
  "button",
18
18
  "toggle",
19
19
  "badge",
package/r/file-tree.json CHANGED
@@ -4,15 +4,15 @@
4
4
  "title": "File tree",
5
5
  "description": "Browse, search, upload and inspect retained versions through one revisionProvider binding. Uses the consumer's shadcn Button and dropdown-menu with shared version-history rows; Base UI style only.",
6
6
  "dependencies": [
7
- "@hachej/boring-ui-kit@^0.4.5",
8
- "@hachej/boring-files@^0.4.5",
7
+ "@hachej/boring-ui-kit@^0.4.6",
8
+ "@hachej/boring-files@^0.4.6",
9
9
  "react@^19.0.0",
10
10
  "lucide-react@^1.51.0"
11
11
  ],
12
12
  "registryDependencies": [
13
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/theme.json",
14
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/utils.json",
15
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/version-history.json",
13
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/theme.json",
14
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/utils.json",
15
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/version-history.json",
16
16
  "button",
17
17
  "dropdown-menu",
18
18
  "collapsible",
package/r/highlights.json CHANGED
@@ -4,13 +4,13 @@
4
4
  "title": "Anchored highlights",
5
5
  "description": "Exact Markdown highlights and comments in a conditional workspace sidecar.",
6
6
  "dependencies": [
7
- "@hachej/boring-ui-kit@^0.4.5",
8
- "@hachej/boring-files@^0.4.5",
7
+ "@hachej/boring-ui-kit@^0.4.6",
8
+ "@hachej/boring-files@^0.4.6",
9
9
  "react@^19.0.0",
10
10
  "react-dom@^19.0.0"
11
11
  ],
12
12
  "registryDependencies": [
13
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/markdown-editor.json",
13
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/markdown-editor.json",
14
14
  "button",
15
15
  "textarea",
16
16
  "label",
@@ -4,13 +4,13 @@
4
4
  "title": "HTML source and passive preview",
5
5
  "description": "Exact HTML source editing with a reconstructed passive preview and borrowed controller. Browser security qualification remains separate.",
6
6
  "dependencies": [
7
- "@hachej/boring-ui-kit@^0.4.5",
8
- "@hachej/boring-files@^0.4.5",
7
+ "@hachej/boring-ui-kit@^0.4.6",
8
+ "@hachej/boring-files@^0.4.6",
9
9
  "react@^19.0.0",
10
10
  "react-dom@^19.0.0"
11
11
  ],
12
12
  "registryDependencies": [
13
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/button.json"
13
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/button.json"
14
14
  ],
15
15
  "files": [
16
16
  {
@@ -8,8 +8,8 @@
8
8
  "lucide-react@^1.51.0"
9
9
  ],
10
10
  "registryDependencies": [
11
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/theme.json",
12
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/diagram-viewer.json",
11
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/theme.json",
12
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/diagram-viewer.json",
13
13
  "alert",
14
14
  "badge",
15
15
  "bubble",
@@ -4,8 +4,8 @@
4
4
  "title": "Markdown editor",
5
5
  "description": "A host-owned styled wrapper over the concrete borrowed Markdown controller.",
6
6
  "dependencies": [
7
- "@hachej/boring-ui-kit@^0.4.5",
8
- "@hachej/boring-files@^0.4.5",
7
+ "@hachej/boring-ui-kit@^0.4.6",
8
+ "@hachej/boring-files@^0.4.6",
9
9
  "react@^19.0.0",
10
10
  "react-dom@^19.0.0",
11
11
  "marked@^17.0.6",
@@ -23,7 +23,7 @@
23
23
  "@tiptap/extension-floating-menu@^3.31.4"
24
24
  ],
25
25
  "registryDependencies": [
26
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/button.json"
26
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/button.json"
27
27
  ],
28
28
  "files": [
29
29
  {
@@ -10,7 +10,7 @@
10
10
  "zod@^4.6.5"
11
11
  ],
12
12
  "registryDependencies": [
13
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/viewers.json",
13
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/viewers.json",
14
14
  "badge",
15
15
  "toggle",
16
16
  "tooltip"
package/r/pi-ambient.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "title": "Pi ambient agent",
5
5
  "description": "AmbientChat, a compact floating agent bar that grows into a chat window over an existing page, and AgentNotifications, toasts for finished, failed and waiting tasks (with the one guarded browser Notification helper). A block over the pi-chat item; its in-window artifact panel uses the pi-workspace layout.",
6
6
  "dependencies": [
7
- "@hachej/boring-ui-kit@^0.4.5",
7
+ "@hachej/boring-ui-kit@^0.4.6",
8
8
  "react@^19.0.0",
9
9
  "react-dom@^19.0.0",
10
10
  "@earendil-works/pi-durable@~1.2.0",
@@ -12,11 +12,11 @@
12
12
  "lucide-react@^1.51.0"
13
13
  ],
14
14
  "registryDependencies": [
15
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/theme.json",
16
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/pi-chat.json",
17
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/pi-workspace.json",
18
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/utils.json",
19
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.5/r/button.json",
15
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/theme.json",
16
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/pi-chat.json",
17
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/pi-workspace.json",
18
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/utils.json",
19
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.6/r/button.json",
20
20
  "message-scroller",
21
21
  "button",
22
22
  "toggle",