@growgroup/visual-editor 0.3.1 → 0.4.1

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 +123 -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 +179 -77
  16. package/src/editor/components/multi-page/PageFramePreview.tsx +99 -21
  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 +194 -26
  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 +43 -11
  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
@@ -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
  }
@@ -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
  /**