@fps-games/editor 0.1.1-beta.1 → 0.1.1-beta.11
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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/local-editor-harness.d.ts +127 -6
- package/dist/local-editor-harness.d.ts.map +1 -1
- package/dist/local-editor-harness.js +1364 -66
- package/dist/local-editor-harness.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/editor-sky.d.ts +33 -0
- package/node_modules/@fps-games/editor-babylon/dist/editor-sky.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/editor-sky.js +231 -0
- package/node_modules/@fps-games/editor-babylon/dist/editor-sky.js.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/editor-world.d.ts +3 -0
- package/node_modules/@fps-games/editor-babylon/dist/editor-world.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/editor-world.js +8 -0
- package/node_modules/@fps-games/editor-babylon/dist/editor-world.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/index.d.ts +5 -0
- package/node_modules/@fps-games/editor-babylon/dist/index.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/index.js +5 -0
- package/node_modules/@fps-games/editor-babylon/dist/index.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/infinite-grid.d.ts +40 -0
- package/node_modules/@fps-games/editor-babylon/dist/infinite-grid.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/infinite-grid.js +237 -0
- package/node_modules/@fps-games/editor-babylon/dist/infinite-grid.js.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/projection-selection-controller.d.ts +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/projection-selection-controller.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/projection-selection-controller.js +15 -50
- package/node_modules/@fps-games/editor-babylon/dist/projection-selection-controller.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/projection.d.ts +27 -0
- package/node_modules/@fps-games/editor-babylon/dist/projection.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/projection.js +368 -14
- package/node_modules/@fps-games/editor-babylon/dist/projection.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/scene-camera-preview.d.ts +25 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-camera-preview.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-camera-preview.js +176 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-camera-preview.js.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-camera-controller.d.ts +18 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-camera-controller.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-camera-controller.js +142 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-camera-controller.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-input-controller.d.ts +2 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-input-controller.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-input-controller.js +6 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-input-controller.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-measurement-controller.d.ts +20 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-measurement-controller.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-measurement-controller.js +187 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-measurement-controller.js.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-spatial-overlay-controller.d.ts +20 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-spatial-overlay-controller.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-spatial-overlay-controller.js +188 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-spatial-overlay-controller.js.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/transform-gizmo-controller.d.ts +22 -2
- package/node_modules/@fps-games/editor-babylon/dist/transform-gizmo-controller.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/transform-gizmo-controller.js +469 -90
- package/node_modules/@fps-games/editor-babylon/dist/transform-gizmo-controller.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/types.d.ts +20 -0
- package/node_modules/@fps-games/editor-babylon/dist/types.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/package.json +4 -4
- package/node_modules/@fps-games/editor-browser/dist/index.d.ts +2 -0
- package/node_modules/@fps-games/editor-browser/dist/index.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/index.js +1 -0
- package/node_modules/@fps-games/editor-browser/dist/index.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-context-menu.d.ts +3 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-context-menu.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-context-menu.js +103 -48
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-context-menu.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.d.ts +17 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.js +327 -33
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.d.ts +3 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.js +143 -17
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-tree.js +0 -2
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-tree.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.js +27 -20
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.d.ts +10 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.js +253 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.d.ts +8 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js +414 -111
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.d.ts +13 -3
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.js +107 -14
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.d.ts +20 -3
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.js +127 -16
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shortcuts.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shortcuts.js +13 -4
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shortcuts.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-theme.d.ts +4 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-theme.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-theme.js +114 -9
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-theme.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-tooltip.d.ts +11 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-tooltip.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-tooltip.js +148 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-tooltip.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts +153 -3
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.d.ts +35 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.js +376 -2
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts +11 -6
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js +1617 -110
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/package.json +4 -1
- package/node_modules/@fps-games/editor-core/dist/editor-session.d.ts +4 -1
- package/node_modules/@fps-games/editor-core/dist/editor-session.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/editor-session.js +13 -3
- package/node_modules/@fps-games/editor-core/dist/editor-session.js.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/index.d.ts +3 -0
- package/node_modules/@fps-games/editor-core/dist/index.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/index.js +3 -0
- package/node_modules/@fps-games/editor-core/dist/index.js.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/inspector.d.ts +6 -1
- package/node_modules/@fps-games/editor-core/dist/inspector.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/inspector.js +6 -1
- package/node_modules/@fps-games/editor-core/dist/inspector.js.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/scene-graph.d.ts +7 -0
- package/node_modules/@fps-games/editor-core/dist/scene-graph.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/scene-graph.js.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/scene-view-input.d.ts +1 -1
- package/node_modules/@fps-games/editor-core/dist/scene-view-input.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/selection-strategy.d.ts +12 -0
- package/node_modules/@fps-games/editor-core/dist/selection-strategy.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-core/dist/selection-strategy.js +52 -0
- package/node_modules/@fps-games/editor-core/dist/selection-strategy.js.map +1 -0
- package/node_modules/@fps-games/editor-core/dist/transform-gizmo.d.ts +78 -2
- package/node_modules/@fps-games/editor-core/dist/transform-gizmo.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/transform-gizmo.js +81 -1
- package/node_modules/@fps-games/editor-core/dist/transform-gizmo.js.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/transform-operations.d.ts +32 -0
- package/node_modules/@fps-games/editor-core/dist/transform-operations.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-core/dist/transform-operations.js +125 -0
- package/node_modules/@fps-games/editor-core/dist/transform-operations.js.map +1 -0
- package/node_modules/@fps-games/editor-core/dist/viewport-tools.d.ts +94 -0
- package/node_modules/@fps-games/editor-core/dist/viewport-tools.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-core/dist/viewport-tools.js +58 -0
- package/node_modules/@fps-games/editor-core/dist/viewport-tools.js.map +1 -0
- package/node_modules/@fps-games/editor-core/package.json +2 -2
- package/node_modules/@fps-games/editor-forge-play/dist/index.d.ts +1 -1
- package/node_modules/@fps-games/editor-forge-play/dist/index.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-forge-play/dist/index.js +5 -1
- package/node_modules/@fps-games/editor-forge-play/dist/index.js.map +1 -1
- package/node_modules/@fps-games/editor-forge-play/package.json +2 -2
- package/node_modules/@fps-games/editor-protocol/dist/index.d.ts +4 -0
- package/node_modules/@fps-games/editor-protocol/dist/index.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-protocol/dist/index.js +4 -0
- package/node_modules/@fps-games/editor-protocol/dist/index.js.map +1 -1
- package/node_modules/@fps-games/editor-protocol/package.json +1 -1
- package/package.json +6 -6
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createLocalEditorIcon } from './local-editor-ui-icons';
|
|
2
|
+
import { createBadge, createAssetList, createDockTab, createEditorInputStyle, createEmptyState, createListItemBlock, createPanelHeader, createPropertyRow, createToolbarButton, createTreeView, createTreeViewItem, } from './local-editor-ui-primitives';
|
|
2
3
|
import { clearElement, toTitle } from './local-editor-ui-shared';
|
|
4
|
+
const DOCK_TAB_ICONS = {
|
|
5
|
+
assets: 'asset',
|
|
6
|
+
history: 'history',
|
|
7
|
+
};
|
|
3
8
|
export function renderHierarchyPanel(doc, panel, state, rename, drop) {
|
|
4
9
|
clearElement(panel);
|
|
5
|
-
const createGroupButton = createToolbarButton(doc, '+
|
|
10
|
+
const createGroupButton = createToolbarButton(doc, '+ Empty', 'object');
|
|
6
11
|
createGroupButton.dataset.editorHierarchyCreateGroup = 'true';
|
|
7
12
|
createGroupButton.style.padding = '3px 7px';
|
|
8
13
|
createGroupButton.style.fontSize = '11px';
|
|
9
|
-
panel.appendChild(createPanelHeader(doc, '
|
|
14
|
+
panel.appendChild(createPanelHeader(doc, 'Hierarchy', [createGroupButton], 'hierarchy'));
|
|
10
15
|
const list = createTreeView(doc);
|
|
11
16
|
for (const item of state.hierarchy) {
|
|
12
17
|
const isRenaming = rename?.id === item.id;
|
|
@@ -35,7 +40,7 @@ export function renderHierarchyPanel(doc, panel, state, rename, drop) {
|
|
|
35
40
|
'border:1px solid var(--fps-editor-accent-strong)',
|
|
36
41
|
'border-radius:2px',
|
|
37
42
|
'background:var(--fps-editor-field)',
|
|
38
|
-
'color
|
|
43
|
+
'color:var(--fps-editor-text-strong)',
|
|
39
44
|
'font-size:12px',
|
|
40
45
|
'font-weight:700',
|
|
41
46
|
'padding:2px 5px',
|
|
@@ -80,7 +85,7 @@ export function renderWorkbenchBottomDockPanel(doc, panel, state, activeTab, pan
|
|
|
80
85
|
{ id: 'history', title: '历史', area: 'bottom' },
|
|
81
86
|
];
|
|
82
87
|
for (const tabPanel of dockTabs) {
|
|
83
|
-
const button = createDockTab(doc, tabPanel.title, activeTab === tabPanel.id);
|
|
88
|
+
const button = createDockTab(doc, tabPanel.title, activeTab === tabPanel.id, DOCK_TAB_ICONS[tabPanel.id]);
|
|
84
89
|
button.dataset.editorDockTab = tabPanel.id;
|
|
85
90
|
tabHeader.appendChild(button);
|
|
86
91
|
}
|
|
@@ -107,30 +112,29 @@ function appendDockTabs(doc, panel, activeTab) {
|
|
|
107
112
|
history: '历史',
|
|
108
113
|
};
|
|
109
114
|
for (const tab of ['assets', 'history']) {
|
|
110
|
-
const button = doc.createElement('button');
|
|
111
|
-
button.type = 'button';
|
|
112
|
-
button.dataset.editorDockTab = tab;
|
|
113
|
-
button.textContent = labels[tab];
|
|
114
115
|
const active = activeTab === tab;
|
|
115
|
-
button
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
'font-weight:900',
|
|
123
|
-
'padding:0 8px 8px',
|
|
124
|
-
'cursor:pointer',
|
|
125
|
-
].join(';');
|
|
116
|
+
const button = createDockTab(doc, labels[tab], active, DOCK_TAB_ICONS[tab]);
|
|
117
|
+
button.dataset.editorDockTab = tab;
|
|
118
|
+
button.style.border = '0';
|
|
119
|
+
button.style.borderBottom = `2px solid ${active ? 'var(--fps-editor-accent-strong)' : 'transparent'}`;
|
|
120
|
+
button.style.background = 'transparent';
|
|
121
|
+
button.style.color = active ? 'var(--fps-editor-text-strong)' : 'var(--fps-editor-muted)';
|
|
122
|
+
button.style.padding = '0 8px 8px';
|
|
126
123
|
tabs.appendChild(button);
|
|
127
124
|
}
|
|
128
125
|
panel.appendChild(tabs);
|
|
129
126
|
}
|
|
127
|
+
function createHeadingLabel(doc, text) {
|
|
128
|
+
const label = doc.createElement('span');
|
|
129
|
+
label.textContent = text;
|
|
130
|
+
label.style.cssText = 'min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';
|
|
131
|
+
return label;
|
|
132
|
+
}
|
|
130
133
|
function renderAssetBrowserContent(doc, panel, state, variant = 'list') {
|
|
131
134
|
const title = doc.createElement('h2');
|
|
132
|
-
title.
|
|
133
|
-
title.
|
|
135
|
+
title.style.cssText = 'display:flex;align-items:center;gap:6px;font-size:13px;margin:0 0 8px;font-weight:800;color:var(--fps-editor-text-strong)';
|
|
136
|
+
title.appendChild(createLocalEditorIcon(doc, 'asset'));
|
|
137
|
+
title.appendChild(createHeadingLabel(doc, '资产浏览器'));
|
|
134
138
|
panel.appendChild(title);
|
|
135
139
|
const filter = doc.createElement('input');
|
|
136
140
|
filter.type = 'search';
|
|
@@ -154,7 +158,17 @@ function renderAssetBrowserContent(doc, panel, state, variant = 'list') {
|
|
|
154
158
|
.filter((asset) => {
|
|
155
159
|
if (!normalizedFilter)
|
|
156
160
|
return true;
|
|
157
|
-
return [
|
|
161
|
+
return [
|
|
162
|
+
asset.id,
|
|
163
|
+
asset.assetId ?? '',
|
|
164
|
+
asset.guid ?? '',
|
|
165
|
+
asset.kind ?? '',
|
|
166
|
+
asset.label,
|
|
167
|
+
asset.meta ?? '',
|
|
168
|
+
asset.external?.platformAssetId ?? '',
|
|
169
|
+
asset.external?.assetPath ?? '',
|
|
170
|
+
asset.external?.assetUrl ?? '',
|
|
171
|
+
]
|
|
158
172
|
.some(value => value.toLowerCase().includes(normalizedFilter));
|
|
159
173
|
})
|
|
160
174
|
.slice(0, 80);
|
|
@@ -177,19 +191,27 @@ function renderAssetBrowserContent(doc, panel, state, variant = 'list') {
|
|
|
177
191
|
'padding:6px 8px',
|
|
178
192
|
'border:1px solid var(--fps-editor-border)',
|
|
179
193
|
'border-radius:3px',
|
|
180
|
-
'background
|
|
194
|
+
'background:var(--fps-editor-asset-card-bg)',
|
|
181
195
|
'color:var(--fps-editor-text)',
|
|
196
|
+
'display:flex',
|
|
197
|
+
'align-items:center',
|
|
198
|
+
'gap:8px',
|
|
182
199
|
'font-size:12px',
|
|
183
200
|
'cursor:pointer',
|
|
184
201
|
].join(';');
|
|
202
|
+
const icon = createLocalEditorIcon(doc, 'asset', { size: 16 });
|
|
203
|
+
const body = doc.createElement('div');
|
|
204
|
+
body.style.cssText = 'min-width:0;flex:1';
|
|
185
205
|
const label = doc.createElement('div');
|
|
186
206
|
label.textContent = asset.label;
|
|
187
207
|
label.style.cssText = 'font-weight:800;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';
|
|
188
208
|
const meta = doc.createElement('div');
|
|
189
209
|
meta.textContent = asset.meta ?? asset.id;
|
|
190
210
|
meta.style.cssText = 'color:var(--fps-editor-muted);font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';
|
|
191
|
-
|
|
192
|
-
|
|
211
|
+
body.appendChild(label);
|
|
212
|
+
body.appendChild(meta);
|
|
213
|
+
button.appendChild(icon);
|
|
214
|
+
button.appendChild(body);
|
|
193
215
|
list.appendChild(button);
|
|
194
216
|
}
|
|
195
217
|
panel.appendChild(list);
|
|
@@ -197,36 +219,21 @@ function renderAssetBrowserContent(doc, panel, state, variant = 'list') {
|
|
|
197
219
|
function renderHistoryPanel(doc, panel, history) {
|
|
198
220
|
const title = doc.createElement('h2');
|
|
199
221
|
const entries = history?.entries ?? [];
|
|
200
|
-
title.
|
|
201
|
-
title.
|
|
222
|
+
title.style.cssText = 'display:flex;align-items:center;gap:6px;font-size:13px;margin:0 0 8px;font-weight:800;color:var(--fps-editor-text-strong)';
|
|
223
|
+
title.appendChild(createLocalEditorIcon(doc, 'history'));
|
|
224
|
+
title.appendChild(createHeadingLabel(doc, `历史记录 (${entries.length})`));
|
|
202
225
|
panel.appendChild(title);
|
|
203
226
|
appendHistoryEntries(doc, panel, entries);
|
|
204
227
|
}
|
|
205
228
|
function appendHistoryEntries(doc, panel, entries) {
|
|
206
229
|
if (entries.length === 0) {
|
|
207
|
-
|
|
208
|
-
empty.textContent = '暂无文档修改。';
|
|
209
|
-
empty.style.cssText = [
|
|
210
|
-
'padding:8px',
|
|
211
|
-
'border:1px solid var(--fps-editor-border-soft)',
|
|
212
|
-
'border-radius:3px',
|
|
213
|
-
'background:var(--fps-editor-field)',
|
|
214
|
-
'color:var(--fps-editor-muted)',
|
|
215
|
-
'font-size:11px',
|
|
216
|
-
].join(';');
|
|
217
|
-
panel.appendChild(empty);
|
|
230
|
+
panel.appendChild(createEmptyState(doc, '暂无文档修改。'));
|
|
218
231
|
return;
|
|
219
232
|
}
|
|
220
233
|
const list = doc.createElement('div');
|
|
221
234
|
list.style.cssText = 'display:flex;flex-direction:column;gap:4px';
|
|
222
235
|
for (const entry of entries) {
|
|
223
|
-
const item = doc
|
|
224
|
-
item.style.cssText = [
|
|
225
|
-
'padding:7px 8px',
|
|
226
|
-
'border:1px solid var(--fps-editor-border-soft)',
|
|
227
|
-
'border-radius:3px',
|
|
228
|
-
'background:var(--fps-editor-panel-soft)',
|
|
229
|
-
].join(';');
|
|
236
|
+
const item = createListItemBlock(doc);
|
|
230
237
|
const label = doc.createElement('div');
|
|
231
238
|
label.textContent = entry.label;
|
|
232
239
|
label.style.cssText = 'font-size:12px;font-weight:900;color:var(--fps-editor-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap';
|
|
@@ -259,10 +266,12 @@ function formatHistoryTimestamp(createdAt) {
|
|
|
259
266
|
].join('');
|
|
260
267
|
}
|
|
261
268
|
export function renderInspectorPanel(doc, panel, state, filter = '', options = {}) {
|
|
269
|
+
const renderSnapshot = captureInspectorPanelRenderSnapshot(doc, panel);
|
|
262
270
|
clearElement(panel);
|
|
263
271
|
const title = doc.createElement('h2');
|
|
264
|
-
title.
|
|
265
|
-
title.
|
|
272
|
+
title.style.cssText = 'display:flex;align-items:center;gap:6px;font-size:13px;margin:-8px -8px 8px;padding:7px 8px;border-bottom:1px solid var(--fps-editor-divider);background:var(--fps-editor-chrome-dark);font-weight:800;color:var(--fps-editor-text-strong)';
|
|
273
|
+
title.appendChild(createLocalEditorIcon(doc, 'inspector'));
|
|
274
|
+
title.appendChild(createHeadingLabel(doc, 'Inspector'));
|
|
266
275
|
panel.appendChild(title);
|
|
267
276
|
const search = createInspectorSearchInput(doc, filter);
|
|
268
277
|
panel.appendChild(search);
|
|
@@ -276,23 +285,45 @@ export function renderInspectorPanel(doc, panel, state, filter = '', options = {
|
|
|
276
285
|
empty.textContent = '请从层级树或 Scene View 中选择一个 GameObject。';
|
|
277
286
|
empty.style.cssText = 'color:var(--fps-editor-muted);line-height:1.45';
|
|
278
287
|
panel.appendChild(empty);
|
|
288
|
+
restoreInspectorPanelRenderSnapshot(panel, renderSnapshot);
|
|
279
289
|
return;
|
|
280
290
|
}
|
|
281
291
|
const controlRegistry = createLocalEditorBrowserInspectorControlRegistry(options.controls, options.controlConflict);
|
|
282
292
|
appendInspectorSummary(doc, panel, inspectorObject, selectionCount);
|
|
283
293
|
const visibleSections = filterInspectorSections(inspectorObject.sections.filter(section => section.placement !== 'summary'), filter);
|
|
284
294
|
if (visibleSections.length === 0) {
|
|
285
|
-
if (!filter.trim() && inspectorObject.sections.length === 0)
|
|
295
|
+
if (!filter.trim() && inspectorObject.sections.length === 0) {
|
|
296
|
+
restoreInspectorPanelRenderSnapshot(panel, renderSnapshot);
|
|
286
297
|
return;
|
|
298
|
+
}
|
|
287
299
|
const empty = doc.createElement('div');
|
|
288
300
|
empty.textContent = '没有匹配的 Inspector 字段。';
|
|
289
301
|
empty.style.cssText = 'color:var(--fps-editor-muted);line-height:1.45';
|
|
290
302
|
panel.appendChild(empty);
|
|
303
|
+
restoreInspectorPanelRenderSnapshot(panel, renderSnapshot);
|
|
291
304
|
return;
|
|
292
305
|
}
|
|
293
306
|
for (const section of visibleSections) {
|
|
294
307
|
panel.appendChild(createInspectorSectionBlock(doc, inspectorObject, section, controlRegistry));
|
|
295
308
|
}
|
|
309
|
+
restoreInspectorPanelRenderSnapshot(panel, renderSnapshot);
|
|
310
|
+
}
|
|
311
|
+
function captureInspectorPanelRenderSnapshot(doc, panel) {
|
|
312
|
+
const activeElement = doc.activeElement;
|
|
313
|
+
const elementConstructor = doc.defaultView?.HTMLElement;
|
|
314
|
+
return {
|
|
315
|
+
preserveScroll: !!activeElement
|
|
316
|
+
&& (elementConstructor ? activeElement instanceof elementConstructor : activeElement instanceof HTMLElement)
|
|
317
|
+
&& panel.contains(activeElement),
|
|
318
|
+
scrollTop: panel.scrollTop,
|
|
319
|
+
scrollLeft: panel.scrollLeft,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function restoreInspectorPanelRenderSnapshot(panel, snapshot) {
|
|
323
|
+
if (!snapshot.preserveScroll)
|
|
324
|
+
return;
|
|
325
|
+
panel.scrollTop = snapshot.scrollTop;
|
|
326
|
+
panel.scrollLeft = snapshot.scrollLeft;
|
|
296
327
|
}
|
|
297
328
|
function createInspectorSearchInput(doc, value) {
|
|
298
329
|
const input = doc.createElement('input');
|
|
@@ -300,26 +331,24 @@ function createInspectorSearchInput(doc, value) {
|
|
|
300
331
|
input.value = value;
|
|
301
332
|
input.placeholder = 'Search...';
|
|
302
333
|
input.style.cssText = [
|
|
334
|
+
createEditorInputStyle(),
|
|
303
335
|
'width:100%',
|
|
304
336
|
'height:30px',
|
|
305
|
-
'box-sizing:border-box',
|
|
306
337
|
'margin:0 0 8px',
|
|
307
|
-
'border:1px solid var(--fps-editor-border)',
|
|
308
|
-
'border-radius:3px',
|
|
309
|
-
'background:var(--fps-editor-field)',
|
|
310
|
-
'color:var(--fps-editor-text)',
|
|
311
|
-
'font-size:12px',
|
|
312
|
-
'padding:0 8px',
|
|
313
338
|
'outline:none',
|
|
314
339
|
].join(';');
|
|
315
340
|
return input;
|
|
316
341
|
}
|
|
317
342
|
function appendInspectorSummary(doc, panel, inspectorObject, selectionCount) {
|
|
318
343
|
const block = createInspectorComponentBlock(doc);
|
|
344
|
+
const header = doc.createElement('div');
|
|
345
|
+
header.style.cssText = 'display:flex;align-items:center;justify-content:space-between;gap:8px;margin:0 0 8px';
|
|
319
346
|
const title = doc.createElement('h3');
|
|
320
347
|
title.textContent = selectionCount > 1 ? 'Selection' : 'GameObject';
|
|
321
|
-
title.style.cssText = 'font-size:12px;margin:0
|
|
322
|
-
|
|
348
|
+
title.style.cssText = 'font-size:12px;margin:0;font-weight:900;color:var(--fps-editor-text-strong)';
|
|
349
|
+
header.appendChild(title);
|
|
350
|
+
header.appendChild(createInspectorAccessBadge(doc, 'readonly'));
|
|
351
|
+
block.appendChild(header);
|
|
323
352
|
appendReadOnlyRow(doc, block, selectionCount > 1 ? 'Selected' : 'Name', inspectorObject.label ?? inspectorObject.activeId ?? inspectorObject.targetIds[0] ?? '无');
|
|
324
353
|
appendReadOnlyRow(doc, block, 'Active ID', inspectorObject.activeId ?? '无');
|
|
325
354
|
if (selectionCount > 1)
|
|
@@ -350,7 +379,11 @@ function createInspectorSectionBlock(doc, inspectorObject, section, controlRegis
|
|
|
350
379
|
const block = doc.createElement('details');
|
|
351
380
|
block.dataset.editorInspectorSection = section.id;
|
|
352
381
|
block.open = section.collapsedByDefault !== true;
|
|
353
|
-
|
|
382
|
+
const status = resolveLocalEditorBrowserInspectorSectionStatus(section);
|
|
383
|
+
const { access, effect } = status;
|
|
384
|
+
block.dataset.editorInspectorAccess = access;
|
|
385
|
+
block.dataset.editorInspectorEffect = effect;
|
|
386
|
+
block.style.cssText = createInspectorBlockStyle(access, effect);
|
|
354
387
|
const sectionTitle = doc.createElement('summary');
|
|
355
388
|
sectionTitle.style.cssText = [
|
|
356
389
|
'font-size:12px',
|
|
@@ -375,11 +408,13 @@ function createInspectorSectionBlock(doc, inspectorObject, section, controlRegis
|
|
|
375
408
|
summary.style.cssText = 'overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-transform:none;font-size:11px';
|
|
376
409
|
meta.appendChild(summary);
|
|
377
410
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
411
|
+
const badge = createInspectorAccessBadge(doc, access);
|
|
412
|
+
badge.style.flex = '0 0 auto';
|
|
413
|
+
meta.appendChild(badge);
|
|
414
|
+
const effectBadge = createInspectorEffectBadge(doc, effect, section.disabledReason);
|
|
415
|
+
if (effectBadge && status.showEffectBadge) {
|
|
416
|
+
effectBadge.style.flex = '0 0 auto';
|
|
417
|
+
meta.appendChild(effectBadge);
|
|
383
418
|
}
|
|
384
419
|
sectionTitle.appendChild(meta);
|
|
385
420
|
block.appendChild(sectionTitle);
|
|
@@ -396,18 +431,133 @@ function createInspectorSectionBlock(doc, inspectorObject, section, controlRegis
|
|
|
396
431
|
}
|
|
397
432
|
function createInspectorComponentBlock(doc) {
|
|
398
433
|
const block = doc.createElement('div');
|
|
399
|
-
block.
|
|
434
|
+
block.dataset.editorInspectorAccess = 'readonly';
|
|
435
|
+
block.dataset.editorInspectorEffect = 'active';
|
|
436
|
+
block.style.cssText = createInspectorBlockStyle('readonly', 'active');
|
|
400
437
|
return block;
|
|
401
438
|
}
|
|
402
|
-
function
|
|
439
|
+
export function resolveLocalEditorBrowserInspectorSectionStatus(section) {
|
|
440
|
+
const access = getInspectorSectionAccess(section);
|
|
441
|
+
const effect = getInspectorSectionEffect(section);
|
|
442
|
+
return {
|
|
443
|
+
access,
|
|
444
|
+
effect,
|
|
445
|
+
showEffectBadge: effect !== 'active'
|
|
446
|
+
&& !(access === 'runtime' && effect === 'runtime')
|
|
447
|
+
&& !(effect === 'default' && inspectorSummaryLooksDefault(section.summary)),
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function createInspectorBlockStyle(_access, _effect = 'active') {
|
|
403
451
|
return [
|
|
404
452
|
'margin:0 0 8px',
|
|
405
453
|
'padding:9px',
|
|
406
|
-
'border:1px solid var(--fps-editor-border)',
|
|
454
|
+
'border:1px solid var(--fps-editor-inspector-section-border, var(--fps-editor-border))',
|
|
407
455
|
'border-radius:3px',
|
|
408
456
|
'background:var(--fps-editor-panel-soft)',
|
|
409
457
|
].join(';');
|
|
410
458
|
}
|
|
459
|
+
function isInspectorPropertyEditable(property) {
|
|
460
|
+
return property.readOnly !== true
|
|
461
|
+
&& property.persistence === 'document'
|
|
462
|
+
&& getInspectorPropertyEffect(property) === 'active';
|
|
463
|
+
}
|
|
464
|
+
function getInspectorPropertyAccess(property) {
|
|
465
|
+
if (property.persistence === 'runtime')
|
|
466
|
+
return 'runtime';
|
|
467
|
+
if (getInspectorPropertyEffect(property) === 'runtime')
|
|
468
|
+
return 'runtime';
|
|
469
|
+
return isInspectorPropertyEditable(property) ? 'editable' : 'readonly';
|
|
470
|
+
}
|
|
471
|
+
function getInspectorSectionAccess(section) {
|
|
472
|
+
if (section.runtimeOnly || section.persistence === 'runtime' || getInspectorSectionEffect(section) === 'runtime')
|
|
473
|
+
return 'runtime';
|
|
474
|
+
const editableCount = section.properties.filter(isInspectorPropertyEditable).length;
|
|
475
|
+
if (editableCount === 0)
|
|
476
|
+
return 'readonly';
|
|
477
|
+
if (editableCount === section.properties.length)
|
|
478
|
+
return 'editable';
|
|
479
|
+
return 'mixed';
|
|
480
|
+
}
|
|
481
|
+
function getInspectorPropertyEffect(property) {
|
|
482
|
+
if (property.effect)
|
|
483
|
+
return property.effect;
|
|
484
|
+
if (property.persistence === 'runtime')
|
|
485
|
+
return 'runtime';
|
|
486
|
+
return 'active';
|
|
487
|
+
}
|
|
488
|
+
function getInspectorSectionEffect(section) {
|
|
489
|
+
if (section.effect)
|
|
490
|
+
return section.effect;
|
|
491
|
+
if (section.runtimeOnly || section.persistence === 'runtime')
|
|
492
|
+
return 'runtime';
|
|
493
|
+
if (section.properties.some(isInspectorPropertyEditable))
|
|
494
|
+
return 'active';
|
|
495
|
+
const effects = new Set(section.properties.map(getInspectorPropertyEffect));
|
|
496
|
+
if (effects.size === 1)
|
|
497
|
+
return [...effects][0] ?? 'active';
|
|
498
|
+
if (effects.has('unsupported'))
|
|
499
|
+
return 'unsupported';
|
|
500
|
+
if (effects.has('default'))
|
|
501
|
+
return 'default';
|
|
502
|
+
if (effects.has('derived'))
|
|
503
|
+
return 'derived';
|
|
504
|
+
if (effects.has('runtime'))
|
|
505
|
+
return 'runtime';
|
|
506
|
+
return 'active';
|
|
507
|
+
}
|
|
508
|
+
function createInspectorAccessBadge(doc, access) {
|
|
509
|
+
const labels = {
|
|
510
|
+
editable: 'EDITABLE',
|
|
511
|
+
mixed: 'MIXED',
|
|
512
|
+
readonly: 'READONLY',
|
|
513
|
+
runtime: 'RUNTIME',
|
|
514
|
+
};
|
|
515
|
+
const badge = createBadge(doc, labels[access], {
|
|
516
|
+
compact: true,
|
|
517
|
+
tone: access === 'editable' ? 'success' : access === 'mixed' || access === 'runtime' ? 'warning' : 'default',
|
|
518
|
+
});
|
|
519
|
+
badge.title = access === 'editable'
|
|
520
|
+
? 'This section writes to the document.'
|
|
521
|
+
: access === 'mixed'
|
|
522
|
+
? 'This section contains both editable and read-only fields.'
|
|
523
|
+
: access === 'runtime'
|
|
524
|
+
? 'Runtime-only context, not saved to the document.'
|
|
525
|
+
: 'Read-only information.';
|
|
526
|
+
return badge;
|
|
527
|
+
}
|
|
528
|
+
function createInspectorEffectBadge(doc, effect, disabledReason) {
|
|
529
|
+
if (effect === 'active')
|
|
530
|
+
return null;
|
|
531
|
+
const labels = {
|
|
532
|
+
default: 'DEFAULTS',
|
|
533
|
+
derived: 'DERIVED',
|
|
534
|
+
runtime: 'RUNTIME',
|
|
535
|
+
unsupported: 'NO EFFECT',
|
|
536
|
+
};
|
|
537
|
+
const badge = createBadge(doc, labels[effect], {
|
|
538
|
+
compact: true,
|
|
539
|
+
tone: effect === 'unsupported' ? 'danger' : 'warning',
|
|
540
|
+
});
|
|
541
|
+
badge.title = disabledReason ?? createInspectorEffectTitle(effect);
|
|
542
|
+
return badge;
|
|
543
|
+
}
|
|
544
|
+
function createInspectorEffectTitle(effect) {
|
|
545
|
+
switch (effect) {
|
|
546
|
+
case 'default':
|
|
547
|
+
return 'Default values are being shown; edits do not affect this object until an override is configured.';
|
|
548
|
+
case 'derived':
|
|
549
|
+
return 'Derived from document or runtime state.';
|
|
550
|
+
case 'runtime':
|
|
551
|
+
return 'Runtime-only context, not saved to the document.';
|
|
552
|
+
case 'unsupported':
|
|
553
|
+
return 'This field is visible but editing is not supported for the current object.';
|
|
554
|
+
default:
|
|
555
|
+
return 'Editable field.';
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
function inspectorSummaryLooksDefault(summary) {
|
|
559
|
+
return summary?.trim().toLowerCase() === 'defaults';
|
|
560
|
+
}
|
|
411
561
|
function groupInspectorProperties(properties) {
|
|
412
562
|
const groups = [];
|
|
413
563
|
const consumed = new Set();
|
|
@@ -436,7 +586,7 @@ function groupInspectorProperties(properties) {
|
|
|
436
586
|
for (const entry of vector)
|
|
437
587
|
consumed.add(entry.path);
|
|
438
588
|
const parts = basePath.split('.');
|
|
439
|
-
groups.push({ kind: 'vector', label:
|
|
589
|
+
groups.push({ kind: 'vector', label: resolveInspectorVectorGroupLabel(vector, parts[parts.length - 1] ?? basePath), properties: vector });
|
|
440
590
|
}
|
|
441
591
|
else {
|
|
442
592
|
groups.push({ kind: 'property', property });
|
|
@@ -445,6 +595,16 @@ function groupInspectorProperties(properties) {
|
|
|
445
595
|
}
|
|
446
596
|
return groups;
|
|
447
597
|
}
|
|
598
|
+
function resolveInspectorVectorGroupLabel(properties, fallback) {
|
|
599
|
+
const labels = properties.map(property => {
|
|
600
|
+
const match = property.label.match(/^(.*)\.(x|y|z|r|g|b)$/);
|
|
601
|
+
return match?.[1]?.trim() ?? '';
|
|
602
|
+
});
|
|
603
|
+
const first = labels[0];
|
|
604
|
+
if (first && labels.every(label => label === first))
|
|
605
|
+
return first;
|
|
606
|
+
return toTitle(fallback);
|
|
607
|
+
}
|
|
448
608
|
const builtinInspectorControlRegistrations = [
|
|
449
609
|
{
|
|
450
610
|
id: 'builtin.readonly',
|
|
@@ -515,7 +675,16 @@ const builtinInspectorControlRegistrations = [
|
|
|
515
675
|
];
|
|
516
676
|
function appendInspectorPropertyRow(doc, parent, inspectorObject, property, controlRegistry) {
|
|
517
677
|
const control = renderInspectorControl(doc, controlRegistry, inspectorObject, property);
|
|
518
|
-
|
|
678
|
+
const access = getInspectorPropertyAccess(property);
|
|
679
|
+
const effect = getInspectorPropertyEffect(property);
|
|
680
|
+
const row = createPropertyRow(doc, property.label, control);
|
|
681
|
+
row.dataset.editorInspectorAccess = access;
|
|
682
|
+
row.dataset.editorInspectorEffect = effect;
|
|
683
|
+
row.title = createInspectorStatusTitle(access, effect, property.tooltip ?? property.disabledReason);
|
|
684
|
+
const label = row.firstElementChild;
|
|
685
|
+
if (label)
|
|
686
|
+
label.style.cssText = createInspectorPropertyLabelStyle(access, effect);
|
|
687
|
+
parent.appendChild(row);
|
|
519
688
|
}
|
|
520
689
|
export function createLocalEditorBrowserInspectorControlRegistry(controls = [], conflict = 'error') {
|
|
521
690
|
const registrations = new Map();
|
|
@@ -544,7 +713,7 @@ function compareInspectorControlRegistrations(left, right) {
|
|
|
544
713
|
return (left.order ?? 0) - (right.order ?? 0) || left.id.localeCompare(right.id);
|
|
545
714
|
}
|
|
546
715
|
function renderInspectorControl(doc, registrations, target, property) {
|
|
547
|
-
if (property
|
|
716
|
+
if (!isInspectorPropertyEditable(property))
|
|
548
717
|
return createInspectorReadonlyControl(doc, target, property);
|
|
549
718
|
const context = {
|
|
550
719
|
doc,
|
|
@@ -576,18 +745,145 @@ function localEditorBrowserInspectorControlSupports(registration, context) {
|
|
|
576
745
|
function appendInspectorVectorInputs(doc, parent, inspectorObject, label, properties) {
|
|
577
746
|
const wrapper = doc.createElement('div');
|
|
578
747
|
wrapper.style.cssText = 'margin:8px 0';
|
|
748
|
+
const access = getInspectorPropertyGroupAccess(properties);
|
|
749
|
+
const effect = getInspectorPropertyGroupEffect(properties);
|
|
750
|
+
wrapper.dataset.editorInspectorAccess = access;
|
|
751
|
+
wrapper.dataset.editorInspectorEffect = effect;
|
|
579
752
|
const labelElement = doc.createElement('div');
|
|
580
|
-
labelElement.
|
|
581
|
-
|
|
753
|
+
labelElement.style.cssText = 'display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:5px';
|
|
754
|
+
const labelText = doc.createElement('span');
|
|
755
|
+
labelText.textContent = label;
|
|
756
|
+
labelText.style.cssText = createInspectorPropertyLabelStyle(access, effect);
|
|
757
|
+
labelElement.appendChild(labelText);
|
|
758
|
+
if (access !== 'editable') {
|
|
759
|
+
const badge = createInspectorAccessBadge(doc, access);
|
|
760
|
+
badge.style.flex = '0 0 auto';
|
|
761
|
+
labelElement.appendChild(badge);
|
|
762
|
+
}
|
|
763
|
+
const effectBadge = createInspectorEffectBadge(doc, effect, getInspectorPropertyGroupDisabledReason(properties));
|
|
764
|
+
if (effectBadge && !(access === 'runtime' && effect === 'runtime')) {
|
|
765
|
+
effectBadge.style.flex = '0 0 auto';
|
|
766
|
+
labelElement.appendChild(effectBadge);
|
|
767
|
+
}
|
|
582
768
|
wrapper.appendChild(labelElement);
|
|
583
769
|
const fields = doc.createElement('div');
|
|
584
770
|
fields.style.cssText = 'display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px';
|
|
585
771
|
for (const property of properties) {
|
|
586
|
-
fields.appendChild(
|
|
772
|
+
fields.appendChild(isInspectorPropertyEditable(property)
|
|
773
|
+
? createInspectorNumberControl(doc, inspectorObject, property)
|
|
774
|
+
: createInspectorReadonlyControl(doc, inspectorObject, property));
|
|
587
775
|
}
|
|
588
776
|
wrapper.appendChild(fields);
|
|
589
777
|
parent.appendChild(wrapper);
|
|
590
778
|
}
|
|
779
|
+
function getInspectorPropertyGroupAccess(properties) {
|
|
780
|
+
if (properties.some(property => property.persistence === 'runtime'))
|
|
781
|
+
return 'runtime';
|
|
782
|
+
const editableCount = properties.filter(isInspectorPropertyEditable).length;
|
|
783
|
+
if (editableCount === 0)
|
|
784
|
+
return 'readonly';
|
|
785
|
+
if (editableCount === properties.length)
|
|
786
|
+
return 'editable';
|
|
787
|
+
return 'mixed';
|
|
788
|
+
}
|
|
789
|
+
function getInspectorPropertyGroupEffect(properties) {
|
|
790
|
+
const effects = new Set(properties.map(getInspectorPropertyEffect));
|
|
791
|
+
if (effects.size === 1)
|
|
792
|
+
return [...effects][0] ?? 'active';
|
|
793
|
+
if (effects.has('unsupported'))
|
|
794
|
+
return 'unsupported';
|
|
795
|
+
if (effects.has('default'))
|
|
796
|
+
return 'default';
|
|
797
|
+
if (effects.has('derived'))
|
|
798
|
+
return 'derived';
|
|
799
|
+
if (effects.has('runtime'))
|
|
800
|
+
return 'runtime';
|
|
801
|
+
return 'active';
|
|
802
|
+
}
|
|
803
|
+
function getInspectorPropertyGroupDisabledReason(properties) {
|
|
804
|
+
return properties.find(property => property.disabledReason)?.disabledReason;
|
|
805
|
+
}
|
|
806
|
+
function createInspectorPropertyLabelStyle(access, effect = 'active') {
|
|
807
|
+
return [
|
|
808
|
+
`color:${effect === 'unsupported' ? 'var(--fps-editor-danger-text)' : effect === 'default' || effect === 'derived' || access === 'runtime' ? 'var(--fps-editor-warn)' : access === 'editable' ? 'var(--fps-editor-muted-strong)' : 'var(--fps-editor-muted)'}`,
|
|
809
|
+
'font-weight:800',
|
|
810
|
+
access === 'readonly' ? 'opacity:0.82' : '',
|
|
811
|
+
].filter(Boolean).join(';');
|
|
812
|
+
}
|
|
813
|
+
function createInspectorAccessTitle(access, tooltip) {
|
|
814
|
+
const accessLabel = access === 'editable'
|
|
815
|
+
? 'Editable'
|
|
816
|
+
: access === 'mixed'
|
|
817
|
+
? 'Mixed editable and read-only'
|
|
818
|
+
: access === 'runtime'
|
|
819
|
+
? 'Runtime-only'
|
|
820
|
+
: 'Read-only';
|
|
821
|
+
return tooltip ? `${accessLabel}: ${tooltip}` : accessLabel;
|
|
822
|
+
}
|
|
823
|
+
function createInspectorStatusTitle(access, effect, detail) {
|
|
824
|
+
const parts = [createInspectorAccessTitle(access)];
|
|
825
|
+
if (effect !== 'active')
|
|
826
|
+
parts.push(createInspectorEffectTitle(effect));
|
|
827
|
+
if (detail)
|
|
828
|
+
parts.push(detail);
|
|
829
|
+
return [...new Set(parts)].join(' ');
|
|
830
|
+
}
|
|
831
|
+
function createInspectorReadonlyValueShell(doc, access, effect = 'active', title) {
|
|
832
|
+
const valueElement = doc.createElement('div');
|
|
833
|
+
valueElement.dataset.editorInspectorAccess = access;
|
|
834
|
+
valueElement.dataset.editorInspectorEffect = effect;
|
|
835
|
+
valueElement.title = title ?? createInspectorStatusTitle(access, effect);
|
|
836
|
+
const warningLike = effect === 'default' || effect === 'derived' || access === 'runtime' || effect === 'runtime';
|
|
837
|
+
const unsupported = effect === 'unsupported';
|
|
838
|
+
valueElement.style.cssText = [
|
|
839
|
+
'min-width:0',
|
|
840
|
+
'min-height:24px',
|
|
841
|
+
'display:flex',
|
|
842
|
+
'align-items:center',
|
|
843
|
+
'gap:5px',
|
|
844
|
+
'padding:3px 6px',
|
|
845
|
+
`border:1px solid ${unsupported ? 'var(--fps-editor-danger-border)' : warningLike ? 'var(--fps-editor-warn-border)' : 'var(--fps-editor-readonly-border)'}`,
|
|
846
|
+
'border-radius:3px',
|
|
847
|
+
`background:${unsupported ? 'var(--fps-editor-danger-soft)' : warningLike ? 'var(--fps-editor-warn-soft)' : 'var(--fps-editor-readonly-bg)'}`,
|
|
848
|
+
`color:${unsupported ? 'var(--fps-editor-danger-text)' : warningLike ? 'var(--fps-editor-warn)' : 'var(--fps-editor-readonly-text)'}`,
|
|
849
|
+
warningLike ? 'font-style:italic' : '',
|
|
850
|
+
].filter(Boolean).join(';');
|
|
851
|
+
const icon = createLocalEditorIcon(doc, 'lock', { size: 11, strokeWidth: 2.3 });
|
|
852
|
+
icon.style.opacity = '0.78';
|
|
853
|
+
valueElement.appendChild(icon);
|
|
854
|
+
return valueElement;
|
|
855
|
+
}
|
|
856
|
+
function appendInspectorReadonlyTextValue(doc, parent, value, access, effect = 'active', title) {
|
|
857
|
+
const valueText = doc.createElement('span');
|
|
858
|
+
valueText.textContent = value;
|
|
859
|
+
valueText.style.cssText = 'min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';
|
|
860
|
+
parent.appendChild(valueText);
|
|
861
|
+
parent.title = title ?? createInspectorStatusTitle(access, effect);
|
|
862
|
+
}
|
|
863
|
+
function createInspectorReadonlyScalarControl(doc, value, access, effect = 'active', title) {
|
|
864
|
+
const valueElement = createInspectorReadonlyValueShell(doc, access, effect, title);
|
|
865
|
+
appendInspectorReadonlyTextValue(doc, valueElement, value, access, effect, title);
|
|
866
|
+
return valueElement;
|
|
867
|
+
}
|
|
868
|
+
function createInspectorReadonlyDetailsShell(doc, property, access) {
|
|
869
|
+
const effect = getInspectorPropertyEffect(property);
|
|
870
|
+
const warningLike = effect === 'default' || effect === 'derived' || access === 'runtime' || effect === 'runtime';
|
|
871
|
+
const unsupported = effect === 'unsupported';
|
|
872
|
+
const details = doc.createElement('details');
|
|
873
|
+
details.dataset.editorInspectorAccess = access;
|
|
874
|
+
details.dataset.editorInspectorEffect = effect;
|
|
875
|
+
details.title = createInspectorStatusTitle(access, effect, property.tooltip ?? property.disabledReason);
|
|
876
|
+
details.style.cssText = [
|
|
877
|
+
'min-width:0',
|
|
878
|
+
'max-width:100%',
|
|
879
|
+
'padding:3px 6px',
|
|
880
|
+
`border:1px solid ${unsupported ? 'var(--fps-editor-danger-border)' : warningLike ? 'var(--fps-editor-warn-border)' : 'var(--fps-editor-readonly-border)'}`,
|
|
881
|
+
'border-radius:3px',
|
|
882
|
+
`background:${unsupported ? 'var(--fps-editor-danger-soft)' : warningLike ? 'var(--fps-editor-warn-soft)' : 'var(--fps-editor-readonly-bg)'}`,
|
|
883
|
+
`color:${unsupported ? 'var(--fps-editor-danger-text)' : warningLike ? 'var(--fps-editor-warn)' : 'var(--fps-editor-readonly-text)'}`,
|
|
884
|
+
].join(';');
|
|
885
|
+
return details;
|
|
886
|
+
}
|
|
591
887
|
function createInspectorInputBase(doc, target, property) {
|
|
592
888
|
const input = doc.createElement('input');
|
|
593
889
|
input.value = property.mixed ? '' : String(property.value);
|
|
@@ -615,7 +911,14 @@ function createInspectorBooleanControl(doc, target, property) {
|
|
|
615
911
|
const input = createInspectorInputBase(doc, target, property);
|
|
616
912
|
input.type = 'checkbox';
|
|
617
913
|
input.checked = property.mixed ? false : property.value === true;
|
|
618
|
-
input.style.cssText =
|
|
914
|
+
input.style.cssText = [
|
|
915
|
+
'width:16px',
|
|
916
|
+
'height:16px',
|
|
917
|
+
'accent-color:var(--fps-editor-accent)',
|
|
918
|
+
'justify-self:start',
|
|
919
|
+
'cursor:pointer',
|
|
920
|
+
'box-shadow:0 0 0 1px var(--fps-editor-editable-border)',
|
|
921
|
+
].join(';');
|
|
619
922
|
return input;
|
|
620
923
|
}
|
|
621
924
|
function createInspectorEnumControl(doc, target, property) {
|
|
@@ -664,36 +967,30 @@ function createInspectorColorControl(doc, target, property) {
|
|
|
664
967
|
const input = createInspectorInputBase(doc, target, property);
|
|
665
968
|
input.type = 'color';
|
|
666
969
|
input.value = colorValueToHex(property.value);
|
|
667
|
-
input.style.cssText = 'width:34px;height:26px;border:1px solid var(--fps-editor-border);border-radius:3px;background:
|
|
970
|
+
input.style.cssText = 'width:34px;height:26px;border:1px solid var(--fps-editor-editable-border);border-radius:3px;background:var(--fps-editor-editable-bg);padding:0;box-shadow:var(--fps-editor-editable-shadow)';
|
|
668
971
|
return input;
|
|
669
972
|
}
|
|
670
973
|
function createInspectorReadonlyControl(doc, _target, property) {
|
|
671
974
|
if (!property.mixed && isExpandableInspectorValue(property.value)) {
|
|
672
975
|
return createInspectorObjectReadonlyControl(doc, property);
|
|
673
976
|
}
|
|
674
|
-
const
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
`color:${property.persistence === 'runtime' ? 'var(--fps-editor-muted)' : 'var(--fps-editor-text)'}`,
|
|
678
|
-
'overflow:hidden',
|
|
679
|
-
'text-overflow:ellipsis',
|
|
680
|
-
'white-space:nowrap',
|
|
681
|
-
property.persistence === 'runtime' ? 'font-style:italic' : '',
|
|
682
|
-
].filter(Boolean).join(';');
|
|
683
|
-
if (property.persistence === 'runtime')
|
|
684
|
-
valueElement.title = 'Runtime-only context';
|
|
685
|
-
return valueElement;
|
|
977
|
+
const access = getInspectorPropertyAccess(property);
|
|
978
|
+
const effect = getInspectorPropertyEffect(property);
|
|
979
|
+
return createInspectorReadonlyScalarControl(doc, property.mixed ? '--' : formatLocalEditorBrowserInspectorValue(property.value), access, effect, createInspectorStatusTitle(access, effect, property.tooltip ?? property.disabledReason));
|
|
686
980
|
}
|
|
687
981
|
function createInspectorObjectReadonlyControl(doc, property) {
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
'max-width:100%',
|
|
692
|
-
`color:${property.persistence === 'runtime' ? 'var(--fps-editor-muted)' : 'var(--fps-editor-text)'}`,
|
|
693
|
-
].join(';');
|
|
982
|
+
const access = getInspectorPropertyAccess(property);
|
|
983
|
+
const effect = getInspectorPropertyEffect(property);
|
|
984
|
+
const details = createInspectorReadonlyDetailsShell(doc, property, access);
|
|
694
985
|
const summary = doc.createElement('summary');
|
|
695
|
-
summary.
|
|
696
|
-
|
|
986
|
+
summary.style.cssText = 'cursor:pointer;display:flex;align-items:center;gap:5px;overflow:hidden;white-space:nowrap';
|
|
987
|
+
const icon = createLocalEditorIcon(doc, 'lock', { size: 11, strokeWidth: 2.3 });
|
|
988
|
+
icon.style.opacity = '0.78';
|
|
989
|
+
summary.appendChild(icon);
|
|
990
|
+
const summaryText = doc.createElement('span');
|
|
991
|
+
summaryText.textContent = formatInspectorObjectSummary(property.value);
|
|
992
|
+
summaryText.style.cssText = 'min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';
|
|
993
|
+
summary.appendChild(summaryText);
|
|
697
994
|
details.appendChild(summary);
|
|
698
995
|
const pre = doc.createElement('pre');
|
|
699
996
|
pre.textContent = formatLocalEditorBrowserInspectorValue(property.value);
|
|
@@ -704,15 +1001,13 @@ function createInspectorObjectReadonlyControl(doc, property) {
|
|
|
704
1001
|
'word-break:break-word',
|
|
705
1002
|
'margin:6px 0 0',
|
|
706
1003
|
'padding:7px',
|
|
707
|
-
|
|
1004
|
+
`border:1px solid ${effect === 'unsupported' ? 'var(--fps-editor-danger-border)' : effect === 'default' || effect === 'derived' || effect === 'runtime' || access === 'runtime' ? 'var(--fps-editor-warn-border)' : 'var(--fps-editor-readonly-border)'}`,
|
|
708
1005
|
'border-radius:3px',
|
|
709
1006
|
'background:var(--fps-editor-field)',
|
|
710
1007
|
'font-size:11px',
|
|
711
1008
|
'line-height:1.45',
|
|
712
1009
|
].join(';');
|
|
713
1010
|
details.appendChild(pre);
|
|
714
|
-
if (property.persistence === 'runtime')
|
|
715
|
-
details.title = 'Runtime-only context';
|
|
716
1011
|
return details;
|
|
717
1012
|
}
|
|
718
1013
|
export function applyLocalEditorBrowserInspectorControlBinding(element, target, property, options) {
|
|
@@ -724,30 +1019,38 @@ export function applyLocalEditorBrowserInspectorControlBinding(element, target,
|
|
|
724
1019
|
element.dataset.serializedValueType = property.valueType;
|
|
725
1020
|
element.dataset.serializedCommitMode = property.commitMode;
|
|
726
1021
|
element.dataset.serializedPersistence = property.persistence;
|
|
1022
|
+
element.dataset.serializedEffect = getInspectorPropertyEffect(property);
|
|
1023
|
+
if (property.disabledReason)
|
|
1024
|
+
element.dataset.serializedDisabledReason = property.disabledReason;
|
|
727
1025
|
if (options?.source)
|
|
728
1026
|
element.dataset.serializedEditSource = options.source;
|
|
729
|
-
|
|
1027
|
+
const access = getInspectorPropertyAccess(property);
|
|
1028
|
+
const effect = getInspectorPropertyEffect(property);
|
|
1029
|
+
element.title = access === 'editable' && effect === 'active'
|
|
1030
|
+
? property.tooltip ?? property.label
|
|
1031
|
+
: createInspectorStatusTitle(access, effect, property.tooltip ?? property.disabledReason ?? property.label);
|
|
730
1032
|
if ('disabled' in element) {
|
|
731
|
-
element.disabled = property
|
|
1033
|
+
element.disabled = !isInspectorPropertyEditable(property);
|
|
732
1034
|
}
|
|
733
1035
|
}
|
|
734
1036
|
function createInspectorInputStyle() {
|
|
735
1037
|
return [
|
|
736
|
-
|
|
737
|
-
'height:28px',
|
|
738
|
-
'border:1px solid var(--fps-editor-border)',
|
|
739
|
-
'border-radius:3px',
|
|
740
|
-
'background:var(--fps-editor-field)',
|
|
741
|
-
'color:var(--fps-editor-text)',
|
|
742
|
-
'font-size:12px',
|
|
1038
|
+
createEditorInputStyle(),
|
|
743
1039
|
'padding:0 6px',
|
|
1040
|
+
'background:var(--fps-editor-editable-bg)',
|
|
1041
|
+
'border-color:var(--fps-editor-editable-border)',
|
|
1042
|
+
'color:var(--fps-editor-text-strong)',
|
|
1043
|
+
'box-shadow:var(--fps-editor-editable-shadow)',
|
|
744
1044
|
].join(';');
|
|
745
1045
|
}
|
|
746
1046
|
function appendReadOnlyRow(doc, parent, label, value) {
|
|
747
|
-
const valueElement = doc
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
1047
|
+
const valueElement = createInspectorReadonlyScalarControl(doc, value, 'readonly', 'active', 'Read-only summary');
|
|
1048
|
+
const row = createPropertyRow(doc, label, valueElement);
|
|
1049
|
+
row.dataset.editorInspectorAccess = 'readonly';
|
|
1050
|
+
const labelElement = row.firstElementChild;
|
|
1051
|
+
if (labelElement)
|
|
1052
|
+
labelElement.style.cssText = createInspectorPropertyLabelStyle('readonly', 'active');
|
|
1053
|
+
parent.appendChild(row);
|
|
751
1054
|
}
|
|
752
1055
|
function createLegacyInspectorObject(serializedObject) {
|
|
753
1056
|
const targetIds = 'targetIds' in serializedObject ? serializedObject.targetIds : [serializedObject.targetId];
|