@jxsuite/studio 0.28.2 → 0.28.4
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 +86144 -83971
- package/dist/studio.js.map +229 -219
- package/package.json +5 -5
- package/src/browse/browse-modal.ts +9 -3
- package/src/browse/browse.ts +139 -82
- package/src/canvas/canvas-diff.ts +35 -18
- package/src/canvas/canvas-helpers.ts +55 -21
- package/src/canvas/canvas-live-render.ts +140 -89
- package/src/canvas/canvas-render.ts +127 -82
- package/src/canvas/canvas-utils.ts +68 -42
- package/src/canvas/nested-site-style.ts +15 -7
- package/src/editor/component-inline-edit.ts +90 -51
- package/src/editor/content-inline-edit.ts +75 -72
- package/src/editor/context-menu.ts +125 -71
- package/src/editor/convert-targets.ts +17 -13
- package/src/editor/convert-to-component.ts +51 -28
- package/src/editor/convert-to-repeater.ts +44 -19
- package/src/editor/inline-edit.ts +107 -52
- package/src/editor/inline-format.ts +128 -60
- package/src/editor/insertion-helper.ts +26 -14
- package/src/editor/shortcuts.ts +89 -43
- package/src/editor/slash-menu.ts +41 -26
- package/src/files/components.ts +9 -7
- package/src/files/file-ops.ts +53 -33
- package/src/files/files.ts +246 -143
- package/src/format/constraints.ts +25 -15
- package/src/format/format-host.ts +33 -10
- package/src/github/github-auth.ts +24 -14
- package/src/github/github-publish.ts +20 -14
- package/src/new-project/new-project-modal.ts +27 -18
- package/src/panels/activity-bar.ts +27 -26
- package/src/panels/ai-panel.ts +100 -44
- package/src/panels/block-action-bar.ts +84 -52
- package/src/panels/canvas-dnd.ts +47 -28
- package/src/panels/data-explorer.ts +24 -11
- package/src/panels/dnd.ts +148 -100
- package/src/panels/editors.ts +54 -37
- package/src/panels/elements-panel.ts +23 -13
- package/src/panels/events-panel.ts +48 -38
- package/src/panels/git-panel.ts +95 -60
- package/src/panels/head-panel.ts +138 -84
- package/src/panels/imports-panel.ts +66 -29
- package/src/panels/layers-panel.ts +80 -36
- package/src/panels/left-panel.ts +86 -53
- package/src/panels/overlays.ts +36 -19
- package/src/panels/panel-events.ts +48 -27
- package/src/panels/panel-scheduler.ts +41 -23
- package/src/panels/preview-render.ts +26 -26
- package/src/panels/properties-panel.ts +155 -118
- package/src/panels/pseudo-preview.ts +31 -15
- package/src/panels/quick-search.ts +28 -13
- package/src/panels/right-panel.ts +35 -24
- package/src/panels/shared.ts +46 -29
- package/src/panels/signals-panel.ts +303 -186
- package/src/panels/statusbar.ts +27 -12
- package/src/panels/style-inputs.ts +30 -24
- package/src/panels/style-panel.ts +130 -90
- package/src/panels/style-utils.ts +63 -23
- package/src/panels/stylebook-layers-panel.ts +27 -30
- package/src/panels/stylebook-panel.ts +140 -72
- package/src/panels/tab-strip.ts +21 -10
- package/src/panels/toolbar.ts +52 -40
- package/src/panels/welcome-screen.ts +1 -1
- package/src/platform.ts +2 -1
- package/src/platforms/devserver.ts +221 -105
- package/src/recent-projects.ts +15 -7
- package/src/resize-edges.ts +7 -5
- package/src/services/cem-export.ts +73 -57
- package/src/services/code-services.ts +52 -25
- package/src/services/monaco-setup.ts +5 -5
- package/src/settings/content-types-editor.ts +133 -62
- package/src/settings/css-vars-editor.ts +31 -14
- package/src/settings/defs-editor.ts +125 -63
- package/src/settings/general-settings.ts +11 -6
- package/src/settings/head-editor.ts +30 -15
- package/src/settings/schema-field-ui.ts +75 -35
- package/src/settings/settings-modal.ts +35 -17
- package/src/site-context.ts +80 -47
- package/src/state.ts +98 -65
- package/src/store.ts +42 -23
- package/src/studio.ts +165 -144
- package/src/tabs/tab.ts +51 -45
- package/src/tabs/transact.ts +230 -109
- package/src/types.ts +60 -51
- package/src/ui/button-group.ts +3 -2
- package/src/ui/color-selector.ts +33 -15
- package/src/ui/expression-editor.ts +139 -86
- package/src/ui/field-input.ts +35 -18
- package/src/ui/field-row.ts +1 -1
- package/src/ui/icons.ts +5 -6
- package/src/ui/layers.ts +33 -26
- package/src/ui/media-picker.ts +13 -7
- package/src/ui/panel-resize.ts +39 -20
- package/src/ui/spectrum.ts +4 -2
- package/src/ui/unit-selector.ts +20 -11
- package/src/ui/value-selector.ts +13 -7
- package/src/ui/widgets.ts +34 -18
- package/src/utils/canvas-media.ts +66 -33
- package/src/utils/edit-display.ts +43 -44
- package/src/utils/google-fonts.ts +10 -6
- package/src/utils/inherited-style.ts +22 -8
- package/src/utils/studio-utils.ts +88 -45
- package/src/view.ts +9 -5
- package/src/workspace/workspace.ts +49 -29
|
@@ -14,42 +14,52 @@ export interface NestingValidator {
|
|
|
14
14
|
blockTags: ReadonlySet<string>;
|
|
15
15
|
inlineTags: ReadonlySet<string>;
|
|
16
16
|
allTags: ReadonlySet<string>;
|
|
17
|
-
isVoid(tag: string)
|
|
18
|
-
isTextOnly(tag: string)
|
|
17
|
+
isVoid: (tag: string) => boolean;
|
|
18
|
+
isTextOnly: (tag: string) => boolean;
|
|
19
19
|
/** Whether childTag may appear inside parentTag ("_root" for the document root). */
|
|
20
|
-
isValidChild(parentTag: string, childTag: string)
|
|
20
|
+
isValidChild: (parentTag: string, childTag: string) => boolean;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/** Build a nesting validator from a format's `$studio.elements` declaration. */
|
|
24
|
-
export function createNestingValidator(elements
|
|
25
|
-
const blockTags = new Set(elements?.block
|
|
26
|
-
const inlineTags = new Set(elements?.inline
|
|
24
|
+
export function createNestingValidator(elements?: ElementsHints): NestingValidator {
|
|
25
|
+
const blockTags = new Set(elements?.block);
|
|
26
|
+
const inlineTags = new Set(elements?.inline);
|
|
27
27
|
const allTags = new Set([...blockTags, ...inlineTags]);
|
|
28
|
-
const voidTags = new Set(elements?.void
|
|
29
|
-
const textOnly = new Set(elements?.textOnly
|
|
28
|
+
const voidTags = new Set(elements?.void);
|
|
29
|
+
const textOnly = new Set(elements?.textOnly);
|
|
30
30
|
const nesting = elements?.nesting ?? {};
|
|
31
31
|
|
|
32
32
|
return {
|
|
33
|
+
allTags,
|
|
33
34
|
blockTags,
|
|
34
35
|
inlineTags,
|
|
35
|
-
allTags,
|
|
36
|
-
isVoid: (tag) => voidTags.has(tag),
|
|
37
36
|
isTextOnly: (tag) => textOnly.has(tag),
|
|
38
37
|
isValidChild(parentTag, childTag) {
|
|
39
38
|
const rule = nesting[parentTag];
|
|
40
|
-
if (!rule)
|
|
39
|
+
if (!rule) {
|
|
40
|
+
return true;
|
|
41
|
+
} // Unknown parents (directive components) allow anything
|
|
41
42
|
|
|
42
|
-
if (rule.only)
|
|
43
|
+
if (rule.only) {
|
|
44
|
+
return rule.only.includes(childTag);
|
|
45
|
+
}
|
|
43
46
|
|
|
44
47
|
const isBlock = blockTags.has(childTag);
|
|
45
48
|
const isInline = inlineTags.has(childTag);
|
|
46
49
|
const isDirective = !allTags.has(childTag);
|
|
47
50
|
|
|
48
|
-
if (isBlock && rule.block)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
if (isBlock && rule.block) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (isInline && rule.inline) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
if (isDirective && rule.directive) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
51
60
|
|
|
52
61
|
return false;
|
|
53
62
|
},
|
|
63
|
+
isVoid: (tag) => voidTags.has(tag),
|
|
54
64
|
};
|
|
55
65
|
}
|
|
@@ -95,15 +95,30 @@ export function formatByExtension(ext: string, capability?: string): StudioForma
|
|
|
95
95
|
|
|
96
96
|
/** Look up a format by its import name. */
|
|
97
97
|
export function formatByName(name: string | null | undefined): StudioFormat | undefined {
|
|
98
|
-
if (!name)
|
|
98
|
+
if (!name) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
99
101
|
return _formats.find((f) => f.name === name);
|
|
100
102
|
}
|
|
101
103
|
|
|
104
|
+
/** Error for opening a non-JSON file when no imported format class claims its extension. */
|
|
105
|
+
export function noFormatError(path: string): Error {
|
|
106
|
+
return new Error(
|
|
107
|
+
`No format class imported for "${path}" — add one to project.json imports ` +
|
|
108
|
+
`(e.g. "Markdown": "@jxsuite/parser/Markdown.class.json") and make sure the ` +
|
|
109
|
+
`project's dependencies are installed`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
102
113
|
/** The format claiming a file path's extension, if any. */
|
|
103
114
|
export function formatForPath(path: string | null | undefined): StudioFormat | undefined {
|
|
104
|
-
if (!path)
|
|
115
|
+
if (!path) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
105
118
|
const dot = path.lastIndexOf(".");
|
|
106
|
-
if (dot === -1)
|
|
119
|
+
if (dot === -1) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
107
122
|
return formatByExtension(path.slice(dot));
|
|
108
123
|
}
|
|
109
124
|
|
|
@@ -111,8 +126,12 @@ export function formatForPath(path: string | null | undefined): StudioFormat | u
|
|
|
111
126
|
export function documentExtensions(kind?: string): string[] {
|
|
112
127
|
const exts = new Set<string>();
|
|
113
128
|
for (const f of _formats) {
|
|
114
|
-
if (kind && !f.documentKinds.includes(kind))
|
|
115
|
-
|
|
129
|
+
if (kind && !f.documentKinds.includes(kind)) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
for (const e of f.extensions) {
|
|
133
|
+
exts.add(e);
|
|
134
|
+
}
|
|
116
135
|
}
|
|
117
136
|
exts.delete(".json");
|
|
118
137
|
return [...exts];
|
|
@@ -140,10 +159,10 @@ export async function formatParse(
|
|
|
140
159
|
options?: Record<string, unknown>,
|
|
141
160
|
): Promise<JxMutableNode> {
|
|
142
161
|
return (await formatAction({
|
|
143
|
-
format: name,
|
|
144
162
|
action: "parse",
|
|
145
|
-
|
|
163
|
+
format: name,
|
|
146
164
|
options,
|
|
165
|
+
source,
|
|
147
166
|
})) as JxMutableNode;
|
|
148
167
|
}
|
|
149
168
|
|
|
@@ -154,9 +173,9 @@ export async function formatSerialize(
|
|
|
154
173
|
options?: Record<string, unknown>,
|
|
155
174
|
): Promise<string> {
|
|
156
175
|
return (await formatAction({
|
|
157
|
-
format: name,
|
|
158
176
|
action: "serialize",
|
|
159
177
|
doc,
|
|
178
|
+
format: name,
|
|
160
179
|
options,
|
|
161
180
|
})) as string;
|
|
162
181
|
}
|
|
@@ -190,13 +209,17 @@ export function splitFormatDocument(format: StudioFormat | undefined, doc: JxMut
|
|
|
190
209
|
|
|
191
210
|
// Content document — children form the root-level body; other keys are frontmatter
|
|
192
211
|
const children = (doc.children as unknown[]) ?? [];
|
|
193
|
-
if (children.length === 0)
|
|
212
|
+
if (children.length === 0) {
|
|
213
|
+
children.push({ children: [], tagName: "p" });
|
|
214
|
+
}
|
|
194
215
|
|
|
195
216
|
const documentKeys = new Set(["state", "imports"]);
|
|
196
217
|
const contentDoc: Record<string, unknown> = { children };
|
|
197
218
|
const frontmatter: Record<string, unknown> = {};
|
|
198
219
|
for (const [key, value] of Object.entries(doc)) {
|
|
199
|
-
if (key === "children")
|
|
220
|
+
if (key === "children") {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
200
223
|
if (documentKeys.has(key)) {
|
|
201
224
|
contentDoc[key] = value;
|
|
202
225
|
} else {
|
|
@@ -28,18 +28,22 @@ export function clearGithubToken() {
|
|
|
28
28
|
*/
|
|
29
29
|
export async function authenticateGithub() {
|
|
30
30
|
const existing = getGithubToken();
|
|
31
|
-
if (existing)
|
|
31
|
+
if (existing) {
|
|
32
|
+
return existing;
|
|
33
|
+
}
|
|
32
34
|
|
|
33
35
|
const codeRes = await fetch("https://github.com/login/device/code", {
|
|
34
|
-
|
|
36
|
+
body: JSON.stringify({ client_id: CLIENT_ID, scope: "repo" }),
|
|
35
37
|
headers: {
|
|
36
|
-
"Content-Type": "application/json",
|
|
37
38
|
Accept: "application/json",
|
|
39
|
+
"Content-Type": "application/json",
|
|
38
40
|
},
|
|
39
|
-
|
|
41
|
+
method: "POST",
|
|
40
42
|
});
|
|
41
43
|
|
|
42
|
-
if (!codeRes.ok)
|
|
44
|
+
if (!codeRes.ok) {
|
|
45
|
+
throw new Error("Failed to initiate GitHub device flow");
|
|
46
|
+
}
|
|
43
47
|
const codeData = await codeRes.json();
|
|
44
48
|
|
|
45
49
|
const { device_code, user_code, verification_uri, interval = 5 } = codeData;
|
|
@@ -49,19 +53,21 @@ export async function authenticateGithub() {
|
|
|
49
53
|
let pollTimer: ReturnType<typeof setTimeout> | null = null;
|
|
50
54
|
|
|
51
55
|
const poll = async () => {
|
|
52
|
-
if (cancelled)
|
|
56
|
+
if (cancelled) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
53
59
|
try {
|
|
54
60
|
const tokenRes = await fetch("https://github.com/login/oauth/access_token", {
|
|
55
|
-
method: "POST",
|
|
56
|
-
headers: {
|
|
57
|
-
"Content-Type": "application/json",
|
|
58
|
-
Accept: "application/json",
|
|
59
|
-
},
|
|
60
61
|
body: JSON.stringify({
|
|
61
62
|
client_id: CLIENT_ID,
|
|
62
63
|
device_code,
|
|
63
64
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
64
65
|
}),
|
|
66
|
+
headers: {
|
|
67
|
+
Accept: "application/json",
|
|
68
|
+
"Content-Type": "application/json",
|
|
69
|
+
},
|
|
70
|
+
method: "POST",
|
|
65
71
|
});
|
|
66
72
|
const tokenData = await tokenRes.json();
|
|
67
73
|
|
|
@@ -81,7 +87,7 @@ export async function authenticateGithub() {
|
|
|
81
87
|
return;
|
|
82
88
|
}
|
|
83
89
|
|
|
84
|
-
//
|
|
90
|
+
// Expired_token or access_denied
|
|
85
91
|
done(null);
|
|
86
92
|
} catch {
|
|
87
93
|
pollTimer = setTimeout(poll, interval * 1000);
|
|
@@ -97,12 +103,16 @@ export async function authenticateGithub() {
|
|
|
97
103
|
cancel-label="Cancel"
|
|
98
104
|
@cancel=${() => {
|
|
99
105
|
cancelled = true;
|
|
100
|
-
if (pollTimer)
|
|
106
|
+
if (pollTimer) {
|
|
107
|
+
clearTimeout(pollTimer);
|
|
108
|
+
}
|
|
101
109
|
done(null);
|
|
102
110
|
}}
|
|
103
111
|
@close=${() => {
|
|
104
112
|
cancelled = true;
|
|
105
|
-
if (pollTimer)
|
|
113
|
+
if (pollTimer) {
|
|
114
|
+
clearTimeout(pollTimer);
|
|
115
|
+
}
|
|
106
116
|
done(null);
|
|
107
117
|
}}
|
|
108
118
|
>
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
/** Publish a local project to GitHub — creates a new repo and pushes. */
|
|
3
3
|
|
|
4
4
|
import { html } from "lit-html";
|
|
5
|
+
import { errorMessage } from "@jxsuite/schema/parse";
|
|
5
6
|
import { ref } from "lit-html/directives/ref.js";
|
|
6
7
|
import { showDialog } from "../ui/layers";
|
|
7
8
|
import { authenticateGithub } from "./github-auth";
|
|
8
9
|
import { getPlatform } from "../platform";
|
|
9
|
-
import { refreshGitStatus } from "../panels/git-panel";
|
|
10
10
|
import { statusMessage } from "../panels/statusbar";
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -18,7 +18,9 @@ import { statusMessage } from "../panels/statusbar";
|
|
|
18
18
|
*/
|
|
19
19
|
export async function publishToGithub({ projectName }: { projectName: string }) {
|
|
20
20
|
const token = await authenticateGithub();
|
|
21
|
-
if (!token)
|
|
21
|
+
if (!token) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
22
24
|
|
|
23
25
|
const repoOpts = await showDialog<{
|
|
24
26
|
name: string;
|
|
@@ -37,9 +39,9 @@ export async function publishToGithub({ projectName }: { projectName: string })
|
|
|
37
39
|
cancel-label="Cancel"
|
|
38
40
|
@confirm=${() => {
|
|
39
41
|
done({
|
|
40
|
-
name: _nameInput?.value || projectName,
|
|
41
42
|
description: _descInput?.value || "",
|
|
42
43
|
isPrivate: _privateToggle?.checked ?? true,
|
|
44
|
+
name: _nameInput?.value || projectName,
|
|
43
45
|
});
|
|
44
46
|
}}
|
|
45
47
|
@cancel=${() => done(null)}
|
|
@@ -81,23 +83,25 @@ export async function publishToGithub({ projectName }: { projectName: string })
|
|
|
81
83
|
`;
|
|
82
84
|
});
|
|
83
85
|
|
|
84
|
-
if (!repoOpts)
|
|
86
|
+
if (!repoOpts) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
85
89
|
|
|
86
90
|
statusMessage("Creating GitHub repository…");
|
|
87
91
|
|
|
88
92
|
const createRes = await fetch("https://api.github.com/user/repos", {
|
|
89
|
-
method: "POST",
|
|
90
|
-
headers: {
|
|
91
|
-
Authorization: `Bearer ${token}`,
|
|
92
|
-
Accept: "application/vnd.github+json",
|
|
93
|
-
"Content-Type": "application/json",
|
|
94
|
-
},
|
|
95
93
|
body: JSON.stringify({
|
|
96
|
-
|
|
94
|
+
auto_init: false,
|
|
97
95
|
description: repoOpts.description,
|
|
96
|
+
name: repoOpts.name,
|
|
98
97
|
private: repoOpts.isPrivate,
|
|
99
|
-
auto_init: false,
|
|
100
98
|
}),
|
|
99
|
+
headers: {
|
|
100
|
+
Accept: "application/vnd.github+json",
|
|
101
|
+
Authorization: `Bearer ${token}`,
|
|
102
|
+
"Content-Type": "application/json",
|
|
103
|
+
},
|
|
104
|
+
method: "POST",
|
|
101
105
|
});
|
|
102
106
|
|
|
103
107
|
if (!createRes.ok) {
|
|
@@ -117,11 +121,13 @@ export async function publishToGithub({ projectName }: { projectName: string })
|
|
|
117
121
|
statusMessage("Pushing to GitHub…");
|
|
118
122
|
try {
|
|
119
123
|
await platform.gitPush({ setUpstream: true });
|
|
120
|
-
} catch (
|
|
121
|
-
statusMessage(`Push failed: ${(
|
|
124
|
+
} catch (error) {
|
|
125
|
+
statusMessage(`Push failed: ${errorMessage(error)}`);
|
|
122
126
|
return false;
|
|
123
127
|
}
|
|
124
128
|
|
|
129
|
+
// Lazy import breaks the github-publish ↔ git-panel module cycle
|
|
130
|
+
const { refreshGitStatus } = await import("../panels/git-panel");
|
|
125
131
|
await refreshGitStatus();
|
|
126
132
|
statusMessage(`Published to GitHub: ${repo.html_url}`);
|
|
127
133
|
return true;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { html } from "lit-html";
|
|
8
|
+
import { errorMessage } from "@jxsuite/schema/parse";
|
|
8
9
|
import { openModal } from "../ui/layers";
|
|
9
10
|
import { getPlatform } from "../platform";
|
|
10
11
|
import type { ProjectConfig } from "@jxsuite/schema/types";
|
|
@@ -21,16 +22,16 @@ let _handle: ReturnType<typeof openModal> | null = null;
|
|
|
21
22
|
* }}
|
|
22
23
|
*/
|
|
23
24
|
let _form = {
|
|
24
|
-
name: "",
|
|
25
|
-
description: "",
|
|
26
|
-
url: "",
|
|
27
25
|
adapter: "static",
|
|
26
|
+
description: "",
|
|
28
27
|
directory: "",
|
|
28
|
+
name: "",
|
|
29
|
+
url: "",
|
|
29
30
|
};
|
|
30
31
|
|
|
31
|
-
let _error
|
|
32
|
+
let _error = "";
|
|
32
33
|
|
|
33
|
-
let _creating
|
|
34
|
+
let _creating = false;
|
|
34
35
|
|
|
35
36
|
/** @type {((result: { root: string; config: object } | null) => void) | null} */
|
|
36
37
|
let _resolve: ((result: { root: string; config: ProjectConfig } | null) => void) | null = null;
|
|
@@ -45,13 +46,15 @@ export function openNewProjectModal(): Promise<{
|
|
|
45
46
|
root: string;
|
|
46
47
|
config: ProjectConfig;
|
|
47
48
|
} | null> {
|
|
48
|
-
if (_handle)
|
|
49
|
+
if (_handle) {
|
|
50
|
+
return Promise.resolve(null);
|
|
51
|
+
}
|
|
49
52
|
_form = {
|
|
50
|
-
name: "",
|
|
51
|
-
description: "",
|
|
52
|
-
url: "",
|
|
53
53
|
adapter: "static",
|
|
54
|
+
description: "",
|
|
54
55
|
directory: "",
|
|
56
|
+
name: "",
|
|
57
|
+
url: "",
|
|
55
58
|
};
|
|
56
59
|
_error = "";
|
|
57
60
|
_creating = false;
|
|
@@ -63,7 +66,9 @@ export function openNewProjectModal(): Promise<{
|
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
export function closeNewProjectModal() {
|
|
66
|
-
if (!_handle)
|
|
69
|
+
if (!_handle) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
67
72
|
_handle.close();
|
|
68
73
|
_handle = null;
|
|
69
74
|
if (_resolve) {
|
|
@@ -83,10 +88,12 @@ function renderModal() {
|
|
|
83
88
|
if (_dirDerived && field === "name") {
|
|
84
89
|
_form.directory = _form.name
|
|
85
90
|
.toLowerCase()
|
|
86
|
-
.
|
|
87
|
-
.
|
|
91
|
+
.replaceAll(/[^a-z0-9]+/g, "-")
|
|
92
|
+
.replaceAll(/^-|-$/g, "");
|
|
93
|
+
}
|
|
94
|
+
if (field === "directory") {
|
|
95
|
+
_dirDerived = false;
|
|
88
96
|
}
|
|
89
|
-
if (field === "directory") _dirDerived = false;
|
|
90
97
|
renderModal();
|
|
91
98
|
};
|
|
92
99
|
|
|
@@ -104,8 +111,8 @@ function renderModal() {
|
|
|
104
111
|
if (!_form.directory.trim()) {
|
|
105
112
|
_form.directory = _form.name
|
|
106
113
|
.toLowerCase()
|
|
107
|
-
.
|
|
108
|
-
.
|
|
114
|
+
.replaceAll(/[^a-z0-9]+/g, "-")
|
|
115
|
+
.replaceAll(/^-|-$/g, "");
|
|
109
116
|
}
|
|
110
117
|
|
|
111
118
|
_creating = true;
|
|
@@ -124,9 +131,9 @@ function renderModal() {
|
|
|
124
131
|
_resolve(result);
|
|
125
132
|
_resolve = null;
|
|
126
133
|
}
|
|
127
|
-
} catch (
|
|
134
|
+
} catch (error) {
|
|
128
135
|
_creating = false;
|
|
129
|
-
_error = (
|
|
136
|
+
_error = errorMessage(error);
|
|
130
137
|
renderModal();
|
|
131
138
|
}
|
|
132
139
|
};
|
|
@@ -136,7 +143,9 @@ function renderModal() {
|
|
|
136
143
|
<div
|
|
137
144
|
class="new-project-modal"
|
|
138
145
|
@keydown=${(e: KeyboardEvent) => {
|
|
139
|
-
if (e.key === "Escape")
|
|
146
|
+
if (e.key === "Escape") {
|
|
147
|
+
closeNewProjectModal();
|
|
148
|
+
}
|
|
140
149
|
}}
|
|
141
150
|
>
|
|
142
151
|
<div class="new-project-modal-header">
|
|
@@ -5,11 +5,13 @@ import { html, render as litRender, nothing } from "lit-html";
|
|
|
5
5
|
import { activityBar, renderOnly } from "../store";
|
|
6
6
|
import { effect, effectScope } from "../reactivity";
|
|
7
7
|
import { activeTab } from "../workspace/workspace";
|
|
8
|
-
import {
|
|
8
|
+
import { applyPanelCollapse, view } from "../view";
|
|
9
9
|
import { openSettingsModal } from "../settings/settings-modal";
|
|
10
10
|
import { refreshGitStatus } from "./git-panel";
|
|
11
|
+
import type { EffectScope } from "@vue/reactivity";
|
|
12
|
+
import type { TemplateResult } from "lit-html";
|
|
11
13
|
|
|
12
|
-
let _scope:
|
|
14
|
+
let _scope: EffectScope | null = null;
|
|
13
15
|
|
|
14
16
|
export function mount() {
|
|
15
17
|
_scope = effectScope();
|
|
@@ -57,27 +59,27 @@ const gitBranchIcon = (s: string) => html`
|
|
|
57
59
|
* @param {string} [size]
|
|
58
60
|
*/
|
|
59
61
|
export function tabIcon(tag: string, size?: string) {
|
|
60
|
-
const m: Record<string, (s: string) =>
|
|
61
|
-
"sp-icon-
|
|
62
|
-
|
|
63
|
-
"sp-icon-
|
|
64
|
-
html`<sp-icon-view-grid slot="icon" size=${s}></sp-icon-view-grid>`,
|
|
62
|
+
const m: Record<string, (s: string) => TemplateResult> = {
|
|
63
|
+
"sp-icon-artboard": (s: string) =>
|
|
64
|
+
html`<sp-icon-artboard slot="icon" size=${s}></sp-icon-artboard>`,
|
|
65
|
+
"sp-icon-box": (s: string) => html`<sp-icon-box slot="icon" size=${s}></sp-icon-box>`,
|
|
65
66
|
"sp-icon-brackets": (s: string) =>
|
|
66
67
|
html`<sp-icon-brackets slot="icon" size=${s}></sp-icon-brackets>`,
|
|
68
|
+
"sp-icon-brush": (s: string) => html`<sp-icon-brush slot="icon" size=${s}></sp-icon-brush>`,
|
|
69
|
+
"sp-icon-chat": (s: string) => html`<sp-icon-chat slot="icon" size=${s}></sp-icon-chat>`,
|
|
67
70
|
"sp-icon-data": (s: string) => html`<sp-icon-data slot="icon" size=${s}></sp-icon-data>`,
|
|
68
|
-
"sp-icon-properties": (s: string) =>
|
|
69
|
-
html`<sp-icon-properties slot="icon" size=${s}></sp-icon-properties>`,
|
|
70
71
|
"sp-icon-event": (s: string) => html`<sp-icon-event slot="icon" size=${s}></sp-icon-event>`,
|
|
71
|
-
"sp-icon-brush": (s: string) => html`<sp-icon-brush slot="icon" size=${s}></sp-icon-brush>`,
|
|
72
72
|
"sp-icon-file-single-web-page": (s: string) =>
|
|
73
73
|
html`<sp-icon-file-single-web-page slot="icon" size=${s}></sp-icon-file-single-web-page>`,
|
|
74
|
+
"sp-icon-folder": (s: string) => html`<sp-icon-folder slot="icon" size=${s}></sp-icon-folder>`,
|
|
75
|
+
"sp-icon-git-branch": gitBranchIcon,
|
|
76
|
+
"sp-icon-layers": (s: string) => html`<sp-icon-layers slot="icon" size=${s}></sp-icon-layers>`,
|
|
77
|
+
"sp-icon-properties": (s: string) =>
|
|
78
|
+
html`<sp-icon-properties slot="icon" size=${s}></sp-icon-properties>`,
|
|
74
79
|
"sp-icon-view-all-tags": (s: string) =>
|
|
75
80
|
html`<sp-icon-view-all-tags slot="icon" size=${s}></sp-icon-view-all-tags>`,
|
|
76
|
-
"sp-icon-
|
|
77
|
-
html`<sp-icon-
|
|
78
|
-
"sp-icon-chat": (s: string) => html`<sp-icon-chat slot="icon" size=${s}></sp-icon-chat>`,
|
|
79
|
-
"sp-icon-box": (s: string) => html`<sp-icon-box slot="icon" size=${s}></sp-icon-box>`,
|
|
80
|
-
"sp-icon-git-branch": gitBranchIcon,
|
|
81
|
+
"sp-icon-view-grid": (s: string) =>
|
|
82
|
+
html`<sp-icon-view-grid slot="icon" size=${s}></sp-icon-view-grid>`,
|
|
81
83
|
};
|
|
82
84
|
const fn = m[tag];
|
|
83
85
|
return fn ? fn(size || "s") : nothing;
|
|
@@ -85,17 +87,17 @@ export function tabIcon(tag: string, size?: string) {
|
|
|
85
87
|
|
|
86
88
|
export function renderActivityBar() {
|
|
87
89
|
const tab = activeTab.value;
|
|
88
|
-
const leftTab = view
|
|
90
|
+
const { leftTab } = view;
|
|
89
91
|
const gitFileCount = tab?.session.ui.gitStatus?.files?.length || 0;
|
|
90
92
|
const tabs = [
|
|
91
|
-
{
|
|
92
|
-
{
|
|
93
|
-
{
|
|
94
|
-
{
|
|
95
|
-
{
|
|
96
|
-
{
|
|
97
|
-
{
|
|
98
|
-
{
|
|
93
|
+
{ icon: "sp-icon-folder", label: "Files", value: "files" },
|
|
94
|
+
{ icon: "sp-icon-layers", label: "Layers", value: "layers" },
|
|
95
|
+
{ icon: "sp-icon-box", label: "Imports", value: "imports" },
|
|
96
|
+
{ icon: "sp-icon-view-grid", label: "Elements", value: "blocks" },
|
|
97
|
+
{ icon: "sp-icon-brackets", label: "State", value: "state" },
|
|
98
|
+
{ icon: "sp-icon-data", label: "Data", value: "data" },
|
|
99
|
+
{ icon: "sp-icon-view-all-tags", label: "Document", value: "head" },
|
|
100
|
+
{ icon: "sp-icon-git-branch", label: "Source Control", value: "git" },
|
|
99
101
|
];
|
|
100
102
|
const tpl = html`
|
|
101
103
|
<sp-tabs
|
|
@@ -107,14 +109,13 @@ export function renderActivityBar() {
|
|
|
107
109
|
if (clicked === view.leftTab && !view.leftPanelCollapsed) {
|
|
108
110
|
view.leftPanelCollapsed = true;
|
|
109
111
|
applyPanelCollapse();
|
|
110
|
-
renderActivityBar();
|
|
111
112
|
} else {
|
|
112
113
|
view.leftTab = clicked;
|
|
113
114
|
view.leftPanelCollapsed = false;
|
|
114
115
|
applyPanelCollapse();
|
|
115
116
|
renderOnly("leftPanel");
|
|
116
|
-
renderActivityBar();
|
|
117
117
|
}
|
|
118
|
+
renderActivityBar();
|
|
118
119
|
}}
|
|
119
120
|
>
|
|
120
121
|
${tabs.map(
|