ltcai 7.7.0 → 7.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +24 -16
  2. package/docs/CHANGELOG.md +20 -0
  3. package/frontend/src/App.tsx +3 -1
  4. package/frontend/src/components/ProductFlow.tsx +19 -23
  5. package/frontend/src/components/onboarding/RecommendationScreen.tsx +11 -12
  6. package/frontend/src/features/brain/BrainConversation.tsx +107 -108
  7. package/frontend/src/features/brain/BrainHome.tsx +32 -33
  8. package/frontend/src/i18n.ts +53 -19
  9. package/frontend/src/pages/Ask.tsx +11 -2
  10. package/frontend/src/pages/Brain.tsx +12 -2
  11. package/frontend/src/styles.css +390 -65
  12. package/lattice_brain/__init__.py +1 -1
  13. package/lattice_brain/runtime/multi_agent.py +1 -1
  14. package/latticeai/__init__.py +1 -1
  15. package/latticeai/core/marketplace.py +1 -1
  16. package/latticeai/core/workspace_os.py +1 -1
  17. package/latticeai/services/architecture_readiness.py +1 -1
  18. package/latticeai/services/product_readiness.py +15 -15
  19. package/package.json +1 -1
  20. package/scripts/product_readiness.py +1 -1
  21. package/src-tauri/Cargo.toml +1 -1
  22. package/src-tauri/tauri.conf.json +1 -1
  23. package/static/app/asset-manifest.json +28 -28
  24. package/static/app/assets/Act-DOvf59ru.js +2 -0
  25. package/static/app/assets/{Act-CX5hL0Z3.js.map → Act-DOvf59ru.js.map} +1 -1
  26. package/static/app/assets/Brain-C7_0mEiI.js +322 -0
  27. package/static/app/assets/Brain-C7_0mEiI.js.map +1 -0
  28. package/static/app/assets/Capture-B9Tlhzqr.js +2 -0
  29. package/static/app/assets/{Capture-CdFSrZnA.js.map → Capture-B9Tlhzqr.js.map} +1 -1
  30. package/static/app/assets/{Library-CZFVGSmG.js → Library-BJPEEm5O.js} +2 -2
  31. package/static/app/assets/{Library-CZFVGSmG.js.map → Library-BJPEEm5O.js.map} +1 -1
  32. package/static/app/assets/System-D6t9jo9V.js +2 -0
  33. package/static/app/assets/{System-CMoOoUrZ.js.map → System-D6t9jo9V.js.map} +1 -1
  34. package/static/app/assets/index-C7g26IF6.css +2 -0
  35. package/static/app/assets/index-DbcEYJQ2.js +17 -0
  36. package/static/app/assets/{index-B4_drWel.js.map → index-DbcEYJQ2.js.map} +1 -1
  37. package/static/app/assets/primitives-CD38lt4n.js +2 -0
  38. package/static/app/assets/{primitives-CFhU5Rka.js.map → primitives-CD38lt4n.js.map} +1 -1
  39. package/static/app/assets/textarea-BZk6ybp5.js +2 -0
  40. package/static/app/assets/{textarea-uBpGDOJM.js.map → textarea-BZk6ybp5.js.map} +1 -1
  41. package/static/app/index.html +2 -2
  42. package/frontend/src/components/BrainConversation.tsx +0 -533
  43. package/frontend/src/components/FirstRunGuide.tsx +0 -99
  44. package/static/app/assets/Act-CX5hL0Z3.js +0 -2
  45. package/static/app/assets/Brain-m19en5wz.js +0 -322
  46. package/static/app/assets/Brain-m19en5wz.js.map +0 -1
  47. package/static/app/assets/Capture-CdFSrZnA.js +0 -2
  48. package/static/app/assets/System-CMoOoUrZ.js +0 -2
  49. package/static/app/assets/index-B4_drWel.js +0 -17
  50. package/static/app/assets/index-DjpDeE0c.css +0 -2
  51. package/static/app/assets/primitives-CFhU5Rka.js +0 -2
  52. package/static/app/assets/textarea-uBpGDOJM.js +0 -2
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Lattice AI
2
2
 
3
- **Lattice AI 7.7 is the complete, finished local-first Digital Brain product. It keeps your knowledge durable across any AI model now undeniably a production-grade professional tool.**
3
+ **Lattice AI 7.8 is the glanceable local-first Digital Brain product. It keeps your knowledge durable across any AI model, with Brain Chat Home rebuilt around immediate use instead of dashboard clutter.**
4
4
 
5
5
  **Lattice AI는 모델이 바뀌어도 내 지식과 맥락을 보존하는 로컬 우선 AI 브레인입니다.**
6
6
 
@@ -11,7 +11,7 @@ downloads, update checks, and other external communication happen only after
11
11
  explicit consent.
12
12
 
13
13
  It is not a ChatGPT clone, a model launcher, a graph database, or a note app.
14
- It is the finished private AI memory layer wrapped in a Living Brain experience — 7.7 complete product.
14
+ It is the finished private AI memory layer wrapped in a Living Brain experience — now with a cleaner 7.8 Brain Chat Home.
15
15
 
16
16
  [![PyPI Version](https://img.shields.io/pypi/v/ltcai?label=PyPI)](https://pypi.org/project/ltcai/)
17
17
  [![npm Version](https://img.shields.io/npm/v/ltcai?label=npm)](https://www.npmjs.com/package/ltcai)
@@ -64,6 +64,10 @@ You need Lattice AI when:
64
64
 
65
65
  ## Living Brain Flow
66
66
 
67
+ The screenshots below are release evidence captures. The 7.8 line keeps the
68
+ same first-run flow and rebuilds the Brain Chat Home layout so chat and the
69
+ composer are the first visible action.
70
+
67
71
  ### 1. Wake Brain
68
72
 
69
73
  The first screen makes the Brain the product. It explains the three-step path:
@@ -206,30 +210,33 @@ See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for developer workflow details.
206
210
 
207
211
  ## Current Release
208
212
 
209
- The current release is **7.7.0 — Complete Finished Product**:
213
+ The current release is **7.8.0 — Brain Chat Home UX Simplification**:
210
214
 
211
- - Brain Home now opens with a product command center: readiness score, next best
212
- action, memory/topic/source/proof metrics, and direct actions for adding
213
- sources, inspecting topics, verifying proof, and managing ownership.
214
- - Review Center release evidence is backed by fixtures and tests, with polished
215
- unavailable states instead of raw JSON parse errors.
215
+ - Brain Chat Home now opens with chat, starter prompts, and the composer as the
216
+ first action. Source ingestion, readiness, proof, timeline, and care controls
217
+ move behind a single utility drawer instead of occupying the first viewport.
218
+ - The default Brain surface now keeps workspace navigation visible, removes
219
+ permanent marketing intro copy, and only reveals deep memory controls after
220
+ the user chooses to travel deeper.
221
+ - Obsolete Brain conversation and first-run guide components were removed so the
222
+ canonical Brain/onboarding implementation is the only path.
216
223
  - Product readiness is machine-checkable through `product_readiness()`, which
217
224
  composes the architecture contract with exact artifact/docs/workflow evidence.
218
225
  - CI and release workflows now run frontend lint/typecheck/build gates plus the
219
- 7.7 product readiness score before a tag can be treated as release-safe.
226
+ 7.8 product readiness score before a tag can be treated as release-safe.
220
227
 
221
- Expected artifacts for 7.7.0 release must use exact filenames:
228
+ Expected artifacts for 7.8.0 release must use exact filenames:
222
229
 
223
- - `dist/ltcai-7.7.0-py3-none-any.whl`
224
- - `dist/ltcai-7.7.0.tar.gz`
225
- - `ltcai-7.7.0.tgz`
226
- - `dist/ltcai-7.7.0.vsix`
227
- - `src-tauri/target/release/bundle/dmg/Lattice AI_7.7.0_aarch64.dmg`
230
+ - `dist/ltcai-7.8.0-py3-none-any.whl`
231
+ - `dist/ltcai-7.8.0.tar.gz`
232
+ - `ltcai-7.8.0.tgz`
233
+ - `dist/ltcai-7.8.0.vsix`
234
+ - `src-tauri/target/release/bundle/dmg/Lattice AI_7.8.0_aarch64.dmg`
228
235
 
229
236
  Do not upload `dist/*`. Package registry publishing remains owner-run.
230
237
 
231
238
  See [docs/ROADMAP_RECOMMENDATIONS.md](docs/ROADMAP_RECOMMENDATIONS.md) for the
232
- strategic roadmap slices applied through 7.7.0 and the follow-up tracks.
239
+ strategic roadmap slices applied through 7.8.0 and the follow-up tracks.
233
240
 
234
241
  ## Known Limitations
235
242
 
@@ -246,6 +253,7 @@ strategic roadmap slices applied through 7.7.0 and the follow-up tracks.
246
253
 
247
254
  | Version | Theme |
248
255
  | --- | --- |
256
+ | 7.8.0 | Brain Chat Home UX Simplification: chat-first first viewport, visible workspace navigation, collapsed source/status utilities, hidden default depth controls, and removal of obsolete Brain UX components |
249
257
  | 7.7.0 | Complete Product Polish: command-center Brain Home, repaired Review Center evidence, exact 7.7 docs/artifacts, product readiness gate, and stronger CI/release checks |
250
258
  | 7.6.0 | Brain-Centered UX & Architecture Closure: Wake Brain first-run surface, concentric memory rings with direct depth controls, and machine-checkable closure of the two architecture/UX review files |
251
259
  | 7.5.0 | Runtime Debt Burn-down & Release Risk Cleanup: API consumers get normalized contract views, retrieval quality uses a 250+ record corpus fixture, stale artifact risk is removed, npm audit is clean, and Tauri is updated past the old block warning |
package/docs/CHANGELOG.md CHANGED
@@ -4,6 +4,26 @@ The top entry is either the current unreleased main-branch work or the current
4
4
  release line. Older entries are historical and may describe behavior as it
5
5
  existed at that release.
6
6
 
7
+ ## [Unreleased]
8
+
9
+ ## [7.8.0] - 2026-06-22
10
+
11
+ ### Changed
12
+ - Rebuilt Brain Chat Home around immediate conversation: chat purpose, starter
13
+ prompts, and the composer now occupy the first viewport.
14
+ - Collapsed source ingestion, readiness, proof, timeline, overview, model
15
+ continuity, and care controls behind one utility drawer.
16
+ - Kept workspace navigation visible on the default Brain surface.
17
+ - Hid default depth controls until the user intentionally travels deeper.
18
+ - Integrated the six post-7.7 UX drafts into the canonical Brain experience:
19
+ first-run value cards, stronger recommendation affordances, product-toned
20
+ Brain Home copy, and routed legacy Brain conversation entry points to the
21
+ canonical Brain Home surface.
22
+ - Removed obsolete Brain conversation and first-run guide components.
23
+ - Moved draft onboarding polish out of inline styles and into the shared design
24
+ stylesheet with responsive behavior and bilingual copy.
25
+ - Updated architecture and product readiness targets to 7.8.0.
26
+
7
27
  ## [7.7.0] - 2026-06-22
8
28
 
9
29
  > 7.7.0 marks the complete, finished product stage for Lattice AI.
@@ -88,7 +88,9 @@ export default function App() {
88
88
  </React.Suspense>
89
89
  </BrainShell>
90
90
  ) : (
91
- <BrainHome brainState={brainState} intensity={intensity} onBrainChange={setBrain} />
91
+ <BrainShell active="brain">
92
+ <BrainHome brainState={brainState} intensity={intensity} onBrainChange={setBrain} />
93
+ </BrainShell>
92
94
  )}
93
95
  </div>
94
96
  );
@@ -3,7 +3,7 @@ import { latticeApi } from "@/api/client";
3
3
  import { type BrainState, LivingBrain } from "@/components/LivingBrain";
4
4
  import { t } from "@/i18n";
5
5
  import { useAppStore } from "@/store/appStore";
6
- import { ArrowRight, Shield, Zap, Cpu } from "lucide-react";
6
+ import { ArrowRight, Cpu, Shield, Zap } from "lucide-react";
7
7
  import {
8
8
  AnalysisScreen,
9
9
  InstallScreen,
@@ -107,25 +107,26 @@ export function ProductFlow({ onComplete }: { onComplete: () => void }) {
107
107
 
108
108
  function WakeBrainScreen({ onWake, onUseExisting }: { onWake: () => void; onUseExisting: () => void }) {
109
109
  const language = useAppStore((state) => state.language);
110
+ const valueCards = [
111
+ { icon: Shield, label: t(language, "flow.wake.value.local.k"), body: t(language, "flow.wake.value.local.v") },
112
+ { icon: Zap, label: t(language, "flow.wake.value.instant.k"), body: t(language, "flow.wake.value.instant.v") },
113
+ { icon: Cpu, label: t(language, "flow.wake.value.brain.k"), body: t(language, "flow.wake.value.brain.v") },
114
+ ];
110
115
  return (
111
116
  <section className="ritual-wake" aria-label={t(language, "flow.wake.aria")}>
112
117
  <div className="ritual-title">{t(language, "flow.wake.title")}</div>
113
118
  <div className="ritual-subtitle">{t(language, "flow.wake.body")}</div>
114
119
 
115
- {/* 직관적 가치 제안 - 첫눈에 쓰고 싶게 */}
116
- <div className="ritual-value-grid" style={{display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: "0.6rem", margin: "1.25rem auto", maxWidth: "620px"}}>
117
- <div style={{border: "1px solid hsl(var(--border)/0.5)", borderRadius: "12px", padding: "0.7rem 0.8rem", background: "hsl(var(--surface))", textAlign: "left"}}>
118
- <div style={{display: "flex", alignItems: "center", gap: "0.4rem", color: "hsl(var(--brain-core))", fontSize: "0.75rem", fontWeight: 700}}><Shield size={14}/> 완전 로컬</div>
119
- <div style={{fontSize: "0.85rem", marginTop: "0.2rem", fontWeight: 600}}>모든 지식과 대화가 이 컴퓨터에만</div>
120
- </div>
121
- <div style={{border: "1px solid hsl(var(--border)/0.5)", borderRadius: "12px", padding: "0.7rem 0.8rem", background: "hsl(var(--surface))", textAlign: "left"}}>
122
- <div style={{display: "flex", alignItems: "center", gap: "0.4rem", color: "hsl(var(--brain-core))", fontSize: "0.75rem", fontWeight: 700}}><Zap size={14}/> 직관적 즉시</div>
123
- <div style={{fontSize: "0.85rem", marginTop: "0.2rem", fontWeight: 600}}>복잡한 설정 없이 3단계로 바로 시작</div>
124
- </div>
125
- <div style={{border: "1px solid hsl(var(--border)/0.5)", borderRadius: "12px", padding: "0.7rem 0.8rem", background: "hsl(var(--surface))", textAlign: "left"}}>
126
- <div style={{display: "flex", alignItems: "center", gap: "0.4rem", color: "hsl(var(--brain-core))", fontSize: "0.75rem", fontWeight: 700}}><Cpu size={14}/> 영원한 Brain</div>
127
- <div style={{fontSize: "0.85rem", marginTop: "0.2rem", fontWeight: 600}}>모델 바꿔도 지식은 그대로 영원히</div>
128
- </div>
120
+ <div className="ritual-value-grid" aria-label={t(language, "flow.wake.value.aria")}>
121
+ {valueCards.map((card) => {
122
+ const Icon = card.icon;
123
+ return (
124
+ <div className="ritual-value-card" key={card.label}>
125
+ <div className="ritual-value-label"><Icon size={14} /> {card.label}</div>
126
+ <div className="ritual-value-body">{card.body}</div>
127
+ </div>
128
+ );
129
+ })}
129
130
  </div>
130
131
 
131
132
  <div className="ritual-wake-plan" aria-label={t(language, "flow.wake.plan.aria")}>
@@ -143,20 +144,15 @@ function WakeBrainScreen({ onWake, onUseExisting }: { onWake: () => void; onUseE
143
144
  </div>
144
145
  </div>
145
146
 
146
- <div className="ritual-button-row" style={{marginTop: "1.25rem"}}>
147
- <button
148
- type="button"
149
- className="ritual-full-button"
150
- onClick={onWake}
151
- style={{fontSize: "1.05rem", padding: "0.85rem 2.2rem", display: "inline-flex", alignItems: "center", gap: "0.5rem"}}
152
- >
147
+ <div className="ritual-button-row ritual-button-row-primary">
148
+ <button type="button" className="ritual-full-button ritual-full-button-primary" onClick={onWake}>
153
149
  {t(language, "flow.wake.primary")} <ArrowRight size={18} />
154
150
  </button>
155
151
  <button type="button" className="ritual-secondary-button" onClick={onUseExisting}>
156
152
  {t(language, "flow.wake.existing")}
157
153
  </button>
158
154
  </div>
159
- <div style={{marginTop: "0.8rem", fontSize: "0.72rem", color: "hsl(var(--fg-muted))"}}>평균 2분 안에 첫 대화 시작 · 완전 무료 · 로컬 전용</div>
155
+ <div className="ritual-start-note">{t(language, "flow.wake.startNote")}</div>
160
156
  </section>
161
157
  );
162
158
  }
@@ -2,7 +2,7 @@ import { Button } from "@/components/ui/button";
2
2
  import { t, type Language } from "@/i18n";
3
3
  import { useAppStore } from "@/store/appStore";
4
4
  import { fallbackModel, type RecommendedModel } from "./recommendationModel";
5
- import { Star, Zap, Gauge, ArrowRight } from "lucide-react";
5
+ import { ArrowRight, Gauge, Star, Zap } from "lucide-react";
6
6
 
7
7
  export function RecommendationScreen({
8
8
  recommendations,
@@ -24,9 +24,9 @@ export function RecommendationScreen({
24
24
 
25
25
  <div className="ritual-model-list">
26
26
  {items[0]?.supported ? (
27
- <div className="ritual-primary-cta" style={{marginBottom: "1rem"}}>
28
- <Button onClick={() => onSelect(items[0])} className="ritual-primary-model-button" style={{display: "flex", alignItems: "center", gap: "0.5rem", fontSize: "1rem"}}>
29
- {t(language, "flow.recommend.primary")} <ArrowRight size={16}/>
27
+ <div className="ritual-primary-cta">
28
+ <Button onClick={() => onSelect(items[0])} className="ritual-primary-model-button">
29
+ {t(language, "flow.recommend.primary")} <ArrowRight size={16} />
30
30
  </Button>
31
31
  <div className="ritual-time-estimate ritual-primary-note">{primaryNote(items[0], language)}</div>
32
32
  <div className="ritual-muted-hint ritual-next-hint">{t(language, "flow.recommend.nextHint")}</div>
@@ -40,24 +40,23 @@ export function RecommendationScreen({
40
40
  className="ritual-model-card"
41
41
  onClick={() => model.supported && onSelect(model)}
42
42
  disabled={!model.supported}
43
- style={{display: "flex", flexDirection: "column", alignItems: "flex-start", gap: "0.3rem"}}
44
43
  >
45
- <div style={{display: "flex", alignItems: "center", gap: "0.4rem", width: "100%"}}>
46
- <Icon size={16} style={{color: "hsl(var(--brain-core))"}} />
47
- <div className="role" style={{marginBottom: 0}}>{rankLabel(model.role, index, language)}</div>
44
+ <div className="ritual-model-heading">
45
+ <Icon size={16} />
46
+ <div className="role">{rankLabel(model.role, index, language)}</div>
48
47
  </div>
49
- <div className="name" style={{fontSize: "1.15rem"}}>{model.shortName}</div>
50
- <div className="reason" style={{fontSize: "0.9rem"}}>
48
+ <div className="name">{model.shortName}</div>
49
+ <div className="reason">
51
50
  {model.reason} · {model.size || t(language, "flow.recommend.sizeReady")}
52
51
  {comparisonLabel(model.role, language) ? (
53
52
  <span className="ritual-model-comparison"> {comparisonLabel(model.role, language)}</span>
54
53
  ) : null}
55
54
  </div>
56
- <div className="ritual-model-stats" style={{marginTop: "0.1rem"}}>
55
+ <div className="ritual-model-stats">
57
56
  <span className="ritual-time-estimate">{timeEstimate(model, language)}</span>
58
57
  </div>
59
58
  {model.supported ? (
60
- <span className="ritual-model-choose" style={{marginTop: "0.2rem", fontWeight: 700}}>{t(language, "flow.recommend.choose")} →</span>
59
+ <span className="ritual-model-choose">{t(language, "flow.recommend.choose")} <ArrowRight size={14} /></span>
61
60
  ) : (
62
61
  <div className="ritual-model-warning">{t(language, "flow.recommend.unsupported")}</div>
63
62
  )}
@@ -12,7 +12,6 @@ import {
12
12
  Loader2,
13
13
  Repeat2,
14
14
  Search,
15
- Settings,
16
15
  ShieldCheck,
17
16
  Sparkles,
18
17
  } from "lucide-react";
@@ -44,7 +43,6 @@ import { BrainOverviewPanel } from "./BrainOverviewPanel";
44
43
 
45
44
  export function BrainConversation({
46
45
  language,
47
- explorationDepth,
48
46
  modelName,
49
47
  messages,
50
48
  starterPrompts,
@@ -100,129 +98,130 @@ export function BrainConversation({
100
98
  return (
101
99
  <section className="brain-conversation" aria-label={t(language, "brain.aria.conversation")}>
102
100
  <div className="brain-conversation-header">
103
- <div>
101
+ <div className="brain-conversation-title">
104
102
  <h1>
105
103
  {t(language, "brain.title")}
106
104
  <span className="brain-edition-badge" title={t(language, "brain.edition.tip")}>
107
105
  {t(language, "brain.edition")}
108
106
  </span>
109
107
  </h1>
110
- <span>{t(language, `brain.depth.${explorationDepth}`)}</span>
111
- </div>
112
- <LanguageSwitcher compact />
113
- <div className="brain-ownership-strip" aria-label={t(language, "brain.aria.ownership")}>
114
- <span>{t(language, "brain.local")}</span>
115
- <span>{t(language, "brain.portable")}</span>
116
- <span>{t(language, "brain.private")}</span>
108
+ <span>{t(language, "brain.chatHome.headerLine")}</span>
117
109
  </div>
118
- <div className="brain-flow-actions" aria-label={t(language, "brain.aria.actions")}>
119
- <button type="button" onClick={() => navigateHash("/capture")}>
120
- <FileUp className="h-3.5 w-3.5" />
121
- {t(language, "brain.action.add")}
122
- </button>
123
- <button type="button" onClick={() => navigateHash("/knowledge-graph")}>
124
- <Search className="h-3.5 w-3.5" />
125
- {t(language, "brain.action.find")}
126
- </button>
127
- <button type="button" onClick={() => navigateHash("/models")}>
128
- <Cpu className="h-3.5 w-3.5" />
129
- {t(language, "brain.action.model")}
130
- </button>
131
- <button type="button" onClick={() => navigateHash("/settings")}>
132
- <Settings className="h-3.5 w-3.5" />
133
- {t(language, "brain.action.settings")}
110
+ <div className="brain-header-tools">
111
+ <LanguageSwitcher compact />
112
+ <div className="brain-model-pill">{modelName}</div>
113
+ <button className="brain-admin-link" type="button" onClick={() => navigateHash("/admin")}>
114
+ <ShieldCheck className="h-3.5 w-3.5" />
115
+ {t(language, "brain.admin")}
134
116
  </button>
135
117
  </div>
136
- <div className="brain-model-pill">{modelName}</div>
137
- <button className="brain-admin-link" type="button" onClick={() => navigateHash("/admin")}>
138
- <ShieldCheck className="h-3.5 w-3.5" />
139
- {t(language, "brain.admin")}
140
- </button>
141
118
  </div>
142
119
 
143
- <div ref={streamRef} className="brain-stream">
144
- <ProductCommandCenter
145
- language={language}
146
- readiness={readiness}
147
- proof={proof}
148
- modelName={modelName}
149
- memories={memories}
150
- concepts={concepts}
151
- emergenceEvents={emergenceEvents}
152
- onOpenDepth={onOpenDepth}
153
- onVerifyModelContinuity={onVerifyModelContinuity}
154
- />
155
- <BrainIngestionPanel
156
- language={language}
157
- uploadingDocument={uploadingDocument}
158
- ingestionStates={ingestionStates}
159
- onUploadDocument={onUploadDocument}
160
- onConnectFolder={onConnectFolder}
161
- onIngestNote={onIngestNote}
162
- onIngestWeb={onIngestWeb}
163
- />
164
- <IngestionTimelineSection language={language} emergenceEvents={emergenceEvents} />
165
- <BrainOverviewPanel
166
- memories={memories}
167
- concepts={concepts}
168
- readiness={readiness}
169
- proof={proof}
170
- onOpenDepth={onOpenDepth}
171
- />
172
- <ModelContinuityDemo
173
- language={language}
174
- proof={proof}
175
- modelName={modelName}
176
- onVerify={onVerifyModelContinuity}
177
- />
178
- {messages.length === 0 ? (
179
- <BrainEmptyState
120
+ <div className="brain-chat-home-layout">
121
+ <section className="brain-chat-home-card" aria-label={t(language, "brain.chatHome.aria")}>
122
+ <div className="brain-chat-home-head">
123
+ <div>
124
+ <span>{t(language, "brain.chatHome.kicker")}</span>
125
+ <h2>{t(language, "brain.chatHome.title")}</h2>
126
+ <p>{t(language, "brain.chatHome.body")}</p>
127
+ </div>
128
+ <div className="brain-chat-home-proof" aria-label={t(language, "brain.aria.ownership")}>
129
+ <span><CheckCircle2 className="h-3.5 w-3.5" />{t(language, "brain.local")}</span>
130
+ <span><CheckCircle2 className="h-3.5 w-3.5" />{t(language, "brain.private")}</span>
131
+ <span><CheckCircle2 className="h-3.5 w-3.5" />{t(language, "brain.portable")}</span>
132
+ </div>
133
+ </div>
134
+
135
+ <div ref={streamRef} className="brain-stream">
136
+ {messages.length === 0 ? (
137
+ <BrainEmptyState
138
+ language={language}
139
+ starterPrompts={starterPrompts}
140
+ uploadingDocument={uploadingDocument}
141
+ onDraftChange={onDraftChange}
142
+ onUploadDocument={onUploadDocument}
143
+ />
144
+ ) : (
145
+ messages.map((message, index) => {
146
+ const messageId = `brain-msg-${index}`;
147
+ const proof = message.role === "assistant" ? message.proof : undefined;
148
+ return (
149
+ <div key={`${message.role}-${index}`} className={`brain-message ${message.role}`}>
150
+ <div className="brain-message-bubble">
151
+ {message.content}
152
+ {proof && proof.citations.length ? (
153
+ <InlineCitationMarkers language={language} proof={proof} messageId={messageId} />
154
+ ) : null}
155
+ </div>
156
+ {proof ? <AnswerProofCard language={language} proof={proof} messageId={messageId} /> : null}
157
+ </div>
158
+ );
159
+ })
160
+ )}
161
+ </div>
162
+
163
+ {memoryFeedback ? (
164
+ <div className="brain-save-feedback" role="status">
165
+ <DatabaseZap className="h-3.5 w-3.5" />
166
+ <span>{memoryFeedback}</span>
167
+ <small>{t(language, "brain.saved.detail")}</small>
168
+ </div>
169
+ ) : null}
170
+
171
+ <BrainComposer
180
172
  language={language}
181
- starterPrompts={starterPrompts}
173
+ draft={draft}
174
+ streaming={streaming}
175
+ imageData={imageData}
182
176
  uploadingDocument={uploadingDocument}
183
177
  onDraftChange={onDraftChange}
178
+ onImageDataChange={onImageDataChange}
184
179
  onUploadDocument={onUploadDocument}
180
+ onSend={onSend}
185
181
  />
186
- ) : (
187
- messages.map((message, index) => {
188
- const messageId = `brain-msg-${index}`;
189
- const proof = message.role === "assistant" ? message.proof : undefined;
190
- return (
191
- <div key={`${message.role}-${index}`} className={`brain-message ${message.role}`}>
192
- <div className="brain-message-bubble">
193
- {message.content}
194
- {proof && proof.citations.length ? (
195
- <InlineCitationMarkers language={language} proof={proof} messageId={messageId} />
196
- ) : null}
197
- </div>
198
- {proof ? <AnswerProofCard language={language} proof={proof} messageId={messageId} /> : null}
199
- </div>
200
- );
201
- })
202
- )}
203
- </div>
204
182
 
205
- {memoryFeedback ? (
206
- <div className="brain-save-feedback" role="status">
207
- <DatabaseZap className="h-3.5 w-3.5" />
208
- <span>{memoryFeedback}</span>
209
- <small>{t(language, "brain.saved.detail")}</small>
210
- </div>
211
- ) : null}
212
-
213
- <BrainCarePanel language={language} />
214
-
215
- <BrainComposer
216
- language={language}
217
- draft={draft}
218
- streaming={streaming}
219
- imageData={imageData}
220
- uploadingDocument={uploadingDocument}
221
- onDraftChange={onDraftChange}
222
- onImageDataChange={onImageDataChange}
223
- onUploadDocument={onUploadDocument}
224
- onSend={onSend}
225
- />
183
+ <details className="brain-utility-drawer">
184
+ <summary>{t(language, "brain.chatHome.utility")}</summary>
185
+ <div className="brain-utility-grid">
186
+ <BrainIngestionPanel
187
+ language={language}
188
+ uploadingDocument={uploadingDocument}
189
+ ingestionStates={ingestionStates}
190
+ onUploadDocument={onUploadDocument}
191
+ onConnectFolder={onConnectFolder}
192
+ onIngestNote={onIngestNote}
193
+ onIngestWeb={onIngestWeb}
194
+ />
195
+ <ProductCommandCenter
196
+ language={language}
197
+ readiness={readiness}
198
+ proof={proof}
199
+ modelName={modelName}
200
+ memories={memories}
201
+ concepts={concepts}
202
+ emergenceEvents={emergenceEvents}
203
+ onOpenDepth={onOpenDepth}
204
+ onVerifyModelContinuity={onVerifyModelContinuity}
205
+ />
206
+ <IngestionTimelineSection language={language} emergenceEvents={emergenceEvents} />
207
+ <ModelContinuityDemo
208
+ language={language}
209
+ proof={proof}
210
+ modelName={modelName}
211
+ onVerify={onVerifyModelContinuity}
212
+ />
213
+ <BrainOverviewPanel
214
+ memories={memories}
215
+ concepts={concepts}
216
+ readiness={readiness}
217
+ proof={proof}
218
+ onOpenDepth={onOpenDepth}
219
+ />
220
+ <BrainCarePanel language={language} />
221
+ </div>
222
+ </details>
223
+ </section>
224
+ </div>
226
225
  </section>
227
226
  );
228
227
  }
@@ -483,11 +483,6 @@ export function BrainHome({
483
483
 
484
484
  return (
485
485
  <main className="brain-home" aria-label={t(language, "brain.aria.home")}>
486
- {/* 강력하고 직관적인 첫인상 - BrainHome */}
487
- <div style={{textAlign: "center", marginBottom: "0.4rem", opacity: 0.9}}>
488
- <div style={{fontSize: "0.78rem", letterSpacing: "1.5px", textTransform: "uppercase", color: "hsl(var(--brain-core))"}}>LATTICE AI — YOUR LIVING BRAIN</div>
489
- <div style={{fontSize: "1.05rem", fontWeight: 600, marginTop: "0.15rem"}}>대화할수록 더 똑똑해지는, 당신만의 영원한 지식</div>
490
- </div>
491
486
  <section className="brain-presence" aria-label={t(language, "brain.aria.exploration")}>
492
487
  <div className="brain-exploration" data-depth={explorationDepth}>
493
488
  <LivingBrain
@@ -499,34 +494,38 @@ export function BrainHome({
499
494
  onInteract={deepen}
500
495
  />
501
496
 
502
- <BrainDepthRings explorationDepth={explorationDepth} onOpenDepth={jumpToDepth} />
503
-
504
- <div className="brain-depth-badge" aria-live="polite">
505
- <span>{t(language, "brain.level")} {explorationDepth}</span>
506
- <strong>{t(language, `brain.depth.${explorationDepth}`)}</strong>
507
- </div>
508
-
509
- <div className="brain-depth-actions" aria-label={t(language, "brain.aria.quickViews")}>
510
- <button type="button" className={explorationDepth === 2 ? "is-active" : ""} onClick={() => jumpToDepth(2)}>{t(language, "brain.view.memories")}</button>
511
- <button type="button" className={explorationDepth === 3 ? "is-active" : ""} onClick={() => jumpToDepth(3)}>{t(language, "brain.view.topics")}</button>
512
- <button type="button" className={explorationDepth === 4 ? "is-active" : ""} onClick={() => jumpToDepth(4)}>{t(language, "brain.view.relationships")}</button>
513
- <button type="button" className={explorationDepth === 5 ? "is-active" : ""} onClick={() => jumpToDepth(5)}>{t(language, "brain.view.graph")}</button>
514
- </div>
515
-
516
- <div className="brain-depth-rail" aria-label={t(language, "brain.depthRail.aria")}>
517
- {DEPTHS.map((depth) => (
518
- <button
519
- key={depth.level}
520
- type="button"
521
- className={depth.level <= explorationDepth ? "is-revealed" : ""}
522
- aria-current={depth.level === explorationDepth ? "step" : undefined}
523
- onClick={() => jumpToDepth(depth.level)}
524
- >
525
- <span>{depth.level}</span>
526
- <strong>{t(language, `brain.depth.${depth.level}`)}</strong>
527
- </button>
528
- ))}
529
- </div>
497
+ {explorationDepth > 1 ? (
498
+ <>
499
+ <BrainDepthRings explorationDepth={explorationDepth} onOpenDepth={jumpToDepth} />
500
+
501
+ <div className="brain-depth-badge" aria-live="polite">
502
+ <span>{t(language, "brain.level")} {explorationDepth}</span>
503
+ <strong>{t(language, `brain.depth.${explorationDepth}`)}</strong>
504
+ </div>
505
+
506
+ <div className="brain-depth-actions" aria-label={t(language, "brain.aria.quickViews")}>
507
+ <button type="button" className={explorationDepth === 2 ? "is-active" : ""} onClick={() => jumpToDepth(2)}>{t(language, "brain.view.memories")}</button>
508
+ <button type="button" className={explorationDepth === 3 ? "is-active" : ""} onClick={() => jumpToDepth(3)}>{t(language, "brain.view.topics")}</button>
509
+ <button type="button" className={explorationDepth === 4 ? "is-active" : ""} onClick={() => jumpToDepth(4)}>{t(language, "brain.view.relationships")}</button>
510
+ <button type="button" className={explorationDepth === 5 ? "is-active" : ""} onClick={() => jumpToDepth(5)}>{t(language, "brain.view.graph")}</button>
511
+ </div>
512
+
513
+ <div className="brain-depth-rail" aria-label={t(language, "brain.depthRail.aria")}>
514
+ {DEPTHS.map((depth) => (
515
+ <button
516
+ key={depth.level}
517
+ type="button"
518
+ className={depth.level <= explorationDepth ? "is-revealed" : ""}
519
+ aria-current={depth.level === explorationDepth ? "step" : undefined}
520
+ onClick={() => jumpToDepth(depth.level)}
521
+ >
522
+ <span>{depth.level}</span>
523
+ <strong>{t(language, `brain.depth.${depth.level}`)}</strong>
524
+ </button>
525
+ ))}
526
+ </div>
527
+ </>
528
+ ) : null}
530
529
 
531
530
  <div className="brain-field-layer" aria-hidden={explorationDepth < 2}>
532
531
  <DepthEmergence