@jxsuite/studio 0.23.2 → 0.24.0
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 +2230 -714
- package/dist/studio.js.map +121 -104
- package/package.json +8 -7
- package/src/browse/{browse-modal.js → browse-modal.ts} +9 -10
- package/src/browse/{browse.js → browse.ts} +109 -84
- package/src/canvas/{canvas-diff.js → canvas-diff.ts} +42 -35
- package/src/canvas/{canvas-helpers.js → canvas-helpers.ts} +25 -19
- package/src/canvas/{canvas-live-render.js → canvas-live-render.ts} +53 -63
- package/src/canvas/{canvas-render.js → canvas-render.ts} +110 -121
- package/src/canvas/{canvas-utils.js → canvas-utils.ts} +48 -44
- package/src/canvas/{nested-site-style.js → nested-site-style.ts} +3 -3
- package/src/editor/{component-inline-edit.js → component-inline-edit.ts} +45 -33
- package/src/editor/{content-inline-edit.js → content-inline-edit.ts} +27 -45
- package/src/editor/{context-menu.js → context-menu.ts} +38 -36
- package/src/editor/{convert-targets.js → convert-targets.ts} +15 -12
- package/src/editor/{convert-to-component.js → convert-to-component.ts} +25 -26
- package/src/editor/{convert-to-repeater.js → convert-to-repeater.ts} +60 -40
- package/src/editor/{inline-edit.js → inline-edit.ts} +91 -55
- package/src/editor/{inline-format.js → inline-format.ts} +46 -51
- package/src/editor/{insertion-helper.js → insertion-helper.ts} +47 -63
- package/src/editor/{shortcuts.js → shortcuts.ts} +45 -31
- package/src/editor/{slash-menu.js → slash-menu.ts} +34 -35
- package/src/files/{components.js → components.ts} +12 -15
- package/src/files/{file-ops.js → file-ops.ts} +36 -29
- package/src/files/{files.js → files.ts} +102 -84
- package/src/github/{github-auth.js → github-auth.ts} +3 -3
- package/src/github/{github-publish.js → github-publish.ts} +20 -18
- package/src/markdown/{md-allowlist.js → md-allowlist.ts} +5 -2
- package/src/markdown/{md-convert.js → md-convert.ts} +146 -169
- package/src/new-project/{new-project-modal.js → new-project-modal.ts} +16 -18
- package/src/panels/{activity-bar.js → activity-bar.ts} +26 -34
- package/src/panels/{ai-panel.js → ai-panel.ts} +30 -30
- package/src/panels/{block-action-bar.js → block-action-bar.ts} +64 -73
- package/src/panels/{canvas-dnd.js → canvas-dnd.ts} +44 -49
- package/src/panels/{data-explorer.js → data-explorer.ts} +21 -15
- package/src/panels/dnd.ts +417 -0
- package/src/panels/{editors.js → editors.ts} +43 -50
- package/src/panels/{elements-panel.js → elements-panel.ts} +30 -27
- package/src/panels/{events-panel.js → events-panel.ts} +33 -25
- package/src/panels/{git-panel.js → git-panel.ts} +97 -83
- package/src/panels/{head-panel.js → head-panel.ts} +117 -139
- package/src/panels/{imports-panel.js → imports-panel.ts} +69 -89
- package/src/panels/{layers-panel.js → layers-panel.ts} +61 -57
- package/src/panels/{left-panel.js → left-panel.ts} +56 -63
- package/src/panels/{overlays.js → overlays.ts} +28 -36
- package/src/panels/{panel-events.js → panel-events.ts} +53 -51
- package/src/panels/{preview-render.js → preview-render.ts} +20 -15
- package/src/panels/{properties-panel.js → properties-panel.ts} +212 -246
- package/src/panels/{pseudo-preview.js → pseudo-preview.ts} +9 -8
- package/src/panels/{quick-search.js → quick-search.ts} +16 -17
- package/src/panels/{right-panel.js → right-panel.ts} +41 -59
- package/src/panels/{shared.js → shared.ts} +7 -5
- package/src/panels/{signals-panel.js → signals-panel.ts} +322 -362
- package/src/panels/{statusbar.js → statusbar.ts} +14 -14
- package/src/panels/{style-inputs.js → style-inputs.ts} +66 -48
- package/src/panels/{style-panel.js → style-panel.ts} +235 -290
- package/src/panels/{style-utils.js → style-utils.ts} +32 -31
- package/src/panels/{stylebook-layers-panel.js → stylebook-layers-panel.ts} +22 -14
- package/src/panels/{stylebook-panel.js → stylebook-panel.ts} +127 -140
- package/src/panels/{tab-strip.js → tab-strip.ts} +12 -14
- package/src/panels/{toolbar.js → toolbar.ts} +68 -70
- package/src/panels/{welcome-screen.js → welcome-screen.ts} +14 -16
- package/src/{platform.js → platform.ts} +5 -3
- package/src/platforms/{devserver.js → devserver.ts} +53 -44
- package/src/{recent-projects.js → recent-projects.ts} +17 -12
- package/src/{resize-edges.js → resize-edges.ts} +18 -13
- package/src/services/{cem-export.js → cem-export.ts} +14 -7
- package/src/services/{code-services.js → code-services.ts} +29 -20
- package/src/services/{monaco-setup.js → monaco-setup.ts} +2 -3
- package/src/settings/{content-types-editor.js → content-types-editor.ts} +88 -67
- package/src/settings/{css-vars-editor.js → css-vars-editor.ts} +78 -51
- package/src/settings/{defs-editor.js → defs-editor.ts} +70 -56
- package/src/settings/{general-settings.js → general-settings.ts} +29 -28
- package/src/settings/{head-editor.js → head-editor.ts} +37 -35
- package/src/settings/{schema-field-ui.js → schema-field-ui.ts} +86 -76
- package/src/settings/{settings-modal.js → settings-modal.ts} +14 -16
- package/src/{site-context.js → site-context.ts} +35 -32
- package/src/{state.js → state.ts} +95 -78
- package/src/{store.js → store.ts} +60 -63
- package/src/{studio.js → studio.ts} +117 -119
- package/src/tabs/tab.ts +196 -0
- package/src/tabs/{transact.js → transact.ts} +107 -60
- package/src/types.ts +185 -0
- package/src/ui/{button-group.js → button-group.ts} +21 -21
- package/src/ui/{color-selector.js → color-selector.ts} +44 -37
- package/src/ui/{expression-editor.js → expression-editor.ts} +65 -40
- package/src/ui/{field-row.js → field-row.ts} +19 -2
- package/src/ui/{icons.js → icons.ts} +2 -2
- package/src/ui/{layers.js → layers.ts} +33 -24
- package/src/ui/{media-picker.js → media-picker.ts} +13 -9
- package/src/ui/{panel-resize.js → panel-resize.ts} +9 -4
- package/src/ui/{spectrum.js → spectrum.ts} +4 -4
- package/src/ui/{unit-selector.js → unit-selector.ts} +16 -19
- package/src/ui/{value-selector.js → value-selector.ts} +28 -28
- package/src/ui/{widgets.js → widgets.ts} +62 -15
- package/src/utils/{canvas-media.js → canvas-media.ts} +29 -14
- package/src/utils/{edit-display.js → edit-display.ts} +23 -20
- package/src/utils/{google-fonts.js → google-fonts.ts} +14 -13
- package/src/utils/{inherited-style.js → inherited-style.ts} +15 -10
- package/src/utils/{studio-utils.js → studio-utils.ts} +35 -23
- package/src/{view.js → view.ts} +53 -55
- package/src/workspace/{workspace.js → workspace.ts} +44 -33
- package/src/panels/dnd.js +0 -426
- package/src/tabs/tab.js +0 -192
- /package/src/{reactivity.js → reactivity.ts} +0 -0
|
@@ -1,16 +1,19 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
1
2
|
/**
|
|
2
3
|
* Edit-mode display transforms — extracted from studio.js (Phase 4i). Pure stateless functions that
|
|
3
4
|
* convert document trees for visual editing (template expressions, $map, $switch, empty
|
|
4
5
|
* placeholders).
|
|
5
6
|
*/
|
|
6
7
|
|
|
8
|
+
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
9
|
+
|
|
7
10
|
/**
|
|
8
11
|
* Convert a template string to a displayable expression for edit mode. Replaces ${expr} with ❮ expr
|
|
9
12
|
* ❯ so the runtime renders it as literal text.
|
|
10
13
|
*
|
|
11
14
|
* @param {string} str
|
|
12
15
|
*/
|
|
13
|
-
export function templateToEditDisplay(str) {
|
|
16
|
+
export function templateToEditDisplay(str: string) {
|
|
14
17
|
return str.replace(/\$\{([^}]+)\}/g, "\u276A $1 \u276B");
|
|
15
18
|
}
|
|
16
19
|
|
|
@@ -20,10 +23,10 @@ export function templateToEditDisplay(str) {
|
|
|
20
23
|
*
|
|
21
24
|
* @param {HTMLElement} el
|
|
22
25
|
*/
|
|
23
|
-
export function restoreTemplateExpressions(el) {
|
|
26
|
+
export function restoreTemplateExpressions(el: HTMLElement) {
|
|
24
27
|
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
|
|
25
28
|
while (walker.nextNode()) {
|
|
26
|
-
const node =
|
|
29
|
+
const node = walker.currentNode as Text;
|
|
27
30
|
if (node.data.includes("\u276A")) {
|
|
28
31
|
node.data = node.data.replace(/\u276A\s*(.*?)\s*\u276B/g, "${$1}");
|
|
29
32
|
}
|
|
@@ -38,14 +41,13 @@ export function restoreTemplateExpressions(el) {
|
|
|
38
41
|
* @param {JxMutableNode} node
|
|
39
42
|
* @returns {JxMutableNode}
|
|
40
43
|
*/
|
|
41
|
-
export function prepareForEditMode(node) {
|
|
44
|
+
export function prepareForEditMode(node: JxMutableNode): JxMutableNode {
|
|
42
45
|
if (!node || typeof node !== "object") return node;
|
|
43
46
|
if (Array.isArray(node)) return node.map(prepareForEditMode);
|
|
44
47
|
|
|
45
|
-
const /** @type {Record<string, unknown>} */ obj =
|
|
48
|
+
const /** @type {Record<string, unknown>} */ obj = node as Record<string, unknown>;
|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
const out = {};
|
|
50
|
+
const out: Record<string, unknown> = {};
|
|
49
51
|
for (const [k, v] of Object.entries(obj)) {
|
|
50
52
|
if (k === "state" || k === "$media" || k === "$props" || k === "$elements") {
|
|
51
53
|
out[k] = v; // preserve as-is for runtime resolution
|
|
@@ -55,34 +57,34 @@ export function prepareForEditMode(node) {
|
|
|
55
57
|
} else if (
|
|
56
58
|
v &&
|
|
57
59
|
typeof v === "object" &&
|
|
58
|
-
|
|
60
|
+
(v as Record<string, unknown>).$prototype === "Array"
|
|
59
61
|
) {
|
|
60
62
|
// Wrap the map template in a visual repeater perimeter
|
|
61
|
-
const vObj =
|
|
63
|
+
const vObj = v as Record<string, unknown>;
|
|
62
64
|
const template = vObj.map;
|
|
63
65
|
if (template && typeof template === "object") {
|
|
64
66
|
out.children = [
|
|
65
67
|
{
|
|
66
68
|
tagName: "div",
|
|
67
69
|
className: "repeater-perimeter",
|
|
68
|
-
children: [prepareForEditMode(
|
|
70
|
+
children: [prepareForEditMode(template as JxMutableNode)],
|
|
69
71
|
},
|
|
70
72
|
];
|
|
71
73
|
} else {
|
|
72
74
|
out.children = [];
|
|
73
75
|
}
|
|
74
76
|
} else {
|
|
75
|
-
out.children = prepareForEditMode(
|
|
77
|
+
out.children = prepareForEditMode(v as JxMutableNode);
|
|
76
78
|
}
|
|
77
79
|
} else if (k === "cases" && obj.$switch && v && typeof v === "object") {
|
|
78
80
|
// Replace $switch cases with a placeholder showing the first case or a label
|
|
79
81
|
const caseKeys = Object.keys(v);
|
|
80
82
|
if (caseKeys.length > 0) {
|
|
81
|
-
const firstCase =
|
|
83
|
+
const firstCase = (v as Record<string, unknown>)[caseKeys[0]];
|
|
82
84
|
if (
|
|
83
85
|
firstCase &&
|
|
84
86
|
typeof firstCase === "object" &&
|
|
85
|
-
!(
|
|
87
|
+
!(firstCase as Record<string, unknown>).$ref
|
|
86
88
|
) {
|
|
87
89
|
out.children = [prepareForEditMode(firstCase)];
|
|
88
90
|
} else {
|
|
@@ -107,8 +109,7 @@ export function prepareForEditMode(node) {
|
|
|
107
109
|
} else if (k === "style") {
|
|
108
110
|
// Replace template strings in style values with empty strings
|
|
109
111
|
if (v && typeof v === "object") {
|
|
110
|
-
|
|
111
|
-
const s = {};
|
|
112
|
+
const s: Record<string, unknown> = {};
|
|
112
113
|
for (const [sk, sv] of Object.entries(v)) {
|
|
113
114
|
s[sk] = typeof sv === "string" && sv.includes("${") ? "" : sv;
|
|
114
115
|
}
|
|
@@ -118,14 +119,16 @@ export function prepareForEditMode(node) {
|
|
|
118
119
|
}
|
|
119
120
|
} else if (typeof v === "string" && v.includes("${")) {
|
|
120
121
|
// Template string in a display property → show raw expression
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
// For URL-bearing attributes, use empty string to avoid triggering network requests
|
|
123
|
+
const isUrlAttr = k === "src" || k === "href" || k === "poster" || k === "action";
|
|
124
|
+
out[k] = isUrlAttr ? "" : templateToEditDisplay(v);
|
|
125
|
+
} else if (v && typeof v === "object" && (v as Record<string, unknown>).$ref) {
|
|
123
126
|
// $ref binding → show ref path as literal text
|
|
124
|
-
const ref =
|
|
127
|
+
const ref = (v as Record<string, unknown>).$ref as string;
|
|
125
128
|
const label = ref.startsWith("#/state/") ? ref.slice(8) : ref;
|
|
126
129
|
out[k] = `{${label}}`;
|
|
127
130
|
} else {
|
|
128
|
-
out[k] = prepareForEditMode(
|
|
131
|
+
out[k] = prepareForEditMode(v as JxMutableNode);
|
|
129
132
|
}
|
|
130
133
|
}
|
|
131
134
|
|
|
@@ -133,7 +136,7 @@ export function prepareForEditMode(node) {
|
|
|
133
136
|
if (out.tagName && !out.textContent && !out.innerHTML) {
|
|
134
137
|
const hasChildren = Array.isArray(out.children) && out.children.length > 0;
|
|
135
138
|
if (!hasChildren) {
|
|
136
|
-
const tag =
|
|
139
|
+
const tag = out.tagName as string;
|
|
137
140
|
const textTags = new Set([
|
|
138
141
|
"p",
|
|
139
142
|
"h1",
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/** Google Fonts helpers — shared between head-panel (per-page) and head-editor (project-level). */
|
|
2
2
|
|
|
3
|
+
import type { JxHeadEntry } from "@jxsuite/schema/types";
|
|
4
|
+
|
|
3
5
|
export const GFONTS_CSS_PREFIX = "https://fonts.googleapis.com/css2?";
|
|
4
6
|
export const GFONTS_PRECONNECT_ORIGINS = [
|
|
5
7
|
"https://fonts.googleapis.com",
|
|
@@ -12,12 +14,12 @@ export const GFONTS_PRECONNECT_ORIGINS = [
|
|
|
12
14
|
* @param {JxHeadEntry} entry
|
|
13
15
|
* @returns {boolean}
|
|
14
16
|
*/
|
|
15
|
-
export function isGoogleFontEntry(entry) {
|
|
17
|
+
export function isGoogleFontEntry(entry: JxHeadEntry) {
|
|
16
18
|
return (
|
|
17
19
|
entry?.tagName === "link" &&
|
|
18
20
|
entry?.attributes?.rel === "stylesheet" &&
|
|
19
21
|
typeof entry?.attributes?.href === "string" &&
|
|
20
|
-
|
|
22
|
+
(entry.attributes.href as string).startsWith(GFONTS_CSS_PREFIX)
|
|
21
23
|
);
|
|
22
24
|
}
|
|
23
25
|
|
|
@@ -27,11 +29,11 @@ export function isGoogleFontEntry(entry) {
|
|
|
27
29
|
* @param {JxHeadEntry} entry
|
|
28
30
|
* @returns {boolean}
|
|
29
31
|
*/
|
|
30
|
-
export function isGoogleFontPreconnect(entry) {
|
|
32
|
+
export function isGoogleFontPreconnect(entry: JxHeadEntry) {
|
|
31
33
|
return (
|
|
32
34
|
entry?.tagName === "link" &&
|
|
33
35
|
entry?.attributes?.rel === "preconnect" &&
|
|
34
|
-
GFONTS_PRECONNECT_ORIGINS.includes(
|
|
36
|
+
GFONTS_PRECONNECT_ORIGINS.includes(entry?.attributes?.href as string)
|
|
35
37
|
);
|
|
36
38
|
}
|
|
37
39
|
|
|
@@ -41,7 +43,7 @@ export function isGoogleFontPreconnect(entry) {
|
|
|
41
43
|
* @param {string} href
|
|
42
44
|
* @returns {string}
|
|
43
45
|
*/
|
|
44
|
-
export function extractFontFamily(href) {
|
|
46
|
+
export function extractFontFamily(href: string) {
|
|
45
47
|
const match = href.match(/family=([^&:]+)/);
|
|
46
48
|
if (!match) return "";
|
|
47
49
|
return decodeURIComponent(match[1].replace(/\+/g, " "));
|
|
@@ -53,7 +55,7 @@ export function extractFontFamily(href) {
|
|
|
53
55
|
* @param {string} family
|
|
54
56
|
* @returns {string}
|
|
55
57
|
*/
|
|
56
|
-
export function buildGoogleFontUrl(family) {
|
|
58
|
+
export function buildGoogleFontUrl(family: string) {
|
|
57
59
|
return `${GFONTS_CSS_PREFIX}family=${encodeURIComponent(family).replace(/%20/g, "+")}&display=swap`;
|
|
58
60
|
}
|
|
59
61
|
|
|
@@ -62,17 +64,16 @@ export function buildGoogleFontUrl(family) {
|
|
|
62
64
|
*
|
|
63
65
|
* @param {JxHeadEntry[]} head
|
|
64
66
|
*/
|
|
65
|
-
export function ensureGoogleFontPreconnects(head) {
|
|
67
|
+
export function ensureGoogleFontPreconnects(head: JxHeadEntry[]) {
|
|
66
68
|
for (const origin of GFONTS_PRECONNECT_ORIGINS) {
|
|
67
69
|
const exists = head.some(
|
|
68
|
-
(
|
|
70
|
+
(e: JxHeadEntry) =>
|
|
69
71
|
e?.tagName === "link" &&
|
|
70
72
|
e?.attributes?.rel === "preconnect" &&
|
|
71
73
|
e?.attributes?.href === origin,
|
|
72
74
|
);
|
|
73
75
|
if (!exists) {
|
|
74
|
-
|
|
75
|
-
const attrs = { rel: "preconnect", href: origin };
|
|
76
|
+
const attrs: Record<string, string | boolean> = { rel: "preconnect", href: origin };
|
|
76
77
|
if (origin === "https://fonts.gstatic.com") attrs.crossorigin = "";
|
|
77
78
|
head.push({ tagName: "link", attributes: attrs });
|
|
78
79
|
}
|
|
@@ -85,10 +86,10 @@ export function ensureGoogleFontPreconnects(head) {
|
|
|
85
86
|
* @param {JxHeadEntry[]} head
|
|
86
87
|
* @returns {JxHeadEntry[]}
|
|
87
88
|
*/
|
|
88
|
-
export function cleanupGoogleFontPreconnects(head) {
|
|
89
|
-
const hasFont = head.some((
|
|
89
|
+
export function cleanupGoogleFontPreconnects(head: JxHeadEntry[]) {
|
|
90
|
+
const hasFont = head.some((e: JxHeadEntry) => isGoogleFontEntry(e));
|
|
90
91
|
if (!hasFont) {
|
|
91
|
-
return head.filter((
|
|
92
|
+
return head.filter((e: JxHeadEntry) => !isGoogleFontPreconnect(e));
|
|
92
93
|
}
|
|
93
94
|
return head;
|
|
94
95
|
}
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* on the current tab.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import type { JxStyle } from "@jxsuite/schema/types";
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
* Compute the inherited style object for a given breakpoint tab.
|
|
11
13
|
*
|
|
@@ -16,35 +18,38 @@
|
|
|
16
18
|
* @param {string | null} activeSelector — current nested selector, or null
|
|
17
19
|
* @returns {Record<string, string | number>} Inherited style map (prop → value)
|
|
18
20
|
*/
|
|
19
|
-
export function computeInheritedStyle(
|
|
21
|
+
export function computeInheritedStyle(
|
|
22
|
+
style: JxStyle,
|
|
23
|
+
mediaNames: string[],
|
|
24
|
+
activeTab: string | null,
|
|
25
|
+
activeSelector: string | null = null,
|
|
26
|
+
) {
|
|
20
27
|
if (activeTab === null || mediaNames.length === 0) return {};
|
|
21
28
|
|
|
22
|
-
|
|
23
|
-
let inherited = {};
|
|
29
|
+
let inherited: Record<string, string | number> = {};
|
|
24
30
|
|
|
25
31
|
if (!activeSelector) {
|
|
26
32
|
// Start with base flat props
|
|
27
33
|
for (const [p, v] of Object.entries(style)) {
|
|
28
|
-
if (typeof v !== "object") inherited[p] = v ?? "";
|
|
34
|
+
if (typeof v !== "object") inherited[p] = (v as string | number) ?? "";
|
|
29
35
|
}
|
|
30
36
|
// Layer each media block in order until current tab
|
|
31
37
|
for (const name of mediaNames) {
|
|
32
38
|
if (name === activeTab) break;
|
|
33
|
-
const block = style[`@${name}`] || {};
|
|
39
|
+
const block = (style[`@${name}`] || {}) as JxStyle;
|
|
34
40
|
for (const [p, v] of Object.entries(block)) {
|
|
35
|
-
if (typeof v !== "object") inherited[p] = v ?? "";
|
|
41
|
+
if (typeof v !== "object") inherited[p] = (v as string | number) ?? "";
|
|
36
42
|
}
|
|
37
43
|
}
|
|
38
44
|
} else {
|
|
39
45
|
// Selector inheritance: base selector → each media's selector block in order
|
|
40
|
-
const baseSel = style[activeSelector] || {};
|
|
46
|
+
const baseSel = (style[activeSelector] || {}) as JxStyle;
|
|
41
47
|
for (const [p, v] of Object.entries(baseSel)) {
|
|
42
|
-
if (typeof v !== "object") inherited[p] = v ?? "";
|
|
48
|
+
if (typeof v !== "object") inherited[p] = (v as string | number) ?? "";
|
|
43
49
|
}
|
|
44
50
|
for (const name of mediaNames) {
|
|
45
51
|
if (name === activeTab) break;
|
|
46
|
-
const selBlock =
|
|
47
|
-
/** @type {Record<string, unknown>} */ (style[`@${name}`] || {})[activeSelector] || {};
|
|
52
|
+
const selBlock = ((style[`@${name}`] || {}) as Record<string, unknown>)[activeSelector] || {};
|
|
48
53
|
for (const [p, v] of Object.entries(selBlock)) {
|
|
49
54
|
if (typeof v !== "object") inherited[p] = v ?? "";
|
|
50
55
|
}
|
|
@@ -4,14 +4,16 @@
|
|
|
4
4
|
* These are all side-effect-free functions used by style/properties/events panels.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import type { ProjectConfig, ContentTypeDef } from "@jxsuite/schema/types";
|
|
8
|
+
|
|
7
9
|
/**
|
|
8
10
|
* CamelCase → kebab-case for inline style attributes
|
|
9
11
|
*
|
|
10
12
|
* @param {string} str
|
|
11
13
|
* @returns {string}
|
|
12
14
|
*/
|
|
13
|
-
export function camelToKebab(str) {
|
|
14
|
-
return str.replace(/[A-Z]/g, (
|
|
15
|
+
export function camelToKebab(str: string) {
|
|
16
|
+
return str.replace(/[A-Z]/g, (c: string) => "-" + c.toLowerCase());
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
/**
|
|
@@ -21,10 +23,14 @@ export function camelToKebab(str) {
|
|
|
21
23
|
* @param {string} prop
|
|
22
24
|
* @returns {string}
|
|
23
25
|
*/
|
|
24
|
-
export function camelToLabel(prop) {
|
|
25
|
-
return prop
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
export function camelToLabel(prop: string) {
|
|
27
|
+
return prop.replace(/([A-Z])/g, " $1").replace(/^./, (c: string) => c.toUpperCase());
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function toCamelCase(str: string): string {
|
|
31
|
+
return str
|
|
32
|
+
.replace(/[^a-zA-Z0-9]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ""))
|
|
33
|
+
.replace(/^[A-Z]/, (c) => c.toLowerCase());
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
/**
|
|
@@ -34,11 +40,10 @@ export function camelToLabel(prop) {
|
|
|
34
40
|
* @param {string} val
|
|
35
41
|
* @returns {string}
|
|
36
42
|
*/
|
|
37
|
-
export function kebabToLabel(val) {
|
|
43
|
+
export function kebabToLabel(val: string) {
|
|
38
44
|
return val.replace(
|
|
39
45
|
/(^|-)(\w)/g,
|
|
40
|
-
(
|
|
41
|
-
(sep ? " " : "") + c.toUpperCase(),
|
|
46
|
+
(_: string, sep: string, c: string) => (sep ? " " : "") + c.toUpperCase(),
|
|
42
47
|
);
|
|
43
48
|
}
|
|
44
49
|
|
|
@@ -49,7 +54,10 @@ export function kebabToLabel(val) {
|
|
|
49
54
|
* @param {string} prop
|
|
50
55
|
* @returns {string}
|
|
51
56
|
*/
|
|
52
|
-
export function propLabel(
|
|
57
|
+
export function propLabel(
|
|
58
|
+
entry: { $label?: string; [key: string]: unknown } | null | undefined,
|
|
59
|
+
prop: string,
|
|
60
|
+
) {
|
|
53
61
|
return entry?.$label || camelToLabel(prop);
|
|
54
62
|
}
|
|
55
63
|
|
|
@@ -60,13 +68,15 @@ export function propLabel(entry, prop) {
|
|
|
60
68
|
* @param {string} attr
|
|
61
69
|
* @returns {string}
|
|
62
70
|
*/
|
|
63
|
-
export function attrLabel(
|
|
71
|
+
export function attrLabel(
|
|
72
|
+
entry: { $label?: string; [key: string]: unknown } | null | undefined,
|
|
73
|
+
attr: string,
|
|
74
|
+
) {
|
|
64
75
|
if (entry?.$label) return entry.$label;
|
|
65
76
|
if (attr.includes("-"))
|
|
66
77
|
return attr.replace(
|
|
67
78
|
/(^|-)(\w)/g,
|
|
68
|
-
(
|
|
69
|
-
(sep ? " " : "") + c.toUpperCase(),
|
|
79
|
+
(_: string, sep: string, c: string) => (sep ? " " : "") + c.toUpperCase(),
|
|
70
80
|
);
|
|
71
81
|
return camelToLabel(attr);
|
|
72
82
|
}
|
|
@@ -77,9 +87,8 @@ export function attrLabel(entry, attr) {
|
|
|
77
87
|
* @param {string} val
|
|
78
88
|
* @returns {string}
|
|
79
89
|
*/
|
|
80
|
-
export function abbreviateValue(val) {
|
|
81
|
-
|
|
82
|
-
const map = {
|
|
90
|
+
export function abbreviateValue(val: string) {
|
|
91
|
+
const map: Record<string, string> = {
|
|
83
92
|
inline: "inl",
|
|
84
93
|
"inline-block": "i-blk",
|
|
85
94
|
"inline-flex": "i-flx",
|
|
@@ -109,7 +118,7 @@ export function abbreviateValue(val) {
|
|
|
109
118
|
* @param {Record<string, unknown>} entry
|
|
110
119
|
* @returns {string}
|
|
111
120
|
*/
|
|
112
|
-
export function inferInputType(entry) {
|
|
121
|
+
export function inferInputType(entry: Record<string, unknown>) {
|
|
113
122
|
if (entry.$shorthand === true) return "shorthand";
|
|
114
123
|
if (entry.$input === "button-group") return "button-group";
|
|
115
124
|
if (entry.$input === "media") return "media";
|
|
@@ -130,10 +139,13 @@ export function inferInputType(entry) {
|
|
|
130
139
|
* @param {ProjectConfig | null | undefined} projectConfig — parsed project.json
|
|
131
140
|
* @returns {{ name: string; schema: ContentTypeSchema } | null}
|
|
132
141
|
*/
|
|
133
|
-
export function findContentTypeSchema(
|
|
142
|
+
export function findContentTypeSchema(
|
|
143
|
+
documentPath: string | null,
|
|
144
|
+
projectConfig: ProjectConfig | null | undefined,
|
|
145
|
+
) {
|
|
134
146
|
if (!documentPath || !projectConfig?.contentTypes) return null;
|
|
135
147
|
for (const [name, def] of Object.entries(
|
|
136
|
-
|
|
148
|
+
projectConfig.contentTypes as Record<string, ContentTypeDef>,
|
|
137
149
|
)) {
|
|
138
150
|
if (!def.source || !def.schema) continue;
|
|
139
151
|
const src = def.source.replace(/^\.\//, "").replace(/\/$/, "");
|
|
@@ -160,7 +172,7 @@ export function findContentTypeSchema(documentPath, projectConfig) {
|
|
|
160
172
|
* @param {string} prefix - E.g. "--font-"
|
|
161
173
|
* @returns {string}
|
|
162
174
|
*/
|
|
163
|
-
export function friendlyNameToVar(name, prefix) {
|
|
175
|
+
export function friendlyNameToVar(name: string, prefix: string) {
|
|
164
176
|
const slug = name
|
|
165
177
|
.trim()
|
|
166
178
|
.toLowerCase()
|
|
@@ -180,13 +192,13 @@ export function friendlyNameToVar(name, prefix) {
|
|
|
180
192
|
* @param {string} prefix
|
|
181
193
|
* @returns {string}
|
|
182
194
|
*/
|
|
183
|
-
export function varDisplayName(varName, prefix) {
|
|
195
|
+
export function varDisplayName(varName: string, prefix: string) {
|
|
184
196
|
return (
|
|
185
197
|
varName
|
|
186
198
|
.replace(new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`), "")
|
|
187
199
|
.replace(/^--/, "")
|
|
188
200
|
.replace(/-/g, " ")
|
|
189
|
-
.replace(/\b\w/g, (
|
|
201
|
+
.replace(/\b\w/g, (c: string) => c.toUpperCase()) || varName
|
|
190
202
|
);
|
|
191
203
|
}
|
|
192
204
|
|
|
@@ -199,7 +211,7 @@ export function varDisplayName(varName, prefix) {
|
|
|
199
211
|
* | { kind: "number" }
|
|
200
212
|
* | { kind: "text" }}
|
|
201
213
|
*/
|
|
202
|
-
export function parseCemType(typeText) {
|
|
214
|
+
export function parseCemType(typeText: string | undefined | null) {
|
|
203
215
|
if (!typeText) return { kind: "text" };
|
|
204
216
|
const t = typeText
|
|
205
217
|
.trim()
|
package/src/{view.js → view.ts}
RENAMED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
1
2
|
/**
|
|
2
3
|
* View.js — Transient view state for Jx Studio
|
|
3
4
|
*
|
|
@@ -6,62 +7,59 @@
|
|
|
6
7
|
* store.js) makes renderer dependencies explicit.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
* }} ViewState
|
|
61
|
-
*/
|
|
10
|
+
interface ViewState {
|
|
11
|
+
panzoomWrap: HTMLElement | null;
|
|
12
|
+
renderGeneration: number;
|
|
13
|
+
centerObserver: ResizeObserver | null;
|
|
14
|
+
needsCenter: boolean;
|
|
15
|
+
panX: number;
|
|
16
|
+
panY: number;
|
|
17
|
+
prevCanvasMode: string | null;
|
|
18
|
+
monacoEditor:
|
|
19
|
+
| (import("monaco-editor").editor.IStandaloneCodeEditor & { _ignoreNextChange?: boolean })
|
|
20
|
+
| null;
|
|
21
|
+
functionEditor:
|
|
22
|
+
| (import("monaco-editor").editor.IStandaloneCodeEditor & {
|
|
23
|
+
_ignoreNextChange?: boolean;
|
|
24
|
+
_editingTarget?: string | null;
|
|
25
|
+
})
|
|
26
|
+
| null;
|
|
27
|
+
componentInlineEdit: {
|
|
28
|
+
el: HTMLElement;
|
|
29
|
+
path: (string | number)[];
|
|
30
|
+
originalText: string;
|
|
31
|
+
mediaName: string | null;
|
|
32
|
+
_outsideHandler?: ((e: MouseEvent) => void) | null;
|
|
33
|
+
[k: string]: unknown;
|
|
34
|
+
} | null;
|
|
35
|
+
inlineEditCleanup: (() => void) | null;
|
|
36
|
+
blockActionBarEl: HTMLElement | null;
|
|
37
|
+
selDragCleanup: (() => void) | null;
|
|
38
|
+
dndCleanups: (() => void)[];
|
|
39
|
+
canvasDndCleanups: (() => void)[];
|
|
40
|
+
canvasEventCleanups: (() => void)[];
|
|
41
|
+
forcedStyleTag: HTMLStyleElement | null;
|
|
42
|
+
forcedAttrEl: HTMLElement | null;
|
|
43
|
+
elementsCollapsed: Set<string>;
|
|
44
|
+
elementsFilter: string;
|
|
45
|
+
lastDragInput: { clientX: number; clientY: number; [k: string]: unknown } | null;
|
|
46
|
+
_currentDropTargetRow: HTMLElement | null;
|
|
47
|
+
layerDragSourceHeight: number;
|
|
48
|
+
savedRange: Range | null;
|
|
49
|
+
_completionRegistered: boolean;
|
|
50
|
+
stylebookElToTag: WeakMap<Element, string>;
|
|
51
|
+
showAddBreakpointForm: boolean;
|
|
52
|
+
addBreakpointPreview: string;
|
|
53
|
+
layoutSelection: unknown;
|
|
54
|
+
leftTab: string;
|
|
55
|
+
leftPanelCollapsed: boolean;
|
|
56
|
+
rightPanelCollapsed: boolean;
|
|
57
|
+
autosaveTimer: ReturnType<typeof setTimeout> | null;
|
|
58
|
+
_layersCollapsed: Set<string> | null;
|
|
59
|
+
[key: string]: unknown;
|
|
60
|
+
}
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
export const view = {
|
|
62
|
+
export const view: ViewState = {
|
|
65
63
|
// Canvas infrastructure
|
|
66
64
|
panzoomWrap: null,
|
|
67
65
|
renderGeneration: 0,
|