@zhin.js/client 2.1.8 → 2.1.10
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/client/index.ts +19 -2
- package/client/mediaSrc.test.ts +15 -0
- package/client/mediaSrc.ts +59 -12
- package/client/message-content/MarkdownContent.tsx +128 -0
- package/client/message-content/markdown.test.ts +48 -0
- package/client/message-content/markdown.ts +247 -0
- package/client/workroom/WorkroomRunsPage.tsx +248 -0
- package/client/{orchestration/registerOrchestrationConsole.test.ts → workroom/registerWorkroomConsole.test.ts} +8 -8
- package/client/workroom/registerWorkroomConsole.ts +17 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/mediaSrc.d.ts.map +1 -1
- package/dist/mediaSrc.js +66 -12
- package/dist/mediaSrc.js.map +1 -1
- package/dist/message-content/MarkdownContent.d.ts +11 -0
- package/dist/message-content/MarkdownContent.d.ts.map +1 -0
- package/dist/message-content/MarkdownContent.js +68 -0
- package/dist/message-content/MarkdownContent.js.map +1 -0
- package/dist/message-content/markdown.d.ts +47 -0
- package/dist/message-content/markdown.d.ts.map +1 -0
- package/dist/message-content/markdown.js +219 -0
- package/dist/message-content/markdown.js.map +1 -0
- package/dist/workroom/WorkroomRunsPage.d.ts +2 -0
- package/dist/workroom/WorkroomRunsPage.d.ts.map +1 -0
- package/dist/workroom/WorkroomRunsPage.js +64 -0
- package/dist/workroom/WorkroomRunsPage.js.map +1 -0
- package/dist/workroom/registerWorkroomConsole.d.ts +4 -0
- package/dist/workroom/registerWorkroomConsole.d.ts.map +1 -0
- package/dist/workroom/registerWorkroomConsole.js +16 -0
- package/dist/workroom/registerWorkroomConsole.js.map +1 -0
- package/package.json +3 -3
- package/client/orchestration/OrchestrationRunsPage.tsx +0 -217
- package/client/orchestration/registerOrchestrationConsole.ts +0 -17
- package/dist/orchestration/OrchestrationRunsPage.d.ts +0 -2
- package/dist/orchestration/OrchestrationRunsPage.d.ts.map +0 -1
- package/dist/orchestration/OrchestrationRunsPage.js +0 -65
- package/dist/orchestration/OrchestrationRunsPage.js.map +0 -1
- package/dist/orchestration/registerOrchestrationConsole.d.ts +0 -4
- package/dist/orchestration/registerOrchestrationConsole.d.ts.map +0 -1
- package/dist/orchestration/registerOrchestrationConsole.js +0 -16
- package/dist/orchestration/registerOrchestrationConsole.js.map +0 -1
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
import { apiFetch } from '../console-utils/remoteApi.js';
|
|
3
|
+
|
|
4
|
+
type WorkroomTask = {
|
|
5
|
+
key: string;
|
|
6
|
+
title: string;
|
|
7
|
+
status: string;
|
|
8
|
+
revision: number;
|
|
9
|
+
attempt: number;
|
|
10
|
+
maxAttempts: number;
|
|
11
|
+
reportRef?: string;
|
|
12
|
+
terminalReason?: string;
|
|
13
|
+
currentAssignmentId?: string;
|
|
14
|
+
blockers?: Array<{ id: string; kind: string; owner: string; reason: string; deadline: number }>;
|
|
15
|
+
latestProgress?: { summary: string; completedUnits?: number; totalUnits?: number };
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type WorkroomAssignment = {
|
|
19
|
+
id: string;
|
|
20
|
+
taskKey: string;
|
|
21
|
+
role: string;
|
|
22
|
+
status: string;
|
|
23
|
+
owner: string;
|
|
24
|
+
attempt: number;
|
|
25
|
+
leaseExpiresAt: number;
|
|
26
|
+
latestProgress?: { summary: string; completedUnits?: number; totalUnits?: number };
|
|
27
|
+
outcome?: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type WorkroomWait = {
|
|
31
|
+
id: string;
|
|
32
|
+
taskKey: string;
|
|
33
|
+
status: string;
|
|
34
|
+
owner: string;
|
|
35
|
+
route: string;
|
|
36
|
+
riskTier: string;
|
|
37
|
+
deadline: number;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
type RunListItem = {
|
|
41
|
+
runId: string;
|
|
42
|
+
projectId: string;
|
|
43
|
+
status: string;
|
|
44
|
+
title: string;
|
|
45
|
+
sequence: number;
|
|
46
|
+
now: number;
|
|
47
|
+
cancelRequested: boolean;
|
|
48
|
+
tasks: Record<string, WorkroomTask>;
|
|
49
|
+
assignments: Record<string, WorkroomAssignment>;
|
|
50
|
+
reviewerAssignments: Record<string, WorkroomWait>;
|
|
51
|
+
sponsorGates: Record<string, WorkroomWait>;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
type RunDetail = RunListItem;
|
|
55
|
+
|
|
56
|
+
export default function WorkroomRunsPage() {
|
|
57
|
+
const [projectId, setProjectId] = useState('');
|
|
58
|
+
const [runs, setRuns] = useState<RunListItem[]>([]);
|
|
59
|
+
const [selected, setSelected] = useState<RunDetail | null>(null);
|
|
60
|
+
const [loading, setLoading] = useState(false);
|
|
61
|
+
const [error, setError] = useState('');
|
|
62
|
+
const [detailView, setDetailView] = useState<'tasks' | 'assignments' | 'acceptance'>('tasks');
|
|
63
|
+
|
|
64
|
+
const loadRuns = useCallback(async () => {
|
|
65
|
+
if (!projectId.trim()) {
|
|
66
|
+
setError('请输入 projectId');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
setLoading(true);
|
|
70
|
+
setError('');
|
|
71
|
+
setSelected(null);
|
|
72
|
+
try {
|
|
73
|
+
const res = await apiFetch(
|
|
74
|
+
`/api/agent/workroom/runs?projectId=${encodeURIComponent(projectId.trim())}`,
|
|
75
|
+
);
|
|
76
|
+
const body = await res.json() as { success?: boolean; error?: string; data?: { runs?: RunListItem[] } };
|
|
77
|
+
if (!res.ok || !body.success) {
|
|
78
|
+
throw new Error(body.error ?? `HTTP ${res.status}`);
|
|
79
|
+
}
|
|
80
|
+
setRuns(body.data?.runs ?? []);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
83
|
+
setRuns([]);
|
|
84
|
+
} finally {
|
|
85
|
+
setLoading(false);
|
|
86
|
+
}
|
|
87
|
+
}, [projectId]);
|
|
88
|
+
|
|
89
|
+
const loadRunDetail = useCallback(async (runId: string, runProjectId: string) => {
|
|
90
|
+
setLoading(true);
|
|
91
|
+
setError('');
|
|
92
|
+
try {
|
|
93
|
+
const res = await apiFetch(`/api/agent/workroom/runs/${encodeURIComponent(runId)}?projectId=${encodeURIComponent(runProjectId)}`);
|
|
94
|
+
const body = await res.json() as { success?: boolean; error?: string; data?: RunDetail };
|
|
95
|
+
if (!res.ok || !body.success || !body.data) {
|
|
96
|
+
throw new Error(body.error ?? `HTTP ${res.status}`);
|
|
97
|
+
}
|
|
98
|
+
setSelected(body.data);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
101
|
+
} finally {
|
|
102
|
+
setLoading(false);
|
|
103
|
+
}
|
|
104
|
+
}, []);
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<div className="p-4 md:p-6 space-y-5 max-w-7xl mx-auto">
|
|
108
|
+
<header className="flex flex-wrap items-end justify-between gap-3 border-b pb-4">
|
|
109
|
+
<div>
|
|
110
|
+
<span className="text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground">Project control plane</span>
|
|
111
|
+
<h1 className="mt-1 text-xl font-semibold tracking-tight">Workroom 控制台</h1>
|
|
112
|
+
<p className="mt-1 max-w-3xl text-sm text-muted-foreground">查看 Journal + CAS Kernel 投影出的 Run、Task、Assignment 与验收门;普通聊天不会隐式创建 Workroom。</p>
|
|
113
|
+
</div>
|
|
114
|
+
<span className="rounded-full border px-2.5 py-1 text-xs text-muted-foreground">只读事实投影</span>
|
|
115
|
+
</header>
|
|
116
|
+
|
|
117
|
+
<details className="rounded-xl border bg-muted/20 open:bg-muted/10">
|
|
118
|
+
<summary className="cursor-pointer list-none px-4 py-3 text-sm font-medium">运行与持久化配置 <span className="ml-2 text-xs font-normal text-muted-foreground">ai.sessions.useDatabase · 修改后需重启</span></summary>
|
|
119
|
+
<div className="grid gap-4 border-t px-4 py-4 md:grid-cols-[1fr_1fr]">
|
|
120
|
+
<div className="space-y-2 text-sm">
|
|
121
|
+
<p className="font-medium">数据库 Journal(默认)</p>
|
|
122
|
+
<p className="text-muted-foreground">`ai.sessions.useDatabase` 不为 false 时写入 `workroom_events`;Database Root Host 未就绪会阻止 candidate generation 发布。</p>
|
|
123
|
+
<pre className="overflow-auto rounded-lg border bg-background p-3 text-xs">{'ai:\n sessions:\n useDatabase: true'}</pre>
|
|
124
|
+
</div>
|
|
125
|
+
<div className="space-y-2 text-sm">
|
|
126
|
+
<p className="font-medium">原子文件 Journal</p>
|
|
127
|
+
<p className="text-muted-foreground">显式设为 false 后使用 `.zhin/workroom-journal`。后端在进程启动时固定,热重载不会切换事实源。</p>
|
|
128
|
+
<pre className="overflow-auto rounded-lg border bg-background p-3 text-xs">{'ai:\n sessions:\n useDatabase: false'}</pre>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
<div className="flex items-center justify-between gap-3 border-t px-4 py-3 text-xs text-muted-foreground"><span>配置项属于 Agent session storage;切换 Journal 后端必须重启 Host,不能只做热重载。</span><a className="rounded-md border bg-background px-2.5 py-1.5 font-medium text-foreground hover:bg-muted" href="/config">打开配置编辑器</a></div>
|
|
132
|
+
</details>
|
|
133
|
+
|
|
134
|
+
<div className="flex flex-wrap gap-2 items-end">
|
|
135
|
+
<label className="flex flex-col gap-1 text-sm flex-1 min-w-[240px]">
|
|
136
|
+
projectId
|
|
137
|
+
<input
|
|
138
|
+
className="border rounded px-2 py-1 text-sm bg-background"
|
|
139
|
+
value={projectId}
|
|
140
|
+
onChange={(e) => setProjectId(e.target.value)}
|
|
141
|
+
placeholder="project-alpha"
|
|
142
|
+
/>
|
|
143
|
+
</label>
|
|
144
|
+
<button
|
|
145
|
+
type="button"
|
|
146
|
+
className="border rounded px-3 py-1 text-sm hover:bg-muted"
|
|
147
|
+
disabled={loading}
|
|
148
|
+
onClick={() => void loadRuns()}
|
|
149
|
+
>
|
|
150
|
+
查询 Runs
|
|
151
|
+
</button>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
{error ? <p className="text-sm text-red-600">{error}</p> : null}
|
|
155
|
+
|
|
156
|
+
<div className="grid gap-4 lg:grid-cols-[minmax(16rem,0.75fr)_minmax(0,1.5fr)]">
|
|
157
|
+
<section className="rounded-xl border p-3 space-y-2">
|
|
158
|
+
<h2 className="text-sm font-medium">Runs ({runs.length})</h2>
|
|
159
|
+
{runs.length === 0 ? (
|
|
160
|
+
<p className="text-sm text-muted-foreground">无数据</p>
|
|
161
|
+
) : (
|
|
162
|
+
<ul className="space-y-2 text-sm">
|
|
163
|
+
{runs.map((item) => (
|
|
164
|
+
<li key={item.runId} className={`rounded-lg border p-2 transition-colors ${selected?.runId === item.runId ? 'border-primary/40 bg-primary/5' : 'hover:bg-muted/30'}`}>
|
|
165
|
+
<button
|
|
166
|
+
type="button"
|
|
167
|
+
className="text-left w-full"
|
|
168
|
+
onClick={() => void loadRunDetail(item.runId, item.projectId)}
|
|
169
|
+
>
|
|
170
|
+
<div className="font-mono text-xs">{item.runId}</div>
|
|
171
|
+
<div className="mt-1 font-medium">{item.title || '(无标题)'}</div>
|
|
172
|
+
<div className="text-muted-foreground">
|
|
173
|
+
{item.status}
|
|
174
|
+
{' · '}
|
|
175
|
+
{Object.keys(item.tasks).length}
|
|
176
|
+
{' tasks'}
|
|
177
|
+
</div>
|
|
178
|
+
</button>
|
|
179
|
+
</li>
|
|
180
|
+
))}
|
|
181
|
+
</ul>
|
|
182
|
+
)}
|
|
183
|
+
</section>
|
|
184
|
+
|
|
185
|
+
<section className="rounded-xl border p-3 space-y-3">
|
|
186
|
+
<h2 className="text-sm font-medium">Run 详情</h2>
|
|
187
|
+
{!selected ? (
|
|
188
|
+
<p className="text-sm text-muted-foreground">选择左侧 Run</p>
|
|
189
|
+
) : (
|
|
190
|
+
<>
|
|
191
|
+
<div className="flex flex-wrap items-start justify-between gap-3 rounded-lg bg-muted/25 p-3 text-sm">
|
|
192
|
+
<div><div className="font-medium">{selected.title}</div><code className="text-xs text-muted-foreground">{selected.runId}</code></div>
|
|
193
|
+
<span className={`rounded-full border px-2 py-0.5 text-xs ${statusTone(selected.status)}`}>{selected.status}</span>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<dl className="grid grid-cols-2 overflow-hidden rounded-lg border text-sm sm:grid-cols-4">
|
|
197
|
+
<div className="p-2.5"><dt className="text-xs text-muted-foreground">Sequence</dt><dd className="mt-0.5 font-medium tabular-nums">{selected.sequence}</dd></div>
|
|
198
|
+
<div className="border-l p-2.5"><dt className="text-xs text-muted-foreground">Tasks</dt><dd className="mt-0.5 font-medium tabular-nums">{Object.keys(selected.tasks).length}</dd></div>
|
|
199
|
+
<div className="border-t p-2.5 sm:border-l sm:border-t-0"><dt className="text-xs text-muted-foreground">Assignments</dt><dd className="mt-0.5 font-medium tabular-nums">{Object.keys(selected.assignments ?? {}).length}</dd></div>
|
|
200
|
+
<div className="border-l border-t p-2.5 sm:border-t-0"><dt className="text-xs text-muted-foreground">Acceptance waits</dt><dd className="mt-0.5 font-medium tabular-nums">{Object.keys(selected.reviewerAssignments ?? {}).length + Object.keys(selected.sponsorGates ?? {}).length}</dd></div>
|
|
201
|
+
</dl>
|
|
202
|
+
|
|
203
|
+
<div className="flex gap-1 rounded-lg bg-muted/35 p-1" role="tablist" aria-label="Workroom 详情视图">
|
|
204
|
+
{(['tasks', 'assignments', 'acceptance'] as const).map((view) => <button key={view} type="button" role="tab" aria-selected={detailView === view} className={`flex-1 rounded-md px-2 py-1.5 text-xs font-medium ${detailView === view ? 'bg-background shadow-sm' : 'text-muted-foreground hover:text-foreground'}`} onClick={() => setDetailView(view)}>{view === 'tasks' ? '任务' : view === 'assignments' ? '执行分配' : '验收门'}</button>)}
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
{detailView === 'tasks' && <ul className="space-y-2 text-sm">
|
|
208
|
+
{Object.values(selected.tasks).map((task) => (
|
|
209
|
+
<li key={task.key} className="rounded-lg border p-3">
|
|
210
|
+
<div className="flex items-start justify-between gap-2"><div><code className="text-xs text-muted-foreground">{task.key}</code><div className="font-medium">{task.title}</div></div><span className={`rounded-full border px-2 py-0.5 text-[11px] ${statusTone(task.status)}`}>{task.status}</span></div>
|
|
211
|
+
<div className="mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground"><span>rev {task.revision}</span><span>attempt {task.attempt}/{task.maxAttempts}</span>{task.currentAssignmentId ? <span>assignment {task.currentAssignmentId}</span> : null}</div>
|
|
212
|
+
{task.blockers?.map((blocker) => <div key={blocker.id} className="mt-2 rounded-md border border-amber-500/25 bg-amber-500/5 p-2 text-xs"><strong>{blocker.kind}</strong> · {blocker.reason}<div className="text-muted-foreground">owner {blocker.owner}</div></div>)}
|
|
213
|
+
{task.reportRef ? <div className="mt-2 break-words rounded bg-muted/35 p-2 font-mono text-xs">report: {task.reportRef}</div> : null}
|
|
214
|
+
{task.terminalReason ? <div className="mt-2 whitespace-pre-wrap text-xs text-red-600">{task.terminalReason}</div> : null}
|
|
215
|
+
</li>
|
|
216
|
+
))}
|
|
217
|
+
</ul>}
|
|
218
|
+
|
|
219
|
+
{detailView === 'assignments' && <ul className="space-y-2 text-sm">
|
|
220
|
+
{Object.values(selected.assignments ?? {}).length === 0 ? <li className="py-8 text-center text-muted-foreground">暂无 Assignment</li> : Object.values(selected.assignments ?? {}).map((assignment) => (
|
|
221
|
+
<li key={assignment.id} className="rounded-lg border p-3">
|
|
222
|
+
<div className="flex items-start justify-between gap-2"><div><code className="text-xs text-muted-foreground">{assignment.id}</code><div className="font-medium">{assignment.taskKey} · {assignment.role}</div></div><span className={`rounded-full border px-2 py-0.5 text-[11px] ${statusTone(assignment.status)}`}>{assignment.status}</span></div>
|
|
223
|
+
<div className="mt-2 grid gap-1 text-xs text-muted-foreground sm:grid-cols-3"><span>owner {assignment.owner}</span><span>attempt {assignment.attempt}</span><span>lease {new Date(assignment.leaseExpiresAt).toLocaleString()}</span></div>
|
|
224
|
+
{assignment.latestProgress ? <div className="mt-2 rounded-md bg-muted/35 p-2 text-xs"><strong>Progress</strong> · {assignment.latestProgress.summary}{assignment.latestProgress.totalUnits ? <div className="mt-1 h-1.5 overflow-hidden rounded-full bg-muted"><i className="block h-full bg-primary" style={{ width: `${Math.min(100, Math.max(0, ((assignment.latestProgress.completedUnits ?? 0) / assignment.latestProgress.totalUnits) * 100))}%` }} /></div> : null}</div> : null}
|
|
225
|
+
</li>
|
|
226
|
+
))}
|
|
227
|
+
</ul>}
|
|
228
|
+
|
|
229
|
+
{detailView === 'acceptance' && <ul className="space-y-2 text-sm">
|
|
230
|
+
{[...Object.values(selected.reviewerAssignments ?? {}), ...Object.values(selected.sponsorGates ?? {})].length === 0 ? <li className="py-8 text-center text-muted-foreground">当前没有 Reviewer / Sponsor 等待项</li> : [...Object.values(selected.reviewerAssignments ?? {}), ...Object.values(selected.sponsorGates ?? {})].map((wait) => (
|
|
231
|
+
<li key={wait.id} className="rounded-lg border p-3"><div className="flex items-center justify-between gap-2"><div><div className="font-medium">{wait.taskKey} · {wait.route}</div><code className="text-xs text-muted-foreground">{wait.id}</code></div><span className={`rounded-full border px-2 py-0.5 text-[11px] ${statusTone(wait.status)}`}>{wait.status}</span></div><div className="mt-2 flex flex-wrap gap-3 text-xs text-muted-foreground"><span>risk {wait.riskTier}</span><span>owner {wait.owner}</span><span>deadline {new Date(wait.deadline).toLocaleString()}</span></div></li>
|
|
232
|
+
))}
|
|
233
|
+
</ul>}
|
|
234
|
+
|
|
235
|
+
</>
|
|
236
|
+
)}
|
|
237
|
+
</section>
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function statusTone(status: string): string {
|
|
244
|
+
if (/completed|accepted|approved|passed|execution_completed/u.test(status)) return 'border-emerald-500/25 bg-emerald-500/5 text-emerald-700 dark:text-emerald-300';
|
|
245
|
+
if (/failed|lost|rejected|expired/u.test(status)) return 'border-red-500/25 bg-red-500/5 text-red-700 dark:text-red-300';
|
|
246
|
+
if (/blocked|replan|rework|cancell/u.test(status)) return 'border-amber-500/25 bg-amber-500/5 text-amber-700 dark:text-amber-300';
|
|
247
|
+
return 'border-sky-500/25 bg-sky-500/5 text-sky-700 dark:text-sky-300';
|
|
248
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import {
|
|
2
|
+
import { registerWorkroomConsole } from './registerWorkroomConsole.js';
|
|
3
3
|
|
|
4
|
-
describe('
|
|
5
|
-
it('registers
|
|
4
|
+
describe('registerWorkroomConsole', () => {
|
|
5
|
+
it('registers Workroom route and tool', () => {
|
|
6
6
|
const addRoute = vi.fn();
|
|
7
7
|
const addTool = vi.fn();
|
|
8
8
|
const React = { createElement: vi.fn((type) => ({ type })) };
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
registerWorkroomConsole({
|
|
11
11
|
React: React as never,
|
|
12
12
|
addRoute,
|
|
13
13
|
addPage: addRoute,
|
|
@@ -15,12 +15,12 @@ describe('registerOrchestrationConsole', () => {
|
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
expect(addRoute).toHaveBeenCalledWith(expect.objectContaining({
|
|
18
|
-
path: '/console/
|
|
19
|
-
name: '
|
|
18
|
+
path: '/console/workroom',
|
|
19
|
+
name: 'Workroom',
|
|
20
20
|
}));
|
|
21
21
|
expect(addTool).toHaveBeenCalledWith(expect.objectContaining({
|
|
22
|
-
id: '
|
|
23
|
-
path: '/console/
|
|
22
|
+
id: 'workroom',
|
|
23
|
+
path: '/console/workroom',
|
|
24
24
|
}));
|
|
25
25
|
});
|
|
26
26
|
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PluginRegisterHostApi } from '@zhin.js/contract';
|
|
2
|
+
import WorkroomRunsPage from './WorkroomRunsPage.js';
|
|
3
|
+
|
|
4
|
+
/** Read-only Workroom Run/Task/Assignment projection. */
|
|
5
|
+
export function registerWorkroomConsole(api: PluginRegisterHostApi): void {
|
|
6
|
+
api.addRoute({
|
|
7
|
+
path: '/console/workroom',
|
|
8
|
+
name: 'Workroom',
|
|
9
|
+
element: api.React.createElement(WorkroomRunsPage),
|
|
10
|
+
meta: { order: 40, group: 'Agent' },
|
|
11
|
+
});
|
|
12
|
+
api.addTool({
|
|
13
|
+
id: 'workroom',
|
|
14
|
+
name: 'Workroom',
|
|
15
|
+
path: '/console/workroom',
|
|
16
|
+
});
|
|
17
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export * from "./types";
|
|
2
2
|
export { ENDPOINT_RPC, ENDPOINT_MANAGEMENT_CAPABILITIES, INBOX_RPC, SIDE_EVENT_PUSH, SIDE_EVENT_RPC, normalizeConsolePushMessage, normalizeConsolePushType, parseConsoleInboxEvent, type ConsoleInboxEvent, type ConsoleInboxEventKind, type ConsoleEndpointPhase, type ConsoleEndpointSummary, type EndpointManagementCapability, } from "@zhin.js/console-protocol";
|
|
3
3
|
export { resolveMediaSrc, pickMediaRawUrl, type MediaKind } from "./mediaSrc";
|
|
4
|
+
export { MarkdownContent, CodeBlock, type MarkdownContentProps, } from "./message-content/MarkdownContent.js";
|
|
5
|
+
export { parseMarkdown, parseMarkdownInline, highlightCodeLine, isSafeMarkdownHref, type MarkdownBlock, type MarkdownInline, type CodeToken, type CodeTokenKind, } from "./message-content/markdown.js";
|
|
4
6
|
export { segmentsForImDelivery, segmentsForAgentPanel } from "./segments.js";
|
|
5
7
|
export { app, type ConsoleApp, type AddRouteInput, type AddToolInput, type ConsoleRouteRecord, type RouteTreeNode, type ToolTreeNode, type ConsoleRouteRenderer, } from "./app";
|
|
6
8
|
export * from "./websocket";
|
|
@@ -9,7 +11,7 @@ export { createRegistryStore, useRegistry, type RegistryStore, } from "./store/c
|
|
|
9
11
|
export { cn } from "./console-utils/cn.js";
|
|
10
12
|
export { apiFetch, getApiBase, getToken, resolveApiUrl, resolveWebSocketUrl, } from "./console-utils/remoteApi.js";
|
|
11
13
|
export { fetchConsoleEntries, createPluginRegisterHostApi, getRegisterFn, loadConsoleEntries, registerConsolePluginsFromEntries, resolveEntryRegister, type CreatePluginRegisterHostApiOptions, type FetchConsoleEntriesOptions, type LoadConsoleEntriesOptions, } from "./bootstrap/loadConsoleEntries.js";
|
|
12
|
-
export {
|
|
13
|
-
export { default as
|
|
14
|
+
export { registerWorkroomConsole } from "./workroom/registerWorkroomConsole.js";
|
|
15
|
+
export { default as WorkroomRunsPage } from "./workroom/WorkroomRunsPage.js";
|
|
14
16
|
export { startAgentSession, continueAgentSession, subscribeAgentStream, iterateAgentStreamNdjson, foldAgentStreamNdjson, type AgentStreamClientOptions, type SubscribeAgentStreamOptions, } from "./agent-stream.js";
|
|
15
17
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,YAAY,EACZ,gCAAgC,EAChC,SAAS,EACT,eAAe,EACf,cAAc,EACd,2BAA2B,EAC3B,wBAAwB,EACxB,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,GAClC,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAG9E,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAG7E,OAAO,EACL,GAAG,EACH,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,oBAAoB,GAC1B,MAAM,OAAO,CAAC;AAGf,cAAc,aAAa,CAAC;AAE5B,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAErE,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,KAAK,aAAa,GACnB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,EAAE,EAAE,MAAM,uBAAuB,CAAC;AAE3C,OAAO,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,aAAa,EACb,mBAAmB,GACpB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACL,mBAAmB,EACnB,2BAA2B,EAC3B,aAAa,EACb,kBAAkB,EAClB,iCAAiC,EACjC,oBAAoB,EACpB,KAAK,kCAAkC,EACvC,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,GAC/B,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,YAAY,EACZ,gCAAgC,EAChC,SAAS,EACT,eAAe,EACf,cAAc,EACd,2BAA2B,EAC3B,wBAAwB,EACxB,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,GAClC,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAG9E,OAAO,EACL,eAAe,EACf,SAAS,EACT,KAAK,oBAAoB,GAC1B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,aAAa,GACnB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAG7E,OAAO,EACL,GAAG,EACH,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,oBAAoB,GAC1B,MAAM,OAAO,CAAC;AAGf,cAAc,aAAa,CAAC;AAE5B,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAErE,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,KAAK,aAAa,GACnB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,EAAE,EAAE,MAAM,uBAAuB,CAAC;AAE3C,OAAO,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,aAAa,EACb,mBAAmB,GACpB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACL,mBAAmB,EACnB,2BAA2B,EAC3B,aAAa,EACb,kBAAkB,EAClB,iCAAiC,EACjC,oBAAoB,EACpB,KAAK,kCAAkC,EACvC,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,GAC/B,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAE7E,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACxB,qBAAqB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,GACjC,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,9 @@ export * from "./types";
|
|
|
3
3
|
export { ENDPOINT_RPC, ENDPOINT_MANAGEMENT_CAPABILITIES, INBOX_RPC, SIDE_EVENT_PUSH, SIDE_EVENT_RPC, normalizeConsolePushMessage, normalizeConsolePushType, parseConsoleInboxEvent, } from "@zhin.js/console-protocol";
|
|
4
4
|
// Media URL resolution
|
|
5
5
|
export { resolveMediaSrc, pickMediaRawUrl } from "./mediaSrc";
|
|
6
|
+
// Safe rich message rendering (Markdown + code blocks; no raw HTML execution)
|
|
7
|
+
export { MarkdownContent, CodeBlock, } from "./message-content/MarkdownContent.js";
|
|
8
|
+
export { parseMarkdown, parseMarkdownInline, highlightCodeLine, isSafeMarkdownHref, } from "./message-content/markdown.js";
|
|
6
9
|
// Segment IM visibility (inbox vs agent panel)
|
|
7
10
|
export { segmentsForImDelivery, segmentsForAgentPanel } from "./segments.js";
|
|
8
11
|
// Console app singleton (pagemanager / registry style)
|
|
@@ -14,7 +17,7 @@ export { createRegistryStore, useRegistry, } from "./store/createRegistryStore.j
|
|
|
14
17
|
export { cn } from "./console-utils/cn.js";
|
|
15
18
|
export { apiFetch, getApiBase, getToken, resolveApiUrl, resolveWebSocketUrl, } from "./console-utils/remoteApi.js";
|
|
16
19
|
export { fetchConsoleEntries, createPluginRegisterHostApi, getRegisterFn, loadConsoleEntries, registerConsolePluginsFromEntries, resolveEntryRegister, } from "./bootstrap/loadConsoleEntries.js";
|
|
17
|
-
export {
|
|
18
|
-
export { default as
|
|
20
|
+
export { registerWorkroomConsole } from "./workroom/registerWorkroomConsole.js";
|
|
21
|
+
export { default as WorkroomRunsPage } from "./workroom/WorkroomRunsPage.js";
|
|
19
22
|
export { startAgentSession, continueAgentSession, subscribeAgentStream, iterateAgentStreamNdjson, foldAgentStreamNdjson, } from "./agent-stream.js";
|
|
20
23
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,YAAY,EACZ,gCAAgC,EAChC,SAAS,EACT,eAAe,EACf,cAAc,EACd,2BAA2B,EAC3B,wBAAwB,EACxB,sBAAsB,GAMvB,MAAM,2BAA2B,CAAC;AAEnC,uBAAuB;AACvB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAkB,MAAM,YAAY,CAAC;AAE9E,+CAA+C;AAC/C,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAE7E,uDAAuD;AACvD,OAAO,EACL,GAAG,GAQJ,MAAM,OAAO,CAAC;AAEf,iCAAiC;AACjC,cAAc,aAAa,CAAC;AAE5B,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAErE,OAAO,EACL,mBAAmB,EACnB,WAAW,GAEZ,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,EAAE,EAAE,MAAM,uBAAuB,CAAC;AAE3C,OAAO,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,aAAa,EACb,mBAAmB,GACpB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACL,mBAAmB,EACnB,2BAA2B,EAC3B,aAAa,EACb,kBAAkB,EAClB,iCAAiC,EACjC,oBAAoB,GAIrB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,YAAY,EACZ,gCAAgC,EAChC,SAAS,EACT,eAAe,EACf,cAAc,EACd,2BAA2B,EAC3B,wBAAwB,EACxB,sBAAsB,GAMvB,MAAM,2BAA2B,CAAC;AAEnC,uBAAuB;AACvB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAkB,MAAM,YAAY,CAAC;AAE9E,8EAA8E;AAC9E,OAAO,EACL,eAAe,EACf,SAAS,GAEV,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,GAKnB,MAAM,+BAA+B,CAAC;AAEvC,+CAA+C;AAC/C,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAE7E,uDAAuD;AACvD,OAAO,EACL,GAAG,GAQJ,MAAM,OAAO,CAAC;AAEf,iCAAiC;AACjC,cAAc,aAAa,CAAC;AAE5B,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAErE,OAAO,EACL,mBAAmB,EACnB,WAAW,GAEZ,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,EAAE,EAAE,MAAM,uBAAuB,CAAC;AAE3C,OAAO,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,aAAa,EACb,mBAAmB,GACpB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACL,mBAAmB,EACnB,2BAA2B,EAC3B,aAAa,EACb,kBAAkB,EAClB,iCAAiC,EACjC,oBAAoB,GAIrB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAE7E,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACxB,qBAAqB,GAGtB,MAAM,mBAAmB,CAAC"}
|
package/dist/mediaSrc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mediaSrc.d.ts","sourceRoot":"","sources":["../client/mediaSrc.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"mediaSrc.d.ts","sourceRoot":"","sources":["../client/mediaSrc.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAA;AAsDnD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAC9B,IAAI,GAAE,SAAmB,GACxB,MAAM,GAAG,SAAS,CA4BpB;AAED,oDAAoD;AACpD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAwB7F"}
|
package/dist/mediaSrc.js
CHANGED
|
@@ -1,7 +1,57 @@
|
|
|
1
1
|
const BASE64_PROTO = 'base64://';
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
const SAFE_DATA_MIME = Object.freeze({
|
|
3
|
+
image: new Set(['image/avif', 'image/gif', 'image/jpeg', 'image/png', 'image/webp']),
|
|
4
|
+
video: new Set(['video/mp4', 'video/ogg', 'video/webm']),
|
|
5
|
+
audio: new Set(['audio/mpeg', 'audio/mp4', 'audio/ogg', 'audio/wav', 'audio/webm']),
|
|
6
|
+
});
|
|
7
|
+
function resolveSafeRemoteUrl(value) {
|
|
8
|
+
try {
|
|
9
|
+
const parsed = new URL(value);
|
|
10
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:' && parsed.protocol !== 'blob:') {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
if (parsed.username || parsed.password)
|
|
14
|
+
return undefined;
|
|
15
|
+
return parsed.href;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function resolveSafeDataUrl(value, kind) {
|
|
22
|
+
if (!value.startsWith('data:'))
|
|
23
|
+
return undefined;
|
|
24
|
+
const separator = value.indexOf(',');
|
|
25
|
+
if (separator < 0)
|
|
26
|
+
return undefined;
|
|
27
|
+
const header = value.slice('data:'.length, separator).toLowerCase();
|
|
28
|
+
if (!header.endsWith(';base64'))
|
|
29
|
+
return undefined;
|
|
30
|
+
const mime = header.slice(0, -';base64'.length);
|
|
31
|
+
if (!SAFE_DATA_MIME[kind].has(mime))
|
|
32
|
+
return undefined;
|
|
33
|
+
const payload = compactBase64(value.slice(separator + 1));
|
|
34
|
+
return payload ? `data:${mime};base64,${payload}` : undefined;
|
|
35
|
+
}
|
|
36
|
+
function compactBase64(value) {
|
|
37
|
+
let compact = '';
|
|
38
|
+
for (const character of value) {
|
|
39
|
+
if (character === ' ' || character === '\n' || character === '\r' || character === '\t')
|
|
40
|
+
continue;
|
|
41
|
+
const code = character.charCodeAt(0);
|
|
42
|
+
const allowed = (code >= 48 && code <= 57)
|
|
43
|
+
|| (code >= 65 && code <= 90)
|
|
44
|
+
|| (code >= 97 && code <= 122)
|
|
45
|
+
|| character === '+' || character === '/' || character === '=';
|
|
46
|
+
if (!allowed)
|
|
47
|
+
return undefined;
|
|
48
|
+
compact += character;
|
|
49
|
+
}
|
|
50
|
+
const padding = compact.indexOf('=');
|
|
51
|
+
if (padding >= 0 && (compact.length - padding > 2 || compact.slice(padding).replace(/=/g, '') !== '')) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
return compact || undefined;
|
|
5
55
|
}
|
|
6
56
|
/**
|
|
7
57
|
* 将消息段中的 url/file 等转为浏览器可用的媒体地址。
|
|
@@ -19,22 +69,26 @@ export function resolveMediaSrc(raw, kind = 'image') {
|
|
|
19
69
|
const s = raw.trim();
|
|
20
70
|
if (!s)
|
|
21
71
|
return undefined;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
72
|
+
const dataUrl = resolveSafeDataUrl(s, kind);
|
|
73
|
+
if (dataUrl)
|
|
74
|
+
return dataUrl;
|
|
75
|
+
const remoteUrl = resolveSafeRemoteUrl(s);
|
|
76
|
+
if (remoteUrl)
|
|
77
|
+
return remoteUrl;
|
|
25
78
|
if (!s.startsWith(BASE64_PROTO)) {
|
|
26
|
-
return
|
|
79
|
+
return undefined;
|
|
27
80
|
}
|
|
28
|
-
const payload = s.slice(BASE64_PROTO.length).
|
|
81
|
+
const payload = s.slice(BASE64_PROTO.length).trimStart();
|
|
29
82
|
if (!payload)
|
|
30
83
|
return undefined;
|
|
31
84
|
// 已是 type/subtype;base64, 片段(不含 data: 前缀)
|
|
32
|
-
|
|
33
|
-
|
|
85
|
+
const declared = resolveSafeDataUrl(`data:${payload}`, kind);
|
|
86
|
+
if (declared) {
|
|
87
|
+
return declared;
|
|
34
88
|
}
|
|
35
89
|
const defaultMime = kind === 'image' ? 'image/png' : kind === 'video' ? 'video/mp4' : 'audio/mpeg';
|
|
36
|
-
const b64 = payload
|
|
37
|
-
return `data:${defaultMime};base64,${b64}
|
|
90
|
+
const b64 = compactBase64(payload);
|
|
91
|
+
return b64 ? `data:${defaultMime};base64,${b64}` : undefined;
|
|
38
92
|
}
|
|
39
93
|
/** 从 segment.data 取常见媒体字段(优先 canonical MediaRef) */
|
|
40
94
|
export function pickMediaRawUrl(data) {
|
package/dist/mediaSrc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mediaSrc.js","sourceRoot":"","sources":["../client/mediaSrc.ts"],"names":[],"mappings":"AAGA,MAAM,YAAY,GAAG,WAAW,CAAA;AAEhC,MAAM,
|
|
1
|
+
{"version":3,"file":"mediaSrc.js","sourceRoot":"","sources":["../client/mediaSrc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface MarkdownContentProps {
|
|
2
|
+
readonly text: string;
|
|
3
|
+
readonly className?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function CodeBlock({ code, language, closed }: {
|
|
6
|
+
readonly code: string;
|
|
7
|
+
readonly language?: string;
|
|
8
|
+
readonly closed?: boolean;
|
|
9
|
+
}): import("react").JSX.Element;
|
|
10
|
+
export declare function MarkdownContent({ text, className }: MarkdownContentProps): import("react").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=MarkdownContent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MarkdownContent.d.ts","sourceRoot":"","sources":["../../client/message-content/MarkdownContent.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAwBD,wBAAgB,SAAS,CAAC,EAAE,IAAI,EAAE,QAAa,EAAE,MAAa,EAAE,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,+BAkDjJ;AAED,wBAAgB,eAAe,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,oBAAoB,+BAyCxE"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, useMemo, useState } from 'react';
|
|
3
|
+
import { highlightCodeLine, parseMarkdown, } from './markdown.js';
|
|
4
|
+
function InlineContent({ content }) {
|
|
5
|
+
const nodes = [];
|
|
6
|
+
content.forEach((token, index) => {
|
|
7
|
+
const key = `${token.type}-${index}`;
|
|
8
|
+
const value = token.value.split('\n').map((line, lineIndex, lines) => (_jsxs(Fragment, { children: [line, lineIndex < lines.length - 1 ? _jsx("br", {}) : null] }, `${key}-line-${lineIndex}`)));
|
|
9
|
+
if (token.type === 'strong')
|
|
10
|
+
nodes.push(_jsx("strong", { children: value }, key));
|
|
11
|
+
else if (token.type === 'emphasis')
|
|
12
|
+
nodes.push(_jsx("em", { children: value }, key));
|
|
13
|
+
else if (token.type === 'strike')
|
|
14
|
+
nodes.push(_jsx("del", { children: value }, key));
|
|
15
|
+
else if (token.type === 'code')
|
|
16
|
+
nodes.push(_jsx("code", { className: "im-inline-code", children: token.value }, key));
|
|
17
|
+
else if (token.type === 'link')
|
|
18
|
+
nodes.push(_jsx("a", { href: token.href, target: token.href.startsWith('#') ? undefined : '_blank', rel: "noreferrer", className: "im-message-link", children: value }, key));
|
|
19
|
+
else
|
|
20
|
+
nodes.push(_jsx(Fragment, { children: value }, key));
|
|
21
|
+
});
|
|
22
|
+
return _jsx(_Fragment, { children: nodes });
|
|
23
|
+
}
|
|
24
|
+
export function CodeBlock({ code, language = '', closed = true }) {
|
|
25
|
+
const [copied, setCopied] = useState(false);
|
|
26
|
+
const [wrapped, setWrapped] = useState(false);
|
|
27
|
+
const [expanded, setExpanded] = useState(false);
|
|
28
|
+
const lines = useMemo(() => code.split('\n'), [code]);
|
|
29
|
+
const isLong = lines.length > 18 || code.length > 1800;
|
|
30
|
+
const visibleLines = isLong && !expanded ? lines.slice(0, 18) : lines;
|
|
31
|
+
const copy = async () => {
|
|
32
|
+
try {
|
|
33
|
+
await navigator.clipboard.writeText(code);
|
|
34
|
+
setCopied(true);
|
|
35
|
+
window.setTimeout(() => setCopied(false), 1600);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
setCopied(false);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
return (_jsxs("figure", { className: "zhin-code-block", children: [_jsxs("figcaption", { className: "zhin-code-toolbar", children: [_jsx("span", { className: "zhin-code-language", children: language || 'plain text' }), !closed ? _jsx("span", { className: "zhin-code-streaming", children: "\u751F\u6210\u4E2D" }) : null, _jsxs("span", { className: "zhin-code-actions", children: [_jsx("button", { type: "button", onClick: () => setWrapped((value) => !value), "aria-pressed": wrapped, children: wrapped ? '不折行' : '折行' }), _jsx("button", { type: "button", onClick: () => void copy(), children: copied ? '已复制' : '复制' })] })] }), _jsx("pre", { className: wrapped ? 'zhin-code-pre zhin-code-pre--wrap' : 'zhin-code-pre', children: _jsx("code", { children: visibleLines.map((line, lineIndex) => (_jsxs("span", { className: "zhin-code-line", children: [_jsx("span", { className: "zhin-code-line-number", "aria-hidden": "true", children: lineIndex + 1 }), _jsxs("span", { className: "zhin-code-line-content", children: [highlightCodeLine(line, language).map((token, tokenIndex) => (_jsx("span", { className: `zhin-code-token zhin-code-token--${token.kind}`, children: token.value }, `${token.kind}-${tokenIndex}`))), '\n'] })] }, `${lineIndex}-${line}`))) }) }), isLong ? (_jsx("button", { type: "button", className: "zhin-code-expand", onClick: () => setExpanded((value) => !value), children: expanded ? '收起代码' : `展开其余 ${lines.length - visibleLines.length} 行` })) : null] }));
|
|
42
|
+
}
|
|
43
|
+
export function MarkdownContent({ text, className }) {
|
|
44
|
+
const blocks = useMemo(() => parseMarkdown(text), [text]);
|
|
45
|
+
return (_jsx("div", { className: ['im-markdown-body', 'zhin-markdown', className].filter(Boolean).join(' '), children: blocks.map((block, index) => {
|
|
46
|
+
if (block.type === 'heading') {
|
|
47
|
+
const Heading = `h${block.level}`;
|
|
48
|
+
return _jsx(Heading, { className: `im-markdown-heading im-markdown-heading-${block.level}`, children: _jsx(InlineContent, { content: block.content }) }, index);
|
|
49
|
+
}
|
|
50
|
+
if (block.type === 'paragraph')
|
|
51
|
+
return _jsx("p", { className: "im-markdown-paragraph", children: _jsx(InlineContent, { content: block.content }) }, index);
|
|
52
|
+
if (block.type === 'quote')
|
|
53
|
+
return _jsx("blockquote", { className: "im-markdown-quote", children: _jsx(InlineContent, { content: block.content }) }, index);
|
|
54
|
+
if (block.type === 'rule')
|
|
55
|
+
return _jsx("hr", { className: "zhin-markdown-rule" }, index);
|
|
56
|
+
if (block.type === 'code')
|
|
57
|
+
return _jsx(CodeBlock, { code: block.value, language: block.language, closed: block.closed }, index);
|
|
58
|
+
if (block.type === 'list') {
|
|
59
|
+
const List = block.ordered ? 'ol' : 'ul';
|
|
60
|
+
return (_jsx(List, { className: `im-markdown-list${block.ordered ? ' im-markdown-list-ordered' : ''}`, children: block.items.map((item, itemIndex) => (_jsxs("li", { className: item.checked === undefined ? undefined : 'zhin-markdown-task', children: [item.checked === undefined ? null : _jsx("input", { type: "checkbox", checked: item.checked, readOnly: true, "aria-label": item.checked ? '已完成' : '未完成' }), _jsx(InlineContent, { content: item.content })] }, itemIndex))) }, index));
|
|
61
|
+
}
|
|
62
|
+
if (block.type === 'table') {
|
|
63
|
+
return (_jsx("div", { className: "zhin-markdown-table-wrap", children: _jsxs("table", { className: "zhin-markdown-table", children: [_jsx("thead", { children: _jsx("tr", { children: block.header.map((cell, cellIndex) => _jsx("th", { children: _jsx(InlineContent, { content: cell }) }, cellIndex)) }) }), _jsx("tbody", { children: block.rows.map((row, rowIndex) => _jsx("tr", { children: row.map((cell, cellIndex) => _jsx("td", { children: _jsx(InlineContent, { content: cell }) }, cellIndex)) }, rowIndex)) })] }) }, index));
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}) }));
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=MarkdownContent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MarkdownContent.js","sourceRoot":"","sources":["../../client/message-content/MarkdownContent.tsx"],"names":[],"mappings":"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"}
|