@bpmn-nova/vue 0.3.4-preview → 0.3.6-preview
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -267
- package/dist/index.d.ts +86 -1
- package/dist/index.js +73 -9
- package/dist/styles.css +72 -12
- package/llms-full.txt +3924 -826
- package/llms.txt +64 -15
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Fragment, Teleport, createApp, defineComponent, h, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
|
|
1
|
+
import { Fragment, Teleport, createApp, defineComponent, h, onBeforeUnmount, onMounted, onUpdated, reactive, readonly, ref, shallowRef, watch } from 'vue';
|
|
2
2
|
import { BpmnDesigner as CoreDesigner } from '@bpmn-nova/studio/designer';
|
|
3
3
|
import { BpmnViewer as CoreViewer } from '@bpmn-nova/studio/viewer';
|
|
4
4
|
import { importBpmn, exportBpmn } from '@bpmn-nova/studio';
|
|
@@ -6,6 +6,15 @@ import { createEmptyProcess } from '@bpmn-nova/studio';
|
|
|
6
6
|
import { BpmnCanvas as CoreCanvas, BpmnStudioShell as CoreStudioShell, createInteractionController, createStudioController, createTemplateRegistry } from '@bpmn-nova/studio';
|
|
7
7
|
import { PalettePanel as CorePalettePanel, createDefaultPaletteRegistry as createCoreDefaultPaletteRegistry } from '@bpmn-nova/studio';
|
|
8
8
|
|
|
9
|
+
export {
|
|
10
|
+
activityState,
|
|
11
|
+
createRuntimePresentation,
|
|
12
|
+
formatRuntimeInstant,
|
|
13
|
+
inspectRuntime,
|
|
14
|
+
normalizeRuntime,
|
|
15
|
+
parseRuntimeInstant,
|
|
16
|
+
} from '@bpmn-nova/studio/runtime';
|
|
17
|
+
|
|
9
18
|
export {
|
|
10
19
|
createContextMenuRegistry,
|
|
11
20
|
createDefaultContextMenuRegistry,
|
|
@@ -49,7 +58,7 @@ function resolveModel(props) {
|
|
|
49
58
|
}
|
|
50
59
|
|
|
51
60
|
export function useBpmnStudio(options = {}) {
|
|
52
|
-
const studio = options.studio || createStudioController({ model: resolveModel(options), propertiesProfile: options.propertiesProfile, allowedNodeTypes: options.allowedNodeTypes, allowedEdgeTypes: options.allowedEdgeTypes });
|
|
61
|
+
const studio = options.studio || createStudioController({ model: resolveModel(options), config: options.config, propertiesProfile: options.propertiesProfile, allowedNodeTypes: options.allowedNodeTypes, allowedEdgeTypes: options.allowedEdgeTypes });
|
|
53
62
|
const state = reactive(studio.getState());
|
|
54
63
|
const off = studio.subscribe((event) => Object.assign(state, event.state));
|
|
55
64
|
onBeforeUnmount(() => { off(); if (!options.studio) studio.destroy(); });
|
|
@@ -126,33 +135,41 @@ export const BpmnPalettePanel = defineComponent({
|
|
|
126
135
|
export const BpmnStudio = defineComponent({
|
|
127
136
|
name: 'BpmnNovaStudio',
|
|
128
137
|
props: {
|
|
129
|
-
studio: Object, model: Object, xml: String, engine: { type: String, default: 'flowable' }, propertiesProfile: String, allowedNodeTypes: Array, allowedEdgeTypes: Array,
|
|
138
|
+
studio: Object, model: Object, xml: String, engine: { type: String, default: 'flowable' }, config: Object, propertiesProfile: String, allowedNodeTypes: Array, allowedEdgeTypes: Array,
|
|
130
139
|
iconRegistry: Object, paletteRegistry: Object, propertiesRegistry: Object, templateRegistry: Object, contextMenuRegistry: Object,
|
|
131
140
|
nodeRenderers: Object, nodeRenderer: Function, nodeSubtitleResolver: Function, slotsConfig: Object, layout: Function, regions: Object, leftWidth: Number, rightWidth: Number,
|
|
132
141
|
runtime: Object, runtimePresenter: Function, runtimeTraceProjector: Function, runtimeAssetResolver: Function, runtimeTimelineRenderer: Function, onRuntimeTraceItemClick: Function, runtimeDetailsRenderer: Function, onRuntimeDetailsOpen: Function,
|
|
133
142
|
runtimeTransitionDetailsRenderer: Function, onRuntimeTransitionDetailsOpen: Function,
|
|
134
143
|
timeline: Object, runtimeDetails: Object, runtimeTraceOptions: Object, onTraceClick: Function, onElementClick: Function,
|
|
135
|
-
mode: String, allowedModes: Array, projection: String, responsive: { type: Boolean, default:
|
|
144
|
+
mode: String, allowedModes: Array, projection: String, responsive: { type: Boolean, default: undefined }, projectionOptions: Array,
|
|
136
145
|
theme: [Object, String], runtimeAppearance: Object, svgExport: Object, onThemeChange: Function,
|
|
137
146
|
},
|
|
138
|
-
emits: ['change', 'selection-change', 'scope-change', 'element-click', 'trace-click', 'update:mode', 'mode-change', 'validation'],
|
|
147
|
+
emits: ['change', 'selection-change', 'scope-change', 'element-click', 'trace-click', 'update:mode', 'mode-change', 'validation', 'sidebar-change'],
|
|
139
148
|
setup(props, { emit, expose, attrs, slots }) {
|
|
140
149
|
const host = ref(null);
|
|
141
150
|
const headerTarget = ref(null);
|
|
142
151
|
const headerStartTarget = ref(null);
|
|
143
152
|
const headerActionsTarget = ref(null);
|
|
153
|
+
const rightTarget = shallowRef(null);
|
|
144
154
|
const headerContext = ref(null);
|
|
145
155
|
const headerStartContext = ref(null);
|
|
146
156
|
const headerActionsContext = ref(null);
|
|
157
|
+
const rightContext = shallowRef(null);
|
|
147
158
|
const owned = !props.studio;
|
|
148
|
-
const studio = props.studio || createStudioController({ model: resolveModel(props), propertiesProfile: props.propertiesProfile, allowedNodeTypes: props.allowedNodeTypes, allowedEdgeTypes: props.allowedEdgeTypes });
|
|
159
|
+
const studio = props.studio || createStudioController({ model: resolveModel(props), config: props.config, propertiesProfile: props.propertiesProfile, allowedNodeTypes: props.allowedNodeTypes, allowedEdgeTypes: props.allowedEdgeTypes });
|
|
149
160
|
const state = reactive(studio.getState());
|
|
150
161
|
const actualMode = ref(props.mode || props.allowedModes?.[0] || 'design');
|
|
162
|
+
const actualUi = reactive({ controlSize: 'medium', controlHeight: '32px' });
|
|
163
|
+
const readonlyUi = readonly(actualUi);
|
|
164
|
+
const panelSelection = shallowRef(Object.freeze({ selection: studio.selection, selectedElement: studio.getSelectedElement(), trace: null }));
|
|
151
165
|
const nodeSubtitleResolver = (context) => props.nodeSubtitleResolver?.(context);
|
|
152
166
|
let shell = null;
|
|
153
167
|
let off = null;
|
|
154
168
|
let offMode = null;
|
|
155
169
|
let offValidation = null;
|
|
170
|
+
let offSidebar = null;
|
|
171
|
+
let offPanelSelection = null;
|
|
172
|
+
let mountedRightSlot = null;
|
|
156
173
|
|
|
157
174
|
const createTeleportSlot = (target, contextRef) => (context) => {
|
|
158
175
|
target.value = context.container;
|
|
@@ -162,6 +179,8 @@ export const BpmnStudio = defineComponent({
|
|
|
162
179
|
if (contextRef.value === context) contextRef.value = null;
|
|
163
180
|
};
|
|
164
181
|
};
|
|
182
|
+
const nativeRightSlot = createTeleportSlot(rightTarget, rightContext);
|
|
183
|
+
const resolveRightSlot = () => slots.right ? nativeRightSlot : props.slotsConfig?.right;
|
|
165
184
|
|
|
166
185
|
onMounted(() => {
|
|
167
186
|
const shellSlots = { ...(props.slotsConfig || {}) };
|
|
@@ -170,14 +189,20 @@ export const BpmnStudio = defineComponent({
|
|
|
170
189
|
if (slots['header-start']) shellSlots.headerStart = createTeleportSlot(headerStartTarget, headerStartContext);
|
|
171
190
|
if (slots['header-actions']) shellSlots.headerActions = createTeleportSlot(headerActionsTarget, headerActionsContext);
|
|
172
191
|
}
|
|
173
|
-
|
|
192
|
+
mountedRightSlot = resolveRightSlot();
|
|
193
|
+
shellSlots.right = mountedRightSlot;
|
|
194
|
+
shell = new CoreStudioShell({ container: host.value, studio, config: props.config, iconRegistry: props.iconRegistry, paletteRegistry: props.paletteRegistry, propertiesRegistry: props.propertiesRegistry, templateRegistry: props.templateRegistry, contextMenuRegistry: props.contextMenuRegistry, rendererOptions: { nodeRenderers: props.nodeRenderers, nodeRenderer: props.nodeRenderer, nodeSubtitleResolver, runtimePresenter: props.runtimePresenter, runtimeTraceProjector: props.runtimeTraceProjector, runtimeAssetResolver: props.runtimeAssetResolver, runtimeTraceOptions: props.runtimeTraceOptions, runtimeTimelineRenderer: props.runtimeTimelineRenderer, timeline: props.timeline, runtimeDetails: props.runtimeDetails, onRuntimeTraceItemClick: (payload) => props.onRuntimeTraceItemClick?.(payload), runtimeDetailsRenderer: props.runtimeDetailsRenderer, onRuntimeDetailsOpen: (payload) => props.onRuntimeDetailsOpen?.(payload), runtimeTransitionDetailsRenderer: props.runtimeTransitionDetailsRenderer, onRuntimeTransitionDetailsOpen: (payload) => props.onRuntimeTransitionDetailsOpen?.(payload), onElementClick: (payload) => emit('element-click', payload), onTraceClick: (payload) => emit('trace-click', payload) }, slots: shellSlots, layout: props.layout, regions: props.regions, runtime: props.runtime, mode: props.mode, allowedModes: props.allowedModes, projection: props.projection, responsive: props.responsive, projectionOptions: props.projectionOptions, leftWidth: props.leftWidth, rightWidth: props.rightWidth, timeline: props.timeline, runtimeDetails: props.runtimeDetails, runtimeTraceOptions: props.runtimeTraceOptions, theme: props.theme, runtimeAppearance: props.runtimeAppearance, svgExport: props.svgExport, onThemeChange: props.onThemeChange });
|
|
174
195
|
actualMode.value = shell.getMode();
|
|
196
|
+
Object.assign(actualUi, shell.ui);
|
|
175
197
|
offMode = shell.subscribeMode((event) => {
|
|
176
198
|
actualMode.value = event.mode;
|
|
177
199
|
emit('update:mode', event.mode);
|
|
178
200
|
emit('mode-change', event);
|
|
179
201
|
});
|
|
180
202
|
offValidation = shell.subscribeValidation((event) => emit('validation', event));
|
|
203
|
+
panelSelection.value = shell.getPanelSelection();
|
|
204
|
+
offPanelSelection = shell.subscribePanelSelection((value) => { panelSelection.value = value; });
|
|
205
|
+
offSidebar = shell.subscribeSidebarChange((event) => emit('sidebar-change', event));
|
|
181
206
|
off = studio.subscribe((event) => {
|
|
182
207
|
Object.assign(state, event.state);
|
|
183
208
|
if (event.type === 'modelChanged') emit('change', studio.model, event.reason, studio.exportXml());
|
|
@@ -185,6 +210,14 @@ export const BpmnStudio = defineComponent({
|
|
|
185
210
|
if (event.type === 'scopeChanged') emit('scope-change', event.activeScopeId, event.scopePath, studio.getState());
|
|
186
211
|
});
|
|
187
212
|
});
|
|
213
|
+
// Vue slot membership is not reactive. Reconcile it after a parent update,
|
|
214
|
+
// keeping the same Teleport mount for ordinary slot-content changes.
|
|
215
|
+
onUpdated(() => {
|
|
216
|
+
const nextSlot = resolveRightSlot();
|
|
217
|
+
if (!shell || nextSlot === mountedRightSlot) return;
|
|
218
|
+
shell._setRightSlot(nextSlot);
|
|
219
|
+
mountedRightSlot = nextSlot;
|
|
220
|
+
});
|
|
188
221
|
watch(() => props.model, (value) => { if (value && value !== studio.model) studio.setModel(value); });
|
|
189
222
|
watch(() => props.xml, (value) => {
|
|
190
223
|
if (value && value !== studio.exportXml(props.engine)) studio.importXml(value, props.engine);
|
|
@@ -196,19 +229,47 @@ export const BpmnStudio = defineComponent({
|
|
|
196
229
|
if (mode) shell.setMode(mode);
|
|
197
230
|
}, { deep: true });
|
|
198
231
|
watch(() => props.projection, (value) => { if (value) shell?.setProjection(value); });
|
|
232
|
+
watch(() => props.config, (value) => {
|
|
233
|
+
if (!shell) return;
|
|
234
|
+
shell.setConfig(value);
|
|
235
|
+
Object.assign(actualUi, shell.ui);
|
|
236
|
+
}, { deep: true });
|
|
199
237
|
watch(() => props.regions, (value) => { if (value !== undefined) shell?.setRegions(value); }, { deep: true });
|
|
200
238
|
watch(() => props.theme, (value) => { if (value !== undefined) shell?.setTheme(value); });
|
|
201
239
|
watch(() => props.runtimeAppearance, (value) => { if (value !== undefined) shell?.setRuntimeAppearance(value); });
|
|
202
240
|
watch(() => props.nodeSubtitleResolver, () => shell?.refreshPresentation());
|
|
203
241
|
watch(() => [props.timeline, props.runtimeDetails, props.runtimeTraceOptions], ([timeline, runtimeDetails, runtimeTraceOptions]) => {
|
|
204
242
|
if (!shell) return;
|
|
243
|
+
if (timeline === undefined && runtimeDetails === undefined && runtimeTraceOptions === undefined) return;
|
|
205
244
|
shell.rendererOptions.timeline = timeline;
|
|
206
245
|
shell.rendererOptions.runtimeDetails = runtimeDetails;
|
|
207
246
|
shell.rendererOptions.runtimeTraceOptions = runtimeTraceOptions;
|
|
208
247
|
shell.viewer?.setDisplayOptions({ timeline, runtimeDetails, runtimeTraceOptions });
|
|
209
248
|
});
|
|
210
|
-
onBeforeUnmount(() => { offMode?.(); offValidation?.(); off?.(); shell?.destroy(); if (owned) studio.destroy(); });
|
|
211
|
-
expose({
|
|
249
|
+
onBeforeUnmount(() => { offMode?.(); offValidation?.(); offSidebar?.(); offPanelSelection?.(); off?.(); shell?.destroy(); if (owned) studio.destroy(); });
|
|
250
|
+
expose({
|
|
251
|
+
getStudio: () => studio,
|
|
252
|
+
getShell: () => shell,
|
|
253
|
+
getActions: () => shell?.actions || null,
|
|
254
|
+
getConfig: () => shell?.getConfig(),
|
|
255
|
+
setConfig: (value) => { const result = shell?.setConfig(value); if (shell) Object.assign(actualUi, shell.ui); return result; },
|
|
256
|
+
getMode: () => shell?.getMode(),
|
|
257
|
+
getAllowedModes: () => shell?.getAllowedModes() || [],
|
|
258
|
+
setMode: (value) => shell?.setMode(value) || false,
|
|
259
|
+
setAllowedModes: (value) => shell?.setAllowedModes(value) || [],
|
|
260
|
+
getSidebarState: () => shell?.getSidebarState(),
|
|
261
|
+
setSidebarCollapsed: (side, collapsed) => shell?.setSidebarCollapsed(side, collapsed) || false,
|
|
262
|
+
subscribeSidebarChange: (listener) => shell?.subscribeSidebarChange(listener) || (() => {}),
|
|
263
|
+
getPanelSelection: () => shell?.getPanelSelection(),
|
|
264
|
+
subscribePanelSelection: (listener) => shell?.subscribePanelSelection(listener) || (() => {}),
|
|
265
|
+
refreshPresentation: () => shell?.refreshPresentation(),
|
|
266
|
+
validate: () => shell?.validate() || [],
|
|
267
|
+
exportXml: (engine) => studio.exportXml(engine),
|
|
268
|
+
exportSvg: (options) => shell?.exportSvg(options),
|
|
269
|
+
openSvgExportPreview: (options) => shell?.openSvgExportPreview(options),
|
|
270
|
+
fitView: () => shell?.fitView(),
|
|
271
|
+
setTheme: (value) => shell?.setTheme(value),
|
|
272
|
+
});
|
|
212
273
|
const renderSlot = (name, target, contextRef) => {
|
|
213
274
|
if (!slots[name] || !target.value || !contextRef.value) return null;
|
|
214
275
|
return h(Teleport, { to: target.value }, slots[name]({
|
|
@@ -217,6 +278,8 @@ export const BpmnStudio = defineComponent({
|
|
|
217
278
|
actions: contextRef.value.actions,
|
|
218
279
|
state,
|
|
219
280
|
mode: actualMode.value,
|
|
281
|
+
ui: readonlyUi,
|
|
282
|
+
panelSelection: panelSelection.value,
|
|
220
283
|
}));
|
|
221
284
|
};
|
|
222
285
|
return () => h(Fragment, null, [
|
|
@@ -224,6 +287,7 @@ export const BpmnStudio = defineComponent({
|
|
|
224
287
|
renderSlot('header', headerTarget, headerContext),
|
|
225
288
|
renderSlot('header-start', headerStartTarget, headerStartContext),
|
|
226
289
|
renderSlot('header-actions', headerActionsTarget, headerActionsContext),
|
|
290
|
+
renderSlot('right', rightTarget, rightContext),
|
|
227
291
|
]);
|
|
228
292
|
},
|
|
229
293
|
});
|
package/dist/styles.css
CHANGED
|
@@ -536,6 +536,14 @@
|
|
|
536
536
|
|
|
537
537
|
/* Gateways */
|
|
538
538
|
.mb-node-gateway { display: grid; place-items: center; z-index: 7; }
|
|
539
|
+
.mb-node .mb-node-scaled-shape {
|
|
540
|
+
position: absolute;
|
|
541
|
+
left: 50%;
|
|
542
|
+
top: 50%;
|
|
543
|
+
box-sizing: border-box;
|
|
544
|
+
transform: translate(-50%, -50%) rotate(var(--mb-node-shape-rotation, 0rad)) scale(var(--mb-node-shape-scale, 1));
|
|
545
|
+
transform-origin: center;
|
|
546
|
+
}
|
|
539
547
|
.mb-gateway-shape {
|
|
540
548
|
width: 49px;
|
|
541
549
|
height: 49px;
|
|
@@ -699,6 +707,8 @@
|
|
|
699
707
|
.mb-port-right:hover { transform: translateY(-50%) scale(1.2); }
|
|
700
708
|
.mb-port-top:hover,
|
|
701
709
|
.mb-port-bottom:hover { transform: translateX(-50%) scale(1.2); }
|
|
710
|
+
.mb-port.mb-port-geometry { transform: translate(-50%, -50%); }
|
|
711
|
+
.mb-port.mb-port-geometry:hover { transform: translate(-50%, -50%) scale(1.2); }
|
|
702
712
|
.mb-node.is-connecting-source .mb-port-right { opacity: 1; border-color: var(--mb-primary); background: var(--mb-primary); box-shadow: 0 0 0 4px color-mix(in srgb, var(--nova-color-primary) 12%, transparent); }
|
|
703
713
|
.mb-port-target {
|
|
704
714
|
width: 12px;
|
|
@@ -1140,7 +1150,7 @@ textarea.property-control { resize: vertical; }
|
|
|
1140
1150
|
.property-branch-target small { color: var(--nova-color-text-muted); font-size: var(--nova-font-size-xs, 10px); line-height: 14px; }.property-branch-target strong { overflow: hidden; color: var(--nova-color-text-secondary); font-size: var(--nova-font-size-sm, 12px); line-height: 18px; font-weight: var(--nova-font-weight-semibold, 600); text-overflow: ellipsis; white-space: nowrap; }.property-branch-target > span { color: var(--nova-color-primary); font-size: var(--nova-font-size-sm, 12px); }
|
|
1141
1151
|
|
|
1142
1152
|
/* @bpmn-nova/internal/studio */
|
|
1143
|
-
.nova-studio-shell { --nova-left-width: 244px; --nova-right-width: 360px; --nova-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; --nova-font-size-xs: 10px; --nova-font-size-sm: 12px; --nova-font-size-md: 14px; --nova-font-size-lg: 16px; --nova-font-weight-regular: 400; --nova-font-weight-medium: 500; --nova-font-weight-semibold: 600; --nova-font-weight-bold: 700; position: relative; isolation: isolate; width: 100%; height: 100%; min-height: 520px; overflow: hidden; display: grid; grid-template-rows:
|
|
1153
|
+
.nova-studio-shell { --nova-left-width: 244px; --nova-right-width: 360px; --nova-control-height: 32px; --nova-header-height: 58px; --nova-control-font-size: 12px; --nova-control-padding-inline: 10px; --nova-mode-control-padding-inline: 12px; --nova-control-icon-size: 14px; --nova-control-radius: 7px; --nova-mode-control-height: 36px; --nova-mode-button-height: 28px; --nova-projection-button-height: 26px; --nova-icon-button-size: 31px; --nova-split-caret-width: 27px; --nova-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; --nova-font-size-xs: 10px; --nova-font-size-sm: 12px; --nova-font-size-md: 14px; --nova-font-size-lg: 16px; --nova-font-weight-regular: 400; --nova-font-weight-medium: 500; --nova-font-weight-semibold: 600; --nova-font-weight-bold: 700; position: relative; isolation: isolate; width: 100%; height: 100%; min-height: 520px; overflow: hidden; display: grid; grid-template-rows: var(--nova-header-height) minmax(0, 1fr); color: var(--nova-color-text, #263148); background: var(--nova-color-canvas, #f6f7fb); font-family: var(--nova-font-family); }
|
|
1144
1154
|
.nova-studio-shell.is-header-hidden { grid-template-rows: minmax(0, 1fr); }
|
|
1145
1155
|
.nova-studio-header[hidden], .nova-studio-left[hidden], .nova-studio-right[hidden], .nova-studio-statusbar[hidden] { display: none !important; }
|
|
1146
1156
|
.nova-studio-header { min-width: 0; border-bottom: 1px solid var(--nova-color-divider, #e4e8f0); background: var(--nova-color-surface, #fff); display: grid; grid-template-columns: minmax(180px, auto) auto minmax(0, 1fr); align-items: center; gap: 18px; padding: 0 12px 0 14px; }
|
|
@@ -1150,20 +1160,22 @@ textarea.property-control { resize: vertical; }
|
|
|
1150
1160
|
.nova-studio-brand-copy strong { color: var(--nova-tone-primary-foreground, #3443b8); font-size: var(--nova-font-size-md); line-height: 20px; font-weight: var(--nova-font-weight-bold); letter-spacing: -.01em; }
|
|
1151
1161
|
.nova-studio-brand-copy span { max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--nova-color-text-muted, #7c8799); font-size: var(--nova-font-size-xs); line-height: 14px; }
|
|
1152
1162
|
.nova-studio-center-controls { min-width: 0; justify-self: center; display: flex; align-items: center; gap: 8px; }
|
|
1153
|
-
.nova-studio-mode-switch { height:
|
|
1154
|
-
.nova-studio-mode-switch button { height:
|
|
1163
|
+
.nova-studio-mode-switch { height: var(--nova-mode-control-height); border: 1px solid var(--nova-color-divider, #e6e8ef); border-radius: calc(var(--nova-control-radius) + 3px); padding: 3px; display: flex; align-items: center; gap: 2px; background: var(--nova-color-surface-muted, #f4f5f8); box-shadow: inset 0 1px 2px rgba(35,43,68,.03); }
|
|
1164
|
+
.nova-studio-mode-switch button { height: var(--nova-mode-button-height); border: 0; border-radius: var(--nova-control-radius); padding: 0 var(--nova-mode-control-padding-inline); display: flex; align-items: center; gap: 5px; color: var(--nova-color-text-muted, #8a93a4); background: transparent; font-size: var(--nova-control-font-size); line-height: 18px; white-space: nowrap; cursor: pointer; }
|
|
1155
1165
|
.nova-studio-mode-switch button.is-active { color: var(--nova-tone-primary-foreground, #4f5dd1); background: var(--nova-color-surface, #fff); font-weight: var(--nova-font-weight-semibold); box-shadow: var(--nova-shadow-sm); }
|
|
1156
|
-
.nova-studio-projection-switch { height:
|
|
1166
|
+
.nova-studio-projection-switch { height: var(--nova-control-height); border: 1px solid var(--nova-color-border, #e2e6ef); border-radius: calc(var(--nova-control-radius) + 1px); padding: 2px; display: flex; align-items: center; gap: 2px; background: var(--nova-color-surface, #fff); }
|
|
1157
1167
|
.nova-studio-projection-switch.is-hidden { display: none; }
|
|
1158
|
-
.nova-studio-projection-switch button { height:
|
|
1168
|
+
.nova-studio-projection-switch button { height: var(--nova-projection-button-height); border: 0; border-radius: max(0px, calc(var(--nova-control-radius) - 1px)); padding: 0 max(0px, calc(var(--nova-control-padding-inline) - 1px)); color: var(--nova-color-text-muted, #7c8799); background: transparent; font-size: var(--nova-control-font-size); line-height: 18px; white-space: nowrap; cursor: pointer; }
|
|
1159
1169
|
.nova-studio-projection-switch button:hover { color: var(--nova-tone-primary-foreground, #4d59ca); background: var(--nova-color-primary-soft, #f4f5ff); }.nova-studio-projection-switch button.is-active { color: var(--nova-tone-primary-foreground, #4654c8); background: var(--nova-color-primary-soft, #eef0ff); font-weight: var(--nova-font-weight-semibold); }
|
|
1160
1170
|
.nova-studio-tools { min-width: 0; overflow: visible; display: flex; align-items: center; justify-content: flex-end; gap: 5px; }
|
|
1161
1171
|
.nova-studio-tools::-webkit-scrollbar { display: none; }
|
|
1162
|
-
.nova-studio-header-actions { min-width: 0; display: flex; align-items: center; justify-content: flex-end; gap: 5px; }
|
|
1163
|
-
.nova-studio-
|
|
1164
|
-
.nova-studio-
|
|
1172
|
+
.nova-studio-header-actions { min-width: 0; min-height: var(--nova-control-height); display: flex; align-items: center; justify-content: flex-end; gap: 5px; }
|
|
1173
|
+
.nova-studio-header .nova-icon { width: var(--nova-control-icon-size); height: var(--nova-control-icon-size); }
|
|
1174
|
+
.nova-studio-header .nova-icon.nova-icon-xs { width: max(12px, calc(var(--nova-control-icon-size) - 2px)); height: max(12px, calc(var(--nova-control-icon-size) - 2px)); }
|
|
1175
|
+
.nova-studio-tool-divider { flex: none; width: 1px; height: calc(var(--nova-control-height) - 12px); margin: 0 2px; background: var(--nova-color-divider, #e4e8f0); }
|
|
1176
|
+
.nova-studio-tool { flex: none; height: var(--nova-control-height); min-height: var(--nova-control-height); border: 1px solid var(--nova-color-border, #e2e6ef); border-radius: var(--nova-control-radius); padding: 0 var(--nova-control-padding-inline); display: inline-flex; align-items: center; justify-content: center; gap: 5px; color: var(--nova-color-text-secondary, #4c586d); background: var(--nova-color-surface, #fff); font-size: var(--nova-control-font-size); line-height: 18px; white-space: nowrap; cursor: pointer; }
|
|
1165
1177
|
.nova-studio-tool:focus { outline: none; }.nova-studio-tool:focus-visible { outline: 2px solid var(--nova-color-focus-ring, #aeb7ff); outline-offset: 1px; }
|
|
1166
|
-
.nova-studio-tool.is-icon { width:
|
|
1178
|
+
.nova-studio-tool.is-icon { width: var(--nova-icon-button-size); padding: 0; font-size: calc(var(--nova-control-font-size) + 2px); }
|
|
1167
1179
|
.nova-studio-tool:hover, .nova-studio-tool.is-active { border-color: var(--nova-tone-primary-border, #bec6ff); color: var(--nova-tone-primary-foreground, #3948c5); background: var(--nova-color-primary-soft, #f2f4ff); }
|
|
1168
1180
|
.nova-studio-tool.is-magic { border-color: var(--nova-tone-primary-border, #d9ddff); color: var(--nova-tone-primary-foreground, #4654c8); background: var(--nova-color-primary-soft, #f4f5ff); font-weight: var(--nova-font-weight-semibold); }
|
|
1169
1181
|
.nova-studio-tool.is-primary { border-color: var(--nova-color-primary); color: var(--nova-color-text-inverse, #fff); background: var(--nova-color-primary, #5361d6); }
|
|
@@ -1182,8 +1194,8 @@ textarea.property-control { resize: vertical; }
|
|
|
1182
1194
|
.nova-studio-export-option.is-hidden { display: none; }
|
|
1183
1195
|
.nova-studio-import-input { display: none; }
|
|
1184
1196
|
.nova-studio-beautify-split { position: relative; flex: none; display: flex; }
|
|
1185
|
-
.nova-studio-beautify-split .nova-studio-beautify { border-radius:
|
|
1186
|
-
.nova-studio-beautify-split .nova-studio-beautify-caret { width:
|
|
1197
|
+
.nova-studio-beautify-split .nova-studio-beautify { border-radius: var(--nova-control-radius) 0 0 var(--nova-control-radius); }
|
|
1198
|
+
.nova-studio-beautify-split .nova-studio-beautify-caret { width: var(--nova-split-caret-width); border-left: 0; border-radius: 0 var(--nova-control-radius) var(--nova-control-radius) 0; padding: 0; }
|
|
1187
1199
|
.nova-studio-beautify-menu { position: absolute; z-index: 30; top: calc(100% + 8px); right: 0; width: 224px; border: 1px solid var(--nova-color-border, #e5e7ee); border-radius: 12px; padding: 12px; color: var(--nova-color-text-secondary, #3d4659); background: var(--nova-color-surface, #fff); box-shadow: var(--nova-shadow-lg); }
|
|
1188
1200
|
.nova-studio-beautify-menu.is-hidden, .nova-studio-canvas-floating-head.is-hidden, .is-hidden-by-mode { display: none !important; }
|
|
1189
1201
|
.nova-studio-beautify-title { margin-bottom: 9px; font-size: 12px; font-weight: 700; line-height: 18px; }
|
|
@@ -1429,7 +1441,7 @@ textarea.property-control { resize: vertical; }
|
|
|
1429
1441
|
.nova-studio-brand-copy span { display: none; }
|
|
1430
1442
|
}
|
|
1431
1443
|
@media (max-width: 720px) {
|
|
1432
|
-
.nova-studio-shell { grid-template-rows:
|
|
1444
|
+
.nova-studio-shell { grid-template-rows: var(--nova-header-height) minmax(0,1fr); }
|
|
1433
1445
|
.nova-studio-shell.is-header-hidden { grid-template-rows: minmax(0, 1fr); }
|
|
1434
1446
|
.nova-studio-header { grid-template-columns: auto minmax(0,1fr); padding-left: 9px; }
|
|
1435
1447
|
.nova-studio-brand-copy { display: none; }
|
|
@@ -1442,3 +1454,51 @@ textarea.property-control { resize: vertical; }
|
|
|
1442
1454
|
.nova-studio-body.is-timeline .nova-studio-statusbar { display: none; }
|
|
1443
1455
|
}
|
|
1444
1456
|
|
|
1457
|
+
/* The managed Shell keeps three tracks, including zero-width collapsed tracks.
|
|
1458
|
+
Legacy base layout above remains usable without the sidebar controller. */
|
|
1459
|
+
.nova-studio-shell .nova-studio-body.is-managed-sidebars {
|
|
1460
|
+
position: relative;
|
|
1461
|
+
grid-template-columns: var(--nova-sidebar-left-track, 0px) minmax(0, 1fr) var(--nova-sidebar-right-track, 0px);
|
|
1462
|
+
}
|
|
1463
|
+
.nova-studio-body.is-managed-sidebars.is-sidebar-animated {
|
|
1464
|
+
transition: grid-template-columns 200ms cubic-bezier(0.2, 0, 0, 1);
|
|
1465
|
+
}
|
|
1466
|
+
.nova-studio-shell .is-managed-sidebars > .nova-studio-left, .nova-studio-shell .is-managed-sidebars > .nova-studio-right {
|
|
1467
|
+
display: block; min-width: 0; min-height: 0; overflow: hidden; border: 0;
|
|
1468
|
+
}
|
|
1469
|
+
.is-managed-sidebars > .nova-studio-left { grid-column: 1; grid-row: 1; }
|
|
1470
|
+
.is-managed-sidebars > .nova-studio-canvas-column { grid-column: 2; grid-row: 1; }
|
|
1471
|
+
.is-managed-sidebars > .nova-studio-right { grid-column: 3; grid-row: 1; }
|
|
1472
|
+
.nova-studio-sidebar-content {
|
|
1473
|
+
box-sizing: border-box; height: 100%; min-width: 0; min-height: 0;
|
|
1474
|
+
scrollbar-width: thin; scrollbar-color: var(--nova-color-border-strong, #d9dee7) transparent;
|
|
1475
|
+
}
|
|
1476
|
+
.nova-studio-left-content { overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; border-right: 1px solid var(--nova-color-divider); }
|
|
1477
|
+
.nova-studio-right-content { border-left: 1px solid var(--nova-color-divider); }
|
|
1478
|
+
.nova-studio-right-content[data-layout="flex"] { display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
|
|
1479
|
+
.nova-studio-right-content[data-layout="scroll"] { display: block; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; }
|
|
1480
|
+
.nova-studio-right-content[data-layout="flex"].nova-properties > .properties-head { flex: none; position: relative; }
|
|
1481
|
+
.nova-studio-right-content[data-layout="flex"].nova-properties > .properties-body { flex: 1; min-height: 0; overflow: auto; overscroll-behavior: contain; }
|
|
1482
|
+
.nova-studio-right-content[data-layout="scroll"].nova-properties > .properties-head { position: relative; }
|
|
1483
|
+
.nova-studio-right-content[data-layout="flex"] > .nova-studio-readonly-panel { display: flex; flex-direction: column; flex: 1; min-height: 0; }
|
|
1484
|
+
.nova-studio-right-content[data-layout="flex"] .nova-studio-readonly-header { flex: none; }
|
|
1485
|
+
.nova-studio-right-content[data-layout="flex"] .nova-studio-readonly-body { flex: 1; min-height: 0; overflow: auto; align-content: start; overscroll-behavior: contain; }
|
|
1486
|
+
.nova-studio-sidebar-content::-webkit-scrollbar, .nova-studio-right-content .properties-body::-webkit-scrollbar, .nova-studio-readonly-body::-webkit-scrollbar { width: 4px; height: 4px; }
|
|
1487
|
+
.nova-studio-sidebar-content::-webkit-scrollbar-thumb, .nova-studio-right-content .properties-body::-webkit-scrollbar-thumb, .nova-studio-readonly-body::-webkit-scrollbar-thumb { border-radius: 999px; background: var(--nova-color-border-strong, #d9dee7); }
|
|
1488
|
+
.nova-studio-sidebar-toggle {
|
|
1489
|
+
position: absolute; z-index: 8; top: 50%; transform: translateY(-50%);
|
|
1490
|
+
box-sizing: border-box; width: 22px; height: 44px; padding: 0;
|
|
1491
|
+
display: grid; place-items: center; border: 1px solid var(--nova-color-border);
|
|
1492
|
+
border-radius: 7px; background: var(--nova-color-surface); color: var(--nova-color-text-muted);
|
|
1493
|
+
box-shadow: var(--nova-shadow-sm); font: 22px/1 sans-serif; cursor: pointer;
|
|
1494
|
+
}
|
|
1495
|
+
.nova-studio-sidebar-toggle[hidden] { display: none; }
|
|
1496
|
+
.nova-studio-sidebar-toggle.is-left { left: max(0px, calc(var(--nova-sidebar-left-track, 0px) - 11px)); }
|
|
1497
|
+
.nova-studio-sidebar-toggle.is-right { right: max(0px, calc(var(--nova-sidebar-right-track, 0px) - 11px)); }
|
|
1498
|
+
.is-sidebar-animated > .nova-studio-sidebar-toggle { transition: left 200ms cubic-bezier(0.2, 0, 0, 1), right 200ms cubic-bezier(0.2, 0, 0, 1); }
|
|
1499
|
+
.nova-studio-sidebar-toggle:hover { color: var(--nova-color-primary); background: var(--nova-color-primary-soft); }
|
|
1500
|
+
.nova-studio-sidebar-toggle:focus-visible { outline: 2px solid var(--nova-color-focus-ring); outline-offset: 2px; }
|
|
1501
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1502
|
+
.nova-studio-body.is-managed-sidebars.is-sidebar-animated, .is-sidebar-animated > .nova-studio-sidebar-toggle { transition: none; }
|
|
1503
|
+
}
|
|
1504
|
+
|