@nextclaw/ui 0.15.20 → 0.15.22
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 +42 -0
- package/dist/assets/{api-tWUa6l9J.js → api-B1yHQYs-.js} +6 -6
- package/dist/assets/app-presenter-provider-uP9waOzv.js +108 -0
- package/dist/assets/appearance-settings-page-C9Zq3hP9.js +1 -0
- package/dist/assets/{book-open-BQ29ZmDE.js → book-open-oSmyVXea.js} +1 -1
- package/dist/assets/{card-CtKueklV.js → card-CGvJZTZp.js} +1 -1
- package/dist/assets/{channels-list-page-C8y3rqYt.js → channels-list-page-5BTxpQHw.js} +2 -2
- package/dist/assets/chat-page-jTvli8Jp.js +1 -0
- package/dist/assets/config-split-page-BgBsUmp1.js +1 -0
- package/dist/assets/confirm-dialog-h849QRWO.js +5 -0
- package/dist/assets/desktop-update-config-BGGHxClR.js +1 -0
- package/dist/assets/{dist-rYHnBo8t.js → dist-CxUhurq9.js} +1 -1
- package/dist/assets/doc-browser-0eL8AX42.js +1 -0
- package/dist/assets/{doc-browser-D8ng4EUc.js → doc-browser-DEqE9lsh.js} +1 -1
- package/dist/assets/{doc-browser-context-CPHNzQ8m.js → doc-browser-context-DkzFIUoK.js} +1 -1
- package/dist/assets/{eye-CiJz3ODY.js → eye-gOWQGt8N.js} +1 -1
- package/dist/assets/{form-actions-BnZVzLZf.js → form-actions-CgjYZUzM.js} +1 -1
- package/dist/assets/index-DSAUWCfl.js +2 -0
- package/dist/assets/index-G7cBryJ4.css +1 -0
- package/dist/assets/loader-circle-BAuycGf6.js +1 -0
- package/dist/assets/{logo-badge-BXI-Kdhd.js → logo-badge-BNcsDsUM.js} +1 -1
- package/dist/assets/{mcp-marketplace-page-CxNe-Q9Q.js → mcp-marketplace-page-BX39MtPa.js} +3 -3
- package/dist/assets/mcp-marketplace-page-CkDtOEA4.js +1 -0
- package/dist/assets/{model-config-page-CR_mgEVp.js → model-config-page-7MIXg2fQ.js} +1 -1
- package/dist/assets/{navigation-link-C1cvB7y4.js → navigation-link-B3iFOdxx.js} +1 -1
- package/dist/assets/plus-yvhFgfal.js +1 -0
- package/dist/assets/{popover-ZoDwVwwd.js → popover-Cpep_rJe.js} +1 -1
- package/dist/assets/{provider-models-CXUgDxGR.js → provider-models-CUrptZ-o.js} +1 -1
- package/dist/assets/provider-scoped-model-input-CHmOzz7x.js +1 -0
- package/dist/assets/providers-config-page-A6J7Y0II.js +1 -0
- package/dist/assets/react-BCJGCjrP.js +8 -0
- package/dist/assets/{refresh-cw-DKewcdAl.js → refresh-cw-CkKwPztT.js} +1 -1
- package/dist/assets/remote-FD-ZfSIn.js +1 -0
- package/dist/assets/rotate-cw-DrJMfHhp.js +1 -0
- package/dist/assets/runtime-config-page-DwjO-Vs6.js +1 -0
- package/dist/assets/{save-BrckuAXY.js → save-BINMWate.js} +1 -1
- package/dist/assets/{search-BhXQB2TO.js → search-7M38azRp.js} +1 -1
- package/dist/assets/{search-config-page-DFOQ6SDJ.js → search-config-page-T6tMhXbP.js} +1 -1
- package/dist/assets/{secrets-config-page-BqqeClqE.js → secrets-config-page-2mVejXf2.js} +2 -2
- package/dist/assets/security-config-DD5RrjDe.js +1 -0
- package/dist/assets/{select-CVzGGT38.js → select-BvidP5Oa.js} +1 -1
- package/dist/assets/{settings-2-BP6L2enG.js → settings-2-DqZstREV.js} +1 -1
- package/dist/assets/skeleton-BEQW2Dva.js +1 -0
- package/dist/assets/{tabs-B-QWjKCL.js → tabs-BeLxVmkt.js} +1 -1
- package/dist/assets/{tag-chip-C88mxjwx.js → tag-chip-BoDaLnWt.js} +1 -1
- package/dist/assets/{trash-2-ck3t33I3.js → trash-2-CYUskMMN.js} +1 -1
- package/dist/assets/{use-config-BAr5bH2c.js → use-config-CxKRXXCl.js} +1 -1
- package/dist/assets/use-confirm-dialog-HASTvOP2.js +1 -0
- package/dist/assets/{use-viewport-layout-k1isXfNm.js → use-viewport-layout-CJxkjoRz.js} +1 -1
- package/dist/assets/x-DdUwjre5.js +1 -0
- package/dist/index.html +29 -29
- package/package.json +8 -8
- package/src/app/components/__tests__/app-notification-runtime.test.tsx +32 -0
- package/src/app/components/app-notification-runtime.tsx +15 -0
- package/src/app/components/layout/sidebar-items.tsx +23 -9
- package/src/app/components/layout/sidebar-rail.styles.ts +0 -2
- package/src/app/components/layout/sidebar.tsx +2 -2
- package/src/app/configs/app-navigation.config.ts +21 -0
- package/src/app/index.tsx +22 -2
- package/src/app/presenters/app.presenter.ts +8 -0
- package/src/app/styles/design-system.css +12 -0
- package/src/features/chat/components/chat-sidebar-list-mode-switch.tsx +2 -2
- package/src/features/chat/components/conversation/__tests__/chat-conversation-content.test.tsx +19 -1
- package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +1 -1
- package/src/features/chat/components/conversation/chat-conversation-content.tsx +5 -6
- package/src/features/chat/components/conversation/chat-conversation-header.tsx +1 -1
- package/src/features/chat/components/layout/__tests__/chat-sidebar-read-state.test.tsx +1 -1
- package/src/features/chat/components/layout/__tests__/chat-sidebar-session-area.test.tsx +52 -0
- package/src/features/chat/components/layout/__tests__/chat-sidebar.test.tsx +11 -17
- package/src/features/chat/components/layout/chat-page-shell.tsx +9 -13
- package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +40 -38
- package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +13 -64
- package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +1 -4
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +1 -1
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +15 -4
- package/src/features/chat/features/conversation/components/session-conversation-area.tsx +7 -7
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +4 -2
- package/src/features/chat/features/conversation/hooks/use-session-conversation-input-attachments.ts +1 -0
- package/src/features/chat/features/conversation/utils/session-conversation-input-toolbar.utils.ts +1 -0
- package/src/features/chat/features/conversation/utils/session-queued-input.utils.ts +1 -0
- package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +3 -0
- package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +0 -1
- package/src/features/chat/features/input/utils/chat-input-toolbar.utils.ts +3 -0
- package/src/features/chat/features/message/utils/__tests__/chat-message-timeline.utils.test.ts +29 -0
- package/src/features/chat/features/message/utils/chat-message-timeline.utils.ts +4 -1
- package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +10 -10
- package/src/features/chat/features/session/components/chat-sidebar-session-list.tsx +2 -2
- package/src/features/chat/features/session/components/session-header/chat-session-header-menu-item.tsx +1 -1
- package/src/features/chat/features/session/components/session-header/chat-session-project-badge.tsx +1 -1
- package/src/features/chat/features/session/components/session-header/chat-session-switcher-groups.tsx +1 -1
- package/src/features/chat/features/session/components/session-header/chat-session-title-switcher.tsx +1 -1
- package/src/features/chat/features/session/utils/__tests__/chat-session-display.utils.test.ts +26 -0
- package/src/features/chat/features/session/utils/__tests__/ncp-session-adapter.utils.test.ts +4 -2
- package/src/features/chat/features/session/utils/chat-session-display.utils.ts +48 -12
- package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +13 -4
- package/src/features/chat/features/welcome/components/__tests__/chat-conversation-welcome.test.tsx +6 -0
- package/src/features/chat/features/welcome/components/__tests__/chat-welcome.test.tsx +10 -7
- package/src/features/chat/features/welcome/components/chat-welcome-agent-picker.tsx +1 -1
- package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +3 -14
- package/src/features/chat/features/welcome/components/chat-welcome-prompt-suggestions.tsx +37 -0
- package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +1 -1
- package/src/features/chat/features/welcome/components/chat-welcome.tsx +33 -31
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +1 -1
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +1 -1
- package/src/features/chat/index.ts +1 -0
- package/src/features/chat/managers/__tests__/chat-completion-notification.manager.test.ts +177 -0
- package/src/features/chat/managers/__tests__/chat-session-list.manager.test.ts +15 -3
- package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +3 -10
- package/src/features/chat/managers/chat-completion-notification.manager.ts +146 -0
- package/src/features/chat/managers/chat-session-list.manager.ts +3 -23
- package/src/features/chat/managers/chat-thread.manager.ts +2 -5
- package/src/features/chat/pages/__tests__/ncp-chat-page.test.tsx +32 -11
- package/src/features/chat/pages/ncp-chat-page.tsx +9 -1
- package/src/features/chat/types/chat-input-bar.types.ts +1 -0
- package/src/features/cron/components/cron-task-discovery.tsx +1 -1
- package/src/features/inbox/components/inbox-delivery-content.test.tsx +57 -0
- package/src/features/inbox/components/inbox-delivery-content.tsx +118 -0
- package/src/features/inbox/components/inbox-reader-dialog.test.tsx +77 -0
- package/src/features/inbox/components/inbox-reader-dialog.tsx +224 -0
- package/src/features/inbox/components/inbox-runtime.tsx +14 -0
- package/src/features/inbox/hooks/use-inbox-deliveries.ts +14 -0
- package/src/features/inbox/index.ts +7 -0
- package/src/features/inbox/managers/inbox.manager.test.ts +86 -0
- package/src/features/inbox/managers/inbox.manager.ts +178 -0
- package/src/features/inbox/pages/inbox-page.test.ts +28 -0
- package/src/features/inbox/pages/inbox-page.tsx +385 -0
- package/src/features/inbox/stores/inbox.store.ts +23 -0
- package/src/features/marketplace/components/marketplace-page-parts.tsx +1 -1
- package/src/features/notifications/components/__tests__/app-notification-toast.test.tsx +51 -0
- package/src/features/notifications/components/app-notification-toast.tsx +80 -0
- package/src/features/notifications/index.ts +8 -0
- package/src/features/notifications/managers/__tests__/app-notification.manager.test.ts +75 -0
- package/src/features/notifications/managers/app-notification.manager.ts +41 -0
- package/src/features/panel-apps/components/panel-app-list-item.tsx +1 -1
- package/src/features/panel-apps/components/panel-app-toolbar.tsx +2 -2
- package/src/features/panel-apps/components/panel-apps-list.tsx +4 -4
- package/src/features/service-apps/components/service-apps-panel.tsx +1 -1
- package/src/features/settings/components/config/provider-auth-section.tsx +90 -59
- package/src/features/settings/components/config/provider-form.tsx +10 -17
- package/src/features/settings/components/config/provider-status-badge.tsx +4 -2
- package/src/features/settings/pages/__tests__/providers-config-page.test.tsx +12 -0
- package/src/features/settings/pages/providers-config-page.tsx +2 -1
- package/src/platforms/mobile/components/__tests__/mobile-app-shell.test.tsx +13 -7
- package/src/platforms/mobile/components/__tests__/mobile-bottom-nav.test.tsx +18 -20
- package/src/platforms/mobile/components/mobile-bottom-nav.tsx +14 -7
- package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +1 -1
- package/src/shared/components/path-picker/server-path-picker-directory-list.tsx +1 -1
- package/src/shared/components/path-picker/server-path-picker-locations.tsx +1 -1
- package/src/shared/components/path-picker/server-path-picker-navigation-bar.tsx +3 -3
- package/src/shared/components/ui/actions/__tests__/icon-action-button.test.tsx +1 -1
- package/src/shared/components/ui/actions/icon-action-button.tsx +1 -1
- package/src/shared/components/ui/button.tsx +4 -4
- package/src/shared/components/ui/dialog.tsx +1 -1
- package/src/shared/components/ui/input.tsx +1 -1
- package/src/shared/components/ui/scroll-area.tsx +2 -0
- package/src/shared/components/ui/select.tsx +1 -1
- package/src/shared/components/ui/switch.tsx +1 -1
- package/src/shared/components/ui/tabs.tsx +1 -1
- package/src/shared/components/ui/textarea.tsx +1 -1
- package/src/shared/lib/api/ncp-session.types.ts +1 -0
- package/src/shared/lib/api/types.ts +2 -0
- package/src/shared/lib/i18n/index.ts +4 -0
- package/src/shared/lib/i18n/locales/en-US/chat.json +16 -14
- package/src/shared/lib/i18n/locales/en-US/inbox.json +30 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +16 -14
- package/src/shared/lib/i18n/locales/zh-CN/inbox.json +30 -0
- package/src/shared/lib/provider-models/__tests__/anonymous-provider-configuration.test.ts +40 -0
- package/src/shared/lib/provider-models/index.ts +4 -3
- package/dist/assets/app-presenter-provider-s-FrOinZ.js +0 -106
- package/dist/assets/appearance-settings-page-dMRBD7ni.js +0 -1
- package/dist/assets/chat-page-MXBltS3G.js +0 -1
- package/dist/assets/config-split-page-uC6d_r9c.js +0 -1
- package/dist/assets/confirm-dialog-C1pbqHZX.js +0 -5
- package/dist/assets/desktop-update-config-DssP8LCk.js +0 -1
- package/dist/assets/doc-browser-AXF1zRsM.js +0 -1
- package/dist/assets/index-CocHWEsI.js +0 -2
- package/dist/assets/index-DN6KM7WD.css +0 -1
- package/dist/assets/loader-circle-AOpK5kkW.js +0 -1
- package/dist/assets/mcp-marketplace-page-5ogeBHnl.js +0 -1
- package/dist/assets/plus-DI37QuHc.js +0 -1
- package/dist/assets/provider-scoped-model-input-DIL6Ex_d.js +0 -1
- package/dist/assets/providers-config-page-D2NXVctW.js +0 -1
- package/dist/assets/react-39CDNZlM.js +0 -8
- package/dist/assets/remote-DYOOO1YG.js +0 -1
- package/dist/assets/rotate-cw-PmqA8LQT.js +0 -1
- package/dist/assets/runtime-config-page-0lb5NyKc.js +0 -1
- package/dist/assets/security-config-13AtPIlm.js +0 -1
- package/dist/assets/skeleton-OwCNQJ7M.js +0 -1
- package/dist/assets/use-confirm-dialog-C4e0nG_4.js +0 -1
- package/dist/assets/x-C1ijNWcE.js +0 -1
- package/src/features/chat/features/welcome/components/chat-welcome-capability-grid.tsx +0 -59
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { fireEvent, render, screen } from "@testing-library/react";
|
|
2
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
2
3
|
import { MemoryRouter } from "react-router-dom";
|
|
3
4
|
import { describe, expect, it, vi } from "vitest";
|
|
4
5
|
import { I18nProvider } from "@/app/components/i18n-provider";
|
|
@@ -11,15 +12,24 @@ vi.mock("@/shared/components/doc-browser", () => ({
|
|
|
11
12
|
useDocBrowser: () => ({ open: vi.fn() }),
|
|
12
13
|
}));
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
function renderBottomNav(pathname: string) {
|
|
16
|
+
const queryClient = new QueryClient({
|
|
17
|
+
defaultOptions: { queries: { retry: false } },
|
|
18
|
+
});
|
|
19
|
+
return render(
|
|
20
|
+
<QueryClientProvider client={queryClient}>
|
|
17
21
|
<I18nProvider>
|
|
18
|
-
<MemoryRouter initialEntries={[
|
|
22
|
+
<MemoryRouter initialEntries={[pathname]}>
|
|
19
23
|
<MobileBottomNav />
|
|
20
24
|
</MemoryRouter>
|
|
21
|
-
</I18nProvider
|
|
22
|
-
|
|
25
|
+
</I18nProvider>
|
|
26
|
+
</QueryClientProvider>,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe("MobileBottomNav", () => {
|
|
31
|
+
it("highlights the settings tab for nested settings routes", () => {
|
|
32
|
+
renderBottomNav("/providers");
|
|
23
33
|
|
|
24
34
|
expect(
|
|
25
35
|
screen.getByRole("link", { name: /settings/i }).getAttribute("aria-current"),
|
|
@@ -37,13 +47,7 @@ describe("MobileBottomNav", () => {
|
|
|
37
47
|
});
|
|
38
48
|
|
|
39
49
|
it("highlights the chat tab for chat routes", () => {
|
|
40
|
-
|
|
41
|
-
<I18nProvider>
|
|
42
|
-
<MemoryRouter initialEntries={["/chat/demo-session"]}>
|
|
43
|
-
<MobileBottomNav />
|
|
44
|
-
</MemoryRouter>
|
|
45
|
-
</I18nProvider>,
|
|
46
|
-
);
|
|
50
|
+
renderBottomNav("/chat/demo-session");
|
|
47
51
|
|
|
48
52
|
expect(
|
|
49
53
|
screen.getByRole("link", { name: /chat/i }).getAttribute("aria-current"),
|
|
@@ -51,13 +55,7 @@ describe("MobileBottomNav", () => {
|
|
|
51
55
|
});
|
|
52
56
|
|
|
53
57
|
it("opens the apps panel from the mobile navigation", () => {
|
|
54
|
-
|
|
55
|
-
<I18nProvider>
|
|
56
|
-
<MemoryRouter initialEntries={["/chat"]}>
|
|
57
|
-
<MobileBottomNav />
|
|
58
|
-
</MemoryRouter>
|
|
59
|
-
</I18nProvider>,
|
|
60
|
-
);
|
|
58
|
+
renderBottomNav("/chat");
|
|
61
59
|
|
|
62
60
|
fireEvent.click(screen.getByRole("button", { name: "Apps" }));
|
|
63
61
|
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
matchesRouteTarget,
|
|
7
7
|
} from "@/app/configs/app-navigation.config";
|
|
8
8
|
import { openApps } from "@/features/panel-apps";
|
|
9
|
+
import { useInboxUnreadCount } from "@/features/inbox";
|
|
9
10
|
import { useDocBrowser } from "@/shared/components/doc-browser";
|
|
10
11
|
import { t } from "@/shared/lib/i18n";
|
|
11
12
|
import { cn } from "@/shared/lib/utils";
|
|
@@ -13,6 +14,7 @@ import { cn } from "@/shared/lib/utils";
|
|
|
13
14
|
export function MobileBottomNav() {
|
|
14
15
|
const { pathname } = useLocation();
|
|
15
16
|
const docBrowser = useDocBrowser();
|
|
17
|
+
const unreadCount = useInboxUnreadCount();
|
|
16
18
|
|
|
17
19
|
return (
|
|
18
20
|
<nav
|
|
@@ -23,7 +25,7 @@ export function MobileBottomNav() {
|
|
|
23
25
|
paddingBottom: "calc(env(safe-area-inset-bottom, 0px) + 0.25rem)",
|
|
24
26
|
}}
|
|
25
27
|
>
|
|
26
|
-
<ul className="grid grid-cols-
|
|
28
|
+
<ul className="grid grid-cols-6 gap-1 px-2 pt-1">
|
|
27
29
|
{getMobileBottomNavItems(t).map((item) => {
|
|
28
30
|
const active = item.target === "/settings"
|
|
29
31
|
? isSettingsRoute(pathname)
|
|
@@ -41,12 +43,17 @@ export function MobileBottomNav() {
|
|
|
41
43
|
: "text-gray-500 hover:bg-gray-50 hover:text-gray-800",
|
|
42
44
|
)}
|
|
43
45
|
>
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
<span className="relative">
|
|
47
|
+
<item.icon
|
|
48
|
+
className={cn(
|
|
49
|
+
"h-3.5 w-3.5",
|
|
50
|
+
active ? "text-gray-900" : "text-gray-400",
|
|
51
|
+
)}
|
|
52
|
+
/>
|
|
53
|
+
{item.target === "/inbox" && unreadCount > 0 ? (
|
|
54
|
+
<span className="absolute -right-1 -top-1 h-1.5 w-1.5 rounded-full bg-primary ring-1 ring-white" />
|
|
55
|
+
) : null}
|
|
56
|
+
</span>
|
|
50
57
|
<span className="max-w-full truncate">{item.label}</span>
|
|
51
58
|
</Link>
|
|
52
59
|
</li>
|
|
@@ -55,7 +55,7 @@ export function DocBrowserAddressToolbar({
|
|
|
55
55
|
onChange={(e) => onUrlInputChange(e.target.value)}
|
|
56
56
|
aria-label={t('docBrowserAddressLabel')}
|
|
57
57
|
placeholder={placeholder}
|
|
58
|
-
className="h-8 w-full rounded-lg border border-border/75 bg-background pl-8 pr-3 text-xs text-foreground transition-colors placeholder:text-muted-foreground/55 focus:
|
|
58
|
+
className="h-8 w-full rounded-lg border border-border/75 bg-background pl-8 pr-3 text-xs text-foreground transition-colors placeholder:text-muted-foreground/55 focus:outline-none focus:ring-0"
|
|
59
59
|
/>
|
|
60
60
|
</form>
|
|
61
61
|
<IconActionButton
|
|
@@ -63,7 +63,7 @@ export function ServerPathPickerDirectoryList({
|
|
|
63
63
|
'grid w-full grid-cols-[minmax(0,1fr)_8rem] items-center rounded-sm border border-transparent px-2 py-1.5 text-left text-sm text-foreground outline-none transition-colors',
|
|
64
64
|
selected
|
|
65
65
|
? 'border-primary/25 bg-primary/15'
|
|
66
|
-
: 'hover:bg-
|
|
66
|
+
: 'hover:bg-[var(--interaction-hover)] focus-visible:border-primary/35 focus-visible:bg-accent/70',
|
|
67
67
|
)}
|
|
68
68
|
onClick={() => onSelect(entry.path)}
|
|
69
69
|
onDoubleClick={() => onOpen(entry.path)}
|
|
@@ -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-primary/12 text-primary' : 'text-foreground hover:bg-
|
|
115
|
+
active ? 'bg-primary/12 text-primary' : 'text-foreground hover:bg-[var(--interaction-hover)]',
|
|
116
116
|
)}
|
|
117
117
|
aria-current={active ? 'location' : undefined}
|
|
118
118
|
onClick={() => onNavigate(item.path)}
|
|
@@ -99,7 +99,7 @@ export function ServerPathPickerNavigationBar({
|
|
|
99
99
|
disabled={!canGoUp || disabled}
|
|
100
100
|
/>
|
|
101
101
|
|
|
102
|
-
<div className="ml-1 flex h-9 min-w-0 flex-1 items-center overflow-hidden rounded-md border border-border bg-background shadow-sm
|
|
102
|
+
<div className="ml-1 flex h-9 min-w-0 flex-1 items-center overflow-hidden rounded-md border border-border bg-background shadow-sm">
|
|
103
103
|
{editingAddress ? (
|
|
104
104
|
<Input
|
|
105
105
|
value={addressDraft}
|
|
@@ -136,7 +136,7 @@ export function ServerPathPickerNavigationBar({
|
|
|
136
136
|
<>
|
|
137
137
|
<button
|
|
138
138
|
type="button"
|
|
139
|
-
className="rounded px-1.5 py-1 text-sm text-muted-foreground hover:bg-
|
|
139
|
+
className="rounded px-1.5 py-1 text-sm text-muted-foreground hover:bg-[var(--interaction-hover)]"
|
|
140
140
|
aria-label={t('pathPickerShowFullAddress')}
|
|
141
141
|
onClick={beginAddressEdit}
|
|
142
142
|
>
|
|
@@ -149,7 +149,7 @@ export function ServerPathPickerNavigationBar({
|
|
|
149
149
|
) : null}
|
|
150
150
|
<button
|
|
151
151
|
type="button"
|
|
152
|
-
className="max-w-40 truncate rounded px-1.5 py-1 text-sm text-foreground hover:bg-
|
|
152
|
+
className="max-w-40 truncate rounded px-1.5 py-1 text-sm text-foreground hover:bg-[var(--interaction-hover)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
|
|
153
153
|
onClick={() => onNavigate(breadcrumb.path)}
|
|
154
154
|
disabled={disabled}
|
|
155
155
|
>
|
|
@@ -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-
|
|
34
|
+
expect(button.className).toContain('hover:bg-[var(--interaction-hover)]');
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
it('ignores restored pointer focus while preserving keyboard tooltip focus', () => {
|
|
@@ -31,7 +31,7 @@ const SIZE_CLASS: Record<IconActionButtonSize, string> = {
|
|
|
31
31
|
|
|
32
32
|
const TONE_CLASS: Record<IconActionButtonTone, string> = {
|
|
33
33
|
default:
|
|
34
|
-
'text-muted-foreground hover:bg-
|
|
34
|
+
'text-muted-foreground hover:bg-[var(--interaction-hover)] hover:text-accent-foreground disabled:text-muted-foreground/45 disabled:hover:bg-transparent disabled:hover:text-muted-foreground/45',
|
|
35
35
|
strong:
|
|
36
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
37
|
};
|
|
@@ -9,12 +9,12 @@ const buttonVariants = cva(
|
|
|
9
9
|
variant: {
|
|
10
10
|
default: 'bg-primary text-primary-foreground hover:bg-primary-600 active:bg-primary-700 shadow-sm',
|
|
11
11
|
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
|
12
|
-
outline: 'border border-border bg-card text-muted-foreground hover:bg-
|
|
13
|
-
secondary: 'bg-muted text-foreground hover:bg-
|
|
14
|
-
ghost: 'text-muted-foreground hover:bg-
|
|
12
|
+
outline: 'border border-border bg-card text-muted-foreground hover:bg-[var(--interaction-hover)] hover:text-accent-foreground',
|
|
13
|
+
secondary: 'bg-muted text-foreground hover:bg-[var(--interaction-hover)] hover:text-accent-foreground',
|
|
14
|
+
ghost: 'text-muted-foreground hover:bg-[var(--interaction-hover)] hover:text-accent-foreground',
|
|
15
15
|
link: 'text-primary underline-offset-4 hover:underline',
|
|
16
16
|
primary: 'bg-primary text-primary-foreground hover:bg-primary-600 active:bg-primary-700 shadow-sm',
|
|
17
|
-
subtle: 'bg-muted text-muted-foreground hover:bg-
|
|
17
|
+
subtle: 'bg-muted text-muted-foreground hover:bg-[var(--interaction-hover)] hover:text-accent-foreground',
|
|
18
18
|
'primary-outline': 'border border-primary/30 text-primary hover:bg-primary hover:text-primary-foreground'
|
|
19
19
|
},
|
|
20
20
|
size: {
|
|
@@ -42,7 +42,7 @@ const DialogContent = React.forwardRef<
|
|
|
42
42
|
{...props}
|
|
43
43
|
>
|
|
44
44
|
{children}
|
|
45
|
-
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-lg p-1 opacity-70 transition-all duration-fast hover:bg-
|
|
45
|
+
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-lg p-1 opacity-70 transition-all duration-fast hover:bg-[var(--interaction-hover)] hover:text-accent-foreground hover:opacity-100 focus:outline-none focus-visible:ring-1 focus-visible:ring-border disabled:pointer-events-none">
|
|
46
46
|
<X className="h-4 w-4 text-muted-foreground" />
|
|
47
47
|
<span className="sr-only">Close</span>
|
|
48
48
|
</DialogPrimitive.Close>
|
|
@@ -9,7 +9,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
9
9
|
<input
|
|
10
10
|
type={type}
|
|
11
11
|
className={cn(
|
|
12
|
-
'flex h-9 w-full rounded-xl border border-border/75 bg-card px-3.5 py-2 text-sm text-foreground file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground/55 placeholder:font-normal transition-colors focus:outline-none focus:ring-0
|
|
12
|
+
'flex h-9 w-full rounded-xl border border-border/75 bg-card px-3.5 py-2 text-sm text-foreground file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground/55 placeholder:font-normal transition-colors focus:outline-none focus:ring-0 disabled:cursor-not-allowed disabled:opacity-50',
|
|
13
13
|
className
|
|
14
14
|
)}
|
|
15
15
|
ref={ref}
|
|
@@ -5,6 +5,8 @@ interface ScrollAreaProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
5
5
|
children: React.ReactNode;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
export const SCROLL_BOTTOM_EDGE_FADE_CLASS = '[mask-image:linear-gradient(to_bottom,black_calc(100%_-_28px),transparent)] [-webkit-mask-image:linear-gradient(to_bottom,black_calc(100%_-_28px),transparent)]';
|
|
9
|
+
|
|
8
10
|
const ScrollArea = React.forwardRef<HTMLDivElement, ScrollAreaProps>(
|
|
9
11
|
({ className, children, ...props }, ref) => (
|
|
10
12
|
<div
|
|
@@ -119,7 +119,7 @@ const SelectItem = React.forwardRef<
|
|
|
119
119
|
<SelectPrimitive.Item
|
|
120
120
|
ref={ref}
|
|
121
121
|
className={cn(
|
|
122
|
-
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 hover:bg-
|
|
122
|
+
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 hover:bg-[var(--interaction-hover)]",
|
|
123
123
|
className
|
|
124
124
|
)}
|
|
125
125
|
{...props}
|
|
@@ -18,7 +18,7 @@ const Switch = React.forwardRef<HTMLButtonElement, SwitchProps>(
|
|
|
18
18
|
ref={ref}
|
|
19
19
|
className={cn(
|
|
20
20
|
'switch-track peer inline-flex h-[22px] w-10 shrink-0 cursor-pointer items-center rounded-full border-2 transition-colors duration-fast focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border disabled:cursor-not-allowed disabled:opacity-50',
|
|
21
|
-
checked ? 'border-primary/70 bg-primary' : 'border-border/60 bg-muted hover:bg-
|
|
21
|
+
checked ? 'border-primary/70 bg-primary' : 'border-border/60 bg-muted hover:bg-[var(--interaction-hover)]',
|
|
22
22
|
className
|
|
23
23
|
)}
|
|
24
24
|
onClick={() => onCheckedChange?.(!checked)}
|
|
@@ -67,7 +67,7 @@ export function TabsTrigger({ value, children, className }: TabsTriggerProps) {
|
|
|
67
67
|
'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-[13px] font-medium transition-all duration-fast focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border disabled:pointer-events-none disabled:opacity-50',
|
|
68
68
|
isActive
|
|
69
69
|
? 'bg-card text-foreground shadow-sm'
|
|
70
|
-
: 'text-muted-foreground hover:bg-
|
|
70
|
+
: 'text-muted-foreground hover:bg-[var(--interaction-hover)] hover:text-accent-foreground',
|
|
71
71
|
className
|
|
72
72
|
)}
|
|
73
73
|
>
|
|
@@ -7,7 +7,7 @@ export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
7
7
|
({ className, ...props }, ref) => (
|
|
8
8
|
<textarea
|
|
9
9
|
className={cn(
|
|
10
|
-
"flex min-h-28 w-full rounded-xl border border-border/75 bg-card px-3.5 py-2.5 text-sm text-foreground placeholder:text-muted-foreground/55 placeholder:font-normal transition-colors focus:outline-none focus:ring-0
|
|
10
|
+
"flex min-h-28 w-full rounded-xl border border-border/75 bg-card px-3.5 py-2.5 text-sm text-foreground placeholder:text-muted-foreground/55 placeholder:font-normal transition-colors focus:outline-none focus:ring-0 disabled:cursor-not-allowed disabled:opacity-50",
|
|
11
11
|
className,
|
|
12
12
|
)}
|
|
13
13
|
ref={ref}
|
|
@@ -39,6 +39,7 @@ export type SessionContextWindowView = {
|
|
|
39
39
|
export type SessionActivityPreviewView = {
|
|
40
40
|
state: "running" | "completed" | "failed" | "cancelled" | "idle";
|
|
41
41
|
timestamp: string;
|
|
42
|
+
statusKind?: "thinking" | "tool-running" | "tool-completed" | "run-failed" | "run-interrupted";
|
|
42
43
|
statusText?: string;
|
|
43
44
|
replyText?: string;
|
|
44
45
|
};
|
|
@@ -68,6 +68,7 @@ export type ProviderInstanceView = {
|
|
|
68
68
|
isCustom: boolean;
|
|
69
69
|
enabled: boolean;
|
|
70
70
|
displayName?: string;
|
|
71
|
+
apiKeyRequired?: boolean;
|
|
71
72
|
apiKeySet: boolean;
|
|
72
73
|
apiKeyMasked?: string;
|
|
73
74
|
apiBase?: string | null;
|
|
@@ -621,6 +622,7 @@ export type ProviderTemplateView = {
|
|
|
621
622
|
envKey: string;
|
|
622
623
|
isGateway?: boolean;
|
|
623
624
|
isLocal?: boolean;
|
|
625
|
+
apiKeyRequired?: boolean;
|
|
624
626
|
defaultApiBase?: string;
|
|
625
627
|
logo?: string;
|
|
626
628
|
apiBaseHelp?: {
|
|
@@ -7,6 +7,7 @@ import zhCron from './locales/zh-CN/cron.json';
|
|
|
7
7
|
import zhDesktopUpdate from './locales/zh-CN/desktop-update.json';
|
|
8
8
|
import zhDocBrowser from './locales/zh-CN/doc-browser.json';
|
|
9
9
|
import zhMarketplace from './locales/zh-CN/marketplace.json';
|
|
10
|
+
import zhInbox from './locales/zh-CN/inbox.json';
|
|
10
11
|
import zhPathPicker from './locales/zh-CN/path-picker.json';
|
|
11
12
|
import zhPwa from './locales/zh-CN/pwa.json';
|
|
12
13
|
import zhRemote from './locales/zh-CN/remote.json';
|
|
@@ -21,6 +22,7 @@ import enCron from './locales/en-US/cron.json';
|
|
|
21
22
|
import enDesktopUpdate from './locales/en-US/desktop-update.json';
|
|
22
23
|
import enDocBrowser from './locales/en-US/doc-browser.json';
|
|
23
24
|
import enMarketplace from './locales/en-US/marketplace.json';
|
|
25
|
+
import enInbox from './locales/en-US/inbox.json';
|
|
24
26
|
import enPathPicker from './locales/en-US/path-picker.json';
|
|
25
27
|
import enPwa from './locales/en-US/pwa.json';
|
|
26
28
|
import enRemote from './locales/en-US/remote.json';
|
|
@@ -54,6 +56,7 @@ const zhCatalog: MessageCatalog = {
|
|
|
54
56
|
...zhChat,
|
|
55
57
|
...zhAgents,
|
|
56
58
|
...zhMarketplace,
|
|
59
|
+
...zhInbox,
|
|
57
60
|
...zhDocBrowser,
|
|
58
61
|
...zhPathPicker,
|
|
59
62
|
...zhPwa,
|
|
@@ -71,6 +74,7 @@ const enCatalog: MessageCatalog = {
|
|
|
71
74
|
...enChat,
|
|
72
75
|
...enAgents,
|
|
73
76
|
...enMarketplace,
|
|
77
|
+
...enInbox,
|
|
74
78
|
...enDocBrowser,
|
|
75
79
|
...enPathPicker,
|
|
76
80
|
...enPwa,
|
|
@@ -202,6 +202,7 @@
|
|
|
202
202
|
"chatRuntimeInitializationFailed": "Chat startup failed. Please retry in a moment or inspect the service logs.",
|
|
203
203
|
"chatInputPlaceholder": "Type a message, / for commands, apps or skills, @ for files, folders, projects or panel apps, Enter to send, Shift + Enter for newline",
|
|
204
204
|
"chatInputPlaceholderCompact": "Message NextClaw...",
|
|
205
|
+
"chatWelcomeInputPlaceholder": "Describe what you want to get done…",
|
|
205
206
|
"chatInputHint": "Multi-turn context is preserved in the current session.",
|
|
206
207
|
"chatSlashSection": "Slash menu",
|
|
207
208
|
"chatSlashSectionCommands": "Commands",
|
|
@@ -283,6 +284,7 @@
|
|
|
283
284
|
"chatModelSearchEmpty": "No matching models",
|
|
284
285
|
"chatFavoriteModel": "Add to favorites",
|
|
285
286
|
"chatUnfavoriteModel": "Remove from favorites",
|
|
287
|
+
"chatManageModels": "Manage models and providers",
|
|
286
288
|
"chatPickerAllSkills": "All skills",
|
|
287
289
|
"chatSend": "Send",
|
|
288
290
|
"chatStop": "Stop",
|
|
@@ -353,6 +355,7 @@
|
|
|
353
355
|
"chatProjectExistingAdded": "Project added",
|
|
354
356
|
"chatProjectExistingFailed": "Failed to add project",
|
|
355
357
|
"chatSessionErrorTitle": "Something went wrong",
|
|
358
|
+
"chatSessionActivityInterrupted": "The run was interrupted. Send the message again.",
|
|
356
359
|
"chatSendFailed": "Failed to send message",
|
|
357
360
|
"chatErrorDetails": "Details",
|
|
358
361
|
"chatRoleUser": "You",
|
|
@@ -419,24 +422,23 @@
|
|
|
419
422
|
"chatSidebarExpandProject": "Expand project",
|
|
420
423
|
"chatSidebarCollapseProject": "Collapse project",
|
|
421
424
|
"chatSidebarProjectViewEmpty": "No projects yet",
|
|
422
|
-
"
|
|
423
|
-
"
|
|
425
|
+
"chatBackgroundReplyFallbackTitle": "New reply",
|
|
426
|
+
"chatBackgroundReplyFallbackPreview": "A reply is ready. Open the session to view it.",
|
|
427
|
+
"chatBackgroundReplyOpenAriaLabel": "Open the new reply in {title}",
|
|
428
|
+
"chatWelcomeTitle": "What would you like to get done?",
|
|
429
|
+
"chatWelcomeContextLabel": "Task context",
|
|
424
430
|
"chatWelcomeProjectPickerLabel": "Choose working directory",
|
|
425
431
|
"chatWelcomeProjectPickerPlaceholder": "Choose working directory",
|
|
426
|
-
"chatWelcomeProjectDefaultBadge": "Default",
|
|
427
432
|
"chatWelcomeProjectRecentTitle": "Recent projects",
|
|
428
433
|
"chatWelcomeProjectNoRecent": "No recent projects yet",
|
|
429
434
|
"chatWelcomeProjectOpenFolder": "Open folder",
|
|
430
435
|
"chatWelcomeSessionTypePickerLabel": "Choose session type",
|
|
431
|
-
"
|
|
432
|
-
"
|
|
433
|
-
"
|
|
434
|
-
"
|
|
435
|
-
"
|
|
436
|
-
"
|
|
437
|
-
"chatWelcomeCapability3Title": "Scheduled Tasks",
|
|
438
|
-
"chatWelcomeCapability3Desc": "Set up scheduled automated tasks",
|
|
439
|
-
"chatWelcomeCapability3Prompt": "Help me design a scheduled automation for this project, including the trigger, message, and safety checks.",
|
|
436
|
+
"chatWelcomeSuggestion1Label": "Review this project",
|
|
437
|
+
"chatWelcomeSuggestion1Prompt": "Help me understand this project and propose the next three concrete things we should build.",
|
|
438
|
+
"chatWelcomeSuggestion2Label": "Find useful skills",
|
|
439
|
+
"chatWelcomeSuggestion2Prompt": "Review the skills that would be useful for this project and recommend which ones I should enable first.",
|
|
440
|
+
"chatWelcomeSuggestion3Label": "Create a scheduled task",
|
|
441
|
+
"chatWelcomeSuggestion3Prompt": "Help me design a scheduled automation for this project, including the trigger, message, and safety checks.",
|
|
440
442
|
"chatSkillsPickerTitle": "Skills",
|
|
441
443
|
"chatSkillsPickerEmpty": "No skills installed",
|
|
442
444
|
"chatSkillsPickerSearchPlaceholder": "Search skills",
|
|
@@ -444,9 +446,9 @@
|
|
|
444
446
|
"chatSkillsPickerOfficial": "Official",
|
|
445
447
|
"chatSkillsPickerManage": "Manage Skills",
|
|
446
448
|
"chatSkillPreviewUnavailable": "This skill file is unavailable. It may have been moved or removed.",
|
|
449
|
+
"chatInputAdd": "Add content",
|
|
447
450
|
"chatInputAttach": "Attach file",
|
|
448
451
|
"chatInputAttachmentUnsupported": "This file type is not supported in the current upload flow.",
|
|
449
452
|
"chatInputAttachmentTooLarge": "Files must be {maxMb} MB or smaller.",
|
|
450
|
-
"chatInputAttachmentReadFailed": "Failed to read the file. Please try again."
|
|
451
|
-
"chatInputAttachComingSoon": "Coming soon"
|
|
453
|
+
"chatInputAttachmentReadFailed": "Failed to read the file. Please try again."
|
|
452
454
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"inboxTitle": "Inbox",
|
|
3
|
+
"inboxDescription": "Reports, recommendations, and articles delivered by AI",
|
|
4
|
+
"inboxUnread": "Unread",
|
|
5
|
+
"inboxAll": "All",
|
|
6
|
+
"inboxArchived": "Archived",
|
|
7
|
+
"inboxEmptyTitle": "Nothing here yet",
|
|
8
|
+
"inboxEmptyDescription": "Reports, recommendations, and articles from AI will be saved here.",
|
|
9
|
+
"inboxSelectTitle": "Select something to read",
|
|
10
|
+
"inboxSelectDescription": "Open an item from the list and its reading state will stay in sync.",
|
|
11
|
+
"inboxDeliveredBy": "Delivered by AI",
|
|
12
|
+
"inboxReaderPosition": "{current} of {total}",
|
|
13
|
+
"inboxPrevious": "Previous",
|
|
14
|
+
"inboxNext": "Next",
|
|
15
|
+
"inboxOpenInbox": "Open in Inbox",
|
|
16
|
+
"inboxReadLater": "Read later",
|
|
17
|
+
"inboxMarkRead": "Mark as read",
|
|
18
|
+
"inboxMarkUnread": "Mark as unread",
|
|
19
|
+
"inboxArchive": "Archive",
|
|
20
|
+
"inboxRestore": "Restore",
|
|
21
|
+
"inboxDelete": "Delete",
|
|
22
|
+
"inboxContinueChat": "Continue in chat",
|
|
23
|
+
"inboxDeleteTitle": "Delete this item?",
|
|
24
|
+
"inboxDeleteDescription": "It cannot be recovered from Inbox. Any chat already created will remain.",
|
|
25
|
+
"inboxDeleteConfirm": "Delete",
|
|
26
|
+
"inboxLoadError": "Inbox could not be loaded. Try again shortly.",
|
|
27
|
+
"inboxActionError": "The action failed. Please try again.",
|
|
28
|
+
"inboxUnreadCount": "{count} unread",
|
|
29
|
+
"inboxNoSummary": "No summary"
|
|
30
|
+
}
|
|
@@ -202,6 +202,7 @@
|
|
|
202
202
|
"chatRuntimeInitializationFailed": "聊天能力启动失败,请稍后重试或检查服务日志。",
|
|
203
203
|
"chatInputPlaceholder": "输入消息,/ 选命令、应用或技能,@ 引用文件、目录、项目或面板应用,Enter 发送,Shift + Enter 换行",
|
|
204
204
|
"chatInputPlaceholderCompact": "发消息...",
|
|
205
|
+
"chatWelcomeInputPlaceholder": "描述你想完成的目标……",
|
|
205
206
|
"chatInputHint": "支持多轮上下文,默认走当前会话。",
|
|
206
207
|
"chatSlashSection": "斜杠菜单",
|
|
207
208
|
"chatSlashSectionCommands": "命令",
|
|
@@ -283,6 +284,7 @@
|
|
|
283
284
|
"chatModelSearchEmpty": "没有匹配的模型",
|
|
284
285
|
"chatFavoriteModel": "收藏模型",
|
|
285
286
|
"chatUnfavoriteModel": "取消收藏",
|
|
287
|
+
"chatManageModels": "管理模型与提供商",
|
|
286
288
|
"chatPickerAllSkills": "全部技能",
|
|
287
289
|
"chatSend": "发送",
|
|
288
290
|
"chatStop": "停止",
|
|
@@ -353,6 +355,7 @@
|
|
|
353
355
|
"chatProjectExistingAdded": "项目已添加",
|
|
354
356
|
"chatProjectExistingFailed": "添加项目失败",
|
|
355
357
|
"chatSessionErrorTitle": "出错了",
|
|
358
|
+
"chatSessionActivityInterrupted": "运行意外中断,请重新发送消息。",
|
|
356
359
|
"chatSendFailed": "发送消息失败",
|
|
357
360
|
"chatErrorDetails": "查看详情",
|
|
358
361
|
"chatRoleUser": "你",
|
|
@@ -419,24 +422,23 @@
|
|
|
419
422
|
"chatSidebarExpandProject": "展开项目",
|
|
420
423
|
"chatSidebarCollapseProject": "收起项目",
|
|
421
424
|
"chatSidebarProjectViewEmpty": "还没有项目",
|
|
422
|
-
"
|
|
423
|
-
"
|
|
425
|
+
"chatBackgroundReplyFallbackTitle": "有新回复",
|
|
426
|
+
"chatBackgroundReplyFallbackPreview": "回复已完成,点击查看。",
|
|
427
|
+
"chatBackgroundReplyOpenAriaLabel": "打开“{title}”的新回复",
|
|
428
|
+
"chatWelcomeTitle": "今天想完成什么?",
|
|
429
|
+
"chatWelcomeContextLabel": "任务上下文",
|
|
424
430
|
"chatWelcomeProjectPickerLabel": "选择工作目录",
|
|
425
431
|
"chatWelcomeProjectPickerPlaceholder": "选择工作目录",
|
|
426
|
-
"chatWelcomeProjectDefaultBadge": "默认",
|
|
427
432
|
"chatWelcomeProjectRecentTitle": "最近项目",
|
|
428
433
|
"chatWelcomeProjectNoRecent": "暂无最近项目",
|
|
429
434
|
"chatWelcomeProjectOpenFolder": "打开文件夹",
|
|
430
435
|
"chatWelcomeSessionTypePickerLabel": "选择会话类型",
|
|
431
|
-
"
|
|
432
|
-
"
|
|
433
|
-
"
|
|
434
|
-
"
|
|
435
|
-
"
|
|
436
|
-
"
|
|
437
|
-
"chatWelcomeCapability3Title": "定时任务",
|
|
438
|
-
"chatWelcomeCapability3Desc": "设置定时执行的自动化任务",
|
|
439
|
-
"chatWelcomeCapability3Prompt": "帮我设计一个适合当前项目的定时自动化任务,包括触发条件、消息内容和安全检查。",
|
|
436
|
+
"chatWelcomeSuggestion1Label": "梳理当前项目",
|
|
437
|
+
"chatWelcomeSuggestion1Prompt": "帮我理解当前项目,并给出接下来最值得推进的三个具体建设方向。",
|
|
438
|
+
"chatWelcomeSuggestion2Label": "推荐实用技能",
|
|
439
|
+
"chatWelcomeSuggestion2Prompt": "帮我看看这个项目适合启用哪些技能,并推荐我优先配置哪些能力。",
|
|
440
|
+
"chatWelcomeSuggestion3Label": "创建定时任务",
|
|
441
|
+
"chatWelcomeSuggestion3Prompt": "帮我设计一个适合当前项目的定时自动化任务,包括触发条件、消息内容和安全检查。",
|
|
440
442
|
"chatSkillsPickerTitle": "技能",
|
|
441
443
|
"chatSkillsPickerEmpty": "暂无已安装技能",
|
|
442
444
|
"chatSkillsPickerSearchPlaceholder": "搜索技能",
|
|
@@ -444,9 +446,9 @@
|
|
|
444
446
|
"chatSkillsPickerOfficial": "官方",
|
|
445
447
|
"chatSkillsPickerManage": "管理技能",
|
|
446
448
|
"chatSkillPreviewUnavailable": "无法打开这个技能文件,它可能已被移动或删除。",
|
|
449
|
+
"chatInputAdd": "添加内容",
|
|
447
450
|
"chatInputAttach": "添加附件",
|
|
448
451
|
"chatInputAttachmentUnsupported": "当前上传流程不支持该文件类型。",
|
|
449
452
|
"chatInputAttachmentTooLarge": "文件不能超过 {maxMb} MB。",
|
|
450
|
-
"chatInputAttachmentReadFailed": "读取文件失败,请重试。"
|
|
451
|
-
"chatInputAttachComingSoon": "即将支持"
|
|
453
|
+
"chatInputAttachmentReadFailed": "读取文件失败,请重试。"
|
|
452
454
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"inboxTitle": "收件箱",
|
|
3
|
+
"inboxDescription": "AI 主动送达的报告、推荐和文章",
|
|
4
|
+
"inboxUnread": "未读",
|
|
5
|
+
"inboxAll": "全部",
|
|
6
|
+
"inboxArchived": "已归档",
|
|
7
|
+
"inboxEmptyTitle": "这里还没有内容",
|
|
8
|
+
"inboxEmptyDescription": "AI 送达的报告、推荐和文章会保存在这里。",
|
|
9
|
+
"inboxSelectTitle": "选择一项开始阅读",
|
|
10
|
+
"inboxSelectDescription": "打开左侧列表中的内容,阅读状态会在这里同步。",
|
|
11
|
+
"inboxDeliveredBy": "由 AI 送达",
|
|
12
|
+
"inboxReaderPosition": "第 {current} 项,共 {total} 项",
|
|
13
|
+
"inboxPrevious": "上一项",
|
|
14
|
+
"inboxNext": "下一项",
|
|
15
|
+
"inboxOpenInbox": "在收件箱中打开",
|
|
16
|
+
"inboxReadLater": "稍后阅读",
|
|
17
|
+
"inboxMarkRead": "标记已读",
|
|
18
|
+
"inboxMarkUnread": "标记未读",
|
|
19
|
+
"inboxArchive": "归档",
|
|
20
|
+
"inboxRestore": "恢复",
|
|
21
|
+
"inboxDelete": "删除",
|
|
22
|
+
"inboxContinueChat": "继续聊",
|
|
23
|
+
"inboxDeleteTitle": "删除这项内容?",
|
|
24
|
+
"inboxDeleteDescription": "删除后将无法从收件箱恢复,但已经创建的会话不会被删除。",
|
|
25
|
+
"inboxDeleteConfirm": "删除",
|
|
26
|
+
"inboxLoadError": "无法加载收件箱,请稍后重试。",
|
|
27
|
+
"inboxActionError": "操作失败,请重试。",
|
|
28
|
+
"inboxUnreadCount": "{count} 项未读",
|
|
29
|
+
"inboxNoSummary": "没有摘要"
|
|
30
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { buildProviderModelCatalog } from '@/shared/lib/provider-models';
|
|
3
|
+
|
|
4
|
+
describe('anonymous provider configuration', () => {
|
|
5
|
+
it('includes an enabled provider that does not require a user API key', () => {
|
|
6
|
+
const catalog = buildProviderModelCatalog({
|
|
7
|
+
onlyConfigured: true,
|
|
8
|
+
providersView: {
|
|
9
|
+
providers: {
|
|
10
|
+
opencode: {
|
|
11
|
+
providerId: 'opencode',
|
|
12
|
+
providerType: 'opencode',
|
|
13
|
+
isBuiltInType: true,
|
|
14
|
+
isCustom: false,
|
|
15
|
+
enabled: true,
|
|
16
|
+
apiKeyRequired: false,
|
|
17
|
+
apiKeySet: false,
|
|
18
|
+
models: ['opencode/big-pickle']
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
templatesView: {
|
|
23
|
+
providerTemplates: [
|
|
24
|
+
{
|
|
25
|
+
id: 'opencode',
|
|
26
|
+
providerType: 'opencode',
|
|
27
|
+
displayName: 'OpenCode Zen Free Trial',
|
|
28
|
+
modelPrefix: 'opencode',
|
|
29
|
+
defaultModels: ['opencode/big-pickle'],
|
|
30
|
+
keywords: ['opencode'],
|
|
31
|
+
envKey: 'OPENCODE_API_KEY'
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(catalog).toHaveLength(1);
|
|
38
|
+
expect(catalog[0]?.models).toEqual(['big-pickle']);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -201,12 +201,13 @@ export function findProviderByModel(
|
|
|
201
201
|
return null;
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
function isProviderConfigured(provider: ProviderConfigView | undefined): boolean {
|
|
204
|
+
export function isProviderConfigured(provider: ProviderConfigView | undefined): boolean {
|
|
205
205
|
if (!provider) {
|
|
206
206
|
return false;
|
|
207
207
|
}
|
|
208
|
-
|
|
209
|
-
|
|
208
|
+
return provider.enabled !== false && (
|
|
209
|
+
provider.apiKeyRequired === false || provider.apiKeySet === true
|
|
210
|
+
);
|
|
210
211
|
}
|
|
211
212
|
|
|
212
213
|
export function buildProviderModelCatalog(params: {
|