@dfosco/storyboard 0.9.6 → 0.10.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 (155) hide show
  1. package/commandpalette.config.json +6 -0
  2. package/dist/storyboard-ui.css +1 -1
  3. package/dist/storyboard-ui.js +16450 -12737
  4. package/dist/storyboard-ui.js.map +1 -1
  5. package/dist/tailwind.css +1 -1
  6. package/package.json +66 -2
  7. package/src/core/artifact/schemas/component.schema.json +55 -0
  8. package/src/core/artifact/schemas/prototype.schema.json +55 -0
  9. package/src/core/canvas/materializer.js +120 -105
  10. package/src/core/canvas/materializer.test.js +172 -1
  11. package/src/core/canvas/server.js +156 -11
  12. package/src/core/cli/canvasConnector.js +51 -2
  13. package/src/core/cli/configureRepository.js +164 -0
  14. package/src/core/cli/dev.js +14 -23
  15. package/src/core/cli/file.js +333 -0
  16. package/src/core/cli/gh.js +101 -0
  17. package/src/core/cli/index.js +4 -0
  18. package/src/core/cli/repoConfig.js +109 -0
  19. package/src/core/cli/repoConfig.test.js +119 -0
  20. package/src/core/cli/setup.js +15 -0
  21. package/src/core/comments/ui/mount.js +27 -0
  22. package/src/core/file/__tests__/server.test.js +604 -0
  23. package/src/core/file/__tests__/tree.test.js +190 -0
  24. package/src/core/file/server.js +519 -0
  25. package/src/core/file/tree.js +151 -0
  26. package/src/core/file-watcher/config.json +28 -0
  27. package/src/core/file-watcher/watcher.js +891 -0
  28. package/src/core/{rename-watcher → file-watcher}/watcher.test.js +142 -1
  29. package/src/core/index.js +4 -1
  30. package/src/core/knobs/coerce.js +93 -0
  31. package/src/core/knobs/coerce.test.js +51 -0
  32. package/src/core/knobs/discovery.js +109 -0
  33. package/src/core/knobs/discovery.test.js +69 -0
  34. package/src/core/knobs/index.js +18 -0
  35. package/src/core/knobs/keys.js +26 -0
  36. package/src/core/knobs/keys.test.js +28 -0
  37. package/src/core/knobs/writer.js +117 -0
  38. package/src/core/knobs/writer.test.js +113 -0
  39. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-trigger.jsx +10 -2
  40. package/src/core/rename-watcher/watcher.js +3 -824
  41. package/src/core/session/session.js +31 -3
  42. package/src/core/session/session.test.js +31 -75
  43. package/src/core/stores/canvasConfig.js +31 -1
  44. package/src/core/stores/canvasConfig.test.js +50 -1
  45. package/src/core/stores/configSchema.js +57 -0
  46. package/src/core/stores/configSchema.test.js +6 -0
  47. package/src/core/stores/themeBootstrap.js +18 -1
  48. package/src/core/stores/themeBootstrap.test.js +17 -6
  49. package/src/core/stores/uiConfig.js +2 -1
  50. package/src/core/tools/handlers/knobs.js +14 -0
  51. package/src/core/tools/registry.js +1 -0
  52. package/src/core/ui/CanvasCreateMenu.jsx +9 -1
  53. package/src/core/ui/CanvasSnap.jsx +14 -7
  54. package/src/core/ui/CanvasZoomToFit.jsx +11 -8
  55. package/src/core/ui/CoreUIBar.jsx +70 -3
  56. package/src/core/ui/KnobsPanel.jsx +320 -0
  57. package/src/core/ui/KnobsPanel.module.css +118 -0
  58. package/src/core/ui/KnobsPanel.test.jsx +135 -0
  59. package/src/core/ui/knobsPanelState.js +25 -0
  60. package/src/core/vite/server-plugin.js +4 -0
  61. package/src/internals/BaseUiForm/BaseUiForm.jsx +85 -3
  62. package/src/internals/BaseUiForm/BaseUiForm.module.css +232 -0
  63. package/src/internals/BranchBar/BranchBar.jsx +3 -1
  64. package/src/internals/CommandPalette/CommandPalette.jsx +55 -25
  65. package/src/internals/Knobs/KnobsForm.jsx +633 -0
  66. package/src/internals/Knobs/KnobsForm.module.css +162 -0
  67. package/src/internals/Knobs/KnobsForm.test.jsx +107 -0
  68. package/src/internals/Knobs/index.js +1 -0
  69. package/src/internals/canvas/CanvasPage.bridge.test.jsx +1 -0
  70. package/src/internals/canvas/CanvasPage.dragdrop.test.jsx +191 -0
  71. package/src/internals/canvas/CanvasPage.jsx +577 -187
  72. package/src/internals/canvas/CanvasPage.multiselect.test.jsx +1 -0
  73. package/src/internals/canvas/CanvasToolbar.jsx +6 -0
  74. package/src/internals/canvas/ConnectorLayer.jsx +54 -12
  75. package/src/internals/canvas/ConnectorLayer.module.css +67 -0
  76. package/src/internals/canvas/PageSelector.jsx +187 -202
  77. package/src/internals/canvas/PageSelector.module.css +12 -6
  78. package/src/internals/canvas/canvasApi.js +21 -2
  79. package/src/internals/canvas/widgets/CodePenEmbed.jsx +9 -2
  80. package/src/internals/canvas/widgets/FigmaEmbed.jsx +8 -1
  81. package/src/internals/canvas/widgets/FileWidget/CodeEditor.jsx +304 -0
  82. package/src/internals/canvas/widgets/FileWidget/CodeEditor.module.css +57 -0
  83. package/src/internals/canvas/widgets/FileWidget/DeleteConfirmController.jsx +62 -0
  84. package/src/internals/canvas/widgets/FileWidget/DeleteConfirmDialog.jsx +46 -0
  85. package/src/internals/canvas/widgets/FileWidget/DeleteConfirmDialog.module.css +48 -0
  86. package/src/internals/canvas/widgets/FileWidget/FilePicker.jsx +309 -0
  87. package/src/internals/canvas/widgets/FileWidget/FilePicker.module.css +170 -0
  88. package/src/internals/canvas/widgets/FileWidget/FilePickerController.jsx +75 -0
  89. package/src/internals/canvas/widgets/FileWidget/FileSecondaryPane.jsx +167 -0
  90. package/src/internals/canvas/widgets/FileWidget/FileWidget.jsx +658 -0
  91. package/src/internals/canvas/widgets/FileWidget/FileWidget.module.css +359 -0
  92. package/src/internals/canvas/widgets/FileWidget/MdxRenderer.jsx +153 -0
  93. package/src/internals/canvas/widgets/FileWidget/MdxRenderer.module.css +81 -0
  94. package/src/internals/canvas/widgets/FileWidget/__tests__/CodeEditor.test.jsx +235 -0
  95. package/src/internals/canvas/widgets/FileWidget/__tests__/DeleteConfirmDialog.test.jsx +51 -0
  96. package/src/internals/canvas/widgets/FileWidget/__tests__/FilePicker.test.jsx +329 -0
  97. package/src/internals/canvas/widgets/FileWidget/__tests__/FileWidget.test.jsx +525 -0
  98. package/src/internals/canvas/widgets/FileWidget/__tests__/MdxRenderer.test.jsx +81 -0
  99. package/src/internals/canvas/widgets/FileWidget/__tests__/fileContentStore.test.js +404 -0
  100. package/src/internals/canvas/widgets/FileWidget/__tests__/fileFlavor.test.js +287 -0
  101. package/src/internals/canvas/widgets/FileWidget/fileContentStore.js +328 -0
  102. package/src/internals/canvas/widgets/FileWidget/fileFlavor.js +250 -0
  103. package/src/internals/canvas/widgets/KnobsWidget.jsx +229 -0
  104. package/src/internals/canvas/widgets/KnobsWidget.module.css +144 -0
  105. package/src/internals/canvas/widgets/KnobsWidget.noreload.test.jsx +215 -0
  106. package/src/internals/canvas/widgets/KnobsWidget.test.jsx +237 -0
  107. package/src/internals/canvas/widgets/MarkdownBlock.jsx +32 -152
  108. package/src/internals/canvas/widgets/MarkdownBlock.module.css +17 -328
  109. package/src/internals/canvas/widgets/PrototypeEmbed.jsx +20 -2
  110. package/src/internals/canvas/widgets/StickyNote.jsx +43 -34
  111. package/src/internals/canvas/widgets/StickyNote.module.css +123 -4
  112. package/src/internals/canvas/widgets/StickyNote.test.jsx +95 -0
  113. package/src/internals/canvas/widgets/StorySetWidget.jsx +25 -3
  114. package/src/internals/canvas/widgets/StoryWidget.jsx +18 -0
  115. package/src/internals/canvas/widgets/TerminalWidget.jsx +46 -0
  116. package/src/internals/canvas/widgets/expandUtils.js +57 -1
  117. package/src/internals/canvas/widgets/iframeRefContract.test.jsx +102 -0
  118. package/src/internals/canvas/widgets/iframeSrcCompare.js +38 -0
  119. package/src/internals/canvas/widgets/iframeSrcCompare.test.js +65 -0
  120. package/src/internals/canvas/widgets/index.js +4 -2
  121. package/src/internals/canvas/widgets/knobsTargetResolver.js +215 -0
  122. package/src/internals/canvas/widgets/knobsTargetResolver.test.js +142 -0
  123. package/src/internals/canvas/widgets/markdown/MarkdownEditor.jsx +175 -0
  124. package/src/internals/canvas/widgets/markdown/MarkdownEditor.module.css +11 -0
  125. package/src/internals/canvas/widgets/markdown/MarkdownView.test.jsx +32 -0
  126. package/src/internals/canvas/widgets/markdown/markdownClass.js +16 -0
  127. package/src/internals/canvas/widgets/markdown/markdownContent.module.css +230 -0
  128. package/src/internals/canvas/widgets/markdown/markdownRender.js +53 -0
  129. package/src/internals/canvas/widgets/storyPath.js +31 -0
  130. package/src/internals/canvas/widgets/storyPath.test.js +59 -0
  131. package/src/internals/canvas/widgets/widgetConfig.js +61 -3
  132. package/src/internals/canvas/widgets/widgetConfig.knobs.test.js +42 -0
  133. package/src/internals/canvas/widgets/widgetConfig.test.js +33 -2
  134. package/src/internals/canvas/widgets/widgetIcons.jsx +6 -1
  135. package/src/internals/hooks/useKnob.js +97 -0
  136. package/src/internals/hooks/useKnob.test.js +133 -0
  137. package/src/internals/index.js +2 -0
  138. package/src/internals/vite/data-plugin.js +45 -7
  139. package/src/internals/vite/data-plugin.test.js +3 -3
  140. package/storyboard.config.json +6 -0
  141. package/toolbar.config.json +15 -2
  142. package/widgets.config.json +235 -133
  143. package/src/core/ui/CanvasSnap.css +0 -27
  144. package/src/core/ui/CanvasZoomToFit.css +0 -18
  145. package/src/internals/canvas/widgets/TilesWidget.jsx +0 -300
  146. package/src/internals/canvas/widgets/TilesWidget.module.css +0 -133
  147. package/src/internals/canvas/widgets/tilePool.js +0 -23
  148. package/src/internals/canvas/widgets/tiles/diagonal-bl.png +0 -0
  149. package/src/internals/canvas/widgets/tiles/diagonal-br.png +0 -0
  150. package/src/internals/canvas/widgets/tiles/diagonal-tl.png +0 -0
  151. package/src/internals/canvas/widgets/tiles/leaf.png +0 -0
  152. package/src/internals/canvas/widgets/tiles/quarter-tl.png +0 -0
  153. package/src/internals/canvas/widgets/tiles/quarter-tr.png +0 -0
  154. package/src/internals/canvas/widgets/tiles/solid-a.png +0 -0
  155. package/src/internals/canvas/widgets/tiles/solid-b.png +0 -0
@@ -1,300 +0,0 @@
1
- import { useState, useCallback, useRef, useEffect, forwardRef, useImperativeHandle, useMemo } from 'react'
2
- import WidgetWrapper from './WidgetWrapper.jsx'
3
- import ResizeHandle from './ResizeHandle.jsx'
4
- import { readProp } from './widgetProps.js'
5
- import { schemas } from './widgetConfig.js'
6
- import { TILE_POOL } from './tilePool.js'
7
- import styles from './TilesWidget.module.css'
8
-
9
- const tilesSchema = schemas['tiles']
10
- const LS_PREFIX = 'storyboard-tiles-'
11
-
12
- /** Read persisted state from localStorage for a given widget ID. */
13
- function loadFromStorage(widgetId) {
14
- try {
15
- const raw = localStorage.getItem(`${LS_PREFIX}${widgetId}`)
16
- return raw ? JSON.parse(raw) : null
17
- } catch { return null }
18
- }
19
-
20
- /** Save state to localStorage for a given widget ID. */
21
- function saveToStorage(widgetId, state) {
22
- try {
23
- localStorage.setItem(`${LS_PREFIX}${widgetId}`, JSON.stringify(state))
24
- } catch { /* quota exceeded — silently ignore */ }
25
- }
26
-
27
- /**
28
- * Canvas widget that arranges square images in an interactive tile grid.
29
- *
30
- * Uses a static pool of tile images bundled with the widget (see tilePool.js).
31
- * In production, state is persisted to localStorage since canvas editing is read-only.
32
- *
33
- * Features:
34
- * - Configurable columns × rows (toolbar actions in dev, inline buttons in prod)
35
- * - Drag & drop reorder
36
- * - Click-select + Cmd+C / Cmd+V to copy-paste tiles
37
- * - Randomize action
38
- * - Tile composition persisted as indexes into TILE_POOL
39
- */
40
- const TilesWidget = forwardRef(function TilesWidget({ id, props, onUpdate, resizable }, ref) {
41
- const containerRef = useRef(null)
42
- const isProd = !onUpdate
43
-
44
- // In prod, load initial state from localStorage
45
- const [localState, setLocalState] = useState(() => loadFromStorage(id))
46
-
47
- const columns = (isProd ? localState?.columns : null) ?? (readProp(props, 'columns', tilesSchema) || 3)
48
- const rows = (isProd ? localState?.rows : null) ?? (readProp(props, 'rows', tilesSchema) || 3)
49
- const tileSize = readProp(props, 'tileSize', tilesSchema) || 80
50
- const savedTiles = (isProd ? localState?.tiles : null) ?? readProp(props, 'tiles', tilesSchema)
51
-
52
- // Local state for interactions
53
- const [selectedIdx, setSelectedIdx] = useState(null)
54
- const [copiedSrc, setCopiedSrc] = useState(null)
55
- const [dragIdx, setDragIdx] = useState(null)
56
- const [dragOverIdx, setDragOverIdx] = useState(null)
57
-
58
- // Clear selection when exiting interact mode (dev only — prod has no gate)
59
- useEffect(() => {
60
- if (isProd) return
61
- const el = containerRef.current
62
- if (!el) return
63
- const observer = new MutationObserver(() => {
64
- const slot = el.closest('[data-widget-interacting]')
65
- if (!slot) {
66
- setSelectedIdx(null)
67
- setCopiedSrc(null)
68
- }
69
- })
70
- const slot = el.closest('[data-widget-selected]')?.parentElement
71
- if (slot) observer.observe(slot, { attributes: true, attributeFilter: ['data-widget-interacting'] })
72
- return () => observer.disconnect()
73
- }, [isProd])
74
-
75
- // Build effective tile list from saved indexes or default pool
76
- const tiles = useMemo(() => {
77
- if (savedTiles && savedTiles.length > 0) {
78
- return savedTiles.map((idx) =>
79
- typeof idx === 'number' ? (TILE_POOL[idx] ?? TILE_POOL[0]) : TILE_POOL[0]
80
- )
81
- }
82
- return [...TILE_POOL]
83
- }, [savedTiles])
84
-
85
- // Total visible slots
86
- const slotCount = columns * rows
87
- const visibleTiles = tiles.slice(0, slotCount)
88
-
89
- // Pad with nulls if we have fewer images than slots
90
- const grid = useMemo(() => {
91
- const g = [...visibleTiles]
92
- while (g.length < slotCount) g.push(null)
93
- return g
94
- }, [visibleTiles, slotCount])
95
-
96
- // Persist — writes to onUpdate (dev) or localStorage (prod)
97
- const persist = useCallback((patch) => {
98
- if (isProd) {
99
- setLocalState((prev) => {
100
- const next = { ...prev, ...patch }
101
- saveToStorage(id, next)
102
- return next
103
- })
104
- } else {
105
- onUpdate?.(patch)
106
- }
107
- }, [isProd, id, onUpdate])
108
-
109
- const persistTiles = useCallback((srcs) => {
110
- const indexes = srcs.map((src) => {
111
- const idx = TILE_POOL.indexOf(src)
112
- return idx >= 0 ? idx : 0
113
- })
114
- persist({ tiles: indexes })
115
- }, [persist])
116
-
117
- // ── Actions (shared by toolbar handleAction and inline buttons) ──
118
- const addColumn = useCallback(() => persist({ columns: columns + 1 }), [columns, persist])
119
- const removeColumn = useCallback(() => { if (columns > 1) persist({ columns: columns - 1 }) }, [columns, persist])
120
- const addRow = useCallback(() => persist({ rows: rows + 1 }), [rows, persist])
121
- const removeRow = useCallback(() => { if (rows > 1) persist({ rows: rows - 1 }) }, [rows, persist])
122
- const randomize = useCallback(() => {
123
- const total = columns * rows
124
- const filled = Array.from({ length: total }, () =>
125
- TILE_POOL[Math.floor(Math.random() * TILE_POOL.length)]
126
- )
127
- persistTiles(filled)
128
- }, [columns, rows, persistTiles])
129
-
130
- // ── Keyboard: Cmd+C / Cmd+V ──
131
- useEffect(() => {
132
- function handleKeyDown(e) {
133
- const el = containerRef.current
134
- if (!el) return
135
- // In dev, only respond when interacting; in prod, always respond when focused
136
- if (!isProd) {
137
- const slot = el.closest('[data-widget-interacting]')
138
- if (!slot) return
139
- }
140
-
141
- if (!e.metaKey && !e.ctrlKey) return
142
- if (e.key === 'c' && selectedIdx !== null && grid[selectedIdx]) {
143
- e.stopPropagation()
144
- e.preventDefault()
145
- setCopiedSrc(grid[selectedIdx])
146
- }
147
- if (e.key === 'v' && selectedIdx !== null && copiedSrc) {
148
- e.stopPropagation()
149
- e.preventDefault()
150
- const newGrid = [...grid]
151
- newGrid[selectedIdx] = copiedSrc
152
- persistTiles(newGrid.filter(Boolean))
153
- setCopiedSrc(null)
154
- }
155
- }
156
- window.addEventListener('keydown', handleKeyDown, true)
157
- return () => window.removeEventListener('keydown', handleKeyDown, true)
158
- }, [selectedIdx, copiedSrc, grid, persistTiles, isProd])
159
-
160
- // ── Drag & drop handlers ──
161
- const handleDragStart = useCallback((e, idx) => {
162
- e.stopPropagation()
163
- setDragIdx(idx)
164
- e.dataTransfer.effectAllowed = 'move'
165
- e.dataTransfer.setData('text/plain', String(idx))
166
- }, [])
167
-
168
- const handleDragOver = useCallback((e, idx) => {
169
- e.preventDefault()
170
- e.stopPropagation()
171
- e.dataTransfer.dropEffect = 'move'
172
- setDragOverIdx(idx)
173
- }, [])
174
-
175
- const handleDragLeave = useCallback(() => {
176
- setDragOverIdx(null)
177
- }, [])
178
-
179
- const handleDrop = useCallback((e, toIdx) => {
180
- e.preventDefault()
181
- e.stopPropagation()
182
- const fromIdx = dragIdx
183
- setDragIdx(null)
184
- setDragOverIdx(null)
185
- if (fromIdx === null || fromIdx === toIdx) return
186
- const newGrid = [...grid]
187
- ;[newGrid[fromIdx], newGrid[toIdx]] = [newGrid[toIdx], newGrid[fromIdx]]
188
- persistTiles(newGrid.filter(Boolean))
189
- }, [dragIdx, grid, persistTiles])
190
-
191
- const handleDragEnd = useCallback(() => {
192
- setDragIdx(null)
193
- setDragOverIdx(null)
194
- }, [])
195
-
196
- // ── Tile click ──
197
- const handleTileClick = useCallback((e, idx) => {
198
- e.stopPropagation()
199
- setSelectedIdx((prev) => (prev === idx ? null : idx))
200
- }, [])
201
-
202
- const handleBackgroundClick = useCallback(() => {
203
- setSelectedIdx(null)
204
- }, [])
205
-
206
- // ── Widget actions (dev toolbar) ──
207
- useImperativeHandle(ref, () => ({
208
- handleAction(actionId) {
209
- if (actionId === 'add-column') { addColumn(); return true }
210
- if (actionId === 'remove-column') { removeColumn(); return true }
211
- if (actionId === 'add-row') { addRow(); return true }
212
- if (actionId === 'remove-row') { removeRow(); return true }
213
- if (actionId === 'randomize') { randomize(); return true }
214
- },
215
- }), [addColumn, removeColumn, addRow, removeRow, randomize])
216
-
217
- const gridStyle = {
218
- gridTemplateColumns: `repeat(${columns}, ${tileSize}px)`,
219
- gridTemplateRows: `repeat(${rows}, ${tileSize}px)`,
220
- gap: '2px',
221
- }
222
-
223
- return (
224
- <WidgetWrapper>
225
- <div
226
- ref={containerRef}
227
- className={styles.container}
228
- onClick={handleBackgroundClick}
229
- data-tiles-widget
230
- >
231
- {isProd && (
232
- <div className={styles.toolbar}>
233
- <button className={styles.toolbarBtn} onClick={(e) => { e.stopPropagation(); randomize() }} title="Randomize">🔀</button>
234
- <span className={styles.toolbarSep} />
235
- <button className={styles.toolbarBtn} onClick={(e) => { e.stopPropagation(); removeColumn() }} title="Remove column" disabled={columns <= 1}>−</button>
236
- <span className={styles.toolbarLabel}>{columns}×{rows}</span>
237
- <button className={styles.toolbarBtn} onClick={(e) => { e.stopPropagation(); addColumn() }} title="Add column">+</button>
238
- <span className={styles.toolbarSep} />
239
- <button className={styles.toolbarBtn} onClick={(e) => { e.stopPropagation(); removeRow() }} title="Remove row" disabled={rows <= 1}>↑</button>
240
- <button className={styles.toolbarBtn} onClick={(e) => { e.stopPropagation(); addRow() }} title="Add row">↓</button>
241
- </div>
242
- )}
243
- <div className={styles.grid} style={gridStyle}>
244
- {grid.map((src, idx) => (
245
- <div
246
- key={`${idx}-${src || 'empty'}`}
247
- className={[
248
- styles.tile,
249
- selectedIdx === idx ? styles.selected : '',
250
- copiedSrc && selectedIdx === idx ? styles.pasteTarget : '',
251
- dragOverIdx === idx ? styles.dragOver : '',
252
- dragIdx === idx ? styles.dragging : '',
253
- ].filter(Boolean).join(' ')}
254
- draggable={!!src}
255
- onDragStart={(e) => handleDragStart(e, idx)}
256
- onDragOver={(e) => handleDragOver(e, idx)}
257
- onDragLeave={handleDragLeave}
258
- onDrop={(e) => handleDrop(e, idx)}
259
- onDragEnd={handleDragEnd}
260
- onClick={(e) => handleTileClick(e, idx)}
261
- onMouseDown={(e) => e.stopPropagation()}
262
- onPointerDown={(e) => e.stopPropagation()}
263
- style={{ width: tileSize, height: tileSize }}
264
- >
265
- {src ? (
266
- <img
267
- src={src}
268
- alt=""
269
- className={styles.tileImage}
270
- draggable={false}
271
- />
272
- ) : (
273
- <span className={styles.emptyTile} />
274
- )}
275
- {copiedSrc && selectedIdx === idx && (
276
- <span className={styles.pasteHint}>⌘V</span>
277
- )}
278
- </div>
279
- ))}
280
- </div>
281
- {selectedIdx !== null && grid[selectedIdx] && !copiedSrc && (
282
- <div className={styles.hint}>⌘C to copy · click another tile · ⌘V to paste</div>
283
- )}
284
- {copiedSrc && selectedIdx !== null && (
285
- <div className={styles.hint}>⌘V to replace this tile</div>
286
- )}
287
- {resizable && (
288
- <ResizeHandle
289
- targetRef={containerRef}
290
- minWidth={200}
291
- minHeight={100}
292
- onResize={(w, h) => onUpdate?.({ width: w, height: h })}
293
- />
294
- )}
295
- </div>
296
- </WidgetWrapper>
297
- )
298
- })
299
-
300
- export default TilesWidget
@@ -1,133 +0,0 @@
1
- .container {
2
- padding: 8px;
3
- user-select: none;
4
- cursor: default;
5
- }
6
-
7
- .toolbar {
8
- display: flex;
9
- align-items: center;
10
- gap: 4px;
11
- padding: 4px 0 8px;
12
- }
13
-
14
- .toolbarBtn {
15
- all: unset;
16
- display: inline-flex;
17
- align-items: center;
18
- justify-content: center;
19
- width: 28px;
20
- height: 28px;
21
- border-radius: 6px;
22
- font-size: 14px;
23
- cursor: pointer;
24
- color: var(--color-foreground, #1f2328);
25
- background: var(--color-background-muted, #f6f8fa);
26
- transition: background 0.12s ease;
27
- }
28
-
29
- .toolbarBtn:hover {
30
- background: var(--color-background-neutral-muted, #d0d7de);
31
- }
32
-
33
- .toolbarBtn:disabled {
34
- opacity: 0.35;
35
- cursor: default;
36
- }
37
-
38
- .toolbarLabel {
39
- font-size: 12px;
40
- font-weight: 600;
41
- color: var(--color-foreground-muted, #656d76);
42
- min-width: 32px;
43
- text-align: center;
44
- }
45
-
46
- .toolbarSep {
47
- width: 1px;
48
- height: 16px;
49
- background: var(--color-border-muted, #d0d7de);
50
- margin: 0 4px;
51
- }
52
-
53
- .grid {
54
- display: grid;
55
- border-radius: 6px;
56
- overflow: hidden;
57
- }
58
-
59
- .tile {
60
- position: relative;
61
- overflow: hidden;
62
- cursor: pointer;
63
- border: 2px solid transparent;
64
- border-radius: 4px;
65
- transition: border-color 0.12s ease, opacity 0.15s ease, transform 0.12s ease;
66
- background: var(--color-background-muted, #f6f8fa);
67
- }
68
-
69
- .tile:hover {
70
- border-color: var(--color-border-accent-emphasis, #0969da);
71
- }
72
-
73
- .selected {
74
- border-color: var(--color-foreground-accent, #0969da);
75
- box-shadow: 0 0 0 2px var(--color-foreground-accent, #0969da);
76
- }
77
-
78
- .pasteTarget {
79
- border-color: var(--color-foreground-success, #1a7f37);
80
- box-shadow: 0 0 0 2px var(--color-foreground-success, #1a7f37);
81
- animation: pulse 1s infinite;
82
- }
83
-
84
- .dragOver {
85
- border-color: var(--color-foreground-accent, #0969da);
86
- background: var(--color-background-accent-muted, #ddf4ff);
87
- transform: scale(1.04);
88
- }
89
-
90
- .dragging {
91
- opacity: 0.4;
92
- }
93
-
94
- .tileImage {
95
- width: 100%;
96
- height: 100%;
97
- object-fit: cover;
98
- display: block;
99
- pointer-events: none;
100
- }
101
-
102
- .emptyTile {
103
- display: block;
104
- width: 100%;
105
- height: 100%;
106
- background: var(--color-background-muted, #f6f8fa);
107
- }
108
-
109
- .pasteHint {
110
- position: absolute;
111
- inset: 0;
112
- display: flex;
113
- align-items: center;
114
- justify-content: center;
115
- background: rgba(0, 0, 0, 0.45);
116
- color: white;
117
- font-size: 11px;
118
- font-weight: 600;
119
- letter-spacing: 0.5px;
120
- pointer-events: none;
121
- }
122
-
123
- .hint {
124
- padding: 6px 0 2px;
125
- text-align: center;
126
- font-size: 11px;
127
- color: var(--color-foreground-muted, #656d76);
128
- }
129
-
130
- @keyframes pulse {
131
- 0%, 100% { box-shadow: 0 0 0 2px var(--color-foreground-success, #1a7f37); }
132
- 50% { box-shadow: 0 0 0 4px var(--color-foreground-success, #1a7f37); }
133
- }
@@ -1,23 +0,0 @@
1
- /**
2
- * Static tile image pool.
3
- * Each import resolves to a Vite asset URL at build time.
4
- */
5
- import solidA from './tiles/solid-a.png'
6
- import solidB from './tiles/solid-b.png'
7
- import quarterTL from './tiles/quarter-tl.png'
8
- import quarterTR from './tiles/quarter-tr.png'
9
- import diagonalBR from './tiles/diagonal-br.png'
10
- import diagonalBL from './tiles/diagonal-bl.png'
11
- import diagonalTL from './tiles/diagonal-tl.png'
12
- import leaf from './tiles/leaf.png'
13
-
14
- export const TILE_POOL = [
15
- solidA,
16
- solidB,
17
- quarterTL,
18
- quarterTR,
19
- diagonalBR,
20
- diagonalBL,
21
- diagonalTL,
22
- leaf,
23
- ]