Skip to content

Commit 433b06c

Browse files
nullvariantclaude
andauthored
refactor(git-id-switcher): design tokens round two — body tokens + pad→size rename (#463)
* refactor(git-id-switcher): add document-body design tokens and rename --gis-pad-* to --gis-size-* Add six new design tokens to :root (--gis-width-readable, --gis-line-height-doc, --gis-border-emphasis, --gis-pad-code, --gis-spinner-size, --gis-spinner-border) and rename --gis-pad-btn/body/body-lg to --gis-size-btn/body/body-lg to reflect their mixed padding/margin usage. Replace all remaining magic numbers in document/loading/error templates with token references. Extend tokenSpec value contracts and add SSOT literal check for --gis-border-emphasis. Signed-off-by: Null;Variant <null@nullvariant.com> 🖥️ IDE: [VS Code](https://code.visualstudio.com/) 🔌 Extension: [Claude Code](https://claude.ai/download) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Model-Raw: claude-opus-4-6 * chore(git-id-switcher): bump version to 0.19.0 and update CHANGELOG Signed-off-by: Null;Variant <null@nullvariant.com> 🖥️ IDE: [VS Code](https://code.visualstudio.com/) 🔌 Extension: [Claude Code](https://claude.ai/download) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Model-Raw: claude-opus-4-6 --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent f834261 commit 433b06c

8 files changed

Lines changed: 96 additions & 22 deletions

File tree

extensions/git-id-switcher/CHANGELOG.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,51 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.19.0] - 2026-04-10
11+
12+
### Security
13+
14+
- **Webview CSP hardening**: Tightened `img-src` from wildcard to explicit `assets.nullvariant.com` subdomain (#462)
15+
- **Link scheme allowlist**: Externalized `linkInterceptScript` and restricted navigable href schemes to `http:`, `https:`, and fragment-only (#460)
16+
- **SanitizedHtml branded type**: Introduced a compile-time brand on pre-sanitized HTML content so `buildDocumentHtml` cannot accept a raw `string` (#455)
17+
- **Webview fail-safe fallback**: Hardened CSP construction with `CspValidationError` and a fail-closed error page when CSP assembly fails (#453)
18+
- **Input length guard**: Added input length limit and field name validation to `toFieldError` (#444)
19+
20+
### Added
21+
22+
- **Defense-in-depth nonce/lang validation**: `buildHtmlShell` now validates nonce format and lang tag at the template boundary, throwing `CspValidationError` on invalid input (#457)
23+
- **npm namespace placeholder**: Published `@nullvariant/git-id-switcher` placeholder package to prevent name squatting (#422)
24+
- **DCO enforcement**: Added Developer Certificate of Origin check workflow for all PRs (#425)
25+
- **CI auto-approval**: Justice bot now auto-approves PRs that pass all CI checks (#430)
26+
- **Coverage thresholds**: Enforced c8 statement/branch/function/line thresholds via `.c8rc.json` (#434)
27+
- **Allstar policy**: Added OpenSSF Allstar security policy configuration (#413)
28+
- **Snyk integration**: Added Snyk policy, vulnerability badge, and documented in SECURITY.md (#416)
29+
- **FOSSA integration**: Added FOSSA license/security badges and documented in SECURITY.md (#424, #426)
30+
31+
### Fixed
32+
33+
- **Webview a11y**: Improved landmark structure, focus management, ARIA attributes, and forced-colors support in all webview templates (#446, #456)
34+
- **Markdown tooltip escaping**: Escape Markdown special characters in status bar tooltip user values (#406)
35+
36+
### Refactored
37+
38+
- **htmlTemplates directory split**: Decomposed monolithic `htmlTemplates.ts` into `shell.ts`, `document.ts`, `loading.ts`, `error.ts`, `baseStyles.ts`, `csp.ts`, `linkIntercept.ts`, and `types.ts` (#447, #458, #459)
39+
- **Design tokens round two**: Added six new tokens (`--gis-width-readable`, `--gis-line-height-doc`, `--gis-border-emphasis`, `--gis-pad-code`, `--gis-spinner-size`, `--gis-spinner-border`), renamed `--gis-pad-*``--gis-size-*`, and eliminated all remaining magic numbers from templates
40+
- **Design tokens round one**: Scoped webview body overrides by `body.gis-*` class and introduced initial `--gis-border-subtle` / `--gis-space-*` / `--gis-pad-*` token set (#448)
41+
- **Webview template extraction**: Extracted pure HTML template functions from webview provider for independent testability (#405)
42+
- **identityManager split**: Split `identityManager.ts` (1181 → 4 modules) into `identityAddForm.ts`, `identityEditFlow.ts`, `identityFormUtils.ts`, `identityFormValidation.ts` (#401)
43+
- **Validation types module**: Split Phase 4 Unified Validation Types into dedicated `validation-types.ts` (#403)
44+
- **Logging consolidation**: Replaced all `console.log/error/warn/debug` with `OutputChannel`-based `extensionLogger`, added disposed guard (#431, #438, #441)
45+
- **secureLogPath extraction**: Separated log path validation into dedicated `secureLogPath.ts` (#432)
46+
- **AddFormState derivation**: Derived `AddFormState` from `Identity` type and removed `GenericQuickPick` wrapper (#412)
47+
- **Narrowed ESLint exception**: Restricted htmlTemplates `var` allowance to `csp.ts` only (#461)
48+
49+
### Tests
50+
51+
- **Branch coverage expansion**: Added tests for `securityLogger` and `configChangeDetector` branch paths (#411)
52+
- **Cross-OS path sanitization**: Added `getSafeStack` tests for Windows/macOS/Linux path formats (#404)
53+
- **Assertion quality**: Improved assertion specificity in `errors.test.ts` and `identityManager.test.ts` (#408, #442)
54+
1055
## [0.18.0] - 2026-03-30
1156

1257
### Changed

extensions/git-id-switcher/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "git-id-switcher",
33
"displayName": "%extension.displayName%",
44
"description": "%extension.description%",
5-
"version": "0.18.0",
5+
"version": "0.19.0",
66
"publisher": "nullvariant",
77
"icon": "images/icon.png",
88
"engines": {

extensions/git-id-switcher/src/test/htmlTemplates.test.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -615,11 +615,17 @@ function testDesignTokenCoverage(): void {
615615
['--gis-space-md', /--gis-space-md:\s*1em\b/],
616616
['--gis-space-lg', /--gis-space-lg:\s*1\.5em\b/],
617617
['--gis-space-xl', /--gis-space-xl:\s*2em\b/],
618-
['--gis-pad-btn', /--gis-pad-btn:\s*4px 12px\b/],
619-
['--gis-pad-body', /--gis-pad-body:\s*20px\b/],
620-
['--gis-pad-body-lg', /--gis-pad-body-lg:\s*40px\b/],
618+
['--gis-size-btn', /--gis-size-btn:\s*4px 12px\b/],
619+
['--gis-size-body', /--gis-size-body:\s*20px\b/],
620+
['--gis-size-body-lg', /--gis-size-body-lg:\s*40px\b/],
621621
['--gis-font-sm', /--gis-font-sm:\s*0\.9em\b/],
622622
['--gis-font-xs', /--gis-font-xs:\s*0\.8em\b/],
623+
['--gis-width-readable', /--gis-width-readable:\s*800px\b/],
624+
['--gis-line-height-doc', /--gis-line-height-doc:\s*1\.6\b/],
625+
['--gis-border-emphasis', /--gis-border-emphasis:\s*4px solid var\(--vscode-textLink-foreground\)/],
626+
['--gis-pad-code', /--gis-pad-code:\s*0\.2em 0\.4em\b/],
627+
['--gis-spinner-size', /--gis-spinner-size:\s*40px\b/],
628+
['--gis-spinner-border', /--gis-spinner-border:\s*3px\b/],
623629
];
624630
for (const [name, re] of tokenSpec) {
625631
assert.match(styles, re, `${name} value contract violated`);
@@ -645,6 +651,18 @@ function testDesignTokenCoverage(): void {
645651
`${name}: literal "1px solid var(--vscode-panel-border)" must appear exactly once (in token definition), found ${matches.length}`
646652
);
647653
}
654+
// The literal `4px solid var(--vscode-textLink-foreground)` must appear
655+
// exactly once per template — inside the --gis-border-emphasis token
656+
// definition. Same SSOT enforcement as above.
657+
const emphasisPattern = /4px solid var\(--vscode-textLink-foreground\)/g;
658+
for (const [name, html] of allTemplates) {
659+
const matches = html.match(emphasisPattern) ?? [];
660+
assert.strictEqual(
661+
matches.length, 1,
662+
`${name}: literal "4px solid var(--vscode-textLink-foreground)" must appear exactly once (in token definition), found ${matches.length}`
663+
);
664+
}
665+
648666
// Only the document template actually consumes --gis-border-subtle
649667
// (loading/error have no panel-border rules). Asserted separately to
650668
// confirm the token is wired up, not merely defined.

extensions/git-id-switcher/src/ui/documentationInternal.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const DOCUMENT_HASHES: Record<string, string> = {
3333
'AGENTS.md': 'c4918e12fd7900bfc41e708992ebc4b7326600ce9327e8020a986fe4dd807f8d',
3434
'CODE_OF_CONDUCT.md': 'a0e9cb2e004663cdedef4e1adc0e429417ccfc479e367cbc17b869f62ae759d2',
3535
'CONTRIBUTING.md': 'ed4d1f391ffe04e3031dfc9f16fd8fd5dcd54ba23af3b3202c07adac5ba23da7',
36-
'extensions/git-id-switcher/CHANGELOG.md': 'b11d9b619f23b9e55c31302b9a55f455ade9c58f65ce485b0d6ae4ddeb289e7a',
36+
'extensions/git-id-switcher/CHANGELOG.md': 'e90ee9f359eb71e410a071afc10b6032ae3cdbbc7e8470a6e189886220d7969d',
3737
'extensions/git-id-switcher/docs/ARCHITECTURE.md': 'd5d879d988054d208739497962a0f937f2f21bdaab51776c4e8363cba99d634c',
3838
'extensions/git-id-switcher/docs/CONTRIBUTING.md': '7d6ad2bc4d8c838790754cb9df848cb65f9fdce7e1a13e5c965b83a3d5b6378c',
3939
'extensions/git-id-switcher/docs/DESIGN_PHILOSOPHY.md': 'f9718b61ac161cb466dbc76845688e7acacf4e5fdc4b8b9553269dba4a094f6b',

extensions/git-id-switcher/src/ui/htmlTemplates/baseStyles.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,26 @@ export function getBaseStyles(): string {
4444
--gis-space-md: 1em;
4545
--gis-space-lg: 1.5em;
4646
--gis-space-xl: 2em;
47-
/* px-based layout spacing (body padding, footer gap, button padding) that
48-
must not scale with inherited font-size. */
49-
--gis-pad-btn: 4px 12px;
50-
--gis-pad-body: 20px;
51-
--gis-pad-body-lg: 40px;
47+
/* px-based layout sizing (body padding, footer gap, button padding)
48+
that must not scale with inherited font-size. */
49+
--gis-size-btn: 4px 12px;
50+
--gis-size-body: 20px;
51+
--gis-size-body-lg: 40px;
5252
/* font-size tokens. Declared against the document root so nested elements
5353
do not multiply em values (0.9em inside a 0.9em ancestor shrinks). */
5454
--gis-font-sm: 0.9em;
5555
--gis-font-xs: 0.8em;
56+
/* Document body readability tokens. */
57+
--gis-width-readable: 800px;
58+
--gis-line-height-doc: 1.6;
59+
/* Emphasis border (blockquote left-edge, distinct from the subtle 1px
60+
panel border). */
61+
--gis-border-emphasis: 4px solid var(--vscode-textLink-foreground);
62+
/* Inline code padding scale. */
63+
--gis-pad-code: 0.2em 0.4em;
64+
/* Spinner dimensions. */
65+
--gis-spinner-size: 40px;
66+
--gis-spinner-border: 3px;
5667
}
5768
body {
5869
font-family: var(--vscode-font-family);

extensions/git-id-switcher/src/ui/htmlTemplates/document.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export function buildDocumentHtml(
6868
h1 { border-bottom: var(--gis-border-subtle); padding-bottom: var(--gis-space-xs); }
6969
code {
7070
background-color: var(--vscode-textCodeBlock-background);
71-
padding: 0.2em 0.4em;
71+
padding: var(--gis-pad-code);
7272
border-radius: var(--gis-radius-sm);
7373
font-family: var(--vscode-editor-font-family);
7474
}
@@ -111,7 +111,7 @@ export function buildDocumentHtml(
111111
background-color: var(--vscode-textCodeBlock-background);
112112
}
113113
blockquote {
114-
border-left: 4px solid var(--vscode-textLink-foreground);
114+
border-left: var(--gis-border-emphasis);
115115
margin: var(--gis-space-md) 0;
116116
padding: var(--gis-space-sm) var(--gis-space-md);
117117
background-color: var(--vscode-textCodeBlock-background);
@@ -131,7 +131,7 @@ export function buildDocumentHtml(
131131
background: var(--vscode-button-secondaryBackground);
132132
color: var(--vscode-button-secondaryForeground);
133133
border: none;
134-
padding: var(--gis-pad-btn);
134+
padding: var(--gis-size-btn);
135135
border-radius: var(--gis-radius-sm);
136136
cursor: pointer;
137137
font-family: var(--vscode-font-family);
@@ -169,8 +169,8 @@ ${getFocusVisibleRule('a:focus-visible, button:focus-visible')}
169169
${getFocusVisibleForcedColorsRule('a:focus-visible, button:focus-visible')}
170170
}
171171
.footer {
172-
margin-top: var(--gis-pad-body-lg);
173-
padding-top: var(--gis-pad-body);
172+
margin-top: var(--gis-size-body-lg);
173+
padding-top: var(--gis-size-body);
174174
border-top: var(--gis-border-subtle);
175175
font-size: var(--gis-font-sm);
176176
}
@@ -181,9 +181,9 @@ ${getFocusVisibleForcedColorsRule('a:focus-visible, button:focus-visible')}
181181
raise specificity above the base body rule, making cascade order
182182
irrelevant. */
183183
body.gis-doc {
184-
padding: var(--gis-pad-body);
185-
line-height: 1.6;
186-
max-width: 800px;
184+
padding: var(--gis-size-body);
185+
line-height: var(--gis-line-height-doc);
186+
max-width: var(--gis-width-readable);
187187
margin: 0 auto;
188188
}`;
189189

extensions/git-id-switcher/src/ui/htmlTemplates/error.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export function buildErrorHtml(
6767
const extraStyles = ` /* Template-specific body layout — scoped by body.gis-error class
6868
to raise specificity above the base body rule. */
6969
body.gis-error {
70-
padding: var(--gis-pad-body-lg);
70+
padding: var(--gis-size-body-lg);
7171
text-align: center;
7272
}
7373
h1 {

extensions/git-id-switcher/src/ui/htmlTemplates/loading.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ export function buildLoadingHtml(cspSource: string, nonce: string): string {
2828
height: 100vh;
2929
}
3030
.spinner {
31-
width: 40px;
32-
height: 40px;
33-
border: 3px solid var(--vscode-panel-border);
31+
width: var(--gis-spinner-size);
32+
height: var(--gis-spinner-size);
33+
border: var(--gis-spinner-border) solid var(--vscode-panel-border);
3434
border-top-color: var(--vscode-textLink-foreground);
3535
border-radius: 50%; /* circle, not a token */
3636
animation: spin 1s linear infinite;

0 commit comments

Comments
 (0)