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
123 changes: 0 additions & 123 deletions apps/server/src/codexTextGenerator.test.ts

This file was deleted.

29 changes: 7 additions & 22 deletions apps/server/src/codexTextGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,16 @@ import type {
PrContentGenerationResult,
TextGenerationService,
} from "./coreServices";
import {
type ProcessRunOptions,
type ProcessRunResult,
runProcess,
} from "./processRunner";
import { type ProcessRunOptions, type ProcessRunResult, runProcess } from "./processRunner";

type ProcessRunner = (
command: string,
args: readonly string[],
options?: ProcessRunOptions,
) => Promise<ProcessRunResult>;

const CODEX_MODEL = "gpt-5.3-codex-spark";
const CODEX_REASONING_EFFORT = "medium";
const CODEX_MODEL = "gpt-5.3-codex";
const CODEX_REASONING_EFFORT = "low";
Comment thread
juliusmarminge marked this conversation as resolved.

const COMMIT_OUTPUT_SCHEMA_JSON = {
type: "object",
Expand Down Expand Up @@ -78,10 +74,7 @@ function limitSection(value: string, maxChars: number): string {
}

async function writeTempFile(prefix: string, content: string): Promise<string> {
const filePath = path.join(
os.tmpdir(),
`t3code-${prefix}-${process.pid}-${randomUUID()}.tmp`,
);
const filePath = path.join(os.tmpdir(), `t3code-${prefix}-${process.pid}-${randomUUID()}.tmp`);
await fs.writeFile(filePath, content, "utf8");
return filePath;
}
Expand Down Expand Up @@ -128,10 +121,7 @@ async function runCodexJson<T>({
parse: (raw: unknown) => T;
run: ProcessRunner;
}): Promise<T> {
const schemaPath = await writeTempFile(
"codex-schema",
JSON.stringify(outputSchemaJson),
);
const schemaPath = await writeTempFile("codex-schema", JSON.stringify(outputSchemaJson));
let outputPath: string | null = null;

try {
Expand Down Expand Up @@ -174,10 +164,7 @@ async function runCodexJson<T>({

return parse(parsedJson);
} finally {
await Promise.all([
safeUnlink(schemaPath),
...(outputPath ? [safeUnlink(outputPath)] : []),
]);
await Promise.all([safeUnlink(schemaPath), ...(outputPath ? [safeUnlink(outputPath)] : [])]);
}
}

Expand Down Expand Up @@ -226,9 +213,7 @@ export class CodexTextGenerator implements TextGenerationService {
};
}

async generatePrContent(
input: PrContentGenerationInput,
): Promise<PrContentGenerationResult> {
async generatePrContent(input: PrContentGenerationInput): Promise<PrContentGenerationResult> {
const prompt = [
"You write GitHub pull request content.",
"Return a JSON object with keys: title, body.",
Expand Down
68 changes: 68 additions & 0 deletions apps/web/src/components/ThreadTerminalDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import {
useRef,
useState,
} from "react";
import {
extractTerminalLinks,
isTerminalLinkActivation,
preferredTerminalEditor,
resolvePathLinkTarget,
} from "../terminal-links";
import { isTerminalClearShortcut } from "../terminal-shortcuts";
import { DEFAULT_THREAD_TERMINAL_HEIGHT } from "../types";

Expand Down Expand Up @@ -298,6 +304,67 @@ export default function ThreadTerminalDrawer({
return false;
});

const terminalLinksDisposable = terminal.registerLinkProvider({
provideLinks: (bufferLineNumber, callback) => {
const activeTerminal = terminalRef.current;
if (!activeTerminal) {
callback(undefined);
return;
}

const line = activeTerminal.buffer.active.getLine(bufferLineNumber - 1);
if (!line) {
callback(undefined);
return;
}

const lineText = line.translateToString(true);
const matches = extractTerminalLinks(lineText);
if (matches.length === 0) {
callback(undefined);
return;
}

callback(
matches.map((match) => ({
text: match.text,
range: {
start: { x: match.start + 1, y: bufferLineNumber },
end: { x: match.end, y: bufferLineNumber },
},
activate: (event: MouseEvent) => {
if (!isTerminalLinkActivation(event)) {
return;
}

const latestTerminal = terminalRef.current;
if (!latestTerminal) {
return;
}

if (match.kind === "url") {
void api.shell.openExternal(match.text).catch((error) => {
writeSystemMessage(
latestTerminal,
error instanceof Error ? error.message : "Unable to open link",
);
});
return;
}

const target = resolvePathLinkTarget(match.text, cwd);
void api.shell.openInEditor(target, preferredTerminalEditor()).catch((error) => {
writeSystemMessage(
latestTerminal,
error instanceof Error ? error.message : "Unable to open path",
);
});
},
})),
);
},
});

const inputDisposable = terminal.onData((data) => {
void api.terminal
.write({ threadId, data })
Expand Down Expand Up @@ -393,6 +460,7 @@ export default function ThreadTerminalDrawer({
window.clearTimeout(fitTimer);
unsubscribe();
inputDisposable.dispose();
terminalLinksDisposable.dispose();
themeObserver.disconnect();
terminalRef.current = null;
fitAddonRef.current = null;
Expand Down
110 changes: 110 additions & 0 deletions apps/web/src/terminal-links.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { describe, expect, it } from "vitest";

import {
extractTerminalLinks,
isTerminalLinkActivation,
resolvePathLinkTarget,
} from "./terminal-links";

describe("extractTerminalLinks", () => {
it("finds http urls and path tokens", () => {
const line =
"failed at https://example.com/docs and src/components/ThreadTerminalDrawer.tsx:42";
expect(extractTerminalLinks(line)).toEqual([
{
kind: "url",
text: "https://example.com/docs",
start: 10,
end: 34,
},
{
kind: "path",
text: "src/components/ThreadTerminalDrawer.tsx:42",
start: 39,
end: 81,
},
]);
});

it("trims trailing punctuation from links", () => {
const line = "(https://example.com/docs), ./src/main.ts:12.";
expect(extractTerminalLinks(line)).toEqual([
{
kind: "url",
text: "https://example.com/docs",
start: 1,
end: 25,
},
{
kind: "path",
text: "./src/main.ts:12",
start: 28,
end: 44,
},
]);
});
});

describe("resolvePathLinkTarget", () => {
it("resolves relative paths against cwd", () => {
expect(
resolvePathLinkTarget(
"src/components/ThreadTerminalDrawer.tsx:42:7",
"/Users/julius/project",
),
).toBe("/Users/julius/project/src/components/ThreadTerminalDrawer.tsx:42:7");
});

it("keeps absolute paths unchanged", () => {
expect(
resolvePathLinkTarget(
"/Users/julius/project/src/main.ts:12",
"/Users/julius/project",
),
).toBe("/Users/julius/project/src/main.ts:12");
});
});

describe("isTerminalLinkActivation", () => {
it("requires cmd on macOS", () => {
expect(
isTerminalLinkActivation(
{
metaKey: true,
ctrlKey: false,
},
"MacIntel",
),
).toBe(true);
expect(
isTerminalLinkActivation(
{
metaKey: false,
ctrlKey: true,
},
"MacIntel",
),
).toBe(false);
});

it("requires ctrl on non-macOS", () => {
expect(
isTerminalLinkActivation(
{
metaKey: false,
ctrlKey: true,
},
"Win32",
),
).toBe(true);
expect(
isTerminalLinkActivation(
{
metaKey: true,
ctrlKey: false,
},
"Linux",
),
).toBe(false);
});
});
Loading