Skip to content

Commit 4d35710

Browse files
authored
Merge pull request #50 from celonymire/ui-revamp
2 parents 676ef95 + c765b8e commit 4d35710

35 files changed

+1189
-1133
lines changed

.github/copilot-instructions.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,21 @@
33
This repository contains a VS Code extension that provides two webview-based tools for competitive programming: **Judge** and **Stress**.
44

55
- Extension backend code lives under `src/extension/**` and uses the VS Code API. `JudgeViewProvider` and `StressViewProvider` extend `BaseViewProvider`, which handles webview setup, CSP nonce generation, workspaceState storage, and message dispatch.
6-
- Webview frontend code lives under `src/webview/**` and is built with Preact. It talks to the extension only through typed messages defined in `src/shared/*-messages.ts`.
7-
- Shared enums, message contracts, and types live under `src/shared/**` and define the protocol between extension and webviews (including the `Status` lifecycle and testcase structures).
6+
- Webview frontend code lives under `src/webview/**` and is built with React + `@legendapp/state` for reactive state management. It talks to the extension only through typed messages defined in `src/shared/*-messages.ts`.
7+
- Shared enums, message contracts, and Valibot schemas live under `src/shared/**` and define the protocol between extension and webviews (including the `Status` lifecycle in `enums.ts` and testcase structures in `schemas.ts`).
88

99
Build and tooling:
1010

1111
- Use `bun install` to install dependencies.
12-
- Use `bun run watch` during development and `bun run prod` for production builds. The build is two-stage: Tailwind CLI compiles `src/styles/global.css` to `dist/styles.css`, then Rspack bundles the extension and webviews into `dist/`.
13-
- Run `bun run lint` and `bun run format` to apply ESLint (TypeScript + Preact) and Prettier rules.
12+
- Use `bun run watch` during development and `bun run prod` for production builds. Rspack bundles the extension and webviews into `dist/`. Each webview has its own `index.css` stylesheet that is bundled alongside its JavaScript.
13+
- Run `bun run lint` and `bun run format` to apply ESLint (TypeScript + React) and Prettier rules.
1414

1515
Design and implementation guidelines:
1616

17-
- All extension ↔ webview communication must use the discriminated unions and enums in `src/shared/*-messages.ts`. Append to enums instead of reordering to keep numeric values and stored data stable.
17+
- All extension ↔ webview communication must use the discriminated unions and Valibot schemas in `src/shared/*-messages.ts`. Append to enums instead of reordering to keep numeric values and stored data stable.
1818
- Use `compile()` and `Runnable` from `src/extension/utils/runtime.ts` for running code, and `resolveVariables` / `resolveCommand` from `src/extension/utils/vscode.ts` for safe, cross-platform commands.
1919
- Use `TextHandler` for streamed output; always call `.reset()` for a fresh run and `.write(data, last)` for updates.
20+
- Use `@legendapp/state` for webview state: wrap components with `observer()`, access values via `.get()`, mutate via `.set()`, and use `<For>` for reactive list rendering.
2021
- Keep changes minimal and consistent with existing patterns. Prefer reusing the Judge/Stress provider and webview patterns over introducing new architectures.
2122

2223
Additional path-specific details are defined in `.github/instructions/*.instructions.md`, which Copilot uses when working in matching files.
Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
---
2-
applyTo: "package.json,rspack.config.ts,eslint.config.ts,tailwind.config.ts,tsconfig*.json"
2+
applyTo: "package.json,rspack.config.ts,eslint.config.ts,tsconfig*.json"
33
---
44

5-
This repository uses Bun, Rspack, Tailwind CSS, TypeScript, and ESLint to build and lint the Fast Olympic Coding VS Code extension and its webviews.
5+
This repository uses Bun, Rspack, TypeScript, and ESLint to build and lint the Fast Olympic Coding VS Code extension and its webviews.
66

77
When working on the build, config, or tooling files matched by this pattern:
88

99
- Use `bun install` to install dependencies. Prefer Bun scripts in `package.json` over ad-hoc commands.
10-
- During development, run `bun run watch` to start Rspack and the Tailwind CLI in watch mode. For production builds, use `bun run prod` to generate minified, no-sourcemap bundles.
11-
- The build is two-stage:
12-
- `build:css`: Tailwind CLI reads from `src/styles/global.css` and writes `dist/styles.css`.
13-
- `build:js`: Rspack bundles the extension backend and webview frontends into `dist/`.
14-
- The Rspack configuration (`rspack.config.ts`) exports two configs: one targeting Node.js/CommonJS for the extension, and one targeting the web/ES modules for the webviews (TypeScript/TSX with Preact). Keep this separation intact when modifying the config.
10+
- During development, run `bun run watch` to start Rspack in watch mode. For production builds, use `bun run prod` to generate minified, no-sourcemap bundles.
11+
- Rspack bundles the extension backend and webview frontends into `dist/`. Each webview has its own `index.css` stylesheet that is bundled alongside its JavaScript.
12+
- The Rspack configuration (`rspack.config.ts`) exports two configs: one targeting Node.js/CommonJS for the extension, and one targeting the web/ES modules for the webviews (TypeScript/TSX with React). Keep this separation intact when modifying the config.
1513
- Type-checking is handled by ForkTsCheckerWebpackPlugin, using `tsconfig.node.json` for the extension and `tsconfig.app.json` for the webviews. Keep these project files aligned with the respective code trees.
16-
- For quality gates, use `bun run lint` (ESLint with TypeScript + Preact rules) and `bun run format` (Prettier). Avoid adding overlapping or conflicting linters/formatters.
17-
- Prefer minimal, focused config changes. Avoid introducing large new toolchains or build systems; extend the existing Rspack + Tailwind + Bun setup instead.
14+
- For quality gates, use `bun run lint` (ESLint with TypeScript + React rules) and `bun run format` (Prettier). Avoid adding overlapping or conflicting linters/formatters.
15+
- Prefer minimal, focused config changes. Avoid introducing large new toolchains or build systems; extend the existing Rspack + Bun setup instead.

.github/instructions/extension-backend.instructions.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ This repository is a VS Code extension called "Fast Olympic Coding." The `src/ex
66

77
When changing files under `src/extension/**`:
88

9-
- Treat `JudgeViewProvider` and `StressViewProvider` as the main controllers for their respective webviews. They extend `BaseViewProvider`, which encapsulates webview setup, CSP nonce generation, message posting, and workspaceState access keyed by active file path.
10-
- All extension ⇄ webview communication must go through the discriminated unions and enums in `src/shared/*-messages.ts`. Do not introduce ad-hoc string message types; instead, extend the shared enums and message unions.
11-
- The `Status` enum in `src/shared/types.ts` represents the lifecycle: COMPILING → RUNNING → (AC | WA | RE | TL | CE | NA | EDITING). Preserve existing numeric values and append new states only at the end.
9+
- Treat `JudgeViewProvider` and `StressViewProvider` as the main controllers for their respective webviews. They extend `BaseViewProvider`, which encapsulates webview setup, CSP nonce generation, message posting via Valibot-validated schemas, and workspaceState access keyed by active file path.
10+
- Webviews rely on Codicons for icons; `BaseViewProvider` already whitelists `@vscode/codicons/dist/codicon.css` in local resource roots and injects the stylesheet. Preserve that setup (CSP, resource roots, and link tag) whenever adjusting webview HTML or resource handling so icons keep rendering.
11+
- All extension ⇄ webview communication must go through the discriminated unions and Valibot schemas in `src/shared/*-messages.ts`. Do not introduce ad-hoc string message types; instead, extend the shared enums and message unions.
12+
- The `Status` enum in `src/shared/enums.ts` represents the lifecycle: COMPILING → RUNNING → (AC | WA | RE | TL | CE | NA | EDITING). Preserve existing numeric values and append new states only at the end.
1213
- Persisted testcases and limits are stored in `workspaceState`, with a top-level key per view ("judge" / "stress") and an inner key per absolute file path. Treat the "default" state (no testcases and timeLimit = 0) as "no data" and delete storage entries rather than persisting defaults indefinitely.
13-
- Use `TextHandler` (from the extension utilities) for all streamed output shown in the webviews. Always call `.reset()` before a fresh run and `.write(data, last)` to update output so truncation, batching, and whitespace handling remain correct.
14+
- Use `TextHandler` (from the extension utilities) for all streamed output shown in the webviews. It must keep the full data for comparisons while truncating display output, normalizes CRLF to LF, and ensures a trailing newline on final writes. Always call `.reset()` before a fresh run and `.write(data, last)` to update output so truncation, batching, and whitespace handling remain correct.
1415
- For compilation and execution, use the helpers in `src/extension/utils/runtime.ts`. Specifically, use `compile()` (which caches builds by md5 of the full command) and `Runnable` (which wraps child processes with timing, timeout via `AbortSignal.timeout`, and exit/timeout information) instead of spawning processes manually.
1516
- In the Stress view logic, keep the sequential generator pattern that feeds testcases to the solution and reference solution, and ensure the loop respects both per-test (`stressTestcaseTimeLimit`) and global (`stressTimeLimit`) limits while exiting early on the first mismatch or failure for speed.
1617
- Always resolve command variables via `resolveVariables` / `resolveCommand` from `src/extension/utils/vscode.ts` before spawning external processes. Use built-in variables like `${exeExtname}`, `${path:...}`, and `${fileDirnameBasename}` for cross-platform-safe paths.

.github/instructions/shared-contracts.instructions.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,20 @@
22
applyTo: "src/shared/**/*.ts"
33
---
44

5-
The `src/shared/**` directory contains enums, message contracts, schemas, and shared types used by both the extension backend (`src/extension/**`) and the webview frontend (`src/webview/**`). These files define the public protocol between the two halves of the extension.
5+
The `src/shared/**` directory contains enums, Valibot schemas, message contracts, and shared types used by both the extension backend (`src/extension/**`) and the webview frontend (`src/webview/**`). These files define the public protocol between the two halves of the extension.
66

77
When changing files under `src/shared/**`:
88

9-
- Treat the message and enum definitions here as the single source of truth for extension ↔ webview communication. All messages should be discriminated unions with clear `type` or `kind` fields, and both sides should switch on these discriminants.
9+
- Treat the message and enum definitions here as the single source of truth for extension ↔ webview communication. All messages use Valibot schemas (e.g., `v.object()`, `v.literal()`, `v.enum()`) with discriminated unions via `v.union([...])` and `type` fields, and both sides should switch on these discriminants.
1010
- When you add new message types or enum members, append them to the existing enums instead of reordering or renaming values. This preserves numeric enum identifiers and avoids breaking persisted data (for example, stored `Status` values).
11-
- Keep message payloads minimal but explicit. Prefer well-typed fields over loosely structured objects to make it clear what each side should expect.
11+
- Keep message payloads minimal but explicit. Prefer well-typed Valibot schemas over loosely structured objects to make it clear what each side should expect.
1212
- If you change a shared type, update both the extension providers (under `src/extension/providers/**`) and the corresponding webview handlers (under `src/webview/**`) in the same change to avoid protocol drift.
13-
- The `Status` enum in `types.ts` models the run lifecycle: COMPILING → RUNNING → (AC | WA | RE | TL | CE | NA | EDITING). Preserve these semantics when adding new statuses and ensure the UI and backend continue to interpret them consistently.
14-
- Schemas and validation helpers here should remain lightweight and focused on the Judge/Stress workflows. Avoid introducing heavy validation frameworks unless absolutely required.
13+
- The `Status` enum in `enums.ts` models the run lifecycle: COMPILING → RUNNING → (AC | WA | RE | TL | CE | NA | EDITING). Preserve these semantics when adding new statuses and ensure the UI and backend continue to interpret them consistently.
14+
- Use Valibot (`import * as v from "valibot"`) for all schemas. Define individual message schemas, then combine them into a union schema and export an inferred type (e.g., `type WebviewMessage = v.InferOutput<typeof WebviewMessageSchema>`).
1515
- Because these contracts are central to the extension's behavior, keep changes focused and backwards-compatible wherever possible.
1616

1717
When adding a new feature that requires extension ↔ webview communication:
1818

19-
- First, define or extend the shared contract in `src/shared/` (add a new enum member, interface, and union entry as needed). Append to existing enums rather than reordering to keep numeric values stable.
19+
- First, define or extend the shared contract in `src/shared/` (add a new enum member, Valibot schema, and union entry). Append to existing enums rather than reordering to keep numeric values stable.
2020
- Then update the relevant Provider class under `src/extension/providers/` to mutate its internal state, call `_postMessage` with the new message type, and persist state via `writeStorage()` only after all mutations are complete.
21-
- Finally, implement handling for the new message type on the webview side under `src/webview/**`, updating the relevant `App.tsx` and components to react to the new messages. Do not rely on message ordering beyond the established initial `INITIAL_STATE` / `SHOW` messages.
21+
- Finally, implement handling for the new message type on the webview side under `src/webview/**`, adding a case to the `window.addEventListener("message", ...)` handler in `App.tsx`. Do not rely on message ordering beyond the established initial `INITIAL_STATE` / `SHOW` messages.

.github/instructions/webview-frontend.instructions.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,24 @@
22
applyTo: "src/webview/**/*.ts,src/webview/**/*.tsx"
33
---
44

5-
The `src/webview/**` tree contains the Preact-based frontend code for the Judge and Stress webviews. These run in an isolated browser-like environment, not in the VS Code extension host.
5+
The `src/webview/**` tree contains the React-based frontend code for the Judge and Stress webviews. These run in an isolated browser-like environment, not in the VS Code extension host.
66

77
When changing files under `src/webview/**`:
88

9-
- Do not import the `vscode` module here; webview code should communicate with the extension only via the typed message contracts defined in `src/shared/*-messages.ts`.
10-
- Treat the `App.tsx` components under `src/webview/judge/` and `src/webview/stress/` as entry points for each view. They receive initial state and subsequent updates exclusively through message types defined in the shared message unions.
11-
- When you need new messages or actions from the webview to the extension, first extend the shared enums and message interfaces in `src/shared/*-messages.ts`, then update both the extension providers and the webview components to handle the new types. Avoid magic string message names.
12-
- Assume styles are provided by an external CSS file built from `src/styles/global.css` into `dist/styles.css` via Tailwind CLI. Do not import CSS directly into TypeScript/TSX; the extension side (`BaseViewProvider`) injects the stylesheet as a `<link>` in the webview HTML.
13-
- Prefer the existing lightweight stack (Preact + signals) for state management. Avoid introducing heavier frontend state libraries; instead, follow the current patterns used in the Judge and Stress apps.
9+
- Do not import the `vscode` module here; webview code should communicate with the extension only via `postProviderMessage()` from each view's `message.ts` file, which wraps `vscode.postMessage()` with typed contracts from `src/shared/*-messages.ts`.
10+
- Treat the `App.tsx` components under `src/webview/judge/` and `src/webview/stress/` as entry points for each view. They receive initial state and subsequent updates exclusively through message types defined in the shared message unions via `window.addEventListener("message", ...)`.
11+
- Codicons are the icon set used across the webviews. The backend already exposes `@vscode/codicons/dist/codicon.css`; prefer the existing `codicon-` classes instead of adding new icon libraries or custom SVG sprite sheets unless strictly necessary.
12+
- When you need new messages or actions from the webview to the extension, first extend the shared enums and Valibot schemas in `src/shared/*-messages.ts`, then update both the extension providers and the webview components to handle the new types. Avoid magic string message names.
13+
- Each webview has its own `index.css` stylesheet (e.g., `src/webview/judge/index.css`, `src/webview/stress/index.css`). Import it in the webview's entry file (`index.tsx`). The CSS is bundled by Rspack alongside the webview JavaScript.
14+
- Use `@legendapp/state` for reactive state management:
15+
- Wrap components with `observer()` from `@legendapp/state/react` for automatic re-rendering.
16+
- Access observable values with `.get()` and mutate with `.set(value)` or `.set(prev => newValue)`.
17+
- Use `<For each={obs$}>` for reactive list rendering; it efficiently handles additions/removals without re-rendering the entire list.
18+
- Use `useObservable(value)` for component-local observable state.
19+
- Collections like `Map` and `Set` have fine-grained reactivity; use `state$.map.get(key)` to get an observable for a specific entry.
20+
- Avoid introducing additional state libraries; follow the patterns in the Judge and Stress apps.
21+
- Shared webview utilities live in `src/webview/utils.ts` (e.g., `getStatusColor`).
1422
- Keep UI logic decoupled from process execution details. The webview should focus on rendering state and sending/receiving typed messages, not on spawning processes or resolving filesystem paths.
1523
- Maintain consistency with existing components such as `AutoresizeTextarea`, `Testcase`, and `State` in terms of props, message handling, and minimal DOM manipulation.
24+
- Keep the file layout lean (e.g., flat component files per view) and avoid introducing extra nested directories unless there's a clear benefit; mirror the existing Judge/Stress structure when adding new pieces.
1625
- Keep changes surgical: avoid large-scale refactors or stylistic rewrites unless they are necessary for a specific task.

.vscodeignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
!LICENSE
55
!media/icon.png
66
!dist/judge/index.js
7+
!dist/judge/index.css
78
!dist/stress/index.js
8-
!dist/extension.js
9-
!dist/styles.css
9+
!dist/stress/index.css
10+
!dist/extension.js

0 commit comments

Comments
 (0)