@allbluecn/web-app 0.4.22 → 0.5.1

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 (99) hide show
  1. package/package.json +12 -8
  2. package/src/components/knowledge/knowledge-toolbar.tsx +124 -1
  3. package/src/components/layout/sidebar-03/app-sidebar.tsx +3 -4
  4. package/src/components/layout/sidebar-03/nav-notifications.tsx +135 -52
  5. package/src/components/layout/sidebar-03/user-menu.tsx +4 -2
  6. package/src/components/notifications/settings-notifications.tsx +198 -0
  7. package/src/components/notifications/team-notifications.tsx +343 -0
  8. package/src/components/settings/personal-team/team/team-client.tsx +51 -12
  9. package/src/components/settings/personal-team/team/team-invitations.tsx +181 -181
  10. package/src/components/shared/wave-physics-loader.tsx +155 -0
  11. package/src/components/story/__tests__/dependency-type-badge.test.ts +89 -0
  12. package/src/components/story/constants.ts +124 -51
  13. package/src/components/story/create-story-dialog.tsx +12 -6
  14. package/src/components/story/detail/__tests__/associated-attachments-panel.test.tsx +114 -0
  15. package/src/components/story/detail/activity-timeline.tsx +15 -0
  16. package/src/components/story/detail/story-detail-body.tsx +14 -3
  17. package/src/components/story/detail/story-detail-toolbar/associated-attachments-panel.tsx +748 -0
  18. package/src/components/story/detail/story-detail-toolbar/categories.tsx +23 -18
  19. package/src/components/story/detail/story-detail-toolbar/children.tsx +51 -274
  20. package/src/components/story/detail/story-detail-toolbar/dependencies.tsx +13 -45
  21. package/src/components/story/detail/story-detail-toolbar/dependency-panel.tsx +473 -0
  22. package/src/components/story/detail/story-detail-toolbar.tsx +237 -10
  23. package/src/components/story/display/__tests__/use-paged-story-groups.test.ts +152 -1
  24. package/src/components/story/display/use-paged-story-groups.ts +370 -28
  25. package/src/components/story/inline-editors/story-select-dialog.tsx +107 -16
  26. package/src/components/story/peek-panel.tsx +8 -1
  27. package/src/components/story/relations/__tests__/attachment-manage-dialog.test.tsx +171 -0
  28. package/src/components/story/relations/__tests__/knowledge-select-dialog.test.tsx +174 -0
  29. package/src/components/story/relations/__tests__/link-manage-dialog.test.tsx +93 -0
  30. package/src/components/story/relations/attachment-manage-dialog.tsx +490 -0
  31. package/src/components/story/relations/file-preview.tsx +173 -0
  32. package/src/components/story/relations/format.ts +120 -0
  33. package/src/{config/sidebar-notifications.ts → components/story/relations/general-groups.ts} +12 -30
  34. package/src/components/story/relations/knowledge-select-dialog.tsx +403 -0
  35. package/src/components/story/relations/link-manage-dialog.tsx +191 -0
  36. package/src/components/story/relations/story-ref-row.tsx +319 -0
  37. package/src/components/story/story-list-view.tsx +3 -1
  38. package/src/components/story/story-panel-header.tsx +4 -1
  39. package/src/components/story/types.ts +30 -18
  40. package/src/components/story/views/story-list/batch-toolbar.tsx +12 -1
  41. package/src/components/story/views/story-list/index.tsx +16 -5
  42. package/src/components/story/views/story-list/row.tsx +5 -4
  43. package/src/components/ui/separator.tsx +1 -1
  44. package/src/hooks/use-notifications.ts +4 -1
  45. package/src/lib/__tests__/datetime.test.ts +16 -1
  46. package/src/lib/changelog/sdk-versions.ts +19 -19
  47. package/src/lib/datetime.ts +1 -0
  48. package/src/lib/story/__tests__/video-embed.test.ts +77 -0
  49. package/src/lib/story/video-embed.ts +59 -0
  50. package/src/routeTree.gen.ts +65 -0
  51. package/src/routes/_nav/notifications/route.lazy.tsx +22 -0
  52. package/src/routes/_nav/notifications/route.tsx +30 -0
  53. package/src/routes/_nav/projects/$projectId/settings/route.lazy.tsx +74 -3
  54. package/src/routes/_nav/projects/$projectId/settings/route.tsx +6 -2
  55. package/src/routes/_nav/settings/notifications/route.lazy.tsx +6 -5
  56. package/src/routes/api/attachments/$id/content.ts +60 -0
  57. package/src/routes/api/attachments/upload.ts +148 -0
  58. package/src/routes/api/notifications/stream.ts +4 -0
  59. package/src/server/notifications/__tests__/notification-service.test.ts +189 -0
  60. package/src/server/notifications/notification-service.ts +137 -27
  61. package/src/server/serverFns/__tests__/project-kb.test.ts +147 -0
  62. package/src/server/serverFns/__tests__/team-invitation.test.ts +73 -7
  63. package/src/server/serverFns/__tests__/team-join-request.test.ts +5 -3
  64. package/src/server/serverFns/knowledge.ts +50 -4
  65. package/src/server/serverFns/locale.ts +15 -3
  66. package/src/server/serverFns/notifications.ts +18 -4
  67. package/src/server/serverFns/project.ts +43 -0
  68. package/src/server/serverFns/story/__tests__/kb-scope.test.ts +56 -0
  69. package/src/server/serverFns/story/__tests__/story-assoc.test.ts +222 -0
  70. package/src/server/serverFns/story/__tests__/story-dependencies.test.ts +265 -0
  71. package/src/server/serverFns/story/__tests__/story-groups.test.ts +19 -1
  72. package/src/server/serverFns/story/index.ts +7 -6
  73. package/src/server/serverFns/story/kb-general.ts +39 -0
  74. package/src/server/serverFns/story/kb-scope.ts +52 -0
  75. package/src/server/serverFns/story/link-preview.ts +115 -0
  76. package/src/server/serverFns/story/story-assoc.ts +339 -50
  77. package/src/server/serverFns/story/story-attachments.ts +12 -43
  78. package/src/server/serverFns/story/story-crud.ts +21 -4
  79. package/src/server/serverFns/story/story-dependencies.ts +138 -162
  80. package/src/server/serverFns/story/story-groups.ts +23 -3
  81. package/src/server/serverFns/story/story-links.ts +13 -1
  82. package/src/server/serverFns/story/story-transfer.ts +5 -3
  83. package/src/server/serverFns/story/story-tree.ts +28 -1
  84. package/src/server/serverFns/story/types.ts +37 -0
  85. package/src/server/serverFns/team.ts +51 -2
  86. package/src/styles/globals.css +25 -0
  87. package/vite.shared.ts +2 -0
  88. package/src/components/notifications/notification-list.tsx +0 -232
  89. package/src/components/notifications/notification-preferences.tsx +0 -156
  90. package/src/components/story/detail/story-detail-toolbar/attachments.tsx +0 -73
  91. package/src/components/story/detail/story-detail-toolbar/knowledge.tsx +0 -73
  92. package/src/components/story/detail/story-detail-toolbar/links.tsx +0 -73
  93. package/src/components/story/relation-badge.tsx +0 -57
  94. package/src/components/story/relations/attachments-panel.tsx +0 -179
  95. package/src/components/story/relations/dependency-panel.tsx +0 -190
  96. package/src/components/story/relations/hierarchy-panel.tsx +0 -180
  97. package/src/components/story/relations/knowledge-panel.tsx +0 -284
  98. package/src/components/story/relations/links-panel.tsx +0 -240
  99. package/src/components/story/relations/tab.tsx +0 -212
@@ -10,7 +10,7 @@ export const SDK_GROUPS: SdkGroup[] = [
10
10
  "name": "@tanstack/react-start",
11
11
  "displayName": "TanStack Start",
12
12
  "version": "v1.168.49",
13
- "lastUpdate": "Aug 29, 2026",
13
+ "lastUpdate": "Sep 1, 2026",
14
14
  "source": "apps/web/package.json#dependencies",
15
15
  "docsUrl": "https://tanstack.com/start/latest"
16
16
  },
@@ -18,7 +18,7 @@ export const SDK_GROUPS: SdkGroup[] = [
18
18
  "name": "@tanstack/react-router",
19
19
  "displayName": "TanStack Router",
20
20
  "version": "v1.170.32",
21
- "lastUpdate": "Aug 29, 2026",
21
+ "lastUpdate": "Sep 1, 2026",
22
22
  "source": "apps/web/package.json#dependencies",
23
23
  "docsUrl": "https://tanstack.com/router/latest"
24
24
  },
@@ -26,7 +26,7 @@ export const SDK_GROUPS: SdkGroup[] = [
26
26
  "name": "@tanstack/react-query",
27
27
  "displayName": "TanStack Query",
28
28
  "version": "v5.102.8",
29
- "lastUpdate": "Aug 29, 2026",
29
+ "lastUpdate": "Sep 1, 2026",
30
30
  "source": "apps/web/package.json#dependencies",
31
31
  "docsUrl": "https://tanstack.com/query/latest"
32
32
  },
@@ -34,7 +34,7 @@ export const SDK_GROUPS: SdkGroup[] = [
34
34
  "name": "@tanstack/ai",
35
35
  "displayName": "TanStack AI",
36
36
  "version": "v0.52.0",
37
- "lastUpdate": "Aug 29, 2026",
37
+ "lastUpdate": "Sep 1, 2026",
38
38
  "source": "apps/web/package.json#dependencies",
39
39
  "docsUrl": "https://tanstack.com/ai/latest"
40
40
  },
@@ -42,7 +42,7 @@ export const SDK_GROUPS: SdkGroup[] = [
42
42
  "name": "@tanstack/react-table",
43
43
  "displayName": "TanStack Table",
44
44
  "version": "v9.2.3",
45
- "lastUpdate": "Aug 29, 2026",
45
+ "lastUpdate": "Sep 1, 2026",
46
46
  "source": "apps/web/package.json#dependencies",
47
47
  "docsUrl": "https://tanstack.com/table/latest"
48
48
  },
@@ -50,7 +50,7 @@ export const SDK_GROUPS: SdkGroup[] = [
50
50
  "name": "@tanstack/react-form",
51
51
  "displayName": "TanStack Form",
52
52
  "version": "v2.0.0-alpha.2",
53
- "lastUpdate": "Aug 29, 2026",
53
+ "lastUpdate": "Sep 1, 2026",
54
54
  "source": "apps/web/package.json#dependencies",
55
55
  "docsUrl": "https://tanstack.com/form/latest"
56
56
  },
@@ -58,7 +58,7 @@ export const SDK_GROUPS: SdkGroup[] = [
58
58
  "name": "@tanstack/charts",
59
59
  "displayName": "TanStack Charts",
60
60
  "version": "v0.16.0",
61
- "lastUpdate": "Aug 29, 2026",
61
+ "lastUpdate": "Sep 1, 2026",
62
62
  "source": "apps/admin/package.json#dependencies",
63
63
  "docsUrl": "https://tanstack.com/charts/latest"
64
64
  },
@@ -66,7 +66,7 @@ export const SDK_GROUPS: SdkGroup[] = [
66
66
  "name": "@tanstack/react-pacer",
67
67
  "displayName": "TanStack Pacer",
68
68
  "version": "v0.23.0",
69
- "lastUpdate": "Aug 29, 2026",
69
+ "lastUpdate": "Sep 1, 2026",
70
70
  "source": "apps/web/package.json#dependencies",
71
71
  "docsUrl": "https://tanstack.com/pacer/latest"
72
72
  },
@@ -74,7 +74,7 @@ export const SDK_GROUPS: SdkGroup[] = [
74
74
  "name": "@tanstack/react-hotkeys",
75
75
  "displayName": "TanStack Hotkeys",
76
76
  "version": "v0.10.0",
77
- "lastUpdate": "Aug 29, 2026",
77
+ "lastUpdate": "Sep 1, 2026",
78
78
  "source": "apps/web/package.json#dependencies",
79
79
  "docsUrl": "https://tanstack.com/hotkeys/latest"
80
80
  },
@@ -82,7 +82,7 @@ export const SDK_GROUPS: SdkGroup[] = [
82
82
  "name": "@tanstack/react-virtual",
83
83
  "displayName": "TanStack Virtual",
84
84
  "version": "v3.14.10",
85
- "lastUpdate": "Aug 29, 2026",
85
+ "lastUpdate": "Sep 1, 2026",
86
86
  "source": "apps/admin/package.json#dependencies",
87
87
  "docsUrl": "https://tanstack.com/virtual/latest"
88
88
  }
@@ -95,15 +95,15 @@ export const SDK_GROUPS: SdkGroup[] = [
95
95
  "items": [
96
96
  {
97
97
  "name": "@allbluecn/web-app",
98
- "version": "v0.4.22",
99
- "lastUpdate": "Aug 29, 2026",
98
+ "version": "v0.5.1",
99
+ "lastUpdate": "Sep 1, 2026",
100
100
  "source": "apps/web/package.json",
101
101
  "docsUrl": "https://github.com/allbluecn/allblue"
102
102
  },
103
103
  {
104
104
  "name": "@allbluecn/kernel",
105
105
  "version": "v0.3.1",
106
- "lastUpdate": "Aug 29, 2026",
106
+ "lastUpdate": "Sep 1, 2026",
107
107
  "source": "packages/kernel/package.json",
108
108
  "docsUrl": "https://github.com/allbluecn/allblue"
109
109
  }
@@ -117,42 +117,42 @@ export const SDK_GROUPS: SdkGroup[] = [
117
117
  {
118
118
  "name": "Vite",
119
119
  "version": "v8.1.5",
120
- "lastUpdate": "Aug 29, 2026",
120
+ "lastUpdate": "Sep 1, 2026",
121
121
  "source": "apps/web/package.json#devDependencies",
122
122
  "docsUrl": "https://vite.dev/"
123
123
  },
124
124
  {
125
125
  "name": "Node.js",
126
126
  "version": "v24.19.0",
127
- "lastUpdate": "Aug 29, 2026",
127
+ "lastUpdate": "Sep 1, 2026",
128
128
  "source": ".nvmrc",
129
129
  "docsUrl": "https://nodejs.org/"
130
130
  },
131
131
  {
132
132
  "name": "Prisma",
133
133
  "version": "v7.8.0",
134
- "lastUpdate": "Aug 29, 2026",
134
+ "lastUpdate": "Sep 1, 2026",
135
135
  "source": "packages/database/package.json#devDependencies",
136
136
  "docsUrl": "https://www.prisma.io/docs"
137
137
  },
138
138
  {
139
139
  "name": "shadcn UI",
140
140
  "version": "v1.2.4",
141
- "lastUpdate": "Aug 29, 2026",
141
+ "lastUpdate": "Sep 1, 2026",
142
142
  "source": "apps/web/package.json#dependencies",
143
143
  "docsUrl": "https://ui.shadcn.com/"
144
144
  },
145
145
  {
146
146
  "name": "TipTap",
147
147
  "version": "v3.27.4",
148
- "lastUpdate": "Aug 29, 2026",
148
+ "lastUpdate": "Sep 1, 2026",
149
149
  "source": "packages/shared/package.json#devDependencies",
150
150
  "docsUrl": "https://tiptap.dev/"
151
151
  },
152
152
  {
153
153
  "name": "Lucide React",
154
154
  "version": "v1.24.0",
155
- "lastUpdate": "Aug 29, 2026",
155
+ "lastUpdate": "Sep 1, 2026",
156
156
  "source": "packages/ui/package.json#dependencies",
157
157
  "docsUrl": "https://lucide.dev/"
158
158
  }
@@ -3,4 +3,5 @@ export {
3
3
  formatDate,
4
4
  formatChatDate,
5
5
  formatRelative,
6
+ formatTimestamp,
6
7
  } from '@allbluecn/shared/lib/datetime'
@@ -0,0 +1,77 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { resolveVideoEmbed } from "../video-embed";
3
+
4
+ describe("resolveVideoEmbed", () => {
5
+ it("非视频链接返回 null", () => {
6
+ expect(resolveVideoEmbed("https://example.com/page")).toBeNull();
7
+ expect(resolveVideoEmbed("https://lucide.dev/icons/layout-list")).toBeNull();
8
+ });
9
+
10
+ it("直链视频文件走 file 分支(忽略 query/hash)", () => {
11
+ expect(resolveVideoEmbed("https://cdn.example.com/a.mp4")).toEqual({
12
+ kind: "file",
13
+ src: "https://cdn.example.com/a.mp4",
14
+ });
15
+ expect(
16
+ resolveVideoEmbed("https://cdn.example.com/a.webm?token=x#t=1"),
17
+ ).toEqual({ kind: "file", src: "https://cdn.example.com/a.webm?token=x#t=1" });
18
+ expect(resolveVideoEmbed("https://cdn.example.com/a.MOV")).toEqual({
19
+ kind: "file",
20
+ src: "https://cdn.example.com/a.MOV",
21
+ });
22
+ // 扩展名仅匹配路径末段,不匹配 query 里的伪扩展名
23
+ expect(resolveVideoEmbed("https://example.com/watch?file=a.mp4")).toBeNull();
24
+ });
25
+
26
+ it("YouTube watch 页转换为 embed 播放器", () => {
27
+ expect(resolveVideoEmbed("https://www.youtube.com/watch?v=dQw4w9WgXcQ")).toEqual({
28
+ kind: "iframe",
29
+ src: "https://www.youtube.com/embed/dQw4w9WgXcQ",
30
+ });
31
+ expect(
32
+ resolveVideoEmbed("https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=30s"),
33
+ ).toEqual({ kind: "iframe", src: "https://www.youtube.com/embed/dQw4w9WgXcQ" });
34
+ });
35
+
36
+ it("YouTube youtu.be 短链与 shorts 转换", () => {
37
+ expect(resolveVideoEmbed("https://youtu.be/dQw4w9WgXcQ")).toEqual({
38
+ kind: "iframe",
39
+ src: "https://www.youtube.com/embed/dQw4w9WgXcQ",
40
+ });
41
+ expect(resolveVideoEmbed("https://www.youtube.com/shorts/abc123")).toEqual({
42
+ kind: "iframe",
43
+ src: "https://www.youtube.com/embed/abc123",
44
+ });
45
+ // 已是 embed 链接原样返回
46
+ expect(resolveVideoEmbed("https://www.youtube.com/embed/dQw4w9WgXcQ")).toEqual({
47
+ kind: "iframe",
48
+ src: "https://www.youtube.com/embed/dQw4w9WgXcQ",
49
+ });
50
+ });
51
+
52
+ it("Bilibili 视频页转换为官方播放器(BV 号 + 分 P)", () => {
53
+ expect(
54
+ resolveVideoEmbed("https://www.bilibili.com/video/BV1GJ411x7h7/"),
55
+ ).toEqual({
56
+ kind: "iframe",
57
+ src: "https://player.bilibili.com/player.html?bvid=BV1GJ411x7h7&autoplay=0",
58
+ });
59
+ expect(
60
+ resolveVideoEmbed("https://www.bilibili.com/video/BV1GJ411x7h7?p=3"),
61
+ ).toEqual({
62
+ kind: "iframe",
63
+ src: "https://player.bilibili.com/player.html?bvid=BV1GJ411x7h7&autoplay=0&p=3",
64
+ });
65
+ });
66
+
67
+ it("Vimeo 转换为播放器", () => {
68
+ expect(resolveVideoEmbed("https://vimeo.com/76979871")).toEqual({
69
+ kind: "iframe",
70
+ src: "https://player.vimeo.com/video/76979871",
71
+ });
72
+ });
73
+
74
+ it("b23.tv 短链无法本地解析,返回 null 交给原有探测逻辑", () => {
75
+ expect(resolveVideoEmbed("https://b23.tv/BV1GJ411x7h7")).toBeNull();
76
+ });
77
+ });
@@ -0,0 +1,59 @@
1
+ // 视频链接识别与官方 embed 转换:供关联面板链接预览使用
2
+ // 返回 null 表示非视频链接(交由原有的可嵌入探测逻辑处理)
3
+
4
+ export type VideoEmbed =
5
+ | { kind: "file"; src: string }
6
+ | { kind: "iframe"; src: string };
7
+
8
+ const VIDEO_FILE_RE = /\.(mp4|webm|ogg|ogv|mov|m4v)$/i;
9
+
10
+ const YOUTUBE_ID_RE =
11
+ /^(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/(?:watch\?(?:.*&)?v=|shorts\/|embed\/)|youtu\.be\/)([A-Za-z0-9_-]{6,})/i;
12
+
13
+ const BILIBILI_BV_RE =
14
+ /^(?:https?:\/\/)?(?:www\.)?bilibili\.com\/video\/(BV[0-9A-Za-z]+)\/?(?:\?.*)?$/i;
15
+
16
+ const VIMEO_ID_RE = /^(?:https?:\/\/)?(?:www\.)?vimeo\.com\/(\d+)(?:\?.*)?$/i;
17
+
18
+ /** 解析 B 站分 P 参数(仅接受纯数字,防止参数注入) */
19
+ function getBilibiliPart(rawUrl: string): string | null {
20
+ try {
21
+ const p = new URL(rawUrl).searchParams.get("p");
22
+ return p && /^\d+$/.test(p) ? p : null;
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
27
+
28
+ export function resolveVideoEmbed(rawUrl: string): VideoEmbed | null {
29
+ if (!rawUrl) return null;
30
+
31
+ // 直链视频文件:扩展名只看 URL 路径末段,避免 query 中的伪扩展名误判
32
+ try {
33
+ const parsed = new URL(rawUrl);
34
+ if (VIDEO_FILE_RE.test(parsed.pathname)) {
35
+ return { kind: "file", src: rawUrl };
36
+ }
37
+ } catch {
38
+ return null;
39
+ }
40
+
41
+ const youtube = rawUrl.match(YOUTUBE_ID_RE);
42
+ if (youtube) {
43
+ return { kind: "iframe", src: `https://www.youtube.com/embed/${youtube[1]}` };
44
+ }
45
+
46
+ const bilibili = rawUrl.match(BILIBILI_BV_RE);
47
+ if (bilibili) {
48
+ const part = getBilibiliPart(rawUrl);
49
+ const src = `https://player.bilibili.com/player.html?bvid=${bilibili[1]}&autoplay=0${part ? `&p=${part}` : ""}`;
50
+ return { kind: "iframe", src };
51
+ }
52
+
53
+ const vimeo = rawUrl.match(VIMEO_ID_RE);
54
+ if (vimeo) {
55
+ return { kind: "iframe", src: `https://player.vimeo.com/video/${vimeo[1]}` };
56
+ }
57
+
58
+ return null;
59
+ }
@@ -31,6 +31,7 @@ import { Route as NavDashboardRouteRouteImport } from './routes/_nav/dashboard/r
31
31
  import { Route as NavDesignRouteRouteImport } from './routes/_nav/design/route'
32
32
  import { Route as NavInspirationRouteRouteImport } from './routes/_nav/inspiration/route'
33
33
  import { Route as NavKnowledgeRouteRouteImport } from './routes/_nav/knowledge/route'
34
+ import { Route as NavNotificationsRouteRouteImport } from './routes/_nav/notifications/route'
34
35
  import { Route as NavNotionLikeRouteRouteImport } from './routes/_nav/notion-like/route'
35
36
  import { Route as NavPrdRouteRouteImport } from './routes/_nav/prd/route'
36
37
  import { Route as NavPricing02RouteRouteImport } from './routes/_nav/pricing-02/route'
@@ -81,6 +82,7 @@ import { Route as NavTagsIdRouteRouteImport } from './routes/_nav/tags/$id/route
81
82
  import { Route as NavTagsSettingsRouteRouteImport } from './routes/_nav/tags/settings/route'
82
83
  import { Route as NavWorkspacesIndexRouteImport } from './routes/_nav/workspaces/index'
83
84
  import { Route as NavWorkspacesWorkspaceIdRouteRouteImport } from './routes/_nav/workspaces/$workspaceId/route'
85
+ import { Route as ApiAttachmentsUploadRouteImport } from './routes/api/attachments/upload'
84
86
  import { Route as ApiAuthSplatRouteImport } from './routes/api.auth.$'
85
87
  import { Route as ApiAuthDesktopCallbackRouteImport } from './routes/api/auth/desktop-callback'
86
88
  import { Route as ApiDesignUploadRouteImport } from './routes/api/design/upload'
@@ -99,6 +101,7 @@ import { Route as NavProjectsProjectIdStoryIdRouteRouteImport } from './routes/_
99
101
  import { Route as NavProjectsProjectIdSettingsRouteRouteImport } from './routes/_nav/projects/$projectId/settings/route'
100
102
  import { Route as NavSettingsAiProviderIdRouteRouteImport } from './routes/_nav/settings/ai/$providerId/route'
101
103
  import { Route as NavWorkspacesWorkspaceIdSettingsRouteRouteImport } from './routes/_nav/workspaces/$workspaceId/settings/route'
104
+ import { Route as ApiAttachmentsIdContentRouteImport } from './routes/api/attachments/$id/content'
102
105
  import { Route as DesignFilesProjectIdArtboardIdFilenameRouteImport } from './routes/design-files/$projectId/$artboardId/$filename'
103
106
  import { Route as NavKnowledgeKbIdDocDocIdRouteRouteImport } from './routes/_nav/knowledge/$kbId/doc/$docId/route'
104
107
  import { Route as ApiSharePrdPrdIdEventsRouteImport } from './routes/api/share/prd/$prdId/events'
@@ -259,6 +262,13 @@ const NavKnowledgeRouteRoute = NavKnowledgeRouteRouteImport.update({
259
262
  path: '/knowledge',
260
263
  getParentRoute: () => NavRouteRoute,
261
264
  } as any)
265
+ const NavNotificationsRouteRoute = NavNotificationsRouteRouteImport.update({
266
+ id: '/notifications',
267
+ path: '/notifications',
268
+ getParentRoute: () => NavRouteRoute,
269
+ } as any).lazy(() =>
270
+ import('./routes/_nav/notifications/route.lazy').then((d) => d.Route),
271
+ )
262
272
  const NavNotionLikeRouteRoute = NavNotionLikeRouteRouteImport.update({
263
273
  id: '/notion-like',
264
274
  path: '/notion-like',
@@ -567,6 +577,11 @@ const NavWorkspacesWorkspaceIdRouteRoute =
567
577
  (d) => d.Route,
568
578
  ),
569
579
  )
580
+ const ApiAttachmentsUploadRoute = ApiAttachmentsUploadRouteImport.update({
581
+ id: '/api/attachments/upload',
582
+ path: '/api/attachments/upload',
583
+ getParentRoute: () => rootRouteImport,
584
+ } as any)
570
585
  const ApiAuthSplatRoute = ApiAuthSplatRouteImport.update({
571
586
  id: '/api/auth/$',
572
587
  path: '/api/auth/$',
@@ -782,6 +797,11 @@ const NavWorkspacesWorkspaceIdSettingsRouteRoute =
782
797
  (d) => d.Route,
783
798
  ),
784
799
  )
800
+ const ApiAttachmentsIdContentRoute = ApiAttachmentsIdContentRouteImport.update({
801
+ id: '/api/attachments/$id/content',
802
+ path: '/api/attachments/$id/content',
803
+ getParentRoute: () => rootRouteImport,
804
+ } as any)
785
805
  const DesignFilesProjectIdArtboardIdFilenameRoute =
786
806
  DesignFilesProjectIdArtboardIdFilenameRouteImport.update({
787
807
  id: '/design-files/$projectId/$artboardId/$filename',
@@ -841,6 +861,7 @@ export interface FileRoutesByFullPath {
841
861
  '/design': typeof NavDesignRouteRouteWithChildren
842
862
  '/inspiration': typeof NavInspirationRouteRoute
843
863
  '/knowledge': typeof NavKnowledgeRouteRouteWithChildren
864
+ '/notifications': typeof NavNotificationsRouteRoute
844
865
  '/notion-like': typeof NavNotionLikeRouteRoute
845
866
  '/prd': typeof NavPrdRouteRouteWithChildren
846
867
  '/pricing-02': typeof NavPricing02RouteRoute
@@ -889,6 +910,7 @@ export interface FileRoutesByFullPath {
889
910
  '/share/law/$id': typeof ShareLawIdRouteRoute
890
911
  '/share/prd/$prdId': typeof SharePrdPrdIdRouteRoute
891
912
  '/chat/$threadId': typeof NavChatThreadIdRoute
913
+ '/api/attachments/upload': typeof ApiAttachmentsUploadRoute
892
914
  '/api/auth/$': typeof ApiAuthSplatRoute
893
915
  '/api/auth/desktop-callback': typeof ApiAuthDesktopCallbackRoute
894
916
  '/api/design/upload': typeof ApiDesignUploadRoute
@@ -907,6 +929,7 @@ export interface FileRoutesByFullPath {
907
929
  '/projects/$projectId/settings': typeof NavProjectsProjectIdSettingsRouteRoute
908
930
  '/settings/ai/$providerId': typeof NavSettingsAiProviderIdRouteRoute
909
931
  '/workspaces/$workspaceId/settings': typeof NavWorkspacesWorkspaceIdSettingsRouteRoute
932
+ '/api/attachments/$id/content': typeof ApiAttachmentsIdContentRoute
910
933
  '/design-files/$projectId/$artboardId/$filename': typeof DesignFilesProjectIdArtboardIdFilenameRoute
911
934
  '/settings/ai/': typeof NavSettingsAiIndexRouteLazyRoute
912
935
  '/knowledge/bookmarks/$id': typeof NavKnowledgeBookmarksIdLazyRoute
@@ -942,6 +965,7 @@ export interface FileRoutesByTo {
942
965
  '/chat': typeof NavChatRouteRouteWithChildren
943
966
  '/dashboard': typeof NavDashboardRouteRoute
944
967
  '/inspiration': typeof NavInspirationRouteRoute
968
+ '/notifications': typeof NavNotificationsRouteRoute
945
969
  '/notion-like': typeof NavNotionLikeRouteRoute
946
970
  '/pricing-02': typeof NavPricing02RouteRoute
947
971
  '/projects': typeof NavProjectsRouteRouteWithChildren
@@ -978,6 +1002,7 @@ export interface FileRoutesByTo {
978
1002
  '/share/law/$id': typeof ShareLawIdRouteRoute
979
1003
  '/share/prd/$prdId': typeof SharePrdPrdIdRouteRoute
980
1004
  '/chat/$threadId': typeof NavChatThreadIdRoute
1005
+ '/api/attachments/upload': typeof ApiAttachmentsUploadRoute
981
1006
  '/api/auth/$': typeof ApiAuthSplatRoute
982
1007
  '/api/auth/desktop-callback': typeof ApiAuthDesktopCallbackRoute
983
1008
  '/api/design/upload': typeof ApiDesignUploadRoute
@@ -996,6 +1021,7 @@ export interface FileRoutesByTo {
996
1021
  '/projects/$projectId/settings': typeof NavProjectsProjectIdSettingsRouteRoute
997
1022
  '/settings/ai/$providerId': typeof NavSettingsAiProviderIdRouteRoute
998
1023
  '/workspaces/$workspaceId/settings': typeof NavWorkspacesWorkspaceIdSettingsRouteRoute
1024
+ '/api/attachments/$id/content': typeof ApiAttachmentsIdContentRoute
999
1025
  '/design-files/$projectId/$artboardId/$filename': typeof DesignFilesProjectIdArtboardIdFilenameRoute
1000
1026
  '/settings/ai': typeof NavSettingsAiIndexRouteLazyRoute
1001
1027
  '/knowledge/bookmarks/$id': typeof NavKnowledgeBookmarksIdLazyRoute
@@ -1037,6 +1063,7 @@ export interface FileRoutesById {
1037
1063
  '/_nav/design': typeof NavDesignRouteRouteWithChildren
1038
1064
  '/_nav/inspiration': typeof NavInspirationRouteRoute
1039
1065
  '/_nav/knowledge': typeof NavKnowledgeRouteRouteWithChildren
1066
+ '/_nav/notifications': typeof NavNotificationsRouteRoute
1040
1067
  '/_nav/notion-like': typeof NavNotionLikeRouteRoute
1041
1068
  '/_nav/prd': typeof NavPrdRouteRouteWithChildren
1042
1069
  '/_nav/pricing-02': typeof NavPricing02RouteRoute
@@ -1085,6 +1112,7 @@ export interface FileRoutesById {
1085
1112
  '/share/law/$id': typeof ShareLawIdRouteRoute
1086
1113
  '/share/prd/$prdId': typeof SharePrdPrdIdRouteRoute
1087
1114
  '/_nav/chat/$threadId': typeof NavChatThreadIdRoute
1115
+ '/api/attachments/upload': typeof ApiAttachmentsUploadRoute
1088
1116
  '/api/auth/$': typeof ApiAuthSplatRoute
1089
1117
  '/api/auth/desktop-callback': typeof ApiAuthDesktopCallbackRoute
1090
1118
  '/api/design/upload': typeof ApiDesignUploadRoute
@@ -1103,6 +1131,7 @@ export interface FileRoutesById {
1103
1131
  '/_nav/projects/$projectId/settings': typeof NavProjectsProjectIdSettingsRouteRoute
1104
1132
  '/_nav/settings/ai/$providerId': typeof NavSettingsAiProviderIdRouteRoute
1105
1133
  '/_nav/workspaces/$workspaceId/settings': typeof NavWorkspacesWorkspaceIdSettingsRouteRoute
1134
+ '/api/attachments/$id/content': typeof ApiAttachmentsIdContentRoute
1106
1135
  '/design-files/$projectId/$artboardId/$filename': typeof DesignFilesProjectIdArtboardIdFilenameRoute
1107
1136
  '/_nav/settings/ai/': typeof NavSettingsAiIndexRouteLazyRoute
1108
1137
  '/_nav/knowledge/bookmarks/$id': typeof NavKnowledgeBookmarksIdLazyRoute
@@ -1142,6 +1171,7 @@ export interface FileRouteTypes {
1142
1171
  | '/design'
1143
1172
  | '/inspiration'
1144
1173
  | '/knowledge'
1174
+ | '/notifications'
1145
1175
  | '/notion-like'
1146
1176
  | '/prd'
1147
1177
  | '/pricing-02'
@@ -1190,6 +1220,7 @@ export interface FileRouteTypes {
1190
1220
  | '/share/law/$id'
1191
1221
  | '/share/prd/$prdId'
1192
1222
  | '/chat/$threadId'
1223
+ | '/api/attachments/upload'
1193
1224
  | '/api/auth/$'
1194
1225
  | '/api/auth/desktop-callback'
1195
1226
  | '/api/design/upload'
@@ -1208,6 +1239,7 @@ export interface FileRouteTypes {
1208
1239
  | '/projects/$projectId/settings'
1209
1240
  | '/settings/ai/$providerId'
1210
1241
  | '/workspaces/$workspaceId/settings'
1242
+ | '/api/attachments/$id/content'
1211
1243
  | '/design-files/$projectId/$artboardId/$filename'
1212
1244
  | '/settings/ai/'
1213
1245
  | '/knowledge/bookmarks/$id'
@@ -1243,6 +1275,7 @@ export interface FileRouteTypes {
1243
1275
  | '/chat'
1244
1276
  | '/dashboard'
1245
1277
  | '/inspiration'
1278
+ | '/notifications'
1246
1279
  | '/notion-like'
1247
1280
  | '/pricing-02'
1248
1281
  | '/projects'
@@ -1279,6 +1312,7 @@ export interface FileRouteTypes {
1279
1312
  | '/share/law/$id'
1280
1313
  | '/share/prd/$prdId'
1281
1314
  | '/chat/$threadId'
1315
+ | '/api/attachments/upload'
1282
1316
  | '/api/auth/$'
1283
1317
  | '/api/auth/desktop-callback'
1284
1318
  | '/api/design/upload'
@@ -1297,6 +1331,7 @@ export interface FileRouteTypes {
1297
1331
  | '/projects/$projectId/settings'
1298
1332
  | '/settings/ai/$providerId'
1299
1333
  | '/workspaces/$workspaceId/settings'
1334
+ | '/api/attachments/$id/content'
1300
1335
  | '/design-files/$projectId/$artboardId/$filename'
1301
1336
  | '/settings/ai'
1302
1337
  | '/knowledge/bookmarks/$id'
@@ -1337,6 +1372,7 @@ export interface FileRouteTypes {
1337
1372
  | '/_nav/design'
1338
1373
  | '/_nav/inspiration'
1339
1374
  | '/_nav/knowledge'
1375
+ | '/_nav/notifications'
1340
1376
  | '/_nav/notion-like'
1341
1377
  | '/_nav/prd'
1342
1378
  | '/_nav/pricing-02'
@@ -1385,6 +1421,7 @@ export interface FileRouteTypes {
1385
1421
  | '/share/law/$id'
1386
1422
  | '/share/prd/$prdId'
1387
1423
  | '/_nav/chat/$threadId'
1424
+ | '/api/attachments/upload'
1388
1425
  | '/api/auth/$'
1389
1426
  | '/api/auth/desktop-callback'
1390
1427
  | '/api/design/upload'
@@ -1403,6 +1440,7 @@ export interface FileRouteTypes {
1403
1440
  | '/_nav/projects/$projectId/settings'
1404
1441
  | '/_nav/settings/ai/$providerId'
1405
1442
  | '/_nav/workspaces/$workspaceId/settings'
1443
+ | '/api/attachments/$id/content'
1406
1444
  | '/design-files/$projectId/$artboardId/$filename'
1407
1445
  | '/_nav/settings/ai/'
1408
1446
  | '/_nav/knowledge/bookmarks/$id'
@@ -1438,12 +1476,14 @@ export interface RootRouteChildren {
1438
1476
  ServerWsRoute: typeof ServerWsRoute
1439
1477
  ApiLicenseActivateRouteRoute: typeof ApiLicenseActivateRouteRoute
1440
1478
  ApiLicenseHeartbeatRouteRoute: typeof ApiLicenseHeartbeatRouteRoute
1479
+ ApiAttachmentsUploadRoute: typeof ApiAttachmentsUploadRoute
1441
1480
  ApiAuthSplatRoute: typeof ApiAuthSplatRoute
1442
1481
  ApiAuthDesktopCallbackRoute: typeof ApiAuthDesktopCallbackRoute
1443
1482
  ApiDesignUploadRoute: typeof ApiDesignUploadRoute
1444
1483
  ApiKnowledgeSyncGithubRoute: typeof ApiKnowledgeSyncGithubRoute
1445
1484
  ApiNotificationsStreamRoute: typeof ApiNotificationsStreamRoute
1446
1485
  ServerAnyhubProxySplatRoute: typeof ServerAnyhubProxySplatRoute
1486
+ ApiAttachmentsIdContentRoute: typeof ApiAttachmentsIdContentRoute
1447
1487
  DesignFilesProjectIdArtboardIdFilenameRoute: typeof DesignFilesProjectIdArtboardIdFilenameRoute
1448
1488
  ApiSharePrdPrdIdEventsRoute: typeof ApiSharePrdPrdIdEventsRoute
1449
1489
  }
@@ -1590,6 +1630,13 @@ declare module '@tanstack/react-router' {
1590
1630
  preLoaderRoute: typeof NavKnowledgeRouteRouteImport
1591
1631
  parentRoute: typeof NavRouteRoute
1592
1632
  }
1633
+ '/_nav/notifications': {
1634
+ id: '/_nav/notifications'
1635
+ path: '/notifications'
1636
+ fullPath: '/notifications'
1637
+ preLoaderRoute: typeof NavNotificationsRouteRouteImport
1638
+ parentRoute: typeof NavRouteRoute
1639
+ }
1593
1640
  '/_nav/notion-like': {
1594
1641
  id: '/_nav/notion-like'
1595
1642
  path: '/notion-like'
@@ -1940,6 +1987,13 @@ declare module '@tanstack/react-router' {
1940
1987
  preLoaderRoute: typeof NavWorkspacesWorkspaceIdRouteRouteImport
1941
1988
  parentRoute: typeof NavWorkspacesRouteRoute
1942
1989
  }
1990
+ '/api/attachments/upload': {
1991
+ id: '/api/attachments/upload'
1992
+ path: '/api/attachments/upload'
1993
+ fullPath: '/api/attachments/upload'
1994
+ preLoaderRoute: typeof ApiAttachmentsUploadRouteImport
1995
+ parentRoute: typeof rootRouteImport
1996
+ }
1943
1997
  '/api/auth/$': {
1944
1998
  id: '/api/auth/$'
1945
1999
  path: '/api/auth/$'
@@ -2143,6 +2197,13 @@ declare module '@tanstack/react-router' {
2143
2197
  preLoaderRoute: typeof NavWorkspacesWorkspaceIdSettingsRouteRouteImport
2144
2198
  parentRoute: typeof NavWorkspacesWorkspaceIdRouteRoute
2145
2199
  }
2200
+ '/api/attachments/$id/content': {
2201
+ id: '/api/attachments/$id/content'
2202
+ path: '/api/attachments/$id/content'
2203
+ fullPath: '/api/attachments/$id/content'
2204
+ preLoaderRoute: typeof ApiAttachmentsIdContentRouteImport
2205
+ parentRoute: typeof rootRouteImport
2206
+ }
2146
2207
  '/design-files/$projectId/$artboardId/$filename': {
2147
2208
  id: '/design-files/$projectId/$artboardId/$filename'
2148
2209
  path: '/design-files/$projectId/$artboardId/$filename'
@@ -2543,6 +2604,7 @@ interface NavRouteRouteChildren {
2543
2604
  NavDesignRouteRoute: typeof NavDesignRouteRouteWithChildren
2544
2605
  NavInspirationRouteRoute: typeof NavInspirationRouteRoute
2545
2606
  NavKnowledgeRouteRoute: typeof NavKnowledgeRouteRouteWithChildren
2607
+ NavNotificationsRouteRoute: typeof NavNotificationsRouteRoute
2546
2608
  NavNotionLikeRouteRoute: typeof NavNotionLikeRouteRoute
2547
2609
  NavPrdRouteRoute: typeof NavPrdRouteRouteWithChildren
2548
2610
  NavPricing02RouteRoute: typeof NavPricing02RouteRoute
@@ -2565,6 +2627,7 @@ const NavRouteRouteChildren: NavRouteRouteChildren = {
2565
2627
  NavDesignRouteRoute: NavDesignRouteRouteWithChildren,
2566
2628
  NavInspirationRouteRoute: NavInspirationRouteRoute,
2567
2629
  NavKnowledgeRouteRoute: NavKnowledgeRouteRouteWithChildren,
2630
+ NavNotificationsRouteRoute: NavNotificationsRouteRoute,
2568
2631
  NavNotionLikeRouteRoute: NavNotionLikeRouteRoute,
2569
2632
  NavPrdRouteRoute: NavPrdRouteRouteWithChildren,
2570
2633
  NavPricing02RouteRoute: NavPricing02RouteRoute,
@@ -2628,12 +2691,14 @@ const rootRouteChildren: RootRouteChildren = {
2628
2691
  ServerWsRoute: ServerWsRoute,
2629
2692
  ApiLicenseActivateRouteRoute: ApiLicenseActivateRouteRoute,
2630
2693
  ApiLicenseHeartbeatRouteRoute: ApiLicenseHeartbeatRouteRoute,
2694
+ ApiAttachmentsUploadRoute: ApiAttachmentsUploadRoute,
2631
2695
  ApiAuthSplatRoute: ApiAuthSplatRoute,
2632
2696
  ApiAuthDesktopCallbackRoute: ApiAuthDesktopCallbackRoute,
2633
2697
  ApiDesignUploadRoute: ApiDesignUploadRoute,
2634
2698
  ApiKnowledgeSyncGithubRoute: ApiKnowledgeSyncGithubRoute,
2635
2699
  ApiNotificationsStreamRoute: ApiNotificationsStreamRoute,
2636
2700
  ServerAnyhubProxySplatRoute: ServerAnyhubProxySplatRoute,
2701
+ ApiAttachmentsIdContentRoute: ApiAttachmentsIdContentRoute,
2637
2702
  DesignFilesProjectIdArtboardIdFilenameRoute:
2638
2703
  DesignFilesProjectIdArtboardIdFilenameRoute,
2639
2704
  ApiSharePrdPrdIdEventsRoute: ApiSharePrdPrdIdEventsRoute,
@@ -0,0 +1,22 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+
3
+ import { createLazyFileRoute } from '@tanstack/react-router'
4
+ import { useTranslation } from 'react-i18next'
5
+ import { TeamNotificationsPanel } from '@/components/notifications/team-notifications'
6
+
7
+ export const Route = createLazyFileRoute('/_nav/notifications')({
8
+ component: NotificationsPage,
9
+ })
10
+
11
+ function NotificationsPage() {
12
+ const { t } = useTranslation('notifications')
13
+ return (
14
+ <div className="flex h-full flex-col gap-6 overflow-hidden p-6">
15
+ <div className="flex shrink-0 flex-col gap-1">
16
+ <h1 className="text-xl font-semibold">{t('title')}</h1>
17
+ <p className="text-sm text-muted-foreground">{t('description')}</p>
18
+ </div>
19
+ <TeamNotificationsPanel />
20
+ </div>
21
+ )
22
+ }
@@ -0,0 +1,30 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+
3
+ import { createFileRoute } from '@tanstack/react-router'
4
+ import type { NotificationListResult } from '@allbluecn/shared'
5
+ import { NOTIFICATION_LIST_KEY, NOTIFICATION_UNREAD_KEY } from '@/hooks/use-notifications'
6
+ import { getUnreadCountFn, listNotificationsFn } from '@/server/serverFns/notifications'
7
+
8
+ export const Route = createFileRoute('/_nav/notifications')({
9
+ loader: async ({ context }) => {
10
+ const { queryClient } = context
11
+ // SSR 预取未读数 + 列表第一页,保证服务端与客户端 hydrate 首帧渲染一致
12
+ // (否则 SSR 渲染空态、客户端首帧已有数据 → hydration mismatch 整树重渲染)
13
+ await Promise.all([
14
+ queryClient.ensureQueryData({
15
+ queryKey: NOTIFICATION_UNREAD_KEY,
16
+ queryFn: () => getUnreadCountFn({ data: {} }),
17
+ }),
18
+ queryClient.fetchInfiniteQuery({
19
+ queryKey: [...NOTIFICATION_LIST_KEY, undefined],
20
+ queryFn: ({ pageParam }) =>
21
+ listNotificationsFn({ data: pageParam ? { cursor: pageParam } : {} }),
22
+ initialPageParam: undefined as string | undefined,
23
+ getNextPageParam: (lastPage: NotificationListResult) =>
24
+ lastPage.hasMore ? lastPage.items[lastPage.items.length - 1].id : undefined,
25
+ }),
26
+ ]).catch(() => {
27
+ // 预取失败降级为客户端 fetch(与旧行为一致),不阻塞页面
28
+ })
29
+ },
30
+ })