@allbluecn/web-app 0.4.9 → 0.4.11
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 +11 -7
- package/public/tanstack-landscape-black.svg +23 -0
- package/public/tanstack-landscape-white.svg +23 -0
- package/src/components/changelog/changelog-page.tsx +382 -0
- package/src/components/{command-palette.tsx → command/command-palette.tsx} +95 -47
- package/src/components/hextaui/command-menu.tsx +460 -0
- package/src/components/layout/sidebar-03/app-sidebar.tsx +3 -1
- package/src/components/layout/sidebar-03/{sidebar-search-trigger.tsx → nav-search-trigger.tsx} +21 -7
- package/src/components/layout/sidebar-03/nav-secondary.tsx +93 -0
- package/src/lib/app-version.ts +20 -0
- package/src/lib/changelog/sdk-versions-types.ts +14 -0
- package/src/lib/changelog/sdk-versions.ts +151 -0
- package/src/lib/commands/registry.ts +2 -2
- package/src/routeTree.gen.ts +23 -0
- package/src/routes/_nav/changelog/route.lazy.tsx +7 -0
- package/src/routes/_nav/changelog/route.tsx +13 -0
- package/src/routes/_nav/route.lazy.tsx +1 -1
- package/src/server/serverFns/changelog/fetch.ts +28 -0
- package/src/styles/globals.css +18 -0
- package/src/styles/typeset.css +464 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allbluecn/web-app",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.11",
|
|
4
4
|
"license": "AGPL-3.0-or-later",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -24,12 +24,16 @@
|
|
|
24
24
|
"@base-ui/react": "^1.4.1",
|
|
25
25
|
"@dnd-kit/react": "^0.5.0",
|
|
26
26
|
"@floating-ui/react": "^0.27.19",
|
|
27
|
+
"@fontsource-variable/geist": "^5.3.0",
|
|
28
|
+
"@fontsource-variable/geist-mono": "^5.3.0",
|
|
27
29
|
"@iconify/react": "^6.0.2",
|
|
28
30
|
"@lobehub/icons": "^5.10.0",
|
|
29
31
|
"@prisma/client": "^7.8.0",
|
|
32
|
+
"@radix-ui/react-dialog": "^1.1.23",
|
|
30
33
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
31
34
|
"@radix-ui/react-popover": "^1.1.15",
|
|
32
35
|
"@radix-ui/react-slot": "^1.2.4",
|
|
36
|
+
"@radix-ui/react-visually-hidden": "^1.2.11",
|
|
33
37
|
"@tanstack/ai": "^0.43.0",
|
|
34
38
|
"@tanstack/ai-anthropic": "^0.16.3",
|
|
35
39
|
"@tanstack/ai-gemini": "^0.20.1",
|
|
@@ -131,14 +135,14 @@
|
|
|
131
135
|
"three": "^0.180.0",
|
|
132
136
|
"tw-animate-css": "^1.4.0",
|
|
133
137
|
"zod": "^4.4.3",
|
|
134
|
-
"@allbluecn/
|
|
138
|
+
"@allbluecn/ai-gateway": "0.4.11",
|
|
139
|
+
"@allbluecn/database": "0.4.11",
|
|
135
140
|
"@allbluecn/kernel": "0.3.0",
|
|
136
|
-
"@allbluecn/
|
|
137
|
-
"@allbluecn/plugins-core": "0.4.9",
|
|
141
|
+
"@allbluecn/plugins-core": "0.4.11",
|
|
138
142
|
"@allbluecn/prototype": "0.1.1",
|
|
139
|
-
"@allbluecn/tiptap": "0.4.
|
|
140
|
-
"@allbluecn/shared": "0.4.
|
|
141
|
-
"@allbluecn/ui": "0.4.
|
|
143
|
+
"@allbluecn/tiptap": "0.4.11",
|
|
144
|
+
"@allbluecn/shared": "0.4.11",
|
|
145
|
+
"@allbluecn/ui": "0.4.11"
|
|
142
146
|
},
|
|
143
147
|
"devDependencies": {
|
|
144
148
|
"@babel/parser": "^8.0.4",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 321.33 50">
|
|
3
|
+
<defs>
|
|
4
|
+
<style>
|
|
5
|
+
.cls-1 {
|
|
6
|
+
fill-rule: evenodd;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.cls-1, .cls-2 {
|
|
10
|
+
fill: #121212;
|
|
11
|
+
}
|
|
12
|
+
</style>
|
|
13
|
+
</defs>
|
|
14
|
+
<path class="cls-1" d="M19.03,0c13.76,0,19.03,8.54,19.03,25s-5.27,25-19.03,25S0,41.46,0,25C0,8.54,5.27,0,19.03,0ZM25.32,36.42c-1.79,0-2.76.49-3.55.88-.68.34-1.21.61-2.38.61-1.17,0-1.71-.27-2.38-.61-.78-.39-1.76-.88-3.55-.88-1.79,0-2.76.49-3.55.88-.68.34-1.21.61-2.38.61v2.6c1.79,0,2.76-.49,3.55-.88.68-.34,1.21-.61,2.38-.61s1.71.27,2.38.61c.78.39,1.76.88,3.55.88s2.76-.49,3.55-.88c.68-.34,1.21-.61,2.38-.61s1.71.27,2.38.61c.78.39,1.76.88,3.55.88v-2.6c-1.17,0-1.71-.27-2.38-.61-.78-.39-1.76-.88-3.55-.88ZM26.17,10.31c1.35-1.22,0-3.43-1.69-2.76-.93.37-1.81.91-2.59,1.62-1.23,1.11-2.05,2.52-2.5,4.04-.44-1.53-1.27-2.93-2.5-4.04-.78-.7-1.66-1.25-2.6-1.62-1.69-.67-3.04,1.54-1.69,2.76l4.95,4.49c-1.62-1.1-3.56-1.75-5.67-1.75-1.15,0-2.25.19-3.28.54-1.73.59-1.25,3.19.57,3.19h6.91c-1.52.29-2.97.97-4.18,2.06-.78.7-1.4,1.53-1.87,2.43-.84,1.61,1.22,3.18,2.57,1.97l5.57-5.04-.42,13.87c0,.17-.1.33-.22.46-.18-.08-.35-.16-.53-.26-.78-.39-1.76-.88-3.55-.88s-2.76.49-3.55.88c-.68.34-1.21.61-2.38.61v2.6c1.79,0,2.76-.49,3.55-.88.68-.34,1.21-.61,2.38-.61s1.71.27,2.38.61c.78.39,1.76.88,3.55.88s2.76-.49,3.55-.88c.68-.34,1.21-.61,2.38-.61s1.71.27,2.38.61c.78.39,1.76.88,3.55.88v-2.6c-1.17,0-1.71-.27-2.38-.61h0c-.78-.4-1.76-.89-3.55-.89s-2.76.49-3.55.88c-.2.1-.39.2-.59.28-.15-.13-.24-.3-.25-.48l-.42-13.94,5.65,5.12c1.35,1.22,3.4-.35,2.57-1.97-.46-.89-1.08-1.72-1.87-2.43-1.21-1.1-2.65-1.77-4.18-2.06h6.91c1.83,0,2.3-2.6.57-3.19-1.03-.35-2.13-.54-3.28-.54-2.1,0-4.04.65-5.67,1.75l4.95-4.49Z"/>
|
|
15
|
+
<path class="cls-2" d="M151.71,43.25h-12.45c-.67,0-1.28-.42-1.53-1.04l-2.55-6.55c-.65-1.7-3.15-1.22-3.17.58-.02,1.8-.02,3.53-.02,5.37,0,.9-.74,1.64-1.64,1.64h-10.47c-.92,0-1.64-.72-1.64-1.64V8.34c0-.9.72-1.64,1.64-1.64h12.43c.69-.02,1.28.41,1.53,1.04l2.61,6.84c.65,1.7,3.15,1.25,3.17-.57l.07-5.69c0-.9.74-1.63,1.64-1.63h10.4c.9,0,1.64.74,1.64,1.66v33.26c0,.92-.74,1.64-1.64,1.64l-.02-.02Z"/>
|
|
16
|
+
<path class="cls-2" d="M98.25,37.35l-3.85-.04c-.81,0-1.5.58-1.64,1.38l-.53,3.18c-.12.79-.81,1.38-1.62,1.38h-11.07c-1.07,0-1.85-.99-1.6-2.03l8.38-33.28c.18-.72.85-1.24,1.6-1.24h16.76c.76.02,1.41.53,1.58,1.27l8.4,33.25c.26,1.04-.51,2.05-1.58,2.05l-11.07.04c-.81,0-1.5-.58-1.62-1.38l-.53-3.16c-.12-.79-.81-1.38-1.6-1.38v-.04ZM97.94,27.4l-.85-4.19c-.18-.88-1.44-.88-1.62,0l-.76,4.2c-.19,1.02.62,1.98,1.65,1.94,1.02-.04,1.76-.97,1.57-1.98v.02Z"/>
|
|
17
|
+
<path class="cls-2" d="M69.77,43.23l-10.49.04c-.92,0-1.65-.74-1.65-1.64v-21.76c0-.9-.72-1.64-1.64-1.64h-5.39c-.9,0-1.64-.76-1.64-1.66v-8.2c0-.9.74-1.64,1.65-1.64h27.8c.9,0,1.64.74,1.64,1.64v8.2c0,.9-.72,1.64-1.64,1.64h-5.39c-.9,0-1.64.74-1.64,1.66v21.75c0,.9-.74,1.64-1.64,1.64l.02-.02Z"/>
|
|
18
|
+
<path class="cls-2" d="M311.9,24.92l9.21,15.85c.63,1.1-.14,2.47-1.41,2.47l-10.72.05c-.6,0-1.14-.32-1.44-.83l-4.07-7.21c-.6-1.08-2.24-.65-2.25.58l-.1,5.81c0,.9-.74,1.63-1.64,1.63h-10.47c-.92,0-1.64-.72-1.64-1.64V8.37c0-.9.72-1.64,1.64-1.64h10.49c.9,0,1.62.71,1.64,1.59l.11,4.26c.04,1.27,1.74,1.64,2.29.49l2.59-5.39c.28-.57.85-.94,1.48-.94h10.88c1.27-.02,2.06,1.34,1.43,2.44l-7.97,14.1c-.28.51-.28,1.13,0,1.64h-.02Z"/>
|
|
19
|
+
<path class="cls-2" d="M238.4,37.38l-3.85-.04c-.81,0-1.5.58-1.64,1.38l-.53,3.18c-.12.79-.81,1.38-1.62,1.38h-11.09c-1.07,0-1.85-.99-1.6-2.03l8.38-33.26c.18-.72.85-1.24,1.58-1.24h16.78c.76-.02,1.41.49,1.6,1.22l8.4,33.26c.26,1.04-.51,2.05-1.58,2.05h-11.07c-.81.04-1.5-.55-1.62-1.34l-.53-3.16c-.12-.79-.81-1.38-1.6-1.38v-.02ZM238.07,27.43l-.74-3.67c-.19-.99-1.62-.99-1.81.02l-.67,3.69c-.19,1.02.62,1.98,1.65,1.94,1.02-.04,1.76-.97,1.57-1.98Z"/>
|
|
20
|
+
<path class="cls-2" d="M189.42,33.35c-.99,11.71-17.78,12.35-29.17,8.2-.67-.25-1.11-.9-1.11-1.61v-9.57c0-1.31,1.37-2.14,2.52-1.5,3.68,2.01,7.39,3.44,11.6,4.29,0,0,1.39.11,1.9-.79.51-.9-.46-1.25-.88-1.61-8.01-3.48-17.32-5.53-16.49-14.59.97-10.48,16.83-11.75,27.62-8.39.7.21,1.18.9,1.18,1.64v9.38c0,1.24-1.27,2.07-2.41,1.57-5.16-2.28-12.16-4.29-11.81-2.95.9,3.39,18.06,3.76,17.04,15.9l.02.04Z"/>
|
|
21
|
+
<path class="cls-2" d="M271.32,29.38c2.8,2.33,6.25,2.44,9.22.94,1.41-.71,3.06.25,3.06,1.82l.05,8.37c0,.87-.51,1.68-1.34,1.96-8.08,2.86-17.55,1.24-23.25-5.81-5.7-7.05-5.42-16.91.23-23.62,5.65-6.71,15.03-8.27,23.01-5.55.83.28,1.36,1.1,1.36,1.98l-.05,8.37c0,1.57-1.65,2.53-3.06,1.84-2.94-1.47-6.28-1.47-9.12.79-2.48,1.98-2.75,6.69-.11,8.9Z"/>
|
|
22
|
+
<path class="cls-2" d="M210.7,43.24l-10.49.04c-.92,0-1.65-.74-1.65-1.64v-21.76c0-.9-.72-1.64-1.64-1.64h-5.39c-.9,0-1.64-.76-1.64-1.66v-8.2c0-.9.74-1.64,1.65-1.64h27.79c.9,0,1.64.74,1.64,1.64v8.2c0,.9-.72,1.64-1.64,1.64h-5.39c-.9,0-1.64.74-1.64,1.66v21.75c0,.9-.72,1.64-1.64,1.64l.02-.02Z"/>
|
|
23
|
+
</svg>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 321.33 50">
|
|
3
|
+
<defs>
|
|
4
|
+
<style>
|
|
5
|
+
.cls-1 {
|
|
6
|
+
fill-rule: evenodd;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.cls-1, .cls-2 {
|
|
10
|
+
fill: #fff;
|
|
11
|
+
}
|
|
12
|
+
</style>
|
|
13
|
+
</defs>
|
|
14
|
+
<path class="cls-1" d="M19.03,0c13.76,0,19.03,8.54,19.03,25s-5.27,25-19.03,25S0,41.46,0,25C0,8.54,5.27,0,19.03,0ZM25.32,36.42c-1.79,0-2.76.49-3.55.88-.68.34-1.21.61-2.38.61-1.17,0-1.71-.27-2.38-.61-.78-.39-1.76-.88-3.55-.88-1.79,0-2.76.49-3.55.88-.68.34-1.21.61-2.38.61v2.6c1.79,0,2.76-.49,3.55-.88.68-.34,1.21-.61,2.38-.61s1.71.27,2.38.61c.78.39,1.76.88,3.55.88s2.76-.49,3.55-.88c.68-.34,1.21-.61,2.38-.61s1.71.27,2.38.61c.78.39,1.76.88,3.55.88v-2.6c-1.17,0-1.71-.27-2.38-.61-.78-.39-1.76-.88-3.55-.88ZM26.17,10.31c1.35-1.22,0-3.43-1.69-2.76-.93.37-1.81.91-2.59,1.62-1.23,1.11-2.05,2.52-2.5,4.04-.44-1.53-1.27-2.93-2.5-4.04-.78-.7-1.66-1.25-2.6-1.62-1.69-.67-3.04,1.54-1.69,2.76l4.95,4.49c-1.62-1.1-3.56-1.75-5.67-1.75-1.15,0-2.25.19-3.28.54-1.73.59-1.25,3.19.57,3.19h6.91c-1.52.29-2.97.97-4.18,2.06-.78.7-1.4,1.53-1.87,2.43-.84,1.61,1.22,3.18,2.57,1.97l5.57-5.04-.42,13.87c0,.17-.1.33-.22.46-.18-.08-.35-.16-.53-.26-.78-.39-1.76-.88-3.55-.88s-2.76.49-3.55.88c-.68.34-1.21.61-2.38.61v2.6c1.79,0,2.76-.49,3.55-.88.68-.34,1.21-.61,2.38-.61s1.71.27,2.38.61c.78.39,1.76.88,3.55.88s2.76-.49,3.55-.88c.68-.34,1.21-.61,2.38-.61s1.71.27,2.38.61c.78.39,1.76.88,3.55.88v-2.6c-1.17,0-1.71-.27-2.38-.61h0c-.78-.4-1.76-.89-3.55-.89s-2.76.49-3.55.88c-.2.1-.39.2-.59.28-.15-.13-.24-.3-.25-.48l-.42-13.94,5.65,5.12c1.35,1.22,3.4-.35,2.57-1.97-.46-.89-1.08-1.72-1.87-2.43-1.21-1.1-2.65-1.77-4.18-2.06h6.91c1.83,0,2.3-2.6.57-3.19-1.03-.35-2.13-.54-3.28-.54-2.1,0-4.04.65-5.67,1.75l4.95-4.49Z"/>
|
|
15
|
+
<path class="cls-2" d="M151.71,43.25h-12.45c-.67,0-1.28-.42-1.53-1.04l-2.55-6.55c-.65-1.7-3.15-1.22-3.17.58-.02,1.8-.02,3.53-.02,5.37,0,.9-.74,1.64-1.64,1.64h-10.47c-.92,0-1.64-.72-1.64-1.64V8.34c0-.9.72-1.64,1.64-1.64h12.43c.69-.02,1.28.41,1.53,1.04l2.61,6.84c.65,1.7,3.15,1.25,3.17-.57l.07-5.69c0-.9.74-1.63,1.64-1.63h10.4c.9,0,1.64.74,1.64,1.66v33.26c0,.92-.74,1.64-1.64,1.64l-.02-.02Z"/>
|
|
16
|
+
<path class="cls-2" d="M98.25,37.35l-3.85-.04c-.81,0-1.5.58-1.64,1.38l-.53,3.18c-.12.79-.81,1.38-1.62,1.38h-11.07c-1.07,0-1.85-.99-1.6-2.03l8.38-33.28c.18-.72.85-1.24,1.6-1.24h16.76c.76.02,1.41.53,1.58,1.27l8.4,33.25c.26,1.04-.51,2.05-1.58,2.05l-11.07.04c-.81,0-1.5-.58-1.62-1.38l-.53-3.16c-.12-.79-.81-1.38-1.6-1.38v-.04ZM97.94,27.4l-.85-4.19c-.18-.88-1.44-.88-1.62,0l-.76,4.2c-.19,1.02.62,1.98,1.65,1.94,1.02-.04,1.76-.97,1.57-1.98v.02Z"/>
|
|
17
|
+
<path class="cls-2" d="M69.77,43.23l-10.49.04c-.92,0-1.65-.74-1.65-1.64v-21.76c0-.9-.72-1.64-1.64-1.64h-5.39c-.9,0-1.64-.76-1.64-1.66v-8.2c0-.9.74-1.64,1.65-1.64h27.8c.9,0,1.64.74,1.64,1.64v8.2c0,.9-.72,1.64-1.64,1.64h-5.39c-.9,0-1.64.74-1.64,1.66v21.75c0,.9-.74,1.64-1.64,1.64l.02-.02Z"/>
|
|
18
|
+
<path class="cls-2" d="M311.9,24.92l9.21,15.85c.63,1.1-.14,2.47-1.41,2.47l-10.72.05c-.6,0-1.14-.32-1.44-.83l-4.07-7.21c-.6-1.08-2.24-.65-2.25.58l-.1,5.81c0,.9-.74,1.63-1.64,1.63h-10.47c-.92,0-1.64-.72-1.64-1.64V8.37c0-.9.72-1.64,1.64-1.64h10.49c.9,0,1.62.71,1.64,1.59l.11,4.26c.04,1.27,1.74,1.64,2.29.49l2.59-5.39c.28-.57.85-.94,1.48-.94h10.88c1.27-.02,2.06,1.34,1.43,2.44l-7.97,14.1c-.28.51-.28,1.13,0,1.64h-.02Z"/>
|
|
19
|
+
<path class="cls-2" d="M238.4,37.38l-3.85-.04c-.81,0-1.5.58-1.64,1.38l-.53,3.18c-.12.79-.81,1.38-1.62,1.38h-11.09c-1.07,0-1.85-.99-1.6-2.03l8.38-33.26c.18-.72.85-1.24,1.58-1.24h16.78c.76-.02,1.41.49,1.6,1.22l8.4,33.26c.26,1.04-.51,2.05-1.58,2.05h-11.07c-.81.04-1.5-.55-1.62-1.34l-.53-3.16c-.12-.79-.81-1.38-1.6-1.38v-.02ZM238.07,27.43l-.74-3.67c-.19-.99-1.62-.99-1.81.02l-.67,3.69c-.19,1.02.62,1.98,1.65,1.94,1.02-.04,1.76-.97,1.57-1.98Z"/>
|
|
20
|
+
<path class="cls-2" d="M189.42,33.35c-.99,11.71-17.78,12.35-29.17,8.2-.67-.25-1.11-.9-1.11-1.61v-9.57c0-1.31,1.37-2.14,2.52-1.5,3.68,2.01,7.39,3.44,11.6,4.29,0,0,1.39.11,1.9-.79.51-.9-.46-1.25-.88-1.61-8.01-3.48-17.32-5.53-16.49-14.59.97-10.48,16.83-11.75,27.62-8.39.7.21,1.18.9,1.18,1.64v9.38c0,1.24-1.27,2.07-2.41,1.57-5.16-2.28-12.16-4.29-11.81-2.95.9,3.39,18.06,3.76,17.04,15.9l.02.04Z"/>
|
|
21
|
+
<path class="cls-2" d="M271.32,29.38c2.8,2.33,6.25,2.44,9.22.94,1.41-.71,3.06.25,3.06,1.82l.05,8.37c0,.87-.51,1.68-1.34,1.96-8.08,2.86-17.55,1.24-23.25-5.81-5.7-7.05-5.42-16.91.23-23.62,5.65-6.71,15.03-8.27,23.01-5.55.83.28,1.36,1.1,1.36,1.98l-.05,8.37c0,1.57-1.65,2.53-3.06,1.84-2.94-1.47-6.28-1.47-9.12.79-2.48,1.98-2.75,6.69-.11,8.9Z"/>
|
|
22
|
+
<path class="cls-2" d="M210.7,43.24l-10.49.04c-.92,0-1.65-.74-1.65-1.64v-21.76c0-.9-.72-1.64-1.64-1.64h-5.39c-.9,0-1.64-.76-1.64-1.66v-8.2c0-.9.74-1.64,1.65-1.64h27.79c.9,0,1.64.74,1.64,1.64v8.2c0,.9-.72,1.64-1.64,1.64h-5.39c-.9,0-1.64.74-1.64,1.66v21.75c0,.9-.72,1.64-1.64,1.64l.02-.02Z"/>
|
|
23
|
+
</svg>
|
|
@@ -0,0 +1,382 @@
|
|
|
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 { cn } from '@/lib/utils'
|
|
19
|
+
import { ScrollText, AlertTriangle, ArrowLeft } from 'lucide-react'
|
|
20
|
+
import ReactMarkdown from 'react-markdown'
|
|
21
|
+
import { useTranslation } from 'react-i18next'
|
|
22
|
+
import { useMemo, useState } from 'react'
|
|
23
|
+
import { Route } from '@/routes/_nav/changelog/route'
|
|
24
|
+
import type { DeveloperLogEntry } from '@allbluecn/shared'
|
|
25
|
+
import { SDK_GROUPS } from '@/lib/changelog/sdk-versions'
|
|
26
|
+
import { APP_FULL_VERSION } from '@/lib/app-version'
|
|
27
|
+
|
|
28
|
+
const TAG_KEYS = ['all', 'feat', 'fix', 'improvement', 'design', 'refactor', 'breaking', 'docs', 'security', 'perf']
|
|
29
|
+
|
|
30
|
+
const REQUIRES_ACTION_TAGS = new Set(['breaking'])
|
|
31
|
+
|
|
32
|
+
function ChangelogSidebar() {
|
|
33
|
+
const { t } = useTranslation('common')
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<aside className="shrink-0 w-150 h-full flex flex-col py-6 pr-12 pl-10 border-r border-border bg-muted/50">
|
|
37
|
+
<div className="flex-1 flex flex-col gap-8 justify-center">
|
|
38
|
+
<div>
|
|
39
|
+
<h1 className="text-4xl font-medium tracking-tight">
|
|
40
|
+
{t('changelog.title')}
|
|
41
|
+
</h1>
|
|
42
|
+
<p className="text-sm text-muted-foreground mt-3 leading-relaxed">
|
|
43
|
+
{t('changelog.description')}
|
|
44
|
+
</p>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div className="flex flex-col gap-4">
|
|
48
|
+
<h2 className="text-sm font-semibold tracking-tight">
|
|
49
|
+
{t('changelog.sdkVersions')}
|
|
50
|
+
</h2>
|
|
51
|
+
{SDK_GROUPS.map((group) => (
|
|
52
|
+
<SdkGroupSection key={group.id} group={group} t={t} />
|
|
53
|
+
))}
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div>
|
|
57
|
+
<h3 className="text-sm font-semibold tracking-tight">
|
|
58
|
+
{t('changelog.subscribe.title')}
|
|
59
|
+
</h3>
|
|
60
|
+
<p className="text-xs text-muted-foreground mt-1 leading-relaxed">
|
|
61
|
+
{t('changelog.subscribe.subtitle')}
|
|
62
|
+
</p>
|
|
63
|
+
<div className="mt-3 flex gap-1.5">
|
|
64
|
+
<input
|
|
65
|
+
type="email"
|
|
66
|
+
placeholder={t('changelog.subscribe.placeholder')}
|
|
67
|
+
className="flex-1 min-w-0 h-8 rounded-md border border-border bg-background px-3 text-xs placeholder:text-muted-foreground/70 focus:outline-none focus:ring-2 focus:ring-primary/20 transition-shadow"
|
|
68
|
+
/>
|
|
69
|
+
<button className="h-8 px-3 rounded-md bg-primary text-primary-foreground text-xs font-medium hover:bg-primary/90 transition-colors shrink-0">
|
|
70
|
+
{t('changelog.subscribe.submit')}
|
|
71
|
+
</button>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<div className="pt-4 border-border/50">
|
|
77
|
+
<code className="text-[10px] font-mono text-primary/80 bg-primary/5 px-2 py-0.5 rounded-full">
|
|
78
|
+
{APP_FULL_VERSION}
|
|
79
|
+
</code>
|
|
80
|
+
</div>
|
|
81
|
+
</aside>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function TagFilter({
|
|
86
|
+
activeTag,
|
|
87
|
+
onTagChange,
|
|
88
|
+
}: {
|
|
89
|
+
activeTag: string
|
|
90
|
+
onTagChange: (tag: string) => void
|
|
91
|
+
}) {
|
|
92
|
+
const { t } = useTranslation('common')
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div className="flex flex-wrap gap-1.5">
|
|
96
|
+
{TAG_KEYS.map((tag) => {
|
|
97
|
+
const isActive = activeTag === tag
|
|
98
|
+
const label = t(`changelog.tags.${tag}`)
|
|
99
|
+
return (
|
|
100
|
+
<button
|
|
101
|
+
key={tag}
|
|
102
|
+
onClick={() => onTagChange(tag)}
|
|
103
|
+
className={cn(
|
|
104
|
+
'inline-flex items-center gap-1 px-2.5 py-1 rounded-md text-xs font-medium transition-colors border',
|
|
105
|
+
isActive
|
|
106
|
+
? 'bg-primary/10 text-primary border-primary/20'
|
|
107
|
+
: 'bg-transparent text-muted-foreground border-border/50 hover:text-foreground hover:border-border'
|
|
108
|
+
)}
|
|
109
|
+
>
|
|
110
|
+
{label}
|
|
111
|
+
{REQUIRES_ACTION_TAGS.has(tag) && (
|
|
112
|
+
<AlertTriangle className="size-3" />
|
|
113
|
+
)}
|
|
114
|
+
</button>
|
|
115
|
+
)
|
|
116
|
+
})}
|
|
117
|
+
</div>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function YearHeader({ year, count }: { year: string; count: number }) {
|
|
122
|
+
const { t } = useTranslation('common')
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<div className="flex items-center gap-3 py-4 mb-1">
|
|
126
|
+
<h3 className="text-xl font-semibold tracking-tight">{year}</h3>
|
|
127
|
+
<span className="text-xs text-muted-foreground font-mono">
|
|
128
|
+
{count} {t('changelog.entries')}
|
|
129
|
+
</span>
|
|
130
|
+
<div className="flex-1 h-px bg-border/50" />
|
|
131
|
+
</div>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function ChangelogEntry({
|
|
136
|
+
entry,
|
|
137
|
+
onClick,
|
|
138
|
+
}: {
|
|
139
|
+
entry: { id: string; date: string; title: string; desc: string; tags: string[] }
|
|
140
|
+
onClick: (id: string) => void
|
|
141
|
+
}) {
|
|
142
|
+
const { t } = useTranslation('common')
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<div
|
|
146
|
+
className="group py-3 hover:bg-muted/30 -mx-3 px-3 rounded-lg transition-colors cursor-pointer"
|
|
147
|
+
onClick={() => onClick(entry.id)}
|
|
148
|
+
>
|
|
149
|
+
<div className="flex items-start gap-3">
|
|
150
|
+
<div className="shrink-0 pt-0.5">
|
|
151
|
+
<span className="text-xs text-muted-foreground tabular-nums">
|
|
152
|
+
{entry.date}
|
|
153
|
+
</span>
|
|
154
|
+
</div>
|
|
155
|
+
<div className="flex-1 min-w-0">
|
|
156
|
+
<div className="flex items-start gap-2">
|
|
157
|
+
<h4 className="text-sm font-semibold leading-snug group-hover:text-primary transition-colors">
|
|
158
|
+
{entry.title}
|
|
159
|
+
</h4>
|
|
160
|
+
{entry.tags.includes('breaking') && (
|
|
161
|
+
<span className="inline-flex items-center gap-0.5 px-1.5 py-0.5 rounded text-[10px] font-medium bg-destructive/10 text-destructive shrink-0 mt-0.5">
|
|
162
|
+
<AlertTriangle className="size-2.5" />
|
|
163
|
+
{t('changelog.requiresAction')}
|
|
164
|
+
</span>
|
|
165
|
+
)}
|
|
166
|
+
</div>
|
|
167
|
+
<p className="text-xs text-muted-foreground leading-relaxed mt-1 line-clamp-2">
|
|
168
|
+
{entry.desc}
|
|
169
|
+
</p>
|
|
170
|
+
{entry.tags.length > 0 && (
|
|
171
|
+
<div className="flex flex-wrap gap-1 mt-1.5">
|
|
172
|
+
{entry.tags.map((tag) => (
|
|
173
|
+
<span
|
|
174
|
+
key={tag}
|
|
175
|
+
className="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] text-muted-foreground bg-muted/50"
|
|
176
|
+
>
|
|
177
|
+
{t(`changelog.tags.${tag}`)}
|
|
178
|
+
</span>
|
|
179
|
+
))}
|
|
180
|
+
</div>
|
|
181
|
+
)}
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function ChangelogDetail({
|
|
189
|
+
entry,
|
|
190
|
+
onBack,
|
|
191
|
+
}: {
|
|
192
|
+
entry: DeveloperLogEntry
|
|
193
|
+
onBack: () => void
|
|
194
|
+
}) {
|
|
195
|
+
const { t } = useTranslation('common')
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
<div className="h-full flex justify-center">
|
|
199
|
+
<div className="max-w-4xl flex flex-col py-5">
|
|
200
|
+
<button
|
|
201
|
+
onClick={onBack}
|
|
202
|
+
className="flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors mb-6 shrink-0"
|
|
203
|
+
>
|
|
204
|
+
<ArrowLeft className="size-3.5" />
|
|
205
|
+
{t('changelog.backToList')}
|
|
206
|
+
</button>
|
|
207
|
+
|
|
208
|
+
<div className="flex items-start gap-3 mb-4">
|
|
209
|
+
<span className="text-xs text-muted-foreground font-mono tabular-nums pt-0.5 shrink-0">
|
|
210
|
+
{entry.date}
|
|
211
|
+
</span>
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
<h2 className="text-2xl font-semibold tracking-tight mb-4">
|
|
215
|
+
{entry.title}
|
|
216
|
+
</h2>
|
|
217
|
+
|
|
218
|
+
{entry.tags.length > 0 && (
|
|
219
|
+
<div className="flex flex-wrap gap-1.5 mb-6">
|
|
220
|
+
{entry.tags.map((tag) => (
|
|
221
|
+
<span
|
|
222
|
+
key={tag}
|
|
223
|
+
className={cn(
|
|
224
|
+
'inline-flex items-center gap-1 px-2 py-0.5 rounded text-[10px] font-medium border',
|
|
225
|
+
tag === 'breaking'
|
|
226
|
+
? 'bg-destructive/10 text-destructive border-destructive/20'
|
|
227
|
+
: 'text-muted-foreground bg-muted/50 border-border/50'
|
|
228
|
+
)}
|
|
229
|
+
>
|
|
230
|
+
{t(`changelog.tags.${tag}`)}
|
|
231
|
+
</span>
|
|
232
|
+
))}
|
|
233
|
+
</div>
|
|
234
|
+
)}
|
|
235
|
+
|
|
236
|
+
<div className="typeset typeset-article max-w-[37em]">
|
|
237
|
+
<ReactMarkdown>{entry.desc}</ReactMarkdown>
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
{entry.commitSha && (
|
|
241
|
+
<div className="mt-6 pt-4 border-t border-border/50">
|
|
242
|
+
<code className="text-[10px] font-mono text-muted-foreground">
|
|
243
|
+
commit: {entry.commitSha}
|
|
244
|
+
</code>
|
|
245
|
+
</div>
|
|
246
|
+
)}
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function SdkGroupSection({ group, t }: { group: (typeof SDK_GROUPS)[0]; t: (key: string) => string }) {
|
|
253
|
+
const isTanstack = group.id === 'tanstack'
|
|
254
|
+
|
|
255
|
+
return (
|
|
256
|
+
<div>
|
|
257
|
+
<div
|
|
258
|
+
className="flex items-center gap-1.5 w-full py-1"
|
|
259
|
+
>
|
|
260
|
+
{isTanstack ? (
|
|
261
|
+
<span className="shrink-0">
|
|
262
|
+
<img
|
|
263
|
+
src="/tanstack-landscape-black.svg"
|
|
264
|
+
alt="TanStack"
|
|
265
|
+
className="h-4 dark:hidden"
|
|
266
|
+
/>
|
|
267
|
+
<img
|
|
268
|
+
src="/tanstack-landscape-white.svg"
|
|
269
|
+
alt="TanStack"
|
|
270
|
+
className="hidden h-4 dark:block"
|
|
271
|
+
/>
|
|
272
|
+
</span>
|
|
273
|
+
) : (
|
|
274
|
+
<span className="text-base">{group.icon}</span>
|
|
275
|
+
)}
|
|
276
|
+
<span className="text-xs mt-1 font-semibold text-foreground/80">
|
|
277
|
+
{t(`changelog.sdkGroups.${group.label}`)}
|
|
278
|
+
</span>
|
|
279
|
+
</div>
|
|
280
|
+
<ul className="mt-1 flex flex-col">
|
|
281
|
+
{group.items.map((sdk) => (
|
|
282
|
+
<li key={sdk.name}>
|
|
283
|
+
<a
|
|
284
|
+
href={sdk.docsUrl || undefined}
|
|
285
|
+
target={sdk.docsUrl ? '_blank' : undefined}
|
|
286
|
+
rel={sdk.docsUrl ? 'noopener noreferrer' : undefined}
|
|
287
|
+
className="flex items-center justify-between gap-2 py-1.5 group"
|
|
288
|
+
>
|
|
289
|
+
<span className="text-xs font-medium text-muted-foreground group-hover:text-primary transition-colors truncate">
|
|
290
|
+
{sdk.name}
|
|
291
|
+
</span>
|
|
292
|
+
<code className="text-[10px] font-mono text-muted-foreground bg-muted/50 px-1.5 py-0.5 rounded shrink-0 group-hover:text-primary transition-colors">
|
|
293
|
+
{sdk.version}
|
|
294
|
+
</code>
|
|
295
|
+
</a>
|
|
296
|
+
</li>
|
|
297
|
+
))}
|
|
298
|
+
</ul>
|
|
299
|
+
</div>
|
|
300
|
+
)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function ChangelogPage() {
|
|
304
|
+
const { t } = useTranslation('common')
|
|
305
|
+
const [activeTag, setActiveTag] = useState<string>('all')
|
|
306
|
+
const [selectedEntryId, setSelectedEntryId] = useState<string | null>(null)
|
|
307
|
+
const { entries } = Route.useLoaderData()
|
|
308
|
+
|
|
309
|
+
const filteredEntries = useMemo(() => {
|
|
310
|
+
if (activeTag === 'all') return entries
|
|
311
|
+
return entries.filter((entry) => entry.tags.includes(activeTag))
|
|
312
|
+
}, [activeTag, entries])
|
|
313
|
+
|
|
314
|
+
const grouped = useMemo(() => {
|
|
315
|
+
const groups: Record<string, DeveloperLogEntry[]> = {}
|
|
316
|
+
filteredEntries.forEach((entry) => {
|
|
317
|
+
if (!groups[entry.year]) groups[entry.year] = []
|
|
318
|
+
groups[entry.year].push(entry)
|
|
319
|
+
})
|
|
320
|
+
return Object.entries(groups).sort(([a], [b]) => Number(b) - Number(a))
|
|
321
|
+
}, [filteredEntries])
|
|
322
|
+
|
|
323
|
+
const selectedEntry = useMemo(
|
|
324
|
+
() => entries.find((e) => e.id === selectedEntryId) ?? null,
|
|
325
|
+
[entries, selectedEntryId]
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
const handleEntryClick = (id: string) => {
|
|
329
|
+
setSelectedEntryId(id)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const handleBack = () => {
|
|
333
|
+
setSelectedEntryId(null)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return (
|
|
337
|
+
<div className="h-full flex">
|
|
338
|
+
<ChangelogSidebar />
|
|
339
|
+
|
|
340
|
+
<div className="flex-1 min-w-0 flex flex-col h-full pl-8 pr-6 py-5">
|
|
341
|
+
{selectedEntry ? (
|
|
342
|
+
<ChangelogDetail entry={selectedEntry} onBack={handleBack} />
|
|
343
|
+
) : (
|
|
344
|
+
<>
|
|
345
|
+
<div className="shrink-0 pb-3">
|
|
346
|
+
<TagFilter activeTag={activeTag} onTagChange={setActiveTag} />
|
|
347
|
+
</div>
|
|
348
|
+
|
|
349
|
+
<div className="flex-1 min-h-0 overflow-y-auto">
|
|
350
|
+
{grouped.length === 0 ? (
|
|
351
|
+
<div className="flex flex-col items-center justify-center h-40 text-center">
|
|
352
|
+
<ScrollText className="size-8 text-muted-foreground/40 mb-2" />
|
|
353
|
+
<p className="text-sm text-muted-foreground">{t('changelog.noMatch')}</p>
|
|
354
|
+
<p className="text-xs text-muted-foreground mt-0.5">{t('changelog.noMatchHint')}</p>
|
|
355
|
+
</div>
|
|
356
|
+
) : (
|
|
357
|
+
<div className="space-y-4">
|
|
358
|
+
{grouped.map(([year, yearEntries]) => (
|
|
359
|
+
<div key={year}>
|
|
360
|
+
<YearHeader year={year} count={yearEntries.length} />
|
|
361
|
+
<div className="space-y-0">
|
|
362
|
+
{yearEntries.map((entry) => (
|
|
363
|
+
<ChangelogEntry
|
|
364
|
+
key={entry.id}
|
|
365
|
+
entry={entry}
|
|
366
|
+
onClick={handleEntryClick}
|
|
367
|
+
/>
|
|
368
|
+
))}
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
))}
|
|
372
|
+
</div>
|
|
373
|
+
)}
|
|
374
|
+
</div>
|
|
375
|
+
</>
|
|
376
|
+
)}
|
|
377
|
+
</div>
|
|
378
|
+
</div>
|
|
379
|
+
)
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export { ChangelogPage }
|
|
@@ -21,15 +21,15 @@ import { useHotkey, useHotkeySequences } from "@tanstack/react-hotkeys"
|
|
|
21
21
|
import type { HotkeySequence } from "@tanstack/hotkeys"
|
|
22
22
|
import { useTranslation } from "react-i18next"
|
|
23
23
|
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
} from "@/components/
|
|
24
|
+
CommandMenu,
|
|
25
|
+
CommandMenuContent,
|
|
26
|
+
CommandMenuInput,
|
|
27
|
+
CommandMenuList,
|
|
28
|
+
CommandMenuEmpty,
|
|
29
|
+
CommandMenuGroup,
|
|
30
|
+
CommandMenuItem,
|
|
31
|
+
useCommandMenu,
|
|
32
|
+
} from "@/components/hextaui/command-menu"
|
|
33
33
|
import { CreatePRDDialog } from "@allbluecn/plugins-core/prd/components/dialog/create-prd-dialog"
|
|
34
34
|
import { useCommands, type ResolvedCommand } from "@/hooks/use-commands"
|
|
35
35
|
import {
|
|
@@ -54,8 +54,8 @@ import {
|
|
|
54
54
|
ShoppingCart,
|
|
55
55
|
Building2,
|
|
56
56
|
Settings,
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
SunMoon,
|
|
58
|
+
LogOut,
|
|
59
59
|
} from "lucide-react"
|
|
60
60
|
import type { LucideIcon } from "lucide-react"
|
|
61
61
|
|
|
@@ -76,8 +76,8 @@ const PALETTE_ICON_MAP: Record<string, LucideIcon> = {
|
|
|
76
76
|
cart: ShoppingCart,
|
|
77
77
|
building2: Building2,
|
|
78
78
|
settings: Settings,
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
sunMoon: SunMoon,
|
|
80
|
+
logOut: LogOut,
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
export function CommandPalette() {
|
|
@@ -100,15 +100,12 @@ export function CommandPalette() {
|
|
|
100
100
|
},
|
|
101
101
|
})
|
|
102
102
|
|
|
103
|
-
const handleClose = () => setCommandPaletteOpen(false)
|
|
104
|
-
|
|
105
103
|
const run = (cmd: ResolvedCommand) => {
|
|
106
|
-
|
|
104
|
+
setCommandPaletteOpen(false)
|
|
107
105
|
execute(cmd)
|
|
108
106
|
}
|
|
109
107
|
|
|
110
108
|
useHotkey("Mod+K", toggleCommandPalette, { ignoreInputs: false })
|
|
111
|
-
|
|
112
109
|
useHotkey("Mod+Shift+L", () => setTheme(theme === "dark" ? "light" : "dark"), {
|
|
113
110
|
ignoreInputs: false,
|
|
114
111
|
})
|
|
@@ -123,50 +120,101 @@ export function CommandPalette() {
|
|
|
123
120
|
})),
|
|
124
121
|
)
|
|
125
122
|
|
|
123
|
+
return (
|
|
124
|
+
<>
|
|
125
|
+
<CommandMenu
|
|
126
|
+
open={open}
|
|
127
|
+
onOpenChange={(next) => {
|
|
128
|
+
if (next) toggleCommandPalette()
|
|
129
|
+
else setCommandPaletteOpen(false)
|
|
130
|
+
}}
|
|
131
|
+
>
|
|
132
|
+
<CommandMenuContent className="w-205">
|
|
133
|
+
<CommandMenuInput
|
|
134
|
+
placeholder={t("command.searchPlaceholder")}
|
|
135
|
+
/>
|
|
136
|
+
<CommandMenuList maxHeight="500px">
|
|
137
|
+
<CommandPaletteList commands={commands} onRun={run} t={t} />
|
|
138
|
+
</CommandMenuList>
|
|
139
|
+
</CommandMenuContent>
|
|
140
|
+
</CommandMenu>
|
|
141
|
+
<CreatePRDDialog open={prdDialogOpen} onOpenChange={setPrdDialogOpen} />
|
|
142
|
+
</>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function resolveShortcut(shortcut: string[]): string {
|
|
147
|
+
const isMac = typeof navigator !== "undefined"
|
|
148
|
+
&& (navigator.platform.toUpperCase().includes("MAC")
|
|
149
|
+
|| navigator.userAgent.toUpperCase().includes("MAC"))
|
|
150
|
+
return shortcut.map((k) => {
|
|
151
|
+
const upper = k.toUpperCase()
|
|
152
|
+
if (upper === "MOD") return isMac ? "CMD" : "CTRL"
|
|
153
|
+
return upper
|
|
154
|
+
}).join("+")
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function CommandPaletteList({
|
|
158
|
+
commands,
|
|
159
|
+
onRun,
|
|
160
|
+
t,
|
|
161
|
+
}: {
|
|
162
|
+
commands: ResolvedCommand[]
|
|
163
|
+
onRun: (cmd: ResolvedCommand) => void
|
|
164
|
+
t: (key: string) => string
|
|
165
|
+
}) {
|
|
166
|
+
const { value } = useCommandMenu()
|
|
167
|
+
|
|
126
168
|
const navCommands = commands.filter((c) => c.group === "navigation")
|
|
127
169
|
const actionCommands = commands.filter((c) => c.group === "action")
|
|
128
170
|
|
|
171
|
+
const filterCommands = (cmds: ResolvedCommand[]): ResolvedCommand[] => {
|
|
172
|
+
if (!value) return cmds
|
|
173
|
+
const lower = value.toLowerCase()
|
|
174
|
+
return cmds.filter(
|
|
175
|
+
(c) =>
|
|
176
|
+
c.title.toLowerCase().includes(lower) ||
|
|
177
|
+
(c.keywords ?? []).some((k) => k.toLowerCase().includes(lower)),
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const filteredNav = filterCommands(navCommands)
|
|
182
|
+
const filteredAction = filterCommands(actionCommands)
|
|
183
|
+
|
|
184
|
+
if (filteredNav.length === 0 && filteredAction.length === 0) {
|
|
185
|
+
return <CommandMenuEmpty>{t("command.empty")}</CommandMenuEmpty>
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
let index = 0
|
|
189
|
+
|
|
129
190
|
const renderItem = (cmd: ResolvedCommand) => {
|
|
130
191
|
const Icon = cmd.icon ? PALETTE_ICON_MAP[cmd.icon] : undefined
|
|
192
|
+
const idx = index++
|
|
131
193
|
return (
|
|
132
|
-
<
|
|
194
|
+
<CommandMenuItem
|
|
133
195
|
key={cmd.id}
|
|
134
|
-
|
|
135
|
-
onSelect={() =>
|
|
196
|
+
index={idx}
|
|
197
|
+
onSelect={() => onRun(cmd)}
|
|
198
|
+
shortcut={cmd.shortcut ? resolveShortcut(cmd.shortcut) : undefined}
|
|
199
|
+
icon={Icon && <Icon className="size-4 shrink-0" />}
|
|
136
200
|
>
|
|
137
|
-
{Icon && <Icon className="size-4 shrink-0" />}
|
|
138
201
|
<span className="truncate">{cmd.title}</span>
|
|
139
|
-
|
|
140
|
-
<CommandShortcut>{cmd.shortcut.join(" → ")}</CommandShortcut>
|
|
141
|
-
)}
|
|
142
|
-
</CommandItem>
|
|
202
|
+
</CommandMenuItem>
|
|
143
203
|
)
|
|
144
204
|
}
|
|
145
205
|
|
|
146
206
|
return (
|
|
147
207
|
<>
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
<CommandList className="max-h-100">
|
|
159
|
-
<CommandEmpty>{t("command.empty")}</CommandEmpty>
|
|
160
|
-
<CommandGroup heading={t("command.groupNavigation")}>
|
|
161
|
-
{navCommands.map(renderItem)}
|
|
162
|
-
</CommandGroup>
|
|
163
|
-
<CommandGroup heading={t("command.groupAction")}>
|
|
164
|
-
{actionCommands.map(renderItem)}
|
|
165
|
-
</CommandGroup>
|
|
166
|
-
</CommandList>
|
|
167
|
-
</Command>
|
|
168
|
-
</CommandDialog>
|
|
169
|
-
<CreatePRDDialog open={prdDialogOpen} onOpenChange={setPrdDialogOpen} />
|
|
208
|
+
{filteredNav.length > 0 && (
|
|
209
|
+
<CommandMenuGroup heading={t("command.groupNavigation")}>
|
|
210
|
+
{filteredNav.map(renderItem)}
|
|
211
|
+
</CommandMenuGroup>
|
|
212
|
+
)}
|
|
213
|
+
{filteredAction.length > 0 && (
|
|
214
|
+
<CommandMenuGroup heading={t("command.groupAction")}>
|
|
215
|
+
{filteredAction.map(renderItem)}
|
|
216
|
+
</CommandMenuGroup>
|
|
217
|
+
)}
|
|
170
218
|
</>
|
|
171
219
|
)
|
|
172
220
|
}
|