@jxsuite/studio 0.15.0 → 0.16.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 +10183 -10116
- package/dist/studio.js.map +40 -40
- package/package.json +2 -2
- package/src/canvas/canvas-render.js +50 -40
- package/src/files/file-ops.js +4 -2
- package/src/files/files.js +67 -67
- package/src/panels/editors.js +10 -6
- package/src/panels/git-panel.js +17 -15
- package/src/panels/left-panel.js +4 -0
- package/src/panels/signals-panel.js +63 -40
- package/src/platforms/devserver.js +10 -0
- package/src/services/code-services.js +4 -4
- package/src/studio.js +18 -4
- package/src/tabs/transact.js +6 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/studio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Jx Studio — visual builder for Jx documents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@atlaskit/pragmatic-drag-and-drop": "^1.8.1",
|
|
32
32
|
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.1.0",
|
|
33
|
-
"@jxsuite/runtime": "^0.
|
|
33
|
+
"@jxsuite/runtime": "^0.15.0",
|
|
34
34
|
"@spectrum-web-components/accordion": "^1.12.1",
|
|
35
35
|
"@spectrum-web-components/action-bar": "1.12.1",
|
|
36
36
|
"@spectrum-web-components/action-button": "^1.12.1",
|
|
@@ -10,6 +10,7 @@ import * as monaco from "monaco-editor/esm/vs/editor/editor.api.js";
|
|
|
10
10
|
import { canvasWrap, canvasPanels, updateCanvas } from "../store.js";
|
|
11
11
|
import { activeTab } from "../workspace/workspace.js";
|
|
12
12
|
import { view } from "../view.js";
|
|
13
|
+
import { loadMarkdown } from "../files/file-ops.js";
|
|
13
14
|
import {
|
|
14
15
|
canvasPanelTemplate,
|
|
15
16
|
applyTransform,
|
|
@@ -144,6 +145,7 @@ export function renderCanvas() {
|
|
|
144
145
|
// Reset inline style overrides from other modes
|
|
145
146
|
canvasWrap.style.padding = "";
|
|
146
147
|
canvasWrap.style.alignItems = "";
|
|
148
|
+
canvasWrap.style.flexDirection = "";
|
|
147
149
|
canvasWrap.style.display = "";
|
|
148
150
|
canvasWrap.style.overflow = "";
|
|
149
151
|
canvasWrap.style.overflow = "";
|
|
@@ -247,72 +249,79 @@ export function renderCanvas() {
|
|
|
247
249
|
return;
|
|
248
250
|
}
|
|
249
251
|
|
|
250
|
-
// Git diff mode — render original (left) and current (right) side-by-side
|
|
252
|
+
// Git diff mode — render original (left) and current (right) side-by-side on panzoom surface
|
|
251
253
|
if (canvasMode === "git-diff") {
|
|
252
254
|
if (!_ctx.gitDiffState) {
|
|
253
|
-
// Fallback to design mode if diff state is missing
|
|
254
255
|
_ctx.setCanvasMode("design");
|
|
255
256
|
renderCanvas();
|
|
256
257
|
return;
|
|
257
258
|
}
|
|
258
259
|
|
|
259
|
-
|
|
260
|
-
|
|
260
|
+
if (modeChanged) {
|
|
261
|
+
canvasWrap.style.padding = "0";
|
|
262
|
+
canvasWrap.style.overflow = "hidden";
|
|
263
|
+
}
|
|
261
264
|
|
|
262
265
|
const gitDiffState = _ctx.gitDiffState;
|
|
263
|
-
const
|
|
266
|
+
const panelWidth = 800;
|
|
267
|
+
|
|
268
|
+
const { tpl: origTpl, panel: origPanel } = canvasPanelTemplate(
|
|
264
269
|
"git-diff-original",
|
|
265
270
|
"Original",
|
|
266
271
|
false,
|
|
267
|
-
|
|
272
|
+
panelWidth,
|
|
268
273
|
);
|
|
269
|
-
const { tpl:
|
|
274
|
+
const { tpl: currTpl, panel: currPanel } = canvasPanelTemplate(
|
|
270
275
|
"git-diff-current",
|
|
271
276
|
"Current",
|
|
272
277
|
false,
|
|
273
|
-
|
|
278
|
+
panelWidth,
|
|
274
279
|
);
|
|
275
280
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
<div
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
281
|
+
litRender(
|
|
282
|
+
html`
|
|
283
|
+
<div
|
|
284
|
+
class="panzoom-wrap"
|
|
285
|
+
style="transform-origin:0 0"
|
|
286
|
+
${ref((el) => {
|
|
287
|
+
if (el) view.panzoomWrap = /** @type {HTMLDivElement} */ (el);
|
|
288
|
+
})}
|
|
289
|
+
>
|
|
290
|
+
${origTpl} ${currTpl}
|
|
283
291
|
</div>
|
|
284
|
-
|
|
285
|
-
|
|
292
|
+
`,
|
|
293
|
+
canvasWrap,
|
|
294
|
+
);
|
|
286
295
|
|
|
287
|
-
litRender(diffTpl, canvasWrap);
|
|
288
296
|
canvasPanels.push(/** @type {any} */ (origPanel));
|
|
289
297
|
canvasPanels.push(/** @type {any} */ (currPanel));
|
|
290
298
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
try {
|
|
299
|
+
/** @param {string} content */
|
|
300
|
+
const parseContent = (content) => {
|
|
294
301
|
if (gitDiffState.isMarkdown) {
|
|
295
|
-
|
|
296
|
-
// For now, use a simple placeholder until we have markdown parsing
|
|
297
|
-
originalDoc = {
|
|
298
|
-
tagName: "div",
|
|
299
|
-
children: [{ tagName: "p", textContent: "Original (markdown)" }],
|
|
300
|
-
};
|
|
301
|
-
} else {
|
|
302
|
-
// Parse as JSON
|
|
303
|
-
originalDoc = JSON.parse(gitDiffState.originalContent);
|
|
302
|
+
return loadMarkdown(content).then((r) => r.document);
|
|
304
303
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
304
|
+
return Promise.resolve().then(() => {
|
|
305
|
+
try {
|
|
306
|
+
return JSON.parse(content);
|
|
307
|
+
} catch {
|
|
308
|
+
return { tagName: "div", children: [{ tagName: "p", textContent: "Failed to parse" }] };
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const featureToggles = S.ui.featureToggles;
|
|
314
|
+
Promise.all([
|
|
315
|
+
parseContent(gitDiffState.originalContent || ""),
|
|
316
|
+
parseContent(gitDiffState.currentContent || ""),
|
|
317
|
+
]).then(([originalDoc, currentDoc]) => {
|
|
318
|
+
renderCanvasIntoPanel(origPanel, new Set(), featureToggles, originalDoc, gitDiffState);
|
|
319
|
+
renderCanvasIntoPanel(currPanel, new Set(), featureToggles, currentDoc, gitDiffState);
|
|
320
|
+
});
|
|
313
321
|
|
|
314
|
-
|
|
315
|
-
|
|
322
|
+
applyTransform();
|
|
323
|
+
if (modeChanged) observeCenterUntilStable();
|
|
324
|
+
renderZoomIndicator();
|
|
316
325
|
return;
|
|
317
326
|
}
|
|
318
327
|
|
|
@@ -498,6 +507,7 @@ function renderCanvasIntoPanel(
|
|
|
498
507
|
} else {
|
|
499
508
|
// Fallback to structural preview
|
|
500
509
|
updateCanvas({ status: "ready", scope: null, error: null });
|
|
510
|
+
panel.canvas.innerHTML = "";
|
|
501
511
|
renderCanvasNode(docToRender, [], panel.canvas, activeBreakpoints, featureToggles);
|
|
502
512
|
}
|
|
503
513
|
registerPanelDnD(panel);
|
package/src/files/file-ops.js
CHANGED
|
@@ -174,9 +174,11 @@ export async function exportFile() {
|
|
|
174
174
|
* @param {import("../tabs/tab.js").Tab} tab
|
|
175
175
|
* @returns {string}
|
|
176
176
|
*/
|
|
177
|
-
function serializeDocument(tab) {
|
|
177
|
+
export function serializeDocument(tab) {
|
|
178
178
|
if (tab.doc.sourceFormat === "md") {
|
|
179
|
-
|
|
179
|
+
const fm = tab.doc.content?.frontmatter || {};
|
|
180
|
+
const fullDoc = { ...fm, children: tab.doc.document.children ?? [] };
|
|
181
|
+
return jxDocToMd(fullDoc);
|
|
180
182
|
}
|
|
181
183
|
if (tab.doc.mode === "content") {
|
|
182
184
|
const mdast = jxToMd(tab.doc.document);
|
package/src/files/files.js
CHANGED
|
@@ -383,83 +383,83 @@ export function setupTreeKeyboard(/** @type {HTMLElement} */ tree) {
|
|
|
383
383
|
// ─── Context menu ─────────────────────────────────────────────────────────────
|
|
384
384
|
|
|
385
385
|
/** @type {HTMLElement | null} */
|
|
386
|
-
let
|
|
386
|
+
let _fileCtxHost = null;
|
|
387
|
+
|
|
388
|
+
function getFileCtxHost() {
|
|
389
|
+
if (!_fileCtxHost) {
|
|
390
|
+
_fileCtxHost = document.createElement("div");
|
|
391
|
+
_fileCtxHost.style.display = "contents";
|
|
392
|
+
(document.querySelector("sp-theme") || document.body).appendChild(_fileCtxHost);
|
|
393
|
+
document.addEventListener("click", dismissFileContextMenu);
|
|
394
|
+
}
|
|
395
|
+
return _fileCtxHost;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function dismissFileContextMenu() {
|
|
399
|
+
const host = _fileCtxHost;
|
|
400
|
+
if (!host) return;
|
|
401
|
+
const popover = host.querySelector("sp-popover");
|
|
402
|
+
if (popover) popover.removeAttribute("open");
|
|
403
|
+
}
|
|
387
404
|
|
|
388
405
|
function showFileContextMenu(
|
|
389
406
|
/** @type {MouseEvent} */ e,
|
|
390
407
|
/** @type {{ name: string; path: string; type: string }} */ entry,
|
|
391
408
|
/** @type {{ openFileFn: (path: string) => void; renderLeftPanel: () => void }} */ ctx,
|
|
392
409
|
) {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
fileContextPopover = null;
|
|
396
|
-
}
|
|
397
|
-
|
|
410
|
+
e.preventDefault();
|
|
411
|
+
const host = getFileCtxHost();
|
|
398
412
|
const isDir = entry.type === "directory";
|
|
399
|
-
fileContextPopover = document.createElement("div");
|
|
400
413
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
placement="right-start"
|
|
404
|
-
open
|
|
405
|
-
style="position:fixed;left:${e.clientX}px;top:${e.clientY}px;z-index:9999"
|
|
406
|
-
>
|
|
407
|
-
<sp-menu style="min-width:160px">
|
|
408
|
-
${!isDir
|
|
409
|
-
? html`<sp-menu-item
|
|
410
|
-
@click=${() => {
|
|
411
|
-
closeFileContextMenu();
|
|
412
|
-
ctx.openFileFn(entry.path);
|
|
413
|
-
}}
|
|
414
|
-
>Open</sp-menu-item
|
|
415
|
-
>`
|
|
416
|
-
: nothing}
|
|
417
|
-
${isDir
|
|
418
|
-
? html`<sp-menu-item
|
|
419
|
-
@click=${() => {
|
|
420
|
-
closeFileContextMenu();
|
|
421
|
-
createNewFile(entry.path, ctx.renderLeftPanel);
|
|
422
|
-
}}
|
|
423
|
-
>New File…</sp-menu-item
|
|
424
|
-
>`
|
|
425
|
-
: nothing}
|
|
426
|
-
<sp-menu-divider></sp-menu-divider>
|
|
427
|
-
<sp-menu-item
|
|
428
|
-
@click=${() => {
|
|
429
|
-
closeFileContextMenu();
|
|
430
|
-
renameFile(entry, ctx.renderLeftPanel);
|
|
431
|
-
}}
|
|
432
|
-
>Rename…</sp-menu-item
|
|
433
|
-
>
|
|
434
|
-
<sp-menu-item
|
|
435
|
-
style="color:var(--danger)"
|
|
436
|
-
@click=${() => {
|
|
437
|
-
closeFileContextMenu();
|
|
438
|
-
deleteFile(entry, ctx.renderLeftPanel);
|
|
439
|
-
}}
|
|
440
|
-
>Delete</sp-menu-item
|
|
441
|
-
>
|
|
442
|
-
</sp-menu>
|
|
443
|
-
</sp-popover>
|
|
444
|
-
`;
|
|
445
|
-
|
|
446
|
-
litRender(tpl, fileContextPopover);
|
|
447
|
-
document.body.appendChild(fileContextPopover);
|
|
448
|
-
|
|
449
|
-
const closeHandler = (/** @type {MouseEvent} */ ev) => {
|
|
450
|
-
if (!fileContextPopover?.contains(/** @type {Node} */ (ev.target))) {
|
|
451
|
-
closeFileContextMenu();
|
|
452
|
-
document.removeEventListener("mousedown", closeHandler, true);
|
|
453
|
-
}
|
|
454
|
-
};
|
|
455
|
-
setTimeout(() => document.addEventListener("mousedown", closeHandler, true), 0);
|
|
456
|
-
}
|
|
414
|
+
/** @type {{ label: string; action?: () => void; danger?: boolean }[]} */
|
|
415
|
+
const items = [];
|
|
457
416
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
fileContextPopover.remove();
|
|
461
|
-
fileContextPopover = null;
|
|
417
|
+
if (!isDir) {
|
|
418
|
+
items.push({ label: "Open", action: () => ctx.openFileFn(entry.path) });
|
|
462
419
|
}
|
|
420
|
+
if (isDir) {
|
|
421
|
+
items.push({
|
|
422
|
+
label: "New File\u2026",
|
|
423
|
+
action: () => createNewFile(entry.path, ctx.renderLeftPanel),
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
items.push({ label: "\u2014" });
|
|
427
|
+
items.push({ label: "Rename\u2026", action: () => renameFile(entry, ctx.renderLeftPanel) });
|
|
428
|
+
items.push({
|
|
429
|
+
label: "Delete",
|
|
430
|
+
action: () => deleteFile(entry, ctx.renderLeftPanel),
|
|
431
|
+
danger: true,
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
litRender(
|
|
435
|
+
html`<sp-popover style="position:fixed;z-index:10000">
|
|
436
|
+
<sp-menu>
|
|
437
|
+
${items.map((item) =>
|
|
438
|
+
item.label === "\u2014"
|
|
439
|
+
? html`<sp-menu-divider></sp-menu-divider>`
|
|
440
|
+
: html`<sp-menu-item
|
|
441
|
+
style=${item.danger ? "color: var(--danger)" : ""}
|
|
442
|
+
@click=${() => {
|
|
443
|
+
dismissFileContextMenu();
|
|
444
|
+
item.action?.();
|
|
445
|
+
}}
|
|
446
|
+
>${item.label}</sp-menu-item
|
|
447
|
+
>`,
|
|
448
|
+
)}
|
|
449
|
+
</sp-menu>
|
|
450
|
+
</sp-popover>`,
|
|
451
|
+
host,
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
const popover = /** @type {HTMLElement} */ (host.querySelector("sp-popover"));
|
|
455
|
+
popover.setAttribute("open", "");
|
|
456
|
+
const menuRect = popover.getBoundingClientRect();
|
|
457
|
+
let x = e.clientX,
|
|
458
|
+
y = e.clientY;
|
|
459
|
+
if (x + menuRect.width > window.innerWidth) x = window.innerWidth - menuRect.width - 4;
|
|
460
|
+
if (y + menuRect.height > window.innerHeight) y = window.innerHeight - menuRect.height - 4;
|
|
461
|
+
popover.style.left = `${x}px`;
|
|
462
|
+
popover.style.top = `${y}px`;
|
|
463
463
|
}
|
|
464
464
|
|
|
465
465
|
// ─── File CRUD ────────────────────────────────────────────────────────────────
|
package/src/panels/editors.js
CHANGED
|
@@ -57,6 +57,8 @@ export function renderFunctionEditor() {
|
|
|
57
57
|
|
|
58
58
|
litRender(nothing, canvasWrap);
|
|
59
59
|
canvasWrap.style.padding = "0";
|
|
60
|
+
canvasWrap.style.flexDirection = "column";
|
|
61
|
+
canvasWrap.style.alignItems = "stretch";
|
|
60
62
|
|
|
61
63
|
// Toolbar breadcrumb handles context display — re-render it
|
|
62
64
|
renderOnly("toolbar");
|
|
@@ -65,12 +67,14 @@ export function renderFunctionEditor() {
|
|
|
65
67
|
/** @type {HTMLDivElement | null} */
|
|
66
68
|
let editorContainer = null;
|
|
67
69
|
litRender(
|
|
68
|
-
html`<div
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
html`<div class="source-wrap">
|
|
71
|
+
<div
|
|
72
|
+
class="source-editor"
|
|
73
|
+
${ref((el) => {
|
|
74
|
+
if (el) editorContainer = /** @type {HTMLDivElement} */ (el);
|
|
75
|
+
})}
|
|
76
|
+
></div>
|
|
77
|
+
</div>`,
|
|
74
78
|
canvasWrap,
|
|
75
79
|
);
|
|
76
80
|
|
package/src/panels/git-panel.js
CHANGED
|
@@ -7,6 +7,7 @@ import { html, nothing } from "lit-html";
|
|
|
7
7
|
import { live } from "lit-html/directives/live.js";
|
|
8
8
|
import { getPlatform } from "../platform.js";
|
|
9
9
|
import { updateUi, renderOnly } from "../store.js";
|
|
10
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
10
11
|
import { view } from "../view.js";
|
|
11
12
|
|
|
12
13
|
async function refreshGitStatus() {
|
|
@@ -70,7 +71,8 @@ export function renderGitPanel(S, ctx) {
|
|
|
70
71
|
const totalChanges = status?.files?.length || 0;
|
|
71
72
|
|
|
72
73
|
const doCommit = async () => {
|
|
73
|
-
const
|
|
74
|
+
const tab = activeTab.value;
|
|
75
|
+
const msg = tab?.session.ui.gitCommitMessage?.trim();
|
|
74
76
|
if (!msg) return;
|
|
75
77
|
updateUi("gitCommitMessage", "");
|
|
76
78
|
await gitAction("gitCommit", msg);
|
|
@@ -145,11 +147,7 @@ export function renderGitPanel(S, ctx) {
|
|
|
145
147
|
}
|
|
146
148
|
}}
|
|
147
149
|
></sp-textfield>
|
|
148
|
-
<sp-action-button
|
|
149
|
-
class="git-commit-btn"
|
|
150
|
-
@click=${doCommit}
|
|
151
|
-
?disabled=${!S.ui.gitCommitMessage?.trim() || loading}
|
|
152
|
-
>
|
|
150
|
+
<sp-action-button class="git-commit-btn" @click=${doCommit} ?disabled=${loading}>
|
|
153
151
|
<sp-icon-checkmark slot="icon" size="xs"></sp-icon-checkmark>
|
|
154
152
|
Commit
|
|
155
153
|
</sp-action-button>
|
|
@@ -162,29 +160,33 @@ export function renderGitPanel(S, ctx) {
|
|
|
162
160
|
const dir = parts.join("/");
|
|
163
161
|
|
|
164
162
|
const onFileClick = async () => {
|
|
165
|
-
// Only support diff for modified/added files
|
|
166
163
|
if (file.status !== "M" && file.status !== "A") return;
|
|
167
164
|
if (!file.path.endsWith(".md") && !file.path.endsWith(".json")) return;
|
|
168
165
|
|
|
169
166
|
try {
|
|
170
167
|
const plat = getPlatform();
|
|
171
168
|
updateUi("gitLoading", true);
|
|
172
|
-
const originalContent = await plat.gitShow({ path: file.path, ref: "HEAD" });
|
|
173
169
|
|
|
174
|
-
|
|
175
|
-
|
|
170
|
+
const [originalContent, currentContent] = await Promise.all([
|
|
171
|
+
file.status === "A"
|
|
172
|
+
? Promise.resolve("")
|
|
173
|
+
: plat.gitShow({ path: file.path, ref: "HEAD" }),
|
|
174
|
+
plat.readFile(file.path),
|
|
175
|
+
]);
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
updateUi("gitDiffState", {
|
|
177
|
+
const isMarkdown = file.path.endsWith(".md");
|
|
178
|
+
const diffState = {
|
|
180
179
|
filePath: file.path,
|
|
181
180
|
originalContent,
|
|
181
|
+
currentContent,
|
|
182
182
|
isMarkdown,
|
|
183
183
|
fileStatus: file.status,
|
|
184
|
-
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
updateUi("gitDiffState", diffState);
|
|
185
187
|
|
|
186
|
-
// Trigger diff mode via context
|
|
187
188
|
if (ctx?.setCanvasMode) {
|
|
189
|
+
if (ctx.setGitDiffState) ctx.setGitDiffState(diffState);
|
|
188
190
|
ctx.setCanvasMode("git-diff");
|
|
189
191
|
}
|
|
190
192
|
} catch (/** @type {any} */ e) {
|
package/src/panels/left-panel.js
CHANGED
|
@@ -40,6 +40,7 @@ import { selectStylebookTag, stylebookMeta } from "./stylebook-panel.js";
|
|
|
40
40
|
* registerElementsDnD: () => void;
|
|
41
41
|
* registerComponentsDnD: () => void;
|
|
42
42
|
* setupTreeKeyboard: (tree: HTMLElement) => void;
|
|
43
|
+
* setGitDiffState: (state: any) => void;
|
|
43
44
|
* }} LeftPanelCtx
|
|
44
45
|
*/
|
|
45
46
|
|
|
@@ -69,6 +70,9 @@ export function mount(ctx) {
|
|
|
69
70
|
void tab.doc.mode;
|
|
70
71
|
void tab.session.selection;
|
|
71
72
|
void tab.session.ui.settingsTab;
|
|
73
|
+
void tab.session.ui.gitStatus;
|
|
74
|
+
void tab.session.ui.gitLoading;
|
|
75
|
+
void tab.session.ui.gitError;
|
|
72
76
|
render();
|
|
73
77
|
});
|
|
74
78
|
});
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
mutateRenameDef,
|
|
15
15
|
} from "../tabs/transact.js";
|
|
16
16
|
import { renderFieldRow } from "../ui/field-row.js";
|
|
17
|
+
import { renderMediaPicker } from "../ui/media-picker.js";
|
|
17
18
|
import { fetchPluginSchema, pluginSchemaCache } from "../services/code-services.js";
|
|
18
19
|
|
|
19
20
|
// ─── Module-local state ─────────────────────────────────────────────────────
|
|
@@ -343,6 +344,9 @@ function renderSignalEditorTemplate(
|
|
|
343
344
|
/** @type {any} */ def,
|
|
344
345
|
/** @type {any} */ ctx,
|
|
345
346
|
) {
|
|
347
|
+
if (typeof def !== "object" || def === null) {
|
|
348
|
+
def = { default: def };
|
|
349
|
+
}
|
|
346
350
|
const cat = defCategory(def);
|
|
347
351
|
|
|
348
352
|
// Helper for picker rows
|
|
@@ -470,20 +474,31 @@ function renderSignalEditorTemplate(
|
|
|
470
474
|
),
|
|
471
475
|
)
|
|
472
476
|
: nothing}
|
|
473
|
-
${
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
|
|
477
|
+
${def.format === "image"
|
|
478
|
+
? renderFieldRow({
|
|
479
|
+
prop: "Default",
|
|
480
|
+
label: "Default",
|
|
481
|
+
hasValue: false,
|
|
482
|
+
widget: renderMediaPicker("default", defaultVal, (/** @type {any} */ v) => {
|
|
483
|
+
transactDoc(activeTab.value, (t) =>
|
|
484
|
+
mutateUpdateDef(t, name, { default: v || undefined }),
|
|
485
|
+
);
|
|
486
|
+
}),
|
|
487
|
+
})
|
|
488
|
+
: signalFieldRow("Default", defaultVal, (/** @type {any} */ v) => {
|
|
489
|
+
let parsed = v;
|
|
490
|
+
if (def.type === "integer") parsed = parseInt(v, 10) || 0;
|
|
491
|
+
else if (def.type === "number") parsed = parseFloat(v) || 0;
|
|
492
|
+
else if (def.type === "boolean") parsed = v === "true";
|
|
493
|
+
else if (def.type === "array" || def.type === "object") {
|
|
494
|
+
try {
|
|
495
|
+
parsed = JSON.parse(v);
|
|
496
|
+
} catch {
|
|
497
|
+
parsed = v;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { default: parsed }));
|
|
501
|
+
})}
|
|
487
502
|
${signalFieldRow("Description", def.description || "", (/** @type {any} */ v) =>
|
|
488
503
|
transactDoc(activeTab.value, (t) =>
|
|
489
504
|
mutateUpdateDef(t, name, { description: v || undefined }),
|
|
@@ -644,7 +659,16 @@ function renderFunctionFields(
|
|
|
644
659
|
/** @type {any} */ textareaRow,
|
|
645
660
|
/** @type {any} */ ctx,
|
|
646
661
|
) {
|
|
647
|
-
const
|
|
662
|
+
const descriptionField = signalFieldRow(
|
|
663
|
+
"Description",
|
|
664
|
+
def.description || "",
|
|
665
|
+
(/** @type {any} */ v) =>
|
|
666
|
+
transactDoc(activeTab.value, (t) =>
|
|
667
|
+
mutateUpdateDef(t, name, { description: v || undefined }),
|
|
668
|
+
),
|
|
669
|
+
);
|
|
670
|
+
|
|
671
|
+
const bodyField = def.$src
|
|
648
672
|
? html`
|
|
649
673
|
${signalFieldRow("Source", def.$src || "", (/** @type {any} */ v) =>
|
|
650
674
|
transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { $src: v || undefined })),
|
|
@@ -655,36 +679,35 @@ function renderFunctionFields(
|
|
|
655
679
|
),
|
|
656
680
|
)}
|
|
657
681
|
`
|
|
658
|
-
:
|
|
659
|
-
"
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
return html`
|
|
667
|
-
${srcFields} ${renderParameterEditorTemplate(S, name, def, ctx)}
|
|
668
|
-
${isCustomElementDoc(S) ? renderEmitsEditorTemplate(S, name, def) : nothing}
|
|
669
|
-
${!def.$src
|
|
670
|
-
? html`
|
|
671
|
-
<button
|
|
672
|
-
class="kv-add"
|
|
673
|
-
style="margin-top:4px"
|
|
682
|
+
: html`
|
|
683
|
+
<div style="display:flex;align-items:center;gap:4px">
|
|
684
|
+
<span class="field-label" style="flex:1">Body</span>
|
|
685
|
+
<sp-action-button
|
|
686
|
+
size="xs"
|
|
687
|
+
quiet
|
|
688
|
+
title="Open in code editor"
|
|
674
689
|
@click=${() => {
|
|
675
690
|
ctx.updateSession({ ui: { editingFunction: { type: "def", defName: name } } });
|
|
676
691
|
ctx.renderCanvas();
|
|
677
692
|
}}
|
|
678
693
|
>
|
|
679
|
-
|
|
680
|
-
</button>
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
694
|
+
<sp-icon-code slot="icon"></sp-icon-code>
|
|
695
|
+
</sp-action-button>
|
|
696
|
+
</div>
|
|
697
|
+
<textarea
|
|
698
|
+
class="field-input"
|
|
699
|
+
style="min-height:60px;font-family:monospace;font-size:11px"
|
|
700
|
+
.value=${def.body || ""}
|
|
701
|
+
@input=${(/** @type {any} */ e) => {
|
|
702
|
+
const v = e.target.value;
|
|
703
|
+
transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { body: v }));
|
|
704
|
+
}}
|
|
705
|
+
></textarea>
|
|
706
|
+
`;
|
|
707
|
+
|
|
708
|
+
return html`
|
|
709
|
+
${descriptionField} ${renderParameterEditorTemplate(S, name, def, ctx)}
|
|
710
|
+
${isCustomElementDoc(S) ? renderEmitsEditorTemplate(S, name, def) : nothing} ${bodyField}
|
|
688
711
|
`;
|
|
689
712
|
}
|
|
690
713
|
|
|
@@ -434,6 +434,16 @@ export function createDevServerPlatform() {
|
|
|
434
434
|
return await res.json();
|
|
435
435
|
},
|
|
436
436
|
|
|
437
|
+
/** @param {{ path: string; ref?: string }} opts */
|
|
438
|
+
async gitShow(opts) {
|
|
439
|
+
const params = new URLSearchParams({ path: opts.path });
|
|
440
|
+
if (opts.ref) params.set("ref", opts.ref);
|
|
441
|
+
const res = await fetch(`/__studio/git/show?${params}`);
|
|
442
|
+
if (!res.ok) throw new Error(await res.text());
|
|
443
|
+
const data = await res.json();
|
|
444
|
+
return data.content;
|
|
445
|
+
},
|
|
446
|
+
|
|
437
447
|
/** @param {string[]} files */
|
|
438
448
|
async gitDiscard(files) {
|
|
439
449
|
const res = await fetch("/__studio/git/discard", {
|
|
@@ -85,13 +85,13 @@ export function setLintMarkers(editor, diagnostics) {
|
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
87
|
* @param {any} editing
|
|
88
|
-
* @param {any}
|
|
88
|
+
* @param {any} document
|
|
89
89
|
*/
|
|
90
|
-
export function getFunctionArgs(editing,
|
|
90
|
+
export function getFunctionArgs(editing, document) {
|
|
91
91
|
if (editing.type === "def") {
|
|
92
|
-
return
|
|
92
|
+
return document?.state?.[editing.defName]?.parameters || ["state", "event"];
|
|
93
93
|
} else if (editing.type === "event") {
|
|
94
|
-
const node = getNodeAtPath(
|
|
94
|
+
const node = getNodeAtPath(document, editing.path);
|
|
95
95
|
return node?.[editing.eventKey]?.parameters || ["state", "event"];
|
|
96
96
|
}
|
|
97
97
|
return ["state", "event"];
|