@hachej/boring-ui-kit 0.3.1 → 0.4.0
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 +46 -18
- package/THIRD_PARTY_NOTICES.md +35 -0
- package/dist/canvas-editor-native.d.ts +4 -1
- package/dist/canvas-editor-native.js +8 -6
- package/dist/canvas-editor.js +1 -1
- package/dist/canvas-proposals.d.ts +3 -1
- package/dist/canvas-proposals.js +4 -3
- package/dist/experience-document-viewer.d.ts +3 -0
- package/dist/experience-document-viewer.js +6 -5
- package/dist/experience.d.ts +3 -0
- package/dist/experience.js +2 -1
- package/dist/file-tree-view.d.ts +76 -3
- package/dist/file-tree-view.js +172 -39
- package/dist/file-tree.d.ts +3 -0
- package/dist/file-tree.js +17 -2
- package/dist/html-viewer.d.ts +4 -0
- package/dist/html-viewer.js +5 -3
- package/dist/markdown-autosave.d.ts +13 -0
- package/dist/markdown-autosave.js +94 -0
- package/dist/markdown-editor.d.ts +16 -0
- package/dist/markdown-editor.js +81 -17
- package/dist/markdown-extensions.js +9 -1
- package/dist/markdown-proposal-diff.d.ts +2 -1
- package/dist/markdown-proposal-diff.js +3 -2
- package/dist/markdown-safety.js +3 -1
- package/dist/native-chat.js +1 -1
- package/dist/remote-chat.js +5 -1
- package/dist/text-buffer.js +3 -2
- package/dist/text-draft-controls.d.ts +3 -1
- package/dist/text-draft-controls.js +3 -2
- package/dist/viewer-components.d.ts +14 -0
- package/dist/viewer-components.js +1 -0
- package/package.json +4 -4
- package/r/app-shell.json +46 -21
- package/r/button.json +19 -11
- package/r/dock.json +2 -2
- package/r/documents.json +59 -0
- package/r/feedback-preview.json +8 -2
- package/r/feedback.json +46 -325
- package/r/file-tree.json +14 -6
- package/r/html-viewer.json +8 -42
- package/r/markdown-editor.json +38 -87
- package/r/pi-ambient.json +15 -8
- package/r/pi-app.json +30 -19
- package/r/pi-chat.json +89 -70
- package/r/pi-workspace.json +7 -5
- package/r/provider-setup.json +9 -4
- package/r/registry.json +505 -878
- package/r/task-list-viewer.json +11 -21
- package/r/theme.json +23 -4
- package/r/version-history.json +24 -0
- package/r/viewers.json +139 -320
- package/styles/file-tree.css +41 -42
- package/dist/file-tree-shadcn.d.ts +0 -8
- package/dist/file-tree-shadcn.js +0 -14
package/r/task-list-viewer.json
CHANGED
|
@@ -4,12 +4,18 @@
|
|
|
4
4
|
"title": "Consumer task-list viewer",
|
|
5
5
|
"description": "A consumer-defined task-list schema and controller over the shared text buffer, with a restylable borrowed React renderer.",
|
|
6
6
|
"dependencies": [
|
|
7
|
-
"@hachej/boring-ui-kit@^0.
|
|
8
|
-
"@hachej/boring-files@^0.
|
|
7
|
+
"@hachej/boring-ui-kit@^0.4.0",
|
|
8
|
+
"@hachej/boring-files@^0.4.0",
|
|
9
9
|
"react@^19.0.0",
|
|
10
10
|
"react-dom@^19.0.0",
|
|
11
11
|
"zod@^4.6.5"
|
|
12
12
|
],
|
|
13
|
+
"registryDependencies": [
|
|
14
|
+
"button",
|
|
15
|
+
"card",
|
|
16
|
+
"input",
|
|
17
|
+
"checkbox"
|
|
18
|
+
],
|
|
13
19
|
"files": [
|
|
14
20
|
{
|
|
15
21
|
"path": "registry/task-list-viewer/task-list-document.ts",
|
|
@@ -25,18 +31,14 @@
|
|
|
25
31
|
},
|
|
26
32
|
{
|
|
27
33
|
"path": "registry/task-list-viewer/task-list-viewer.tsx",
|
|
28
|
-
"content": "'use client';\n\nimport { randomUUID } from '@hachej/boring-files/platform';\nimport React, { useId, useRef, useState, useSyncExternalStore } from 'react';\nimport type { ReadResult } from '@hachej/boring-files';\nimport type { SaveResult } from '@hachej/boring-ui-kit/resources';\nimport type { TaskListController } from './task-list-controller';\nimport type { TaskListOperation } from './task-list-document';\n\nexport interface TaskListViewerProps {\n readonly controller: TaskListController;\n readonly title?: string;\n readonly className?: string;\n}\n\nexport function TaskListViewer({ controller, title = 'Tasks', className }: TaskListViewerProps) {\n const state = useSyncExternalStore(controller.subscribe, controller.getSnapshot, controller.getSnapshot);\n const current = useRef({ controller });\n if (current.current.controller !== controller) current.current = { controller };\n const owner = current.current;\n const [form, setForm] = useState({ owner, draftTitle: '', notice: '' });\n const draftTitle = form.owner === owner ? form.draftTitle : '';\n const notice = form.owner === owner ? form.notice : '';\n const updateForm = (patch: Partial<{ draftTitle: string; notice: string }>) => {\n if (current.current !== owner) return;\n setForm(previous => ({ ...(previous.owner === owner ? previous : { owner, draftTitle: '', notice: '' }), ...patch }));\n };\n const setDraftTitle = (value: string) => updateForm({ draftTitle: value });\n const setNotice = (value: string) => updateForm({ notice: value });\n const inputId = useId();\n const disabled = state.readOnly || state.lifecycle === 'disposed';\n const edit = (operations: readonly TaskListOperation[]) => {\n const result = controller.actions.edit(controller.actions.selection(), operations);\n setNotice(result.kind === 'applied' ? '' : `${result.kind}: ${result.reason}`);\n return result;\n };\n const run = async (action: () => Promise<ReadResult | SaveResult>) => {\n try {\n const result = await action();\n setNotice(result.kind === 'available' || result.kind === 'saved' ? ''\n : result.kind === 'missing' ? 'missing: Resource is missing'\n : `${result.kind}${'reason' in result ? `: ${result.reason}` : ''}`);\n }\n catch (error) { setNotice(String(error)); }\n };\n const save = state.save.kind === 'settled' ? state.save.result : undefined;\n return <
|
|
34
|
+
"content": "'use client';\n\nimport { randomUUID } from '@hachej/boring-files/platform';\nimport React, { useId, useRef, useState, useSyncExternalStore } from 'react';\nimport { Button } from '@/components/ui/button';\nimport { Checkbox } from '@/components/ui/checkbox';\nimport { Card } from '@/components/ui/card';\nimport { Input } from '@/components/ui/input';\nimport type { ReadResult } from '@hachej/boring-files';\nimport type { SaveResult } from '@hachej/boring-ui-kit/resources';\nimport type { TaskListController } from './task-list-controller';\nimport type { TaskListOperation } from './task-list-document';\n\nexport interface TaskListViewerProps {\n readonly controller: TaskListController;\n readonly title?: string;\n readonly className?: string;\n}\n\nexport function TaskListViewer({ controller, title = 'Tasks', className }: TaskListViewerProps) {\n const state = useSyncExternalStore(controller.subscribe, controller.getSnapshot, controller.getSnapshot);\n const current = useRef({ controller });\n if (current.current.controller !== controller) current.current = { controller };\n const owner = current.current;\n const [form, setForm] = useState({ owner, draftTitle: '', notice: '' });\n const draftTitle = form.owner === owner ? form.draftTitle : '';\n const notice = form.owner === owner ? form.notice : '';\n const updateForm = (patch: Partial<{ draftTitle: string; notice: string }>) => {\n if (current.current !== owner) return;\n setForm(previous => ({ ...(previous.owner === owner ? previous : { owner, draftTitle: '', notice: '' }), ...patch }));\n };\n const setDraftTitle = (value: string) => updateForm({ draftTitle: value });\n const setNotice = (value: string) => updateForm({ notice: value });\n const inputId = useId();\n const disabled = state.readOnly || state.lifecycle === 'disposed';\n const edit = (operations: readonly TaskListOperation[]) => {\n const result = controller.actions.edit(controller.actions.selection(), operations);\n setNotice(result.kind === 'applied' ? '' : `${result.kind}: ${result.reason}`);\n return result;\n };\n const run = async (action: () => Promise<ReadResult | SaveResult>) => {\n try {\n const result = await action();\n setNotice(result.kind === 'available' || result.kind === 'saved' ? ''\n : result.kind === 'missing' ? 'missing: Resource is missing'\n : `${result.kind}${'reason' in result ? `: ${result.reason}` : ''}`);\n }\n catch (error) { setNotice(String(error)); }\n };\n const save = state.save.kind === 'settled' ? state.save.result : undefined;\n return <Card className={['boring-task-list-recipe', className].filter(Boolean).join(' ')} data-boring=\"task-list-viewer\">\n <h2>{title}</h2>\n <p role=\"status\" aria-live=\"polite\">{state.lifecycle === 'disposed' ? 'Closed' : state.readOnly ? 'Read only' : state.dirty ? 'Unsaved changes' : 'Saved'}\n {state.save.kind === 'pending' ? ' · Saving' : save ? ` · ${save.kind}${'reason' in save ? `: ${save.reason}` : ''}` : ''}\n {state.remote ? ' · Remote changes available' : ''}</p>\n <ul>{state.document.items.map(item => <li key={item.id} data-task-id={item.id}>\n <label><Checkbox aria-label={`Complete ${item.title}`} checked={item.completed} disabled={disabled}\n onCheckedChange={completed => edit([{ kind: 'set-completed', id: item.id, completed }])} />{item.title}</label>\n <Button variant=\"ghost\" aria-label={`Remove ${item.title}`} disabled={disabled} onClick={() => edit([{ kind: 'remove', id: item.id }])}>Remove</Button>\n </li>)}</ul>\n <form onSubmit={event => { event.preventDefault(); if (edit([{ kind: 'add', id: randomUUID(), title: draftTitle }]).kind === 'applied') setDraftTitle(''); }}>\n <label htmlFor={inputId}>New task</label><Input id={inputId} value={draftTitle} disabled={disabled} onChange={event => setDraftTitle(event.target.value)} />\n <Button variant=\"default\" type=\"submit\" disabled={disabled || !draftTitle.trim()}>Add task</Button>\n </form>\n <div className=\"boring-task-list-actions\">\n <Button variant=\"default\" disabled={disabled || !state.dirty || state.save.kind === 'pending'} onClick={() => void run(() => controller.flush(controller.actions.selection()))}>Save</Button>\n <Button variant=\"ghost\" disabled={state.lifecycle === 'disposed'} onClick={() => void run(controller.actions.refresh)}>Refresh</Button>\n <Button variant=\"ghost\" disabled={disabled || (!state.dirty && !state.remote)} onClick={() => void run(controller.actions.discardToRemote)}>Discard</Button>\n <Button variant=\"ghost\" disabled={state.lifecycle === 'disposed' || save?.kind !== 'unknown'} onClick={() => void run(controller.actions.reconcile)}>Reconcile</Button>\n <Button variant=\"ghost\" disabled={state.lifecycle === 'disposed' || save?.kind !== 'unknown'} onClick={() => void run(controller.actions.abandon)}>Abandon save</Button>\n </div>\n {notice && <p role=\"alert\">{notice}</p>}\n </Card>;\n}\n",
|
|
29
35
|
"type": "registry:component",
|
|
30
36
|
"target": "components/task-list-viewer/task-list-viewer.tsx"
|
|
31
37
|
}
|
|
32
38
|
],
|
|
33
39
|
"css": {
|
|
34
40
|
".boring-task-list-recipe[data-boring=\"task-list-viewer\"]": {
|
|
35
|
-
"padding": "
|
|
36
|
-
"border": "1px solid var(--border, #d1d5db)",
|
|
37
|
-
"border-radius": "var(--boring-task-list-radius, 0.75rem)",
|
|
38
|
-
"background": "var(--background, #fff)",
|
|
39
|
-
"color": "var(--foreground, #111827)"
|
|
41
|
+
"padding-inline": "var(--card-spacing)"
|
|
40
42
|
},
|
|
41
43
|
".boring-task-list-recipe[data-boring=\"task-list-viewer\"] ul": {
|
|
42
44
|
"list-style": "none",
|
|
@@ -50,20 +52,8 @@
|
|
|
50
52
|
"gap": "0.5rem",
|
|
51
53
|
"flex-wrap": "wrap"
|
|
52
54
|
},
|
|
53
|
-
".boring-task-list-recipe[data-boring=\"task-list-viewer\"] button": {
|
|
54
|
-
"min-height": "2.25rem",
|
|
55
|
-
"padding": "0.375rem 0.75rem"
|
|
56
|
-
},
|
|
57
|
-
".boring-task-list-recipe[data-boring=\"task-list-viewer\"] input[type=\"text\"]": {
|
|
58
|
-
"min-height": "2.25rem",
|
|
59
|
-
"max-width": "100%"
|
|
60
|
-
},
|
|
61
55
|
".boring-task-list-recipe[data-boring=\"task-list-viewer\"] [role=\"alert\"]": {
|
|
62
|
-
"color": "var(--destructive
|
|
63
|
-
},
|
|
64
|
-
".boring-task-list-recipe[data-boring=\"task-list-viewer\"] :focus-visible": {
|
|
65
|
-
"outline": "2px solid var(--ring, #2563eb)",
|
|
66
|
-
"outline-offset": "2px"
|
|
56
|
+
"color": "var(--destructive)"
|
|
67
57
|
}
|
|
68
58
|
},
|
|
69
59
|
"docs": "The installed task-list schema/controller are trusted consumer code; publication and draft recovery come from @hachej/boring-ui-kit/text-buffer. Construct a feature with a host-authenticated ResourceClient and exact source, then pass its concrete controller to TaskListViewer. Install native tools separately on the server. No host credentials or native kernel are copied into this item. Local tarball qualification only; no hosted release endpoint is advertised.",
|
package/r/theme.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
3
|
"name": "theme",
|
|
4
4
|
"title": "Theme",
|
|
5
|
-
"description": "The shadcn tokens every Tailwind item is styled with (neutral base, light and dark) and their Tailwind v4 `@theme inline` bindings. The CLI adds only the tokens the host does not define yet. The examples compile the same values (
|
|
5
|
+
"description": "The shadcn tokens every Tailwind item is styled with (neutral base, light and dark) and their Tailwind v4 `@theme inline` bindings. The CLI adds only the tokens the host does not define yet. The examples compile the same values (lab/studio/tailwind.mjs).",
|
|
6
6
|
"cssVars": {
|
|
7
7
|
"theme": {
|
|
8
8
|
"color-background": "var(--background)",
|
|
@@ -28,7 +28,14 @@
|
|
|
28
28
|
"radius-lg": "var(--radius)",
|
|
29
29
|
"radius-xl": "calc(var(--radius) + 4px)",
|
|
30
30
|
"radius-2xl": "calc(var(--radius) * 1.8)",
|
|
31
|
-
"radius-3xl": "calc(var(--radius) * 2.2)"
|
|
31
|
+
"radius-3xl": "calc(var(--radius) * 2.2)",
|
|
32
|
+
"default-border-width": "var(--border-width, 1px)",
|
|
33
|
+
"shadow-xs": "var(--shadow-xs)",
|
|
34
|
+
"shadow-sm": "var(--shadow-sm)",
|
|
35
|
+
"shadow-md": "var(--shadow-md)",
|
|
36
|
+
"shadow-lg": "var(--shadow-lg)",
|
|
37
|
+
"shadow-xl": "var(--shadow-xl)",
|
|
38
|
+
"shadow-2xl": "var(--shadow-2xl)"
|
|
32
39
|
},
|
|
33
40
|
"light": {
|
|
34
41
|
"radius": "0.75rem",
|
|
@@ -50,7 +57,13 @@
|
|
|
50
57
|
"border": "oklch(0.92 0.004 286.32)",
|
|
51
58
|
"input": "oklch(0.89 0.005 286.32)",
|
|
52
59
|
"ring": "oklch(0.55 0.02 286.067)",
|
|
53
|
-
"shell": "oklch(0.975 0.002 286)"
|
|
60
|
+
"shell": "oklch(0.975 0.002 286)",
|
|
61
|
+
"shadow-xs": "0 1px 2px 0 rgb(0 0 0 / 0.05)",
|
|
62
|
+
"shadow-sm": "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
|
|
63
|
+
"shadow-md": "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
|
|
64
|
+
"shadow-lg": "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)",
|
|
65
|
+
"shadow-xl": "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)",
|
|
66
|
+
"shadow-2xl": "0 25px 50px -12px rgb(0 0 0 / 0.25)"
|
|
54
67
|
},
|
|
55
68
|
"dark": {
|
|
56
69
|
"background": "oklch(0.175 0.006 285.885)",
|
|
@@ -71,7 +84,13 @@
|
|
|
71
84
|
"border": "oklch(1 0 0 / 11%)",
|
|
72
85
|
"input": "oklch(1 0 0 / 16%)",
|
|
73
86
|
"ring": "oklch(0.65 0.02 286.067)",
|
|
74
|
-
"shell": "oklch(0.13 0.005 285.823)"
|
|
87
|
+
"shell": "oklch(0.13 0.005 285.823)",
|
|
88
|
+
"shadow-xs": "0 1px 2px 0 rgb(0 0 0 / 0.05)",
|
|
89
|
+
"shadow-sm": "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
|
|
90
|
+
"shadow-md": "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
|
|
91
|
+
"shadow-lg": "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)",
|
|
92
|
+
"shadow-xl": "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)",
|
|
93
|
+
"shadow-2xl": "0 25px 50px -12px rgb(0 0 0 / 0.25)"
|
|
75
94
|
}
|
|
76
95
|
},
|
|
77
96
|
"type": "registry:theme"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "version-history",
|
|
4
|
+
"title": "Version history rows",
|
|
5
|
+
"description": "Shared view-only version rows and menu metadata with relative time, provenance and Current. No file reads, publication or restoration.",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"@hachej/boring-files@^0.4.0",
|
|
8
|
+
"react@^19.0.0"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [
|
|
11
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.0/r/theme.json",
|
|
12
|
+
"https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.0/r/button.json",
|
|
13
|
+
"button"
|
|
14
|
+
],
|
|
15
|
+
"files": [
|
|
16
|
+
{
|
|
17
|
+
"path": "registry/version-history/version-history.tsx",
|
|
18
|
+
"content": "'use client';\n\nimport type { SavedRevision } from '@hachej/boring-files/revision';\nimport { Button } from '@/components/ui/button';\nimport { ActionMenuItemContent } from '../button/actions';\nimport type { MenuItem } from '../button/actions';\n\nexport const defaultVersionHistoryLabels = {\n versionHistory: 'Version history',\n currentVersion: 'Current',\n versionYou: 'You',\n versionAgent: 'Agent',\n versionObserved: 'External change',\n versionUnknownAuthor: 'Saved version',\n versionJustNow: 'Just now',\n versionMinutesAgo: (count: number) => `${count} min ago`,\n versionHoursAgo: (count: number) => `${count} h ago`,\n versionYesterdayAt: (time: string) => `Yesterday ${time}`,\n versionToday: 'Today',\n versionYesterday: 'Yesterday',\n};\nexport type VersionHistoryLabels = typeof defaultVersionHistoryLabels;\nexport const frenchVersionHistoryLabels: VersionHistoryLabels = {\n versionHistory: 'Historique des versions',\n currentVersion: 'Actuelle',\n versionYou: 'Vous',\n versionAgent: 'Agent',\n versionObserved: 'Modification externe',\n versionUnknownAuthor: 'Version enregistrée',\n versionJustNow: 'À l’instant',\n versionMinutesAgo: count => `Il y a ${count} min`,\n versionHoursAgo: count => `Il y a ${count} h`,\n versionYesterdayAt: time => `Hier ${time}`,\n versionToday: 'Aujourd’hui',\n versionYesterday: 'Hier',\n};\n\nexport interface VersionHistoryEntry {\n readonly id: string;\n readonly label: string;\n readonly latest?: boolean;\n readonly savedAt?: number;\n readonly source?: SavedRevision['source'];\n /** Only shown when the host has measured the change. Never inferred from a revision id. */\n readonly changeSummary?: string;\n}\n\nfunction dayLabel(time: number, now: number, labels: VersionHistoryLabels): string {\n const date = new Date(time), today = new Date(now), yesterday = new Date(now);\n yesterday.setDate(yesterday.getDate() - 1);\n return date.toDateString() === today.toDateString() ? labels.versionToday\n : date.toDateString() === yesterday.toDateString() ? labels.versionYesterday\n : date.toLocaleDateString();\n}\n\nfunction relativeTime(time: number, now: number, labels: VersionHistoryLabels): string {\n const minutes = Math.max(0, Math.floor((now - time) / 60000));\n if (minutes < 1) return labels.versionJustNow;\n if (minutes < 60) return labels.versionMinutesAgo(minutes);\n const yesterday = new Date(now); yesterday.setDate(yesterday.getDate() - 1);\n if (new Date(time).toDateString() === yesterday.toDateString()) return labels.versionYesterdayAt(new Date(time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false }));\n if (minutes < 1440) return labels.versionHoursAgo(Math.floor(minutes / 60));\n return new Date(time).toLocaleString([], { dateStyle: 'medium', timeStyle: 'short' });\n}\n\n/** Current is the displayed revision, not necessarily the latest one. Shared by menus and inline rows. */\nexport function versionHistoryItems({ versions, current, onSelect, labels, now, testId }: {\n readonly versions: readonly VersionHistoryEntry[];\n readonly current: string;\n readonly onSelect: (id: string) => void;\n readonly labels: VersionHistoryLabels;\n readonly now: number;\n readonly testId: string;\n}): readonly MenuItem[] {\n return versions.map(version => {\n const author = version.source === 'publish' ? labels.versionYou : version.source === 'agent' ? labels.versionAgent : version.source === 'observed' ? labels.versionObserved : labels.versionUnknownAuthor;\n return { id: version.id, label: version.savedAt !== undefined ? relativeTime(version.savedAt, now, labels) : version.label,\n description: version.changeSummary ? `${author} · ${version.changeSummary}` : author,\n ...(version.savedAt !== undefined ? { group: dayLabel(version.savedAt, now, labels) } : {}),\n ...(version.id === current ? { hint: labels.currentVersion } : {}), checked: version.id === current,\n testId: `${testId}-item`, onSelect: () => onSelect(version.id) };\n });\n}\n\n/** Inline form of the viewer menu's rows. The consumer's shadcn Button owns each selection control. */\nexport function VersionHistoryRows({ versions, current, onSelect, labels: given, testId = 'version-history' }: {\n readonly versions: readonly VersionHistoryEntry[];\n readonly current: string;\n readonly onSelect: (id: string) => void;\n readonly labels?: Partial<VersionHistoryLabels>;\n readonly testId?: string;\n}) {\n const labels = { ...defaultVersionHistoryLabels, ...given };\n const items = versionHistoryItems({ versions, current, onSelect, labels, now: Date.now(), testId });\n return <ul data-boring=\"version-history\" className=\"m-0 list-none p-0\">\n {items.map((item, index) => <li key={item.id}>\n {item.group && item.group !== items[index - 1]?.group && <p className=\"px-2 pt-3 pb-1 text-xs text-muted-foreground\">{item.group}</p>}\n <Button type=\"button\" variant=\"ghost\" aria-pressed={item.checked} data-item={item.id} data-testid={item.testId} onClick={item.onSelect} className=\"h-auto min-h-11 w-full justify-start gap-2 text-left\">\n <ActionMenuItemContent item={item} testId={testId} />\n </Button>\n </li>)}\n </ul>;\n}\n",
|
|
19
|
+
"type": "registry:component",
|
|
20
|
+
"target": "components/version-history/version-history.tsx"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"type": "registry:component"
|
|
24
|
+
}
|