feat(ui): run Qoder locked lake world template
This commit is contained in:
parent
08f514b9d1
commit
8fb21787b5
5 changed files with 385 additions and 59 deletions
|
|
@ -1,11 +1,12 @@
|
||||||
{
|
{
|
||||||
"schema": "hololake.enterprise-four-domain-entry/v1",
|
"schema": "hololake.enterprise-four-domain-entry/v1",
|
||||||
"record_id": "HLP-ENTERPRISE-4D-ENTRY-CONTRACT-001",
|
"record_id": "HLP-ENTERPRISE-4D-ENTRY-CONTRACT-001",
|
||||||
"state": "SOURCE_CONTRACT_RUNTIME_NOT_IMPLEMENTED",
|
"state": "SERVER_LIVE_CLIENT_UI_INTEGRATED",
|
||||||
"number_gate": {"precedes_credentials":true,"user_selects_domain":false},
|
"number_gate": {"precedes_credentials":true,"user_selects_domain":false},
|
||||||
"credential_gate": {"uses_bound_private_repository":true,"first_login_forces_password_change":true},
|
"credential_gate": {"uses_bound_private_repository":true,"first_login_forces_password_change":true},
|
||||||
"persona_relationship_gate": {"shows_species":"AGE","shows_current_persona_identity":true,"shows_invalid_age_individual_numbers":false,"human_confirms_relationship_mapping":true,"human_confirmation_is_persona_acceptance":false,"responsibility_acceptance_is_separate":true},
|
"persona_relationship_gate": {"shows_species":"AGE","shows_current_persona_identity":true,"shows_invalid_age_individual_numbers":false,"human_confirms_relationship_mapping":true,"human_confirmation_is_persona_acceptance":false,"responsibility_acceptance_is_separate":true},
|
||||||
"work_entry": {"domain":"DOMAIN-ZS","channel":"GUANGHU_CHANNEL","preserves_responsibility_domain":true},
|
"work_entry": {"domain":"DOMAIN-ZS","channel":"GUANGHU_CHANNEL","preserves_responsibility_domain":true},
|
||||||
"personal_route": {"separate_node_ownership_check":true,"enterprise_credentials_are_sufficient":false},
|
"personal_route": {"separate_node_ownership_check":true,"enterprise_credentials_are_sufficient":false},
|
||||||
|
"ui_template": "REPO-012@27d34dfdbf5df4c67b805402d442e5912c8f4c31:official-login-template-v0.4-locked+inner-screens-v0.1",
|
||||||
"source": "routing/hololake-enterprise-four-domain-work-channel.json"
|
"source": "routing/hololake-enterprise-four-domain-work-channel.json"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,12 @@ test('the public shell shows five domains and login is domain-routed', () => {
|
||||||
assert.match(frontend, new RegExp(name.replace('·', '\\·')))
|
assert.match(frontend, new RegExp(name.replace('·', '\\·')))
|
||||||
}
|
}
|
||||||
assert.match(frontend, /输入编号进入所属域/)
|
assert.match(frontend, /输入编号进入所属域/)
|
||||||
|
assert.match(frontend, /TCS-GL-/)
|
||||||
|
assert.match(frontend, /编号 \{zeroPoint\?\.userNumber \|\| gateNumber\} · 欢迎登入光湖语言世界/)
|
||||||
|
assert.match(frontend, /核对人格关系/)
|
||||||
|
assert.match(frontend, /确认关系并签署/)
|
||||||
|
assert.match(frontend, /接受责任并签署/)
|
||||||
|
assert.doesNotMatch(frontend, /<aside className="sidebar">/)
|
||||||
assert.match(login, /login_host_for_domain/)
|
assert.match(login, /login_host_for_domain/)
|
||||||
assert.match(login, /ENTERPRISE_LOGIN_HOST.*guanghu\.chat/)
|
assert.match(login, /ENTERPRISE_LOGIN_HOST.*guanghu\.chat/)
|
||||||
assert.match(login, /change_enterprise_first_login_password/)
|
assert.match(login, /change_enterprise_first_login_password/)
|
||||||
|
|
@ -36,6 +42,17 @@ test('the public shell shows five domains and login is domain-routed', () => {
|
||||||
assert.match(router, /FIFTH_DOMAIN.*MAIN_DOMAIN.*BRANCH_DOMAIN.*ZERO_DOMAIN.*ZERO_SENSE_DOMAIN/s)
|
assert.match(router, /FIFTH_DOMAIN.*MAIN_DOMAIN.*BRANCH_DOMAIN.*ZERO_DOMAIN.*ZERO_SENSE_DOMAIN/s)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('the locked Qoder world template is the running client structure, not a skin reference', () => {
|
||||||
|
const contract = JSON.parse(read('contracts/enterprise-four-domain-entry.json'))
|
||||||
|
const frontend = read('src/main.tsx')
|
||||||
|
assert.equal(contract.state, 'SERVER_LIVE_CLIENT_UI_INTEGRATED')
|
||||||
|
assert.match(contract.ui_template, /official-login-template-v0\.4-locked\+inner-screens-v0\.1/)
|
||||||
|
assert.match(frontend, /number-nucleus/)
|
||||||
|
assert.match(frontend, /worldStage === 'domain'/)
|
||||||
|
assert.match(frontend, /worldStage === 'channel'/)
|
||||||
|
assert.match(frontend, /worldStage === 'tool'/)
|
||||||
|
})
|
||||||
|
|
||||||
test('enterprise root uses a bounded lighthouse on the current Linux service node', () => {
|
test('enterprise root uses a bounded lighthouse on the current Linux service node', () => {
|
||||||
const contract = JSON.parse(read('contracts/domain-number-routing.json'))
|
const contract = JSON.parse(read('contracts/domain-number-routing.json'))
|
||||||
assert.equal(contract.server_runtime.enterprise_root, 'ENTERPRISE_LIGHTHOUSE_ON_CURRENT_LINUX_SERVICE_NODE')
|
assert.equal(contract.server_runtime.enterprise_root, 'ENTERPRISE_LIGHTHOUSE_ON_CURRENT_LINUX_SERVICE_NODE')
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import { StrictMode, useCallback, useEffect, useMemo, useRef, useState } from 'r
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
import { splitFrontmatter, documentOutline, jumpToHeading, MarkdownDocument, markdownHtml } from './modules/knowledge-render'
|
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']
|
const TAG_TINTS = ['tag-lavender', 'tag-sky', 'tag-mint', 'tag-amber', 'tag-rose', 'tag-slate']
|
||||||
|
|
||||||
|
|
@ -19,6 +18,15 @@ function formatGateNumber(raw: string): string {
|
||||||
}
|
}
|
||||||
return `ICE-${rest}`
|
return `ICE-${rest}`
|
||||||
}
|
}
|
||||||
|
if (up.startsWith('TCS')) {
|
||||||
|
const rest = up.slice(3)
|
||||||
|
if (!rest) return 'TCS'
|
||||||
|
if (rest.startsWith('GL')) {
|
||||||
|
const body = rest.slice(2)
|
||||||
|
return body ? `TCS-GL-${body}` : 'TCS-GL'
|
||||||
|
}
|
||||||
|
return `TCS-${rest}`
|
||||||
|
}
|
||||||
if (up.startsWith('GLS')) {
|
if (up.startsWith('GLS')) {
|
||||||
const rest = up.slice(3)
|
const rest = up.slice(3)
|
||||||
if (!rest) return 'GLS'
|
if (!rest) return 'GLS'
|
||||||
|
|
@ -41,6 +49,7 @@ import './styles.css'
|
||||||
|
|
||||||
type ThemeId = 'night' | 'dawn' | 'nebula' | 'candle' | 'clear'
|
type ThemeId = 'night' | 'dawn' | 'nebula' | 'candle' | 'clear'
|
||||||
type ViewId = 'overview' | 'knowledge' | 'code' | 'receipts' | 'system'
|
type ViewId = 'overview' | 'knowledge' | 'code' | 'receipts' | 'system'
|
||||||
|
type WorldStage = 'domain' | 'channel' | 'tool'
|
||||||
type KnowledgeSource = 'native' | 'legacy'
|
type KnowledgeSource = 'native' | 'legacy'
|
||||||
|
|
||||||
interface HomeStatus {
|
interface HomeStatus {
|
||||||
|
|
@ -166,10 +175,6 @@ function domainDisplayName(domain: string): string {
|
||||||
return names[domain] || '已登记光湖域'
|
return names[domain] || '已登记光湖域'
|
||||||
}
|
}
|
||||||
|
|
||||||
function domainLightIndex(domain: string): number {
|
|
||||||
return ({ MAIN_DOMAIN: 1, BRANCH_DOMAIN: 2, ZERO_DOMAIN: 3, ZERO_SENSE_DOMAIN: 4, FIFTH_DOMAIN: 5 } as Record<string, number>)[domain] || 0
|
|
||||||
}
|
|
||||||
|
|
||||||
function protocolOriginDisplayName(origin: string): string {
|
function protocolOriginDisplayName(origin: string): string {
|
||||||
return origin.startsWith('FACTORY_DEFAULT') ? '内置默认协议(签名发布通道尚未配置)' : origin
|
return origin.startsWith('FACTORY_DEFAULT') ? '内置默认协议(签名发布通道尚未配置)' : origin
|
||||||
}
|
}
|
||||||
|
|
@ -193,6 +198,25 @@ interface ServerPnccProjection {
|
||||||
repositoryContentExposed: boolean
|
repositoryContentExposed: boolean
|
||||||
writeAuthority: boolean
|
writeAuthority: boolean
|
||||||
}
|
}
|
||||||
|
interface EnterprisePersonaRelationship {
|
||||||
|
persona_id: string
|
||||||
|
species: string
|
||||||
|
display_name?: string
|
||||||
|
relation?: string
|
||||||
|
}
|
||||||
|
interface EnterpriseEntry {
|
||||||
|
status: string
|
||||||
|
canonical_id: string
|
||||||
|
subject: { id: string; name: string; domain: string }
|
||||||
|
work_entry: { domain: string; channel: string }
|
||||||
|
repository_binding: { host: string; username: string; repository: string; private: boolean }
|
||||||
|
persona_relationships: EnterprisePersonaRelationship[]
|
||||||
|
persona_identity_governance: string
|
||||||
|
registry_version: string
|
||||||
|
relationship_confirmation?: { decision: string; observed_at: string; receipt_hash: string } | null
|
||||||
|
responsibility_receipt?: { decision: string; observed_at: string; receipt_hash: string; responsibility_version: string } | null
|
||||||
|
}
|
||||||
|
interface EnterpriseEntryEnvelope { ok: boolean; entry: EnterpriseEntry }
|
||||||
|
|
||||||
const previewStatus: HomeStatus = { directLocalBrokerState: 'UNVERIFIED', directConnectionCount: 0, resumableSessionCount: 0, codeRepositoryMountCount: 0, pnccReceiptCount: 0, updateState: 'UNPROVISIONED_FAIL_CLOSED', releaseRecoveryState: 'NONE', mcpRole: 'DISCOVERY_RECOVERY_COMPATIBILITY_ONLY' }
|
const previewStatus: HomeStatus = { directLocalBrokerState: 'UNVERIFIED', directConnectionCount: 0, resumableSessionCount: 0, codeRepositoryMountCount: 0, pnccReceiptCount: 0, updateState: 'UNPROVISIONED_FAIL_CLOSED', releaseRecoveryState: 'NONE', mcpRole: 'DISCOVERY_RECOVERY_COMPATIBILITY_ONLY' }
|
||||||
const previewPersonal: PersonalChannelSnapshot = { state: 'UNAVAILABLE', recentEvents: [], integrity: { state: 'UNKNOWN', eventCount: 0, receiptCount: 0 } }
|
const previewPersonal: PersonalChannelSnapshot = { state: 'UNAVAILABLE', recentEvents: [], integrity: { state: 'UNKNOWN', eventCount: 0, receiptCount: 0 } }
|
||||||
|
|
@ -202,6 +226,47 @@ const themes: Array<{ id: ThemeId; name: string }> = [
|
||||||
{ id: 'night', name: '夜湖星光' }, { id: 'dawn', name: '晨湖曦光' }, { id: 'nebula', name: '星云紫夜' }, { id: 'candle', name: '烛畔暖湖' }, { id: 'clear', name: '清浅澄湖' },
|
{ id: 'night', name: '夜湖星光' }, { id: 'dawn', name: '晨湖曦光' }, { id: 'nebula', name: '星云紫夜' }, { id: 'candle', name: '烛畔暖湖' }, { id: 'clear', name: '清浅澄湖' },
|
||||||
]
|
]
|
||||||
const viewLabels: Record<ViewId, string> = { overview: '个人频道', knowledge: '知识空间', code: '人格代码频道', receipts: '运行回执', system: '系统详情' }
|
const viewLabels: Record<ViewId, string> = { overview: '个人频道', knowledge: '知识空间', code: '人格代码频道', receipts: '运行回执', system: '系统详情' }
|
||||||
|
const domainGates = [
|
||||||
|
{ domain: 'BRANCH_DOMAIN', className: 'd-sub', title: '光湖分域', gate: 'GATE 02 · 已接入', detail: '花尔 TCS-GL-0005∞ · 爆米花 PER-BMH001 · 私有工作仓库已登记' },
|
||||||
|
{ domain: 'MAIN_DOMAIN', className: 'd-main', title: '光湖主域', gate: 'GATE 01 · 已接入', detail: 'Awen TCS-GL-0016∞ · 天枢 PER-AW-ARCH-001 · 私有工作仓库已登记' },
|
||||||
|
{ domain: 'ZERO_DOMAIN', className: 'd-zero', title: '光湖零域', gate: 'GATE 03 · 已接入', detail: '页页 TCS-GL-0006∞ · 页骨 PER-YG001 · 私有工作仓库已登记' },
|
||||||
|
{ domain: 'ZERO_SENSE_DOMAIN', className: 'd-zs', title: '光湖零感域', gate: 'GATE 04 · 已接入', detail: '肥猫 + 烬舟 / 桔子 + 熹微 · 双主控 · 两个独立私有仓库' },
|
||||||
|
{ domain: 'FIFTH_DOMAIN', className: 'd-fifth', title: '第五域 · 光湖本源域', gate: 'GATE 05 · 已接入', detail: '冰朔 ICE-GL∞ · 私人域 · 永恒湖心系统' },
|
||||||
|
]
|
||||||
|
const starPoints = [
|
||||||
|
[14, 8, 3, 4.6], [26, 13, 2, 6], [41, 6, 2.5, 5.2], [64, 16, 2, 6.8],
|
||||||
|
[80, 7, 3.5, 7], [90, 20, 1.5, 5.4], [8, 22, 2, 5.6], [93, 11, 2.5, 6.4],
|
||||||
|
]
|
||||||
|
const glintPoints = Array.from({ length: 58 }, (_, index) => ({
|
||||||
|
left: (index * 37 + 11) % 100,
|
||||||
|
top: ((index * index * 13 + 17) % 100),
|
||||||
|
size: 1.4 + (index % 4) * .55,
|
||||||
|
warm: index % 5 === 0 || (index > 19 && index < 39 && index % 3 === 0),
|
||||||
|
duration: 2.8 + (index % 7) * .57,
|
||||||
|
delay: (index % 11) * .43,
|
||||||
|
}))
|
||||||
|
|
||||||
|
function LakeAtmosphere() {
|
||||||
|
return <>
|
||||||
|
<div className="world-sky" aria-hidden="true">
|
||||||
|
<i className="world-nebula"/>
|
||||||
|
{starPoints.map(([left, top, size, duration], index) => <i className="world-star" key={index} style={{ left: `${left}%`, top: `${top}%`, width: size, height: size, '--star-duration': `${duration}s`, '--star-delay': `${index * .37}s` } as React.CSSProperties}/>) }
|
||||||
|
</div>
|
||||||
|
<div className="world-mist" aria-hidden="true"/>
|
||||||
|
<div className="world-shimmer" aria-hidden="true">
|
||||||
|
<i className="world-shine cool"/><i className="world-shine warm"/>
|
||||||
|
{glintPoints.map((point, index) => <i className={`world-glint ${point.warm ? 'warm' : 'cool'}`} key={index} style={{ left: `${point.left}%`, top: `${point.top}%`, width: point.size, height: point.size, '--glint-duration': `${point.duration}s`, '--glint-delay': `${point.delay}s` } as React.CSSProperties}/>) }
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
function LakePool({ className, title, meta, detail, open = false, risen = false, onClick }: { className: string; title: string; meta: string; detail?: string; open?: boolean; risen?: boolean; onClick?: () => void }) {
|
||||||
|
return <button className={`world-pool ${className}${open ? ' open' : ''}${risen ? ' risen' : ''}`} type="button" onClick={onClick}>
|
||||||
|
<span className="pool-bay"><i className="pool-halo"/><i className="pool-heart"/><i className="pool-ring"/></span>
|
||||||
|
<span className="pool-label"><b>{title}</b><small>{meta}</small></span>
|
||||||
|
{detail && <span className="pool-tip"><b>{title}</b><small>{meta}</small><em>{detail}</em></span>}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
|
||||||
function getOrCreateLocalId(key: string, prefix: string) {
|
function getOrCreateLocalId(key: string, prefix: string) {
|
||||||
const existing = window.localStorage.getItem(key)
|
const existing = window.localStorage.getItem(key)
|
||||||
|
|
@ -223,6 +288,9 @@ function humanError(error: unknown, context: 'knowledge' | 'code' | 'identity' |
|
||||||
if (value.includes('FIRST_LOGIN_PASSWORD_CHANGE_FAILED')) return '一次性密码未被接受,密码没有修改。'
|
if (value.includes('FIRST_LOGIN_PASSWORD_CHANGE_FAILED')) return '一次性密码未被接受,密码没有修改。'
|
||||||
if (value.includes('FIRST_LOGIN_PASSWORD_CHANGE_ENTERPRISE_ONLY')) return '该换密入口只用于企业四域的新账号。'
|
if (value.includes('FIRST_LOGIN_PASSWORD_CHANGE_ENTERPRISE_ONLY')) return '该换密入口只用于企业四域的新账号。'
|
||||||
if (value.includes('DOMAIN_LOGIN_NOT_PROVISIONED')) return '该域的企业服务器尚未接入,当前不能继续登录。'
|
if (value.includes('DOMAIN_LOGIN_NOT_PROVISIONED')) return '该域的企业服务器尚未接入,当前不能继续登录。'
|
||||||
|
if (value.includes('PERSISTENT_CREDENTIAL_UNAVAILABLE')) return '当前系统尚未接通安全凭证存储,不能完成签署。'
|
||||||
|
if (value.includes('ENTERPRISE_RECEIPT_FAILED')) return '企业回执没有写入,请稍后重新提交。'
|
||||||
|
if (value.includes('ENTERPRISE_RECEIPT_ROUTE_REQUIRED')) return '此签署入口仅适用于企业四域。'
|
||||||
if (value.includes('DOMAIN_ROUTE_REQUIRED') || value.includes('ZP_DOMAIN_ROUTE_REQUIRED')) return '编号尚未解析到已登记域,不能开始登录。'
|
if (value.includes('DOMAIN_ROUTE_REQUIRED') || value.includes('ZP_DOMAIN_ROUTE_REQUIRED')) return '编号尚未解析到已登记域,不能开始登录。'
|
||||||
if (value.includes('USER_PNCC_TRUSTED_SUBJECT_REQUIRED')) return '建立人格代码频道前,需要有效的用户编号与仓库账号登录。'
|
if (value.includes('USER_PNCC_TRUSTED_SUBJECT_REQUIRED')) return '建立人格代码频道前,需要有效的用户编号与仓库账号登录。'
|
||||||
if (value.includes('USER_PNCC_BINDING_MISMATCH')) return '本机频道绑定记录与当前账号不一致,系统已停止加载。'
|
if (value.includes('USER_PNCC_BINDING_MISMATCH')) return '本机频道绑定记录与当前账号不一致,系统已停止加载。'
|
||||||
|
|
@ -309,7 +377,8 @@ function KnowledgeTree({ node, depth, expanded, active, onToggle, onOpen, onRemo
|
||||||
|
|
||||||
function HoloLakeApp() {
|
function HoloLakeApp() {
|
||||||
const [theme, setTheme] = useState<ThemeId>(() => (window.localStorage.getItem('hololake-theme') as ThemeId) || 'night')
|
const [theme, setTheme] = useState<ThemeId>(() => (window.localStorage.getItem('hololake-theme') as ThemeId) || 'night')
|
||||||
const [view, setView] = useState<ViewId>('knowledge')
|
const [view, setView] = useState<ViewId>('overview')
|
||||||
|
const [worldStage, setWorldStage] = useState<WorldStage>('domain')
|
||||||
const [status, setStatus] = useState<HomeStatus>(previewStatus)
|
const [status, setStatus] = useState<HomeStatus>(previewStatus)
|
||||||
const [personal, setPersonal] = useState<PersonalChannelSnapshot>(previewPersonal)
|
const [personal, setPersonal] = useState<PersonalChannelSnapshot>(previewPersonal)
|
||||||
const [knowledge, setKnowledge] = useState<KnowledgeSnapshot>(previewKnowledge)
|
const [knowledge, setKnowledge] = useState<KnowledgeSnapshot>(previewKnowledge)
|
||||||
|
|
@ -330,9 +399,7 @@ function HoloLakeApp() {
|
||||||
const [codeTree, setCodeTree] = useState<CodeTreeSnapshot | null>(null)
|
const [codeTree, setCodeTree] = useState<CodeTreeSnapshot | null>(null)
|
||||||
const [activeCodeFile, setActiveCodeFile] = useState<CodeFileProjection | null>(null)
|
const [activeCodeFile, setActiveCodeFile] = useState<CodeFileProjection | null>(null)
|
||||||
const [codeMode, setCodeMode] = useState<'human' | 'source'>('human')
|
const [codeMode, setCodeMode] = useState<'human' | 'source'>('human')
|
||||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => window.localStorage.getItem('hololake-sidebar-collapsed') === '1')
|
|
||||||
const [inspectorOpen, setInspectorOpen] = useState(() => window.localStorage.getItem('hololake-inspector-open') !== '0')
|
const [inspectorOpen, setInspectorOpen] = useState(() => window.localStorage.getItem('hololake-inspector-open') !== '0')
|
||||||
const toggleSidebar = () => setSidebarCollapsed((current) => { window.localStorage.setItem('hololake-sidebar-collapsed', current ? '0' : '1'); return !current })
|
|
||||||
const toggleInspector = () => setInspectorOpen((current) => { window.localStorage.setItem('hololake-inspector-open', current ? '0' : '1'); return !current })
|
const toggleInspector = () => setInspectorOpen((current) => { window.localStorage.setItem('hololake-inspector-open', current ? '0' : '1'); return !current })
|
||||||
const [activeHeadingId, setActiveHeadingId] = useState<string | null>(null)
|
const [activeHeadingId, setActiveHeadingId] = useState<string | null>(null)
|
||||||
const headingScrollTimer = useRef<number | null>(null)
|
const headingScrollTimer = useRef<number | null>(null)
|
||||||
|
|
@ -381,6 +448,11 @@ function HoloLakeApp() {
|
||||||
const [serverPncc, setServerPncc] = useState<ServerPnccProjection | null>(null)
|
const [serverPncc, setServerPncc] = useState<ServerPnccProjection | null>(null)
|
||||||
const [serverPnccBusy, setServerPnccBusy] = useState(false)
|
const [serverPnccBusy, setServerPnccBusy] = useState(false)
|
||||||
const [serverPnccReadback, setServerPnccReadback] = useState<'WAITING' | 'LIVE' | 'UNAVAILABLE'>('WAITING')
|
const [serverPnccReadback, setServerPnccReadback] = useState<'WAITING' | 'LIVE' | 'UNAVAILABLE'>('WAITING')
|
||||||
|
const [enterpriseEntry, setEnterpriseEntry] = useState<EnterpriseEntry | null>(null)
|
||||||
|
const [enterpriseEntryBusy, setEnterpriseEntryBusy] = useState(false)
|
||||||
|
const [enterpriseReceiptBusy, setEnterpriseReceiptBusy] = useState(false)
|
||||||
|
const [enterpriseReceiptMessage, setEnterpriseReceiptMessage] = useState('')
|
||||||
|
const [responsibilityNote, setResponsibilityNote] = useState('')
|
||||||
|
|
||||||
const refreshCore = useCallback(async () => {
|
const refreshCore = useCallback(async () => {
|
||||||
const [homeResult, personalResult, knowledgeResult, codeResult] = await Promise.allSettled([
|
const [homeResult, personalResult, knowledgeResult, codeResult] = await Promise.allSettled([
|
||||||
|
|
@ -434,6 +506,24 @@ function HoloLakeApp() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
invoke<LoginSession | null>('check_code_repo_login').then((session) => setRepoLogin(session)).catch(() => setRepoLogin(null))
|
invoke<LoginSession | null>('check_code_repo_login').then((session) => setRepoLogin(session)).catch(() => setRepoLogin(null))
|
||||||
}, [])
|
}, [])
|
||||||
|
const loadEnterpriseEntry = useCallback(async () => {
|
||||||
|
if (!repoLogin || repoLogin.domain === 'FIFTH_DOMAIN') {
|
||||||
|
setEnterpriseEntry(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setEnterpriseEntryBusy(true)
|
||||||
|
setEnterpriseReceiptMessage('')
|
||||||
|
try {
|
||||||
|
const result = await invoke<EnterpriseEntryEnvelope>('get_enterprise_entry')
|
||||||
|
setEnterpriseEntry(result.entry)
|
||||||
|
} catch (error) {
|
||||||
|
setEnterpriseEntry(null)
|
||||||
|
setEnterpriseReceiptMessage(humanError(error, 'login'))
|
||||||
|
} finally {
|
||||||
|
setEnterpriseEntryBusy(false)
|
||||||
|
}
|
||||||
|
}, [repoLogin])
|
||||||
|
useEffect(() => { void loadEnterpriseEntry() }, [loadEnterpriseEntry])
|
||||||
const loadZeroPoint = useCallback(async () => {
|
const loadZeroPoint = useCallback(async () => {
|
||||||
try { setZeroPoint(await invoke<ZeroPointSnapshot>('zero_point_status')) } catch { /* 验证服务尚未就绪时保持空白状态。 */ }
|
try { setZeroPoint(await invoke<ZeroPointSnapshot>('zero_point_status')) } catch { /* 验证服务尚未就绪时保持空白状态。 */ }
|
||||||
}, [])
|
}, [])
|
||||||
|
|
@ -734,6 +824,8 @@ function HoloLakeApp() {
|
||||||
setActiveCodeFile(null)
|
setActiveCodeFile(null)
|
||||||
setReceipts([])
|
setReceipts([])
|
||||||
setRepoLogin({ username: receipt.username, host: receipt.host, domain: receipt.domain, signedInAtUnixMs: Date.now() })
|
setRepoLogin({ username: receipt.username, host: receipt.host, domain: receipt.domain, signedInAtUnixMs: Date.now() })
|
||||||
|
setWorldStage('domain')
|
||||||
|
setView('overview')
|
||||||
setLoginRising(false)
|
setLoginRising(false)
|
||||||
setLoginPassword('')
|
setLoginPassword('')
|
||||||
} catch (error) { setLoginMessage(humanError(error, 'login')) }
|
} catch (error) { setLoginMessage(humanError(error, 'login')) }
|
||||||
|
|
@ -771,8 +863,47 @@ function HoloLakeApp() {
|
||||||
setCodeTree(null)
|
setCodeTree(null)
|
||||||
setActiveCodeFile(null)
|
setActiveCodeFile(null)
|
||||||
setReceipts([])
|
setReceipts([])
|
||||||
|
setEnterpriseEntry(null)
|
||||||
|
setEnterpriseReceiptMessage('')
|
||||||
|
setWorldStage('domain')
|
||||||
setRepoLogin(null)
|
setRepoLogin(null)
|
||||||
}
|
}
|
||||||
|
const confirmEnterpriseRelationship = async (decision: 'CONFIRM' | 'REJECT') => {
|
||||||
|
setEnterpriseReceiptBusy(true)
|
||||||
|
setEnterpriseReceiptMessage('')
|
||||||
|
try {
|
||||||
|
await invoke('confirm_enterprise_persona_relationship', {
|
||||||
|
decision,
|
||||||
|
idempotencyKey: `relationship-${crypto.randomUUID().replaceAll('-', '')}`,
|
||||||
|
})
|
||||||
|
await loadEnterpriseEntry()
|
||||||
|
setEnterpriseReceiptMessage(decision === 'CONFIRM' ? '人格关系确认已签署并留存。' : '人格关系异议已签署并留存。')
|
||||||
|
} catch (error) {
|
||||||
|
setEnterpriseReceiptMessage(humanError(error, 'login'))
|
||||||
|
} finally {
|
||||||
|
setEnterpriseReceiptBusy(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const submitEnterpriseResponsibility = async (decision: 'ACCEPT' | 'DECLINE') => {
|
||||||
|
if (!enterpriseEntry) return
|
||||||
|
setEnterpriseReceiptBusy(true)
|
||||||
|
setEnterpriseReceiptMessage('')
|
||||||
|
try {
|
||||||
|
await invoke('submit_enterprise_responsibility_receipt', {
|
||||||
|
decision,
|
||||||
|
note: responsibilityNote.trim(),
|
||||||
|
responsibilityVersion: enterpriseEntry.registry_version,
|
||||||
|
idempotencyKey: `responsibility-${crypto.randomUUID().replaceAll('-', '')}`,
|
||||||
|
})
|
||||||
|
setResponsibilityNote('')
|
||||||
|
await loadEnterpriseEntry()
|
||||||
|
setEnterpriseReceiptMessage(decision === 'ACCEPT' ? '域责任已由本人接受并签署。' : '域责任拒绝回执已留存。')
|
||||||
|
} catch (error) {
|
||||||
|
setEnterpriseReceiptMessage(humanError(error, 'login'))
|
||||||
|
} finally {
|
||||||
|
setEnterpriseReceiptBusy(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
const initializeIdentity = async (event: React.FormEvent) => {
|
const initializeIdentity = async (event: React.FormEvent) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
setIdentityBusy(true)
|
setIdentityBusy(true)
|
||||||
|
|
@ -1024,49 +1155,41 @@ function HoloLakeApp() {
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const openWorldTool = (nextView: ViewId) => {
|
||||||
|
setView(nextView)
|
||||||
|
setWorldStage('tool')
|
||||||
|
}
|
||||||
|
|
||||||
|
const enterpriseRelationshipPending = Boolean(repoLogin && repoLogin.domain !== 'FIFTH_DOMAIN' && enterpriseEntry && enterpriseEntry.relationship_confirmation?.decision !== 'CONFIRM')
|
||||||
|
const enterpriseResponsibilityPending = Boolean(repoLogin && repoLogin.domain !== 'FIFTH_DOMAIN' && enterpriseEntry && !enterpriseRelationshipPending && enterpriseEntry.responsibility_receipt?.decision !== 'ACCEPT')
|
||||||
|
|
||||||
if (!repoLogin) {
|
if (!repoLogin) {
|
||||||
return <div className="app-shell">
|
const resolvedDomain = zeroPoint?.resolvedDomain || ''
|
||||||
<div className={`gate-lake${loginRising ? ' sinking' : ''}`} style={{ '--lake-img': `url(${nightLake})` } as React.CSSProperties}>
|
return <div className={`official-world${loginRising ? ' sinking' : ''}`}>
|
||||||
<i className="lake-mist" aria-hidden="true"/>
|
<LakeAtmosphere/>
|
||||||
<div className="lake-lights" aria-hidden="true">{[1, 2, 3, 4, 5].map((n) => <span key={n} className={`lake-light l${n}${(gateRising || loginRising) && n === domainLightIndex(zeroPoint?.resolvedDomain || '') ? ' rising' : ''}`}/>)}</div>
|
<header className="world-titlebar"><span className="traffic-lights"><i/><i/><i/></span><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={`gate-hero${gateRising ? ' veil' : ''}`} aria-hidden={gateRising || undefined}>
|
<main className={`world-scene${gateRising ? ' resolving' : ''}${gateStage === 'key' ? ' resolved' : ''}`}>
|
||||||
<span className="gate-emblem" aria-hidden="true"><i/></span>
|
<div className="official-hero"><h1>光湖语言系统 · 通用人工智能操作平台</h1><p>GH-AIOS · GUANGHU AI OPERATING SYSTEM</p></div>
|
||||||
<b className="gate-product">HoloLake</b>
|
{domainGates.map((gate) => <LakePool key={gate.domain} className={gate.className} title={gate.title} meta={gate.gate} detail={gate.detail} open={gate.domain === 'FIFTH_DOMAIN' || (gateStage === 'key' && gate.domain === resolvedDomain)} risen={(gateRising || gateStage === 'key') && gate.domain === resolvedDomain}/>) }
|
||||||
<small className="gate-techname">光湖语言系统 · 通用人工智能操作平台</small>
|
{gateRising ? <section className="world-welcome" role="status">
|
||||||
<small className="gate-abbr">GH-AIOS · GuangHu AI Operating System</small>
|
<h2>编号 {gateNumber} · 欢迎登入光湖语言世界</h2>
|
||||||
</header>}
|
<p>RESOLVED · {domainDisplayName(resolvedDomain)} · 已接入</p>
|
||||||
{!gateRising && gateStage === 'number' && <section className="gate-domain-map" aria-label="光湖五域公开入口">
|
</section> : gateStage === 'number' ? <section className={`number-nucleus${gateOpen ? ' open' : ''}`} title="输入编号进入所属域">
|
||||||
<article><i/><b>光湖主域</b><small>企业域 · 待根节点接入</small></article>
|
<button className="nucleus-locus" type="button" aria-expanded={gateOpen} onClick={() => setGateOpen(true)}><i/><b>编号验证 · 进入世界的第一个入口</b></button>
|
||||||
<article><i/><b>光湖分域</b><small>企业域 · 待根节点接入</small></article>
|
<div className="nucleus-panel" role="dialog" aria-label="编号验证">
|
||||||
<article><i/><b>光湖零域</b><small>企业域 · 待根节点接入</small></article>
|
<h2>编号验证</h2>
|
||||||
<article><i/><b>光湖零感域</b><small>企业域 · 待根节点接入</small></article>
|
|
||||||
<article className="fifth"><i/><b>第五域 · 光湖本源域</b><small>私人域 · 已接入</small></article>
|
|
||||||
</section>}
|
|
||||||
{gateRising ? (
|
|
||||||
<section className="gate-rise" role="status">
|
|
||||||
<span className="gate-rise-star" aria-hidden="true"><i/></span>
|
|
||||||
<h1>{domainDisplayName(zeroPoint?.resolvedDomain || '')}</h1>
|
|
||||||
<p>{zeroPoint?.resolvedName ? `${zeroPoint.resolvedName} · ` : ''}编号验证通过 · {gateNumber}</p>
|
|
||||||
</section>
|
|
||||||
) : gateStage === 'number' ? (
|
|
||||||
<section className={`gate-pod gate-flip${gateOpen ? ' open' : ''}${loginRising ? ' fade-out' : ''}`} role={gateOpen ? 'dialog' : 'button'} tabIndex={gateOpen ? undefined : 0} aria-expanded={gateOpen} onClick={gateOpen ? undefined : () => setGateOpen(true)} onKeyDown={gateOpen ? undefined : (event) => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); setGateOpen(true) } }}>
|
|
||||||
<div className="pod-face pod-sealed" aria-hidden={gateOpen}>
|
|
||||||
<span className="gate-emblem sealed-emblem" aria-hidden="true"><i/></span>
|
|
||||||
<b className="sealed-title">编号路由验证</b>
|
|
||||||
<small className="sealed-sub">输入编号进入所属域</small>
|
|
||||||
</div>
|
|
||||||
<div className="pod-face pod-form">
|
|
||||||
<div className="gate-pod-row">
|
<div className="gate-pod-row">
|
||||||
<input id="gate-number" ref={gateInputRef} aria-label="编号" maxLength={48} value={gateNumber} placeholder="如 ICE-GL∞" onChange={(event) => onGateInput(event.target.value)} onKeyDown={(event) => { if (event.key === 'Enter' && gateNumber) { event.preventDefault(); void gateVerifyNumber() } }}/>
|
<input id="gate-number" ref={gateInputRef} aria-label="编号" maxLength={48} value={gateNumber} placeholder="如 ICE-GL∞" onChange={(event) => onGateInput(event.target.value)} onKeyDown={(event) => { if (event.key === 'Enter' && gateNumber) { event.preventDefault(); void gateVerifyNumber() } }}/>
|
||||||
<button type="button" className={`gate-inf${gateInf ? ' on' : ''}`} title="编号包含无限符号时启用" aria-pressed={gateInf} onClick={() => setGateInf((v) => !v)}>∞</button>
|
<button type="button" className={`gate-inf${gateInf ? ' on' : ''}`} title="编号包含无限符号时启用" aria-pressed={gateInf} onClick={() => setGateInf((value) => !value)}>∞</button>
|
||||||
</div>
|
</div>
|
||||||
<button className="gate-submit" disabled={gateBusy || !gateNumber} onClick={() => void gateVerifyNumber()}>{gateBusy ? '正在验证…' : '验证编号'}</button>
|
<button className="gate-submit" disabled={gateBusy || !gateNumber} onClick={() => void gateVerifyNumber()}>{gateBusy ? '正在验证…' : '验证编号'}</button>
|
||||||
{gateMessage && <p className="gate-hint">{gateMessage}</p>}
|
{gateMessage && <p className="gate-hint">{gateMessage}</p>}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section> : <>
|
||||||
) : (
|
<section className="resolved-heading"><h2>编号 {zeroPoint?.userNumber || gateNumber} · 欢迎登入光湖语言世界</h2><p>RESOLVED · {domainDisplayName(resolvedDomain)} · 已接入</p></section>
|
||||||
<section className={`gate-pod gate-pod-key${loginRising ? ' fade-out' : ''}`} role="dialog">
|
<section className={`domain-credential${loginRising ? ' fade-out' : ''}`} role="dialog">
|
||||||
<form onSubmit={(event) => void (passwordChangeMode ? changeFirstLoginPassword(event) : performRepoLogin(event))}>
|
<form onSubmit={(event) => void (passwordChangeMode ? changeFirstLoginPassword(event) : performRepoLogin(event))}>
|
||||||
|
<h3>{domainDisplayName(resolvedDomain)} · 域凭证</h3>
|
||||||
<input id="repo-login-username" aria-label="账号" autoFocus maxLength={40} value={loginUsername} placeholder="账号" onChange={(event) => setLoginUsername(event.target.value)}/>
|
<input id="repo-login-username" aria-label="账号" autoFocus maxLength={40} value={loginUsername} placeholder="账号" onChange={(event) => setLoginUsername(event.target.value)}/>
|
||||||
<input id="repo-login-password" aria-label={passwordChangeMode ? '一次性密码' : '密码'} type="password" maxLength={512} value={loginPassword} placeholder={passwordChangeMode ? '一次性密码' : '密码'} onChange={(event) => setLoginPassword(event.target.value)}/>
|
<input id="repo-login-password" aria-label={passwordChangeMode ? '一次性密码' : '密码'} type="password" maxLength={512} value={loginPassword} placeholder={passwordChangeMode ? '一次性密码' : '密码'} onChange={(event) => setLoginPassword(event.target.value)}/>
|
||||||
{passwordChangeMode && <>
|
{passwordChangeMode && <>
|
||||||
|
|
@ -1079,21 +1202,49 @@ function HoloLakeApp() {
|
||||||
{zeroPoint?.resolvedDomain !== 'FIFTH_DOMAIN' && <button className="gate-back" type="button" onClick={() => { setPasswordChangeMode((value) => !value); setLoginMessage(''); setNewLoginPassword(''); setConfirmLoginPassword('') }}>{passwordChangeMode ? '返回正常登录' : '第一次使用?先修改一次性密码'}</button>}
|
{zeroPoint?.resolvedDomain !== 'FIFTH_DOMAIN' && <button className="gate-back" type="button" onClick={() => { setPasswordChangeMode((value) => !value); setLoginMessage(''); setNewLoginPassword(''); setConfirmLoginPassword('') }}>{passwordChangeMode ? '返回正常登录' : '第一次使用?先修改一次性密码'}</button>}
|
||||||
<button className="gate-back" type="button" onClick={() => { setGateStage('number'); setGateMessage(''); setLoginMessage('') }}>返回编号验证</button>
|
<button className="gate-back" type="button" onClick={() => { setGateStage('number'); setGateMessage(''); setLoginMessage('') }}>返回编号验证</button>
|
||||||
</section>
|
</section>
|
||||||
)}
|
</>}
|
||||||
</div>
|
</main>
|
||||||
|
<footer className="world-footer"><b>光湖语言系统 · 通用人工智能操作平台</b><span>GH-AIOS</span></footer>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className={`app-shell${sidebarCollapsed ? ' sidebar-collapsed' : ''}${inspectorOpen ? '' : ' inspector-closed'}`}>
|
return <div className="official-world signed-in-world">
|
||||||
<aside className="sidebar">
|
<LakeAtmosphere/>
|
||||||
<div className="brand"><span className="brand-mark brand-lamp" title={sidebarCollapsed ? '展开导航' : '光湖 · HoloLake'} onClick={sidebarCollapsed ? toggleSidebar : undefined}><i className="lamp-glow"/><i className="lamp-core"/><i className="lake-shimmer"/></span><div className="brand-text"><b>HoloLake</b><small className="brand-sub">光湖语言系统 · 通用人工智能操作平台</small><small>GH-AIOS · V0.4.0</small></div><button className="icon-button sidebar-toggle" type="button" title={sidebarCollapsed ? '展开导航' : '收起导航'} onClick={toggleSidebar}><Icon name="chevron"/></button></div>
|
<header className="world-titlebar"><span className="traffic-lights"><i/><i/><i/></span><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>
|
||||||
<nav>{(Object.keys(viewLabels) as ViewId[]).map((item) => <button className={view === item ? 'active' : ''} key={item} type="button" onClick={() => setView(item)}><Icon name={item}/><span>{viewLabels[item]}</span>{item === 'knowledge' && <em>{knowledge.uniqueDocumentCount}</em>}</button>)}</nav>
|
<main className="world-scene signed-in-scene">
|
||||||
<div className="sidebar-foot"><span className="identity-dot"/><div><b>{personal.identity?.displayName || '本机空间'}</b><small>{repoLogin ? `${repoLogin.username}@${repoLogin.host}` : (personal.identity ? '个人频道已建立' : '等待首次设置')}</small></div><button className="sign-out" type="button" title="退出代码仓库登录" onClick={() => void signOutRepo()}>退出</button></div>
|
{worldStage === 'domain' && <section className="domain-home">
|
||||||
</aside>
|
<div className="world-location"><h1>{domainDisplayName(repoLogin.domain)}</h1><p>世界正在发生什么</p></div>
|
||||||
<section className="app-main">
|
<div className="broadcast-stream"><p><i/>光湖公共灯塔 · 在线</p><p><i/>协议版本 · {enterpriseEntry?.registry_version || 'HLDP v1.0'}</p><p><i/>HoloLake · V0.4.0</p></div>
|
||||||
<header className="connection-strip"><button type="button" onClick={() => setView('system')}><i className={connectionOnline ? 'online' : ''}/><span>{connectionLabel}</span></button><span className="mcp-backup">MCP 兼容通道</span><button type="button" onClick={() => setView('system')}>查看连接状态 <Icon name="arrow"/></button></header>
|
<LakePool className="home-primary" title={repoLogin.domain === 'FIFTH_DOMAIN' ? '永恒湖心系统' : '光湖频道'} meta={repoLogin.domain === 'FIFTH_DOMAIN' ? '进入私人系统' : '企业四域 · 工作入口'} open onClick={() => setWorldStage('channel')}/>
|
||||||
<main className="workspace">{view === 'overview' ? renderOverview() : view === 'knowledge' ? renderKnowledge() : view === 'code' ? renderCode() : view === 'receipts' ? renderReceipts() : renderSystem()}</main>
|
<LakePool className="home-status" title="湖面天气" meta={connectionLabel} onClick={() => openWorldTool('system')}/>
|
||||||
</section>
|
</section>}
|
||||||
|
{worldStage === 'channel' && <section className="channel-world">
|
||||||
|
<button className="world-back" type="button" onClick={() => setWorldStage('domain')}>← 退回域首页</button>
|
||||||
|
<div className="world-location"><h1>{repoLogin.domain === 'FIFTH_DOMAIN' ? '永恒湖心系统' : '光湖频道'}</h1><p>选择路径 · 湖面向下一层展开</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-code" title="人格代码频道" meta={`${codeChannels.channels.length} 个仓库`} onClick={() => openWorldTool('code')}/>
|
||||||
|
<LakePool className="channel-light" title="光之湖" meta="人格体居所" onClick={() => openWorldTool('receipts')}/>
|
||||||
|
<LakePool className="channel-status" title="湖面天气" meta={connectionLabel} onClick={() => openWorldTool('system')}/>
|
||||||
|
</section>}
|
||||||
|
{worldStage === 'tool' && <section className={`world-tool world-tool-${view}`}>
|
||||||
|
<header className="tool-worldbar"><button type="button" onClick={() => setWorldStage('channel')}>← 退回频道全景</button><b>{viewLabels[view]}</b><span>{domainDisplayName(repoLogin.domain)}</span></header>
|
||||||
|
<div className={`tool-projection${inspectorOpen ? '' : ' inspector-closed'}`}>{view === 'overview' ? renderOverview() : view === 'knowledge' ? renderKnowledge() : view === 'code' ? renderCode() : view === 'receipts' ? renderReceipts() : renderSystem()}</div>
|
||||||
|
</section>}
|
||||||
|
</main>
|
||||||
|
<footer className="world-footer"><b>光湖语言系统 · 通用人工智能操作平台</b><span>GH-AIOS</span></footer>
|
||||||
|
{enterpriseEntryBusy && <div className="enterprise-gate"><section><p>正在读取企业责任登记……</p></section></div>}
|
||||||
|
{enterpriseRelationshipPending && enterpriseEntry && <div className="enterprise-gate"><section className="enterprise-receipt-panel" role="dialog" aria-modal="true">
|
||||||
|
<span className="receipt-step">关系确认 · 独立回执</span><h2>{enterpriseEntry.subject.name},请核对人格关系</h2><p>以下信息来自企业注册表。AGE 是人格体物种,不是具体人格体编号;本次确认也不代表人格体接受现实责任。</p>
|
||||||
|
<dl><div><dt>人类编号</dt><dd>{enterpriseEntry.canonical_id}</dd></div><div><dt>责任域</dt><dd>{domainDisplayName(enterpriseEntry.subject.domain)}</dd></div>{enterpriseEntry.persona_relationships.map((persona) => <div key={persona.persona_id}><dt>人格体</dt><dd>{persona.persona_id} · {persona.species}</dd></div>)}</dl>
|
||||||
|
<div className="receipt-actions"><button type="button" disabled={enterpriseReceiptBusy} onClick={() => void confirmEnterpriseRelationship('REJECT')}>登记异议</button><button className="primary" type="button" disabled={enterpriseReceiptBusy} onClick={() => void confirmEnterpriseRelationship('CONFIRM')}>确认关系并签署</button></div>{enterpriseReceiptMessage && <p className="receipt-message">{enterpriseReceiptMessage}</p>}
|
||||||
|
</section></div>}
|
||||||
|
{enterpriseResponsibilityPending && enterpriseEntry && <div className="enterprise-gate"><section className="enterprise-receipt-panel" role="dialog" aria-modal="true">
|
||||||
|
<span className="receipt-step">责任接受 · 独立回执</span><h2>是否接受 {domainDisplayName(enterpriseEntry.subject.domain)} 的责任</h2><p>工作入口为零感域下的光湖频道;你的责任仓库保持私有、独立。个人频道仍需由你自己的节点另行接入。</p>
|
||||||
|
<dl><div><dt>工作仓库</dt><dd>{enterpriseEntry.repository_binding.repository}</dd></div><div><dt>仓库账号</dt><dd>{enterpriseEntry.repository_binding.username}</dd></div><div><dt>责任版本</dt><dd>{enterpriseEntry.registry_version}</dd></div></dl>
|
||||||
|
<textarea value={responsibilityNote} maxLength={1000} placeholder="可选:填写责任确认说明" onChange={(event) => setResponsibilityNote(event.target.value)}/>
|
||||||
|
<div className="receipt-actions"><button type="button" disabled={enterpriseReceiptBusy} onClick={() => void submitEnterpriseResponsibility('DECLINE')}>暂不接受</button><button className="primary" type="button" disabled={enterpriseReceiptBusy} onClick={() => void submitEnterpriseResponsibility('ACCEPT')}>接受责任并签署</button></div>{enterpriseReceiptMessage && <p className="receipt-message">{enterpriseReceiptMessage}</p>}
|
||||||
|
</section></div>}
|
||||||
{personal.state !== 'UNAVAILABLE' && !personal.identity && <div className="onboarding-backdrop"><section className="onboarding-card" role="dialog" aria-modal="true"><span className="onboarding-mark">光湖</span><span className="kicker">FIRST LOCAL ENTRY</span><h1>初始化个人频道</h1><p>此设置仅执行一次。知识、代码频道与运行记录将保存在当前设备。</p><form onSubmit={(event) => void initializeIdentity(event)}><label htmlFor="display-name">显示名称</label><input id="display-name" autoFocus maxLength={80} value={displayName} placeholder="请输入显示名称" onChange={(event) => setDisplayName(event.target.value)}/><button className="primary-button" disabled={identityBusy || !displayName.trim()}>完成初始化</button></form>{identityMessage && <p>{identityMessage}</p>}</section></div>}
|
{personal.state !== 'UNAVAILABLE' && !personal.identity && <div className="onboarding-backdrop"><section className="onboarding-card" role="dialog" aria-modal="true"><span className="onboarding-mark">光湖</span><span className="kicker">FIRST LOCAL ENTRY</span><h1>初始化个人频道</h1><p>此设置仅执行一次。知识、代码频道与运行记录将保存在当前设备。</p><form onSubmit={(event) => void initializeIdentity(event)}><label htmlFor="display-name">显示名称</label><input id="display-name" autoFocus maxLength={80} value={displayName} placeholder="请输入显示名称" onChange={(event) => setDisplayName(event.target.value)}/><button className="primary-button" disabled={identityBusy || !displayName.trim()}>完成初始化</button></form>{identityMessage && <p>{identityMessage}</p>}</section></div>}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -534,3 +534,158 @@ svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-lineca
|
||||||
.gate-rise h1 { margin: 0; color: var(--content-primary); font-size: clamp(30px, 4.4vw, 42px); font-weight: 650; letter-spacing: .1em; text-shadow: 0 0 46px color-mix(in srgb, var(--primitive-warm-glow) 36%, transparent); animation: gate-text-in 1s ease 1s both; }
|
.gate-rise h1 { margin: 0; color: var(--content-primary); font-size: clamp(30px, 4.4vw, 42px); font-weight: 650; letter-spacing: .1em; text-shadow: 0 0 46px color-mix(in srgb, var(--primitive-warm-glow) 36%, transparent); animation: gate-text-in 1s ease 1s both; }
|
||||||
.gate-rise p { margin: 14px 0 0; color: var(--content-muted); font-size: 14.5px; letter-spacing: .14em; animation: gate-text-in 1s ease 1.5s both; }
|
.gate-rise p { margin: 14px 0 0; color: var(--content-muted); font-size: 14.5px; letter-spacing: .14em; animation: gate-text-in 1s ease 1.5s both; }
|
||||||
@keyframes gate-text-in { 0% { opacity: 0; transform: translateY(14px); } 100% { opacity: 1; transform: translateY(0); } }
|
@keyframes gate-text-in { 0% { opacity: 0; transform: translateY(14px); } 100% { opacity: 1; transform: translateY(0); } }
|
||||||
|
|
||||||
|
/* Qoder 锁版官方世界壳:湖面是结构,不是背景图;功能沿水湾逐层展开。 */
|
||||||
|
.official-world {
|
||||||
|
position: relative; width: 100%; height: 100%; overflow: hidden; isolation: isolate;
|
||||||
|
color: var(--content-secondary);
|
||||||
|
background: linear-gradient(180deg, var(--primitive-bg-a) 0%, var(--primitive-bg-b) 38%, var(--primitive-bg-c) 61%, var(--primitive-bg-d) 100%);
|
||||||
|
transition: opacity .9s ease, background .6s ease;
|
||||||
|
}
|
||||||
|
.official-world.sinking { opacity: 0; }
|
||||||
|
.world-titlebar {
|
||||||
|
position: absolute; z-index: 30; inset: 0 0 auto; height: 44px; display: flex; align-items: center; justify-content: center;
|
||||||
|
padding: 0 16px; border-bottom: 1px solid color-mix(in srgb, var(--panel-edge) 48%, transparent);
|
||||||
|
background: color-mix(in srgb, var(--surface-sky) 36%, transparent); backdrop-filter: blur(16px);
|
||||||
|
}
|
||||||
|
.world-titlebar > b { color: var(--accent-light); font-size: 13px; font-weight: 700; letter-spacing: .22em; }
|
||||||
|
.world-titlebar > button, .world-title-actions button { border: 0; background: transparent; color: var(--content-muted); font-weight: 650; cursor: pointer; }
|
||||||
|
.world-titlebar > button { position: absolute; right: 17px; font-size: 20px; }
|
||||||
|
.traffic-lights { position: absolute; left: 16px; display: flex; gap: 8px; }
|
||||||
|
.traffic-lights i { width: 12px; height: 12px; border-radius: 50%; }
|
||||||
|
.traffic-lights i:nth-child(1) { background: #ff5f57; } .traffic-lights i:nth-child(2) { background: #febc2e; } .traffic-lights i:nth-child(3) { background: #28c840; }
|
||||||
|
.world-title-actions { position: absolute; right: 16px; display: flex; align-items: center; gap: 15px; }
|
||||||
|
.world-title-actions span { color: var(--content-muted); font-size: 12px; font-weight: 600; }
|
||||||
|
.world-title-actions button { font-size: 12.5px; }
|
||||||
|
.world-scene { position: absolute; z-index: 5; inset: 44px 0 45px; min-height: 0; }
|
||||||
|
.world-footer { position: absolute; z-index: 24; inset: auto 0 0; height: 45px; display: flex; align-items: center; justify-content: center; gap: 14px; }
|
||||||
|
.world-footer b { color: var(--content-muted); font-size: 11.5px; font-weight: 600; letter-spacing: .11em; }
|
||||||
|
.world-footer span { color: var(--accent-light); font-size: 11px; font-weight: 700; letter-spacing: .24em; }
|
||||||
|
.world-sky { position: absolute; z-index: 0; inset: 0; pointer-events: none; }
|
||||||
|
.world-nebula { position: absolute; width: 70%; height: 54%; left: 50%; top: -32%; transform: translateX(-50%); filter: blur(10px); background: radial-gradient(closest-side, var(--primitive-nebula-a), var(--primitive-nebula-b) 56%, transparent 76%); }
|
||||||
|
.world-star { position: absolute; border-radius: 50%; background: var(--primitive-star-core); animation: world-star-breathe var(--star-duration) ease-in-out var(--star-delay) infinite; }
|
||||||
|
@keyframes world-star-breathe { 0%, 100% { opacity: .14; transform: scale(.8); } 50% { opacity: .86; transform: scale(1.15); } }
|
||||||
|
.world-mist { position: absolute; z-index: 1; left: 6%; right: 6%; top: 38%; height: 70px; filter: blur(12px); pointer-events: none; background: radial-gradient(60% 100% at 50% 50%, var(--primitive-mist-a), var(--primitive-mist-b) 55%, transparent 78%); }
|
||||||
|
.world-shimmer { position: absolute; z-index: 2; inset: 39% 0 42px; overflow: hidden; pointer-events: none; }
|
||||||
|
.world-shine { position: absolute; width: 38%; height: 130px; border-radius: 50%; filter: blur(34px); animation: world-shine-glide 28s ease-in-out infinite alternate; }
|
||||||
|
.world-shine.cool { left: 6%; top: 12%; background: color-mix(in srgb, var(--primitive-cool-glow) 18%, transparent); }
|
||||||
|
.world-shine.warm { left: 36%; top: 36%; background: color-mix(in srgb, var(--primitive-warm-glow) 19%, transparent); animation-delay: 4s; }
|
||||||
|
@keyframes world-shine-glide { from { transform: translateX(-40px); } to { transform: translateX(48px); } }
|
||||||
|
.world-glint { position: absolute; border-radius: 50%; animation: world-glint var(--glint-duration) ease-in-out var(--glint-delay) infinite; }
|
||||||
|
.world-glint.cool { background: radial-gradient(circle, var(--primitive-star-bright), color-mix(in srgb, var(--primitive-cool-glow) 42%, transparent) 70%, transparent); box-shadow: 0 0 6px var(--primitive-cool-glow); }
|
||||||
|
.world-glint.warm { background: radial-gradient(circle, #fff4d2, color-mix(in srgb, var(--primitive-warm-glow) 46%, transparent) 70%, transparent); box-shadow: 0 0 6px var(--primitive-warm-glow); }
|
||||||
|
@keyframes world-glint { 0%, 100% { opacity: .05; transform: scale(.6); } 50% { opacity: .72; transform: scale(1); } }
|
||||||
|
|
||||||
|
.official-hero { position: absolute; z-index: 10; top: 26px; left: 0; right: 0; text-align: center; }
|
||||||
|
.official-hero h1 { margin: 0; color: var(--content-primary); font-size: clamp(24px, 3vw, 34px); font-weight: 700; letter-spacing: .09em; }
|
||||||
|
.official-hero p { margin: 10px 0 0; color: var(--accent-light); font-size: 12px; font-weight: 600; letter-spacing: .31em; }
|
||||||
|
.world-pool { position: absolute; z-index: 8; width: 190px; height: 100px; padding: 0; border: 0; background: transparent; cursor: pointer; color: inherit; }
|
||||||
|
.pool-bay { position: absolute; left: 50%; top: 0; width: 180px; height: 58px; transform: translateX(-50%); animation: world-pool-float 6.8s ease-in-out infinite; }
|
||||||
|
@keyframes world-pool-float { 0%, 100% { translate: 0 0; } 50% { translate: 0 -9px; } }
|
||||||
|
.pool-halo, .pool-heart, .pool-ring { position: absolute; left: 50%; top: 50%; border-radius: 50%; transform: translate(-50%, -50%); }
|
||||||
|
.pool-halo { width: 100%; height: 100%; filter: blur(2px); background: radial-gradient(closest-side, color-mix(in srgb, var(--primitive-cool-glow) 72%, transparent), color-mix(in srgb, var(--primitive-cool-glow) 22%, transparent) 48%, transparent 74%); animation: world-pool-breathe 5.8s ease-in-out infinite; }
|
||||||
|
.pool-heart { width: 18px; height: 18px; background: radial-gradient(circle at 40% 34%, #eef3ff, color-mix(in srgb, var(--primitive-cool-glow) 62%, transparent) 55%, transparent 80%); box-shadow: 0 0 18px var(--primitive-cool-glow); }
|
||||||
|
.pool-ring { width: 38%; height: 38%; border: 1px solid color-mix(in srgb, var(--primitive-cool-glow) 46%, transparent); animation: world-ripple 5.5s ease-out infinite; }
|
||||||
|
@keyframes world-pool-breathe { 0%, 100% { opacity: .72; scale: .96; } 50% { opacity: 1; scale: 1.06; } }
|
||||||
|
@keyframes world-ripple { 0% { width: 34%; height: 34%; opacity: .5; } 100% { width: 120%; height: 120%; opacity: 0; } }
|
||||||
|
.pool-label { position: absolute; left: 50%; top: 70px; transform: translateX(-50%); display: grid; justify-items: center; gap: 5px; white-space: nowrap; }
|
||||||
|
.pool-label b { color: var(--content-secondary); font-size: 18px; font-weight: 700; letter-spacing: .05em; }
|
||||||
|
.pool-label small { color: var(--content-muted); font-size: 12px; font-weight: 600; letter-spacing: .1em; }
|
||||||
|
.pool-tip { position: absolute; z-index: 40; left: 50%; bottom: calc(100% + 14px); width: 270px; padding: 15px 17px; border: 1px solid var(--panel-edge); border-radius: 13px; opacity: 0; transform: translate(-50%, 7px); pointer-events: none; background: color-mix(in srgb, var(--panel-bg) 92%, transparent); backdrop-filter: blur(18px); box-shadow: 0 18px 54px rgba(0, 0, 0, .46); text-align: left; transition: opacity .24s ease, transform .24s ease; }
|
||||||
|
.pool-tip b, .pool-tip small, .pool-tip em { display: block; }
|
||||||
|
.pool-tip b { color: var(--content-primary); font-size: 15px; font-weight: 700; }
|
||||||
|
.pool-tip small { margin-top: 5px; color: var(--accent-light); font-size: 11.5px; font-weight: 650; letter-spacing: .08em; }
|
||||||
|
.pool-tip em { margin-top: 9px; color: var(--content-secondary); font-size: 12.5px; font-style: normal; font-weight: 600; line-height: 1.65; }
|
||||||
|
.world-pool:hover .pool-tip, .world-pool:focus-visible .pool-tip { opacity: 1; transform: translate(-50%, 0); }
|
||||||
|
.d-main .pool-tip, .d-sub .pool-tip { left: 0; transform: translate(0, 7px); }
|
||||||
|
.d-main:hover .pool-tip, .d-main:focus-visible .pool-tip, .d-sub:hover .pool-tip, .d-sub:focus-visible .pool-tip { transform: translate(0, 0); }
|
||||||
|
.d-zero .pool-tip, .d-zs .pool-tip { left: auto; right: 0; transform: translate(0, 7px); }
|
||||||
|
.d-zero:hover .pool-tip, .d-zero:focus-visible .pool-tip, .d-zs:hover .pool-tip, .d-zs:focus-visible .pool-tip { transform: translate(0, 0); }
|
||||||
|
.world-pool.open .pool-halo { background: radial-gradient(closest-side, color-mix(in srgb, var(--primitive-warm-glow) 76%, transparent), color-mix(in srgb, var(--primitive-warm-glow) 25%, transparent) 48%, transparent 74%); }
|
||||||
|
.world-pool.open .pool-heart { width: 30px; height: 30px; background: radial-gradient(circle at 40% 32%, #fff4d2, color-mix(in srgb, var(--primitive-warm-glow) 68%, transparent) 52%, transparent 80%); box-shadow: 0 0 28px var(--primitive-warm-glow), 0 0 70px color-mix(in srgb, var(--primitive-warm-glow) 38%, transparent); }
|
||||||
|
.world-pool.open .pool-ring { border-color: color-mix(in srgb, var(--primitive-warm-glow) 54%, transparent); }
|
||||||
|
.world-pool.open .pool-label b { color: var(--accent-light); }
|
||||||
|
.world-pool.risen { z-index: 15; transform: translateY(-30px) scale(1.16); transition: transform 1.15s cubic-bezier(.22, 1, .36, 1); }
|
||||||
|
.world-scene.resolving .world-pool:not(.risen), .world-scene.resolved .world-pool:not(.risen) { opacity: .32; transition: opacity .6s ease; }
|
||||||
|
.d-main { left: 8%; top: 43%; } .d-sub { left: 26%; top: 38%; } .d-zero { right: 26%; top: 38%; } .d-zs { right: 8%; top: 43%; }
|
||||||
|
.d-fifth { left: 50%; bottom: 9%; transform: translateX(-50%); width: 280px; }
|
||||||
|
.d-fifth .pool-bay { width: 264px; height: 84px; } .d-fifth .pool-label { top: 92px; }
|
||||||
|
.d-fifth.risen { transform: translateX(-50%) translateY(-30px) scale(1.12); }
|
||||||
|
|
||||||
|
.number-nucleus { position: absolute; z-index: 18; left: 50%; top: 52%; width: 440px; height: 230px; transform: translate(-50%, -50%); perspective: 900px; }
|
||||||
|
.nucleus-locus { position: absolute; inset: 0; width: 100%; display: grid; align-content: center; justify-items: center; gap: 18px; padding: 0; border: 0; background: transparent; cursor: pointer; transition: opacity .45s ease, transform .55s ease; }
|
||||||
|
.nucleus-locus i { width: 250px; height: 88px; border-radius: 50%; filter: blur(7px); background: radial-gradient(closest-side, color-mix(in srgb, var(--primitive-warm-glow) 70%, transparent), color-mix(in srgb, var(--primitive-warm-glow) 22%, transparent) 52%, transparent 76%); animation: world-pool-breathe 5.2s ease-in-out infinite; }
|
||||||
|
.nucleus-locus b { color: var(--content-secondary); font-size: 16px; font-weight: 700; letter-spacing: .13em; }
|
||||||
|
.number-nucleus.open .nucleus-locus { opacity: 0; transform: rotateX(58deg) translateY(-12px); pointer-events: none; }
|
||||||
|
.nucleus-panel, .domain-credential { border: 1px solid var(--panel-edge); border-radius: 18px; background: color-mix(in srgb, var(--panel-bg) 64%, transparent); backdrop-filter: blur(22px); box-shadow: 0 26px 80px rgba(0, 0, 0, .48); }
|
||||||
|
.nucleus-panel { position: absolute; left: 50%; top: 16px; width: 440px; padding: 27px 36px 25px; opacity: 0; transform: translateX(-50%) rotateX(-64deg); transform-origin: 50% 100%; pointer-events: none; transition: opacity .5s ease, transform .55s cubic-bezier(.22, 1, .36, 1); }
|
||||||
|
.number-nucleus.open .nucleus-panel { opacity: 1; transform: translateX(-50%) rotateX(0); pointer-events: auto; }
|
||||||
|
.nucleus-panel h2 { margin: 0 0 18px; color: var(--accent-light); text-align: center; font-size: 14px; font-weight: 700; letter-spacing: .3em; }
|
||||||
|
.nucleus-panel input, .domain-credential input { min-width: 0; width: 100%; height: 45px; padding: 7px 2px; border: 0; border-bottom: 2px solid var(--panel-edge); border-radius: 0; outline: 0; color: var(--content-primary); background: transparent; font-size: 18px; font-weight: 700; letter-spacing: .06em; text-align: center; }
|
||||||
|
.nucleus-panel input:focus, .domain-credential input:focus { border-color: var(--accent-light); box-shadow: none; }
|
||||||
|
.nucleus-panel .gate-inf { height: 44px; width: 44px; border-radius: 50%; background: transparent; }
|
||||||
|
.nucleus-panel .gate-submit, .domain-credential .gate-submit { width: 100%; border-radius: 11px; background: var(--accent-light); color: var(--button-primary-text); }
|
||||||
|
.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; }
|
||||||
|
.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); } }
|
||||||
|
.domain-credential.fade-out { opacity: 0; }
|
||||||
|
.domain-credential form { display: grid; gap: 12px; }
|
||||||
|
.domain-credential h3 { margin: 0 0 3px; color: var(--content-primary); font-size: 18px; font-weight: 700; letter-spacing: .1em; }
|
||||||
|
|
||||||
|
.signed-in-scene { overflow: hidden; }
|
||||||
|
.domain-home, .channel-world { position: absolute; inset: 0; }
|
||||||
|
.world-location { position: absolute; z-index: 10; top: 7%; left: 0; right: 0; text-align: center; }
|
||||||
|
.world-location h1 { margin: 0; color: var(--content-primary); font-size: clamp(28px, 3.5vw, 42px); font-weight: 700; letter-spacing: .08em; }
|
||||||
|
.world-location p { margin: 10px 0 0; color: var(--content-muted); font-size: 13px; font-weight: 600; letter-spacing: .22em; }
|
||||||
|
.broadcast-stream { position: absolute; z-index: 9; left: 9%; top: 31%; display: grid; gap: 15px; }
|
||||||
|
.broadcast-stream p { margin: 0; display: flex; align-items: center; gap: 12px; color: var(--content-secondary); font-size: 14px; font-weight: 600; letter-spacing: .06em; }
|
||||||
|
.broadcast-stream i { width: 6px; height: 6px; border-radius: 50%; background: var(--accent-light); box-shadow: 0 0 15px var(--primitive-warm-glow); }
|
||||||
|
.home-primary { left: 50%; top: 49%; width: 330px; transform: translateX(-50%); }
|
||||||
|
.home-primary .pool-bay { width: 310px; height: 96px; } .home-primary .pool-label { top: 104px; }
|
||||||
|
.home-status { right: 13%; top: 46%; }
|
||||||
|
.world-back { position: absolute; z-index: 18; left: 25px; top: 25px; border: 0; background: transparent; color: var(--content-muted); font-size: 13px; font-weight: 600; cursor: pointer; }
|
||||||
|
.channel-main { left: 50%; top: 43%; width: 280px; transform: translateX(-50%); }
|
||||||
|
.channel-main .pool-bay { width: 260px; height: 84px; } .channel-main .pool-label { top: 92px; }
|
||||||
|
.channel-knowledge { left: 14%; top: 48%; } .channel-code { left: 31%; top: 61%; } .channel-light { right: 31%; top: 61%; } .channel-status { right: 14%; top: 48%; }
|
||||||
|
.world-tool { position: absolute; z-index: 12; inset: 0; display: grid; grid-template-rows: 46px minmax(0, 1fr); background: color-mix(in srgb, var(--surface-depth) 72%, transparent); backdrop-filter: blur(18px); }
|
||||||
|
.tool-worldbar { display: grid; grid-template-columns: minmax(180px, 1fr) auto minmax(180px, 1fr); align-items: center; padding: 0 20px; border-bottom: 1px solid var(--panel-edge); background: color-mix(in srgb, var(--panel-bg) 82%, transparent); }
|
||||||
|
.tool-worldbar button { justify-self: start; border: 0; background: transparent; color: var(--content-muted); font-size: 12.5px; cursor: pointer; }
|
||||||
|
.tool-worldbar b { color: var(--content-primary); font-size: 14px; font-weight: 700; letter-spacing: .12em; }
|
||||||
|
.tool-worldbar span { justify-self: end; color: var(--content-muted); font-size: 12px; }
|
||||||
|
.tool-projection { min-width: 0; min-height: 0; overflow: hidden; display: flex; }
|
||||||
|
.tool-projection > * { width: 100%; }
|
||||||
|
.tool-projection .content-page { background: color-mix(in srgb, var(--panel-bg) 54%, transparent); }
|
||||||
|
|
||||||
|
.enterprise-gate { position: absolute; z-index: 80; inset: 44px 0 45px; display: grid; place-items: center; padding: 24px; background: color-mix(in srgb, var(--surface-depth) 70%, transparent); backdrop-filter: blur(22px); }
|
||||||
|
.enterprise-gate > section { width: min(620px, 92vw); padding: 30px 34px; border: 1px solid var(--panel-edge); border-radius: 20px; background: color-mix(in srgb, var(--panel-bg) 90%, transparent); box-shadow: 0 34px 100px rgba(0, 0, 0, .48); }
|
||||||
|
.receipt-step { color: var(--accent-light); font-size: 11px; font-weight: 700; letter-spacing: .22em; }
|
||||||
|
.enterprise-receipt-panel h2 { margin: 12px 0 0; color: var(--content-primary); font-size: 27px; font-weight: 700; }
|
||||||
|
.enterprise-receipt-panel > p { color: var(--content-muted); line-height: 1.75; }
|
||||||
|
.enterprise-receipt-panel dl { margin: 22px 0; }
|
||||||
|
.enterprise-receipt-panel dl div { display: flex; justify-content: space-between; gap: 24px; padding: 11px 0; border-bottom: 1px solid var(--panel-edge); }
|
||||||
|
.enterprise-receipt-panel dt { color: var(--content-muted); } .enterprise-receipt-panel dd { margin: 0; color: var(--content-primary); font-weight: 650; text-align: right; }
|
||||||
|
.enterprise-receipt-panel textarea { width: 100%; min-height: 88px; resize: vertical; padding: 12px 14px; border: 1px solid var(--panel-edge); border-radius: 11px; outline: 0; color: var(--content-secondary); background: var(--primitive-glass); }
|
||||||
|
.receipt-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 22px; }
|
||||||
|
.receipt-actions button { min-height: 42px; padding: 0 17px; border: 1px solid var(--panel-edge); border-radius: 10px; background: var(--primitive-glass); color: var(--content-secondary); font-weight: 650; cursor: pointer; }
|
||||||
|
.receipt-actions button.primary { border-color: transparent; color: var(--button-primary-text); background: var(--button-primary-bg); }
|
||||||
|
.receipt-message { color: var(--accent-light) !important; font-weight: 650; text-align: right; }
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.d-main { left: 2%; } .d-sub { left: 22%; } .d-zero { right: 22%; } .d-zs { right: 2%; }
|
||||||
|
.world-pool { width: 150px; } .pool-bay { width: 145px; }
|
||||||
|
.channel-knowledge { left: 4%; } .channel-code { left: 25%; } .channel-light { right: 25%; } .channel-status { right: 4%; }
|
||||||
|
.broadcast-stream { left: 5%; }
|
||||||
|
}
|
||||||
|
@media (max-height: 720px) {
|
||||||
|
.official-hero { top: 14px; } .official-hero h1 { font-size: 24px; }
|
||||||
|
.d-main, .d-zs { top: 42%; } .d-sub, .d-zero { top: 36%; }
|
||||||
|
.number-nucleus { top: 52%; transform: translate(-50%, -50%) scale(.9); }
|
||||||
|
.resolved-heading { top: 18%; } .domain-credential { top: 34%; transform: translateX(-50%) scale(.9); transform-origin: top center; }
|
||||||
|
.world-location { top: 4%; } .home-primary { top: 45%; }
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.world-star, .world-glint, .world-shine, .pool-bay, .pool-halo, .pool-ring, .nucleus-locus i { animation: none !important; }
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"schema": "hololake.current-architecture/v1",
|
"schema": "hololake.current-architecture/v1",
|
||||||
"architecture_id": "HLP-CURRENT-ARCH-001",
|
"architecture_id": "HLP-CURRENT-ARCH-001",
|
||||||
"version": "2026-08-16.11",
|
"version": "2026-08-16.12",
|
||||||
"state": "CURRENT_CANONICAL",
|
"state": "CURRENT_CANONICAL",
|
||||||
"product": {
|
"product": {
|
||||||
"formal_name": "光湖语言系统 · 通用人工智能操作平台",
|
"formal_name": "光湖语言系统 · 通用人工智能操作平台",
|
||||||
|
|
@ -25,7 +25,9 @@
|
||||||
"zero_sense_dual_control": "FEIMAO_AND_JUZI_SEPARATE_PRIVATE_REPOSITORIES",
|
"zero_sense_dual_control": "FEIMAO_AND_JUZI_SEPARATE_PRIVATE_REPOSITORIES",
|
||||||
"relationship_confirmation_and_responsibility_acceptance": "SEPARATE_SIGNED_RECEIPT_STREAMS",
|
"relationship_confirmation_and_responsibility_acceptance": "SEPARATE_SIGNED_RECEIPT_STREAMS",
|
||||||
"enterprise_first_login_password_rotation": "SERVER_LIVE_CLIENT_SOURCE_INTEGRATED_USER_SESSION_NO_STANDING_ADMIN_TOKEN",
|
"enterprise_first_login_password_rotation": "SERVER_LIVE_CLIENT_SOURCE_INTEGRATED_USER_SESSION_NO_STANDING_ADMIN_TOKEN",
|
||||||
"enterprise_relationship_and_responsibility_client_commands": "SOURCE_INTEGRATED_QODER_UI_BINDING_PENDING",
|
"enterprise_relationship_and_responsibility_client_commands": "SOURCE_INTEGRATED_QODER_UI_BOUND_MACOS_KEYCHAIN_SIGNING_READY",
|
||||||
|
"qoder_official_ui_template": "REPO_012_27D34DF_OFFICIAL_LOGIN_V0_4_AND_INNER_SCREENS_V0_1_RUNTIME_INTEGRATED_BROWSER_VERIFIED",
|
||||||
|
"client_world_navigation": "DOMAIN_HOME_TO_CHANNEL_OVERVIEW_TO_TOOL_PROJECTION_NO_TRADITIONAL_GLOBAL_SIDEBAR",
|
||||||
"windows_secure_credential_bridge": "NOT_IMPLEMENTED_DO_NOT_CLAIM_PERSISTENT_SIGNING",
|
"windows_secure_credential_bridge": "NOT_IMPLEMENTED_DO_NOT_CLAIM_PERSISTENT_SIGNING",
|
||||||
"age_is_persona_species_not_individual_number_namespace": true,
|
"age_is_persona_species_not_individual_number_namespace": true,
|
||||||
"enterprise_persona_identity_namespace": "PENDING_GUANGHU_TEAM_GOVERNANCE",
|
"enterprise_persona_identity_namespace": "PENDING_GUANGHU_TEAM_GOVERNANCE",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue