@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.
Files changed (40) hide show
  1. package/dist/studio.js +50941 -34749
  2. package/dist/studio.js.map +461 -345
  3. package/package.json +46 -35
  4. package/src/browse/browse.js +414 -0
  5. package/src/editor/context-menu.js +48 -1
  6. package/src/editor/convert-to-component.js +208 -0
  7. package/src/editor/inline-edit.js +33 -6
  8. package/src/editor/shortcuts.js +6 -1
  9. package/src/files/components.js +4 -2
  10. package/src/files/file-ops.js +102 -54
  11. package/src/files/files.js +22 -8
  12. package/src/markdown/md-convert.js +309 -11
  13. package/src/panels/activity-bar.js +3 -0
  14. package/src/panels/head-panel.js +576 -0
  15. package/src/panels/overlays.js +133 -0
  16. package/src/panels/right-panel.js +130 -0
  17. package/src/panels/shared.js +41 -0
  18. package/src/panels/signals-panel.js +95 -94
  19. package/src/panels/statusbar.js +15 -1
  20. package/src/panels/toolbar.js +223 -0
  21. package/src/platforms/devserver.js +58 -16
  22. package/src/settings/collections-editor.js +428 -0
  23. package/src/settings/defs-editor.js +418 -0
  24. package/src/settings/schema-field-ui.js +329 -0
  25. package/src/state.js +99 -2
  26. package/src/store.js +112 -41
  27. package/src/studio.js +1551 -1565
  28. package/src/ui/button-group.js +91 -0
  29. package/src/ui/color-selector.js +299 -0
  30. package/src/ui/field-row.js +47 -0
  31. package/src/ui/media-picker.js +172 -0
  32. package/src/ui/panel-resize.js +96 -0
  33. package/src/ui/spectrum.js +36 -2
  34. package/src/ui/unit-selector.js +106 -0
  35. package/src/ui/{jx-styled-combobox.js → value-selector.js} +7 -7
  36. package/src/ui/widgets.js +106 -0
  37. package/src/utils/canvas-media.js +151 -0
  38. package/src/utils/inherited-style.js +54 -0
  39. package/src/utils/studio-utils.js +32 -0
  40. 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
+ };