@growgroup/visual-editor 0.3.0 → 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.
- package/README.md +10 -1
- package/dist/editor.css +1 -1
- package/package.json +3 -2
- package/src/editor/EditorContext.tsx +11 -2
- package/src/editor/EditorToolbar.tsx +8 -2
- package/src/editor/FrontendVisualEditor.tsx +128 -45
- package/src/editor/components/EditorCanvas.tsx +87 -15
- package/src/editor/components/EditorContextMenu.tsx +12 -3
- package/src/editor/components/EditorFooter.tsx +2 -0
- package/src/editor/components/EditorHeader.tsx +3 -2
- package/src/editor/components/EditorLayerPanel.tsx +28 -2
- package/src/editor/components/EditorPropertyPanel.tsx +54 -25
- 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 +204 -58
- package/src/editor/components/multi-page/PageFramePreview.tsx +131 -22
- 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 +44 -1
- package/src/editor/contexts/EditorArtboardContext.tsx +60 -1
- package/src/editor/contexts/EditorComponentsContext.tsx +48 -31
- package/src/editor/contexts/EditorUIStateContext.tsx +4 -3
- package/src/editor/contexts/EditorVariablesContext.tsx +5 -4
- package/src/editor/contexts/MultiPageCanvasContext.tsx +211 -34
- 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 +27 -10
- 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/html-utils.ts +19 -6
- 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/index.ts +5 -0
- 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 +63 -4
- package/src/types/editor.ts +6 -2
- package/src/version.ts +9 -0
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
ComponentInstance,
|
|
15
15
|
OverridableProperties,
|
|
16
16
|
} from '../../types/editor-components';
|
|
17
|
+
import { debugLog } from './debug';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Find an element within the ComponentElement tree by ID
|
|
@@ -148,7 +149,7 @@ function detectSinglePropertyOverride(
|
|
|
148
149
|
switch (type) {
|
|
149
150
|
case 'text': {
|
|
150
151
|
// Debug: Log text comparison attempt
|
|
151
|
-
|
|
152
|
+
debugLog('[detectOverrides] Text comparison for element:', {
|
|
152
153
|
elementId,
|
|
153
154
|
masterHasInnerHTML: masterElement.innerHTML !== undefined,
|
|
154
155
|
masterChildrenCount: masterElement.children.length,
|
|
@@ -160,7 +161,7 @@ function detectSinglePropertyOverride(
|
|
|
160
161
|
// Skip text comparison for elements that use innerHTML (mixed content)
|
|
161
162
|
// because the text is embedded in the HTML structure
|
|
162
163
|
if (masterElement.innerHTML !== undefined) {
|
|
163
|
-
|
|
164
|
+
debugLog('[detectOverrides] Skipping: element uses innerHTML');
|
|
164
165
|
return null;
|
|
165
166
|
}
|
|
166
167
|
|
|
@@ -171,7 +172,7 @@ function detectSinglePropertyOverride(
|
|
|
171
172
|
if (masterElement.children.length === 0) {
|
|
172
173
|
const masterText = masterElement.textContent || '';
|
|
173
174
|
|
|
174
|
-
|
|
175
|
+
debugLog('[detectOverrides] Comparing text (leaf element):', {
|
|
175
176
|
elementId,
|
|
176
177
|
instanceText,
|
|
177
178
|
masterText,
|
|
@@ -179,7 +180,7 @@ function detectSinglePropertyOverride(
|
|
|
179
180
|
});
|
|
180
181
|
|
|
181
182
|
if (instanceText !== masterText) {
|
|
182
|
-
|
|
183
|
+
debugLog('[detectOverrides] Text override detected:', {
|
|
183
184
|
elementId,
|
|
184
185
|
instanceText: instanceText.substring(0, 50),
|
|
185
186
|
masterText: masterText.substring(0, 50),
|
|
@@ -196,7 +197,7 @@ function detectSinglePropertyOverride(
|
|
|
196
197
|
// Text overrides should only be created at leaf elements (no children)
|
|
197
198
|
// Creating a parent-level text override would destroy the child structure
|
|
198
199
|
// when the override is applied (textContent replaces all children)
|
|
199
|
-
|
|
200
|
+
debugLog('[detectOverrides] Skipping text override for element with children:', {
|
|
200
201
|
elementId,
|
|
201
202
|
childrenCount: masterElement.children.length,
|
|
202
203
|
reason: 'Text overrides at parent level would destroy child structure',
|
|
@@ -351,7 +352,7 @@ export function detectOverrides(
|
|
|
351
352
|
|
|
352
353
|
// Debug: Log child matching
|
|
353
354
|
if (componentElement.children.length > 0 || domChildren.length > 0) {
|
|
354
|
-
|
|
355
|
+
debugLog('[detectOverrides] Child matching:', {
|
|
355
356
|
parentId: componentElement.id,
|
|
356
357
|
domChildrenCount: domChildren.length,
|
|
357
358
|
domChildrenIds: domChildren.map(dc => dc.getAttribute('data-element-id')),
|
|
@@ -375,7 +376,7 @@ export function detectOverrides(
|
|
|
375
376
|
if (matchingDomChild) {
|
|
376
377
|
processElement(matchingDomChild, componentChild);
|
|
377
378
|
} else {
|
|
378
|
-
|
|
379
|
+
debugLog('[detectOverrides] No matching DOM child for component child:', {
|
|
379
380
|
componentChildId: componentChild.id,
|
|
380
381
|
componentChildTagName: componentChild.tagName,
|
|
381
382
|
availableDomIds: domChildren.map(dc => dc.getAttribute('data-element-id')),
|
|
@@ -387,10 +388,10 @@ export function detectOverrides(
|
|
|
387
388
|
|
|
388
389
|
processElement(instanceElement, masterElement);
|
|
389
390
|
|
|
390
|
-
|
|
391
|
-
|
|
391
|
+
debugLog('[detectOverrides] ========== FINAL RESULT ==========');
|
|
392
|
+
debugLog('[detectOverrides] Total overrides detected:', overrides.length);
|
|
392
393
|
if (overrides.length > 0) {
|
|
393
|
-
|
|
394
|
+
debugLog('[detectOverrides] Detected overrides:', overrides.map(o => ({
|
|
394
395
|
type: o.type,
|
|
395
396
|
targetElementId: o.targetElementId,
|
|
396
397
|
value: typeof o.value === 'string' ? o.value.substring(0, 50) : o.value,
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { twMerge } from 'tailwind-merge';
|
|
8
|
+
import { debugLog } from './debug';
|
|
8
9
|
import {
|
|
9
10
|
TAILWIND_CONFLICT_GROUPS,
|
|
10
11
|
ENUM_MAPPINGS,
|
|
@@ -63,7 +64,7 @@ export function cssToTailwindClass(
|
|
|
63
64
|
// CSS変数参照はstyle属性に直接設定
|
|
64
65
|
if (value.startsWith('var(')) {
|
|
65
66
|
// DEBUG: var() を検出した時のログ
|
|
66
|
-
|
|
67
|
+
debugLog('[DEBUG tailwind-utils] cssToTailwindClass detected var():', {
|
|
67
68
|
property,
|
|
68
69
|
value,
|
|
69
70
|
result: { className: null, styleValue: value },
|
|
@@ -421,7 +422,7 @@ export function applyTailwindStyles(
|
|
|
421
422
|
styles: Record<string, string>
|
|
422
423
|
): void {
|
|
423
424
|
// DEBUG: applyTailwindStyles 開始時のログ
|
|
424
|
-
|
|
425
|
+
debugLog('[DEBUG tailwind-utils] applyTailwindStyles called:', {
|
|
425
426
|
elementId: element.id,
|
|
426
427
|
elementTagName: element.tagName,
|
|
427
428
|
inputStyles: styles,
|
|
@@ -492,7 +493,7 @@ export function applyTailwindStyles(
|
|
|
492
493
|
});
|
|
493
494
|
|
|
494
495
|
// DEBUG: applyTailwindStyles 最終適用結果のログ
|
|
495
|
-
|
|
496
|
+
debugLog('[DEBUG tailwind-utils] applyTailwindStyles completed:', {
|
|
496
497
|
elementId: element.id,
|
|
497
498
|
classesToAdd,
|
|
498
499
|
stylesToApply,
|
package/src/index.ts
CHANGED
|
@@ -22,11 +22,16 @@ export type { FrontendVisualEditorProps } from "./editor/FrontendVisualEditor";
|
|
|
22
22
|
|
|
23
23
|
export { EditorProvider, useEditorContext } from "./editor/EditorContext";
|
|
24
24
|
export type { ContentListItem, EditorMode } from "./editor/EditorContext";
|
|
25
|
+
export type { DocumentAttributes } from "./editor/contexts/EditorArtboardContext";
|
|
26
|
+
export { computeContentDepths } from "./editor/contexts/EditorArtboardContext";
|
|
25
27
|
|
|
26
28
|
export { WEBPAGE_WIDTH, WEBPAGE_MIN_HEIGHT, SLIDE_WIDTH, SLIDE_HEIGHT, BREAKPOINT_PRESETS } from "./editor/constants";
|
|
27
29
|
|
|
28
30
|
export { registerAutoSaveFlush, flushAutoSave } from "./editor/autosave";
|
|
29
31
|
|
|
32
|
+
/** パッケージの版。利用側が dev サーバーの束ねたコードと node_modules の食い違い(再起動漏れ)を検出するのに使う */
|
|
33
|
+
export { EDITOR_VERSION } from "./version";
|
|
34
|
+
|
|
30
35
|
/**
|
|
31
36
|
* コメントの一覧(カンバン)。エディタとは別の画面に単独で置ける。
|
|
32
37
|
*
|
package/src/io.ts
CHANGED
|
@@ -36,15 +36,36 @@ export type EditorContent = {
|
|
|
36
36
|
comments?: EditorComment[];
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* 範囲アンカー。紙面(`#artboard`)の左上を原点にした px。
|
|
41
|
+
* `ref` は投稿したときの紙面の大きさ。紙面の幅が変わったら(ノート欄の有無など)
|
|
42
|
+
* 横方向だけ幅の比で補正する。縦は内容が増減すると動くので補正しない。
|
|
43
|
+
* width / height が 0 のときは「点」(クリックで置いたピン)。
|
|
44
|
+
*/
|
|
45
|
+
export type CommentRect = {
|
|
46
|
+
x: number;
|
|
47
|
+
y: number;
|
|
48
|
+
width: number;
|
|
49
|
+
height: number;
|
|
50
|
+
ref?: { width: number; height: number };
|
|
51
|
+
};
|
|
52
|
+
|
|
39
53
|
export type EditorComment = {
|
|
40
54
|
id: string;
|
|
41
55
|
author: string;
|
|
42
56
|
text: string;
|
|
43
57
|
createdAt: string;
|
|
44
58
|
resolved?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* ページ内の通し番号(投稿順)。削除しても振り直さないので、返信で「#3 の件」と
|
|
61
|
+
* 書いても指す先が変わらない。ホストが採番する(無いホストでは付かない)
|
|
62
|
+
*/
|
|
63
|
+
seq?: number;
|
|
45
64
|
/** 要素アンカー。無ければ全体へのコメント */
|
|
46
65
|
anchorSrc?: string;
|
|
47
66
|
anchorLabel?: string;
|
|
67
|
+
/** 範囲アンカー(紙面の px)。要素アンカーより優先して表示する */
|
|
68
|
+
anchorRect?: CommentRect;
|
|
48
69
|
replies?: { id: string; author: string; text: string; createdAt: string }[];
|
|
49
70
|
};
|
|
50
71
|
|
|
@@ -55,7 +76,7 @@ export type EditorDeck = {
|
|
|
55
76
|
};
|
|
56
77
|
|
|
57
78
|
export type CommentAction =
|
|
58
|
-
| { action: "add"; author: string; text: string; anchorSrc?: string; anchorLabel?: string }
|
|
79
|
+
| { action: "add"; author: string; text: string; anchorSrc?: string; anchorLabel?: string; anchorRect?: CommentRect }
|
|
59
80
|
| { action: "reply"; commentId: string; author: string; text: string }
|
|
60
81
|
| { action: "resolve"; commentId: string; resolved: boolean }
|
|
61
82
|
| { action: "delete"; commentId: string };
|
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
CSSVariableDefinition,
|
|
18
18
|
CSSVariableCollection,
|
|
19
19
|
} from '../../types/css-variables';
|
|
20
|
+
import { debugLog } from '../../editor/utils/debug';
|
|
20
21
|
|
|
21
22
|
// ============================================
|
|
22
23
|
// スコープタイプ定義
|
|
@@ -184,7 +185,7 @@ export async function getCSSVariables(
|
|
|
184
185
|
const data = readDoc(key);
|
|
185
186
|
|
|
186
187
|
if (!data) {
|
|
187
|
-
|
|
188
|
+
debugLog(`[CSSVariables] No variables found for ${scope}:`, resourceId);
|
|
188
189
|
return null;
|
|
189
190
|
}
|
|
190
191
|
|
|
@@ -240,7 +241,7 @@ export async function saveCSSVariables(
|
|
|
240
241
|
};
|
|
241
242
|
|
|
242
243
|
writeDoc(key, data);
|
|
243
|
-
|
|
244
|
+
debugLog(`[CSSVariables] Saved variables for ${scope}:`, resourceId, 'count:', variables.length);
|
|
244
245
|
} catch (error) {
|
|
245
246
|
console.error('[CSSVariables] Error saving variables:', error);
|
|
246
247
|
throw error;
|
|
@@ -298,7 +299,7 @@ export async function deleteCSSVariablesCollection(websiteId: string): Promise<v
|
|
|
298
299
|
const key = getCSSVariablesKey(websiteId);
|
|
299
300
|
const store = getStore();
|
|
300
301
|
if (store) store.removeItem(key);
|
|
301
|
-
|
|
302
|
+
debugLog('[CSSVariables] Deleted variables for website:', websiteId);
|
|
302
303
|
} catch (error) {
|
|
303
304
|
console.error('[CSSVariables] Error deleting variables:', error);
|
|
304
305
|
throw error;
|
|
@@ -42,6 +42,7 @@ async function persistDataUrl(dataUrl: string, fileName: string): Promise<string
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
import type { PageResourceUpload } from '../../types/page';
|
|
45
|
+
import { debugLog } from '../../editor/utils/debug';
|
|
45
46
|
|
|
46
47
|
// ============================================
|
|
47
48
|
// 内部ヘルパー
|
|
@@ -161,7 +162,7 @@ export async function uploadPageResource(
|
|
|
161
162
|
|
|
162
163
|
// ファイル削除(ローカルではリモート実体が無いため no-op)
|
|
163
164
|
export async function deletePageResource(storagePath: string): Promise<void> {
|
|
164
|
-
|
|
165
|
+
debugLog('[storage] deletePageResource is a no-op in local mode:', storagePath);
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
// ファイル情報取得(ローカルではメタデータを保持しないためスタブを返す)
|
|
@@ -264,7 +265,7 @@ export async function uploadFigmaImage(
|
|
|
264
265
|
// コンポーネントの画像ファイルを削除(ローカルでは no-op)
|
|
265
266
|
export async function deleteFigmaComponentImages(componentId: string): Promise<void> {
|
|
266
267
|
const basePath = `components/${componentId}/figma-images/`;
|
|
267
|
-
|
|
268
|
+
debugLog(`[storage] deleteFigmaComponentImages is a no-op in local mode: ${basePath}`);
|
|
268
269
|
}
|
|
269
270
|
|
|
270
271
|
// 画像ファイルサイズの推定(Base64から)
|
package/src/styles/skin.css
CHANGED
|
@@ -258,9 +258,46 @@
|
|
|
258
258
|
.gg-editor-ui .ed-resize-handle:hover, .gg-editor-ui .ed-resize-handle:focus-visible { background-color: var(--ed-accent); }
|
|
259
259
|
.gg-editor-ui .ed-layer-selected { box-shadow: inset 3px 0 var(--ed-accent); }
|
|
260
260
|
.gg-editor-ui .ed-layer-selected :is(.text-white, .text-blue-100, .text-blue-200) { color: var(--ed-accent); }
|
|
261
|
-
|
|
262
|
-
|
|
261
|
+
/* ── 右のプロパティパネル(Figma UI3 の寸法に寄せる) ──────────────
|
|
262
|
+
節は 1px の罫線で区切り、見出しは 13px/600・高さ 40px、中身は左右 16px。
|
|
263
|
+
入力は高さ 32px・角丸 6px・面の色(枠線は無し。ホバーで枠、フォーカスでアクセント)。
|
|
264
|
+
ラベルは 11px の補助色。数値の小さな入力(十字のパディング等)は 28px */
|
|
263
265
|
.gg-editor-ui [data-property-panel] .ed-panel-heading { position: sticky; top: 0; z-index: 1; }
|
|
266
|
+
.gg-editor-ui [data-property-panel] .p-3 { padding: 0; }
|
|
267
|
+
.gg-editor-ui [data-property-panel] .p-3 > * { padding-left: 16px; padding-right: 16px; }
|
|
268
|
+
.gg-editor-ui [data-property-panel] .p-3 > .mb-3 { min-height: 44px; margin-bottom: 0; }
|
|
269
|
+
.gg-editor-ui [data-property-panel] .p-3 > [data-state] { border-top: 1px solid var(--ed-line); }
|
|
270
|
+
.gg-editor-ui [data-property-panel] .p-3 > .ed-part-card { margin: 0 16px 12px; padding: 10px 12px; }
|
|
271
|
+
.gg-editor-ui [data-property-panel] [data-state] > button[data-state] {
|
|
272
|
+
min-height: 40px; padding: 0; border-top: 0; font-size: 13px; font-weight: 600; color: var(--ed-text); border-radius: 0;
|
|
273
|
+
}
|
|
274
|
+
.gg-editor-ui [data-property-panel] [data-state] > button[data-state]:hover { background: transparent; color: var(--ed-text); }
|
|
275
|
+
.gg-editor-ui [data-property-panel] [data-state] > button[data-state] > span:first-child > svg { width: 14px; height: 14px; color: var(--ed-muted); }
|
|
276
|
+
.gg-editor-ui [data-property-panel] [data-state] > button[data-state] > svg:last-child { width: 14px; height: 14px; color: var(--ed-muted); }
|
|
277
|
+
.gg-editor-ui [data-property-panel] [data-state] > div[data-state="open"] { padding-bottom: 16px; }
|
|
278
|
+
.gg-editor-ui [data-property-panel] label, .gg-editor-ui [data-property-panel] .ed-prop-label { font-size: 11px; line-height: 1.4; color: var(--ed-muted); font-weight: 500; }
|
|
279
|
+
.gg-editor-ui [data-property-panel] :is(input:not([type="color"]):not([type="checkbox"]):not([type="range"]), select, [role="combobox"]):not(.group *) {
|
|
280
|
+
min-height: 32px; height: 32px; border-radius: 6px; background: var(--ed-surface); border: 1px solid transparent !important;
|
|
281
|
+
color: var(--ed-text); font-size: 12px; padding-left: 8px; padding-right: 8px; box-shadow: none;
|
|
282
|
+
}
|
|
283
|
+
.gg-editor-ui [data-property-panel] :is(input:not([type="color"]):not([type="checkbox"]):not([type="range"]), select, [role="combobox"]):not(.group *):hover { border-color: var(--ed-line-strong) !important; }
|
|
284
|
+
.gg-editor-ui [data-property-panel] :is(input:not([type="color"]):not([type="checkbox"]):not([type="range"]), select, [role="combobox"]):not(.group *):focus-visible,
|
|
285
|
+
.gg-editor-ui [data-property-panel] :is(input:not([type="color"]):not([type="checkbox"]):not([type="range"]), select, [role="combobox"]):not(.group *):focus-within { border-color: var(--ed-accent) !important; outline: none; box-shadow: none; }
|
|
286
|
+
/* 数値のスクラブ入力(CompactNumberInput。器 .group が枠) */
|
|
287
|
+
.gg-editor-ui [data-property-panel] .group.h-5 { height: 28px; min-width: 40px; border-radius: 6px; background: var(--ed-surface); border-color: transparent; }
|
|
288
|
+
.gg-editor-ui [data-property-panel] .group.h-5:hover { border-color: var(--ed-line-strong); }
|
|
289
|
+
.gg-editor-ui [data-property-panel] .group.h-5:focus-within { border-color: var(--ed-accent); }
|
|
290
|
+
.gg-editor-ui [data-property-panel] .group.h-5 input { font-size: 12px; color: var(--ed-text); }
|
|
291
|
+
.gg-editor-ui [data-property-panel] .group.h-5 input:focus { box-shadow: none; }
|
|
292
|
+
/* 配置・書式のトグル(h-7 w-7)は 32px の角丸 6px。押下は面の色で示す */
|
|
293
|
+
.gg-editor-ui [data-property-panel] button.h-7.w-7, .gg-editor-ui [data-property-panel] button.h-7 { height: 32px; min-width: 32px; border-radius: 6px; }
|
|
294
|
+
.gg-editor-ui [data-property-panel] button.h-7.w-7 svg { width: 14px; height: 14px; }
|
|
295
|
+
.gg-editor-ui [data-property-panel] button[data-state]:not([role="checkbox"]):not([role="switch"]) { min-height: 32px; }
|
|
296
|
+
.gg-editor-ui [data-property-panel] button[role="checkbox"] { width: 16px; height: 16px; min-height: 16px; border-radius: 4px; border: 1px solid var(--ed-line-strong); background: var(--ed-surface); }
|
|
297
|
+
.gg-editor-ui [data-property-panel] button[role="checkbox"][data-state="checked"] { background: var(--ed-accent-strong); border-color: var(--ed-accent-strong); color: #fff; }
|
|
298
|
+
.gg-editor-ui [data-property-panel] input[type="color"] { width: 28px; height: 28px; border-radius: 6px; border: 1px solid var(--ed-line-strong); padding: 0; }
|
|
299
|
+
.gg-editor-ui [data-property-panel] .grid.grid-cols-2 { gap: 8px; }
|
|
300
|
+
.gg-editor-ui [data-property-panel] .space-y-2 > * + *, .gg-editor-ui [data-property-panel] .space-y-3 > * + * { margin-top: 10px; }
|
|
264
301
|
/* ポータル自身も配色スコープ。利用側にToasterがある場合もその設定を奪わない。 */
|
|
265
302
|
.gg-editor-ui[data-editor-surface="popup"], .gg-editor-ui[data-editor-surface="dialog"], .gg-editor-ui[data-editor-surface="tooltip"] {
|
|
266
303
|
color: var(--ed-text); background-color: var(--ed-panel-raised); border-color: var(--ed-line); border-radius: 12px; box-shadow: var(--ed-shadow); z-index: 10000;
|
|
@@ -288,13 +325,31 @@
|
|
|
288
325
|
.gg-editor-ui :is(input:not([type="color"]), textarea, select):not([data-editor-preview] *) { border-color: var(--ed-line-strong) !important; }
|
|
289
326
|
|
|
290
327
|
/* ── マルチフレームのキャンバス(Figma 風) ──────────────
|
|
291
|
-
|
|
328
|
+
フレーム名とリングはオーバーレイの中で scale(1/倍率) を掛けて画面上の大きさに戻す
|
|
329
|
+
(倍率に関係なく同じ太さ・同じ文字サイズ)。
|
|
292
330
|
見るだけの紙面の骨組み(スケルトン)だけは転写層の中にあるので、フレームの寸法に対する % で描く */
|
|
331
|
+
/* フレームの白地だけ。枠線と階層の線は画面の座標の canvas(CanvasFrameDecor)が描く。
|
|
332
|
+
転写層の中に枠線を持たせると、倍率が変わるたびに転写層まるごとが塗り直される(実測) */
|
|
333
|
+
.gg-editor-ui .ed-frame-shell { background: #fff; }
|
|
334
|
+
.gg-editor-ui .ed-frame-label-row { position: absolute; left: 0; height: 20px; display: inline-flex; align-items: center; gap: 2px; max-width: 100%; }
|
|
293
335
|
.gg-editor-ui .ed-frame-label {
|
|
294
|
-
|
|
336
|
+
height: 20px; min-width: 0; display: inline-flex; align-items: center; gap: 6px;
|
|
295
337
|
padding: 0 6px 0 4px; border-radius: 4px; font-size: 11px; line-height: 1; white-space: nowrap;
|
|
296
338
|
color: var(--ed-muted); pointer-events: auto; cursor: pointer; overflow: hidden; text-overflow: ellipsis;
|
|
297
339
|
}
|
|
340
|
+
/* 別タブで開く(編集しない表示)。名前の右。ホバー・編集中のときだけ見せる */
|
|
341
|
+
.gg-editor-ui .ed-frame-label-link {
|
|
342
|
+
display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 18px; height: 18px;
|
|
343
|
+
border-radius: 4px; color: var(--ed-muted); pointer-events: auto; opacity: 0; transition: opacity .12s;
|
|
344
|
+
}
|
|
345
|
+
.gg-editor-ui .ed-frame-label-link svg { width: 12px; height: 12px; }
|
|
346
|
+
.gg-editor-ui .ed-frame-label-row:hover .ed-frame-label-link, .gg-editor-ui .ed-frame-label-active + .ed-frame-label-link, .gg-editor-ui .ed-frame-label-link:focus-visible { opacity: 1; }
|
|
347
|
+
.gg-editor-ui .ed-frame-label-link:hover { color: var(--ed-text); background: var(--ed-panel); box-shadow: 0 0 0 1px var(--ed-line); }
|
|
348
|
+
/* 部品(data-part)のカード。紙面の紫の選択枠と同じ色 */
|
|
349
|
+
.gg-editor-ui .ed-part-card { border: 1px solid color-mix(in srgb, #7b61ff 45%, var(--ed-line)); background: color-mix(in srgb, #7b61ff 8%, var(--ed-panel)); color: var(--ed-text); }
|
|
350
|
+
.gg-editor-ui .ed-part-accent { color: #7b61ff; }
|
|
351
|
+
.gg-editor-ui[data-editor-theme="dark"] .ed-part-accent { color: #a99cff; }
|
|
352
|
+
.gg-editor-ui .ed-part-note { color: var(--ed-muted); }
|
|
298
353
|
.gg-editor-ui .ed-frame-label:hover { color: var(--ed-text); background: var(--ed-panel); box-shadow: 0 0 0 1px var(--ed-line); }
|
|
299
354
|
.gg-editor-ui .ed-frame-label-active, .gg-editor-ui .ed-frame-label-active:hover { color: var(--ed-accent); font-weight: 600; }
|
|
300
355
|
.gg-editor-ui .ed-frame-label-index { font-variant-numeric: tabular-nums; opacity: 0.7; }
|
|
@@ -306,6 +361,10 @@
|
|
|
306
361
|
.gg-editor-ui .ed-canvas-marquee { position: absolute; z-index: 20; pointer-events: none; border: 1px solid var(--ed-accent); background: color-mix(in srgb, var(--ed-accent) 12%, transparent); }
|
|
307
362
|
.gg-editor-ui .ed-frame-ring-active { box-shadow: 0 0 0 1.5px var(--ed-accent-strong); }
|
|
308
363
|
.gg-editor-ui .ed-frame-ring-hover { box-shadow: 0 0 0 1px var(--ed-accent-strong); opacity: 0.55; }
|
|
364
|
+
/* 縮小の途中(倍率 0.4 未満)は、画像が読めている紙面の iframe を止めて、下に敷いた画像を見せる。
|
|
365
|
+
iframe を外して(メモリを返して)しまうのは操作が止まってから ── その間もラスタは止まっている。
|
|
366
|
+
属性 1 つ(キャンバスの容器の data-canvas-preview-image)の書き換えで 26 枚がいっぺんに切り替わる */
|
|
367
|
+
.gg-editor-ui [data-canvas-preview-image="1"] [data-page-preview][data-image-ready="1"] iframe { visibility: hidden; }
|
|
309
368
|
.gg-editor-ui .ed-frame-skeleton { position: absolute; inset: 0; padding: 6% 8%; display: flex; flex-direction: column; gap: 3%; background: #fff; }
|
|
310
369
|
.gg-editor-ui .ed-frame-skeleton-bar { display: block; height: 2.5%; min-height: 8px; border-radius: 999px; background: #eceff3; }
|
|
311
370
|
.gg-editor-ui .ed-frame-skeleton-note { font-size: 40px; color: #9aa3b2; }
|
package/src/types/editor.ts
CHANGED
|
@@ -16,7 +16,8 @@ export type EditorTool =
|
|
|
16
16
|
| 'text' // T - Text tool
|
|
17
17
|
| 'scale' // K - Scale tool
|
|
18
18
|
| 'eraser' // E - Eraser (ペン/鉛筆ストロークを消す)
|
|
19
|
-
| 'shape'
|
|
19
|
+
| 'shape' // 図形ギャラリーで選んだ図形(ドラッグでサイズ指定)
|
|
20
|
+
| 'comment'; // C - コメント(紙面をドラッグして範囲を指定。クリックで点)
|
|
20
21
|
|
|
21
22
|
// Shape types that can be drawn
|
|
22
23
|
export type ShapeType = 'rectangle' | 'ellipse' | 'line' | 'arrow' | 'text' | 'pen' | 'pencil' | 'frame';
|
|
@@ -101,6 +102,7 @@ export type ShortcutAction =
|
|
|
101
102
|
| 'text'
|
|
102
103
|
| 'frame'
|
|
103
104
|
| 'scale'
|
|
105
|
+
| 'comment'
|
|
104
106
|
// Edit operations
|
|
105
107
|
| 'undo'
|
|
106
108
|
| 'redo'
|
|
@@ -189,7 +191,7 @@ export interface ToolConfig {
|
|
|
189
191
|
label: string;
|
|
190
192
|
shortcut: string;
|
|
191
193
|
icon: string; // Lucide icon name
|
|
192
|
-
group: 'selection' | 'shapes' | 'drawing' | 'text' | 'frame';
|
|
194
|
+
group: 'selection' | 'shapes' | 'drawing' | 'text' | 'frame' | 'comment';
|
|
193
195
|
}
|
|
194
196
|
|
|
195
197
|
// Default tool configurations
|
|
@@ -205,6 +207,7 @@ export const TOOL_CONFIGS: ToolConfig[] = [
|
|
|
205
207
|
{ id: 'pen', label: 'ペン', shortcut: 'P', icon: 'Pen', group: 'drawing' },
|
|
206
208
|
{ id: 'pencil', label: 'フリーハンド', shortcut: '⇧P', icon: 'Pencil', group: 'drawing' },
|
|
207
209
|
{ id: 'text', label: 'テキスト', shortcut: 'T', icon: 'Type', group: 'text' },
|
|
210
|
+
{ id: 'comment', label: 'コメント(範囲を指定)', shortcut: 'C', icon: 'MessageSquare', group: 'comment' },
|
|
208
211
|
];
|
|
209
212
|
|
|
210
213
|
/**
|
|
@@ -232,6 +235,7 @@ export const KEYBOARD_SHORTCUTS: Record<string, ShortcutAction> = {
|
|
|
232
235
|
'shift+p': 'pencil',
|
|
233
236
|
't': 'text',
|
|
234
237
|
'f': 'frame',
|
|
238
|
+
'c': 'comment',
|
|
235
239
|
|
|
236
240
|
// Edit operations (Cmd/Ctrl + key)
|
|
237
241
|
'meta+z': 'undo',
|
package/src/version.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* パッケージの版(package.json の version と同じ値。scripts/check-version.mjs が一致を検査する)。
|
|
3
|
+
*
|
|
4
|
+
* 利用側が「いま動いているエディタの版」を知るために使う。dev サーバー(Vite)は起動時に
|
|
5
|
+
* node_modules を束ねるので、パッケージを更新しても再起動するまで古いコードが動く。
|
|
6
|
+
* 利用側はこの値と node_modules の package.json を見比べて、食い違っていれば
|
|
7
|
+
* 「再起動してください」と出せる(構成ラフのテンプレートが行っている)
|
|
8
|
+
*/
|
|
9
|
+
export const EDITOR_VERSION = '0.4.0';
|