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
14 changes: 7 additions & 7 deletions apps/app/src/app/components/mcp-auth-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export default function McpAuthModal(props: McpAuthModalProps) {
statusPoll = window.setInterval(async () => {
if (Date.now() - startedAt >= MCP_AUTH_TIMEOUT_MS) {
stopStatusPolling();
setError(translate("mcp.auth.request_timed_out"));
setError("Request timed out.");
return;
}

Expand Down Expand Up @@ -694,7 +694,7 @@ export default function McpAuthModal(props: McpAuthModalProps) {
<CheckCircle2 size={24} class="text-green-11" />
</div>
<div>
<p class="text-sm font-medium text-gray-12">{translate("mcp.auth.already_connected")}</p>
<p class="text-sm font-medium text-gray-12">Already Connected</p>
<p class="text-xs text-gray-11">
{translate("mcp.auth.already_connected_description", { server: serverName() })}
</p>
Expand Down Expand Up @@ -804,7 +804,7 @@ export default function McpAuthModal(props: McpAuthModalProps) {
</div>
<div class="rounded-xl border border-gray-6/70 bg-gray-2/40 px-3 py-2 flex items-center gap-3">
<div class="flex-1 min-w-0">
<div class="text-[10px] uppercase tracking-wide text-gray-8">{translate("mcp.auth.authorization_link")}</div>
<div class="text-[10px] uppercase tracking-wide text-gray-8">Authorization link</div>
<div class="text-[11px] text-gray-11 font-mono truncate">
{authorizationUrl()}
</div>
Expand All @@ -814,7 +814,7 @@ export default function McpAuthModal(props: McpAuthModalProps) {
class="text-xs"
onClick={handleCopyAuthorizationUrl}
>
{authUrlCopied() ? translate("mcp.auth.copied") : translate("mcp.auth.copy_link")}
{authUrlCopied() ? "Copied" : "Copy link"}
</Button>
</div>
<TextInput
Expand Down Expand Up @@ -851,7 +851,7 @@ export default function McpAuthModal(props: McpAuthModalProps) {
1
</div>
<div>
<p class="text-sm font-medium text-gray-12">{translate("mcp.auth.step1_title")}</p>
<p class="text-sm font-medium text-gray-12">Opening your browser</p>
<p class="text-xs text-gray-10 mt-1">
{translate("mcp.auth.step1_description", { server: serverName() })}
</p>
Expand All @@ -863,7 +863,7 @@ export default function McpAuthModal(props: McpAuthModalProps) {
2
</div>
<div>
<p class="text-sm font-medium text-gray-12">{translate("mcp.auth.step2_title")}</p>
<p class="text-sm font-medium text-gray-12">Authorize OpenWork</p>
<p class="text-xs text-gray-10 mt-1">
{translate("mcp.auth.step2_description")}
</p>
Expand All @@ -875,7 +875,7 @@ export default function McpAuthModal(props: McpAuthModalProps) {
3
</div>
<div>
<p class="text-sm font-medium text-gray-12">{translate("mcp.auth.step3_title")}</p>
<p class="text-sm font-medium text-gray-12">Return here when you're done</p>
<p class="text-xs text-gray-10 mt-1">
{translate("mcp.auth.step3_description")}
</p>
Expand Down
15 changes: 7 additions & 8 deletions apps/app/src/app/components/question-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import type { QuestionInfo } from "@opencode-ai/sdk/v2/client";
import { Check, ChevronRight, HelpCircle } from "lucide-solid";

import Button from "./button";
import { t } from "../../i18n";

export type QuestionModalProps = {
open: boolean;
Expand Down Expand Up @@ -139,10 +138,10 @@ export default function QuestionModal(props: QuestionModalProps) {
</div>
<div>
<h3 class="text-lg font-semibold text-gray-12">
{currentQuestion()!.header || t("common.question")}
{currentQuestion()!.header || "Question"}
</h3>
<div class="text-xs text-gray-11 font-medium">
{t("question_modal.question_counter", undefined, { current: currentIndex() + 1, total: props.questions.length })}
Question {currentIndex() + 1} of {props.questions.length}
</div>
</div>
</div>
Expand Down Expand Up @@ -187,14 +186,14 @@ export default function QuestionModal(props: QuestionModalProps) {
<Show when={currentQuestion()!.custom}>
<div class="mt-4 pt-4 border-t border-dls-border">
<label class="block text-xs font-semibold text-dls-secondary mb-2 uppercase tracking-wide">
{t("question_modal.custom_answer_label")}
Or type a custom answer
</label>
<input
type="text"
value={customInput()}
onInput={(e) => setCustomInput(e.currentTarget.value)}
class="w-full px-4 py-3 rounded-xl bg-dls-surface border border-dls-border focus:border-dls-accent focus:ring-4 focus:ring-[rgba(var(--dls-accent-rgb),0.2)] focus:outline-none text-sm text-dls-text placeholder:text-dls-secondary transition-shadow"
placeholder={t("question_modal.custom_answer_placeholder")}
placeholder="Type your answer here..."
onKeyDown={(e) => {
if (e.key === "Enter") {
if (e.isComposing || e.keyCode === 229) return;
Expand All @@ -210,15 +209,15 @@ export default function QuestionModal(props: QuestionModalProps) {
<div class="p-6 border-t border-dls-border bg-dls-hover flex justify-between items-center">
<div class="text-xs text-dls-secondary flex items-center gap-2">
<span class="px-1.5 py-0.5 rounded border border-dls-border bg-dls-active font-mono">↑↓</span>
<span>{t("common.navigate")}</span>
<span>navigate</span>
<span class="px-1.5 py-0.5 rounded border border-gray-6 bg-gray-3 font-mono ml-2">↵</span>
<span>{t("common.select")}</span>
<span>select</span>
</div>

<div class="flex gap-2">
<Show when={currentQuestion()?.multiple || currentQuestion()?.custom}>
<Button onClick={handleNext} disabled={!canProceed() || props.busy} class="!px-6">
{isLastQuestion() ? t("common.submit") : t("common.next")}
{isLastQuestion() ? "Submit" : "Next"}
<Show when={!isLastQuestion()}>
<ChevronRight size={16} class="ml-1 -mr-1 opacity-60" />
</Show>
Expand Down
58 changes: 29 additions & 29 deletions apps/app/src/app/components/session/composer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import ComposerNotice, { type ComposerNotice as ComposerNoticeData } from "./com

import type { ComposerAttachment, ComposerDraft, ComposerPart, PromptMode, SlashCommandOption } from "../../types";
import { perfNow, recordPerfLog } from "../../lib/perf-log";
import { t } from "../../../i18n";

type MentionOption = {
id: string;
Expand Down Expand Up @@ -517,7 +516,7 @@ export default function Composer(props: ComposerProps) {
span.dataset.pasteId = part.id;
span.dataset.pasteLabel = part.label;
span.dataset.pasteLines = String(part.lines);
span.title = t("composer.expand_pasted");
span.title = "Click to expand pasted text";
span.className =
"inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-semibold bg-gray-3 text-gray-10 border border-gray-6 cursor-pointer hover:bg-gray-4 hover:text-gray-11";
return span;
Expand Down Expand Up @@ -1059,7 +1058,7 @@ export default function Composer(props: ComposerProps) {
const addAttachments = async (files: File[]) => {
if (attachmentsDisabled()) {
props.onNotice({
title: props.attachmentsDisabledReason ?? t("composer.attachments_unavailable"),
title: props.attachmentsDisabledReason ?? "Attachments are unavailable.",
tone: "warning",
});
return;
Expand All @@ -1079,7 +1078,7 @@ export default function Composer(props: ComposerProps) {
for (const file of supportedFiles) {
if (file.size > MAX_ATTACHMENT_BYTES) {
props.onNotice({
title: t("composer.file_exceeds_limit", undefined, { name: file.name }),
title: `${file.name} exceeds the 8MB limit.`,
tone: "warning",
});
continue;
Expand All @@ -1090,7 +1089,7 @@ export default function Composer(props: ComposerProps) {
const estimatedJsonBytes = estimateInlineAttachmentBytes(processed);
if (estimatedJsonBytes > MAX_ATTACHMENT_BYTES) {
props.onNotice({
title: t("composer.file_too_large_encoding", undefined, { name: file.name }),
title: `${file.name} is too large after encoding. Try a smaller image.`,
tone: "warning",
});
continue;
Expand All @@ -1106,7 +1105,7 @@ export default function Composer(props: ComposerProps) {
});
} catch (error) {
props.onNotice({
title: error instanceof Error ? error.message : t("composer.failed_read_attachment"),
title: error instanceof Error ? error.message : "Failed to read attachment",
tone: "error",
});
}
Expand Down Expand Up @@ -1222,29 +1221,29 @@ export default function Composer(props: ComposerProps) {
props.onNotice({
title:
links.length === 1
? t("composer.uploaded_single_file", undefined, { name: links[0].name })
: t("composer.uploaded_multiple_files", undefined, { count: links.length }),
? `Uploaded ${links[0].name} to the shared folder and inserted a link.`
: `Uploaded ${links.length} files to the shared folder and inserted links.`,
tone: "success",
});
return;
}
}
props.onNotice({
title: t("composer.upload_failed_local_links"),
title: "Couldn't upload to the shared folder. Inserted local links instead.",
tone: "warning",
});
}

const text = formatLinks(fallbackLinks());
if (!text) {
props.onNotice({ title: t("composer.unsupported_attachment_type"), tone: "warning" });
props.onNotice({ title: "Unsupported attachment type.", tone: "warning" });
return;
}
insertPlainTextAtSelection(text);
updateMentionQuery();
updateSlashQuery();
emitDraftChange();
props.onNotice({ title: t("composer.inserted_links_unsupported"), tone: "info" });
props.onNotice({ title: "Inserted links for unsupported files.", tone: "info" });
};

const handlePaste = (event: ClipboardEvent) => {
Expand Down Expand Up @@ -1278,9 +1277,10 @@ export default function Composer(props: ComposerProps) {
const hasAbsoluteWindows = /(^|\s)[a-zA-Z]:\\/.test(trimmedForCheck);
if (hasFileUrl || hasAbsolutePosix || hasAbsoluteWindows) {
props.onNotice({
title: t("composer.remote_worker_paste_warning"),
title:
"This is a remote worker. Sandboxes are remote too. To share files with it, upload them to the Shared folder in the sidebar.",
tone: "warning",
actionLabel: props.onUploadInboxFiles ? t("composer.upload_to_shared_folder") : undefined,
actionLabel: props.onUploadInboxFiles ? "Upload to shared folder" : undefined,
onAction: props.onUploadInboxFiles ? () => inboxFileInputRef?.click() : undefined,
});
}
Expand Down Expand Up @@ -1572,7 +1572,7 @@ export default function Composer(props: ComposerProps) {
<div class="max-h-64 overflow-y-auto bg-dls-surface p-2" onMouseDown={(event: MouseEvent) => event.preventDefault()}>
<Show
when={mentionVisible().length}
fallback={<div class="px-3 py-2 text-xs text-gray-10">{t("composer.no_matches")}</div>}
fallback={<div class="px-3 py-2 text-xs text-gray-10">No matches found.</div>}
>
<For each={mentionVisible()}>
{(option: MentionOption) => {
Expand Down Expand Up @@ -1635,7 +1635,7 @@ export default function Composer(props: ComposerProps) {
when={slashFiltered().length}
fallback={
<div class="px-3 py-2 text-xs text-gray-10">
{slashLoading() ? t("composer.loading_commands") : t("composer.no_commands")}
{slashLoading() ? "Loading commands..." : "No commands found."}
</div>
}
>
Expand All @@ -1662,7 +1662,7 @@ export default function Composer(props: ComposerProps) {
</div>
<Show when={cmd.source && cmd.source !== "command"}>
<span class="text-[10px] uppercase tracking-wider text-gray-10 shrink-0">
{cmd.source === "skill" ? t("composer.skill_source") : cmd.source === "mcp" ? "MCP" : ""}
{cmd.source === "skill" ? "Skill" : cmd.source === "mcp" ? "MCP" : ""}
</span>
</Show>
</button>
Expand Down Expand Up @@ -1697,7 +1697,7 @@ export default function Composer(props: ComposerProps) {
<div class="max-w-[160px]">
<div class="truncate text-gray-11">{attachment.name}</div>
<div class="text-[10px] text-gray-10">
{attachment.kind === "image" ? t("composer.image_kind") : attachment.mimeType || t("composer.file_kind")}
{attachment.kind === "image" ? "Image" : attachment.mimeType || "File"}
</div>
</div>
<button
Expand All @@ -1721,7 +1721,7 @@ export default function Composer(props: ComposerProps) {
<div class="relative">
<Show when={!hasDraftContent()}>
<div class="absolute left-0 top-0 text-gray-9 text-[15px] leading-relaxed pointer-events-none">
{t("composer.placeholder")}
Describe your task...
</div>
</Show>
<div
Expand Down Expand Up @@ -1775,8 +1775,8 @@ export default function Composer(props: ComposerProps) {
disabled={attachmentsDisabled()}
title={
attachmentsDisabled()
? props.attachmentsDisabledReason ?? t("composer.attachments_unavailable")
: t("composer.attach_files")
? props.attachmentsDisabledReason ?? "Attachments are unavailable."
: "Attach files"
}
>
<Paperclip size={16} />
Expand All @@ -1794,21 +1794,21 @@ export default function Composer(props: ComposerProps) {
? "bg-gray-4 text-gray-10"
: "bg-dls-accent text-white hover:bg-[var(--dls-accent-hover)]"
}`}
title={t("composer.run_task")}
title="Run task"
>
<ArrowUp size={15} />
<span>{t("composer.run_task")}</span>
<span>Run task</span>
</button>
}
>
<button
type="button"
onClick={() => props.onStop()}
class="inline-flex items-center gap-2 rounded-full bg-gray-12 px-4 py-2 text-[13px] font-medium text-gray-1 transition-colors hover:bg-gray-11"
title={t("composer.stop")}
title="Stop"
>
<Square size={12} fill="currentColor" />
<span>{t("composer.stop")}</span>
<span>Stop</span>
</button>
</Show>
</div>
Expand All @@ -1829,7 +1829,7 @@ export default function Composer(props: ComposerProps) {
onClick={props.onToggleAgentPicker}
disabled={props.busy}
aria-expanded={props.agentPickerOpen}
title={t("composer.agent_label")}
title="Agent"
>
<span class="max-w-[140px] truncate">{props.agentLabel}</span>
<ChevronDown size={13} />
Expand All @@ -1838,14 +1838,14 @@ export default function Composer(props: ComposerProps) {
<Show when={props.agentPickerOpen}>
<div class="absolute left-0 bottom-full z-40 mb-2 w-64 overflow-hidden rounded-[18px] border border-dls-border bg-dls-surface shadow-[var(--dls-shell-shadow)]">
<div class="border-b border-dls-border px-3 pt-2 pb-1 text-[10px] font-semibold uppercase tracking-[0.2em] text-gray-10">
{t("composer.agent_label")}
Agent
</div>

<div class="p-2 space-y-1 max-h-64 overflow-y-auto" onMouseDown={(event: MouseEvent) => event.preventDefault()}>
<Show
when={!props.agentPickerBusy}
fallback={
<div class="px-3 py-2 text-xs text-gray-10">{t("composer.loading_agents")}</div>
<div class="px-3 py-2 text-xs text-gray-10">Loading agents...</div>
}
>
<Show when={!props.agentPickerError}>
Expand All @@ -1860,7 +1860,7 @@ export default function Composer(props: ComposerProps) {
props.onSelectAgent(null);
}}
>
<span>{t("composer.default_agent")}</span>
<span>Default agent</span>
<Show when={!props.selectedAgent}>
<Check size={14} class="text-gray-10" />
</Show>
Expand Down Expand Up @@ -1931,7 +1931,7 @@ export default function Composer(props: ComposerProps) {
<Show when={variantMenuOpen()}>
<div class="absolute left-0 bottom-full z-40 mb-2 w-48 overflow-hidden rounded-[18px] border border-dls-border bg-dls-surface shadow-[var(--dls-shell-shadow)]">
<div class="border-b border-dls-border px-3 pt-2 pb-1 text-[10px] font-semibold uppercase tracking-[0.2em] text-gray-10">
{t("composer.behavior_label")}
Behavior
</div>
<div class="p-2 space-y-1">
<For each={props.modelBehaviorOptions}>
Expand Down
Loading
Loading