From 59a822a878cbbc0ccc2a7c6f8c79ee51fb71f225 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 7 Feb 2026 22:23:41 +0000
Subject: [PATCH 1/8] Initial plan
From cf7923404ce3521cb30b76dbd482e23af9bf528f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 7 Feb 2026 22:30:40 +0000
Subject: [PATCH 2/8] Add #sym detection and fix diagnostics logging issues
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Add detection for #sym alias (in addition to #symbol) in text analysis
- Add symbol reference detection from contentReferences (kind='reference' with name field)
- Display symbol references in log viewer with 🔤 icon and proper formatting
- Separate symbols from files in context reference details
- Remove DEBUG console.log statements from diagnostics view
- Document logging differences between extension and webview in copilot-instructions.md
Co-authored-by: rajbos <6085745+rajbos@users.noreply.github.com>
---
.github/copilot-instructions.md | 8 +++++++
package-lock.json | 7 ++++++
src/extension.ts | 17 +++++++++++++-
src/webview/diagnostics/main.ts | 8 -------
src/webview/logviewer/main.ts | 39 ++++++++++++++++++++++++++++-----
5 files changed, 65 insertions(+), 14 deletions(-)
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
index b0e644e..f7fb6d2 100644
--- a/.github/copilot-instructions.md
+++ b/.github/copilot-instructions.md
@@ -40,6 +40,14 @@ The entire extension's logic is contained within the `CopilotTokenTracker` class
**CRITICAL**: Do NOT add debug logging statements like `log('[DEBUG] message')` for troubleshooting during development. This approach has been found to interfere with the output channel and can hide existing log messages from appearing.
+**Root Cause**: The issue occurs when webview code (diagnostics panel, etc.) uses `console.log` statements with DEBUG prefixes. These logs are written to the browser console (Developer Tools) of the webview, not to the extension's output channel. When clearing the cache or performing other operations, if there are DEBUG console.log statements in the webview code, they don't affect the output channel directly. However, the pattern of using DEBUG prefixes in webviews was removed to maintain consistency with the extension's logging guidelines and avoid confusion.
+
+**The Difference**:
+- **Extension logging** (`src/extension.ts`): Uses `this.outputChannel.appendLine()` to write to VS Code's Output Channel, which is visible in the Output panel.
+- **Webview logging** (`src/webview/*/main.ts`): Uses `console.log()` to write to the browser console, which is only visible when you open Developer Tools in the webview.
+
+When you clear the output channel using `outputChannel.clear()` or similar operations, it only affects the extension's output channel, not the webview's browser console. The two are separate logging systems.
+
- **Use Existing Logs**: The extension already has comprehensive logging throughout. Review existing log statements to understand what's being tracked.
- **Minimal Logging**: Only add logging if absolutely necessary for a new feature. Keep messages concise and informative.
- **Remove Debug Logs**: Any temporary debug logging added during development MUST be removed before committing code.
diff --git a/package-lock.json b/package-lock.json
index 149eaf5..26a156b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -563,6 +563,7 @@
}
],
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=18"
},
@@ -604,6 +605,7 @@
}
],
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -1894,6 +1896,7 @@
"integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.54.0",
"@typescript-eslint/types": "8.54.0",
@@ -2554,6 +2557,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -4036,6 +4040,7 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -8648,6 +8653,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=12"
},
@@ -8883,6 +8889,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
+ "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
diff --git a/src/extension.ts b/src/extension.ts
index da7c837..f5a2753 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1807,11 +1807,15 @@ class CopilotTokenTracker implements vscode.Disposable {
refs.selection += selectionMatches.length;
}
- // Count #symbol references
+ // Count #symbol and #sym references (both aliases)
const symbolMatches = text.match(/#symbol/gi);
+ const symMatches = text.match(/#sym\b/gi); // \b ensures we don't match #symbol
if (symbolMatches) {
refs.symbol += symbolMatches.length;
}
+ if (symMatches) {
+ refs.symbol += symMatches.length;
+ }
// Count #codebase references
const codebaseMatches = text.match(/#codebase/gi);
@@ -1894,6 +1898,17 @@ class CopilotTokenTracker implements vscode.Disposable {
const pathKey = fsPath.length > 100 ? '...' + fsPath.substring(fsPath.length - 97) : fsPath;
refs.byPath[pathKey] = (refs.byPath[pathKey] || 0) + 1;
}
+
+ // Handle symbol references (e.g., #sym:functionName)
+ // Symbol references have a 'name' field instead of fsPath
+ const symbolName = reference.name;
+ if (typeof symbolName === 'string' && kind === 'reference') {
+ // This is a symbol reference, track it
+ refs.symbol++;
+ // Track symbol by name for display (use 'name' as path)
+ const symbolKey = `#sym:${symbolName}`;
+ refs.byPath[symbolKey] = (refs.byPath[symbolKey] || 0) + 1;
+ }
}
}
}
diff --git a/src/webview/diagnostics/main.ts b/src/webview/diagnostics/main.ts
index 784294d..1226025 100644
--- a/src/webview/diagnostics/main.ts
+++ b/src/webview/diagnostics/main.ts
@@ -1109,8 +1109,6 @@ function renderLayout(data: DiagnosticsData): void {
btnTab.disabled = false;
}
- console.log('DEBUG Cache cleared confirmation received');
-
// Re-enable buttons after a short delay and reset to original state
setTimeout(() => {
if (btnReport) {
@@ -1146,7 +1144,6 @@ function renderLayout(data: DiagnosticsData): void {
if (ageValue) { ageValue.textContent = '0 seconds ago'; }
}
}
- console.log('DEBUG Cache refreshed with new data:', cacheInfo);
}
}
});
@@ -1301,7 +1298,6 @@ function setupStorageLinkHandlers(): void {
}
document.getElementById('btn-clear-cache')?.addEventListener('click', () => {
- console.log('DEBUG Clear cache button clicked (report tab)');
const btn = document.getElementById('btn-clear-cache') as HTMLButtonElement | null;
if (btn) {
btn.style.background = '#d97706';
@@ -1314,7 +1310,6 @@ function setupStorageLinkHandlers(): void {
});
document.getElementById('btn-clear-cache-tab')?.addEventListener('click', () => {
- console.log('DEBUG Clear cache button clicked (cache tab)');
const btn = document.getElementById('btn-clear-cache-tab') as HTMLButtonElement | null;
if (btn) {
btn.style.background = '#d97706';
@@ -1331,7 +1326,6 @@ function setupStorageLinkHandlers(): void {
const target = event.target as HTMLElement;
if (!target) { return; }
if (target.id === 'btn-clear-cache' || target.id === 'btn-clear-cache-tab') {
- console.log('DEBUG Clear cache button clicked via delegated handler', target.id);
target.style.background = '#d97706';
target.innerHTML = '⏳Clearing...';
if (target instanceof HTMLButtonElement) {
@@ -1350,12 +1344,10 @@ function setupStorageLinkHandlers(): void {
// Backend configuration buttons
document.getElementById('btn-configure-backend')?.addEventListener('click', () => {
- console.log('[DEBUG] Configure backend button clicked');
vscode.postMessage({ command: 'configureBackend' });
});
document.getElementById('btn-open-settings')?.addEventListener('click', () => {
- console.log('[DEBUG] Open settings button clicked');
vscode.postMessage({ command: 'openSettings' });
});
diff --git a/src/webview/logviewer/main.ts b/src/webview/logviewer/main.ts
index f082897..c9bffed 100644
--- a/src/webview/logviewer/main.ts
+++ b/src/webview/logviewer/main.ts
@@ -147,10 +147,33 @@ function renderContextReferencesDetailed(refs: ContextReferenceUsage): string {
// Show file paths if any
if (refs.byPath && Object.keys(refs.byPath).length > 0) {
- const pathList = Object.entries(refs.byPath)
- .map(([path, count]) => `${getFileName(path)}: ${count}`)
- .join(', ');
- sections.push(`
Files: ${pathList}
`);
+ // Separate symbols from files
+ const symbols: [string, number][] = [];
+ const files: [string, number][] = [];
+
+ Object.entries(refs.byPath).forEach(([path, count]) => {
+ if (path.startsWith('#sym:')) {
+ symbols.push([path.substring(5), count]); // Remove '#sym:' prefix
+ } else {
+ files.push([path, count]);
+ }
+ });
+
+ // Show files
+ if (files.length > 0) {
+ const pathList = files
+ .map(([path, count]) => `${getFileName(path)}: ${count}`)
+ .join(', ');
+ sections.push(`Files: ${pathList}
`);
+ }
+
+ // Show symbols
+ if (symbols.length > 0) {
+ const symbolList = symbols
+ .map(([symbolName, count]) => `${symbolName}: ${count}`)
+ .join(', ');
+ sections.push(`Symbols: ${symbolList}
`);
+ }
}
return sections.length > 0 ? sections.join('') : 'No additional details
';
@@ -214,7 +237,13 @@ function renderTurnCard(turn: ChatTurn): string {
});
otherPaths.forEach(([path]) => {
- contextFileBadges.push(`📄 ${escapeHtml(getFileName(path))}`);
+ // Check if this is a symbol reference
+ if (path.startsWith('#sym:')) {
+ const symbolName = path.substring(5); // Remove '#sym:' prefix
+ contextFileBadges.push(`🔤 ${escapeHtml(symbolName)}`);
+ } else {
+ contextFileBadges.push(`📄 ${escapeHtml(getFileName(path))}`);
+ }
});
}
From f7f7d22df7610ed7f830d329ec43a16b0837b2ec Mon Sep 17 00:00:00 2001
From: Rob Bos
Date: Sun, 8 Feb 2026 17:09:19 +0100
Subject: [PATCH 3/8] Refactor context reference handling: consolidate utility
functions and enhance context reference analysis in webviews
---
package-lock.json | 7 --
src/extension.ts | 157 ++++++++++++++------------
src/webview/diagnostics/main.ts | 111 ++++++++++++------
src/webview/logviewer/main.ts | 45 +-------
src/webview/shared/contextRefUtils.ts | 81 +++++++++++++
src/webview/usage/main.ts | 25 +---
6 files changed, 245 insertions(+), 181 deletions(-)
create mode 100644 src/webview/shared/contextRefUtils.ts
diff --git a/package-lock.json b/package-lock.json
index 26a156b..149eaf5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -563,7 +563,6 @@
}
],
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=18"
},
@@ -605,7 +604,6 @@
}
],
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=18"
}
@@ -1896,7 +1894,6 @@
"integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.54.0",
"@typescript-eslint/types": "8.54.0",
@@ -2557,7 +2554,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -4040,7 +4036,6 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -8653,7 +8648,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -8889,7 +8883,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
diff --git a/src/extension.ts b/src/extension.ts
index c80d1c6..19ad556 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1558,9 +1558,15 @@ class CopilotTokenTracker implements vscode.Disposable {
if (event.kind === 0 && event.v?.inputState?.mode?.kind) {
sessionMode = event.v.inputState.mode.kind;
- // Detect implicit selections in initial state
- if (event.v?.inputState?.selections && Array.isArray(event.v.inputState.selections) && event.v.inputState.selections.length > 0) {
- analysis.contextReferences.implicitSelection++;
+ // Detect implicit selections in initial state (only if there's an actual range)
+ if (event.v?.inputState?.selections && Array.isArray(event.v.inputState.selections)) {
+ for (const sel of event.v.inputState.selections) {
+ // Only count if it's an actual selection (not just a cursor position)
+ if (sel.startLineNumber !== sel.endLineNumber || sel.startColumn !== sel.endColumn) {
+ analysis.contextReferences.implicitSelection++;
+ break; // Count once per session
+ }
+ }
}
}
@@ -1570,8 +1576,24 @@ class CopilotTokenTracker implements vscode.Disposable {
}
// Detect implicit selections in updates to inputState.selections
- if (event.kind === 1 && event.k?.includes('selections') && Array.isArray(event.v) && event.v.length > 0) {
- analysis.contextReferences.implicitSelection++;
+ if (event.kind === 1 && event.k?.includes('selections') && Array.isArray(event.v)) {
+ for (const sel of event.v) {
+ // Only count if it's an actual selection (not just a cursor position)
+ if (sel && (sel.startLineNumber !== sel.endLineNumber || sel.startColumn !== sel.endColumn)) {
+ analysis.contextReferences.implicitSelection++;
+ break; // Count once per update
+ }
+ }
+ }
+
+ // Handle contentReferences updates (kind: 1 with contentReferences update)
+ if (event.kind === 1 && event.k?.includes('contentReferences') && Array.isArray(event.v)) {
+ this.analyzeContentReferences(event.v, analysis.contextReferences);
+ }
+
+ // Handle variableData updates (kind: 1 with variableData update)
+ if (event.kind === 1 && event.k?.includes('variableData') && event.v) {
+ this.analyzeVariableData(event.v, analysis.contextReferences);
}
// Handle VS Code incremental format - count requests as interactions
@@ -1594,10 +1616,8 @@ class CopilotTokenTracker implements vscode.Disposable {
analysis.toolCalls.byTool[toolName] = (analysis.toolCalls.byTool[toolName] || 0) + 1;
}
- // Analyze contentReferences if present
- if (request.contentReferences && Array.isArray(request.contentReferences)) {
- this.analyzeContentReferences(request.contentReferences, analysis.contextReferences);
- }
+ // Analyze all context references from this request
+ this.analyzeRequestContext(request, analysis.contextReferences);
// Extract tool calls from request.response array (when full request is added)
if (request.response && Array.isArray(request.response)) {
@@ -1704,52 +1724,8 @@ class CopilotTokenTracker implements vscode.Disposable {
analysis.modeUsage.ask++;
}
- // Analyze user message for context references
- if (request.message) {
- if (request.message.text) {
- this.analyzeContextReferences(request.message.text, analysis.contextReferences);
- }
- if (request.message.parts) {
- for (const part of request.message.parts) {
- if (part.text) {
- this.analyzeContextReferences(part.text, analysis.contextReferences);
- }
- }
- }
- }
-
- // Analyze variableData for @workspace, @terminal, @vscode references
- if (request.variableData) {
- // Process variables array for prompt files and other context
- this.analyzeVariableData(request.variableData, analysis.contextReferences);
-
- // Also check for @ references in variable names/values
- const varDataStr = JSON.stringify(request.variableData).toLowerCase();
- if (varDataStr.includes('workspace')) {
- analysis.contextReferences.workspace++;
- }
- if (varDataStr.includes('terminal')) {
- analysis.contextReferences.terminal++;
- }
- if (varDataStr.includes('vscode')) {
- analysis.contextReferences.vscode++;
- }
- }
-
- // Analyze contentReferences if present
- if (request.contentReferences && Array.isArray(request.contentReferences)) {
- this.analyzeContentReferences(request.contentReferences, analysis.contextReferences);
- }
-
- // Analyze variableData if present
- if (request.variableData) {
- this.analyzeVariableData(request.variableData, analysis.contextReferences);
- }
-
- // Analyze variableData if present
- if (request.variableData) {
- this.analyzeVariableData(request.variableData, analysis.contextReferences);
- }
+ // Analyze all context references from this request
+ this.analyzeRequestContext(request, analysis.contextReferences);
// Analyze response for tool calls and MCP tools
if (request.response && Array.isArray(request.response)) {
@@ -1857,6 +1833,36 @@ class CopilotTokenTracker implements vscode.Disposable {
}
}
+ /**
+ * Analyze a request object for all context references.
+ * This is the unified method that processes text, contentReferences, and variableData.
+ */
+ private analyzeRequestContext(request: any, refs: ContextReferenceUsage): void {
+ // Analyze user message text for context references
+ if (request.message) {
+ if (request.message.text) {
+ this.analyzeContextReferences(request.message.text, refs);
+ }
+ if (request.message.parts) {
+ for (const part of request.message.parts) {
+ if (part.text) {
+ this.analyzeContextReferences(part.text, refs);
+ }
+ }
+ }
+ }
+
+ // Analyze contentReferences if present
+ if (request.contentReferences && Array.isArray(request.contentReferences)) {
+ this.analyzeContentReferences(request.contentReferences, refs);
+ }
+
+ // Analyze variableData if present
+ if (request.variableData) {
+ this.analyzeVariableData(request.variableData, refs);
+ }
+ }
+
/**
* Analyze text for context references like #file, #selection, @workspace
*/
@@ -1874,8 +1880,9 @@ class CopilotTokenTracker implements vscode.Disposable {
}
// Count #symbol and #sym references (both aliases)
+ // Note: #sym:symbolName format is handled via variableData, not text matching
const symbolMatches = text.match(/#symbol/gi);
- const symMatches = text.match(/#sym\b/gi); // \b ensures we don't match #symbol
+ const symMatches = text.match(/#sym(?![:\w])/gi); // Negative lookahead: don't match #symbol or #sym:
if (symbolMatches) {
refs.symbol += symbolMatches.length;
}
@@ -1947,13 +1954,18 @@ class CopilotTokenTracker implements vscode.Disposable {
// Normalize path separators for pattern matching
const normalizedPath = fsPath.replace(/\\/g, '/').toLowerCase();
- // Track specific patterns
+ // Track specific patterns - these are auto-attached, not user-explicit #file refs
if (normalizedPath.endsWith('/.github/copilot-instructions.md') ||
normalizedPath.includes('.github/copilot-instructions.md')) {
refs.copilotInstructions++;
} else if (normalizedPath.endsWith('/agents.md') ||
normalizedPath.match(/\/agents\.md$/i)) {
refs.agentsMd++;
+ } else if (normalizedPath.endsWith('.instructions.md') ||
+ normalizedPath.includes('.instructions.md')) {
+ // Other instruction files (e.g., github-actions.instructions.md) are auto-attached
+ // Track as copilotInstructions since they're part of the instructions system
+ refs.copilotInstructions++;
} else {
// For other files, increment the general file counter
// This makes actual file attachments show up in context ref counts
@@ -1999,6 +2011,15 @@ class CopilotTokenTracker implements vscode.Disposable {
refs.byKind[kind] = (refs.byKind[kind] || 0) + 1;
}
+ // Handle symbol references (e.g., #sym:functionName)
+ // These appear as kind="generic" with name starting with "sym:"
+ if (kind === 'generic' && typeof variable.name === 'string' && variable.name.startsWith('sym:')) {
+ refs.symbol++;
+ // Track symbol by name for display
+ const symbolKey = `#${variable.name}`;
+ refs.byPath[symbolKey] = (refs.byPath[symbolKey] || 0) + 1;
+ }
+
// Process promptFile variables that contain file references
if (kind === 'promptFile' && variable.value) {
const value = variable.value;
@@ -2610,6 +2631,8 @@ class CopilotTokenTracker implements vscode.Disposable {
timestamps.push(new Date(ts).getTime());
}
+ // Analyze all context references from this request
+ this.analyzeRequestContext(request, details.contextReferences);
// Analyze context references
if (request.message?.text) {
this.analyzeContextReferences(request.message.text, details.contextReferences);
@@ -2740,17 +2763,9 @@ class CopilotTokenTracker implements vscode.Disposable {
const contextRefs = this.createEmptyContextRefs();
const userMessage = request.message?.text || '';
- this.analyzeContextReferences(userMessage, contextRefs);
-
- // Analyze contentReferences from request
- if (request.contentReferences && Array.isArray(request.contentReferences)) {
- this.analyzeContentReferences(request.contentReferences, contextRefs);
- }
- // Analyze variableData from request
- if (request.variableData) {
- this.analyzeVariableData(request.variableData, contextRefs);
- }
+ // Analyze all context references from this request
+ this.analyzeRequestContext(request, contextRefs);
// Get model from request or fall back to session model
const requestModel = request.modelId ||
@@ -2874,13 +2889,7 @@ class CopilotTokenTracker implements vscode.Disposable {
// Analyze context references
const contextRefs = this.createEmptyContextRefs();
- this.analyzeContextReferences(userMessage, contextRefs);
- if (request.variableData) {
- const varDataStr = JSON.stringify(request.variableData).toLowerCase();
- if (varDataStr.includes('workspace')) { contextRefs.workspace++; }
- if (varDataStr.includes('terminal')) { contextRefs.terminal++; }
- if (varDataStr.includes('vscode')) { contextRefs.vscode++; }
- }
+ this.analyzeRequestContext(request, contextRefs);
// Extract model
const model = this.getModelFromRequest(request);
diff --git a/src/webview/diagnostics/main.ts b/src/webview/diagnostics/main.ts
index 3b842b0..a60a478 100644
--- a/src/webview/diagnostics/main.ts
+++ b/src/webview/diagnostics/main.ts
@@ -9,20 +9,7 @@ const LOADING_MESSAGE = `⏳ Loading diagnostic data...
This may take a few moments depending on the number of session files.
The view will automatically update when data is ready.`;
-type ContextReferenceUsage = {
- file: number;
- selection: number;
- implicitSelection: number;
- symbol: number;
- codebase: number;
- workspace: number;
- terminal: number;
- vscode: number;
- byKind: { [kind: string]: number };
- copilotInstructions: number;
- agentsMd: number;
- byPath: { [path: string]: number };
-};
+import { ContextReferenceUsage, getTotalContextRefs, getContextRefsSummary } from '../shared/contextRefUtils';
type SessionFileDetails = {
file: string;
@@ -92,6 +79,7 @@ const initialData = window.__INITIAL_DIAGNOSTICS__;
let currentSortColumn: 'lastInteraction' = 'lastInteraction';
let currentSortDirection: 'asc' | 'desc' = 'desc';
let currentEditorFilter: string | null = null; // null = show all
+let currentContextRefFilter: keyof ContextReferenceUsage | null = null; // null = show all
function escapeHtml(text: string): string {
return text
@@ -150,26 +138,6 @@ function sanitizeNumber(value: number | undefined | null): string {
return value.toString();
}
-function getTotalContextRefs(refs: ContextReferenceUsage): number {
- return refs.file + refs.selection + refs.implicitSelection + refs.symbol + refs.codebase +
- refs.workspace + refs.terminal + refs.vscode + refs.copilotInstructions + refs.agentsMd;
-}
-
-function getContextRefsSummary(refs: ContextReferenceUsage): string {
- const parts: string[] = [];
- if (refs.file > 0) { parts.push(`#file: ${refs.file}`); }
- if (refs.selection > 0) { parts.push(`#sel: ${refs.selection}`); }
- if (refs.implicitSelection > 0) { parts.push(`impl: ${refs.implicitSelection}`); }
- if (refs.symbol > 0) { parts.push(`#sym: ${refs.symbol}`); }
- if (refs.codebase > 0) { parts.push(`#cb: ${refs.codebase}`); }
- if (refs.workspace > 0) { parts.push(`@ws: ${refs.workspace}`); }
- if (refs.terminal > 0) { parts.push(`@term: ${refs.terminal}`); }
- if (refs.vscode > 0) { parts.push(`@vsc: ${refs.vscode}`); }
- if (refs.copilotInstructions > 0) { parts.push(`📋 inst: ${refs.copilotInstructions}`); }
- if (refs.agentsMd > 0) { parts.push(`🤖 ag: ${refs.agentsMd}`); }
- return parts.length > 0 ? parts.join(', ') : 'None';
-}
-
function getFileName(filePath: string): string {
const parts = filePath.split(/[/\\]/);
return parts[parts.length - 1];
@@ -293,14 +261,39 @@ function renderSessionTable(detailedFiles: SessionFileDetails[], isLoading: bool
const editors = Object.keys(editorStats).sort();
// Apply editor filter
- const filteredFiles = currentEditorFilter
+ let filteredFiles = currentEditorFilter
? detailedFiles.filter(sf => sf.editorSource === currentEditorFilter)
: detailedFiles;
+ // Apply context ref filter
+ if (currentContextRefFilter) {
+ filteredFiles = filteredFiles.filter(sf => {
+ const refType = currentContextRefFilter!; // Assert non-null since we're inside the if block
+ const value = sf.contextReferences[refType];
+ return typeof value === 'number' && value > 0;
+ });
+ }
+
// Summary stats for filtered files
const totalInteractions = filteredFiles.reduce((sum, sf) => sum + Number(sf.interactions || 0), 0);
const totalContextRefs = filteredFiles.reduce((sum, sf) => sum + getTotalContextRefs(sf.contextReferences), 0);
+ // Aggregate context ref breakdown
+ const aggContextRefs = filteredFiles.reduce((agg, sf) => {
+ const r = sf.contextReferences;
+ agg.file += r.file;
+ agg.symbol += r.symbol;
+ agg.selection += r.selection;
+ agg.implicitSelection += r.implicitSelection;
+ agg.codebase += r.codebase;
+ agg.workspace += r.workspace;
+ agg.terminal += r.terminal;
+ agg.vscode += r.vscode;
+ agg.copilotInstructions += r.copilotInstructions;
+ agg.agentsMd += r.agentsMd;
+ return agg;
+ }, { file: 0, symbol: 0, selection: 0, implicitSelection: 0, codebase: 0, workspace: 0, terminal: 0, vscode: 0, copilotInstructions: 0, agentsMd: 0 });
+
// Sort filtered files
const sortedFiles = sortSessionFiles(filteredFiles);
@@ -337,6 +330,16 @@ function renderSessionTable(detailedFiles: SessionFileDetails[], isLoading: bool
🔗 Context References
${safeText(totalContextRefs)}
+
+ ${totalContextRefs === 0 ? 'None' : ''}
+ ${aggContextRefs.file > 0 ? `
#file ${aggContextRefs.file}
` : ''}
+ ${aggContextRefs.symbol > 0 ? `
#sym ${aggContextRefs.symbol}
` : ''}
+ ${aggContextRefs.implicitSelection > 0 ? `
implicit ${aggContextRefs.implicitSelection}
` : ''}
+ ${aggContextRefs.copilotInstructions > 0 ? `
📋 instructions ${aggContextRefs.copilotInstructions}
` : ''}
+ ${aggContextRefs.agentsMd > 0 ? `
🤖 agents ${aggContextRefs.agentsMd}
` : ''}
+ ${aggContextRefs.workspace > 0 ? `
@workspace ${aggContextRefs.workspace}
` : ''}
+ ${aggContextRefs.vscode > 0 ? `
@vscode ${aggContextRefs.vscode}
` : ''}
+
📅 Time Range
@@ -742,6 +745,23 @@ function renderLayout(data: DiagnosticsData): void {
}
.summary-label { font-size: 11px; color: #b3b3b3; margin-bottom: 4px; }
.summary-value { font-size: 18px; font-weight: 600; color: #fff; }
+ .summary-sub { font-size: 10px; color: #94a3b8; text-align: left; margin-top: 6px; }
+ .context-ref-filter {
+ cursor: pointer;
+ padding: 2px 6px;
+ border-radius: 3px;
+ margin: 2px 0;
+ transition: all 0.2s;
+ }
+ .context-ref-filter:hover {
+ background: rgba(79, 195, 247, 0.2);
+ color: #4FC3F7;
+ }
+ .context-ref-filter.active {
+ background: rgba(79, 195, 247, 0.3);
+ color: #4FC3F7;
+ font-weight: 600;
+ }
/* Table styles */
.table-container {
@@ -1269,6 +1289,25 @@ function setupStorageLinkHandlers(): void {
});
});
}
+
+ // Wire up context ref filter handlers
+ function setupContextRefFilterHandlers(): void {
+ document.querySelectorAll('.context-ref-filter').forEach(filter => {
+ filter.addEventListener('click', () => {
+ const refType = (filter as HTMLElement).getAttribute('data-ref-type') as keyof ContextReferenceUsage | null;
+
+ // Toggle: if clicking the same filter, clear it
+ if (currentContextRefFilter === refType) {
+ currentContextRefFilter = null;
+ } else {
+ currentContextRefFilter = refType;
+ }
+
+ // Re-render table
+ reRenderTable();
+ });
+ });
+ }
// Re-render the session table with current filter/sort state
function reRenderTable(): void {
@@ -1278,6 +1317,7 @@ function setupStorageLinkHandlers(): void {
if (!isLoading) {
setupSortHandlers();
setupEditorFilterHandlers();
+ setupContextRefFilterHandlers();
setupFileLinks();
}
}
@@ -1398,6 +1438,7 @@ function setupStorageLinkHandlers(): void {
setupSortHandlers();
setupEditorFilterHandlers();
+ setupContextRefFilterHandlers();
setupFileLinks();
setupStorageLinkHandlers();
diff --git a/src/webview/logviewer/main.ts b/src/webview/logviewer/main.ts
index c9bffed..044b680 100644
--- a/src/webview/logviewer/main.ts
+++ b/src/webview/logviewer/main.ts
@@ -1,18 +1,5 @@
// Log Viewer webview - displays session file details and chat turns
-type ContextReferenceUsage = {
- file: number;
- selection: number;
- implicitSelection: number;
- symbol: number;
- codebase: number;
- workspace: number;
- terminal: number;
- vscode: number;
- byKind: { [kind: string]: number };
- copilotInstructions: number;
- agentsMd: number;
- byPath: { [path: string]: number };
-};
+import { ContextReferenceUsage, getTotalContextRefs, getImplicitContextRefs, getExplicitContextRefs, getContextRefsSummary } from '../shared/contextRefUtils';
type ChatTurn = {
turnNumber: number;
@@ -101,26 +88,6 @@ function formatFileSize(bytes: number): string {
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
}
-function getTotalContextRefs(refs: ContextReferenceUsage): number {
- return refs.file + refs.selection + refs.implicitSelection + refs.symbol + refs.codebase +
- refs.workspace + refs.terminal + refs.vscode + refs.copilotInstructions + refs.agentsMd;
-}
-
-function getContextRefsSummary(refs: ContextReferenceUsage): string {
- const parts: string[] = [];
- if (refs.file > 0) { parts.push(`#file: ${refs.file}`); }
- if (refs.selection > 0) { parts.push(`#selection: ${refs.selection}`); }
- if (refs.implicitSelection > 0) { parts.push(`implicit: ${refs.implicitSelection}`); }
- if (refs.symbol > 0) { parts.push(`#symbol: ${refs.symbol}`); }
- if (refs.codebase > 0) { parts.push(`#codebase: ${refs.codebase}`); }
- if (refs.workspace > 0) { parts.push(`@workspace: ${refs.workspace}`); }
- if (refs.terminal > 0) { parts.push(`@terminal: ${refs.terminal}`); }
- if (refs.vscode > 0) { parts.push(`@vscode: ${refs.vscode}`); }
- if (refs.copilotInstructions > 0) { parts.push(`📋 instructions: ${refs.copilotInstructions}`); }
- if (refs.agentsMd > 0) { parts.push(`🤖 agents: ${refs.agentsMd}`); }
- return parts.length > 0 ? parts.join(', ') : 'None';
-}
-
function getContextRefBadges(refs: ContextReferenceUsage): string {
const badges: string[] = [];
if (refs.selection > 0) { badges.push(`
#selection: ${refs.selection}`); }
@@ -374,6 +341,8 @@ function renderLayout(data: SessionLogData): void {
const usageTopMcpTools = usage ? getTopEntries(usage.mcpTools.byTool, 3) : [];
const usageContextRefs = usage?.contextReferences || data.contextReferences;
const usageContextTotal = getTotalContextRefs(usageContextRefs);
+ const usageContextImplicit = getImplicitContextRefs(usageContextRefs);
+ const usageContextExplicit = getExplicitContextRefs(usageContextRefs);
const formatTopList = (entries: { key: string; value: number }[], mapper?: (k: string) => string) => {
if (!entries.length) { return 'None'; }
@@ -1065,13 +1034,7 @@ function renderLayout(data: SessionLogData): void {
🔗 Context Refs
${usageContextTotal}
- ${usageContextTotal === 0 ? 'None' : ''}
- ${usageContextRefs.file > 0 ? `
#file ${usageContextRefs.file}
` : ''}
- ${usageContextRefs.implicitSelection > 0 ? `
implicit ${usageContextRefs.implicitSelection}
` : ''}
- ${usageContextRefs.copilotInstructions > 0 ? `
📋 instructions ${usageContextRefs.copilotInstructions}
` : ''}
- ${usageContextRefs.agentsMd > 0 ? `
🤖 agents ${usageContextRefs.agentsMd}
` : ''}
- ${usageContextRefs.workspace > 0 ? `
@workspace ${usageContextRefs.workspace}
` : ''}
- ${usageContextRefs.vscode > 0 ? `
@vscode ${usageContextRefs.vscode}
` : ''}
+ ${usageContextTotal === 0 ? 'None' : `implicit ${usageContextImplicit}, explicit ${usageContextExplicit}`}
diff --git a/src/webview/shared/contextRefUtils.ts b/src/webview/shared/contextRefUtils.ts
new file mode 100644
index 0000000..89d18bb
--- /dev/null
+++ b/src/webview/shared/contextRefUtils.ts
@@ -0,0 +1,81 @@
+/**
+ * Shared utilities for working with context references across webviews
+ */
+
+export type ContextReferenceUsage = {
+ file: number;
+ selection: number;
+ implicitSelection: number;
+ symbol: number;
+ codebase: number;
+ workspace: number;
+ terminal: number;
+ vscode: number;
+ byKind: { [kind: string]: number };
+ copilotInstructions: number;
+ agentsMd: number;
+ byPath: { [path: string]: number };
+};
+
+/**
+ * Calculate the total number of context references.
+ * This is the single source of truth for what constitutes a context reference.
+ */
+export function getTotalContextRefs(refs: ContextReferenceUsage): number {
+ return refs.file + refs.selection + refs.implicitSelection + refs.symbol + refs.codebase +
+ refs.workspace + refs.terminal + refs.vscode + refs.copilotInstructions + refs.agentsMd;
+}
+
+/**
+ * Calculate the count of implicit (auto-attached) context references.
+ * Implicit refs are not user-initiated: copilotInstructions, agentsMd, implicitSelection
+ */
+export function getImplicitContextRefs(refs: ContextReferenceUsage): number {
+ return refs.copilotInstructions + refs.agentsMd + refs.implicitSelection;
+}
+
+/**
+ * Calculate the count of explicit (user-initiated) context references.
+ * Explicit refs are user-initiated: #file, #selection, #symbol, #codebase, @workspace, @terminal, @vscode
+ */
+export function getExplicitContextRefs(refs: ContextReferenceUsage): number {
+ return refs.file + refs.selection + refs.symbol + refs.codebase +
+ refs.workspace + refs.terminal + refs.vscode;
+}
+
+/**
+ * Generate a summary string of context references.
+ * @param refs - The context reference usage counts
+ * @param abbreviated - If true, use short labels (e.g., '#sel' instead of '#selection')
+ */
+export function getContextRefsSummary(refs: ContextReferenceUsage, abbreviated = false): string {
+ const parts: string[] = [];
+
+ if (abbreviated) {
+ // Abbreviated labels for compact display (used in diagnostics)
+ if (refs.file > 0) { parts.push(`#file: ${refs.file}`); }
+ if (refs.selection > 0) { parts.push(`#sel: ${refs.selection}`); }
+ if (refs.implicitSelection > 0) { parts.push(`impl: ${refs.implicitSelection}`); }
+ if (refs.symbol > 0) { parts.push(`#sym: ${refs.symbol}`); }
+ if (refs.codebase > 0) { parts.push(`#cb: ${refs.codebase}`); }
+ if (refs.workspace > 0) { parts.push(`@ws: ${refs.workspace}`); }
+ if (refs.terminal > 0) { parts.push(`@term: ${refs.terminal}`); }
+ if (refs.vscode > 0) { parts.push(`@vsc: ${refs.vscode}`); }
+ if (refs.copilotInstructions > 0) { parts.push(`📋 inst: ${refs.copilotInstructions}`); }
+ if (refs.agentsMd > 0) { parts.push(`🤖 ag: ${refs.agentsMd}`); }
+ } else {
+ // Full labels for detailed display (used in logviewer)
+ if (refs.file > 0) { parts.push(`#file: ${refs.file}`); }
+ if (refs.selection > 0) { parts.push(`#selection: ${refs.selection}`); }
+ if (refs.implicitSelection > 0) { parts.push(`implicit: ${refs.implicitSelection}`); }
+ if (refs.symbol > 0) { parts.push(`#symbol: ${refs.symbol}`); }
+ if (refs.codebase > 0) { parts.push(`#codebase: ${refs.codebase}`); }
+ if (refs.workspace > 0) { parts.push(`@workspace: ${refs.workspace}`); }
+ if (refs.terminal > 0) { parts.push(`@terminal: ${refs.terminal}`); }
+ if (refs.vscode > 0) { parts.push(`@vscode: ${refs.vscode}`); }
+ if (refs.copilotInstructions > 0) { parts.push(`📋 instructions: ${refs.copilotInstructions}`); }
+ if (refs.agentsMd > 0) { parts.push(`🤖 agents: ${refs.agentsMd}`); }
+ }
+
+ return parts.length > 0 ? parts.join(', ') : 'None';
+}
diff --git a/src/webview/usage/main.ts b/src/webview/usage/main.ts
index e1c2d8d..e7ddfdc 100644
--- a/src/webview/usage/main.ts
+++ b/src/webview/usage/main.ts
@@ -1,22 +1,9 @@
// Usage Analysis webview
import { el } from '../shared/domUtils';
import { buttonHtml } from '../shared/buttonConfig';
+import { ContextReferenceUsage, getTotalContextRefs } from '../shared/contextRefUtils';
type ModeUsage = { ask: number; edit: number; agent: number };
-type ContextReferenceUsage = {
- file: number;
- selection: number;
- implicitSelection: number;
- symbol: number;
- codebase: number;
- workspace: number;
- terminal: number;
- vscode: number;
- byKind: { [kind: string]: number };
- copilotInstructions: number;
- agentsMd: number;
- byPath: { [path: string]: number };
-};
type ToolCallUsage = { total: number; byTool: { [key: string]: number } };
type McpToolUsage = { total: number; byServer: { [key: string]: number }; byTool: { [key: string]: number } };
@@ -71,16 +58,6 @@ function escapeHtml(text: string): string {
.replace(/'/g, ''');
}
-function getTotalContextRefs(refs: ContextReferenceUsage): number {
- const basicRefs = refs.file + refs.selection + refs.implicitSelection + refs.symbol + refs.codebase +
- refs.workspace + refs.terminal + refs.vscode;
-
- // Add contentReferences counts
- const contentRefs = refs.copilotInstructions + refs.agentsMd;
-
- return basicRefs + contentRefs;
-}
-
import toolNames from '../../toolNames.json';
let TOOL_NAME_MAP: { [key: string]: string } | null = toolNames || null;
From bd8169761e6776876e96613f5351919f1be511b0 Mon Sep 17 00:00:00 2001
From: Rob Bos
Date: Sun, 8 Feb 2026 17:17:08 +0100
Subject: [PATCH 4/8] Update context ref details
---
src/webview/logviewer/main.ts | 144 +++++++++++++++++++++++++++-------
1 file changed, 115 insertions(+), 29 deletions(-)
diff --git a/src/webview/logviewer/main.ts b/src/webview/logviewer/main.ts
index 044b680..f4c5551 100644
--- a/src/webview/logviewer/main.ts
+++ b/src/webview/logviewer/main.ts
@@ -102,48 +102,95 @@ function getContextRefBadges(refs: ContextReferenceUsage): string {
}
function renderContextReferencesDetailed(refs: ContextReferenceUsage): string {
- const sections: string[] = [];
+ const rows: { category: string; name: string; count: number; type: 'implicit' | 'explicit' }[] = [];
- // Show instruction file references
- if (refs.copilotInstructions > 0 || refs.agentsMd > 0) {
- const instrRefs: string[] = [];
- if (refs.copilotInstructions > 0) { instrRefs.push(`📋 copilot-instructions: ${refs.copilotInstructions}`); }
- if (refs.agentsMd > 0) { instrRefs.push(`🤖 agents.md: ${refs.agentsMd}`); }
- sections.push(`Instructions: ${instrRefs.join(', ')}
`);
+ // Implicit selections (implicit)
+ if (refs.implicitSelection > 0) {
+ rows.push({ category: '📝 Selection', name: 'editor selection', count: refs.implicitSelection, type: 'implicit' });
}
- // Show file paths if any
+ // File paths and symbols from byPath
if (refs.byPath && Object.keys(refs.byPath).length > 0) {
- // Separate symbols from files
- const symbols: [string, number][] = [];
- const files: [string, number][] = [];
-
Object.entries(refs.byPath).forEach(([path, count]) => {
if (path.startsWith('#sym:')) {
- symbols.push([path.substring(5), count]); // Remove '#sym:' prefix
+ // Symbols are explicit user references
+ rows.push({ category: '🔣 Symbol', name: path.substring(5), count, type: 'explicit' });
} else {
- files.push([path, count]);
+ // Check if this is an instruction file (implicit) or regular file (explicit)
+ const normalizedPath = path.replace(/\\/g, '/').toLowerCase();
+ const isInstructionFile = normalizedPath.includes('copilot-instructions.md') ||
+ normalizedPath.endsWith('.instructions.md') ||
+ normalizedPath.endsWith('/agents.md');
+ if (isInstructionFile) {
+ rows.push({ category: '📋 Instructions', name: getFileName(path), count, type: 'implicit' });
+ } else {
+ // Regular file references are explicit
+ rows.push({ category: '📁 File', name: getFileName(path), count, type: 'explicit' });
+ }
}
});
-
- // Show files
- if (files.length > 0) {
- const pathList = files
- .map(([path, count]) => `${getFileName(path)}: ${count}`)
- .join(', ');
- sections.push(`Files: ${pathList}
`);
+ }
+
+ // Instruction counters that aren't in byPath (fallback)
+ // Only show if we haven't already added instruction files from byPath
+ const hasInstructionFiles = rows.some(r => r.category === '📋 Instructions');
+ if (!hasInstructionFiles) {
+ if (refs.copilotInstructions > 0) {
+ rows.push({ category: '📋 Instructions', name: 'copilot-instructions', count: refs.copilotInstructions, type: 'implicit' });
}
-
- // Show symbols
- if (symbols.length > 0) {
- const symbolList = symbols
- .map(([symbolName, count]) => `${symbolName}: ${count}`)
- .join(', ');
- sections.push(`Symbols: ${symbolList}
`);
+ if (refs.agentsMd > 0) {
+ rows.push({ category: '🤖 Agents', name: 'agents.md', count: refs.agentsMd, type: 'implicit' });
}
}
- return sections.length > 0 ? sections.join('') : 'No additional details
';
+ // Explicit @ references
+ if (refs.workspace > 0) {
+ rows.push({ category: '🌐 Workspace', name: '@workspace', count: refs.workspace, type: 'explicit' });
+ }
+ if (refs.terminal > 0) {
+ rows.push({ category: '💻 Terminal', name: '@terminal', count: refs.terminal, type: 'explicit' });
+ }
+ if (refs.vscode > 0) {
+ rows.push({ category: '⚙️ VS Code', name: '@vscode', count: refs.vscode, type: 'explicit' });
+ }
+ if (refs.codebase > 0) {
+ rows.push({ category: '📚 Codebase', name: '#codebase', count: refs.codebase, type: 'explicit' });
+ }
+ if (refs.selection > 0) {
+ rows.push({ category: '✂️ Selection', name: '#selection', count: refs.selection, type: 'explicit' });
+ }
+
+ if (rows.length === 0) {
+ return 'No context references
';
+ }
+
+ // Build table
+ const tableRows = rows.map(row => {
+ const typeClass = row.type === 'implicit' ? 'context-type-implicit' : 'context-type-explicit';
+ const typeLabel = row.type === 'implicit' ? '🔒 implicit' : '👤 explicit';
+ return `
+ | ${row.category} |
+ ${escapeHtml(row.name)} |
+ ${row.count} |
+ ${typeLabel} |
+
`;
+ }).join('');
+
+ return `
+
+
+
+ | Category |
+ Reference |
+ Count |
+ Type |
+
+
+
+ ${tableRows}
+
+
+ `;
}
function getTopEntries(map: { [key: string]: number } = {}, limit = 3): { key: string; value: number }[] {
@@ -869,6 +916,45 @@ function renderLayout(data: SessionLogData): void {
color: #94a3b8;
font-weight: 600;
}
+ .context-refs-table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 13px;
+ margin-top: 8px;
+ }
+ .context-refs-table th,
+ .context-refs-table td {
+ padding: 8px 12px;
+ text-align: left;
+ border-bottom: 1px solid #334155;
+ }
+ .context-refs-table th {
+ color: #94a3b8;
+ font-weight: 600;
+ font-size: 11px;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ background: #1e293b;
+ }
+ .context-refs-table td {
+ color: #cbd5e1;
+ }
+ .context-refs-table tbody tr:hover {
+ background: #1e293b;
+ }
+ .context-refs-table .count-cell {
+ text-align: center;
+ font-weight: 600;
+ color: #60a5fa;
+ }
+ .context-type-implicit {
+ color: #94a3b8;
+ font-size: 12px;
+ }
+ .context-type-explicit {
+ color: #4ade80;
+ font-size: 12px;
+ }
.context-path {
padding-left: 10px;
color: #9ca3af;
From 6accef89d46e12ea689022847e25e2e7db917949 Mon Sep 17 00:00:00 2001
From: Rob Bos
Date: Sun, 8 Feb 2026 17:26:14 +0100
Subject: [PATCH 5/8] CSS fixes
---
src/webview/logviewer/main.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/webview/logviewer/main.ts b/src/webview/logviewer/main.ts
index f4c5551..0e305ba 100644
--- a/src/webview/logviewer/main.ts
+++ b/src/webview/logviewer/main.ts
@@ -989,11 +989,10 @@ function renderLayout(data: SessionLogData): void {
::-webkit-scrollbar-thumb:hover {
background: #4a4a54;
}
- }
/* Context References */
.turn-context-refs {
- margin-bottom: 14px;
+ margin: 0 16px 14px 16px;
background: linear-gradient(135deg, #2a2535 0%, #252530 100%);
border: 1px solid #4a4a5a;
border-radius: 8px;
From d17e93d0e5d320603f3a3c367ebe3343ca5d2e04 Mon Sep 17 00:00:00 2001
From: Rob Bos
Date: Sun, 8 Feb 2026 18:17:22 +0100
Subject: [PATCH 6/8] Add stylelint configuration and new CSS styles for
webview components
- Created a .stylelintrc.json file to configure stylelint with custom rules.
- Added TypeScript declaration for CSS modules in src/types/css.d.ts.
- Implemented new styles for various webview components including charts, details, diagnostics, log viewer, and usage.
- Ensured consistent styling across components with a dark theme and responsive design.
---
.stylelintrc.json | 10 +
esbuild.js | 1 +
package-lock.json | 862 ++++++++++++++++++++++++++++-
package.json | 3 +
src/types/css.d.ts | 4 +
src/webview/chart/main.ts | 29 +-
src/webview/chart/styles.css | 162 ++++++
src/webview/details/main.ts | 33 +-
src/webview/details/styles.css | 154 ++++++
src/webview/diagnostics/main.ts | 272 +--------
src/webview/diagnostics/styles.css | 423 ++++++++++++++
src/webview/logviewer/main.ts | 676 +---------------------
src/webview/logviewer/styles.css | 746 +++++++++++++++++++++++++
src/webview/usage/main.ts | 123 +---
src/webview/usage/styles.css | 235 ++++++++
15 files changed, 2602 insertions(+), 1131 deletions(-)
create mode 100644 .stylelintrc.json
create mode 100644 src/types/css.d.ts
create mode 100644 src/webview/chart/styles.css
create mode 100644 src/webview/details/styles.css
create mode 100644 src/webview/diagnostics/styles.css
create mode 100644 src/webview/logviewer/styles.css
create mode 100644 src/webview/usage/styles.css
diff --git a/.stylelintrc.json b/.stylelintrc.json
new file mode 100644
index 0000000..1bbdb38
--- /dev/null
+++ b/.stylelintrc.json
@@ -0,0 +1,10 @@
+{
+ "extends": ["stylelint-config-standard"],
+ "rules": {
+ "selector-class-pattern": null,
+ "custom-property-pattern": null,
+ "no-descending-specificity": null,
+ "color-function-notation": null,
+ "alpha-value-notation": null
+ }
+}
diff --git a/esbuild.js b/esbuild.js
index 21d0b03..2b42477 100644
--- a/esbuild.js
+++ b/esbuild.js
@@ -59,6 +59,7 @@ async function main() {
external: ['vscode'],
logLevel: 'silent',
plugins: [esbuildProblemMatcherPlugin],
+ loader: { '.css': 'text' },
});
if (watch) {
diff --git a/package-lock.json b/package-lock.json
index 149eaf5..481bb71 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -32,6 +32,8 @@
"esbuild": "^0.27.2",
"eslint": "^9.39.2",
"npm-run-all": "^4.1.5",
+ "stylelint": "^17.1.1",
+ "stylelint-config-standard": "^40.0.0",
"typescript": "^5.9.3"
},
"engines": {
@@ -479,6 +481,67 @@
"node": ">=18"
}
},
+ "node_modules/@cacheable/memory": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.0.7.tgz",
+ "integrity": "sha512-RbxnxAMf89Tp1dLhXMS7ceft/PGsDl1Ip7T20z5nZ+pwIAsQ1p2izPjVG69oCLv/jfQ7HDPHTWK0c9rcAWXN3A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@cacheable/utils": "^2.3.3",
+ "@keyv/bigmap": "^1.3.0",
+ "hookified": "^1.14.0",
+ "keyv": "^5.5.5"
+ }
+ },
+ "node_modules/@cacheable/memory/node_modules/@keyv/bigmap": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz",
+ "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hashery": "^1.4.0",
+ "hookified": "^1.15.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "keyv": "^5.6.0"
+ }
+ },
+ "node_modules/@cacheable/memory/node_modules/keyv": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz",
+ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@keyv/serialize": "^1.1.1"
+ }
+ },
+ "node_modules/@cacheable/utils": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.3.4.tgz",
+ "integrity": "sha512-knwKUJEYgIfwShABS1BX6JyJJTglAFcEU7EXqzTdiGCXur4voqkiJkdgZIQtWNFhynzDWERcTYv/sETMu3uJWA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hashery": "^1.3.0",
+ "keyv": "^5.6.0"
+ }
+ },
+ "node_modules/@cacheable/utils/node_modules/keyv": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz",
+ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@keyv/serialize": "^1.1.1"
+ }
+ },
"node_modules/@csstools/color-helpers": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
@@ -571,9 +634,9 @@
}
},
"node_modules/@csstools/css-syntax-patches-for-csstree": {
- "version": "1.0.22",
- "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.22.tgz",
- "integrity": "sha512-qBcx6zYlhleiFfdtzkRgwNC7VVoAwfK76Vmsw5t+PbvtdknO9StgRk7ROvq9so1iqbdW4uLIDAsXRsTfUrIoOw==",
+ "version": "1.0.26",
+ "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.26.tgz",
+ "integrity": "sha512-6boXK0KkzT5u5xOgF6TKB+CLq9SOpEGmkZw0g5n9/7yg85wab3UzSxB8TxhLJ31L4SGJ6BCFRw/iftTha1CJXA==",
"funding": [
{
"type": "github",
@@ -584,10 +647,7 @@
"url": "https://opencollective.com/csstools"
}
],
- "license": "MIT-0",
- "engines": {
- "node": ">=18"
- }
+ "license": "MIT-0"
},
"node_modules/@csstools/css-tokenizer": {
"version": "3.0.4",
@@ -608,6 +668,52 @@
"node": ">=18"
}
},
+ "node_modules/@csstools/selector-resolve-nested": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-4.0.0.tgz",
+ "integrity": "sha512-9vAPxmp+Dx3wQBIUwc1v7Mdisw1kbbaGqXUM8QLTgWg7SoPGYtXBsMXvsFs/0Bn5yoFhcktzxNZGNaUt0VjgjA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "peerDependencies": {
+ "postcss-selector-parser": "^7.1.1"
+ }
+ },
+ "node_modules/@csstools/selector-specificity": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-6.0.0.tgz",
+ "integrity": "sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "peerDependencies": {
+ "postcss-selector-parser": "^7.1.1"
+ }
+ },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
@@ -1387,6 +1493,13 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@keyv/serialize": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz",
+ "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@kurkle/color": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
@@ -2969,6 +3082,30 @@
}
}
},
+ "node_modules/cacheable": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.3.2.tgz",
+ "integrity": "sha512-w+ZuRNmex9c1TR9RcsxbfTKCjSL0rh1WA5SABbrWprIHeNBdmyQLSYonlDy9gpD+63XT8DgZ/wNh1Smvc9WnJA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@cacheable/memory": "^2.0.7",
+ "@cacheable/utils": "^2.3.3",
+ "hookified": "^1.15.0",
+ "keyv": "^5.5.5",
+ "qified": "^0.6.0"
+ }
+ },
+ "node_modules/cacheable/node_modules/keyv": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz",
+ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@keyv/serialize": "^1.1.1"
+ }
+ },
"node_modules/call-bind": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
@@ -3297,6 +3434,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/colord": {
+ "version": "2.9.3",
+ "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
+ "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -3330,6 +3474,52 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/cosmiconfig": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
+ "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "env-paths": "^2.2.1",
+ "import-fresh": "^3.3.0",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/d-fischer"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.9.5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/cosmiconfig/node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -3345,6 +3535,16 @@
"node": ">= 8"
}
},
+ "node_modules/css-functions-list": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.3.tgz",
+ "integrity": "sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12 || >=16"
+ }
+ },
"node_modules/css-select": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
@@ -3388,6 +3588,19 @@
"url": "https://github.com/sponsors/fb55"
}
},
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/cssstyle": {
"version": "5.3.5",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.5.tgz",
@@ -3807,6 +4020,16 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
+ "node_modules/env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/environment": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
@@ -4368,6 +4591,16 @@
"fxparser": "src/cli/cli.js"
}
},
+ "node_modules/fastest-levenshtein": {
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
+ "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4.9.1"
+ }
+ },
"node_modules/fastq": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
@@ -4711,6 +4944,47 @@
"node": ">= 6"
}
},
+ "node_modules/global-modules": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
+ "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "global-prefix": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/global-prefix": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz",
+ "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ini": "^1.3.5",
+ "kind-of": "^6.0.2",
+ "which": "^1.3.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/global-prefix/node_modules/which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
"node_modules/globals": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
@@ -4773,6 +5047,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/globjoin": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz",
+ "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -4874,6 +5155,19 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/hashery": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.4.0.tgz",
+ "integrity": "sha512-Wn2i1In6XFxl8Az55kkgnFRiAlIAushzh26PTjL2AKtQcEfXrcLa7Hn5QOWGZEf3LU057P9TwwZjFyxfS1VuvQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hookified": "^1.14.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
@@ -4897,6 +5191,13 @@
"he": "bin/he"
}
},
+ "node_modules/hookified": {
+ "version": "1.15.1",
+ "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz",
+ "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/hosted-git-info": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
@@ -4929,6 +5230,19 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/html-tags": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-5.1.0.tgz",
+ "integrity": "sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/htmlparser2": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz",
@@ -5057,6 +5371,17 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/import-meta-resolve": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz",
+ "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
@@ -5091,8 +5416,7 @@
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"dev": true,
- "license": "ISC",
- "optional": true
+ "license": "ISC"
},
"node_modules/internal-slot": {
"version": "1.1.0",
@@ -5449,6 +5773,16 @@
"node": ">=8"
}
},
+ "node_modules/is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/is-potential-custom-element-name": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
@@ -5822,6 +6156,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -5943,6 +6284,23 @@
"json-buffer": "3.0.1"
}
},
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/known-css-properties": {
+ "version": "0.37.0",
+ "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.37.0.tgz",
+ "integrity": "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@@ -5977,6 +6335,13 @@
"immediate": "~3.0.5"
}
},
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/load-json-file": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
@@ -6114,6 +6479,17 @@
"node": ">= 0.4"
}
},
+ "node_modules/mathml-tag-names": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-4.0.0.tgz",
+ "integrity": "sha512-aa6AU2Pcx0VP/XWnh8IGL0SYSgQHDT6Ucror2j2mXeFAlN3ahaNs8EZtG1YiticMkSLj3Gt6VPFfZogt7G5iFQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/mdn-data": {
"version": "2.12.2",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
@@ -6129,6 +6505,19 @@
"node": ">= 0.10.0"
}
},
+ "node_modules/meow": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-14.0.0.tgz",
+ "integrity": "sha512-JhC3R1f6dbspVtmF3vKjAWz1EVIvwFrGGPLSdU6rK79xBwHWTuHoLnRX/t1/zHS1Ch1Y2UtIrih7DAHuH9JFJA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -6355,10 +6744,29 @@
"dev": true,
"license": "ISC"
},
- "node_modules/napi-build-utils": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
- "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/napi-build-utils": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
+ "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
"dev": true,
"license": "MIT",
"optional": true
@@ -7172,6 +7580,83 @@
"node": ">= 0.4"
}
},
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-safe-parser": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz",
+ "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz",
+ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/prebuild-install": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
@@ -7247,6 +7732,19 @@
"node": ">=6"
}
},
+ "node_modules/qified": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/qified/-/qified-0.6.0.tgz",
+ "integrity": "sha512-tsSGN1x3h569ZSU1u6diwhltLyfUWDp3YbFHedapTmpBl0B3P6U3+Qptg7xu+v+1io1EwhdPyyRHYbEw0KN2FA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hookified": "^1.14.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
"node_modules/qs": {
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
@@ -8348,6 +8846,324 @@
"boundary": "^2.0.0"
}
},
+ "node_modules/stylelint": {
+ "version": "17.1.1",
+ "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.1.1.tgz",
+ "integrity": "sha512-SBHVcLEcRF1M9OkD3oT0hT2PayDNLw2hd+aovmzfNQ2ys4Xd3oS9ZNizILWqhQvW802AqKN/vUTMwJqQYMBlWw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/stylelint"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/stylelint"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/css-parser-algorithms": "^4.0.0",
+ "@csstools/css-syntax-patches-for-csstree": "^1.0.25",
+ "@csstools/css-tokenizer": "^4.0.0",
+ "@csstools/media-query-list-parser": "^5.0.0",
+ "@csstools/selector-resolve-nested": "^4.0.0",
+ "@csstools/selector-specificity": "^6.0.0",
+ "balanced-match": "^3.0.1",
+ "colord": "^2.9.3",
+ "cosmiconfig": "^9.0.0",
+ "css-functions-list": "^3.2.3",
+ "css-tree": "^3.1.0",
+ "debug": "^4.4.3",
+ "fast-glob": "^3.3.3",
+ "fastest-levenshtein": "^1.0.16",
+ "file-entry-cache": "^11.1.2",
+ "global-modules": "^2.0.0",
+ "globby": "^16.1.0",
+ "globjoin": "^0.1.4",
+ "html-tags": "^5.1.0",
+ "ignore": "^7.0.5",
+ "import-meta-resolve": "^4.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-plain-object": "^5.0.0",
+ "known-css-properties": "^0.37.0",
+ "mathml-tag-names": "^4.0.0",
+ "meow": "^14.0.0",
+ "micromatch": "^4.0.8",
+ "normalize-path": "^3.0.0",
+ "picocolors": "^1.1.1",
+ "postcss": "^8.5.6",
+ "postcss-safe-parser": "^7.0.1",
+ "postcss-selector-parser": "^7.1.1",
+ "postcss-value-parser": "^4.2.0",
+ "string-width": "^8.1.0",
+ "supports-hyperlinks": "^4.4.0",
+ "svg-tags": "^1.0.0",
+ "table": "^6.9.0",
+ "write-file-atomic": "^7.0.0"
+ },
+ "bin": {
+ "stylelint": "bin/stylelint.mjs"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ }
+ },
+ "node_modules/stylelint-config-recommended": {
+ "version": "18.0.0",
+ "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-18.0.0.tgz",
+ "integrity": "sha512-mxgT2XY6YZ3HWWe3Di8umG6aBmWmHTblTgu/f10rqFXnyWxjKWwNdjSWkgkwCtxIKnqjSJzvFmPT5yabVIRxZg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/stylelint"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/stylelint"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "peerDependencies": {
+ "stylelint": "^17.0.0"
+ }
+ },
+ "node_modules/stylelint-config-standard": {
+ "version": "40.0.0",
+ "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-40.0.0.tgz",
+ "integrity": "sha512-EznGJxOUhtWck2r6dJpbgAdPATIzvpLdK9+i5qPd4Lx70es66TkBPljSg4wN3Qnc6c4h2n+WbUrUynQ3fanjHw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/stylelint"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/stylelint"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "stylelint-config-recommended": "^18.0.0"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "peerDependencies": {
+ "stylelint": "^17.0.0"
+ }
+ },
+ "node_modules/stylelint/node_modules/@csstools/css-parser-algorithms": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz",
+ "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "peerDependencies": {
+ "@csstools/css-tokenizer": "^4.0.0"
+ }
+ },
+ "node_modules/stylelint/node_modules/@csstools/css-tokenizer": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz",
+ "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.19.0"
+ }
+ },
+ "node_modules/stylelint/node_modules/@csstools/media-query-list-parser": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-5.0.0.tgz",
+ "integrity": "sha512-T9lXmZOfnam3eMERPsszjY5NK0jX8RmThmmm99FZ8b7z8yMaFZWKwLWGZuTwdO3ddRY5fy13GmmEYZXB4I98Eg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^4.0.0",
+ "@csstools/css-tokenizer": "^4.0.0"
+ }
+ },
+ "node_modules/stylelint/node_modules/@sindresorhus/merge-streams": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz",
+ "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/stylelint/node_modules/balanced-match": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-3.0.1.tgz",
+ "integrity": "sha512-vjtV3hiLqYDNRoiAv0zC4QaGAMPomEoq83PRmYIofPswwZurCeWR5LByXm7SyoL0Zh5+2z0+HC7jG8gSZJUh0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ }
+ },
+ "node_modules/stylelint/node_modules/file-entry-cache": {
+ "version": "11.1.2",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.2.tgz",
+ "integrity": "sha512-N2WFfK12gmrK1c1GXOqiAJ1tc5YE+R53zvQ+t5P8S5XhnmKYVB5eZEiLNZKDSmoG8wqqbF9EXYBBW/nef19log==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^6.1.20"
+ }
+ },
+ "node_modules/stylelint/node_modules/flat-cache": {
+ "version": "6.1.20",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.20.tgz",
+ "integrity": "sha512-AhHYqwvN62NVLp4lObVXGVluiABTHapoB57EyegZVmazN+hhGhLTn3uZbOofoTw4DSDvVCadzzyChXhOAvy8uQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cacheable": "^2.3.2",
+ "flatted": "^3.3.3",
+ "hookified": "^1.15.0"
+ }
+ },
+ "node_modules/stylelint/node_modules/globby": {
+ "version": "16.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-16.1.0.tgz",
+ "integrity": "sha512-+A4Hq7m7Ze592k9gZRy4gJ27DrXRNnC1vPjxTt1qQxEY8RxagBkBxivkCwg7FxSTG0iLLEMaUx13oOr0R2/qcQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@sindresorhus/merge-streams": "^4.0.0",
+ "fast-glob": "^3.3.3",
+ "ignore": "^7.0.5",
+ "is-path-inside": "^4.0.0",
+ "slash": "^5.1.0",
+ "unicorn-magic": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/stylelint/node_modules/has-flag": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-5.0.1.tgz",
+ "integrity": "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/stylelint/node_modules/is-path-inside": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz",
+ "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/stylelint/node_modules/string-width": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.1.tgz",
+ "integrity": "sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "^1.3.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/stylelint/node_modules/supports-hyperlinks": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-4.4.0.tgz",
+ "integrity": "sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^5.0.1",
+ "supports-color": "^10.2.2"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1"
+ }
+ },
+ "node_modules/stylelint/node_modules/unicorn-magic": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.4.0.tgz",
+ "integrity": "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/supports-color": {
"version": "10.2.2",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz",
@@ -8402,6 +9218,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/svg-tags": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz",
+ "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==",
+ "dev": true
+ },
"node_modules/symbol-tree": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
@@ -9313,6 +10135,20 @@
"license": "ISC",
"optional": true
},
+ "node_modules/write-file-atomic": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-7.0.0.tgz",
+ "integrity": "sha512-YnlPC6JqnZl6aO4uRc+dx5PHguiR9S6WeoLtpxNT9wIG+BDya7ZNE1q7KOjVgaA73hKhKLpVPgJ5QA9THQ5BRg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "imurmurhash": "^0.1.4",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
"node_modules/ws": {
"version": "8.18.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
diff --git a/package.json b/package.json
index 9a8a26c..4336ab5 100644
--- a/package.json
+++ b/package.json
@@ -233,6 +233,7 @@
"pretest": "npm run compile && npm run compile-tests && npm run lint",
"check-types": "tsc --noEmit",
"lint": "eslint src",
+ "lint:css": "stylelint \"src/webview/**/*.css\"",
"test": "vscode-test",
"test:node": "npm run compile-tests && node --test out/test/test-node/backend-identity.test.js",
"test:coverage": "npm run compile-tests && node --require ./out/test/test-node/vscode-shim-register.js --experimental-test-coverage --test --test-coverage-lines=60 --test-coverage-functions=60 --test-coverage-branches=60 --test-coverage-include=out/test/backend/**/*.js --test-coverage-include=out/test/utils/**/*.js out/test/test-node/backend-identity.test.js out/test/test-node/utils-errors.test.js out/test/test-node/backend-settings.test.js out/test/test-node/backend-copyConfig.test.js out/test/test-node/backend-integration.test.js out/test/test-node/backend-commands.test.js out/test/test-node/backend-facade-helpers.test.js out/test/test-node/backend-facade-rollups.test.js out/test/test-node/backend-facade-query.test.js",
@@ -251,6 +252,8 @@
"esbuild": "^0.27.2",
"eslint": "^9.39.2",
"npm-run-all": "^4.1.5",
+ "stylelint": "^17.1.1",
+ "stylelint-config-standard": "^40.0.0",
"typescript": "^5.9.3"
},
"dependencies": {
diff --git a/src/types/css.d.ts b/src/types/css.d.ts
new file mode 100644
index 0000000..31f07ea
--- /dev/null
+++ b/src/types/css.d.ts
@@ -0,0 +1,4 @@
+declare module '*.css' {
+ const content: string;
+ export default content;
+}
diff --git a/src/webview/chart/main.ts b/src/webview/chart/main.ts
index 6df9e07..bc6a4b4 100644
--- a/src/webview/chart/main.ts
+++ b/src/webview/chart/main.ts
@@ -1,6 +1,8 @@
// @ts-nocheck // Chart.js ESM bundle is loaded dynamically; skip CJS resolution noise
import { el, createButton } from '../shared/domUtils';
import { BUTTONS } from '../shared/buttonConfig';
+// CSS imported as text via esbuild
+import styles from './styles.css';
type ChartModule = typeof import('chart.js/auto');
type ChartConstructor = ChartModule['default'];
@@ -64,32 +66,7 @@ function renderLayout(data: InitialChartData): void {
root.replaceChildren();
const style = document.createElement('style');
- style.textContent = `
- :root { color: #e7e7e7; background: #0e0e0f; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
- body { margin: 0; background: #0e0e0f; }
- .container { padding: 16px; display: flex; flex-direction: column; gap: 14px; max-width: 1200px; margin: 0 auto; }
- .header { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding-bottom: 4px; }
- .header-left { display: flex; align-items: center; gap: 8px; }
- .header-icon { font-size: 20px; }
- .header-title { font-size: 16px; font-weight: 700; color: #fff; text-align: left; }
- .button-row { display: flex; flex-wrap: wrap; gap: 8px; }
- .section { background: linear-gradient(135deg, #1b1b1e 0%, #1f1f22 100%); border: 1px solid #2e2e34; border-radius: 10px; padding: 12px; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.28); text-align: center; }
- .section h3 { margin: 0 0 10px 0; font-size: 14px; display: flex; align-items: center; gap: 6px; color: #ffffff; letter-spacing: 0.2px; text-align: left; }
- .cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 10px; text-align: center; }
- .card { background: #1b1b1e; border: 1px solid #2a2a30; border-radius: 8px; padding: 12px; box-shadow: 0 2px 6px rgba(0,0,0,0.24); text-align: center; }
- .card-label { color: #b8b8b8; font-size: 11px; margin-bottom: 6px; }
- .card-value { color: #f6f6f6; font-size: 18px; font-weight: 700; }
- .card-sub { color: #9aa0a6; font-size: 11px; margin-top: 2px; }
- .chart-shell { background: #1b1b1e; border: 1px solid #2a2a30; border-radius: 10px; padding: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.22); text-align: center; }
- .chart-controls { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; justify-content: center; }
- .toggle { background: #202024; border: 1px solid #2d2d33; color: #e7e7e7; padding: 8px 12px; border-radius: 6px; font-size: 12px; cursor: pointer; transition: all 0.15s ease; }
- .toggle.active { background: #0e639c; border-color: #1177bb; color: #fff; }
- .toggle:hover { background: #2a2a30; }
- .toggle.active:hover { background: #1177bb; }
- .canvas-wrap { position: relative; height: 420px; }
- .footer { color: #a0a0a0; font-size: 11px; margin-top: 6px; text-align: center; }
- .footer em { color: #c0c0c0; }
- `;
+ style.textContent = styles;
const container = el('div', 'container');
const header = el('div', 'header');
diff --git a/src/webview/chart/styles.css b/src/webview/chart/styles.css
new file mode 100644
index 0000000..6d2d759
--- /dev/null
+++ b/src/webview/chart/styles.css
@@ -0,0 +1,162 @@
+:root {
+ color: #e7e7e7;
+ background: #0e0e0f;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+}
+
+body {
+ margin: 0;
+ background: #0e0e0f;
+}
+
+.container {
+ padding: 16px;
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+.header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 12px;
+ padding-bottom: 4px;
+}
+
+.header-left {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.header-icon {
+ font-size: 20px;
+}
+
+.header-title {
+ font-size: 16px;
+ font-weight: 700;
+ color: #fff;
+ text-align: left;
+}
+
+.button-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.section {
+ background: linear-gradient(135deg, #1b1b1e 0%, #1f1f22 100%);
+ border: 1px solid #2e2e34;
+ border-radius: 10px;
+ padding: 12px;
+ box-shadow: 0 4px 10px rgb(0, 0, 0, 0.28);
+ text-align: center;
+}
+
+.section h3 {
+ margin: 0 0 10px;
+ font-size: 14px;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ color: #fff;
+ letter-spacing: 0.2px;
+ text-align: left;
+}
+
+.cards {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
+ gap: 10px;
+ text-align: center;
+}
+
+.card {
+ background: #1b1b1e;
+ border: 1px solid #2a2a30;
+ border-radius: 8px;
+ padding: 12px;
+ box-shadow: 0 2px 6px rgb(0, 0, 0, 0.24);
+ text-align: center;
+}
+
+.card-label {
+ color: #b8b8b8;
+ font-size: 11px;
+ margin-bottom: 6px;
+}
+
+.card-value {
+ color: #f6f6f6;
+ font-size: 18px;
+ font-weight: 700;
+}
+
+.card-sub {
+ color: #9aa0a6;
+ font-size: 11px;
+ margin-top: 2px;
+}
+
+.chart-shell {
+ background: #1b1b1e;
+ border: 1px solid #2a2a30;
+ border-radius: 10px;
+ padding: 12px;
+ box-shadow: 0 2px 8px rgb(0, 0, 0, 0.22);
+ text-align: center;
+}
+
+.chart-controls {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-bottom: 8px;
+ justify-content: center;
+}
+
+.toggle {
+ background: #202024;
+ border: 1px solid #2d2d33;
+ color: #e7e7e7;
+ padding: 8px 12px;
+ border-radius: 6px;
+ font-size: 12px;
+ cursor: pointer;
+ transition: all 0.15s ease;
+}
+
+.toggle.active {
+ background: #0e639c;
+ border-color: #17b;
+ color: #fff;
+}
+
+.toggle:hover {
+ background: #2a2a30;
+}
+
+.toggle.active:hover {
+ background: #17b;
+}
+
+.canvas-wrap {
+ position: relative;
+ height: 420px;
+}
+
+.footer {
+ color: #a0a0a0;
+ font-size: 11px;
+ margin-top: 6px;
+ text-align: center;
+}
+
+.footer em {
+ color: #c0c0c0;
+}
diff --git a/src/webview/details/main.ts b/src/webview/details/main.ts
index 19252db..bd6d94a 100644
--- a/src/webview/details/main.ts
+++ b/src/webview/details/main.ts
@@ -6,6 +6,8 @@ import { BUTTONS } from '../shared/buttonConfig';
// Token estimators loaded from JSON
// @ts-ignore
import tokenEstimatorsJson from '../../tokenEstimators.json';
+// CSS imported as text via esbuild
+import styles from './styles.css';
type ModelUsage = Record;
type EditorUsage = Record;
@@ -100,36 +102,7 @@ function renderShell(
root.replaceChildren();
const style = document.createElement('style');
- style.textContent = `
- :root {
- color: #e7e7e7;
- background: #1e1e1e;
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
- }
- body { margin: 0; background: #0e0e0f; }
- .container { padding: 16px; display: flex; flex-direction: column; gap: 14px; max-width: 1200px; margin: 0 auto; }
- .header { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding-bottom: 4px; }
- .title { display: flex; align-items: center; gap: 8px; font-size: 16px; font-weight: 700; color: #fff; }
- .button-row { display: flex; flex-wrap: wrap; gap: 8px; }
- .sections { display: flex; flex-direction: column; gap: 16px; }
- .section { background: linear-gradient(135deg, #1b1b1e 0%, #1f1f22 100%); border: 1px solid #2e2e34; border-radius: 10px; padding: 12px; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.28); }
- .section h3 { margin: 0 0 10px 0; font-size: 14px; display: flex; align-items: center; gap: 6px; color: #ffffff; letter-spacing: 0.2px; }
- .stats-table { width: 100%; border-collapse: collapse; table-layout: fixed; background: #1b1b1e; border: 1px solid #2a2a30; border-radius: 8px; overflow: hidden; }
- .stats-table thead { background: #242429; }
- .stats-table th, .stats-table td { padding: 10px 12px; border-bottom: 1px solid #2d2d33; vertical-align: middle; }
- .stats-table th { text-align: left; color: #d0d0d0; font-weight: 700; font-size: 12px; letter-spacing: 0.1px; }
- .stats-table td { color: #f0f0f0; font-size: 12px; }
- .stats-table th.align-right, .stats-table td.align-right { text-align: right; }
- .stats-table tbody tr:nth-child(even) { background: #18181b; }
- .metric-label { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; }
- .period-header { display: flex; align-items: center; gap: 4px; color: #c8c8c8; }
- .align-right .period-header { justify-content: flex-end; }
- .value-right { text-align: right; }
- .muted { color: #a0a0a0; font-size: 11px; margin-top: 4px; }
- .notes { margin: 4px 0 0 0; padding-left: 16px; color: #c8c8c8; }
- .notes li { margin: 4px 0; line-height: 1.4; }
- .footer { color: #a0a0a0; font-size: 11px; margin-top: 6px; }
- `;
+ style.textContent = styles;
const container = el('div', 'container');
const header = el('div', 'header');
diff --git a/src/webview/details/styles.css b/src/webview/details/styles.css
new file mode 100644
index 0000000..4fffe5d
--- /dev/null
+++ b/src/webview/details/styles.css
@@ -0,0 +1,154 @@
+:root {
+ color: #e7e7e7;
+ background: #1e1e1e;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+}
+
+body {
+ margin: 0;
+ background: #0e0e0f;
+}
+
+.container {
+ padding: 16px;
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+.header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 12px;
+ padding-bottom: 4px;
+}
+
+.title {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 16px;
+ font-weight: 700;
+ color: #fff;
+}
+
+.button-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.sections {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+.section {
+ background: linear-gradient(135deg, #1b1b1e 0%, #1f1f22 100%);
+ border: 1px solid #2e2e34;
+ border-radius: 10px;
+ padding: 12px;
+ box-shadow: 0 4px 10px rgb(0, 0, 0, 0.28);
+}
+
+.section h3 {
+ margin: 0 0 10px;
+ font-size: 14px;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ color: #fff;
+ letter-spacing: 0.2px;
+}
+
+.stats-table {
+ width: 100%;
+ border-collapse: collapse;
+ table-layout: fixed;
+ background: #1b1b1e;
+ border: 1px solid #2a2a30;
+ border-radius: 8px;
+ overflow: hidden;
+}
+
+.stats-table thead {
+ background: #242429;
+}
+
+.stats-table th,
+.stats-table td {
+ padding: 10px 12px;
+ border-bottom: 1px solid #2d2d33;
+ vertical-align: middle;
+}
+
+.stats-table th {
+ text-align: left;
+ color: #d0d0d0;
+ font-weight: 700;
+ font-size: 12px;
+ letter-spacing: 0.1px;
+}
+
+.stats-table td {
+ color: #f0f0f0;
+ font-size: 12px;
+}
+
+.stats-table th.align-right,
+.stats-table td.align-right {
+ text-align: right;
+}
+
+.stats-table tbody tr:nth-child(even) {
+ background: #18181b;
+}
+
+.metric-label {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ font-weight: 600;
+}
+
+.period-header {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ color: #c8c8c8;
+}
+
+.align-right .period-header {
+ justify-content: flex-end;
+}
+
+.value-right {
+ text-align: right;
+}
+
+.muted {
+ color: #a0a0a0;
+ font-size: 11px;
+ margin-top: 4px;
+}
+
+.notes {
+ margin: 4px 0 0;
+ padding-left: 16px;
+ color: #c8c8c8;
+}
+
+.notes li {
+ margin: 4px 0;
+ line-height: 1.4;
+}
+
+.footer {
+ color: #a0a0a0;
+ font-size: 11px;
+ margin-top: 6px;
+}
diff --git a/src/webview/diagnostics/main.ts b/src/webview/diagnostics/main.ts
index a60a478..5de96d1 100644
--- a/src/webview/diagnostics/main.ts
+++ b/src/webview/diagnostics/main.ts
@@ -1,5 +1,7 @@
// Diagnostics Report webview with tabbed interface
import { buttonHtml } from '../shared/buttonConfig';
+// CSS imported as text via esbuild
+import styles from './styles.css';
// Constants
const LOADING_PLACEHOLDER = 'Loading...';
@@ -607,275 +609,7 @@ function renderLayout(data: DiagnosticsData): void {
const detailedFiles = data.detailedSessionFiles || [];
root.innerHTML = `
-
+