Skip to content
This repository was archived by the owner on Feb 25, 2026. It is now read-only.

Commit 90193bf

Browse files
Merge pull request #254 from Kilo-Org/catrielmuller/kilo-opencode-v1.1.57
OpenCode v1.1.57
2 parents ba14236 + 0a0bf96 commit 90193bf

61 files changed

Lines changed: 1975 additions & 820 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bun.lock

Lines changed: 275 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,11 @@
6868
"husky": "9.1.7",
6969
"prettier": "3.6.2",
7070
"semver": "^7.6.0",
71+
"sst": "3.17.23",
7172
"turbo": "2.5.6"
7273
},
7374
"dependencies": {
75+
"@aws-sdk/client-s3": "3.933.0",
7476
"@kilocode/plugin": "workspace:*",
7577
"@opencode-ai/script": "workspace:*",
7678
"@kilocode/sdk": "workspace:*",

packages/app/src/app.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ function UiI18nBridge(props: ParentProps) {
4343

4444
declare global {
4545
interface Window {
46-
__OPENCODE__?: { updaterEnabled?: boolean; serverPassword?: string; deepLinks?: string[] }
46+
__OPENCODE__?: { updaterEnabled?: boolean; serverPassword?: string; deepLinks?: string[]; wsl?: boolean }
4747
}
4848
}
4949

packages/app/src/components/session/session-header.tsx

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createEffect, createMemo, onCleanup, Show } from "solid-js"
1+
import { createEffect, createMemo, createResource, onCleanup, Show } from "solid-js"
22
import { createStore } from "solid-js/store"
33
import { Portal } from "solid-js/web"
44
import { useParams } from "@solidjs/router"
@@ -18,6 +18,7 @@ import { IconButton } from "@opencode-ai/ui/icon-button"
1818
import { Button } from "@opencode-ai/ui/button"
1919
import { AppIcon } from "@opencode-ai/ui/app-icon"
2020
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
21+
import { Spinner } from "@opencode-ai/ui/spinner"
2122
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
2223
import { Popover } from "@opencode-ai/ui/popover"
2324
import { TextField } from "@opencode-ai/ui/text-field"
@@ -167,6 +168,7 @@ export function SessionHeader() {
167168

168169
const [prefs, setPrefs] = persisted(Persist.global("open.app"), createStore({ app: "finder" as OpenApp }))
169170
const [menu, setMenu] = createStore({ open: false })
171+
const [openRequest, setOpenRequest] = createStore({ app: undefined as OpenApp | undefined, version: 0 })
170172

171173
const canOpen = createMemo(() => platform.platform === "desktop" && !!platform.openPath && server.isLocal())
172174
const current = createMemo(() => options().find((o) => o.id === prefs.app) ?? options()[0])
@@ -179,20 +181,32 @@ export function SessionHeader() {
179181
setPrefs("app", options()[0]?.id ?? "finder")
180182
})
181183

182-
const openDir = (app: OpenApp) => {
183-
const directory = projectDirectory()
184-
if (!directory) return
185-
if (!canOpen()) return
186-
187-
const item = options().find((o) => o.id === app)
188-
const openWith = item && "openWith" in item ? item.openWith : undefined
189-
Promise.resolve(platform.openPath?.(directory, openWith)).catch((err: unknown) => {
190-
showToast({
191-
variant: "error",
192-
title: language.t("common.requestFailed"),
193-
description: err instanceof Error ? err.message : String(err),
194-
})
184+
const [openTask] = createResource(
185+
() => openRequest.app && openRequest.version,
186+
async () => {
187+
const app = openRequest.app
188+
const directory = projectDirectory()
189+
if (!app || !directory || !canOpen()) return
190+
191+
const item = options().find((o) => o.id === app)
192+
const openWith = item && "openWith" in item ? item.openWith : undefined
193+
await platform.openPath?.(directory, openWith)
194+
},
195+
)
196+
197+
createEffect(() => {
198+
const err = openTask.error
199+
if (!err) return
200+
showToast({
201+
variant: "error",
202+
title: language.t("common.requestFailed"),
203+
description: err instanceof Error ? err.message : String(err),
195204
})
205+
})
206+
207+
const openDir = (app: OpenApp) => {
208+
if (openTask.loading) return
209+
setOpenRequest({ app, version: openRequest.version + 1 })
196210
}
197211

198212
const copyPath = () => {
@@ -329,29 +343,37 @@ export function SessionHeader() {
329343
<Show
330344
when={canOpen()}
331345
fallback={
332-
<Button
333-
variant="ghost"
334-
class="rounded-sm h-[24px] py-1.5 pr-3 pl-2 gap-2 border-none shadow-none"
335-
onClick={copyPath}
336-
aria-label={language.t("session.header.open.copyPath")}
337-
>
338-
<Icon name="copy" size="small" class="text-icon-base" />
339-
<span class="text-12-regular text-text-strong">
340-
{language.t("session.header.open.copyPath")}
341-
</span>
342-
</Button>
346+
<div class="flex h-[24px] box-border items-center rounded-md border border-border-base bg-surface-panel overflow-hidden">
347+
<Button
348+
variant="ghost"
349+
class="rounded-none h-full py-0 pr-3 pl-2 gap-2 border-none shadow-none"
350+
onClick={copyPath}
351+
aria-label={language.t("session.header.open.copyPath")}
352+
>
353+
<Icon name="copy" size="small" class="text-icon-base" />
354+
<span class="text-12-regular text-text-strong">
355+
{language.t("session.header.open.copyPath")}
356+
</span>
357+
</Button>
358+
</div>
343359
}
344360
>
345361
<div class="flex items-center">
346362
<div class="flex h-[24px] box-border items-center rounded-md border border-border-base bg-surface-panel overflow-hidden">
347363
<Button
348364
variant="ghost"
349-
class="rounded-none h-full py-0 pr-3 pl-2 gap-1.5 border-none shadow-none"
365+
class="rounded-none h-full py-0 pr-3 pl-2 gap-1.5 border-none shadow-none disabled:!cursor-default"
366+
classList={{
367+
"bg-surface-raised-base-active": openTask.loading,
368+
}}
350369
onClick={() => openDir(current().id)}
370+
disabled={openTask.loading}
351371
aria-label={language.t("session.header.open.ariaLabel", { app: current().label })}
352372
>
353373
<div class="flex size-5 shrink-0 items-center justify-center">
354-
<AppIcon id={current().icon} class="size-4" />
374+
<Show when={openTask.loading} fallback={<AppIcon id={current().icon} class="size-4" />}>
375+
<Spinner class="size-3.5 text-icon-base" />
376+
</Show>
355377
</div>
356378
<span class="text-12-regular text-text-strong">Open</span>
357379
</Button>
@@ -366,7 +388,11 @@ export function SessionHeader() {
366388
as={IconButton}
367389
icon="chevron-down"
368390
variant="ghost"
369-
class="rounded-none h-full w-[24px] p-0 border-none shadow-none data-[expanded]:bg-surface-raised-base-active"
391+
disabled={openTask.loading}
392+
class="rounded-none h-full w-[24px] p-0 border-none shadow-none data-[expanded]:bg-surface-raised-base-active disabled:!cursor-default"
393+
classList={{
394+
"bg-surface-raised-base-active": openTask.loading,
395+
}}
370396
aria-label={language.t("session.header.open.menu")}
371397
/>
372398
<DropdownMenu.Portal>
@@ -383,6 +409,7 @@ export function SessionHeader() {
383409
{options().map((o) => (
384410
<DropdownMenu.RadioItem
385411
value={o.id}
412+
disabled={openTask.loading}
386413
onSelect={() => {
387414
setMenu("open", false)
388415
openDir(o.id)

packages/app/src/components/settings-general.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,34 @@ export const SettingsGeneral: Component = () => {
367367
</div>
368368
</div>
369369

370+
<Show when={platform.platform === "desktop" && platform.os === "windows" && platform.getWslEnabled}>
371+
{(_) => {
372+
const [enabledResource, actions] = createResource(() => platform.getWslEnabled?.())
373+
const enabled = () => (enabledResource.state === "pending" ? undefined : enabledResource.latest)
374+
375+
return (
376+
<div class="flex flex-col gap-1">
377+
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.desktop.section.wsl")}</h3>
378+
379+
<div class="bg-surface-raised-base px-4 rounded-lg">
380+
<SettingsRow
381+
title={language.t("settings.desktop.wsl.title")}
382+
description={language.t("settings.desktop.wsl.description")}
383+
>
384+
<div data-action="settings-wsl">
385+
<Switch
386+
checked={enabled() ?? false}
387+
disabled={enabledResource.state === "pending"}
388+
onChange={(checked) => platform.setWslEnabled?.(checked)?.finally(() => actions.refetch())}
389+
/>
390+
</div>
391+
</SettingsRow>
392+
</div>
393+
</div>
394+
)
395+
}}
396+
</Show>
397+
370398
{/* Updates Section */}
371399
<div class="flex flex-col gap-1">
372400
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.updates")}</h3>

packages/app/src/components/terminal.tsx

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,12 @@ export const Terminal = (props: TerminalProps) => {
130130
const t = term
131131
if (!t) return
132132
t.focus()
133+
t.textarea?.focus()
133134
setTimeout(() => t.textarea?.focus(), 0)
134135
}
135136
const handlePointerDown = () => {
136137
const activeElement = document.activeElement
137-
if (activeElement instanceof HTMLElement && activeElement !== container) {
138+
if (activeElement instanceof HTMLElement && activeElement !== container && !container.contains(activeElement)) {
138139
activeElement.blur()
139140
}
140141
focusTerminal()
@@ -204,44 +205,32 @@ export const Terminal = (props: TerminalProps) => {
204205
ghostty = g
205206
term = t
206207

207-
const copy = () => {
208+
const handleCopy = (event: ClipboardEvent) => {
208209
const selection = t.getSelection()
209-
if (!selection) return false
210-
211-
const body = document.body
212-
if (body) {
213-
const textarea = document.createElement("textarea")
214-
textarea.value = selection
215-
textarea.setAttribute("readonly", "")
216-
textarea.style.position = "fixed"
217-
textarea.style.opacity = "0"
218-
body.appendChild(textarea)
219-
textarea.select()
220-
const copied = document.execCommand("copy")
221-
body.removeChild(textarea)
222-
if (copied) return true
223-
}
210+
if (!selection) return
224211

225-
const clipboard = navigator.clipboard
226-
if (clipboard?.writeText) {
227-
clipboard.writeText(selection).catch(() => {})
228-
return true
229-
}
212+
const clipboard = event.clipboardData
213+
if (!clipboard) return
214+
215+
event.preventDefault()
216+
clipboard.setData("text/plain", selection)
217+
}
218+
219+
const handlePaste = (event: ClipboardEvent) => {
220+
const clipboard = event.clipboardData
221+
const text = clipboard?.getData("text/plain") ?? clipboard?.getData("text") ?? ""
222+
if (!text) return
230223

231-
return false
224+
event.preventDefault()
225+
event.stopPropagation()
226+
t.paste(text)
232227
}
233228

234229
t.attachCustomKeyEventHandler((event) => {
235230
const key = event.key.toLowerCase()
236231

237232
if (event.ctrlKey && event.shiftKey && !event.metaKey && key === "c") {
238-
copy()
239-
return true
240-
}
241-
242-
if (event.metaKey && !event.ctrlKey && !event.altKey && key === "c") {
243-
if (!t.hasSelection()) return true
244-
copy()
233+
document.execCommand("copy")
245234
return true
246235
}
247236

@@ -252,6 +241,12 @@ export const Terminal = (props: TerminalProps) => {
252241
return matchKeybind(keybinds, event)
253242
})
254243

244+
container.addEventListener("copy", handleCopy, true)
245+
cleanups.push(() => container.removeEventListener("copy", handleCopy, true))
246+
247+
container.addEventListener("paste", handlePaste, true)
248+
cleanups.push(() => container.removeEventListener("paste", handlePaste, true))
249+
255250
const fit = new mod.FitAddon()
256251
const serializer = new SerializeAddon()
257252
cleanups.push(() => disposeIfDisposable(fit))

packages/app/src/context/platform.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ export type Platform = {
5757
/** Set the default server URL to use on app startup (platform-specific) */
5858
setDefaultServerUrl?(url: string | null): Promise<void> | void
5959

60+
/** Get the configured WSL integration (desktop only) */
61+
getWslEnabled?(): Promise<boolean>
62+
63+
/** Set the configured WSL integration (desktop only) */
64+
setWslEnabled?(config: boolean): Promise<void> | void
65+
6066
/** Get the preferred display backend (desktop only) */
6167
getDisplayBackend?(): Promise<DisplayBackend | null> | DisplayBackend | null
6268

packages/app/src/i18n/ar.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ export const dict = {
139139
"provider.connect.oauth.code.invalid": "رمز التفويض غير صالح",
140140
"provider.connect.oauth.auto.visit.prefix": "قم بزيارة ",
141141
"provider.connect.oauth.auto.visit.link": "هذا الرابط",
142-
"provider.connect.oauth.auto.visit.suffix": " وأدخل الرمز أدناه لتوصيل حسابك واستخدام نماذج {{provider}} في Kilo.",
142+
"provider.connect.oauth.auto.visit.suffix":
143+
" وأدخل الرمز أدناه لتوصيل حسابك واستخدام نماذج {{provider}} في Kilo.",
143144
"provider.connect.oauth.auto.confirmationCode": "رمز التأكيد",
144145
"provider.connect.toast.connected.title": "تم توصيل {{provider}}",
145146
"provider.connect.toast.connected.description": "نماذج {{provider}} متاحة الآن للاستخدام.",
@@ -507,6 +508,9 @@ export const dict = {
507508
"settings.section.server": "الخادم",
508509
"settings.tab.general": "عام",
509510
"settings.tab.shortcuts": "اختصارات",
511+
"settings.desktop.section.wsl": "WSL",
512+
"settings.desktop.wsl.title": "WSL integration",
513+
"settings.desktop.wsl.description": "Run the Kilo server inside WSL on Windows.",
510514

511515
"settings.general.section.appearance": "المظهر",
512516
"settings.general.section.notifications": "إشعارات النظام",

packages/app/src/i18n/br.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,9 @@ export const dict = {
512512
"settings.section.server": "Servidor",
513513
"settings.tab.general": "Geral",
514514
"settings.tab.shortcuts": "Atalhos",
515+
"settings.desktop.section.wsl": "WSL",
516+
"settings.desktop.wsl.title": "WSL integration",
517+
"settings.desktop.wsl.description": "Run the Kilo server inside WSL on Windows.",
515518

516519
"settings.general.section.appearance": "Aparência",
517520
"settings.general.section.notifications": "Notificações do sistema",

packages/app/src/i18n/bs.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,9 @@ export const dict = {
539539
"settings.section.server": "Server",
540540
"settings.tab.general": "Opšte",
541541
"settings.tab.shortcuts": "Prečice",
542+
"settings.desktop.section.wsl": "WSL",
543+
"settings.desktop.wsl.title": "WSL integration",
544+
"settings.desktop.wsl.description": "Run the Kilo server inside WSL on Windows.",
542545

543546
"settings.general.section.appearance": "Izgled",
544547
"settings.general.section.notifications": "Sistemske obavijesti",

0 commit comments

Comments
 (0)