@nextclaw/ui 0.15.1 → 0.15.2
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 +31 -0
- package/dist/assets/{api-b1R9VdeH.js → api-B7Qigvtm.js} +2 -2
- package/dist/assets/{appearance-settings-page-D2O9pch9.js → appearance-settings-page-CuE0Iiqj.js} +1 -1
- package/dist/assets/{book-open-B77lQ2Tg.js → book-open-Ba2f7YiG.js} +1 -1
- package/dist/assets/channels-list-page-CTsM6TcP.js +8 -0
- package/dist/assets/{chat-page-CZ7WxPir.js → chat-page-C_5dcuE3.js} +1 -1
- package/dist/assets/config-split-page-Fbb4PtTf.js +1 -0
- package/dist/assets/{confirm-dialog-C-NXSioX.js → confirm-dialog-D2EG3nqE.js} +1 -1
- package/dist/assets/{desktop-update-config-usHpXugP.js → desktop-update-config-BGR3HUi1.js} +1 -1
- package/dist/assets/{dist-jdGmbb6w.js → dist-7JTjycxE.js} +1 -1
- package/dist/assets/{dist-YtoqU-AT.js → dist-wXLhk4hl.js} +1 -1
- package/dist/assets/doc-browser-bHyXnxlj.js +1 -0
- package/dist/assets/doc-browser-context-CYAGm5d5.js +1 -0
- package/dist/assets/doc-browser-gTrQ3jCF.js +1 -0
- package/dist/assets/{es2015-bNpL2LKy.js → es2015-j1SRCIX5.js} +1 -1
- package/dist/assets/{external-link-BM6fk8q-.js → external-link-DxuPaDoK.js} +1 -1
- package/dist/assets/form-actions-BztL7P_T.js +1 -0
- package/dist/assets/{i18n-provider-CntWolRA.js → i18n-provider-B4oMxOFx.js} +1 -1
- package/dist/assets/index-CvrvblgX.css +1 -0
- package/dist/assets/index-Obd4JYU2.js +104 -0
- package/dist/assets/loader-circle-CdzgK4eH.js +1 -0
- package/dist/assets/{mcp-marketplace-page-CLmUstLN.js → mcp-marketplace-page-Bt7CuOoq.js} +2 -2
- package/dist/assets/mcp-marketplace-page-Ck3t78_n.js +1 -0
- package/dist/assets/model-config-page-DeKk9gvE.js +1 -0
- package/dist/assets/plus-Bx7wQbhG.js +1 -0
- package/dist/assets/{provider-scoped-model-input-Cm_a4GHf.js → provider-scoped-model-input-DsH2LF1q.js} +1 -1
- package/dist/assets/providers-config-page-BFnGp68F.js +1 -0
- package/dist/assets/{react-BN8uzPnp.js → react-UirzUuST.js} +1 -1
- package/dist/assets/{refresh-cw-BpVcFPmJ.js → refresh-cw-eT8BF-8B.js} +1 -1
- package/dist/assets/remote-CDkHZtA8.js +1 -0
- package/dist/assets/rotate-cw-1JqxHTuw.js +1 -0
- package/dist/assets/runtime-config-page-BelAqiNG.js +1 -0
- package/dist/assets/{save-BpFW5fpk.js → save-Cb2g0BOi.js} +1 -1
- package/dist/assets/{search-D0uO7enf.js → search-BcHjwdsb.js} +1 -1
- package/dist/assets/search-config-page-zPm6--Ig.js +1 -0
- package/dist/assets/secrets-config-page-Cxyp-KuQ.js +3 -0
- package/dist/assets/security-config-B_gCq9fn.js +1 -0
- package/dist/assets/{select-Bh-CrA4p.js → select-CdSEOwVM.js} +1 -1
- package/dist/assets/{setting-row-BJfCHRcb.js → setting-row-CTAtGexr.js} +1 -1
- package/dist/assets/{settings-2-Blzi1vr6.js → settings-2-2DWauOjY.js} +1 -1
- package/dist/assets/{side-dock-ZdaSrQVB.js → side-dock-DPvakJ0a.js} +2 -2
- package/dist/assets/{skeleton-BLlEzebZ.js → skeleton-P5oM14E1.js} +1 -1
- package/dist/assets/{switch-CFLJFNSd.js → switch-BU_UEULb.js} +1 -1
- package/dist/assets/{tag-chip-CzGihiQx.js → tag-chip-D-WsKoT1.js} +1 -1
- package/dist/assets/{tooltip-CdcrBjrC.js → tooltip-CjkOaHW7.js} +1 -1
- package/dist/assets/{use-config-CVPgsBmU.js → use-config--hx5oxQn.js} +1 -1
- package/dist/assets/{use-confirm-dialog-CSeBk9SF.js → use-confirm-dialog-CD9a_SK1.js} +1 -1
- package/dist/assets/x-DT5x45JU.js +1 -0
- package/dist/index.html +38 -30
- package/index.html +12 -4
- package/package.json +9 -9
- package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +29 -1
- package/src/app/components/layout/sidebar-items.tsx +43 -4
- package/src/app/components/layout/sidebar.tsx +83 -61
- package/src/app/configs/app-navigation.config.ts +49 -0
- package/src/app/styles/design-system.css +82 -0
- package/src/features/channels/components/config/channel-form.tsx +14 -11
- package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +40 -0
- package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +3 -1
- package/src/features/chat/components/conversation/chat-conversation-panel.tsx +1 -1
- package/src/features/chat/components/layout/__tests__/chat-sidebar-toolbar.test.tsx +48 -0
- package/src/features/chat/components/layout/__tests__/chat-sidebar-utility-menu.test.tsx +55 -0
- package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +0 -3
- package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +3 -4
- package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +29 -30
- package/src/features/chat/components/layout/chat-sidebar.tsx +21 -7
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +2 -1
- package/src/features/chat/features/message/components/__tests__/chat-inline-panel-app-card.test.tsx +22 -1
- package/src/features/chat/features/message/components/chat-inline-panel-app-card.tsx +10 -5
- package/src/features/chat/features/message/utils/chat-inline-panel-app-card.utils.ts +0 -11
- package/src/features/chat/features/session/components/__tests__/chat-sidebar-project-groups.test.tsx +116 -0
- package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +108 -46
- package/src/features/chat/features/session/components/chat-sidebar-session-entry.tsx +6 -0
- package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +48 -24
- package/src/features/chat/features/session/components/chat-sidebar-session-list.tsx +0 -3
- package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +0 -4
- package/src/features/chat/features/session/utils/chat-sidebar-session-groups.utils.ts +25 -3
- package/src/features/chat/features/welcome/components/chat-welcome-capability-grid.tsx +9 -4
- package/src/features/chat/features/welcome/components/chat-welcome.tsx +0 -5
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +159 -56
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +1 -0
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +144 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-directory-browser.tsx +99 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +3 -1
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +66 -7
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +3 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +44 -1
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +45 -3
- package/src/features/chat/managers/__tests__/chat-session-list.manager.test.ts +33 -2
- package/src/features/chat/managers/chat-session-list.manager.ts +32 -0
- package/src/features/chat/stores/chat-session-list.store.ts +28 -5
- package/src/features/marketplace/components/mcp/mcp-marketplace-page.tsx +5 -12
- package/src/features/panel-apps/components/panel-app-list-item.tsx +6 -6
- package/src/features/panel-apps/index.ts +1 -0
- package/src/features/panel-apps/utils/panel-app-doc-browser.utils.tsx +2 -10
- package/src/features/panel-apps/utils/panel-app-iframe.utils.ts +6 -0
- package/src/features/pwa/managers/__tests__/pwa-shell-theme.manager.test.ts +11 -3
- package/src/features/pwa/managers/pwa-shell-theme.manager.ts +3 -1
- package/src/features/service-apps/components/service-apps-panel.tsx +145 -35
- package/src/features/settings/components/config/provider-auth-section.tsx +1 -1
- package/src/features/settings/components/config/provider-form.tsx +11 -8
- package/src/features/settings/components/config/provider-models-section.tsx +7 -7
- package/src/features/settings/components/config/provider-pill-selector.tsx +1 -1
- package/src/features/settings/components/config/secrets-config-form.tsx +5 -4
- package/src/features/settings/pages/model-config-page.tsx +26 -25
- package/src/features/settings/pages/providers-config-page.tsx +10 -13
- package/src/features/settings/pages/search-config-page.tsx +8 -5
- package/src/features/system-status/components/config/runtime-config-editor.tsx +5 -4
- package/src/index.css +18 -0
- package/src/shared/components/config-split-page.tsx +6 -6
- package/src/shared/components/doc-browser/__tests__/doc-browser-url.utils.test.ts +73 -0
- package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +48 -0
- package/src/shared/components/doc-browser/doc-browser-context.tsx +4 -0
- package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +5 -1
- package/src/shared/components/doc-browser/doc-browser-renderer.types.ts +2 -1
- package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +10 -10
- package/src/shared/components/doc-browser/doc-browser.tsx +6 -4
- package/src/shared/components/doc-browser/utils/doc-browser-url.utils.ts +62 -4
- package/src/shared/components/resizable-right-panel/__tests__/resizable-right-panel.test.tsx +18 -0
- package/src/shared/components/resizable-right-panel/resizable-right-panel.tsx +28 -3
- package/src/shared/components/ui/actions/__tests__/icon-action-button.test.tsx +36 -0
- package/src/shared/components/ui/actions/form-actions.tsx +25 -0
- package/src/shared/components/ui/actions/icon-action-button.tsx +25 -2
- package/src/shared/components/ui/card.tsx +12 -4
- package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +2 -1
- package/src/shared/hooks/use-server-path-browse.ts +26 -4
- package/src/shared/hooks/use-server-path-read.ts +11 -1
- package/src/shared/lib/api/utils/server-path.utils.ts +1 -0
- package/src/shared/lib/i18n/locales/en-US/chat.json +16 -0
- package/src/shared/lib/i18n/locales/en-US/core.json +3 -0
- package/src/shared/lib/i18n/locales/en-US/doc-browser.json +5 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +16 -0
- package/src/shared/lib/i18n/locales/zh-CN/core.json +3 -0
- package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +5 -0
- package/src/shared/lib/session-project/workspace-file-breadcrumb.utils.ts +9 -5
- package/src/shared/lib/theme/index.test.ts +21 -0
- package/src/shared/lib/theme/index.ts +6 -4
- package/src/vite-env.d.ts +2 -0
- package/dist/assets/channels-list-page-DcsIQc6A.js +0 -8
- package/dist/assets/config-split-page-BWQ6_Raq.js +0 -1
- package/dist/assets/doc-browser-B_Tf9n70.js +0 -1
- package/dist/assets/doc-browser-BdZPFOVa.js +0 -1
- package/dist/assets/doc-browser-context-DwokemUn.js +0 -1
- package/dist/assets/index-BIcEw90u.js +0 -104
- package/dist/assets/index-BiNSTKTU.css +0 -1
- package/dist/assets/loader-circle-MBgNSVIH.js +0 -1
- package/dist/assets/mcp-marketplace-page-rZkmWPMy.js +0 -1
- package/dist/assets/model-config-page-Di0ySJD2.js +0 -1
- package/dist/assets/plus-vDqW5Kxp.js +0 -1
- package/dist/assets/providers-config-page-BAf2W0MV.js +0 -1
- package/dist/assets/remote-CLQ0IBM8.js +0 -1
- package/dist/assets/rotate-cw-CvNBv7_w.js +0 -1
- package/dist/assets/runtime-config-page-CflvvoDc.js +0 -1
- package/dist/assets/search-config-page-D0klFUXd.js +0 -1
- package/dist/assets/secrets-config-page-dVZ631Wp.js +0 -3
- package/dist/assets/security-config-C5gnlkuj.js +0 -1
- package/dist/assets/x-BF2KQ--V.js +0 -1
- /package/dist/assets/{config-hints-fGnUjDe9.js → config-hints-C_iP1zl_.js} +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { IconActionButton } from '@/shared/components/ui/actions/icon-action-button';
|
|
4
|
+
|
|
5
|
+
describe('IconActionButton', () => {
|
|
6
|
+
it('applies compact size and strong tone classes for nested hover surfaces', () => {
|
|
7
|
+
render(
|
|
8
|
+
<IconActionButton
|
|
9
|
+
size="sm"
|
|
10
|
+
tone="strong"
|
|
11
|
+
icon={<span data-testid="icon" />}
|
|
12
|
+
label="Pin"
|
|
13
|
+
/>,
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const button = screen.getByRole('button', { name: 'Pin' });
|
|
17
|
+
expect(button.className).toContain('h-6');
|
|
18
|
+
expect(button.className).toContain('w-6');
|
|
19
|
+
expect(button.className).toContain('p-1');
|
|
20
|
+
expect(button.className).toContain('hover:bg-black/10');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('defaults to medium size and soft accent hover', () => {
|
|
24
|
+
render(
|
|
25
|
+
<IconActionButton
|
|
26
|
+
icon={<span data-testid="icon" />}
|
|
27
|
+
label="More"
|
|
28
|
+
/>,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const button = screen.getByRole('button', { name: 'More' });
|
|
32
|
+
expect(button.className).toContain('h-7');
|
|
33
|
+
expect(button.className).toContain('w-7');
|
|
34
|
+
expect(button.className).toContain('hover:bg-accent');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'react';
|
|
2
|
+
import { cn } from '@/shared/lib/utils';
|
|
3
|
+
|
|
4
|
+
type FormActionsProps = HTMLAttributes<HTMLDivElement> & {
|
|
5
|
+
align?: 'start' | 'end' | 'between';
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function FormActions({
|
|
9
|
+
align = 'end',
|
|
10
|
+
className,
|
|
11
|
+
...props
|
|
12
|
+
}: FormActionsProps) {
|
|
13
|
+
return (
|
|
14
|
+
<div
|
|
15
|
+
className={cn(
|
|
16
|
+
'flex flex-wrap items-center gap-2',
|
|
17
|
+
align === 'start' && 'justify-start',
|
|
18
|
+
align === 'end' && 'justify-end',
|
|
19
|
+
align === 'between' && 'justify-between',
|
|
20
|
+
className,
|
|
21
|
+
)}
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -7,16 +7,35 @@ import {
|
|
|
7
7
|
} from '@/shared/components/ui/tooltip';
|
|
8
8
|
import { cn } from '@/shared/lib/utils';
|
|
9
9
|
|
|
10
|
+
type IconActionButtonSize = 'sm' | 'md' | 'lg';
|
|
11
|
+
type IconActionButtonTone = 'default' | 'strong';
|
|
12
|
+
|
|
10
13
|
type IconActionButtonProps = Omit<
|
|
11
14
|
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
12
15
|
'children' | 'type'
|
|
13
16
|
> & {
|
|
14
17
|
icon: React.ReactNode;
|
|
15
18
|
label: string;
|
|
19
|
+
size?: IconActionButtonSize;
|
|
20
|
+
/** default: soft accent hover. strong: denser gray hover for nested hover surfaces. */
|
|
21
|
+
tone?: IconActionButtonTone;
|
|
16
22
|
tooltip?: string | false | null;
|
|
17
23
|
tooltipSide?: 'top' | 'right' | 'bottom' | 'left';
|
|
18
24
|
};
|
|
19
25
|
|
|
26
|
+
const SIZE_CLASS: Record<IconActionButtonSize, string> = {
|
|
27
|
+
sm: 'h-6 w-6 rounded-md p-1',
|
|
28
|
+
md: 'h-7 w-7 rounded-md p-1.5',
|
|
29
|
+
lg: 'h-8 w-8 rounded-lg p-1.5',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const TONE_CLASS: Record<IconActionButtonTone, string> = {
|
|
33
|
+
default:
|
|
34
|
+
'text-muted-foreground hover:bg-accent hover:text-accent-foreground disabled:text-muted-foreground/45 disabled:hover:bg-transparent disabled:hover:text-muted-foreground/45',
|
|
35
|
+
strong:
|
|
36
|
+
'text-muted-foreground hover:bg-black/10 hover:text-foreground disabled:text-muted-foreground/45 disabled:hover:bg-transparent disabled:hover:text-muted-foreground/45',
|
|
37
|
+
};
|
|
38
|
+
|
|
20
39
|
const IconActionButton = React.forwardRef<HTMLButtonElement, IconActionButtonProps>(
|
|
21
40
|
(
|
|
22
41
|
{
|
|
@@ -24,6 +43,8 @@ const IconActionButton = React.forwardRef<HTMLButtonElement, IconActionButtonPro
|
|
|
24
43
|
disabled = false,
|
|
25
44
|
icon,
|
|
26
45
|
label,
|
|
46
|
+
size = 'md',
|
|
47
|
+
tone = 'default',
|
|
27
48
|
tooltip,
|
|
28
49
|
tooltipSide = 'bottom',
|
|
29
50
|
...buttonProps
|
|
@@ -38,8 +59,9 @@ const IconActionButton = React.forwardRef<HTMLButtonElement, IconActionButtonPro
|
|
|
38
59
|
disabled={disabled}
|
|
39
60
|
aria-label={label}
|
|
40
61
|
className={cn(
|
|
41
|
-
'inline-flex
|
|
42
|
-
|
|
62
|
+
'inline-flex shrink-0 items-center justify-center transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 disabled:cursor-not-allowed',
|
|
63
|
+
SIZE_CLASS[size],
|
|
64
|
+
TONE_CLASS[tone],
|
|
43
65
|
className
|
|
44
66
|
)}
|
|
45
67
|
>
|
|
@@ -63,3 +85,4 @@ const IconActionButton = React.forwardRef<HTMLButtonElement, IconActionButtonPro
|
|
|
63
85
|
IconActionButton.displayName = 'IconActionButton';
|
|
64
86
|
|
|
65
87
|
export { IconActionButton };
|
|
88
|
+
export type { IconActionButtonProps, IconActionButtonSize, IconActionButtonTone };
|
|
@@ -3,13 +3,21 @@ import { cn } from '@/shared/lib/utils';
|
|
|
3
3
|
|
|
4
4
|
const Card = React.forwardRef<
|
|
5
5
|
HTMLDivElement,
|
|
6
|
-
React.HTMLAttributes<HTMLDivElement> & {
|
|
7
|
-
|
|
6
|
+
React.HTMLAttributes<HTMLDivElement> & {
|
|
7
|
+
hover?: boolean;
|
|
8
|
+
/** Flat settings-style surface: light border, no shadow. */
|
|
9
|
+
surface?: 'raised' | 'flat';
|
|
10
|
+
}
|
|
11
|
+
>(({ className, hover = true, surface = 'raised', ...props }, ref) => (
|
|
8
12
|
<div
|
|
9
13
|
ref={ref}
|
|
10
14
|
className={cn(
|
|
11
|
-
'rounded-2xl border
|
|
12
|
-
|
|
15
|
+
'rounded-2xl border bg-card text-card-foreground transition-colors duration-base',
|
|
16
|
+
surface === 'raised'
|
|
17
|
+
? 'border-border shadow-card'
|
|
18
|
+
: 'border-border/75 shadow-none',
|
|
19
|
+
hover && surface === 'raised' && 'hover:border-primary/25 hover:shadow-card-hover',
|
|
20
|
+
hover && surface === 'flat' && 'hover:bg-muted/40',
|
|
13
21
|
className
|
|
14
22
|
)}
|
|
15
23
|
{...props}
|
|
@@ -147,11 +147,12 @@ function CompactTabItem({
|
|
|
147
147
|
<CompactTabLabelButton tab={tab} labelClassName={labelClassName} />
|
|
148
148
|
{tab.onClose && tab.closePlacement !== "leading-hover" ? (
|
|
149
149
|
<IconActionButton
|
|
150
|
+
size="sm"
|
|
151
|
+
tone="strong"
|
|
150
152
|
icon={<X className="h-3 w-3" />}
|
|
151
153
|
label={tab.closeLabel ?? ""}
|
|
152
154
|
tooltip={tab.closeLabel ?? null}
|
|
153
155
|
onClick={(event) => closeCompactTab(event, tab.onClose!)}
|
|
154
|
-
className="h-5 w-5 rounded p-0.5 hover:bg-black/10"
|
|
155
156
|
/>
|
|
156
157
|
) : null}
|
|
157
158
|
</div>
|
|
@@ -1,19 +1,41 @@
|
|
|
1
1
|
import { useQuery } from '@tanstack/react-query';
|
|
2
2
|
import { fetchServerPathBrowse } from '@/shared/lib/api';
|
|
3
3
|
|
|
4
|
+
export function buildServerPathBrowseQueryKey(params: {
|
|
5
|
+
path?: string | null;
|
|
6
|
+
basePath?: string | null;
|
|
7
|
+
includeFiles?: boolean;
|
|
8
|
+
}) {
|
|
9
|
+
return [
|
|
10
|
+
'server-path-browse',
|
|
11
|
+
params.path?.trim() ?? '',
|
|
12
|
+
params.basePath?.trim() ?? '',
|
|
13
|
+
params.includeFiles ?? false,
|
|
14
|
+
] as const;
|
|
15
|
+
}
|
|
16
|
+
|
|
4
17
|
export function useServerPathBrowse(params: {
|
|
5
18
|
path?: string | null;
|
|
19
|
+
basePath?: string | null;
|
|
6
20
|
includeFiles?: boolean;
|
|
7
21
|
enabled?: boolean;
|
|
8
22
|
}) {
|
|
23
|
+
const { basePath, enabled, includeFiles, path } = params;
|
|
24
|
+
const normalizedPath = path?.trim() ?? '';
|
|
25
|
+
const normalizedBasePath = basePath?.trim() ?? '';
|
|
9
26
|
return useQuery({
|
|
10
|
-
queryKey:
|
|
27
|
+
queryKey: buildServerPathBrowseQueryKey({
|
|
28
|
+
path: normalizedPath,
|
|
29
|
+
basePath: normalizedBasePath,
|
|
30
|
+
includeFiles,
|
|
31
|
+
}),
|
|
11
32
|
queryFn: () =>
|
|
12
33
|
fetchServerPathBrowse({
|
|
13
|
-
path:
|
|
14
|
-
|
|
34
|
+
path: normalizedPath,
|
|
35
|
+
basePath: normalizedBasePath,
|
|
36
|
+
includeFiles,
|
|
15
37
|
}),
|
|
16
|
-
enabled:
|
|
38
|
+
enabled: enabled ?? true,
|
|
17
39
|
staleTime: 0,
|
|
18
40
|
});
|
|
19
41
|
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { useQuery } from '@tanstack/react-query';
|
|
2
2
|
import { fetchServerPathRead } from '@/shared/lib/api';
|
|
3
3
|
|
|
4
|
+
export function buildServerPathReadQueryKey(params: {
|
|
5
|
+
path?: string | null;
|
|
6
|
+
basePath?: string | null;
|
|
7
|
+
}) {
|
|
8
|
+
return ['server-path-read', params.path?.trim() ?? '', params.basePath ?? null] as const;
|
|
9
|
+
}
|
|
10
|
+
|
|
4
11
|
export function useServerPathRead(params: {
|
|
5
12
|
path?: string | null;
|
|
6
13
|
basePath?: string | null;
|
|
@@ -8,7 +15,10 @@ export function useServerPathRead(params: {
|
|
|
8
15
|
}) {
|
|
9
16
|
const normalizedPath = params.path?.trim() ?? '';
|
|
10
17
|
return useQuery({
|
|
11
|
-
queryKey:
|
|
18
|
+
queryKey: buildServerPathReadQueryKey({
|
|
19
|
+
path: normalizedPath,
|
|
20
|
+
basePath: params.basePath,
|
|
21
|
+
}),
|
|
12
22
|
queryFn: () =>
|
|
13
23
|
fetchServerPathRead({
|
|
14
24
|
path: normalizedPath,
|
|
@@ -17,6 +17,7 @@ function encodeServerPathSegments(path: string): string {
|
|
|
17
17
|
|
|
18
18
|
export async function fetchServerPathBrowse(params?: {
|
|
19
19
|
path?: string | null;
|
|
20
|
+
basePath?: string | null;
|
|
20
21
|
includeFiles?: boolean;
|
|
21
22
|
}): Promise<ServerPathBrowseView> {
|
|
22
23
|
return await nextclawClient.serverPaths.browse(params);
|
|
@@ -41,6 +41,9 @@
|
|
|
41
41
|
"chatChildSessionEmpty": "No child session messages yet.",
|
|
42
42
|
"chatWorkspaceBack": "Back in workspace",
|
|
43
43
|
"chatWorkspaceForward": "Forward in workspace",
|
|
44
|
+
"chatWorkspaceRefreshFile": "Refresh preview",
|
|
45
|
+
"chatWorkspaceMaximizePanel": "Maximize workspace panel",
|
|
46
|
+
"chatWorkspaceRestorePanel": "Restore workspace panel",
|
|
44
47
|
"chatWorkspaceClosePanel": "Close workspace panel",
|
|
45
48
|
"chatWorkspaceSideChatDraftTitle": "Side chat",
|
|
46
49
|
"chatWorkspaceSideChatDraftSubtitle": "This draft will inherit the parent session context when you send the first message.",
|
|
@@ -49,7 +52,13 @@
|
|
|
49
52
|
"chatWorkspacePreview": "Preview",
|
|
50
53
|
"chatWorkspaceDiff": "Diff",
|
|
51
54
|
"chatWorkspaceCloseFile": "Close file",
|
|
55
|
+
"chatWorkspaceLoadingPreview": "Loading preview…",
|
|
52
56
|
"chatWorkspaceLoadingFile": "Loading file…",
|
|
57
|
+
"chatWorkspaceLoadingDirectory": "Loading directory…",
|
|
58
|
+
"chatWorkspaceOpenDirectory": "Open directory",
|
|
59
|
+
"chatWorkspaceOpenFile": "Open file",
|
|
60
|
+
"chatWorkspaceDirectoryEmpty": "This directory is empty.",
|
|
61
|
+
"chatWorkspaceDirectoryLoadFailed": "Failed to load directory",
|
|
53
62
|
"chatWorkspacePreviewUnsupported": "This file is not supported in the sidebar preview yet.",
|
|
54
63
|
"chatWorkspacePreviewEmpty": "No file content is available for preview.",
|
|
55
64
|
"chatWorkspaceDiffEmpty": "No diff content is available.",
|
|
@@ -199,6 +208,13 @@
|
|
|
199
208
|
"chatSidebarYesterday": "Yesterday",
|
|
200
209
|
"chatSidebarPrevious7Days": "Previous 7 Days",
|
|
201
210
|
"chatSidebarOlder": "Older",
|
|
211
|
+
"chatSidebarPinned": "Pinned",
|
|
212
|
+
"chatSidebarPinSession": "Pin session",
|
|
213
|
+
"chatSidebarUnpinSession": "Unpin session",
|
|
214
|
+
"chatSidebarPinProject": "Pin project",
|
|
215
|
+
"chatSidebarUnpinProject": "Unpin project",
|
|
216
|
+
"chatSidebarExpandProject": "Expand project",
|
|
217
|
+
"chatSidebarCollapseProject": "Collapse project",
|
|
202
218
|
"chatSidebarProjectViewEmpty": "No project conversations yet",
|
|
203
219
|
"chatWelcomeTitle": "Hello, how can I help you?",
|
|
204
220
|
"chatWelcomeSubtitle": "Describe the goal, choose the project context, and let NextClaw take it from there.",
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
"marketplace": "Marketplace",
|
|
12
12
|
"advanced": "Advanced",
|
|
13
13
|
"settings": "Settings",
|
|
14
|
+
"settingsGroupBasic": "Basic Configuration",
|
|
15
|
+
"settingsGroupAdvanced": "Advanced Configuration",
|
|
14
16
|
"appearance": "Appearance",
|
|
15
17
|
"settingsMenu": "Settings menu",
|
|
16
18
|
"sidebarCollapse": "Collapse sidebar",
|
|
@@ -41,6 +43,7 @@
|
|
|
41
43
|
"language": "Language",
|
|
42
44
|
"theme": "Theme",
|
|
43
45
|
"themeNatural": "Natural",
|
|
46
|
+
"themeWork": "Default",
|
|
44
47
|
"themeMinimal": "Minimal",
|
|
45
48
|
"themeWarm": "Paper",
|
|
46
49
|
"themeCool": "Mist",
|
|
@@ -65,6 +65,8 @@
|
|
|
65
65
|
"serviceAppsLoading": "Loading service apps...",
|
|
66
66
|
"serviceAppsLoadFailed": "Failed to load service apps",
|
|
67
67
|
"serviceAppsEmpty": "No service apps yet",
|
|
68
|
+
"serviceAppsEmptyTitle": "No service apps",
|
|
69
|
+
"serviceAppsEmptyDescription": "Service apps expose local runtimes and actions that panel apps can call.",
|
|
68
70
|
"serviceAppsRefresh": "Refresh service apps",
|
|
69
71
|
"serviceAppsDiscoverActions": "Connect and discover actions",
|
|
70
72
|
"serviceAppsDiscoverActionsHint": "Connect to the service app runtime and discover its available actions.",
|
|
@@ -84,6 +86,9 @@
|
|
|
84
86
|
"serviceAppsStatusHint_running": "The service app runtime is connected and ready for action discovery or invocation.",
|
|
85
87
|
"serviceAppsStatusHint_failed": "The runtime connection failed; check the error or disconnect and try again.",
|
|
86
88
|
"serviceAppsStatusHint_stopped": "The service app is stopped.",
|
|
89
|
+
"serviceAppsActionsSection": "Actions",
|
|
90
|
+
"serviceAppsActionsEmpty": "No actions discovered yet",
|
|
91
|
+
"serviceAppsDetails": "Runtime details",
|
|
87
92
|
"serviceAppsGrantedTo": "Granted to",
|
|
88
93
|
"serviceAppsRevokeGrant": "Revoke grant",
|
|
89
94
|
"serviceAppsRuntimeState_matched": "Matched",
|
|
@@ -41,6 +41,9 @@
|
|
|
41
41
|
"chatChildSessionEmpty": "子会话还没有消息。",
|
|
42
42
|
"chatWorkspaceBack": "工作区后退",
|
|
43
43
|
"chatWorkspaceForward": "工作区前进",
|
|
44
|
+
"chatWorkspaceRefreshFile": "刷新预览",
|
|
45
|
+
"chatWorkspaceMaximizePanel": "最大化工作区侧栏",
|
|
46
|
+
"chatWorkspaceRestorePanel": "还原工作区侧栏",
|
|
44
47
|
"chatWorkspaceClosePanel": "关闭工作区侧栏",
|
|
45
48
|
"chatWorkspaceSideChatDraftTitle": "Side chat",
|
|
46
49
|
"chatWorkspaceSideChatDraftSubtitle": "发送第一条消息后,会继承父会话上下文并创建真实 child 会话。",
|
|
@@ -49,7 +52,13 @@
|
|
|
49
52
|
"chatWorkspacePreview": "预览",
|
|
50
53
|
"chatWorkspaceDiff": "Diff",
|
|
51
54
|
"chatWorkspaceCloseFile": "关闭文件",
|
|
55
|
+
"chatWorkspaceLoadingPreview": "正在加载预览…",
|
|
52
56
|
"chatWorkspaceLoadingFile": "正在加载文件…",
|
|
57
|
+
"chatWorkspaceLoadingDirectory": "正在加载目录…",
|
|
58
|
+
"chatWorkspaceOpenDirectory": "打开目录",
|
|
59
|
+
"chatWorkspaceOpenFile": "打开文件",
|
|
60
|
+
"chatWorkspaceDirectoryEmpty": "这个目录为空。",
|
|
61
|
+
"chatWorkspaceDirectoryLoadFailed": "读取目录失败",
|
|
53
62
|
"chatWorkspacePreviewUnsupported": "该文件暂不支持在侧栏预览。",
|
|
54
63
|
"chatWorkspacePreviewEmpty": "当前没有可显示的文件内容。",
|
|
55
64
|
"chatWorkspaceDiffEmpty": "当前没有可显示的 diff 内容。",
|
|
@@ -199,6 +208,13 @@
|
|
|
199
208
|
"chatSidebarYesterday": "昨天",
|
|
200
209
|
"chatSidebarPrevious7Days": "近 7 天",
|
|
201
210
|
"chatSidebarOlder": "更早",
|
|
211
|
+
"chatSidebarPinned": "已置顶",
|
|
212
|
+
"chatSidebarPinSession": "置顶会话",
|
|
213
|
+
"chatSidebarUnpinSession": "取消置顶会话",
|
|
214
|
+
"chatSidebarPinProject": "置顶项目",
|
|
215
|
+
"chatSidebarUnpinProject": "取消置顶项目",
|
|
216
|
+
"chatSidebarExpandProject": "展开项目",
|
|
217
|
+
"chatSidebarCollapseProject": "收起项目",
|
|
202
218
|
"chatSidebarProjectViewEmpty": "还没有绑定项目的会话",
|
|
203
219
|
"chatWelcomeTitle": "你好,有什么可以帮你的吗?",
|
|
204
220
|
"chatWelcomeSubtitle": "描述目标,选择项目上下文,让 NextClaw 接住这次任务。",
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
"marketplace": "市场",
|
|
12
12
|
"advanced": "高级",
|
|
13
13
|
"settings": "设置",
|
|
14
|
+
"settingsGroupBasic": "基础配置",
|
|
15
|
+
"settingsGroupAdvanced": "高级配置",
|
|
14
16
|
"appearance": "外观",
|
|
15
17
|
"settingsMenu": "设置菜单",
|
|
16
18
|
"sidebarCollapse": "收起侧边栏",
|
|
@@ -41,6 +43,7 @@
|
|
|
41
43
|
"language": "语言",
|
|
42
44
|
"theme": "主题",
|
|
43
45
|
"themeNatural": "自然",
|
|
46
|
+
"themeWork": "默认",
|
|
44
47
|
"themeMinimal": "简白",
|
|
45
48
|
"themeWarm": "纸暖",
|
|
46
49
|
"themeCool": "雾蓝",
|
|
@@ -65,6 +65,8 @@
|
|
|
65
65
|
"serviceAppsLoading": "加载服务应用中...",
|
|
66
66
|
"serviceAppsLoadFailed": "服务应用加载失败",
|
|
67
67
|
"serviceAppsEmpty": "暂无服务应用",
|
|
68
|
+
"serviceAppsEmptyTitle": "暂无服务应用",
|
|
69
|
+
"serviceAppsEmptyDescription": "服务应用提供本地运行时与动作,供面板应用调用。",
|
|
68
70
|
"serviceAppsRefresh": "刷新服务应用",
|
|
69
71
|
"serviceAppsDiscoverActions": "连接并发现动作",
|
|
70
72
|
"serviceAppsDiscoverActionsHint": "连接服务应用运行时,并发现它当前可用的动作。",
|
|
@@ -84,6 +86,9 @@
|
|
|
84
86
|
"serviceAppsStatusHint_running": "服务应用运行时已连接,可用于发现或调用动作。",
|
|
85
87
|
"serviceAppsStatusHint_failed": "运行时连接失败,请查看错误信息或断开连接后重试。",
|
|
86
88
|
"serviceAppsStatusHint_stopped": "服务应用已停止。",
|
|
89
|
+
"serviceAppsActionsSection": "动作",
|
|
90
|
+
"serviceAppsActionsEmpty": "尚未发现动作",
|
|
91
|
+
"serviceAppsDetails": "运行时详情",
|
|
87
92
|
"serviceAppsGrantedTo": "已授权给",
|
|
88
93
|
"serviceAppsRevokeGrant": "撤销授权",
|
|
89
94
|
"serviceAppsRuntimeState_matched": "已匹配",
|
|
@@ -131,12 +131,13 @@ function readRelativeSegments(params: {
|
|
|
131
131
|
function buildSegmentsFromLabels(params: {
|
|
132
132
|
labels: string[];
|
|
133
133
|
basePath?: string | null;
|
|
134
|
+
currentKind?: "file" | "directory";
|
|
134
135
|
leading?: WorkspaceFileBreadcrumbSegmentViewModel | null;
|
|
135
136
|
}): WorkspaceFileBreadcrumbSegmentViewModel[] {
|
|
136
|
-
const { basePath = null, labels, leading = null } = params;
|
|
137
|
+
const { basePath = null, currentKind = "file", labels, leading = null } = params;
|
|
137
138
|
const items = labels.map<WorkspaceFileBreadcrumbSegmentViewModel>(
|
|
138
139
|
(label, index) => {
|
|
139
|
-
const kind = index === labels.length - 1 ?
|
|
140
|
+
const kind = index === labels.length - 1 ? currentKind : "directory";
|
|
140
141
|
const path = joinPathSegments(basePath, labels.slice(0, index + 1));
|
|
141
142
|
return {
|
|
142
143
|
key: `${index}:${label}`,
|
|
@@ -167,12 +168,13 @@ function buildLocationLabel(params: {
|
|
|
167
168
|
|
|
168
169
|
export function buildWorkspaceFileBreadcrumb(params: {
|
|
169
170
|
path: string;
|
|
171
|
+
kind?: "file" | "directory";
|
|
170
172
|
sessionProjectRoot: string | null;
|
|
171
173
|
line?: number | null;
|
|
172
174
|
column?: number | null;
|
|
173
175
|
truncated: boolean;
|
|
174
176
|
}): WorkspaceFileBreadcrumbViewModel {
|
|
175
|
-
const { column, line, path, sessionProjectRoot, truncated } = params;
|
|
177
|
+
const { column, kind = "file", line, path, sessionProjectRoot, truncated } = params;
|
|
176
178
|
const fullPath = path.trim();
|
|
177
179
|
const relativeSegments =
|
|
178
180
|
sessionProjectRoot?.trim() && fullPath
|
|
@@ -192,6 +194,7 @@ export function buildWorkspaceFileBreadcrumb(params: {
|
|
|
192
194
|
segments = buildSegmentsFromLabels({
|
|
193
195
|
labels: relativeSegments,
|
|
194
196
|
basePath: workspacePath,
|
|
197
|
+
currentKind: kind,
|
|
195
198
|
leading: {
|
|
196
199
|
key: `workspace:${workspaceLabel}`,
|
|
197
200
|
label: workspaceLabel,
|
|
@@ -207,6 +210,7 @@ export function buildWorkspaceFileBreadcrumb(params: {
|
|
|
207
210
|
segments = buildSegmentsFromLabels({
|
|
208
211
|
labels,
|
|
209
212
|
basePath: rootPath,
|
|
213
|
+
currentKind: kind,
|
|
210
214
|
leading: prefix
|
|
211
215
|
? {
|
|
212
216
|
key: `root:${prefix}`,
|
|
@@ -225,9 +229,9 @@ export function buildWorkspaceFileBreadcrumb(params: {
|
|
|
225
229
|
{
|
|
226
230
|
key: "file:unknown",
|
|
227
231
|
label: fullPath || "file",
|
|
228
|
-
kind
|
|
232
|
+
kind,
|
|
229
233
|
path: fullPath,
|
|
230
|
-
browsePath: readParentPath(fullPath),
|
|
234
|
+
browsePath: kind === "directory" ? fullPath : readParentPath(fullPath),
|
|
231
235
|
isCurrent: true,
|
|
232
236
|
},
|
|
233
237
|
];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_UI_THEME,
|
|
4
|
+
resolveInitialTheme,
|
|
5
|
+
THEME_OPTIONS,
|
|
6
|
+
} from './index';
|
|
7
|
+
|
|
8
|
+
describe('ui theme defaults', () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
window.localStorage.clear();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('uses the default theme when no preference is saved', () => {
|
|
14
|
+
expect(DEFAULT_UI_THEME).toBe('work');
|
|
15
|
+
expect(resolveInitialTheme()).toBe(DEFAULT_UI_THEME);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('lists the default theme first', () => {
|
|
19
|
+
expect(THEME_OPTIONS[0]?.value).toBe(DEFAULT_UI_THEME);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type UiTheme =
|
|
2
2
|
| 'natural'
|
|
3
|
+
| 'work'
|
|
3
4
|
| 'minimal'
|
|
4
5
|
| 'warm'
|
|
5
6
|
| 'cool'
|
|
@@ -16,9 +17,10 @@ type UiThemeDefinition = {
|
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
const THEME_STORAGE_KEY = 'nextclaw.ui.theme';
|
|
19
|
-
const
|
|
20
|
+
export const DEFAULT_UI_THEME: UiTheme = 'work';
|
|
20
21
|
|
|
21
22
|
const THEME_DEFINITIONS: readonly UiThemeDefinition[] = [
|
|
23
|
+
{ value: 'work', labelKey: 'themeWork', appearance: 'light' },
|
|
22
24
|
{ value: 'night', labelKey: 'themeNight', appearance: 'dark' },
|
|
23
25
|
{ value: 'natural', labelKey: 'themeNatural', appearance: 'light' },
|
|
24
26
|
{ value: 'minimal', labelKey: 'themeMinimal', appearance: 'light' },
|
|
@@ -59,13 +61,13 @@ export function getThemeAppearance(theme: UiTheme): UiThemeAppearance {
|
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
class UiThemeOwner {
|
|
62
|
-
private activeTheme: UiTheme =
|
|
64
|
+
private activeTheme: UiTheme = DEFAULT_UI_THEME;
|
|
63
65
|
private initialized = false;
|
|
64
66
|
private readonly listeners = new Set<(theme: UiTheme) => void>();
|
|
65
67
|
|
|
66
68
|
resolveInitialTheme = (): UiTheme => {
|
|
67
69
|
if (typeof window === 'undefined') {
|
|
68
|
-
return
|
|
70
|
+
return DEFAULT_UI_THEME;
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
try {
|
|
@@ -78,7 +80,7 @@ class UiThemeOwner {
|
|
|
78
80
|
// ignore storage failures
|
|
79
81
|
}
|
|
80
82
|
|
|
81
|
-
return
|
|
83
|
+
return DEFAULT_UI_THEME;
|
|
82
84
|
};
|
|
83
85
|
|
|
84
86
|
initializeTheme = (): UiTheme => {
|
package/src/vite-env.d.ts
CHANGED