magic-agent 1.0.1 → 1.0.2
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-CONTRACT.md +298 -0
- package/README.md +35 -0
- package/client-ui/assets/OFL-Caveat.txt +93 -0
- package/client-ui/assets/caveat.woff2 +0 -0
- package/client-ui/index.html +246 -0
- package/client-ui/pc/app.css +5110 -0
- package/client-ui/pc/assets/damiao-mascot.png +0 -0
- package/client-ui/pc/assets/icon.png +0 -0
- package/client-ui/pc/index.html +154 -0
- package/client-ui/pc/js/00-base.js +52 -0
- package/client-ui/pc/js/03-chat-message.js +880 -0
- package/client-ui/pc/js/04-task-list.js +430 -0
- package/client-ui/pc/js/05-task-list-render.js +812 -0
- package/client-ui/pc/js/06-ask-answer.js +204 -0
- package/client-ui/pc/js/07-chat-stream-render.js +493 -0
- package/client-ui/pc/js/08-ask-event-map.js +171 -0
- package/client-ui/pc/js/09-stream-apply.js +367 -0
- package/client-ui/pc/js/10-markdown.js +430 -0
- package/client-ui/pc/js/18-askbox.js +262 -0
- package/client-ui/pc/js/19-attachments.js +679 -0
- package/client-ui/pc/js/29-msg-variants.js +462 -0
- package/client-ui/pc/js/90-glue.js +212 -0
- package/client-ui/pc/js/91-workbench-stubs.js +134 -0
- package/client-ui/pc/js/92-composer.js +406 -0
- package/client-ui/pc/js/93-rail.js +362 -0
- package/client-ui/pc/vendor/highlight.min.js +1262 -0
- package/client-ui/pc/vendor/marked.min.js +6 -0
- package/client-ui/pc/vendor/purify.min.js +3 -0
- package/client-ui/shared/conversation.css +256 -0
- package/client-ui/shared/conversation.js +1225 -0
- package/client-ui/shared/transport-http.js +123 -0
- package/client-ui-mobile/assets/OFL-Caveat.txt +93 -0
- package/client-ui-mobile/assets/caveat.woff2 +0 -0
- package/client-ui-mobile/index.html +173 -0
- package/npm/dist/darwin-arm64/magic-agent +0 -0
- package/npm/dist/darwin-x64/magic-agent +0 -0
- package/npm/dist/linux-arm64/magic-agent +0 -0
- package/npm/dist/linux-x64/magic-agent +0 -0
- package/npm/dist/win32-x64/magic-agent.exe +0 -0
- package/package.json +4 -1
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>magic-agent · 对话</title>
|
|
7
|
+
<!--
|
|
8
|
+
PC 端基础 UI —— **从 magic-test 现成的对话框复制而来**,不是重画一份。
|
|
9
|
+
|
|
10
|
+
── 为什么反过来抄 magic-test ──
|
|
11
|
+
那份对话框是长期打磨出来的:气泡 / 工具卡 / 思考块 / markdown / 文件变更 / diff
|
|
12
|
+
这些渲染细节都带着真实报障的修法(见各文件的头注释)。重新按别的设计画一遍,
|
|
13
|
+
等于把这些经验丢掉再踩一遍。所以这里先**原样搬过来**,再在此基础上找增强点。
|
|
14
|
+
|
|
15
|
+
── 目录 ──
|
|
16
|
+
app.css 整份从 magic-test/app.css 复制(5110 行,逐字未改)
|
|
17
|
+
vendor/ 第三方库:marked(markdown)/ purify(消毒)/ highlight(代码高亮,按需加载)
|
|
18
|
+
从 magic-test/vendor 复制。floating-ui 未用(弹出层定位是本仓库自写的简化版)
|
|
19
|
+
assets/ icon.png(bot 头像)+ damiao-mascot.png(输入框角落吉祥物)
|
|
20
|
+
js/00-base.js $ / $$ / escHtml / toast / htmlWrite / qaImeComposing —— 也从上游搬来
|
|
21
|
+
js/04-task-list.js 任务状态 + 引擎 / 模型 / 权限档位 + 能力探测(整份逐字复制,424 行)
|
|
22
|
+
js/91-workbench-stubs.js 工作台侧符号:纯函数原样搬,缺失能力一律如实说(增强点清单)
|
|
23
|
+
js/10-markdown.js markdown 渲染
|
|
24
|
+
js/03-chat-message.js 消息渲染(气泡 / 工具卡 / 思考块 …)
|
|
25
|
+
js/29-msg-variants.js 消息变体(气泡/标记/工具卡/代码面板/文件变更/Diff)
|
|
26
|
+
js/08-ask-event-map.js 引擎事件 → 归一化形态
|
|
27
|
+
js/09-stream-apply.js 事件→画面 的映射管线(从 09-ask-event.js 取 1–365 行)
|
|
28
|
+
js/07-chat-stream-render.js 流式渲染与事件委托(含 qaSend / qaStopActive)
|
|
29
|
+
js/06-ask-answer.js 提问卡作答与待发队列
|
|
30
|
+
js/05-task-list-render.js 会话登记与快照
|
|
31
|
+
js/19-attachments.js 附件 + +菜单 + askBoxMountAll(输入框的挂载入口)
|
|
32
|
+
js/18-askbox.js 融合输入框(含 + / 引擎 / 模型 / 权限 四只菜单的标记)
|
|
33
|
+
js/92-composer.js 撰写区交互层:四只菜单的开关与点选 + 发送主链 + 事件→画面订阅
|
|
34
|
+
js/90-glue.js 只在这一层做适配:transport、desk 桥、挂载
|
|
35
|
+
|
|
36
|
+
── 与上游的差别(只有这一处)──
|
|
37
|
+
上游这些文件直接调 `window.desk.*`(Electron IPC)。magic-agent 是 CLI,没有 IPC,
|
|
38
|
+
所以 90-glue.js 提供一层同名的适配,把 desk 的四个通道映射到 transport 上:
|
|
39
|
+
desk.ask / desk.askStop / desk.askAppend → transport.ask / control
|
|
40
|
+
desk.onAskEvent → transport.ask 的 onEvent 回调
|
|
41
|
+
其余片段一个字都没动 —— 这是这次移植的纪律:先搬,再改。
|
|
42
|
+
-->
|
|
43
|
+
<link rel="stylesheet" href="app.css">
|
|
44
|
+
<style>
|
|
45
|
+
/* 宿主自身的最小排布:把对话区铺满窗口。
|
|
46
|
+
上游这些尺寸由工作台布局给(左栏 + 主区),这里没有工作台,所以补上外层。 */
|
|
47
|
+
html, body { height: 100%; margin: 0; }
|
|
48
|
+
body { overflow: hidden; background: var(--bg-page); }
|
|
49
|
+
#app { height: 100vh; height: 100dvh; display: flex; }
|
|
50
|
+
#view-chat { flex: 1; min-width: 0; display: flex; }
|
|
51
|
+
|
|
52
|
+
/* ── 左侧会话栏(93-rail.js)─────────────────────────────────────────
|
|
53
|
+
列表 / 搜索 / 分组那几块**复用 app.css 里现成的 .tk-* 类**(与旧工作台同一套观感);
|
|
54
|
+
这里只补它没覆盖的:栏宽、栏头、状态点、行尾菜单。 */
|
|
55
|
+
.pc-rail{
|
|
56
|
+
width: 248px; flex: none; display: flex; flex-direction: column; gap: 2px;
|
|
57
|
+
padding: 12px 10px 8px; border-right: 1px solid var(--line);
|
|
58
|
+
background: var(--bg-page); overflow: hidden;
|
|
59
|
+
}
|
|
60
|
+
.rail-hd{ display: flex; align-items: center; gap: 6px; padding: 0 2px 2px; }
|
|
61
|
+
.rail-title{ flex: 1; min-width: 0; font-family: var(--serif); font-size: 12px; letter-spacing: .08em;
|
|
62
|
+
color: var(--paper-mute); }
|
|
63
|
+
.rail-acts{ display: flex; align-items: center; gap: 2px; }
|
|
64
|
+
.rail-ic{ width: 22px; height: 22px; display: grid; place-items: center; padding: 0; cursor: pointer;
|
|
65
|
+
border: 1px solid transparent; border-radius: 7px; background: transparent;
|
|
66
|
+
color: var(--paper-mute); font-size: 13px; line-height: 1; }
|
|
67
|
+
.rail-ic:hover{ color: var(--paper); background: rgba(28,25,23,.06); }
|
|
68
|
+
.rail-dot{ display: block; width: 7px; height: 7px; border-radius: 999px; background: var(--paper-mute); }
|
|
69
|
+
.rail-dot.live{ background: #2E9E6B; box-shadow: 0 0 0 3px rgba(46,158,107,.16); }
|
|
70
|
+
.rail-dot.done{ background: #C9C1B6; }
|
|
71
|
+
.rail-dot.failed{ background: #C4553C; }
|
|
72
|
+
/* 「无历史」标记:这条会话有会话 id(能续接),但引擎当时非流式跑、没留下事件日志。
|
|
73
|
+
在行上先标出来,免得点开才发现只有一句说明(`93-rail.js` 的 noLog)。 */
|
|
74
|
+
.rail-nolog{
|
|
75
|
+
flex: none; margin-left: 5px; padding: 0 4px; border-radius: 5px;
|
|
76
|
+
font-size: 10px; line-height: 15px; color: var(--paper-mute);
|
|
77
|
+
background: rgba(28,25,23,.05); border: 1px solid rgba(28,25,23,.07);
|
|
78
|
+
}
|
|
79
|
+
#rail-list{ flex: 1; min-height: 0; overflow-y: auto; }
|
|
80
|
+
.rail-menu{
|
|
81
|
+
position: fixed; z-index: 60; min-width: 168px; padding: 4px;
|
|
82
|
+
background: #FFFDFB; border: 1px solid var(--line); border-radius: 10px;
|
|
83
|
+
box-shadow: 0 10px 28px rgba(40,30,20,.14);
|
|
84
|
+
}
|
|
85
|
+
.rail-menu button{
|
|
86
|
+
display: block; width: 100%; text-align: left; padding: 7px 10px; cursor: pointer;
|
|
87
|
+
border: none; border-radius: 7px; background: transparent; color: var(--paper);
|
|
88
|
+
font: 12px/1.5 -apple-system, "PingFang SC", system-ui, sans-serif;
|
|
89
|
+
}
|
|
90
|
+
.rail-menu button:hover{ background: rgba(28,25,23,.06); }
|
|
91
|
+
</style>
|
|
92
|
+
</head>
|
|
93
|
+
<body>
|
|
94
|
+
|
|
95
|
+
<div id="app">
|
|
96
|
+
<!-- 左栏:会话清单(数据来自 magic-agent 的 --sessions / --session-log,见 93-rail.js) -->
|
|
97
|
+
<aside class="pc-rail" id="pc-rail" aria-label="会话列表">
|
|
98
|
+
<div class="rail-hd">
|
|
99
|
+
<span class="rail-title">会话</span>
|
|
100
|
+
<span class="rail-acts">
|
|
101
|
+
<button class="rail-ic" id="rail-new" type="button" title="新建对话" data-testid="rail-new">+</button>
|
|
102
|
+
<button class="rail-ic" id="rail-refresh" type="button" title="刷新列表" data-testid="rail-refresh">↻</button>
|
|
103
|
+
</span>
|
|
104
|
+
</div>
|
|
105
|
+
<div class="tk-search" id="rail-search">
|
|
106
|
+
<svg class="si" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><circle cx="10.5" cy="10.5" r="6"/><path d="M15 15l4.5 4.5"/></svg>
|
|
107
|
+
<input id="rail-search-input" type="search" placeholder="搜索会话" autocomplete="off" spellcheck="false" aria-label="搜索会话" data-testid="rail-search" />
|
|
108
|
+
<button class="sx" id="rail-search-clear" type="button" hidden aria-label="清空搜索" title="清空搜索">
|
|
109
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg>
|
|
110
|
+
</button>
|
|
111
|
+
</div>
|
|
112
|
+
<div class="tk-tabs" id="rail-tabs"></div>
|
|
113
|
+
<div class="nav-tasks" id="rail-list"></div>
|
|
114
|
+
<div class="tk-note" id="rail-note"></div>
|
|
115
|
+
</aside>
|
|
116
|
+
<section class="view" id="view-chat" data-title="任务对话">
|
|
117
|
+
<div class="chat-wrap rv rv-1" data-testid="chat-entry">
|
|
118
|
+
<div class="chat-empty" id="chat-empty">
|
|
119
|
+
<div class="ce-bubble"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><rect x="2.5" y="4" width="19" height="16" rx="3"/><path d="M6.5 9.5l2.5 2.5-2.5 2.5M11.5 15h6"/></svg></div>
|
|
120
|
+
<div class="ce-title">描述你要做的事</div>
|
|
121
|
+
<div class="ce-sub">回答会流式生成,并留存在这个任务里</div>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="qa-scroll" id="qa-scroll">
|
|
124
|
+
<div class="qa-stream" id="qa-stream" hidden></div>
|
|
125
|
+
</div>
|
|
126
|
+
<!-- 输入框 = 基础组件 askBox,标记由 askBoxMountAll 注入 -->
|
|
127
|
+
<div class="home-entry" id="ask-home"></div>
|
|
128
|
+
</div>
|
|
129
|
+
</section>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<div id="toast" role="status"><span id="toast-msg"></span></div>
|
|
133
|
+
|
|
134
|
+
<script src="vendor/marked.min.js"></script>
|
|
135
|
+
<script src="vendor/purify.min.js"></script>
|
|
136
|
+
<script src="js/00-base.js"></script>
|
|
137
|
+
<script src="js/04-task-list.js"></script>
|
|
138
|
+
<script src="js/91-workbench-stubs.js"></script>
|
|
139
|
+
<script src="js/10-markdown.js"></script>
|
|
140
|
+
<script src="js/03-chat-message.js"></script>
|
|
141
|
+
<script src="js/29-msg-variants.js"></script>
|
|
142
|
+
<script src="js/08-ask-event-map.js"></script>
|
|
143
|
+
<script src="js/09-stream-apply.js"></script>
|
|
144
|
+
<script src="js/07-chat-stream-render.js"></script>
|
|
145
|
+
<script src="js/06-ask-answer.js"></script>
|
|
146
|
+
<script src="js/05-task-list-render.js"></script>
|
|
147
|
+
<script src="js/19-attachments.js"></script>
|
|
148
|
+
<script src="js/18-askbox.js"></script>
|
|
149
|
+
<script src="../shared/transport-http.js"></script>
|
|
150
|
+
<script src="js/92-composer.js"></script>
|
|
151
|
+
<script src="js/93-rail.js"></script>
|
|
152
|
+
<script src="js/90-glue.js"></script>
|
|
153
|
+
</body>
|
|
154
|
+
</html>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/* 基础助手(从 magic-test 的 01-core.js / 15-layout.js 原样搬来)。
|
|
2
|
+
*
|
|
3
|
+
* 这一份是为了让**逐字复制过来的对话框代码**能跑起来 —— 它们按 magic-test 的全局函数风格写的
|
|
4
|
+
* (`$("#home-input")` 这类),所以这里保持同名同义,不去改那些文件里的调用点。
|
|
5
|
+
* 改动请优先改上游(magic-test 的 src/js/),再同步到这里,避免两份漂移。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/* −− 选择器 −−
|
|
9
|
+
⚠️ 注意是 **CSS 选择器**版,不是 id 版:调用点写的是 `$("#home-input")`。 */
|
|
10
|
+
const $ = (s, el = document) => el.querySelector(s);
|
|
11
|
+
const $$ = (s, el = document) => [...el.querySelectorAll(s)];
|
|
12
|
+
|
|
13
|
+
/** 幂等写 innerHTML:渲染结果字符串没变就不碰 DOM。
|
|
14
|
+
* 上游为「运行中列表每 700ms 重建一次导致闪动」而写;对话框的流式重绘也吃这条。 */
|
|
15
|
+
function htmlWrite(el, html) {
|
|
16
|
+
if (!el) return false;
|
|
17
|
+
if (el.__html === html) return false;
|
|
18
|
+
el.__html = html;
|
|
19
|
+
el.innerHTML = html;
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function escHtml(s) {
|
|
24
|
+
return String(s == null ? "" : s)
|
|
25
|
+
.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")
|
|
26
|
+
.replace(/"/g, """).replace(/'/g, "'");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** 输入法合成守卫:中文输入法里「选词那一下按的 Enter」归输入法,不是提交。
|
|
30
|
+
* 对话框的回车提交必须先过它,否则拼音打到一半敲回车会把半截消息发出去。 */
|
|
31
|
+
function qaImeComposing(e) {
|
|
32
|
+
return !!e && (e.isComposing === true || e.keyCode === 229);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let toastTimer = null;
|
|
36
|
+
function toast(msg, asHtml) {
|
|
37
|
+
const el = $("#toast");
|
|
38
|
+
if (!el) return;
|
|
39
|
+
const box = $("#toast-msg") || el;
|
|
40
|
+
if (asHtml) box.innerHTML = msg;
|
|
41
|
+
else box.textContent = msg;
|
|
42
|
+
el.classList.add("show");
|
|
43
|
+
clearTimeout(toastTimer);
|
|
44
|
+
toastTimer = setTimeout(() => el.classList.remove("show"), 2600);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* 全局状态:上游 01-core.js 的那一份里混了工作台的东西(view/tasks/cases/prds),
|
|
48
|
+
这里只保留对话框真正会读的字段;其余留空对象,避免别处读到 undefined。 */
|
|
49
|
+
const state = {
|
|
50
|
+
view: "chat",
|
|
51
|
+
tasks: [], cases: [], prds: [], timer: null, activeTask: null,
|
|
52
|
+
};
|