@nextclaw/ui 0.12.15 → 0.12.16

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 (88) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/assets/{api-HsS9C7l0.js → api-BIg--UMJ.js} +1 -1
  3. package/dist/assets/{app-manager-provider-CNa6cmOk.js → app-manager-provider-BfKiVYea.js} +1 -1
  4. package/dist/assets/{app-navigation.config-Dy69P_3O.js → app-navigation.config-xIjCAn-R.js} +1 -1
  5. package/dist/assets/{book-open-DJSe9YLj.js → book-open-CUd69I2f.js} +1 -1
  6. package/dist/assets/{channels-list-page-DtjYJ1FX.js → channels-list-page-5wQy-UW7.js} +1 -1
  7. package/dist/assets/chat-CXrERmQ1.js +60 -0
  8. package/dist/assets/{chat-page-Cslvp6SG.js → chat-page-CTLX0KsB.js} +1 -1
  9. package/dist/assets/{chunk-JZWAC4HX-CMPxE3BJ.js → chunk-JZWAC4HX-THqEFwu9.js} +1 -1
  10. package/dist/assets/{config-split-page-v7p1CQjU.js → config-split-page-UJSTBsEU.js} +1 -1
  11. package/dist/assets/{createLucideIcon-Y1cWg3R9.js → createLucideIcon-DVAlgDOi.js} +1 -1
  12. package/dist/assets/{desktop-update-config-CqJmmpjR.js → desktop-update-config-B0vQ5JID.js} +1 -1
  13. package/dist/assets/{dialog-CYis0wx_.js → dialog-t7OAmObC.js} +1 -1
  14. package/dist/assets/{dist-DIWOVzPY.js → dist-DWPNydLC.js} +1 -1
  15. package/dist/assets/doc-browser-BIggpN8Z.js +1 -0
  16. package/dist/assets/{doc-browser-BQT0fa3g.js → doc-browser-DznuT-CU.js} +1 -1
  17. package/dist/assets/{doc-browser-context-BacwCbOG.js → doc-browser-context-zXaTjrpA.js} +1 -1
  18. package/dist/assets/{doc-browser-CP6kjGaF.js → doc-browser-t96ibd-b.js} +1 -1
  19. package/dist/assets/{es2015-3VnZDFPs.js → es2015-BRVsmfFO.js} +1 -1
  20. package/dist/assets/{external-link-CPhZo92k.js → external-link-D1Xqff6i.js} +1 -1
  21. package/dist/assets/{folder-orBwjR8h.js → folder-B_fuaX3x.js} +1 -1
  22. package/dist/assets/{hash-BvWW8Scd.js → hash-BRvv_UUq.js} +1 -1
  23. package/dist/assets/i18n-CF_jgT_-.js +1 -0
  24. package/dist/assets/index-DuHqJ8wn.css +1 -0
  25. package/dist/assets/{index-D0_J-bk_.js → index-mw77my39.js} +2 -2
  26. package/dist/assets/{key-round-B5u2XQwa.js → key-round-DFVNXZcD.js} +1 -1
  27. package/dist/assets/loader-circle-DEz3bHGb.js +1 -0
  28. package/dist/assets/{logo-badge-BLQdO_th.js → logo-badge-CRVKkIl9.js} +1 -1
  29. package/dist/assets/{logos-WPtZ1joO.js → logos-BVCi_7_I.js} +1 -1
  30. package/dist/assets/marketplace-page-DVmk8dZk.js +1 -0
  31. package/dist/assets/{marketplace-page-It54wla_.js → marketplace-page-apq5LpYx.js} +1 -1
  32. package/dist/assets/mcp-marketplace-page-CskrJuKU.js +1 -0
  33. package/dist/assets/{mcp-marketplace-page-C64VUmni.js → mcp-marketplace-page-DiqTAdRJ.js} +1 -1
  34. package/dist/assets/message-square-CLhDWybk.js +1 -0
  35. package/dist/assets/{model-config-Cu6zLI8a.js → model-config-cth12uRn.js} +1 -1
  36. package/dist/assets/{notice-card-DDVM6IFQ.js → notice-card-CXY09tsa.js} +1 -1
  37. package/dist/assets/play-CnnPm8ca.js +1 -0
  38. package/dist/assets/plus-CdYMdiws.js +1 -0
  39. package/dist/assets/{popover-Dliq2K8O.js → popover-CbQxrchk.js} +1 -1
  40. package/dist/assets/{provider-scoped-model-input-D0oXIrWA.js → provider-scoped-model-input-CZEB2m98.js} +1 -1
  41. package/dist/assets/{providers-list-CrwTWzge.js → providers-list-Dsj2BYPm.js} +1 -1
  42. package/dist/assets/{refresh-ccw-pX3BUxPF.js → refresh-ccw-C-ytTHiq.js} +1 -1
  43. package/dist/assets/remote-DMMC2PSo.js +1 -0
  44. package/dist/assets/{rotate-cw-Bm8W8iaF.js → rotate-cw-ClSrRUa0.js} +1 -1
  45. package/dist/assets/{runtime-config-page-CYZyCr04.js → runtime-config-page-JpAUjHTI.js} +1 -1
  46. package/dist/assets/{save-CZ9zvmBJ.js → save-KxhpE3Zr.js} +1 -1
  47. package/dist/assets/{search-DpxqxcpP.js → search-Bz3Q64sr.js} +1 -1
  48. package/dist/assets/{search-config-D_H6cr07.js → search-config-C_xRBv_i.js} +1 -1
  49. package/dist/assets/{secrets-config-BG9fic_R.js → secrets-config-BOL024Fj.js} +1 -1
  50. package/dist/assets/{select-D-gjRjhr.js → select-tRTLG4FK.js} +1 -1
  51. package/dist/assets/{sessions-config-page-DzSOCrQt.js → sessions-config-page-CLEEGNYL.js} +1 -1
  52. package/dist/assets/{setting-row-Cl4_XpIR.js → setting-row-DMDgBCC7.js} +1 -1
  53. package/dist/assets/{settings-C9WgQdGW.js → settings-Cto6z-Ij.js} +1 -1
  54. package/dist/assets/skeleton-BwfJfVK3.js +1 -0
  55. package/dist/assets/{sparkles-gvRilA9f.js → sparkles-xZ74eW0P.js} +1 -1
  56. package/dist/assets/{status-dot-W2TPs1Zf.js → status-dot-Bobpfutv.js} +1 -1
  57. package/dist/assets/{tabs-custom-BxZfWeD1.js → tabs-custom-C3Mf-NLb.js} +1 -1
  58. package/dist/assets/{tag-chip-DfmhmN50.js → tag-chip-BZ14i5b1.js} +1 -1
  59. package/dist/assets/{theme-provider-CajCgE_N.js → theme-provider-D6Cgm6i-.js} +1 -1
  60. package/dist/assets/{tooltip-C1XVx_h1.js → tooltip-7lsLGcL9.js} +1 -1
  61. package/dist/assets/{trash-2-WC4Dlakj.js → trash-2-WFSNa5oj.js} +1 -1
  62. package/dist/assets/{use-config-BIErdQNR.js → use-config-CXu7dFzw.js} +1 -1
  63. package/dist/assets/{use-confirm-dialog-B3ZsM4V-.js → use-confirm-dialog-Df1SozKw.js} +1 -1
  64. package/dist/assets/{use-infinite-scroll-loader-rO55YHGO.js → use-infinite-scroll-loader-BogRuzgz.js} +1 -1
  65. package/dist/assets/{use-viewport-layout-zLAsjh3A.js → use-viewport-layout-CWlW5b-T.js} +1 -1
  66. package/dist/assets/x-BvS2y4e_.js +1 -0
  67. package/dist/index.html +39 -39
  68. package/package.json +4 -4
  69. package/src/features/chat/components/conversation/chat-input-bar.container.tsx +5 -1
  70. package/src/features/chat/utils/chat-input-bar.utils.test.ts +3 -3
  71. package/src/features/chat/utils/chat-input-toolbar.utils.ts +1 -1
  72. package/src/index.css +97 -5
  73. package/src/shared/components/common/agent-avatar.test.tsx +33 -0
  74. package/src/shared/components/common/agent-avatar.tsx +6 -9
  75. package/src/shared/lib/i18n/chat.ts +2 -1
  76. package/dist/assets/chat-Rmwi-kQn.js +0 -58
  77. package/dist/assets/doc-browser-DK2tXLi_.js +0 -1
  78. package/dist/assets/i18n-BhoFaF7E.js +0 -1
  79. package/dist/assets/index-OrUEVLgT.css +0 -1
  80. package/dist/assets/loader-circle-DotuB8NZ.js +0 -1
  81. package/dist/assets/marketplace-page-CJHkdgv_.js +0 -1
  82. package/dist/assets/mcp-marketplace-page-DIItTRy_.js +0 -1
  83. package/dist/assets/message-square-CO_7rU-N.js +0 -1
  84. package/dist/assets/play-kSfmJAme.js +0 -1
  85. package/dist/assets/plus-BNaF7Wq5.js +0 -1
  86. package/dist/assets/remote-BpAw88FR.js +0 -1
  87. package/dist/assets/skeleton-BN6fOana.js +0 -1
  88. package/dist/assets/x-D1867E7F.js +0 -1
package/dist/index.html CHANGED
@@ -78,45 +78,45 @@
78
78
  })();
79
79
  </script>
80
80
  <title>NextClaw</title>
81
- <script type="module" crossorigin src="/assets/index-D0_J-bk_.js"></script>
82
- <link rel="modulepreload" crossorigin href="/assets/i18n-BhoFaF7E.js">
83
- <link rel="modulepreload" crossorigin href="/assets/api-HsS9C7l0.js">
84
- <link rel="modulepreload" crossorigin href="/assets/es2015-3VnZDFPs.js">
85
- <link rel="modulepreload" crossorigin href="/assets/createLucideIcon-Y1cWg3R9.js">
86
- <link rel="modulepreload" crossorigin href="/assets/select-D-gjRjhr.js">
87
- <link rel="modulepreload" crossorigin href="/assets/dist-DIWOVzPY.js">
88
- <link rel="modulepreload" crossorigin href="/assets/x-D1867E7F.js">
89
- <link rel="modulepreload" crossorigin href="/assets/dialog-CYis0wx_.js">
90
- <link rel="modulepreload" crossorigin href="/assets/popover-Dliq2K8O.js">
91
- <link rel="modulepreload" crossorigin href="/assets/tooltip-C1XVx_h1.js">
92
- <link rel="modulepreload" crossorigin href="/assets/chunk-JZWAC4HX-CMPxE3BJ.js">
93
- <link rel="modulepreload" crossorigin href="/assets/use-config-BIErdQNR.js">
94
- <link rel="modulepreload" crossorigin href="/assets/theme-provider-CajCgE_N.js">
95
- <link rel="modulepreload" crossorigin href="/assets/search-DpxqxcpP.js">
96
- <link rel="modulepreload" crossorigin href="/assets/book-open-DJSe9YLj.js">
97
- <link rel="modulepreload" crossorigin href="/assets/external-link-CPhZo92k.js">
98
- <link rel="modulepreload" crossorigin href="/assets/folder-orBwjR8h.js">
99
- <link rel="modulepreload" crossorigin href="/assets/logos-WPtZ1joO.js">
100
- <link rel="modulepreload" crossorigin href="/assets/loader-circle-DotuB8NZ.js">
101
- <link rel="modulepreload" crossorigin href="/assets/plus-BNaF7Wq5.js">
102
- <link rel="modulepreload" crossorigin href="/assets/refresh-ccw-pX3BUxPF.js">
103
- <link rel="modulepreload" crossorigin href="/assets/settings-C9WgQdGW.js">
104
- <link rel="modulepreload" crossorigin href="/assets/sparkles-gvRilA9f.js">
105
- <link rel="modulepreload" crossorigin href="/assets/trash-2-WC4Dlakj.js">
106
- <link rel="modulepreload" crossorigin href="/assets/doc-browser-context-BacwCbOG.js">
107
- <link rel="modulepreload" crossorigin href="/assets/doc-browser-CP6kjGaF.js">
108
- <link rel="modulepreload" crossorigin href="/assets/doc-browser-BQT0fa3g.js">
109
- <link rel="modulepreload" crossorigin href="/assets/use-viewport-layout-zLAsjh3A.js">
110
- <link rel="modulepreload" crossorigin href="/assets/logo-badge-BLQdO_th.js">
111
- <link rel="modulepreload" crossorigin href="/assets/skeleton-BN6fOana.js">
112
- <link rel="modulepreload" crossorigin href="/assets/chat-Rmwi-kQn.js">
113
- <link rel="modulepreload" crossorigin href="/assets/key-round-B5u2XQwa.js">
114
- <link rel="modulepreload" crossorigin href="/assets/message-square-CO_7rU-N.js">
115
- <link rel="modulepreload" crossorigin href="/assets/app-navigation.config-Dy69P_3O.js">
116
- <link rel="modulepreload" crossorigin href="/assets/notice-card-DDVM6IFQ.js">
117
- <link rel="modulepreload" crossorigin href="/assets/status-dot-W2TPs1Zf.js">
118
- <link rel="modulepreload" crossorigin href="/assets/app-manager-provider-CNa6cmOk.js">
119
- <link rel="stylesheet" crossorigin href="/assets/index-OrUEVLgT.css">
81
+ <script type="module" crossorigin src="/assets/index-mw77my39.js"></script>
82
+ <link rel="modulepreload" crossorigin href="/assets/i18n-CF_jgT_-.js">
83
+ <link rel="modulepreload" crossorigin href="/assets/api-BIg--UMJ.js">
84
+ <link rel="modulepreload" crossorigin href="/assets/es2015-BRVsmfFO.js">
85
+ <link rel="modulepreload" crossorigin href="/assets/createLucideIcon-DVAlgDOi.js">
86
+ <link rel="modulepreload" crossorigin href="/assets/select-tRTLG4FK.js">
87
+ <link rel="modulepreload" crossorigin href="/assets/dist-DWPNydLC.js">
88
+ <link rel="modulepreload" crossorigin href="/assets/x-BvS2y4e_.js">
89
+ <link rel="modulepreload" crossorigin href="/assets/dialog-t7OAmObC.js">
90
+ <link rel="modulepreload" crossorigin href="/assets/popover-CbQxrchk.js">
91
+ <link rel="modulepreload" crossorigin href="/assets/tooltip-7lsLGcL9.js">
92
+ <link rel="modulepreload" crossorigin href="/assets/chunk-JZWAC4HX-THqEFwu9.js">
93
+ <link rel="modulepreload" crossorigin href="/assets/use-config-CXu7dFzw.js">
94
+ <link rel="modulepreload" crossorigin href="/assets/theme-provider-D6Cgm6i-.js">
95
+ <link rel="modulepreload" crossorigin href="/assets/search-Bz3Q64sr.js">
96
+ <link rel="modulepreload" crossorigin href="/assets/book-open-CUd69I2f.js">
97
+ <link rel="modulepreload" crossorigin href="/assets/external-link-D1Xqff6i.js">
98
+ <link rel="modulepreload" crossorigin href="/assets/folder-B_fuaX3x.js">
99
+ <link rel="modulepreload" crossorigin href="/assets/logos-BVCi_7_I.js">
100
+ <link rel="modulepreload" crossorigin href="/assets/loader-circle-DEz3bHGb.js">
101
+ <link rel="modulepreload" crossorigin href="/assets/plus-CdYMdiws.js">
102
+ <link rel="modulepreload" crossorigin href="/assets/refresh-ccw-C-ytTHiq.js">
103
+ <link rel="modulepreload" crossorigin href="/assets/settings-Cto6z-Ij.js">
104
+ <link rel="modulepreload" crossorigin href="/assets/sparkles-xZ74eW0P.js">
105
+ <link rel="modulepreload" crossorigin href="/assets/trash-2-WFSNa5oj.js">
106
+ <link rel="modulepreload" crossorigin href="/assets/doc-browser-context-zXaTjrpA.js">
107
+ <link rel="modulepreload" crossorigin href="/assets/doc-browser-t96ibd-b.js">
108
+ <link rel="modulepreload" crossorigin href="/assets/doc-browser-DznuT-CU.js">
109
+ <link rel="modulepreload" crossorigin href="/assets/use-viewport-layout-CWlW5b-T.js">
110
+ <link rel="modulepreload" crossorigin href="/assets/logo-badge-CRVKkIl9.js">
111
+ <link rel="modulepreload" crossorigin href="/assets/skeleton-BwfJfVK3.js">
112
+ <link rel="modulepreload" crossorigin href="/assets/chat-CXrERmQ1.js">
113
+ <link rel="modulepreload" crossorigin href="/assets/key-round-DFVNXZcD.js">
114
+ <link rel="modulepreload" crossorigin href="/assets/message-square-CLhDWybk.js">
115
+ <link rel="modulepreload" crossorigin href="/assets/app-navigation.config-xIjCAn-R.js">
116
+ <link rel="modulepreload" crossorigin href="/assets/notice-card-CXY09tsa.js">
117
+ <link rel="modulepreload" crossorigin href="/assets/status-dot-Bobpfutv.js">
118
+ <link rel="modulepreload" crossorigin href="/assets/app-manager-provider-BfKiVYea.js">
119
+ <link rel="stylesheet" crossorigin href="/assets/index-DuHqJ8wn.css">
120
120
  </head>
121
121
 
122
122
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/ui",
3
- "version": "0.12.15",
3
+ "version": "0.12.16",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,11 +28,11 @@
28
28
  "tailwind-merge": "^2.5.4",
29
29
  "zod": "^3.23.8",
30
30
  "zustand": "^5.0.2",
31
+ "@nextclaw/agent-chat-ui": "0.3.11",
31
32
  "@nextclaw/ncp-http-agent-client": "0.3.17",
32
- "@nextclaw/agent-chat": "0.1.10",
33
- "@nextclaw/agent-chat-ui": "0.3.10",
34
33
  "@nextclaw/ncp-react": "0.4.25",
35
- "@nextclaw/ncp": "0.5.5"
34
+ "@nextclaw/ncp": "0.5.5",
35
+ "@nextclaw/agent-chat": "0.1.10"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@testing-library/react": "^16.3.0",
@@ -6,6 +6,7 @@ import type { SessionSkillEntryView } from '@/shared/lib/api';
6
6
  import { buildChatSlashItems, buildModelStateHint, buildModelToolbarSelect, buildSkillPickerModel, buildThinkingToolbarSelect, type ChatModelRecord, type ChatSkillRecord, type ChatThinkingLevel } from '@/features/chat/utils/chat-input-bar.utils';
7
7
  import { usePresenter } from '@/features/chat/components/providers/chat-presenter.provider';
8
8
  import { useI18n } from '@/app/components/i18n-provider';
9
+ import { useViewportLayout } from '@/app/hooks/use-viewport-layout';
9
10
  import { useChatInputStore } from '@/features/chat/stores/chat-input.store';
10
11
  import { chatRecentModelsManager, CHAT_RECENT_MODELS_MIN_OPTIONS } from '@/features/chat/managers/chat-recent-models.manager';
11
12
  import { chatRecentSkillsManager, CHAT_RECENT_SKILLS_MIN_OPTIONS } from '@/features/chat/managers/chat-recent-skills.manager';
@@ -203,6 +204,7 @@ function buildSkillPicker(params: { allSkillsLabel: string; presenter: ChatPrese
203
204
  export function ChatInputBarContainer() {
204
205
  const presenter = usePresenter();
205
206
  const { language } = useI18n();
207
+ const { isMobile } = useViewportLayout();
206
208
  const snapshot = useChatInputStore((state) => state.snapshot);
207
209
  const runtimeAvailability = useChatRuntimeAvailability();
208
210
  const [slashQuery, setSlashQuery] = useState<string | null>(null);
@@ -215,7 +217,9 @@ export function ChatInputBarContainer() {
215
217
  const isModelOptionsEmpty = isNcpChatModelOptionsEmpty(snapshot);
216
218
  const inputDisabled = isNcpChatComposerDisabled(snapshot);
217
219
  const attachmentSupported = typeof presenter.chatInputManager.addAttachments === 'function';
218
- const textareaPlaceholder = isModelOptionsEmpty ? t('chatModelNoOptions') : t('chatInputPlaceholder');
220
+ const textareaPlaceholder = isModelOptionsEmpty
221
+ ? t('chatModelNoOptions')
222
+ : t(isMobile ? 'chatInputPlaceholderCompact' : 'chatInputPlaceholder');
219
223
  const slashItems = useMemo(
220
224
  () => buildChatSlashItems(skillRecords, slashQuery ?? '', labels.slashTexts, recentSkillValues),
221
225
  [labels.slashTexts, recentSkillValues, skillRecords, slashQuery]
@@ -212,14 +212,14 @@ describe('buildModelToolbarSelect', () => {
212
212
  });
213
213
 
214
214
  expect(select.value).toBe('minimax/MiniMax-M2.7');
215
- expect(select.selectedLabel).toBe('MiniMax-M2.7');
215
+ expect(select.selectedLabel).toBe('MiniMax/MiniMax-M2.7');
216
216
  expect(select.options[0]).toEqual({
217
217
  value: 'minimax/MiniMax-M2.7',
218
218
  label: 'MiniMax/MiniMax-M2.7'
219
219
  });
220
220
  });
221
221
 
222
- it('keeps provider labels in the dropdown while using only the model name in the trigger', () => {
222
+ it('keeps the full provider/model label in shared state while exposing a compact mobile label', () => {
223
223
  const select = buildModelToolbarSelect({
224
224
  modelOptions: [
225
225
  {
@@ -241,7 +241,7 @@ describe('buildModelToolbarSelect', () => {
241
241
  }
242
242
  });
243
243
 
244
- expect(select.selectedLabel).toBe('claude-sonnet-4-very-long-name');
244
+ expect(select.selectedLabel).toBe('Anthropic/claude-sonnet-4-very-long-name');
245
245
  expect(select.options[0]?.label).toBe('Anthropic/claude-sonnet-4-very-long-name');
246
246
  });
247
247
 
@@ -113,7 +113,7 @@ export function buildModelToolbarSelect({
113
113
  value: resolvedValue,
114
114
  placeholder: texts.modelSelectPlaceholder,
115
115
  selectedLabel: resolvedModelOption
116
- ? resolvedModelOption.modelLabel.trim() || formatModelOptionLabel(resolvedModelOption)
116
+ ? formatModelOptionLabel(resolvedModelOption)
117
117
  : undefined,
118
118
  icon: "sparkles",
119
119
  options: modelOptions.map((option) => ({
package/src/index.css CHANGED
@@ -220,6 +220,16 @@
220
220
  --md-code-toolbar-bg: rgba(15, 23, 42, 0.56);
221
221
  --md-code-toolbar-border: rgba(148, 163, 184, 0.3);
222
222
  --md-code-text: #e5e7eb;
223
+ --md-code-muted: #9ca3af;
224
+ --md-code-keyword: #c084fc;
225
+ --md-code-title: #93c5fd;
226
+ --md-code-string: #86efac;
227
+ --md-code-number: #fbbf24;
228
+ --md-code-comment: #94a3b8;
229
+ --md-code-attr: #67e8f9;
230
+ --md-code-meta: #f0abfc;
231
+ --md-code-deletion: #fca5a5;
232
+ --md-code-addition: #86efac;
223
233
  --md-table-head-bg: rgba(148, 163, 184, 0.14);
224
234
 
225
235
  color: var(--md-text);
@@ -241,6 +251,16 @@
241
251
  --md-code-toolbar-bg: rgba(2, 6, 23, 0.88);
242
252
  --md-code-toolbar-border: rgba(191, 219, 254, 0.26);
243
253
  --md-code-text: #e2e8f0;
254
+ --md-code-muted: #a5b4fc;
255
+ --md-code-keyword: #d8b4fe;
256
+ --md-code-title: #bfdbfe;
257
+ --md-code-string: #bbf7d0;
258
+ --md-code-number: #fde68a;
259
+ --md-code-comment: #cbd5e1;
260
+ --md-code-attr: #a5f3fc;
261
+ --md-code-meta: #f5d0fe;
262
+ --md-code-deletion: #fecaca;
263
+ --md-code-addition: #bbf7d0;
244
264
  --md-table-head-bg: rgba(191, 219, 254, 0.16);
245
265
  }
246
266
 
@@ -394,9 +414,12 @@
394
414
  .chat-codeblock {
395
415
  margin: 0.2rem 0 0.1rem;
396
416
  border: 1px solid var(--md-code-toolbar-border);
397
- border-radius: 0.78rem;
417
+ border-radius: 0.5rem;
398
418
  overflow: hidden;
399
- background: var(--md-code-surface);
419
+ background:
420
+ linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0)),
421
+ var(--md-code-surface);
422
+ box-shadow: 0 12px 34px rgba(15, 23, 42, 0.13);
400
423
  }
401
424
 
402
425
  .chat-codeblock-toolbar {
@@ -426,7 +449,7 @@
426
449
  font-weight: 600;
427
450
  color: var(--md-code-text);
428
451
  border: 1px solid var(--md-code-toolbar-border);
429
- border-radius: 0.48rem;
452
+ border-radius: 0.42rem;
430
453
  padding: 0.18rem 0.36rem;
431
454
  background: transparent;
432
455
  cursor: pointer;
@@ -439,17 +462,86 @@
439
462
 
440
463
  .chat-codeblock pre {
441
464
  margin: 0;
442
- padding: 0.75rem 0.82rem 0.8rem;
465
+ padding: 0.8rem 0.9rem 0.86rem;
443
466
  overflow-x: auto;
444
467
  background: transparent;
468
+ scrollbar-color: rgba(148, 163, 184, 0.48) transparent;
469
+ scrollbar-width: thin;
445
470
  }
446
471
 
447
472
  .chat-codeblock code {
448
473
  display: block;
449
474
  min-width: max-content;
450
475
  white-space: pre;
451
- line-height: 1.58;
476
+ line-height: 1.62;
452
477
  color: var(--md-code-text);
478
+ font-size: 0.79rem;
479
+ font-variant-ligatures: none;
480
+ tab-size: 2;
481
+ }
482
+
483
+ .chat-codeblock .hljs-keyword,
484
+ .chat-codeblock .hljs-built_in,
485
+ .chat-codeblock .hljs-type,
486
+ .chat-codeblock .hljs-literal,
487
+ .chat-codeblock .hljs-selector-tag {
488
+ color: var(--md-code-keyword);
489
+ }
490
+
491
+ .chat-codeblock .hljs-title,
492
+ .chat-codeblock .hljs-title.function_,
493
+ .chat-codeblock .hljs-title.class_,
494
+ .chat-codeblock .hljs-section,
495
+ .chat-codeblock .hljs-selector-id {
496
+ color: var(--md-code-title);
497
+ }
498
+
499
+ .chat-codeblock .hljs-string,
500
+ .chat-codeblock .hljs-regexp,
501
+ .chat-codeblock .hljs-symbol,
502
+ .chat-codeblock .hljs-bullet {
503
+ color: var(--md-code-string);
504
+ }
505
+
506
+ .chat-codeblock .hljs-number,
507
+ .chat-codeblock .hljs-variable,
508
+ .chat-codeblock .hljs-template-variable,
509
+ .chat-codeblock .hljs-params {
510
+ color: var(--md-code-number);
511
+ }
512
+
513
+ .chat-codeblock .hljs-comment,
514
+ .chat-codeblock .hljs-quote {
515
+ color: var(--md-code-comment);
516
+ font-style: italic;
517
+ }
518
+
519
+ .chat-codeblock .hljs-attr,
520
+ .chat-codeblock .hljs-attribute,
521
+ .chat-codeblock .hljs-property,
522
+ .chat-codeblock .hljs-name,
523
+ .chat-codeblock .hljs-selector-class {
524
+ color: var(--md-code-attr);
525
+ }
526
+
527
+ .chat-codeblock .hljs-meta,
528
+ .chat-codeblock .hljs-doctag,
529
+ .chat-codeblock .hljs-tag {
530
+ color: var(--md-code-meta);
531
+ }
532
+
533
+ .chat-codeblock .hljs-subst,
534
+ .chat-codeblock .hljs-punctuation,
535
+ .chat-codeblock .hljs-operator {
536
+ color: var(--md-code-muted);
537
+ }
538
+
539
+ .chat-codeblock .hljs-deletion {
540
+ color: var(--md-code-deletion);
541
+ }
542
+
543
+ .chat-codeblock .hljs-addition {
544
+ color: var(--md-code-addition);
453
545
  }
454
546
 
455
547
  .chat-table-wrap {
@@ -0,0 +1,33 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { AgentAvatar } from './agent-avatar';
4
+
5
+ describe('AgentAvatar', () => {
6
+ it('uses the configured image for the main agent before fallback', () => {
7
+ render(
8
+ <AgentAvatar
9
+ agentId="main"
10
+ displayName="Main"
11
+ avatarUrl="https://example.com/main-avatar.png"
12
+ />,
13
+ );
14
+
15
+ const avatar = screen.getByRole('img', { name: 'Main' });
16
+ expect(avatar.getAttribute('src')).toBe('https://example.com/main-avatar.png');
17
+ expect(avatar.querySelector('svg')).toBeNull();
18
+ });
19
+
20
+ it('uses the bot icon for the main agent fallback', () => {
21
+ render(<AgentAvatar agentId="main" displayName="Main" />);
22
+
23
+ const avatar = screen.getByLabelText('Main');
24
+ expect(avatar.querySelector('svg')).toBeTruthy();
25
+ expect(avatar.textContent).not.toContain('M');
26
+ });
27
+
28
+ it('keeps letter fallback avatars for specialist agents', () => {
29
+ render(<AgentAvatar agentId="engineer" displayName="Engineer" />);
30
+
31
+ expect(screen.getByLabelText('Engineer').textContent).toBe('E');
32
+ });
33
+ });
@@ -1,4 +1,5 @@
1
1
  import { cn } from '@/shared/lib/utils';
2
+ import { Bot } from 'lucide-react';
2
3
 
3
4
  type AgentAvatarProps = {
4
5
  agentId: string;
@@ -25,14 +26,6 @@ function hashText(value: string): number {
25
26
  return Math.abs(hash);
26
27
  }
27
28
 
28
- function resolveLetter(value: string): string {
29
- const trimmed = value.trim();
30
- if (!trimmed) {
31
- return 'A';
32
- }
33
- return trimmed.slice(0, 1).toUpperCase();
34
- }
35
-
36
29
  export function AgentAvatar({ agentId, displayName, avatarUrl, className }: AgentAvatarProps) {
37
30
  const seed = displayName?.trim() || agentId;
38
31
  const [bgClass, textClass] = PALETTE[hashText(agentId) % PALETTE.length] ?? PALETTE[0];
@@ -57,7 +50,11 @@ export function AgentAvatar({ agentId, displayName, avatarUrl, className }: Agen
57
50
  )}
58
51
  aria-label={displayName?.trim() || agentId}
59
52
  >
60
- {resolveLetter(seed)}
53
+ {agentId.trim().toLowerCase() === 'main' ? (
54
+ <Bot className="h-[55%] w-[55%]" strokeWidth={2.4} />
55
+ ) : (
56
+ (seed.trim() || 'A').slice(0, 1).toUpperCase()
57
+ )}
61
58
  </div>
62
59
  );
63
60
  }
@@ -67,7 +67,8 @@ export const CHAT_LABELS: Record<string, { zh: string; en: string }> = {
67
67
  zh: '聊天能力启动失败,请稍后重试或检查服务日志。',
68
68
  en: 'Chat startup failed. Please retry in a moment or inspect the service logs.'
69
69
  },
70
- chatInputPlaceholder: { zh: '发消息...', en: 'Message NextClaw...' },
70
+ chatInputPlaceholder: { zh: '输入消息,输入 / 选择技能,Enter 发送,Shift + Enter 换行', en: 'Type a message, type / to select skills, Enter to send, Shift + Enter for newline' },
71
+ chatInputPlaceholderCompact: { zh: '发消息...', en: 'Message NextClaw...' },
71
72
  chatInputHint: { zh: '支持多轮上下文,默认走当前会话。', en: 'Multi-turn context is preserved in the current session.' },
72
73
  chatSlashSectionCommands: { zh: '命令', en: 'Commands' },
73
74
  chatSlashSectionSkills: { zh: '技能', en: 'Skills' },