@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.
- package/CHANGELOG.md +39 -0
- package/dist/.vite/manifest.json +366 -365
- package/dist/assets/{account-panel-ZM2qzKF4.js → account-panel-DVi_jhkG.js} +1 -1
- package/dist/assets/{api-Cjq-o1VN.js → api-BZvNpFOS.js} +2 -2
- package/dist/assets/app-presenter-provider-BcYL-VNe.js +124 -0
- package/dist/assets/{appearance-settings-page-B4zouKzg.js → appearance-settings-page-AZNBzLmT.js} +1 -1
- package/dist/assets/{book-open-AteA89HX.js → book-open-CmRdT3N1.js} +1 -1
- package/dist/assets/card-CdRBL1E0.js +1 -0
- package/dist/assets/{channels-list-page-fj8kMo-T.js → channels-list-page-eZN74FN3.js} +2 -2
- package/dist/assets/{circle-dashed-LSYZTzM7.js → circle-dashed-HOgt3gIJ.js} +1 -1
- package/dist/assets/{client-DudCRJw4.js → client-DKKbdJ6Q.js} +1 -1
- package/dist/assets/config-split-page-BA3V6b3Q.js +1 -0
- package/dist/assets/{confirm-dialog-DShT64F6.js → confirm-dialog-8vqYZ0wy.js} +1 -1
- package/dist/assets/context-menu-DHVVdHzN.js +1 -0
- package/dist/assets/{createLucideIcon-D7hvIHV-.js → createLucideIcon-DE0sB9Nu.js} +1 -1
- package/dist/assets/{desktop-capabilities-page-CzqzFZUY.js → desktop-capabilities-page-B9-B9k1A.js} +1 -1
- package/dist/assets/{desktop-update-config-CgeQ0Ffo.js → desktop-update-config-Cfr3IRUe.js} +1 -1
- package/dist/assets/{dialog-C7glmAQJ.js → dialog-BUfWO8hr.js} +2 -2
- package/dist/assets/{doc-browser-context-BlhBAOLH.js → doc-browser-context-ClLgMbxd.js} +1 -1
- package/dist/assets/doc-browser-w8q1j0jL.js +1 -0
- package/dist/assets/{download-BYgECE0n.js → download-CKqS1Ksj.js} +1 -1
- package/dist/assets/{es2015-CsjAAQ6X.js → es2015-BiB05Sav.js} +1 -1
- package/dist/assets/{extensions-qYL-NpL3.js → extensions-vPD4dGqt.js} +1 -1
- package/dist/assets/{eye-BBuTzJ53.js → eye-bKzylu3p.js} +1 -1
- package/dist/assets/{form-actions-CLGWO52v.js → form-actions-CCoxz33Q.js} +1 -1
- package/dist/assets/{i18n-qBGEbcW6.js → i18n-OGmF0Ws_.js} +2 -2
- package/dist/assets/{keyboard-shortcuts-settings-page-B-wK_6zG.js → keyboard-shortcuts-settings-page-D8rosOVh.js} +1 -1
- package/dist/assets/loader-circle-Clq1Gtb1.js +1 -0
- package/dist/assets/{login-page-Cd84lpVV.js → login-page-BT3GV3Sb.js} +1 -1
- package/dist/assets/login-page-DKId3By8.js +1 -0
- package/dist/assets/main-bEWCGDHr.js +2 -0
- package/dist/assets/main-i18n-catalog.config-Bpu5GcwF.js +1 -0
- package/dist/assets/main-kqMRTwOs.css +1 -0
- package/dist/assets/mcp-marketplace-page-CZIP6j3L.js +1 -0
- package/dist/assets/{model-config-page-D0DQlSDa.js → model-config-page-D21a-BO0.js} +1 -1
- package/dist/assets/{navigation-link-32iM1-e6.js → navigation-link-DC-Pku0Y.js} +1 -1
- package/dist/assets/panel-app-BYfQu_TS.js +1 -0
- package/dist/assets/panel-app-i18n-catalog.config-D44qmtfk.js +1 -0
- package/dist/assets/panelStandalone-BRg7B1EI.css +1 -0
- package/dist/assets/{panelStandalone-C9EKaApN.js → panelStandalone-CX8X2hjh.js} +2 -2
- package/dist/assets/{privacy-settings-page-Bxxx12OO.js → privacy-settings-page-e0LFHZJm.js} +1 -1
- package/dist/assets/projects-Rki7Fmth.js +1 -0
- package/dist/assets/provider-models-DFQgF4hS.js +1 -0
- package/dist/assets/provider-scoped-model-input-DaONL0j_.js +1 -0
- package/dist/assets/providers-config-page-CEOhQlMe.js +1 -0
- package/dist/assets/{puzzle-CWHfFyYN.js → puzzle-D_TiFXOv.js} +1 -1
- package/dist/assets/{pwa-CiGUjpcd.js → pwa-C9ac_1dg.js} +1 -1
- package/dist/assets/{refresh-cw-y3L6PyLt.js → refresh-cw-BG8VfcdA.js} +1 -1
- package/dist/assets/remote-DkSx5x4R.js +1 -0
- package/dist/assets/{rotate-cw-CNE9qSHi.js → rotate-cw-DPEegUU2.js} +1 -1
- package/dist/assets/{runtime-config-page-5o98T-TD.js → runtime-config-page-DDT1dVLt.js} +1 -1
- package/dist/assets/{save-B0p5a0ET.js → save-CPlctBjT.js} +1 -1
- package/dist/assets/{search-config-page-CpGcnIzi.js → search-config-page-CDwmd6eX.js} +1 -1
- package/dist/assets/{secrets-config-page-C4VTiW3r.js → secrets-config-page-1OvgmwPv.js} +1 -1
- package/dist/assets/security-config-Pv4CM-kw.js +1 -0
- package/dist/assets/{select-CctTN205.js → select-Y9IEbx9x.js} +1 -1
- package/dist/assets/service-apps-panel-DPrLeF3N.js +1 -0
- package/dist/assets/{settings-2-EPflrNk6.js → settings-2-CrGx9_De.js} +1 -1
- package/dist/assets/{status-dot-CensgJZC.js → status-dot-Cb8ewlUr.js} +1 -1
- package/dist/assets/{tag-chip-Bjzb4-qW.js → tag-chip-D-6aPuuh.js} +1 -1
- package/dist/assets/{theme-provider-hlle7Skk.js → theme-provider-BjF1Eowc.js} +1 -1
- package/dist/assets/{trash-2-C5KH5Rw9.js → trash-2-DER2EJKK.js} +1 -1
- package/dist/assets/{use-config-CPl_F79t.js → use-config-BM5jC65L.js} +1 -1
- package/dist/assets/{use-desktop-capabilities-CEg9-DrQ.js → use-desktop-capabilities-ezKJwKqg.js} +1 -1
- package/dist/assets/{use-viewport-layout-CPZUmTPi.js → use-viewport-layout-vmbQUNT6.js} +1 -1
- package/dist/assets/x-DSz9cGhy.js +1 -0
- package/dist/index.html +34 -34
- package/dist/panel-standalone.html +11 -11
- package/package.json +3 -3
- package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +2 -2
- package/src/app/components/layout/page-layout.tsx +5 -2
- package/src/app/components/layout/sidebar-rail.styles.ts +3 -3
- package/src/app/configs/__tests__/app-navigation.config.test.ts +10 -0
- package/src/app/configs/app-navigation.config.ts +6 -2
- package/src/app/styles/charcoal-theme.css +0 -3
- package/src/app/styles/design-system.css +18 -6
- package/src/app/styles/island-theme.css +2 -15
- package/src/features/agents/components/agent-details-dialog.tsx +1 -1
- package/src/features/agents/components/agents-page.tsx +5 -3
- package/src/features/app-data/components/app-data-removal-choice.tsx +1 -1
- package/src/features/apps/components/app-package-card.tsx +7 -6
- package/src/features/channels/pages/channels-list-page.tsx +1 -1
- package/src/features/chat/components/chat-sidebar-list-mode-switch.tsx +1 -1
- package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +28 -16
- package/src/features/chat/components/conversation/chat-conversation-content.tsx +8 -2
- package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +3 -3
- package/src/features/chat/components/conversation/chat-conversation-header.tsx +4 -4
- package/src/features/chat/components/layout/__tests__/chat-sidebar-session-area.test.tsx +6 -4
- package/src/features/chat/components/layout/__tests__/chat-sidebar-toolbar.test.tsx +22 -1
- package/src/features/chat/components/layout/chat-page-shell.tsx +1 -1
- package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +10 -14
- package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +50 -12
- package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +4 -4
- package/src/features/chat/components/layout/chat-sidebar.tsx +2 -2
- package/src/features/chat/features/conversation/components/session-queued-input-rows.tsx +1 -1
- package/src/features/chat/features/message/components/chat-message-list.container.tsx +12 -1
- package/src/features/chat/features/message/components/chat-tool-call-stress-harness.tsx +32 -2
- package/src/features/chat/features/message/hooks/__tests__/use-chat-message-virtualizer.test.tsx +7 -5
- package/src/features/chat/features/message/hooks/use-chat-message-virtualizer.ts +7 -3
- package/src/features/chat/features/session/components/__tests__/chat-sidebar-project-groups.test.tsx +1 -1
- package/src/features/chat/features/session/components/chat-mobile-session-row.tsx +2 -2
- package/src/features/chat/features/session/components/chat-sidebar-context-card.tsx +1 -1
- package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +1 -1
- package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +7 -5
- package/src/features/chat/features/session/components/chat-sidebar-session-list.tsx +45 -10
- package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +1 -1
- package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +3 -5
- package/src/features/chat/features/session/components/session-header/chat-session-header-menu-item.tsx +4 -3
- package/src/features/chat/features/session/components/session-header/chat-session-more-actions-menu.tsx +3 -0
- package/src/features/chat/features/session/components/session-header/chat-session-switcher-groups.tsx +6 -6
- package/src/features/chat/features/session/components/session-header/chat-session-title-switcher.tsx +6 -5
- package/src/features/chat/features/session-type/components/chat-session-type-option-item.tsx +1 -1
- package/src/features/chat/features/welcome/components/chat-welcome-agent-picker.tsx +0 -1
- package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +1 -1
- package/src/features/chat/features/welcome/components/chat-welcome-prompt-suggestions.tsx +1 -1
- package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +1 -1
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumb-browser.tsx +2 -2
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +2 -2
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +1 -1
- package/src/features/chat/features/workspace/components/child-sessions/chat-session-child-sessions.tsx +2 -2
- package/src/features/chat/features/workspace/components/file-content-preview/workspace-spreadsheet-preview.tsx +2 -2
- package/src/features/chat/features/workspace/components/overview/chat-session-continuous-attention.tsx +3 -3
- package/src/features/chat/features/workspace/components/overview/chat-session-token-usage.tsx +1 -1
- package/src/features/chat/features/workspace/components/project-files/workspace-directory-tree.tsx +4 -3
- package/src/features/chat/features/workspace/components/project-files/workspace-new-folder-tree-item.tsx +2 -1
- package/src/features/chat/features/workspace/components/project-files/workspace-project-files-toolbar.tsx +3 -1
- package/src/features/chat/features/workspace/components/session-cron-job-content.tsx +3 -3
- package/src/features/chat/features/workspace/components/workspace-file-content-preview.tsx +2 -2
- package/src/features/chat/features/workspace/components/workspace-markdown-outline.tsx +1 -1
- package/src/features/cron/components/cron-config.tsx +3 -2
- package/src/features/cron/components/cron-job-detail-dialog.tsx +1 -1
- package/src/features/cron/components/cron-job-row.tsx +7 -7
- package/src/features/cron/components/cron-task-discovery.tsx +1 -1
- package/src/features/inbox/components/inbox-delivery-content.tsx +1 -1
- package/src/features/inbox/pages/inbox-page.tsx +25 -29
- package/src/features/marketplace/components/curated-shelves/marketplace-curated-shelves.tsx +2 -2
- package/src/features/marketplace/components/curated-shelves/marketplace-shelf-card.tsx +2 -2
- package/src/features/marketplace/components/marketplace-list-card.tsx +4 -4
- package/src/features/marketplace/components/marketplace-page-parts.tsx +4 -3
- package/src/features/marketplace/components/marketplace-page.tsx +4 -4
- package/src/features/marketplace/components/mcp/mcp-marketplace-card.tsx +2 -2
- package/src/features/marketplace/components/mcp/mcp-marketplace-page.tsx +3 -3
- package/src/features/panel-apps/components/panel-app-list-item.tsx +3 -3
- package/src/features/panel-apps/components/panel-app-open-standalone-menu-item.tsx +3 -1
- package/src/features/projects/components/project-overview.tsx +1 -1
- package/src/features/projects/pages/project-home-page.tsx +11 -2
- package/src/features/projects/pages/projects-page.test.tsx +4 -2
- package/src/features/pwa/components/pwa-install-entry.tsx +1 -1
- package/src/features/right-panel-resources/components/__tests__/page-resource-sidebar-nav.test.tsx +9 -1
- package/src/features/right-panel-resources/components/page-resource-sidebar-nav.tsx +1 -1
- package/src/features/right-panel-resources/components/right-panel-resource-home-page.tsx +1 -1
- package/src/features/service-apps/components/service-action-row.tsx +3 -3
- package/src/features/service-apps/components/service-app-list-item.tsx +4 -4
- package/src/features/service-apps/components/service-apps-panel.tsx +1 -1
- package/src/features/settings/components/config/provider-model-list.tsx +5 -5
- package/src/features/settings/components/config/provider-model-suggestions-panel.tsx +4 -4
- package/src/features/settings/components/config/provider-pill-selector.tsx +1 -1
- package/src/features/settings/pages/appearance-settings-page.tsx +2 -2
- package/src/features/settings/pages/providers-config-page.tsx +6 -6
- package/src/features/side-dock/components/side-dock.tsx +4 -4
- package/src/index.css +4 -0
- package/src/platforms/desktop/components/desktop-window-chrome.tsx +1 -1
- package/src/platforms/mobile/components/mobile-app-shell.tsx +1 -1
- package/src/platforms/mobile/components/mobile-bottom-nav.tsx +10 -9
- package/src/platforms/mobile/components/mobile-settings-shell.tsx +24 -23
- package/src/platforms/mobile/components/mobile-topbar.tsx +10 -11
- package/src/shared/components/common/brand-header.tsx +1 -1
- package/src/shared/components/common/searchable-model-input.tsx +2 -2
- package/src/shared/components/config-split-page.tsx +3 -3
- package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +34 -2
- package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +38 -7
- package/src/shared/components/doc-browser/doc-browser.tsx +5 -1
- package/src/shared/components/path-picker/server-path-picker-locations.tsx +1 -1
- package/src/shared/components/settings/settings-page.tsx +1 -1
- package/src/shared/components/ui/actions/__tests__/icon-action-button.test.tsx +5 -5
- package/src/shared/components/ui/actions/action-feedback.ts +8 -0
- package/src/shared/components/ui/actions/icon-action-button.tsx +13 -12
- package/src/shared/components/ui/button.tsx +3 -3
- package/src/shared/components/ui/card.tsx +1 -1
- package/src/shared/components/ui/context-menu/context-menu-items.tsx +6 -5
- package/src/shared/components/ui/dialog.tsx +4 -3
- package/src/shared/components/ui/input.tsx +7 -2
- package/src/shared/components/ui/overlays/sheet.tsx +1 -1
- package/src/shared/components/ui/popover.tsx +1 -1
- package/src/shared/components/ui/select.tsx +3 -2
- package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +8 -6
- package/src/shared/components/ui/tabs.tsx +3 -3
- package/src/shared/components/ui/textarea.tsx +1 -0
- package/src/shared/components/workbench/workbench-surface-toolbar.tsx +3 -4
- package/src/shared/components/workbench/workbench-surface.tsx +2 -2
- package/src/shared/lib/i18n/locales/en-US/agents.json +2 -0
- package/src/shared/lib/i18n/locales/en-US/core.json +1 -0
- package/src/shared/lib/i18n/locales/en-US/desktop-update.json +1 -1
- package/src/shared/lib/i18n/locales/zh-CN/agents.json +2 -0
- package/src/shared/lib/i18n/locales/zh-CN/core.json +1 -0
- package/src/shared/lib/i18n/locales/zh-CN/desktop-update.json +1 -1
- package/dist/assets/app-presenter-provider-C02YPoMr.js +0 -124
- package/dist/assets/card-BLXvBstA.js +0 -1
- package/dist/assets/config-split-page-G7Kt2BnV.js +0 -1
- package/dist/assets/context-menu-rd7b_kVD.js +0 -1
- package/dist/assets/doc-browser-dBiR9Qi0.js +0 -1
- package/dist/assets/loader-circle-BA7EMkih.js +0 -1
- package/dist/assets/login-page-VdMyNkFf.js +0 -1
- package/dist/assets/main-CR0SrTBf.js +0 -2
- package/dist/assets/main-DXH6EdXI.css +0 -1
- package/dist/assets/main-i18n-catalog.config-Btp_OowF.js +0 -1
- package/dist/assets/mcp-marketplace-page-jacRwiI-.js +0 -1
- package/dist/assets/panel-app-CC0bVSu4.js +0 -1
- package/dist/assets/panel-app-i18n-catalog.config-LUSBp5Ce.js +0 -1
- package/dist/assets/panelStandalone-Y91OJkgV.css +0 -1
- package/dist/assets/projects-D56yAPNT.js +0 -1
- package/dist/assets/provider-models-C3frdXeW.js +0 -1
- package/dist/assets/provider-scoped-model-input-CYetrPYZ.js +0 -1
- package/dist/assets/providers-config-page-DESB1h8g.js +0 -1
- package/dist/assets/remote-DiGFV8nP.js +0 -1
- package/dist/assets/security-config-C5-RQVpS.js +0 -1
- package/dist/assets/service-apps-panel-CKE5HKNY.js +0 -1
- package/dist/assets/x--xxUvHKW.js +0 -1
|
@@ -69,7 +69,7 @@ export function ProviderModelSuggestionsPanel(props: ProviderModelSuggestionsPan
|
|
|
69
69
|
<button
|
|
70
70
|
type='button'
|
|
71
71
|
aria-expanded={expanded}
|
|
72
|
-
className='flex min-w-0 flex-1 items-center gap-1.5 rounded-lg px-2 py-1.5 text-left text-xs text-muted-foreground transition-colors hover:bg-
|
|
72
|
+
className='flex min-w-0 flex-1 items-center gap-1.5 rounded-lg px-2 py-1.5 text-left text-xs text-muted-foreground transition-colors hover:bg-[var(--interaction-hover)] hover:text-foreground'
|
|
73
73
|
onClick={() => onExpandedChange(!expanded)}
|
|
74
74
|
>
|
|
75
75
|
<Sparkles className='h-3.5 w-3.5 shrink-0 text-primary' />
|
|
@@ -81,7 +81,7 @@ export function ProviderModelSuggestionsPanel(props: ProviderModelSuggestionsPan
|
|
|
81
81
|
{models.length <= BULK_ADD_LIMIT ? (
|
|
82
82
|
<button
|
|
83
83
|
type='button'
|
|
84
|
-
className='shrink-0 rounded-lg px-2 py-1.5 text-xs font-medium text-primary transition-colors hover:bg-
|
|
84
|
+
className='shrink-0 rounded-lg px-2 py-1.5 text-xs font-medium text-primary transition-colors hover:bg-[var(--interaction-hover)]'
|
|
85
85
|
onClick={() => addModels(models)}
|
|
86
86
|
>
|
|
87
87
|
{t('providerModelsSuggestionsAddAll')}
|
|
@@ -105,7 +105,7 @@ export function ProviderModelSuggestionsPanel(props: ProviderModelSuggestionsPan
|
|
|
105
105
|
{visibleModels.map((modelName) => (
|
|
106
106
|
<label
|
|
107
107
|
key={modelName}
|
|
108
|
-
className='grid min-w-0 cursor-pointer grid-cols-[auto_minmax(0,1fr)] items-center gap-2 rounded-lg px-2 py-1.5 hover:bg-
|
|
108
|
+
className='grid min-w-0 cursor-pointer grid-cols-[auto_minmax(0,1fr)] items-center gap-2 rounded-lg px-2 py-1.5 hover:bg-[var(--interaction-hover)]'
|
|
109
109
|
>
|
|
110
110
|
<input
|
|
111
111
|
type='checkbox'
|
|
@@ -137,7 +137,7 @@ export function ProviderModelSuggestionsPanel(props: ProviderModelSuggestionsPan
|
|
|
137
137
|
<button
|
|
138
138
|
type='button'
|
|
139
139
|
disabled={selectedModels.length === 0}
|
|
140
|
-
className='shrink-0 rounded-md bg-primary/10 px-2 py-1 text-[11px] font-medium text-primary transition-colors hover:bg-
|
|
140
|
+
className='shrink-0 rounded-md bg-primary/10 px-2 py-1 text-[11px] font-medium text-primary transition-colors hover:bg-[var(--interaction-hover)] disabled:cursor-not-allowed disabled:opacity-45'
|
|
141
141
|
onClick={() => addModels(selectedModels)}
|
|
142
142
|
>
|
|
143
143
|
{t('providerModelsSuggestionsAddSelected')}
|
|
@@ -16,7 +16,7 @@ export function ProviderPillSelector(props: ProviderPillSelectorProps) {
|
|
|
16
16
|
type="button"
|
|
17
17
|
onClick={() => onChange(option.value)}
|
|
18
18
|
aria-pressed={selected}
|
|
19
|
-
className={`rounded-full border px-3 py-1.5 text-xs font-medium transition-colors ${selected ? 'border-
|
|
19
|
+
className={`rounded-full border px-3 py-1.5 text-xs font-medium transition-colors ${selected ? 'border-[var(--interaction-selection-border)] bg-[var(--interaction-selection)] text-foreground' : 'border-border/55 bg-muted/50 text-muted-foreground hover:border-border hover:bg-[var(--interaction-hover)] hover:text-foreground'}`}
|
|
20
20
|
>
|
|
21
21
|
{option.label}
|
|
22
22
|
</button>
|
|
@@ -100,8 +100,8 @@ export function AppearanceSettingsPage() {
|
|
|
100
100
|
className={cn(
|
|
101
101
|
'flex min-w-0 items-start gap-3 rounded-lg px-3 py-2.5 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40',
|
|
102
102
|
selected
|
|
103
|
-
? 'bg-
|
|
104
|
-
: 'text-muted-foreground hover:bg-
|
|
103
|
+
? 'bg-[var(--interaction-selection)] text-foreground'
|
|
104
|
+
: 'text-muted-foreground hover:bg-[var(--interaction-hover)] hover:text-foreground'
|
|
105
105
|
)}
|
|
106
106
|
>
|
|
107
107
|
<span
|
|
@@ -156,7 +156,7 @@ export function ProvidersConfigPage() {
|
|
|
156
156
|
<div className='grid max-h-[24rem] grid-cols-2 gap-2 overflow-y-auto sm:grid-cols-3'>
|
|
157
157
|
<button
|
|
158
158
|
type='button'
|
|
159
|
-
className='flex min-h-20 w-full flex-col items-start gap-2 rounded-lg border border-dashed border-border/70 bg-muted/20 px-3 py-2 text-left transition-colors hover:border-border hover:bg-
|
|
159
|
+
className='flex min-h-20 w-full flex-col items-start gap-2 rounded-lg border border-dashed border-border/70 bg-muted/20 px-3 py-2 text-left transition-colors hover:border-border hover:bg-[var(--interaction-hover)]'
|
|
160
160
|
onClick={async () => {
|
|
161
161
|
try {
|
|
162
162
|
const result = await createProvider.mutateAsync({
|
|
@@ -183,7 +183,7 @@ export function ProvidersConfigPage() {
|
|
|
183
183
|
<button
|
|
184
184
|
key={template.providerType}
|
|
185
185
|
type='button'
|
|
186
|
-
className='flex min-h-20 w-full flex-col items-start gap-2 rounded-lg border border-border/55 bg-muted/15 px-3 py-2 text-left transition-colors hover:border-border/80 hover:bg-
|
|
186
|
+
className='flex min-h-20 w-full flex-col items-start gap-2 rounded-lg border border-border/55 bg-muted/15 px-3 py-2 text-left transition-colors hover:border-border/80 hover:bg-[var(--interaction-hover)]'
|
|
187
187
|
onClick={async () => {
|
|
188
188
|
try {
|
|
189
189
|
const result = await createProvider.mutateAsync({
|
|
@@ -254,8 +254,8 @@ export function ProvidersConfigPage() {
|
|
|
254
254
|
className={cn(
|
|
255
255
|
'group w-full rounded-xl p-2.5 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40',
|
|
256
256
|
resolvedSelectedProvider === provider.providerId
|
|
257
|
-
? 'bg-
|
|
258
|
-
: 'bg-transparent text-muted-foreground hover:bg-
|
|
257
|
+
? 'bg-[var(--interaction-selection)] text-foreground'
|
|
258
|
+
: 'bg-transparent text-muted-foreground hover:bg-[var(--interaction-hover)] hover:text-foreground'
|
|
259
259
|
)}
|
|
260
260
|
>
|
|
261
261
|
<div className='relative min-h-10'>
|
|
@@ -300,7 +300,7 @@ export function ProvidersConfigPage() {
|
|
|
300
300
|
<PopoverTrigger asChild>
|
|
301
301
|
<button
|
|
302
302
|
type='button'
|
|
303
|
-
className='inline-flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground/70 hover:bg-
|
|
303
|
+
className='inline-flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground/70 hover:bg-[var(--interaction-hover)] hover:text-foreground'
|
|
304
304
|
onClick={(event) => event.stopPropagation()}
|
|
305
305
|
title={t('more')}
|
|
306
306
|
>
|
|
@@ -310,7 +310,7 @@ export function ProvidersConfigPage() {
|
|
|
310
310
|
<PopoverContent className='w-40 p-1' align='end' onClick={(event) => event.stopPropagation()}>
|
|
311
311
|
<button
|
|
312
312
|
type='button'
|
|
313
|
-
className='flex w-full items-center gap-2 rounded-md px-2 py-2 text-sm text-red-600 hover:bg-
|
|
313
|
+
className='flex w-full items-center gap-2 rounded-md px-2 py-2 text-sm text-red-600 hover:bg-destructive/10'
|
|
314
314
|
onClick={() => setProviderToDelete(provider.providerId)}
|
|
315
315
|
>
|
|
316
316
|
<Trash2 className='h-4 w-4' />
|
|
@@ -60,9 +60,9 @@ function SideDockButton({
|
|
|
60
60
|
onClick={() => onOpen(item)}
|
|
61
61
|
className={cn(
|
|
62
62
|
'flex h-10 w-10 shrink-0 items-center justify-center rounded-lg text-muted-foreground transition-colors',
|
|
63
|
-
'hover:bg-
|
|
63
|
+
'hover:bg-[var(--interaction-hover)] hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border',
|
|
64
64
|
active
|
|
65
|
-
? 'bg-
|
|
65
|
+
? 'bg-[var(--interaction-selection)] text-foreground hover:bg-[var(--interaction-selection)]'
|
|
66
66
|
: 'bg-transparent',
|
|
67
67
|
)}
|
|
68
68
|
>
|
|
@@ -78,7 +78,7 @@ function SideDockButton({
|
|
|
78
78
|
event.stopPropagation();
|
|
79
79
|
onUnpin(item);
|
|
80
80
|
}}
|
|
81
|
-
className="absolute -right-1 -top-1 hidden h-4 w-4 items-center justify-center rounded-full bg-background text-muted-foreground shadow-sm ring-1 ring-border/70 transition-colors hover:bg-
|
|
81
|
+
className="absolute -right-1 -top-1 hidden h-4 w-4 items-center justify-center rounded-full bg-background text-muted-foreground shadow-sm ring-1 ring-border/70 transition-colors hover:bg-[var(--interaction-hover)] hover:text-foreground group-hover:flex focus-visible:flex focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
|
|
82
82
|
>
|
|
83
83
|
<X className="h-3 w-3" aria-hidden="true" />
|
|
84
84
|
</button>
|
|
@@ -109,7 +109,7 @@ function SideDockActionButton({
|
|
|
109
109
|
onClick={onClick}
|
|
110
110
|
className={cn(
|
|
111
111
|
'flex h-10 w-10 shrink-0 items-center justify-center rounded-lg text-muted-foreground transition-colors',
|
|
112
|
-
'hover:bg-
|
|
112
|
+
'hover:bg-[var(--interaction-hover)] hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border',
|
|
113
113
|
)}
|
|
114
114
|
>
|
|
115
115
|
<Icon className="h-5 w-5" aria-hidden="true" />
|
package/src/index.css
CHANGED
|
@@ -90,7 +90,7 @@ function DesktopWindowControlButton({
|
|
|
90
90
|
className={
|
|
91
91
|
control.variant === "danger"
|
|
92
92
|
? "desktop-window-no-drag flex h-10 w-[46px] items-center justify-center text-gray-700 transition-colors hover:bg-red-500 hover:text-white"
|
|
93
|
-
: "desktop-window-no-drag flex h-10 w-[46px] items-center justify-center text-gray-700 transition-colors hover:bg-
|
|
93
|
+
: "desktop-window-no-drag flex h-10 w-[46px] items-center justify-center text-gray-700 transition-colors hover:bg-[var(--interaction-hover)]"
|
|
94
94
|
}
|
|
95
95
|
onClick={() => {
|
|
96
96
|
void window.nextclawDesktop?.controlWindow?.(control.action);
|
|
@@ -42,7 +42,7 @@ export function MobileAppShell({
|
|
|
42
42
|
{isMainRoute ? (
|
|
43
43
|
<div className="h-full min-h-0 overflow-hidden">{children}</div>
|
|
44
44
|
) : (
|
|
45
|
-
<main className="h-full overflow-auto px-4 py-4 custom-scrollbar">
|
|
45
|
+
<main className="h-full overflow-auto overscroll-contain px-4 py-4 custom-scrollbar">
|
|
46
46
|
<div className="mx-auto max-w-3xl animate-fade-in">{children}</div>
|
|
47
47
|
</main>
|
|
48
48
|
)}
|
|
@@ -19,13 +19,13 @@ export function MobileBottomNav() {
|
|
|
19
19
|
return (
|
|
20
20
|
<nav
|
|
21
21
|
data-testid="mobile-bottom-nav"
|
|
22
|
-
aria-label={t("
|
|
22
|
+
aria-label={t("mobileNavigation")}
|
|
23
23
|
className="shrink-0 border-t border-border/60 bg-background text-foreground"
|
|
24
24
|
style={{
|
|
25
25
|
paddingBottom: "calc(env(safe-area-inset-bottom, 0px) + 0.25rem)",
|
|
26
26
|
}}
|
|
27
27
|
>
|
|
28
|
-
<ul className="grid grid-cols-6 gap-
|
|
28
|
+
<ul className="grid grid-cols-6 gap-0.5 px-1 pt-1">
|
|
29
29
|
{getMobileBottomNavItems(t).map((item) => {
|
|
30
30
|
const active = item.target === "/settings"
|
|
31
31
|
? isSettingsRoute(pathname)
|
|
@@ -34,19 +34,20 @@ export function MobileBottomNav() {
|
|
|
34
34
|
<li key={item.target}>
|
|
35
35
|
<Link
|
|
36
36
|
to={item.target}
|
|
37
|
+
aria-label={item.label}
|
|
37
38
|
aria-current={active ? "page" : undefined}
|
|
38
39
|
data-testid={active ? "mobile-nav-active-indicator" : undefined}
|
|
39
40
|
className={cn(
|
|
40
|
-
"group flex min-h-
|
|
41
|
+
"group flex min-h-12 flex-col items-center justify-center gap-1 rounded-xl px-0.5 py-1 text-[11px] font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border",
|
|
41
42
|
active
|
|
42
|
-
? "bg-
|
|
43
|
-
: "text-muted-foreground hover:bg-
|
|
43
|
+
? "bg-[var(--interaction-selection)] text-foreground"
|
|
44
|
+
: "text-muted-foreground hover:bg-[var(--interaction-hover)] hover:text-foreground",
|
|
44
45
|
)}
|
|
45
46
|
>
|
|
46
47
|
<span className="relative">
|
|
47
48
|
<item.icon
|
|
48
49
|
className={cn(
|
|
49
|
-
"h-
|
|
50
|
+
"h-5 w-5",
|
|
50
51
|
active ? "text-foreground" : "text-muted-foreground",
|
|
51
52
|
)}
|
|
52
53
|
/>
|
|
@@ -54,7 +55,7 @@ export function MobileBottomNav() {
|
|
|
54
55
|
<span className="absolute -right-1 -top-1 h-1.5 w-1.5 rounded-full bg-primary ring-1 ring-background" />
|
|
55
56
|
) : null}
|
|
56
57
|
</span>
|
|
57
|
-
<span className="max-w-full truncate">{item.label}</span>
|
|
58
|
+
<span className="max-w-full truncate">{item.target === "/agents" ? t("mobileAgents") : item.label}</span>
|
|
58
59
|
</Link>
|
|
59
60
|
</li>
|
|
60
61
|
);
|
|
@@ -63,9 +64,9 @@ export function MobileBottomNav() {
|
|
|
63
64
|
<button
|
|
64
65
|
type="button"
|
|
65
66
|
onClick={() => openApps(docBrowser)}
|
|
66
|
-
className="group flex min-h-
|
|
67
|
+
className="group flex min-h-12 w-full flex-col items-center justify-center gap-1 rounded-xl px-0.5 py-1 text-[11px] font-medium 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"
|
|
67
68
|
>
|
|
68
|
-
<Boxes className="h-
|
|
69
|
+
<Boxes className="h-5 w-5 text-muted-foreground" />
|
|
69
70
|
<span className="max-w-full truncate">{t("appsTitle")}</span>
|
|
70
71
|
</button>
|
|
71
72
|
</li>
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { NavLink } from "react-router-dom";
|
|
2
|
+
import { ChevronRight } from "lucide-react";
|
|
2
3
|
import { getSettingsNavSections } from "@/app/configs/app-navigation.config";
|
|
3
4
|
import { t } from "@/shared/lib/i18n";
|
|
4
5
|
import { cn } from "@/shared/lib/utils";
|
|
5
6
|
import { useDesktopCapabilityAvailability } from "@/features/desktop-capabilities";
|
|
7
|
+
import { SettingsGroup } from "@/shared/components/settings/setting-row";
|
|
6
8
|
|
|
7
9
|
export function MobileSettingsShell() {
|
|
8
10
|
const desktopCapabilityAvailable = useDesktopCapabilityAvailability();
|
|
@@ -19,29 +21,28 @@ export function MobileSettingsShell() {
|
|
|
19
21
|
{settingsNavSections.map((section) => (
|
|
20
22
|
<section key={section.label} className="space-y-2">
|
|
21
23
|
<h2 className="px-1 pt-2 text-xs font-medium text-muted-foreground">{section.label}</h2>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
))}
|
|
24
|
+
<SettingsGroup>
|
|
25
|
+
{section.items.map((item) => (
|
|
26
|
+
<NavLink
|
|
27
|
+
key={item.target}
|
|
28
|
+
to={item.target}
|
|
29
|
+
className={({ isActive }) => cn(
|
|
30
|
+
"flex min-h-14 items-center gap-3 px-4 py-3 transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-border",
|
|
31
|
+
isActive
|
|
32
|
+
? "bg-[var(--interaction-selection)]"
|
|
33
|
+
: "hover:bg-[var(--interaction-hover)] active:bg-[var(--interaction-hover)]",
|
|
34
|
+
)}
|
|
35
|
+
>
|
|
36
|
+
<div className="flex h-8 w-8 shrink-0 items-center justify-center text-muted-foreground">
|
|
37
|
+
<item.icon className="h-5 w-5" />
|
|
38
|
+
</div>
|
|
39
|
+
<div className="min-w-0 flex-1">
|
|
40
|
+
<p className="text-[15px] font-medium text-foreground">{item.label}</p>
|
|
41
|
+
</div>
|
|
42
|
+
<ChevronRight className="h-4 w-4 shrink-0 text-muted-foreground/60" aria-hidden="true" />
|
|
43
|
+
</NavLink>
|
|
44
|
+
))}
|
|
45
|
+
</SettingsGroup>
|
|
45
46
|
</section>
|
|
46
47
|
))}
|
|
47
48
|
</div>
|
|
@@ -2,6 +2,7 @@ import { ArrowLeft } from "lucide-react";
|
|
|
2
2
|
import { useNavigate, useLocation } from "react-router-dom";
|
|
3
3
|
import { resolveMobileRouteMeta } from "@/app/configs/app-navigation.config";
|
|
4
4
|
import { t } from "@/shared/lib/i18n";
|
|
5
|
+
import { IconActionButton } from "@/shared/components/ui/actions/icon-action-button";
|
|
5
6
|
|
|
6
7
|
export function MobileTopbar({ leadingInset }: { leadingInset?: string }) {
|
|
7
8
|
const navigate = useNavigate();
|
|
@@ -11,31 +12,29 @@ export function MobileTopbar({ leadingInset }: { leadingInset?: string }) {
|
|
|
11
12
|
return (
|
|
12
13
|
<header
|
|
13
14
|
data-testid="mobile-topbar"
|
|
14
|
-
className="shrink-0 border-b border-border/60 bg-
|
|
15
|
+
className="shrink-0 border-b border-border/60 bg-background text-foreground"
|
|
15
16
|
style={{
|
|
16
|
-
paddingTop: "
|
|
17
|
+
paddingTop: "env(safe-area-inset-top, 0px)",
|
|
17
18
|
}}
|
|
18
19
|
>
|
|
19
20
|
<div
|
|
20
|
-
className="flex
|
|
21
|
+
className="flex h-[var(--mobile-header-height)] items-center gap-2 pl-3 pr-3"
|
|
21
22
|
style={{ paddingLeft: leadingInset ? `calc(${leadingInset} + 0.75rem)` : undefined }}
|
|
22
23
|
>
|
|
23
24
|
{routeMeta.backTarget ? (
|
|
24
|
-
<
|
|
25
|
-
|
|
25
|
+
<IconActionButton
|
|
26
|
+
size="lg"
|
|
26
27
|
onClick={() => navigate(routeMeta.backTarget as string)}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<ArrowLeft className="h-4 w-4" />
|
|
31
|
-
</button>
|
|
28
|
+
label={routeMeta.backLabel ?? t("backToMain")}
|
|
29
|
+
icon={<ArrowLeft className="h-4 w-4" />}
|
|
30
|
+
/>
|
|
32
31
|
) : null}
|
|
33
32
|
<div className="min-w-0 flex-1">
|
|
34
33
|
<h1 className="truncate text-center text-[17px] font-semibold text-foreground">
|
|
35
34
|
{routeMeta.title}
|
|
36
35
|
</h1>
|
|
37
36
|
</div>
|
|
38
|
-
{routeMeta.backTarget ? <div className="
|
|
37
|
+
{routeMeta.backTarget ? <div className="w-[var(--icon-control-size)] shrink-0" aria-hidden="true" /> : null}
|
|
39
38
|
</div>
|
|
40
39
|
</header>
|
|
41
40
|
);
|
|
@@ -181,7 +181,7 @@ function RuntimeUpdateReleaseNotesHover({
|
|
|
181
181
|
external
|
|
182
182
|
icon={null}
|
|
183
183
|
aria-label={label}
|
|
184
|
-
className="whitespace-nowrap rounded-md border bg-popover px-3 py-1.5 text-left text-xs font-medium text-popover-foreground shadow-md outline-none transition-colors hover:bg-
|
|
184
|
+
className="whitespace-nowrap rounded-md border bg-popover px-3 py-1.5 text-left text-xs font-medium text-popover-foreground shadow-md outline-none transition-colors hover:bg-[var(--interaction-hover)] focus-visible:ring-2 focus-visible:ring-gray-300"
|
|
185
185
|
>
|
|
186
186
|
{label}
|
|
187
187
|
</NavigationLink>
|
|
@@ -152,7 +152,7 @@ export function SearchableModelInput({
|
|
|
152
152
|
onChange(value.trim());
|
|
153
153
|
closeMenu();
|
|
154
154
|
}}
|
|
155
|
-
className="flex w-full items-center gap-2 px-3 py-2 text-left text-sm hover:bg-
|
|
155
|
+
className="flex w-full items-center gap-2 px-3 py-2 text-left text-sm hover:bg-[var(--interaction-hover)]"
|
|
156
156
|
>
|
|
157
157
|
<Check className="h-4 w-4 text-transparent" />
|
|
158
158
|
<span className="truncate text-gray-700">
|
|
@@ -170,7 +170,7 @@ export function SearchableModelInput({
|
|
|
170
170
|
onChange(option);
|
|
171
171
|
closeMenu();
|
|
172
172
|
}}
|
|
173
|
-
className="flex w-full items-center gap-2 px-3 py-2 text-left text-sm hover:bg-
|
|
173
|
+
className="flex w-full items-center gap-2 px-3 py-2 text-left text-sm hover:bg-[var(--interaction-hover)]"
|
|
174
174
|
>
|
|
175
175
|
<Check className={cn('h-4 w-4', option === value.trim() ? 'text-primary' : 'text-transparent')} />
|
|
176
176
|
<span className="truncate text-gray-700">{option}</span>
|
|
@@ -47,7 +47,7 @@ export function ConfigSplitPage({
|
|
|
47
47
|
<button
|
|
48
48
|
type='button'
|
|
49
49
|
onClick={onMobileBack}
|
|
50
|
-
className='inline-flex items-center gap-2 rounded-lg px-2 py-1.5 text-sm font-medium text-muted-foreground transition-colors hover:bg-
|
|
50
|
+
className='inline-flex items-center gap-2 rounded-lg px-2 py-1.5 text-sm font-medium text-muted-foreground transition-colors hover:bg-[var(--interaction-hover)] hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40'
|
|
51
51
|
>
|
|
52
52
|
<ArrowLeft className='h-4 w-4' />
|
|
53
53
|
<span>{mobileListLabel ?? t('backToMain')}</span>
|
|
@@ -122,8 +122,8 @@ export function ConfigSelectionCard({
|
|
|
122
122
|
className={cn(
|
|
123
123
|
'w-full rounded-xl p-2.5 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40',
|
|
124
124
|
active
|
|
125
|
-
? 'bg-
|
|
126
|
-
: 'bg-transparent text-muted-foreground hover:bg-
|
|
125
|
+
? 'bg-[var(--interaction-selection)] text-foreground'
|
|
126
|
+
: 'bg-transparent text-muted-foreground hover:bg-[var(--interaction-hover)] hover:text-foreground',
|
|
127
127
|
className
|
|
128
128
|
)}
|
|
129
129
|
{...props}
|
|
@@ -545,7 +545,8 @@ describe("DocBrowser panel app navigation", () => {
|
|
|
545
545
|
|
|
546
546
|
expect(screen.queryByRole("button", { name: "Pin to left sidebar" })).toBeNull();
|
|
547
547
|
await user.click(screen.getByRole("button", { name: "More panel app actions" }));
|
|
548
|
-
await user.click(screen.getByRole("
|
|
548
|
+
await user.click(screen.getByRole("menuitem", { name: "Layout and position" }));
|
|
549
|
+
await user.click(screen.getByRole("menuitem", { name: "Pin to left sidebar" }));
|
|
549
550
|
expect(panelAppHooks.mutate).toHaveBeenCalledWith({
|
|
550
551
|
id: "piano",
|
|
551
552
|
preferences: { mainSidebar: true },
|
|
@@ -571,7 +572,8 @@ describe("DocBrowser panel app navigation", () => {
|
|
|
571
572
|
render(<DocBrowser customTabRenderers={PANEL_APPS_DOC_BROWSER_RENDERERS} />);
|
|
572
573
|
|
|
573
574
|
await user.click(screen.getByRole("button", { name: "More panel app actions" }));
|
|
574
|
-
await user.click(screen.getByRole("
|
|
575
|
+
await user.click(screen.getByRole("menuitem", { name: "Layout and position" }));
|
|
576
|
+
await user.click(screen.getByRole("menuitem", { name: "Pin to left sidebar" }));
|
|
575
577
|
expect(panelAppHooks.mutate).toHaveBeenCalledWith({
|
|
576
578
|
id: "piano",
|
|
577
579
|
preferences: { mainSidebar: true },
|
|
@@ -651,6 +653,36 @@ describe("DocBrowser floating interactions", () => {
|
|
|
651
653
|
installDocBrowserPointerTestEnvironment();
|
|
652
654
|
});
|
|
653
655
|
|
|
656
|
+
it("keeps compact navigation, tab selection, resource and app actions reachable", () => {
|
|
657
|
+
const onBack = vi.fn();
|
|
658
|
+
const onSelect = vi.fn();
|
|
659
|
+
const onClose = vi.fn();
|
|
660
|
+
const onAdd = vi.fn();
|
|
661
|
+
const onRefresh = vi.fn();
|
|
662
|
+
render(<DocBrowserTabStrip compact tabs={docBrowserState.tabs} activeTabId="docs"
|
|
663
|
+
canGoBack canGoForward={false} onGoBack={onBack} onGoForward={vi.fn()}
|
|
664
|
+
onOpenNewTab={vi.fn()} onSetActiveTab={onSelect} onCloseTab={onClose}
|
|
665
|
+
mobileToolbar={<button onClick={onRefresh}>Refresh app</button>}
|
|
666
|
+
getTabMenuGroups={() => [{ key: 'chat', items: [{ key: 'add', label: 'Add to Chat', onSelect: onAdd }] }]}
|
|
667
|
+
/>);
|
|
668
|
+
const open = () => fireEvent.click(screen.getByRole('button', { name: 'More tab actions' }));
|
|
669
|
+
expect(screen.queryByRole('button', { name: 'Back' })).toBeNull();
|
|
670
|
+
open();
|
|
671
|
+
fireEvent.click(screen.getByRole('menuitem', { name: 'Back' }));
|
|
672
|
+
expect(onBack).toHaveBeenCalledOnce();
|
|
673
|
+
open();
|
|
674
|
+
fireEvent.click(screen.getByRole('menuitem', { name: 'Docs' }));
|
|
675
|
+
expect(onSelect).toHaveBeenCalledWith('docs');
|
|
676
|
+
open();
|
|
677
|
+
fireEvent.click(screen.getByRole('menuitem', { name: 'Add to Chat' }));
|
|
678
|
+
expect(onAdd).toHaveBeenCalledOnce();
|
|
679
|
+
open();
|
|
680
|
+
fireEvent.click(screen.getByRole('button', { name: 'Refresh app' }));
|
|
681
|
+
expect(onRefresh).toHaveBeenCalledOnce();
|
|
682
|
+
fireEvent.click(screen.getByRole('menuitem', { name: 'Close tab' }));
|
|
683
|
+
expect(onClose).toHaveBeenCalledWith('docs');
|
|
684
|
+
});
|
|
685
|
+
|
|
654
686
|
it("drags the shared header without stealing tab actions", () => {
|
|
655
687
|
new WorkbenchSurfaceManager().place('global-resources', 'floating');
|
|
656
688
|
render(<DocBrowser />);
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { PageResourceIcon } from '@/features/right-panel-resources';
|
|
2
|
+
import { useState, type ReactNode } from 'react';
|
|
3
|
+
import { ChevronDown } from 'lucide-react';
|
|
4
|
+
import { Popover, PopoverContent, PopoverTrigger } from '@/shared/components/ui/popover';
|
|
5
|
+
import { ContextMenuItems } from '@/shared/components/ui/context-menu/context-menu';
|
|
2
6
|
import {
|
|
3
7
|
ArrowLeft,
|
|
4
8
|
ArrowRight,
|
|
@@ -16,6 +20,8 @@ import {
|
|
|
16
20
|
import { t } from "@/shared/lib/i18n";
|
|
17
21
|
|
|
18
22
|
type DocBrowserTabStripProps = {
|
|
23
|
+
compact?: boolean;
|
|
24
|
+
mobileToolbar?: ReactNode;
|
|
19
25
|
tabs: DocBrowserTab[];
|
|
20
26
|
activeTabId: string;
|
|
21
27
|
canGoBack: boolean;
|
|
@@ -47,7 +53,10 @@ export function DocBrowserTabStrip({
|
|
|
47
53
|
onSetActiveTab,
|
|
48
54
|
onCloseTab,
|
|
49
55
|
getTabMenuGroups,
|
|
56
|
+
mobileToolbar,
|
|
57
|
+
compact = false,
|
|
50
58
|
}: DocBrowserTabStripProps) {
|
|
59
|
+
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
51
60
|
const backLabel = t("docBrowserBack");
|
|
52
61
|
const closeTabLabel = t("workbenchCloseTab");
|
|
53
62
|
const dockLabel = dockState?.isDocked
|
|
@@ -87,20 +96,42 @@ export function DocBrowserTabStrip({
|
|
|
87
96
|
: []),
|
|
88
97
|
];
|
|
89
98
|
|
|
99
|
+
const current = tabs.find(tab => tab.id === activeTabId);
|
|
100
|
+
const mobileGroups: ContextMenuGroup[] = [
|
|
101
|
+
{ key: 'tabs', items: compactTabs.map(tab => ({ key: tab.key, label: tab.label, icon: tab.leadingIcon, pressed: tab.active, onSelect: tab.onSelect })) },
|
|
102
|
+
{ key: 'navigation', items: actions.map(action => ({ key: action.key, label: action.label, icon: action.icon, disabled: action.disabled, onSelect: action.onClick })) },
|
|
103
|
+
...(current ? getTabMenuGroups?.(current) ?? [] : []),
|
|
104
|
+
...(current ? [{ key: 'close-tab', items: [{ key: 'close-tab', label: closeTabLabel, onSelect: () => onCloseTab(current.id) }] }] : []),
|
|
105
|
+
];
|
|
106
|
+
if (compact) return (
|
|
107
|
+
<div className="flex h-12 min-w-0 flex-1 items-center">
|
|
108
|
+
<Popover open={mobileMenuOpen} onOpenChange={setMobileMenuOpen}>
|
|
109
|
+
<PopoverTrigger asChild>
|
|
110
|
+
<button type="button" className="flex h-8 min-w-0 items-center gap-2 rounded-lg px-2 text-sm font-medium hover:bg-[var(--interaction-hover)]" aria-label={t('docBrowserTabMoreActions')}>
|
|
111
|
+
{current ? <DocBrowserTabIcon tab={current} /> : null}
|
|
112
|
+
<span className="truncate">{current?.title || t('docBrowserTabUntitled')}</span><ChevronDown className="h-4 w-4 shrink-0" />
|
|
113
|
+
</button>
|
|
114
|
+
</PopoverTrigger>
|
|
115
|
+
<PopoverContent align="start" className="max-h-[70dvh] w-72 overflow-y-auto p-1">
|
|
116
|
+
<ContextMenuItems groups={mobileGroups} onClose={() => setMobileMenuOpen(false)} />
|
|
117
|
+
{mobileToolbar}
|
|
118
|
+
</PopoverContent>
|
|
119
|
+
</Popover>
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
90
122
|
return (
|
|
91
123
|
<CompactTabStrip
|
|
92
124
|
testId="doc-browser-tab-strip"
|
|
93
125
|
actionsTestId="doc-browser-tab-actions"
|
|
94
126
|
tabs={compactTabs}
|
|
95
127
|
actions={actions}
|
|
96
|
-
className="h-10 min-w-0 flex-1 gap-1 border-0 bg-transparent px-0 select-none"
|
|
97
|
-
scrollClassName="doc-browser-tab-scrollbar flex h-full items-center gap-0.5"
|
|
128
|
+
className="flex h-10 min-w-0 flex-1 gap-1 border-0 bg-transparent px-0 select-none"
|
|
129
|
+
scrollClassName="doc-browser-tab-scrollbar flex h-full max-md:h-11 max-md:basis-full items-center gap-0.5"
|
|
98
130
|
tabsClassName="items-center gap-0.5"
|
|
99
|
-
actionsClassName="h-full items-center gap-0.5"
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
inactiveTabClassName="text-muted-foreground hover:bg-muted/45 hover:text-foreground"
|
|
131
|
+
actionsClassName="h-full max-md:h-11 max-md:w-full items-center gap-0.5"
|
|
132
|
+
tabBaseClassName="inline-flex cursor-pointer items-center gap-0.5 h-7 max-md:min-h-11 px-2 rounded-md text-xs max-w-[220px] shrink-0 transition-colors"
|
|
133
|
+
activeTabClassName="bg-[var(--interaction-selection)] text-foreground"
|
|
134
|
+
inactiveTabClassName="text-muted-foreground hover:bg-[var(--interaction-hover)] hover:text-foreground"
|
|
104
135
|
labelClassName="px-0.5 text-xs font-normal"
|
|
105
136
|
|
|
106
137
|
/>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
2
|
+
import { useViewportLayout } from '@/app/hooks/use-viewport-layout';
|
|
2
3
|
import {
|
|
3
4
|
DOC_BROWSER_HOME_TAB_KIND,
|
|
4
5
|
getDefaultDocsUrl,
|
|
@@ -258,6 +259,7 @@ export function DocBrowser({
|
|
|
258
259
|
|
|
259
260
|
|
|
260
261
|
const isFullscreen = displayMode === 'fullscreen';
|
|
262
|
+
const { isMobile } = useViewportLayout();
|
|
261
263
|
const customRenderParams = currentTab ? {
|
|
262
264
|
currentUrl,
|
|
263
265
|
open,
|
|
@@ -284,7 +286,7 @@ export function DocBrowser({
|
|
|
284
286
|
urlInput={urlInput}
|
|
285
287
|
/>
|
|
286
288
|
|
|
287
|
-
{customToolbar}
|
|
289
|
+
{!isMobile ? customToolbar : null}
|
|
288
290
|
|
|
289
291
|
<DocBrowserFrameContent
|
|
290
292
|
currentTab={currentTab}
|
|
@@ -310,6 +312,8 @@ export function DocBrowser({
|
|
|
310
312
|
onWidthCommit={setDockedWidth} onClose={close} closeLabel={t('workbenchHideGroup')}
|
|
311
313
|
hidden={!isOpen} fullscreen={isFullscreen} navigation={
|
|
312
314
|
<DocBrowserTabStrip
|
|
315
|
+
mobileToolbar={customToolbar}
|
|
316
|
+
compact={isMobile}
|
|
313
317
|
tabs={tabs}
|
|
314
318
|
activeTabId={activeTabId}
|
|
315
319
|
canGoBack={canGoBack}
|
|
@@ -112,7 +112,7 @@ export function ServerPathPickerLocations({
|
|
|
112
112
|
type="button"
|
|
113
113
|
className={cn(
|
|
114
114
|
'flex w-full items-center gap-2 rounded-md px-2 py-2 text-left text-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border',
|
|
115
|
-
active ? 'bg-
|
|
115
|
+
active ? 'bg-[var(--interaction-selection)] text-foreground' : 'text-foreground hover:bg-[var(--interaction-hover)]',
|
|
116
116
|
)}
|
|
117
117
|
aria-current={active ? 'location' : undefined}
|
|
118
118
|
onClick={() => onNavigate(item.path)}
|
|
@@ -27,7 +27,7 @@ export function SettingsPage({
|
|
|
27
27
|
className
|
|
28
28
|
)}
|
|
29
29
|
>
|
|
30
|
-
<PageHeader title={title} description={description} actions={actions} />
|
|
30
|
+
<PageHeader title={title} description={description} actions={actions} mobileTitleInShell />
|
|
31
31
|
{children}
|
|
32
32
|
</PageLayout>
|
|
33
33
|
);
|
|
@@ -17,7 +17,7 @@ describe('IconActionButton', () => {
|
|
|
17
17
|
expect(button.className).toContain('h-6');
|
|
18
18
|
expect(button.className).toContain('w-6');
|
|
19
19
|
expect(button.className).toContain('p-1');
|
|
20
|
-
expect(button.className).toContain('hover:bg-
|
|
20
|
+
expect(button.className).toContain('hover:before:bg-[var(--interaction-hover)]');
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
it('defaults to medium size and soft accent hover', () => {
|
|
@@ -31,7 +31,7 @@ describe('IconActionButton', () => {
|
|
|
31
31
|
const button = screen.getByRole('button', { name: 'More' });
|
|
32
32
|
expect(button.className).toContain('h-7');
|
|
33
33
|
expect(button.className).toContain('w-7');
|
|
34
|
-
expect(button.className).toContain('hover:bg-[var(--interaction-hover)]');
|
|
34
|
+
expect(button.className).toContain('hover:before:bg-[var(--interaction-hover)]');
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
it('uses surface-relative feedback without introducing a new surface', () => {
|
|
@@ -44,9 +44,9 @@ describe('IconActionButton', () => {
|
|
|
44
44
|
);
|
|
45
45
|
|
|
46
46
|
const button = screen.getByRole('button', { name: 'Add project' });
|
|
47
|
-
expect(button.className).toContain('hover:bg-
|
|
48
|
-
expect(button.className).toContain('hover:text-
|
|
49
|
-
expect(button.className).toContain('active:bg-
|
|
47
|
+
expect(button.className).toContain('hover:before:bg-[var(--interaction-hover)]');
|
|
48
|
+
expect(button.className).toContain('hover:text-accent-foreground');
|
|
49
|
+
expect(button.className).toContain('active:before:bg-[var(--interaction-hover)]');
|
|
50
50
|
});
|
|
51
51
|
|
|
52
52
|
it('ignores restored pointer focus while preserving keyboard tooltip focus', () => {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Neutral actions share one theme color across icon and menu surfaces. */
|
|
2
|
+
export const ACTION_MENU_ITEM_CLASS = 'flex min-h-8 w-full items-center gap-2 rounded-lg px-2.5 py-1 text-left text-[13px] leading-5 font-medium outline-none transition-colors disabled:pointer-events-none disabled:opacity-45';
|
|
3
|
+
|
|
4
|
+
export const ACTION_FEEDBACK = {
|
|
5
|
+
icon: 'hover:before:bg-[var(--interaction-hover)] active:before:bg-[var(--interaction-hover)] disabled:hover:before:bg-transparent disabled:active:before:bg-transparent',
|
|
6
|
+
item: 'hover:bg-[var(--interaction-hover)] focus-visible:bg-[var(--interaction-hover)] active:bg-[var(--interaction-hover)] disabled:hover:bg-transparent disabled:active:bg-transparent',
|
|
7
|
+
destructive: 'text-destructive hover:bg-destructive/10 focus-visible:bg-destructive/10 disabled:hover:bg-transparent',
|
|
8
|
+
} as const;
|