foxclaw 0.1.2 → 0.1.3

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.
Files changed (344) hide show
  1. package/index.js +1 -8
  2. package/package.json +1 -1
  3. package/python/.env.example +8 -0
  4. package/python/.github/workflows/ci.yml +47 -2
  5. package/python/CHANGELOG.md +57 -0
  6. package/python/CHANGELOG_CN.md +57 -0
  7. package/python/README.md +11 -2
  8. package/python/README_CN.md +32 -20
  9. package/python/bench/bm25_audit.py +1 -14
  10. package/python/bench/bm25_regression.py +0 -1
  11. package/python/bench/bm25_stress.py +0 -4
  12. package/python/bench/cd_audit.py +0 -23
  13. package/python/bench/chaos.py +6 -8
  14. package/python/bench/e2e_real_models.py +0 -1
  15. package/python/bench/llm_retry_test.py +2 -9
  16. package/python/bench/load_test.py +5 -8
  17. package/python/bench/vector_ivf_upgrade.py +1 -7
  18. package/python/docs/RELEASE.md +4 -3
  19. package/python/docs/RUN_CN.md +2 -2
  20. package/python/evals/governance/cases.py +0 -37
  21. package/python/evals/governance/run_evals.py +5 -37
  22. package/python/examples/governance_middleware_demo.py +1 -6
  23. package/python/examples/langgraph_governance_demo.py +7 -11
  24. package/python/foxclaw/__init__.py +34 -152
  25. package/python/foxclaw/agents/base.py +65 -136
  26. package/python/foxclaw/agents/impl/_helpers.py +2 -10
  27. package/python/foxclaw/agents/impl/finance.py +4 -21
  28. package/python/foxclaw/agents/impl/office.py +7 -24
  29. package/python/foxclaw/agents/impl/supervisor.py +12 -79
  30. package/python/foxclaw/agents/mixins/_context.py +0 -23
  31. package/python/foxclaw/agents/mixins/augment.py +10 -16
  32. package/python/foxclaw/agents/mixins/llm.py +7 -6
  33. package/python/foxclaw/agents/mixins/memory.py +7 -1
  34. package/python/foxclaw/agents/mixins/preference.py +58 -8
  35. package/python/foxclaw/agents/mixins/prompt.py +0 -6
  36. package/python/foxclaw/agents/mixins/skillbox.py +6 -6
  37. package/python/foxclaw/agents/mixins/tool_exec.py +16 -42
  38. package/python/foxclaw/api/__init__.py +18 -77
  39. package/python/foxclaw/api/_state.py +43 -0
  40. package/python/foxclaw/api/{app.py → bootstrap.py} +5 -32
  41. package/python/foxclaw/api/middleware.py +5 -28
  42. package/python/foxclaw/api/routes/ahs.py +60 -73
  43. package/python/foxclaw/api/routes/attachments.py +5 -6
  44. package/python/foxclaw/api/routes/browser.py +7 -11
  45. package/python/foxclaw/api/routes/domain.py +18 -26
  46. package/python/foxclaw/api/routes/knowledge.py +3 -21
  47. package/python/foxclaw/api/routes/llm_admin.py +21 -33
  48. package/python/foxclaw/api/routes/memory.py +28 -12
  49. package/python/foxclaw/api/routes/orchestration.py +3 -5
  50. package/python/foxclaw/api/routes/recovery.py +4 -3
  51. package/python/foxclaw/api/routes/review.py +2 -11
  52. package/python/foxclaw/api/routes/search.py +3 -7
  53. package/python/foxclaw/api/routes/system.py +33 -49
  54. package/python/foxclaw/api/routes/task.py +31 -49
  55. package/python/foxclaw/api/routes/ws.py +2 -6
  56. package/python/foxclaw/api/spine.py +1 -4
  57. package/python/foxclaw/browser/action/action_executor.py +1 -5
  58. package/python/foxclaw/browser/action/action_types.py +0 -1
  59. package/python/foxclaw/browser/action/url_policy.py +1 -5
  60. package/python/foxclaw/browser/adapter/web_adapter.py +1 -1
  61. package/python/foxclaw/browser/driver/playwright_driver.py +20 -16
  62. package/python/foxclaw/browser/perception/dom_extractor.py +0 -1
  63. package/python/foxclaw/browser/perception/element_indexer.py +1 -2
  64. package/python/foxclaw/browser/service.py +7 -11
  65. package/python/foxclaw/capability/embedding_service.py +4 -6
  66. package/python/foxclaw/channels/__init__.py +9 -11
  67. package/python/foxclaw/channels/discord.py +4 -13
  68. package/python/foxclaw/channels/dispatcher.py +2 -27
  69. package/python/foxclaw/channels/feishu.py +10 -20
  70. package/python/foxclaw/channels/openclaw_bridge.py +4 -9
  71. package/python/foxclaw/channels/signing.py +9 -9
  72. package/python/foxclaw/channels/telegram.py +3 -4
  73. package/python/foxclaw/channels/weixin.py +7 -17
  74. package/python/foxclaw/cli.py +42 -13
  75. package/python/foxclaw/cognitive/reasoner.py +9 -10
  76. package/python/foxclaw/cognitive/state.py +18 -24
  77. package/python/foxclaw/core/audit_chain.py +1 -3
  78. package/python/foxclaw/core/audit_sink.py +3 -3
  79. package/python/foxclaw/core/compaction.py +8 -26
  80. package/python/foxclaw/core/config.py +2 -2
  81. package/python/foxclaw/core/confirm_nonce.py +6 -12
  82. package/python/foxclaw/core/envfile.py +2 -2
  83. package/python/foxclaw/core/failure_policy.py +17 -26
  84. package/python/foxclaw/core/gateway.py +6 -10
  85. package/python/foxclaw/core/governance_audit.py +3 -7
  86. package/python/foxclaw/core/idempotency.py +15 -22
  87. package/python/foxclaw/core/injection_guard.py +10 -29
  88. package/python/foxclaw/core/memory_adapter.py +30 -57
  89. package/python/foxclaw/core/metrics.py +4 -5
  90. package/python/foxclaw/core/model_router.py +36 -148
  91. package/python/foxclaw/core/paths.py +1 -1
  92. package/python/foxclaw/core/recovery.py +21 -34
  93. package/python/foxclaw/core/scheduler.py +23 -26
  94. package/python/foxclaw/core/secrets.py +0 -3
  95. package/python/foxclaw/core/sessions.py +4 -6
  96. package/python/foxclaw/core/settings.py +16 -32
  97. package/python/foxclaw/core/shared_write.py +11 -23
  98. package/python/foxclaw/core/steering_queue.py +12 -11
  99. package/python/foxclaw/core/system_bus.py +1 -1
  100. package/python/foxclaw/core/thought_chain.py +30 -32
  101. package/python/foxclaw/core/token_meter.py +9 -13
  102. package/python/foxclaw/core/tools.py +4 -12
  103. package/python/foxclaw/core/tracing.py +25 -40
  104. package/python/foxclaw/core/truth_pipeline.py +5 -9
  105. package/python/foxclaw/core/update_check.py +149 -0
  106. package/python/foxclaw/core/vec_recall_adapter.py +11 -37
  107. package/python/foxclaw/core/verify.py +6 -14
  108. package/python/foxclaw/core/web_search.py +4 -28
  109. package/python/foxclaw/finance/evidence.py +14 -18
  110. package/python/foxclaw/finance/fx.py +1 -3
  111. package/python/foxclaw/finance/graph.py +9 -18
  112. package/python/foxclaw/finance/models.py +20 -24
  113. package/python/foxclaw/finance/nlparse.py +12 -72
  114. package/python/foxclaw/finance/policy.py +8 -36
  115. package/python/foxclaw/finance/reasoner.py +3 -11
  116. package/python/foxclaw/finance/reasoning.py +13 -34
  117. package/python/foxclaw/finance/reconcile.py +5 -10
  118. package/python/foxclaw/finance/runtime.py +19 -46
  119. package/python/foxclaw/governance/middleware.py +13 -21
  120. package/python/foxclaw/harness/_base.py +2 -8
  121. package/python/foxclaw/harness/_policy.py +19 -38
  122. package/python/foxclaw/harness/drift.py +1 -13
  123. package/python/foxclaw/harness/event_bus.py +2 -5
  124. package/python/foxclaw/harness/harness.py +9 -97
  125. package/python/foxclaw/harness/loop_detection.py +11 -18
  126. package/python/foxclaw/itinerary/amap_client.py +2 -5
  127. package/python/foxclaw/itinerary/models.py +7 -7
  128. package/python/foxclaw/itinerary/reasoner.py +1 -6
  129. package/python/foxclaw/itinerary/reasoning.py +15 -21
  130. package/python/foxclaw/itinerary/runtime.py +8 -12
  131. package/python/foxclaw/kernel/capabilities.py +5 -6
  132. package/python/foxclaw/kernel/policy_gate.py +4 -10
  133. package/python/foxclaw/kernel/process_manager.py +17 -36
  134. package/python/foxclaw/kernel/scheduler.py +3 -9
  135. package/python/foxclaw/kernel/sentinel.py +13 -23
  136. package/python/foxclaw/kernel/sentinel_client.py +12 -15
  137. package/python/foxclaw/kernel/spine.py +9 -9
  138. package/python/foxclaw/knowledge/__init__.py +4 -20
  139. package/python/foxclaw/knowledge/truth_crystallizer.py +5 -8
  140. package/python/foxclaw/learning/knowledge_graph.py +4 -9
  141. package/python/foxclaw/learning/models.py +13 -13
  142. package/python/foxclaw/learning/reasoner.py +1 -4
  143. package/python/foxclaw/learning/runtime.py +19 -31
  144. package/python/foxclaw/learning/spaced_repetition.py +2 -7
  145. package/python/foxclaw/mcp_server.py +2 -3
  146. package/python/foxclaw/orchestration/__init__.py +16 -33
  147. package/python/foxclaw/pipeline/cognitive_pipeline.py +45 -97
  148. package/python/foxclaw/pipeline/stage_meta.py +21 -0
  149. package/python/foxclaw/pipeline/supervisor_stages.py +13 -42
  150. package/python/foxclaw/planning/llm_planner.py +3 -3
  151. package/python/foxclaw/planning/planner.py +6 -11
  152. package/python/foxclaw/search/agent_port.py +7 -11
  153. package/python/foxclaw/search/evidence.py +0 -4
  154. package/python/foxclaw/search/internal_search.py +3 -5
  155. package/python/foxclaw/search/models.py +11 -13
  156. package/python/foxclaw/search/planner.py +3 -3
  157. package/python/foxclaw/search/retriever.py +1 -1
  158. package/python/foxclaw/search/runtime.py +1 -8
  159. package/python/foxclaw/skills/__init__.py +46 -100
  160. package/python/foxclaw/skills/archive.py +111 -0
  161. package/python/foxclaw/skills/fetch.py +226 -0
  162. package/python/foxclaw/skills/hooks_spec.py +21 -25
  163. package/python/foxclaw/skills/install_cmd.py +216 -0
  164. package/python/foxclaw/skills/lifecycle.py +16 -42
  165. package/python/foxclaw/skills/markdown_skill.py +10 -9
  166. package/python/foxclaw/skills/mcp_client.py +5 -11
  167. package/python/foxclaw/skills/mcp_store.py +0 -3
  168. package/python/foxclaw/skills/permission.py +13 -48
  169. package/python/foxclaw/skills/pipeline.py +21 -53
  170. package/python/foxclaw/skills/policy.py +4 -10
  171. package/python/foxclaw/skills/review.py +3 -3
  172. package/python/frontend/deck/auth.js +0 -7
  173. package/python/frontend/deck/chat_model_picker.js +0 -54
  174. package/python/frontend/deck/domain.js +0 -20
  175. package/python/frontend/deck/main.js +0 -144
  176. package/python/frontend/deck/markdown.js +0 -4
  177. package/python/frontend/deck/settings.js +1 -18
  178. package/python/frontend/deck/state.js +0 -1
  179. package/python/frontend/deck/util.js +0 -2
  180. package/python/frontend/deck/visuals.js +0 -22
  181. package/python/frontend/planet/planet.js +0 -201
  182. package/python/frontend/v3/assets/index-CQSBdxk4.js +34 -0
  183. package/python/frontend/v3/assets/index-D5nIsjms.css +1 -0
  184. package/python/frontend/v3/index.html +3 -3
  185. package/python/frontend/widget_runtime.js +0 -43
  186. package/python/frontend/widgets.js +0 -57
  187. package/python/frontend_src/index.html +1 -1
  188. package/python/frontend_src/package-lock.json +2 -2
  189. package/python/frontend_src/package.json +1 -1
  190. package/python/frontend_src/src/App.vue +3 -7
  191. package/python/frontend_src/src/api.js +0 -7
  192. package/python/frontend_src/src/components/ChatDock.vue +27 -39
  193. package/python/frontend_src/src/components/DocViewer.vue +0 -9
  194. package/python/frontend_src/src/components/Inspector.vue +0 -6
  195. package/python/frontend_src/src/components/LeftRail.vue +60 -39
  196. package/python/frontend_src/src/components/MenuPopover.vue +9 -11
  197. package/python/frontend_src/src/components/MessageStream.vue +18 -30
  198. package/python/frontend_src/src/components/ModelPicker.vue +0 -5
  199. package/python/frontend_src/src/components/QuickIngest.vue +0 -6
  200. package/python/frontend_src/src/components/Stage.vue +71 -25
  201. package/python/frontend_src/src/components/ToolMenu.vue +0 -3
  202. package/python/frontend_src/src/components/TopBar.vue +38 -10
  203. package/python/frontend_src/src/components/panels/ActivityPanel.vue +0 -2
  204. package/python/frontend_src/src/components/panels/AgentsPanel.vue +2 -5
  205. package/python/frontend_src/src/components/panels/KnowledgePanel.vue +0 -16
  206. package/python/frontend_src/src/components/panels/MemoryPanel.vue +0 -1
  207. package/python/frontend_src/src/components/panels/PanelBox.vue +6 -7
  208. package/python/frontend_src/src/components/panels/ReviewPanel.vue +0 -16
  209. package/python/frontend_src/src/components/panels/SettingsPanel.vue +142 -38
  210. package/python/frontend_src/src/components/panels/TasksPanel.vue +0 -1
  211. package/python/frontend_src/src/components/panels/ToolsPanel.vue +125 -19
  212. package/python/frontend_src/src/lib/fmt.js +0 -4
  213. package/python/frontend_src/src/lib/i18n.js +53 -0
  214. package/python/frontend_src/src/lib/locales/en.js +173 -0
  215. package/python/frontend_src/src/lib/locales/zh.js +174 -0
  216. package/python/frontend_src/src/lib/md.js +0 -14
  217. package/python/frontend_src/src/main.js +2 -0
  218. package/python/frontend_src/src/stores/app.js +17 -32
  219. package/python/frontend_src/src/stores/chat.js +0 -34
  220. package/python/frontend_src/src/stores/model.js +0 -8
  221. package/python/frontend_src/src/stores/panels.js +0 -41
  222. package/python/frontend_src/src/stores/profile.js +31 -0
  223. package/python/frontend_src/src/stores/system.js +25 -0
  224. package/python/frontend_src/vite.config.js +0 -7
  225. package/python/pyproject.toml +6 -3
  226. package/python/requirements.txt +311 -0
  227. package/python/tests/conftest.py +0 -22
  228. package/python/tests/run_all.py +3 -66
  229. package/python/tests/test_agent_lifecycle.py +4 -38
  230. package/python/tests/test_ahs_p2.py +4 -25
  231. package/python/tests/test_anthropic_tools.py +1 -15
  232. package/python/tests/test_api.py +19 -9
  233. package/python/tests/test_api_auth.py +1 -5
  234. package/python/tests/test_attachments.py +9 -25
  235. package/python/tests/test_audit_chain.py +2 -7
  236. package/python/tests/test_audit_sink.py +0 -6
  237. package/python/tests/test_browser.py +2 -6
  238. package/python/tests/test_browser_request_guard.py +0 -4
  239. package/python/tests/test_capabilities.py +0 -6
  240. package/python/tests/test_channel_signing.py +2 -9
  241. package/python/tests/test_channels.py +1 -11
  242. package/python/tests/test_config_layering.py +0 -4
  243. package/python/tests/test_confirm_nonce.py +1 -12
  244. package/python/tests/test_context_compaction_wiring.py +1 -7
  245. package/python/tests/test_dispatch.py +0 -6
  246. package/python/tests/test_domain_evals.py +11 -80
  247. package/python/tests/test_domain_import.py +0 -6
  248. package/python/tests/test_domain_reasoners.py +0 -14
  249. package/python/tests/test_e2e.py +0 -17
  250. package/python/tests/test_emb_cache.py +7 -17
  251. package/python/tests/test_embedding_singleton.py +1 -3
  252. package/python/tests/test_evals_smoke.py +0 -24
  253. package/python/tests/test_event_spine.py +10 -18
  254. package/python/tests/test_evidence_semantic.py +1 -14
  255. package/python/tests/test_evidence_sse.py +3 -6
  256. package/python/tests/test_file_ingest.py +0 -11
  257. package/python/tests/test_finance.py +0 -9
  258. package/python/tests/test_finance_ledger.py +5 -12
  259. package/python/tests/test_finance_persist.py +8 -12
  260. package/python/tests/test_gateway.py +3 -22
  261. package/python/tests/test_governance.py +5 -52
  262. package/python/tests/test_governance_audit.py +4 -39
  263. package/python/tests/test_governance_middleware.py +1 -7
  264. package/python/tests/test_hkc.py +6 -24
  265. package/python/tests/test_hkc_index_build_manager.py +0 -33
  266. package/python/tests/test_hkc_manifest_reuse.py +4 -27
  267. package/python/tests/test_hkc_search_event_sync.py +3 -54
  268. package/python/tests/test_import_path_guard.py +0 -6
  269. package/python/tests/test_injection_guard.py +18 -36
  270. package/python/tests/test_integration.py +5 -29
  271. package/python/tests/test_itinerary.py +0 -3
  272. package/python/tests/test_kee_semantic.py +0 -5
  273. package/python/tests/test_kernel.py +1 -11
  274. package/python/tests/test_learning.py +3 -12
  275. package/python/tests/test_llm_admin.py +4 -19
  276. package/python/tests/test_llm_planner.py +1 -23
  277. package/python/tests/test_markdown_skills.py +3 -14
  278. package/python/tests/test_markdown_skills_openclaw.py +4 -9
  279. package/python/tests/test_mcp_connectors.py +3 -15
  280. package/python/tests/test_mcp_guardian_discovery.py +2 -6
  281. package/python/tests/test_mcp_no_memory.py +3 -3
  282. package/python/tests/test_mcp_policy_gate.py +0 -1
  283. package/python/tests/test_mcp_server.py +6 -9
  284. package/python/tests/test_memory_recall.py +1 -4
  285. package/python/tests/test_metrics.py +0 -8
  286. package/python/tests/test_multi_chain.py +1 -13
  287. package/python/tests/test_multimodal.py +5 -18
  288. package/python/tests/test_negotiation.py +3 -13
  289. package/python/tests/test_orchestration.py +1 -11
  290. package/python/tests/test_orchestration_decoupled.py +0 -6
  291. package/python/tests/test_p3_p5_hardening.py +3 -17
  292. package/python/tests/test_parallel_dispatch.py +4 -12
  293. package/python/tests/test_pipeline.py +17 -58
  294. package/python/tests/test_plan_visible.py +4 -4
  295. package/python/tests/test_planner.py +0 -7
  296. package/python/tests/test_policy_gate.py +2 -3
  297. package/python/tests/test_profile.py +67 -0
  298. package/python/tests/test_readme_claims.py +163 -0
  299. package/python/tests/test_reasoner.py +2 -17
  300. package/python/tests/test_release_check.py +2 -13
  301. package/python/tests/test_resume_checkpoint.py +7 -15
  302. package/python/tests/test_routing.py +2 -12
  303. package/python/tests/test_scheduler.py +7 -10
  304. package/python/tests/test_search.py +1 -11
  305. package/python/tests/test_secrets.py +4 -10
  306. package/python/tests/test_semantic.py +1 -10
  307. package/python/tests/test_sessions.py +3 -4
  308. package/python/tests/test_settings.py +2 -18
  309. package/python/tests/test_shadow_learning.py +3 -43
  310. package/python/tests/test_shared_write.py +0 -7
  311. package/python/tests/test_skill_taint_gate.py +1 -10
  312. package/python/tests/test_skills.py +2 -18
  313. package/python/tests/test_skills_fetch.py +282 -0
  314. package/python/tests/test_stats_and_events.py +2 -5
  315. package/python/tests/test_streaming.py +2 -7
  316. package/python/tests/test_supervisor_pipeline.py +4 -5
  317. package/python/tests/test_supervisor_triage.py +4 -10
  318. package/python/tests/test_taint.py +2 -7
  319. package/python/tests/test_task_stream.py +1 -12
  320. package/python/tests/test_thinking_sse.py +0 -1
  321. package/python/tests/test_tool_center.py +1 -17
  322. package/python/tests/test_tool_parallel.py +2 -2
  323. package/python/tests/test_tool_params.py +1 -5
  324. package/python/tests/test_tool_use.py +0 -13
  325. package/python/tests/test_toplevel_concurrency.py +3 -4
  326. package/python/tests/test_tracing.py +7 -41
  327. package/python/tests/test_truth_crystallizer.py +4 -6
  328. package/python/tests/test_truth_flow.py +2 -7
  329. package/python/tests/test_update_check.py +95 -0
  330. package/python/tests/test_verify_evidence.py +8 -21
  331. package/python/tests/test_version_sync.py +35 -6
  332. package/python/tests/test_web_trigger.py +0 -17
  333. package/python/tools/check_faiss_type.py +0 -1
  334. package/python/tools/check_log.ps1 +0 -8
  335. package/python/tools/mypy_baseline.txt +12 -4
  336. package/python/tools/mypy_ratchet.py +52 -6
  337. package/python/tools/pack.py +0 -5
  338. package/python/tools/release_check.py +7 -7
  339. package/python/tools/restart_foxclaw.ps1 +5 -8
  340. package/python/tools/shared_write_audit.py +1 -15
  341. package/python/tools/size_check.py +1 -1
  342. package/python/tools/strip_comments.py +228 -0
  343. package/python/frontend/v3/assets/index-BD7VPbMV.js +0 -33
  344. package/python/frontend/v3/assets/index-CAvnYWGH.css +0 -1
@@ -0,0 +1 @@
1
+ .topbar[data-v-c3cf2a1d]{display:flex;align-items:center;gap:10px;padding:0 16px;background:#ffffff80;-webkit-backdrop-filter:blur(18px) saturate(160%);backdrop-filter:blur(18px) saturate(160%);border-bottom:1px solid var(--line-soft)}.brand[data-v-c3cf2a1d]{display:flex;align-items:center;gap:9px}.logo[data-v-c3cf2a1d]{width:28px;height:28px;border-radius:9px;display:grid;place-items:center;background:var(--green);color:#fff;font-weight:800;font-size:15px;box-shadow:0 4px 12px #1a9d5f52}.brand h1[data-v-c3cf2a1d]{font-size:15px;font-weight:700;letter-spacing:-.01em}.ver[data-v-c3cf2a1d]{font-size:11px;font-weight:600;color:var(--dim);padding:2px 7px;border-radius:var(--r-pill);background:#ffffffb3;border:1px solid var(--line)}.upd[data-v-c3cf2a1d]{display:grid;place-items:center;width:18px;height:18px;margin-left:-3px;border-radius:50%;font-size:11px;font-weight:700;line-height:1;color:var(--amber);background:#d69e2e1f;border:1px solid rgba(214,158,46,.35)}.upd[data-v-c3cf2a1d]:hover{background:#d69e2e33}.spacer[data-v-c3cf2a1d]{flex:1}.stat[data-v-c3cf2a1d]{display:flex;align-items:center;gap:6px;font-size:12.5px;color:var(--dim)}.stat .val[data-v-c3cf2a1d]{font-weight:700;color:var(--ink)}.stat .val.ok[data-v-c3cf2a1d]{color:var(--green)}.stat .val.bad[data-v-c3cf2a1d]{color:var(--amber)}.stat .val.dim[data-v-c3cf2a1d]{color:var(--dim)}.tbtn[data-v-c3cf2a1d]{font-size:12.5px;font-weight:600;color:var(--ink);padding:6px 11px;border-radius:var(--r-sm);border:1px solid var(--line);background:#ffffffb3;transition:color .15s,background .15s}.tbtn[data-v-c3cf2a1d]:hover{color:var(--ink);background:#fff}.tbtn.on[data-v-c3cf2a1d]{color:var(--green);background:#fff;border-color:#1a9d5f52}.nav-pop[data-v-28d78e1a]{position:fixed;z-index:var(--z-menu);width:214px;max-height:min(64vh,420px);display:flex;flex-direction:column;border-radius:var(--r-mid);background:#ffffffe0;-webkit-backdrop-filter:blur(24px) saturate(180%);backdrop-filter:blur(24px) saturate(180%);border:1px solid rgba(255,255,255,.9);box-shadow:var(--sh-pop);overflow:hidden;animation:pop-in-28d78e1a .16s ease}@keyframes pop-in-28d78e1a{0%{opacity:0;transform:translateY(-6px) scale(.985)}}.pop-head[data-v-28d78e1a]{display:flex;align-items:center;justify-content:space-between;padding:9px 11px;flex-shrink:0;font-size:11px;font-weight:700;letter-spacing:.06em;color:var(--faint);text-transform:uppercase;border-bottom:1px solid var(--line-soft)}.pop-head .x[data-v-28d78e1a]{cursor:pointer;font-size:11px;color:var(--faint);padding:2px 4px}.pop-head .x[data-v-28d78e1a]:hover{color:var(--ink)}.pop-body[data-v-28d78e1a]{padding:6px;overflow-y:auto}.pop-sec[data-v-28d78e1a]{padding:9px 6px 5px;font-size:11px;font-weight:700;letter-spacing:.08em;color:var(--dim);text-transform:uppercase}.pop-item[data-v-28d78e1a]{width:100%;display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 9px;border-radius:var(--r-sm);transition:background .14s}.pop-item .lbl[data-v-28d78e1a]{font-size:13px;font-weight:600;color:var(--ink)}.pop-item .tag[data-v-28d78e1a]{font-size:11px;font-weight:600;color:var(--dim)}.pop-item[data-v-28d78e1a]:hover{background:var(--surface-2)}.pop-item.on[data-v-28d78e1a]{background:var(--accent-soft)}.pop-item.on .lbl[data-v-28d78e1a]{color:var(--accent);font-weight:600}.rail-left[data-v-310e54b3]{padding:12px;gap:10px}.new-sess[data-v-310e54b3]{display:flex;align-items:center;justify-content:center;gap:6px;padding:9px 12px;flex-shrink:0;font-size:13px;font-weight:600;color:var(--green);background:#1a9d5f1a;border-radius:var(--r-sm);transition:background .16s,transform .1s}.new-sess[data-v-310e54b3]:hover{background:#1a9d5f2b}.new-sess[data-v-310e54b3]:active{transform:scale(.985)}.rail-nav[data-v-310e54b3]{display:flex;flex-direction:column;gap:2px;flex-shrink:0}.rail-row[data-v-310e54b3]{position:relative;display:flex;align-items:center}.rail-item[data-v-310e54b3]{flex:1;min-width:0;display:flex;align-items:center;gap:9px;padding:7px 9px;border-radius:var(--r-sm);border:1px solid transparent;transition:background .14s,border-color .14s}.rail-item .ic[data-v-310e54b3]{width:15px;height:15px;display:grid;place-items:center;color:var(--dim);flex-shrink:0}.rail-item .ic svg[data-v-310e54b3]{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}.rail-item b[data-v-310e54b3]{flex:1;min-width:0;text-align:left;font-size:13px;font-weight:600;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rail-row:hover .rail-item[data-v-310e54b3]{background:#fff9}.rail-row.on .rail-item[data-v-310e54b3]{background:#ffffffeb;border-color:var(--line);box-shadow:0 1px 2px #17201a0d}.rail-row.on .rail-item .ic[data-v-310e54b3]{color:var(--green)}.rail-row.on .rail-item b[data-v-310e54b3]{font-weight:600}.rail-row.sub .rail-item[data-v-310e54b3]{background:#ffffff80}.rail-row.sub .rail-item .ic[data-v-310e54b3]{color:var(--green)}.beta[data-v-310e54b3]{flex-shrink:0;font-size:10px;font-weight:700;letter-spacing:.08em;color:var(--amber);padding:2px 5px;border-radius:4px;background:#c774001f}.rail-acts[data-v-310e54b3]{position:absolute;right:5px;top:50%;transform:translateY(-50%);display:flex;gap:2px;padding-left:14px;background:linear-gradient(90deg,#fff0,#fffffff0 34%);opacity:0;transition:opacity .13s;pointer-events:none}.rail-row:hover .rail-acts[data-v-310e54b3],.rail-acts[data-v-310e54b3]:focus-within{opacity:1;pointer-events:auto}.act[data-v-310e54b3]{width:21px;height:21px;display:grid;place-items:center;border-radius:6px;color:var(--dim);transition:background .13s,color .13s}.act svg[data-v-310e54b3]{width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}.act[data-v-310e54b3]:hover{background:#1a9d5f1f;color:var(--green)}.cur[data-v-310e54b3]{font-size:11.5px;color:var(--dim);flex-shrink:0}.caret[data-v-310e54b3]{font-size:10.5px;color:var(--dim);transition:transform .18s;flex-shrink:0}.caret.open[data-v-310e54b3]{transform:rotate(180deg)}.sec-head[data-v-310e54b3]{display:flex;align-items:center;justify-content:space-between;padding:2px 4px 0;font-size:11px;font-weight:700;letter-spacing:.06em;color:var(--dim);text-transform:uppercase;flex-shrink:0}.sec-head em[data-v-310e54b3]{font-size:11px;font-weight:700;color:var(--ink);padding:2px 7px;border-radius:var(--r-pill);background:#ffffffdb;border:1px solid var(--line)}.ns-list[data-v-310e54b3]{display:flex;flex-direction:column;gap:3px;flex:1;margin:0 -4px;padding:0 4px}.rail-foot[data-v-310e54b3]{flex-shrink:0;margin-top:2px;padding-top:8px;border-top:1px solid var(--line)}.ns-row[data-v-310e54b3]{position:relative;display:flex;align-items:center;border-radius:var(--r-sm);border:1px solid transparent;transition:background .14s,border-color .14s}.ns-row[data-v-310e54b3]:hover{background:#ffffffa8}.ns-row.on[data-v-310e54b3]{background:#ffffffeb;border-color:var(--line);box-shadow:0 1px 2px #17201a0d}.ns-item[data-v-310e54b3]{flex:1;min-width:0;text-align:left;display:flex;flex-direction:column;gap:2px;padding:8px 10px}.ns-item b[data-v-310e54b3]{font-size:13px;font-weight:600;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ns-item span[data-v-310e54b3]{font-size:11.5px;color:var(--dim)}.ns-acts[data-v-310e54b3]{position:absolute;right:4px;top:50%;transform:translateY(-50%);display:flex;gap:2px;padding-left:16px;background:linear-gradient(90deg,#fff0,#fffffff5 36%);opacity:0;pointer-events:none;transition:opacity .13s}.ns-row:hover .ns-acts[data-v-310e54b3],.ns-acts[data-v-310e54b3]:focus-within{opacity:1;pointer-events:auto}.act.del[data-v-310e54b3]:hover{background:#b4443a1f;color:#b4443a}.ns-edit[data-v-310e54b3]{width:100%;padding:7px 9px;font-size:12.5px;color:var(--ink);background:#fff;border:1px solid var(--green);border-radius:var(--r-sm);outline:none}.cli[data-v-045bb22d]{flex:1;min-width:0;display:flex;flex-direction:column;margin:8px;border-radius:var(--r-mid);overflow:hidden;background:var(--cli-bg);border:1px solid rgba(255,255,255,.06);box-shadow:0 18px 44px #0b0f0a57}.term-bar[data-v-045bb22d]{display:flex;align-items:center;gap:6px;flex-shrink:0;padding:9px 12px;background:var(--cli-bg-2);border-bottom:1px solid var(--cli-line)}.tl[data-v-045bb22d]{width:9px;height:9px;border-radius:50%;background:#ffffff24}.tl[data-v-045bb22d]:first-child{background:#ff685c9e}.tl[data-v-045bb22d]:nth-child(2){background:#e2c07a9e}.tl[data-v-045bb22d]:nth-child(3){background:#7fe08c94}.term-bar .path[data-v-045bb22d]{margin-left:8px;font-family:var(--mono);font-size:11.5px;color:var(--cli-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.term-bar .lang[data-v-045bb22d]{margin-left:auto;flex-shrink:0;font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.06em;color:var(--cli-bg);background:var(--cli-acc);padding:2px 8px;border-radius:var(--r-pill)}.term-body[data-v-045bb22d]{flex:1;overflow:auto;padding:10px 0 16px;font-family:var(--mono);font-size:12.5px;line-height:1.72}.term-body[data-v-045bb22d]::-webkit-scrollbar-thumb{background:#ffffff29}.row[data-v-045bb22d]{display:flex;white-space:pre}.row[data-v-045bb22d]:hover{background:#ffffff09}.no[data-v-045bb22d]{flex-shrink:0;width:48px;text-align:right;padding-right:14px;color:#ffffff6b;user-select:none}.code[data-v-045bb22d]{color:var(--cli-ink);padding-right:24px}.read[data-v-045bb22d]{flex:1;overflow-y:auto;padding:34px 8px 60px}.read-head[data-v-045bb22d],.read-body[data-v-045bb22d]{max-width:68ch;margin:0 auto}.read-head[data-v-045bb22d]{padding:0 20px 18px;border-bottom:1px solid var(--line-soft)}.read-head h2[data-v-045bb22d]{font-family:var(--serif);font-size:23px;font-weight:600;letter-spacing:-.01em;line-height:1.4}.read-meta[data-v-045bb22d]{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:8px;font-size:11.5px;color:var(--dim)}.read-meta code[data-v-045bb22d]{font-family:var(--mono);font-size:11.5px;color:var(--ink)}.read-body[data-v-045bb22d]{padding:20px 20px 0}.read-body[data-v-045bb22d] p{font-family:var(--serif);font-size:15px;line-height:1.92;color:#24302a;margin-bottom:15px;text-align:justify}.read-body[data-v-045bb22d] h3,.read-body[data-v-045bb22d] h4,.read-body[data-v-045bb22d] h5{font-family:var(--serif);color:var(--ink);line-height:1.5;margin:22px 0 8px}.read-body[data-v-045bb22d] h3{font-size:18px;font-weight:650}.read-body[data-v-045bb22d] h4{font-size:15.5px;font-weight:600}.read-body[data-v-045bb22d] h5{font-size:12px;font-weight:700;letter-spacing:.06em;color:var(--dim);text-transform:uppercase}.read-body[data-v-045bb22d] ul{margin:0 0 15px;padding-left:18px}.read-body[data-v-045bb22d] li{font-family:var(--serif);font-size:14.5px;line-height:1.85;color:#24302a;margin-bottom:5px}.read-body[data-v-045bb22d] li::marker{color:var(--faint)}.read-body[data-v-045bb22d] blockquote{margin:0 0 15px;padding:8px 14px;border-left:2px solid var(--line);font-family:var(--serif);font-size:14px;line-height:1.85;color:var(--dim)}.read-body[data-v-045bb22d] hr{margin:20px 0;border:0;border-top:1px solid var(--line-soft)}.read-body[data-v-045bb22d] strong{font-weight:650;color:#16201b}.read-body[data-v-045bb22d] code{font-family:var(--mono);font-size:12px;padding:1px 5px;border-radius:4px;background:#1a9d5f17;color:var(--green)}.read-body[data-v-045bb22d] a{color:var(--green);border-bottom:1px solid rgba(26,157,95,.3)}.read-body[data-v-045bb22d] ol{margin:0 0 15px;padding-left:20px}.read-body[data-v-045bb22d] h2{font-family:var(--serif);font-size:20px;font-weight:650;line-height:1.5;color:var(--ink);margin:26px 0 10px}.read-body[data-v-045bb22d] pre{margin:0 0 15px;padding:11px 13px;background:var(--cli-bg);border-radius:var(--r-sm);overflow-x:auto}.read-body[data-v-045bb22d] pre code{padding:0;background:none;color:var(--cli-ink);font-size:12px;line-height:1.72}.read-body[data-v-045bb22d] table{width:100%;border-collapse:collapse;margin:0 0 15px}.read-body[data-v-045bb22d] th,.read-body[data-v-045bb22d] td{padding:6px 10px;border-bottom:1px solid var(--line-soft);font-size:13px;text-align:left;vertical-align:top}.read-body[data-v-045bb22d] th{font-size:11.5px;font-weight:600;color:var(--dim)}.ms[data-v-efc12ae7]{flex:1;min-height:0;overflow-y:auto}.ms-in[data-v-efc12ae7]{max-width:780px;margin:0 auto;padding:18px 22px 26px;display:flex;flex-direction:column;gap:20px}.msg.user[data-v-efc12ae7]{display:flex;justify-content:flex-end}.u-body[data-v-efc12ae7]{max-width:78%;display:flex;flex-direction:column;align-items:flex-end;gap:5px}.u-atts[data-v-efc12ae7]{display:flex;gap:5px;flex-wrap:wrap;justify-content:flex-end}.u-att[data-v-efc12ae7]{font-size:11.5px;padding:3px 9px;border-radius:var(--r-pill);color:var(--ink);background:#ffffffe6;border:1px solid var(--line)}.u-text[data-v-efc12ae7]{padding:9px 14px;border-radius:14px 14px 4px;font-size:13.5px;line-height:1.65;color:var(--ink);background:linear-gradient(180deg,#fff,#f2f8ee);border:1px solid var(--line-soft);box-shadow:0 1px 2px #17201a0d;white-space:pre-wrap;word-break:break-word}.u-time[data-v-efc12ae7]{font-size:11px;color:var(--faint);font-family:var(--mono)}.a-body[data-v-efc12ae7]{display:flex;flex-direction:column;gap:9px}.a-head[data-v-efc12ae7]{display:flex;align-items:center;gap:7px;font-size:11.5px;color:var(--dim)}.avatar[data-v-efc12ae7]{width:7px;height:7px;border-radius:50%;background:var(--green);box-shadow:0 0 6px #1a9d5f80}.avatar.live[data-v-efc12ae7]{animation:pulse-efc12ae7 1.1s ease-in-out infinite}@keyframes pulse-efc12ae7{50%{opacity:.35;transform:scale(.8)}}.who[data-v-efc12ae7]{font-weight:700;color:var(--ink)}.a-head .stage[data-v-efc12ae7]{padding:2px 8px;border-radius:var(--r-pill);background:#1a9d5f1f;color:var(--green);font-family:var(--mono);font-size:10.5px;font-weight:600}.a-head .live-tag[data-v-efc12ae7]{color:var(--green);font-size:10.5px;font-weight:600}.a-time[data-v-efc12ae7]{margin-left:auto;font-family:var(--mono)}.a-text[data-v-efc12ae7]{font-size:13.5px;line-height:1.8;color:var(--ink);word-break:break-word;white-space:pre-wrap}.a-text[data-v-efc12ae7] pre,.a-text[data-v-efc12ae7] pre code{white-space:pre}.a-text.waiting[data-v-efc12ae7]{color:var(--faint)}.a-text[data-v-efc12ae7] p{margin:0 0 8px}.a-text[data-v-efc12ae7] p:last-child{margin-bottom:0}.a-text[data-v-efc12ae7] h2,.a-text[data-v-efc12ae7] h3,.a-text[data-v-efc12ae7] h4,.a-text[data-v-efc12ae7] h5{font-family:var(--serif);color:var(--ink);line-height:1.5;margin:14px 0 6px}.a-text[data-v-efc12ae7] h2{font-size:16.5px;font-weight:650}.a-text[data-v-efc12ae7] h3{font-size:15px;font-weight:650}.a-text[data-v-efc12ae7] h4,.a-text[data-v-efc12ae7] h5{font-size:13.5px;font-weight:650}.a-text[data-v-efc12ae7] ul,.a-text[data-v-efc12ae7] ol{margin:0 0 8px;padding-left:20px}.a-text[data-v-efc12ae7] li{margin-bottom:3px}.a-text[data-v-efc12ae7] li::marker{color:var(--faint)}.a-text[data-v-efc12ae7] blockquote{margin:0 0 8px;padding:3px 10px;border-left:2px solid var(--line);color:var(--dim)}.a-text[data-v-efc12ae7] hr{margin:12px 0;border:0;border-top:1px solid var(--line-soft)}.a-text[data-v-efc12ae7] strong{font-weight:650;color:var(--ink)}.a-text[data-v-efc12ae7] del{color:var(--faint)}.a-text[data-v-efc12ae7] code{font-family:var(--mono);font-size:12px;padding:1px 5px;border-radius:4px;background:#1a9d5f17;color:var(--green);white-space:pre-wrap}.a-text[data-v-efc12ae7] pre{margin:8px 0;padding:10px 12px;background:#17211c;border-radius:var(--r-sm);overflow-x:auto}.a-text[data-v-efc12ae7] pre code{padding:0;background:none;color:#dfe9e2;font-size:12px;line-height:1.7;white-space:pre}.a-text[data-v-efc12ae7] table{display:block;overflow-x:auto;border-collapse:collapse;margin:8px 0;font-size:12.5px}.a-text[data-v-efc12ae7] th,.a-text[data-v-efc12ae7] td{padding:4px 9px;border-bottom:1px solid var(--line-soft);text-align:left;vertical-align:top}.a-text[data-v-efc12ae7] th{color:var(--dim);font-size:11.5px}.a-text[data-v-efc12ae7] a{color:var(--green);border-bottom:1px solid rgba(26,157,95,.3)}.a-text.streaming[data-v-efc12ae7]>:last-child:after{content:"";display:inline-block;width:6px;height:13px;margin-left:3px;vertical-align:-2px;background:var(--green);animation:blink-efc12ae7 1s step-end infinite}@keyframes blink-efc12ae7{50%{opacity:0}}.dots[data-v-efc12ae7]{animation:blink-efc12ae7 1.2s step-end infinite;letter-spacing:2px}.fold[data-v-efc12ae7]{border-radius:var(--r-sm);background:#f1f4efb3;border:1px solid var(--line-soft);overflow:hidden}.fold summary[data-v-efc12ae7]{display:flex;align-items:center;gap:7px;padding:7px 11px;cursor:pointer;font-size:12px;font-weight:600;color:var(--ink);list-style:none}.fold summary[data-v-efc12ae7]::-webkit-details-marker{display:none}.fold summary[data-v-efc12ae7]:before{content:"▸";font-size:10px;color:var(--dim)}.fold[open] summary[data-v-efc12ae7]:before{content:"▾"}.fold summary em[data-v-efc12ae7]{font-style:normal;font-family:var(--mono);font-size:10.5px;color:var(--dim)}.fold-body[data-v-efc12ae7]{margin:0;padding:2px 12px 10px;font-size:11.5px;line-height:1.7;color:var(--dim);white-space:pre-wrap;word-break:break-word;max-height:280px;overflow-y:auto}.fold-body ol[data-v-efc12ae7]{margin:0;padding-left:18px}.fold.think .fold-body[data-v-efc12ae7]{font-family:var(--mono);font-size:11px}.fold.ev .fold-body[data-v-efc12ae7]{padding-bottom:8px}.ev-row[data-v-efc12ae7]{display:flex;align-items:baseline;gap:7px;padding:3px 0;flex-wrap:wrap}.ev-row b[data-v-efc12ae7]{font-size:12px;font-weight:700;color:var(--ink)}.ev-st[data-v-efc12ae7]{font-family:var(--mono);font-size:10.5px;color:var(--dim)}.ev-dot[data-v-efc12ae7]{width:5px;height:5px;border-radius:50%;background:var(--amber);flex-shrink:0}.ev-dot.verified[data-v-efc12ae7]{background:var(--green)}.ev-dot.failed[data-v-efc12ae7]{background:var(--red)}.ev-link[data-v-efc12ae7],.ev-file[data-v-efc12ae7]{font-size:11.5px;color:var(--accent);text-decoration:none}.ev-file[data-v-efc12ae7]{color:var(--dim)}.ev-link[data-v-efc12ae7]:hover{text-decoration:underline}.sres[data-v-efc12ae7]{border-radius:var(--r-sm);background:#ffffffbf;border:1px solid var(--line-soft);overflow:hidden}.sres-h[data-v-efc12ae7]{display:flex;align-items:center;gap:10px;padding:7px 11px;border-bottom:1px solid var(--line-soft);font-size:10.5px;color:var(--dim);font-family:var(--mono)}.sres-h .sroute[data-v-efc12ae7]{padding:2px 7px;border-radius:var(--r-pill);color:var(--green);background:#1a9d5f1f;font-weight:700}.srow[data-v-efc12ae7]{display:flex;align-items:baseline;gap:8px;padding:6px 10px;border-bottom:1px solid rgba(23,32,26,.04)}.srow[data-v-efc12ae7]:last-child{border-bottom:0}.ssrc[data-v-efc12ae7]{flex-shrink:0;font-size:10.5px;font-weight:600;padding:2px 6px;border-radius:4px;color:var(--dim);background:var(--surface-2)}.ssrc.knowledge[data-v-efc12ae7]{color:var(--green);background:#1a9d5f1f}.ssrc.web[data-v-efc12ae7]{color:var(--accent);background:#0a84ff1f}.srow p[data-v-efc12ae7]{flex:1;min-width:0;font-size:12px;line-height:1.6;color:var(--ink);word-break:break-word}.srow em[data-v-efc12ae7]{font-style:normal;font-family:var(--mono);font-size:10.5px;color:var(--dim)}.notice[data-v-efc12ae7]{padding:6px 10px;border-radius:var(--r-sm);font-size:11.5px;color:var(--amber);background:#c88c2817}.a-acts[data-v-efc12ae7]{display:flex;gap:6px;opacity:0;transition:opacity .13s}.msg:hover .a-acts[data-v-efc12ae7],.a-acts[data-v-efc12ae7]:focus-within{opacity:1}.mini[data-v-efc12ae7]{font-size:11.5px;padding:3px 10px;border-radius:var(--r-pill);color:var(--dim);background:#ffffffdb;border:1px solid var(--line);transition:color .13s,border-color .13s,background .13s}.mini[data-v-efc12ae7]:hover{color:var(--green);background:#fff;border-color:#1a9d5f59}@media(hover:none){.a-acts[data-v-efc12ae7]{opacity:1}}.err[data-v-efc12ae7]{padding:7px 10px;border-radius:var(--r-sm);font-size:11.5px;color:var(--red);background:#e5484d12;border:1px solid rgba(229,72,77,.16);white-space:pre-wrap}.panel[data-v-a7fcdff6]{flex:1;min-width:0;min-height:0;display:flex;flex-direction:column}.ph[data-v-a7fcdff6]{display:flex;align-items:center;gap:10px;flex-shrink:0;padding:11px 16px 10px;border-bottom:1px solid var(--line-soft)}.tt[data-v-a7fcdff6]{display:flex;align-items:baseline;gap:8px;min-width:0}.tt h2[data-v-a7fcdff6]{font-size:13.5px;font-weight:650;letter-spacing:-.01em;color:var(--ink)}.rf[data-v-a7fcdff6]{margin-left:auto;flex-shrink:0;padding:5px 11px;border-radius:var(--r-pill);font-size:11.5px;font-weight:600;color:var(--ink);background:#ffffffdb;border:1px solid var(--line);transition:color .14s,background .14s,border-color .14s}.rf[data-v-a7fcdff6]:hover{color:var(--green);background:#fff;border-color:#1a9d5f59}.rf.spin[data-v-a7fcdff6]{color:var(--dim)}.pb[data-v-a7fcdff6]{flex:1;padding:14px 16px 18px}.pn-row.static[data-v-cdbea439]:hover{background:transparent}.id[data-v-828bb5bf]{font-family:var(--mono);font-size:11px;font-style:normal;color:var(--dim);margin-left:4px}.det[data-v-828bb5bf]{margin:2px 0 8px 10px;padding:8px 12px;border-left:2px solid var(--line)}.kv[data-v-828bb5bf]{display:flex;justify-content:space-between;gap:12px;padding:4px 0;font-size:12px;color:var(--dim)}.kv b[data-v-828bb5bf]{font-weight:600;color:var(--ink);font-family:var(--mono);font-size:11.5px;text-align:right}.sw[data-v-5b252a48]{flex-shrink:0;padding:4px 10px;border-radius:var(--r-pill);font-size:11px;font-family:var(--mono);background:var(--surface-2);color:var(--faint);border:1px solid transparent}.sw.on[data-v-5b252a48]{background:#1a9d5f1f;color:var(--green)}.sw[data-v-5b252a48]:disabled{opacity:.5}.mbtn[data-v-5b252a48]{flex-shrink:0;padding:4px 10px;border-radius:var(--r-pill);font-size:11px;font-family:var(--mono);color:var(--dim);background:#fffc;border:1px solid var(--line-soft);transition:color .13s,border-color .13s,background .13s}.mbtn[data-v-5b252a48]:hover{color:var(--green);border-color:#1a9d5f59;background:#fff}.mbtn.on[data-v-5b252a48]{color:var(--green);background:#1a9d5f1a;border-color:#1a9d5f66}.mbtn.danger[data-v-5b252a48]:hover{color:var(--red);border-color:#e5484d59}.mbtn[data-v-5b252a48]:disabled{opacity:.5}.tk-form[data-v-5b252a48],.tk-test[data-v-5b252a48]{margin:4px 0 6px;padding:11px 12px;border-radius:var(--r-mid);background:#ffffffdb;border:1px solid var(--line-soft)}.tk-test[data-v-5b252a48]{display:flex;flex-direction:column;gap:7px}.tk-test .f span em[data-v-5b252a48]{font-style:normal;font-weight:600;font-family:var(--mono);font-size:11px;color:var(--dim)}.frow[data-v-5b252a48]{display:flex;gap:8px}.frow .f[data-v-5b252a48]{flex:1 1 0;min-width:0}.frow .f.grow[data-v-5b252a48]{flex:2 1 0}.frow .f.narrow[data-v-5b252a48]{flex:0 1 104px}.f[data-v-5b252a48]{display:block;margin-bottom:7px}.f>span[data-v-5b252a48]{display:block;margin-bottom:3px;font-size:11px;color:var(--faint)}.f input[data-v-5b252a48],.f select[data-v-5b252a48],.f textarea[data-v-5b252a48]{display:block;width:100%;padding:5px 8px;border-radius:6px;font-family:inherit;font-size:12.5px;color:var(--ink);background:#17201a0a;border:1px solid transparent;outline:none;transition:border-color .14s,background .14s}.f textarea[data-v-5b252a48]{resize:vertical;line-height:1.6}.f input[data-v-5b252a48]:focus,.f select[data-v-5b252a48]:focus,.f textarea[data-v-5b252a48]:focus{border-color:var(--line);background:#fff}.f input[data-v-5b252a48]::placeholder,.f textarea[data-v-5b252a48]::placeholder{color:var(--dim)}.f input[type=file][data-v-5b252a48]{padding:4px 6px;font-size:11.5px;color:var(--dim)}.chk[data-v-5b252a48]{display:flex;align-items:center;gap:6px;margin-bottom:6px;font-size:12px;color:var(--dim)}.chk input[data-v-5b252a48]{accent-color:var(--green)}.fbar[data-v-5b252a48]{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:3px}.go[data-v-5b252a48]{flex-shrink:0;padding:6px 15px;border-radius:var(--r-pill);font-size:12.5px;font-weight:600;color:#fff;background:var(--green);transition:opacity .15s,transform .12s}.go[data-v-5b252a48]:hover:not(:disabled){opacity:.9}.go[data-v-5b252a48]:active:not(:disabled){transform:scale(.97)}.go[data-v-5b252a48]:disabled{background:#17201a1f;color:var(--faint)}.go.warn[data-v-5b252a48]{background:var(--amber)}.tip[data-v-5b252a48]{flex:1 1 auto;min-width:0;font-size:11.5px;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tip.dim[data-v-5b252a48]{color:var(--faint)}.tip.ok[data-v-5b252a48]{color:var(--green)}.tip.bad[data-v-5b252a48]{color:var(--red);white-space:normal}.tk-res[data-v-5b252a48]{margin-top:4px;padding:8px 10px;border-radius:var(--r-sm);background:#17201a08;border:1px solid var(--line-soft)}.tk-res .r-h[data-v-5b252a48]{display:flex;gap:6px;font-size:12px;color:var(--dim)}.tk-res .r-h b[data-v-5b252a48]{font-family:var(--mono);font-size:11.5px;color:var(--ink)}.tk-res.bad .r-h b[data-v-5b252a48]{color:var(--amber)}.tk-res pre[data-v-5b252a48]{margin:6px 0 0;max-height:220px;overflow:auto;font-family:var(--mono);font-size:11.5px;line-height:1.6;color:var(--dim);white-space:pre-wrap;word-break:break-word}.pn-note.meta[data-v-5b252a48]{margin-top:6px}.pn-note.meta.bad[data-v-5b252a48]{color:var(--red)}.acts[data-v-1a916225]{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.btn[data-v-1a916225]{padding:7px 13px;border-radius:var(--r-sm);font-size:11.5px;color:var(--green);font-weight:600;background:#1a9d5f1a;border:1px solid rgba(26,157,95,.16)}.btn[data-v-1a916225]:hover{background:#1a9d5f29}.hint[data-v-1a916225]{font-size:11.5px;color:var(--dim)}.kb-in[data-v-11ecf8c3]{margin-bottom:14px;border-radius:var(--r-mid);background:#ffffffe6;border:1px solid rgba(26,157,95,.16);overflow:hidden;transition:border-color .15s,box-shadow .15s}.kb-in[data-v-11ecf8c3]:focus-within{border-color:#1a9d5f61;box-shadow:0 2px 12px #1a9d5f14}.kb-in textarea[data-v-11ecf8c3]{display:block;width:100%;padding:11px 12px 6px;font-family:var(--sans);font-size:13px;line-height:1.7;color:var(--ink);background:transparent;border:0;outline:none;resize:vertical;min-height:62px}.kb-in textarea[data-v-11ecf8c3]::placeholder{color:var(--dim)}.kb-bar[data-v-11ecf8c3]{display:flex;align-items:center;gap:6px;padding:6px 8px 8px 12px;border-top:1px solid var(--line-soft)}.kb-bar input[data-v-11ecf8c3]{padding:4px 8px;border-radius:6px;font-size:11.5px;color:var(--ink);background:#17201a0a;border:1px solid transparent;outline:none}.kb-bar input[data-v-11ecf8c3]:focus{border-color:var(--line);background:#fff}.kb-bar input[data-v-11ecf8c3]::placeholder{color:var(--dim)}.f-title[data-v-11ecf8c3]{flex:1 1 110px;max-width:190px;min-width:0}.f-domain[data-v-11ecf8c3]{flex:0 1 96px;min-width:60px}.tip[data-v-11ecf8c3]{flex:1 1 auto;min-width:0;font-size:11.5px;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tip.dim[data-v-11ecf8c3]{color:var(--faint)}.tip.ok[data-v-11ecf8c3]{color:var(--green)}.tip.bad[data-v-11ecf8c3]{color:var(--red)}.go[data-v-11ecf8c3]{flex-shrink:0;padding:6px 15px;border-radius:var(--r-pill);font-size:11.5px;font-weight:600;color:#fff;background:var(--green);transition:opacity .15s,transform .12s}.go[data-v-11ecf8c3]:hover:not(:disabled){opacity:.9}.go[data-v-11ecf8c3]:active:not(:disabled){transform:scale(.97)}.go[data-v-11ecf8c3]:disabled{background:#17201a12;color:var(--dim);border:1px solid var(--line-soft)}.meta[data-v-11ecf8c3]{margin-top:8px}.sec-gap[data-v-11ecf8c3]{margin-top:14px}.pn-note.meta.bad[data-v-11ecf8c3]{color:var(--red)}.kb-find[data-v-11ecf8c3]{display:flex;align-items:center;gap:6px;margin-bottom:8px}.kb-find input[data-v-11ecf8c3]{flex:1 1 auto;min-width:0;padding:7px 10px;border-radius:var(--r-sm);font-size:12.5px;color:var(--ink);background:#ffffffe6;border:1px solid var(--line-soft);outline:none;transition:border-color .15s}.kb-find input[data-v-11ecf8c3]:focus{border-color:#1a9d5f73}.kb-find input[data-v-11ecf8c3]::placeholder{color:var(--dim)}.kb-find select[data-v-11ecf8c3]{padding:6px 8px;border-radius:var(--r-sm);font-size:12px;font-weight:600;color:var(--ink);background:#ffffffe6;border:1px solid var(--line);outline:none}.kb-find .go[data-v-11ecf8c3]{padding:6px 13px}.kb-hits[data-v-11ecf8c3]{display:flex;flex-direction:column;gap:2px}.kb-hit[data-v-11ecf8c3]{display:flex;align-items:center;gap:8px;width:100%;padding:7px 9px;border-radius:var(--r-sm);text-align:left;transition:background .13s}.kb-hit[data-v-11ecf8c3]:hover{background:#ffffffbf}.kb-hit .m[data-v-11ecf8c3]{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}.kb-hit .m b[data-v-11ecf8c3]{font-size:13px;font-weight:600;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kb-hit .m .s[data-v-11ecf8c3]{font-size:11.5px;color:var(--dim)}.kb-hit .r[data-v-11ecf8c3]{flex-shrink:0;font-size:11.5px;color:var(--dim);font-variant-numeric:tabular-nums}.pn-chips.gap[data-v-11ecf8c3]{margin-top:8px}.kb-ab[data-v-11ecf8c3]{margin-top:10px;padding:10px 12px;border-radius:var(--r-mid);background:#ffffffdb;border:1px solid var(--line-soft)}.kb-ab-h[data-v-11ecf8c3]{display:flex;align-items:baseline;gap:6px;font-size:13px;font-weight:600;color:var(--ink);margin-bottom:5px}.kb-ab-h .v[data-v-11ecf8c3]{font-size:11.5px;font-weight:600;color:var(--dim)}.rv-grid[data-v-1a353dab]{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,1fr);gap:22px;align-items:start}@container (max-width: 820px){.rv-grid[data-v-1a353dab]{grid-template-columns:minmax(0,1fr);gap:20px}}.card[data-v-1a353dab]{padding:16px 18px 18px;border-radius:var(--r-md);background:#ffffffdb;border:1px solid var(--line-soft);box-shadow:0 1px 3px #17201a0a}.ct[data-v-1a353dab]{display:flex;align-items:center;gap:8px;margin-bottom:10px}.ct .dim[data-v-1a353dab]{font-size:11.5px;color:var(--dim)}.pn-tag.new[data-v-1a353dab]{background:#1a9d5f1f;color:var(--green)}.card h3[data-v-1a353dab]{font-family:var(--serif);font-size:17px;line-height:1.62;font-weight:600;color:var(--ink);margin-bottom:12px}.sum[data-v-1a353dab]{font-size:13px;line-height:1.8;color:var(--dim);margin-bottom:10px}.src[data-v-1a353dab]{font-size:12px;line-height:1.85;color:var(--dim);padding:9px 12px;border-left:2px solid var(--line);background:#17201a06;margin-bottom:14px;white-space:pre-wrap}.hint[data-v-1a353dab]{font-size:12px;color:var(--dim);margin:4px 0 14px;line-height:1.7}.err[data-v-1a353dab]{margin-top:10px;font-size:11px;color:#b4453c}.act[data-v-1a353dab]{display:inline-block;padding:8px 18px;border-radius:var(--r-pill);font-size:12px;font-weight:600;color:#fff;background:var(--green);transition:opacity .15s}.act[data-v-1a353dab]:hover{opacity:.9}.grades[data-v-1a353dab]{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:7px}.g[data-v-1a353dab]{display:flex;flex-direction:column;align-items:center;gap:2px;padding:9px 4px 8px;border-radius:var(--r-sm);font-size:12px;font-weight:600;color:var(--ink);background:#ffffffe6;border:1px solid var(--line-soft);transition:border-color .14s,background .14s,color .14s}.g span[data-v-1a353dab]{font-size:11px;font-weight:500;color:var(--dim)}.g[data-v-1a353dab]:hover{border-color:#1a9d5f6b;background:#1a9d5f0f}.g0[data-v-1a353dab]:hover{border-color:#b4453c6b;background:#b4453c0f}.done[data-v-1a353dab],.idle[data-v-1a353dab]{padding:14px 2px}.done b[data-v-1a353dab]{font-size:13px}.done p[data-v-1a353dab],.idle p[data-v-1a353dab]{font-size:12px;line-height:1.8;color:var(--dim);margin:6px 0 14px}.idle b[data-v-1a353dab]{color:var(--ink)}.curve[data-v-1a353dab]{display:block;width:100%;height:130px;overflow:visible}.curve .ref[data-v-1a353dab]{stroke:#17201a1f;stroke-width:1;stroke-dasharray:3 3;vector-effect:non-scaling-stroke}.curve .line[data-v-1a353dab]{fill:none;stroke:var(--green);stroke-width:1.8;vector-effect:non-scaling-stroke}.curve .dot[data-v-1a353dab]{fill:var(--green);stroke:#fff;stroke-width:1.5;vector-effect:non-scaling-stroke}.axis[data-v-1a353dab]{display:flex;justify-content:space-between;align-items:baseline;margin-top:5px;font-size:11px;font-weight:600;color:var(--dim)}.axis .dim[data-v-1a353dab]{font-family:var(--mono);font-size:11px}.note[data-v-1a353dab]{margin-top:8px;font-size:11.5px;line-height:1.75;color:var(--dim)}.note.warn[data-v-1a353dab]{padding:7px 9px;border-radius:var(--r-sm);background:#c48a1a12;border-left:2px solid rgba(196,138,26,.45);color:#8a6a24}.load[data-v-1a353dab]{display:flex;align-items:flex-end;gap:6px;padding:8px 2px 0}.load .bar[data-v-1a353dab]{flex:1;display:flex;flex-direction:column;align-items:center;gap:3px;min-width:0}.load i[data-v-1a353dab]{display:block;width:100%;max-width:26px;border-radius:2px 2px 0 0;background:#1a9d5f8c}.load i.zero[data-v-1a353dab]{background:#17201a24}.load span[data-v-1a353dab]{font-size:10.5px;color:var(--dim)}.load em[data-v-1a353dab]{font-style:normal;font-family:var(--mono);font-size:10.5px;font-weight:600;color:var(--ink)}.sec-gap[data-v-6518b64e]{margin-top:14px}.del[data-v-6518b64e]{flex-shrink:0;padding:4px 10px;border-radius:var(--r-pill);font-size:11px;font-weight:600;color:var(--dim);background:var(--surface-2);border:1px solid var(--line-soft)}.del[data-v-6518b64e]:hover{color:var(--red);background:#e5484d1a}.del[data-v-6518b64e]:disabled{opacity:.5}.card[data-v-01d0339a]{border-radius:var(--r-mid);background:#ffffffd1;border:1px solid var(--line-soft);box-shadow:0 1px 2px #17201a0a;padding:4px 6px 6px;margin-bottom:12px}.grp[data-v-01d0339a]{padding:8px 8px 6px;font-size:11px;font-weight:700;letter-spacing:.08em;color:var(--dim);text-transform:uppercase}.row[data-v-01d0339a]{width:100%;display:flex;align-items:center;gap:10px;padding:8px;border-radius:var(--r-sm);text-align:left;transition:background .14s}.row[data-v-01d0339a]:hover{background:#ffffffe6}.row.static[data-v-01d0339a]:hover{background:transparent}.row .ic[data-v-01d0339a]{width:22px;height:22px;display:grid;place-items:center;color:var(--dim);flex-shrink:0}.row .ic svg[data-v-01d0339a]{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}.row .m[data-v-01d0339a]{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.row .m b[data-v-01d0339a]{font-size:13px;font-weight:600;color:var(--ink)}.row .m span[data-v-01d0339a]{font-size:11.5px;color:var(--dim)}.hint[data-v-01d0339a]{display:flex;align-items:center;gap:5px;font-size:12px;font-weight:600;color:var(--dim);flex-shrink:0}.hint .dot[data-v-01d0339a]{width:7px;height:7px;border-radius:50%}.chev[data-v-01d0339a]{color:var(--dim);font-size:13px;flex-shrink:0;transition:transform .16s}.chev.open[data-v-01d0339a]{transform:rotate(90deg)}.seg[data-v-01d0339a]{display:flex;gap:2px;padding:2px;border-radius:var(--r-sm);background:#17201a0d;flex-shrink:0}.seg button[data-v-01d0339a]{padding:4px 10px;border-radius:7px;font-size:11.5px;color:var(--dim);transition:background .15s,color .15s}.seg button.on[data-v-01d0339a]{background:#fff;color:var(--ink);font-weight:600;box-shadow:0 1px 2px #17201a12}.sub[data-v-01d0339a]{margin:2px 8px 6px 40px;padding:4px;border-left:1px solid var(--line-soft)}.opt[data-v-01d0339a]{width:100%;display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:7px;text-align:left;transition:background .13s}.opt[data-v-01d0339a]:hover{background:#ffffffe6}.opt.on[data-v-01d0339a]{background:#1a9d5f17}.opt .dot[data-v-01d0339a]{width:7px;height:7px;border-radius:50%;flex-shrink:0}.opt .m[data-v-01d0339a]{flex:1;min-width:0;display:flex;align-items:baseline;gap:7px}.opt .m b[data-v-01d0339a]{font-size:12.5px;font-weight:600;color:var(--ink)}.opt .m span[data-v-01d0339a]{font-size:11.5px;color:var(--dim);font-family:var(--mono)}.tip[data-v-01d0339a]{padding:4px 8px 2px;font-size:11.5px;line-height:1.6;color:var(--dim)}.tip.err[data-v-01d0339a]{color:var(--red)}.nick-edit[data-v-01d0339a]{display:flex;align-items:center;gap:6px;padding:4px 8px 2px}.nick-in[data-v-01d0339a]{flex:1;min-width:0;padding:5px 8px;border-radius:7px;font-size:12.5px;color:var(--ink);background:#fff;border:1px solid var(--line)}.nick-in[data-v-01d0339a]:focus{outline:none;border-color:#1a9d5f80}.nick-save[data-v-01d0339a]{flex-shrink:0;padding:5px 11px;border-radius:7px;font-size:12px;font-weight:600;color:#fff;background:var(--green)}.nick-save[data-v-01d0339a]:disabled{opacity:.45}.kv[data-v-01d0339a]{display:flex;align-items:center;gap:10px;padding:4px 8px}.kv kbd[data-v-01d0339a]{min-width:92px;flex-shrink:0;font-family:var(--mono);font-size:11.5px;font-weight:600;color:var(--ink);padding:2px 6px;border-radius:5px;background:#17201a0d}.kv span[data-v-01d0339a]{font-size:11.5px;color:var(--dim)}.foot[data-v-01d0339a]{display:flex;align-items:center;justify-content:space-between;margin-top:2px;padding:10px 4px 2px;border-top:1px solid var(--line-soft)}.foot .mono[data-v-01d0339a]{font-family:var(--mono);font-size:11.5px;color:var(--dim)}.foot .dev[data-v-01d0339a]{font-size:12px;color:var(--faint)}.stage[data-v-31f35103]{display:flex;flex-direction:column;min-height:0}.tabs[data-v-31f35103]{display:flex;align-items:center;gap:4px;padding:8px 10px 0;flex-shrink:0;overflow-x:auto}.tab[data-v-31f35103]{display:flex;align-items:center;gap:6px;padding:6px 11px;border-radius:var(--r-sm) var(--r-sm) 0 0;font-size:12px;color:var(--dim);border:1px solid transparent;border-bottom:0;white-space:nowrap}.tab[data-v-31f35103]:hover{color:var(--ink);background:#ffffff80}.tab.on[data-v-31f35103]{color:var(--ink);font-weight:600;background:#ffffffdb;border-color:var(--line-soft)}.tab.doc[data-v-31f35103]{max-width:260px}.tab.doc .t[data-v-31f35103]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tab.doc .x[data-v-31f35103]{font-size:11px;color:var(--dim)}.tab.doc .x[data-v-31f35103]:hover{color:var(--red)}.stage-body[data-v-31f35103]{flex:1;min-height:0;margin:0 8px 8px;border-radius:0 var(--r-mid) var(--r-mid) var(--r-mid);background:var(--mid-panel);border:1px solid rgba(26,157,95,.1);overflow:hidden;display:flex}.stage-body.bare[data-v-31f35103]{margin-top:8px;border-radius:var(--r-mid)}.core[data-v-31f35103]{flex:1;min-height:0;display:flex;flex-direction:column}.core-empty[data-v-31f35103]{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:18px;position:relative;padding:24px 18px;overflow-y:auto}.core-cap[data-v-31f35103]{font-size:12.5px;color:var(--dim);line-height:1.7;text-align:center}.hero[data-v-31f35103]{text-align:center}.hero-t[data-v-31f35103]{font-size:19px;font-weight:600;letter-spacing:-.02em;color:var(--ink);margin-bottom:6px}.ask-nick[data-v-31f35103]{display:flex;align-items:center;gap:8px;width:100%;max-width:420px;padding:9px 11px;border-radius:var(--r-sm);background:#1a9d5f0f;border:1px solid rgba(26,157,95,.22)}.an-q[data-v-31f35103]{font-size:12.5px;font-weight:600;color:var(--ink);flex-shrink:0}.an-in[data-v-31f35103]{flex:1;min-width:56px;padding:5px 8px;border-radius:7px;font-size:12.5px;color:var(--ink);background:#fff;border:1px solid var(--line)}.an-in[data-v-31f35103]:focus{outline:none;border-color:#1a9d5f80}.an-ok[data-v-31f35103]{flex-shrink:0;padding:5px 10px;border-radius:7px;font-size:12px;font-weight:600;color:#fff;background:var(--green)}.an-ok[data-v-31f35103]:disabled{opacity:.45;cursor:default}.an-skip[data-v-31f35103]{flex-shrink:0;padding:4px 2px;font-size:11.5px;color:var(--dim)}.an-skip[data-v-31f35103]:hover{color:var(--ink)}.tries[data-v-31f35103]{display:flex;flex-direction:column;gap:6px;width:100%;max-width:420px}.try[data-v-31f35103]{display:flex;align-items:center;gap:8px;width:100%;padding:10px 13px;border-radius:var(--r-sm);font-size:12.5px;color:var(--ink);text-align:left;background:#ffffffd1;border:1px solid var(--line);transition:color .14s,border-color .14s,background .14s,transform .12s}.try[data-v-31f35103]:hover{color:var(--ink);background:#fff;border-color:#1a9d5f6b}.try[data-v-31f35103]:active{transform:scale(.99)}.try .arrow[data-v-31f35103]{font-size:12px;color:var(--green);flex-shrink:0}.resume[data-v-31f35103]{display:flex;align-items:center;gap:7px;max-width:420px;padding:7px 13px;border-radius:var(--r-pill);background:var(--surface-2);color:var(--dim);transition:background .14s,color .14s}.resume[data-v-31f35103]:hover{background:var(--surface-3);color:var(--ink)}.resume .rl[data-v-31f35103]{font-size:11px;font-weight:700;color:var(--dim);text-transform:uppercase;letter-spacing:.07em}.resume b[data-v-31f35103]{font-size:12px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.resume .rm[data-v-31f35103]{font-size:11px;color:var(--dim);flex-shrink:0}.recent[data-v-31f35103]{width:100%;max-width:420px}.recent .rh[data-v-31f35103]{margin-bottom:5px;font-size:11px;font-weight:700;letter-spacing:.08em;color:var(--dim);text-transform:uppercase}.rk-list[data-v-31f35103]{display:flex;flex-wrap:wrap;gap:5px}.rk[data-v-31f35103]{max-width:190px;padding:5px 10px;border-radius:var(--r-pill);font-size:11.5px;color:var(--ink);background:#fffc;border:1px solid var(--line);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:color .13s,border-color .13s,background .13s}.rk[data-v-31f35103]:hover{color:var(--green);background:#fff;border-color:#1a9d5f66}.cm[data-v-d6468d56]{position:absolute;left:8px;right:8px;bottom:calc(100% + 6px);z-index:40;max-height:264px;overflow-y:auto;padding:5px;border-radius:var(--r-mid);background:#fffffff0;-webkit-backdrop-filter:blur(24px) saturate(180%);backdrop-filter:blur(24px) saturate(180%);border:1px solid rgba(255,255,255,.9);box-shadow:var(--sh-pop);animation:cm-in-d6468d56 .13s ease}@keyframes cm-in-d6468d56{0%{opacity:0;transform:translateY(4px)}}.cm-h[data-v-d6468d56]{padding:4px 7px 5px;font-size:11px;font-weight:700;letter-spacing:.07em;color:var(--dim);text-transform:uppercase}.cm-it[data-v-d6468d56]{width:100%;display:flex;align-items:baseline;gap:8px;padding:7px 9px;border-radius:var(--r-sm);text-align:left}.cm-it.on[data-v-d6468d56]{background:var(--surface-2)}.cm-it b[data-v-d6468d56]{font-size:12.5px;font-weight:700;font-family:var(--mono);flex-shrink:0;color:var(--ink)}.cm-it span[data-v-d6468d56]{flex:1;min-width:0;font-size:12px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cm-it em[data-v-d6468d56]{font-style:normal;font-size:10.5px;font-weight:600;color:var(--dim);padding:2px 6px;border-radius:var(--r-pill);background:var(--surface-2)}.cm-empty[data-v-d6468d56]{padding:10px 8px;font-size:12px;color:var(--dim)}.cm-f[data-v-d6468d56]{padding:7px 8px 3px;margin-top:3px;border-top:1px solid var(--line-soft);font-size:11px;color:var(--dim);font-family:var(--mono)}.mp[data-v-1c7db88f]{position:relative;flex-shrink:0}.pill[data-v-1c7db88f]{display:flex;align-items:center;gap:5px;padding:5px 9px;border-radius:var(--r-pill);font-size:12px;font-weight:600;color:var(--ink);background:var(--surface-2);max-width:150px;transition:background .15s,color .15s}.pill[data-v-1c7db88f]:hover,.pill.on[data-v-1c7db88f]{background:var(--surface-3);color:var(--ink)}.pill .dot[data-v-1c7db88f]{width:5px;height:5px;border-radius:50%;flex-shrink:0}.pill .nm[data-v-1c7db88f]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pill .caret[data-v-1c7db88f]{font-size:10px;color:var(--dim)}.pop[data-v-1c7db88f]{position:absolute;left:0;bottom:calc(100% + 8px);z-index:var(--z-menu);width:272px;max-height:54vh;display:flex;flex-direction:column;padding:6px;border-radius:var(--r-mid);background:#ffffffeb;-webkit-backdrop-filter:blur(24px) saturate(180%);backdrop-filter:blur(24px) saturate(180%);border:1px solid rgba(255,255,255,.9);box-shadow:var(--sh-pop);overflow-y:auto;animation:mp-in-1c7db88f .15s ease}@keyframes mp-in-1c7db88f{0%{opacity:0;transform:translateY(5px)}}.it[data-v-1c7db88f]{width:100%;display:flex;align-items:center;gap:8px;padding:6px 7px;border-radius:var(--r-sm);text-align:left;transition:background .13s}.it[data-v-1c7db88f]:hover{background:var(--surface-2)}.it.on[data-v-1c7db88f]{background:var(--accent-soft)}.it.dis[data-v-1c7db88f]{opacity:.55}.it .dot[data-v-1c7db88f]{width:6px;height:6px;border-radius:50%;flex-shrink:0}.it .tx[data-v-1c7db88f]{display:flex;flex-direction:column;gap:1px;flex:1;min-width:0}.it .tx b[data-v-1c7db88f]{font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.it .tx span[data-v-1c7db88f]{font-size:11px;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.it .tx.plain[data-v-1c7db88f]{background:none;padding:0}.tg[data-v-1c7db88f]{flex-shrink:0;font-size:10.5px;font-weight:600;padding:2px 6px;border-radius:var(--r-pill);color:var(--accent);background:#1a9d5f21}.tg.warn[data-v-1c7db88f]{color:var(--amber);background:#c88c2821}.it .x[data-v-1c7db88f]{flex-shrink:0;font-size:11px;color:var(--dim);padding:2px 4px}.it .x[data-v-1c7db88f]:hover{color:var(--red)}button.x[data-v-1c7db88f]{background:none;border:0;font-family:inherit;cursor:pointer}button.x[data-v-1c7db88f]:hover:not(:disabled){color:var(--accent)}button.x[data-v-1c7db88f]:disabled{opacity:.4;cursor:default}.tres[data-v-1c7db88f]{margin-top:5px;padding:7px 9px;border-radius:var(--r-sm);font-size:11.5px;line-height:1.55;color:var(--dim);background:var(--surface-2);overflow-wrap:anywhere}.tres.ok[data-v-1c7db88f]{color:var(--green);background:#1a9d5f14}.tres.err[data-v-1c7db88f]{color:var(--red);background:#e5484d12}.sep[data-v-1c7db88f]{padding:9px 7px 4px;font-size:11px;font-weight:700;letter-spacing:.08em;color:var(--dim);text-transform:uppercase}.empty[data-v-1c7db88f]{padding:10px 7px;font-size:12px;color:var(--dim)}.foot[data-v-1c7db88f]{position:sticky;bottom:-6px;display:flex;gap:5px;padding:6px 4px 4px;margin-top:5px;border-top:1px solid var(--line-soft);background:#fffffff5;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.fbtn[data-v-1c7db88f]{flex:1;padding:7px;border-radius:var(--r-sm);font-size:12px;font-weight:600;color:var(--dim);background:var(--surface-2)}.fbtn[data-v-1c7db88f]:hover{color:var(--ink);background:var(--surface-3)}.form[data-v-1c7db88f]{display:flex;flex-direction:column;gap:6px;padding:4px}.fhead[data-v-1c7db88f]{display:flex;align-items:center;justify-content:space-between;font-size:12.5px;font-weight:700;padding:2px 3px 4px}.fhead .x[data-v-1c7db88f]{cursor:pointer;color:var(--dim);font-size:12px}.form label[data-v-1c7db88f]{display:flex;flex-direction:column;gap:3px;font-size:11.5px;font-weight:600;color:var(--dim)}.form input[data-v-1c7db88f]{padding:6px 8px;border-radius:7px;font-size:12px;background:#fff;border:1px solid var(--line)}.form input[data-v-1c7db88f]:focus{border-color:#1a9d5f73}.form input[data-v-1c7db88f]:disabled{color:var(--faint);background:var(--surface-2)}.submit[data-v-1c7db88f]{margin-top:3px;padding:7px;border-radius:var(--r-sm);font-size:12px;font-weight:600;color:#fff;background:var(--green)}.submit[data-v-1c7db88f]:disabled{opacity:.6}.tm[data-v-28d25609]{position:relative;flex-shrink:0}.plus[data-v-28d25609]{width:28px;height:28px;border-radius:50%;display:grid;place-items:center;font-size:15px;color:var(--dim);background:var(--surface-2);transition:color .15s,background .15s}.plus[data-v-28d25609]:hover,.plus.on[data-v-28d25609]{color:var(--ink);background:var(--surface-3)}.pop[data-v-28d25609]{position:absolute;left:0;bottom:calc(100% + 8px);z-index:var(--z-menu);width:250px;max-height:54vh;display:flex;flex-direction:column;padding:6px;border-radius:var(--r-mid);background:#ffffffeb;-webkit-backdrop-filter:blur(24px) saturate(180%);backdrop-filter:blur(24px) saturate(180%);border:1px solid rgba(255,255,255,.9);box-shadow:var(--sh-pop);overflow-y:auto;animation:tm-in-28d25609 .15s ease}@keyframes tm-in-28d25609{0%{opacity:0;transform:translateY(5px)}}.sec[data-v-28d25609]{padding:6px 7px 4px;font-size:11px;font-weight:700;letter-spacing:.08em;color:var(--dim);text-transform:uppercase}.it[data-v-28d25609]{width:100%;display:flex;align-items:center;gap:9px;padding:8px;border-radius:var(--r-sm);text-align:left;transition:background .13s}.it[data-v-28d25609]:hover{background:var(--surface-2)}.it .ic[data-v-28d25609]{font-size:14px;width:16px;text-align:center;flex-shrink:0}.it .tx[data-v-28d25609]{display:flex;flex-direction:column;gap:2px}.it .tx b[data-v-28d25609]{font-size:13px;font-weight:600}.it .tx span[data-v-28d25609]{font-size:11px;color:var(--dim)}.foot[data-v-28d25609]{margin-top:4px;padding:7px 7px 3px;border-top:1px solid var(--line-soft);font-size:11px;color:var(--dim);font-family:var(--mono)}.dock[data-v-371234e7]{padding:0 8px 8px;position:relative}.dock-in[data-v-371234e7]{position:relative;width:min(736px,calc(100% - 44px));margin:0 auto}.chips[data-v-371234e7]{display:flex;gap:6px;flex-wrap:wrap;padding:0 2px 7px}.chip[data-v-371234e7]{display:flex;align-items:center;gap:6px;padding:5px 9px;border-radius:var(--r-pill);font-size:11.5px;background:#ffffffe6;border:1px solid var(--line);box-shadow:0 1px 2px #17201a0a;max-width:220px}.chip .cn[data-v-371234e7]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink)}.chip .cs[data-v-371234e7]{font-family:var(--mono);font-size:10.5px;color:var(--dim);flex-shrink:0}.chip .cx[data-v-371234e7]{cursor:pointer;font-size:11px;color:var(--dim);padding:0 2px;flex-shrink:0}.chip .cx[data-v-371234e7]:hover{color:var(--red)}.chip.img[data-v-371234e7]{padding:3px 5px}.chip.img img[data-v-371234e7]{width:22px;height:22px;border-radius:5px;object-fit:cover}.chip.ing[data-v-371234e7]{border-color:#1a9d5f4d}.chip.ing.error[data-v-371234e7]{border-color:#c83c3c57}.chip.ing .bar[data-v-371234e7]{width:44px;height:3px;border-radius:2px;overflow:hidden;background:#17201a1a;flex-shrink:0}.chip.ing .bar i[data-v-371234e7]{display:block;height:100%;background:var(--green);transition:width .3s}.chip.ing.error .bar i[data-v-371234e7]{background:var(--red)}.wrap[data-v-371234e7]{display:flex;align-items:flex-end;gap:8px;padding:7px 9px;background:#ffffffe6;border:1px solid var(--line-soft);border-radius:var(--r-mid);box-shadow:var(--sh-card);transition:border-color .15s,box-shadow .15s}.wrap[data-v-371234e7]:focus-within{border-color:#1a9d5f66;box-shadow:0 0 0 3px #1a9d5f14,var(--sh-card)}textarea[data-v-371234e7]{flex:1;min-width:0;font-size:13.5px;line-height:1.6;padding:5px 2px;border:0;background:none;resize:none;overflow-y:auto;max-height:340px;transition:height .14s ease}textarea[data-v-371234e7]:focus{outline:none}textarea[data-v-371234e7]::placeholder{color:var(--dim);opacity:1}.send[data-v-371234e7]{width:30px;height:30px;border-radius:50%;flex-shrink:0;display:grid;place-items:center;font-size:12px;color:#fff;background:var(--green);box-shadow:0 4px 12px #1a9d5f4d;transition:transform .1s,filter .15s,background .15s}.send[data-v-371234e7]:hover{filter:brightness(1.06)}.send[data-v-371234e7]:active{transform:scale(.94)}.send.stop[data-v-371234e7]{background:var(--red);box-shadow:0 4px 12px #e5484d47;font-size:10px}.toast[data-v-371234e7]{position:absolute;left:50%;bottom:62px;transform:translate(-50%);padding:7px 13px;border-radius:var(--r-pill);font-size:11.5px;color:var(--cli-ink);background:#11160feb;box-shadow:var(--sh-pop);white-space:nowrap;max-width:82vw;overflow:hidden;text-overflow:ellipsis;z-index:30}.fade-enter-active[data-v-371234e7],.fade-leave-active[data-v-371234e7]{transition:opacity .18s}.fade-enter-from[data-v-371234e7],.fade-leave-to[data-v-371234e7]{opacity:0}.qi[data-v-6cdbc607]{padding:0 0 10px;margin-bottom:14px;border-bottom:1px solid var(--line-soft)}.qi-h[data-v-6cdbc607]{display:flex;align-items:baseline;gap:6px;margin-bottom:7px;font-size:11px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--dim)}.qi-h em[data-v-6cdbc607]{font-style:normal;font-weight:500;letter-spacing:0;text-transform:none;font-size:11px;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.qi-ta[data-v-6cdbc607]{display:block;width:100%;box-sizing:border-box;resize:vertical;min-height:46px;max-height:140px;padding:7px 9px;border-radius:var(--r-sm);font-family:var(--sans);font-size:12.5px;line-height:1.6;color:var(--ink);background:#fff;border:1px solid var(--line);transition:border-color .15s}.qi-ta[data-v-6cdbc607]::placeholder{color:var(--dim)}.qi-ta[data-v-6cdbc607]:focus{outline:none;border-color:#1a9d5f6b}.qi-b[data-v-6cdbc607]{display:flex;gap:6px;margin-top:6px}.qi-title[data-v-6cdbc607]{flex:1 1 auto;min-width:0;padding:5px 8px;border-radius:var(--r-pill);font-size:11.5px;color:var(--ink);background:#ffffffdb;border:1px solid var(--line)}.qi-title[data-v-6cdbc607]::placeholder{color:var(--dim)}.qi-title[data-v-6cdbc607]:focus{outline:none;border-color:#1a9d5f59}.qi-go[data-v-6cdbc607]{flex-shrink:0;padding:6px 13px;border-radius:var(--r-pill);font-size:11.5px;font-weight:600;color:#fff;background:var(--green);border:1px solid var(--green);transition:opacity .15s}.qi-go[data-v-6cdbc607]:disabled{background:#17201a12;color:var(--dim);border-color:var(--line)}.qi-msg[data-v-6cdbc607]{margin-top:6px;font-size:11px;line-height:1.5}.qi-msg.ok[data-v-6cdbc607]{color:var(--green)}.qi-msg.busy[data-v-6cdbc607]{color:var(--dim)}.qi-msg.err[data-v-6cdbc607]{color:#b4453c}.rail-right[data-v-6327fda1]{padding:0}.head[data-v-6327fda1]{display:flex;align-items:center;gap:8px;flex-shrink:0;padding:10px 12px 9px;border-bottom:1px solid var(--line-soft)}.seg[data-v-6327fda1]{display:flex;gap:2px;padding:2px;border-radius:var(--r-sm);background:#4032200e}.seg button[data-v-6327fda1]{display:flex;align-items:center;gap:4px;padding:4px 11px;border-radius:7px;font-size:11.5px;color:var(--dim);transition:background .15s,color .15s}.seg button.on[data-v-6327fda1]{background:#fff;color:var(--ink);font-weight:600;box-shadow:0 1px 2px #40322017}.seg .pip[data-v-6327fda1]{width:5px;height:5px;border-radius:50%;background:var(--green)}.collapse[data-v-6327fda1]{margin-left:auto;font-size:13px;color:var(--faint);padding:2px 5px}.collapse[data-v-6327fda1]:hover{color:var(--ink)}.body[data-v-6327fda1]{flex:1;padding:12px}.sec[data-v-6327fda1]{margin-bottom:20px}.sec-h[data-v-6327fda1]{display:flex;align-items:center;justify-content:space-between;font-size:11.5px;font-weight:700;letter-spacing:.07em;color:var(--dim);text-transform:uppercase;margin-bottom:8px}.sec-h em[data-v-6327fda1]{font-family:var(--mono);font-size:11.5px;font-weight:700;color:var(--ink)}.kpi[data-v-6327fda1]{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-bottom:8px}.kpi div[data-v-6327fda1]{padding:8px 9px;border-radius:var(--r-sm);background:#ffffffe6;border:1px solid var(--line)}.kpi b[data-v-6327fda1]{display:block;font-size:16px;font-weight:700;letter-spacing:-.01em;color:var(--w-ink)}.kpi span[data-v-6327fda1]{font-size:11.5px;font-weight:500;color:var(--w-dim)}.go[data-v-6327fda1]{width:100%;padding:8px;border-radius:var(--r-sm);font-size:12px;font-weight:600;color:#fff;background:var(--green);transition:opacity .15s}.go[data-v-6327fda1]:disabled{color:var(--w-dim);background:#40322012;font-weight:600;border:1px solid var(--line-soft)}.go[data-v-6327fda1]:not(:disabled):hover{opacity:.9}.sub-h[data-v-6327fda1]{display:flex;justify-content:space-between;padding:5px 2px 6px;font-size:11px;font-weight:600;color:var(--w-dim)}.sub-h em[data-v-6327fda1]{font-style:normal;font-family:var(--mono)}.row[data-v-6327fda1]{width:100%;text-align:left;display:flex;flex-direction:column;gap:2px;padding:8px 9px;border-radius:var(--r-sm);margin-bottom:2px;transition:background .14s}.row[data-v-6327fda1]:hover{background:#ffffffbf}.row b[data-v-6327fda1]{font-size:13px;font-weight:600;color:var(--w-ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.row span[data-v-6327fda1]{font-size:11.5px;color:var(--w-dim)}.more[data-v-6327fda1]{width:100%;display:flex;align-items:center;justify-content:center;gap:6px;margin-top:4px;padding:7px 9px;border-radius:var(--r-sm);font-size:12px;font-weight:600;color:var(--w-dim);border:1px dashed var(--line);transition:color .15s,border-color .15s,background .15s}.more[data-v-6327fda1]:hover{color:var(--green);border-color:#1a9d5f52;background:#1a9d5f0d}.bars[data-v-6327fda1]{display:flex;flex-direction:column;gap:2px}.bars i[data-v-6327fda1]{display:block;width:10px;height:1.5px;border-radius:1px;background:currentColor;opacity:.75}.doc-head[data-v-6327fda1]{padding:0 4px 12px;border-bottom:1px solid var(--line-soft);margin-bottom:12px}.kind[data-v-6327fda1]{display:inline-block;margin-bottom:7px;padding:3px 8px;border-radius:var(--r-pill);font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.08em}.kind.code[data-v-6327fda1]{background:var(--cli-bg);color:var(--cli-acc)}.kind.text[data-v-6327fda1]{background:var(--surface-2);color:var(--w-ink)}.doc-head h3[data-v-6327fda1]{font-size:15px;font-weight:650;line-height:1.45;color:var(--w-ink)}.path[data-v-6327fda1]{display:block;margin-top:6px;font-family:var(--mono);font-size:11px;color:var(--w-dim);word-break:break-all}.meta[data-v-6327fda1]{margin-top:5px;font-size:11.5px;color:var(--w-dim)}.out-line[data-v-6327fda1]{display:flex;gap:7px;padding:6px 7px;border-radius:7px;font-size:12px;color:var(--w-ink);line-height:1.55}.out-line[data-v-6327fda1]:hover{background:#ffffffb3;color:var(--w-ink)}.out-line .n[data-v-6327fda1]{font-family:var(--mono);font-size:10.5px;color:var(--w-dim);padding-top:2px}.acts[data-v-6327fda1]{padding-top:6px}.acts button[data-v-6327fda1]{width:100%;padding:8px;border-radius:var(--r-sm);font-size:12.5px;font-weight:600;color:var(--w-ink);background:#fffc;border:1px solid var(--line)}.acts button[data-v-6327fda1]:hover{color:var(--ink);background:#fff}:root{--g-left: #d7e8d0;--g-mid: #eef7e8;--g-right: #f8f3e9;--band: linear-gradient(100deg, var(--g-left) 0%, var(--g-mid) 46%, var(--g-right) 100%);--veil-left: rgba(255, 255, 255, .28);--veil-mid: rgba(236, 246, 228, .9);--veil-right: rgba(251, 247, 239, .92);--mid-panel: rgba(238, 247, 232, .95);--right-panel: #fbf7ef;--ink: #17201a;--dim: #3f4c42;--faint: #65716a;--line: rgba(23, 32, 26, .1);--line-soft: rgba(23, 32, 26, .06);--w-ink: #211d17;--w-dim: #4a4237;--w-faint: #776d5c;--w-line: rgba(64, 50, 32, .12);--w-line-soft: rgba(64, 50, 32, .06);--w-surface-2: #f3ede1;--w-surface-3: #e9e1d2;--accent: #0a84ff;--accent-soft: rgba(10, 132, 255, .1);--green: #1a9d5f;--amber: #c77400;--red: #e5484d;--surface: #ffffff;--surface-2: #f1f4ef;--surface-3: #e4eae2;--r-card: 20px;--r-mid: 14px;--r-sm: 10px;--r-pill: 999px;--sh-card: 0 1px 2px rgba(23, 32, 26, .04), 0 10px 26px rgba(23, 32, 26, .07);--sh-pop: 0 14px 40px rgba(23, 32, 26, .18), 0 2px 10px rgba(23, 32, 26, .08);--gap: 14px;--pad: 14px;--cli-bg: #11160f;--cli-bg-2: #0b0f0a;--cli-ink: #d9e6d2;--cli-dim: #849480;--cli-line: rgba(255, 255, 255, .07);--cli-acc: #7fe08c;--cli-warn: #e2c07a;--font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "PingFang SC", "Microsoft YaHei", system-ui, sans-serif;--mono: ui-monospace, SFMono-Regular, "JetBrains Mono", "Cascadia Code", Consolas, monospace;--serif: "Songti SC", "Noto Serif SC", Georgia, serif;--z-menu: 100;--z-top: 500}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html,body,#app{height:100%}body{font-family:var(--font);color:var(--ink);background:var(--g-right);-webkit-font-smoothing:antialiased;overflow:hidden}button{font-family:inherit;border:0;background:none;color:inherit;cursor:pointer}input,textarea{font-family:inherit;border:0;background:none;color:inherit;outline:none}em{font-style:normal}.app{height:100vh;display:grid;grid-template-rows:56px 1fr;min-height:0}.workspace{--rail-w: 344px;display:grid;grid-template-columns:296px minmax(0,1fr) var(--rail-w);gap:var(--gap);padding:var(--pad);min-height:0;background:var(--band);transition:grid-template-columns .24s ease}.workspace.is-collapsed{--rail-w: 0px}.col{position:relative;display:flex;flex-direction:column;min-height:0;min-width:0;border-radius:var(--r-card);border:1px solid rgba(255,255,255,.55);background:var(--veil-left);-webkit-backdrop-filter:blur(16px) saturate(150%);backdrop-filter:blur(16px) saturate(150%);box-shadow:var(--sh-card);overflow:hidden}.col--mid{background:var(--veil-mid)}.col--right{background:var(--right-panel);--ink: var(--w-ink);--dim: var(--w-dim);--faint: var(--w-faint);--line: var(--w-line);--line-soft: var(--w-line-soft);--surface-2: var(--w-surface-2);--surface-3: var(--w-surface-3);transition:opacity .18s ease}.col--right .scroll::-webkit-scrollbar-thumb{background:#40322029}.workspace.is-collapsed .col--right{opacity:0;pointer-events:none}.mid{display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(0,1fr) auto}.scroll{overflow-y:auto;overscroll-behavior:contain;min-height:0}.scroll::-webkit-scrollbar{width:8px;height:8px}.scroll::-webkit-scrollbar-thumb{background:#17201a24;border-radius:99px}.scroll::-webkit-scrollbar-track{background:transparent}.empty{padding:28px 12px;text-align:center;font-size:12.5px;color:var(--faint);line-height:1.7}.dot{width:6px;height:6px;border-radius:50%;background:var(--green);flex-shrink:0}.rail-handle{position:fixed;right:0;top:50%;transform:translateY(-50%);z-index:var(--z-menu);padding:16px 6px;font-size:11px;letter-spacing:.04em;color:var(--dim);background:#ffffffdb;border:1px solid var(--line);border-right:0;border-radius:12px 0 0 12px;box-shadow:var(--sh-card);writing-mode:vertical-rl}.rail-handle:hover{color:var(--ink)}@media(prefers-reduced-motion:reduce){.workspace,.col--right{transition:none}}@media(max-width:1180px){.workspace{grid-template-columns:248px minmax(0,1fr)}.col--right{position:fixed;top:calc(56px + var(--pad));right:var(--pad);bottom:var(--pad);width:344px;z-index:60;box-shadow:var(--sh-pop);transition:transform .2s ease,opacity .18s ease}.workspace.is-collapsed .col--right{transform:translate(calc(100% + var(--pad)))}}.rail-scrim{position:fixed;inset:56px 0 0;z-index:59;background:#17201a29}.pn-sec{margin-bottom:18px}.pn-sec:last-child{margin-bottom:0}.pn-h{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;font-size:11.5px;font-weight:700;letter-spacing:.07em;color:var(--dim);text-transform:uppercase}.pn-h em{font-style:normal;font-family:var(--mono);font-size:11.5px;font-weight:700;color:var(--ink);text-transform:none}.pn-kpi{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px}.pn-kpi>div{flex:1 1 104px;padding:10px 11px;border-radius:var(--r-sm);background:#ffffffc7;border:1px solid var(--line-soft)}.pn-kpi b{display:block;font-size:18px;font-weight:650;letter-spacing:-.02em;line-height:1.25}.pn-kpi span{font-size:11.5px;font-weight:500;color:var(--dim)}.pn-row{width:100%;text-align:left;display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:var(--r-sm);border:1px solid transparent;transition:background .14s,border-color .14s}.pn-row+.pn-row{margin-top:2px}.pn-row:hover{background:#ffffffb8}.pn-row.clickable:hover{border-color:var(--line);background:#fff}.pn-row .m{flex:1;min-width:0}.pn-row b{display:block;font-size:13.5px;font-weight:600;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pn-row span.s{display:block;margin-top:2px;font-size:11.5px;color:var(--dim)}.pn-row .r{flex-shrink:0;font-family:var(--mono);font-size:11.5px;color:var(--dim)}.pn-tag{display:inline-block;flex-shrink:0;padding:3px 8px;border-radius:var(--r-pill);font-family:var(--mono);font-size:11px;letter-spacing:.04em;background:var(--surface-2);color:var(--dim)}.pn-tag.green{background:#1a9d5f1f;color:var(--green)}.pn-tag.amber{background:#c7740021;color:var(--amber)}.pn-tag.red{background:#e5484d1f;color:var(--red)}.pn-chips{display:flex;flex-wrap:wrap;gap:6px}.pn-chip{padding:5px 10px;border-radius:var(--r-pill);font-family:var(--mono);font-size:11.5px;color:var(--dim);background:#ffffffc7;border:1px solid var(--line-soft);transition:color .13s,border-color .13s,background .13s}button.pn-chip:hover{color:var(--green);background:#fff;border-color:#1a9d5f59}button.pn-chip.on{color:var(--green);background:#1a9d5f1a;border-color:#1a9d5f66}.pn-chip.warn{color:var(--amber);background:#c7740017;border-color:#c774003d}.pn-empty{padding:16px 12px;border-radius:var(--r-sm);font-size:12px;line-height:1.7;color:var(--dim);background:#fff9;border:1px dashed var(--line)}.pn-st{padding:14px 4px;font-size:12px;color:var(--dim)}.pn-st.err{color:var(--amber)}.pn-cols{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:18px;align-items:start}.panel>.pb{container-type:inline-size}@container (max-width: 660px){.pn-cols{grid-template-columns:minmax(0,1fr);gap:0}}@container (max-width: 300px){.pn-row{flex-wrap:wrap;row-gap:6px}.pn-row .m{flex:1 1 100%}.pn-h{flex-wrap:wrap;gap:6px}.pn-kpi>div{flex:1 1 76px}}.pn-note{font-size:11.5px;line-height:1.65;color:var(--dim);padding:8px 10px;border-radius:var(--r-sm);background:#fff9;border:1px solid var(--line-soft)}.pn-note code{font-family:var(--mono);font-size:11px;color:var(--ink);word-break:break-all}
@@ -3,11 +3,11 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Foxclaw Deck · v3</title>
6
+ <title>Foxclaw</title>
7
7
  <!-- 内联 SVG 图标:不新增静态文件,也避免浏览器探 /favicon.ico 报 404 -->
8
8
  <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='9' fill='%23179d5f'/><circle cx='16' cy='16' r='6.5' fill='%23f4fbf0'/></svg>" />
9
- <script type="module" crossorigin src="/v3/assets/index-BD7VPbMV.js"></script>
10
- <link rel="stylesheet" crossorigin href="/v3/assets/index-CAvnYWGH.css">
9
+ <script type="module" crossorigin src="/v3/assets/index-CQSBdxk4.js"></script>
10
+ <link rel="stylesheet" crossorigin href="/v3/assets/index-D5nIsjms.css">
11
11
  </head>
12
12
  <body>
13
13
  <div id="app"></div>
@@ -1,39 +1,16 @@
1
- /* ════════════════════════════════════════════════════════════
2
- FOXCLAW DECK — Widget Runtime
3
- 可插拔 widget 系统。新模块只需 registerWidget() 一次。
4
- ════════════════════════════════════════════════════════════ */
5
1
 
6
2
  const WidgetRuntime = (() => {
7
3
 
8
- // ── 注册表:定义有哪些 widget 类型 ──
9
4
  const registry = new Map();
10
5
 
11
- // ── 实例:当前布局中存在的 widget ──
12
- // { instanceId, widgetId, column, order }
13
6
  let instances = [];
14
7
 
15
- // ── 事件订阅:widgetId -> Set<eventType> ──
16
8
  const eventSubs = new Map();
17
9
 
18
- // ── 共享上下文,所有 widget 都能访问 ──
19
10
  let ctx = {};
20
11
 
21
- // ── DOM 列引用 ──
22
12
  const columns = {};
23
13
 
24
- /* ───────────────────────────────────────────
25
- 注册一个 widget 类型
26
- spec = {
27
- id, title, icon,
28
- defaultColumn: 'left'|'center'|'right',
29
- size: {minH, maxH, flex},
30
- events: ['harness_block', ...], // 关心的事件类型
31
- render(el, ctx, instance), // 渲染主体
32
- onEvent(evt, el, ctx), // 收到订阅事件
33
- onAgentChange(agentId, el, ctx), // 激活 Agent 变化
34
- refresh(el, ctx, agentId), // 数据刷新
35
- }
36
- ─────────────────────────────────────────── */
37
14
  function register(spec) {
38
15
  if (!spec.id) throw new Error('widget 必须有 id');
39
16
  registry.set(spec.id, {
@@ -46,14 +23,12 @@ const WidgetRuntime = (() => {
46
23
  refresh: null,
47
24
  ...spec,
48
25
  });
49
- // 建立事件订阅索引
50
26
  (spec.events || []).forEach(ev => {
51
27
  if (!eventSubs.has(ev)) eventSubs.set(ev, new Set());
52
28
  eventSubs.get(ev).add(spec.id);
53
29
  });
54
30
  }
55
31
 
56
- /* 初始化:绑定列容器 + 共享上下文 */
57
32
  function init(columnEls, sharedCtx) {
58
33
  columns.left = columnEls.left;
59
34
  columns.center = columnEls.center;
@@ -61,7 +36,6 @@ const WidgetRuntime = (() => {
61
36
  ctx = sharedCtx || {};
62
37
  }
63
38
 
64
- /* 设置布局(从默认或 localStorage 恢复) */
65
39
  function setLayout(layout) {
66
40
  instances = layout.map((item, i) => ({
67
41
  instanceId: item.instanceId || `inst_${item.widgetId}_${i}`,
@@ -72,7 +46,6 @@ const WidgetRuntime = (() => {
72
46
  renderAll();
73
47
  }
74
48
 
75
- /* 默认布局:每个注册的 widget 放进它的 defaultColumn */
76
49
  function defaultLayout() {
77
50
  return [...registry.values()].map((spec, i) => ({
78
51
  instanceId: `inst_${spec.id}`,
@@ -82,7 +55,6 @@ const WidgetRuntime = (() => {
82
55
  }));
83
56
  }
84
57
 
85
- /* 渲染全部实例 */
86
58
  function renderAll() {
87
59
  Object.values(columns).forEach(col => { if (col) col.innerHTML = ''; });
88
60
  const byCol = { left: [], center: [], right: [] };
@@ -96,7 +68,6 @@ const WidgetRuntime = (() => {
96
68
  });
97
69
  }
98
70
 
99
- /* 挂载单个 widget 到 DOM */
100
71
  function mountWidget(inst, colName) {
101
72
  const spec = registry.get(inst.widgetId);
102
73
  if (!spec) return;
@@ -127,26 +98,22 @@ const WidgetRuntime = (() => {
127
98
  inst._el = panel;
128
99
  inst._body = body;
129
100
 
130
- // 移除按钮
131
101
  panel.querySelector('.ph-remove').onclick = (e) => {
132
102
  e.stopPropagation();
133
103
  removeInstance(inst.instanceId);
134
104
  };
135
105
 
136
- // 调用 widget 自己的渲染
137
106
  try {
138
107
  spec.render(body, ctx, inst);
139
108
  } catch (err) {
140
109
  body.innerHTML = `<div class="empty">渲染错误: ${err.message}</div>`;
141
110
  }
142
111
 
143
- // 首次同步当前 Agent
144
112
  if (spec.onAgentChange && ctx.activeAgent) {
145
113
  try { spec.onAgentChange(ctx.activeAgent, body, ctx); } catch {}
146
114
  }
147
115
  }
148
116
 
149
- /* 事件分发:把事件发给所有订阅的 widget 实例 */
150
117
  function dispatch(eventType, payload) {
151
118
  const subscribers = eventSubs.get(eventType);
152
119
  if (!subscribers) return;
@@ -159,7 +126,6 @@ const WidgetRuntime = (() => {
159
126
  });
160
127
  }
161
128
 
162
- /* 通知所有 widget:激活 Agent 变了 */
163
129
  function notifyAgentChange(agentId) {
164
130
  ctx.activeAgent = agentId;
165
131
  instances.forEach(inst => {
@@ -170,7 +136,6 @@ const WidgetRuntime = (() => {
170
136
  });
171
137
  }
172
138
 
173
- /* 刷新所有 widget 的数据 */
174
139
  function refreshAll(agentId) {
175
140
  instances.forEach(inst => {
176
141
  const spec = registry.get(inst.widgetId);
@@ -180,7 +145,6 @@ const WidgetRuntime = (() => {
180
145
  });
181
146
  }
182
147
 
183
- /* 设置某个实例的 head status 文本 */
184
148
  function setStatus(instanceId, text) {
185
149
  const inst = instances.find(i => i.instanceId === instanceId);
186
150
  if (inst && inst._el) {
@@ -189,7 +153,6 @@ const WidgetRuntime = (() => {
189
153
  }
190
154
  }
191
155
 
192
- /* ── 增删实例 ── */
193
156
  function addInstance(widgetId, column) {
194
157
  const spec = registry.get(widgetId);
195
158
  if (!spec) return;
@@ -214,7 +177,6 @@ const WidgetRuntime = (() => {
214
177
  saveLayout();
215
178
  }
216
179
 
217
- /* ── 拖拽:重排实例 ── */
218
180
  function captureLayoutFromDOM() {
219
181
  const newInstances = [];
220
182
  ['left', 'center', 'right'].forEach(colName => {
@@ -239,7 +201,6 @@ const WidgetRuntime = (() => {
239
201
  saveLayout();
240
202
  }
241
203
 
242
- /* ── 持久化 ── */
243
204
  function saveLayout() {
244
205
  try {
245
206
  const layout = instances.map(i => ({
@@ -256,7 +217,6 @@ const WidgetRuntime = (() => {
256
217
  try {
257
218
  const saved = JSON.parse(localStorage.getItem('foxclaw_widget_layout'));
258
219
  if (saved && saved.length) {
259
- // 过滤掉已不存在的 widget 类型
260
220
  const valid = saved.filter(i => registry.has(i.widgetId));
261
221
  if (valid.length) { setLayout(valid); return true; }
262
222
  }
@@ -264,7 +224,6 @@ const WidgetRuntime = (() => {
264
224
  return false;
265
225
  }
266
226
 
267
- /* ── 编辑模式:显示/隐藏移除按钮 ── */
268
227
  function setEditMode(on) {
269
228
  instances.forEach(inst => {
270
229
  if (!inst._el) return;
@@ -273,7 +232,6 @@ const WidgetRuntime = (() => {
273
232
  });
274
233
  }
275
234
 
276
- /* ── 面板库:列出未激活的 widget 供添加 ── */
277
235
  function getLibrary() {
278
236
  return [...registry.values()].map(spec => ({
279
237
  id: spec.id,
@@ -283,7 +241,6 @@ const WidgetRuntime = (() => {
283
241
  }));
284
242
  }
285
243
 
286
- /* 公开 API */
287
244
  return {
288
245
  register, init, setLayout, defaultLayout, renderAll,
289
246
  dispatch, notifyAgentChange, refreshAll, setStatus,
@@ -1,16 +1,7 @@
1
- /* ════════════════════════════════════════════════════════════
2
- FOXCLAW DECK — Widget Definitions
3
- 每个 widget 是一个 registerWidget() 注册。
4
- 新增模块只需在这里加一个 register block。
5
- ════════════════════════════════════════════════════════════ */
6
1
 
7
- /* 共享工具(由主文件注入到 ctx) */
8
2
  function _esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\n/g,'<br>');}
9
3
  function _statusColor(s){return{PASS:'#1dffa6',WARN:'#ffb347',BLOCK:'#ff4060',BASELINE:'#b066ff',PENDING:'#3a4d68'}[s]||'#1de9ff';}
10
4
 
11
- /* ─────────────────────────────────────────────
12
- WIDGET 1: AGENT 编队 (roster)
13
- ───────────────────────────────────────────── */
14
5
  WidgetRuntime.register({
15
6
  id: 'roster',
16
7
  title: 'AGENT 编队',
@@ -44,9 +35,6 @@ WidgetRuntime.register({
44
35
  },
45
36
  });
46
37
 
47
- /* ─────────────────────────────────────────────
48
- WIDGET 2: 任务流 (tasks)
49
- ───────────────────────────────────────────── */
50
38
  WidgetRuntime.register({
51
39
  id: 'tasks',
52
40
  title: '任务流',
@@ -59,9 +47,6 @@ WidgetRuntime.register({
59
47
  },
60
48
  });
61
49
 
62
- /* ─────────────────────────────────────────────
63
- WIDGET 3: 中央核心 (core) — 激活 Agent
64
- ───────────────────────────────────────────── */
65
50
  WidgetRuntime.register({
66
51
  id: 'core',
67
52
  title: '中央核心 · 激活单元',
@@ -94,7 +79,6 @@ WidgetRuntime.register({
94
79
  </div>
95
80
  <div class="chat-log" data-role="chat-log"></div>
96
81
  </div>`;
97
- // orbit ticks
98
82
  const tick = el.querySelector('[data-role="orbit-tick"]');
99
83
  let h=''; for(let i=0;i<24;i++)h+=`<span style="transform:translateX(-50%) rotate(${i*15}deg)"></span>`;
100
84
  tick.innerHTML=h;
@@ -111,9 +95,6 @@ WidgetRuntime.register({
111
95
  },
112
96
  });
113
97
 
114
- /* ─────────────────────────────────────────────
115
- WIDGET 4: HARNESS 遥测 (harness)
116
- ───────────────────────────────────────────── */
117
98
  WidgetRuntime.register({
118
99
  id: 'harness',
119
100
  title: 'HARNESS 遥测',
@@ -150,14 +131,10 @@ WidgetRuntime.register({
150
131
  ctx.loadHarnessInto(el, agentId);
151
132
  },
152
133
  onEvent(evt, el, ctx) {
153
- // policy 更新或校准完成时立即刷新
154
134
  ctx.loadHarnessInto(el, ctx.activeAgent);
155
135
  },
156
136
  });
157
137
 
158
- /* ─────────────────────────────────────────────
159
- WIDGET 5: 推理链 STREAM (thoughtchain)
160
- ───────────────────────────────────────────── */
161
138
  WidgetRuntime.register({
162
139
  id: 'thoughtchain',
163
140
  title: '推理链 STREAM',
@@ -175,9 +152,6 @@ WidgetRuntime.register({
175
152
  },
176
153
  });
177
154
 
178
- /* ─────────────────────────────────────────────
179
- WIDGET 6: 事件流 LIVE (events)
180
- ───────────────────────────────────────────── */
181
155
  WidgetRuntime.register({
182
156
  id: 'events',
183
157
  title: '事件流 · LIVE',
@@ -206,10 +180,6 @@ WidgetRuntime.register({
206
180
  },
207
181
  });
208
182
 
209
- /* ─────────────────────────────────────────────
210
- WIDGET 7: 协同 DAG (dag) — 多 Agent 星舰控制流
211
- 命令 → 主管分发 → 子 Agent → Harness 关卡 → 记忆汇聚
212
- ───────────────────────────────────────────── */
213
183
  WidgetRuntime.register({
214
184
  id: 'dag',
215
185
  title: '协同 DAG · 控制流',
@@ -243,7 +213,6 @@ WidgetRuntime.register({
243
213
  },
244
214
  });
245
215
 
246
- /* DAG 拓扑定义 */
247
216
  const _DAG_NODES = {
248
217
  command: { x: 60, y: 115, label: '指令', icon: '❯', kind: 'entry', color: '#1de9ff' },
249
218
  supervisor: { x: 180, y: 115, label: '主管', icon: '🏢', kind: 'hub', color: '#1de9ff' },
@@ -265,7 +234,6 @@ function _dagBuild(el, ctx) {
265
234
  const edgesG = el.querySelector('[data-role="dag-edges"]');
266
235
  const nodesG = el.querySelector('[data-role="dag-nodes"]');
267
236
 
268
- // edges
269
237
  edgesG.innerHTML = _DAG_EDGES.map(([from, to], i) => {
270
238
  const a = _DAG_NODES[from], b = _DAG_NODES[to];
271
239
  const curve = (from === 'memory' && to === 'supervisor')
@@ -275,7 +243,6 @@ function _dagBuild(el, ctx) {
275
243
  fill="none" stroke="#1a2942" stroke-width="1.5" marker-end="url(#dagArrow)"/>`;
276
244
  }).join('');
277
245
 
278
- // nodes
279
246
  nodesG.innerHTML = Object.entries(_DAG_NODES).map(([id, n]) => {
280
247
  const r = n.kind === 'hub' ? 17 : n.kind === 'gate' ? 16 : 14;
281
248
  const shape = n.kind === 'gate'
@@ -301,7 +268,6 @@ function _dagSetActive(el, agentId) {
301
268
  el.querySelectorAll('.dag-node').forEach(g => {
302
269
  g.classList.toggle('dag-dim', false);
303
270
  });
304
- // highlight active agent path: command→supervisor→agent→harness→memory
305
271
  const path = ['command','supervisor', agentId, 'harness','memory'];
306
272
  el.querySelectorAll('.dag-node').forEach(g => {
307
273
  const id = g.dataset.nodeG;
@@ -353,7 +319,6 @@ function _dagFlashGate(el, color) {
353
319
  const gate = el.querySelector('[data-node="harness"]');
354
320
  if (!gate) return;
355
321
  const g = gate.closest('.dag-node') || gate;
356
- // 注入一次性 keyframes(如未注入)
357
322
  if (!document.getElementById('dag-flash-style')) {
358
323
  const st = document.createElement('style');
359
324
  st.id = 'dag-flash-style';
@@ -371,7 +336,6 @@ function _dagFlashGate(el, color) {
371
336
  gate.classList.remove('dag-gate-flash');
372
337
  void gate.offsetWidth; // 强制重排,允许动画重放
373
338
  gate.classList.add('dag-gate-flash');
374
- // 同时让 HARNESS 文字标签也闪一下
375
339
  const label = el.querySelector('.dag-node[data-node-g="harness"] .dag-label');
376
340
  if (label) { label.style.fill = color; label.style.fontWeight = '700'; }
377
341
  setTimeout(() => {
@@ -381,11 +345,6 @@ function _dagFlashGate(el, color) {
381
345
  }, 1200);
382
346
  }
383
347
 
384
- /* ─────────────────────────────────────────────
385
- WIDGET 8: 执行终端 (terminal) — Shell 执行流
386
- 命令回显 · 记忆检索 · 推理 · 工具调用 · Harness 校验
387
- 带打字机流式效果,营造 "AI 正在工作" 的真实感
388
- ───────────────────────────────────────────── */
389
348
  WidgetRuntime.register({
390
349
  id: 'terminal',
391
350
  title: '执行终端 · SHELL',
@@ -464,14 +423,9 @@ function _termWrite(el, text, type = 'out', stream = false) {
464
423
  txtEl.textContent = text;
465
424
  }
466
425
  out.scrollTop = out.scrollHeight;
467
- // 限制行数
468
426
  while (out.children.length > 80) out.firstChild?.remove();
469
427
  }
470
428
 
471
- /* ─────────────────────────────────────────────
472
- WIDGET 9: AI 浏览器 (browser) — 网页操作可视化
473
- 模拟 Agent 浏览网页:地址栏 · 加载流 · 操作高亮(点击/输入/滚动)
474
- ───────────────────────────────────────────── */
475
429
  WidgetRuntime.register({
476
430
  id: 'browser',
477
431
  title: 'AI 浏览器 · BROWSE',
@@ -507,14 +461,12 @@ function _brzNavigate(el, url, actions) {
507
461
  statusEl.textContent = 'LOADING';
508
462
  statusEl.className = 'brz-status loading';
509
463
 
510
- // 进度条动画
511
464
  progEl.style.width = '0%';
512
465
  progEl.style.opacity = '1';
513
466
  setTimeout(() => progEl.style.width = '70%', 80);
514
467
  setTimeout(() => { progEl.style.width = '100%'; }, 700);
515
468
  setTimeout(() => { progEl.style.opacity = '0'; statusEl.textContent = 'LOADED'; statusEl.className = 'brz-status loaded'; }, 950);
516
469
 
517
- // 渲染一个抽象的"网页骨架" + 操作高亮
518
470
  vp.innerHTML = `
519
471
  <div class="brz-page">
520
472
  <div class="brz-skel brz-skel-title"></div>
@@ -531,7 +483,6 @@ function _brzNavigate(el, url, actions) {
531
483
  <div class="brz-cursor" data-role="brz-cursor">➤</div>
532
484
  </div>`;
533
485
 
534
- // AI 操作序列高亮
535
486
  const acts = actions || [
536
487
  { type: 'scan', label: '解析页面结构', x: 50, y: 20 },
537
488
  { type: 'click', label: '点击主内容区', x: 30, y: 45 },
@@ -548,7 +499,6 @@ function _brzNavigate(el, url, actions) {
548
499
  cursor.style.top = a.y + '%';
549
500
  cursor.setAttribute('data-label', a.label);
550
501
  cursor.className = `brz-cursor act-${a.type}`;
551
- // 涟漪
552
502
  const ripple = document.createElement('div');
553
503
  ripple.className = 'brz-ripple';
554
504
  ripple.style.left = a.x + '%';
@@ -561,11 +511,6 @@ function _brzNavigate(el, url, actions) {
561
511
  setTimeout(runAct, 1000);
562
512
  }
563
513
 
564
- /* ─────────────────────────────────────────────
565
- WIDGET 10: 语音输入 (voice) — 麦克风波形 + 语音识别
566
- Web Audio API 真实波形 + 浏览器原生 SpeechRecognition
567
- 识别结果可直接作为指令传输给激活 Agent
568
- ───────────────────────────────────────────── */
569
514
  WidgetRuntime.register({
570
515
  id: 'voice',
571
516
  title: '语音输入 · VOICE',
@@ -600,7 +545,6 @@ function _voiceInit(el, ctx) {
600
545
  let audioCtx = null, analyser = null, dataArr = null, rafId = null, stream = null;
601
546
  let recognition = null, recognizing = false, finalText = '';
602
547
 
603
- // 语音识别(浏览器原生)
604
548
  const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
605
549
  if (SR) {
606
550
  recognition = new SR();
@@ -685,7 +629,6 @@ function _voiceInit(el, ctx) {
685
629
  const text = transcriptEl.textContent.trim();
686
630
  if (!text || text === '聆听中...') return;
687
631
  stopMic();
688
- // 把识别结果填入控制台并传输
689
632
  const input = document.getElementById('consoleInput');
690
633
  if (input) { input.value = text; }
691
634
  if (typeof transmit === 'function') transmit();
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Foxclaw Deck · v3</title>
6
+ <title>Foxclaw</title>
7
7
  <!-- 内联 SVG 图标:不新增静态文件,也避免浏览器探 /favicon.ico 报 404 -->
8
8
  <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='9' fill='%23179d5f'/><circle cx='16' cy='16' r='6.5' fill='%23f4fbf0'/></svg>" />
9
9
  </head>
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "foxclaw-deck-next",
3
- "version": "3.0.0",
3
+ "version": "0.1.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "foxclaw-deck-next",
9
- "version": "3.0.0",
9
+ "version": "0.1.2",
10
10
  "dependencies": {
11
11
  "vue": "^3.5.13"
12
12
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "foxclaw-deck-next",
3
3
  "private": true,
4
- "version": "3.0.0",
4
+ "version": "0.1.3",
5
5
  "type": "module",
6
6
  "description": "Foxclaw Deck v3 - Vue 3 rendering layer (builds into frontend/v3)",
7
7
  "scripts": {
@@ -7,10 +7,8 @@ import Inspector from './components/Inspector.vue'
7
7
  import { onBeforeUnmount, onMounted, ref } from 'vue'
8
8
  import { state, setModule } from './stores/app.js'
9
9
  import { bootChat } from './stores/chat.js'
10
+ import { t } from './lib/i18n.js'
10
11
 
11
- /* 窄窗断点与 base.css 的 @media (max-width: 1180px) 是同一个数:
12
- 到这条线以下右栏变成抽屉(见 base.css),这里负责「变窄时自动收起」。
13
- 否则 900px 窗口里三栏并存,中栏只剩 252px,输入框被 + / 模型胶囊挤到 115px。 */
14
12
  const NARROW = 1180
15
13
  const narrow = ref(false)
16
14
 
@@ -21,7 +19,6 @@ function syncNarrow() {
21
19
  if (n) state.railOpen = false
22
20
  }
23
21
 
24
- /* 恢复上次会话历史(后端落盘) */
25
22
  onMounted(() => {
26
23
  bootChat()
27
24
  narrow.value = window.innerWidth < NARROW
@@ -34,7 +31,6 @@ onBeforeUnmount(() => {
34
31
  window.removeEventListener('resize', syncNarrow)
35
32
  })
36
33
 
37
- /* Ctrl+, 打开设置(顶栏 ⚙ 旁边写的那个键位)。输入框里也生效 —— 它是全局入口 */
38
34
  function onKey(e) {
39
35
  if (e.ctrlKey && (e.key === ',' || e.code === 'Comma')) {
40
36
  e.preventDefault()
@@ -55,8 +51,8 @@ function onKey(e) {
55
51
  <Inspector />
56
52
  </div>
57
53
  <div v-if="narrow && state.railOpen" class="rail-scrim" @click="state.railOpen = false" />
58
- <button v-if="!state.railOpen" class="rail-handle" title="展开检视栏" @click="state.railOpen = true">
59
- 检视
54
+ <button v-if="!state.railOpen" class="rail-handle" :title="t('app.expandInspector')" @click="state.railOpen = true">
55
+ {{ t('app.inspector') }}
60
56
  </button>
61
57
  </div>
62
58
  </template>