@allbluecn/web-app 0.24.4 → 0.24.6
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 -1
- package/src/components/desktop/__tests__/sync-indicator.test.tsx +16 -9
- package/src/components/desktop/sync-indicator.tsx +3 -1
- package/src/components/shared-kit/auth/auth-layout.tsx +4 -4
- package/src/lib/changelog/sdk-versions.ts +1 -1
- package/src/paraglide/messages/_index.js +15 -0
- package/src/paraglide/messages/en.js +5 -0
- package/src/paraglide/messages/zh.js +5 -0
- package/src/routes/__root.tsx +2 -1
- package/src/routes/_login/login/route.lazy.tsx +24 -4
- package/src/styles/globals.css +11 -0
package/package.json
CHANGED
|
@@ -65,18 +65,24 @@ afterEach(() => {
|
|
|
65
65
|
})
|
|
66
66
|
|
|
67
67
|
describe('DesktopSyncIndicator 三态徽标(A11)', () => {
|
|
68
|
-
|
|
68
|
+
// guest(本地模式)不再渲染徽标(2026-11-03 用户裁决)→ 无基线可等;
|
|
69
|
+
// 先 flush 初始 desktopState 查询(迟到 resolve 会覆盖后续推送态)
|
|
70
|
+
const flushInitialQuery = () =>
|
|
71
|
+
act(async () => {
|
|
72
|
+
await new Promise((r) => setTimeout(r, 0))
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('guest(本地模式)默认态不渲染徽标(2026-11-03 用户裁决移除常驻显示)', async () => {
|
|
69
76
|
bridgeRef.current = makeBridge()
|
|
70
77
|
render(<DesktopSyncIndicator />)
|
|
71
|
-
|
|
72
|
-
expect(
|
|
73
|
-
expect(badge.textContent).toContain(m.desktop_state_guest())
|
|
78
|
+
await flushInitialQuery()
|
|
79
|
+
expect(screen.queryByTestId('desktop-state-badge')).toBeNull()
|
|
74
80
|
})
|
|
75
81
|
|
|
76
82
|
it('事件推送 transferring:徽标随推送更新(过境态)', async () => {
|
|
77
83
|
bridgeRef.current = makeBridge()
|
|
78
84
|
render(<DesktopSyncIndicator />)
|
|
79
|
-
await
|
|
85
|
+
await flushInitialQuery()
|
|
80
86
|
await act(async () => {
|
|
81
87
|
stateListener?.({ kind: 'transferring', updatedAt: '2026-11-01T00:00:01Z' })
|
|
82
88
|
})
|
|
@@ -88,7 +94,7 @@ describe('DesktopSyncIndicator 三态徽标(A11)', () => {
|
|
|
88
94
|
it('事件推送 cloud:徽标迁云态(身份切换终态)', async () => {
|
|
89
95
|
bridgeRef.current = makeBridge()
|
|
90
96
|
render(<DesktopSyncIndicator />)
|
|
91
|
-
await
|
|
97
|
+
await flushInitialQuery()
|
|
92
98
|
await act(async () => {
|
|
93
99
|
stateListener?.({ kind: 'cloud', updatedAt: '2026-11-01T00:00:02Z' })
|
|
94
100
|
})
|
|
@@ -116,15 +122,16 @@ describe('DesktopSyncIndicator 三态徽标(A11)', () => {
|
|
|
116
122
|
expect(container.textContent).toBe('')
|
|
117
123
|
})
|
|
118
124
|
|
|
119
|
-
it('
|
|
125
|
+
it('三态齐备时同步相位行与徽标共存(双行,非 guest 态)', async () => {
|
|
120
126
|
bridgeRef.current = makeBridge()
|
|
121
127
|
render(<DesktopSyncIndicator />)
|
|
122
|
-
await
|
|
128
|
+
await flushInitialQuery()
|
|
123
129
|
await act(async () => {
|
|
130
|
+
stateListener?.({ kind: 'transferring', updatedAt: '2026-11-01T00:00:00Z' })
|
|
124
131
|
syncListener?.({ phase: 'idle', pending: 0, lastError: null, lastSyncedAt: '2026-11-01' })
|
|
125
132
|
})
|
|
126
133
|
const wrapper = screen.getByTestId('desktop-sync-indicator')
|
|
127
|
-
expect(wrapper.textContent).toContain(m.
|
|
134
|
+
expect(wrapper.textContent).toContain(m.desktop_state_transferring())
|
|
128
135
|
expect(wrapper.textContent).toContain('已同步')
|
|
129
136
|
})
|
|
130
137
|
})
|
|
@@ -85,7 +85,9 @@ export function DesktopSyncIndicator() {
|
|
|
85
85
|
|
|
86
86
|
return (
|
|
87
87
|
<div className="space-y-0.5" data-testid="desktop-sync-indicator">
|
|
88
|
-
{
|
|
88
|
+
{/* guest(本地模式)是默认态、常驻无信息量——用户裁决不展示(2026-11-03);
|
|
89
|
+
仅展示过境/迁云等有信息量的状态 */}
|
|
90
|
+
{deskState && deskState !== "guest" && (
|
|
89
91
|
<div
|
|
90
92
|
className="flex items-center gap-1.5 px-2 py-1 text-xs text-muted-foreground"
|
|
91
93
|
data-testid="desktop-state-badge"
|
|
@@ -15,7 +15,7 @@
|
|
|
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
|
-
import type { ReactNode } from 'react'
|
|
18
|
+
import type { ElementType, ReactNode } from 'react'
|
|
19
19
|
import { AnimatedThemeToggler } from '@/components/shared-kit/theme/animated-theme-toggler'
|
|
20
20
|
|
|
21
21
|
interface AuthLayoutProps {
|
|
@@ -55,11 +55,11 @@ export function AuthLayoutRight({ children }: { children: ReactNode }) {
|
|
|
55
55
|
return <>{children}</>
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
function findChild(children: ReactNode, type:
|
|
58
|
+
function findChild(children: ReactNode, type: ElementType) {
|
|
59
59
|
const arr = Array.isArray(children) ? children : [children]
|
|
60
60
|
for (const child of arr) {
|
|
61
|
-
if ((child as
|
|
62
|
-
return (child as
|
|
61
|
+
if ((child as { type?: unknown })?.type === type) {
|
|
62
|
+
return (child as { props: { children: ReactNode } }).props.children
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
return null
|
|
@@ -112,7 +112,7 @@ export const SDK_GROUPS: SdkGroup[] = [
|
|
|
112
112
|
"items": [
|
|
113
113
|
{
|
|
114
114
|
"name": "@allbluecn/web-app",
|
|
115
|
-
"version": "v0.24.
|
|
115
|
+
"version": "v0.24.6",
|
|
116
116
|
"lastUpdate": "Nov 3, 2026",
|
|
117
117
|
"source": "apps/web/package.json",
|
|
118
118
|
"docsUrl": "https://github.com/allbluecn/allblue"
|
|
@@ -289,6 +289,7 @@ import { getLocale, experimentalStaticLocale } from "../runtime.js"
|
|
|
289
289
|
/** @typedef {{}} Auth_Forgotpassword_Submitting1Inputs */
|
|
290
290
|
/** @typedef {{}} Auth_Forgotpassword_Subtitle1Inputs */
|
|
291
291
|
/** @typedef {{}} Auth_Forgotpassword_Title1Inputs */
|
|
292
|
+
/** @typedef {{}} Auth_Layout_BackInputs */
|
|
292
293
|
/** @typedef {{}} Auth_Login_Createaccount1Inputs */
|
|
293
294
|
/** @typedef {{}} Auth_Login_Defaultaction1Inputs */
|
|
294
295
|
/** @typedef {{}} Auth_Login_Emaillabel1Inputs */
|
|
@@ -9953,6 +9954,20 @@ export { auth_forgotpassword_title1 as "auth_forgotPassword_title" }
|
|
|
9953
9954
|
/**
|
|
9954
9955
|
* | output |
|
|
9955
9956
|
* | --- |
|
|
9957
|
+
* | "Back" |
|
|
9958
|
+
*
|
|
9959
|
+
* @param {Auth_Layout_BackInputs} inputs
|
|
9960
|
+
* @param {{ locale?: "zh" | "en" }} options
|
|
9961
|
+
* @returns {LocalizedString}
|
|
9962
|
+
*/
|
|
9963
|
+
export const auth_layout_back = /** @type {((inputs?: Auth_Layout_BackInputs, options?: { locale?: "zh" | "en" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Auth_Layout_BackInputs, { locale?: "zh" | "en" }, {}>} */ ((inputs = {}, options = {}) => {
|
|
9964
|
+
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
|
9965
|
+
if (locale === "en") return __en.auth_layout_back(inputs)
|
|
9966
|
+
return __zh.auth_layout_back(inputs)
|
|
9967
|
+
});
|
|
9968
|
+
/**
|
|
9969
|
+
* | output |
|
|
9970
|
+
* | --- |
|
|
9956
9971
|
* | "Create account" |
|
|
9957
9972
|
*
|
|
9958
9973
|
* @param {Auth_Login_Createaccount1Inputs} inputs
|
|
@@ -287,6 +287,7 @@
|
|
|
287
287
|
/** @typedef {{}} Auth_Forgotpassword_Submitting1Inputs */
|
|
288
288
|
/** @typedef {{}} Auth_Forgotpassword_Subtitle1Inputs */
|
|
289
289
|
/** @typedef {{}} Auth_Forgotpassword_Title1Inputs */
|
|
290
|
+
/** @typedef {{}} Auth_Layout_BackInputs */
|
|
290
291
|
/** @typedef {{}} Auth_Login_Createaccount1Inputs */
|
|
291
292
|
/** @typedef {{}} Auth_Login_Defaultaction1Inputs */
|
|
292
293
|
/** @typedef {{}} Auth_Login_Emaillabel1Inputs */
|
|
@@ -6873,6 +6874,10 @@ export const auth_forgotpassword_title1 = /** @type {(inputs: Auth_Forgotpasswor
|
|
|
6873
6874
|
return /** @type {LocalizedString} */ (`Forgot Password`)
|
|
6874
6875
|
};
|
|
6875
6876
|
|
|
6877
|
+
export const auth_layout_back = /** @type {(inputs: Auth_Layout_BackInputs) => LocalizedString} */ () => {
|
|
6878
|
+
return /** @type {LocalizedString} */ (`Back`)
|
|
6879
|
+
};
|
|
6880
|
+
|
|
6876
6881
|
export const auth_login_createaccount1 = /** @type {(inputs: Auth_Login_Createaccount1Inputs) => LocalizedString} */ () => {
|
|
6877
6882
|
return /** @type {LocalizedString} */ (`Create account`)
|
|
6878
6883
|
};
|
|
@@ -287,6 +287,7 @@
|
|
|
287
287
|
/** @typedef {{}} Auth_Forgotpassword_Submitting1Inputs */
|
|
288
288
|
/** @typedef {{}} Auth_Forgotpassword_Subtitle1Inputs */
|
|
289
289
|
/** @typedef {{}} Auth_Forgotpassword_Title1Inputs */
|
|
290
|
+
/** @typedef {{}} Auth_Layout_BackInputs */
|
|
290
291
|
/** @typedef {{}} Auth_Login_Createaccount1Inputs */
|
|
291
292
|
/** @typedef {{}} Auth_Login_Defaultaction1Inputs */
|
|
292
293
|
/** @typedef {{}} Auth_Login_Emaillabel1Inputs */
|
|
@@ -6873,6 +6874,10 @@ export const auth_forgotpassword_title1 = /** @type {(inputs: Auth_Forgotpasswor
|
|
|
6873
6874
|
return /** @type {LocalizedString} */ (`忘记密码`)
|
|
6874
6875
|
};
|
|
6875
6876
|
|
|
6877
|
+
export const auth_layout_back = /** @type {(inputs: Auth_Layout_BackInputs) => LocalizedString} */ () => {
|
|
6878
|
+
return /** @type {LocalizedString} */ (`返回`)
|
|
6879
|
+
};
|
|
6880
|
+
|
|
6876
6881
|
export const auth_login_createaccount1 = /** @type {(inputs: Auth_Login_Createaccount1Inputs) => LocalizedString} */ () => {
|
|
6877
6882
|
return /** @type {LocalizedString} */ (`创建账号`)
|
|
6878
6883
|
};
|
package/src/routes/__root.tsx
CHANGED
|
@@ -36,6 +36,7 @@ import { Button } from '@/components/ui/button'
|
|
|
36
36
|
import type { Session } from '@/lib/auth/auth-client'
|
|
37
37
|
import { getLocale } from '@/paraglide/runtime.js'
|
|
38
38
|
import { m } from "@/paraglide/messages.js";
|
|
39
|
+
import { isDesktopEdition } from '@/lib/edition-desktop'
|
|
39
40
|
|
|
40
41
|
export const Route = createRootRouteWithContext<RouterContext>()({
|
|
41
42
|
beforeLoad: async () => {
|
|
@@ -83,7 +84,7 @@ function RootComponent() {
|
|
|
83
84
|
}, [])
|
|
84
85
|
|
|
85
86
|
return (
|
|
86
|
-
<html lang={getLocale()} suppressHydrationWarning>
|
|
87
|
+
<html lang={getLocale()} suppressHydrationWarning className={isDesktopEdition() ? 'desktop-shell' : undefined}>
|
|
87
88
|
<head>
|
|
88
89
|
<HeadContent />
|
|
89
90
|
</head>
|
|
@@ -16,14 +16,15 @@
|
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
18
|
import { useEffect } from 'react'
|
|
19
|
-
import { createLazyFileRoute, Link } from '@tanstack/react-router'
|
|
19
|
+
import { createLazyFileRoute, Link, useNavigate } from '@tanstack/react-router'
|
|
20
20
|
import { toast } from '@/components/ui/toast'
|
|
21
21
|
import { RainbowButton } from '@/components/magicui/rainbow-button'
|
|
22
|
+
import { Button } from '@/components/ui/button'
|
|
22
23
|
import { Checkbox } from '@/components/ui/checkbox'
|
|
23
24
|
import { Field, FieldLabel } from '@/components/ui/field'
|
|
24
25
|
import { Separator } from '@/components/ui/separator'
|
|
25
26
|
import { useAppForm, firstFieldErrorMessage } from '@/components/forms'
|
|
26
|
-
import { ArrowRight, Mail } from 'lucide-react'
|
|
27
|
+
import { ArrowLeft, ArrowRight, Mail } from 'lucide-react'
|
|
27
28
|
import { AuthLayout, AuthLayoutLeft, AuthLayoutRight } from '@/components/auth/auth-layout'
|
|
28
29
|
import { AuthHeader } from '@/components/auth/auth-header'
|
|
29
30
|
import { AuthBrandPanel } from '@/components/auth/auth-brand-panel'
|
|
@@ -36,6 +37,7 @@ import { buildAuthFeatures } from '@/components/auth/auth-features'
|
|
|
36
37
|
import { loginSchema } from '@/routes/_login/schemas/-auth'
|
|
37
38
|
import { m } from "@/paraglide/messages.js";
|
|
38
39
|
import { submitCredentialsLogin } from '@/lib/auth/submit-credentials'
|
|
40
|
+
import { isDesktopEdition } from '@/lib/edition-desktop'
|
|
39
41
|
|
|
40
42
|
export const Route = createLazyFileRoute('/_login/login')({
|
|
41
43
|
component: LoginPage,
|
|
@@ -44,6 +46,7 @@ export const Route = createLazyFileRoute('/_login/login')({
|
|
|
44
46
|
function LoginPage() {
|
|
45
47
|
const { redirect: redirectTo, error: oauthError, ssoToken, ssoError } = Route.useSearch()
|
|
46
48
|
const { googleEnabled } = Route.useLoaderData()
|
|
49
|
+
const navigate = useNavigate()
|
|
47
50
|
|
|
48
51
|
useEffect(() => {
|
|
49
52
|
if (ssoError) {
|
|
@@ -171,8 +174,25 @@ function LoginPage() {
|
|
|
171
174
|
/>
|
|
172
175
|
</form>
|
|
173
176
|
</form.AppForm>
|
|
174
|
-
|
|
175
|
-
|
|
177
|
+
{/* 桌面壳:返回应用(免登录态随时可回),置于「还没有账号?」前。浏览器/
|
|
178
|
+
云端不展示——redirect 流程中 history.back 会回到需登录页再被弹回,死循环 */}
|
|
179
|
+
<div className="flex items-center justify-center gap-1 text-sm">
|
|
180
|
+
{isDesktopEdition() && (
|
|
181
|
+
<Button
|
|
182
|
+
variant="ghost"
|
|
183
|
+
size="sm"
|
|
184
|
+
className="h-7 gap-1 px-2 text-muted-foreground"
|
|
185
|
+
aria-label={m.auth_layout_back()}
|
|
186
|
+
onClick={() => {
|
|
187
|
+
if (window.history.length > 1) window.history.back()
|
|
188
|
+
else void navigate({ to: '/dashboard' })
|
|
189
|
+
}}
|
|
190
|
+
>
|
|
191
|
+
<ArrowLeft className="size-3.5" />
|
|
192
|
+
{m.auth_layout_back()}
|
|
193
|
+
</Button>
|
|
194
|
+
)}
|
|
195
|
+
<span>{m.auth_login_noAccount()}</span>
|
|
176
196
|
<Link to="/register" className="font-medium text-primary hover:underline">{m.auth_login_createAccount()}</Link>
|
|
177
197
|
</div>
|
|
178
198
|
</AuthLayoutLeft>
|
package/src/styles/globals.css
CHANGED
|
@@ -862,3 +862,14 @@ html.theme-transitioning *::after {
|
|
|
862
862
|
.app-region-no-drag {
|
|
863
863
|
-webkit-app-region: no-drag;
|
|
864
864
|
}
|
|
865
|
+
|
|
866
|
+
/* 桌面壳原生毛玻璃侧栏(2026-11-03):Electron darwin 侧 vibrancy:'sidebar'
|
|
867
|
+
(Pro 仓 main.ts)在网页内容之下铺系统毛玻璃,desktop-shell 置透明 body 让其
|
|
868
|
+
露出,侧栏卡片半透明染色 → 随壁纸/窗外内容隐约变化(macOS 原生质感)。
|
|
869
|
+
内容卡片(bg-background)保持不透明;浏览器/云端无 desktop-shell 类,全 no-op。 */
|
|
870
|
+
html.desktop-shell body {
|
|
871
|
+
background-color: transparent;
|
|
872
|
+
}
|
|
873
|
+
html.desktop-shell [data-slot='sidebar-inner'] {
|
|
874
|
+
background-color: color-mix(in srgb, var(--sidebar) 72%, transparent);
|
|
875
|
+
}
|