ltcai 4.6.0 → 4.6.1

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.
@@ -78,39 +78,58 @@ export function ProductFlow({ onComplete }: { onComplete: () => void }) {
78
78
  }, [analysis, step]);
79
79
 
80
80
  return (
81
- <main className="product-flow-shell" aria-label="Lattice first run">
82
- <div className="product-flow-orbit" aria-hidden="true" />
83
- {step === "login" ? (
84
- <LoginScreen onSuccess={() => setStep("analysis")} />
85
- ) : null}
86
- {step === "analysis" ? (
87
- <AnalysisScreen
88
- analysis={analysis}
89
- error={analysisError}
90
- onContinue={() => setStep("recommend")}
91
- />
92
- ) : null}
93
- {step === "recommend" ? (
94
- <RecommendationScreen
95
- recommendations={recommendations}
96
- onBack={() => setStep("analysis")}
97
- onSelect={(model) => {
98
- setSelected(model);
99
- setStep("install");
100
- }}
101
- />
102
- ) : null}
103
- {step === "install" ? (
104
- <InstallScreen
105
- model={selected || recommendations[0] || fallbackModel()}
106
- onBack={() => setStep("recommend")}
107
- onComplete={() => {
108
- try { localStorage.setItem(FLOW_COMPLETE_KEY, "true"); } catch {}
109
- onComplete();
110
- }}
111
- />
112
- ) : null}
113
- </main>
81
+ <div className="ritual-shell" aria-label="Awaken your Brain">
82
+ <div className="ritual-container">
83
+ {/* The living presence participates in the ritual at every step */}
84
+ <div className="ritual-brain">
85
+ <LivingBrain
86
+ state={
87
+ step === "login" ? "idle" :
88
+ step === "analysis" ? "listening" :
89
+ step === "recommend" ? "recalling" :
90
+ "thinking"
91
+ }
92
+ intensity={step === "install" ? 0.92 : 0.7}
93
+ size="large"
94
+ showLabel={false}
95
+ />
96
+ </div>
97
+
98
+ {step === "login" && (
99
+ <LoginScreen onSuccess={() => setStep("analysis")} />
100
+ )}
101
+
102
+ {step === "analysis" && (
103
+ <AnalysisScreen
104
+ analysis={analysis}
105
+ error={analysisError}
106
+ onContinue={() => setStep("recommend")}
107
+ />
108
+ )}
109
+
110
+ {step === "recommend" && (
111
+ <RecommendationScreen
112
+ recommendations={recommendations}
113
+ onBack={() => setStep("analysis")}
114
+ onSelect={(model) => {
115
+ setSelected(model);
116
+ setStep("install");
117
+ }}
118
+ />
119
+ )}
120
+
121
+ {step === "install" && (
122
+ <InstallScreen
123
+ model={selected || recommendations[0] || fallbackModel()}
124
+ onBack={() => setStep("recommend")}
125
+ onComplete={() => {
126
+ try { localStorage.setItem(FLOW_COMPLETE_KEY, "true"); } catch {}
127
+ onComplete();
128
+ }}
129
+ />
130
+ )}
131
+ </div>
132
+ </div>
114
133
  );
115
134
  }
116
135
 
@@ -157,34 +176,36 @@ function LoginScreen({ onSuccess }: { onSuccess: () => void }) {
157
176
  }
158
177
 
159
178
  return (
160
- <section className="login-screen" aria-label="Login">
161
- <div className="login-mark" aria-hidden="true"><LockKeyhole className="h-5 w-5" /></div>
162
- <div className="login-card">
163
- <div>
164
- <div className="login-kicker">Lattice AI</div>
165
- <h1>Enter your Brain.</h1>
166
- <p>Your private workspace starts with a local profile.</p>
179
+ <div>
180
+ <div className="ritual-title">Welcome to your mind.</div>
181
+ <div className="ritual-subtitle">This is private. Everything stays on your machine. Begin by opening a local profile for your Brain.</div>
182
+
183
+ <form onSubmit={submit} className="ritual-card" style={{ maxWidth: 420, margin: "0 auto" }}>
184
+ <div style={{ display: "grid", gap: "0.85rem" }}>
185
+ <div>
186
+ <div style={{ fontSize: "0.75rem", textTransform: "uppercase", letterSpacing: "1px", color: "hsl(var(--fg-muted))", marginBottom: 4 }}>Your name</div>
187
+ <Input value={name} onChange={(e) => setName(e.target.value)} placeholder="You" />
188
+ </div>
189
+ <div>
190
+ <div style={{ fontSize: "0.75rem", textTransform: "uppercase", letterSpacing: "1px", color: "hsl(var(--fg-muted))", marginBottom: 4 }}>Email (local only)</div>
191
+ <Input value={email} onChange={(e) => setEmail(e.target.value)} type="email" placeholder="you@local" />
192
+ </div>
193
+ <div>
194
+ <div style={{ fontSize: "0.75rem", textTransform: "uppercase", letterSpacing: "1px", color: "hsl(var(--fg-muted))", marginBottom: 4 }}>Password</div>
195
+ <Input value={password} onChange={(e) => setPassword(e.target.value)} type="password" placeholder="Create a strong local password" />
196
+ </div>
167
197
  </div>
168
- <form className="login-form" onSubmit={submit}>
169
- <label>
170
- <span>Name</span>
171
- <Input value={name} onChange={(event) => setName(event.target.value)} autoComplete="name" />
172
- </label>
173
- <label>
174
- <span>Email</span>
175
- <Input value={email} onChange={(event) => setEmail(event.target.value)} type="email" autoComplete="email" />
176
- </label>
177
- <label>
178
- <span>Password</span>
179
- <Input value={password} onChange={(event) => setPassword(event.target.value)} type="password" autoComplete="current-password" placeholder="Use your local password" />
180
- </label>
181
- {error ? <div className="flow-error">{error}</div> : null}
182
- <Button className="login-submit" type="submit" disabled={busy || !email.trim()}>
183
- {busy ? "Opening" : "Continue"} <ChevronRight className="h-4 w-4" />
184
- </Button>
185
- </form>
186
- </div>
187
- </section>
198
+
199
+ {error && <div style={{ marginTop: "0.85rem", padding: "0.6rem 0.85rem", background: "hsl(var(--destructive)/0.12)", border: "1px solid hsl(var(--destructive)/0.4)", borderRadius: 10, fontSize: "0.9rem" }}>{error}</div>}
200
+
201
+ <Button type="submit" disabled={busy || !email.trim()} style={{ width: "100%", marginTop: "1rem" }}>
202
+ {busy ? "Opening the Brain..." : "Open my Brain"}
203
+ </Button>
204
+ <div style={{ fontSize: "0.75rem", color: "hsl(var(--fg-muted))", marginTop: "0.6rem" }}>
205
+ Your first conversation will feel like coming home.
206
+ </div>
207
+ </form>
208
+ </div>
188
209
  );
189
210
  }
190
211
 
@@ -199,36 +220,42 @@ function AnalysisScreen({
199
220
  }) {
200
221
  const detected = buildDetectedFacts(analysis);
201
222
  return (
202
- <section className="flow-panel analysis-screen" aria-label="Environment Analysis">
203
- <div className="flow-panel-head">
204
- <div>
205
- <div className="flow-kicker"><MonitorCog className="h-4 w-4" /> Environment Analysis</div>
206
- <h1>Learning what your computer can do.</h1>
207
- <p>Lattice checks the essentials, then recommends the best local Brain for this machine.</p>
208
- </div>
209
- <Badge variant={analysis ? "success" : "muted"}>{analysis ? "complete" : "analyzing"}</Badge>
223
+ <div>
224
+ <div className="ritual-title">Understanding your home.</div>
225
+ <div className="ritual-subtitle">
226
+ We are learning what kind of mind this computer can support. Your Brain will live here — quietly, privately, powerfully.
210
227
  </div>
211
- <div className="analysis-grid">
212
- {detected.map((item) => (
213
- <div key={item.label} className="analysis-fact">
214
- <span>{item.label}</span>
215
- <strong>{item.value}</strong>
216
- <small>{item.detail}</small>
228
+
229
+ <div className="ritual-fact-grid">
230
+ {detected.map((item, idx) => (
231
+ <div key={idx} className="ritual-fact">
232
+ <div className="ritual-fact-label">{item.label}</div>
233
+ <div className="ritual-fact-value">{item.value}</div>
234
+ <div style={{ fontSize: "0.8rem", color: "hsl(var(--fg-muted))", marginTop: 3 }}>{item.detail}</div>
217
235
  </div>
218
236
  ))}
219
237
  </div>
220
- <div className="recommendation-callout">
221
- <Sparkles className="h-5 w-5" />
222
- <div>
223
- <strong>{analysis ? recommendedSummary(analysis) : "Recommendation is being prepared."}</strong>
224
- <span>{analysis ? "You will choose from a short, ranked list next." : "This usually takes a moment."}</span>
238
+
239
+ <div className="ritual-card" style={{ marginTop: "1rem" }}>
240
+ <div style={{ display: "flex", alignItems: "center", gap: "0.6rem" }}>
241
+ <Sparkles style={{ color: "hsl(var(--brain-core))" }} />
242
+ <div>
243
+ <div style={{ fontWeight: 620 }}>{analysis ? recommendedSummary(analysis) : "Preparing the best fit..."}</div>
244
+ <div style={{ fontSize: "0.9rem", color: "hsl(var(--fg-muted))" }}>
245
+ {analysis ? "A short, personal list of minds is ready for you to choose from." : "Reading your machine. This is gentle."}
246
+ </div>
247
+ </div>
225
248
  </div>
226
249
  </div>
227
- {error ? <div className="flow-error">{error}</div> : null}
228
- <div className="flow-actions">
229
- <Button onClick={onContinue} disabled={!analysis && !error}>See recommended models</Button>
250
+
251
+ {error && <div className="ritual-card" style={{ borderColor: "hsl(var(--destructive)/0.4)", background: "hsl(var(--destructive)/0.06)" }}>{error}</div>}
252
+
253
+ <div style={{ marginTop: "1.25rem" }}>
254
+ <Button onClick={onContinue} disabled={!analysis && !error} style={{ minWidth: 260 }}>
255
+ See how your Brain can think
256
+ </Button>
230
257
  </div>
231
- </section>
258
+ </div>
232
259
  );
233
260
  }
234
261
 
@@ -243,36 +270,34 @@ function RecommendationScreen({
243
270
  }) {
244
271
  const items = recommendations.length ? recommendations : [fallbackModel()];
245
272
  return (
246
- <section className="flow-panel recommendation-screen" aria-label="Recommended Models">
247
- <div className="flow-panel-head">
248
- <div>
249
- <div className="flow-kicker"><Cpu className="h-4 w-4" /> Recommended Models</div>
250
- <h1>Recommended for your computer.</h1>
251
- <p>A short list, ranked for this Mac. No catalog digging required.</p>
252
- </div>
273
+ <div>
274
+ <div className="ritual-title">How shall your mind think today?</div>
275
+ <div className="ritual-subtitle">
276
+ A short, honest list chosen for the computer you are on right now. Pick the one that feels right.
253
277
  </div>
254
- <div className="model-recommendation-list">
278
+
279
+ <div style={{ maxWidth: 560, margin: "0 auto" }}>
255
280
  {items.slice(0, 3).map((model, index) => (
256
281
  <button
257
282
  key={`${model.role}-${model.id}`}
258
- className={cn("model-recommendation-card", model.role)}
259
- onClick={() => onSelect(model)}
283
+ className="ritual-model-card"
284
+ onClick={() => model.supported && onSelect(model)}
260
285
  disabled={!model.supported}
286
+ style={{ width: "100%" }}
261
287
  >
262
- <span className="model-rank">{rankLabel(model.role, index)}</span>
263
- <span>
264
- <strong>{model.shortName}</strong>
265
- <small>{model.reason}</small>
266
- </span>
267
- <Badge variant={model.supported ? "success" : "warning"}>{model.supported ? model.size || "ready" : "needs update"}</Badge>
288
+ <div className="role">{rankLabel(model.role, index)}</div>
289
+ <div className="name">{model.shortName}</div>
290
+ <div className="reason">{model.reason} · {model.size || "ready"}</div>
291
+ {!model.supported && <div style={{ color: "hsl(var(--destructive))", marginTop: 6, fontSize: "0.85rem" }}>Needs attention on this machine</div>}
268
292
  </button>
269
293
  ))}
270
294
  </div>
271
- <div className="flow-actions split">
295
+
296
+ <div style={{ marginTop: "1.1rem", display: "flex", justifyContent: "center", gap: "1rem", alignItems: "center" }}>
272
297
  <Button variant="ghost" onClick={onBack}>Back</Button>
273
- <span>Choose one recommendation to continue.</span>
298
+ <div style={{ fontSize: "0.82rem", color: "hsl(var(--fg-muted))" }}>Your choice becomes the current voice of your Brain.</div>
274
299
  </div>
275
- </section>
300
+ </div>
276
301
  );
277
302
  }
278
303
 
@@ -288,7 +313,7 @@ function InstallScreen({
288
313
  const [busy, setBusy] = React.useState(false);
289
314
  const [stage, setStage] = React.useState<InstallStage>("idle");
290
315
  const [percent, setPercent] = React.useState(0);
291
- const [message, setMessage] = React.useState("Ready when you are.");
316
+ const [message, setMessage] = React.useState("Your Brain is waiting for this mind.");
292
317
  const [error, setError] = React.useState<string | null>(null);
293
318
 
294
319
  async function start() {
@@ -329,39 +354,72 @@ function InstallScreen({
329
354
  }
330
355
  }
331
356
 
357
+ const brainStateForStage: any =
358
+ stage === "download" ? "thinking" :
359
+ stage === "validate" ? "recalling" :
360
+ stage === "load" ? "synthesizing" :
361
+ stage === "done" ? "idle" : "listening";
362
+
332
363
  return (
333
- <section className="flow-panel install-screen" aria-label="Install and Load">
334
- <div className="install-hero">
335
- <LivingBrain activity={busy ? "thinking" : stage === "done" ? "listening" : "idle"} compact />
336
- <div>
337
- <div className="flow-kicker"><Download className="h-4 w-4" /> Install & Load</div>
338
- <h1>{model.shortName}</h1>
339
- <p>Lattice will install, download, validate, and load the selected Brain.</p>
340
- </div>
364
+ <div>
365
+ <div className="ritual-title">Bring this mind home.</div>
366
+ <div className="ritual-subtitle">
367
+ <strong>{model.shortName}</strong> — {model.reason}.<br />
368
+ We will download (if needed), validate, and load it. Nothing happens without your explicit consent.
341
369
  </div>
342
- <div className="install-steps">
343
- {(["install", "download", "validate", "load"] as const).map((item) => (
344
- <div key={item} className={cn("install-step", installStepState(stage, item))}>
345
- <CheckCircle2 className="h-4 w-4" />
346
- <span>{installLabel(item)}</span>
347
- </div>
348
- ))}
370
+
371
+ {/* Living Brain reacts to the ceremony of installation */}
372
+ <div style={{ margin: "0.6rem auto 1rem" }}>
373
+ <LivingBrain
374
+ state={brainStateForStage}
375
+ intensity={stage === "download" || stage === "load" ? 0.96 : 0.82}
376
+ size="normal"
377
+ />
378
+ </div>
379
+
380
+ <div className="ritual-progress">
381
+ <div className="ritual-stage-list">
382
+ {(["install", "download", "validate", "load"] as const).map((item) => (
383
+ <div key={item} className={`ritual-stage ${installStepState(stage, item)}`}>
384
+ <CheckCircle2 style={{ width: 15, height: 15 }} />
385
+ <span>{installLabel(item)}</span>
386
+ </div>
387
+ ))}
388
+ </div>
389
+
390
+ <div className="ritual-bar">
391
+ <span style={{ width: `${Math.max(4, Math.min(100, percent))}%` }} />
392
+ </div>
349
393
  </div>
350
- <div className="install-progress">
351
- <div>
352
- <strong>{message}</strong>
353
- <span>{stage === "error" ? "We will explain what to try next." : `${Math.round(percent)}%`}</span>
394
+
395
+ <div className="ritual-status">{message}</div>
396
+
397
+ {error && (
398
+ <div className="ritual-card" style={{ borderColor: "hsl(var(--destructive)/0.45)", background: "hsl(var(--destructive)/0.07)", marginBottom: "1rem" }}>
399
+ {error}
400
+ <div style={{ marginTop: "0.5rem", fontSize: "0.85rem" }}>You can go back and choose a different mind, or try again.</div>
354
401
  </div>
355
- <div className="install-bar"><span style={{ width: `${Math.max(0, Math.min(100, percent))}%` }} /></div>
402
+ )}
403
+
404
+ <div style={{ display: "flex", gap: "0.75rem", justifyContent: "center", marginTop: "1rem" }}>
405
+ <Button variant="ghost" onClick={onBack} disabled={busy}>Choose differently</Button>
406
+
407
+ {stage !== "done" ? (
408
+ <Button
409
+ onClick={start}
410
+ disabled={busy || !model.supported}
411
+ >
412
+ {busy ? "Waking the mind..." : "Yes — make this my Brain"}
413
+ </Button>
414
+ ) : (
415
+ <Button onClick={onComplete}>Enter your Brain</Button>
416
+ )}
356
417
  </div>
357
- {error ? <div className="flow-error">{error}</div> : null}
358
- <div className="flow-actions split">
359
- <Button variant="ghost" onClick={onBack} disabled={busy}>Back</Button>
360
- <Button onClick={stage === "done" ? onComplete : start} disabled={busy || !model.supported}>
361
- {stage === "done" ? "Enter Brain" : busy ? "Loading" : "Install & Load"}
362
- </Button>
418
+
419
+ <div style={{ fontSize: "0.72rem", color: "hsl(var(--fg-muted))", marginTop: "0.9rem" }}>
420
+ Explicit consent only. All work happens locally on your machine.
363
421
  </div>
364
- </section>
422
+ </div>
365
423
  );
366
424
  }
367
425