@kispace-io/core 0.7.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 +80 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/services.d.ts +27 -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/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/version-info.d.ts +2 -0
- package/dist/commands/version-info.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/k-app-selector.d.ts +17 -0
- package/dist/components/k-app-selector.d.ts.map +1 -0
- package/dist/components/k-app-switcher.d.ts +13 -0
- package/dist/components/k-app-switcher.d.ts.map +1 -0
- package/dist/components/k-command.d.ts +31 -0
- package/dist/components/k-command.d.ts.map +1 -0
- package/dist/components/k-extensions.d.ts +32 -0
- package/dist/components/k-extensions.d.ts.map +1 -0
- package/dist/components/k-fastviews.d.ts +34 -0
- package/dist/components/k-fastviews.d.ts.map +1 -0
- package/dist/components/k-filebrowser.d.ts +40 -0
- package/dist/components/k-filebrowser.d.ts.map +1 -0
- package/dist/components/k-language-selector.d.ts +12 -0
- package/dist/components/k-language-selector.d.ts.map +1 -0
- package/dist/components/k-log-terminal.d.ts +36 -0
- package/dist/components/k-log-terminal.d.ts.map +1 -0
- package/dist/components/k-part-name.d.ts +12 -0
- package/dist/components/k-part-name.d.ts.map +1 -0
- package/dist/components/k-tasks.d.ts +13 -0
- package/dist/components/k-tasks.d.ts.map +1 -0
- package/dist/components/k-workspace-name.d.ts +14 -0
- package/dist/components/k-workspace-name.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 +21 -0
- package/dist/core/constants.d.ts.map +1 -0
- package/dist/core/contributionregistry.d.ts +49 -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 +73 -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.d.ts +139 -0
- package/dist/core/filesys.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/k-utils.d.ts +2 -0
- package/dist/core/k-utils.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 +19 -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 +20 -0
- package/dist/externals/lit.d.ts.map +1 -0
- package/dist/externals/lit.js +15 -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 +2 -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 +52 -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 +35 -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/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +80 -0
- package/dist/index.js.map +1 -0
- package/dist/k-icon-BZC7dQV0.js +492 -0
- package/dist/k-icon-BZC7dQV0.js.map +1 -0
- package/dist/k-nocontent-Bh_yToGh.js +48 -0
- package/dist/k-nocontent-Bh_yToGh.js.map +1 -0
- package/dist/k-resizable-grid-Ch3iWZaL.js +3157 -0
- package/dist/k-resizable-grid-Ch3iWZaL.js.map +1 -0
- package/dist/k-standard-layout-CQ1VZoxa.js +5011 -0
- package/dist/k-standard-layout-CQ1VZoxa.js.map +1 -0
- package/dist/layouts/k-standard-layout.d.ts +16 -0
- package/dist/layouts/k-standard-layout.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 +53 -0
- package/dist/parts/index.js.map +1 -0
- package/dist/parts/k-app.d.ts +11 -0
- package/dist/parts/k-app.d.ts.map +1 -0
- package/dist/parts/k-container.d.ts +4 -0
- package/dist/parts/k-container.d.ts.map +1 -0
- package/dist/parts/k-contextmenu.d.ts +38 -0
- package/dist/parts/k-contextmenu.d.ts.map +1 -0
- package/dist/parts/k-dialog-content.d.ts +9 -0
- package/dist/parts/k-dialog-content.d.ts.map +1 -0
- package/dist/parts/k-element.d.ts +36 -0
- package/dist/parts/k-element.d.ts.map +1 -0
- package/dist/parts/k-part.d.ts +96 -0
- package/dist/parts/k-part.d.ts.map +1 -0
- package/dist/parts/k-resizable-grid.d.ts +31 -0
- package/dist/parts/k-resizable-grid.d.ts.map +1 -0
- package/dist/parts/k-tabs.d.ts +74 -0
- package/dist/parts/k-tabs.d.ts.map +1 -0
- package/dist/parts/k-toolbar.d.ts +21 -0
- package/dist/parts/k-toolbar.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/k-icon.d.ts +10 -0
- package/dist/widgets/k-icon.d.ts.map +1 -0
- package/dist/widgets/k-nocontent.d.ts +13 -0
- package/dist/widgets/k-nocontent.d.ts.map +1 -0
- package/dist/widgets/k-widget.d.ts +25 -0
- package/dist/widgets/k-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 +52 -0
- package/src/api/types.ts +46 -0
- package/src/commands/files.ts +829 -0
- package/src/commands/global.ts +225 -0
- package/src/commands/index.ts +4 -0
- package/src/commands/version-info.ts +214 -0
- package/src/components/index.ts +10 -0
- package/src/components/k-app-selector.ts +233 -0
- package/src/components/k-app-switcher.ts +126 -0
- package/src/components/k-command.ts +236 -0
- package/src/components/k-extensions.ts +615 -0
- package/src/components/k-fastviews.ts +314 -0
- package/src/components/k-filebrowser.ts +442 -0
- package/src/components/k-language-selector.ts +166 -0
- package/src/components/k-log-terminal.ts +337 -0
- package/src/components/k-part-name.ts +54 -0
- package/src/components/k-tasks.ts +267 -0
- package/src/components/k-workspace-name.ts +56 -0
- package/src/contributions/default-ui-contributions.ts +51 -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 +27 -0
- package/src/core/contributionregistry.ts +77 -0
- package/src/core/di.ts +54 -0
- package/src/core/dialogservice.ts +266 -0
- package/src/core/editorregistry.ts +303 -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.ts +618 -0
- package/src/core/i18n.ts +221 -0
- package/src/core/index.ts +51 -0
- package/src/core/k-utils.ts +11 -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 +15 -0
- package/src/core/settingsservice.ts +70 -0
- package/src/core/signals.ts +18 -0
- package/src/core/taskservice.ts +72 -0
- package/src/core/toast.ts +11 -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 +26 -0
- package/src/externals/third-party.ts +9 -0
- package/src/externals/webawesome.ts +54 -0
- package/src/i18n/extensions.json +39 -0
- package/src/i18n/fastviews.json +10 -0
- package/src/i18n/filebrowser.json +33 -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/k-standard-layout.ts +174 -0
- package/src/parts/index.ts +6 -0
- package/src/parts/k-app.ts +29 -0
- package/src/parts/k-container.ts +4 -0
- package/src/parts/k-contextmenu.ts +245 -0
- package/src/parts/k-dialog-content.ts +31 -0
- package/src/parts/k-element.ts +100 -0
- package/src/parts/k-part.ts +158 -0
- package/src/parts/k-resizable-grid.ts +366 -0
- package/src/parts/k-tabs.ts +574 -0
- package/src/parts/k-toolbar.ts +158 -0
- package/src/vite-env.d.ts +2 -0
- package/src/widgets/index.ts +2 -0
- package/src/widgets/k-icon.ts +39 -0
- package/src/widgets/k-nocontent.ts +40 -0
- package/src/widgets/k-widget.ts +90 -0
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
|
|
2
|
+
import { css, html, TemplateResult } from 'lit'
|
|
3
|
+
import { customElement, state } from 'lit/decorators.js'
|
|
4
|
+
import { KPart } from "../parts/k-part";
|
|
5
|
+
import {
|
|
6
|
+
Directory,
|
|
7
|
+
File,
|
|
8
|
+
Resource,
|
|
9
|
+
TOPIC_WORKSPACE_CHANGED,
|
|
10
|
+
TOPIC_WORKSPACE_CONNECTED,
|
|
11
|
+
workspaceService
|
|
12
|
+
} from "../core/filesys";
|
|
13
|
+
import { when } from "lit/directives/when.js";
|
|
14
|
+
import { subscribe } from "../core/events";
|
|
15
|
+
import { createRef, ref } from "lit/directives/ref.js";
|
|
16
|
+
import { HIDE_DOT_RESOURCE } from "../core/constants";
|
|
17
|
+
|
|
18
|
+
import { commandRegistry } from "../core/commandregistry";
|
|
19
|
+
import { TreeNode, treeNodeComparator } from "../core/tree-utils";
|
|
20
|
+
import { activeSelectionSignal } from "../core/appstate";
|
|
21
|
+
import { confirmDialog } from "../dialogs";
|
|
22
|
+
import { editorRegistry } from "../core/editorregistry";
|
|
23
|
+
import { TOPIC_CONTRIBUTEIONS_CHANGED, type ContributionChangeEvent } from '../core/contributionregistry';
|
|
24
|
+
import { i18n } from '../core/i18n';
|
|
25
|
+
|
|
26
|
+
const t = i18n('filebrowser');
|
|
27
|
+
|
|
28
|
+
@customElement('k-filebrowser')
|
|
29
|
+
export class KFileBrowser extends KPart {
|
|
30
|
+
|
|
31
|
+
@state()
|
|
32
|
+
private root?: TreeNode;
|
|
33
|
+
private workspaceDir?: Directory
|
|
34
|
+
private treeRef = createRef<HTMLElement>();
|
|
35
|
+
private loadingNodes = new Set<TreeNode>();
|
|
36
|
+
|
|
37
|
+
protected doBeforeUI() {
|
|
38
|
+
this.initializeWorkspace();
|
|
39
|
+
|
|
40
|
+
subscribe(TOPIC_CONTRIBUTEIONS_CHANGED, (event: ContributionChangeEvent) => {
|
|
41
|
+
if (event.target === 'system.icons') {
|
|
42
|
+
this.requestUpdate();
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
this.subscribe(TOPIC_WORKSPACE_CHANGED, (workspaceDir: Directory) => this.onWorkspaceChanged(workspaceDir));
|
|
47
|
+
this.subscribe(TOPIC_WORKSPACE_CONNECTED, (workspaceDir: Directory) => this.onWorkspaceConnected(workspaceDir));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
protected firstUpdated(changedProperties: Map<string, any>) {
|
|
51
|
+
super.firstUpdated(changedProperties);
|
|
52
|
+
this.setupDragAndDrop();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
protected updated(changedProperties: Map<string, any>) {
|
|
56
|
+
super.updated(changedProperties);
|
|
57
|
+
if (changedProperties.has('workspaceDir') && this.workspaceDir) {
|
|
58
|
+
this.setupDragAndDrop();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private async initializeWorkspace() {
|
|
63
|
+
const workspaceDir = await workspaceService.getWorkspace()
|
|
64
|
+
if (workspaceDir) {
|
|
65
|
+
await this.loadWorkspace(workspaceDir!)
|
|
66
|
+
} else {
|
|
67
|
+
commandRegistry.execute("help")
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
protected renderToolbar() {
|
|
72
|
+
const canModify = activeSelectionSignal.get() instanceof Resource;
|
|
73
|
+
|
|
74
|
+
return html`
|
|
75
|
+
<k-command icon="folder-open" title="${t('CONNECT_WORKSPACE')}" dropdown="filebrowser.connections"></k-command>
|
|
76
|
+
<k-command cmd="reload_workspace" icon="repeat" title="${t('RELOAD_WORKSPACE')}"></k-command>
|
|
77
|
+
<k-command cmd="create_file" icon="plus" title="${t('CREATE_NEW')}" dropdown="filebrowser.create"></k-command>
|
|
78
|
+
<k-command cmd="rename_resource" icon="pen" ?disabled=${!canModify} title="${t('RENAME_RESOURCE')}"></k-command>
|
|
79
|
+
<k-command cmd="delete_resource" icon="trash" ?disabled=${!canModify} title="${t('DELETE_RESOURCE')}"></k-command>
|
|
80
|
+
`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
protected renderContextMenu() {
|
|
84
|
+
const canModify = activeSelectionSignal.get() instanceof Resource;
|
|
85
|
+
|
|
86
|
+
return html`
|
|
87
|
+
<k-command cmd="open_editor" icon="folder-open">${t('OPEN')}</k-command>
|
|
88
|
+
<k-command cmd="create_file" icon="plus" dropdown="filebrowser.create">${t('CREATE_NEW')}</k-command>
|
|
89
|
+
<k-command cmd="rename_resource" icon="pen" ?disabled=${!canModify}>${t('RENAME')}</k-command>
|
|
90
|
+
<k-command cmd="delete_resource" icon="trash" ?disabled=${!canModify}>${t('DELETE')}</k-command>
|
|
91
|
+
`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async onWorkspaceChanged(workspaceDir: Directory) {
|
|
95
|
+
activeSelectionSignal.set(undefined)
|
|
96
|
+
await this.loadWorkspace(workspaceDir)
|
|
97
|
+
await this.syncTreeSelection()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async onWorkspaceConnected(workspaceDir: Directory) {
|
|
101
|
+
await this.loadWorkspace(workspaceDir)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async loadWorkspace(workspaceDir?: Directory) {
|
|
105
|
+
this.workspaceDir = workspaceDir
|
|
106
|
+
if (!workspaceDir) {
|
|
107
|
+
this.root = undefined
|
|
108
|
+
} else {
|
|
109
|
+
this.root = await this.resourceToTreeNode(workspaceDir, true)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private async syncTreeSelection() {
|
|
114
|
+
await this.updateComplete
|
|
115
|
+
const waTree = this.treeRef.value?.querySelector('wa-tree')
|
|
116
|
+
// @ts-ignore
|
|
117
|
+
const selectedItems = waTree?.selectedItems || []
|
|
118
|
+
if (selectedItems.length > 0) {
|
|
119
|
+
// @ts-ignore
|
|
120
|
+
activeSelectionSignal.set(selectedItems[0].model?.data)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async resourceToTreeNode(resource: Resource, loadChildren: boolean = false): Promise<TreeNode> {
|
|
125
|
+
const isFile = resource instanceof File;
|
|
126
|
+
const node: TreeNode = {
|
|
127
|
+
data: resource,
|
|
128
|
+
label: resource.getName(),
|
|
129
|
+
leaf: isFile,
|
|
130
|
+
children: []
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
if (resource instanceof Directory && loadChildren) {
|
|
134
|
+
for (const childResource of await resource.listChildren(false)) {
|
|
135
|
+
if (HIDE_DOT_RESOURCE && childResource.getName().startsWith(".")) {
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
const child = await this.resourceToTreeNode(childResource, false);
|
|
139
|
+
node.children.push(child);
|
|
140
|
+
}
|
|
141
|
+
node.children.sort(treeNodeComparator)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return node;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
createTreeItems(node: TreeNode, expanded = false): TemplateResult {
|
|
148
|
+
if (!node) {
|
|
149
|
+
return html``
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const isLazy = !node.leaf && node.children.length === 0;
|
|
153
|
+
const resource = node.data as Resource;
|
|
154
|
+
const isFile = resource instanceof File;
|
|
155
|
+
const icon = isFile
|
|
156
|
+
? editorRegistry.getFileIcon(resource.getName())
|
|
157
|
+
: (node.icon || "folder-open");
|
|
158
|
+
|
|
159
|
+
return html`
|
|
160
|
+
<wa-tree-item
|
|
161
|
+
draggable=${isFile}
|
|
162
|
+
@dragstart=${isFile ? this.nobubble((e: Event) => this.onDragStart(e as DragEvent, resource as File)) : undefined}
|
|
163
|
+
@dblclick=${this.nobubble(this.onFileDoubleClicked)}
|
|
164
|
+
@wa-lazy-load=${this.nobubble((e: Event) => this.onLazyLoad(e, node))}
|
|
165
|
+
.model=${node}
|
|
166
|
+
?expanded=${expanded}
|
|
167
|
+
?lazy=${isLazy}>
|
|
168
|
+
<span><wa-icon name=${icon} label="${node.leaf ? t('FILE') : t('FOLDER')}"></wa-icon> ${node.label}</span>
|
|
169
|
+
${node.children.map(child => this.createTreeItems(child, false))}
|
|
170
|
+
</wa-tree-item>`
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private onDragStart(e: DragEvent, file: File) {
|
|
174
|
+
if (!e.dataTransfer) return;
|
|
175
|
+
|
|
176
|
+
const filePath = file.getWorkspacePath();
|
|
177
|
+
const fileName = file.getName();
|
|
178
|
+
|
|
179
|
+
e.dataTransfer.effectAllowed = 'copy';
|
|
180
|
+
e.dataTransfer.setData('text/plain', filePath);
|
|
181
|
+
e.dataTransfer.setData('application/x-workspace-file', filePath);
|
|
182
|
+
e.dataTransfer.setData('text/uri-list', filePath);
|
|
183
|
+
|
|
184
|
+
if (e.dataTransfer.setDragImage) {
|
|
185
|
+
const dragImage = document.createElement('div');
|
|
186
|
+
dragImage.textContent = fileName;
|
|
187
|
+
dragImage.style.position = 'absolute';
|
|
188
|
+
dragImage.style.top = '-1000px';
|
|
189
|
+
dragImage.style.padding = '4px 8px';
|
|
190
|
+
dragImage.style.background = 'var(--wa-color-neutral-10)';
|
|
191
|
+
dragImage.style.border = '1px solid var(--wa-color-neutral-30)';
|
|
192
|
+
dragImage.style.borderRadius = '4px';
|
|
193
|
+
document.body.appendChild(dragImage);
|
|
194
|
+
e.dataTransfer.setDragImage(dragImage, 0, 0);
|
|
195
|
+
setTimeout(() => document.body.removeChild(dragImage), 0);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private async onLazyLoad(event: Event, node: TreeNode) {
|
|
200
|
+
const resource = node.data as Resource;
|
|
201
|
+
if (resource instanceof Directory && node.children.length === 0) {
|
|
202
|
+
await this.loadNodeChildren(node, resource);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private async loadNodeChildren(node: TreeNode, resource: Directory) {
|
|
207
|
+
if (this.loadingNodes.has(node)) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
this.loadingNodes.add(node);
|
|
212
|
+
try {
|
|
213
|
+
for (const childResource of await resource.listChildren(false)) {
|
|
214
|
+
if (HIDE_DOT_RESOURCE && childResource.getName().startsWith(".")) {
|
|
215
|
+
continue
|
|
216
|
+
}
|
|
217
|
+
const child = await this.resourceToTreeNode(childResource, false);
|
|
218
|
+
node.children.push(child);
|
|
219
|
+
}
|
|
220
|
+
node.children.sort(treeNodeComparator);
|
|
221
|
+
this.requestUpdate();
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.error('Failed to load directory children:', error);
|
|
224
|
+
} finally {
|
|
225
|
+
this.loadingNodes.delete(node);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async onFileDoubleClicked(event: Event) {
|
|
230
|
+
// @ts-ignore
|
|
231
|
+
const node: TreeNode = event.currentTarget.model
|
|
232
|
+
const filePath = (node.data as Resource).getWorkspacePath()
|
|
233
|
+
this.executeCommand("open_editor", {
|
|
234
|
+
"path": filePath
|
|
235
|
+
})
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
onSelectionChanged(event: Event) {
|
|
239
|
+
// @ts-ignore
|
|
240
|
+
const selection = event.detail.selection
|
|
241
|
+
if (selection && selection.length > 0) {
|
|
242
|
+
const node: TreeNode = selection[0].model
|
|
243
|
+
activeSelectionSignal.set(node.data)
|
|
244
|
+
} else {
|
|
245
|
+
activeSelectionSignal.set(undefined)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private currentDropTarget?: HTMLElement;
|
|
250
|
+
|
|
251
|
+
private setupDragAndDrop() {
|
|
252
|
+
const treeElement = this.treeRef.value;
|
|
253
|
+
if (!treeElement) return;
|
|
254
|
+
|
|
255
|
+
const dragOverHandler = (e: DragEvent) => {
|
|
256
|
+
if (!e.dataTransfer?.types.includes('Files')) return;
|
|
257
|
+
|
|
258
|
+
e.preventDefault();
|
|
259
|
+
e.dataTransfer.dropEffect = 'copy';
|
|
260
|
+
|
|
261
|
+
treeElement.classList.add('drag-over');
|
|
262
|
+
|
|
263
|
+
const target = e.target as HTMLElement;
|
|
264
|
+
const treeItem = target.closest('wa-tree-item') as HTMLElement;
|
|
265
|
+
|
|
266
|
+
if (treeItem && treeItem !== this.currentDropTarget) {
|
|
267
|
+
this.currentDropTarget?.classList.remove('drop-target');
|
|
268
|
+
this.currentDropTarget = treeItem;
|
|
269
|
+
treeItem.classList.add('drop-target');
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const dragEnterHandler = (e: DragEvent) => {
|
|
274
|
+
if (!e.dataTransfer?.types.includes('Files')) return;
|
|
275
|
+
|
|
276
|
+
e.preventDefault();
|
|
277
|
+
treeElement.classList.add('drag-over');
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const dragLeaveHandler = (e: DragEvent) => {
|
|
281
|
+
const rect = treeElement.getBoundingClientRect();
|
|
282
|
+
const x = e.clientX;
|
|
283
|
+
const y = e.clientY;
|
|
284
|
+
|
|
285
|
+
if (x <= rect.left || x >= rect.right || y <= rect.top || y >= rect.bottom) {
|
|
286
|
+
treeElement.classList.remove('drag-over');
|
|
287
|
+
this.currentDropTarget?.classList.remove('drop-target');
|
|
288
|
+
this.currentDropTarget = undefined;
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const dropHandler = async (e: DragEvent) => {
|
|
293
|
+
e.preventDefault();
|
|
294
|
+
treeElement.classList.remove('drag-over');
|
|
295
|
+
this.currentDropTarget?.classList.remove('drop-target');
|
|
296
|
+
this.currentDropTarget = undefined;
|
|
297
|
+
|
|
298
|
+
if (!e.dataTransfer || !this.workspaceDir) return;
|
|
299
|
+
|
|
300
|
+
const files = Array.from(e.dataTransfer.files);
|
|
301
|
+
if (files.length === 0) return;
|
|
302
|
+
|
|
303
|
+
const targetDir = await this.getDropTarget(e);
|
|
304
|
+
await this.handleFilesDrop(files, targetDir);
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
treeElement.removeEventListener('dragover', dragOverHandler as any);
|
|
308
|
+
treeElement.removeEventListener('dragenter', dragEnterHandler as any);
|
|
309
|
+
treeElement.removeEventListener('dragleave', dragLeaveHandler as any);
|
|
310
|
+
treeElement.removeEventListener('drop', dropHandler as any);
|
|
311
|
+
|
|
312
|
+
treeElement.addEventListener('dragover', dragOverHandler);
|
|
313
|
+
treeElement.addEventListener('dragenter', dragEnterHandler);
|
|
314
|
+
treeElement.addEventListener('dragleave', dragLeaveHandler);
|
|
315
|
+
treeElement.addEventListener('drop', dropHandler);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
private async getDropTarget(e: DragEvent): Promise<Directory> {
|
|
319
|
+
const target = e.target as HTMLElement;
|
|
320
|
+
const treeItem = target.closest('wa-tree-item');
|
|
321
|
+
|
|
322
|
+
if (treeItem) {
|
|
323
|
+
const node: TreeNode = (treeItem as any).model;
|
|
324
|
+
const resource = node.data as Resource;
|
|
325
|
+
|
|
326
|
+
if (resource instanceof Directory) {
|
|
327
|
+
return resource;
|
|
328
|
+
}
|
|
329
|
+
const parent = resource.getParent();
|
|
330
|
+
if (parent) {
|
|
331
|
+
return parent;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return this.workspaceDir!;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
private async handleFilesDrop(files: globalThis.File[], targetDir: Directory) {
|
|
339
|
+
const total = files.length;
|
|
340
|
+
let processed = 0;
|
|
341
|
+
let failed = 0;
|
|
342
|
+
let skipped = 0;
|
|
343
|
+
|
|
344
|
+
for (const file of files) {
|
|
345
|
+
try {
|
|
346
|
+
const targetPath = this.buildTargetPath(targetDir, file.name);
|
|
347
|
+
|
|
348
|
+
const existingFile = await this.workspaceDir!.getResource(targetPath);
|
|
349
|
+
if (existingFile) {
|
|
350
|
+
const overwrite = await confirmDialog(t('FILE_EXISTS_OVERWRITE', { fileName: file.name }));
|
|
351
|
+
if (!overwrite) {
|
|
352
|
+
skipped++;
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const workspaceFile = await this.workspaceDir!.getResource(
|
|
358
|
+
targetPath,
|
|
359
|
+
{ create: true }
|
|
360
|
+
) as File;
|
|
361
|
+
|
|
362
|
+
await workspaceFile.saveContents(file);
|
|
363
|
+
|
|
364
|
+
processed++;
|
|
365
|
+
} catch (error) {
|
|
366
|
+
console.error(`Failed to upload ${file.name}:`, error);
|
|
367
|
+
failed++;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
console.log(`Uploaded ${processed}/${total} files${skipped > 0 ? `, ${skipped} skipped` : ''}${failed > 0 ? `, ${failed} failed` : ''}`);
|
|
372
|
+
|
|
373
|
+
await this.loadWorkspace(this.workspaceDir);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
private buildTargetPath(targetDir: Directory, fileName: string): string {
|
|
377
|
+
const dirPath = targetDir.getWorkspacePath();
|
|
378
|
+
return dirPath ? `${dirPath}/${fileName}` : fileName;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
render() {
|
|
382
|
+
return html`
|
|
383
|
+
<div class="tree" ${ref(this.treeRef)} style="--drop-files-text: '${t('DROP_FILES_HERE')}'">
|
|
384
|
+
${when(!this.workspaceDir, () => html`
|
|
385
|
+
<k-no-content message="${t('SELECT_WORKSPACE')}"></k-no-content>`, () => html`
|
|
386
|
+
`)}
|
|
387
|
+
<wa-tree @wa-selection-change=${this.nobubble(this.onSelectionChanged)}
|
|
388
|
+
style="--indent-guide-width: 1px;">
|
|
389
|
+
${this.createTreeItems(this.root!, true)}
|
|
390
|
+
</wa-tree>
|
|
391
|
+
</div>
|
|
392
|
+
`
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
static styles = css`
|
|
396
|
+
:host {
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.tree {
|
|
400
|
+
height: 100%;
|
|
401
|
+
position: relative;
|
|
402
|
+
transition: all 0.2s ease;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.tree.drag-over {
|
|
406
|
+
background-color: var(--wa-color-brand-fill-quiet);
|
|
407
|
+
outline: 2px dashed var(--wa-color-brand-border-normal);
|
|
408
|
+
outline-offset: -4px;
|
|
409
|
+
border-radius: var(--wa-border-radius-medium);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.tree.drag-over::before {
|
|
413
|
+
content: var(--drop-files-text);
|
|
414
|
+
position: absolute;
|
|
415
|
+
top: 50%;
|
|
416
|
+
left: 50%;
|
|
417
|
+
transform: translate(-50%, -50%);
|
|
418
|
+
background: var(--wa-color-brand-fill-loud);
|
|
419
|
+
color: var(--wa-color-brand-on-loud);
|
|
420
|
+
padding: var(--wa-spacing-large);
|
|
421
|
+
border-radius: var(--wa-border-radius-large);
|
|
422
|
+
font-weight: bold;
|
|
423
|
+
pointer-events: none;
|
|
424
|
+
z-index: 1000;
|
|
425
|
+
opacity: 0.3;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
wa-tree-item.drop-target {
|
|
429
|
+
background-color: var(--wa-color-brand-fill-loud);
|
|
430
|
+
color: var(--wa-color-brand-on-loud);
|
|
431
|
+
border-radius: var(--wa-border-radius-small);
|
|
432
|
+
outline: 2px solid var(--wa-color-brand-border-loud);
|
|
433
|
+
outline-offset: -2px;
|
|
434
|
+
}
|
|
435
|
+
`;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
declare global {
|
|
439
|
+
interface HTMLElementTagNameMap {
|
|
440
|
+
'k-filebrowser': KFileBrowser
|
|
441
|
+
}
|
|
442
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { html, render, css } from "lit";
|
|
2
|
+
import { customElement } from "lit/decorators.js";
|
|
3
|
+
import { KElement } from "../parts/k-element";
|
|
4
|
+
import { currentLanguageSignal, languageContributionsSignal, SETTINGS_KEY_LANGUAGE } from "../core/i18n";
|
|
5
|
+
import { appSettings } from "../core/settingsservice";
|
|
6
|
+
|
|
7
|
+
function getDialogContainer(): HTMLElement {
|
|
8
|
+
let container = document.getElementById('global-dialog-container');
|
|
9
|
+
if (!container) {
|
|
10
|
+
container = document.createElement('div');
|
|
11
|
+
container.id = 'global-dialog-container';
|
|
12
|
+
document.body.appendChild(container);
|
|
13
|
+
}
|
|
14
|
+
return container;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const getLanguageName = (code: string): string => {
|
|
18
|
+
try {
|
|
19
|
+
return new Intl.DisplayNames([code], { type: 'language' }).of(code) || code.toUpperCase();
|
|
20
|
+
} catch {
|
|
21
|
+
return code.toUpperCase();
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const getAvailableLanguages = (): string[] => {
|
|
26
|
+
const contributions = languageContributionsSignal.get();
|
|
27
|
+
const languages = new Set<string>();
|
|
28
|
+
|
|
29
|
+
for (const contribution of contributions) {
|
|
30
|
+
if (contribution.namespace) {
|
|
31
|
+
const contributionObj = contribution as any;
|
|
32
|
+
for (const key in contributionObj) {
|
|
33
|
+
if (key !== 'namespace' && key !== 'label' && key !== 'language' && key !== 'translations' && typeof contributionObj[key] === 'object') {
|
|
34
|
+
languages.add(key);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return Array.from(languages).sort();
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const showLanguageSelectorDialog = async (): Promise<void> => {
|
|
44
|
+
const availableLanguages = getAvailableLanguages();
|
|
45
|
+
const currentLanguage = currentLanguageSignal.get();
|
|
46
|
+
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
const container = getDialogContainer();
|
|
49
|
+
|
|
50
|
+
let isResolved = false;
|
|
51
|
+
|
|
52
|
+
const handleClose = () => {
|
|
53
|
+
const dialog = container.querySelector('wa-dialog') as any;
|
|
54
|
+
if (dialog && !isResolved) {
|
|
55
|
+
dialog.open = false;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const handleAfterHide = () => {
|
|
60
|
+
if (isResolved) return;
|
|
61
|
+
isResolved = true;
|
|
62
|
+
render(html``, container);
|
|
63
|
+
resolve();
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const selectLanguage = async (language: string) => {
|
|
67
|
+
await appSettings.set(SETTINGS_KEY_LANGUAGE, language);
|
|
68
|
+
handleClose();
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const template = html`
|
|
72
|
+
<wa-dialog
|
|
73
|
+
label="Select Language"
|
|
74
|
+
open
|
|
75
|
+
light-dismiss
|
|
76
|
+
@wa-request-close=${handleClose}
|
|
77
|
+
@wa-after-hide=${handleAfterHide}>
|
|
78
|
+
<style>
|
|
79
|
+
.language-list {
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-direction: column;
|
|
82
|
+
gap: 0.5rem;
|
|
83
|
+
padding: 1rem;
|
|
84
|
+
min-width: 300px;
|
|
85
|
+
max-height: 400px;
|
|
86
|
+
overflow-y: auto;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.language-item {
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
padding: 0.75rem;
|
|
93
|
+
border-radius: var(--wa-border-radius-small);
|
|
94
|
+
cursor: pointer;
|
|
95
|
+
transition: background-color 0.2s;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.language-item:hover {
|
|
99
|
+
background-color: var(--wa-color-neutral-fill-quiet);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.language-item.active {
|
|
103
|
+
background-color: var(--wa-color-brand-fill-quiet);
|
|
104
|
+
font-weight: 600;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.language-code {
|
|
108
|
+
font-family: monospace;
|
|
109
|
+
margin-right: 0.75rem;
|
|
110
|
+
min-width: 3rem;
|
|
111
|
+
color: var(--wa-color-neutral-600);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.language-name {
|
|
115
|
+
flex: 1;
|
|
116
|
+
}
|
|
117
|
+
</style>
|
|
118
|
+
|
|
119
|
+
<div class="language-list">
|
|
120
|
+
${availableLanguages.map(lang => html`
|
|
121
|
+
<div
|
|
122
|
+
class="language-item ${lang === currentLanguage ? 'active' : ''}"
|
|
123
|
+
@click=${() => selectLanguage(lang)}>
|
|
124
|
+
<span class="language-code">${lang.toUpperCase()}</span>
|
|
125
|
+
<span class="language-name">${getLanguageName(lang)}</span>
|
|
126
|
+
</div>
|
|
127
|
+
`)}
|
|
128
|
+
</div>
|
|
129
|
+
</wa-dialog>
|
|
130
|
+
`;
|
|
131
|
+
|
|
132
|
+
render(template, container);
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
@customElement('k-language-selector')
|
|
137
|
+
export class KLanguageSelector extends KElement {
|
|
138
|
+
static styles = css`
|
|
139
|
+
:host {
|
|
140
|
+
display: inline-block;
|
|
141
|
+
}
|
|
142
|
+
`;
|
|
143
|
+
|
|
144
|
+
protected render() {
|
|
145
|
+
const currentLanguage = currentLanguageSignal.get();
|
|
146
|
+
const languageName = getLanguageName(currentLanguage);
|
|
147
|
+
const buttonLabel = `${currentLanguage.toUpperCase()} ${languageName}`;
|
|
148
|
+
|
|
149
|
+
return html`
|
|
150
|
+
<wa-button
|
|
151
|
+
appearance="plain"
|
|
152
|
+
size="small"
|
|
153
|
+
title="Current language: ${languageName}"
|
|
154
|
+
@click=${() => showLanguageSelectorDialog()}>
|
|
155
|
+
${buttonLabel}
|
|
156
|
+
</wa-button>
|
|
157
|
+
`;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
declare global {
|
|
162
|
+
interface HTMLElementTagNameMap {
|
|
163
|
+
'k-language-selector': KLanguageSelector;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|