@hachej/boring-ui-kit 0.4.35 → 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.
Files changed (54) hide show
  1. package/dist/file-tree-view.d.ts +1 -0
  2. package/dist/file-tree-view.js +10 -6
  3. package/dist/file-tree.d.ts +3 -3
  4. package/dist/file-tree.js +85 -6
  5. package/package.json +2 -2
  6. package/r/agent-target.json +4 -4
  7. package/r/app-shell.json +10 -10
  8. package/r/board-viewer.json +6 -6
  9. package/r/button.json +1 -1
  10. package/r/dock.json +1 -1
  11. package/r/documents.json +12 -12
  12. package/r/feedback-preview.json +1 -1
  13. package/r/feedback.json +6 -6
  14. package/r/file-tree.json +4 -4
  15. package/r/highlights.json +3 -3
  16. package/r/html-viewer.json +3 -3
  17. package/r/looks-viewer.json +2 -2
  18. package/r/maquette-viewer.json +1 -1
  19. package/r/markdown-editor.json +3 -3
  20. package/r/mockup-viewer.json +1 -1
  21. package/r/pi-ambient.json +8 -8
  22. package/r/pi-app-kinds.json +10 -10
  23. package/r/pi-app.json +10 -10
  24. package/r/pi-artifacts.json +2 -2
  25. package/r/pi-chat-approval.json +2 -2
  26. package/r/pi-chat-attachments.json +1 -1
  27. package/r/pi-chat-markdown.json +2 -2
  28. package/r/pi-chat-mentions.json +1 -1
  29. package/r/pi-chat-pickers.json +1 -1
  30. package/r/pi-chat-plugins.json +11 -11
  31. package/r/pi-chat-question.json +1 -1
  32. package/r/pi-chat-slash.json +2 -2
  33. package/r/pi-chat-subagent.json +1 -1
  34. package/r/pi-chat-voice.json +3 -3
  35. package/r/pi-chat.json +5 -5
  36. package/r/pi-workspace.json +3 -3
  37. package/r/placed-agent.json +4 -4
  38. package/r/pm-panel.json +13 -13
  39. package/r/provider-setup.json +1 -1
  40. package/r/registry.json +183 -183
  41. package/r/requests-board.json +1 -1
  42. package/r/shared-chat.json +6 -6
  43. package/r/task-board.json +2 -2
  44. package/r/task-list-viewer.json +2 -2
  45. package/r/task-mentions.json +4 -4
  46. package/r/version-history.json +2 -2
  47. package/r/viewer-code.json +2 -2
  48. package/r/viewer-frame.json +4 -4
  49. package/r/viewer-html.json +4 -4
  50. package/r/viewer-image.json +2 -2
  51. package/r/viewer-markdown.json +5 -5
  52. package/r/viewer-pdf.json +3 -3
  53. package/r/viewers.json +6 -6
  54. package/r/windows.json +1 -1
@@ -59,6 +59,7 @@ export declare const defaultFileTreeLabels: {
59
59
  delete: string;
60
60
  deleteTitle: (name: string) => string;
61
61
  deleteDescription: string;
62
+ deleteFolderDescription: string;
62
63
  cancel: string;
63
64
  changing: string;
64
65
  renamed: (to: string) => string;
@@ -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) => entry.kind === 'file' && !temporaryPaths.has(entry.path) && uploadRows.get(entry.path)?.kind !== 'pending' && mutationRows.get(entry.path)?.state.kind !== 'pending';
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" }) }), !folder && !temporary && !status && !editing && mutation?.state.kind !== 'pending' && _jsx(FileActions, { Actions: Actions, controller: controller, entry: entry, labels: labels, onOpen: onOpen ? () => onOpen(entry.path) : undefined, onHistory: history ? () => 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);
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 folders = asking?.kind === 'move' ? [...new Set(['', ...[...state.directories.values(), state.search].flatMap(listing => listing.entries.filter(entry => entry.kind === 'directory').map(entry => entry.path))])].sort() : [];
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
- items.push({ id: 'download', label: labels.download, icon: _jsx(Download, { "aria-hidden": "true" }), onSelect: () => { void download(); } });
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)
@@ -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', checkedPath(path)),
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.35",
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.35",
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",
@@ -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.35",
8
- "@hachej/boring-feedback@^0.4.35",
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.35/r/app-shell.json",
15
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/pi-chat.json"
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.35",
8
- "@hachej/boring-ui-kit@^0.4.35",
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.35/r/pi-chat.json",
16
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/pi-chat-plugins.json",
17
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/pi-chat-voice.json",
18
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/dock.json",
19
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/pm-panel.json",
20
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/feedback.json",
21
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/utils.json",
22
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/button.json"
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
  {
@@ -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.35",
8
- "@hachej/boring-files@^0.4.35",
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.35/r/maquette-viewer.json",
15
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/mockup-viewer.json",
16
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/pi-artifacts.json",
17
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/utils.json"
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
@@ -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.35/r/utils.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/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.35/r/utils.json"
10
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/utils.json"
11
11
  ],
12
12
  "files": [
13
13
  {
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.35",
8
- "@hachej/boring-files@^0.4.35",
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.35/r/utils.json",
14
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/button.json",
15
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/pi-artifacts.json",
16
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/pi-app.json",
17
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/pi-app-kinds.json",
18
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/app-shell.json",
19
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/file-tree.json",
20
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/viewer-frame.json",
21
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/viewer-image.json",
22
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/viewer-pdf.json"
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
  {
@@ -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.35",
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.35",
8
- "@hachej/boring-ui-kit@^0.4.35",
9
- "@hachej/boring-files@^0.4.35",
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.35/r/pi-chat.json",
16
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/pi-chat-voice.json",
17
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/utils.json"
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.35",
8
- "@hachej/boring-files@^0.4.35",
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.35/r/utils.json",
14
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/version-history.json"
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.35",
8
- "@hachej/boring-files@^0.4.35",
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.35/r/markdown-editor.json"
13
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/markdown-editor.json"
14
14
  ],
15
15
  "files": [
16
16
  {
@@ -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.35",
8
- "@hachej/boring-files@^0.4.35",
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.35/r/button.json"
13
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/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.35/r/mockup-viewer.json",
12
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/utils.json"
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
  {
@@ -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.35/r/diagram-viewer.json"
11
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/diagram-viewer.json"
12
12
  ],
13
13
  "files": [
14
14
  {
@@ -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.35",
8
- "@hachej/boring-files@^0.4.35",
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.35/r/button.json"
27
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/button.json"
28
28
  ],
29
29
  "files": [
30
30
  {
@@ -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.35/r/viewer-frame.json"
13
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/viewer-frame.json"
14
14
  ],
15
15
  "files": [
16
16
  {
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 over the kit's shared chat-react run notifications (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.35",
7
+ "@hachej/boring-ui-kit@^0.4.36",
8
8
  "react@^19.0.0",
9
9
  "react-dom@^19.0.0",
10
10
  "@earendil-works/pi-durable@~1.4.0",
@@ -12,13 +12,13 @@
12
12
  "lucide-react@^1.51.0"
13
13
  ],
14
14
  "registryDependencies": [
15
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/pi-chat.json",
16
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/pi-chat-plugins.json",
17
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/pi-chat-question.json",
18
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/pi-artifacts.json",
19
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/pi-workspace.json",
20
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/utils.json",
21
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/button.json"
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-question.json",
18
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/pi-artifacts.json",
19
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/pi-workspace.json",
20
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/utils.json",
21
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/button.json"
22
22
  ],
23
23
  "files": [
24
24
  {
@@ -4,16 +4,16 @@
4
4
  "title": "Pi agent app default kinds",
5
5
  "description": "defaultKinds({ locale, comments }): the artifact kinds the pi-app item draws with by default, in order: maquette, diagram, mock-up and records, then Markdown, HTML, SVG, CSV, image and PDF (documentKinds). Pass it as AgentWorkspace's artifactKinds (or ViewerOptions.kinds), with an app's own kinds before it. Each kind loads its viewer on first open.",
6
6
  "registryDependencies": [
7
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/pi-app.json",
8
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/viewer-markdown.json",
9
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/viewer-html.json",
10
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/viewer-image.json",
11
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/viewer-pdf.json",
12
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/viewer-code.json",
13
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/maquette-viewer.json",
14
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/diagram-viewer.json",
15
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/mockup-viewer.json",
16
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.35/r/record-viewer.json"
7
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/pi-app.json",
8
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/viewer-markdown.json",
9
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/viewer-html.json",
10
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/viewer-image.json",
11
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/viewer-pdf.json",
12
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/viewer-code.json",
13
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/maquette-viewer.json",
14
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.36/r/diagram-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/record-viewer.json"
17
17
  ],
18
18
  "files": [
19
19
  {