@jxsuite/studio 0.19.0 → 0.21.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 +13476 -12330
- package/dist/studio.js.map +152 -148
- 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 +19 -7
- package/src/browse/browse-modal.js +16 -13
- package/src/browse/browse.js +44 -35
- package/src/canvas/canvas-diff.js +24 -16
- package/src/canvas/canvas-helpers.js +24 -19
- package/src/canvas/canvas-live-render.js +113 -39
- package/src/canvas/canvas-render.js +119 -73
- package/src/canvas/canvas-utils.js +51 -21
- 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 +28 -14
- package/src/editor/convert-targets.js +60 -0
- package/src/editor/convert-to-component.js +20 -19
- package/src/editor/convert-to-repeater.js +226 -0
- package/src/editor/inline-edit.js +69 -28
- package/src/editor/insertion-helper.js +16 -12
- package/src/editor/shortcuts.js +14 -4
- package/src/editor/slash-menu.js +40 -19
- package/src/files/components.js +1 -1
- package/src/files/file-ops.js +37 -16
- package/src/files/files.js +322 -68
- package/src/github/github-auth.js +122 -0
- package/src/github/github-publish.js +126 -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 +27 -25
- package/src/panels/ai-panel.js +406 -0
- package/src/panels/block-action-bar.js +77 -28
- 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 +475 -99
- package/src/panels/head-panel.js +121 -81
- package/src/panels/imports-panel.js +94 -65
- package/src/panels/layers-panel.js +64 -43
- package/src/panels/left-panel.js +105 -48
- 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 +378 -257
- package/src/panels/pseudo-preview.js +2 -2
- package/src/panels/quick-search.js +11 -9
- package/src/panels/right-panel.js +42 -16
- package/src/panels/shared.js +4 -4
- package/src/panels/signals-panel.js +415 -166
- package/src/panels/statusbar.js +10 -6
- package/src/panels/style-inputs.js +67 -35
- package/src/panels/style-panel.js +239 -134
- 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/tab-strip.js +5 -2
- 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 +8 -8
- 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/settings/settings-modal.js +12 -9
- package/src/site-context.js +41 -31
- package/src/state.js +70 -29
- package/src/store.js +36 -35
- package/src/studio.js +120 -90
- 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/layers.js +31 -1
- 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 +32 -26
- package/src/utils/google-fonts.js +11 -11
- package/src/utils/inherited-style.js +9 -8
- package/src/utils/studio-utils.js +18 -18
- package/src/view.js +58 -1
- package/src/workspace/workspace.js +29 -7
|
@@ -34,7 +34,7 @@ export function updateForcedPseudoPreview() {
|
|
|
34
34
|
const node = getNodeAtPath(tab.doc.document, tab.session.selection);
|
|
35
35
|
if (!node?.style) return;
|
|
36
36
|
const activeMedia = tab.session.ui.activeMedia;
|
|
37
|
-
/** @type {
|
|
37
|
+
/** @type {JxStyle} */
|
|
38
38
|
const ctx = activeMedia ? node.style[`@${activeMedia}`] || {} : node.style;
|
|
39
39
|
const rules = ctx[sel];
|
|
40
40
|
if (!rules || typeof rules !== "object") return;
|
|
@@ -43,7 +43,7 @@ export function updateForcedPseudoPreview() {
|
|
|
43
43
|
.filter(([k]) => typeof rules[k] === "string" || typeof rules[k] === "number")
|
|
44
44
|
.map(
|
|
45
45
|
([k, v]) =>
|
|
46
|
-
`${k.replace(/[A-Z]/g, (/** @type {
|
|
46
|
+
`${k.replace(/[A-Z]/g, (/** @type {string} */ c) => `-${c.toLowerCase()}`)}: ${v} !important`,
|
|
47
47
|
)
|
|
48
48
|
.join("; ");
|
|
49
49
|
if (!cssProps) return;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { html, render as litRender, nothing } from "lit-html";
|
|
2
2
|
import { classMap } from "lit-html/directives/class-map.js";
|
|
3
3
|
import { live } from "lit-html/directives/live.js";
|
|
4
|
+
import { ref } from "lit-html/directives/ref.js";
|
|
4
5
|
import { getPlatform } from "../platform.js";
|
|
5
6
|
import { openFileInTab } from "../files/files.js";
|
|
6
7
|
import { getRecentFiles, trackRecentFile } from "../recent-projects.js";
|
|
@@ -8,7 +9,7 @@ import { getLayerSlot } from "../ui/layers.js";
|
|
|
8
9
|
|
|
9
10
|
let _open = false;
|
|
10
11
|
let _query = "";
|
|
11
|
-
/** @type {
|
|
12
|
+
/** @type {{ path: string; name?: string }[]} */
|
|
12
13
|
let _results = [];
|
|
13
14
|
let _selectedIndex = 0;
|
|
14
15
|
let _debounceTimer = 0;
|
|
@@ -28,10 +29,6 @@ export function openQuickSearch() {
|
|
|
28
29
|
_results = [];
|
|
29
30
|
_selectedIndex = 0;
|
|
30
31
|
renderOverlay();
|
|
31
|
-
requestAnimationFrame(() => {
|
|
32
|
-
const input = getContainer().querySelector(".quick-search-input");
|
|
33
|
-
if (input) /** @type {HTMLInputElement} */ (input).focus();
|
|
34
|
-
});
|
|
35
32
|
}
|
|
36
33
|
|
|
37
34
|
export function closeQuickSearch() {
|
|
@@ -60,7 +57,9 @@ async function doSearch(/** @type {string} */ query) {
|
|
|
60
57
|
function onInput(/** @type {Event} */ e) {
|
|
61
58
|
_query = /** @type {HTMLInputElement} */ (e.target).value;
|
|
62
59
|
clearTimeout(_debounceTimer);
|
|
63
|
-
_debounceTimer = /** @type {
|
|
60
|
+
_debounceTimer = /** @type {number} */ (
|
|
61
|
+
/** @type {unknown} */ (setTimeout(() => doSearch(_query), 150))
|
|
62
|
+
);
|
|
64
63
|
renderOverlay();
|
|
65
64
|
}
|
|
66
65
|
|
|
@@ -88,10 +87,10 @@ function onKeydown(/** @type {KeyboardEvent} */ e) {
|
|
|
88
87
|
}
|
|
89
88
|
}
|
|
90
89
|
|
|
91
|
-
function selectItem(/** @type {
|
|
90
|
+
function selectItem(/** @type {{ path: string; name?: string }} */ item) {
|
|
92
91
|
closeQuickSearch();
|
|
93
92
|
const path = item.path;
|
|
94
|
-
trackRecentFile({ path, name: path.split("/").pop() });
|
|
93
|
+
trackRecentFile({ path, name: path.split("/").pop() || "" });
|
|
95
94
|
openFileInTab(path);
|
|
96
95
|
}
|
|
97
96
|
|
|
@@ -134,6 +133,9 @@ function renderOverlay() {
|
|
|
134
133
|
.value=${live(_query)}
|
|
135
134
|
@input=${onInput}
|
|
136
135
|
@keydown=${onKeydown}
|
|
136
|
+
${ref((el) => {
|
|
137
|
+
if (el) requestAnimationFrame(() => /** @type {HTMLInputElement} */ (el).focus());
|
|
138
|
+
})}
|
|
137
139
|
/>
|
|
138
140
|
<div class="quick-search-results">
|
|
139
141
|
${items.length === 0 && _query.trim()
|
|
@@ -156,7 +158,7 @@ function renderOverlay() {
|
|
|
156
158
|
}}
|
|
157
159
|
>
|
|
158
160
|
<span class="quick-search-icon"
|
|
159
|
-
>${fileIcon(item.name || item.path.split("/").pop())}</span
|
|
161
|
+
>${fileIcon(item.name || item.path.split("/").pop() || "")}</span
|
|
160
162
|
>
|
|
161
163
|
<span class="quick-search-name">${item.name || item.path.split("/").pop()}</span>
|
|
162
164
|
<span class="quick-search-path">${dirPart(item.path)}</span>
|
|
@@ -15,6 +15,12 @@ import { isCustomElementDoc } from "./signals-panel.js";
|
|
|
15
15
|
import { isColorPopoverOpen } from "../ui/color-selector.js";
|
|
16
16
|
import { renderStylePanelTemplate } from "./style-panel.js";
|
|
17
17
|
import { renderPropertiesPanelTemplate } from "./properties-panel.js";
|
|
18
|
+
import {
|
|
19
|
+
renderAiPanelTemplate,
|
|
20
|
+
mountAiPanel,
|
|
21
|
+
mountQuikChat,
|
|
22
|
+
registerRightPanelRender,
|
|
23
|
+
} from "./ai-panel.js";
|
|
18
24
|
|
|
19
25
|
/**
|
|
20
26
|
* @typedef {{
|
|
@@ -33,6 +39,16 @@ let _scope = null;
|
|
|
33
39
|
|
|
34
40
|
let _rendering = false;
|
|
35
41
|
let _scheduled = false;
|
|
42
|
+
let _hasFocus = false;
|
|
43
|
+
|
|
44
|
+
function _onFocusIn() {
|
|
45
|
+
_hasFocus = true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function _onFocusOut() {
|
|
49
|
+
_hasFocus = false;
|
|
50
|
+
render();
|
|
51
|
+
}
|
|
36
52
|
|
|
37
53
|
/**
|
|
38
54
|
* Mount the right panel.
|
|
@@ -41,6 +57,10 @@ let _scheduled = false;
|
|
|
41
57
|
*/
|
|
42
58
|
export function mount(ctx) {
|
|
43
59
|
_ctx = ctx;
|
|
60
|
+
mountAiPanel();
|
|
61
|
+
registerRightPanelRender(render);
|
|
62
|
+
rightPanel.addEventListener("focusin", _onFocusIn);
|
|
63
|
+
rightPanel.addEventListener("focusout", _onFocusOut);
|
|
44
64
|
_scope = effectScope();
|
|
45
65
|
_scope.run(() => {
|
|
46
66
|
effect(() => {
|
|
@@ -58,20 +78,7 @@ export function mount(ctx) {
|
|
|
58
78
|
void tab.session.ui.styleFilterActive;
|
|
59
79
|
void tab.session.ui.inspectorSections;
|
|
60
80
|
|
|
61
|
-
|
|
62
|
-
const activeTag = document.activeElement?.tagName;
|
|
63
|
-
const rightHasFocus =
|
|
64
|
-
!colorPopoverOpen &&
|
|
65
|
-
rightPanel.contains(document.activeElement) &&
|
|
66
|
-
(activeTag === "INPUT" ||
|
|
67
|
-
activeTag === "TEXTAREA" ||
|
|
68
|
-
activeTag === "SP-TEXTFIELD" ||
|
|
69
|
-
activeTag === "SP-NUMBER-FIELD" ||
|
|
70
|
-
activeTag === "SP-PICKER" ||
|
|
71
|
-
activeTag === "SP-COMBOBOX" ||
|
|
72
|
-
activeTag === "SP-SEARCH");
|
|
73
|
-
|
|
74
|
-
if (!rightHasFocus) {
|
|
81
|
+
if (!_hasFocus && !isColorPopoverOpen()) {
|
|
75
82
|
render();
|
|
76
83
|
}
|
|
77
84
|
});
|
|
@@ -82,6 +89,9 @@ export function unmount() {
|
|
|
82
89
|
_scope?.stop();
|
|
83
90
|
_scope = null;
|
|
84
91
|
_ctx = null;
|
|
92
|
+
rightPanel.removeEventListener("focusin", _onFocusIn);
|
|
93
|
+
rightPanel.removeEventListener("focusout", _onFocusOut);
|
|
94
|
+
_hasFocus = false;
|
|
85
95
|
}
|
|
86
96
|
|
|
87
97
|
export function render() {
|
|
@@ -113,6 +123,7 @@ function _flush() {
|
|
|
113
123
|
} finally {
|
|
114
124
|
_rendering = false;
|
|
115
125
|
}
|
|
126
|
+
requestAnimationFrame(() => mountQuikChat());
|
|
116
127
|
_ctx.updateForcedPseudoPreview();
|
|
117
128
|
}
|
|
118
129
|
|
|
@@ -120,7 +131,14 @@ function rightPanelTemplate() {
|
|
|
120
131
|
const ctx = /** @type {RightPanelCtx} */ (_ctx);
|
|
121
132
|
const aTab = activeTab.value;
|
|
122
133
|
if (!aTab) return nothing;
|
|
123
|
-
const S = /**
|
|
134
|
+
const S = /**
|
|
135
|
+
* @type {{
|
|
136
|
+
* ui: Record<string, unknown>;
|
|
137
|
+
* document: JxMutableNode;
|
|
138
|
+
* mode: string;
|
|
139
|
+
* selection: (string | number)[] | null;
|
|
140
|
+
* }}
|
|
141
|
+
*/ ({
|
|
124
142
|
ui: aTab.session.ui,
|
|
125
143
|
document: aTab.doc.document,
|
|
126
144
|
mode: aTab.doc.mode,
|
|
@@ -132,6 +150,7 @@ function rightPanelTemplate() {
|
|
|
132
150
|
{ value: "properties", icon: "sp-icon-properties", label: "Properties" },
|
|
133
151
|
{ value: "events", icon: "sp-icon-event", label: "Events" },
|
|
134
152
|
{ value: "style", icon: "sp-icon-brush", label: "Style" },
|
|
153
|
+
{ value: "assistant", icon: "sp-icon-chat", label: "Assistant" },
|
|
135
154
|
];
|
|
136
155
|
|
|
137
156
|
const tabsT = html`
|
|
@@ -171,10 +190,17 @@ function rightPanelTemplate() {
|
|
|
171
190
|
} catch (/** @type {unknown} */ e) {
|
|
172
191
|
console.error("[renderStylePanelTemplate]", e);
|
|
173
192
|
}
|
|
193
|
+
} else if (tab === "assistant") {
|
|
194
|
+
bodyT = renderAiPanelTemplate();
|
|
174
195
|
}
|
|
175
196
|
|
|
176
197
|
return html`
|
|
177
198
|
${tabsT}
|
|
178
|
-
<div
|
|
199
|
+
<div
|
|
200
|
+
class="panel-body"
|
|
201
|
+
style=${tab === "assistant" ? "display:flex;flex-direction:column;overflow:hidden" : ""}
|
|
202
|
+
>
|
|
203
|
+
${bodyT}
|
|
204
|
+
</div>
|
|
179
205
|
`;
|
|
180
206
|
}
|
package/src/panels/shared.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* Convert a $media key like "--tablet" to a friendly display name "Tablet". "--" returns "Base".
|
|
8
8
|
*
|
|
9
|
-
* @param {
|
|
9
|
+
* @param {string} name
|
|
10
10
|
*/
|
|
11
11
|
export function mediaDisplayName(name) {
|
|
12
12
|
if (name === "--") return "Base";
|
|
@@ -14,7 +14,7 @@ export function mediaDisplayName(name) {
|
|
|
14
14
|
name
|
|
15
15
|
.replace(/^--/, "")
|
|
16
16
|
.replace(/-/g, " ")
|
|
17
|
-
.replace(/\b\w/g, (/** @type {
|
|
17
|
+
.replace(/\b\w/g, (/** @type {string} */ c) => c.toUpperCase()) || name
|
|
18
18
|
);
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -23,10 +23,10 @@ export const unsafeTags = new Set(["script", "style", "link", "iframe", "object"
|
|
|
23
23
|
/**
|
|
24
24
|
* Generate a sensible default Jx node for a given tag name.
|
|
25
25
|
*
|
|
26
|
-
* @param {
|
|
26
|
+
* @param {string} tag
|
|
27
27
|
*/
|
|
28
28
|
export function defaultDef(tag) {
|
|
29
|
-
/** @type {
|
|
29
|
+
/** @type {JxMutableNode} */
|
|
30
30
|
const def = { tagName: tag };
|
|
31
31
|
if (/^h[1-6]$/.test(tag)) def.textContent = "Heading";
|
|
32
32
|
else if (tag === "p") def.textContent = "Paragraph text";
|