@eclipse-lyra/core 0.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/dist/api/base-classes.d.ts +7 -0
- package/dist/api/base-classes.d.ts.map +1 -0
- package/dist/api/constants.d.ts +2 -0
- package/dist/api/constants.d.ts.map +1 -0
- package/dist/api/index.d.ts +6 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +84 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/services.d.ts +28 -0
- package/dist/api/services.d.ts.map +1 -0
- package/dist/api/types.d.ts +11 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/commands/editor.d.ts +1 -0
- package/dist/commands/editor.d.ts.map +1 -0
- package/dist/commands/files.d.ts +2 -0
- package/dist/commands/files.d.ts.map +1 -0
- package/dist/commands/global.d.ts +1 -0
- package/dist/commands/global.d.ts.map +1 -0
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/open-view-as-editor.d.ts +2 -0
- package/dist/commands/open-view-as-editor.d.ts.map +1 -0
- package/dist/commands/version-info.d.ts +2 -0
- package/dist/commands/version-info.d.ts.map +1 -0
- package/dist/components/app-selector.d.ts +17 -0
- package/dist/components/app-selector.d.ts.map +1 -0
- package/dist/components/app-switcher.d.ts +13 -0
- package/dist/components/app-switcher.d.ts.map +1 -0
- package/dist/components/command.d.ts +31 -0
- package/dist/components/command.d.ts.map +1 -0
- package/dist/components/extensions.d.ts +32 -0
- package/dist/components/extensions.d.ts.map +1 -0
- package/dist/components/fastviews.d.ts +34 -0
- package/dist/components/fastviews.d.ts.map +1 -0
- package/dist/components/filebrowser.d.ts +45 -0
- package/dist/components/filebrowser.d.ts.map +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/language-selector.d.ts +12 -0
- package/dist/components/language-selector.d.ts.map +1 -0
- package/dist/components/log-terminal.d.ts +36 -0
- package/dist/components/log-terminal.d.ts.map +1 -0
- package/dist/components/part-name.d.ts +12 -0
- package/dist/components/part-name.d.ts.map +1 -0
- package/dist/components/tasks.d.ts +13 -0
- package/dist/components/tasks.d.ts.map +1 -0
- package/dist/contributions/default-ui-contributions.d.ts +2 -0
- package/dist/contributions/default-ui-contributions.d.ts.map +1 -0
- package/dist/contributions/index.d.ts +1 -0
- package/dist/contributions/index.d.ts.map +1 -0
- package/dist/contributions/marketplace-catalog-contributions.d.ts +2 -0
- package/dist/contributions/marketplace-catalog-contributions.d.ts.map +1 -0
- package/dist/core/app-host-config.d.ts +7 -0
- package/dist/core/app-host-config.d.ts.map +1 -0
- package/dist/core/apploader.d.ts +214 -0
- package/dist/core/apploader.d.ts.map +1 -0
- package/dist/core/appstate.d.ts +12 -0
- package/dist/core/appstate.d.ts.map +1 -0
- package/dist/core/commandregistry.d.ts +79 -0
- package/dist/core/commandregistry.d.ts.map +1 -0
- package/dist/core/config.d.ts +15 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/constants.d.ts +22 -0
- package/dist/core/constants.d.ts.map +1 -0
- package/dist/core/contributionregistry.d.ts +53 -0
- package/dist/core/contributionregistry.d.ts.map +1 -0
- package/dist/core/di.d.ts +18 -0
- package/dist/core/di.d.ts.map +1 -0
- package/dist/core/dialogservice.d.ts +33 -0
- package/dist/core/dialogservice.d.ts.map +1 -0
- package/dist/core/editorregistry.d.ts +87 -0
- package/dist/core/editorregistry.d.ts.map +1 -0
- package/dist/core/esmsh-service.d.ts +40 -0
- package/dist/core/esmsh-service.d.ts.map +1 -0
- package/dist/core/events.d.ts +7 -0
- package/dist/core/events.d.ts.map +1 -0
- package/dist/core/events.js +63 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/extensionregistry.d.ts +98 -0
- package/dist/core/extensionregistry.d.ts.map +1 -0
- package/dist/core/filesys/common.d.ts +122 -0
- package/dist/core/filesys/common.d.ts.map +1 -0
- package/dist/core/filesys/fs-access.d.ts +31 -0
- package/dist/core/filesys/fs-access.d.ts.map +1 -0
- package/dist/core/filesys/index.d.ts +5 -0
- package/dist/core/filesys/index.d.ts.map +1 -0
- package/dist/core/filesys/indexeddb.d.ts +41 -0
- package/dist/core/filesys/indexeddb.d.ts.map +1 -0
- package/dist/core/filesys/opfs.d.ts +14 -0
- package/dist/core/filesys/opfs.d.ts.map +1 -0
- package/dist/core/i18n.d.ts +50 -0
- package/dist/core/i18n.d.ts.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/keybindings.d.ts +67 -0
- package/dist/core/keybindings.d.ts.map +1 -0
- package/dist/core/logger.d.ts +44 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/marketplaceregistry.d.ts +25 -0
- package/dist/core/marketplaceregistry.d.ts.map +1 -0
- package/dist/core/packageinfoservice.d.ts +16 -0
- package/dist/core/packageinfoservice.d.ts.map +1 -0
- package/dist/core/persistenceservice.d.ts +6 -0
- package/dist/core/persistenceservice.d.ts.map +1 -0
- package/dist/core/settingsservice.d.ts +54 -0
- package/dist/core/settingsservice.d.ts.map +1 -0
- package/dist/core/signals.d.ts +3 -0
- package/dist/core/signals.d.ts.map +1 -0
- package/dist/core/taskservice.d.ts +20 -0
- package/dist/core/taskservice.d.ts.map +1 -0
- package/dist/core/toast.d.ts +4 -0
- package/dist/core/toast.d.ts.map +1 -0
- package/dist/core/tree-utils.d.ts +16 -0
- package/dist/core/tree-utils.d.ts.map +1 -0
- package/dist/dialogs/confirm-dialog.d.ts +14 -0
- package/dist/dialogs/confirm-dialog.d.ts.map +1 -0
- package/dist/dialogs/index.d.ts +5 -0
- package/dist/dialogs/index.d.ts.map +1 -0
- package/dist/dialogs/info-dialog.d.ts +13 -0
- package/dist/dialogs/info-dialog.d.ts.map +1 -0
- package/dist/dialogs/navigable-info-dialog.d.ts +33 -0
- package/dist/dialogs/navigable-info-dialog.d.ts.map +1 -0
- package/dist/dialogs/prompt-dialog.d.ts +21 -0
- package/dist/dialogs/prompt-dialog.d.ts.map +1 -0
- package/dist/externals/lit.d.ts +21 -0
- package/dist/externals/lit.d.ts.map +1 -0
- package/dist/externals/lit.js +24 -0
- package/dist/externals/lit.js.map +1 -0
- package/dist/externals/third-party.d.ts +7 -0
- package/dist/externals/third-party.d.ts.map +1 -0
- package/dist/externals/third-party.js +7 -0
- package/dist/externals/third-party.js.map +1 -0
- package/dist/externals/webawesome.d.ts +1 -0
- package/dist/externals/webawesome.d.ts.map +1 -0
- package/dist/externals/webawesome.js +75 -0
- package/dist/externals/webawesome.js.map +1 -0
- package/dist/i18n/extensions.json.d.ts +42 -0
- package/dist/i18n/fastviews.json.d.ts +13 -0
- package/dist/i18n/filebrowser.json.d.ts +29 -0
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/logterminal.json.d.ts +45 -0
- package/dist/i18n/partname.json.d.ts +15 -0
- package/dist/i18n/tasks.json.d.ts +15 -0
- package/dist/i18n/workspace.json.d.ts +15 -0
- package/dist/icon-DN6fp0dg.js +487 -0
- package/dist/icon-DN6fp0dg.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +84 -0
- package/dist/index.js.map +1 -0
- package/dist/layouts/standard-layout.d.ts +16 -0
- package/dist/layouts/standard-layout.d.ts.map +1 -0
- package/dist/nocontent-BhrN6yxJ.js +48 -0
- package/dist/nocontent-BhrN6yxJ.js.map +1 -0
- package/dist/parts/container.d.ts +4 -0
- package/dist/parts/container.d.ts.map +1 -0
- package/dist/parts/contextmenu.d.ts +50 -0
- package/dist/parts/contextmenu.d.ts.map +1 -0
- package/dist/parts/dialog-content.d.ts +9 -0
- package/dist/parts/dialog-content.d.ts.map +1 -0
- package/dist/parts/element.d.ts +36 -0
- package/dist/parts/element.d.ts.map +1 -0
- package/dist/parts/index.d.ts +1 -0
- package/dist/parts/index.d.ts.map +1 -0
- package/dist/parts/index.js +3 -0
- package/dist/parts/index.js.map +1 -0
- package/dist/parts/part.d.ts +96 -0
- package/dist/parts/part.d.ts.map +1 -0
- package/dist/parts/resizable-grid.d.ts +31 -0
- package/dist/parts/resizable-grid.d.ts.map +1 -0
- package/dist/parts/tabs.d.ts +75 -0
- package/dist/parts/tabs.d.ts.map +1 -0
- package/dist/parts/toolbar.d.ts +35 -0
- package/dist/parts/toolbar.d.ts.map +1 -0
- package/dist/resizable-grid-BRH3MyZK.js +3813 -0
- package/dist/resizable-grid-BRH3MyZK.js.map +1 -0
- package/dist/standard-layout-BSGa06lP.js +4907 -0
- package/dist/standard-layout-BSGa06lP.js.map +1 -0
- package/dist/widgets/icon.d.ts +10 -0
- package/dist/widgets/icon.d.ts.map +1 -0
- package/dist/widgets/index.d.ts +1 -0
- package/dist/widgets/index.d.ts.map +1 -0
- package/dist/widgets/index.js +3 -0
- package/dist/widgets/index.js.map +1 -0
- package/dist/widgets/nocontent.d.ts +13 -0
- package/dist/widgets/nocontent.d.ts.map +1 -0
- package/dist/widgets/widget.d.ts +25 -0
- package/dist/widgets/widget.d.ts.map +1 -0
- package/package.json +81 -0
- package/src/api/base-classes.ts +10 -0
- package/src/api/constants.ts +3 -0
- package/src/api/index.ts +31 -0
- package/src/api/services.ts +58 -0
- package/src/api/types.ts +46 -0
- package/src/commands/editor.ts +1 -0
- package/src/commands/files.ts +425 -0
- package/src/commands/global.ts +198 -0
- package/src/commands/index.ts +6 -0
- package/src/commands/open-view-as-editor.ts +28 -0
- package/src/commands/version-info.ts +214 -0
- package/src/components/app-selector.ts +233 -0
- package/src/components/app-switcher.ts +126 -0
- package/src/components/command.ts +236 -0
- package/src/components/extensions.ts +615 -0
- package/src/components/fastviews.ts +314 -0
- package/src/components/filebrowser.ts +518 -0
- package/src/components/index.ts +9 -0
- package/src/components/language-selector.ts +166 -0
- package/src/components/log-terminal.ts +337 -0
- package/src/components/part-name.ts +54 -0
- package/src/components/tasks.ts +275 -0
- package/src/contributions/default-ui-contributions.ts +44 -0
- package/src/contributions/index.ts +3 -0
- package/src/contributions/marketplace-catalog-contributions.ts +6 -0
- package/src/core/app-host-config.ts +23 -0
- package/src/core/apploader.ts +630 -0
- package/src/core/appstate.ts +15 -0
- package/src/core/commandregistry.ts +210 -0
- package/src/core/config.ts +29 -0
- package/src/core/constants.ts +29 -0
- package/src/core/contributionregistry.ts +81 -0
- package/src/core/di.ts +54 -0
- package/src/core/dialogservice.ts +266 -0
- package/src/core/editorregistry.ts +347 -0
- package/src/core/esmsh-service.ts +404 -0
- package/src/core/events.ts +68 -0
- package/src/core/extensionregistry.ts +399 -0
- package/src/core/filesys/common.ts +474 -0
- package/src/core/filesys/fs-access.ts +339 -0
- package/src/core/filesys/index.ts +5 -0
- package/src/core/filesys/indexeddb.ts +596 -0
- package/src/core/filesys/opfs.ts +95 -0
- package/src/core/i18n.ts +221 -0
- package/src/core/index.ts +51 -0
- package/src/core/keybindings.ts +274 -0
- package/src/core/logger.ts +187 -0
- package/src/core/marketplaceregistry.ts +197 -0
- package/src/core/packageinfoservice.ts +56 -0
- package/src/core/persistenceservice.ts +46 -0
- package/src/core/settingsservice.ts +191 -0
- package/src/core/signals.ts +18 -0
- package/src/core/taskservice.ts +72 -0
- package/src/core/toast.ts +21 -0
- package/src/core/tree-utils.ts +24 -0
- package/src/dialogs/confirm-dialog.ts +72 -0
- package/src/dialogs/index.ts +4 -0
- package/src/dialogs/info-dialog.ts +67 -0
- package/src/dialogs/navigable-info-dialog.ts +256 -0
- package/src/dialogs/prompt-dialog.ts +123 -0
- package/src/externals/lit.ts +36 -0
- package/src/externals/third-party.ts +10 -0
- package/src/externals/webawesome.ts +76 -0
- package/src/i18n/extensions.json +39 -0
- package/src/i18n/fastviews.json +10 -0
- package/src/i18n/filebrowser.json +27 -0
- package/src/i18n/index.ts +25 -0
- package/src/i18n/logterminal.json +42 -0
- package/src/i18n/partname.json +12 -0
- package/src/i18n/tasks.json +12 -0
- package/src/i18n/workspace.json +12 -0
- package/src/icons/icons.txt +3 -0
- package/src/icons/js.svg +6 -0
- package/src/icons/jupyter.svg +18 -0
- package/src/icons/python.svg +15 -0
- package/src/index.ts +3 -0
- package/src/layouts/standard-layout.ts +174 -0
- package/src/parts/container.ts +4 -0
- package/src/parts/contextmenu.ts +266 -0
- package/src/parts/dialog-content.ts +31 -0
- package/src/parts/element.ts +100 -0
- package/src/parts/index.ts +5 -0
- package/src/parts/part.ts +158 -0
- package/src/parts/resizable-grid.ts +366 -0
- package/src/parts/tabs.ts +581 -0
- package/src/parts/toolbar.ts +260 -0
- package/src/vite-env.d.ts +16 -0
- package/src/widgets/icon.ts +38 -0
- package/src/widgets/index.ts +2 -0
- package/src/widgets/nocontent.ts +40 -0
- package/src/widgets/widget.ts +92 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
import { persistenceService } from "../persistenceservice";
|
|
2
|
+
import { publish } from "../events";
|
|
3
|
+
import { rootContext } from "../di";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export const TOPIC_WORKSPACE_CHANGED = "events/filesys/workspaceChanged"
|
|
7
|
+
export const TOPIC_WORKSPACE_CONNECTED = "events/filesys/workspaceConnected"
|
|
8
|
+
|
|
9
|
+
export abstract class Resource {
|
|
10
|
+
public state: { [p: string]: any } = {};
|
|
11
|
+
|
|
12
|
+
public abstract getName(): string;
|
|
13
|
+
|
|
14
|
+
public abstract getParent(): Directory | undefined;
|
|
15
|
+
|
|
16
|
+
public abstract delete(name?: string, recursive?: boolean): Promise<void>;
|
|
17
|
+
|
|
18
|
+
public abstract copyTo(targetPath: string): Promise<void>;
|
|
19
|
+
|
|
20
|
+
public abstract rename(newName: string): Promise<void>;
|
|
21
|
+
|
|
22
|
+
public getWorkspacePath(): string {
|
|
23
|
+
const paths: string[] = []
|
|
24
|
+
let current: Resource | undefined = this
|
|
25
|
+
let root: Directory | undefined
|
|
26
|
+
while (current) {
|
|
27
|
+
paths.push(current.getName())
|
|
28
|
+
const parent = current.getParent()
|
|
29
|
+
if (!parent) root = current as Directory
|
|
30
|
+
current = parent
|
|
31
|
+
}
|
|
32
|
+
paths.reverse()
|
|
33
|
+
const workspace = typeof workspaceService?.getWorkspaceSync === 'function' ? workspaceService.getWorkspaceSync() : undefined
|
|
34
|
+
if (workspace && root && 'isDirectChild' in workspace && typeof (workspace as any).isDirectChild === 'function' && (workspace as any).isDirectChild(root)) {
|
|
35
|
+
const folderName = (workspace as any).getFolderNameForDirectory(root)
|
|
36
|
+
if (folderName && paths.length > 0) return paths.length > 1 ? folderName + '/' + paths.slice(1).join('/') : folderName
|
|
37
|
+
}
|
|
38
|
+
paths.shift()
|
|
39
|
+
return paths.join("/");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public getWorkspace(): Directory {
|
|
43
|
+
let current: Resource | undefined = this
|
|
44
|
+
while (current) {
|
|
45
|
+
const parent: any = current.getParent()
|
|
46
|
+
if (parent) {
|
|
47
|
+
current = parent
|
|
48
|
+
} else {
|
|
49
|
+
break
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return current as Directory
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const resourceComparator = (c1: Resource, c2: Resource) => {
|
|
57
|
+
if (c1 instanceof Directory && c2 instanceof File) {
|
|
58
|
+
return -1
|
|
59
|
+
}
|
|
60
|
+
if (c1 instanceof File && c2 instanceof Directory) {
|
|
61
|
+
return 1
|
|
62
|
+
}
|
|
63
|
+
return c1.getName().localeCompare(c2.getName())
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export enum FileContentType {
|
|
67
|
+
TEXT, BINARY
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export enum FileContentEncoding {
|
|
71
|
+
BASE64
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface FileContentsOptions {
|
|
75
|
+
contentType?: FileContentType;
|
|
76
|
+
encoding?: FileContentEncoding;
|
|
77
|
+
uri?: boolean;
|
|
78
|
+
blob?: boolean;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface GetResourceOptions {
|
|
82
|
+
create?: boolean
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export abstract class File extends Resource {
|
|
86
|
+
public abstract getContents(options?: FileContentsOptions): Promise<any>;
|
|
87
|
+
|
|
88
|
+
public abstract saveContents(contents: any, options?: FileContentsOptions): Promise<void>;
|
|
89
|
+
|
|
90
|
+
public abstract size(): Promise<number | null>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export abstract class Directory extends Resource {
|
|
94
|
+
public abstract listChildren(forceRefresh: boolean): Promise<Resource[]>;
|
|
95
|
+
|
|
96
|
+
public abstract getResource(path: string, options?: GetResourceOptions): Promise<Resource | null>;
|
|
97
|
+
|
|
98
|
+
public abstract touch(): void;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export class StringFile extends File {
|
|
102
|
+
private contents: string;
|
|
103
|
+
private name: string;
|
|
104
|
+
|
|
105
|
+
constructor(contents: string, name: string) {
|
|
106
|
+
super();
|
|
107
|
+
this.contents = contents
|
|
108
|
+
this.name = name
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async getContents(_options?: FileContentsOptions): Promise<any> {
|
|
112
|
+
return this.contents
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async saveContents(contents: any, _options?: FileContentsOptions): Promise<void> {
|
|
116
|
+
this.contents = contents
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async size(): Promise<number | null> {
|
|
120
|
+
return this.contents.length || null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async copyTo(_targetPath: string): Promise<void> {
|
|
124
|
+
throw Error(`Not supported`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
delete(_name?: string, _recursive?: boolean): Promise<void> {
|
|
128
|
+
throw Error(`Not supported`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async rename(_newName: string): Promise<void> {
|
|
132
|
+
throw Error(`Not supported`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
getName(): string {
|
|
136
|
+
return this.name;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
getParent(): Directory | undefined {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface ResourceMap {
|
|
145
|
+
[key: string]: Resource;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
export class CompositeDirectory extends Directory {
|
|
150
|
+
private readonly entriesByName: Map<string, Directory>;
|
|
151
|
+
|
|
152
|
+
constructor(
|
|
153
|
+
directories: Directory[],
|
|
154
|
+
private readonly displayName: string = '/'
|
|
155
|
+
) {
|
|
156
|
+
super();
|
|
157
|
+
this.entriesByName = new Map(directories.map(d => [d.getName(), d]));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
getFolderNameForDirectory(dir: Directory): string | undefined {
|
|
161
|
+
for (const [name, d] of this.entriesByName) {
|
|
162
|
+
if (d === dir) return name;
|
|
163
|
+
}
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
isDirectChild(dir: Directory): boolean {
|
|
168
|
+
return this.getFolderNameForDirectory(dir) !== undefined;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
getName(): string {
|
|
172
|
+
return this.displayName;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
getParent(): Directory | undefined {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async listChildren(_forceRefresh: boolean): Promise<Resource[]> {
|
|
180
|
+
return Array.from(this.entriesByName.values());
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async getResource(path: string, options?: GetResourceOptions): Promise<Resource | null> {
|
|
184
|
+
if (!path || !path.trim()) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
const idx = path.indexOf('/');
|
|
188
|
+
const folderName = idx >= 0 ? path.slice(0, idx).trim() : path.trim();
|
|
189
|
+
const rest = idx >= 0 ? path.slice(idx + 1).trim() : '';
|
|
190
|
+
const dir = this.entriesByName.get(folderName);
|
|
191
|
+
if (!dir) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
if (!rest) {
|
|
195
|
+
return dir;
|
|
196
|
+
}
|
|
197
|
+
return dir.getResource(rest, options);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
touch(): void {
|
|
201
|
+
for (const dir of this.entriesByName.values()) {
|
|
202
|
+
dir.touch();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async delete(_name?: string, _recursive?: boolean): Promise<void> {
|
|
207
|
+
throw new Error('Delete not supported on workspace root');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async copyTo(_targetPath: string): Promise<void> {
|
|
211
|
+
throw new Error('Copy not supported on workspace root');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async rename(_newName: string): Promise<void> {
|
|
215
|
+
throw new Error('Rename not supported on workspace root');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
getFolderByName(name: string): Directory | undefined {
|
|
219
|
+
return this.entriesByName.get(name);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export interface WorkspaceContribution {
|
|
224
|
+
type: string;
|
|
225
|
+
name: string;
|
|
226
|
+
canHandle(input: any): boolean;
|
|
227
|
+
connect(input: any): Promise<Directory>;
|
|
228
|
+
restore?(data: any): Promise<Directory | undefined>;
|
|
229
|
+
persist?(workspace: Directory): Promise<any>;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
interface PersistedWorkspaceData {
|
|
233
|
+
type?: string;
|
|
234
|
+
data?: any;
|
|
235
|
+
folders?: Array<{ type: string; data: any }>;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const LEGACY_WORKSPACE_KEY = "workspace_data";
|
|
239
|
+
|
|
240
|
+
export class WorkspaceService {
|
|
241
|
+
private workspace?: Promise<Directory | undefined>;
|
|
242
|
+
private _currentWorkspace: Directory | undefined = undefined;
|
|
243
|
+
private folders: Array<{ type: string; data: any; directory: Directory }> = [];
|
|
244
|
+
private currentType?: string;
|
|
245
|
+
private contributions: Map<string, WorkspaceContribution> = new Map();
|
|
246
|
+
private initPromise: Promise<void>;
|
|
247
|
+
|
|
248
|
+
getWorkspaceSync(): Directory | undefined {
|
|
249
|
+
return this._currentWorkspace;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
constructor() {
|
|
253
|
+
let resolveInit!: () => void;
|
|
254
|
+
this.initPromise = new Promise<void>((resolve) => {
|
|
255
|
+
resolveInit = resolve;
|
|
256
|
+
});
|
|
257
|
+
this.loadPersistedWorkspace(resolveInit);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
registerContribution(contribution: WorkspaceContribution): void {
|
|
261
|
+
this.contributions.set(contribution.type, contribution);
|
|
262
|
+
console.log(`Workspace contribution registered: ${contribution.name} (${contribution.type})`);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
getContributions(): WorkspaceContribution[] {
|
|
266
|
+
return Array.from(this.contributions.values());
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private static readonly DEFAULT_INDEXEDDB_FOLDER_NAME = 'My Folder';
|
|
270
|
+
|
|
271
|
+
private async loadPersistedWorkspace(resolveInit: () => void): Promise<void> {
|
|
272
|
+
try {
|
|
273
|
+
const raw = await persistenceService.getObject(LEGACY_WORKSPACE_KEY) as PersistedWorkspaceData | null;
|
|
274
|
+
if (!raw) {
|
|
275
|
+
this.workspace = Promise.resolve(undefined);
|
|
276
|
+
this._currentWorkspace = undefined;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (raw?.folders && Array.isArray(raw.folders) && raw.folders.length > 0) {
|
|
280
|
+
const normalized = raw.folders.map((f: { type: string; data: any }) => ({ type: f.type, data: f.data }));
|
|
281
|
+
await this.resolveFolders(normalized);
|
|
282
|
+
const composite = this.buildComposite();
|
|
283
|
+
this.workspace = Promise.resolve(composite);
|
|
284
|
+
this._currentWorkspace = composite ?? undefined;
|
|
285
|
+
if (composite) {
|
|
286
|
+
publish(TOPIC_WORKSPACE_CONNECTED, composite);
|
|
287
|
+
}
|
|
288
|
+
resolveInit();
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (raw && raw.type && raw.data !== undefined) {
|
|
293
|
+
const contribution = this.contributions.get(raw.type);
|
|
294
|
+
if (contribution?.restore) {
|
|
295
|
+
try {
|
|
296
|
+
const dir = await contribution.restore!(raw.data);
|
|
297
|
+
if (dir) {
|
|
298
|
+
this.folders = [{ type: raw.type, data: raw.data, directory: dir }];
|
|
299
|
+
const comp = this.buildComposite();
|
|
300
|
+
this.workspace = Promise.resolve(comp);
|
|
301
|
+
this._currentWorkspace = comp ?? undefined;
|
|
302
|
+
this.currentType = raw.type;
|
|
303
|
+
await this.persistFolders();
|
|
304
|
+
publish(TOPIC_WORKSPACE_CONNECTED, comp);
|
|
305
|
+
}
|
|
306
|
+
} catch (error) {
|
|
307
|
+
console.error('Failed to restore legacy workspace:', error);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (!this.workspace) {
|
|
312
|
+
this.workspace = Promise.resolve(undefined);
|
|
313
|
+
this._currentWorkspace = undefined;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
resolveInit();
|
|
317
|
+
} finally {
|
|
318
|
+
if (this.folders.length === 0) {
|
|
319
|
+
try {
|
|
320
|
+
await this.connectFolder({ indexeddb: true, name: WorkspaceService.DEFAULT_INDEXEDDB_FOLDER_NAME });
|
|
321
|
+
} catch (e) {
|
|
322
|
+
console.warn('Failed to connect default IndexedDB folder', e);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private async resolveFolders(persisted: Array<{ type: string; data: any }>): Promise<void> {
|
|
329
|
+
this.folders = [];
|
|
330
|
+
for (const folder of persisted) {
|
|
331
|
+
const contribution = this.contributions.get(folder.type);
|
|
332
|
+
if (!contribution?.restore) {
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
try {
|
|
336
|
+
const dir = await contribution.restore(folder.data);
|
|
337
|
+
if (dir) {
|
|
338
|
+
this.folders.push({ type: folder.type, data: folder.data, directory: dir });
|
|
339
|
+
}
|
|
340
|
+
} catch (error) {
|
|
341
|
+
console.warn(`Failed to restore folder (${folder.type}):`, error);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
private buildComposite(): CompositeDirectory | undefined {
|
|
347
|
+
if (this.folders.length === 0) {
|
|
348
|
+
return undefined;
|
|
349
|
+
}
|
|
350
|
+
return new CompositeDirectory(this.folders.map(f => f.directory));
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
private async persistFolders(): Promise<void> {
|
|
354
|
+
const toPersist = this.folders.length > 0
|
|
355
|
+
? { folders: this.folders.map(f => ({ type: f.type, data: f.data })) }
|
|
356
|
+
: null;
|
|
357
|
+
await persistenceService.persistObject(LEGACY_WORKSPACE_KEY, toPersist);
|
|
358
|
+
await persistenceService.persistObject("workspace", null);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async getFolders(): Promise<Array<{ name: string; type: string }>> {
|
|
362
|
+
await this.initPromise;
|
|
363
|
+
return this.folders.map(f => ({ name: f.directory.getName(), type: f.type }));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async getFolderInfoForDirectory(directory: Directory): Promise<{ name: string; type: string; backendName: string } | undefined> {
|
|
367
|
+
await this.initPromise;
|
|
368
|
+
const folder = this.folders.find(f => f.directory === directory);
|
|
369
|
+
if (!folder) {
|
|
370
|
+
return undefined;
|
|
371
|
+
}
|
|
372
|
+
const name =
|
|
373
|
+
(folder.data && typeof folder.data === 'object' && (folder.data as any).name) ||
|
|
374
|
+
folder.directory.getName();
|
|
375
|
+
const contribution = this.contributions.get(folder.type);
|
|
376
|
+
const backendName = contribution?.name ?? folder.type;
|
|
377
|
+
return { name, type: folder.type, backendName };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Update persisted metadata for a workspace root directory.
|
|
382
|
+
* Currently used to keep display names of roots (e.g. IndexedDB) in sync
|
|
383
|
+
* with their in-memory Directory instances.
|
|
384
|
+
*/
|
|
385
|
+
async updateFolderName(directory: Directory, name: string): Promise<void> {
|
|
386
|
+
await this.initPromise;
|
|
387
|
+
const folder = this.folders.find(f => f.directory === directory);
|
|
388
|
+
if (!folder) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
if (folder.data && typeof folder.data === 'object') {
|
|
392
|
+
folder.data = { ...(folder.data as any), name };
|
|
393
|
+
} else {
|
|
394
|
+
folder.data = { name };
|
|
395
|
+
}
|
|
396
|
+
await this.persistFolders();
|
|
397
|
+
const composite = this.buildComposite();
|
|
398
|
+
this.workspace = Promise.resolve(composite);
|
|
399
|
+
this._currentWorkspace = composite ?? undefined;
|
|
400
|
+
publish(TOPIC_WORKSPACE_CONNECTED, composite);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async connectFolder(input: any): Promise<Directory> {
|
|
404
|
+
await this.initPromise;
|
|
405
|
+
const contribution = Array.from(this.contributions.values()).find(c => c.canHandle(input));
|
|
406
|
+
if (!contribution) {
|
|
407
|
+
throw new Error('No workspace contribution can handle this input');
|
|
408
|
+
}
|
|
409
|
+
const directory = await contribution.connect(input);
|
|
410
|
+
const data = contribution.persist ? await contribution.persist(directory) : input;
|
|
411
|
+
this.folders.push({ type: contribution.type, data, directory });
|
|
412
|
+
await this.persistFolders();
|
|
413
|
+
this.currentType = this.folders.length === 1 ? contribution.type : undefined;
|
|
414
|
+
const composite = this.buildComposite()!;
|
|
415
|
+
this.workspace = Promise.resolve(composite);
|
|
416
|
+
this._currentWorkspace = composite;
|
|
417
|
+
publish(TOPIC_WORKSPACE_CONNECTED, composite);
|
|
418
|
+
return composite;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
async disconnectFolder(directory: Directory): Promise<void> {
|
|
422
|
+
await this.initPromise;
|
|
423
|
+
const idx = this.folders.findIndex(f => f.directory === directory);
|
|
424
|
+
if (idx < 0) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
this.folders.splice(idx, 1);
|
|
428
|
+
await this.persistFolders();
|
|
429
|
+
if (this.folders.length > 0) {
|
|
430
|
+
this.currentType = this.folders[0].type;
|
|
431
|
+
} else {
|
|
432
|
+
this.currentType = undefined;
|
|
433
|
+
}
|
|
434
|
+
const composite = this.buildComposite();
|
|
435
|
+
this.workspace = Promise.resolve(composite);
|
|
436
|
+
this._currentWorkspace = composite ?? undefined;
|
|
437
|
+
publish(TOPIC_WORKSPACE_CONNECTED, composite);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async connectWorkspace(input: any): Promise<Directory> {
|
|
441
|
+
return this.connectFolder(input);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
public async getWorkspace(): Promise<Directory | undefined> {
|
|
445
|
+
await this.initPromise;
|
|
446
|
+
if (!this.workspace) {
|
|
447
|
+
throw new Error('No workspace connected.');
|
|
448
|
+
}
|
|
449
|
+
return await this.workspace;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
public isConnected(): boolean {
|
|
453
|
+
return this.folders.length > 0;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
public getWorkspaceType(): string | undefined {
|
|
457
|
+
return this.currentType;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
public async disconnectWorkspace(): Promise<void> {
|
|
461
|
+
await this.initPromise;
|
|
462
|
+
this.workspace = Promise.resolve(undefined);
|
|
463
|
+
this._currentWorkspace = undefined;
|
|
464
|
+
this.folders = [];
|
|
465
|
+
this.currentType = undefined;
|
|
466
|
+
await this.persistFolders();
|
|
467
|
+
publish(TOPIC_WORKSPACE_CONNECTED, undefined as any);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
export const workspaceService = new WorkspaceService();
|
|
472
|
+
rootContext.put("workspaceService", workspaceService);
|
|
473
|
+
|
|
474
|
+
|