@geminilight/mindos 0.5.7 → 0.5.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -17
- package/README_zh.md +15 -14
- package/app/app/api/mcp/agents/route.ts +7 -0
- package/app/app/api/mcp/install-skill/route.ts +7 -1
- package/app/app/api/setup/check-port/route.ts +27 -3
- package/app/app/api/setup/route.ts +2 -9
- package/app/app/globals.css +18 -2
- package/app/app/login/page.tsx +1 -1
- package/app/app/view/[...path]/ViewPageClient.tsx +9 -9
- package/app/components/AskModal.tsx +1 -1
- package/app/components/FileTree.tsx +5 -5
- package/app/components/HomeContent.tsx +1 -1
- package/app/components/SetupWizard.tsx +283 -141
- package/app/components/SyncStatusBar.tsx +3 -3
- package/app/components/ask/MessageList.tsx +2 -2
- package/app/components/ask/SessionHistory.tsx +1 -1
- package/app/components/renderers/agent-inspector/AgentInspectorRenderer.tsx +5 -5
- package/app/components/renderers/config/ConfigRenderer.tsx +3 -3
- package/app/components/renderers/csv/types.ts +1 -1
- package/app/components/renderers/diff/DiffRenderer.tsx +9 -9
- package/app/components/renderers/timeline/TimelineRenderer.tsx +1 -1
- package/app/components/renderers/workflow/WorkflowRenderer.tsx +2 -2
- package/app/components/settings/McpTab.tsx +66 -24
- package/app/components/settings/Primitives.tsx +3 -3
- package/app/components/settings/SyncTab.tsx +5 -5
- package/app/lib/i18n.ts +48 -4
- package/app/lib/mcp-agents.ts +81 -10
- package/bin/lib/build.js +25 -0
- package/bin/lib/gateway.js +44 -4
- package/bin/lib/mcp-agents.js +81 -0
- package/bin/lib/mcp-install.js +34 -4
- package/package.json +13 -1
- package/scripts/setup.js +43 -6
- package/skills/project-wiki/SKILL.md +223 -0
- package/skills/project-wiki/assets/api-reference.tmpl.md +49 -0
- package/skills/project-wiki/assets/backlog.tmpl.md +15 -0
- package/skills/project-wiki/assets/changelog.tmpl.md +16 -0
- package/skills/project-wiki/assets/conventions.tmpl.md +29 -0
- package/skills/project-wiki/assets/design-exploration.tmpl.md +26 -0
- package/skills/project-wiki/assets/design-principle.tmpl.md +48 -0
- package/skills/project-wiki/assets/development-guide.tmpl.md +38 -0
- package/skills/project-wiki/assets/glossary.tmpl.md +9 -0
- package/skills/project-wiki/assets/known-pitfalls.tmpl.md +21 -0
- package/skills/project-wiki/assets/postmortem.tmpl.md +38 -0
- package/skills/project-wiki/assets/product-proposal.tmpl.md +41 -0
- package/skills/project-wiki/assets/project-roadmap.tmpl.md +23 -0
- package/skills/project-wiki/assets/stage-x.tmpl.md +78 -0
- package/skills/project-wiki/assets/system-architecture.tmpl.md +62 -0
- package/skills/project-wiki/references/file-reference.md +254 -0
- package/skills/project-wiki/references/writing-guide.md +28 -0
- package/app/data/pages/home-dark.png +0 -0
- package/app/data/pages/home-mobile-crop.png +0 -0
- package/app/data/pages/home-mobile.png +0 -0
- package/app/data/pages/home.png +0 -0
- package/app/data/pages/view-dir.png +0 -0
- package/app/data/pages/view-file-bot.png +0 -0
- package/app/data/pages/view-file-dark-crop.png +0 -0
- package/app/data/pages/view-file-dark.png +0 -0
- package/app/data/pages/view-file-mobile.png +0 -0
- package/app/data/pages/view-file-sm.png +0 -0
- package/app/data/pages/view-file-top.png +0 -0
- package/app/data/pages/view-file.png +0 -0
- package/app/eslint.config.mjs +0 -18
- package/app/public/landing/index.html +0 -353
- package/app/public/landing/style.css +0 -216
- package/app/vitest.config.ts +0 -14
- package/assets/demo-flow-zh.html +0 -622
package/README.md
CHANGED
|
@@ -48,42 +48,43 @@ MindOS is a **Human-AI Collaborative Mind System**—a local-first knowledge bas
|
|
|
48
48
|
> Help me execute the XXX SOP from MindOS.
|
|
49
49
|
> ```
|
|
50
50
|
|
|
51
|
-
## 🧠
|
|
51
|
+
## 🧠 Human-AI Shared Mind
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
> No more fragmented memory, no more black-box behavior, no more lost experience.
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
**1. Global Sync — Breaking Memory Silos**
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
Each Agent keeps its own memory — switching tools means manually hauling context. **MindOS lets all Agents share one knowledge base via MCP and Skills — record once, reuse everywhere.**
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
**2. Transparent & Controllable — No More Black Boxes**
|
|
60
|
+
|
|
61
|
+
What did your Agent remember? Is it even correct? You have no way to know. **MindOS saves every read/write as local plain text — humans can audit, correct, and delete in the GUI.**
|
|
60
62
|
|
|
61
63
|
**3. Symbiotic Evolution — Experience Flows Back as Instructions**
|
|
62
64
|
|
|
63
|
-
|
|
65
|
+
All that experience from your conversations — gone the moment you close the window. **MindOS auto-distills conversation experience into Skills/SOPs. Notes are instructions. The knowledge base gets better with use.**
|
|
64
66
|
|
|
65
|
-
> **Foundation:** Local-first by default
|
|
67
|
+
> **Foundation:** Local-first by default — all data stays in local plain text for privacy, ownership, and speed.
|
|
66
68
|
|
|
67
69
|
## ✨ Features
|
|
68
70
|
|
|
69
71
|
**For Humans**
|
|
70
72
|
|
|
71
|
-
- **GUI
|
|
72
|
-
- **Built-in Agent Assistant**: converse in context
|
|
73
|
-
- **Plugin
|
|
73
|
+
- **GUI Workbench**: browse, edit, search notes with unified search + AI entry (`⌘K` / `⌘/`), designed for human-AI co-creation.
|
|
74
|
+
- **Built-in Agent Assistant**: converse with the knowledge base in context; edits seamlessly capture human-curated knowledge.
|
|
75
|
+
- **Plugin Extensions**: multiple built-in renderer plugins — TODO Board, CSV Views, Wiki Graph, Timeline, Agent Inspector, and more.
|
|
74
76
|
|
|
75
77
|
**For Agents**
|
|
76
78
|
|
|
77
|
-
- **MCP Server + Skills**:
|
|
78
|
-
- **Structured Templates**:
|
|
79
|
-
- **
|
|
79
|
+
- **MCP Server + Skills**: stdio + HTTP dual transport, full-lineup Agent compatible (OpenClaw, Claude Code, Cursor, etc.). Zero-config access.
|
|
80
|
+
- **Structured Templates**: pre-set directory structures for Profiles, Workflows, Configurations, etc., to jumpstart personal context.
|
|
81
|
+
- **Agent-Ready Docs**: everyday notes naturally double as high-quality executable Agent commands — no format conversion needed, write and dispatch.
|
|
80
82
|
|
|
81
83
|
**Infrastructure**
|
|
82
84
|
|
|
83
|
-
- **
|
|
84
|
-
- **Knowledge Graph**:
|
|
85
|
-
- **Git Time Machine**:
|
|
86
|
-
- **Cross-Device Sync**: auto-commit, push, and pull via Git — edits on one device appear on all others within minutes.
|
|
85
|
+
- **Security**: Bearer Token auth, path sandboxing, INSTRUCTION.md write-protection, atomic writes.
|
|
86
|
+
- **Knowledge Graph**: dynamically parses and visualizes inter-file references and dependencies.
|
|
87
|
+
- **Git Time Machine**: Git auto-sync (commit/push/pull), records every edit by both humans and Agents. One-click rollback, cross-device sync.
|
|
87
88
|
|
|
88
89
|
<details>
|
|
89
90
|
<summary><strong>Coming Soon</strong></summary>
|
package/README_zh.md
CHANGED
|
@@ -48,19 +48,21 @@ MindOS 是一个**人机协同心智系统**——基于本地优先的协作知
|
|
|
48
48
|
> 帮我执行 MindOS 里的 XXX 工作流。
|
|
49
49
|
> ```
|
|
50
50
|
|
|
51
|
-
## 🧠
|
|
51
|
+
## 🧠 人机共享心智
|
|
52
|
+
|
|
53
|
+
> 记忆不再割裂,行为不再黑箱,经验不再断流。
|
|
52
54
|
|
|
53
55
|
**1. 全局同步 — 打破记忆割裂**
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
多个 Agent 各记各的,切换工具靠人工搬运上下文。**MindOS 通过 MCP 和 Skill 让所有 Agent 共享同一份知识库——一处记录,全局复用。**
|
|
56
58
|
|
|
57
59
|
**2. 透明可控 — 消除记忆黑箱**
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
Agent 记了什么、记对没有,用户无从知晓。**MindOS 将每次读写沉淀为本地纯文本,人类可在 GUI 中审查、修正、删除。**
|
|
60
62
|
|
|
61
63
|
**3. 共生演进 — 经验回流为指令**
|
|
62
64
|
|
|
63
|
-
|
|
65
|
+
对话里攒下的经验,关掉窗口就散了。**MindOS 自动将对话经验沉淀为 Skill/SOP,笔记即指令,知识库越用越好。**
|
|
64
66
|
|
|
65
67
|
> **底层原则:** 默认本地优先,全部数据以本地纯文本保存,兼顾隐私、主权与性能。
|
|
66
68
|
|
|
@@ -68,22 +70,21 @@ MindOS 是一个**人机协同心智系统**——基于本地优先的协作知
|
|
|
68
70
|
|
|
69
71
|
**人类侧**
|
|
70
72
|
|
|
71
|
-
- **GUI
|
|
72
|
-
- **内置 Agent
|
|
73
|
-
-
|
|
73
|
+
- **GUI 工作台**:浏览、编辑、搜索笔记,统一搜索 + AI 入口(`⌘K` / `⌘/`),专为人机共创设计。
|
|
74
|
+
- **内置 Agent 助手**:在上下文中与知识库对话,编辑无缝沉淀为可管理知识。
|
|
75
|
+
- **插件扩展**:多种内置渲染器插件——TODO Board、CSV Views、Wiki Graph、Timeline、Agent Inspector 等。
|
|
74
76
|
|
|
75
77
|
**Agent 侧**
|
|
76
78
|
|
|
77
|
-
- **MCP Server + Skills
|
|
78
|
-
-
|
|
79
|
-
-
|
|
79
|
+
- **MCP Server + Skills**:stdio + HTTP 双传输,全阵容 Agent 兼容(OpenClaw, Claude Code, Cursor 等),零配置接入。
|
|
80
|
+
- **结构化模板**:预置 Profile、Workflows、Configurations 等目录骨架,快速冷启动个人 Context。
|
|
81
|
+
- **笔记即指令**:日常笔记天然就是 Agent 可直接执行的高质量指令——无需额外格式转换,写下即可调度。
|
|
80
82
|
|
|
81
83
|
**基础设施**
|
|
82
84
|
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
- **Git
|
|
86
|
-
- **跨设备同步**:通过 Git 自动 commit、push、pull —— 一台设备的编辑几分钟内同步到所有设备。
|
|
85
|
+
- **安全防线**:Bearer Token 认证、路径沙箱、INSTRUCTION.md 写保护、原子写入。
|
|
86
|
+
- **知识图谱**:动态解析并可视化文件间的引用与依赖关系。
|
|
87
|
+
- **Git 时光机**:Git 自动同步(commit/push/pull),记录人类与 Agent 的每次编辑历史,一键回滚,跨设备同步。
|
|
87
88
|
|
|
88
89
|
<details>
|
|
89
90
|
<summary><strong>即将到来</strong></summary>
|
|
@@ -20,6 +20,13 @@ export async function GET() {
|
|
|
20
20
|
preferredTransport: agent.preferredTransport,
|
|
21
21
|
};
|
|
22
22
|
});
|
|
23
|
+
|
|
24
|
+
// Sort: installed first, then detected, then not found
|
|
25
|
+
agents.sort((a, b) => {
|
|
26
|
+
const rank = (x: typeof a) => x.installed ? 0 : x.present ? 1 : 2;
|
|
27
|
+
return rank(a) - rank(b);
|
|
28
|
+
});
|
|
29
|
+
|
|
23
30
|
return NextResponse.json({ agents });
|
|
24
31
|
} catch (err) {
|
|
25
32
|
return NextResponse.json({ error: String(err) }, { status: 500 });
|
|
@@ -15,7 +15,7 @@ const UNIVERSAL_AGENTS = new Set([
|
|
|
15
15
|
]);
|
|
16
16
|
|
|
17
17
|
// Agents that do NOT support Skills at all
|
|
18
|
-
const SKILL_UNSUPPORTED = new Set([
|
|
18
|
+
const SKILL_UNSUPPORTED = new Set<string>([]);
|
|
19
19
|
|
|
20
20
|
// MCP agent key → npx skills agent name (for non-universal agents)
|
|
21
21
|
const AGENT_NAME_MAP: Record<string, string> = {
|
|
@@ -24,6 +24,12 @@ const AGENT_NAME_MAP: Record<string, string> = {
|
|
|
24
24
|
'trae': 'trae',
|
|
25
25
|
'openclaw': 'openclaw',
|
|
26
26
|
'codebuddy': 'codebuddy',
|
|
27
|
+
'iflow-cli': 'iflow-cli',
|
|
28
|
+
'pi': 'pi',
|
|
29
|
+
'augment': 'augment',
|
|
30
|
+
'qwen-code': 'qwen-code',
|
|
31
|
+
'trae-cn': 'trae-cn',
|
|
32
|
+
'roo': 'roo',
|
|
27
33
|
};
|
|
28
34
|
|
|
29
35
|
/* ── Helpers ──────────────────────────────────────────────────── */
|
|
@@ -27,29 +27,53 @@ async function isSelfPort(port: number): Promise<boolean> {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
async function findFreePort(start: number): Promise<number | null> {
|
|
30
|
+
async function findFreePort(start: number, selfPorts: Set<number>): Promise<number | null> {
|
|
31
31
|
for (let p = start; p <= 65535; p++) {
|
|
32
|
+
if (selfPorts.has(p)) continue;
|
|
32
33
|
if (!await isPortInUse(p)) return p;
|
|
33
34
|
}
|
|
34
35
|
return null;
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
/**
|
|
39
|
+
* The port this MindOS web server is actually listening on.
|
|
40
|
+
* Derived from the incoming request URL — always reliable, no network round-trip.
|
|
41
|
+
*
|
|
42
|
+
* Note: We intentionally do NOT read settings here. Settings contain *configured*
|
|
43
|
+
* ports (webPort / mcpPort), which may not actually be listening yet (e.g. during
|
|
44
|
+
* first onboard, or if MCP server hasn't started). Treating configured-but-not-
|
|
45
|
+
* listening ports as "self" would mask real conflicts.
|
|
46
|
+
*/
|
|
47
|
+
function getListeningPort(req: NextRequest): number {
|
|
48
|
+
return parseInt(req.nextUrl.port || '0', 10);
|
|
49
|
+
}
|
|
50
|
+
|
|
37
51
|
export async function POST(req: NextRequest) {
|
|
38
52
|
try {
|
|
39
53
|
const { port } = await req.json() as { port: number };
|
|
40
54
|
if (!port || port < 1024 || port > 65535) {
|
|
41
55
|
return NextResponse.json({ error: 'Invalid port' }, { status: 400 });
|
|
42
56
|
}
|
|
57
|
+
|
|
58
|
+
const myPort = getListeningPort(req);
|
|
59
|
+
|
|
60
|
+
// Fast path: if checking the port we're currently listening on, skip network round-trip
|
|
61
|
+
if (myPort > 0 && port === myPort) {
|
|
62
|
+
return NextResponse.json({ available: true, isSelf: true });
|
|
63
|
+
}
|
|
64
|
+
|
|
43
65
|
const inUse = await isPortInUse(port);
|
|
44
66
|
if (!inUse) {
|
|
45
67
|
return NextResponse.json({ available: true, isSelf: false });
|
|
46
68
|
}
|
|
47
|
-
// Port is occupied — check if it's
|
|
69
|
+
// Port is occupied — check if it's another MindOS instance
|
|
48
70
|
const self = await isSelfPort(port);
|
|
49
71
|
if (self) {
|
|
50
72
|
return NextResponse.json({ available: true, isSelf: true });
|
|
51
73
|
}
|
|
52
|
-
const
|
|
74
|
+
const skipPorts = new Set<number>();
|
|
75
|
+
if (myPort > 0) skipPorts.add(myPort);
|
|
76
|
+
const suggestion = await findFreePort(port + 1, skipPorts);
|
|
53
77
|
return NextResponse.json({ available: false, isSelf: false, suggestion });
|
|
54
78
|
} catch (err) {
|
|
55
79
|
return NextResponse.json({ error: String(err) }, { status: 500 });
|
|
@@ -67,17 +67,10 @@ export async function POST(req: NextRequest) {
|
|
|
67
67
|
return NextResponse.json({ error: `Invalid MCP port: ${mcpPortNum}` }, { status: 400 });
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
// Apply template
|
|
70
|
+
// Apply template (copyRecursive has skip-existing protection)
|
|
71
71
|
const dirExists = fs.existsSync(resolvedRoot);
|
|
72
|
-
let dirEmpty = true;
|
|
73
|
-
if (dirExists) {
|
|
74
|
-
try {
|
|
75
|
-
const entries = fs.readdirSync(resolvedRoot).filter(e => !e.startsWith('.'));
|
|
76
|
-
dirEmpty = entries.length === 0;
|
|
77
|
-
} catch { /* treat as empty */ }
|
|
78
|
-
}
|
|
79
72
|
|
|
80
|
-
if (template
|
|
73
|
+
if (template) {
|
|
81
74
|
applyTemplate(template, resolvedRoot);
|
|
82
75
|
} else if (!dirExists) {
|
|
83
76
|
fs.mkdirSync(resolvedRoot, { recursive: true });
|
package/app/app/globals.css
CHANGED
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
--color-input: var(--input);
|
|
27
27
|
--color-border: var(--border);
|
|
28
28
|
--color-destructive: var(--destructive);
|
|
29
|
+
--color-success: var(--success);
|
|
30
|
+
--color-error: var(--error);
|
|
29
31
|
--color-accent-foreground: var(--accent-foreground);
|
|
30
32
|
--color-accent: var(--accent);
|
|
31
33
|
--color-muted-foreground: var(--muted-foreground);
|
|
@@ -69,10 +71,12 @@ body {
|
|
|
69
71
|
--destructive: oklch(0.58 0.22 27);
|
|
70
72
|
--border: rgba(28, 26, 23, 0.1);
|
|
71
73
|
--input: rgba(28, 26, 23, 0.12);
|
|
72
|
-
--ring:
|
|
74
|
+
--ring: var(--amber);
|
|
73
75
|
--radius: 0.5rem;
|
|
74
76
|
--amber: #c8873a;
|
|
75
77
|
--amber-dim: rgba(200, 135, 58, 0.12);
|
|
78
|
+
--success: #7aad80;
|
|
79
|
+
--error: #c85050;
|
|
76
80
|
--sidebar: #ede9e1;
|
|
77
81
|
--sidebar-foreground: #1c1a17;
|
|
78
82
|
--sidebar-primary: #1c1a17;
|
|
@@ -101,9 +105,11 @@ body {
|
|
|
101
105
|
--destructive: oklch(0.704 0.191 22.216);
|
|
102
106
|
--border: rgba(232, 228, 220, 0.08);
|
|
103
107
|
--input: rgba(232, 228, 220, 0.1);
|
|
104
|
-
--ring:
|
|
108
|
+
--ring: var(--amber);
|
|
105
109
|
--amber: #d4954a;
|
|
106
110
|
--amber-dim: rgba(212, 149, 74, 0.12);
|
|
111
|
+
--success: #7aad80;
|
|
112
|
+
--error: #c85050;
|
|
107
113
|
--sidebar: #1c1a17;
|
|
108
114
|
--sidebar-foreground: #e8e4dc;
|
|
109
115
|
--sidebar-primary: #d4954a;
|
|
@@ -290,6 +296,16 @@ body {
|
|
|
290
296
|
button, a { -webkit-tap-highlight-color: transparent; }
|
|
291
297
|
}
|
|
292
298
|
|
|
299
|
+
/* Respect user's reduced-motion preference */
|
|
300
|
+
@media (prefers-reduced-motion: reduce) {
|
|
301
|
+
*, *::before, *::after {
|
|
302
|
+
animation-duration: 0.01ms !important;
|
|
303
|
+
animation-iteration-count: 1 !important;
|
|
304
|
+
transition-duration: 0.01ms !important;
|
|
305
|
+
scroll-behavior: auto !important;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
293
309
|
/* Global focus-visible ring for interactive elements */
|
|
294
310
|
button:focus-visible,
|
|
295
311
|
a:focus-visible,
|
package/app/app/login/page.tsx
CHANGED
|
@@ -90,7 +90,7 @@ function LoginForm() {
|
|
|
90
90
|
autoFocus
|
|
91
91
|
autoComplete="current-password"
|
|
92
92
|
required
|
|
93
|
-
className="w-full px-3 py-2 text-sm bg-background border border-border rounded-lg text-foreground placeholder:text-muted-foreground outline-none focus:ring-1 focus:ring-ring disabled:opacity-50"
|
|
93
|
+
className="w-full px-3 py-2 text-sm bg-background border border-border rounded-lg text-foreground placeholder:text-muted-foreground outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:opacity-50"
|
|
94
94
|
/>
|
|
95
95
|
</div>
|
|
96
96
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useTransition, useCallback, useEffect, useRef, useSyncExternalStore, useMemo, Suspense } from 'react';
|
|
4
4
|
import { useRouter } from 'next/navigation';
|
|
5
|
-
import { Edit3, Save, X, Loader2, LayoutTemplate, ArrowLeft } from 'lucide-react';
|
|
5
|
+
import { Edit3, Save, X, Loader2, LayoutTemplate, ArrowLeft, Share2, FileText, Code } from 'lucide-react';
|
|
6
6
|
import { lazy } from 'react';
|
|
7
7
|
import MarkdownView from '@/components/MarkdownView';
|
|
8
8
|
import JsonView from '@/components/JsonView';
|
|
@@ -213,13 +213,13 @@ export default function ViewPageClient({
|
|
|
213
213
|
|
|
214
214
|
<div className="flex items-center gap-1.5 md:gap-2 shrink-0">
|
|
215
215
|
{saveSuccess && (
|
|
216
|
-
<span className="text-xs flex items-center gap-1.5 font-display" style={{ color: '
|
|
217
|
-
<span className="w-1.5 h-1.5 rounded-full" style={{ background: '
|
|
216
|
+
<span className="text-xs flex items-center gap-1.5 font-display" style={{ color: 'var(--success)' }}>
|
|
217
|
+
<span className="w-1.5 h-1.5 rounded-full" style={{ background: 'var(--success)' }} />
|
|
218
218
|
<span className="hidden sm:inline">saved</span>
|
|
219
219
|
</span>
|
|
220
220
|
)}
|
|
221
221
|
{saveError && (
|
|
222
|
-
<span className="text-xs text-
|
|
222
|
+
<span className="text-xs text-error hidden sm:inline">{saveError}</span>
|
|
223
223
|
)}
|
|
224
224
|
|
|
225
225
|
{/* Graph toggle — only for md files, hidden when graph plugin is disabled */}
|
|
@@ -233,8 +233,8 @@ export default function ViewPageClient({
|
|
|
233
233
|
}}
|
|
234
234
|
title={effectiveGraphMode ? 'Switch to document view' : 'Switch to Wiki Graph'}
|
|
235
235
|
>
|
|
236
|
-
<
|
|
237
|
-
<span className="hidden sm:inline">Graph</span>
|
|
236
|
+
{effectiveGraphMode ? <FileText size={13} /> : <Share2 size={13} />}
|
|
237
|
+
<span className="hidden sm:inline">{effectiveGraphMode ? 'Doc' : 'Graph'}</span>
|
|
238
238
|
</button>
|
|
239
239
|
)}
|
|
240
240
|
|
|
@@ -244,12 +244,12 @@ export default function ViewPageClient({
|
|
|
244
244
|
onClick={handleToggleRaw}
|
|
245
245
|
className="flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-colors font-display"
|
|
246
246
|
style={{
|
|
247
|
-
background: effectiveUseRaw ? 'var(--
|
|
248
|
-
color: effectiveUseRaw ? 'var(--
|
|
247
|
+
background: effectiveUseRaw ? `${'var(--amber)'}22` : 'var(--muted)',
|
|
248
|
+
color: effectiveUseRaw ? 'var(--amber)' : 'var(--muted-foreground)',
|
|
249
249
|
}}
|
|
250
250
|
title={effectiveUseRaw ? `Switch to ${registryRenderer?.name}` : 'View raw'}
|
|
251
251
|
>
|
|
252
|
-
<LayoutTemplate size={13} />
|
|
252
|
+
{effectiveUseRaw ? <LayoutTemplate size={13} /> : <Code size={13} />}
|
|
253
253
|
<span className="hidden sm:inline">{effectiveUseRaw ? registryRenderer.name : 'Raw'}</span>
|
|
254
254
|
</button>
|
|
255
255
|
)}
|
|
@@ -310,7 +310,7 @@ export default function AskModal({ open, onClose, currentFile }: AskModalProps)
|
|
|
310
310
|
)}
|
|
311
311
|
|
|
312
312
|
{upload.uploadError && (
|
|
313
|
-
<div className="px-4 pb-1 text-xs text-
|
|
313
|
+
<div className="px-4 pb-1 text-xs text-error">{upload.uploadError}</div>
|
|
314
314
|
)}
|
|
315
315
|
|
|
316
316
|
{/* @-mention dropdown */}
|
|
@@ -65,7 +65,7 @@ function NewFileInline({ dirPath, depth, onDone }: { dirPath: string; depth: num
|
|
|
65
65
|
className="
|
|
66
66
|
flex-1 bg-muted border border-border rounded px-2 py-1
|
|
67
67
|
text-xs text-foreground placeholder:text-muted-foreground
|
|
68
|
-
focus:outline-none focus:
|
|
68
|
+
focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring
|
|
69
69
|
"
|
|
70
70
|
/>
|
|
71
71
|
{isPending
|
|
@@ -80,7 +80,7 @@ function NewFileInline({ dirPath, depth, onDone }: { dirPath: string; depth: num
|
|
|
80
80
|
)
|
|
81
81
|
}
|
|
82
82
|
</div>
|
|
83
|
-
{error && <p className="text-xs text-
|
|
83
|
+
{error && <p className="text-xs text-error mt-0.5 px-1">{error}</p>}
|
|
84
84
|
</div>
|
|
85
85
|
);
|
|
86
86
|
}
|
|
@@ -161,7 +161,7 @@ function DirectoryNode({ node, depth, currentPath, onNavigate }: {
|
|
|
161
161
|
if (e.key === 'Escape') setRenaming(false);
|
|
162
162
|
}}
|
|
163
163
|
onBlur={commitRename}
|
|
164
|
-
className="w-full bg-muted border border-
|
|
164
|
+
className="w-full bg-muted border border-border rounded px-2 py-0.5 text-xs text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
165
165
|
/>
|
|
166
166
|
{isPending && <Loader2 size={12} className="absolute right-3 top-1/2 -translate-y-1/2 animate-spin text-zinc-500" />}
|
|
167
167
|
</div>
|
|
@@ -304,7 +304,7 @@ function FileNodeItem({ node, depth, currentPath, onNavigate }: {
|
|
|
304
304
|
if (e.key === 'Escape') setRenaming(false);
|
|
305
305
|
}}
|
|
306
306
|
onBlur={commitRename}
|
|
307
|
-
className="w-full bg-muted border border-
|
|
307
|
+
className="w-full bg-muted border border-border rounded px-2 py-0.5 text-xs text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
308
308
|
/>
|
|
309
309
|
{isPending && <Loader2 size={12} className="absolute right-3 top-1/2 -translate-y-1/2 animate-spin text-zinc-500" />}
|
|
310
310
|
</div>
|
|
@@ -334,7 +334,7 @@ function FileNodeItem({ node, depth, currentPath, onNavigate }: {
|
|
|
334
334
|
<button onClick={startRename} className="p-0.5 rounded text-muted-foreground hover:text-foreground hover:bg-muted transition-colors" title={t.fileTree.rename}>
|
|
335
335
|
<Pencil size={12} />
|
|
336
336
|
</button>
|
|
337
|
-
<button onClick={handleDelete} className="p-0.5 rounded text-muted-foreground hover:text-
|
|
337
|
+
<button onClick={handleDelete} className="p-0.5 rounded text-muted-foreground hover:text-error hover:bg-muted transition-colors" title={t.fileTree.delete}>
|
|
338
338
|
<Trash2 size={12} />
|
|
339
339
|
</button>
|
|
340
340
|
</div>
|
|
@@ -278,7 +278,7 @@ export default function HomeContent({ recent, existingFiles }: { recent: RecentF
|
|
|
278
278
|
className="flex items-center gap-3 px-3 py-2.5 rounded-lg transition-all duration-100 group-hover:translate-x-0.5 hover:bg-muted"
|
|
279
279
|
>
|
|
280
280
|
{isCSV
|
|
281
|
-
? <Table size={13} className="shrink-0" style={{ color: '
|
|
281
|
+
? <Table size={13} className="shrink-0" style={{ color: 'var(--success)' }} />
|
|
282
282
|
: <FileText size={13} className="shrink-0" style={{ color: 'var(--muted-foreground)' }} />
|
|
283
283
|
}
|
|
284
284
|
<div className="flex-1 min-w-0">
|