@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
package/src/panels/toolbar.js
CHANGED
|
@@ -16,17 +16,39 @@ import { openQuickSearch } from "./quick-search.js";
|
|
|
16
16
|
import { getPlatform } from "../platform.js";
|
|
17
17
|
import { refreshGitStatus } from "./git-panel.js";
|
|
18
18
|
import { openBrowseModal } from "../browse/browse-modal.js";
|
|
19
|
+
import { openNewProjectModal } from "../new-project/new-project-modal.js";
|
|
19
20
|
|
|
20
21
|
/** @type {HTMLElement | null} */
|
|
21
22
|
let _rootEl = null;
|
|
22
23
|
|
|
23
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {{
|
|
26
|
+
* navigateBack: () => void;
|
|
27
|
+
* navigateToLevel: (level: number) => void;
|
|
28
|
+
* openProject: () => void;
|
|
29
|
+
* openFile?: (path: string) => void;
|
|
30
|
+
* saveFile: () => void;
|
|
31
|
+
* parseMediaEntries: (media: Record<string, string> | null | undefined) => {
|
|
32
|
+
* sizeBreakpoints: { name: string; query: string; width: number; type: string }[];
|
|
33
|
+
* featureQueries: { name: string; query: string }[];
|
|
34
|
+
* baseWidth: number;
|
|
35
|
+
* };
|
|
36
|
+
* getCanvasMode: () => string;
|
|
37
|
+
* setCanvasMode: (mode: string) => void;
|
|
38
|
+
* renderCanvas: () => void;
|
|
39
|
+
* safeRenderRightPanel: () => void;
|
|
40
|
+
* openRecentProject: (root: string) => Promise<void>;
|
|
41
|
+
* closeFunctionEditor: () => void;
|
|
42
|
+
* }} ToolbarCtx
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/** @type {ToolbarCtx | null} */
|
|
24
46
|
let _ctx = null;
|
|
25
47
|
|
|
26
48
|
/** @type {import("@vue/reactivity").EffectScope | null} */
|
|
27
49
|
let _scope = null;
|
|
28
50
|
|
|
29
|
-
const toolbarIconMap = /** @type {Record<string,
|
|
51
|
+
const toolbarIconMap = /** @type {Record<string, import("lit-html").TemplateResult>} */ ({
|
|
30
52
|
"sp-icon-folder-open": html`<sp-icon-folder-open slot="icon"></sp-icon-folder-open>`,
|
|
31
53
|
"sp-icon-save-floppy": html`<sp-icon-save-floppy slot="icon"></sp-icon-save-floppy>`,
|
|
32
54
|
"sp-icon-back": html`<sp-icon-back slot="icon"></sp-icon-back>`,
|
|
@@ -45,9 +67,9 @@ const toolbarIconMap = /** @type {Record<string, any>} */ ({
|
|
|
45
67
|
});
|
|
46
68
|
|
|
47
69
|
/**
|
|
48
|
-
* @param {
|
|
49
|
-
* @param {
|
|
50
|
-
* @param {
|
|
70
|
+
* @param {string} label
|
|
71
|
+
* @param {Function} onClick
|
|
72
|
+
* @param {string} [iconTag]
|
|
51
73
|
*/
|
|
52
74
|
function tbBtnTpl(label, onClick, iconTag) {
|
|
53
75
|
return html`
|
|
@@ -61,30 +83,37 @@ function tbBtnTpl(label, onClick, iconTag) {
|
|
|
61
83
|
* Mount the toolbar panel.
|
|
62
84
|
*
|
|
63
85
|
* @param {HTMLElement} rootEl
|
|
64
|
-
* @param {
|
|
86
|
+
* @param {ToolbarCtx} ctx — { navigateBack, closeFunctionEditor, openProject, openFile, saveFile,
|
|
65
87
|
* parseMediaEntries, getCanvasMode, setCanvasMode, renderCanvas, safeRenderRightPanel }
|
|
66
88
|
*/
|
|
67
89
|
export function mount(rootEl, ctx) {
|
|
68
90
|
_rootEl = rootEl;
|
|
69
91
|
_ctx = ctx;
|
|
70
|
-
if (
|
|
92
|
+
if (
|
|
93
|
+
/** @type {{ __jxPlatform?: { windowControls?: unknown } }} */ (
|
|
94
|
+
/** @type {unknown} */ (globalThis)
|
|
95
|
+
).__jxPlatform?.windowControls
|
|
96
|
+
) {
|
|
71
97
|
rootEl.classList.add("electrobun-webkit-app-region-drag");
|
|
72
98
|
}
|
|
73
99
|
_scope = effectScope();
|
|
74
100
|
_scope.run(() => {
|
|
75
101
|
effect(() => {
|
|
76
102
|
const tab = activeTab.value;
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
103
|
+
if (tab) {
|
|
104
|
+
// Read reactive properties to establish tracking
|
|
105
|
+
void tab.doc.document;
|
|
106
|
+
void tab.doc.dirty;
|
|
107
|
+
void tab.doc.mode;
|
|
108
|
+
void tab.session.selection;
|
|
109
|
+
void tab.session.ui.canvasMode;
|
|
110
|
+
void tab.session.ui.editingFunction;
|
|
111
|
+
void tab.session.ui.featureToggles;
|
|
112
|
+
void tab.session.ui.rightTab;
|
|
113
|
+
void tab.session.ui.gitStatus;
|
|
114
|
+
void tab.history.index;
|
|
115
|
+
void tab.history.snapshots.length;
|
|
116
|
+
}
|
|
88
117
|
render();
|
|
89
118
|
});
|
|
90
119
|
});
|
|
@@ -106,10 +135,155 @@ export function render() {
|
|
|
106
135
|
}
|
|
107
136
|
}
|
|
108
137
|
|
|
138
|
+
async function handleNewProject() {
|
|
139
|
+
const result = await openNewProjectModal();
|
|
140
|
+
if (result && _ctx) {
|
|
141
|
+
await _ctx.openRecentProject(result.root);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** @param {ToolbarCtx} ctx */
|
|
146
|
+
function minimalToolbarTemplate(ctx) {
|
|
147
|
+
const recentProjects = getRecentProjects();
|
|
148
|
+
const recentProjectsTpl = html`
|
|
149
|
+
<overlay-trigger placement="bottom-start" triggered-by="click">
|
|
150
|
+
<sp-action-button size="s" slot="trigger" title="Recent projects" class="tb-split-trigger">
|
|
151
|
+
<sp-icon-chevron-down slot="icon"></sp-icon-chevron-down>
|
|
152
|
+
</sp-action-button>
|
|
153
|
+
<sp-popover slot="click-content" tip>
|
|
154
|
+
<sp-menu
|
|
155
|
+
@change=${(/** @type {Event} */ e) => {
|
|
156
|
+
const val = /** @type {HTMLInputElement} */ (/** @type {unknown} */ (e.target)).value;
|
|
157
|
+
if (val === "__new__") {
|
|
158
|
+
handleNewProject();
|
|
159
|
+
} else {
|
|
160
|
+
ctx.openRecentProject(val);
|
|
161
|
+
}
|
|
162
|
+
}}
|
|
163
|
+
>
|
|
164
|
+
<sp-menu-item value="__new__">New Project…</sp-menu-item>
|
|
165
|
+
${recentProjects.length
|
|
166
|
+
? html`<sp-menu-divider></sp-menu-divider> ${recentProjects.map(
|
|
167
|
+
(p) => html`<sp-menu-item value=${p.root}>${p.name}</sp-menu-item>`,
|
|
168
|
+
)}`
|
|
169
|
+
: nothing}
|
|
170
|
+
</sp-menu>
|
|
171
|
+
</sp-popover>
|
|
172
|
+
</overlay-trigger>
|
|
173
|
+
`;
|
|
174
|
+
|
|
175
|
+
const windowControls = /**
|
|
176
|
+
* @type {{
|
|
177
|
+
* __jxPlatform?: {
|
|
178
|
+
* windowControls?: { minimize: () => void; maximize: () => void; close: () => void };
|
|
179
|
+
* };
|
|
180
|
+
* }}
|
|
181
|
+
*/ (/** @type {unknown} */ (globalThis)).__jxPlatform?.windowControls;
|
|
182
|
+
const csdTpl = windowControls
|
|
183
|
+
? html`
|
|
184
|
+
<sp-action-group class="window-controls" size="s">
|
|
185
|
+
<sp-action-button
|
|
186
|
+
quiet
|
|
187
|
+
size="s"
|
|
188
|
+
title="Minimize"
|
|
189
|
+
@click=${() => windowControls.minimize()}
|
|
190
|
+
>
|
|
191
|
+
<sp-icon-remove slot="icon"></sp-icon-remove>
|
|
192
|
+
</sp-action-button>
|
|
193
|
+
<sp-action-button
|
|
194
|
+
quiet
|
|
195
|
+
size="s"
|
|
196
|
+
title="Maximize"
|
|
197
|
+
@click=${() => windowControls.maximize()}
|
|
198
|
+
>
|
|
199
|
+
<sp-icon-rectangle slot="icon"></sp-icon-rectangle>
|
|
200
|
+
</sp-action-button>
|
|
201
|
+
<sp-action-button
|
|
202
|
+
quiet
|
|
203
|
+
size="s"
|
|
204
|
+
title="Close"
|
|
205
|
+
class="csd-close"
|
|
206
|
+
@click=${() => windowControls.close()}
|
|
207
|
+
>
|
|
208
|
+
<sp-icon-close slot="icon"></sp-icon-close>
|
|
209
|
+
</sp-action-button>
|
|
210
|
+
</sp-action-group>
|
|
211
|
+
`
|
|
212
|
+
: nothing;
|
|
213
|
+
|
|
214
|
+
return html`
|
|
215
|
+
<div class="tb-split-btn">
|
|
216
|
+
<sp-action-button size="s" class="tb-split-main" @click=${ctx.openProject}>
|
|
217
|
+
${toolbarIconMap["sp-icon-folder-open"]} Open Project
|
|
218
|
+
</sp-action-button>
|
|
219
|
+
${recentProjectsTpl}
|
|
220
|
+
</div>
|
|
221
|
+
${tbBtnTpl("Manage", openBrowseModal, "sp-icon-view-list")}
|
|
222
|
+
<sp-action-button size="s" disabled>
|
|
223
|
+
${toolbarIconMap["sp-icon-save-floppy"]} Save
|
|
224
|
+
</sp-action-button>
|
|
225
|
+
<sp-action-group compact size="s">
|
|
226
|
+
<sp-action-button size="s" disabled>
|
|
227
|
+
${toolbarIconMap["sp-icon-undo"]} Undo
|
|
228
|
+
</sp-action-button>
|
|
229
|
+
<sp-action-button size="s" disabled>
|
|
230
|
+
${toolbarIconMap["sp-icon-redo"]} Redo
|
|
231
|
+
</sp-action-button>
|
|
232
|
+
</sp-action-group>
|
|
233
|
+
<div class="tb-spacer"></div>
|
|
234
|
+
<sp-action-button class="tb-search-trigger" size="s" quiet @click=${openQuickSearch}>
|
|
235
|
+
<sp-icon-search slot="icon"></sp-icon-search>
|
|
236
|
+
<span class="tb-search-label">Search files… <kbd>⌘P</kbd></span>
|
|
237
|
+
</sp-action-button>
|
|
238
|
+
<div class="tb-spacer"></div>
|
|
239
|
+
<sp-action-group selects="single" size="s" compact>
|
|
240
|
+
${modes.map(
|
|
241
|
+
(m) => html`
|
|
242
|
+
<sp-action-button size="s" disabled ?selected=${m.key === "design"}>
|
|
243
|
+
${toolbarIconMap[m.iconTag]}${m.label}
|
|
244
|
+
</sp-action-button>
|
|
245
|
+
`,
|
|
246
|
+
)}
|
|
247
|
+
</sp-action-group>
|
|
248
|
+
<sp-action-button
|
|
249
|
+
quiet
|
|
250
|
+
size="s"
|
|
251
|
+
title="Toggle Right Panel"
|
|
252
|
+
@click=${() => {
|
|
253
|
+
view.rightPanelCollapsed = !view.rightPanelCollapsed;
|
|
254
|
+
applyPanelCollapse();
|
|
255
|
+
render();
|
|
256
|
+
}}
|
|
257
|
+
>
|
|
258
|
+
${view.rightPanelCollapsed
|
|
259
|
+
? html`<sp-icon-rail-right-open slot="icon"></sp-icon-rail-right-open>`
|
|
260
|
+
: html`<sp-icon-rail-right-close slot="icon"></sp-icon-rail-right-close>`}
|
|
261
|
+
</sp-action-button>
|
|
262
|
+
${csdTpl}
|
|
263
|
+
`;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const modes = [
|
|
267
|
+
{ key: "edit", label: "Edit", iconTag: "sp-icon-edit" },
|
|
268
|
+
{ key: "design", label: "Design", iconTag: "sp-icon-artboard" },
|
|
269
|
+
{ key: "preview", label: "Preview", iconTag: "sp-icon-preview" },
|
|
270
|
+
{ key: "source", label: "Code", iconTag: "sp-icon-code" },
|
|
271
|
+
{ key: "stylebook", label: "Stylebook", iconTag: "sp-icon-brush" },
|
|
272
|
+
];
|
|
273
|
+
|
|
109
274
|
function toolbarTemplate() {
|
|
110
275
|
const tab = activeTab.value;
|
|
111
|
-
if (!
|
|
112
|
-
const
|
|
276
|
+
if (!_ctx) return html``;
|
|
277
|
+
const ctx = _ctx;
|
|
278
|
+
|
|
279
|
+
if (!tab) return minimalToolbarTemplate(ctx);
|
|
280
|
+
|
|
281
|
+
const allowedModes = new Set(tab.capabilities.modes);
|
|
282
|
+
const canUndo = tab.history.index > 0;
|
|
283
|
+
const canRedo = tab.history.index < tab.history.snapshots.length - 1;
|
|
284
|
+
const canSave = tab.doc.dirty;
|
|
285
|
+
|
|
286
|
+
const S = {
|
|
113
287
|
document: tab.doc.document,
|
|
114
288
|
ui: tab.session.ui,
|
|
115
289
|
mode: tab.doc.mode,
|
|
@@ -118,19 +292,19 @@ function toolbarTemplate() {
|
|
|
118
292
|
documentPath: tab.documentPath,
|
|
119
293
|
fileHandle: tab.fileHandle,
|
|
120
294
|
documentStack: tab.session.documentStack,
|
|
121
|
-
}
|
|
122
|
-
const canvasMode =
|
|
295
|
+
};
|
|
296
|
+
const canvasMode = ctx.getCanvasMode();
|
|
123
297
|
const hasStack = S.documentStack && S.documentStack.length > 0;
|
|
124
298
|
|
|
125
299
|
const breadcrumbTpl = hasStack
|
|
126
300
|
? html`
|
|
127
301
|
<div class="breadcrumb">
|
|
128
|
-
<sp-action-button size="s" title="Return to parent document" @click=${
|
|
302
|
+
<sp-action-button size="s" title="Return to parent document" @click=${ctx.navigateBack}>
|
|
129
303
|
${toolbarIconMap["sp-icon-back"]}Back
|
|
130
304
|
</sp-action-button>
|
|
131
305
|
${S.documentStack.map(
|
|
132
|
-
(/** @type {
|
|
133
|
-
<span class="breadcrumb-item clickable" @click=${() =>
|
|
306
|
+
(/** @type {DocumentStackEntry} */ frame, /** @type {number} */ i) => html`
|
|
307
|
+
<span class="breadcrumb-item clickable" @click=${() => ctx.navigateToLevel(i)}
|
|
134
308
|
>${frame.documentPath?.split("/").pop() || "untitled"}</span
|
|
135
309
|
>
|
|
136
310
|
<span class="breadcrumb-sep"> › </span>
|
|
@@ -143,13 +317,13 @@ function toolbarTemplate() {
|
|
|
143
317
|
`
|
|
144
318
|
: nothing;
|
|
145
319
|
|
|
146
|
-
const { featureQueries } =
|
|
320
|
+
const { featureQueries } = ctx.parseMediaEntries(getEffectiveMedia(S.document.$media));
|
|
147
321
|
const togglesTpl =
|
|
148
322
|
featureQueries.length > 0
|
|
149
323
|
? html`
|
|
150
324
|
<sp-action-group compact size="s">
|
|
151
325
|
${featureQueries.map(
|
|
152
|
-
(/** @type {
|
|
326
|
+
(/** @type {{ name: string; query: string }} */ { name, query }) => html`
|
|
153
327
|
<sp-action-button
|
|
154
328
|
toggles
|
|
155
329
|
size="s"
|
|
@@ -171,17 +345,6 @@ function toolbarTemplate() {
|
|
|
171
345
|
`
|
|
172
346
|
: nothing;
|
|
173
347
|
|
|
174
|
-
const modes = [
|
|
175
|
-
{ key: "edit", label: "Edit", iconTag: "sp-icon-edit" },
|
|
176
|
-
{ key: "design", label: "Design", iconTag: "sp-icon-artboard" },
|
|
177
|
-
{ key: "preview", label: "Preview", iconTag: "sp-icon-preview" },
|
|
178
|
-
{ key: "source", label: "Code", iconTag: "sp-icon-code" },
|
|
179
|
-
{ key: "stylebook", label: "Stylebook", iconTag: "sp-icon-brush" },
|
|
180
|
-
];
|
|
181
|
-
|
|
182
|
-
const isProjectFile = S.documentPath === "project.json";
|
|
183
|
-
const allowedModes = isProjectFile ? new Set(["stylebook", "source"]) : null;
|
|
184
|
-
|
|
185
348
|
const modeSwitcherTpl = html`
|
|
186
349
|
<sp-action-group selects="single" size="s" compact>
|
|
187
350
|
${modes.map(
|
|
@@ -189,25 +352,25 @@ function toolbarTemplate() {
|
|
|
189
352
|
<sp-action-button
|
|
190
353
|
size="s"
|
|
191
354
|
?selected=${canvasMode === m.key}
|
|
192
|
-
?disabled=${
|
|
355
|
+
?disabled=${!allowedModes.has(m.key)}
|
|
193
356
|
@click=${() => {
|
|
194
357
|
if (canvasMode === m.key) return;
|
|
195
|
-
if (
|
|
358
|
+
if (!allowedModes.has(m.key)) return;
|
|
196
359
|
if (S.ui.editingFunction) {
|
|
197
360
|
if (view.functionEditor) {
|
|
198
361
|
view.functionEditor.dispose();
|
|
199
362
|
view.functionEditor = null;
|
|
200
363
|
}
|
|
201
364
|
}
|
|
202
|
-
|
|
365
|
+
ctx.setCanvasMode(m.key);
|
|
203
366
|
view.panX = 0;
|
|
204
367
|
view.panY = 0;
|
|
205
|
-
/** @type {
|
|
368
|
+
/** @type {{ editingFunction: null; rightTab?: string }} */
|
|
206
369
|
const uiPatch = { editingFunction: null };
|
|
207
370
|
if (m.key === "stylebook") uiPatch.rightTab = "style";
|
|
208
371
|
updateSession({ ui: uiPatch });
|
|
209
|
-
|
|
210
|
-
|
|
372
|
+
ctx.renderCanvas();
|
|
373
|
+
ctx.safeRenderRightPanel();
|
|
211
374
|
}}
|
|
212
375
|
>
|
|
213
376
|
${toolbarIconMap[m.iconTag]}${m.label}
|
|
@@ -217,84 +380,145 @@ function toolbarTemplate() {
|
|
|
217
380
|
</sp-action-group>
|
|
218
381
|
`;
|
|
219
382
|
|
|
220
|
-
const windowControls = /**
|
|
383
|
+
const windowControls = /**
|
|
384
|
+
* @type {{
|
|
385
|
+
* __jxPlatform?: {
|
|
386
|
+
* windowControls?: { minimize: () => void; maximize: () => void; close: () => void };
|
|
387
|
+
* };
|
|
388
|
+
* }}
|
|
389
|
+
*/ (/** @type {unknown} */ (globalThis)).__jxPlatform?.windowControls;
|
|
390
|
+
const isMac = navigator.platform.startsWith("Mac");
|
|
221
391
|
const csdTpl = windowControls
|
|
222
|
-
?
|
|
223
|
-
|
|
224
|
-
<sp-action-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
392
|
+
? isMac
|
|
393
|
+
? html`
|
|
394
|
+
<sp-action-group class="window-controls mac" size="s">
|
|
395
|
+
<sp-action-button
|
|
396
|
+
quiet
|
|
397
|
+
size="s"
|
|
398
|
+
title="Close"
|
|
399
|
+
class="csd-close"
|
|
400
|
+
@click=${() => windowControls.close()}
|
|
401
|
+
>
|
|
402
|
+
<sp-icon-close slot="icon"></sp-icon-close>
|
|
403
|
+
</sp-action-button>
|
|
404
|
+
<sp-action-button
|
|
405
|
+
quiet
|
|
406
|
+
size="s"
|
|
407
|
+
title="Minimize"
|
|
408
|
+
class="csd-minimize"
|
|
409
|
+
@click=${() => windowControls.minimize()}
|
|
410
|
+
>
|
|
411
|
+
<sp-icon-remove slot="icon"></sp-icon-remove>
|
|
412
|
+
</sp-action-button>
|
|
413
|
+
<sp-action-button
|
|
414
|
+
quiet
|
|
415
|
+
size="s"
|
|
416
|
+
title="Maximize"
|
|
417
|
+
class="csd-maximize"
|
|
418
|
+
@click=${() => windowControls.maximize()}
|
|
419
|
+
>
|
|
420
|
+
<sp-icon-rectangle slot="icon"></sp-icon-rectangle>
|
|
421
|
+
</sp-action-button>
|
|
422
|
+
</sp-action-group>
|
|
423
|
+
`
|
|
424
|
+
: html`
|
|
425
|
+
<sp-action-group class="window-controls" size="s">
|
|
426
|
+
<sp-action-button
|
|
427
|
+
quiet
|
|
428
|
+
size="s"
|
|
429
|
+
title="Minimize"
|
|
430
|
+
class="csd-minimize"
|
|
431
|
+
@click=${() => windowControls.minimize()}
|
|
432
|
+
>
|
|
433
|
+
<sp-icon-remove slot="icon"></sp-icon-remove>
|
|
434
|
+
</sp-action-button>
|
|
435
|
+
<sp-action-button
|
|
436
|
+
quiet
|
|
437
|
+
size="s"
|
|
438
|
+
title="Maximize"
|
|
439
|
+
class="csd-maximize"
|
|
440
|
+
@click=${() => windowControls.maximize()}
|
|
441
|
+
>
|
|
442
|
+
<sp-icon-rectangle slot="icon"></sp-icon-rectangle>
|
|
443
|
+
</sp-action-button>
|
|
444
|
+
<sp-action-button
|
|
445
|
+
quiet
|
|
446
|
+
size="s"
|
|
447
|
+
title="Close"
|
|
448
|
+
class="csd-close"
|
|
449
|
+
@click=${() => windowControls.close()}
|
|
450
|
+
>
|
|
451
|
+
<sp-icon-close slot="icon"></sp-icon-close>
|
|
452
|
+
</sp-action-button>
|
|
453
|
+
</sp-action-group>
|
|
454
|
+
`
|
|
251
455
|
: nothing;
|
|
252
456
|
|
|
253
457
|
const recentProjects = getRecentProjects();
|
|
254
|
-
const recentProjectsTpl =
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
)
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
458
|
+
const recentProjectsTpl = html`
|
|
459
|
+
<overlay-trigger placement="bottom-start" triggered-by="click">
|
|
460
|
+
<sp-action-button size="s" slot="trigger" title="Recent projects" class="tb-split-trigger">
|
|
461
|
+
<sp-icon-chevron-down slot="icon"></sp-icon-chevron-down>
|
|
462
|
+
</sp-action-button>
|
|
463
|
+
<sp-popover slot="click-content" tip>
|
|
464
|
+
<sp-menu
|
|
465
|
+
@change=${(/** @type {Event} */ e) => {
|
|
466
|
+
const val = /** @type {HTMLInputElement} */ (/** @type {unknown} */ (e.target)).value;
|
|
467
|
+
if (val === "__new__") {
|
|
468
|
+
handleNewProject();
|
|
469
|
+
} else {
|
|
470
|
+
ctx.openRecentProject(val);
|
|
471
|
+
}
|
|
472
|
+
}}
|
|
473
|
+
>
|
|
474
|
+
<sp-menu-item value="__new__">New Project…</sp-menu-item>
|
|
475
|
+
${recentProjects.length
|
|
476
|
+
? html`<sp-menu-divider></sp-menu-divider> ${recentProjects.map(
|
|
477
|
+
(p) => html`<sp-menu-item value=${p.root}>${p.name}</sp-menu-item>`,
|
|
478
|
+
)}`
|
|
479
|
+
: nothing}
|
|
480
|
+
</sp-menu>
|
|
481
|
+
</sp-popover>
|
|
482
|
+
</overlay-trigger>
|
|
483
|
+
`;
|
|
270
484
|
|
|
271
485
|
return html`
|
|
272
|
-
${
|
|
486
|
+
${isMac ? csdTpl : nothing}
|
|
487
|
+
<div class="tb-split-btn">
|
|
488
|
+
<sp-action-button size="s" class="tb-split-main" @click=${ctx.openProject}>
|
|
489
|
+
${toolbarIconMap["sp-icon-folder-open"]} Open Project
|
|
490
|
+
</sp-action-button>
|
|
491
|
+
${recentProjectsTpl}
|
|
492
|
+
</div>
|
|
273
493
|
${tbBtnTpl("Manage", openBrowseModal, "sp-icon-view-list")}
|
|
274
|
-
|
|
494
|
+
<sp-action-button size="s" ?disabled=${!canSave} @click=${ctx.saveFile}>
|
|
495
|
+
${toolbarIconMap["sp-icon-save-floppy"]} Save
|
|
496
|
+
</sp-action-button>
|
|
275
497
|
<sp-action-group compact size="s">
|
|
276
|
-
|
|
277
|
-
|
|
498
|
+
<sp-action-button size="s" ?disabled=${!canUndo} @click=${() => tabUndo(activeTab.value)}>
|
|
499
|
+
${toolbarIconMap["sp-icon-undo"]} Undo
|
|
500
|
+
</sp-action-button>
|
|
501
|
+
<sp-action-button size="s" ?disabled=${!canRedo} @click=${() => tabRedo(activeTab.value)}>
|
|
502
|
+
${toolbarIconMap["sp-icon-redo"]} Redo
|
|
503
|
+
</sp-action-button>
|
|
278
504
|
</sp-action-group>
|
|
279
505
|
<div class="tb-spacer"></div>
|
|
280
506
|
<sp-action-button class="tb-search-trigger" size="s" quiet @click=${openQuickSearch}>
|
|
281
507
|
<sp-icon-search slot="icon"></sp-icon-search>
|
|
282
508
|
<span class="tb-search-label">Search files… <kbd>⌘P</kbd></span>
|
|
283
509
|
</sp-action-button>
|
|
284
|
-
${
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
>
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
: nothing
|
|
297
|
-
}
|
|
510
|
+
${(activeTab.value?.session.ui.gitStatus?.behind ?? 0) > 0
|
|
511
|
+
? html`<sp-action-button
|
|
512
|
+
size="s"
|
|
513
|
+
@click=${async () => {
|
|
514
|
+
await getPlatform().gitPull();
|
|
515
|
+
await refreshGitStatus();
|
|
516
|
+
}}
|
|
517
|
+
>
|
|
518
|
+
<sp-icon-download slot="icon"></sp-icon-download>
|
|
519
|
+
Sync Project
|
|
520
|
+
</sp-action-button>`
|
|
521
|
+
: nothing}
|
|
298
522
|
<div class="tb-spacer"></div>
|
|
299
523
|
${breadcrumbTpl} ${togglesTpl} ${modeSwitcherTpl}
|
|
300
524
|
<sp-action-button
|
|
@@ -311,6 +535,6 @@ function toolbarTemplate() {
|
|
|
311
535
|
? html`<sp-icon-rail-right-open slot="icon"></sp-icon-rail-right-open>`
|
|
312
536
|
: html`<sp-icon-rail-right-close slot="icon"></sp-icon-rail-right-close>`}
|
|
313
537
|
</sp-action-button>
|
|
314
|
-
${csdTpl}
|
|
538
|
+
${isMac ? nothing : csdTpl}
|
|
315
539
|
`;
|
|
316
540
|
}
|