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.
Files changed (183) hide show
  1. package/README.md +33 -72
  2. package/docs/CHANGELOG.md +52 -874
  3. package/docs/DEVELOPMENT.md +26 -3
  4. package/docs/LEGACY_COMPATIBILITY.md +3 -1
  5. package/docs/TRUST_MODEL.md +26 -26
  6. package/docs/WHY_LATTICE.md +28 -25
  7. package/docs/kg-schema.md +6 -4
  8. package/lattice_brain/__init__.py +1 -1
  9. package/lattice_brain/embeddings.py +9 -1
  10. package/lattice_brain/graph/schema.py +6 -0
  11. package/lattice_brain/runtime/multi_agent.py +1 -1
  12. package/latticeai/__init__.py +1 -1
  13. package/latticeai/api/computer_use.py +2 -0
  14. package/latticeai/api/memory.py +13 -0
  15. package/latticeai/app_factory.py +43 -162
  16. package/latticeai/brain/__init__.py +1 -1
  17. package/latticeai/core/config.py +10 -0
  18. package/latticeai/core/local_embeddings.py +8 -0
  19. package/latticeai/core/marketplace.py +1 -1
  20. package/latticeai/core/mcp_registry.py +140 -1
  21. package/latticeai/core/tool_registry.py +10 -0
  22. package/latticeai/core/workspace_graph_trace.py +132 -0
  23. package/latticeai/core/workspace_memory.py +75 -0
  24. package/latticeai/core/workspace_os.py +135 -1283
  25. package/latticeai/core/workspace_os_utils.py +132 -0
  26. package/latticeai/core/workspace_permissions.py +99 -0
  27. package/latticeai/core/workspace_plugins.py +97 -0
  28. package/latticeai/core/workspace_runs.py +612 -0
  29. package/latticeai/core/workspace_skills.py +114 -0
  30. package/latticeai/core/workspace_snapshots.py +195 -0
  31. package/latticeai/core/workspace_timeline.py +107 -0
  32. package/latticeai/runtime/audit_runtime.py +64 -0
  33. package/latticeai/runtime/config_runtime.py +4 -0
  34. package/latticeai/runtime/sso_runtime.py +52 -0
  35. package/latticeai/services/architecture_readiness.py +6 -5
  36. package/latticeai/services/memory_service.py +215 -7
  37. package/latticeai/services/model_capability_registry.py +68 -0
  38. package/latticeai/services/model_engines.py +603 -0
  39. package/latticeai/services/model_loading.py +482 -0
  40. package/latticeai/services/model_recommendation.py +5 -0
  41. package/latticeai/services/model_runtime.py +146 -837
  42. package/latticeai/services/product_readiness.py +19 -18
  43. package/package.json +2 -2
  44. package/scripts/build_frontend_assets.mjs +11 -2
  45. package/src-tauri/Cargo.lock +1 -1
  46. package/src-tauri/Cargo.toml +1 -1
  47. package/src-tauri/tauri.conf.json +1 -1
  48. package/static/app/asset-manifest.json +11 -102
  49. package/static/app/assets/Act-D9jIknFd.js +1 -0
  50. package/static/app/assets/Brain-CFOtWbPN.js +321 -0
  51. package/static/app/assets/{Capture-B9Tlhzqr.js → Capture-Q4WYzwr5.js} +1 -2
  52. package/static/app/assets/Library-C5Q2yWee.js +1 -0
  53. package/static/app/assets/System-BLbjdr1_.js +1 -0
  54. package/static/app/assets/core-CwxXejkd.js +1 -2
  55. package/static/app/assets/index-BqammyNu.js +16 -0
  56. package/static/app/assets/index-ty1iGgZu.css +2 -0
  57. package/static/app/assets/primitives-Br8uSfZ4.js +1 -0
  58. package/static/app/assets/{textarea-BZk6ybp5.js → textarea-BnhNs1_X.js} +1 -2
  59. package/static/app/index.html +3 -10
  60. package/static/app/theme-boot.js +8 -0
  61. package/static/sw.js +1 -1
  62. package/tools/__init__.py +2 -1
  63. package/tools/computer.py +21 -0
  64. package/docs/CARRYOVER_AUDIT_v3.6.0.md +0 -61
  65. package/docs/HANDOVER_v3.6.0.md +0 -46
  66. package/docs/RUNTIME_HOOK_COVERAGE_v3.5.0.md +0 -56
  67. package/docs/RUNTIME_HOOK_COVERAGE_v3.6.0.md +0 -49
  68. package/docs/V2_ARCHITECTURE.md +0 -561
  69. package/docs/V3_2_AUDIT.md +0 -82
  70. package/docs/V3_BACKEND_ARCHITECTURE.md +0 -138
  71. package/docs/V3_FRONTEND.md +0 -140
  72. package/docs/V4_1_FRONTEND_ARCHITECTURE_REVIEW.md +0 -65
  73. package/docs/V4_1_FRONTEND_MIGRATION_REPORT.md +0 -70
  74. package/docs/V4_1_VALIDATION_REPORT.md +0 -47
  75. package/docs/V4_2_BRAIN_CORE_ARCHITECTURE.md +0 -97
  76. package/docs/V4_2_STORAGE_MIGRATION_REPORT.md +0 -91
  77. package/docs/V4_2_VALIDATION_REPORT.md +0 -89
  78. package/docs/V4_3_2_DEADCODE_AUDIT_REPORT.md +0 -174
  79. package/docs/V4_3_2_DOCUMENTATION_CLEANUP_REPORT.md +0 -81
  80. package/docs/V4_3_2_GITHUB_VERCEL_CHECK_REPORT.md +0 -75
  81. package/docs/V4_3_2_GRAPH_UX_REPORT.md +0 -48
  82. package/docs/V4_3_2_INDEPENDENT_AUDIT_PACKAGE.md +0 -209
  83. package/docs/V4_3_2_PRODUCT_POLISH_REPORT.md +0 -57
  84. package/docs/V4_3_2_SELF_AUDIT_REPORT.md +0 -63
  85. package/docs/V4_3_2_VALIDATION_REPORT.md +0 -97
  86. package/docs/V4_3_3_VALIDATION_REPORT.md +0 -46
  87. package/docs/V4_3_PORTABILITY_ARCHITECTURE.md +0 -69
  88. package/docs/V4_3_PRIVACY_AUDIT.md +0 -60
  89. package/docs/V4_3_PRODUCT_HARDENING_REPORT.md +0 -53
  90. package/docs/V4_3_VALIDATION_REPORT.md +0 -58
  91. package/docs/V4_4_0_EXTRACTION_REPORT.md +0 -239
  92. package/docs/V4_5_0_GEMMA_RUNTIME_COMPATIBILITY_REPORT.md +0 -49
  93. package/docs/V4_5_0_GRAPH_UX_REPORT.md +0 -34
  94. package/docs/V4_5_0_MODEL_RUNTIME_UX_REPORT.md +0 -40
  95. package/docs/V4_5_0_ONBOARDING_REPORT.md +0 -31
  96. package/docs/V4_5_0_PRODUCT_EXPERIENCE_RECOVERY_REPORT.md +0 -49
  97. package/docs/V4_5_0_VALIDATION_REPORT.md +0 -60
  98. package/docs/V4_5_1_GRAPH_EXPERIENCE_REPORT.md +0 -33
  99. package/docs/V4_5_1_MODEL_EXPERIENCE_REPORT.md +0 -37
  100. package/docs/V4_5_1_NAVIGATION_REPORT.md +0 -37
  101. package/docs/V4_5_1_ONBOARDING_REPORT.md +0 -29
  102. package/docs/V4_5_1_PRODUCT_REIMAGINING_REPORT.md +0 -61
  103. package/docs/V4_5_1_RC_ARTIFACTS.md +0 -44
  104. package/docs/V4_5_1_UX_REPORT.md +0 -45
  105. package/docs/V4_5_1_VALIDATION_REPORT.md +0 -55
  106. package/docs/V4_5_1_VISUAL_DESIGN_REPORT.md +0 -30
  107. package/docs/V4_6_0_LIVING_BRAIN_EXPERIENCE_REPORT.md +0 -72
  108. package/docs/V4_6_1_RELEASE_REFRESH_REPORT.md +0 -42
  109. package/docs/V4_7_0_ADMIN_SEPARATION_REPORT.md +0 -42
  110. package/docs/V4_7_1_ADMIN_OPERATIONS_REPORT.md +0 -49
  111. package/docs/V4_7_2_INTUITIVE_BRAIN_UX_REPORT.md +0 -62
  112. package/docs/V4_BRAIN_ARCHITECTURE.md +0 -322
  113. package/docs/V4_DIGITAL_BRAIN_RECOVERY.md +0 -555
  114. package/docs/V4_IMPLEMENTATION_PLAN.md +0 -470
  115. package/frontend/index.html +0 -24
  116. package/frontend/openapi.json +0 -15425
  117. package/frontend/src/App.tsx +0 -243
  118. package/frontend/src/api/client.ts +0 -580
  119. package/frontend/src/api/openapi.ts +0 -17892
  120. package/frontend/src/components/AdminAccessGate.tsx +0 -70
  121. package/frontend/src/components/FeedbackState.tsx +0 -45
  122. package/frontend/src/components/LanguageSwitcher.tsx +0 -23
  123. package/frontend/src/components/LivingBrain.tsx +0 -220
  124. package/frontend/src/components/ProductFlow.tsx +0 -171
  125. package/frontend/src/components/WorkspaceProfileSwitcher.tsx +0 -176
  126. package/frontend/src/components/onboarding/AnalysisScreen.tsx +0 -135
  127. package/frontend/src/components/onboarding/DownloadConsentPanel.tsx +0 -29
  128. package/frontend/src/components/onboarding/InstallScreen.tsx +0 -263
  129. package/frontend/src/components/onboarding/LanguageChooser.tsx +0 -23
  130. package/frontend/src/components/onboarding/LoginScreen.tsx +0 -131
  131. package/frontend/src/components/onboarding/ProductFlowScreens.tsx +0 -13
  132. package/frontend/src/components/onboarding/RecommendationScreen.tsx +0 -116
  133. package/frontend/src/components/onboarding/recommendationModel.ts +0 -189
  134. package/frontend/src/components/primitives.tsx +0 -392
  135. package/frontend/src/components/ui/badge.tsx +0 -27
  136. package/frontend/src/components/ui/button.tsx +0 -37
  137. package/frontend/src/components/ui/card.tsx +0 -22
  138. package/frontend/src/components/ui/input.tsx +0 -16
  139. package/frontend/src/components/ui/textarea.tsx +0 -16
  140. package/frontend/src/features/admin/AdminConsole.tsx +0 -334
  141. package/frontend/src/features/brain/BrainCarePanel.tsx +0 -254
  142. package/frontend/src/features/brain/BrainComposer.tsx +0 -85
  143. package/frontend/src/features/brain/BrainConversation.tsx +0 -688
  144. package/frontend/src/features/brain/BrainGraphLayer.tsx +0 -365
  145. package/frontend/src/features/brain/BrainHome.tsx +0 -624
  146. package/frontend/src/features/brain/BrainMemoryLayer.tsx +0 -45
  147. package/frontend/src/features/brain/BrainOverviewPanel.tsx +0 -149
  148. package/frontend/src/features/brain/BrainRelationshipLayer.tsx +0 -43
  149. package/frontend/src/features/brain/DepthEmergence.tsx +0 -53
  150. package/frontend/src/features/brain/brainData.ts +0 -246
  151. package/frontend/src/features/brain/graphLayout.ts +0 -37
  152. package/frontend/src/features/brain/types.ts +0 -150
  153. package/frontend/src/features/review/ReviewCard.tsx +0 -100
  154. package/frontend/src/features/review/ReviewInbox.tsx +0 -127
  155. package/frontend/src/features/review/reviewHelpers.ts +0 -69
  156. package/frontend/src/i18n.ts +0 -1303
  157. package/frontend/src/lib/utils.ts +0 -33
  158. package/frontend/src/main.tsx +0 -23
  159. package/frontend/src/pages/Act.tsx +0 -458
  160. package/frontend/src/pages/Ask.tsx +0 -14
  161. package/frontend/src/pages/Brain.tsx +0 -914
  162. package/frontend/src/pages/Capture.tsx +0 -258
  163. package/frontend/src/pages/Library.tsx +0 -486
  164. package/frontend/src/pages/System.tsx +0 -621
  165. package/frontend/src/routes.ts +0 -92
  166. package/frontend/src/store/appStore.ts +0 -94
  167. package/frontend/src/styles.css +0 -6579
  168. package/static/app/assets/Act-DOvf59ru.js +0 -2
  169. package/static/app/assets/Act-DOvf59ru.js.map +0 -1
  170. package/static/app/assets/Brain-C7_0mEiI.js +0 -322
  171. package/static/app/assets/Brain-C7_0mEiI.js.map +0 -1
  172. package/static/app/assets/Capture-B9Tlhzqr.js.map +0 -1
  173. package/static/app/assets/Library-BJPEEm5O.js +0 -2
  174. package/static/app/assets/Library-BJPEEm5O.js.map +0 -1
  175. package/static/app/assets/System-D6t9jo9V.js +0 -2
  176. package/static/app/assets/System-D6t9jo9V.js.map +0 -1
  177. package/static/app/assets/core-CwxXejkd.js.map +0 -1
  178. package/static/app/assets/index-C7g26IF6.css +0 -2
  179. package/static/app/assets/index-DbcEYJQ2.js +0 -17
  180. package/static/app/assets/index-DbcEYJQ2.js.map +0 -1
  181. package/static/app/assets/primitives-CD38lt4n.js +0 -2
  182. package/static/app/assets/primitives-CD38lt4n.js.map +0 -1
  183. package/static/app/assets/textarea-BZk6ybp5.js.map +0 -1
@@ -1,258 +0,0 @@
1
- import * as React from "react";
2
- import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
3
- import { AlertCircle, CheckCircle2, FolderPlus, Globe2, HardDrive, Loader2, RotateCcw, Upload } from "lucide-react";
4
- import { latticeApi } from "@/api/client";
5
- import { ActionButton, DataPanel, EntityList, OperationResult, StructuredView, Tabs } from "@/components/primitives";
6
- import { Button } from "@/components/ui/button";
7
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
8
- import { Input } from "@/components/ui/input";
9
- import { t, type Language } from "@/i18n";
10
- import { asArray } from "@/lib/utils";
11
- import { useAppStore } from "@/store/appStore";
12
-
13
- type CaptureTab = "files" | "local" | "browser" | "pipeline";
14
-
15
- export function CapturePage({ initialTab }: { initialTab?: string }) {
16
- const language = useAppStore((state) => state.language);
17
- const [tab, setTab] = React.useState<CaptureTab>((initialTab as CaptureTab) || "files");
18
- const tabs: Array<{ id: CaptureTab; label: string }> = [
19
- { id: "files", label: t(language, "capture.tab.files") },
20
- { id: "local", label: t(language, "capture.tab.local") },
21
- { id: "browser", label: t(language, "capture.tab.browser") },
22
- { id: "pipeline", label: t(language, "capture.tab.pipeline") },
23
- ];
24
- React.useEffect(() => {
25
- if (initialTab === "pipeline" || initialTab === "local" || initialTab === "files") setTab(initialTab);
26
- }, [initialTab]);
27
- return (
28
- <div className="space-y-5">
29
- <header className="page-hero">
30
- <div className="page-kicker"><Upload className="h-4 w-4" /> {t(language, "capture.kicker")}</div>
31
- <h1 className="page-title">{t(language, "capture.title")}</h1>
32
- <p className="page-copy">{t(language, "capture.body")}</p>
33
- </header>
34
- <Tabs tabs={tabs} value={tab} onChange={(id) => setTab(id as CaptureTab)} />
35
- {tab === "files" ? <FilesPanel /> : null}
36
- {tab === "local" ? <LocalPanel /> : null}
37
- {tab === "browser" ? <BrowserPanel /> : null}
38
- {tab === "pipeline" ? <PipelinePanel /> : null}
39
- </div>
40
- );
41
- }
42
-
43
- function FilesPanel() {
44
- const language = useAppStore((state) => state.language);
45
- const qc = useQueryClient();
46
- const docs = useQuery({ queryKey: ["documents"], queryFn: () => latticeApi.documents(200) });
47
- const [queue, setQueue] = React.useState<UploadQueueItem[]>([]);
48
- const upload = useMutation({
49
- mutationFn: (files: File[]) => uploadFiles(files, setQueue),
50
- onSuccess: () => {
51
- void qc.invalidateQueries({ queryKey: ["documents"] });
52
- void qc.invalidateQueries({ queryKey: ["graphStats"] });
53
- void qc.invalidateQueries({ queryKey: ["memoryManager"] });
54
- },
55
- });
56
- const beginUpload = React.useCallback((files: FileList | File[]) => {
57
- const nextFiles = Array.from(files);
58
- if (!nextFiles.length) return;
59
- upload.mutate(nextFiles);
60
- }, [upload]);
61
- return (
62
- <div className="grid gap-4 xl:grid-cols-[0.75fr_1.25fr]">
63
- <Card>
64
- <CardHeader>
65
- <CardTitle className="flex items-center gap-2"><Upload className="h-4 w-4" /> {t(language, "capture.files.title")}</CardTitle>
66
- <CardDescription>{t(language, "capture.files.description")}</CardDescription>
67
- </CardHeader>
68
- <CardContent>
69
- <label
70
- className="flex min-h-56 cursor-pointer flex-col items-center justify-center gap-3 rounded-lg border border-dashed border-border bg-muted/30 p-6 text-center transition hover:bg-muted/50"
71
- onDragOver={(event) => event.preventDefault()}
72
- onDrop={(event) => {
73
- event.preventDefault();
74
- beginUpload(event.dataTransfer.files);
75
- }}
76
- >
77
- <Upload className="h-7 w-7 text-primary" />
78
- <span className="text-lg font-semibold">{t(language, "capture.files.drop")}</span>
79
- <span className="max-w-sm text-sm leading-6 text-muted-foreground">{t(language, "capture.files.dropDetail")}</span>
80
- <input type="file" multiple className="sr-only" onChange={(e) => e.target.files && beginUpload(e.target.files)} />
81
- </label>
82
- <DocumentUploadQueue queue={queue} onRetry={(file) => beginUpload([file])} />
83
- </CardContent>
84
- </Card>
85
- <DataPanel title={t(language, "capture.files.uploaded")} result={docs.data}>
86
- {(data) => (
87
- <div className="space-y-3">
88
- <EntityList items={(data as Record<string, unknown>).documents || data} titleKey="filename" metaKey="ingest_state" limit={12} />
89
- <div className="rounded-md border border-border bg-background/55 p-3 text-sm text-muted-foreground">
90
- {t(language, "capture.files.completed")}
91
- </div>
92
- </div>
93
- )}
94
- </DataPanel>
95
- </div>
96
- );
97
- }
98
-
99
- type UploadQueueItem = {
100
- id: string;
101
- file: File;
102
- name: string;
103
- size: number;
104
- status: "queued" | "uploading" | "done" | "failed";
105
- result?: Awaited<ReturnType<typeof latticeApi.uploadDocument>>;
106
- };
107
-
108
- async function uploadFiles(files: File[], setQueue: React.Dispatch<React.SetStateAction<UploadQueueItem[]>>) {
109
- const rows = files.map((file) => ({
110
- id: `${file.name}-${file.size}-${file.lastModified}-${Date.now()}`,
111
- file,
112
- name: file.name,
113
- size: file.size,
114
- status: "queued" as const,
115
- }));
116
- setQueue((items) => [...rows, ...items].slice(0, 12));
117
- const results = [];
118
- for (const row of rows) {
119
- setQueue((items) => items.map((item) => item.id === row.id ? { ...item, status: "uploading" } : item));
120
- const result = await latticeApi.uploadDocument(row.file);
121
- results.push(result);
122
- setQueue((items) => items.map((item) => item.id === row.id ? { ...item, status: result.ok ? "done" : "failed", result } : item));
123
- }
124
- return results;
125
- }
126
-
127
- function DocumentUploadQueue({ queue, onRetry }: { queue: UploadQueueItem[]; onRetry: (file: File) => void }) {
128
- const language = useAppStore((state) => state.language);
129
- if (!queue.length) return null;
130
- return (
131
- <div className="mt-4 space-y-2">
132
- {queue.map((item) => {
133
- const detail = uploadResultDetail(item, language);
134
- return (
135
- <div key={item.id} className="rounded-md border border-border bg-background/55 p-3 text-sm">
136
- <div className="flex flex-wrap items-start justify-between gap-3">
137
- <div>
138
- <div className="flex items-center gap-2 font-medium">
139
- {item.status === "done" ? <CheckCircle2 className="h-4 w-4 text-emerald-400" /> : item.status === "failed" ? <AlertCircle className="h-4 w-4 text-amber-400" /> : <Loader2 className="h-4 w-4 animate-spin text-primary" />}
140
- {item.name}
141
- </div>
142
- <div className="mt-1 text-xs text-muted-foreground">
143
- {Math.max(1, Math.round(item.size / 1024))} KB · {detail}
144
- </div>
145
- </div>
146
- {item.status === "failed" ? (
147
- <Button size="sm" variant="outline" onClick={() => onRetry(item.file)}>
148
- <RotateCcw className="h-3.5 w-3.5" /> {t(language, "capture.retry")}
149
- </Button>
150
- ) : null}
151
- </div>
152
- {item.result ? <OperationResult result={item.result} successLabel={t(language, "capture.files.success")} /> : null}
153
- </div>
154
- );
155
- })}
156
- </div>
157
- );
158
- }
159
-
160
- function uploadResultDetail(item: UploadQueueItem, language: Language) {
161
- if (item.status === "queued") return t(language, "capture.files.queued");
162
- if (item.status === "uploading") return t(language, "capture.files.uploading");
163
- if (!item.result?.ok) return item.result?.error || t(language, "capture.files.failed");
164
- const data = item.result.data || {};
165
- const node = String(data.node_id || data.graph_node || data.provenance_id || "");
166
- return node ? t(language, "capture.files.capturedWithNode", { node }) : t(language, "capture.files.captured");
167
- }
168
-
169
- function LocalPanel() {
170
- const language = useAppStore((state) => state.language);
171
- const qc = useQueryClient();
172
- const [path, setPath] = React.useState("");
173
- const local = useQuery({ queryKey: ["localSources"], queryFn: latticeApi.localSources });
174
- const agent = useQuery({ queryKey: ["localAgent"], queryFn: latticeApi.localAgent });
175
- const connect = useMutation({
176
- mutationFn: () => latticeApi.connectFolder(path),
177
- onSuccess: () => qc.invalidateQueries({ queryKey: ["localSources"] }),
178
- });
179
- return (
180
- <div className="grid gap-4 xl:grid-cols-[0.9fr_1.1fr]">
181
- <Card>
182
- <CardHeader>
183
- <CardTitle className="flex items-center gap-2"><FolderPlus className="h-4 w-4" /> {t(language, "capture.local.title")}</CardTitle>
184
- <CardDescription>{t(language, "capture.local.description")}</CardDescription>
185
- </CardHeader>
186
- <CardContent className="space-y-3">
187
- <Input value={path} onChange={(e) => setPath(e.target.value)} placeholder={t(language, "capture.local.placeholder")} />
188
- <Button disabled={!path.trim() || connect.isPending} onClick={() => connect.mutate()}>{t(language, "capture.local.connect")}</Button>
189
- {connect.data ? <OperationResult result={connect.data} successLabel={t(language, "capture.local.success")} /> : null}
190
- </CardContent>
191
- </Card>
192
- <DataPanel title={t(language, "capture.local.sources")} result={local.data}>
193
- {(data) => (
194
- <div className="space-y-3">
195
- <EntityList items={(data as Record<string, unknown>).sources} titleKey="path" metaKey="status" />
196
- {asArray<Record<string, unknown>>((data as Record<string, unknown>).sources).map((source) => (
197
- <ActionButton
198
- key={String(source.id || source.source_id || source.path)}
199
- label={`Stop ${String(source.path || source.id || "source")}`}
200
- action={() => latticeApi.localWatchStop(String(source.id || source.source_id))}
201
- invalidate={["localSources"]}
202
- />
203
- ))}
204
- </div>
205
- )}
206
- </DataPanel>
207
- <DataPanel title={t(language, "capture.local.access")} result={agent.data} className="xl:col-span-2">
208
- {(data) => <StructuredView value={data} />}
209
- </DataPanel>
210
- </div>
211
- );
212
- }
213
-
214
- function BrowserPanel() {
215
- const language = useAppStore((state) => state.language);
216
- const [url, setUrl] = React.useState("");
217
- const read = useMutation({ mutationFn: () => latticeApi.browserReadUrl(url) });
218
- return (
219
- <Card>
220
- <CardHeader>
221
- <CardTitle className="flex items-center gap-2"><Globe2 className="h-4 w-4" /> {t(language, "capture.browser.title")}</CardTitle>
222
- <CardDescription>{t(language, "capture.browser.description")}</CardDescription>
223
- </CardHeader>
224
- <CardContent className="space-y-3">
225
- <div className="flex flex-col gap-2 sm:flex-row">
226
- <Input value={url} onChange={(e) => setUrl(e.target.value)} placeholder={t(language, "capture.browser.placeholder")} />
227
- <Button disabled={!url.trim() || read.isPending} onClick={() => read.mutate()}>{t(language, "capture.browser.capture")}</Button>
228
- </div>
229
- {read.data ? <OperationResult result={read.data} successLabel={t(language, "capture.browser.success")} /> : null}
230
- </CardContent>
231
- </Card>
232
- );
233
- }
234
-
235
- function PipelinePanel() {
236
- const language = useAppStore((state) => state.language);
237
- const index = useQuery({ queryKey: ["index"], queryFn: latticeApi.indexStatus });
238
- const stats = useQuery({ queryKey: ["graphStats"], queryFn: latticeApi.graphStats });
239
- return (
240
- <div className="grid gap-4 xl:grid-cols-2">
241
- <DataPanel title={t(language, "capture.pipeline.status")} result={index.data}>
242
- {(data) => <StructuredView value={data} />}
243
- </DataPanel>
244
- <DataPanel title={t(language, "capture.pipeline.growth")} result={stats.data}>
245
- {(data) => <StructuredView value={data} />}
246
- </DataPanel>
247
- <Card className="xl:col-span-2">
248
- <CardHeader>
249
- <CardTitle className="flex items-center gap-2"><HardDrive className="h-4 w-4" /> {t(language, "capture.pipeline.refresh")}</CardTitle>
250
- <CardDescription>{t(language, "capture.pipeline.description")}</CardDescription>
251
- </CardHeader>
252
- <CardContent>
253
- <ActionButton label={t(language, "capture.pipeline.rebuild")} action={() => latticeApi.rebuildIndex()} invalidate={["index"]} />
254
- </CardContent>
255
- </Card>
256
- </div>
257
- );
258
- }