@nextclaw/ui 0.26.2 → 0.26.4

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 (218) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/.vite/manifest.json +366 -365
  3. package/dist/assets/{account-panel-ZM2qzKF4.js → account-panel-DVi_jhkG.js} +1 -1
  4. package/dist/assets/{api-Cjq-o1VN.js → api-BZvNpFOS.js} +2 -2
  5. package/dist/assets/app-presenter-provider-BcYL-VNe.js +124 -0
  6. package/dist/assets/{appearance-settings-page-B4zouKzg.js → appearance-settings-page-AZNBzLmT.js} +1 -1
  7. package/dist/assets/{book-open-AteA89HX.js → book-open-CmRdT3N1.js} +1 -1
  8. package/dist/assets/card-CdRBL1E0.js +1 -0
  9. package/dist/assets/{channels-list-page-fj8kMo-T.js → channels-list-page-eZN74FN3.js} +2 -2
  10. package/dist/assets/{circle-dashed-LSYZTzM7.js → circle-dashed-HOgt3gIJ.js} +1 -1
  11. package/dist/assets/{client-DudCRJw4.js → client-DKKbdJ6Q.js} +1 -1
  12. package/dist/assets/config-split-page-BA3V6b3Q.js +1 -0
  13. package/dist/assets/{confirm-dialog-DShT64F6.js → confirm-dialog-8vqYZ0wy.js} +1 -1
  14. package/dist/assets/context-menu-DHVVdHzN.js +1 -0
  15. package/dist/assets/{createLucideIcon-D7hvIHV-.js → createLucideIcon-DE0sB9Nu.js} +1 -1
  16. package/dist/assets/{desktop-capabilities-page-CzqzFZUY.js → desktop-capabilities-page-B9-B9k1A.js} +1 -1
  17. package/dist/assets/{desktop-update-config-CgeQ0Ffo.js → desktop-update-config-Cfr3IRUe.js} +1 -1
  18. package/dist/assets/{dialog-C7glmAQJ.js → dialog-BUfWO8hr.js} +2 -2
  19. package/dist/assets/{doc-browser-context-BlhBAOLH.js → doc-browser-context-ClLgMbxd.js} +1 -1
  20. package/dist/assets/doc-browser-w8q1j0jL.js +1 -0
  21. package/dist/assets/{download-BYgECE0n.js → download-CKqS1Ksj.js} +1 -1
  22. package/dist/assets/{es2015-CsjAAQ6X.js → es2015-BiB05Sav.js} +1 -1
  23. package/dist/assets/{extensions-qYL-NpL3.js → extensions-vPD4dGqt.js} +1 -1
  24. package/dist/assets/{eye-BBuTzJ53.js → eye-bKzylu3p.js} +1 -1
  25. package/dist/assets/{form-actions-CLGWO52v.js → form-actions-CCoxz33Q.js} +1 -1
  26. package/dist/assets/{i18n-qBGEbcW6.js → i18n-OGmF0Ws_.js} +2 -2
  27. package/dist/assets/{keyboard-shortcuts-settings-page-B-wK_6zG.js → keyboard-shortcuts-settings-page-D8rosOVh.js} +1 -1
  28. package/dist/assets/loader-circle-Clq1Gtb1.js +1 -0
  29. package/dist/assets/{login-page-Cd84lpVV.js → login-page-BT3GV3Sb.js} +1 -1
  30. package/dist/assets/login-page-DKId3By8.js +1 -0
  31. package/dist/assets/main-bEWCGDHr.js +2 -0
  32. package/dist/assets/main-i18n-catalog.config-Bpu5GcwF.js +1 -0
  33. package/dist/assets/main-kqMRTwOs.css +1 -0
  34. package/dist/assets/mcp-marketplace-page-CZIP6j3L.js +1 -0
  35. package/dist/assets/{model-config-page-D0DQlSDa.js → model-config-page-D21a-BO0.js} +1 -1
  36. package/dist/assets/{navigation-link-32iM1-e6.js → navigation-link-DC-Pku0Y.js} +1 -1
  37. package/dist/assets/panel-app-BYfQu_TS.js +1 -0
  38. package/dist/assets/panel-app-i18n-catalog.config-D44qmtfk.js +1 -0
  39. package/dist/assets/panelStandalone-BRg7B1EI.css +1 -0
  40. package/dist/assets/{panelStandalone-C9EKaApN.js → panelStandalone-CX8X2hjh.js} +2 -2
  41. package/dist/assets/{privacy-settings-page-Bxxx12OO.js → privacy-settings-page-e0LFHZJm.js} +1 -1
  42. package/dist/assets/projects-Rki7Fmth.js +1 -0
  43. package/dist/assets/provider-models-DFQgF4hS.js +1 -0
  44. package/dist/assets/provider-scoped-model-input-DaONL0j_.js +1 -0
  45. package/dist/assets/providers-config-page-CEOhQlMe.js +1 -0
  46. package/dist/assets/{puzzle-CWHfFyYN.js → puzzle-D_TiFXOv.js} +1 -1
  47. package/dist/assets/{pwa-CiGUjpcd.js → pwa-C9ac_1dg.js} +1 -1
  48. package/dist/assets/{refresh-cw-y3L6PyLt.js → refresh-cw-BG8VfcdA.js} +1 -1
  49. package/dist/assets/remote-DkSx5x4R.js +1 -0
  50. package/dist/assets/{rotate-cw-CNE9qSHi.js → rotate-cw-DPEegUU2.js} +1 -1
  51. package/dist/assets/{runtime-config-page-5o98T-TD.js → runtime-config-page-DDT1dVLt.js} +1 -1
  52. package/dist/assets/{save-B0p5a0ET.js → save-CPlctBjT.js} +1 -1
  53. package/dist/assets/{search-config-page-CpGcnIzi.js → search-config-page-CDwmd6eX.js} +1 -1
  54. package/dist/assets/{secrets-config-page-C4VTiW3r.js → secrets-config-page-1OvgmwPv.js} +1 -1
  55. package/dist/assets/security-config-Pv4CM-kw.js +1 -0
  56. package/dist/assets/{select-CctTN205.js → select-Y9IEbx9x.js} +1 -1
  57. package/dist/assets/service-apps-panel-DPrLeF3N.js +1 -0
  58. package/dist/assets/{settings-2-EPflrNk6.js → settings-2-CrGx9_De.js} +1 -1
  59. package/dist/assets/{status-dot-CensgJZC.js → status-dot-Cb8ewlUr.js} +1 -1
  60. package/dist/assets/{tag-chip-Bjzb4-qW.js → tag-chip-D-6aPuuh.js} +1 -1
  61. package/dist/assets/{theme-provider-hlle7Skk.js → theme-provider-BjF1Eowc.js} +1 -1
  62. package/dist/assets/{trash-2-C5KH5Rw9.js → trash-2-DER2EJKK.js} +1 -1
  63. package/dist/assets/{use-config-CPl_F79t.js → use-config-BM5jC65L.js} +1 -1
  64. package/dist/assets/{use-desktop-capabilities-CEg9-DrQ.js → use-desktop-capabilities-ezKJwKqg.js} +1 -1
  65. package/dist/assets/{use-viewport-layout-CPZUmTPi.js → use-viewport-layout-vmbQUNT6.js} +1 -1
  66. package/dist/assets/x-DSz9cGhy.js +1 -0
  67. package/dist/index.html +34 -34
  68. package/dist/panel-standalone.html +11 -11
  69. package/package.json +3 -3
  70. package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +2 -2
  71. package/src/app/components/layout/page-layout.tsx +5 -2
  72. package/src/app/components/layout/sidebar-rail.styles.ts +3 -3
  73. package/src/app/configs/__tests__/app-navigation.config.test.ts +10 -0
  74. package/src/app/configs/app-navigation.config.ts +6 -2
  75. package/src/app/styles/charcoal-theme.css +0 -3
  76. package/src/app/styles/design-system.css +18 -6
  77. package/src/app/styles/island-theme.css +2 -15
  78. package/src/features/agents/components/agent-details-dialog.tsx +1 -1
  79. package/src/features/agents/components/agents-page.tsx +5 -3
  80. package/src/features/app-data/components/app-data-removal-choice.tsx +1 -1
  81. package/src/features/apps/components/app-package-card.tsx +7 -6
  82. package/src/features/channels/pages/channels-list-page.tsx +1 -1
  83. package/src/features/chat/components/chat-sidebar-list-mode-switch.tsx +1 -1
  84. package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +28 -16
  85. package/src/features/chat/components/conversation/chat-conversation-content.tsx +8 -2
  86. package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +3 -3
  87. package/src/features/chat/components/conversation/chat-conversation-header.tsx +4 -4
  88. package/src/features/chat/components/layout/__tests__/chat-sidebar-session-area.test.tsx +6 -4
  89. package/src/features/chat/components/layout/__tests__/chat-sidebar-toolbar.test.tsx +22 -1
  90. package/src/features/chat/components/layout/chat-page-shell.tsx +1 -1
  91. package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +10 -14
  92. package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +50 -12
  93. package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +4 -4
  94. package/src/features/chat/components/layout/chat-sidebar.tsx +2 -2
  95. package/src/features/chat/features/conversation/components/session-queued-input-rows.tsx +1 -1
  96. package/src/features/chat/features/message/components/chat-message-list.container.tsx +12 -1
  97. package/src/features/chat/features/message/components/chat-tool-call-stress-harness.tsx +32 -2
  98. package/src/features/chat/features/message/hooks/__tests__/use-chat-message-virtualizer.test.tsx +7 -5
  99. package/src/features/chat/features/message/hooks/use-chat-message-virtualizer.ts +7 -3
  100. package/src/features/chat/features/session/components/__tests__/chat-sidebar-project-groups.test.tsx +1 -1
  101. package/src/features/chat/features/session/components/chat-mobile-session-row.tsx +2 -2
  102. package/src/features/chat/features/session/components/chat-sidebar-context-card.tsx +1 -1
  103. package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +1 -1
  104. package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +7 -5
  105. package/src/features/chat/features/session/components/chat-sidebar-session-list.tsx +45 -10
  106. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +1 -1
  107. package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +3 -5
  108. package/src/features/chat/features/session/components/session-header/chat-session-header-menu-item.tsx +4 -3
  109. package/src/features/chat/features/session/components/session-header/chat-session-more-actions-menu.tsx +3 -0
  110. package/src/features/chat/features/session/components/session-header/chat-session-switcher-groups.tsx +6 -6
  111. package/src/features/chat/features/session/components/session-header/chat-session-title-switcher.tsx +6 -5
  112. package/src/features/chat/features/session-type/components/chat-session-type-option-item.tsx +1 -1
  113. package/src/features/chat/features/welcome/components/chat-welcome-agent-picker.tsx +0 -1
  114. package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +1 -1
  115. package/src/features/chat/features/welcome/components/chat-welcome-prompt-suggestions.tsx +1 -1
  116. package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +1 -1
  117. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumb-browser.tsx +2 -2
  118. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +2 -2
  119. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +1 -1
  120. package/src/features/chat/features/workspace/components/child-sessions/chat-session-child-sessions.tsx +2 -2
  121. package/src/features/chat/features/workspace/components/file-content-preview/workspace-spreadsheet-preview.tsx +2 -2
  122. package/src/features/chat/features/workspace/components/overview/chat-session-continuous-attention.tsx +3 -3
  123. package/src/features/chat/features/workspace/components/overview/chat-session-token-usage.tsx +1 -1
  124. package/src/features/chat/features/workspace/components/project-files/workspace-directory-tree.tsx +4 -3
  125. package/src/features/chat/features/workspace/components/project-files/workspace-new-folder-tree-item.tsx +2 -1
  126. package/src/features/chat/features/workspace/components/project-files/workspace-project-files-toolbar.tsx +3 -1
  127. package/src/features/chat/features/workspace/components/session-cron-job-content.tsx +3 -3
  128. package/src/features/chat/features/workspace/components/workspace-file-content-preview.tsx +2 -2
  129. package/src/features/chat/features/workspace/components/workspace-markdown-outline.tsx +1 -1
  130. package/src/features/cron/components/cron-config.tsx +3 -2
  131. package/src/features/cron/components/cron-job-detail-dialog.tsx +1 -1
  132. package/src/features/cron/components/cron-job-row.tsx +7 -7
  133. package/src/features/cron/components/cron-task-discovery.tsx +1 -1
  134. package/src/features/inbox/components/inbox-delivery-content.tsx +1 -1
  135. package/src/features/inbox/pages/inbox-page.tsx +25 -29
  136. package/src/features/marketplace/components/curated-shelves/marketplace-curated-shelves.tsx +2 -2
  137. package/src/features/marketplace/components/curated-shelves/marketplace-shelf-card.tsx +2 -2
  138. package/src/features/marketplace/components/marketplace-list-card.tsx +4 -4
  139. package/src/features/marketplace/components/marketplace-page-parts.tsx +4 -3
  140. package/src/features/marketplace/components/marketplace-page.tsx +4 -4
  141. package/src/features/marketplace/components/mcp/mcp-marketplace-card.tsx +2 -2
  142. package/src/features/marketplace/components/mcp/mcp-marketplace-page.tsx +3 -3
  143. package/src/features/panel-apps/components/panel-app-list-item.tsx +3 -3
  144. package/src/features/panel-apps/components/panel-app-open-standalone-menu-item.tsx +3 -1
  145. package/src/features/projects/components/project-overview.tsx +1 -1
  146. package/src/features/projects/pages/project-home-page.tsx +11 -2
  147. package/src/features/projects/pages/projects-page.test.tsx +4 -2
  148. package/src/features/pwa/components/pwa-install-entry.tsx +1 -1
  149. package/src/features/right-panel-resources/components/__tests__/page-resource-sidebar-nav.test.tsx +9 -1
  150. package/src/features/right-panel-resources/components/page-resource-sidebar-nav.tsx +1 -1
  151. package/src/features/right-panel-resources/components/right-panel-resource-home-page.tsx +1 -1
  152. package/src/features/service-apps/components/service-action-row.tsx +3 -3
  153. package/src/features/service-apps/components/service-app-list-item.tsx +4 -4
  154. package/src/features/service-apps/components/service-apps-panel.tsx +1 -1
  155. package/src/features/settings/components/config/provider-model-list.tsx +5 -5
  156. package/src/features/settings/components/config/provider-model-suggestions-panel.tsx +4 -4
  157. package/src/features/settings/components/config/provider-pill-selector.tsx +1 -1
  158. package/src/features/settings/pages/appearance-settings-page.tsx +2 -2
  159. package/src/features/settings/pages/providers-config-page.tsx +6 -6
  160. package/src/features/side-dock/components/side-dock.tsx +4 -4
  161. package/src/index.css +4 -0
  162. package/src/platforms/desktop/components/desktop-window-chrome.tsx +1 -1
  163. package/src/platforms/mobile/components/mobile-app-shell.tsx +1 -1
  164. package/src/platforms/mobile/components/mobile-bottom-nav.tsx +10 -9
  165. package/src/platforms/mobile/components/mobile-settings-shell.tsx +24 -23
  166. package/src/platforms/mobile/components/mobile-topbar.tsx +10 -11
  167. package/src/shared/components/common/brand-header.tsx +1 -1
  168. package/src/shared/components/common/searchable-model-input.tsx +2 -2
  169. package/src/shared/components/config-split-page.tsx +3 -3
  170. package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +34 -2
  171. package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +38 -7
  172. package/src/shared/components/doc-browser/doc-browser.tsx +5 -1
  173. package/src/shared/components/path-picker/server-path-picker-locations.tsx +1 -1
  174. package/src/shared/components/settings/settings-page.tsx +1 -1
  175. package/src/shared/components/ui/actions/__tests__/icon-action-button.test.tsx +5 -5
  176. package/src/shared/components/ui/actions/action-feedback.ts +8 -0
  177. package/src/shared/components/ui/actions/icon-action-button.tsx +13 -12
  178. package/src/shared/components/ui/button.tsx +3 -3
  179. package/src/shared/components/ui/card.tsx +1 -1
  180. package/src/shared/components/ui/context-menu/context-menu-items.tsx +6 -5
  181. package/src/shared/components/ui/dialog.tsx +4 -3
  182. package/src/shared/components/ui/input.tsx +7 -2
  183. package/src/shared/components/ui/overlays/sheet.tsx +1 -1
  184. package/src/shared/components/ui/popover.tsx +1 -1
  185. package/src/shared/components/ui/select.tsx +3 -2
  186. package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +8 -6
  187. package/src/shared/components/ui/tabs.tsx +3 -3
  188. package/src/shared/components/ui/textarea.tsx +1 -0
  189. package/src/shared/components/workbench/workbench-surface-toolbar.tsx +3 -4
  190. package/src/shared/components/workbench/workbench-surface.tsx +2 -2
  191. package/src/shared/lib/i18n/locales/en-US/agents.json +2 -0
  192. package/src/shared/lib/i18n/locales/en-US/core.json +1 -0
  193. package/src/shared/lib/i18n/locales/en-US/desktop-update.json +1 -1
  194. package/src/shared/lib/i18n/locales/zh-CN/agents.json +2 -0
  195. package/src/shared/lib/i18n/locales/zh-CN/core.json +1 -0
  196. package/src/shared/lib/i18n/locales/zh-CN/desktop-update.json +1 -1
  197. package/dist/assets/app-presenter-provider-C02YPoMr.js +0 -124
  198. package/dist/assets/card-BLXvBstA.js +0 -1
  199. package/dist/assets/config-split-page-G7Kt2BnV.js +0 -1
  200. package/dist/assets/context-menu-rd7b_kVD.js +0 -1
  201. package/dist/assets/doc-browser-dBiR9Qi0.js +0 -1
  202. package/dist/assets/loader-circle-BA7EMkih.js +0 -1
  203. package/dist/assets/login-page-VdMyNkFf.js +0 -1
  204. package/dist/assets/main-CR0SrTBf.js +0 -2
  205. package/dist/assets/main-DXH6EdXI.css +0 -1
  206. package/dist/assets/main-i18n-catalog.config-Btp_OowF.js +0 -1
  207. package/dist/assets/mcp-marketplace-page-jacRwiI-.js +0 -1
  208. package/dist/assets/panel-app-CC0bVSu4.js +0 -1
  209. package/dist/assets/panel-app-i18n-catalog.config-LUSBp5Ce.js +0 -1
  210. package/dist/assets/panelStandalone-Y91OJkgV.css +0 -1
  211. package/dist/assets/projects-D56yAPNT.js +0 -1
  212. package/dist/assets/provider-models-C3frdXeW.js +0 -1
  213. package/dist/assets/provider-scoped-model-input-CYetrPYZ.js +0 -1
  214. package/dist/assets/providers-config-page-DESB1h8g.js +0 -1
  215. package/dist/assets/remote-DiGFV8nP.js +0 -1
  216. package/dist/assets/security-config-C5-RQVpS.js +0 -1
  217. package/dist/assets/service-apps-panel-CKE5HKNY.js +0 -1
  218. package/dist/assets/x--xxUvHKW.js +0 -1
@@ -133,7 +133,7 @@ export function CronTemplateGallery({
133
133
  onClick={() => onSelect(t(template.promptKey))}
134
134
  className="group grid min-h-24 grid-cols-[38px_minmax(0,1fr)] items-center gap-3 rounded-2xl border border-border/60 bg-card px-4 py-4 text-left transition-all hover:-translate-y-0.5 hover:border-primary/20 hover:shadow-[0_12px_30px_rgba(30,45,52,0.08)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
135
135
  >
136
- <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-muted text-muted-foreground transition-colors group-hover:bg-primary/10 group-hover:text-primary">
136
+ <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-muted text-muted-foreground transition-colors group-hover:bg-[var(--interaction-hover)] group-hover:text-primary">
137
137
  <Icon className="h-4 w-4" aria-hidden="true" />
138
138
  </span>
139
139
  <span className="min-w-0">
@@ -74,7 +74,7 @@ function InboxHtmlContent({
74
74
  return (
75
75
  <iframe
76
76
  className={cn(
77
- "w-full rounded-xl bg-background",
77
+ "w-full md:rounded-xl bg-background",
78
78
  fillHeight
79
79
  ? "h-full min-h-0 border-0"
80
80
  : "h-[min(60vh,680px)] min-h-[360px] border border-border/70",
@@ -2,7 +2,9 @@ import { PageResourceActionsMenu } from '@/features/right-panel-resources';
2
2
  import { pageResourceFromSystemObject } from '@/features/right-panel-resources';
3
3
  import { useEffect, useMemo, useState } from "react";
4
4
  import type { InboxDelivery } from "@nextclaw/shared";
5
- import { Archive, ArrowLeft, Inbox, MessageCircle, RotateCcw, Trash2 } from "lucide-react";
5
+ import { Archive, Inbox, MessageCircle, MoreVertical, RotateCcw, Trash2 } from "lucide-react";
6
+ import { ContextMenu, ContextMenuTrigger } from '@/shared/components/ui/context-menu/context-menu';
7
+ import { IconActionButton } from '@/shared/components/ui/actions/icon-action-button';
6
8
  import { Link, useNavigate, useParams } from "react-router-dom";
7
9
  import { useAppPresenter } from "@/app/components/app-presenter-provider";
8
10
  import { PageHeader } from "@/app/components/layout/page-layout";
@@ -93,8 +95,9 @@ function InboxListPane({
93
95
  key={item.id}
94
96
  type="button"
95
97
  onClick={() => onFilterChange(item.id)}
98
+ aria-pressed={filter === item.id}
96
99
  className={cn(
97
- "inline-flex items-center gap-1.5 rounded-md px-2.5 py-1.5 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border",
100
+ "inline-flex min-h-11 items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border md:min-h-0 md:px-2.5 md:text-xs",
98
101
  filter === item.id
99
102
  ? "bg-[var(--interaction-selection)] text-foreground"
100
103
  : "text-muted-foreground hover:bg-[var(--interaction-hover)] hover:text-foreground",
@@ -117,12 +120,12 @@ function InboxListPane({
117
120
  {filteredDeliveries.map((delivery) => (
118
121
  <li key={delivery.id} className={cn(
119
122
  "group/page-row relative rounded-lg hover:bg-[var(--interaction-hover)] focus-within:bg-[var(--interaction-hover)] has-[[data-context-menu-open]]:bg-[var(--interaction-hover)]",
120
- activeDeliveryId === delivery.id && "bg-[var(--interaction-selection)]",
123
+ activeDeliveryId === delivery.id && "bg-[var(--interaction-selection)] hover:bg-[var(--interaction-selection)] focus-within:bg-[var(--interaction-selection)] has-[[data-context-menu-open]]:bg-[var(--interaction-selection)]",
121
124
  )}>
122
125
  <Link
123
126
  to={`/inbox/${encodeURIComponent(delivery.id)}`}
124
127
  className={cn(
125
- "block rounded-lg px-2.5 py-2 pr-10 text-[13px] transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border",
128
+ "block rounded-lg px-2.5 py-3 pr-12 text-sm md:py-2 md:pr-10 md:text-[13px] transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border",
126
129
  activeDeliveryId === delivery.id
127
130
  ? "text-foreground"
128
131
  : "text-foreground/80 hover:text-foreground",
@@ -165,20 +168,16 @@ function InboxListPane({
165
168
  function InboxDetailPane({
166
169
  delivery,
167
170
  error,
168
- isMobile,
169
171
  pending,
170
172
  onArchiveToggle,
171
- onBack,
172
173
  onContinue,
173
174
  onDelete,
174
175
  onReadToggle,
175
176
  }: {
176
177
  delivery: InboxDelivery | null;
177
178
  error: string | null;
178
- isMobile: boolean;
179
179
  pending: boolean;
180
180
  onArchiveToggle: () => void;
181
- onBack: () => void;
182
181
  onContinue: () => void;
183
182
  onDelete: () => void;
184
183
  onReadToggle: () => void;
@@ -193,17 +192,7 @@ function InboxDetailPane({
193
192
  const isHtml = delivery.contentType === "html";
194
193
  return (
195
194
  <main className="flex min-h-0 flex-col">
196
- <div className="shrink-0 border-b border-border/50 px-5 py-3 sm:px-6">
197
- {isMobile ? (
198
- <button
199
- type="button"
200
- onClick={onBack}
201
- className="mb-3 inline-flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground"
202
- >
203
- <ArrowLeft className="h-4 w-4" />
204
- {t("inboxTitle")}
205
- </button>
206
- ) : null}
195
+ <div className="shrink-0 px-4 py-2 md:border-b md:border-border/50 md:px-6 md:py-3">
207
196
  <div className="flex min-h-7 min-w-0 items-center gap-2">
208
197
  <h2
209
198
  title={delivery.title}
@@ -223,7 +212,7 @@ function InboxDetailPane({
223
212
  <div className={cn(
224
213
  "min-h-0 flex-1",
225
214
  isHtml
226
- ? "p-3 sm:p-4"
215
+ ? "p-0 md:p-4"
227
216
  : "custom-scrollbar overflow-y-auto px-5 py-5 sm:px-6 sm:py-6",
228
217
  )}
229
218
  ref={scrollRef}
@@ -239,10 +228,10 @@ function InboxDetailPane({
239
228
  />
240
229
  </div>
241
230
  </div>
242
- <div className="shrink-0 border-t border-border/50 px-5 py-3 sm:px-6">
231
+ <div className="shrink-0 border-t border-border/50 px-4 py-2 md:px-6 md:py-3">
243
232
  {error ? <p role="alert" className="mb-2 text-sm text-destructive">{error}</p> : null}
244
- <div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
245
- <div className="flex flex-wrap items-center gap-1">
233
+ <div className="flex items-center justify-between gap-2">
234
+ <div className="hidden md:flex flex-wrap items-center gap-1">
246
235
  <Button size="sm" variant="outline" disabled={pending} onClick={onReadToggle}>
247
236
  {delivery.readAt ? t("inboxMarkUnread") : t("inboxMarkRead")}
248
237
  </Button>
@@ -257,6 +246,15 @@ function InboxDetailPane({
257
246
  {t("inboxDelete")}
258
247
  </Button>
259
248
  </div>
249
+ <div className="md:hidden">
250
+ <ContextMenu label={t('inboxTitle')} groups={[{ key: 'delivery', items: [
251
+ { key: 'read', label: delivery.readAt ? t('inboxMarkUnread') : t('inboxMarkRead'), disabled: pending, onSelect: onReadToggle },
252
+ { key: 'archive', label: delivery.archivedAt ? t('inboxRestore') : t('inboxArchive'), icon: <Archive className="h-4 w-4" />, disabled: pending, onSelect: onArchiveToggle },
253
+ { key: 'delete', label: t('inboxDelete'), icon: <Trash2 className="h-4 w-4" />, destructive: true, disabled: pending, onSelect: onDelete },
254
+ ] }]}>
255
+ <div><ContextMenuTrigger><IconActionButton icon={<MoreVertical className="h-4 w-4" />} label={t('more')} /></ContextMenuTrigger></div>
256
+ </ContextMenu>
257
+ </div>
260
258
  <Button size="sm" disabled={pending} onClick={onContinue}>
261
259
  <MessageCircle className="mr-2 h-4 w-4" />
262
260
  {t("inboxContinueChat")}
@@ -365,14 +363,14 @@ export function InboxPage() {
365
363
  const showDetail = !isMobile || Boolean(activeDelivery);
366
364
 
367
365
  return (
368
- <div className="flex h-full min-h-0 flex-col gap-6">
369
- <PageHeader
366
+ <div className="flex h-full min-h-0 flex-col md:gap-6">
367
+ {!isMobile ? <PageHeader
370
368
  headingLevel={1}
371
369
  title={t("inboxTitle")}
372
370
  className="px-4 sm:px-0"
373
- />
371
+ /> : null}
374
372
 
375
- <div className="min-h-0 flex-1 overflow-hidden border-y border-border/60 bg-background sm:rounded-2xl sm:border">
373
+ <div className="min-h-0 flex-1 overflow-hidden bg-background md:rounded-2xl md:border md:border-border/60">
376
374
  {deliveriesQuery.isError ? (
377
375
  <div role="alert" className="p-6 text-sm text-destructive">{t("inboxLoadError")}</div>
378
376
  ) : (
@@ -386,10 +384,8 @@ export function InboxPage() {
386
384
  {showDetail ? <InboxDetailPane
387
385
  delivery={activeDelivery}
388
386
  error={error}
389
- isMobile={isMobile}
390
387
  pending={pendingAction !== null}
391
388
  onArchiveToggle={toggleArchive}
392
- onBack={() => navigate("/inbox")}
393
389
  onContinue={continueInChat}
394
390
  onDelete={() => void deleteDelivery()}
395
391
  onReadToggle={toggleRead}
@@ -131,7 +131,7 @@ function GoalCard(props: {
131
131
  type="button"
132
132
  onClick={() => onSelect(scene.scene)}
133
133
  className={cn(
134
- "group flex min-h-[74px] flex-col justify-center rounded-lg border px-3 py-2.5 text-left shadow-sm transition-colors hover:border-gray-300 hover:bg-gray-50/70",
134
+ "group flex min-h-[74px] flex-col justify-center rounded-lg border px-3 py-2.5 text-left shadow-sm transition-colors hover:border-gray-300 hover:bg-[var(--interaction-hover)]",
135
135
  tone.card,
136
136
  )}
137
137
  >
@@ -194,7 +194,7 @@ export function MarketplaceCuratedSceneView(props: {
194
194
  <button
195
195
  type="button"
196
196
  onClick={onBack}
197
- className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-gray-600 transition-colors hover:bg-gray-100 hover:text-gray-900"
197
+ className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-gray-600 transition-colors hover:bg-[var(--interaction-hover)] hover:text-gray-900"
198
198
  aria-label={readLocalized({ zh: "返回", en: "Back" }, language)}
199
199
  >
200
200
  <ArrowLeft className="h-4 w-4" />
@@ -42,7 +42,7 @@ export function SkillShelfCard(props: {
42
42
  <article
43
43
  onClick={() => onOpen(entry)}
44
44
  className={cn(
45
- "group flex min-h-[166px] cursor-pointer flex-col justify-between rounded-xl border border-gray-200/70 bg-white p-3 shadow-sm transition-colors hover:border-gray-300 hover:bg-gray-50/60",
45
+ "group flex min-h-[166px] cursor-pointer flex-col justify-between rounded-xl border border-gray-200/70 bg-white p-3 shadow-sm transition-colors hover:border-gray-300 hover:bg-[var(--interaction-hover)]",
46
46
  layout === "rail" ? "w-[260px] shrink-0" : "w-full min-w-0",
47
47
  )}
48
48
  >
@@ -81,7 +81,7 @@ export function SkillShelfCard(props: {
81
81
  event.stopPropagation();
82
82
  onInstall(item);
83
83
  }}
84
- className="inline-flex h-7 items-center gap-1.5 rounded-md border border-gray-200 bg-white px-2 text-[11px] font-semibold text-gray-700 transition-colors hover:border-gray-300 hover:bg-gray-50 disabled:opacity-50"
84
+ className="inline-flex h-7 items-center gap-1.5 rounded-md border border-gray-200 bg-white px-2 text-[11px] font-semibold text-gray-700 transition-colors hover:border-gray-300 hover:bg-[var(--interaction-hover)] disabled:opacity-50"
85
85
  >
86
86
  <Download className="h-3.5 w-3.5" />
87
87
  {isInstalling ? t("marketplaceInstalling") : t("marketplaceInstall")}
@@ -125,7 +125,7 @@ function MarketplaceListCardActionButtons(props: MarketplaceListCardActionProps)
125
125
  onInstall(item);
126
126
  }}
127
127
  disabled={isInstalling}
128
- className="inline-flex h-6 items-center gap-1 whitespace-nowrap rounded-md bg-primary px-2 text-[11px] font-medium text-white transition-colors hover:bg-primary-600 disabled:opacity-50"
128
+ className="inline-flex h-6 items-center gap-1 whitespace-nowrap rounded-md bg-primary px-2 text-[11px] font-medium text-white transition-colors hover:bg-[hsl(var(--primary-hover))] disabled:opacity-50"
129
129
  >
130
130
  <Download className="h-3 w-3" />
131
131
  {isInstalling ? t("marketplaceInstalling") : t("marketplaceInstall")}
@@ -139,7 +139,7 @@ function MarketplaceListCardActionButtons(props: MarketplaceListCardActionProps)
139
139
  event.stopPropagation();
140
140
  onManage("update", record);
141
141
  }}
142
- className="inline-flex h-6 items-center gap-1 whitespace-nowrap rounded-md border border-blue-200/80 bg-white px-2 text-[11px] font-medium text-blue-600 transition-colors hover:border-blue-300 hover:bg-blue-50 disabled:opacity-50"
142
+ className="inline-flex h-6 items-center gap-1 whitespace-nowrap rounded-md border border-blue-200/80 bg-white px-2 text-[11px] font-medium text-blue-600 transition-colors hover:border-blue-300 hover:bg-[var(--interaction-hover)] disabled:opacity-50"
143
143
  >
144
144
  <RefreshCw className="h-3 w-3" />
145
145
  {busyAction === "update"
@@ -155,7 +155,7 @@ function MarketplaceListCardActionButtons(props: MarketplaceListCardActionProps)
155
155
  event.stopPropagation();
156
156
  onManage("uninstall", record);
157
157
  }}
158
- className="inline-flex h-6 items-center gap-1 whitespace-nowrap rounded-md border border-gray-200/80 bg-white px-2 text-[11px] font-medium text-gray-500 transition-colors hover:border-rose-200 hover:bg-rose-50 hover:text-rose-600 disabled:opacity-50"
158
+ className="inline-flex h-6 items-center gap-1 whitespace-nowrap rounded-md border border-gray-200/80 bg-white px-2 text-[11px] font-medium text-gray-500 transition-colors hover:border-rose-200 hover:bg-destructive/10 hover:text-rose-600 disabled:opacity-50"
159
159
  >
160
160
  <Trash2 className="h-3 w-3" />
161
161
  {busyAction === "uninstall"
@@ -263,7 +263,7 @@ export function MarketplaceListCard(props: {
263
263
  return (
264
264
  <article
265
265
  onClick={onOpen}
266
- className="group flex h-full min-h-[156px] cursor-pointer flex-col rounded-xl border border-gray-200/60 bg-white p-3.5 shadow-sm transition-colors hover:border-gray-300 hover:bg-gray-50/60"
266
+ className="group flex h-full min-h-[156px] cursor-pointer flex-col rounded-xl border border-gray-200/60 bg-white p-3.5 shadow-sm transition-colors hover:border-gray-300 hover:bg-[var(--interaction-hover)]"
267
267
  >
268
268
  <div className="flex min-w-0 items-start gap-2.5">
269
269
  <MarketplaceItemIcon
@@ -7,6 +7,7 @@ import {
7
7
  SelectValue,
8
8
  } from "@/shared/components/ui/select";
9
9
  import { Skeleton } from "@/shared/components/ui/skeleton";
10
+ import { Input } from "@/shared/components/ui/input";
10
11
  import { t } from "@/shared/lib/i18n";
11
12
  import { Loader2, PackageSearch } from "lucide-react";
12
13
  import type { Ref } from "react";
@@ -30,10 +31,10 @@ export function FilterPanel({
30
31
  }) {
31
32
  return (
32
33
  <div className="mb-4">
33
- <div className="flex items-center gap-3">
34
+ <div className="flex flex-wrap items-center gap-3">
34
35
  <div className="relative min-w-0 flex-1">
35
36
  <PackageSearch className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400" />
36
- <input
37
+ <Input
37
38
  value={searchText}
38
39
  onChange={(event) => onSearchTextChange(event.target.value)}
39
40
  placeholder={searchPlaceholder}
@@ -52,7 +53,7 @@ export function FilterPanel({
52
53
  value={sort}
53
54
  onValueChange={(value) => onSortChange(value as MarketplaceSort)}
54
55
  >
55
- <SelectTrigger className="h-9 w-[150px] shrink-0 rounded-lg">
56
+ <SelectTrigger className="h-9 w-[110px] sm:w-[150px] shrink-0 rounded-lg">
56
57
  <SelectValue />
57
58
  </SelectTrigger>
58
59
  <SelectContent>
@@ -142,21 +142,21 @@ export function MarketplacePage({
142
142
  <PageLayout className="flex h-full min-h-0 flex-col pb-0">
143
143
  {!curatedSceneRoute.isSceneRoute && (
144
144
  <>
145
- <div className="mb-3 flex items-start justify-between gap-3 border-b border-gray-200/60">
145
+ <div className="mb-3 flex flex-wrap items-start justify-between gap-3 border-b border-border/60">
146
146
  <Tabs
147
147
  value={scope}
148
148
  onValueChange={(value) => setScope(value as ScopeType)}
149
149
  >
150
- <TabsList className="mb-0 h-auto flex-1 justify-start gap-6 rounded-none border-b-0 bg-transparent p-0 text-gray-500">
150
+ <TabsList className="mb-0 h-auto flex-1 justify-start gap-4 sm:gap-6 rounded-none border-b-0 bg-transparent p-0 text-muted-foreground">
151
151
  <TabsTrigger
152
152
  value="all"
153
- className="rounded-none border-b-2 border-transparent bg-transparent px-0 pb-3 pt-0 shadow-none hover:bg-transparent data-[state=active]:border-primary data-[state=active]:bg-transparent data-[state=active]:text-gray-950 data-[state=active]:shadow-none"
153
+ className="rounded-none border-b-2 border-transparent bg-transparent px-0 pb-3 pt-0 shadow-none hover:bg-[var(--interaction-hover)] data-[state=active]:border-primary data-[state=active]:bg-transparent data-[state=active]:text-gray-950 data-[state=active]:shadow-none"
154
154
  >
155
155
  {t("marketplaceTabMarketplaceSkills")}
156
156
  </TabsTrigger>
157
157
  <TabsTrigger
158
158
  value="installed"
159
- className="gap-1.5 rounded-none border-b-2 border-transparent bg-transparent px-0 pb-3 pt-0 shadow-none hover:bg-transparent data-[state=active]:border-primary data-[state=active]:bg-transparent data-[state=active]:text-gray-950 data-[state=active]:shadow-none"
159
+ className="gap-1.5 rounded-none border-b-2 border-transparent bg-transparent px-0 pb-3 pt-0 shadow-none hover:bg-[var(--interaction-hover)] data-[state=active]:border-primary data-[state=active]:bg-transparent data-[state=active]:text-gray-950 data-[state=active]:shadow-none"
160
160
  >
161
161
  {t("marketplaceTabInstalledSkills")}
162
162
  <span className="text-[11px] font-medium text-gray-500">
@@ -98,7 +98,7 @@ export function McpMarketplaceCard(props: {
98
98
  type="button"
99
99
  size="sm"
100
100
  variant="outline"
101
- className="rounded-xl border-blue-200 text-blue-700 hover:border-blue-300 hover:bg-blue-50 hover:text-blue-700"
101
+ className="rounded-xl border-blue-200 text-blue-700 hover:border-blue-300 hover:bg-[var(--interaction-hover)] hover:text-blue-700"
102
102
  onClick={(event) => {
103
103
  event.stopPropagation();
104
104
  onDoctor();
@@ -112,7 +112,7 @@ export function McpMarketplaceCard(props: {
112
112
  type="button"
113
113
  size="sm"
114
114
  variant="outline"
115
- className="rounded-xl border-rose-200 text-rose-600 hover:border-rose-300 hover:bg-rose-50 hover:text-rose-600"
115
+ className="rounded-xl border-rose-200 text-rose-600 hover:border-rose-300 hover:bg-destructive/10 hover:text-rose-600"
116
116
  onClick={(event) => {
117
117
  event.stopPropagation();
118
118
  onRemove();
@@ -288,8 +288,8 @@ export function McpMarketplacePage() {
288
288
  className={cn(
289
289
  "relative flex min-w-0 items-center gap-2 rounded-lg px-3 py-2 text-sm font-medium transition-colors",
290
290
  isActive
291
- ? "bg-card text-foreground shadow-sm"
292
- : "text-muted-foreground hover:bg-card/60 hover:text-foreground"
291
+ ? "bg-[var(--interaction-selection)] text-foreground"
292
+ : "text-muted-foreground hover:bg-[var(--interaction-hover)] hover:text-foreground"
293
293
  )}
294
294
  >
295
295
  <Icon className="h-4 w-4 shrink-0" />
@@ -297,7 +297,7 @@ export function McpMarketplacePage() {
297
297
  {tab.id === 'installed' && typeof tab.count === 'number' && (
298
298
  <span className={cn(
299
299
  "flex h-5 min-w-5 items-center justify-center rounded-full px-1.5 text-[11px] font-semibold",
300
- isActive ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"
300
+ isActive ? "bg-[var(--interaction-selection)] text-foreground" : "bg-muted text-muted-foreground"
301
301
  )}>
302
302
  {tab.count}
303
303
  </span>
@@ -40,7 +40,7 @@ export function PanelAppListItem({
40
40
  setIsDeleteDialogOpen(true);
41
41
  };
42
42
  return (
43
- <div className="group w-full min-w-0 rounded-lg border border-border/60 bg-card px-2.5 py-2.5 transition-colors hover:bg-muted/40">
43
+ <div className="group w-full min-w-0 rounded-lg border border-border/60 bg-card px-2.5 py-2.5 transition-colors hover:bg-[var(--interaction-hover)]">
44
44
  <div className="flex min-w-0 items-start gap-2">
45
45
  <button type="button" onClick={onOpen} className="min-w-0 flex-1 text-left">
46
46
  <span className="flex min-w-0 items-center gap-2">
@@ -59,7 +59,7 @@ export function PanelAppListItem({
59
59
  type="button"
60
60
  onClick={handleFavorite}
61
61
  disabled={favoritePending}
62
- className="rounded-md p-1.5 text-muted-foreground/70 transition-colors hover:bg-muted hover:text-foreground disabled:opacity-50"
62
+ className="rounded-md p-1.5 text-muted-foreground/70 transition-colors hover:bg-[var(--interaction-hover)] hover:text-foreground disabled:opacity-50"
63
63
  title={favoriteLabel}
64
64
  aria-label={favoriteLabel}
65
65
  >
@@ -69,7 +69,7 @@ export function PanelAppListItem({
69
69
  <PopoverTrigger asChild>
70
70
  <button
71
71
  type="button"
72
- className="rounded-md p-1.5 text-muted-foreground/70 transition-colors hover:bg-muted hover:text-foreground"
72
+ className="rounded-md p-1.5 text-muted-foreground/70 transition-colors hover:bg-[var(--interaction-hover)] hover:text-foreground"
73
73
  aria-label={t('panelAppsMoreActions')}
74
74
  disabled={deletePending}
75
75
  >
@@ -1,4 +1,6 @@
1
1
  import { ExternalLink } from "lucide-react";
2
+ import { ACTION_FEEDBACK, ACTION_MENU_ITEM_CLASS } from '@/shared/components/ui/actions/action-feedback';
3
+ import { cn } from '@/shared/lib/utils';
2
4
  import type { PanelAppEntryView } from "@/shared/lib/api";
3
5
  import { t } from "@/shared/lib/i18n";
4
6
 
@@ -22,7 +24,7 @@ export function PanelAppOpenStandaloneMenuItem({
22
24
  // `opener` keeps this trusted same-origin target auxiliary, so the current browser owns the new tab.
23
25
  rel="opener"
24
26
  referrerPolicy="no-referrer"
25
- className="flex w-full items-center gap-2 rounded-lg px-2.5 py-2 text-left text-sm text-muted-foreground transition-colors hover:bg-[var(--interaction-hover)] hover:text-accent-foreground"
27
+ className={cn(ACTION_MENU_ITEM_CLASS, ACTION_FEEDBACK.item)}
26
28
  onClick={onSelect}
27
29
  >
28
30
  <ExternalLink className="h-4 w-4 shrink-0" />
@@ -32,7 +32,7 @@ export function ProjectOverview({
32
32
  return (
33
33
  <div className="space-y-5">
34
34
  <section
35
- className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4"
35
+ className="grid grid-cols-2 gap-3 lg:grid-cols-4"
36
36
  aria-label={t("projectsWorkSummary")}
37
37
  >
38
38
  {summary.data ? (
@@ -81,8 +81,17 @@ export function ProjectsPage() {
81
81
  );
82
82
  if (!selectedProject || !tab)
83
83
  return (
84
- <main className="p-6 text-sm text-muted-foreground">
85
- {t("projectsChoose")}
84
+ <main className="space-y-3 p-4 text-sm text-muted-foreground sm:p-6">
85
+ <label htmlFor="project-home-selection" className="block font-medium text-foreground">{t("projectsChoose")}</label>
86
+ <select
87
+ id="project-home-selection"
88
+ value=""
89
+ onChange={(event) => navigate(`/projects/${encodeURIComponent(event.target.value)}/overview`)}
90
+ className="min-h-11 w-full rounded-xl border border-border bg-card px-3 text-base text-foreground"
91
+ >
92
+ <option value="" disabled>{t("projectsChoose")}</option>
93
+ {registered.map((project) => <option key={project.id} value={project.id}>{project.name}</option>)}
94
+ </select>
86
95
  </main>
87
96
  );
88
97
 
@@ -135,11 +135,13 @@ describe("ProjectsPage", () => {
135
135
  ).toBeTruthy();
136
136
  });
137
137
 
138
- it("asks the user to choose from the sidebar when no project route is selected", () => {
138
+ it("lets users enter a project without a desktop sidebar", () => {
139
139
  renderPage("/projects");
140
140
  expect(mocks.useProjectAgreement).toHaveBeenCalledWith(null);
141
141
  expect(mocks.useProjectSkills).toHaveBeenCalledWith(null);
142
- expect(screen.getByText(/Choose a project|选择项目/)).toBeTruthy();
142
+ const picker = screen.getByRole("combobox", { name: /Choose a project|选择项目/ });
143
+ fireEvent.change(picker, { target: { value: "project-1" } });
144
+ expect(screen.getByRole("tab", { name: /Overview|概览/ }).getAttribute("aria-selected")).toBe("true");
143
145
  });
144
146
 
145
147
  it("loads each fixed project material only on its own tab", () => {
@@ -122,7 +122,7 @@ export function PwaInstallBanner() {
122
122
  <button
123
123
  type='button'
124
124
  aria-label={t('pwaInstallDismiss')}
125
- className='rounded-full p-1 text-gray-400 transition hover:bg-gray-100 hover:text-gray-600'
125
+ className='rounded-full p-1 text-gray-400 transition hover:bg-[var(--interaction-hover)] hover:text-gray-600'
126
126
  onClick={() => {
127
127
  pwaInstallManager.dismissInstallPrompt();
128
128
  }}
@@ -110,7 +110,15 @@ describe("shared left pages", () => {
110
110
  it("preserves the collapsed group preference", async () => {
111
111
  mocks.entries = [createEntry({ mainSidebar: true })];
112
112
  renderNav();
113
- await userEvent.click(screen.getByRole("button", { name: "Pages 1" }));
113
+ const groupTrigger = screen.getByRole("button", { name: "Pages 1" });
114
+ expect(groupTrigger.className).toContain(
115
+ "hover:bg-[var(--sidebar-interaction-hover)]",
116
+ );
117
+ expect(groupTrigger.className).toContain(
118
+ "active:bg-[var(--sidebar-interaction-selection)]",
119
+ );
120
+ expect(groupTrigger.className).toContain("focus-visible:ring-1");
121
+ await userEvent.click(groupTrigger);
114
122
  expect(screen.queryByRole("link")).toBeNull();
115
123
  expect(
116
124
  useViewportLayoutStore.getState().isMainSidebarAppGroupCollapsed,
@@ -105,7 +105,7 @@ export function PageResourceSidebarNav({
105
105
  type="button"
106
106
  aria-expanded={!groupCollapsed}
107
107
  onClick={viewportLayoutManager.toggleMainSidebarAppGroupCollapsed}
108
- className="flex w-full items-center gap-2 px-2.5 py-1.5 text-[13px] text-muted-foreground"
108
+ className={`flex w-full items-center gap-2 px-2.5 py-1.5 text-[13px] ${SIDEBAR_RAIL_SURFACE_CLASS}`}
109
109
  >
110
110
  <ChevronDown
111
111
  className={`h-3.5 w-3.5 ${groupCollapsed ? "-rotate-90" : ""}`}
@@ -52,7 +52,7 @@ const HOME_ITEM_VIEW_BY_ID: Record<string, RightPanelResourceHomeItem> = {
52
52
  function resolveHomeItemView(item: RightPanelResourceHomeNavigationItem): RightPanelResourceHomeItem {
53
53
  return HOME_ITEM_VIEW_BY_ID[item.id] ?? {
54
54
  accentClassName: 'bg-gray-50 text-gray-700',
55
- hoverAccentClassName: 'group-hover:bg-gray-100',
55
+ hoverAccentClassName: 'group-hover:bg-[var(--interaction-hover)]',
56
56
  icon: <Boxes className="h-5 w-5" />,
57
57
  };
58
58
  }
@@ -66,7 +66,7 @@ export function ServiceActionRow({
66
66
  <button
67
67
  type="button"
68
68
  disabled={grantAgentPending || availableAgents.length === 0}
69
- className="ml-1 rounded-md p-1 text-muted-foreground/70 transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border disabled:cursor-not-allowed disabled:opacity-40"
69
+ className="ml-1 rounded-md p-1 text-muted-foreground/70 transition-colors hover:bg-[var(--interaction-hover)] hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border disabled:cursor-not-allowed disabled:opacity-40"
70
70
  title={t("serviceAppsGrantToAgent")}
71
71
  aria-label={t("serviceAppsGrantToAgent")}
72
72
  >
@@ -85,7 +85,7 @@ export function ServiceActionRow({
85
85
  onGrantAgent(action.id, agent.id);
86
86
  setAgentMenuOpen(false);
87
87
  }}
88
- className="flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-left text-xs text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
88
+ className="flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-left text-xs text-foreground transition-colors hover:bg-[var(--interaction-hover)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
89
89
  >
90
90
  <Bot className="h-3.5 w-3.5 text-muted-foreground" />
91
91
  <span className="min-w-0 truncate">
@@ -111,7 +111,7 @@ export function ServiceActionRow({
111
111
  <button
112
112
  type="button"
113
113
  onClick={() => onRevoke(grant)}
114
- className="rounded-md p-1 text-muted-foreground/70 transition-colors hover:bg-muted hover:text-rose-600 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
114
+ className="rounded-md p-1 text-muted-foreground/70 transition-colors hover:bg-[var(--interaction-hover)] hover:text-rose-600 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
115
115
  title={t("serviceAppsRevokeGrant")}
116
116
  aria-label={t("serviceAppsRevokeGrant")}
117
117
  >
@@ -99,7 +99,7 @@ export function ServiceAppListItem({
99
99
  };
100
100
  return (
101
101
  <TooltipProvider delayDuration={250}>
102
- <section className="group bg-card transition-colors hover:bg-muted/25">
102
+ <section className="group bg-card transition-colors hover:bg-[var(--interaction-hover)]">
103
103
  <div className="flex items-start gap-3 px-4 py-3.5">
104
104
  <div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground">
105
105
  <Server className="h-4 w-4" />
@@ -155,7 +155,7 @@ export function ServiceAppListItem({
155
155
  <PopoverTrigger asChild>
156
156
  <button
157
157
  type="button"
158
- className="rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border disabled:opacity-50"
158
+ className="rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-[var(--interaction-hover)] hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border disabled:opacity-50"
159
159
  aria-label={t("serviceAppsMoreActions")}
160
160
  disabled={deletePending}
161
161
  >
@@ -208,7 +208,7 @@ export function ServiceAppListItem({
208
208
  <button
209
209
  type="button"
210
210
  onClick={() => onActionsOpenChange(!actionsOpen)}
211
- className="flex min-w-0 flex-1 items-center justify-between gap-2 px-4 py-2 text-left text-xs text-muted-foreground transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-border"
211
+ className="flex min-w-0 flex-1 items-center justify-between gap-2 px-4 py-2 text-left text-xs text-muted-foreground transition-colors hover:bg-[var(--interaction-hover)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-border"
212
212
  aria-expanded={actionsOpen}
213
213
  >
214
214
  <div className="flex min-w-0 items-center gap-2">
@@ -228,7 +228,7 @@ export function ServiceAppListItem({
228
228
  <button
229
229
  type="button"
230
230
  onClick={() => setIsDiagnosticsOpen((open) => !open)}
231
- className="flex shrink-0 items-center gap-1 border-l border-border/60 px-3 text-xs text-muted-foreground transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-border"
231
+ className="flex shrink-0 items-center gap-1 border-l border-border/60 px-3 text-xs text-muted-foreground transition-colors hover:bg-[var(--interaction-hover)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-border"
232
232
  aria-expanded={isDiagnosticsOpen}
233
233
  >
234
234
  <span>{t("serviceAppsDetails")}</span>
@@ -110,7 +110,7 @@ export function ServiceAppsPanel({
110
110
  <TooltipTrigger asChild>
111
111
  <button
112
112
  type="button"
113
- className="rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
113
+ className="rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-[var(--interaction-hover)] hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
114
114
  aria-label={t("serviceAppsRefresh")}
115
115
  onClick={refetch}
116
116
  >
@@ -143,7 +143,7 @@ export function ProviderModelList(props: ProviderModelListProps) {
143
143
  return (
144
144
  <label
145
145
  key={modelName}
146
- className="inline-flex max-w-full cursor-pointer items-center gap-2 rounded-full border border-border/55 bg-muted/45 px-3 py-1.5 hover:bg-muted/70"
146
+ className="inline-flex max-w-full cursor-pointer items-center gap-2 rounded-full border border-border/55 bg-muted/45 px-3 py-1.5 hover:bg-[var(--interaction-hover)]"
147
147
  >
148
148
  <input
149
149
  type="checkbox"
@@ -180,7 +180,7 @@ export function ProviderModelList(props: ProviderModelListProps) {
180
180
  <PopoverTrigger asChild>
181
181
  <button
182
182
  type="button"
183
- className="inline-flex h-5 w-5 items-center justify-center rounded-full text-muted-foreground/70 opacity-100 transition-opacity hover:bg-muted hover:text-foreground md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100"
183
+ className="inline-flex h-5 w-5 items-center justify-center rounded-full text-muted-foreground/70 opacity-100 transition-opacity hover:bg-[var(--interaction-hover)] hover:text-foreground md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100"
184
184
  aria-label={t("providerModelThinkingTitle")}
185
185
  title={t("providerModelThinkingTitle")}
186
186
  >
@@ -224,8 +224,8 @@ export function ProviderModelList(props: ProviderModelListProps) {
224
224
  }
225
225
  className={`rounded-full border px-2.5 py-1 text-xs font-medium transition-colors ${
226
226
  selected
227
- ? "border-foreground/15 bg-foreground text-background"
228
- : "border-border/55 bg-muted/50 text-muted-foreground hover:border-border hover:bg-muted hover:text-foreground"
227
+ ? "border-[var(--interaction-selection-border)] bg-[var(--interaction-selection)] text-foreground"
228
+ : "border-border/55 bg-muted/50 text-muted-foreground hover:border-border hover:bg-[var(--interaction-hover)] hover:text-foreground"
229
229
  }`}
230
230
  >
231
231
  {formatThinkingLevelLabel(level)}
@@ -274,7 +274,7 @@ export function ProviderModelList(props: ProviderModelListProps) {
274
274
  <button
275
275
  type="button"
276
276
  onClick={() => onRemoveModel(modelName)}
277
- className="inline-flex h-5 w-5 items-center justify-center rounded-full text-muted-foreground/70 opacity-100 transition-opacity hover:bg-muted hover:text-foreground md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100"
277
+ className="inline-flex h-5 w-5 items-center justify-center rounded-full text-muted-foreground/70 opacity-100 transition-opacity hover:bg-[var(--interaction-hover)] hover:text-foreground md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100"
278
278
  aria-label={t("remove")}
279
279
  >
280
280
  <X className="h-3 w-3" />