Chat tabs, chat mode, own modes, compaction notice, CLI binaries again
- Terminals and file managers as tabs of a chat; chat terminals keep running on the server (no idle timeout) and re-attach from any device - "Just chat" in New session and a built-in chat mode with a lean prompt and only web tools; custom modes with kind (coding/conversation), read-only and tool picker - Mode selector as a dropdown - The chat shows running compactions (automatic: tokens and share of the limit) and labels summaries - Docker image builds and serves the terminal client binaries again - Fix: atomic session updates; background title/usage writes no longer undo each other (auto compaction triggered one turn late) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
242b80605c
commit
c850586a81
29 files changed
+831
-187
No files matched your search
@@ -56,10 +56,16 @@ function icon(e: Entry) {
|
||||
return <Icon name="file" className="ficon" />;
|
||||
}
|
||||
|
||||
export function Files({ onMenu }: { onMenu: () => void }) {
|
||||
/** Embedded in a chat tab: own start folder, no page header, folder kept by the caller. */
|
||||
export interface FilesEmbed {
|
||||
startDir: string;
|
||||
onDirChange?: (dir: string) => void;
|
||||
}
|
||||
|
||||
export function Files({ onMenu, embed }: { onMenu?: () => void; embed?: FilesEmbed }) {
|
||||
const t = useT();
|
||||
const roots = useFetch<{ path: string; name: string }[]>("/fs/roots").data ?? [];
|
||||
const [dir, setDir] = useState<string>(() => localStorage.getItem("codecraft.filesDir") ?? "");
|
||||
const [dir, setDir] = useState<string>(() => embed?.startDir ?? localStorage.getItem("codecraft.filesDir") ?? "");
|
||||
const [listing, setListing] = useState<Listing>();
|
||||
const [hidden, setHidden] = useState(false);
|
||||
const [error, setError] = useState<string>();
|
||||
@@ -79,7 +85,8 @@ export function Files({ onMenu }: { onMenu: () => void }) {
|
||||
const l = await get<Listing>(`/files/list?path=${encodeURIComponent(path)}&hidden=${hidden}`);
|
||||
setListing(l);
|
||||
setDir(l.path);
|
||||
localStorage.setItem("codecraft.filesDir", l.path);
|
||||
if (embed) embed.onDirChange?.(l.path);
|
||||
else localStorage.setItem("codecraft.filesDir", l.path);
|
||||
setError(undefined);
|
||||
} catch (e: any) {
|
||||
setError(e.message);
|
||||
@@ -143,7 +150,7 @@ export function Files({ onMenu }: { onMenu: () => void }) {
|
||||
|
||||
// "Edit file" links from other pages (e.g. automations) open the file directly.
|
||||
useEffect(() => {
|
||||
const target = sessionStorage.getItem("codecraft.openFile");
|
||||
const target = embed ? null : sessionStorage.getItem("codecraft.openFile");
|
||||
if (!target) return;
|
||||
sessionStorage.removeItem("codecraft.openFile");
|
||||
setDir(target.slice(0, target.lastIndexOf("/")) || "/");
|
||||
@@ -432,12 +439,14 @@ export function Files({ onMenu }: { onMenu: () => void }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="topbar">
|
||||
<button className="ghost menu-btn" onClick={onMenu} aria-label={t("nav.menu")}>
|
||||
<Icon name="menu" />
|
||||
</button>
|
||||
<div className={embed ? "files-embed-bar" : "topbar"}>
|
||||
{!embed && (
|
||||
<button className="ghost menu-btn" onClick={onMenu} aria-label={t("nav.menu")}>
|
||||
<Icon name="menu" />
|
||||
</button>
|
||||
)}
|
||||
<div className="grow">
|
||||
<h1>{t("nav.files")}</h1>
|
||||
{!embed && <h1>{t("nav.files")}</h1>}
|
||||
<div className="path">{listing?.path}</div>
|
||||
</div>
|
||||
{listing?.repo && (
|
||||
|
||||
Reference in new issue
Block a user