@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
@@ -30,6 +30,7 @@ import {
30
30
  } from '../utils/viewport-utils';
31
31
  import type { SerializedElement, BoundingBox, StyleClipboard, ClipboardData } from '../types';
32
32
  import type { globalLastUsedStylesRef } from '../contexts/EditorRefsContext';
33
+ import { debugLog } from '../utils/debug';
33
34
 
34
35
  /**
35
36
  * 要素の種類を判定(shape / text / line)
@@ -202,7 +203,7 @@ function captureLastUsedStyles(
202
203
  lastUsedStylesRef: typeof globalLastUsedStylesRef
203
204
  ) {
204
205
  const category = getElementCategory(el);
205
- console.log('[captureLastUsedStyles] Category:', category, 'Styles:', styles);
206
+ debugLog('[captureLastUsedStyles] Category:', category, 'Styles:', styles);
206
207
 
207
208
  if (category === 'shape') {
208
209
  const shapeStyles: Partial<typeof lastUsedStylesRef.current.shape> = {};
@@ -214,7 +215,7 @@ function captureLastUsedStyles(
214
215
  if (styles.opacity) shapeStyles.opacity = styles.opacity;
215
216
  if (Object.keys(shapeStyles).length > 0) {
216
217
  lastUsedStylesRef.updateShape(shapeStyles);
217
- console.log('[captureLastUsedStyles] Updated shape styles:', shapeStyles);
218
+ debugLog('[captureLastUsedStyles] Updated shape styles:', shapeStyles);
218
219
  }
219
220
  } else if (category === 'text') {
220
221
  const textStyles: Partial<typeof lastUsedStylesRef.current.text> = {};
@@ -227,7 +228,7 @@ function captureLastUsedStyles(
227
228
  if (styles.textAlign) textStyles.textAlign = styles.textAlign;
228
229
  if (Object.keys(textStyles).length > 0) {
229
230
  lastUsedStylesRef.updateText(textStyles);
230
- console.log('[captureLastUsedStyles] Updated text styles:', textStyles);
231
+ debugLog('[captureLastUsedStyles] Updated text styles:', textStyles);
231
232
  }
232
233
  } else if (category === 'line') {
233
234
  const lineStyles: Partial<typeof lastUsedStylesRef.current.line> = {};
@@ -235,7 +236,7 @@ function captureLastUsedStyles(
235
236
  if (styles.strokeWidth) lineStyles.strokeWidth = styles.strokeWidth;
236
237
  if (Object.keys(lineStyles).length > 0) {
237
238
  lastUsedStylesRef.updateLine(lineStyles);
238
- console.log('[captureLastUsedStyles] Updated line styles:', lineStyles);
239
+ debugLog('[captureLastUsedStyles] Updated line styles:', lineStyles);
239
240
  }
240
241
  }
241
242
  }
@@ -533,11 +534,11 @@ export function useElementActions() {
533
534
 
534
535
  // 複数選択されている場合は全て更新
535
536
  if (selectedElementIds.length > 0) {
536
- console.log('[DEBUG useElementActions] Applying styles to', selectedElementIds.length, 'elements');
537
+ debugLog('[DEBUG useElementActions] Applying styles to', selectedElementIds.length, 'elements');
537
538
  selectedElementIds.forEach(id => {
538
539
  const el = getIframeElement(iframeDoc, id);
539
540
  if (el) {
540
- console.log('[DEBUG useElementActions] Applying to element:', id);
541
+ debugLog('[DEBUG useElementActions] Applying to element:', id);
541
542
  applyStylesWithViewportHandling(el, styles);
542
543
  }
543
544
  });
@@ -699,7 +700,7 @@ export function useElementActions() {
699
700
  if (newInstance) {
700
701
  // 新しいインスタンスIDで属性を更新
701
702
  clone.setAttribute('data-component-instance', newInstance.id);
702
- console.log('[duplicateElement] Created new component instance:', newInstance.id, 'from:', instanceId);
703
+ debugLog('[duplicateElement] Created new component instance:', newInstance.id, 'from:', instanceId);
703
704
  }
704
705
  } catch (error) {
705
706
  // インスタンス作成に失敗した場合、コンポーネント属性を削除(通常要素として複製)
@@ -1159,7 +1160,7 @@ export function useElementActions() {
1159
1160
  const convertChildrenToAbsolute = async () => {
1160
1161
  // オートレイアウトモードの場合は変換をスキップ
1161
1162
  if (layoutMode === 'auto') {
1162
- console.log('[groupElements] Auto layout mode - skipping absolute positioning conversion');
1163
+ debugLog('[groupElements] Auto layout mode - skipping absolute positioning conversion');
1163
1164
  return;
1164
1165
  }
1165
1166
 
@@ -1175,7 +1176,7 @@ export function useElementActions() {
1175
1176
  convertSingleElementToAbsolute(iframeDoc, el);
1176
1177
  });
1177
1178
 
1178
- console.log('[groupElements] Converted', elements.length, 'child elements to absolute positioning');
1179
+ debugLog('[groupElements] Converted', elements.length, 'child elements to absolute positioning');
1179
1180
  notifyIframeChange();
1180
1181
  } catch (err) {
1181
1182
  console.error('[groupElements] Error converting children:', err);
@@ -1206,7 +1207,7 @@ export function useElementActions() {
1206
1207
 
1207
1208
  // グループ解除可能かどうかを判定(詳細な条件をチェック)
1208
1209
  if (!canUngroup(parent)) {
1209
- console.log('[ungroupElements] Element cannot be ungrouped:', parent.tagName);
1210
+ debugLog('[ungroupElements] Element cannot be ungrouped:', parent.tagName);
1210
1211
  return;
1211
1212
  }
1212
1213
 
@@ -1226,7 +1227,7 @@ export function useElementActions() {
1226
1227
  });
1227
1228
 
1228
1229
  if (children.length === 0) {
1229
- console.log('[ungroupElements] No editable children to ungroup');
1230
+ debugLog('[ungroupElements] No editable children to ungroup');
1230
1231
  return;
1231
1232
  }
1232
1233
 
@@ -1307,7 +1308,7 @@ export function useElementActions() {
1307
1308
  const finalizePositioning = async () => {
1308
1309
  // オートレイアウトモードの場合は変換をスキップ
1309
1310
  if (layoutMode === 'auto') {
1310
- console.log('[ungroupElements] Auto layout mode - skipping absolute positioning conversion');
1311
+ debugLog('[ungroupElements] Auto layout mode - skipping absolute positioning conversion');
1311
1312
  return;
1312
1313
  }
1313
1314
 
@@ -1322,7 +1323,7 @@ export function useElementActions() {
1322
1323
  convertSingleElementToAbsolute(iframeDoc, el);
1323
1324
  });
1324
1325
 
1325
- console.log('[ungroupElements] Converted', childElements.length, 'elements to absolute positioning');
1326
+ debugLog('[ungroupElements] Converted', childElements.length, 'elements to absolute positioning');
1326
1327
  notifyIframeChange();
1327
1328
  } catch (err) {
1328
1329
  console.error('[ungroupElements] Error finalizing positions:', err);
@@ -1335,7 +1336,7 @@ export function useElementActions() {
1335
1336
  setSelectedElement(null);
1336
1337
  notifyIframeChange();
1337
1338
 
1338
- console.log('[ungroupElements] Ungrouped', childIds.length, 'elements from', selectedElement.id);
1339
+ debugLog('[ungroupElements] Ungrouped', childIds.length, 'elements from', selectedElement.id);
1339
1340
  }, [selectedElement, getIframeDoc, setSelectedElementIds, setSelectedElement, notifyIframeChange]);
1340
1341
 
1341
1342
  /**
@@ -1525,10 +1526,10 @@ export function useElementActions() {
1525
1526
  * Ctrl/Cmd + Alt + C
1526
1527
  */
1527
1528
  const copyStyle = useCallback(() => {
1528
- console.log('[copyStyle] Called, selectedElement:', selectedElement?.id);
1529
+ debugLog('[copyStyle] Called, selectedElement:', selectedElement?.id);
1529
1530
  const iframeDoc = getIframeDoc();
1530
1531
  if (!iframeDoc || !selectedElement) {
1531
- console.log('[copyStyle] No element selected or no iframeDoc');
1532
+ debugLog('[copyStyle] No element selected or no iframeDoc');
1532
1533
  return false;
1533
1534
  }
1534
1535
 
@@ -1600,8 +1601,8 @@ export function useElementActions() {
1600
1601
  ) as StyleClipboard;
1601
1602
 
1602
1603
  styleClipboardRef.current = cleanedStyle;
1603
- console.log('[copyStyle] Style copied:', cleanedStyle);
1604
- console.log('[copyStyle] Clipboard after setting:', styleClipboardRef.current);
1604
+ debugLog('[copyStyle] Style copied:', cleanedStyle);
1605
+ debugLog('[copyStyle] Clipboard after setting:', styleClipboardRef.current);
1605
1606
  return true;
1606
1607
  }, [selectedElement, getIframeDoc, styleClipboardRef]);
1607
1608
 
@@ -1610,17 +1611,17 @@ export function useElementActions() {
1610
1611
  * Ctrl/Cmd + Alt + V
1611
1612
  */
1612
1613
  const pasteStyle = useCallback(() => {
1613
- console.log('[pasteStyle] Called');
1614
- console.log('[pasteStyle] styleClipboardRef.current:', styleClipboardRef.current);
1614
+ debugLog('[pasteStyle] Called');
1615
+ debugLog('[pasteStyle] styleClipboardRef.current:', styleClipboardRef.current);
1615
1616
  const iframeDoc = getIframeDoc();
1616
1617
  if (!iframeDoc) {
1617
- console.log('[pasteStyle] No iframeDoc');
1618
+ debugLog('[pasteStyle] No iframeDoc');
1618
1619
  return false;
1619
1620
  }
1620
1621
 
1621
1622
  const copiedStyle = styleClipboardRef.current;
1622
1623
  if (!copiedStyle || Object.keys(copiedStyle).length === 0) {
1623
- console.log('[pasteStyle] No style in clipboard');
1624
+ debugLog('[pasteStyle] No style in clipboard');
1624
1625
  return false;
1625
1626
  }
1626
1627
 
@@ -1630,7 +1631,7 @@ export function useElementActions() {
1630
1631
  : selectedElement ? [selectedElement.id] : [];
1631
1632
 
1632
1633
  if (targetIds.length === 0) {
1633
- console.log('[pasteStyle] No element selected');
1634
+ debugLog('[pasteStyle] No element selected');
1634
1635
  return false;
1635
1636
  }
1636
1637
 
@@ -1707,7 +1708,7 @@ export function useElementActions() {
1707
1708
  }
1708
1709
  }
1709
1710
 
1710
- console.log('[pasteStyle] Style pasted to', targetIds.length, 'element(s)');
1711
+ debugLog('[pasteStyle] Style pasted to', targetIds.length, 'element(s)');
1711
1712
  return true;
1712
1713
  }, [selectedElement, selectedElementIds, getIframeDoc, styleClipboardRef, notifyIframeChange, setSelectedElement]);
1713
1714
 
@@ -1754,13 +1755,13 @@ export function useElementActions() {
1754
1755
  return { success: false, error: 'Selected elements not found in document' };
1755
1756
  }
1756
1757
 
1757
- console.log('[copyToFigma] Copying', elements.length, 'element(s) to Figma format');
1758
+ debugLog('[copyToFigma] Copying', elements.length, 'element(s) to Figma format');
1758
1759
 
1759
1760
  try {
1760
1761
  const result = await copyElementsToFigma(elements);
1761
1762
 
1762
1763
  if (result.success) {
1763
- console.log('[copyToFigma] Successfully copied', result.nodeCount, 'nodes to clipboard');
1764
+ debugLog('[copyToFigma] Successfully copied', result.nodeCount, 'nodes to clipboard');
1764
1765
  return { success: true, nodeCount: result.nodeCount };
1765
1766
  } else {
1766
1767
  console.error('[copyToFigma] Failed:', result.error);
@@ -24,6 +24,7 @@ import {
24
24
  import { extractElementInfo } from "../utils/style-utils";
25
25
  import { MARQUEE_DRAG_THRESHOLD } from "../constants";
26
26
  import type { DragState, ResizeState, MarqueeState } from "../types";
27
+ import { debugLog } from '../utils/debug';
27
28
 
28
29
  /**
29
30
  * mousedown で保留し、mouseup で意味を確定するクリック情報
@@ -453,7 +454,7 @@ export function useElementSelection(
453
454
 
454
455
  // 真のインライン要素(テキストフロー内)はドラッグしない
455
456
  if (isTrueInlineInTextFlow(element, iframeDoc)) {
456
- console.log(
457
+ debugLog(
457
458
  "[startElementDrag] Skipping true inline element:",
458
459
  element.tagName,
459
460
  element.getAttribute("data-element-id"),
@@ -918,14 +919,15 @@ export function useElementSelection(
918
919
  if (editingElement.classList.contains("selected")) {
919
920
  updateSelectionBox(iframeDoc, editingElement);
920
921
  }
921
- console.log("[Canvas] Text editing exited via click elsewhere");
922
+ debugLog("[Canvas] Text editing exited via click elsewhere");
922
923
  }
923
924
 
924
925
  // 描画モード・テキストモードでは選択しない
925
926
  const bodyClasses = iframeDoc.body.classList;
926
927
  if (
927
928
  bodyClasses.contains("draw-mode") ||
928
- bodyClasses.contains("text-mode")
929
+ bodyClasses.contains("text-mode") ||
930
+ bodyClasses.contains("comment-mode")
929
931
  )
930
932
  return;
931
933
 
@@ -1030,7 +1032,7 @@ export function useElementSelection(
1030
1032
  centerY,
1031
1033
  };
1032
1034
  iframeDoc.body.classList.add("rotating");
1033
- console.log("[Canvas] Rotation started:", handleType);
1035
+ debugLog("[Canvas] Rotation started:", handleType);
1034
1036
  } else if (handleType === "radius") {
1035
1037
  // 角丸ハンドル
1036
1038
  const borderRadius =
@@ -1057,7 +1059,7 @@ export function useElementSelection(
1057
1059
  centerX: 0,
1058
1060
  centerY: 0,
1059
1061
  };
1060
- console.log("[Canvas] Border radius resize started");
1062
+ debugLog("[Canvas] Border radius resize started");
1061
1063
  } else {
1062
1064
  // 通常のリサイズハンドル
1063
1065
  resizeStateRef.current = {
@@ -1082,7 +1084,7 @@ export function useElementSelection(
1082
1084
  centerX: 0,
1083
1085
  centerY: 0,
1084
1086
  };
1085
- console.log("[Canvas] Resize started:", handleType);
1087
+ debugLog("[Canvas] Resize started:", handleType);
1086
1088
  }
1087
1089
  }
1088
1090
  return;
@@ -1129,7 +1131,7 @@ export function useElementSelection(
1129
1131
  e,
1130
1132
  iframeDoc,
1131
1133
  );
1132
- console.log(
1134
+ debugLog(
1133
1135
  "[Canvas] Multi-element drag via selection-box:",
1134
1136
  existingIds,
1135
1137
  started ? "started" : "blocked",
@@ -1138,7 +1140,7 @@ export function useElementSelection(
1138
1140
  } else {
1139
1141
  // 単一選択時:対象要素をドラッグ
1140
1142
  startElementDrag(targetEl, e, iframeDoc);
1141
- console.log(
1143
+ debugLog(
1142
1144
  "[Canvas] Drag via selection-box for:",
1143
1145
  forElementId,
1144
1146
  );
@@ -1212,7 +1214,7 @@ export function useElementSelection(
1212
1214
  if (marqueeGeomRef) marqueeGeomRef.current = { ...geom };
1213
1215
  setMarqueeStateRef.current(geom);
1214
1216
  marqueeStartPendingRef.current = true;
1215
- console.log(
1217
+ debugLog(
1216
1218
  "[Canvas] Marquee pending (blank) at",
1217
1219
  e.clientX,
1218
1220
  e.clientY,
@@ -1251,7 +1253,7 @@ export function useElementSelection(
1251
1253
  }
1252
1254
 
1253
1255
  if (target.getAttribute("contenteditable") === "true") {
1254
- console.log("[Canvas] Skip - contenteditable active");
1256
+ debugLog("[Canvas] Skip - contenteditable active");
1255
1257
  return;
1256
1258
  }
1257
1259
 
@@ -1287,7 +1289,7 @@ export function useElementSelection(
1287
1289
  .filter((el) => el !== null);
1288
1290
  if (selectedElements.length > 0) {
1289
1291
  const started = startGroupDrag(selectedElements, e, iframeDoc);
1290
- console.log(
1292
+ debugLog(
1291
1293
  "[Canvas] Group drag:",
1292
1294
  existingIds.length,
1293
1295
  started ? "started" : "blocked",
@@ -1295,21 +1297,21 @@ export function useElementSelection(
1295
1297
  }
1296
1298
  } else {
1297
1299
  startElementDrag(target, e, iframeDoc);
1298
- console.log("[Canvas] Single drag start:", elementId);
1300
+ debugLog("[Canvas] Single drag start:", elementId);
1299
1301
  }
1300
1302
  return;
1301
1303
  }
1302
1304
 
1303
1305
  if (e.shiftKey) {
1304
1306
  // 未選択 + Shift → 追加するかは mouseup で確定(ここでは動かさない)
1305
- console.log("[Canvas] Shift add pending:", elementId);
1307
+ debugLog("[Canvas] Shift add pending:", elementId);
1306
1308
  return;
1307
1309
  }
1308
1310
 
1309
1311
  // 未選択 + 素クリック → その場で単独選択してドラッグ開始(モード非依存)
1310
1312
  selectSingle(iframeDoc, target);
1311
1313
  startElementDrag(target, e, iframeDoc);
1312
- console.log("[Canvas] Select + drag start:", elementId);
1314
+ debugLog("[Canvas] Select + drag start:", elementId);
1313
1315
  };
1314
1316
 
1315
1317
  /**
@@ -1421,7 +1423,8 @@ export function useElementSelection(
1421
1423
  if (bodyClasses.contains("gg-cropping")) return;
1422
1424
  if (
1423
1425
  bodyClasses.contains("draw-mode") ||
1424
- bodyClasses.contains("text-mode")
1426
+ bodyClasses.contains("text-mode") ||
1427
+ bodyClasses.contains("comment-mode")
1425
1428
  )
1426
1429
  return;
1427
1430
 
@@ -1452,7 +1455,7 @@ export function useElementSelection(
1452
1455
  deepElement.classList.add("selected");
1453
1456
  setSelectedElementIdsRef.current([elementId]);
1454
1457
  enableTextEditing(deepElement);
1455
- console.log(
1458
+ debugLog(
1456
1459
  "[Canvas] Text editing started via double-click:",
1457
1460
  elementId,
1458
1461
  );
@@ -7,6 +7,7 @@
7
7
 
8
8
  import { useCallback, useRef, useEffect } from 'react';
9
9
  import { useEditorContext } from '../EditorContext';
10
+ import { debugLog } from '../utils/debug';
10
11
 
11
12
  interface UseFocusManagementReturn {
12
13
  /**
@@ -49,7 +50,7 @@ export function useFocusManagement(): UseFocusManagementReturn {
49
50
  const iframe = iframeRef.current;
50
51
  const iframeDoc = getIframeDoc();
51
52
  if (!iframeDoc) {
52
- console.log('[FocusManagement] Cannot restore focus: iframe doc not available');
53
+ debugLog('[FocusManagement] Cannot restore focus: iframe doc not available');
53
54
  return;
54
55
  }
55
56
 
@@ -59,7 +60,7 @@ export function useFocusManagement(): UseFocusManagementReturn {
59
60
  // まずiframe要素自体にフォーカス(親ドキュメントの観点から)
60
61
  if (iframe) iframe.focus();
61
62
  editingElement.focus();
62
- console.log('[FocusManagement] Restored focus to editing element');
63
+ debugLog('[FocusManagement] Restored focus to editing element');
63
64
  return;
64
65
  }
65
66
 
@@ -70,7 +71,7 @@ export function useFocusManagement(): UseFocusManagementReturn {
70
71
  }
71
72
  if (iframeDoc.body) {
72
73
  iframeDoc.body.focus();
73
- console.log('[FocusManagement] Restored focus to iframe and body');
74
+ debugLog('[FocusManagement] Restored focus to iframe and body');
74
75
  }
75
76
  }, [iframeRef, getIframeDoc]);
76
77
 
@@ -139,7 +140,7 @@ export function useFocusManagement(): UseFocusManagementReturn {
139
140
  relatedTarget.closest('[data-dropdown-menu]');
140
141
 
141
142
  if (isDialogOrPopover) {
142
- console.log('[FocusManagement] Focus moved to dialog/popover, allowing');
143
+ debugLog('[FocusManagement] Focus moved to dialog/popover, allowing');
143
144
  return;
144
145
  }
145
146
 
@@ -151,7 +152,7 @@ export function useFocusManagement(): UseFocusManagementReturn {
151
152
  relatedTarget.getAttribute('contenteditable') === 'true';
152
153
 
153
154
  if (isInputField) {
154
- console.log('[FocusManagement] Focus moved to input field, allowing');
155
+ debugLog('[FocusManagement] Focus moved to input field, allowing');
155
156
  return;
156
157
  }
157
158
 
@@ -170,7 +171,7 @@ export function useFocusManagement(): UseFocusManagementReturn {
170
171
  focusRecoveryTimerRef.current = setTimeout(() => {
171
172
  // まだフォーカスがエディタ外にあれば復元
172
173
  if (!isEditorFocused()) {
173
- console.log('[FocusManagement] Focus lost, attempting recovery');
174
+ debugLog('[FocusManagement] Focus lost, attempting recovery');
174
175
  restoreFocus();
175
176
  }
176
177
  }, 30); // 30msに短縮してフォーカス復元を高速化
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useState, useEffect, useCallback, useMemo } from 'react';
4
+ import { debugLog } from '../utils/debug';
4
5
 
5
6
  /**
6
7
  * Google Fontの情報
@@ -183,7 +184,7 @@ export function useGoogleFonts() {
183
184
 
184
185
  // APIエラーまたは空の場合はフォールバック
185
186
  if (data.error || !data.items || data.items.length === 0) {
186
- console.log('[useGoogleFonts] API returned empty, using popular fonts');
187
+ debugLog('[useGoogleFonts] API returned empty, using popular fonts');
187
188
  setFonts([...SYSTEM_FONTS, ...POPULAR_GOOGLE_FONTS]);
188
189
  return;
189
190
  }
@@ -254,7 +255,7 @@ export function useGoogleFonts() {
254
255
  // ロード済みセットに追加
255
256
  setLoadedFonts(prev => new Set(prev).add(fontKey));
256
257
 
257
- console.log(`[useGoogleFonts] Loaded font: ${fontFamily}`);
258
+ debugLog(`[useGoogleFonts] Loaded font: ${fontFamily}`);
258
259
  }, [loadedFonts]);
259
260
 
260
261
  /**
@@ -17,6 +17,7 @@ import {
17
17
  } from '../utils/dom-utils';
18
18
  import { generateEditableHtml } from '../utils/html-utils';
19
19
  import { extractElementInfo } from '../utils/style-utils';
20
+ import { debugLog } from '../utils/debug';
20
21
 
21
22
  /**
22
23
  * iframeの初期化とイベントハンドラ設定を行うHook
@@ -357,7 +358,7 @@ export function useIframeInitializer() {
357
358
 
358
359
  element.addEventListener('mousedown', (e) => {
359
360
  const bodyClasses = iframeDoc.body.classList;
360
- if (bodyClasses.contains('draw-mode') || bodyClasses.contains('text-mode')) return;
361
+ if (bodyClasses.contains('draw-mode') || bodyClasses.contains('text-mode') || bodyClasses.contains('comment-mode')) return;
361
362
  if (element.getAttribute('contenteditable') === 'true') return;
362
363
 
363
364
  e.preventDefault();
@@ -506,7 +507,7 @@ export function useIframeInitializer() {
506
507
  // 別の幅で崩れ、デザインツールへの取り込みで意味を失う。
507
508
  if (editorMode !== 'webpage') {
508
509
  const count = convertToAbsolutePositioning(iframeDoc);
509
- console.log('[Editor] 絶対配置へ変換:', count, '要素');
510
+ debugLog('[Editor] 絶対配置へ変換:', count, '要素');
510
511
  }
511
512
 
512
513
  // 変更を通知
@@ -567,7 +568,7 @@ export function useIframeInitializer() {
567
568
  const iframeDoc = getIframeDoc();
568
569
  if (iframeDoc?.body) {
569
570
  iframeDoc.body.classList.remove(
570
- 'move-mode', 'draw-mode', 'text-mode',
571
+ 'move-mode', 'draw-mode', 'text-mode', 'comment-mode',
571
572
  'tool-rectangle', 'tool-ellipse', 'tool-line', 'tool-arrow',
572
573
  'tool-pen', 'tool-pencil', 'tool-text', 'tool-frame'
573
574
  );
@@ -578,6 +579,8 @@ export function useIframeInitializer() {
578
579
  iframeDoc.body.classList.add('draw-mode', `tool-${activeTool}`);
579
580
  } else if (activeTool === 'text') {
580
581
  iframeDoc.body.classList.add('text-mode', 'tool-text');
582
+ } else if (activeTool === 'comment') {
583
+ iframeDoc.body.classList.add('comment-mode');
581
584
  }
582
585
  }
583
586
  }, [activeTool, getIframeDoc]);
@@ -11,6 +11,7 @@ import { useEditorContext } from '../EditorContext';
11
11
  import { buildDomTree, isInlineElement } from '../utils/dom-utils';
12
12
  import { EDITOR_IFRAME_STYLES } from '../constants';
13
13
  import { isLockedInsidePart } from '../parts';
14
+ import { debugLog } from '../utils/debug';
14
15
 
15
16
  interface UseIframeSetupReturn {
16
17
  /**
@@ -145,7 +146,7 @@ export function useIframeSetup(): UseIframeSetupReturn {
145
146
  */
146
147
  const initializeIframeDocument = useCallback(
147
148
  (iframeDoc: Document) => {
148
- console.log('[IframeSetup] Initializing iframe document...');
149
+ debugLog('[IframeSetup] Initializing iframe document...');
149
150
 
150
151
  // スタイルを注入
151
152
  const style = iframeDoc.createElement('style');
@@ -158,7 +159,7 @@ export function useIframeSetup(): UseIframeSetupReturn {
158
159
 
159
160
  // 全要素にdata-editableとdata-element-idを付与
160
161
  const elementCount = initializeEditableElements(iframeDoc);
161
- console.log('[IframeSetup] Initialized', elementCount, 'editable elements');
162
+ debugLog('[IframeSetup] Initialized', elementCount, 'editable elements');
162
163
 
163
164
  // DOMツリーを構築
164
165
  const newTree = buildDomTree(iframeDoc);
@@ -278,7 +279,7 @@ export function useIframeSetup(): UseIframeSetupReturn {
278
279
  const handleMouseMove = (e: MouseEvent) => {
279
280
  // 描画モードやテキストモードでは無効
280
281
  const bodyClasses = iframeDoc.body.classList;
281
- if (bodyClasses.contains('draw-mode') || bodyClasses.contains('text-mode') || bodyClasses.contains('move-mode')) {
282
+ if (bodyClasses.contains('draw-mode') || bodyClasses.contains('text-mode') || bodyClasses.contains('move-mode') || bodyClasses.contains('comment-mode')) {
282
283
  if (currentHoveredTextElement) {
283
284
  currentHoveredTextElement.classList.remove('text-editable-hover');
284
285
  currentHoveredTextElement = null;
@@ -17,6 +17,7 @@ import {
17
17
  } from '../../lib/firebase/storage';
18
18
  import { buildDomTree } from '../utils/dom-utils';
19
19
  import { findInsertionParent, findSlideRoot } from '../utils/slide-root';
20
+ import { debugLog } from '../utils/debug';
20
21
 
21
22
  interface UploadResult {
22
23
  storageUrl: string;
@@ -177,15 +178,15 @@ export function useImageUpload(options: UseImageUploadOptions = {}) {
177
178
  clipboardData: DataTransfer,
178
179
  insertOptions?: { x?: number; y?: number }
179
180
  ): Promise<UploadResult | null> => {
180
- console.log('[useImageUpload] uploadFromClipboard called');
181
+ debugLog('[useImageUpload] uploadFromClipboard called');
181
182
  // クリップボードから画像を探す
182
183
  const items = Array.from(clipboardData.items);
183
- console.log('[useImageUpload] Clipboard items:', items.map(i => ({ type: i.type, kind: i.kind })));
184
+ debugLog('[useImageUpload] Clipboard items:', items.map(i => ({ type: i.type, kind: i.kind })));
184
185
 
185
186
  for (const item of items) {
186
187
  if (item.type.startsWith('image/')) {
187
188
  const file = item.getAsFile();
188
- console.log('[useImageUpload] Got file from clipboard item:', file);
189
+ debugLog('[useImageUpload] Got file from clipboard item:', file);
189
190
  if (file) {
190
191
  return uploadFromFile(file, insertOptions);
191
192
  }
@@ -18,6 +18,7 @@ import {
18
18
  import { extractElementInfo } from '../utils/style-utils';
19
19
  import { MARQUEE_DRAG_THRESHOLD } from '../constants';
20
20
  import type { MarqueeState } from '../types';
21
+ import { debugLog } from '../utils/debug';
21
22
 
22
23
  interface UseMarqueeSelectionOptions {
23
24
  /** マーキー選択の開始保留フラグ */
@@ -305,7 +306,7 @@ export function useMarqueeSelection(
305
306
  window.postMessage({ type: 'ELEMENT_DESELECTED' }, '*');
306
307
  }
307
308
 
308
- console.log(
309
+ debugLog(
309
310
  '[Canvas] Marquee selected',
310
311
  selectedIds.length,
311
312
  additive ? '(additive)' : '',