@spzxg/cli-ui 0.6.2 → 0.6.5
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/CHANGELOG.md +26 -0
- package/README.md +137 -11
- package/dist/cli-ui-embed-BmLN2Jjg.js +43290 -0
- package/dist/{cli-ui-index-BY5i3wId.js → cli-ui-index-D4qfPHMd.js} +2 -2
- package/dist/{cli-ui-jszip.min-BZsUwI-s.js → cli-ui-jszip.min-DC9898pC.js} +1 -1
- package/dist/{cli-ui-jszip.min-m01fexHW.js → cli-ui-jszip.min-DpEl0G1z.js} +2 -2
- package/dist/{cli-ui-xlsx-CpWicJLT.js → cli-ui-xlsx-zs7oEtPj.js} +1 -1
- package/dist/cli-ui.css +32 -1
- package/dist/cli-ui.js +1 -1
- package/package.json +14 -8
- package/src/board/BrowserPane.tsx +160 -74
- package/src/board/ChangesPane.tsx +2 -2
- package/src/board/CliBoard.tsx +79 -50
- package/src/board/FileViewer.tsx +32 -22
- package/src/board/FilesPane.tsx +32 -15
- package/src/board/PanelControls.tsx +28 -0
- package/src/board/blocks.tsx +5 -5
- package/src/board/context.ts +65 -0
- package/src/board/fileOps.ts +26 -3
- package/src/board/fileWatch.ts +88 -0
- package/src/board/folderDownload.ts +30 -14
- package/src/board/panels.ts +33 -0
- package/src/board/textLoad.ts +2 -2
- package/src/board/workspace.ts +38 -9
- package/src/chat/CliChat.tsx +73 -30
- package/src/chat/Composer.tsx +41 -41
- package/src/chat/RunControls.tsx +99 -0
- package/src/chat/configuration.ts +15 -0
- package/src/chat/draft.ts +41 -0
- package/src/chat/export.ts +2 -2
- package/src/components.tsx +13 -0
- package/src/core/blocks.ts +2 -2
- package/src/core/callbacks.ts +8 -0
- package/src/core/client.ts +8 -6
- package/src/core/conversation.ts +3 -3
- package/src/core/id.ts +15 -0
- package/src/core/identity.ts +33 -0
- package/src/core/provider.tsx +24 -14
- package/src/core/store.ts +83 -26
- package/src/core/types.ts +34 -0
- package/src/elements.tsx +135 -29
- package/src/embed.ts +6 -8
- package/src/index.ts +24 -17
- package/src/layout/CliSplit.tsx +1 -1
- package/src/msglist/CliMsgList.tsx +10 -10
- package/src/operator/branchFlow.ts +35 -0
- package/src/operator/index.ts +9 -0
- package/src/operator/liveStream.ts +1 -0
- package/src/operator/node/AgentConnect.tsx +624 -0
- package/src/operator/node/AppearancePanel.tsx +35 -0
- package/src/operator/node/BranchHistory.tsx +67 -0
- package/src/operator/node/BranchNavigator.tsx +36 -0
- package/src/operator/node/BrowserPane.tsx +128 -0
- package/src/operator/node/BrowserPopup.tsx +22 -0
- package/src/operator/node/Chat.tsx +517 -0
- package/src/operator/node/CommandPalette.tsx +213 -0
- package/src/operator/node/Composer.tsx +287 -0
- package/src/operator/node/InsightsView.tsx +831 -0
- package/src/operator/node/Layout.tsx +190 -0
- package/src/operator/node/Markdown.tsx +61 -0
- package/src/operator/node/Modal.tsx +179 -0
- package/src/operator/node/NeedsYouMenu.tsx +54 -0
- package/src/operator/node/OnboardingWizard.tsx +250 -0
- package/src/operator/node/Orchestrator.tsx +634 -0
- package/src/operator/node/ProjectLanding.tsx +86 -0
- package/src/operator/node/ProjectsColumn.tsx +137 -0
- package/src/operator/node/ProviderAuth.tsx +561 -0
- package/src/operator/node/RunControls.tsx +2 -0
- package/src/operator/node/RunSwitch.tsx +113 -0
- package/src/operator/node/Services.tsx +245 -0
- package/src/operator/node/SessionRail.tsx +260 -0
- package/src/operator/node/SessionView.tsx +288 -0
- package/src/operator/node/SettingsView.tsx +690 -0
- package/src/operator/node/TaskBoard.tsx +352 -0
- package/src/operator/node/TaskChanges.tsx +530 -0
- package/src/operator/node/TaskHero.tsx +50 -0
- package/src/operator/node/TasksColumn.tsx +200 -0
- package/src/operator/node/Terminal.tsx +143 -0
- package/src/operator/node/Transcript.tsx +463 -0
- package/src/operator/node/ViewFile.tsx +79 -0
- package/src/operator/node/Welcome.tsx +101 -0
- package/src/operator/node/WorktreePrune.tsx +333 -0
- package/src/operator/node/agents.ts +58 -0
- package/src/operator/node/api.ts +40 -0
- package/src/operator/node/boardBackend.ts +80 -0
- package/src/operator/node/boardEvents.ts +22 -0
- package/src/operator/node/boardInteraction.ts +15 -0
- package/src/operator/node/boardState.ts +57 -0
- package/src/operator/node/boardWorkspace.ts +12 -0
- package/src/operator/node/branchFlow.ts +1 -0
- package/src/operator/node/composerDraft.ts +52 -0
- package/src/operator/node/fileLinks.ts +81 -0
- package/src/operator/node/format.ts +290 -0
- package/src/operator/node/github.tsx +234 -0
- package/src/operator/node/icons.tsx +88 -0
- package/src/operator/node/liveStream.ts +270 -0
- package/src/operator/node/modals.tsx +720 -0
- package/src/operator/node/navHistory.ts +70 -0
- package/src/operator/node/persist.ts +26 -0
- package/src/operator/node/protocol/agents/optIn.ts +9 -0
- package/src/operator/node/protocol/agents/types.ts +394 -0
- package/src/operator/node/protocol/approvalFailure.ts +77 -0
- package/src/operator/node/protocol/authFailure.ts +66 -0
- package/src/operator/node/protocol/customModels.ts +23 -0
- package/src/operator/node/protocol/events.ts +13 -0
- package/src/operator/node/protocol/features.ts +70 -0
- package/src/operator/node/protocol/internalUsage.ts +1 -0
- package/src/operator/node/protocol/promptLimits.ts +52 -0
- package/src/operator/node/protocol/storageRetention.ts +13 -0
- package/src/operator/node/protocol/types.ts +332 -0
- package/src/operator/node/protocol/usageLimit.ts +72 -0
- package/src/operator/node/shared.tsx +89 -0
- package/src/operator/node/transcriptModel.ts +129 -0
- package/src/operator/node/types.ts +296 -0
- package/src/operator/node/useGlobalEvents.ts +97 -0
- package/src/operator/node/useOrchestrator.ts +696 -0
- package/src/operator/node/usePrefs.ts +114 -0
- package/src/operator/node/useRecaps.ts +91 -0
- package/src/operator/node/useTaskStream.ts +279 -0
- package/src/operator.base.css +1798 -0
- package/src/operator.css +186 -16
- package/src/styles.css +20 -0
- package/src/ui/Markdown.tsx +7 -2
- package/src/ui/Popover.tsx +77 -0
- package/src/ui/TextBlock.tsx +3 -3
- package/src/ui/Transcript.tsx +10 -8
- package/src/ui/fileLinks.ts +6 -0
- package/src/ui/format.ts +1 -1
- package/src/ui/shared.tsx +1 -1
- package/src/ui/transcriptModel.ts +2 -2
- package/dist/cli-ui-embed-6IJ_jRVh.js +0 -41223
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Icon } from "./icons";
|
|
4
|
+
import { Markdown } from "./Markdown";
|
|
5
|
+
import { relTime } from "./format";
|
|
6
|
+
import { ErrNote } from "./shared";
|
|
7
|
+
import type { ProjectRow, RecapInfo } from "./types";
|
|
8
|
+
|
|
9
|
+
// Shown in the session pane when a project is open but no task is selected.
|
|
10
|
+
// Surfaces the auto-generated "where you left off" recap when one exists / is
|
|
11
|
+
// brewing; otherwise the plain create-a-task prompt.
|
|
12
|
+
export function ProjectLanding({ project, recap, onNewTask, onRefreshRecap }: {
|
|
13
|
+
project: ProjectRow; recap?: RecapInfo; onNewTask: () => void; onRefreshRecap: () => void;
|
|
14
|
+
}) {
|
|
15
|
+
const generating = recap?.generating && !recap?.recap;
|
|
16
|
+
const hasRecap = !!recap?.recap;
|
|
17
|
+
|
|
18
|
+
if (generating) {
|
|
19
|
+
return (
|
|
20
|
+
<div className="empty" style={{ margin: "auto" }}>
|
|
21
|
+
<div className="e-ic">{Icon.clock()}</div>
|
|
22
|
+
<div className="e-t">正在为你回顾…</div>
|
|
23
|
+
<div className="e-s">正在整理你在 {project.name} 上次的进度。</div>
|
|
24
|
+
<span className="typing" style={{ marginTop: 14 }}><i /><i /><i /></span>
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Recap fetch/generation failed and there's nothing older to show — offer a
|
|
30
|
+
// retry rather than silently falling through to the plain empty state.
|
|
31
|
+
if (recap?.error && !hasRecap) {
|
|
32
|
+
return (
|
|
33
|
+
<div className="empty" style={{ margin: "auto", maxWidth: 340 }}>
|
|
34
|
+
<div className="e-ic">{Icon.clock()}</div>
|
|
35
|
+
<div className="e-t">无法生成回顾</div>
|
|
36
|
+
<div className="e-s">{project.name} 的回顾加载失败:{recap.error}</div>
|
|
37
|
+
<div style={{ display: "flex", gap: 10, justifyContent: "center", marginTop: 16 }}>
|
|
38
|
+
<button className="btn btn-line" onClick={onRefreshRecap}>{Icon.restore()} 重试</button>
|
|
39
|
+
<button className="btn btn-accent" onClick={onNewTask}>{Icon.plus()} New task</button>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (hasRecap) {
|
|
46
|
+
return (
|
|
47
|
+
<div className="transcript">
|
|
48
|
+
<div className="tw" style={{ maxWidth: 720 }}>
|
|
49
|
+
<div className="recap-card">
|
|
50
|
+
<div className="recap-head">
|
|
51
|
+
<span className="recap-badge">{Icon.clock()} 上次进度</span>
|
|
52
|
+
<span className="recap-meta">{recap!.recap_at ? `回顾于 ${relTime(recap!.recap_at)}` : ""}</span>
|
|
53
|
+
<span className="spacer" />
|
|
54
|
+
<button
|
|
55
|
+
className="btn btn-line btn-sm"
|
|
56
|
+
onClick={onRefreshRecap}
|
|
57
|
+
disabled={recap!.generating}
|
|
58
|
+
title="根据最新动态重新生成回顾"
|
|
59
|
+
>
|
|
60
|
+
{Icon.clear()} {recap!.generating ? "刷新中…" : "刷新"}
|
|
61
|
+
</button>
|
|
62
|
+
</div>
|
|
63
|
+
{recap!.error && !recap!.generating && (
|
|
64
|
+
<ErrNote style={{ margin: "12px 14px 0" }}>刷新失败:{recap!.error}</ErrNote>
|
|
65
|
+
)}
|
|
66
|
+
<div className="recap-body"><Markdown>{recap!.recap ?? ""}</Markdown></div>
|
|
67
|
+
<div className="recap-foot">
|
|
68
|
+
<span className="recap-meta">挑一个任务继续,或者新建一个。</span>
|
|
69
|
+
<span className="spacer" />
|
|
70
|
+
<button className="btn btn-accent btn-sm" onClick={onNewTask}>{Icon.plus()} New task</button>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<div className="empty" style={{ margin: "auto" }}>
|
|
80
|
+
<div className="e-ic">{Icon.bolt()}</div>
|
|
81
|
+
<div className="e-t">未选择任务</div>
|
|
82
|
+
<div className="e-s">创建一个任务来启动智能体会话。</div>
|
|
83
|
+
<button className="btn btn-accent" style={{ marginTop: 16 }} onClick={onNewTask}>{Icon.plus()} New task</button>
|
|
84
|
+
</div>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Icon } from "./icons";
|
|
5
|
+
import { fmtCost, relTime } from "./format";
|
|
6
|
+
import { SEARCH_MIN, type AgentInfo, type ProjectRow } from "./types";
|
|
7
|
+
import { SearchBar } from "./shared";
|
|
8
|
+
|
|
9
|
+
// Footer line under "Your workspace": the real auth state per connected agent,
|
|
10
|
+
// from GET /api/agents (which reports the EFFECTIVE billing credential — an
|
|
11
|
+
// active API key outranks a stored subscription login; see issue #4).
|
|
12
|
+
function agentAuthLine(agents: AgentInfo[]): string {
|
|
13
|
+
const parts = agents
|
|
14
|
+
.filter((a) => a.authenticated)
|
|
15
|
+
.map((a) =>
|
|
16
|
+
a.account?.method === "bedrock"
|
|
17
|
+
? `${a.label} · Amazon Bedrock`
|
|
18
|
+
: a.account?.method === "compatible" ? `${a.label} · 兼容端点(${a.account.plan ?? "已配置"})`
|
|
19
|
+
: a.account?.method === "api_key" ? `${a.label} · API 密钥` : `${a.label} · ${a.account?.plan ? `${a.account.plan} 登录` : "订阅账号"}`,
|
|
20
|
+
);
|
|
21
|
+
return parts.length ? parts.join("、") : "未连接任何智能体";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function ProjectsColumn({ projects, deprecated, agents, selId, running, width, onSelect, onNew, onOpenAppearance, onReorder, onRestore, onCollapse, settingsActive, onOpenSettings, mobile }: {
|
|
25
|
+
projects: ProjectRow[]; deprecated: ProjectRow[]; agents: AgentInfo[]; selId: string | null; running: Set<string>; width: number;
|
|
26
|
+
onSelect: (id: string) => void; onNew: () => void; onOpenAppearance: () => void;
|
|
27
|
+
onReorder: (ids: string[]) => void; onRestore: (id: string) => void; onCollapse: () => void;
|
|
28
|
+
settingsActive: boolean; onOpenSettings: () => void; mobile?: boolean;
|
|
29
|
+
}) {
|
|
30
|
+
const [dragId, setDragId] = useState<string | null>(null);
|
|
31
|
+
const [overId, setOverId] = useState<string | null>(null);
|
|
32
|
+
const [showDeprecated, setShowDeprecated] = useState(false);
|
|
33
|
+
const [query, setQuery] = useState("");
|
|
34
|
+
const q = query.trim().toLowerCase();
|
|
35
|
+
const shown = q ? projects.filter((p) => p.name.toLowerCase().includes(q) || (p.sub ?? "").toLowerCase().includes(q)) : projects;
|
|
36
|
+
|
|
37
|
+
const drop = (targetId: string) => {
|
|
38
|
+
if (dragId && dragId !== targetId) {
|
|
39
|
+
const ids = projects.map((p) => p.id);
|
|
40
|
+
const from = ids.indexOf(dragId);
|
|
41
|
+
const to = ids.indexOf(targetId);
|
|
42
|
+
if (from !== -1 && to !== -1) {
|
|
43
|
+
ids.splice(to, 0, ids.splice(from, 1)[0]);
|
|
44
|
+
onReorder(ids);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
setDragId(null);
|
|
48
|
+
setOverId(null);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div className="col col-projects" style={{ flexBasis: width }}>
|
|
53
|
+
<div className="col-head">
|
|
54
|
+
<span className="ch-title">项目</span>
|
|
55
|
+
<span className="spacer" />
|
|
56
|
+
<button className="icon-btn" title="外观" onClick={onOpenAppearance}>{Icon.sliders()}</button>
|
|
57
|
+
<button className="icon-btn" title="新建项目" onClick={onNew}>{Icon.plus()}</button>
|
|
58
|
+
{!mobile && <button className="icon-btn" title="隐藏项目面板" onClick={onCollapse}>{Icon.chevRight({ style: { transform: "rotate(180deg)" } })}</button>}
|
|
59
|
+
</div>
|
|
60
|
+
{projects.length >= SEARCH_MIN && <SearchBar value={query} onChange={setQuery} placeholder="搜索项目…" />}
|
|
61
|
+
<div className="scroll">
|
|
62
|
+
<div className="proj-list">
|
|
63
|
+
{shown.map((p) => (
|
|
64
|
+
<button
|
|
65
|
+
key={p.id}
|
|
66
|
+
className={`proj ${p.id === selId ? "sel" : ""} ${dragId === p.id ? "dragging" : ""} ${overId === p.id && dragId && dragId !== p.id ? "drag-over" : ""}`}
|
|
67
|
+
onClick={() => onSelect(p.id)}
|
|
68
|
+
draggable
|
|
69
|
+
onDragStart={(e) => { setDragId(p.id); e.dataTransfer.effectAllowed = "move"; }}
|
|
70
|
+
onDragOver={(e) => { e.preventDefault(); e.dataTransfer.dropEffect = "move"; if (dragId) setOverId(p.id); }}
|
|
71
|
+
onDrop={(e) => { e.preventDefault(); drop(p.id); }}
|
|
72
|
+
onDragEnd={() => { setDragId(null); setOverId(null); }}
|
|
73
|
+
title="拖动可排序"
|
|
74
|
+
>
|
|
75
|
+
<div className="pic" style={{ background: p.color }}>
|
|
76
|
+
{p.name[0]}
|
|
77
|
+
{p.awaiting_count > 0 && (
|
|
78
|
+
<span className="proj-await" title={`${p.awaiting_count} 个任务在等你处理`}>{p.awaiting_count}</span>
|
|
79
|
+
)}
|
|
80
|
+
</div>
|
|
81
|
+
<div className="pmeta">
|
|
82
|
+
<div className="pname">{p.name}</div>
|
|
83
|
+
<div className="psub">
|
|
84
|
+
{p.task_count} 个任务{p.sub ? ` · ${p.sub}` : ""}
|
|
85
|
+
{p.cost_usd > 0 && <span className="psub-cost" title="该项目所有任务的累计花费"> · {fmtCost(p.cost_usd)}</span>}
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
<div className="pcount" title={p.last_activity ? `最近活动于 ${relTime(p.last_activity)}` : "从未活动"}>
|
|
89
|
+
{p.last_activity ? relTime(p.last_activity) : "从未"}
|
|
90
|
+
</div>
|
|
91
|
+
</button>
|
|
92
|
+
))}
|
|
93
|
+
{q && shown.length === 0 && <div className="search-empty">没有匹配“{query.trim()}”的项目。</div>}
|
|
94
|
+
{!q && (
|
|
95
|
+
<button className="proj" style={{ color: "var(--ink-3)" }} onClick={onNew}>
|
|
96
|
+
<div className="pic" style={{ background: "var(--surface-2)", color: "var(--ink-3)", boxShadow: "inset 0 0 0 1px var(--line-2)" }}>{Icon.plus()}</div>
|
|
97
|
+
<div className="pmeta"><div className="pname" style={{ fontWeight: 600, color: "var(--ink-3)" }}>新建项目</div></div>
|
|
98
|
+
</button>
|
|
99
|
+
)}
|
|
100
|
+
|
|
101
|
+
{!q && deprecated.length > 0 && (
|
|
102
|
+
<div className="dep-area">
|
|
103
|
+
<button className="dep-head" onClick={() => setShowDeprecated((s) => !s)} title="你暂时搁置的项目。恢复后可以继续在其上开发。">
|
|
104
|
+
<span className={`dep-chev ${showDeprecated ? "open" : ""}`}>{Icon.chevRight()}</span>
|
|
105
|
+
{Icon.archive()}
|
|
106
|
+
<span className="dep-title">已归档</span>
|
|
107
|
+
<span className="dep-count">{deprecated.length}</span>
|
|
108
|
+
</button>
|
|
109
|
+
{showDeprecated && deprecated.map((p) => (
|
|
110
|
+
<div key={p.id} className="proj dep" title={`${p.name} — 已归档。恢复后可继续开发。`}>
|
|
111
|
+
<div className="pic" style={{ background: p.color }}>{p.name[0]}</div>
|
|
112
|
+
<div className="pmeta">
|
|
113
|
+
<div className="pname">{p.name}</div>
|
|
114
|
+
<div className="psub">{p.task_count} 个任务{p.sub ? ` · ${p.sub}` : ""}</div>
|
|
115
|
+
</div>
|
|
116
|
+
<button className="icon-btn" title={`恢复 ${p.name}`} onClick={() => onRestore(p.id)}>{Icon.restore()}</button>
|
|
117
|
+
</div>
|
|
118
|
+
))}
|
|
119
|
+
</div>
|
|
120
|
+
)}
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
<div className="proj-foot">
|
|
124
|
+
<button className={`nav-item${settingsActive ? " active" : ""}`} onClick={onOpenSettings} title="应用设置">
|
|
125
|
+
{Icon.gear()} 设置
|
|
126
|
+
</button>
|
|
127
|
+
<div className="user-chip">
|
|
128
|
+
<div className="av">{Icon.bolt()}</div>
|
|
129
|
+
<div style={{ flex: 1, minWidth: 0 }}>
|
|
130
|
+
<div className="un">你的工作区</div>
|
|
131
|
+
<div className="ue">{agentAuthLine(agents)}</div>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
);
|
|
137
|
+
}
|