Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24.13.1
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,36 @@ T3 Code is a minimal web GUI for coding agents (currently Codex and Claude, more

## Installation

## Local Development Toolchain

This repo expects the versions declared in `.mise.toml`:

- Node `24.13.1`
- Bun `1.3.9`

Recommended setup:

```bash
mise install
```

Then activate `mise` in your shell so entering the repo automatically uses the right versions. If you use `nvm`, there is also an `.nvmrc` with the same Node version.

Quick verification:

```bash
node -v
bun -v
```

Both should match the versions above before you run `bun run typecheck`, `bun run start:desktop:main-state`, or other repo scripts.

The root scripts now fail fast with a direct toolchain error if your shell is on the wrong Node version. As a fallback, you can also run commands through `mise` explicitly:

```bash
mise exec node@24.13.1 -- bun run typecheck
```

> [!WARNING]
> T3 Code currently supports Codex and Claude.
> Install and authenticate at least one provider before use:
Expand Down
24 changes: 16 additions & 8 deletions apps/web/src/components/SnippetPickerDialog.browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ describe("SnippetPickerDialog", () => {
}
});

it("selects the currently highlighted snippet when pressing enter", async () => {
it("selects the highlighted snippet when pressing enter after keyboard navigation", async () => {
const mounted = await mountDialog({
snippets: [
makeSnippet({
Expand All @@ -148,15 +148,23 @@ describe("SnippetPickerDialog", () => {
{ timeout: 8_000, interval: 16 },
);

getResultRows()[0]?.dispatchEvent(
new MouseEvent("mouseenter", {
input.dispatchEvent(
new KeyboardEvent("keydown", {
key: "ArrowDown",
bubbles: true,
cancelable: true,
}),
);
await new Promise<void>((resolve) => {
window.requestAnimationFrame(() => resolve());
});

await vi.waitFor(
() => {
const results = getResultRows();
expect(results[1]?.dataset.highlighted).toBe("true");
expect(results[0]?.dataset.highlighted).toBeUndefined();
},
{ timeout: 8_000, interval: 16 },
);

input.dispatchEvent(
new KeyboardEvent("keydown", {
key: "Enter",
Expand All @@ -169,8 +177,8 @@ describe("SnippetPickerDialog", () => {
() => {
expect(mounted.onSelectSnippet).toHaveBeenCalledWith(
expect.objectContaining({
id: "snippet-2",
text: "Second saved snippet",
id: "snippet-1",
text: "First saved snippet",
}),
);
},
Expand Down
51 changes: 26 additions & 25 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,35 +24,36 @@
},
"type": "module",
"scripts": {
"dev": "node scripts/dev-runner.ts dev",
"dev:server": "node scripts/dev-runner.ts dev:server",
"dev:web": "node scripts/dev-runner.ts dev:web",
"dev:marketing": "turbo run dev --filter=@t3tools/marketing",
"dev:desktop": "node scripts/dev-runner.ts dev:desktop",
"start": "turbo run start --filter=t3",
"start:desktop": "turbo run start --filter=@t3tools/desktop",
"start:desktop:main-state": "cd apps/desktop && T3CODE_STATE_DIR=\"$HOME/.t3/userdata\" bun run start",
"start:marketing": "turbo run preview --filter=@t3tools/marketing",
"start:mock-update-server": "bun run scripts/mock-update-server.ts",
"build": "turbo run build",
"build:marketing": "turbo run build --filter=@t3tools/marketing",
"build:desktop": "turbo run build --filter=@t3tools/desktop --filter=t3",
"typecheck": "turbo run typecheck",
"toolchain:check": "node scripts/check-toolchain.mjs",
"dev": "bun run toolchain:check && node scripts/dev-runner.ts dev",
"dev:server": "bun run toolchain:check && node scripts/dev-runner.ts dev:server",
"dev:web": "bun run toolchain:check && node scripts/dev-runner.ts dev:web",
"dev:marketing": "bun run toolchain:check && turbo run dev --filter=@t3tools/marketing",
"dev:desktop": "bun run toolchain:check && node scripts/dev-runner.ts dev:desktop",
"start": "bun run toolchain:check && turbo run start --filter=t3",
"start:desktop": "bun run toolchain:check && turbo run start --filter=@t3tools/desktop",
"start:desktop:main-state": "bun run toolchain:check && cd apps/desktop && T3CODE_STATE_DIR=\"$HOME/.t3/userdata\" bun run start",
"start:marketing": "bun run toolchain:check && turbo run preview --filter=@t3tools/marketing",
"start:mock-update-server": "bun run toolchain:check && bun run scripts/mock-update-server.ts",
"build": "bun run toolchain:check && turbo run build",
"build:marketing": "bun run toolchain:check && turbo run build --filter=@t3tools/marketing",
"build:desktop": "bun run toolchain:check && turbo run build --filter=@t3tools/desktop --filter=t3",
"typecheck": "bun run toolchain:check && turbo run typecheck",
"lint": "oxlint --report-unused-disable-directives",
"test": "turbo run test",
"test:desktop-smoke": "turbo run smoke-test --filter=@t3tools/desktop",
"test": "bun run toolchain:check && turbo run test",
"test:desktop-smoke": "bun run toolchain:check && turbo run smoke-test --filter=@t3tools/desktop",
"fmt": "oxfmt",
"fmt:check": "oxfmt --check",
"build:contracts": "turbo run build --filter=@t3tools/contracts",
"dist:desktop:artifact": "node scripts/build-desktop-artifact.ts",
"dist:desktop:dmg": "node scripts/build-desktop-artifact.ts --platform mac --target dmg",
"dist:desktop:dmg:arm64": "node scripts/build-desktop-artifact.ts --platform mac --target dmg --arch arm64",
"dist:desktop:dmg:x64": "node scripts/build-desktop-artifact.ts --platform mac --target dmg --arch x64",
"dist:desktop:linux": "node scripts/build-desktop-artifact.ts --platform linux --target AppImage --arch x64",
"dist:desktop:win": "node scripts/build-desktop-artifact.ts --platform win --target nsis --arch x64",
"release:smoke": "node scripts/release-smoke.ts",
"build:contracts": "bun run toolchain:check && turbo run build --filter=@t3tools/contracts",
"dist:desktop:artifact": "bun run toolchain:check && node scripts/build-desktop-artifact.ts",
"dist:desktop:dmg": "bun run toolchain:check && node scripts/build-desktop-artifact.ts --platform mac --target dmg",
"dist:desktop:dmg:arm64": "bun run toolchain:check && node scripts/build-desktop-artifact.ts --platform mac --target dmg --arch arm64",
"dist:desktop:dmg:x64": "bun run toolchain:check && node scripts/build-desktop-artifact.ts --platform mac --target dmg --arch x64",
"dist:desktop:linux": "bun run toolchain:check && node scripts/build-desktop-artifact.ts --platform linux --target AppImage --arch x64",
"dist:desktop:win": "bun run toolchain:check && node scripts/build-desktop-artifact.ts --platform win --target nsis --arch x64",
"release:smoke": "bun run toolchain:check && node scripts/release-smoke.ts",
"clean": "rm -rf node_modules apps/*/node_modules packages/*/node_modules apps/*/dist apps/*/dist-electron packages/*/dist .turbo apps/*/.turbo packages/*/.turbo",
"sync:vscode-icons": "node scripts/sync-vscode-icons.mjs"
"sync:vscode-icons": "bun run toolchain:check && node scripts/sync-vscode-icons.mjs"
},
"devDependencies": {
"@types/node": "catalog:",
Expand Down
78 changes: 78 additions & 0 deletions scripts/check-toolchain.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/usr/bin/env node

import { execFileSync } from "node:child_process";
import { readFileSync } from "node:fs";
import { join } from "node:path";

const FALLBACK_NODE_VERSION = "24.13.1";
const FALLBACK_BUN_VERSION = "1.3.9";

function readMiseToolVersion(toolName) {
const misePath = join(import.meta.dirname, "..", ".mise.toml");
const contents = readFileSync(misePath, "utf8");
const match = contents.match(new RegExp(`^${toolName}\\s*=\\s*"([^"]+)"$`, "m"));
return match?.[1] ?? null;
}

function normalizeVersion(version) {
return version.startsWith("v") ? version.slice(1) : version;
}

function parseVersion(version) {
const [major = "0", minor = "0", patch = "0"] = normalizeVersion(version).split(".");
return {
major: Number(major),
minor: Number(minor),
patch: Number(patch),
};
}

function isCompatibleVersion(current, expected) {
if (current.major !== expected.major) {
return false;
}
if (current.minor !== expected.minor) {
return current.minor > expected.minor;
}
return current.patch >= expected.patch;
}

function readCurrentBunVersion() {
try {
return normalizeVersion(execFileSync("bun", ["--version"], { encoding: "utf8" }).trim());
} catch {
return null;
}
}

function exitWithToolingGuidance(message) {
console.error(message);
console.error("");
console.error("Fix:");
console.error(" 1. Run `mise install`.");
console.error(
" 2. Activate mise in your shell so this repo picks the right Node/Bun automatically.",
);
console.error(" 3. Or run commands with `mise exec node@24.13.1 -- bun run <script>`.");
process.exit(1);
}

const expectedNodeVersion = readMiseToolVersion("node") ?? FALLBACK_NODE_VERSION;
const expectedBunVersion = readMiseToolVersion("bun") ?? FALLBACK_BUN_VERSION;
const currentNodeVersion = normalizeVersion(process.version);
const currentBunVersion = readCurrentBunVersion();

if (!isCompatibleVersion(parseVersion(currentNodeVersion), parseVersion(expectedNodeVersion))) {
exitWithToolingGuidance(
`This repo requires Node ${expectedNodeVersion}, but current Node is ${currentNodeVersion}.`,
);
}

if (
currentBunVersion &&
!isCompatibleVersion(parseVersion(currentBunVersion), parseVersion(expectedBunVersion))
) {
exitWithToolingGuidance(
`This repo requires Bun ${expectedBunVersion}, but current Bun is ${currentBunVersion}.`,
);
}
Loading