@jxsuite/studio 3.0.0 → 4.0.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/README.md +5 -5
- package/dist/chunks/Overlay.dev-723bv0df.js +23 -0
- package/dist/chunks/{cf-settings-zj2cj1ar.js → cf-settings-45fgyx8y.js} +6 -5
- package/dist/chunks/{components-z6cnkyy1.js → components-zmx10t62.js} +6 -5
- package/dist/chunks/content-source-t88vb2dr.js +49 -0
- package/dist/chunks/{convert-to-repeater-6vvx96n2.js → convert-to-repeater-dhw1nrz9.js} +27 -19
- package/dist/chunks/entry-commands-6z6bhxg0.js +46 -0
- package/dist/chunks/{extension-sections-kwzvpemr.js → extension-sections-3ydgjbdy.js} +32 -24
- package/dist/chunks/file-ops-rskmhxyq.js +48 -0
- package/dist/chunks/files-0cratsr0.js +92 -0
- package/dist/chunks/{format-host-8fr3545e.js → format-host-tgcmfs4p.js} +3 -3
- package/dist/chunks/fs-events-hrrg328g.js +21 -0
- package/dist/chunks/{git-panel-g01d93vc.js → git-panel-0yywyk76.js} +21 -16
- package/dist/chunks/{jx-validate-jj9rnt4n.js → jx-validate-8q2g05x1.js} +6 -4
- package/dist/chunks/layers-panel-z3tqe5cd.js +67 -0
- package/dist/chunks/layers-q75tj2mf.js +45 -0
- package/dist/chunks/lit-html-j055mkh8.js +22 -0
- package/dist/chunks/monaco-buffer-dqw0xxpx.js +25 -0
- package/dist/chunks/{monaco-setup-a5wkmbgh.js → monaco-setup-rws7k4br.js} +4 -4
- package/dist/chunks/notify-yjm79n4s.js +30 -0
- package/dist/chunks/parse-24d0gay3.js +19 -0
- package/dist/chunks/platform-ba1j1gbh.js +16 -0
- package/dist/chunks/publish-panel-54jvhskq.js +28 -0
- package/dist/chunks/quick-search-cgfmjwq5.js +65 -0
- package/dist/chunks/references-3qx8fkyp.js +29 -0
- package/dist/chunks/right-panel-cqp8bc4v.js +46 -0
- package/dist/chunks/sp-overlay.dev-yztmtrs1.js +18 -0
- package/dist/chunks/sp-popover.dev-n2etmqdp.js +9 -0
- package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +15 -0
- package/dist/chunks/sp-tooltip.dev-xcvpnqr0.js +15 -0
- package/dist/chunks/{sp-tray.dev-64v4pxw8.js → sp-tray.dev-tkbscm06.js} +5 -5
- package/dist/chunks/{studio-8ezdegcx.js → studio-1hbqnt19.js} +36 -32
- package/dist/chunks/{studio-r5xbtctf.js → studio-2zkz8evn.js} +446 -2451
- package/dist/chunks/{studio-pkp61mh8.js → studio-4pqqxbrt.js} +23 -15
- package/dist/chunks/{studio-kvfx9z2c.js → studio-4rzyrtjz.js} +70 -31
- package/dist/chunks/{studio-3dkcd4c5.js → studio-5q69gfag.js} +60 -56
- package/dist/chunks/{studio-rh1cnamv.js → studio-5rywrkxz.js} +563 -133
- package/dist/chunks/{studio-6kv3apa9.js → studio-68n67094.js} +25 -9
- package/dist/chunks/{studio-0rqqhrdy.js → studio-7nte2hj4.js} +2 -2
- package/dist/chunks/{studio-xntfdy1x.js → studio-8swrjbny.js} +31 -18
- package/dist/chunks/studio-92ab2jqe.js +282 -0
- package/dist/chunks/{studio-sas7mt3k.js → studio-9ab8gqsm.js} +2 -2
- package/dist/chunks/{studio-ry1vz5sv.js → studio-9c4k4vhv.js} +15 -13
- package/dist/chunks/studio-9n4mrmnb.js +184 -0
- package/dist/chunks/{studio-tr20m8g8.js → studio-9r6j5ev1.js} +37 -2
- package/dist/chunks/{studio-2x9zbxmx.js → studio-ahjfdy6e.js} +9 -7
- package/dist/chunks/{studio-s519zc99.js → studio-bgxt93ga.js} +3 -3
- package/dist/chunks/{studio-dc8s8heb.js → studio-cb12yz9w.js} +8528 -8780
- package/dist/chunks/studio-cft9r4r7.js +119 -0
- package/dist/chunks/{studio-hcxzyk3x.js → studio-eenfkt4e.js} +6 -6
- package/dist/chunks/studio-eesa1myv.js +498 -0
- package/dist/chunks/{studio-ay8wcr2b.js → studio-eqq1w638.js} +7 -9
- package/dist/chunks/{studio-7p35254k.js → studio-ex8zyp98.js} +30 -26
- package/dist/chunks/studio-gv8vvshc.js +118 -0
- package/dist/chunks/studio-hbtp0eh1.js +1315 -0
- package/dist/chunks/{studio-a0yp7syn.js → studio-htsf0w6m.js} +29 -114
- package/dist/chunks/{studio-j595e748.js → studio-kjjsjak4.js} +139 -63
- package/dist/chunks/{studio-5x5774ak.js → studio-kryvsw6j.js} +3 -3
- package/dist/chunks/{studio-kfpek536.js → studio-kyh9avvv.js} +2 -2
- package/dist/chunks/{studio-g5gdqnyd.js → studio-kyk2v92a.js} +14 -14
- package/dist/chunks/{studio-twsafr1e.js → studio-m3hwn7y1.js} +3 -3
- package/dist/chunks/{studio-tzx3xmvx.js → studio-mmv72a27.js} +8 -8
- package/dist/chunks/{studio-vhk5fmfv.js → studio-n8b9wqwy.js} +5 -5
- package/dist/chunks/{studio-cjm7fdjf.js → studio-p4ngc046.js} +8 -104
- package/dist/chunks/{studio-p5dja96s.js → studio-p8j86wag.js} +2 -2
- package/dist/chunks/{studio-6z5eyjwn.js → studio-pdfjntbk.js} +66 -129
- package/dist/chunks/studio-q43a5y9h.js +394 -0
- package/dist/chunks/studio-q7v10cg2.js +106 -0
- package/dist/chunks/{studio-2w11grav.js → studio-qq6jym04.js} +3 -3
- package/dist/chunks/studio-r1r7afxx.js +1066 -0
- package/dist/chunks/{studio-3fk1ac9z.js → studio-rdx8jcxy.js} +4 -4
- package/dist/chunks/{studio-4mqb9a0j.js → studio-rn9ta2mz.js} +4 -4
- package/dist/chunks/studio-sf7zjgv1.js +91 -0
- package/dist/chunks/studio-t7qb9db8.js +665 -0
- package/dist/chunks/studio-tmc5emrw.js +8 -0
- package/dist/chunks/{studio-wdt9y92a.js → studio-txwkm6p9.js} +2 -2
- package/dist/chunks/{studio-k4jvjcfx.js → studio-v38hmvx1.js} +7 -7
- package/dist/chunks/{studio-7ejemhej.js → studio-vadzgkrz.js} +6 -6
- package/dist/chunks/studio-wcy8trfx.js +263 -0
- package/dist/chunks/{studio-5efrtn1m.js → studio-wx5609xd.js} +21 -17
- package/dist/chunks/{studio-qpfq7xhm.js → studio-xrapxwwc.js} +22 -285
- package/dist/chunks/{studio-hj2se583.js → studio-yxrkyv8a.js} +41 -31
- package/dist/chunks/{studio-xm3dbxvn.js → studio-zczps70q.js} +2 -2
- package/dist/chunks/workspace-xpres40v.js +89 -0
- package/dist/iframe-entry.js +108 -24
- package/dist/studio.js +2362 -1695
- package/package.json +16 -15
- package/src/browse/library-pane.ts +57 -3
- package/src/canvas/canvas-render.ts +23 -1
- package/src/canvas/iframe-patch.ts +47 -21
- package/src/canvas/iframe-render.ts +22 -6
- package/src/commands/app-commands.ts +4 -0
- package/src/commands/command-args.ts +29 -0
- package/src/commands/context.ts +15 -1
- package/src/commands/defaults.ts +29 -1
- package/src/commands/levels.ts +16 -0
- package/src/commands/live-context.ts +3 -0
- package/src/content/collection-match.ts +267 -0
- package/src/content/entry-commands.ts +38 -29
- package/src/content/entry-fields.ts +6 -0
- package/src/editor/shortcuts.ts +5 -0
- package/src/files/file-ops.ts +10 -42
- package/src/files/files.ts +485 -59
- package/src/files/fs-events.ts +9 -0
- package/src/files/gitignore.ts +432 -0
- package/src/files/serialize-document.ts +60 -0
- package/src/format/convert-file.ts +511 -0
- package/src/format/format-choices.ts +199 -0
- package/src/grid/grid-view.ts +16 -2
- package/src/media/media-open.ts +58 -0
- package/src/media/media-pane.ts +310 -0
- package/src/new-project/import-tab.ts +332 -160
- package/src/new-project/new-project-modal.ts +27 -34
- package/src/packages/ensure-deps.ts +14 -7
- package/src/packages/jxsuite-update.ts +11 -11
- package/src/page-params.ts +10 -46
- package/src/panels/activity-bar.ts +45 -16
- package/src/panels/ai-chat/attached-context.ts +6 -1
- package/src/panels/ai-chat/chat-view.ts +255 -13
- package/src/panels/ai-chat/composer.ts +21 -97
- package/src/panels/ai-panel.ts +116 -2
- package/src/panels/navigator-panels.ts +7 -6
- package/src/panels/settings-menu.ts +799 -0
- package/src/panels/toolbar.ts +135 -15
- package/src/platforms/cloud.ts +20 -0
- package/src/platforms/devserver.ts +67 -33
- package/src/preview/preview-overlay.ts +226 -0
- package/src/services/ai-ask.ts +247 -0
- package/src/services/ai-import-tools.ts +494 -0
- package/src/services/ai-project-tools.ts +20 -26
- package/src/services/ai-system-prompt.ts +88 -2
- package/src/services/ai-turn-signal.ts +70 -0
- package/src/services/connection.ts +74 -0
- package/src/services/context-manager.ts +89 -0
- package/src/services/document-assistant.ts +45 -4
- package/src/services/import-client.ts +33 -6
- package/src/services/import-run.ts +158 -0
- package/src/services/import-seed.ts +120 -0
- package/src/services/jx-validate.ts +58 -6
- package/src/services/project-adoption.ts +71 -0
- package/src/services/references.ts +0 -0
- package/src/services/settings/definitions.ts +17 -0
- package/src/services/tool-executor.ts +34 -2
- package/src/settings/contexts-section.ts +45 -3
- package/src/settings/dependencies-editor.ts +43 -23
- package/src/settings/general-settings.ts +30 -12
- package/src/settings/preferences-dialog.ts +18 -41
- package/src/settings/preferences-sections.ts +57 -0
- package/src/settings/settings-document.ts +65 -9
- package/src/studio.ts +33 -6
- package/src/tabs/transact.ts +42 -19
- package/src/types.ts +103 -5
- package/src/ui/ai-model-picker.ts +162 -0
- package/src/ui/choice-field.ts +72 -0
- package/src/ui/layers.ts +83 -5
- package/src/utils/studio-utils.ts +22 -52
- package/styles/forced-colors.css +7 -0
- package/styles/overlays.css +256 -0
- package/styles/panels.css +13 -1
- package/styles/shell.css +111 -0
- package/dist/chunks/Overlay.dev-d2ajy6sj.js +0 -23
- package/dist/chunks/content-source-7fy9qrm0.js +0 -40
- package/dist/chunks/files-xcybanxe.js +0 -70
- package/dist/chunks/layers-panel-27essgkc.js +0 -60
- package/dist/chunks/publish-panel-09sy2bab.js +0 -22
- package/dist/chunks/quick-search-sefb61v8.js +0 -55
- package/dist/chunks/right-panel-9r32jd5j.js +0 -39
- package/dist/chunks/sp-overlay.dev-s18hymn9.js +0 -18
- package/dist/chunks/sp-popover.dev-dj9x7e7a.js +0 -9
- package/dist/chunks/sp-progress-circle.dev-0zxcyz3r.js +0 -15
- package/dist/chunks/sp-tooltip.dev-5c6d1kjz.js +0 -15
- package/dist/chunks/studio-c31yb4t6.js +0 -55
- package/src/canvas/site-style-css.ts +0 -117
|
@@ -3,74 +3,117 @@ import {
|
|
|
3
3
|
measuredRowHeight,
|
|
4
4
|
revealListRow,
|
|
5
5
|
watchListWindow
|
|
6
|
-
} from "./studio-
|
|
6
|
+
} from "./studio-p8j86wag.js";
|
|
7
|
+
import {
|
|
8
|
+
formatBytes,
|
|
9
|
+
loadMediaMeta,
|
|
10
|
+
peekMediaMeta,
|
|
11
|
+
recordImageSize
|
|
12
|
+
} from "./studio-gv8vvshc.js";
|
|
7
13
|
import {
|
|
8
14
|
autoSyncProjectOnOpen,
|
|
9
15
|
cleanupGitPanel,
|
|
10
16
|
maybePromptJxsuiteUpdate,
|
|
11
17
|
shouldInstallAutomation
|
|
12
|
-
} from "./studio-
|
|
18
|
+
} from "./studio-ex8zyp98.js";
|
|
19
|
+
import {
|
|
20
|
+
collectionOfPath
|
|
21
|
+
} from "./studio-sf7zjgv1.js";
|
|
13
22
|
import {
|
|
14
23
|
openCsvGridTab,
|
|
15
24
|
openPagesGrid
|
|
16
|
-
} from "./studio-
|
|
25
|
+
} from "./studio-4pqqxbrt.js";
|
|
17
26
|
import {
|
|
18
|
-
collectionDirs,
|
|
19
|
-
collectionInfo,
|
|
20
27
|
confirmFileDelete,
|
|
21
|
-
|
|
28
|
+
disarmPreviewOverlay,
|
|
29
|
+
loadMediaUsages,
|
|
30
|
+
mediaUsageHeadline,
|
|
22
31
|
parseSourceForPath,
|
|
32
|
+
peekMediaUsages,
|
|
23
33
|
renamePromptMessage
|
|
24
|
-
} from "./studio-
|
|
34
|
+
} from "./studio-cb12yz9w.js";
|
|
25
35
|
import {
|
|
26
|
-
|
|
36
|
+
ensureIgnoreLayers,
|
|
37
|
+
isIgnoredEntry,
|
|
38
|
+
markLocalMutation,
|
|
39
|
+
resetIgnoreCache
|
|
40
|
+
} from "./studio-q43a5y9h.js";
|
|
41
|
+
import {
|
|
42
|
+
extensionOf,
|
|
27
43
|
isImage,
|
|
44
|
+
isMediaFile,
|
|
45
|
+
mediaKind,
|
|
46
|
+
mediaSiteUrl,
|
|
47
|
+
previewFileSrc,
|
|
28
48
|
uploadAccept,
|
|
29
49
|
uploadAssets
|
|
30
|
-
} from "./studio-
|
|
50
|
+
} from "./studio-htsf0w6m.js";
|
|
51
|
+
import {
|
|
52
|
+
invalidateUsages
|
|
53
|
+
} from "./studio-cft9r4r7.js";
|
|
31
54
|
import {
|
|
32
55
|
showProgressModal
|
|
33
|
-
} from "./studio-
|
|
56
|
+
} from "./studio-9c4k4vhv.js";
|
|
34
57
|
import {
|
|
35
58
|
rectOf,
|
|
36
59
|
registerPanel
|
|
37
|
-
} from "./studio-
|
|
60
|
+
} from "./studio-kjjsjak4.js";
|
|
61
|
+
import {
|
|
62
|
+
markSessionRestored,
|
|
63
|
+
persistedSession,
|
|
64
|
+
resetProjectShell,
|
|
65
|
+
restoreSession,
|
|
66
|
+
setActivityTab
|
|
67
|
+
} from "./studio-hbtp0eh1.js";
|
|
38
68
|
import {
|
|
39
|
-
activateTab,
|
|
40
69
|
activeRegistry,
|
|
41
|
-
|
|
42
|
-
|
|
70
|
+
paneRegion,
|
|
71
|
+
renderPopover,
|
|
72
|
+
showPromptDialog
|
|
73
|
+
} from "./studio-t7qb9db8.js";
|
|
74
|
+
import {
|
|
75
|
+
errorMessage
|
|
76
|
+
} from "./studio-9n4mrmnb.js";
|
|
77
|
+
import {
|
|
78
|
+
activateTab,
|
|
79
|
+
collectionForDirectory,
|
|
80
|
+
collectionForFile,
|
|
43
81
|
isCollabPath,
|
|
44
82
|
localeLabel,
|
|
45
83
|
localeOfPath,
|
|
46
|
-
markSessionRestored,
|
|
47
84
|
moveTabToPane,
|
|
48
85
|
openTab,
|
|
49
86
|
paneOfTab,
|
|
50
|
-
persistedSession,
|
|
51
87
|
receivingPane,
|
|
52
88
|
renameTab,
|
|
53
|
-
renderPopover,
|
|
54
89
|
replaceAllTabs,
|
|
55
|
-
resetProjectShell,
|
|
56
90
|
resolveI18n,
|
|
57
|
-
restoreSession,
|
|
58
|
-
setActivityTab,
|
|
59
91
|
setWorkspaceProject,
|
|
60
|
-
showPromptDialog,
|
|
61
92
|
workspace
|
|
62
|
-
} from "./studio-
|
|
93
|
+
} from "./studio-2zkz8evn.js";
|
|
94
|
+
import {
|
|
95
|
+
SETTINGS,
|
|
96
|
+
readStoredSetting,
|
|
97
|
+
setSetting
|
|
98
|
+
} from "./studio-92ab2jqe.js";
|
|
99
|
+
import {
|
|
100
|
+
effect,
|
|
101
|
+
effectScope,
|
|
102
|
+
notify,
|
|
103
|
+
now
|
|
104
|
+
} from "./studio-xrapxwwc.js";
|
|
63
105
|
import {
|
|
64
106
|
classMap
|
|
65
|
-
} from "./studio-
|
|
107
|
+
} from "./studio-m3hwn7y1.js";
|
|
66
108
|
import {
|
|
67
109
|
ref,
|
|
68
110
|
repeat
|
|
69
|
-
} from "./studio-
|
|
111
|
+
} from "./studio-4rzyrtjz.js";
|
|
70
112
|
import {
|
|
71
113
|
html,
|
|
72
|
-
nothing
|
|
73
|
-
|
|
114
|
+
nothing,
|
|
115
|
+
render
|
|
116
|
+
} from "./studio-kryvsw6j.js";
|
|
74
117
|
import {
|
|
75
118
|
combine,
|
|
76
119
|
draggable,
|
|
@@ -79,46 +122,47 @@ import {
|
|
|
79
122
|
} from "./studio-g1wdche0.js";
|
|
80
123
|
import {
|
|
81
124
|
loadComponentRegistry
|
|
82
|
-
} from "./studio-
|
|
125
|
+
} from "./studio-bgxt93ga.js";
|
|
83
126
|
import {
|
|
84
127
|
projectState,
|
|
85
128
|
requireProjectState,
|
|
86
129
|
setProjectState
|
|
87
|
-
} from "./studio-
|
|
88
|
-
import {
|
|
89
|
-
notify,
|
|
90
|
-
now
|
|
91
|
-
} from "./studio-qpfq7xhm.js";
|
|
130
|
+
} from "./studio-p4ngc046.js";
|
|
92
131
|
import {
|
|
93
132
|
documentExtensions,
|
|
133
|
+
formatByExtension,
|
|
94
134
|
formatForPath,
|
|
135
|
+
getFormats,
|
|
95
136
|
loadFormats,
|
|
96
137
|
noFormatError,
|
|
97
138
|
refreshExtensionUi,
|
|
98
139
|
refreshFormats
|
|
99
|
-
} from "./studio-
|
|
140
|
+
} from "./studio-eenfkt4e.js";
|
|
100
141
|
import {
|
|
101
142
|
getPlatform,
|
|
102
143
|
hasPlatform
|
|
103
|
-
} from "./studio-
|
|
144
|
+
} from "./studio-9ab8gqsm.js";
|
|
145
|
+
import {
|
|
146
|
+
__require
|
|
147
|
+
} from "./studio-a0tswy0w.js";
|
|
104
148
|
|
|
105
149
|
// src/packages/ensure-deps.ts
|
|
106
150
|
async function ensureDependenciesInstalled() {
|
|
107
151
|
if (shouldInstallAutomation(location.search)) {
|
|
108
|
-
return;
|
|
152
|
+
return false;
|
|
109
153
|
}
|
|
110
154
|
const platform = getPlatform();
|
|
111
155
|
if (!platform.dependenciesNeedInstall || !platform.installDependencies) {
|
|
112
|
-
return;
|
|
156
|
+
return false;
|
|
113
157
|
}
|
|
114
158
|
let needs = false;
|
|
115
159
|
try {
|
|
116
160
|
needs = await platform.dependenciesNeedInstall();
|
|
117
161
|
} catch {
|
|
118
|
-
return;
|
|
162
|
+
return false;
|
|
119
163
|
}
|
|
120
164
|
if (!needs) {
|
|
121
|
-
return;
|
|
165
|
+
return false;
|
|
122
166
|
}
|
|
123
167
|
const progress = showProgressModal({
|
|
124
168
|
status: "Running bun install…",
|
|
@@ -128,89 +172,287 @@ async function ensureDependenciesInstalled() {
|
|
|
128
172
|
const result = await platform.installDependencies();
|
|
129
173
|
if (result.ok) {
|
|
130
174
|
progress.done();
|
|
131
|
-
|
|
132
|
-
progress.fail(result.log ?? "bun install failed");
|
|
175
|
+
return true;
|
|
133
176
|
}
|
|
177
|
+
progress.fail(result.log ?? "bun install failed");
|
|
134
178
|
} catch (error) {
|
|
135
179
|
progress.fail(error instanceof Error ? error.message : String(error));
|
|
136
180
|
}
|
|
181
|
+
return false;
|
|
137
182
|
}
|
|
138
183
|
|
|
139
|
-
// src/
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
184
|
+
// src/media/media-pane.ts
|
|
185
|
+
var MEDIA_MODE = "media";
|
|
186
|
+
var FONT_EXTENSIONS = new Set([".woff", ".woff2", ".ttf", ".otf"]);
|
|
187
|
+
var SPECIMEN = "The quick brown fox jumps over the lazy dog";
|
|
188
|
+
var SPECIMEN_SIZES = [48, 32, 24, 18, 14];
|
|
189
|
+
var _active = new Map;
|
|
190
|
+
function activeIn(paneId) {
|
|
191
|
+
return _active.get(paneId) ?? null;
|
|
192
|
+
}
|
|
193
|
+
function mediaPaneMounted(paneId, tab) {
|
|
194
|
+
const panel = activeIn(paneId);
|
|
195
|
+
return panel !== null && panel.tabId === tab.id && panel.wrap.isConnected;
|
|
196
|
+
}
|
|
197
|
+
function detachMediaPane(paneId) {
|
|
198
|
+
const panel = _active.get(paneId);
|
|
199
|
+
if (!panel) {
|
|
200
|
+
return;
|
|
147
201
|
}
|
|
148
|
-
|
|
149
|
-
|
|
202
|
+
panel.scope.stop();
|
|
203
|
+
_active.delete(paneId);
|
|
204
|
+
}
|
|
205
|
+
function assetTpl(path, src, redraw) {
|
|
206
|
+
const ext = extensionOf(path);
|
|
207
|
+
const kind = mediaKind({ name: path });
|
|
208
|
+
if (FONT_EXTENSIONS.has(ext)) {
|
|
209
|
+
const family = `jx-specimen-${path.replaceAll(/[^a-zA-Z0-9]/g, "-")}`;
|
|
210
|
+
return html`
|
|
211
|
+
<style>
|
|
212
|
+
@font-face {
|
|
213
|
+
font-family: "${family}";
|
|
214
|
+
src: url("${src}");
|
|
215
|
+
}
|
|
216
|
+
</style>
|
|
217
|
+
<div class="media-specimen" style="font-family: '${family}', system-ui, sans-serif">
|
|
218
|
+
${SPECIMEN_SIZES.map((size) => html`<p class="media-specimen-line" style="font-size: ${size}px">
|
|
219
|
+
${SPECIMEN}
|
|
220
|
+
</p>`)}
|
|
221
|
+
</div>
|
|
222
|
+
`;
|
|
223
|
+
}
|
|
224
|
+
switch (kind) {
|
|
225
|
+
case "image": {
|
|
226
|
+
return html`<img
|
|
227
|
+
class="media-image"
|
|
228
|
+
src=${src}
|
|
229
|
+
alt=""
|
|
230
|
+
@load=${(e) => {
|
|
231
|
+
const img = e.target;
|
|
232
|
+
if (recordImageSize(path, img.naturalWidth, img.naturalHeight)) {
|
|
233
|
+
redraw();
|
|
234
|
+
}
|
|
235
|
+
}}
|
|
236
|
+
/>`;
|
|
237
|
+
}
|
|
238
|
+
case "video": {
|
|
239
|
+
return html`<video class="media-video" src=${src} controls></video>`;
|
|
240
|
+
}
|
|
241
|
+
case "audio": {
|
|
242
|
+
return html`<audio class="media-audio" src=${src} controls></audio>`;
|
|
243
|
+
}
|
|
244
|
+
default: {
|
|
245
|
+
return ext === ".pdf" ? html`<embed class="media-embed" src=${src} type="application/pdf" />` : html`<div class="media-unviewable">
|
|
246
|
+
<p>Jx has no viewer for <code>${ext || "this file"}</code>.</p>
|
|
247
|
+
<p class="media-note">The file is in the project and builds normally.</p>
|
|
248
|
+
</div>`;
|
|
249
|
+
}
|
|
150
250
|
}
|
|
151
|
-
return {
|
|
152
|
-
def: info.def,
|
|
153
|
-
dir: info.dir,
|
|
154
|
-
ext: info.ext,
|
|
155
|
-
name: info.name,
|
|
156
|
-
schema: info.def.schema ?? null
|
|
157
|
-
};
|
|
158
251
|
}
|
|
159
|
-
function
|
|
160
|
-
|
|
252
|
+
function factsTpl(path, meta) {
|
|
253
|
+
const facts = [mediaKind({ name: path })];
|
|
254
|
+
if (meta?.width != null && meta.height != null) {
|
|
255
|
+
facts.push(`${meta.width} × ${meta.height}`);
|
|
256
|
+
}
|
|
257
|
+
if (meta?.bytes != null) {
|
|
258
|
+
facts.push(formatBytes(meta.bytes));
|
|
259
|
+
}
|
|
260
|
+
if (meta?.modified) {
|
|
261
|
+
facts.push(`modified ${meta.modified.slice(0, 10)}`);
|
|
262
|
+
}
|
|
263
|
+
return html`<p class="media-facts">${facts.join(" · ")}</p>`;
|
|
161
264
|
}
|
|
162
|
-
function
|
|
163
|
-
const
|
|
164
|
-
return
|
|
265
|
+
function refTpl(path) {
|
|
266
|
+
const ref2 = mediaSiteUrl(path);
|
|
267
|
+
return html`
|
|
268
|
+
<div class="media-ref">
|
|
269
|
+
<code class="media-ref-value">${ref2}</code>
|
|
270
|
+
<sp-action-button
|
|
271
|
+
size="s"
|
|
272
|
+
quiet
|
|
273
|
+
title="Copy the reference"
|
|
274
|
+
@click=${() => {
|
|
275
|
+
navigator.clipboard?.writeText(ref2);
|
|
276
|
+
}}
|
|
277
|
+
>Copy</sp-action-button
|
|
278
|
+
>
|
|
279
|
+
</div>
|
|
280
|
+
`;
|
|
165
281
|
}
|
|
166
|
-
function
|
|
167
|
-
|
|
168
|
-
|
|
282
|
+
function usageTpl(usage) {
|
|
283
|
+
const headline = mediaUsageHeadline(usage);
|
|
284
|
+
if (headline === null) {
|
|
285
|
+
return nothing;
|
|
169
286
|
}
|
|
170
|
-
|
|
287
|
+
const files = usage?.status === "ready" ? usage.result.files : [];
|
|
288
|
+
return html`
|
|
289
|
+
<div class="media-usage">
|
|
290
|
+
<h4 class="media-usage-title">Used by</h4>
|
|
291
|
+
<p class="media-usage-headline">${headline}</p>
|
|
292
|
+
${files.length === 0 ? nothing : html`<ul class="media-usage-list">
|
|
293
|
+
${files.map((file) => html`<li>
|
|
294
|
+
<button
|
|
295
|
+
type="button"
|
|
296
|
+
class="media-usage-link"
|
|
297
|
+
@click=${() => {
|
|
298
|
+
openDocument(file.path);
|
|
299
|
+
}}
|
|
300
|
+
>
|
|
301
|
+
${file.path}
|
|
302
|
+
</button>
|
|
303
|
+
<span class="media-usage-count">${file.count}</span>
|
|
304
|
+
</li>`)}
|
|
305
|
+
</ul>`}
|
|
306
|
+
</div>
|
|
307
|
+
`;
|
|
171
308
|
}
|
|
172
|
-
function
|
|
173
|
-
|
|
174
|
-
|
|
309
|
+
async function openDocument(path) {
|
|
310
|
+
const { openFileInTab } = await import("./files-0cratsr0.js");
|
|
311
|
+
await openFileInTab(path);
|
|
312
|
+
}
|
|
313
|
+
function renderMediaMode(surface, tab) {
|
|
314
|
+
const { paneId, wrap: canvasWrap } = surface;
|
|
315
|
+
if (mediaPaneMounted(paneId, tab)) {
|
|
316
|
+
return;
|
|
175
317
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
318
|
+
detachMediaPane(paneId);
|
|
319
|
+
const scope = effectScope();
|
|
320
|
+
const panel = { paneId, scope, tabId: tab.id, wrap: canvasWrap };
|
|
321
|
+
_active.set(paneId, panel);
|
|
322
|
+
const path = tab.documentPath ?? "";
|
|
323
|
+
const redraw = () => {
|
|
324
|
+
if (activeIn(paneId) === panel) {
|
|
325
|
+
draw();
|
|
179
326
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
327
|
+
};
|
|
328
|
+
const draw = () => {
|
|
329
|
+
const meta = peekMediaMeta(path);
|
|
330
|
+
const usage = peekMediaUsages(path);
|
|
331
|
+
if (meta === null) {
|
|
332
|
+
loadMediaMeta(path).then(redraw);
|
|
183
333
|
}
|
|
184
|
-
|
|
185
|
-
|
|
334
|
+
if (usage === null || usage.status === "pending") {
|
|
335
|
+
loadMediaUsages(path).then(redraw);
|
|
186
336
|
}
|
|
187
|
-
|
|
188
|
-
|
|
337
|
+
render(html`
|
|
338
|
+
<div class="media-viewer" data-jx-region=${paneRegion(paneId, "media")}>
|
|
339
|
+
<div class="media-stage">${assetTpl(path, previewFileSrc(path), redraw)}</div>
|
|
340
|
+
<div class="media-details">
|
|
341
|
+
<h3 class="media-name">${path.split("/").pop()}</h3>
|
|
342
|
+
${factsTpl(path, meta)} ${refTpl(path)} ${usageTpl(usage)}
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
`, canvasWrap);
|
|
346
|
+
};
|
|
347
|
+
scope.run(() => {
|
|
348
|
+
effect(() => {
|
|
349
|
+
if (activeIn(paneId) !== panel) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
tab.documentPath;
|
|
353
|
+
draw();
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/media/media-open.ts
|
|
359
|
+
var MEDIA_STUB_DOCUMENT = { children: [], tagName: "div" };
|
|
360
|
+
function isViewableMedia(path) {
|
|
361
|
+
return isMediaFile(path);
|
|
362
|
+
}
|
|
363
|
+
function mediaTabModes(path) {
|
|
364
|
+
return extensionOf(path) === ".svg" ? [MEDIA_MODE, "source"] : [MEDIA_MODE];
|
|
365
|
+
}
|
|
366
|
+
function openMediaTab(path) {
|
|
367
|
+
const existing = workspace.tabs.get(path);
|
|
368
|
+
if (existing) {
|
|
369
|
+
activateTab(path);
|
|
370
|
+
return existing;
|
|
371
|
+
}
|
|
372
|
+
return openTab({
|
|
373
|
+
capabilities: { modes: mediaTabModes(path) },
|
|
374
|
+
document: structuredClone(MEDIA_STUB_DOCUMENT),
|
|
375
|
+
documentPath: path,
|
|
376
|
+
id: path,
|
|
377
|
+
sourceFormat: null
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// src/format/format-choices.ts
|
|
382
|
+
var DOCUMENT_KINDS = ["page", "component"];
|
|
383
|
+
var DOCUMENT_ROOTS = { components: "component", pages: "page" };
|
|
384
|
+
function normalize(path) {
|
|
385
|
+
return path.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
386
|
+
}
|
|
387
|
+
function documentRootOf(path) {
|
|
388
|
+
const lower = normalize(path).toLowerCase();
|
|
389
|
+
for (const [dir, kind] of Object.entries(DOCUMENT_ROOTS)) {
|
|
390
|
+
if (lower.startsWith(`${dir}/`)) {
|
|
391
|
+
return kind;
|
|
189
392
|
}
|
|
190
|
-
|
|
191
|
-
|
|
393
|
+
}
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
function declaresDocumentKind(kinds) {
|
|
397
|
+
return DOCUMENT_KINDS.some((kind) => kinds.includes(kind));
|
|
398
|
+
}
|
|
399
|
+
function isLayoutPath(path) {
|
|
400
|
+
const declared = projectState?.projectConfig?.layout;
|
|
401
|
+
if (typeof declared !== "string") {
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
return normalize(declared).toLowerCase() === normalize(path).toLowerCase();
|
|
405
|
+
}
|
|
406
|
+
function creationFormats(kind) {
|
|
407
|
+
const rows = [{ ext: ".json", label: "JSON (.json)" }];
|
|
408
|
+
for (const ext of documentExtensions(kind)) {
|
|
409
|
+
const reader = formatByExtension(ext, "parse");
|
|
410
|
+
if (reader && formatByExtension(ext, "serialize")) {
|
|
411
|
+
rows.push({ ext, label: `${reader.name} (${ext})` });
|
|
192
412
|
}
|
|
193
413
|
}
|
|
414
|
+
return rows;
|
|
415
|
+
}
|
|
416
|
+
function knownDocumentExtensions() {
|
|
417
|
+
return new Set([".json", ...getFormats().flatMap((format) => format.extensions)]);
|
|
194
418
|
}
|
|
195
|
-
function
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
return {};
|
|
419
|
+
function convertTargets(path) {
|
|
420
|
+
if (path === null || path === undefined || path === "") {
|
|
421
|
+
return [];
|
|
199
422
|
}
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
423
|
+
const root = documentRootOf(path);
|
|
424
|
+
if (root === null || isLayoutPath(path) || collectionForFile(path)) {
|
|
425
|
+
return [];
|
|
426
|
+
}
|
|
427
|
+
const source = formatForPath(path);
|
|
428
|
+
const readable = source ? Boolean(source.capabilities.parse) && declaresDocumentKind(source.documentKinds) : normalize(path).toLowerCase().endsWith(".json");
|
|
429
|
+
if (!readable) {
|
|
430
|
+
return [];
|
|
431
|
+
}
|
|
432
|
+
const rows = [];
|
|
433
|
+
if (source) {
|
|
434
|
+
rows.push({ ext: ".json", label: "JSON (.json)" });
|
|
435
|
+
}
|
|
436
|
+
for (const ext of documentExtensions()) {
|
|
437
|
+
const writer = formatByExtension(ext, "serialize");
|
|
438
|
+
if (!writer || writer.name === source?.name || !declaresDocumentKind(writer.documentKinds) || !writer.documentKinds.includes(root)) {
|
|
439
|
+
continue;
|
|
207
440
|
}
|
|
441
|
+
rows.push({ ext, label: `${writer.name} (${ext})` });
|
|
208
442
|
}
|
|
209
|
-
return
|
|
443
|
+
return rows;
|
|
210
444
|
}
|
|
211
|
-
function
|
|
212
|
-
const
|
|
213
|
-
|
|
445
|
+
function convertTargetExtensions() {
|
|
446
|
+
const exts = new Set([".json"]);
|
|
447
|
+
for (const format of getFormats()) {
|
|
448
|
+
if (!format.capabilities.serialize || !declaresDocumentKind(format.documentKinds)) {
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
for (const ext of format.extensions) {
|
|
452
|
+
exts.add(ext);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return [...exts].toSorted();
|
|
214
456
|
}
|
|
215
457
|
|
|
216
458
|
// src/recent-projects.ts
|
|
@@ -332,12 +574,21 @@ async function loadDirectory(dirPath) {
|
|
|
332
574
|
}
|
|
333
575
|
try {
|
|
334
576
|
const platform = getPlatform();
|
|
335
|
-
const entries = await
|
|
577
|
+
const [entries] = await Promise.all([
|
|
578
|
+
platform.listDirectory(dirPath),
|
|
579
|
+
ensureIgnoreLayers(dirPath)
|
|
580
|
+
]);
|
|
336
581
|
projectState.dirs.set(dirPath, entries);
|
|
337
582
|
} catch {
|
|
338
583
|
projectState.dirs.set(dirPath, []);
|
|
339
584
|
}
|
|
340
585
|
}
|
|
586
|
+
function showIgnoredFiles() {
|
|
587
|
+
return readStoredSetting(SETTINGS.showIgnoredFiles) === "true";
|
|
588
|
+
}
|
|
589
|
+
function setShowIgnoredFiles(show) {
|
|
590
|
+
setSetting(SETTINGS.showIgnoredFiles, show ? "true" : "");
|
|
591
|
+
}
|
|
341
592
|
async function loadProject() {
|
|
342
593
|
try {
|
|
343
594
|
const platform = getPlatform();
|
|
@@ -349,6 +600,7 @@ async function loadProject() {
|
|
|
349
600
|
refreshFormats();
|
|
350
601
|
loadFormats();
|
|
351
602
|
refreshExtensionUi(platform);
|
|
603
|
+
resetIgnoreCache();
|
|
352
604
|
setProjectState({
|
|
353
605
|
dirs: new Map,
|
|
354
606
|
expanded: new Set,
|
|
@@ -367,7 +619,11 @@ async function loadProject() {
|
|
|
367
619
|
if (info.isSiteProject) {
|
|
368
620
|
addRecentProject(requireProjectState().name, meta.root);
|
|
369
621
|
await autoSyncProjectOnOpen();
|
|
370
|
-
await ensureDependenciesInstalled()
|
|
622
|
+
if (await ensureDependenciesInstalled()) {
|
|
623
|
+
refreshFormats();
|
|
624
|
+
await loadFormats();
|
|
625
|
+
refreshExtensionUi(platform);
|
|
626
|
+
}
|
|
371
627
|
await loadDirectory(".");
|
|
372
628
|
await loadComponentRegistry();
|
|
373
629
|
await openLastSessionOrHome();
|
|
@@ -389,9 +645,11 @@ async function openProject({
|
|
|
389
645
|
document: { children: [], tagName: "div" },
|
|
390
646
|
id: "initial"
|
|
391
647
|
});
|
|
648
|
+
disarmPreviewOverlay();
|
|
392
649
|
refreshFormats();
|
|
393
650
|
loadFormats();
|
|
394
651
|
refreshExtensionUi(platform);
|
|
652
|
+
resetIgnoreCache();
|
|
395
653
|
setProjectState({
|
|
396
654
|
...projectState,
|
|
397
655
|
dirs: new Map,
|
|
@@ -405,7 +663,11 @@ async function openProject({
|
|
|
405
663
|
});
|
|
406
664
|
setWorkspaceProject(handle.root, config);
|
|
407
665
|
await autoSyncProjectOnOpen();
|
|
408
|
-
await ensureDependenciesInstalled()
|
|
666
|
+
if (await ensureDependenciesInstalled()) {
|
|
667
|
+
refreshFormats();
|
|
668
|
+
await loadFormats();
|
|
669
|
+
refreshExtensionUi(platform);
|
|
670
|
+
}
|
|
409
671
|
await loadDirectory(".");
|
|
410
672
|
await loadComponentRegistry();
|
|
411
673
|
const conventionalDirs = new Set([
|
|
@@ -551,6 +813,7 @@ function renderFilesTemplate({
|
|
|
551
813
|
<sp-button variant="accent" size="s" @click=${openProjectFn}>Open Project</sp-button>
|
|
552
814
|
</div>`;
|
|
553
815
|
}
|
|
816
|
+
const showingIgnored = showIgnoredFiles();
|
|
554
817
|
return html`
|
|
555
818
|
${projectState.isSiteProject ? html`
|
|
556
819
|
<div class="project-header">
|
|
@@ -573,6 +836,7 @@ function renderFilesTemplate({
|
|
|
573
836
|
label="Refresh"
|
|
574
837
|
@click=${async () => {
|
|
575
838
|
requireProjectState().dirs.clear();
|
|
839
|
+
resetIgnoreCache();
|
|
576
840
|
await loadDirectory(".");
|
|
577
841
|
for (const dir of requireProjectState().expanded) {
|
|
578
842
|
await loadDirectory(dir);
|
|
@@ -582,6 +846,17 @@ function renderFilesTemplate({
|
|
|
582
846
|
>
|
|
583
847
|
<sp-icon-refresh slot="icon"></sp-icon-refresh>
|
|
584
848
|
</sp-action-button>
|
|
849
|
+
<sp-action-button
|
|
850
|
+
size="xs"
|
|
851
|
+
label=${showingIgnored ? "Hide ignored files" : "Show ignored files"}
|
|
852
|
+
?selected=${showingIgnored}
|
|
853
|
+
@click=${() => {
|
|
854
|
+
setShowIgnoredFiles(!showingIgnored);
|
|
855
|
+
renderLeftPanel();
|
|
856
|
+
}}
|
|
857
|
+
>
|
|
858
|
+
${showingIgnored ? html`<sp-icon-visibility slot="icon"></sp-icon-visibility>` : html`<sp-icon-visibility-off slot="icon"></sp-icon-visibility-off>`}
|
|
859
|
+
</sp-action-button>
|
|
585
860
|
</sp-action-group>
|
|
586
861
|
<sp-search
|
|
587
862
|
size="s"
|
|
@@ -647,8 +922,9 @@ function collectFileRows(dirPath, depth, rows, ctx, i18n) {
|
|
|
647
922
|
}
|
|
648
923
|
return a.name.localeCompare(b.name);
|
|
649
924
|
});
|
|
925
|
+
const visible = showIgnoredFiles() ? sorted : sorted.filter((e) => !isIgnoredEntry(dirPath, e.path, e.type === "directory"));
|
|
650
926
|
const query = requireProjectState().searchQuery.toLowerCase();
|
|
651
|
-
const filtered = query ?
|
|
927
|
+
const filtered = query ? visible.filter((e) => e.type === "directory" || e.name.toLowerCase().includes(query)) : visible;
|
|
652
928
|
for (const [index, entry] of filtered.entries()) {
|
|
653
929
|
const isDir = entry.type === "directory";
|
|
654
930
|
const isExpanded = isDir && requireProjectState().expanded.has(entry.path);
|
|
@@ -1025,6 +1301,7 @@ async function moveFileEntry(oldPath, newPath, renderLeftPanel) {
|
|
|
1025
1301
|
markLocalMutation(oldPath, newPath);
|
|
1026
1302
|
try {
|
|
1027
1303
|
const report = await platform.renameFile(oldPath, newPath);
|
|
1304
|
+
invalidateUsages();
|
|
1028
1305
|
for (const [id] of workspace.tabs.entries()) {
|
|
1029
1306
|
if (id === oldPath) {
|
|
1030
1307
|
renameTab(oldPath, newPath, newPath);
|
|
@@ -1044,7 +1321,7 @@ async function moveFileEntry(oldPath, newPath, renderLeftPanel) {
|
|
|
1044
1321
|
}
|
|
1045
1322
|
reloadRewrittenTabs(report, newPath);
|
|
1046
1323
|
renderLeftPanel();
|
|
1047
|
-
|
|
1324
|
+
notifyMoveOutcome(`Moved to ${newPath}`, report, newPath);
|
|
1048
1325
|
} catch (error) {
|
|
1049
1326
|
notify.error(`Could not move ${oldPath}.`, {
|
|
1050
1327
|
detail: errorMessage(error),
|
|
@@ -1068,13 +1345,18 @@ function dismissFileContextMenu() {
|
|
|
1068
1345
|
function fileRowFacts(entry) {
|
|
1069
1346
|
const facts = {};
|
|
1070
1347
|
if (entry.type === "directory") {
|
|
1071
|
-
const collection =
|
|
1072
|
-
if (collection) {
|
|
1348
|
+
const collection = collectionForDirectory(entry.path);
|
|
1349
|
+
if (collection?.isSourceRoot === true && !collection.fileBacked) {
|
|
1073
1350
|
facts.name = collection.name;
|
|
1074
1351
|
}
|
|
1075
|
-
|
|
1352
|
+
return facts;
|
|
1353
|
+
}
|
|
1354
|
+
if (collectionOfPath(entry.path)) {
|
|
1076
1355
|
facts.path = entry.path;
|
|
1077
1356
|
}
|
|
1357
|
+
if (convertTargets(entry.path).length > 0) {
|
|
1358
|
+
facts.source = entry.path;
|
|
1359
|
+
}
|
|
1078
1360
|
return facts;
|
|
1079
1361
|
}
|
|
1080
1362
|
function placedFileItems(entry) {
|
|
@@ -1184,40 +1466,122 @@ function showFileContextMenu(e, entry, ctx) {
|
|
|
1184
1466
|
});
|
|
1185
1467
|
}
|
|
1186
1468
|
var BLANK_DOCUMENT = JSON.stringify({ children: [{ children: [], tagName: "p" }], tagName: "div" }, null, 2);
|
|
1469
|
+
var OTHER_FORMAT = "__other__";
|
|
1470
|
+
function slugify(input) {
|
|
1471
|
+
return input.trim().toLowerCase().replaceAll(/\s+/g, "-").replaceAll(/[^a-z\d-]/g, "");
|
|
1472
|
+
}
|
|
1473
|
+
function extensionOfName(name) {
|
|
1474
|
+
const dot = name.lastIndexOf(".");
|
|
1475
|
+
return dot > 0 ? name.slice(dot) : "";
|
|
1476
|
+
}
|
|
1477
|
+
function formatRowsFor(choice) {
|
|
1478
|
+
const rows = choice.kind === "locked" ? [
|
|
1479
|
+
{
|
|
1480
|
+
ext: choice.ext,
|
|
1481
|
+
label: `${formatByExtension(choice.ext)?.name ?? choice.ext} (${choice.ext})`
|
|
1482
|
+
}
|
|
1483
|
+
] : creationFormats(choice.docKind);
|
|
1484
|
+
return [
|
|
1485
|
+
...rows.map((row) => ({ label: row.label, value: row.ext })),
|
|
1486
|
+
{ dividerBefore: true, label: "Other…", value: OTHER_FORMAT }
|
|
1487
|
+
];
|
|
1488
|
+
}
|
|
1489
|
+
function initialPickFor(choice, rows, suggested) {
|
|
1490
|
+
const stated = choice.kind === "choose" ? choice.defaultExt : choice.ext;
|
|
1491
|
+
if (stated !== undefined && rows.some((row) => row.value === stated)) {
|
|
1492
|
+
return stated;
|
|
1493
|
+
}
|
|
1494
|
+
const fromPrefill = extensionOfName(suggested);
|
|
1495
|
+
if (fromPrefill !== "" && rows.some((row) => row.value === fromPrefill)) {
|
|
1496
|
+
return fromPrefill;
|
|
1497
|
+
}
|
|
1498
|
+
return rows[0]?.value ?? OTHER_FORMAT;
|
|
1499
|
+
}
|
|
1187
1500
|
async function createFileIn(request) {
|
|
1188
|
-
const { dir,
|
|
1501
|
+
const { dir, format: choice, source = "Files" } = request;
|
|
1189
1502
|
await loadFormats();
|
|
1190
1503
|
let taken = new Set;
|
|
1191
1504
|
try {
|
|
1192
1505
|
const listing = await getPlatform().listDirectory(dir);
|
|
1193
|
-
taken = new Set(listing.map((entry) => entry.name));
|
|
1506
|
+
taken = new Set(listing.map((entry) => entry.name.toLowerCase()));
|
|
1194
1507
|
} catch {}
|
|
1195
|
-
const
|
|
1508
|
+
const picker = choice !== undefined && choice.kind !== "fixed" ? choice : null;
|
|
1509
|
+
const rows = picker === null ? null : formatRowsFor(picker);
|
|
1510
|
+
const initialPick = picker === null || rows === null ? "" : initialPickFor(picker, rows, request.suggestedName ?? "");
|
|
1511
|
+
const fileNameFor = (input, picked2) => {
|
|
1512
|
+
const trimmed = input.trim();
|
|
1513
|
+
if (choice === undefined || picked2 === OTHER_FORMAT) {
|
|
1514
|
+
return trimmed;
|
|
1515
|
+
}
|
|
1516
|
+
if (choice.kind === "fixed") {
|
|
1517
|
+
const slug = slugify(trimmed);
|
|
1518
|
+
return slug === "" ? "" : `${slug}${choice.ext}`;
|
|
1519
|
+
}
|
|
1520
|
+
return trimmed.toLowerCase().endsWith(picked2.toLowerCase()) ? trimmed : `${trimmed}${picked2}`;
|
|
1521
|
+
};
|
|
1522
|
+
const known = knownDocumentExtensions();
|
|
1523
|
+
const validate = (value, picked2) => {
|
|
1524
|
+
const trimmed = value.trim();
|
|
1525
|
+
if (!trimmed) {
|
|
1526
|
+
return choice?.kind === "fixed" ? "Enter a name." : "Enter a file name.";
|
|
1527
|
+
}
|
|
1528
|
+
if (choice?.kind === "fixed" && slugify(trimmed) === "") {
|
|
1529
|
+
return "Enter at least one letter or number.";
|
|
1530
|
+
}
|
|
1531
|
+
const normalized = trimmed.replaceAll("\\", "/");
|
|
1532
|
+
if (normalized.startsWith("/") || normalized.endsWith("/")) {
|
|
1533
|
+
return "Enter a name, not a path that starts or ends with a slash.";
|
|
1534
|
+
}
|
|
1535
|
+
if (normalized.split("/").includes("..")) {
|
|
1536
|
+
return "A name cannot step outside the destination folder.";
|
|
1537
|
+
}
|
|
1538
|
+
if (normalized.includes("/") && choice !== undefined && picked2 !== OTHER_FORMAT) {
|
|
1539
|
+
return `Pick Other… to create a file in a subfolder of ${dir === "." ? "the project root" : dir}.`;
|
|
1540
|
+
}
|
|
1541
|
+
const typedExt = extensionOfName(normalized);
|
|
1542
|
+
if (picked2 !== "" && picked2 !== OTHER_FORMAT && typedExt !== "" && typedExt.toLowerCase() !== picked2.toLowerCase() && known.has(typedExt.toLowerCase())) {
|
|
1543
|
+
const label = rows?.find((row) => row.value === picked2)?.label ?? picked2;
|
|
1544
|
+
return `You picked ${label}; this name ends in ${typedExt}.`;
|
|
1545
|
+
}
|
|
1546
|
+
if (choice?.kind === "locked" && picked2 === OTHER_FORMAT) {
|
|
1547
|
+
const foreign = typedExt !== "" && typedExt.toLowerCase() !== choice.ext.toLowerCase();
|
|
1548
|
+
if (foreign && known.has(typedExt.toLowerCase())) {
|
|
1549
|
+
return choice.because;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
const candidate = fileNameFor(value, picked2);
|
|
1553
|
+
return taken.has(candidate.toLowerCase()) ? `${candidate} already exists in ${dir}/.` : "";
|
|
1554
|
+
};
|
|
1555
|
+
let picked = initialPick;
|
|
1196
1556
|
const entered = await showPromptDialog(request.title ?? "New File", {
|
|
1557
|
+
...rows === null ? {} : {
|
|
1558
|
+
choice: {
|
|
1559
|
+
initial: initialPick,
|
|
1560
|
+
label: "Format",
|
|
1561
|
+
onChange: (next) => {
|
|
1562
|
+
picked = next;
|
|
1563
|
+
},
|
|
1564
|
+
options: () => rows
|
|
1565
|
+
}
|
|
1566
|
+
},
|
|
1197
1567
|
confirmLabel: "Create",
|
|
1198
1568
|
message: dir === "." ? "Creating in the project root." : `Creating in ${dir}/`,
|
|
1199
1569
|
...request.suggestedName === undefined ? {} : { placeholder: request.suggestedName },
|
|
1200
|
-
select: "stem",
|
|
1201
|
-
validate
|
|
1202
|
-
if (!value.trim()) {
|
|
1203
|
-
return ext === undefined ? "Enter a file name." : "Enter a name.";
|
|
1204
|
-
}
|
|
1205
|
-
const candidate = fileNameFor(value);
|
|
1206
|
-
if (!candidate || candidate === ext) {
|
|
1207
|
-
return "Enter at least one letter or number.";
|
|
1208
|
-
}
|
|
1209
|
-
return taken.has(candidate) ? `${candidate} already exists in ${dir}/.` : "";
|
|
1210
|
-
},
|
|
1570
|
+
select: rows === null ? "stem" : "all",
|
|
1571
|
+
validate,
|
|
1211
1572
|
value: request.suggestedName ?? "untitled.json"
|
|
1212
1573
|
});
|
|
1213
1574
|
if (!entered) {
|
|
1214
1575
|
return null;
|
|
1215
1576
|
}
|
|
1216
|
-
const fileName = fileNameFor(entered);
|
|
1577
|
+
const fileName = fileNameFor(entered, picked);
|
|
1217
1578
|
const path = dir === "." ? fileName : `${dir}/${fileName}`;
|
|
1218
1579
|
markLocalMutation(path);
|
|
1219
1580
|
const format = formatForPath(fileName);
|
|
1220
|
-
const
|
|
1581
|
+
const suppliedBody = typeof request.content === "function" ? await request.content(extensionOfName(fileName), fileName) : request.content;
|
|
1582
|
+
const chosenAsDocument = choice !== undefined && picked !== OTHER_FORMAT;
|
|
1583
|
+
const content = suppliedBody ?? format?.studio?.newFileTemplate ?? (format ? "" : fileName.toLowerCase().endsWith(".json") ? chosenAsDocument ? BLANK_DOCUMENT : `{}
|
|
1584
|
+
` : "");
|
|
1221
1585
|
try {
|
|
1222
1586
|
await getPlatform().writeFile(path, content);
|
|
1223
1587
|
await loadDirectory(dir);
|
|
@@ -1233,7 +1597,32 @@ async function createFileIn(request) {
|
|
|
1233
1597
|
}
|
|
1234
1598
|
}
|
|
1235
1599
|
async function createNewFile(dirPath, renderLeftPanel) {
|
|
1236
|
-
|
|
1600
|
+
await loadFormats();
|
|
1601
|
+
const collection = collectionForDirectory(dirPath);
|
|
1602
|
+
if (collection?.unresolvedFormat != null) {
|
|
1603
|
+
notify.error(`New files in ${dirPath}/ cannot be constrained to the collection's format.`, {
|
|
1604
|
+
detail: `The "${collection.name}" content type declares format "${collection.unresolvedFormat}", ` + "which no installed extension provides. Install the extension that supplies it, or correct " + "the name in Project Settings › Content Types.",
|
|
1605
|
+
path: dirPath,
|
|
1606
|
+
source: "Files"
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
const constrained = collection?.ext != null && collection.unresolvedFormat === null;
|
|
1610
|
+
const seedable = constrained && collection.isSourceRoot && collection.def.schema !== undefined;
|
|
1611
|
+
let created;
|
|
1612
|
+
if (seedable) {
|
|
1613
|
+
const { createEntry } = await import("./entry-commands-6z6bhxg0.js");
|
|
1614
|
+
created = await createEntry(collection.name, { dir: dirPath });
|
|
1615
|
+
} else {
|
|
1616
|
+
created = await createFileIn({
|
|
1617
|
+
dir: dirPath,
|
|
1618
|
+
format: constrained && collection.ext !== null ? {
|
|
1619
|
+
because: `${collection.name} entries are ${collection.ext} files, so a document of another format here would not be one.`,
|
|
1620
|
+
ext: collection.ext,
|
|
1621
|
+
kind: "locked"
|
|
1622
|
+
} : { defaultExt: ".json", kind: "choose" },
|
|
1623
|
+
suggestedName: "untitled"
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1237
1626
|
if (created !== null) {
|
|
1238
1627
|
renderLeftPanel();
|
|
1239
1628
|
}
|
|
@@ -1259,6 +1648,20 @@ function renameStatus(newName, report) {
|
|
|
1259
1648
|
}
|
|
1260
1649
|
return `Renamed to ${newName}`;
|
|
1261
1650
|
}
|
|
1651
|
+
function notifyMoveOutcome(headline, report, path) {
|
|
1652
|
+
const stuck = report.errors ?? [];
|
|
1653
|
+
if (stuck.length === 0) {
|
|
1654
|
+
notify.success(headline);
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
const files = stuck.length === 1 ? "1 file" : `${stuck.length} files`;
|
|
1658
|
+
notify.warn(`${headline} — references in ${files} could not be updated`, {
|
|
1659
|
+
detail: stuck.map((e) => `${e.path}: ${e.error}`).join(`
|
|
1660
|
+
`),
|
|
1661
|
+
path,
|
|
1662
|
+
source: "Files"
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1262
1665
|
function reloadRewrittenTabs(report, skipPath) {
|
|
1263
1666
|
for (const f of report.references?.files ?? []) {
|
|
1264
1667
|
if (f.path !== skipPath && workspace.tabs.has(f.path)) {
|
|
@@ -1266,29 +1669,40 @@ function reloadRewrittenTabs(report, skipPath) {
|
|
|
1266
1669
|
}
|
|
1267
1670
|
}
|
|
1268
1671
|
}
|
|
1672
|
+
function parentDirOf(path) {
|
|
1673
|
+
const normalized = path.replaceAll("\\", "/");
|
|
1674
|
+
return normalized.includes("/") ? normalized.slice(0, normalized.lastIndexOf("/")) : ".";
|
|
1675
|
+
}
|
|
1676
|
+
async function settleRename(from, to, report) {
|
|
1677
|
+
invalidateUsages();
|
|
1678
|
+
const fromDir = parentDirOf(from);
|
|
1679
|
+
const toDir = parentDirOf(to);
|
|
1680
|
+
await loadDirectory(fromDir);
|
|
1681
|
+
if (toDir !== fromDir) {
|
|
1682
|
+
await loadDirectory(toDir);
|
|
1683
|
+
}
|
|
1684
|
+
if (requireProjectState().selectedPath === from) {
|
|
1685
|
+
requireProjectState().selectedPath = to;
|
|
1686
|
+
}
|
|
1687
|
+
reloadRewrittenTabs(report, to);
|
|
1688
|
+
}
|
|
1269
1689
|
async function renameFile(entry, renderLeftPanel) {
|
|
1270
1690
|
const newName = await showRenameFileDialog(entry.name, entry.path);
|
|
1271
1691
|
if (!newName || newName === entry.name) {
|
|
1272
1692
|
return;
|
|
1273
1693
|
}
|
|
1274
|
-
const
|
|
1275
|
-
const parentDirPath = entryPath.includes("/") ? entryPath.slice(0, entryPath.lastIndexOf("/")) : ".";
|
|
1694
|
+
const parentDirPath = parentDirOf(entry.path);
|
|
1276
1695
|
const newPath = parentDirPath === "." ? newName : `${parentDirPath}/${newName}`;
|
|
1277
1696
|
markLocalMutation(entry.path, newPath);
|
|
1278
1697
|
try {
|
|
1279
1698
|
const platform = getPlatform();
|
|
1280
1699
|
const report = await platform.renameFile(entry.path, newPath);
|
|
1281
|
-
invalidateUsages();
|
|
1282
|
-
await loadDirectory(parentDirPath);
|
|
1283
|
-
if (requireProjectState().selectedPath === entry.path) {
|
|
1284
|
-
requireProjectState().selectedPath = newPath;
|
|
1285
|
-
}
|
|
1286
1700
|
if (workspace.tabs.has(entry.path)) {
|
|
1287
1701
|
renameTab(entry.path, newPath, newPath);
|
|
1288
1702
|
}
|
|
1289
|
-
|
|
1703
|
+
await settleRename(entry.path, newPath, report);
|
|
1290
1704
|
renderLeftPanel();
|
|
1291
|
-
|
|
1705
|
+
notifyMoveOutcome(renameStatus(newName, report), report, newPath);
|
|
1292
1706
|
} catch (error) {
|
|
1293
1707
|
notify.error(`Could not rename ${entry.name}.`, {
|
|
1294
1708
|
detail: errorMessage(error),
|
|
@@ -1345,6 +1759,18 @@ async function openFileInTab(path, opts = {}) {
|
|
|
1345
1759
|
return;
|
|
1346
1760
|
}
|
|
1347
1761
|
}
|
|
1762
|
+
if (isViewableMedia(path)) {
|
|
1763
|
+
openMediaTab(path);
|
|
1764
|
+
if (follows) {
|
|
1765
|
+
requireProjectState().selectedPath = path;
|
|
1766
|
+
}
|
|
1767
|
+
trackRecentFile({
|
|
1768
|
+
name: path.split("/").pop() || path,
|
|
1769
|
+
path,
|
|
1770
|
+
root: requireProjectState().projectRoot
|
|
1771
|
+
});
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1348
1774
|
if (path.toLowerCase().endsWith(".csv")) {
|
|
1349
1775
|
try {
|
|
1350
1776
|
await openCsvGridTab(path);
|
|
@@ -1367,6 +1793,10 @@ async function openFileInTab(path, opts = {}) {
|
|
|
1367
1793
|
try {
|
|
1368
1794
|
const content = await platform.readFile(path);
|
|
1369
1795
|
if (!content) {
|
|
1796
|
+
await loadFormats();
|
|
1797
|
+
if (!formatForPath(path) && !path.toLowerCase().endsWith(".json")) {
|
|
1798
|
+
throw noFormatError(path);
|
|
1799
|
+
}
|
|
1370
1800
|
return;
|
|
1371
1801
|
}
|
|
1372
1802
|
await loadFormats();
|
|
@@ -1467,7 +1897,7 @@ function registerFilesPanel() {
|
|
|
1467
1897
|
});
|
|
1468
1898
|
}
|
|
1469
1899
|
|
|
1470
|
-
export { ensureDependenciesInstalled,
|
|
1900
|
+
export { ensureDependenciesInstalled, mediaPaneMounted, detachMediaPane, renderMediaMode, convertTargets, convertTargetExtensions, hydrateRecentProjects, getRecentProjects, addRecentProject, removeRecentProject, clearRecentProjects, getRecentFiles, trackRecentFile, loadDirectory, showIgnoredFiles, setShowIgnoredFiles, loadProject, openProject, initProjectRepo, findHomePage, openHomePage, openLastSessionOrHome, renderFilesTemplate, FILE_ROW_HEIGHT, isFileDrag, uploadFilesToDir, pickAndUploadTo, registerFileTreeDnD, OTHER_FORMAT, createFileIn, notifyMoveOutcome, parentDirOf, settleRename, openFileInTab, openFileInPane, reloadCleanTab, reloadFileInTab, registerFilesPanel };
|
|
1471
1901
|
|
|
1472
|
-
//# debugId=
|
|
1473
|
-
//# sourceMappingURL=studio-
|
|
1902
|
+
//# debugId=3C1BF8083E5F1C8564756E2164756E21
|
|
1903
|
+
//# sourceMappingURL=studio-5rywrkxz.js.map
|