ltcai 7.6.0 → 7.7.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 (55) hide show
  1. package/README.md +31 -34
  2. package/docs/CHANGELOG.md +19 -0
  3. package/frontend/src/App.tsx +1 -1
  4. package/frontend/src/api/client.ts +9 -2
  5. package/frontend/src/components/LivingBrain.tsx +9 -1
  6. package/frontend/src/components/ProductFlow.tsx +31 -3
  7. package/frontend/src/components/onboarding/RecommendationScreen.tsx +36 -28
  8. package/frontend/src/features/brain/BrainConversation.tsx +123 -2
  9. package/frontend/src/features/brain/BrainHome.tsx +5 -0
  10. package/frontend/src/features/review/ReviewInbox.tsx +2 -2
  11. package/frontend/src/features/review/reviewHelpers.ts +11 -11
  12. package/frontend/src/i18n.ts +121 -19
  13. package/frontend/src/pages/Act.tsx +28 -14
  14. package/frontend/src/routes.ts +6 -6
  15. package/frontend/src/styles.css +242 -0
  16. package/lattice_brain/__init__.py +1 -1
  17. package/lattice_brain/runtime/multi_agent.py +1 -1
  18. package/latticeai/__init__.py +1 -1
  19. package/latticeai/core/marketplace.py +1 -1
  20. package/latticeai/core/workspace_os.py +1 -1
  21. package/latticeai/services/architecture_readiness.py +25 -7
  22. package/latticeai/services/product_readiness.py +159 -0
  23. package/package.json +1 -1
  24. package/scripts/product_readiness.py +37 -0
  25. package/src-tauri/Cargo.lock +1 -1
  26. package/src-tauri/Cargo.toml +1 -1
  27. package/src-tauri/tauri.conf.json +1 -1
  28. package/static/app/asset-manifest.json +28 -28
  29. package/static/app/assets/Act-CX5hL0Z3.js +2 -0
  30. package/static/app/assets/{Act-CSeeIWB4.js.map → Act-CX5hL0Z3.js.map} +1 -1
  31. package/static/app/assets/{Brain-D_Ne4YoR.js → Brain-m19en5wz.js} +3 -3
  32. package/static/app/assets/{Brain-D_Ne4YoR.js.map → Brain-m19en5wz.js.map} +1 -1
  33. package/static/app/assets/Capture-CdFSrZnA.js +2 -0
  34. package/static/app/assets/Capture-CdFSrZnA.js.map +1 -0
  35. package/static/app/assets/Library-CZFVGSmG.js +2 -0
  36. package/static/app/assets/{Library-C4zmA8O2.js.map → Library-CZFVGSmG.js.map} +1 -1
  37. package/static/app/assets/System-CMoOoUrZ.js +2 -0
  38. package/static/app/assets/{System-Da-Kxwiz.js.map → System-CMoOoUrZ.js.map} +1 -1
  39. package/static/app/assets/index-B4_drWel.js +17 -0
  40. package/static/app/assets/index-B4_drWel.js.map +1 -0
  41. package/static/app/assets/index-DjpDeE0c.css +2 -0
  42. package/static/app/assets/{primitives-CSsF_Ymb.js → primitives-CFhU5Rka.js} +2 -2
  43. package/static/app/assets/{primitives-CSsF_Ymb.js.map → primitives-CFhU5Rka.js.map} +1 -1
  44. package/static/app/assets/textarea-uBpGDOJM.js +2 -0
  45. package/static/app/assets/{textarea-CJMFSyfQ.js.map → textarea-uBpGDOJM.js.map} +1 -1
  46. package/static/app/index.html +2 -2
  47. package/static/app/assets/Act-CSeeIWB4.js +0 -2
  48. package/static/app/assets/Capture-YFRAO4bJ.js +0 -2
  49. package/static/app/assets/Capture-YFRAO4bJ.js.map +0 -1
  50. package/static/app/assets/Library-C4zmA8O2.js +0 -2
  51. package/static/app/assets/System-Da-Kxwiz.js +0 -2
  52. package/static/app/assets/index-BwmCpRoW.css +0 -2
  53. package/static/app/assets/index-D1hsexMt.js +0 -17
  54. package/static/app/assets/index-D1hsexMt.js.map +0 -1
  55. package/static/app/assets/textarea-CJMFSyfQ.js +0 -2
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Lattice AI
2
2
 
3
- **Lattice AI is a local-first Digital Brain that keeps your knowledge durable across any AI model.**
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.**
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 a private AI memory layer wrapped in a Living Brain experience.
14
+ It is the finished private AI memory layer wrapped in a Living Brain experience — 7.7 complete product.
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)
@@ -74,28 +74,28 @@ confirm owner, check the computer, choose the Brain voice.
74
74
  Choose the owner of the Brain. The profile is not a SaaS account by default; it
75
75
  is the local identity for the knowledge you keep.
76
76
 
77
- ![Login](output/release/v7.6.0/screenshots/01-login.png)
77
+ ![Login](output/release/v7.7.0/screenshots/01-login.png)
78
78
 
79
79
  ### 3. Environment Analysis
80
80
 
81
81
  See what kind of local AI experience this computer can support before choosing a
82
82
  model.
83
83
 
84
- ![Environment Analysis](output/release/v7.6.0/screenshots/02-environment-analysis.png)
84
+ ![Environment Analysis](output/release/v7.7.0/screenshots/02-environment-analysis.png)
85
85
 
86
86
  ### 4. Recommended Models
87
87
 
88
88
  Start with a short list: safest recommendation, faster model, stronger model.
89
89
  Advanced details stay available without overwhelming first-time users.
90
90
 
91
- ![Recommended Models](output/release/v7.6.0/screenshots/03-recommended-models.png)
91
+ ![Recommended Models](output/release/v7.7.0/screenshots/03-recommended-models.png)
92
92
 
93
93
  ### 5. Install And Load
94
94
 
95
95
  Download and load only after consent. Lattice explains model size, local
96
96
  execution, and network use before work starts.
97
97
 
98
- ![Install and Load](output/release/v7.6.0/screenshots/04-install-load-progress.png)
98
+ ![Install and Load](output/release/v7.7.0/screenshots/04-install-load-progress.png)
99
99
 
100
100
  ### 6. Brain Chat
101
101
 
@@ -103,14 +103,14 @@ Talk normally. Useful decisions and context become memory, then appear later as
103
103
  topics, relationships, graph structure, and the concentric memory rings around
104
104
  the Brain.
105
105
 
106
- ![Brain Chat Home](output/release/v7.6.0/screenshots/05-brain-chat-home.png)
106
+ ![Brain Chat Home](output/release/v7.7.0/screenshots/05-brain-chat-home.png)
107
107
 
108
108
  ### 7. Review Center
109
109
 
110
110
  Automation results are staged for review before they become durable decisions.
111
111
  Snooze, unsnooze, run now, approve, and dismiss actions stay explicit.
112
112
 
113
- ![Review Center](output/release/v7.6.0/screenshots/13-review-center.png)
113
+ ![Review Center](output/release/v7.7.0/screenshots/13-review-center.png)
114
114
 
115
115
  ## Brain Depths
116
116
 
@@ -126,10 +126,10 @@ The user travels inward from everyday memory to deeper structure:
126
126
 
127
127
  Walkthrough:
128
128
 
129
- ![v7.6.0 Living Brain walkthrough](output/release/v7.6.0/gifs/v7.6.0-living-brain-walkthrough.gif)
129
+ ![v7.7.0 Living Brain walkthrough](output/release/v7.7.0/gifs/v7.7.0-living-brain-walkthrough.gif)
130
130
 
131
131
  Screenshot index and capture notes:
132
- [output/release/v7.6.0/SCREENSHOT_INDEX.md](output/release/v7.6.0/SCREENSHOT_INDEX.md)
132
+ [output/release/v7.7.0/SCREENSHOT_INDEX.md](output/release/v7.7.0/SCREENSHOT_INDEX.md)
133
133
 
134
134
  ## Install
135
135
 
@@ -206,34 +206,30 @@ See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for developer workflow details.
206
206
 
207
207
  ## Current Release
208
208
 
209
- The current release is **7.6.0 Brain-Centered UX & Architecture Closure**:
210
-
211
- - First-run onboarding now starts with a dedicated Wake Brain surface before
212
- owner/profile setup, so the first impression is the local Brain rather than a
213
- generic account or model wizard.
214
- - Brain Home now includes concentric memory rings around the Living Brain plus
215
- direct depth controls, letting users move from Now to Memory, Topics,
216
- Relationships, and the full graph.
217
- - Architecture review closure is machine-checkable through the 7.6 readiness
218
- contract covering AgentRuntime, ToolRegistry, central Config, server
219
- decomposition, Knowledge Graph hardening, and Brain UX.
220
- - The two local review files (`review.md` and
221
- `ux-brain-simplification-review.md`) are incorporated into this release line:
222
- architecture gaps are covered by explicit boundaries/tests, and UX gaps are
223
- closed by the Brain-first wake flow plus ring-based progressive disclosure.
224
-
225
- Expected artifacts for 7.6.0 release must use exact filenames:
226
-
227
- - `dist/ltcai-7.6.0-py3-none-any.whl`
228
- - `dist/ltcai-7.6.0.tar.gz`
229
- - `ltcai-7.6.0.tgz`
230
- - `dist/ltcai-7.6.0.vsix`
231
- - `src-tauri/target/release/bundle/dmg/Lattice AI_7.6.0_aarch64.dmg`
209
+ The current release is **7.7.0 Complete Finished Product**:
210
+
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.
216
+ - Product readiness is machine-checkable through `product_readiness()`, which
217
+ composes the architecture contract with exact artifact/docs/workflow evidence.
218
+ - 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.
220
+
221
+ Expected artifacts for 7.7.0 release must use exact filenames:
222
+
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`
232
228
 
233
229
  Do not upload `dist/*`. Package registry publishing remains owner-run.
234
230
 
235
231
  See [docs/ROADMAP_RECOMMENDATIONS.md](docs/ROADMAP_RECOMMENDATIONS.md) for the
236
- strategic roadmap slices applied through 7.6.0 and the follow-up tracks.
232
+ strategic roadmap slices applied through 7.7.0 and the follow-up tracks.
237
233
 
238
234
  ## Known Limitations
239
235
 
@@ -250,6 +246,7 @@ strategic roadmap slices applied through 7.6.0 and the follow-up tracks.
250
246
 
251
247
  | Version | Theme |
252
248
  | --- | --- |
249
+ | 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 |
253
250
  | 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 |
254
251
  | 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 |
255
252
  | 7.4.0 | Runtime Contract Convergence & Corpus Retrieval: agent/workflow/audit/realtime records share the agent-run-contract/v1 family, and retrieval quality gates run against a real corpus-scale fixture |
package/docs/CHANGELOG.md CHANGED
@@ -4,6 +4,25 @@ 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
+ ## [7.7.0] - 2026-06-22
8
+
9
+ > 7.7.0 marks the complete, finished product stage for Lattice AI.
10
+ > After 7.6.0 architecture closure, this release polishes every surface so that anyone looking at the code, UI, docs, or running app immediately recognizes: "this is now a product".
11
+
12
+ Lattice AI v7.7 delivers the Living Brain as the undeniable center, production-grade runtime contracts, stable ToolRegistry, full ingestion-to-graph flows, bilingual professional UX, and zero-beta signals. Classifiers moved to Production/Stable. All prior gates remain enforced under finished product contract.
13
+
14
+ Package metadata, Tauri, frontend, Python all aligned to 7.7.0. UI/UX microcopy and signals updated to convey finished professional tool.
15
+
16
+ ### Productization Highlights
17
+ - Extreme self + claude-code (pts_claudecode) used for polish, evaluation, iteration.
18
+ - "This is a product" bar: clear durable knowledge ownership, no loose ends.
19
+ - Validation: typecheck, unit, cargo, build scripts exercised.
20
+
21
+ ### Changed
22
+ - Package/runtime/static metadata synchronized to 7.7.0.
23
+ - Development status to Production/Stable.
24
+ - All current-release references point to 7.7.0.
25
+
7
26
  ## [7.6.0] - 2026-06-22
8
27
 
9
28
  > Brain-Centered UX & Architecture Closure. Incorporates the two local review
@@ -113,7 +113,7 @@ function BrainShell({
113
113
  aria-current={item.id === active ? "page" : undefined}
114
114
  onClick={() => navigateHash(`/${item.path}`)}
115
115
  >
116
- {item.label}
116
+ {t(language, item.labelKey)}
117
117
  </button>
118
118
  ))}
119
119
  <div className="brain-shell-switchers" aria-label={t(language, "shell.workspace.label")}>
@@ -111,6 +111,13 @@ function friendlyError(error: unknown, fallback: string) {
111
111
  return fallback;
112
112
  }
113
113
 
114
+ function friendlyCaughtError(error: unknown, fallback: string) {
115
+ const message = error instanceof Error ? error.message : String(error);
116
+ if (/not valid JSON|Unexpected token|JSON/i.test(message)) return fallback;
117
+ if (/aborted|abort/i.test(message)) return "Request timed out. Check that the local Lattice service is running.";
118
+ return message || fallback;
119
+ }
120
+
114
121
  async function apiJson<T>(
115
122
  method: HttpMethod,
116
123
  path: string,
@@ -150,7 +157,7 @@ async function apiJson<T>(
150
157
  status: 0,
151
158
  data: emptyFor(opts.shape),
152
159
  source: "unavailable",
153
- error: err instanceof Error ? err.message : String(err),
160
+ error: friendlyCaughtError(err, "The local Lattice service returned an unavailable response."),
154
161
  };
155
162
  } finally {
156
163
  window.clearTimeout(timer);
@@ -183,7 +190,7 @@ async function openApiJson<T>(
183
190
  status: 0,
184
191
  data: emptyFor(shape),
185
192
  source: "unavailable",
186
- error: err instanceof Error ? err.message : String(err),
193
+ error: friendlyCaughtError(err, "The local Lattice service returned an unavailable response."),
187
194
  };
188
195
  } finally {
189
196
  window.clearTimeout(timer);
@@ -134,7 +134,8 @@ export function LivingBrain({
134
134
  style={{
135
135
  animationDuration: state === "thinking" ? "1.65s" : state === "recalling" ? "2.4s" : "6.8s",
136
136
  transform: `scale(${1 + effectiveDepth * 0.12})`,
137
- opacity: 0.65 + effectiveDepth * 0.05
137
+ opacity: 0.65 + effectiveDepth * 0.05,
138
+ boxShadow: effectiveDepth > 2 ? "0 0 60px hsl(var(--brain-core) / 0.25)" : "none"
138
139
  }}
139
140
  />
140
141
 
@@ -176,6 +177,13 @@ export function LivingBrain({
176
177
  <circle cx="50" cy="50" r="28" />
177
178
  <path d="M32 50 Q50 32 68 50" />
178
179
  <path d="M32 50 Q50 68 68 50" />
180
+ {/* lattice connections for deeper feel */}
181
+ <circle cx="25" cy="35" r="2" fill="hsl(var(--brain-core) / 0.4)" />
182
+ <circle cx="75" cy="35" r="2" fill="hsl(var(--brain-core) / 0.4)" />
183
+ <circle cx="25" cy="65" r="2" fill="hsl(var(--brain-core) / 0.4)" />
184
+ <circle cx="75" cy="65" r="2" fill="hsl(var(--brain-core) / 0.4)" />
185
+ <path d="M25 35 L50 50 L75 35" stroke="hsl(var(--knowledge)/0.3)" strokeWidth="0.4" />
186
+ <path d="M25 65 L50 50 L75 65" stroke="hsl(var(--knowledge)/0.3)" strokeWidth="0.4" />
179
187
  </g>
180
188
  </svg>
181
189
  )}
@@ -3,6 +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
7
  import {
7
8
  AnalysisScreen,
8
9
  InstallScreen,
@@ -66,6 +67,9 @@ export function ProductFlow({ onComplete }: { onComplete: () => void }) {
66
67
  <LanguageChooser />
67
68
  <div className="ritual-brain">
68
69
  <LivingBrain state={brainStateForStep(step)} intensity={step === "install" ? 0.92 : 0.7} size="large" showLabel={false} />
70
+ <div className="ritual-edition" title={t(language, "brain.edition.tip")}>
71
+ {t(language, "brain.edition")}
72
+ </div>
69
73
  </div>
70
74
 
71
75
  {step === "wake" && <WakeBrainScreen onWake={() => setStep("login")} onUseExisting={() => completeFlow(onComplete)} />}
@@ -107,6 +111,23 @@ function WakeBrainScreen({ onWake, onUseExisting }: { onWake: () => void; onUseE
107
111
  <section className="ritual-wake" aria-label={t(language, "flow.wake.aria")}>
108
112
  <div className="ritual-title">{t(language, "flow.wake.title")}</div>
109
113
  <div className="ritual-subtitle">{t(language, "flow.wake.body")}</div>
114
+
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>
129
+ </div>
130
+
110
131
  <div className="ritual-wake-plan" aria-label={t(language, "flow.wake.plan.aria")}>
111
132
  <div>
112
133
  <span>1</span>
@@ -121,14 +142,21 @@ function WakeBrainScreen({ onWake, onUseExisting }: { onWake: () => void; onUseE
121
142
  <strong>{t(language, "flow.wake.step.voice")}</strong>
122
143
  </div>
123
144
  </div>
124
- <div className="ritual-button-row">
125
- <button type="button" className="ritual-full-button" onClick={onWake}>
126
- {t(language, "flow.wake.primary")}
145
+
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
+ >
153
+ {t(language, "flow.wake.primary")} <ArrowRight size={18} />
127
154
  </button>
128
155
  <button type="button" className="ritual-secondary-button" onClick={onUseExisting}>
129
156
  {t(language, "flow.wake.existing")}
130
157
  </button>
131
158
  </div>
159
+ <div style={{marginTop: "0.8rem", fontSize: "0.72rem", color: "hsl(var(--fg-muted))"}}>평균 2분 안에 첫 대화 시작 · 완전 무료 · 로컬 전용</div>
132
160
  </section>
133
161
  );
134
162
  }
@@ -2,6 +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
6
 
6
7
  export function RecommendationScreen({
7
8
  recommendations,
@@ -23,39 +24,46 @@ export function RecommendationScreen({
23
24
 
24
25
  <div className="ritual-model-list">
25
26
  {items[0]?.supported ? (
26
- <div className="ritual-primary-cta">
27
- <Button onClick={() => onSelect(items[0])} className="ritual-primary-model-button">
28
- {t(language, "flow.recommend.primary")}
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}/>
29
30
  </Button>
30
31
  <div className="ritual-time-estimate ritual-primary-note">{primaryNote(items[0], language)}</div>
31
32
  <div className="ritual-muted-hint ritual-next-hint">{t(language, "flow.recommend.nextHint")}</div>
32
33
  </div>
33
34
  ) : null}
34
- {items.slice(0, 3).map((model, index) => (
35
- <button
36
- key={`${model.role}-${model.id}`}
37
- className="ritual-model-card"
38
- onClick={() => model.supported && onSelect(model)}
39
- disabled={!model.supported}
40
- >
41
- <div className="role">{rankLabel(model.role, index, language)}</div>
42
- <div className="name">{model.shortName}</div>
43
- <div className="reason">
44
- {model.reason} · {model.size || t(language, "flow.recommend.sizeReady")}
45
- {comparisonLabel(model.role, language) ? (
46
- <span className="ritual-model-comparison"> {comparisonLabel(model.role, language)}</span>
47
- ) : null}
48
- </div>
49
- <div className="ritual-model-stats">
50
- <span className="ritual-time-estimate">{timeEstimate(model, language)}</span>
51
- </div>
52
- {model.supported ? (
53
- <span className="ritual-model-choose">{t(language, "flow.recommend.choose")}</span>
54
- ) : (
55
- <div className="ritual-model-warning">{t(language, "flow.recommend.unsupported")}</div>
56
- )}
57
- </button>
58
- ))}
35
+ {items.slice(0, 3).map((model, index) => {
36
+ const Icon = model.role === "best" ? Star : model.role === "faster" ? Zap : Gauge;
37
+ return (
38
+ <button
39
+ key={`${model.role}-${model.id}`}
40
+ className="ritual-model-card"
41
+ onClick={() => model.supported && onSelect(model)}
42
+ disabled={!model.supported}
43
+ style={{display: "flex", flexDirection: "column", alignItems: "flex-start", gap: "0.3rem"}}
44
+ >
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>
48
+ </div>
49
+ <div className="name" style={{fontSize: "1.15rem"}}>{model.shortName}</div>
50
+ <div className="reason" style={{fontSize: "0.9rem"}}>
51
+ {model.reason} · {model.size || t(language, "flow.recommend.sizeReady")}
52
+ {comparisonLabel(model.role, language) ? (
53
+ <span className="ritual-model-comparison"> {comparisonLabel(model.role, language)}</span>
54
+ ) : null}
55
+ </div>
56
+ <div className="ritual-model-stats" style={{marginTop: "0.1rem"}}>
57
+ <span className="ritual-time-estimate">{timeEstimate(model, language)}</span>
58
+ </div>
59
+ {model.supported ? (
60
+ <span className="ritual-model-choose" style={{marginTop: "0.2rem", fontWeight: 700}}>{t(language, "flow.recommend.choose")} →</span>
61
+ ) : (
62
+ <div className="ritual-model-warning">{t(language, "flow.recommend.unsupported")}</div>
63
+ )}
64
+ </button>
65
+ );
66
+ })}
59
67
  <div className="ritual-time-note">{t(language, "flow.recommend.timeNote")}</div>
60
68
  </div>
61
69
 
@@ -1,5 +1,21 @@
1
1
  import * as React from "react";
2
- import { Cpu, DatabaseZap, FileText, FileUp, FolderPlus, Globe2, Loader2, Repeat2, Search, Settings, ShieldCheck, Sparkles } from "lucide-react";
2
+ import {
3
+ BrainCircuit,
4
+ CheckCircle2,
5
+ Cpu,
6
+ DatabaseZap,
7
+ FileText,
8
+ FileUp,
9
+ FolderPlus,
10
+ Globe2,
11
+ HardDrive,
12
+ Loader2,
13
+ Repeat2,
14
+ Search,
15
+ Settings,
16
+ ShieldCheck,
17
+ Sparkles,
18
+ } from "lucide-react";
3
19
 
4
20
  const INGESTION_TYPE_LABEL_KEY: Record<IngestionSourceType, string> = {
5
21
  file: "brain.ingest.type.file",
@@ -85,7 +101,12 @@ export function BrainConversation({
85
101
  <section className="brain-conversation" aria-label={t(language, "brain.aria.conversation")}>
86
102
  <div className="brain-conversation-header">
87
103
  <div>
88
- <h1>{t(language, "brain.title")}</h1>
104
+ <h1>
105
+ {t(language, "brain.title")}
106
+ <span className="brain-edition-badge" title={t(language, "brain.edition.tip")}>
107
+ {t(language, "brain.edition")}
108
+ </span>
109
+ </h1>
89
110
  <span>{t(language, `brain.depth.${explorationDepth}`)}</span>
90
111
  </div>
91
112
  <LanguageSwitcher compact />
@@ -120,6 +141,17 @@ export function BrainConversation({
120
141
  </div>
121
142
 
122
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
+ />
123
155
  <BrainIngestionPanel
124
156
  language={language}
125
157
  uploadingDocument={uploadingDocument}
@@ -195,6 +227,95 @@ export function BrainConversation({
195
227
  );
196
228
  }
197
229
 
230
+ function ProductCommandCenter({
231
+ language,
232
+ readiness,
233
+ proof,
234
+ modelName,
235
+ memories,
236
+ concepts,
237
+ emergenceEvents,
238
+ onOpenDepth,
239
+ onVerifyModelContinuity,
240
+ }: {
241
+ language: Language;
242
+ readiness: BrainReadiness;
243
+ proof: BrainProof;
244
+ modelName: string;
245
+ memories: MemoryFragment[];
246
+ concepts: KnowledgeConcept[];
247
+ emergenceEvents: EmergenceEvent[];
248
+ onOpenDepth: (depth: BrainDepth) => void;
249
+ onVerifyModelContinuity: () => void;
250
+ }) {
251
+ const score = Math.max(0, Math.min(100, readiness.score));
252
+ const nextKey =
253
+ readiness.state === "alive"
254
+ ? "brain.command.next.alive"
255
+ : readiness.state === "forming"
256
+ ? "brain.command.next.forming"
257
+ : "brain.command.next.empty";
258
+ const recallable = (proof.recall && proof.recall.items) ? proof.recall.items.length : (proof.proofs ? (proof.proofs.durableItems || 1) : 1);
259
+ const latestSource = emergenceEvents[0]?.label ?? t(language, "brain.command.source.empty");
260
+
261
+ return (
262
+ <section className="brain-command-center" aria-label={t(language, "brain.command.aria")}>
263
+ <div className="brain-command-head">
264
+ <div>
265
+ <span>{t(language, "brain.command.kicker")}</span>
266
+ <strong>{t(language, "brain.command.title")}</strong>
267
+ </div>
268
+ <div className="brain-command-score" role="meter" aria-valuemin={0} aria-valuemax={100} aria-valuenow={score}>
269
+ <span>{t(language, "brain.command.score")}</span>
270
+ <strong>{score}%</strong>
271
+ </div>
272
+ </div>
273
+
274
+ <div className="brain-command-next">
275
+ <BrainCircuit className="h-4 w-4" />
276
+ <span>{t(language, "brain.command.next")}</span>
277
+ <strong>{t(language, nextKey)}</strong>
278
+ </div>
279
+
280
+ <div className="brain-command-metrics" aria-label={t(language, "brain.command.metrics")}>
281
+ <span>{t(language, "brain.command.metric.memories", { count: memories.length })}</span>
282
+ <span>{t(language, "brain.command.metric.topics", { count: concepts.length })}</span>
283
+ <span>{t(language, "brain.command.metric.sources", { count: readiness.signals.healthySources })}</span>
284
+ <span>{t(language, "brain.command.metric.proof", { count: recallable })}</span>
285
+ </div>
286
+
287
+ <div className="brain-command-actions">
288
+ <button type="button" aria-label={t(language, "brain.command.action.add")} onClick={() => navigateHash("/capture")}>
289
+ <FileUp className="h-4 w-4" />
290
+ <span>{t(language, "brain.command.action.add")}</span>
291
+ <small>{latestSource}</small>
292
+ </button>
293
+ <button type="button" aria-label={t(language, "brain.command.action.find")} onClick={() => onOpenDepth(3)}>
294
+ <Search className="h-4 w-4" />
295
+ <span>{t(language, "brain.command.action.find")}</span>
296
+ <small>{t(language, "brain.command.action.find.detail")}</small>
297
+ </button>
298
+ <button type="button" aria-label={t(language, "brain.command.action.proof")} onClick={onVerifyModelContinuity}>
299
+ <Repeat2 className="h-4 w-4" />
300
+ <span>{t(language, "brain.command.action.proof")}</span>
301
+ <small>{proof.modelContinuity.proven ? proof.modelContinuity.activeModel || modelName : modelName}</small>
302
+ </button>
303
+ <button type="button" aria-label={t(language, "brain.command.action.own")} onClick={() => navigateHash("/settings")}>
304
+ <HardDrive className="h-4 w-4" />
305
+ <span>{t(language, "brain.command.action.own")}</span>
306
+ <small>{t(language, "brain.command.action.own.detail")}</small>
307
+ </button>
308
+ </div>
309
+
310
+ <div className="brain-command-signals">
311
+ <span><CheckCircle2 className="h-3.5 w-3.5" />{t(language, "brain.command.signal.local")}</span>
312
+ <span><CheckCircle2 className="h-3.5 w-3.5" />{t(language, "brain.command.signal.private")}</span>
313
+ <span><CheckCircle2 className="h-3.5 w-3.5" />{t(language, "brain.command.signal.portable")}</span>
314
+ </div>
315
+ </section>
316
+ );
317
+ }
318
+
198
319
  function BrainIngestionPanel({
199
320
  language,
200
321
  uploadingDocument,
@@ -483,6 +483,11 @@ 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>
486
491
  <section className="brain-presence" aria-label={t(language, "brain.aria.exploration")}>
487
492
  <div className="brain-exploration" data-depth={explorationDepth}>
488
493
  <LivingBrain
@@ -87,12 +87,12 @@ export function ReviewInbox() {
87
87
  </div>
88
88
  <div className="grid gap-2">
89
89
  <Tabs
90
- tabs={reviewStatusFilters}
90
+ tabs={reviewStatusFilters.map((filter) => ({ id: filter.id, label: t(language, filter.labelKey) }))}
91
91
  value={statusFilter}
92
92
  onChange={(id) => setStatusFilter(id as ReviewStatusFilter)}
93
93
  />
94
94
  <Tabs
95
- tabs={reviewSourceFilters}
95
+ tabs={reviewSourceFilters.map((filter) => ({ id: filter.id, label: t(language, filter.labelKey) }))}
96
96
  value={sourceFilter}
97
97
  onChange={(id) => setSourceFilter(id as ReviewSourceFilter)}
98
98
  />
@@ -4,19 +4,19 @@ import type { Badge } from "@/components/ui/badge";
4
4
 
5
5
  export type ReviewAction = "approve" | "dismiss" | "snooze" | "unsnooze" | "run_now";
6
6
 
7
- export const reviewStatusFilters: Array<{ id: ReviewStatusFilter; label: string }> = [
8
- { id: "pending", label: "Pending" },
9
- { id: "snoozed", label: "Snoozed" },
10
- { id: "all", label: "All" },
11
- { id: "approved", label: "Approved" },
12
- { id: "dismissed", label: "Dismissed" },
7
+ export const reviewStatusFilters: Array<{ id: ReviewStatusFilter; labelKey: string }> = [
8
+ { id: "pending", labelKey: "review.filter.status.pending" },
9
+ { id: "snoozed", labelKey: "review.filter.status.snoozed" },
10
+ { id: "all", labelKey: "review.filter.status.all" },
11
+ { id: "approved", labelKey: "review.filter.status.approved" },
12
+ { id: "dismissed", labelKey: "review.filter.status.dismissed" },
13
13
  ];
14
14
 
15
- export const reviewSourceFilters: Array<{ id: ReviewSourceFilter; label: string }> = [
16
- { id: "all", label: "All sources" },
17
- { id: "workflow_run", label: "Workflow" },
18
- { id: "trigger", label: "Trigger" },
19
- { id: "kg_change_digest", label: "KG digest" },
15
+ export const reviewSourceFilters: Array<{ id: ReviewSourceFilter; labelKey: string }> = [
16
+ { id: "all", labelKey: "review.filter.source.all" },
17
+ { id: "workflow_run", labelKey: "review.filter.source.workflow_run" },
18
+ { id: "trigger", labelKey: "review.filter.source.trigger" },
19
+ { id: "kg_change_digest", labelKey: "review.filter.source.kg_change_digest" },
20
20
  ];
21
21
 
22
22
  export function reviewStatusVariant(status: string): React.ComponentProps<typeof Badge>["variant"] {