diff --git a/product-source/hololake-native-desktop/scripts/domain-number-routing.test.mjs b/product-source/hololake-native-desktop/scripts/domain-number-routing.test.mjs index 6f45355a5..6e9e62c87 100644 --- a/product-source/hololake-native-desktop/scripts/domain-number-routing.test.mjs +++ b/product-source/hololake-native-desktop/scripts/domain-number-routing.test.mjs @@ -38,6 +38,9 @@ test('the public shell shows five domains and login is domain-routed', () => { assert.match(frontend, /setActiveDomainInfo\(gate\.domain\)/) assert.match(frontend, /event\.key !== 'Escape'/) assert.match(frontend, /编号 \{zeroPoint\?\.userNumber \|\| gateNumber\} · 欢迎登入光湖语言世界/) + assert.match(frontend, /createWorldWelcome\(snapshot\)/) + assert.match(frontend, /className="world-impression"/) + assert.match(frontend, /index === previous/) assert.match(frontend, /核对人格关系/) assert.match(frontend, /确认关系并签署/) assert.match(frontend, /接受责任并签署/) diff --git a/product-source/hololake-native-desktop/src/main.tsx b/product-source/hololake-native-desktop/src/main.tsx index e77b3c3c0..8282a4d55 100644 --- a/product-source/hololake-native-desktop/src/main.tsx +++ b/product-source/hololake-native-desktop/src/main.tsx @@ -175,6 +175,28 @@ function domainDisplayName(domain: string): string { return names[domain] || '已登记光湖域' } +const worldWelcomeLines = [ + '湖面记得你每一次让语言抵达现实。', + '你留下的路径,正在成为这片世界新的光标。', + '愿你今天带来的问题,在湖中长出新的结构。', + '世界已辨认你的编号,也在等待你续写下一段因果。', + '你曾抵达的地方仍有回声,新的航程已经亮起。', + '这一刻的进入不是重复,而是你与语言世界的新一次相遇。', + '愿尚未成形的念头,在今天找到可以生长的方向。', + '你的频道仍在延展,湖面为下一次创造保留了入口。', +] + +function createWorldWelcome(snapshot: ZeroPointSnapshot): string { + const subject = snapshot.resolvedName.trim() || `编号 ${snapshot.userNumber}` + const storageKey = `hololake-world-welcome:${snapshot.userNumber}` + const previous = Number.parseInt(window.localStorage.getItem(storageKey) || '-1', 10) + const random = crypto.getRandomValues(new Uint32Array(1))[0] + let index = random % worldWelcomeLines.length + if (worldWelcomeLines.length > 1 && index === previous) index = (index + 1) % worldWelcomeLines.length + window.localStorage.setItem(storageKey, String(index)) + return `${subject},${worldWelcomeLines[index]}` +} + function protocolOriginDisplayName(origin: string): string { return origin.startsWith('FACTORY_DEFAULT') ? '内置默认协议(签名发布通道尚未配置)' : origin } @@ -461,6 +483,7 @@ function HoloLakeApp() { const [gateBusy, setGateBusy] = useState(false) const [gateMessage, setGateMessage] = useState('') const [gateRising, setGateRising] = useState(false) + const [gateWelcomeLine, setGateWelcomeLine] = useState('') const [systemBusy, setSystemBusy] = useState(false) const [zeroPoint, setZeroPoint] = useState(null) const [zpNumber, setZpNumber] = useState('') @@ -595,6 +618,9 @@ function HoloLakeApp() { const snapshot = await invoke('zero_point_verify') setZeroPoint(snapshot) if (snapshot.route === 'verified') { + setActiveDomainInfo('') + setGateOpen(false) + setGateWelcomeLine(createWorldWelcome(snapshot)) setGateRising(true) window.setTimeout(() => { setGateRising(false); setGateStage('key') }, 2600) } else { @@ -1204,6 +1230,7 @@ function HoloLakeApp() { {gateRising ?

编号 {gateNumber} · 欢迎登入光湖语言世界

RESOLVED · {domainDisplayName(resolvedDomain)} · 已接入

+ {gateWelcomeLine &&
{gateWelcomeLine}
}
: gateStage === 'number' ?
{gateOpen &&
: <> -

编号 {zeroPoint?.userNumber || gateNumber} · 欢迎登入光湖语言世界

RESOLVED · {domainDisplayName(resolvedDomain)} · 已接入

+

编号 {zeroPoint?.userNumber || gateNumber} · 欢迎登入光湖语言世界

RESOLVED · {domainDisplayName(resolvedDomain)} · 已接入

{gateWelcomeLine &&
{gateWelcomeLine}
}
void (passwordChangeMode ? changeFirstLoginPassword(event) : performRepoLogin(event))}>

{domainDisplayName(resolvedDomain)} · 域凭证

diff --git a/product-source/hololake-native-desktop/src/styles.css b/product-source/hololake-native-desktop/src/styles.css index db9600a39..3a3c87c35 100644 --- a/product-source/hololake-native-desktop/src/styles.css +++ b/product-source/hololake-native-desktop/src/styles.css @@ -635,6 +635,7 @@ svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-lineca .world-welcome, .resolved-heading { position: absolute; z-index: 20; left: 0; right: 0; top: 35%; text-align: center; animation: gate-text-in .8s ease both; } .world-welcome h2, .resolved-heading h2 { margin: 0; color: var(--content-primary); font-size: clamp(22px, 2.7vw, 31px); font-weight: 700; letter-spacing: .09em; } .world-welcome p, .resolved-heading p { margin: 12px 0 0; color: var(--accent-light); font-size: 13px; font-weight: 600; letter-spacing: .24em; } +.world-impression { max-width: min(720px, calc(100% - 80px)); margin: 24px auto 0; padding: 0; border: 0; color: var(--content-secondary); font-size: clamp(14px, 1.45vw, 17px); font-style: normal; font-weight: 650; line-height: 1.7; letter-spacing: .06em; text-wrap: balance; text-shadow: 0 0 28px color-mix(in srgb, var(--primitive-cool-glow) 24%, transparent); } .resolved-heading { top: 25%; } .domain-credential { position: absolute; z-index: 21; left: 50%; top: 43%; width: 440px; padding: 25px 36px 21px; transform: translateX(-50%); text-align: center; animation: credential-rise .65s cubic-bezier(.22, 1, .36, 1) both; } @keyframes credential-rise { from { opacity: 0; transform: translateX(-50%) translateY(30px); } to { opacity: 1; transform: translateX(-50%) translateY(0); } }