mirror of
https://github.com/NeuralNomadsAI/CodeNomad.git
synced 2026-08-11 09:23:32 +00:00
Merge c8a704d729 into 31414521f6
This commit is contained in:
commit
d2726892cf
4 changed files with 84 additions and 6 deletions
|
|
@ -220,6 +220,13 @@ export interface FileSystemFileContentResponse {
|
|||
encoding: "utf-8" | "base64"
|
||||
}
|
||||
|
||||
export interface DetectPathExistingInRecentResponse {
|
||||
exists: boolean
|
||||
currentPath: string
|
||||
currentReal: string
|
||||
foundResult: string | undefined
|
||||
}
|
||||
|
||||
export interface ConfigFileDescriptor {
|
||||
id: string
|
||||
label: string
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { FastifyInstance } from "fastify"
|
||||
import { z } from "zod"
|
||||
import fs from "node:fs/promises"
|
||||
import { FileSystemBrowser } from "../../filesystem/browser"
|
||||
import { RecentFolder, RecentFolderSchema } from '../../config/schema'
|
||||
|
||||
interface RouteDeps {
|
||||
fileSystemBrowser: FileSystemBrowser
|
||||
|
|
@ -21,6 +23,11 @@ const FilesystemFileContentQuerySchema = z.object({
|
|||
encoding: z.enum(["utf-8", "base64"]).optional(),
|
||||
})
|
||||
|
||||
const FilesystemFileRealpathQuerySchema = z.object({
|
||||
currentPath: z.string(),
|
||||
recentPaths: z.array(z.string()).default([]),
|
||||
})
|
||||
|
||||
export function registerFilesystemRoutes(app: FastifyInstance, deps: RouteDeps) {
|
||||
app.get("/api/filesystem", async (request, reply) => {
|
||||
const query = FilesystemQuerySchema.parse(request.query ?? {})
|
||||
|
|
@ -66,4 +73,39 @@ export function registerFilesystemRoutes(app: FastifyInstance, deps: RouteDeps)
|
|||
reply.code(400).type("text/plain").send((error as Error).message)
|
||||
}
|
||||
})
|
||||
|
||||
app.post("/api/filesystem/detect-path-existing-in-recent", async (request, reply) => {
|
||||
const query = FilesystemFileRealpathQuerySchema.parse(request.body ?? {})
|
||||
|
||||
try {
|
||||
const currentPath = query.currentPath
|
||||
const currentReal = await fs.realpath(currentPath)
|
||||
|
||||
let exists = false
|
||||
let foundResult: string | undefined
|
||||
|
||||
const fn = async (path: string) => {
|
||||
await fs.access(path, fs.constants.F_OK)
|
||||
return currentReal === await fs.realpath(path)
|
||||
}
|
||||
|
||||
for (const path of query.recentPaths) {
|
||||
if (currentPath === path || currentReal === path || await fn(path).catch(() => false)) {
|
||||
exists = true
|
||||
foundResult = path
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
exists,
|
||||
currentPath,
|
||||
currentReal,
|
||||
foundResult
|
||||
}
|
||||
} catch (error) {
|
||||
reply.code(400).type("text/plain").send((error as Error).message)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import {
|
|||
showFolderSelection,
|
||||
setShowFolderSelection,
|
||||
} from "./stores/ui"
|
||||
import { useConfig } from "./stores/preferences"
|
||||
import { recentFolders, useConfig } from "./stores/preferences"
|
||||
import {
|
||||
createInstance,
|
||||
getExistingInstanceForFolder,
|
||||
|
|
@ -70,6 +70,9 @@ import {
|
|||
selectInstanceTab,
|
||||
selectSidecarTab,
|
||||
} from "./stores/app-tabs"
|
||||
import { serverApi } from './lib/api-client'
|
||||
import { RecentFolder } from '../../server/src/api-types'
|
||||
import { Instance } from './types/instance'
|
||||
|
||||
const log = getLogger("actions")
|
||||
|
||||
|
|
@ -280,13 +283,31 @@ const App: Component = () => {
|
|||
if (!folderPath) {
|
||||
return
|
||||
}
|
||||
|
||||
let existingInstance = getExistingInstanceForFolder(folderPath)
|
||||
|
||||
if (!existingInstance) {
|
||||
const detectResult = await serverApi.detectPathExistingInRecent(folderPath, [
|
||||
...(Array.from(instances().values()) as Instance[]).reduce((acc: string[], instance: Instance) => {
|
||||
if (instance.status === "stopped") return acc
|
||||
acc.push(instance.folder)
|
||||
return acc
|
||||
}, [] as string[]),
|
||||
...recentFolders().map((folder: RecentFolder) => folder.path),
|
||||
]).catch(() => null)
|
||||
|
||||
if (detectResult?.exists) {
|
||||
folderPath = detectResult.foundResult!
|
||||
existingInstance = getExistingInstanceForFolder(folderPath)
|
||||
}
|
||||
}
|
||||
|
||||
const selectedBinary = binaryPath || serverSettings().opencodeBinary || "opencode"
|
||||
const projectName = getProjectNameForFolder(folderPath)
|
||||
recordWorkspaceLaunch(folderPath, selectedBinary)
|
||||
clearLaunchError()
|
||||
|
||||
if (!options?.forceNew) {
|
||||
const existingInstance = getExistingInstanceForFolder(folderPath)
|
||||
if (existingInstance) {
|
||||
setAlreadyOpenFolderChoice({ folderPath, binaryPath: selectedBinary, instanceId: existingInstance.id })
|
||||
return
|
||||
|
|
@ -500,7 +521,7 @@ const App: Component = () => {
|
|||
const tauriBridge = (window as { __TAURI__?: { event?: { listen: (event: string, handler: (event: { payload: unknown }) => void) => Promise<() => void> } } }).__TAURI__
|
||||
if (tauriBridge?.event) {
|
||||
let unlistenMenu: (() => void) | null = null
|
||||
|
||||
|
||||
tauriBridge.event.listen("menu:newInstance", () => {
|
||||
handleNewInstanceRequest()
|
||||
}).then((unlisten) => {
|
||||
|
|
@ -542,7 +563,7 @@ const App: Component = () => {
|
|||
<p class="text-xs font-medium text-muted uppercase tracking-wide mb-1">{t("app.launchError.binaryPathLabel")}</p>
|
||||
<p class="text-sm font-mono text-primary break-all">{launchErrorPath()}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<Show when={launchErrorMessage()}>
|
||||
<div class="rounded-lg border border-base bg-surface-secondary p-4 flex flex-col gap-2 flex-1 min-h-0">
|
||||
<p class="text-xs font-medium text-muted uppercase tracking-wide">{t("app.launchError.errorOutputLabel")}</p>
|
||||
|
|
@ -667,7 +688,7 @@ const App: Component = () => {
|
|||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
|
||||
<SettingsScreen />
|
||||
<SideCarPickerDialog open={sidecarPickerOpen()} onClose={() => setSidecarPickerOpen(false)} onOpenSidecar={handleOpenSidecar} />
|
||||
<Show when={alreadyOpenFolderChoice()}>
|
||||
|
|
@ -694,7 +715,7 @@ const App: Component = () => {
|
|||
</Dialog.Portal>
|
||||
</Dialog>
|
||||
</Show>
|
||||
|
||||
|
||||
<AlertDialog />
|
||||
|
||||
<Toaster
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ import type {
|
|||
WorktreeCreateRequest,
|
||||
WorktreeGitDiffResponse,
|
||||
WorktreeGitStatusResponse,
|
||||
RecentFolder,
|
||||
DetectPathExistingInRecentResponse,
|
||||
} from "../../../server/src/api-types"
|
||||
import { getClientIdentity } from "./client-identity"
|
||||
import { getLogger } from "./logger"
|
||||
|
|
@ -484,6 +486,12 @@ export const serverApi = {
|
|||
}
|
||||
return request<FileSystemFileContentResponse>(`/api/filesystem/files/content?${params.toString()}`)
|
||||
},
|
||||
detectPathExistingInRecent(currentPath: string, recentPaths: string[]): Promise<DetectPathExistingInRecentResponse> {
|
||||
return request<DetectPathExistingInRecentResponse>(`/api/filesystem/detect-path-existing-in-recent`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ currentPath, recentPaths }),
|
||||
})
|
||||
},
|
||||
readInstanceData(id: string): Promise<InstanceData> {
|
||||
return request<InstanceData>(`/api/storage/instances/${encodeURIComponent(id)}`)
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue