@aprovan/patchwork 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 (225) hide show
  1. package/.eslintrc.json +22 -0
  2. package/.github/workflows/publish.yml +41 -0
  3. package/.prettierignore +17 -0
  4. package/LICENSE +373 -0
  5. package/README.md +15 -0
  6. package/apps/chat/.utcp_config.json +14 -0
  7. package/apps/chat/.working/widgets/27060b91-a2a5-4272-b243-6eb904bd4070/main.tsx +107 -0
  8. package/apps/chat/index.html +17 -0
  9. package/apps/chat/node_modules/.bin/autoprefixer +17 -0
  10. package/apps/chat/node_modules/.bin/browserslist +17 -0
  11. package/apps/chat/node_modules/.bin/conc +17 -0
  12. package/apps/chat/node_modules/.bin/concurrently +17 -0
  13. package/apps/chat/node_modules/.bin/copilot-proxy +17 -0
  14. package/apps/chat/node_modules/.bin/jiti +17 -0
  15. package/apps/chat/node_modules/.bin/tailwind +17 -0
  16. package/apps/chat/node_modules/.bin/tailwindcss +17 -0
  17. package/apps/chat/node_modules/.bin/tsc +17 -0
  18. package/apps/chat/node_modules/.bin/tsserver +17 -0
  19. package/apps/chat/node_modules/.bin/tsx +17 -0
  20. package/apps/chat/node_modules/.bin/vite +17 -0
  21. package/apps/chat/package.json +55 -0
  22. package/apps/chat/postcss.config.js +6 -0
  23. package/apps/chat/src/App.tsx +7 -0
  24. package/apps/chat/src/components/ui/avatar.tsx +48 -0
  25. package/apps/chat/src/components/ui/badge.tsx +36 -0
  26. package/apps/chat/src/components/ui/button.tsx +56 -0
  27. package/apps/chat/src/components/ui/card.tsx +86 -0
  28. package/apps/chat/src/components/ui/collapsible.tsx +9 -0
  29. package/apps/chat/src/components/ui/dialog.tsx +60 -0
  30. package/apps/chat/src/components/ui/input.tsx +25 -0
  31. package/apps/chat/src/components/ui/scroll-area.tsx +46 -0
  32. package/apps/chat/src/index.css +190 -0
  33. package/apps/chat/src/lib/utils.ts +6 -0
  34. package/apps/chat/src/main.tsx +10 -0
  35. package/apps/chat/src/pages/ChatPage.tsx +460 -0
  36. package/apps/chat/tailwind.config.js +71 -0
  37. package/apps/chat/tsconfig.json +25 -0
  38. package/apps/chat/vite.config.ts +26 -0
  39. package/package.json +35 -0
  40. package/packages/bobbin/node_modules/.bin/esbuild +14 -0
  41. package/packages/bobbin/node_modules/.bin/jiti +17 -0
  42. package/packages/bobbin/node_modules/.bin/tsc +17 -0
  43. package/packages/bobbin/node_modules/.bin/tsserver +17 -0
  44. package/packages/bobbin/node_modules/.bin/tsup +17 -0
  45. package/packages/bobbin/node_modules/.bin/tsup-node +17 -0
  46. package/packages/bobbin/node_modules/.bin/tsx +17 -0
  47. package/packages/bobbin/package.json +30 -0
  48. package/packages/bobbin/src/Bobbin.tsx +89 -0
  49. package/packages/bobbin/src/components/EditPanel/EditPanel.tsx +376 -0
  50. package/packages/bobbin/src/components/EditPanel/controls/ColorPicker.tsx +138 -0
  51. package/packages/bobbin/src/components/EditPanel/controls/QuickSelectDropdown.tsx +142 -0
  52. package/packages/bobbin/src/components/EditPanel/controls/SliderInput.tsx +94 -0
  53. package/packages/bobbin/src/components/EditPanel/controls/SpacingControl.tsx +285 -0
  54. package/packages/bobbin/src/components/EditPanel/controls/ToggleGroup.tsx +37 -0
  55. package/packages/bobbin/src/components/EditPanel/controls/TokenDropdown.tsx +33 -0
  56. package/packages/bobbin/src/components/EditPanel/sections/AnnotationSection.tsx +136 -0
  57. package/packages/bobbin/src/components/EditPanel/sections/BackgroundSection.tsx +79 -0
  58. package/packages/bobbin/src/components/EditPanel/sections/EffectsSection.tsx +85 -0
  59. package/packages/bobbin/src/components/EditPanel/sections/LayoutSection.tsx +224 -0
  60. package/packages/bobbin/src/components/EditPanel/sections/SectionWrapper.tsx +57 -0
  61. package/packages/bobbin/src/components/EditPanel/sections/SizeSection.tsx +166 -0
  62. package/packages/bobbin/src/components/EditPanel/sections/SpacingSection.tsx +69 -0
  63. package/packages/bobbin/src/components/EditPanel/sections/TypographySection.tsx +148 -0
  64. package/packages/bobbin/src/components/Inspector/Inspector.tsx +221 -0
  65. package/packages/bobbin/src/components/Overlay/ControlHandles.tsx +572 -0
  66. package/packages/bobbin/src/components/Overlay/MarginPaddingOverlay.tsx +229 -0
  67. package/packages/bobbin/src/components/Overlay/SelectionOverlay.tsx +73 -0
  68. package/packages/bobbin/src/components/Pill/Pill.tsx +155 -0
  69. package/packages/bobbin/src/components/ThemeToggle/ThemeToggle.tsx +72 -0
  70. package/packages/bobbin/src/core/changeSerializer.ts +139 -0
  71. package/packages/bobbin/src/core/useBobbin.ts +399 -0
  72. package/packages/bobbin/src/core/useChangeTracker.ts +186 -0
  73. package/packages/bobbin/src/core/useClipboard.ts +21 -0
  74. package/packages/bobbin/src/core/useElementSelection.ts +146 -0
  75. package/packages/bobbin/src/index.ts +46 -0
  76. package/packages/bobbin/src/tokens/borders.ts +19 -0
  77. package/packages/bobbin/src/tokens/colors.ts +150 -0
  78. package/packages/bobbin/src/tokens/index.ts +37 -0
  79. package/packages/bobbin/src/tokens/shadows.ts +10 -0
  80. package/packages/bobbin/src/tokens/spacing.ts +37 -0
  81. package/packages/bobbin/src/tokens/typography.ts +51 -0
  82. package/packages/bobbin/src/types.ts +157 -0
  83. package/packages/bobbin/src/utils/animation.ts +40 -0
  84. package/packages/bobbin/src/utils/dom.ts +36 -0
  85. package/packages/bobbin/src/utils/selectors.ts +76 -0
  86. package/packages/bobbin/tsconfig.json +10 -0
  87. package/packages/bobbin/tsup.config.ts +10 -0
  88. package/packages/compiler/node_modules/.bin/esbuild +17 -0
  89. package/packages/compiler/node_modules/.bin/jiti +17 -0
  90. package/packages/compiler/node_modules/.bin/tsc +17 -0
  91. package/packages/compiler/node_modules/.bin/tsserver +17 -0
  92. package/packages/compiler/node_modules/.bin/tsup +17 -0
  93. package/packages/compiler/node_modules/.bin/tsup-node +17 -0
  94. package/packages/compiler/node_modules/.bin/tsx +17 -0
  95. package/packages/compiler/package.json +38 -0
  96. package/packages/compiler/src/compiler.ts +258 -0
  97. package/packages/compiler/src/images/index.ts +13 -0
  98. package/packages/compiler/src/images/loader.ts +234 -0
  99. package/packages/compiler/src/images/registry.ts +112 -0
  100. package/packages/compiler/src/index.ts +141 -0
  101. package/packages/compiler/src/mount/bridge.ts +399 -0
  102. package/packages/compiler/src/mount/embedded.ts +306 -0
  103. package/packages/compiler/src/mount/iframe.ts +433 -0
  104. package/packages/compiler/src/mount/index.ts +18 -0
  105. package/packages/compiler/src/schemas.ts +169 -0
  106. package/packages/compiler/src/transforms/cdn.ts +411 -0
  107. package/packages/compiler/src/transforms/index.ts +4 -0
  108. package/packages/compiler/src/transforms/vfs.ts +138 -0
  109. package/packages/compiler/src/types.ts +233 -0
  110. package/packages/compiler/src/vfs/backends/indexeddb.ts +66 -0
  111. package/packages/compiler/src/vfs/backends/local-fs.ts +41 -0
  112. package/packages/compiler/src/vfs/backends/s3.ts +60 -0
  113. package/packages/compiler/src/vfs/index.ts +11 -0
  114. package/packages/compiler/src/vfs/project.ts +56 -0
  115. package/packages/compiler/src/vfs/store.ts +53 -0
  116. package/packages/compiler/src/vfs/types.ts +20 -0
  117. package/packages/compiler/tsconfig.json +8 -0
  118. package/packages/compiler/tsup.config.ts +14 -0
  119. package/packages/editor/node_modules/.bin/jiti +17 -0
  120. package/packages/editor/node_modules/.bin/tsc +17 -0
  121. package/packages/editor/node_modules/.bin/tsserver +17 -0
  122. package/packages/editor/node_modules/.bin/tsup +17 -0
  123. package/packages/editor/node_modules/.bin/tsup-node +17 -0
  124. package/packages/editor/node_modules/.bin/tsx +17 -0
  125. package/packages/editor/package.json +45 -0
  126. package/packages/editor/src/components/CodeBlockExtension.tsx +190 -0
  127. package/packages/editor/src/components/CodePreview.tsx +344 -0
  128. package/packages/editor/src/components/MarkdownEditor.tsx +270 -0
  129. package/packages/editor/src/components/ServicesInspector.tsx +118 -0
  130. package/packages/editor/src/components/edit/EditHistory.tsx +89 -0
  131. package/packages/editor/src/components/edit/EditModal.tsx +236 -0
  132. package/packages/editor/src/components/edit/FileTree.tsx +144 -0
  133. package/packages/editor/src/components/edit/api.ts +100 -0
  134. package/packages/editor/src/components/edit/index.ts +6 -0
  135. package/packages/editor/src/components/edit/types.ts +53 -0
  136. package/packages/editor/src/components/edit/useEditSession.ts +164 -0
  137. package/packages/editor/src/components/index.ts +5 -0
  138. package/packages/editor/src/index.ts +72 -0
  139. package/packages/editor/src/lib/code-extractor.ts +210 -0
  140. package/packages/editor/src/lib/diff.ts +308 -0
  141. package/packages/editor/src/lib/index.ts +4 -0
  142. package/packages/editor/src/lib/utils.ts +6 -0
  143. package/packages/editor/src/lib/vfs.ts +106 -0
  144. package/packages/editor/tsconfig.json +10 -0
  145. package/packages/editor/tsup.config.ts +10 -0
  146. package/packages/images/ink/node_modules/.bin/jiti +17 -0
  147. package/packages/images/ink/node_modules/.bin/tsc +17 -0
  148. package/packages/images/ink/node_modules/.bin/tsserver +17 -0
  149. package/packages/images/ink/node_modules/.bin/tsup +17 -0
  150. package/packages/images/ink/node_modules/.bin/tsup-node +17 -0
  151. package/packages/images/ink/node_modules/.bin/tsx +17 -0
  152. package/packages/images/ink/package.json +53 -0
  153. package/packages/images/ink/src/index.ts +48 -0
  154. package/packages/images/ink/src/runner.ts +331 -0
  155. package/packages/images/ink/src/setup.ts +123 -0
  156. package/packages/images/ink/tsconfig.json +10 -0
  157. package/packages/images/ink/tsup.config.ts +11 -0
  158. package/packages/images/shadcn/node_modules/.bin/jiti +17 -0
  159. package/packages/images/shadcn/node_modules/.bin/tsc +17 -0
  160. package/packages/images/shadcn/node_modules/.bin/tsserver +17 -0
  161. package/packages/images/shadcn/node_modules/.bin/tsup +17 -0
  162. package/packages/images/shadcn/node_modules/.bin/tsup-node +17 -0
  163. package/packages/images/shadcn/node_modules/.bin/tsx +17 -0
  164. package/packages/images/shadcn/package.json +82 -0
  165. package/packages/images/shadcn/src/html.ts +341 -0
  166. package/packages/images/shadcn/src/index.ts +37 -0
  167. package/packages/images/shadcn/src/setup.ts +287 -0
  168. package/packages/images/shadcn/tsconfig.json +9 -0
  169. package/packages/images/shadcn/tsup.config.ts +13 -0
  170. package/packages/images/vanilla/node_modules/.bin/jiti +17 -0
  171. package/packages/images/vanilla/node_modules/.bin/tsc +17 -0
  172. package/packages/images/vanilla/node_modules/.bin/tsserver +17 -0
  173. package/packages/images/vanilla/node_modules/.bin/tsup +17 -0
  174. package/packages/images/vanilla/node_modules/.bin/tsup-node +17 -0
  175. package/packages/images/vanilla/node_modules/.bin/tsx +17 -0
  176. package/packages/images/vanilla/package.json +35 -0
  177. package/packages/images/vanilla/src/index.ts +7 -0
  178. package/packages/images/vanilla/src/setup.ts +6 -0
  179. package/packages/images/vanilla/tsconfig.json +9 -0
  180. package/packages/images/vanilla/tsup.config.ts +10 -0
  181. package/packages/patchwork/node_modules/.bin/jiti +17 -0
  182. package/packages/patchwork/node_modules/.bin/tsc +17 -0
  183. package/packages/patchwork/node_modules/.bin/tsserver +17 -0
  184. package/packages/patchwork/node_modules/.bin/tsup +17 -0
  185. package/packages/patchwork/node_modules/.bin/tsup-node +17 -0
  186. package/packages/patchwork/node_modules/.bin/tsx +17 -0
  187. package/packages/patchwork/package.json +27 -0
  188. package/packages/patchwork/src/index.ts +15 -0
  189. package/packages/patchwork/src/services/index.ts +11 -0
  190. package/packages/patchwork/src/services/proxy.ts +213 -0
  191. package/packages/patchwork/src/services/types.ts +28 -0
  192. package/packages/patchwork/src/types.ts +116 -0
  193. package/packages/patchwork/tsconfig.json +8 -0
  194. package/packages/patchwork/tsup.config.ts +14 -0
  195. package/packages/stitchery/node_modules/.bin/jiti +17 -0
  196. package/packages/stitchery/node_modules/.bin/tsc +17 -0
  197. package/packages/stitchery/node_modules/.bin/tsserver +17 -0
  198. package/packages/stitchery/node_modules/.bin/tsup +17 -0
  199. package/packages/stitchery/node_modules/.bin/tsup-node +17 -0
  200. package/packages/stitchery/node_modules/.bin/tsx +17 -0
  201. package/packages/stitchery/package.json +40 -0
  202. package/packages/stitchery/src/cli.ts +116 -0
  203. package/packages/stitchery/src/index.ts +16 -0
  204. package/packages/stitchery/src/prompts.ts +326 -0
  205. package/packages/stitchery/src/server/index.ts +365 -0
  206. package/packages/stitchery/src/server/local-packages.ts +91 -0
  207. package/packages/stitchery/src/server/routes.ts +122 -0
  208. package/packages/stitchery/src/server/services.ts +382 -0
  209. package/packages/stitchery/src/server/vfs-routes.ts +142 -0
  210. package/packages/stitchery/src/types.ts +59 -0
  211. package/packages/stitchery/tsconfig.json +13 -0
  212. package/packages/stitchery/tsup.config.ts +15 -0
  213. package/packages/utcp/node_modules/.bin/jiti +17 -0
  214. package/packages/utcp/node_modules/.bin/tsc +17 -0
  215. package/packages/utcp/node_modules/.bin/tsserver +17 -0
  216. package/packages/utcp/node_modules/.bin/tsup +17 -0
  217. package/packages/utcp/node_modules/.bin/tsup-node +17 -0
  218. package/packages/utcp/node_modules/.bin/tsx +17 -0
  219. package/packages/utcp/package.json +38 -0
  220. package/packages/utcp/src/index.ts +153 -0
  221. package/packages/utcp/tsconfig.json +8 -0
  222. package/packages/utcp/tsup.config.ts +12 -0
  223. package/pnpm-workspace.yaml +3 -0
  224. package/tsconfig.json +18 -0
  225. package/turbo.json +23 -0
@@ -0,0 +1,376 @@
1
+ import { useState, useMemo } from 'react';
2
+ import type { SelectedElement, BobbinActions, DesignTokens, Change, StyleChange, Annotation } from '../../types';
3
+ import { LayoutSection } from './sections/LayoutSection';
4
+ import { SpacingSection } from './sections/SpacingSection';
5
+ import { SizeSection } from './sections/SizeSection';
6
+ import { TypographySection } from './sections/TypographySection';
7
+ import { BackgroundSection } from './sections/BackgroundSection';
8
+ import { EffectsSection } from './sections/EffectsSection';
9
+ import { AnnotationSection } from './sections/AnnotationSection';
10
+
11
+ // Theme icons
12
+ const SunIcon = () => (
13
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
14
+ <circle cx="12" cy="12" r="5" />
15
+ <line x1="12" y1="1" x2="12" y2="3" />
16
+ <line x1="12" y1="21" x2="12" y2="23" />
17
+ <line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
18
+ <line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
19
+ <line x1="1" y1="12" x2="3" y2="12" />
20
+ <line x1="21" y1="12" x2="23" y2="12" />
21
+ <line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
22
+ <line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
23
+ </svg>
24
+ );
25
+
26
+ const MoonIcon = () => (
27
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
28
+ <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
29
+ </svg>
30
+ );
31
+
32
+ const MonitorIcon = () => (
33
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
34
+ <rect x="2" y="3" width="20" height="14" rx="2" ry="2" />
35
+ <line x1="8" y1="21" x2="16" y2="21" />
36
+ <line x1="12" y1="17" x2="12" y2="21" />
37
+ </svg>
38
+ );
39
+
40
+ // Spacing visualization icon
41
+ const SpacingIcon = () => (
42
+ <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
43
+ <rect x="3" y="3" width="18" height="18" rx="2" />
44
+ <rect x="7" y="7" width="10" height="10" rx="1" />
45
+ </svg>
46
+ );
47
+
48
+ // Reset icon
49
+ const ResetIcon = () => (
50
+ <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
51
+ <path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
52
+ <path d="M3 3v5h5" />
53
+ </svg>
54
+ );
55
+
56
+ interface EditPanelProps {
57
+ selectedElement: SelectedElement;
58
+ actions: BobbinActions;
59
+ tokens: DesignTokens;
60
+ onClose: () => void;
61
+ showMarginPadding: boolean;
62
+ zIndex?: number;
63
+ theme: 'light' | 'dark' | 'system';
64
+ onThemeToggle: () => void;
65
+ changes: Change[];
66
+ annotations: Annotation[];
67
+ onReset: () => void;
68
+ }
69
+
70
+ type Section = 'layout' | 'spacing' | 'size' | 'typography' | 'background' | 'effects' | 'annotation';
71
+
72
+ // Map CSS properties to sections
73
+ const propertySectionMap: Record<string, Section> = {
74
+ 'display': 'layout',
75
+ 'flex-direction': 'layout',
76
+ 'justify-content': 'layout',
77
+ 'align-items': 'layout',
78
+ 'flex-wrap': 'layout',
79
+ 'gap': 'layout',
80
+ 'margin-top': 'spacing',
81
+ 'margin-right': 'spacing',
82
+ 'margin-bottom': 'spacing',
83
+ 'margin-left': 'spacing',
84
+ 'padding-top': 'spacing',
85
+ 'padding-right': 'spacing',
86
+ 'padding-bottom': 'spacing',
87
+ 'padding-left': 'spacing',
88
+ 'width': 'size',
89
+ 'height': 'size',
90
+ 'min-width': 'size',
91
+ 'min-height': 'size',
92
+ 'max-width': 'size',
93
+ 'max-height': 'size',
94
+ 'font-size': 'typography',
95
+ 'font-weight': 'typography',
96
+ 'font-family': 'typography',
97
+ 'line-height': 'typography',
98
+ 'letter-spacing': 'typography',
99
+ 'text-align': 'typography',
100
+ 'color': 'typography',
101
+ 'background-color': 'background',
102
+ 'background': 'background',
103
+ 'border-radius': 'effects',
104
+ 'box-shadow': 'effects',
105
+ 'border': 'effects',
106
+ 'border-width': 'effects',
107
+ 'opacity': 'effects',
108
+ };
109
+
110
+ export function EditPanel({
111
+ selectedElement,
112
+ actions,
113
+ tokens,
114
+ onClose,
115
+ showMarginPadding,
116
+ zIndex = 9999,
117
+ theme,
118
+ onThemeToggle,
119
+ changes,
120
+ annotations,
121
+ onReset,
122
+ }: EditPanelProps) {
123
+ const [expandedSections, setExpandedSections] = useState<Set<Section>>(
124
+ new Set(['annotation', 'layout', 'spacing', 'typography'])
125
+ );
126
+
127
+ // Calculate which sections have changes for the current element
128
+ const changedSections = useMemo(() => {
129
+ const sections = new Set<Section>();
130
+ const elementChanges = changes.filter(
131
+ (c) => c.target.path === selectedElement.path && c.type === 'style'
132
+ ) as StyleChange[];
133
+
134
+ for (const change of elementChanges) {
135
+ const property = change.after.property;
136
+ const section = propertySectionMap[property];
137
+ if (section) {
138
+ sections.add(section);
139
+ }
140
+ }
141
+ return sections;
142
+ }, [changes, selectedElement.path]);
143
+
144
+ // Get changed properties for spacing section highlighting
145
+ const changedSpacingProps = useMemo(() => {
146
+ const props: Record<string, boolean> = {};
147
+ const elementChanges = changes.filter(
148
+ (c) => c.target.path === selectedElement.path && c.type === 'style'
149
+ ) as StyleChange[];
150
+
151
+ for (const change of elementChanges) {
152
+ const prop = change.after.property;
153
+ if (prop.startsWith('margin-') || prop.startsWith('padding-')) {
154
+ props[prop] = true;
155
+ }
156
+ }
157
+ return props;
158
+ }, [changes, selectedElement.path]);
159
+
160
+ const toggleSection = (section: Section) => {
161
+ setExpandedSections(prev => {
162
+ const next = new Set(prev);
163
+ if (next.has(section)) {
164
+ next.delete(section);
165
+ } else {
166
+ next.add(section);
167
+ }
168
+ return next;
169
+ });
170
+ };
171
+
172
+ const computedStyle = window.getComputedStyle(selectedElement.element);
173
+
174
+ const themeIcons = {
175
+ light: <SunIcon />,
176
+ dark: <MoonIcon />,
177
+ system: <MonitorIcon />,
178
+ };
179
+
180
+ const hasAnyChanges = changes.some((c) => c.target.path === selectedElement.path);
181
+
182
+ // ShadCN-style: white background, subtle borders, clean typography
183
+ return (
184
+ <div
185
+ data-bobbin="edit-panel"
186
+ style={{
187
+ position: 'fixed',
188
+ top: '16px',
189
+ right: '16px',
190
+ width: '280px',
191
+ maxHeight: 'calc(100vh - 32px)',
192
+ backgroundColor: '#fafafa',
193
+ borderRadius: '8px',
194
+ border: '1px solid #e4e4e7',
195
+ boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
196
+ zIndex,
197
+ overflow: 'hidden',
198
+ display: 'flex',
199
+ flexDirection: 'column',
200
+ color: '#18181b',
201
+ fontFamily: 'system-ui, -apple-system, sans-serif',
202
+ fontSize: '13px',
203
+ }}
204
+ >
205
+ {/* Header */}
206
+ <div
207
+ style={{
208
+ padding: '10px 12px',
209
+ borderBottom: '1px solid #e4e4e7',
210
+ display: 'flex',
211
+ alignItems: 'center',
212
+ justifyContent: 'space-between',
213
+ backgroundColor: '#ffffff',
214
+ }}
215
+ >
216
+ <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
217
+ <span style={{ fontWeight: 500, fontSize: '12px' }}>{selectedElement.tagName}</span>
218
+ {selectedElement.id && (
219
+ <span style={{ color: '#71717a', fontSize: '11px' }}>#{selectedElement.id}</span>
220
+ )}
221
+ {selectedElement.classList.length > 0 && (
222
+ <span style={{ color: '#a1a1aa', fontSize: '10px' }}>
223
+ .{selectedElement.classList.slice(0, 2).join('.')}
224
+ {selectedElement.classList.length > 2 && '...'}
225
+ </span>
226
+ )}
227
+ </div>
228
+ <div style={{ display: 'flex', gap: '4px' }}>
229
+ {/* Theme toggle */}
230
+ <button
231
+ onClick={onThemeToggle}
232
+ style={{
233
+ padding: '4px 6px',
234
+ borderRadius: '4px',
235
+ border: '1px solid #e4e4e7',
236
+ backgroundColor: '#ffffff',
237
+ cursor: 'pointer',
238
+ fontSize: '12px',
239
+ }}
240
+ title={`Theme: ${theme}`}
241
+ >
242
+ {themeIcons[theme]}
243
+ </button>
244
+ {/* Toggle margin/padding view */}
245
+ <button
246
+ onClick={actions.toggleMarginPadding}
247
+ style={{
248
+ padding: '4px 6px',
249
+ borderRadius: '4px',
250
+ border: '1px solid #e4e4e7',
251
+ backgroundColor: showMarginPadding ? '#18181b' : '#ffffff',
252
+ color: showMarginPadding ? '#fafafa' : '#71717a',
253
+ cursor: 'pointer',
254
+ fontSize: '10px',
255
+ display: 'flex',
256
+ alignItems: 'center',
257
+ justifyContent: 'center',
258
+ }}
259
+ title="Toggle spacing visualization"
260
+ >
261
+ <SpacingIcon />
262
+ </button>
263
+ {/* Reset button */}
264
+ {hasAnyChanges && (
265
+ <button
266
+ onClick={onReset}
267
+ style={{
268
+ padding: '4px 6px',
269
+ borderRadius: '4px',
270
+ border: '1px solid #e4e4e7',
271
+ backgroundColor: '#ffffff',
272
+ color: '#71717a',
273
+ cursor: 'pointer',
274
+ fontSize: '10px',
275
+ display: 'flex',
276
+ alignItems: 'center',
277
+ justifyContent: 'center',
278
+ }}
279
+ title="Reset all changes"
280
+ >
281
+ <ResetIcon />
282
+ </button>
283
+ )}
284
+ {/* Close */}
285
+ <button
286
+ onClick={onClose}
287
+ style={{
288
+ width: '22px',
289
+ height: '22px',
290
+ borderRadius: '4px',
291
+ border: '1px solid #e4e4e7',
292
+ backgroundColor: '#ffffff',
293
+ color: '#71717a',
294
+ cursor: 'pointer',
295
+ display: 'flex',
296
+ alignItems: 'center',
297
+ justifyContent: 'center',
298
+ fontSize: '12px',
299
+ }}
300
+ >
301
+ ×
302
+ </button>
303
+ </div>
304
+ </div>
305
+
306
+ {/* Scrollable content */}
307
+ <div style={{ flex: 1, overflow: 'auto', padding: '8px 0' }}>
308
+ {/* Annotation section moved to top and default open */}
309
+ <AnnotationSection
310
+ expanded={expandedSections.has('annotation')}
311
+ onToggle={() => toggleSection('annotation')}
312
+ onAnnotate={actions.annotate}
313
+ existingAnnotation={
314
+ annotations.find((a) => a.elementPath === selectedElement.path)?.content
315
+ }
316
+ hasChanges={annotations.some((a) => a.elementPath === selectedElement.path)}
317
+ />
318
+
319
+ <LayoutSection
320
+ expanded={expandedSections.has('layout')}
321
+ onToggle={() => toggleSection('layout')}
322
+ computedStyle={computedStyle}
323
+ onApplyStyle={actions.applyStyle}
324
+ tokens={tokens}
325
+ hasChanges={changedSections.has('layout')}
326
+ />
327
+
328
+ <SpacingSection
329
+ expanded={expandedSections.has('spacing')}
330
+ onToggle={() => toggleSection('spacing')}
331
+ computedStyle={computedStyle}
332
+ onApplyStyle={actions.applyStyle}
333
+ tokens={tokens}
334
+ hasChanges={changedSections.has('spacing')}
335
+ changedProps={changedSpacingProps}
336
+ />
337
+
338
+ <SizeSection
339
+ expanded={expandedSections.has('size')}
340
+ onToggle={() => toggleSection('size')}
341
+ computedStyle={computedStyle}
342
+ onApplyStyle={actions.applyStyle}
343
+ tokens={tokens}
344
+ hasChanges={changedSections.has('size')}
345
+ />
346
+
347
+ <TypographySection
348
+ expanded={expandedSections.has('typography')}
349
+ onToggle={() => toggleSection('typography')}
350
+ computedStyle={computedStyle}
351
+ onApplyStyle={actions.applyStyle}
352
+ tokens={tokens}
353
+ hasChanges={changedSections.has('typography')}
354
+ />
355
+
356
+ <BackgroundSection
357
+ expanded={expandedSections.has('background')}
358
+ onToggle={() => toggleSection('background')}
359
+ computedStyle={computedStyle}
360
+ onApplyStyle={actions.applyStyle}
361
+ tokens={tokens}
362
+ hasChanges={changedSections.has('background')}
363
+ />
364
+
365
+ <EffectsSection
366
+ expanded={expandedSections.has('effects')}
367
+ onToggle={() => toggleSection('effects')}
368
+ computedStyle={computedStyle}
369
+ onApplyStyle={actions.applyStyle}
370
+ tokens={tokens}
371
+ hasChanges={changedSections.has('effects')}
372
+ />
373
+ </div>
374
+ </div>
375
+ );
376
+ }
@@ -0,0 +1,138 @@
1
+ import { useState, useMemo } from 'react';
2
+
3
+ interface ColorPickerProps {
4
+ value: string;
5
+ colors: Record<string, Record<string, string>>;
6
+ onChange: (value: string) => void;
7
+ }
8
+
9
+ export function ColorPicker({ value, colors, onChange }: ColorPickerProps) {
10
+ const [isExpanded, setIsExpanded] = useState(false);
11
+
12
+ // Flatten colors for display
13
+ const colorGrid = useMemo(() => {
14
+ const grid: Array<{ name: string; shade: string; value: string }> = [];
15
+ for (const [name, shades] of Object.entries(colors)) {
16
+ if (typeof shades === 'object') {
17
+ for (const [shade, colorValue] of Object.entries(shades)) {
18
+ grid.push({ name, shade, value: colorValue });
19
+ }
20
+ }
21
+ }
22
+ return grid;
23
+ }, [colors]);
24
+
25
+ // Get common shades for compact view
26
+ const commonShades = ['500', '600', '700'];
27
+ const compactColors = useMemo(() => {
28
+ return colorGrid.filter(c => commonShades.includes(c.shade));
29
+ }, [colorGrid]);
30
+
31
+ return (
32
+ <div>
33
+ {/* Current color preview */}
34
+ <div
35
+ style={{
36
+ display: 'flex',
37
+ alignItems: 'center',
38
+ gap: '6px',
39
+ marginBottom: '6px',
40
+ }}
41
+ >
42
+ <div
43
+ style={{
44
+ width: '22px',
45
+ height: '22px',
46
+ borderRadius: '4px',
47
+ backgroundColor: value,
48
+ border: '1px solid #e4e4e7',
49
+ boxShadow: 'inset 0 0 0 1px rgba(0,0,0,0.06)',
50
+ }}
51
+ />
52
+ <input
53
+ type="text"
54
+ value={value}
55
+ onChange={(e) => onChange(e.target.value)}
56
+ style={{
57
+ flex: 1,
58
+ backgroundColor: '#ffffff',
59
+ border: '1px solid #e4e4e7',
60
+ borderRadius: '4px',
61
+ padding: '4px 8px',
62
+ color: '#18181b',
63
+ fontSize: '11px',
64
+ fontFamily: 'ui-monospace, monospace',
65
+ }}
66
+ />
67
+ <button
68
+ onClick={() => setIsExpanded(!isExpanded)}
69
+ style={{
70
+ padding: '4px 6px',
71
+ borderRadius: '4px',
72
+ border: '1px solid #e4e4e7',
73
+ backgroundColor: '#ffffff',
74
+ color: '#71717a',
75
+ fontSize: '10px',
76
+ cursor: 'pointer',
77
+ }}
78
+ >
79
+ {isExpanded ? '▲' : '▼'}
80
+ </button>
81
+ </div>
82
+
83
+ {/* Color grid */}
84
+ {isExpanded && (
85
+ <div
86
+ style={{
87
+ display: 'grid',
88
+ gridTemplateColumns: 'repeat(11, 1fr)',
89
+ gap: '2px',
90
+ padding: '6px',
91
+ backgroundColor: '#fafafa',
92
+ border: '1px solid #e4e4e7',
93
+ borderRadius: '8px',
94
+ maxHeight: '200px',
95
+ overflow: 'auto',
96
+ }}
97
+ >
98
+ {colorGrid.map((color, i) => (
99
+ <button
100
+ key={i}
101
+ onClick={() => onChange(color.value)}
102
+ style={{
103
+ width: '18px',
104
+ height: '18px',
105
+ borderRadius: '3px',
106
+ backgroundColor: color.value,
107
+ border: value === color.value ? '2px solid #18181b' : '1px solid #e4e4e7',
108
+ cursor: 'pointer',
109
+ }}
110
+ title={`${color.name}-${color.shade}`}
111
+ />
112
+ ))}
113
+ </div>
114
+ )}
115
+
116
+ {/* Compact color swatches */}
117
+ {!isExpanded && (
118
+ <div style={{ display: 'flex', gap: '3px', flexWrap: 'wrap' }}>
119
+ {compactColors.map((color, i) => (
120
+ <button
121
+ key={i}
122
+ onClick={() => onChange(color.value)}
123
+ style={{
124
+ width: '14px',
125
+ height: '14px',
126
+ borderRadius: '3px',
127
+ backgroundColor: color.value,
128
+ border: value === color.value ? '2px solid #18181b' : '1px solid #e4e4e7',
129
+ cursor: 'pointer',
130
+ }}
131
+ title={`${color.name}-${color.shade}`}
132
+ />
133
+ ))}
134
+ </div>
135
+ )}
136
+ </div>
137
+ );
138
+ }
@@ -0,0 +1,142 @@
1
+ import { useState, useMemo } from 'react';
2
+
3
+ interface QuickSelectDropdownProps {
4
+ value: string;
5
+ tokens: Record<string, string>;
6
+ quickKeys: string[]; // Keys to show as quick buttons
7
+ onChange: (value: string) => void;
8
+ placeholder?: string;
9
+ }
10
+
11
+ export function QuickSelectDropdown({
12
+ value,
13
+ tokens,
14
+ quickKeys,
15
+ onChange,
16
+ placeholder = 'More...',
17
+ }: QuickSelectDropdownProps) {
18
+ const [showDropdown, setShowDropdown] = useState(false);
19
+
20
+ // Split tokens into quick buttons and dropdown items
21
+ const { quickItems, dropdownItems } = useMemo(() => {
22
+ const quick: Array<{ key: string; value: string }> = [];
23
+ const dropdown: Array<{ key: string; value: string }> = [];
24
+
25
+ for (const [key, tokenValue] of Object.entries(tokens)) {
26
+ if (quickKeys.includes(key)) {
27
+ quick.push({ key, value: tokenValue });
28
+ } else {
29
+ dropdown.push({ key, value: tokenValue });
30
+ }
31
+ }
32
+
33
+ // Sort quick items by quickKeys order
34
+ quick.sort((a, b) => quickKeys.indexOf(a.key) - quickKeys.indexOf(b.key));
35
+
36
+ return { quickItems: quick, dropdownItems: dropdown };
37
+ }, [tokens, quickKeys]);
38
+
39
+ // Check if current value matches a token
40
+ const isSelected = (tokenValue: string) => {
41
+ // Normalize values for comparison
42
+ const normalizeValue = (v: string) => v.replace(/\s+/g, '').toLowerCase();
43
+ return normalizeValue(value) === normalizeValue(tokenValue);
44
+ };
45
+
46
+ return (
47
+ <div style={{ display: 'flex', alignItems: 'center', gap: '3px', flexWrap: 'wrap' }}>
48
+ {/* Quick select buttons */}
49
+ {quickItems.map(({ key, value: tokenValue }) => (
50
+ <button
51
+ key={key}
52
+ onClick={() => onChange(tokenValue)}
53
+ style={{
54
+ padding: '3px 6px',
55
+ borderRadius: '3px',
56
+ border: '1px solid',
57
+ borderColor: isSelected(tokenValue) ? '#18181b' : '#e4e4e7',
58
+ backgroundColor: isSelected(tokenValue) ? '#18181b' : '#ffffff',
59
+ color: isSelected(tokenValue) ? '#fafafa' : '#18181b',
60
+ fontSize: '10px',
61
+ fontFamily: 'ui-monospace, monospace',
62
+ cursor: 'pointer',
63
+ transition: 'all 0.1s ease',
64
+ minWidth: '28px',
65
+ textAlign: 'center',
66
+ }}
67
+ title={`${key}: ${tokenValue}`}
68
+ >
69
+ {key}
70
+ </button>
71
+ ))}
72
+
73
+ {/* Dropdown for remaining options */}
74
+ {dropdownItems.length > 0 && (
75
+ <div style={{ position: 'relative' }}>
76
+ <button
77
+ onClick={() => setShowDropdown(!showDropdown)}
78
+ onBlur={() => setTimeout(() => setShowDropdown(false), 150)}
79
+ style={{
80
+ padding: '3px 6px',
81
+ borderRadius: '3px',
82
+ border: '1px solid #e4e4e7',
83
+ backgroundColor: '#ffffff',
84
+ color: '#71717a',
85
+ fontSize: '10px',
86
+ cursor: 'pointer',
87
+ display: 'flex',
88
+ alignItems: 'center',
89
+ gap: '2px',
90
+ }}
91
+ >
92
+ <span>...</span>
93
+ </button>
94
+
95
+ {showDropdown && (
96
+ <div
97
+ style={{
98
+ position: 'absolute',
99
+ top: '100%',
100
+ left: 0,
101
+ marginTop: '2px',
102
+ backgroundColor: '#ffffff',
103
+ border: '1px solid #e4e4e7',
104
+ borderRadius: '4px',
105
+ boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1)',
106
+ zIndex: 10,
107
+ maxHeight: '150px',
108
+ overflow: 'auto',
109
+ minWidth: '100px',
110
+ }}
111
+ >
112
+ {dropdownItems.map(({ key, value: tokenValue }) => (
113
+ <button
114
+ key={key}
115
+ onClick={() => {
116
+ onChange(tokenValue);
117
+ setShowDropdown(false);
118
+ }}
119
+ style={{
120
+ display: 'block',
121
+ width: '100%',
122
+ padding: '4px 8px',
123
+ border: 'none',
124
+ backgroundColor: isSelected(tokenValue) ? '#f4f4f5' : 'transparent',
125
+ color: '#18181b',
126
+ fontSize: '10px',
127
+ fontFamily: 'ui-monospace, monospace',
128
+ cursor: 'pointer',
129
+ textAlign: 'left',
130
+ }}
131
+ >
132
+ <span style={{ fontWeight: 500 }}>{key}</span>
133
+ <span style={{ color: '#71717a', marginLeft: '4px' }}>{tokenValue}</span>
134
+ </button>
135
+ ))}
136
+ </div>
137
+ )}
138
+ </div>
139
+ )}
140
+ </div>
141
+ );
142
+ }