Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,23 @@ file is the human-readable summary of what changed and why.

### Desktop / Web UI

- **The Mount project dialog can no longer silently unmount everything** —
opened moments after launch, before the sandbox state had loaded, the dialog
showed the mounted folders as an empty list: the list was captured once when
the dialog opened and never updated, so it stayed empty even after the state
arrived a beat later. Saving from that dialog then wrote the empty list back,
quietly unmounting every extra folder — while their projects stayed in the
sidebar, still looking mounted. The dialog now waits for the sandbox's state
before it builds the folder list.
- **The Work sidebar's folder button now mounts a folder into the sandbox** —
the button next to **Projects** on the Work side used to open a directory
picker over the sandbox's own filesystem, the same "Add project" it is on
Code — where folders reach a sandbox by being mounted, not picked. It now
opens the mount dialog for the sandbox, the same one as the server's menu
item in Settings, which is itself renamed from **Folders...** to **Mount
project** to match. The dialog's title follows suit (it was **Sandbox
folders**), and its intro line no longer warns that the agent edits mounted
files without asking. Code keeps the existing Add project behavior.
- **First-run setup on the desktop app** — with no AI provider configured, the
desktop app now opens a setup dialog that cannot be dismissed until a
provider exists, offering the same three paths as the CLI's `codev
Expand Down
25 changes: 21 additions & 4 deletions packages/app/src/components/app-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,33 @@ export function AppSidebar(props: { class?: string; debugTools?: { visible: bool
!(model.projects().length === 0 && model.recentlyClosed().length > 0)
}
>
<TooltipV2 placement="bottom" value={language.t("home.project.add")}>
{/* Fork: on Work this mounts a host folder into the sandbox — the
same dialog as the server row's Mount project item. The Code
behavior (a picker over the server's own filesystem) would
browse the guest here, which is not how folders reach a
sandbox. */}
<TooltipV2
placement="bottom"
value={language.t(side() === "work" ? "sandbox.server.mounts" : "home.project.add")}
>
<IconButtonV2
data-action="home-add-project"
data-action={side() === "work" ? "work-mount-project" : "home-add-project"}
variant="ghost-muted"
size="large"
class="titlebar-icon [&_[data-slot=icon-svg]]:text-v2-icon-icon-muted"
icon={<IconV2 name="folder-add-left" />}
disabled={global.servers.health[ServerConnection.key(sideServers()[0]!)]?.healthy === false}
onClick={() => model.chooseProject(sideServers()[0]!)}
aria-label={language.t("home.project.add")}
onClick={() => {
const conn = sideServers()[0]!
if (side() === "work") {
void import("@/sandbox/dialog-mounts").then((module) => {
dialog.show(() => <module.DialogSandboxMounts id={ServerConnection.key(conn)} />)
})
return
}
model.chooseProject(conn)
}}
aria-label={language.t(side() === "work" ? "sandbox.server.mounts" : "home.project.add")}
/>
</TooltipV2>
</Show>
Expand Down
7 changes: 3 additions & 4 deletions packages/app/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ export const dict = {
"sandbox.server.start": "Start",
"sandbox.server.stop": "Stop",
"sandbox.server.reset": "Reset",
"sandbox.server.mounts": "Folders...",
"sandbox.server.mounts": "Mount project",
"sandbox.status.starting": "Starting...",
"sandbox.status.ready": "Ready",
"sandbox.status.stopped": "Stopped",
Expand Down Expand Up @@ -425,9 +425,8 @@ export const dict = {
"sandbox.add.resourcesInvalid": "CPUs must be {{minCpus}}\u2013{{maxCpus}} and memory {{minMemory}}\u2013{{maxMemory}} GB.",
"sandbox.add.downloadProgress": "Downloading image… {{done}}/{{total}} MB",
"sandbox.add.creating": "Creating the sandbox\u2026 this takes a few minutes.",
"sandbox.mounts.title": "Sandbox folders",
"sandbox.mounts.hint":
"Folders mounted here are the only ones on this computer the sandbox can reach, and the agent edits files in them without asking.",
"sandbox.mounts.title": "Mount project",
"sandbox.mounts.hint": "Folders mounted here are the only ones on this computer the sandbox can reach.",
"sandbox.mounts.primary": "Project folder",
"sandbox.mounts.empty": "No extra folders are mounted.",
"sandbox.mounts.add": "Add folder...",
Expand Down
7 changes: 3 additions & 4 deletions packages/app/src/i18n/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ export const dict = {
"sandbox.server.start": "Iniciar",
"sandbox.server.stop": "Detener",
"sandbox.server.reset": "Restablecer",
"sandbox.server.mounts": "Carpetas...",
"sandbox.server.mounts": "Montar proyecto",
"sandbox.status.starting": "Iniciando...",
"sandbox.status.ready": "Listo",
"sandbox.status.stopped": "Detenido",
Expand Down Expand Up @@ -416,9 +416,8 @@ export const dict = {
"sandbox.add.resourcesInvalid": "Las CPU deben ser {{minCpus}}\u2013{{maxCpus}} y la memoria {{minMemory}}\u2013{{maxMemory}} GB.",
"sandbox.add.downloadProgress": "Descargando imagen… {{done}}/{{total}} MB",
"sandbox.add.creating": "Creando el sandbox\u2026 esto tarda unos minutos.",
"sandbox.mounts.title": "Carpetas del sandbox",
"sandbox.mounts.hint":
"Las carpetas montadas aquí son las únicas de este equipo a las que el sandbox puede acceder, y el agente edita archivos en ellas sin preguntar.",
"sandbox.mounts.title": "Montar proyecto",
"sandbox.mounts.hint": "Las carpetas montadas aquí son las únicas de este equipo a las que el sandbox puede acceder.",
"sandbox.mounts.primary": "Carpeta del proyecto",
"sandbox.mounts.empty": "No hay carpetas adicionales montadas.",
"sandbox.mounts.add": "Añadir carpeta...",
Expand Down
29 changes: 29 additions & 0 deletions packages/app/src/sandbox/dialog-mounts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,35 @@ import { validateSandboxMounts } from "./mounts-model"
// else can be added and removed, applied hot on Windows and by restarting the
// sandbox elsewhere.
export function DialogSandboxMounts(props: { id: string }) {
const language = useLanguage()
const sandboxes = useSandboxes()
const item = () => sandboxes.state.data?.sandboxes.find((x) => x.config.id === props.id)
// The editor snapshots the mount list when it is created, so it must not be
// created before the sandbox state has loaded: a snapshot taken while the
// query is still empty freezes as an empty list, reads as a pending removal
// of every mount once the state arrives, and saving it silently unmounts
// everything.
return (
<Show
when={item()}
fallback={
<Dialog fit>
<DialogHeader hideClose={true}>
<DialogTitle>{language.t("sandbox.mounts.title")}</DialogTitle>
</DialogHeader>
<DividerV2 />
<DialogBody class="flex items-center justify-center px-4 py-6">
<LoaderV2 />
</DialogBody>
</Dialog>
}
>
<SandboxMountsEditor id={props.id} />
</Show>
)
}

function SandboxMountsEditor(props: { id: string }) {
const language = useLanguage()
const platform = usePlatform()
const dialog = useDialog()
Expand Down
Loading