@eclipse-docks/core 0.7.110 → 0.7.112

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.
@@ -49,5 +49,11 @@ export type ExtensionSideEffectsListOptions = {
49
49
  pattern: RegExp;
50
50
  };
51
51
  export declare function listExtensionSideEffectPackages(dependencies: Record<string, string>, sideEffects: ExtensionSideEffectsListOptions): string[];
52
+ /**
53
+ * Walks extension package.json `dependencies` to collect transitive extension-* packages
54
+ * (e.g. extension-utils → extension-python-runtime → extension-terminal).
55
+ */
56
+ export declare function collectTransitiveExtensionPackages(appRoot: string, seeds: string[], sideEffects: ExtensionSideEffectsListOptions): string[];
57
+ export declare function resolveExtensionSideEffectPackages(appRoot: string, dependencies: Record<string, string>, sideEffects: ExtensionSideEffectsListOptions): string[];
52
58
  export declare function resolveDepVersionsPlugin(options?: ResolveDepVersionsPluginOptions): Plugin;
53
59
  //# sourceMappingURL=vite-plugin-resolve-deps.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin-resolve-deps.d.ts","sourceRoot":"","sources":["../src/vite-plugin-resolve-deps.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;CACnC;AAoDD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IAAE,sBAAsB,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7C,mBAAmB,GAAG,IAAI,CAkB5B;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IAAE,sBAAsB,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7C,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGxB;AAID,8GAA8G;AAC9G,eAAO,MAAM,yBAAyB,4CAA4C,CAAC;AAanF,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,+BAA+B,GAAG;IAC5C,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,OAAO,GAAG,2BAA2B,CAAC;CAC9D,CAAC;AAEF,4EAA4E;AAC5E,MAAM,MAAM,+BAA+B,GAAG;IAC5C,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAqBF,wBAAgB,+BAA+B,CAC7C,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,WAAW,EAAE,+BAA+B,GAC3C,MAAM,EAAE,CAQV;AAED,wBAAgB,wBAAwB,CACtC,OAAO,CAAC,EAAE,+BAA+B,GACxC,MAAM,CAyER"}
1
+ {"version":3,"file":"vite-plugin-resolve-deps.d.ts","sourceRoot":"","sources":["../src/vite-plugin-resolve-deps.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;CACnC;AA8DD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IAAE,sBAAsB,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7C,mBAAmB,GAAG,IAAI,CAkB5B;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IAAE,sBAAsB,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7C,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGxB;AAID,8GAA8G;AAC9G,eAAO,MAAM,yBAAyB,4CAA4C,CAAC;AAanF,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,+BAA+B,GAAG;IAC5C,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,OAAO,GAAG,2BAA2B,CAAC;CAC9D,CAAC;AAEF,4EAA4E;AAC5E,MAAM,MAAM,+BAA+B,GAAG;IAC5C,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAqBF,wBAAgB,+BAA+B,CAC7C,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,WAAW,EAAE,+BAA+B,GAC3C,MAAM,EAAE,CAQV;AAED;;;GAGG;AACH,wBAAgB,kCAAkC,CAChD,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EAAE,EACf,WAAW,EAAE,+BAA+B,GAC3C,MAAM,EAAE,CAqBV;AAED,wBAAgB,kCAAkC,CAChD,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,WAAW,EAAE,+BAA+B,GAC3C,MAAM,EAAE,CAQV;AAED,wBAAgB,wBAAwB,CACtC,OAAO,CAAC,EAAE,+BAA+B,GACxC,MAAM,CA0ER"}
@@ -1,25 +1,33 @@
1
1
  import { existsSync, readFileSync } from "fs";
2
2
  import path from "path";
3
3
  //#region src/vite-plugin-resolve-deps.ts
4
- function findPackageVersion(appRoot, depName) {
5
- const segments = depName.startsWith("@") ? depName.split("/") : [depName];
4
+ function packageJsonPath(appRoot, packageName) {
5
+ const segments = packageName.startsWith("@") ? packageName.split("/") : [packageName];
6
6
  const relativePath = path.join("node_modules", ...segments, "package.json");
7
7
  let dir = path.resolve(appRoot);
8
8
  const root = path.parse(dir).root;
9
9
  while (true) {
10
10
  const pkgPath = path.join(dir, relativePath);
11
- if (existsSync(pkgPath)) {
12
- try {
13
- const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
14
- if (typeof pkg.version === "string") return pkg.version;
15
- } catch {}
16
- return null;
17
- }
11
+ if (existsSync(pkgPath)) return pkgPath;
18
12
  if (dir === root) break;
19
13
  dir = path.dirname(dir);
20
14
  }
21
15
  return null;
22
16
  }
17
+ function readPackageJson(appRoot, packageName) {
18
+ const pkgPath = packageJsonPath(appRoot, packageName);
19
+ if (!pkgPath) return null;
20
+ try {
21
+ return JSON.parse(readFileSync(pkgPath, "utf8"));
22
+ } catch {
23
+ return null;
24
+ }
25
+ }
26
+ function findPackageVersion(appRoot, depName) {
27
+ const pkg = readPackageJson(appRoot, depName);
28
+ if (typeof pkg?.version === "string") return pkg.version;
29
+ return null;
30
+ }
23
31
  function resolveDepVersionsFromPkg(appRoot, pkg, options) {
24
32
  const deps = { ...pkg.dependencies };
25
33
  if (options?.includeDevDependencies && pkg.devDependencies) Object.assign(deps, pkg.devDependencies);
@@ -77,6 +85,34 @@ function listExtensionSideEffectPackages(dependencies, sideEffects) {
77
85
  const rest = names.filter((n) => !prioritySet.has(n)).sort((a, b) => a.localeCompare(b));
78
86
  return [...first, ...rest];
79
87
  }
88
+ /**
89
+ * Walks extension package.json `dependencies` to collect transitive extension-* packages
90
+ * (e.g. extension-utils → extension-python-runtime → extension-terminal).
91
+ */
92
+ function collectTransitiveExtensionPackages(appRoot, seeds, sideEffects) {
93
+ const found = /* @__PURE__ */ new Set();
94
+ const queue = [...seeds];
95
+ const visited = /* @__PURE__ */ new Set();
96
+ while (queue.length > 0) {
97
+ const name = queue.shift();
98
+ if (visited.has(name)) continue;
99
+ visited.add(name);
100
+ const pkg = readPackageJson(appRoot, name);
101
+ if (!pkg?.dependencies) continue;
102
+ for (const dep of Object.keys(pkg.dependencies)) {
103
+ if (!sideEffects.pattern.test(dep) || sideEffects.exclude.has(dep)) continue;
104
+ found.add(dep);
105
+ queue.push(dep);
106
+ }
107
+ }
108
+ return [...found];
109
+ }
110
+ function resolveExtensionSideEffectPackages(appRoot, dependencies, sideEffects) {
111
+ const direct = listExtensionSideEffectPackages(dependencies, sideEffects);
112
+ const transitive = collectTransitiveExtensionPackages(appRoot, direct, sideEffects);
113
+ const allNames = [...new Set([...direct, ...transitive])];
114
+ return listExtensionSideEffectPackages(Object.fromEntries(allNames.map((name) => [name, "*"])), sideEffects);
115
+ }
80
116
  function resolveDepVersionsPlugin(options) {
81
117
  let appRoot = process.cwd();
82
118
  let extensionSideEffectsActive = false;
@@ -101,7 +137,8 @@ function resolveDepVersionsPlugin(options) {
101
137
  extensionImportPackages = [];
102
138
  return;
103
139
  }
104
- extensionImportPackages = listExtensionSideEffectPackages(resolvePackageInfo(appRoot, options)?.dependencies ?? {}, normalized);
140
+ const info = resolvePackageInfo(appRoot, options);
141
+ extensionImportPackages = resolveExtensionSideEffectPackages(appRoot, info?.dependencies ?? {}, normalized);
105
142
  },
106
143
  resolveId(id) {
107
144
  if (id === "virtual:eclipse-docks-extension-imports") return RESOLVED_VIRTUAL_EXTENSION_IMPORTS;
@@ -125,6 +162,6 @@ function resolveDepVersionsPlugin(options) {
125
162
  };
126
163
  }
127
164
  //#endregion
128
- export { VIRTUAL_EXTENSION_IMPORTS, listExtensionSideEffectPackages, resolveDepVersions, resolveDepVersionsPlugin, resolvePackageInfo };
165
+ export { VIRTUAL_EXTENSION_IMPORTS, collectTransitiveExtensionPackages, listExtensionSideEffectPackages, resolveDepVersions, resolveDepVersionsPlugin, resolveExtensionSideEffectPackages, resolvePackageInfo };
129
166
 
130
167
  //# sourceMappingURL=vite-plugin-resolve-deps.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin-resolve-deps.js","names":[],"sources":["../src/vite-plugin-resolve-deps.ts"],"sourcesContent":["import { readFileSync, existsSync } from 'fs';\nimport path from 'path';\nimport type { Plugin } from 'vite';\n\nexport interface ResolvedPackageInfo {\n name: string;\n version: string;\n description?: string;\n dependencies: Record<string, string>;\n marketplaceCatalogUrls?: string[];\n}\n\ninterface PackageJson {\n name?: string;\n version?: string;\n description?: string;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\nfunction findPackageVersion(appRoot: string, depName: string): string | null {\n const segments = depName.startsWith('@')\n ? depName.split('/')\n : [depName];\n const relativePath = path.join('node_modules', ...segments, 'package.json');\n let dir = path.resolve(appRoot);\n const root = path.parse(dir).root;\n\n while (true) {\n const pkgPath = path.join(dir, relativePath);\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as PackageJson;\n if (typeof pkg.version === 'string') return pkg.version;\n } catch {\n // ignore parse errors\n }\n return null;\n }\n if (dir === root) break;\n dir = path.dirname(dir);\n }\n return null;\n}\n\nfunction resolveDepVersionsFromPkg(\n appRoot: string,\n pkg: PackageJson,\n options?: { includeDevDependencies?: boolean }\n): Record<string, string> {\n const deps = { ...pkg.dependencies };\n if (options?.includeDevDependencies && pkg.devDependencies) {\n Object.assign(deps, pkg.devDependencies);\n }\n const result: Record<string, string> = {};\n for (const [name, specifier] of Object.entries(deps)) {\n const version = findPackageVersion(appRoot, name);\n result[name] = version ?? specifier;\n }\n return result;\n}\n\nexport function resolvePackageInfo(\n appRoot: string,\n options?: { includeDevDependencies?: boolean }\n): ResolvedPackageInfo | null {\n const pkgPath = path.join(appRoot, 'package.json');\n if (!existsSync(pkgPath)) return null;\n\n let pkg: PackageJson;\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as PackageJson;\n } catch {\n return null;\n }\n\n const name = typeof pkg.name === 'string' ? pkg.name : '';\n const version = typeof pkg.version === 'string' ? pkg.version : '0.0.0';\n const description = typeof pkg.description === 'string' ? pkg.description : undefined;\n const dependencies = resolveDepVersionsFromPkg(appRoot, pkg, options);\n const marketplaceCatalogUrls = (pkg as { marketplace?: { catalogUrls?: string[] } }).marketplace?.catalogUrls;\n\n return { name, version, description, dependencies, marketplaceCatalogUrls };\n}\n\nexport function resolveDepVersions(\n appRoot: string,\n options?: { includeDevDependencies?: boolean }\n): Record<string, string> {\n const info = resolvePackageInfo(appRoot, options);\n return info?.dependencies ?? {};\n}\n\nconst RESOLVED_PACKAGE_INFO_KEY = '__RESOLVED_PACKAGE_INFO__';\n\n/** Virtual module id; injected into `index.html` before `main.ts` when extension side-effects are enabled. */\nexport const VIRTUAL_EXTENSION_IMPORTS = 'virtual:eclipse-docks-extension-imports';\n\nconst RESOLVED_VIRTUAL_EXTENSION_IMPORTS = `\\0${VIRTUAL_EXTENSION_IMPORTS}`;\n\n/** Unscoped `extension-*` or scoped `@namespace/extension-*` (any npm scope). */\nconst DEFAULT_EXTENSION_PATTERN = /^(?:@[^/]+\\/)?extension-/;\n\nconst DEFAULT_PRIORITY_FIRST = ['@eclipse-docks/extension-pwa', 'extension-pwa'];\n\n/** Matches Vite’s default app entry in index.html. */\nconst MAIN_TS_SCRIPT_RE =\n /<script\\b[^>]*\\btype\\s*=\\s*[\"']module[\"'][^>]*\\bsrc\\s*=\\s*[\"'][^\"']*\\/src\\/main\\.ts[\"'][^>]*>\\s*<\\/script>/i;\n\nexport interface ExtensionSideEffectsOptions {\n /**\n * When false, disables automatic extension side-effect imports. Omitted or true keeps them on.\n */\n enabled?: boolean;\n /** Dependency names to skip (even if they match `packageNamePattern`). */\n exclude?: string[];\n /**\n * Packages to load first, in order (only those present in dependencies are imported).\n * Default includes PWA so `beforeinstallprompt` can register early.\n */\n priorityFirst?: string[];\n /**\n * Which direct `dependencies` keys qualify as Docks-style extensions (`extension-*`, with an optional npm scope).\n * @default /^(?:@[^/]+\\/)?extension-/\n */\n packageNamePattern?: RegExp;\n}\n\nexport type ResolveDepVersionsPluginOptions = {\n includeDevDependencies?: boolean;\n /**\n * By default, registers a virtual module that side-effect-imports every matching direct\n * `dependencies` entry (see `ExtensionSideEffectsOptions`), and injects\n * `import 'virtual:eclipse-docks-extension-imports'` into `index.html` **before** `/src/main.ts`.\n * Pass `false` or `{ enabled: false }` to disable.\n */\n extensionSideEffects?: boolean | ExtensionSideEffectsOptions;\n};\n\n/** Normalized match options for {@link listExtensionSideEffectPackages}. */\nexport type ExtensionSideEffectsListOptions = {\n exclude: Set<string>;\n priorityFirst: string[];\n pattern: RegExp;\n};\n\nfunction normalizeExtensionSideEffects(\n opt: boolean | ExtensionSideEffectsOptions | undefined,\n): ExtensionSideEffectsListOptions | null {\n if (opt === false) return null;\n if (opt === undefined || opt === true) {\n return {\n exclude: new Set(),\n priorityFirst: [...DEFAULT_PRIORITY_FIRST],\n pattern: DEFAULT_EXTENSION_PATTERN,\n };\n }\n if (opt.enabled === false) return null;\n return {\n exclude: new Set(opt.exclude ?? []),\n priorityFirst: opt.priorityFirst ?? [...DEFAULT_PRIORITY_FIRST],\n pattern: opt.packageNamePattern ?? DEFAULT_EXTENSION_PATTERN,\n };\n}\n\nexport function listExtensionSideEffectPackages(\n dependencies: Record<string, string>,\n sideEffects: ExtensionSideEffectsListOptions,\n): string[] {\n const names = Object.keys(dependencies).filter(\n (name) => sideEffects.pattern.test(name) && !sideEffects.exclude.has(name),\n );\n const prioritySet = new Set(sideEffects.priorityFirst);\n const first = sideEffects.priorityFirst.filter((p) => names.includes(p));\n const rest = names.filter((n) => !prioritySet.has(n)).sort((a, b) => a.localeCompare(b));\n return [...first, ...rest];\n}\n\nexport function resolveDepVersionsPlugin(\n options?: ResolveDepVersionsPluginOptions,\n): Plugin {\n let appRoot = process.cwd();\n let extensionSideEffectsActive = false;\n let extensionImportPackages: string[] = [];\n\n return {\n name: 'resolve-dep-versions',\n config(config) {\n const root = config.root ? path.resolve(config.root) : process.cwd();\n const info = resolvePackageInfo(root, options);\n const value =\n info ?? {\n name: '',\n version: '0.0.0',\n description: undefined,\n dependencies: {},\n marketplaceCatalogUrls: undefined,\n };\n return {\n define: {\n [RESOLVED_PACKAGE_INFO_KEY]: JSON.stringify(value),\n },\n };\n },\n configResolved(config) {\n appRoot = path.resolve(config.root ?? process.cwd());\n const normalized = normalizeExtensionSideEffects(options?.extensionSideEffects);\n extensionSideEffectsActive = normalized !== null;\n if (!normalized) {\n extensionImportPackages = [];\n return;\n }\n const info = resolvePackageInfo(appRoot, options);\n extensionImportPackages = listExtensionSideEffectPackages(\n info?.dependencies ?? {},\n normalized,\n );\n },\n resolveId(id) {\n if (id === VIRTUAL_EXTENSION_IMPORTS) {\n return RESOLVED_VIRTUAL_EXTENSION_IMPORTS;\n }\n return undefined;\n },\n load(id) {\n if (id !== RESOLVED_VIRTUAL_EXTENSION_IMPORTS) {\n return null;\n }\n if (extensionImportPackages.length === 0) {\n return 'export {};\\n';\n }\n return extensionImportPackages.map((pkg) => `import ${JSON.stringify(pkg)};\\n`).join('');\n },\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n if (!extensionSideEffectsActive) {\n return html;\n }\n if (extensionImportPackages.length === 0) {\n return html;\n }\n if (html.includes(VIRTUAL_EXTENSION_IMPORTS)) {\n return html;\n }\n if (!MAIN_TS_SCRIPT_RE.test(html)) {\n return html;\n }\n const inject = `<script type=\"module\">import ${JSON.stringify(VIRTUAL_EXTENSION_IMPORTS)};</script>\\n`;\n return html.replace(MAIN_TS_SCRIPT_RE, (match) => `${inject}${match}`);\n },\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"vite-plugin-resolve-deps.js","names":[],"sources":["../src/vite-plugin-resolve-deps.ts"],"sourcesContent":["import { readFileSync, existsSync } from 'fs';\nimport path from 'path';\nimport type { Plugin } from 'vite';\n\nexport interface ResolvedPackageInfo {\n name: string;\n version: string;\n description?: string;\n dependencies: Record<string, string>;\n marketplaceCatalogUrls?: string[];\n}\n\ninterface PackageJson {\n name?: string;\n version?: string;\n description?: string;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\nfunction packageJsonPath(appRoot: string, packageName: string): string | null {\n const segments = packageName.startsWith('@')\n ? packageName.split('/')\n : [packageName];\n const relativePath = path.join('node_modules', ...segments, 'package.json');\n let dir = path.resolve(appRoot);\n const root = path.parse(dir).root;\n\n while (true) {\n const pkgPath = path.join(dir, relativePath);\n if (existsSync(pkgPath)) {\n return pkgPath;\n }\n if (dir === root) break;\n dir = path.dirname(dir);\n }\n return null;\n}\n\nfunction readPackageJson(appRoot: string, packageName: string): PackageJson | null {\n const pkgPath = packageJsonPath(appRoot, packageName);\n if (!pkgPath) return null;\n try {\n return JSON.parse(readFileSync(pkgPath, 'utf8')) as PackageJson;\n } catch {\n return null;\n }\n}\n\nfunction findPackageVersion(appRoot: string, depName: string): string | null {\n const pkg = readPackageJson(appRoot, depName);\n if (typeof pkg?.version === 'string') return pkg.version;\n return null;\n}\n\nfunction resolveDepVersionsFromPkg(\n appRoot: string,\n pkg: PackageJson,\n options?: { includeDevDependencies?: boolean }\n): Record<string, string> {\n const deps = { ...pkg.dependencies };\n if (options?.includeDevDependencies && pkg.devDependencies) {\n Object.assign(deps, pkg.devDependencies);\n }\n const result: Record<string, string> = {};\n for (const [name, specifier] of Object.entries(deps)) {\n const version = findPackageVersion(appRoot, name);\n result[name] = version ?? specifier;\n }\n return result;\n}\n\nexport function resolvePackageInfo(\n appRoot: string,\n options?: { includeDevDependencies?: boolean }\n): ResolvedPackageInfo | null {\n const pkgPath = path.join(appRoot, 'package.json');\n if (!existsSync(pkgPath)) return null;\n\n let pkg: PackageJson;\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as PackageJson;\n } catch {\n return null;\n }\n\n const name = typeof pkg.name === 'string' ? pkg.name : '';\n const version = typeof pkg.version === 'string' ? pkg.version : '0.0.0';\n const description = typeof pkg.description === 'string' ? pkg.description : undefined;\n const dependencies = resolveDepVersionsFromPkg(appRoot, pkg, options);\n const marketplaceCatalogUrls = (pkg as { marketplace?: { catalogUrls?: string[] } }).marketplace?.catalogUrls;\n\n return { name, version, description, dependencies, marketplaceCatalogUrls };\n}\n\nexport function resolveDepVersions(\n appRoot: string,\n options?: { includeDevDependencies?: boolean }\n): Record<string, string> {\n const info = resolvePackageInfo(appRoot, options);\n return info?.dependencies ?? {};\n}\n\nconst RESOLVED_PACKAGE_INFO_KEY = '__RESOLVED_PACKAGE_INFO__';\n\n/** Virtual module id; injected into `index.html` before `main.ts` when extension side-effects are enabled. */\nexport const VIRTUAL_EXTENSION_IMPORTS = 'virtual:eclipse-docks-extension-imports';\n\nconst RESOLVED_VIRTUAL_EXTENSION_IMPORTS = `\\0${VIRTUAL_EXTENSION_IMPORTS}`;\n\n/** Unscoped `extension-*` or scoped `@namespace/extension-*` (any npm scope). */\nconst DEFAULT_EXTENSION_PATTERN = /^(?:@[^/]+\\/)?extension-/;\n\nconst DEFAULT_PRIORITY_FIRST = ['@eclipse-docks/extension-pwa', 'extension-pwa'];\n\n/** Matches Vite’s default app entry in index.html. */\nconst MAIN_TS_SCRIPT_RE =\n /<script\\b[^>]*\\btype\\s*=\\s*[\"']module[\"'][^>]*\\bsrc\\s*=\\s*[\"'][^\"']*\\/src\\/main\\.ts[\"'][^>]*>\\s*<\\/script>/i;\n\nexport interface ExtensionSideEffectsOptions {\n /**\n * When false, disables automatic extension side-effect imports. Omitted or true keeps them on.\n */\n enabled?: boolean;\n /** Dependency names to skip (even if they match `packageNamePattern`). */\n exclude?: string[];\n /**\n * Packages to load first, in order (only those present in dependencies are imported).\n * Default includes PWA so `beforeinstallprompt` can register early.\n */\n priorityFirst?: string[];\n /**\n * Which direct `dependencies` keys qualify as Docks-style extensions (`extension-*`, with an optional npm scope).\n * @default /^(?:@[^/]+\\/)?extension-/\n */\n packageNamePattern?: RegExp;\n}\n\nexport type ResolveDepVersionsPluginOptions = {\n includeDevDependencies?: boolean;\n /**\n * By default, registers a virtual module that side-effect-imports every matching direct\n * `dependencies` entry (see `ExtensionSideEffectsOptions`), and injects\n * `import 'virtual:eclipse-docks-extension-imports'` into `index.html` **before** `/src/main.ts`.\n * Pass `false` or `{ enabled: false }` to disable.\n */\n extensionSideEffects?: boolean | ExtensionSideEffectsOptions;\n};\n\n/** Normalized match options for {@link listExtensionSideEffectPackages}. */\nexport type ExtensionSideEffectsListOptions = {\n exclude: Set<string>;\n priorityFirst: string[];\n pattern: RegExp;\n};\n\nfunction normalizeExtensionSideEffects(\n opt: boolean | ExtensionSideEffectsOptions | undefined,\n): ExtensionSideEffectsListOptions | null {\n if (opt === false) return null;\n if (opt === undefined || opt === true) {\n return {\n exclude: new Set(),\n priorityFirst: [...DEFAULT_PRIORITY_FIRST],\n pattern: DEFAULT_EXTENSION_PATTERN,\n };\n }\n if (opt.enabled === false) return null;\n return {\n exclude: new Set(opt.exclude ?? []),\n priorityFirst: opt.priorityFirst ?? [...DEFAULT_PRIORITY_FIRST],\n pattern: opt.packageNamePattern ?? DEFAULT_EXTENSION_PATTERN,\n };\n}\n\nexport function listExtensionSideEffectPackages(\n dependencies: Record<string, string>,\n sideEffects: ExtensionSideEffectsListOptions,\n): string[] {\n const names = Object.keys(dependencies).filter(\n (name) => sideEffects.pattern.test(name) && !sideEffects.exclude.has(name),\n );\n const prioritySet = new Set(sideEffects.priorityFirst);\n const first = sideEffects.priorityFirst.filter((p) => names.includes(p));\n const rest = names.filter((n) => !prioritySet.has(n)).sort((a, b) => a.localeCompare(b));\n return [...first, ...rest];\n}\n\n/**\n * Walks extension package.json `dependencies` to collect transitive extension-* packages\n * (e.g. extension-utils → extension-python-runtime → extension-terminal).\n */\nexport function collectTransitiveExtensionPackages(\n appRoot: string,\n seeds: string[],\n sideEffects: ExtensionSideEffectsListOptions,\n): string[] {\n const found = new Set<string>();\n const queue = [...seeds];\n const visited = new Set<string>();\n\n while (queue.length > 0) {\n const name = queue.shift()!;\n if (visited.has(name)) continue;\n visited.add(name);\n\n const pkg = readPackageJson(appRoot, name);\n if (!pkg?.dependencies) continue;\n\n for (const dep of Object.keys(pkg.dependencies)) {\n if (!sideEffects.pattern.test(dep) || sideEffects.exclude.has(dep)) continue;\n found.add(dep);\n queue.push(dep);\n }\n }\n\n return [...found];\n}\n\nexport function resolveExtensionSideEffectPackages(\n appRoot: string,\n dependencies: Record<string, string>,\n sideEffects: ExtensionSideEffectsListOptions,\n): string[] {\n const direct = listExtensionSideEffectPackages(dependencies, sideEffects);\n const transitive = collectTransitiveExtensionPackages(appRoot, direct, sideEffects);\n const allNames = [...new Set([...direct, ...transitive])];\n return listExtensionSideEffectPackages(\n Object.fromEntries(allNames.map((name) => [name, '*'])),\n sideEffects,\n );\n}\n\nexport function resolveDepVersionsPlugin(\n options?: ResolveDepVersionsPluginOptions,\n): Plugin {\n let appRoot = process.cwd();\n let extensionSideEffectsActive = false;\n let extensionImportPackages: string[] = [];\n\n return {\n name: 'resolve-dep-versions',\n config(config) {\n const root = config.root ? path.resolve(config.root) : process.cwd();\n const info = resolvePackageInfo(root, options);\n const value =\n info ?? {\n name: '',\n version: '0.0.0',\n description: undefined,\n dependencies: {},\n marketplaceCatalogUrls: undefined,\n };\n return {\n define: {\n [RESOLVED_PACKAGE_INFO_KEY]: JSON.stringify(value),\n },\n };\n },\n configResolved(config) {\n appRoot = path.resolve(config.root ?? process.cwd());\n const normalized = normalizeExtensionSideEffects(options?.extensionSideEffects);\n extensionSideEffectsActive = normalized !== null;\n if (!normalized) {\n extensionImportPackages = [];\n return;\n }\n const info = resolvePackageInfo(appRoot, options);\n extensionImportPackages = resolveExtensionSideEffectPackages(\n appRoot,\n info?.dependencies ?? {},\n normalized,\n );\n },\n resolveId(id) {\n if (id === VIRTUAL_EXTENSION_IMPORTS) {\n return RESOLVED_VIRTUAL_EXTENSION_IMPORTS;\n }\n return undefined;\n },\n load(id) {\n if (id !== RESOLVED_VIRTUAL_EXTENSION_IMPORTS) {\n return null;\n }\n if (extensionImportPackages.length === 0) {\n return 'export {};\\n';\n }\n return extensionImportPackages.map((pkg) => `import ${JSON.stringify(pkg)};\\n`).join('');\n },\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n if (!extensionSideEffectsActive) {\n return html;\n }\n if (extensionImportPackages.length === 0) {\n return html;\n }\n if (html.includes(VIRTUAL_EXTENSION_IMPORTS)) {\n return html;\n }\n if (!MAIN_TS_SCRIPT_RE.test(html)) {\n return html;\n }\n const inject = `<script type=\"module\">import ${JSON.stringify(VIRTUAL_EXTENSION_IMPORTS)};</script>\\n`;\n return html.replace(MAIN_TS_SCRIPT_RE, (match) => `${inject}${match}`);\n },\n },\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"widget-DbolQH0C.js","names":[],"sources":["../src/core/toast.ts","../src/core/contribution-mapping.ts","../src/core/contributionregistry.ts","../src/core/appstate.ts","../src/core/commandregistry.ts","../src/externals/wa-tree-shadow-parts.ts","../src/core/signals.ts","../src/widgets/widget.ts"],"sourcesContent":["import Toastify from 'toastify-js';\nimport 'toastify-js/src/toastify.css';\nimport { createLogger } from './logger';\n\nconst logger = createLogger('Toast');\n\nconst DURATION = 4000;\nconst BASE = { duration: DURATION, gravity: 'bottom' as const, position: 'right' as const, close: true };\n\nconst show = (msg: string, style: { background: string; color: string }) => {\n Toastify({ ...BASE, text: msg, style }).showToast();\n};\n\nexport const toastInfo = (msg: string) => {\n logger.info(msg);\n show(msg, { background: 'var(--wa-color-brand-50)', color: 'var(--wa-color-brand-on)' });\n};\n\nexport const toastError = (msg: string) => {\n logger.error(msg);\n show(msg, { background: 'var(--wa-color-danger-50)', color: 'var(--wa-color-danger-on)' });\n};\n\nexport const toastWarning = (msg: string) => {\n logger.warn(msg);\n show(msg, { background: 'var(--wa-color-warning-50)', color: 'var(--wa-color-warning-on)' });\n};\n","import { rootContext } from \"./di\";\nimport type { Contribution } from \"./contributionregistry\";\n\nexport interface ContributionNameRemap {\n /**\n * Globally unique, namespaced contribution name\n * (e.g. \"view.filebrowser\", \"toolbar.filebrowser.rename\").\n */\n name: string;\n /**\n * Effective target slots for this contribution.\n * If empty, the original target is used.\n */\n targets: string[];\n}\n\ninterface NameRemapState {\n appTargets: string[] | undefined;\n globalTargets: string[] | undefined;\n}\n\n/**\n * Registry responsible for resolving effective contribution targets.\n *\n * It supports:\n * - Global (framework-level) remaps.\n * - App-level remaps coming from AppDefinition.\n * - Multiple targets per contribution.\n *\n * Resolution precedence:\n * app-level remap > global remap > original target\n */\nclass ContributionTargetMappingRegistry {\n private globalNameRemaps = new Map<string, string[]>();\n private appNameRemaps = new Map<string, string[]>();\n\n setGlobalNameRemap(name: string, targets: string[]): void {\n this.globalNameRemaps.set(name, this.normalizeTargets(targets));\n }\n\n /** Clears all remaps. For testing only. */\n resetForTesting(): void {\n this.globalNameRemaps.clear();\n this.appNameRemaps.clear();\n }\n\n applyAppNameRemaps(remaps: ContributionNameRemap[] | undefined | null): void {\n this.appNameRemaps.clear();\n if (!remaps) return;\n for (const remap of remaps) {\n this.appNameRemaps.set(remap.name, this.normalizeTargets(remap.targets));\n }\n }\n\n /**\n * Returns the effective target slots for a contribution given its original target.\n *\n * Semantics:\n * - When a remap exists with a non-empty targets array, those targets are used.\n * - When a remap exists with an empty targets array, the contribution is disabled (no targets).\n * - When no remap exists, the original target is used.\n */\n getEffectiveTargets(originalTarget: string, contribution: Contribution): string[] {\n const name = (contribution as any).name as string | undefined;\n if (!name) {\n return [originalTarget];\n }\n\n const appTargets = this.appNameRemaps.get(name);\n if (appTargets) {\n return appTargets.length > 0 ? appTargets : [];\n }\n\n const globalTargets = this.globalNameRemaps.get(name);\n if (globalTargets) {\n return globalTargets.length > 0 ? globalTargets : [];\n }\n\n return [originalTarget];\n }\n\n /**\n * Returns current mapping state for debugging and tooling.\n */\n listNameRemaps(): Record<string, NameRemapState> {\n const result: Record<string, NameRemapState> = {};\n const allNames = new Set<string>([\n ...this.globalNameRemaps.keys(),\n ...this.appNameRemaps.keys()\n ]);\n for (const name of allNames) {\n result[name] = {\n appTargets: this.appNameRemaps.get(name),\n globalTargets: this.globalNameRemaps.get(name)\n };\n }\n return result;\n }\n\n private normalizeTargets(targets: string[]): string[] {\n const seen = new Set<string>();\n const result: string[] = [];\n for (const t of targets) {\n if (!t || seen.has(t)) continue;\n seen.add(t);\n result.push(t);\n }\n return result;\n }\n}\n\nexport const contributionTargetMappingRegistry = new ContributionTargetMappingRegistry();\nrootContext.put(\"contributionTargetMappingRegistry\", contributionTargetMappingRegistry);\n\n/**\n * Public helper to override contribution targets programmatically.\n * This sets a global remap that applies across all apps.\n */\nexport function overrideContributionTargets(name: string, targets: string | string[]): void {\n const normalized = Array.isArray(targets) ? targets : [targets];\n contributionTargetMappingRegistry.setGlobalNameRemap(name, normalized);\n}\n\n/**\n * Apply config-driven name remaps from a simple object.\n * Intended for hosts that want to load JSON/YAML mapping files.\n */\nexport function applyConfigNameRemaps(config: Record<string, string | string[]>): void {\n for (const [name, targets] of Object.entries(config)) {\n const normalized = Array.isArray(targets) ? targets : [targets];\n contributionTargetMappingRegistry.setGlobalNameRemap(name, normalized);\n }\n}\n\n/**\n * Convenience helper for debugging: returns current remap state\n * for all known contribution names.\n */\nexport function listContributionNameRemaps(): Record<string, NameRemapState> {\n return contributionTargetMappingRegistry.listNameRemaps();\n}\n\n","import { Signal } from \"@lit-labs/signals\";\nimport { TemplateResult } from \"lit\";\nimport { publish } from \"./events\";\nimport { rootContext } from \"./di\";\nimport { contributionTargetMappingRegistry } from \"./contribution-mapping\";\n\nexport const TOPIC_CONTRIBUTEIONS_CHANGED = \"events/contributionregistry/contributionsChanged\"\n\nexport interface ContributionChangeEvent {\n target: string;\n contributions: Contribution[];\n}\n\nexport interface Contribution {\n /** Optional globally unique, namespaced contribution name (e.g. \"view.filebrowser\"). */\n name?: string;\n target?: string;\n label: string;\n /** When true, UI may show `label` alongside the icon (e.g. toolbar). Honored where supported in UI. */\n showLabel?: boolean;\n icon?: string;\n slot?: string;\n disabled?: (() => boolean) | Signal.Computed<boolean>;\n /** When false, the contribution is not rendered (default: shown). Honored where supported in UI. */\n visible?: (() => boolean) | Signal.Computed<boolean>;\n}\n\nexport interface CommandContribution extends Contribution {\n command: string;\n params?: Record<string, any>;\n}\n\nexport function isCommandContribution(contribution: Contribution): contribution is CommandContribution {\n return \"command\" in contribution;\n}\n\n/** Default true when `visible` is omitted. */\nexport function getContributionVisible(contribution: Contribution): boolean {\n const visible = contribution.visible as Signal.Computed<boolean> | undefined;\n if (!visible) return true;\n return visible.get() !== false;\n}\n\n/** Default false when `disabled` is omitted. */\nexport function getContributionDisabled(contribution: Contribution): boolean {\n const disabled = contribution.disabled as Signal.Computed<boolean> | undefined;\n if (!disabled) return false;\n return disabled.get() === true;\n}\n\nfunction wrapContributionReactiveFlags(contribution: Contribution): void {\n if (contribution.disabled instanceof Function) {\n contribution.disabled = new Signal.Computed<boolean>(contribution.disabled);\n }\n if (contribution.visible instanceof Function) {\n contribution.visible = new Signal.Computed<boolean>(contribution.visible);\n }\n}\n\nexport interface HTMLContribution extends Contribution {\n component: string | (() => TemplateResult)\n}\n\nexport interface TabContribution extends Contribution {\n name: string;\n /**\n * Stable editor-type identifier used for contribution targets (for example `toolbar:${editorId}`).\n * Needed because `name` is often tab-instance specific (such as file/path based) and would\n * fragment toolbar contributions across many dynamic targets.\n */\n editorId?: string;\n closable?: boolean;\n /** When true or omitted, a docks-toolbar is rendered for this tab; when false, no toolbar is shown. */\n toolbar?: boolean;\n /** When true or omitted, a contextmenu is rendered for this tab; when false, no context menu is shown. */\n contextMenu?: boolean;\n /**\n * Main-area editor type ids (`TabContribution.editorId` of the editor tab) that should reveal this tab\n * when any of them becomes the active editor in the main editor area. Omitted or empty = no auto-reveal.\n */\n coupledEditors?: string[];\n component?: (id: string) => TemplateResult;\n}\n\nexport interface IconContribution extends Contribution {\n mappings: Record<string, string>;\n priority?: number;\n}\n\nexport interface LayoutContribution extends Contribution {\n id: string;\n name: string;\n /** Optional icon name (e.g. Font Awesome) for the layout switcher. */\n icon?: string;\n component: string | { tag: string; attributes?: Record<string, string> } | (() => TemplateResult);\n onShow?: () => void | Promise<void>;\n}\n\nclass ContributionRegistry {\n private contributions: Map<string, Contribution[]> = new Map();\n\n registerContribution<T extends Contribution>(target: string, contribution: T) {\n const targetSlot = this.getOrCreateSlot(target);\n wrapContributionReactiveFlags(contribution);\n targetSlot.push(contribution);\n publish(TOPIC_CONTRIBUTEIONS_CHANGED, { target, contributions: targetSlot } as ContributionChangeEvent);\n\n const effectiveTargets = contributionTargetMappingRegistry.getEffectiveTargets(target, contribution);\n for (const effectiveTarget of effectiveTargets) {\n if (effectiveTarget === target) continue;\n const contributionsForSlot = this.getContributions(effectiveTarget);\n publish(TOPIC_CONTRIBUTEIONS_CHANGED, { target: effectiveTarget, contributions: contributionsForSlot } as ContributionChangeEvent);\n }\n }\n\n /**\n * Returns all contributions whose *effective* targets include the given slot.\n *\n * Note: This currently scans all registered contributions and resolves\n * remaps on each call (O(N) in number of contributions). This is acceptable\n * for typical Docks apps (dozens/hundreds of contributions and few slots),\n * but if contribution counts grow significantly we may want to introduce a\n * cached index of effective targets per slot to keep lookups O(1).\n */\n getContributions<T extends Contribution>(target: string): T[] {\n const results: T[] = [];\n for (const [originalTarget, list] of this.contributions.entries()) {\n const typedList = list as T[];\n for (const contribution of typedList) {\n const effectiveTargets = contributionTargetMappingRegistry.getEffectiveTargets(originalTarget, contribution);\n if (effectiveTargets.includes(target)) {\n results.push(contribution);\n }\n }\n }\n if (results.length === 0) {\n this.getOrCreateSlot(target);\n }\n return results;\n }\n\n private getOrCreateSlot(target: string): Contribution[] {\n if (!this.contributions.has(target)) {\n this.contributions.set(target, []);\n }\n return this.contributions.get(target)!;\n }\n}\n\nexport const contributionRegistry = new ContributionRegistry()\nrootContext.put(\"contributionRegistry\", contributionRegistry)","import { signal } from '@lit-labs/signals';\nimport { DocksPart } from \"../parts/part\";\n\nexport const EMPTY_SIGNALPORT = {} as any\n\nexport const activePartSignal = signal<DocksPart>(null as unknown as DocksPart)\nexport const activeEditorSignal = signal<DocksPart>(null as unknown as DocksPart)\n\nexport const partDirtySignal = signal<DocksPart>(null as unknown as DocksPart)\n\nexport const activeTasksSignal = signal<number>(0)\n\nexport const activeSelectionSignal = signal<any>(undefined)\n\nexport const perspectiveSwitchedSignal = signal<{name: string, timestamp: number}>({name: '', timestamp: 0})","import logger from \"./logger\";\nimport { publish } from \"./events\";\nimport { CommandContribution, Contribution, contributionRegistry } from \"./contributionregistry\";\nimport { rootContext } from \"./di\";\nimport { activePartSignal, activeEditorSignal } from \"./appstate\";\n\nexport const TOPIC_COMMAND_REGISTERED = \"events/commandregistry/commandRegistered\";\n\nexport interface Parameter {\n name: string;\n description: string;\n type?: string;\n required: boolean;\n allowedValues?: string[];\n}\n\nexport interface Variable {\n name: string;\n description: string;\n type?: string;\n}\n\nexport interface LLMOptions {\n prompt?: string;\n model?: string;\n}\n\nexport class Command {\n id: string;\n name: string;\n description: string;\n icon?: string;\n keyBinding?: string;\n parameters?: Parameter[];\n output?: Variable[];\n llm?: LLMOptions;\n\n constructor(id: string, name: string, description: string, parameters?: Parameter[], output?: Variable[]) {\n this.id = id;\n this.name = name;\n this.description = description;\n this.parameters = parameters || [];\n this.output = output || [];\n }\n}\n\nexport interface ExecutionContext {\n [key: string]: any;\n\n params?: ExecuteParams;\n activePart?: any;\n activeEditor?: any;\n}\n\nexport interface ExecuteParams {\n [key: string]: any;\n}\n\nexport interface Handler {\n canExecute?: (context: ExecutionContext) => boolean;\n execute: (context: ExecutionContext) => any | Promise<any>;\n ranking?: number; // Higher ranking = higher priority (default: 0)\n}\n\nexport interface Commands {\n [commandId: string]: Command\n}\n\nfunction formatCommandResultForLog(value: unknown): string {\n if (value === undefined) {\n return \"undefined\";\n }\n if (value === null) {\n return \"null\";\n }\n if (typeof value === \"string\") {\n return value.length > 300 ? `${value.slice(0, 300)}…` : value;\n }\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n try {\n const s = JSON.stringify(value);\n return s.length > 300 ? `${s.slice(0, 300)}…` : s;\n } catch {\n return String(value);\n }\n}\n\nexport class CommandStack {\n //private stack: any[] = []\n\n public async execute(commandId: any, context: ExecutionContext) {\n return commandRegistry.execute(commandId, context);\n }\n\n public async undo() {\n // TODO undo last command on stack\n }\n\n public async redo() {\n // TODO redo last command on stack\n }\n}\n\nexport class CommandRegistry {\n private commands: Commands = {};\n private handlers: Map<string, Handler[]> = new Map();\n\n registerHandler(commandId: string, handler: Handler) {\n if (!this.handlers.has(commandId)) {\n this.handlers.set(commandId, []);\n }\n const handlerList = this.handlers.get(commandId) as Handler[];\n handlerList.push(handler);\n // Sort by ranking (higher ranking first), default ranking is 0\n handlerList.sort((a, b) => (b.ranking ?? 0) - (a.ranking ?? 0));\n }\n\n getHandler(commandId: string): Handler[] | undefined {\n return this.handlers.get(commandId)\n }\n\n /**\n * Creates an ExecutionContext with the current application state\n * \n * @param params Optional command parameters\n * @returns ExecutionContext with captured application state\n */\n createExecutionContext(params?: ExecuteParams): ExecutionContext {\n const context: ExecutionContext = {\n params: params || {},\n activePart: activePartSignal.get(),\n activeEditor: activeEditorSignal.get()\n };\n\n return context;\n }\n\n async execute(commandId: string, context: ExecutionContext = {}) {\n const handlers = this.getHandler(commandId);\n\n if (!handlers) {\n logger.debug(`[CommandRegistry] No handlers registered for command: ${commandId}`);\n throw new Error(`No handlers registered for command: ${commandId}`);\n }\n\n const command = this.getCommand(commandId);\n const paramsStr = context.params ? ` params: ${JSON.stringify(context.params)}` : '';\n logger.debug(`[CommandRegistry] Executing command: ${commandId}${command ? ` (${command.name})` : ''}${paramsStr}`);\n\n // Handlers are already sorted by ranking, so iterate in order\n for (const handler of handlers) {\n if (handler.canExecute === undefined || handler.canExecute(context)) {\n try {\n const raw = handler.execute(context);\n const result = await Promise.resolve(raw);\n logger.debug(\n `[CommandRegistry] Command executed successfully: ${commandId} (result: ${formatCommandResultForLog(result)})`,\n );\n return result;\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n logger.error(`[CommandRegistry] Command execution failed: ${commandId} - ${errorMsg}`);\n throw error;\n }\n }\n }\n logger.error(`[CommandRegistry] No handler found to execute command: ${commandId}`);\n }\n\n createAndRegisterCommand(id: string, name: string, description: string, parameters: Parameter[], handler?: Handler) {\n const command = new Command(id, name, description, parameters);\n this.registerCommand(command)\n if (handler) {\n this.registerHandler(id, handler)\n }\n }\n\n registerCommand(command: Command): void {\n this.commands[command.id] = command;\n publish(TOPIC_COMMAND_REGISTERED, command);\n }\n\n hasCommand(commandId: string): boolean {\n return commandId in this.commands\n }\n\n listCommands(context?: ExecutionContext): Commands {\n if (context) {\n // filter by commands which have at least one executable handler\n return Object.values(this.commands).filter(command => {\n const handlers = commandRegistry.getHandler(command.id) || []\n return handlers.some(handler => handler.canExecute === undefined || handler.canExecute(context))\n }).reduce((acc: any, command) => {\n acc[command.id] = command\n return acc\n }, {})\n }\n return this.commands\n }\n\n getCommand(commandId: string): Command {\n return <Command>this.commands[commandId]\n }\n\n registerAll(options: RegisterOptions) {\n const commandId = options.command.id\n this.registerCommand(options.command)\n if (options.handler) {\n this.registerHandler(commandId, options.handler)\n }\n if (options.contribution && options.contribution.target) {\n contributionRegistry.registerContribution(options.contribution.target, {\n command: commandId,\n ...options.contribution\n } as CommandContribution)\n }\n }\n}\n\nexport const commandRegistry = new CommandRegistry();\nrootContext.put(\"commandRegistry\", commandRegistry);\n\nexport interface RegisterOptions {\n command: Command,\n handler?: Handler,\n contribution?: Contribution\n}\n\nexport const registerAll = (options: RegisterOptions) => {\n commandRegistry.registerAll(options);\n}","import { css } from 'lit';\n\n/**\n * Stretch the Web Awesome label slot across the rest of the tree row.\n *\n * By default the slot is only as wide as its content, so clicks (and the context menu’s\n * synthetic click) in the empty area beside a short name often miss the slotted nodes.\n * `wa-tree` only updates selection when mousedown and click targets line up, so those\n * misses leave the wrong row selected. `flex: 1` makes the label column fill the row\n * so hits stay on the item’s content.\n *\n * Applied via `DocksWidget.finalizeStyles`: document-level CSS cannot target `::part()`\n * on `wa-tree-item` that lives only inside nested Lit shadow roots.\n */\nexport const waTreeLabelSlotStretch = css`\n wa-tree-item::part(label) {\n flex: 1;\n min-width: 0;\n }\n`;\n","import {Signal} from \"@lit-labs/signals\";\n\nexport const watchSignal = (signal: Signal.State<any> | Signal.Computed<any>, callback: (value: any) => void): (() => void) => {\n const watcher = new Signal.subtle.Watcher(async () => {\n try {\n await 0;\n callback(signal.get());\n } finally {\n watcher.watch(signal);\n }\n });\n watcher.watch(signal);\n signal.get();\n \n return () => {\n watcher.unwatch(signal);\n };\n}\n","import {LitElement, PropertyValues, type CSSResultGroup} from \"lit\";\nimport {waTreeLabelSlotStretch} from \"../externals/wa-tree-shadow-parts\";\nimport {subscribe as event_subscribe, unsubscribe as event_unsubscribe, type SubscriptionToken} from \"../core/events\";\nimport {toastError, toastInfo} from \"../core/toast\";\nimport {commandRegistry, ExecuteParams} from \"../core/commandregistry\";\nimport {Signal, SignalWatcher} from \"@lit-labs/signals\";\nimport {watchSignal} from \"../core/signals\";\n\nObject.defineProperty(LitElement.prototype, \"model\", {\n enumerable: true,\n configurable: true,\n writable: true\n});\n\nconst DocksWidgetBase = SignalWatcher(LitElement) as unknown as typeof LitElement;\n\nexport abstract class DocksWidget extends DocksWidgetBase {\n static finalizeStyles(styles?: CSSResultGroup) {\n return [waTreeLabelSlotStretch, ...super.finalizeStyles(styles)];\n }\n\n private signalCleanups = new Set<() => void>();\n private eventSubscriptions = new Set<SubscriptionToken>();\n\n connectedCallback() {\n super.connectedCallback();\n this.doBeforeUI()\n }\n\n disconnectedCallback() {\n this.doClose();\n super.disconnectedCallback();\n this.eventSubscriptions.forEach(token => event_unsubscribe(token));\n this.eventSubscriptions.clear();\n this.signalCleanups.forEach(cleanup => cleanup());\n this.signalCleanups.clear();\n }\n\n protected subscribe(topic: string, callback: Function): void {\n const token = event_subscribe(topic, callback.bind(this));\n this.eventSubscriptions.add(token);\n }\n\n protected showInfo(msg: string) {\n toastInfo(msg)\n }\n\n protected showError(msg: string) {\n toastError(msg)\n }\n\n protected nobubble(wrap: (event: Event) => void) {\n return (event: Event) => {\n event.stopPropagation();\n wrap.bind(this)(event);\n };\n }\n\n protected command(command: string, state: {} = {}) {\n return () => {\n void this.executeCommand(command, state);\n };\n }\n\n protected async executeCommand(command: string, params: ExecuteParams) {\n const execContext = commandRegistry.createExecutionContext(params);\n await commandRegistry.execute(command, execContext);\n }\n\n protected watch(signal: Signal.State<any> | Signal.Computed<any>, callback: (value: any) => void): void {\n const cleanup = watchSignal(signal as Signal.State<any>, callback.bind(this));\n this.signalCleanups.add(cleanup);\n }\n\n protected firstUpdated(_changedProperties: PropertyValues) {\n super.firstUpdated(_changedProperties);\n this.doInitUI()\n }\n\n protected updateIdle() {\n requestIdleCallback(() => this.requestUpdate())\n }\n\n public updateLater() {\n requestAnimationFrame(() => this.requestUpdate())\n }\n\n protected doBeforeUI() {\n }\n\n protected doInitUI() {\n }\n\n /**\n * Override to release resources when the element is disconnected (called from {@link disconnectedCallback}).\n */\n protected doClose() {\n }\n\n withRefresh(callback: () => void) {\n callback()\n this.updateLater()\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"widget-DbolQH0C.js","names":[],"sources":["../src/core/toast.ts","../src/core/contribution-mapping.ts","../src/core/contributionregistry.ts","../src/core/appstate.ts","../src/core/commandregistry.ts","../src/externals/wa-tree-shadow-parts.ts","../src/core/signals.ts","../src/widgets/widget.ts"],"sourcesContent":["import Toastify from 'toastify-js';\nimport 'toastify-js/src/toastify.css';\nimport { createLogger } from './logger';\n\nconst logger = createLogger('Toast');\n\nconst DURATION = 4000;\nconst BASE = { duration: DURATION, gravity: 'bottom' as const, position: 'right' as const, close: true };\n\nconst show = (msg: string, style: { background: string; color: string }) => {\n Toastify({ ...BASE, text: msg, style }).showToast();\n};\n\nexport const toastInfo = (msg: string) => {\n logger.info(msg);\n show(msg, { background: 'var(--wa-color-brand-50)', color: 'var(--wa-color-brand-on)' });\n};\n\nexport const toastError = (msg: string) => {\n logger.error(msg);\n show(msg, { background: 'var(--wa-color-danger-50)', color: 'var(--wa-color-danger-on)' });\n};\n\nexport const toastWarning = (msg: string) => {\n logger.warn(msg);\n show(msg, { background: 'var(--wa-color-warning-50)', color: 'var(--wa-color-warning-on)' });\n};\n","import { rootContext } from \"./di\";\nimport type { Contribution } from \"./contributionregistry\";\n\nexport interface ContributionNameRemap {\n /**\n * Globally unique, namespaced contribution name\n * (e.g. \"view.filebrowser\", \"toolbar.filebrowser.rename\").\n */\n name: string;\n /**\n * Effective target slots for this contribution.\n * If empty, the original target is used.\n */\n targets: string[];\n}\n\ninterface NameRemapState {\n appTargets: string[] | undefined;\n globalTargets: string[] | undefined;\n}\n\n/**\n * Registry responsible for resolving effective contribution targets.\n *\n * It supports:\n * - Global (framework-level) remaps.\n * - App-level remaps coming from AppDefinition.\n * - Multiple targets per contribution.\n *\n * Resolution precedence:\n * app-level remap > global remap > original target\n */\nclass ContributionTargetMappingRegistry {\n private globalNameRemaps = new Map<string, string[]>();\n private appNameRemaps = new Map<string, string[]>();\n\n setGlobalNameRemap(name: string, targets: string[]): void {\n this.globalNameRemaps.set(name, this.normalizeTargets(targets));\n }\n\n /** Clears all remaps. For testing only. */\n resetForTesting(): void {\n this.globalNameRemaps.clear();\n this.appNameRemaps.clear();\n }\n\n applyAppNameRemaps(remaps: ContributionNameRemap[] | undefined | null): void {\n this.appNameRemaps.clear();\n if (!remaps) return;\n for (const remap of remaps) {\n this.appNameRemaps.set(remap.name, this.normalizeTargets(remap.targets));\n }\n }\n\n /**\n * Returns the effective target slots for a contribution given its original target.\n *\n * Semantics:\n * - When a remap exists with a non-empty targets array, those targets are used.\n * - When a remap exists with an empty targets array, the contribution is disabled (no targets).\n * - When no remap exists, the original target is used.\n */\n getEffectiveTargets(originalTarget: string, contribution: Contribution): string[] {\n const name = (contribution as any).name as string | undefined;\n if (!name) {\n return [originalTarget];\n }\n\n const appTargets = this.appNameRemaps.get(name);\n if (appTargets) {\n return appTargets.length > 0 ? appTargets : [];\n }\n\n const globalTargets = this.globalNameRemaps.get(name);\n if (globalTargets) {\n return globalTargets.length > 0 ? globalTargets : [];\n }\n\n return [originalTarget];\n }\n\n /**\n * Returns current mapping state for debugging and tooling.\n */\n listNameRemaps(): Record<string, NameRemapState> {\n const result: Record<string, NameRemapState> = {};\n const allNames = new Set<string>([\n ...this.globalNameRemaps.keys(),\n ...this.appNameRemaps.keys()\n ]);\n for (const name of allNames) {\n result[name] = {\n appTargets: this.appNameRemaps.get(name),\n globalTargets: this.globalNameRemaps.get(name)\n };\n }\n return result;\n }\n\n private normalizeTargets(targets: string[]): string[] {\n const seen = new Set<string>();\n const result: string[] = [];\n for (const t of targets) {\n if (!t || seen.has(t)) continue;\n seen.add(t);\n result.push(t);\n }\n return result;\n }\n}\n\nexport const contributionTargetMappingRegistry = new ContributionTargetMappingRegistry();\nrootContext.put(\"contributionTargetMappingRegistry\", contributionTargetMappingRegistry);\n\n/**\n * Public helper to override contribution targets programmatically.\n * This sets a global remap that applies across all apps.\n */\nexport function overrideContributionTargets(name: string, targets: string | string[]): void {\n const normalized = Array.isArray(targets) ? targets : [targets];\n contributionTargetMappingRegistry.setGlobalNameRemap(name, normalized);\n}\n\n/**\n * Apply config-driven name remaps from a simple object.\n * Intended for hosts that want to load JSON/YAML mapping files.\n */\nexport function applyConfigNameRemaps(config: Record<string, string | string[]>): void {\n for (const [name, targets] of Object.entries(config)) {\n const normalized = Array.isArray(targets) ? targets : [targets];\n contributionTargetMappingRegistry.setGlobalNameRemap(name, normalized);\n }\n}\n\n/**\n * Convenience helper for debugging: returns current remap state\n * for all known contribution names.\n */\nexport function listContributionNameRemaps(): Record<string, NameRemapState> {\n return contributionTargetMappingRegistry.listNameRemaps();\n}\n\n","import { Signal } from \"@lit-labs/signals\";\nimport { TemplateResult } from \"lit\";\nimport { publish } from \"./events\";\nimport { rootContext } from \"./di\";\nimport { contributionTargetMappingRegistry } from \"./contribution-mapping\";\n\nexport const TOPIC_CONTRIBUTEIONS_CHANGED = \"events/contributionregistry/contributionsChanged\"\n\nexport interface ContributionChangeEvent {\n target: string;\n contributions: Contribution[];\n}\n\nexport interface Contribution {\n /** Optional globally unique, namespaced contribution name (e.g. \"view.filebrowser\"). */\n name?: string;\n target?: string;\n label: string;\n /** When true, UI may show `label` alongside the icon (e.g. toolbar). Honored where supported in UI. */\n showLabel?: boolean;\n icon?: string;\n slot?: string;\n disabled?: (() => boolean) | Signal.Computed<boolean>;\n /** When false, the contribution is not rendered (default: shown). Honored where supported in UI. */\n visible?: (() => boolean) | Signal.Computed<boolean>;\n}\n\nexport interface CommandContribution extends Contribution {\n command: string;\n params?: Record<string, any>;\n}\n\nexport function isCommandContribution(contribution: Contribution): contribution is CommandContribution {\n return \"command\" in contribution;\n}\n\n/** Default true when `visible` is omitted. */\nexport function getContributionVisible(contribution: Contribution): boolean {\n const visible = contribution.visible as Signal.Computed<boolean> | undefined;\n if (!visible) return true;\n return visible.get() !== false;\n}\n\n/** Default false when `disabled` is omitted. */\nexport function getContributionDisabled(contribution: Contribution): boolean {\n const disabled = contribution.disabled as Signal.Computed<boolean> | undefined;\n if (!disabled) return false;\n return disabled.get() === true;\n}\n\nfunction wrapContributionReactiveFlags(contribution: Contribution): void {\n if (contribution.disabled instanceof Function) {\n contribution.disabled = new Signal.Computed<boolean>(contribution.disabled);\n }\n if (contribution.visible instanceof Function) {\n contribution.visible = new Signal.Computed<boolean>(contribution.visible);\n }\n}\n\nexport interface HTMLContribution extends Contribution {\n component: string | (() => TemplateResult)\n}\n\nexport interface TabContribution extends Contribution {\n name: string;\n /**\n * Stable editor-type identifier used for contribution targets (for example `toolbar:${editorId}`).\n * Needed because `name` is often tab-instance specific (such as file/path based) and would\n * fragment toolbar contributions across many dynamic targets.\n */\n editorId?: string;\n closable?: boolean;\n /** When true or omitted, a docks-toolbar is rendered for this tab; when false, no toolbar is shown. */\n toolbar?: boolean;\n /** When true or omitted, a contextmenu is rendered for this tab; when false, no context menu is shown. */\n contextMenu?: boolean;\n /**\n * Main-area editor type ids (`TabContribution.editorId` of the editor tab) that should reveal this tab\n * when any of them becomes the active editor in the main editor area. Omitted or empty = no auto-reveal.\n */\n coupledEditors?: string[];\n /** Return false to cancel tab close (after dirty-tab prompt, if any). */\n beforeClose?: () => boolean | Promise<boolean>;\n component?: (id: string) => TemplateResult;\n}\n\nexport interface IconContribution extends Contribution {\n mappings: Record<string, string>;\n priority?: number;\n}\n\nexport interface LayoutContribution extends Contribution {\n id: string;\n name: string;\n /** Optional icon name (e.g. Font Awesome) for the layout switcher. */\n icon?: string;\n component: string | { tag: string; attributes?: Record<string, string> } | (() => TemplateResult);\n onShow?: () => void | Promise<void>;\n}\n\nclass ContributionRegistry {\n private contributions: Map<string, Contribution[]> = new Map();\n\n registerContribution<T extends Contribution>(target: string, contribution: T) {\n const targetSlot = this.getOrCreateSlot(target);\n wrapContributionReactiveFlags(contribution);\n targetSlot.push(contribution);\n publish(TOPIC_CONTRIBUTEIONS_CHANGED, { target, contributions: targetSlot } as ContributionChangeEvent);\n\n const effectiveTargets = contributionTargetMappingRegistry.getEffectiveTargets(target, contribution);\n for (const effectiveTarget of effectiveTargets) {\n if (effectiveTarget === target) continue;\n const contributionsForSlot = this.getContributions(effectiveTarget);\n publish(TOPIC_CONTRIBUTEIONS_CHANGED, { target: effectiveTarget, contributions: contributionsForSlot } as ContributionChangeEvent);\n }\n }\n\n /**\n * Returns all contributions whose *effective* targets include the given slot.\n *\n * Note: This currently scans all registered contributions and resolves\n * remaps on each call (O(N) in number of contributions). This is acceptable\n * for typical Docks apps (dozens/hundreds of contributions and few slots),\n * but if contribution counts grow significantly we may want to introduce a\n * cached index of effective targets per slot to keep lookups O(1).\n */\n getContributions<T extends Contribution>(target: string): T[] {\n const results: T[] = [];\n for (const [originalTarget, list] of this.contributions.entries()) {\n const typedList = list as T[];\n for (const contribution of typedList) {\n const effectiveTargets = contributionTargetMappingRegistry.getEffectiveTargets(originalTarget, contribution);\n if (effectiveTargets.includes(target)) {\n results.push(contribution);\n }\n }\n }\n if (results.length === 0) {\n this.getOrCreateSlot(target);\n }\n return results;\n }\n\n private getOrCreateSlot(target: string): Contribution[] {\n if (!this.contributions.has(target)) {\n this.contributions.set(target, []);\n }\n return this.contributions.get(target)!;\n }\n}\n\nexport const contributionRegistry = new ContributionRegistry()\nrootContext.put(\"contributionRegistry\", contributionRegistry)","import { signal } from '@lit-labs/signals';\nimport { DocksPart } from \"../parts/part\";\n\nexport const EMPTY_SIGNALPORT = {} as any\n\nexport const activePartSignal = signal<DocksPart>(null as unknown as DocksPart)\nexport const activeEditorSignal = signal<DocksPart>(null as unknown as DocksPart)\n\nexport const partDirtySignal = signal<DocksPart>(null as unknown as DocksPart)\n\nexport const activeTasksSignal = signal<number>(0)\n\nexport const activeSelectionSignal = signal<any>(undefined)\n\nexport const perspectiveSwitchedSignal = signal<{name: string, timestamp: number}>({name: '', timestamp: 0})","import logger from \"./logger\";\nimport { publish } from \"./events\";\nimport { CommandContribution, Contribution, contributionRegistry } from \"./contributionregistry\";\nimport { rootContext } from \"./di\";\nimport { activePartSignal, activeEditorSignal } from \"./appstate\";\n\nexport const TOPIC_COMMAND_REGISTERED = \"events/commandregistry/commandRegistered\";\n\nexport interface Parameter {\n name: string;\n description: string;\n type?: string;\n required: boolean;\n allowedValues?: string[];\n}\n\nexport interface Variable {\n name: string;\n description: string;\n type?: string;\n}\n\nexport interface LLMOptions {\n prompt?: string;\n model?: string;\n}\n\nexport class Command {\n id: string;\n name: string;\n description: string;\n icon?: string;\n keyBinding?: string;\n parameters?: Parameter[];\n output?: Variable[];\n llm?: LLMOptions;\n\n constructor(id: string, name: string, description: string, parameters?: Parameter[], output?: Variable[]) {\n this.id = id;\n this.name = name;\n this.description = description;\n this.parameters = parameters || [];\n this.output = output || [];\n }\n}\n\nexport interface ExecutionContext {\n [key: string]: any;\n\n params?: ExecuteParams;\n activePart?: any;\n activeEditor?: any;\n}\n\nexport interface ExecuteParams {\n [key: string]: any;\n}\n\nexport interface Handler {\n canExecute?: (context: ExecutionContext) => boolean;\n execute: (context: ExecutionContext) => any | Promise<any>;\n ranking?: number; // Higher ranking = higher priority (default: 0)\n}\n\nexport interface Commands {\n [commandId: string]: Command\n}\n\nfunction formatCommandResultForLog(value: unknown): string {\n if (value === undefined) {\n return \"undefined\";\n }\n if (value === null) {\n return \"null\";\n }\n if (typeof value === \"string\") {\n return value.length > 300 ? `${value.slice(0, 300)}…` : value;\n }\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n try {\n const s = JSON.stringify(value);\n return s.length > 300 ? `${s.slice(0, 300)}…` : s;\n } catch {\n return String(value);\n }\n}\n\nexport class CommandStack {\n //private stack: any[] = []\n\n public async execute(commandId: any, context: ExecutionContext) {\n return commandRegistry.execute(commandId, context);\n }\n\n public async undo() {\n // TODO undo last command on stack\n }\n\n public async redo() {\n // TODO redo last command on stack\n }\n}\n\nexport class CommandRegistry {\n private commands: Commands = {};\n private handlers: Map<string, Handler[]> = new Map();\n\n registerHandler(commandId: string, handler: Handler) {\n if (!this.handlers.has(commandId)) {\n this.handlers.set(commandId, []);\n }\n const handlerList = this.handlers.get(commandId) as Handler[];\n handlerList.push(handler);\n // Sort by ranking (higher ranking first), default ranking is 0\n handlerList.sort((a, b) => (b.ranking ?? 0) - (a.ranking ?? 0));\n }\n\n getHandler(commandId: string): Handler[] | undefined {\n return this.handlers.get(commandId)\n }\n\n /**\n * Creates an ExecutionContext with the current application state\n * \n * @param params Optional command parameters\n * @returns ExecutionContext with captured application state\n */\n createExecutionContext(params?: ExecuteParams): ExecutionContext {\n const context: ExecutionContext = {\n params: params || {},\n activePart: activePartSignal.get(),\n activeEditor: activeEditorSignal.get()\n };\n\n return context;\n }\n\n async execute(commandId: string, context: ExecutionContext = {}) {\n const handlers = this.getHandler(commandId);\n\n if (!handlers) {\n logger.debug(`[CommandRegistry] No handlers registered for command: ${commandId}`);\n throw new Error(`No handlers registered for command: ${commandId}`);\n }\n\n const command = this.getCommand(commandId);\n const paramsStr = context.params ? ` params: ${JSON.stringify(context.params)}` : '';\n logger.debug(`[CommandRegistry] Executing command: ${commandId}${command ? ` (${command.name})` : ''}${paramsStr}`);\n\n // Handlers are already sorted by ranking, so iterate in order\n for (const handler of handlers) {\n if (handler.canExecute === undefined || handler.canExecute(context)) {\n try {\n const raw = handler.execute(context);\n const result = await Promise.resolve(raw);\n logger.debug(\n `[CommandRegistry] Command executed successfully: ${commandId} (result: ${formatCommandResultForLog(result)})`,\n );\n return result;\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n logger.error(`[CommandRegistry] Command execution failed: ${commandId} - ${errorMsg}`);\n throw error;\n }\n }\n }\n logger.error(`[CommandRegistry] No handler found to execute command: ${commandId}`);\n }\n\n createAndRegisterCommand(id: string, name: string, description: string, parameters: Parameter[], handler?: Handler) {\n const command = new Command(id, name, description, parameters);\n this.registerCommand(command)\n if (handler) {\n this.registerHandler(id, handler)\n }\n }\n\n registerCommand(command: Command): void {\n this.commands[command.id] = command;\n publish(TOPIC_COMMAND_REGISTERED, command);\n }\n\n hasCommand(commandId: string): boolean {\n return commandId in this.commands\n }\n\n listCommands(context?: ExecutionContext): Commands {\n if (context) {\n // filter by commands which have at least one executable handler\n return Object.values(this.commands).filter(command => {\n const handlers = commandRegistry.getHandler(command.id) || []\n return handlers.some(handler => handler.canExecute === undefined || handler.canExecute(context))\n }).reduce((acc: any, command) => {\n acc[command.id] = command\n return acc\n }, {})\n }\n return this.commands\n }\n\n getCommand(commandId: string): Command {\n return <Command>this.commands[commandId]\n }\n\n registerAll(options: RegisterOptions) {\n const commandId = options.command.id\n this.registerCommand(options.command)\n if (options.handler) {\n this.registerHandler(commandId, options.handler)\n }\n if (options.contribution && options.contribution.target) {\n contributionRegistry.registerContribution(options.contribution.target, {\n command: commandId,\n ...options.contribution\n } as CommandContribution)\n }\n }\n}\n\nexport const commandRegistry = new CommandRegistry();\nrootContext.put(\"commandRegistry\", commandRegistry);\n\nexport interface RegisterOptions {\n command: Command,\n handler?: Handler,\n contribution?: Contribution\n}\n\nexport const registerAll = (options: RegisterOptions) => {\n commandRegistry.registerAll(options);\n}","import { css } from 'lit';\n\n/**\n * Stretch the Web Awesome label slot across the rest of the tree row.\n *\n * By default the slot is only as wide as its content, so clicks (and the context menu’s\n * synthetic click) in the empty area beside a short name often miss the slotted nodes.\n * `wa-tree` only updates selection when mousedown and click targets line up, so those\n * misses leave the wrong row selected. `flex: 1` makes the label column fill the row\n * so hits stay on the item’s content.\n *\n * Applied via `DocksWidget.finalizeStyles`: document-level CSS cannot target `::part()`\n * on `wa-tree-item` that lives only inside nested Lit shadow roots.\n */\nexport const waTreeLabelSlotStretch = css`\n wa-tree-item::part(label) {\n flex: 1;\n min-width: 0;\n }\n`;\n","import {Signal} from \"@lit-labs/signals\";\n\nexport const watchSignal = (signal: Signal.State<any> | Signal.Computed<any>, callback: (value: any) => void): (() => void) => {\n const watcher = new Signal.subtle.Watcher(async () => {\n try {\n await 0;\n callback(signal.get());\n } finally {\n watcher.watch(signal);\n }\n });\n watcher.watch(signal);\n signal.get();\n \n return () => {\n watcher.unwatch(signal);\n };\n}\n","import {LitElement, PropertyValues, type CSSResultGroup} from \"lit\";\nimport {waTreeLabelSlotStretch} from \"../externals/wa-tree-shadow-parts\";\nimport {subscribe as event_subscribe, unsubscribe as event_unsubscribe, type SubscriptionToken} from \"../core/events\";\nimport {toastError, toastInfo} from \"../core/toast\";\nimport {commandRegistry, ExecuteParams} from \"../core/commandregistry\";\nimport {Signal, SignalWatcher} from \"@lit-labs/signals\";\nimport {watchSignal} from \"../core/signals\";\n\nObject.defineProperty(LitElement.prototype, \"model\", {\n enumerable: true,\n configurable: true,\n writable: true\n});\n\nconst DocksWidgetBase = SignalWatcher(LitElement) as unknown as typeof LitElement;\n\nexport abstract class DocksWidget extends DocksWidgetBase {\n static finalizeStyles(styles?: CSSResultGroup) {\n return [waTreeLabelSlotStretch, ...super.finalizeStyles(styles)];\n }\n\n private signalCleanups = new Set<() => void>();\n private eventSubscriptions = new Set<SubscriptionToken>();\n\n connectedCallback() {\n super.connectedCallback();\n this.doBeforeUI()\n }\n\n disconnectedCallback() {\n this.doClose();\n super.disconnectedCallback();\n this.eventSubscriptions.forEach(token => event_unsubscribe(token));\n this.eventSubscriptions.clear();\n this.signalCleanups.forEach(cleanup => cleanup());\n this.signalCleanups.clear();\n }\n\n protected subscribe(topic: string, callback: Function): void {\n const token = event_subscribe(topic, callback.bind(this));\n this.eventSubscriptions.add(token);\n }\n\n protected showInfo(msg: string) {\n toastInfo(msg)\n }\n\n protected showError(msg: string) {\n toastError(msg)\n }\n\n protected nobubble(wrap: (event: Event) => void) {\n return (event: Event) => {\n event.stopPropagation();\n wrap.bind(this)(event);\n };\n }\n\n protected command(command: string, state: {} = {}) {\n return () => {\n void this.executeCommand(command, state);\n };\n }\n\n protected async executeCommand(command: string, params: ExecuteParams) {\n const execContext = commandRegistry.createExecutionContext(params);\n await commandRegistry.execute(command, execContext);\n }\n\n protected watch(signal: Signal.State<any> | Signal.Computed<any>, callback: (value: any) => void): void {\n const cleanup = watchSignal(signal as Signal.State<any>, callback.bind(this));\n this.signalCleanups.add(cleanup);\n }\n\n protected firstUpdated(_changedProperties: PropertyValues) {\n super.firstUpdated(_changedProperties);\n this.doInitUI()\n }\n\n protected updateIdle() {\n requestIdleCallback(() => this.requestUpdate())\n }\n\n public updateLater() {\n requestAnimationFrame(() => this.requestUpdate())\n }\n\n protected doBeforeUI() {\n }\n\n protected doInitUI() {\n }\n\n /**\n * Override to release resources when the element is disconnected (called from {@link disconnectedCallback}).\n */\n protected doClose() {\n }\n\n withRefresh(callback: () => void) {\n callback()\n this.updateLater()\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eclipse-docks/core",
3
- "version": "0.7.110",
3
+ "version": "0.7.112",
4
4
  "description": "Eclipse Docks platform core: registries, services, parts, widgets, and API",
5
5
  "type": "module",
6
6
  "license": "EPL-2.0",
@@ -416,14 +416,8 @@ class AppLoaderService {
416
416
  this.systemRequiredExtensions.forEach(extId => extensionsSet.add(extId));
417
417
  app.extensions = Array.from(extensionsSet);
418
418
 
419
- // At this point, all core/built-in extension modules and app contributions
420
- // have been registered. It is now safe for the extension registry to
421
- // load any extensions that are marked as enabled in settings (including
422
- // persisted external extensions).
423
- this.dispatchLoadProgress('Loading extensions…');
424
- await extensionRegistry.loadEnabledExtensions();
425
-
426
- // Enable and load app extensions so commands/contributions are registered before UI is shown
419
+ // Enable and load app extensions first so declared dependencies
420
+ // are loaded before persisted settings extensions run.
427
421
  if (app.extensions.length > 0) {
428
422
  this.dispatchLoadProgress('Enabling extensions…');
429
423
  await Promise.all(
@@ -435,6 +429,11 @@ class AppLoaderService {
435
429
  );
436
430
  }
437
431
 
432
+ // Load any additional extensions marked as enabled in settings (including
433
+ // persisted external extensions).
434
+ this.dispatchLoadProgress('Loading extensions…');
435
+ await extensionRegistry.loadEnabledExtensions();
436
+
438
437
  // Initialize new app
439
438
  if (app.initialize) {
440
439
  this.dispatchLoadProgress('Initializing…');
@@ -79,6 +79,8 @@ export interface TabContribution extends Contribution {
79
79
  * when any of them becomes the active editor in the main editor area. Omitted or empty = no auto-reveal.
80
80
  */
81
81
  coupledEditors?: string[];
82
+ /** Return false to cancel tab close (after dirty-tab prompt, if any). */
83
+ beforeClose?: () => boolean | Promise<boolean>;
82
84
  component?: (id: string) => TemplateResult;
83
85
  }
84
86
 
@@ -301,6 +301,24 @@ class ExtensionRegistry {
301
301
  * await extensionRegistry.load('@eclipse-docks/extension-notebook')
302
302
  * ```
303
303
  */
304
+ /**
305
+ * Extension ids match npm package names. When a dependency was not side-effect-imported
306
+ * (e.g. only a transitive package.json dependency), importing the package registers it.
307
+ */
308
+ private async ensureExtensionRegistered(extensionId: string): Promise<void> {
309
+ if (this.extensions[extensionId]) {
310
+ return;
311
+ }
312
+ try {
313
+ await import(/* @vite-ignore */ extensionId);
314
+ } catch (error) {
315
+ logger.debug(`Could not side-effect import extension package ${extensionId}: ${error}`);
316
+ }
317
+ if (!this.extensions[extensionId]) {
318
+ throw new Error("Extension not found: " + extensionId);
319
+ }
320
+ }
321
+
304
322
  public async load(extensionId: string, loadingChain: string[] = []): Promise<void> {
305
323
  // Already loaded, return immediately
306
324
  if (this.loadedExtensions.has(extensionId)) {
@@ -321,15 +339,20 @@ class ExtensionRegistry {
321
339
 
322
340
  const extension = this.extensions[extensionId]
323
341
  if (!extension) {
342
+ await this.ensureExtensionRegistered(extensionId);
343
+ }
344
+
345
+ const resolvedExtension = this.extensions[extensionId]
346
+ if (!resolvedExtension) {
324
347
  throw new Error("Extension not found: " + extensionId)
325
348
  }
326
349
 
327
350
  const loadingPromise = (async () => {
328
351
  try {
329
352
  logger.debug(`Loading extension: ${extensionId}`);
330
- if (extension.dependencies && extension.dependencies.length > 0) {
353
+ if (resolvedExtension.dependencies && resolvedExtension.dependencies.length > 0) {
331
354
  const newChain = [...loadingChain, extensionId]
332
- for (const depId of extension.dependencies) {
355
+ for (const depId of resolvedExtension.dependencies) {
333
356
  await this.load(depId, newChain)
334
357
  // Enable the dependency if it's not already enabled
335
358
  if (!this.isEnabled(depId)) {
@@ -339,14 +362,14 @@ class ExtensionRegistry {
339
362
  }
340
363
  }
341
364
 
342
- const module = await taskService.runAsync("Loading extension: " + extension.name, async () => {
343
- if (extension.loader) {
344
- return extension.loader()
345
- } else if (extension.url) {
346
- let finalUrl = extension.url;
347
- if (esmShService.isSourceIdentifier(extension.url)) {
348
- finalUrl = esmShService.normalizeToEsmSh(extension.url);
349
- logger.debug(`Normalized extension URL: ${extension.url} -> ${finalUrl}`);
365
+ const module = await taskService.runAsync("Loading extension: " + resolvedExtension.name, async () => {
366
+ if (resolvedExtension.loader) {
367
+ return resolvedExtension.loader()
368
+ } else if (resolvedExtension.url) {
369
+ let finalUrl = resolvedExtension.url;
370
+ if (esmShService.isSourceIdentifier(resolvedExtension.url)) {
371
+ finalUrl = esmShService.normalizeToEsmSh(resolvedExtension.url);
372
+ logger.debug(`Normalized extension URL: ${resolvedExtension.url} -> ${finalUrl}`);
350
373
  }
351
374
  return import(/* @vite-ignore */ finalUrl)
352
375
  }
@@ -139,11 +139,11 @@ export class DocksStandardLayout extends DocksContainer {
139
139
  id="left-sidebar-split"
140
140
  orientation="vertical"
141
141
  sizes="50%, 50%">
142
- <docks-tabs id="${SIDEBAR_MAIN}" placement="start" icon-only with-toolbar></docks-tabs>
142
+ <docks-tabs id="${SIDEBAR_MAIN}" placement="start" icon-only with-toolbar item-size="large"></docks-tabs>
143
143
  <docks-tabs id="${SIDEBAR_MAIN_BOTTOM}" placement="start" icon-only></docks-tabs>
144
144
  </docks-resizable-grid>
145
145
  `
146
- : html`<docks-tabs id="${SIDEBAR_MAIN}" placement="start" icon-only with-toolbar></docks-tabs>`
146
+ : html`<docks-tabs id="${SIDEBAR_MAIN}" placement="start" icon-only with-toolbar item-size="large"></docks-tabs>`
147
147
  }
148
148
  `
149
149
  : nothing
package/src/parts/part.ts CHANGED
@@ -9,7 +9,8 @@ import { ifDefined } from "lit/directives/if-defined.js";
9
9
  import { DocksContextMenu } from "./contextmenu";
10
10
 
11
11
  export abstract class DocksPart extends DocksContainer {
12
- protected scrollMode: 'scroller' | 'native' = 'scroller';
12
+ /** `scroller`: wrap content in wa-scroller; `native`: overflow on part-content; `none`: no outer scroll (child manages scroll). */
13
+ protected scrollMode: 'scroller' | 'native' | 'none' = 'scroller';
13
14
 
14
15
  @property()
15
16
  private dirty: boolean = false