refactor(web): show IDENTITY.md in workspace, filter managed skills, hide bootstrap toggle
IDENTITY.md is now user-editable (no longer a system file). Managed skill filter updated from dench to crm/browser. Bootstrap seed toggle hidden from create-workspace dialog.
This commit is contained in:
parent
1398e556a1
commit
70ca59a66d
@ -37,7 +37,6 @@ const SKIP_DIRS = new Set([
|
||||
function listDir(
|
||||
absDir: string,
|
||||
filter?: string,
|
||||
workspaceRoot?: string,
|
||||
): SuggestItem[] {
|
||||
let entries: Dirent[];
|
||||
try {
|
||||
@ -47,13 +46,8 @@ function listDir(
|
||||
}
|
||||
|
||||
const lowerFilter = filter?.toLowerCase();
|
||||
const hideRootIdentity =
|
||||
typeof workspaceRoot === "string" &&
|
||||
resolve(absDir) === resolve(workspaceRoot);
|
||||
|
||||
const sorted = entries
|
||||
.filter((e) => !e.name.startsWith("."))
|
||||
.filter((e) => !(hideRootIdentity && e.name === "IDENTITY.md"))
|
||||
.filter((e) => !(e.isDirectory() && SKIP_DIRS.has(e.name)))
|
||||
.filter((e) => !lowerFilter || e.name.toLowerCase().includes(lowerFilter))
|
||||
.toSorted((a, b) => {
|
||||
@ -90,7 +84,6 @@ function searchFiles(
|
||||
query: string,
|
||||
results: SuggestItem[],
|
||||
maxResults: number,
|
||||
workspaceRoot: string,
|
||||
depth = 0,
|
||||
): void {
|
||||
if (depth > 6 || results.length >= maxResults) {return;}
|
||||
@ -103,12 +96,9 @@ function searchFiles(
|
||||
}
|
||||
|
||||
const lowerQuery = query.toLowerCase();
|
||||
const hideRootIdentity = resolve(absDir) === resolve(workspaceRoot);
|
||||
|
||||
for (const entry of entries) {
|
||||
if (results.length >= maxResults) {return;}
|
||||
if (entry.name.startsWith(".")) {continue;}
|
||||
if (hideRootIdentity && entry.name === "IDENTITY.md") {continue;}
|
||||
if (entry.isDirectory() && SKIP_DIRS.has(entry.name)) {continue;}
|
||||
|
||||
const absPath = join(absDir, entry.name);
|
||||
@ -131,7 +121,7 @@ function searchFiles(
|
||||
}
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
searchFiles(absPath, query, results, maxResults, workspaceRoot, depth + 1);
|
||||
searchFiles(absPath, query, results, maxResults, depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -404,7 +394,7 @@ export async function GET(req: Request) {
|
||||
if (searchQuery) {
|
||||
// File search: workspace only (skip expensive home dir traversal)
|
||||
const fileResults: SuggestItem[] = [];
|
||||
searchFiles(workspaceRoot, searchQuery, fileResults, 15, workspaceRoot);
|
||||
searchFiles(workspaceRoot, searchQuery, fileResults, 15);
|
||||
|
||||
// DuckDB search: objects and entries (sequential to avoid lock contention)
|
||||
const objectResults = await searchObjects(searchQuery, workspaceRoot, 10);
|
||||
@ -426,15 +416,15 @@ export async function GET(req: Request) {
|
||||
const resolved = resolvePath(pathQuery, workspaceRoot);
|
||||
if (!resolved) {
|
||||
const results: SuggestItem[] = [];
|
||||
searchFiles(workspaceRoot, pathQuery, results, 20, workspaceRoot);
|
||||
searchFiles(workspaceRoot, pathQuery, results, 20);
|
||||
return Response.json({ items: results });
|
||||
}
|
||||
const items = listDir(resolved.dir, resolved.filter, workspaceRoot);
|
||||
const items = listDir(resolved.dir, resolved.filter);
|
||||
return Response.json({ items });
|
||||
}
|
||||
|
||||
// Default: list workspace root + all objects
|
||||
const fileItems = listDir(workspaceRoot, undefined, workspaceRoot);
|
||||
const fileItems = listDir(workspaceRoot);
|
||||
const objectItems = await searchObjects("", workspaceRoot, 20);
|
||||
// Deduplicate: if an object also appears as a folder, keep the object version
|
||||
const objectNames = new Set(objectItems.map((o) => o.name));
|
||||
|
||||
@ -115,9 +115,6 @@ function buildTree(
|
||||
for (const entry of sorted) {
|
||||
// .object.yaml is consumed for metadata; only show it as a visible node when revealing hidden files
|
||||
if (entry.name === ".object.yaml" && !showHidden) {continue;}
|
||||
// Keep the root identity system file out of the workspace sidebar tree.
|
||||
if (!relativeBase && entry.name === "IDENTITY.md") {continue;}
|
||||
|
||||
const absPath = join(absDir, entry.name);
|
||||
const relPath = relativeBase
|
||||
? `${relativeBase}/${entry.name}`
|
||||
@ -203,7 +200,7 @@ function buildSkillsVirtualFolder(): TreeNode | null {
|
||||
const entries = readdirSync(dir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (!entry.isDirectory() || seen.has(entry.name)) {continue;}
|
||||
if (entry.name === "dench") {continue;}
|
||||
if (entry.name === "crm" || entry.name === "browser") {continue;}
|
||||
const skillMdPath = join(dir, entry.name, "SKILL.md");
|
||||
if (!existsSync(skillMdPath)) {continue;}
|
||||
|
||||
|
||||
@ -204,7 +204,7 @@ export function CreateWorkspaceDialog({ isOpen, onClose, onCreated }: CreateWork
|
||||
</div>
|
||||
|
||||
{/* Bootstrap toggle */}
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<label className="flex items-center gap-2 cursor-pointer hidden">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={seedBootstrap}
|
||||
|
||||
@ -71,7 +71,6 @@ const ALWAYS_SYSTEM_PATTERNS = [
|
||||
const ROOT_ONLY_SYSTEM_PATTERNS = [
|
||||
/^workspace\.duckdb/,
|
||||
/^workspace_context\.yaml$/,
|
||||
/^IDENTITY\.md$/,
|
||||
];
|
||||
|
||||
function isSystemFile(path: string): boolean {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState, useRef, useCallback, useMemo } from "react";
|
||||
import { useEffect, useState, useRef, useCallback } from "react";
|
||||
import { FileManagerTree, type TreeNode } from "./file-manager-tree";
|
||||
import { ProfileSwitcher } from "./profile-switcher";
|
||||
import { CreateWorkspaceDialog } from "./create-workspace-dialog";
|
||||
@ -389,27 +389,6 @@ function dirDisplayName(dir: string): string {
|
||||
return dir.split("/").pop() || dir;
|
||||
}
|
||||
|
||||
function filterSidebarTree(nodes: TreeNode[]): TreeNode[] {
|
||||
const filtered: TreeNode[] = [];
|
||||
for (const node of nodes) {
|
||||
// Root identity file is system-managed and hidden in Ironclaw UI.
|
||||
if (node.path === "IDENTITY.md") {
|
||||
continue;
|
||||
}
|
||||
// Dench is an always-on managed skill; hide it from the sidebar list.
|
||||
if (node.path === "~skills/dench/SKILL.md") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const children = node.children ? filterSidebarTree(node.children) : undefined;
|
||||
if (node.path === "~skills" && (!children || children.length === 0)) {
|
||||
continue;
|
||||
}
|
||||
filtered.push(children ? { ...node, children } : node);
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
|
||||
export function WorkspaceSidebar({
|
||||
tree,
|
||||
activePath,
|
||||
@ -436,7 +415,6 @@ export function WorkspaceSidebar({
|
||||
}: WorkspaceSidebarProps) {
|
||||
const isBrowsing = browseDir != null;
|
||||
const width = mobile ? "280px" : (widthProp ?? 260);
|
||||
const visibleTree = useMemo(() => filterSidebarTree(tree), [tree]);
|
||||
const [createWorkspaceOpen, setCreateWorkspaceOpen] = useState(false);
|
||||
|
||||
const sidebar = (
|
||||
@ -594,7 +572,7 @@ export function WorkspaceSidebar({
|
||||
</div>
|
||||
) : (
|
||||
<FileManagerTree
|
||||
tree={visibleTree}
|
||||
tree={tree}
|
||||
activePath={activePath}
|
||||
onSelect={onSelect}
|
||||
onRefresh={onRefresh}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user