@growgroup/visual-editor 0.3.1 → 0.4.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 (60) hide show
  1. package/README.md +6 -1
  2. package/dist/editor.css +1 -1
  3. package/package.json +1 -1
  4. package/src/editor/EditorContext.tsx +3 -2
  5. package/src/editor/EditorToolbar.tsx +8 -2
  6. package/src/editor/FrontendVisualEditor.tsx +114 -44
  7. package/src/editor/components/EditorCanvas.tsx +73 -15
  8. package/src/editor/components/EditorFooter.tsx +2 -0
  9. package/src/editor/components/EditorHeader.tsx +3 -2
  10. package/src/editor/components/FigmaColorPicker.tsx +2 -1
  11. package/src/editor/components/JsEditorDialog.tsx +1 -0
  12. package/src/editor/components/PageSettingsDialog.tsx +1 -0
  13. package/src/editor/components/comments/CommentBoard.tsx +9 -6
  14. package/src/editor/components/multi-page/CanvasFrameDecor.tsx +172 -0
  15. package/src/editor/components/multi-page/MultiPageCanvasView.tsx +173 -77
  16. package/src/editor/components/multi-page/PageFramePreview.tsx +96 -20
  17. package/src/editor/components/multi-page/index.ts +1 -0
  18. package/src/editor/components/ppt/PptChrome.tsx +6 -2
  19. package/src/editor/components/ppt/PptComments.tsx +411 -71
  20. package/src/editor/constants.ts +16 -1
  21. package/src/editor/contexts/EditorArtboardContext.tsx +8 -0
  22. package/src/editor/contexts/EditorComponentsContext.tsx +31 -27
  23. package/src/editor/contexts/EditorUIStateContext.tsx +4 -3
  24. package/src/editor/contexts/EditorVariablesContext.tsx +5 -4
  25. package/src/editor/contexts/MultiPageCanvasContext.tsx +26 -2
  26. package/src/editor/hooks/useAiReplace.ts +4 -3
  27. package/src/editor/hooks/useBrowserZoomPrevention.ts +2 -1
  28. package/src/editor/hooks/useComponentEditMode.ts +27 -26
  29. package/src/editor/hooks/useEditorMessages.ts +3 -2
  30. package/src/editor/hooks/useElementActions.ts +27 -26
  31. package/src/editor/hooks/useElementSelection.ts +19 -16
  32. package/src/editor/hooks/useFocusManagement.ts +7 -6
  33. package/src/editor/hooks/useGoogleFonts.ts +3 -2
  34. package/src/editor/hooks/useIframeInitializer.ts +6 -3
  35. package/src/editor/hooks/useIframeSetup.ts +4 -3
  36. package/src/editor/hooks/useImageUpload.ts +4 -3
  37. package/src/editor/hooks/useMarqueeSelection.ts +2 -1
  38. package/src/editor/hooks/usePageSettingsManager.ts +24 -23
  39. package/src/editor/hooks/useRichPaste.ts +11 -10
  40. package/src/editor/parts.ts +7 -25
  41. package/src/editor/utils/component-renderer.ts +8 -7
  42. package/src/editor/utils/component-sync.ts +16 -15
  43. package/src/editor/utils/debug.ts +52 -0
  44. package/src/editor/utils/dom-utils.ts +11 -10
  45. package/src/editor/utils/drop-target.ts +135 -0
  46. package/src/editor/utils/eyedropper.ts +3 -1
  47. package/src/editor/utils/figma-export.ts +6 -5
  48. package/src/editor/utils/figma-kiwi-decoder.ts +28 -27
  49. package/src/editor/utils/figma-kiwi-encoder.ts +9 -8
  50. package/src/editor/utils/figma-paste.ts +57 -56
  51. package/src/editor/utils/flex-utils.ts +12 -10
  52. package/src/editor/utils/override-apply.ts +3 -2
  53. package/src/editor/utils/override-detection.ts +11 -10
  54. package/src/editor/utils/tailwind-utils.ts +4 -3
  55. package/src/io.ts +22 -1
  56. package/src/lib/firebase/css-variables.ts +4 -3
  57. package/src/lib/firebase/storage.ts +3 -2
  58. package/src/styles/skin.css +9 -4
  59. package/src/types/editor.ts +6 -2
  60. package/src/version.ts +1 -1
@@ -38,6 +38,14 @@ export interface ContentListItem {
38
38
  parentId?: string | null;
39
39
  /** 編集しない表示(ページそのもの・ビューア)の URL。渡すとフレーム名と一覧に「別タブで開く」が付く */
40
40
  href?: string;
41
+ /**
42
+ * ページ全体を写した画像(PNG / JPEG)の URL。幅は 480px 以上、縦横比はページと同じ。
43
+ * マルチフレームのキャンバスが大きく縮小されている間(倍率 0.5 未満)は、見るだけの紙面を
44
+ * srcdoc の iframe ではなくこの画像で描く(26 枚の iframe をラスタライズし直さずに済む)。
45
+ * 内容が変わったら URL も変える(`&v=…`)。変わると紙面が読み直す。
46
+ * 渡さないページ・画像が読めなかったページは、今までどおり iframe で描く
47
+ */
48
+ thumbnail?: string;
41
49
  /**
42
50
  * 本文の版。利用側でファイルが外から変わったとき(部品の一括反映・CLI・他の人の編集)に
43
51
  * 数を進めると、キャンバスの見るだけの紙面がそのページを読み直す。編集中のページには効かない
@@ -46,6 +46,7 @@ import {
46
46
  import { io } from '../../io';
47
47
  import type { EditorPartDef, EditorPartsLibrary } from '../../io';
48
48
  import { useMultiPageCanvasOptional } from './MultiPageCanvasContext';
49
+ import { debugLog, isDebugEnabled } from '../utils/debug';
49
50
  import {
50
51
  materializePart,
51
52
  partDefFromElement,
@@ -211,7 +212,7 @@ export function htmlElementToComponentElement(element: HTMLElement, isRoot: bool
211
212
  if (key && value) {
212
213
  // Skip position-related styles for root element
213
214
  if (isRoot && POSITION_STYLES.has(key)) {
214
- console.log(`[htmlElementToComponentElement] Skipping position style for root: ${key}`);
215
+ debugLog(`[htmlElementToComponentElement] Skipping position style for root: ${key}`);
215
216
  continue;
216
217
  }
217
218
  // Convert kebab-case to camelCase
@@ -238,7 +239,7 @@ export function htmlElementToComponentElement(element: HTMLElement, isRoot: bool
238
239
  if (hasMixedContent) {
239
240
  // Mixed content: use innerHTML to preserve all content
240
241
  innerHTML = element.innerHTML;
241
- console.log('[htmlElementToComponentElement] Mixed content detected, using innerHTML');
242
+ debugLog('[htmlElementToComponentElement] Mixed content detected, using innerHTML');
242
243
  } else if (hasElementChildren) {
243
244
  // Only element children: recursively process (not root)
244
245
  for (const child of Array.from(element.children)) {
@@ -261,17 +262,20 @@ export function htmlElementToComponentElement(element: HTMLElement, isRoot: bool
261
262
  overridable: { ...defaultOverridable },
262
263
  };
263
264
 
264
- // Debug logging
265
- console.log('[htmlElementToComponentElement] Converted:', {
266
- tagName: result.tagName,
267
- className: result.className,
268
- hasTextContent: !!result.textContent,
269
- hasInnerHTML: !!result.innerHTML,
270
- textContentPreview: result.textContent?.substring(0, 50),
271
- innerHTMLPreview: result.innerHTML?.substring(0, 50),
272
- childrenCount: result.children.length,
273
- stylesCount: Object.keys(result.styles).length,
274
- });
265
+ // 要素 1 つにつき 1 行出る。部品 89 個 + 26 ページで 2,000 件を超えるので、
266
+ // 出さないときは引数のオブジェクトを組み立てること自体をやめる
267
+ if (isDebugEnabled()) {
268
+ debugLog('[htmlElementToComponentElement] Converted:', {
269
+ tagName: result.tagName,
270
+ className: result.className,
271
+ hasTextContent: !!result.textContent,
272
+ hasInnerHTML: !!result.innerHTML,
273
+ textContentPreview: result.textContent?.substring(0, 50),
274
+ innerHTMLPreview: result.innerHTML?.substring(0, 50),
275
+ childrenCount: result.children.length,
276
+ stylesCount: Object.keys(result.styles).length,
277
+ });
278
+ }
275
279
 
276
280
  return result;
277
281
  }
@@ -586,7 +590,7 @@ export function EditorComponentsProvider({
586
590
  setIsLoadingComponents(true);
587
591
  setError(null);
588
592
  try {
589
- console.log(
593
+ debugLog(
590
594
  `[EditorComponentsContext] Loading components for website: ${targetWebsiteId}`
591
595
  );
592
596
 
@@ -610,13 +614,13 @@ export function EditorComponentsProvider({
610
614
  setMasterComponents(componentsMap);
611
615
  setComponentLibrary(categoriesFromParts(lib, componentsMap));
612
616
  setComponentInstances(new Map());
613
- console.log(`[EditorComponentsContext] Loaded ${defs.size} parts`);
617
+ debugLog(`[EditorComponentsContext] Loaded ${defs.size} parts`);
614
618
  return;
615
619
  }
616
620
 
617
621
  // Load master components from Firestore
618
622
  const components = await getAllMasterComponents(targetWebsiteId);
619
- console.log(`[EditorComponentsContext] Loaded ${components.length} components`);
623
+ debugLog(`[EditorComponentsContext] Loaded ${components.length} components`);
620
624
 
621
625
  // Create Map from components
622
626
  const componentsMap = new Map<string, MasterComponent>();
@@ -689,9 +693,9 @@ export function EditorComponentsProvider({
689
693
 
690
694
  const loadInstances = async () => {
691
695
  try {
692
- console.log(`[EditorComponentsContext] Loading instances for page: ${pageId}`);
696
+ debugLog(`[EditorComponentsContext] Loading instances for page: ${pageId}`);
693
697
  const instances = await getPageComponentInstances(websiteId, pageId);
694
- console.log(`[EditorComponentsContext] Loaded ${instances.length} instances`);
698
+ debugLog(`[EditorComponentsContext] Loaded ${instances.length} instances`);
695
699
 
696
700
  // Create Map from instances (keyed by domElementId)
697
701
  const instancesMap = new Map<string, ComponentInstance>();
@@ -766,7 +770,7 @@ export function EditorComponentsProvider({
766
770
  });
767
771
  }
768
772
 
769
- console.log(
773
+ debugLog(
770
774
  `[EditorComponentsContext] Created master component: ${name} (${id})`
771
775
  );
772
776
  return masterComponent;
@@ -996,7 +1000,7 @@ export function EditorComponentsProvider({
996
1000
  });
997
1001
  }
998
1002
 
999
- console.log(`[EditorComponentsContext] Deleted master component: ${id}`);
1003
+ debugLog(`[EditorComponentsContext] Deleted master component: ${id}`);
1000
1004
  }, [websiteId, partsMode]);
1001
1005
 
1002
1006
  const getMasterComponent = useCallback(
@@ -1069,7 +1073,7 @@ export function EditorComponentsProvider({
1069
1073
  });
1070
1074
  }
1071
1075
 
1072
- console.log(
1076
+ debugLog(
1073
1077
  `[EditorComponentsContext] Created instance: ${id} of master ${masterComponentId}`
1074
1078
  );
1075
1079
  return instance;
@@ -1157,7 +1161,7 @@ export function EditorComponentsProvider({
1157
1161
  return next;
1158
1162
  });
1159
1163
 
1160
- console.log(`[EditorComponentsContext] Deleted instance: ${instanceId}`);
1164
+ debugLog(`[EditorComponentsContext] Deleted instance: ${instanceId}`);
1161
1165
  }, [websiteId, pageId]);
1162
1166
 
1163
1167
  const getInstanceByDomId = useCallback(
@@ -1219,7 +1223,7 @@ export function EditorComponentsProvider({
1219
1223
  return next;
1220
1224
  });
1221
1225
 
1222
- console.log(
1226
+ debugLog(
1223
1227
  `[EditorComponentsContext] Added override to instance: ${instanceId}`
1224
1228
  );
1225
1229
  },
@@ -1269,7 +1273,7 @@ export function EditorComponentsProvider({
1269
1273
  return next;
1270
1274
  });
1271
1275
 
1272
- console.log(
1276
+ debugLog(
1273
1277
  `[EditorComponentsContext] Removed override ${overrideId} from instance: ${instanceId}`
1274
1278
  );
1275
1279
  },
@@ -1321,7 +1325,7 @@ export function EditorComponentsProvider({
1321
1325
  return next;
1322
1326
  });
1323
1327
 
1324
- console.log(
1328
+ debugLog(
1325
1329
  `[EditorComponentsContext] Reset all overrides for instance: ${instanceId}`
1326
1330
  );
1327
1331
  }, [websiteId, pageId]);
@@ -1394,7 +1398,7 @@ export function EditorComponentsProvider({
1394
1398
  return next;
1395
1399
  });
1396
1400
 
1397
- console.log(
1401
+ debugLog(
1398
1402
  `[EditorComponentsContext] Changed variant for instance ${instanceId} to ${variantId}`
1399
1403
  );
1400
1404
  },
@@ -1458,7 +1462,7 @@ export function EditorComponentsProvider({
1458
1462
  });
1459
1463
  }
1460
1464
 
1461
- console.log(
1465
+ debugLog(
1462
1466
  `[EditorComponentsContext] Detached instance: ${instanceId}`
1463
1467
  );
1464
1468
  return htmlElement;
@@ -18,6 +18,7 @@
18
18
  import React, { createContext, useContext, useState, useCallback } from 'react';
19
19
  import type { DOMTreeNode, PanelSections, MarqueeState } from '../types';
20
20
  import { DEFAULT_PANEL_SECTIONS } from '../constants';
21
+ import { debugLog } from '../utils/debug';
21
22
 
22
23
  export interface EditorUIStateContextValue {
23
24
  // DOMツリー
@@ -81,7 +82,7 @@ export function EditorUIStateProvider({
81
82
  const restoreFocus = useCallback(() => {
82
83
  const iframeDoc = getIframeDoc();
83
84
  if (!iframeDoc) {
84
- console.log('[EditorUIStateContext] restoreFocus: iframe doc not available');
85
+ debugLog('[EditorUIStateContext] restoreFocus: iframe doc not available');
85
86
  return;
86
87
  }
87
88
 
@@ -89,14 +90,14 @@ export function EditorUIStateProvider({
89
90
  const editingElement = iframeDoc.querySelector('[contenteditable="true"]') as HTMLElement;
90
91
  if (editingElement) {
91
92
  editingElement.focus();
92
- console.log('[EditorUIStateContext] restoreFocus: focused editing element');
93
+ debugLog('[EditorUIStateContext] restoreFocus: focused editing element');
93
94
  return;
94
95
  }
95
96
 
96
97
  // iframeのbodyにフォーカスを設定
97
98
  if (iframeDoc.body) {
98
99
  iframeDoc.body.focus();
99
- console.log('[EditorUIStateContext] restoreFocus: focused iframe body');
100
+ debugLog('[EditorUIStateContext] restoreFocus: focused iframe body');
100
101
  }
101
102
  }, [getIframeDoc]);
102
103
 
@@ -22,6 +22,7 @@
22
22
  */
23
23
 
24
24
  import React, { createContext, useContext, useState, useCallback, useMemo, useRef } from 'react';
25
+ import { debugLog } from '../utils/debug';
25
26
  import type {
26
27
  CSSVariableDefinition,
27
28
  CSSVariableCategory,
@@ -264,7 +265,7 @@ export function EditorVariablesProvider({
264
265
  await onSave(variables);
265
266
  savedVariablesRef.current = [...variables];
266
267
  setHasChanges(false);
267
- console.log('[EditorVariablesContext] Variables saved successfully');
268
+ debugLog('[EditorVariablesContext] Variables saved successfully');
268
269
  } catch (error) {
269
270
  console.error('[EditorVariablesContext] Failed to save variables:', error);
270
271
  throw error;
@@ -286,7 +287,7 @@ export function EditorVariablesProvider({
286
287
  setVariablesState(loaded);
287
288
  savedVariablesRef.current = [...loaded];
288
289
  setHasChanges(false);
289
- console.log('[EditorVariablesContext] Variables loaded:', loaded.length);
290
+ debugLog('[EditorVariablesContext] Variables loaded:', loaded.length);
290
291
  } catch (error) {
291
292
  console.error('[EditorVariablesContext] Failed to load variables:', error);
292
293
  throw error;
@@ -299,7 +300,7 @@ export function EditorVariablesProvider({
299
300
  const syncFromTheme = useCallback((themeVariables: CSSVariableDefinition[]) => {
300
301
  setVariablesState(themeVariables);
301
302
  markAsChanged();
302
- console.log('[EditorVariablesContext] Synced from theme:', themeVariables.length, 'variables');
303
+ debugLog('[EditorVariablesContext] Synced from theme:', themeVariables.length, 'variables');
303
304
  }, [markAsChanged]);
304
305
 
305
306
  // バルク設定
@@ -327,7 +328,7 @@ export function EditorVariablesProvider({
327
328
  }
328
329
 
329
330
  styleEl.textContent = cssString;
330
- console.log('[EditorVariablesContext] Injected CSS variables to iframe');
331
+ debugLog('[EditorVariablesContext] Injected CSS variables to iframe');
331
332
  }, [cssString]);
332
333
 
333
334
  // Context値
@@ -62,6 +62,15 @@ export const RULER_SIZE = 20;
62
62
  const VIEW_ANIMATION_MS = 240;
63
63
  /** 見るだけの紙面を同時に読む枚数 */
64
64
  const PREVIEW_PARALLEL = 4;
65
+ /**
66
+ * 見るだけの紙面を画像で描く倍率のしきい値。これ未満に縮小されている間は
67
+ * srcdoc の iframe ではなく contentList[].thumbnail の画像を出す。
68
+ * 判定は操作が終わってからしか動かさない(縮小の途中で 26 枚を差し替えない)。
69
+ * 行き来でちらつかないように戻りは別のしきい値にする(ヒステリシス)
70
+ */
71
+ export const PREVIEW_IMAGE_ZOOM = 0.5;
72
+ /** 画像 → iframe に戻す倍率(PREVIEW_IMAGE_ZOOM 以上)。iframe → 画像は PREVIEW_IMAGE_ZOOM_OUT 未満 */
73
+ export const PREVIEW_IMAGE_ZOOM_OUT = 0.4;
65
74
 
66
75
  /** フレームどうしの間隔(紙面の座標系、等倍のとき)。フレーム名を出すかの判定にも使う */
67
76
  export const FRAME_GAP: Record<EditorMode, { x: number; y: number }> = {
@@ -99,6 +108,8 @@ export interface PageFrame {
99
108
  depth: number;
100
109
  /** 編集しない表示の URL(contentList[].href) */
101
110
  href?: string;
111
+ /** ページ全体を写した画像の URL(contentList[].thumbnail)。大きく縮小した間だけ紙面の代わりに出す */
112
+ thumbnail?: string;
102
113
  /** 利用側が進める本文の版(contentList[].revision)。変わったら読み直す */
103
114
  revision: number;
104
115
  /**
@@ -244,6 +255,17 @@ export function useCanvasViewState(): CanvasViewState {
244
255
  return useSyncExternalStore(viewStore.subscribe, viewStore.get, viewStore.get);
245
256
  }
246
257
 
258
+ /**
259
+ * 編集中のページだけを購読する。
260
+ * useCanvasViewState() だと倍率・位置が変わるたびに描き直る(ホイール 1 段ごとに
261
+ * キャンバス全体が React を通る)。編集中のページしか要らない側はこちらを使う
262
+ */
263
+ export function useCanvasActivePageId(): string | null {
264
+ const { viewStore } = useMultiPageCanvas();
265
+ const getSnapshot = useCallback(() => viewStore.get().activePageId, [viewStore]);
266
+ return useSyncExternalStore(viewStore.subscribe, getSnapshot, getSnapshot);
267
+ }
268
+
247
269
  const NULL_VIEW_STORE: CanvasViewStore = {
248
270
  get: () => NULL_VIEW,
249
271
  set: () => undefined,
@@ -487,6 +509,7 @@ export function MultiPageCanvasProvider({ children, enabled, storageKey }: Multi
487
509
  const depth = depths.get(content.id) ?? 0;
488
510
  const revision = content.revision ?? 0;
489
511
  const href = content.href;
512
+ const thumbnail = content.thumbnail;
490
513
  if (existing) {
491
514
  const title = content.title || existing.title;
492
515
  const width = frameWidth;
@@ -499,9 +522,9 @@ export function MultiPageCanvasProvider({ children, enabled, storageKey }: Multi
499
522
  if (
500
523
  title !== existing.title || html !== existing.html || width !== existing.size.width ||
501
524
  parentId !== existing.parentId || depth !== existing.depth || href !== existing.href ||
502
- revision !== existing.revision || stale !== existing.stale
525
+ revision !== existing.revision || stale !== existing.stale || thumbnail !== existing.thumbnail
503
526
  ) changed = true;
504
- next.set(content.id, { ...existing, title, html, stale, parentId, depth, href, revision, size: { width, height: existing.size.height } });
527
+ next.set(content.id, { ...existing, title, html, stale, parentId, depth, href, thumbnail, revision, size: { width, height: existing.size.height } });
505
528
  } else {
506
529
  changed = true;
507
530
  next.set(content.id, {
@@ -516,6 +539,7 @@ export function MultiPageCanvasProvider({ children, enabled, storageKey }: Multi
516
539
  parentId,
517
540
  depth,
518
541
  href,
542
+ thumbnail,
519
543
  revision,
520
544
  stale: false,
521
545
  });
@@ -13,6 +13,7 @@ import { createAuthApi } from '../../lib/api/auth-fetch';
13
13
  import { convertSingleElementToAbsolute, generateElementId } from '../utils/dom-utils';
14
14
  import type { SlideAgentRequest, SlideAgentResponse, AttachedFile as SlideAttachedFile } from '../../lib/agent/slide-agent/types';
15
15
  import type { WebsiteAgentRequest, WebsiteAgentResponse, AttachedFile as WebsiteAttachedFile } from '../../lib/agent/website-agent/types';
16
+ import { debugLog } from '../utils/debug';
16
17
 
17
18
  // AttachedFile型を共通化(両方同じ構造)
18
19
  export type AttachedFile = SlideAttachedFile;
@@ -176,7 +177,7 @@ export function useAiReplace({ presentationId, websiteId }: UseAiReplaceProps =
176
177
  const convertChildrenToAbsolute = async () => {
177
178
  // オートレイアウトモードの場合は変換をスキップ
178
179
  if (layoutMode === 'auto') {
179
- console.log('[useAiReplace] Auto layout mode - skipping absolute positioning conversion for children');
180
+ debugLog('[useAiReplace] Auto layout mode - skipping absolute positioning conversion for children');
180
181
  return;
181
182
  }
182
183
 
@@ -196,7 +197,7 @@ export function useAiReplace({ presentationId, websiteId }: UseAiReplaceProps =
196
197
  convertSingleElementToAbsolute(iframeDoc, htmlChild);
197
198
  });
198
199
 
199
- console.log('[useAiReplace] Converted', editableChildren.length, 'child elements to absolute positioning');
200
+ debugLog('[useAiReplace] Converted', editableChildren.length, 'child elements to absolute positioning');
200
201
 
201
202
  // 変更を再度通知
202
203
  notifyIframeChange();
@@ -263,7 +264,7 @@ export function useAiReplace({ presentationId, websiteId }: UseAiReplaceProps =
263
264
  // editorModeに基づいてAPI呼び出しを分岐
264
265
  const isWebpageMode = editorMode === 'webpage';
265
266
 
266
- console.log('[useAiReplace] API呼び出し:', {
267
+ debugLog('[useAiReplace] API呼び出し:', {
267
268
  editorMode,
268
269
  apiEndpoint: isWebpageMode ? '/api/website-agent' : '/api/slide-agent',
269
270
  promptLength: prompt.length,
@@ -1,4 +1,5 @@
1
1
  import { useEffect } from 'react';
2
+ import { debugLog } from '../utils/debug';
2
3
 
3
4
  /**
4
5
  * ブラウザのネイティブズーム(ピンチ、Ctrl+ホイール、キーボードショートカット等)を防止するフック
@@ -67,7 +68,7 @@ export function useBrowserZoomPrevention(): void {
67
68
  // e.key ではなく e.code を使用して物理キーを判定する
68
69
  // preventDefault のみでブラウザのデフォルト動作を止め、自前のハンドラーは動くようにする
69
70
  if (isCmdOrCtrl && e.altKey && (e.code === 'KeyC' || e.code === 'KeyV')) {
70
- console.log('[preventBrowserShortcuts] Preventing browser default for Cmd/Ctrl+Alt+C/V', {
71
+ debugLog('[preventBrowserShortcuts] Preventing browser default for Cmd/Ctrl+Alt+C/V', {
71
72
  key: e.key,
72
73
  code: e.code,
73
74
  });
@@ -7,6 +7,7 @@ import { buildDomTree } from '../utils/dom-utils';
7
7
  import { applyLockInside, detachPart, partInfoOf, unlockPartDescendants } from '../parts';
8
8
  import { toast } from 'sonner';
9
9
  import type { MasterComponent, ComponentInstance } from '../../types/editor-components';
10
+ import { debugLog } from '../utils/debug';
10
11
 
11
12
  interface UseComponentEditModeProps {
12
13
  contentId?: string;
@@ -201,7 +202,7 @@ export function useComponentEditMode({
201
202
 
202
203
  try {
203
204
  // Debug: Log the original element
204
- console.log('[handleConfirmCreateComponent] Original element:', {
205
+ debugLog('[handleConfirmCreateComponent] Original element:', {
205
206
  tagName: element.tagName,
206
207
  className: element.className,
207
208
  innerHTML: element.innerHTML.substring(0, 200),
@@ -212,7 +213,7 @@ export function useComponentEditMode({
212
213
 
213
214
  // Create the master component
214
215
  const component = createMasterComponent(element, newComponentName.trim(), newComponentCategory || 'content');
215
- console.log('[handleConfirmCreateComponent] Created component:', {
216
+ debugLog('[handleConfirmCreateComponent] Created component:', {
216
217
  id: component.id,
217
218
  name: component.name,
218
219
  variantsCount: component.variants.length,
@@ -248,7 +249,7 @@ export function useComponentEditMode({
248
249
  { x: positionX, y: positionY } // initialPosition
249
250
  );
250
251
  if (instance) {
251
- console.log('[handleConfirmCreateComponent] Created instance:', {
252
+ debugLog('[handleConfirmCreateComponent] Created instance:', {
252
253
  id: instance.id,
253
254
  domElementId: instance.domElementId,
254
255
  variantId: instance.variantId,
@@ -259,7 +260,7 @@ export function useComponentEditMode({
259
260
  const { renderInstance } = await import('../utils/component-renderer');
260
261
  const instanceElement = renderInstance(component, instance, iframeDoc);
261
262
 
262
- console.log('[handleConfirmCreateComponent] Rendered instance element:', {
263
+ debugLog('[handleConfirmCreateComponent] Rendered instance element:', {
263
264
  tagName: instanceElement.tagName,
264
265
  className: instanceElement.className,
265
266
  innerHTML: instanceElement.innerHTML.substring(0, 200),
@@ -281,7 +282,7 @@ export function useComponentEditMode({
281
282
  // Update history
282
283
  notifyIframeChange(true);
283
284
 
284
- console.log('[handleConfirmCreateComponent] Replaced element with instance:', instance.id);
285
+ debugLog('[handleConfirmCreateComponent] Replaced element with instance:', instance.id);
285
286
  }
286
287
 
287
288
  setCreateComponentDialogOpen(false);
@@ -349,11 +350,11 @@ export function useComponentEditMode({
349
350
  originalPageHtmlRef.current = artboard.innerHTML;
350
351
 
351
352
  // DEBUG: Log what instances are being saved
352
- console.log('[enterComponentEditMode] ========== SAVING PAGE HTML ==========');
353
+ debugLog('[enterComponentEditMode] ========== SAVING PAGE HTML ==========');
353
354
  const instancesToSave = artboard.querySelectorAll('[data-component-instance]');
354
- console.log('[enterComponentEditMode] Saving', instancesToSave.length, 'instances');
355
+ debugLog('[enterComponentEditMode] Saving', instancesToSave.length, 'instances');
355
356
  instancesToSave.forEach((inst, idx) => {
356
- console.log(`[enterComponentEditMode] Instance ${idx} text being saved:`, inst.textContent?.substring(0, 100));
357
+ debugLog(`[enterComponentEditMode] Instance ${idx} text being saved:`, inst.textContent?.substring(0, 100));
357
358
  });
358
359
  }
359
360
 
@@ -458,16 +459,16 @@ export function useComponentEditMode({
458
459
  const artboard = iframeDoc.getElementById('artboard');
459
460
 
460
461
  // DEBUG: Log what we're about to restore
461
- console.log('[exitComponentEditMode] ========== HTML RESTORE DEBUG ==========');
462
- console.log('[exitComponentEditMode] originalPageHtmlRef exists:', originalPageHtmlRef.current !== null);
462
+ debugLog('[exitComponentEditMode] ========== HTML RESTORE DEBUG ==========');
463
+ debugLog('[exitComponentEditMode] originalPageHtmlRef exists:', originalPageHtmlRef.current !== null);
463
464
  if (originalPageHtmlRef.current) {
464
465
  // Extract instance text from saved HTML for debugging
465
466
  const tempDiv = document.createElement('div');
466
467
  tempDiv.innerHTML = originalPageHtmlRef.current;
467
468
  const savedInstances = tempDiv.querySelectorAll('[data-component-instance]');
468
- console.log('[exitComponentEditMode] Saved HTML has', savedInstances.length, 'instances');
469
+ debugLog('[exitComponentEditMode] Saved HTML has', savedInstances.length, 'instances');
469
470
  savedInstances.forEach((inst, idx) => {
470
- console.log(`[exitComponentEditMode] Saved instance ${idx} text:`, inst.textContent?.substring(0, 100));
471
+ debugLog(`[exitComponentEditMode] Saved instance ${idx} text:`, inst.textContent?.substring(0, 100));
471
472
  });
472
473
  }
473
474
 
@@ -477,9 +478,9 @@ export function useComponentEditMode({
477
478
 
478
479
  // DEBUG: Log what's in the DOM after restore
479
480
  const restoredInstances = artboard.querySelectorAll('[data-component-instance]');
480
- console.log('[exitComponentEditMode] After restore, DOM has', restoredInstances.length, 'instances');
481
+ debugLog('[exitComponentEditMode] After restore, DOM has', restoredInstances.length, 'instances');
481
482
  restoredInstances.forEach((inst, idx) => {
482
- console.log(`[exitComponentEditMode] Restored instance ${idx} text:`, inst.textContent?.substring(0, 100));
483
+ debugLog(`[exitComponentEditMode] Restored instance ${idx} text:`, inst.textContent?.substring(0, 100));
483
484
  });
484
485
  }
485
486
 
@@ -489,8 +490,8 @@ export function useComponentEditMode({
489
490
  const instancesArray = Array.from(componentInstances.values());
490
491
 
491
492
  // Debug: Log the updated master and instances
492
- console.log('[exitComponentEditMode] ========== SYNC DEBUG ==========');
493
- console.log('[exitComponentEditMode] Updated master:', {
493
+ debugLog('[exitComponentEditMode] ========== SYNC DEBUG ==========');
494
+ debugLog('[exitComponentEditMode] Updated master:', {
494
495
  id: updatedMaster.id,
495
496
  name: updatedMaster.name,
496
497
  variantsCount: updatedMaster.variants.length,
@@ -499,7 +500,7 @@ export function useComponentEditMode({
499
500
  // Log master's text content
500
501
  const defaultVariant = updatedMaster.variants.find(v => v.id === updatedMaster.defaultVariantId);
501
502
  if (defaultVariant) {
502
- console.log('[exitComponentEditMode] Master rootElement:', {
503
+ debugLog('[exitComponentEditMode] Master rootElement:', {
503
504
  tagName: defaultVariant.rootElement.tagName,
504
505
  textContent: defaultVariant.rootElement.textContent?.substring(0, 100),
505
506
  innerHTML: defaultVariant.rootElement.innerHTML?.substring(0, 100),
@@ -507,7 +508,7 @@ export function useComponentEditMode({
507
508
  });
508
509
  }
509
510
 
510
- console.log('[exitComponentEditMode] Instances to sync:', instancesArray.map(i => ({
511
+ debugLog('[exitComponentEditMode] Instances to sync:', instancesArray.map(i => ({
511
512
  id: i.id,
512
513
  masterComponentId: i.masterComponentId,
513
514
  domElementId: i.domElementId,
@@ -519,9 +520,9 @@ export function useComponentEditMode({
519
520
  const artboardForSync = iframeDoc.getElementById('artboard');
520
521
  if (artboardForSync) {
521
522
  const instanceElements = artboardForSync.querySelectorAll(`[data-component-master="${updatedMaster.id}"]`);
522
- console.log('[exitComponentEditMode] Instance elements in DOM:', instanceElements.length);
523
+ debugLog('[exitComponentEditMode] Instance elements in DOM:', instanceElements.length);
523
524
  instanceElements.forEach((el, i) => {
524
- console.log(`[exitComponentEditMode] DOM instance ${i}:`, {
525
+ debugLog(`[exitComponentEditMode] DOM instance ${i}:`, {
525
526
  instanceId: el.getAttribute('data-component-instance'),
526
527
  masterId: el.getAttribute('data-component-master'),
527
528
  elementId: el.getAttribute('data-element-id'),
@@ -532,11 +533,11 @@ export function useComponentEditMode({
532
533
 
533
534
  // Propagate master changes to all instances in the DOM
534
535
  const syncResult = propagateMasterChanges(updatedMaster, instancesArray, iframeDoc);
535
- console.log('[exitComponentEditMode] Synced instances:', syncResult);
536
+ debugLog('[exitComponentEditMode] Synced instances:', syncResult);
536
537
 
537
538
  // Persist updated instances with their newly detected overrides
538
539
  if (syncResult.updatedInstances.length > 0) {
539
- console.log('[exitComponentEditMode] Persisting updated instances with overrides:',
540
+ debugLog('[exitComponentEditMode] Persisting updated instances with overrides:',
540
541
  syncResult.updatedInstances.map(i => ({
541
542
  id: i.id,
542
543
  overridesCount: i.overrides.length,
@@ -576,7 +577,7 @@ export function useComponentEditMode({
576
577
  // Go to main component - enters component edit mode to directly edit the master in canvas
577
578
  const handleGoToMainComponent = useCallback(() => {
578
579
  if (!selectedElementInstance) return;
579
- console.log('Go to main component:', selectedElementInstance.masterComponentId);
580
+ debugLog('Go to main component:', selectedElementInstance.masterComponentId);
580
581
  enterComponentEditMode(selectedElementInstance.masterComponentId);
581
582
  }, [selectedElementInstance, enterComponentEditMode]);
582
583
 
@@ -610,7 +611,7 @@ export function useComponentEditMode({
610
611
  }
611
612
  }
612
613
 
613
- console.log('Detached instance:', selectedElementInstance.id);
614
+ debugLog('Detached instance:', selectedElementInstance.id);
614
615
  closeContextMenu();
615
616
  toast.success('インスタンスを解除しました');
616
617
  } catch (error) {
@@ -650,7 +651,7 @@ export function useComponentEditMode({
650
651
  }
651
652
  }
652
653
 
653
- console.log('Reset overrides for instance:', selectedElementInstance.id);
654
+ debugLog('Reset overrides for instance:', selectedElementInstance.id);
654
655
  closeContextMenu();
655
656
  toast.success('オーバーライドをリセットしました');
656
657
  } catch (error) {
@@ -662,7 +663,7 @@ export function useComponentEditMode({
662
663
  // Push overrides to main (placeholder - requires more complex implementation)
663
664
  const handlePushOverridesToMain = useCallback(() => {
664
665
  if (!selectedElementInstance) return;
665
- console.log('Push overrides to main - not yet implemented');
666
+ debugLog('Push overrides to main - not yet implemented');
666
667
  closeContextMenu();
667
668
  toast.info('この機能は現在開発中です');
668
669
  }, [selectedElementInstance, closeContextMenu]);
@@ -9,6 +9,7 @@ import { useEditorContext } from '../EditorContext';
9
9
  import { buildDomTree, updateSelectionBox } from '../utils/dom-utils';
10
10
  import { extractElementInfo } from '../utils/style-utils';
11
11
  import type { DOMTreeNode } from '../types';
12
+ import { debugLog } from '../utils/debug';
12
13
 
13
14
  /**
14
15
  * postMessageの受信と処理を行うHook
@@ -55,7 +56,7 @@ export function useEditorMessages() {
55
56
 
56
57
  // DOMツリー更新
57
58
  if (event.data?.type === 'DOM_TREE_UPDATED') {
58
- console.log('[EditorMessages] DOM_TREE_UPDATED received:', event.data.tree?.length, 'nodes');
59
+ debugLog('[EditorMessages] DOM_TREE_UPDATED received:', event.data.tree?.length, 'nodes');
59
60
  if (event.data.tree && Array.isArray(event.data.tree)) {
60
61
  setDomTree(event.data.tree);
61
62
  // 初期状態で最初のレベルを展開
@@ -71,7 +72,7 @@ export function useEditorMessages() {
71
72
  const iframeDoc = getIframeDoc();
72
73
  if (iframeDoc) {
73
74
  const tree = buildDomTree(iframeDoc);
74
- console.log('[EditorMessages] Rebuilt DOM tree:', tree.length, 'nodes');
75
+ debugLog('[EditorMessages] Rebuilt DOM tree:', tree.length, 'nodes');
75
76
  setDomTree(tree);
76
77
  }
77
78
  }