From c1fc5c7bca58f5112d7d210d4f6047f39053199b Mon Sep 17 00:00:00 2001 From: Jiayuan Date: Thu, 26 Mar 2026 20:57:14 +0800 Subject: [PATCH] chore(web): remove knowledge base module and fix worktree port conflicts Remove the frontend-only knowledge base page, sidebar nav entry, and unused BookOpen import. Fix setup-worktree to always generate unique ports via init-worktree-env.sh instead of skipping when .env.worktree already exists with default ports. Co-Authored-By: Claude Opus 4.6 --- Makefile | 6 +- .../(dashboard)/_components/app-sidebar.tsx | 2 - .../app/(dashboard)/knowledge-base/page.tsx | 360 ------------------ 3 files changed, 2 insertions(+), 366 deletions(-) delete mode 100644 apps/web/app/(dashboard)/knowledge-base/page.tsx diff --git a/Makefile b/Makefile index 2fb7fcc4..12964d71 100644 --- a/Makefile +++ b/Makefile @@ -95,10 +95,8 @@ check-main: @ENV_FILE=$(MAIN_ENV_FILE) bash scripts/check.sh setup-worktree: - @if [ ! -f "$(WORKTREE_ENV_FILE)" ]; then \ - echo "==> No $(WORKTREE_ENV_FILE) found, generating..."; \ - bash scripts/init-worktree-env.sh $(WORKTREE_ENV_FILE); \ - fi + @echo "==> Generating $(WORKTREE_ENV_FILE) with unique ports..." + @FORCE=1 bash scripts/init-worktree-env.sh $(WORKTREE_ENV_FILE) @$(MAKE) setup ENV_FILE=$(WORKTREE_ENV_FILE) start-worktree: diff --git a/apps/web/app/(dashboard)/_components/app-sidebar.tsx b/apps/web/app/(dashboard)/_components/app-sidebar.tsx index 4bdf6bc1..64f84698 100644 --- a/apps/web/app/(dashboard)/_components/app-sidebar.tsx +++ b/apps/web/app/(dashboard)/_components/app-sidebar.tsx @@ -7,7 +7,6 @@ import { ListTodo, Bot, Monitor, - BookOpen, ChevronDown, Settings, LogOut, @@ -53,7 +52,6 @@ const workspaceNav = [ { href: "/agents", label: "Agents", icon: Bot }, { href: "/runtimes", label: "Runtimes", icon: Monitor }, { href: "/skills", label: "Skills", icon: Sparkles }, - { href: "/knowledge-base", label: "Knowledge Base", icon: BookOpen }, { href: "/settings", label: "Settings", icon: Settings }, ]; diff --git a/apps/web/app/(dashboard)/knowledge-base/page.tsx b/apps/web/app/(dashboard)/knowledge-base/page.tsx deleted file mode 100644 index 740d37fe..00000000 --- a/apps/web/app/(dashboard)/knowledge-base/page.tsx +++ /dev/null @@ -1,360 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { useDefaultLayout } from "react-resizable-panels"; -import { - FileText, - Plus, - Search, - Link as LinkIcon, -} from "lucide-react"; -import { Input } from "@/components/ui/input"; -import { Button } from "@/components/ui/button"; -import { - ResizablePanelGroup, - ResizablePanel, - ResizableHandle, -} from "@/components/ui/resizable"; -// --------------------------------------------------------------------------- -// Helpers -// --------------------------------------------------------------------------- - -function timeAgo(dateStr: string): string { - const diff = Date.now() - new Date(dateStr).getTime(); - const hours = Math.floor(diff / 3600000); - if (hours < 24) return `${hours}h ago`; - const days = Math.floor(hours / 24); - return `${days}d ago`; -} - -interface KBDocument { - id: string; - title: string; - content: string; - createdBy: string; - updatedAt: string; - referencedBy: string[]; -} - -// --------------------------------------------------------------------------- -// Simple Markdown-ish renderer (handles headers, code blocks, tables, lists) -// --------------------------------------------------------------------------- - -function renderMarkdown(text: string): React.ReactNode[] { - const lines = text.split("\n"); - const elements: React.ReactNode[] = []; - let i = 0; - - while (i < lines.length) { - const line = lines[i]!; - - // Code block - if (line.startsWith("```")) { - const lang = line.slice(3).trim(); - const codeLines: string[] = []; - i++; - while (i < lines.length && !lines[i]!.startsWith("```")) { - codeLines.push(lines[i]!); - i++; - } - i++; // skip closing ``` - elements.push( -
-          {codeLines.join("\n")}
-        
- ); - continue; - } - - // Table (simplified: detect | pipes) - if (line.includes("|") && line.trim().startsWith("|")) { - const tableRows: string[] = []; - while (i < lines.length && lines[i]!.includes("|") && lines[i]!.trim().startsWith("|")) { - tableRows.push(lines[i]!); - i++; - } - // Filter out separator rows (|---|---|) - const dataRows = tableRows.filter((r) => !r.match(/^\|[\s-|]+\|$/)); - if (dataRows.length > 0) { - const parseRow = (row: string) => - row.split("|").filter((c) => c.trim() !== "").map((c) => c.trim()); - const header = parseRow(dataRows[0]!); - const body = dataRows.slice(1).map(parseRow); - elements.push( -
- - - - {header.map((h, hi) => ( - - ))} - - - - {body.map((row, ri) => ( - - {row.map((cell, ci) => ( - - ))} - - ))} - -
- {h} -
- {cell} -
-
- ); - } - continue; - } - - // Heading - if (line.startsWith("## ")) { - elements.push( -

- {line.slice(3)} -

, - ); - i++; - continue; - } - if (line.startsWith("### ")) { - elements.push( -

- {line.slice(4)} -

, - ); - i++; - continue; - } - - // List item - if (/^- \[[ x]\] /.test(line)) { - const checked = line.includes("[x]"); - const text = line.replace(/^- \[[ x]\] /, ""); - elements.push( -
- - {text} -
- ); - i++; - continue; - } - if (line.startsWith("- ")) { - elements.push( -
- - {renderInline(line.slice(2))} -
- ); - i++; - continue; - } - // Numbered list - if (/^\d+\. /.test(line)) { - const num = line.match(/^(\d+)\. /)![1]!; - const text = line.replace(/^\d+\. /, ""); - elements.push( -
- {num}. - {text} -
- ); - i++; - continue; - } - - // Empty line — guard is redundant since line is already asserted, but keeps TS happy - if (line.trim() === "") { - elements.push(
); - i++; - continue; - } - - // Paragraph - elements.push( -

- {renderInline(line)} -

- ); - i++; - } - - return elements; -} - -function renderInline(text: string): React.ReactNode { - // Handle inline code `...` - const parts = text.split(/(`[^`]+`)/); - return parts.map((part, i) => { - if (part.startsWith("`") && part.endsWith("`")) { - return ( - - {part.slice(1, -1)} - - ); - } - return part; - }); -} - -// --------------------------------------------------------------------------- -// Components -// --------------------------------------------------------------------------- - -function DocListItem({ - doc, - isSelected, - onClick, -}: { - doc: KBDocument; - isSelected: boolean; - onClick: () => void; -}) { - return ( - - ); -} - -function DocDetail({ doc }: { doc: KBDocument }) { - return ( -
-
- {/* Title */} -

{doc.title}

- - {/* Meta */} -
- By {doc.createdBy} - · - Updated {timeAgo(doc.updatedAt)} -
- - {/* Content */} -
{renderMarkdown(doc.content)}
- - {/* Referenced by */} - {doc.referencedBy.length > 0 && ( -
-
- - Referenced by -
-
- {doc.referencedBy.map((ref) => ( - - {ref} - - ))} -
-
- )} -
-
- ); -} - -// --------------------------------------------------------------------------- -// Page -// --------------------------------------------------------------------------- - -export default function KnowledgeBasePage() { - const [documents] = useState([]); - const [selectedId, setSelectedId] = useState(""); - const [search, setSearch] = useState(""); - const { defaultLayout, onLayoutChanged } = useDefaultLayout({ - id: "multica_kb_layout", - }); - - const filtered = search - ? documents.filter((d) => - d.title.toLowerCase().includes(search.toLowerCase()) - ) - : documents; - - const selected = documents.find((d) => d.id === selectedId) ?? null; - - return ( - - - {/* Left: Document list */} -
-
-

Knowledge Base

- -
- - {/* Search */} -
-
- - setSearch(e.target.value)} - className="border-0 bg-transparent shadow-none focus-visible:ring-0 flex-1 text-sm" - /> -
-
- - {/* Document list */} -
- {filtered.map((doc) => ( - setSelectedId(doc.id)} - /> - ))} - {filtered.length === 0 && ( -
- No documents found -
- )} -
-
-
- - - - - {/* Right: Document content */} - {selected ? ( - - ) : ( -
- Select a document -
- )} -
-
- ); -}