@allbluecn/web-app 0.24.4 → 0.24.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allbluecn/web-app",
3
- "version": "0.24.4",
3
+ "version": "0.24.5",
4
4
  "license": "AGPL-3.0-or-later",
5
5
  "type": "module",
6
6
  "files": [
@@ -65,18 +65,24 @@ afterEach(() => {
65
65
  })
66
66
 
67
67
  describe('DesktopSyncIndicator 三态徽标(A11)', () => {
68
- it('初始查询建立基线:guest 徽标 + i18n 文案', async () => {
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
- const badge = await screen.findByTestId('desktop-state-badge')
72
- expect(badge.getAttribute('data-desktop-state')).toBe('guest')
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 screen.findByTestId('desktop-state-badge')
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 screen.findByTestId('desktop-state-badge')
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('三态齐备时同步相位行与徽标共存(双行)', async () => {
125
+ it('三态齐备时同步相位行与徽标共存(双行,非 guest 态)', async () => {
120
126
  bridgeRef.current = makeBridge()
121
127
  render(<DesktopSyncIndicator />)
122
- await screen.findByTestId('desktop-state-badge')
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.desktop_state_guest())
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
- {deskState && (
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,12 @@
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
+ import { useNavigate } from '@tanstack/react-router'
20
+ import { ArrowLeft } from 'lucide-react'
21
+ import { Button } from '@/components/ui/button'
22
+ import { isDesktopEdition } from '@/lib/edition-desktop'
23
+ import { m } from '@/paraglide/messages.js'
19
24
  import { AnimatedThemeToggler } from '@/components/shared-kit/theme/animated-theme-toggler'
20
25
 
21
26
  interface AuthLayoutProps {
@@ -25,10 +30,27 @@ interface AuthLayoutProps {
25
30
  export function AuthLayout({ children }: AuthLayoutProps) {
26
31
  const left = findChild(children, AuthLayoutLeft)
27
32
  const right = findChild(children, AuthLayoutRight)
33
+ const navigate = useNavigate()
28
34
 
29
35
  return (
30
36
  <div className="flex min-h-screen">
31
37
  <div className="relative flex flex-1 items-center justify-center p-6">
38
+ {/* 桌面壳:认证页左上角返回(免登录态可随时回到应用)。浏览器/云端不展示——
39
+ redirect 流程中 history.back 会回到需登录页再被弹回登录,形成死循环 */}
40
+ {isDesktopEdition() && (
41
+ <Button
42
+ variant="ghost"
43
+ size="icon"
44
+ className="absolute left-6 top-6 size-8"
45
+ aria-label={m.auth_layout_back()}
46
+ onClick={() => {
47
+ if (window.history.length > 1) window.history.back()
48
+ else void navigate({ to: '/dashboard' })
49
+ }}
50
+ >
51
+ <ArrowLeft className="size-4" />
52
+ </Button>
53
+ )}
32
54
  <div className="absolute" style={{ top: '1.5rem', right: '1.5rem' }}>
33
55
  <AnimatedThemeToggler className="flex size-6 items-center justify-center rounded-full hover:bg-accent" />
34
56
  </div>
@@ -55,11 +77,11 @@ export function AuthLayoutRight({ children }: { children: ReactNode }) {
55
77
  return <>{children}</>
56
78
  }
57
79
 
58
- function findChild(children: ReactNode, type: any) {
80
+ function findChild(children: ReactNode, type: ElementType) {
59
81
  const arr = Array.isArray(children) ? children : [children]
60
82
  for (const child of arr) {
61
- if ((child as any)?.type === type) {
62
- return (child as any).props.children
83
+ if ((child as { type?: unknown })?.type === type) {
84
+ return (child as { props: { children: ReactNode } }).props.children
63
85
  }
64
86
  }
65
87
  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.4",
115
+ "version": "v0.24.5",
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
  };
@@ -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>
@@ -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
+ }