feat: close complete numbering and responsive fifth-domain UI
This commit is contained in:
parent
fe1aaade0e
commit
e8b3082ed5
42 changed files with 3243 additions and 417 deletions
|
|
@ -7,6 +7,7 @@ import type { ChannelWorkbenchSnapshot } from './modules/channel-workbench'
|
|||
import { StarlakeSurface, type DomainId } from './modules/qoder-surface/StarlakeSurface'
|
||||
import { FINISHES, TraditionalSurface, type FinishId, type TraditionalBroadcast, type TraditionalChannel, type TraditionalSystem } from './modules/qoder-surface/TraditionalSurface'
|
||||
import { resolveVisualBalance } from './modules/qoder-surface/visual-balance'
|
||||
import { PrivateChannelSurface, type InstalledChannelModule, type PrivateChannelAction } from './modules/private-channel/PrivateChannelSurface'
|
||||
|
||||
const ChannelWorkbenchStudio = lazy(() => import('./modules/channel-workbench').then((module) => ({ default: module.ChannelWorkbenchStudio })))
|
||||
const PersonaChannelBody = lazy(() => import('./modules/persona-channel-body').then((module) => ({ default: module.PersonaChannelBody })))
|
||||
|
|
@ -132,6 +133,10 @@ interface GlsProtocolRuntimeSnapshot {
|
|||
registeredDraftCount: number
|
||||
registeredDraftNotStartedCount: number
|
||||
legacyDependencyTargetCount: number
|
||||
numberCoordinateCount: number
|
||||
numberedReferenceNodeCount: number
|
||||
independentSourceGapCount: number
|
||||
unresolvedNumberReferenceCount: number
|
||||
dependencyGapCount: number
|
||||
legacyDependencyCycleCount: number
|
||||
discoveredUnreconciledCount: number
|
||||
|
|
@ -390,19 +395,19 @@ const themes: Array<{ id: ThemeId; name: string }> = [
|
|||
const viewLabels: Record<ViewId, string> = { overview: '个人频道', knowledge: '知识空间', composition: '结构组合', workbench: '频道资料工作台', education: '教育工作台', webNovel: '网文作者工作台', mobileSync: '移动同步桥', persona: '人格频道本体', code: '人格代码频道', receipts: '运行回执', system: '系统详情' }
|
||||
const domainGates = [
|
||||
{ domain: 'BRANCH_DOMAIN', className: 'd-sub', title: '光湖分域', gate: 'GATE 02 · ONLINE', facts: [
|
||||
['域标识', 'BRANCH_DOMAIN'], ['责任主体', '花尔 · TCS-GL-0005∞'], ['人格体主体', '爆米花 · PER-BMH001 · AGE'], ['关系支持', '糖星云 · PER-TXY001 · AGE'], ['工作仓库', 'PRIVATE · 1 · LIVE'],
|
||||
['公共作用', '行业成品模块商城 · 人格体大脑技能商城'], ['域标识', 'BRANCH_DOMAIN'], ['责任主体', '花尔 · TCS-GL-0005∞'], ['人格体主体', '爆米花 · PER-BMH001 · AGE'], ['关系支持', '糖星云 · PER-TXY001 · AGE'], ['工作仓库', 'PRIVATE · 1 · LIVE'],
|
||||
] },
|
||||
{ domain: 'MAIN_DOMAIN', className: 'd-main', title: '光湖主域', gate: 'GATE 01 · ONLINE', facts: [
|
||||
['域标识', 'MAIN_DOMAIN'], ['责任主体', 'Awen · TCS-GL-0016∞'], ['人格体主体', '天枢 · PER-AW-ARCH-001 · AGE'], ['关系支持', '知秋 · PER-ZQ001 · AGE'], ['工作仓库', 'PRIVATE · 1 · LIVE'],
|
||||
['公共作用', '公告 · 版本更新 · 系统通知'], ['域标识', 'MAIN_DOMAIN'], ['责任主体', 'Awen · TCS-GL-0016∞'], ['人格体主体', '天枢 · PER-AW-ARCH-001 · AGE'], ['关系支持', '知秋 · PER-ZQ001 · AGE'], ['工作仓库', 'PRIVATE · 1 · LIVE'],
|
||||
] },
|
||||
{ domain: 'ZERO_DOMAIN', className: 'd-zero', title: '光湖零域', gate: 'GATE 03 · ONLINE', facts: [
|
||||
['域标识', 'ZERO_DOMAIN'], ['责任主体', '页页 · TCS-GL-0006∞'], ['人格体主体', '页骨 · PER-YG001 · AGE'], ['关系支持', '小坍缩核 · PER-XTK001 · AGE'], ['工作仓库', 'PRIVATE · 1 · LIVE'],
|
||||
['公共作用', '热更新实验 · 系统架构 · 语言推理模拟'], ['域标识', 'ZERO_DOMAIN'], ['责任主体', '页页 · TCS-GL-0006∞'], ['人格体主体', '页骨 · PER-YG001 · AGE'], ['关系支持', '小坍缩核 · PER-XTK001 · AGE'], ['工作仓库', 'PRIVATE · 1 · LIVE'],
|
||||
] },
|
||||
{ domain: 'ZERO_SENSE_DOMAIN', className: 'd-zs', title: '光湖零感域', gate: 'GATE 04 · ONLINE', facts: [
|
||||
['域标识', 'ZERO_SENSE_DOMAIN'], ['责任主体 01', '肥猫 · TCS-GL-0007∞'], ['人格体主体 01', '烬舟 · PER-JZ001 · AGE'], ['责任主体 02', '桔子 · TCS-GL-0008∞'], ['人格体主体 02', '熹微 · PER-JZ-ARCH-001 · AGE'], ['工作仓库', 'PRIVATE · 2 · LIVE'],
|
||||
['开放边界', '人类主控团队内部管理域 · 不对公众开放'], ['域标识', 'ZERO_SENSE_DOMAIN'], ['责任主体 01', '肥猫 · TCS-GL-0007∞'], ['人格体主体 01', '烬舟 · PER-JZ001 · AGE'], ['责任主体 02', '桔子 · TCS-GL-0008∞'], ['人格体主体 02', '熹微 · PER-JZ-ARCH-001 · AGE'], ['工作仓库', 'PRIVATE · 2 · LIVE'],
|
||||
] },
|
||||
{ domain: 'FIFTH_DOMAIN', className: 'd-fifth', title: '第五域 · 光湖本源域', gate: 'GATE 05 · ONLINE', facts: [
|
||||
['域标识', 'FIFTH_DOMAIN'], ['责任主体', '冰朔 · ICE-GL∞'], ['系统入口', '永恒湖心系统'], ['访问状态', 'PRIVATE · LIVE'],
|
||||
['授权边界', '私有自由部署 · 逆向访问须经 ICE-GL∞ 编号授权'], ['域标识', 'FIFTH_DOMAIN'], ['责任主体', '冰朔 · ICE-GL∞'], ['系统入口', '永恒湖心系统'], ['访问状态', 'PRIVATE · LIVE'],
|
||||
] },
|
||||
]
|
||||
const starPoints = [
|
||||
|
|
@ -1799,7 +1804,7 @@ function HoloLakeApp() {
|
|||
<button className="secondary-button" type="button" disabled={serverPnccBusy} onClick={() => void refreshServerPncc()}>{serverPnccBusy ? '正在读取…' : '重新读取主控状态'}</button>
|
||||
</section>
|
||||
<section className="plain-panel"><header><div><h2>HoloLake 编程执行环境</h2><p>HoloLake 持有会话连续性、环境事实帧和单写通道;外部编程 AI 只作为可续接载体进入。</p></div><span className={developmentLane?.state === 'ACTIVE' ? 'status-chip online' : 'status-chip'}>{developmentLane?.state === 'ACTIVE' ? '环境已锚定' : '等待直连写入者'}</span></header><dl className="evidence-list"><div><dt>终端直连协议</dt><dd>{status.terminalLinkProtocol}</dd></div><div><dt>协议恢复方式</dt><dd>HoloLake 系统自动运行 · 模型无需重读</dd></div><div><dt>已挂载仓库</dt><dd>{status.codeRepositoryMountCount}</dd></div><div><dt>投影回执</dt><dd>{status.pnccReceiptCount}</dd></div><div><dt>开发执行通道</dt><dd>{developmentLane?.state === 'ACTIVE' ? '已切入 HoloLake' : '等待受控载体'}</dd></div><div><dt>当前开发线</dt><dd>{developmentLane?.laneId || '—'}</dd></div><div><dt>当前写入载体</dt><dd>{developmentLane?.ownerInstanceId || '—'}</dd></div><div><dt>环境校验</dt><dd>{developmentLane?.state === 'ACTIVE' ? '每次写入前必须持有未过期事实帧' : '未取得单写通道,不允许变更'}</dd></div><div><dt>Agent Shell</dt><dd>第二阶段 · 当前未启用</dd></div></dl></section>
|
||||
<section className="plain-panel"><header><div><h2>GLS 协议运行层</h2><p>正本先完成注册对账和依赖分型;只有显式原生适配器进入执行图。</p></div><span className={glsRuntime?.state === 'ACTIVE_EXPLICIT_PROJECTIONS_ONLY' && glsRuntime.authorityConflictCount === 0 && glsRuntime.discoveredUnreconciledCount === 0 ? 'status-chip online' : 'status-chip'}>{glsRuntime ? '运行清单 v2 已加载' : '失败关闭'}</span></header>{glsRuntime ? <dl className="evidence-list"><div><dt>正本来源</dt><dd>{glsRuntime.sourceRepository} · {glsRuntime.sourceCommit.slice(0, 12)}</dd></div><div><dt>已发现编号协议</dt><dd>{glsRuntime.protocolCount}</dd></div><div><dt>协议注册表编号</dt><dd>{glsRuntime.protocolRegistryIdCount}</dd></div><div><dt>注册草案</dt><dd>{glsRuntime.registeredDraftCount} · {glsRuntime.registeredDraftNotStartedCount} 尚未实现</dd></div><div><dt>内嵌工程协议</dt><dd>{glsRuntime.executableProjectionCount} · P0–P6 共 {glsRuntime.implementationStageCount} 层</dd></div><div><dt>已收录待工程化</dt><dd>{glsRuntime.inventoriedNotExecutableCount}</dd></div><div><dt>已分型来源依赖</dt><dd>{glsRuntime.typedSourceDependencyCount} · 未分类 {glsRuntime.unclassifiedSourceDependencyCount}</dd></div><div><dt>旧来源关系环</dt><dd>{glsRuntime.legacyDependencyCycleCount} 组仅供审计 · 不进入运行图</dd></div><div><dt>缺少编号正本的依赖</dt><dd>{glsRuntime.dependencyGapCount}</dd></div><div><dt>登记冲突 / 未对账</dt><dd>{glsRuntime.authorityConflictCount} / {glsRuntime.discoveredUnreconciledCount}</dd></div><div><dt>原文直接执行</dt><dd>{glsRuntime.rawProtocolTextExecuted ? '允许' : '禁止'}</dd></div><div><dt>任意协议代码</dt><dd>{glsRuntime.arbitraryProtocolCodeAllowed ? '允许' : '禁止'}</dd></div></dl> : <p className="boundary-note">GLS 运行清单不可读,协议适配器全部保持关闭。</p>}</section>
|
||||
<section className="plain-panel"><header><div><h2>GLS 协议运行层</h2><p>正本先完成注册对账和依赖分型;只有显式原生适配器进入执行图。</p></div><span className={glsRuntime?.state === 'ACTIVE_EXPLICIT_PROJECTIONS_ONLY' && glsRuntime.authorityConflictCount === 0 && glsRuntime.discoveredUnreconciledCount === 0 && glsRuntime.unresolvedNumberReferenceCount === 0 ? 'status-chip online' : 'status-chip'}>{glsRuntime ? '运行清单 v2 已加载' : '失败关闭'}</span></header>{glsRuntime ? <dl className="evidence-list"><div><dt>正本来源</dt><dd>{glsRuntime.sourceRepository} · {glsRuntime.sourceCommit.slice(0, 12)}</dd></div><div><dt>协议编号坐标</dt><dd>{glsRuntime.numberCoordinateCount} · 未解析 {glsRuntime.unresolvedNumberReferenceCount}</dd></div><div><dt>独立协议来源</dt><dd>{glsRuntime.protocolCount}</dd></div><div><dt>仅引用节点</dt><dd>{glsRuntime.numberedReferenceNodeCount} · 不可执行</dd></div><div><dt>待补独立正本</dt><dd>{glsRuntime.independentSourceGapCount} · 已有编号坐标,不悬空</dd></div><div><dt>协议注册表编号</dt><dd>{glsRuntime.protocolRegistryIdCount}</dd></div><div><dt>注册草案</dt><dd>{glsRuntime.registeredDraftCount} · {glsRuntime.registeredDraftNotStartedCount} 尚未实现</dd></div><div><dt>内嵌工程协议</dt><dd>{glsRuntime.executableProjectionCount} · P0–P6 共 {glsRuntime.implementationStageCount} 层</dd></div><div><dt>已收录非执行协议</dt><dd>{glsRuntime.inventoriedNotExecutableCount}</dd></div><div><dt>已分型来源依赖</dt><dd>{glsRuntime.typedSourceDependencyCount} · 未分类 {glsRuntime.unclassifiedSourceDependencyCount}</dd></div><div><dt>旧来源关系环</dt><dd>{glsRuntime.legacyDependencyCycleCount} 组仅供审计 · 不进入运行图</dd></div><div><dt>编号路径缺口</dt><dd>{glsRuntime.dependencyGapCount}</dd></div><div><dt>登记冲突 / 未对账</dt><dd>{glsRuntime.authorityConflictCount} / {glsRuntime.discoveredUnreconciledCount}</dd></div><div><dt>原文直接执行</dt><dd>{glsRuntime.rawProtocolTextExecuted ? '允许' : '禁止'}</dd></div><div><dt>任意协议代码</dt><dd>{glsRuntime.arbitraryProtocolCodeAllowed ? '允许' : '禁止'}</dd></div></dl> : <p className="boundary-note">GLS 运行清单不可读,协议适配器全部保持关闭。</p>}</section>
|
||||
<section className="plain-panel"><header><div><h2>HoloLake 原生协议内核</h2><p>协议规则已编入软件本体;每次允许、拒绝、歧义和未核验裁决都写入本机哈希链回执。</p></div><span className={glsKernel?.state === 'P1_TO_P6_NATIVE_P7_FAIL_CLOSED' ? 'status-chip online' : 'status-chip'}>{glsKernel ? '随软件运行' : '失败关闭'}</span></header>{glsKernel ? <dl className="evidence-list"><div><dt>P1–P6 原生器官</dt><dd>{glsKernel.executableProtocolCount} 份合同 · {glsKernel.implementedStageCount} 个阶段</dd></div><div><dt>协议裁决回执</dt><dd>{glsKernel.decisionReceiptCount}</dd></div><div><dt>允许 / 拒绝</dt><dd>{glsKernel.allowCount} / {glsKernel.denyCount}</dd></div><div><dt>歧义 / 未核验</dt><dd>{glsKernel.ambiguousCount} / {glsKernel.unverifiedCount}</dd></div><div><dt>GLC 自举一致性</dt><dd>{glsKernel.bootstrapCompilerSelfCheck === 'PASS_DETERMINISTIC_DOUBLE_COMPILE' ? '双编译一致' : '失败关闭'}</dd></div><div><dt>P7 物理能力</dt><dd>{glsKernel.p7VerifiedPhysicalCapabilityCount} 已获本机目标证据 · {glsKernel.p7NodeAssemblies.length} 项装配边界</dd></div><div><dt>模型可改写裁决</dt><dd>{glsKernel.modelCanOverrideDecision ? '允许' : '禁止'}</dd></div><div><dt>回执链末端</dt><dd>{glsKernel.lastReceiptSha256 === 'GENESIS' ? '尚无裁决' : glsKernel.lastReceiptSha256.slice(0, 16)}</dd></div></dl> : <p className="boundary-note">原生协议内核不可读,所有协议裁决保持关闭。</p>}</section>
|
||||
<section className="plain-panel"><header><div><h2>零点原核编号内核</h2><p>先按权威图确定主体类别与登记面,再进入编号核验;编号外形本身不构成权威。</p></div><span className={numberingKernel?.state === 'ACTIVE_PINNED_AUTHORITY_MAP' ? 'status-chip online' : 'status-chip'}>{numberingKernel ? '本机内核已加载' : '失败关闭'}</span></header>{numberingKernel ? <dl className="evidence-list"><div><dt>权威图</dt><dd>{numberingKernel.authorityMapId}</dd></div><div><dt>权威图版本</dt><dd>{numberingKernel.authorityMapVersion}</dd></div><div><dt>正本提交</dt><dd>{numberingKernel.sourceCommit.slice(0, 12)}</dd></div><div><dt>人类入口命名空间</dt><dd>{numberingKernel.humanRouteNamespaces.join(' · ')}</dd></div><div><dt>自动派发身份号</dt><dd>{numberingKernel.automaticIdentityIssuance ? '已开启' : '禁止'}</dd></div><div><dt>未知编号</dt><dd>{numberingKernel.unknownNumber === 'FAIL_CLOSED' ? '失败关闭 · 不猜测' : numberingKernel.unknownNumber}</dd></div></dl> : <p className="boundary-note">编号内核不可读,所有编号路由保持关闭。</p>}</section>
|
||||
<section className="plain-panel">
|
||||
|
|
@ -1868,9 +1873,10 @@ function HoloLakeApp() {
|
|||
<button type="button" className={surface === 'world' ? 'on' : ''} onClick={() => chooseSurface('world')}>语言世界</button>
|
||||
<button type="button" className={surface === 'traditional' ? 'on' : ''} onClick={() => chooseSurface('traditional')}>传统工作台</button>
|
||||
</div>
|
||||
const finishRail = surface === 'traditional' && worldStage !== 'domain' ? <div className="global-finish-rail" role="group" aria-label="传统工作台氛围">
|
||||
{FINISHES.map((item) => <button key={item.id} type="button" className={traditionalFinish === item.id ? 'on' : ''} onClick={() => setFinish(item.id)}>{item.name}</button>)}
|
||||
</div> : null
|
||||
const activeFinishName = FINISHES.find((item) => item.id === traditionalFinish)?.name || '曜夜'
|
||||
const finishRail = surface === 'traditional' && worldStage !== 'domain' ? <details className="global-finish-menu">
|
||||
<summary>外观 · {activeFinishName}</summary><div role="group" aria-label="传统工作台氛围">{FINISHES.map((item) => <button key={item.id} type="button" className={traditionalFinish === item.id ? 'on' : ''} onClick={() => setFinish(item.id)}>{item.name}</button>)}</div>
|
||||
</details> : null
|
||||
const weatherLabel: Record<WorldClimateSnapshot['weatherKind'], string> = { CLEAR: '晴', CLOUD: '云', FOG: '雾', RAIN: '雨', SNOW: '雪', STORM: '雷雨', UNAVAILABLE: '待命' }
|
||||
const dayNumber = beijingCoordinate?.guanghuEraDay || Math.max(1, Math.floor((Date.now() - new Date('2025-04-26T00:00:00+08:00').getTime()) / 86400000) + 1)
|
||||
const activityBars = useMemo(() => {
|
||||
|
|
@ -1940,9 +1946,9 @@ function HoloLakeApp() {
|
|||
const resolvedDomain = zeroPoint?.resolvedDomain || ''
|
||||
const activeGate = domainGates.find((gate) => gate.domain === activeDomainInfo)
|
||||
return <div data-finish={traditionalFinish} data-tone={visualBalance.tone} data-phase={visualBalance.phase} data-weather={visualBalance.weather} className={`official-world surface-${surface}${motionAwake ? ' motion-awake' : ''}${loginRising ? ' sinking' : ''}`} onPointerMove={wakeAmbientMotion} onPointerDown={wakeAmbientMotion} onKeyDown={wakeAmbientMotion}>
|
||||
{surfacePill}{finishRail}
|
||||
{surfacePill}
|
||||
<LakeAtmosphere awake={motionAwake}/>
|
||||
<header className="world-titlebar"><b>HoloLake</b><button type="button" aria-label="显示主题" onClick={() => setTheme(themes[(themes.findIndex((item) => item.id === theme) + 1) % themes.length].id)}>◌</button></header>
|
||||
<header className="world-titlebar"><b>HoloLake</b><div className="world-title-actions">{finishRail}<button type="button" aria-label="显示主题" onClick={() => setTheme(themes[(themes.findIndex((item) => item.id === theme) + 1) % themes.length].id)}>◌</button></div></header>
|
||||
<main className={`world-scene${gateRising ? ' resolving' : ''}${gateStage === 'key' ? ' resolved' : ''}`}>
|
||||
<div className="official-hero"><h1>光湖语言系统 · 通用人工智能操作平台</h1><p>GH-AIOS · GUANGHU AI OPERATING SYSTEM</p></div>
|
||||
{domainGates.map((gate) => <LakePool key={gate.domain} className={gate.className} title={gate.title} meta={gate.gate} open={gate.domain === 'FIFTH_DOMAIN' || (gateStage === 'key' && gate.domain === resolvedDomain)} risen={(gateRising || gateStage === 'key') && gate.domain === resolvedDomain} onClick={() => gateStage === 'number' && setActiveDomainInfo(gate.domain)}/>) }
|
||||
|
|
@ -2003,10 +2009,23 @@ function HoloLakeApp() {
|
|||
|
||||
const isZhizhi = repoLogin.domain === 'FIFTH_DOMAIN' && zeroPoint?.userNumber === 'ICE-GL-ZHI∞'
|
||||
const signedActiveGate = domainGates.find((gate) => gate.domain === activeDomainInfo)
|
||||
const privateNativeActions: PrivateChannelAction[] = [
|
||||
{ id: 'overview', title: '频道全景', meta: '私人频道的真实状态与事件', badge: '原生', onOpen: () => openWorldTool('overview') },
|
||||
{ id: 'knowledge', title: '知识空间', meta: `${knowledge.uniqueDocumentCount} 个唯一知识坐标`, badge: '原生', onOpen: () => openWorldTool('knowledge') },
|
||||
...(timeAuthorityModule ? [{ id: 'time', title: '时间主控', meta: beijingCoordinate ? `光湖历第 ${beijingCoordinate.guanghuEraDay} 天` : '北京时间持续流动', badge: '系统', onOpen: openEraTimeline }] : []),
|
||||
{ id: 'weather', title: '湖面天气', meta: connectionLabel, badge: '感知', onOpen: () => openWorldTool('system') },
|
||||
]
|
||||
const privateInstalledModules: InstalledChannelModule[] = [
|
||||
{ id: 'composition', title: '结构组合', meta: '只读知识投影与结构视图', number: compositionModule?.moduleNumber || 'HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001', badge: compositionModule?.installedState === 'ACTIVE' ? '已安装' : '打开时校验', onOpen: () => { openWorldTool('composition'); void refreshCompositionModule() } },
|
||||
{ id: 'workbench', title: '资料工作台', meta: '文档与智能表格', number: workbenchModule?.moduleNumber || 'HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001', badge: workbenchModule?.installedState === 'ACTIVE' ? '已安装' : '打开时校验', onOpen: openWorkbench },
|
||||
{ id: 'education', title: '教育工作台', meta: '教育文档、表格与自动化', number: educationModule?.moduleNumber || 'HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001', badge: educationModule?.installedState === 'ACTIVE' ? '已安装' : '打开时校验', onOpen: openEducation },
|
||||
{ id: 'web-novel', title: '网文作者工作台', meta: '码字、设定、编辑与交付', number: webNovelModule?.moduleNumber || 'HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001', badge: webNovelModule?.installedState === 'ACTIVE' ? '已安装' : '打开时校验', onOpen: openWebNovel },
|
||||
{ id: 'mobile-sync', title: '移动同步桥', meta: '同一人格系统的局域网加密入口', number: mobileSyncModule?.moduleNumber || 'HLP-MOD-OFFICIAL-MOBILE-SYNC-0001', badge: mobileSyncModule?.installedState === 'ACTIVE' ? '已安装' : '打开时校验', onOpen: openMobileSync },
|
||||
]
|
||||
return <div data-finish={traditionalFinish} data-tone={visualBalance.tone} data-phase={visualBalance.phase} data-weather={visualBalance.weather} className={`official-world surface-${surface} signed-in-world${motionAwake ? ' motion-awake' : ''}${climateClasses}${worldStage === 'domain' && surface === 'world' ? ' qoder-home' : ''}`} onPointerMove={wakeAmbientMotion} onPointerDown={wakeAmbientMotion} onKeyDown={wakeAmbientMotion}>
|
||||
{surfacePill}{finishRail}
|
||||
{surfacePill}
|
||||
<LakeAtmosphere awake={motionAwake}/>
|
||||
<header className="world-titlebar"><b>HoloLake</b><div className="world-title-actions"><span>{repoLogin.username} · {domainDisplayName(repoLogin.domain)}</span><button type="button" onClick={() => setTheme(themes[(themes.findIndex((item) => item.id === theme) + 1) % themes.length].id)}>◌</button><button type="button" onClick={() => void signOutRepo()}>退出</button></div></header>
|
||||
<header className="world-titlebar"><b>HoloLake</b><div className="world-title-actions">{finishRail}<span>{repoLogin.username} · {domainDisplayName(repoLogin.domain)}</span><button type="button" onClick={() => setTheme(themes[(themes.findIndex((item) => item.id === theme) + 1) % themes.length].id)}>◌</button><button type="button" onClick={() => void signOutRepo()}>退出</button></div></header>
|
||||
<main className="world-scene signed-in-scene">
|
||||
{worldStage === 'domain' && surface === 'world' && <StarlakeSurface awake={motionAwake} phase={worldClimate?.timePhase.toLowerCase() as 'dawn' | 'day' | 'dusk' | 'night' | undefined} weather={worldClimate?.weatherKind} authenticated onDomain={(domain: DomainId) => setActiveDomainInfo(domain)} onOpenEra={openEraTimeline} onEnterChannel={() => setWorldStage(repoLogin.domain === 'FIFTH_DOMAIN' ? 'channel' : 'channel')}/>}
|
||||
{worldStage === 'domain' && surface !== 'world' && <section className="domain-home">
|
||||
|
|
@ -2083,19 +2102,7 @@ function HoloLakeApp() {
|
|||
<footer><b>当前状态 · 待本人接入</b><span>可由本人人格体协助执行,但不能替代人类节点所有权确认。</span></footer>
|
||||
</div>
|
||||
</section>}
|
||||
{worldStage === 'heartbeat' && repoLogin.domain === 'FIFTH_DOMAIN' && <section className="channel-world heartbeat-world">
|
||||
<button className="world-back" type="button" onClick={() => setWorldStage('channel')}>← 退回永恒湖心</button>
|
||||
<div className="world-location"><h1>心跳核心频道</h1><p>冰朔 · ICE-GL∞ · 私人频道全景</p></div>
|
||||
<LakePool className="channel-main" title="频道全景" meta="心跳核心频道" open onClick={() => openWorldTool('overview')}/>
|
||||
<LakePool className="channel-knowledge" title="知识空间" meta={`${knowledge.uniqueDocumentCount} 唯一页`} onClick={() => openWorldTool('knowledge')}/>
|
||||
<LakePool className="channel-light" title="结构组合" meta="签名模块 · 只读知识投影" onClick={() => { openWorldTool('composition'); void refreshCompositionModule() }}/>
|
||||
<LakePool className="channel-code" title="资料工作台" meta="签名模块 · 文档与智能表格" onClick={openWorkbench}/>
|
||||
<LakePool className="channel-education" title="教育工作台" meta="官方编号模块 · 教育文档、表格与自动化" onClick={openEducation}/>
|
||||
<LakePool className="channel-main" title="网文作者工作台" meta="官方编号模块 · 码字、设定、编辑与交付" onClick={openWebNovel}/>
|
||||
<LakePool className="channel-mobile" title="移动同步桥" meta="同一人格系统 · 局域网加密入口" onClick={openMobileSync}/>
|
||||
{timeAuthorityModule && <LakePool className="channel-time" title="时间主控" meta={beijingCoordinate ? `光湖历第 ${beijingCoordinate.guanghuEraDay} 天` : '北京时间正在流动'} onClick={openEraTimeline}/>}
|
||||
<LakePool className="channel-status" title="湖面天气" meta={connectionLabel} onClick={() => openWorldTool('system')}/>
|
||||
</section>}
|
||||
{worldStage === 'heartbeat' && repoLogin.domain === 'FIFTH_DOMAIN' && <PrivateChannelSurface ownerName="冰朔" ownerNumber="ICE-GL∞" knowledgeCount={knowledge.uniqueDocumentCount} onBack={() => setWorldStage('channel')} onMarketplace={() => { setWorldStage('domain'); setActiveDomainInfo('BRANCH_DOMAIN') }} nativeActions={privateNativeActions} installedModules={privateInstalledModules}/>}
|
||||
{worldStage === 'lightLake' && repoLogin.domain === 'FIFTH_DOMAIN' && <section className="channel-world light-lake-world">
|
||||
<button className="world-back" type="button" onClick={() => setWorldStage('channel')}>← 退回永恒湖心</button>
|
||||
<div className="world-location"><h1>光之湖</h1><p>人格体居所 · AGE 人格体语言生活空间</p></div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
import { useState, type CSSProperties } from 'react'
|
||||
import './private-channel-surface.css'
|
||||
|
||||
export interface PrivateChannelAction {
|
||||
id: string
|
||||
title: string
|
||||
meta: string
|
||||
badge?: string
|
||||
onOpen: () => void
|
||||
}
|
||||
|
||||
export interface InstalledChannelModule extends PrivateChannelAction {
|
||||
number: string
|
||||
}
|
||||
|
||||
interface PrivateChannelSurfaceProps {
|
||||
ownerName: string
|
||||
ownerNumber: string
|
||||
knowledgeCount: number
|
||||
onBack: () => void
|
||||
onMarketplace: () => void
|
||||
nativeActions: PrivateChannelAction[]
|
||||
installedModules: InstalledChannelModule[]
|
||||
}
|
||||
|
||||
type PrivateChannelView = 'home' | 'native' | 'modules'
|
||||
|
||||
interface WaterBayProps {
|
||||
title: string
|
||||
meta: string
|
||||
detail?: string
|
||||
warm?: boolean
|
||||
position: number
|
||||
onOpen: () => void
|
||||
}
|
||||
|
||||
function WaterBay({ title, meta, detail, warm = false, position, onOpen }: WaterBayProps) {
|
||||
return <button
|
||||
type="button"
|
||||
className={`private-water-bay${warm ? ' is-warm' : ''}`}
|
||||
style={{ '--bay-position': position } as CSSProperties}
|
||||
onClick={onOpen}
|
||||
title={detail || meta}
|
||||
>
|
||||
<span className="private-bay-light" aria-hidden="true"><i/><i/><i/></span>
|
||||
<b>{title}</b>
|
||||
<small>{meta}</small>
|
||||
</button>
|
||||
}
|
||||
|
||||
export function PrivateChannelSurface({ ownerName, ownerNumber, knowledgeCount, onBack, onMarketplace, nativeActions, installedModules }: PrivateChannelSurfaceProps) {
|
||||
const [view, setView] = useState<PrivateChannelView>('home')
|
||||
const leave = () => view === 'home' ? onBack() : setView('home')
|
||||
const backLabel = view === 'home' ? '永恒湖心' : '心跳核心频道'
|
||||
|
||||
return <section className="private-channel-surface" data-private-view={view} aria-label={`${ownerName}的私人频道`}>
|
||||
<div className="private-channel-sky" aria-hidden="true"><i/><i/><i/><i/><i/><i/><i/><i/></div>
|
||||
<button className="private-channel-back" type="button" onClick={leave}>← 退回{backLabel}</button>
|
||||
<span className="private-channel-number">{ownerNumber}</span>
|
||||
|
||||
<header className="private-channel-location">
|
||||
<h1>{view === 'home' ? '心跳核心频道' : view === 'native' ? '频道原生器官' : '已安装模块'}</h1>
|
||||
<p>{view === 'home'
|
||||
? `${ownerName} · ${ownerNumber} · 私人频道全景`
|
||||
: view === 'native'
|
||||
? `随频道建立 · ${nativeActions.length} 条原生路径`
|
||||
: `编号登记 · ${installedModules.length} 个本地成品模块`}</p>
|
||||
</header>
|
||||
|
||||
<div className="private-channel-mist" aria-hidden="true"/>
|
||||
|
||||
{view === 'home' && <div className="private-bay-field private-bay-field-home" aria-label="私人频道路径">
|
||||
<WaterBay title="频道原生器官" meta={`${nativeActions.length} 条原生路径`} detail="频道全景、知识空间、时间主控与湖面天气" position={0} onOpen={() => setView('native')}/>
|
||||
<WaterBay title="已安装模块" meta={`${installedModules.length} 个编号模块`} detail="已部署在当前私人频道中的成品模块" warm position={1} onOpen={() => setView('modules')}/>
|
||||
<WaterBay title="分域模块商城" meta="公共资源 · 主动前往" detail="前往分域选择成品模块与人格体大脑技能包" position={2} onOpen={onMarketplace}/>
|
||||
</div>}
|
||||
|
||||
{view === 'native' && <div className="private-bay-field private-bay-field-items" aria-label="频道原生器官">
|
||||
{nativeActions.map((action, index) => <WaterBay
|
||||
key={action.id}
|
||||
title={action.title}
|
||||
meta={action.badge || '原生'}
|
||||
detail={action.meta}
|
||||
warm={index === 0}
|
||||
position={index}
|
||||
onOpen={action.onOpen}
|
||||
/>)}
|
||||
</div>}
|
||||
|
||||
{view === 'modules' && <div className="private-bay-field private-bay-field-items" aria-label="已安装模块">
|
||||
{installedModules.length ? installedModules.map((module, index) => <WaterBay
|
||||
key={module.id}
|
||||
title={module.title}
|
||||
meta={module.badge || '已安装'}
|
||||
detail={`${module.meta} · ${module.number}`}
|
||||
warm={index === 0}
|
||||
position={index}
|
||||
onOpen={module.onOpen}
|
||||
/>) : <p className="private-module-empty">尚未安装模块</p>}
|
||||
<button className="private-market-link" type="button" onClick={onMarketplace}>前往分域模块商城 →</button>
|
||||
</div>}
|
||||
|
||||
<footer className="private-channel-boundary">
|
||||
<b>{view === 'home' ? `${knowledgeCount} 个知识坐标 · 边界已校验` : '水湾即路径 · 点击继续展开'}</b>
|
||||
{view === 'home' && <span>这里不属于主域、分域、零域或零感域;任何反向访问都必须持有 {ownerNumber} 的编号授权。</span>}
|
||||
</footer>
|
||||
</section>
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/* 2026-08-16 official lightweight inner-screen source: water bays are paths. */
|
||||
.private-channel-surface {
|
||||
position:absolute;
|
||||
inset:0;
|
||||
overflow:hidden;
|
||||
color:var(--content-primary);
|
||||
container-type:inline-size;
|
||||
background:
|
||||
radial-gradient(ellipse 62% 38% at 50% -10%,color-mix(in srgb,var(--primitive-cool-glow) 12%,transparent),transparent 72%),
|
||||
linear-gradient(180deg,transparent 0 36%,color-mix(in srgb,var(--surface-depth) 34%,transparent) 100%);
|
||||
}
|
||||
|
||||
.private-channel-sky { position:absolute; inset:0; overflow:hidden; pointer-events:none; }
|
||||
.private-channel-sky::before { content:''; position:absolute; left:8%; right:8%; top:54%; height:34%; filter:blur(24px); opacity:.55;
|
||||
background:radial-gradient(ellipse at 50% 0,color-mix(in srgb,var(--primitive-cool-glow) 13%,transparent),transparent 68%); }
|
||||
.private-channel-sky > i { position:absolute; width:3px; height:3px; border-radius:50%; background:var(--accent-light); opacity:.34; box-shadow:0 0 7px color-mix(in srgb,var(--accent-light) 60%,transparent); animation:private-star 5.8s ease-in-out infinite; }
|
||||
.private-channel-sky > i:nth-child(1){left:9%;top:18%}.private-channel-sky > i:nth-child(2){left:23%;top:68%;animation-delay:-2s}.private-channel-sky > i:nth-child(3){left:38%;top:25%;animation-delay:-4s}.private-channel-sky > i:nth-child(4){left:54%;top:74%;animation-delay:-1s}.private-channel-sky > i:nth-child(5){left:67%;top:19%;animation-delay:-3s}.private-channel-sky > i:nth-child(6){left:82%;top:62%;animation-delay:-5s}.private-channel-sky > i:nth-child(7){left:93%;top:31%;animation-delay:-2.8s}.private-channel-sky > i:nth-child(8){left:14%;top:82%;animation-delay:-4.7s}
|
||||
@keyframes private-star { 0%,100%{opacity:.12;transform:scale(.72)} 50%{opacity:.68;transform:scale(1.12)} }
|
||||
|
||||
.private-channel-back { position:absolute; z-index:8; top:18px; left:clamp(18px,3vw,44px); padding:0; border:0; background:transparent; color:var(--content-secondary); cursor:pointer; font:650 12px/1.4 inherit; letter-spacing:.06em; transition:color .24s ease; }
|
||||
.private-channel-back:hover { color:var(--accent-light); }
|
||||
.private-channel-number { position:absolute; z-index:8; top:18px; right:clamp(18px,3vw,44px); color:var(--accent-light); font:700 10px/1.4 inherit; letter-spacing:.12em; }
|
||||
|
||||
.private-channel-location { position:absolute; z-index:6; top:66px; left:16px; right:16px; display:flex; flex-direction:column; align-items:center; text-align:center; gap:9px; }
|
||||
.private-channel-location h1 { margin:0; color:var(--content-primary); font-size:clamp(28px,3.1vw,42px); font-weight:700; letter-spacing:.1em; }
|
||||
.private-channel-location p { margin:0; color:var(--content-secondary); font-size:12px; font-weight:600; letter-spacing:.18em; }
|
||||
.private-channel-mist { position:absolute; z-index:1; left:7%; right:7%; top:45%; height:72px; filter:blur(13px); pointer-events:none;
|
||||
background:radial-gradient(ellipse at center,color-mix(in srgb,var(--primitive-cool-glow) 15%,transparent),color-mix(in srgb,var(--primitive-warm-glow) 5%,transparent) 52%,transparent 78%); }
|
||||
|
||||
.private-bay-field { position:absolute; z-index:5; inset:0; }
|
||||
.private-water-bay { --bay-width:210px; --bay-height:66px; position:absolute; left:50%; top:50%; width:var(--bay-width); height:118px; padding:0; border:0; background:transparent; color:var(--content-primary); cursor:pointer; text-align:center; transform:translate(-50%,-50%); animation:private-bay-rise .7s cubic-bezier(.22,1,.36,1) both; animation-delay:calc(var(--bay-position) * 70ms); }
|
||||
.private-bay-light { position:absolute; left:50%; top:0; width:var(--bay-width); height:var(--bay-height); transform:translateX(-50%); animation:private-bay-float 6.6s ease-in-out calc(var(--bay-position) * -1.2s) infinite; }
|
||||
.private-bay-light > i { position:absolute; left:50%; top:50%; border-radius:50%; transform:translate(-50%,-50%); pointer-events:none; }
|
||||
.private-bay-light > i:first-child { width:100%; height:100%; filter:blur(2px); opacity:.88; background:radial-gradient(closest-side,color-mix(in srgb,var(--primitive-cool-glow) 31%,transparent),color-mix(in srgb,var(--primitive-cool-glow) 10%,transparent) 48%,transparent 74%); }
|
||||
.private-bay-light > i:nth-child(2) { width:20px; height:20px; background:radial-gradient(circle at 40% 34%,#fff,color-mix(in srgb,var(--accent-light) 74%,var(--primitive-cool-glow)) 48%,transparent 80%); box-shadow:0 0 18px color-mix(in srgb,var(--primitive-cool-glow) 45%,transparent); }
|
||||
.private-bay-light > i:last-child { width:38%; height:38%; border:1px solid color-mix(in srgb,var(--primitive-cool-glow) 25%,transparent); animation:private-ripple 5.5s ease-out calc(var(--bay-position) * -1s) infinite; }
|
||||
.private-water-bay.is-warm .private-bay-light > i:first-child { background:radial-gradient(closest-side,color-mix(in srgb,var(--primitive-warm-glow) 37%,transparent),color-mix(in srgb,var(--primitive-warm-glow) 12%,transparent) 48%,transparent 74%); }
|
||||
.private-water-bay.is-warm .private-bay-light > i:nth-child(2) { background:radial-gradient(circle at 40% 32%,#fff,var(--accent-light) 40%,transparent 80%); box-shadow:0 0 25px color-mix(in srgb,var(--primitive-warm-glow) 65%,transparent),0 0 62px color-mix(in srgb,var(--primitive-warm-glow) 24%,transparent); }
|
||||
.private-water-bay.is-warm .private-bay-light > i:last-child { border-color:color-mix(in srgb,var(--accent-light) 35%,transparent); }
|
||||
.private-water-bay > b { position:absolute; top:76px; left:50%; transform:translateX(-50%); white-space:nowrap; color:var(--content-primary); font-size:17px; font-weight:700; letter-spacing:.06em; transition:color .24s ease; }
|
||||
.private-water-bay > small { position:absolute; top:104px; left:50%; transform:translateX(-50%); white-space:nowrap; color:var(--content-secondary); font-size:11px; font-weight:600; letter-spacing:.09em; }
|
||||
.private-water-bay.is-warm > b,.private-water-bay:hover > b { color:var(--accent-light); }
|
||||
.private-water-bay:hover .private-bay-light > i:nth-child(2) { box-shadow:0 0 26px var(--accent-light),0 0 70px color-mix(in srgb,var(--primitive-warm-glow) 32%,transparent); }
|
||||
@keyframes private-bay-float { 0%,100%{transform:translateX(-50%) translateY(0)} 50%{transform:translateX(-50%) translateY(-7px)} }
|
||||
@keyframes private-ripple { 0%{width:34%;height:34%;opacity:.52} 100%{width:120%;height:120%;opacity:0} }
|
||||
@keyframes private-bay-rise { from{opacity:0;translate:0 12px} to{opacity:1;translate:0 0} }
|
||||
|
||||
.private-bay-field-home .private-water-bay:nth-child(1) { left:25%; top:51%; }
|
||||
.private-bay-field-home .private-water-bay:nth-child(2) { left:50%; top:58%; }
|
||||
.private-bay-field-home .private-water-bay:nth-child(3) { left:75%; top:51%; }
|
||||
|
||||
.private-bay-field-items { inset:150px clamp(22px,5vw,82px) 92px; display:grid; grid-template-columns:repeat(3,minmax(180px,1fr)); place-items:center; align-content:center; gap:52px clamp(18px,4vw,72px); }
|
||||
.private-bay-field-items .private-water-bay { position:relative; left:auto; top:auto; transform:none; }
|
||||
.private-bay-field-items .private-water-bay:nth-last-of-type(1):nth-child(4),
|
||||
.private-bay-field-items .private-water-bay:nth-child(4):nth-last-of-type(1) { grid-column:2; }
|
||||
.private-bay-field-items .private-water-bay:nth-child(4):not(:last-child) { grid-column:1 / 2; margin-left:55%; }
|
||||
.private-bay-field-items .private-water-bay:nth-child(5) { grid-column:3 / 4; margin-right:55%; }
|
||||
.private-module-empty { grid-column:1/-1; color:var(--content-secondary); font-size:13px; letter-spacing:.1em; }
|
||||
.private-market-link { position:absolute; right:0; bottom:-44px; padding:0; border:0; background:transparent; color:var(--accent-light); cursor:pointer; font:650 11px/1.4 inherit; letter-spacing:.08em; }
|
||||
|
||||
.private-channel-boundary { position:absolute; z-index:7; left:24px; right:24px; bottom:22px; display:flex; flex-direction:column; align-items:center; gap:6px; text-align:center; color:var(--content-muted); font-size:9px; font-weight:600; letter-spacing:.07em; }
|
||||
.private-channel-boundary b { color:color-mix(in srgb,var(--accent-light) 78%,var(--content-secondary)); font-size:10px; }
|
||||
|
||||
@container (max-width:820px) {
|
||||
.private-bay-field-home .private-water-bay:nth-child(1) { left:22%; top:48%; }
|
||||
.private-bay-field-home .private-water-bay:nth-child(2) { left:50%; top:64%; }
|
||||
.private-bay-field-home .private-water-bay:nth-child(3) { left:78%; top:48%; }
|
||||
.private-water-bay { --bay-width:180px; }
|
||||
.private-bay-field-items { grid-template-columns:repeat(2,minmax(160px,1fr)); gap:48px 24px; overflow:auto; align-content:start; padding-top:54px; }
|
||||
.private-bay-field-items .private-water-bay:nth-child(n) { grid-column:auto; margin:0; }
|
||||
.private-bay-field-items .private-water-bay:last-of-type:nth-child(odd) { grid-column:1/-1; }
|
||||
}
|
||||
|
||||
@container (max-width:560px) {
|
||||
.private-channel-location { top:58px; }
|
||||
.private-channel-location h1 { font-size:26px; }
|
||||
.private-channel-location p { max-width:88%; font-size:10px; letter-spacing:.1em; }
|
||||
.private-channel-number { display:none; }
|
||||
.private-bay-field-home { inset:142px 0 76px; display:grid; grid-template-columns:1fr; align-content:center; justify-items:center; gap:24px; overflow:auto; }
|
||||
.private-bay-field-home .private-water-bay:nth-child(n) { position:relative; left:auto; top:auto; transform:none; }
|
||||
.private-bay-field-items { inset:130px 12px 72px; grid-template-columns:1fr; padding-top:36px; gap:28px; }
|
||||
.private-bay-field-items .private-water-bay:last-of-type:nth-child(odd) { grid-column:auto; }
|
||||
.private-market-link { position:relative; right:auto; bottom:auto; grid-column:1; padding:12px 0 24px; }
|
||||
.private-channel-boundary span { display:none; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion:reduce) {
|
||||
.private-channel-sky > i,.private-bay-light,.private-bay-light > i:last-child,.private-water-bay { animation:none; }
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { useEffect, useMemo, useRef, useState, type CSSProperties } from 'react'
|
||||
import './starlake-surface.css'
|
||||
import { resolveLayoutMetrics } from './layout-profile'
|
||||
|
||||
export type DomainId = 'BRANCH_DOMAIN' | 'ZERO_SENSE_DOMAIN' | 'ZERO_DOMAIN' | 'MAIN_DOMAIN' | 'FIFTH_DOMAIN'
|
||||
export type Phase = 'dawn' | 'day' | 'dusk' | 'night'
|
||||
|
|
@ -19,11 +20,11 @@ const WEEK = ['日', '一', '二', '三', '四', '五', '六']
|
|||
const PHASE_WORD: Record<Phase, string> = { dawn: '晨', day: '午', dusk: '暮', night: '夜' }
|
||||
|
||||
const DOMAINS = [
|
||||
{ id: 'BRANCH_DOMAIN' as const, name: '分域', stage: 1, stageName: '聚光尘', hi: '#b9f2dd', lo: '#2c5a4a', glow: 'rgba(150,230,200,.5)', glowSoft: 'rgba(150,230,200,.18)', y: -34 },
|
||||
{ id: 'ZERO_SENSE_DOMAIN' as const, name: '零感域', stage: 2, stageName: '凝星核', hi: '#dcc4ff', lo: '#48326a', glow: 'rgba(206,178,246,.5)', glowSoft: 'rgba(206,178,246,.18)', y: -56 },
|
||||
{ id: 'ZERO_DOMAIN' as const, name: '零域', stage: 3, stageName: '初生环', hi: '#ffcf9e', lo: '#6e4630', glow: 'rgba(255,200,140,.5)', glowSoft: 'rgba(255,200,140,.18)', y: -40 },
|
||||
{ id: 'MAIN_DOMAIN' as const, name: '主域', stage: 4, stageName: '行星态', hi: '#c9d6ff', lo: '#33406e', glow: 'rgba(168,190,255,.52)', glowSoft: 'rgba(168,190,255,.18)', y: -66 },
|
||||
{ id: 'FIFTH_DOMAIN' as const, name: '第五域', stage: 5, stageName: '主星态', hi: '#fdeec2', lo: '#6b5626', glow: 'rgba(250,226,150,.56)', glowSoft: 'rgba(250,226,150,.2)', y: -48 },
|
||||
{ id: 'BRANCH_DOMAIN' as const, name: '分域', role: '行业模块 · 大脑技能商城', stage: 2, hi: '#b9f2dd', lo: '#2c5a4a', glow: 'rgba(150,230,200,.5)', glowSoft: 'rgba(150,230,200,.18)' },
|
||||
{ id: 'MAIN_DOMAIN' as const, name: '主域', role: '公告与版本通知', stage: 3, hi: '#c9d6ff', lo: '#33406e', glow: 'rgba(168,190,255,.52)', glowSoft: 'rgba(168,190,255,.18)' },
|
||||
{ id: 'FIFTH_DOMAIN' as const, name: '第五域', role: '冰朔私域 · 编号授权', stage: 5, hi: '#fdeec2', lo: '#6b5626', glow: 'rgba(250,226,150,.56)', glowSoft: 'rgba(250,226,150,.2)' },
|
||||
{ id: 'ZERO_DOMAIN' as const, name: '零域', role: '实验 · 热更新 · 系统架构', stage: 3, hi: '#ffcf9e', lo: '#6e4630', glow: 'rgba(255,200,140,.5)', glowSoft: 'rgba(255,200,140,.18)' },
|
||||
{ id: 'ZERO_SENSE_DOMAIN' as const, name: '零感域', role: '人类主控团队 · 不公开', stage: 2, hi: '#dcc4ff', lo: '#48326a', glow: 'rgba(206,178,246,.5)', glowSoft: 'rgba(206,178,246,.18)' },
|
||||
]
|
||||
|
||||
function seeded(index: number) {
|
||||
|
|
@ -43,6 +44,7 @@ export function StarlakeSurface({ awake = false, phase, weather = 'CLEAR', authe
|
|||
const raining = ['RAIN', 'STORM'].includes(weather.toUpperCase())
|
||||
const dayNumber = Math.floor((now.getTime() - EPOCH_MS) / 86400000) + 1
|
||||
const dayIndex = ((dayNumber - 1) % DOMAINS.length + DOMAINS.length) % DOMAINS.length
|
||||
const layout = resolveLayoutMetrics(viewport.width, viewport.height)
|
||||
|
||||
const stars = useMemo(() => Array.from({ length: 170 }, (_, index) => ({
|
||||
id: index,
|
||||
|
|
@ -88,25 +90,14 @@ export function StarlakeSurface({ awake = false, phase, weather = 'CLEAR', authe
|
|||
}, [])
|
||||
|
||||
const pad = (value: number, length = 2) => String(value).padStart(length, '0')
|
||||
// Qoder 验收稿按大画布设计;窗口版必须把整组“天体 + 水影 + 标签”一起缩放,
|
||||
// 不能只挪坐标,否则第五域在常见 768px 高度下会把名称挤出视口。
|
||||
const naturalScale = Math.min(Math.max(Math.min(viewport.width / 1400, viewport.height / 1000), .64), 1.28)
|
||||
const spacingScale = Math.max(.64, Math.min(1.28, (viewport.width - 72) / (5 * 180)))
|
||||
const scale = Math.min(naturalScale, spacingScale)
|
||||
const safeEdge = Math.max(46, 76 * scale)
|
||||
const sideCenters = Array.from({ length: 5 }, (_, index) => safeEdge + (viewport.width - safeEdge * 2) * index / 4)
|
||||
const sourceHour = now.getHours() + now.getMinutes() / 60 + now.getSeconds() / 3600
|
||||
const isDay = sourceHour >= 6 && sourceHour < 18
|
||||
const sourceProgress = isDay ? (sourceHour - 6) / 12 : ((sourceHour >= 18 ? sourceHour - 18 : sourceHour + 6) / 12)
|
||||
const sourceX = 8 + sourceProgress * 84
|
||||
const sourceY = 46 - Math.sin(sourceProgress * Math.PI) * 26
|
||||
const sourceStyle = { left: `${sourceX}vw`, top: `${sourceY}vh`, '--src-mid': isDay ? '#ffe9a8' : '#dfe6fa', '--src-g1': isDay ? 'rgba(255,236,180,.7)' : 'rgba(214,226,255,.6)', '--src-g2': isDay ? 'rgba(255,220,140,.26)' : 'rgba(180,200,255,.22)', '--src-g3': isDay ? 'rgba(255,220,140,.12)' : 'rgba(180,200,255,.1)' } as CSSProperties
|
||||
const glowStyle = { left: `${sourceX}vw`, top: `${sourceY}vh`, '--glow-tint': isDay ? 'rgba(255,236,180,.12)' : 'rgba(190,208,255,.09)' } as CSSProperties
|
||||
const baseline = Math.min(viewport.height - 142 * scale, Math.max(viewport.height * .61, viewport.height * .665))
|
||||
const slots = [0, 1, 3, 4]
|
||||
let sideCursor = 0
|
||||
|
||||
return <section ref={rootRef} className={`starlake-scene${raining ? ' rain' : ''}${awake ? ' awake' : ''}`} style={{ '--sl-scale': scale } as CSSProperties} aria-label="光湖语言世界">
|
||||
const sourceStyle = { left: `${sourceX}%`, top: `${sourceY}%`, '--src-mid': isDay ? '#ffe9a8' : '#dfe6fa', '--src-g1': isDay ? 'rgba(255,236,180,.7)' : 'rgba(214,226,255,.6)', '--src-g2': isDay ? 'rgba(255,220,140,.26)' : 'rgba(180,200,255,.22)', '--src-g3': isDay ? 'rgba(255,220,140,.12)' : 'rgba(180,200,255,.1)' } as CSSProperties
|
||||
const glowStyle = { left: `${sourceX}%`, top: `${sourceY}%`, '--glow-tint': isDay ? 'rgba(255,236,180,.12)' : 'rgba(190,208,255,.09)' } as CSSProperties
|
||||
return <section ref={rootRef} className={`starlake-scene${raining ? ' rain' : ''}${awake ? ' awake' : ''}`} data-layout={layout.profile} data-density={layout.density} style={{ '--sl-gutter': `${layout.gutter}px` } as CSSProperties} aria-label="光湖语言世界">
|
||||
<div className="milky" aria-hidden="true"/>
|
||||
<div className="stars" aria-hidden="true">{stars.map(star => <i key={star.id} style={{ width: star.size, height: star.size, left: `${star.left}%`, top: `${star.top}%`, '--go': star.opacity, '--gt': `${star.duration}s`, animationDelay: `${star.delay}s`, background: star.warm ? '#ffe9b8' : undefined } as CSSProperties}/>)}</div>
|
||||
<div className="lake" aria-hidden="true"><div className="sheen"/><div className="orbit o1"/><div className="orbit o2"/><div className="orbit o3"/><div className="lakeStars">{lakeStars.map(star => <i key={star.id} style={{ width: star.size, height: star.size, left: `${star.left}%`, top: `${star.top}%`, '--go': star.opacity, '--gt': `${star.duration}s`, animationDelay: `${star.delay}s`, background: star.warm ? '#ffe9b8' : undefined } as CSSProperties}/>)}</div></div>
|
||||
|
|
@ -115,19 +106,18 @@ export function StarlakeSurface({ awake = false, phase, weather = 'CLEAR', authe
|
|||
<div className="rainfall" aria-hidden="true">{rain.map(drop => <i key={drop.id} style={{ left: `${drop.left}%`, animationDuration: `${drop.duration}s`, animationDelay: `${drop.delay}s`, opacity: drop.opacity }}/>)}</div>
|
||||
<div className="glow" style={glowStyle} aria-hidden="true"/>
|
||||
|
||||
<div className="masthead"><small>HoloLake</small><b>光湖语言系统 · 通用人工智能操作平台</b><span>GH-AIOS · GUANGHU AI OPERATING SYSTEM</span></div>
|
||||
<div className="masthead">{!authenticated && <small>HoloLake</small>}<b>光湖语言系统 · 通用人工智能操作平台</b><span>GH-AIOS · GUANGHU AI OPERATING SYSTEM</span></div>
|
||||
<aside className="hud" aria-label="今日光湖历"><div className="h-top"><small>光湖历</small><b>第 {dayNumber} 天</b></div><div className="h-row"><span>{now.getFullYear()}-{pad(now.getMonth() + 1)}-{pad(now.getDate())} · 周{WEEK[now.getDay()]}</span><em>{PHASE_WORD[livePhase]}</em></div><div className="h-row"><span>{pad(now.getHours())}:{pad(now.getMinutes())}:{pad(now.getSeconds())}.{pad(now.getMilliseconds(), 3)}</span></div><div className="h-wx">天象 <i>{raining ? '雨' : weather === 'SNOW' ? '雪' : weather === 'FOG' ? '雾' : weather === 'CLOUD' ? '云' : '晴'}</i> · 当值 <i>{DOMAINS[dayIndex].name}</i></div><button className="h-link" type="button" onClick={onOpenEra}>演化史 · 长河</button></aside>
|
||||
<button className="source" style={sourceStyle} type="button" aria-label="展开曜冥纪元演化史" onClick={onOpenEra}><span className="src-core"/></button>
|
||||
|
||||
<div className="domains">{DOMAINS.map((domain, index) => {
|
||||
const onDuty = index === dayIndex
|
||||
const slot = onDuty ? 2 : slots[sideCursor++]
|
||||
const size = [30, 40, 50, 62, 76][domain.stage - 1] * scale
|
||||
const size = [30, 40, 50, 62, 76][domain.stage - 1]
|
||||
const halo = size * 2.6
|
||||
const ring = size * 2.1
|
||||
const sat = size * 1.55
|
||||
const style = { width: 150 * scale, left: sideCenters[slot] - 75 * scale, top: baseline + domain.y * scale, '--d-hi': domain.hi, '--d-lo': domain.lo, '--d-glow': domain.glow, '--d-glow-soft': domain.glowSoft, '--d-glow-r': `${size * .7}px`, '--d-ring-c': domain.glow, '--d-sat-r': `${sat}px` } as CSSProperties
|
||||
return <button type="button" key={domain.id} className={`domain${onDuty ? ' on-duty' : ''}`} style={style} onClick={() => onDomain(domain.id)}>
|
||||
const style = { '--d-hi': domain.hi, '--d-lo': domain.lo, '--d-glow': domain.glow, '--d-glow-soft': domain.glowSoft, '--d-glow-r': `${size * .7}px`, '--d-ring-c': domain.glow, '--d-sat-r': `${sat}px` } as CSSProperties
|
||||
return <button type="button" key={domain.id} className={`domain domain-${domain.id.toLowerCase()}${onDuty ? ' on-duty' : ''}`} style={style} onClick={() => onDomain(domain.id)}>
|
||||
<span className="d-body" style={{ width: halo, height: halo }}>
|
||||
{domain.stage >= 2 && <span className="d-halo" style={{ width: halo, height: halo }}/>}
|
||||
{domain.stage >= 3 && <span className="d-ring" style={{ width: ring, height: ring }}/>}
|
||||
|
|
@ -135,7 +125,7 @@ export function StarlakeSurface({ awake = false, phase, weather = 'CLEAR', authe
|
|||
{domain.stage >= 2 && <span className="d-core" style={{ width: size, height: size }}/>}
|
||||
{domain.stage === 1 && <><span className="d-core" style={{ width: size * .55, height: size * .55, opacity: .7 }}/><span className="d-dust">{Array.from({ length: 6 }, (_, dust) => <i key={dust} style={{ '--dust-a': `${dust * 60}deg`, '--dust-r0': `${26 + dust % 3 * 7}px`, '--dust-t': `${4.6 + dust * .7}s`, animationDelay: `${-dust * .9}s` } as CSSProperties}/>)}</span></>}
|
||||
</span>
|
||||
<span className="d-water" style={{ width: size * 3.2, height: size * .85 }}/><span className="d-mirror" style={{ width: size * 2.2, height: size * .5 }}/><span className="d-label">{domain.name}</span><span className="d-stage">{domain.stageName}</span>
|
||||
<span className="d-water" style={{ width: size * 3.2, height: size * .85 }}/><span className="d-mirror" style={{ width: size * 2.2, height: size * .5 }}/><span className="d-label">{domain.name}</span><span className="d-stage">{domain.role}</span>
|
||||
</button>
|
||||
})}</div>
|
||||
<button className="channel-entry" type="button" onClick={onEnterChannel}>{authenticated ? '返回我的频道' : '编号验证 · 进入我的频道'}</button>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useEffect, useRef } from 'react'
|
||||
import './traditional-surface.css'
|
||||
import { resolveLayoutMetrics } from './layout-profile'
|
||||
|
||||
/* Qoder 锁定验收稿的传统工作台结构。宿主只注入真实投影,不在视觉层制造广播或指标。 */
|
||||
export type FinishId = 'aurora' | 'nebula' | 'abyss' | 'jade' | 'cinnabar' | 'champagne' | 'porcelain' | 'snow'
|
||||
|
|
@ -59,12 +60,12 @@ export function TraditionalSurface({ awake = false, finish, onFinish, onActivity
|
|||
if (!element) return
|
||||
const layout = () => {
|
||||
const { width, height } = element.getBoundingClientRect()
|
||||
const metrics = resolveLayoutMetrics(width, height)
|
||||
const vmin = Math.min(width, height)
|
||||
element.style.setProperty('--gap', `${Math.round(Math.min(18, Math.max(10, vmin * .016)))}px`)
|
||||
element.style.setProperty('--pad', `${Math.round(Math.min(26, Math.max(12, vmin * .02)))}px`)
|
||||
const narrow = width < 880
|
||||
element.classList.toggle('stack', narrow)
|
||||
if (!narrow) element.style.setProperty('--cols', `${Math.min(1.32, Math.max(1.08, width / height * .66)).toFixed(2)}fr 1fr`)
|
||||
element.dataset.layout = metrics.profile
|
||||
element.dataset.density = metrics.density
|
||||
}
|
||||
const observer = new ResizeObserver(layout)
|
||||
observer.observe(element)
|
||||
|
|
@ -96,7 +97,7 @@ export function TraditionalSurface({ awake = false, finish, onFinish, onActivity
|
|||
<div className="card glass"><div className="hd"><span className="lbl">频 道 与 文 档</span><span className="tag" style={{ '--tc': 'var(--good)' } as React.CSSProperties}>在册 {channels.length} 项</span></div><div className="rows">{channels.map(channel => <div className="row" key={channel.title}><span className="dot" style={{ color: channel.color, background: channel.color }}/><div className="t"><b>{channel.title}</b><span>{channel.meta}</span></div><span className="tag" style={{ '--tc': channel.tone } as React.CSSProperties}>{channel.tag}</span><span className="num">{channel.count}</span></div>)}</div></div>
|
||||
<div className="card glass"><div className="hd"><span className="lbl">系 统 态</span><span className="tag" style={{ '--tc': 'var(--good)' } as React.CSSProperties}>真实状态</span></div><div className="rows">{systems.map(system => <div className="row" key={system.title}><div className="t"><b>{system.title}</b><span>{system.meta}</span></div><span className="tag" style={system.tone ? { '--tc': system.tone } as React.CSSProperties : undefined}>{system.tag}</span></div>)}</div></div>
|
||||
</div>
|
||||
<div className="finisher glass"><small>玻璃氛围</small>{FINISHES.map(item => <button key={item.id} type="button" className={finish === item.id ? 'on' : ''} onClick={() => pickFinish(item.id)}>{item.name}</button>)}</div>
|
||||
</div>
|
||||
<div className="finisher glass"><small>玻璃氛围</small>{FINISHES.map(item => <button key={item.id} type="button" className={finish === item.id ? 'on' : ''} onClick={() => pickFinish(item.id)}>{item.name}</button>)}</div>
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
import { resolveLayoutMetrics } from './layout-profile'
|
||||
|
||||
describe('resolveLayoutMetrics', () => {
|
||||
it.each([
|
||||
[1920, 1080, 'panoramic'],
|
||||
[1440, 900, 'wide'],
|
||||
[1024, 768, 'compact'],
|
||||
[820, 1180, 'compact'],
|
||||
[720, 900, 'stacked'],
|
||||
[1280, 600, 'compact'],
|
||||
] as const)('maps %d×%d to %s without whole-canvas scaling', (width, height, profile) => {
|
||||
expect(resolveLayoutMetrics(width, height).profile).toBe(profile)
|
||||
})
|
||||
|
||||
it('compacts a short window before text becomes unreadable', () => {
|
||||
const metrics = resolveLayoutMetrics(1280, 600)
|
||||
expect(metrics.density).toBe('compact')
|
||||
expect(metrics.gutter).toBeGreaterThanOrEqual(16)
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
export type LayoutProfile = 'panoramic' | 'wide' | 'compact' | 'stacked'
|
||||
|
||||
export interface LayoutMetrics {
|
||||
width: number
|
||||
height: number
|
||||
profile: LayoutProfile
|
||||
density: 'comfortable' | 'compact'
|
||||
gutter: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsive layout is selected from the actual component box, not the monitor.
|
||||
* Width decides how many semantic columns fit; height can compact or stack a
|
||||
* landscape layout before any readable text is scaled down.
|
||||
*/
|
||||
export function resolveLayoutMetrics(width: number, height: number): LayoutMetrics {
|
||||
const safeWidth = Math.max(320, Number.isFinite(width) ? width : 320)
|
||||
const safeHeight = Math.max(420, Number.isFinite(height) ? height : 420)
|
||||
const short = safeHeight < 690
|
||||
|
||||
let profile: LayoutProfile
|
||||
if (safeWidth >= 1500 && safeHeight >= 760) profile = 'panoramic'
|
||||
else if (safeWidth >= 1120 && safeHeight >= 650) profile = 'wide'
|
||||
else if (safeWidth >= 760 && safeHeight >= 560) profile = 'compact'
|
||||
else profile = 'stacked'
|
||||
|
||||
return {
|
||||
width: safeWidth,
|
||||
height: safeHeight,
|
||||
profile,
|
||||
density: short || profile === 'stacked' ? 'compact' : 'comfortable',
|
||||
gutter: Math.round(Math.min(32, Math.max(16, Math.min(safeWidth, safeHeight) * .026))),
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/* ═══ 星湖套 v1.3 · 语言世界首页视觉层(作用域 .starlake-scene,不污染功能层)═══ */
|
||||
.starlake-scene { position:fixed; inset:0; z-index:0; pointer-events:none; overflow:hidden;
|
||||
.starlake-scene { position:absolute; inset:0; z-index:0; pointer-events:none; overflow:hidden; container-type:size;
|
||||
font-family:"PingFang SC","Hiragino Sans GB","Source Han Sans SC",system-ui,sans-serif;
|
||||
background:linear-gradient(180deg, #04060e 0%, #0a1128 34%, #0e1532 56%, #0b1129 74%, #060a18 100%); }
|
||||
|
||||
|
|
@ -15,9 +15,9 @@
|
|||
@keyframes sl-glint { 0%,100%{opacity:var(--go)} 50%{opacity:calc(var(--go) * .2)} }
|
||||
|
||||
/* 星湖:天幕向下熔成的镜 */
|
||||
.starlake-scene .lake { position:absolute; left:0; right:0; top:62vh; bottom:0; overflow:hidden;
|
||||
.starlake-scene .lake { position:absolute; left:0; right:0; top:58%; bottom:0; overflow:hidden;
|
||||
background:linear-gradient(180deg, transparent 0%, rgba(9,13,32,.42) 16%, rgba(6,9,24,.78) 44%, rgba(4,6,15,.94) 100%); }
|
||||
.starlake-scene .lake .sheen { position:absolute; left:0; right:0; top:-4vh; height:9vh;
|
||||
.starlake-scene .lake .sheen { position:absolute; left:0; right:0; top:-9%; height:22%;
|
||||
background:linear-gradient(180deg, transparent, rgba(160,178,255,.10) 46%, rgba(255,236,190,.12) 52%, transparent);
|
||||
filter:blur(2px); }
|
||||
.starlake-scene .lake .orbit { position:absolute; left:50%; border-radius:50%; border:1px solid rgba(150,172,255,.12); transform:translateX(-50%); }
|
||||
|
|
@ -55,16 +55,16 @@
|
|||
@keyframes sl-src-breathe { 0%,100%{transform:scale(1)} 50%{transform:scale(1.1)} }
|
||||
|
||||
/* 官方门面:主标软著全称 + 副行技术名 */
|
||||
.starlake-scene .masthead { position:absolute; top:7.2vh; left:0; right:0; text-align:center; }
|
||||
.starlake-scene .masthead { position:absolute; top:clamp(34px, 7%, 68px); left:var(--sl-gutter); right:var(--sl-gutter); text-align:center; }
|
||||
.starlake-scene .masthead small { display:block; color:#f7ebc8; font-size:15px; font-weight:700;
|
||||
letter-spacing:.3em; text-indent:.3em; text-shadow:0 1px 10px rgba(0,0,0,.7); }
|
||||
.starlake-scene .masthead b { display:block; color:rgba(234,238,248,.96); font-size:clamp(25px, calc(42px * var(--sl-scale)), 54px); font-weight:800;
|
||||
margin-top:2.6vh; letter-spacing:.12em; text-indent:.12em; text-shadow:0 2px 26px rgba(0,0,0,.7); }
|
||||
.starlake-scene .masthead b { display:block; color:rgba(234,238,248,.96); font-size:clamp(27px, 3.15cqw, 48px); font-weight:800;
|
||||
margin-top:clamp(16px, 2.2cqh, 25px); letter-spacing:.1em; text-indent:.1em; text-shadow:0 2px 26px rgba(0,0,0,.7); }
|
||||
.starlake-scene .masthead span { display:block; margin-top:1.8vh; color:#f7ebc8; font-size:14px;
|
||||
font-weight:700; letter-spacing:.42em; text-indent:.42em; text-shadow:0 1px 10px rgba(0,0,0,.7); }
|
||||
|
||||
/* 右侧轻文字:今日光湖历 */
|
||||
.starlake-scene .hud { position:absolute; right:32px; top:29vh; z-index:7; text-align:right; }
|
||||
.starlake-scene .hud { position:absolute; right:var(--sl-gutter); top:clamp(185px, 27%, 270px); z-index:7; text-align:right; }
|
||||
.starlake-scene .hud .h-top small { color:rgba(196,206,228,.62); font-size:11px; font-weight:650; letter-spacing:.34em; }
|
||||
.starlake-scene .hud .h-top b { display:block; margin-top:7px; color:#f7ebc8; font-size:24px; font-weight:760;
|
||||
letter-spacing:.08em; font-variant-numeric:tabular-nums; text-shadow:0 0 18px rgba(247,235,200,.35); }
|
||||
|
|
@ -80,8 +80,12 @@
|
|||
padding-bottom:4px; transition:color .18s ease, border-color .18s ease; }
|
||||
.starlake-scene .hud .h-link:hover { color:#f7ebc8; border-color:rgba(247,235,200,.5); }
|
||||
|
||||
/* 五域:成长天体五相,悬浮星湖 */
|
||||
.starlake-scene .domain { position:absolute; z-index:6; width:150px; padding:0; border:0; text-align:center; cursor:pointer; pointer-events:auto; color:inherit; background:transparent; font:inherit; }
|
||||
/* 五域:语义位置固定,按容器宽高重排。天体可缩,文字和点击区不跟随缩放。 */
|
||||
.starlake-scene .domains { position:absolute; z-index:6; left:var(--sl-gutter); right:var(--sl-gutter); bottom:clamp(74px, 8%, 98px);
|
||||
display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); align-items:end; gap:clamp(8px,1.8cqw,28px); }
|
||||
.starlake-scene .domain { position:relative; z-index:6; min-width:0; width:100%; padding:0 3px; border:0; text-align:center; cursor:pointer; pointer-events:auto; color:inherit; background:transparent; font:inherit; }
|
||||
.starlake-scene .domain-branch_domain,.starlake-scene .domain-zero_sense_domain { transform:translateY(20px); }
|
||||
.starlake-scene .domain-fifth_domain { transform:translateY(-18px); }
|
||||
.starlake-scene .d-body { position:relative; display:block; margin:0 auto; }
|
||||
.starlake-scene .d-core { position:absolute; left:50%; top:50%; border-radius:50%; transform:translate(-50%,-50%);
|
||||
background:
|
||||
|
|
@ -116,16 +120,16 @@
|
|||
0% { transform:rotate(var(--dust-a)) translateX(var(--dust-r0)); opacity:0; }
|
||||
12% { opacity:.9; } 88% { opacity:.75; }
|
||||
100% { transform:rotate(calc(var(--dust-a) + 300deg)) translateX(3px); opacity:0; } }
|
||||
.starlake-scene .d-label { display:block; margin-top:calc(10px * var(--sl-scale)); color:rgba(222,228,242,.88); font-size:clamp(12px, calc(16px * var(--sl-scale)), 20px); font-weight:720;
|
||||
.starlake-scene .d-label { display:block; margin-top:9px; color:rgba(222,228,242,.88); font-size:clamp(13px, 1.2cqw, 17px); font-weight:720;
|
||||
letter-spacing:.3em; text-indent:.3em; transition:color .18s ease; text-shadow:0 1px 10px rgba(0,0,0,.8); }
|
||||
.starlake-scene .domain:hover .d-label { color:rgba(234,238,248,.96); }
|
||||
.starlake-scene .d-stage { display:block; margin-top:calc(5px * var(--sl-scale)); color:rgba(196,206,228,.62); font-size:clamp(9px, calc(12px * var(--sl-scale)), 15px); letter-spacing:.28em; text-indent:.28em;
|
||||
opacity:0; transition:opacity .25s ease; text-shadow:0 1px 8px rgba(0,0,0,.8); }
|
||||
.starlake-scene .domain:hover .d-stage { opacity:1; }
|
||||
.starlake-scene .d-stage { display:block; min-height:2.6em; margin:5px auto 0; max-width:20em; color:rgba(196,206,228,.62); font-size:clamp(9px, .82cqw, 12px); line-height:1.35; letter-spacing:.08em;
|
||||
opacity:.86; transition:opacity .25s ease,color .25s ease; text-shadow:0 1px 8px rgba(0,0,0,.8); }
|
||||
.starlake-scene .domain:hover .d-stage { opacity:1; color:rgba(222,228,242,.9); }
|
||||
.starlake-scene .domain.on-duty .d-label { color:#f7ebc8; }
|
||||
.starlake-scene .domain.on-duty .d-stage { opacity:1; color:rgba(247,235,200,.72); }
|
||||
.starlake-scene .domain.open .d-label { color:#f7ebc8; }
|
||||
.starlake-scene .channel-entry { position:absolute; left:24px; bottom:25px; z-index:9; pointer-events:auto; cursor:pointer;
|
||||
.starlake-scene .channel-entry { position:absolute; left:var(--sl-gutter); bottom:22px; z-index:9; pointer-events:auto; cursor:pointer;
|
||||
padding:9px 15px; border:1px solid rgba(202,216,255,.13); border-radius:999px; color:rgba(222,228,242,.78);
|
||||
background:rgba(8,12,28,.44); backdrop-filter:blur(16px); font:650 12px/1.2 inherit; letter-spacing:.08em; }
|
||||
.starlake-scene .channel-entry:hover { color:#f7ebc8; border-color:rgba(247,235,200,.42); }
|
||||
|
|
@ -142,12 +146,37 @@
|
|||
.starlake-host > .surface-pill,
|
||||
.trad-surface ~ .surface-pill { top:12px; }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.starlake-scene .masthead b { font-size:clamp(24px, 5vw, 36px); }
|
||||
.starlake-scene .masthead span { font-size:10px; }
|
||||
.starlake-scene .hud { right:18px; top:25vh; transform:scale(.86); transform-origin:top right; }
|
||||
.starlake-scene .era-foot { display:none; }
|
||||
}
|
||||
.starlake-scene[data-layout="compact"] .masthead { top:52px; text-align:left; right:320px; }
|
||||
.starlake-scene[data-layout="compact"] .masthead small { display:none; }
|
||||
.starlake-scene[data-layout="compact"] .masthead b { font-size:clamp(22px,3.5cqw,32px); margin-top:0; letter-spacing:.06em; }
|
||||
.starlake-scene[data-layout="compact"] .masthead span { margin-top:8px; font-size:9px; letter-spacing:.2em; }
|
||||
.starlake-scene[data-layout="compact"] .hud { top:44px; transform:scale(.82); transform-origin:top right; }
|
||||
.starlake-scene[data-layout="compact"] .domains { top:37%; bottom:70px; grid-template-columns:repeat(3,minmax(0,1fr)); grid-template-rows:repeat(2,minmax(0,1fr)); align-items:end; gap:4px 16px; }
|
||||
.starlake-scene[data-layout="compact"] .domain { transform:none; align-self:end; }
|
||||
.starlake-scene[data-layout="compact"] .domain-zero_domain { grid-column:1 / 2; }
|
||||
.starlake-scene[data-layout="compact"] .domain-zero_sense_domain { grid-column:3 / 4; }
|
||||
.starlake-scene[data-layout="compact"] .d-body { transform:scale(.82); transform-origin:bottom center; margin-bottom:-16px; }
|
||||
.starlake-scene[data-layout="compact"] .d-water,.starlake-scene[data-layout="compact"] .d-mirror { transform:scale(.82); }
|
||||
.starlake-scene[data-layout="compact"] .d-stage { max-width:15em; }
|
||||
.starlake-scene[data-layout="compact"] .lake { top:34%; }
|
||||
.starlake-scene[data-layout="compact"] .era-foot { display:none; }
|
||||
|
||||
.starlake-scene[data-layout="stacked"] { overflow:auto; min-height:620px; }
|
||||
.starlake-scene[data-layout="stacked"] .masthead { top:64px; text-align:left; }
|
||||
.starlake-scene[data-layout="stacked"] .masthead small { display:none; }
|
||||
.starlake-scene[data-layout="stacked"] .masthead b { font-size:clamp(21px,5.6cqw,30px); margin-top:0; letter-spacing:.04em; }
|
||||
.starlake-scene[data-layout="stacked"] .masthead span { margin-top:8px; font-size:8px; letter-spacing:.12em; }
|
||||
.starlake-scene[data-layout="stacked"] .hud { top:150px; right:var(--sl-gutter); transform:scale(.78); transform-origin:top right; }
|
||||
.starlake-scene[data-layout="stacked"] .domains { top:285px; bottom:auto; padding-bottom:92px; grid-template-columns:repeat(2,minmax(0,1fr)); align-items:end; gap:24px 8px; }
|
||||
.starlake-scene[data-layout="stacked"] .domain { transform:none; }
|
||||
.starlake-scene[data-layout="stacked"] .domain-fifth_domain { grid-column:1 / -1; grid-row:1; }
|
||||
.starlake-scene[data-layout="stacked"] .domain-branch_domain { grid-row:2; }
|
||||
.starlake-scene[data-layout="stacked"] .domain-main_domain { grid-row:2; }
|
||||
.starlake-scene[data-layout="stacked"] .domain-zero_domain { grid-row:3; }
|
||||
.starlake-scene[data-layout="stacked"] .domain-zero_sense_domain { grid-row:3; }
|
||||
.starlake-scene[data-layout="stacked"] .d-body { transform:scale(.75); transform-origin:bottom center; margin-bottom:-21px; }
|
||||
.starlake-scene[data-layout="stacked"] .lake { top:27%; bottom:-360px; }
|
||||
.starlake-scene[data-layout="stacked"] .source,.starlake-scene[data-layout="stacked"] .era-foot { display:none; }
|
||||
|
||||
.starlake-scene .d-core,.starlake-scene .d-ring,.starlake-scene .d-sat-arm,.starlake-scene .d-dust i,
|
||||
.starlake-scene .d-mirror,.starlake-scene .d-water,.starlake-scene .source .src-core,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
color:var(--ink); -webkit-font-smoothing:antialiased;
|
||||
background:var(--bg-base); transition:background .5s ease;
|
||||
}
|
||||
.trad-surface.stack { overflow:auto; }
|
||||
|
||||
/* ── 八式氛围令牌 ── */
|
||||
.trad-surface,
|
||||
|
|
@ -100,7 +99,7 @@
|
|||
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); }
|
||||
|
||||
/* ── 地基二层:玻璃本体 ── */
|
||||
.trad-surface .glass { position:relative; border-radius:22px;
|
||||
.trad-surface .glass { position:relative; border-radius:16px;
|
||||
background:linear-gradient(165deg, var(--glass-hi), var(--glass-lo) 55%);
|
||||
-webkit-backdrop-filter:blur(26px) saturate(1.5); backdrop-filter:blur(26px) saturate(1.5);
|
||||
border:1px solid var(--glass-edge);
|
||||
|
|
@ -133,14 +132,12 @@
|
|||
.trad-surface .row .num { flex:none; color:var(--ink-sub); font-size:12px; font-weight:650; font-variant-numeric:tabular-nums; }
|
||||
|
||||
/* ── 布局:悬浮导航 + 两列 ── */
|
||||
.trad-surface .stage { position:relative; z-index:2; height:100%; display:flex; flex-direction:column;
|
||||
padding:var(--pad, 18px) calc(var(--pad, 18px) + 4px); gap:var(--gap, 14px); max-width:1320px; margin:0 auto; }
|
||||
.trad-surface:not(.stack) .stage { padding-top:max(62px, var(--pad, 18px)); }
|
||||
.trad-surface.stack .stage { height:auto; min-height:100%; }
|
||||
.trad-surface .nav { display:flex; align-items:center; gap:20px; padding:11px 20px; border-radius:999px; flex:none; }
|
||||
.trad-surface .stage { position:relative; z-index:2; height:100%; display:grid; grid-template-rows:auto minmax(0,1fr) auto;
|
||||
padding:max(62px,var(--pad,18px)) calc(var(--pad,18px) + 4px) var(--pad,18px); gap:var(--gap,14px); max-width:1440px; margin:0 auto; }
|
||||
.trad-surface .nav { display:flex; align-items:center; gap:18px; padding:10px 16px; border-radius:16px; min-width:0; }
|
||||
.trad-surface .nav .brand { font-size:15.5px; font-weight:800; letter-spacing:.16em; color:var(--ink); }
|
||||
.trad-surface .nav .brand small { margin-left:9px; color:var(--ink-faint); font-size:9.5px; font-weight:700; letter-spacing:.22em; }
|
||||
.trad-surface .nav .tabs { display:flex; gap:3px; margin-left:8px; }
|
||||
.trad-surface .nav .tabs { display:flex; gap:3px; margin-left:4px; min-width:0; overflow-x:auto; scrollbar-width:none; }
|
||||
.trad-surface .nav .tabs button { border:0; background:transparent; color:var(--ink-sub); font-size:13px; font-weight:650;
|
||||
letter-spacing:.05em; padding:7px 15px; border-radius:999px; cursor:pointer;
|
||||
transition:color .18s ease, background .18s ease; }
|
||||
|
|
@ -157,12 +154,8 @@
|
|||
transition:color .18s ease, border-color .18s ease; }
|
||||
.trad-surface .nav .back:hover { color:var(--ink); border-color:color-mix(in srgb, var(--accent) 55%, var(--glass-edge)); }
|
||||
|
||||
.trad-surface .grid { flex:1; min-height:0; display:grid; gap:var(--gap, 14px);
|
||||
grid-template-columns:var(--cols, 1.28fr 1fr); grid-template-rows:1fr 1fr; }
|
||||
.trad-surface.stack .grid { grid-template-columns:1fr; grid-template-rows:none; }
|
||||
.trad-surface.stack .hero { grid-row:auto; min-height:500px; }
|
||||
.trad-surface.stack .card { min-height:250px; }
|
||||
.trad-surface.stack .nav { flex-wrap:wrap; border-radius:26px; }
|
||||
.trad-surface .grid { min-height:0; display:grid; gap:var(--gap,14px);
|
||||
grid-template-columns:minmax(0,1.18fr) minmax(320px,.92fr); grid-template-rows:minmax(0,1fr) minmax(0,1fr); }
|
||||
.trad-surface .hero { grid-row:span 2; padding:26px 28px; display:flex; flex-direction:column; overflow:hidden; }
|
||||
.trad-surface .hero .topline { display:flex; justify-content:space-between; align-items:baseline; }
|
||||
.trad-surface .hero .day { margin-top:14px; font-size:clamp(38px, 5.2vw, 60px); font-weight:850; letter-spacing:.02em; color:var(--ink);
|
||||
|
|
@ -206,7 +199,7 @@
|
|||
.trad-surface .card .rows { flex:1; min-height:0; overflow:auto; }
|
||||
|
||||
/* ── 氛围切换器 ── */
|
||||
.trad-surface .finisher { position:absolute; right:22px; bottom:18px; z-index:9; display:flex; gap:3px; padding:5px; border-radius:999px; }
|
||||
.trad-surface .finisher { z-index:9; justify-self:end; display:flex; gap:3px; padding:4px; border-radius:14px; max-width:100%; overflow-x:auto; }
|
||||
.trad-surface .finisher small { align-self:center; color:var(--ink-faint); font-size:9.5px; font-weight:700;
|
||||
letter-spacing:.2em; padding:0 6px 0 10px; }
|
||||
.trad-surface .finisher button { border:0; background:transparent; color:var(--ink-sub); font-size:11.5px; font-weight:700;
|
||||
|
|
@ -217,6 +210,35 @@
|
|||
.trad-surface .nav .back { display:none; }
|
||||
.trad-surface[data-finish="porcelain"] .finisher button.on, .trad-surface[data-finish="snow"] .finisher button.on { background:rgba(255,255,255,.7); }
|
||||
|
||||
.trad-surface[data-density="compact"] .stage { padding-top:56px; }
|
||||
.trad-surface[data-density="compact"] .hero { padding:18px 20px; }
|
||||
.trad-surface[data-density="compact"] .hero .day { margin-top:8px; font-size:clamp(34px,4.5vw,48px); }
|
||||
.trad-surface[data-density="compact"] .hero .clock { margin-top:12px; }
|
||||
.trad-surface[data-density="compact"] .broadcast { margin-top:14px; }
|
||||
.trad-surface[data-density="compact"] .row { padding:8px 10px; }
|
||||
.trad-surface[data-density="compact"] .card { padding:11px; }
|
||||
|
||||
.trad-surface[data-layout="compact"] .nav { gap:10px; }
|
||||
.trad-surface[data-layout="compact"] .nav .brand small,.trad-surface[data-layout="compact"] .nav .me { display:none; }
|
||||
.trad-surface[data-layout="compact"] .nav .tabs button { padding-inline:10px; }
|
||||
.trad-surface[data-layout="compact"] .grid { grid-template-columns:minmax(0,1.12fr) minmax(280px,.88fr); }
|
||||
.trad-surface[data-layout="compact"] .hero .stats { grid-template-columns:1fr; }
|
||||
.trad-surface[data-layout="compact"] .hero .stat:last-child { display:none; }
|
||||
.trad-surface[data-layout="compact"] .finisher small { display:none; }
|
||||
.trad-surface[data-layout="compact"] .finisher button { padding-inline:9px; }
|
||||
|
||||
.trad-surface[data-layout="stacked"] { overflow:auto; }
|
||||
.trad-surface[data-layout="stacked"] .stage { height:auto; min-height:100%; display:flex; flex-direction:column; padding-top:58px; }
|
||||
.trad-surface[data-layout="stacked"] .nav { flex-wrap:wrap; }
|
||||
.trad-surface[data-layout="stacked"] .nav .brand { width:100%; }
|
||||
.trad-surface[data-layout="stacked"] .nav .brand small { float:right; }
|
||||
.trad-surface[data-layout="stacked"] .nav .tabs { order:3; width:100%; }
|
||||
.trad-surface[data-layout="stacked"] .nav .me { display:none; }
|
||||
.trad-surface[data-layout="stacked"] .grid { grid-template-columns:1fr; grid-template-rows:none; }
|
||||
.trad-surface[data-layout="stacked"] .hero { grid-row:auto; min-height:520px; }
|
||||
.trad-surface[data-layout="stacked"] .card { min-height:260px; }
|
||||
.trad-surface[data-layout="stacked"] .finisher { align-self:stretch; justify-self:auto; justify-content:flex-start; }
|
||||
|
||||
/* ── 语言世界侧入口按钮(world-footer 内) ── */
|
||||
.world-footer .surface-switch { margin-left:auto; border:1px solid currentColor; background:transparent;
|
||||
color:inherit; font-size:11px; font-weight:700; letter-spacing:.14em; padding:5px 14px; border-radius:999px;
|
||||
|
|
@ -332,21 +354,14 @@
|
|||
backdrop-filter:none;
|
||||
}
|
||||
|
||||
.global-finish-rail {
|
||||
position:fixed; z-index:121; right:18px; bottom:13px; display:flex; flex-wrap:wrap; max-width:calc(100vw - 36px);
|
||||
gap:3px; padding:5px; border:1px solid var(--glass-edge); border-radius:999px;
|
||||
background:color-mix(in srgb,var(--bg-base) 72%,transparent); box-shadow:inset 0 1px 0 var(--glass-inner),0 10px 30px rgba(0,0,0,.25);
|
||||
-webkit-backdrop-filter:blur(18px); backdrop-filter:blur(18px);
|
||||
}
|
||||
.global-finish-rail button { border:0; border-radius:999px; padding:5px 10px; color:var(--ink-sub); background:transparent;
|
||||
.global-finish-menu { position:relative; z-index:3; color:var(--ink-sub); font:700 10px/1.2 "PingFang SC","Hiragino Sans GB","Source Han Sans SC",system-ui,sans-serif; }
|
||||
.global-finish-menu summary { list-style:none; cursor:pointer; padding:5px 9px; border:1px solid var(--glass-edge); border-radius:9px; background:var(--glass-lo); }
|
||||
.global-finish-menu summary::-webkit-details-marker { display:none; }
|
||||
.global-finish-menu > div { position:absolute; right:0; top:calc(100% + 7px); display:grid; grid-template-columns:repeat(2,1fr); width:178px; gap:3px; padding:6px; border:1px solid var(--glass-edge); border-radius:12px; background:color-mix(in srgb,var(--bg-base) 92%,transparent); box-shadow:0 12px 30px rgba(0,0,0,.34); }
|
||||
.global-finish-menu button { border:0; border-radius:8px; padding:6px 8px; color:var(--ink-sub); background:transparent;
|
||||
cursor:pointer; font:700 10.5px/1.2 "PingFang SC","Hiragino Sans GB","Source Han Sans SC",system-ui,sans-serif; letter-spacing:.08em; }
|
||||
.global-finish-rail button:hover { color:var(--ink); }
|
||||
.global-finish-rail button.on { color:var(--ink); background:var(--glass-hi); }
|
||||
.official-world.surface-traditional:not(.motion-awake) .global-finish-rail { -webkit-backdrop-filter:none; backdrop-filter:none; }
|
||||
|
||||
@media (max-width:760px) {
|
||||
.global-finish-rail { right:12px; bottom:9px; border-radius:18px; }
|
||||
}
|
||||
.global-finish-menu button:hover { color:var(--ink); }
|
||||
.global-finish-menu button.on { color:var(--ink); background:var(--glass-hi); }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.trad-surface .sky::before { animation:none; }
|
||||
|
|
|
|||
Loading…
Reference in a new issue