@toolr/ui-design 0.1.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.
Files changed (112) hide show
  1. package/README.md +63 -0
  2. package/components/content/info-panel-primitives.tsx +297 -0
  3. package/components/diagrams/diagram-utils.tsx +908 -0
  4. package/components/hooks/use-click-outside.ts +27 -0
  5. package/components/hooks/use-dropdown-max-height.ts +20 -0
  6. package/components/hooks/use-navigation-history.ts +94 -0
  7. package/components/lib/ai-tools.tsx +44 -0
  8. package/components/lib/cn.ts +6 -0
  9. package/components/lib/form-colors.ts +32 -0
  10. package/components/lib/theme-engine.ts +97 -0
  11. package/components/lib/toolr-brand.tsx +31 -0
  12. package/components/sections/ai-tools-paths/index.ts +37 -0
  13. package/components/sections/ai-tools-paths/tools-paths-panel.tsx +212 -0
  14. package/components/sections/ai-tools-paths/types.ts +111 -0
  15. package/components/sections/ai-tools-paths/use-tools-paths.ts +159 -0
  16. package/components/sections/captured-issues/captured-issues-panel.tsx +214 -0
  17. package/components/sections/captured-issues/index.ts +38 -0
  18. package/components/sections/captured-issues/types.ts +113 -0
  19. package/components/sections/captured-issues/use-captured-issues.ts +111 -0
  20. package/components/sections/golden-snapshots/file-diff-viewer.tsx +420 -0
  21. package/components/sections/golden-snapshots/golden-sync-panel.tsx +223 -0
  22. package/components/sections/golden-snapshots/index.ts +145 -0
  23. package/components/sections/golden-snapshots/snapshot-manager.tsx +200 -0
  24. package/components/sections/golden-snapshots/status-overview.tsx +305 -0
  25. package/components/sections/golden-snapshots/types.ts +288 -0
  26. package/components/sections/golden-snapshots/use-golden-sync.ts +477 -0
  27. package/components/sections/golden-snapshots/version-manager.tsx +186 -0
  28. package/components/sections/prompt-editor/file-type-tabbed-prompt-editor.tsx +210 -0
  29. package/components/sections/prompt-editor/index.ts +121 -0
  30. package/components/sections/prompt-editor/simulator-prompt-editor.tsx +276 -0
  31. package/components/sections/prompt-editor/tabbed-prompt-editor.tsx +514 -0
  32. package/components/sections/prompt-editor/types.ts +101 -0
  33. package/components/sections/prompt-editor/use-prompt-editor.ts +131 -0
  34. package/components/sections/report-bug/error-logger.ts +392 -0
  35. package/components/sections/report-bug/index.ts +59 -0
  36. package/components/sections/report-bug/issue-reporter-api.ts +83 -0
  37. package/components/sections/report-bug/report-bug-form.tsx +282 -0
  38. package/components/sections/report-bug/screenshot-uploader.tsx +228 -0
  39. package/components/sections/report-bug/use-report-bug.ts +170 -0
  40. package/components/sections/snapshot-browser/index.ts +53 -0
  41. package/components/sections/snapshot-browser/snapshot-browser-panel.tsx +147 -0
  42. package/components/sections/snapshot-browser/snapshot-tree.tsx +451 -0
  43. package/components/sections/snapshot-browser/types.ts +106 -0
  44. package/components/sections/snapshot-browser/use-snapshot-browser.ts +125 -0
  45. package/components/sections/snippets-editor/index.ts +31 -0
  46. package/components/sections/snippets-editor/snippets-editor.tsx +381 -0
  47. package/components/sections/snippets-editor/types.ts +48 -0
  48. package/components/sections/snippets-editor/use-snippets-editor.ts +217 -0
  49. package/components/ui/action-dialog.tsx +309 -0
  50. package/components/ui/ai-action-button.tsx +137 -0
  51. package/components/ui/ai-execution-action-buttons.tsx +106 -0
  52. package/components/ui/badge.tsx +67 -0
  53. package/components/ui/bottom-panel-header.tsx +240 -0
  54. package/components/ui/breadcrumb.tsx +168 -0
  55. package/components/ui/checkbox.tsx +102 -0
  56. package/components/ui/collapsible-section.tsx +100 -0
  57. package/components/ui/confirm-badge.tsx +71 -0
  58. package/components/ui/detail-section.tsx +67 -0
  59. package/components/ui/detail-view-wrapper.tsx +55 -0
  60. package/components/ui/editor-placeholder-card.tsx +197 -0
  61. package/components/ui/editor-toolbar.tsx +123 -0
  62. package/components/ui/execution-details-panel.tsx +93 -0
  63. package/components/ui/extension-list-card.tsx +105 -0
  64. package/components/ui/file-structure-section.tsx +373 -0
  65. package/components/ui/file-tree.tsx +171 -0
  66. package/components/ui/files-panel.tsx +251 -0
  67. package/components/ui/filter-dropdown.tsx +173 -0
  68. package/components/ui/form-actions.tsx +127 -0
  69. package/components/ui/frontmatter-form-header.tsx +80 -0
  70. package/components/ui/icon-button.tsx +388 -0
  71. package/components/ui/input.tsx +211 -0
  72. package/components/ui/label.tsx +159 -0
  73. package/components/ui/layout-tab-bar.tsx +289 -0
  74. package/components/ui/modal.tsx +194 -0
  75. package/components/ui/nav-card.tsx +81 -0
  76. package/components/ui/navigation-bar.tsx +285 -0
  77. package/components/ui/number-input.tsx +165 -0
  78. package/components/ui/registry-browser.tsx +261 -0
  79. package/components/ui/registry-card.tsx +710 -0
  80. package/components/ui/registry-detail.tsx +224 -0
  81. package/components/ui/resizable-textarea.tsx +290 -0
  82. package/components/ui/scope-badge.tsx +67 -0
  83. package/components/ui/segmented-toggle.tsx +133 -0
  84. package/components/ui/select.tsx +172 -0
  85. package/components/ui/selection-grid.tsx +313 -0
  86. package/components/ui/setting-row.tsx +97 -0
  87. package/components/ui/snapshot-card.tsx +107 -0
  88. package/components/ui/snippets-panel.tsx +161 -0
  89. package/components/ui/sort-dropdown.tsx +109 -0
  90. package/components/ui/status-card.tsx +96 -0
  91. package/components/ui/tab-bar.tsx +340 -0
  92. package/components/ui/toggle.tsx +142 -0
  93. package/components/ui/tooltip.tsx +326 -0
  94. package/dist/content.d.ts +110 -0
  95. package/dist/content.js +195 -0
  96. package/dist/diagrams.d.ts +371 -0
  97. package/dist/diagrams.js +702 -0
  98. package/dist/index.d.ts +2714 -0
  99. package/dist/index.js +11220 -0
  100. package/dist/preset.d.ts +24 -0
  101. package/dist/preset.js +17 -0
  102. package/dist/tokens/tokens/primitives.css +45 -0
  103. package/dist/tokens/tokens/semantic.css +46 -0
  104. package/dist/tokens/tokens/theme.css +11 -0
  105. package/dist/tokens/tokens/tokens.json +65 -0
  106. package/index.ts +123 -0
  107. package/package.json +63 -0
  108. package/tailwind-preset.ts +22 -0
  109. package/tokens/primitives.css +45 -0
  110. package/tokens/semantic.css +46 -0
  111. package/tokens/theme.css +11 -0
  112. package/tokens/tokens.json +65 -0
@@ -0,0 +1,305 @@
1
+ import { useState, useRef } from 'react'
2
+ import { Archive, Check, AlertTriangle, RotateCcw } from 'lucide-react'
3
+ import { useClickOutside } from '../../hooks/use-click-outside.ts'
4
+ import { useDropdownMaxHeight } from '../../hooks/use-dropdown-max-height.ts'
5
+ import { IconButton } from '../../ui/icon-button.tsx'
6
+ import { Tooltip } from '../../ui/tooltip.tsx'
7
+ import type { GoldenStatus, GoldenMeta, SnapshotsManifest } from './types.ts'
8
+ import { formatDate, formatBytes } from './use-golden-sync.ts'
9
+
10
+ export interface StatusOverviewProps {
11
+ status: GoldenStatus | null
12
+ components: string[]
13
+ componentLabels?: Record<string, string>
14
+ devtools: boolean
15
+ manifest?: SnapshotsManifest | null
16
+ resettingComponent: string | null
17
+ resettingAll: boolean
18
+ onResetComponent: (component: string) => void
19
+ onResetAll: () => void
20
+ }
21
+
22
+ function getComponentVersion(meta: GoldenMeta | undefined, component: string): string | undefined {
23
+ return meta?.components[component]?.version
24
+ }
25
+
26
+ function renderVersionBadge(meta: GoldenMeta | undefined, color: string) {
27
+ if (!meta) return null
28
+ return (
29
+ <span className={`px-2 py-0.5 ${color} text-xs rounded font-mono`}>
30
+ {meta.version}
31
+ </span>
32
+ )
33
+ }
34
+
35
+ function VersionStatus({ exists, version, mismatch, defaultColor = 'text-green-400/70' }: { exists: boolean; version?: string; mismatch: boolean; defaultColor?: string }) {
36
+ if (!exists) return <span className="text-red-400">Missing</span>
37
+ if (!version) return <span className="text-green-400">Found</span>
38
+ return <span className={`text-xs font-mono ${mismatch ? 'text-yellow-400' : defaultColor}`}>v{version}</span>
39
+ }
40
+
41
+ function SyncStatusBanner({ goldenMeta, liveMeta, components }: { goldenMeta: GoldenMeta; liveMeta: GoldenMeta; components: string[] }) {
42
+ const mismatches = components.filter((comp) => {
43
+ const gv = getComponentVersion(goldenMeta, comp)
44
+ const lv = getComponentVersion(liveMeta, comp)
45
+ return gv !== lv
46
+ }).length
47
+
48
+ return mismatches === 0 ? (
49
+ <div className="flex items-center gap-2 px-3 py-2 rounded-lg bg-green-500/5 border border-green-500/20">
50
+ <Check className="w-4 h-4 text-green-400" />
51
+ <span className="text-sm text-green-400">Golden and Live versions are in sync</span>
52
+ </div>
53
+ ) : (
54
+ <div className="flex items-center gap-2 px-3 py-2 rounded-lg bg-yellow-500/5 border border-yellow-500/20">
55
+ <AlertTriangle className="w-4 h-4 text-yellow-400" />
56
+ <span className="text-sm text-yellow-400">Version mismatch — {mismatches} component{mismatches > 1 ? 's' : ''} differ</span>
57
+ </div>
58
+ )
59
+ }
60
+
61
+ export function StatusOverview({
62
+ status,
63
+ components,
64
+ componentLabels,
65
+ devtools,
66
+ manifest,
67
+ resettingComponent,
68
+ resettingAll,
69
+ onResetComponent,
70
+ onResetAll,
71
+ }: StatusOverviewProps) {
72
+ const anyResetting = resettingComponent !== null || resettingAll
73
+ const [showResetMenu, setShowResetMenu] = useState(false)
74
+ const resetMenuRef = useRef<HTMLDivElement>(null)
75
+ const resetMenuDropdownRef = useDropdownMaxHeight<HTMLDivElement>(showResetMenu)
76
+
77
+ useClickOutside(resetMenuRef, showResetMenu, () => setShowResetMenu(false))
78
+
79
+ const getLabel = (comp: string) => componentLabels?.[comp] ?? comp.charAt(0).toUpperCase() + comp.slice(1)
80
+
81
+ return (
82
+ <div className="space-y-4">
83
+ {/* Bundled Seed Info */}
84
+ <div className="bg-[#181825] rounded-lg p-4 border border-amber-500/30">
85
+ <div className="flex items-center gap-3 mb-3">
86
+ <Archive className="w-5 h-5 text-amber-400" />
87
+ <h3 className="text-[#cdd6f4] font-medium">Bundled Seed (App Distribution)</h3>
88
+ {status?.seed.meta && renderVersionBadge(status.seed.meta, 'bg-amber-500/20 text-amber-400')}
89
+ </div>
90
+ <div className="grid grid-cols-2 gap-4 text-sm">
91
+ <div>
92
+ <p className="text-[#6c7086]">Status</p>
93
+ <p className={status?.seed.exists ? 'text-green-400' : 'text-red-400'}>
94
+ {status?.seed.exists ? 'Found' : 'Missing'}
95
+ </p>
96
+ </div>
97
+ <div>
98
+ <p className="text-[#6c7086]">Size (compressed)</p>
99
+ <p className="text-[#cdd6f4]">{formatBytes(status?.seed.sizeBytes ?? 0)}</p>
100
+ </div>
101
+ <div>
102
+ <p className="text-[#6c7086]">Modified</p>
103
+ <p className="text-[#cdd6f4]">{status?.seed.modifiedAt ? formatDate(status.seed.modifiedAt) : 'N/A'}</p>
104
+ </div>
105
+ </div>
106
+ {!devtools && (
107
+ <p className="text-xs text-[#585b70] mt-3">
108
+ A compressed archive shipped with every release containing default configuration and templates. On first launch, the seed is extracted to initialize your local files.
109
+ </p>
110
+ )}
111
+ </div>
112
+
113
+ {/* Sync Status Banner */}
114
+ {status?.goldenMeta && status?.liveMeta && (
115
+ <SyncStatusBanner goldenMeta={status.goldenMeta} liveMeta={status.liveMeta} components={components} />
116
+ )}
117
+
118
+ {/* Local Directories */}
119
+ <div className="grid grid-cols-2 gap-4">
120
+ {/* Golden */}
121
+ <div className="bg-[#181825] rounded-lg p-4 border border-blue-500/30">
122
+ <div className="flex items-center gap-2 mb-3">
123
+ <div className="w-3 h-3 rounded-full bg-blue-400" />
124
+ <h4 className="text-[#cdd6f4] font-medium">Golden (Reference)</h4>
125
+ {renderVersionBadge(status?.goldenMeta, 'bg-blue-500/20 text-blue-400')}
126
+ </div>
127
+ <div className="space-y-2 text-sm">
128
+ {components.map((comp) => {
129
+ const version = getComponentVersion(status?.goldenMeta, comp)
130
+ const dirKey = `${comp}Golden`
131
+ const dirExists = status?.directories[dirKey]?.exists ?? false
132
+ return (
133
+ <div key={comp} className="flex justify-between items-center">
134
+ <span className="text-[#6c7086]">{getLabel(comp)}</span>
135
+ <VersionStatus
136
+ exists={dirExists || !!version}
137
+ version={version}
138
+ mismatch={false}
139
+ defaultColor="text-blue-400/70"
140
+ />
141
+ </div>
142
+ )
143
+ })}
144
+ </div>
145
+ {!devtools && (
146
+ <p className="text-xs text-[#585b70] mt-3">
147
+ Reference copy extracted from the bundled seed. Always overwritten during app updates to match the latest release. Do not edit directly.
148
+ </p>
149
+ )}
150
+ </div>
151
+
152
+ {/* Live */}
153
+ <div className="bg-[#181825] rounded-lg p-4 border border-green-500/30">
154
+ <div className="flex items-center gap-2 mb-3">
155
+ <div className="w-3 h-3 rounded-full bg-green-400" />
156
+ <h4 className="text-[#cdd6f4] font-medium">Live (Working Copy)</h4>
157
+ {renderVersionBadge(status?.liveMeta, 'bg-green-500/20 text-green-400')}
158
+ <div className="ml-auto relative" ref={resetMenuRef}>
159
+ <IconButton
160
+ icon={<RotateCcw className={anyResetting ? 'animate-spin' : ''} />}
161
+ onClick={() => setShowResetMenu((prev) => !prev)}
162
+ disabled={anyResetting}
163
+ size="xs"
164
+ color="orange"
165
+ tooltip={{ title: 'Reset options', description: 'Reset live files to golden' }}
166
+ />
167
+ {showResetMenu && (
168
+ <div ref={resetMenuDropdownRef} className="absolute right-0 top-full mt-1 w-56 bg-[#1e1e2e] border border-[#313244] rounded-lg shadow-xl z-50 py-1 overflow-hidden">
169
+ <button
170
+ onClick={() => { onResetAll(); setShowResetMenu(false) }}
171
+ className="w-full text-left px-3 py-2 text-sm text-[#cdd6f4] hover:bg-[#313244] transition-colors"
172
+ >
173
+ Reset All to Golden
174
+ </button>
175
+ <div className="border-t border-[#313244] my-1" />
176
+ {components.map((comp) => (
177
+ <button
178
+ key={comp}
179
+ onClick={() => { onResetComponent(comp); setShowResetMenu(false) }}
180
+ className="w-full text-left px-3 py-2 text-sm text-[#a6adc8] hover:bg-[#313244] transition-colors"
181
+ >
182
+ Reset {getLabel(comp)}
183
+ </button>
184
+ ))}
185
+ </div>
186
+ )}
187
+ </div>
188
+ </div>
189
+ <div className="space-y-2 text-sm">
190
+ {components.map((comp) => {
191
+ const liveV = getComponentVersion(status?.liveMeta, comp)
192
+ const goldenV = getComponentVersion(status?.goldenMeta, comp)
193
+ const dirKey = `${comp}Live`
194
+ const dirExists = status?.directories[dirKey]?.exists ?? false
195
+ const mismatch = !!(goldenV && liveV && goldenV !== liveV)
196
+ return (
197
+ <div key={comp} className="flex justify-between items-center">
198
+ <span className="text-[#6c7086]">{getLabel(comp)}</span>
199
+ <span className="flex items-center gap-2">
200
+ <VersionStatus exists={dirExists || !!liveV} version={liveV} mismatch={mismatch} />
201
+ <IconButton
202
+ icon={<RotateCcw className={resettingComponent === comp ? 'animate-spin' : ''} />}
203
+ onClick={() => onResetComponent(comp)}
204
+ disabled={anyResetting}
205
+ size="xss"
206
+ color="orange"
207
+ tooltip={{ title: `Reset ${getLabel(comp)}`, description: `Reset ${getLabel(comp).toLowerCase()} to golden version` }}
208
+ />
209
+ </span>
210
+ </div>
211
+ )
212
+ })}
213
+ </div>
214
+ {!devtools && (
215
+ <p className="text-xs text-[#585b70] mt-3">
216
+ Your active working copy — what the app uses at runtime. Customizations are made here and preserved across app updates.
217
+ </p>
218
+ )}
219
+ </div>
220
+ </div>
221
+
222
+ {/* Update Flow — settings only */}
223
+ {!devtools && (
224
+ <div className="bg-[#181825]/50 border border-[#313244] rounded-lg p-4">
225
+ <p className="text-xs font-medium text-[#a6adc8] mb-4">
226
+ How updates work <span className="font-normal text-[#585b70]">— checked per component ({components.map(getLabel).join(', ')})</span>
227
+ </p>
228
+ <div className="grid grid-cols-[auto_auto_auto_auto_auto_auto_auto] items-center gap-x-2 text-xs">
229
+ {/* Row 1: top branch outcome */}
230
+ <span /><span /><span /><span /><span />
231
+ <span className="text-[#585b70] text-base justify-self-center self-end mb-[-4px]">↗</span>
232
+ <div className="flex flex-col items-center">
233
+ <Tooltip content={{ title: 'Live updated', description: 'No local customizations were found for this component, so it was automatically updated to match the new golden version.' }} multiline maxWidth={260}>
234
+ <div className="rounded bg-green-500/10 border border-green-500/20 px-3 py-1.5 whitespace-nowrap w-full text-center cursor-help">
235
+ <span className="text-green-400 font-medium">Live updated</span>
236
+ </div>
237
+ </Tooltip>
238
+ <span className="text-[#585b70] mt-0.5">no changes</span>
239
+ </div>
240
+
241
+ {/* Row 2: linear flow */}
242
+ <div className="flex flex-col items-center">
243
+ <Tooltip content={{ title: 'App Update', description: 'A new version is installed, shipping a fresh seed archive with updated configuration and templates.' }} multiline maxWidth={260}>
244
+ <div className="rounded bg-amber-500/10 border border-amber-500/20 px-3 py-1.5 text-center w-full cursor-help">
245
+ <span className="text-amber-400 font-medium">App Update</span>
246
+ </div>
247
+ </Tooltip>
248
+ <span className="text-[#585b70] mt-0.5">ships new seed</span>
249
+ </div>
250
+ <span className="text-[#585b70] text-lg self-start justify-self-center px-2">→</span>
251
+ <div className="flex flex-col items-center">
252
+ <Tooltip content={{ title: 'Golden overwritten', description: 'The golden reference directory is always overwritten with the latest seed. This ensures golden always reflects the newest release.' }} multiline maxWidth={260}>
253
+ <div className="rounded bg-blue-500/10 border border-blue-500/20 px-3 py-1.5 text-center w-full cursor-help">
254
+ <span className="text-blue-400 font-medium">Golden</span>
255
+ </div>
256
+ </Tooltip>
257
+ <span className="text-[#585b70] mt-0.5">overwritten</span>
258
+ </div>
259
+ <span className="text-[#585b70] text-lg self-start justify-self-center px-2">→</span>
260
+ <div className="flex flex-col items-center">
261
+ <Tooltip content={{ title: 'Compare per component', description: 'Each component is compared individually. If the live version has local changes, it is left untouched.' }} multiline maxWidth={260}>
262
+ <div className="rounded bg-[#313244]/50 border border-[#313244] px-3 py-1.5 text-center w-full cursor-help">
263
+ <span className="text-[#a6adc8] font-medium">Compare</span>
264
+ </div>
265
+ </Tooltip>
266
+ <span className="text-[#585b70] mt-0.5">per component</span>
267
+ </div>
268
+ <span /><span />
269
+
270
+ {/* Row 3: bottom branch outcome */}
271
+ <span /><span /><span /><span /><span />
272
+ <span className="text-[#585b70] text-base justify-self-center self-start mt-[-20px]">↘</span>
273
+ <div className="flex flex-col items-center">
274
+ <Tooltip content={{ title: 'Live unchanged', description: 'Local customizations were detected for this component, so it was left untouched. Use the File Diff tab to review differences, or reset it manually.' }} multiline maxWidth={260}>
275
+ <div className="rounded bg-yellow-500/10 border border-yellow-500/20 px-3 py-1.5 whitespace-nowrap w-full text-center cursor-help">
276
+ <span className="text-yellow-400 font-medium">Live unchanged</span>
277
+ </div>
278
+ </Tooltip>
279
+ <span className="text-[#585b70] mt-0.5">has changes</span>
280
+ </div>
281
+ </div>
282
+ <p className="text-xs text-[#585b70] mt-3">
283
+ When a component has no local changes, it is automatically updated to match the new golden version. Components with customizations are left untouched to preserve your edits — use the File Diff tab to review differences and selectively apply changes. You can reset any component to its golden version at any time using the reset buttons above.
284
+ </p>
285
+ </div>
286
+ )}
287
+
288
+ {/* Local Snapshots - devtools only */}
289
+ {devtools && manifest && (
290
+ <div className="bg-[#181825] rounded-lg p-4 border border-[#313244]">
291
+ <div className="flex items-center gap-2 mb-3">
292
+ <Archive className="w-4 h-4 text-[#6c7086]" />
293
+ <h4 className="text-[#cdd6f4] font-medium">Local Snapshots</h4>
294
+ </div>
295
+ <div className="flex gap-6 text-sm">
296
+ <div>
297
+ <span className="text-[#6c7086]">Count: </span>
298
+ <span className="text-[#cdd6f4]">{manifest.snapshots.length}</span>
299
+ </div>
300
+ </div>
301
+ </div>
302
+ )}
303
+ </div>
304
+ )
305
+ }
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Golden Snapshots — Shared type definitions
3
+ *
4
+ * Part of: Sections > Golden Snapshots
5
+ *
6
+ * These types define the CONTRACT between the UI layer (this package) and
7
+ * the Rust/Tauri backend that each app must implement. Every type here has
8
+ * a 1:1 mapping to a Rust struct returned by Tauri commands.
9
+ *
10
+ * IMPORTANT FOR AI AGENTS:
11
+ * When adding a new field here, the corresponding Rust struct must also be
12
+ * updated (and vice versa). The serde rename attributes in Rust use camelCase
13
+ * to match these TypeScript interfaces.
14
+ *
15
+ * Generic design:
16
+ * - Components are keyed by string name (not hardcoded to specific apps)
17
+ * - Configr uses: "runtime", "verifier", "simulator", "generator"
18
+ * - Reviewr uses: "runtime", plus app-specific components
19
+ * - The UI renders whatever components the backend returns
20
+ */
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Component & Version tracking
24
+ // ---------------------------------------------------------------------------
25
+
26
+ /** Version info for a single component (runtime, verifier, etc.) */
27
+ export interface ComponentVersion {
28
+ version: string
29
+ description?: string
30
+ updatedAt?: string
31
+ hash?: string
32
+ }
33
+
34
+ /**
35
+ * Golden metadata — describes the state of a golden (reference) or live (working) copy.
36
+ *
37
+ * Rust equivalent: `GoldenMeta` in mod.rs
38
+ * Returned by: `get_status`, `get_golden_meta`
39
+ *
40
+ * `components` is a flat map of component name → version info.
41
+ * Each app defines its own component names.
42
+ */
43
+ export interface GoldenMeta {
44
+ version: string
45
+ createdAt: string
46
+ description?: string
47
+ components: Record<string, ComponentVersion>
48
+ }
49
+
50
+ // ---------------------------------------------------------------------------
51
+ // Status & Directory info
52
+ // ---------------------------------------------------------------------------
53
+
54
+ /** Filesystem status of a directory (golden, live, snapshots, etc.) */
55
+ export interface DirectoryStatus {
56
+ exists: boolean
57
+ path: string
58
+ fileCount: number
59
+ totalSizeBytes: number
60
+ }
61
+
62
+ /** Info about the bundled seed archive */
63
+ export interface SeedInfo {
64
+ exists: boolean
65
+ path: string
66
+ sizeBytes: number
67
+ modifiedAt?: string
68
+ meta?: GoldenMeta
69
+ fileCount: number
70
+ fileList: string[]
71
+ }
72
+
73
+ /**
74
+ * Overall status of the golden/live system.
75
+ *
76
+ * Rust equivalent: `ConfigrStatus` in mod.rs (generalize the name per app)
77
+ * Returned by: `get_status` command
78
+ *
79
+ * Contains metadata for both golden and live copies, directory statuses,
80
+ * and seed info. The UI uses this to render the Status Overview panel.
81
+ *
82
+ * `directories` is a flat map so apps can have any number of tracked directories.
83
+ * Convention: keys are "{component}Golden" and "{component}Live".
84
+ */
85
+ export interface GoldenStatus {
86
+ goldenMeta?: GoldenMeta
87
+ liveMeta?: GoldenMeta
88
+ directories: Record<string, DirectoryStatus>
89
+ seed: SeedInfo
90
+ }
91
+
92
+ // ---------------------------------------------------------------------------
93
+ // File diff
94
+ // ---------------------------------------------------------------------------
95
+
96
+ export type FileDiffStatus = 'added' | 'removed' | 'modified' | 'unchanged'
97
+
98
+ /** A single file's diff info between golden and live */
99
+ export interface FileDiffInfo {
100
+ /** Relative path within the component directory */
101
+ path: string
102
+ /** Which component this file belongs to (e.g. "runtime", "verifier") */
103
+ component: string
104
+ status: FileDiffStatus
105
+ goldenSize: number
106
+ liveSize: number
107
+ }
108
+
109
+ export interface DiffSummary {
110
+ added: number
111
+ removed: number
112
+ modified: number
113
+ unchanged: number
114
+ total: number
115
+ }
116
+
117
+ /** Which components have changes between golden and live */
118
+ export type ChangedComponents = Record<string, boolean>
119
+
120
+ /**
121
+ * Full diff result between golden and live directories.
122
+ *
123
+ * Rust equivalent: `GoldenLiveDiff` in mod.rs
124
+ * Returned by: `get_golden_live_diff` command
125
+ */
126
+ export interface GoldenLiveDiff {
127
+ files: FileDiffInfo[]
128
+ summary: DiffSummary
129
+ componentChanges: ChangedComponents
130
+ }
131
+
132
+ // ---------------------------------------------------------------------------
133
+ // Snapshots (versioned archives)
134
+ // ---------------------------------------------------------------------------
135
+
136
+ /** A single snapshot entry in the manifest */
137
+ export interface SnapshotInfo {
138
+ version: number
139
+ filename: string
140
+ createdAt: string
141
+ description?: string
142
+ pinned: boolean
143
+ metaVersion?: string
144
+ }
145
+
146
+ /**
147
+ * Manifest listing all snapshots.
148
+ *
149
+ * Rust equivalent: `SnapshotsManifest` in mod.rs
150
+ * Returned by: `list_golden_snapshots` command
151
+ */
152
+ export interface SnapshotsManifest {
153
+ activeVersion: number
154
+ pinnedVersion?: number
155
+ snapshots: SnapshotInfo[]
156
+ }
157
+
158
+ /** Result from creating a new snapshot */
159
+ export interface CreateSnapshotResult {
160
+ version: number
161
+ filename: string
162
+ seedUpdated: boolean
163
+ changedComponents: ChangedComponents
164
+ newMeta: GoldenMeta
165
+ }
166
+
167
+ /** Result from resetting files */
168
+ export interface ResetResult {
169
+ filesReset: number
170
+ }
171
+
172
+ // ---------------------------------------------------------------------------
173
+ // API adapter interface
174
+ // ---------------------------------------------------------------------------
175
+
176
+ /**
177
+ * GoldenSnapshotsApi — The callback interface that each app must implement.
178
+ *
179
+ * This is the bridge between the shared UI and the app-specific backend.
180
+ * Each function maps to a Tauri command (or any other backend).
181
+ *
182
+ * ┌─────────────────────────────────────────────────────────────────────┐
183
+ * │ RUST BACKEND IMPLEMENTATION GUIDE │
184
+ * │ │
185
+ * │ Each method below corresponds to a Tauri #[tauri::command]. │
186
+ * │ The Rust reference implementation lives in: │
187
+ * │ configr/development/src-tauri/src/commands/snapshots/ │
188
+ * │ ├── mod.rs — Types + helpers │
189
+ * │ ├── lifecycle.rs — All command implementations │
190
+ * │ ├── runtime_init.rs — Bootstrap (seed → golden → live) │
191
+ * │ └── zip_ops.rs — Zip archive utilities │
192
+ * │ │
193
+ * │ REQUIRED Tauri commands for full functionality: │
194
+ * │ │
195
+ * │ STATUS & METADATA: │
196
+ * │ get_status() → GoldenStatus │
197
+ * │ get_golden_meta() → GoldenMeta | null │
198
+ * │ get_golden_live_diff() → GoldenLiveDiff │
199
+ * │ │
200
+ * │ FILE OPERATIONS: │
201
+ * │ get_file_content_for_diff(component, path, source) → string|null │
202
+ * │ save_live_file_content(component, path, content) → void │
203
+ * │ reset_golden_file_to_live(component, path) → void │
204
+ * │ reset_golden_component_to_live(component) → ResetResult │
205
+ * │ │
206
+ * │ SNAPSHOT MANAGEMENT (devtools only): │
207
+ * │ create_golden_snapshot(description?) → CreateSnapshotResult │
208
+ * │ list_golden_snapshots() → SnapshotsManifest │
209
+ * │ restore_golden_snapshot(version, resetLive) → void │
210
+ * │ pin_golden_snapshot(version, pinned) → void │
211
+ * │ delete_golden_snapshot(version) → void │
212
+ * │ │
213
+ * │ VERSION MANAGEMENT: │
214
+ * │ update_component_version(component, version, desc?) → void │
215
+ * │ bump_golden_version(newVersion, desc?) → void │
216
+ * │ │
217
+ * │ DIRECTORY STRUCTURE (Rust backend must maintain): │
218
+ * │ {app_data_dir}/ │
219
+ * │ ├── golden/ — Reference copy (overwritten on update) │
220
+ * │ │ ├── .meta/meta.json — GoldenMeta serialized │
221
+ * │ │ └── {components}/ — One dir per component │
222
+ * │ ├── live/ — Working copy (user edits preserved) │
223
+ * │ │ ├── .meta/meta.json │
224
+ * │ │ └── {components}/ │
225
+ * │ └── snapshots/ — Versioned zip archives │
226
+ * │ ├── manifest.json — SnapshotsManifest │
227
+ * │ └── snapshot_v001_YYYY-MM-DD.zip │
228
+ * │ │
229
+ * │ KEY BEHAVIORS the Rust backend must implement: │
230
+ * │ 1. Hash-based change detection between golden and live files │
231
+ * │ 2. Semver auto-bump per component on snapshot creation │
232
+ * │ 3. Selective sync: only update components without user changes │
233
+ * │ 4. Snapshot pruning: keep N most recent + all pinned │
234
+ * │ 5. Snapshot = zip of live dirs + meta.json │
235
+ * │ 6. create_snapshot copies live → golden after archiving │
236
+ * └─────────────────────────────────────────────────────────────────────┘
237
+ */
238
+ export interface GoldenSnapshotsApi {
239
+ // --- Status & Metadata ---
240
+
241
+ /** Get overall system status (golden/live metadata, directory info, seed) */
242
+ getStatus: () => Promise<GoldenStatus>
243
+
244
+ /** Get golden metadata only (lighter than full status) */
245
+ getGoldenMeta: () => Promise<GoldenMeta | null>
246
+
247
+ /** Get file-level diff between golden and live directories */
248
+ getDiff: () => Promise<GoldenLiveDiff>
249
+
250
+ // --- File Operations ---
251
+
252
+ /** Get content of a single file from golden or live */
253
+ getFileContent: (component: string, path: string, source: 'golden' | 'live') => Promise<string | null>
254
+
255
+ /** Save edited content to a live file (devtools only) */
256
+ saveFile: (component: string, path: string, content: string) => Promise<void>
257
+
258
+ /** Reset a single file: copy golden → live */
259
+ resetFile: (component: string, path: string) => Promise<void>
260
+
261
+ /** Reset an entire component (or 'all'): copy golden → live */
262
+ resetComponent: (component: string) => Promise<ResetResult>
263
+
264
+ // --- Snapshot Management (devtools only) ---
265
+
266
+ /** Create a new snapshot from current live state */
267
+ createSnapshot: (description?: string) => Promise<CreateSnapshotResult>
268
+
269
+ /** List all snapshots */
270
+ listSnapshots: () => Promise<SnapshotsManifest>
271
+
272
+ /** Restore a snapshot by version number */
273
+ restoreSnapshot: (version: number, resetLive: boolean) => Promise<void>
274
+
275
+ /** Pin/unpin a snapshot (pinned snapshots are never auto-pruned) */
276
+ pinSnapshot: (version: number, pinned: boolean) => Promise<void>
277
+
278
+ /** Delete a snapshot */
279
+ deleteSnapshot: (version: number) => Promise<void>
280
+
281
+ // --- Version Management ---
282
+
283
+ /** Update version for a single component */
284
+ updateComponentVersion: (component: string, version: string, description?: string) => Promise<void>
285
+
286
+ /** Bump the overall golden version */
287
+ bumpVersion: (newVersion: string, description?: string) => Promise<void>
288
+ }