ltcai 7.5.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.
- package/README.md +48 -44
- package/docs/CHANGELOG.md +45 -0
- package/frontend/src/App.tsx +1 -1
- package/frontend/src/api/client.ts +9 -2
- package/frontend/src/components/LivingBrain.tsx +9 -1
- package/frontend/src/components/ProductFlow.tsx +64 -1
- package/frontend/src/components/onboarding/RecommendationScreen.tsx +36 -28
- package/frontend/src/components/onboarding/recommendationModel.ts +1 -1
- package/frontend/src/features/brain/BrainConversation.tsx +123 -2
- package/frontend/src/features/brain/BrainHome.tsx +34 -0
- package/frontend/src/features/review/ReviewInbox.tsx +2 -2
- package/frontend/src/features/review/reviewHelpers.ts +11 -11
- package/frontend/src/i18n.ts +148 -16
- package/frontend/src/pages/Act.tsx +28 -14
- package/frontend/src/routes.ts +6 -6
- package/frontend/src/styles.css +398 -0
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/core/local_embeddings.py +4 -3
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/sessions.py +8 -2
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/models/router.py +19 -0
- package/latticeai/services/architecture_readiness.py +119 -0
- package/latticeai/services/model_runtime.py +8 -3
- package/latticeai/services/product_readiness.py +159 -0
- package/package.json +1 -1
- package/scripts/product_readiness.py +37 -0
- package/scripts/pts-claudecode-discord-bridge.mjs +20 -3
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/tauri.conf.json +1 -1
- package/static/app/asset-manifest.json +28 -28
- package/static/app/assets/Act-CX5hL0Z3.js +2 -0
- package/static/app/assets/{Act-Di4tRFWY.js.map → Act-CX5hL0Z3.js.map} +1 -1
- package/static/app/assets/{Brain-BZB3Gy9w.js → Brain-m19en5wz.js} +3 -3
- package/static/app/assets/{Brain-BZB3Gy9w.js.map → Brain-m19en5wz.js.map} +1 -1
- package/static/app/assets/Capture-CdFSrZnA.js +2 -0
- package/static/app/assets/Capture-CdFSrZnA.js.map +1 -0
- package/static/app/assets/Library-CZFVGSmG.js +2 -0
- package/static/app/assets/{Library-DAtDDLdg.js.map → Library-CZFVGSmG.js.map} +1 -1
- package/static/app/assets/System-CMoOoUrZ.js +2 -0
- package/static/app/assets/{System-DEu0xNUc.js.map → System-CMoOoUrZ.js.map} +1 -1
- package/static/app/assets/index-B4_drWel.js +17 -0
- package/static/app/assets/index-B4_drWel.js.map +1 -0
- package/static/app/assets/index-DjpDeE0c.css +2 -0
- package/static/app/assets/{primitives-CdwcE--L.js → primitives-CFhU5Rka.js} +2 -2
- package/static/app/assets/{primitives-CdwcE--L.js.map → primitives-CFhU5Rka.js.map} +1 -1
- package/static/app/assets/textarea-uBpGDOJM.js +2 -0
- package/static/app/assets/{textarea-CqOdBPL1.js.map → textarea-uBpGDOJM.js.map} +1 -1
- package/static/app/index.html +2 -2
- package/static/app/assets/Act-Di4tRFWY.js +0 -2
- package/static/app/assets/Capture-tNyYWxnh.js +0 -2
- package/static/app/assets/Capture-tNyYWxnh.js.map +0 -1
- package/static/app/assets/Library-DAtDDLdg.js +0 -2
- package/static/app/assets/System-DEu0xNUc.js +0 -2
- package/static/app/assets/index-Bi_bpigM.css +0 -2
- package/static/app/assets/index-COuGp7_5.js +0 -17
- package/static/app/assets/index-COuGp7_5.js.map +0 -1
- package/static/app/assets/textarea-CqOdBPL1.js +0 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Lattice AI
|
|
2
2
|
|
|
3
|
-
**Lattice AI is
|
|
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
|
|
14
|
+
It is the finished private AI memory layer wrapped in a Living Brain experience — 7.7 complete product.
|
|
15
15
|
|
|
16
16
|
[](https://pypi.org/project/ltcai/)
|
|
17
17
|
[](https://www.npmjs.com/package/ltcai)
|
|
@@ -54,57 +54,63 @@ You need Lattice AI when:
|
|
|
54
54
|
|
|
55
55
|
## One-Minute Flow
|
|
56
56
|
|
|
57
|
-
1. Launch the app.
|
|
57
|
+
1. Launch the app and wake the Brain.
|
|
58
58
|
2. Create or open a local profile.
|
|
59
59
|
3. Let Lattice explain what this computer can run.
|
|
60
|
-
4. Start with the recommended model, or skip and choose later.
|
|
60
|
+
4. Start with the recommended model as the Brain's voice, or skip and choose later.
|
|
61
61
|
5. Talk to your Brain.
|
|
62
|
-
6.
|
|
62
|
+
6. Use the memory rings to move from current context to the full knowledge graph.
|
|
63
63
|
7. Back up, inspect, export, or restore the Brain when you need ownership actions.
|
|
64
64
|
|
|
65
65
|
## Living Brain Flow
|
|
66
66
|
|
|
67
|
-
### 1.
|
|
67
|
+
### 1. Wake Brain
|
|
68
|
+
|
|
69
|
+
The first screen makes the Brain the product. It explains the three-step path:
|
|
70
|
+
confirm owner, check the computer, choose the Brain voice.
|
|
71
|
+
|
|
72
|
+
### 2. Login
|
|
68
73
|
|
|
69
74
|
Choose the owner of the Brain. The profile is not a SaaS account by default; it
|
|
70
75
|
is the local identity for the knowledge you keep.
|
|
71
76
|
|
|
72
|
-

|
|
73
78
|
|
|
74
|
-
###
|
|
79
|
+
### 3. Environment Analysis
|
|
75
80
|
|
|
76
81
|
See what kind of local AI experience this computer can support before choosing a
|
|
77
82
|
model.
|
|
78
83
|
|
|
79
|
-

|
|
80
85
|
|
|
81
|
-
###
|
|
86
|
+
### 4. Recommended Models
|
|
82
87
|
|
|
83
88
|
Start with a short list: safest recommendation, faster model, stronger model.
|
|
84
89
|
Advanced details stay available without overwhelming first-time users.
|
|
85
90
|
|
|
86
|
-

|
|
87
92
|
|
|
88
|
-
###
|
|
93
|
+
### 5. Install And Load
|
|
89
94
|
|
|
90
95
|
Download and load only after consent. Lattice explains model size, local
|
|
91
96
|
execution, and network use before work starts.
|
|
92
97
|
|
|
93
|
-

|
|
94
99
|
|
|
95
|
-
###
|
|
100
|
+
### 6. Brain Chat
|
|
96
101
|
|
|
97
102
|
Talk normally. Useful decisions and context become memory, then appear later as
|
|
98
|
-
topics, relationships, and
|
|
103
|
+
topics, relationships, graph structure, and the concentric memory rings around
|
|
104
|
+
the Brain.
|
|
99
105
|
|
|
100
|
-

|
|
101
107
|
|
|
102
|
-
###
|
|
108
|
+
### 7. Review Center
|
|
103
109
|
|
|
104
110
|
Automation results are staged for review before they become durable decisions.
|
|
105
111
|
Snooze, unsnooze, run now, approve, and dismiss actions stay explicit.
|
|
106
112
|
|
|
107
|
-

|
|
108
114
|
|
|
109
115
|
## Brain Depths
|
|
110
116
|
|
|
@@ -120,10 +126,10 @@ The user travels inward from everyday memory to deeper structure:
|
|
|
120
126
|
|
|
121
127
|
Walkthrough:
|
|
122
128
|
|
|
123
|
-

|
|
124
130
|
|
|
125
131
|
Screenshot index and capture notes:
|
|
126
|
-
[output/release/v7.
|
|
132
|
+
[output/release/v7.7.0/SCREENSHOT_INDEX.md](output/release/v7.7.0/SCREENSHOT_INDEX.md)
|
|
127
133
|
|
|
128
134
|
## Install
|
|
129
135
|
|
|
@@ -200,34 +206,30 @@ See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for developer workflow details.
|
|
|
200
206
|
|
|
201
207
|
## Current Release
|
|
202
208
|
|
|
203
|
-
The current release is **7.
|
|
204
|
-
|
|
205
|
-
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
- `
|
|
222
|
-
- `dist/ltcai-7.5.0.tar.gz`
|
|
223
|
-
- `ltcai-7.5.0.tgz`
|
|
224
|
-
- `dist/ltcai-7.5.0.vsix`
|
|
225
|
-
- `src-tauri/target/release/bundle/dmg/Lattice AI_7.5.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`
|
|
226
228
|
|
|
227
229
|
Do not upload `dist/*`. Package registry publishing remains owner-run.
|
|
228
230
|
|
|
229
231
|
See [docs/ROADMAP_RECOMMENDATIONS.md](docs/ROADMAP_RECOMMENDATIONS.md) for the
|
|
230
|
-
strategic roadmap slices applied through 7.
|
|
232
|
+
strategic roadmap slices applied through 7.7.0 and the follow-up tracks.
|
|
231
233
|
|
|
232
234
|
## Known Limitations
|
|
233
235
|
|
|
@@ -244,6 +246,8 @@ strategic roadmap slices applied through 7.5.0 and the follow-up tracks.
|
|
|
244
246
|
|
|
245
247
|
| Version | Theme |
|
|
246
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 |
|
|
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 |
|
|
247
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 |
|
|
248
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 |
|
|
249
253
|
| 7.3.0 | Runtime Contract & Retrieval Quality: shared agent-run contract across runtimes plus deterministic hybrid recall/ranking regression gates |
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,48 @@ 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
|
+
|
|
26
|
+
## [7.6.0] - 2026-06-22
|
|
27
|
+
|
|
28
|
+
> Brain-Centered UX & Architecture Closure. Incorporates the two local review
|
|
29
|
+
> files into the release line with a Wake Brain first-run surface, memory rings
|
|
30
|
+
> plus direct depth controls, and machine-checkable architecture readiness gates.
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
- Wake Brain first-run entry before owner/profile setup, reducing onboarding to
|
|
34
|
+
the product promise first and the setup mechanics second.
|
|
35
|
+
- Concentric memory rings around Brain Home plus direct controls for Now, Memory,
|
|
36
|
+
Topics, Relationships, and Full Graph navigation.
|
|
37
|
+
- `latticeai.services.architecture_readiness.architecture_readiness()` and
|
|
38
|
+
`tests/unit/test_v76_review_completion.py` to keep AgentRuntime, ToolRegistry,
|
|
39
|
+
Config, server decomposition, KG hardening, and Brain UX review closure under
|
|
40
|
+
test.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
- Package/runtime/static metadata is synchronized to 7.6.0; package publish and
|
|
44
|
+
deployment remain owner-run only.
|
|
45
|
+
- README and release docs now describe 7.6.0 as the current release and point to
|
|
46
|
+
refreshed 7.6.0 screenshots, walkthrough GIF, and release evidence index under
|
|
47
|
+
`output/release/v7.6.0/`.
|
|
48
|
+
|
|
7
49
|
## [7.5.0] - 2026-06-20
|
|
8
50
|
|
|
9
51
|
> Runtime Debt Burn-down & Release Risk Cleanup. Turns the 7.4.0 contract
|
|
@@ -27,6 +69,9 @@ existed at that release.
|
|
|
27
69
|
- npm dependency overrides move `js-yaml` to a non-vulnerable version; `npm
|
|
28
70
|
audit` reports 0 vulnerabilities.
|
|
29
71
|
- CI lint compatibility is restored for the Brain quality gate script.
|
|
72
|
+
- Local MLX model preparation now recognizes valid existing Hugging Face cache
|
|
73
|
+
snapshots, avoiding an unnecessary re-download when the model already exists
|
|
74
|
+
outside Lattice's managed `~/.ltcai/hf-models` directory.
|
|
30
75
|
- Package/runtime/static metadata is synchronized to 7.5.0; package publish and
|
|
31
76
|
deployment remain owner-run only.
|
|
32
77
|
|
package/frontend/src/App.tsx
CHANGED
|
@@ -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.
|
|
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
|
|
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
|
|
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,
|
|
@@ -27,7 +28,7 @@ export function readProductFlowComplete() {
|
|
|
27
28
|
|
|
28
29
|
export function ProductFlow({ onComplete }: { onComplete: () => void }) {
|
|
29
30
|
const language = useAppStore((state) => state.language);
|
|
30
|
-
const [step, setStep] = React.useState<FlowStep>("
|
|
31
|
+
const [step, setStep] = React.useState<FlowStep>("wake");
|
|
31
32
|
const [analysis, setAnalysis] = React.useState<FlowAnalysis | null>(null);
|
|
32
33
|
const [analysisError, setAnalysisError] = React.useState<string | null>(null);
|
|
33
34
|
const [selected, setSelected] = React.useState<RecommendedModel | null>(null);
|
|
@@ -66,8 +67,13 @@ 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
|
|
|
75
|
+
{step === "wake" && <WakeBrainScreen onWake={() => setStep("login")} onUseExisting={() => completeFlow(onComplete)} />}
|
|
76
|
+
|
|
71
77
|
{step === "login" && <LoginScreen onSuccess={() => setStep("analysis")} />}
|
|
72
78
|
|
|
73
79
|
{step === "analysis" && (
|
|
@@ -99,12 +105,69 @@ export function ProductFlow({ onComplete }: { onComplete: () => void }) {
|
|
|
99
105
|
);
|
|
100
106
|
}
|
|
101
107
|
|
|
108
|
+
function WakeBrainScreen({ onWake, onUseExisting }: { onWake: () => void; onUseExisting: () => void }) {
|
|
109
|
+
const language = useAppStore((state) => state.language);
|
|
110
|
+
return (
|
|
111
|
+
<section className="ritual-wake" aria-label={t(language, "flow.wake.aria")}>
|
|
112
|
+
<div className="ritual-title">{t(language, "flow.wake.title")}</div>
|
|
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
|
+
|
|
131
|
+
<div className="ritual-wake-plan" aria-label={t(language, "flow.wake.plan.aria")}>
|
|
132
|
+
<div>
|
|
133
|
+
<span>1</span>
|
|
134
|
+
<strong>{t(language, "flow.wake.step.identity")}</strong>
|
|
135
|
+
</div>
|
|
136
|
+
<div>
|
|
137
|
+
<span>2</span>
|
|
138
|
+
<strong>{t(language, "flow.wake.step.check")}</strong>
|
|
139
|
+
</div>
|
|
140
|
+
<div>
|
|
141
|
+
<span>3</span>
|
|
142
|
+
<strong>{t(language, "flow.wake.step.voice")}</strong>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
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} />
|
|
154
|
+
</button>
|
|
155
|
+
<button type="button" className="ritual-secondary-button" onClick={onUseExisting}>
|
|
156
|
+
{t(language, "flow.wake.existing")}
|
|
157
|
+
</button>
|
|
158
|
+
</div>
|
|
159
|
+
<div style={{marginTop: "0.8rem", fontSize: "0.72rem", color: "hsl(var(--fg-muted))"}}>평균 2분 안에 첫 대화 시작 · 완전 무료 · 로컬 전용</div>
|
|
160
|
+
</section>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
102
164
|
function completeFlow(onComplete: () => void) {
|
|
103
165
|
try { localStorage.setItem(FLOW_COMPLETE_KEY, "true"); } catch {}
|
|
104
166
|
onComplete();
|
|
105
167
|
}
|
|
106
168
|
|
|
107
169
|
function brainStateForStep(step: FlowStep): BrainState {
|
|
170
|
+
if (step === "wake") return "idle";
|
|
108
171
|
if (step === "analysis") return "listening";
|
|
109
172
|
if (step === "recommend") return "recalling";
|
|
110
173
|
if (step === "install") return "thinking";
|
|
@@ -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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
{
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
<div className="ritual-model-
|
|
56
|
-
|
|
57
|
-
|
|
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,6 +1,6 @@
|
|
|
1
1
|
import { asArray } from "@/lib/utils";
|
|
2
2
|
|
|
3
|
-
export type FlowStep = "login" | "analysis" | "recommend" | "install";
|
|
3
|
+
export type FlowStep = "wake" | "login" | "analysis" | "recommend" | "install";
|
|
4
4
|
export type ApiData = Record<string, unknown>;
|
|
5
5
|
|
|
6
6
|
export type FlowAnalysis = {
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
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>
|
|
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,
|