Skip to content

Commit b65b723

Browse files
authored
Merge branch 'main' into qwen-code
2 parents b4f1ec4 + 5ac5440 commit b65b723

File tree

14 files changed

+1867
-467
lines changed

14 files changed

+1867
-467
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import * as fs from "fs"
2+
import * as path from "path"
3+
4+
export const extractAllCSSVariables = (): Record<string, string> => {
5+
const variables: Record<string, string> = {}
6+
7+
// There are too many of these and we don't need them
8+
const isExcludedVariable = (property: string): boolean => {
9+
return property.includes("-icon-") && (property.includes("-content") || property.includes("-font-family"))
10+
}
11+
12+
const extractFromStyleRule = (style: CSSStyleDeclaration): void => {
13+
for (let i = 0; i < style.length; i++) {
14+
const property = style[i]
15+
if (property.startsWith("--") && !isExcludedVariable(property)) {
16+
const value = style.getPropertyValue(property).trim()
17+
if (value) {
18+
variables[property] = value
19+
}
20+
}
21+
}
22+
}
23+
24+
const extractFromStylesheets = (): void => {
25+
for (let i = 0; i < document.styleSheets.length; i++) {
26+
const sheet = document.styleSheets[i]
27+
try {
28+
const rules = sheet.cssRules || []
29+
for (const rule of Array.from(rules)) {
30+
if (rule instanceof CSSStyleRule) {
31+
extractFromStyleRule(rule.style)
32+
}
33+
}
34+
} catch (_e) {
35+
// Skip inaccessible stylesheets
36+
}
37+
}
38+
}
39+
40+
const extractFromComputedStyles = (): void => {
41+
const rootStyles = getComputedStyle(document.documentElement)
42+
for (let i = 0; i < rootStyles.length; i++) {
43+
const property = rootStyles[i]
44+
if (property.startsWith("--") && !isExcludedVariable(property)) {
45+
const value = rootStyles.getPropertyValue(property).trim()
46+
if (value && !variables[property]) {
47+
variables[property] = value
48+
}
49+
}
50+
}
51+
}
52+
53+
extractFromStylesheets()
54+
extractFromComputedStyles()
55+
56+
return variables
57+
}
58+
59+
export const generateCSSOutput = (allVariables: Record<string, string>): string => {
60+
const sortedEntries = Object.entries(allVariables).sort(([a], [b]) => a.localeCompare(b))
61+
const cssLines = sortedEntries.map(([key, value]) => `${key}: ${value};`)
62+
63+
return `/* All CSS Variables - Auto-extracted from VS Code via Playwright */
64+
/* Generated on ${new Date().toISOString()} */
65+
66+
${cssLines.map((line) => ` ${line}`).join("\n")}
67+
`
68+
}
69+
70+
export const saveVariablesToFile = async (cssOutput: string, finalFilename: string): Promise<string> => {
71+
const outputDir = path.join(process.cwd(), "../storybook/generated-theme-styles")
72+
await fs.promises.mkdir(outputDir, { recursive: true })
73+
const outputPath = path.join(outputDir, finalFilename)
74+
await fs.promises.writeFile(outputPath, cssOutput)
75+
return outputPath
76+
}
77+

apps/playwright-e2e/helpers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export * from "./console-logging"
44
export * from "./test-setup-helpers"
55
export * from "./chat-helpers"
66
export * from "./notification-helpers"
7+
export * from "./vscode-helpers"

apps/playwright-e2e/helpers/test-setup-helpers.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// kilocode_change - new file
22
import { type Page } from "@playwright/test"
3-
import { verifyExtensionInstalled, waitForWebviewText, configureApiKeyThroughUI } from "./webview-helpers"
3+
import { waitForWebviewText, configureApiKeyThroughUI } from "./webview-helpers"
4+
import { verifyExtensionInstalled } from "./vscode-helpers"
45

56
export async function setupTestEnvironment(page: Page): Promise<void> {
67
await verifyExtensionInstalled(page)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { type Page, expect } from "@playwright/test"
2+
3+
const modifier = process.platform === "darwin" ? "Meta" : "Control"
4+
5+
export async function verifyExtensionInstalled(page: Page) {
6+
try {
7+
const activityBarIcon = page.locator('[aria-label*="Kilo"], [title*="Kilo"]').first()
8+
expect(await activityBarIcon).toBeDefined()
9+
console.log("✅ Extension installed!")
10+
} catch (_error) {
11+
throw new Error("Failed to find the installed extension! Check if the build failed and try again.")
12+
}
13+
}
14+
15+
export async function closeAllTabs(page: Page): Promise<void> {
16+
const tabs = page.locator(".tab a.label-name")
17+
const count = await tabs.count()
18+
if (count > 0) {
19+
// Close all editor tabs using the default keyboard command [Cmd+K Cmd+W]
20+
await page.keyboard.press(`${modifier}+K`)
21+
await page.keyboard.press(`${modifier}+W`)
22+
23+
const dismissedTabs = page.locator(".tab a.label-name")
24+
await expect(dismissedTabs).not.toBeVisible()
25+
}
26+
}
27+
28+
export async function waitForAllExtensionActivation(page: Page): Promise<void> {
29+
try {
30+
const activatingStatus = page.locator("text=Activating Extensions")
31+
const activatingStatusCount = await activatingStatus.count()
32+
if (activatingStatusCount > 0) {
33+
console.log("⌛️ Waiting for `Activating Extensions` to go away...")
34+
await activatingStatus.waitFor({ state: "hidden", timeout: 10000 })
35+
}
36+
} catch {
37+
// noop
38+
}
39+
}
40+
41+
export async function switchToTheme(page: Page, themeName: string): Promise<void> {
42+
await page.keyboard.press(`${modifier}+K`)
43+
await page.waitForTimeout(100)
44+
await page.keyboard.press(`${modifier}+T`)
45+
await page.waitForTimeout(100)
46+
47+
await page.keyboard.type(themeName)
48+
await page.waitForTimeout(100)
49+
50+
await page.keyboard.press("Enter")
51+
await page.waitForTimeout(100)
52+
}

apps/playwright-e2e/helpers/webview-helpers.ts

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import { type Page, type FrameLocator, expect } from "@playwright/test"
33
import type { WebviewMessage } from "../../../src/shared/WebviewMessage"
44
import { ProviderSettings } from "@roo-code/types"
55

6-
const modifier = process.platform === "darwin" ? "Meta" : "Control"
7-
86
const defaultPlaywrightApiConfig = {
97
apiProvider: "openrouter" as const,
108
openRouterApiKey: process.env.OPENROUTER_API_KEY,
@@ -48,16 +46,6 @@ export async function postWebviewMessage(page: Page, message: WebviewMessage): P
4846
}
4947
}
5048

51-
export async function verifyExtensionInstalled(page: Page) {
52-
try {
53-
const activityBarIcon = page.locator('[aria-label*="Kilo"], [title*="Kilo"]').first()
54-
expect(await activityBarIcon).toBeDefined()
55-
console.log("✅ Extension installed!")
56-
} catch (_error) {
57-
throw new Error("Failed to find the installed extension! Check if the build failed and try again.")
58-
}
59-
}
60-
6149
export async function upsertApiConfiguration(page: Page, apiConfiguration?: Partial<ProviderSettings>): Promise<void> {
6250
await postWebviewMessage(page, {
6351
type: "upsertApiConfiguration",
@@ -98,29 +86,3 @@ export async function configureApiKeyThroughUI(page: Page): Promise<void> {
9886
await submitButton.click()
9987
console.log("✅ Provider configured!")
10088
}
101-
102-
export async function closeAllTabs(page: Page): Promise<void> {
103-
const tabs = page.locator(".tab a.label-name")
104-
const count = await tabs.count()
105-
if (count > 0) {
106-
// Close all editor tabs using the default keyboard command [Cmd+K Cmd+W]
107-
await page.keyboard.press(`${modifier}+K`)
108-
await page.keyboard.press(`${modifier}+W`)
109-
110-
const dismissedTabs = page.locator(".tab a.label-name")
111-
await expect(dismissedTabs).not.toBeVisible()
112-
}
113-
}
114-
115-
export async function waitForAllExtensionActivation(page: Page): Promise<void> {
116-
try {
117-
const activatingStatus = page.locator("text=Activating Extensions")
118-
const activatingStatusCount = await activatingStatus.count()
119-
if (activatingStatusCount > 0) {
120-
console.log("⌛️ Waiting for `Activating Extensions` to go away...")
121-
await activatingStatus.waitFor({ state: "hidden", timeout: 10000 })
122-
}
123-
} catch {
124-
// noop
125-
}
126-
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* VS Code theme extraction using Playwright
5+
* Extracts comprehensive CSS variables from running VS Code instance
6+
* Replaces the old theme generation script with complete variable extraction
7+
*/
8+
9+
import { execSync } from "child_process"
10+
import fs from "fs"
11+
import path from "path"
12+
import { fileURLToPath } from "url"
13+
14+
const __filename = fileURLToPath(import.meta.url)
15+
const __dirname = path.dirname(__filename)
16+
17+
const PLAYWRIGHT_E2E_DIR = path.resolve(__dirname, "..")
18+
const STORYBOOK_OUTPUT_DIR = path.resolve(__dirname, "../../storybook/generated-theme-styles")
19+
20+
async function extractThemeVariables() {
21+
console.log("🎨 Extracting VS Code themes using Playwright...\n")
22+
23+
try {
24+
// Run the Playwright extraction test
25+
console.log("🚀 Running Playwright CSS extraction...")
26+
const playwrightCommand = `cd "${PLAYWRIGHT_E2E_DIR}" && npx playwright test --config=scripts/theme-extraction.config.ts --reporter=line`
27+
execSync(playwrightCommand, { stdio: "inherit", cwd: PLAYWRIGHT_E2E_DIR })
28+
29+
// Verify the files were created directly in the storybook directory
30+
const darkOutputPath = path.join(STORYBOOK_OUTPUT_DIR, "dark-modern.css")
31+
const lightOutputPath = path.join(STORYBOOK_OUTPUT_DIR, "light-modern.css")
32+
33+
if (!fs.existsSync(darkOutputPath) || !fs.existsSync(lightOutputPath)) {
34+
throw new Error("Playwright extraction failed - CSS files not found in storybook directory")
35+
}
36+
37+
console.log(`✅ Generated dark-modern.css`)
38+
console.log(`✅ Generated light-modern.css`)
39+
console.log(`\n🎉 Theme extraction complete! Files saved to ${STORYBOOK_OUTPUT_DIR}`)
40+
} catch (error) {
41+
console.error("❌ Theme extraction failed:", error.message)
42+
process.exit(1)
43+
}
44+
}
45+
46+
// Run if called directly
47+
if (import.meta.url === `file://${process.argv[1]}`) {
48+
extractThemeVariables().catch((error) => {
49+
console.error("Fatal error:", error)
50+
process.exit(1)
51+
})
52+
}
53+
54+
export { extractThemeVariables }
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { test, expect, type TestFixtures } from "../tests/playwright-base-test"
2+
import { findWebview } from "../helpers"
3+
import { type Page, type FrameLocator } from "@playwright/test"
4+
import { extractAllCSSVariables, generateCSSOutput, saveVariablesToFile } from "../helpers/css-extraction-helpers"
5+
import { switchToTheme } from "../helpers/vscode-helpers"
6+
7+
const extractVariablesForTheme = async (workbox: Page, webviewFrame: FrameLocator) => {
8+
const mainWindowVariables = await workbox.evaluate(extractAllCSSVariables)
9+
const webviewVariables = await webviewFrame.locator("body").evaluate(extractAllCSSVariables)
10+
11+
const allVariables = { ...webviewVariables, ...mainWindowVariables }
12+
13+
return {
14+
mainWindowVariables,
15+
webviewVariables,
16+
allVariables,
17+
}
18+
}
19+
20+
test.describe("CSS Variable Extraction", () => {
21+
test("should extract CSS variables in both light and dark themes", async ({ workbox }: TestFixtures) => {
22+
const webviewFrame = await findWebview(workbox)
23+
24+
await switchToTheme(workbox, "dark")
25+
const darkResults = await extractVariablesForTheme(workbox, webviewFrame)
26+
const darkCSSOutput = generateCSSOutput(darkResults.allVariables)
27+
await saveVariablesToFile(darkCSSOutput, "dark-modern.css")
28+
29+
await switchToTheme(workbox, "light")
30+
const lightResults = await extractVariablesForTheme(workbox, webviewFrame)
31+
const lightCSSOutput = generateCSSOutput(lightResults.allVariables)
32+
await saveVariablesToFile(lightCSSOutput, "light-modern.css")
33+
34+
expect(Object.keys(darkResults.allVariables).length).toBeGreaterThan(0)
35+
expect(Object.keys(lightResults.allVariables).length).toBeGreaterThan(0)
36+
})
37+
})
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { defineConfig } from "@playwright/test"
2+
import { TestOptions } from "../tests/playwright-base-test"
3+
import * as path from "path"
4+
import { fileURLToPath } from "url"
5+
6+
// ES module equivalent of __dirname
7+
const __filename = fileURLToPath(import.meta.url)
8+
const __dirname = path.dirname(__filename)
9+
10+
export default defineConfig<void, TestOptions>({
11+
timeout: 120_000,
12+
expect: { timeout: 30_000 },
13+
reporter: "line",
14+
workers: 1,
15+
retries: 0,
16+
globalSetup: "../playwright.globalSetup",
17+
testDir: ".",
18+
testMatch: "theme-extraction-script.test.ts", // Only run this specific test
19+
outputDir: "../test-results",
20+
projects: [{ name: "VSCode stable", use: { vscodeVersion: "stable" } }],
21+
use: {
22+
trace: "on-first-retry",
23+
video: "retry-with-video",
24+
},
25+
})

apps/playwright-e2e/tests/settings.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { test, expect, type TestFixtures } from "./playwright-base-test"
2-
import { verifyExtensionInstalled, findWebview, upsertApiConfiguration } from "../helpers/webview-helpers"
3-
import { closeAllToastNotifications } from "../helpers"
2+
import { findWebview, upsertApiConfiguration, closeAllToastNotifications, verifyExtensionInstalled } from "../helpers"
43

54
test.describe("Settings", () => {
65
test("screenshots", async ({ workbox: page, takeScreenshot }: TestFixtures) => {

apps/playwright-e2e/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
"playwright.config.ts",
2222
"playwright.globalSetup.ts",
2323
"types/**/*",
24-
"helpers/console-logging.ts"
24+
"helpers/**/*",
25+
"scripts/theme-extraction-script.test.ts"
2526
],
2627
"exclude": ["node_modules", "test-results", "tests/**/__tests__/**/*"]
2728
}

0 commit comments

Comments
 (0)