@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.
- package/README.md +6 -1
- package/dist/editor.css +1 -1
- package/package.json +1 -1
- package/src/editor/EditorContext.tsx +3 -2
- package/src/editor/EditorToolbar.tsx +8 -2
- package/src/editor/FrontendVisualEditor.tsx +123 -44
- package/src/editor/components/EditorCanvas.tsx +73 -15
- package/src/editor/components/EditorFooter.tsx +2 -0
- package/src/editor/components/EditorHeader.tsx +3 -2
- package/src/editor/components/FigmaColorPicker.tsx +2 -1
- package/src/editor/components/JsEditorDialog.tsx +1 -0
- package/src/editor/components/PageSettingsDialog.tsx +1 -0
- package/src/editor/components/comments/CommentBoard.tsx +9 -6
- package/src/editor/components/multi-page/CanvasFrameDecor.tsx +172 -0
- package/src/editor/components/multi-page/MultiPageCanvasView.tsx +179 -77
- package/src/editor/components/multi-page/PageFramePreview.tsx +99 -21
- package/src/editor/components/multi-page/index.ts +1 -0
- package/src/editor/components/ppt/PptChrome.tsx +6 -2
- package/src/editor/components/ppt/PptComments.tsx +411 -71
- package/src/editor/constants.ts +16 -1
- package/src/editor/contexts/EditorArtboardContext.tsx +8 -0
- package/src/editor/contexts/EditorComponentsContext.tsx +31 -27
- package/src/editor/contexts/EditorUIStateContext.tsx +4 -3
- package/src/editor/contexts/EditorVariablesContext.tsx +5 -4
- package/src/editor/contexts/MultiPageCanvasContext.tsx +194 -26
- package/src/editor/hooks/useAiReplace.ts +4 -3
- package/src/editor/hooks/useBrowserZoomPrevention.ts +2 -1
- package/src/editor/hooks/useComponentEditMode.ts +27 -26
- package/src/editor/hooks/useEditorMessages.ts +3 -2
- package/src/editor/hooks/useElementActions.ts +27 -26
- package/src/editor/hooks/useElementSelection.ts +19 -16
- package/src/editor/hooks/useFocusManagement.ts +7 -6
- package/src/editor/hooks/useGoogleFonts.ts +3 -2
- package/src/editor/hooks/useIframeInitializer.ts +6 -3
- package/src/editor/hooks/useIframeSetup.ts +4 -3
- package/src/editor/hooks/useImageUpload.ts +4 -3
- package/src/editor/hooks/useMarqueeSelection.ts +2 -1
- package/src/editor/hooks/usePageSettingsManager.ts +24 -23
- package/src/editor/hooks/useRichPaste.ts +11 -10
- package/src/editor/parts.ts +7 -25
- package/src/editor/utils/component-renderer.ts +8 -7
- package/src/editor/utils/component-sync.ts +16 -15
- package/src/editor/utils/debug.ts +52 -0
- package/src/editor/utils/dom-utils.ts +43 -11
- package/src/editor/utils/drop-target.ts +135 -0
- package/src/editor/utils/eyedropper.ts +3 -1
- package/src/editor/utils/figma-export.ts +6 -5
- package/src/editor/utils/figma-kiwi-decoder.ts +28 -27
- package/src/editor/utils/figma-kiwi-encoder.ts +9 -8
- package/src/editor/utils/figma-paste.ts +57 -56
- package/src/editor/utils/flex-utils.ts +12 -10
- package/src/editor/utils/override-apply.ts +3 -2
- package/src/editor/utils/override-detection.ts +11 -10
- package/src/editor/utils/tailwind-utils.ts +4 -3
- package/src/io.ts +22 -1
- package/src/lib/firebase/css-variables.ts +4 -3
- package/src/lib/firebase/storage.ts +3 -2
- package/src/styles/skin.css +9 -4
- package/src/types/editor.ts +6 -2
- package/src/version.ts +1 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
181
|
+
debugLog('[useImageUpload] uploadFromClipboard called');
|
|
181
182
|
// クリップボードから画像を探す
|
|
182
183
|
const items = Array.from(clipboardData.items);
|
|
183
|
-
|
|
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
|
-
|
|
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
|
-
|
|
309
|
+
debugLog(
|
|
309
310
|
'[Canvas] Marquee selected',
|
|
310
311
|
selectedIds.length,
|
|
311
312
|
additive ? '(additive)' : '',
|
|
@@ -6,6 +6,7 @@ import type { PageSettings, ProjectPageSettings } from '../types/page-settings';
|
|
|
6
6
|
import type { HtmlImportResult } from '../components';
|
|
7
7
|
import { buildDomTree, getArtboardContent } from '../utils/dom-utils';
|
|
8
8
|
import { makeChildrenEditable } from '../utils/html-utils';
|
|
9
|
+
import { debugLog } from '../utils/debug';
|
|
9
10
|
|
|
10
11
|
interface UsePageSettingsManagerProps {
|
|
11
12
|
parentId?: string;
|
|
@@ -116,12 +117,12 @@ export function usePageSettingsManager({
|
|
|
116
117
|
|
|
117
118
|
// CSSがStorageにある場合
|
|
118
119
|
if (pageData.settings.customCssUrl) {
|
|
119
|
-
|
|
120
|
+
debugLog('[loadPageSettings] Fetching CSS from Storage:', pageData.settings.customCssUrl);
|
|
120
121
|
try {
|
|
121
122
|
const response = await fetch(pageData.settings.customCssUrl);
|
|
122
123
|
if (response.ok) {
|
|
123
124
|
customCss = await response.text();
|
|
124
|
-
|
|
125
|
+
debugLog(`[loadPageSettings] CSS fetched: ${customCss.length} chars`);
|
|
125
126
|
}
|
|
126
127
|
} catch (e) {
|
|
127
128
|
console.error('[loadPageSettings] Failed to fetch CSS from Storage:', e);
|
|
@@ -130,12 +131,12 @@ export function usePageSettingsManager({
|
|
|
130
131
|
|
|
131
132
|
// JSがStorageにある場合
|
|
132
133
|
if (pageData.settings.customJsUrl) {
|
|
133
|
-
|
|
134
|
+
debugLog('[loadPageSettings] Fetching JS from Storage:', pageData.settings.customJsUrl);
|
|
134
135
|
try {
|
|
135
136
|
const response = await fetch(pageData.settings.customJsUrl);
|
|
136
137
|
if (response.ok) {
|
|
137
138
|
customJs = await response.text();
|
|
138
|
-
|
|
139
|
+
debugLog(`[loadPageSettings] JS fetched: ${customJs.length} chars`);
|
|
139
140
|
}
|
|
140
141
|
} catch (e) {
|
|
141
142
|
console.error('[loadPageSettings] Failed to fetch JS from Storage:', e);
|
|
@@ -177,7 +178,7 @@ export function usePageSettingsManager({
|
|
|
177
178
|
setImportedJs(customJs);
|
|
178
179
|
}
|
|
179
180
|
|
|
180
|
-
|
|
181
|
+
debugLog('[loadPageSettings] Loaded settings:', {
|
|
181
182
|
...loadedSettings,
|
|
182
183
|
customCss: customCss.length + ' chars',
|
|
183
184
|
customJs: customJs.length + ' chars',
|
|
@@ -219,7 +220,7 @@ export function usePageSettingsManager({
|
|
|
219
220
|
}
|
|
220
221
|
if (styleElement.textContent !== importedCss) {
|
|
221
222
|
styleElement.textContent = importedCss;
|
|
222
|
-
|
|
223
|
+
debugLog('[applyStylesToIframe] Applied CSS:', importedCss.length, 'chars');
|
|
223
224
|
}
|
|
224
225
|
}
|
|
225
226
|
|
|
@@ -231,7 +232,7 @@ export function usePageSettingsManager({
|
|
|
231
232
|
const safeJs = `;try{${importedJs}}catch(e){console.error('[imported-scripts] Error:',e);}`;
|
|
232
233
|
// 既にスクリプトが存在し、同じ内容であればスキップ
|
|
233
234
|
if (existingScript?.textContent === safeJs) {
|
|
234
|
-
|
|
235
|
+
debugLog('[applyStylesToIframe] JS already applied, skipping');
|
|
235
236
|
return;
|
|
236
237
|
}
|
|
237
238
|
// 既存のスクリプトを削除
|
|
@@ -243,7 +244,7 @@ export function usePageSettingsManager({
|
|
|
243
244
|
scriptElement.id = 'imported-scripts';
|
|
244
245
|
scriptElement.textContent = safeJs;
|
|
245
246
|
iframeDoc.body.appendChild(scriptElement);
|
|
246
|
-
|
|
247
|
+
debugLog('[applyStylesToIframe] Applied JS:', importedJs.length, 'chars');
|
|
247
248
|
}
|
|
248
249
|
};
|
|
249
250
|
|
|
@@ -275,11 +276,11 @@ export function usePageSettingsManager({
|
|
|
275
276
|
iframeDoc.head.appendChild(styleElement);
|
|
276
277
|
}
|
|
277
278
|
styleElement.textContent = css;
|
|
278
|
-
|
|
279
|
+
debugLog('[handleSaveCss] Updated CSS:', css.length, 'chars');
|
|
279
280
|
} else if (styleElement) {
|
|
280
281
|
// CSSが空の場合はスタイル要素を削除
|
|
281
282
|
styleElement.remove();
|
|
282
|
-
|
|
283
|
+
debugLog('[handleSaveCss] Removed empty CSS');
|
|
283
284
|
}
|
|
284
285
|
|
|
285
286
|
// 状態を更新
|
|
@@ -298,7 +299,7 @@ export function usePageSettingsManager({
|
|
|
298
299
|
const styleElement = iframeDoc.getElementById('imported-styles');
|
|
299
300
|
if (styleElement) {
|
|
300
301
|
styleElement.remove();
|
|
301
|
-
|
|
302
|
+
debugLog('[handleClearCss] Cleared imported CSS');
|
|
302
303
|
}
|
|
303
304
|
}
|
|
304
305
|
setImportedCss('');
|
|
@@ -338,11 +339,11 @@ export function usePageSettingsManager({
|
|
|
338
339
|
const safeJs = `;try{${js}}catch(e){console.error('[imported-scripts] Error:',e);}`;
|
|
339
340
|
newScriptElement.textContent = safeJs;
|
|
340
341
|
iframeDoc.body.appendChild(newScriptElement);
|
|
341
|
-
|
|
342
|
+
debugLog('[handleSaveJs] Updated JS:', js.length, 'chars');
|
|
342
343
|
} else if (scriptElement) {
|
|
343
344
|
// JSが空の場合はスクリプト要素を削除
|
|
344
345
|
scriptElement.remove();
|
|
345
|
-
|
|
346
|
+
debugLog('[handleSaveJs] Removed empty JS');
|
|
346
347
|
}
|
|
347
348
|
|
|
348
349
|
// 状態を更新
|
|
@@ -361,7 +362,7 @@ export function usePageSettingsManager({
|
|
|
361
362
|
const scriptElement = iframeDoc.getElementById('imported-scripts');
|
|
362
363
|
if (scriptElement) {
|
|
363
364
|
scriptElement.remove();
|
|
364
|
-
|
|
365
|
+
debugLog('[handleClearJs] Cleared imported JS');
|
|
365
366
|
}
|
|
366
367
|
}
|
|
367
368
|
setImportedJs('');
|
|
@@ -461,13 +462,13 @@ export function usePageSettingsManager({
|
|
|
461
462
|
if (settings.customJs) firestoreSettings.customJs = settings.customJs;
|
|
462
463
|
|
|
463
464
|
await api.patch(apiPath, { settings: firestoreSettings });
|
|
464
|
-
|
|
465
|
+
debugLog('[handleSavePageSettings] Saved settings to Firestore:', firestoreSettings);
|
|
465
466
|
} catch (error) {
|
|
466
467
|
console.error('[handleSavePageSettings] Failed to save settings to Firestore:', error);
|
|
467
468
|
}
|
|
468
469
|
}
|
|
469
470
|
|
|
470
|
-
|
|
471
|
+
debugLog('[handleSavePageSettings] Saved settings:', settings);
|
|
471
472
|
}, [getIframeDoc, notifyIframeChange, parentId, contentId, editorMode, getIdToken]);
|
|
472
473
|
|
|
473
474
|
// 現在のページ設定を保存(メイン保存ボタンから呼ばれる)
|
|
@@ -492,7 +493,7 @@ export function usePageSettingsManager({
|
|
|
492
493
|
|
|
493
494
|
// CSS/JSが空の場合は保存不要
|
|
494
495
|
if (!currentCss && !currentJs && !pageSettings.title && !pageSettings.description) {
|
|
495
|
-
|
|
496
|
+
debugLog('[handleSaveCurrentSettings] No settings to save');
|
|
496
497
|
return;
|
|
497
498
|
}
|
|
498
499
|
|
|
@@ -525,7 +526,7 @@ export function usePageSettingsManager({
|
|
|
525
526
|
if (currentJs) firestoreSettings.customJs = currentJs;
|
|
526
527
|
|
|
527
528
|
await api.patch(apiPath, { settings: firestoreSettings });
|
|
528
|
-
|
|
529
|
+
debugLog('[handleSaveCurrentSettings] Saved settings to Firestore:', {
|
|
529
530
|
customCss: currentCss.length + ' chars',
|
|
530
531
|
customJs: currentJs.length + ' chars',
|
|
531
532
|
});
|
|
@@ -581,7 +582,7 @@ export function usePageSettingsManager({
|
|
|
581
582
|
document.body.removeChild(a);
|
|
582
583
|
URL.revokeObjectURL(url);
|
|
583
584
|
|
|
584
|
-
|
|
585
|
+
debugLog('[handleExport] Downloaded:', filename, 'format:', format);
|
|
585
586
|
} catch (error) {
|
|
586
587
|
console.error('[handleExport] Export failed:', error);
|
|
587
588
|
}
|
|
@@ -643,7 +644,7 @@ export function usePageSettingsManager({
|
|
|
643
644
|
styleElement.id = 'imported-styles';
|
|
644
645
|
styleElement.textContent = result.css;
|
|
645
646
|
iframeDoc.head.appendChild(styleElement);
|
|
646
|
-
|
|
647
|
+
debugLog('[handleHtmlImport] Injected CSS:', result.css.length, 'chars');
|
|
647
648
|
|
|
648
649
|
// CSS状態を保存(編集用)
|
|
649
650
|
setImportedCss(result.css);
|
|
@@ -667,7 +668,7 @@ export function usePageSettingsManager({
|
|
|
667
668
|
const safeJs = `;try{${result.js}}catch(e){console.error('[imported-scripts] Error:',e);}`;
|
|
668
669
|
scriptElement.textContent = safeJs;
|
|
669
670
|
iframeDoc.body.appendChild(scriptElement);
|
|
670
|
-
|
|
671
|
+
debugLog('[handleHtmlImport] Injected JS:', result.js.length, 'chars');
|
|
671
672
|
|
|
672
673
|
// JS状態を保存(編集用)- 元のJSを保存
|
|
673
674
|
setImportedJs(result.js);
|
|
@@ -677,11 +678,11 @@ export function usePageSettingsManager({
|
|
|
677
678
|
|
|
678
679
|
// インポートされた要素に data-editable と data-element-id を付与
|
|
679
680
|
const count = makeChildrenEditable(artboard);
|
|
680
|
-
|
|
681
|
+
debugLog('[handleHtmlImport] Made', count, 'elements editable');
|
|
681
682
|
|
|
682
683
|
// リソース情報をログ
|
|
683
684
|
if (result.resources) {
|
|
684
|
-
|
|
685
|
+
debugLog('[handleHtmlImport] Resources:', {
|
|
685
686
|
images: result.resources.images?.length || 0,
|
|
686
687
|
stylesheets: result.resources.stylesheets?.length || 0,
|
|
687
688
|
scripts: result.resources.scripts?.length || 0,
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
PasteProcessResult,
|
|
19
19
|
} from '../utils/paste-processors';
|
|
20
20
|
import { fetchAndApplyFigmaImages } from '../utils/figma-paste';
|
|
21
|
+
import { debugLog } from '../utils/debug';
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Result of a rich paste operation
|
|
@@ -95,7 +96,7 @@ export function useRichPaste() {
|
|
|
95
96
|
|
|
96
97
|
// Detect content type (or use forced type)
|
|
97
98
|
const contentType = forceContentType || detectContentType(clipboardData);
|
|
98
|
-
|
|
99
|
+
debugLog('[useRichPaste] Detected content type:', contentType, forceContentType ? '(forced)' : '');
|
|
99
100
|
|
|
100
101
|
// Skip images (handled by useImageUpload)
|
|
101
102
|
if (contentType === 'image') {
|
|
@@ -176,7 +177,7 @@ export function useRichPaste() {
|
|
|
176
177
|
}
|
|
177
178
|
}
|
|
178
179
|
|
|
179
|
-
|
|
180
|
+
debugLog('[useRichPaste] Successfully pasted', pastedIds.length, 'elements');
|
|
180
181
|
|
|
181
182
|
// For Figma content, fetch and apply images asynchronously
|
|
182
183
|
let figmaImagesApplied: number | undefined;
|
|
@@ -190,7 +191,7 @@ export function useRichPaste() {
|
|
|
190
191
|
result.figmaFileKey &&
|
|
191
192
|
(hasImageHashes || hasImageNodeIds)
|
|
192
193
|
) {
|
|
193
|
-
|
|
194
|
+
debugLog('[useRichPaste] Fetching Figma images...',
|
|
194
195
|
hasImageHashes ? `${result.figmaImageHashes!.length} hashes` : 'no hashes',
|
|
195
196
|
hasImageNodeIds ? `${result.figmaImageNodeIds!.length} nodeIds` : 'no nodeIds',
|
|
196
197
|
);
|
|
@@ -211,7 +212,7 @@ export function useRichPaste() {
|
|
|
211
212
|
if (imageResult.applied > 0) {
|
|
212
213
|
// Notify change again since images were applied
|
|
213
214
|
notifyIframeChange();
|
|
214
|
-
|
|
215
|
+
debugLog(`[useRichPaste] Applied ${imageResult.applied} Figma images`);
|
|
215
216
|
}
|
|
216
217
|
}
|
|
217
218
|
|
|
@@ -228,21 +229,21 @@ export function useRichPaste() {
|
|
|
228
229
|
* Debug helper: Log clipboard contents
|
|
229
230
|
*/
|
|
230
231
|
const debugClipboard = useCallback((clipboardData: DataTransfer): void => {
|
|
231
|
-
|
|
232
|
-
|
|
232
|
+
debugLog('[useRichPaste] === Clipboard Debug ===');
|
|
233
|
+
debugLog('Available types:', Array.from(clipboardData.types));
|
|
233
234
|
|
|
234
235
|
clipboardData.types.forEach((type) => {
|
|
235
236
|
const data = clipboardData.getData(type);
|
|
236
|
-
|
|
237
|
+
debugLog(`[${type}]:`, data.substring(0, 500) + (data.length > 500 ? '...' : ''));
|
|
237
238
|
});
|
|
238
239
|
|
|
239
|
-
|
|
240
|
+
debugLog('Items:', Array.from(clipboardData.items).map(item => ({
|
|
240
241
|
kind: item.kind,
|
|
241
242
|
type: item.type,
|
|
242
243
|
})));
|
|
243
244
|
|
|
244
|
-
|
|
245
|
-
|
|
245
|
+
debugLog('Detected type:', detectContentType(clipboardData));
|
|
246
|
+
debugLog('[useRichPaste] === End Debug ===');
|
|
246
247
|
}, []);
|
|
247
248
|
|
|
248
249
|
return {
|
package/src/editor/parts.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* (移動・削除・並び替え)。スロットの外を直したいときは「部品から切り離す」。
|
|
16
16
|
*/
|
|
17
17
|
import type { EditorPartDef } from '../io';
|
|
18
|
+
import { findFlowInsertion } from './utils/drop-target';
|
|
18
19
|
|
|
19
20
|
export const PART_ATTR = 'data-part';
|
|
20
21
|
export const PART_VERSION_ATTR = 'data-part-v';
|
|
@@ -281,34 +282,15 @@ export function detachPart(el: HTMLElement): void {
|
|
|
281
282
|
|
|
282
283
|
/**
|
|
283
284
|
* フロー(流し込み)への挿入位置を決める。
|
|
284
|
-
*
|
|
285
|
+
* ドロップ位置の要素から、「セクションの器」の直下にある祖先まで上がり、その前後に入れる。
|
|
285
286
|
* 器 = `[data-wf-body]` / `main` / `#artboard` / body。何も当たらなければ器の末尾。
|
|
287
|
+
*
|
|
288
|
+
* 位置の計算そのものは drop-target.ts に置いてある。ドラッグ中に出す印
|
|
289
|
+
* (どこに入るかの横棒)と同じ関数を使わないと、印と実際の挿入位置がずれるため。
|
|
286
290
|
*/
|
|
287
291
|
export function insertIntoFlow(doc: Document, el: HTMLElement, x: number, y: number): void {
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
(node === doc.body ||
|
|
291
|
-
node.id === 'artboard' ||
|
|
292
|
-
node.hasAttribute('data-wf-body') ||
|
|
293
|
-
node.tagName === 'MAIN');
|
|
294
|
-
const fallback = (): Element =>
|
|
295
|
-
doc.querySelector('[data-wf-body]') ?? doc.querySelector('main') ?? doc.getElementById('artboard') ?? doc.body;
|
|
296
|
-
|
|
297
|
-
let hit: Element | null = doc.elementFromPoint(x, y);
|
|
298
|
-
if (!hit || hit === doc.body || hit.id === 'artboard' || hit.closest('.selection-box')) {
|
|
299
|
-
fallback().appendChild(el);
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
// 器の直下にあたる祖先まで上がる
|
|
303
|
-
let anchor: Element = hit;
|
|
304
|
-
while (anchor.parentElement && !isContainer(anchor.parentElement)) {
|
|
305
|
-
anchor = anchor.parentElement;
|
|
306
|
-
}
|
|
307
|
-
if (!anchor.parentElement) {
|
|
308
|
-
fallback().appendChild(el);
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
anchor.after(el);
|
|
292
|
+
const { parent, before } = findFlowInsertion(doc, x, y);
|
|
293
|
+
parent.insertBefore(el, before);
|
|
312
294
|
}
|
|
313
295
|
|
|
314
296
|
/**
|
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
OverrideImageValue,
|
|
15
15
|
} from '../../types/editor-components';
|
|
16
16
|
import { generateElementId } from './dom-utils';
|
|
17
|
+
import { debugLog } from './debug';
|
|
17
18
|
|
|
18
19
|
// ============================================================
|
|
19
20
|
// Constants
|
|
@@ -56,7 +57,7 @@ export function createDOMElement(
|
|
|
56
57
|
doc: Document = document
|
|
57
58
|
): HTMLElement {
|
|
58
59
|
// Debug logging
|
|
59
|
-
|
|
60
|
+
debugLog('[createDOMElement] Creating:', {
|
|
60
61
|
tagName: element.tagName,
|
|
61
62
|
className: element.className,
|
|
62
63
|
hasTextContent: !!element.textContent,
|
|
@@ -110,13 +111,13 @@ export function createDOMElement(
|
|
|
110
111
|
if (element.innerHTML !== undefined) {
|
|
111
112
|
// Use innerHTML for mixed content (text + elements)
|
|
112
113
|
el.innerHTML = element.innerHTML;
|
|
113
|
-
|
|
114
|
+
debugLog('[createDOMElement] Using innerHTML for mixed content');
|
|
114
115
|
} else if (element.textContent !== undefined) {
|
|
115
116
|
// textContent is explicitly set (possibly via override) - use it
|
|
116
117
|
// This takes precedence over children, which is important for text overrides
|
|
117
118
|
el.textContent = element.textContent;
|
|
118
119
|
if (element.children.length > 0) {
|
|
119
|
-
|
|
120
|
+
debugLog('[createDOMElement] textContent override replacing children structure');
|
|
120
121
|
}
|
|
121
122
|
} else {
|
|
122
123
|
// Recursively create and append children
|
|
@@ -155,7 +156,7 @@ export function createDOMElement(
|
|
|
155
156
|
}
|
|
156
157
|
}
|
|
157
158
|
|
|
158
|
-
|
|
159
|
+
debugLog('[createDOMElement] Created element:', el.tagName, 'with innerHTML length:', el.innerHTML.length);
|
|
159
160
|
|
|
160
161
|
return el;
|
|
161
162
|
}
|
|
@@ -417,7 +418,7 @@ export function renderInstance(
|
|
|
417
418
|
instance: ComponentInstance,
|
|
418
419
|
doc: Document = document
|
|
419
420
|
): HTMLElement {
|
|
420
|
-
|
|
421
|
+
debugLog('[renderInstance] Starting render:', {
|
|
421
422
|
masterId: master.id,
|
|
422
423
|
masterName: master.name,
|
|
423
424
|
instanceId: instance.id,
|
|
@@ -428,7 +429,7 @@ export function renderInstance(
|
|
|
428
429
|
// Log the master component's root element structure
|
|
429
430
|
const defaultVariant = master.variants.find(v => v.id === instance.variantId);
|
|
430
431
|
if (defaultVariant) {
|
|
431
|
-
|
|
432
|
+
debugLog('[renderInstance] Variant rootElement:', {
|
|
432
433
|
tagName: defaultVariant.rootElement.tagName,
|
|
433
434
|
className: defaultVariant.rootElement.className,
|
|
434
435
|
hasTextContent: !!defaultVariant.rootElement.textContent,
|
|
@@ -440,7 +441,7 @@ export function renderInstance(
|
|
|
440
441
|
|
|
441
442
|
// Resolve the instance to a ComponentElement
|
|
442
443
|
const resolved = resolveInstance(master, instance);
|
|
443
|
-
|
|
444
|
+
debugLog('[renderInstance] Resolved element:', {
|
|
444
445
|
tagName: resolved.tagName,
|
|
445
446
|
className: resolved.className,
|
|
446
447
|
hasTextContent: !!resolved.textContent,
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
resolveInstance,
|
|
20
20
|
} from './component-renderer';
|
|
21
21
|
import { detectOverrides } from './override-detection';
|
|
22
|
+
import { debugLog } from './debug';
|
|
22
23
|
|
|
23
24
|
// ============================================================
|
|
24
25
|
// Types
|
|
@@ -360,7 +361,7 @@ export function propagateMasterChanges(
|
|
|
360
361
|
// Find all DOM elements for this master
|
|
361
362
|
const domElements = findInstancesByMaster(iframeDoc, master.id);
|
|
362
363
|
|
|
363
|
-
|
|
364
|
+
debugLog('[propagateMasterChanges] Found DOM elements for master:', {
|
|
364
365
|
masterId: master.id,
|
|
365
366
|
domElementsCount: domElements.length,
|
|
366
367
|
});
|
|
@@ -369,13 +370,13 @@ export function propagateMasterChanges(
|
|
|
369
370
|
const domElementMap = new Map<string, HTMLElement>();
|
|
370
371
|
for (const el of domElements) {
|
|
371
372
|
const instanceId = getInstanceIdFromElement(el);
|
|
372
|
-
|
|
373
|
+
debugLog('[propagateMasterChanges] DOM element instanceId:', instanceId);
|
|
373
374
|
if (instanceId) {
|
|
374
375
|
domElementMap.set(instanceId, el);
|
|
375
376
|
}
|
|
376
377
|
}
|
|
377
378
|
|
|
378
|
-
|
|
379
|
+
debugLog('[propagateMasterChanges] Processing instances:', {
|
|
379
380
|
totalInstances: instances.length,
|
|
380
381
|
instanceIds: instances.map(i => i.id),
|
|
381
382
|
domMapKeys: Array.from(domElementMap.keys()),
|
|
@@ -385,13 +386,13 @@ export function propagateMasterChanges(
|
|
|
385
386
|
for (const instance of instances) {
|
|
386
387
|
// Skip detached instances
|
|
387
388
|
if (instance.isDetached) {
|
|
388
|
-
|
|
389
|
+
debugLog('[propagateMasterChanges] Skipping detached instance:', instance.id);
|
|
389
390
|
continue;
|
|
390
391
|
}
|
|
391
392
|
|
|
392
393
|
// Skip instances for other masters
|
|
393
394
|
if (instance.masterComponentId !== master.id) {
|
|
394
|
-
|
|
395
|
+
debugLog('[propagateMasterChanges] Skipping instance for other master:', instance.id);
|
|
395
396
|
continue;
|
|
396
397
|
}
|
|
397
398
|
|
|
@@ -400,11 +401,11 @@ export function propagateMasterChanges(
|
|
|
400
401
|
const domEl = domElementMap.get(instance.id);
|
|
401
402
|
if (!domEl) {
|
|
402
403
|
// Instance not in DOM, skip
|
|
403
|
-
|
|
404
|
+
debugLog('[propagateMasterChanges] DOM element not found for instance:', instance.id);
|
|
404
405
|
continue;
|
|
405
406
|
}
|
|
406
407
|
|
|
407
|
-
|
|
408
|
+
debugLog('[propagateMasterChanges] Found DOM element for instance:', instance.id);
|
|
408
409
|
|
|
409
410
|
// Validate variant still exists
|
|
410
411
|
let variantId = instance.variantId;
|
|
@@ -423,15 +424,15 @@ export function propagateMasterChanges(
|
|
|
423
424
|
let detectedOverrides: ComponentOverride[] = [];
|
|
424
425
|
if (effectiveVariant) {
|
|
425
426
|
// Debug: Log what we're comparing
|
|
426
|
-
|
|
427
|
-
|
|
427
|
+
debugLog('[propagateMasterChanges] === Override Detection Debug ===');
|
|
428
|
+
debugLog('[propagateMasterChanges] DOM element:', {
|
|
428
429
|
tagName: domEl.tagName,
|
|
429
430
|
elementId: domEl.getAttribute('data-element-id'),
|
|
430
431
|
textContent: domEl.textContent?.substring(0, 100),
|
|
431
432
|
innerHTML: domEl.innerHTML?.substring(0, 200),
|
|
432
433
|
childrenCount: domEl.children.length,
|
|
433
434
|
});
|
|
434
|
-
|
|
435
|
+
debugLog('[propagateMasterChanges] Master rootElement:', {
|
|
435
436
|
tagName: effectiveVariant.rootElement.tagName,
|
|
436
437
|
id: effectiveVariant.rootElement.id,
|
|
437
438
|
textContent: effectiveVariant.rootElement.textContent?.substring(0, 100),
|
|
@@ -442,7 +443,7 @@ export function propagateMasterChanges(
|
|
|
442
443
|
|
|
443
444
|
try {
|
|
444
445
|
detectedOverrides = detectOverrides(domEl, effectiveVariant.rootElement);
|
|
445
|
-
|
|
446
|
+
debugLog('[propagateMasterChanges] Detected DOM overrides:', {
|
|
446
447
|
instanceId: instance.id,
|
|
447
448
|
count: detectedOverrides.length,
|
|
448
449
|
overrides: detectedOverrides.map(o => ({
|
|
@@ -477,7 +478,7 @@ export function propagateMasterChanges(
|
|
|
477
478
|
}
|
|
478
479
|
const mergedOverrides = Array.from(overrideMap.values());
|
|
479
480
|
|
|
480
|
-
|
|
481
|
+
debugLog('[propagateMasterChanges] Merged overrides:', {
|
|
481
482
|
instanceId: instance.id,
|
|
482
483
|
existing: existingOverrides.length,
|
|
483
484
|
detected: detectedOverrides.length,
|
|
@@ -536,7 +537,7 @@ export function propagateMasterChanges(
|
|
|
536
537
|
});
|
|
537
538
|
};
|
|
538
539
|
applyIdMappings(newEl);
|
|
539
|
-
|
|
540
|
+
debugLog('[propagateMasterChanges] Applied ID mappings for duplicated instance:', {
|
|
540
541
|
instanceId: instance.id,
|
|
541
542
|
mappingsCount: idMappings.size,
|
|
542
543
|
});
|
|
@@ -563,7 +564,7 @@ export function propagateMasterChanges(
|
|
|
563
564
|
newEl.style.setProperty(prop, value);
|
|
564
565
|
}
|
|
565
566
|
|
|
566
|
-
|
|
567
|
+
debugLog('[propagateMasterChanges] Preserved position styles:', {
|
|
567
568
|
instanceId: instance.id,
|
|
568
569
|
preservedStyles: styleObj,
|
|
569
570
|
});
|
|
@@ -585,7 +586,7 @@ export function propagateMasterChanges(
|
|
|
585
586
|
}
|
|
586
587
|
}
|
|
587
588
|
|
|
588
|
-
|
|
589
|
+
debugLog(
|
|
589
590
|
`[propagateMasterChanges] Updated ${result.updatedCount} instances, failed ${result.failedCount}`
|
|
590
591
|
);
|
|
591
592
|
|