@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,322 @@
1
+ 'use client'
2
+
3
+ import { useState, useCallback, useEffect, useRef } from 'react'
4
+
5
+ const NAV_ITEMS = [
6
+ { id: 'how-it-works', label: 'How It Works' },
7
+ { id: 'use-cases', label: 'Use Cases' },
8
+ { id: 'quick-start', label: 'Quick Start' },
9
+ { id: 'framework-guides', label: 'Framework Guides' },
10
+ { id: 'troubleshooting', label: 'Troubleshooting' },
11
+ { id: 'faq', label: 'FAQ' },
12
+ ] as const
13
+
14
+ export function Sidebar() {
15
+ const [activeId, setActiveId] = useState<string>('how-it-works')
16
+ const observerRef = useRef<IntersectionObserver | null>(null)
17
+
18
+ useEffect(() => {
19
+ observerRef.current = new IntersectionObserver(
20
+ (entries) => {
21
+ // Find the topmost visible section
22
+ const visible = entries
23
+ .filter((e) => e.isIntersecting)
24
+ .sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)
25
+ if (visible.length > 0) {
26
+ setActiveId(visible[0].target.id)
27
+ }
28
+ },
29
+ { rootMargin: '-80px 0px -60% 0px', threshold: 0 },
30
+ )
31
+
32
+ const sections = NAV_ITEMS.map((item) =>
33
+ document.getElementById(item.id),
34
+ ).filter(Boolean) as HTMLElement[]
35
+
36
+ sections.forEach((el) => observerRef.current!.observe(el))
37
+
38
+ return () => observerRef.current?.disconnect()
39
+ }, [])
40
+
41
+ const handleClick = (id: string) => {
42
+ const el = document.getElementById(id)
43
+ if (el) {
44
+ el.scrollIntoView({ behavior: 'smooth', block: 'start' })
45
+ }
46
+ }
47
+
48
+ return (
49
+ <nav
50
+ className="hidden lg:flex flex-col gap-1 sticky top-12 self-start"
51
+ style={{ minWidth: 160 }}
52
+ >
53
+ <span
54
+ className="text-sm font-semibold uppercase tracking-wider mb-2 px-2"
55
+ style={{ color: 'var(--text-muted)' }}
56
+ >
57
+ On this page
58
+ </span>
59
+ {NAV_ITEMS.map((item) => (
60
+ <button
61
+ key={item.id}
62
+ onClick={() => handleClick(item.id)}
63
+ className="text-left text-sm px-2 py-1.5 rounded transition-colors"
64
+ style={{
65
+ color:
66
+ activeId === item.id ? 'var(--accent)' : 'var(--text-secondary)',
67
+ background:
68
+ activeId === item.id ? 'var(--accent-bg)' : 'transparent',
69
+ borderLeft:
70
+ activeId === item.id
71
+ ? '2px solid var(--accent)'
72
+ : '2px solid transparent',
73
+ }}
74
+ >
75
+ {item.label}
76
+ </button>
77
+ ))}
78
+ </nav>
79
+ )
80
+ }
81
+
82
+ const FRAMEWORK_IDS = [
83
+ 'nextjs',
84
+ 'vite-react',
85
+ 'create-react-app',
86
+ 'plain-html',
87
+ 'react-native-expo',
88
+ 'vue-nuxt',
89
+ 'svelte-sveltekit',
90
+ ] as const
91
+
92
+ type FrameworkId = (typeof FRAMEWORK_IDS)[number]
93
+
94
+ export function CopyButton({ text }: { text: string }) {
95
+ const [copied, setCopied] = useState(false)
96
+
97
+ const handleCopy = useCallback(async () => {
98
+ try {
99
+ await navigator.clipboard.writeText(text)
100
+ setCopied(true)
101
+ setTimeout(() => setCopied(false), 2000)
102
+ } catch {
103
+ // Fallback: user can manually select & copy
104
+ }
105
+ }, [text])
106
+
107
+ return (
108
+ <button
109
+ onClick={handleCopy}
110
+ className="px-3 py-1 text-xs font-medium rounded whitespace-nowrap transition-colors shrink-0"
111
+ style={{
112
+ background: copied ? 'var(--success)' : 'var(--accent)',
113
+ color: '#fff',
114
+ }}
115
+ >
116
+ {copied ? 'Copied!' : 'Copy'}
117
+ </button>
118
+ )
119
+ }
120
+
121
+ export function CodeBlock({
122
+ code,
123
+ copyText,
124
+ language,
125
+ }: {
126
+ code: string
127
+ copyText?: string
128
+ language?: string
129
+ }) {
130
+ return (
131
+ <div
132
+ className="rounded-md overflow-hidden"
133
+ style={{ border: '1px solid var(--border)' }}
134
+ >
135
+ <div
136
+ className="flex items-center justify-between px-3 py-1.5"
137
+ style={{
138
+ background: 'var(--bg-tertiary)',
139
+ borderBottom: '1px solid var(--border)',
140
+ }}
141
+ >
142
+ <span className="text-sm" style={{ color: 'var(--text-muted)' }}>
143
+ {language || 'html'}
144
+ </span>
145
+ <CopyButton text={copyText || code} />
146
+ </div>
147
+ <pre
148
+ className="p-3 overflow-x-auto text-sm leading-relaxed m-0"
149
+ style={{
150
+ background: 'var(--bg-primary)',
151
+ color: 'var(--text-primary)',
152
+ }}
153
+ >
154
+ <code>{code}</code>
155
+ </pre>
156
+ </div>
157
+ )
158
+ }
159
+
160
+ export function FrameworkAccordion({
161
+ children,
162
+ }: {
163
+ children: React.ReactNode
164
+ }) {
165
+ const [openSections, setOpenSections] = useState<Set<string>>(
166
+ new Set(['nextjs']),
167
+ )
168
+
169
+ const toggle = useCallback((id: string) => {
170
+ setOpenSections((prev) => {
171
+ const next = new Set(prev)
172
+ if (next.has(id)) {
173
+ next.delete(id)
174
+ } else {
175
+ next.add(id)
176
+ }
177
+ return next
178
+ })
179
+ }, [])
180
+
181
+ return (
182
+ <AccordionContext.Provider value={{ openSections, toggle }}>
183
+ <div className="flex flex-col gap-2">{children}</div>
184
+ </AccordionContext.Provider>
185
+ )
186
+ }
187
+
188
+ import { createContext, useContext } from 'react'
189
+
190
+ const AccordionContext = createContext<{
191
+ openSections: Set<string>
192
+ toggle: (id: string) => void
193
+ }>({
194
+ openSections: new Set(),
195
+ toggle: () => {},
196
+ })
197
+
198
+ export function FrameworkSection({
199
+ id,
200
+ title,
201
+ icon,
202
+ children,
203
+ }: {
204
+ id: string
205
+ title: string
206
+ icon: string
207
+ children: React.ReactNode
208
+ }) {
209
+ const { openSections, toggle } = useContext(AccordionContext)
210
+ const isOpen = openSections.has(id)
211
+
212
+ return (
213
+ <div
214
+ className="rounded-md overflow-hidden"
215
+ style={{ border: '1px solid var(--border)' }}
216
+ >
217
+ <button
218
+ onClick={() => toggle(id)}
219
+ className="w-full flex items-center gap-3 px-4 py-3 text-left transition-colors"
220
+ style={{
221
+ background: isOpen ? 'var(--bg-tertiary)' : 'var(--bg-secondary)',
222
+ color: 'var(--text-primary)',
223
+ }}
224
+ >
225
+ <span className="text-base">{icon}</span>
226
+ <span className="flex-1 text-base font-medium">{title}</span>
227
+ <span
228
+ className="text-xs transition-transform"
229
+ style={{
230
+ color: 'var(--text-muted)',
231
+ transform: isOpen ? 'rotate(90deg)' : 'rotate(0deg)',
232
+ }}
233
+ >
234
+ &#9654;
235
+ </span>
236
+ </button>
237
+ {isOpen && (
238
+ <div
239
+ className="px-4 py-4 flex flex-col gap-4"
240
+ style={{ background: 'var(--bg-secondary)' }}
241
+ >
242
+ {children}
243
+ </div>
244
+ )}
245
+ </div>
246
+ )
247
+ }
248
+
249
+ export function FaqAccordion({ children }: { children: React.ReactNode }) {
250
+ const [openSections, setOpenSections] = useState<Set<string>>(new Set())
251
+
252
+ const toggle = useCallback((id: string) => {
253
+ setOpenSections((prev) => {
254
+ const next = new Set(prev)
255
+ if (next.has(id)) {
256
+ next.delete(id)
257
+ } else {
258
+ next.add(id)
259
+ }
260
+ return next
261
+ })
262
+ }, [])
263
+
264
+ return (
265
+ <AccordionContext.Provider value={{ openSections, toggle }}>
266
+ <div className="flex flex-col gap-2">{children}</div>
267
+ </AccordionContext.Provider>
268
+ )
269
+ }
270
+
271
+ export function FaqSection({
272
+ id,
273
+ question,
274
+ children,
275
+ }: {
276
+ id: string
277
+ question: string
278
+ children: React.ReactNode
279
+ }) {
280
+ const { openSections, toggle } = useContext(AccordionContext)
281
+ const isOpen = openSections.has(id)
282
+
283
+ return (
284
+ <div
285
+ className="rounded-md overflow-hidden"
286
+ style={{ border: '1px solid var(--border)' }}
287
+ >
288
+ <button
289
+ onClick={() => toggle(id)}
290
+ className="w-full flex items-center gap-3 px-4 py-3 text-left transition-colors"
291
+ style={{
292
+ background: isOpen ? 'var(--bg-tertiary)' : 'var(--bg-secondary)',
293
+ color: 'var(--text-primary)',
294
+ }}
295
+ >
296
+ <span className="flex-1 text-base font-medium">{question}</span>
297
+ <span
298
+ className="text-xs transition-transform"
299
+ style={{
300
+ color: 'var(--text-muted)',
301
+ transform: isOpen ? 'rotate(90deg)' : 'rotate(0deg)',
302
+ }}
303
+ >
304
+ &#9654;
305
+ </span>
306
+ </button>
307
+ {isOpen && (
308
+ <div
309
+ className="px-4 py-4"
310
+ style={{
311
+ background: 'var(--bg-secondary)',
312
+ color: 'var(--text-secondary)',
313
+ fontSize: '0.9rem',
314
+ lineHeight: 1.6,
315
+ }}
316
+ >
317
+ {children}
318
+ </div>
319
+ )}
320
+ </div>
321
+ )
322
+ }
@@ -0,0 +1,7 @@
1
+ export default function DocsLayout({
2
+ children,
3
+ }: {
4
+ children: React.ReactNode
5
+ }) {
6
+ return <>{children}</>
7
+ }