ltcai 8.0.0 → 8.2.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 +33 -72
- package/docs/CHANGELOG.md +52 -874
- package/docs/DEVELOPMENT.md +26 -3
- package/docs/LEGACY_COMPATIBILITY.md +3 -1
- package/docs/TRUST_MODEL.md +26 -26
- package/docs/WHY_LATTICE.md +28 -25
- package/docs/kg-schema.md +6 -4
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/embeddings.py +9 -1
- package/lattice_brain/graph/schema.py +6 -0
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/computer_use.py +2 -0
- package/latticeai/api/memory.py +13 -0
- package/latticeai/app_factory.py +43 -162
- package/latticeai/brain/__init__.py +1 -1
- package/latticeai/core/config.py +10 -0
- package/latticeai/core/local_embeddings.py +8 -0
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/mcp_registry.py +140 -1
- package/latticeai/core/tool_registry.py +10 -0
- package/latticeai/core/workspace_graph_trace.py +132 -0
- package/latticeai/core/workspace_memory.py +75 -0
- package/latticeai/core/workspace_os.py +135 -1283
- package/latticeai/core/workspace_os_utils.py +132 -0
- package/latticeai/core/workspace_permissions.py +99 -0
- package/latticeai/core/workspace_plugins.py +97 -0
- package/latticeai/core/workspace_runs.py +612 -0
- package/latticeai/core/workspace_skills.py +114 -0
- package/latticeai/core/workspace_snapshots.py +195 -0
- package/latticeai/core/workspace_timeline.py +107 -0
- package/latticeai/runtime/audit_runtime.py +64 -0
- package/latticeai/runtime/config_runtime.py +4 -0
- package/latticeai/runtime/sso_runtime.py +52 -0
- package/latticeai/services/architecture_readiness.py +6 -5
- package/latticeai/services/memory_service.py +215 -7
- package/latticeai/services/model_capability_registry.py +68 -0
- package/latticeai/services/model_engines.py +603 -0
- package/latticeai/services/model_loading.py +482 -0
- package/latticeai/services/model_recommendation.py +5 -0
- package/latticeai/services/model_runtime.py +146 -837
- package/latticeai/services/product_readiness.py +19 -18
- package/package.json +2 -2
- package/scripts/build_frontend_assets.mjs +11 -2
- 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 +11 -102
- package/static/app/assets/Act-D9jIknFd.js +1 -0
- package/static/app/assets/Brain-CFOtWbPN.js +321 -0
- package/static/app/assets/{Capture-B9Tlhzqr.js → Capture-Q4WYzwr5.js} +1 -2
- package/static/app/assets/Library-C5Q2yWee.js +1 -0
- package/static/app/assets/System-BLbjdr1_.js +1 -0
- package/static/app/assets/core-CwxXejkd.js +1 -2
- package/static/app/assets/index-BqammyNu.js +16 -0
- package/static/app/assets/index-ty1iGgZu.css +2 -0
- package/static/app/assets/primitives-Br8uSfZ4.js +1 -0
- package/static/app/assets/{textarea-BZk6ybp5.js → textarea-BnhNs1_X.js} +1 -2
- package/static/app/index.html +3 -10
- package/static/app/theme-boot.js +8 -0
- package/static/sw.js +1 -1
- package/tools/__init__.py +2 -1
- package/tools/computer.py +21 -0
- package/docs/CARRYOVER_AUDIT_v3.6.0.md +0 -61
- package/docs/HANDOVER_v3.6.0.md +0 -46
- package/docs/RUNTIME_HOOK_COVERAGE_v3.5.0.md +0 -56
- package/docs/RUNTIME_HOOK_COVERAGE_v3.6.0.md +0 -49
- package/docs/V2_ARCHITECTURE.md +0 -561
- package/docs/V3_2_AUDIT.md +0 -82
- package/docs/V3_BACKEND_ARCHITECTURE.md +0 -138
- package/docs/V3_FRONTEND.md +0 -140
- package/docs/V4_1_FRONTEND_ARCHITECTURE_REVIEW.md +0 -65
- package/docs/V4_1_FRONTEND_MIGRATION_REPORT.md +0 -70
- package/docs/V4_1_VALIDATION_REPORT.md +0 -47
- package/docs/V4_2_BRAIN_CORE_ARCHITECTURE.md +0 -97
- package/docs/V4_2_STORAGE_MIGRATION_REPORT.md +0 -91
- package/docs/V4_2_VALIDATION_REPORT.md +0 -89
- package/docs/V4_3_2_DEADCODE_AUDIT_REPORT.md +0 -174
- package/docs/V4_3_2_DOCUMENTATION_CLEANUP_REPORT.md +0 -81
- package/docs/V4_3_2_GITHUB_VERCEL_CHECK_REPORT.md +0 -75
- package/docs/V4_3_2_GRAPH_UX_REPORT.md +0 -48
- package/docs/V4_3_2_INDEPENDENT_AUDIT_PACKAGE.md +0 -209
- package/docs/V4_3_2_PRODUCT_POLISH_REPORT.md +0 -57
- package/docs/V4_3_2_SELF_AUDIT_REPORT.md +0 -63
- package/docs/V4_3_2_VALIDATION_REPORT.md +0 -97
- package/docs/V4_3_3_VALIDATION_REPORT.md +0 -46
- package/docs/V4_3_PORTABILITY_ARCHITECTURE.md +0 -69
- package/docs/V4_3_PRIVACY_AUDIT.md +0 -60
- package/docs/V4_3_PRODUCT_HARDENING_REPORT.md +0 -53
- package/docs/V4_3_VALIDATION_REPORT.md +0 -58
- package/docs/V4_4_0_EXTRACTION_REPORT.md +0 -239
- package/docs/V4_5_0_GEMMA_RUNTIME_COMPATIBILITY_REPORT.md +0 -49
- package/docs/V4_5_0_GRAPH_UX_REPORT.md +0 -34
- package/docs/V4_5_0_MODEL_RUNTIME_UX_REPORT.md +0 -40
- package/docs/V4_5_0_ONBOARDING_REPORT.md +0 -31
- package/docs/V4_5_0_PRODUCT_EXPERIENCE_RECOVERY_REPORT.md +0 -49
- package/docs/V4_5_0_VALIDATION_REPORT.md +0 -60
- package/docs/V4_5_1_GRAPH_EXPERIENCE_REPORT.md +0 -33
- package/docs/V4_5_1_MODEL_EXPERIENCE_REPORT.md +0 -37
- package/docs/V4_5_1_NAVIGATION_REPORT.md +0 -37
- package/docs/V4_5_1_ONBOARDING_REPORT.md +0 -29
- package/docs/V4_5_1_PRODUCT_REIMAGINING_REPORT.md +0 -61
- package/docs/V4_5_1_RC_ARTIFACTS.md +0 -44
- package/docs/V4_5_1_UX_REPORT.md +0 -45
- package/docs/V4_5_1_VALIDATION_REPORT.md +0 -55
- package/docs/V4_5_1_VISUAL_DESIGN_REPORT.md +0 -30
- package/docs/V4_6_0_LIVING_BRAIN_EXPERIENCE_REPORT.md +0 -72
- package/docs/V4_6_1_RELEASE_REFRESH_REPORT.md +0 -42
- package/docs/V4_7_0_ADMIN_SEPARATION_REPORT.md +0 -42
- package/docs/V4_7_1_ADMIN_OPERATIONS_REPORT.md +0 -49
- package/docs/V4_7_2_INTUITIVE_BRAIN_UX_REPORT.md +0 -62
- package/docs/V4_BRAIN_ARCHITECTURE.md +0 -322
- package/docs/V4_DIGITAL_BRAIN_RECOVERY.md +0 -555
- package/docs/V4_IMPLEMENTATION_PLAN.md +0 -470
- package/frontend/index.html +0 -24
- package/frontend/openapi.json +0 -15425
- package/frontend/src/App.tsx +0 -243
- package/frontend/src/api/client.ts +0 -580
- package/frontend/src/api/openapi.ts +0 -17892
- package/frontend/src/components/AdminAccessGate.tsx +0 -70
- package/frontend/src/components/FeedbackState.tsx +0 -45
- package/frontend/src/components/LanguageSwitcher.tsx +0 -23
- package/frontend/src/components/LivingBrain.tsx +0 -220
- package/frontend/src/components/ProductFlow.tsx +0 -171
- package/frontend/src/components/WorkspaceProfileSwitcher.tsx +0 -176
- package/frontend/src/components/onboarding/AnalysisScreen.tsx +0 -135
- package/frontend/src/components/onboarding/DownloadConsentPanel.tsx +0 -29
- package/frontend/src/components/onboarding/InstallScreen.tsx +0 -263
- package/frontend/src/components/onboarding/LanguageChooser.tsx +0 -23
- package/frontend/src/components/onboarding/LoginScreen.tsx +0 -131
- package/frontend/src/components/onboarding/ProductFlowScreens.tsx +0 -13
- package/frontend/src/components/onboarding/RecommendationScreen.tsx +0 -116
- package/frontend/src/components/onboarding/recommendationModel.ts +0 -189
- package/frontend/src/components/primitives.tsx +0 -392
- package/frontend/src/components/ui/badge.tsx +0 -27
- package/frontend/src/components/ui/button.tsx +0 -37
- package/frontend/src/components/ui/card.tsx +0 -22
- package/frontend/src/components/ui/input.tsx +0 -16
- package/frontend/src/components/ui/textarea.tsx +0 -16
- package/frontend/src/features/admin/AdminConsole.tsx +0 -334
- package/frontend/src/features/brain/BrainCarePanel.tsx +0 -254
- package/frontend/src/features/brain/BrainComposer.tsx +0 -85
- package/frontend/src/features/brain/BrainConversation.tsx +0 -688
- package/frontend/src/features/brain/BrainGraphLayer.tsx +0 -365
- package/frontend/src/features/brain/BrainHome.tsx +0 -624
- package/frontend/src/features/brain/BrainMemoryLayer.tsx +0 -45
- package/frontend/src/features/brain/BrainOverviewPanel.tsx +0 -149
- package/frontend/src/features/brain/BrainRelationshipLayer.tsx +0 -43
- package/frontend/src/features/brain/DepthEmergence.tsx +0 -53
- package/frontend/src/features/brain/brainData.ts +0 -246
- package/frontend/src/features/brain/graphLayout.ts +0 -37
- package/frontend/src/features/brain/types.ts +0 -150
- package/frontend/src/features/review/ReviewCard.tsx +0 -100
- package/frontend/src/features/review/ReviewInbox.tsx +0 -127
- package/frontend/src/features/review/reviewHelpers.ts +0 -69
- package/frontend/src/i18n.ts +0 -1303
- package/frontend/src/lib/utils.ts +0 -33
- package/frontend/src/main.tsx +0 -23
- package/frontend/src/pages/Act.tsx +0 -458
- package/frontend/src/pages/Ask.tsx +0 -14
- package/frontend/src/pages/Brain.tsx +0 -914
- package/frontend/src/pages/Capture.tsx +0 -258
- package/frontend/src/pages/Library.tsx +0 -486
- package/frontend/src/pages/System.tsx +0 -621
- package/frontend/src/routes.ts +0 -92
- package/frontend/src/store/appStore.ts +0 -94
- package/frontend/src/styles.css +0 -6579
- package/static/app/assets/Act-DOvf59ru.js +0 -2
- package/static/app/assets/Act-DOvf59ru.js.map +0 -1
- package/static/app/assets/Brain-C7_0mEiI.js +0 -322
- package/static/app/assets/Brain-C7_0mEiI.js.map +0 -1
- package/static/app/assets/Capture-B9Tlhzqr.js.map +0 -1
- package/static/app/assets/Library-BJPEEm5O.js +0 -2
- package/static/app/assets/Library-BJPEEm5O.js.map +0 -1
- package/static/app/assets/System-D6t9jo9V.js +0 -2
- package/static/app/assets/System-D6t9jo9V.js.map +0 -1
- package/static/app/assets/core-CwxXejkd.js.map +0 -1
- package/static/app/assets/index-C7g26IF6.css +0 -2
- package/static/app/assets/index-DbcEYJQ2.js +0 -17
- package/static/app/assets/index-DbcEYJQ2.js.map +0 -1
- package/static/app/assets/primitives-CD38lt4n.js +0 -2
- package/static/app/assets/primitives-CD38lt4n.js.map +0 -1
- package/static/app/assets/textarea-BZk6ybp5.js.map +0 -1
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { ShieldCheck, Lock } from "lucide-react";
|
|
3
|
-
import { t, type Language } from "@/i18n";
|
|
4
|
-
import { useAppStore, type WorkspaceMode } from "@/store/appStore";
|
|
5
|
-
|
|
6
|
-
const MODES: WorkspaceMode[] = ["basic", "advanced", "admin"];
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Role-aware admin entry point for the Brain shell.
|
|
10
|
-
*
|
|
11
|
-
* Mode is the access proxy used across the app (SystemPage gates the admin
|
|
12
|
-
* tab and AdminPanel on `mode`). When the user is in advanced/admin mode we
|
|
13
|
-
* surface a discoverable Admin Console button plus a compact mode switcher.
|
|
14
|
-
* In basic mode the console is hidden behind an explained affordance that
|
|
15
|
-
* promotes the user to admin mode rather than failing silently.
|
|
16
|
-
*/
|
|
17
|
-
export function AdminAccessGate({ language }: { language: Language }) {
|
|
18
|
-
const mode = useAppStore((state) => state.mode);
|
|
19
|
-
const setMode = useAppStore((state) => state.setMode);
|
|
20
|
-
const canSeeAdmin = mode === "advanced" || mode === "admin";
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<div className="admin-access-gate">
|
|
24
|
-
{canSeeAdmin ? (
|
|
25
|
-
<button
|
|
26
|
-
type="button"
|
|
27
|
-
className="admin-access-button"
|
|
28
|
-
title={t(language, "shell.admin.tooltip")}
|
|
29
|
-
aria-label={t(language, "shell.admin.open")}
|
|
30
|
-
onClick={() => {
|
|
31
|
-
window.location.hash = "/admin";
|
|
32
|
-
}}
|
|
33
|
-
>
|
|
34
|
-
<ShieldCheck className="h-3.5 w-3.5" aria-hidden="true" />
|
|
35
|
-
<span>{t(language, "shell.admin.label")}</span>
|
|
36
|
-
</button>
|
|
37
|
-
) : (
|
|
38
|
-
<button
|
|
39
|
-
type="button"
|
|
40
|
-
className="admin-access-button is-locked"
|
|
41
|
-
title={t(language, "shell.admin.needsMode")}
|
|
42
|
-
aria-label={t(language, "shell.admin.needsMode")}
|
|
43
|
-
onClick={() => setMode("admin")}
|
|
44
|
-
>
|
|
45
|
-
<Lock className="h-3.5 w-3.5" aria-hidden="true" />
|
|
46
|
-
<span>{t(language, "shell.admin.enable")}</span>
|
|
47
|
-
</button>
|
|
48
|
-
)}
|
|
49
|
-
|
|
50
|
-
<div
|
|
51
|
-
className="mode-switcher-mini"
|
|
52
|
-
role="group"
|
|
53
|
-
aria-label={t(language, "shell.mode.label")}
|
|
54
|
-
title={t(language, "shell.mode.info")}
|
|
55
|
-
>
|
|
56
|
-
{MODES.map((item) => (
|
|
57
|
-
<button
|
|
58
|
-
key={item}
|
|
59
|
-
type="button"
|
|
60
|
-
className={mode === item ? "is-active" : ""}
|
|
61
|
-
aria-pressed={mode === item}
|
|
62
|
-
onClick={() => setMode(item)}
|
|
63
|
-
>
|
|
64
|
-
{t(language, `shell.mode.${item}`)}
|
|
65
|
-
</button>
|
|
66
|
-
))}
|
|
67
|
-
</div>
|
|
68
|
-
</div>
|
|
69
|
-
);
|
|
70
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { AlertTriangle, Inbox, RotateCcw } from "lucide-react";
|
|
3
|
-
import { t, type Language } from "@/i18n";
|
|
4
|
-
|
|
5
|
-
export type FeedbackTone = "empty" | "error";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Shared empty / error feedback surface so every view tells the user what just
|
|
9
|
-
* happened and what to do next, instead of a blank or silent failure.
|
|
10
|
-
*/
|
|
11
|
-
export function FeedbackState({
|
|
12
|
-
tone,
|
|
13
|
-
language,
|
|
14
|
-
title,
|
|
15
|
-
body,
|
|
16
|
-
actionLabel,
|
|
17
|
-
onAction,
|
|
18
|
-
}: {
|
|
19
|
-
tone: FeedbackTone;
|
|
20
|
-
language: Language;
|
|
21
|
-
title: string;
|
|
22
|
-
body?: string;
|
|
23
|
-
actionLabel?: string;
|
|
24
|
-
onAction?: () => void;
|
|
25
|
-
}) {
|
|
26
|
-
const isError = tone === "error";
|
|
27
|
-
const resolvedActionLabel = actionLabel || (isError ? t(language, "feedback.retry") : undefined);
|
|
28
|
-
return (
|
|
29
|
-
<div className={`feedback-state is-${tone}`} role={isError ? "alert" : "status"}>
|
|
30
|
-
<span className="feedback-state-icon" aria-hidden="true">
|
|
31
|
-
{isError ? <AlertTriangle className="h-4 w-4" /> : <Inbox className="h-4 w-4" />}
|
|
32
|
-
</span>
|
|
33
|
-
<div className="feedback-state-body">
|
|
34
|
-
<strong>{title}</strong>
|
|
35
|
-
{body ? <span>{body}</span> : null}
|
|
36
|
-
</div>
|
|
37
|
-
{resolvedActionLabel && onAction ? (
|
|
38
|
-
<button type="button" className="feedback-state-action" onClick={onAction}>
|
|
39
|
-
{isError ? <RotateCcw className="h-3.5 w-3.5" /> : null}
|
|
40
|
-
{resolvedActionLabel}
|
|
41
|
-
</button>
|
|
42
|
-
) : null}
|
|
43
|
-
</div>
|
|
44
|
-
);
|
|
45
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { LANGUAGE_LABELS, t, type Language } from "@/i18n";
|
|
2
|
-
import { useAppStore } from "@/store/appStore";
|
|
3
|
-
|
|
4
|
-
export function LanguageSwitcher({ compact = false }: { compact?: boolean }) {
|
|
5
|
-
const language = useAppStore((state) => state.language);
|
|
6
|
-
const setLanguage = useAppStore((state) => state.setLanguage);
|
|
7
|
-
|
|
8
|
-
return (
|
|
9
|
-
<div className={compact ? "language-switcher compact" : "language-switcher"} aria-label={t(language, "language.label")}>
|
|
10
|
-
{(["ko", "en"] as Language[]).map((item) => (
|
|
11
|
-
<button
|
|
12
|
-
key={item}
|
|
13
|
-
type="button"
|
|
14
|
-
className={language === item ? "is-active" : ""}
|
|
15
|
-
onClick={() => setLanguage(item)}
|
|
16
|
-
aria-pressed={language === item}
|
|
17
|
-
>
|
|
18
|
-
{LANGUAGE_LABELS[item]}
|
|
19
|
-
</button>
|
|
20
|
-
))}
|
|
21
|
-
</div>
|
|
22
|
-
);
|
|
23
|
-
}
|
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { cn } from "@/lib/utils";
|
|
3
|
-
|
|
4
|
-
export type BrainState = "idle" | "listening" | "thinking" | "recalling" | "synthesizing" | "planning" | "acting" | "resting";
|
|
5
|
-
|
|
6
|
-
export interface LivingBrainProps {
|
|
7
|
-
state?: BrainState;
|
|
8
|
-
intensity?: number; // 0-1 how "alive" it feels right now
|
|
9
|
-
onPulse?: () => void; // allow parent to trigger a memory pulse
|
|
10
|
-
size?: "normal" | "large" | "trace";
|
|
11
|
-
label?: string;
|
|
12
|
-
className?: string;
|
|
13
|
-
showLabel?: boolean;
|
|
14
|
-
depth?: number; // 0-5 progressive exploration depth; higher = more "open" / revealing
|
|
15
|
-
onInteract?: () => void; // called on click to advance exploration (travel deeper)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* The Living Brain — the primary visual and emotional object in the product.
|
|
20
|
-
* It is not decoration. It is the other participant.
|
|
21
|
-
* It breathes. It reacts. It remembers.
|
|
22
|
-
*/
|
|
23
|
-
export function LivingBrain({
|
|
24
|
-
state = "idle",
|
|
25
|
-
intensity = 0.6,
|
|
26
|
-
onPulse,
|
|
27
|
-
size = "large",
|
|
28
|
-
label,
|
|
29
|
-
className,
|
|
30
|
-
showLabel = true,
|
|
31
|
-
depth = 0,
|
|
32
|
-
onInteract,
|
|
33
|
-
}: LivingBrainProps) {
|
|
34
|
-
const [isPulsing, setIsPulsing] = React.useState(false);
|
|
35
|
-
const organismRef = React.useRef<HTMLButtonElement>(null);
|
|
36
|
-
|
|
37
|
-
// External trigger for memory / important recall moments
|
|
38
|
-
React.useEffect(() => {
|
|
39
|
-
if (onPulse) {
|
|
40
|
-
const handler = () => firePulse();
|
|
41
|
-
// allow global window event too for simplicity across components
|
|
42
|
-
window.addEventListener("brain:recall", handler as EventListener);
|
|
43
|
-
return () => window.removeEventListener("brain:recall", handler as EventListener);
|
|
44
|
-
}
|
|
45
|
-
}, [onPulse]);
|
|
46
|
-
|
|
47
|
-
function firePulse() {
|
|
48
|
-
setIsPulsing(true);
|
|
49
|
-
if (organismRef.current) {
|
|
50
|
-
organismRef.current.classList.add("pulse");
|
|
51
|
-
// clean after animation
|
|
52
|
-
window.setTimeout(() => {
|
|
53
|
-
if (organismRef.current) organismRef.current.classList.remove("pulse");
|
|
54
|
-
setIsPulsing(false);
|
|
55
|
-
}, 1350);
|
|
56
|
-
}
|
|
57
|
-
onPulse?.();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Auto gentle pulse when recalling or high intensity
|
|
61
|
-
React.useEffect(() => {
|
|
62
|
-
if ((state === "recalling" || intensity > 0.82) && !isPulsing) {
|
|
63
|
-
const t = window.setTimeout(() => firePulse(), 180);
|
|
64
|
-
return () => clearTimeout(t);
|
|
65
|
-
}
|
|
66
|
-
}, [state, intensity]);
|
|
67
|
-
|
|
68
|
-
const dataState = state;
|
|
69
|
-
const isLarge = size === "large";
|
|
70
|
-
const isTrace = size === "trace";
|
|
71
|
-
|
|
72
|
-
const dynamicIntensity = Math.max(0.35, Math.min(1, intensity));
|
|
73
|
-
const effectiveDepth = Math.max(0, Math.min(5, depth || 0));
|
|
74
|
-
const canTravel = state !== "thinking";
|
|
75
|
-
|
|
76
|
-
const handleClick = () => {
|
|
77
|
-
if (!canTravel) return;
|
|
78
|
-
firePulse();
|
|
79
|
-
onInteract?.();
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
return (
|
|
83
|
-
<div
|
|
84
|
-
className={cn(
|
|
85
|
-
"brain-presence select-none",
|
|
86
|
-
isLarge && "large",
|
|
87
|
-
isTrace && "trace",
|
|
88
|
-
className,
|
|
89
|
-
effectiveDepth > 0 && "is-exploring"
|
|
90
|
-
)}
|
|
91
|
-
aria-label="Your living Brain"
|
|
92
|
-
role="group"
|
|
93
|
-
data-depth={effectiveDepth}
|
|
94
|
-
>
|
|
95
|
-
<button
|
|
96
|
-
type="button"
|
|
97
|
-
ref={organismRef}
|
|
98
|
-
className={cn("brain-organism", `size-${size}`, `depth-${effectiveDepth}`)}
|
|
99
|
-
data-state={dataState}
|
|
100
|
-
aria-label={effectiveDepth < 5 ? "Travel deeper into your Brain" : "Rest inside the Knowledge Graph"}
|
|
101
|
-
aria-disabled={!canTravel}
|
|
102
|
-
style={{
|
|
103
|
-
transform: `scale(${0.96 + (dynamicIntensity - 0.5) * 0.09 + effectiveDepth * 0.015})`,
|
|
104
|
-
}}
|
|
105
|
-
onClick={handleClick}
|
|
106
|
-
title={effectiveDepth < 5 ? "Travel deeper into your Brain" : "The core of your knowledge"}
|
|
107
|
-
>
|
|
108
|
-
{/* Living anatomical presence. The glow opens with depth; the folds make it unmistakably a Brain. */}
|
|
109
|
-
<div className="brain-core" style={{ transform: `scale(${1 + effectiveDepth * 0.045})` }}>
|
|
110
|
-
<svg className="brain-anatomy" viewBox="0 0 220 174" aria-hidden>
|
|
111
|
-
<path
|
|
112
|
-
className="brain-lobe brain-lobe-left"
|
|
113
|
-
d="M102 30c-13-20-44-19-55 1-18 1-29 16-28 33-13 8-18 25-11 39-9 16-1 36 17 42 5 19 27 26 43 15 13 10 33 8 43-5 5-7 8-16 8-27V52c0-8-6-17-17-22Z"
|
|
114
|
-
/>
|
|
115
|
-
<path
|
|
116
|
-
className="brain-lobe brain-lobe-right"
|
|
117
|
-
d="M118 30c13-20 44-19 55 1 18 1 29 16 28 33 13 8 18 25 11 39 9 16 1 36-17 42-5 19-27 26-43 15-13 10-33 8-43-5-5-7-8-16-8-27V52c0-8 6-17 17-22Z"
|
|
118
|
-
/>
|
|
119
|
-
<path className="brain-bridge" d="M103 48c9-8 24-8 33 0 7 6 9 16 5 25-5 11-16 15-31 12-15 3-26-1-31-12-4-9-2-19 5-25 5-4 12-6 19 0Z" />
|
|
120
|
-
<path className="brain-stem" d="M92 137c10 9 26 9 36 0 1 14 7 25 20 33H76c12-8 17-19 16-33Z" />
|
|
121
|
-
<path className="brain-fold fold-a" d="M48 50c18-11 38-8 47 8" />
|
|
122
|
-
<path className="brain-fold fold-b" d="M34 82c22-8 45-5 58 8" />
|
|
123
|
-
<path className="brain-fold fold-c" d="M43 119c18 5 35 2 49-11" />
|
|
124
|
-
<path className="brain-fold fold-d" d="M172 50c-18-11-38-8-47 8" />
|
|
125
|
-
<path className="brain-fold fold-e" d="M186 82c-22-8-45-5-58 8" />
|
|
126
|
-
<path className="brain-fold fold-f" d="M177 119c-18 5-35 2-49-11" />
|
|
127
|
-
<path className="brain-fold fold-mid" d="M110 38c-5 30-5 70 0 112" />
|
|
128
|
-
</svg>
|
|
129
|
-
</div>
|
|
130
|
-
|
|
131
|
-
{/* Breathing field expands as we go deeper. */}
|
|
132
|
-
<div
|
|
133
|
-
className="brain-aura"
|
|
134
|
-
style={{
|
|
135
|
-
animationDuration: state === "thinking" ? "1.65s" : state === "recalling" ? "2.4s" : "6.8s",
|
|
136
|
-
transform: `scale(${1 + effectiveDepth * 0.12})`,
|
|
137
|
-
opacity: 0.65 + effectiveDepth * 0.05,
|
|
138
|
-
boxShadow: effectiveDepth > 2 ? "0 0 60px hsl(var(--brain-core) / 0.25)" : "none"
|
|
139
|
-
}}
|
|
140
|
-
/>
|
|
141
|
-
|
|
142
|
-
{/* Thought activity — increases and starts to "resolve" into structure at higher depths */}
|
|
143
|
-
<div className="thought-activity" aria-hidden>
|
|
144
|
-
{Array.from({ length: Math.min(12, 5 + effectiveDepth * 2) }).map((_, i) => (
|
|
145
|
-
<div
|
|
146
|
-
key={i}
|
|
147
|
-
className={cn("thought-particle", effectiveDepth >= 3 && "resolving")}
|
|
148
|
-
style={{
|
|
149
|
-
left: `${18 + ((i * 13 + effectiveDepth * 4) % 64)}%`,
|
|
150
|
-
top: `${22 + (i % 5) * 14}%`,
|
|
151
|
-
animationDelay: `-${i * 0.55 + (intensity * 1.1) - effectiveDepth * 0.2}s`,
|
|
152
|
-
animationDuration: `${2.8 + (1 - dynamicIntensity) * 1.6 - effectiveDepth * 0.15}s`,
|
|
153
|
-
}}
|
|
154
|
-
/>
|
|
155
|
-
))}
|
|
156
|
-
</div>
|
|
157
|
-
|
|
158
|
-
{/* Memory ripples — more and stronger as depth increases (echoes surfacing) */}
|
|
159
|
-
{Array.from({ length: 1 + Math.floor(effectiveDepth / 1.5) }).map((_, i) => (
|
|
160
|
-
<div
|
|
161
|
-
key={i}
|
|
162
|
-
className="memory-ripple"
|
|
163
|
-
aria-hidden
|
|
164
|
-
style={{
|
|
165
|
-
inset: `${18 + i * 6}%`,
|
|
166
|
-
animationDelay: `${180 + i * 220}ms`,
|
|
167
|
-
opacity: 0.55 + effectiveDepth * 0.06
|
|
168
|
-
}}
|
|
169
|
-
/>
|
|
170
|
-
))}
|
|
171
|
-
|
|
172
|
-
{/* Relationship structure appears only near the deepest layers. */}
|
|
173
|
-
{effectiveDepth >= 4 && (
|
|
174
|
-
<svg className="brain-inner-structure" viewBox="0 0 100 100" aria-hidden>
|
|
175
|
-
<g stroke="hsl(var(--brain-core) / 0.35)" strokeWidth="0.6" fill="none">
|
|
176
|
-
<circle cx="50" cy="50" r="18" />
|
|
177
|
-
<circle cx="50" cy="50" r="28" />
|
|
178
|
-
<path d="M32 50 Q50 32 68 50" />
|
|
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" />
|
|
187
|
-
</g>
|
|
188
|
-
</svg>
|
|
189
|
-
)}
|
|
190
|
-
</button>
|
|
191
|
-
|
|
192
|
-
{showLabel && !isTrace && (
|
|
193
|
-
<div className="brain-presence-label" data-state={state}>
|
|
194
|
-
<span className="dot" />
|
|
195
|
-
{label || (effectiveDepth > 0 ? `Depth ${effectiveDepth}` : humanState(state))}
|
|
196
|
-
</div>
|
|
197
|
-
)}
|
|
198
|
-
</div>
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function humanState(s: BrainState) {
|
|
203
|
-
switch (s) {
|
|
204
|
-
case "listening": return "Listening";
|
|
205
|
-
case "thinking": return "Thinking with you";
|
|
206
|
-
case "recalling": return "Remembering";
|
|
207
|
-
case "synthesizing": return "Making sense";
|
|
208
|
-
case "planning": return "Planning";
|
|
209
|
-
case "acting": return "Acting";
|
|
210
|
-
case "resting": return "With you";
|
|
211
|
-
default: return "Here";
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Helper to broadcast recall pulses from anywhere (conversation, memory surface, etc)
|
|
216
|
-
export function triggerBrainRecall() {
|
|
217
|
-
if (typeof window !== "undefined") {
|
|
218
|
-
window.dispatchEvent(new CustomEvent("brain:recall"));
|
|
219
|
-
}
|
|
220
|
-
}
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { latticeApi } from "@/api/client";
|
|
3
|
-
import { type BrainState, LivingBrain } from "@/components/LivingBrain";
|
|
4
|
-
import { t } from "@/i18n";
|
|
5
|
-
import { useAppStore } from "@/store/appStore";
|
|
6
|
-
import { ArrowRight, Cpu, Shield, Zap } from "lucide-react";
|
|
7
|
-
import {
|
|
8
|
-
AnalysisScreen,
|
|
9
|
-
InstallScreen,
|
|
10
|
-
LanguageChooser,
|
|
11
|
-
LoginScreen,
|
|
12
|
-
RecommendationScreen,
|
|
13
|
-
buildRecommendations,
|
|
14
|
-
fallbackModel,
|
|
15
|
-
type FlowAnalysis,
|
|
16
|
-
type FlowStep,
|
|
17
|
-
type RecommendedModel,
|
|
18
|
-
} from "@/components/onboarding/ProductFlowScreens";
|
|
19
|
-
|
|
20
|
-
const FLOW_COMPLETE_KEY = "lattice.productFlow.complete";
|
|
21
|
-
|
|
22
|
-
export function readProductFlowComplete() {
|
|
23
|
-
try {
|
|
24
|
-
return localStorage.getItem(FLOW_COMPLETE_KEY) === "true";
|
|
25
|
-
} catch {}
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function ProductFlow({ onComplete }: { onComplete: () => void }) {
|
|
30
|
-
const language = useAppStore((state) => state.language);
|
|
31
|
-
const [step, setStep] = React.useState<FlowStep>("wake");
|
|
32
|
-
const [analysis, setAnalysis] = React.useState<FlowAnalysis | null>(null);
|
|
33
|
-
const [analysisError, setAnalysisError] = React.useState<string | null>(null);
|
|
34
|
-
const [selected, setSelected] = React.useState<RecommendedModel | null>(null);
|
|
35
|
-
|
|
36
|
-
const recommendations = React.useMemo(() => buildRecommendations(analysis), [analysis]);
|
|
37
|
-
|
|
38
|
-
React.useEffect(() => {
|
|
39
|
-
if (step !== "analysis" || analysis) return;
|
|
40
|
-
let cancelled = false;
|
|
41
|
-
async function runAnalysis() {
|
|
42
|
-
setAnalysisError(null);
|
|
43
|
-
const [setup, recommendationsResult, models, sysinfo] = await Promise.all([
|
|
44
|
-
latticeApi.setupScan(),
|
|
45
|
-
latticeApi.modelRecommendations("local_mlx"),
|
|
46
|
-
latticeApi.models(),
|
|
47
|
-
latticeApi.sysinfo(),
|
|
48
|
-
]);
|
|
49
|
-
if (cancelled) return;
|
|
50
|
-
setAnalysis({
|
|
51
|
-
setup: setup.ok ? setup.data as Record<string, unknown> : null,
|
|
52
|
-
recommendations: recommendationsResult.ok ? recommendationsResult.data as Record<string, unknown> : null,
|
|
53
|
-
models: models.ok ? models.data as Record<string, unknown> : null,
|
|
54
|
-
sysinfo: sysinfo.ok ? sysinfo.data as Record<string, unknown> : null,
|
|
55
|
-
});
|
|
56
|
-
if (!setup.ok && !recommendationsResult.ok && !models.ok) {
|
|
57
|
-
setAnalysisError(t(language, "flow.analysis.error"));
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
void runAnalysis();
|
|
61
|
-
return () => { cancelled = true; };
|
|
62
|
-
}, [analysis, language, step]);
|
|
63
|
-
|
|
64
|
-
return (
|
|
65
|
-
<div className="ritual-shell" aria-label={t(language, "flow.shell")}>
|
|
66
|
-
<div className="ritual-container">
|
|
67
|
-
<LanguageChooser />
|
|
68
|
-
<div className="ritual-brain">
|
|
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>
|
|
73
|
-
</div>
|
|
74
|
-
|
|
75
|
-
{step === "wake" && <WakeBrainScreen onWake={() => setStep("login")} onUseExisting={() => completeFlow(onComplete)} />}
|
|
76
|
-
|
|
77
|
-
{step === "login" && <LoginScreen onSuccess={() => setStep("analysis")} />}
|
|
78
|
-
|
|
79
|
-
{step === "analysis" && (
|
|
80
|
-
<AnalysisScreen analysis={analysis} error={analysisError} onContinue={() => setStep("recommend")} />
|
|
81
|
-
)}
|
|
82
|
-
|
|
83
|
-
{step === "recommend" && (
|
|
84
|
-
<RecommendationScreen
|
|
85
|
-
recommendations={recommendations}
|
|
86
|
-
onBack={() => setStep("analysis")}
|
|
87
|
-
onSkipModel={() => completeFlow(onComplete)}
|
|
88
|
-
onSelect={(model) => {
|
|
89
|
-
setSelected(model);
|
|
90
|
-
setStep("install");
|
|
91
|
-
}}
|
|
92
|
-
/>
|
|
93
|
-
)}
|
|
94
|
-
|
|
95
|
-
{step === "install" && (
|
|
96
|
-
<InstallScreen
|
|
97
|
-
model={selected || recommendations[0] || fallbackModel()}
|
|
98
|
-
onBack={() => setStep("recommend")}
|
|
99
|
-
onComplete={() => completeFlow(onComplete)}
|
|
100
|
-
onLater={() => completeFlow(onComplete)}
|
|
101
|
-
/>
|
|
102
|
-
)}
|
|
103
|
-
</div>
|
|
104
|
-
</div>
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function WakeBrainScreen({ onWake, onUseExisting }: { onWake: () => void; onUseExisting: () => void }) {
|
|
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
|
-
];
|
|
115
|
-
return (
|
|
116
|
-
<section className="ritual-wake" aria-label={t(language, "flow.wake.aria")}>
|
|
117
|
-
<div className="ritual-title">{t(language, "flow.wake.title")}</div>
|
|
118
|
-
<div className="ritual-subtitle">{t(language, "flow.wake.body")}</div>
|
|
119
|
-
|
|
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
|
-
})}
|
|
130
|
-
</div>
|
|
131
|
-
|
|
132
|
-
<div className="ritual-wake-plan" aria-label={t(language, "flow.wake.plan.aria")}>
|
|
133
|
-
<div>
|
|
134
|
-
<span>1</span>
|
|
135
|
-
<strong>{t(language, "flow.wake.step.identity")}</strong>
|
|
136
|
-
</div>
|
|
137
|
-
<div>
|
|
138
|
-
<span>2</span>
|
|
139
|
-
<strong>{t(language, "flow.wake.step.check")}</strong>
|
|
140
|
-
</div>
|
|
141
|
-
<div>
|
|
142
|
-
<span>3</span>
|
|
143
|
-
<strong>{t(language, "flow.wake.step.voice")}</strong>
|
|
144
|
-
</div>
|
|
145
|
-
</div>
|
|
146
|
-
|
|
147
|
-
<div className="ritual-button-row ritual-button-row-primary">
|
|
148
|
-
<button type="button" className="ritual-full-button ritual-full-button-primary" onClick={onWake}>
|
|
149
|
-
{t(language, "flow.wake.primary")} <ArrowRight size={18} />
|
|
150
|
-
</button>
|
|
151
|
-
<button type="button" className="ritual-secondary-button" onClick={onUseExisting}>
|
|
152
|
-
{t(language, "flow.wake.existing")}
|
|
153
|
-
</button>
|
|
154
|
-
</div>
|
|
155
|
-
<div className="ritual-start-note">{t(language, "flow.wake.startNote")}</div>
|
|
156
|
-
</section>
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function completeFlow(onComplete: () => void) {
|
|
161
|
-
try { localStorage.setItem(FLOW_COMPLETE_KEY, "true"); } catch {}
|
|
162
|
-
onComplete();
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function brainStateForStep(step: FlowStep): BrainState {
|
|
166
|
-
if (step === "wake") return "idle";
|
|
167
|
-
if (step === "analysis") return "listening";
|
|
168
|
-
if (step === "recommend") return "recalling";
|
|
169
|
-
if (step === "install") return "thinking";
|
|
170
|
-
return "idle";
|
|
171
|
-
}
|