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,392 +0,0 @@
1
- import * as React from "react";
2
- import { useMutation, useQueryClient } from "@tanstack/react-query";
3
- import { AlertCircle, CheckCircle2, Loader2, LockKeyhole, Sparkles } from "lucide-react";
4
- import type { ApiResult } from "@/api/client";
5
- import { Badge } from "@/components/ui/badge";
6
- import { Button } from "@/components/ui/button";
7
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
8
- import { useAppStore } from "@/store/appStore";
9
- import { cn, asArray, fmtNumber, shortId, titleize } from "@/lib/utils";
10
-
11
- export function SourceBadge({ result }: { result?: Pick<ApiResult, "source" | "ok" | "status"> }) {
12
- const mode = useAppStore((state) => state.mode);
13
- if (!result) return <Badge variant="muted">not loaded</Badge>;
14
- if (result.source === "live" && result.ok) return <Badge variant="success">{mode === "basic" ? "ready" : "connected"}</Badge>;
15
- return <Badge variant="warning">{mode === "basic" ? "needs setup" : "unavailable"}</Badge>;
16
- }
17
-
18
- export function EmptyState({ title = "Unavailable", detail }: { title?: string; detail?: React.ReactNode }) {
19
- return (
20
- <div className="flex min-h-36 flex-col items-center justify-center gap-2 rounded-lg border border-dashed border-border bg-muted/24 p-6 text-center text-sm text-muted-foreground">
21
- <div className="grid h-10 w-10 place-items-center rounded-md border border-border bg-card">
22
- <Sparkles className="h-5 w-5 text-primary" />
23
- </div>
24
- <div className="text-base font-semibold text-foreground">{title}</div>
25
- {detail ? <div className="max-w-md leading-6">{detail}</div> : null}
26
- </div>
27
- );
28
- }
29
-
30
- export function DataPanel<T>({
31
- title,
32
- description,
33
- result,
34
- children,
35
- className,
36
- }: {
37
- title: string;
38
- description?: string;
39
- result?: ApiResult<T>;
40
- children: (data: T) => React.ReactNode;
41
- className?: string;
42
- }) {
43
- const mode = useAppStore((state) => state.mode);
44
- return (
45
- <Card className={cn("overflow-hidden", className)}>
46
- <CardHeader className="flex-row items-start justify-between gap-3">
47
- <div>
48
- <CardTitle>{title}</CardTitle>
49
- {description ? <CardDescription>{description}</CardDescription> : null}
50
- </div>
51
- <SourceBadge result={result} />
52
- </CardHeader>
53
- <CardContent>
54
- {result?.ok ? children(result.data) : (
55
- <EmptyState detail={mode === "basic" ? "This area needs setup or is not available yet." : result?.error || "This capability is not reporting right now."} />
56
- )}
57
- </CardContent>
58
- </Card>
59
- );
60
- }
61
-
62
- export function LoadingPanel({ title }: { title: string }) {
63
- return (
64
- <Card>
65
- <CardHeader>
66
- <CardTitle>{title}</CardTitle>
67
- </CardHeader>
68
- <CardContent>
69
- <div className="flex items-center gap-2 text-sm text-muted-foreground">
70
- <Loader2 className="h-4 w-4 animate-spin" /> Loading
71
- </div>
72
- </CardContent>
73
- </Card>
74
- );
75
- }
76
-
77
- export function StatGrid({ stats }: { stats: Array<{ label: string; value: unknown; hint?: string }> }) {
78
- return (
79
- <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
80
- {stats.map((stat) => (
81
- <div key={stat.label} className="rounded-lg border border-border bg-background/55 p-4">
82
- <div className="text-xs uppercase text-muted-foreground">{stat.label}</div>
83
- <div className="mt-2 text-2xl font-semibold leading-tight">{typeof stat.value === "number" ? fmtNumber(stat.value) : String(stat.value ?? "-")}</div>
84
- {stat.hint ? <div className="mt-2 text-xs leading-5 text-muted-foreground">{stat.hint}</div> : null}
85
- </div>
86
- ))}
87
- </div>
88
- );
89
- }
90
-
91
- function isRecord(value: unknown): value is Record<string, unknown> {
92
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
93
- }
94
-
95
- function scalarText(value: unknown) {
96
- if (value === null || value === undefined || value === "") return "-";
97
- if (typeof value === "number") return Number.isFinite(value) ? fmtNumber(value) : "-";
98
- if (typeof value === "boolean") return value ? "Enabled" : "Disabled";
99
- return String(value);
100
- }
101
-
102
- const BASIC_HIDDEN_KEY = /(^id$|_id$|token|secret|passphrase|fingerprint|public_key|private_key|dsn|schema|endpoint|base_url|localhost|127\.0\.0\.1|stack|trace|raw|runtime|engine|module|port|host|api|internal)/i;
103
-
104
- function hideInBasic(key: string) {
105
- return BASIC_HIDDEN_KEY.test(key);
106
- }
107
-
108
- function humanText(value: unknown) {
109
- const text = scalarText(value);
110
- if (text === "-") return text;
111
- if (text.includes("/") || text.includes("@") || /\.[a-z0-9]{2,5}$/i.test(text)) return text;
112
- return titleize(text.replace(/^agent:/i, "").replace(/^tool:/i, ""));
113
- }
114
-
115
- function firstRecordList(value: Record<string, unknown>) {
116
- const preferred = [
117
- "documents", "sources", "items", "agents", "workflows", "runs", "events",
118
- "permissions", "models", "peers", "invitations", "roles", "policies",
119
- "hooks", "tools", "templates", "plugins", "recent_events",
120
- ];
121
- for (const key of preferred) {
122
- const rows = asArray<Record<string, unknown>>(value[key]);
123
- if (rows.length) return rows;
124
- }
125
- return [];
126
- }
127
-
128
- export function ValuePreview({ value }: { value: unknown }) {
129
- if (typeof value === "boolean") {
130
- return <Badge variant={value ? "success" : "muted"}>{value ? "enabled" : "disabled"}</Badge>;
131
- }
132
- if (Array.isArray(value)) {
133
- if (!value.length) return <span className="text-muted-foreground">None</span>;
134
- const primitive = value.every((item) => item === null || ["string", "number", "boolean"].includes(typeof item));
135
- if (primitive) {
136
- return (
137
- <span className="flex flex-wrap gap-1">
138
- {value.slice(0, 5).map((item, index) => <Badge key={`${String(item)}-${index}`} variant="muted">{scalarText(item)}</Badge>)}
139
- {value.length > 5 ? <Badge variant="muted">+{value.length - 5}</Badge> : null}
140
- </span>
141
- );
142
- }
143
- return <span className="text-muted-foreground">{fmtNumber(value.length)} records</span>;
144
- }
145
- if (isRecord(value)) {
146
- const keys = Object.keys(value);
147
- if (!keys.length) return <span className="text-muted-foreground">No fields</span>;
148
- return <span className="text-muted-foreground">{keys.slice(0, 4).map(titleize).join(", ")}{keys.length > 4 ? ` +${keys.length - 4}` : ""}</span>;
149
- }
150
- const text = scalarText(value);
151
- return <span className="break-words">{text.length > 96 ? shortId(text, 96) : text}</span>;
152
- }
153
-
154
- export function KeyValueList({ data, limit = 8 }: { data: Record<string, unknown>; limit?: number }) {
155
- const mode = useAppStore((state) => state.mode);
156
- const rows = Object.entries(data || {})
157
- .filter(([key]) => mode !== "basic" || !hideInBasic(key))
158
- .slice(0, limit);
159
- if (!rows.length) return <EmptyState title="No values" />;
160
- return (
161
- <div className="divide-y divide-border rounded-md border border-border">
162
- {rows.map(([key, value]) => (
163
- <div key={key} className="grid grid-cols-[minmax(9rem,0.5fr)_1fr] gap-3 p-3 text-sm">
164
- <span className="font-medium text-muted-foreground">{titleize(key)}</span>
165
- <span className="min-w-0 break-words"><ValuePreview value={value} /></span>
166
- </div>
167
- ))}
168
- </div>
169
- );
170
- }
171
-
172
- export function StructuredView({
173
- value,
174
- titleKey = "title",
175
- metaKey = "status",
176
- limit = 8,
177
- }: {
178
- value: unknown;
179
- titleKey?: string;
180
- metaKey?: string;
181
- limit?: number;
182
- }) {
183
- const mode = useAppStore((state) => state.mode);
184
- if (mode === "basic") return <FriendlySummary value={value} titleKey={titleKey} metaKey={metaKey} limit={limit} />;
185
- if (Array.isArray(value)) {
186
- if (!value.length) return <EmptyState title="Nothing here yet" detail="New items will appear here when Lattice has something to show." />;
187
- if (value.every((item) => isRecord(item))) {
188
- return <EntityList items={value} titleKey={titleKey} metaKey={metaKey} limit={limit} />;
189
- }
190
- return (
191
- <div className="flex flex-wrap gap-1 rounded-md border border-border bg-background p-3">
192
- {value.slice(0, limit).map((item, index) => <Badge key={`${String(item)}-${index}`} variant="muted">{scalarText(item)}</Badge>)}
193
- {value.length > limit ? <Badge variant="muted">+{value.length - limit}</Badge> : null}
194
- </div>
195
- );
196
- }
197
- if (isRecord(value)) return <KeyValueList data={value} limit={limit} />;
198
- return (
199
- <div className="rounded-md border border-border bg-background p-3 text-sm">
200
- <ValuePreview value={value} />
201
- </div>
202
- );
203
- }
204
-
205
- export function FriendlySummary({
206
- value,
207
- titleKey = "title",
208
- metaKey = "status",
209
- limit = 6,
210
- }: {
211
- value: unknown;
212
- titleKey?: string;
213
- metaKey?: string;
214
- limit?: number;
215
- }) {
216
- if (Array.isArray(value)) {
217
- if (!value.length) return <EmptyState title="Nothing here yet" detail="New items will appear here when Lattice has something to show." />;
218
- if (value.every((item) => isRecord(item))) {
219
- return <EntityList items={value} titleKey={titleKey} metaKey={metaKey} limit={limit} />;
220
- }
221
- return (
222
- <div className="flex flex-wrap gap-1 rounded-md border border-border bg-background/55 p-3">
223
- {value.slice(0, limit).map((item, index) => <Badge key={`${String(item)}-${index}`} variant="muted">{humanText(item)}</Badge>)}
224
- {value.length > limit ? <Badge variant="muted">+{value.length - limit}</Badge> : null}
225
- </div>
226
- );
227
- }
228
- if (isRecord(value)) {
229
- const list = firstRecordList(value);
230
- if (list.length) return <EntityList items={list} titleKey={titleKey} metaKey={metaKey} limit={limit} />;
231
- const friendly = Object.fromEntries(
232
- Object.entries(value)
233
- .filter(([key]) => !hideInBasic(key))
234
- .map(([key, item]) => [key, Array.isArray(item) ? `${fmtNumber(item.length)} items` : isRecord(item) ? "available" : item]),
235
- );
236
- return <KeyValueList data={friendly} limit={limit} />;
237
- }
238
- return (
239
- <div className="rounded-md border border-border bg-background/55 p-3 text-sm">
240
- {humanText(value)}
241
- </div>
242
- );
243
- }
244
-
245
- export function OperationResult({
246
- result,
247
- successLabel = "Request completed",
248
- }: {
249
- result?: ApiResult<unknown> | null;
250
- successLabel?: string;
251
- }) {
252
- const mode = useAppStore((state) => state.mode);
253
- if (!result) return null;
254
- if (!result.ok) {
255
- return <EmptyState title="Request unavailable" detail={result.error || <ValuePreview value={result.data} />} />;
256
- }
257
- return (
258
- <div className="space-y-2 rounded-md border border-border bg-background p-3">
259
- <Badge variant="success">{successLabel}</Badge>
260
- {mode === "basic" ? <FriendlySummary value={result.data} /> : <StructuredView value={result.data} />}
261
- </div>
262
- );
263
- }
264
-
265
- export function EntityList({
266
- items,
267
- titleKey = "title",
268
- metaKey = "type",
269
- limit = 8,
270
- }: {
271
- items: unknown;
272
- titleKey?: string;
273
- metaKey?: string;
274
- limit?: number;
275
- }) {
276
- const mode = useAppStore((state) => state.mode);
277
- const rows = asArray<Record<string, unknown>>(items).slice(0, limit);
278
- if (!rows.length) return <EmptyState title="Nothing here yet" detail="New items will appear here when Lattice has something to show." />;
279
- return (
280
- <div className="grid gap-2">
281
- {rows.map((item, index) => (
282
- <div key={String(item.id || item.name || index)} className="rounded-lg border border-border bg-background/55 p-3">
283
- <div className="flex flex-wrap items-center justify-between gap-2">
284
- <div className="font-medium">{mode === "basic" ? humanText(item[titleKey] || item.name || item.label || `Item ${index + 1}`) : String(item[titleKey] || item.name || item.id || `Record ${index + 1}`)}</div>
285
- <Badge variant="muted">{mode === "basic" ? humanText(item[metaKey] || item.status || item.state || "ready") : String(item[metaKey] || item.status || item.state || "record")}</Badge>
286
- </div>
287
- {item.summary || item.description || item.path || (item.id && item[titleKey] !== item.id) ? (
288
- <p className="mt-1 text-sm text-muted-foreground">{String(item.summary || item.description || item.path || item.id)}</p>
289
- ) : null}
290
- {mode !== "basic" && item.id && item[titleKey] !== item.id ? (
291
- <div className="mt-1 text-xs text-muted-foreground">{shortId(item.id, 48)}</div>
292
- ) : null}
293
- </div>
294
- ))}
295
- </div>
296
- );
297
- }
298
-
299
- export function ModeGate({
300
- title = "Advanced controls",
301
- detail = "Switch modes when you want diagnostics or administrative controls. Basic mode keeps the product focused on everyday use.",
302
- target = "advanced",
303
- }: {
304
- title?: string;
305
- detail?: string;
306
- target?: "advanced" | "admin";
307
- }) {
308
- const setMode = useAppStore((state) => state.setMode);
309
- return (
310
- <Card>
311
- <CardContent className="flex flex-col items-start gap-3 p-6">
312
- <div className="grid h-10 w-10 place-items-center rounded-md border border-border bg-background/70">
313
- <LockKeyhole className="h-5 w-5 text-primary" />
314
- </div>
315
- <div>
316
- <div className="text-lg font-semibold">{title}</div>
317
- <p className="mt-1 max-w-2xl text-sm leading-6 text-muted-foreground">{detail}</p>
318
- </div>
319
- <Button onClick={() => setMode(target)}>{target === "admin" ? "Switch to Admin Console" : "Switch to Advanced"}</Button>
320
- </CardContent>
321
- </Card>
322
- );
323
- }
324
-
325
- export function ActionButton({
326
- label,
327
- successLabel = "Done",
328
- action,
329
- invalidate,
330
- variant = "outline",
331
- disabled,
332
- }: {
333
- label: string;
334
- successLabel?: string;
335
- action: () => Promise<ApiResult<unknown>>;
336
- invalidate?: string[];
337
- variant?: React.ComponentProps<typeof Button>["variant"];
338
- disabled?: boolean;
339
- }) {
340
- const qc = useQueryClient();
341
- const [result, setResult] = React.useState<string | null>(null);
342
- const mut = useMutation({
343
- mutationFn: action,
344
- onSuccess: async (res) => {
345
- setResult(res.ok ? successLabel : res.error || "Unavailable");
346
- if (invalidate) {
347
- await Promise.all(invalidate.map((key) => qc.invalidateQueries({ queryKey: [key] })));
348
- }
349
- },
350
- });
351
- return (
352
- <div className="flex flex-wrap items-center gap-2">
353
- <Button variant={variant} disabled={disabled || mut.isPending} onClick={() => mut.mutate()}>
354
- {mut.isPending ? <Loader2 className="h-4 w-4 animate-spin" /> : null}
355
- {label}
356
- </Button>
357
- {result ? (
358
- <span className={cn("inline-flex items-center gap-1 text-xs", result === successLabel ? "text-emerald-300" : "text-amber-300")}>
359
- {result === successLabel ? <CheckCircle2 className="h-3.5 w-3.5" /> : <AlertCircle className="h-3.5 w-3.5" />}
360
- {result}
361
- </span>
362
- ) : null}
363
- </div>
364
- );
365
- }
366
-
367
- export function Tabs({
368
- tabs,
369
- value,
370
- onChange,
371
- }: {
372
- tabs: Array<{ id: string; label: string }>;
373
- value: string;
374
- onChange: (id: string) => void;
375
- }) {
376
- return (
377
- <div className="inline-flex max-w-full flex-wrap gap-1 rounded-lg border border-border bg-muted/28 p-1">
378
- {tabs.map((tab) => (
379
- <button
380
- key={tab.id}
381
- onClick={() => onChange(tab.id)}
382
- className={cn(
383
- "h-9 rounded-md px-3.5 text-sm font-semibold transition",
384
- value === tab.id ? "bg-card text-foreground shadow-sm" : "text-muted-foreground hover:bg-card/60 hover:text-foreground",
385
- )}
386
- >
387
- {tab.label}
388
- </button>
389
- ))}
390
- </div>
391
- );
392
- }
@@ -1,27 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "@/lib/utils";
3
-
4
- type BadgeProps = React.HTMLAttributes<HTMLSpanElement> & {
5
- variant?: "default" | "success" | "warning" | "muted" | "danger";
6
- };
7
-
8
- const variants = {
9
- default: "border-primary/25 bg-primary/12 text-primary",
10
- success: "border-emerald-500/25 bg-emerald-500/12 text-emerald-300",
11
- warning: "border-amber-500/25 bg-amber-500/12 text-amber-300",
12
- muted: "border-border bg-muted/70 text-muted-foreground",
13
- danger: "border-destructive/30 bg-destructive/12 text-destructive",
14
- };
15
-
16
- export function Badge({ className, variant = "default", ...props }: BadgeProps) {
17
- return (
18
- <span
19
- className={cn(
20
- "inline-flex min-h-6 max-w-full items-center rounded-md border px-2 py-0.5 text-xs font-semibold leading-none",
21
- variants[variant],
22
- className,
23
- )}
24
- {...props}
25
- />
26
- );
27
- }
@@ -1,37 +0,0 @@
1
- import * as React from "react";
2
- import { cva, type VariantProps } from "class-variance-authority";
3
- import { cn } from "@/lib/utils";
4
-
5
- const buttonVariants = cva(
6
- "inline-flex h-10 max-w-full items-center justify-center gap-2 rounded-md px-3.5 text-sm font-semibold leading-none transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-45",
7
- {
8
- variants: {
9
- variant: {
10
- default: "bg-primary text-primary-foreground shadow-[0_10px_30px_hsl(var(--primary)/0.16)] hover:bg-primary/92",
11
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/82",
12
- ghost: "text-foreground hover:bg-muted",
13
- outline: "border border-border bg-card/70 hover:bg-muted/88",
14
- destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
15
- },
16
- size: {
17
- sm: "h-8 px-2.5 text-xs",
18
- md: "h-10 px-3.5",
19
- icon: "h-10 w-10 px-0",
20
- },
21
- },
22
- defaultVariants: {
23
- variant: "default",
24
- size: "md",
25
- },
26
- },
27
- );
28
-
29
- export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
30
- VariantProps<typeof buttonVariants>;
31
-
32
- export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
33
- ({ className, variant, size, ...props }, ref) => (
34
- <button ref={ref} className={cn(buttonVariants({ variant, size, className }))} {...props} />
35
- ),
36
- );
37
- Button.displayName = "Button";
@@ -1,22 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "@/lib/utils";
3
-
4
- export function Card({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
5
- return <section className={cn("premium-surface rounded-lg text-card-foreground", className)} {...props} />;
6
- }
7
-
8
- export function CardHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
9
- return <div className={cn("flex flex-col gap-1.5 p-5", className)} {...props} />;
10
- }
11
-
12
- export function CardTitle({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
13
- return <h2 className={cn("text-base font-semibold tracking-normal", className)} {...props} />;
14
- }
15
-
16
- export function CardDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
17
- return <p className={cn("text-sm leading-6 text-muted-foreground", className)} {...props} />;
18
- }
19
-
20
- export function CardContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
21
- return <div className={cn("p-5 pt-0", className)} {...props} />;
22
- }
@@ -1,16 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "@/lib/utils";
3
-
4
- export const Input = React.forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>(
5
- ({ className, ...props }, ref) => (
6
- <input
7
- ref={ref}
8
- className={cn(
9
- "h-10 w-full rounded-md border border-input bg-background/70 px-3 text-sm outline-none transition placeholder:text-muted-foreground focus:border-ring focus:ring-2 focus:ring-ring/35 disabled:cursor-not-allowed disabled:opacity-50",
10
- className,
11
- )}
12
- {...props}
13
- />
14
- ),
15
- );
16
- Input.displayName = "Input";
@@ -1,16 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "@/lib/utils";
3
-
4
- export const Textarea = React.forwardRef<HTMLTextAreaElement, React.TextareaHTMLAttributes<HTMLTextAreaElement>>(
5
- ({ className, ...props }, ref) => (
6
- <textarea
7
- ref={ref}
8
- className={cn(
9
- "min-h-28 w-full resize-y rounded-md border border-input bg-background/70 px-3 py-3 text-sm leading-6 outline-none transition placeholder:text-muted-foreground focus:border-ring focus:ring-2 focus:ring-ring/35 disabled:cursor-not-allowed disabled:opacity-50",
10
- className,
11
- )}
12
- {...props}
13
- />
14
- ),
15
- );
16
- Textarea.displayName = "Textarea";