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
7 changes: 6 additions & 1 deletion gui/src/fetch-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,10 @@ export async function readJsonOrThrow<T>(

export async function readJsonIfOk<T>(res: Response): Promise<T | null | undefined> {
if (!res.ok) return null;
return readJsonBody<T>(res);
try {
return await readJsonBody<T>(res);
} catch {
// Malformed / non-JSON OK bodies must not throw — callers treat null as "no usable payload".
return null;
}
}
4 changes: 4 additions & 0 deletions gui/src/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,9 @@ export const de: Record<TKey, string> = {
"api.confirm": "Bestätigen",
"api.deleteAria": "API-Schlüssel löschen",
"api.usageSampleInput": "Hallo, Welt!",
"api.keysLoadFailed": "API-Schlüssel konnten nicht geladen werden.",
"api.createFailed": "API-Schlüssel konnte nicht erstellt werden.",
"api.deleteFailed": "API-Schlüssel konnte nicht gelöscht werden.",
// Claude Code inbound
"nav.claude": "Claude",
"claude.subtitle": "GPT, Gemini und andere Modelle in Claude Code verwenden.",
Expand Down Expand Up @@ -815,6 +818,7 @@ export const de: Record<TKey, string> = {
"claude.removeMapping": "Regel entfernen",
"claude.aliases": "Verfügbare Modelle",
"claude.aliasesHint": "Modelle, die im /model-Menü von Claude Code erscheinen.",
"claude.aliasProviderOther": "Sonstiges",
"claude.loading": "Lädt…",
"claude.loadFail": "Claude-Einstellungen konnten nicht geladen werden",
"claude.saved": "Gespeichert.",
Expand Down
4 changes: 4 additions & 0 deletions gui/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,9 @@ export const en = {
"api.usageResponsesTitle": "Responses example",
"api.usageMessagesTitle": "Messages example",
"api.usageSampleInput": "Hello, world!",
"api.keysLoadFailed": "Could not load API keys.",
"api.createFailed": "Could not create API key.",
"api.deleteFailed": "Could not delete API key.",
// Claude Code inbound
"nav.claude": "Claude",
"claude.subtitle": "Use GPT, Gemini, and other models inside Claude Code.",
Expand Down Expand Up @@ -1119,6 +1122,7 @@ export const en = {
"claude.removeMapping": "Remove rule",
"claude.aliases": "Available models",
"claude.aliasesHint": "Models that appear in Claude Code's /model menu.",
"claude.aliasProviderOther": "Other",
"claude.loading": "Loading…",
"claude.loadFail": "Failed to load Claude settings",
"claude.saved": "Saved.",
Expand Down
4 changes: 4 additions & 0 deletions gui/src/i18n/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,9 @@ export const ja: Record<TKey, string> = {
"api.confirm": "確認",
"api.deleteAria": "API キーを削除",
"api.usageSampleInput": "こんにちは、世界!",
"api.keysLoadFailed": "APIキーを読み込めませんでした。",
"api.createFailed": "APIキーを作成できませんでした。",
"api.deleteFailed": "APIキーを削除できませんでした。",
// Claude Code inbound
"nav.claude": "Claude",
"claude.subtitle": "Claude Code 内で GPT、Gemini などのモデルを使用します。",
Expand Down Expand Up @@ -1073,6 +1076,7 @@ export const ja: Record<TKey, string> = {
"claude.removeMapping": "ルールを削除",
"claude.aliases": "利用可能なモデル",
"claude.aliasesHint": "Claude Code の /model メニューに表示されるモデル。",
"claude.aliasProviderOther": "その他",
"claude.loading": "読み込み中…",
"claude.loadFail": "Claude 設定の読み込みに失敗しました",
"claude.saved": "保存しました。",
Expand Down
4 changes: 4 additions & 0 deletions gui/src/i18n/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,9 @@ export const ko: Record<TKey, string> = {
"api.confirm": "확인",
"api.deleteAria": "API 키 삭제",
"api.usageSampleInput": "안녕하세요, 세계!",
"api.keysLoadFailed": "API 키를 불러오지 못했습니다.",
"api.createFailed": "API 키를 만들지 못했습니다.",
"api.deleteFailed": "API 키를 삭제하지 못했습니다.",
// Claude Code inbound
"nav.claude": "Claude",
"claude.subtitle": "Claude Code에서 GPT, Gemini 등 다른 모델도 쓸 수 있게 해줍니다.",
Expand Down Expand Up @@ -835,6 +838,7 @@ export const ko: Record<TKey, string> = {
"claude.removeMapping": "규칙 삭제",
"claude.aliases": "사용 가능한 모델",
"claude.aliasesHint": "Claude Code의 /model 메뉴에 표시되는 모델 목록입니다.",
"claude.aliasProviderOther": "기타",
"claude.loading": "불러오는 중…",
"claude.loadFail": "Claude 설정을 불러오지 못했습니다",
"claude.saved": "저장되었습니다.",
Expand Down
4 changes: 4 additions & 0 deletions gui/src/i18n/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,9 @@ export const ru: Record<TKey, string> = {
"api.confirm": "Подтвердить",
"api.deleteAria": "Удалить API-ключ",
"api.usageSampleInput": "Привет, мир!",
"api.keysLoadFailed": "Не удалось загрузить API-ключи.",
"api.createFailed": "Не удалось создать API-ключ.",
"api.deleteFailed": "Не удалось удалить API-ключ.",
// Claude Code inbound
"nav.claude": "Claude",
"claude.subtitle": "Используйте GPT, Gemini и другие модели внутри Claude Code.",
Expand Down Expand Up @@ -1118,6 +1121,7 @@ export const ru: Record<TKey, string> = {
"claude.removeMapping": "Удалить правило",
"claude.aliases": "Доступные модели",
"claude.aliasesHint": "Модели, которые появляются в меню /model в Claude Code.",
"claude.aliasProviderOther": "Другое",
"claude.loading": "Загрузка…",
"claude.loadFail": "Не удалось загрузить настройки Claude",
"claude.saved": "Сохранено.",
Expand Down
4 changes: 4 additions & 0 deletions gui/src/i18n/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,9 @@ export const zh: Record<TKey, string> = {
"api.confirm": "确认",
"api.deleteAria": "删除 API 密钥",
"api.usageSampleInput": "你好,世界!",
"api.keysLoadFailed": "无法加载 API 密钥。",
"api.createFailed": "无法创建 API 密钥。",
"api.deleteFailed": "无法删除 API 密钥。",
// Claude Code inbound
"nav.claude": "Claude",
"claude.subtitle": "在 Claude Code 中使用 GPT、Gemini 等其他模型。",
Expand Down Expand Up @@ -835,6 +838,7 @@ export const zh: Record<TKey, string> = {
"claude.removeMapping": "删除规则",
"claude.aliases": "可用模型",
"claude.aliasesHint": "Claude Code 的 /model 菜单中显示的模型列表。",
"claude.aliasProviderOther": "其他",
"claude.loading": "加载中…",
"claude.loadFail": "加载 Claude 设置失败",
"claude.saved": "已保存。",
Expand Down
11 changes: 8 additions & 3 deletions gui/src/intl-formatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,19 @@ const CREDIT_DATE_FORMAT = new Intl.DateTimeFormat(undefined, {
});

export function formatCreditDate(iso: string): string {
return CREDIT_DATE_FORMAT.format(new Date(iso));
const date = new Date(iso);
if (Number.isNaN(date.getTime())) return "\u2014";
return CREDIT_DATE_FORMAT.format(date);
}

/** Format a USD cost estimate for display. Returns "—" when unavailable. */
export function formatEstimatedUsdValue(value: number, locale?: string): string {
if (!Number.isFinite(value) || value < 0) return "\u2014";
return `~$${cachedNumberFormat(locale, {
const formatted = cachedNumberFormat(locale, {
style: "currency",
currency: "USD",
minimumFractionDigits: 4,
maximumFractionDigits: 4,
}).format(value)}`;
}).format(value);
return `~${formatted}`;
}
Loading
Loading