@nextclaw/ui 0.15.20 → 0.15.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/assets/{api-tWUa6l9J.js → api-CHo2gekJ.js} +5 -5
  3. package/dist/assets/app-presenter-provider-2_DmWYT9.js +108 -0
  4. package/dist/assets/{appearance-settings-page-dMRBD7ni.js → appearance-settings-page-BEy2UE7R.js} +1 -1
  5. package/dist/assets/{book-open-BQ29ZmDE.js → book-open-BrZL4o9L.js} +1 -1
  6. package/dist/assets/{card-CtKueklV.js → card-B5rRLu_6.js} +1 -1
  7. package/dist/assets/{channels-list-page-C8y3rqYt.js → channels-list-page-C1dnm68I.js} +1 -1
  8. package/dist/assets/chat-page-Dz6tvZ91.js +1 -0
  9. package/dist/assets/{config-split-page-uC6d_r9c.js → config-split-page-6y6W6dz4.js} +1 -1
  10. package/dist/assets/{confirm-dialog-C1pbqHZX.js → confirm-dialog-q1CpEmR8.js} +1 -1
  11. package/dist/assets/{desktop-update-config-DssP8LCk.js → desktop-update-config-DrdlwP4j.js} +1 -1
  12. package/dist/assets/{dist-rYHnBo8t.js → dist-C9EX46wM.js} +1 -1
  13. package/dist/assets/doc-browser-BfttDSti.js +1 -0
  14. package/dist/assets/{doc-browser-D8ng4EUc.js → doc-browser-DH-3hDnI.js} +1 -1
  15. package/dist/assets/{doc-browser-context-CPHNzQ8m.js → doc-browser-context-TU2heon0.js} +1 -1
  16. package/dist/assets/{eye-CiJz3ODY.js → eye-B-3dyZNA.js} +1 -1
  17. package/dist/assets/{form-actions-BnZVzLZf.js → form-actions-DvvxCpL4.js} +1 -1
  18. package/dist/assets/index-CXDPLDFD.css +1 -0
  19. package/dist/assets/index-k_eWScvN.js +2 -0
  20. package/dist/assets/loader-circle-B35NlHTI.js +1 -0
  21. package/dist/assets/{logo-badge-BXI-Kdhd.js → logo-badge-DB51c0cS.js} +1 -1
  22. package/dist/assets/mcp-marketplace-page-CkPtwPBk.js +1 -0
  23. package/dist/assets/{mcp-marketplace-page-CxNe-Q9Q.js → mcp-marketplace-page-DWtsIqml.js} +1 -1
  24. package/dist/assets/{model-config-page-CR_mgEVp.js → model-config-page-D6cGNnMM.js} +1 -1
  25. package/dist/assets/{navigation-link-C1cvB7y4.js → navigation-link-BSJEUiwD.js} +1 -1
  26. package/dist/assets/plus-BRVe5jSJ.js +1 -0
  27. package/dist/assets/{popover-ZoDwVwwd.js → popover-Bp9IpE_N.js} +1 -1
  28. package/dist/assets/{provider-scoped-model-input-DIL6Ex_d.js → provider-scoped-model-input-dk7fz9NL.js} +1 -1
  29. package/dist/assets/{providers-config-page-D2NXVctW.js → providers-config-page-DTP2YfxA.js} +1 -1
  30. package/dist/assets/react-D6ShNq95.js +8 -0
  31. package/dist/assets/{refresh-cw-DKewcdAl.js → refresh-cw-C2e94pmp.js} +1 -1
  32. package/dist/assets/remote-FllhXW3o.js +1 -0
  33. package/dist/assets/rotate-cw-CTC-Qszi.js +1 -0
  34. package/dist/assets/runtime-config-page-C57OLdPm.js +1 -0
  35. package/dist/assets/{save-BrckuAXY.js → save-CkwDOgvt.js} +1 -1
  36. package/dist/assets/{search-BhXQB2TO.js → search-CLXM2reO.js} +1 -1
  37. package/dist/assets/{search-config-page-DFOQ6SDJ.js → search-config-page-tnJXaaSr.js} +1 -1
  38. package/dist/assets/{secrets-config-page-BqqeClqE.js → secrets-config-page-DQ_Curaj.js} +1 -1
  39. package/dist/assets/security-config-S00DjFfO.js +1 -0
  40. package/dist/assets/{select-CVzGGT38.js → select-CNFBf00l.js} +1 -1
  41. package/dist/assets/{settings-2-BP6L2enG.js → settings-2-B9IT2HGd.js} +1 -1
  42. package/dist/assets/skeleton-Dc06gatK.js +1 -0
  43. package/dist/assets/{tabs-B-QWjKCL.js → tabs-BuNr8LSO.js} +1 -1
  44. package/dist/assets/{tag-chip-C88mxjwx.js → tag-chip-CDkB5smU.js} +1 -1
  45. package/dist/assets/{trash-2-ck3t33I3.js → trash-2-GchaJuEM.js} +1 -1
  46. package/dist/assets/{use-config-BAr5bH2c.js → use-config-C69rR15i.js} +1 -1
  47. package/dist/assets/{use-confirm-dialog-C4e0nG_4.js → use-confirm-dialog-DF_EgPYx.js} +1 -1
  48. package/dist/assets/{use-viewport-layout-k1isXfNm.js → use-viewport-layout-BACNZyMY.js} +1 -1
  49. package/dist/assets/x-BXFquY9e.js +1 -0
  50. package/dist/index.html +28 -28
  51. package/package.json +7 -7
  52. package/src/app/components/__tests__/app-notification-runtime.test.tsx +32 -0
  53. package/src/app/components/app-notification-runtime.tsx +15 -0
  54. package/src/app/components/layout/sidebar-items.tsx +23 -9
  55. package/src/app/configs/app-navigation.config.ts +21 -0
  56. package/src/app/index.tsx +22 -2
  57. package/src/app/presenters/app.presenter.ts +8 -0
  58. package/src/features/chat/components/layout/chat-page-shell.tsx +7 -2
  59. package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +12 -0
  60. package/src/features/chat/index.ts +1 -0
  61. package/src/features/chat/managers/__tests__/chat-completion-notification.manager.test.ts +177 -0
  62. package/src/features/chat/managers/chat-completion-notification.manager.ts +146 -0
  63. package/src/features/chat/pages/__tests__/ncp-chat-page.test.tsx +29 -2
  64. package/src/features/chat/pages/ncp-chat-page.tsx +9 -0
  65. package/src/features/inbox/components/inbox-delivery-content.test.tsx +57 -0
  66. package/src/features/inbox/components/inbox-delivery-content.tsx +118 -0
  67. package/src/features/inbox/components/inbox-reader-dialog.test.tsx +77 -0
  68. package/src/features/inbox/components/inbox-reader-dialog.tsx +224 -0
  69. package/src/features/inbox/components/inbox-runtime.tsx +14 -0
  70. package/src/features/inbox/hooks/use-inbox-deliveries.ts +14 -0
  71. package/src/features/inbox/index.ts +7 -0
  72. package/src/features/inbox/managers/inbox.manager.test.ts +86 -0
  73. package/src/features/inbox/managers/inbox.manager.ts +178 -0
  74. package/src/features/inbox/pages/inbox-page.test.ts +28 -0
  75. package/src/features/inbox/pages/inbox-page.tsx +385 -0
  76. package/src/features/inbox/stores/inbox.store.ts +23 -0
  77. package/src/features/notifications/components/__tests__/app-notification-toast.test.tsx +48 -0
  78. package/src/features/notifications/components/app-notification-toast.tsx +80 -0
  79. package/src/features/notifications/index.ts +8 -0
  80. package/src/features/notifications/managers/__tests__/app-notification.manager.test.ts +75 -0
  81. package/src/features/notifications/managers/app-notification.manager.ts +41 -0
  82. package/src/platforms/mobile/components/mobile-bottom-nav.tsx +14 -7
  83. package/src/shared/lib/i18n/index.ts +4 -0
  84. package/src/shared/lib/i18n/locales/en-US/chat.json +3 -0
  85. package/src/shared/lib/i18n/locales/en-US/inbox.json +30 -0
  86. package/src/shared/lib/i18n/locales/zh-CN/chat.json +3 -0
  87. package/src/shared/lib/i18n/locales/zh-CN/inbox.json +30 -0
  88. package/dist/assets/app-presenter-provider-s-FrOinZ.js +0 -106
  89. package/dist/assets/chat-page-MXBltS3G.js +0 -1
  90. package/dist/assets/doc-browser-AXF1zRsM.js +0 -1
  91. package/dist/assets/index-CocHWEsI.js +0 -2
  92. package/dist/assets/index-DN6KM7WD.css +0 -1
  93. package/dist/assets/loader-circle-AOpK5kkW.js +0 -1
  94. package/dist/assets/mcp-marketplace-page-5ogeBHnl.js +0 -1
  95. package/dist/assets/plus-DI37QuHc.js +0 -1
  96. package/dist/assets/react-39CDNZlM.js +0 -8
  97. package/dist/assets/remote-DYOOO1YG.js +0 -1
  98. package/dist/assets/rotate-cw-PmqA8LQT.js +0 -1
  99. package/dist/assets/runtime-config-page-0lb5NyKc.js +0 -1
  100. package/dist/assets/security-config-13AtPIlm.js +0 -1
  101. package/dist/assets/skeleton-OwCNQJ7M.js +0 -1
  102. package/dist/assets/x-C1ijNWcE.js +0 -1
@@ -0,0 +1,80 @@
1
+ import { Link } from "react-router-dom";
2
+
3
+ export type AppNotificationToastProps = {
4
+ title: string;
5
+ description?: string;
6
+ href?: string;
7
+ iconSrc?: string;
8
+ ariaLabel?: string;
9
+ onDismiss: () => void;
10
+ };
11
+
12
+ const NOTIFICATION_CARD_CLASS =
13
+ "group ml-auto flex min-h-[74px] w-[320px] max-w-[calc(100vw-2rem)] items-center gap-3 rounded-[20px] border border-border/80 bg-background px-[18px] py-3 text-left text-foreground shadow-[0_8px_18px_rgba(0,0,0,0.12),0_2px_5px_rgba(0,0,0,0.06)] transition-colors duration-150 hover:bg-muted/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background active:bg-muted/20 motion-reduce:transition-none";
14
+
15
+ function AppNotificationContent({
16
+ title,
17
+ description,
18
+ iconSrc,
19
+ }: Pick<AppNotificationToastProps, "title" | "description" | "iconSrc">) {
20
+ return (
21
+ <>
22
+ <span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md border border-border/80 bg-background p-[3px]">
23
+ <img
24
+ aria-hidden="true"
25
+ alt=""
26
+ src={iconSrc ?? "/logo.svg"}
27
+ className="h-full w-full object-contain"
28
+ />
29
+ </span>
30
+ <span className="min-w-0 flex-1">
31
+ <span className="block truncate text-[15px] font-semibold leading-5 tracking-[-0.01em]">
32
+ {title}
33
+ </span>
34
+ {description ? (
35
+ <span className="mt-0.5 block truncate text-[14px] leading-5 text-muted-foreground">
36
+ {description}
37
+ </span>
38
+ ) : null}
39
+ </span>
40
+ </>
41
+ );
42
+ }
43
+
44
+ export function AppNotificationToast({
45
+ title,
46
+ description,
47
+ href,
48
+ iconSrc,
49
+ ariaLabel,
50
+ onDismiss,
51
+ }: AppNotificationToastProps) {
52
+ const accessibleLabel = ariaLabel ?? [title, description].filter(Boolean).join(": ");
53
+
54
+ if (href) {
55
+ return (
56
+ <Link
57
+ to={href}
58
+ aria-label={accessibleLabel}
59
+ className={NOTIFICATION_CARD_CLASS}
60
+ onClick={onDismiss}
61
+ >
62
+ <AppNotificationContent
63
+ title={title}
64
+ description={description}
65
+ iconSrc={iconSrc}
66
+ />
67
+ </Link>
68
+ );
69
+ }
70
+
71
+ return (
72
+ <div role="status" aria-label={accessibleLabel} className={NOTIFICATION_CARD_CLASS}>
73
+ <AppNotificationContent
74
+ title={title}
75
+ description={description}
76
+ iconSrc={iconSrc}
77
+ />
78
+ </div>
79
+ );
80
+ }
@@ -0,0 +1,8 @@
1
+ export {
2
+ AppNotificationToast,
3
+ type AppNotificationToastProps,
4
+ } from "./components/app-notification-toast";
5
+ export {
6
+ AppNotificationManager,
7
+ type AppNotification,
8
+ } from "./managers/app-notification.manager";
@@ -0,0 +1,75 @@
1
+ import { isValidElement } from "react";
2
+ import { beforeEach, describe, expect, it, vi } from "vitest";
3
+ import {
4
+ AppNotificationManager,
5
+ AppNotificationToast,
6
+ type AppNotificationToastProps,
7
+ } from "@/features/notifications";
8
+
9
+ const mocks = vi.hoisted(() => ({
10
+ custom: vi.fn(),
11
+ dismiss: vi.fn(),
12
+ }));
13
+
14
+ vi.mock("sonner", () => ({
15
+ toast: {
16
+ custom: mocks.custom,
17
+ dismiss: mocks.dismiss,
18
+ },
19
+ }));
20
+
21
+ describe("AppNotificationManager", () => {
22
+ beforeEach(() => {
23
+ mocks.custom.mockReset();
24
+ mocks.dismiss.mockReset();
25
+ mocks.custom.mockReturnValue("toast-result");
26
+ });
27
+
28
+ it("uses the shared custom notification surface and stable lifecycle defaults", () => {
29
+ const manager = new AppNotificationManager();
30
+ const result = manager.show({
31
+ id: "reply:message-1",
32
+ title: "Research complete",
33
+ description: "The findings are ready.",
34
+ href: "/chat/session-1",
35
+ });
36
+
37
+ expect(result).toBe("toast-result");
38
+ expect(mocks.custom).toHaveBeenCalledWith(expect.any(Function), {
39
+ id: "reply:message-1",
40
+ duration: 8_000,
41
+ unstyled: true,
42
+ });
43
+
44
+ const renderer = mocks.custom.mock.calls[0]?.[0] as (
45
+ id: string | number,
46
+ ) => unknown;
47
+ const element = renderer("toast-rendered");
48
+ expect(isValidElement<AppNotificationToastProps>(element)).toBe(true);
49
+ if (!isValidElement<AppNotificationToastProps>(element)) {
50
+ throw new Error("Expected AppNotificationToast element");
51
+ }
52
+ expect(element.type).toBe(AppNotificationToast);
53
+ expect(element.props).toEqual(expect.objectContaining({
54
+ title: "Research complete",
55
+ description: "The findings are ready.",
56
+ href: "/chat/session-1",
57
+ }));
58
+
59
+ element.props.onDismiss();
60
+ expect(mocks.dismiss).toHaveBeenCalledWith("toast-rendered");
61
+ });
62
+
63
+ it("supports a caller-owned duration and explicit dismiss", () => {
64
+ const manager = new AppNotificationManager();
65
+ manager.show({ title: "Saved", durationMs: 2_500 });
66
+ manager.dismiss("saved-toast");
67
+
68
+ expect(mocks.custom).toHaveBeenCalledWith(expect.any(Function), {
69
+ id: undefined,
70
+ duration: 2_500,
71
+ unstyled: true,
72
+ });
73
+ expect(mocks.dismiss).toHaveBeenCalledWith("saved-toast");
74
+ });
75
+ });
@@ -0,0 +1,41 @@
1
+ import { createElement } from "react";
2
+ import { toast } from "sonner";
3
+ import { AppNotificationToast } from "@/features/notifications/components/app-notification-toast";
4
+
5
+ export type AppNotification = {
6
+ id?: string | number;
7
+ title: string;
8
+ description?: string;
9
+ href?: string;
10
+ iconSrc?: string;
11
+ ariaLabel?: string;
12
+ durationMs?: number;
13
+ };
14
+
15
+ const DEFAULT_NOTIFICATION_DURATION_MS = 8_000;
16
+
17
+ export class AppNotificationManager {
18
+ show = (notification: AppNotification): string | number =>
19
+ toast.custom(
20
+ (toastId) =>
21
+ createElement(AppNotificationToast, {
22
+ title: notification.title,
23
+ description: notification.description,
24
+ href: notification.href,
25
+ iconSrc: notification.iconSrc,
26
+ ariaLabel: notification.ariaLabel,
27
+ onDismiss: () => {
28
+ toast.dismiss(toastId);
29
+ },
30
+ }),
31
+ {
32
+ id: notification.id,
33
+ duration: notification.durationMs ?? DEFAULT_NOTIFICATION_DURATION_MS,
34
+ unstyled: true,
35
+ },
36
+ );
37
+
38
+ dismiss = (id: string | number): void => {
39
+ toast.dismiss(id);
40
+ };
41
+ }
@@ -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-5 gap-1 px-2 pt-1">
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
- <item.icon
45
- className={cn(
46
- "h-3.5 w-3.5",
47
- active ? "text-gray-900" : "text-gray-400",
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>
@@ -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,
@@ -419,6 +419,9 @@
419
419
  "chatSidebarExpandProject": "Expand project",
420
420
  "chatSidebarCollapseProject": "Collapse project",
421
421
  "chatSidebarProjectViewEmpty": "No projects yet",
422
+ "chatBackgroundReplyFallbackTitle": "New reply",
423
+ "chatBackgroundReplyFallbackPreview": "A reply is ready. Open the session to view it.",
424
+ "chatBackgroundReplyOpenAriaLabel": "Open the new reply in {title}",
422
425
  "chatWelcomeTitle": "Hello, how can I help you?",
423
426
  "chatWelcomeSubtitle": "Describe the goal, choose the project context, and let NextClaw take it from there.",
424
427
  "chatWelcomeProjectPickerLabel": "Choose working directory",
@@ -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
+ }
@@ -419,6 +419,9 @@
419
419
  "chatSidebarExpandProject": "展开项目",
420
420
  "chatSidebarCollapseProject": "收起项目",
421
421
  "chatSidebarProjectViewEmpty": "还没有项目",
422
+ "chatBackgroundReplyFallbackTitle": "有新回复",
423
+ "chatBackgroundReplyFallbackPreview": "回复已完成,点击查看。",
424
+ "chatBackgroundReplyOpenAriaLabel": "打开“{title}”的新回复",
422
425
  "chatWelcomeTitle": "你好,有什么可以帮你的吗?",
423
426
  "chatWelcomeSubtitle": "描述目标,选择项目上下文,让 NextClaw 接住这次任务。",
424
427
  "chatWelcomeProjectPickerLabel": "选择工作目录",
@@ -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
+ }