mirror of
https://github.com/anomalyco/opencode.git
synced 2026-03-13 10:14:26 +00:00
Compare commits
22 Commits
move-statu
...
effect-aut
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87a6c54226 | ||
|
|
d9dd33aeeb | ||
|
|
0a281c7390 | ||
|
|
3016efba47 | ||
|
|
3998df8112 | ||
|
|
7066e2a25e | ||
|
|
c173988aaa | ||
|
|
268855dc5a | ||
|
|
bfb736e94a | ||
|
|
df8464f89c | ||
|
|
3ea387f364 | ||
|
|
9d3c42c8c4 | ||
|
|
f2cad046e6 | ||
|
|
d722026a8d | ||
|
|
42a5af6c8f | ||
|
|
f0542fae7a | ||
|
|
02c75821a8 | ||
|
|
1739817ee7 | ||
|
|
11e2c85336 | ||
|
|
201e80956a | ||
|
|
7f12976ea0 | ||
|
|
f7259617e5 |
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -8,7 +8,9 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
# Keep every run on dev so cancelled checks do not pollute the default branch
|
||||
# commit history. PRs and other branches still share a group and cancel stale runs.
|
||||
group: ${{ case(github.ref == 'refs/heads/dev', format('{0}-{1}', github.workflow, github.run_id), format('{0}-{1}', github.workflow, github.event.pull_request.number || github.ref)) }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
|
||||
32
bun.lock
32
bun.lock
@@ -26,7 +26,7 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -77,7 +77,7 @@
|
||||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
@@ -111,7 +111,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -138,7 +138,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "2.0.0",
|
||||
"@ai-sdk/openai": "2.0.2",
|
||||
@@ -162,7 +162,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -186,7 +186,7 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"dependencies": {
|
||||
"@opencode-ai/app": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
@@ -219,7 +219,7 @@
|
||||
},
|
||||
"packages/desktop-electron": {
|
||||
"name": "@opencode-ai/desktop-electron",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"dependencies": {
|
||||
"@opencode-ai/app": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
@@ -250,7 +250,7 @@
|
||||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"dependencies": {
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
"@opencode-ai/util": "workspace:*",
|
||||
@@ -279,7 +279,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "catalog:",
|
||||
@@ -295,7 +295,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -416,7 +416,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"zod": "catalog:",
|
||||
@@ -440,7 +440,7 @@
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"devDependencies": {
|
||||
"@hey-api/openapi-ts": "0.90.10",
|
||||
"@tsconfig/node22": "catalog:",
|
||||
@@ -451,7 +451,7 @@
|
||||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
@@ -486,7 +486,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -532,7 +532,7 @@
|
||||
},
|
||||
"packages/util": {
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"dependencies": {
|
||||
"zod": "catalog:",
|
||||
},
|
||||
@@ -543,7 +543,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
@@ -6,6 +6,7 @@ const serverHost = process.env.PLAYWRIGHT_SERVER_HOST ?? "127.0.0.1"
|
||||
const serverPort = process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"
|
||||
const command = `bun run dev -- --host 0.0.0.0 --port ${port}`
|
||||
const reuse = !process.env.CI
|
||||
const workers = Number(process.env.PLAYWRIGHT_WORKERS ?? (process.env.CI ? 5 : 0)) || undefined
|
||||
|
||||
export default defineConfig({
|
||||
testDir: "./e2e",
|
||||
@@ -17,6 +18,7 @@ export default defineConfig({
|
||||
fullyParallel: process.env.PLAYWRIGHT_FULLY_PARALLEL === "1",
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
workers,
|
||||
reporter: [["html", { outputFolder: "e2e/playwright-report", open: "never" }], ["line"]],
|
||||
webServer: {
|
||||
command,
|
||||
|
||||
@@ -48,7 +48,7 @@ import {
|
||||
type PromptHistoryStoredEntry,
|
||||
promptLength,
|
||||
} from "./prompt-input/history"
|
||||
import { createPromptSubmit } from "./prompt-input/submit"
|
||||
import { createPromptSubmit, type FollowupDraft } from "./prompt-input/submit"
|
||||
import { PromptPopover, type AtOption, type SlashCommand } from "./prompt-input/slash-popover"
|
||||
import { PromptContextItems } from "./prompt-input/context-items"
|
||||
import { PromptImageAttachments } from "./prompt-input/image-attachments"
|
||||
@@ -61,6 +61,11 @@ interface PromptInputProps {
|
||||
ref?: (el: HTMLDivElement) => void
|
||||
newSessionWorktree?: string
|
||||
onNewSessionWorktreeReset?: () => void
|
||||
edit?: { id: string; prompt: Prompt; context: FollowupDraft["context"] }
|
||||
onEditLoaded?: () => void
|
||||
shouldQueue?: () => boolean
|
||||
onQueue?: (draft: FollowupDraft) => void
|
||||
onAbort?: () => void
|
||||
onSubmit?: () => void
|
||||
}
|
||||
|
||||
@@ -947,6 +952,45 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
setCurrentHistory("entries", next)
|
||||
}
|
||||
|
||||
createEffect(
|
||||
on(
|
||||
() => props.edit?.id,
|
||||
(id) => {
|
||||
const edit = props.edit
|
||||
if (!id || !edit) return
|
||||
|
||||
for (const item of prompt.context.items()) {
|
||||
prompt.context.remove(item.key)
|
||||
}
|
||||
|
||||
for (const item of edit.context) {
|
||||
prompt.context.add({
|
||||
type: item.type,
|
||||
path: item.path,
|
||||
selection: item.selection,
|
||||
comment: item.comment,
|
||||
commentID: item.commentID,
|
||||
commentOrigin: item.commentOrigin,
|
||||
preview: item.preview,
|
||||
})
|
||||
}
|
||||
|
||||
setStore("mode", "normal")
|
||||
setStore("popover", null)
|
||||
setStore("historyIndex", -1)
|
||||
setStore("savedPrompt", null)
|
||||
prompt.set(edit.prompt, promptLength(edit.prompt))
|
||||
requestAnimationFrame(() => {
|
||||
editorRef.focus()
|
||||
setCursorPosition(editorRef, promptLength(edit.prompt))
|
||||
queueScroll()
|
||||
})
|
||||
props.onEditLoaded?.()
|
||||
},
|
||||
{ defer: true },
|
||||
),
|
||||
)
|
||||
|
||||
const navigateHistory = (direction: "up" | "down") => {
|
||||
const result = navigatePromptHistory({
|
||||
direction,
|
||||
@@ -1001,6 +1045,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
setPopover: (popover) => setStore("popover", popover),
|
||||
newSessionWorktree: () => props.newSessionWorktree,
|
||||
onNewSessionWorktreeReset: props.onNewSessionWorktreeReset,
|
||||
shouldQueue: props.shouldQueue,
|
||||
onQueue: props.onQueue,
|
||||
onAbort: props.onAbort,
|
||||
onSubmit: props.onSubmit,
|
||||
})
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useLanguage } from "@/context/language"
|
||||
import { useLayout } from "@/context/layout"
|
||||
import { useLocal } from "@/context/local"
|
||||
import { usePermission } from "@/context/permission"
|
||||
import { type ImageAttachmentPart, type Prompt, usePrompt } from "@/context/prompt"
|
||||
import { type ContextItem, type ImageAttachmentPart, type Prompt, usePrompt } from "@/context/prompt"
|
||||
import { useSDK } from "@/context/sdk"
|
||||
import { useSync } from "@/context/sync"
|
||||
import { Identifier } from "@/utils/id"
|
||||
@@ -25,6 +25,145 @@ type PendingPrompt = {
|
||||
|
||||
const pending = new Map<string, PendingPrompt>()
|
||||
|
||||
export type FollowupDraft = {
|
||||
sessionID: string
|
||||
sessionDirectory: string
|
||||
prompt: Prompt
|
||||
context: (ContextItem & { key: string })[]
|
||||
agent: string
|
||||
model: { providerID: string; modelID: string }
|
||||
variant?: string
|
||||
}
|
||||
|
||||
type FollowupSendInput = {
|
||||
client: ReturnType<typeof useSDK>["client"]
|
||||
globalSync: ReturnType<typeof useGlobalSync>
|
||||
sync: ReturnType<typeof useSync>
|
||||
draft: FollowupDraft
|
||||
messageID?: string
|
||||
optimisticBusy?: boolean
|
||||
before?: () => Promise<boolean> | boolean
|
||||
}
|
||||
|
||||
const draftText = (prompt: Prompt) => prompt.map((part) => ("content" in part ? part.content : "")).join("")
|
||||
|
||||
const draftImages = (prompt: Prompt) => prompt.filter((part): part is ImageAttachmentPart => part.type === "image")
|
||||
|
||||
export async function sendFollowupDraft(input: FollowupSendInput) {
|
||||
const text = draftText(input.draft.prompt)
|
||||
const images = draftImages(input.draft.prompt)
|
||||
const [, setStore] = input.globalSync.child(input.draft.sessionDirectory)
|
||||
|
||||
const setBusy = () => {
|
||||
if (!input.optimisticBusy) return
|
||||
setStore("session_status", input.draft.sessionID, { type: "busy" })
|
||||
}
|
||||
|
||||
const setIdle = () => {
|
||||
if (!input.optimisticBusy) return
|
||||
setStore("session_status", input.draft.sessionID, { type: "idle" })
|
||||
}
|
||||
|
||||
const wait = async () => {
|
||||
const ok = await input.before?.()
|
||||
if (ok === false) return false
|
||||
return true
|
||||
}
|
||||
|
||||
const [head, ...tail] = text.split(" ")
|
||||
const cmd = head?.startsWith("/") ? head.slice(1) : undefined
|
||||
if (cmd && input.sync.data.command.find((item) => item.name === cmd)) {
|
||||
setBusy()
|
||||
try {
|
||||
if (!(await wait())) {
|
||||
setIdle()
|
||||
return false
|
||||
}
|
||||
|
||||
await input.client.session.command({
|
||||
sessionID: input.draft.sessionID,
|
||||
command: cmd,
|
||||
arguments: tail.join(" "),
|
||||
agent: input.draft.agent,
|
||||
model: `${input.draft.model.providerID}/${input.draft.model.modelID}`,
|
||||
variant: input.draft.variant,
|
||||
parts: images.map((attachment) => ({
|
||||
id: Identifier.ascending("part"),
|
||||
type: "file" as const,
|
||||
mime: attachment.mime,
|
||||
url: attachment.dataUrl,
|
||||
filename: attachment.filename,
|
||||
})),
|
||||
})
|
||||
return true
|
||||
} catch (err) {
|
||||
setIdle()
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
const messageID = input.messageID ?? Identifier.ascending("message")
|
||||
const { requestParts, optimisticParts } = buildRequestParts({
|
||||
prompt: input.draft.prompt,
|
||||
context: input.draft.context,
|
||||
images,
|
||||
text,
|
||||
sessionID: input.draft.sessionID,
|
||||
messageID,
|
||||
sessionDirectory: input.draft.sessionDirectory,
|
||||
})
|
||||
|
||||
const message: Message = {
|
||||
id: messageID,
|
||||
sessionID: input.draft.sessionID,
|
||||
role: "user",
|
||||
time: { created: Date.now() },
|
||||
agent: input.draft.agent,
|
||||
model: input.draft.model,
|
||||
variant: input.draft.variant,
|
||||
}
|
||||
|
||||
const add = () =>
|
||||
input.sync.session.optimistic.add({
|
||||
directory: input.draft.sessionDirectory,
|
||||
sessionID: input.draft.sessionID,
|
||||
message,
|
||||
parts: optimisticParts,
|
||||
})
|
||||
|
||||
const remove = () =>
|
||||
input.sync.session.optimistic.remove({
|
||||
directory: input.draft.sessionDirectory,
|
||||
sessionID: input.draft.sessionID,
|
||||
messageID,
|
||||
})
|
||||
|
||||
setBusy()
|
||||
add()
|
||||
|
||||
try {
|
||||
if (!(await wait())) {
|
||||
setIdle()
|
||||
remove()
|
||||
return false
|
||||
}
|
||||
|
||||
await input.client.session.promptAsync({
|
||||
sessionID: input.draft.sessionID,
|
||||
agent: input.draft.agent,
|
||||
model: input.draft.model,
|
||||
messageID,
|
||||
parts: requestParts,
|
||||
variant: input.draft.variant,
|
||||
})
|
||||
return true
|
||||
} catch (err) {
|
||||
setIdle()
|
||||
remove()
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
type PromptSubmitInput = {
|
||||
info: Accessor<{ id: string } | undefined>
|
||||
imageAttachments: Accessor<ImageAttachmentPart[]>
|
||||
@@ -41,6 +180,9 @@ type PromptSubmitInput = {
|
||||
setPopover: (popover: "at" | "slash" | null) => void
|
||||
newSessionWorktree?: Accessor<string | undefined>
|
||||
onNewSessionWorktreeReset?: () => void
|
||||
shouldQueue?: Accessor<boolean>
|
||||
onQueue?: (draft: FollowupDraft) => void
|
||||
onAbort?: () => void
|
||||
onSubmit?: () => void
|
||||
}
|
||||
|
||||
@@ -82,6 +224,8 @@ export function createPromptSubmit(input: PromptSubmitInput) {
|
||||
const [, setStore] = globalSync.child(sdk.directory)
|
||||
setStore("todo", sessionID, [])
|
||||
|
||||
input.onAbort?.()
|
||||
|
||||
const queued = pending.get(sessionID)
|
||||
if (queued) {
|
||||
queued.abort.abort()
|
||||
@@ -116,6 +260,12 @@ export function createPromptSubmit(input: PromptSubmitInput) {
|
||||
}
|
||||
}
|
||||
|
||||
const clearContext = () => {
|
||||
for (const item of prompt.context.items()) {
|
||||
prompt.context.remove(item.key)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async (event: Event) => {
|
||||
event.preventDefault()
|
||||
|
||||
@@ -215,14 +365,22 @@ export function createPromptSubmit(input: PromptSubmitInput) {
|
||||
return
|
||||
}
|
||||
|
||||
input.onSubmit?.()
|
||||
|
||||
const model = {
|
||||
modelID: currentModel.id,
|
||||
providerID: currentModel.provider.id,
|
||||
}
|
||||
const agent = currentAgent.name
|
||||
const variant = local.model.variant.current()
|
||||
const context = prompt.context.items().slice()
|
||||
const draft: FollowupDraft = {
|
||||
sessionID: session.id,
|
||||
sessionDirectory,
|
||||
prompt: currentPrompt,
|
||||
context,
|
||||
agent,
|
||||
model,
|
||||
variant,
|
||||
}
|
||||
|
||||
const clearInput = () => {
|
||||
prompt.reset()
|
||||
@@ -243,6 +401,15 @@ export function createPromptSubmit(input: PromptSubmitInput) {
|
||||
})
|
||||
}
|
||||
|
||||
if (!isNewSession && mode === "normal" && input.shouldQueue?.()) {
|
||||
input.onQueue?.(draft)
|
||||
clearContext()
|
||||
clearInput()
|
||||
return
|
||||
}
|
||||
|
||||
input.onSubmit?.()
|
||||
|
||||
if (mode === "shell") {
|
||||
clearInput()
|
||||
client.session
|
||||
@@ -295,48 +462,19 @@ export function createPromptSubmit(input: PromptSubmitInput) {
|
||||
}
|
||||
}
|
||||
|
||||
const context = prompt.context.items().slice()
|
||||
const commentItems = context.filter((item) => item.type === "file" && !!item.comment?.trim())
|
||||
|
||||
const messageID = Identifier.ascending("message")
|
||||
const { requestParts, optimisticParts } = buildRequestParts({
|
||||
prompt: currentPrompt,
|
||||
context,
|
||||
images,
|
||||
text,
|
||||
sessionID: session.id,
|
||||
messageID,
|
||||
sessionDirectory,
|
||||
})
|
||||
|
||||
const optimisticMessage: Message = {
|
||||
id: messageID,
|
||||
sessionID: session.id,
|
||||
role: "user",
|
||||
time: { created: Date.now() },
|
||||
agent,
|
||||
model,
|
||||
variant,
|
||||
}
|
||||
|
||||
const addOptimisticMessage = () =>
|
||||
sync.session.optimistic.add({
|
||||
directory: sessionDirectory,
|
||||
sessionID: session.id,
|
||||
message: optimisticMessage,
|
||||
parts: optimisticParts,
|
||||
})
|
||||
|
||||
const removeOptimisticMessage = () =>
|
||||
const removeOptimisticMessage = () => {
|
||||
sync.session.optimistic.remove({
|
||||
directory: sessionDirectory,
|
||||
sessionID: session.id,
|
||||
messageID,
|
||||
})
|
||||
}
|
||||
|
||||
removeCommentItems(commentItems)
|
||||
clearInput()
|
||||
addOptimisticMessage()
|
||||
|
||||
const waitForWorktree = async () => {
|
||||
const worktree = WorktreeState.get(sessionDirectory)
|
||||
@@ -393,20 +531,15 @@ export function createPromptSubmit(input: PromptSubmitInput) {
|
||||
return true
|
||||
}
|
||||
|
||||
const send = async () => {
|
||||
const ok = await waitForWorktree()
|
||||
if (!ok) return
|
||||
await client.session.promptAsync({
|
||||
sessionID: session.id,
|
||||
agent,
|
||||
model,
|
||||
messageID,
|
||||
parts: requestParts,
|
||||
variant,
|
||||
})
|
||||
}
|
||||
|
||||
void send().catch((err) => {
|
||||
void sendFollowupDraft({
|
||||
client,
|
||||
sync,
|
||||
globalSync,
|
||||
draft,
|
||||
messageID,
|
||||
optimisticBusy: sessionDirectory === projectDirectory,
|
||||
before: waitForWorktree,
|
||||
}).catch((err) => {
|
||||
pending.delete(session.id)
|
||||
if (sessionDirectory === projectDirectory) {
|
||||
sync.set("session_status", session.id, { type: "idle" })
|
||||
|
||||
@@ -113,6 +113,11 @@ export const SettingsGeneral: Component = () => {
|
||||
{ value: "dark", label: language.t("theme.scheme.dark") },
|
||||
])
|
||||
|
||||
const followupOptions = createMemo((): { value: "queue" | "steer"; label: string }[] => [
|
||||
{ value: "queue", label: language.t("settings.general.row.followup.option.queue") },
|
||||
{ value: "steer", label: language.t("settings.general.row.followup.option.steer") },
|
||||
])
|
||||
|
||||
const languageOptions = createMemo(() =>
|
||||
language.locales.map((locale) => ({
|
||||
value: locale,
|
||||
@@ -170,10 +175,8 @@ export const SettingsGeneral: Component = () => {
|
||||
triggerVariant: "settings" as const,
|
||||
})
|
||||
|
||||
const AppearanceSection = () => (
|
||||
const GeneralSection = () => (
|
||||
<div class="flex flex-col gap-1">
|
||||
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.appearance")}</h3>
|
||||
|
||||
<div class="bg-surface-raised-base px-4 rounded-lg">
|
||||
<SettingsRow
|
||||
title={language.t("settings.general.row.language.title")}
|
||||
@@ -193,8 +196,70 @@ export const SettingsGeneral: Component = () => {
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title={language.t("settings.general.row.appearance.title")}
|
||||
description={language.t("settings.general.row.appearance.description")}
|
||||
title={language.t("settings.general.row.reasoningSummaries.title")}
|
||||
description={language.t("settings.general.row.reasoningSummaries.description")}
|
||||
>
|
||||
<div data-action="settings-feed-reasoning-summaries">
|
||||
<Switch
|
||||
checked={settings.general.showReasoningSummaries()}
|
||||
onChange={(checked) => settings.general.setShowReasoningSummaries(checked)}
|
||||
/>
|
||||
</div>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title={language.t("settings.general.row.shellToolPartsExpanded.title")}
|
||||
description={language.t("settings.general.row.shellToolPartsExpanded.description")}
|
||||
>
|
||||
<div data-action="settings-feed-shell-tool-parts-expanded">
|
||||
<Switch
|
||||
checked={settings.general.shellToolPartsExpanded()}
|
||||
onChange={(checked) => settings.general.setShellToolPartsExpanded(checked)}
|
||||
/>
|
||||
</div>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title={language.t("settings.general.row.editToolPartsExpanded.title")}
|
||||
description={language.t("settings.general.row.editToolPartsExpanded.description")}
|
||||
>
|
||||
<div data-action="settings-feed-edit-tool-parts-expanded">
|
||||
<Switch
|
||||
checked={settings.general.editToolPartsExpanded()}
|
||||
onChange={(checked) => settings.general.setEditToolPartsExpanded(checked)}
|
||||
/>
|
||||
</div>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title={language.t("settings.general.row.followup.title")}
|
||||
description={language.t("settings.general.row.followup.description")}
|
||||
>
|
||||
<Select
|
||||
data-action="settings-followup"
|
||||
options={followupOptions()}
|
||||
current={followupOptions().find((o) => o.value === settings.general.followup())}
|
||||
value={(o) => o.value}
|
||||
label={(o) => o.label}
|
||||
onSelect={(option) => option && settings.general.setFollowup(option.value)}
|
||||
variant="secondary"
|
||||
size="small"
|
||||
triggerVariant="settings"
|
||||
triggerStyle={{ "min-width": "180px" }}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const AppearanceSection = () => (
|
||||
<div class="flex flex-col gap-1">
|
||||
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.appearance")}</h3>
|
||||
|
||||
<div class="bg-surface-raised-base px-4 rounded-lg">
|
||||
<SettingsRow
|
||||
title={language.t("settings.general.row.colorScheme.title")}
|
||||
description={language.t("settings.general.row.colorScheme.description")}
|
||||
>
|
||||
<Select
|
||||
data-action="settings-color-scheme"
|
||||
@@ -211,6 +276,7 @@ export const SettingsGeneral: Component = () => {
|
||||
variant="secondary"
|
||||
size="small"
|
||||
triggerVariant="settings"
|
||||
triggerStyle={{ "min-width": "220px" }}
|
||||
/>
|
||||
</SettingsRow>
|
||||
|
||||
@@ -271,50 +337,6 @@ export const SettingsGeneral: Component = () => {
|
||||
</div>
|
||||
)
|
||||
|
||||
const FeedSection = () => (
|
||||
<div class="flex flex-col gap-1">
|
||||
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.feed")}</h3>
|
||||
|
||||
<div class="bg-surface-raised-base px-4 rounded-lg">
|
||||
<SettingsRow
|
||||
title={language.t("settings.general.row.reasoningSummaries.title")}
|
||||
description={language.t("settings.general.row.reasoningSummaries.description")}
|
||||
>
|
||||
<div data-action="settings-feed-reasoning-summaries">
|
||||
<Switch
|
||||
checked={settings.general.showReasoningSummaries()}
|
||||
onChange={(checked) => settings.general.setShowReasoningSummaries(checked)}
|
||||
/>
|
||||
</div>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title={language.t("settings.general.row.shellToolPartsExpanded.title")}
|
||||
description={language.t("settings.general.row.shellToolPartsExpanded.description")}
|
||||
>
|
||||
<div data-action="settings-feed-shell-tool-parts-expanded">
|
||||
<Switch
|
||||
checked={settings.general.shellToolPartsExpanded()}
|
||||
onChange={(checked) => settings.general.setShellToolPartsExpanded(checked)}
|
||||
/>
|
||||
</div>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title={language.t("settings.general.row.editToolPartsExpanded.title")}
|
||||
description={language.t("settings.general.row.editToolPartsExpanded.description")}
|
||||
>
|
||||
<div data-action="settings-feed-edit-tool-parts-expanded">
|
||||
<Switch
|
||||
checked={settings.general.editToolPartsExpanded()}
|
||||
onChange={(checked) => settings.general.setEditToolPartsExpanded(checked)}
|
||||
/>
|
||||
</div>
|
||||
</SettingsRow>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const NotificationsSection = () => (
|
||||
<div class="flex flex-col gap-1">
|
||||
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.notifications")}</h3>
|
||||
@@ -465,9 +487,9 @@ export const SettingsGeneral: Component = () => {
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-8 w-full">
|
||||
<AppearanceSection />
|
||||
<GeneralSection />
|
||||
|
||||
<FeedSection />
|
||||
<AppearanceSection />
|
||||
|
||||
<NotificationsSection />
|
||||
|
||||
@@ -551,12 +573,12 @@ interface SettingsRowProps {
|
||||
|
||||
const SettingsRow: Component<SettingsRowProps> = (props) => {
|
||||
return (
|
||||
<div class="flex flex-wrap items-center justify-between gap-4 py-3 border-b border-border-weak-base last:border-none">
|
||||
<div class="flex flex-col gap-0.5 min-w-0">
|
||||
<div class="flex flex-wrap items-center gap-4 py-3 border-b border-border-weak-base last:border-none sm:flex-nowrap">
|
||||
<div class="flex min-w-0 flex-1 flex-col gap-0.5">
|
||||
<span class="text-14-medium text-text-strong">{props.title}</span>
|
||||
<span class="text-12-regular text-text-weak">{props.description}</span>
|
||||
</div>
|
||||
<div class="flex-shrink-0">{props.children}</div>
|
||||
<div class="flex w-full justify-end sm:w-auto sm:shrink-0">{props.children}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ export interface Settings {
|
||||
general: {
|
||||
autoSave: boolean
|
||||
releaseNotes: boolean
|
||||
followup: "queue" | "steer"
|
||||
showReasoningSummaries: boolean
|
||||
shellToolPartsExpanded: boolean
|
||||
editToolPartsExpanded: boolean
|
||||
@@ -45,6 +46,7 @@ const defaultSettings: Settings = {
|
||||
general: {
|
||||
autoSave: true,
|
||||
releaseNotes: true,
|
||||
followup: "steer",
|
||||
showReasoningSummaries: false,
|
||||
shellToolPartsExpanded: true,
|
||||
editToolPartsExpanded: false,
|
||||
@@ -126,6 +128,10 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
|
||||
setReleaseNotes(value: boolean) {
|
||||
setStore("general", "releaseNotes", value)
|
||||
},
|
||||
followup: withFallback(() => store.general?.followup, defaultSettings.general.followup),
|
||||
setFollowup(value: "queue" | "steer") {
|
||||
setStore("general", "followup", value)
|
||||
},
|
||||
showReasoningSummaries: withFallback(
|
||||
() => store.general?.showReasoningSummaries,
|
||||
defaultSettings.general.showReasoningSummaries,
|
||||
|
||||
@@ -233,8 +233,15 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
if (!tracked(input.directory, input.sessionID)) return
|
||||
setMeta("loading", key, false)
|
||||
setMeta(
|
||||
produce((draft) => {
|
||||
if (!tracked(input.directory, input.sessionID)) {
|
||||
delete draft.loading[key]
|
||||
return
|
||||
}
|
||||
draft.loading[key] = false
|
||||
}),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ export const dict = {
|
||||
"dialog.model.empty": "لا توجد نتائج للنماذج",
|
||||
"dialog.model.manage": "إدارة النماذج",
|
||||
"dialog.model.manage.description": "تخصيص النماذج التي تظهر في محدد النماذج.",
|
||||
"dialog.model.manage.provider.toggle": "تبديل جميع نماذج {{provider}}",
|
||||
"dialog.model.unpaid.freeModels.title": "نماذج مجانية مقدمة من OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "إضافة المزيد من النماذج من موفرين مشهورين",
|
||||
"dialog.provider.viewAll": "عرض المزيد من الموفرين",
|
||||
@@ -288,6 +289,11 @@ export const dict = {
|
||||
"dialog.server.add.error": "تعذر الاتصال بالخادم",
|
||||
"dialog.server.add.checking": "جارٍ التحقق...",
|
||||
"dialog.server.add.button": "إضافة خادم",
|
||||
"dialog.server.add.name": "اسم الخادم (اختياري)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "اسم المستخدم (اختياري)",
|
||||
"dialog.server.add.password": "كلمة المرور (اختياري)",
|
||||
"dialog.server.edit.title": "تحرير الخادم",
|
||||
"dialog.server.default.title": "الخادم الافتراضي",
|
||||
"dialog.server.default.description":
|
||||
"الاتصال بهذا الخادم عند بدء تشغيل التطبيق بدلاً من بدء خادم محلي. يتطلب إعادة التشغيل.",
|
||||
@@ -358,6 +364,7 @@ export const dict = {
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "لغة",
|
||||
"toast.language.description": "تم التبديل إلى {{language}}",
|
||||
"toast.theme.title": "تم تبديل السمة",
|
||||
@@ -444,8 +451,11 @@ export const dict = {
|
||||
"session.review.loadingChanges": "جارٍ تحميل التغييرات...",
|
||||
"session.review.empty": "لا توجد تغييرات في هذه الجلسة بعد",
|
||||
"session.review.noChanges": "لا توجد تغييرات",
|
||||
"session.review.noVcs": "لم يتم اكتشاف نظام التحكم في الإصدار Git، لن يتم عرض التغييرات",
|
||||
"session.review.noSnapshot": "تم تعطيل تتبع اللقطات في التكوين، لذا فإن تغييرات الجلسة غير متوفرة",
|
||||
"session.files.selectToOpen": "اختر ملفًا لفتحه",
|
||||
"session.files.all": "كل الملفات",
|
||||
"session.files.empty": "لا توجد ملفات",
|
||||
"session.files.binaryContent": "ملف ثنائي (لا يمكن عرض المحتوى)",
|
||||
"session.messages.renderEarlier": "عرض الرسائل السابقة",
|
||||
"session.messages.loadingEarlier": "جارٍ تحميل الرسائل السابقة...",
|
||||
@@ -456,6 +466,17 @@ export const dict = {
|
||||
"session.todo.title": "المهام",
|
||||
"session.todo.collapse": "طي",
|
||||
"session.todo.expand": "توسيع",
|
||||
"session.followupDock.summary.one": "{{count}} رسالة في الانتظار",
|
||||
"session.followupDock.summary.other": "{{count}} رسائل في الانتظار",
|
||||
"session.followupDock.sendNow": "إرسال الآن",
|
||||
"session.followupDock.edit": "تحرير",
|
||||
"session.followupDock.collapse": "طي الرسائل المنتظرة",
|
||||
"session.followupDock.expand": "توسيع الرسائل المنتظرة",
|
||||
"session.revertDock.summary.one": "{{count}} رسالة تم التراجع عنها",
|
||||
"session.revertDock.summary.other": "{{count}} رسائل تم التراجع عنها",
|
||||
"session.revertDock.collapse": "طي الرسائل التي تم التراجع عنها",
|
||||
"session.revertDock.expand": "توسيع الرسائل التي تم التراجع عنها",
|
||||
"session.revertDock.restore": "استعادة الرسالة",
|
||||
"session.new.title": "ابنِ أي شيء",
|
||||
"session.new.worktree.main": "الفرع الرئيسي",
|
||||
"session.new.worktree.mainWithBranch": "الفرع الرئيسي ({{branch}})",
|
||||
@@ -538,10 +559,18 @@ export const dict = {
|
||||
"settings.general.row.language.description": "تغيير لغة العرض لـ OpenCode",
|
||||
"settings.general.row.appearance.title": "المظهر",
|
||||
"settings.general.row.appearance.description": "تخصيص كيفية ظهور OpenCode على جهازك",
|
||||
"settings.general.row.colorScheme.title": "مخطط الألوان",
|
||||
"settings.general.row.colorScheme.description": "اختر ما إذا كان OpenCode يتبع سمة النظام أو الفاتح أو الداكن",
|
||||
"settings.general.row.theme.title": "السمة",
|
||||
"settings.general.row.theme.description": "تخصيص سمة OpenCode.",
|
||||
"settings.general.row.font.title": "الخط",
|
||||
"settings.general.row.font.description": "تخصيص الخط الأحادي المستخدم في كتل التعليمات البرمجية",
|
||||
"settings.general.row.followup.title": "سلوك المتابعة",
|
||||
"settings.general.row.followup.description": "اختر ما إذا كانت طلبات المتابعة توجه فورًا أو تنتظر في قائمة انتظار",
|
||||
"settings.general.row.followup.option.queue": "قائمة انتظار",
|
||||
"settings.general.row.followup.option.steer": "توجيه",
|
||||
"settings.general.row.reasoningSummaries.title": "إظهار ملخصات الاستنتاج",
|
||||
"settings.general.row.reasoningSummaries.description": "عرض ملخصات استنتاج النموذج في الشريط الزمني",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "توسيع أجزاء أداة shell",
|
||||
"settings.general.row.shellToolPartsExpanded.description":
|
||||
"إظهار أجزاء أداة shell موسعة بشكل افتراضي في الشريط الزمني",
|
||||
|
||||
@@ -104,6 +104,7 @@ export const dict = {
|
||||
"dialog.model.empty": "Nenhum resultado de modelo",
|
||||
"dialog.model.manage": "Gerenciar modelos",
|
||||
"dialog.model.manage.description": "Personalizar quais modelos aparecem no seletor de modelos.",
|
||||
"dialog.model.manage.provider.toggle": "Alternar todos os modelos {{provider}}",
|
||||
"dialog.model.unpaid.freeModels.title": "Modelos gratuitos fornecidos pelo OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Adicionar mais modelos de provedores populares",
|
||||
"dialog.provider.viewAll": "Ver mais provedores",
|
||||
@@ -288,6 +289,11 @@ export const dict = {
|
||||
"dialog.server.add.error": "Não foi possível conectar ao servidor",
|
||||
"dialog.server.add.checking": "Verificando...",
|
||||
"dialog.server.add.button": "Adicionar",
|
||||
"dialog.server.add.name": "Nome do servidor (opcional)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "Nome de usuário (opcional)",
|
||||
"dialog.server.add.password": "Senha (opcional)",
|
||||
"dialog.server.edit.title": "Editar servidor",
|
||||
"dialog.server.default.title": "Servidor padrão",
|
||||
"dialog.server.default.description":
|
||||
"Conectar a este servidor na inicialização do aplicativo ao invés de iniciar um servidor local. Requer reinicialização.",
|
||||
@@ -359,6 +365,7 @@ export const dict = {
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "Idioma",
|
||||
"toast.language.description": "Alterado para {{language}}",
|
||||
"toast.theme.title": "Tema alterado",
|
||||
@@ -446,9 +453,13 @@ export const dict = {
|
||||
"session.review.change.other": "Alterações",
|
||||
"session.review.loadingChanges": "Carregando alterações...",
|
||||
"session.review.empty": "Nenhuma alteração nesta sessão ainda",
|
||||
"session.review.noVcs": "Nenhum Sistema de Controle de Versão Git detectado, alterações não exibidas",
|
||||
"session.review.noSnapshot":
|
||||
"O rastreamento de snapshot está desabilitado na configuração, então as alterações da sessão estão indisponíveis",
|
||||
"session.review.noChanges": "Sem alterações",
|
||||
"session.files.selectToOpen": "Selecione um arquivo para abrir",
|
||||
"session.files.all": "Todos os arquivos",
|
||||
"session.files.empty": "Nenhum arquivo",
|
||||
"session.files.binaryContent": "Arquivo binário (conteúdo não pode ser exibido)",
|
||||
"session.messages.renderEarlier": "Renderizar mensagens anteriores",
|
||||
"session.messages.loadingEarlier": "Carregando mensagens anteriores...",
|
||||
@@ -459,6 +470,17 @@ export const dict = {
|
||||
"session.todo.title": "Tarefas",
|
||||
"session.todo.collapse": "Recolher",
|
||||
"session.todo.expand": "Expandir",
|
||||
"session.followupDock.summary.one": "{{count}} mensagem na fila",
|
||||
"session.followupDock.summary.other": "{{count}} mensagens na fila",
|
||||
"session.followupDock.sendNow": "Enviar agora",
|
||||
"session.followupDock.edit": "Editar",
|
||||
"session.followupDock.collapse": "Recolher mensagens na fila",
|
||||
"session.followupDock.expand": "Expandir mensagens na fila",
|
||||
"session.revertDock.summary.one": "{{count}} mensagem revertida",
|
||||
"session.revertDock.summary.other": "{{count}} mensagens revertidas",
|
||||
"session.revertDock.collapse": "Recolher mensagens revertidas",
|
||||
"session.revertDock.expand": "Expandir mensagens revertidas",
|
||||
"session.revertDock.restore": "Restaurar mensagem",
|
||||
"session.new.title": "Crie qualquer coisa",
|
||||
"session.new.worktree.main": "Branch principal",
|
||||
"session.new.worktree.mainWithBranch": "Branch principal ({{branch}})",
|
||||
@@ -544,10 +566,19 @@ export const dict = {
|
||||
"settings.general.row.language.description": "Alterar o idioma de exibição do OpenCode",
|
||||
"settings.general.row.appearance.title": "Aparência",
|
||||
"settings.general.row.appearance.description": "Personalize como o OpenCode aparece no seu dispositivo",
|
||||
"settings.general.row.colorScheme.title": "Esquema de cores",
|
||||
"settings.general.row.colorScheme.description": "Escolha se o OpenCode segue o tema do sistema, claro ou escuro",
|
||||
"settings.general.row.theme.title": "Tema",
|
||||
"settings.general.row.theme.description": "Personalize como o OpenCode é tematizado.",
|
||||
"settings.general.row.font.title": "Fonte",
|
||||
"settings.general.row.font.description": "Personalize a fonte monoespaçada usada em blocos de código",
|
||||
"settings.general.row.followup.title": "Comportamento de acompanhamento",
|
||||
"settings.general.row.followup.description":
|
||||
"Escolha se os prompts de acompanhamento orientam imediatamente ou esperam na fila",
|
||||
"settings.general.row.followup.option.queue": "Fila",
|
||||
"settings.general.row.followup.option.steer": "Orientar",
|
||||
"settings.general.row.reasoningSummaries.title": "Mostrar resumos de raciocínio",
|
||||
"settings.general.row.reasoningSummaries.description": "Exibir resumos de raciocínio do modelo na linha do tempo",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Expandir partes da ferramenta shell",
|
||||
"settings.general.row.shellToolPartsExpanded.description":
|
||||
"Mostrar partes da ferramenta shell expandidas por padrão na linha do tempo",
|
||||
|
||||
@@ -113,6 +113,7 @@ export const dict = {
|
||||
"dialog.model.empty": "Nema rezultata za modele",
|
||||
"dialog.model.manage": "Upravljaj modelima",
|
||||
"dialog.model.manage.description": "Prilagodi koji se modeli prikazuju u izborniku modela.",
|
||||
"dialog.model.manage.provider.toggle": "Uključi/isključi sve {{provider}} modele",
|
||||
|
||||
"dialog.model.unpaid.freeModels.title": "Besplatni modeli koje obezbjeđuje OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Dodaj još modela od popularnih provajdera",
|
||||
@@ -315,6 +316,11 @@ export const dict = {
|
||||
"dialog.server.add.error": "Nije moguće povezati se na server",
|
||||
"dialog.server.add.checking": "Provjera...",
|
||||
"dialog.server.add.button": "Dodaj server",
|
||||
"dialog.server.add.name": "Ime servera (opcionalno)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "Korisničko ime (opcionalno)",
|
||||
"dialog.server.add.password": "Lozinka (opcionalno)",
|
||||
"dialog.server.edit.title": "Uredi server",
|
||||
"dialog.server.default.title": "Podrazumijevani server",
|
||||
"dialog.server.default.description":
|
||||
"Poveži se na ovaj server pri pokretanju aplikacije umjesto pokretanja lokalnog servera. Potreban je restart.",
|
||||
@@ -393,6 +399,7 @@ export const dict = {
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
|
||||
"toast.language.title": "Jezik",
|
||||
"toast.language.description": "Prebačeno na {{language}}",
|
||||
@@ -498,10 +505,14 @@ export const dict = {
|
||||
"session.review.change.other": "Izmjene",
|
||||
"session.review.loadingChanges": "Učitavanje izmjena...",
|
||||
"session.review.empty": "Još nema izmjena u ovoj sesiji",
|
||||
"session.review.noVcs": "Nije detektovan Git sistem kontrole verzija, promjene se ne prikazuju",
|
||||
"session.review.noSnapshot":
|
||||
"Praćenje snimaka (snapshot) je onemogućeno u konfiguraciji, pa promjene sesije nisu dostupne",
|
||||
"session.review.noChanges": "Nema izmjena",
|
||||
|
||||
"session.files.selectToOpen": "Odaberi datoteku za otvaranje",
|
||||
"session.files.all": "Sve datoteke",
|
||||
"session.files.empty": "Nema datoteka",
|
||||
"session.files.binaryContent": "Binarna datoteka (sadržaj se ne može prikazati)",
|
||||
|
||||
"session.messages.renderEarlier": "Prikaži ranije poruke",
|
||||
@@ -514,6 +525,17 @@ export const dict = {
|
||||
"session.todo.title": "Zadaci",
|
||||
"session.todo.collapse": "Sažmi",
|
||||
"session.todo.expand": "Proširi",
|
||||
"session.followupDock.summary.one": "{{count}} poruka na čekanju",
|
||||
"session.followupDock.summary.other": "{{count}} poruka na čekanju",
|
||||
"session.followupDock.sendNow": "Pošalji sada",
|
||||
"session.followupDock.edit": "Uredi",
|
||||
"session.followupDock.collapse": "Sažmi poruke na čekanju",
|
||||
"session.followupDock.expand": "Proširi poruke na čekanju",
|
||||
"session.revertDock.summary.one": "{{count}} vraćena poruka",
|
||||
"session.revertDock.summary.other": "{{count}} vraćenih poruka",
|
||||
"session.revertDock.collapse": "Sažmi vraćene poruke",
|
||||
"session.revertDock.expand": "Proširi vraćene poruke",
|
||||
"session.revertDock.restore": "Vrati poruku",
|
||||
|
||||
"session.new.title": "Napravi bilo šta",
|
||||
"session.new.worktree.main": "Glavna grana",
|
||||
@@ -609,10 +631,18 @@ export const dict = {
|
||||
"settings.general.row.language.description": "Promijeni jezik prikaza u OpenCode-u",
|
||||
"settings.general.row.appearance.title": "Izgled",
|
||||
"settings.general.row.appearance.description": "Prilagodi kako OpenCode izgleda na tvom uređaju",
|
||||
"settings.general.row.colorScheme.title": "Šema boja",
|
||||
"settings.general.row.colorScheme.description": "Odaberi da li OpenCode prati sistemsku, svijetlu ili tamnu temu",
|
||||
"settings.general.row.theme.title": "Tema",
|
||||
"settings.general.row.theme.description": "Prilagodi temu OpenCode-a.",
|
||||
"settings.general.row.font.title": "Font",
|
||||
"settings.general.row.font.description": "Prilagodi monospace font koji se koristi u blokovima koda",
|
||||
"settings.general.row.followup.title": "Ponašanje nadovezivanja",
|
||||
"settings.general.row.followup.description": "Odaberi da li upiti nadovezivanja usmjeravaju odmah ili čekaju u redu",
|
||||
"settings.general.row.followup.option.queue": "Red čekanja",
|
||||
"settings.general.row.followup.option.steer": "Usmjeri",
|
||||
"settings.general.row.reasoningSummaries.title": "Prikaži sažetke rasuđivanja",
|
||||
"settings.general.row.reasoningSummaries.description": "Prikaži sažetke rasuđivanja modela na vremenskoj traci",
|
||||
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Proširi dijelove shell alata",
|
||||
"settings.general.row.shellToolPartsExpanded.description":
|
||||
|
||||
@@ -113,6 +113,7 @@ export const dict = {
|
||||
"dialog.model.empty": "Ingen modeller fundet",
|
||||
"dialog.model.manage": "Administrer modeller",
|
||||
"dialog.model.manage.description": "Tilpas hvilke modeller der vises i modelvælgeren.",
|
||||
"dialog.model.manage.provider.toggle": "Skift alle {{provider}}-modeller",
|
||||
|
||||
"dialog.model.unpaid.freeModels.title": "Gratis modeller leveret af OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Tilføj flere modeller fra populære udbydere",
|
||||
@@ -313,6 +314,11 @@ export const dict = {
|
||||
"dialog.server.add.error": "Kunne ikke forbinde til server",
|
||||
"dialog.server.add.checking": "Tjekker...",
|
||||
"dialog.server.add.button": "Tilføj server",
|
||||
"dialog.server.add.name": "Servernavn (valgfrit)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "Brugernavn (valgfrit)",
|
||||
"dialog.server.add.password": "Adgangskode (valgfrit)",
|
||||
"dialog.server.edit.title": "Rediger server",
|
||||
"dialog.server.default.title": "Standardserver",
|
||||
"dialog.server.default.description":
|
||||
"Forbind til denne server ved start af app i stedet for at starte en lokal server. Kræver genstart.",
|
||||
@@ -391,6 +397,7 @@ export const dict = {
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
|
||||
"toast.language.title": "Sprog",
|
||||
"toast.language.description": "Skiftede til {{language}}",
|
||||
@@ -495,9 +502,13 @@ export const dict = {
|
||||
"session.review.change.other": "Ændringer",
|
||||
"session.review.loadingChanges": "Indlæser ændringer...",
|
||||
"session.review.empty": "Ingen ændringer i denne session endnu",
|
||||
"session.review.noVcs": "Intet Git versionsstyringssystem fundet, ændringer vises ikke",
|
||||
"session.review.noSnapshot":
|
||||
"Snapshot-sporing er deaktiveret i konfigurationen, så sessionsændringer er ikke tilgængelige",
|
||||
"session.review.noChanges": "Ingen ændringer",
|
||||
"session.files.selectToOpen": "Vælg en fil at åbne",
|
||||
"session.files.all": "Alle filer",
|
||||
"session.files.empty": "Ingen filer",
|
||||
"session.files.binaryContent": "Binær fil (indhold kan ikke vises)",
|
||||
"session.messages.renderEarlier": "Vis tidligere beskeder",
|
||||
"session.messages.loadingEarlier": "Indlæser tidligere beskeder...",
|
||||
@@ -509,6 +520,17 @@ export const dict = {
|
||||
"session.todo.title": "Opgaver",
|
||||
"session.todo.collapse": "Skjul",
|
||||
"session.todo.expand": "Udvid",
|
||||
"session.followupDock.summary.one": "{{count}} besked i kø",
|
||||
"session.followupDock.summary.other": "{{count}} beskeder i kø",
|
||||
"session.followupDock.sendNow": "Send nu",
|
||||
"session.followupDock.edit": "Rediger",
|
||||
"session.followupDock.collapse": "Skjul beskeder i kø",
|
||||
"session.followupDock.expand": "Udvid beskeder i kø",
|
||||
"session.revertDock.summary.one": "{{count}} tilbagerullet besked",
|
||||
"session.revertDock.summary.other": "{{count}} tilbagerullede beskeder",
|
||||
"session.revertDock.collapse": "Skjul tilbagerullede beskeder",
|
||||
"session.revertDock.expand": "Udvid tilbagerullede beskeder",
|
||||
"session.revertDock.restore": "Gendan besked",
|
||||
|
||||
"session.new.title": "Byg hvad som helst",
|
||||
"session.new.worktree.main": "Hovedgren",
|
||||
@@ -604,10 +626,18 @@ export const dict = {
|
||||
"settings.general.row.language.description": "Ændr visningssproget for OpenCode",
|
||||
"settings.general.row.appearance.title": "Udseende",
|
||||
"settings.general.row.appearance.description": "Tilpas hvordan OpenCode ser ud på din enhed",
|
||||
"settings.general.row.colorScheme.title": "Farveskema",
|
||||
"settings.general.row.colorScheme.description": "Vælg om OpenCode følger systemets, lyst eller mørkt tema",
|
||||
"settings.general.row.theme.title": "Tema",
|
||||
"settings.general.row.theme.description": "Tilpas hvordan OpenCode er temabestemt.",
|
||||
"settings.general.row.font.title": "Skrifttype",
|
||||
"settings.general.row.font.description": "Tilpas mono-skrifttypen brugt i kodeblokke",
|
||||
"settings.general.row.followup.title": "Opfølgningsadfærd",
|
||||
"settings.general.row.followup.description": "Vælg om opfølgende forespørgsler skal styre straks eller vente i kø",
|
||||
"settings.general.row.followup.option.queue": "Kø",
|
||||
"settings.general.row.followup.option.steer": "Styr",
|
||||
"settings.general.row.reasoningSummaries.title": "Vis tænkeoversigter",
|
||||
"settings.general.row.reasoningSummaries.description": "Vis model tænkeoversigter i tidslinjen",
|
||||
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Udvid shell-værktøjsdele",
|
||||
"settings.general.row.shellToolPartsExpanded.description": "Vis shell-værktøjsdele udvidet som standard i tidslinjen",
|
||||
|
||||
@@ -108,6 +108,7 @@ export const dict = {
|
||||
"dialog.model.empty": "Keine Modellergebnisse",
|
||||
"dialog.model.manage": "Modelle verwalten",
|
||||
"dialog.model.manage.description": "Anpassen, welche Modelle in der Modellauswahl erscheinen.",
|
||||
"dialog.model.manage.provider.toggle": "Alle {{provider}}-Modelle umschalten",
|
||||
"dialog.model.unpaid.freeModels.title": "Kostenlose Modelle von OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Weitere Modelle von beliebten Anbietern hinzufügen",
|
||||
"dialog.provider.viewAll": "Mehr Anbieter anzeigen",
|
||||
@@ -294,6 +295,11 @@ export const dict = {
|
||||
"dialog.server.add.error": "Verbindung zum Server fehlgeschlagen",
|
||||
"dialog.server.add.checking": "Prüfen...",
|
||||
"dialog.server.add.button": "Server hinzufügen",
|
||||
"dialog.server.add.name": "Servername (optional)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "Benutzername (optional)",
|
||||
"dialog.server.add.password": "Passwort (optional)",
|
||||
"dialog.server.edit.title": "Server bearbeiten",
|
||||
"dialog.server.default.title": "Standardserver",
|
||||
"dialog.server.default.description":
|
||||
"Beim App-Start mit diesem Server verbinden, anstatt einen lokalen Server zu starten. Erfordert Neustart.",
|
||||
@@ -366,6 +372,7 @@ export const dict = {
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "Sprache",
|
||||
"toast.language.description": "Zu {{language}} gewechselt",
|
||||
"toast.theme.title": "Thema gewechselt",
|
||||
@@ -454,9 +461,13 @@ export const dict = {
|
||||
"session.review.change.other": "Änderungen",
|
||||
"session.review.loadingChanges": "Lade Änderungen...",
|
||||
"session.review.empty": "Noch keine Änderungen in dieser Sitzung",
|
||||
"session.review.noVcs": "Kein Git-Versionskontrollsystem erkannt, Änderungen werden nicht angezeigt",
|
||||
"session.review.noSnapshot":
|
||||
"Snapshot-Tracking ist in der Konfiguration deaktiviert, daher sind Sitzungsänderungen nicht verfügbar",
|
||||
"session.review.noChanges": "Keine Änderungen",
|
||||
"session.files.selectToOpen": "Datei zum Öffnen auswählen",
|
||||
"session.files.all": "Alle Dateien",
|
||||
"session.files.empty": "Keine Dateien",
|
||||
"session.files.binaryContent": "Binärdatei (Inhalt kann nicht angezeigt werden)",
|
||||
"session.messages.renderEarlier": "Frühere Nachrichten rendern",
|
||||
"session.messages.loadingEarlier": "Lade frühere Nachrichten...",
|
||||
@@ -467,6 +478,17 @@ export const dict = {
|
||||
"session.todo.title": "Aufgaben",
|
||||
"session.todo.collapse": "Einklappen",
|
||||
"session.todo.expand": "Ausklappen",
|
||||
"session.followupDock.summary.one": "{{count}} Nachricht in der Warteschlange",
|
||||
"session.followupDock.summary.other": "{{count}} Nachrichten in der Warteschlange",
|
||||
"session.followupDock.sendNow": "Jetzt senden",
|
||||
"session.followupDock.edit": "Bearbeiten",
|
||||
"session.followupDock.collapse": "Warteschlange einklappen",
|
||||
"session.followupDock.expand": "Warteschlange ausklappen",
|
||||
"session.revertDock.summary.one": "{{count}} zurückgesetzte Nachricht",
|
||||
"session.revertDock.summary.other": "{{count}} zurückgesetzte Nachrichten",
|
||||
"session.revertDock.collapse": "Zurückgesetzte Nachrichten einklappen",
|
||||
"session.revertDock.expand": "Zurückgesetzte Nachrichten ausklappen",
|
||||
"session.revertDock.restore": "Nachricht wiederherstellen",
|
||||
"session.new.title": "Baue, was du willst",
|
||||
"session.new.worktree.main": "Haupt-Branch",
|
||||
"session.new.worktree.mainWithBranch": "Haupt-Branch ({{branch}})",
|
||||
@@ -553,10 +575,21 @@ export const dict = {
|
||||
"settings.general.row.language.description": "Die Anzeigesprache für OpenCode ändern",
|
||||
"settings.general.row.appearance.title": "Erscheinungsbild",
|
||||
"settings.general.row.appearance.description": "Anpassen, wie OpenCode auf Ihrem Gerät aussieht",
|
||||
"settings.general.row.colorScheme.title": "Farbschema",
|
||||
"settings.general.row.colorScheme.description":
|
||||
"Wählen Sie, ob OpenCode dem System-, hellen oder dunklen Thema folgt",
|
||||
"settings.general.row.theme.title": "Thema",
|
||||
"settings.general.row.theme.description": "Das Thema von OpenCode anpassen.",
|
||||
"settings.general.row.font.title": "Schriftart",
|
||||
"settings.general.row.font.description": "Die in Codeblöcken verwendete Monospace-Schriftart anpassen",
|
||||
"settings.general.row.followup.title": "Verhalten bei Folgefragen",
|
||||
"settings.general.row.followup.description":
|
||||
"Wählen Sie, ob Folgefragen sofort steuern oder in einer Warteschlange warten",
|
||||
"settings.general.row.followup.option.queue": "Warteschlange",
|
||||
"settings.general.row.followup.option.steer": "Steuern",
|
||||
"settings.general.row.reasoningSummaries.title": "Reasoning-Zusammenfassungen anzeigen",
|
||||
"settings.general.row.reasoningSummaries.description":
|
||||
"Zusammenfassungen des Modell-Reasonings in der Timeline anzeigen",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Shell-Tool-Abschnitte ausklappen",
|
||||
"settings.general.row.shellToolPartsExpanded.description":
|
||||
"Shell-Tool-Abschnitte standardmäßig in der Timeline ausgeklappt anzeigen",
|
||||
|
||||
@@ -530,6 +530,12 @@ export const dict = {
|
||||
"session.todo.title": "Todos",
|
||||
"session.todo.collapse": "Collapse",
|
||||
"session.todo.expand": "Expand",
|
||||
"session.followupDock.summary.one": "{{count}} queued message",
|
||||
"session.followupDock.summary.other": "{{count}} queued messages",
|
||||
"session.followupDock.sendNow": "Send now",
|
||||
"session.followupDock.edit": "Edit",
|
||||
"session.followupDock.collapse": "Collapse queued messages",
|
||||
"session.followupDock.expand": "Expand queued messages",
|
||||
"session.revertDock.summary.one": "{{count}} rolled back message",
|
||||
"session.revertDock.summary.other": "{{count}} rolled back messages",
|
||||
"session.revertDock.collapse": "Collapse rolled back messages",
|
||||
@@ -638,10 +644,16 @@ export const dict = {
|
||||
"settings.general.row.language.description": "Change the display language for OpenCode",
|
||||
"settings.general.row.appearance.title": "Appearance",
|
||||
"settings.general.row.appearance.description": "Customise how OpenCode looks on your device",
|
||||
"settings.general.row.colorScheme.title": "Color scheme",
|
||||
"settings.general.row.colorScheme.description": "Choose whether OpenCode follows the system, light, or dark theme",
|
||||
"settings.general.row.theme.title": "Theme",
|
||||
"settings.general.row.theme.description": "Customise how OpenCode is themed.",
|
||||
"settings.general.row.font.title": "Font",
|
||||
"settings.general.row.font.description": "Customise the mono font used in code blocks",
|
||||
"settings.general.row.followup.title": "Follow-up behavior",
|
||||
"settings.general.row.followup.description": "Choose whether follow-up prompts steer immediately or wait in a queue",
|
||||
"settings.general.row.followup.option.queue": "Queue",
|
||||
"settings.general.row.followup.option.steer": "Steer",
|
||||
"settings.general.row.reasoningSummaries.title": "Show reasoning summaries",
|
||||
"settings.general.row.reasoningSummaries.description": "Display model reasoning summaries in the timeline",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Expand shell tool parts",
|
||||
|
||||
@@ -113,6 +113,7 @@ export const dict = {
|
||||
"dialog.model.empty": "Sin resultados de modelos",
|
||||
"dialog.model.manage": "Gestionar modelos",
|
||||
"dialog.model.manage.description": "Personalizar qué modelos aparecen en el selector de modelos.",
|
||||
"dialog.model.manage.provider.toggle": "Alternar todos los modelos de {{provider}}",
|
||||
|
||||
"dialog.model.unpaid.freeModels.title": "Modelos gratuitos proporcionados por OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Añadir más modelos de proveedores populares",
|
||||
@@ -314,6 +315,11 @@ export const dict = {
|
||||
"dialog.server.add.error": "No se pudo conectar al servidor",
|
||||
"dialog.server.add.checking": "Comprobando...",
|
||||
"dialog.server.add.button": "Añadir servidor",
|
||||
"dialog.server.add.name": "Nombre del servidor (opcional)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "Nombre de usuario (opcional)",
|
||||
"dialog.server.add.password": "Contraseña (opcional)",
|
||||
"dialog.server.edit.title": "Editar servidor",
|
||||
"dialog.server.default.title": "Servidor predeterminado",
|
||||
"dialog.server.default.description":
|
||||
"Conectar a este servidor al iniciar la app en lugar de iniciar un servidor local. Requiere reinicio.",
|
||||
@@ -393,6 +399,7 @@ export const dict = {
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
|
||||
"toast.language.title": "Idioma",
|
||||
"toast.language.description": "Cambiado a {{language}}",
|
||||
@@ -499,10 +506,14 @@ export const dict = {
|
||||
"session.review.change.other": "Cambios",
|
||||
"session.review.loadingChanges": "Cargando cambios...",
|
||||
"session.review.empty": "No hay cambios en esta sesión aún",
|
||||
"session.review.noVcs": "No se detectó Sistema de Control de Versiones Git, los cambios no se muestran",
|
||||
"session.review.noSnapshot":
|
||||
"El seguimiento de instantáneas está deshabilitado en la configuración, por lo que los cambios de sesión no están disponibles",
|
||||
"session.review.noChanges": "Sin cambios",
|
||||
|
||||
"session.files.selectToOpen": "Selecciona un archivo para abrir",
|
||||
"session.files.all": "Todos los archivos",
|
||||
"session.files.empty": "Sin archivos",
|
||||
"session.files.binaryContent": "Archivo binario (el contenido no puede ser mostrado)",
|
||||
|
||||
"session.messages.renderEarlier": "Renderizar mensajes anteriores",
|
||||
@@ -515,6 +526,17 @@ export const dict = {
|
||||
"session.todo.title": "Tareas",
|
||||
"session.todo.collapse": "Contraer",
|
||||
"session.todo.expand": "Expandir",
|
||||
"session.followupDock.summary.one": "{{count}} mensaje en cola",
|
||||
"session.followupDock.summary.other": "{{count}} mensajes en cola",
|
||||
"session.followupDock.sendNow": "Enviar ahora",
|
||||
"session.followupDock.edit": "Editar",
|
||||
"session.followupDock.collapse": "Contraer mensajes en cola",
|
||||
"session.followupDock.expand": "Expandir mensajes en cola",
|
||||
"session.revertDock.summary.one": "{{count}} mensaje revertido",
|
||||
"session.revertDock.summary.other": "{{count}} mensajes revertidos",
|
||||
"session.revertDock.collapse": "Contraer mensajes revertidos",
|
||||
"session.revertDock.expand": "Expandir mensajes revertidos",
|
||||
"session.revertDock.restore": "Restaurar mensaje",
|
||||
|
||||
"session.new.title": "Construye lo que quieras",
|
||||
"session.new.worktree.main": "Rama principal",
|
||||
@@ -612,11 +634,20 @@ export const dict = {
|
||||
"settings.general.row.language.description": "Cambiar el idioma de visualización para OpenCode",
|
||||
"settings.general.row.appearance.title": "Apariencia",
|
||||
"settings.general.row.appearance.description": "Personaliza cómo se ve OpenCode en tu dispositivo",
|
||||
"settings.general.row.colorScheme.title": "Esquema de color",
|
||||
"settings.general.row.colorScheme.description": "Elige si OpenCode sigue el tema del sistema, claro u oscuro",
|
||||
"settings.general.row.theme.title": "Tema",
|
||||
"settings.general.row.theme.description": "Personaliza el tema de OpenCode.",
|
||||
"settings.general.row.font.title": "Fuente",
|
||||
"settings.general.row.font.description": "Personaliza la fuente monoespaciada usada en bloques de código",
|
||||
|
||||
"settings.general.row.followup.title": "Comportamiento de seguimiento",
|
||||
"settings.general.row.followup.description":
|
||||
"Elige si los prompts de seguimiento se dirigen inmediatamente o esperan en una cola",
|
||||
"settings.general.row.followup.option.queue": "Cola",
|
||||
"settings.general.row.followup.option.steer": "Dirigir",
|
||||
"settings.general.row.reasoningSummaries.title": "Mostrar resúmenes de razonamiento",
|
||||
"settings.general.row.reasoningSummaries.description":
|
||||
"Mostrar resúmenes del razonamiento del modelo en la línea de tiempo",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Expandir partes de la herramienta shell",
|
||||
"settings.general.row.shellToolPartsExpanded.description":
|
||||
"Mostrar las partes de la herramienta shell expandidas por defecto en la línea de tiempo",
|
||||
|
||||
@@ -104,6 +104,7 @@ export const dict = {
|
||||
"dialog.model.empty": "Aucun résultat de modèle",
|
||||
"dialog.model.manage": "Gérer les modèles",
|
||||
"dialog.model.manage.description": "Personnalisez les modèles qui apparaissent dans le sélecteur.",
|
||||
"dialog.model.manage.provider.toggle": "Basculer tous les modèles {{provider}}",
|
||||
"dialog.model.unpaid.freeModels.title": "Modèles gratuits fournis par OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Ajouter plus de modèles de fournisseurs populaires",
|
||||
"dialog.provider.viewAll": "Voir plus de fournisseurs",
|
||||
@@ -288,6 +289,11 @@ export const dict = {
|
||||
"dialog.server.add.error": "Impossible de se connecter au serveur",
|
||||
"dialog.server.add.checking": "Vérification...",
|
||||
"dialog.server.add.button": "Ajouter un serveur",
|
||||
"dialog.server.add.name": "Nom du serveur (optionnel)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "Nom d'utilisateur (optionnel)",
|
||||
"dialog.server.add.password": "Mot de passe (optionnel)",
|
||||
"dialog.server.edit.title": "Modifier le serveur",
|
||||
"dialog.server.default.title": "Serveur par défaut",
|
||||
"dialog.server.default.description":
|
||||
"Se connecter à ce serveur au lancement de l'application au lieu de démarrer un serveur local. Nécessite un redémarrage.",
|
||||
@@ -360,6 +366,7 @@ export const dict = {
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "Langue",
|
||||
"toast.language.description": "Passé à {{language}}",
|
||||
"toast.theme.title": "Thème changé",
|
||||
@@ -451,8 +458,12 @@ export const dict = {
|
||||
"session.review.loadingChanges": "Chargement des modifications...",
|
||||
"session.review.empty": "Aucune modification dans cette session pour l'instant",
|
||||
"session.review.noChanges": "Aucune modification",
|
||||
"session.review.noVcs": "Aucun système de contrôle de version Git détecté, modifications non affichées",
|
||||
"session.review.noSnapshot":
|
||||
"Le suivi des instantanés est désactivé dans la configuration, les modifications de session sont donc indisponibles",
|
||||
"session.files.selectToOpen": "Sélectionnez un fichier à ouvrir",
|
||||
"session.files.all": "Tous les fichiers",
|
||||
"session.files.empty": "Aucun fichier",
|
||||
"session.files.binaryContent": "Fichier binaire (le contenu ne peut pas être affiché)",
|
||||
"session.messages.renderEarlier": "Afficher les messages précédents",
|
||||
"session.messages.loadingEarlier": "Chargement des messages précédents...",
|
||||
@@ -463,6 +474,17 @@ export const dict = {
|
||||
"session.todo.title": "Tâches",
|
||||
"session.todo.collapse": "Réduire",
|
||||
"session.todo.expand": "Développer",
|
||||
"session.followupDock.summary.one": "{{count}} message en file d'attente",
|
||||
"session.followupDock.summary.other": "{{count}} messages en file d'attente",
|
||||
"session.followupDock.sendNow": "Envoyer maintenant",
|
||||
"session.followupDock.edit": "Modifier",
|
||||
"session.followupDock.collapse": "Réduire les messages en file d'attente",
|
||||
"session.followupDock.expand": "Développer les messages en file d'attente",
|
||||
"session.revertDock.summary.one": "{{count}} message annulé",
|
||||
"session.revertDock.summary.other": "{{count}} messages annulés",
|
||||
"session.revertDock.collapse": "Réduire les messages annulés",
|
||||
"session.revertDock.expand": "Développer les messages annulés",
|
||||
"session.revertDock.restore": "Restaurer le message",
|
||||
"session.new.title": "Créez ce que vous voulez",
|
||||
"session.new.worktree.main": "Branche principale",
|
||||
"session.new.worktree.mainWithBranch": "Branche principale ({{branch}})",
|
||||
@@ -550,10 +572,20 @@ export const dict = {
|
||||
"settings.general.row.language.description": "Changer la langue d'affichage pour OpenCode",
|
||||
"settings.general.row.appearance.title": "Apparence",
|
||||
"settings.general.row.appearance.description": "Personnaliser l'apparence d'OpenCode sur votre appareil",
|
||||
"settings.general.row.colorScheme.title": "Schéma de couleurs",
|
||||
"settings.general.row.colorScheme.description": "Choisissez si OpenCode suit le thème système, clair ou sombre",
|
||||
"settings.general.row.theme.title": "Thème",
|
||||
"settings.general.row.theme.description": "Personnaliser le thème d'OpenCode.",
|
||||
"settings.general.row.font.title": "Police",
|
||||
"settings.general.row.font.description": "Personnaliser la police mono utilisée dans les blocs de code",
|
||||
"settings.general.row.followup.title": "Comportement de suivi",
|
||||
"settings.general.row.followup.description":
|
||||
"Choisissez si les messages de suivi dirigent immédiatement ou attendent dans une file d'attente",
|
||||
"settings.general.row.followup.option.queue": "File d'attente",
|
||||
"settings.general.row.followup.option.steer": "Diriger",
|
||||
"settings.general.row.reasoningSummaries.title": "Afficher les résumés de raisonnement",
|
||||
"settings.general.row.reasoningSummaries.description":
|
||||
"Afficher les résumés de raisonnement du modèle dans la chronologie",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Développer les parties de l'outil shell",
|
||||
"settings.general.row.shellToolPartsExpanded.description":
|
||||
"Afficher les parties de l'outil shell développées par défaut dans la chronologie",
|
||||
|
||||
@@ -104,6 +104,7 @@ export const dict = {
|
||||
"dialog.model.empty": "モデルが見つかりません",
|
||||
"dialog.model.manage": "モデルを管理",
|
||||
"dialog.model.manage.description": "モデルセレクターに表示するモデルをカスタマイズします。",
|
||||
"dialog.model.manage.provider.toggle": "すべての{{provider}}モデルを切り替え",
|
||||
"dialog.model.unpaid.freeModels.title": "OpenCodeが提供する無料モデル",
|
||||
"dialog.model.unpaid.addMore.title": "人気のプロバイダーからモデルを追加",
|
||||
"dialog.provider.viewAll": "さらにプロバイダーを表示",
|
||||
@@ -287,6 +288,11 @@ export const dict = {
|
||||
"dialog.server.add.error": "サーバーに接続できませんでした",
|
||||
"dialog.server.add.checking": "確認中...",
|
||||
"dialog.server.add.button": "サーバーを追加",
|
||||
"dialog.server.add.name": "サーバー名 (オプション)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "ユーザー名 (オプション)",
|
||||
"dialog.server.add.password": "パスワード (オプション)",
|
||||
"dialog.server.edit.title": "サーバーを編集",
|
||||
"dialog.server.default.title": "デフォルトサーバー",
|
||||
"dialog.server.default.description":
|
||||
"ローカルサーバーを起動する代わりに、アプリ起動時にこのサーバーに接続します。再起動が必要です。",
|
||||
@@ -358,6 +364,7 @@ export const dict = {
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "言語",
|
||||
"toast.language.description": "{{language}}に切り替えました",
|
||||
"toast.theme.title": "テーマが切り替わりました",
|
||||
@@ -444,9 +451,12 @@ export const dict = {
|
||||
"session.review.change.other": "変更",
|
||||
"session.review.loadingChanges": "変更を読み込み中...",
|
||||
"session.review.empty": "このセッションでの変更はまだありません",
|
||||
"session.review.noVcs": "Gitバージョン管理システムが検出されないため、変更は表示されません",
|
||||
"session.review.noSnapshot": "設定でスナップショット追跡が無効になっているため、セッションの変更は利用できません",
|
||||
"session.review.noChanges": "変更なし",
|
||||
"session.files.selectToOpen": "開くファイルを選択",
|
||||
"session.files.all": "すべてのファイル",
|
||||
"session.files.empty": "ファイルなし",
|
||||
"session.files.binaryContent": "バイナリファイル(内容を表示できません)",
|
||||
"session.messages.renderEarlier": "以前のメッセージを表示",
|
||||
"session.messages.loadingEarlier": "以前のメッセージを読み込み中...",
|
||||
@@ -457,6 +467,17 @@ export const dict = {
|
||||
"session.todo.title": "ToDo",
|
||||
"session.todo.collapse": "折りたたむ",
|
||||
"session.todo.expand": "展開",
|
||||
"session.followupDock.summary.one": "{{count}} 件のメッセージが待機中",
|
||||
"session.followupDock.summary.other": "{{count}} 件のメッセージが待機中",
|
||||
"session.followupDock.sendNow": "今すぐ送信",
|
||||
"session.followupDock.edit": "編集",
|
||||
"session.followupDock.collapse": "待機中のメッセージを折りたたむ",
|
||||
"session.followupDock.expand": "待機中のメッセージを展開",
|
||||
"session.revertDock.summary.one": "{{count}} 件のロールバックされたメッセージ",
|
||||
"session.revertDock.summary.other": "{{count}} 件のロールバックされたメッセージ",
|
||||
"session.revertDock.collapse": "ロールバックされたメッセージを折りたたむ",
|
||||
"session.revertDock.expand": "ロールバックされたメッセージを展開",
|
||||
"session.revertDock.restore": "メッセージを復元",
|
||||
"session.new.title": "何でも作る",
|
||||
"session.new.worktree.main": "メインブランチ",
|
||||
"session.new.worktree.mainWithBranch": "メインブランチ ({{branch}})",
|
||||
@@ -542,10 +563,19 @@ export const dict = {
|
||||
"settings.general.row.language.description": "OpenCodeの表示言語を変更します",
|
||||
"settings.general.row.appearance.title": "外観",
|
||||
"settings.general.row.appearance.description": "デバイスでのOpenCodeの表示をカスタマイズします",
|
||||
"settings.general.row.colorScheme.title": "配色",
|
||||
"settings.general.row.colorScheme.description": "OpenCodeがシステム、ライト、またはダークテーマに従うかを選択します",
|
||||
"settings.general.row.theme.title": "テーマ",
|
||||
"settings.general.row.theme.description": "OpenCodeのテーマをカスタマイズします。",
|
||||
"settings.general.row.font.title": "フォント",
|
||||
"settings.general.row.font.description": "コードブロックで使用する等幅フォントをカスタマイズします",
|
||||
"settings.general.row.followup.title": "フォローアップの動作",
|
||||
"settings.general.row.followup.description":
|
||||
"フォローアッププロンプトを即座に実行するか、キューで待機させるかを選択します",
|
||||
"settings.general.row.followup.option.queue": "キューに追加",
|
||||
"settings.general.row.followup.option.steer": "即座に実行 (Steer)",
|
||||
"settings.general.row.reasoningSummaries.title": "推論の要約を表示",
|
||||
"settings.general.row.reasoningSummaries.description": "タイムラインにモデルの推論の要約を表示します",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "shell ツールパーツを展開",
|
||||
"settings.general.row.shellToolPartsExpanded.description":
|
||||
"タイムラインで shell ツールパーツをデフォルトで展開して表示します",
|
||||
|
||||
@@ -108,6 +108,7 @@ export const dict = {
|
||||
"dialog.model.empty": "모델 결과 없음",
|
||||
"dialog.model.manage": "모델 관리",
|
||||
"dialog.model.manage.description": "모델 선택기에 표시할 모델 사용자 지정",
|
||||
"dialog.model.manage.provider.toggle": "모든 {{provider}} 모델 토글",
|
||||
"dialog.model.unpaid.freeModels.title": "OpenCode에서 제공하는 무료 모델",
|
||||
"dialog.model.unpaid.addMore.title": "인기 공급자의 모델 추가",
|
||||
"dialog.provider.viewAll": "더 많은 공급자 보기",
|
||||
@@ -291,6 +292,11 @@ export const dict = {
|
||||
"dialog.server.add.error": "서버에 연결할 수 없습니다",
|
||||
"dialog.server.add.checking": "확인 중...",
|
||||
"dialog.server.add.button": "서버 추가",
|
||||
"dialog.server.add.name": "서버 이름 (선택 사항)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "사용자 이름 (선택 사항)",
|
||||
"dialog.server.add.password": "비밀번호 (선택 사항)",
|
||||
"dialog.server.edit.title": "서버 편집",
|
||||
"dialog.server.default.title": "기본 서버",
|
||||
"dialog.server.default.description":
|
||||
"로컬 서버를 시작하는 대신 앱 실행 시 이 서버에 연결합니다. 다시 시작해야 합니다.",
|
||||
@@ -361,6 +367,7 @@ export const dict = {
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "언어",
|
||||
"toast.language.description": "{{language}}(으)로 전환됨",
|
||||
"toast.theme.title": "테마 전환됨",
|
||||
@@ -446,9 +453,12 @@ export const dict = {
|
||||
"session.review.change.other": "변경",
|
||||
"session.review.loadingChanges": "변경 사항 로드 중...",
|
||||
"session.review.empty": "이 세션에 변경 사항이 아직 없습니다",
|
||||
"session.review.noVcs": "Git 버전 관리 시스템이 감지되지 않아 변경 사항이 표시되지 않습니다",
|
||||
"session.review.noSnapshot": "구성에서 스냅샷 추적이 비활성화되어 있어 세션 변경 사항을 사용할 수 없습니다",
|
||||
"session.review.noChanges": "변경 없음",
|
||||
"session.files.selectToOpen": "열 파일을 선택하세요",
|
||||
"session.files.all": "모든 파일",
|
||||
"session.files.empty": "파일 없음",
|
||||
"session.files.binaryContent": "바이너리 파일 (내용을 표시할 수 없음)",
|
||||
"session.messages.renderEarlier": "이전 메시지 렌더링",
|
||||
"session.messages.loadingEarlier": "이전 메시지 로드 중...",
|
||||
@@ -459,6 +469,17 @@ export const dict = {
|
||||
"session.todo.title": "할 일",
|
||||
"session.todo.collapse": "접기",
|
||||
"session.todo.expand": "펼치기",
|
||||
"session.followupDock.summary.one": "{{count}}개의 대기 중인 메시지",
|
||||
"session.followupDock.summary.other": "{{count}}개의 대기 중인 메시지",
|
||||
"session.followupDock.sendNow": "지금 전송",
|
||||
"session.followupDock.edit": "편집",
|
||||
"session.followupDock.collapse": "대기 중인 메시지 접기",
|
||||
"session.followupDock.expand": "대기 중인 메시지 펼치기",
|
||||
"session.revertDock.summary.one": "{{count}}개의 롤백된 메시지",
|
||||
"session.revertDock.summary.other": "{{count}}개의 롤백된 메시지",
|
||||
"session.revertDock.collapse": "롤백된 메시지 접기",
|
||||
"session.revertDock.expand": "롤백된 메시지 펼치기",
|
||||
"session.revertDock.restore": "메시지 복원",
|
||||
"session.new.title": "무엇이든 만들기",
|
||||
"session.new.worktree.main": "메인 브랜치",
|
||||
"session.new.worktree.mainWithBranch": "메인 브랜치 ({{branch}})",
|
||||
@@ -543,10 +564,18 @@ export const dict = {
|
||||
"settings.general.row.language.description": "OpenCode 표시 언어 변경",
|
||||
"settings.general.row.appearance.title": "모양",
|
||||
"settings.general.row.appearance.description": "기기에서 OpenCode가 보이는 방식 사용자 지정",
|
||||
"settings.general.row.colorScheme.title": "색상 테마",
|
||||
"settings.general.row.colorScheme.description": "OpenCode가 시스템, 라이트 또는 다크 테마를 따를지 선택하세요",
|
||||
"settings.general.row.theme.title": "테마",
|
||||
"settings.general.row.theme.description": "OpenCode 테마 사용자 지정",
|
||||
"settings.general.row.font.title": "글꼴",
|
||||
"settings.general.row.font.description": "코드 블록에 사용되는 고정폭 글꼴 사용자 지정",
|
||||
"settings.general.row.followup.title": "후속 조치 동작",
|
||||
"settings.general.row.followup.description": "후속 프롬프트를 즉시 실행할지 대기열에 넣을지 선택하세요",
|
||||
"settings.general.row.followup.option.queue": "대기열",
|
||||
"settings.general.row.followup.option.steer": "조종",
|
||||
"settings.general.row.reasoningSummaries.title": "추론 요약 표시",
|
||||
"settings.general.row.reasoningSummaries.description": "타임라인에 모델 추론 요약 표시",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "shell 도구 파트 펼치기",
|
||||
"settings.general.row.shellToolPartsExpanded.description":
|
||||
"타임라인에서 기본적으로 shell 도구 파트를 펼친 상태로 표시합니다",
|
||||
|
||||
@@ -116,6 +116,7 @@ export const dict = {
|
||||
"dialog.model.empty": "Ingen modellresultater",
|
||||
"dialog.model.manage": "Administrer modeller",
|
||||
"dialog.model.manage.description": "Tilpass hvilke modeller som vises i modellvelgeren.",
|
||||
"dialog.model.manage.provider.toggle": "Veksle alle {{provider}}-modeller",
|
||||
|
||||
"dialog.model.unpaid.freeModels.title": "Gratis modeller levert av OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Legg til flere modeller fra populære leverandører",
|
||||
@@ -216,7 +217,7 @@ export const dict = {
|
||||
|
||||
"common.search.placeholder": "Søk",
|
||||
"common.goBack": "Gå tilbake",
|
||||
"common.goForward": "Navigate forward",
|
||||
"common.goForward": "Gå frem",
|
||||
"common.loading": "Laster",
|
||||
"common.loading.ellipsis": "...",
|
||||
"common.cancel": "Avbryt",
|
||||
@@ -317,6 +318,11 @@ export const dict = {
|
||||
"dialog.server.add.error": "Kunne ikke koble til server",
|
||||
"dialog.server.add.checking": "Sjekker...",
|
||||
"dialog.server.add.button": "Legg til server",
|
||||
"dialog.server.add.name": "Servernavn (valgfritt)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "Brukernavn (valgfritt)",
|
||||
"dialog.server.add.password": "Passord (valgfritt)",
|
||||
"dialog.server.edit.title": "Rediger server",
|
||||
"dialog.server.default.title": "Standardserver",
|
||||
"dialog.server.default.description":
|
||||
"Koble til denne serveren ved oppstart i stedet for å starte en lokal server. Krever omstart.",
|
||||
@@ -394,6 +400,7 @@ export const dict = {
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
|
||||
"toast.language.title": "Språk",
|
||||
"toast.language.description": "Byttet til {{language}}",
|
||||
@@ -499,10 +506,14 @@ export const dict = {
|
||||
"session.review.change.other": "Endringer",
|
||||
"session.review.loadingChanges": "Laster endringer...",
|
||||
"session.review.empty": "Ingen endringer i denne sesjonen ennå",
|
||||
"session.review.noVcs": "Ingen Git-versjonskontrollsystem oppdaget, endringer vises ikke",
|
||||
"session.review.noSnapshot":
|
||||
"Snapshot-sporing er deaktivert i konfigurasjonen, så sesjonsendringer er ikke tilgjengelige",
|
||||
"session.review.noChanges": "Ingen endringer",
|
||||
|
||||
"session.files.selectToOpen": "Velg en fil å åpne",
|
||||
"session.files.all": "Alle filer",
|
||||
"session.files.empty": "Ingen filer",
|
||||
"session.files.binaryContent": "Binær fil (innhold kan ikke vises)",
|
||||
|
||||
"session.messages.renderEarlier": "Vis tidligere meldinger",
|
||||
@@ -515,6 +526,17 @@ export const dict = {
|
||||
"session.todo.title": "Oppgaver",
|
||||
"session.todo.collapse": "Skjul",
|
||||
"session.todo.expand": "Utvid",
|
||||
"session.followupDock.summary.one": "{{count}} melding i kø",
|
||||
"session.followupDock.summary.other": "{{count}} meldinger i kø",
|
||||
"session.followupDock.sendNow": "Send nå",
|
||||
"session.followupDock.edit": "Rediger",
|
||||
"session.followupDock.collapse": "Skjul meldinger i kø",
|
||||
"session.followupDock.expand": "Utvid meldinger i kø",
|
||||
"session.revertDock.summary.one": "{{count}} tilbakestilt melding",
|
||||
"session.revertDock.summary.other": "{{count}} tilbakestilte meldinger",
|
||||
"session.revertDock.collapse": "Skjul tilbakestilte meldinger",
|
||||
"session.revertDock.expand": "Utvid tilbakestilte meldinger",
|
||||
"session.revertDock.restore": "Gjenopprett melding",
|
||||
|
||||
"session.new.title": "Bygg hva som helst",
|
||||
"session.new.worktree.main": "Hovedgren",
|
||||
@@ -612,11 +634,18 @@ export const dict = {
|
||||
"settings.general.row.language.description": "Endre visningsspråket for OpenCode",
|
||||
"settings.general.row.appearance.title": "Utseende",
|
||||
"settings.general.row.appearance.description": "Tilpass hvordan OpenCode ser ut på enheten din",
|
||||
"settings.general.row.colorScheme.title": "Fargevalg",
|
||||
"settings.general.row.colorScheme.description": "Velg om OpenCode skal følge systemets, lyst eller mørkt tema",
|
||||
"settings.general.row.theme.title": "Tema",
|
||||
"settings.general.row.theme.description": "Tilpass hvordan OpenCode er tematisert.",
|
||||
"settings.general.row.font.title": "Skrift",
|
||||
"settings.general.row.font.description": "Tilpass mono-skriften som brukes i kodeblokker",
|
||||
|
||||
"settings.general.row.followup.title": "Oppfølgingsadferd",
|
||||
"settings.general.row.followup.description": "Velg om oppfølgingsspørsmål skal kjøres umiddelbart eller vente i kø",
|
||||
"settings.general.row.followup.option.queue": "Kø",
|
||||
"settings.general.row.followup.option.steer": "Styr",
|
||||
"settings.general.row.reasoningSummaries.title": "Vis resonneringssammendrag",
|
||||
"settings.general.row.reasoningSummaries.description": "Vis sammendrag av modellresonnering i tidslinjen",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Utvid shell-verktøydeler",
|
||||
"settings.general.row.shellToolPartsExpanded.description": "Vis shell-verktøydeler utvidet som standard i tidslinjen",
|
||||
"settings.general.row.editToolPartsExpanded.title": "Utvid edit-verktøydeler",
|
||||
|
||||
@@ -104,6 +104,7 @@ export const dict = {
|
||||
"dialog.model.empty": "Brak wyników modelu",
|
||||
"dialog.model.manage": "Zarządzaj modelami",
|
||||
"dialog.model.manage.description": "Dostosuj, które modele pojawiają się w wyborze modelu.",
|
||||
"dialog.model.manage.provider.toggle": "Przełącz wszystkie modele {{provider}}",
|
||||
"dialog.model.unpaid.freeModels.title": "Darmowe modele dostarczane przez OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Dodaj więcej modeli od popularnych dostawców",
|
||||
"dialog.provider.viewAll": "Zobacz więcej dostawców",
|
||||
@@ -289,6 +290,11 @@ export const dict = {
|
||||
"dialog.server.add.error": "Nie można połączyć się z serwerem",
|
||||
"dialog.server.add.checking": "Sprawdzanie...",
|
||||
"dialog.server.add.button": "Dodaj serwer",
|
||||
"dialog.server.add.name": "Nazwa serwera (opcjonalnie)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "Nazwa użytkownika (opcjonalnie)",
|
||||
"dialog.server.add.password": "Hasło (opcjonalnie)",
|
||||
"dialog.server.edit.title": "Edytuj serwer",
|
||||
"dialog.server.default.title": "Domyślny serwer",
|
||||
"dialog.server.default.description":
|
||||
"Połącz z tym serwerem przy uruchomieniu aplikacji zamiast uruchamiać lokalny serwer. Wymaga restartu.",
|
||||
@@ -359,6 +365,7 @@ export const dict = {
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
"toast.language.title": "Język",
|
||||
"toast.language.description": "Przełączono na {{language}}",
|
||||
"toast.theme.title": "Przełączono motyw",
|
||||
@@ -445,9 +452,12 @@ export const dict = {
|
||||
"session.review.change.other": "Zmiany",
|
||||
"session.review.loadingChanges": "Ładowanie zmian...",
|
||||
"session.review.empty": "Brak zmian w tej sesji",
|
||||
"session.review.noVcs": "Nie wykryto systemu kontroli wersji Git, zmiany nie są wyświetlane",
|
||||
"session.review.noSnapshot": "Śledzenie migawek jest wyłączone w konfiguracji, więc zmiany w sesji są niedostępne",
|
||||
"session.review.noChanges": "Brak zmian",
|
||||
"session.files.selectToOpen": "Wybierz plik do otwarcia",
|
||||
"session.files.all": "Wszystkie pliki",
|
||||
"session.files.empty": "Brak plików",
|
||||
"session.files.binaryContent": "Plik binarny (zawartość nie może być wyświetlona)",
|
||||
"session.messages.renderEarlier": "Renderuj wcześniejsze wiadomości",
|
||||
"session.messages.loadingEarlier": "Ładowanie wcześniejszych wiadomości...",
|
||||
@@ -458,6 +468,17 @@ export const dict = {
|
||||
"session.todo.title": "Zadania",
|
||||
"session.todo.collapse": "Zwiń",
|
||||
"session.todo.expand": "Rozwiń",
|
||||
"session.followupDock.summary.one": "{{count}} wiadomość w kolejce",
|
||||
"session.followupDock.summary.other": "{{count}} wiadomości w kolejce",
|
||||
"session.followupDock.sendNow": "Wyślij teraz",
|
||||
"session.followupDock.edit": "Edytuj",
|
||||
"session.followupDock.collapse": "Zwiń wiadomości w kolejce",
|
||||
"session.followupDock.expand": "Rozwiń wiadomości w kolejce",
|
||||
"session.revertDock.summary.one": "{{count}} cofnięta wiadomość",
|
||||
"session.revertDock.summary.other": "{{count}} cofnięte wiadomości",
|
||||
"session.revertDock.collapse": "Zwiń cofnięte wiadomości",
|
||||
"session.revertDock.expand": "Rozwiń cofnięte wiadomości",
|
||||
"session.revertDock.restore": "Przywróć wiadomość",
|
||||
"session.new.title": "Zbuduj cokolwiek",
|
||||
"session.new.worktree.main": "Główna gałąź",
|
||||
"session.new.worktree.mainWithBranch": "Główna gałąź ({{branch}})",
|
||||
@@ -543,10 +564,19 @@ export const dict = {
|
||||
"settings.general.row.language.description": "Zmień język wyświetlania dla OpenCode",
|
||||
"settings.general.row.appearance.title": "Wygląd",
|
||||
"settings.general.row.appearance.description": "Dostosuj wygląd OpenCode na swoim urządzeniu",
|
||||
"settings.general.row.colorScheme.title": "Schemat kolorów",
|
||||
"settings.general.row.colorScheme.description":
|
||||
"Wybierz, czy OpenCode ma używać motywu systemowego, jasnego czy ciemnego",
|
||||
"settings.general.row.theme.title": "Motyw",
|
||||
"settings.general.row.theme.description": "Dostosuj motyw OpenCode.",
|
||||
"settings.general.row.font.title": "Czcionka",
|
||||
"settings.general.row.font.description": "Dostosuj czcionkę mono używaną w blokach kodu",
|
||||
"settings.general.row.followup.title": "Zachowanie kontynuacji",
|
||||
"settings.general.row.followup.description": "Wybierz, czy kontynuacja ma być natychmiastowa, czy czekać w kolejce",
|
||||
"settings.general.row.followup.option.queue": "Kolejka",
|
||||
"settings.general.row.followup.option.steer": "Sterowanie",
|
||||
"settings.general.row.reasoningSummaries.title": "Pokaż podsumowania wnioskowania",
|
||||
"settings.general.row.reasoningSummaries.description": "Wyświetlaj podsumowania wnioskowania modelu na osi czasu",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Rozwijaj elementy narzędzia shell",
|
||||
"settings.general.row.shellToolPartsExpanded.description":
|
||||
"Domyślnie pokazuj rozwinięte elementy narzędzia shell na osi czasu",
|
||||
|
||||
@@ -113,6 +113,7 @@ export const dict = {
|
||||
"dialog.model.empty": "Модели не найдены",
|
||||
"dialog.model.manage": "Управление моделями",
|
||||
"dialog.model.manage.description": "Настройте какие модели появляются в выборе модели",
|
||||
"dialog.model.manage.provider.toggle": "Переключить все модели {{provider}}",
|
||||
|
||||
"dialog.model.unpaid.freeModels.title": "Бесплатные модели от OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "Добавьте больше моделей от популярных провайдеров",
|
||||
@@ -314,6 +315,11 @@ export const dict = {
|
||||
"dialog.server.add.error": "Не удалось подключиться к серверу",
|
||||
"dialog.server.add.checking": "Проверка...",
|
||||
"dialog.server.add.button": "Добавить сервер",
|
||||
"dialog.server.add.name": "Имя сервера (необязательно)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "Имя пользователя (необязательно)",
|
||||
"dialog.server.add.password": "Пароль (необязательно)",
|
||||
"dialog.server.edit.title": "Редактировать сервер",
|
||||
"dialog.server.default.title": "Сервер по умолчанию",
|
||||
"dialog.server.default.description":
|
||||
"Подключаться к этому серверу при запуске приложения вместо запуска локального сервера. Требуется перезапуск.",
|
||||
@@ -393,6 +399,7 @@ export const dict = {
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
|
||||
"toast.language.title": "Язык",
|
||||
"toast.language.description": "Переключено на {{language}}",
|
||||
@@ -499,9 +506,12 @@ export const dict = {
|
||||
"session.review.change.other": "Изменения",
|
||||
"session.review.loadingChanges": "Загрузка изменений...",
|
||||
"session.review.empty": "Изменений в этой сессии пока нет",
|
||||
"session.review.noVcs": "Система контроля версий Git не обнаружена, изменения не отображаются",
|
||||
"session.review.noSnapshot": "Отслеживание снимков отключено в настройках, поэтому изменения сессии недоступны",
|
||||
"session.review.noChanges": "Нет изменений",
|
||||
"session.files.selectToOpen": "Выберите файл, чтобы открыть",
|
||||
"session.files.all": "Все файлы",
|
||||
"session.files.empty": "Нет файлов",
|
||||
"session.files.binaryContent": "Двоичный файл (содержимое не может быть отображено)",
|
||||
"session.messages.renderEarlier": "Показать предыдущие сообщения",
|
||||
"session.messages.loadingEarlier": "Загрузка предыдущих сообщений...",
|
||||
@@ -513,6 +523,17 @@ export const dict = {
|
||||
"session.todo.title": "Задачи",
|
||||
"session.todo.collapse": "Свернуть",
|
||||
"session.todo.expand": "Развернуть",
|
||||
"session.followupDock.summary.one": "{{count}} сообщение в очереди",
|
||||
"session.followupDock.summary.other": "{{count}} сообщений в очереди",
|
||||
"session.followupDock.sendNow": "Отправить сейчас",
|
||||
"session.followupDock.edit": "Редактировать",
|
||||
"session.followupDock.collapse": "Свернуть сообщения в очереди",
|
||||
"session.followupDock.expand": "Развернуть сообщения в очереди",
|
||||
"session.revertDock.summary.one": "{{count}} сообщение возвращено",
|
||||
"session.revertDock.summary.other": "{{count}} сообщений возвращено",
|
||||
"session.revertDock.collapse": "Свернуть возвращённые сообщения",
|
||||
"session.revertDock.expand": "Развернуть возвращённые сообщения",
|
||||
"session.revertDock.restore": "Восстановить сообщение",
|
||||
|
||||
"session.new.title": "Создавайте что угодно",
|
||||
"session.new.worktree.main": "Основная ветка",
|
||||
@@ -610,10 +631,19 @@ export const dict = {
|
||||
"settings.general.row.language.description": "Изменить язык отображения OpenCode",
|
||||
"settings.general.row.appearance.title": "Внешний вид",
|
||||
"settings.general.row.appearance.description": "Настройте как OpenCode выглядит на вашем устройстве",
|
||||
"settings.general.row.colorScheme.title": "Цветовая схема",
|
||||
"settings.general.row.colorScheme.description": "Выберите, следует ли OpenCode системной, светлой или тёмной теме",
|
||||
"settings.general.row.theme.title": "Тема",
|
||||
"settings.general.row.theme.description": "Настройте оформление OpenCode.",
|
||||
"settings.general.row.font.title": "Шрифт",
|
||||
"settings.general.row.font.description": "Настройте моноширинный шрифт для блоков кода",
|
||||
"settings.general.row.followup.title": "Поведение уточняющих вопросов",
|
||||
"settings.general.row.followup.description":
|
||||
"Выберите, отправлять ли уточняющие вопросы сразу или помещать их в очередь",
|
||||
"settings.general.row.followup.option.queue": "Очередь",
|
||||
"settings.general.row.followup.option.steer": "Направлять",
|
||||
"settings.general.row.reasoningSummaries.title": "Показывать сводки рассуждений",
|
||||
"settings.general.row.reasoningSummaries.description": "Отображать сводки рассуждений модели в ленте",
|
||||
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Разворачивать элементы инструмента shell",
|
||||
"settings.general.row.shellToolPartsExpanded.description":
|
||||
@@ -767,30 +797,31 @@ export const dict = {
|
||||
"settings.permissions.tool.glob.description": "Сопоставление файлов по паттернам glob",
|
||||
"settings.permissions.tool.grep.title": "Grep",
|
||||
"settings.permissions.tool.grep.description": "Поиск по содержимому файлов с использованием регулярных выражений",
|
||||
"settings.permissions.tool.list.title": "Список",
|
||||
"settings.permissions.tool.list.title": "List",
|
||||
"settings.permissions.tool.list.description": "Список файлов в директории",
|
||||
"settings.permissions.tool.bash.title": "Bash",
|
||||
"settings.permissions.tool.bash.description": "Выполнение команд оболочки",
|
||||
"settings.permissions.tool.bash.description": "Запуск команд оболочки",
|
||||
"settings.permissions.tool.task.title": "Task",
|
||||
"settings.permissions.tool.task.description": "Запуск под-агентов",
|
||||
"settings.permissions.tool.task.description": "Запуск подагентов",
|
||||
"settings.permissions.tool.skill.title": "Skill",
|
||||
"settings.permissions.tool.skill.description": "Загрузить навык по имени",
|
||||
"settings.permissions.tool.skill.description": "Загрузка навыка по имени",
|
||||
"settings.permissions.tool.lsp.title": "LSP",
|
||||
"settings.permissions.tool.lsp.description": "Выполнение запросов к языковому серверу",
|
||||
"settings.permissions.tool.todoread.title": "Чтение списка задач",
|
||||
"settings.permissions.tool.lsp.description": "Запросы к языковому серверу",
|
||||
"settings.permissions.tool.todoread.title": "Todo Read",
|
||||
"settings.permissions.tool.todoread.description": "Чтение списка задач",
|
||||
"settings.permissions.tool.todowrite.title": "Запись списка задач",
|
||||
"settings.permissions.tool.todowrite.title": "Todo Write",
|
||||
"settings.permissions.tool.todowrite.description": "Обновление списка задач",
|
||||
"settings.permissions.tool.webfetch.title": "Web Fetch",
|
||||
"settings.permissions.tool.webfetch.description": "Получить содержимое по URL",
|
||||
"settings.permissions.tool.webfetch.description": "Получение контента по URL",
|
||||
"settings.permissions.tool.websearch.title": "Web Search",
|
||||
"settings.permissions.tool.websearch.description": "Поиск в интернете",
|
||||
"settings.permissions.tool.codesearch.title": "Поиск кода",
|
||||
"settings.permissions.tool.codesearch.title": "Code Search",
|
||||
"settings.permissions.tool.codesearch.description": "Поиск кода в интернете",
|
||||
"settings.permissions.tool.external_directory.title": "Внешняя директория",
|
||||
"settings.permissions.tool.external_directory.description": "Доступ к файлам вне директории проекта",
|
||||
"settings.permissions.tool.doom_loop.title": "Doom Loop",
|
||||
"settings.permissions.tool.doom_loop.description": "Обнаружение повторных вызовов инструментов с одинаковым вводом",
|
||||
"settings.permissions.tool.doom_loop.description":
|
||||
"Обнаружение повторяющихся вызовов инструментов с одинаковыми входными данными",
|
||||
|
||||
"session.delete.failed.title": "Не удалось удалить сессию",
|
||||
"session.delete.title": "Удалить сессию",
|
||||
@@ -807,21 +838,21 @@ export const dict = {
|
||||
"workspace.reset.failed.title": "Не удалось сбросить рабочее пространство",
|
||||
"workspace.reset.success.title": "Рабочее пространство сброшено",
|
||||
"workspace.reset.success.description": "Рабочее пространство теперь соответствует ветке по умолчанию.",
|
||||
"workspace.error.stillPreparing": "Рабочее пространство всё ещё готовится",
|
||||
"workspace.status.checking": "Проверка наличия неслитых изменений...",
|
||||
"workspace.error.stillPreparing": "Рабочее пространство всё ещё подготавливается",
|
||||
"workspace.status.checking": "Проверка незафиксированных изменений...",
|
||||
"workspace.status.error": "Не удалось проверить статус git.",
|
||||
"workspace.status.clean": "Неслитые изменения не обнаружены.",
|
||||
"workspace.status.dirty": "Обнаружены неслитые изменения в этом рабочем пространстве.",
|
||||
"workspace.status.clean": "Незафиксированных изменений не обнаружено.",
|
||||
"workspace.status.dirty": "В этом рабочем пространстве обнаружены незафиксированные изменения.",
|
||||
"workspace.delete.title": "Удалить рабочее пространство",
|
||||
"workspace.delete.confirm": 'Удалить рабочее пространство "{{name}}"?',
|
||||
"workspace.delete.button": "Удалить рабочее пространство",
|
||||
"workspace.reset.title": "Сбросить рабочее пространство",
|
||||
"workspace.reset.confirm": 'Сбросить рабочее пространство "{{name}}"?',
|
||||
"workspace.reset.button": "Сбросить рабочее пространство",
|
||||
"workspace.reset.archived.none": "Никакие активные сессии не будут архивированы.",
|
||||
"workspace.reset.archived.none": "Активные сессии не будут архивированы.",
|
||||
"workspace.reset.archived.one": "1 сессия будет архивирована.",
|
||||
"workspace.reset.archived.many": "{{count}} сессий будет архивировано.",
|
||||
"workspace.reset.note": "Рабочее пространство будет сброшено в соответствие с веткой по умолчанию.",
|
||||
"workspace.reset.note": "Это сбросит рабочее пространство до соответствия ветке по умолчанию.",
|
||||
"common.open": "Открыть",
|
||||
"dialog.releaseNotes.action.getStarted": "Начать",
|
||||
"dialog.releaseNotes.action.next": "Далее",
|
||||
|
||||
@@ -113,6 +113,7 @@ export const dict = {
|
||||
"dialog.model.empty": "ไม่พบผลลัพธ์โมเดล",
|
||||
"dialog.model.manage": "จัดการโมเดล",
|
||||
"dialog.model.manage.description": "ปรับแต่งโมเดลที่จะปรากฏในตัวเลือกโมเดล",
|
||||
"dialog.model.manage.provider.toggle": "สลับโมเดลทั้งหมดของ {{provider}}",
|
||||
|
||||
"dialog.model.unpaid.freeModels.title": "โมเดลฟรีที่จัดหาให้โดย OpenCode",
|
||||
"dialog.model.unpaid.addMore.title": "เพิ่มโมเดลเพิ่มเติมจากผู้ให้บริการยอดนิยม",
|
||||
@@ -314,6 +315,11 @@ export const dict = {
|
||||
"dialog.server.add.error": "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์",
|
||||
"dialog.server.add.checking": "กำลังตรวจสอบ...",
|
||||
"dialog.server.add.button": "เพิ่มเซิร์ฟเวอร์",
|
||||
"dialog.server.add.name": "ชื่อเซิร์ฟเวอร์ (ไม่บังคับ)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "ชื่อผู้ใช้ (ไม่บังคับ)",
|
||||
"dialog.server.add.password": "รหัสผ่าน (ไม่บังคับ)",
|
||||
"dialog.server.edit.title": "แก้ไขเซิร์ฟเวอร์",
|
||||
"dialog.server.default.title": "เซิร์ฟเวอร์เริ่มต้น",
|
||||
"dialog.server.default.description":
|
||||
"เชื่อมต่อกับเซิร์ฟเวอร์นี้เมื่อเปิดแอปแทนการเริ่มเซิร์ฟเวอร์ในเครื่อง ต้องรีสตาร์ท",
|
||||
@@ -391,6 +397,7 @@ export const dict = {
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
|
||||
"toast.language.title": "ภาษา",
|
||||
"toast.language.description": "สลับไปที่ {{language}}",
|
||||
@@ -494,9 +501,12 @@ export const dict = {
|
||||
"session.review.change.other": "การเปลี่ยนแปลง",
|
||||
"session.review.loadingChanges": "กำลังโหลดการเปลี่ยนแปลง...",
|
||||
"session.review.empty": "ยังไม่มีการเปลี่ยนแปลงในเซสชันนี้",
|
||||
"session.review.noVcs": "ไม่ตรวจพบระบบควบคุมเวอร์ชัน Git การเปลี่ยนแปลงจะไม่แสดง",
|
||||
"session.review.noSnapshot": "การติดตามสแนปชอตถูกปิดใช้งานในการกำหนดค่า ดังนั้นการเปลี่ยนแปลงเซสชันจึงไม่พร้อมใช้งาน",
|
||||
"session.review.noChanges": "ไม่มีการเปลี่ยนแปลง",
|
||||
|
||||
"session.files.selectToOpen": "เลือกไฟล์เพื่อเปิด",
|
||||
"session.files.empty": "ไม่มีไฟล์",
|
||||
"session.files.all": "ไฟล์ทั้งหมด",
|
||||
"session.files.binaryContent": "ไฟล์ไบนารี (ไม่สามารถแสดงเนื้อหาได้)",
|
||||
|
||||
@@ -510,6 +520,17 @@ export const dict = {
|
||||
"session.todo.title": "สิ่งที่ต้องทำ",
|
||||
"session.todo.collapse": "ย่อ",
|
||||
"session.todo.expand": "ขยาย",
|
||||
"session.followupDock.summary.one": "{{count}} ข้อความในคิว",
|
||||
"session.followupDock.summary.other": "{{count}} ข้อความในคิว",
|
||||
"session.followupDock.sendNow": "ส่งทันที",
|
||||
"session.followupDock.edit": "แก้ไข",
|
||||
"session.followupDock.collapse": "ย่อข้อความในคิว",
|
||||
"session.followupDock.expand": "ขยายข้อความในคิว",
|
||||
"session.revertDock.summary.one": "{{count}} ข้อความที่ถูกย้อนกลับ",
|
||||
"session.revertDock.summary.other": "{{count}} ข้อความที่ถูกย้อนกลับ",
|
||||
"session.revertDock.collapse": "ย่อข้อความที่ถูกย้อนกลับ",
|
||||
"session.revertDock.expand": "ขยายข้อความที่ถูกย้อนกลับ",
|
||||
"session.revertDock.restore": "กู้คืนข้อความ",
|
||||
|
||||
"session.new.title": "สร้างอะไรก็ได้",
|
||||
"session.new.worktree.main": "สาขาหลัก",
|
||||
@@ -604,11 +625,18 @@ export const dict = {
|
||||
"settings.general.row.language.description": "เปลี่ยนภาษาที่แสดงสำหรับ OpenCode",
|
||||
"settings.general.row.appearance.title": "รูปลักษณ์",
|
||||
"settings.general.row.appearance.description": "ปรับแต่งวิธีการที่ OpenCode มีลักษณะบนอุปกรณ์ของคุณ",
|
||||
"settings.general.row.colorScheme.title": "โทนสี",
|
||||
"settings.general.row.colorScheme.description": "เลือกว่าจะให้ OpenCode ใช้ธีมตามระบบ สว่าง หรือมืด",
|
||||
"settings.general.row.theme.title": "ธีม",
|
||||
"settings.general.row.theme.description": "ปรับแต่งวิธีการที่ OpenCode มีธีม",
|
||||
"settings.general.row.font.title": "ฟอนต์",
|
||||
"settings.general.row.font.description": "ปรับแต่งฟอนต์โมโนที่ใช้ในบล็อกโค้ด",
|
||||
|
||||
"settings.general.row.followup.title": "พฤติกรรมการติดตามผล",
|
||||
"settings.general.row.followup.description": "เลือกว่าจะให้พร้อมท์ติดตามผลทำงานทันทีหรือรอในคิว",
|
||||
"settings.general.row.followup.option.queue": "คิว",
|
||||
"settings.general.row.followup.option.steer": "นำทาง",
|
||||
"settings.general.row.reasoningSummaries.title": "แสดงสรุปการใช้เหตุผล",
|
||||
"settings.general.row.reasoningSummaries.description": "แสดงสรุปการใช้เหตุผลของโมเดลในไทม์ไลน์",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "ขยายส่วนเครื่องมือ shell",
|
||||
"settings.general.row.shellToolPartsExpanded.description": "แสดงส่วนเครื่องมือ shell แบบขยายตามค่าเริ่มต้นในไทม์ไลน์",
|
||||
"settings.general.row.editToolPartsExpanded.title": "ขยายส่วนเครื่องมือ edit",
|
||||
|
||||
@@ -320,6 +320,11 @@ export const dict = {
|
||||
"dialog.server.add.error": "Sunucuya bağlanılamadı",
|
||||
"dialog.server.add.checking": "Kontrol ediliyor...",
|
||||
"dialog.server.add.button": "Sunucu ekle",
|
||||
"dialog.server.add.name": "Sunucu adı (isteğe bağlı)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "Kullanıcı adı (isteğe bağlı)",
|
||||
"dialog.server.add.password": "Şifre (isteğe bağlı)",
|
||||
"dialog.server.edit.title": "Sunucuyu düzenle",
|
||||
"dialog.server.default.title": "Varsayılan sunucu",
|
||||
"dialog.server.default.description":
|
||||
"Uygulama başlatıldığında yerel sunucu başlatmak yerine bu sunucuya bağlan. Yeniden başlatma gerektirir.",
|
||||
@@ -506,10 +511,13 @@ export const dict = {
|
||||
"session.review.loadingChanges": "Değişiklikler yükleniyor...",
|
||||
"session.review.empty": "Bu oturumda henüz değişiklik yok",
|
||||
"session.review.noVcs": "Git VCS algılanamadı, oturum değişiklikleri tespit edilemeyecek",
|
||||
"session.review.noSnapshot":
|
||||
"Yapılandırmada anlık görüntü takibi devre dışı bırakıldı, bu nedenle oturum değişiklikleri kullanılamıyor",
|
||||
"session.review.noChanges": "Değişiklik yok",
|
||||
|
||||
"session.files.selectToOpen": "Açmak için bir dosya seçin",
|
||||
"session.files.all": "Tüm dosyalar",
|
||||
"session.files.empty": "Dosya yok",
|
||||
"session.files.binaryContent": "İkili dosya (içerik görüntülenemiyor)",
|
||||
|
||||
"session.messages.renderEarlier": "Önceki mesajları göster",
|
||||
@@ -522,6 +530,17 @@ export const dict = {
|
||||
"session.todo.title": "Görevler",
|
||||
"session.todo.collapse": "Daralt",
|
||||
"session.todo.expand": "Genişlet",
|
||||
"session.followupDock.summary.one": "{{count}} sıradaki mesaj",
|
||||
"session.followupDock.summary.other": "{{count}} sıradaki mesaj",
|
||||
"session.followupDock.sendNow": "Şimdi gönder",
|
||||
"session.followupDock.edit": "Düzenle",
|
||||
"session.followupDock.collapse": "Sıradaki mesajları daralt",
|
||||
"session.followupDock.expand": "Sıradaki mesajları genişlet",
|
||||
"session.revertDock.summary.one": "{{count}} geri alınan mesaj",
|
||||
"session.revertDock.summary.other": "{{count}} geri alınan mesaj",
|
||||
"session.revertDock.collapse": "Geri alınan mesajları daralt",
|
||||
"session.revertDock.expand": "Geri alınan mesajları genişlet",
|
||||
"session.revertDock.restore": "Mesajı geri yükle",
|
||||
|
||||
"session.new.title": "İstediğini yap",
|
||||
"session.new.worktree.main": "Ana dal",
|
||||
@@ -618,10 +637,18 @@ export const dict = {
|
||||
"settings.general.row.language.description": "OpenCode'un görünüm dilini değiştirin",
|
||||
"settings.general.row.appearance.title": "Görünüm",
|
||||
"settings.general.row.appearance.description": "OpenCode'un cihazınızdaki görünümünü özelleştirin",
|
||||
"settings.general.row.colorScheme.title": "Renk şeması",
|
||||
"settings.general.row.colorScheme.description":
|
||||
"OpenCode'un sistem, açık veya koyu temayı takip etip etmeyeceğini seçin",
|
||||
"settings.general.row.theme.title": "Tema",
|
||||
"settings.general.row.theme.description": "OpenCode'un temasını özelleştirin.",
|
||||
"settings.general.row.font.title": "Yazı Tipi",
|
||||
"settings.general.row.font.description": "Kod bloklarında kullanılan monospace yazı tipini özelleştirin",
|
||||
"settings.general.row.followup.title": "Takip davranışı",
|
||||
"settings.general.row.followup.description":
|
||||
"Takip komutlarının hemen yönlendirilmesini mi yoksa sırada beklemesini mi istediğinizi seçin",
|
||||
"settings.general.row.followup.option.queue": "Sıra",
|
||||
"settings.general.row.followup.option.steer": "Yönlendir",
|
||||
"settings.general.row.reasoningSummaries.title": "Akıl yürütme özetlerini göster",
|
||||
"settings.general.row.reasoningSummaries.description": "Zaman çizelgesinde model akıl yürütme özetlerini görüntüle",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "Kabuk araç bileşenlerini genişlet",
|
||||
|
||||
@@ -140,6 +140,7 @@ export const dict = {
|
||||
"dialog.model.empty": "未找到模型",
|
||||
"dialog.model.manage": "管理模型",
|
||||
"dialog.model.manage.description": "自定义模型选择器中显示的模型。",
|
||||
"dialog.model.manage.provider.toggle": "切换所有 {{provider}} 模型",
|
||||
"dialog.model.unpaid.freeModels.title": "OpenCode 提供的免费模型",
|
||||
"dialog.model.unpaid.addMore.title": "从热门提供商添加更多模型",
|
||||
|
||||
@@ -334,6 +335,11 @@ export const dict = {
|
||||
"dialog.server.add.error": "无法连接到服务器",
|
||||
"dialog.server.add.checking": "检查中...",
|
||||
"dialog.server.add.button": "添加服务器",
|
||||
"dialog.server.add.name": "服务器名称(可选)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "用户名(可选)",
|
||||
"dialog.server.add.password": "密码(可选)",
|
||||
"dialog.server.edit.title": "编辑服务器",
|
||||
"dialog.server.default.title": "默认服务器",
|
||||
"dialog.server.default.description": "应用启动时连接此服务器,而不是启动本地服务器。需要重启。",
|
||||
"dialog.server.default.none": "未选择服务器",
|
||||
@@ -406,6 +412,7 @@ export const dict = {
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
|
||||
"toast.language.title": "语言",
|
||||
"toast.language.description": "已切换到{{language}}",
|
||||
@@ -497,9 +504,12 @@ export const dict = {
|
||||
"session.review.change.other": "更改",
|
||||
"session.review.loadingChanges": "正在加载更改...",
|
||||
"session.review.empty": "此会话暂无更改",
|
||||
"session.review.noVcs": "未检测到 Git 版本控制系统,无法显示更改",
|
||||
"session.review.noSnapshot": "配置中已禁用快照跟踪,因此会话更改不可用",
|
||||
"session.review.noChanges": "无更改",
|
||||
"session.files.selectToOpen": "选择要打开的文件",
|
||||
"session.files.all": "所有文件",
|
||||
"session.files.empty": "无文件",
|
||||
"session.files.binaryContent": "二进制文件(无法显示内容)",
|
||||
"session.messages.renderEarlier": "显示更早的消息",
|
||||
"session.messages.loadingEarlier": "正在加载更早的消息...",
|
||||
@@ -510,6 +520,17 @@ export const dict = {
|
||||
"session.todo.title": "待办事项",
|
||||
"session.todo.collapse": "折叠",
|
||||
"session.todo.expand": "展开",
|
||||
"session.followupDock.summary.one": "{{count}} 条排队消息",
|
||||
"session.followupDock.summary.other": "{{count}} 条排队消息",
|
||||
"session.followupDock.sendNow": "立即发送",
|
||||
"session.followupDock.edit": "编辑",
|
||||
"session.followupDock.collapse": "折叠排队消息",
|
||||
"session.followupDock.expand": "展开排队消息",
|
||||
"session.revertDock.summary.one": "{{count}} 条已回滚消息",
|
||||
"session.revertDock.summary.other": "{{count}} 条已回滚消息",
|
||||
"session.revertDock.collapse": "折叠已回滚消息",
|
||||
"session.revertDock.expand": "展开已回滚消息",
|
||||
"session.revertDock.restore": "恢复消息",
|
||||
"session.new.title": "构建任何东西",
|
||||
"session.new.worktree.main": "主分支",
|
||||
"session.new.worktree.mainWithBranch": "主分支({{branch}})",
|
||||
@@ -604,10 +625,18 @@ export const dict = {
|
||||
"settings.general.row.language.description": "更改 OpenCode 的显示语言",
|
||||
"settings.general.row.appearance.title": "外观",
|
||||
"settings.general.row.appearance.description": "自定义 OpenCode 在你的设备上的外观",
|
||||
"settings.general.row.colorScheme.title": "配色方案",
|
||||
"settings.general.row.colorScheme.description": "选择 OpenCode 跟随系统、浅色或深色主题",
|
||||
"settings.general.row.theme.title": "主题",
|
||||
"settings.general.row.theme.description": "自定义 OpenCode 的主题。",
|
||||
"settings.general.row.font.title": "字体",
|
||||
"settings.general.row.font.description": "自定义代码块使用的等宽字体",
|
||||
"settings.general.row.followup.title": "跟进消息行为",
|
||||
"settings.general.row.followup.description": "选择跟进提示是立即引导还是在队列中等待",
|
||||
"settings.general.row.followup.option.queue": "排队",
|
||||
"settings.general.row.followup.option.steer": "引导",
|
||||
"settings.general.row.reasoningSummaries.title": "显示推理摘要",
|
||||
"settings.general.row.reasoningSummaries.description": "在时间线中显示模型推理摘要",
|
||||
"settings.general.row.shellToolPartsExpanded.title": "展开 shell 工具部分",
|
||||
"settings.general.row.shellToolPartsExpanded.description": "默认在时间线中展开 shell 工具部分",
|
||||
"settings.general.row.editToolPartsExpanded.title": "展开编辑工具部分",
|
||||
|
||||
@@ -117,6 +117,7 @@ export const dict = {
|
||||
"dialog.model.empty": "找不到模型",
|
||||
"dialog.model.manage": "管理模型",
|
||||
"dialog.model.manage.description": "自訂模型選擇器中顯示的模型。",
|
||||
"dialog.model.manage.provider.toggle": "切換所有 {{provider}} 模型",
|
||||
|
||||
"dialog.model.unpaid.freeModels.title": "OpenCode 提供的免費模型",
|
||||
"dialog.model.unpaid.addMore.title": "從熱門提供者新增更多模型",
|
||||
@@ -314,6 +315,11 @@ export const dict = {
|
||||
"dialog.server.add.error": "無法連線到伺服器",
|
||||
"dialog.server.add.checking": "檢查中...",
|
||||
"dialog.server.add.button": "新增伺服器",
|
||||
"dialog.server.add.name": "伺服器名稱(選填)",
|
||||
"dialog.server.add.namePlaceholder": "Localhost",
|
||||
"dialog.server.add.username": "使用者名稱(選填)",
|
||||
"dialog.server.add.password": "密碼(選填)",
|
||||
"dialog.server.edit.title": "編輯伺服器",
|
||||
"dialog.server.default.title": "預設伺服器",
|
||||
"dialog.server.default.description": "應用程式啟動時連線此伺服器,而不是啟動本地伺服器。需要重新啟動。",
|
||||
"dialog.server.default.none": "未選擇伺服器",
|
||||
@@ -390,6 +396,7 @@ export const dict = {
|
||||
"language.br": "Português (Brasil)",
|
||||
"language.bs": "Bosanski",
|
||||
"language.th": "ไทย",
|
||||
"language.tr": "Türkçe",
|
||||
|
||||
"toast.language.title": "語言",
|
||||
"toast.language.description": "已切換到 {{language}}",
|
||||
@@ -493,8 +500,11 @@ export const dict = {
|
||||
"session.review.loadingChanges": "正在載入變更...",
|
||||
"session.review.empty": "此工作階段暫無變更",
|
||||
"session.review.noChanges": "沒有變更",
|
||||
"session.review.noVcs": "未偵測到 Git 版本控制系統,無法顯示變更",
|
||||
"session.review.noSnapshot": "設定中已停用快照追蹤,因此無法使用工作階段變更",
|
||||
"session.files.selectToOpen": "選取要開啟的檔案",
|
||||
"session.files.all": "所有檔案",
|
||||
"session.files.empty": "沒有檔案",
|
||||
"session.files.binaryContent": "二進位檔案(無法顯示內容)",
|
||||
"session.messages.renderEarlier": "顯示更早的訊息",
|
||||
"session.messages.loadingEarlier": "正在載入更早的訊息...",
|
||||
@@ -506,6 +516,17 @@ export const dict = {
|
||||
"session.todo.title": "待辦事項",
|
||||
"session.todo.collapse": "折疊",
|
||||
"session.todo.expand": "展開",
|
||||
"session.followupDock.summary.one": "{{count}} 則佇列訊息",
|
||||
"session.followupDock.summary.other": "{{count}} 則佇列訊息",
|
||||
"session.followupDock.sendNow": "立即傳送",
|
||||
"session.followupDock.edit": "編輯",
|
||||
"session.followupDock.collapse": "收合佇列訊息",
|
||||
"session.followupDock.expand": "展開佇列訊息",
|
||||
"session.revertDock.summary.one": "{{count}} 則已回復訊息",
|
||||
"session.revertDock.summary.other": "{{count}} 則已回復訊息",
|
||||
"session.revertDock.collapse": "收合已回復訊息",
|
||||
"session.revertDock.expand": "展開已回復訊息",
|
||||
"session.revertDock.restore": "還原訊息",
|
||||
|
||||
"session.new.title": "建構任何東西",
|
||||
"session.new.worktree.main": "主分支",
|
||||
@@ -585,8 +606,8 @@ export const dict = {
|
||||
"settings.tab.general": "一般",
|
||||
"settings.tab.shortcuts": "快速鍵",
|
||||
"settings.desktop.section.wsl": "WSL",
|
||||
"settings.desktop.wsl.title": "WSL integration",
|
||||
"settings.desktop.wsl.description": "Run the OpenCode server inside WSL on Windows.",
|
||||
"settings.desktop.wsl.title": "WSL 整合",
|
||||
"settings.desktop.wsl.description": "在 Windows 上的 WSL 中執行 OpenCode 伺服器。",
|
||||
|
||||
"settings.general.section.appearance": "外觀",
|
||||
"settings.general.section.notifications": "系統通知",
|
||||
@@ -599,10 +620,18 @@ export const dict = {
|
||||
"settings.general.row.language.description": "變更 OpenCode 的顯示語言",
|
||||
"settings.general.row.appearance.title": "外觀",
|
||||
"settings.general.row.appearance.description": "自訂 OpenCode 在你的裝置上的外觀",
|
||||
"settings.general.row.colorScheme.title": "配色方案",
|
||||
"settings.general.row.colorScheme.description": "選擇 OpenCode 要跟隨系統、淺色或深色主題",
|
||||
"settings.general.row.theme.title": "主題",
|
||||
"settings.general.row.theme.description": "自訂 OpenCode 的主題。",
|
||||
"settings.general.row.font.title": "字型",
|
||||
"settings.general.row.font.description": "自訂程式碼區塊使用的等寬字型",
|
||||
"settings.general.row.followup.title": "後續追問行為",
|
||||
"settings.general.row.followup.description": "選擇後續追問提示是立即引導還是進入佇列等待",
|
||||
"settings.general.row.followup.option.queue": "佇列",
|
||||
"settings.general.row.followup.option.steer": "引導",
|
||||
"settings.general.row.reasoningSummaries.title": "顯示推理摘要",
|
||||
"settings.general.row.reasoningSummaries.description": "在時間軸中顯示模型推理摘要",
|
||||
|
||||
"settings.general.row.shellToolPartsExpanded.title": "展開 shell 工具區塊",
|
||||
"settings.general.row.shellToolPartsExpanded.description": "在時間軸中預設展開 shell 工具區塊",
|
||||
|
||||
@@ -2203,8 +2203,8 @@ export default function Layout(props: ParentProps) {
|
||||
mobile ? (
|
||||
<SidebarPanel project={currentProject()} mobile />
|
||||
) : (
|
||||
<Show when={currentProject()} keyed>
|
||||
{(project) => <SidebarPanel project={project} merged />}
|
||||
<Show when={currentProject()}>
|
||||
<SidebarPanel project={currentProject()} merged />
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
@@ -2332,8 +2332,8 @@ export default function Layout(props: ParentProps) {
|
||||
arm()
|
||||
}}
|
||||
>
|
||||
<Show when={peek()} keyed>
|
||||
{(project) => <SidebarPanel project={project} merged={false} />}
|
||||
<Show when={peek()}>
|
||||
<SidebarPanel project={peek()} merged={false} />
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Project, UserMessage } from "@opencode-ai/sdk/v2"
|
||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||
import {
|
||||
batch,
|
||||
onCleanup,
|
||||
Show,
|
||||
Match,
|
||||
@@ -34,8 +35,10 @@ import { useLanguage } from "@/context/language"
|
||||
import { useLayout } from "@/context/layout"
|
||||
import { usePrompt } from "@/context/prompt"
|
||||
import { useSDK } from "@/context/sdk"
|
||||
import { useSettings } from "@/context/settings"
|
||||
import { useSync } from "@/context/sync"
|
||||
import { useTerminal } from "@/context/terminal"
|
||||
import { type FollowupDraft, sendFollowupDraft } from "@/components/prompt-input/submit"
|
||||
import { createSessionComposerState, SessionComposerRegion } from "@/pages/session/composer"
|
||||
import { createOpenReviewFile, createSessionTabs, createSizing, focusTerminalById } from "@/pages/session/helpers"
|
||||
import { MessageTimeline } from "@/pages/session/message-timeline"
|
||||
@@ -46,15 +49,18 @@ import { SessionSidePanel } from "@/pages/session/session-side-panel"
|
||||
import { TerminalPanel } from "@/pages/session/terminal-panel"
|
||||
import { useSessionCommands } from "@/pages/session/use-session-commands"
|
||||
import { useSessionHashScroll } from "@/pages/session/use-session-hash-scroll"
|
||||
import { Identifier } from "@/utils/id"
|
||||
import { extractPromptFromParts } from "@/utils/prompt"
|
||||
import { same } from "@/utils/same"
|
||||
import { formatServerError } from "@/utils/server-errors"
|
||||
|
||||
const emptyUserMessages: UserMessage[] = []
|
||||
const emptyFollowups: (FollowupDraft & { id: string })[] = []
|
||||
|
||||
type SessionHistoryWindowInput = {
|
||||
sessionID: () => string | undefined
|
||||
messagesReady: () => boolean
|
||||
loaded: () => number
|
||||
visibleUserMessages: () => UserMessage[]
|
||||
historyMore: () => boolean
|
||||
historyLoading: () => boolean
|
||||
@@ -152,23 +158,39 @@ function createSessionHistoryWindow(input: SessionHistoryWindowInput) {
|
||||
|
||||
const start = turnStart()
|
||||
const beforeVisible = input.visibleUserMessages().length
|
||||
let loaded = input.loaded()
|
||||
|
||||
if (start > 0) setTurnStart(0)
|
||||
|
||||
if (!input.historyMore() || input.historyLoading()) return
|
||||
|
||||
await input.loadMore(id)
|
||||
if (input.sessionID() !== id) return
|
||||
let afterVisible = beforeVisible
|
||||
let added = 0
|
||||
|
||||
const afterVisible = input.visibleUserMessages().length
|
||||
const growth = afterVisible - beforeVisible
|
||||
while (true) {
|
||||
await input.loadMore(id)
|
||||
if (input.sessionID() !== id) return
|
||||
|
||||
afterVisible = input.visibleUserMessages().length
|
||||
const nextLoaded = input.loaded()
|
||||
const raw = nextLoaded - loaded
|
||||
added += raw
|
||||
loaded = nextLoaded
|
||||
|
||||
if (afterVisible > beforeVisible) break
|
||||
if (raw <= 0) break
|
||||
if (!input.historyMore()) break
|
||||
}
|
||||
|
||||
if (added <= 0) return
|
||||
if (state.prefetchNoGrowth) setState("prefetchNoGrowth", 0)
|
||||
|
||||
const growth = afterVisible - beforeVisible
|
||||
if (growth <= 0) return
|
||||
if (turnStart() !== 0) return
|
||||
|
||||
const target = Math.min(afterVisible, Math.max(beforeVisible, renderedUserMessages().length) + turnBatch)
|
||||
const nextStart = Math.max(0, afterVisible - target)
|
||||
preserveScroll(() => setTurnStart(nextStart))
|
||||
const target = Math.min(afterVisible, beforeVisible + turnBatch)
|
||||
setTurnStart(Math.max(0, afterVisible - target))
|
||||
}
|
||||
|
||||
/** Scroll/prefetch path: fetch older history from server. */
|
||||
@@ -187,19 +209,35 @@ function createSessionHistoryWindow(input: SessionHistoryWindowInput) {
|
||||
const start = turnStart()
|
||||
const beforeVisible = input.visibleUserMessages().length
|
||||
const beforeRendered = start <= 0 ? beforeVisible : renderedUserMessages().length
|
||||
let loaded = input.loaded()
|
||||
let added = 0
|
||||
let growth = 0
|
||||
|
||||
await input.loadMore(id)
|
||||
if (input.sessionID() !== id) return
|
||||
while (true) {
|
||||
await input.loadMore(id)
|
||||
if (input.sessionID() !== id) return
|
||||
|
||||
const nextLoaded = input.loaded()
|
||||
const raw = nextLoaded - loaded
|
||||
added += raw
|
||||
loaded = nextLoaded
|
||||
growth = input.visibleUserMessages().length - beforeVisible
|
||||
|
||||
if (growth > 0) break
|
||||
if (raw <= 0) break
|
||||
if (opts?.prefetch) break
|
||||
if (!input.historyMore()) break
|
||||
}
|
||||
|
||||
const afterVisible = input.visibleUserMessages().length
|
||||
const growth = afterVisible - beforeVisible
|
||||
|
||||
if (opts?.prefetch) {
|
||||
setState("prefetchNoGrowth", growth > 0 ? 0 : state.prefetchNoGrowth + 1)
|
||||
} else if (growth > 0 && state.prefetchNoGrowth) {
|
||||
setState("prefetchNoGrowth", added > 0 ? 0 : state.prefetchNoGrowth + 1)
|
||||
} else if (added > 0 && state.prefetchNoGrowth) {
|
||||
setState("prefetchNoGrowth", 0)
|
||||
}
|
||||
|
||||
if (added <= 0) return
|
||||
if (growth <= 0) return
|
||||
if (turnStart() !== start) return
|
||||
|
||||
@@ -269,6 +307,7 @@ export default function Page() {
|
||||
const language = useLanguage()
|
||||
const navigate = useNavigate()
|
||||
const sdk = useSDK()
|
||||
const settings = useSettings()
|
||||
const prompt = usePrompt()
|
||||
const comments = useComments()
|
||||
const terminal = useTerminal()
|
||||
@@ -291,6 +330,7 @@ export default function Page() {
|
||||
git: false,
|
||||
pendingMessage: undefined as string | undefined,
|
||||
restoring: undefined as string | undefined,
|
||||
reverting: false,
|
||||
reviewSnap: false,
|
||||
scrollGesture: 0,
|
||||
scroll: {
|
||||
@@ -464,6 +504,17 @@ export default function Page() {
|
||||
deferRender: false,
|
||||
})
|
||||
|
||||
const [followup, setFollowup] = createStore({
|
||||
items: {} as Record<string, (FollowupDraft & { id: string })[] | undefined>,
|
||||
sending: {} as Record<string, string | undefined>,
|
||||
failed: {} as Record<string, string | undefined>,
|
||||
paused: {} as Record<string, boolean | undefined>,
|
||||
edit: {} as Record<
|
||||
string,
|
||||
{ id: string; prompt: FollowupDraft["prompt"]; context: FollowupDraft["context"] } | undefined
|
||||
>,
|
||||
})
|
||||
|
||||
createComputed((prev) => {
|
||||
const key = sessionKey()
|
||||
if (key !== prev) {
|
||||
@@ -1143,6 +1194,7 @@ export default function Page() {
|
||||
|
||||
let scrollStateFrame: number | undefined
|
||||
let scrollStateTarget: HTMLDivElement | undefined
|
||||
let fillFrame: number | undefined
|
||||
|
||||
const updateScrollState = (el: HTMLDivElement) => {
|
||||
const max = el.scrollHeight - el.clientHeight
|
||||
@@ -1190,10 +1242,14 @@ export default function Page() {
|
||||
),
|
||||
)
|
||||
|
||||
let fill = () => {}
|
||||
|
||||
const setScrollRef = (el: HTMLDivElement | undefined) => {
|
||||
scroller = el
|
||||
autoScroll.scrollRef(el)
|
||||
if (el) scheduleScrollState(el)
|
||||
if (!el) return
|
||||
scheduleScrollState(el)
|
||||
fill()
|
||||
}
|
||||
|
||||
const markUserScroll = () => {
|
||||
@@ -1205,12 +1261,14 @@ export default function Page() {
|
||||
() => {
|
||||
const el = scroller
|
||||
if (el) scheduleScrollState(el)
|
||||
fill()
|
||||
},
|
||||
)
|
||||
|
||||
const historyWindow = createSessionHistoryWindow({
|
||||
sessionID: () => params.id,
|
||||
messagesReady,
|
||||
loaded: () => messages().length,
|
||||
visibleUserMessages,
|
||||
historyMore,
|
||||
historyLoading,
|
||||
@@ -1219,6 +1277,45 @@ export default function Page() {
|
||||
scroller: () => scroller,
|
||||
})
|
||||
|
||||
fill = () => {
|
||||
if (fillFrame !== undefined) return
|
||||
|
||||
fillFrame = requestAnimationFrame(() => {
|
||||
fillFrame = undefined
|
||||
|
||||
if (!params.id || !messagesReady()) return
|
||||
if (autoScroll.userScrolled() || historyLoading()) return
|
||||
|
||||
const el = scroller
|
||||
if (!el) return
|
||||
if (el.scrollHeight > el.clientHeight + 1) return
|
||||
if (historyWindow.turnStart() <= 0 && !historyMore()) return
|
||||
|
||||
void historyWindow.loadAndReveal()
|
||||
})
|
||||
}
|
||||
|
||||
createEffect(
|
||||
on(
|
||||
() =>
|
||||
[
|
||||
params.id,
|
||||
messagesReady(),
|
||||
historyWindow.turnStart(),
|
||||
historyMore(),
|
||||
historyLoading(),
|
||||
autoScroll.userScrolled(),
|
||||
visibleUserMessages().length,
|
||||
] as const,
|
||||
([id, ready, start, more, loading, scrolled]) => {
|
||||
if (!id || !ready || loading || scrolled) return
|
||||
if (start <= 0 && !more) return
|
||||
fill()
|
||||
},
|
||||
{ defer: true },
|
||||
),
|
||||
)
|
||||
|
||||
const draft = (id: string) =>
|
||||
extractPromptFromParts(sync.data.part[id] ?? [], {
|
||||
directory: sdk.directory,
|
||||
@@ -1243,13 +1340,136 @@ export default function Page() {
|
||||
})
|
||||
}
|
||||
|
||||
const merge = (next: NonNullable<ReturnType<typeof info>>) =>
|
||||
sync.set("session", (list) => {
|
||||
const idx = list.findIndex((item) => item.id === next.id)
|
||||
if (idx < 0) return list
|
||||
const out = list.slice()
|
||||
out[idx] = next
|
||||
return out
|
||||
})
|
||||
|
||||
const roll = (sessionID: string, next: NonNullable<ReturnType<typeof info>>["revert"]) =>
|
||||
sync.set("session", (list) => {
|
||||
const idx = list.findIndex((item) => item.id === sessionID)
|
||||
if (idx < 0) return list
|
||||
const out = list.slice()
|
||||
out[idx] = { ...out[idx], revert: next }
|
||||
return out
|
||||
})
|
||||
|
||||
const busy = (sessionID: string) => {
|
||||
if (sync.data.session_status[sessionID]?.type !== "idle") return true
|
||||
if ((sync.data.session_status[sessionID] ?? { type: "idle" as const }).type !== "idle") return true
|
||||
return (sync.data.message[sessionID] ?? []).some(
|
||||
(item) => item.role === "assistant" && typeof item.time.completed !== "number",
|
||||
)
|
||||
}
|
||||
|
||||
const queuedFollowups = createMemo(() => {
|
||||
const id = params.id
|
||||
if (!id) return emptyFollowups
|
||||
return followup.items[id] ?? emptyFollowups
|
||||
})
|
||||
|
||||
const editingFollowup = createMemo(() => {
|
||||
const id = params.id
|
||||
if (!id) return
|
||||
return followup.edit[id]
|
||||
})
|
||||
|
||||
const sendingFollowup = createMemo(() => {
|
||||
const id = params.id
|
||||
if (!id) return
|
||||
return followup.sending[id]
|
||||
})
|
||||
|
||||
const queueEnabled = createMemo(() => {
|
||||
const id = params.id
|
||||
if (!id) return false
|
||||
return settings.general.followup() === "queue" && busy(id) && !composer.blocked()
|
||||
})
|
||||
|
||||
const followupText = (item: FollowupDraft) => {
|
||||
const text = item.prompt
|
||||
.map((part) => {
|
||||
if (part.type === "image") return `[image:${part.filename}]`
|
||||
if (part.type === "file") return `[file:${part.path}]`
|
||||
if (part.type === "agent") return `@${part.name}`
|
||||
return part.content
|
||||
})
|
||||
.join("")
|
||||
.split(/\r?\n/)
|
||||
.map((line) => line.trim())
|
||||
.find((line) => !!line)
|
||||
|
||||
if (text) return text
|
||||
return `[${language.t("common.attachment")}]`
|
||||
}
|
||||
|
||||
const queueFollowup = (draft: FollowupDraft) => {
|
||||
setFollowup("items", draft.sessionID, (items) => [
|
||||
...(items ?? []),
|
||||
{ id: Identifier.ascending("message"), ...draft },
|
||||
])
|
||||
setFollowup("failed", draft.sessionID, undefined)
|
||||
setFollowup("paused", draft.sessionID, undefined)
|
||||
}
|
||||
|
||||
const followupDock = createMemo(() => queuedFollowups().map((item) => ({ id: item.id, text: followupText(item) })))
|
||||
|
||||
const sendFollowup = (sessionID: string, id: string, opts?: { manual?: boolean }) => {
|
||||
const item = (followup.items[sessionID] ?? []).find((entry) => entry.id === id)
|
||||
if (!item) return Promise.resolve()
|
||||
if (followup.sending[sessionID]) return Promise.resolve()
|
||||
|
||||
if (opts?.manual) setFollowup("paused", sessionID, undefined)
|
||||
setFollowup("sending", sessionID, id)
|
||||
setFollowup("failed", sessionID, undefined)
|
||||
|
||||
return sendFollowupDraft({
|
||||
client: sdk.client,
|
||||
sync,
|
||||
globalSync,
|
||||
draft: item,
|
||||
optimisticBusy: item.sessionDirectory === sdk.directory,
|
||||
})
|
||||
.then((ok) => {
|
||||
if (ok === false) return
|
||||
setFollowup("items", sessionID, (items) => (items ?? []).filter((entry) => entry.id !== id))
|
||||
if (opts?.manual) resumeScroll()
|
||||
})
|
||||
.catch((err) => {
|
||||
setFollowup("failed", sessionID, id)
|
||||
fail(err)
|
||||
})
|
||||
.finally(() => {
|
||||
setFollowup("sending", sessionID, (value) => (value === id ? undefined : value))
|
||||
})
|
||||
}
|
||||
|
||||
const editFollowup = (id: string) => {
|
||||
const sessionID = params.id
|
||||
if (!sessionID) return
|
||||
if (followup.sending[sessionID]) return
|
||||
|
||||
const item = queuedFollowups().find((entry) => entry.id === id)
|
||||
if (!item) return
|
||||
|
||||
setFollowup("items", sessionID, (items) => (items ?? []).filter((entry) => entry.id !== id))
|
||||
setFollowup("failed", sessionID, (value) => (value === id ? undefined : value))
|
||||
setFollowup("edit", sessionID, {
|
||||
id: item.id,
|
||||
prompt: item.prompt,
|
||||
context: item.context,
|
||||
})
|
||||
}
|
||||
|
||||
const clearFollowupEdit = () => {
|
||||
const id = params.id
|
||||
if (!id) return
|
||||
setFollowup("edit", id, undefined)
|
||||
}
|
||||
|
||||
const halt = (sessionID: string) =>
|
||||
busy(sessionID) ? sdk.client.session.abort({ sessionID }).catch(() => {}) : Promise.resolve()
|
||||
|
||||
@@ -1275,42 +1495,77 @@ export default function Page() {
|
||||
}
|
||||
|
||||
const revert = (input: { sessionID: string; messageID: string }) => {
|
||||
if (ui.reverting || ui.restoring) return
|
||||
const prev = prompt.current().slice()
|
||||
const last = info()?.revert
|
||||
const value = draft(input.messageID)
|
||||
batch(() => {
|
||||
setUi("reverting", true)
|
||||
roll(input.sessionID, { messageID: input.messageID })
|
||||
prompt.set(value)
|
||||
})
|
||||
return halt(input.sessionID)
|
||||
.then(() => sdk.client.session.revert(input))
|
||||
.then(() => {
|
||||
prompt.set(value)
|
||||
.then((result) => {
|
||||
if (result.data) merge(result.data)
|
||||
})
|
||||
.catch((err) => {
|
||||
batch(() => {
|
||||
roll(input.sessionID, last)
|
||||
prompt.set(prev)
|
||||
})
|
||||
fail(err)
|
||||
})
|
||||
.finally(() => {
|
||||
setUi("reverting", false)
|
||||
})
|
||||
.catch(fail)
|
||||
}
|
||||
|
||||
const restore = (id: string) => {
|
||||
const sessionID = params.id
|
||||
if (!sessionID || ui.restoring) return
|
||||
if (!sessionID || ui.restoring || ui.reverting) return
|
||||
|
||||
const next = userMessages().find((item) => item.id > id)
|
||||
setUi("restoring", id)
|
||||
const prev = prompt.current().slice()
|
||||
const last = info()?.revert
|
||||
|
||||
batch(() => {
|
||||
setUi("restoring", id)
|
||||
setUi("reverting", true)
|
||||
roll(sessionID, next ? { messageID: next.id } : undefined)
|
||||
if (next) {
|
||||
prompt.set(draft(next.id))
|
||||
return
|
||||
}
|
||||
prompt.reset()
|
||||
})
|
||||
|
||||
const task = !next
|
||||
? halt(sessionID)
|
||||
.then(() => sdk.client.session.unrevert({ sessionID }))
|
||||
.then(() => {
|
||||
prompt.reset()
|
||||
})
|
||||
: halt(sessionID)
|
||||
.then(() =>
|
||||
sdk.client.session.revert({
|
||||
sessionID,
|
||||
messageID: next.id,
|
||||
}),
|
||||
)
|
||||
.then(() => {
|
||||
prompt.set(draft(next.id))
|
||||
})
|
||||
? halt(sessionID).then(() => sdk.client.session.unrevert({ sessionID }))
|
||||
: halt(sessionID).then(() =>
|
||||
sdk.client.session.revert({
|
||||
sessionID,
|
||||
messageID: next.id,
|
||||
}),
|
||||
)
|
||||
|
||||
return task.catch(fail).finally(() => {
|
||||
setUi("restoring", (value) => (value === id ? undefined : value))
|
||||
})
|
||||
return task
|
||||
.then((result) => {
|
||||
if (result.data) merge(result.data)
|
||||
})
|
||||
.catch((err) => {
|
||||
batch(() => {
|
||||
roll(sessionID, last)
|
||||
prompt.set(prev)
|
||||
})
|
||||
fail(err)
|
||||
})
|
||||
.finally(() => {
|
||||
batch(() => {
|
||||
setUi("restoring", (value) => (value === id ? undefined : value))
|
||||
setUi("reverting", false)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const rolled = createMemo(() => {
|
||||
@@ -1323,6 +1578,21 @@ export default function Page() {
|
||||
|
||||
const actions = { fork, revert }
|
||||
|
||||
createEffect(() => {
|
||||
const sessionID = params.id
|
||||
if (!sessionID) return
|
||||
|
||||
const item = queuedFollowups()[0]
|
||||
if (!item) return
|
||||
if (followup.sending[sessionID]) return
|
||||
if (followup.failed[sessionID] === item.id) return
|
||||
if (followup.paused[sessionID]) return
|
||||
if (composer.blocked()) return
|
||||
if (busy(sessionID)) return
|
||||
|
||||
void sendFollowup(sessionID, item.id)
|
||||
})
|
||||
|
||||
createResizeObserver(
|
||||
() => promptDock,
|
||||
({ height }) => {
|
||||
@@ -1341,6 +1611,7 @@ export default function Page() {
|
||||
if (stick) autoScroll.forceScrollToBottom()
|
||||
|
||||
if (el) scheduleScrollState(el)
|
||||
fill()
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1374,6 +1645,7 @@ export default function Page() {
|
||||
if (diffFrame !== undefined) cancelAnimationFrame(diffFrame)
|
||||
if (diffTimer !== undefined) window.clearTimeout(diffTimer)
|
||||
if (scrollStateFrame !== undefined) cancelAnimationFrame(scrollStateFrame)
|
||||
if (fillFrame !== undefined) cancelAnimationFrame(fillFrame)
|
||||
})
|
||||
|
||||
return (
|
||||
@@ -1482,11 +1754,33 @@ export default function Page() {
|
||||
resumeScroll()
|
||||
}}
|
||||
onResponseSubmit={resumeScroll}
|
||||
followup={
|
||||
params.id
|
||||
? {
|
||||
queue: queueEnabled,
|
||||
items: followupDock(),
|
||||
sending: sendingFollowup(),
|
||||
edit: editingFollowup(),
|
||||
onQueue: queueFollowup,
|
||||
onAbort: () => {
|
||||
const id = params.id
|
||||
if (!id) return
|
||||
setFollowup("paused", id, true)
|
||||
},
|
||||
onSend: (id) => {
|
||||
void sendFollowup(params.id!, id, { manual: true })
|
||||
},
|
||||
onEdit: editFollowup,
|
||||
onEditLoaded: clearFollowupEdit,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
revert={
|
||||
rolled().length > 0
|
||||
? {
|
||||
items: rolled(),
|
||||
restoring: ui.restoring,
|
||||
disabled: ui.reverting,
|
||||
onRestore: restore,
|
||||
}
|
||||
: undefined
|
||||
|
||||
@@ -8,9 +8,11 @@ import { getSessionHandoff, setSessionHandoff } from "@/pages/session/handoff"
|
||||
import { useSessionKey } from "@/pages/session/session-layout"
|
||||
import { SessionPermissionDock } from "@/pages/session/composer/session-permission-dock"
|
||||
import { SessionQuestionDock } from "@/pages/session/composer/session-question-dock"
|
||||
import { SessionFollowupDock } from "@/pages/session/composer/session-followup-dock"
|
||||
import { SessionRevertDock } from "@/pages/session/composer/session-revert-dock"
|
||||
import type { SessionComposerState } from "@/pages/session/composer/session-composer-state"
|
||||
import { SessionTodoDock } from "@/pages/session/composer/session-todo-dock"
|
||||
import type { FollowupDraft } from "@/components/prompt-input/submit"
|
||||
|
||||
export function SessionComposerRegion(props: {
|
||||
state: SessionComposerState
|
||||
@@ -21,9 +23,21 @@ export function SessionComposerRegion(props: {
|
||||
onNewSessionWorktreeReset: () => void
|
||||
onSubmit: () => void
|
||||
onResponseSubmit: () => void
|
||||
followup?: {
|
||||
queue: () => boolean
|
||||
items: { id: string; text: string }[]
|
||||
sending?: string
|
||||
edit?: { id: string; prompt: FollowupDraft["prompt"]; context: FollowupDraft["context"] }
|
||||
onQueue: (draft: FollowupDraft) => void
|
||||
onAbort: () => void
|
||||
onSend: (id: string) => void
|
||||
onEdit: (id: string) => void
|
||||
onEditLoaded: () => void
|
||||
}
|
||||
revert?: {
|
||||
items: { id: string; text: string }[]
|
||||
restoring?: string
|
||||
disabled?: boolean
|
||||
onRestore: (id: string) => void
|
||||
}
|
||||
setPromptDockRef: (el: HTMLDivElement) => void
|
||||
@@ -156,6 +170,7 @@ export function SessionComposerRegion(props: {
|
||||
<SessionRevertDock
|
||||
items={revert.items}
|
||||
restoring={revert.restoring}
|
||||
disabled={revert.disabled}
|
||||
onRestore={revert.onRestore}
|
||||
/>
|
||||
</div>
|
||||
@@ -195,7 +210,12 @@ export function SessionComposerRegion(props: {
|
||||
"margin-top": `${-36 * value()}px`,
|
||||
}}
|
||||
>
|
||||
<SessionRevertDock items={revert.items} restoring={revert.restoring} onRestore={revert.onRestore} />
|
||||
<SessionRevertDock
|
||||
items={revert.items}
|
||||
restoring={revert.restoring}
|
||||
disabled={revert.disabled}
|
||||
onRestore={revert.onRestore}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Show>
|
||||
@@ -207,10 +227,23 @@ export function SessionComposerRegion(props: {
|
||||
"margin-top": `${-lift()}px`,
|
||||
}}
|
||||
>
|
||||
<Show when={props.followup?.items.length}>
|
||||
<SessionFollowupDock
|
||||
items={props.followup!.items}
|
||||
sending={props.followup!.sending}
|
||||
onSend={props.followup!.onSend}
|
||||
onEdit={props.followup!.onEdit}
|
||||
/>
|
||||
</Show>
|
||||
<PromptInput
|
||||
ref={props.inputRef}
|
||||
newSessionWorktree={props.newSessionWorktree}
|
||||
onNewSessionWorktreeReset={props.onNewSessionWorktreeReset}
|
||||
edit={props.followup?.edit}
|
||||
onEditLoaded={props.followup?.onEditLoaded}
|
||||
shouldQueue={props.followup?.queue}
|
||||
onQueue={props.followup?.onQueue}
|
||||
onAbort={props.followup?.onAbort}
|
||||
onSubmit={props.onSubmit}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import { For, Show, createMemo } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { Button } from "@opencode-ai/ui/button"
|
||||
import { DockTray } from "@opencode-ai/ui/dock-surface"
|
||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||
import { useLanguage } from "@/context/language"
|
||||
|
||||
export function SessionFollowupDock(props: {
|
||||
items: { id: string; text: string }[]
|
||||
sending?: string
|
||||
onSend: (id: string) => void
|
||||
onEdit: (id: string) => void
|
||||
}) {
|
||||
const language = useLanguage()
|
||||
const [store, setStore] = createStore({
|
||||
collapsed: false,
|
||||
})
|
||||
|
||||
const toggle = () => setStore("collapsed", (value) => !value)
|
||||
const total = createMemo(() => props.items.length)
|
||||
const label = createMemo(() =>
|
||||
language.t(total() === 1 ? "session.followupDock.summary.one" : "session.followupDock.summary.other", {
|
||||
count: total(),
|
||||
}),
|
||||
)
|
||||
const preview = createMemo(() => props.items[0]?.text ?? "")
|
||||
|
||||
return (
|
||||
<DockTray
|
||||
data-component="session-followup-dock"
|
||||
style={{
|
||||
"margin-bottom": "-0.875rem",
|
||||
"border-bottom-left-radius": 0,
|
||||
"border-bottom-right-radius": 0,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class="pl-3 pr-2 py-2 flex items-center gap-2"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={toggle}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key !== "Enter" && event.key !== " ") return
|
||||
event.preventDefault()
|
||||
toggle()
|
||||
}}
|
||||
>
|
||||
<span class="shrink-0 text-13-medium text-text-strong cursor-default">{label()}</span>
|
||||
<Show when={store.collapsed && preview()}>
|
||||
<span class="min-w-0 flex-1 truncate text-13-regular text-text-base cursor-default">{preview()}</span>
|
||||
</Show>
|
||||
<div class="ml-auto shrink-0">
|
||||
<IconButton
|
||||
data-collapsed={store.collapsed ? "true" : "false"}
|
||||
icon="chevron-down"
|
||||
size="normal"
|
||||
variant="ghost"
|
||||
style={{ transform: `rotate(${store.collapsed ? 180 : 0}deg)` }}
|
||||
onMouseDown={(event) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
}}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
toggle()
|
||||
}}
|
||||
aria-label={
|
||||
store.collapsed ? language.t("session.followupDock.expand") : language.t("session.followupDock.collapse")
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Show when={store.collapsed}>
|
||||
<div class="h-5" aria-hidden="true" />
|
||||
</Show>
|
||||
|
||||
<Show when={!store.collapsed}>
|
||||
<div class="px-3 pb-7 flex flex-col gap-1.5 max-h-42 overflow-y-auto no-scrollbar">
|
||||
<For each={props.items}>
|
||||
{(item) => (
|
||||
<div class="flex items-center gap-2 min-w-0 py-1">
|
||||
<span class="min-w-0 flex-1 truncate text-13-regular text-text-strong">{item.text}</span>
|
||||
<Button
|
||||
size="small"
|
||||
variant="secondary"
|
||||
class="shrink-0"
|
||||
disabled={!!props.sending}
|
||||
onClick={() => props.onSend(item.id)}
|
||||
>
|
||||
{language.t("session.followupDock.sendNow")}
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
variant="ghost"
|
||||
class="shrink-0"
|
||||
disabled={!!props.sending}
|
||||
onClick={() => props.onEdit(item.id)}
|
||||
>
|
||||
{language.t("session.followupDock.edit")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
</DockTray>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { For, Show, createMemo } from "solid-js"
|
||||
import { For, Show, createEffect, createMemo } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { Button } from "@opencode-ai/ui/button"
|
||||
import { DockTray } from "@opencode-ai/ui/dock-surface"
|
||||
@@ -8,11 +8,18 @@ import { useLanguage } from "@/context/language"
|
||||
export function SessionRevertDock(props: {
|
||||
items: { id: string; text: string }[]
|
||||
restoring?: string
|
||||
disabled?: boolean
|
||||
onRestore: (id: string) => void
|
||||
}) {
|
||||
const language = useLanguage()
|
||||
const [store, setStore] = createStore({
|
||||
collapsed: false,
|
||||
collapsed: true,
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
props.items.length
|
||||
props.items[0]?.id
|
||||
setStore("collapsed", true)
|
||||
})
|
||||
|
||||
const toggle = () => setStore("collapsed", (value) => !value)
|
||||
@@ -68,16 +75,16 @@ export function SessionRevertDock(props: {
|
||||
</Show>
|
||||
|
||||
<Show when={!store.collapsed}>
|
||||
<div class="px-3 pb-11 flex flex-col gap-1.5 max-h-42 overflow-y-auto no-scrollbar">
|
||||
<div class="px-3 pb-7 flex flex-col gap-1.5 max-h-42 overflow-y-auto no-scrollbar">
|
||||
<For each={props.items}>
|
||||
{(item) => (
|
||||
<div class="flex items-center gap-2 min-w-0 rounded-[10px] border border-border-weak-base bg-background-stronger px-2.5 py-2">
|
||||
<div class="flex items-center gap-2 min-w-0 py-1">
|
||||
<span class="min-w-0 flex-1 truncate text-13-regular text-text-strong">{item.text}</span>
|
||||
<Button
|
||||
size="small"
|
||||
variant="secondary"
|
||||
class="shrink-0"
|
||||
disabled={!!props.restoring}
|
||||
disabled={props.disabled || !!props.restoring}
|
||||
onClick={() => props.onRestore(item.id)}
|
||||
>
|
||||
{language.t("session.revertDock.restore")}
|
||||
|
||||
@@ -942,12 +942,6 @@ export function MessageTimeline(props: {
|
||||
<For each={rendered()}>
|
||||
{(messageID) => {
|
||||
const active = createMemo(() => activeMessageID() === messageID)
|
||||
const queued = createMemo(() => {
|
||||
if (active()) return false
|
||||
const activeID = activeMessageID()
|
||||
if (activeID) return messageID > activeID
|
||||
return false
|
||||
})
|
||||
const comments = createMemo(() => messageComments(sync.data.part[messageID] ?? []), [], {
|
||||
equals: (a, b) => JSON.stringify(a) === JSON.stringify(b),
|
||||
})
|
||||
@@ -1007,7 +1001,6 @@ export function MessageTimeline(props: {
|
||||
messageID={messageID}
|
||||
actions={props.actions}
|
||||
active={active()}
|
||||
queued={queued()}
|
||||
status={active() ? sessionStatus() : undefined}
|
||||
showReasoningSummaries={settings.general.showReasoningSummaries()}
|
||||
shellToolDefaultOpen={settings.general.shellToolPartsExpanded()}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -136,6 +136,11 @@ export async function handler(
|
||||
...createBodyConverter(opts.format, providerInfo.format)(body),
|
||||
model: providerInfo.model,
|
||||
...(providerInfo.payloadModifier ?? {}),
|
||||
...Object.fromEntries(
|
||||
Object.entries(providerInfo.payloadMappings ?? {})
|
||||
.map(([k, v]) => [k, input.request.headers.get(v)])
|
||||
.filter(([_k, v]) => !!v),
|
||||
),
|
||||
},
|
||||
authInfo?.workspaceID,
|
||||
),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -47,6 +47,7 @@ export namespace ZenData {
|
||||
format: FormatSchema.optional(),
|
||||
headerMappings: z.record(z.string(), z.string()).optional(),
|
||||
payloadModifier: z.record(z.string(), z.any()).optional(),
|
||||
payloadMappings: z.record(z.string(), z.string()).optional(),
|
||||
})
|
||||
|
||||
const ModelsSchema = z.object({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@opencode-ai/desktop-electron",
|
||||
"private": true,
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"homepage": "https://opencode.ai",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"private": true,
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
id = "opencode"
|
||||
name = "OpenCode"
|
||||
description = "The open source coding agent."
|
||||
version = "1.2.24"
|
||||
version = "1.2.25"
|
||||
schema_version = 1
|
||||
authors = ["Anomaly"]
|
||||
repository = "https://github.com/anomalyco/opencode"
|
||||
@@ -11,26 +11,26 @@ name = "OpenCode"
|
||||
icon = "./icons/opencode.svg"
|
||||
|
||||
[agent_servers.opencode.targets.darwin-aarch64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.24/opencode-darwin-arm64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.25/opencode-darwin-arm64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.darwin-x86_64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.24/opencode-darwin-x64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.25/opencode-darwin-x64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-aarch64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.24/opencode-linux-arm64.tar.gz"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.25/opencode-linux-arm64.tar.gz"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-x86_64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.24/opencode-linux-x64.tar.gz"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.25/opencode-linux-x64.tar.gz"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.windows-x86_64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.24/opencode-windows-x64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.2.25/opencode-windows-x64.zip"
|
||||
cmd = "./opencode.exe"
|
||||
args = ["acp"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import path from "path"
|
||||
import { Global } from "../global"
|
||||
import { Effect } from "effect"
|
||||
import z from "zod"
|
||||
import { Filesystem } from "../util/filesystem"
|
||||
import { runtime } from "@/effect/runtime"
|
||||
import * as S from "./service"
|
||||
|
||||
export const OAUTH_DUMMY_KEY = "opencode-oauth-dummy-key"
|
||||
export { OAUTH_DUMMY_KEY } from "./service"
|
||||
|
||||
function runPromise<A>(f: (service: S.AuthService.Service) => Effect.Effect<A, S.AuthServiceError>) {
|
||||
return runtime.runPromise(S.AuthService.use(f))
|
||||
}
|
||||
|
||||
export namespace Auth {
|
||||
export const Oauth = z
|
||||
@@ -35,39 +39,19 @@ export namespace Auth {
|
||||
export const Info = z.discriminatedUnion("type", [Oauth, Api, WellKnown]).meta({ ref: "Auth" })
|
||||
export type Info = z.infer<typeof Info>
|
||||
|
||||
const filepath = path.join(Global.Path.data, "auth.json")
|
||||
|
||||
export async function get(providerID: string) {
|
||||
const auth = await all()
|
||||
return auth[providerID]
|
||||
return runPromise((service) => service.get(providerID))
|
||||
}
|
||||
|
||||
export async function all(): Promise<Record<string, Info>> {
|
||||
const data = await Filesystem.readJson<Record<string, unknown>>(filepath).catch(() => ({}))
|
||||
return Object.entries(data).reduce(
|
||||
(acc, [key, value]) => {
|
||||
const parsed = Info.safeParse(value)
|
||||
if (!parsed.success) return acc
|
||||
acc[key] = parsed.data
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, Info>,
|
||||
)
|
||||
return runPromise((service) => service.all())
|
||||
}
|
||||
|
||||
export async function set(key: string, info: Info) {
|
||||
const normalized = key.replace(/\/+$/, "")
|
||||
const data = await all()
|
||||
if (normalized !== key) delete data[key]
|
||||
delete data[normalized + "/"]
|
||||
await Filesystem.writeJson(filepath, { ...data, [normalized]: info }, 0o600)
|
||||
return runPromise((service) => service.set(key, info))
|
||||
}
|
||||
|
||||
export async function remove(key: string) {
|
||||
const normalized = key.replace(/\/+$/, "")
|
||||
const data = await all()
|
||||
delete data[key]
|
||||
delete data[normalized]
|
||||
await Filesystem.writeJson(filepath, data, 0o600)
|
||||
return runPromise((service) => service.remove(key))
|
||||
}
|
||||
}
|
||||
|
||||
101
packages/opencode/src/auth/service.ts
Normal file
101
packages/opencode/src/auth/service.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import path from "path"
|
||||
import { Effect, Layer, Record, Result, Schema, ServiceMap } from "effect"
|
||||
import { Global } from "../global"
|
||||
import { Filesystem } from "../util/filesystem"
|
||||
|
||||
export const OAUTH_DUMMY_KEY = "opencode-oauth-dummy-key"
|
||||
|
||||
export class Oauth extends Schema.Class<Oauth>("OAuth")({
|
||||
type: Schema.Literal("oauth"),
|
||||
refresh: Schema.String,
|
||||
access: Schema.String,
|
||||
expires: Schema.Number,
|
||||
accountId: Schema.optional(Schema.String),
|
||||
enterpriseUrl: Schema.optional(Schema.String),
|
||||
}) {}
|
||||
|
||||
export class Api extends Schema.Class<Api>("ApiAuth")({
|
||||
type: Schema.Literal("api"),
|
||||
key: Schema.String,
|
||||
}) {}
|
||||
|
||||
export class WellKnown extends Schema.Class<WellKnown>("WellKnownAuth")({
|
||||
type: Schema.Literal("wellknown"),
|
||||
key: Schema.String,
|
||||
token: Schema.String,
|
||||
}) {}
|
||||
|
||||
export const Info = Schema.Union([Oauth, Api, WellKnown])
|
||||
export type Info = Schema.Schema.Type<typeof Info>
|
||||
|
||||
export class AuthServiceError extends Schema.TaggedErrorClass<AuthServiceError>()("AuthServiceError", {
|
||||
message: Schema.String,
|
||||
cause: Schema.optional(Schema.Defect),
|
||||
}) {}
|
||||
|
||||
const file = path.join(Global.Path.data, "auth.json")
|
||||
|
||||
const fail = (message: string) => (cause: unknown) => new AuthServiceError({ message, cause })
|
||||
|
||||
export namespace AuthService {
|
||||
export interface Service {
|
||||
readonly get: (providerID: string) => Effect.Effect<Info | undefined, AuthServiceError>
|
||||
readonly all: () => Effect.Effect<Record<string, Info>, AuthServiceError>
|
||||
readonly set: (key: string, info: Info) => Effect.Effect<void, AuthServiceError>
|
||||
readonly remove: (key: string) => Effect.Effect<void, AuthServiceError>
|
||||
}
|
||||
}
|
||||
|
||||
export class AuthService extends ServiceMap.Service<AuthService, AuthService.Service>()("@opencode/Auth") {
|
||||
static readonly layer = Layer.effect(
|
||||
AuthService,
|
||||
Effect.gen(function* () {
|
||||
const decode = Schema.decodeUnknownOption(Info)
|
||||
|
||||
const all = Effect.fn("AuthService.all")(() =>
|
||||
Effect.tryPromise({
|
||||
try: async () => {
|
||||
const data = await Filesystem.readJson<Record<string, unknown>>(file).catch(() => ({}))
|
||||
return Record.filterMap(data, (value) => Result.fromOption(decode(value), () => undefined))
|
||||
},
|
||||
catch: fail("Failed to read auth data"),
|
||||
}),
|
||||
)
|
||||
|
||||
const get = Effect.fn("AuthService.get")(function* (providerID: string) {
|
||||
return (yield* all())[providerID]
|
||||
})
|
||||
|
||||
const set = Effect.fn("AuthService.set")(function* (key: string, info: Info) {
|
||||
const norm = key.replace(/\/+$/, "")
|
||||
const data = yield* all()
|
||||
if (norm !== key) delete data[key]
|
||||
delete data[norm + "/"]
|
||||
yield* Effect.tryPromise({
|
||||
try: () => Filesystem.writeJson(file, { ...data, [norm]: info }, 0o600),
|
||||
catch: fail("Failed to write auth data"),
|
||||
})
|
||||
})
|
||||
|
||||
const remove = Effect.fn("AuthService.remove")(function* (key: string) {
|
||||
const norm = key.replace(/\/+$/, "")
|
||||
const data = yield* all()
|
||||
delete data[key]
|
||||
delete data[norm]
|
||||
yield* Effect.tryPromise({
|
||||
try: () => Filesystem.writeJson(file, data, 0o600),
|
||||
catch: fail("Failed to write auth data"),
|
||||
})
|
||||
})
|
||||
|
||||
return AuthService.of({
|
||||
get,
|
||||
all,
|
||||
set,
|
||||
remove,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
static readonly defaultLayer = AuthService.layer
|
||||
}
|
||||
@@ -192,3 +192,28 @@ export const OrgsCommand = cmd({
|
||||
await runtime.runPromise(orgsEffect())
|
||||
},
|
||||
})
|
||||
|
||||
export const ConsoleCommand = cmd({
|
||||
command: "console",
|
||||
describe: "manage console account",
|
||||
builder: (yargs) =>
|
||||
yargs
|
||||
.command({
|
||||
...LoginCommand,
|
||||
describe: "log in to console",
|
||||
})
|
||||
.command({
|
||||
...LogoutCommand,
|
||||
describe: "log out from console",
|
||||
})
|
||||
.command({
|
||||
...SwitchCommand,
|
||||
describe: "switch active org",
|
||||
})
|
||||
.command({
|
||||
...OrgsCommand,
|
||||
describe: "list orgs",
|
||||
})
|
||||
.demandCommand(),
|
||||
async handler() {},
|
||||
})
|
||||
|
||||
@@ -318,10 +318,10 @@ export const ProvidersLoginCommand = cmd({
|
||||
|
||||
const priority: Record<string, number> = {
|
||||
opencode: 0,
|
||||
anthropic: 1,
|
||||
openai: 1,
|
||||
"github-copilot": 2,
|
||||
openai: 3,
|
||||
google: 4,
|
||||
google: 3,
|
||||
anthropic: 4,
|
||||
openrouter: 5,
|
||||
vercel: 6,
|
||||
}
|
||||
|
||||
@@ -677,20 +677,6 @@ function App() {
|
||||
},
|
||||
])
|
||||
|
||||
createEffect(() => {
|
||||
const currentModel = local.model.current()
|
||||
if (!currentModel) return
|
||||
if (currentModel.providerID === "openrouter" && !kv.get("openrouter_warning", false)) {
|
||||
untrack(() => {
|
||||
DialogAlert.show(
|
||||
dialog,
|
||||
"Warning",
|
||||
"While openrouter is a convenient way to access LLMs your request will often be routed to subpar providers that do not work well in our testing.\n\nFor reliable access to models check out OpenCode Zen\nhttps://opencode.ai/zen",
|
||||
).then(() => kv.set("openrouter_warning", true))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
sdk.event.on(TuiEvent.CommandExecute.type, (evt) => {
|
||||
command.trigger(evt.properties.command)
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ManagedRuntime } from "effect"
|
||||
import { Layer, ManagedRuntime } from "effect"
|
||||
import { AccountService } from "@/account/service"
|
||||
import { AuthService } from "@/auth/service"
|
||||
|
||||
export const runtime = ManagedRuntime.make(AccountService.defaultLayer)
|
||||
export const runtime = ManagedRuntime.make(Layer.mergeAll(AccountService.defaultLayer, AuthService.defaultLayer))
|
||||
|
||||
@@ -3,7 +3,7 @@ import { hideBin } from "yargs/helpers"
|
||||
import { RunCommand } from "./cli/cmd/run"
|
||||
import { GenerateCommand } from "./cli/cmd/generate"
|
||||
import { Log } from "./util/log"
|
||||
import { LoginCommand, LogoutCommand, SwitchCommand, OrgsCommand } from "./cli/cmd/account"
|
||||
import { ConsoleCommand } from "./cli/cmd/account"
|
||||
import { ProvidersCommand } from "./cli/cmd/providers"
|
||||
import { AgentCommand } from "./cli/cmd/agent"
|
||||
import { UpgradeCommand } from "./cli/cmd/upgrade"
|
||||
@@ -135,10 +135,7 @@ let cli = yargs(hideBin(process.argv))
|
||||
.command(RunCommand)
|
||||
.command(GenerateCommand)
|
||||
.command(DebugCommand)
|
||||
.command(LoginCommand)
|
||||
.command(LogoutCommand)
|
||||
.command(SwitchCommand)
|
||||
.command(OrgsCommand)
|
||||
.command(ConsoleCommand)
|
||||
.command(ProvidersCommand)
|
||||
.command(AgentCommand)
|
||||
.command(UpgradeCommand)
|
||||
|
||||
@@ -25,6 +25,11 @@ export namespace Plugin {
|
||||
const client = createOpencodeClient({
|
||||
baseUrl: "http://localhost:4096",
|
||||
directory: Instance.directory,
|
||||
headers: Flag.OPENCODE_SERVER_PASSWORD
|
||||
? {
|
||||
Authorization: `Basic ${Buffer.from(`${Flag.OPENCODE_SERVER_USERNAME ?? "opencode"}:${Flag.OPENCODE_SERVER_PASSWORD}`).toString("base64")}`,
|
||||
}
|
||||
: undefined,
|
||||
fetch: async (...args) => Server.Default().fetch(...args),
|
||||
})
|
||||
const config = await Config.get()
|
||||
|
||||
@@ -425,14 +425,14 @@ export namespace SessionPrompt {
|
||||
extra: { bypassAgentCheck: true },
|
||||
messages: msgs,
|
||||
async metadata(input) {
|
||||
await Session.updatePart({
|
||||
part = (await Session.updatePart({
|
||||
...part,
|
||||
type: "tool",
|
||||
state: {
|
||||
...part.state,
|
||||
...input,
|
||||
},
|
||||
} satisfies MessageV2.ToolPart)
|
||||
} satisfies MessageV2.ToolPart)) as MessageV2.ToolPart
|
||||
},
|
||||
async ask(req) {
|
||||
await PermissionNext.ask({
|
||||
@@ -493,7 +493,7 @@ export namespace SessionPrompt {
|
||||
start: part.state.status === "running" ? part.state.time.start : Date.now(),
|
||||
end: Date.now(),
|
||||
},
|
||||
metadata: part.metadata,
|
||||
metadata: "metadata" in part.state ? part.state.metadata : undefined,
|
||||
input: part.state.input,
|
||||
},
|
||||
} satisfies MessageV2.ToolPart)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
|
||||
@@ -23,10 +23,6 @@
|
||||
max-width: 100%;
|
||||
gap: 0;
|
||||
|
||||
&[data-interrupted] {
|
||||
color: var(--text-weak);
|
||||
}
|
||||
|
||||
[data-slot="user-message-attachments"] {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -54,10 +50,6 @@
|
||||
border-color: var(--border-strong-base);
|
||||
}
|
||||
|
||||
&[data-queued] {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&[data-type="image"] {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
@@ -111,11 +103,6 @@
|
||||
border: 1px solid var(--border-weak-base);
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
transition: opacity 0.3s ease;
|
||||
|
||||
&[data-queued] {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
[data-highlight="file"] {
|
||||
color: var(--syntax-property);
|
||||
@@ -128,14 +115,6 @@
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
[data-slot="user-message-queued-indicator"] {
|
||||
margin-top: 6px;
|
||||
margin-right: 2px;
|
||||
font-size: var(--font-size-small);
|
||||
color: var(--text-weak);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[data-slot="user-message-copy-wrapper"] {
|
||||
min-height: 24px;
|
||||
margin-top: 4px;
|
||||
@@ -182,10 +161,6 @@
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
[data-slot="user-message-copy-wrapper"][data-interrupted] {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
&:hover [data-slot="user-message-copy-wrapper"],
|
||||
&:focus-within [data-slot="user-message-copy-wrapper"] {
|
||||
opacity: 1;
|
||||
|
||||
@@ -131,8 +131,6 @@ export interface MessageProps {
|
||||
parts: PartType[]
|
||||
actions?: UserActions
|
||||
showAssistantCopyPartID?: string | null
|
||||
interrupted?: boolean
|
||||
queued?: boolean
|
||||
showReasoningSummaries?: boolean
|
||||
}
|
||||
|
||||
@@ -345,6 +343,17 @@ function urls(text: string | undefined) {
|
||||
})
|
||||
}
|
||||
|
||||
function sessionLink(id: string | undefined, path: string, href?: (id: string) => string | undefined) {
|
||||
if (!id) return
|
||||
|
||||
const direct = href?.(id)
|
||||
if (direct) return direct
|
||||
|
||||
const idx = path.indexOf("/session")
|
||||
if (idx === -1) return
|
||||
return `${path.slice(0, idx)}/session/${id}`
|
||||
}
|
||||
|
||||
const CONTEXT_GROUP_TOOLS = new Set(["read", "glob", "grep", "list"])
|
||||
const HIDDEN_TOOLS = new Set(["todowrite", "todoread"])
|
||||
|
||||
@@ -681,13 +690,7 @@ export function Message(props: MessageProps) {
|
||||
<Switch>
|
||||
<Match when={props.message.role === "user" && props.message}>
|
||||
{(userMessage) => (
|
||||
<UserMessageDisplay
|
||||
message={userMessage() as UserMessage}
|
||||
parts={props.parts}
|
||||
actions={props.actions}
|
||||
interrupted={props.interrupted}
|
||||
queued={props.queued}
|
||||
/>
|
||||
<UserMessageDisplay message={userMessage() as UserMessage} parts={props.parts} actions={props.actions} />
|
||||
)}
|
||||
</Match>
|
||||
<Match when={props.message.role === "assistant" && props.message}>
|
||||
@@ -878,13 +881,7 @@ function ContextToolGroup(props: { parts: ToolPart[]; busy?: boolean }) {
|
||||
)
|
||||
}
|
||||
|
||||
export function UserMessageDisplay(props: {
|
||||
message: UserMessage
|
||||
parts: PartType[]
|
||||
actions?: UserActions
|
||||
interrupted?: boolean
|
||||
queued?: boolean
|
||||
}) {
|
||||
export function UserMessageDisplay(props: { message: UserMessage; parts: PartType[]; actions?: UserActions }) {
|
||||
const data = useData()
|
||||
const dialog = useDialog()
|
||||
const i18n = useI18n()
|
||||
@@ -939,10 +936,7 @@ export function UserMessageDisplay(props: {
|
||||
return items.filter((x) => !!x).join("\u00A0\u00B7\u00A0")
|
||||
})
|
||||
|
||||
const metaTail = createMemo(() => {
|
||||
const items = [stamp(), props.interrupted ? i18n.t("ui.message.interrupted") : ""]
|
||||
return items.filter((x) => !!x).join("\u00A0\u00B7\u00A0")
|
||||
})
|
||||
const metaTail = stamp
|
||||
|
||||
const openImagePreview = (url: string, alt?: string) => {
|
||||
dialog.show(() => <ImagePreview src={url} alt={alt} />)
|
||||
@@ -973,7 +967,7 @@ export function UserMessageDisplay(props: {
|
||||
}
|
||||
|
||||
return (
|
||||
<div data-component="user-message" data-interrupted={props.interrupted ? "" : undefined}>
|
||||
<div data-component="user-message">
|
||||
<Show when={attachments().length > 0}>
|
||||
<div data-slot="user-message-attachments">
|
||||
<For each={attachments()}>
|
||||
@@ -981,7 +975,6 @@ export function UserMessageDisplay(props: {
|
||||
<div
|
||||
data-slot="user-message-attachment"
|
||||
data-type={file.mime.startsWith("image/") ? "image" : "file"}
|
||||
data-queued={props.queued ? "" : undefined}
|
||||
onClick={() => {
|
||||
if (file.mime.startsWith("image/") && file.url) {
|
||||
openImagePreview(file.url, file.filename)
|
||||
@@ -1010,16 +1003,11 @@ export function UserMessageDisplay(props: {
|
||||
<Show when={text()}>
|
||||
<>
|
||||
<div data-slot="user-message-body">
|
||||
<div data-slot="user-message-text" data-queued={props.queued ? "" : undefined}>
|
||||
<div data-slot="user-message-text">
|
||||
<HighlightedText text={text()} references={inlineFiles()} agents={agents()} />
|
||||
</div>
|
||||
<Show when={props.queued}>
|
||||
<div data-slot="user-message-queued-indicator">
|
||||
<TextShimmer text={i18n.t("ui.message.queued")} />
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
<div data-slot="user-message-copy-wrapper" data-interrupted={props.interrupted ? "" : undefined}>
|
||||
<div data-slot="user-message-copy-wrapper">
|
||||
<Show when={metaHead() || metaTail()}>
|
||||
<span data-slot="user-message-meta-wrap">
|
||||
<Show when={metaHead()}>
|
||||
@@ -1224,6 +1212,7 @@ function ToolFileAccordion(props: { path: string; actions?: JSX.Element; childre
|
||||
}
|
||||
|
||||
PART_MAPPING["tool"] = function ToolPartDisplay(props) {
|
||||
const data = useData()
|
||||
const i18n = useI18n()
|
||||
const part = () => props.part as ToolPart
|
||||
if (part().tool === "todowrite" || part().tool === "todoread") return null
|
||||
@@ -1238,6 +1227,21 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) {
|
||||
const input = () => part().state?.input ?? emptyInput
|
||||
// @ts-expect-error
|
||||
const partMetadata = () => part().state?.metadata ?? emptyMetadata
|
||||
const taskId = createMemo(() => {
|
||||
if (part().tool !== "task") return
|
||||
const value = partMetadata().sessionId
|
||||
if (typeof value === "string" && value) return value
|
||||
})
|
||||
const taskHref = createMemo(() => {
|
||||
if (part().tool !== "task") return
|
||||
return sessionLink(taskId(), useLocation().pathname, data.sessionHref)
|
||||
})
|
||||
const taskSubtitle = createMemo(() => {
|
||||
if (part().tool !== "task") return undefined
|
||||
const value = input().description
|
||||
if (typeof value === "string" && value) return value
|
||||
return taskId()
|
||||
})
|
||||
|
||||
const render = createMemo(() => ToolRegistry.render(part().tool) ?? GenericTool)
|
||||
|
||||
@@ -1257,7 +1261,15 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return <ToolErrorCard tool={part().tool} error={error()} defaultOpen={props.defaultOpen} />
|
||||
return (
|
||||
<ToolErrorCard
|
||||
tool={part().tool}
|
||||
error={error()}
|
||||
defaultOpen={props.defaultOpen}
|
||||
subtitle={taskSubtitle()}
|
||||
href={taskHref()}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
@@ -1279,14 +1291,13 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) {
|
||||
)
|
||||
}
|
||||
|
||||
PART_MAPPING["compaction"] = function CompactionPartDisplay() {
|
||||
const i18n = useI18n()
|
||||
export function MessageDivider(props: { label: string }) {
|
||||
return (
|
||||
<div data-component="compaction-part">
|
||||
<div data-slot="compaction-part-divider">
|
||||
<span data-slot="compaction-part-line" />
|
||||
<span data-slot="compaction-part-label" class="text-12-regular text-text-weak">
|
||||
{i18n.t("ui.messagePart.compaction")}
|
||||
{props.label}
|
||||
</span>
|
||||
<span data-slot="compaction-part-line" />
|
||||
</div>
|
||||
@@ -1294,6 +1305,11 @@ PART_MAPPING["compaction"] = function CompactionPartDisplay() {
|
||||
)
|
||||
}
|
||||
|
||||
PART_MAPPING["compaction"] = function CompactionPartDisplay() {
|
||||
const i18n = useI18n()
|
||||
return <MessageDivider label={i18n.t("ui.messagePart.compaction")} />
|
||||
}
|
||||
|
||||
PART_MAPPING["text"] = function TextPartDisplay(props) {
|
||||
const data = useData()
|
||||
const i18n = useI18n()
|
||||
@@ -1634,25 +1650,14 @@ ToolRegistry.register({
|
||||
return raw[0]!.toUpperCase() + raw.slice(1)
|
||||
})
|
||||
const title = createMemo(() => agentTitle(i18n, type()))
|
||||
const description = createMemo(() => {
|
||||
const subtitle = createMemo(() => {
|
||||
const value = props.input.description
|
||||
if (typeof value === "string") return value
|
||||
return undefined
|
||||
if (typeof value === "string" && value) return value
|
||||
return childSessionId()
|
||||
})
|
||||
const running = createMemo(() => props.status === "pending" || props.status === "running")
|
||||
|
||||
const href = createMemo(() => {
|
||||
const sessionId = childSessionId()
|
||||
if (!sessionId) return
|
||||
|
||||
const direct = data.sessionHref?.(sessionId)
|
||||
if (direct) return direct
|
||||
|
||||
const path = location.pathname
|
||||
const idx = path.indexOf("/session")
|
||||
if (idx === -1) return
|
||||
return `${path.slice(0, idx)}/session/${sessionId}`
|
||||
})
|
||||
const href = createMemo(() => sessionLink(childSessionId(), location.pathname, data.sessionHref))
|
||||
|
||||
const titleContent = () => <TextShimmer text={title()} active={running()} />
|
||||
|
||||
@@ -1662,7 +1667,7 @@ ToolRegistry.register({
|
||||
<span data-slot="basic-tool-tool-title" class="capitalize agent-title">
|
||||
{titleContent()}
|
||||
</span>
|
||||
<Show when={description()}>
|
||||
<Show when={subtitle()}>
|
||||
<Switch>
|
||||
<Match when={href()}>
|
||||
<a
|
||||
@@ -1671,11 +1676,11 @@ ToolRegistry.register({
|
||||
href={href()!}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{description()}
|
||||
{subtitle()}
|
||||
</a>
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
<span data-slot="basic-tool-tool-subtitle">{description()}</span>
|
||||
<span data-slot="basic-tool-tool-subtitle">{subtitle()}</span>
|
||||
</Match>
|
||||
</Switch>
|
||||
</Show>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Binary } from "@opencode-ai/util/binary"
|
||||
import { getDirectory, getFilename } from "@opencode-ai/util/path"
|
||||
import { createEffect, createMemo, createSignal, For, on, ParentProps, Show } from "solid-js"
|
||||
import { Dynamic } from "solid-js/web"
|
||||
import { AssistantParts, Message, Part, PART_MAPPING, type UserActions } from "./message-part"
|
||||
import { AssistantParts, Message, MessageDivider, PART_MAPPING, type UserActions } from "./message-part"
|
||||
import { Card } from "./card"
|
||||
import { Accordion } from "./accordion"
|
||||
import { StickyAccordionHeader } from "./sticky-accordion-header"
|
||||
@@ -146,7 +146,6 @@ export function SessionTurn(
|
||||
shellToolDefaultOpen?: boolean
|
||||
editToolDefaultOpen?: boolean
|
||||
active?: boolean
|
||||
queued?: boolean
|
||||
status?: SessionStatus
|
||||
onUserInteracted?: () => void
|
||||
classes?: {
|
||||
@@ -193,7 +192,7 @@ export function SessionTurn(
|
||||
})
|
||||
|
||||
const pending = createMemo(() => {
|
||||
if (typeof props.active === "boolean" && typeof props.queued === "boolean") return
|
||||
if (typeof props.active === "boolean") return
|
||||
const messages = allMessages() ?? emptyMessages
|
||||
return messages.findLast(
|
||||
(item): item is AssistantMessage => item.role === "assistant" && typeof item.time.completed !== "number",
|
||||
@@ -218,16 +217,6 @@ export function SessionTurn(
|
||||
return parent.id === msg.id
|
||||
})
|
||||
|
||||
const queued = createMemo(() => {
|
||||
if (typeof props.queued === "boolean") return props.queued
|
||||
const id = message()?.id
|
||||
if (!id) return false
|
||||
if (!pendingUser()) return false
|
||||
const item = pending()
|
||||
if (!item) return false
|
||||
return id > item.id
|
||||
})
|
||||
|
||||
const parts = createMemo(() => {
|
||||
const msg = message()
|
||||
if (!msg) return emptyParts
|
||||
@@ -287,6 +276,11 @@ export function SessionTurn(
|
||||
)
|
||||
|
||||
const interrupted = createMemo(() => assistantMessages().some((m) => m.error?.name === "MessageAbortedError"))
|
||||
const divider = createMemo(() => {
|
||||
if (compaction()) return i18n.t("ui.messagePart.compaction")
|
||||
if (interrupted()) return i18n.t("ui.message.interrupted")
|
||||
return ""
|
||||
})
|
||||
const error = createMemo(
|
||||
() => assistantMessages().find((m) => m.error && m.error.name !== "MessageAbortedError")?.error,
|
||||
)
|
||||
@@ -367,7 +361,6 @@ export function SessionTurn(
|
||||
)
|
||||
const showThinking = createMemo(() => {
|
||||
if (!working() || !!error()) return false
|
||||
if (queued()) return false
|
||||
if (status().type === "retry") return false
|
||||
if (showReasoningSummaries()) return assistantVisible() === 0
|
||||
return true
|
||||
@@ -396,17 +389,11 @@ export function SessionTurn(
|
||||
class={props.classes?.container}
|
||||
>
|
||||
<div data-slot="session-turn-message-content" aria-live="off">
|
||||
<Message
|
||||
message={message()!}
|
||||
parts={parts()}
|
||||
actions={props.actions}
|
||||
interrupted={interrupted()}
|
||||
queued={queued()}
|
||||
/>
|
||||
<Message message={message()!} parts={parts()} actions={props.actions} />
|
||||
</div>
|
||||
<Show when={compaction()}>
|
||||
<Show when={divider()}>
|
||||
<div data-slot="session-turn-compaction">
|
||||
<Part part={compaction()!} message={message()!} hideDetails />
|
||||
<MessageDivider label={divider()} />
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={assistantMessages().length > 0}>
|
||||
|
||||
@@ -10,19 +10,22 @@ export interface ToolErrorCardProps extends Omit<ComponentProps<typeof Card>, "c
|
||||
tool: string
|
||||
error: string
|
||||
defaultOpen?: boolean
|
||||
subtitle?: string
|
||||
href?: string
|
||||
}
|
||||
|
||||
export function ToolErrorCard(props: ToolErrorCardProps) {
|
||||
const i18n = useI18n()
|
||||
const [open, setOpen] = createSignal(props.defaultOpen ?? false)
|
||||
const [copied, setCopied] = createSignal(false)
|
||||
const [split, rest] = splitProps(props, ["tool", "error", "defaultOpen"])
|
||||
const [split, rest] = splitProps(props, ["tool", "error", "defaultOpen", "subtitle", "href"])
|
||||
const name = createMemo(() => {
|
||||
const map: Record<string, string> = {
|
||||
read: "ui.tool.read",
|
||||
list: "ui.tool.list",
|
||||
glob: "ui.tool.glob",
|
||||
grep: "ui.tool.grep",
|
||||
task: "Task",
|
||||
webfetch: "ui.tool.webfetch",
|
||||
websearch: "ui.tool.websearch",
|
||||
codesearch: "ui.tool.codesearch",
|
||||
@@ -32,6 +35,7 @@ export function ToolErrorCard(props: ToolErrorCardProps) {
|
||||
}
|
||||
const key = map[split.tool]
|
||||
if (!key) return split.tool
|
||||
if (!key.includes(".")) return key
|
||||
return i18n.t(key)
|
||||
})
|
||||
const cleaned = createMemo(() => split.error.replace(/^Error:\s*/, "").trim())
|
||||
@@ -43,6 +47,7 @@ export function ToolErrorCard(props: ToolErrorCardProps) {
|
||||
})
|
||||
|
||||
const subtitle = createMemo(() => {
|
||||
if (split.subtitle) return split.subtitle
|
||||
const parts = tail().split(": ")
|
||||
if (parts.length <= 1) return "Failed"
|
||||
const head = (parts[0] ?? "").trim()
|
||||
@@ -77,7 +82,19 @@ export function ToolErrorCard(props: ToolErrorCardProps) {
|
||||
<div data-slot="basic-tool-tool-info-structured">
|
||||
<div data-slot="basic-tool-tool-info-main">
|
||||
<span data-slot="basic-tool-tool-title">{name()}</span>
|
||||
<span data-slot="basic-tool-tool-subtitle">{subtitle()}</span>
|
||||
<Show
|
||||
when={split.href && split.subtitle}
|
||||
fallback={<span data-slot="basic-tool-tool-subtitle">{subtitle()}</span>}
|
||||
>
|
||||
<a
|
||||
data-slot="basic-tool-tool-subtitle"
|
||||
class="clickable subagent-link"
|
||||
href={split.href!}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{subtitle()}
|
||||
</a>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -126,6 +126,8 @@ export const dict = {
|
||||
"ui.message.collapse": "طي الرسالة",
|
||||
"ui.message.copy": "نسخ",
|
||||
"ui.message.copyMessage": "نسخ الرسالة",
|
||||
"ui.message.forkMessage": "تشعب إلى جلسة جديدة",
|
||||
"ui.message.revertMessage": "إعادة التعيين إلى هذه النقطة",
|
||||
"ui.message.copyResponse": "نسخ الرد",
|
||||
"ui.message.copied": "تم النسخ!",
|
||||
"ui.message.interrupted": "تمت المقاطعة",
|
||||
|
||||
@@ -126,6 +126,8 @@ export const dict = {
|
||||
"ui.message.collapse": "Recolher mensagem",
|
||||
"ui.message.copy": "Copiar",
|
||||
"ui.message.copyMessage": "Copiar mensagem",
|
||||
"ui.message.forkMessage": "Bifurcar para nova sessão",
|
||||
"ui.message.revertMessage": "Redefinir para este ponto",
|
||||
"ui.message.copyResponse": "Copiar resposta",
|
||||
"ui.message.copied": "Copiado!",
|
||||
"ui.message.interrupted": "Interrompido",
|
||||
|
||||
@@ -130,6 +130,8 @@ export const dict = {
|
||||
"ui.message.collapse": "Sažmi poruku",
|
||||
"ui.message.copy": "Kopiraj",
|
||||
"ui.message.copyMessage": "Kopiraj poruku",
|
||||
"ui.message.forkMessage": "Forkaj u novu sesiju",
|
||||
"ui.message.revertMessage": "Resetuj na ovu tačku",
|
||||
"ui.message.copyResponse": "Kopiraj odgovor",
|
||||
"ui.message.copied": "Kopirano!",
|
||||
"ui.message.interrupted": "Prekinuto",
|
||||
|
||||
@@ -125,6 +125,8 @@ export const dict = {
|
||||
"ui.message.collapse": "Skjul besked",
|
||||
"ui.message.copy": "Kopier",
|
||||
"ui.message.copyMessage": "Kopier besked",
|
||||
"ui.message.forkMessage": "Forgren til ny session",
|
||||
"ui.message.revertMessage": "Nulstil til dette punkt",
|
||||
"ui.message.copyResponse": "Kopier svar",
|
||||
"ui.message.copied": "Kopieret!",
|
||||
"ui.message.interrupted": "Afbrudt",
|
||||
|
||||
@@ -131,6 +131,8 @@ export const dict = {
|
||||
"ui.message.collapse": "Nachricht reduzieren",
|
||||
"ui.message.copy": "Kopieren",
|
||||
"ui.message.copyMessage": "Nachricht kopieren",
|
||||
"ui.message.forkMessage": "In neue Sitzung abzweigen",
|
||||
"ui.message.revertMessage": "Auf diesen Punkt zurücksetzen",
|
||||
"ui.message.copyResponse": "Antwort kopieren",
|
||||
"ui.message.copied": "Kopiert!",
|
||||
"ui.message.interrupted": "Unterbrochen",
|
||||
|
||||
@@ -128,7 +128,7 @@ export const dict: Record<string, string> = {
|
||||
"ui.message.copy": "Copy",
|
||||
"ui.message.copyMessage": "Copy message",
|
||||
"ui.message.forkMessage": "Fork to new session",
|
||||
"ui.message.revertMessage": "Reset to this point",
|
||||
"ui.message.revertMessage": "Revert message",
|
||||
"ui.message.copyResponse": "Copy response",
|
||||
"ui.message.copied": "Copied",
|
||||
"ui.message.interrupted": "Interrupted",
|
||||
|
||||
@@ -126,6 +126,8 @@ export const dict = {
|
||||
"ui.message.collapse": "Colapsar mensaje",
|
||||
"ui.message.copy": "Copiar",
|
||||
"ui.message.copyMessage": "Copiar mensaje",
|
||||
"ui.message.forkMessage": "Bifurcar a nueva sesión",
|
||||
"ui.message.revertMessage": "Restablecer a este punto",
|
||||
"ui.message.copyResponse": "Copiar respuesta",
|
||||
"ui.message.copied": "¡Copiado!",
|
||||
"ui.message.interrupted": "Interrumpido",
|
||||
|
||||
@@ -126,6 +126,8 @@ export const dict = {
|
||||
"ui.message.collapse": "Réduire le message",
|
||||
"ui.message.copy": "Copier",
|
||||
"ui.message.copyMessage": "Copier le message",
|
||||
"ui.message.forkMessage": "Bifurquer vers une nouvelle session",
|
||||
"ui.message.revertMessage": "Réinitialiser à ce point",
|
||||
"ui.message.copyResponse": "Copier la réponse",
|
||||
"ui.message.copied": "Copié !",
|
||||
"ui.message.interrupted": "Interrompu",
|
||||
|
||||
@@ -125,6 +125,8 @@ export const dict = {
|
||||
"ui.message.collapse": "メッセージを折りたたむ",
|
||||
"ui.message.copy": "コピー",
|
||||
"ui.message.copyMessage": "メッセージをコピー",
|
||||
"ui.message.forkMessage": "新しいセッションにフォーク",
|
||||
"ui.message.revertMessage": "この時点までリセット",
|
||||
"ui.message.copyResponse": "応答をコピー",
|
||||
"ui.message.copied": "コピーしました!",
|
||||
"ui.message.interrupted": "中断",
|
||||
|
||||
@@ -126,6 +126,8 @@ export const dict = {
|
||||
"ui.message.collapse": "메시지 접기",
|
||||
"ui.message.copy": "복사",
|
||||
"ui.message.copyMessage": "메시지 복사",
|
||||
"ui.message.forkMessage": "새 세션으로 분기",
|
||||
"ui.message.revertMessage": "이 시점으로 초기화",
|
||||
"ui.message.copyResponse": "응답 복사",
|
||||
"ui.message.copied": "복사됨!",
|
||||
"ui.message.interrupted": "중단됨",
|
||||
|
||||
@@ -105,7 +105,7 @@ export const dict: Record<Keys, string> = {
|
||||
"ui.tool.todos.read": "Les gjøremål",
|
||||
"ui.tool.questions": "Spørsmål",
|
||||
"ui.tool.agent": "{{type}}-agent",
|
||||
"ui.tool.agent.default": "agent",
|
||||
"ui.tool.agent.default": "Agent",
|
||||
|
||||
"ui.common.file.one": "fil",
|
||||
"ui.common.file.other": "filer",
|
||||
@@ -129,6 +129,8 @@ export const dict: Record<Keys, string> = {
|
||||
"ui.message.collapse": "Skjul melding",
|
||||
"ui.message.copy": "Kopier",
|
||||
"ui.message.copyMessage": "Kopier melding",
|
||||
"ui.message.forkMessage": "Forgren til ny sesjon",
|
||||
"ui.message.revertMessage": "Tilbakestill til dette punktet",
|
||||
"ui.message.copyResponse": "Kopier svar",
|
||||
"ui.message.copied": "Kopiert!",
|
||||
"ui.message.interrupted": "Avbrutt",
|
||||
|
||||
@@ -125,6 +125,8 @@ export const dict = {
|
||||
"ui.message.collapse": "Zwiń wiadomość",
|
||||
"ui.message.copy": "Kopiuj",
|
||||
"ui.message.copyMessage": "Kopiuj wiadomość",
|
||||
"ui.message.forkMessage": "Rozwidlij do nowej sesji",
|
||||
"ui.message.revertMessage": "Zresetuj do tego punktu",
|
||||
"ui.message.copyResponse": "Kopiuj odpowiedź",
|
||||
"ui.message.copied": "Skopiowano!",
|
||||
"ui.message.interrupted": "Przerwano",
|
||||
|
||||
@@ -125,6 +125,8 @@ export const dict = {
|
||||
"ui.message.collapse": "Свернуть сообщение",
|
||||
"ui.message.copy": "Копировать",
|
||||
"ui.message.copyMessage": "Копировать сообщение",
|
||||
"ui.message.forkMessage": "Ответвить в новую сессию",
|
||||
"ui.message.revertMessage": "Сбросить до этого момента",
|
||||
"ui.message.copyResponse": "Копировать ответ",
|
||||
"ui.message.copied": "Скопировано!",
|
||||
"ui.message.interrupted": "Прервано",
|
||||
|
||||
@@ -127,6 +127,8 @@ export const dict = {
|
||||
"ui.message.collapse": "ย่อข้อความ",
|
||||
"ui.message.copy": "คัดลอก",
|
||||
"ui.message.copyMessage": "คัดลอกข้อความ",
|
||||
"ui.message.forkMessage": "แตกแขนงไปยังเซสชันใหม่",
|
||||
"ui.message.revertMessage": "รีเซ็ตไปยังจุดนี้",
|
||||
"ui.message.copyResponse": "คัดลอกคำตอบ",
|
||||
"ui.message.copied": "คัดลอกแล้ว!",
|
||||
"ui.message.interrupted": "ถูกขัดจังหวะ",
|
||||
|
||||
@@ -22,6 +22,16 @@ export const dict = {
|
||||
"ui.sessionReview.largeDiff.meta": "Limit: {{limit}} değişen satır. Mevcut: {{current}} değişen satır.",
|
||||
"ui.sessionReview.largeDiff.renderAnyway": "Yine de göster",
|
||||
|
||||
"ui.fileMedia.kind.image": "görsel",
|
||||
"ui.fileMedia.kind.audio": "ses",
|
||||
"ui.fileMedia.state.removed": "{{kind}} dosyası kaldırıldı.",
|
||||
"ui.fileMedia.state.loading": "{{kind}} yükleniyor...",
|
||||
"ui.fileMedia.state.error": "{{kind}} yüklenemedi.",
|
||||
"ui.fileMedia.state.unavailable": "{{kind}} önizlemesi kullanılamıyor.",
|
||||
"ui.fileMedia.binary.title": "İkili dosya",
|
||||
"ui.fileMedia.binary.description.path": "{{path}} ikili dosyadır.",
|
||||
"ui.fileMedia.binary.description.default": "İkili içerik",
|
||||
|
||||
"ui.lineComment.label.prefix": "Yorum: ",
|
||||
"ui.lineComment.label.suffix": "",
|
||||
"ui.lineComment.editorLabel.prefix": "Yorum yapılıyor: ",
|
||||
@@ -122,6 +132,8 @@ export const dict = {
|
||||
"ui.message.collapse": "Mesajı daralt",
|
||||
"ui.message.copy": "Kopyala",
|
||||
"ui.message.copyMessage": "Mesajı kopyala",
|
||||
"ui.message.forkMessage": "Yeni oturuma dallandır",
|
||||
"ui.message.revertMessage": "Bu noktaya sıfırla",
|
||||
"ui.message.copyResponse": "Yanıtı kopyala",
|
||||
"ui.message.copied": "Kopyalandı",
|
||||
"ui.message.interrupted": "Kesildi",
|
||||
|
||||
@@ -130,6 +130,8 @@ export const dict = {
|
||||
"ui.message.collapse": "收起消息",
|
||||
"ui.message.copy": "复制",
|
||||
"ui.message.copyMessage": "复制消息",
|
||||
"ui.message.forkMessage": "分叉到新会话",
|
||||
"ui.message.revertMessage": "重置到此点",
|
||||
"ui.message.copyResponse": "复制回复",
|
||||
"ui.message.copied": "已复制!",
|
||||
"ui.message.interrupted": "已中断",
|
||||
|
||||
@@ -130,6 +130,8 @@ export const dict = {
|
||||
"ui.message.collapse": "收合訊息",
|
||||
"ui.message.copy": "複製",
|
||||
"ui.message.copyMessage": "複製訊息",
|
||||
"ui.message.forkMessage": "分支到新工作階段",
|
||||
"ui.message.revertMessage": "重設至此點",
|
||||
"ui.message.copyResponse": "複製回覆",
|
||||
"ui.message.copied": "已複製!",
|
||||
"ui.message.interrupted": "已中斷",
|
||||
|
||||
@@ -14,6 +14,7 @@ import vesperThemeJson from "./themes/vesper.json"
|
||||
import carbonfoxThemeJson from "./themes/carbonfox.json"
|
||||
import gruvboxThemeJson from "./themes/gruvbox.json"
|
||||
import auraThemeJson from "./themes/aura.json"
|
||||
import amoledThemeJson from "./themes/amoled.json"
|
||||
|
||||
export const oc2Theme = oc2ThemeJson as DesktopTheme
|
||||
export const tokyonightTheme = tokyoThemeJson as DesktopTheme
|
||||
@@ -30,9 +31,11 @@ export const vesperTheme = vesperThemeJson as DesktopTheme
|
||||
export const carbonfoxTheme = carbonfoxThemeJson as DesktopTheme
|
||||
export const gruvboxTheme = gruvboxThemeJson as DesktopTheme
|
||||
export const auraTheme = auraThemeJson as DesktopTheme
|
||||
export const amoledTheme = amoledThemeJson as DesktopTheme
|
||||
|
||||
export const DEFAULT_THEMES: Record<string, DesktopTheme> = {
|
||||
"oc-2": oc2Theme,
|
||||
amoled: amoledTheme,
|
||||
aura: auraTheme,
|
||||
ayu: ayuTheme,
|
||||
carbonfox: carbonfoxTheme,
|
||||
|
||||
49
packages/ui/src/theme/themes/amoled.json
Normal file
49
packages/ui/src/theme/themes/amoled.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"$schema": "https://opencode.ai/desktop-theme.json",
|
||||
"name": "AMOLED",
|
||||
"id": "amoled",
|
||||
"light": {
|
||||
"palette": {
|
||||
"neutral": "#f0f0f0",
|
||||
"ink": "#0a0a0a",
|
||||
"primary": "#6200ff",
|
||||
"accent": "#ff0080",
|
||||
"success": "#00e676",
|
||||
"warning": "#ffab00",
|
||||
"error": "#ff1744",
|
||||
"info": "#00b0ff",
|
||||
"diffAdd": "#00e676",
|
||||
"diffDelete": "#ff1744"
|
||||
},
|
||||
"overrides": {
|
||||
"syntax-comment": "#757575",
|
||||
"syntax-keyword": "#d500f9",
|
||||
"syntax-string": "#00e676",
|
||||
"syntax-primitive": "#00b0ff",
|
||||
"syntax-property": "#ff9100",
|
||||
"syntax-constant": "#6200ff"
|
||||
}
|
||||
},
|
||||
"dark": {
|
||||
"palette": {
|
||||
"neutral": "#000000",
|
||||
"ink": "#ffffff",
|
||||
"primary": "#b388ff",
|
||||
"accent": "#ff4081",
|
||||
"success": "#00ff88",
|
||||
"warning": "#ffea00",
|
||||
"error": "#ff1744",
|
||||
"info": "#18ffff",
|
||||
"diffAdd": "#00ff88",
|
||||
"diffDelete": "#ff1744"
|
||||
},
|
||||
"overrides": {
|
||||
"syntax-comment": "#555555",
|
||||
"syntax-keyword": "#ff00ff",
|
||||
"syntax-string": "#00ff88",
|
||||
"syntax-primitive": "#18ffff",
|
||||
"syntax-property": "#ffea00",
|
||||
"syntax-constant": "#b388ff"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@opencode-ai/web",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "opencode",
|
||||
"displayName": "opencode",
|
||||
"description": "opencode for VS Code",
|
||||
"version": "1.2.24",
|
||||
"version": "1.2.25",
|
||||
"publisher": "sst-dev",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Reference in New Issue
Block a user