@vscode/webview-themes 0.0.0-stage → 0.0.2-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +303 -2
  3. package/ThirdPartyNotices.txt +28 -0
  4. package/catalog-sources.json +11 -0
  5. package/dist/extractor/cli.d.ts +2 -0
  6. package/dist/extractor/cli.js +49 -0
  7. package/dist/extractor/extract.d.ts +9 -0
  8. package/dist/extractor/extract.js +135 -0
  9. package/dist/extractor/probe.cjs +201 -0
  10. package/dist/extractor/probe.d.cts +1 -0
  11. package/dist/extractor/themeFiles.d.ts +3 -0
  12. package/dist/extractor/themeFiles.js +100 -0
  13. package/dist/index.d.ts +4 -0
  14. package/dist/index.js +2 -0
  15. package/dist/schema.d.ts +60 -0
  16. package/dist/schema.js +108 -0
  17. package/dist/theme.d.ts +10 -0
  18. package/dist/theme.js +96 -0
  19. package/package.json +55 -5
  20. package/themes/catalog.json +25749 -0
  21. package/themes/github-github-vscode-theme-dark-colorblind.css +999 -0
  22. package/themes/github-github-vscode-theme-dark-colorblind.json +942 -0
  23. package/themes/github-github-vscode-theme-dark-default.css +999 -0
  24. package/themes/github-github-vscode-theme-dark-default.json +942 -0
  25. package/themes/github-github-vscode-theme-dark-dimmed.css +999 -0
  26. package/themes/github-github-vscode-theme-dark-dimmed.json +942 -0
  27. package/themes/github-github-vscode-theme-dark-high-contrast.css +999 -0
  28. package/themes/github-github-vscode-theme-dark-high-contrast.json +896 -0
  29. package/themes/github-github-vscode-theme-dark.css +999 -0
  30. package/themes/github-github-vscode-theme-dark.json +944 -0
  31. package/themes/github-github-vscode-theme-light-colorblind.css +999 -0
  32. package/themes/github-github-vscode-theme-light-colorblind.json +942 -0
  33. package/themes/github-github-vscode-theme-light-default.css +999 -0
  34. package/themes/github-github-vscode-theme-light-default.json +942 -0
  35. package/themes/github-github-vscode-theme-light-high-contrast.css +999 -0
  36. package/themes/github-github-vscode-theme-light-high-contrast.json +909 -0
  37. package/themes/github-github-vscode-theme-light.css +999 -0
  38. package/themes/github-github-vscode-theme-light.json +944 -0
  39. package/themes/index.json +397 -0
  40. package/themes/vscode-theme-abyss-abyss-color-theme.css +999 -0
  41. package/themes/vscode-theme-abyss-abyss-color-theme.json +907 -0
  42. package/themes/vscode-theme-defaults-2026-dark.css +999 -0
  43. package/themes/vscode-theme-defaults-2026-dark.json +961 -0
  44. package/themes/vscode-theme-defaults-2026-light.css +999 -0
  45. package/themes/vscode-theme-defaults-2026-light.json +961 -0
  46. package/themes/vscode-theme-defaults-dark-modern.css +999 -0
  47. package/themes/vscode-theme-defaults-dark-modern.json +941 -0
  48. package/themes/vscode-theme-defaults-dark-plus.css +999 -0
  49. package/themes/vscode-theme-defaults-dark-plus.json +912 -0
  50. package/themes/vscode-theme-defaults-dark-vs.css +999 -0
  51. package/themes/vscode-theme-defaults-dark-vs.json +912 -0
  52. package/themes/vscode-theme-defaults-hc-black.css +999 -0
  53. package/themes/vscode-theme-defaults-hc-black.json +805 -0
  54. package/themes/vscode-theme-defaults-hc-light.css +999 -0
  55. package/themes/vscode-theme-defaults-hc-light.json +839 -0
  56. package/themes/vscode-theme-defaults-light-modern.css +999 -0
  57. package/themes/vscode-theme-defaults-light-modern.json +943 -0
  58. package/themes/vscode-theme-defaults-light-plus.css +999 -0
  59. package/themes/vscode-theme-defaults-light-plus.json +917 -0
  60. package/themes/vscode-theme-defaults-light-vs.css +999 -0
  61. package/themes/vscode-theme-defaults-light-vs.json +917 -0
  62. package/themes/vscode-theme-kimbie-dark-kimbie-dark-color-theme.css +999 -0
  63. package/themes/vscode-theme-kimbie-dark-kimbie-dark-color-theme.json +908 -0
  64. package/themes/vscode-theme-monokai-dimmed-dimmed-monokai-color-theme.css +999 -0
  65. package/themes/vscode-theme-monokai-dimmed-dimmed-monokai-color-theme.json +907 -0
  66. package/themes/vscode-theme-monokai-monokai-color-theme.css +999 -0
  67. package/themes/vscode-theme-monokai-monokai-color-theme.json +909 -0
  68. package/themes/vscode-theme-quietlight-quietlight-color-theme.css +999 -0
  69. package/themes/vscode-theme-quietlight-quietlight-color-theme.json +911 -0
  70. package/themes/vscode-theme-red-red-color-theme.css +999 -0
  71. package/themes/vscode-theme-red-red-color-theme.json +908 -0
  72. package/themes/vscode-theme-solarized-dark-solarized-dark-color-theme.css +999 -0
  73. package/themes/vscode-theme-solarized-dark-solarized-dark-color-theme.json +909 -0
  74. package/themes/vscode-theme-solarized-light-solarized-light-color-theme.css +999 -0
  75. package/themes/vscode-theme-solarized-light-solarized-light-color-theme.json +911 -0
  76. package/themes/vscode-theme-tomorrow-night-blue-tomorrow-night-blue-color-theme.css +999 -0
  77. package/themes/vscode-theme-tomorrow-night-blue-tomorrow-night-blue-color-theme.json +907 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Microsoft Corporation.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,304 @@
1
- # Temporary Holding Version
1
+ # @vscode/webview-themes
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Actual VS Code webview colors for Component Explorer fixtures. The browser
4
+ runtime has **no dependencies**, works with React 16 or newer, plain DOM,
5
+ Webpack and Vite, and does not require VS Code or network access.
6
+
7
+ The checked-in theme data includes all built-in VS Code color themes and all nine
8
+ variants of `GitHub.github-vscode-theme`, including both high-contrast variants.
9
+ These are **resolved snapshots**, not theme JSON files layered over a guessed
10
+ palette. Each snapshot includes the CSS variables VS Code actually injects,
11
+ including default/derived colors and font settings.
12
+
13
+ ## Data layout
14
+
15
+ The package ships a small JSON index plus one JSON and one CSS file per theme:
16
+
17
+ ```text
18
+ @vscode/webview-themes/index.json
19
+ @vscode/webview-themes/themes/github-github-vscode-theme-dark-default.json
20
+ @vscode/webview-themes/themes/github-github-vscode-theme-dark-default.css
21
+ ```
22
+
23
+ The index contains `schemaVersion`, build provenance in `source`, and a `themes`
24
+ array. Each entry has `id`, `label`, `settingsId`, `extensionId`, `themePath`,
25
+ `light`, `highContrast`, `className`, `json`, and `css`. Asset paths are relative
26
+ to the physical index file in `themes/`. There are no palettes in the index:
27
+ a picker can list themes without loading every theme's variables.
28
+
29
+ Each theme JSON has this shape:
30
+
31
+ ```ts
32
+ interface Theme {
33
+ readonly id: string;
34
+ readonly label: string;
35
+ readonly settingsId?: string;
36
+ readonly light: boolean;
37
+ readonly highContrast: boolean;
38
+ readonly cssVariableValues: Readonly<Record<string, string>>;
39
+ }
40
+ ```
41
+
42
+ Generated themes always have `settingsId`, the original VS Code theme selector.
43
+ `id` is an extension-qualified slug derived from the extension ID and theme asset
44
+ path, not the display label. For example, `GitHub.github-vscode-theme` and
45
+ `themes/dark-default.json` produce `github-github-vscode-theme-dark-default`.
46
+ Renaming/localizing a label does not rename the files. Slug collisions fail
47
+ generation explicitly instead of overwriting another theme.
48
+
49
+ | Theme kind | `light` | `highContrast` |
50
+ | --- | --- | --- |
51
+ | Dark | false | false |
52
+ | Light | true | false |
53
+ | High-contrast dark | false | true |
54
+ | High-contrast light | true | true |
55
+
56
+ Variable keys include the full CSS name, such as `--vscode-editor-background`.
57
+ `getThemeKind(theme)` derives the corresponding VS Code class name from the two
58
+ booleans; a redundant kind string is not stored in the per-theme files.
59
+
60
+ ## Fixtures with the JS controller
61
+
62
+ ```ts
63
+ import { applyTheme, parseTheme } from '@vscode/webview-themes';
64
+ import themeData from '@vscode/webview-themes/themes/github-github-vscode-theme-dark-default.json';
65
+
66
+ // Inside a fixture render(container, context):
67
+ const controller = context.addDisposable(applyTheme(container, parseTheme(themeData)));
68
+ ```
69
+
70
+ Import `@vscode/webview-themes/index.json` to populate a picker. The application
71
+ chooses how to load selected JSON files (static imports, bundler dynamic imports,
72
+ or fetching assets it serves); the runtime does not fetch or eagerly import data.
73
+ `controller.setTheme(nextTheme)` switches without remounting the component.
74
+ Disposal restores the previous styles, classes and attributes. Overlapping
75
+ controllers on the same target are rejected.
76
+
77
+ The wrapper sets `--vscode-*`, `--text-link-decoration`, VS Code's theme classes,
78
+ and `data-vscode-theme-id`, `data-vscode-theme-name`, and `data-vscode-theme-kind`.
79
+ `data-vscode-theme-id` uses `settingsId` when present, preserving real webview
80
+ selectors; it falls back to `id` for caller-created themes.
81
+ High-contrast light receives both `vscode-high-contrast-light` and
82
+ `vscode-high-contrast`, matching VS Code. Absent properties are masked, rather
83
+ than inheriting a stale value from the previous theme or Explorer.
84
+
85
+ **Layout and product CSS remain yours.** There is no padding, reset stylesheet,
86
+ link styling or syntax-token palette. A typical fixture surface uses:
87
+
88
+ ```css
89
+ .fixture-root {
90
+ background: var(--vscode-editor-background);
91
+ color: var(--vscode-editor-foreground);
92
+ font-family: var(--vscode-font-family);
93
+ font-size: var(--vscode-font-size);
94
+ font-weight: var(--vscode-font-weight);
95
+ }
96
+ ```
97
+
98
+ For an isolated iframe with product selectors such as `body.vscode-dark`, use:
99
+
100
+ ```ts
101
+ import { applyThemeToDocument } from '@vscode/webview-themes';
102
+
103
+ context.addDisposable(applyThemeToDocument(container.ownerDocument, theme));
104
+ ```
105
+
106
+ This applies variables to that document's root and metadata to its body, just
107
+ like VS Code. Only use it for a document owned by the fixture; it intentionally
108
+ themes the entire document. Use `applyTheme` for siblings or shadow-root content.
109
+ CSS selectors targeting `body` cannot be simulated by a subtree wrapper.
110
+
111
+ Component Explorer's `background: 'dark' | 'light'` controls its preview canvas,
112
+ not the component's theme. Choose that independently. Inputs can use Explorer's
113
+ `watchInput` to call `setTheme`; the runtime itself does not depend on Explorer
114
+ or Zod, create a picker, or persist a hidden localStorage preference.
115
+
116
+ ## CSS-only use
117
+
118
+ ```ts
119
+ import '@vscode/webview-themes/themes/github-github-vscode-theme-dark-default.css';
120
+ ```
121
+
122
+ ```html
123
+ <div class="vscode-theme-github-github-vscode-theme-dark-default vscode-dark">
124
+ <!-- Component content -->
125
+ </div>
126
+ ```
127
+
128
+ Use the exact `className` from the index rather than reconstructing it. Each CSS
129
+ file contains only one scoped variable rule: no layout, global selectors or
130
+ imports. CSS exports are marked as side effects so bundlers retain them.
131
+
132
+ For switching, remove the previous named theme class before adding the next one.
133
+ Also update the standard kind classes if product styles use them: `vscode-light`,
134
+ `vscode-dark`, `vscode-high-contrast`, or **both** `vscode-high-contrast-light` and
135
+ `vscode-high-contrast` for high-contrast light. CSS cannot set DOM classes or
136
+ metadata attributes; the JS controller handles those automatically and does not
137
+ need a theme CSS import.
138
+
139
+ Every generated CSS file resets catalog-known variables absent from its own
140
+ theme to `initial`, preventing inheritance from a different enclosing theme.
141
+ The JSON preserves those variables by omission. Unknown variables from extensions
142
+ not present during extraction are outside the CSS reset set.
143
+
144
+ ## Extract from any VS Code build
145
+
146
+ ```powershell
147
+ vscode-webview-themes --vscode 'C:\path\to\Code.exe' --out-dir .\themes
148
+ ```
149
+
150
+ The executable must be the desktop Electron binary, not the `code` shell
151
+ wrapper. On macOS use the executable inside the application bundle.
152
+ On Linux run under a display, for example:
153
+
154
+ ```sh
155
+ xvfb-run -a vscode-webview-themes --vscode /path/to/code --out-dir ./themes
156
+ ```
157
+
158
+ The CLI supports:
159
+
160
+ | Option | Meaning |
161
+ | --- | --- |
162
+ | `--vscode <executable>` | Required; caller chooses/downloads/builds VS Code |
163
+ | `--out-dir <directory>` | Generate index, per-theme JSON/CSS and compatibility catalog |
164
+ | `--out <file>` | Legacy aggregate output instead of `--out-dir` |
165
+ | `--extension <directory>` | Repeatable local extension inputs |
166
+ | `--theme <settings-id>` | Repeatable selection; defaults to all available themes |
167
+ | `--timeout <seconds>` | VS Code process deadline; defaults to 300, then up to 5 seconds to stop |
168
+
169
+ The extractor uses a new temporary user-data directory and extensions directory,
170
+ with automatic theme switching, updates and telemetry disabled. It does not
171
+ connect to your running VS Code, read your profile, or install extensions into
172
+ your normal extensions directory.
173
+
174
+ Additional extension inputs are copied into this temporary environment. Only
175
+ `contributes.themes` and `contributes.colors` survive in their manifests:
176
+ **their extension code and other contributions are not loaded**. Theme JSONC,
177
+ includes, defaults, and color references are resolved by VS Code itself.
178
+ Supply trusted local theme assets; this is not a sandbox for hostile files.
179
+
180
+ For a PR-extension fixture, pass the PR extension directory to register its
181
+ `pullRequests.*` colors as well as a GitHub Theme directory to add its themes.
182
+ Neither input directory is modified. Registered color contributions matter:
183
+ a theme's arbitrary color key is not exported by VS Code unless registered.
184
+
185
+ ## Automated catalog refresh
186
+
187
+ From this project directory:
188
+
189
+ ```sh
190
+ pnpm build
191
+ pnpm update-catalog
192
+ ```
193
+
194
+ The repository-only update script downloads the VS Code version pinned in
195
+ [catalog-sources.json](./catalog-sources.json), downloads/checksums the pinned
196
+ community VSIX packages, and invokes **the same extractor**.
197
+ Use `pnpm update-catalog --vscode <executable>` to supply a different build, or
198
+ `--out <directory>` to emit an artifact without replacing the committed data.
199
+ Adding a community theme requires pinning its version and SHA-256 and reviewing
200
+ its redistribution license; update [ThirdPartyNotices.txt](./ThirdPartyNotices.txt).
201
+ The extractor itself performs no downloads.
202
+
203
+ **All generated data is checked in under `themes/`.** The updater changes these
204
+ files directly; ordinary `pnpm build` only compiles TypeScript and never downloads
205
+ or refreshes data. JSON, CSS, index and compatibility output are generated from
206
+ one extraction. The index is written last. Removed themes' old JSON/CSS files are
207
+ removed using the previous index; unrelated files are preserved. Validation
208
+ failures occur before writing. Individual file replacements are atomic, but the
209
+ directory is not a transactional snapshot: don't serve it while an update runs.
210
+
211
+ Both automation hosts can use this:
212
+
213
+ - **Nightly engineering job:** obtain a VS Code build, run the updater or the
214
+ standalone CLI, and open a catalog-data PR if the output changed.
215
+ - **VS Code commit CI:** pass the job's packaged executable to the same CLI and
216
+ upload the generated directory as an artifact. No private workbench API or
217
+ source patch is needed. An update PR can consume that artifact later.
218
+
219
+ Scheduling, authentication, release selection, update PRs and npm publication
220
+ are deliberately outside the extractor. The package CI builds/tests and uploads
221
+ an npm tarball; it does not publish or configure a nightly job.
222
+
223
+ The output records its schema version, VS Code version and build commit,
224
+ platform, architecture, and contributing extension versions. Community archive
225
+ checksums are recorded in the shipped source configuration. Property and theme
226
+ ordering is deterministic; there are no timestamps, user paths or profile IDs.
227
+ The supplied packaged VS Code build must contain `product.json` with a commit.
228
+ Unstamped source-development launches are not accepted as reproducible builds.
229
+
230
+ Fonts are VS Code's defaults **for the recorded platform**. Refresh on the same
231
+ platform with the same build/extension inputs to obtain byte-identical results.
232
+ Switching the generating OS can legitimately change font variables.
233
+ The JSON snapshots preserve undefined colors by omission; the CSS uses `initial`
234
+ for these tokens, not invented transparent values. TextMate and semantic tokenization, file/product
235
+ icons, product stylesheets, and host messaging are not part of this package.
236
+
237
+ ## Compatibility with the initial prototype
238
+
239
+ `@vscode/webview-themes/catalog.json`, `parseCatalog`, and `getTheme` remain as
240
+ deprecated compatibility exports for the already-linked PR Explorer. That
241
+ aggregate uses VS Code settings IDs and the old `kind`/`variables` shape.
242
+ `parseTheme` and the controller accept it as input. Parsed themes expose
243
+ non-enumerable, derived `kind` and `variables` accessors for existing callers;
244
+ new code should use `light`, `highContrast`, and `cssVariableValues`.
245
+ The compatibility catalog is generated, never maintained separately.
246
+
247
+ ## Publishing
248
+
249
+ The existing Azure npm pipelines publish this package using their shared
250
+ `microsoft/vscode-engineering` release template and credentials:
251
+
252
+ - [Nightly](../.azure-pipelines/publish-nightly.yml): pushes to `main` publish
253
+ a prerelease under the `next` npm tag. `publishWebviewThemes` defaults to `true`.
254
+ - [Stable](../.azure-pipelines/publish-stable.yml): manually queue the pipeline
255
+ with `publishWebviewThemes: true` to publish under `latest`. Other package
256
+ publishing flags can remain disabled.
257
+
258
+ Both channels use the same [build steps](../.azure-pipelines/webview-themes/build-steps.yml)
259
+ to compile the package and run browser and checked-in asset tests before
260
+ packaging. Releases ship the checked-in snapshots; they do not run extraction
261
+ or update the catalog. Real VS Code extraction remains covered by GitHub Actions.
262
+
263
+ Use `npm install @vscode/webview-themes@next` after the first successful nightly
264
+ release. The unqualified package name uses `latest` after a stable release.
265
+ Merging the pipeline configuration alone does not confirm publication; verify
266
+ that the Azure publish stage succeeded and the version is available on npm.
267
+
268
+ ## Development and validation
269
+
270
+ ```sh
271
+ pnpm build
272
+ pnpm exec playwright install chromium
273
+ pnpm test
274
+ pnpm test:integration
275
+ pnpm pack
276
+ ```
277
+
278
+ Tests verify every checked-in file matches deterministic generation. Browser
279
+ tests verify real CSS inheritance, standalone CSS switching/nesting, the JS
280
+ controller, cleanup, compatibility and iframe placement.
281
+ Integration tests launch VS Code to check all four theme kinds,
282
+ JSONC/includes, contributed defaults, deterministic output, inactive extension
283
+ code, timeouts and failure preservation. They download the pinned build unless
284
+ `VSCODE_THEMES_EXECUTABLE` names an existing executable. Linux integration tests
285
+ need Xvfb. Both the integration tests and catalog updater create the download
286
+ cache and its parent directories automatically on a fresh checkout.
287
+ Linux CI configures the downloaded `chrome-sandbox` helper with root ownership
288
+ and mode `4755` on the ephemeral runner, keeping Electron sandboxing enabled.
289
+ Integration tests and catalog generation then share that configured executable.
290
+ There are no edits to consumer projects.
291
+
292
+ ### Extraction design
293
+
294
+ Parsing JSON alone misses derived defaults. Importing `ColorThemeData` can reuse
295
+ the resolver, but collecting all product and extension registrations outside
296
+ the workbench requires maintaining an internal integration. An internal VS Code
297
+ export command could be useful later but would first need a product change.
298
+
299
+ The probe instead launches VS Code in extension-test mode, selects each theme,
300
+ and captures an otherwise empty webview's injected root properties and body
301
+ metadata. It waits for the exact requested theme ID and validates the response;
302
+ there are no fixed sleeps or light/dark-only change notifications. Mutation
303
+ observers run after VS Code finishes updating the theme metadata and variables.
304
+ The browser receives data only; Electron is an extraction-time requirement.
@@ -0,0 +1,28 @@
1
+ The bundled catalog includes resolved color values from VS Code's built-in
2
+ themes (Microsoft Corporation, MIT; see LICENSE) and GitHub Theme 6.3.5:
3
+ https://github.com/primer/github-vscode-theme/tree/v6.3.5
4
+
5
+ GitHub Theme
6
+ -----------
7
+
8
+ MIT License
9
+
10
+ Copyright (c) 2020 Primer
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
@@ -0,0 +1,11 @@
1
+ {
2
+ "vscodeVersion": "1.140.0",
3
+ "extensions": [
4
+ {
5
+ "id": "GitHub.github-vscode-theme",
6
+ "version": "6.3.5",
7
+ "url": "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/GitHub/vsextensions/github-vscode-theme/6.3.5/vspackage",
8
+ "sha256": "74e69da1804f718ae9ce6a8ea49c06fbf9cfc137c9b65b0e14353715cb5d474a"
9
+ }
10
+ ]
11
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from 'node:util';
3
+ import { extract } from './extract.js';
4
+ try {
5
+ const { values } = parseArgs({
6
+ options: {
7
+ vscode: { type: 'string' },
8
+ out: { type: 'string' },
9
+ 'out-dir': { type: 'string' },
10
+ extension: { type: 'string', multiple: true, default: [] },
11
+ theme: { type: 'string', multiple: true, default: [] },
12
+ timeout: { type: 'string', default: '300' },
13
+ help: { type: 'boolean', short: 'h' },
14
+ },
15
+ });
16
+ if (values.help) {
17
+ console.log(`Usage: vscode-webview-themes --vscode <executable> --out-dir <themes-directory>
18
+ --out-dir <directory> Write index.json and per-theme JSON/CSS files
19
+ --out <catalog.json> Legacy aggregate output (instead of --out-dir)
20
+ --extension <directory> Additional theme/color extension (repeatable; code is not loaded)
21
+ --theme <settings-id> Only extract these themes (repeatable; default: all)
22
+ --timeout <seconds> VS Code process timeout (default: 300)
23
+
24
+ Uses a fresh isolated profile. No downloads, publication, or changes to your VS Code profile.
25
+ Pass the Electron executable, not the code/code-insiders shell wrapper. On Linux, use xvfb-run.`);
26
+ }
27
+ else {
28
+ const output = values['out-dir'] ?? values.out;
29
+ if (!values.vscode || !output || (values.out && values['out-dir'])) {
30
+ throw new Error('--vscode and exactly one of --out-dir or --out are required; see --help');
31
+ }
32
+ const timeout = Number(values.timeout);
33
+ if (!Number.isFinite(timeout) || timeout <= 0) {
34
+ throw new Error('--timeout must be a positive number of seconds');
35
+ }
36
+ await extract({
37
+ executable: values.vscode,
38
+ output,
39
+ format: values['out-dir'] ? 'directory' : 'catalog',
40
+ extensions: values.extension,
41
+ themes: values.theme,
42
+ timeoutMs: timeout * 1000,
43
+ });
44
+ }
45
+ }
46
+ catch (error) {
47
+ console.error(error);
48
+ process.exitCode = 1;
49
+ }
@@ -0,0 +1,9 @@
1
+ export interface ExtractOptions {
2
+ readonly executable: string;
3
+ readonly output: string;
4
+ readonly extensions: readonly string[];
5
+ readonly themes: readonly string[];
6
+ readonly timeoutMs: number;
7
+ readonly format?: 'catalog' | 'directory';
8
+ }
9
+ export declare function extract(options: ExtractOptions): Promise<void>;
@@ -0,0 +1,135 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { cp, mkdir, mkdtemp, readFile, rename, rm, writeFile } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { basename, dirname, join, resolve } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { object, parseCatalog, text } from '../schema.js';
7
+ import { writeThemeFiles } from './themeFiles.js';
8
+ export async function extract(options) {
9
+ const temporary = await mkdtemp(join(tmpdir(), 'vscode-webview-themes-'));
10
+ try {
11
+ const extensionsPath = join(temporary, 'extensions');
12
+ const userDataPath = join(temporary, 'user-data');
13
+ const probePath = join(temporary, 'probe');
14
+ await Promise.all([mkdir(extensionsPath), mkdir(join(userDataPath, 'User'), { recursive: true }), mkdir(probePath)]);
15
+ const ids = new Set();
16
+ for (const directory of options.extensions) {
17
+ const manifest = object(JSON.parse(await readFile(join(directory, 'package.json'), 'utf8')), 'extension manifest');
18
+ const publisher = text(manifest.publisher, 'publisher');
19
+ const name = text(manifest.name, 'name');
20
+ const id = `${publisher}.${name}`;
21
+ if (!/^[\w-]+\.[\w-]+$/.test(id) || ids.has(id.toLowerCase())) {
22
+ throw new Error(`Invalid or duplicate extension ID: ${id}`);
23
+ }
24
+ ids.add(id.toLowerCase());
25
+ const contributes = object(manifest.contributes, `${id}.contributes`);
26
+ if (!Array.isArray(contributes.themes) && !Array.isArray(contributes.colors)) {
27
+ throw new Error(`${id} has no theme or color contributions`);
28
+ }
29
+ const destination = join(extensionsPath, id);
30
+ await cp(resolve(directory), destination, {
31
+ recursive: true,
32
+ filter: source => !['.git', 'node_modules'].includes(basename(source)),
33
+ });
34
+ // Never run supplied extensions. Only VS Code's declarative theme/color loaders are needed.
35
+ await writeFile(join(destination, 'package.json'), JSON.stringify({
36
+ name, publisher,
37
+ version: text(manifest.version, `${id}.version`),
38
+ engines: manifest.engines,
39
+ contributes: { themes: contributes.themes, colors: contributes.colors },
40
+ }));
41
+ }
42
+ await writeFile(join(probePath, 'package.json'), JSON.stringify({
43
+ name: 'webview-theme-probe', publisher: 'vscode-theme-extractor', version: '0.0.1',
44
+ engines: { vscode: '^1.100.0' },
45
+ }));
46
+ await writeFile(join(userDataPath, 'User', 'settings.json'), JSON.stringify({
47
+ 'window.autoDetectColorScheme': false,
48
+ 'window.autoDetectHighContrast': false,
49
+ 'workbench.startupEditor': 'none',
50
+ 'workbench.enableExperiments': false,
51
+ 'telemetry.telemetryLevel': 'off',
52
+ 'update.mode': 'none',
53
+ 'extensions.autoUpdate': false,
54
+ 'extensions.autoCheckUpdates': false,
55
+ 'security.workspace.trust.enabled': false,
56
+ }));
57
+ const resultPath = join(temporary, 'catalog.json');
58
+ await launch(options.executable, [
59
+ '--user-data-dir', userDataPath,
60
+ '--extensions-dir', extensionsPath,
61
+ '--extensionDevelopmentPath', probePath,
62
+ '--extensionTestsPath', fileURLToPath(new URL('./probe.cjs', import.meta.url)),
63
+ '--skip-welcome', '--skip-release-notes', '--disable-updates', '--disable-telemetry',
64
+ '--disable-workspace-trust', '--new-window',
65
+ ], {
66
+ VSCODE_PORTABLE: temporary,
67
+ WEBVIEW_THEME_REQUEST: JSON.stringify({ themes: options.themes, extensions: [...ids], output: resultPath }),
68
+ }, options.timeoutMs);
69
+ const catalog = parseCatalog(JSON.parse(await readFile(resultPath, 'utf8')));
70
+ const output = resolve(options.output);
71
+ if (options.format === 'directory') {
72
+ await writeThemeFiles(output, catalog);
73
+ console.log(`Extracted ${catalog.themes.length} JSON/CSS themes from VS Code ${catalog.source.vscodeVersion} to ${output}`);
74
+ return;
75
+ }
76
+ await mkdir(dirname(output), { recursive: true });
77
+ const staging = `${output}.tmp-${process.pid}`;
78
+ try {
79
+ await writeFile(staging, `${JSON.stringify(catalog, null, 2)}\n`);
80
+ await rename(staging, output);
81
+ }
82
+ finally {
83
+ await rm(staging, { force: true });
84
+ }
85
+ console.log(`Extracted ${catalog.themes.length} themes from VS Code ${catalog.source.vscodeVersion} to ${output}`);
86
+ }
87
+ finally {
88
+ await rm(temporary, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
89
+ }
90
+ }
91
+ async function launch(executable, args, environment, timeoutMs) {
92
+ const env = { ...process.env, ...environment };
93
+ delete env.ELECTRON_RUN_AS_NODE;
94
+ delete env.VSCODE_IPC_HOOK_CLI;
95
+ delete env.VSCODE_APPDATA;
96
+ delete env.VSCODE_EXTENSIONS;
97
+ delete env.VSCODE_DEV;
98
+ await new Promise((resolve, reject) => {
99
+ const child = spawn(executable, args, { env, shell: false, stdio: ['ignore', 'pipe', 'pipe'] });
100
+ let failure;
101
+ let killTimer;
102
+ const terminate = () => {
103
+ child.kill();
104
+ killTimer ??= setTimeout(() => child.kill('SIGKILL'), 5000);
105
+ };
106
+ const timer = setTimeout(() => {
107
+ failure = new Error(`VS Code theme extraction timed out after ${timeoutMs} ms`);
108
+ terminate();
109
+ }, timeoutMs);
110
+ const cancel = () => {
111
+ failure = new Error('VS Code theme extraction interrupted');
112
+ terminate();
113
+ };
114
+ process.once('SIGINT', cancel);
115
+ process.once('SIGTERM', cancel);
116
+ child.stdout.on('data', data => process.stdout.write(data));
117
+ child.stderr.on('data', data => process.stderr.write(data));
118
+ const finish = (error) => {
119
+ clearTimeout(timer);
120
+ clearTimeout(killTimer);
121
+ process.removeListener('SIGINT', cancel);
122
+ process.removeListener('SIGTERM', cancel);
123
+ child.stdout.destroy();
124
+ child.stderr.destroy();
125
+ if (error) {
126
+ reject(error);
127
+ }
128
+ else {
129
+ resolve();
130
+ }
131
+ };
132
+ child.once('error', error => finish(error));
133
+ child.once('exit', (code, signal) => finish(failure ?? (code === 0 ? undefined : new Error(`VS Code exited with ${code ?? signal}`))));
134
+ });
135
+ }