feat: add HoloLake Era shell prototype

This commit is contained in:
冰朔 2026-07-13 23:33:23 +08:00
parent 831c82f7a8
commit 508acd0f12
8 changed files with 130 additions and 11 deletions

View File

@ -12,6 +12,11 @@ rust-version = "1.77.2"
name = "tolaria_lib" name = "tolaria_lib"
crate-type = ["staticlib", "cdylib", "rlib"] crate-type = ["staticlib", "cdylib", "rlib"]
[features]
# Release builds use Tauri's embedded asset protocol so the desktop application
# starts from its packaged frontend instead of a local development web server.
custom-protocol = ["tauri/custom-protocol"]
[build-dependencies] [build-dependencies]
tauri-build = { version = "2.5.4", features = [] } tauri-build = { version = "2.5.4", features = [] }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 521 KiB

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

View File

@ -323,8 +323,10 @@ fn setup_deep_link_runtime_registration(
fn setup_desktop_plugins(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> { fn setup_desktop_plugins(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
setup_macos_webview_shortcut_prevention(app)?; setup_macos_webview_shortcut_prevention(app)?;
setup_deep_link_runtime_registration(app)?; setup_deep_link_runtime_registration(app)?;
app.handle() // The signed HoloLake Era release service is not configured yet. Registering
.plugin(tauri_plugin_updater::Builder::new().build())?; // Tauri's updater without its required public key and HTTPS endpoint makes
// the whole desktop application fail during startup. The update commands
// remain unavailable until that release service is configured deliberately.
app.handle().plugin(tauri_plugin_process::init())?; app.handle().plugin(tauri_plugin_process::init())?;
app.handle().plugin(tauri_plugin_opener::init())?; app.handle().plugin(tauri_plugin_opener::init())?;
if should_use_native_desktop_menu(std::env::consts::OS) { if should_use_native_desktop_menu(std::env::consts::OS) {

View File

@ -1,7 +1,7 @@
{ {
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"productName": "光湖", "productName": "HoloLake Era",
"version": "0.1.0", "version": "0.1.1",
"identifier": "com.guanghu.desktop", "identifier": "com.guanghu.desktop",
"build": { "build": {
"frontendDist": "../dist", "frontendDist": "../dist",
@ -14,7 +14,7 @@
"macOSPrivateApi": true, "macOSPrivateApi": true,
"windows": [ "windows": [
{ {
"title": "光湖", "title": "HoloLake Era",
"width": 1400, "width": 1400,
"height": 900, "height": 900,
"minWidth": 480, "minWidth": 480,

View File

@ -1,5 +1,36 @@
/* App layout - minimal CSS, most styling via Tailwind */ /* App layout - minimal CSS, most styling via Tailwind */
.app-shell { .app-shell {
/* HoloLake Era visual system: the inherited Tolaria dark palette is warm
charcoal; override semantic tokens here so every existing panel speaks
the same deep-lake blue language as the world entrance. */
--surface-app: #071126;
--surface-sidebar: #0a1730;
--surface-panel: #0d1c38;
--surface-card: #102343;
--surface-popover: #12284c;
--surface-input: #0a1730;
--surface-button: #172e55;
--surface-dialog: #0d1d3a;
--surface-editor: #09162d;
--surface-overlay: rgba(2, 7, 18, .68);
--text-primary: #edf4ff;
--text-secondary: #b9c9e2;
--text-tertiary: #8ea4c6;
--text-muted: #7188ac;
--text-heading: #f3f7ff;
--border-default: #1d3a67;
--border-subtle: #142b4d;
--border-strong: #315588;
--border-input: #274a78;
--sidebar-border: #17365f;
--state-hover: #142b4e;
--state-hover-subtle: #0e213f;
--state-selected: #173d70;
--state-selected-strong: #24538d;
--state-active: #1d477e;
--accent-blue: #8dbbff;
--accent-blue-bg: rgba(105, 165, 255, .23);
--accent-blue-light: rgba(105, 165, 255, .16);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
@ -12,6 +43,33 @@
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--border-primary) 82%, var(--accent-blue)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--border-primary) 82%, var(--accent-blue));
} }
.hololake-bar { height: 52px; flex: 0 0 52px; display: flex; align-items: center; gap: 14px; padding: 0 20px; color: #eef4ff; background: linear-gradient(100deg, #08122b, #172d58 52%, #0b1731); border-bottom: 1px solid rgba(153, 189, 255, .28); letter-spacing: .02em; }
.hololake-bar__brand, .hololake-bar__library { border: 0; background: transparent; color: inherit; cursor: pointer; }
.hololake-bar__brand { display: inline-flex; align-items: center; gap: 9px; font-size: 16px; font-weight: 650; letter-spacing: .04em; }
.hololake-bar__brand b { color: #9cc3ff; font-weight: 500; }
.hololake-bar__mark { display: grid; place-items: center; width: 24px; height: 24px; color: #dceaff; border: 1px solid rgba(205, 225, 255, .48); border-radius: 50%; box-shadow: 0 0 22px rgba(96, 165, 250, .65); }
.hololake-bar__line { width: 1px; height: 17px; background: rgba(196, 218, 255, .35); }
.hololake-bar__context { color: #b7c8e7; font-size: 12px; }
.hololake-bar__library { margin-left: auto; padding: 6px 10px; border: 1px solid rgba(185, 211, 255, .36); border-radius: 999px; color: #dceaff; font-size: 12px; }
.hololake-home { position: relative; z-index: 2; width: 100%; overflow: auto; padding: clamp(32px, 6vw, 84px); color: #ecf4ff; background: radial-gradient(circle at 75% 10%, rgba(75, 135, 255, .25), transparent 28%), radial-gradient(circle at 12% 92%, rgba(83, 220, 255, .15), transparent 34%), #071126; }
.hololake-home__hero { position: relative; max-width: 740px; padding: 24px 0 56px; overflow: hidden; }
.hololake-home__halo { position: absolute; right: -110px; top: -180px; width: 390px; aspect-ratio: 1; border: 1px solid rgba(166, 205, 255, .28); border-radius: 50%; box-shadow: 0 0 0 45px rgba(103, 165, 255, .05), 0 0 0 105px rgba(103, 165, 255, .035), 0 0 100px rgba(77, 143, 255, .4); }
.hololake-home__eyebrow, .hololake-home__section-head span { color: #8db5f7; font-size: 12px; letter-spacing: .16em; text-transform: uppercase; }
.hololake-home h1 { position: relative; margin: 18px 0 0; font-family: Georgia, 'Times New Roman', serif; font-size: clamp(52px, 7vw, 92px); font-weight: 500; line-height: .95; letter-spacing: -.06em; }
.hololake-home h1 em { color: #9cc7ff; font-style: italic; font-weight: 400; }
.hololake-home__lead { margin: 20px 0 0; font-size: 18px; letter-spacing: .16em; }
.hololake-home__copy { max-width: 560px; margin: 16px 0 0; color: #b9c8df; font-size: 15px; line-height: 1.9; }
.hololake-home__actions { display: flex; align-items: center; gap: 18px; margin-top: 30px; }
.hololake-home__primary { padding: 12px 18px; border: 1px solid #b9d5ff; border-radius: 8px; background: linear-gradient(135deg, #c8dcff, #6093e8); color: #061126; font-weight: 700; cursor: pointer; }
.hololake-home__primary span, .hololake-module button span { margin-left: 12px; }
.hololake-home__status { color: #9eb3d4; font-size: 12px; }.hololake-home__status i { display: inline-block; width: 7px; height: 7px; margin-right: 7px; border-radius: 50%; background: #67e8b6; box-shadow: 0 0 12px #67e8b6; }
.hololake-home__signal-grid, .hololake-home__module-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.hololake-home__signal-grid article, .hololake-module { border: 1px solid rgba(157, 192, 244, .2); border-radius: 14px; background: linear-gradient(145deg, rgba(30, 57, 105, .55), rgba(12, 25, 51, .78)); box-shadow: inset 0 1px rgba(255,255,255,.06); }
.hololake-home__signal-grid article { padding: 18px; }.hololake-home__signal-grid span, .hololake-home__signal-grid small { display: block; color: #91a9ce; font-size: 11px; }.hololake-home__signal-grid strong { display: block; margin: 10px 0 6px; font-size: 15px; }
.hololake-home__modules { margin-top: 52px; }.hololake-home__section-head { display: flex; align-items: end; justify-content: space-between; margin-bottom: 14px; }.hololake-home__section-head p { margin: 0; color: #91a9ce; font-size: 13px; }
.hololake-module { min-height: 215px; padding: 22px; display: flex; flex-direction: column; }.hololake-module__number { color: #78aaf7; font-family: monospace; font-size: 12px; }.hololake-module h2 { margin: auto 0 8px; font-size: 22px; font-weight: 600; }.hololake-module p { margin: 0; color: #aebed7; font-size: 13px; line-height: 1.7; }.hololake-module button { margin-top: 19px; padding: 0; border: 0; color: #c8dcff; background: transparent; text-align: left; font: inherit; cursor: pointer; }.hololake-module__soon { margin-top: 19px; color: #7185a8; font-size: 12px; }
@media (max-width: 780px) { .hololake-home__signal-grid, .hololake-home__module-grid { grid-template-columns: 1fr; }.hololake-bar__context { display: none; }.hololake-home { padding: 28px; } }
.app { .app {
display: flex; display: flex;
flex: 1; flex: 1;
@ -24,7 +82,7 @@
min-width: 220px; min-width: 220px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background: linear-gradient(180deg, color-mix(in srgb, var(--surface-sidebar) 92%, var(--guanghu-glow)), var(--surface-sidebar)); background: linear-gradient(180deg, #10254a, var(--surface-sidebar) 40%, #08152c);
} }
.app__sidebar > * { .app__sidebar > * {
@ -42,9 +100,8 @@
flex-direction: column; flex-direction: column;
position: relative; position: relative;
z-index: 20; z-index: 20;
background: background: linear-gradient(180deg, #0d2040, #091730 48%, #08152c);
linear-gradient(180deg, color-mix(in srgb, var(--surface-sidebar) 94%, var(--guanghu-glow)), var(--surface-sidebar)); box-shadow: 14px 0 30px rgba(1, 7, 20, .36);
box-shadow: 14px 0 30px color-mix(in srgb, var(--surface-app) 60%, transparent);
} }
.app__note-list > * { .app__note-list > * {

View File

@ -21,6 +21,7 @@ import { FeedbackDialog } from './components/FeedbackDialog'
import { McpSetupDialog } from './components/McpSetupDialog' import { McpSetupDialog } from './components/McpSetupDialog'
import { NoteRetargetingDialogs } from './components/note-retargeting/NoteRetargetingDialogs' import { NoteRetargetingDialogs } from './components/note-retargeting/NoteRetargetingDialogs'
import { StartupScreen } from './components/StartupScreen' import { StartupScreen } from './components/StartupScreen'
import { HoloLakeHome } from './components/HoloLakeHome'
import { useAiAgentsOnboarding } from './hooks/useAiAgentsOnboarding' import { useAiAgentsOnboarding } from './hooks/useAiAgentsOnboarding'
import { useAiAgentsStatus } from './hooks/useAiAgentsStatus' import { useAiAgentsStatus } from './hooks/useAiAgentsStatus'
import { useVaultAiGuidanceStatus } from './hooks/useVaultAiGuidanceStatus' import { useVaultAiGuidanceStatus } from './hooks/useVaultAiGuidanceStatus'
@ -165,6 +166,7 @@ function App() {
function MainApp({ noteWindowParams }: { noteWindowParams: NoteWindowParams | null }) { function MainApp({ noteWindowParams }: { noteWindowParams: NoteWindowParams | null }) {
const aiWorkspaceWindow = false const aiWorkspaceWindow = false
const [showHoloLakeHome, setShowHoloLakeHome] = useState(true)
const [selection, setSelection] = useState<SidebarSelection>(DEFAULT_SELECTION) const [selection, setSelection] = useState<SidebarSelection>(DEFAULT_SELECTION)
const [noteListFilter, setNoteListFilter] = useState<NoteListFilter>('open') const [noteListFilter, setNoteListFilter] = useState<NoteListFilter>('open')
const [pendingNoteListPdfExportPath, setPendingNoteListPdfExportPath] = useState<string | null>(null) const [pendingNoteListPdfExportPath, setPendingNoteListPdfExportPath] = useState<string | null>(null)
@ -1621,6 +1623,12 @@ function MainApp({ noteWindowParams }: { noteWindowParams: NoteWindowParams | nu
return ( return (
<AppPreferencesProvider dateDisplayFormat={dateDisplayFormat}> <AppPreferencesProvider dateDisplayFormat={dateDisplayFormat}>
<div className="app-shell"> <div className="app-shell">
<header className="hololake-bar">
<button className="hololake-bar__brand" onClick={() => setShowHoloLakeHome(true)}><span className="hololake-bar__mark"></span><span>HoloLake <b>Era</b></span></button>
<span className="hololake-bar__line" />
<span className="hololake-bar__context"></span>
<button className="hololake-bar__library" onClick={() => setShowHoloLakeHome(false)}></button>
</header>
<div className="app"> <div className="app">
{sidebarVisible && ( {sidebarVisible && (
<> <>
@ -1643,7 +1651,7 @@ function MainApp({ noteWindowParams }: { noteWindowParams: NoteWindowParams | nu
</> </>
)} )}
<div className={`app__editor${aiActivity.highlightElement === 'editor' || aiActivity.highlightElement === 'tab' ? ' ai-highlight' : ''}`}> <div className={`app__editor${aiActivity.highlightElement === 'editor' || aiActivity.highlightElement === 'tab' ? ' ai-highlight' : ''}`}>
<Editor {showHoloLakeHome ? <HoloLakeHome onEnterKnowledgeBase={() => setShowHoloLakeHome(false)} /> : <Editor
tabs={notes.tabs} tabs={notes.tabs}
activeTabPath={notes.activeTabPath} activeTabPath={notes.activeTabPath}
isVaultLoading={isVaultContentLoading} isVaultLoading={isVaultContentLoading}
@ -1719,7 +1727,7 @@ function MainApp({ noteWindowParams }: { noteWindowParams: NoteWindowParams | nu
flushPendingRawContentRef={flushPendingRawContentRef} flushPendingRawContentRef={flushPendingRawContentRef}
onToast={setToastMessage} onToast={setToastMessage}
locale={appLocale} locale={appLocale}
/> />}
</div> </div>
</div> </div>
<UpdateBanner status={updateStatus} actions={updateActions} locale={appLocale} /> <UpdateBanner status={updateStatus} actions={updateActions} locale={appLocale} />

View File

@ -0,0 +1,47 @@
type HoloLakeHomeProps = {
onEnterKnowledgeBase: () => void
}
const modules = [
['知识湖', '把代码仓库、文档与路径转译成可阅读、可追溯的知识页面。', '进入知识库'],
['协作空间', '人类与人格体在同一上下文中对话、确认、留存回执。', '筹备中'],
['授权中心', '任何真实操作先形成可读计划,再由人类一次确认。', '筹备中'],
]
export function HoloLakeHome({ onEnterKnowledgeBase }: HoloLakeHomeProps) {
return (
<main className="hololake-home" aria-label="HoloLake Era 世界入口">
<section className="hololake-home__hero">
<div className="hololake-home__halo" aria-hidden="true" />
<p className="hololake-home__eyebrow">ICE-GL · </p>
<h1>HoloLake <em>Era</em></h1>
<p className="hololake-home__lead"></p>
<p className="hololake-home__copy"></p>
<div className="hololake-home__actions">
<button className="hololake-home__primary" onClick={onEnterKnowledgeBase}> <span></span></button>
<span className="hololake-home__status"><i /> · </span>
</div>
</section>
<section className="hololake-home__signal-grid" aria-label="系统状态">
<article><span></span><strong>SYS-GLW-0001</strong><small></small></article>
<article><span></span><strong>Tolaria </strong><small></small></article>
<article><span></span><strong></strong><small></small></article>
</section>
<section className="hololake-home__modules">
<div className="hololake-home__section-head"><span>01 / </span><p></p></div>
<div className="hololake-home__module-grid">
{modules.map(([title, description, action], index) => (
<article className="hololake-module" key={title}>
<span className="hololake-module__number">0{index + 1}</span>
<h2>{title}</h2>
<p>{description}</p>
{index === 0 ? <button onClick={onEnterKnowledgeBase}>{action} <span></span></button> : <span className="hololake-module__soon">{action}</span>}
</article>
))}
</div>
</section>
</main>
)
}