@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,275 @@
1
+ 'use client'
2
+
3
+ import { useCallback, useState, useRef, useEffect } from 'react'
4
+ import { useEditorStore } from '@/store'
5
+ import { sendViaIframe } from '@/hooks/usePostMessage'
6
+ import { ElementBreadcrumb } from './ElementBreadcrumb'
7
+ import { ElementLogBox } from '../ElementLogBox'
8
+ import { BorderSection } from './BorderSection'
9
+ import { LayoutSection } from './LayoutSection'
10
+ import { SizeSection } from './SizeSection'
11
+ import { PositionSection } from './PositionSection'
12
+ import { AppearanceSection } from './AppearanceSection'
13
+ import { TextSection } from './TextSection'
14
+ import { BackgroundSection } from './BackgroundSection'
15
+ import { ShadowBlurSection } from './ShadowBlurSection'
16
+ import { PropertiesSection } from './PropertiesSection'
17
+ import { SVGSection } from './SVGSection'
18
+ import { DesignCSSTabToggle } from './DesignCSSTabToggle'
19
+ import { CSSRawView } from './CSSRawView'
20
+
21
+ const SVG_TAG_NAMES = new Set([
22
+ 'svg',
23
+ 'path',
24
+ 'circle',
25
+ 'rect',
26
+ 'line',
27
+ 'polyline',
28
+ 'polygon',
29
+ 'ellipse',
30
+ 'g',
31
+ 'use',
32
+ 'text',
33
+ 'tspan',
34
+ 'image',
35
+ 'clippath',
36
+ 'mask',
37
+ 'defs',
38
+ ])
39
+
40
+ function ChangeScopeToggle() {
41
+ const changeScope = useEditorStore((s) => s.changeScope)
42
+ const setChangeScope = useEditorStore((s) => s.setChangeScope)
43
+ const updateAllSnapshotsScope = useEditorStore(
44
+ (s) => s.updateAllSnapshotsScope,
45
+ )
46
+ const activeBreakpoint = useEditorStore((s) => s.activeBreakpoint)
47
+
48
+ const handleScopeChange = useCallback(
49
+ (scope: 'all' | 'breakpoint-only') => {
50
+ setChangeScope(scope)
51
+ updateAllSnapshotsScope(scope)
52
+ },
53
+ [setChangeScope, updateAllSnapshotsScope],
54
+ )
55
+
56
+ return (
57
+ <div
58
+ className="flex items-center justify-between px-3 py-1.5"
59
+ style={{ borderBottom: '1px solid var(--border)' }}
60
+ >
61
+ <span className="text-[11px]" style={{ color: 'var(--text-muted)' }}>
62
+ Apply to
63
+ </span>
64
+ <div
65
+ className="flex items-center gap-0.5 rounded p-0.5"
66
+ style={{ background: 'var(--bg-tertiary)' }}
67
+ >
68
+ <button
69
+ onClick={() => handleScopeChange('all')}
70
+ className="px-2 py-0.5 text-[11px] rounded transition-colors"
71
+ style={{
72
+ background:
73
+ changeScope === 'all'
74
+ ? 'var(--accent-bg, rgba(74,158,255,0.15))'
75
+ : 'transparent',
76
+ color:
77
+ changeScope === 'all' ? 'var(--accent)' : 'var(--text-muted)',
78
+ }}
79
+ >
80
+ All
81
+ </button>
82
+ <button
83
+ onClick={() => handleScopeChange('breakpoint-only')}
84
+ className="px-2 py-0.5 text-[11px] rounded transition-colors capitalize"
85
+ style={{
86
+ background:
87
+ changeScope === 'breakpoint-only'
88
+ ? 'var(--accent-bg, rgba(74,158,255,0.15))'
89
+ : 'transparent',
90
+ color:
91
+ changeScope === 'breakpoint-only'
92
+ ? 'var(--accent)'
93
+ : 'var(--text-muted)',
94
+ }}
95
+ >
96
+ {activeBreakpoint} only
97
+ </button>
98
+ </div>
99
+ </div>
100
+ )
101
+ }
102
+
103
+ const MIN_TOP_HEIGHT = 80
104
+ const DEFAULT_TOP_HEIGHT = 240
105
+ const MAX_TOP_HEIGHT = 500
106
+
107
+ export function DesignPanel() {
108
+ const selectorPath = useEditorStore((s) => s.selectorPath)
109
+ const tagName = useEditorStore((s) => s.tagName)
110
+ const isSVGElement = !!tagName && SVG_TAG_NAMES.has(tagName.toLowerCase())
111
+ const [activeTab, setActiveTab] = useState<'design' | 'css'>('design')
112
+ const [topHeight, setTopHeight] = useState(DEFAULT_TOP_HEIGHT)
113
+ const isDragging = useRef(false)
114
+ const startY = useRef(0)
115
+ const startHeight = useRef(0)
116
+
117
+ // Hide selection overlay while user interacts with design panel controls
118
+ const designRef = useRef<HTMLDivElement>(null)
119
+ const overlayHidden = useRef(false)
120
+
121
+ useEffect(() => {
122
+ const el = designRef.current
123
+ if (!el) return
124
+
125
+ const hideOverlay = () => {
126
+ if (!overlayHidden.current) {
127
+ overlayHidden.current = true
128
+ sendViaIframe({ type: 'HIDE_SELECTION_OVERLAY' })
129
+ }
130
+ }
131
+
132
+ const showOverlay = () => {
133
+ if (overlayHidden.current) {
134
+ overlayHidden.current = false
135
+ sendViaIframe({ type: 'SHOW_SELECTION_OVERLAY' })
136
+ }
137
+ }
138
+
139
+ // Any pointer interaction inside the design panel hides the overlay
140
+ el.addEventListener('pointerdown', hideOverlay)
141
+
142
+ // Clicks outside the design panel restore the overlay
143
+ const onDocumentClick = (e: MouseEvent) => {
144
+ if (overlayHidden.current && !el.contains(e.target as Node)) {
145
+ showOverlay()
146
+ }
147
+ }
148
+ document.addEventListener('pointerdown', onDocumentClick)
149
+
150
+ // Also restore when an input blurs and focus leaves the panel entirely
151
+ const onFocusOut = (e: FocusEvent) => {
152
+ if (!overlayHidden.current) return
153
+ const related = e.relatedTarget as Node | null
154
+ if (!related || !el.contains(related)) {
155
+ // Small delay so clicking another control within the panel doesn't flash
156
+ setTimeout(() => {
157
+ if (
158
+ overlayHidden.current &&
159
+ document.activeElement &&
160
+ !el.contains(document.activeElement)
161
+ ) {
162
+ showOverlay()
163
+ }
164
+ }, 150)
165
+ }
166
+ }
167
+ el.addEventListener('focusout', onFocusOut)
168
+
169
+ return () => {
170
+ el.removeEventListener('pointerdown', hideOverlay)
171
+ document.removeEventListener('pointerdown', onDocumentClick)
172
+ el.removeEventListener('focusout', onFocusOut)
173
+ }
174
+ }, [])
175
+
176
+ const handleDragStart = useCallback(
177
+ (e: React.MouseEvent) => {
178
+ e.preventDefault()
179
+ isDragging.current = true
180
+ startY.current = e.clientY
181
+ startHeight.current = topHeight
182
+
183
+ const handleDragMove = (ev: MouseEvent) => {
184
+ if (!isDragging.current) return
185
+ const delta = ev.clientY - startY.current
186
+ const next = Math.min(
187
+ MAX_TOP_HEIGHT,
188
+ Math.max(MIN_TOP_HEIGHT, startHeight.current + delta),
189
+ )
190
+ setTopHeight(next)
191
+ }
192
+
193
+ const handleDragEnd = () => {
194
+ isDragging.current = false
195
+ document.removeEventListener('mousemove', handleDragMove)
196
+ document.removeEventListener('mouseup', handleDragEnd)
197
+ }
198
+
199
+ document.addEventListener('mousemove', handleDragMove)
200
+ document.addEventListener('mouseup', handleDragEnd)
201
+ },
202
+ [topHeight],
203
+ )
204
+
205
+ if (!selectorPath) {
206
+ return (
207
+ <div
208
+ className="flex items-center justify-center h-full text-xs"
209
+ style={{ color: 'var(--text-muted)' }}
210
+ >
211
+ Select an element to edit
212
+ </div>
213
+ )
214
+ }
215
+
216
+ return (
217
+ <div className="flex flex-col h-full">
218
+ {/* Top section: element info (resizable) */}
219
+ <div
220
+ className="flex-shrink-0 flex flex-col"
221
+ style={{ height: topHeight }}
222
+ >
223
+ <ElementBreadcrumb />
224
+ <ChangeScopeToggle />
225
+ <div className="flex-1 overflow-y-auto min-h-0">
226
+ <ElementLogBox />
227
+ </div>
228
+ </div>
229
+
230
+ {/* Drag handle */}
231
+ <div
232
+ onMouseDown={handleDragStart}
233
+ className="flex-shrink-0 flex items-center justify-center"
234
+ style={{
235
+ height: 6,
236
+ cursor: 'row-resize',
237
+ background: 'var(--bg-primary)',
238
+ borderTop: '1px solid var(--border)',
239
+ borderBottom: '1px solid var(--border)',
240
+ }}
241
+ >
242
+ <div
243
+ style={{
244
+ width: 32,
245
+ height: 2,
246
+ borderRadius: 1,
247
+ background: 'var(--text-muted)',
248
+ opacity: 0.4,
249
+ }}
250
+ />
251
+ </div>
252
+
253
+ {/* Bottom section: design properties (fills remaining) */}
254
+ <div ref={designRef} className="flex-1 overflow-y-auto min-h-0">
255
+ <DesignCSSTabToggle activeTab={activeTab} onTabChange={setActiveTab} />
256
+ {activeTab === 'design' ? (
257
+ <>
258
+ <PositionSection />
259
+ <LayoutSection />
260
+ <SizeSection />
261
+ <AppearanceSection />
262
+ {isSVGElement && <SVGSection />}
263
+ <TextSection />
264
+ <BackgroundSection />
265
+ <BorderSection />
266
+ <ShadowBlurSection />
267
+ <PropertiesSection />
268
+ </>
269
+ ) : (
270
+ <CSSRawView />
271
+ )}
272
+ </div>
273
+ </div>
274
+ )
275
+ }
@@ -0,0 +1,51 @@
1
+ 'use client'
2
+
3
+ import { useEditorStore } from '@/store'
4
+
5
+ export function ElementBreadcrumb() {
6
+ const selectorPath = useEditorStore((state) => state.selectorPath)
7
+
8
+ if (!selectorPath) {
9
+ return (
10
+ <div
11
+ className="px-3 py-2 text-xs"
12
+ style={{
13
+ borderBottom: '1px solid var(--border)',
14
+ color: 'var(--text-muted)',
15
+ }}
16
+ >
17
+ No element selected
18
+ </div>
19
+ )
20
+ }
21
+
22
+ // Parse the selector path to extract individual elements
23
+ const parts = selectorPath.split(' > ').map((part) => part.trim())
24
+
25
+ return (
26
+ <div
27
+ className="px-3 py-2 text-xs flex items-center gap-1 overflow-x-auto"
28
+ style={{
29
+ borderBottom: '1px solid var(--border)',
30
+ color: 'var(--text-secondary)',
31
+ }}
32
+ >
33
+ {parts.map((part, index) => (
34
+ <span key={index} className="flex items-center gap-1 flex-shrink-0">
35
+ {index > 0 && <span style={{ color: 'var(--text-muted)' }}>›</span>}
36
+ <span
37
+ className="hover:underline cursor-pointer"
38
+ style={{
39
+ color:
40
+ index === parts.length - 1
41
+ ? 'var(--accent)'
42
+ : 'var(--text-secondary)',
43
+ }}
44
+ >
45
+ {part}
46
+ </span>
47
+ </span>
48
+ ))}
49
+ </div>
50
+ )
51
+ }