@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.
Files changed (52) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/dist/InlineEditor.d.ts +15 -1
  3. package/dist/InlineEditor.js +226 -52
  4. package/dist/canvas-ui/tree/TreePanel.d.ts +2 -1
  5. package/dist/canvas-ui/tree/TreePanel.js +79 -39
  6. package/dist/chrome/EditorChrome.d.ts +2 -0
  7. package/dist/chrome/EditorChrome.js +1 -1
  8. package/dist/chrome/VersionMenu.d.ts +3 -1
  9. package/dist/chrome/VersionMenu.js +24 -12
  10. package/dist/chrome/topbarPrimitives.d.ts +5 -52
  11. package/dist/chrome/topbarPrimitives.js +13 -31
  12. package/dist/dialogs/ClickActionDialog.js +4 -5
  13. package/dist/dialogs/CodeField.d.ts +4 -0
  14. package/dist/dialogs/CodeField.js +10 -0
  15. package/dist/dialogs/HtmlEditDialog.js +3 -2
  16. package/dist/dialogs/ImageEditDialog.js +53 -15
  17. package/dist/dialogs/ImageSettingDialog.js +3 -3
  18. package/dist/dialogs/InsertElementDialog.js +10 -6
  19. package/dist/dialogs/MediaPreview.d.ts +5 -0
  20. package/dist/dialogs/MediaPreview.js +34 -0
  21. package/dist/dialogs/ReplaceVideoDialog.js +8 -4
  22. package/dist/index.d.ts +1 -0
  23. package/dist/kernel/ai/aiInlineApply.d.ts +4 -3
  24. package/dist/kernel/blocks/BlockOverlay.js +1 -1
  25. package/dist/kernel/blocks/resolveBlocks.d.ts +16 -0
  26. package/dist/kernel/blocks/resolveBlocks.js +39 -6
  27. package/dist/kernel/changelist/ChangeListPanel.js +11 -9
  28. package/dist/kernel/changelist/useChangeList.d.ts +5 -0
  29. package/dist/kernel/changelist/useChangeList.js +85 -10
  30. package/dist/kernel/selection/SelectionOverlay.js +1 -1
  31. package/dist/kernel/selection/SelectionToolbar.js +26 -8
  32. package/dist/kernel/selection/buildRootShelf.d.ts +56 -2
  33. package/dist/kernel/selection/buildRootShelf.js +189 -41
  34. package/dist/kernel/selection/elementAiContext.d.ts +11 -3
  35. package/dist/kernel/selection/resolveAnchors.d.ts +17 -0
  36. package/dist/kernel/selection/resolveAnchors.js +59 -11
  37. package/dist/locales.d.ts +201 -48
  38. package/dist/locales.js +13 -9
  39. package/dist/panels/EditorStatus.d.ts +5 -0
  40. package/dist/panels/EditorStatus.js +4 -0
  41. package/dist/panels/MovePositionControls.d.ts +6 -0
  42. package/dist/panels/MovePositionControls.js +35 -0
  43. package/dist/panels/ScreenshotCard.d.ts +7 -0
  44. package/dist/panels/ScreenshotCard.js +57 -0
  45. package/dist/panels/StylePanel.d.ts +9 -1
  46. package/dist/panels/StylePanel.js +108 -12
  47. package/dist/reviewLocales.d.ts +149 -0
  48. package/dist/reviewLocales.js +143 -0
  49. package/dist/tree/LayersPanel.js +4 -2
  50. package/dist/useInlineEditor.d.ts +1 -0
  51. package/dist/useInlineEditor.js +40 -1
  52. package/package.json +2 -2
@@ -0,0 +1,143 @@
1
+ export const reviewLocales = {
2
+ 'zh-CN': {
3
+ pageLoadFailed: '目标页面无法加载',
4
+ pageLoadHint: '请返回检查页面地址、网站访问权限和网络连接后重试。',
5
+ backFromFailed: '返回修改',
6
+ generating: '正在生成截图…',
7
+ noLayers: '没有匹配的图层', clearSearch: '清除搜索', searchLayers: '搜索图层',
8
+ linkCopied: '链接已复制',
9
+ copyLinkManual: '复制失败,请从下方手动复制链接',
10
+ uploadError: '上传失败,可重试;本地图片仍可下载',
11
+ screenshot: '元素截图',
12
+ enlarge: '放大预览',
13
+ shotTruncated: '元素超出可视区,只截到可见部分',
14
+ shotScale: '截图分辨率约为原图的 {{scale}}%',
15
+ downloadImage: '下载图片',
16
+ imageCopied: '图片已复制',
17
+ copyError: '复制失败,请下载图片',
18
+ copyImage: '复制图片',
19
+ uploading: '正在上传…',
20
+ copyLink: '复制链接',
21
+ shotLink: '截图链接',
22
+ reset: '清除覆盖',
23
+ colorSwatch: '选择颜色',
24
+ saveDraft: '保存草稿',
25
+ versions: '版本管理',
26
+ removeEmptyVersion: '移除空版本',
27
+ versionLimit: '最多 4 个版本',
28
+ imageLimit: '请选择不超过 10 MB 的图片',
29
+ currentMedia: '当前媒体',
30
+ uploadFile: '上传文件',
31
+ useLink: '使用链接',
32
+ replaceImage: '替换当前图片',
33
+ replaceBackground: '替换背景图片',
34
+ insertImage: '插入新图片',
35
+ dropImage: '拖入图片或选择文件,支持图片格式,最大 10 MB。上传只准备素材,应用后才修改画布。',
36
+ pendingMedia: '待应用媒体',
37
+ altHelp: '替代文本用于描述图片内容。装饰图片可留空。',
38
+ videoLinkHint: '支持视频文件链接和 YouTube 嵌入链接。加载就绪后应用到画布。',
39
+ mediaPreview: '媒体预览',
40
+ loading: '正在加载预览…',
41
+ loadError: '预览加载失败',
42
+ retry: '重试',
43
+ ready: '媒体已就绪,应用后修改画布',
44
+ align_left: '左对齐',
45
+ align_center: '居中',
46
+ align_right: '右对齐',
47
+ align_justify: '两端对齐'
48
+ },
49
+ 'en-US': {
50
+ pageLoadFailed: 'Unable to load the target page',
51
+ pageLoadHint: 'Go back to check the page URL, site permissions and network connection, then retry.',
52
+ backFromFailed: 'Go back',
53
+ generating: 'Generating screenshot…',
54
+ noLayers: 'No matching layers', clearSearch: 'Clear search', searchLayers: 'Search layers',
55
+ linkCopied: 'Link copied',
56
+ copyLinkManual: 'Copy failed. Copy the link below manually.',
57
+ uploadError: 'Upload failed. Retry or download the local image.',
58
+ screenshot: 'Element screenshot',
59
+ enlarge: 'Enlarge preview',
60
+ shotTruncated: 'Only the visible part of the element was captured.',
61
+ shotScale: 'Screenshot resolution is about {{scale}}% of the original.',
62
+ downloadImage: 'Download image',
63
+ imageCopied: 'Image copied',
64
+ copyError: 'Copy failed. Download the image instead.',
65
+ copyImage: 'Copy image',
66
+ uploading: 'Uploading…',
67
+ copyLink: 'Copy link',
68
+ shotLink: 'Screenshot link',
69
+ reset: 'Clear override',
70
+ colorSwatch: 'Choose color',
71
+ saveDraft: 'Save draft',
72
+ versions: 'Manage versions',
73
+ removeEmptyVersion: 'Remove empty version',
74
+ versionLimit: 'Maximum 4 versions',
75
+ imageLimit: 'Choose an image up to 10 MB.',
76
+ currentMedia: 'Current media',
77
+ uploadFile: 'Upload file',
78
+ useLink: 'Use link',
79
+ replaceImage: 'Replace current image',
80
+ replaceBackground: 'Replace background image',
81
+ insertImage: 'Insert new image',
82
+ dropImage: 'Drop an image or choose a file, up to 10 MB. Upload prepares the asset; Apply changes the canvas.',
83
+ pendingMedia: 'Media to apply',
84
+ altHelp: 'Describe the image for assistive technology. Leave blank for decorative images.',
85
+ videoLinkHint: 'Use a video file URL or YouTube embed link. Apply after the preview is ready.',
86
+ mediaPreview: 'Media preview',
87
+ loading: 'Loading preview…',
88
+ loadError: 'Preview failed to load',
89
+ retry: 'Retry',
90
+ ready: 'Ready. Apply to update the canvas.',
91
+ align_left: 'Align left',
92
+ align_center: 'Center',
93
+ align_right: 'Align right',
94
+ align_justify: 'Justify'
95
+ },
96
+ 'ja-JP': {
97
+ pageLoadFailed: '対象ページを読み込めません',
98
+ pageLoadHint: '戻ってページ URL、サイトへのアクセス権限、ネットワーク接続を確認して再試行してください。',
99
+ backFromFailed: '戻って修正',
100
+ generating: 'スクリーンショットを生成中…',
101
+ noLayers: '一致するレイヤーがありません', clearSearch: '検索をクリア', searchLayers: 'レイヤーを検索',
102
+ linkCopied: 'リンクをコピーしました',
103
+ copyLinkManual: 'コピーできませんでした。下のリンクを手動でコピーしてください。',
104
+ uploadError: 'アップロードできませんでした。再試行または画像をダウンロードしてください。',
105
+ screenshot: '要素のスクリーンショット',
106
+ enlarge: 'プレビューを拡大',
107
+ shotTruncated: '表示されている部分のみ撮影しました。',
108
+ shotScale: '解像度は元の約 {{scale}}% です。',
109
+ downloadImage: '画像をダウンロード',
110
+ imageCopied: '画像をコピーしました',
111
+ copyError: 'コピーできませんでした。画像をダウンロードしてください。',
112
+ copyImage: '画像をコピー',
113
+ uploading: 'アップロード中…',
114
+ copyLink: 'リンクをコピー',
115
+ shotLink: 'スクリーンショットのリンク',
116
+ reset: '上書きを解除',
117
+ colorSwatch: '色を選択',
118
+ saveDraft: '下書きを保存',
119
+ versions: 'バージョン管理',
120
+ removeEmptyVersion: '空のバージョンを削除',
121
+ versionLimit: '最大4バージョン',
122
+ imageLimit: '10 MB 以下の画像を選択してください。',
123
+ currentMedia: '現在のメディア',
124
+ uploadFile: 'ファイルをアップロード',
125
+ useLink: 'リンクを使用',
126
+ replaceImage: '現在の画像を置き換え',
127
+ replaceBackground: '背景画像を置き換え',
128
+ insertImage: '新しい画像を挿入',
129
+ dropImage: '画像をドロップするか選択してください(最大10 MB)。アップロードで素材を準備し、適用でキャンバスを変更します。',
130
+ pendingMedia: '適用するメディア',
131
+ altHelp: '画像の内容を説明してください。装飾用の画像は空欄にできます。',
132
+ videoLinkHint: '動画ファイルのURLまたはYouTube埋め込みリンクを入力し、読み込み後に適用してください。',
133
+ mediaPreview: 'メディアプレビュー',
134
+ loading: 'プレビューを読み込み中…',
135
+ loadError: 'プレビューを読み込めませんでした',
136
+ retry: '再試行',
137
+ ready: '準備完了。適用するとキャンバスを変更します。',
138
+ align_left: '左揃え',
139
+ align_center: '中央揃え',
140
+ align_right: '右揃え',
141
+ align_justify: '両端揃え'
142
+ }
143
+ };
@@ -1,15 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useTranslation } from 'react-i18next';
2
3
  import { useState } from 'react';
3
- import { Input } from '@ptengine/design-components';
4
+ import { SearchInput } from '@ptengine/design-components';
4
5
  import { TreePanel } from '../canvas-ui/index.js';
5
6
  import { findIdBySelector } from './domTreeAdapter.js';
6
7
  export function LayersPanel({ tree, selectedSelector, hoverSelector, onSelect, onHover, onMove, onExpand }) {
8
+ const { t } = useTranslation();
7
9
  const [query, setQuery] = useState('');
8
10
  if (!tree) {
9
11
  return (_jsx("p", { "data-testid": "layers-empty", className: "px-3 py-6 text-center text-xs text-[#8993A4]", children: "\u8FD8\u6CA1\u6709\u62FF\u5230\u9875\u9762\u7ED3\u6784" }));
10
12
  }
11
13
  const selectorOf = (id) => tree.nodes[id]?.selector ?? id;
12
14
  const idOf = (selector) => selector ? findIdBySelector(tree, selector) : null;
13
- return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", "data-testid": "layers-panel", children: [_jsx("div", { className: "p-2", children: _jsx(Input, { "data-testid": "layers-search", placeholder: "\u641C\u7D22\u56FE\u5C42", value: query, onChange: e => setQuery(e.target.value) }) }), _jsx("div", { className: "min-h-0 flex-1 overflow-auto", children: _jsx(TreePanel, { nodes: tree.nodes, rootId: tree.rootId, selectedId: idOf(selectedSelector), hoverId: idOf(hoverSelector ?? null), query: query, onSelect: id => onSelect(selectorOf(id)), onHover: id => onHover(id ? selectorOf(id) : ''), onMove: onMove &&
15
+ return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", "data-testid": "layers-panel", children: [_jsx("div", { className: "p-2", children: _jsx(SearchInput, { clearLabel: t('inlineCanvas.review.clearSearch', '清除搜索'), "data-testid": "layers-search", placeholder: t('inlineCanvas.review.searchLayers', '搜索图层'), value: query, onValueChange: setQuery, onClear: () => setQuery('') }) }), _jsx("div", { className: "min-h-0 flex-1 overflow-auto", children: _jsx(TreePanel, { emptyLabel: t('inlineCanvas.review.noLayers', '没有匹配的图层'), nodes: tree.nodes, rootId: tree.rootId, selectedId: idOf(selectedSelector), hoverId: idOf(hoverSelector ?? null), query: query, onSelect: id => onSelect(selectorOf(id)), onHover: id => onHover(id ? selectorOf(id) : ''), onMove: onMove &&
14
16
  ((dragId, targetId, pos) => onMove(selectorOf(dragId), selectorOf(targetId), pos)), onExpand: onExpand }) })] }));
15
17
  }
@@ -42,6 +42,7 @@ export interface InlineEditorSelection {
42
42
  declaredStyles: Record<string, string>;
43
43
  }
44
44
  export interface UseInlineEditorInput {
45
+ onHistoryShortcut?: (direction: 'undo' | 'redo') => void;
45
46
  url: string;
46
47
  device: CanvasDevice;
47
48
  /** change list 变化时回调 —— 宿主自己决定存哪(本 hook 不做持久化)。 */
@@ -1,4 +1,5 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { CANVAS_MSG_NAMESPACE } from '@ptengine/inline-core/protocol';
2
3
  import { useCanvasSession } from './kernel/useCanvasSession.js';
3
4
  import { useCanvasStream } from './kernel/useCanvasStream.js';
4
5
  import { useMobileFit } from './kernel/useMobileFit.js';
@@ -49,10 +50,21 @@ export function useInlineEditor(input) {
49
50
  documentSizeRef.current?.(payload);
50
51
  }, []);
51
52
  const stream = useCanvasStream(undefined, geometry.onGeometry, onTextEditCommit, onLifecycle, onDocumentSize);
53
+ const historyShortcutRef = useRef(input.onHistoryShortcut);
54
+ historyShortcutRef.current = input.onHistoryShortcut;
55
+ const onStreamEvent = useCallback((msg) => {
56
+ if (msg.namespace === CANVAS_MSG_NAMESPACE && msg.event === 'historyShortcut') {
57
+ const direction = msg.payload?.direction;
58
+ if (direction === 'undo' || direction === 'redo')
59
+ historyShortcutRef.current?.(direction);
60
+ }
61
+ else
62
+ stream.onStreamEvent(msg);
63
+ }, [stream.onStreamEvent]);
52
64
  const session = useCanvasSession({
53
65
  url: input.url,
54
66
  device: input.device,
55
- onStreamEvent: stream.onStreamEvent
67
+ onStreamEvent
56
68
  });
57
69
  const sessionId = session.frameProps?.sessionId;
58
70
  const changeList = useChangeList(sessionId, {
@@ -147,6 +159,33 @@ export function useInlineEditor(input) {
147
159
  ? graftSubtree(prev, anchorId, root, { renderIcon: renderIconRef.current })
148
160
  : prev);
149
161
  }, [sessionId]);
162
+ const treeLoaded = tree !== null;
163
+ const contentRevision = changeList.list
164
+ .filter(record => ['elementContent', 'elementText', 'elementMove'].includes(record.change.type))
165
+ .map(record => record.changeId)
166
+ .join('|');
167
+ const refreshedContent = useRef('');
168
+ useEffect(() => {
169
+ if (!wantTree ||
170
+ !domReady ||
171
+ !sessionId ||
172
+ !treeRef.current ||
173
+ refreshedContent.current === contentRevision)
174
+ return;
175
+ let cancelled = false;
176
+ const loaded = Object.values(treeRef.current.nodes);
177
+ void getDomTree(sessionId, { maxDepth: 16, maxNodes: Math.max(1500, loaded.length) })
178
+ .then(root => {
179
+ if (!cancelled) {
180
+ refreshedContent.current = contentRevision;
181
+ setTree(adaptDomTree(root, { renderIcon: renderIconRef.current }));
182
+ }
183
+ })
184
+ .catch(() => { });
185
+ return () => {
186
+ cancelled = true;
187
+ };
188
+ }, [contentRevision, domReady, sessionId, wantTree, treeLoaded]);
150
189
  const revealAttemptedRef = useRef(new Set());
151
190
  const selectedSelector = selection?.selector ?? null;
152
191
  const selectionChain = selection?.chain;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ptengine/inline-editor",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Ptengine Inline 编辑器:画布会话编排、图层树、属性面板与组件库(含内部 canvas-ui)",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "lucide-react": "^0.577.0",
37
- "@ptengine/inline-core": "~0.2.0"
37
+ "@ptengine/inline-core": "~0.2.1"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@ptengine/design-components": "^0.10.0",