diff --git a/apps/web/app/api/workspace/tree/route.ts b/apps/web/app/api/workspace/tree/route.ts index 0d44792c2e5..a4a986d185f 100644 --- a/apps/web/app/api/workspace/tree/route.ts +++ b/apps/web/app/api/workspace/tree/route.ts @@ -177,16 +177,18 @@ async function buildTree( const isSymlink = entry.isSymbolicLink(); if (effectiveType === "directory") { - // Detect .dench.app folders -- treat as app nodes (no children exposed) + // Detect .dench.app folders -- treat as app nodes if (entry.name.endsWith(".dench.app")) { const manifest = await readAppManifest(absPath); const displayName = manifest?.name || entry.name.replace(/\.dench\.app$/, ""); + const children = showHidden ? await buildTree(absPath, relPath, dbObjects, showHidden) : undefined; nodes.push({ name: displayName, path: relPath, type: "app", icon: manifest?.icon, appManifest: manifest ?? { name: displayName, entry: "index.html", runtime: "static" }, + ...(children && children.length > 0 && { children }), ...(isSymlink && { symlink: true }), }); continue; diff --git a/apps/web/app/components/workspace/file-manager-tree.tsx b/apps/web/app/components/workspace/file-manager-tree.tsx index ba4a7a75bfc..7e0fc7e137e 100644 --- a/apps/web/app/components/workspace/file-manager-tree.tsx +++ b/apps/web/app/components/workspace/file-manager-tree.tsx @@ -470,7 +470,7 @@ function DraggableNode({ // Workspace root in browse mode: non-expandable entry point back to workspace const isWorkspaceRoot = !!workspaceRoot && node.path === workspaceRoot; const hasChildren = node.children && node.children.length > 0; - const isExpandable = isWorkspaceRoot ? false : node.type === "app" ? false : (hasChildren || node.type === "folder" || node.type === "object"); + const isExpandable = isWorkspaceRoot ? false : node.type === "app" ? !!hasChildren : (hasChildren || node.type === "folder" || node.type === "object"); const isExpanded = isWorkspaceRoot ? false : expandedPaths.has(node.path); const isActive = activePath === node.path; const isSelected = selectedPath === node.path; diff --git a/apps/web/app/workspace/workspace-content.tsx b/apps/web/app/workspace/workspace-content.tsx index 91831f19635..8aabdecad37 100644 --- a/apps/web/app/workspace/workspace-content.tsx +++ b/apps/web/app/workspace/workspace-content.tsx @@ -184,6 +184,17 @@ export type DenchAppManifest = { entry?: string; runtime?: "static" | "esbuild" | "build"; permissions?: string[]; + display?: "full" | "widget"; + widget?: { + width?: number; + height?: number; + refreshInterval?: number; + }; + tools?: Array<{ + name: string; + description: string; + inputSchema?: unknown; + }>; }; type SidebarPreviewContent = @@ -1063,6 +1074,7 @@ function WorkspacePageInner() { // Clicking the cron directory → show cron dashboard if (node.path === openclawDir + "/cron") { setBrowseDir(null); + openTabForNode({ path: "~cron", name: "Cron", type: "folder" }); setActivePath("~cron"); setContent({ kind: "cron-dashboard" }); return; @@ -1105,6 +1117,7 @@ function WorkspacePageInner() { const jobId = node.path.slice("~cron/".length); const job = cronJobs.find((j) => j.id === jobId); if (job) { + openTabForNode(node); setActivePath(node.path); setContent({ kind: "cron-job", jobId, job }); return; @@ -1112,6 +1125,7 @@ function WorkspacePageInner() { } // Clicking the Cron folder itself opens the dashboard if (node.path === "~cron") { + openTabForNode(node); setActivePath(node.path); setContent({ kind: "cron-dashboard" }); return;