@jxsuite/studio 2.4.2 → 2.4.3
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 +104 -5
- package/package.json +9 -9
- package/src/platforms/cloud.ts +61 -2
package/dist/studio.js
CHANGED
|
@@ -28687,6 +28687,71 @@ function renderAiPanelTemplate() {
|
|
|
28687
28687
|
`;
|
|
28688
28688
|
}
|
|
28689
28689
|
|
|
28690
|
+
// ../schema/src/component-meta.ts
|
|
28691
|
+
function collectSlotDefs(node, out = []) {
|
|
28692
|
+
if (!node || typeof node !== "object" || Array.isArray(node)) {
|
|
28693
|
+
return out;
|
|
28694
|
+
}
|
|
28695
|
+
const el = node;
|
|
28696
|
+
if (el.tagName === "slot") {
|
|
28697
|
+
const attrs = el.attributes;
|
|
28698
|
+
const rawName = attrs?.name;
|
|
28699
|
+
const name = typeof rawName === "string" ? rawName.trim() : "";
|
|
28700
|
+
const { children } = el;
|
|
28701
|
+
out.push({
|
|
28702
|
+
name,
|
|
28703
|
+
...Array.isArray(children) && children.length > 0 ? { fallback: children } : {}
|
|
28704
|
+
});
|
|
28705
|
+
}
|
|
28706
|
+
if (Array.isArray(el.children)) {
|
|
28707
|
+
for (const c of el.children) {
|
|
28708
|
+
collectSlotDefs(c, out);
|
|
28709
|
+
}
|
|
28710
|
+
}
|
|
28711
|
+
return out;
|
|
28712
|
+
}
|
|
28713
|
+
function isPropEntry(def) {
|
|
28714
|
+
if (def == null) {
|
|
28715
|
+
return false;
|
|
28716
|
+
}
|
|
28717
|
+
if (typeof def !== "object") {
|
|
28718
|
+
return true;
|
|
28719
|
+
}
|
|
28720
|
+
const obj = def;
|
|
28721
|
+
return !obj.$prototype && !obj.$handler && !obj.$compute;
|
|
28722
|
+
}
|
|
28723
|
+
function componentMetaFrom(doc, path) {
|
|
28724
|
+
if (!doc || typeof doc !== "object" || Array.isArray(doc)) {
|
|
28725
|
+
return null;
|
|
28726
|
+
}
|
|
28727
|
+
const content = doc;
|
|
28728
|
+
const { tagName } = content;
|
|
28729
|
+
if (typeof tagName !== "string" || !tagName.includes("-")) {
|
|
28730
|
+
return null;
|
|
28731
|
+
}
|
|
28732
|
+
const slots = collectSlotDefs(content);
|
|
28733
|
+
const state2 = content.state ?? {};
|
|
28734
|
+
return {
|
|
28735
|
+
$id: typeof content.$id === "string" && content.$id ? content.$id : null,
|
|
28736
|
+
hasElements: Array.isArray(content.$elements) && content.$elements.length > 0,
|
|
28737
|
+
path,
|
|
28738
|
+
props: Object.entries(state2).filter(([, def]) => isPropEntry(def)).map(([name, def]) => {
|
|
28739
|
+
if (typeof def !== "object" || def === null) {
|
|
28740
|
+
return { default: def, name, type: typeof def };
|
|
28741
|
+
}
|
|
28742
|
+
const obj = def;
|
|
28743
|
+
return {
|
|
28744
|
+
default: obj.default,
|
|
28745
|
+
format: obj.format,
|
|
28746
|
+
name,
|
|
28747
|
+
type: obj.type
|
|
28748
|
+
};
|
|
28749
|
+
}),
|
|
28750
|
+
...slots.length > 0 ? { slots } : {},
|
|
28751
|
+
tagName
|
|
28752
|
+
};
|
|
28753
|
+
}
|
|
28754
|
+
|
|
28690
28755
|
// ../protocol/src/problem.ts
|
|
28691
28756
|
var PROBLEM_TYPE_BASE = "https://jxsuite.com/problems/";
|
|
28692
28757
|
function problemSlug(type) {
|
|
@@ -29098,7 +29163,41 @@ function createCloudPlatform(project) {
|
|
|
29098
29163
|
return data.result;
|
|
29099
29164
|
},
|
|
29100
29165
|
async discoverComponents() {
|
|
29101
|
-
|
|
29166
|
+
const SKIP = new Set(["node_modules", "dist", ".git", ".claude", ".obsidian", "build"]);
|
|
29167
|
+
const MAX_FILES = 400;
|
|
29168
|
+
const MAX_DEPTH = 6;
|
|
29169
|
+
const jsonPaths = [];
|
|
29170
|
+
const walk = async (dir, depth) => {
|
|
29171
|
+
if (depth > MAX_DEPTH || jsonPaths.length >= MAX_FILES) {
|
|
29172
|
+
return;
|
|
29173
|
+
}
|
|
29174
|
+
let entries;
|
|
29175
|
+
try {
|
|
29176
|
+
entries = await platform.listDirectory(dir);
|
|
29177
|
+
} catch {
|
|
29178
|
+
return;
|
|
29179
|
+
}
|
|
29180
|
+
const dirs = [];
|
|
29181
|
+
for (const entry of entries) {
|
|
29182
|
+
if (entry.type === "directory") {
|
|
29183
|
+
if (!SKIP.has(entry.name) && !entry.name.startsWith(".")) {
|
|
29184
|
+
dirs.push(entry.path);
|
|
29185
|
+
}
|
|
29186
|
+
} else if (entry.name.endsWith(".json") && jsonPaths.length < MAX_FILES) {
|
|
29187
|
+
jsonPaths.push(entry.path);
|
|
29188
|
+
}
|
|
29189
|
+
}
|
|
29190
|
+
await Promise.all(dirs.map(async (d) => walk(d, depth + 1)));
|
|
29191
|
+
};
|
|
29192
|
+
await walk("", 0);
|
|
29193
|
+
const metas = await Promise.all(jsonPaths.map(async (path) => {
|
|
29194
|
+
try {
|
|
29195
|
+
return componentMetaFrom(JSON.parse(await platform.readFile(path)), path);
|
|
29196
|
+
} catch {
|
|
29197
|
+
return null;
|
|
29198
|
+
}
|
|
29199
|
+
}));
|
|
29200
|
+
return metas.filter((m) => m !== null);
|
|
29102
29201
|
},
|
|
29103
29202
|
async codeService() {
|
|
29104
29203
|
return null;
|
|
@@ -58591,9 +58690,9 @@ function renderDefsEditor(container) {
|
|
|
58591
58690
|
|
|
58592
58691
|
// src/version.ts
|
|
58593
58692
|
var APP_NAME = "Jx Studio";
|
|
58594
|
-
var VERSION = "2.4.
|
|
58595
|
-
var BUILD_DATE = "2026-08-
|
|
58596
|
-
var GIT_COMMIT = "
|
|
58693
|
+
var VERSION = "2.4.3";
|
|
58694
|
+
var BUILD_DATE = "2026-08-25T03:20:06.206Z";
|
|
58695
|
+
var GIT_COMMIT = "624ef53";
|
|
58597
58696
|
var LINKS = {
|
|
58598
58697
|
github: "https://github.com/jxsuite/jx",
|
|
58599
58698
|
docs: "https://github.com/jxsuite/jx#readme",
|
|
@@ -61876,5 +61975,5 @@ export {
|
|
|
61876
61975
|
hasUnsavedTabs
|
|
61877
61976
|
};
|
|
61878
61977
|
|
|
61879
|
-
//# debugId=
|
|
61978
|
+
//# debugId=980E02AF5DBA79D264756E2164756E21
|
|
61880
61979
|
//# sourceMappingURL=studio.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/studio",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.3",
|
|
4
4
|
"description": "Jx Studio — visual builder for Jx documents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -57,14 +57,14 @@
|
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@atlaskit/pragmatic-drag-and-drop": "^3.0.0",
|
|
59
59
|
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^2.1.0",
|
|
60
|
-
"@jxsuite/ai": "^0.36.
|
|
61
|
-
"@jxsuite/collab": "^0.8.
|
|
62
|
-
"@jxsuite/create": "^1.3.
|
|
63
|
-
"@jxsuite/formulas": "^0.0.
|
|
64
|
-
"@jxsuite/markup": "^0.4.
|
|
65
|
-
"@jxsuite/protocol": "^1.2.
|
|
66
|
-
"@jxsuite/runtime": "^2.0.
|
|
67
|
-
"@jxsuite/schema": "^1.8.
|
|
60
|
+
"@jxsuite/ai": "^0.36.3",
|
|
61
|
+
"@jxsuite/collab": "^0.8.2",
|
|
62
|
+
"@jxsuite/create": "^1.3.6",
|
|
63
|
+
"@jxsuite/formulas": "^0.0.13",
|
|
64
|
+
"@jxsuite/markup": "^0.4.6",
|
|
65
|
+
"@jxsuite/protocol": "^1.2.1",
|
|
66
|
+
"@jxsuite/runtime": "^2.0.5",
|
|
67
|
+
"@jxsuite/schema": "^1.8.1",
|
|
68
68
|
"@spectrum-web-components/accordion": "^1.12.2",
|
|
69
69
|
"@spectrum-web-components/action-bar": "1.12.2",
|
|
70
70
|
"@spectrum-web-components/action-button": "^1.12.2",
|
package/src/platforms/cloud.ts
CHANGED
|
@@ -15,8 +15,10 @@ import { negotiateCollab } from "@jxsuite/collab/negotiate";
|
|
|
15
15
|
import type { CollabNegotiation } from "@jxsuite/collab/negotiate";
|
|
16
16
|
import type { WsCollabConnection } from "@jxsuite/collab/client";
|
|
17
17
|
import type { ProjectConfig } from "@jxsuite/schema/types";
|
|
18
|
+
import { componentMetaFrom } from "@jxsuite/schema/component-meta";
|
|
18
19
|
import type {
|
|
19
20
|
AccountStatus,
|
|
21
|
+
ComponentMeta,
|
|
20
22
|
CreateProjectDestination,
|
|
21
23
|
DirEntry,
|
|
22
24
|
ExtensionsInfo,
|
|
@@ -518,9 +520,66 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
518
520
|
|
|
519
521
|
// ─── Components / code services (cloud: static-only posture) ──────────
|
|
520
522
|
|
|
523
|
+
/**
|
|
524
|
+
* Component discovery, by READING rather than executing.
|
|
525
|
+
*
|
|
526
|
+
* This returned `[]` under "no execution of project JS in the cloud", and the cost was not the
|
|
527
|
+
* loss of discovery — it was the canvas. `canvas-live-render` injects the `$elements` a
|
|
528
|
+
* document's tags need only when the registry is non-empty, so an empty registry meant nothing
|
|
529
|
+
* was ever registered, no component was ever fetched, and every component instance rendered as
|
|
530
|
+
* an unregistered custom element: an empty inline box. A page built from components came up as
|
|
531
|
+
* blank space between the layout's chrome.
|
|
532
|
+
*
|
|
533
|
+
* Discovery does not need to execute anything. For a JSON component it is a file read and a
|
|
534
|
+
* property lookup — `componentMetaFrom` is that function, shared with the two Bun backends so
|
|
535
|
+
* "which `state` entries are props" has one answer. Non-JSON component formats DO need a
|
|
536
|
+
* project-supplied parser, and those stay out: the posture is kept where it actually applies
|
|
537
|
+
* rather than across the whole feature.
|
|
538
|
+
*
|
|
539
|
+
* Bounded rather than unbounded: the walk skips the directories a scan has no business in and
|
|
540
|
+
* stops at a depth and a file count, because this runs against a remote session over HTTP and a
|
|
541
|
+
* pathological tree must not turn opening a project into thousands of requests.
|
|
542
|
+
*/
|
|
521
543
|
async discoverComponents() {
|
|
522
|
-
|
|
523
|
-
|
|
544
|
+
const SKIP = new Set(["node_modules", "dist", ".git", ".claude", ".obsidian", "build"]);
|
|
545
|
+
const MAX_FILES = 400;
|
|
546
|
+
const MAX_DEPTH = 6;
|
|
547
|
+
const jsonPaths: string[] = [];
|
|
548
|
+
|
|
549
|
+
const walk = async (dir: string, depth: number): Promise<void> => {
|
|
550
|
+
if (depth > MAX_DEPTH || jsonPaths.length >= MAX_FILES) {
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
let entries: DirEntry[];
|
|
554
|
+
try {
|
|
555
|
+
entries = await platform.listDirectory(dir);
|
|
556
|
+
} catch {
|
|
557
|
+
return; // A directory that vanished mid-walk is not a discovery failure.
|
|
558
|
+
}
|
|
559
|
+
const dirs: string[] = [];
|
|
560
|
+
for (const entry of entries) {
|
|
561
|
+
if (entry.type === "directory") {
|
|
562
|
+
if (!SKIP.has(entry.name) && !entry.name.startsWith(".")) {
|
|
563
|
+
dirs.push(entry.path);
|
|
564
|
+
}
|
|
565
|
+
} else if (entry.name.endsWith(".json") && jsonPaths.length < MAX_FILES) {
|
|
566
|
+
jsonPaths.push(entry.path);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
await Promise.all(dirs.map(async (d) => walk(d, depth + 1)));
|
|
570
|
+
};
|
|
571
|
+
await walk("", 0);
|
|
572
|
+
|
|
573
|
+
const metas = await Promise.all(
|
|
574
|
+
jsonPaths.map(async (path) => {
|
|
575
|
+
try {
|
|
576
|
+
return componentMetaFrom(JSON.parse(await platform.readFile(path)), path);
|
|
577
|
+
} catch {
|
|
578
|
+
return null; // Not JSON, not readable, or not a component — all the same answer here.
|
|
579
|
+
}
|
|
580
|
+
}),
|
|
581
|
+
);
|
|
582
|
+
return metas.filter((m): m is NonNullable<typeof m> => m !== null) as ComponentMeta[];
|
|
524
583
|
},
|
|
525
584
|
|
|
526
585
|
async codeService() {
|