feat: add remote-first education work lake

This commit is contained in:
冰朔 2026-08-07 20:41:03 +08:00
commit 333cd222c5
43 changed files with 923 additions and 74 deletions

View file

@ -1436,6 +1436,7 @@ function MainApp({
telemetryConsent: settings.telemetry_consent,
vaultIsLoading: vault.isLoading,
vaultSwitcher,
worldEntryVisible: showHoloLakeHome,
})
const deepLinks = useDeepLinks({
activeEntry: activeTab?.entry ?? null,

View file

@ -19,11 +19,6 @@ const installLinkTargets = [
['ai-agents-onboarding-install-claude_code', 'https://docs.anthropic.com/en/docs/claude-code'],
['ai-agents-onboarding-install-codex', 'https://developers.openai.com/codex/cli'],
['ai-agents-onboarding-install-copilot', 'https://docs.github.com/en/copilot/how-tos/copilot-cli/set-up-copilot-cli/install-copilot-cli'],
['ai-agents-onboarding-install-opencode', 'https://opencode.ai/docs/'],
['ai-agents-onboarding-install-pi', 'https://pi.dev'],
['ai-agents-onboarding-install-antigravity', 'https://antigravity.google/docs/cli/install'],
['ai-agents-onboarding-install-kiro', 'https://kiro.dev/docs/cli'],
['ai-agents-onboarding-install-hermes', 'https://hermes-agent.nousresearch.com/docs/getting-started/quickstart'],
] as const
vi.mock('../utils/url', () => ({

View file

@ -0,0 +1,30 @@
.education-work-lake { display: grid; gap: 20px; width: min(1160px, 100%); margin: 0 auto; padding-bottom: 48px; }
.education-work-lake__header { display: flex; align-items: end; justify-content: space-between; gap: 28px; padding: 24px 0 8px; }
.education-work-lake__header p { margin: 0 0 8px; color: var(--accent-blue); font: 11px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .12em; }
.education-work-lake__header h1 { margin: 0; font-family: Georgia, "Songti SC", serif; font-size: clamp(34px, 5vw, 58px); font-weight: 500; letter-spacing: -.04em; }
.education-work-lake__header span { display: block; max-width: 720px; margin-top: 12px; color: var(--text-secondary); line-height: 1.7; }
.education-work-lake__header aside { min-width: 190px; padding: 14px 16px; border: 1px solid var(--border-default); border-radius: 10px; background: var(--surface-card); }
.education-work-lake__header aside i { display: inline-block; width: 7px; height: 7px; margin-right: 8px; border-radius: 50%; background: var(--accent-green); }
.education-work-lake__header aside strong, .education-work-lake__header aside small { display: block; margin-top: 4px; }
.education-work-lake__header aside small { color: var(--text-secondary); }
.education-command { display: grid; grid-template-columns: 1fr auto; gap: 10px; padding: 14px; border: 1px solid color-mix(in srgb, var(--accent-blue) 35%, var(--border-default)); border-radius: 12px; background: color-mix(in srgb, var(--surface-card) 92%, var(--accent-blue)); }
.education-command input { min-height: 42px; }
.education-receipt { display: flex; align-items: center; gap: 12px; min-height: 38px; padding: 8px 12px; border: 1px solid color-mix(in srgb, var(--accent-green) 36%, var(--border-default)); border-radius: 8px; color: var(--text-secondary); background: color-mix(in srgb, var(--surface-card) 94%, var(--accent-green)); }
.education-receipt code { color: var(--accent-green); }.education-receipt small { margin-left: auto; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.education-work-lake [data-slot="tabs-list"] { max-width: 100%; overflow-x: auto; }
.education-work-lake [data-slot="tabs-content"] { min-height: 430px; margin-top: 12px; padding: 18px; border: 1px solid var(--border-default); border-radius: 12px; background: var(--surface-card); }
.education-document { min-height: 390px; resize: vertical; font: 16px/1.8 ui-serif, "Songti SC", serif; }
.education-sheet { height: 430px; overflow: hidden; border-radius: 8px; }
.education-chart { display: grid; gap: 18px; padding: 24px 16px; }
.education-chart__row { display: grid; grid-template-columns: 64px minmax(140px, 1fr) 42px; align-items: center; gap: 12px; }
.education-chart__row > div { height: 18px; overflow: hidden; border-radius: 999px; background: color-mix(in srgb, var(--surface-muted) 82%, transparent); }
.education-chart__row i { display: block; height: 100%; border-radius: inherit; background: linear-gradient(90deg, var(--accent-blue), #8b7cf6); }
.education-dashboard { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.education-dashboard > article { padding: 20px; border: 1px solid var(--border-default); border-radius: 10px; background: var(--surface-app); }
.education-dashboard article span, .education-dashboard article small { display: block; color: var(--text-secondary); }
.education-dashboard article strong { display: block; margin: 12px 0 8px; font-size: 30px; }
.education-dashboard__chart { grid-column: 1 / -1; }
.education-knowledge { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.education-knowledge article { padding: 22px; border: 1px solid var(--border-default); border-radius: 10px; background: var(--surface-app); }
.education-knowledge code, .education-knowledge strong { display: block; }.education-knowledge code { color: var(--accent-blue); font-size: 11px; }.education-knowledge strong { margin-top: 16px; font-size: 17px; }.education-knowledge p { color: var(--text-secondary); line-height: 1.7; }
@media (max-width: 760px) { .education-work-lake__header { align-items: stretch; flex-direction: column; }.education-work-lake__header aside { min-width: 0; }.education-command { grid-template-columns: 1fr; }.education-dashboard, .education-knowledge { grid-template-columns: 1fr; }.education-dashboard__chart { grid-column: auto; }.education-receipt { align-items: flex-start; flex-direction: column; }.education-receipt small { margin-left: 0; } }

View file

@ -0,0 +1,45 @@
import { fireEvent, render, screen } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { EducationWorkLake } from './EducationWorkLake'
vi.mock('./SheetEditor', () => ({
SheetEditor: () => <div data-testid="education-sheet-editor">sheet</div>,
}))
describe('EducationWorkLake', () => {
beforeEach(() => {
localStorage.clear()
})
it('opens an editable sheet from a language instruction and emits a receipt', () => {
render(<EducationWorkLake locale="zh-CN" />)
fireEvent.change(screen.getByLabelText('告诉人格体你要处理什么'), {
target: { value: '整理一下这个表格里面的数据' },
})
fireEvent.click(screen.getByRole('button', { name: '运行指令' }))
expect(screen.getByTestId('education-sheet-editor')).toBeInTheDocument()
expect(screen.getByText('HL-MOD-SHEET-001')).toBeInTheDocument()
expect(screen.getByText(/已在应用内打开/)).toBeInTheDocument()
})
it('keeps a document draft inside the browser workspace', () => {
const { unmount } = render(<EducationWorkLake locale="zh-CN" />)
fireEvent.change(screen.getByLabelText('告诉人格体你要处理什么'), {
target: { value: '起草一份教研文档' },
})
fireEvent.click(screen.getByRole('button', { name: '运行指令' }))
fireEvent.change(screen.getByLabelText('教研文档内容'), {
target: { value: '八年级物理:光的折射' },
})
unmount()
render(<EducationWorkLake locale="zh-CN" />)
fireEvent.change(screen.getByLabelText('告诉人格体你要处理什么'), {
target: { value: '打开文档' },
})
fireEvent.click(screen.getByRole('button', { name: '运行指令' }))
expect(screen.getByLabelText('教研文档内容')).toHaveValue('八年级物理:光的折射')
})
})

View file

@ -0,0 +1,185 @@
import { useMemo, useState } from 'react'
import type { AppLocale, TranslationKey } from '../lib/i18n'
import { translate } from '../lib/i18n'
import {
educationMetricsFromSheet,
routeEducationInstruction,
type EducationSurface,
} from '../lib/educationWorkLake'
import { trackEvent } from '../lib/telemetry'
import { Button } from './ui/button'
import { Input } from './ui/input'
import { Textarea } from './ui/textarea'
import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs'
import { SheetEditor } from './SheetEditor'
import './EducationWorkLake.css'
const DOCUMENT_STORAGE_KEY = 'hololake:education-work-lake:document'
const SHEET_STORAGE_KEY = 'hololake:education-work-lake:sheet'
const DEFAULT_DOCUMENT = '教研主题:八年级物理学情分析\n\n在这里编辑教案、报告或协作文档。'
const DEFAULT_SHEET = [
'---',
'type: Note',
'_display: sheet',
'---',
'班级,平均分',
'一班,86',
'二班,91',
'三班,79',
'四班,88',
].join('\n')
type EducationReceipt = {
capabilityId: string
id: string
}
function readWorkspaceValue(key: string, fallback: string): string {
try {
return localStorage.getItem(key) ?? fallback
} catch {
return fallback
}
}
function storeWorkspaceValue(key: string, value: string): void {
try {
localStorage.setItem(key, value)
} catch {
// The workspace remains editable in memory when browser storage is unavailable.
}
}
function ComparisonChart({ metrics }: { metrics: ReturnType<typeof educationMetricsFromSheet> }) {
const maximum = Math.max(1, ...metrics.map((metric) => metric.value))
return (
<div className="education-chart" role="img" aria-label="班级平均分对比图">
{metrics.map((metric) => (
<div className="education-chart__row" key={metric.label}>
<span>{metric.label}</span>
<div><i style={{ width: `${Math.max(4, metric.value / maximum * 100)}%` }} /></div>
<strong>{metric.value}</strong>
</div>
))}
</div>
)
}
function Dashboard({ metrics }: { metrics: ReturnType<typeof educationMetricsFromSheet> }) {
const total = metrics.reduce((sum, metric) => sum + metric.value, 0)
const average = metrics.length > 0 ? Math.round(total / metrics.length * 10) / 10 : 0
const highest = metrics.reduce((current, metric) =>
metric.value > current.value ? metric : current, { label: '—', value: 0 })
return (
<div className="education-dashboard">
<article><span></span><strong>{metrics.length}</strong><small></small></article>
<article><span></span><strong>{average}</strong><small></small></article>
<article><span></span><strong>{highest.label}</strong><small>{highest.value} </small></article>
<div className="education-dashboard__chart"><ComparisonChart metrics={metrics} /></div>
</div>
)
}
function KnowledgeSurface() {
return (
<div className="education-knowledge">
<article><code>EDU-KNOW-001</code><strong></strong><p></p></article>
<article><code>EDU-KNOW-002</code><strong></strong><p></p></article>
<article><code>EDU-KNOW-003</code><strong></strong><p></p></article>
</div>
)
}
export function EducationWorkLake({ locale }: { locale: AppLocale }) {
const t = (key: TranslationKey) => translate(locale, key)
const [instruction, setInstruction] = useState('')
const [surface, setSurface] = useState<EducationSurface>('knowledge')
const [receipt, setReceipt] = useState<EducationReceipt | null>(null)
const [document, setDocument] = useState(() => readWorkspaceValue(DOCUMENT_STORAGE_KEY, DEFAULT_DOCUMENT))
const [sheet, setSheet] = useState(() => readWorkspaceValue(SHEET_STORAGE_KEY, DEFAULT_SHEET))
const metrics = useMemo(() => educationMetricsFromSheet(sheet), [sheet])
const runInstruction = () => {
const route = routeEducationInstruction(instruction)
const receiptId = `EDU-LOCAL-${Date.now().toString(36).toUpperCase()}`
setSurface(route.surface)
setReceipt({ capabilityId: route.capabilityId, id: receiptId })
trackEvent('education_work_lake_instruction_routed', {
capabilityId: route.capabilityId,
reason: route.reason,
surface: route.surface,
})
}
const updateDocument = (value: string) => {
setDocument(value)
storeWorkspaceValue(DOCUMENT_STORAGE_KEY, value)
}
const updateSheet = (_path: string, value: string) => {
setSheet(value)
storeWorkspaceValue(SHEET_STORAGE_KEY, value)
}
return (
<section className="education-work-lake">
<header className="education-work-lake__header">
<div>
<p>EDU-WORK-LAKE-001 · REMOTE FIRST</p>
<h1>{t('education.title')}</h1>
<span>{t('education.description')}</span>
</div>
<aside><i /><strong>JD-FD-PRIMARY</strong><small>{t('education.hostingState')}</small></aside>
</header>
<div className="education-command">
<Input
aria-label={t('education.instructionLabel')}
onChange={(event) => setInstruction(event.target.value)}
placeholder={t('education.instructionPlaceholder')}
value={instruction}
/>
<Button onClick={runInstruction}>{t('education.runInstruction')}</Button>
</div>
{receipt && (
<div className="education-receipt" role="status">
<span>{t('education.openedInsideApp')}</span>
<code>{receipt.capabilityId}</code>
<small>{receipt.id}</small>
</div>
)}
<Tabs value={surface} onValueChange={(value) => setSurface(value as EducationSurface)}>
<TabsList aria-label={t('education.surfaceNavigation')}>
<TabsTrigger value="document">{t('education.document')}</TabsTrigger>
<TabsTrigger value="sheet">{t('education.sheet')}</TabsTrigger>
<TabsTrigger value="chart">{t('education.chart')}</TabsTrigger>
<TabsTrigger value="dashboard">{t('education.dashboard')}</TabsTrigger>
<TabsTrigger value="knowledge">{t('education.knowledge')}</TabsTrigger>
</TabsList>
<TabsContent value="document">
<Textarea
aria-label={t('education.documentLabel')}
className="education-document"
onChange={(event) => updateDocument(event.target.value)}
value={document}
/>
</TabsContent>
<TabsContent value="sheet">
<div className="education-sheet">
<SheetEditor
content={sheet}
locale={locale}
onContentChange={updateSheet}
path="education-work-lake/class-comparison.md"
/>
</div>
</TabsContent>
<TabsContent value="chart"><ComparisonChart metrics={metrics} /></TabsContent>
<TabsContent value="dashboard"><Dashboard metrics={metrics} /></TabsContent>
<TabsContent value="knowledge"><KnowledgeSurface /></TabsContent>
</Tabs>
</section>
)
}

View file

@ -22,11 +22,13 @@ import { GUANGHU_THEMES, type GuanghuTheme } from '../lib/guanghuTheme'
import { planGuanghuLivingSystem } from '../utils/planGuanghuLivingSystem'
import { Button } from './ui/button'
import { HOLOLAKE_REPOSITORY_URL } from '../constants/feedback'
import { isEducationWorkLakePath } from '../lib/educationWorkLake'
import { GuanghuRouterConsole } from './GuanghuRouterConsole'
import { GuanghuWorldMap } from './GuanghuWorldMap'
import { GuanghuWorldLoginGate } from './GuanghuWorldLoginGate'
import { FifthDomainSystems } from './FifthDomainSystems'
import { EternalLakeHeartPage } from './EternalLakeHeartPage'
import { EducationWorkLake } from './EducationWorkLake'
import {
Dialog,
DialogContent,
@ -89,14 +91,17 @@ export function HoloLakeHome({
onOpenLocalWorkspace,
livingSystemTarget,
}: HoloLakeHomeProps) {
const [route, setRoute] = useState<ChannelRoute>('world')
const initialRoute: ChannelRoute = isEducationWorkLakePath(window.location.pathname)
? 'education-work-lake'
: 'world'
const [route, setRoute] = useState<ChannelRoute>(initialRoute)
const [livingPlan, setLivingPlan] = useState<GuanghuLivingSystemPlan>(() => (
createDeterministicLivingSystemPlan(createLivingSystemEvent({
currentRoute: 'world',
currentRoute: initialRoute,
eventId: 'system-start',
intent: 'navigate',
receiptIds: [],
requestedRoute: 'world',
requestedRoute: initialRoute,
worldOpen: false,
}))
))
@ -407,6 +412,10 @@ export function HoloLakeHome({
)
}
if (route === 'education-work-lake') {
return <EducationWorkLake locale={locale} />
}
if (route === 'servers') {
return (
<section className="channel-stage channel-stage--node">
@ -453,6 +462,7 @@ export function HoloLakeHome({
<p className="channel-stage__lead">{t('hololake.channel.heartbeatDescription')}</p>
<div className="channel-grid channel-grid--three">
<RouteCard eyebrow="PERSONA · AI AGENT" title="人格体工作舱" description="与当前频道的人格体持续协作;保留多 Agent、模型选择、权限模式、工具调用、执行回执与历史会话。" action="打开人格体工作舱" onOpen={onOpenAiWorkspace ? openAiWorkspace : undefined} />
<RouteCard eyebrow="INDUSTRY · EDUCATION" title="教育工作湖" description="通过语言指令调出并编辑文档、表格、对比图、仪表盘与知识。第一阶段权限保留在应用内。" action="进入教育工作湖" onOpen={() => navigate('education-work-lake')} />
<RouteCard eyebrow="MODULE · KNOWLEDGE" title={t('hololake.home.moduleKnowledgeTitle')} description={t('hololake.home.moduleKnowledgeDescription')} action={t('hololake.channel.openKnowledge')} onOpen={openKnowledgeBase} />
<RouteCard eyebrow="LOCAL COMPUTER · LIVE FOLDER" title="本地电脑工作区" description="选择电脑上的任意项目文件夹。编程 AI 直接在原文件夹写入页面,光湖实时显示,不再复制到旧笔记库。" action="打开本地文件夹" onOpen={onOpenLocalWorkspace ? openLocalWorkspace : undefined} />
<RouteCard eyebrow="MODULE · VIDEO AI" title={t('hololake.channel.videoAiTitle')} description={t('hololake.channel.videoAiDescription')} status={t('hololake.channel.prototypeMounted')} />
@ -492,6 +502,7 @@ export function HoloLakeHome({
<Button data-active={route === 'world'} variant="ghost" onClick={() => navigate('world')}><i data-color="cyan" /></Button>
<Button variant="ghost" onClick={openKnowledgeBase}><i data-color="blue" /></Button>
<Button disabled={!onOpenAiWorkspace} variant="ghost" onClick={openAiWorkspace}><i data-color="violet" />Agent </Button>
<Button data-active={route === 'education-work-lake'} variant="ghost" onClick={() => navigate('education-work-lake')}><i data-color="green" /></Button>
<Button disabled={!onOpenLocalWorkspace} variant="ghost" onClick={openLocalWorkspace}><i data-color="amber" /></Button>
</>}
{fifthDomainOpen && <>

View file

@ -57,11 +57,6 @@ describe('useAiAgentsStatus', () => {
expect(result.current.claude_code).toEqual({ status: 'installed', version: '1.0.20' })
expect(result.current.codex).toEqual({ status: 'missing', version: null })
expect(result.current.copilot).toEqual({ status: 'installed', version: '1.0.58' })
expect(result.current.opencode).toEqual({ status: 'installed', version: '0.3.1' })
expect(result.current.pi).toEqual({ status: 'installed', version: '0.70.2' })
expect(result.current.antigravity).toEqual({ status: 'installed', version: 'Antigravity CLI 1.0.0' })
expect(result.current.kiro).toEqual({ status: 'installed', version: '0.4.0' })
expect(result.current.hermes).toEqual({ status: 'installed', version: 'Hermes Agent 0.16.0' })
})
})

View file

@ -1085,9 +1085,9 @@ describe('reload-vault command', () => {
expect(cmd).toBeDefined()
expect(cmd!.label).toBe('Switch AI Agent to Codex')
expect(findCommand(result.current, 'switch-ai-agent-copilot')).toBeDefined()
expect(findCommand(result.current, 'switch-ai-agent-opencode')).toBeDefined()
expect(findCommand(result.current, 'switch-ai-agent-pi')).toBeDefined()
expect(findCommand(result.current, 'switch-ai-agent-antigravity')).toBeDefined()
expect(findCommand(result.current, 'switch-ai-agent-opencode')).toBeUndefined()
expect(findCommand(result.current, 'switch-ai-agent-pi')).toBeUndefined()
expect(findCommand(result.current, 'switch-ai-agent-antigravity')).toBeUndefined()
cmd!.execute()
expect(onSetDefaultAiAgent).toHaveBeenCalledWith('codex')

View file

@ -0,0 +1,27 @@
import { renderHook } from '@testing-library/react'
import { describe, expect, it } from 'vitest'
import { useStartupScreenState } from './useStartupScreenState'
describe('useStartupScreenState', () => {
it('does not let local vault onboarding replace the HoloLake world entry', () => {
const { result } = renderHook(() => useStartupScreenState({
aiAgentsPromptVisible: false,
isNoteWindow: false,
onboardingState: { status: 'welcome' },
runtimeMissingVaultPath: '/blocked/config/path',
selectedVaultPath: null,
settingsLoaded: true,
showMcpSetupDialog: false,
telemetryConsent: null,
vaultIsLoading: false,
vaultSwitcher: {
allVaults: [],
loaded: true,
vaultPath: '',
},
worldEntryVisible: true,
}))
expect(result.current.shouldShowStartupScreen).toBe(false)
})
})

View file

@ -22,6 +22,7 @@ interface UseStartupScreenStateArgs {
telemetryConsent: boolean | null
vaultIsLoading: boolean
vaultSwitcher: StartupVaultSwitcherState
worldEntryVisible?: boolean
}
interface StartupScreenState {
@ -122,6 +123,7 @@ export function useStartupScreenState({
telemetryConsent,
vaultIsLoading,
vaultSwitcher,
worldEntryVisible = false,
}: UseStartupScreenStateArgs): StartupScreenState {
const shouldResumeFreshStartOnboarding = useMemo(
() => shouldResumeFreshStart(onboardingState, selectedVaultPath, vaultSwitcher),
@ -129,7 +131,7 @@ export function useStartupScreenState({
)
const isStartupLoading = !isNoteWindow && onboardingState.status === 'loading'
const shouldShowStartupScreen = shouldShowStartupScreenForState({
const shouldShowStartupScreen = !worldEntryVisible && shouldShowStartupScreenForState({
aiAgentsPromptVisible,
isNoteWindow,
isStartupLoading,

View file

@ -11,12 +11,12 @@ describe('aiAgents helpers', () => {
expect(normalizeStoredAiAgent('claude_code')).toBe('claude_code')
expect(normalizeStoredAiAgent('codex')).toBe('codex')
expect(normalizeStoredAiAgent('copilot')).toBe('copilot')
expect(normalizeStoredAiAgent('opencode')).toBe('opencode')
expect(normalizeStoredAiAgent('pi')).toBe('pi')
expect(normalizeStoredAiAgent('antigravity')).toBe('antigravity')
expect(normalizeStoredAiAgent('gemini')).toBe('antigravity')
expect(normalizeStoredAiAgent('kiro')).toBe('kiro')
expect(normalizeStoredAiAgent('hermes')).toBe('hermes')
expect(normalizeStoredAiAgent('opencode')).toBeNull()
expect(normalizeStoredAiAgent('pi')).toBeNull()
expect(normalizeStoredAiAgent('antigravity')).toBeNull()
expect(normalizeStoredAiAgent('gemini')).toBeNull()
expect(normalizeStoredAiAgent('kiro')).toBeNull()
expect(normalizeStoredAiAgent('hermes')).toBeNull()
expect(normalizeStoredAiAgent('cursor')).toBeNull()
})
@ -40,29 +40,25 @@ describe('aiAgents helpers', () => {
expect(statuses.claude_code).toEqual({ status: 'installed', version: '1.0.20' })
expect(statuses.codex).toEqual({ status: 'missing', version: null })
expect(statuses.copilot).toEqual({ status: 'installed', version: '1.0.58' })
expect(statuses.opencode).toEqual({ status: 'installed', version: '0.3.1' })
expect(statuses.pi).toEqual({ status: 'installed', version: '0.70.2' })
expect(statuses.antigravity).toEqual({ status: 'installed', version: 'Antigravity CLI 1.0.0' })
expect(statuses.kiro).toEqual({ status: 'installed', version: '0.12.0' })
expect(statuses.hermes).toEqual({ status: 'installed', version: 'Hermes Agent 0.16.0' })
expect(statuses.opencode).toBeUndefined()
expect(statuses.pi).toBeUndefined()
expect(statuses.antigravity).toBeUndefined()
expect(statuses.kiro).toBeUndefined()
expect(statuses.hermes).toBeUndefined()
})
it('normalizes legacy Gemini status payloads to Antigravity', () => {
it('does not expose legacy Gemini status payloads', () => {
const statuses = normalizeAiAgentsStatus({
gemini: { installed: true, version: '0.5.1' },
})
expect(statuses.antigravity).toEqual({ status: 'installed', version: '0.5.1' })
expect(statuses.antigravity).toBeUndefined()
})
it('cycles through the supported agents', () => {
expect(getNextAiAgentId('claude_code')).toBe('codex')
expect(getNextAiAgentId('codex')).toBe('copilot')
expect(getNextAiAgentId('copilot')).toBe('opencode')
expect(getNextAiAgentId('opencode')).toBe('pi')
expect(getNextAiAgentId('pi')).toBe('antigravity')
expect(getNextAiAgentId('antigravity')).toBe('kiro')
expect(getNextAiAgentId('kiro')).toBe('hermes')
expect(getNextAiAgentId('hermes')).toBe('claude_code')
expect(getNextAiAgentId('copilot')).toBe('claude_code')
expect(getNextAiAgentId('opencode')).toBe('claude_code')
})
})

View file

@ -40,37 +40,40 @@ export const AI_AGENT_DEFINITIONS: readonly AiAgentDefinition[] = [
shortLabel: 'Copilot',
installUrl: 'https://docs.github.com/en/copilot/how-tos/copilot-cli/set-up-copilot-cli/install-copilot-cli',
},
{
] as const
const LEGACY_AI_AGENT_DEFINITIONS: Partial<Record<AiAgentId, AiAgentDefinition>> = {
opencode: {
id: 'opencode',
label: 'OpenCode',
shortLabel: 'OpenCode',
installUrl: 'https://opencode.ai/docs/',
},
{
pi: {
id: 'pi',
label: 'Pi',
shortLabel: 'Pi',
installUrl: 'https://pi.dev',
},
{
antigravity: {
id: 'antigravity',
label: 'Antigravity CLI',
label: 'Antigravity',
shortLabel: 'Antigravity',
installUrl: 'https://antigravity.google/docs/cli/install',
},
{
kiro: {
id: 'kiro',
label: 'Kiro',
shortLabel: 'Kiro',
installUrl: 'https://kiro.dev/docs/cli',
},
{
hermes: {
id: 'hermes',
label: 'Hermes Agent',
shortLabel: 'Hermes',
installUrl: 'https://hermes-agent.nousresearch.com/docs/getting-started/quickstart',
},
] as const
}
export function createAiAgentAvailability(status: AiAgentStatus = 'checking', version: string | null = null): AiAgentAvailability {
return { status, version }
@ -94,7 +97,6 @@ export function createMissingAiAgentsStatus(): AiAgentsStatus {
}
export function normalizeStoredAiAgent(value: string | null | undefined): AiAgentId | null {
if (value === 'gemini') return 'antigravity'
if (AI_AGENT_DEFINITIONS.some((definition) => definition.id === value)) return value as AiAgentId
return null
}
@ -104,7 +106,9 @@ export function resolveDefaultAiAgent(value: string | null | undefined): AiAgent
}
export function getAiAgentDefinition(agent: AiAgentId): AiAgentDefinition {
return AI_AGENT_DEFINITIONS.find((definition) => definition.id === agent) ?? AI_AGENT_DEFINITIONS[0]
return AI_AGENT_DEFINITIONS.find((definition) => definition.id === agent)
?? LEGACY_AI_AGENT_DEFINITIONS[agent]
?? AI_AGENT_DEFINITIONS[0]
}
function normalizeAvailability(agent: { installed?: boolean | null; version?: string | null } | null | undefined): AiAgentAvailability {
@ -116,7 +120,7 @@ function normalizeAvailability(agent: { installed?: boolean | null; version?: st
}
function payloadForAgent(payload: AiAgentsStatusPayload | null | undefined, agent: AiAgentId) {
return agent === 'antigravity' ? payload?.antigravity ?? payload?.gemini : payload?.[agent]
return payload?.[agent]
}
export function normalizeAiAgentsStatus(payload: AiAgentsStatusPayload | null | undefined): AiAgentsStatus {

View file

@ -59,7 +59,7 @@ describe('ai target provider contract', () => {
})
})
it('accepts legacy agent ids saved in the default target field', () => {
it('falls back safely when the default target contains a hidden legacy agent', () => {
const target = resolveAiTarget({
default_ai_agent: 'claude_code',
default_ai_target: 'kiro',
@ -67,12 +67,12 @@ describe('ai target provider contract', () => {
expect(target).toMatchObject({
kind: 'agent',
agent: 'kiro',
id: 'agent:kiro',
agent: 'claude_code',
id: 'agent:claude_code',
})
})
it('uses the legacy default agent when a saved agent target is stale', () => {
it('falls back safely when the saved legacy default agent is no longer selectable', () => {
const target = resolveAiTarget({
default_ai_agent: 'kiro',
default_ai_target: 'agent:claude_code',
@ -80,8 +80,8 @@ describe('ai target provider contract', () => {
expect(target).toMatchObject({
kind: 'agent',
agent: 'kiro',
id: 'agent:kiro',
agent: 'claude_code',
id: 'agent:claude_code',
})
})

View file

@ -0,0 +1,46 @@
import { describe, expect, it } from 'vitest'
import {
educationMetricsFromSheet,
isEducationWorkLakePath,
routeEducationInstruction,
} from './educationWorkLake'
describe('routeEducationInstruction', () => {
it.each([
['整理一下这个表格里面的数据', 'sheet'],
['对比一下并做一个对比图', 'chart'],
['把结果做成仪表盘', 'dashboard'],
['起草一份教研文档', 'document'],
['找一下课程知识库', 'knowledge'],
] as const)('routes %s to %s', (instruction, expectedSurface) => {
expect(routeEducationInstruction(instruction).surface).toBe(expectedSurface)
})
it('returns a stable safe fallback without claiming execution', () => {
expect(routeEducationInstruction('帮我看看')).toMatchObject({
surface: 'knowledge',
capabilityId: 'HL-MOD-KNOWLEDGE-001',
reason: 'safe-fallback',
})
})
it('prioritizes dashboards over their underlying chart keyword', () => {
expect(routeEducationInstruction('把对比图放进仪表盘')).toMatchObject({
surface: 'dashboard',
capabilityId: 'HL-MOD-DASHBOARD-001',
})
})
it('projects numeric spreadsheet rows into chart metrics', () => {
expect(educationMetricsFromSheet('---\n_display: sheet\n---\n班级,平均分\n一班,86\n二班,91\n备注,待补')).toEqual([
{ label: '一班', value: 86 },
{ label: '二班', value: 91 },
])
})
it('recognizes the server-hosted education entry without matching similar words', () => {
expect(isEducationWorkLakePath('/jd/education/')).toBe(true)
expect(isEducationWorkLakePath('/jd/education/assets/app.js')).toBe(true)
expect(isEducationWorkLakePath('/jd/educational/')).toBe(false)
})
})

View file

@ -0,0 +1,92 @@
export const EDUCATION_SURFACES = [
'document',
'sheet',
'chart',
'dashboard',
'knowledge',
] as const
export type EducationSurface = typeof EDUCATION_SURFACES[number]
export type EducationInstructionRoute = {
capabilityId: string
reason: 'keyword-match' | 'safe-fallback'
surface: EducationSurface
}
export type EducationMetric = {
label: string
value: number
}
const ROUTES: ReadonlyArray<{
capabilityId: string
keywords: readonly string[]
surface: EducationSurface
}> = [
{
capabilityId: 'HL-MOD-DASHBOARD-001',
keywords: ['仪表盘', '看板', 'dashboard'],
surface: 'dashboard',
},
{
capabilityId: 'HL-MOD-CHART-001',
keywords: ['对比图', '图表', '柱状图', '折线图', 'chart'],
surface: 'chart',
},
{
capabilityId: 'HL-MOD-SHEET-001',
keywords: ['表格', '数据', '计算', '统计', 'sheet', 'spreadsheet'],
surface: 'sheet',
},
{
capabilityId: 'HL-MOD-DOC-001',
keywords: ['文档', '教案', '报告', '总结', '起草', 'document'],
surface: 'document',
},
{
capabilityId: 'HL-MOD-KNOWLEDGE-001',
keywords: ['知识库', '课程', '资料', '搜索', '找', 'knowledge'],
surface: 'knowledge',
},
]
export function routeEducationInstruction(instruction: string): EducationInstructionRoute {
const normalizedInstruction = instruction.trim().toLocaleLowerCase()
const route = ROUTES.find((candidate) =>
candidate.keywords.some((keyword) => normalizedInstruction.includes(keyword)))
if (route) {
return {
capabilityId: route.capabilityId,
reason: 'keyword-match',
surface: route.surface,
}
}
return {
capabilityId: 'HL-MOD-KNOWLEDGE-001',
reason: 'safe-fallback',
surface: 'knowledge',
}
}
export function educationMetricsFromSheet(content: string): EducationMetric[] {
const body = content.includes('---')
? content.split('---').slice(2).join('---')
: content
return body
.trim()
.split(/\r?\n/)
.slice(1)
.map((row) => {
const [label = '', value = ''] = row.split(',')
return { label: label.trim(), value: Number(value.trim()) }
})
.filter((metric) => metric.label.length > 0 && Number.isFinite(metric.value))
}
export function isEducationWorkLakePath(pathname: string): boolean {
return pathname.split('/').filter(Boolean).includes('education')
}

View file

@ -9,6 +9,7 @@ export const GUANGHU_CHANNEL_ROUTES = [
'eternal-lake-heart',
'light-lake',
'heartbeat-core',
'education-work-lake',
'love-core',
'servers',
] as const

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1000,6 +1000,20 @@
"locale.ukUA": "Ukrainian",
"locale.svSE": "Swedish",
"locale.skSK": "Slovak",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content",
"hololake.home.ariaLabel": "HoloLake Era world entry",
"hololake.home.eyebrow": "Guanghu Language World · Fifth Domain",
"hololake.home.lead": "Language-personality-driven operating system",

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}

View file

@ -1000,6 +1000,20 @@
"locale.ukUA": "乌克兰语",
"locale.svSE": "瑞典语",
"locale.skSK": "斯洛伐克语",
"education.title": "教育工作湖",
"education.description": "人格体通过语言指令调出并编辑文档、表格、对比图、仪表盘与知识,所有呈现在用户设备完成。",
"education.hostingState": "临时隔离承载",
"education.instructionLabel": "告诉人格体你要处理什么",
"education.instructionPlaceholder": "例如:整理这个表格,并生成一个对比仪表盘",
"education.runInstruction": "运行指令",
"education.openedInsideApp": "已在应用内打开",
"education.surfaceNavigation": "教育工作区界面",
"education.document": "文档",
"education.sheet": "表格",
"education.chart": "对比图",
"education.dashboard": "仪表盘",
"education.knowledge": "知识",
"education.documentLabel": "教研文档内容",
"hololake.home.ariaLabel": "HoloLake Era 世界入口",
"hololake.home.eyebrow": "光湖语言世界 · 第五域",
"hololake.home.lead": "语言人格驱动操作系统",

View file

@ -1121,5 +1121,19 @@
"hololake.login.errorTitle": "The world entrance did not open",
"hololake.login.retry": "Try again",
"hololake.login.privacy": "HoloLake never receives or stores your mailbox address. The Fifth Domain controller resolves the bound Guanghu number.",
"hololake.login.nodeReady": "Fifth Domain world entrance ready"
"hololake.login.nodeReady": "Fifth Domain world entrance ready",
"education.title": "Education Work Lake",
"education.description": "A client-rendered workspace where the persona can open and edit documents, spreadsheets, charts, dashboards, and knowledge through language instructions.",
"education.hostingState": "Temporary isolated hosting",
"education.instructionLabel": "Tell the persona what you want to work on",
"education.instructionPlaceholder": "For example: organize this spreadsheet and create a comparison dashboard",
"education.runInstruction": "Run instruction",
"education.openedInsideApp": "Opened inside the app",
"education.surfaceNavigation": "Education workspace surfaces",
"education.document": "Document",
"education.sheet": "Spreadsheet",
"education.chart": "Chart",
"education.dashboard": "Dashboard",
"education.knowledge": "Knowledge",
"education.documentLabel": "Education document content"
}