diff --git a/product-source/hololake-native-desktop/src/main.tsx b/product-source/hololake-native-desktop/src/main.tsx index 725aae611..8996d2685 100644 --- a/product-source/hololake-native-desktop/src/main.tsx +++ b/product-source/hololake-native-desktop/src/main.tsx @@ -2,8 +2,35 @@ import { StrictMode, useCallback, useEffect, useMemo, useRef, useState } from 'r import { createRoot } from 'react-dom/client' import { invoke } from '@tauri-apps/api/core' import { splitFrontmatter, documentOutline, jumpToHeading, MarkdownDocument, markdownHtml } from './modules/knowledge-render' +import nightLake from './assets/hololake-night-lake.png' const TAG_TINTS = ['tag-lavender', 'tag-sky', 'tag-mint', 'tag-amber', 'tag-rose', 'tag-slate'] + +// 编号自动补齐:人只敲字母与数字,杠由系统按已登记文法自动出。 +// ICE 系(ICE-GL∞ / ICE-P-ZY001)、GLS 系(GLS-LA-20260720-003);认不出的文法只在字母数字交界处补杠。 +function formatGateNumber(raw: string): string { + const up = raw.toUpperCase() + if (up.startsWith('ICE')) { + const rest = up.slice(3) + if (!rest) return 'ICE' + if (rest.startsWith('P')) { + const body = rest.slice(1) + return body ? `ICE-P-${body}` : 'ICE-P' + } + return `ICE-${rest}` + } + if (up.startsWith('GLS')) { + const rest = up.slice(3) + if (!rest) return 'GLS' + const letters = rest.match(/^[A-Z]*/)?.[0] ?? '' + const digits = rest.slice(letters.length) + if (!digits) return `GLS-${letters}` + const d1 = digits.slice(0, 8) + const d2 = digits.slice(8, 11) + return `GLS-${letters}-${d1}${d2 ? `-${d2}` : ''}` + } + return up.replace(/([A-Z]+)([0-9])/g, '$1-$2').replace(/([0-9]+)([A-Z])/g, '$1-$2') +} function tagTint(tag: string) { let hash = 0 for (const char of tag) hash = (hash * 31 + (char.codePointAt(0) || 0)) % 997 @@ -257,6 +284,21 @@ function HoloLakeApp() { const [loginBusy, setLoginBusy] = useState(false) const [loginRising, setLoginRising] = useState(false) const [loginMessage, setLoginMessage] = useState('') + const [gateStage, setGateStage] = useState<'number' | 'key'>('number') + const [gateRaw, setGateRaw] = useState('') + const [gateInf, setGateInf] = useState(false) + const gateNumber = formatGateNumber(gateRaw) + (gateInf ? '∞' : '') + const [gateOpen, setGateOpen] = useState(false) + const gateInputRef = useRef(null) + // 封存舱点开翻转后半程再把光标送进输入格,免得翻到一半抢焦点 + useEffect(() => { + if (!gateOpen || gateStage !== 'number') return + const timer = window.setTimeout(() => gateInputRef.current?.focus(), 640) + return () => window.clearTimeout(timer) + }, [gateOpen, gateStage]) + const [gateBusy, setGateBusy] = useState(false) + const [gateMessage, setGateMessage] = useState('') + const [gateRising, setGateRising] = useState(false) const [systemBusy, setSystemBusy] = useState(false) const [zeroPoint, setZeroPoint] = useState(null) const [zpNumber, setZpNumber] = useState('') @@ -313,6 +355,39 @@ function HoloLakeApp() { if (zeroPoint.route !== 'persona' && view === 'parlor') setView('generic') if (zeroPoint.route === 'persona' && view === 'generic') setView('knowledge') }, [zeroPoint, view]) + // 大门·编号门:本机已有绑定编号时回填,省得主人重报家门 + useEffect(() => { + if (gateStage !== 'number' || gateRaw) return + const bound = zeroPoint?.binding === 'bound' ? zeroPoint.userNumber : '' + if (!bound) return + setGateInf(bound.includes('∞')) + setGateRaw(bound.toUpperCase().replace(/[^A-Z0-9]/g, '')) + }, [zeroPoint, gateStage, gateRaw]) + // 大门·编号门输入:只收字母数字(杠系统补、∞开关点),删到∞时开关跟着灭 + const onGateInput = (value: string) => { + if (gateInf && !value.includes('∞')) setGateInf(false) + setGateRaw(value.toUpperCase().replace(/[^A-Z0-9]/g, '')) + } + // 大门·编号门:先报编号→灯塔查号→只答有效/无效→域浮起→才见钥匙门 + const gateVerifyNumber = async () => { + setGateBusy(true) + setGateMessage('') + try { + await invoke('zero_point_bind', { input: { number: gateNumber } }) + const snapshot = await invoke('zero_point_verify') + setZeroPoint(snapshot) + if (snapshot.route === 'persona') { + setGateRising(true) + window.setTimeout(() => { setGateRising(false); setGateStage('key') }, 2600) + } else { + setGateMessage('编号无效') + } + } catch (error) { + setGateMessage(humanError(error, 'login')) + } finally { + setGateBusy(false) + } + } useEffect(() => { if (view === 'receipts') void loadReceipts() }, [loadReceipts, view]) const visibleDocuments = useMemo(() => { @@ -877,7 +952,49 @@ function HoloLakeApp() { if (!repoLogin) { return
-
光湖CODE REPOSITORY ENTRY

登录光湖代码频道

使用第五域代码仓库(guanghulab.com)的账号与密码登录。登录即验证了背后绑定的服务器;凭证只存本机钥匙串,不落明文。

void performRepoLogin(event)}> setLoginUsername(event.target.value)}/> setLoginPassword(event.target.value)}/>
{loginMessage &&

{loginMessage}

}
+
+