@hachej/boring-ui-kit 0.4.34 → 0.4.36
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/dist/chat-view.js +2 -1
- package/dist/file-tree-view.d.ts +1 -0
- package/dist/file-tree-view.js +10 -6
- package/dist/file-tree.d.ts +3 -3
- package/dist/file-tree.js +85 -6
- package/package.json +2 -2
- package/r/agent-target.json +4 -4
- package/r/app-shell.json +10 -10
- package/r/board-viewer.json +6 -6
- package/r/button.json +1 -1
- package/r/dock.json +1 -1
- package/r/documents.json +12 -12
- package/r/feedback-preview.json +1 -1
- package/r/feedback.json +6 -6
- package/r/file-tree.json +4 -4
- package/r/highlights.json +3 -3
- package/r/html-viewer.json +3 -3
- package/r/looks-viewer.json +2 -2
- package/r/maquette-viewer.json +1 -1
- package/r/markdown-editor.json +3 -3
- package/r/mockup-viewer.json +1 -1
- package/r/pi-ambient.json +9 -9
- package/r/pi-app-kinds.json +10 -10
- package/r/pi-app.json +10 -10
- package/r/pi-artifacts.json +2 -2
- package/r/pi-chat-approval.json +2 -2
- package/r/pi-chat-attachments.json +1 -1
- package/r/pi-chat-markdown.json +2 -2
- package/r/pi-chat-mentions.json +1 -1
- package/r/pi-chat-pickers.json +1 -1
- package/r/pi-chat-plugins.json +11 -11
- package/r/pi-chat-question.json +1 -1
- package/r/pi-chat-slash.json +2 -2
- package/r/pi-chat-subagent.json +1 -1
- package/r/pi-chat-voice.json +3 -3
- package/r/pi-chat.json +10 -10
- package/r/pi-workspace.json +3 -3
- package/r/placed-agent.json +4 -4
- package/r/pm-panel.json +16 -16
- package/r/provider-setup.json +1 -1
- package/r/registry.json +183 -183
- package/r/requests-board.json +1 -1
- package/r/shared-chat.json +7 -7
- package/r/task-board.json +3 -3
- package/r/task-list-viewer.json +2 -2
- package/r/task-mentions.json +4 -4
- package/r/version-history.json +2 -2
- package/r/viewer-code.json +2 -2
- package/r/viewer-frame.json +4 -4
- package/r/viewer-html.json +4 -4
- package/r/viewer-image.json +2 -2
- package/r/viewer-markdown.json +5 -5
- package/r/viewer-pdf.json +3 -3
- package/r/viewers.json +6 -6
- package/r/windows.json +1 -1
package/dist/chat-view.js
CHANGED
|
@@ -18,7 +18,8 @@ export function targetContent(text) {
|
|
|
18
18
|
const target = object(JSON.parse(json));
|
|
19
19
|
if (!target)
|
|
20
20
|
return;
|
|
21
|
-
|
|
21
|
+
// The chip never prints raw values (ids, slots): the target's own `label`, else the display keys it wraps its fields under.
|
|
22
|
+
const label = typeof target.label === 'string' && target.label.trim() ? target.label : Object.entries(target).flatMap(([key, value]) => value !== null && typeof value === 'object' ? [key] : []).join(' · ') || 'Target';
|
|
22
23
|
return { target, speech: text.slice(end + 11), label };
|
|
23
24
|
}
|
|
24
25
|
catch {
|
package/dist/file-tree-view.d.ts
CHANGED
package/dist/file-tree-view.js
CHANGED
|
@@ -60,6 +60,7 @@ export const defaultFileTreeLabels = {
|
|
|
60
60
|
delete: 'Delete',
|
|
61
61
|
deleteTitle: (name) => `Delete ${name}?`,
|
|
62
62
|
deleteDescription: 'It is removed from this workspace for everyone who uses it.',
|
|
63
|
+
deleteFolderDescription: 'Every file in this folder is removed from this workspace for everyone who uses it.',
|
|
63
64
|
cancel: 'Cancel',
|
|
64
65
|
changing: 'Saving…',
|
|
65
66
|
renamed: (to) => `Renamed to ${to}`,
|
|
@@ -125,6 +126,7 @@ export const frenchFileTreeLabels = {
|
|
|
125
126
|
delete: 'Supprimer',
|
|
126
127
|
deleteTitle: name => `Supprimer ${name} ?`,
|
|
127
128
|
deleteDescription: 'Il est retiré de cet espace pour tous ceux qui l’utilisent.',
|
|
129
|
+
deleteFolderDescription: 'Tous les fichiers de ce dossier sont retirés de cet espace pour tous ceux qui l’utilisent.',
|
|
128
130
|
cancel: 'Annuler',
|
|
129
131
|
changing: 'Enregistrement…',
|
|
130
132
|
renamed: to => `Renommé en ${to}`,
|
|
@@ -389,7 +391,7 @@ export function FileTreeView({ components, controller, onOpen, selectedPath, cla
|
|
|
389
391
|
node.focus();
|
|
390
392
|
};
|
|
391
393
|
/** Files only: a provider renames and deletes regular files, and a row busy uploading or changing waits. */
|
|
392
|
-
const mutable = (entry) =>
|
|
394
|
+
const mutable = (entry) => !temporaryPaths.has(entry.path) && uploadRows.get(entry.path)?.kind !== 'pending' && mutationRows.get(entry.path)?.state.kind !== 'pending';
|
|
393
395
|
/** A blob pointer's row shows the file it stands for, not the pointer's own name: it can be moved or deleted, not renamed in place. */
|
|
394
396
|
const renameable = (entry) => !entry.blob;
|
|
395
397
|
const keyboard = (event, entry) => {
|
|
@@ -470,7 +472,7 @@ export function FileTreeView({ components, controller, onOpen, selectedPath, cla
|
|
|
470
472
|
const Icon = kind.icon;
|
|
471
473
|
const mutation = mutationRows.get(entry.path);
|
|
472
474
|
const changeable = mutable(entry);
|
|
473
|
-
const draggable = changeable && can.rename;
|
|
475
|
+
const draggable = changeable && can.rename && !folder;
|
|
474
476
|
const button = _jsxs(Button, { type: "button", variant: selectedPath === entry.path ? 'secondary' : 'ghost', "data-active": selectedPath === entry.path, tabIndex: -1, title: entry.path, disabled: temporary, className: "file-tree-row-button", draggable: draggable || undefined, onClick: () => { focus(entry.path); if (!folder)
|
|
475
477
|
onOpen?.(entry.path); }, children: [folder ? _jsx(ChevronRight, { "aria-hidden": "true" }) : list && _jsx("span", { className: "file-tree-chevron-space", "aria-hidden": "true" }), _jsx(Icon, { "aria-hidden": "true" }), _jsxs("span", { className: "file-tree-copy", children: [_jsx("span", { className: "file-tree-name", children: name }), list ? folder ? childCount !== undefined && _jsx("span", { "data-file-meta": true, children: childCount }) : _jsxs("span", { "data-file-meta": true, children: [kind.label, " \u00B7 ", fileSize(blob?.size ?? entry.size, locale), parent && ` · ${parent}`] }) : parent && _jsx("span", { className: "file-tree-parent", "data-file-meta": true, children: parent })] })] });
|
|
476
478
|
const editing = renaming?.path === entry.path && can.rename;
|
|
@@ -503,14 +505,15 @@ export function FileTreeView({ components, controller, onOpen, selectedPath, cla
|
|
|
503
505
|
},
|
|
504
506
|
// Focus moved somewhere on purpose cancels; a closing menu that drops focus (no related target) does not.
|
|
505
507
|
onBlur: event => { if (event.relatedTarget)
|
|
506
|
-
setRenaming(current => current?.path === entry.path ? undefined : current); } })] }) : folder ? _jsx(CollapsibleTrigger, { render: button }) : button, (status?.kind === 'pending' || mutation?.state.kind === 'pending') && _jsx(LoaderCircle, { "data-upload-spinner": status ? '' : undefined, "data-mutation-spinner": mutation ? '' : undefined, className: "animate-spin motion-reduce:animate-none", "aria-hidden": "true" }), status?.kind === 'failed' && _jsx(Button, { type: "button", variant: "ghost", "data-upload-retry": true, "aria-label": status.retryLabel, onClick: status.retry, children: status.retryLabel }), mutation && mutationFailed && _jsx(Button, { type: "button", variant: "ghost", size: "icon", "data-mutation-dismiss": true, "aria-label": labels.dismiss(entry.path), title: labels.dismiss(entry.path), onClick: () => controller.dismiss(mutation.operationId), children: _jsx(X, { "aria-hidden": "true" }) }), !
|
|
508
|
+
setRenaming(current => current?.path === entry.path ? undefined : current); } })] }) : folder ? _jsx(CollapsibleTrigger, { render: button }) : button, (status?.kind === 'pending' || mutation?.state.kind === 'pending') && _jsx(LoaderCircle, { "data-upload-spinner": status ? '' : undefined, "data-mutation-spinner": mutation ? '' : undefined, className: "animate-spin motion-reduce:animate-none", "aria-hidden": "true" }), status?.kind === 'failed' && _jsx(Button, { type: "button", variant: "ghost", "data-upload-retry": true, "aria-label": status.retryLabel, onClick: status.retry, children: status.retryLabel }), mutation && mutationFailed && _jsx(Button, { type: "button", variant: "ghost", size: "icon", "data-mutation-dismiss": true, "aria-label": labels.dismiss(entry.path), title: labels.dismiss(entry.path), onClick: () => controller.dismiss(mutation.operationId), children: _jsx(X, { "aria-hidden": "true" }) }), !temporary && !status && !editing && mutation?.state.kind !== 'pending' && (!folder || changeable && (can.rename || can.delete)) && _jsx(FileActions, { Actions: Actions, controller: controller, entry: entry, labels: labels, onOpen: onOpen && !folder ? () => onOpen(entry.path) : undefined, onHistory: history && !folder ? () => setHistoryPath(entry.path) : undefined, onError: setUploadError, onRename: changeable && can.rename && renameable(entry) ? () => startRename(entry.path) : undefined, onMove: changeable && can.rename && Dialog ? () => ask('move', entry.path) : undefined, onDelete: changeable && can.delete ? () => ask('delete', entry.path) : undefined })] }), status?.kind === 'failed' && _jsx("span", { "data-upload-error": true, children: status.reason }), editing && renaming?.error && _jsx("span", { "data-mutation-error": true, role: "alert", children: renaming.error }), mutation && mutationFailed && _jsx("span", { "data-mutation-error": true, children: mutationReason(mutation) }), folder && !state.query && _jsx(CollapsibleContent, { render: _jsx(SidebarMenuSub, { role: "group" }), children: renderEntries(entry.path, level + 1) })] }, entry.path);
|
|
507
509
|
return folder ? _jsx(CollapsibleRoot, { render: item, open: state.expanded.has(entry.path), onOpenChange: () => { void controller.toggle(entry.path); } }, entry.path) : item;
|
|
508
510
|
};
|
|
509
511
|
const visibleListing = state.query ? state.search : state.directories.get('');
|
|
510
512
|
const paginated = visibleListing?.kind === 'ready' && visibleListing.cursor !== undefined;
|
|
511
513
|
const rootDrop = dropHandlers('');
|
|
512
514
|
const dialogPath = asking?.path ?? '';
|
|
513
|
-
const
|
|
515
|
+
const dialogIsFolder = [...state.directories.values(), state.search].some(listing => listing.entries.some(entry => entry.path === dialogPath && entry.kind === 'directory'));
|
|
516
|
+
const folders = asking?.kind === 'move' ? [...new Set(['', ...[...state.directories.values(), state.search].flatMap(listing => listing.entries.filter(entry => entry.kind === 'directory').map(entry => entry.path))])].filter(folder => folder !== dialogPath && !folder.startsWith(`${dialogPath}/`)).sort() : [];
|
|
514
517
|
return _jsxs("section", { "data-boring": "file-tree", "data-variant": variant, "data-dropping": dropping || undefined, "data-drop-target": dropTarget === '' ? 'true' : undefined, className: className, "aria-label": labels.region, onDragEnter: event => { rootDrop.onDragEnter?.(event); if (upload && event.dataTransfer.types.includes('Files')) {
|
|
515
518
|
event.preventDefault();
|
|
516
519
|
dragDepth.current++;
|
|
@@ -535,7 +538,7 @@ export function FileTreeView({ components, controller, onOpen, selectedPath, cla
|
|
|
535
538
|
const percent = item.progress && item.progress.total > 0 ? Math.min(100, Math.round(item.progress.loaded / item.progress.total * 100)) : null;
|
|
536
539
|
return _jsxs("li", { "data-blob-upload": item.name, "data-progress": percent ?? undefined, className: "flex items-center gap-2", children: [_jsx("span", { className: "min-w-0 flex-1 truncate", children: item.name }), Progress ? _jsx("span", { className: "w-24 shrink-0", children: _jsx(Progress, { value: percent, "aria-label": labels.uploadProgress(item.name) }) }) : _jsx("span", { role: "status", children: percent === null ? labels.uploading : `${percent}%` }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", "data-blob-upload-cancel": true, "aria-label": labels.cancelUpload(item.name), title: labels.cancelUpload(item.name), onClick: () => item.abort.abort(), children: _jsx(X, { "aria-hidden": "true" }) })] }, key);
|
|
537
540
|
}) }), _jsxs(SidebarMenu, { ref: tree, role: "tree", "aria-label": labels.tree, className: "file-tree-menu", children: [state.query ? _jsxs(_Fragment, { children: [state.search.entries.map(entry => renderEntry(entry, 1)), listingStatus(state.search, '', true)] }) : renderEntries('', 1), temporaryEntries.map(entry => renderEntry(entry, 1))] }), _jsxs("ul", { "aria-label": labels.uploads, "aria-live": "polite", "aria-relevant": "additions text", className: "file-tree-announcements", children: [state.uploads.map(upload => _jsxs("li", { "data-operation": upload.operationId, children: [upload.path, ": ", upload.state.kind === 'pending' ? labels.uploading : upload.state.result.kind === 'committed' ? labels.saved : upload.state.result.kind === 'partial' ? labels.unconfirmed : upload.state.result.reason] }, upload.operationId)), [...localFailures].map(([path, failure]) => _jsxs("li", { children: [path, ": ", failure.reason] }, path)), state.mutations.map(item => _jsxs("li", { "data-operation": item.operationId, children: [item.path, ": ", mutationReason(item)] }, item.operationId))] }), Dialog && _jsx(Dialog, { open: asking !== undefined, onOpenChange: open => { if (!open)
|
|
538
|
-
closeAsking(); }, title: asking?.kind === 'move' ? labels.moveTitle(baseName(dialogPath)) : labels.deleteTitle(baseName(dialogPath)), description: asking?.kind === 'move' ? labels.moveDescription : labels.deleteDescription, footer: asking?.kind === 'delete' ? _jsxs(_Fragment, { children: [_jsx(Button, { type: "button", variant: "outline", "data-confirm": "cancel", onClick: closeAsking, children: labels.cancel }), _jsxs(Button, { type: "button", variant: "destructive", "data-confirm": "delete", onClick: () => {
|
|
541
|
+
closeAsking(); }, title: asking?.kind === 'move' ? labels.moveTitle(baseName(dialogPath)) : labels.deleteTitle(baseName(dialogPath)), description: asking?.kind === 'move' ? labels.moveDescription : dialogIsFolder ? labels.deleteFolderDescription : labels.deleteDescription, footer: asking?.kind === 'delete' ? _jsxs(_Fragment, { children: [_jsx(Button, { type: "button", variant: "outline", "data-confirm": "cancel", onClick: closeAsking, children: labels.cancel }), _jsxs(Button, { type: "button", variant: "destructive", "data-confirm": "delete", onClick: () => {
|
|
539
542
|
const path = dialogPath;
|
|
540
543
|
setAsking(undefined);
|
|
541
544
|
void controller.remove(path).then(report).catch(refused(path));
|
|
@@ -593,7 +596,8 @@ function FileActions({ Actions, controller, entry, labels, onOpen, onHistory, on
|
|
|
593
596
|
items.push({ id: 'open', label: labels.open, icon: _jsx(FolderOpen, { "aria-hidden": "true" }), onSelect: onOpen });
|
|
594
597
|
if (onHistory)
|
|
595
598
|
items.push({ id: 'history', label: labels.history, icon: _jsx(History, { "aria-hidden": "true" }), onSelect: onHistory });
|
|
596
|
-
|
|
599
|
+
if (entry.kind === 'file')
|
|
600
|
+
items.push({ id: 'download', label: labels.download, icon: _jsx(Download, { "aria-hidden": "true" }), onSelect: () => { void download(); } });
|
|
597
601
|
if (onRename)
|
|
598
602
|
items.push({ id: 'rename', label: labels.rename, icon: _jsx(Pencil, { "aria-hidden": "true" }), onSelect: onRename, ownsFocus: true });
|
|
599
603
|
if (onMove)
|
package/dist/file-tree.d.ts
CHANGED
|
@@ -74,11 +74,11 @@ export interface FileTreeController {
|
|
|
74
74
|
};
|
|
75
75
|
/** Learn the revision of a file as its row shows it (on opening its menu, renaming, dragging): a later rename or delete is conditional on it. */
|
|
76
76
|
readonly prepare: (path: string, signal?: AbortSignal) => Promise<string | undefined>;
|
|
77
|
-
/** Rename a file through the client's conditional rename. `to` is a full path; its folder must exist. */
|
|
77
|
+
/** Rename a file through the client's conditional rename. `to` is a full path; its folder must exist. A listed folder renames file by file. */
|
|
78
78
|
readonly rename: (path: string, to: string) => Promise<FileMutation>;
|
|
79
|
-
/** Move a file into `folder` (`''` is the workspace root): a rename that keeps its name. */
|
|
79
|
+
/** Move a file or a listed folder into `folder` (`''` is the workspace root): a rename that keeps its name. */
|
|
80
80
|
readonly move: (path: string, folder: string) => Promise<FileMutation>;
|
|
81
|
-
/** Delete a file through the client's conditional delete. */
|
|
81
|
+
/** Delete a file through the client's conditional delete. A listed folder deletes every file in it. */
|
|
82
82
|
readonly remove: (path: string) => Promise<FileMutation>;
|
|
83
83
|
/** Forget a settled rename or delete (its row's message). */
|
|
84
84
|
readonly dismiss: (operationId: string) => void;
|
package/dist/file-tree.js
CHANGED
|
@@ -138,7 +138,7 @@ export function createFileTreeController({ revisionProvider }) {
|
|
|
138
138
|
void load('', false, true);
|
|
139
139
|
return settled;
|
|
140
140
|
}
|
|
141
|
-
async function mutate(kind, path, to) {
|
|
141
|
+
async function mutate(kind, path, to, prune = false) {
|
|
142
142
|
active();
|
|
143
143
|
if (kind === 'rename' ? !revisionProvider.rename : !revisionProvider.delete)
|
|
144
144
|
throw new Error(`This workspace cannot ${kind} files`);
|
|
@@ -156,8 +156,8 @@ export function createFileTreeController({ revisionProvider }) {
|
|
|
156
156
|
else {
|
|
157
157
|
const from = revisionProvider.locate(path);
|
|
158
158
|
const answer = kind === 'rename' && to !== undefined
|
|
159
|
-
? await revisionProvider.rename({ from, to: revisionProvider.locate(to), expectedRevision: known, operationId }, lifetime.signal)
|
|
160
|
-
: await revisionProvider.delete({ path: from, expectedRevision: known, operationId }, lifetime.signal);
|
|
159
|
+
? await revisionProvider.rename({ from, to: revisionProvider.locate(to), expectedRevision: known, operationId, ...(prune ? { pruneEmptied: true } : {}) }, lifetime.signal)
|
|
160
|
+
: await revisionProvider.delete({ path: from, expectedRevision: known, operationId, ...(prune ? { pruneEmptied: true } : {}) }, lifetime.signal);
|
|
161
161
|
try {
|
|
162
162
|
result = parsePublicationResult(answer);
|
|
163
163
|
}
|
|
@@ -175,6 +175,83 @@ export function createFileTreeController({ revisionProvider }) {
|
|
|
175
175
|
revisions.delete(path);
|
|
176
176
|
return settle(current, result);
|
|
177
177
|
}
|
|
178
|
+
const isFolder = (path) => [...state.directories.values(), state.search].some(listing => listing.entries.some(entry => entry.path === path && entry.kind === 'directory'));
|
|
179
|
+
/** Every file under a folder, read now: a folder exists only through its files, so a retry finds what is left. */
|
|
180
|
+
async function filesIn(folder) {
|
|
181
|
+
const files = [];
|
|
182
|
+
let cursor;
|
|
183
|
+
do {
|
|
184
|
+
const page = await revisionProvider.list({ directory: folder, ...(cursor ? { cursor } : {}) }, lifetime.signal);
|
|
185
|
+
for (const entry of page.entries) {
|
|
186
|
+
if (entry.kind === 'directory')
|
|
187
|
+
files.push(...await filesIn(entry.path));
|
|
188
|
+
else
|
|
189
|
+
files.push(entry.path);
|
|
190
|
+
}
|
|
191
|
+
cursor = page.cursor;
|
|
192
|
+
} while (cursor);
|
|
193
|
+
return files;
|
|
194
|
+
}
|
|
195
|
+
/** Whether a path is listed in its folder, read now. */
|
|
196
|
+
async function present(path) {
|
|
197
|
+
let cursor;
|
|
198
|
+
do {
|
|
199
|
+
const page = await revisionProvider.list({ directory: parentOf(path), ...(cursor ? { cursor } : {}) }, lifetime.signal);
|
|
200
|
+
if (page.entries.some(entry => entry.path === path))
|
|
201
|
+
return true;
|
|
202
|
+
cursor = page.cursor;
|
|
203
|
+
} while (cursor);
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
/** A folder's rename, move or delete, one file at a time: providers change one file per operation. It stops at the first refusal and says how far it got. */
|
|
207
|
+
async function mutateFolder(kind, path, to) {
|
|
208
|
+
active();
|
|
209
|
+
if (kind === 'rename' ? !revisionProvider.rename : !revisionProvider.delete)
|
|
210
|
+
throw new Error(`This workspace cannot ${kind} files`);
|
|
211
|
+
if (state.mutations.some(item => item.path === path && item.state.kind === 'pending'))
|
|
212
|
+
throw new Error(`${path} is already being changed; wait for it to finish`);
|
|
213
|
+
const operationId = randomUUID();
|
|
214
|
+
const current = { operationId, kind, path, ...(to === undefined ? {} : { to }), state: { kind: 'pending' } };
|
|
215
|
+
update({ ...state, mutations: [...state.mutations.filter(item => item.path !== path), current] });
|
|
216
|
+
let result, done = 0, total = 0;
|
|
217
|
+
try {
|
|
218
|
+
// A rename onto a folder that exists would merge them: refused before anything moves.
|
|
219
|
+
if (to !== undefined && await present(to))
|
|
220
|
+
result = { kind: 'conflict', current: [], reason: `${nameOf(to)} already exists` };
|
|
221
|
+
else {
|
|
222
|
+
const files = await filesIn(path);
|
|
223
|
+
total = files.length;
|
|
224
|
+
for (const file of files) {
|
|
225
|
+
const finished = await mutate(kind, file, to === undefined ? undefined : `${to}${file.slice(path.length)}`, true);
|
|
226
|
+
const outcome = finished.state.kind === 'settled' ? finished.state.result : undefined;
|
|
227
|
+
// An unconfirmed file may have changed: the count would be a guess, and a blind retry a mistake.
|
|
228
|
+
if (outcome?.kind === 'unknown') {
|
|
229
|
+
result = unknown(operationId, `At least ${done} of ${total} files of ${nameOf(path)} were changed, and one is unconfirmed; read the folder again before retrying`);
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
if (outcome?.kind !== 'committed') {
|
|
233
|
+
result = { kind: 'unavailable', reason: `${done} of ${total} files of ${nameOf(path)} were changed. Try again to change the rest.` };
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
update({ ...state, mutations: state.mutations.filter(item => item.operationId !== finished.operationId) });
|
|
237
|
+
result = outcome;
|
|
238
|
+
done++;
|
|
239
|
+
}
|
|
240
|
+
result ??= { kind: 'unavailable', reason: `${nameOf(path)} has no files to change` };
|
|
241
|
+
// The listing leaves out ignored files, so a folder can outlive every file the tree could see: say so rather than report it gone.
|
|
242
|
+
if (result.kind === 'committed' && await present(path))
|
|
243
|
+
result = { kind: 'unavailable', reason: `${done} of ${total} files of ${nameOf(path)} were changed. The folder is still there: it may hold files this list does not show.` };
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
if (error instanceof Error && lifetime.signal.aborted)
|
|
248
|
+
throw error;
|
|
249
|
+
result = unknown(operationId, `${done} of ${total || 'its'} files of ${nameOf(path)} were changed; read the folder again before retrying`);
|
|
250
|
+
}
|
|
251
|
+
const settled = settle(current, result);
|
|
252
|
+
update({ ...state, expanded: new Set([...state.expanded].filter(folder => folder !== path && !folder.startsWith(`${path}/`))) });
|
|
253
|
+
return settled;
|
|
254
|
+
}
|
|
178
255
|
const controller = {
|
|
179
256
|
revisionProvider, getSnapshot: () => state,
|
|
180
257
|
subscribe: listener => { if (state.lifecycle === 'disposed')
|
|
@@ -295,6 +372,8 @@ export function createFileTreeController({ revisionProvider }) {
|
|
|
295
372
|
catch (error) {
|
|
296
373
|
return Promise.reject(error);
|
|
297
374
|
}
|
|
375
|
+
if (isFolder(path))
|
|
376
|
+
return Promise.resolve(undefined);
|
|
298
377
|
// A rename or delete started meanwhile waits for this read, so it uses the revision the row learned.
|
|
299
378
|
const reading = (async () => {
|
|
300
379
|
try {
|
|
@@ -320,7 +399,7 @@ export function createFileTreeController({ revisionProvider }) {
|
|
|
320
399
|
checkedPath(to);
|
|
321
400
|
if (to === path)
|
|
322
401
|
throw new TypeError('The new name is the current name');
|
|
323
|
-
return mutate('rename', path, to);
|
|
402
|
+
return isFolder(path) ? mutateFolder('rename', path, to) : mutate('rename', path, to);
|
|
324
403
|
},
|
|
325
404
|
move: async (path, folder) => {
|
|
326
405
|
checkedPath(path);
|
|
@@ -331,9 +410,9 @@ export function createFileTreeController({ revisionProvider }) {
|
|
|
331
410
|
throw new TypeError(`${nameOf(path)} is already in that folder`);
|
|
332
411
|
if (folder === path || folder.startsWith(`${path}/`))
|
|
333
412
|
throw new TypeError('A file cannot move into itself');
|
|
334
|
-
return mutate('rename', path, to);
|
|
413
|
+
return isFolder(path) ? mutateFolder('rename', path, to) : mutate('rename', path, to);
|
|
335
414
|
},
|
|
336
|
-
remove: async (path) => mutate('delete',
|
|
415
|
+
remove: async (path) => isFolder(checkedPath(path)) ? mutateFolder('delete', path) : mutate('delete', path),
|
|
337
416
|
dismiss: operationId => { if (state.mutations.some(item => item.operationId === operationId && item.state.kind === 'settled'))
|
|
338
417
|
update({ ...state, mutations: state.mutations.filter(item => item.operationId !== operationId) }); },
|
|
339
418
|
history: (path, signal) => { active(); return revisionProvider.history(path, signal ? AbortSignal.any([signal, lifetime.signal]) : lifetime.signal); },
|
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.36",
|
|
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.36",
|
|
158
158
|
"@earendil-works/pi-durable": "~1.4.0",
|
|
159
159
|
"react": "^19.0.0",
|
|
160
160
|
"react-dom": "^19.0.0",
|
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.36",
|
|
8
|
+
"@hachej/boring-feedback@^0.4.36",
|
|
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.36/r/app-shell.json",
|
|
15
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/pi-chat.json"
|
|
16
16
|
],
|
|
17
17
|
"files": [
|
|
18
18
|
{
|
package/r/app-shell.json
CHANGED
|
@@ -4,22 +4,22 @@
|
|
|
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.
|
|
8
|
-
"@hachej/boring-ui-kit@^0.4.
|
|
7
|
+
"@hachej/boring-feedback@^0.4.36",
|
|
8
|
+
"@hachej/boring-ui-kit@^0.4.36",
|
|
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.
|
|
16
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
17
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
18
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
19
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
20
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
21
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
22
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
15
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/pi-chat.json",
|
|
16
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/pi-chat-plugins.json",
|
|
17
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/pi-chat-voice.json",
|
|
18
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/dock.json",
|
|
19
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/pm-panel.json",
|
|
20
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/feedback.json",
|
|
21
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/utils.json",
|
|
22
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/button.json"
|
|
23
23
|
],
|
|
24
24
|
"files": [
|
|
25
25
|
{
|
package/r/board-viewer.json
CHANGED
|
@@ -4,17 +4,17 @@
|
|
|
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.
|
|
8
|
-
"@hachej/boring-files@^0.4.
|
|
7
|
+
"@hachej/boring-ui-kit@^0.4.36",
|
|
8
|
+
"@hachej/boring-files@^0.4.36",
|
|
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.
|
|
15
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
16
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
17
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
14
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/maquette-viewer.json",
|
|
15
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/mockup-viewer.json",
|
|
16
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/pi-artifacts.json",
|
|
17
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/utils.json"
|
|
18
18
|
],
|
|
19
19
|
"files": [
|
|
20
20
|
{
|
package/r/button.json
CHANGED
package/r/dock.json
CHANGED
package/r/documents.json
CHANGED
|
@@ -4,22 +4,22 @@
|
|
|
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.
|
|
8
|
-
"@hachej/boring-files@^0.4.
|
|
7
|
+
"@hachej/boring-ui-kit@^0.4.36",
|
|
8
|
+
"@hachej/boring-files@^0.4.36",
|
|
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.
|
|
14
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
15
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
16
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
17
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
18
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
19
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
20
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
21
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
22
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
13
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/utils.json",
|
|
14
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/button.json",
|
|
15
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/pi-artifacts.json",
|
|
16
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/pi-app.json",
|
|
17
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/pi-app-kinds.json",
|
|
18
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/app-shell.json",
|
|
19
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/file-tree.json",
|
|
20
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/viewer-frame.json",
|
|
21
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/viewer-image.json",
|
|
22
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/viewer-pdf.json"
|
|
23
23
|
],
|
|
24
24
|
"files": [
|
|
25
25
|
{
|
package/r/feedback-preview.json
CHANGED
|
@@ -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.
|
|
7
|
+
"@hachej/boring-feedback@^0.4.36",
|
|
8
8
|
"@earendil-works/pi-ai@~1.4.0",
|
|
9
9
|
"react@^19.0.0"
|
|
10
10
|
],
|
package/r/feedback.json
CHANGED
|
@@ -4,17 +4,17 @@
|
|
|
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.
|
|
8
|
-
"@hachej/boring-ui-kit@^0.4.
|
|
9
|
-
"@hachej/boring-files@^0.4.
|
|
7
|
+
"@hachej/boring-feedback@^0.4.36",
|
|
8
|
+
"@hachej/boring-ui-kit@^0.4.36",
|
|
9
|
+
"@hachej/boring-files@^0.4.36",
|
|
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.
|
|
16
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
17
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
15
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/pi-chat.json",
|
|
16
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/pi-chat-voice.json",
|
|
17
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/utils.json"
|
|
18
18
|
],
|
|
19
19
|
"files": [
|
|
20
20
|
{
|
package/r/file-tree.json
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
"title": "File tree",
|
|
5
5
|
"description": "Browse, search, upload, rename, move, delete and inspect retained versions through one revisionProvider binding. Uses the consumer's shadcn Button, dropdown-menu and dialog with shared version-history rows; Base UI style only.",
|
|
6
6
|
"dependencies": [
|
|
7
|
-
"@hachej/boring-ui-kit@^0.4.
|
|
8
|
-
"@hachej/boring-files@^0.4.
|
|
7
|
+
"@hachej/boring-ui-kit@^0.4.36",
|
|
8
|
+
"@hachej/boring-files@^0.4.36",
|
|
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.
|
|
14
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
13
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/utils.json",
|
|
14
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/version-history.json"
|
|
15
15
|
],
|
|
16
16
|
"files": [
|
|
17
17
|
{
|
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.
|
|
8
|
-
"@hachej/boring-files@^0.4.
|
|
7
|
+
"@hachej/boring-ui-kit@^0.4.36",
|
|
8
|
+
"@hachej/boring-files@^0.4.36",
|
|
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.
|
|
13
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/markdown-editor.json"
|
|
14
14
|
],
|
|
15
15
|
"files": [
|
|
16
16
|
{
|
package/r/html-viewer.json
CHANGED
|
@@ -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.
|
|
8
|
-
"@hachej/boring-files@^0.4.
|
|
7
|
+
"@hachej/boring-ui-kit@^0.4.36",
|
|
8
|
+
"@hachej/boring-files@^0.4.36",
|
|
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.
|
|
13
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/button.json"
|
|
14
14
|
],
|
|
15
15
|
"files": [
|
|
16
16
|
{
|
package/r/looks-viewer.json
CHANGED
|
@@ -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.
|
|
12
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
11
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/mockup-viewer.json",
|
|
12
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/utils.json"
|
|
13
13
|
],
|
|
14
14
|
"files": [
|
|
15
15
|
{
|
package/r/maquette-viewer.json
CHANGED
package/r/markdown-editor.json
CHANGED
|
@@ -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.
|
|
8
|
-
"@hachej/boring-files@^0.4.
|
|
7
|
+
"@hachej/boring-ui-kit@^0.4.36",
|
|
8
|
+
"@hachej/boring-files@^0.4.36",
|
|
9
9
|
"react@^19.0.0",
|
|
10
10
|
"react-dom@^19.0.0",
|
|
11
11
|
"marked@^17.0.6",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"lucide-react@^1.51.0"
|
|
25
25
|
],
|
|
26
26
|
"registryDependencies": [
|
|
27
|
-
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.
|
|
27
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/button.json"
|
|
28
28
|
],
|
|
29
29
|
"files": [
|
|
30
30
|
{
|
package/r/mockup-viewer.json
CHANGED