Open a folder chip on its folder, in the folder that really holds it

A bot's folder chip went to bare /admin/files instead of the folder it
named, and even once there the computer volume pointed at the wrong
directory, so the folder a bot linked to looked empty.

The chip lost its folder because folderRefUrl only accepted a ref's
volume name (computer) while the chip hands it the browser's
(computers), so storageVolumeFromRef found nothing and the whole query
was dropped. Both spellings resolve now, through parseStorageSpec, so a
bad owner falls back cleanly instead of emitting owner=0.

The deeper bug: a computer's home was assumed to be
DATA_DIR/computers/<id>, but on a bind mount it can be anywhere on the
docker host, and a computer the app didn't provision (the main one on a
server reached at MAIN_COMPUTER_URL, i.e. a dev server outside docker)
records nothing at all. resolveComputerHomeDir holds the precedence:
the directory provisionComputer actually bound in
(containers.metadata.data_dir), else the new MAIN_COMPUTER_DATA_DIR app
setting for the main computer, else the conventional folder.

A path that isn't on disk also no longer reads as an empty folder:
listStorageEntries reports it as missing and the page says so, so a
stale folder link can't look like a folder nothing has written yet.
This commit is contained in:
2026-10-22 10:57:14 +01:00
parent 841149921b
commit fd75111bb1
13 changed files with 329 additions and 17 deletions
@@ -4,6 +4,7 @@ import Span from "@moduletrace/twui/layout/Span";
import Stack from "@moduletrace/twui/layout/Stack";
import type { StorageFolderPayload, StorageSpecOption } from "@/src/types";
import type { StorageSpec } from "@/src/utils/storage-volumes";
import { emptyFolderText, folderSummary } from "../(functions)/folder-text";
import FilesBreadcrumbRow from "../(partials)/files-breadcrumb-row";
import FilesSearchInput from "../(partials)/files-search-input";
import FilesVolumeRow from "../(partials)/files-volume-row";
@@ -81,9 +82,7 @@ export default function FilesBrowser({
"Files"}
</H2>
<Span className="text-[13px] leading-[18px] text-ink-secondary">
{folder
? `${folder.label}: ${folder.total} item(s). Everything a bot, a workspace or a computer keeps for you.`
: "Pick a folder to look inside."}
{folderSummary({ folder })}
</Span>
</Stack>
<FilesBreadcrumbRow
@@ -111,12 +110,7 @@ export default function FilesBrowser({
))
) : (
<Span className="py-8 text-[13px] leading-[18px] text-ink-secondary">
{loading
? "Loading..."
: error ||
(search
? `Nothing matches "${search}".`
: "This folder is empty.")}
{emptyFolderText({ folder, search, loading, error })}
</Span>
)}
</Stack>