@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
package/src/core/i18n.ts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { signal, Signal } from '@lit-labs/signals';
|
|
2
|
+
import { Contribution, contributionRegistry, TOPIC_CONTRIBUTEIONS_CHANGED, ContributionChangeEvent } from "./contributionregistry";
|
|
3
|
+
import { appSettings, TOPIC_SETTINGS_CHANGED } from "./settingsservice";
|
|
4
|
+
import { subscribe } from "./events";
|
|
5
|
+
import { rootContext } from "./di";
|
|
6
|
+
|
|
7
|
+
export const SYSTEM_LANGUAGE_BUNDLES = 'system.language_bundles';
|
|
8
|
+
export const SETTINGS_KEY_LANGUAGE = 'language';
|
|
9
|
+
|
|
10
|
+
export interface LanguageBundleContribution extends Omit<Contribution, 'label'> {
|
|
11
|
+
namespace: string;
|
|
12
|
+
language?: string;
|
|
13
|
+
translations?: Record<string, string>;
|
|
14
|
+
[languageCode: string]: any;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function replaceParameters(text: string, params?: Record<string, string>): string {
|
|
18
|
+
if (!params) {
|
|
19
|
+
return text;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return text.replace(/\{(\w+)\}/g, (match, paramKey) => {
|
|
23
|
+
return params[paramKey] !== undefined ? params[paramKey] : match;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class LazyTranslation extends String {
|
|
28
|
+
private cachedValue?: string;
|
|
29
|
+
private cachedLanguage?: string;
|
|
30
|
+
|
|
31
|
+
constructor(
|
|
32
|
+
private readonly i18nService: I18nService,
|
|
33
|
+
private readonly namespace: string,
|
|
34
|
+
private readonly key: string,
|
|
35
|
+
private readonly params?: Record<string, string>
|
|
36
|
+
) {
|
|
37
|
+
super('');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
toString(): string {
|
|
41
|
+
const currentLanguage = this.i18nService.currentLanguageSignal.get();
|
|
42
|
+
|
|
43
|
+
if (this.cachedValue !== undefined && this.cachedLanguage === currentLanguage) {
|
|
44
|
+
return this.cachedValue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
this.cachedValue = this.i18nService.translate(this.namespace, this.key, this.params);
|
|
48
|
+
this.cachedLanguage = currentLanguage;
|
|
49
|
+
return this.cachedValue;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
valueOf(): string {
|
|
53
|
+
return this.toString();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
[Symbol.toPrimitive](hint: 'default' | 'string' | 'number'): string | number {
|
|
57
|
+
if (hint === 'number') {
|
|
58
|
+
const num = Number(this.toString());
|
|
59
|
+
return isNaN(num) ? 0 : num;
|
|
60
|
+
}
|
|
61
|
+
return this.toString();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
toJSON(): string {
|
|
65
|
+
return this.toString();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type UILabel = string | LazyTranslation;
|
|
70
|
+
|
|
71
|
+
class I18nService {
|
|
72
|
+
private static readonly DEFAULT_LANGUAGE = 'en';
|
|
73
|
+
private readonly translationCache = new Map<string, Record<string, string>>();
|
|
74
|
+
|
|
75
|
+
public readonly currentLanguageSignal: Signal.State<string>;
|
|
76
|
+
public readonly languageContributionsSignal: Signal.State<LanguageBundleContribution[]>;
|
|
77
|
+
|
|
78
|
+
constructor() {
|
|
79
|
+
this.currentLanguageSignal = signal<string>(this.getBrowserLanguage());
|
|
80
|
+
this.languageContributionsSignal = signal<LanguageBundleContribution[]>([]);
|
|
81
|
+
|
|
82
|
+
this.initialize();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private getBrowserLanguage(): string {
|
|
86
|
+
const browserLanguage = navigator.language || navigator.languages?.[0] || I18nService.DEFAULT_LANGUAGE;
|
|
87
|
+
return browserLanguage.split('-')[0].toLowerCase();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private async initializeLanguage(): Promise<string> {
|
|
91
|
+
const settingsLanguage = await appSettings.get(SETTINGS_KEY_LANGUAGE);
|
|
92
|
+
return settingsLanguage || this.getBrowserLanguage();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private async updateLanguageFromSettings(): Promise<void> {
|
|
96
|
+
const language = await this.initializeLanguage();
|
|
97
|
+
this.currentLanguageSignal.set(language);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private getPrimaryLanguage(language: string): string {
|
|
101
|
+
return language.split('-')[0].toLowerCase();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private updateLanguageContributions(): void {
|
|
105
|
+
const contributions = contributionRegistry.getContributions<Contribution>(SYSTEM_LANGUAGE_BUNDLES) as unknown as LanguageBundleContribution[];
|
|
106
|
+
this.languageContributionsSignal.set(contributions);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private createCacheKey(namespace: string, language: string): string {
|
|
110
|
+
return `${namespace}:${language}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public mergeTranslationsForLanguage(
|
|
114
|
+
contributions: LanguageBundleContribution[],
|
|
115
|
+
namespace: string,
|
|
116
|
+
language: string
|
|
117
|
+
): Record<string, string> {
|
|
118
|
+
const cacheKey = this.createCacheKey(namespace, language);
|
|
119
|
+
const cached = this.translationCache.get(cacheKey);
|
|
120
|
+
if (cached !== undefined) {
|
|
121
|
+
return cached;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const merged: Record<string, string> = {};
|
|
125
|
+
|
|
126
|
+
for (const contribution of contributions) {
|
|
127
|
+
if (contribution.namespace !== namespace) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let translations: Record<string, string> | undefined;
|
|
132
|
+
|
|
133
|
+
if (contribution.translations && contribution.language === language) {
|
|
134
|
+
translations = contribution.translations;
|
|
135
|
+
} else if (contribution[language] && typeof contribution[language] === 'object') {
|
|
136
|
+
translations = contribution[language] as Record<string, string>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (translations) {
|
|
140
|
+
Object.assign(merged, translations);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
this.translationCache.set(cacheKey, merged);
|
|
145
|
+
return merged;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private invalidateTranslationCache(): void {
|
|
149
|
+
this.translationCache.clear();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public translate(namespace: string, key: string, params?: Record<string, string>): string {
|
|
153
|
+
const currentLanguage = this.currentLanguageSignal.get();
|
|
154
|
+
const primaryLanguage = this.getPrimaryLanguage(currentLanguage);
|
|
155
|
+
const contributions = this.languageContributionsSignal.get();
|
|
156
|
+
|
|
157
|
+
let translation: string | undefined;
|
|
158
|
+
|
|
159
|
+
const currentLangTranslations = this.mergeTranslationsForLanguage(contributions, namespace, currentLanguage);
|
|
160
|
+
if (currentLangTranslations[key]) {
|
|
161
|
+
translation = currentLangTranslations[key];
|
|
162
|
+
} else {
|
|
163
|
+
const primaryLangTranslations = currentLanguage !== primaryLanguage
|
|
164
|
+
? this.mergeTranslationsForLanguage(contributions, namespace, primaryLanguage)
|
|
165
|
+
: currentLangTranslations;
|
|
166
|
+
if (primaryLangTranslations[key]) {
|
|
167
|
+
translation = primaryLangTranslations[key];
|
|
168
|
+
} else if (primaryLanguage !== I18nService.DEFAULT_LANGUAGE && currentLanguage !== I18nService.DEFAULT_LANGUAGE) {
|
|
169
|
+
const defaultLangTranslations = this.mergeTranslationsForLanguage(contributions, namespace, I18nService.DEFAULT_LANGUAGE);
|
|
170
|
+
if (defaultLangTranslations[key]) {
|
|
171
|
+
translation = defaultLangTranslations[key];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (!translation) {
|
|
177
|
+
return key;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return replaceParameters(translation, params);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private initialize(): void {
|
|
184
|
+
subscribe(TOPIC_SETTINGS_CHANGED, async (settings: any) => {
|
|
185
|
+
const language = settings?.[SETTINGS_KEY_LANGUAGE] || this.getBrowserLanguage();
|
|
186
|
+
this.currentLanguageSignal.set(language);
|
|
187
|
+
this.invalidateTranslationCache();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
subscribe(TOPIC_CONTRIBUTEIONS_CHANGED, (event: ContributionChangeEvent) => {
|
|
191
|
+
if (event.target === SYSTEM_LANGUAGE_BUNDLES) {
|
|
192
|
+
this.invalidateTranslationCache();
|
|
193
|
+
this.updateLanguageContributions();
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
this.updateLanguageFromSettings();
|
|
198
|
+
this.updateLanguageContributions();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
public i18n(namespace: string) {
|
|
202
|
+
return (key: string, params?: Record<string, string>): string => {
|
|
203
|
+
return this.translate(namespace, key, params);
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
public i18nLazy(namespace: string) {
|
|
208
|
+
return (key: string, params?: Record<string, string>): LazyTranslation => {
|
|
209
|
+
return new LazyTranslation(this, namespace, key, params);
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export const i18nService = new I18nService();
|
|
215
|
+
rootContext.put("i18nService", i18nService);
|
|
216
|
+
|
|
217
|
+
export const currentLanguageSignal = i18nService.currentLanguageSignal;
|
|
218
|
+
export const languageContributionsSignal = i18nService.languageContributionsSignal;
|
|
219
|
+
|
|
220
|
+
export const i18n = (namespace: string) => i18nService.i18n(namespace);
|
|
221
|
+
export const i18nLazy = (namespace: string) => i18nService.i18nLazy(namespace);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// 0. Context seed (lit, constants, toast) – di only provides empty contexts
|
|
2
|
+
import { rootContext, uiContext } from './di';
|
|
3
|
+
import { html, render } from 'lit';
|
|
4
|
+
import * as constants from './constants';
|
|
5
|
+
import { toastInfo, toastError, toastWarning } from './toast';
|
|
6
|
+
|
|
7
|
+
rootContext.put('constants', constants);
|
|
8
|
+
uiContext.put('html', html);
|
|
9
|
+
uiContext.put('render', render);
|
|
10
|
+
uiContext.put('toastInfo', toastInfo);
|
|
11
|
+
uiContext.put('toastError', toastError);
|
|
12
|
+
uiContext.put('toastWarning', toastWarning);
|
|
13
|
+
|
|
14
|
+
// 1. Foundation: logging, persistence, settings
|
|
15
|
+
import './logger';
|
|
16
|
+
import './persistenceservice';
|
|
17
|
+
import './settingsservice';
|
|
18
|
+
|
|
19
|
+
// 2. Registries and task service (contribution first – others depend on it)
|
|
20
|
+
import './contributionregistry';
|
|
21
|
+
import './packageinfoservice';
|
|
22
|
+
import './taskservice';
|
|
23
|
+
|
|
24
|
+
// 3. i18n and ESM
|
|
25
|
+
import './i18n';
|
|
26
|
+
import './esmsh-service';
|
|
27
|
+
|
|
28
|
+
// 4. Command and extension registries
|
|
29
|
+
import './commandregistry';
|
|
30
|
+
import './extensionregistry';
|
|
31
|
+
|
|
32
|
+
// 5. UI and workspace services
|
|
33
|
+
import './keybindings';
|
|
34
|
+
import './dialogservice';
|
|
35
|
+
import './editorregistry';
|
|
36
|
+
import './filesys';
|
|
37
|
+
|
|
38
|
+
// 6. App loader and marketplace
|
|
39
|
+
import './apploader';
|
|
40
|
+
import './marketplaceregistry';
|
|
41
|
+
|
|
42
|
+
// 7. Language bundles and dialogs (use contributionRegistry / dialogService)
|
|
43
|
+
import '../i18n';
|
|
44
|
+
import '../dialogs';
|
|
45
|
+
|
|
46
|
+
// 8. Parts, components, widgets, contributions, commands (extensions registered by app host)
|
|
47
|
+
import '../parts';
|
|
48
|
+
import '../components';
|
|
49
|
+
import '../widgets';
|
|
50
|
+
import '../contributions';
|
|
51
|
+
import '../commands';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {registerIconLibrary} from '@awesome.me/webawesome'
|
|
2
|
+
|
|
3
|
+
registerIconLibrary('k', {
|
|
4
|
+
resolver: (name: string) => {
|
|
5
|
+
return new URL(`../icons/${name}.svg`, import.meta.url).href;
|
|
6
|
+
},
|
|
7
|
+
mutator: (svg: SVGElement) => {
|
|
8
|
+
svg.setAttribute('fill', 'currentColor');
|
|
9
|
+
svg.setAttribute('stroke', 'currentColor');
|
|
10
|
+
}
|
|
11
|
+
});
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Key Binding Manager for geo!space
|
|
3
|
+
*
|
|
4
|
+
* Handles keyboard shortcuts and binds them to commands.
|
|
5
|
+
* Supports standard modifiers: CTRL, ALT, SHIFT, META (CMD on Mac)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import logger from "./logger";
|
|
9
|
+
import { subscribe } from "./events";
|
|
10
|
+
import { commandRegistry, TOPIC_COMMAND_REGISTERED } from "./commandregistry";
|
|
11
|
+
import { rootContext } from "./di";
|
|
12
|
+
|
|
13
|
+
import type { Command } from "./commandregistry";
|
|
14
|
+
|
|
15
|
+
export type ModifierField = 'ctrl' | 'alt' | 'shift' | 'meta';
|
|
16
|
+
|
|
17
|
+
export interface KeyBinding {
|
|
18
|
+
commandId: string;
|
|
19
|
+
key: string;
|
|
20
|
+
ctrl?: boolean;
|
|
21
|
+
alt?: boolean;
|
|
22
|
+
shift?: boolean;
|
|
23
|
+
meta?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const MODIFIER_FIELDS: ModifierField[] = ['alt', 'ctrl', 'meta', 'shift'];
|
|
27
|
+
|
|
28
|
+
const MODIFIER_ALIASES: Record<string, ModifierField> = {
|
|
29
|
+
CTRL: 'ctrl', CONTROL: 'ctrl',
|
|
30
|
+
ALT: 'alt', OPTION: 'alt',
|
|
31
|
+
SHIFT: 'shift',
|
|
32
|
+
META: 'meta', CMD: 'meta', COMMAND: 'meta', WIN: 'meta', WINDOWS: 'meta',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const MODIFIER_DISPLAY: Record<ModifierField, string> = {
|
|
36
|
+
ctrl: 'Ctrl', alt: 'Alt', shift: 'Shift', meta: 'Cmd',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const KEY_NORMALIZE: Record<string, string> = {
|
|
40
|
+
SPACE: ' ', ESC: 'ESCAPE', RETURN: 'ENTER',
|
|
41
|
+
LEFT: 'ARROWLEFT', RIGHT: 'ARROWRIGHT', UP: 'ARROWUP', DOWN: 'ARROWDOWN',
|
|
42
|
+
DEL: 'DELETE', INS: 'INSERT', PAGEUP: 'PAGEUP', PAGEDOWN: 'PAGEDOWN',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const KNOWN_MODIFIERS = new Set<string>(Object.keys(MODIFIER_ALIASES));
|
|
46
|
+
|
|
47
|
+
function normalizeKey(key: string): string {
|
|
48
|
+
return KEY_NORMALIZE[key] ?? key;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class KeyBindingManager {
|
|
52
|
+
private bindings: Map<string, KeyBinding[]> = new Map();
|
|
53
|
+
private enabled: boolean = true;
|
|
54
|
+
|
|
55
|
+
constructor() {
|
|
56
|
+
document.addEventListener('keydown', this.handleKeyDown.bind(this), true);
|
|
57
|
+
this.registerExistingCommandBindings();
|
|
58
|
+
subscribe(TOPIC_COMMAND_REGISTERED, (command: Command) => {
|
|
59
|
+
if (command.keyBinding) {
|
|
60
|
+
this.registerKeyBinding(command.id, command.keyBinding);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private registerExistingCommandBindings() {
|
|
66
|
+
const commands = commandRegistry.listCommands();
|
|
67
|
+
Object.values(commands).forEach((command: Command) => {
|
|
68
|
+
if (command.keyBinding) {
|
|
69
|
+
this.registerKeyBinding(command.id, command.keyBinding);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Parse a key binding string like "CTRL+SHIFT+P" or "ALT+S"
|
|
76
|
+
*/
|
|
77
|
+
parseKeyBinding(keyBindingString: string): KeyBinding | null {
|
|
78
|
+
if (!keyBindingString || keyBindingString.trim() === '') {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const parts = keyBindingString.toUpperCase().split('+').map(p => p.trim());
|
|
83
|
+
if (parts.length === 0) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const key = parts[parts.length - 1];
|
|
88
|
+
const modifiers = parts.slice(0, -1);
|
|
89
|
+
if (parts.length === 1 && KNOWN_MODIFIERS.has(key)) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const binding: Partial<KeyBinding> = { ctrl: false, alt: false, shift: false, meta: false };
|
|
94
|
+
for (const modifier of modifiers) {
|
|
95
|
+
const field = MODIFIER_ALIASES[modifier];
|
|
96
|
+
if (field === undefined) return null;
|
|
97
|
+
binding[field] = true;
|
|
98
|
+
}
|
|
99
|
+
binding.key = normalizeKey(key);
|
|
100
|
+
return binding as KeyBinding;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create a unique key for a binding
|
|
105
|
+
*/
|
|
106
|
+
private getBindingKey(binding: KeyBinding): string {
|
|
107
|
+
const parts = [...MODIFIER_FIELDS.filter(f => binding[f]), binding.key.toUpperCase()];
|
|
108
|
+
return parts.join('+');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Register a key binding for a command
|
|
113
|
+
*/
|
|
114
|
+
registerKeyBinding(commandId: string, keyBindingString: string): boolean {
|
|
115
|
+
const binding = this.parseKeyBinding(keyBindingString);
|
|
116
|
+
|
|
117
|
+
if (!binding) {
|
|
118
|
+
logger.error(`Invalid key binding: ${keyBindingString}`);
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
binding.commandId = commandId;
|
|
123
|
+
|
|
124
|
+
const bindingKey = this.getBindingKey(binding);
|
|
125
|
+
|
|
126
|
+
if (!this.bindings.has(bindingKey)) {
|
|
127
|
+
this.bindings.set(bindingKey, []);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const existing = this.bindings.get(bindingKey)!;
|
|
131
|
+
const sameCommand = existing.find(b => b.commandId === commandId);
|
|
132
|
+
if (sameCommand) {
|
|
133
|
+
logger.error(`Key binding ${keyBindingString} already registered for command ${commandId}`);
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
const otherCommand = existing.find(b => b.commandId !== commandId);
|
|
137
|
+
if (otherCommand) {
|
|
138
|
+
logger.warn(`Key binding ${keyBindingString} already used by command ${otherCommand.commandId}; refusing for ${commandId}`);
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
existing.push(binding);
|
|
143
|
+
logger.debug(`Key binding registered: ${keyBindingString} -> ${commandId}`);
|
|
144
|
+
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Unregister a key binding
|
|
150
|
+
*/
|
|
151
|
+
unregisterKeyBinding(commandId: string, keyBindingString?: string): void {
|
|
152
|
+
if (keyBindingString) {
|
|
153
|
+
const binding = this.parseKeyBinding(keyBindingString);
|
|
154
|
+
if (binding) {
|
|
155
|
+
const bindingKey = this.getBindingKey(binding);
|
|
156
|
+
const bindings = this.bindings.get(bindingKey);
|
|
157
|
+
if (bindings) {
|
|
158
|
+
const filtered = bindings.filter(b => b.commandId !== commandId);
|
|
159
|
+
if (filtered.length === 0) {
|
|
160
|
+
this.bindings.delete(bindingKey);
|
|
161
|
+
} else {
|
|
162
|
+
this.bindings.set(bindingKey, filtered);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
// Remove all bindings for this command
|
|
168
|
+
for (const [key, bindings] of this.bindings.entries()) {
|
|
169
|
+
const filtered = bindings.filter(b => b.commandId !== commandId);
|
|
170
|
+
if (filtered.length === 0) {
|
|
171
|
+
this.bindings.delete(key);
|
|
172
|
+
} else {
|
|
173
|
+
this.bindings.set(key, filtered);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Get all key bindings for a command
|
|
181
|
+
*/
|
|
182
|
+
getKeyBindingsForCommand(commandId: string): string[] {
|
|
183
|
+
const result: string[] = [];
|
|
184
|
+
for (const bindings of this.bindings.values()) {
|
|
185
|
+
for (const binding of bindings) {
|
|
186
|
+
if (binding.commandId === commandId) {
|
|
187
|
+
result.push(this.formatKeyBinding(binding));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return result.sort();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Format a key binding for display
|
|
196
|
+
*/
|
|
197
|
+
formatKeyBinding(binding: KeyBinding): string {
|
|
198
|
+
const parts = MODIFIER_FIELDS.filter(f => binding[f]).map(f => MODIFIER_DISPLAY[f]);
|
|
199
|
+
const key = binding.key.length === 1
|
|
200
|
+
? binding.key.toUpperCase()
|
|
201
|
+
: binding.key.charAt(0).toUpperCase() + binding.key.slice(1).toLowerCase();
|
|
202
|
+
parts.push(key);
|
|
203
|
+
return parts.join('+');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Handle keyboard events
|
|
208
|
+
*/
|
|
209
|
+
private handleKeyDown(event: KeyboardEvent): void {
|
|
210
|
+
if (!this.enabled) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
// event.key can be empty or layout-specific with some IMEs or dead keys
|
|
214
|
+
const eventBinding: KeyBinding = {
|
|
215
|
+
commandId: '',
|
|
216
|
+
key: normalizeKey(event.key.toUpperCase()),
|
|
217
|
+
ctrl: event.ctrlKey,
|
|
218
|
+
alt: event.altKey,
|
|
219
|
+
shift: event.shiftKey,
|
|
220
|
+
meta: event.metaKey
|
|
221
|
+
};
|
|
222
|
+
const bindingKey = this.getBindingKey(eventBinding);
|
|
223
|
+
const bindings = this.bindings.get(bindingKey);
|
|
224
|
+
|
|
225
|
+
if (bindings && bindings.length > 0) {
|
|
226
|
+
const binding = bindings[0];
|
|
227
|
+
try {
|
|
228
|
+
event.preventDefault();
|
|
229
|
+
event.stopPropagation();
|
|
230
|
+
const context = commandRegistry.createExecutionContext({});
|
|
231
|
+
commandRegistry.execute(binding.commandId, context);
|
|
232
|
+
logger.debug(`Executed command via key binding: ${binding.commandId}`);
|
|
233
|
+
} catch (error: any) {
|
|
234
|
+
logger.error(`Failed to execute command ${binding.commandId}: ${error.message}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Enable or disable key binding handling
|
|
241
|
+
*/
|
|
242
|
+
setEnabled(enabled: boolean): void {
|
|
243
|
+
this.enabled = enabled;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Check if key binding handling is enabled
|
|
248
|
+
*/
|
|
249
|
+
isEnabled(): boolean {
|
|
250
|
+
return this.enabled;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Get all registered key bindings
|
|
255
|
+
*/
|
|
256
|
+
getAllBindings(): Map<string, KeyBinding[]> {
|
|
257
|
+
const copy = new Map<string, KeyBinding[]>();
|
|
258
|
+
for (const [key, bindings] of this.bindings) {
|
|
259
|
+
copy.set(key, [...bindings]);
|
|
260
|
+
}
|
|
261
|
+
return copy;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Clear all key bindings
|
|
266
|
+
*/
|
|
267
|
+
clearAll(): void {
|
|
268
|
+
this.bindings.clear();
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export const keyBindingManager = new KeyBindingManager();
|
|
273
|
+
rootContext.put("keyBindingManager", keyBindingManager);
|
|
274
|
+
|