@nextclaw/ui 0.12.24 → 0.12.26

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 (210) hide show
  1. package/CHANGELOG.md +136 -29
  2. package/dist/assets/api-DGD9_Bg4.js +15 -0
  3. package/dist/assets/app-manager-provider-oYdeYPSv.js +1 -0
  4. package/dist/assets/{book-open-DDlN5MvX.js → book-open-BcnAiKde.js} +1 -1
  5. package/dist/assets/channels-list-page-HgLgrEg4.js +8 -0
  6. package/dist/assets/chat-page-DAKMFDrS.js +1 -0
  7. package/dist/assets/config-split-page-CcrEUtwu.js +1 -0
  8. package/dist/assets/cpu-DPPwMzoC.js +3 -0
  9. package/dist/assets/{createLucideIcon-BLMK3QUd.js → createLucideIcon-DzY6wN61.js} +1 -1
  10. package/dist/assets/desktop-DVUbOWbR.js +3 -0
  11. package/dist/assets/desktop-update-config-CP8dFYXK.js +1 -0
  12. package/dist/assets/{dialog-C3D7Be0p.js → dialog-BKo0RItd.js} +1 -1
  13. package/dist/assets/{dist-CPlbUgwU.js → dist-CFiwgaLs.js} +1 -1
  14. package/dist/assets/doc-browser-CAhfnm0D.js +1 -0
  15. package/dist/assets/{doc-browser-context-BJuMaI3o.js → doc-browser-context-FukQHvyo.js} +1 -1
  16. package/dist/assets/doc-browser-p9DDNPWB.js +1 -0
  17. package/dist/assets/doc-browser-rZIQIjuw.js +1 -0
  18. package/dist/assets/download-CMM8po31.js +1 -0
  19. package/dist/assets/{es2015-xqN1slyW.js → es2015-BhznEEyJ.js} +1 -1
  20. package/dist/assets/{external-link-DwfSfTLB.js → external-link-CpEvG65F.js} +1 -1
  21. package/dist/assets/i18n-D1144VAA.js +1 -0
  22. package/dist/assets/index-Cuwst6cc.js +100 -0
  23. package/dist/assets/index-dlcqieQ0.css +1 -0
  24. package/dist/assets/{key-round-CJ5gDAAG.js → key-round-DUq47t0P.js} +1 -1
  25. package/dist/assets/marketplace-page-BeFbwxR-.js +105 -0
  26. package/dist/assets/marketplace-page-CR4xq-TM.js +1 -0
  27. package/dist/assets/mcp-marketplace-page-DlRrSCj3.js +1 -0
  28. package/dist/assets/mcp-marketplace-page-DwnaLNTx.js +40 -0
  29. package/dist/assets/model-config-L2l6YAlQ.js +1 -0
  30. package/dist/assets/{notice-card-BFDbKQDA.js → notice-card-Dr6xCwva.js} +1 -1
  31. package/dist/assets/play-AqrNslHI.js +1 -0
  32. package/dist/assets/plus-B-YHtTNC.js +1 -0
  33. package/dist/assets/{popover-B86Dbfhf.js → popover-BDFNiLlg.js} +1 -1
  34. package/dist/assets/provider-scoped-model-input-BMTp4BEH.js +1 -0
  35. package/dist/assets/providers-list-DYAEunOp.js +1 -0
  36. package/dist/assets/refresh-cw-CrbD8EkT.js +1 -0
  37. package/dist/assets/remote-Dr3jcfWP.js +1 -0
  38. package/dist/assets/{rotate-cw-BZ2JObNs.js → rotate-cw-BN9yjccP.js} +1 -1
  39. package/dist/assets/runtime-config-page-BdeU8PEK.js +1 -0
  40. package/dist/assets/{save-euRxl8pI.js → save-CO_4qf6b.js} +1 -1
  41. package/dist/assets/{search-CLd7m0M7.js → search-CRtQwr-h.js} +1 -1
  42. package/dist/assets/search-config-CQUhd5RU.js +1 -0
  43. package/dist/assets/secrets-config-D-NWlW9q.js +3 -0
  44. package/dist/assets/{select-CJ0wbo3D.js → select-BUTwE_lC.js} +1 -1
  45. package/dist/assets/{setting-row-D1Yygqp7.js → setting-row-BavcnXw1.js} +1 -1
  46. package/dist/assets/settings-MWL2SMyk.js +1 -0
  47. package/dist/assets/{sparkles-DVfeSVJQ.js → sparkles-BmgOD4nY.js} +1 -1
  48. package/dist/assets/{status-dot-ChvPCib9.js → status-dot-l3kPFdq_.js} +1 -1
  49. package/dist/assets/{tabs-custom-Hia_ong0.js → tabs-custom-D48zdZoc.js} +1 -1
  50. package/dist/assets/{tag-chip-FrkmkT8r.js → tag-chip-Dm2Lqnpu.js} +1 -1
  51. package/dist/assets/use-config-Cyv5IuSt.js +1 -0
  52. package/dist/assets/use-infinite-scroll-loader-CFVdPpNv.js +1 -0
  53. package/dist/assets/x-BeyYA_h6.js +1 -0
  54. package/dist/index.html +29 -40
  55. package/package.json +9 -9
  56. package/src/app/components/layout/sidebar.layout.test.tsx +2 -4
  57. package/src/app/components/theme-provider.tsx +1 -0
  58. package/src/app/configs/app-navigation.config.ts +0 -6
  59. package/src/app/index.tsx +4 -7
  60. package/src/features/agents/components/agents-page.test.tsx +25 -15
  61. package/src/features/agents/components/agents-page.tsx +133 -172
  62. package/src/features/channels/components/config/channel-form.test.tsx +1 -0
  63. package/src/features/channels/components/config/channel-form.tsx +4 -3
  64. package/src/features/channels/components/config/weixin-channel-auth-section.test.tsx +38 -1
  65. package/src/features/channels/components/config/weixin-channel-auth-section.tsx +137 -40
  66. package/src/features/channels/index.ts +1 -1
  67. package/src/features/channels/utils/channel-form-fields.utils.test.ts +26 -0
  68. package/src/features/channels/utils/channel-form-fields.utils.ts +32 -18
  69. package/src/features/chat/components/chat-session-workspace-panel-nav.tsx +23 -4
  70. package/src/features/chat/components/chat-session-workspace-panel.tsx +53 -35
  71. package/src/features/chat/components/chat-sidebar-session-item.tsx +16 -12
  72. package/src/features/chat/components/conversation/chat-conversation-header.test.tsx +74 -0
  73. package/src/features/chat/components/conversation/chat-conversation-header.tsx +8 -2
  74. package/src/features/chat/components/conversation/chat-conversation-panel.test.tsx +262 -114
  75. package/src/features/chat/components/conversation/chat-conversation-panel.tsx +210 -174
  76. package/src/features/chat/components/conversation/chat-input-bar.container.tsx +11 -1
  77. package/src/features/chat/components/conversation/session-header/chat-session-header-actions.test.tsx +24 -0
  78. package/src/features/chat/components/conversation/session-header/chat-session-header-actions.tsx +27 -6
  79. package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +174 -0
  80. package/src/features/chat/components/layout/chat-sidebar.test.tsx +45 -8
  81. package/src/features/chat/components/layout/chat-sidebar.tsx +29 -46
  82. package/src/features/chat/components/providers/chat-presenter.provider.tsx +4 -0
  83. package/src/features/chat/components/workspace/session-cron-job-content.tsx +103 -0
  84. package/src/features/chat/hooks/use-ncp-agent-runtime.test.tsx +153 -80
  85. package/src/features/chat/hooks/use-ncp-chat-page-data.test.tsx +70 -0
  86. package/src/features/chat/hooks/use-ncp-chat-page-data.ts +1 -1
  87. package/src/features/chat/hooks/use-ncp-child-session-tabs-view.ts +2 -8
  88. package/src/features/chat/hooks/use-ncp-session-list-view.ts +1 -2
  89. package/src/features/chat/managers/chat-session-list.manager.test.ts +7 -9
  90. package/src/features/chat/managers/chat-session-list.manager.ts +5 -10
  91. package/src/features/chat/managers/ncp-chat-input.manager.test.ts +20 -2
  92. package/src/features/chat/managers/ncp-chat-input.manager.ts +18 -0
  93. package/src/features/chat/managers/ncp-chat-presenter.manager.ts +7 -0
  94. package/src/features/chat/managers/ncp-chat-thread.manager.test.ts +52 -1
  95. package/src/features/chat/managers/ncp-chat-thread.manager.ts +21 -0
  96. package/src/features/chat/pages/ncp-chat-page.tsx +9 -5
  97. package/src/features/chat/stores/chat-input.store.ts +3 -1
  98. package/src/features/chat/stores/chat-session-list.store.ts +0 -2
  99. package/src/features/chat/stores/chat-thread.store.ts +4 -0
  100. package/src/features/chat/utils/chat-session-display.utils.test.ts +83 -1
  101. package/src/features/chat/utils/chat-session-display.utils.ts +73 -0
  102. package/src/features/chat/utils/ncp-chat-input-availability.utils.test.ts +1 -0
  103. package/src/features/chat/utils/ncp-session-adapter.utils.test.ts +22 -0
  104. package/src/features/chat/utils/ncp-session-adapter.utils.ts +32 -0
  105. package/src/features/marketplace/components/curated-shelves/marketplace-curated-scene-route.test.tsx +235 -0
  106. package/src/features/marketplace/components/curated-shelves/marketplace-curated-shelves.config.ts +162 -0
  107. package/src/features/marketplace/components/curated-shelves/marketplace-curated-shelves.tsx +355 -0
  108. package/src/features/marketplace/components/curated-shelves/marketplace-shelf-card.tsx +118 -0
  109. package/src/features/marketplace/components/detail-doc/marketplace-detail-doc-renderer.ts +201 -0
  110. package/src/features/marketplace/components/detail-doc/marketplace-detail-doc.test.ts +40 -0
  111. package/src/features/marketplace/components/marketplace-catalog-grid.tsx +114 -0
  112. package/src/features/marketplace/components/marketplace-detail-doc.ts +73 -24
  113. package/src/features/marketplace/components/marketplace-item-icon.tsx +45 -0
  114. package/src/features/marketplace/components/marketplace-list-card.tsx +177 -93
  115. package/src/features/marketplace/components/marketplace-page-detail.test.tsx +9 -2
  116. package/src/features/marketplace/components/marketplace-page-parts.tsx +1 -1
  117. package/src/features/marketplace/components/marketplace-page.test.tsx +25 -6
  118. package/src/features/marketplace/components/marketplace-page.tsx +154 -132
  119. package/src/features/marketplace/hooks/use-marketplace-curated-scene-route.ts +97 -0
  120. package/src/features/marketplace/hooks/use-marketplace.ts +59 -3
  121. package/src/features/system-status/components/config/runtime-agent-list-card.tsx +4 -8
  122. package/src/features/system-status/components/config/runtime-binding-list-card.tsx +5 -7
  123. package/src/features/system-status/components/config/runtime-config-editor.tsx +1 -19
  124. package/src/features/system-status/components/config/runtime-entry-list-card.tsx +10 -11
  125. package/src/features/system-status/components/config/runtime-settings-card.tsx +15 -23
  126. package/src/features/system-status/components/runtime-control-card.test.tsx +8 -6
  127. package/src/features/system-status/components/runtime-control-card.tsx +7 -6
  128. package/src/features/system-status/pages/runtime-config-page.test.tsx +19 -9
  129. package/src/features/system-status/pages/runtime-config-page.tsx +2 -3
  130. package/src/features/system-status/utils/runtime-config-agent.utils.ts +4 -4
  131. package/src/features/system-status/utils/system-status.utils.ts +31 -6
  132. package/src/index.css +8 -0
  133. package/src/platforms/desktop/components/desktop-app-shell.test.tsx +68 -0
  134. package/src/platforms/desktop/components/desktop-app-shell.tsx +46 -18
  135. package/src/platforms/desktop/components/desktop-window-chrome.tsx +30 -0
  136. package/src/platforms/desktop/index.ts +6 -0
  137. package/src/platforms/desktop/types/desktop-update.types.ts +3 -0
  138. package/src/platforms/desktop/utils/desktop-host.utils.ts +56 -0
  139. package/src/shared/components/common/brand-header.tsx +36 -16
  140. package/src/shared/components/config/provider-form-support.ts +2 -22
  141. package/src/shared/components/cron-config.tsx +12 -58
  142. package/src/shared/components/doc-browser/doc-browser.tsx +4 -4
  143. package/src/shared/components/ui/select.tsx +19 -7
  144. package/src/shared/lib/api/channel-auth.types.ts +1 -0
  145. package/src/shared/lib/api/ncp-session.types.ts +9 -0
  146. package/src/shared/lib/api/types.ts +12 -1
  147. package/src/shared/lib/api/utils/marketplace.utils.ts +7 -1
  148. package/src/shared/lib/cron/cron-job-view.utils.ts +59 -0
  149. package/src/shared/lib/cron/index.ts +1 -0
  150. package/src/shared/lib/i18n/{channel-auth.ts → channel-auth.constants.ts} +31 -0
  151. package/src/shared/lib/i18n/chat-labels.utils.ts +3 -2
  152. package/src/shared/lib/i18n/index.ts +20 -59
  153. package/src/shared/lib/i18n/{runtime-control.ts → runtime-control-labels.utils.ts} +30 -1
  154. package/src/shared/lib/provider-models/index.test.ts +39 -0
  155. package/src/shared/lib/provider-models/index.ts +1 -3
  156. package/src/shared/lib/ui-document-title/index.ts +0 -1
  157. package/tsconfig.json +1 -0
  158. package/vite.config.ts +1 -1
  159. package/vitest.config.ts +1 -1
  160. package/dist/assets/api-D2xRKmZd.js +0 -15
  161. package/dist/assets/app-manager-provider-CNaZboG4.js +0 -1
  162. package/dist/assets/app-navigation.config-Ihhrrt--.js +0 -1
  163. package/dist/assets/channels-list-page-p26lgxLk.js +0 -8
  164. package/dist/assets/chat-Dkh2qtuz.js +0 -61
  165. package/dist/assets/chat-page-DoTmE2wx.js +0 -1
  166. package/dist/assets/chunk-JZWAC4HX-Kydj4yEz.js +0 -3
  167. package/dist/assets/config-split-page-DIOCjj2Q.js +0 -1
  168. package/dist/assets/desktop-update-config-DlpzDfKM.js +0 -1
  169. package/dist/assets/doc-browser-C8FM5fC0.js +0 -1
  170. package/dist/assets/doc-browser-RJUOL_GO.js +0 -1
  171. package/dist/assets/doc-browser-p82AdNO-.js +0 -1
  172. package/dist/assets/folder-CeJKPx5P.js +0 -1
  173. package/dist/assets/hash-BqxRTZW5.js +0 -1
  174. package/dist/assets/i18n-DnTGDIRw.js +0 -1
  175. package/dist/assets/index-D8MKmXtO.css +0 -1
  176. package/dist/assets/index-pBvbJ5Mt.js +0 -2
  177. package/dist/assets/loader-circle-fd-vQKtW.js +0 -1
  178. package/dist/assets/logo-badge-KAe-7d8c.js +0 -1
  179. package/dist/assets/logos-C4sYP1Vl.js +0 -1
  180. package/dist/assets/marketplace-page-Cql0kDi-.js +0 -1
  181. package/dist/assets/marketplace-page-m4P5g_Ht.js +0 -49
  182. package/dist/assets/mcp-marketplace-page-9WVKl1m1.js +0 -1
  183. package/dist/assets/mcp-marketplace-page-ByzBQZcx.js +0 -40
  184. package/dist/assets/message-square-z_osm9c0.js +0 -1
  185. package/dist/assets/model-config-Dbr_0APb.js +0 -1
  186. package/dist/assets/play-Dv6Nr1Ew.js +0 -1
  187. package/dist/assets/plus-D8eKFY7h.js +0 -1
  188. package/dist/assets/provider-scoped-model-input-DFm6N2f7.js +0 -1
  189. package/dist/assets/providers-list-BJcLOjun.js +0 -1
  190. package/dist/assets/refresh-ccw-ByVwmnN_.js +0 -1
  191. package/dist/assets/refresh-cw-PcqoYB3K.js +0 -1
  192. package/dist/assets/remote-BOxo9iwd.js +0 -1
  193. package/dist/assets/runtime-config-page-CjLhnbSl.js +0 -1
  194. package/dist/assets/search-config-J4Htco-P.js +0 -1
  195. package/dist/assets/secrets-config-CUdERjco.js +0 -3
  196. package/dist/assets/sessions-config-page-DpK991fs.js +0 -2
  197. package/dist/assets/settings-drbWqzA4.js +0 -1
  198. package/dist/assets/skeleton-BK1SOSRA.js +0 -1
  199. package/dist/assets/theme-provider-0hxjiPc_.js +0 -2
  200. package/dist/assets/tooltip-Cj4yA0gH.js +0 -1
  201. package/dist/assets/trash-2-CBsHCfqq.js +0 -1
  202. package/dist/assets/use-config-38Ur-89i.js +0 -1
  203. package/dist/assets/use-confirm-dialog-DPQThaeU.js +0 -1
  204. package/dist/assets/use-infinite-scroll-loader-5Gf1xQi7.js +0 -1
  205. package/dist/assets/use-viewport-layout-D1XzKeip.js +0 -1
  206. package/dist/assets/x-CM-XDMpk.js +0 -1
  207. package/src/features/chat/components/config/sessions-config-detail-pane.tsx +0 -244
  208. package/src/features/chat/pages/sessions-config-page.test.tsx +0 -152
  209. package/src/features/chat/pages/sessions-config-page.tsx +0 -192
  210. /package/dist/assets/{config-hints-MogHYQ8G.js → config-hints-BNfpOL4J.js} +0 -0
@@ -1,6 +1,5 @@
1
1
  import { useMemo, useState } from "react";
2
2
  import {
3
- useCreateAgent,
4
3
  useDeleteAgent,
5
4
  useAgents,
6
5
  useUpdateAgent,
@@ -8,9 +7,7 @@ import {
8
7
  import { useConfig, useConfigMeta } from "@/shared/hooks/use-config";
9
8
  import type { AgentProfileView } from "@/shared/lib/api";
10
9
  import {
11
- AgentCreateDialog,
12
10
  AgentEditDialog,
13
- type AgentCreateFormState,
14
11
  type AgentEditFormState,
15
12
  } from "@/features/agents/components/agent-dialogs";
16
13
  import {
@@ -24,7 +21,11 @@ import {
24
21
  import { AgentAvatar } from "@/shared/components/common/agent-avatar";
25
22
  import { Button } from "@/shared/components/ui/button";
26
23
  import { Card, CardContent } from "@/shared/components/ui/card";
27
- import { NoticeCard } from "@/shared/components/ui/notice-card";
24
+ import {
25
+ Popover,
26
+ PopoverContent,
27
+ PopoverTrigger,
28
+ } from "@/shared/components/ui/popover";
28
29
  import { TagChip } from "@/shared/components/ui/tag-chip";
29
30
  import { PageLayout } from "@/app/components/layout/page-layout";
30
31
  import { t } from "@/shared/lib/i18n";
@@ -34,6 +35,7 @@ import {
34
35
  Bot,
35
36
  House,
36
37
  MessageCircle,
38
+ MoreHorizontal,
37
39
  Pencil,
38
40
  Plus,
39
41
  ShieldCheck,
@@ -41,86 +43,75 @@ import {
41
43
  Trash2,
42
44
  } from "lucide-react";
43
45
 
44
- const CARD_TONES = [
45
- {
46
- strip: "bg-[#efc37a]",
47
- chip: "border-[#f2d7a7] bg-[#fff8eb] text-[#8d5a18]",
48
- },
49
- {
50
- strip: "bg-[#8fd4c0]",
51
- chip: "border-[#bde6da] bg-[#effbf7] text-[#156653]",
52
- },
53
- {
54
- strip: "bg-[#b7c9fb]",
55
- chip: "border-[#d7e2ff] bg-[#f4f7ff] text-[#2d4d8f]",
56
- },
57
- ] as const;
58
-
59
- function resolveAgentTone(index: number, builtIn: boolean) {
60
- if (builtIn) {
61
- return {
62
- strip: "bg-[#e6b765]",
63
- chip: "border-[#f2d19c] bg-[#fff8ec] text-[#90550d]",
64
- };
65
- }
66
- return CARD_TONES[index % CARD_TONES.length];
67
- }
46
+ const AGENT_CREATION_PROMPT =
47
+ "请直接创建一个默认示例 Agent,不要问我问题。创建完成后,简单告诉我它能做什么。";
68
48
 
69
49
  function AgentsHero(props: { agentCount: number; onCreate: () => void }) {
70
50
  const { agentCount, onCreate } = props;
71
51
 
72
52
  return (
73
- <section className="relative overflow-hidden rounded-[28px] border border-[#f0d6aa] bg-[linear-gradient(135deg,#fff7ea_0%,#fff9f1_32%,#f2fbff_100%)] px-5 py-5 sm:px-6">
74
- <div className="absolute inset-y-0 right-0 w-[46%] bg-[radial-gradient(circle_at_top_right,rgba(255,215,163,0.52),transparent_54%)]" />
75
- <div className="absolute -bottom-10 left-8 h-32 w-32 rounded-full bg-[#ffe6c0]/55 blur-3xl" />
76
- <div className="relative grid gap-5 xl:grid-cols-[minmax(0,1fr)_320px] xl:items-center">
77
- <div className="max-w-3xl space-y-3">
78
- <div className="inline-flex items-center gap-2 rounded-full border border-white/70 bg-white/80 px-3 py-1 text-[11px] font-semibold tracking-[0.16em] text-[#9b6118]">
79
- <Sparkles className="h-3.5 w-3.5" />
53
+ <section className="flex flex-col gap-3 border-b border-gray-200 pb-4 sm:flex-row sm:items-end sm:justify-between">
54
+ <div className="min-w-0 space-y-1">
55
+ <div className="flex flex-wrap items-center gap-2">
56
+ <h1 className="text-xl font-semibold text-gray-950">
80
57
  {t("agentsHeroEyebrow")}
81
- </div>
82
- <div className="space-y-2">
83
- <h1 className="max-w-2xl text-[30px] font-semibold leading-tight tracking-[-0.05em] text-[#2f2212] sm:text-[38px]">
84
- {t("agentsHeroTitle")}
85
- </h1>
86
- <p className="max-w-2xl text-sm leading-6 text-[#6d5841] sm:text-[15px] sm:leading-7">
87
- {t("agentsHeroDescription")}
88
- </p>
89
- </div>
90
- <div className="pt-1">
91
- <div className="inline-flex items-center gap-3 rounded-2xl border border-[#f2d5a4] bg-white/82 px-3 py-2 text-[#7a4d12] shadow-[0_14px_30px_rgba(167,117,47,0.07)]">
92
- <span className="text-[11px] font-semibold tracking-[0.14em]">
93
- {t("agentsOverviewTotal")}
94
- </span>
95
- <span className="text-xl font-semibold tracking-[-0.04em] text-[#1f2937]">
96
- {agentCount}
97
- </span>
98
- </div>
99
- </div>
100
- </div>
101
- <div className="flex shrink-0 flex-col gap-3">
102
- <Button
103
- type="button"
104
- variant="primary"
105
- className="h-10 rounded-2xl px-5 text-sm font-semibold"
106
- onClick={onCreate}
107
- >
108
- <Plus className="mr-2 h-4 w-4" />
109
- {t("agentsCreateButton")}
110
- </Button>
111
- <NoticeCard
112
- title={t("agentsCreateDialogHint")}
113
- className="border-white/70 bg-white/72 text-xs leading-6 shadow-[0_18px_40px_rgba(167,117,47,0.08)]"
114
- />
58
+ </h1>
59
+ <span className="rounded-full border border-gray-200 bg-white px-2 py-0.5 text-xs font-medium text-gray-500">
60
+ {agentCount}
61
+ </span>
115
62
  </div>
63
+ <p className="max-w-2xl text-sm leading-6 text-gray-500">
64
+ {t("agentsHeroDescription")}
65
+ </p>
116
66
  </div>
67
+ <Button
68
+ type="button"
69
+ variant="primary"
70
+ className="h-9 shrink-0 rounded-xl px-4 text-sm font-semibold"
71
+ onClick={onCreate}
72
+ >
73
+ <Plus className="mr-2 h-4 w-4" />
74
+ {t("agentsCreateButton")}
75
+ </Button>
117
76
  </section>
118
77
  );
119
78
  }
120
79
 
80
+ function AgentActionMenuItem(props: {
81
+ icon: typeof Pencil;
82
+ label: string;
83
+ disabled?: boolean;
84
+ destructive?: boolean;
85
+ onClick: () => void;
86
+ }) {
87
+ const {
88
+ icon: Icon,
89
+ label,
90
+ disabled = false,
91
+ destructive = false,
92
+ onClick,
93
+ } = props;
94
+
95
+ return (
96
+ <button
97
+ type="button"
98
+ className={cn(
99
+ "flex w-full items-center gap-2 rounded-lg px-3 py-2 text-left text-sm transition-colors disabled:cursor-not-allowed disabled:opacity-50",
100
+ destructive
101
+ ? "text-destructive hover:bg-destructive/10"
102
+ : "text-gray-700 hover:bg-gray-100",
103
+ )}
104
+ onClick={onClick}
105
+ disabled={disabled}
106
+ >
107
+ <Icon className="h-4 w-4 shrink-0" />
108
+ <span>{label}</span>
109
+ </button>
110
+ );
111
+ }
112
+
121
113
  function AgentListCard(props: {
122
114
  agent: AgentProfileView;
123
- index: number;
124
115
  runtimeOptions: { value: string; label: string }[];
125
116
  defaultRuntimeLabel: string;
126
117
  updatePending: boolean;
@@ -131,7 +122,6 @@ function AgentListCard(props: {
131
122
  }) {
132
123
  const {
133
124
  agent,
134
- index,
135
125
  runtimeOptions,
136
126
  defaultRuntimeLabel,
137
127
  updatePending,
@@ -140,7 +130,6 @@ function AgentListCard(props: {
140
130
  onEdit,
141
131
  onDelete,
142
132
  } = props;
143
- const tone = resolveAgentTone(index, Boolean(agent.builtIn));
144
133
  const runtimeValue = agent.runtime?.trim() || agent.engine?.trim() || "";
145
134
  const runtimeLabel = runtimeValue
146
135
  ? (runtimeOptions.find(
@@ -149,94 +138,94 @@ function AgentListCard(props: {
149
138
  : defaultRuntimeLabel;
150
139
 
151
140
  return (
152
- <Card className="overflow-hidden border border-gray-200 bg-white shadow-sm transition-shadow duration-200 hover:shadow-md">
153
- <div className={cn("h-1.5 w-full", tone.strip)} />
154
- <CardContent className="flex h-full flex-col gap-4 px-4 py-4">
155
- <div className="flex items-start gap-3">
141
+ <Card className="group overflow-hidden border border-gray-200 bg-white shadow-none transition-colors duration-200 hover:border-gray-300">
142
+ <CardContent className="relative flex h-full flex-col gap-3 px-3.5 py-3.5">
143
+ <div className="flex items-start gap-2.5 pr-16">
156
144
  <AgentAvatar
157
145
  agentId={agent.id}
158
146
  displayName={agent.displayName}
159
147
  avatarUrl={agent.avatarUrl}
160
- className="h-11 w-11 shrink-0"
148
+ className="h-9 w-9 shrink-0"
161
149
  />
162
- <div className="min-w-0 flex-1 space-y-1 pt-0.5">
163
- <div className="flex flex-wrap items-center gap-2">
164
- <div className="truncate text-lg font-semibold tracking-[-0.03em] text-[#1f2937]">
150
+ <div className="min-w-0 flex-1 space-y-0.5">
151
+ <div className="flex min-w-0 items-center gap-2">
152
+ <div className="truncate text-sm font-semibold text-gray-950">
165
153
  {agent.displayName?.trim() || agent.id}
166
154
  </div>
167
155
  {agent.builtIn ? (
168
- <TagChip tone="warning" className={cn("gap-1", tone.chip)}>
156
+ <TagChip
157
+ tone="warning"
158
+ className="h-5 gap-1 border-amber-200 bg-amber-50 px-1.5 text-[10px] text-amber-700"
159
+ >
169
160
  <ShieldCheck className="h-3 w-3" />
170
161
  {t("agentsCardBuiltInTag")}
171
162
  </TagChip>
172
163
  ) : null}
173
164
  </div>
174
- <div className="text-[11px] font-medium uppercase tracking-[0.18em] text-[#94a3b8]">
175
- @{agent.id}
176
- </div>
165
+ <div className="truncate text-xs text-gray-400">@{agent.id}</div>
177
166
  </div>
178
167
  </div>
179
168
 
180
- <p className="text-sm leading-6 text-[#64748b]">
169
+ <div className="absolute right-2.5 top-2.5 flex items-center gap-1 opacity-100 transition-opacity md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100">
170
+ <Button
171
+ type="button"
172
+ variant="ghost"
173
+ size="icon"
174
+ className="h-8 w-8 rounded-lg text-gray-400 hover:text-gray-800"
175
+ aria-label={t("agentsCardStartChat")}
176
+ title={t("agentsCardStartChat")}
177
+ onClick={onStartChat}
178
+ >
179
+ <MessageCircle className="h-4 w-4" />
180
+ </Button>
181
+ <Popover>
182
+ <PopoverTrigger asChild>
183
+ <Button
184
+ type="button"
185
+ variant="ghost"
186
+ size="icon"
187
+ className="h-8 w-8 rounded-lg text-gray-400 hover:text-gray-800"
188
+ aria-label={t("chatSessionMoreActions")}
189
+ title={t("chatSessionMoreActions")}
190
+ >
191
+ <MoreHorizontal className="h-4 w-4" />
192
+ </Button>
193
+ </PopoverTrigger>
194
+ <PopoverContent align="end" className="w-44 p-1.5">
195
+ <AgentActionMenuItem
196
+ icon={Pencil}
197
+ label={t("agentsEditAction")}
198
+ onClick={onEdit}
199
+ disabled={updatePending}
200
+ />
201
+ {!agent.builtIn ? (
202
+ <AgentActionMenuItem
203
+ icon={Trash2}
204
+ label={t("agentsRemoveAction")}
205
+ onClick={onDelete}
206
+ disabled={deletePending}
207
+ destructive
208
+ />
209
+ ) : null}
210
+ </PopoverContent>
211
+ </Popover>
212
+ </div>
213
+
214
+ <p className="line-clamp-2 min-h-10 text-sm leading-5 text-gray-600">
181
215
  {agent.description?.trim() ||
182
216
  (agent.builtIn
183
217
  ? t("agentsCardBuiltInSummary")
184
218
  : t("agentsCardCustomSummary"))}
185
219
  </p>
186
220
 
187
- <div className="mt-auto flex flex-col gap-4">
188
- <div>
189
- <div className="flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-[#94a3b8]">
190
- <Sparkles className="h-3.5 w-3.5" />
191
- {t("agentsCardRuntimeLabel")}
192
- </div>
193
- <div className="mt-1.5 text-sm leading-6 text-[#475569]">
194
- {runtimeLabel}
195
- </div>
196
- </div>
197
-
198
- <div className="border-t border-gray-100 pt-3">
199
- <div className="flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-[#94a3b8]">
200
- <House className="h-3.5 w-3.5" />
201
- {t("agentsCardHomeLabel")}
202
- </div>
203
- <div className="mt-1.5 break-all text-sm leading-6 text-[#475569]">
204
- {agent.workspace ?? "-"}
205
- </div>
221
+ <div className="mt-auto grid gap-2 border-t border-gray-100 pt-2 text-xs text-gray-500">
222
+ <div className="flex min-w-0 items-center gap-2">
223
+ <Sparkles className="h-3.5 w-3.5 shrink-0 text-gray-300" />
224
+ <span className="truncate">{runtimeLabel}</span>
206
225
  </div>
207
-
208
- <div className="flex flex-wrap items-center gap-2">
209
- <Button
210
- type="button"
211
- variant="primary"
212
- className="h-9 rounded-xl px-4"
213
- onClick={onStartChat}
214
- >
215
- <MessageCircle className="mr-2 h-4 w-4" />
216
- {t("agentsCardStartChat")}
217
- </Button>
218
- <Button
219
- type="button"
220
- variant="ghost"
221
- className="h-8 rounded-xl px-3 text-xs text-[#7b8794] hover:bg-[#f3f4f6] hover:text-[#475569]"
222
- onClick={onEdit}
223
- disabled={updatePending}
224
- >
225
- <Pencil className="mr-1.5 h-3.5 w-3.5" />
226
- {t("agentsEditAction")}
227
- </Button>
228
- {!agent.builtIn ? (
229
- <Button
230
- type="button"
231
- variant="ghost"
232
- className="h-8 rounded-xl px-3 text-xs text-[#7b8794] hover:bg-[#f3f4f6] hover:text-[#475569]"
233
- onClick={onDelete}
234
- disabled={deletePending}
235
- >
236
- <Trash2 className="mr-1.5 h-3.5 w-3.5" />
237
- {t("agentsRemoveAction")}
238
- </Button>
239
- ) : null}
226
+ <div className="flex min-w-0 items-center gap-2">
227
+ <House className="h-3.5 w-3.5 shrink-0 text-gray-300" />
228
+ <span className="truncate">{agent.workspace ?? "-"}</span>
240
229
  </div>
241
230
  </div>
242
231
  </CardContent>
@@ -250,10 +239,8 @@ export function AgentsPage() {
250
239
  const configQuery = useConfig();
251
240
  const configMetaQuery = useConfigMeta();
252
241
  const sessionTypesQuery = useNcpChatSessionTypes();
253
- const createAgent = useCreateAgent();
254
242
  const updateAgent = useUpdateAgent();
255
243
  const deleteAgent = useDeleteAgent();
256
- const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);
257
244
  const [editingAgent, setEditingAgent] = useState<AgentProfileView | null>(
258
245
  null,
259
246
  );
@@ -294,25 +281,6 @@ export function AgentsPage() {
294
281
  [defaultRuntime, runtimeOptions],
295
282
  );
296
283
 
297
- const handleCreate = async (form: AgentCreateFormState) => {
298
- await createAgent.mutateAsync({
299
- data: {
300
- id: form.id,
301
- ...(form.displayName.trim()
302
- ? { displayName: form.displayName.trim() }
303
- : {}),
304
- ...(form.description.trim()
305
- ? { description: form.description.trim() }
306
- : {}),
307
- ...(form.avatar.trim() ? { avatar: form.avatar.trim() } : {}),
308
- ...(form.home.trim() ? { home: form.home.trim() } : {}),
309
- ...(form.model.trim() ? { model: form.model.trim() } : {}),
310
- ...(form.runtime.trim() ? { runtime: form.runtime.trim() } : {}),
311
- },
312
- });
313
- setIsCreateDialogOpen(false);
314
- };
315
-
316
284
  const handleStartEdit = (agent: AgentProfileView) => {
317
285
  setEditingAgent(agent);
318
286
  };
@@ -344,7 +312,9 @@ export function AgentsPage() {
344
312
  <PageLayout className="space-y-5">
345
313
  <AgentsHero
346
314
  agentCount={agents.length}
347
- onCreate={() => setIsCreateDialogOpen(true)}
315
+ onCreate={() =>
316
+ presenter.startAgentCreationDraft(AGENT_CREATION_PROMPT)
317
+ }
348
318
  />
349
319
 
350
320
  <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
@@ -370,7 +340,9 @@ export function AgentsPage() {
370
340
  type="button"
371
341
  variant="primary"
372
342
  className="mt-5 rounded-2xl px-5"
373
- onClick={() => setIsCreateDialogOpen(true)}
343
+ onClick={() =>
344
+ presenter.startAgentCreationDraft(AGENT_CREATION_PROMPT)
345
+ }
374
346
  >
375
347
  <Plus className="mr-2 h-4 w-4" />
376
348
  {t("agentsCreateButton")}
@@ -378,11 +350,10 @@ export function AgentsPage() {
378
350
  </CardContent>
379
351
  </Card>
380
352
  ) : (
381
- sortedAgents.map((agent, index) => (
353
+ sortedAgents.map((agent) => (
382
354
  <AgentListCard
383
355
  key={agent.id}
384
356
  agent={agent}
385
- index={index}
386
357
  runtimeOptions={runtimeOptions}
387
358
  defaultRuntimeLabel={defaultRuntimeLabel}
388
359
  updatePending={updateAgent.isPending}
@@ -395,16 +366,6 @@ export function AgentsPage() {
395
366
  )}
396
367
  </div>
397
368
 
398
- <AgentCreateDialog
399
- open={isCreateDialogOpen}
400
- pending={createAgent.isPending}
401
- providerCatalog={providerCatalog}
402
- runtimeOptions={runtimeOptions}
403
- defaultRuntime={defaultRuntime}
404
- onOpenChange={setIsCreateDialogOpen}
405
- onSubmit={handleCreate}
406
- />
407
-
408
369
  <AgentEditDialog
409
370
  agent={editingAgent}
410
371
  pending={updateAgent.isPending}
@@ -64,6 +64,7 @@ vi.mock('@/shared/lib/transport', () => ({
64
64
  }));
65
65
 
66
66
  vi.mock('@/features/channels/components/config/weixin-channel-auth-section', () => ({
67
+ QrChannelAuthSection: () => null,
67
68
  WeixinChannelAuthSection: () => null
68
69
  }));
69
70
 
@@ -10,7 +10,7 @@ import { nextclawClient } from '@/shared/lib/api';
10
10
  import { useConfig, useConfigMeta, useConfigSchema, useExecuteConfigAction, useUpdateChannel } from '@/shared/hooks/use-config';
11
11
  import type { ConfigActionManifest, ConfigUiHints } from '@/shared/lib/api';
12
12
  import { ChannelFormFieldsSection } from '@/features/channels/components/channel-form-fields-section';
13
- import { WeixinChannelAuthSection } from '@/features/channels/components/config/weixin-channel-auth-section';
13
+ import { QrChannelAuthSection } from '@/features/channels/components/config/weixin-channel-auth-section';
14
14
  import { buildChannelFormDefinitions, type ChannelField, type ChannelFormBlock, type ChannelFormFieldSection } from '@/features/channels/utils/channel-form-fields.utils';
15
15
  import { ConfigSplitDetailPane, ConfigSplitEmptyPane, ConfigSplitPaneBody, ConfigSplitPaneFooter, ConfigSplitPaneHeader } from '@/shared/components/config-split-page';
16
16
  import { hintForPath } from '@/shared/lib/config-hints';
@@ -205,11 +205,12 @@ function ChannelFormBlocks(props: {
205
205
  </details>
206
206
  );
207
207
  }
208
- return block.sectionId === 'weixin-auth' ? (
209
- <WeixinChannelAuthSection
208
+ return block.sectionId === 'weixin-auth' || block.sectionId === 'feishu-auth' ? (
209
+ <QrChannelAuthSection
210
210
  key={`${block.type}-${block.sectionId}-${index}`}
211
211
  channelConfig={props.channelConfig}
212
212
  formData={props.formData}
213
+ channelName={props.channelName === 'feishu' ? 'feishu' : 'weixin'}
213
214
  channelEnabled={props.enabled}
214
215
  disabled={props.disabled}
215
216
  />
@@ -1,7 +1,7 @@
1
1
  import { render, screen, waitFor } from '@testing-library/react';
2
2
  import userEvent from '@testing-library/user-event';
3
3
  import type * as ReactQueryModule from '@tanstack/react-query';
4
- import { WeixinChannelAuthSection } from '@/features/channels/components/config/weixin-channel-auth-section';
4
+ import { QrChannelAuthSection, WeixinChannelAuthSection } from '@/features/channels/components/config/weixin-channel-auth-section';
5
5
 
6
6
  const mocks = vi.hoisted(() => ({
7
7
  startChannelAuthMutateAsync: vi.fn(),
@@ -117,4 +117,41 @@ describe('WeixinChannelAuthSection', () => {
117
117
  ).toBeTruthy();
118
118
  expect(screen.getByRole('button', { name: 'Reconnect with QR' })).toBeTruthy();
119
119
  });
120
+
121
+ it('starts feishu QR auth with the selected domain', async () => {
122
+ const user = userEvent.setup();
123
+ mocks.startChannelAuthMutateAsync.mockResolvedValue({
124
+ channel: 'feishu',
125
+ kind: 'qr_code',
126
+ sessionId: 'session-1',
127
+ qrCode: 'qr-token',
128
+ qrCodeUrl: 'https://accounts.feishu.cn/qr',
129
+ expiresAt: '2026-03-24T10:00:00.000Z',
130
+ intervalMs: 60_000,
131
+ note: '请扫码'
132
+ });
133
+ mocks.pollChannelAuthMutateAsync.mockImplementation(() => new Promise(() => {}));
134
+
135
+ render(
136
+ <QrChannelAuthSection
137
+ channelName="feishu"
138
+ channelConfig={{ enabled: false, domain: 'feishu' }}
139
+ formData={{ domain: 'lark', defaultAccountId: 'primary' }}
140
+ channelEnabled={false}
141
+ />
142
+ );
143
+
144
+ await user.click(screen.getByRole('button', { name: 'Scan QR to connect Feishu' }));
145
+
146
+ await waitFor(() => {
147
+ expect(mocks.startChannelAuthMutateAsync).toHaveBeenCalledWith({
148
+ channel: 'feishu',
149
+ data: {
150
+ accountId: 'primary',
151
+ baseUrl: undefined,
152
+ domain: 'lark'
153
+ }
154
+ });
155
+ });
156
+ });
120
157
  });