@jxsuite/studio 0.1.0 → 0.5.1
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 +50941 -34749
- package/dist/studio.js.map +461 -345
- package/package.json +46 -35
- package/src/browse/browse.js +414 -0
- package/src/editor/context-menu.js +48 -1
- package/src/editor/convert-to-component.js +208 -0
- package/src/editor/inline-edit.js +33 -6
- package/src/editor/shortcuts.js +6 -1
- package/src/files/components.js +4 -2
- package/src/files/file-ops.js +102 -54
- package/src/files/files.js +22 -8
- package/src/markdown/md-convert.js +309 -11
- package/src/panels/activity-bar.js +3 -0
- package/src/panels/head-panel.js +576 -0
- package/src/panels/overlays.js +133 -0
- package/src/panels/right-panel.js +130 -0
- package/src/panels/shared.js +41 -0
- package/src/panels/signals-panel.js +95 -94
- package/src/panels/statusbar.js +15 -1
- package/src/panels/toolbar.js +223 -0
- package/src/platforms/devserver.js +58 -16
- package/src/settings/collections-editor.js +428 -0
- package/src/settings/defs-editor.js +418 -0
- package/src/settings/schema-field-ui.js +329 -0
- package/src/state.js +99 -2
- package/src/store.js +112 -41
- package/src/studio.js +1551 -1565
- package/src/ui/button-group.js +91 -0
- package/src/ui/color-selector.js +299 -0
- package/src/ui/field-row.js +47 -0
- package/src/ui/media-picker.js +172 -0
- package/src/ui/panel-resize.js +96 -0
- package/src/ui/spectrum.js +36 -2
- package/src/ui/unit-selector.js +106 -0
- package/src/ui/{jx-styled-combobox.js → value-selector.js} +7 -7
- package/src/ui/widgets.js +106 -0
- package/src/utils/canvas-media.js +151 -0
- package/src/utils/inherited-style.js +54 -0
- package/src/utils/studio-utils.js +32 -0
- package/src/view.js +68 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inherited-style.js — Computes the effective inherited style for a given breakpoint tab.
|
|
3
|
+
*
|
|
4
|
+
* Walks the cascade (base → each media block in order) up to but not including the active
|
|
5
|
+
* breakpoint, producing the set of property values that would apply if no explicit override exists
|
|
6
|
+
* on the current tab.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Compute the inherited style object for a given breakpoint tab.
|
|
11
|
+
*
|
|
12
|
+
* @param {Record<string, any>} style — full style object (flat props + @media blocks + selectors)
|
|
13
|
+
* @param {string[]} mediaNames — ordered breakpoint names (from parseMediaEntries, respects cascade
|
|
14
|
+
* direction)
|
|
15
|
+
* @param {string | null} activeTab — current breakpoint tab name, or null for base
|
|
16
|
+
* @param {string | null} activeSelector — current nested selector, or null
|
|
17
|
+
* @returns {Record<string, any>} Inherited style map (prop → value)
|
|
18
|
+
*/
|
|
19
|
+
export function computeInheritedStyle(style, mediaNames, activeTab, activeSelector = null) {
|
|
20
|
+
if (activeTab === null || mediaNames.length === 0) return {};
|
|
21
|
+
|
|
22
|
+
/** @type {Record<string, any>} */
|
|
23
|
+
let inherited = {};
|
|
24
|
+
|
|
25
|
+
if (!activeSelector) {
|
|
26
|
+
// Start with base flat props
|
|
27
|
+
for (const [p, v] of Object.entries(style)) {
|
|
28
|
+
if (typeof v !== "object") inherited[p] = v;
|
|
29
|
+
}
|
|
30
|
+
// Layer each media block in order until current tab
|
|
31
|
+
for (const name of mediaNames) {
|
|
32
|
+
if (name === activeTab) break;
|
|
33
|
+
const block = style[`@${name}`] || {};
|
|
34
|
+
for (const [p, v] of Object.entries(block)) {
|
|
35
|
+
if (typeof v !== "object") inherited[p] = v;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
// Selector inheritance: base selector → each media's selector block in order
|
|
40
|
+
const baseSel = style[activeSelector] || {};
|
|
41
|
+
for (const [p, v] of Object.entries(baseSel)) {
|
|
42
|
+
if (typeof v !== "object") inherited[p] = v;
|
|
43
|
+
}
|
|
44
|
+
for (const name of mediaNames) {
|
|
45
|
+
if (name === activeTab) break;
|
|
46
|
+
const selBlock = (style[`@${name}`] || {})[activeSelector] || {};
|
|
47
|
+
for (const [p, v] of Object.entries(selBlock)) {
|
|
48
|
+
if (typeof v !== "object") inherited[p] = v;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return inherited;
|
|
54
|
+
}
|
|
@@ -112,7 +112,9 @@ export function abbreviateValue(val) {
|
|
|
112
112
|
export function inferInputType(entry) {
|
|
113
113
|
if (entry.$shorthand === true) return "shorthand";
|
|
114
114
|
if (entry.$input === "button-group") return "button-group";
|
|
115
|
+
if (entry.$input === "media") return "media";
|
|
115
116
|
if (entry.format === "color") return "color";
|
|
117
|
+
if (entry.format === "uri-reference") return "media";
|
|
116
118
|
if (entry.$units !== undefined) return "number-unit";
|
|
117
119
|
if (entry.type === "number") return "number";
|
|
118
120
|
if (Array.isArray(entry.enum)) return "select";
|
|
@@ -120,6 +122,36 @@ export function inferInputType(entry) {
|
|
|
120
122
|
return "text";
|
|
121
123
|
}
|
|
122
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Match a document path to a content collection and return its schema. Uses simple directory-prefix
|
|
127
|
+
* + extension matching against the collection's `source` glob.
|
|
128
|
+
*
|
|
129
|
+
* @param {string | null} documentPath — project-relative path (e.g. "blog/hello.md")
|
|
130
|
+
* @param {any} projectConfig — parsed project.json
|
|
131
|
+
* @returns {{ name: string; schema: any } | null}
|
|
132
|
+
*/
|
|
133
|
+
export function findCollectionSchema(documentPath, projectConfig) {
|
|
134
|
+
if (!documentPath || !projectConfig?.collections) return null;
|
|
135
|
+
for (const [name, def] of Object.entries(
|
|
136
|
+
/** @type {Record<string, any>} */ (projectConfig.collections),
|
|
137
|
+
)) {
|
|
138
|
+
if (!def.source || !def.schema) continue;
|
|
139
|
+
const src = def.source.replace(/^\.\//, "");
|
|
140
|
+
const dir = src.split("/")[0];
|
|
141
|
+
const ext = src.includes("*.md")
|
|
142
|
+
? ".md"
|
|
143
|
+
: src.includes("*.json")
|
|
144
|
+
? ".json"
|
|
145
|
+
: src.includes("*.csv")
|
|
146
|
+
? ".csv"
|
|
147
|
+
: null;
|
|
148
|
+
if (documentPath.startsWith(dir + "/") && (!ext || documentPath.endsWith(ext))) {
|
|
149
|
+
return { name, schema: def.schema };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
123
155
|
/**
|
|
124
156
|
* Convert a human-readable name to a CSS variable name. E.g. "Geometric Humanist" →
|
|
125
157
|
* "--font-geometric-humanist"
|
package/src/view.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* View.js — Transient view state for Jx Studio
|
|
3
|
+
*
|
|
4
|
+
* Holds DOM references, editor instances, cleanup functions, and other mutable state that is the
|
|
5
|
+
* OUTPUT of renderers (not the input). Separating this from persistent app state (in S via
|
|
6
|
+
* store.js) makes renderer dependencies explicit.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** @type {any} */
|
|
10
|
+
export const view = {
|
|
11
|
+
// Canvas infrastructure
|
|
12
|
+
panzoomWrap: null,
|
|
13
|
+
liveScope: null,
|
|
14
|
+
renderGeneration: 0,
|
|
15
|
+
centerObserver: null,
|
|
16
|
+
needsCenter: true,
|
|
17
|
+
panX: 0,
|
|
18
|
+
panY: 0,
|
|
19
|
+
prevCanvasMode: null,
|
|
20
|
+
|
|
21
|
+
// Editor instances
|
|
22
|
+
monacoEditor: null,
|
|
23
|
+
functionEditor: null,
|
|
24
|
+
|
|
25
|
+
// Inline editing
|
|
26
|
+
componentInlineEdit: null,
|
|
27
|
+
pendingInlineEdit: null,
|
|
28
|
+
inlineEditCleanup: null,
|
|
29
|
+
|
|
30
|
+
// Floating UI containers
|
|
31
|
+
blockActionBarEl: null,
|
|
32
|
+
linkPopoverHost: null,
|
|
33
|
+
|
|
34
|
+
// Selection & drag
|
|
35
|
+
selDragCleanup: null,
|
|
36
|
+
|
|
37
|
+
// Cleanup arrays (reset on each render cycle)
|
|
38
|
+
dndCleanups: [],
|
|
39
|
+
canvasDndCleanups: [],
|
|
40
|
+
canvasEventCleanups: [],
|
|
41
|
+
|
|
42
|
+
// Pseudo-state preview
|
|
43
|
+
forcedStyleTag: null,
|
|
44
|
+
forcedAttrEl: null,
|
|
45
|
+
|
|
46
|
+
// Left panel / elements UI
|
|
47
|
+
elementsCollapsed: new Set(),
|
|
48
|
+
elementsFilter: "",
|
|
49
|
+
|
|
50
|
+
// Drag interaction
|
|
51
|
+
lastDragInput: null,
|
|
52
|
+
_currentDropTargetRow: null,
|
|
53
|
+
layerDragSourceHeight: 0,
|
|
54
|
+
|
|
55
|
+
// Editor state
|
|
56
|
+
savedRange: null,
|
|
57
|
+
_completionRegistered: false,
|
|
58
|
+
|
|
59
|
+
// Canvas / stylebook
|
|
60
|
+
stylebookElToTag: new WeakMap(),
|
|
61
|
+
|
|
62
|
+
// Responsive breakpoints UI
|
|
63
|
+
showAddBreakpointForm: false,
|
|
64
|
+
addBreakpointPreview: "",
|
|
65
|
+
|
|
66
|
+
// Autosave
|
|
67
|
+
autosaveTimer: null,
|
|
68
|
+
};
|