@allbluecn/web-app 0.9.1 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -2
- package/src/components/devtools/ai-chat-panel.tsx +54 -0
- package/src/components/devtools/devtools-panel.tsx +100 -0
- package/src/components/devtools/index.ts +20 -0
- package/src/components/devtools/session-panel.tsx +102 -0
- package/src/lib/changelog/sdk-versions.ts +1 -8
- package/src/lib/perf.ts +1 -1
- package/src/routes/__root.tsx +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allbluecn/web-app",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"license": "AGPL-3.0-or-later",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -202,7 +202,6 @@
|
|
|
202
202
|
"@allbluecn/kernel": "0.5.1"
|
|
203
203
|
},
|
|
204
204
|
"devDependencies": {
|
|
205
|
-
"@allblue/devtools": "workspace:*",
|
|
206
205
|
"@babel/parser": "^8.0.4",
|
|
207
206
|
"@babel/traverse": "^8.0.4",
|
|
208
207
|
"@cloudflare/vite-plugin": "^1.40.1",
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
//
|
|
5
|
+
// This program is free software: you can redistribute it and/or modify
|
|
6
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
// (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU Affero General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
export function AIChatPanel() {
|
|
19
|
+
return (
|
|
20
|
+
<div className="flex h-full flex-col gap-4 overflow-auto p-3">
|
|
21
|
+
<div className="space-y-2">
|
|
22
|
+
<h4 className="text-sm font-semibold">AI Chat 监控</h4>
|
|
23
|
+
<p className="text-xs text-muted-foreground">
|
|
24
|
+
预留面板:监控 AI 对话状态、模型选择和 Token 消耗。
|
|
25
|
+
</p>
|
|
26
|
+
</div>
|
|
27
|
+
<div className="space-y-2">
|
|
28
|
+
<h4 className="text-sm font-semibold">配置</h4>
|
|
29
|
+
<div className="rounded border p-3 space-y-2">
|
|
30
|
+
<div className="text-xs">
|
|
31
|
+
<span className="text-muted-foreground">SSE 端点: </span>
|
|
32
|
+
<code>/api/chat</code>
|
|
33
|
+
</div>
|
|
34
|
+
<div className="text-xs">
|
|
35
|
+
<span className="text-muted-foreground">Adapter: </span>
|
|
36
|
+
<span>TanStack AI + OpenRouter/Anthropic/Gemini/…</span>
|
|
37
|
+
</div>
|
|
38
|
+
<div className="text-xs">
|
|
39
|
+
<span className="text-muted-foreground">持久化: </span>
|
|
40
|
+
<span>Prisma (AiProviderConfig)</span>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
<div className="space-y-2">
|
|
45
|
+
<h4 className="text-sm font-semibold">调试提示</h4>
|
|
46
|
+
<div className="rounded border border-amber-200 bg-amber-50 p-2">
|
|
47
|
+
<p className="text-xs text-amber-800">
|
|
48
|
+
使用浏览器 DevTools Network 面板查看 <code>/api/chat</code> SSE 连接。
|
|
49
|
+
</p>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
//
|
|
5
|
+
// This program is free software: you can redistribute it and/or modify
|
|
6
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
// (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU Affero General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
import { useEffect, useState, useCallback } from 'react'
|
|
19
|
+
import { AIChatPanel } from './ai-chat-panel'
|
|
20
|
+
import { SessionPanel } from './session-panel'
|
|
21
|
+
|
|
22
|
+
type TabName = 'ai-chat' | 'session'
|
|
23
|
+
|
|
24
|
+
interface TabConfig {
|
|
25
|
+
id: TabName
|
|
26
|
+
label: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const TABS: TabConfig[] = [
|
|
30
|
+
{ id: 'ai-chat', label: 'AI Chat' },
|
|
31
|
+
{ id: 'session', label: 'Session' },
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
export function DevtoolsPanel() {
|
|
35
|
+
const [isOpen, setIsOpen] = useState(false)
|
|
36
|
+
const [activeTab, setActiveTab] = useState<TabName>('ai-chat')
|
|
37
|
+
|
|
38
|
+
const toggle = useCallback(() => setIsOpen((v) => !v), [])
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const handleEsc = (e: KeyboardEvent) => {
|
|
42
|
+
if (e.key === 'Escape') setIsOpen(false)
|
|
43
|
+
}
|
|
44
|
+
const handleModD = (e: KeyboardEvent) => {
|
|
45
|
+
if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === 'D') {
|
|
46
|
+
e.preventDefault()
|
|
47
|
+
toggle()
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
document.addEventListener('keydown', handleEsc)
|
|
51
|
+
document.addEventListener('keydown', handleModD)
|
|
52
|
+
return () => {
|
|
53
|
+
document.removeEventListener('keydown', handleEsc)
|
|
54
|
+
document.removeEventListener('keydown', handleModD)
|
|
55
|
+
}
|
|
56
|
+
}, [toggle])
|
|
57
|
+
|
|
58
|
+
const panel = activeTab === 'ai-chat' ? <AIChatPanel /> : <SessionPanel />
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<>
|
|
62
|
+
<button
|
|
63
|
+
onClick={toggle}
|
|
64
|
+
className="fixed left-4 bottom-4 z-[10000] rounded-full bg-blue-600 px-3 py-1.5 text-xs text-white shadow-lg hover:bg-blue-700"
|
|
65
|
+
>
|
|
66
|
+
AllBlue Dev
|
|
67
|
+
</button>
|
|
68
|
+
{isOpen && (
|
|
69
|
+
<div className="fixed right-4 top-4 z-[10000] h-96 w-11/12 max-w-md overflow-hidden rounded-lg border bg-background shadow-2xl">
|
|
70
|
+
<div className="flex items-center justify-between border-b px-3 py-2">
|
|
71
|
+
<div className="flex gap-1">
|
|
72
|
+
{TABS.map((tab) => (
|
|
73
|
+
<button
|
|
74
|
+
key={tab.id}
|
|
75
|
+
onClick={() => setActiveTab(tab.id)}
|
|
76
|
+
className={`rounded px-2 py-1 text-xs font-medium ${
|
|
77
|
+
activeTab === tab.id
|
|
78
|
+
? 'bg-primary text-primary-foreground'
|
|
79
|
+
: 'text-muted-foreground hover:text-foreground'
|
|
80
|
+
}`}
|
|
81
|
+
>
|
|
82
|
+
{tab.label}
|
|
83
|
+
</button>
|
|
84
|
+
))}
|
|
85
|
+
</div>
|
|
86
|
+
<button
|
|
87
|
+
onClick={() => setIsOpen(false)}
|
|
88
|
+
className="text-muted-foreground hover:text-foreground"
|
|
89
|
+
>
|
|
90
|
+
<span className="text-sm">×</span>
|
|
91
|
+
</button>
|
|
92
|
+
</div>
|
|
93
|
+
<div className="h-[calc(100%-44px)] overflow-auto">
|
|
94
|
+
{panel}
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
)}
|
|
98
|
+
</>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
//
|
|
5
|
+
// This program is free software: you can redistribute it and/or modify
|
|
6
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
// (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU Affero General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
export { AIChatPanel } from './ai-chat-panel'
|
|
19
|
+
export { DevtoolsPanel } from './devtools-panel'
|
|
20
|
+
export { SessionPanel } from './session-panel'
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
//
|
|
5
|
+
// This program is free software: you can redistribute it and/or modify
|
|
6
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
// (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU Affero General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
import { useEffect, useState } from 'react'
|
|
19
|
+
|
|
20
|
+
type UserInfo = {
|
|
21
|
+
id: string
|
|
22
|
+
email: string
|
|
23
|
+
name: string | null
|
|
24
|
+
role: string
|
|
25
|
+
aiProvider?: string | null
|
|
26
|
+
aiModel?: string | null
|
|
27
|
+
hasAiKey?: boolean | null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function SessionPanel() {
|
|
31
|
+
const [user, setUser] = useState<UserInfo | null>(null)
|
|
32
|
+
const [loading, setLoading] = useState(true)
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
fetch('/api/auth/session', { credentials: 'include' })
|
|
36
|
+
.then((res) => res.ok ? res.json() : null)
|
|
37
|
+
.then((data) => {
|
|
38
|
+
setUser(data?.user ?? null)
|
|
39
|
+
setLoading(false)
|
|
40
|
+
})
|
|
41
|
+
.catch(() => setLoading(false))
|
|
42
|
+
}, [])
|
|
43
|
+
|
|
44
|
+
if (loading) {
|
|
45
|
+
return (
|
|
46
|
+
<div className="flex h-full items-center justify-center">
|
|
47
|
+
<span className="text-sm text-muted-foreground">加载中…</span>
|
|
48
|
+
</div>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!user) {
|
|
53
|
+
return (
|
|
54
|
+
<div className="flex h-full items-center justify-center">
|
|
55
|
+
<span className="text-sm text-muted-foreground">未登录</span>
|
|
56
|
+
</div>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div className="flex h-full flex-col gap-4 overflow-auto p-3">
|
|
62
|
+
<div className="space-y-2">
|
|
63
|
+
<h4 className="text-sm font-semibold">当前用户</h4>
|
|
64
|
+
<div className="grid grid-cols-2 gap-2 text-xs">
|
|
65
|
+
<div>
|
|
66
|
+
<span className="text-muted-foreground">ID: </span>
|
|
67
|
+
<code>{user.id}</code>
|
|
68
|
+
</div>
|
|
69
|
+
<div>
|
|
70
|
+
<span className="text-muted-foreground">Email: </span>
|
|
71
|
+
<span>{user.email}</span>
|
|
72
|
+
</div>
|
|
73
|
+
<div>
|
|
74
|
+
<span className="text-muted-foreground">Name: </span>
|
|
75
|
+
<span>{user.name || '-'}</span>
|
|
76
|
+
</div>
|
|
77
|
+
<div>
|
|
78
|
+
<span className="text-muted-foreground">Role: </span>
|
|
79
|
+
<span className="font-medium">{user.role}</span>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
<div className="space-y-2">
|
|
84
|
+
<h4 className="text-sm font-semibold">AI 配置</h4>
|
|
85
|
+
<div className="grid grid-cols-2 gap-2 text-xs">
|
|
86
|
+
<div>
|
|
87
|
+
<span className="text-muted-foreground">Provider: </span>
|
|
88
|
+
<span>{user.aiProvider || '未配置'}</span>
|
|
89
|
+
</div>
|
|
90
|
+
<div>
|
|
91
|
+
<span className="text-muted-foreground">Model: </span>
|
|
92
|
+
<span>{user.aiModel || '未配置'}</span>
|
|
93
|
+
</div>
|
|
94
|
+
<div>
|
|
95
|
+
<span className="text-muted-foreground">Has Key: </span>
|
|
96
|
+
<span>{user.hasAiKey ? '是' : '否'}</span>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
@@ -95,7 +95,7 @@ export const SDK_GROUPS: SdkGroup[] = [
|
|
|
95
95
|
"items": [
|
|
96
96
|
{
|
|
97
97
|
"name": "@allbluecn/web-app",
|
|
98
|
-
"version": "v0.9.
|
|
98
|
+
"version": "v0.9.2",
|
|
99
99
|
"lastUpdate": "Sep 3, 2026",
|
|
100
100
|
"source": "apps/web/package.json",
|
|
101
101
|
"docsUrl": "https://github.com/allbluecn/allblue"
|
|
@@ -141,13 +141,6 @@ export const SDK_GROUPS: SdkGroup[] = [
|
|
|
141
141
|
"lastUpdate": "Sep 3, 2026",
|
|
142
142
|
"source": "apps/web/package.json#dependencies",
|
|
143
143
|
"docsUrl": "https://ui.shadcn.com/"
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
"name": "TipTap",
|
|
147
|
-
"version": "v3.27.4",
|
|
148
|
-
"lastUpdate": "Sep 3, 2026",
|
|
149
|
-
"source": "packages/shared/package.json#devDependencies",
|
|
150
|
-
"docsUrl": "https://tiptap.dev/"
|
|
151
144
|
}
|
|
152
145
|
]
|
|
153
146
|
}
|
package/src/lib/perf.ts
CHANGED
|
@@ -15,5 +15,5 @@
|
|
|
15
15
|
// You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
|
-
// 全站虚拟化/分页门槛常量统一自 @allbluecn/
|
|
18
|
+
// 全站虚拟化/分页门槛常量统一自 @allbluecn/kernel(web / admin / desktop 共享)
|
|
19
19
|
export { VIRTUALIZE_THRESHOLD, LOAD_MORE_STEP, DND_STICKY_THRESHOLD } from "@allbluecn/kernel";
|
package/src/routes/__root.tsx
CHANGED
|
@@ -26,7 +26,7 @@ import globalsCss from '@/styles/globals.css?url'
|
|
|
26
26
|
import type { RouterContext } from '@/lib/router-context'
|
|
27
27
|
import { Providers } from '@/providers'
|
|
28
28
|
import { Toaster } from '@/components/ui/sonner'
|
|
29
|
-
import { DevtoolsPanel } from '
|
|
29
|
+
import { DevtoolsPanel } from '@/components/devtools'
|
|
30
30
|
import { getSessionFn } from '@/server/serverFns/auth/session'
|
|
31
31
|
import { getLocaleFn } from '@/server/serverFns/locale'
|
|
32
32
|
import type { AppLocale } from '@/i18n/config'
|