@ptengine/inline-editor 0.1.5 → 0.1.7
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/CHANGELOG.md +69 -0
- package/dist/InlineEditor.d.ts +15 -1
- package/dist/InlineEditor.js +226 -52
- package/dist/canvas-ui/tree/TreePanel.d.ts +2 -1
- package/dist/canvas-ui/tree/TreePanel.js +79 -39
- package/dist/chrome/EditorChrome.d.ts +2 -0
- package/dist/chrome/EditorChrome.js +1 -1
- package/dist/chrome/VersionMenu.d.ts +3 -1
- package/dist/chrome/VersionMenu.js +24 -12
- package/dist/chrome/topbarPrimitives.d.ts +5 -52
- package/dist/chrome/topbarPrimitives.js +13 -31
- package/dist/dialogs/ClickActionDialog.js +4 -5
- package/dist/dialogs/CodeField.d.ts +4 -0
- package/dist/dialogs/CodeField.js +10 -0
- package/dist/dialogs/HtmlEditDialog.js +3 -2
- package/dist/dialogs/ImageEditDialog.js +53 -15
- package/dist/dialogs/ImageSettingDialog.js +3 -3
- package/dist/dialogs/InsertElementDialog.js +10 -6
- package/dist/dialogs/MediaPreview.d.ts +5 -0
- package/dist/dialogs/MediaPreview.js +34 -0
- package/dist/dialogs/ReplaceVideoDialog.js +8 -4
- package/dist/index.d.ts +1 -0
- package/dist/kernel/ai/aiInlineApply.d.ts +4 -3
- package/dist/kernel/blocks/BlockOverlay.js +1 -1
- package/dist/kernel/blocks/resolveBlocks.d.ts +16 -0
- package/dist/kernel/blocks/resolveBlocks.js +39 -6
- package/dist/kernel/changelist/ChangeListPanel.js +11 -9
- package/dist/kernel/changelist/useChangeList.d.ts +5 -0
- package/dist/kernel/changelist/useChangeList.js +85 -10
- package/dist/kernel/selection/SelectionOverlay.js +1 -1
- package/dist/kernel/selection/SelectionToolbar.js +26 -8
- package/dist/kernel/selection/buildRootShelf.d.ts +56 -2
- package/dist/kernel/selection/buildRootShelf.js +189 -41
- package/dist/kernel/selection/elementAiContext.d.ts +11 -3
- package/dist/kernel/selection/resolveAnchors.d.ts +17 -0
- package/dist/kernel/selection/resolveAnchors.js +59 -11
- package/dist/locales.d.ts +201 -48
- package/dist/locales.js +13 -9
- package/dist/panels/EditorStatus.d.ts +5 -0
- package/dist/panels/EditorStatus.js +4 -0
- package/dist/panels/MovePositionControls.d.ts +6 -0
- package/dist/panels/MovePositionControls.js +35 -0
- package/dist/panels/ScreenshotCard.d.ts +7 -0
- package/dist/panels/ScreenshotCard.js +57 -0
- package/dist/panels/StylePanel.d.ts +9 -1
- package/dist/panels/StylePanel.js +108 -12
- package/dist/reviewLocales.d.ts +149 -0
- package/dist/reviewLocales.js +143 -0
- package/dist/tree/LayersPanel.js +4 -2
- package/dist/useInlineEditor.d.ts +1 -0
- package/dist/useInlineEditor.js +40 -1
- package/package.json +2 -2
package/dist/InlineEditor.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { EditorStatus } from './panels/EditorStatus.js';
|
|
3
|
+
import { MovePositionControls } from './panels/MovePositionControls.js';
|
|
4
|
+
import { ScreenshotCard } from './panels/ScreenshotCard.js';
|
|
2
5
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
6
|
import { useTranslation } from 'react-i18next';
|
|
7
|
+
import { createPortal } from 'react-dom';
|
|
4
8
|
import { CANVAS_UI_SCOPE } from './canvas-ui/index.js';
|
|
5
9
|
import { EditorChrome } from './chrome/EditorChrome.js';
|
|
6
10
|
import { CanvasFrame, TARGET_VIEWPORT_WIDTH, computeZoom } from './kernel/CanvasFrame.js';
|
|
@@ -13,17 +17,17 @@ import { ChangeListPanel } from './kernel/changelist/ChangeListPanel.js';
|
|
|
13
17
|
import { ChangeListPopover } from './kernel/changelist/ChangeListPopover.js';
|
|
14
18
|
import { frameRectToProductRect, measureFrameContext } from './kernel/selection/geometry.js';
|
|
15
19
|
import { buildElementAiContext, extractPageLinks, linksFragment, ROOT_SHELF_LIMIT, SHELF_TREE_MAX_DEPTH, SHELF_TREE_MAX_NODES } from './kernel/selection/elementAiContext.js';
|
|
16
|
-
import { getAncestorChain, getDomTree, getElementCapabilities, getElementImages, getOuterHtml, resolveCanvasSelector } from './kernel/canvasCommands.js';
|
|
20
|
+
import { getAncestorChain, getDomTree, getElementCapabilities, getElementImages, getOuterHtml, resolveCanvasSelector, queryElements } from './kernel/canvasCommands.js';
|
|
17
21
|
import { useAiSelectionContext } from './kernel/selection/useAiSelectionContext.js';
|
|
18
22
|
import { buildDescendantShelf } from './kernel/selection/buildDescendantShelf.js';
|
|
19
|
-
import { buildRootShelf } from './kernel/selection/buildRootShelf.js';
|
|
23
|
+
import { ROOT_SHELF_TREE_PARAMS, buildRootShelf } from './kernel/selection/buildRootShelf.js';
|
|
20
24
|
import { SHELF_LAYOUT_FIELDS } from './kernel/selection/rowLayout.js';
|
|
21
25
|
import { backgroundImageAncestorFor, blockContainerFor, resolveInlinePlanAnchors, withImageRows } from './kernel/selection/resolveAnchors.js';
|
|
22
26
|
import { elementGeometryForAi } from './kernel/selection/elementGeometryForAi.js';
|
|
23
27
|
import { applyAiChanges } from './kernel/ai/aiInlineApply.js';
|
|
24
28
|
import { APPLY_CHECK_SETTLE_MS } from './kernel/ai/postApplyCheck.js';
|
|
25
29
|
import { deleteElement, editHtml, editStyle, insertElement, replaceImage, replaceVideo, setClickAction, setImageAlt } from './kernel/changelist/useChangeList.js';
|
|
26
|
-
import { Dialog, DialogContent, DialogHeader, DialogTitle, ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@ptengine/design-components';
|
|
30
|
+
import { Button, Dialog, DialogContent, DialogHeader, DialogTitle, ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@ptengine/design-components';
|
|
27
31
|
import { pasteModes } from './kernel/selection/toolbarButtons.js';
|
|
28
32
|
import { ClickActionDialog } from './dialogs/ClickActionDialog.js';
|
|
29
33
|
import { ImageEditDialog } from './dialogs/ImageEditDialog.js';
|
|
@@ -32,6 +36,7 @@ import { ImageSettingDialog } from './dialogs/ImageSettingDialog.js';
|
|
|
32
36
|
import { InsertElementDialog } from './dialogs/InsertElementDialog.js';
|
|
33
37
|
import { ReplaceVideoDialog } from './dialogs/ReplaceVideoDialog.js';
|
|
34
38
|
import { StylePanel } from './panels/StylePanel.js';
|
|
39
|
+
import { STYLE_SECTIONS } from './panels/styleFields.js';
|
|
35
40
|
import { HtmlEditDialog } from './dialogs/HtmlEditDialog.js';
|
|
36
41
|
import { setCode } from './kernel/canvasCommands.js';
|
|
37
42
|
import { describeCanvasError } from './kernel/canvasErrors.js';
|
|
@@ -51,13 +56,15 @@ function imageTargetOf(caps) {
|
|
|
51
56
|
return branch === 'background' ? 'background' : undefined;
|
|
52
57
|
}
|
|
53
58
|
const AI_ELEMENT_IMAGE_LIMIT = 4;
|
|
54
|
-
export function InlineEditor({ url, device, onDeviceChange, title, versionLabel = 'V1', chrome = 'full', onExit, onSave, libraryComponents, libraryLoading, onAddVersion, onRenameVersion, onPreview, onToggleAi, aiOpen, uploadImage, initialChanges, onHydrated, onAskAi, onPageContextReady, anchors, onEditorReady, onSessionReady, onAiContextChange, onAiApplyReady, onAiInsertedRemoved, onAiInsertedRestored, trackAiSelection, renderNodeIcon, versions, activeVersionId, versionChanges, onVersionChange, onVersionLeave, onVersionEnter, resolveChangeId, allowedLinkUrls, viewportWidthOverride, blockOverlay }) {
|
|
59
|
+
export function InlineEditor({ url, device, onDeviceChange, title, versionLabel = 'V1', chrome = 'full', onExit, onSave, libraryComponents, libraryLoading, onAddVersion, onRenameVersion, removableVersionIds, onRemoveVersion, onPreview, onToggleAi, aiOpen, uploadImage, initialChanges, onHydrated, onAskAi, onPageContextReady, anchors, pageBlocks, onEditorReady, onSessionReady, onAiContextChange, onAiApplyReady, onAiInsertedRemoved, onAiInsertedRestored, trackAiSelection, renderNodeIcon, versions, activeVersionId, versionChanges, onVersionChange, onVersionLeave, onVersionEnter, resolveChangeId, allowedLinkUrls, viewportWidthOverride, blockOverlay }) {
|
|
55
60
|
const { t } = useTranslation();
|
|
56
61
|
const canvasAreaRef = useRef(null);
|
|
57
62
|
const canvasIframeRef = useRef(null);
|
|
58
63
|
const insertedRemovedRef = useRef(null);
|
|
59
64
|
const insertedRestoredRef = useRef(null);
|
|
65
|
+
const historyShortcutRef = useRef(() => { });
|
|
60
66
|
const editor = useInlineEditor({
|
|
67
|
+
onHistoryShortcut: direction => historyShortcutRef.current(direction),
|
|
61
68
|
onInsertedRemoved: selectors => insertedRemovedRef.current?.(selectors),
|
|
62
69
|
onInsertedRestored: selectors => insertedRestoredRef.current?.(selectors),
|
|
63
70
|
url,
|
|
@@ -73,11 +80,11 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
73
80
|
const [commandError, setCommandError] = useState(null);
|
|
74
81
|
const [aiApplyNote, setAiApplyNote] = useState(null);
|
|
75
82
|
const [shot, setShot] = useState(null);
|
|
76
|
-
const [shotUrl, setShotUrl] = useState(null);
|
|
77
83
|
const [panelWidth, setPanelWidth] = useState(0);
|
|
78
84
|
const [showChangeList, setShowChangeList] = useState(false);
|
|
79
85
|
const [stylePanelOpen, setStylePanelOpen] = useState(false);
|
|
80
86
|
const [htmlDialog, setHtmlDialog] = useState({ open: false, seed: '' });
|
|
87
|
+
const [currentStyles, setCurrentStyles] = useState({});
|
|
81
88
|
const [imageDialog, setImageDialog] = useState({ open: false, seed: '' });
|
|
82
89
|
const [imageSettingDialog, setImageSettingDialog] = useState({ open: false, seed: '' });
|
|
83
90
|
const [videoDialog, setVideoDialog] = useState({ open: false, seed: '' });
|
|
@@ -94,6 +101,25 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
94
101
|
return '';
|
|
95
102
|
}
|
|
96
103
|
}, [url]);
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (!stylePanelOpen || !selection || !frameProps?.sessionId)
|
|
106
|
+
return;
|
|
107
|
+
let cancelled = false;
|
|
108
|
+
void queryElements(frameProps.sessionId, [selection.selector], {
|
|
109
|
+
fields: STYLE_SECTIONS.flatMap(section => section.fields.map(field => `computed:${field.property}`))
|
|
110
|
+
})
|
|
111
|
+
.then(result => {
|
|
112
|
+
if (!cancelled)
|
|
113
|
+
setCurrentStyles(result.results[0]?.matches[0]?.computed ?? {});
|
|
114
|
+
})
|
|
115
|
+
.catch(error => {
|
|
116
|
+
if (!cancelled)
|
|
117
|
+
setCommandError(describeCanvasError(error));
|
|
118
|
+
});
|
|
119
|
+
return () => {
|
|
120
|
+
cancelled = true;
|
|
121
|
+
};
|
|
122
|
+
}, [stylePanelOpen, selection?.selector, frameProps?.sessionId, changeList.list]);
|
|
97
123
|
const changeListRef = useRef(changeList);
|
|
98
124
|
changeListRef.current = changeList;
|
|
99
125
|
const versionChangesRef = useRef(versionChanges);
|
|
@@ -170,6 +196,27 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
170
196
|
}
|
|
171
197
|
void run().catch(e => setCommandError(describeCanvasError(e)));
|
|
172
198
|
}, [t]);
|
|
199
|
+
historyShortcutRef.current = direction => {
|
|
200
|
+
if (!domReady || textEditing || switchingRef.current)
|
|
201
|
+
return;
|
|
202
|
+
uiWrite(() => enqueue(() => direction === 'undo' ? changeListRef.current.undo() : changeListRef.current.redo()));
|
|
203
|
+
};
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
const onKey = (event) => {
|
|
206
|
+
if (!(event.metaKey || event.ctrlKey) || event.altKey)
|
|
207
|
+
return;
|
|
208
|
+
const target = event.target;
|
|
209
|
+
if (target?.closest('input, textarea, select, [contenteditable="true"], [role="textbox"]'))
|
|
210
|
+
return;
|
|
211
|
+
const key = event.key.toLowerCase();
|
|
212
|
+
if (key !== 'z' && !(key === 'y' && event.ctrlKey))
|
|
213
|
+
return;
|
|
214
|
+
event.preventDefault();
|
|
215
|
+
historyShortcutRef.current(event.shiftKey || key === 'y' ? 'redo' : 'undo');
|
|
216
|
+
};
|
|
217
|
+
document.addEventListener('keydown', onKey);
|
|
218
|
+
return () => document.removeEventListener('keydown', onKey);
|
|
219
|
+
}, []);
|
|
173
220
|
useEffect(() => {
|
|
174
221
|
if (!hasVersions || !activeVersionId)
|
|
175
222
|
return;
|
|
@@ -200,18 +247,43 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
200
247
|
}, [sessionIdForAssets]);
|
|
201
248
|
const hydratedSessionRef = useRef(null);
|
|
202
249
|
const sessionIdForHydrate = frameProps?.sessionId;
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
250
|
+
const deviceSnapshot = useRef(null);
|
|
251
|
+
const changeDevice = (next) => {
|
|
252
|
+
if (next === device || switchingRef.current)
|
|
253
|
+
return;
|
|
254
|
+
switchingRef.current = true;
|
|
255
|
+
setSwitching(true);
|
|
256
|
+
void enqueue(async () => {
|
|
257
|
+
await changeListRef.current.whenIdle();
|
|
258
|
+
const live = changeListRef.current.peekList();
|
|
259
|
+
deviceSnapshot.current = { version: activeVersionId, changes: live.map(r => r.change) };
|
|
260
|
+
if (activeVersionId)
|
|
261
|
+
versionCallbacksRef.current.onVersionLeave?.(activeVersionId, live);
|
|
262
|
+
onDeviceChange(next);
|
|
263
|
+
})
|
|
264
|
+
.catch(error => setCommandError(describeCanvasError(error)))
|
|
265
|
+
.finally(() => {
|
|
266
|
+
switchingRef.current = false;
|
|
267
|
+
setSwitching(false);
|
|
268
|
+
});
|
|
269
|
+
};
|
|
270
|
+
const bootChanges = deviceSnapshot.current?.version === activeVersionId && deviceSnapshot.current
|
|
271
|
+
? deviceSnapshot.current.changes
|
|
272
|
+
: hasVersions && activeVersionId && versionChanges?.[activeVersionId]
|
|
273
|
+
? versionChanges[activeVersionId]
|
|
274
|
+
: initialChanges;
|
|
206
275
|
useEffect(() => {
|
|
207
276
|
if (!domReady || !sessionIdForHydrate)
|
|
208
277
|
return;
|
|
209
278
|
if (hydratedSessionRef.current === sessionIdForHydrate)
|
|
210
279
|
return;
|
|
211
280
|
hydratedSessionRef.current = sessionIdForHydrate;
|
|
281
|
+
deviceSnapshot.current = null;
|
|
212
282
|
if (!bootChanges?.length)
|
|
213
283
|
return;
|
|
214
284
|
const versionId = hasVersions ? activeVersionId : undefined;
|
|
285
|
+
setSwitching(true);
|
|
286
|
+
switchingRef.current = true;
|
|
215
287
|
void enqueue(async () => {
|
|
216
288
|
const { outcome, idMap } = await changeListRef.current.replaceAll(bootChanges, prevIdsFor(versionId, bootChanges));
|
|
217
289
|
if (versionId) {
|
|
@@ -220,7 +292,12 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
220
292
|
versionCallbacksRef.current.onVersionEnter?.(versionId, idMap);
|
|
221
293
|
}
|
|
222
294
|
onHydrated?.(outcome);
|
|
223
|
-
})
|
|
295
|
+
})
|
|
296
|
+
.catch(e => setCommandError(describeCanvasError(e)))
|
|
297
|
+
.finally(() => {
|
|
298
|
+
switchingRef.current = false;
|
|
299
|
+
setSwitching(false);
|
|
300
|
+
});
|
|
224
301
|
}, [domReady, sessionIdForHydrate, bootChanges]);
|
|
225
302
|
const commandsRef = useRef(commands);
|
|
226
303
|
commandsRef.current = commands;
|
|
@@ -349,7 +426,10 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
349
426
|
tag: self.tag,
|
|
350
427
|
imageTarget: imageTargetOf(selection?.capabilities),
|
|
351
428
|
images: elementImages?.selector === self.selector ? elementImages.images : undefined,
|
|
352
|
-
ancestors: (selection?.chain ?? [])
|
|
429
|
+
ancestors: (selection?.chain ?? [])
|
|
430
|
+
.map(n => n.selector)
|
|
431
|
+
.slice()
|
|
432
|
+
.reverse(),
|
|
353
433
|
outerHtml,
|
|
354
434
|
descendants: descendantShelfRef.current,
|
|
355
435
|
hasChildren: (selection?.capabilities?.childElementCount ?? 0) > 0,
|
|
@@ -426,6 +506,10 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
426
506
|
useEffect(() => {
|
|
427
507
|
anchorsRef.current = anchors;
|
|
428
508
|
}, [anchors]);
|
|
509
|
+
const pageBlocksRef = useRef(pageBlocks);
|
|
510
|
+
useEffect(() => {
|
|
511
|
+
pageBlocksRef.current = pageBlocks;
|
|
512
|
+
}, [pageBlocks]);
|
|
429
513
|
useEffect(() => {
|
|
430
514
|
rootShelfRef.current = [];
|
|
431
515
|
rootShelfAncestorsRef.current = new Map();
|
|
@@ -442,21 +526,14 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
442
526
|
pageContextSessionRef.current = sessionIdForAiContext;
|
|
443
527
|
let stale = false;
|
|
444
528
|
void (async () => {
|
|
445
|
-
const root = await getDomTree(sessionIdForAiContext,
|
|
446
|
-
rootSelector: 'body',
|
|
447
|
-
maxDepth: 16,
|
|
448
|
-
maxChildren: 40,
|
|
449
|
-
maxNodes: 1500,
|
|
450
|
-
visibleOnly: true,
|
|
451
|
-
includeRect: true,
|
|
452
|
-
fields: [...SHELF_LAYOUT_FIELDS]
|
|
453
|
-
});
|
|
529
|
+
const root = await getDomTree(sessionIdForAiContext, ROOT_SHELF_TREE_PARAMS);
|
|
454
530
|
const { html } = await getOuterHtml(sessionIdForAiContext, 'body');
|
|
455
531
|
if (stale)
|
|
456
532
|
return;
|
|
457
533
|
const { shelf, ancestorsOf } = buildRootShelf(root, {
|
|
458
534
|
limit: ROOT_SHELF_LIMIT,
|
|
459
|
-
viewportWidth: geometryRef.current?.viewportWidth
|
|
535
|
+
viewportWidth: geometryRef.current?.viewportWidth,
|
|
536
|
+
blocks: pageBlocksRef.current
|
|
460
537
|
});
|
|
461
538
|
const planAnchors = anchorsRef.current ?? [];
|
|
462
539
|
const anchorOut = planAnchors.length
|
|
@@ -480,7 +557,11 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
480
557
|
: null;
|
|
481
558
|
if (stale)
|
|
482
559
|
return;
|
|
483
|
-
const
|
|
560
|
+
const blockIdBySelector = new Map(shelf.flatMap(r => (r.blockId ? [[r.selector, r.blockId]] : [])));
|
|
561
|
+
const anchorRows = (anchorOut?.shelf ?? []).map(r => {
|
|
562
|
+
const blockId = blockIdBySelector.get(r.selector);
|
|
563
|
+
return blockId && !r.blockId ? { ...r, blockId } : r;
|
|
564
|
+
});
|
|
484
565
|
const anchorSelectors = new Set(anchorRows.map(r => r.selector));
|
|
485
566
|
const mergedShelf = [
|
|
486
567
|
...anchorRows,
|
|
@@ -505,14 +586,22 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
505
586
|
for (const { anchor, selector } of anchorBlocks) {
|
|
506
587
|
try {
|
|
507
588
|
const { html: blockHtml } = await getOuterHtml(sessionIdForAiContext, selector);
|
|
508
|
-
fragments.push({
|
|
589
|
+
fragments.push({
|
|
590
|
+
label: anchor.blockId ?? anchor.label,
|
|
591
|
+
selector,
|
|
592
|
+
html: blockHtml
|
|
593
|
+
});
|
|
509
594
|
}
|
|
510
595
|
catch {
|
|
511
596
|
}
|
|
512
597
|
const backgroundUp = anchor.blockSelectors?.length
|
|
513
598
|
? null
|
|
514
599
|
: backgroundImageAncestorFor(anchor.selector, mergedShelf, mergedAncestors);
|
|
515
|
-
const imageRoots = Array.from(new Set([
|
|
600
|
+
const imageRoots = Array.from(new Set([
|
|
601
|
+
...(anchor.blockSelectors ?? []),
|
|
602
|
+
selector,
|
|
603
|
+
...(backgroundUp ? [backgroundUp] : [])
|
|
604
|
+
])).slice(0, 3);
|
|
516
605
|
const seenUrls = new Set(images.map(i => i.url));
|
|
517
606
|
for (const root of imageRoots) {
|
|
518
607
|
try {
|
|
@@ -572,7 +661,9 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
572
661
|
label: anchor.label,
|
|
573
662
|
selector: shotSelector
|
|
574
663
|
};
|
|
575
|
-
shots.push('url' in shot
|
|
664
|
+
shots.push('url' in shot
|
|
665
|
+
? { ...base, url: shot.url }
|
|
666
|
+
: { ...base, error: shot.error });
|
|
576
667
|
}
|
|
577
668
|
return shots;
|
|
578
669
|
})
|
|
@@ -642,14 +733,23 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
642
733
|
identifier: picked.selector,
|
|
643
734
|
descendants: () => [
|
|
644
735
|
...descendantShelfRef.current.map(d => d.selector),
|
|
736
|
+
...(pageMode ? rootShelfRef.current.map(d => d.selector) : []),
|
|
645
737
|
...aiInsertedRef.current
|
|
646
738
|
],
|
|
647
|
-
ancestors: (selection?.chain ?? []).slice(1).map(n => n.selector)
|
|
739
|
+
ancestors: (selection?.chain ?? []).slice(1).map(n => n.selector),
|
|
740
|
+
...(pageMode
|
|
741
|
+
? {
|
|
742
|
+
ancestorsOf: (selector) => rootShelfAncestorsRef.current.get(selector)
|
|
743
|
+
}
|
|
744
|
+
: {})
|
|
648
745
|
}
|
|
649
746
|
: {
|
|
650
747
|
selector: 'body',
|
|
651
748
|
identifier: 'body',
|
|
652
|
-
descendants: () => [
|
|
749
|
+
descendants: () => [
|
|
750
|
+
...rootShelfRef.current.map(d => d.selector),
|
|
751
|
+
...aiInsertedRef.current
|
|
752
|
+
],
|
|
653
753
|
ancestors: [],
|
|
654
754
|
ancestorsOf: (selector) => rootShelfAncestorsRef.current.get(selector)
|
|
655
755
|
};
|
|
@@ -683,7 +783,11 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
683
783
|
scope: opts?.grant ? { ...scope, grant: opts.grant } : scope,
|
|
684
784
|
resolve: async (selector) => {
|
|
685
785
|
const r = await resolveCanvasSelector(sessionIdForAiContext, selector);
|
|
686
|
-
return {
|
|
786
|
+
return {
|
|
787
|
+
matched: r.matched,
|
|
788
|
+
matchCount: r.matchCount,
|
|
789
|
+
viewportRect: r.viewportRect
|
|
790
|
+
};
|
|
687
791
|
},
|
|
688
792
|
insertedSelectorsOf: (changeId) => changeListRef.current.peekList().find(r => r.changeId === changeId)
|
|
689
793
|
?.insertedSelectors ?? [],
|
|
@@ -783,11 +887,40 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
783
887
|
return;
|
|
784
888
|
}
|
|
785
889
|
if (buttonId === 'replace-image') {
|
|
786
|
-
|
|
890
|
+
void Promise.all([
|
|
891
|
+
getOuterHtml(sessionId, self.selector),
|
|
892
|
+
selection?.capabilities?.hasBackgroundImage && !selection?.capabilities?.isImg
|
|
893
|
+
? queryElements(sessionId, [self.selector], {
|
|
894
|
+
fields: ['computed:background-image']
|
|
895
|
+
})
|
|
896
|
+
: Promise.resolve(null)
|
|
897
|
+
])
|
|
898
|
+
.then(([{ html }, facts]) => {
|
|
899
|
+
const node = new DOMParser().parseFromString(html, 'text/html').body
|
|
900
|
+
.firstElementChild;
|
|
901
|
+
const background = facts?.results[0]?.matches[0]?.computed?.['background-image'] ?? '';
|
|
902
|
+
const url = /^url\(["']?(.*?)["']?\)$/.exec(background)?.[1] ?? '';
|
|
903
|
+
setImageDialog({
|
|
904
|
+
open: true,
|
|
905
|
+
seed: facts ? url : (node?.getAttribute('src') ?? '')
|
|
906
|
+
});
|
|
907
|
+
})
|
|
908
|
+
.catch(error => setCommandError(describeCanvasError(error)));
|
|
787
909
|
return;
|
|
788
910
|
}
|
|
789
911
|
if (buttonId === 'replace-video') {
|
|
790
|
-
|
|
912
|
+
void getOuterHtml(sessionId, self.selector)
|
|
913
|
+
.then(({ html }) => {
|
|
914
|
+
const node = new DOMParser().parseFromString(html, 'text/html').body
|
|
915
|
+
.firstElementChild;
|
|
916
|
+
setVideoDialog({
|
|
917
|
+
open: true,
|
|
918
|
+
seed: node?.getAttribute('src') ??
|
|
919
|
+
node?.querySelector('source')?.getAttribute('src') ??
|
|
920
|
+
''
|
|
921
|
+
});
|
|
922
|
+
})
|
|
923
|
+
.catch(error => setCommandError(describeCanvasError(error)));
|
|
791
924
|
return;
|
|
792
925
|
}
|
|
793
926
|
if (buttonId === 'insert') {
|
|
@@ -795,11 +928,29 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
795
928
|
return;
|
|
796
929
|
}
|
|
797
930
|
if (buttonId === 'image-setting') {
|
|
798
|
-
|
|
931
|
+
void getOuterHtml(sessionId, self.selector)
|
|
932
|
+
.then(({ html }) => {
|
|
933
|
+
const node = new DOMParser().parseFromString(html, 'text/html').body
|
|
934
|
+
.firstElementChild;
|
|
935
|
+
setImageSettingDialog({
|
|
936
|
+
open: true,
|
|
937
|
+
seed: node?.getAttribute('alt') ?? ''
|
|
938
|
+
});
|
|
939
|
+
})
|
|
940
|
+
.catch(error => setCommandError(describeCanvasError(error)));
|
|
799
941
|
return;
|
|
800
942
|
}
|
|
801
943
|
if (buttonId === 'click-action') {
|
|
802
|
-
|
|
944
|
+
const existing = [...changeListRef.current.peekList()]
|
|
945
|
+
.reverse()
|
|
946
|
+
.find(r => r.change.type === 'changeElementAction' &&
|
|
947
|
+
r.change.selector === self.selector);
|
|
948
|
+
setActionDialog({
|
|
949
|
+
open: true,
|
|
950
|
+
seed: existing?.change.type === 'changeElementAction'
|
|
951
|
+
? existing.change.action
|
|
952
|
+
: null
|
|
953
|
+
});
|
|
803
954
|
return;
|
|
804
955
|
}
|
|
805
956
|
if (buttonId === 'edit-style') {
|
|
@@ -822,15 +973,10 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
822
973
|
if (buttonId === 'screenshot') {
|
|
823
974
|
setCommandError(null);
|
|
824
975
|
setShot(null);
|
|
825
|
-
setShotUrl(null);
|
|
826
976
|
commands
|
|
827
977
|
.captureSelectedElement(self.selector)
|
|
828
978
|
.then(async (outcome) => {
|
|
829
979
|
setShot(outcome);
|
|
830
|
-
if (!uploadImage)
|
|
831
|
-
return;
|
|
832
|
-
const blob = await (await fetch(outcome.dataUrl)).blob();
|
|
833
|
-
setShotUrl(await uploadImage(blob));
|
|
834
980
|
})
|
|
835
981
|
.catch(error => setCommandError(describeCanvasError(error)));
|
|
836
982
|
return;
|
|
@@ -844,17 +990,16 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
844
990
|
}
|
|
845
991
|
}, [clipboard, commands, depsOf, frameProps?.sessionId, selection, t, uploadImage]);
|
|
846
992
|
const self = selection?.chain[0];
|
|
847
|
-
return (_jsxs("div", { className: `${CANVAS_UI_SCOPE} flex h-full min-h-0 flex-col bg-[#F5F7FC]`, children: [_jsx(EditorChrome, { variant: chrome, title: title, versionLabel: versionLabel, versions: versions, activeVersionId: activeVersionId, versionSwitching: switching, onVersionChange: onVersionChange, changeCount: changeList.list.length, onExit: onExit, onSave: () => {
|
|
993
|
+
return (_jsxs("div", { className: `${CANVAS_UI_SCOPE} flex h-full min-h-0 flex-col bg-[#F5F7FC]`, children: [_jsx(EditorChrome, { slotRight: capturing ? (_jsx("span", { role: "status", className: "text-xs text-muted-foreground", children: t('inlineCanvas.review.generating', '正在生成截图…') })) : undefined, variant: chrome, title: title, versionLabel: versionLabel, versions: versions, activeVersionId: activeVersionId, versionSwitching: switching, onVersionChange: onVersionChange, canUndo: !switching && textEditing === null && changeList.canUndo, canRedo: !switching && textEditing === null && changeList.canRedo, onUndo: () => uiWrite(() => enqueue(() => changeListRef.current.undo())), onRedo: () => uiWrite(() => enqueue(() => changeListRef.current.redo())), changeCount: changeList.list.length, onExit: onExit, onSave: () => {
|
|
848
994
|
if (switchingRef.current) {
|
|
849
995
|
setCommandError(t('inlineCanvas.version.switching', '正在切换版本,请稍候'));
|
|
850
996
|
return;
|
|
851
997
|
}
|
|
852
998
|
onSave?.(editor);
|
|
853
|
-
}, onAddVersion: onAddVersion, onRenameVersion: onRenameVersion, onPreview: onPreview, onToggleChangeList: () => setShowChangeList(v => !v), changeListOpen: showChangeList, onToggleAi: onToggleAi, aiOpen: aiOpen }), _jsxs("div", { className: "relative flex min-h-0 flex-1", children: [_jsxs(ResizablePanelGroup, { direction: "horizontal", autoSaveId: "inline-editor-panes-v2", className: "flex min-h-0 w-full flex-1", children: [showLeftPanel && (_jsxs(_Fragment, { children: [_jsx(ResizablePanel, { id: "left", order: 1, defaultSize: 18, minSize: 12, maxSize: 40, className: "flex min-h-0 flex-col", children: _jsxs("aside", { "data-testid": "inline-editor-left-panel", className: "inline-editor-left flex h-full min-h-0 flex-col bg-white", children: [!!libraryComponents && (_jsx("div", { className: "flex h-9 shrink-0 items-center gap-1 border-b\n border-[#EBECF0] px-2", children: [
|
|
999
|
+
}, onAddVersion: onAddVersion, onRenameVersion: onRenameVersion, removableVersionIds: removableVersionIds, onRemoveVersion: onRemoveVersion, onPreview: onPreview, onToggleChangeList: () => setShowChangeList(v => !v), changeListOpen: showChangeList, onToggleAi: onToggleAi, aiOpen: aiOpen }), _jsxs("div", { className: "relative flex min-h-0 flex-1", children: [_jsxs(ResizablePanelGroup, { direction: "horizontal", autoSaveId: "inline-editor-panes-v2", className: "flex min-h-0 w-full flex-1", children: [showLeftPanel && (_jsxs(_Fragment, { children: [_jsx(ResizablePanel, { id: "left", order: 1, defaultSize: 18, minSize: 12, maxSize: 40, className: "flex min-h-0 flex-col", children: _jsxs("aside", { "data-testid": "inline-editor-left-panel", className: "inline-editor-left flex h-full min-h-0 flex-col bg-white", children: [!!libraryComponents && (_jsx("div", { className: "flex h-9 shrink-0 items-center gap-1 border-b\n border-[#EBECF0] px-2", children: [
|
|
854
1000
|
['layers', '图层'],
|
|
855
1001
|
['assets', '组件']
|
|
856
|
-
]
|
|
857
|
-
.map(([tab, label]) => (_jsx("button", { type: "button", "data-testid": `left-tab-${tab}`, "aria-pressed": leftTab === tab, onClick: () => setLeftTab(tab), className: `rounded px-2 py-1 text-[12px] ${leftTab === tab
|
|
1002
|
+
].map(([tab, label]) => (_jsx("button", { type: "button", "data-testid": `left-tab-${tab}`, "aria-pressed": leftTab === tab, onClick: () => setLeftTab(tab), className: `rounded px-2 py-1 text-[12px] ${leftTab === tab
|
|
858
1003
|
? 'bg-[#F4F5F7] font-semibold text-[#091E42]'
|
|
859
1004
|
: 'text-[#6B778C]'}`, children: label }, tab))) })), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden", children: leftTab === 'assets' && libraryComponents ? (_jsx(AssetsPanel, { components: libraryComponents, loading: libraryLoading, canInsert: !!selection?.chain[0] && !switching, onInsert: component => void insertComponent(component) })) : (_jsx(LayersPanel, { tree: tree, selectedSelector: selection?.selector ?? null, onSelect: s => void selectAndReveal(s), onHover: s => void commands.hover(s), onExpand: id => void commands.expandTreeNode(id), onMove: (dragSelector, targetSelector, pos) => uiWrite(() => writes.addMove({
|
|
860
1005
|
identifier: dragSelector,
|
|
@@ -862,25 +1007,54 @@ export function InlineEditor({ url, device, onDeviceChange, title, versionLabel
|
|
|
862
1007
|
targetIdentifier: targetSelector,
|
|
863
1008
|
targetSelector,
|
|
864
1009
|
mode: moveModeFromDropPos(pos)
|
|
865
|
-
})) })) })] }) }), _jsx(ResizableHandle, { withHandle: true })] })), _jsx(ResizablePanel, { id: "canvas", order: 2, minSize: 30, children: _jsxs("div", { className: "flex h-full min-h-0 min-w-0 flex-col", children: [_jsx(CanvasContextBar, { url: url, device: device, onDeviceChange:
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
1010
|
+
})) })) })] }) }), _jsx(ResizableHandle, { withHandle: true })] })), _jsx(ResizablePanel, { id: "canvas", order: 2, minSize: 30, children: _jsxs("div", { className: "flex h-full min-h-0 min-w-0 flex-col", children: [_jsx(CanvasContextBar, { url: url, device: device, onDeviceChange: changeDevice }), _jsxs("div", { ref: canvasAreaRef, className: "relative min-h-0 flex-1 overflow-hidden p-3", children: [editor.session.state === 'failed' && (_jsxs("div", { role: "alert", "data-testid": "inline-editor-session-failed", className: "absolute inset-3 z-20 flex flex-col items-center justify-center gap-3 rounded-lg bg-white p-6 text-center text-sm", children: [_jsx("p", { className: "font-semibold", children: t('inlineCanvas.review.pageLoadFailed', '目标页面无法加载') }), _jsx("p", { children: [
|
|
1011
|
+
'NO_EXTENSION',
|
|
1012
|
+
'EXTENSION_ID_MISSING',
|
|
1013
|
+
'CONTRACT_MISMATCH'
|
|
1014
|
+
].includes(editor.session.reason ?? '')
|
|
1015
|
+
? describeCanvasError(editor.session.reason)
|
|
1016
|
+
: t('inlineCanvas.review.pageLoadHint', '请返回检查页面地址、网站访问权限和网络连接后重试。') }), _jsx("code", { className: "text-xs text-muted-foreground", children: editor.session.reason }), _jsx(Button, { variant: "secondary", onClick: onExit, children: t('inlineCanvas.review.backFromFailed', '返回修改') })] })), frameProps && (_jsx(CanvasFrame, { url: frameProps.url, sessionId: frameProps.sessionId, device: frameProps.device, panelWidth: panelWidth, targetViewportWidth: targetViewportWidth[frameProps.device], onFrameLoad: () => { }, domReady: domReady, iframeRef: canvasIframeRef }, frameProps.sessionId))] })] }) })] }), !capturing && shot && (_jsx(ScreenshotCard, { dataUrl: shot.dataUrl, truncated: shot.truncated, scale: shot.scale, uploadImage: uploadImage, onClose: () => {
|
|
1017
|
+
setShot(null);
|
|
1018
|
+
} }, shot.dataUrl)), _jsx(ChangeListPopover, { open: showChangeList && !capturing, onClose: () => setShowChangeList(false), children: _jsx(ChangeListPanel, { list: changeList.list, onRevert: id => uiWrite(() => writes.revert(id)), onRevertAll: () => uiWrite(() => writes.revertAll()), lastError: changeList.lastError, onHover: s => void commands.hover(s), onSelect: s => void selectAndReveal(s) }) })] }), !capturing && (_jsx(SelectionOverlay, { geometry: geometry.latest, context: overlayContext, selectedLabel: self ? `<${self.tag}>` : undefined })), !capturing && blockOverlay && (_jsx(BlockOverlay, { blocks: blockOverlay.blocks, activeId: blockOverlay.activeId, onHover: blockOverlay.onHover, geometry: geometry.latest, context: overlayContext, scale: editor.mobileFit.scale })), !capturing && commandError && (_jsxs(EditorStatus, { tone: "error", "data-testid": "inline-editor-command-error", style: { left: `${area.x + area.width / 2}px`, top: `${area.y + 8}px` }, children: [_jsx("span", { children: commandError }), _jsx(Button, { size: "sm", variant: "secondary", onClick: () => setCommandError(null), children: t('inlineCanvas.chrome.close', '关闭') })] })), !capturing && aiApplyNote && (_jsxs(EditorStatus, { "data-testid": "inline-editor-ai-apply-note", style: { left: `${area.x + area.width / 2}px`, top: `${area.y + 8}px` }, children: [_jsx("span", { role: "status", children: aiApplyNote }), _jsx(Button, { size: "sm", variant: "secondary", onClick: () => setAiApplyNote(null), children: t('inlineCanvas.chrome.close', '关闭') })] })), !capturing && move && (_jsx("div", { role: "toolbar", "aria-label": t('inlineCanvas.move.title'), "data-testid": "inline-editor-move-bar", className: "fixed z-[903] -translate-x-1/2 rounded-lg border bg-popover p-3 text-xs text-popover-foreground shadow-md", style: {
|
|
1019
|
+
width: 'min(600px, calc(100vw - 32px))',
|
|
1020
|
+
left: `clamp(min(316px, 50vw), ${area.x + area.width / 2}px, max(calc(100vw - 316px), 50vw))`,
|
|
1021
|
+
top: `${area.y + 8}px`
|
|
1022
|
+
}, children: move.target === null ? (_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("span", { children: move.rejected === 'same-element'
|
|
869
1023
|
? '不能移到它自己里面,请另选一个目标'
|
|
870
|
-
: t('inlineCanvas.move.pickTarget') })
|
|
871
|
-
.confirmMove(pos)
|
|
872
|
-
.catch(e => setCommandError(describeCanvasError(e))), children: t(`inlineCanvas.move.${pos}`) }, pos)))] })), _jsx("button", { type: "button", "data-testid": "move-cancel", className: "ml-1 text-white/70 underline", onClick: () => commands.cancelMove(), children: t('inlineCanvas.move.cancel') })] })), !capturing && textEditing !== null && (_jsx("div", { "data-testid": "inline-editor-text-editing-hint", className: "pointer-events-none fixed z-[2147483003] -translate-x-1/2 rounded\n bg-[#091E42] px-2.5 py-1 text-[11px] leading-4 text-white shadow", style: {
|
|
1024
|
+
: t('inlineCanvas.move.pickTarget') }), _jsx(Button, { size: "default", variant: "secondary", type: "button", "data-testid": "move-cancel", onClick: () => commands.cancelMove(), children: t('inlineCanvas.move.cancel') })] })) : (_jsx(MovePositionControls, { onConfirm: commands.confirmMove, onCancel: commands.cancelMove, onError: e => setCommandError(describeCanvasError(e)) }, move.target)) })), !capturing && textEditing !== null && (_jsx(EditorStatus, { "data-testid": "inline-editor-text-editing-hint", style: {
|
|
873
1025
|
left: `${area.x + area.width / 2}px`,
|
|
874
1026
|
top: `${area.y + 8}px`
|
|
875
|
-
}, children: "\u6B63\u5728\u7F16\u8F91\u6587\u5B57 \u00B7 \u56DE\u8F66\u63D0\u4EA4 \u00B7 Esc \u53D6\u6D88" })), !capturing && !switching && (_jsx(SelectionToolbar, { selectedRect: selectedRect, area: area, capabilities: selection?.capabilities ?? null, isScrolling: geometry.isScrolling, onAction: onToolbarAction, host: { askAi: !!onAskAi } })), stylePanelOpen &&
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
1027
|
+
}, children: "\u6B63\u5728\u7F16\u8F91\u6587\u5B57 \u00B7 \u56DE\u8F66\u63D0\u4EA4 \u00B7 Esc \u53D6\u6D88" })), !capturing && !switching && !move && textEditing === null && (_jsx(SelectionToolbar, { selectedRect: selectedRect, area: area, capabilities: selection?.capabilities ?? null, isScrolling: geometry.isScrolling, onAction: onToolbarAction, host: { askAi: !!onAskAi } })), stylePanelOpen &&
|
|
1028
|
+
!switching &&
|
|
1029
|
+
selectedRect &&
|
|
1030
|
+
self &&
|
|
1031
|
+
createPortal(_jsx("div", { className: `${CANVAS_UI_SCOPE} fixed z-[1000]`, children: _jsx(StylePanel, { anchor: {
|
|
1032
|
+
x: Math.min(selectedRect.x + selectedRect.width + 12, area.x + area.width - 332),
|
|
1033
|
+
y: Math.max(area.y + 8, selectedRect.y)
|
|
1034
|
+
}, tag: self.tag, capabilities: selection?.capabilities ?? null, resettableProperties: changeList.list.flatMap(r => r.change.type === 'elementStyle' &&
|
|
1035
|
+
r.change.selector === self.selector
|
|
1036
|
+
? [r.change.property]
|
|
1037
|
+
: []), declaredStyles: Object.assign({}, currentStyles, ...changeList.list
|
|
1038
|
+
.filter(r => r.change.type === 'elementStyle' &&
|
|
1039
|
+
r.change.selector === self.selector)
|
|
1040
|
+
.map(r => r.change.type === 'elementStyle'
|
|
1041
|
+
? { [r.change.property]: r.change.value }
|
|
1042
|
+
: {})), onReset: property => {
|
|
1043
|
+
void uiWrite(async () => {
|
|
1044
|
+
for (const record of changeList
|
|
1045
|
+
.peekList()
|
|
1046
|
+
.filter(r => r.change.type === 'elementStyle' &&
|
|
1047
|
+
r.change.selector === self.selector &&
|
|
1048
|
+
r.change.property === property)
|
|
1049
|
+
.reverse())
|
|
1050
|
+
await writes.revert(record.changeId);
|
|
1051
|
+
});
|
|
1052
|
+
}, onClose: () => setStylePanelOpen(false), onApply: (property, value) => uiWrite(() => editStyle(depsOf(self), property, value)) }, self.selector) }), document.body), _jsx(Dialog, { open: pasteDialog, onOpenChange: setPasteDialog, children: _jsxs(DialogContent, { className: "max-w-[480px]", "data-testid": "paste-dialog", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: t('inlineCanvas.clipboard.pastePosition') }) }), _jsx("div", { className: "flex flex-wrap gap-1.5", children: (selection?.capabilities ? pasteModes(selection.capabilities) : []).map(mode => (_jsxs(Button, { variant: "secondary", type: "button", "data-testid": `paste-mode-${mode}`, onClick: () => {
|
|
879
1053
|
if (!self)
|
|
880
1054
|
return;
|
|
881
1055
|
setPasteDialog(false);
|
|
882
1056
|
void commands
|
|
883
1057
|
.pasteInto(self.selector, mode)
|
|
884
1058
|
.catch(e => setCommandError(describeCanvasError(e)));
|
|
885
|
-
}, children: t(`inlineCanvas.clipboard.${mode}`) }, mode))) })] }) }), _jsx(ImageEditDialog, { open: imageDialog.open, capabilities: selection?.capabilities ?? null, seed: imageDialog.seed, uploadImage: uploadImage, onOpenChange: open => setImageDialog(prev => ({ ...prev, open })), onApply: (input) => self && uiWrite(() => replaceImage(depsOf(self), input)) }), _jsx(ReplaceVideoDialog, { open: videoDialog.open, seed: videoDialog.seed, onOpenChange: open => setVideoDialog(prev => ({ ...prev, open })), onApply: url => self && uiWrite(() => replaceVideo(depsOf(self), url)) }), _jsx(InsertElementDialog, { open: insertDialog, capabilities: selection?.capabilities ?? null, onOpenChange: setInsertDialog, onApply: input => self && uiWrite(() => insertElement(depsOf(self), input)) }), _jsx(ImageSettingDialog, { open: imageSettingDialog.open, seed: imageSettingDialog.seed, onOpenChange: open => setImageSettingDialog(prev => ({ ...prev, open })), onApply: alt => self && uiWrite(() => setImageAlt(depsOf(self), alt)) }), _jsx(ClickActionDialog, { open: actionDialog.open, seed: actionDialog.seed, onOpenChange: open => setActionDialog(prev => ({ ...prev, open })), onApply: action => self && uiWrite(() => setClickAction(depsOf(self), action)) }), _jsx(HtmlEditDialog, { open: htmlDialog.open, seed: htmlDialog.seed, onOpenChange: open => setHtmlDialog(prev => ({ ...prev, open })), onApply: html => self && uiWrite(() => editHtml(depsOf(self), html)) })] }));
|
|
1059
|
+
}, children: [t('inlineCanvas.clipboard.paste', '粘贴'), " \u00B7", ' ', t(`inlineCanvas.clipboard.${mode}`)] }, mode))) })] }) }), _jsx(ImageEditDialog, { open: imageDialog.open, capabilities: selection?.capabilities ?? null, seed: imageDialog.seed, uploadImage: uploadImage, onOpenChange: open => setImageDialog(prev => ({ ...prev, open })), onApply: (input) => self && uiWrite(() => replaceImage(depsOf(self), input)) }), _jsx(ReplaceVideoDialog, { open: videoDialog.open, seed: videoDialog.seed, onOpenChange: open => setVideoDialog(prev => ({ ...prev, open })), onApply: url => self && uiWrite(() => replaceVideo(depsOf(self), url)) }), _jsx(InsertElementDialog, { open: insertDialog, capabilities: selection?.capabilities ?? null, onOpenChange: setInsertDialog, onApply: input => self && uiWrite(() => insertElement(depsOf(self), input)) }), _jsx(ImageSettingDialog, { open: imageSettingDialog.open, seed: imageSettingDialog.seed, onOpenChange: open => setImageSettingDialog(prev => ({ ...prev, open })), onApply: alt => self && uiWrite(() => setImageAlt(depsOf(self), alt)) }), _jsx(ClickActionDialog, { open: actionDialog.open, seed: actionDialog.seed, onOpenChange: open => setActionDialog(prev => ({ ...prev, open })), onApply: action => self && uiWrite(() => setClickAction(depsOf(self), action)) }), _jsx(HtmlEditDialog, { open: htmlDialog.open, seed: htmlDialog.seed, onOpenChange: open => setHtmlDialog(prev => ({ ...prev, open })), onApply: html => self && uiWrite(() => editHtml(depsOf(self), html)) })] }));
|
|
886
1060
|
}
|
|
@@ -26,6 +26,7 @@ export interface TreePanelNode extends TreeNode {
|
|
|
26
26
|
truncated?: boolean;
|
|
27
27
|
}
|
|
28
28
|
export interface TreePanelProps {
|
|
29
|
+
emptyLabel?: string;
|
|
29
30
|
nodes: Record<string, TreePanelNode>;
|
|
30
31
|
rootId: string;
|
|
31
32
|
selectedId: string | null;
|
|
@@ -44,4 +45,4 @@ export interface TreePanelProps {
|
|
|
44
45
|
*/
|
|
45
46
|
onExpand?: (id: string) => void;
|
|
46
47
|
}
|
|
47
|
-
export declare function TreePanel({ nodes, rootId, selectedId, hoverId, query, onSelect, onShiftSelect, onHover, onMove, onExpand }: TreePanelProps): React.JSX.Element;
|
|
48
|
+
export declare function TreePanel({ emptyLabel, nodes, rootId, selectedId, hoverId, query, onSelect, onShiftSelect, onHover, onMove, onExpand }: TreePanelProps): React.JSX.Element;
|