@tutti-os/workspace-file-manager 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +51 -0
- package/dist/chunk-2AG4ND4D.js +208 -0
- package/dist/chunk-2AG4ND4D.js.map +1 -0
- package/dist/chunk-IMOFXYBB.js +1759 -0
- package/dist/chunk-IMOFXYBB.js.map +1 -0
- package/dist/fileKinds-MpDaZhVB.d.ts +392 -0
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/index.js +11 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index-6L2biT8M.d.ts +90 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +2802 -0
- package/dist/index.js.map +1 -0
- package/dist/services/index.d.ts +8 -0
- package/dist/services/index.js +41 -0
- package/dist/services/index.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,1759 @@
|
|
|
1
|
+
// src/services/internal/workspaceFileManagerStore.ts
|
|
2
|
+
import { proxy } from "valtio";
|
|
3
|
+
|
|
4
|
+
// src/services/workspaceFileManagerTypes.ts
|
|
5
|
+
var workspaceFileManagerLogicalRoot = "/";
|
|
6
|
+
var workspaceFileManagerPersistedStateSchemaVersion = 2;
|
|
7
|
+
|
|
8
|
+
// src/services/internal/model/fileKinds.ts
|
|
9
|
+
import {
|
|
10
|
+
classifyWorkspaceFilePreviewKind as classifySharedWorkspaceFilePreviewKind,
|
|
11
|
+
resolveWorkspaceFileActivationTarget as resolveSharedWorkspaceFileActivationTarget,
|
|
12
|
+
resolveWorkspaceFilePreviewReadiness as resolveSharedWorkspaceFilePreviewReadiness
|
|
13
|
+
} from "@tutti-os/workspace-file-preview";
|
|
14
|
+
import {
|
|
15
|
+
copyWorkspaceFilePreviewBytes,
|
|
16
|
+
createWorkspaceFilePreviewLoadedState,
|
|
17
|
+
decodeWorkspaceTextFile,
|
|
18
|
+
formatWorkspacePreviewByteLimit,
|
|
19
|
+
isWorkspaceFileBrowserOpenable,
|
|
20
|
+
isWorkspacePreviewFileTooLarge,
|
|
21
|
+
isWorkspaceTextFileTooLarge,
|
|
22
|
+
looksLikeBinaryText,
|
|
23
|
+
normalizeWorkspaceFilePreviewBytes,
|
|
24
|
+
resolveWorkspaceFileExtension,
|
|
25
|
+
resolveWorkspaceFileVisualKind,
|
|
26
|
+
resolveWorkspaceImageMimeType,
|
|
27
|
+
workspaceFilePreviewMaxBytes,
|
|
28
|
+
workspaceFileTextMaxBytes
|
|
29
|
+
} from "@tutti-os/workspace-file-preview";
|
|
30
|
+
function classifyWorkspaceFilePreviewKind(entry) {
|
|
31
|
+
return classifySharedWorkspaceFilePreviewKind(entry);
|
|
32
|
+
}
|
|
33
|
+
function resolveWorkspaceFileActivationTarget(entry) {
|
|
34
|
+
const target = resolveSharedWorkspaceFileActivationTarget(entry);
|
|
35
|
+
if (!target) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
fileKind: target.fileKind,
|
|
40
|
+
mtimeMs: target.mtimeMs ?? null,
|
|
41
|
+
name: target.name,
|
|
42
|
+
path: target.path,
|
|
43
|
+
sizeBytes: target.sizeBytes ?? null
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function resolveWorkspaceFilePreviewReadiness(entry) {
|
|
47
|
+
const readiness = resolveSharedWorkspaceFilePreviewReadiness(entry);
|
|
48
|
+
if (readiness.status !== "ready") {
|
|
49
|
+
return readiness;
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
entry,
|
|
53
|
+
status: "ready",
|
|
54
|
+
target: {
|
|
55
|
+
fileKind: readiness.target.fileKind,
|
|
56
|
+
mtimeMs: readiness.target.mtimeMs ?? null,
|
|
57
|
+
name: readiness.target.name,
|
|
58
|
+
path: readiness.target.path,
|
|
59
|
+
sizeBytes: readiness.target.sizeBytes ?? null
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/services/internal/model/formatters.ts
|
|
65
|
+
import { formatNextopShortDateTime } from "@tutti-os/ui-system/date-format";
|
|
66
|
+
function formatWorkspaceFileBytes(sizeBytes) {
|
|
67
|
+
if (sizeBytes === null || !Number.isFinite(sizeBytes)) {
|
|
68
|
+
return "--";
|
|
69
|
+
}
|
|
70
|
+
if (sizeBytes < 1024) {
|
|
71
|
+
return `${sizeBytes} B`;
|
|
72
|
+
}
|
|
73
|
+
const units = ["KB", "MB", "GB", "TB"];
|
|
74
|
+
let value = sizeBytes / 1024;
|
|
75
|
+
let unitIndex = 0;
|
|
76
|
+
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
77
|
+
value /= 1024;
|
|
78
|
+
unitIndex += 1;
|
|
79
|
+
}
|
|
80
|
+
return `${value >= 10 ? value.toFixed(1) : value.toFixed(2)} ${units[unitIndex]}`;
|
|
81
|
+
}
|
|
82
|
+
function formatWorkspaceFileModifiedTime(mtimeMs, locale) {
|
|
83
|
+
if (mtimeMs === null || !Number.isFinite(mtimeMs) || mtimeMs <= 0) {
|
|
84
|
+
return "--";
|
|
85
|
+
}
|
|
86
|
+
return formatNextopShortDateTime(mtimeMs, locale);
|
|
87
|
+
}
|
|
88
|
+
function splitWorkspaceFileName(name) {
|
|
89
|
+
const extensionIndex = name.lastIndexOf(".");
|
|
90
|
+
const hasExtension = extensionIndex > 0 && extensionIndex < name.length - 1;
|
|
91
|
+
if (hasExtension) {
|
|
92
|
+
return {
|
|
93
|
+
end: name.slice(extensionIndex),
|
|
94
|
+
start: name.slice(0, extensionIndex)
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (name.length <= 24) {
|
|
98
|
+
return { end: "", start: name };
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
end: name.slice(-10),
|
|
102
|
+
start: name.slice(0, -10)
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/services/internal/model/paths.ts
|
|
107
|
+
function normalizeWorkspaceFilePath(value, rootPath) {
|
|
108
|
+
const root = normalizeWorkspaceFileAbsolutePath(rootPath);
|
|
109
|
+
const raw = String(value ?? "").trim().replaceAll("\\", "/");
|
|
110
|
+
if (!raw) {
|
|
111
|
+
return root;
|
|
112
|
+
}
|
|
113
|
+
if (!raw.startsWith("/") && root) {
|
|
114
|
+
return normalizeWorkspaceFileAbsolutePath(`${root}/${raw}`);
|
|
115
|
+
}
|
|
116
|
+
return normalizeWorkspaceFileAbsolutePath(raw);
|
|
117
|
+
}
|
|
118
|
+
function normalizeWorkspaceFileAbsolutePath(value) {
|
|
119
|
+
const raw = String(value ?? "").trim().replaceAll("\\", "/");
|
|
120
|
+
if (!raw) {
|
|
121
|
+
return workspaceFileManagerLogicalRoot;
|
|
122
|
+
}
|
|
123
|
+
const segments = raw.split("/").filter(Boolean).filter((segment) => segment !== ".");
|
|
124
|
+
const result = [];
|
|
125
|
+
for (const segment of segments) {
|
|
126
|
+
if (segment === "..") {
|
|
127
|
+
result.pop();
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
result.push(segment);
|
|
131
|
+
}
|
|
132
|
+
if (result.length === 0) {
|
|
133
|
+
return workspaceFileManagerLogicalRoot;
|
|
134
|
+
}
|
|
135
|
+
return `/${result.join("/")}`;
|
|
136
|
+
}
|
|
137
|
+
function workspaceFileName(path) {
|
|
138
|
+
return normalizeWorkspaceFilePath(path).split("/").filter(Boolean).at(-1) ?? "workspace";
|
|
139
|
+
}
|
|
140
|
+
function workspaceFileDirectory(path, rootPath) {
|
|
141
|
+
const root = normalizeWorkspaceFilePath(rootPath);
|
|
142
|
+
const normalized = normalizeWorkspaceFilePath(path, root);
|
|
143
|
+
if (normalized === root) {
|
|
144
|
+
return root;
|
|
145
|
+
}
|
|
146
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
147
|
+
parts.pop();
|
|
148
|
+
if (parts.length === 0) {
|
|
149
|
+
return root;
|
|
150
|
+
}
|
|
151
|
+
const directory = `/${parts.join("/")}`;
|
|
152
|
+
if (root !== workspaceFileManagerLogicalRoot) {
|
|
153
|
+
return isWorkspaceFilePathWithinRoot(directory, root) ? directory : root;
|
|
154
|
+
}
|
|
155
|
+
return directory;
|
|
156
|
+
}
|
|
157
|
+
function workspaceFilePathHasHiddenSegment(path) {
|
|
158
|
+
return normalizeWorkspaceFilePath(path).split("/").filter(Boolean).some((segment) => segment.startsWith("."));
|
|
159
|
+
}
|
|
160
|
+
function sortWorkspaceEntries(entries) {
|
|
161
|
+
return [...entries].sort((left, right) => {
|
|
162
|
+
if (left.kind !== right.kind) {
|
|
163
|
+
return left.kind === "directory" ? -1 : 1;
|
|
164
|
+
}
|
|
165
|
+
return left.name.localeCompare(right.name, void 0, {
|
|
166
|
+
sensitivity: "base"
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
function buildWorkspaceFileBreadcrumbs(path, rootLabel, rootPath) {
|
|
171
|
+
const root = normalizeWorkspaceFilePath(rootPath);
|
|
172
|
+
const current = normalizeWorkspaceFilePath(path, root);
|
|
173
|
+
const relative = current === root ? "" : current.slice(root.length).replace(/^\//, "");
|
|
174
|
+
const breadcrumbs = [
|
|
175
|
+
{ label: rootLabel, path: root }
|
|
176
|
+
];
|
|
177
|
+
let cursor = root;
|
|
178
|
+
for (const segment of relative.split("/").filter(Boolean)) {
|
|
179
|
+
cursor = `${cursor}/${segment}`.replace(/\/+/g, "/");
|
|
180
|
+
breadcrumbs.push({ label: segment, path: cursor });
|
|
181
|
+
}
|
|
182
|
+
return breadcrumbs;
|
|
183
|
+
}
|
|
184
|
+
function isWorkspaceFilePathWithinRoot(path, rootPath) {
|
|
185
|
+
const root = normalizeWorkspaceFilePath(rootPath);
|
|
186
|
+
const normalized = normalizeWorkspaceFilePath(path, root);
|
|
187
|
+
return root === workspaceFileManagerLogicalRoot || normalized === root || normalized.startsWith(`${root}/`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// src/services/internal/model/validation.ts
|
|
191
|
+
function validateWorkspaceFileEntryName(name) {
|
|
192
|
+
const trimmed = name.trim();
|
|
193
|
+
if (!trimmed) {
|
|
194
|
+
return "required";
|
|
195
|
+
}
|
|
196
|
+
if (trimmed.includes("/") || trimmed.includes("\\") || trimmed === "." || trimmed === "..") {
|
|
197
|
+
return "invalid";
|
|
198
|
+
}
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/services/internal/workspaceFileManagerStore.ts
|
|
203
|
+
function createWorkspaceFileManagerStore(input) {
|
|
204
|
+
const persistedState = normalizeWorkspaceFileManagerPersistedState(
|
|
205
|
+
input.persistedState
|
|
206
|
+
);
|
|
207
|
+
const initialDirectoryPath = normalizeWorkspaceFilePath(
|
|
208
|
+
input.initialDirectoryPath
|
|
209
|
+
);
|
|
210
|
+
return proxy({
|
|
211
|
+
busyAction: null,
|
|
212
|
+
capabilities: input.capabilities,
|
|
213
|
+
contextMenu: null,
|
|
214
|
+
contextMenuEntryPath: null,
|
|
215
|
+
createDialog: null,
|
|
216
|
+
currentDirectoryPath: persistedState?.currentDirectoryPath ?? initialDirectoryPath ?? workspaceFileManagerLogicalRoot,
|
|
217
|
+
deleteDialog: null,
|
|
218
|
+
inlineRenameEntryPath: null,
|
|
219
|
+
inlineRenameValidation: null,
|
|
220
|
+
dragDepth: 0,
|
|
221
|
+
entries: [],
|
|
222
|
+
error: null,
|
|
223
|
+
isLoading: false,
|
|
224
|
+
isMutating: false,
|
|
225
|
+
isSearching: false,
|
|
226
|
+
navigationBackStack: persistedState?.navigationBackStack ?? [],
|
|
227
|
+
navigationForwardStack: persistedState?.navigationForwardStack ?? [],
|
|
228
|
+
pendingDirectoryPath: null,
|
|
229
|
+
previewState: { status: "empty" },
|
|
230
|
+
root: workspaceFileManagerLogicalRoot,
|
|
231
|
+
searchEntries: [],
|
|
232
|
+
searchError: null,
|
|
233
|
+
searchQuery: "",
|
|
234
|
+
selectedPath: null,
|
|
235
|
+
unsupportedDialog: null,
|
|
236
|
+
importConflictDialog: null,
|
|
237
|
+
workspaceID: input.workspaceID
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
function getWorkspaceFileManagerPersistedState(state) {
|
|
241
|
+
return {
|
|
242
|
+
currentDirectoryPath: normalizeWorkspaceFilePath(
|
|
243
|
+
state.currentDirectoryPath,
|
|
244
|
+
state.root
|
|
245
|
+
),
|
|
246
|
+
navigationBackStack: normalizePersistedStack(
|
|
247
|
+
state.navigationBackStack,
|
|
248
|
+
state.root
|
|
249
|
+
),
|
|
250
|
+
navigationForwardStack: normalizePersistedStack(
|
|
251
|
+
state.navigationForwardStack,
|
|
252
|
+
state.root
|
|
253
|
+
),
|
|
254
|
+
schemaVersion: workspaceFileManagerPersistedStateSchemaVersion
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function normalizeWorkspaceFileManagerPersistedState(value) {
|
|
258
|
+
if (!isPersistedStateRecord(value) || value.schemaVersion !== workspaceFileManagerPersistedStateSchemaVersion || typeof value.currentDirectoryPath !== "string" || !isStringArray(value.navigationBackStack) || !isStringArray(value.navigationForwardStack)) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
currentDirectoryPath: normalizeWorkspaceFilePath(
|
|
263
|
+
value.currentDirectoryPath
|
|
264
|
+
),
|
|
265
|
+
navigationBackStack: normalizePersistedStack(value.navigationBackStack),
|
|
266
|
+
navigationForwardStack: normalizePersistedStack(
|
|
267
|
+
value.navigationForwardStack
|
|
268
|
+
),
|
|
269
|
+
schemaVersion: workspaceFileManagerPersistedStateSchemaVersion
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function isPersistedStateRecord(value) {
|
|
273
|
+
return typeof value === "object" && value !== null;
|
|
274
|
+
}
|
|
275
|
+
function isStringArray(value) {
|
|
276
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
277
|
+
}
|
|
278
|
+
function normalizePersistedStack(values, root) {
|
|
279
|
+
if (!Array.isArray(values)) {
|
|
280
|
+
return [];
|
|
281
|
+
}
|
|
282
|
+
return values.map(
|
|
283
|
+
(value) => typeof value === "string" ? normalizeWorkspaceFilePath(value, root) : null
|
|
284
|
+
).filter((value) => value !== null);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// src/services/internal/workspaceFileManagerSession.ts
|
|
288
|
+
import { subscribe } from "valtio/vanilla";
|
|
289
|
+
|
|
290
|
+
// src/services/internal/workspaceFileManagerActivationController.ts
|
|
291
|
+
var WorkspaceFileManagerActivationController = class {
|
|
292
|
+
copy;
|
|
293
|
+
host;
|
|
294
|
+
loadDirectory;
|
|
295
|
+
resolveErrorMessage;
|
|
296
|
+
store;
|
|
297
|
+
constructor(input) {
|
|
298
|
+
this.copy = input.copy;
|
|
299
|
+
this.host = input.host;
|
|
300
|
+
this.loadDirectory = input.loadDirectory;
|
|
301
|
+
this.resolveErrorMessage = input.resolveErrorMessage;
|
|
302
|
+
this.store = input.store;
|
|
303
|
+
}
|
|
304
|
+
async activateFile(request) {
|
|
305
|
+
const copy = this.copy();
|
|
306
|
+
if (!this.host.activateFile) {
|
|
307
|
+
return {
|
|
308
|
+
disposition: "unsupported",
|
|
309
|
+
message: copy.t("unsupportedViewBody", { name: request.entry.name }),
|
|
310
|
+
title: copy.t("unsupportedViewTitle")
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
try {
|
|
314
|
+
const result = await this.host.activateFile(
|
|
315
|
+
request,
|
|
316
|
+
this.store.workspaceID
|
|
317
|
+
);
|
|
318
|
+
if (result.disposition !== "fallback" || !result.actions) {
|
|
319
|
+
return result;
|
|
320
|
+
}
|
|
321
|
+
const fallbackActionKinds = new Set(
|
|
322
|
+
result.actions.map((action) => action.kind)
|
|
323
|
+
);
|
|
324
|
+
return {
|
|
325
|
+
...result,
|
|
326
|
+
actions: result.actions.map(
|
|
327
|
+
(action) => this.wrapFallbackAction(action, copy)
|
|
328
|
+
),
|
|
329
|
+
message: result.message ?? (fallbackActionKinds.has("download") ? copy.t("previewUnavailableDownloadBody", {
|
|
330
|
+
name: request.entry.name
|
|
331
|
+
}) : copy.t("previewUnavailableOpenBody", {
|
|
332
|
+
name: request.entry.name
|
|
333
|
+
})),
|
|
334
|
+
title: result.title ?? copy.t("previewUnavailableTitle")
|
|
335
|
+
};
|
|
336
|
+
} catch (error) {
|
|
337
|
+
return {
|
|
338
|
+
actions: [
|
|
339
|
+
{
|
|
340
|
+
kind: "open",
|
|
341
|
+
label: copy.t("retryLabel"),
|
|
342
|
+
onSelect: async () => this.activateFile(request)
|
|
343
|
+
}
|
|
344
|
+
],
|
|
345
|
+
disposition: "unsupported",
|
|
346
|
+
message: this.resolveErrorMessage(error),
|
|
347
|
+
title: copy.t("openFailedTitle")
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
async handleFallbackAction(action) {
|
|
352
|
+
if (action.kind === "none" || !action.onSelect) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
const entryPath = this.store.unsupportedDialog?.entryPath ?? null;
|
|
356
|
+
const entry = entryPath ? this.findEntry(entryPath) : null;
|
|
357
|
+
this.store.busyAction = "view";
|
|
358
|
+
try {
|
|
359
|
+
const result = await action.onSelect();
|
|
360
|
+
if (!entry) {
|
|
361
|
+
this.store.unsupportedDialog = null;
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
this.applyActivationResult(result, entry);
|
|
365
|
+
} finally {
|
|
366
|
+
this.store.busyAction = null;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
async openEntry(entry) {
|
|
370
|
+
this.store.contextMenu = null;
|
|
371
|
+
if (entry.kind === "directory") {
|
|
372
|
+
this.store.pendingDirectoryPath = entry.path;
|
|
373
|
+
try {
|
|
374
|
+
await this.loadDirectory(entry.path);
|
|
375
|
+
} finally {
|
|
376
|
+
if (this.store.pendingDirectoryPath === entry.path) {
|
|
377
|
+
this.store.pendingDirectoryPath = null;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
this.store.busyAction = "view";
|
|
383
|
+
try {
|
|
384
|
+
const result = await this.activateFile({
|
|
385
|
+
entry,
|
|
386
|
+
target: resolveWorkspaceFileActivationTarget(entry)
|
|
387
|
+
});
|
|
388
|
+
this.applyActivationResult(result, entry);
|
|
389
|
+
} finally {
|
|
390
|
+
this.store.busyAction = null;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
applyActivationResult(result, entry) {
|
|
394
|
+
if (!result || result.disposition === "handled") {
|
|
395
|
+
this.store.unsupportedDialog = null;
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
this.store.importConflictDialog = null;
|
|
399
|
+
this.store.unsupportedDialog = {
|
|
400
|
+
actions: result.actions ?? null,
|
|
401
|
+
entryPath: entry.path,
|
|
402
|
+
kind: "view",
|
|
403
|
+
message: result.message,
|
|
404
|
+
title: result.title
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
findEntry(entryPath) {
|
|
408
|
+
return this.store.entries.find((entry) => entry.path === entryPath) ?? null;
|
|
409
|
+
}
|
|
410
|
+
wrapFallbackAction(action, copy) {
|
|
411
|
+
if (action.kind === "none" || !action.onSelect) {
|
|
412
|
+
return action;
|
|
413
|
+
}
|
|
414
|
+
return {
|
|
415
|
+
...action,
|
|
416
|
+
onSelect: async () => {
|
|
417
|
+
try {
|
|
418
|
+
return await action.onSelect();
|
|
419
|
+
} catch (error) {
|
|
420
|
+
return {
|
|
421
|
+
actions: [
|
|
422
|
+
this.wrapFallbackAction(
|
|
423
|
+
{
|
|
424
|
+
...action,
|
|
425
|
+
label: copy.t("retryLabel")
|
|
426
|
+
},
|
|
427
|
+
copy
|
|
428
|
+
)
|
|
429
|
+
],
|
|
430
|
+
disposition: "unsupported",
|
|
431
|
+
message: this.resolveErrorMessage(error),
|
|
432
|
+
title: action.kind === "download" ? copy.t("downloadFailedTitle") : copy.t("openFailedTitle")
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
// src/services/internal/workspaceFileManagerMutationController.ts
|
|
441
|
+
var WorkspaceFileManagerMutationController = class {
|
|
442
|
+
host;
|
|
443
|
+
onErrorMessage;
|
|
444
|
+
refresh;
|
|
445
|
+
resolveErrorMessage;
|
|
446
|
+
store;
|
|
447
|
+
constructor(input) {
|
|
448
|
+
this.host = input.host;
|
|
449
|
+
this.onErrorMessage = input.onErrorMessage;
|
|
450
|
+
this.refresh = input.refresh;
|
|
451
|
+
this.resolveErrorMessage = input.resolveErrorMessage;
|
|
452
|
+
this.store = input.store;
|
|
453
|
+
}
|
|
454
|
+
async createDirectory(path) {
|
|
455
|
+
if (!this.host.createDirectory) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
await this.mutate(
|
|
459
|
+
"create",
|
|
460
|
+
() => this.host.createDirectory?.({
|
|
461
|
+
path: normalizeWorkspaceFilePath(path, this.store.root),
|
|
462
|
+
workspaceID: this.store.workspaceID
|
|
463
|
+
}) ?? Promise.resolve()
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
async createFile(path) {
|
|
467
|
+
if (!this.host.createFile) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
await this.mutate(
|
|
471
|
+
"create",
|
|
472
|
+
() => this.host.createFile?.({
|
|
473
|
+
path: normalizeWorkspaceFilePath(path, this.store.root),
|
|
474
|
+
workspaceID: this.store.workspaceID
|
|
475
|
+
}) ?? Promise.resolve()
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
async deleteSelected() {
|
|
479
|
+
if (!this.host.deleteEntry || !this.store.selectedPath) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const entry = this.store.entries.find(
|
|
483
|
+
(candidate) => candidate.path === this.store.selectedPath
|
|
484
|
+
);
|
|
485
|
+
await this.mutate(
|
|
486
|
+
"delete",
|
|
487
|
+
() => this.host.deleteEntry?.({
|
|
488
|
+
kind: entryKindForDelete(entry?.kind),
|
|
489
|
+
path: this.store.selectedPath ?? "",
|
|
490
|
+
workspaceID: this.store.workspaceID
|
|
491
|
+
}) ?? Promise.resolve()
|
|
492
|
+
);
|
|
493
|
+
this.store.selectedPath = null;
|
|
494
|
+
}
|
|
495
|
+
async moveEntry(entry, targetDirectoryPath) {
|
|
496
|
+
if (!this.host.moveEntry || entry.kind === "unknown") {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
const entryKind = entry.kind;
|
|
500
|
+
const normalizedTargetDirectoryPath = normalizeWorkspaceFilePath(
|
|
501
|
+
targetDirectoryPath,
|
|
502
|
+
this.store.root
|
|
503
|
+
);
|
|
504
|
+
if (entry.path === normalizedTargetDirectoryPath) {
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
if (entry.kind === "directory" && normalizedTargetDirectoryPath.startsWith(`${entry.path}/`)) {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
if (workspaceFileDirectory(entry.path, this.store.root) === normalizedTargetDirectoryPath) {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
let movedPath = null;
|
|
514
|
+
await this.mutate("move", async () => {
|
|
515
|
+
const movedEntry = await this.host.moveEntry?.({
|
|
516
|
+
kind: entryKind,
|
|
517
|
+
path: entry.path,
|
|
518
|
+
targetDirectoryPath: normalizedTargetDirectoryPath,
|
|
519
|
+
workspaceID: this.store.workspaceID
|
|
520
|
+
}) ?? null;
|
|
521
|
+
movedPath = movedEntry?.path ?? null;
|
|
522
|
+
});
|
|
523
|
+
this.store.selectedPath = movedPath;
|
|
524
|
+
}
|
|
525
|
+
async renameEntry(entry, newName) {
|
|
526
|
+
if (!this.host.renameEntry) {
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
let renamedPath = null;
|
|
530
|
+
await this.mutate("rename", async () => {
|
|
531
|
+
const renamedEntry = await this.host.renameEntry?.({
|
|
532
|
+
newName: newName.trim(),
|
|
533
|
+
path: entry.path,
|
|
534
|
+
workspaceID: this.store.workspaceID
|
|
535
|
+
}) ?? null;
|
|
536
|
+
renamedPath = renamedEntry?.path ?? null;
|
|
537
|
+
});
|
|
538
|
+
this.store.selectedPath = renamedPath;
|
|
539
|
+
}
|
|
540
|
+
async mutate(actionKind, operation) {
|
|
541
|
+
this.store.isMutating = true;
|
|
542
|
+
this.store.error = null;
|
|
543
|
+
try {
|
|
544
|
+
await operation();
|
|
545
|
+
await this.refresh();
|
|
546
|
+
} catch (error) {
|
|
547
|
+
const message = this.resolveErrorMessage(error);
|
|
548
|
+
const handled = this.onErrorMessage?.({
|
|
549
|
+
actionKind,
|
|
550
|
+
error,
|
|
551
|
+
message
|
|
552
|
+
});
|
|
553
|
+
if (!handled) {
|
|
554
|
+
this.store.error = message;
|
|
555
|
+
}
|
|
556
|
+
} finally {
|
|
557
|
+
this.store.isMutating = false;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
function entryKindForDelete(kind) {
|
|
562
|
+
if (kind === "file" || kind === "directory") {
|
|
563
|
+
return kind;
|
|
564
|
+
}
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// src/services/internal/workspaceFileManagerNavigationController.ts
|
|
569
|
+
var WorkspaceFileManagerNavigationController = class {
|
|
570
|
+
host;
|
|
571
|
+
resolveErrorMessage;
|
|
572
|
+
store;
|
|
573
|
+
requestSeq = 0;
|
|
574
|
+
constructor(input) {
|
|
575
|
+
this.host = input.host;
|
|
576
|
+
this.resolveErrorMessage = input.resolveErrorMessage;
|
|
577
|
+
this.store = input.store;
|
|
578
|
+
}
|
|
579
|
+
async goBack() {
|
|
580
|
+
const previous = this.store.navigationBackStack.pop();
|
|
581
|
+
if (!previous) {
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
this.store.navigationForwardStack.push(this.store.currentDirectoryPath);
|
|
585
|
+
await this.replaceDirectory(previous);
|
|
586
|
+
}
|
|
587
|
+
async goForward() {
|
|
588
|
+
const next = this.store.navigationForwardStack.pop();
|
|
589
|
+
if (!next) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
this.store.navigationBackStack.push(this.store.currentDirectoryPath);
|
|
593
|
+
await this.replaceDirectory(next);
|
|
594
|
+
}
|
|
595
|
+
async loadDirectory(path = this.store.currentDirectoryPath) {
|
|
596
|
+
const normalizedPath = normalizeWorkspaceFilePath(path, this.store.root);
|
|
597
|
+
const requestID = ++this.requestSeq;
|
|
598
|
+
this.store.isLoading = true;
|
|
599
|
+
this.store.error = null;
|
|
600
|
+
try {
|
|
601
|
+
const listing = await this.host.listDirectory({
|
|
602
|
+
includeHidden: workspaceFilePathHasHiddenSegment(normalizedPath),
|
|
603
|
+
path: this.resolveRequestPath(normalizedPath),
|
|
604
|
+
workspaceID: this.store.workspaceID
|
|
605
|
+
});
|
|
606
|
+
if (requestID !== this.requestSeq) {
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
const previousDirectoryPath = this.store.currentDirectoryPath;
|
|
610
|
+
if (previousDirectoryPath !== listing.directoryPath && previousDirectoryPath !== "/") {
|
|
611
|
+
this.store.navigationBackStack.push(this.store.currentDirectoryPath);
|
|
612
|
+
this.store.navigationForwardStack = [];
|
|
613
|
+
}
|
|
614
|
+
this.store.root = normalizeWorkspaceFilePath(listing.root);
|
|
615
|
+
this.store.currentDirectoryPath = listing.directoryPath;
|
|
616
|
+
this.store.entries = sortWorkspaceEntries(listing.entries);
|
|
617
|
+
this.store.selectedPath = null;
|
|
618
|
+
} catch (error) {
|
|
619
|
+
if (requestID === this.requestSeq) {
|
|
620
|
+
this.store.error = this.resolveErrorMessage(error);
|
|
621
|
+
}
|
|
622
|
+
} finally {
|
|
623
|
+
if (requestID === this.requestSeq) {
|
|
624
|
+
this.store.isLoading = false;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
async refresh() {
|
|
629
|
+
await this.loadDirectory(this.store.currentDirectoryPath);
|
|
630
|
+
}
|
|
631
|
+
async revealPath(path) {
|
|
632
|
+
const normalizedPath = normalizeWorkspaceFilePath(path, this.store.root);
|
|
633
|
+
const directoryPath = workspaceFileDirectory(
|
|
634
|
+
normalizedPath,
|
|
635
|
+
this.store.root
|
|
636
|
+
);
|
|
637
|
+
const requestID = ++this.requestSeq;
|
|
638
|
+
this.store.isLoading = true;
|
|
639
|
+
this.store.error = null;
|
|
640
|
+
try {
|
|
641
|
+
const listing = await this.host.listDirectory({
|
|
642
|
+
includeHidden: workspaceFilePathHasHiddenSegment(normalizedPath),
|
|
643
|
+
path: this.resolveRequestPath(directoryPath),
|
|
644
|
+
workspaceID: this.store.workspaceID
|
|
645
|
+
});
|
|
646
|
+
if (requestID !== this.requestSeq) {
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
const previousDirectoryPath = this.store.currentDirectoryPath;
|
|
650
|
+
if (previousDirectoryPath !== listing.directoryPath && previousDirectoryPath !== "/") {
|
|
651
|
+
this.store.navigationBackStack.push(this.store.currentDirectoryPath);
|
|
652
|
+
this.store.navigationForwardStack = [];
|
|
653
|
+
}
|
|
654
|
+
this.store.root = normalizeWorkspaceFilePath(listing.root);
|
|
655
|
+
this.store.currentDirectoryPath = listing.directoryPath;
|
|
656
|
+
this.store.entries = sortWorkspaceEntries(listing.entries);
|
|
657
|
+
this.store.selectedPath = normalizedPath;
|
|
658
|
+
} catch (error) {
|
|
659
|
+
if (requestID === this.requestSeq) {
|
|
660
|
+
this.store.error = this.resolveErrorMessage(error);
|
|
661
|
+
}
|
|
662
|
+
} finally {
|
|
663
|
+
if (requestID === this.requestSeq) {
|
|
664
|
+
this.store.isLoading = false;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
async replaceDirectory(path) {
|
|
669
|
+
const normalizedPath = normalizeWorkspaceFilePath(path, this.store.root);
|
|
670
|
+
const requestID = ++this.requestSeq;
|
|
671
|
+
this.store.isLoading = true;
|
|
672
|
+
this.store.error = null;
|
|
673
|
+
try {
|
|
674
|
+
const listing = await this.host.listDirectory({
|
|
675
|
+
includeHidden: workspaceFilePathHasHiddenSegment(normalizedPath),
|
|
676
|
+
path: this.resolveRequestPath(normalizedPath),
|
|
677
|
+
workspaceID: this.store.workspaceID
|
|
678
|
+
});
|
|
679
|
+
if (requestID !== this.requestSeq) {
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
this.store.root = normalizeWorkspaceFilePath(listing.root);
|
|
683
|
+
this.store.currentDirectoryPath = listing.directoryPath;
|
|
684
|
+
this.store.entries = sortWorkspaceEntries(listing.entries);
|
|
685
|
+
this.store.selectedPath = null;
|
|
686
|
+
} catch (error) {
|
|
687
|
+
if (requestID === this.requestSeq) {
|
|
688
|
+
this.store.error = this.resolveErrorMessage(error);
|
|
689
|
+
}
|
|
690
|
+
} finally {
|
|
691
|
+
if (requestID === this.requestSeq) {
|
|
692
|
+
this.store.isLoading = false;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
resolveRequestPath(path) {
|
|
697
|
+
if (this.store.root === "/" && path === "/") {
|
|
698
|
+
return "";
|
|
699
|
+
}
|
|
700
|
+
return path;
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
// src/services/internal/workspaceFileManagerPreviewController.ts
|
|
705
|
+
var WorkspaceFileManagerPreviewController = class {
|
|
706
|
+
copy;
|
|
707
|
+
host;
|
|
708
|
+
isDisposed;
|
|
709
|
+
resolveErrorMessage;
|
|
710
|
+
store;
|
|
711
|
+
previewObjectUrl = null;
|
|
712
|
+
previewRequestSeq = 0;
|
|
713
|
+
constructor(input) {
|
|
714
|
+
this.copy = input.copy;
|
|
715
|
+
this.host = input.host;
|
|
716
|
+
this.isDisposed = input.isDisposed;
|
|
717
|
+
this.resolveErrorMessage = input.resolveErrorMessage;
|
|
718
|
+
this.store = input.store;
|
|
719
|
+
}
|
|
720
|
+
dispose() {
|
|
721
|
+
this.previewRequestSeq += 1;
|
|
722
|
+
this.revokePreviewObjectUrl();
|
|
723
|
+
}
|
|
724
|
+
async syncPreviewState() {
|
|
725
|
+
const requestID = this.previewRequestSeq + 1;
|
|
726
|
+
this.previewRequestSeq = requestID;
|
|
727
|
+
this.revokePreviewObjectUrl();
|
|
728
|
+
const selectedEntry = this.store.entries.find(
|
|
729
|
+
(entry) => entry.path === this.store.selectedPath
|
|
730
|
+
) ?? null;
|
|
731
|
+
const copy = this.copy();
|
|
732
|
+
if (!selectedEntry) {
|
|
733
|
+
this.store.previewState = { status: "empty" };
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
const readiness = resolveWorkspaceFilePreviewReadiness(selectedEntry);
|
|
737
|
+
if (readiness.status === "directory") {
|
|
738
|
+
this.store.previewState = {
|
|
739
|
+
entry: selectedEntry,
|
|
740
|
+
status: "directory"
|
|
741
|
+
};
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
if (readiness.status === "unsupported") {
|
|
745
|
+
this.store.previewState = {
|
|
746
|
+
entry: selectedEntry,
|
|
747
|
+
message: copy.t("previewUnsupported"),
|
|
748
|
+
status: "unsupported"
|
|
749
|
+
};
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
if (readiness.status === "readonly") {
|
|
753
|
+
this.store.previewState = {
|
|
754
|
+
entry: selectedEntry,
|
|
755
|
+
message: resolveWorkspaceFileManagerPreviewReadonlyMessage(
|
|
756
|
+
copy,
|
|
757
|
+
readiness.reason,
|
|
758
|
+
readiness.maxSizeBytes
|
|
759
|
+
),
|
|
760
|
+
status: "readonly"
|
|
761
|
+
};
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
const activationTarget = readiness.target;
|
|
765
|
+
this.store.previewState = {
|
|
766
|
+
entry: activationTarget,
|
|
767
|
+
status: "loading"
|
|
768
|
+
};
|
|
769
|
+
if (!this.host.readPreviewFile) {
|
|
770
|
+
this.store.previewState = {
|
|
771
|
+
entry: selectedEntry,
|
|
772
|
+
message: copy.t("previewUnsupported"),
|
|
773
|
+
status: "unsupported"
|
|
774
|
+
};
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
try {
|
|
778
|
+
const bytes = normalizeWorkspaceFilePreviewBytes(
|
|
779
|
+
await this.host.readPreviewFile(
|
|
780
|
+
this.store.workspaceID,
|
|
781
|
+
selectedEntry.path
|
|
782
|
+
)
|
|
783
|
+
);
|
|
784
|
+
if (this.isDisposed() || requestID !== this.previewRequestSeq) {
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
const loadedState = createWorkspaceFilePreviewLoadedState({
|
|
788
|
+
bytes,
|
|
789
|
+
entry: selectedEntry,
|
|
790
|
+
target: activationTarget
|
|
791
|
+
});
|
|
792
|
+
if (loadedState.status === "image") {
|
|
793
|
+
const objectUrl = URL.createObjectURL(
|
|
794
|
+
new Blob([loadedState.bytes], {
|
|
795
|
+
type: loadedState.contentType
|
|
796
|
+
})
|
|
797
|
+
);
|
|
798
|
+
if (this.isDisposed() || requestID !== this.previewRequestSeq) {
|
|
799
|
+
URL.revokeObjectURL(objectUrl);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
this.previewObjectUrl = objectUrl;
|
|
803
|
+
this.store.previewState = {
|
|
804
|
+
entry: activationTarget,
|
|
805
|
+
objectUrl,
|
|
806
|
+
status: "image"
|
|
807
|
+
};
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
if (loadedState.status === "text") {
|
|
811
|
+
this.store.previewState = loadedState;
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
if (loadedState.status === "readonly") {
|
|
815
|
+
this.store.previewState = {
|
|
816
|
+
entry: selectedEntry,
|
|
817
|
+
message: resolveWorkspaceFileManagerPreviewReadonlyMessage(
|
|
818
|
+
copy,
|
|
819
|
+
loadedState.reason,
|
|
820
|
+
loadedState.maxSizeBytes
|
|
821
|
+
),
|
|
822
|
+
status: "readonly"
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
} catch (error) {
|
|
826
|
+
if (this.isDisposed() || requestID !== this.previewRequestSeq) {
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
this.store.previewState = {
|
|
830
|
+
entry: selectedEntry,
|
|
831
|
+
message: this.resolveErrorMessage(error, {
|
|
832
|
+
preview_file_too_large: copy.t("previewFileTooLarge", {
|
|
833
|
+
maxSize: formatWorkspacePreviewByteLimit(
|
|
834
|
+
workspaceFilePreviewMaxBytes
|
|
835
|
+
)
|
|
836
|
+
})
|
|
837
|
+
}),
|
|
838
|
+
status: "error"
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
revokePreviewObjectUrl() {
|
|
843
|
+
if (!this.previewObjectUrl) {
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
URL.revokeObjectURL(this.previewObjectUrl);
|
|
847
|
+
this.previewObjectUrl = null;
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
function resolveWorkspaceFileManagerPreviewReadonlyMessage(copy, reason, maxSizeBytes) {
|
|
851
|
+
switch (reason) {
|
|
852
|
+
case "binary":
|
|
853
|
+
return copy.t("previewBinary");
|
|
854
|
+
case "decode_failed":
|
|
855
|
+
return copy.t("previewDecodeFailed");
|
|
856
|
+
case "file_too_large":
|
|
857
|
+
return copy.t("previewFileTooLarge", {
|
|
858
|
+
maxSize: formatWorkspacePreviewByteLimit(maxSizeBytes ?? 0)
|
|
859
|
+
});
|
|
860
|
+
case "text_too_large":
|
|
861
|
+
return copy.t("previewTooLarge", {
|
|
862
|
+
maxSize: formatWorkspacePreviewByteLimit(maxSizeBytes ?? 0)
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// src/services/internal/workspaceFileManagerImportController.ts
|
|
868
|
+
var WorkspaceFileManagerImportController = class {
|
|
869
|
+
applyHostActionResult;
|
|
870
|
+
copy;
|
|
871
|
+
host;
|
|
872
|
+
refresh;
|
|
873
|
+
resolveErrorMessage;
|
|
874
|
+
store;
|
|
875
|
+
constructor(input) {
|
|
876
|
+
this.applyHostActionResult = input.applyHostActionResult;
|
|
877
|
+
this.copy = input.copy;
|
|
878
|
+
this.host = input.host;
|
|
879
|
+
this.refresh = input.refresh;
|
|
880
|
+
this.resolveErrorMessage = input.resolveErrorMessage;
|
|
881
|
+
this.store = input.store;
|
|
882
|
+
}
|
|
883
|
+
async confirmImportConflict() {
|
|
884
|
+
const importConflictDialog = this.store.importConflictDialog;
|
|
885
|
+
if (!importConflictDialog?.onConfirm) {
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
this.store.busyAction = "import";
|
|
889
|
+
try {
|
|
890
|
+
const result = await importConflictDialog.onConfirm();
|
|
891
|
+
this.store.importConflictDialog = null;
|
|
892
|
+
this.applyHostActionResult(result, { kind: "import" });
|
|
893
|
+
} finally {
|
|
894
|
+
this.store.busyAction = null;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
async importDroppedFiles(dataTransfer, targetDirectoryPath) {
|
|
898
|
+
if (!this.host.resolveDroppedPaths || !this.host.importPaths) {
|
|
899
|
+
const result = {
|
|
900
|
+
message: this.copy().t("unsupportedImportBody"),
|
|
901
|
+
supported: false,
|
|
902
|
+
title: this.copy().t("unsupportedImportTitle")
|
|
903
|
+
};
|
|
904
|
+
this.applyHostActionResult(result, { kind: "import" });
|
|
905
|
+
return result;
|
|
906
|
+
}
|
|
907
|
+
const sourcePaths = this.host.resolveDroppedPaths(dataTransfer);
|
|
908
|
+
if (sourcePaths.length === 0) {
|
|
909
|
+
return { supported: true };
|
|
910
|
+
}
|
|
911
|
+
this.store.busyAction = "import";
|
|
912
|
+
try {
|
|
913
|
+
const result = await this.wrapImportAction(
|
|
914
|
+
() => this.host.importPaths?.(
|
|
915
|
+
this.store.workspaceID,
|
|
916
|
+
targetDirectoryPath,
|
|
917
|
+
sourcePaths
|
|
918
|
+
) ?? Promise.resolve({ supported: true })
|
|
919
|
+
);
|
|
920
|
+
this.applyHostActionResult(result, { kind: "import" });
|
|
921
|
+
return result;
|
|
922
|
+
} finally {
|
|
923
|
+
this.store.busyAction = null;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
async importFiles(targetDirectoryPath) {
|
|
927
|
+
if (!this.host.importFiles) {
|
|
928
|
+
const result = {
|
|
929
|
+
message: this.copy().t("unsupportedImportBody"),
|
|
930
|
+
supported: false,
|
|
931
|
+
title: this.copy().t("unsupportedImportTitle")
|
|
932
|
+
};
|
|
933
|
+
this.applyHostActionResult(result, { kind: "import" });
|
|
934
|
+
return result;
|
|
935
|
+
}
|
|
936
|
+
this.store.busyAction = "import";
|
|
937
|
+
try {
|
|
938
|
+
const result = await this.wrapImportAction(
|
|
939
|
+
() => this.host.importFiles?.(
|
|
940
|
+
this.store.workspaceID,
|
|
941
|
+
targetDirectoryPath
|
|
942
|
+
) ?? Promise.resolve({ supported: true })
|
|
943
|
+
);
|
|
944
|
+
this.applyHostActionResult(result, { kind: "import" });
|
|
945
|
+
return result;
|
|
946
|
+
} finally {
|
|
947
|
+
this.store.busyAction = null;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
async wrapImportAction(action) {
|
|
951
|
+
try {
|
|
952
|
+
const result = await action();
|
|
953
|
+
if (result.importConflict) {
|
|
954
|
+
const importConflict = result.importConflict;
|
|
955
|
+
const importConflictConfirm = importConflict.onConfirm;
|
|
956
|
+
return {
|
|
957
|
+
...result,
|
|
958
|
+
importConflict: {
|
|
959
|
+
...importConflict,
|
|
960
|
+
onConfirm: importConflictConfirm ? async () => {
|
|
961
|
+
const confirmResult = await importConflictConfirm();
|
|
962
|
+
await this.refresh();
|
|
963
|
+
return confirmResult;
|
|
964
|
+
} : void 0
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
await this.refresh();
|
|
969
|
+
return result;
|
|
970
|
+
} catch (error) {
|
|
971
|
+
return {
|
|
972
|
+
message: this.resolveErrorMessage(error),
|
|
973
|
+
supported: false,
|
|
974
|
+
title: this.copy().t("importFailedTitle")
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
|
|
980
|
+
// src/services/internal/model/openWithApplicationsCache.ts
|
|
981
|
+
import { resolveWorkspaceFileExtension as resolveWorkspaceFileExtension2 } from "@tutti-os/workspace-file-preview";
|
|
982
|
+
var openWithWarmupLimit = 8;
|
|
983
|
+
function resolveWorkspaceFileOpenWithCacheKey(entry) {
|
|
984
|
+
const extension = resolveWorkspaceFileExtension2(
|
|
985
|
+
entry.path || entry.name || ""
|
|
986
|
+
);
|
|
987
|
+
return `${entry.kind}:${extension || "(no-ext)"}`;
|
|
988
|
+
}
|
|
989
|
+
var WorkspaceFileOpenWithApplicationsCache = class {
|
|
990
|
+
applicationsByKey = /* @__PURE__ */ new Map();
|
|
991
|
+
inflightByKey = /* @__PURE__ */ new Map();
|
|
992
|
+
warmupScheduledKeys = /* @__PURE__ */ new Set();
|
|
993
|
+
get(key) {
|
|
994
|
+
return this.applicationsByKey.get(key) ?? null;
|
|
995
|
+
}
|
|
996
|
+
async resolve(key, load) {
|
|
997
|
+
const cached = this.applicationsByKey.get(key);
|
|
998
|
+
if (cached) {
|
|
999
|
+
return cached;
|
|
1000
|
+
}
|
|
1001
|
+
const inflight = this.inflightByKey.get(key);
|
|
1002
|
+
if (inflight) {
|
|
1003
|
+
return inflight;
|
|
1004
|
+
}
|
|
1005
|
+
const promise = load().then((applications) => {
|
|
1006
|
+
this.applicationsByKey.set(key, applications);
|
|
1007
|
+
this.inflightByKey.delete(key);
|
|
1008
|
+
return applications;
|
|
1009
|
+
}).catch((error) => {
|
|
1010
|
+
this.inflightByKey.delete(key);
|
|
1011
|
+
throw error;
|
|
1012
|
+
});
|
|
1013
|
+
this.inflightByKey.set(key, promise);
|
|
1014
|
+
return promise;
|
|
1015
|
+
}
|
|
1016
|
+
scheduleWarmup(entries, load) {
|
|
1017
|
+
let scheduled = 0;
|
|
1018
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
1019
|
+
for (const entry of entries) {
|
|
1020
|
+
if (entry.kind !== "file" || scheduled >= openWithWarmupLimit) {
|
|
1021
|
+
continue;
|
|
1022
|
+
}
|
|
1023
|
+
const key = resolveWorkspaceFileOpenWithCacheKey(entry);
|
|
1024
|
+
if (seenKeys.has(key) || this.applicationsByKey.has(key) || this.inflightByKey.has(key) || this.warmupScheduledKeys.has(key)) {
|
|
1025
|
+
continue;
|
|
1026
|
+
}
|
|
1027
|
+
seenKeys.add(key);
|
|
1028
|
+
this.warmupScheduledKeys.add(key);
|
|
1029
|
+
scheduled += 1;
|
|
1030
|
+
void load(entry).finally(() => {
|
|
1031
|
+
this.warmupScheduledKeys.delete(key);
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
};
|
|
1036
|
+
|
|
1037
|
+
// src/services/internal/workspaceFileManagerSession.ts
|
|
1038
|
+
var DefaultWorkspaceFileManagerSession = class {
|
|
1039
|
+
store;
|
|
1040
|
+
host;
|
|
1041
|
+
hasInitialized = false;
|
|
1042
|
+
initializePromise = null;
|
|
1043
|
+
isActive = false;
|
|
1044
|
+
isDisposed = false;
|
|
1045
|
+
lastObservedEntries;
|
|
1046
|
+
lastObservedPersistedState;
|
|
1047
|
+
lastObservedSelectedPath;
|
|
1048
|
+
lastRevealRequestID = null;
|
|
1049
|
+
onHostActionMessage;
|
|
1050
|
+
onMutationErrorMessage;
|
|
1051
|
+
searchRequestSeq = 0;
|
|
1052
|
+
activationController;
|
|
1053
|
+
copy;
|
|
1054
|
+
mutationController;
|
|
1055
|
+
navigationController;
|
|
1056
|
+
previewController;
|
|
1057
|
+
persistence;
|
|
1058
|
+
unsubscribeStore = null;
|
|
1059
|
+
importController;
|
|
1060
|
+
openWithApplicationsCache = new WorkspaceFileOpenWithApplicationsCache();
|
|
1061
|
+
constructor(input) {
|
|
1062
|
+
this.copy = input.copy;
|
|
1063
|
+
this.host = input.host;
|
|
1064
|
+
this.onHostActionMessage = input.onHostActionMessage;
|
|
1065
|
+
this.onMutationErrorMessage = input.onMutationErrorMessage;
|
|
1066
|
+
this.persistence = input.persistence;
|
|
1067
|
+
this.store = input.store;
|
|
1068
|
+
this.navigationController = new WorkspaceFileManagerNavigationController({
|
|
1069
|
+
host: input.host,
|
|
1070
|
+
resolveErrorMessage: (error) => this.resolveErrorMessage(error),
|
|
1071
|
+
store: this.store
|
|
1072
|
+
});
|
|
1073
|
+
this.activationController = new WorkspaceFileManagerActivationController({
|
|
1074
|
+
copy: () => this.copy,
|
|
1075
|
+
host: input.host,
|
|
1076
|
+
loadDirectory: (path) => this.navigationController.loadDirectory(path),
|
|
1077
|
+
resolveErrorMessage: (error, overrides) => this.resolveErrorMessage(error, overrides),
|
|
1078
|
+
store: this.store
|
|
1079
|
+
});
|
|
1080
|
+
this.mutationController = new WorkspaceFileManagerMutationController({
|
|
1081
|
+
host: input.host,
|
|
1082
|
+
onErrorMessage: (message) => this.onMutationErrorMessage?.(message),
|
|
1083
|
+
refresh: () => this.refresh(),
|
|
1084
|
+
resolveErrorMessage: (error) => this.resolveErrorMessage(error),
|
|
1085
|
+
store: this.store
|
|
1086
|
+
});
|
|
1087
|
+
this.previewController = new WorkspaceFileManagerPreviewController({
|
|
1088
|
+
copy: () => this.copy,
|
|
1089
|
+
host: input.host,
|
|
1090
|
+
isDisposed: () => this.isDisposed,
|
|
1091
|
+
resolveErrorMessage: (error, overrides) => this.resolveErrorMessage(error, overrides),
|
|
1092
|
+
store: this.store
|
|
1093
|
+
});
|
|
1094
|
+
this.importController = new WorkspaceFileManagerImportController({
|
|
1095
|
+
applyHostActionResult: (result, fallback) => this.applyHostActionResult(result, fallback),
|
|
1096
|
+
copy: () => this.copy,
|
|
1097
|
+
host: input.host,
|
|
1098
|
+
refresh: () => this.refresh(),
|
|
1099
|
+
resolveErrorMessage: (error) => this.resolveErrorMessage(error),
|
|
1100
|
+
store: this.store
|
|
1101
|
+
});
|
|
1102
|
+
this.lastObservedEntries = this.store.entries;
|
|
1103
|
+
this.lastObservedPersistedState = serializePersistedState(
|
|
1104
|
+
this.getPersistedState()
|
|
1105
|
+
);
|
|
1106
|
+
this.lastObservedSelectedPath = this.store.selectedPath;
|
|
1107
|
+
}
|
|
1108
|
+
async activateFile(request) {
|
|
1109
|
+
return this.activationController.activateFile(request);
|
|
1110
|
+
}
|
|
1111
|
+
async applyRevealIntent(intent) {
|
|
1112
|
+
if (!intent?.path || !intent.requestID) {
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
if (intent.requestID === this.lastRevealRequestID) {
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
this.lastRevealRequestID = intent.requestID;
|
|
1119
|
+
await this.revealPath(intent.path);
|
|
1120
|
+
}
|
|
1121
|
+
closeContextMenu() {
|
|
1122
|
+
this.store.contextMenu = null;
|
|
1123
|
+
this.store.contextMenuEntryPath = null;
|
|
1124
|
+
}
|
|
1125
|
+
closeCreateDialog() {
|
|
1126
|
+
if (this.store.busyAction === "create") {
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
this.store.createDialog = null;
|
|
1130
|
+
}
|
|
1131
|
+
closeDeleteDialog() {
|
|
1132
|
+
if (this.store.busyAction === "delete") {
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
this.store.deleteDialog = null;
|
|
1136
|
+
}
|
|
1137
|
+
cancelInlineRename() {
|
|
1138
|
+
if (this.store.busyAction === "rename") {
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
this.store.inlineRenameEntryPath = null;
|
|
1142
|
+
this.store.inlineRenameValidation = null;
|
|
1143
|
+
}
|
|
1144
|
+
closeTransientUi() {
|
|
1145
|
+
this.store.contextMenu = null;
|
|
1146
|
+
this.store.contextMenuEntryPath = null;
|
|
1147
|
+
if (this.store.busyAction !== null) {
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
this.store.createDialog = null;
|
|
1151
|
+
this.store.deleteDialog = null;
|
|
1152
|
+
this.store.inlineRenameEntryPath = null;
|
|
1153
|
+
this.store.inlineRenameValidation = null;
|
|
1154
|
+
this.store.unsupportedDialog = null;
|
|
1155
|
+
this.store.importConflictDialog = null;
|
|
1156
|
+
}
|
|
1157
|
+
closeUnsupportedDialog() {
|
|
1158
|
+
if (this.store.busyAction === "view") {
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
this.store.unsupportedDialog = null;
|
|
1162
|
+
}
|
|
1163
|
+
closeImportConflictDialog() {
|
|
1164
|
+
if (this.store.busyAction === "import") {
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
this.store.importConflictDialog = null;
|
|
1168
|
+
}
|
|
1169
|
+
async confirmCreateDialog() {
|
|
1170
|
+
const createDialog = this.store.createDialog;
|
|
1171
|
+
if (!createDialog) {
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
const validation = validateWorkspaceFileEntryName(createDialog.name);
|
|
1175
|
+
if (validation) {
|
|
1176
|
+
this.store.createDialog = {
|
|
1177
|
+
...createDialog,
|
|
1178
|
+
errorMessage: validation === "required" ? this.copy.t("createNameRequired") : this.copy.t("createNameInvalid")
|
|
1179
|
+
};
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
const path = `${this.store.currentDirectoryPath}/${createDialog.name.trim()}`.replaceAll(
|
|
1183
|
+
/\/+/g,
|
|
1184
|
+
"/"
|
|
1185
|
+
);
|
|
1186
|
+
this.store.busyAction = "create";
|
|
1187
|
+
try {
|
|
1188
|
+
if (createDialog.kind === "directory") {
|
|
1189
|
+
await this.createDirectory(path);
|
|
1190
|
+
} else {
|
|
1191
|
+
await this.createFile(path);
|
|
1192
|
+
}
|
|
1193
|
+
this.store.createDialog = null;
|
|
1194
|
+
} finally {
|
|
1195
|
+
this.store.busyAction = null;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
async confirmDeleteDialog() {
|
|
1199
|
+
const deleteDialog = this.store.deleteDialog;
|
|
1200
|
+
if (!deleteDialog) {
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
this.store.busyAction = "delete";
|
|
1204
|
+
try {
|
|
1205
|
+
this.store.selectedPath = deleteDialog.entryPath;
|
|
1206
|
+
await this.deleteSelected();
|
|
1207
|
+
this.store.deleteDialog = null;
|
|
1208
|
+
} finally {
|
|
1209
|
+
this.store.busyAction = null;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
async confirmInlineRename(newName) {
|
|
1213
|
+
const inlineRenameEntryPath = this.store.inlineRenameEntryPath;
|
|
1214
|
+
if (!inlineRenameEntryPath) {
|
|
1215
|
+
return true;
|
|
1216
|
+
}
|
|
1217
|
+
const entry = this.store.entries.find(
|
|
1218
|
+
(candidate) => candidate.path === inlineRenameEntryPath
|
|
1219
|
+
);
|
|
1220
|
+
if (!entry) {
|
|
1221
|
+
this.store.inlineRenameEntryPath = null;
|
|
1222
|
+
this.store.inlineRenameValidation = null;
|
|
1223
|
+
return true;
|
|
1224
|
+
}
|
|
1225
|
+
const trimmedName = newName.trim();
|
|
1226
|
+
if (trimmedName === entry.name) {
|
|
1227
|
+
this.store.inlineRenameEntryPath = null;
|
|
1228
|
+
this.store.inlineRenameValidation = null;
|
|
1229
|
+
return true;
|
|
1230
|
+
}
|
|
1231
|
+
const validation = validateWorkspaceFileEntryName(trimmedName);
|
|
1232
|
+
if (validation) {
|
|
1233
|
+
this.store.inlineRenameValidation = validation;
|
|
1234
|
+
return false;
|
|
1235
|
+
}
|
|
1236
|
+
this.store.inlineRenameValidation = null;
|
|
1237
|
+
this.store.busyAction = "rename";
|
|
1238
|
+
try {
|
|
1239
|
+
await this.mutationController.renameEntry(entry, trimmedName);
|
|
1240
|
+
this.store.inlineRenameEntryPath = null;
|
|
1241
|
+
return true;
|
|
1242
|
+
} finally {
|
|
1243
|
+
this.store.busyAction = null;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
async confirmImportConflict() {
|
|
1247
|
+
await this.importController.confirmImportConflict();
|
|
1248
|
+
}
|
|
1249
|
+
async createDirectory(path) {
|
|
1250
|
+
await this.mutationController.createDirectory(path);
|
|
1251
|
+
}
|
|
1252
|
+
async createFile(path) {
|
|
1253
|
+
await this.mutationController.createFile(path);
|
|
1254
|
+
}
|
|
1255
|
+
async deleteSelected() {
|
|
1256
|
+
await this.mutationController.deleteSelected();
|
|
1257
|
+
}
|
|
1258
|
+
decrementDragDepth() {
|
|
1259
|
+
this.store.dragDepth = this.store.dragDepth <= 1 ? 0 : this.store.dragDepth - 1;
|
|
1260
|
+
}
|
|
1261
|
+
dispose() {
|
|
1262
|
+
this.isDisposed = true;
|
|
1263
|
+
this.hasInitialized = false;
|
|
1264
|
+
this.initializePromise = null;
|
|
1265
|
+
this.isActive = false;
|
|
1266
|
+
this.searchRequestSeq += 1;
|
|
1267
|
+
this.store.isSearching = false;
|
|
1268
|
+
this.unsubscribeStore?.();
|
|
1269
|
+
this.unsubscribeStore = null;
|
|
1270
|
+
this.previewController.dispose();
|
|
1271
|
+
}
|
|
1272
|
+
async goBack() {
|
|
1273
|
+
await this.navigationController.goBack();
|
|
1274
|
+
}
|
|
1275
|
+
async goForward() {
|
|
1276
|
+
await this.navigationController.goForward();
|
|
1277
|
+
}
|
|
1278
|
+
getPersistedState() {
|
|
1279
|
+
return getWorkspaceFileManagerPersistedState(this.store);
|
|
1280
|
+
}
|
|
1281
|
+
async handleActivationFallbackAction(action) {
|
|
1282
|
+
await this.activationController.handleFallbackAction(action);
|
|
1283
|
+
}
|
|
1284
|
+
async exportEntry(entry) {
|
|
1285
|
+
if (!this.host.exportEntry) {
|
|
1286
|
+
return { supported: false };
|
|
1287
|
+
}
|
|
1288
|
+
this.store.busyAction = "export";
|
|
1289
|
+
this.store.error = null;
|
|
1290
|
+
try {
|
|
1291
|
+
const result = await this.host.exportEntry({
|
|
1292
|
+
entry,
|
|
1293
|
+
workspaceID: this.store.workspaceID
|
|
1294
|
+
});
|
|
1295
|
+
this.applyHostActionResult(result, {
|
|
1296
|
+
actionKind: "export",
|
|
1297
|
+
entry,
|
|
1298
|
+
kind: "view"
|
|
1299
|
+
});
|
|
1300
|
+
return result;
|
|
1301
|
+
} catch (error) {
|
|
1302
|
+
const result = {
|
|
1303
|
+
message: this.resolveErrorMessage(error),
|
|
1304
|
+
supported: false,
|
|
1305
|
+
title: this.copy.t("downloadFailedTitle")
|
|
1306
|
+
};
|
|
1307
|
+
this.applyHostActionResult(result, {
|
|
1308
|
+
actionKind: "export",
|
|
1309
|
+
entry,
|
|
1310
|
+
kind: "view"
|
|
1311
|
+
});
|
|
1312
|
+
return result;
|
|
1313
|
+
} finally {
|
|
1314
|
+
this.store.busyAction = null;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
incrementDragDepth() {
|
|
1318
|
+
if (!this.isActive) {
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
this.store.dragDepth += 1;
|
|
1322
|
+
}
|
|
1323
|
+
async initialize() {
|
|
1324
|
+
if (this.hasInitialized) {
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1327
|
+
if (this.initializePromise) {
|
|
1328
|
+
await this.initializePromise;
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
this.isDisposed = false;
|
|
1332
|
+
this.initializePromise = (async () => {
|
|
1333
|
+
this.observeStore();
|
|
1334
|
+
if (!this.hasLoadedDirectoryState()) {
|
|
1335
|
+
await this.navigationController.loadDirectory();
|
|
1336
|
+
}
|
|
1337
|
+
await this.previewController.syncPreviewState();
|
|
1338
|
+
this.hasInitialized = true;
|
|
1339
|
+
})();
|
|
1340
|
+
try {
|
|
1341
|
+
await this.initializePromise;
|
|
1342
|
+
} finally {
|
|
1343
|
+
this.initializePromise = null;
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
async loadDirectory(path = this.store.currentDirectoryPath) {
|
|
1347
|
+
await this.navigationController.loadDirectory(path);
|
|
1348
|
+
}
|
|
1349
|
+
openContextMenu(input) {
|
|
1350
|
+
this.store.contextMenuEntryPath = input.entryPath;
|
|
1351
|
+
this.store.contextMenu = input;
|
|
1352
|
+
}
|
|
1353
|
+
openCreateDirectoryDialog() {
|
|
1354
|
+
this.store.contextMenu = null;
|
|
1355
|
+
this.store.contextMenuEntryPath = null;
|
|
1356
|
+
this.store.createDialog = {
|
|
1357
|
+
errorMessage: null,
|
|
1358
|
+
kind: "directory",
|
|
1359
|
+
name: ""
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
openCreateFileDialog() {
|
|
1363
|
+
this.store.contextMenu = null;
|
|
1364
|
+
this.store.contextMenuEntryPath = null;
|
|
1365
|
+
this.store.createDialog = {
|
|
1366
|
+
errorMessage: null,
|
|
1367
|
+
kind: "file",
|
|
1368
|
+
name: ""
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
openDeleteDialog(entry) {
|
|
1372
|
+
this.store.contextMenu = null;
|
|
1373
|
+
this.store.contextMenuEntryPath = null;
|
|
1374
|
+
this.store.deleteDialog = {
|
|
1375
|
+
entryPath: entry.path
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1378
|
+
startInlineRename(entry) {
|
|
1379
|
+
this.store.contextMenu = null;
|
|
1380
|
+
this.store.contextMenuEntryPath = null;
|
|
1381
|
+
this.store.inlineRenameEntryPath = entry.path;
|
|
1382
|
+
this.store.inlineRenameValidation = null;
|
|
1383
|
+
this.store.selectedPath = entry.path;
|
|
1384
|
+
}
|
|
1385
|
+
async copyToClipboard(entry) {
|
|
1386
|
+
if (!this.host.copyEntriesToClipboard) {
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
await this.host.copyEntriesToClipboard({
|
|
1390
|
+
paths: [entry.path],
|
|
1391
|
+
workspaceID: this.store.workspaceID
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
getCachedOpenWithApplications(entry) {
|
|
1395
|
+
if (!this.host.listOpenWithApplications) {
|
|
1396
|
+
return null;
|
|
1397
|
+
}
|
|
1398
|
+
return this.openWithApplicationsCache.get(
|
|
1399
|
+
resolveWorkspaceFileOpenWithCacheKey(entry)
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
async listOpenWithApplications(entry) {
|
|
1403
|
+
if (!this.host.listOpenWithApplications) {
|
|
1404
|
+
return [];
|
|
1405
|
+
}
|
|
1406
|
+
return this.openWithApplicationsCache.resolve(
|
|
1407
|
+
resolveWorkspaceFileOpenWithCacheKey(entry),
|
|
1408
|
+
() => this.host.listOpenWithApplications({
|
|
1409
|
+
path: entry.path,
|
|
1410
|
+
workspaceID: this.store.workspaceID
|
|
1411
|
+
})
|
|
1412
|
+
);
|
|
1413
|
+
}
|
|
1414
|
+
async openEntry(entry) {
|
|
1415
|
+
await this.activationController.openEntry(entry);
|
|
1416
|
+
}
|
|
1417
|
+
async openFileWithApplication(entry, applicationPath) {
|
|
1418
|
+
if (!this.host.openFileWithApplication) {
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
this.store.contextMenu = null;
|
|
1422
|
+
this.store.contextMenuEntryPath = null;
|
|
1423
|
+
await this.host.openFileWithApplication({
|
|
1424
|
+
applicationPath,
|
|
1425
|
+
path: entry.path,
|
|
1426
|
+
workspaceID: this.store.workspaceID
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
async openFileInAppBrowser(entry) {
|
|
1430
|
+
if (!this.host.openFileInAppBrowser) {
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
this.store.contextMenu = null;
|
|
1434
|
+
this.store.contextMenuEntryPath = null;
|
|
1435
|
+
await this.host.openFileInAppBrowser({
|
|
1436
|
+
path: entry.path,
|
|
1437
|
+
workspaceID: this.store.workspaceID
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1440
|
+
async openFileInDefaultBrowser(entry) {
|
|
1441
|
+
if (!this.host.openFileInDefaultBrowser) {
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1444
|
+
this.store.contextMenu = null;
|
|
1445
|
+
this.store.contextMenuEntryPath = null;
|
|
1446
|
+
await this.host.openFileInDefaultBrowser({
|
|
1447
|
+
path: entry.path,
|
|
1448
|
+
workspaceID: this.store.workspaceID
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
async openFileWithOtherApplication(entry) {
|
|
1452
|
+
if (!this.host.openFileWithOtherApplication) {
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
this.store.contextMenu = null;
|
|
1456
|
+
this.store.contextMenuEntryPath = null;
|
|
1457
|
+
await this.host.openFileWithOtherApplication({
|
|
1458
|
+
applicationPickerPrompt: this.copy.t("openWithOtherPickerPrompt"),
|
|
1459
|
+
path: entry.path,
|
|
1460
|
+
workspaceID: this.store.workspaceID
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
async revealEntry(entry) {
|
|
1464
|
+
if (!this.host.revealEntry) {
|
|
1465
|
+
return;
|
|
1466
|
+
}
|
|
1467
|
+
this.store.contextMenu = null;
|
|
1468
|
+
this.store.contextMenuEntryPath = null;
|
|
1469
|
+
await this.host.revealEntry({
|
|
1470
|
+
path: entry.path,
|
|
1471
|
+
workspaceID: this.store.workspaceID
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
async moveEntry(entry, targetDirectoryPath) {
|
|
1475
|
+
this.store.busyAction = "move";
|
|
1476
|
+
try {
|
|
1477
|
+
await this.mutationController.moveEntry(entry, targetDirectoryPath);
|
|
1478
|
+
} finally {
|
|
1479
|
+
this.store.busyAction = null;
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
async refresh() {
|
|
1483
|
+
await this.navigationController.refresh();
|
|
1484
|
+
}
|
|
1485
|
+
async revealPath(path) {
|
|
1486
|
+
await this.navigationController.revealPath(path);
|
|
1487
|
+
}
|
|
1488
|
+
resetDragDepth() {
|
|
1489
|
+
this.store.dragDepth = 0;
|
|
1490
|
+
}
|
|
1491
|
+
async search(query) {
|
|
1492
|
+
const requestID = ++this.searchRequestSeq;
|
|
1493
|
+
this.store.searchQuery = query;
|
|
1494
|
+
this.store.searchError = null;
|
|
1495
|
+
if (!this.host.search || query.trim() === "") {
|
|
1496
|
+
this.store.searchEntries = [];
|
|
1497
|
+
this.store.isSearching = false;
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
this.store.isSearching = true;
|
|
1501
|
+
try {
|
|
1502
|
+
const result = await this.host.search({
|
|
1503
|
+
query,
|
|
1504
|
+
workspaceID: this.store.workspaceID
|
|
1505
|
+
});
|
|
1506
|
+
if (this.isDisposed || requestID !== this.searchRequestSeq) {
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
this.store.searchEntries = result.entries;
|
|
1510
|
+
} catch (error) {
|
|
1511
|
+
if (!this.isDisposed && requestID === this.searchRequestSeq) {
|
|
1512
|
+
this.store.searchError = this.resolveErrorMessage(error);
|
|
1513
|
+
}
|
|
1514
|
+
} finally {
|
|
1515
|
+
if (!this.isDisposed && requestID === this.searchRequestSeq) {
|
|
1516
|
+
this.store.isSearching = false;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
select(path) {
|
|
1521
|
+
const nextSelectedPath = path ? normalizeWorkspaceFilePath(path, this.store.root) : null;
|
|
1522
|
+
if (this.store.selectedPath === nextSelectedPath) {
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
this.store.selectedPath = nextSelectedPath;
|
|
1526
|
+
}
|
|
1527
|
+
setActive(active) {
|
|
1528
|
+
if (this.isActive === active) {
|
|
1529
|
+
return;
|
|
1530
|
+
}
|
|
1531
|
+
this.isActive = active;
|
|
1532
|
+
if (active) {
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1535
|
+
this.store.contextMenu = null;
|
|
1536
|
+
this.store.contextMenuEntryPath = null;
|
|
1537
|
+
this.store.dragDepth = 0;
|
|
1538
|
+
}
|
|
1539
|
+
setI18nRuntime(copy) {
|
|
1540
|
+
if (this.copy === copy) {
|
|
1541
|
+
return;
|
|
1542
|
+
}
|
|
1543
|
+
this.copy = copy;
|
|
1544
|
+
void this.previewController.syncPreviewState();
|
|
1545
|
+
}
|
|
1546
|
+
updateCreateDialogName(name) {
|
|
1547
|
+
if (!this.store.createDialog) {
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
this.store.createDialog = {
|
|
1551
|
+
...this.store.createDialog,
|
|
1552
|
+
errorMessage: null,
|
|
1553
|
+
name
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
clearInlineRenameValidation() {
|
|
1557
|
+
this.store.inlineRenameValidation = null;
|
|
1558
|
+
}
|
|
1559
|
+
async importDroppedFiles(dataTransfer, targetDirectoryPath) {
|
|
1560
|
+
return this.importController.importDroppedFiles(
|
|
1561
|
+
dataTransfer,
|
|
1562
|
+
targetDirectoryPath
|
|
1563
|
+
);
|
|
1564
|
+
}
|
|
1565
|
+
async importFiles(targetDirectoryPath) {
|
|
1566
|
+
return this.importController.importFiles(targetDirectoryPath);
|
|
1567
|
+
}
|
|
1568
|
+
applyHostActionResult(result, fallback) {
|
|
1569
|
+
if (!result) {
|
|
1570
|
+
return;
|
|
1571
|
+
}
|
|
1572
|
+
this.notifyHostActionMessages(result, fallback);
|
|
1573
|
+
if (result.importConflict) {
|
|
1574
|
+
this.store.unsupportedDialog = null;
|
|
1575
|
+
this.store.importConflictDialog = result.importConflict;
|
|
1576
|
+
return;
|
|
1577
|
+
}
|
|
1578
|
+
if (result.supported === false) {
|
|
1579
|
+
this.store.importConflictDialog = null;
|
|
1580
|
+
this.store.unsupportedDialog = {
|
|
1581
|
+
entryPath: "entry" in fallback ? fallback.entry.path : null,
|
|
1582
|
+
kind: fallback.kind,
|
|
1583
|
+
message: result.message,
|
|
1584
|
+
title: result.title
|
|
1585
|
+
};
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
this.store.unsupportedDialog = null;
|
|
1589
|
+
this.store.importConflictDialog = null;
|
|
1590
|
+
}
|
|
1591
|
+
hasLoadedDirectoryState() {
|
|
1592
|
+
return this.store.error !== null || this.store.entries.length > 0 || this.store.selectedPath !== null;
|
|
1593
|
+
}
|
|
1594
|
+
notifyHostActionMessages(result, fallback) {
|
|
1595
|
+
if (!this.onHostActionMessage) {
|
|
1596
|
+
return;
|
|
1597
|
+
}
|
|
1598
|
+
const entry = "entry" in fallback ? fallback.entry : null;
|
|
1599
|
+
const actionKind = fallback.actionKind ?? (fallback.kind === "import" ? "import" : "export");
|
|
1600
|
+
if (result.cancelledMessage?.trim()) {
|
|
1601
|
+
this.notifyHostActionMessage(actionKind, entry, "cancelled", result);
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
if (result.supported === false || result.importConflict) {
|
|
1605
|
+
return;
|
|
1606
|
+
}
|
|
1607
|
+
if (result.completedMessage?.trim()) {
|
|
1608
|
+
this.notifyHostActionMessage(actionKind, entry, "completed", result);
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
this.notifyHostActionMessage(actionKind, entry, "started", result);
|
|
1612
|
+
}
|
|
1613
|
+
notifyHostActionMessage(actionKind, entry, status, result) {
|
|
1614
|
+
const messageByStatus = {
|
|
1615
|
+
cancelled: "cancelledMessage",
|
|
1616
|
+
completed: "completedMessage",
|
|
1617
|
+
started: "startedMessage"
|
|
1618
|
+
};
|
|
1619
|
+
const message = result[messageByStatus[status]]?.trim();
|
|
1620
|
+
if (!message) {
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
this.onHostActionMessage?.({
|
|
1624
|
+
actionKind,
|
|
1625
|
+
entry,
|
|
1626
|
+
message,
|
|
1627
|
+
status
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
observeStore() {
|
|
1631
|
+
if (this.unsubscribeStore) {
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1634
|
+
this.lastObservedEntries = this.store.entries;
|
|
1635
|
+
this.lastObservedSelectedPath = this.store.selectedPath;
|
|
1636
|
+
this.unsubscribeStore = subscribe(this.store, () => {
|
|
1637
|
+
if (this.isDisposed) {
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
if (this.lastObservedEntries === this.store.entries && this.lastObservedSelectedPath === this.store.selectedPath) {
|
|
1641
|
+
this.savePersistedStateIfChanged();
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
this.lastObservedEntries = this.store.entries;
|
|
1645
|
+
this.lastObservedSelectedPath = this.store.selectedPath;
|
|
1646
|
+
this.savePersistedStateIfChanged();
|
|
1647
|
+
void this.previewController.syncPreviewState();
|
|
1648
|
+
this.warmOpenWithApplicationsCache(this.store.entries);
|
|
1649
|
+
});
|
|
1650
|
+
}
|
|
1651
|
+
warmOpenWithApplicationsCache(entries) {
|
|
1652
|
+
if (!this.host.listOpenWithApplications || this.store.isLoading) {
|
|
1653
|
+
return;
|
|
1654
|
+
}
|
|
1655
|
+
this.openWithApplicationsCache.scheduleWarmup(
|
|
1656
|
+
entries,
|
|
1657
|
+
(entry) => this.listOpenWithApplications(entry)
|
|
1658
|
+
);
|
|
1659
|
+
}
|
|
1660
|
+
savePersistedStateIfChanged() {
|
|
1661
|
+
if (!this.persistence?.save) {
|
|
1662
|
+
return;
|
|
1663
|
+
}
|
|
1664
|
+
const nextState = this.getPersistedState();
|
|
1665
|
+
const serialized = serializePersistedState(nextState);
|
|
1666
|
+
if (serialized === this.lastObservedPersistedState) {
|
|
1667
|
+
return;
|
|
1668
|
+
}
|
|
1669
|
+
this.lastObservedPersistedState = serialized;
|
|
1670
|
+
this.persistence.save(nextState);
|
|
1671
|
+
}
|
|
1672
|
+
resolveErrorMessage(error, overrides = {}) {
|
|
1673
|
+
const filteredOverrides = Object.fromEntries(
|
|
1674
|
+
Object.entries(overrides).filter((entry) => entry[1] !== void 0)
|
|
1675
|
+
);
|
|
1676
|
+
if (this.host.resolveErrorMessage) {
|
|
1677
|
+
return this.host.resolveErrorMessage(error, filteredOverrides);
|
|
1678
|
+
}
|
|
1679
|
+
if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string") {
|
|
1680
|
+
const override = filteredOverrides[error.code];
|
|
1681
|
+
if (override) {
|
|
1682
|
+
return override;
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
return this.copy.t("unknownErrorMessage");
|
|
1686
|
+
}
|
|
1687
|
+
};
|
|
1688
|
+
function serializePersistedState(state) {
|
|
1689
|
+
return JSON.stringify(state);
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
// src/services/internal/workspaceFileManagerService.ts
|
|
1693
|
+
var DefaultWorkspaceFileManagerService = class {
|
|
1694
|
+
createSession(input) {
|
|
1695
|
+
const capabilities = capabilitiesFromHost(input);
|
|
1696
|
+
const store = createWorkspaceFileManagerStore({
|
|
1697
|
+
capabilities,
|
|
1698
|
+
initialDirectoryPath: input.initialDirectoryPath,
|
|
1699
|
+
persistedState: normalizeWorkspaceFileManagerPersistedState(
|
|
1700
|
+
input.persistedState ?? input.persistence?.load?.()
|
|
1701
|
+
),
|
|
1702
|
+
workspaceID: input.workspaceID
|
|
1703
|
+
});
|
|
1704
|
+
return new DefaultWorkspaceFileManagerSession({
|
|
1705
|
+
copy: input.i18n,
|
|
1706
|
+
host: input.host,
|
|
1707
|
+
onHostActionMessage: input.onHostActionMessage,
|
|
1708
|
+
onMutationErrorMessage: input.onMutationErrorMessage,
|
|
1709
|
+
persistence: input.persistence,
|
|
1710
|
+
store
|
|
1711
|
+
});
|
|
1712
|
+
}
|
|
1713
|
+
};
|
|
1714
|
+
function capabilitiesFromHost(input) {
|
|
1715
|
+
return {
|
|
1716
|
+
canCopy: input.host.copyEntriesToClipboard !== void 0,
|
|
1717
|
+
canCreateDirectory: input.host.createDirectory !== void 0,
|
|
1718
|
+
canCreateFile: input.host.createFile !== void 0,
|
|
1719
|
+
canDelete: input.host.deleteEntry !== void 0,
|
|
1720
|
+
canExport: input.host.exportEntry !== void 0,
|
|
1721
|
+
canImportFromDrop: input.host.resolveDroppedPaths !== void 0 && input.host.importPaths !== void 0,
|
|
1722
|
+
canImportFromPicker: input.host.importFiles !== void 0,
|
|
1723
|
+
canMove: input.host.moveEntry !== void 0,
|
|
1724
|
+
canOpenInAppBrowser: input.host.openFileInAppBrowser !== void 0,
|
|
1725
|
+
canOpenInDefaultBrowser: input.host.openFileInDefaultBrowser !== void 0,
|
|
1726
|
+
canOpenWith: input.host.listOpenWithApplications !== void 0,
|
|
1727
|
+
canPickOtherOpenWithApplication: input.host.openFileWithOtherApplication !== void 0,
|
|
1728
|
+
canRevealInFolder: input.host.revealEntry !== void 0,
|
|
1729
|
+
canRename: input.host.renameEntry !== void 0,
|
|
1730
|
+
canSearch: input.host.search !== void 0
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
// src/services/createWorkspaceFileManagerService.ts
|
|
1735
|
+
function createWorkspaceFileManagerService() {
|
|
1736
|
+
return new DefaultWorkspaceFileManagerService();
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
export {
|
|
1740
|
+
classifyWorkspaceFilePreviewKind,
|
|
1741
|
+
resolveWorkspaceFileActivationTarget,
|
|
1742
|
+
decodeWorkspaceTextFile,
|
|
1743
|
+
isWorkspaceFileBrowserOpenable,
|
|
1744
|
+
isWorkspaceTextFileTooLarge,
|
|
1745
|
+
looksLikeBinaryText,
|
|
1746
|
+
resolveWorkspaceFileExtension,
|
|
1747
|
+
resolveWorkspaceFileVisualKind,
|
|
1748
|
+
resolveWorkspaceImageMimeType,
|
|
1749
|
+
workspaceFilePreviewMaxBytes,
|
|
1750
|
+
workspaceFileTextMaxBytes,
|
|
1751
|
+
formatWorkspaceFileBytes,
|
|
1752
|
+
formatWorkspaceFileModifiedTime,
|
|
1753
|
+
splitWorkspaceFileName,
|
|
1754
|
+
normalizeWorkspaceFilePath,
|
|
1755
|
+
workspaceFileName,
|
|
1756
|
+
buildWorkspaceFileBreadcrumbs,
|
|
1757
|
+
createWorkspaceFileManagerService
|
|
1758
|
+
};
|
|
1759
|
+
//# sourceMappingURL=chunk-IMOFXYBB.js.map
|