feihong-code 0.6.0 → 7.0.0

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.
@@ -72,6 +72,10 @@
72
72
  <span class="icon">📎</span>
73
73
  <span class="label" data-i18n="sidebar.office">办公助理</span>
74
74
  </div>
75
+ <div class="sidebar-icon" data-nav="nodes" title="节点管理" data-i18n="sidebar.nodes">
76
+ <span class="icon">🔗</span>
77
+ <span class="label" data-i18n="sidebar.nodes">节点管理</span>
78
+ </div>
75
79
  <div class="sidebar-icon" data-nav="memory" title="记忆系统" data-i18n="sidebar.memory">
76
80
  <span class="icon">🧠</span>
77
81
  <span class="label" data-i18n="sidebar.memory">记忆系统</span>
@@ -142,15 +146,16 @@
142
146
  <div class="dropdown-item" data-action="screenshot" data-i18n="chat.screenshot">📷 截图</div>
143
147
  <div class="dropdown-item" data-action="upload-file" data-i18n="chat.upload_file">📄 上传文件</div>
144
148
  <div class="dropdown-item" data-action="upload-image" data-i18n="chat.upload_image">🖼 上传图片</div>
145
- <div class="dropdown-item" data-action="direct-mode" data-i18n="chat.direct_mode">🖥 直接操作电脑</div>
149
+ <div class="dropdown-item" data-action="direct-mode" data-i18n="chat.direct_mode">🖥 电脑操作</div>
146
150
  <div class="dropdown-item" data-action="voice" data-i18n="chat.voice">🎤 语音输入</div>
147
151
  <div class="dropdown-divider"></div>
148
152
  <div class="dropdown-item" data-action="agent-type" data-i18n="chat.agent_type">🤖 选择智能体类型</div>
149
153
  </div>
150
154
  </div>
151
155
  <button class="pill" id="screenshotBtn" title="截图(框选屏幕区域发给 AI)" data-i18n="chat.screenshot">📷 截图</button>
156
+ <button class="pill" id="voiceBtn" title="语音输入(点击开始/停止)" data-i18n="chat.voice">🎤 语音</button>
152
157
  <span class="pill" id="skillPill" title="当前智能体" data-i18n="chat.agent_default">🤖 通用助手</span>
153
- <span class="pill" id="directModePill" style="display:none;" title="直接操作电脑模式已开启" data-i18n="chat.direct_mode">🖥 直接操作电脑</span>
158
+ <span class="pill" id="directModePill" style="display:none;" title="电脑操作模式已开启" data-i18n="chat.direct_mode">🖥 电脑操作</span>
154
159
  <span id="voiceBadge" class="voice-badge" style="display:none;" data-i18n="chat.listening">● 正在听…</span>
155
160
  <span class="spacer"></span>
156
161
  <button id="sendBtn" data-i18n="chat.send">▶ 发送</button>
@@ -183,7 +188,10 @@
183
188
  <div data-i18n="nav.automations"><h2>⚡ 自动化</h2><div class="sub">把常用目标存成指令,一键发起任务</div></div>
184
189
  <button id="newAutoBtn" data-i18n="btn.new_automation">+ 新建指令</button>
185
190
  </div>
186
- <div class="grid" id="autoGrid" data-i18n="empty.no_automations"><div class="empty">还没有快捷指令</div></div>
191
+ <div class="section-title">常用快捷指令</div>
192
+ <div class="grid" id="builtinAutoGrid"></div>
193
+ <div class="section-title" style="margin-top:20px;">我的指令</div>
194
+ <div class="grid" id="autoGrid" data-i18n="empty.no_automations"><div class="empty">还没有自定义指令,点击上方「新建指令」或从常用指令保存</div></div>
187
195
  </div>
188
196
 
189
197
  <!-- 模板库页面 -->
@@ -194,6 +202,10 @@
194
202
  </div>
195
203
  <div class="section-title" data-i18n="tpl.builtin">内置模板</div>
196
204
  <div class="grid" id="builtinGrid"></div>
205
+ <div class="section-title" style="margin-top:20px;" id="nodeTplTitle" data-i18n="tpl.node">来自节点</div>
206
+ <div class="grid" id="nodeTplGrid"><div class="empty">暂无节点模板,去「节点管理」添加节点</div></div>
207
+ <div class="section-title" style="margin-top:20px;" id="customTplTitle" data-i18n="tpl.custom">自定义来源</div>
208
+ <div class="grid" id="customTplGrid"><div class="empty">暂无自定义来源模板</div></div>
197
209
  <div class="section-title" style="margin-top:20px;" data-i18n="tpl.my">我的模板</div>
198
210
  <div class="grid" id="userTplGrid" data-i18n="empty.no_templates"><div class="empty">暂无自定义模板</div></div>
199
211
  </div>
@@ -208,6 +220,8 @@
208
220
  <option value="all" data-i18n="market.source_all">全部来源</option>
209
221
  <option value="clawhub">ClawHub</option>
210
222
  <option value="agent-foundry">Agent-Foundry</option>
223
+ <option value="node">节点</option>
224
+ <option value="custom">自定义来源</option>
211
225
  </select>
212
226
  <button id="marketRefresh" data-i18n="btn.refresh">刷新</button>
213
227
  </div>
@@ -225,6 +239,21 @@
225
239
  <div class="grid" id="officeGrid" data-i18n="common.loading"><div class="empty">加载中…</div></div>
226
240
  </div>
227
241
 
242
+ <!-- 节点管理页面 -->
243
+ <div class="page-view" data-view="nodes">
244
+ <div class="page-head">
245
+ <div><h2>🔗 节点管理</h2><div class="sub">连接外部节点,扩展模板/插件/办公能力来源</div></div>
246
+ <div class="row" style="flex:0 0 auto; gap:8px;">
247
+ <button id="addNodeBtn">+ 添加节点</button>
248
+ <button id="addSourceBtn" class="ghost">+ 自定义来源</button>
249
+ </div>
250
+ </div>
251
+ <div class="section-title">已连接节点</div>
252
+ <div class="grid" id="nodesGrid"><div class="empty">还没有添加节点,点击上方「添加节点」连接外部服务</div></div>
253
+ <div class="section-title" style="margin-top:20px;">自定义来源</div>
254
+ <div class="grid" id="sourcesGrid"><div class="empty">还没有自定义来源,点击上方「自定义来源」添加远程数据源</div></div>
255
+ </div>
256
+
228
257
  <!-- 记忆系统页面 -->
229
258
  <div class="page-view" data-view="memory">
230
259
  <div class="page-head">
@@ -267,8 +296,17 @@
267
296
  <div class="row" style="margin-bottom:16px;align-items:center;">
268
297
  <input type="date" id="memDateInput" style="margin:0;width:160px;" />
269
298
  <button id="memLoadDay" style="margin:0;" data-i18n="btn.view">查看</button>
299
+ <button id="memAddShort" style="margin:0;" class="ghost">+ 添加记录</button>
270
300
  <span class="muted" id="memDateLabel" style="margin-left:8px;"></span>
271
301
  </div>
302
+ <!-- 添加记录文本框(默认隐藏,点击添加记录后展开) -->
303
+ <div id="memShortEditor" style="display:none;margin-bottom:16px;padding:12px;border:1px solid var(--border);border-radius:8px;background:var(--bg);">
304
+ <textarea id="memShortInput" rows="4" placeholder="直接输入你想记录的内容,点击保存即可添加到今天的短期记忆..." style="width:100%;padding:10px;border:1px solid var(--border);border-radius:6px;background:var(--card);color:var(--ink);font-size:13px;resize:vertical;box-sizing:border-box;"></textarea>
305
+ <div style="margin-top:8px;display:flex;gap:8px;justify-content:flex-end;">
306
+ <button id="memShortCancel" class="ghost">取消</button>
307
+ <button id="memShortSave" class="primary">保存</button>
308
+ </div>
309
+ </div>
272
310
  <div class="card memory-content" id="memShortContent">
273
311
  <div class="empty" data-i18n="btn.view">选择日期查看短期记忆</div>
274
312
  </div>
@@ -276,9 +314,26 @@
276
314
 
277
315
  <!-- 长期记忆面板 -->
278
316
  <div class="mem-panel" id="memLongPanel" style="display:none;">
317
+ <div class="row" style="margin-bottom:16px;align-items:center;">
318
+ <button id="memEditLong" style="margin:0;">✎ 编辑</button>
319
+ <button id="memSaveLong" style="margin:0;display:none;" class="primary">💾 保存</button>
320
+ <button id="memCancelLong" style="margin:0;display:none;" class="ghost">取消</button>
321
+ <button id="memAppendLong" style="margin:0;" class="ghost">+ 追加</button>
322
+ <span class="muted" style="margin-left:8px;">长期记忆会在对话中被 AI 参考</span>
323
+ </div>
324
+ <!-- 追加条目文本框(默认隐藏) -->
325
+ <div id="memLongAppendEditor" style="display:none;margin-bottom:16px;padding:12px;border:1px solid var(--border);border-radius:8px;background:var(--bg);">
326
+ <input type="text" id="memAppendTitle" placeholder="条目标题(如:项目知识/用户偏好)" style="width:100%;padding:8px 10px;border:1px solid var(--border);border-radius:6px;background:var(--card);color:var(--ink);font-size:13px;margin-bottom:8px;box-sizing:border-box;" />
327
+ <textarea id="memAppendContent" rows="3" placeholder="输入你想追加的记忆内容..." style="width:100%;padding:10px;border:1px solid var(--border);border-radius:6px;background:var(--card);color:var(--ink);font-size:13px;resize:vertical;box-sizing:border-box;"></textarea>
328
+ <div style="margin-top:8px;display:flex;gap:8px;justify-content:flex-end;">
329
+ <button id="memAppendCancel" class="ghost">取消</button>
330
+ <button id="memAppendSave" class="primary">追加</button>
331
+ </div>
332
+ </div>
279
333
  <div class="card memory-content" id="memLongContent">
280
334
  <div class="empty" data-i18n="common.loading">加载中…</div>
281
335
  </div>
336
+ <textarea id="memLongEditor" style="display:none;width:100%;min-height:400px;padding:12px;border:1px solid var(--border);border-radius:8px;background:var(--bg);color:var(--ink);font-family:monospace;font-size:13px;resize:vertical;box-sizing:border-box;"></textarea>
282
337
  </div>
283
338
 
284
339
  <!-- 总结历史面板 -->
@@ -475,6 +530,17 @@
475
530
  <button id="modelSettingsShortcut" class="ghost-btn" title="大模型设置" data-i18n="sidebar.settings">🧠</button>
476
531
  </div>
477
532
  </div>
533
+ <!-- 视频通话窗口 -->
534
+ <div class="video-call-window" id="videoCallWindow" style="display:none;">
535
+ <div class="video-call-header">
536
+ <span>📹 视频通话中</span>
537
+ <div style="display:flex;gap:8px;align-items:center;">
538
+ <button class="video-call-snapshot" id="videoSnapshotBtn" title="截取当前画面发送">📸</button>
539
+ <button class="video-call-close" id="videoCallClose">✕</button>
540
+ </div>
541
+ </div>
542
+ <video id="localVideo" autoplay muted playsinline></video>
543
+ </div>
478
544
  </main>
479
545
 
480
546
  <!-- 右侧面板 -->
@@ -482,6 +548,7 @@
482
548
  <div class="right-tabs">
483
549
  <div class="right-tab active" data-tab="detail" data-i18n="right.detail">📦 任务详情</div>
484
550
  <div class="right-tab" data-tab="preview" data-i18n="right.preview">👁 产物预览</div>
551
+ <div class="right-tab" data-tab="computer" id="computerTab" style="display:none;">🖥 电脑操作</div>
485
552
  </div>
486
553
  <div class="right-body">
487
554
  <div class="preview-panel active" id="detailPanel">
@@ -491,6 +558,34 @@
491
558
  <div class="preview-title" id="previewTitle" data-i18n="right.preview_title">预览</div>
492
559
  <div class="preview-content" id="previewContent" data-i18n="right.preview_title"><div class="muted">点击任务或对话中的文件/代码进行预览。</div></div>
493
560
  </div>
561
+ <div class="preview-panel" id="computerPanel">
562
+ <div class="computer-controls">
563
+ <button id="screenCaptureBtn" class="pill">📷 截取屏幕</button>
564
+ <span id="mousePos" class="muted" style="font-size:12px;">坐标: -</span>
565
+ </div>
566
+ <div class="screen-preview" id="screenPreview">
567
+ <div class="muted" style="text-align:center;padding:40px;">点击「截取屏幕」查看当前屏幕</div>
568
+ </div>
569
+ <div class="computer-actions">
570
+ <div style="margin-bottom:8px;font-size:13px;font-weight:600;">快捷操作</div>
571
+ <div style="display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px;">
572
+ <button class="pill" id="clickLeftBtn">左键点击</button>
573
+ <button class="pill" id="clickRightBtn">右键点击</button>
574
+ <button class="pill" id="doubleClickBtn">双击</button>
575
+ </div>
576
+ <div style="margin-bottom:8px;">
577
+ <input type="text" id="typeTextInput" placeholder="输入文字后回车发送" style="width:100%;padding:6px 8px;font-size:12px;border:1px solid var(--border);border-radius:6px;" />
578
+ </div>
579
+ <div style="display:flex;gap:6px;flex-wrap:wrap;">
580
+ <button class="pill" data-key="{ENTER}">回车</button>
581
+ <button class="pill" data-key="{TAB}">Tab</button>
582
+ <button class="pill" data-key="{ESC}">Esc</button>
583
+ <button class="pill" data-key="^c">复制</button>
584
+ <button class="pill" data-key="^v">粘贴</button>
585
+ <button class="pill" data-key="^a">全选</button>
586
+ </div>
587
+ </div>
588
+ </div>
494
589
  </div>
495
590
  </aside>
496
591
  </div>
@@ -513,6 +608,62 @@
513
608
  </div>
514
609
  </div>
515
610
 
611
+ <!-- 添加节点模态框 -->
612
+ <div class="mask" id="nodeModal">
613
+ <div class="modal">
614
+ <span class="close-x" data-close="nodeModal">✕</span>
615
+ <h3 id="nodeModalTitle">添加节点</h3>
616
+ <p class="hint">连接外部节点,扩展模板/插件/办公能力来源。节点需提供标准 API 或本地 JSON 文件。</p>
617
+ <input type="hidden" id="nodeEditId" />
618
+ <label for="nodeName">节点名称</label>
619
+ <input type="text" id="nodeName" placeholder="如:公司内部技能库" />
620
+ <label for="nodeType">节点类型</label>
621
+ <select id="nodeType">
622
+ <option value="http">HTTP API 节点</option>
623
+ <option value="local">本地目录节点</option>
624
+ <option value="git">Git 仓库节点</option>
625
+ </select>
626
+ <label for="nodeUrl">地址(URL 或本地路径)</label>
627
+ <input type="text" id="nodeUrl" placeholder="如:https://api.example.com 或 D:\nodes\my-node" />
628
+ <label for="nodeApiKey">API Key(可选)</label>
629
+ <input type="password" id="nodeApiKey" placeholder="留空表示不需要认证" autocomplete="off" />
630
+ <label>提供的能力</label>
631
+ <div style="display:flex;gap:16px;margin-bottom:12px;">
632
+ <label style="display:flex;align-items:center;gap:6px;cursor:pointer;"><input type="checkbox" id="nodeCapTemplates" checked /> 模板</label>
633
+ <label style="display:flex;align-items:center;gap:6px;cursor:pointer;"><input type="checkbox" id="nodeCapSkills" checked /> 插件</label>
634
+ <label style="display:flex;align-items:center;gap:6px;cursor:pointer;"><input type="checkbox" id="nodeCapOffice" checked /> 办公</label>
635
+ </div>
636
+ <div class="footer">
637
+ <button class="ghost" data-close="nodeModal">取消</button>
638
+ <button id="nodeSaveBtn" class="primary">保存并测试连接</button>
639
+ </div>
640
+ </div>
641
+ </div>
642
+
643
+ <!-- 添加自定义来源模态框 -->
644
+ <div class="mask" id="sourceModal">
645
+ <div class="modal">
646
+ <span class="close-x" data-close="sourceModal">✕</span>
647
+ <h3 id="sourceModalTitle">添加自定义来源</h3>
648
+ <p class="hint">添加远程 JSON 数据源,自动拉取模板/插件/办公能力。</p>
649
+ <input type="hidden" id="sourceEditId" />
650
+ <label for="sourceName">来源名称</label>
651
+ <input type="text" id="sourceName" placeholder="如:团队共享模板" />
652
+ <label for="sourceType">来源类型</label>
653
+ <select id="sourceType">
654
+ <option value="templates">模板</option>
655
+ <option value="skills">插件</option>
656
+ <option value="office">办公能力</option>
657
+ </select>
658
+ <label for="sourceUrl">JSON 数据地址</label>
659
+ <input type="text" id="sourceUrl" placeholder="https://example.com/templates.json" />
660
+ <div class="footer">
661
+ <button class="ghost" data-close="sourceModal">取消</button>
662
+ <button id="sourceSaveBtn" class="primary">保存</button>
663
+ </div>
664
+ </div>
665
+ </div>
666
+
516
667
  <div class="mask" id="tplModal">
517
668
  <div class="modal">
518
669
  <span class="close-x" data-close="tplModal">✕</span>
@@ -602,14 +753,47 @@
602
753
  </div>
603
754
 
604
755
  <div class="mask" id="folderPickerModal">
605
- <div class="modal" style="width:520px;">
756
+ <div class="modal" style="width:560px;">
606
757
  <span class="close-x" data-close="folderPickerModal">✕</span>
607
- <h3 data-i18n="modal.folder_title">📂 选择工作区文件夹</h3>
608
- <div class="fp-path" id="fpPath" style="font-size:12px;color:var(--ink-2);word-break:break-all;padding:8px;background:var(--bg);border-radius:8px;">—</div>
609
- <div class="file-tree" id="fpTree" style="max-height:280px;overflow:auto;margin:10px 0;"></div>
758
+ <h3 id="fpTitle">📂 选择工作区文件夹</h3>
759
+
760
+ <!-- 驱动器选择视图 -->
761
+ <div id="fpDrivesView">
762
+ <p class="hint" style="margin:8px 0;">请选择一个驱动器,然后在其中选择或新建文件夹作为工作区。</p>
763
+ <div class="fp-drives-grid" id="fpDrivesGrid" style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin:10px 0;max-height:320px;overflow:auto;"></div>
764
+ </div>
765
+
766
+ <!-- 文件夹选择视图 -->
767
+ <div id="fpFoldersView" style="display:none;">
768
+ <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;">
769
+ <button class="ghost sm" id="fpBackToDrives" style="flex-shrink:0;">← 驱动器</button>
770
+ <button class="ghost sm" id="fpUpDir" style="flex-shrink:0;">↑ 上级</button>
771
+ <div class="fp-path" id="fpPath" style="flex:1;font-size:12px;color:var(--ink-2);word-break:break-all;padding:6px 8px;background:var(--bg);border-radius:6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">—</div>
772
+ </div>
773
+ <div style="display:flex;gap:8px;margin-bottom:8px;">
774
+ <button class="ghost sm" id="fpNewFolder">+ 新建文件夹</button>
775
+ <button class="ghost sm" id="fpRenameFolder" disabled>✎ 重命名</button>
776
+ </div>
777
+ <div class="file-tree" id="fpTree" style="max-height:260px;overflow:auto;margin:10px 0;"></div>
778
+ </div>
779
+
780
+ <!-- 新建文件夹输入框 -->
781
+ <div id="fpMkdirBar" style="display:none;gap:8px;margin-bottom:8px;">
782
+ <input type="text" id="fpMkdirName" placeholder="输入新文件夹名称" style="flex:1;padding:6px 10px;border:1px solid var(--line);border-radius:6px;font-size:13px;" />
783
+ <button class="primary sm" id="fpMkdirConfirm">确定</button>
784
+ <button class="ghost sm" id="fpMkdirCancel">取消</button>
785
+ </div>
786
+
787
+ <!-- 重命名输入框 -->
788
+ <div id="fpRenameBar" style="display:none;gap:8px;margin-bottom:8px;">
789
+ <input type="text" id="fpRenameName" placeholder="输入新名称" style="flex:1;padding:6px 10px;border:1px solid var(--line);border-radius:6px;font-size:13px;" />
790
+ <button class="primary sm" id="fpRenameConfirm">确定</button>
791
+ <button class="ghost sm" id="fpRenameCancel">取消</button>
792
+ </div>
793
+
610
794
  <div class="footer">
611
- <button class="ghost" id="fpCancel" data-close="folderPickerModal" data-i18n="btn.cancel">取消</button>
612
- <button class="primary" id="fpConfirm" data-i18n="btn.select_folder">选择此文件夹</button>
795
+ <button class="ghost" id="fpCancel" data-close="folderPickerModal">取消</button>
796
+ <button class="primary" id="fpConfirm" disabled>选择此文件夹</button>
613
797
  </div>
614
798
  </div>
615
799
  </div>
@@ -107,8 +107,80 @@
107
107
  }
108
108
 
109
109
  async function loadTemplates() {
110
- try { const d = await api('/api/templates'); renderBuiltin(d.builtin || []); renderUserTpl(d.user || []); }
111
- catch (e) { console.warn('加载模板失败', e); }
110
+ try {
111
+ const d = await api('/api/templates');
112
+ renderBuiltin(d.builtin || []);
113
+ renderNodeTpl(d.node || []);
114
+ renderCustomTpl(d.custom || []);
115
+ renderUserTpl(d.user || []);
116
+ } catch (e) { console.warn('加载模板失败', e); }
117
+ }
118
+
119
+ /* ========== 节点管理 ========== */
120
+ async function loadNodes() {
121
+ try {
122
+ const d = await api('/api/nodes');
123
+ renderNodesGrid(d.nodes || []);
124
+ } catch (e) { console.warn('加载节点失败', e); }
125
+ }
126
+ async function saveNode(data) {
127
+ try {
128
+ const d = data.id
129
+ ? await api('/api/nodes/' + data.id, 'PUT', data)
130
+ : await api('/api/nodes', 'POST', data);
131
+ await loadNodes();
132
+ toast(data.id ? '节点已更新' : '节点已添加,连接状态:' + (d.connection?.ok ? '成功' : '失败'));
133
+ return d;
134
+ } catch (e) { toast('保存失败:' + e.message); }
135
+ }
136
+ async function deleteNode(id) {
137
+ try { await api('/api/nodes/' + id, 'DELETE'); await loadNodes(); toast('已删除节点'); }
138
+ catch (e) { toast('删除失败:' + e.message); }
139
+ }
140
+ async function testNode(id) {
141
+ try {
142
+ const d = await api('/api/nodes/' + id + '/test', 'POST');
143
+ toast(d.ok ? '连接成功' : '连接失败:' + (d.error || ''));
144
+ await loadNodes();
145
+ return d;
146
+ } catch (e) { toast('测试失败:' + e.message); }
147
+ }
148
+ async function syncNode(id) {
149
+ try {
150
+ const d = await api('/api/nodes/' + id + '/sync', 'POST');
151
+ toast(d.ok ? '同步完成:' + (d.synced || []).join('、') : '同步失败:' + (d.error || ''));
152
+ await loadNodes();
153
+ await loadTemplates();
154
+ await loadMarket();
155
+ await loadOffice();
156
+ return d;
157
+ } catch (e) { toast('同步失败:' + e.message); }
158
+ }
159
+ async function toggleNode(id, enabled) {
160
+ try { await api('/api/nodes/' + id, 'PUT', { enabled }); await loadNodes(); }
161
+ catch (e) { toast('操作失败:' + e.message); }
162
+ }
163
+
164
+ /* ========== 自定义来源 ========== */
165
+ async function loadSources() {
166
+ try {
167
+ const d = await api('/api/sources');
168
+ renderSourcesGrid(d.sources || []);
169
+ } catch (e) { console.warn('加载来源失败', e); }
170
+ }
171
+ async function saveSource(data) {
172
+ try {
173
+ const d = data.id
174
+ ? await api('/api/sources/' + data.id, 'PUT', data)
175
+ : await api('/api/sources', 'POST', data);
176
+ await loadSources();
177
+ toast(data.id ? '来源已更新' : '来源已添加');
178
+ return d;
179
+ } catch (e) { toast('保存失败:' + e.message); }
180
+ }
181
+ async function deleteSource(id) {
182
+ try { await api('/api/sources/' + id, 'DELETE'); await loadSources(); toast('已删除来源'); }
183
+ catch (e) { toast('删除失败:' + e.message); }
112
184
  }
113
185
 
114
186
  async function delAuto(id) {
@@ -122,7 +194,7 @@
122
194
  }
123
195
 
124
196
  async function loadAutomations() {
125
- try { const d = await api('/api/automations'); renderAutoGrid(d.automations || []); }
197
+ try { const d = await api('/api/automations'); renderBuiltinAutomations(d.builtin || []); renderAutoGrid(d.automations || []); }
126
198
  catch (e) { console.warn('加载自动化失败', e); }
127
199
  }
128
200
 
@@ -272,6 +344,7 @@
272
344
  status.classList.remove('err');
273
345
  document.getElementById('loginOverlay').style.display = 'none';
274
346
  document.getElementById('appLayout').classList.add('show');
347
+ document.documentElement.classList.add('logged-in');
275
348
  await afterLogin();
276
349
  } catch (e) {
277
350
  status.textContent = '登录失败:' + e.message;