@antigenic-oss/paint 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 (158) hide show
  1. package/LICENSE +178 -0
  2. package/NOTICE +4 -0
  3. package/README.md +180 -0
  4. package/bin/paint.js +266 -0
  5. package/next-env.d.ts +6 -0
  6. package/next.config.ts +19 -0
  7. package/package.json +81 -0
  8. package/postcss.config.mjs +8 -0
  9. package/public/dev-editor-inspector.js +1872 -0
  10. package/src/app/api/claude/analyze/route.ts +319 -0
  11. package/src/app/api/claude/apply/route.ts +185 -0
  12. package/src/app/api/claude/pick-folder/route.ts +64 -0
  13. package/src/app/api/claude/scan/route.ts +221 -0
  14. package/src/app/api/claude/status/route.ts +55 -0
  15. package/src/app/api/project/scan/route.ts +634 -0
  16. package/src/app/api/project-scan/css-variables/route.ts +238 -0
  17. package/src/app/api/project-scan/route.ts +40 -0
  18. package/src/app/api/project-scan/tailwind-config/route.ts +172 -0
  19. package/src/app/api/proxy/[[...path]]/route.ts +2400 -0
  20. package/src/app/docs/DocsClient.tsx +322 -0
  21. package/src/app/docs/layout.tsx +7 -0
  22. package/src/app/docs/page.tsx +855 -0
  23. package/src/app/globals.css +176 -0
  24. package/src/app/layout.tsx +19 -0
  25. package/src/app/page.tsx +46 -0
  26. package/src/bridge/api-handlers.ts +885 -0
  27. package/src/bridge/proxy-handler.ts +329 -0
  28. package/src/bridge/server.ts +113 -0
  29. package/src/components/BreakpointTabs.tsx +72 -0
  30. package/src/components/ChangeSummaryModal.tsx +267 -0
  31. package/src/components/ConnectModal.tsx +994 -0
  32. package/src/components/Editor.tsx +90 -0
  33. package/src/components/PageSelector.tsx +208 -0
  34. package/src/components/PreviewFrame.tsx +299 -0
  35. package/src/components/ProjectFolderBanner.tsx +91 -0
  36. package/src/components/ResponsiveToolbar.tsx +222 -0
  37. package/src/components/TargetSelector.tsx +243 -0
  38. package/src/components/TopBar.tsx +315 -0
  39. package/src/components/common/CollapsibleSection.tsx +36 -0
  40. package/src/components/common/ColorPicker.tsx +920 -0
  41. package/src/components/common/EditablePre.tsx +136 -0
  42. package/src/components/common/ErrorBoundary.tsx +65 -0
  43. package/src/components/common/ResizablePanel.tsx +83 -0
  44. package/src/components/common/ScanAnimation.tsx +76 -0
  45. package/src/components/common/ToastContainer.tsx +97 -0
  46. package/src/components/common/UnitInput.tsx +77 -0
  47. package/src/components/common/VariableColorPicker.tsx +622 -0
  48. package/src/components/left-panel/AddElementPanel.tsx +237 -0
  49. package/src/components/left-panel/ComponentsPanel.tsx +609 -0
  50. package/src/components/left-panel/IconSidebar.tsx +99 -0
  51. package/src/components/left-panel/LayerNode.tsx +874 -0
  52. package/src/components/left-panel/LayerSearch.tsx +23 -0
  53. package/src/components/left-panel/LayersPanel.tsx +52 -0
  54. package/src/components/left-panel/LeftPanel.tsx +122 -0
  55. package/src/components/left-panel/PagesPanel.tsx +114 -0
  56. package/src/components/left-panel/icons.tsx +162 -0
  57. package/src/components/left-panel/terminal/ScanOverlay.tsx +66 -0
  58. package/src/components/left-panel/terminal/TerminalPanel.tsx +217 -0
  59. package/src/components/right-panel/ElementLogBox.tsx +248 -0
  60. package/src/components/right-panel/PanelTabs.tsx +83 -0
  61. package/src/components/right-panel/RightPanel.tsx +41 -0
  62. package/src/components/right-panel/changes/AiScanResultPanel.tsx +285 -0
  63. package/src/components/right-panel/changes/ChangeEntry.tsx +59 -0
  64. package/src/components/right-panel/changes/ChangelogActions.tsx +105 -0
  65. package/src/components/right-panel/changes/ChangesPanel.tsx +1474 -0
  66. package/src/components/right-panel/claude/ApplyConfirmModal.tsx +376 -0
  67. package/src/components/right-panel/claude/ClaudeErrorState.tsx +125 -0
  68. package/src/components/right-panel/claude/ClaudeIntegrationPanel.tsx +482 -0
  69. package/src/components/right-panel/claude/ClaudeProgressIndicator.tsx +76 -0
  70. package/src/components/right-panel/claude/DiffCard.tsx +130 -0
  71. package/src/components/right-panel/claude/DiffViewer.tsx +54 -0
  72. package/src/components/right-panel/claude/ProjectRootSelector.tsx +275 -0
  73. package/src/components/right-panel/claude/ResultsSummary.tsx +119 -0
  74. package/src/components/right-panel/claude/SetupFlow.tsx +315 -0
  75. package/src/components/right-panel/console/ConsolePanel.tsx +209 -0
  76. package/src/components/right-panel/design/AppearanceSection.tsx +703 -0
  77. package/src/components/right-panel/design/BackgroundSection.tsx +516 -0
  78. package/src/components/right-panel/design/BorderSection.tsx +161 -0
  79. package/src/components/right-panel/design/CSSRawView.tsx +412 -0
  80. package/src/components/right-panel/design/DesignCSSTabToggle.tsx +51 -0
  81. package/src/components/right-panel/design/DesignPanel.tsx +275 -0
  82. package/src/components/right-panel/design/ElementBreadcrumb.tsx +51 -0
  83. package/src/components/right-panel/design/GradientEditor.tsx +726 -0
  84. package/src/components/right-panel/design/LayoutSection.tsx +1948 -0
  85. package/src/components/right-panel/design/PositionSection.tsx +865 -0
  86. package/src/components/right-panel/design/PropertiesSection.tsx +86 -0
  87. package/src/components/right-panel/design/SVGSection.tsx +361 -0
  88. package/src/components/right-panel/design/ShadowBlurSection.tsx +227 -0
  89. package/src/components/right-panel/design/SizeSection.tsx +183 -0
  90. package/src/components/right-panel/design/TextSection.tsx +719 -0
  91. package/src/components/right-panel/design/icons.tsx +948 -0
  92. package/src/components/right-panel/design/inputs/BoxModelPreview.tsx +467 -0
  93. package/src/components/right-panel/design/inputs/ColorInput.tsx +43 -0
  94. package/src/components/right-panel/design/inputs/CompactInput.tsx +333 -0
  95. package/src/components/right-panel/design/inputs/DraggableLabel.tsx +118 -0
  96. package/src/components/right-panel/design/inputs/IconToggleGroup.tsx +54 -0
  97. package/src/components/right-panel/design/inputs/LinkedInputPair.tsx +174 -0
  98. package/src/components/right-panel/design/inputs/SectionHeader.tsx +79 -0
  99. package/src/components/right-panel/variables/VariablesPanel.tsx +388 -0
  100. package/src/hooks/useBridge.ts +95 -0
  101. package/src/hooks/useChangeTracker.ts +563 -0
  102. package/src/hooks/useClaudeAPI.ts +118 -0
  103. package/src/hooks/useDOMTree.ts +25 -0
  104. package/src/hooks/useKeyboardShortcuts.ts +76 -0
  105. package/src/hooks/usePostMessage.ts +589 -0
  106. package/src/hooks/useProjectScan.ts +204 -0
  107. package/src/hooks/useResizable.ts +20 -0
  108. package/src/hooks/useSelectedElement.ts +51 -0
  109. package/src/hooks/useTargetUrl.ts +81 -0
  110. package/src/inspector/DOMTraverser.ts +71 -0
  111. package/src/inspector/ElementSelector.ts +23 -0
  112. package/src/inspector/HoverHighlighter.ts +54 -0
  113. package/src/inspector/SelectionHighlighter.ts +27 -0
  114. package/src/inspector/StyleExtractor.ts +19 -0
  115. package/src/inspector/inspector.ts +17 -0
  116. package/src/inspector/messaging.ts +30 -0
  117. package/src/lib/apiBase.ts +15 -0
  118. package/src/lib/classifyElement.ts +430 -0
  119. package/src/lib/claude-bin.ts +197 -0
  120. package/src/lib/claude-stream.ts +158 -0
  121. package/src/lib/clientProjectScanner.ts +344 -0
  122. package/src/lib/componentMatcher.ts +156 -0
  123. package/src/lib/constants.ts +573 -0
  124. package/src/lib/cssVariableUtils.ts +409 -0
  125. package/src/lib/diffParser.ts +206 -0
  126. package/src/lib/folderPicker.ts +84 -0
  127. package/src/lib/gradientParser.ts +160 -0
  128. package/src/lib/projectScanner.ts +355 -0
  129. package/src/lib/promptBuilder.ts +402 -0
  130. package/src/lib/shadowParser.ts +124 -0
  131. package/src/lib/tailwindClassParser.ts +248 -0
  132. package/src/lib/textShadowUtils.ts +106 -0
  133. package/src/lib/utils.ts +299 -0
  134. package/src/lib/validatePath.ts +40 -0
  135. package/src/proxy.ts +92 -0
  136. package/src/server/terminal-server.ts +104 -0
  137. package/src/store/changeSlice.ts +288 -0
  138. package/src/store/claudeSlice.ts +222 -0
  139. package/src/store/componentSlice.ts +90 -0
  140. package/src/store/consoleSlice.ts +51 -0
  141. package/src/store/cssVariableSlice.ts +94 -0
  142. package/src/store/elementSlice.ts +78 -0
  143. package/src/store/index.ts +35 -0
  144. package/src/store/terminalSlice.ts +30 -0
  145. package/src/store/treeSlice.ts +69 -0
  146. package/src/store/uiSlice.ts +327 -0
  147. package/src/types/changelog.ts +49 -0
  148. package/src/types/claude.ts +131 -0
  149. package/src/types/component.ts +49 -0
  150. package/src/types/cssVariables.ts +18 -0
  151. package/src/types/element.ts +21 -0
  152. package/src/types/file-system-access.d.ts +27 -0
  153. package/src/types/gradient.ts +12 -0
  154. package/src/types/messages.ts +392 -0
  155. package/src/types/shadow.ts +8 -0
  156. package/src/types/tree.ts +9 -0
  157. package/tsconfig.json +42 -0
  158. package/tsconfig.server.json +12 -0
@@ -0,0 +1,327 @@
1
+ import type { StateCreator } from 'zustand'
2
+ import type { Breakpoint } from '@/types/changelog'
3
+ import {
4
+ PANEL_DEFAULTS,
5
+ LOCAL_STORAGE_KEYS,
6
+ PREVIEW_WIDTH_MIN,
7
+ PREVIEW_WIDTH_MAX,
8
+ BREAKPOINTS,
9
+ } from '@/lib/constants'
10
+
11
+ export interface Toast {
12
+ id: string
13
+ type: 'success' | 'error' | 'info'
14
+ message: string
15
+ }
16
+
17
+ export type ConnectionStatus =
18
+ | 'disconnected'
19
+ | 'confirming'
20
+ | 'scanning'
21
+ | 'connecting'
22
+ | 'connected'
23
+ export type BridgeStatus =
24
+ | 'disconnected'
25
+ | 'checking'
26
+ | 'connected'
27
+ | 'unavailable'
28
+
29
+ export interface UISlice {
30
+ targetUrl: string | null
31
+ connectionStatus: ConnectionStatus
32
+ pendingTargetUrl: string | null
33
+ pendingFolderPath: string | null
34
+ recentUrls: string[]
35
+ activeBreakpoint: Breakpoint
36
+ leftPanelOpen: boolean
37
+ rightPanelOpen: boolean
38
+ leftPanelWidth: number
39
+ rightPanelWidth: number
40
+ activeRightTab: 'design' | 'variables' | 'changes' | 'claude' | 'console'
41
+ changeScope: 'all' | 'breakpoint-only'
42
+ pageLinks: Array<{ href: string; text: string }>
43
+ currentPagePath: string
44
+ selectionMode: boolean
45
+ viewMode: boolean
46
+ activeLeftTab: 'layers' | 'pages' | 'components' | 'terminal' | 'add-element'
47
+ previewWidth: number
48
+ toasts: Toast[]
49
+ bridgeUrl: string | null
50
+ bridgeStatus: BridgeStatus
51
+
52
+ setTargetUrl: (url: string | null) => void
53
+ setConnectionStatus: (status: ConnectionStatus) => void
54
+ setPendingConnection: (url: string, folder: string) => void
55
+ finalizeConnection: () => void
56
+ cancelPendingConnection: () => void
57
+ addRecentUrl: (url: string) => void
58
+ setActiveBreakpoint: (bp: Breakpoint) => void
59
+ toggleLeftPanel: () => void
60
+ toggleRightPanel: () => void
61
+ setLeftPanelWidth: (width: number) => void
62
+ setRightPanelWidth: (width: number) => void
63
+ setActiveRightTab: (
64
+ tab: 'design' | 'variables' | 'changes' | 'claude' | 'console',
65
+ ) => void
66
+ setChangeScope: (scope: 'all' | 'breakpoint-only') => void
67
+ setPageLinks: (links: Array<{ href: string; text: string }>) => void
68
+ setCurrentPagePath: (path: string) => void
69
+ setSelectionMode: (enabled: boolean) => void
70
+ toggleSelectionMode: () => void
71
+ toggleViewMode: () => void
72
+ setActiveLeftTab: (
73
+ tab: 'layers' | 'pages' | 'components' | 'terminal' | 'add-element',
74
+ ) => void
75
+ setPreviewWidth: (width: number) => void
76
+ showToast: (type: Toast['type'], message: string) => void
77
+ dismissToast: (id: string) => void
78
+ setBridgeUrl: (url: string | null) => void
79
+ setBridgeStatus: (status: BridgeStatus) => void
80
+ loadPersistedUI: () => void
81
+ }
82
+
83
+ export const createUISlice: StateCreator<UISlice, [], [], UISlice> = (
84
+ set,
85
+ get,
86
+ ) => ({
87
+ targetUrl: null,
88
+ connectionStatus: 'disconnected',
89
+ pendingTargetUrl: null,
90
+ pendingFolderPath: null,
91
+ recentUrls: [],
92
+ activeBreakpoint: 'desktop',
93
+ leftPanelOpen: true,
94
+ rightPanelOpen: true,
95
+ leftPanelWidth: PANEL_DEFAULTS.leftWidth,
96
+ rightPanelWidth: PANEL_DEFAULTS.rightWidth,
97
+ activeRightTab: 'design',
98
+ changeScope: 'breakpoint-only',
99
+ pageLinks: [],
100
+ currentPagePath: '/',
101
+ selectionMode: true,
102
+ viewMode: false,
103
+ activeLeftTab: 'layers',
104
+ previewWidth: 1920,
105
+ toasts: [],
106
+ bridgeUrl: null,
107
+ bridgeStatus: 'disconnected',
108
+
109
+ setTargetUrl: (url) => {
110
+ set({ targetUrl: url })
111
+ },
112
+
113
+ setConnectionStatus: (status) => {
114
+ set({ connectionStatus: status })
115
+ },
116
+
117
+ setPendingConnection: (url, folder) => {
118
+ set({
119
+ pendingTargetUrl: url,
120
+ pendingFolderPath: folder,
121
+ connectionStatus: folder ? 'confirming' : 'connecting',
122
+ // If no folder, go straight to connecting
123
+ ...(folder ? {} : { targetUrl: url }),
124
+ })
125
+ },
126
+
127
+ finalizeConnection: () => {
128
+ const { pendingTargetUrl } = get()
129
+ if (!pendingTargetUrl) return
130
+ set({
131
+ targetUrl: pendingTargetUrl,
132
+ connectionStatus: 'connecting',
133
+ pendingTargetUrl: null,
134
+ pendingFolderPath: null,
135
+ })
136
+ },
137
+
138
+ cancelPendingConnection: () => {
139
+ set({
140
+ connectionStatus: 'disconnected',
141
+ pendingTargetUrl: null,
142
+ pendingFolderPath: null,
143
+ targetUrl: null,
144
+ })
145
+ },
146
+
147
+ addRecentUrl: (url) => {
148
+ const { recentUrls } = get()
149
+ const filtered = recentUrls.filter((u) => u !== url)
150
+ const updated = [url, ...filtered].slice(0, 10)
151
+ set({ recentUrls: updated })
152
+ try {
153
+ localStorage.setItem(
154
+ LOCAL_STORAGE_KEYS.RECENT_URLS,
155
+ JSON.stringify(updated),
156
+ )
157
+ } catch {}
158
+ },
159
+
160
+ setActiveBreakpoint: (bp) => set({ activeBreakpoint: bp }),
161
+
162
+ toggleLeftPanel: () => {
163
+ const next = !get().leftPanelOpen
164
+ set({ leftPanelOpen: next })
165
+ try {
166
+ const vis = JSON.parse(
167
+ localStorage.getItem(LOCAL_STORAGE_KEYS.PANEL_VISIBILITY) || '{}',
168
+ )
169
+ vis.left = next
170
+ localStorage.setItem(
171
+ LOCAL_STORAGE_KEYS.PANEL_VISIBILITY,
172
+ JSON.stringify(vis),
173
+ )
174
+ } catch {}
175
+ },
176
+
177
+ toggleRightPanel: () => {
178
+ const next = !get().rightPanelOpen
179
+ set({ rightPanelOpen: next })
180
+ try {
181
+ const vis = JSON.parse(
182
+ localStorage.getItem(LOCAL_STORAGE_KEYS.PANEL_VISIBILITY) || '{}',
183
+ )
184
+ vis.right = next
185
+ localStorage.setItem(
186
+ LOCAL_STORAGE_KEYS.PANEL_VISIBILITY,
187
+ JSON.stringify(vis),
188
+ )
189
+ } catch {}
190
+ },
191
+
192
+ setLeftPanelWidth: (width) => {
193
+ const clamped = Math.min(
194
+ Math.max(width, PANEL_DEFAULTS.leftMin),
195
+ PANEL_DEFAULTS.leftMax,
196
+ )
197
+ set({ leftPanelWidth: clamped })
198
+ try {
199
+ const sizes = JSON.parse(
200
+ localStorage.getItem(LOCAL_STORAGE_KEYS.PANEL_SIZES) || '{}',
201
+ )
202
+ sizes.left = clamped
203
+ localStorage.setItem(
204
+ LOCAL_STORAGE_KEYS.PANEL_SIZES,
205
+ JSON.stringify(sizes),
206
+ )
207
+ } catch {}
208
+ },
209
+
210
+ setRightPanelWidth: (width) => {
211
+ const clamped = Math.min(
212
+ Math.max(width, PANEL_DEFAULTS.rightMin),
213
+ PANEL_DEFAULTS.rightMax,
214
+ )
215
+ set({ rightPanelWidth: clamped })
216
+ try {
217
+ const sizes = JSON.parse(
218
+ localStorage.getItem(LOCAL_STORAGE_KEYS.PANEL_SIZES) || '{}',
219
+ )
220
+ sizes.right = clamped
221
+ localStorage.setItem(
222
+ LOCAL_STORAGE_KEYS.PANEL_SIZES,
223
+ JSON.stringify(sizes),
224
+ )
225
+ } catch {}
226
+ },
227
+
228
+ setActiveRightTab: (tab) => set({ activeRightTab: tab }),
229
+
230
+ setChangeScope: (scope) => set({ changeScope: scope }),
231
+
232
+ setPageLinks: (links) => set({ pageLinks: links }),
233
+ setCurrentPagePath: (path) => set({ currentPagePath: path }),
234
+ setSelectionMode: (enabled) => set({ selectionMode: enabled }),
235
+ toggleSelectionMode: () => set({ selectionMode: !get().selectionMode }),
236
+
237
+ toggleViewMode: () => {
238
+ const { viewMode } = get()
239
+ if (viewMode) {
240
+ // Exit preview — restore panels
241
+ set({
242
+ viewMode: false,
243
+ leftPanelOpen: true,
244
+ rightPanelOpen: true,
245
+ activeLeftTab: 'layers',
246
+ })
247
+ } else {
248
+ // Enter preview — show left panel with pages tab, hide right panel
249
+ set({
250
+ viewMode: true,
251
+ leftPanelOpen: true,
252
+ rightPanelOpen: false,
253
+ activeLeftTab: 'pages',
254
+ })
255
+ }
256
+ },
257
+
258
+ setActiveLeftTab: (tab) => set({ activeLeftTab: tab }),
259
+
260
+ showToast: (type, message) => {
261
+ const id = Math.random().toString(36).slice(2, 9)
262
+ set((state) => ({ toasts: [...state.toasts, { id, type, message }] }))
263
+ setTimeout(() => {
264
+ set((state) => ({ toasts: state.toasts.filter((t) => t.id !== id) }))
265
+ }, 4000)
266
+ },
267
+
268
+ dismissToast: (id) => {
269
+ set((state) => ({ toasts: state.toasts.filter((t) => t.id !== id) }))
270
+ },
271
+
272
+ setPreviewWidth: (width) => {
273
+ const clamped = Math.min(
274
+ Math.max(width, PREVIEW_WIDTH_MIN),
275
+ PREVIEW_WIDTH_MAX,
276
+ )
277
+ // Auto-detect matching breakpoint
278
+ const match = (
279
+ Object.entries(BREAKPOINTS) as [
280
+ Breakpoint,
281
+ { label: string; width: number },
282
+ ][]
283
+ ).find(([, bp]) => bp.width === clamped)
284
+ set({
285
+ previewWidth: clamped,
286
+ ...(match ? { activeBreakpoint: match[0] } : {}),
287
+ })
288
+ },
289
+
290
+ setBridgeUrl: (url) => {
291
+ set({ bridgeUrl: url })
292
+ try {
293
+ if (url) {
294
+ localStorage.setItem('dev-editor:bridge-url', url)
295
+ } else {
296
+ localStorage.removeItem('dev-editor:bridge-url')
297
+ }
298
+ } catch {}
299
+ },
300
+
301
+ setBridgeStatus: (status) => set({ bridgeStatus: status }),
302
+
303
+ loadPersistedUI: () => {
304
+ try {
305
+ const urls = localStorage.getItem(LOCAL_STORAGE_KEYS.RECENT_URLS)
306
+ if (urls) set({ recentUrls: JSON.parse(urls) })
307
+
308
+ const sizes = localStorage.getItem(LOCAL_STORAGE_KEYS.PANEL_SIZES)
309
+ if (sizes) {
310
+ const parsed = JSON.parse(sizes)
311
+ if (parsed.left) set({ leftPanelWidth: parsed.left })
312
+ if (parsed.right) set({ rightPanelWidth: parsed.right })
313
+ }
314
+
315
+ const vis = localStorage.getItem(LOCAL_STORAGE_KEYS.PANEL_VISIBILITY)
316
+ if (vis) {
317
+ const parsed = JSON.parse(vis)
318
+ if (typeof parsed.left === 'boolean')
319
+ set({ leftPanelOpen: parsed.left })
320
+ if (typeof parsed.right === 'boolean')
321
+ set({ rightPanelOpen: parsed.right })
322
+ }
323
+ const bridgeUrl = localStorage.getItem('dev-editor:bridge-url')
324
+ if (bridgeUrl) set({ bridgeUrl })
325
+ } catch {}
326
+ },
327
+ })
@@ -0,0 +1,49 @@
1
+ import type { SourceInfo } from './claude'
2
+
3
+ export type Breakpoint = 'mobile' | 'tablet' | 'desktop'
4
+
5
+ export interface StyleChange {
6
+ id: string
7
+ elementSelector: string
8
+ property: string
9
+ originalValue: string
10
+ newValue: string
11
+ breakpoint: Breakpoint
12
+ timestamp: number
13
+ changeScope?: 'all' | 'breakpoint-only'
14
+ }
15
+
16
+ export interface ElementSnapshot {
17
+ selectorPath: string
18
+ tagName: string
19
+ className: string | null
20
+ elementId: string | null
21
+ attributes: Record<string, string>
22
+ innerText: string | null
23
+ computedStyles: Record<string, string>
24
+ pagePath: string
25
+ changeScope: 'all' | 'breakpoint-only'
26
+ sourceInfo?: SourceInfo | null
27
+ }
28
+
29
+ export interface UndoRedoAction {
30
+ elementSelector: string
31
+ property: string
32
+ beforeValue: string
33
+ afterValue: string
34
+ breakpoint: Breakpoint
35
+ wasNewChange: boolean
36
+ changeScope?: 'all' | 'breakpoint-only'
37
+ }
38
+
39
+ export const BREAKPOINT_WIDTHS: Record<Breakpoint, number> = {
40
+ mobile: 375,
41
+ tablet: 768,
42
+ desktop: 1280,
43
+ } as const
44
+
45
+ export const BREAKPOINT_LABELS: Record<Breakpoint, string> = {
46
+ mobile: 'Mobile',
47
+ tablet: 'Tablet',
48
+ desktop: 'Desktop',
49
+ } as const
@@ -0,0 +1,131 @@
1
+ export type ClaudeStatus =
2
+ | 'idle'
3
+ | 'analyzing'
4
+ | 'complete'
5
+ | 'applying'
6
+ | 'applied'
7
+ | 'error'
8
+
9
+ export type ClaudeErrorCode =
10
+ | 'CLI_NOT_FOUND'
11
+ | 'AUTH_REQUIRED'
12
+ | 'TIMEOUT'
13
+ | 'PARSE_FAILURE'
14
+ | 'UNKNOWN'
15
+
16
+ export interface ClaudeError {
17
+ code: ClaudeErrorCode
18
+ message: string
19
+ }
20
+
21
+ export interface DiffLine {
22
+ type: 'context' | 'addition' | 'removal'
23
+ content: string
24
+ }
25
+
26
+ export interface DiffHunk {
27
+ header: string
28
+ lines: DiffLine[]
29
+ }
30
+
31
+ export interface ParsedDiff {
32
+ filePath: string
33
+ hunks: DiffHunk[]
34
+ linesAdded: number
35
+ linesRemoved: number
36
+ }
37
+
38
+ export interface ClaudeAnalyzeRequest {
39
+ changelog: string
40
+ projectRoot: string
41
+ /** When provided (from AI Scan), used as-is instead of wrapping changelog. */
42
+ smartPrompt?: string
43
+ }
44
+
45
+ export interface ClaudeAnalyzeResponse {
46
+ sessionId: string
47
+ diffs: ParsedDiff[]
48
+ summary: string
49
+ }
50
+
51
+ export interface ClaudeApplyRequest {
52
+ sessionId: string
53
+ projectRoot: string
54
+ }
55
+
56
+ export interface ClaudeApplyResponse {
57
+ success: boolean
58
+ filesModified: string[]
59
+ summary: string
60
+ }
61
+
62
+ export interface ClaudeStatusResponse {
63
+ available: boolean
64
+ version?: string
65
+ error?: string
66
+ }
67
+
68
+ export interface ClaudeScanRequest {
69
+ changelog: string
70
+ projectRoot: string
71
+ }
72
+
73
+ export type ScanGroupCategory =
74
+ | 'typography'
75
+ | 'spacing'
76
+ | 'colors'
77
+ | 'layout'
78
+ | 'borders'
79
+ | 'background'
80
+ | 'effects'
81
+ | 'mixed'
82
+
83
+ export interface ScanGroup {
84
+ label: string
85
+ category: ScanGroupCategory
86
+ changeCount: number
87
+ suggestedFiles: string[]
88
+ }
89
+
90
+ export interface ClaudeScanResponse {
91
+ smartPrompt: string
92
+ intent: string
93
+ groups: ScanGroup[]
94
+ warnings: string[]
95
+ }
96
+
97
+ export interface RouteEntry {
98
+ urlPattern: string // e.g. "/", "/about", "/blog/[slug]" (web only)
99
+ filePath: string // relative to projectRoot
100
+ type: 'page' | 'layout' | 'loading' | 'error' | 'not-found' | 'template'
101
+ }
102
+
103
+ export interface ComponentEntry {
104
+ name: string // PascalCase: "Button", "HomeScreen", "ProfileWidget"
105
+ filePath: string // relative to projectRoot
106
+ nameLower: string // lowercase for matching: "button", "homescreen"
107
+ category: 'component' | 'screen' | 'page' | 'layout' | 'widget' | 'view'
108
+ }
109
+
110
+ export interface FileMap {
111
+ routes: RouteEntry[]
112
+ components: ComponentEntry[]
113
+ }
114
+
115
+ export interface SourceInfo {
116
+ fileName: string // Absolute path from React fiber _debugSource
117
+ lineNumber: number
118
+ columnNumber?: number
119
+ componentName: string | null
120
+ componentChain: string[]
121
+ }
122
+
123
+ export interface ProjectScanResult {
124
+ framework: string | null
125
+ cssStrategy: string[]
126
+ cssFiles: string[]
127
+ srcDirs: string[]
128
+ packageName: string | null
129
+ fileMap?: FileMap
130
+ assetDirs?: string[]
131
+ }
@@ -0,0 +1,49 @@
1
+ // Component detection types for the Components tab
2
+
3
+ export type DetectionMethod =
4
+ | 'semantic-html'
5
+ | 'custom-element'
6
+ | 'aria-role'
7
+ | 'class-pattern'
8
+ | 'data-attribute'
9
+
10
+ export interface ComponentBoundingRect {
11
+ top: number
12
+ left: number
13
+ width: number
14
+ height: number
15
+ }
16
+
17
+ export interface VariantOption {
18
+ label: string
19
+ className: string | null
20
+ removeClassNames: string[] | null
21
+ pseudoState: string | null
22
+ pseudoStyles: Record<string, string> | null
23
+ }
24
+
25
+ export interface VariantGroup {
26
+ groupName: string
27
+ type: 'class' | 'pseudo'
28
+ options: VariantOption[]
29
+ activeIndex: number
30
+ }
31
+
32
+ export interface DetectedComponent {
33
+ selectorPath: string
34
+ name: string
35
+ tagName: string
36
+ detectionMethod: DetectionMethod
37
+ className: string | null
38
+ elementId: string | null
39
+ innerText: string | null
40
+ boundingRect: ComponentBoundingRect
41
+ variants: VariantGroup[]
42
+ childComponentCount: number
43
+ }
44
+
45
+ export interface CreatedComponent {
46
+ name: string
47
+ selectorPath: string
48
+ timestamp: number
49
+ }
@@ -0,0 +1,18 @@
1
+ export interface CSSVariableDefinition {
2
+ value: string // raw value from stylesheet
3
+ resolvedValue: string // computed value from getComputedStyle
4
+ selector: string // e.g. ':root'
5
+ scope?: 'root' | 'dark' | 'media-dark' | 'custom' // theme scope
6
+ }
7
+
8
+ export interface CSSVariableFamilyMember {
9
+ name: string
10
+ suffix: string
11
+ value: string
12
+ resolvedValue: string
13
+ }
14
+
15
+ export interface CSSVariableFamily {
16
+ prefix: string // e.g. '--primary'
17
+ members: CSSVariableFamilyMember[]
18
+ }
@@ -0,0 +1,21 @@
1
+ export interface BoundingRect {
2
+ x: number
3
+ y: number
4
+ width: number
5
+ height: number
6
+ top: number
7
+ right: number
8
+ bottom: number
9
+ left: number
10
+ }
11
+
12
+ export interface SelectedElement {
13
+ selectorPath: string | null
14
+ tagName: string | null
15
+ className: string | null
16
+ id: string | null
17
+ attributes: Record<string, string>
18
+ innerText: string | null
19
+ computedStyles: Record<string, string>
20
+ boundingRect: BoundingRect | null
21
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Type augmentations for the File System Access API.
3
+ * These APIs are available in Chromium-based browsers (Chrome, Edge)
4
+ * but not yet in TypeScript's standard lib definitions.
5
+ */
6
+
7
+ interface FileSystemDirectoryHandle {
8
+ entries(): AsyncIterableIterator<[string, FileSystemHandle]>
9
+ getDirectoryHandle(
10
+ name: string,
11
+ options?: { create?: boolean },
12
+ ): Promise<FileSystemDirectoryHandle>
13
+ getFileHandle(
14
+ name: string,
15
+ options?: { create?: boolean },
16
+ ): Promise<FileSystemFileHandle>
17
+ }
18
+
19
+ interface FileSystemFileHandle {
20
+ getFile(): Promise<File>
21
+ }
22
+
23
+ interface Window {
24
+ showDirectoryPicker?(options?: {
25
+ mode?: 'read' | 'readwrite'
26
+ }): Promise<FileSystemDirectoryHandle>
27
+ }
@@ -0,0 +1,12 @@
1
+ export interface GradientStop {
2
+ color: string
3
+ position: number
4
+ opacity: number
5
+ }
6
+
7
+ export interface GradientData {
8
+ type: 'linear' | 'radial' | 'conic'
9
+ angle: number
10
+ stops: GradientStop[]
11
+ repeat?: boolean
12
+ }