Merge remote-tracking branch 'upstream/dev' into desktop-wsl-onboarding
This commit is contained in:
@@ -3,7 +3,7 @@ import { createStore, produce, reconcile } from "solid-js/store"
|
||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||
import { showToast } from "@opencode-ai/ui/toast"
|
||||
import { useParams } from "@solidjs/router"
|
||||
import { getFilename } from "@opencode-ai/shared/util/path"
|
||||
import { getFilename } from "@opencode-ai/core/util/path"
|
||||
import { useSDK } from "./sdk"
|
||||
import { useSync } from "./sync"
|
||||
import { useLanguage } from "@/context/language"
|
||||
|
||||
@@ -8,11 +8,10 @@ import type {
|
||||
Todo,
|
||||
} from "@opencode-ai/sdk/v2/client"
|
||||
import { showToast } from "@opencode-ai/ui/toast"
|
||||
import { getFilename } from "@opencode-ai/shared/util/path"
|
||||
import { createContext, getOwner, onCleanup, onMount, type ParentProps, untrack, useContext } from "solid-js"
|
||||
import { getFilename } from "@opencode-ai/core/util/path"
|
||||
import { batch, createContext, getOwner, onCleanup, onMount, type ParentProps, untrack, useContext } from "solid-js"
|
||||
import { createStore, produce, reconcile } from "solid-js/store"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { Persist, persisted } from "@/utils/persist"
|
||||
import type { InitError } from "../pages/error"
|
||||
import { useGlobalSDK } from "./global-sdk"
|
||||
import { useServer } from "./server"
|
||||
@@ -25,7 +24,6 @@ import { estimateRootSessionTotal, loadRootSessionsWithFallback } from "./global
|
||||
import { trimSessions } from "./global-sync/session-trim"
|
||||
import type { ProjectMeta } from "./global-sync/types"
|
||||
import { SESSION_RECENT_LIMIT } from "./global-sync/types"
|
||||
import { sanitizeProject } from "./global-sync/utils"
|
||||
import { formatServerError } from "@/utils/server-errors"
|
||||
import { queryOptions, skipToken, useQueryClient } from "@tanstack/solid-query"
|
||||
|
||||
@@ -58,15 +56,10 @@ function createGlobalSync() {
|
||||
const sessionLoads = new Map<string, Promise<void>>()
|
||||
const sessionMeta = new Map<string, { limit: number }>()
|
||||
|
||||
const [projectCache, setProjectCache, projectInit] = persisted(
|
||||
Persist.global("globalSync.project", ["globalSync.project.v1"]),
|
||||
createStore({ value: [] as Project[] }),
|
||||
)
|
||||
|
||||
const [globalStore, setGlobalStore] = createStore<GlobalStore>({
|
||||
ready: false,
|
||||
path: { state: "", config: "", worktree: "", directory: "", home: "" },
|
||||
project: projectCache.value,
|
||||
project: [],
|
||||
session_todo: {},
|
||||
provider: { all: [], connected: [], default: {} },
|
||||
provider_auth: {},
|
||||
@@ -75,37 +68,18 @@ function createGlobalSync() {
|
||||
})
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
let active = true
|
||||
let projectWritten = false
|
||||
let bootedAt = 0
|
||||
let bootingRoot = false
|
||||
let eventFrame: number | undefined
|
||||
let eventTimer: ReturnType<typeof setTimeout> | undefined
|
||||
|
||||
onCleanup(() => {
|
||||
active = false
|
||||
})
|
||||
onCleanup(() => {
|
||||
if (eventFrame !== undefined) cancelAnimationFrame(eventFrame)
|
||||
if (eventTimer !== undefined) clearTimeout(eventTimer)
|
||||
})
|
||||
|
||||
const cacheProjects = () => {
|
||||
setProjectCache(
|
||||
"value",
|
||||
untrack(() => globalStore.project.map(sanitizeProject)),
|
||||
)
|
||||
}
|
||||
|
||||
const setProjects = (next: Project[] | ((draft: Project[]) => void)) => {
|
||||
projectWritten = true
|
||||
if (typeof next === "function") {
|
||||
setGlobalStore("project", produce(next))
|
||||
cacheProjects()
|
||||
return
|
||||
}
|
||||
const setProjects = (next: Project[] | ((draft: Project[]) => Project[])) => {
|
||||
setGlobalStore("project", next)
|
||||
cacheProjects()
|
||||
}
|
||||
|
||||
const setBootStore = ((...input: unknown[]) => {
|
||||
@@ -118,22 +92,12 @@ function createGlobalSync() {
|
||||
|
||||
const set = ((...input: unknown[]) => {
|
||||
if (input[0] === "project" && (Array.isArray(input[1]) || typeof input[1] === "function")) {
|
||||
setProjects(input[1] as Project[] | ((draft: Project[]) => void))
|
||||
setProjects(input[1] as Project[] | ((draft: Project[]) => Project[]))
|
||||
return input[1]
|
||||
}
|
||||
return (setGlobalStore as (...args: unknown[]) => unknown)(...input)
|
||||
}) as typeof setGlobalStore
|
||||
|
||||
if (projectInit instanceof Promise) {
|
||||
void projectInit.then(() => {
|
||||
if (!active) return
|
||||
if (projectWritten) return
|
||||
const cached = projectCache.value
|
||||
if (cached.length === 0) return
|
||||
setGlobalStore("project", cached)
|
||||
})
|
||||
}
|
||||
|
||||
const setSessionTodo = (sessionID: string, todos: Todo[] | undefined) => {
|
||||
if (!sessionID) return
|
||||
if (!todos) {
|
||||
@@ -225,16 +189,18 @@ function createGlobalSync() {
|
||||
limit,
|
||||
permission: store.permission,
|
||||
})
|
||||
setStore(
|
||||
"sessionTotal",
|
||||
estimateRootSessionTotal({
|
||||
count: nonArchived.length,
|
||||
limit: x.limit,
|
||||
limited: x.limited,
|
||||
}),
|
||||
)
|
||||
setStore("session", reconcile(sessions, { key: "id" }))
|
||||
cleanupDroppedSessionCaches(store, setStore, sessions, setSessionTodo)
|
||||
batch(() => {
|
||||
setStore(
|
||||
"sessionTotal",
|
||||
estimateRootSessionTotal({
|
||||
count: nonArchived.length,
|
||||
limit: x.limit,
|
||||
limited: x.limited,
|
||||
}),
|
||||
)
|
||||
setStore("session", reconcile(sessions, { key: "id" }))
|
||||
cleanupDroppedSessionCaches(store, setStore, sessions, setSessionTodo)
|
||||
})
|
||||
sessionMeta.set(directory, { limit })
|
||||
})
|
||||
.catch((err) => {
|
||||
@@ -301,6 +267,19 @@ function createGlobalSync() {
|
||||
const event = e.details
|
||||
const recent = bootingRoot || Date.now() - bootedAt < 1500
|
||||
|
||||
if (event.type === "session.error") {
|
||||
const error = event.properties.error
|
||||
if (error?.name !== "MessageAbortedError") {
|
||||
console.error("[global-sync] session error", {
|
||||
scope: directory === "global" ? "global" : "workspace",
|
||||
directory: directory === "global" ? undefined : directory,
|
||||
project: directory === "global" ? undefined : getFilename(directory),
|
||||
sessionID: event.properties.sessionID,
|
||||
error,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (directory === "global") {
|
||||
applyGlobalEvent({
|
||||
event,
|
||||
|
||||
@@ -11,15 +11,14 @@ import type {
|
||||
Todo,
|
||||
} from "@opencode-ai/sdk/v2/client"
|
||||
import { showToast } from "@opencode-ai/ui/toast"
|
||||
import { getFilename } from "@opencode-ai/shared/util/path"
|
||||
import { retry } from "@opencode-ai/shared/util/retry"
|
||||
import { getFilename } from "@opencode-ai/core/util/path"
|
||||
import { retry } from "@opencode-ai/core/util/retry"
|
||||
import { batch } from "solid-js"
|
||||
import { reconcile, type SetStoreFunction, type Store } from "solid-js/store"
|
||||
import type { State, VcsCache } from "./types"
|
||||
import { cmp, normalizeAgentList, normalizeProviderList } from "./utils"
|
||||
import { formatServerError } from "@/utils/server-errors"
|
||||
import { QueryClient, queryOptions, skipToken } from "@tanstack/solid-query"
|
||||
import { loadSessionsQuery } from "../global-sync"
|
||||
|
||||
type GlobalStore = {
|
||||
ready: boolean
|
||||
@@ -80,9 +79,12 @@ export async function bootstrapGlobal(input: {
|
||||
() =>
|
||||
retry(() =>
|
||||
input.globalSDK.global.config.get().then((x) => {
|
||||
input.setGlobalStore("config", x.data!)
|
||||
input.setGlobalStore("config", reconcile(x.data!, { merge: false }))
|
||||
}),
|
||||
),
|
||||
]
|
||||
|
||||
const slow = [
|
||||
() =>
|
||||
input.queryClient.fetchQuery({
|
||||
...loadProvidersQuery(null, input.serverKey),
|
||||
@@ -94,9 +96,6 @@ export async function bootstrapGlobal(input: {
|
||||
}),
|
||||
),
|
||||
}),
|
||||
]
|
||||
|
||||
const slow = [
|
||||
() =>
|
||||
retry(() =>
|
||||
input.globalSDK.path.get().then((x) => {
|
||||
@@ -184,8 +183,44 @@ function warmSessions(input: {
|
||||
export const loadProvidersQuery = (directory: string | null, serverKey: string | undefined) =>
|
||||
queryOptions<null>({ queryKey: [serverKey, directory, "providers"], queryFn: skipToken })
|
||||
|
||||
export const loadAgentsQuery = (directory: string | null, serverKey: string | undefined) =>
|
||||
queryOptions<null>({ queryKey: [serverKey, directory, "agents"], queryFn: skipToken })
|
||||
export const loadAgentsQuery = (
|
||||
directory: string | null,
|
||||
serverKey: string | undefined,
|
||||
sdk?: OpencodeClient,
|
||||
transform?: (x: Awaited<ReturnType<OpencodeClient["app"]["agents"]>>) => void,
|
||||
) =>
|
||||
queryOptions<null>({
|
||||
queryKey: [serverKey, directory, "agents"],
|
||||
queryFn:
|
||||
sdk && transform
|
||||
? () =>
|
||||
retry(() =>
|
||||
sdk.app
|
||||
.agents()
|
||||
.then(transform)
|
||||
.then(() => null),
|
||||
)
|
||||
: skipToken,
|
||||
})
|
||||
|
||||
export const loadPathQuery = (
|
||||
directory: string | null,
|
||||
sdk?: OpencodeClient,
|
||||
transform?: (x: Awaited<ReturnType<OpencodeClient["path"]["get"]>>) => void,
|
||||
) =>
|
||||
queryOptions<Path>({
|
||||
queryKey: [directory, "path"],
|
||||
queryFn:
|
||||
sdk && transform
|
||||
? () =>
|
||||
retry(() =>
|
||||
sdk.path.get().then(async (x) => {
|
||||
transform(x)
|
||||
return x.data!
|
||||
}),
|
||||
)
|
||||
: skipToken,
|
||||
})
|
||||
|
||||
export async function bootstrapDirectory(input: {
|
||||
directory: string
|
||||
@@ -213,7 +248,7 @@ export async function bootstrapDirectory(input: {
|
||||
input.setStore("provider", input.global.provider)
|
||||
}
|
||||
if (Object.keys(input.store.config).length === 0 && Object.keys(input.global.config).length > 0) {
|
||||
input.setStore("config", input.global.config)
|
||||
input.setStore("config", reconcile(input.global.config, { merge: false }))
|
||||
}
|
||||
if (loading || input.store.provider.all.length === 0) {
|
||||
input.setStore("provider_ready", false)
|
||||
@@ -224,45 +259,30 @@ export async function bootstrapDirectory(input: {
|
||||
input.setStore("lsp", [])
|
||||
if (loading) input.setStore("status", "partial")
|
||||
|
||||
const fast = [() => Promise.resolve(input.loadSessions(input.directory))]
|
||||
|
||||
const errs = errors(await runAll(fast))
|
||||
if (errs.length > 0) {
|
||||
console.error("Failed to bootstrap instance", errs[0])
|
||||
const project = getFilename(input.directory)
|
||||
showToast({
|
||||
variant: "error",
|
||||
title: input.translate("toast.project.reloadFailed.title", { project }),
|
||||
description: formatServerError(errs[0], input.translate),
|
||||
})
|
||||
}
|
||||
|
||||
const rev = (providerRev.get(input.directory) ?? 0) + 1
|
||||
providerRev.set(input.directory, rev)
|
||||
;(async () => {
|
||||
const slow = [
|
||||
() => Promise.resolve(input.loadSessions(input.directory)),
|
||||
() =>
|
||||
input.queryClient.ensureQueryData({
|
||||
...loadAgentsQuery(input.directory, input.serverKey),
|
||||
queryFn: () =>
|
||||
retry(() => input.sdk.app.agents().then((x) => input.setStore("agent", normalizeAgentList(x.data)))).then(
|
||||
() => null,
|
||||
),
|
||||
}),
|
||||
() => retry(() => input.sdk.config.get().then((x) => input.setStore("config", x.data!))),
|
||||
input.queryClient.ensureQueryData(
|
||||
loadAgentsQuery(input.directory, input.serverKey, input.sdk, (x) =>
|
||||
input.setStore("agent", normalizeAgentList(x.data)),
|
||||
),
|
||||
),
|
||||
() =>
|
||||
retry(() => input.sdk.config.get().then((x) => input.setStore("config", reconcile(x.data!, { merge: false })))),
|
||||
() => retry(() => input.sdk.session.status().then((x) => input.setStore("session_status", x.data!))),
|
||||
() =>
|
||||
seededProject
|
||||
? Promise.resolve()
|
||||
: retry(() => input.sdk.project.current()).then((x) => input.setStore("project", x.data!.id)),
|
||||
() =>
|
||||
seededPath
|
||||
? Promise.resolve()
|
||||
: retry(() =>
|
||||
input.sdk.path.get().then((x) => {
|
||||
input.setStore("path", x.data!)
|
||||
const next = projectID(x.data?.directory ?? input.directory, input.global.project)
|
||||
if (next) input.setStore("project", next)
|
||||
}),
|
||||
),
|
||||
!seededProject &&
|
||||
(() => retry(() => input.sdk.project.current()).then((x) => input.setStore("project", x.data!.id))),
|
||||
!seededPath &&
|
||||
(() =>
|
||||
input.queryClient.ensureQueryData(
|
||||
loadPathQuery(input.directory, input.sdk, (x) => {
|
||||
const next = projectID(x.data?.directory ?? input.directory, input.global.project)
|
||||
if (next) input.setStore("project", next)
|
||||
}),
|
||||
)),
|
||||
() =>
|
||||
retry(() =>
|
||||
input.sdk.vcs.get().then((x) => {
|
||||
@@ -332,7 +352,28 @@ export async function bootstrapDirectory(input: {
|
||||
input.setStore("mcp_ready", true)
|
||||
}),
|
||||
),
|
||||
]
|
||||
() =>
|
||||
input.queryClient.ensureQueryData({
|
||||
...loadProvidersQuery(input.directory, input.serverKey),
|
||||
queryFn: () =>
|
||||
retry(() => input.sdk.provider.list())
|
||||
.then((x) => {
|
||||
if (providerRev.get(input.directory) !== rev) return
|
||||
input.setStore("provider", normalizeProviderList(x.data!))
|
||||
input.setStore("provider_ready", true)
|
||||
})
|
||||
.catch((err) => {
|
||||
if (providerRev.get(input.directory) !== rev) console.error("Failed to refresh provider list", err)
|
||||
const project = getFilename(input.directory)
|
||||
showToast({
|
||||
variant: "error",
|
||||
title: input.translate("toast.project.reloadFailed.title", { project }),
|
||||
description: formatServerError(err, input.translate),
|
||||
})
|
||||
})
|
||||
.then(() => null),
|
||||
}),
|
||||
].filter(Boolean) as (() => Promise<any>)[]
|
||||
|
||||
await waitForPaint()
|
||||
const slowErrs = errors(await runAll(slow))
|
||||
@@ -346,29 +387,6 @@ export async function bootstrapDirectory(input: {
|
||||
})
|
||||
}
|
||||
|
||||
if (loading && errs.length === 0 && slowErrs.length === 0) input.setStore("status", "complete")
|
||||
|
||||
const rev = (providerRev.get(input.directory) ?? 0) + 1
|
||||
providerRev.set(input.directory, rev)
|
||||
void input.queryClient.ensureQueryData({
|
||||
...loadProvidersQuery(input.directory, input.serverKey),
|
||||
queryFn: () =>
|
||||
retry(() => input.sdk.provider.list())
|
||||
.then((x) => {
|
||||
if (providerRev.get(input.directory) !== rev) return
|
||||
input.setStore("provider", normalizeProviderList(x.data!))
|
||||
input.setStore("provider_ready", true)
|
||||
})
|
||||
.catch((err) => {
|
||||
if (providerRev.get(input.directory) !== rev) console.error("Failed to refresh provider list", err)
|
||||
const project = getFilename(input.directory)
|
||||
showToast({
|
||||
variant: "error",
|
||||
title: input.translate("toast.project.reloadFailed.title", { project }),
|
||||
description: formatServerError(err, input.translate),
|
||||
})
|
||||
})
|
||||
.then(() => null),
|
||||
})
|
||||
if (loading && slowErrs.length === 0) input.setStore("status", "complete")
|
||||
})()
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ import {
|
||||
type VcsCache,
|
||||
} from "./types"
|
||||
import { canDisposeDirectory, pickDirectoriesToEvict } from "./eviction"
|
||||
import { useQuery } from "@tanstack/solid-query"
|
||||
import { loadPathQuery } from "./bootstrap"
|
||||
|
||||
export function createChildStoreManager(input: {
|
||||
owner: Owner
|
||||
@@ -161,16 +163,21 @@ export function createChildStoreManager(input: {
|
||||
|
||||
const init = () =>
|
||||
createRoot((dispose) => {
|
||||
const initialMeta = meta[0].value
|
||||
const initialIcon = icon[0].value
|
||||
|
||||
const pathQuery = useQuery(() => loadPathQuery(directory))
|
||||
const child = createStore<State>({
|
||||
project: "",
|
||||
projectMeta: initialMeta,
|
||||
projectMeta: undefined,
|
||||
icon: initialIcon,
|
||||
provider_ready: false,
|
||||
provider: { all: [], connected: [], default: {} },
|
||||
config: {},
|
||||
path: { state: "", config: "", worktree: "", directory: "", home: "" },
|
||||
get path() {
|
||||
if (pathQuery.isLoading || !pathQuery.data)
|
||||
return { state: "", config: "", worktree: "", directory: "", home: "" }
|
||||
return pathQuery.data
|
||||
},
|
||||
status: "loading" as const,
|
||||
agent: [],
|
||||
command: [],
|
||||
@@ -207,11 +214,6 @@ export function createChildStoreManager(input: {
|
||||
child[1]("vcs", (value) => value ?? cached)
|
||||
})
|
||||
|
||||
onPersistedInit(meta[2], () => {
|
||||
if (child[0].projectMeta !== initialMeta) return
|
||||
child[1]("projectMeta", meta[0].value)
|
||||
})
|
||||
|
||||
onPersistedInit(icon[2], () => {
|
||||
if (child[0].icon !== initialIcon) return
|
||||
child[1]("icon", icon[0].value)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Binary } from "@opencode-ai/shared/util/binary"
|
||||
import { Binary } from "@opencode-ai/core/util/binary"
|
||||
import { produce, reconcile, type SetStoreFunction, type Store } from "solid-js/store"
|
||||
import type {
|
||||
Message,
|
||||
@@ -21,7 +21,7 @@ const SKIP_PARTS = new Set(["patch", "step-start", "step-finish"])
|
||||
export function applyGlobalEvent(input: {
|
||||
event: { type: string; properties?: unknown }
|
||||
project: Project[]
|
||||
setGlobalProject: (next: Project[] | ((draft: Project[]) => void)) => void
|
||||
setGlobalProject: (next: Project[] | ((draft: Project[]) => Project[])) => void
|
||||
refresh: () => void
|
||||
}) {
|
||||
if (input.event.type === "global.disposed" || input.event.type === "server.connected") {
|
||||
@@ -33,14 +33,18 @@ export function applyGlobalEvent(input: {
|
||||
const properties = input.event.properties as Project
|
||||
const result = Binary.search(input.project, properties.id, (s) => s.id)
|
||||
if (result.found) {
|
||||
input.setGlobalProject((draft) => {
|
||||
draft[result.index] = { ...draft[result.index], ...properties }
|
||||
})
|
||||
input.setGlobalProject(
|
||||
produce((draft) => {
|
||||
draft[result.index] = { ...draft[result.index], ...properties }
|
||||
}),
|
||||
)
|
||||
return
|
||||
}
|
||||
input.setGlobalProject((draft) => {
|
||||
draft.splice(result.index, 0, properties)
|
||||
})
|
||||
input.setGlobalProject(
|
||||
produce((draft) => {
|
||||
draft.splice(result.index, 0, properties)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
function cleanupSessionCaches(
|
||||
|
||||
@@ -391,37 +391,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
||||
? globalSync.data.project.find((x) => x.id === projectID)
|
||||
: globalSync.data.project.find((x) => x.worktree === project.worktree)
|
||||
|
||||
const local = childStore.projectMeta
|
||||
const localOverride =
|
||||
local?.name !== undefined ||
|
||||
local?.commands?.start !== undefined ||
|
||||
local?.icon?.override !== undefined ||
|
||||
local?.icon?.color !== undefined
|
||||
|
||||
const base = {
|
||||
...metadata,
|
||||
...project,
|
||||
icon: {
|
||||
url: metadata?.icon?.url,
|
||||
override: metadata?.icon?.override ?? childStore.icon,
|
||||
color: metadata?.icon?.color,
|
||||
},
|
||||
}
|
||||
|
||||
const isGlobal = projectID === "global" || (metadata?.id === undefined && localOverride)
|
||||
if (!isGlobal) return base
|
||||
|
||||
return {
|
||||
...base,
|
||||
id: base.id ?? "global",
|
||||
name: local?.name,
|
||||
commands: local?.commands,
|
||||
icon: {
|
||||
url: base.icon?.url,
|
||||
override: local?.icon?.override,
|
||||
color: local?.icon?.color,
|
||||
},
|
||||
}
|
||||
return { ...metadata, ...project }
|
||||
}
|
||||
|
||||
const roots = createMemo(() => {
|
||||
@@ -516,7 +486,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
||||
}
|
||||
|
||||
for (const project of projects) {
|
||||
if (project.icon?.color) continue
|
||||
if (project.icon?.color || project.icon?.override || project.icon?.url) continue
|
||||
const worktree = project.worktree
|
||||
const existing = colors[worktree]
|
||||
const color = existing ?? pickAvailableColor(used)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||
import { base64Encode } from "@opencode-ai/shared/util/encode"
|
||||
import { base64Encode } from "@opencode-ai/core/util/encode"
|
||||
import { useParams } from "@solidjs/router"
|
||||
import { batch, createEffect, createMemo } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
|
||||
@@ -7,8 +7,8 @@ import { useGlobalSync } from "./global-sync"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { useSettings } from "@/context/settings"
|
||||
import { Binary } from "@opencode-ai/shared/util/binary"
|
||||
import { base64Encode } from "@opencode-ai/shared/util/encode"
|
||||
import { Binary } from "@opencode-ai/core/util/binary"
|
||||
import { base64Encode } from "@opencode-ai/core/util/encode"
|
||||
import { decode64 } from "@/utils/base64"
|
||||
import { EventSessionError } from "@opencode-ai/sdk/v2"
|
||||
import { Persist, persisted } from "@/utils/persist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import type { PermissionRequest, Session } from "@opencode-ai/sdk/v2/client"
|
||||
import { base64Encode } from "@opencode-ai/shared/util/encode"
|
||||
import { base64Encode } from "@opencode-ai/core/util/encode"
|
||||
import { autoRespondsPermission, isDirectoryAutoAccepting } from "./permission-auto-respond"
|
||||
|
||||
const session = (input: { id: string; parentID?: string }) =>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { base64Encode } from "@opencode-ai/shared/util/encode"
|
||||
import { base64Encode } from "@opencode-ai/core/util/encode"
|
||||
|
||||
export function acceptKey(sessionID: string, directory?: string) {
|
||||
if (!directory) return sessionID
|
||||
|
||||
@@ -154,11 +154,11 @@ export type Platform = {
|
||||
/** Storage mechanism, defaults to localStorage */
|
||||
storage?: (name?: string) => SyncStorage | AsyncStorage
|
||||
|
||||
/** Check for updates (Tauri only) */
|
||||
/** Check for a downloadable desktop update */
|
||||
checkUpdate?(): Promise<UpdateInfo>
|
||||
|
||||
/** Install updates (Tauri only) */
|
||||
update?(): Promise<void>
|
||||
/** Install the downloaded update using the platform restart flow */
|
||||
updateAndRestart?(): Promise<void>
|
||||
|
||||
/** Fetch override */
|
||||
fetch?: typeof fetch
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||
import { checksum } from "@opencode-ai/shared/util/encode"
|
||||
import { checksum } from "@opencode-ai/core/util/encode"
|
||||
import { useParams } from "@solidjs/router"
|
||||
import { batch, createMemo, createRoot, getOwner, onCleanup } from "solid-js"
|
||||
import { createStore, type SetStoreFunction } from "solid-js/store"
|
||||
@@ -185,9 +185,9 @@ function createPromptSession(dir: string, id: string | undefined) {
|
||||
|
||||
return {
|
||||
ready,
|
||||
current: createMemo(() => store.prompt),
|
||||
current: () => store.prompt,
|
||||
cursor: createMemo(() => store.cursor),
|
||||
dirty: createMemo(() => !isPromptEqual(store.prompt, DEFAULT_PROMPT)),
|
||||
dirty: () => !isPromptEqual(store.prompt, DEFAULT_PROMPT),
|
||||
context: {
|
||||
items: createMemo(() => store.context.items),
|
||||
add(item: ContextItem) {
|
||||
@@ -280,7 +280,7 @@ export const { use: usePrompt, provider: PromptProvider } = createSimpleContext(
|
||||
const pick = (scope?: Scope) => (scope ? load(scope.dir, scope.id) : session())
|
||||
|
||||
return {
|
||||
ready: () => session().ready(),
|
||||
ready: () => session().ready,
|
||||
current: () => session().current(),
|
||||
cursor: () => session().cursor(),
|
||||
dirty: () => session().dirty(),
|
||||
|
||||
@@ -31,6 +31,7 @@ export interface Settings {
|
||||
showReasoningSummaries: boolean
|
||||
shellToolPartsExpanded: boolean
|
||||
editToolPartsExpanded: boolean
|
||||
showSessionProgressBar: boolean
|
||||
}
|
||||
updates: {
|
||||
startup: boolean
|
||||
@@ -56,7 +57,8 @@ export const terminalDefault = "JetBrainsMono Nerd Font Mono"
|
||||
const monoFallback =
|
||||
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
|
||||
const sansFallback = 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'
|
||||
const terminalFallback = '"JetBrainsMono Nerd Font Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
|
||||
const terminalFallback =
|
||||
'"JetBrainsMono Nerd Font Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
|
||||
|
||||
const monoBase = monoFallback
|
||||
const sansBase = sansFallback
|
||||
@@ -114,6 +116,7 @@ const defaultSettings: Settings = {
|
||||
showReasoningSummaries: false,
|
||||
shellToolPartsExpanded: false,
|
||||
editToolPartsExpanded: false,
|
||||
showSessionProgressBar: true,
|
||||
},
|
||||
updates: {
|
||||
startup: true,
|
||||
@@ -226,6 +229,13 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
|
||||
setEditToolPartsExpanded(value: boolean) {
|
||||
setStore("general", "editToolPartsExpanded", value)
|
||||
},
|
||||
showSessionProgressBar: withFallback(
|
||||
() => store.general?.showSessionProgressBar,
|
||||
defaultSettings.general.showSessionProgressBar,
|
||||
),
|
||||
setShowSessionProgressBar(value: boolean) {
|
||||
setStore("general", "showSessionProgressBar", value)
|
||||
},
|
||||
},
|
||||
updates: {
|
||||
startup: withFallback(() => store.updates?.startup, defaultSettings.updates.startup),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { batch, createMemo } from "solid-js"
|
||||
import { createStore, produce, reconcile } from "solid-js/store"
|
||||
import { Binary } from "@opencode-ai/shared/util/binary"
|
||||
import { retry } from "@opencode-ai/shared/util/retry"
|
||||
import { Binary } from "@opencode-ai/core/util/binary"
|
||||
import { retry } from "@opencode-ai/core/util/retry"
|
||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||
import {
|
||||
clearSessionPrefetch,
|
||||
|
||||
Reference in New Issue
Block a user