-
Notifications
You must be signed in to change notification settings - Fork 974
Expand file tree
/
Copy pathChatView.logic.ts
More file actions
126 lines (114 loc) · 3.75 KB
/
ChatView.logic.ts
File metadata and controls
126 lines (114 loc) · 3.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { ProjectId, type ProviderKind, type ThreadId } from "@t3tools/contracts";
import { type ChatMessage, type Thread } from "../types";
import { randomUUID } from "~/lib/utils";
import { getAppModelOptions } from "../appSettings";
import { type ComposerImageAttachment, type DraftThreadState } from "../composerDraftStore";
import { Schema } from "effect";
export const LAST_INVOKED_SCRIPT_BY_PROJECT_KEY = "t3code:last-invoked-script-by-project";
const WORKTREE_BRANCH_PREFIX = "t3code";
export const LastInvokedScriptByProjectSchema = Schema.Record(ProjectId, Schema.String);
export function buildLocalDraftThread(
threadId: ThreadId,
draftThread: DraftThreadState,
fallbackModel: string,
error: string | null,
): Thread {
return {
id: threadId,
codexThreadId: null,
projectId: draftThread.projectId,
title: "New thread",
model: fallbackModel,
runtimeMode: draftThread.runtimeMode,
interactionMode: draftThread.interactionMode,
session: null,
messages: [],
error,
createdAt: draftThread.createdAt,
latestTurn: null,
lastVisitedAt: draftThread.createdAt,
branch: draftThread.branch,
worktreePath: draftThread.worktreePath,
turnDiffSummaries: [],
activities: [],
proposedPlans: [],
};
}
export function revokeBlobPreviewUrl(previewUrl: string | undefined): void {
if (!previewUrl || typeof URL === "undefined" || !previewUrl.startsWith("blob:")) {
return;
}
URL.revokeObjectURL(previewUrl);
}
export function revokeUserMessagePreviewUrls(message: ChatMessage): void {
if (message.role !== "user" || !message.attachments) {
return;
}
for (const attachment of message.attachments) {
if (attachment.type !== "image") {
continue;
}
revokeBlobPreviewUrl(attachment.previewUrl);
}
}
export function collectUserMessageBlobPreviewUrls(message: ChatMessage): string[] {
if (message.role !== "user" || !message.attachments) {
return [];
}
const previewUrls: string[] = [];
for (const attachment of message.attachments) {
if (attachment.type !== "image") continue;
if (!attachment.previewUrl || !attachment.previewUrl.startsWith("blob:")) continue;
previewUrls.push(attachment.previewUrl);
}
return previewUrls;
}
export type SendPhase = "idle" | "preparing-worktree" | "sending-turn";
export interface PullRequestDialogState {
initialReference: string | null;
key: number;
}
export function readFileAsDataUrl(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.addEventListener("load", () => {
if (typeof reader.result === "string") {
resolve(reader.result);
return;
}
reject(new Error("Could not read image data."));
});
reader.addEventListener("error", () => {
reject(reader.error ?? new Error("Failed to read image."));
});
reader.readAsDataURL(file);
});
}
export function buildTemporaryWorktreeBranchName(): string {
// Keep the 8-hex suffix shape for backend temporary-branch detection.
const token = randomUUID().slice(0, 8).toLowerCase();
return `${WORKTREE_BRANCH_PREFIX}/${token}`;
}
export function cloneComposerImageForRetry(
image: ComposerImageAttachment,
): ComposerImageAttachment {
if (typeof URL === "undefined" || !image.previewUrl.startsWith("blob:")) {
return image;
}
try {
return {
...image,
previewUrl: URL.createObjectURL(image.file),
};
} catch {
return image;
}
}
export function getCustomModelOptionsByProvider(settings: {
customCodexModels: readonly string[];
}): Record<ProviderKind, ReadonlyArray<{ slug: string; name: string }>> {
return {
codex: getAppModelOptions("codex", settings.customCodexModels),
claudeCode: getAppModelOptions("claudeCode", []),
};
}