@jxsuite/studio 0.19.0 → 0.20.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.css +98 -98
- package/dist/studio.js +12577 -12183
- package/dist/studio.js.map +146 -144
- package/dist/workers/editor.worker.js +79 -79
- package/dist/workers/json.worker.js +109 -109
- package/dist/workers/ts.worker.js +82 -82
- package/package.json +17 -6
- package/src/browse/browse.js +25 -19
- package/src/canvas/canvas-diff.js +24 -16
- package/src/canvas/canvas-helpers.js +24 -19
- package/src/canvas/canvas-live-render.js +94 -39
- package/src/canvas/canvas-render.js +119 -73
- package/src/canvas/canvas-utils.js +38 -15
- package/src/canvas/nested-site-style.js +50 -0
- package/src/editor/component-inline-edit.js +14 -13
- package/src/editor/content-inline-edit.js +22 -10
- package/src/editor/context-menu.js +1 -1
- package/src/editor/convert-to-component.js +12 -10
- package/src/editor/inline-edit.js +66 -28
- package/src/editor/insertion-helper.js +16 -12
- package/src/editor/shortcuts.js +4 -2
- package/src/editor/slash-menu.js +6 -4
- package/src/files/components.js +1 -1
- package/src/files/file-ops.js +37 -16
- package/src/files/files.js +78 -52
- package/src/github/github-auth.js +122 -0
- package/src/github/github-publish.js +115 -0
- package/src/markdown/md-convert.js +172 -107
- package/src/new-project/new-project-modal.js +204 -0
- package/src/panels/activity-bar.js +22 -20
- package/src/panels/ai-panel.js +399 -0
- package/src/panels/block-action-bar.js +5 -5
- package/src/panels/canvas-dnd.js +8 -2
- package/src/panels/data-explorer.js +19 -13
- package/src/panels/dnd.js +144 -92
- package/src/panels/editors.js +55 -35
- package/src/panels/elements-panel.js +33 -19
- package/src/panels/events-panel.js +15 -10
- package/src/panels/git-panel.js +469 -98
- package/src/panels/head-panel.js +121 -81
- package/src/panels/imports-panel.js +88 -64
- package/src/panels/layers-panel.js +64 -43
- package/src/panels/left-panel.js +85 -47
- package/src/panels/overlays.js +1 -0
- package/src/panels/panel-events.js +45 -30
- package/src/panels/preview-render.js +23 -18
- package/src/panels/properties-panel.js +365 -249
- package/src/panels/pseudo-preview.js +2 -2
- package/src/panels/quick-search.js +7 -5
- package/src/panels/right-panel.js +26 -2
- package/src/panels/shared.js +4 -4
- package/src/panels/signals-panel.js +295 -166
- package/src/panels/statusbar.js +4 -4
- package/src/panels/style-inputs.js +67 -35
- package/src/panels/style-panel.js +233 -132
- package/src/panels/style-utils.js +34 -16
- package/src/panels/stylebook-layers-panel.js +12 -10
- package/src/panels/stylebook-panel.js +134 -66
- package/src/panels/toolbar.js +333 -109
- package/src/panels/welcome-screen.js +121 -0
- package/src/platform.js +2 -4
- package/src/platforms/devserver.js +113 -7
- package/src/resize-edges.js +98 -0
- package/src/services/cem-export.js +12 -7
- package/src/services/code-services.js +30 -12
- package/src/settings/content-types-editor.js +7 -7
- package/src/settings/css-vars-editor.js +30 -24
- package/src/settings/defs-editor.js +12 -7
- package/src/settings/general-settings.js +85 -3
- package/src/settings/head-editor.js +30 -24
- package/src/settings/schema-field-ui.js +58 -39
- package/src/site-context.js +41 -31
- package/src/state.js +70 -29
- package/src/store.js +21 -26
- package/src/studio.js +103 -81
- package/src/tabs/tab.js +67 -43
- package/src/tabs/transact.js +23 -13
- package/src/ui/button-group.js +24 -24
- package/src/ui/color-selector.js +28 -23
- package/src/ui/field-row.js +3 -3
- package/src/ui/icons.js +2 -2
- package/src/ui/media-picker.js +7 -7
- package/src/ui/spectrum.js +5 -1
- package/src/ui/unit-selector.js +18 -16
- package/src/ui/value-selector.js +21 -15
- package/src/ui/widgets.js +22 -19
- package/src/utils/canvas-media.js +5 -4
- package/src/utils/edit-display.js +31 -20
- package/src/utils/google-fonts.js +11 -11
- package/src/utils/inherited-style.js +9 -8
- package/src/utils/studio-utils.js +7 -7
- package/src/view.js +58 -1
- package/src/workspace/workspace.js +10 -7
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Welcome screen — shown when no project is loaded and no document tabs are open. Mimics VS Code's
|
|
3
|
+
* welcome tab with start actions and recent projects.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { html, render as litRender, nothing } from "lit-html";
|
|
7
|
+
import { getRecentProjects } from "../recent-projects.js";
|
|
8
|
+
import { platformSupportsClone } from "./git-panel.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {{
|
|
12
|
+
* openProject: () => void;
|
|
13
|
+
* openRecentProject: (root: string) => void;
|
|
14
|
+
* openNewProject: () => void;
|
|
15
|
+
* cloneRepository: () => void;
|
|
16
|
+
* }} WelcomeCtx
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** @type {WelcomeCtx | null} */
|
|
20
|
+
let _ctx = null;
|
|
21
|
+
|
|
22
|
+
/** @param {WelcomeCtx} ctx */
|
|
23
|
+
export function initWelcome(ctx) {
|
|
24
|
+
_ctx = ctx;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** @param {HTMLElement} host */
|
|
28
|
+
export function renderWelcome(host) {
|
|
29
|
+
const ctx = /** @type {WelcomeCtx} */ (_ctx);
|
|
30
|
+
const recent = getRecentProjects();
|
|
31
|
+
const showClone = platformSupportsClone();
|
|
32
|
+
|
|
33
|
+
litRender(
|
|
34
|
+
html`
|
|
35
|
+
<div class="welcome-screen">
|
|
36
|
+
<div class="welcome-content">
|
|
37
|
+
<h1 class="welcome-title">Jx Studio</h1>
|
|
38
|
+
<p class="welcome-subtitle">Visual component builder</p>
|
|
39
|
+
|
|
40
|
+
<div class="welcome-section">
|
|
41
|
+
<h2 class="welcome-section-title">Start</h2>
|
|
42
|
+
<button class="welcome-action" @click=${() => ctx.openNewProject()}>
|
|
43
|
+
<svg
|
|
44
|
+
class="welcome-action-icon"
|
|
45
|
+
width="16"
|
|
46
|
+
height="16"
|
|
47
|
+
viewBox="0 0 16 16"
|
|
48
|
+
fill="currentColor"
|
|
49
|
+
>
|
|
50
|
+
<path
|
|
51
|
+
d="M9.5 1.1l3.4 3.5.1.4v2h-1V6H8.5L8 5.5V2H3.5l-.5.5v11l.5.5H6v1H3.5l-1.5-1.5v-11l1.5-1.5h5.7l.3.1zM9 2v3h2.9L9 2zm4 14h-1v-3H9v-1h3V9h1v3h3v1h-3v3z"
|
|
52
|
+
/>
|
|
53
|
+
</svg>
|
|
54
|
+
New Project...
|
|
55
|
+
</button>
|
|
56
|
+
<button class="welcome-action" @click=${() => ctx.openProject()}>
|
|
57
|
+
<svg
|
|
58
|
+
class="welcome-action-icon"
|
|
59
|
+
width="16"
|
|
60
|
+
height="16"
|
|
61
|
+
viewBox="0 0 16 16"
|
|
62
|
+
fill="currentColor"
|
|
63
|
+
>
|
|
64
|
+
<path
|
|
65
|
+
d="M14.5 3H7.71l-.85-.85L6.51 2h-5l-.5.5v11l.5.5h13l.5-.5v-10L14.5 3zm-.51 8.49V13h-12V7h4.49l.35-.15.86-.86H14v5.5zM6.51 6l-.35.15-.86.86H2v-3h4.29l.85.85.36.15H14V6H6.51z"
|
|
66
|
+
/>
|
|
67
|
+
</svg>
|
|
68
|
+
Open Project...
|
|
69
|
+
</button>
|
|
70
|
+
${showClone
|
|
71
|
+
? html`<button class="welcome-action" @click=${() => ctx.cloneRepository()}>
|
|
72
|
+
<svg
|
|
73
|
+
class="welcome-action-icon"
|
|
74
|
+
width="16"
|
|
75
|
+
height="16"
|
|
76
|
+
viewBox="0 0 16 16"
|
|
77
|
+
fill="currentColor"
|
|
78
|
+
>
|
|
79
|
+
<path
|
|
80
|
+
d="M4.7 12.3a4.2 4.2 0 0 1-1.2-1.7A4.4 4.4 0 0 1 3 8.9a4 4 0 0 1 .3-1.5 3.8 3.8 0 0 1 .8-1.3c.4-.4.8-.7 1.3-.9.5-.2 1-.4 1.6-.4V3l3.5 2.5L7 8V6.2c-.9.1-1.6.5-2.1 1.1a3 3 0 0 0-.8 2.1c0 .4.1.8.2 1.2.1.3.3.7.5 1l-.1.7zM11 6.9c.9-.1 1.6-.5 2.1-1.1a3 3 0 0 0 .8-2.1 3 3 0 0 0-.2-1.2 3.5 3.5 0 0 0-.5-1l.1-.7a4.2 4.2 0 0 1 1.2 1.7c.3.5.4 1.1.4 1.7a4 4 0 0 1-.3 1.5 3.8 3.8 0 0 1-.8 1.3c-.4.4-.8.7-1.3.9-.5.2-1 .4-1.6.4V11L7.5 8.5 11 6v.9z"
|
|
81
|
+
/>
|
|
82
|
+
</svg>
|
|
83
|
+
Clone Git Repository...
|
|
84
|
+
</button>`
|
|
85
|
+
: nothing}
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
${recent.length > 0
|
|
89
|
+
? html`
|
|
90
|
+
<div class="welcome-section">
|
|
91
|
+
<h2 class="welcome-section-title">Recent</h2>
|
|
92
|
+
${recent.map(
|
|
93
|
+
(p) => html`
|
|
94
|
+
<button
|
|
95
|
+
class="welcome-recent"
|
|
96
|
+
@click=${() => ctx.openRecentProject(p.root)}
|
|
97
|
+
title=${p.root}
|
|
98
|
+
>
|
|
99
|
+
<span class="welcome-recent-name">${p.name}</span>
|
|
100
|
+
<span class="welcome-recent-path">${shortenPath(p.root)}</span>
|
|
101
|
+
</button>
|
|
102
|
+
`,
|
|
103
|
+
)}
|
|
104
|
+
</div>
|
|
105
|
+
`
|
|
106
|
+
: nothing}
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
`,
|
|
110
|
+
host,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** @param {string} path */
|
|
115
|
+
function shortenPath(path) {
|
|
116
|
+
if (path.startsWith("/home/")) {
|
|
117
|
+
const parts = path.split("/");
|
|
118
|
+
return "~/" + parts.slice(3).join("/");
|
|
119
|
+
}
|
|
120
|
+
return path;
|
|
121
|
+
}
|
package/src/platform.js
CHANGED
|
@@ -11,10 +11,8 @@
|
|
|
11
11
|
* See spec/desktop.md §3 for the full StudioPlatform interface.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
/** @
|
|
15
|
-
|
|
16
|
-
/** @type {any} */
|
|
17
|
-
const g = globalThis;
|
|
14
|
+
/** @type {{ __jxPlatform?: StudioPlatform }} */
|
|
15
|
+
const g = /** @type {{ __jxPlatform?: StudioPlatform }} */ (/** @type {unknown} */ (globalThis));
|
|
18
16
|
|
|
19
17
|
/** @param {StudioPlatform} platform */
|
|
20
18
|
export function registerPlatform(platform) {
|
|
@@ -79,10 +79,13 @@ export function createDevServerPlatform() {
|
|
|
79
79
|
|
|
80
80
|
let dirHandle;
|
|
81
81
|
try {
|
|
82
|
-
dirHandle =
|
|
83
|
-
|
|
82
|
+
dirHandle =
|
|
83
|
+
await /** @type {{ showDirectoryPicker(opts: { mode: string }): Promise<FileSystemDirectoryHandle> }} */ (
|
|
84
|
+
window
|
|
85
|
+
).showDirectoryPicker({ mode: "readwrite" });
|
|
86
|
+
} catch (/** @type {unknown} */ e) {
|
|
84
87
|
// User cancelled the picker
|
|
85
|
-
if (e.name === "AbortError") return null;
|
|
88
|
+
if (/** @type {Error} */ (e).name === "AbortError") return null;
|
|
86
89
|
throw e;
|
|
87
90
|
}
|
|
88
91
|
|
|
@@ -102,7 +105,8 @@ export function createDevServerPlatform() {
|
|
|
102
105
|
if (!sitesRes.ok) throw new Error("Failed to fetch site list from server");
|
|
103
106
|
const sites = await sitesRes.json();
|
|
104
107
|
const match = sites.find(
|
|
105
|
-
/** @param {
|
|
108
|
+
/** @param {{ config: unknown; path: string }} s */ (s) =>
|
|
109
|
+
JSON.stringify(s.config) === JSON.stringify(config),
|
|
106
110
|
);
|
|
107
111
|
|
|
108
112
|
if (!match) {
|
|
@@ -149,6 +153,30 @@ export function createDevServerPlatform() {
|
|
|
149
153
|
}
|
|
150
154
|
},
|
|
151
155
|
|
|
156
|
+
// ─── Project creation ─────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @param {{
|
|
160
|
+
* name: string;
|
|
161
|
+
* description?: string;
|
|
162
|
+
* url?: string;
|
|
163
|
+
* adapter?: string;
|
|
164
|
+
* directory: string;
|
|
165
|
+
* }} opts
|
|
166
|
+
*/
|
|
167
|
+
async createProject(opts) {
|
|
168
|
+
const res = await fetch("/__studio/create-project", {
|
|
169
|
+
method: "POST",
|
|
170
|
+
headers: { "Content-Type": "application/json" },
|
|
171
|
+
body: JSON.stringify(opts),
|
|
172
|
+
});
|
|
173
|
+
if (!res.ok) {
|
|
174
|
+
const data = await res.json();
|
|
175
|
+
throw new Error(data.error || "Failed to create project");
|
|
176
|
+
}
|
|
177
|
+
return await res.json();
|
|
178
|
+
},
|
|
179
|
+
|
|
152
180
|
// ─── File operations ──────────────────────────────────────────────────
|
|
153
181
|
|
|
154
182
|
/** @param {string} dir */
|
|
@@ -270,7 +298,7 @@ export function createDevServerPlatform() {
|
|
|
270
298
|
|
|
271
299
|
/**
|
|
272
300
|
* @param {string} action
|
|
273
|
-
* @param {
|
|
301
|
+
* @param {unknown} payload
|
|
274
302
|
*/
|
|
275
303
|
async codeService(action, payload) {
|
|
276
304
|
try {
|
|
@@ -399,8 +427,13 @@ export function createDevServerPlatform() {
|
|
|
399
427
|
return await res.json();
|
|
400
428
|
},
|
|
401
429
|
|
|
402
|
-
|
|
403
|
-
|
|
430
|
+
/** @param {{ setUpstream?: boolean }} [opts] */
|
|
431
|
+
async gitPush(opts) {
|
|
432
|
+
const res = await fetch("/__studio/git/push", {
|
|
433
|
+
method: "POST",
|
|
434
|
+
headers: { "Content-Type": "application/json" },
|
|
435
|
+
body: JSON.stringify(opts || {}),
|
|
436
|
+
});
|
|
404
437
|
if (!res.ok) throw new Error((await res.json()).error);
|
|
405
438
|
return await res.json();
|
|
406
439
|
},
|
|
@@ -466,5 +499,78 @@ export function createDevServerPlatform() {
|
|
|
466
499
|
if (!res.ok) throw new Error((await res.json()).error);
|
|
467
500
|
return await res.json();
|
|
468
501
|
},
|
|
502
|
+
|
|
503
|
+
/** @param {string} url */
|
|
504
|
+
async gitClone(url) {
|
|
505
|
+
const res = await fetch("/__studio/git/clone", {
|
|
506
|
+
method: "POST",
|
|
507
|
+
headers: { "Content-Type": "application/json" },
|
|
508
|
+
body: JSON.stringify({ url }),
|
|
509
|
+
});
|
|
510
|
+
if (!res.ok) throw new Error((await res.json()).error);
|
|
511
|
+
return await res.json();
|
|
512
|
+
},
|
|
513
|
+
|
|
514
|
+
async gitInit() {
|
|
515
|
+
const res = await fetch("/__studio/git/init", { method: "POST" });
|
|
516
|
+
if (!res.ok) throw new Error((await res.json()).error);
|
|
517
|
+
},
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* @param {string} name
|
|
521
|
+
* @param {string} url
|
|
522
|
+
*/
|
|
523
|
+
async gitAddRemote(name, url) {
|
|
524
|
+
const res = await fetch("/__studio/git/add-remote", {
|
|
525
|
+
method: "POST",
|
|
526
|
+
headers: { "Content-Type": "application/json" },
|
|
527
|
+
body: JSON.stringify({ name, url }),
|
|
528
|
+
});
|
|
529
|
+
if (!res.ok) throw new Error((await res.json()).error);
|
|
530
|
+
},
|
|
531
|
+
|
|
532
|
+
// ─── AI Assistant ───────────────────────────────────
|
|
533
|
+
|
|
534
|
+
async aiAuthStatus() {
|
|
535
|
+
const res = await fetch("/__studio/ai/auth-status");
|
|
536
|
+
return await res.json();
|
|
537
|
+
},
|
|
538
|
+
|
|
539
|
+
/** @param {{ message: string; systemPrompt?: string }} opts */
|
|
540
|
+
async aiCreateSession(opts) {
|
|
541
|
+
const res = await fetch("/__studio/ai/session", {
|
|
542
|
+
method: "POST",
|
|
543
|
+
headers: { "Content-Type": "application/json" },
|
|
544
|
+
body: JSON.stringify(opts),
|
|
545
|
+
});
|
|
546
|
+
if (!res.ok) throw new Error((await res.json()).error);
|
|
547
|
+
return await res.json();
|
|
548
|
+
},
|
|
549
|
+
|
|
550
|
+
/** @param {string} id @param {string} message */
|
|
551
|
+
async aiSendMessage(id, message) {
|
|
552
|
+
const res = await fetch(`/__studio/ai/session/${id}/message`, {
|
|
553
|
+
method: "POST",
|
|
554
|
+
headers: { "Content-Type": "application/json" },
|
|
555
|
+
body: JSON.stringify({ message }),
|
|
556
|
+
});
|
|
557
|
+
if (!res.ok) throw new Error((await res.json()).error);
|
|
558
|
+
return await res.json();
|
|
559
|
+
},
|
|
560
|
+
|
|
561
|
+
/** @param {string} id */
|
|
562
|
+
aiStreamUrl(id) {
|
|
563
|
+
return `/__studio/ai/session/${id}/stream`;
|
|
564
|
+
},
|
|
565
|
+
|
|
566
|
+
/** @param {string} id */
|
|
567
|
+
async aiStopSession(id) {
|
|
568
|
+
await fetch(`/__studio/ai/session/${id}/stop`, { method: "POST" });
|
|
569
|
+
},
|
|
570
|
+
|
|
571
|
+
/** @param {string} id */
|
|
572
|
+
async aiDeleteSession(id) {
|
|
573
|
+
await fetch(`/__studio/ai/session/${id}`, { method: "DELETE" });
|
|
574
|
+
},
|
|
469
575
|
};
|
|
470
576
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @type {{
|
|
3
|
+
* __jxPlatform?: {
|
|
4
|
+
* windowControls?: {
|
|
5
|
+
* getFrame: () => Promise<{ x: number; y: number; width: number; height: number }>;
|
|
6
|
+
* setFrame: (x: number, y: number, w: number, h: number) => void;
|
|
7
|
+
* };
|
|
8
|
+
* };
|
|
9
|
+
* }}
|
|
10
|
+
*/
|
|
11
|
+
const g = /** @type {any} */ (globalThis);
|
|
12
|
+
|
|
13
|
+
const EDGES = [
|
|
14
|
+
"top",
|
|
15
|
+
"bottom",
|
|
16
|
+
"left",
|
|
17
|
+
"right",
|
|
18
|
+
"top-left",
|
|
19
|
+
"top-right",
|
|
20
|
+
"bottom-left",
|
|
21
|
+
"bottom-right",
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const MIN_WIDTH = 400;
|
|
25
|
+
const MIN_HEIGHT = 300;
|
|
26
|
+
|
|
27
|
+
let mounted = false;
|
|
28
|
+
|
|
29
|
+
export function mountResizeEdges() {
|
|
30
|
+
const wc = g.__jxPlatform?.windowControls;
|
|
31
|
+
if (!wc || !wc.getFrame || mounted) return;
|
|
32
|
+
mounted = true;
|
|
33
|
+
|
|
34
|
+
const container = document.createElement("div");
|
|
35
|
+
container.id = "resize-edges";
|
|
36
|
+
|
|
37
|
+
for (const edge of EDGES) {
|
|
38
|
+
const el = document.createElement("div");
|
|
39
|
+
el.className = `resize-edge ${edge}`;
|
|
40
|
+
el.addEventListener("mousedown", (e) => startResize(e, edge, wc));
|
|
41
|
+
container.appendChild(el);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
document.body.appendChild(container);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {MouseEvent} e
|
|
49
|
+
* @param {string} edge
|
|
50
|
+
* @param {{
|
|
51
|
+
* getFrame: () => Promise<{ x: number; y: number; width: number; height: number }>;
|
|
52
|
+
* setFrame: (x: number, y: number, w: number, h: number) => void;
|
|
53
|
+
* }} wc
|
|
54
|
+
*/
|
|
55
|
+
async function startResize(e, edge, wc) {
|
|
56
|
+
e.preventDefault();
|
|
57
|
+
e.stopPropagation();
|
|
58
|
+
|
|
59
|
+
const startX = e.screenX;
|
|
60
|
+
const startY = e.screenY;
|
|
61
|
+
const frame = await wc.getFrame();
|
|
62
|
+
const startFrame = { ...frame };
|
|
63
|
+
|
|
64
|
+
/** @param {MouseEvent} me */
|
|
65
|
+
function onMove(me) {
|
|
66
|
+
const dx = me.screenX - startX;
|
|
67
|
+
const dy = me.screenY - startY;
|
|
68
|
+
|
|
69
|
+
let { x, y, width, height } = startFrame;
|
|
70
|
+
|
|
71
|
+
if (edge.includes("right")) {
|
|
72
|
+
width = Math.max(MIN_WIDTH, width + dx);
|
|
73
|
+
}
|
|
74
|
+
if (edge.includes("left")) {
|
|
75
|
+
const newW = Math.max(MIN_WIDTH, width - dx);
|
|
76
|
+
x = x + (width - newW);
|
|
77
|
+
width = newW;
|
|
78
|
+
}
|
|
79
|
+
if (edge.includes("bottom")) {
|
|
80
|
+
height = Math.max(MIN_HEIGHT, height + dy);
|
|
81
|
+
}
|
|
82
|
+
if (edge.includes("top")) {
|
|
83
|
+
const newH = Math.max(MIN_HEIGHT, height - dy);
|
|
84
|
+
y = y + (height - newH);
|
|
85
|
+
height = newH;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
wc.setFrame(x, y, width, height);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function onUp() {
|
|
92
|
+
document.removeEventListener("mousemove", onMove);
|
|
93
|
+
document.removeEventListener("mouseup", onUp);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
document.addEventListener("mousemove", onMove);
|
|
97
|
+
document.addEventListener("mouseup", onUp);
|
|
98
|
+
}
|
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
/** Collect slot elements from the document tree. */
|
|
2
|
-
export function collectSlots(
|
|
2
|
+
export function collectSlots(
|
|
3
|
+
/** @type {JxMutableNode | null | undefined} */ node,
|
|
4
|
+
/** @type {string[]} */ slots = [],
|
|
5
|
+
) {
|
|
3
6
|
if (node?.tagName === "slot") {
|
|
4
7
|
slots.push(node.attributes?.name || "");
|
|
5
8
|
}
|
|
6
9
|
if (Array.isArray(node?.children))
|
|
7
|
-
node.children.forEach((/** @type {
|
|
10
|
+
node.children.forEach((/** @type {JxMutableNode | string} */ c) =>
|
|
11
|
+
collectSlots(typeof c === "string" ? undefined : c, slots),
|
|
12
|
+
);
|
|
8
13
|
return slots;
|
|
9
14
|
}
|
|
10
15
|
|
|
11
16
|
/**
|
|
12
17
|
* Generate and download a CEM 2.1.0 manifest for the current document.
|
|
13
18
|
*
|
|
14
|
-
* @param {
|
|
19
|
+
* @param {{ document: JxMutableNode }} S - Studio state
|
|
15
20
|
* @param {{
|
|
16
|
-
* defCategory: (d:
|
|
17
|
-
* normParam: (p:
|
|
18
|
-
* collectCssParts: (node:
|
|
21
|
+
* defCategory: (d: unknown) => string;
|
|
22
|
+
* normParam: (p: unknown) => unknown;
|
|
23
|
+
* collectCssParts: (node: JxMutableNode) => { name: string }[];
|
|
19
24
|
* }} helpers
|
|
20
25
|
*/
|
|
21
26
|
export function exportCemManifest(S, helpers) {
|
|
@@ -83,7 +88,7 @@ export function exportCemManifest(S, helpers) {
|
|
|
83
88
|
|
|
84
89
|
// Slots
|
|
85
90
|
const slotNames = collectSlots(doc);
|
|
86
|
-
const slots = slotNames.map((/** @type {
|
|
91
|
+
const slots = slotNames.map((/** @type {string} */ name) => ({
|
|
87
92
|
name: name || "",
|
|
88
93
|
...(name ? {} : { description: "Default slot" }),
|
|
89
94
|
}));
|
|
@@ -5,8 +5,8 @@ import { getNodeAtPath } from "../store.js";
|
|
|
5
5
|
import * as monaco from "monaco-editor/esm/vs/editor/editor.api.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @param {
|
|
9
|
-
* @param {
|
|
8
|
+
* @param {string} action
|
|
9
|
+
* @param {unknown} payload
|
|
10
10
|
*/
|
|
11
11
|
export async function codeService(action, payload) {
|
|
12
12
|
const platform = getPlatform();
|
|
@@ -17,7 +17,7 @@ export async function codeService(action, payload) {
|
|
|
17
17
|
/**
|
|
18
18
|
* Ask the server to locate a document by filename within the project root.
|
|
19
19
|
*
|
|
20
|
-
* @param {
|
|
20
|
+
* @param {string} name
|
|
21
21
|
*/
|
|
22
22
|
export async function locateDocument(name) {
|
|
23
23
|
const platform = getPlatform();
|
|
@@ -31,8 +31,8 @@ export const pluginSchemaCache = new Map();
|
|
|
31
31
|
/**
|
|
32
32
|
* Fetch and cache the schema for an external $prototype + $src module via the server.
|
|
33
33
|
*
|
|
34
|
-
* @param {
|
|
35
|
-
* @param {
|
|
34
|
+
* @param {{ $src?: string; $prototype?: string }} def
|
|
35
|
+
* @param {{ documentPath?: string }} state
|
|
36
36
|
*/
|
|
37
37
|
export async function fetchPluginSchema(def, state) {
|
|
38
38
|
if (!def.$src || !def.$prototype) return null;
|
|
@@ -56,8 +56,19 @@ export async function fetchPluginSchema(def, state) {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
* @
|
|
60
|
-
*
|
|
59
|
+
* @typedef {{
|
|
60
|
+
* severity: string;
|
|
61
|
+
* message: string;
|
|
62
|
+
* help?: string;
|
|
63
|
+
* code?: string;
|
|
64
|
+
* url?: string;
|
|
65
|
+
* labels?: { span: { line: number; column: number; length?: number } }[];
|
|
66
|
+
* }} OxLintDiagnostic
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @param {import("monaco-editor").editor.IStandaloneCodeEditor} editor
|
|
71
|
+
* @param {OxLintDiagnostic[]} diagnostics
|
|
61
72
|
*/
|
|
62
73
|
export function setLintMarkers(editor, diagnostics) {
|
|
63
74
|
const model = editor.getModel();
|
|
@@ -80,19 +91,26 @@ export function setLintMarkers(editor, diagnostics) {
|
|
|
80
91
|
};
|
|
81
92
|
})
|
|
82
93
|
.filter(Boolean);
|
|
83
|
-
monaco.editor.setModelMarkers(
|
|
94
|
+
monaco.editor.setModelMarkers(
|
|
95
|
+
model,
|
|
96
|
+
"oxlint",
|
|
97
|
+
/** @type {import("monaco-editor").editor.IMarkerData[]} */ (markers),
|
|
98
|
+
);
|
|
84
99
|
}
|
|
85
100
|
|
|
86
101
|
/**
|
|
87
|
-
* @param {
|
|
88
|
-
* @param {
|
|
102
|
+
* @param {{ type: string; defName?: string; path?: JxPath; eventKey?: string }} editing
|
|
103
|
+
* @param {JxMutableNode | null | undefined} document
|
|
89
104
|
*/
|
|
90
105
|
export function getFunctionArgs(editing, document) {
|
|
91
106
|
if (editing.type === "def") {
|
|
92
|
-
|
|
107
|
+
const defName = editing.defName;
|
|
108
|
+
return (defName && document?.state?.[defName]?.parameters) || ["state", "event"];
|
|
93
109
|
} else if (editing.type === "event") {
|
|
110
|
+
if (!document || !editing.path) return ["state", "event"];
|
|
94
111
|
const node = getNodeAtPath(document, editing.path);
|
|
95
|
-
|
|
112
|
+
const eventKey = editing.eventKey;
|
|
113
|
+
return (eventKey && node?.[eventKey]?.parameters) || ["state", "event"];
|
|
96
114
|
}
|
|
97
115
|
return ["state", "event"];
|
|
98
116
|
}
|
|
@@ -29,7 +29,7 @@ let newContentTypeName = "";
|
|
|
29
29
|
|
|
30
30
|
async function saveProjectConfig() {
|
|
31
31
|
const platform = getPlatform();
|
|
32
|
-
const config = /** @type {
|
|
32
|
+
const config = /** @type {{ projectConfig: ProjectConfig }} */ (projectState).projectConfig;
|
|
33
33
|
await platform.writeFile("project.json", JSON.stringify(config, null, "\t"));
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -138,7 +138,7 @@ function handleRenameField(oldName, newName, rerender) {
|
|
|
138
138
|
const schema = getSelectedSchema();
|
|
139
139
|
if (!schema?.properties || !newName || schema.properties[newName]) return;
|
|
140
140
|
|
|
141
|
-
/** @type {Record<string,
|
|
141
|
+
/** @type {Record<string, unknown>} */
|
|
142
142
|
const newProps = {};
|
|
143
143
|
for (const [key, val] of Object.entries(schema.properties)) {
|
|
144
144
|
newProps[key === oldName ? newName : key] = val;
|
|
@@ -276,7 +276,7 @@ function handleRenameNested(parentName, oldChild, newChild, rerender) {
|
|
|
276
276
|
const parent = schema?.properties?.[parentName];
|
|
277
277
|
if (!parent?.properties || !newChild || parent.properties[newChild]) return;
|
|
278
278
|
|
|
279
|
-
/** @type {Record<string,
|
|
279
|
+
/** @type {Record<string, unknown>} */
|
|
280
280
|
const newProps = {};
|
|
281
281
|
for (const [key, val] of Object.entries(parent.properties)) {
|
|
282
282
|
newProps[key === oldChild ? newChild : key] = val;
|
|
@@ -382,10 +382,10 @@ export function renderContentTypesEditor(container) {
|
|
|
382
382
|
size="s"
|
|
383
383
|
placeholder="content-type-name"
|
|
384
384
|
.value=${newContentTypeName}
|
|
385
|
-
@input=${(/** @type {
|
|
386
|
-
newContentTypeName = e.target.value;
|
|
385
|
+
@input=${(/** @type {Event} */ e) => {
|
|
386
|
+
newContentTypeName = /** @type {HTMLInputElement} */ (e.target).value;
|
|
387
387
|
}}
|
|
388
|
-
@keydown=${(/** @type {
|
|
388
|
+
@keydown=${(/** @type {KeyboardEvent} */ e) => {
|
|
389
389
|
if (e.key === "Enter") handleNewContentType(rerender);
|
|
390
390
|
if (e.key === "Escape") {
|
|
391
391
|
showNewContentType = false;
|
|
@@ -445,7 +445,7 @@ export function renderContentTypesEditor(container) {
|
|
|
445
445
|
([name, def]) =>
|
|
446
446
|
fieldCardTpl(
|
|
447
447
|
name,
|
|
448
|
-
/** @type {
|
|
448
|
+
/** @type {import("./schema-field-ui.js").SchemaProperty} */ (def),
|
|
449
449
|
required.includes(name),
|
|
450
450
|
handlers,
|
|
451
451
|
contentTypeNames,
|