@growgroup/visual-editor 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 (219) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +52 -0
  3. package/README.md +143 -0
  4. package/dist/editor.css +2 -0
  5. package/package.json +94 -0
  6. package/src/components/DeckSlideRender.tsx +21 -0
  7. package/src/components/SaveNote.tsx +21 -0
  8. package/src/components/auth/AuthProvider.tsx +44 -0
  9. package/src/components/ui/accordion.tsx +57 -0
  10. package/src/components/ui/alert-dialog.tsx +141 -0
  11. package/src/components/ui/badge.tsx +36 -0
  12. package/src/components/ui/button.tsx +56 -0
  13. package/src/components/ui/card.tsx +76 -0
  14. package/src/components/ui/checkbox.tsx +30 -0
  15. package/src/components/ui/collapsible.tsx +11 -0
  16. package/src/components/ui/context-menu.tsx +200 -0
  17. package/src/components/ui/dialog.tsx +122 -0
  18. package/src/components/ui/dropdown-menu.tsx +201 -0
  19. package/src/components/ui/input.tsx +22 -0
  20. package/src/components/ui/label.tsx +26 -0
  21. package/src/components/ui/popover.tsx +33 -0
  22. package/src/components/ui/scroll-area.tsx +48 -0
  23. package/src/components/ui/scrubbable-label.tsx +95 -0
  24. package/src/components/ui/select.tsx +159 -0
  25. package/src/components/ui/separator.tsx +31 -0
  26. package/src/components/ui/slider.tsx +28 -0
  27. package/src/components/ui/switch.tsx +29 -0
  28. package/src/components/ui/tabs.tsx +55 -0
  29. package/src/components/ui/textarea.tsx +22 -0
  30. package/src/components/ui/tooltip.tsx +32 -0
  31. package/src/components/viewer/useDeck.ts +58 -0
  32. package/src/editor/EditorContext.tsx +632 -0
  33. package/src/editor/EditorToolbar.tsx +446 -0
  34. package/src/editor/FrontendVisualEditor.tsx +2595 -0
  35. package/src/editor/autosave.ts +34 -0
  36. package/src/editor/components/AiPromptPopover.tsx +378 -0
  37. package/src/editor/components/BreakpointGuides.tsx +323 -0
  38. package/src/editor/components/BreakpointSelector.tsx +180 -0
  39. package/src/editor/components/ComponentPropertyEditor.tsx +426 -0
  40. package/src/editor/components/CssEditorDialog.tsx +168 -0
  41. package/src/editor/components/EditorCanvas.tsx +987 -0
  42. package/src/editor/components/EditorContextMenu.tsx +515 -0
  43. package/src/editor/components/EditorFooter.tsx +81 -0
  44. package/src/editor/components/EditorHeader.tsx +524 -0
  45. package/src/editor/components/EditorLayerPanel.tsx +1197 -0
  46. package/src/editor/components/EditorPropertyPanel.tsx +2996 -0
  47. package/src/editor/components/EditorSidebar.tsx +226 -0
  48. package/src/editor/components/FigmaColorPicker.tsx +1577 -0
  49. package/src/editor/components/HtmlEditorDialog.tsx +71 -0
  50. package/src/editor/components/HtmlImportDialog.tsx +346 -0
  51. package/src/editor/components/JsEditorDialog.tsx +175 -0
  52. package/src/editor/components/LayerTreeItem.tsx +494 -0
  53. package/src/editor/components/MasterComponentEditor.tsx +846 -0
  54. package/src/editor/components/MediaLibraryDialog.tsx +447 -0
  55. package/src/editor/components/PageSettingsDialog.tsx +776 -0
  56. package/src/editor/components/VariablePicker.tsx +355 -0
  57. package/src/editor/components/VariablesDialog.tsx +564 -0
  58. package/src/editor/components/VariantEditor.tsx +687 -0
  59. package/src/editor/components/VariantMatrix.tsx +507 -0
  60. package/src/editor/components/component-panel/CategoryAccordion.tsx +139 -0
  61. package/src/editor/components/component-panel/ComponentGrid.tsx +51 -0
  62. package/src/editor/components/component-panel/ComponentItem.tsx +368 -0
  63. package/src/editor/components/component-panel/ComponentPanel.tsx +275 -0
  64. package/src/editor/components/component-panel/ComponentSearch.tsx +82 -0
  65. package/src/editor/components/component-panel/VariantList.tsx +260 -0
  66. package/src/editor/components/component-panel/index.ts +12 -0
  67. package/src/editor/components/index.ts +48 -0
  68. package/src/editor/components/multi-page/CanvasZoomControls.tsx +71 -0
  69. package/src/editor/components/multi-page/InfiniteCanvas.tsx +95 -0
  70. package/src/editor/components/multi-page/MultiPageCanvasView.tsx +1404 -0
  71. package/src/editor/components/multi-page/PageFrameOverlay.tsx +76 -0
  72. package/src/editor/components/multi-page/PageLabel.tsx +34 -0
  73. package/src/editor/components/multi-page/PageLivePreview.tsx +242 -0
  74. package/src/editor/components/multi-page/PageThumbnail.tsx +59 -0
  75. package/src/editor/components/multi-page/index.ts +7 -0
  76. package/src/editor/components/ppt/PptChrome.tsx +2508 -0
  77. package/src/editor/components/ppt/PptComments.tsx +589 -0
  78. package/src/editor/components/ppt/PptDesignProposals.tsx +275 -0
  79. package/src/editor/components/ppt/PptFormatPane.tsx +320 -0
  80. package/src/editor/components/ppt/PptNotes.tsx +246 -0
  81. package/src/editor/components/property-panel/AlignmentPanel.tsx +110 -0
  82. package/src/editor/components/property-panel/AutoLayoutPanel.tsx +615 -0
  83. package/src/editor/components/property-panel/BorderSection.tsx +104 -0
  84. package/src/editor/components/property-panel/CompactNumberInput.tsx +237 -0
  85. package/src/editor/components/property-panel/CompactSizeInput.tsx +148 -0
  86. package/src/editor/components/property-panel/EffectSection.tsx +157 -0
  87. package/src/editor/components/property-panel/FillSection.tsx +69 -0
  88. package/src/editor/components/property-panel/GoogleFontPicker.tsx +192 -0
  89. package/src/editor/components/property-panel/ImageSection.tsx +206 -0
  90. package/src/editor/components/property-panel/ImgSrcSection.tsx +125 -0
  91. package/src/editor/components/property-panel/InstanceOverrideSection.tsx +413 -0
  92. package/src/editor/components/property-panel/LayoutSection.tsx +105 -0
  93. package/src/editor/components/property-panel/LinkSection.tsx +103 -0
  94. package/src/editor/components/property-panel/PropertyVariablePicker.tsx +311 -0
  95. package/src/editor/components/property-panel/ScalePanel.tsx +212 -0
  96. package/src/editor/components/property-panel/TypographySection.tsx +230 -0
  97. package/src/editor/components/property-panel/UnitAwareNumberInput.tsx +238 -0
  98. package/src/editor/components/property-panel/VariableAwareColorInput.tsx +477 -0
  99. package/src/editor/components/property-panel/VariableAwareInput.tsx +606 -0
  100. package/src/editor/components/property-panel/VariableAwareSizeInput.tsx +588 -0
  101. package/src/editor/components/property-panel/VariableAwareUnitInput.tsx +546 -0
  102. package/src/editor/components/property-panel/index.ts +24 -0
  103. package/src/editor/components/property-panel/unit-utils.ts +261 -0
  104. package/src/editor/components/shell/EditorTopBar.tsx +392 -0
  105. package/src/editor/components/shell/LeftPanel.tsx +47 -0
  106. package/src/editor/components/shell/PagesPanel.tsx +118 -0
  107. package/src/editor/components/variables-panel/ColorValueCell.tsx +165 -0
  108. package/src/editor/components/variables-panel/TextValueCell.tsx +116 -0
  109. package/src/editor/components/variables-panel/VariablesPanel.tsx +518 -0
  110. package/src/editor/components/variables-panel/index.ts +7 -0
  111. package/src/editor/constants.ts +468 -0
  112. package/src/editor/contexts/EditorArtboardContext.tsx +229 -0
  113. package/src/editor/contexts/EditorComponentsContext.tsx +1320 -0
  114. package/src/editor/contexts/EditorDocumentContext.tsx +113 -0
  115. package/src/editor/contexts/EditorHistoryContext.tsx +516 -0
  116. package/src/editor/contexts/EditorRefsContext.tsx +204 -0
  117. package/src/editor/contexts/EditorSelectionContext.tsx +54 -0
  118. package/src/editor/contexts/EditorToolContext.tsx +58 -0
  119. package/src/editor/contexts/EditorUIStateContext.tsx +122 -0
  120. package/src/editor/contexts/EditorVariablesContext.tsx +397 -0
  121. package/src/editor/contexts/EditorViewContext.tsx +101 -0
  122. package/src/editor/contexts/MultiPageCanvasContext.tsx +498 -0
  123. package/src/editor/contexts/index.ts +17 -0
  124. package/src/editor/editor-skin.css +122 -0
  125. package/src/editor/hooks/index.ts +38 -0
  126. package/src/editor/hooks/useAiReplace.ts +397 -0
  127. package/src/editor/hooks/useAltMeasure.ts +67 -0
  128. package/src/editor/hooks/useBrowserZoomPrevention.ts +121 -0
  129. package/src/editor/hooks/useCanvasControls.ts +1222 -0
  130. package/src/editor/hooks/useComponentEditMode.ts +658 -0
  131. package/src/editor/hooks/useComponentInstances.ts +515 -0
  132. package/src/editor/hooks/useContextMenuHandler.ts +157 -0
  133. package/src/editor/hooks/useCoordinateTransform.ts +207 -0
  134. package/src/editor/hooks/useDragResize.ts +1317 -0
  135. package/src/editor/hooks/useDrawingMode.ts +472 -0
  136. package/src/editor/hooks/useEditorColors.ts +144 -0
  137. package/src/editor/hooks/useEditorMessages.ts +108 -0
  138. package/src/editor/hooks/useElementActions.ts +1754 -0
  139. package/src/editor/hooks/useElementSelection.ts +1573 -0
  140. package/src/editor/hooks/useFocusManagement.ts +249 -0
  141. package/src/editor/hooks/useGoogleFonts.ts +305 -0
  142. package/src/editor/hooks/useIframeInitializer.ts +584 -0
  143. package/src/editor/hooks/useIframeSetup.ts +360 -0
  144. package/src/editor/hooks/useImageUpload.ts +270 -0
  145. package/src/editor/hooks/useInfiniteCanvas.ts +301 -0
  146. package/src/editor/hooks/useKeyboardShortcuts.ts +205 -0
  147. package/src/editor/hooks/useMarqueeSelection.ts +388 -0
  148. package/src/editor/hooks/useMediaLibrary.ts +129 -0
  149. package/src/editor/hooks/usePageSettingsManager.ts +739 -0
  150. package/src/editor/hooks/useResizablePanel.ts +163 -0
  151. package/src/editor/hooks/useRichPaste.ts +255 -0
  152. package/src/editor/hooks/useTouchGestures.ts +197 -0
  153. package/src/editor/index.ts +27 -0
  154. package/src/editor/types/page-settings.ts +513 -0
  155. package/src/editor/types.ts +382 -0
  156. package/src/editor/utils/align-elements.ts +169 -0
  157. package/src/editor/utils/api-json.ts +21 -0
  158. package/src/editor/utils/aspect-lock.ts +50 -0
  159. package/src/editor/utils/component-renderer.ts +702 -0
  160. package/src/editor/utils/component-sync.ts +845 -0
  161. package/src/editor/utils/crop-mode.ts +265 -0
  162. package/src/editor/utils/dom-utils.ts +2238 -0
  163. package/src/editor/utils/element-effects.ts +218 -0
  164. package/src/editor/utils/eyedropper.ts +35 -0
  165. package/src/editor/utils/figma-export.ts +560 -0
  166. package/src/editor/utils/figma-kiwi-decoder.ts +722 -0
  167. package/src/editor/utils/figma-kiwi-encoder.ts +583 -0
  168. package/src/editor/utils/figma-paste.ts +2262 -0
  169. package/src/editor/utils/flex-reorder.ts +231 -0
  170. package/src/editor/utils/flex-utils.ts +1230 -0
  171. package/src/editor/utils/geometry.ts +232 -0
  172. package/src/editor/utils/grid-layout.ts +210 -0
  173. package/src/editor/utils/html-utils.ts +470 -0
  174. package/src/editor/utils/index.ts +11 -0
  175. package/src/editor/utils/ink-style.ts +57 -0
  176. package/src/editor/utils/inline-format.ts +236 -0
  177. package/src/editor/utils/measure-distance.ts +120 -0
  178. package/src/editor/utils/override-apply.ts +526 -0
  179. package/src/editor/utils/override-detection.ts +613 -0
  180. package/src/editor/utils/paste-processors.ts +609 -0
  181. package/src/editor/utils/paste-sanitizer.ts +244 -0
  182. package/src/editor/utils/restore-flow.ts +188 -0
  183. package/src/editor/utils/shape-library.ts +171 -0
  184. package/src/editor/utils/slide-root.ts +29 -0
  185. package/src/editor/utils/smart-guides.ts +218 -0
  186. package/src/editor/utils/style-utils.ts +685 -0
  187. package/src/editor/utils/table-edit.ts +151 -0
  188. package/src/editor/utils/tailwind-mappings.ts +778 -0
  189. package/src/editor/utils/tailwind-utils.ts +804 -0
  190. package/src/editor/utils/text-highlight.ts +216 -0
  191. package/src/editor/utils/theme-to-variables.ts +341 -0
  192. package/src/editor/utils/variant-resolver.ts +456 -0
  193. package/src/editor/utils/viewport-utils.ts +295 -0
  194. package/src/hooks/useEditorHistory.ts +195 -0
  195. package/src/hooks/useEditorShortcuts.ts +260 -0
  196. package/src/index.ts +40 -0
  197. package/src/io.ts +134 -0
  198. package/src/lib/agent/slide-agent/types.ts +547 -0
  199. package/src/lib/agent/website-agent/types.ts +805 -0
  200. package/src/lib/api/auth-fetch.ts +58 -0
  201. package/src/lib/deck.ts +69 -0
  202. package/src/lib/export.ts +47 -0
  203. package/src/lib/firebase/config.ts +29 -0
  204. package/src/lib/firebase/css-variables.ts +378 -0
  205. package/src/lib/firebase/editor-components.ts +677 -0
  206. package/src/lib/firebase/storage.ts +469 -0
  207. package/src/lib/utils.ts +6 -0
  208. package/src/styles/editor.css +52 -0
  209. package/src/styles/skin.css +122 -0
  210. package/src/types/css-variables.ts +645 -0
  211. package/src/types/editor-components.ts +944 -0
  212. package/src/types/editor.ts +420 -0
  213. package/src/types/page-master.ts +197 -0
  214. package/src/types/page.ts +419 -0
  215. package/src/types/slide.ts +402 -0
  216. package/src/types/website-theme.ts +788 -0
  217. package/src/types/website.ts +374 -0
  218. package/src/vendor/firebase-firestore.ts +13 -0
  219. package/src/vendor/firebase-functions.ts +17 -0
@@ -0,0 +1,515 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * エディタ用コンテキストメニュー
5
+ * 右クリックで要素操作メニューを表示
6
+ */
7
+
8
+ import { useCallback, useEffect, useRef } from 'react';
9
+ import {
10
+ Copy,
11
+ Scissors,
12
+ Clipboard,
13
+ Trash2,
14
+ CopyPlus,
15
+ ArrowUp,
16
+ ArrowDown,
17
+ ChevronsUp,
18
+ ChevronsDown,
19
+ Group,
20
+ Ungroup,
21
+ Lock,
22
+ Unlock,
23
+ Sparkles,
24
+ Code,
25
+ Paintbrush,
26
+ ClipboardPaste,
27
+ ImageUp,
28
+ Images,
29
+ Component,
30
+ ArrowUpRight,
31
+ Unlink,
32
+ RotateCcw,
33
+ ArrowUpFromLine,
34
+ Link2,
35
+ BetweenHorizontalStart,
36
+ BetweenHorizontalEnd,
37
+ BetweenVerticalStart,
38
+ BetweenVerticalEnd,
39
+ Rows3,
40
+ Columns3,
41
+ } from 'lucide-react';
42
+
43
+ export interface ContextMenuPosition {
44
+ x: number;
45
+ y: number;
46
+ }
47
+
48
+ export interface ContextMenuProps {
49
+ position: ContextMenuPosition | null;
50
+ onClose: () => void;
51
+ // 選択状態
52
+ hasSelection: boolean;
53
+ selectionCount: number;
54
+ isGroup: boolean;
55
+ hasStyleInClipboard?: boolean;
56
+ // コンポーネント状態
57
+ isComponentInstance?: boolean;
58
+ hasOverrides?: boolean;
59
+ // アクション
60
+ /** 画像の変更(選択が<img>のときのみ渡される) */
61
+ onReplaceImageFromFile?: () => void;
62
+ onReplaceImageFromLibrary?: () => void;
63
+ onReplaceImageFromClipboard?: () => void;
64
+ onCopy?: () => void;
65
+ onCut?: () => void;
66
+ onPaste?: () => void;
67
+ onDelete?: () => void;
68
+ onDuplicate?: () => void;
69
+ onCopyStyle?: () => void;
70
+ onPasteStyle?: () => void;
71
+ onBringForward?: () => void;
72
+ onSendBackward?: () => void;
73
+ onBringToFront?: () => void;
74
+ onSendToBack?: () => void;
75
+ onGroup?: () => void;
76
+ onUngroup?: () => void;
77
+ /** 表の編集(右クリックしたセルが表の中のときだけ渡される) */
78
+ onInsertRowAbove?: () => void;
79
+ onInsertRowBelow?: () => void;
80
+ onInsertColumnLeft?: () => void;
81
+ onInsertColumnRight?: () => void;
82
+ onDeleteRow?: () => void;
83
+ onDeleteColumn?: () => void;
84
+ /** リンク(href)の編集 */
85
+ onEditLink?: () => void;
86
+ // AI生成
87
+ onAiRegenerate?: () => void;
88
+ // HTML編集
89
+ onEditHtml?: () => void;
90
+ // コンポーネント操作
91
+ onCreateComponent?: () => void;
92
+ onGoToMainComponent?: () => void;
93
+ onDetachInstance?: () => void;
94
+ onResetOverrides?: () => void;
95
+ onPushOverridesToMain?: () => void;
96
+ }
97
+
98
+ interface MenuItemProps {
99
+ icon: React.ReactNode;
100
+ label: string;
101
+ shortcut?: string;
102
+ onClick?: () => void;
103
+ disabled?: boolean;
104
+ danger?: boolean;
105
+ }
106
+
107
+ function MenuItem({ icon, label, shortcut, onClick, disabled, danger }: MenuItemProps) {
108
+ return (
109
+ <button
110
+ className={`
111
+ w-full flex items-center gap-2 px-3 py-1.5 text-xs
112
+ ${disabled
113
+ ? 'text-gray-500 cursor-not-allowed'
114
+ : danger
115
+ ? 'text-red-400 hover:bg-red-500/20'
116
+ : 'text-gray-300 hover:bg-[#444444]'
117
+ }
118
+ transition-colors
119
+ `}
120
+ onClick={(e) => {
121
+ e.stopPropagation();
122
+ if (!disabled && onClick) onClick();
123
+ }}
124
+ disabled={disabled}
125
+ >
126
+ <span className="w-4 h-4 flex items-center justify-center">{icon}</span>
127
+ <span className="flex-1 text-left">{label}</span>
128
+ {shortcut && (
129
+ <span className="text-[10px] text-gray-500">{shortcut}</span>
130
+ )}
131
+ </button>
132
+ );
133
+ }
134
+
135
+ function MenuDivider() {
136
+ return <div className="h-px bg-[#444444] my-1" />;
137
+ }
138
+
139
+ export function EditorContextMenu({
140
+ position,
141
+ onClose,
142
+ hasSelection,
143
+ selectionCount,
144
+ isGroup,
145
+ hasStyleInClipboard,
146
+ isComponentInstance,
147
+ hasOverrides,
148
+ onCopy,
149
+ onCut,
150
+ onPaste,
151
+ onDelete,
152
+ onDuplicate,
153
+ onReplaceImageFromFile,
154
+ onReplaceImageFromLibrary,
155
+ onReplaceImageFromClipboard,
156
+ onCopyStyle,
157
+ onPasteStyle,
158
+ onBringForward,
159
+ onSendBackward,
160
+ onBringToFront,
161
+ onSendToBack,
162
+ onGroup,
163
+ onUngroup,
164
+ onInsertRowAbove,
165
+ onInsertRowBelow,
166
+ onInsertColumnLeft,
167
+ onInsertColumnRight,
168
+ onDeleteRow,
169
+ onDeleteColumn,
170
+ onEditLink,
171
+ onAiRegenerate,
172
+ onEditHtml,
173
+ onCreateComponent,
174
+ onGoToMainComponent,
175
+ onDetachInstance,
176
+ onResetOverrides,
177
+ onPushOverridesToMain,
178
+ }: ContextMenuProps) {
179
+ const menuRef = useRef<HTMLDivElement>(null);
180
+
181
+ // メニュー外クリックで閉じる
182
+ useEffect(() => {
183
+ if (!position) return;
184
+
185
+ const handleClickOutside = (e: MouseEvent) => {
186
+ if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
187
+ onClose();
188
+ }
189
+ };
190
+
191
+ const handleEscape = (e: KeyboardEvent) => {
192
+ if (e.key === 'Escape') {
193
+ onClose();
194
+ }
195
+ };
196
+
197
+ // 次のフレームでリスナーを追加(右クリックイベントの完了を待つ)
198
+ requestAnimationFrame(() => {
199
+ document.addEventListener('mousedown', handleClickOutside);
200
+ document.addEventListener('keydown', handleEscape);
201
+ });
202
+
203
+ return () => {
204
+ document.removeEventListener('mousedown', handleClickOutside);
205
+ document.removeEventListener('keydown', handleEscape);
206
+ };
207
+ }, [position, onClose]);
208
+
209
+ // メニュー位置の調整(画面外にはみ出さないように)
210
+ const getMenuStyle = useCallback(() => {
211
+ if (!position) return {};
212
+
213
+ const menuWidth = 200;
214
+ const menuHeight = 320;
215
+ const padding = 8;
216
+
217
+ let x = position.x;
218
+ let y = position.y;
219
+
220
+ // 右端チェック
221
+ if (x + menuWidth > window.innerWidth - padding) {
222
+ x = window.innerWidth - menuWidth - padding;
223
+ }
224
+
225
+ // 下端チェック
226
+ if (y + menuHeight > window.innerHeight - padding) {
227
+ y = window.innerHeight - menuHeight - padding;
228
+ }
229
+
230
+ return {
231
+ left: `${x}px`,
232
+ top: `${y}px`,
233
+ };
234
+ }, [position]);
235
+
236
+ if (!position) return null;
237
+
238
+ const handleAction = (action?: () => void) => {
239
+ if (action) {
240
+ action();
241
+ onClose();
242
+ }
243
+ };
244
+
245
+ return (
246
+ <div
247
+ ref={menuRef}
248
+ className="fixed z-[60] bg-[#2c2c2c] border border-[#444444] rounded-lg shadow-xl py-1 min-w-[180px]"
249
+ style={getMenuStyle()}
250
+ onClick={(e) => e.stopPropagation()}
251
+ >
252
+ {/* クリップボード操作 */}
253
+ <MenuItem
254
+ icon={<Copy className="w-3.5 h-3.5" />}
255
+ label="コピー"
256
+ shortcut="⌘C"
257
+ onClick={() => handleAction(onCopy)}
258
+ disabled={!hasSelection}
259
+ />
260
+ <MenuItem
261
+ icon={<Scissors className="w-3.5 h-3.5" />}
262
+ label="カット"
263
+ shortcut="⌘X"
264
+ onClick={() => handleAction(onCut)}
265
+ disabled={!hasSelection}
266
+ />
267
+ <MenuItem
268
+ icon={<Clipboard className="w-3.5 h-3.5" />}
269
+ label="ペースト"
270
+ shortcut="⌘V"
271
+ onClick={() => handleAction(onPaste)}
272
+ />
273
+
274
+ {(onReplaceImageFromFile || onReplaceImageFromLibrary || onReplaceImageFromClipboard) && (
275
+ <>
276
+ <MenuDivider />
277
+ {/* 実機PowerPointの「画像の変更」。位置・サイズ・書式は保ったままsrcだけ替える */}
278
+ <div className="px-3 pb-0.5 pt-1 text-[10px] text-gray-500">画像の変更</div>
279
+ {onReplaceImageFromFile && (
280
+ <MenuItem
281
+ icon={<ImageUp className="w-3.5 h-3.5" />}
282
+ label="ファイルから…"
283
+ onClick={() => handleAction(onReplaceImageFromFile)}
284
+ />
285
+ )}
286
+ {onReplaceImageFromLibrary && (
287
+ <MenuItem
288
+ icon={<Images className="w-3.5 h-3.5" />}
289
+ label="メディアライブラリから…"
290
+ onClick={() => handleAction(onReplaceImageFromLibrary)}
291
+ />
292
+ )}
293
+ {onReplaceImageFromClipboard && (
294
+ <MenuItem
295
+ icon={<ClipboardPaste className="w-3.5 h-3.5" />}
296
+ label="クリップボードから"
297
+ onClick={() => handleAction(onReplaceImageFromClipboard)}
298
+ />
299
+ )}
300
+ </>
301
+ )}
302
+
303
+ {/* 表の編集。右クリックしたセルを起点に行・列を足し引きする */}
304
+ {onInsertRowAbove && (
305
+ <>
306
+ <MenuDivider />
307
+ <div className="px-3 pb-0.5 pt-1 text-[10px] text-gray-500">表</div>
308
+ <MenuItem
309
+ icon={<BetweenHorizontalStart className="w-3.5 h-3.5" />}
310
+ label="行を上に挿入"
311
+ onClick={() => handleAction(onInsertRowAbove)}
312
+ />
313
+ <MenuItem
314
+ icon={<BetweenHorizontalEnd className="w-3.5 h-3.5" />}
315
+ label="行を下に挿入"
316
+ onClick={() => handleAction(onInsertRowBelow)}
317
+ />
318
+ <MenuItem
319
+ icon={<BetweenVerticalStart className="w-3.5 h-3.5" />}
320
+ label="列を左に挿入"
321
+ onClick={() => handleAction(onInsertColumnLeft)}
322
+ />
323
+ <MenuItem
324
+ icon={<BetweenVerticalEnd className="w-3.5 h-3.5" />}
325
+ label="列を右に挿入"
326
+ onClick={() => handleAction(onInsertColumnRight)}
327
+ />
328
+ <MenuItem
329
+ icon={<Rows3 className="w-3.5 h-3.5" />}
330
+ label="行を削除"
331
+ onClick={() => handleAction(onDeleteRow)}
332
+ disabled={!onDeleteRow}
333
+ danger
334
+ />
335
+ <MenuItem
336
+ icon={<Columns3 className="w-3.5 h-3.5" />}
337
+ label="列を削除"
338
+ onClick={() => handleAction(onDeleteColumn)}
339
+ disabled={!onDeleteColumn}
340
+ danger
341
+ />
342
+ </>
343
+ )}
344
+
345
+ <MenuDivider />
346
+
347
+ {/* 編集操作 */}
348
+ <MenuItem
349
+ icon={<CopyPlus className="w-3.5 h-3.5" />}
350
+ label="複製"
351
+ shortcut="⌘D"
352
+ onClick={() => handleAction(onDuplicate)}
353
+ disabled={!hasSelection}
354
+ />
355
+ <MenuItem
356
+ icon={<Trash2 className="w-3.5 h-3.5" />}
357
+ label="削除"
358
+ shortcut="⌫"
359
+ onClick={() => handleAction(onDelete)}
360
+ disabled={!hasSelection}
361
+ danger
362
+ />
363
+
364
+ <MenuDivider />
365
+
366
+ {/* スタイル操作 */}
367
+ <MenuItem
368
+ icon={<Paintbrush className="w-3.5 h-3.5" />}
369
+ label="スタイルをコピー"
370
+ shortcut="⌘⌥C"
371
+ onClick={() => handleAction(onCopyStyle)}
372
+ disabled={!hasSelection}
373
+ />
374
+ <MenuItem
375
+ icon={<ClipboardPaste className="w-3.5 h-3.5" />}
376
+ label="スタイルをペースト"
377
+ shortcut="⌘⌥V"
378
+ onClick={() => handleAction(onPasteStyle)}
379
+ disabled={!hasSelection || !hasStyleInClipboard}
380
+ />
381
+
382
+ <MenuDivider />
383
+
384
+ {/* AI生成 */}
385
+ <MenuItem
386
+ icon={<Sparkles className="w-3.5 h-3.5 text-purple-400" />}
387
+ label="AIで再生成"
388
+ onClick={() => {
389
+ if (onAiRegenerate) {
390
+ onAiRegenerate();
391
+ onClose();
392
+ }
393
+ }}
394
+ disabled={!hasSelection || selectionCount > 1}
395
+ />
396
+
397
+ <MenuDivider />
398
+
399
+ {/* リンク */}
400
+ <MenuItem
401
+ icon={<Link2 className="w-3.5 h-3.5" />}
402
+ label="リンクを編集…"
403
+ onClick={() => handleAction(onEditLink)}
404
+ disabled={!onEditLink || !hasSelection || selectionCount > 1}
405
+ />
406
+
407
+ {/* HTML編集 */}
408
+ <MenuItem
409
+ icon={<Code className="w-3.5 h-3.5" />}
410
+ label="HTMLを編集"
411
+ onClick={() => handleAction(onEditHtml)}
412
+ disabled={!hasSelection || selectionCount > 1}
413
+ />
414
+
415
+ <MenuDivider />
416
+
417
+ {/* レイヤー操作 */}
418
+ <MenuItem
419
+ icon={<ArrowUp className="w-3.5 h-3.5" />}
420
+ label="前面へ"
421
+ shortcut="⌘]"
422
+ onClick={() => handleAction(onBringForward)}
423
+ disabled={!hasSelection}
424
+ />
425
+ <MenuItem
426
+ icon={<ArrowDown className="w-3.5 h-3.5" />}
427
+ label="背面へ"
428
+ shortcut="⌘["
429
+ onClick={() => handleAction(onSendBackward)}
430
+ disabled={!hasSelection}
431
+ />
432
+ <MenuItem
433
+ icon={<ChevronsUp className="w-3.5 h-3.5" />}
434
+ label="最前面へ"
435
+ shortcut="⌘⇧]"
436
+ onClick={() => handleAction(onBringToFront)}
437
+ disabled={!hasSelection}
438
+ />
439
+ <MenuItem
440
+ icon={<ChevronsDown className="w-3.5 h-3.5" />}
441
+ label="最背面へ"
442
+ shortcut="⌘⇧["
443
+ onClick={() => handleAction(onSendToBack)}
444
+ disabled={!hasSelection}
445
+ />
446
+
447
+ {/* グループ操作(複数選択時のみ表示) */}
448
+ {(selectionCount > 1 || isGroup) && (
449
+ <>
450
+ <MenuDivider />
451
+ {selectionCount > 1 && (
452
+ <MenuItem
453
+ icon={<Group className="w-3.5 h-3.5" />}
454
+ label="グループ化"
455
+ shortcut="⌘G"
456
+ onClick={() => handleAction(onGroup)}
457
+ />
458
+ )}
459
+ {isGroup && (
460
+ <MenuItem
461
+ icon={<Ungroup className="w-3.5 h-3.5" />}
462
+ label="グループ解除"
463
+ shortcut="⌘⇧G"
464
+ onClick={() => handleAction(onUngroup)}
465
+ />
466
+ )}
467
+ </>
468
+ )}
469
+
470
+ {/* コンポーネント操作 */}
471
+ {hasSelection && selectionCount === 1 && (
472
+ <>
473
+ <MenuDivider />
474
+ {/* 通常要素の場合: コンポーネント作成 */}
475
+ {!isComponentInstance && (
476
+ <MenuItem
477
+ icon={<Component className="w-3.5 h-3.5 text-purple-400" />}
478
+ label="コンポーネントを作成"
479
+ onClick={() => handleAction(onCreateComponent)}
480
+ />
481
+ )}
482
+ {/* コンポーネントインスタンスの場合 */}
483
+ {isComponentInstance && (
484
+ <>
485
+ <MenuItem
486
+ icon={<ArrowUpRight className="w-3.5 h-3.5" />}
487
+ label="メインコンポーネントへ移動"
488
+ onClick={() => handleAction(onGoToMainComponent)}
489
+ />
490
+ <MenuItem
491
+ icon={<Unlink className="w-3.5 h-3.5" />}
492
+ label="インスタンスを解除"
493
+ onClick={() => handleAction(onDetachInstance)}
494
+ />
495
+ {hasOverrides && (
496
+ <>
497
+ <MenuItem
498
+ icon={<RotateCcw className="w-3.5 h-3.5" />}
499
+ label="オーバーライドをリセット"
500
+ onClick={() => handleAction(onResetOverrides)}
501
+ />
502
+ <MenuItem
503
+ icon={<ArrowUpFromLine className="w-3.5 h-3.5" />}
504
+ label="メインに変更を適用"
505
+ onClick={() => handleAction(onPushOverridesToMain)}
506
+ />
507
+ </>
508
+ )}
509
+ </>
510
+ )}
511
+ </>
512
+ )}
513
+ </div>
514
+ );
515
+ }
@@ -0,0 +1,81 @@
1
+ 'use client';
2
+
3
+ import { useEditorContext } from '../EditorContext';
4
+ import { BreakpointSelector } from './BreakpointSelector';
5
+
6
+ /**
7
+ * エディタのフッター(ショートカットヘルプ表示 + ブレイクポイントセレクター)
8
+ */
9
+ export function EditorFooter() {
10
+ const { activeTool, editorMode } = useEditorContext();
11
+
12
+ return (
13
+ <div className="px-4 py-2 bg-[#2c2c2c] border-t border-[#444444]">
14
+ <div className="flex items-center justify-between text-xs text-gray-500">
15
+ {/* 左側: ショートカットヘルプ */}
16
+ <div className="flex items-center gap-4">
17
+ {activeTool === 'select' ? (
18
+ <>
19
+ <span>
20
+ <kbd className="px-1 py-0.5 bg-[#444444] rounded text-gray-400">
21
+ クリック
22
+ </kbd>{' '}
23
+ 選択 & 編集
24
+ </span>
25
+ <span>
26
+ <kbd className="px-1 py-0.5 bg-[#444444] rounded text-gray-400">
27
+ Enter
28
+ </kbd>{' '}
29
+ 確定
30
+ </span>
31
+ <span>
32
+ <kbd className="px-1 py-0.5 bg-[#444444] rounded text-gray-400">
33
+ Esc
34
+ </kbd>{' '}
35
+ キャンセル
36
+ </span>
37
+ </>
38
+ ) : (
39
+ <>
40
+ <span>
41
+ <kbd className="px-1 py-0.5 bg-[#444444] rounded text-gray-400">
42
+ ドラッグ
43
+ </kbd>{' '}
44
+ 描画
45
+ </span>
46
+ <span>
47
+ <kbd className="px-1 py-0.5 bg-[#444444] rounded text-gray-400">
48
+ V
49
+ </kbd>{' '}
50
+ 選択ツールに戻る
51
+ </span>
52
+ </>
53
+ )}
54
+ </div>
55
+
56
+ {/* 中央: ブレイクポイントセレクター(webpageモードのみ) */}
57
+ {editorMode === 'webpage' && (
58
+ <div className="flex items-center">
59
+ <BreakpointSelector />
60
+ </div>
61
+ )}
62
+
63
+ {/* 右側: Undo/Redoショートカット */}
64
+ <div className="flex items-center gap-4">
65
+ <span>
66
+ <kbd className="px-1 py-0.5 bg-[#444444] rounded text-gray-400">
67
+ ⌘Z
68
+ </kbd>{' '}
69
+ 元に戻す
70
+ </span>
71
+ <span>
72
+ <kbd className="px-1 py-0.5 bg-[#444444] rounded text-gray-400">
73
+ ⌘⇧Z
74
+ </kbd>{' '}
75
+ やり直し
76
+ </span>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ );
81
+ }