@nextclaw/ui 0.14.4 → 0.15.0
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 +54 -0
- package/dist/assets/api-DLtsiBKm.js +13 -0
- package/dist/assets/appearance-settings-page-CtkeYXux.js +1 -0
- package/dist/assets/book-open-BQwWBDKb.js +1 -0
- package/dist/assets/channels-list-page-Cv8o9Hxg.js +8 -0
- package/dist/assets/chat-page-BtA7H1Ea.js +1 -0
- package/dist/assets/{config-split-page-CRhlZ_pH.js → config-split-page-EidRBpP2.js} +1 -1
- package/dist/assets/confirm-dialog-iIJFFp2T.js +5 -0
- package/dist/assets/desktop-update-config-C6n_TB1r.js +1 -0
- package/dist/assets/{dist-CikkhOlq.js → dist-CBxWOazz.js} +1 -1
- package/dist/assets/{dist-PVA_xL1D.js → dist-D2eZC3OR.js} +1 -1
- package/dist/assets/doc-browser-B76XOXmW.js +1 -0
- package/dist/assets/doc-browser-DBnj9UMB.js +1 -0
- package/dist/assets/{doc-browser-context-MTw4EvKd.js → doc-browser-context-Sv-044Cl.js} +1 -1
- package/dist/assets/{ellipsis-y6vDtq4M.js → ellipsis-2uzhypw5.js} +1 -1
- package/dist/assets/es2015-DhQo_oS6.js +41 -0
- package/dist/assets/{external-link-DVJIQf_G.js → external-link-CM5dnUI6.js} +1 -1
- package/dist/assets/host-capabilities-9bB_998Z.js +1 -0
- package/dist/assets/i18n-provider-di1waTGX.js +1 -0
- package/dist/assets/index-CBRP-7-a.css +1 -0
- package/dist/assets/index-Z1fYpbNd.js +104 -0
- package/dist/assets/loader-circle-BjmN5DeJ.js +1 -0
- package/dist/assets/mcp-marketplace-page-C_MVQUbT.js +9 -0
- package/dist/assets/mcp-marketplace-page-CoZIiD6G.js +1 -0
- package/dist/assets/model-config-page-Cdr9P6TQ.js +1 -0
- package/dist/assets/plus-3ztRZD_l.js +1 -0
- package/dist/assets/provider-scoped-model-input-g1WkGiJ8.js +1 -0
- package/dist/assets/providers-config-page-CWlO7J4A.js +1 -0
- package/dist/assets/{react-VNqT5Zp7.js → react-DjpzoZXB.js} +1 -1
- package/dist/assets/refresh-cw-DaTv3zxE.js +1 -0
- package/dist/assets/remote-CKWZfVVB.js +1 -0
- package/dist/assets/rotate-cw-CHcaRUDz.js +1 -0
- package/dist/assets/runtime-config-page-CfTqh1nv.js +1 -0
- package/dist/assets/{save-BOJ477H-.js → save-BZof1uie.js} +1 -1
- package/dist/assets/{search-BEti5beA.js → search-MxAarS_g.js} +1 -1
- package/dist/assets/search-config-page-Dm8daYny.js +1 -0
- package/dist/assets/{secrets-config-page--IzrIOid.js → secrets-config-page-D8AbcR05.js} +2 -2
- package/dist/assets/security-config-Ds0LsJmo.js +1 -0
- package/dist/assets/select-D7GcNP0z.js +1 -0
- package/dist/assets/setting-row-CuozlnJd.js +1 -0
- package/dist/assets/side-dock-BcLau5S_.js +3 -0
- package/dist/assets/skeleton-shfmXuYp.js +1 -0
- package/dist/assets/switch-C1BWMU5c.js +1 -0
- package/dist/assets/tag-chip-DCUrQUCP.js +1 -0
- package/dist/assets/tooltip-DfAHHA76.js +1 -0
- package/dist/assets/use-config-DAdbT5QJ.js +1 -0
- package/dist/assets/use-confirm-dialog-eWcJ6V-s.js +1 -0
- package/dist/assets/x-DqL1mYLC.js +1 -0
- package/dist/index.html +53 -17
- package/index.html +25 -2
- package/package.json +9 -9
- package/src/app/components/layout/__tests__/app-layout.test.tsx +27 -0
- package/src/app/components/layout/__tests__/settings-entry-page.test.tsx +9 -1
- package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +6 -3
- package/src/app/components/layout/app-layout.tsx +10 -3
- package/src/app/components/layout/sidebar-items.tsx +8 -8
- package/src/app/components/layout/sidebar-rail.styles.ts +4 -2
- package/src/app/components/layout/sidebar.tsx +21 -28
- package/src/app/configs/app-navigation.config.ts +11 -5
- package/src/app/index.tsx +8 -0
- package/src/app/managers/__tests__/viewport-layout.manager.test.ts +59 -0
- package/src/app/managers/viewport-layout.manager.ts +39 -0
- package/src/app/presenters/app.presenter.ts +25 -2
- package/src/app/styles/design-system.css +91 -4
- package/src/features/chat/components/conversation/__tests__/chat-conversation-content.test.tsx +86 -0
- package/src/features/chat/components/conversation/chat-conversation-content.tsx +47 -17
- package/src/features/chat/components/layout/__tests__/chat-sidebar-read-state.test.tsx +7 -0
- package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +9 -5
- package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +5 -5
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +51 -2
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +170 -1
- package/src/features/chat/features/conversation/components/session-conversation-area.tsx +23 -1
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +24 -7
- package/src/features/chat/features/conversation/components/session-queued-input-rows.tsx +96 -0
- package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +181 -0
- package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +351 -89
- package/src/features/chat/features/input/hooks/__tests__/use-chat-input-surface-state.test.tsx +29 -4
- package/src/features/chat/features/input/hooks/use-chat-input-surface-state.ts +28 -3
- package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +108 -8
- package/src/features/chat/features/input/input-surface-plugins/panel-app-reference-plugin.utils.ts +50 -14
- package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +77 -6
- package/src/features/chat/features/message/components/__tests__/chat-inline-panel-app-card.test.tsx +21 -12
- package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +166 -0
- package/src/features/chat/features/message/components/chat-inline-panel-app-card.tsx +19 -11
- package/src/features/chat/features/message/components/chat-message-list.container.tsx +38 -64
- package/src/features/chat/features/message/utils/__tests__/chat-message-process-summary.utils.test.ts +59 -0
- package/src/features/chat/features/message/utils/__tests__/chat-message-show-content-tool-card.utils.test.ts +56 -1
- package/src/features/chat/features/message/utils/chat-inline-panel-app-card.utils.ts +0 -2
- package/src/features/chat/features/message/utils/chat-message-process-summary.utils.ts +80 -0
- package/src/features/chat/features/message/utils/chat-message-show-content-tool-card.utils.ts +12 -3
- package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +60 -0
- package/src/features/chat/features/message/utils/chat-message.utils.ts +3 -0
- package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-agent-runtime.test.tsx +39 -0
- package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +2 -2
- package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +1 -1
- package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +3 -1
- package/src/features/chat/features/session/components/session-header/chat-session-metadata-dialog.tsx +1 -1
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +270 -115
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumb-browser.tsx +121 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +75 -20
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +70 -5
- package/src/features/chat/features/workspace/utils/chat-workspace-file-tab-persistence.utils.ts +83 -0
- package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +79 -1
- package/src/features/chat/managers/__tests__/chat-ui.manager.test.ts +2 -0
- package/src/features/chat/managers/chat-thread.manager.ts +9 -3
- package/src/features/chat/managers/chat-ui.manager.ts +5 -1
- package/src/features/chat/presenters/chat.presenter.ts +2 -1
- package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +4 -0
- package/src/features/chat/stores/chat-thread.store.ts +6 -64
- package/src/features/chat/types/chat-message.types.ts +4 -0
- package/src/features/marketplace/components/__tests__/marketplace-page-detail.test.tsx +33 -26
- package/src/features/marketplace/components/__tests__/marketplace-page.test.tsx +1 -0
- package/src/features/marketplace/components/curated-shelves/__tests__/marketplace-curated-scene-route.test.tsx +1 -0
- package/src/features/marketplace/components/detail-doc/__tests__/marketplace-detail-doc.test.tsx +115 -0
- package/src/features/marketplace/components/marketplace-detail-doc.tsx +441 -0
- package/src/features/marketplace/components/mcp/__tests__/mcp-marketplace-page.test.tsx +3 -3
- package/src/features/marketplace/components/mcp/mcp-marketplace-doc.ts +0 -60
- package/src/features/marketplace/components/mcp/mcp-marketplace-page.tsx +64 -29
- package/src/features/marketplace/hooks/use-marketplace-item-detail.ts +42 -31
- package/src/features/marketplace/index.ts +3 -0
- package/src/features/marketplace/stores/marketplace-detail-doc.store.ts +38 -0
- package/src/features/panel-apps/components/__tests__/panel-apps-list.test.tsx +32 -2
- package/src/features/pwa/managers/__tests__/pwa-shell-theme.manager.test.ts +11 -0
- package/src/features/pwa/managers/pwa-shell-theme.manager.ts +8 -3
- package/src/features/settings/pages/__tests__/appearance-settings-page.test.tsx +25 -0
- package/src/features/settings/pages/appearance-settings-page.tsx +40 -0
- package/src/features/side-dock/components/__tests__/side-dock.test.tsx +44 -2
- package/src/features/side-dock/components/side-dock.tsx +100 -13
- package/src/features/side-dock/configs/side-dock-built-in-items.config.ts +10 -0
- package/src/features/side-dock/managers/__tests__/side-dock.manager.test.ts +32 -2
- package/src/features/side-dock/managers/side-dock.manager.ts +17 -7
- package/src/features/side-dock/stores/side-dock.store.ts +9 -0
- package/src/features/side-dock/types/side-dock.types.ts +10 -3
- package/src/features/side-dock/utils/side-dock-item.utils.ts +15 -2
- package/src/index.css +501 -27
- package/src/platforms/desktop/components/desktop-window-chrome.tsx +1 -1
- package/src/shared/components/doc-browser/__tests__/doc-browser-context.test.tsx +30 -1
- package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +206 -38
- package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +34 -11
- package/src/shared/components/doc-browser/doc-browser-renderer.types.ts +1 -1
- package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +23 -5
- package/src/shared/components/doc-browser/doc-browser.tsx +128 -75
- package/src/shared/components/doc-browser/managers/doc-browser.manager.ts +9 -1
- package/src/shared/components/doc-browser/utils/doc-browser-floating-panel.utils.ts +83 -0
- package/src/shared/components/ui/__tests__/dialog.test.tsx +27 -0
- package/src/shared/components/ui/actions/icon-action-button.tsx +14 -2
- package/src/shared/components/ui/dialog.tsx +2 -2
- package/src/shared/components/ui/popover.tsx +1 -1
- package/src/shared/components/ui/select.tsx +1 -1
- package/src/shared/components/ui/switch.tsx +4 -3
- package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +54 -0
- package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +4 -5
- package/src/shared/components/ui/tooltip.tsx +1 -1
- package/src/shared/lib/api/utils/server-path.utils.ts +27 -1
- package/src/shared/lib/i18n/locales/en-US/chat.json +15 -6
- package/src/shared/lib/i18n/locales/en-US/core.json +2 -0
- package/src/shared/lib/i18n/locales/en-US/doc-browser.json +11 -0
- package/src/shared/lib/i18n/locales/en-US/marketplace.json +5 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +15 -6
- package/src/shared/lib/i18n/locales/zh-CN/core.json +2 -0
- package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +11 -0
- package/src/shared/lib/i18n/locales/zh-CN/marketplace.json +5 -0
- package/src/shared/lib/session-project/__tests__/workspace-file-breadcrumb.test.ts +14 -0
- package/src/shared/lib/session-project/index.ts +1 -1
- package/src/shared/lib/session-project/{workspace-file-breadcrumb.ts → workspace-file-breadcrumb.utils.ts} +62 -8
- package/src/shared/lib/theme/index.ts +51 -13
- package/dist/assets/api-BP_8IaxB.js +0 -13
- package/dist/assets/channels-list-page-DEh5Jjqx.js +0 -8
- package/dist/assets/chat-page-BRB0N5WW.js +0 -105
- package/dist/assets/confirm-dialog-DsivFJ4r.js +0 -5
- package/dist/assets/desktop-update-config-B0w9p2ep.js +0 -1
- package/dist/assets/doc-browser-BoNJ7-tE.js +0 -1
- package/dist/assets/doc-browser-CdJ_d4ik.js +0 -1
- package/dist/assets/index-BiE-K8-q.css +0 -1
- package/dist/assets/index-D9BAE_L_.js +0 -105
- package/dist/assets/mcp-marketplace-page-B1SZNY5m.js +0 -40
- package/dist/assets/mcp-marketplace-page-Cq_Tw42T.js +0 -1
- package/dist/assets/model-config-page-DotaTUHb.js +0 -1
- package/dist/assets/plus-BbZqquJe.js +0 -1
- package/dist/assets/provider-scoped-model-input-DsFRuWJH.js +0 -1
- package/dist/assets/providers-config-page-DW4tQSLN.js +0 -1
- package/dist/assets/remote-B8TiUfwW.js +0 -1
- package/dist/assets/rotate-cw-DjHDTkCv.js +0 -1
- package/dist/assets/runtime-config-page-S6ErRHR8.js +0 -1
- package/dist/assets/search-config-page-DGj5am03.js +0 -1
- package/dist/assets/select-hoj8dK4K.js +0 -41
- package/dist/assets/tag-chip-T8-h-die.js +0 -1
- package/dist/assets/use-config-CjRV2hwL.js +0 -1
- package/dist/assets/x-D9PXs_xP.js +0 -1
- package/src/features/marketplace/components/detail-doc/__tests__/marketplace-detail-doc.test.ts +0 -40
- package/src/features/marketplace/components/detail-doc/marketplace-detail-doc-renderer.ts +0 -201
- package/src/features/marketplace/components/marketplace-detail-doc.ts +0 -142
- /package/dist/assets/{config-hints-BkYBc7z0.js → config-hints-fGnUjDe9.js} +0 -0
- /package/dist/assets/{middleware-CkFT3R4v.js → middleware-BILWbJE4.js} +0 -0
package/index.html
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<meta name="mobile-web-app-capable" content="yes" />
|
|
13
13
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
14
14
|
<meta name="theme-color" content="#FAF9F7" />
|
|
15
|
-
<meta name="color-scheme" content="light" />
|
|
15
|
+
<meta name="color-scheme" content="light dark" />
|
|
16
16
|
<style>
|
|
17
17
|
html,
|
|
18
18
|
body,
|
|
@@ -50,6 +50,12 @@
|
|
|
50
50
|
background: #f8f8f7;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
html[data-theme="night"],
|
|
54
|
+
html[data-theme="night"] body,
|
|
55
|
+
html[data-theme="night"] #root {
|
|
56
|
+
background: #101318;
|
|
57
|
+
}
|
|
58
|
+
|
|
53
59
|
html[data-theme="probe"],
|
|
54
60
|
html[data-theme="probe"] body,
|
|
55
61
|
html[data-theme="probe"] #root {
|
|
@@ -67,8 +73,19 @@
|
|
|
67
73
|
cool: '#F8FAFC',
|
|
68
74
|
dawn: '#FAF7F4',
|
|
69
75
|
graphite: '#F8F8F7',
|
|
76
|
+
night: '#101318',
|
|
70
77
|
probe: '#FEFCF6'
|
|
71
78
|
};
|
|
79
|
+
const themeAppearances = {
|
|
80
|
+
natural: 'light',
|
|
81
|
+
minimal: 'light',
|
|
82
|
+
warm: 'light',
|
|
83
|
+
cool: 'light',
|
|
84
|
+
dawn: 'light',
|
|
85
|
+
graphite: 'light',
|
|
86
|
+
night: 'dark',
|
|
87
|
+
probe: 'light'
|
|
88
|
+
};
|
|
72
89
|
const legacyThemeAliases = {
|
|
73
90
|
leaf: 'warm'
|
|
74
91
|
};
|
|
@@ -82,13 +99,19 @@
|
|
|
82
99
|
window.localStorage.setItem(storageKey, nextTheme);
|
|
83
100
|
}
|
|
84
101
|
const themeColor = themeColors[nextTheme];
|
|
102
|
+
const themeAppearance = themeAppearances[nextTheme];
|
|
85
103
|
document.documentElement.setAttribute('data-theme', nextTheme);
|
|
104
|
+
document.documentElement.setAttribute('data-theme-appearance', themeAppearance);
|
|
86
105
|
document.documentElement.style.backgroundColor = themeColor;
|
|
87
|
-
document.documentElement.style.colorScheme =
|
|
106
|
+
document.documentElement.style.colorScheme = themeAppearance;
|
|
88
107
|
const themeMeta = document.querySelector('meta[name="theme-color"]');
|
|
89
108
|
if (themeMeta) {
|
|
90
109
|
themeMeta.setAttribute('content', themeColor);
|
|
91
110
|
}
|
|
111
|
+
const colorSchemeMeta = document.querySelector('meta[name="color-scheme"]');
|
|
112
|
+
if (colorSchemeMeta) {
|
|
113
|
+
colorSchemeMeta.setAttribute('content', themeAppearance);
|
|
114
|
+
}
|
|
92
115
|
})();
|
|
93
116
|
</script>
|
|
94
117
|
<script>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,14 +28,14 @@
|
|
|
28
28
|
"tailwind-merge": "^2.5.4",
|
|
29
29
|
"zod": "^3.23.8",
|
|
30
30
|
"zustand": "^5.0.2",
|
|
31
|
-
"@nextclaw/agent-chat-ui": "0.
|
|
32
|
-
"@nextclaw/agent-chat": "0.
|
|
33
|
-
"@nextclaw/
|
|
34
|
-
"@nextclaw/
|
|
35
|
-
"@nextclaw/ncp-
|
|
36
|
-
"@nextclaw/ncp-
|
|
37
|
-
"@nextclaw/shared": "0.
|
|
38
|
-
"@nextclaw/ncp-
|
|
31
|
+
"@nextclaw/agent-chat-ui": "0.6.0",
|
|
32
|
+
"@nextclaw/agent-chat": "0.3.0",
|
|
33
|
+
"@nextclaw/client-sdk": "0.5.0",
|
|
34
|
+
"@nextclaw/ncp": "0.7.0",
|
|
35
|
+
"@nextclaw/ncp-toolkit": "0.6.0",
|
|
36
|
+
"@nextclaw/ncp-react": "0.5.0",
|
|
37
|
+
"@nextclaw/shared": "0.4.0",
|
|
38
|
+
"@nextclaw/ncp-http-agent-client": "0.4.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@testing-library/react": "^16.3.0",
|
|
@@ -6,6 +6,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
6
6
|
import { AppLayout } from "@/app/components/layout/app-layout";
|
|
7
7
|
import { I18nProvider } from "@/app/components/i18n-provider";
|
|
8
8
|
import { viewportLayoutManager } from "@/app/managers/viewport-layout.manager";
|
|
9
|
+
import { useSideDockStore } from "@/features/side-dock";
|
|
9
10
|
|
|
10
11
|
const { useViewportLayoutMock } = vi.hoisted(() => ({
|
|
11
12
|
useViewportLayoutMock: vi.fn(() => ({
|
|
@@ -53,6 +54,8 @@ describe("AppLayout", () => {
|
|
|
53
54
|
isDesktop: true,
|
|
54
55
|
});
|
|
55
56
|
window.nextclawDesktop = undefined;
|
|
57
|
+
useSideDockStore.getState().setVisible(true);
|
|
58
|
+
useSideDockStore.getState().setPinnedItems([]);
|
|
56
59
|
});
|
|
57
60
|
|
|
58
61
|
it("treats /agents as a main workspace route instead of the settings shell", () => {
|
|
@@ -79,6 +82,30 @@ describe("AppLayout", () => {
|
|
|
79
82
|
expect(screen.getByTestId("side-dock")).toBeTruthy();
|
|
80
83
|
});
|
|
81
84
|
|
|
85
|
+
it("hides the side dock when the persisted visibility preference is off", () => {
|
|
86
|
+
useSideDockStore.getState().setVisible(false);
|
|
87
|
+
|
|
88
|
+
render(
|
|
89
|
+
<I18nProvider>
|
|
90
|
+
<MemoryRouter initialEntries={["/chat"]}>
|
|
91
|
+
<Routes>
|
|
92
|
+
<Route
|
|
93
|
+
path="*"
|
|
94
|
+
element={
|
|
95
|
+
<AppLayout>
|
|
96
|
+
<div data-testid="chat-content">Chat Content</div>
|
|
97
|
+
</AppLayout>
|
|
98
|
+
}
|
|
99
|
+
/>
|
|
100
|
+
</Routes>
|
|
101
|
+
</MemoryRouter>
|
|
102
|
+
</I18nProvider>,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
expect(screen.getByTestId("chat-content")).toBeTruthy();
|
|
106
|
+
expect(screen.queryByTestId("side-dock")).toBeNull();
|
|
107
|
+
});
|
|
108
|
+
|
|
82
109
|
it("keeps settings routes on the shared shell without channel-specific scroll locking", () => {
|
|
83
110
|
const { container } = render(
|
|
84
111
|
<I18nProvider>
|
|
@@ -23,7 +23,7 @@ vi.mock("@/platforms/mobile", async () => ({
|
|
|
23
23
|
}));
|
|
24
24
|
|
|
25
25
|
describe("SettingsEntryPage", () => {
|
|
26
|
-
it("redirects desktop users to the model settings page", () => {
|
|
26
|
+
it("redirects desktop users to the primary model settings page", () => {
|
|
27
27
|
render(
|
|
28
28
|
<I18nProvider>
|
|
29
29
|
<MemoryRouter initialEntries={["/settings"]}>
|
|
@@ -61,6 +61,14 @@ describe("SettingsEntryPage", () => {
|
|
|
61
61
|
expect(screen.getByTestId("mobile-settings-shell")).toBeTruthy();
|
|
62
62
|
const links = screen.getAllByRole("link");
|
|
63
63
|
expect(links[0]?.textContent?.trim()).toBe("Language");
|
|
64
|
+
expect(links.slice(1, 7).map((link) => link.textContent?.trim())).toEqual([
|
|
65
|
+
"Model",
|
|
66
|
+
"Providers",
|
|
67
|
+
"Channels",
|
|
68
|
+
"Search Channels",
|
|
69
|
+
"Appearance",
|
|
70
|
+
"Security",
|
|
71
|
+
]);
|
|
64
72
|
expect(links.some((link) => link.getAttribute("href") === "/cron")).toBe(
|
|
65
73
|
false,
|
|
66
74
|
);
|
|
@@ -81,6 +81,7 @@ describe("Sidebar", () => {
|
|
|
81
81
|
expect(nav?.className).toContain("flex-1");
|
|
82
82
|
expect(nav?.className).toContain("min-h-0");
|
|
83
83
|
expect(nav?.className).toContain("overflow-y-auto");
|
|
84
|
+
expect(nav?.className).toContain("[mask-image:linear-gradient");
|
|
84
85
|
expect(
|
|
85
86
|
screen.getByRole("link", { current: "page" }).className,
|
|
86
87
|
).not.toContain("font-semibold");
|
|
@@ -101,7 +102,7 @@ describe("Sidebar", () => {
|
|
|
101
102
|
expect(backLink).toBeTruthy();
|
|
102
103
|
expect(header.className).not.toContain("bg-white");
|
|
103
104
|
expect(header.className).not.toContain("rounded-2xl");
|
|
104
|
-
expect(backLink.className).toContain("hover:bg-
|
|
105
|
+
expect(backLink.className).toContain("hover:bg-gray-200/60");
|
|
105
106
|
});
|
|
106
107
|
|
|
107
108
|
it("keeps the settings navigation in the expected product order", () => {
|
|
@@ -125,10 +126,11 @@ describe("Sidebar", () => {
|
|
|
125
126
|
"Providers",
|
|
126
127
|
"Channels",
|
|
127
128
|
"Search Channels",
|
|
129
|
+
"Appearance",
|
|
130
|
+
"Security",
|
|
128
131
|
"Routing & Runtime",
|
|
129
132
|
"Updates",
|
|
130
133
|
"Remote Access",
|
|
131
|
-
"Security",
|
|
132
134
|
"Secrets",
|
|
133
135
|
"MCP",
|
|
134
136
|
]);
|
|
@@ -149,7 +151,8 @@ describe("Sidebar", () => {
|
|
|
149
151
|
expect(screen.getByText("user@example.com")).toBeTruthy();
|
|
150
152
|
expect(accountEntry.className).toContain("py-2");
|
|
151
153
|
expect(accountEntry.className).toContain("text-muted-foreground");
|
|
152
|
-
expect(accountEntry.className).toContain("hover:bg-
|
|
154
|
+
expect(accountEntry.className).toContain("hover:bg-gray-200/60");
|
|
155
|
+
expect(accountEntry.parentElement?.className).not.toContain("border-t");
|
|
153
156
|
expect(accountStatus.className).toContain("text-[11px]");
|
|
154
157
|
});
|
|
155
158
|
|
|
@@ -10,6 +10,7 @@ import { useViewportLayout } from "@/app/hooks/use-viewport-layout";
|
|
|
10
10
|
import { DesktopAppShell, getDesktopHostPlatform } from "@/platforms/desktop";
|
|
11
11
|
import { MobileAppShell } from "@/platforms/mobile";
|
|
12
12
|
import { PANEL_APPS_DOC_BROWSER_RENDERERS } from "@/features/panel-apps";
|
|
13
|
+
import { MARKETPLACE_DETAIL_DOC_BROWSER_RENDERERS } from "@/features/marketplace";
|
|
13
14
|
import {
|
|
14
15
|
SideDock,
|
|
15
16
|
type SideDockManager,
|
|
@@ -23,6 +24,11 @@ interface AppLayoutProps {
|
|
|
23
24
|
children: React.ReactNode;
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
const DOC_BROWSER_RENDERERS = {
|
|
28
|
+
...PANEL_APPS_DOC_BROWSER_RENDERERS,
|
|
29
|
+
...MARKETPLACE_DETAIL_DOC_BROWSER_RENDERERS,
|
|
30
|
+
};
|
|
31
|
+
|
|
26
32
|
function AppLayoutInner({
|
|
27
33
|
children,
|
|
28
34
|
sideDockManager,
|
|
@@ -33,6 +39,7 @@ function AppLayoutInner({
|
|
|
33
39
|
const { language } = useI18n();
|
|
34
40
|
const { isMobile } = useViewportLayout();
|
|
35
41
|
const desktopHostPlatform = getDesktopHostPlatform();
|
|
42
|
+
const isSideDockVisible = useSideDockStore((state) => state.isVisible);
|
|
36
43
|
useSideDockStore((state) => state.pinnedItems);
|
|
37
44
|
const docBrowserDockControls: DocBrowserDockControls = {
|
|
38
45
|
getDockState: sideDockManager.getDockState,
|
|
@@ -50,7 +57,7 @@ function AppLayoutInner({
|
|
|
50
57
|
pathname={pathname}
|
|
51
58
|
isDocBrowserOpen={isOpen}
|
|
52
59
|
docBrowserDockControls={docBrowserDockControls}
|
|
53
|
-
docBrowserRenderers={
|
|
60
|
+
docBrowserRenderers={DOC_BROWSER_RENDERERS}
|
|
54
61
|
topbarLeadingInset={
|
|
55
62
|
desktopHostPlatform === "darwin" ? "4.75rem" : undefined
|
|
56
63
|
}
|
|
@@ -67,8 +74,8 @@ function AppLayoutInner({
|
|
|
67
74
|
isDocBrowserOpen={isOpen}
|
|
68
75
|
docBrowserMode={mode}
|
|
69
76
|
docBrowserDockControls={docBrowserDockControls}
|
|
70
|
-
docBrowserRenderers={
|
|
71
|
-
sideDock={<SideDock manager={sideDockManager} />}
|
|
77
|
+
docBrowserRenderers={DOC_BROWSER_RENDERERS}
|
|
78
|
+
sideDock={isSideDockVisible ? <SideDock manager={sideDockManager} /> : null}
|
|
72
79
|
>
|
|
73
80
|
{children}
|
|
74
81
|
</DesktopAppShell>
|
|
@@ -111,10 +111,10 @@ export function SidebarNavLinkItem({
|
|
|
111
111
|
isActive
|
|
112
112
|
? collapsed
|
|
113
113
|
? SIDEBAR_RAIL_ACTIVE_SURFACE_CLASS
|
|
114
|
-
: "bg-
|
|
114
|
+
: "bg-gray-200/80 text-gray-900 shadow-sm"
|
|
115
115
|
: collapsed
|
|
116
116
|
? SIDEBAR_RAIL_SURFACE_CLASS
|
|
117
|
-
: "text-muted-foreground hover:bg-
|
|
117
|
+
: "text-muted-foreground hover:bg-gray-200/60 hover:text-gray-900",
|
|
118
118
|
className,
|
|
119
119
|
)}
|
|
120
120
|
>
|
|
@@ -123,8 +123,8 @@ export function SidebarNavLinkItem({
|
|
|
123
123
|
collapsed ? SIDEBAR_RAIL_ICON_CLASS : tone.icon,
|
|
124
124
|
"shrink-0 transition-colors",
|
|
125
125
|
isActive
|
|
126
|
-
? "text-
|
|
127
|
-
: "text-muted-foreground/75 group-hover:text-
|
|
126
|
+
? "text-gray-700"
|
|
127
|
+
: "text-muted-foreground/75 group-hover:text-gray-700",
|
|
128
128
|
)}
|
|
129
129
|
/>
|
|
130
130
|
<span className={collapsed ? "sr-only" : "min-w-0 flex-1 text-left"}>
|
|
@@ -174,7 +174,7 @@ export function SidebarActionItem({
|
|
|
174
174
|
onClick={onClick}
|
|
175
175
|
aria-label={label}
|
|
176
176
|
className={cn(
|
|
177
|
-
"group flex w-full items-center rounded-xl font-medium text-muted-foreground transition-
|
|
177
|
+
"group flex w-full items-center rounded-xl font-medium text-muted-foreground transition-colors duration-base hover:bg-gray-200/60 hover:text-gray-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/35",
|
|
178
178
|
collapsed
|
|
179
179
|
? cn(
|
|
180
180
|
SIDEBAR_RAIL_CONTROL_CLASS,
|
|
@@ -189,7 +189,7 @@ export function SidebarActionItem({
|
|
|
189
189
|
<Icon
|
|
190
190
|
className={cn(
|
|
191
191
|
collapsed ? SIDEBAR_RAIL_ICON_CLASS : tone.icon,
|
|
192
|
-
"shrink-0 text-muted-foreground/75 transition-colors group-hover:text-
|
|
192
|
+
"shrink-0 text-muted-foreground/75 transition-colors group-hover:text-gray-700",
|
|
193
193
|
)}
|
|
194
194
|
/>
|
|
195
195
|
<span
|
|
@@ -248,7 +248,7 @@ export function SidebarSelectItem({
|
|
|
248
248
|
<SelectTrigger
|
|
249
249
|
aria-label={label}
|
|
250
250
|
className={cn(
|
|
251
|
-
"group h-auto w-full rounded-xl border-0 bg-transparent font-medium text-muted-foreground shadow-none hover:bg-
|
|
251
|
+
"group h-auto w-full rounded-xl border-0 bg-transparent font-medium text-muted-foreground shadow-none hover:bg-gray-200/60 hover:text-gray-900 focus:ring-0 focus-visible:ring-2 focus-visible:ring-primary/35",
|
|
252
252
|
collapsed
|
|
253
253
|
? cn(
|
|
254
254
|
SIDEBAR_RAIL_CONTROL_CLASS,
|
|
@@ -267,7 +267,7 @@ export function SidebarSelectItem({
|
|
|
267
267
|
<Icon
|
|
268
268
|
className={cn(
|
|
269
269
|
collapsed ? SIDEBAR_RAIL_ICON_CLASS : tone.icon,
|
|
270
|
-
"shrink-0 text-muted-foreground/75 transition-colors group-hover:text-
|
|
270
|
+
"shrink-0 text-muted-foreground/75 transition-colors group-hover:text-gray-700",
|
|
271
271
|
)}
|
|
272
272
|
/>
|
|
273
273
|
<span className={collapsed ? "sr-only" : "text-left"}>{label}</span>
|
|
@@ -5,11 +5,13 @@ export const SIDEBAR_RAIL_CONTROL_CLASS = "h-9 w-9 rounded-xl";
|
|
|
5
5
|
export const SIDEBAR_RAIL_ICON_CLASS = "h-4 w-4";
|
|
6
6
|
export const SIDEBAR_RAIL_STACK_CLASS = "space-y-1";
|
|
7
7
|
export const SIDEBAR_RAIL_ITEM_GAP_CLASS = "gap-1";
|
|
8
|
+
export const SIDEBAR_SCROLL_EDGE_FADE_CLASS =
|
|
9
|
+
"[mask-image:linear-gradient(to_bottom,black_calc(100%_-_28px),transparent)] [-webkit-mask-image:linear-gradient(to_bottom,black_calc(100%_-_28px),transparent)]";
|
|
8
10
|
|
|
9
11
|
export const SIDEBAR_RAIL_SURFACE_CLASS =
|
|
10
|
-
"text-muted-foreground transition-colors duration-base hover:bg-
|
|
12
|
+
"text-muted-foreground transition-colors duration-base hover:bg-gray-200/60 hover:text-gray-900 active:bg-gray-200/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/35";
|
|
11
13
|
|
|
12
|
-
export const SIDEBAR_RAIL_ACTIVE_SURFACE_CLASS = "bg-
|
|
14
|
+
export const SIDEBAR_RAIL_ACTIVE_SURFACE_CLASS = "bg-gray-200/80 text-gray-900";
|
|
13
15
|
|
|
14
16
|
export const SIDEBAR_RAIL_PRIMARY_SURFACE_CLASS =
|
|
15
17
|
"bg-primary/10 text-primary transition-colors duration-base hover:bg-primary/15 hover:text-primary-700 active:bg-primary/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/35";
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
SIDEBAR_RAIL_STACK_CLASS,
|
|
42
42
|
SIDEBAR_RAIL_SURFACE_CLASS,
|
|
43
43
|
SIDEBAR_RAIL_WIDTH_CLASS,
|
|
44
|
+
SIDEBAR_SCROLL_EDGE_FADE_CLASS,
|
|
44
45
|
} from "@/app/components/layout/sidebar-rail.styles";
|
|
45
46
|
|
|
46
47
|
type SidebarMode = "main" | "settings";
|
|
@@ -105,7 +106,7 @@ function SettingsSidebarHeader({
|
|
|
105
106
|
to="/chat"
|
|
106
107
|
aria-label={t("backToMain")}
|
|
107
108
|
className={cn(
|
|
108
|
-
"group inline-flex min-w-0 items-center rounded-lg text-[12px] font-medium text-muted-foreground transition-colors hover:bg-
|
|
109
|
+
"group inline-flex min-w-0 items-center rounded-lg text-[12px] font-medium text-muted-foreground transition-colors hover:bg-gray-200/60 hover:text-gray-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/35",
|
|
109
110
|
isCollapsed
|
|
110
111
|
? cn(
|
|
111
112
|
SIDEBAR_RAIL_CONTROL_CLASS,
|
|
@@ -118,7 +119,7 @@ function SettingsSidebarHeader({
|
|
|
118
119
|
<ArrowLeft
|
|
119
120
|
className={cn(
|
|
120
121
|
isCollapsed ? SIDEBAR_RAIL_ICON_CLASS : "h-3.5 w-3.5",
|
|
121
|
-
"shrink-0 text-muted-foreground/75 group-hover:text-
|
|
122
|
+
"shrink-0 text-muted-foreground/75 group-hover:text-gray-700",
|
|
122
123
|
)}
|
|
123
124
|
/>
|
|
124
125
|
<span className={isCollapsed ? "sr-only" : "truncate"}>
|
|
@@ -208,6 +209,7 @@ function SidebarNavigation({
|
|
|
208
209
|
className={cn(
|
|
209
210
|
"custom-scrollbar min-h-0 flex-1 overflow-y-auto",
|
|
210
211
|
isCollapsed ? "pr-0" : "pr-1",
|
|
212
|
+
SIDEBAR_SCROLL_EDGE_FADE_CLASS,
|
|
211
213
|
)}
|
|
212
214
|
>
|
|
213
215
|
<ul
|
|
@@ -219,22 +221,20 @@ function SidebarNavigation({
|
|
|
219
221
|
: "space-y-1 pb-4",
|
|
220
222
|
)}
|
|
221
223
|
>
|
|
222
|
-
{items.map((item) =>
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
);
|
|
237
|
-
})}
|
|
224
|
+
{items.map((item) => (
|
|
225
|
+
<li
|
|
226
|
+
key={item.target}
|
|
227
|
+
className={isCollapsed ? "flex justify-center" : undefined}
|
|
228
|
+
>
|
|
229
|
+
<SidebarNavLinkItem
|
|
230
|
+
to={item.target}
|
|
231
|
+
label={item.label}
|
|
232
|
+
icon={item.icon}
|
|
233
|
+
density={density}
|
|
234
|
+
collapsed={isCollapsed}
|
|
235
|
+
/>
|
|
236
|
+
</li>
|
|
237
|
+
))}
|
|
238
238
|
</ul>
|
|
239
239
|
</nav>
|
|
240
240
|
);
|
|
@@ -264,13 +264,9 @@ export function Sidebar({ mode }: SidebarProps) {
|
|
|
264
264
|
const accountConnected = Boolean(remoteStatus.data?.account.loggedIn);
|
|
265
265
|
|
|
266
266
|
const handleThemeSwitch = (nextTheme: UiTheme) => {
|
|
267
|
-
if (theme
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
setTheme(nextTheme);
|
|
267
|
+
if (theme !== nextTheme) setTheme(nextTheme);
|
|
271
268
|
};
|
|
272
269
|
|
|
273
|
-
// Core navigation items - primary features
|
|
274
270
|
const mainNavItems: SidebarNavItem[] = [
|
|
275
271
|
{
|
|
276
272
|
target: "/chat",
|
|
@@ -294,8 +290,7 @@ export function Sidebar({ mode }: SidebarProps) {
|
|
|
294
290
|
},
|
|
295
291
|
];
|
|
296
292
|
|
|
297
|
-
const
|
|
298
|
-
const navItems = isSettingsMode ? settingsNavItems : mainNavItems;
|
|
293
|
+
const navItems = isSettingsMode ? getSettingsNavItems(t) : mainNavItems;
|
|
299
294
|
const sidebarDensity = isSettingsMode ? "compact" : "default";
|
|
300
295
|
|
|
301
296
|
return (
|
|
@@ -315,7 +310,6 @@ export function Sidebar({ mode }: SidebarProps) {
|
|
|
315
310
|
/>
|
|
316
311
|
|
|
317
312
|
<div className="flex min-h-0 flex-1 flex-col">
|
|
318
|
-
{/* Navigation */}
|
|
319
313
|
<SidebarNavigation
|
|
320
314
|
isSettingsMode={isSettingsMode}
|
|
321
315
|
isCollapsed={isCollapsed}
|
|
@@ -323,10 +317,9 @@ export function Sidebar({ mode }: SidebarProps) {
|
|
|
323
317
|
density={sidebarDensity}
|
|
324
318
|
/>
|
|
325
319
|
|
|
326
|
-
{/* Footer actions stay reachable while the nav scrolls independently. */}
|
|
327
320
|
<div
|
|
328
321
|
className={cn(
|
|
329
|
-
"shrink-0
|
|
322
|
+
"shrink-0 bg-secondary",
|
|
330
323
|
isCollapsed
|
|
331
324
|
? "mt-2 pt-2"
|
|
332
325
|
: isSettingsMode
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
Languages,
|
|
9
9
|
MessageCircle,
|
|
10
10
|
MessageSquare,
|
|
11
|
+
Palette,
|
|
11
12
|
Search,
|
|
12
13
|
Settings,
|
|
13
14
|
Shield,
|
|
@@ -103,6 +104,16 @@ export function getSettingsNavItems(
|
|
|
103
104
|
label: translate("searchChannels"),
|
|
104
105
|
icon: Search,
|
|
105
106
|
},
|
|
107
|
+
{
|
|
108
|
+
target: "/appearance",
|
|
109
|
+
label: translate("appearance"),
|
|
110
|
+
icon: Palette,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
target: "/security",
|
|
114
|
+
label: translate("security"),
|
|
115
|
+
icon: Shield,
|
|
116
|
+
},
|
|
106
117
|
{
|
|
107
118
|
target: "/runtime",
|
|
108
119
|
label: translate("runtime"),
|
|
@@ -118,11 +129,6 @@ export function getSettingsNavItems(
|
|
|
118
129
|
label: translate("remote"),
|
|
119
130
|
icon: Wifi,
|
|
120
131
|
},
|
|
121
|
-
{
|
|
122
|
-
target: "/security",
|
|
123
|
-
label: translate("security"),
|
|
124
|
-
icon: Shield,
|
|
125
|
-
},
|
|
126
132
|
{
|
|
127
133
|
target: "/secrets",
|
|
128
134
|
label: translate("secrets"),
|
package/src/app/index.tsx
CHANGED
|
@@ -26,6 +26,10 @@ const ModelConfigPage = lazy(async () => ({
|
|
|
26
26
|
default: (await import("@/features/settings/pages/model-config-page"))
|
|
27
27
|
.ModelConfigPage,
|
|
28
28
|
}));
|
|
29
|
+
const AppearanceSettingsPage = lazy(async () => ({
|
|
30
|
+
default: (await import("@/features/settings/pages/appearance-settings-page"))
|
|
31
|
+
.AppearanceSettingsPage,
|
|
32
|
+
}));
|
|
29
33
|
const ChatPage = lazy(async () => ({
|
|
30
34
|
default: (await import("@/components/chat/chat-page")).ChatPage,
|
|
31
35
|
}));
|
|
@@ -114,6 +118,10 @@ const protectedRouteDefinitions: ProtectedRouteDefinition[] = [
|
|
|
114
118
|
path: "/cron",
|
|
115
119
|
element: createLazyElement(<ChatPage view="cron" />),
|
|
116
120
|
},
|
|
121
|
+
{
|
|
122
|
+
path: "/appearance",
|
|
123
|
+
element: createLazyElement(<AppearanceSettingsPage />),
|
|
124
|
+
},
|
|
117
125
|
{
|
|
118
126
|
path: "/model",
|
|
119
127
|
element: createLazyElement(<ModelConfigPage />),
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
DENSE_RIGHT_PANELS_AUTO_COLLAPSE_MAX_WIDTH,
|
|
4
|
+
viewportLayoutManager,
|
|
5
|
+
} from "@/app/managers/viewport-layout.manager";
|
|
6
|
+
import { useViewportLayoutStore } from "@/app/stores/viewport-layout.store";
|
|
7
|
+
|
|
8
|
+
function setViewportWidth(width: number): void {
|
|
9
|
+
Object.defineProperty(window, "innerWidth", {
|
|
10
|
+
configurable: true,
|
|
11
|
+
value: width,
|
|
12
|
+
writable: true,
|
|
13
|
+
});
|
|
14
|
+
viewportLayoutManager.resetForTests();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("ViewportLayoutManager dense right panels", () => {
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
window.localStorage.clear();
|
|
20
|
+
setViewportWidth(DENSE_RIGHT_PANELS_AUTO_COLLAPSE_MAX_WIDTH);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("collapses the left sidebar once when two docked right panels crowd a desktop viewport", () => {
|
|
24
|
+
viewportLayoutManager.collapseSidebarForDenseRightPanels({
|
|
25
|
+
isDocBrowserDocked: true,
|
|
26
|
+
isDocBrowserOpen: true,
|
|
27
|
+
isWorkspacePanelOpen: true,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
expect(useViewportLayoutStore.getState().isSidebarCollapsed).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("keeps the left sidebar expanded when only one right panel is open", () => {
|
|
34
|
+
viewportLayoutManager.collapseSidebarForDenseRightPanels({
|
|
35
|
+
isDocBrowserDocked: true,
|
|
36
|
+
isDocBrowserOpen: true,
|
|
37
|
+
isWorkspacePanelOpen: false,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
expect(useViewportLayoutStore.getState().isSidebarCollapsed).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("does not collapse the left sidebar for floating DocBrowser or wide desktop layouts", () => {
|
|
44
|
+
viewportLayoutManager.collapseSidebarForDenseRightPanels({
|
|
45
|
+
isDocBrowserDocked: false,
|
|
46
|
+
isDocBrowserOpen: true,
|
|
47
|
+
isWorkspacePanelOpen: true,
|
|
48
|
+
});
|
|
49
|
+
expect(useViewportLayoutStore.getState().isSidebarCollapsed).toBe(false);
|
|
50
|
+
|
|
51
|
+
setViewportWidth(DENSE_RIGHT_PANELS_AUTO_COLLAPSE_MAX_WIDTH + 1);
|
|
52
|
+
viewportLayoutManager.collapseSidebarForDenseRightPanels({
|
|
53
|
+
isDocBrowserDocked: true,
|
|
54
|
+
isDocBrowserOpen: true,
|
|
55
|
+
isWorkspacePanelOpen: true,
|
|
56
|
+
});
|
|
57
|
+
expect(useViewportLayoutStore.getState().isSidebarCollapsed).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -5,6 +5,14 @@ import {
|
|
|
5
5
|
type ViewportLayoutSnapshot,
|
|
6
6
|
} from "@/app/stores/viewport-layout.store";
|
|
7
7
|
|
|
8
|
+
export const DENSE_RIGHT_PANELS_AUTO_COLLAPSE_MAX_WIDTH = 1800;
|
|
9
|
+
|
|
10
|
+
type DenseRightPanelsLayout = {
|
|
11
|
+
isDocBrowserDocked: boolean;
|
|
12
|
+
isDocBrowserOpen: boolean;
|
|
13
|
+
isWorkspacePanelOpen: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
8
16
|
export class ViewportLayoutManager {
|
|
9
17
|
private consumerCount = 0;
|
|
10
18
|
|
|
@@ -51,6 +59,37 @@ export class ViewportLayoutManager {
|
|
|
51
59
|
useViewportLayoutStore.setState({ isSidebarCollapsed });
|
|
52
60
|
};
|
|
53
61
|
|
|
62
|
+
collapseSidebarForDenseRightPanels = (layout: DenseRightPanelsLayout) => {
|
|
63
|
+
if (
|
|
64
|
+
!layout.isDocBrowserOpen ||
|
|
65
|
+
!layout.isDocBrowserDocked ||
|
|
66
|
+
!layout.isWorkspacePanelOpen
|
|
67
|
+
) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const snapshot = this.getSnapshot();
|
|
72
|
+
if (snapshot.mode !== "desktop" || snapshot.isSidebarCollapsed) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const width =
|
|
77
|
+
typeof snapshot.width === "number"
|
|
78
|
+
? snapshot.width
|
|
79
|
+
: typeof window === "undefined"
|
|
80
|
+
? null
|
|
81
|
+
: window.innerWidth;
|
|
82
|
+
if (
|
|
83
|
+
typeof width !== "number" ||
|
|
84
|
+
!Number.isFinite(width) ||
|
|
85
|
+
width > DENSE_RIGHT_PANELS_AUTO_COLLAPSE_MAX_WIDTH
|
|
86
|
+
) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
this.setSidebarCollapsed(true);
|
|
91
|
+
};
|
|
92
|
+
|
|
54
93
|
toggleSidebarCollapsed = () => {
|
|
55
94
|
useViewportLayoutStore.setState((state) => ({
|
|
56
95
|
isSidebarCollapsed: !state.isSidebarCollapsed,
|
|
@@ -1,16 +1,39 @@
|
|
|
1
1
|
import { AccountManager } from '@/features/account';
|
|
2
|
-
import {
|
|
2
|
+
import { viewportLayoutManager } from '@/app/managers/viewport-layout.manager';
|
|
3
|
+
import {
|
|
4
|
+
ChatDraftIntentManager,
|
|
5
|
+
useChatThreadStore,
|
|
6
|
+
} from '@/features/chat';
|
|
3
7
|
import { PanelAppBridgeManager } from '@/features/panel-apps';
|
|
4
8
|
import { RightPanelResourceRouteResolver } from '@/features/right-panel-resources';
|
|
5
9
|
import { RemoteAccessManager } from '@/features/remote';
|
|
6
10
|
import { ServiceActionAuthorizationManager } from '@/features/service-apps';
|
|
7
11
|
import { SideDockManager } from '@/features/side-dock';
|
|
8
12
|
import { DocBrowserManager } from '@/shared/components/doc-browser/managers/doc-browser.manager';
|
|
13
|
+
import { useDocBrowserStore } from '@/shared/components/doc-browser/stores/doc-browser.store';
|
|
14
|
+
|
|
15
|
+
type ChatThreadSnapshot = ReturnType<typeof useChatThreadStore.getState>['snapshot'];
|
|
16
|
+
|
|
17
|
+
function isChatWorkspacePanelOpen(snapshot: ChatThreadSnapshot): boolean {
|
|
18
|
+
return snapshot.activeWorkspacePanelKind != null;
|
|
19
|
+
}
|
|
9
20
|
|
|
10
21
|
export class AppPresenter {
|
|
11
22
|
accountManager = new AccountManager();
|
|
12
23
|
rightPanelResourceRouteResolver = new RightPanelResourceRouteResolver();
|
|
13
|
-
|
|
24
|
+
notifyRightPanelOpened = () => {
|
|
25
|
+
const docBrowser = useDocBrowserStore.getState().snapshot;
|
|
26
|
+
const chatThread = useChatThreadStore.getState().snapshot;
|
|
27
|
+
viewportLayoutManager.collapseSidebarForDenseRightPanels({
|
|
28
|
+
isDocBrowserDocked: docBrowser.mode === 'docked',
|
|
29
|
+
isDocBrowserOpen: docBrowser.isOpen,
|
|
30
|
+
isWorkspacePanelOpen: isChatWorkspacePanelOpen(chatThread),
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
docBrowserManager = new DocBrowserManager(
|
|
34
|
+
this.rightPanelResourceRouteResolver,
|
|
35
|
+
this.notifyRightPanelOpened,
|
|
36
|
+
);
|
|
14
37
|
sideDockManager = new SideDockManager(this.docBrowserManager);
|
|
15
38
|
chatDraftIntentManager = new ChatDraftIntentManager();
|
|
16
39
|
serviceActionAuthorizationManager = new ServiceActionAuthorizationManager();
|