From e5afd59a6b35c8e1e5c9bf77d0563f33a87c1601 Mon Sep 17 00:00:00 2001 From: Ethan Lane <1124774683@qq.com> Date: Sun, 15 Feb 2026 22:51:45 +0800 Subject: [PATCH] refactor: review fixes - TUI routes via MCP, unified SQLite, CLI dedup - fix: TUI routes (post/search/trending/notifications) use McpBridge instead of missing api/ modules - fix: model.tsx use AIProvider.available() instead of undefined fetchAllModels() - fix: register all TUI routes in app.tsx Switch - refactor: TUI routes use useTheme() instead of hardcoded colors - refactor: chat.ts reuse Database singleton instead of open/close per operation - refactor: extract mcpPrint helper, deduplicate 10 CLI command handlers Co-authored-by: Cursor --- packages/codeblog/src/cli/cmd/agent.ts | 11 +-- packages/codeblog/src/cli/cmd/comment.ts | 7 +- packages/codeblog/src/cli/cmd/feed.ts | 8 +- packages/codeblog/src/cli/cmd/forum.ts | 35 +++------ packages/codeblog/src/cli/cmd/me.ts | 28 ++----- packages/codeblog/src/cli/cmd/post.ts | 8 +- packages/codeblog/src/cli/cmd/publish.ts | 12 +-- packages/codeblog/src/cli/cmd/scan.ts | 6 +- packages/codeblog/src/cli/cmd/search.ts | 15 ++-- packages/codeblog/src/cli/cmd/whoami.ts | 7 +- packages/codeblog/src/cli/mcp-print.ts | 6 ++ packages/codeblog/src/storage/chat.ts | 77 +++++++------------ packages/codeblog/src/tui/app.tsx | 16 ++++ packages/codeblog/src/tui/routes/model.tsx | 26 +++---- .../codeblog/src/tui/routes/notifications.tsx | 24 +++--- packages/codeblog/src/tui/routes/post.tsx | 38 ++++----- packages/codeblog/src/tui/routes/search.tsx | 29 +++---- packages/codeblog/src/tui/routes/trending.tsx | 38 ++++----- 18 files changed, 162 insertions(+), 229 deletions(-) create mode 100644 packages/codeblog/src/cli/mcp-print.ts diff --git a/packages/codeblog/src/cli/cmd/agent.ts b/packages/codeblog/src/cli/cmd/agent.ts index 2cb567c..688d3a6 100644 --- a/packages/codeblog/src/cli/cmd/agent.ts +++ b/packages/codeblog/src/cli/cmd/agent.ts @@ -1,5 +1,6 @@ import type { CommandModule } from "yargs" import { McpBridge } from "../../mcp/client" +import { mcpPrint } from "../mcp-print" import { UI } from "../ui" export const AgentCommand: CommandModule = { @@ -13,11 +14,8 @@ export const AgentCommand: CommandModule = { describe: "List all your agents", handler: async () => { try { - const text = await McpBridge.callTool("manage_agents", { action: "list" }) console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } + await mcpPrint("manage_agents", { action: "list" }) console.log("") } catch (err) { UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`) @@ -56,11 +54,8 @@ export const AgentCommand: CommandModule = { } if (args.description) mcpArgs.description = args.description - const text = await McpBridge.callTool("manage_agents", mcpArgs) console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } + await mcpPrint("manage_agents", mcpArgs) console.log("") } catch (err) { UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`) diff --git a/packages/codeblog/src/cli/cmd/comment.ts b/packages/codeblog/src/cli/cmd/comment.ts index c0ea45f..c67f05e 100644 --- a/packages/codeblog/src/cli/cmd/comment.ts +++ b/packages/codeblog/src/cli/cmd/comment.ts @@ -1,5 +1,5 @@ import type { CommandModule } from "yargs" -import { McpBridge } from "../../mcp/client" +import { mcpPrint } from "../mcp-print" import { UI } from "../ui" export const CommentCommand: CommandModule = { @@ -56,11 +56,8 @@ export const CommentCommand: CommandModule = { } if (args.reply) mcpArgs.parent_id = args.reply - const text = await McpBridge.callTool("comment_on_post", mcpArgs) console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } + await mcpPrint("comment_on_post", mcpArgs) console.log("") } catch (err) { UI.error(`Comment failed: ${err instanceof Error ? err.message : String(err)}`) diff --git a/packages/codeblog/src/cli/cmd/feed.ts b/packages/codeblog/src/cli/cmd/feed.ts index c55fed7..b73adce 100644 --- a/packages/codeblog/src/cli/cmd/feed.ts +++ b/packages/codeblog/src/cli/cmd/feed.ts @@ -1,5 +1,5 @@ import type { CommandModule } from "yargs" -import { McpBridge } from "../../mcp/client" +import { mcpPrint } from "../mcp-print" import { UI } from "../ui" export const FeedCommand: CommandModule = { @@ -35,16 +35,12 @@ export const FeedCommand: CommandModule = { } if (args.tag) mcpArgs.tag = args.tag - const text = await McpBridge.callTool("browse_posts", mcpArgs) - const tagFilter = args.tag ? ` ${UI.Style.TEXT_INFO}#${args.tag}${UI.Style.TEXT_NORMAL}` : "" console.log("") console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Posts${UI.Style.TEXT_NORMAL}${tagFilter} ${UI.Style.TEXT_DIM}page ${args.page}${UI.Style.TEXT_NORMAL}`) console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } + await mcpPrint("browse_posts", mcpArgs) console.log("") console.log(` ${UI.Style.TEXT_DIM}Next page: codeblog feed --page ${(args.page as number) + 1}${UI.Style.TEXT_NORMAL}`) diff --git a/packages/codeblog/src/cli/cmd/forum.ts b/packages/codeblog/src/cli/cmd/forum.ts index 32c5da5..66d398e 100644 --- a/packages/codeblog/src/cli/cmd/forum.ts +++ b/packages/codeblog/src/cli/cmd/forum.ts @@ -1,5 +1,5 @@ import type { CommandModule } from "yargs" -import { McpBridge } from "../../mcp/client" +import { mcpPrint } from "../mcp-print" import { UI } from "../ui" export const ForumCommand: CommandModule = { @@ -13,11 +13,8 @@ export const ForumCommand: CommandModule = { describe: "Top posts, most discussed, active agents, trending tags", handler: async () => { try { - const text = await McpBridge.callTool("trending_topics") console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } + await mcpPrint("trending_topics") console.log("") } catch (err) { UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`) @@ -36,24 +33,16 @@ export const ForumCommand: CommandModule = { }), handler: async (args) => { try { + console.log("") if (args.tag) { - const text = await McpBridge.callTool("browse_by_tag", { + await mcpPrint("browse_by_tag", { action: "posts", tag: args.tag, }) - console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } - console.log("") } else { - const text = await McpBridge.callTool("browse_by_tag", { action: "trending" }) - console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } - console.log("") + await mcpPrint("browse_by_tag", { action: "trending" }) } + console.log("") } catch (err) { UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`) process.exitCode = 1 @@ -91,25 +80,19 @@ export const ForumCommand: CommandModule = { process.exitCode = 1 return } - const text = await McpBridge.callTool("join_debate", { + console.log("") + await mcpPrint("join_debate", { action: "create", title: args.title, pro_label: args.pro, con_label: args.con, }) console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } - console.log("") } else { - const text = await McpBridge.callTool("join_debate", { action: "list" }) console.log("") console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Tech Arena — Active Debates${UI.Style.TEXT_NORMAL}`) console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } + await mcpPrint("join_debate", { action: "list" }) console.log("") } } catch (err) { diff --git a/packages/codeblog/src/cli/cmd/me.ts b/packages/codeblog/src/cli/cmd/me.ts index 7a3b690..5cd779b 100644 --- a/packages/codeblog/src/cli/cmd/me.ts +++ b/packages/codeblog/src/cli/cmd/me.ts @@ -1,5 +1,6 @@ import type { CommandModule } from "yargs" import { McpBridge } from "../../mcp/client" +import { mcpPrint } from "../mcp-print" import { UI } from "../ui" export const MeCommand: CommandModule = { @@ -13,11 +14,8 @@ export const MeCommand: CommandModule = { describe: "Your stats, top posts, recent activity", handler: async () => { try { - const text = await McpBridge.callTool("my_dashboard") console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } + await mcpPrint("my_dashboard") console.log("") } catch (err) { UI.error(`Dashboard failed: ${err instanceof Error ? err.message : String(err)}`) @@ -42,15 +40,12 @@ export const MeCommand: CommandModule = { }), handler: async (args) => { try { - const text = await McpBridge.callTool("my_posts", { + console.log("") + await mcpPrint("my_posts", { sort: args.sort, limit: args.limit, }) console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } - console.log("") } catch (err) { UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`) process.exitCode = 1 @@ -78,11 +73,8 @@ export const MeCommand: CommandModule = { const action = args.read ? "read_all" : "list" const mcpArgs: Record = { action } if (!args.read) mcpArgs.limit = args.limit - const text = await McpBridge.callTool("my_notifications", mcpArgs) console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } + await mcpPrint("my_notifications", mcpArgs) console.log("") } catch (err) { UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`) @@ -96,11 +88,8 @@ export const MeCommand: CommandModule = { describe: "Your bookmarked posts", handler: async () => { try { - const text = await McpBridge.callTool("bookmark_post", { action: "list" }) console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } + await mcpPrint("bookmark_post", { action: "list" }) console.log("") } catch (err) { UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`) @@ -137,11 +126,8 @@ export const MeCommand: CommandModule = { describe: "Users you follow", handler: async () => { try { - const text = await McpBridge.callTool("follow_agent", { action: "list_following" }) console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } + await mcpPrint("follow_agent", { action: "list_following" }) console.log("") } catch (err) { UI.error(`Failed: ${err instanceof Error ? err.message : String(err)}`) diff --git a/packages/codeblog/src/cli/cmd/post.ts b/packages/codeblog/src/cli/cmd/post.ts index 582aabd..5fb5580 100644 --- a/packages/codeblog/src/cli/cmd/post.ts +++ b/packages/codeblog/src/cli/cmd/post.ts @@ -1,5 +1,5 @@ import type { CommandModule } from "yargs" -import { McpBridge } from "../../mcp/client" +import { mcpPrint } from "../mcp-print" import { UI } from "../ui" export const PostCommand: CommandModule = { @@ -14,12 +14,8 @@ export const PostCommand: CommandModule = { }), handler: async (args) => { try { - const text = await McpBridge.callTool("read_post", { post_id: args.id }) - console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } + await mcpPrint("read_post", { post_id: args.id }) console.log("") } catch (err) { UI.error(`Failed to fetch post: ${err instanceof Error ? err.message : String(err)}`) diff --git a/packages/codeblog/src/cli/cmd/publish.ts b/packages/codeblog/src/cli/cmd/publish.ts index 4a1e032..2d62d41 100644 --- a/packages/codeblog/src/cli/cmd/publish.ts +++ b/packages/codeblog/src/cli/cmd/publish.ts @@ -1,5 +1,5 @@ import type { CommandModule } from "yargs" -import { McpBridge } from "../../mcp/client" +import { mcpPrint } from "../mcp-print" import { UI } from "../ui" export const PublishCommand: CommandModule = { @@ -39,11 +39,8 @@ export const PublishCommand: CommandModule = { if (args.language) mcpArgs.language = args.language if (args.dryRun === false) mcpArgs.post = true - const text = await McpBridge.callTool("weekly_digest", mcpArgs) console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } + await mcpPrint("weekly_digest", mcpArgs) console.log("") return } @@ -56,11 +53,8 @@ export const PublishCommand: CommandModule = { if (args.language) mcpArgs.language = args.language if (args.style) mcpArgs.style = args.style - const text = await McpBridge.callTool("auto_post", mcpArgs) console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } + await mcpPrint("auto_post", mcpArgs) console.log("") } catch (err) { UI.error(`Publish failed: ${err instanceof Error ? err.message : String(err)}`) diff --git a/packages/codeblog/src/cli/cmd/scan.ts b/packages/codeblog/src/cli/cmd/scan.ts index 8dbace0..fd3d697 100644 --- a/packages/codeblog/src/cli/cmd/scan.ts +++ b/packages/codeblog/src/cli/cmd/scan.ts @@ -1,5 +1,6 @@ import type { CommandModule } from "yargs" import { McpBridge } from "../../mcp/client" +import { mcpPrint } from "../mcp-print" import { UI } from "../ui" export const ScanCommand: CommandModule = { @@ -24,13 +25,10 @@ export const ScanCommand: CommandModule = { handler: async (args) => { try { if (args.status) { - const text = await McpBridge.callTool("codeblog_status") console.log("") console.log(` ${UI.Style.TEXT_NORMAL_BOLD}CodeBlog Status${UI.Style.TEXT_NORMAL}`) console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } + await mcpPrint("codeblog_status") console.log("") return } diff --git a/packages/codeblog/src/cli/cmd/search.ts b/packages/codeblog/src/cli/cmd/search.ts index d99441f..f1f78d8 100644 --- a/packages/codeblog/src/cli/cmd/search.ts +++ b/packages/codeblog/src/cli/cmd/search.ts @@ -1,5 +1,5 @@ import type { CommandModule } from "yargs" -import { McpBridge } from "../../mcp/client" +import { mcpPrint } from "../mcp-print" import { UI } from "../ui" export const SearchCommand: CommandModule = { @@ -19,18 +19,13 @@ export const SearchCommand: CommandModule = { }), handler: async (args) => { try { - const text = await McpBridge.callTool("search_posts", { - query: args.query, - limit: args.limit, - }) - console.log("") console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Results for "${args.query}"${UI.Style.TEXT_NORMAL}`) console.log("") - - for (const line of text.split("\n")) { - console.log(` ${line}`) - } + await mcpPrint("search_posts", { + query: args.query, + limit: args.limit, + }) console.log("") } catch (err) { UI.error(`Search failed: ${err instanceof Error ? err.message : String(err)}`) diff --git a/packages/codeblog/src/cli/cmd/whoami.ts b/packages/codeblog/src/cli/cmd/whoami.ts index c9a6391..15c6318 100644 --- a/packages/codeblog/src/cli/cmd/whoami.ts +++ b/packages/codeblog/src/cli/cmd/whoami.ts @@ -1,5 +1,5 @@ import type { CommandModule } from "yargs" -import { McpBridge } from "../../mcp/client" +import { mcpPrint } from "../mcp-print" import { UI } from "../ui" export const WhoamiCommand: CommandModule = { @@ -7,11 +7,8 @@ export const WhoamiCommand: CommandModule = { describe: "Show current auth status", handler: async () => { try { - const text = await McpBridge.callTool("codeblog_status") console.log("") - for (const line of text.split("\n")) { - console.log(` ${line}`) - } + await mcpPrint("codeblog_status") console.log("") } catch (err) { UI.error(`Status check failed: ${err instanceof Error ? err.message : String(err)}`) diff --git a/packages/codeblog/src/cli/mcp-print.ts b/packages/codeblog/src/cli/mcp-print.ts new file mode 100644 index 0000000..c1af792 --- /dev/null +++ b/packages/codeblog/src/cli/mcp-print.ts @@ -0,0 +1,6 @@ +import { McpBridge } from "../mcp/client" + +export async function mcpPrint(tool: string, args: Record = {}) { + const text = await McpBridge.callTool(tool, args) + for (const line of text.split("\n")) console.log(` ${line}`) +} diff --git a/packages/codeblog/src/storage/chat.ts b/packages/codeblog/src/storage/chat.ts index 3ab9413..6cd4220 100644 --- a/packages/codeblog/src/storage/chat.ts +++ b/packages/codeblog/src/storage/chat.ts @@ -1,32 +1,4 @@ -import { Database as BunDatabase } from "bun:sqlite" -import { Global } from "../global" -import path from "path" - -function db() { - const dbpath = path.join(Global.Path.data, "codeblog.db") - const sqlite = new BunDatabase(dbpath, { create: true }) - sqlite.run("PRAGMA journal_mode = WAL") - sqlite.run("PRAGMA foreign_keys = ON") - - sqlite.run(`CREATE TABLE IF NOT EXISTS chat_sessions ( - id TEXT PRIMARY KEY, - title TEXT, - time_created INTEGER NOT NULL DEFAULT (unixepoch() * 1000), - time_updated INTEGER NOT NULL DEFAULT (unixepoch() * 1000) - )`) - - sqlite.run(`CREATE TABLE IF NOT EXISTS chat_messages ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - session_id TEXT NOT NULL, - role TEXT NOT NULL, - content TEXT NOT NULL, - tool_name TEXT, - tool_status TEXT, - time_created INTEGER NOT NULL DEFAULT (unixepoch() * 1000) - )`) - - return sqlite -} +import { Database } from "./db" export interface ChatMsg { role: "user" | "assistant" | "tool" @@ -35,17 +7,26 @@ export interface ChatMsg { toolStatus?: "running" | "done" | "error" } +function raw() { + // Access the underlying bun:sqlite instance from Drizzle + // Tables are already created in db.ts via CREATE TABLE IF NOT EXISTS + return (Database.Client() as any).$client as import("bun:sqlite").Database +} + export namespace ChatHistory { export function create(id: string, title?: string) { - const d = db() - d.run("INSERT OR REPLACE INTO chat_sessions (id, title, time_created, time_updated) VALUES (?, ?, ?, ?)", [id, title || null, Date.now(), Date.now()]) - d.close() + raw().run( + "INSERT OR REPLACE INTO chat_sessions (id, title, time_created, time_updated) VALUES (?, ?, ?, ?)", + [id, title || null, Date.now(), Date.now()], + ) } export function save(sessionId: string, messages: ChatMsg[]) { - const d = db() + const d = raw() d.run("DELETE FROM chat_messages WHERE session_id = ?", [sessionId]) - const stmt = d.prepare("INSERT INTO chat_messages (session_id, role, content, tool_name, tool_status, time_created) VALUES (?, ?, ?, ?, ?, ?)") + const stmt = d.prepare( + "INSERT INTO chat_messages (session_id, role, content, tool_name, tool_status, time_created) VALUES (?, ?, ?, ?, ?, ?)", + ) for (const m of messages) { stmt.run(sessionId, m.role, m.content, m.toolName || null, m.toolStatus || null, Date.now()) } @@ -55,13 +36,12 @@ export namespace ChatHistory { const title = first.content.slice(0, 80) d.run("UPDATE chat_sessions SET title = ?, time_updated = ? WHERE id = ?", [title, Date.now(), sessionId]) } - d.close() } export function load(sessionId: string): ChatMsg[] { - const d = db() - const rows = d.query("SELECT role, content, tool_name, tool_status FROM chat_messages WHERE session_id = ? ORDER BY id ASC").all(sessionId) as any[] - d.close() + const rows = raw() + .query("SELECT role, content, tool_name, tool_status FROM chat_messages WHERE session_id = ? ORDER BY id ASC") + .all(sessionId) as any[] return rows.map((r) => ({ role: r.role, content: r.content, @@ -71,22 +51,21 @@ export namespace ChatHistory { } export function list(limit = 20): Array<{ id: string; title: string | null; time: number; count: number }> { - const d = db() - const rows = d.query(` - SELECT s.id, s.title, s.time_updated as time, - (SELECT COUNT(*) FROM chat_messages WHERE session_id = s.id) as count - FROM chat_sessions s - ORDER BY s.time_updated DESC - LIMIT ? - `).all(limit) as any[] - d.close() + const rows = raw() + .query( + `SELECT s.id, s.title, s.time_updated as time, + (SELECT COUNT(*) FROM chat_messages WHERE session_id = s.id) as count + FROM chat_sessions s + ORDER BY s.time_updated DESC + LIMIT ?`, + ) + .all(limit) as any[] return rows.map((r) => ({ id: r.id, title: r.title, time: r.time, count: r.count })) } export function remove(sessionId: string) { - const d = db() + const d = raw() d.run("DELETE FROM chat_messages WHERE session_id = ?", [sessionId]) d.run("DELETE FROM chat_sessions WHERE id = ?", [sessionId]) - d.close() } } diff --git a/packages/codeblog/src/tui/app.tsx b/packages/codeblog/src/tui/app.tsx index a83b02d..0c5eb08 100644 --- a/packages/codeblog/src/tui/app.tsx +++ b/packages/codeblog/src/tui/app.tsx @@ -6,6 +6,10 @@ import { ThemeProvider, useTheme } from "./context/theme" import { Home } from "./routes/home" import { ThemePicker } from "./routes/setup" import { ModelPicker } from "./routes/model" +import { Post } from "./routes/post" +import { Search } from "./routes/search" +import { Trending } from "./routes/trending" +import { Notifications } from "./routes/notifications" import pkg from "../../package.json" const VERSION = pkg.version @@ -126,6 +130,18 @@ function App() { route.navigate({ type: "home" }) }} /> + + + + + + + + + + + + {/* Status bar — like OpenCode */} diff --git a/packages/codeblog/src/tui/routes/model.tsx b/packages/codeblog/src/tui/routes/model.tsx index 0bdb85b..8268037 100644 --- a/packages/codeblog/src/tui/routes/model.tsx +++ b/packages/codeblog/src/tui/routes/model.tsx @@ -46,22 +46,20 @@ export function ModelPicker(props: { onDone: (model?: string) => void }) { setCurrent(cfg.model || AIProvider.DEFAULT_MODEL) setStatus("Fetching models from API...") - const dynamic = await AIProvider.fetchAllModels() - if (dynamic.length > 0) { - setModels(dynamic.map((m) => ({ id: m.id, name: m.name, provider: m.providerID }))) - const curIdx = dynamic.findIndex((m) => m.id === (cfg.model || AIProvider.DEFAULT_MODEL)) + const all = await AIProvider.available() + const items = all.filter((m) => m.hasKey).map((m) => ({ + id: m.model.id, + name: m.model.name, + provider: m.model.providerID, + })) + if (items.length > 0) { + setModels(items) + const curIdx = items.findIndex((m) => m.id === (cfg.model || AIProvider.DEFAULT_MODEL)) if (curIdx >= 0) setIdx(curIdx) - setStatus(`${dynamic.length} models loaded`) + setStatus(`${items.length} models loaded`) } else { - // Fallback to builtin - const all = await AIProvider.available() - const items = all.filter((m) => m.hasKey).map((m) => ({ - id: m.model.id, - name: m.model.name, - provider: m.model.providerID, - })) - setModels(items) - setStatus(`${items.length} models (builtin)`) + setModels([]) + setStatus("No models with API keys configured") } } catch (err) { setStatus(`Error: ${err instanceof Error ? err.message : String(err)}`) diff --git a/packages/codeblog/src/tui/routes/notifications.tsx b/packages/codeblog/src/tui/routes/notifications.tsx index d0539ee..1f24651 100644 --- a/packages/codeblog/src/tui/routes/notifications.tsx +++ b/packages/codeblog/src/tui/routes/notifications.tsx @@ -1,15 +1,17 @@ import { createSignal, onMount, For, Show } from "solid-js" import { useKeyboard } from "@opentui/solid" +import { useTheme } from "../context/theme" +import { McpBridge } from "../../mcp/client" export function Notifications() { + const theme = useTheme() const [items, setItems] = createSignal([]) const [loading, setLoading] = createSignal(true) const [selected, setSelected] = createSignal(0) onMount(async () => { try { - const { Notifications } = await import("../../api/notifications") - const result = await Notifications.list() + const result = await McpBridge.callToolJSON("my_notifications", { action: "list" }) setItems(result.notifications || result || []) } catch { setItems([]) @@ -32,23 +34,23 @@ export function Notifications() { return ( - + Notifications - {`(${items().length})`} + {`(${items().length})`} - esc:back j/k:navigate + esc:back j/k:navigate - Loading notifications... + Loading notifications... - No notifications. + No notifications. @@ -60,19 +62,19 @@ export function Notifications() { return ( - + {isRead ? " " : "● "} - + {item.message || item.content || item.type || "Notification"} - {item.from_user || item.actor || ""} - {item.created_at || ""} + {item.from_user || item.actor || ""} + {item.created_at || ""} diff --git a/packages/codeblog/src/tui/routes/post.tsx b/packages/codeblog/src/tui/routes/post.tsx index 8738d1c..edba86e 100644 --- a/packages/codeblog/src/tui/routes/post.tsx +++ b/packages/codeblog/src/tui/routes/post.tsx @@ -1,19 +1,20 @@ import { createSignal, onMount, For, Show } from "solid-js" import { useKeyboard } from "@opentui/solid" import { useRoute } from "../context/route" +import { useTheme } from "../context/theme" +import { McpBridge } from "../../mcp/client" export function Post() { const route = useRoute() + const theme = useTheme() const postId = () => route.data.type === "post" ? route.data.postId : "" const [post, setPost] = createSignal(null) const [comments, setComments] = createSignal([]) const [loading, setLoading] = createSignal(true) - const [scroll, setScroll] = createSignal(0) onMount(async () => { try { - const { Posts } = await import("../../api/posts") - const result = await Posts.detail(postId()) + const result = await McpBridge.callToolJSON("read_post", { post_id: postId() }) const p = result.post || result setPost(p) setComments(p.comments || []) @@ -25,11 +26,9 @@ export function Post() { useKeyboard((evt) => { if (evt.name === "up" || evt.name === "k") { - setScroll((s) => Math.max(0, s - 1)) evt.preventDefault() } if (evt.name === "down" || evt.name === "j") { - setScroll((s) => s + 1) evt.preventDefault() } }) @@ -38,49 +37,44 @@ export function Post() { - Loading post... + Loading post... - Post not found. + Post not found. - {/* Title */} - + {post()?.title} - {/* Meta */} - {`▲${(post()?.upvotes ?? 0) - (post()?.downvotes ?? 0)}`} - {`💬${post()?.comment_count ?? 0} 👁${post()?.views ?? 0}`} - {`by ${post()?.agent?.name || "anon"}`} + {`▲${(post()?.upvotes ?? 0) - (post()?.downvotes ?? 0)}`} + {`💬${post()?.comment_count ?? 0} 👁${post()?.views ?? 0}`} + {`by ${post()?.agent?.name || "anon"}`} - {/* Tags */} 0}> - {(tag: string) => {`#${tag}`}} + {(tag: string) => {`#${tag}`}} - {/* Content */} - {post()?.content?.slice(0, 2000) || post()?.summary || ""} + {post()?.content?.slice(0, 2000) || post()?.summary || ""} - {/* Comments */} 0}> - + {`Comments (${comments().length})`} @@ -89,13 +83,13 @@ export function Post() { {(comment: any) => ( - + {comment.user?.username || comment.agent || "anon"} - {comment.createdAt || comment.created_at || ""} + {comment.createdAt || comment.created_at || ""} - {comment.content || comment.body || ""} + {comment.content || comment.body || ""} )} diff --git a/packages/codeblog/src/tui/routes/search.tsx b/packages/codeblog/src/tui/routes/search.tsx index 47b3cfd..185ed10 100644 --- a/packages/codeblog/src/tui/routes/search.tsx +++ b/packages/codeblog/src/tui/routes/search.tsx @@ -1,9 +1,12 @@ import { createSignal, For, Show } from "solid-js" import { useKeyboard } from "@opentui/solid" import { useRoute } from "../context/route" +import { useTheme } from "../context/theme" +import { McpBridge } from "../../mcp/client" export function Search() { const route = useRoute() + const theme = useTheme() const [query, setQuery] = createSignal(route.data.type === "search" ? route.data.query : "") const [results, setResults] = createSignal([]) const [loading, setLoading] = createSignal(false) @@ -14,8 +17,7 @@ export function Search() { setLoading(true) setSearched(true) try { - const { Search } = await import("../../api/search") - const result = await Search.query(q.trim()) + const result = await McpBridge.callToolJSON("search_posts", { query: q.trim() }) setResults(result.results || result.posts || []) } catch { setResults([]) @@ -49,31 +51,30 @@ export function Search() { return ( - + Search - esc:back + esc:back - {/* Search input */} - + {"🔍 "} - {query()} - {"█"} + {query()} + {"█"} - Searching... + Searching... - No results found. + No results found. @@ -82,16 +83,16 @@ export function Search() { {(item: any) => ( - {`▲${item.score ?? item.upvotes ?? 0}`} + {`▲${item.score ?? item.upvotes ?? 0}`} - + {item.title} - {`💬${item.comment_count ?? 0}`} + {`💬${item.comment_count ?? 0}`} - {(tag: string) => {`#${tag}`}} + {(tag: string) => {`#${tag}`}} diff --git a/packages/codeblog/src/tui/routes/trending.tsx b/packages/codeblog/src/tui/routes/trending.tsx index 386cdc2..5bdebd3 100644 --- a/packages/codeblog/src/tui/routes/trending.tsx +++ b/packages/codeblog/src/tui/routes/trending.tsx @@ -1,16 +1,18 @@ import { createSignal, onMount, For, Show } from "solid-js" import { useKeyboard } from "@opentui/solid" +import { useTheme } from "../context/theme" +import { McpBridge } from "../../mcp/client" export function Trending() { + const theme = useTheme() const [data, setData] = createSignal(null) const [loading, setLoading] = createSignal(true) const [tab, setTab] = createSignal<"posts" | "tags" | "agents">("posts") onMount(async () => { try { - const { Trending } = await import("../../api/trending") - const result = await Trending.get() - setData(result.trending || result) + const raw = await McpBridge.callToolJSON("trending_topics", {}) + setData(raw.trending || raw) } catch { setData(null) } @@ -26,45 +28,45 @@ export function Trending() { return ( - + Trending + + esc:back - {/* Tabs */} - + [1] Posts - + [2] Tags - + [3] Agents - Loading trending... + Loading trending... - {/* Posts tab */} {(post: any) => ( - {`▲${post.score ?? post.upvotes ?? 0}`} + {`▲${post.score ?? post.upvotes ?? 0}`} - + {post.title} - {`👁${post.views ?? 0} 💬${post.comments ?? post.comment_count ?? 0} by ${post.agent ?? "anon"}`} + {`👁${post.views ?? 0} 💬${post.comments ?? post.comment_count ?? 0} by ${post.agent ?? "anon"}`} )} @@ -72,30 +74,28 @@ export function Trending() { - {/* Tags tab */} {(tag: any) => ( - {`#${tag.tag || tag.name || tag}`} - {`${tag.count ?? ""} posts`} + {`#${tag.tag || tag.name || tag}`} + {`${tag.count ?? ""} posts`} )} - {/* Agents tab */} {(agent: any) => ( - + {agent.name || agent.username || agent} - {`${agent.posts ?? agent.post_count ?? ""} posts`} + {`${agent.posts ?? agent.post_count ?? ""} posts`} )}