@eclipse-lyra/core 0.7.15 → 0.7.16

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.
Files changed (32) hide show
  1. package/dist/api/index.js +17 -13
  2. package/dist/api/services.d.ts +1 -0
  3. package/dist/api/services.d.ts.map +1 -1
  4. package/dist/{config-C8viItpG.js → config-CTHJsmbF.js} +16 -6
  5. package/dist/config-CTHJsmbF.js.map +1 -0
  6. package/dist/core/apploader.d.ts +6 -0
  7. package/dist/core/apploader.d.ts.map +1 -1
  8. package/dist/core/contribution-mapping.d.ts +68 -0
  9. package/dist/core/contribution-mapping.d.ts.map +1 -0
  10. package/dist/core/contributionregistry.d.ts +12 -0
  11. package/dist/core/contributionregistry.d.ts.map +1 -1
  12. package/dist/core/ui-ids.d.ts +11 -0
  13. package/dist/core/ui-ids.d.ts.map +1 -0
  14. package/dist/{icon-DN6fp0dg.js → icon-C3H7IMWU.js} +135 -14
  15. package/dist/icon-C3H7IMWU.js.map +1 -0
  16. package/dist/index.js +17 -13
  17. package/dist/{nocontent-BhrN6yxJ.js → nocontent-TgnGDnXO.js} +2 -2
  18. package/dist/{nocontent-BhrN6yxJ.js.map → nocontent-TgnGDnXO.js.map} +1 -1
  19. package/dist/parts/index.js +1 -1
  20. package/dist/{resizable-grid-CIgUY5TR.js → resizable-grid-B6j8J49V.js} +13 -2
  21. package/dist/resizable-grid-B6j8J49V.js.map +1 -0
  22. package/dist/widgets/index.js +2 -2
  23. package/package.json +1 -1
  24. package/src/api/services.ts +6 -0
  25. package/src/contributions/default-ui-contributions.ts +14 -2
  26. package/src/core/apploader.ts +28 -7
  27. package/src/core/contribution-mapping.ts +142 -0
  28. package/src/core/contributionregistry.ts +44 -8
  29. package/src/core/ui-ids.ts +14 -0
  30. package/dist/config-C8viItpG.js.map +0 -1
  31. package/dist/icon-DN6fp0dg.js.map +0 -1
  32. package/dist/resizable-grid-CIgUY5TR.js.map +0 -1
@@ -1,3 +1,3 @@
1
- import "../nocontent-BhrN6yxJ.js";
2
- import "../icon-DN6fp0dg.js";
1
+ import "../nocontent-TgnGDnXO.js";
2
+ import "../icon-C3H7IMWU.js";
3
3
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eclipse-lyra/core",
3
- "version": "0.7.15",
3
+ "version": "0.7.16",
4
4
  "description": "Eclipse Lyra platform core: registries, services, parts, widgets, and API",
5
5
  "type": "module",
6
6
  "license": "EPL-2.0",
@@ -51,4 +51,10 @@ export { persistenceService } from '../core/persistenceservice';
51
51
  export { esmShService } from '../core/esmsh-service';
52
52
  export type { EsmShSource, EsmShOptions } from '../core/esmsh-service';
53
53
  export { i18nLazy, i18n, SYSTEM_LANGUAGE_BUNDLES } from '../core/i18n';
54
+ export {
55
+ contributionTargetMappingRegistry,
56
+ overrideContributionTargets,
57
+ applyConfigNameRemaps,
58
+ listContributionNameRemaps
59
+ } from '../core/contribution-mapping';
54
60
 
@@ -5,22 +5,31 @@ import {
5
5
  TOOLBAR_BOTTOM_END,
6
6
  TOOLBAR_MAIN_RIGHT
7
7
  } from "../core/constants";
8
+ import {
9
+ VIEW_FILEBROWSER,
10
+ VIEW_LOG_TERMINAL,
11
+ TOOLBAR_APP_SWITCHER,
12
+ TOOLBAR_FAST_VIEWS,
13
+ TOOLBAR_INFO,
14
+ TOOLBAR_LANGUAGE_SELECTOR
15
+ } from "../core/ui-ids";
8
16
 
9
17
  contributionRegistry.registerContribution(SIDEBAR_MAIN, {
10
- name: "filebrowser",
18
+ name: VIEW_FILEBROWSER,
11
19
  label: "Workspace",
12
20
  icon: "folder-open",
13
21
  component: (id: string) => html`<lyra-filebrowser id="${id}"></lyra-filebrowser>`
14
22
  });
15
23
 
16
24
  contributionRegistry.registerContribution("system.fastviews-bottomend", {
17
- name: "log-terminal",
25
+ name: VIEW_LOG_TERMINAL,
18
26
  label: "Log Messages",
19
27
  icon: "list",
20
28
  component: (id: string) => html`<lyra-log-terminal id="${id}"></lyra-log-terminal>`
21
29
  });
22
30
 
23
31
  contributionRegistry.registerContribution(TOOLBAR_BOTTOM_END, {
32
+ name: TOOLBAR_INFO,
24
33
  label: "Info",
25
34
  icon: "circle-info",
26
35
  command: "show_version_info",
@@ -28,16 +37,19 @@ contributionRegistry.registerContribution(TOOLBAR_BOTTOM_END, {
28
37
  });
29
38
 
30
39
  contributionRegistry.registerContribution(TOOLBAR_BOTTOM_END, {
40
+ name: TOOLBAR_FAST_VIEWS,
31
41
  label: `Fast Views`,
32
42
  component: `<lyra-fastviews target="system.fastviews-bottomend" icon="bolt" title="Fast Views"></lyra-fastviews>`
33
43
  });
34
44
 
35
45
  contributionRegistry.registerContribution(TOOLBAR_BOTTOM_END, {
46
+ name: TOOLBAR_LANGUAGE_SELECTOR,
36
47
  label: "Language",
37
48
  component: () => html`<lyra-language-selector></lyra-language-selector>`
38
49
  });
39
50
 
40
51
  contributionRegistry.registerContribution(TOOLBAR_MAIN_RIGHT, {
52
+ name: TOOLBAR_APP_SWITCHER,
41
53
  label: "App Switcher",
42
54
  component: () => html`<lyra-layout-switcher></lyra-layout-switcher>`
43
55
  } as HTMLContribution);
@@ -12,14 +12,16 @@
12
12
  * - App Loader: Bridge between framework and application
13
13
  */
14
14
 
15
- import {render, TemplateResult, html} from "lit";
16
- import {rootContext} from "./di";
17
- import {createLogger} from "./logger";
18
- import {extensionRegistry, Extension} from "./extensionregistry";
19
- import {contributionRegistry, Contribution, LayoutContribution} from "./contributionregistry";
20
- import {SYSTEM_LAYOUTS} from "./constants";
21
- import {appSettings} from "./settingsservice";
15
+ import { render, TemplateResult, html } from "lit";
16
+ import { rootContext } from "./di";
17
+ import { createLogger } from "./logger";
18
+ import { extensionRegistry, Extension } from "./extensionregistry";
19
+ import { contributionRegistry, Contribution, LayoutContribution, TOPIC_CONTRIBUTEIONS_CHANGED } from "./contributionregistry";
20
+ import { SYSTEM_LAYOUTS } from "./constants";
21
+ import { appSettings } from "./settingsservice";
22
22
  import { marketplaceRegistry } from "./marketplaceregistry";
23
+ import { contributionTargetMappingRegistry, type ContributionNameRemap } from "./contribution-mapping";
24
+ import { publish } from "./events";
23
25
 
24
26
 
25
27
  const logger = createLogger('AppLoader');
@@ -209,6 +211,12 @@ export interface AppDefinition {
209
211
 
210
212
  /** Marketplace catalog URLs for this app. Registered when the app is registered. */
211
213
  marketplaceCatalogUrls?: string[];
214
+
215
+ /**
216
+ * Optional contribution remaps for this application.
217
+ * Allows apps to declaratively remap contributions to different targets.
218
+ */
219
+ remaps?: ContributionNameRemap[];
212
220
  }
213
221
 
214
222
  /**
@@ -451,6 +459,19 @@ class AppLoaderService {
451
459
  }
452
460
  }
453
461
 
462
+ // Apply app-level contribution remaps before registering contributions
463
+ contributionTargetMappingRegistry.applyAppNameRemaps(app.remaps);
464
+ if (app.remaps?.length) {
465
+ const remappedTargets = new Set<string>();
466
+ for (const r of app.remaps) {
467
+ for (const t of r.targets) remappedTargets.add(t);
468
+ }
469
+ for (const target of remappedTargets) {
470
+ const contributions = contributionRegistry.getContributions(target);
471
+ publish(TOPIC_CONTRIBUTEIONS_CHANGED, { target, contributions });
472
+ }
473
+ }
474
+
454
475
  // Register app contributions
455
476
  if (app.contributions) {
456
477
  logger.info('Registering app contributions...');
@@ -0,0 +1,142 @@
1
+ import { rootContext } from "./di";
2
+ import type { Contribution } from "./contributionregistry";
3
+
4
+ export interface ContributionNameRemap {
5
+ /**
6
+ * Globally unique, namespaced contribution name
7
+ * (e.g. "view.filebrowser", "toolbar.filebrowser.rename").
8
+ */
9
+ name: string;
10
+ /**
11
+ * Effective target slots for this contribution.
12
+ * If empty, the original target is used.
13
+ */
14
+ targets: string[];
15
+ }
16
+
17
+ interface NameRemapState {
18
+ appTargets: string[] | undefined;
19
+ globalTargets: string[] | undefined;
20
+ }
21
+
22
+ /**
23
+ * Registry responsible for resolving effective contribution targets.
24
+ *
25
+ * It supports:
26
+ * - Global (framework-level) remaps.
27
+ * - App-level remaps coming from AppDefinition.
28
+ * - Multiple targets per contribution.
29
+ *
30
+ * Resolution precedence:
31
+ * app-level remap > global remap > original target
32
+ */
33
+ class ContributionTargetMappingRegistry {
34
+ private globalNameRemaps = new Map<string, string[]>();
35
+ private appNameRemaps = new Map<string, string[]>();
36
+
37
+ setGlobalNameRemap(name: string, targets: string[]): void {
38
+ this.globalNameRemaps.set(name, this.normalizeTargets(targets));
39
+ }
40
+
41
+ /** Clears all remaps. For testing only. */
42
+ resetForTesting(): void {
43
+ this.globalNameRemaps.clear();
44
+ this.appNameRemaps.clear();
45
+ }
46
+
47
+ applyAppNameRemaps(remaps: ContributionNameRemap[] | undefined | null): void {
48
+ this.appNameRemaps.clear();
49
+ if (!remaps) return;
50
+ for (const remap of remaps) {
51
+ this.appNameRemaps.set(remap.name, this.normalizeTargets(remap.targets));
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Returns the effective target slots for a contribution given its original target.
57
+ *
58
+ * Semantics:
59
+ * - When a remap exists with a non-empty targets array, those targets are used.
60
+ * - When a remap exists with an empty targets array, the contribution is disabled (no targets).
61
+ * - When no remap exists, the original target is used.
62
+ */
63
+ getEffectiveTargets(originalTarget: string, contribution: Contribution): string[] {
64
+ const name = (contribution as any).name as string | undefined;
65
+ if (!name) {
66
+ return [originalTarget];
67
+ }
68
+
69
+ const appTargets = this.appNameRemaps.get(name);
70
+ if (appTargets) {
71
+ return appTargets.length > 0 ? appTargets : [];
72
+ }
73
+
74
+ const globalTargets = this.globalNameRemaps.get(name);
75
+ if (globalTargets) {
76
+ return globalTargets.length > 0 ? globalTargets : [];
77
+ }
78
+
79
+ return [originalTarget];
80
+ }
81
+
82
+ /**
83
+ * Returns current mapping state for debugging and tooling.
84
+ */
85
+ listNameRemaps(): Record<string, NameRemapState> {
86
+ const result: Record<string, NameRemapState> = {};
87
+ const allNames = new Set<string>([
88
+ ...this.globalNameRemaps.keys(),
89
+ ...this.appNameRemaps.keys()
90
+ ]);
91
+ for (const name of allNames) {
92
+ result[name] = {
93
+ appTargets: this.appNameRemaps.get(name),
94
+ globalTargets: this.globalNameRemaps.get(name)
95
+ };
96
+ }
97
+ return result;
98
+ }
99
+
100
+ private normalizeTargets(targets: string[]): string[] {
101
+ const seen = new Set<string>();
102
+ const result: string[] = [];
103
+ for (const t of targets) {
104
+ if (!t || seen.has(t)) continue;
105
+ seen.add(t);
106
+ result.push(t);
107
+ }
108
+ return result;
109
+ }
110
+ }
111
+
112
+ export const contributionTargetMappingRegistry = new ContributionTargetMappingRegistry();
113
+ rootContext.put("contributionTargetMappingRegistry", contributionTargetMappingRegistry);
114
+
115
+ /**
116
+ * Public helper to override contribution targets programmatically.
117
+ * This sets a global remap that applies across all apps.
118
+ */
119
+ export function overrideContributionTargets(name: string, targets: string | string[]): void {
120
+ const normalized = Array.isArray(targets) ? targets : [targets];
121
+ contributionTargetMappingRegistry.setGlobalNameRemap(name, normalized);
122
+ }
123
+
124
+ /**
125
+ * Apply config-driven name remaps from a simple object.
126
+ * Intended for hosts that want to load JSON/YAML mapping files.
127
+ */
128
+ export function applyConfigNameRemaps(config: Record<string, string | string[]>): void {
129
+ for (const [name, targets] of Object.entries(config)) {
130
+ const normalized = Array.isArray(targets) ? targets : [targets];
131
+ contributionTargetMappingRegistry.setGlobalNameRemap(name, normalized);
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Convenience helper for debugging: returns current remap state
137
+ * for all known contribution names.
138
+ */
139
+ export function listContributionNameRemaps(): Record<string, NameRemapState> {
140
+ return contributionTargetMappingRegistry.listNameRemaps();
141
+ }
142
+
@@ -1,7 +1,8 @@
1
- import {Signal} from "@lit-labs/signals";
2
- import {TemplateResult} from "lit";
3
- import {publish} from "./events";
4
- import {rootContext} from "./di";
1
+ import { Signal } from "@lit-labs/signals";
2
+ import { TemplateResult } from "lit";
3
+ import { publish } from "./events";
4
+ import { rootContext } from "./di";
5
+ import { contributionTargetMappingRegistry } from "./contribution-mapping";
5
6
 
6
7
  export const TOPIC_CONTRIBUTEIONS_CHANGED = "events/contributionregistry/contributionsChanged"
7
8
 
@@ -11,6 +12,8 @@ export interface ContributionChangeEvent {
11
12
  }
12
13
 
13
14
  export interface Contribution {
15
+ /** Optional globally unique, namespaced contribution name (e.g. "view.filebrowser"). */
16
+ name?: string;
14
17
  target?: string;
15
18
  label: string;
16
19
  icon?: string;
@@ -58,7 +61,7 @@ class ContributionRegistry {
58
61
  private contributions: Map<string, Contribution[]> = new Map();
59
62
 
60
63
  registerContribution<T extends Contribution>(target: string, contribution: T) {
61
- const targetSlot = this.getContributions(target)!
64
+ const targetSlot = this.getOrCreateSlot(target);
62
65
  if ("command" in contribution) {
63
66
  const cmd = contribution as unknown as CommandContribution
64
67
  if (cmd.disabled instanceof Function) {
@@ -66,14 +69,47 @@ class ContributionRegistry {
66
69
  }
67
70
  }
68
71
  targetSlot.push(contribution);
69
- publish(TOPIC_CONTRIBUTEIONS_CHANGED, { target, contributions: targetSlot } as ContributionChangeEvent)
72
+ publish(TOPIC_CONTRIBUTEIONS_CHANGED, { target, contributions: targetSlot } as ContributionChangeEvent);
73
+
74
+ const effectiveTargets = contributionTargetMappingRegistry.getEffectiveTargets(target, contribution);
75
+ for (const effectiveTarget of effectiveTargets) {
76
+ if (effectiveTarget === target) continue;
77
+ const contributionsForSlot = this.getContributions(effectiveTarget);
78
+ publish(TOPIC_CONTRIBUTEIONS_CHANGED, { target: effectiveTarget, contributions: contributionsForSlot } as ContributionChangeEvent);
79
+ }
70
80
  }
71
81
 
82
+ /**
83
+ * Returns all contributions whose *effective* targets include the given slot.
84
+ *
85
+ * Note: This currently scans all registered contributions and resolves
86
+ * remaps on each call (O(N) in number of contributions). This is acceptable
87
+ * for typical Lyra apps (dozens/hundreds of contributions and few slots),
88
+ * but if contribution counts grow significantly we may want to introduce a
89
+ * cached index of effective targets per slot to keep lookups O(1).
90
+ */
72
91
  getContributions<T extends Contribution>(target: string): T[] {
92
+ const results: T[] = [];
93
+ for (const [originalTarget, list] of this.contributions.entries()) {
94
+ const typedList = list as T[];
95
+ for (const contribution of typedList) {
96
+ const effectiveTargets = contributionTargetMappingRegistry.getEffectiveTargets(originalTarget, contribution);
97
+ if (effectiveTargets.includes(target)) {
98
+ results.push(contribution);
99
+ }
100
+ }
101
+ }
102
+ if (results.length === 0) {
103
+ this.getOrCreateSlot(target);
104
+ }
105
+ return results;
106
+ }
107
+
108
+ private getOrCreateSlot(target: string): Contribution[] {
73
109
  if (!this.contributions.has(target)) {
74
- this.contributions.set(target, [])
110
+ this.contributions.set(target, []);
75
111
  }
76
- return this.contributions.get(target)! as T[]
112
+ return this.contributions.get(target)!;
77
113
  }
78
114
  }
79
115
 
@@ -0,0 +1,14 @@
1
+ export const VIEW_FILEBROWSER = "view.filebrowser";
2
+ export const VIEW_LOG_TERMINAL = "view.logTerminal";
3
+
4
+ export const TOOLBAR_INFO = "toolbar.info";
5
+ export const TOOLBAR_FAST_VIEWS = "toolbar.fastViews";
6
+ export const TOOLBAR_LANGUAGE_SELECTOR = "toolbar.languageSelector";
7
+ export const TOOLBAR_APP_SWITCHER = "toolbar.appSwitcher";
8
+
9
+ export const TOOLBAR_FILEBROWSER_RENAME = "toolbar.filebrowser.rename";
10
+ export const TOOLBAR_FILEBROWSER_DELETE = "toolbar.filebrowser.delete";
11
+
12
+ export const CTXMENU_FILEBROWSER_RENAME = "contextmenu.filebrowser.rename";
13
+ export const CTXMENU_FILEBROWSER_DELETE = "contextmenu.filebrowser.delete";
14
+