@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.
- package/LICENSE +21 -0
- package/NOTICE +52 -0
- package/README.md +143 -0
- package/dist/editor.css +2 -0
- package/package.json +94 -0
- package/src/components/DeckSlideRender.tsx +21 -0
- package/src/components/SaveNote.tsx +21 -0
- package/src/components/auth/AuthProvider.tsx +44 -0
- package/src/components/ui/accordion.tsx +57 -0
- package/src/components/ui/alert-dialog.tsx +141 -0
- package/src/components/ui/badge.tsx +36 -0
- package/src/components/ui/button.tsx +56 -0
- package/src/components/ui/card.tsx +76 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/collapsible.tsx +11 -0
- package/src/components/ui/context-menu.tsx +200 -0
- package/src/components/ui/dialog.tsx +122 -0
- package/src/components/ui/dropdown-menu.tsx +201 -0
- package/src/components/ui/input.tsx +22 -0
- package/src/components/ui/label.tsx +26 -0
- package/src/components/ui/popover.tsx +33 -0
- package/src/components/ui/scroll-area.tsx +48 -0
- package/src/components/ui/scrubbable-label.tsx +95 -0
- package/src/components/ui/select.tsx +159 -0
- package/src/components/ui/separator.tsx +31 -0
- package/src/components/ui/slider.tsx +28 -0
- package/src/components/ui/switch.tsx +29 -0
- package/src/components/ui/tabs.tsx +55 -0
- package/src/components/ui/textarea.tsx +22 -0
- package/src/components/ui/tooltip.tsx +32 -0
- package/src/components/viewer/useDeck.ts +58 -0
- package/src/editor/EditorContext.tsx +632 -0
- package/src/editor/EditorToolbar.tsx +446 -0
- package/src/editor/FrontendVisualEditor.tsx +2595 -0
- package/src/editor/autosave.ts +34 -0
- package/src/editor/components/AiPromptPopover.tsx +378 -0
- package/src/editor/components/BreakpointGuides.tsx +323 -0
- package/src/editor/components/BreakpointSelector.tsx +180 -0
- package/src/editor/components/ComponentPropertyEditor.tsx +426 -0
- package/src/editor/components/CssEditorDialog.tsx +168 -0
- package/src/editor/components/EditorCanvas.tsx +987 -0
- package/src/editor/components/EditorContextMenu.tsx +515 -0
- package/src/editor/components/EditorFooter.tsx +81 -0
- package/src/editor/components/EditorHeader.tsx +524 -0
- package/src/editor/components/EditorLayerPanel.tsx +1197 -0
- package/src/editor/components/EditorPropertyPanel.tsx +2996 -0
- package/src/editor/components/EditorSidebar.tsx +226 -0
- package/src/editor/components/FigmaColorPicker.tsx +1577 -0
- package/src/editor/components/HtmlEditorDialog.tsx +71 -0
- package/src/editor/components/HtmlImportDialog.tsx +346 -0
- package/src/editor/components/JsEditorDialog.tsx +175 -0
- package/src/editor/components/LayerTreeItem.tsx +494 -0
- package/src/editor/components/MasterComponentEditor.tsx +846 -0
- package/src/editor/components/MediaLibraryDialog.tsx +447 -0
- package/src/editor/components/PageSettingsDialog.tsx +776 -0
- package/src/editor/components/VariablePicker.tsx +355 -0
- package/src/editor/components/VariablesDialog.tsx +564 -0
- package/src/editor/components/VariantEditor.tsx +687 -0
- package/src/editor/components/VariantMatrix.tsx +507 -0
- package/src/editor/components/component-panel/CategoryAccordion.tsx +139 -0
- package/src/editor/components/component-panel/ComponentGrid.tsx +51 -0
- package/src/editor/components/component-panel/ComponentItem.tsx +368 -0
- package/src/editor/components/component-panel/ComponentPanel.tsx +275 -0
- package/src/editor/components/component-panel/ComponentSearch.tsx +82 -0
- package/src/editor/components/component-panel/VariantList.tsx +260 -0
- package/src/editor/components/component-panel/index.ts +12 -0
- package/src/editor/components/index.ts +48 -0
- package/src/editor/components/multi-page/CanvasZoomControls.tsx +71 -0
- package/src/editor/components/multi-page/InfiniteCanvas.tsx +95 -0
- package/src/editor/components/multi-page/MultiPageCanvasView.tsx +1404 -0
- package/src/editor/components/multi-page/PageFrameOverlay.tsx +76 -0
- package/src/editor/components/multi-page/PageLabel.tsx +34 -0
- package/src/editor/components/multi-page/PageLivePreview.tsx +242 -0
- package/src/editor/components/multi-page/PageThumbnail.tsx +59 -0
- package/src/editor/components/multi-page/index.ts +7 -0
- package/src/editor/components/ppt/PptChrome.tsx +2508 -0
- package/src/editor/components/ppt/PptComments.tsx +589 -0
- package/src/editor/components/ppt/PptDesignProposals.tsx +275 -0
- package/src/editor/components/ppt/PptFormatPane.tsx +320 -0
- package/src/editor/components/ppt/PptNotes.tsx +246 -0
- package/src/editor/components/property-panel/AlignmentPanel.tsx +110 -0
- package/src/editor/components/property-panel/AutoLayoutPanel.tsx +615 -0
- package/src/editor/components/property-panel/BorderSection.tsx +104 -0
- package/src/editor/components/property-panel/CompactNumberInput.tsx +237 -0
- package/src/editor/components/property-panel/CompactSizeInput.tsx +148 -0
- package/src/editor/components/property-panel/EffectSection.tsx +157 -0
- package/src/editor/components/property-panel/FillSection.tsx +69 -0
- package/src/editor/components/property-panel/GoogleFontPicker.tsx +192 -0
- package/src/editor/components/property-panel/ImageSection.tsx +206 -0
- package/src/editor/components/property-panel/ImgSrcSection.tsx +125 -0
- package/src/editor/components/property-panel/InstanceOverrideSection.tsx +413 -0
- package/src/editor/components/property-panel/LayoutSection.tsx +105 -0
- package/src/editor/components/property-panel/LinkSection.tsx +103 -0
- package/src/editor/components/property-panel/PropertyVariablePicker.tsx +311 -0
- package/src/editor/components/property-panel/ScalePanel.tsx +212 -0
- package/src/editor/components/property-panel/TypographySection.tsx +230 -0
- package/src/editor/components/property-panel/UnitAwareNumberInput.tsx +238 -0
- package/src/editor/components/property-panel/VariableAwareColorInput.tsx +477 -0
- package/src/editor/components/property-panel/VariableAwareInput.tsx +606 -0
- package/src/editor/components/property-panel/VariableAwareSizeInput.tsx +588 -0
- package/src/editor/components/property-panel/VariableAwareUnitInput.tsx +546 -0
- package/src/editor/components/property-panel/index.ts +24 -0
- package/src/editor/components/property-panel/unit-utils.ts +261 -0
- package/src/editor/components/shell/EditorTopBar.tsx +392 -0
- package/src/editor/components/shell/LeftPanel.tsx +47 -0
- package/src/editor/components/shell/PagesPanel.tsx +118 -0
- package/src/editor/components/variables-panel/ColorValueCell.tsx +165 -0
- package/src/editor/components/variables-panel/TextValueCell.tsx +116 -0
- package/src/editor/components/variables-panel/VariablesPanel.tsx +518 -0
- package/src/editor/components/variables-panel/index.ts +7 -0
- package/src/editor/constants.ts +468 -0
- package/src/editor/contexts/EditorArtboardContext.tsx +229 -0
- package/src/editor/contexts/EditorComponentsContext.tsx +1320 -0
- package/src/editor/contexts/EditorDocumentContext.tsx +113 -0
- package/src/editor/contexts/EditorHistoryContext.tsx +516 -0
- package/src/editor/contexts/EditorRefsContext.tsx +204 -0
- package/src/editor/contexts/EditorSelectionContext.tsx +54 -0
- package/src/editor/contexts/EditorToolContext.tsx +58 -0
- package/src/editor/contexts/EditorUIStateContext.tsx +122 -0
- package/src/editor/contexts/EditorVariablesContext.tsx +397 -0
- package/src/editor/contexts/EditorViewContext.tsx +101 -0
- package/src/editor/contexts/MultiPageCanvasContext.tsx +498 -0
- package/src/editor/contexts/index.ts +17 -0
- package/src/editor/editor-skin.css +122 -0
- package/src/editor/hooks/index.ts +38 -0
- package/src/editor/hooks/useAiReplace.ts +397 -0
- package/src/editor/hooks/useAltMeasure.ts +67 -0
- package/src/editor/hooks/useBrowserZoomPrevention.ts +121 -0
- package/src/editor/hooks/useCanvasControls.ts +1222 -0
- package/src/editor/hooks/useComponentEditMode.ts +658 -0
- package/src/editor/hooks/useComponentInstances.ts +515 -0
- package/src/editor/hooks/useContextMenuHandler.ts +157 -0
- package/src/editor/hooks/useCoordinateTransform.ts +207 -0
- package/src/editor/hooks/useDragResize.ts +1317 -0
- package/src/editor/hooks/useDrawingMode.ts +472 -0
- package/src/editor/hooks/useEditorColors.ts +144 -0
- package/src/editor/hooks/useEditorMessages.ts +108 -0
- package/src/editor/hooks/useElementActions.ts +1754 -0
- package/src/editor/hooks/useElementSelection.ts +1573 -0
- package/src/editor/hooks/useFocusManagement.ts +249 -0
- package/src/editor/hooks/useGoogleFonts.ts +305 -0
- package/src/editor/hooks/useIframeInitializer.ts +584 -0
- package/src/editor/hooks/useIframeSetup.ts +360 -0
- package/src/editor/hooks/useImageUpload.ts +270 -0
- package/src/editor/hooks/useInfiniteCanvas.ts +301 -0
- package/src/editor/hooks/useKeyboardShortcuts.ts +205 -0
- package/src/editor/hooks/useMarqueeSelection.ts +388 -0
- package/src/editor/hooks/useMediaLibrary.ts +129 -0
- package/src/editor/hooks/usePageSettingsManager.ts +739 -0
- package/src/editor/hooks/useResizablePanel.ts +163 -0
- package/src/editor/hooks/useRichPaste.ts +255 -0
- package/src/editor/hooks/useTouchGestures.ts +197 -0
- package/src/editor/index.ts +27 -0
- package/src/editor/types/page-settings.ts +513 -0
- package/src/editor/types.ts +382 -0
- package/src/editor/utils/align-elements.ts +169 -0
- package/src/editor/utils/api-json.ts +21 -0
- package/src/editor/utils/aspect-lock.ts +50 -0
- package/src/editor/utils/component-renderer.ts +702 -0
- package/src/editor/utils/component-sync.ts +845 -0
- package/src/editor/utils/crop-mode.ts +265 -0
- package/src/editor/utils/dom-utils.ts +2238 -0
- package/src/editor/utils/element-effects.ts +218 -0
- package/src/editor/utils/eyedropper.ts +35 -0
- package/src/editor/utils/figma-export.ts +560 -0
- package/src/editor/utils/figma-kiwi-decoder.ts +722 -0
- package/src/editor/utils/figma-kiwi-encoder.ts +583 -0
- package/src/editor/utils/figma-paste.ts +2262 -0
- package/src/editor/utils/flex-reorder.ts +231 -0
- package/src/editor/utils/flex-utils.ts +1230 -0
- package/src/editor/utils/geometry.ts +232 -0
- package/src/editor/utils/grid-layout.ts +210 -0
- package/src/editor/utils/html-utils.ts +470 -0
- package/src/editor/utils/index.ts +11 -0
- package/src/editor/utils/ink-style.ts +57 -0
- package/src/editor/utils/inline-format.ts +236 -0
- package/src/editor/utils/measure-distance.ts +120 -0
- package/src/editor/utils/override-apply.ts +526 -0
- package/src/editor/utils/override-detection.ts +613 -0
- package/src/editor/utils/paste-processors.ts +609 -0
- package/src/editor/utils/paste-sanitizer.ts +244 -0
- package/src/editor/utils/restore-flow.ts +188 -0
- package/src/editor/utils/shape-library.ts +171 -0
- package/src/editor/utils/slide-root.ts +29 -0
- package/src/editor/utils/smart-guides.ts +218 -0
- package/src/editor/utils/style-utils.ts +685 -0
- package/src/editor/utils/table-edit.ts +151 -0
- package/src/editor/utils/tailwind-mappings.ts +778 -0
- package/src/editor/utils/tailwind-utils.ts +804 -0
- package/src/editor/utils/text-highlight.ts +216 -0
- package/src/editor/utils/theme-to-variables.ts +341 -0
- package/src/editor/utils/variant-resolver.ts +456 -0
- package/src/editor/utils/viewport-utils.ts +295 -0
- package/src/hooks/useEditorHistory.ts +195 -0
- package/src/hooks/useEditorShortcuts.ts +260 -0
- package/src/index.ts +40 -0
- package/src/io.ts +134 -0
- package/src/lib/agent/slide-agent/types.ts +547 -0
- package/src/lib/agent/website-agent/types.ts +805 -0
- package/src/lib/api/auth-fetch.ts +58 -0
- package/src/lib/deck.ts +69 -0
- package/src/lib/export.ts +47 -0
- package/src/lib/firebase/config.ts +29 -0
- package/src/lib/firebase/css-variables.ts +378 -0
- package/src/lib/firebase/editor-components.ts +677 -0
- package/src/lib/firebase/storage.ts +469 -0
- package/src/lib/utils.ts +6 -0
- package/src/styles/editor.css +52 -0
- package/src/styles/skin.css +122 -0
- package/src/types/css-variables.ts +645 -0
- package/src/types/editor-components.ts +944 -0
- package/src/types/editor.ts +420 -0
- package/src/types/page-master.ts +197 -0
- package/src/types/page.ts +419 -0
- package/src/types/slide.ts +402 -0
- package/src/types/website-theme.ts +788 -0
- package/src/types/website.ts +374 -0
- package/src/vendor/firebase-firestore.ts +13 -0
- package/src/vendor/firebase-functions.ts +17 -0
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MultiPageCanvasContext
|
|
5
|
+
*
|
|
6
|
+
* マルチページ無限キャンバスの状態管理Context
|
|
7
|
+
* 全ページを常にキャンバス上に表示し、クリックで即座にページ切替
|
|
8
|
+
* Figmaライクな統合ビュー(モード切替なし)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import React, {
|
|
12
|
+
createContext,
|
|
13
|
+
useContext,
|
|
14
|
+
useState,
|
|
15
|
+
useCallback,
|
|
16
|
+
useMemo,
|
|
17
|
+
useRef,
|
|
18
|
+
useEffect,
|
|
19
|
+
} from 'react';
|
|
20
|
+
import { useEditorArtboard } from './EditorArtboardContext';
|
|
21
|
+
import { useEditorHistory } from './EditorHistoryContext';
|
|
22
|
+
import { useEditorRefs } from './EditorRefsContext';
|
|
23
|
+
import { generateEditableHtml } from '../utils/html-utils';
|
|
24
|
+
|
|
25
|
+
// ============================================================
|
|
26
|
+
// 定数
|
|
27
|
+
// ============================================================
|
|
28
|
+
|
|
29
|
+
/** ウェブページの標準幅 (px) */
|
|
30
|
+
export const WEBPAGE_WIDTH = 1440;
|
|
31
|
+
/** ウェブページの最小高さ (px) */
|
|
32
|
+
export const WEBPAGE_MIN_HEIGHT = 900;
|
|
33
|
+
/** ページ間の水平間隔 (px) */
|
|
34
|
+
const LAYOUT_GAP_X = 100;
|
|
35
|
+
/** ズーム範囲 */
|
|
36
|
+
const MIN_ZOOM = 0.02;
|
|
37
|
+
const MAX_ZOOM = 2.0;
|
|
38
|
+
|
|
39
|
+
// ============================================================
|
|
40
|
+
// 型定義
|
|
41
|
+
// ============================================================
|
|
42
|
+
|
|
43
|
+
export interface PageFrame {
|
|
44
|
+
id: string;
|
|
45
|
+
title: string;
|
|
46
|
+
position: { x: number; y: number };
|
|
47
|
+
size: { width: number; height: number };
|
|
48
|
+
thumbnailHtml: string;
|
|
49
|
+
thumbnailDataUrl: string | null;
|
|
50
|
+
isDirty: boolean;
|
|
51
|
+
html: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface CanvasViewState {
|
|
55
|
+
canvasOffset: { x: number; y: number };
|
|
56
|
+
canvasZoom: number;
|
|
57
|
+
activePageId: string | null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface MultiPageCanvasContextValue {
|
|
61
|
+
// ページフレーム管理
|
|
62
|
+
pages: PageFrame[];
|
|
63
|
+
getPage: (id: string) => PageFrame | undefined;
|
|
64
|
+
updatePageFrame: (id: string, updates: Partial<PageFrame>) => void;
|
|
65
|
+
|
|
66
|
+
// ビュー状態
|
|
67
|
+
viewState: CanvasViewState;
|
|
68
|
+
setCanvasOffset: (offset: { x: number; y: number }) => void;
|
|
69
|
+
setCanvasZoom: (zoom: number) => void;
|
|
70
|
+
|
|
71
|
+
// ページ切替(Figmaスタイル: モード切替なし)
|
|
72
|
+
switchPage: (id: string) => void;
|
|
73
|
+
|
|
74
|
+
// 軽量ページフォーカス(iframe再読込なし、activePageId + contentId更新のみ)
|
|
75
|
+
focusPage: (id: string) => void;
|
|
76
|
+
|
|
77
|
+
// ページ配置
|
|
78
|
+
autoLayoutPages: () => void;
|
|
79
|
+
|
|
80
|
+
// ズーム操作
|
|
81
|
+
zoomToFit: (containerWidth: number, containerHeight: number) => void;
|
|
82
|
+
zoomIn: () => void;
|
|
83
|
+
zoomOut: () => void;
|
|
84
|
+
|
|
85
|
+
// サムネイル更新
|
|
86
|
+
updateThumbnail: (id: string, dataUrl: string) => void;
|
|
87
|
+
|
|
88
|
+
// 有効化フラグ
|
|
89
|
+
isEnabled: boolean;
|
|
90
|
+
|
|
91
|
+
// グローバルUndo/Redoハンドラー (MultiPageCanvasViewから登録される)
|
|
92
|
+
undo: () => void;
|
|
93
|
+
redo: () => void;
|
|
94
|
+
canUndo: boolean;
|
|
95
|
+
canRedo: boolean;
|
|
96
|
+
registerUndoHandlers: (handlers: { undo: () => void; redo: () => void; canUndo: () => boolean; canRedo: () => boolean }) => void;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ============================================================
|
|
100
|
+
// Context
|
|
101
|
+
// ============================================================
|
|
102
|
+
|
|
103
|
+
const MultiPageCanvasContext = createContext<MultiPageCanvasContextValue | null>(null);
|
|
104
|
+
|
|
105
|
+
export function useMultiPageCanvas(): MultiPageCanvasContextValue {
|
|
106
|
+
const context = useContext(MultiPageCanvasContext);
|
|
107
|
+
if (!context) {
|
|
108
|
+
throw new Error('useMultiPageCanvas must be used within MultiPageCanvasProvider');
|
|
109
|
+
}
|
|
110
|
+
return context;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* マルチページキャンバスが有効な場合のみ値を返す(オプショナル使用用)
|
|
115
|
+
*/
|
|
116
|
+
export function useMultiPageCanvasOptional(): MultiPageCanvasContextValue | null {
|
|
117
|
+
return useContext(MultiPageCanvasContext);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ============================================================
|
|
121
|
+
// レイアウト計算ユーティリティ
|
|
122
|
+
// ============================================================
|
|
123
|
+
|
|
124
|
+
function calculateAutoLayout(pages: PageFrame[]): PageFrame[] {
|
|
125
|
+
return pages.map((page, index) => ({
|
|
126
|
+
...page,
|
|
127
|
+
position: {
|
|
128
|
+
x: index * (WEBPAGE_WIDTH + LAYOUT_GAP_X),
|
|
129
|
+
y: 0,
|
|
130
|
+
},
|
|
131
|
+
size: { width: WEBPAGE_WIDTH, height: page.size.height || WEBPAGE_MIN_HEIGHT },
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function calculateFitZoom(
|
|
136
|
+
pages: PageFrame[],
|
|
137
|
+
containerWidth: number,
|
|
138
|
+
containerHeight: number
|
|
139
|
+
): { zoom: number; offset: { x: number; y: number } } {
|
|
140
|
+
if (pages.length === 0) return { zoom: 0.5, offset: { x: 0, y: 0 } };
|
|
141
|
+
|
|
142
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
143
|
+
pages.forEach(p => {
|
|
144
|
+
minX = Math.min(minX, p.position.x);
|
|
145
|
+
minY = Math.min(minY, p.position.y);
|
|
146
|
+
maxX = Math.max(maxX, p.position.x + p.size.width);
|
|
147
|
+
maxY = Math.max(maxY, p.position.y + p.size.height);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const totalWidth = maxX - minX;
|
|
151
|
+
const totalHeight = maxY - minY;
|
|
152
|
+
const padding = 80;
|
|
153
|
+
|
|
154
|
+
const scaleX = (containerWidth - padding * 2) / totalWidth;
|
|
155
|
+
const scaleY = (containerHeight - padding * 2) / totalHeight;
|
|
156
|
+
const zoom = Math.min(scaleX, scaleY, 1);
|
|
157
|
+
|
|
158
|
+
const offset = {
|
|
159
|
+
x: (containerWidth - totalWidth * zoom) / 2 - minX * zoom,
|
|
160
|
+
y: (containerHeight - totalHeight * zoom) / 2 - minY * zoom,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
return { zoom: Math.max(zoom, MIN_ZOOM), offset };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ============================================================
|
|
167
|
+
// Provider
|
|
168
|
+
// ============================================================
|
|
169
|
+
|
|
170
|
+
interface MultiPageCanvasProviderProps {
|
|
171
|
+
children: React.ReactNode;
|
|
172
|
+
enabled: boolean;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function MultiPageCanvasProvider({
|
|
176
|
+
children,
|
|
177
|
+
enabled,
|
|
178
|
+
}: MultiPageCanvasProviderProps) {
|
|
179
|
+
const artboard = useEditorArtboard();
|
|
180
|
+
const history = useEditorHistory();
|
|
181
|
+
const refs = useEditorRefs();
|
|
182
|
+
|
|
183
|
+
// ページフレーム状態
|
|
184
|
+
const [pagesMap, setPagesMap] = useState<Map<string, PageFrame>>(new Map());
|
|
185
|
+
const [isLayoutInitialized, setIsLayoutInitialized] = useState(false);
|
|
186
|
+
|
|
187
|
+
// ビュー状態(モードなし — 常に統合ビュー)
|
|
188
|
+
const [viewState, setViewState] = useState<CanvasViewState>({
|
|
189
|
+
canvasOffset: { x: 0, y: 0 },
|
|
190
|
+
canvasZoom: 0.15,
|
|
191
|
+
activePageId: null,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// contentListからページフレームを同期
|
|
195
|
+
const prevContentListRef = useRef<string>('');
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
if (!enabled) return;
|
|
198
|
+
|
|
199
|
+
const contentListKey = artboard.contentList.map(c => c.id).join(',');
|
|
200
|
+
if (contentListKey === prevContentListRef.current) return;
|
|
201
|
+
prevContentListRef.current = contentListKey;
|
|
202
|
+
|
|
203
|
+
setPagesMap(prev => {
|
|
204
|
+
const newMap = new Map<string, PageFrame>();
|
|
205
|
+
artboard.contentList.forEach(content => {
|
|
206
|
+
const existing = prev.get(content.id);
|
|
207
|
+
if (existing) {
|
|
208
|
+
newMap.set(content.id, {
|
|
209
|
+
...existing,
|
|
210
|
+
title: content.title,
|
|
211
|
+
html: content.thumbnailHtml || existing.html,
|
|
212
|
+
thumbnailHtml: content.thumbnailHtml || existing.thumbnailHtml,
|
|
213
|
+
});
|
|
214
|
+
} else {
|
|
215
|
+
newMap.set(content.id, {
|
|
216
|
+
id: content.id,
|
|
217
|
+
title: content.title,
|
|
218
|
+
position: { x: 0, y: 0 },
|
|
219
|
+
size: { width: WEBPAGE_WIDTH, height: WEBPAGE_MIN_HEIGHT },
|
|
220
|
+
thumbnailHtml: content.thumbnailHtml || '',
|
|
221
|
+
thumbnailDataUrl: null,
|
|
222
|
+
isDirty: false,
|
|
223
|
+
html: content.thumbnailHtml || '',
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
return newMap;
|
|
228
|
+
});
|
|
229
|
+
setIsLayoutInitialized(false);
|
|
230
|
+
}, [enabled, artboard.contentList]);
|
|
231
|
+
|
|
232
|
+
// 初期レイアウト
|
|
233
|
+
useEffect(() => {
|
|
234
|
+
if (!enabled || isLayoutInitialized || pagesMap.size === 0) return;
|
|
235
|
+
setPagesMap(prev => {
|
|
236
|
+
const pages = Array.from(prev.values());
|
|
237
|
+
const laid = calculateAutoLayout(pages);
|
|
238
|
+
const newMap = new Map<string, PageFrame>();
|
|
239
|
+
laid.forEach(p => newMap.set(p.id, p));
|
|
240
|
+
return newMap;
|
|
241
|
+
});
|
|
242
|
+
setIsLayoutInitialized(true);
|
|
243
|
+
}, [enabled, isLayoutInitialized, pagesMap.size]);
|
|
244
|
+
|
|
245
|
+
// 初回: 最初のページを自動アクティブ化
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
if (!enabled || viewState.activePageId) return;
|
|
248
|
+
if (artboard.contentList.length > 0) {
|
|
249
|
+
const firstId = artboard.contentList[0].id;
|
|
250
|
+
setViewState(prev => ({ ...prev, activePageId: firstId }));
|
|
251
|
+
}
|
|
252
|
+
}, [enabled, artboard.contentList, viewState.activePageId]);
|
|
253
|
+
|
|
254
|
+
// ページ配列(順序付き)
|
|
255
|
+
const pages = useMemo(() => {
|
|
256
|
+
const contentOrder = artboard.contentList.map(c => c.id);
|
|
257
|
+
return Array.from(pagesMap.values()).sort(
|
|
258
|
+
(a, b) => contentOrder.indexOf(a.id) - contentOrder.indexOf(b.id)
|
|
259
|
+
);
|
|
260
|
+
}, [pagesMap, artboard.contentList]);
|
|
261
|
+
|
|
262
|
+
// --- コールバック ---
|
|
263
|
+
|
|
264
|
+
const getPage = useCallback((id: string) => pagesMap.get(id), [pagesMap]);
|
|
265
|
+
|
|
266
|
+
const updatePageFrame = useCallback((id: string, updates: Partial<PageFrame>) => {
|
|
267
|
+
setPagesMap(prev => {
|
|
268
|
+
const existing = prev.get(id);
|
|
269
|
+
if (!existing) return prev;
|
|
270
|
+
const newMap = new Map(prev);
|
|
271
|
+
newMap.set(id, { ...existing, ...updates });
|
|
272
|
+
return newMap;
|
|
273
|
+
});
|
|
274
|
+
}, []);
|
|
275
|
+
|
|
276
|
+
const setCanvasOffset = useCallback((offset: { x: number; y: number }) => {
|
|
277
|
+
setViewState(prev => ({ ...prev, canvasOffset: offset }));
|
|
278
|
+
}, []);
|
|
279
|
+
|
|
280
|
+
const setCanvasZoom = useCallback((zoom: number) => {
|
|
281
|
+
setViewState(prev => ({
|
|
282
|
+
...prev,
|
|
283
|
+
canvasZoom: Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, zoom)),
|
|
284
|
+
}));
|
|
285
|
+
}, []);
|
|
286
|
+
|
|
287
|
+
// Stale closure回避用ref
|
|
288
|
+
const activePageIdRef = useRef<string | null>(null);
|
|
289
|
+
activePageIdRef.current = viewState.activePageId;
|
|
290
|
+
const pagesMapRef = useRef(pagesMap);
|
|
291
|
+
pagesMapRef.current = pagesMap;
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* ページ切替(Figmaスタイル)
|
|
295
|
+
* 1. 現在のページのHTMLをサムネイルに保存
|
|
296
|
+
* 2. 新しいページのキャッシュHTMLをiframeにロード
|
|
297
|
+
* 3. 履歴クリア
|
|
298
|
+
* 4. activePageId更新
|
|
299
|
+
* 5. API経由で最新HTML非同期取得
|
|
300
|
+
*/
|
|
301
|
+
const switchPage = useCallback((id: string) => {
|
|
302
|
+
const currentPagesMap = pagesMapRef.current;
|
|
303
|
+
const page = currentPagesMap.get(id);
|
|
304
|
+
if (!page) {
|
|
305
|
+
console.warn('[MultiPageCanvas] switchPage: page not found:', id);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const currentActiveId = activePageIdRef.current;
|
|
310
|
+
if (currentActiveId === id) return; // 同じページなら何もしない
|
|
311
|
+
|
|
312
|
+
// 1. 現在のページのHTMLをサムネイルに保存
|
|
313
|
+
if (currentActiveId) {
|
|
314
|
+
const currentEditHtml = history.html;
|
|
315
|
+
if (currentEditHtml) {
|
|
316
|
+
updatePageFrame(currentActiveId, { thumbnailHtml: currentEditHtml });
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// 2. 新しいページのHTMLをセット
|
|
321
|
+
const newHtml = page.thumbnailHtml || '';
|
|
322
|
+
history.setHtml(newHtml);
|
|
323
|
+
history.clearHistory();
|
|
324
|
+
|
|
325
|
+
// 3. iframeを新しいHTMLでリロード
|
|
326
|
+
const iframe = refs.iframeRef.current;
|
|
327
|
+
if (iframe) {
|
|
328
|
+
refs.setIframeReady(false);
|
|
329
|
+
const blob = new Blob([generateEditableHtml(newHtml, 'webpage')], { type: 'text/html' });
|
|
330
|
+
iframe.src = URL.createObjectURL(blob);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 4. activePageId更新
|
|
334
|
+
setViewState(prev => ({ ...prev, activePageId: id }));
|
|
335
|
+
|
|
336
|
+
// 5. API経由で最新HTMLを非同期取得
|
|
337
|
+
if (artboard.onContentChange && id !== artboard.currentContentId) {
|
|
338
|
+
artboard.onContentChange(id);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
console.log('[MultiPageCanvas] switchPage:', currentActiveId, '→', id);
|
|
342
|
+
}, [history, refs, artboard, updatePageFrame]);
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* 軽量ページフォーカス(キャンバスモード専用)
|
|
346
|
+
* iframeを再読込せず、activePageIdとcontentIdのみ更新
|
|
347
|
+
* → レイヤーパネル・CSS/JS・ページ設定が正しいページに切り替わる
|
|
348
|
+
*/
|
|
349
|
+
const focusPage = useCallback((id: string) => {
|
|
350
|
+
const currentActiveId = activePageIdRef.current;
|
|
351
|
+
if (currentActiveId === id) return;
|
|
352
|
+
|
|
353
|
+
// activePageId更新 → レイヤーパネルが正しいページを表示
|
|
354
|
+
setViewState(prev => ({ ...prev, activePageId: id }));
|
|
355
|
+
|
|
356
|
+
// contentId更新 → CSS/JS/ページ設定が正しいページから読み込まれる
|
|
357
|
+
if (artboard.onContentChange && id !== artboard.currentContentId) {
|
|
358
|
+
artboard.onContentChange(id);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
console.log('[MultiPageCanvas] focusPage:', currentActiveId, '→', id);
|
|
362
|
+
}, [artboard]);
|
|
363
|
+
|
|
364
|
+
const autoLayoutPages = useCallback(() => {
|
|
365
|
+
setPagesMap(prev => {
|
|
366
|
+
const pageArray = Array.from(prev.values());
|
|
367
|
+
const laid = calculateAutoLayout(pageArray);
|
|
368
|
+
const newMap = new Map<string, PageFrame>();
|
|
369
|
+
laid.forEach(p => newMap.set(p.id, p));
|
|
370
|
+
return newMap;
|
|
371
|
+
});
|
|
372
|
+
}, []);
|
|
373
|
+
|
|
374
|
+
const zoomToFit = useCallback((containerWidth: number, containerHeight: number) => {
|
|
375
|
+
const pageArray = Array.from(pagesMap.values());
|
|
376
|
+
const { zoom, offset } = calculateFitZoom(pageArray, containerWidth, containerHeight);
|
|
377
|
+
setViewState(prev => ({
|
|
378
|
+
...prev,
|
|
379
|
+
canvasZoom: zoom,
|
|
380
|
+
canvasOffset: offset,
|
|
381
|
+
}));
|
|
382
|
+
}, [pagesMap]);
|
|
383
|
+
|
|
384
|
+
const zoomIn = useCallback(() => {
|
|
385
|
+
setViewState(prev => ({
|
|
386
|
+
...prev,
|
|
387
|
+
canvasZoom: Math.min(MAX_ZOOM, prev.canvasZoom * 1.25),
|
|
388
|
+
}));
|
|
389
|
+
}, []);
|
|
390
|
+
|
|
391
|
+
const zoomOut = useCallback(() => {
|
|
392
|
+
setViewState(prev => ({
|
|
393
|
+
...prev,
|
|
394
|
+
canvasZoom: Math.max(MIN_ZOOM, prev.canvasZoom * 0.8),
|
|
395
|
+
}));
|
|
396
|
+
}, []);
|
|
397
|
+
|
|
398
|
+
const updateThumbnail = useCallback((id: string, dataUrl: string) => {
|
|
399
|
+
setPagesMap(prev => {
|
|
400
|
+
const existing = prev.get(id);
|
|
401
|
+
if (!existing) return prev;
|
|
402
|
+
const newMap = new Map(prev);
|
|
403
|
+
newMap.set(id, { ...existing, thumbnailDataUrl: dataUrl });
|
|
404
|
+
return newMap;
|
|
405
|
+
});
|
|
406
|
+
}, []);
|
|
407
|
+
|
|
408
|
+
// --- グローバルUndo/Redoハンドラー用のState ---
|
|
409
|
+
const [undoHandlers, setUndoHandlers] = useState({
|
|
410
|
+
undo: () => {},
|
|
411
|
+
redo: () => {},
|
|
412
|
+
canUndo: false,
|
|
413
|
+
canRedo: false,
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
const registerUndoHandlers = useCallback((handlers: { undo: () => void; redo: () => void; canUndo: () => boolean; canRedo: () => boolean }) => {
|
|
417
|
+
// 参照を保持し、再レンダリングをトリガーするためにはcanUndo()の状態を追跡する必要があるが、
|
|
418
|
+
// ここでは簡易的に関数のみ登録し、判定は都度させる(必要に応じてフラグ化)
|
|
419
|
+
setUndoHandlers({
|
|
420
|
+
undo: handlers.undo,
|
|
421
|
+
redo: handlers.redo,
|
|
422
|
+
// React state is not ideal for this real-time check inside Toolbar, but we pass getters for now
|
|
423
|
+
canUndo: handlers.canUndo(),
|
|
424
|
+
canRedo: handlers.canRedo(),
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// To dynamically update Toolbar when stacks change, we'd need MultiPageCanvasView to call this repeatedly,
|
|
428
|
+
// or expose them as refs. For now, Toolbar evaluates button state on render or we expose static getters.
|
|
429
|
+
// Let's modify the interface to use getters/setters properly if needed, but for now just pass the object.
|
|
430
|
+
}, []);
|
|
431
|
+
|
|
432
|
+
// --- Context値 ---
|
|
433
|
+
|
|
434
|
+
const value = useMemo<MultiPageCanvasContextValue>(() => ({
|
|
435
|
+
pages,
|
|
436
|
+
getPage,
|
|
437
|
+
updatePageFrame,
|
|
438
|
+
viewState,
|
|
439
|
+
setCanvasOffset,
|
|
440
|
+
setCanvasZoom,
|
|
441
|
+
switchPage,
|
|
442
|
+
focusPage,
|
|
443
|
+
autoLayoutPages,
|
|
444
|
+
zoomToFit,
|
|
445
|
+
zoomIn,
|
|
446
|
+
zoomOut,
|
|
447
|
+
updateThumbnail,
|
|
448
|
+
isEnabled: enabled,
|
|
449
|
+
undo: undoHandlers.undo,
|
|
450
|
+
redo: undoHandlers.redo,
|
|
451
|
+
canUndo: undoHandlers.canUndo,
|
|
452
|
+
canRedo: undoHandlers.canRedo,
|
|
453
|
+
registerUndoHandlers,
|
|
454
|
+
}), [
|
|
455
|
+
pages,
|
|
456
|
+
getPage,
|
|
457
|
+
updatePageFrame,
|
|
458
|
+
viewState,
|
|
459
|
+
setCanvasOffset,
|
|
460
|
+
setCanvasZoom,
|
|
461
|
+
switchPage,
|
|
462
|
+
focusPage,
|
|
463
|
+
autoLayoutPages,
|
|
464
|
+
zoomToFit,
|
|
465
|
+
zoomIn,
|
|
466
|
+
zoomOut,
|
|
467
|
+
updateThumbnail,
|
|
468
|
+
enabled,
|
|
469
|
+
undoHandlers,
|
|
470
|
+
registerUndoHandlers,
|
|
471
|
+
]);
|
|
472
|
+
|
|
473
|
+
return (
|
|
474
|
+
<MultiPageCanvasContext.Provider value={value}>
|
|
475
|
+
{children}
|
|
476
|
+
</MultiPageCanvasContext.Provider>
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* 条件付きProvider: enabledがfalseの場合はchildrenをそのまま返す
|
|
482
|
+
*/
|
|
483
|
+
export function ConditionalMultiPageProvider({
|
|
484
|
+
enabled,
|
|
485
|
+
children,
|
|
486
|
+
}: {
|
|
487
|
+
enabled: boolean;
|
|
488
|
+
children: React.ReactNode;
|
|
489
|
+
}) {
|
|
490
|
+
if (!enabled) {
|
|
491
|
+
return <>{children}</>;
|
|
492
|
+
}
|
|
493
|
+
return (
|
|
494
|
+
<MultiPageCanvasProvider enabled={enabled}>
|
|
495
|
+
{children}
|
|
496
|
+
</MultiPageCanvasProvider>
|
|
497
|
+
);
|
|
498
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* エディタContext統合エクスポート
|
|
3
|
+
*
|
|
4
|
+
* Phase 1: Context分割によるパフォーマンス最適化
|
|
5
|
+
* - 51個のstateを8つの専門Contextに分割
|
|
6
|
+
* - ファサードパターンで後方互換性を維持
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// 個別Context(パフォーマンス最適化用)
|
|
10
|
+
export { EditorRefsProvider, useEditorRefs, type EditorRefsContextValue } from './EditorRefsContext';
|
|
11
|
+
export { EditorSelectionProvider, useEditorSelection, type EditorSelectionContextValue } from './EditorSelectionContext';
|
|
12
|
+
export { EditorToolProvider, useEditorTool, type EditorToolContextValue } from './EditorToolContext';
|
|
13
|
+
export { EditorViewProvider, useEditorView, type EditorViewContextValue } from './EditorViewContext';
|
|
14
|
+
export { EditorHistoryProvider, useEditorHistory, type EditorHistoryContextValue } from './EditorHistoryContext';
|
|
15
|
+
export { EditorDocumentProvider, useEditorDocument, type EditorDocumentContextValue } from './EditorDocumentContext';
|
|
16
|
+
export { EditorArtboardProvider, useEditorArtboard, type EditorArtboardContextValue } from './EditorArtboardContext';
|
|
17
|
+
export { EditorUIStateProvider, useEditorUIState, type EditorUIStateContextValue } from './EditorUIStateContext';
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [移植時の追加] エディタの配色スキン。配色の正本はこの1ファイル。
|
|
3
|
+
*
|
|
4
|
+
* 移植元のエディタはVS Code風のグレーが47ファイルに直書きされている。
|
|
5
|
+
* 全ファイルを書き換えるとマージが破綻するので、エディタのルートに `.gg-editor-skin` を付け、
|
|
6
|
+
* その配下でだけ元のクラスが指す色を差し替える。
|
|
7
|
+
*
|
|
8
|
+
* 意匠はFigmaデスクトップ(ダーク)の実測値:
|
|
9
|
+
* キャンバス=#1e1e1e / パネル=#2c2c2c / 入力=#383838 / 罫線=white 10% / アクセント=#0d99ff
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
.gg-editor-skin {
|
|
13
|
+
--ed-bg: #1e1e1e; /* キャンバスの地(Figmaのカンバス) */
|
|
14
|
+
--ed-panel: #2c2c2c; /* パネル・ツールバーの面 */
|
|
15
|
+
--ed-panel-raised: #2c2c2c; /* Figmaはパネルとカードが同面 */
|
|
16
|
+
--ed-surface: #383838; /* 入力・ボタンの面 */
|
|
17
|
+
--ed-surface-hover: #444444;
|
|
18
|
+
--ed-line: rgba(255, 255, 255, 0.1);
|
|
19
|
+
--ed-line-strong: rgba(255, 255, 255, 0.16);
|
|
20
|
+
--ed-accent: #0d99ff; /* Figmaブルー */
|
|
21
|
+
--ed-accent-strong: #0d99ff;
|
|
22
|
+
--ed-accent-hover: #0c8ce9;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* ── 地・面 ───────────────────────────────────────── */
|
|
26
|
+
.gg-editor-skin .bg-\[\#1e1e1e\] {
|
|
27
|
+
background-color: var(--ed-bg);
|
|
28
|
+
}
|
|
29
|
+
.gg-editor-skin .bg-\[\#2c2c2c\],
|
|
30
|
+
.gg-editor-skin .bg-\[\#2a2a2a\] {
|
|
31
|
+
background-color: var(--ed-panel);
|
|
32
|
+
}
|
|
33
|
+
.gg-editor-skin .bg-\[\#383838\] {
|
|
34
|
+
background-color: var(--ed-surface);
|
|
35
|
+
}
|
|
36
|
+
.gg-editor-skin .bg-\[\#444444\],
|
|
37
|
+
.gg-editor-skin .bg-\[\#4a4a4a\],
|
|
38
|
+
.gg-editor-skin .bg-\[\#5d5d5d\] {
|
|
39
|
+
background-color: var(--ed-surface-hover);
|
|
40
|
+
}
|
|
41
|
+
.gg-editor-skin .hover\:bg-\[\#444444\]:hover,
|
|
42
|
+
.gg-editor-skin .hover\:bg-\[\#383838\]:hover {
|
|
43
|
+
background-color: rgba(255, 255, 255, 0.08);
|
|
44
|
+
}
|
|
45
|
+
.gg-editor-skin .hover\:bg-\[\#4a4a4a\]:hover {
|
|
46
|
+
background-color: var(--ed-surface-hover);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* ── 罫線 ─────────────────────────────────────────── */
|
|
50
|
+
.gg-editor-skin .border-\[\#444444\],
|
|
51
|
+
.gg-editor-skin .border-\[\#383838\],
|
|
52
|
+
.gg-editor-skin .border-\[\#2c2c2c\] {
|
|
53
|
+
border-color: var(--ed-line);
|
|
54
|
+
}
|
|
55
|
+
.gg-editor-skin .border-\[\#4a4a4a\],
|
|
56
|
+
.gg-editor-skin .border-\[\#5d5d5d\] {
|
|
57
|
+
border-color: var(--ed-line-strong);
|
|
58
|
+
}
|
|
59
|
+
.gg-editor-skin .hover\:border-\[\#4a4a4a\]:hover,
|
|
60
|
+
.gg-editor-skin .hover\:border-\[\#5d5d5d\]:hover {
|
|
61
|
+
border-color: rgba(13, 153, 255, 0.6);
|
|
62
|
+
}
|
|
63
|
+
/* 区切り線として使われている塗り */
|
|
64
|
+
.gg-editor-skin .w-px.bg-\[\#444444\],
|
|
65
|
+
.gg-editor-skin .h-px.bg-\[\#444444\] {
|
|
66
|
+
background-color: var(--ed-line);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* ── アクセント(選択・保存・共有ボタン等) ─────────── */
|
|
70
|
+
.gg-editor-skin .bg-\[\#0d99ff\],
|
|
71
|
+
.gg-editor-skin .bg-blue-500,
|
|
72
|
+
.gg-editor-skin .bg-blue-600 {
|
|
73
|
+
background-color: var(--ed-accent-strong);
|
|
74
|
+
}
|
|
75
|
+
.gg-editor-skin .hover\:bg-\[\#0c8ce9\]:hover,
|
|
76
|
+
.gg-editor-skin .hover\:bg-blue-600:hover,
|
|
77
|
+
.gg-editor-skin .hover\:bg-blue-700:hover {
|
|
78
|
+
background-color: var(--ed-accent-hover);
|
|
79
|
+
}
|
|
80
|
+
.gg-editor-skin .text-\[\#0d99ff\],
|
|
81
|
+
.gg-editor-skin .text-\[\#4fb8ff\],
|
|
82
|
+
.gg-editor-skin .text-blue-400,
|
|
83
|
+
.gg-editor-skin .text-blue-500 {
|
|
84
|
+
color: var(--ed-accent);
|
|
85
|
+
}
|
|
86
|
+
.gg-editor-skin .border-\[\#0d99ff\],
|
|
87
|
+
.gg-editor-skin .border-blue-500 {
|
|
88
|
+
border-color: var(--ed-accent);
|
|
89
|
+
}
|
|
90
|
+
.gg-editor-skin .ring-\[\#0d99ff\],
|
|
91
|
+
.gg-editor-skin .ring-blue-500 {
|
|
92
|
+
--tw-ring-color: var(--ed-accent);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* ── 文字色(Figmaの白/70%/45%) ────────────────────── */
|
|
96
|
+
.gg-editor-skin .text-gray-300 {
|
|
97
|
+
color: rgba(255, 255, 255, 0.85);
|
|
98
|
+
}
|
|
99
|
+
.gg-editor-skin .text-gray-400 {
|
|
100
|
+
color: rgba(255, 255, 255, 0.7);
|
|
101
|
+
}
|
|
102
|
+
.gg-editor-skin .text-gray-500 {
|
|
103
|
+
color: rgba(255, 255, 255, 0.45);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* ── 角丸・影(Figmaのメニュー影) ─────────────────── */
|
|
107
|
+
.gg-editor-skin .rounded {
|
|
108
|
+
border-radius: 0.375rem;
|
|
109
|
+
}
|
|
110
|
+
.gg-editor-skin .shadow-lg,
|
|
111
|
+
.gg-editor-skin .shadow-xl,
|
|
112
|
+
.gg-editor-skin .shadow-2xl {
|
|
113
|
+
box-shadow: 0 5px 17px rgba(0, 0, 0, 0.35), 0 2px 7px rgba(0, 0, 0, 0.15);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* ── フォーカスリング(Figmaブルー) ────────────────── */
|
|
117
|
+
.gg-editor-skin input:focus-visible,
|
|
118
|
+
.gg-editor-skin button:focus-visible,
|
|
119
|
+
.gg-editor-skin select:focus-visible {
|
|
120
|
+
outline: 2px solid var(--ed-accent);
|
|
121
|
+
outline-offset: 1px;
|
|
122
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* エディタフック群のエクスポート
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// 既存のhooks
|
|
6
|
+
export { useEditorMessages, postEditorMessage } from './useEditorMessages';
|
|
7
|
+
export { useDrawingMode } from './useDrawingMode';
|
|
8
|
+
export { useElementActions } from './useElementActions';
|
|
9
|
+
export { useCanvasControls, editorZoomApiRef } from './useCanvasControls';
|
|
10
|
+
export { useImageUpload } from './useImageUpload';
|
|
11
|
+
export { useMediaLibrary, invalidateMediaLibraryCache, toEditorMediaUrl } from './useMediaLibrary';
|
|
12
|
+
export type { MediaItem } from './useMediaLibrary';
|
|
13
|
+
export { useRichPaste } from './useRichPaste';
|
|
14
|
+
export { useEditorColors } from './useEditorColors';
|
|
15
|
+
export { useAiReplace } from './useAiReplace';
|
|
16
|
+
|
|
17
|
+
// EditorCanvas.tsx から分割した新規hooks
|
|
18
|
+
export { useTouchGestures } from './useTouchGestures';
|
|
19
|
+
export { useKeyboardShortcuts } from './useKeyboardShortcuts';
|
|
20
|
+
export { useCoordinateTransform } from './useCoordinateTransform';
|
|
21
|
+
export { useElementSelection } from './useElementSelection';
|
|
22
|
+
export { useMarqueeSelection } from './useMarqueeSelection';
|
|
23
|
+
export { useDragResize } from './useDragResize';
|
|
24
|
+
export { useContextMenuHandler } from './useContextMenuHandler';
|
|
25
|
+
export { useIframeSetup } from './useIframeSetup';
|
|
26
|
+
export { useFocusManagement } from './useFocusManagement';
|
|
27
|
+
export { useResizablePanel } from './useResizablePanel';
|
|
28
|
+
|
|
29
|
+
// Component system hooks
|
|
30
|
+
export { useComponentInstances } from './useComponentInstances';
|
|
31
|
+
|
|
32
|
+
// FrontendVisualEditorInner から分割した新規hooks
|
|
33
|
+
export { useBrowserZoomPrevention } from './useBrowserZoomPrevention';
|
|
34
|
+
export { usePageSettingsManager } from './usePageSettingsManager';
|
|
35
|
+
export { useComponentEditMode } from './useComponentEditMode';
|
|
36
|
+
|
|
37
|
+
/** @deprecated Use useEditorColors instead */
|
|
38
|
+
export { useEditorColors as useSlideColors } from './useEditorColors';
|