@jxsuite/studio 0.28.2 → 0.28.4
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/studio.js +86144 -83971
- package/dist/studio.js.map +229 -219
- package/package.json +5 -5
- package/src/browse/browse-modal.ts +9 -3
- package/src/browse/browse.ts +139 -82
- package/src/canvas/canvas-diff.ts +35 -18
- package/src/canvas/canvas-helpers.ts +55 -21
- package/src/canvas/canvas-live-render.ts +140 -89
- package/src/canvas/canvas-render.ts +127 -82
- package/src/canvas/canvas-utils.ts +68 -42
- package/src/canvas/nested-site-style.ts +15 -7
- package/src/editor/component-inline-edit.ts +90 -51
- package/src/editor/content-inline-edit.ts +75 -72
- package/src/editor/context-menu.ts +125 -71
- package/src/editor/convert-targets.ts +17 -13
- package/src/editor/convert-to-component.ts +51 -28
- package/src/editor/convert-to-repeater.ts +44 -19
- package/src/editor/inline-edit.ts +107 -52
- package/src/editor/inline-format.ts +128 -60
- package/src/editor/insertion-helper.ts +26 -14
- package/src/editor/shortcuts.ts +89 -43
- package/src/editor/slash-menu.ts +41 -26
- package/src/files/components.ts +9 -7
- package/src/files/file-ops.ts +53 -33
- package/src/files/files.ts +246 -143
- package/src/format/constraints.ts +25 -15
- package/src/format/format-host.ts +33 -10
- package/src/github/github-auth.ts +24 -14
- package/src/github/github-publish.ts +20 -14
- package/src/new-project/new-project-modal.ts +27 -18
- package/src/panels/activity-bar.ts +27 -26
- package/src/panels/ai-panel.ts +100 -44
- package/src/panels/block-action-bar.ts +84 -52
- package/src/panels/canvas-dnd.ts +47 -28
- package/src/panels/data-explorer.ts +24 -11
- package/src/panels/dnd.ts +148 -100
- package/src/panels/editors.ts +54 -37
- package/src/panels/elements-panel.ts +23 -13
- package/src/panels/events-panel.ts +48 -38
- package/src/panels/git-panel.ts +95 -60
- package/src/panels/head-panel.ts +138 -84
- package/src/panels/imports-panel.ts +66 -29
- package/src/panels/layers-panel.ts +80 -36
- package/src/panels/left-panel.ts +86 -53
- package/src/panels/overlays.ts +36 -19
- package/src/panels/panel-events.ts +48 -27
- package/src/panels/panel-scheduler.ts +41 -23
- package/src/panels/preview-render.ts +26 -26
- package/src/panels/properties-panel.ts +155 -118
- package/src/panels/pseudo-preview.ts +31 -15
- package/src/panels/quick-search.ts +28 -13
- package/src/panels/right-panel.ts +35 -24
- package/src/panels/shared.ts +46 -29
- package/src/panels/signals-panel.ts +303 -186
- package/src/panels/statusbar.ts +27 -12
- package/src/panels/style-inputs.ts +30 -24
- package/src/panels/style-panel.ts +130 -90
- package/src/panels/style-utils.ts +63 -23
- package/src/panels/stylebook-layers-panel.ts +27 -30
- package/src/panels/stylebook-panel.ts +140 -72
- package/src/panels/tab-strip.ts +21 -10
- package/src/panels/toolbar.ts +52 -40
- package/src/panels/welcome-screen.ts +1 -1
- package/src/platform.ts +2 -1
- package/src/platforms/devserver.ts +221 -105
- package/src/recent-projects.ts +15 -7
- package/src/resize-edges.ts +7 -5
- package/src/services/cem-export.ts +73 -57
- package/src/services/code-services.ts +52 -25
- package/src/services/monaco-setup.ts +5 -5
- package/src/settings/content-types-editor.ts +133 -62
- package/src/settings/css-vars-editor.ts +31 -14
- package/src/settings/defs-editor.ts +125 -63
- package/src/settings/general-settings.ts +11 -6
- package/src/settings/head-editor.ts +30 -15
- package/src/settings/schema-field-ui.ts +75 -35
- package/src/settings/settings-modal.ts +35 -17
- package/src/site-context.ts +80 -47
- package/src/state.ts +98 -65
- package/src/store.ts +42 -23
- package/src/studio.ts +165 -144
- package/src/tabs/tab.ts +51 -45
- package/src/tabs/transact.ts +230 -109
- package/src/types.ts +60 -51
- package/src/ui/button-group.ts +3 -2
- package/src/ui/color-selector.ts +33 -15
- package/src/ui/expression-editor.ts +139 -86
- package/src/ui/field-input.ts +35 -18
- package/src/ui/field-row.ts +1 -1
- package/src/ui/icons.ts +5 -6
- package/src/ui/layers.ts +33 -26
- package/src/ui/media-picker.ts +13 -7
- package/src/ui/panel-resize.ts +39 -20
- package/src/ui/spectrum.ts +4 -2
- package/src/ui/unit-selector.ts +20 -11
- package/src/ui/value-selector.ts +13 -7
- package/src/ui/widgets.ts +34 -18
- package/src/utils/canvas-media.ts +66 -33
- package/src/utils/edit-display.ts +43 -44
- package/src/utils/google-fonts.ts +10 -6
- package/src/utils/inherited-style.ts +22 -8
- package/src/utils/studio-utils.ts +88 -45
- package/src/view.ts +9 -5
- package/src/workspace/workspace.ts +49 -29
package/src/panels/statusbar.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
/** Statusbar — status message display for Jx Studio */
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { getNodeAtPath, nodeLabel, renderOnly, statusbarEl, updateSession } from "../store";
|
|
5
5
|
import { effect, effectScope } from "../reactivity";
|
|
6
6
|
import { activeTab } from "../workspace/workspace";
|
|
7
7
|
import type { JxPath } from "../state";
|
|
8
|
+
import type { EffectScope } from "@vue/reactivity";
|
|
8
9
|
|
|
9
10
|
// ─── Module state ────────────────────────────────────────────────────────────
|
|
10
11
|
|
|
@@ -12,7 +13,7 @@ let statusMsg = "";
|
|
|
12
13
|
/** @type {ReturnType<typeof setTimeout> | undefined} */
|
|
13
14
|
let statusTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
14
15
|
let _rerender: (() => void) | null = null;
|
|
15
|
-
let _scope:
|
|
16
|
+
let _scope: EffectScope | null = null;
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Register the callback used to re-render the statusbar. Called once from studio.js during init.
|
|
@@ -29,7 +30,9 @@ export function mountStatusbar() {
|
|
|
29
30
|
_scope.run(() => {
|
|
30
31
|
effect(() => {
|
|
31
32
|
const tab = activeTab.value;
|
|
32
|
-
if (!tab)
|
|
33
|
+
if (!tab) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
33
36
|
// Track relevant reactive properties
|
|
34
37
|
void tab.doc.document;
|
|
35
38
|
void tab.doc.mode;
|
|
@@ -52,14 +55,16 @@ export function unmountStatusbar() {
|
|
|
52
55
|
|
|
53
56
|
/** @param {string} text */
|
|
54
57
|
function esc(text: string) {
|
|
55
|
-
return String(text).
|
|
58
|
+
return String(text).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
56
59
|
}
|
|
57
60
|
|
|
58
61
|
/** Render the statusbar content. */
|
|
59
62
|
export function renderStatusbar() {
|
|
60
63
|
const tab = activeTab.value;
|
|
61
64
|
const parts = [];
|
|
62
|
-
if (tab?.doc.mode === "content")
|
|
65
|
+
if (tab?.doc.mode === "content") {
|
|
66
|
+
parts.push("Content Mode");
|
|
67
|
+
}
|
|
63
68
|
if (tab?.session.selection?.length) {
|
|
64
69
|
const sel = tab.session.selection as JxPath;
|
|
65
70
|
const node = getNodeAtPath(tab.doc.document, sel);
|
|
@@ -69,7 +74,11 @@ export function renderStatusbar() {
|
|
|
69
74
|
for (let i = 0; i < sel.length; i += 2) {
|
|
70
75
|
const subPath = sel.slice(0, i + 2);
|
|
71
76
|
const childNode = getNodeAtPath(tab.doc.document, subPath);
|
|
72
|
-
const
|
|
77
|
+
const fallbackTag = childNode?.tag;
|
|
78
|
+
const label =
|
|
79
|
+
childNode?.tagName ||
|
|
80
|
+
(typeof fallbackTag === "string" ? fallbackTag : "") ||
|
|
81
|
+
`[${sel[i + 1]}]`;
|
|
73
82
|
const dataPath = JSON.stringify(subPath);
|
|
74
83
|
pathSegments.push(
|
|
75
84
|
`<span class="sb-path-seg" data-path='${esc(dataPath)}'>${esc(label)}</span>`,
|
|
@@ -78,24 +87,30 @@ export function renderStatusbar() {
|
|
|
78
87
|
parts.push(`Path: ${pathSegments.join(' <span class="sb-path-sep">></span> ')}`);
|
|
79
88
|
} else if (tab?.session.ui.stylebookSelection) {
|
|
80
89
|
const sel = tab.session.ui.stylebookSelection;
|
|
81
|
-
parts.push(`Style: ${esc(sel.
|
|
90
|
+
parts.push(`Style: ${esc(sel.replaceAll(" ", " > "))}`);
|
|
91
|
+
}
|
|
92
|
+
if (statusMsg) {
|
|
93
|
+
parts.push(esc(statusMsg));
|
|
82
94
|
}
|
|
83
|
-
if (statusMsg) parts.push(esc(statusMsg));
|
|
84
95
|
statusbarEl.innerHTML = parts.join(" | ") || "Jx Studio";
|
|
85
96
|
}
|
|
86
97
|
|
|
87
98
|
/** @param {Event} e */
|
|
88
99
|
function _onStatusbarClick(e: Event) {
|
|
89
100
|
const target = e.target as HTMLElement;
|
|
90
|
-
if (!target.classList.contains("sb-path-seg"))
|
|
101
|
+
if (!target.classList.contains("sb-path-seg")) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
91
104
|
const pathStr = target.dataset.path;
|
|
92
|
-
if (!pathStr)
|
|
105
|
+
if (!pathStr) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
93
108
|
try {
|
|
94
109
|
const path = JSON.parse(pathStr);
|
|
95
110
|
updateSession({ selection: path });
|
|
96
111
|
renderOnly("leftPanel", "rightPanel", "canvas");
|
|
97
112
|
} catch {
|
|
98
|
-
//
|
|
113
|
+
// Ignore
|
|
99
114
|
}
|
|
100
115
|
}
|
|
101
116
|
|
|
@@ -105,7 +120,7 @@ function _onStatusbarClick(e: Event) {
|
|
|
105
120
|
* @param {string} msg
|
|
106
121
|
* @param {number} [duration]
|
|
107
122
|
*/
|
|
108
|
-
export function statusMessage(msg: string, duration
|
|
123
|
+
export function statusMessage(msg: string, duration = 3000) {
|
|
109
124
|
statusMsg = msg;
|
|
110
125
|
_rerender?.();
|
|
111
126
|
clearTimeout(statusTimeout);
|
|
@@ -5,15 +5,15 @@ import { html } from "lit-html";
|
|
|
5
5
|
import { live } from "lit-html/directives/live.js";
|
|
6
6
|
import { debouncedStyleCommit } from "../store";
|
|
7
7
|
import { activeTab } from "../workspace/workspace";
|
|
8
|
-
import {
|
|
8
|
+
import { mutateUpdateStyle, transactDoc } from "../tabs/transact";
|
|
9
9
|
import { widgetForType as _widgetForType } from "../ui/widgets";
|
|
10
|
-
import {
|
|
10
|
+
import { friendlyNameToVar, kebabToLabel, varDisplayName } from "../utils/studio-utils";
|
|
11
11
|
import {
|
|
12
12
|
TYPO_PREVIEW_PROPS,
|
|
13
|
+
camelToKebab,
|
|
13
14
|
currentFontFamily,
|
|
14
|
-
getFontVars,
|
|
15
15
|
getCssInitialMap,
|
|
16
|
-
|
|
16
|
+
getFontVars,
|
|
17
17
|
} from "./style-utils";
|
|
18
18
|
|
|
19
19
|
/**
|
|
@@ -38,7 +38,7 @@ export function renderKeywordInput(
|
|
|
38
38
|
? kebabToLabel(v)
|
|
39
39
|
: v.replace(/^./, (c: string) => c.toUpperCase());
|
|
40
40
|
const style = isTypoPreview ? `${cssProp}: ${v};${font ? ` font-family: ${font}` : ""}` : "";
|
|
41
|
-
return { value: v
|
|
41
|
+
return { label, style, value: v };
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
return html`<jx-value-selector
|
|
@@ -54,16 +54,17 @@ export function renderKeywordInput(
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
* @param {
|
|
58
|
-
* string) => void} onChange
|
|
57
|
+
* @param {Record<string, unknown>} entry @param {string} prop @param {string | number | undefined}
|
|
58
|
+
* value @param {(value: string) => void} onChange
|
|
59
59
|
*/
|
|
60
60
|
export function renderSelectInput(
|
|
61
|
-
entry:
|
|
61
|
+
entry: Record<string, unknown>,
|
|
62
62
|
prop: string,
|
|
63
|
-
value: string,
|
|
63
|
+
value: string | number | undefined,
|
|
64
64
|
onChange: (value: string) => void,
|
|
65
65
|
) {
|
|
66
|
-
|
|
66
|
+
const options = Array.isArray(entry.enum) ? (entry.enum as string[]) : [];
|
|
67
|
+
return renderKeywordInput(options, prop, String(value ?? ""), onChange);
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
/** @param {{ title: string; value: string }} preset @param {(value: string) => void} onChange */
|
|
@@ -87,13 +88,17 @@ function handleFontSelection(
|
|
|
87
88
|
presets: { title: string; value: string }[],
|
|
88
89
|
onChange: (value: string) => void,
|
|
89
90
|
) {
|
|
90
|
-
if (!val)
|
|
91
|
+
if (!val) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
91
94
|
if (val.startsWith("__preset__:")) {
|
|
92
95
|
const title = val.slice("__preset__:".length);
|
|
93
96
|
const preset = presets.find(
|
|
94
97
|
(/** @type {{ title: string; value: string }} */ p) => p.title === title,
|
|
95
98
|
);
|
|
96
|
-
if (preset)
|
|
99
|
+
if (preset) {
|
|
100
|
+
handleFontPresetSelection(preset, onChange);
|
|
101
|
+
}
|
|
97
102
|
return;
|
|
98
103
|
}
|
|
99
104
|
if (val.startsWith("--")) {
|
|
@@ -132,9 +137,9 @@ export function buildFontOptions(
|
|
|
132
137
|
) {
|
|
133
138
|
const opts: ({ value: string; label: string; style: string } | { divider: true })[] =
|
|
134
139
|
fontVars.map((fv) => ({
|
|
135
|
-
value: fv.name,
|
|
136
140
|
label: varDisplayName(fv.name, "--font-"),
|
|
137
141
|
style: `font-family: ${fv.value}`,
|
|
142
|
+
value: fv.name,
|
|
138
143
|
}));
|
|
139
144
|
const unadded = presets.filter(
|
|
140
145
|
(/** @type {{ title: string; value: string }} */ p) =>
|
|
@@ -143,12 +148,14 @@ export function buildFontOptions(
|
|
|
143
148
|
fv.name === friendlyNameToVar(p.title, "--font-"),
|
|
144
149
|
),
|
|
145
150
|
);
|
|
146
|
-
if (unadded.length > 0 && opts.length > 0)
|
|
151
|
+
if (unadded.length > 0 && opts.length > 0) {
|
|
152
|
+
opts.push({ divider: true });
|
|
153
|
+
}
|
|
147
154
|
for (const p of unadded) {
|
|
148
155
|
opts.push({
|
|
149
|
-
value: "__preset__:" + p.title,
|
|
150
156
|
label: p.title,
|
|
151
157
|
style: `font-family: ${p.value}`,
|
|
158
|
+
value: `__preset__:${p.title}`,
|
|
152
159
|
});
|
|
153
160
|
}
|
|
154
161
|
return opts;
|
|
@@ -163,19 +170,18 @@ export function buildFontOptions(
|
|
|
163
170
|
* @param {string} prop @param {string} value @param {(value: string) => void} onChange
|
|
164
171
|
*/
|
|
165
172
|
export function renderComboboxInput(
|
|
166
|
-
entry:
|
|
167
|
-
enum?: string[];
|
|
168
|
-
examples?: string[];
|
|
169
|
-
presets?: { title: string; value: string }[];
|
|
170
|
-
},
|
|
173
|
+
entry: Record<string, unknown>,
|
|
171
174
|
prop: string,
|
|
172
|
-
|
|
175
|
+
rawValue: string | number | undefined,
|
|
173
176
|
onChange: (value: string) => void,
|
|
174
177
|
) {
|
|
178
|
+
const value = String(rawValue ?? "");
|
|
175
179
|
const cssInitialMap = getCssInitialMap();
|
|
176
180
|
const fontVars = prop === "fontFamily" ? getFontVars() : [];
|
|
177
|
-
const presets = entry.presets
|
|
178
|
-
|
|
181
|
+
const presets = Array.isArray(entry.presets)
|
|
182
|
+
? (entry.presets as { title: string; value: string }[])
|
|
183
|
+
: [];
|
|
184
|
+
const examples = Array.isArray(entry.examples) ? (entry.examples as string[]) : [];
|
|
179
185
|
|
|
180
186
|
if (prop === "fontFamily") {
|
|
181
187
|
const varMatch = typeof value === "string" && value.match(/^var\((--[^)]+)\)$/);
|
|
@@ -225,7 +231,7 @@ export function widgetForType(
|
|
|
225
231
|
const cssInitialMap = getCssInitialMap();
|
|
226
232
|
return _widgetForType(type, entry, prop, value, onCommit as (val: string | number) => void, {
|
|
227
233
|
placeholder: opts.placeholder || cssInitialMap.get(prop) || "",
|
|
228
|
-
renderSelect: renderSelectInput,
|
|
229
234
|
renderCombobox: renderComboboxInput,
|
|
235
|
+
renderSelect: renderSelectInput,
|
|
230
236
|
});
|
|
231
237
|
}
|