@useatlas/create 0.0.2 → 0.0.4
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 +4 -18
- package/index.ts +191 -31
- package/package.json +1 -1
- package/templates/docker/.env.example +3 -3
- package/templates/docker/Dockerfile.sidecar +28 -0
- package/templates/docker/bin/__tests__/plugin-cli.test.ts +9 -9
- package/templates/docker/bin/atlas.ts +108 -44
- package/templates/docker/data/demo-semantic/catalog.yml +51 -27
- package/templates/docker/data/demo-semantic/entities/accounts.yml +95 -103
- package/templates/docker/data/demo-semantic/entities/companies.yml +88 -152
- package/templates/docker/data/demo-semantic/entities/people.yml +82 -95
- package/templates/docker/data/demo-semantic/glossary.yml +104 -8
- package/templates/docker/data/demo-semantic/metrics/accounts.yml +62 -23
- package/templates/docker/data/demo-semantic/metrics/companies.yml +52 -78
- package/templates/docker/docker-compose.yml +1 -1
- package/templates/docker/docs/deploy.md +2 -39
- package/templates/docker/package.json +17 -1
- package/templates/docker/semantic/catalog.yml +62 -3
- package/templates/docker/semantic/entities/accounts.yml +162 -0
- package/templates/docker/semantic/entities/companies.yml +143 -0
- package/templates/docker/semantic/entities/people.yml +132 -0
- package/templates/docker/semantic/glossary.yml +116 -4
- package/templates/docker/semantic/metrics/accounts.yml +77 -0
- package/templates/docker/semantic/metrics/companies.yml +63 -0
- package/templates/docker/sidecar/Dockerfile +5 -6
- package/templates/docker/sidecar/railway.json +1 -2
- package/templates/docker/src/api/__tests__/admin.test.ts +7 -7
- package/templates/docker/src/api/__tests__/health-plugin.test.ts +7 -0
- package/templates/docker/src/api/__tests__/health.test.ts +30 -8
- package/templates/docker/src/api/routes/admin.ts +549 -8
- package/templates/docker/src/api/routes/chat.ts +5 -20
- package/templates/docker/src/api/routes/health.ts +39 -27
- package/templates/docker/src/api/routes/openapi.ts +1329 -74
- package/templates/docker/src/api/routes/query.ts +2 -1
- package/templates/docker/src/api/server.ts +27 -0
- package/templates/docker/src/app/api/[...route]/route.ts +2 -2
- package/templates/docker/src/app/globals.css +13 -12
- package/templates/docker/src/app/layout.tsx +9 -2
- package/templates/docker/src/components/ui/alert-dialog.tsx +196 -0
- package/templates/docker/src/components/ui/badge.tsx +48 -0
- package/templates/docker/src/components/ui/button.tsx +64 -0
- package/templates/docker/src/components/ui/card.tsx +92 -0
- package/templates/docker/src/components/ui/collapsible.tsx +33 -0
- package/templates/docker/src/components/ui/command.tsx +184 -0
- package/templates/docker/src/components/ui/dialog.tsx +158 -0
- package/templates/docker/src/components/ui/dropdown-menu.tsx +257 -0
- package/templates/docker/src/components/ui/input.tsx +21 -0
- package/templates/docker/src/components/ui/scroll-area.tsx +58 -0
- package/templates/docker/src/components/ui/select.tsx +190 -0
- package/templates/docker/src/components/ui/separator.tsx +28 -0
- package/templates/docker/src/components/ui/sheet.tsx +143 -0
- package/templates/docker/src/components/ui/sidebar.tsx +726 -0
- package/templates/docker/src/components/ui/skeleton.tsx +13 -0
- package/templates/docker/src/components/ui/table.tsx +116 -0
- package/templates/docker/src/components/ui/tabs.tsx +91 -0
- package/templates/docker/src/components/ui/toggle-group.tsx +83 -0
- package/templates/docker/src/components/ui/toggle.tsx +47 -0
- package/templates/docker/src/components/ui/tooltip.tsx +57 -0
- package/templates/docker/src/hooks/use-mobile.ts +19 -0
- package/templates/docker/src/lib/__tests__/agent-cache.test.ts +2 -0
- package/templates/docker/src/lib/__tests__/agent-dialect.test.ts +17 -0
- package/templates/docker/src/lib/__tests__/agent-health-annotations.test.ts +2 -0
- package/templates/docker/src/lib/__tests__/agent-integration.test.ts +2 -0
- package/templates/docker/src/lib/__tests__/config.test.ts +69 -19
- package/templates/docker/src/lib/__tests__/plugin-aware-validation.test.ts +321 -0
- package/templates/docker/src/lib/__tests__/providers.test.ts +32 -1
- package/templates/docker/src/lib/__tests__/startup-actions.test.ts +9 -0
- package/templates/docker/src/lib/__tests__/startup-first-run.test.ts +429 -0
- package/templates/docker/src/lib/__tests__/startup.test.ts +5 -0
- package/templates/docker/src/lib/agent-query.ts +5 -23
- package/templates/docker/src/lib/agent.ts +32 -112
- package/templates/docker/src/lib/auth/__tests__/migrate.test.ts +5 -3
- package/templates/docker/src/lib/auth/middleware.ts +30 -4
- package/templates/docker/src/lib/auth/migrate.ts +97 -0
- package/templates/docker/src/lib/auth/server.ts +12 -1
- package/templates/docker/src/lib/config.ts +37 -39
- package/templates/docker/src/lib/db/__tests__/connection.test.ts +89 -14
- package/templates/docker/src/lib/db/__tests__/registry-health.test.ts +1 -18
- package/templates/docker/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -19
- package/templates/docker/src/lib/db/__tests__/registry.test.ts +11 -208
- package/templates/docker/src/lib/db/connection.ts +87 -265
- package/templates/docker/src/lib/db/internal.ts +6 -1
- package/templates/docker/src/lib/plugins/__tests__/hooks-integration.test.ts +3 -1
- package/templates/docker/src/lib/plugins/__tests__/hooks.test.ts +2 -2
- package/templates/docker/src/lib/plugins/__tests__/migrate.test.ts +355 -1
- package/templates/docker/src/lib/plugins/__tests__/registry.test.ts +32 -5
- package/templates/docker/src/lib/plugins/__tests__/wiring.test.ts +228 -14
- package/templates/docker/src/lib/plugins/index.ts +4 -1
- package/templates/docker/src/lib/plugins/migrate.ts +103 -0
- package/templates/docker/src/lib/plugins/registry.ts +12 -6
- package/templates/docker/src/lib/plugins/wiring.ts +113 -4
- package/templates/docker/src/lib/providers.ts +6 -1
- package/templates/docker/src/lib/security.ts +24 -0
- package/templates/docker/src/lib/semantic.ts +2 -0
- package/templates/docker/src/lib/sidecar-types.ts +12 -1
- package/templates/docker/src/lib/startup.ts +71 -101
- package/templates/docker/src/lib/tools/__tests__/custom-validation.test.ts +2 -0
- package/templates/docker/src/lib/tools/__tests__/explore-nsjail.test.ts +32 -18
- package/templates/docker/src/lib/tools/__tests__/explore-plugin.test.ts +14 -14
- package/templates/docker/src/lib/tools/__tests__/explore-sidecar.test.ts +5 -3
- package/templates/docker/src/lib/tools/__tests__/python-nsjail.test.ts +515 -0
- package/templates/docker/src/lib/tools/__tests__/python-sandbox.test.ts +397 -0
- package/templates/docker/src/lib/tools/__tests__/python-sidecar.test.ts +365 -0
- package/templates/docker/src/lib/tools/__tests__/python.test.ts +331 -0
- package/templates/docker/src/lib/tools/__tests__/registry-actions.test.ts +1 -13
- package/templates/docker/src/lib/tools/__tests__/registry.test.ts +38 -31
- package/templates/docker/src/lib/tools/__tests__/sql-audit.test.ts +2 -0
- package/templates/docker/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +2 -0
- package/templates/docker/src/lib/tools/__tests__/sql-ratelimit.test.ts +2 -0
- package/templates/docker/src/lib/tools/__tests__/sql.test.ts +5 -308
- package/templates/docker/src/lib/tools/explore-nsjail.ts +17 -12
- package/templates/docker/src/lib/tools/explore-sidecar.ts +25 -0
- package/templates/docker/src/lib/tools/explore.ts +28 -32
- package/templates/docker/src/lib/tools/python-nsjail.ts +396 -0
- package/templates/docker/src/lib/tools/python-sandbox.ts +476 -0
- package/templates/docker/src/lib/tools/python-sidecar.ts +150 -0
- package/templates/docker/src/lib/tools/python.ts +367 -0
- package/templates/docker/src/lib/tools/registry.ts +49 -22
- package/templates/docker/src/lib/tools/sql.ts +88 -88
- package/templates/docker/src/types/vercel-sandbox.d.ts +7 -0
- package/templates/docker/src/ui/components/admin/admin-layout.tsx +77 -8
- package/templates/docker/src/ui/components/admin/admin-sidebar.tsx +25 -17
- package/templates/docker/src/ui/components/admin/change-password-dialog.tsx +128 -0
- package/templates/docker/src/ui/components/admin/entity-detail.tsx +3 -3
- package/templates/docker/src/ui/components/admin/semantic-file-tree.tsx +159 -0
- package/templates/docker/src/ui/components/atlas-chat.tsx +64 -12
- package/templates/docker/src/ui/components/chart/result-chart.tsx +25 -15
- package/templates/docker/src/ui/components/chat/markdown.tsx +88 -42
- package/templates/docker/src/ui/components/chat/python-result-card.tsx +244 -0
- package/templates/docker/src/ui/components/chat/sql-block.tsx +39 -15
- package/templates/docker/src/ui/components/chat/sql-result-card.tsx +6 -1
- package/templates/docker/src/ui/components/chat/tool-part.tsx +12 -3
- package/templates/docker/src/ui/components/chat/typing-indicator.tsx +5 -2
- package/templates/docker/src/ui/components/conversations/conversation-item.tsx +25 -20
- package/templates/docker/src/ui/context.tsx +1 -1
- package/templates/docker/src/ui/hooks/use-conversations.ts +3 -3
- package/templates/docker/src/ui/hooks/use-dark-mode.ts +17 -10
- package/templates/docker/tsconfig.json +2 -2
- package/templates/nextjs-standalone/.env.example +1 -1
- package/templates/nextjs-standalone/bin/__tests__/plugin-cli.test.ts +9 -9
- package/templates/nextjs-standalone/bin/atlas.ts +108 -44
- package/templates/nextjs-standalone/data/demo-semantic/catalog.yml +51 -27
- package/templates/nextjs-standalone/data/demo-semantic/entities/accounts.yml +95 -103
- package/templates/nextjs-standalone/data/demo-semantic/entities/companies.yml +88 -152
- package/templates/nextjs-standalone/data/demo-semantic/entities/people.yml +82 -95
- package/templates/nextjs-standalone/data/demo-semantic/glossary.yml +104 -8
- package/templates/nextjs-standalone/data/demo-semantic/metrics/accounts.yml +62 -23
- package/templates/nextjs-standalone/data/demo-semantic/metrics/companies.yml +52 -78
- package/templates/nextjs-standalone/docs/deploy.md +2 -39
- package/templates/nextjs-standalone/package.json +11 -2
- package/templates/nextjs-standalone/scripts/migrate-auth.ts +25 -0
- package/templates/nextjs-standalone/scripts/seed-demo.ts +94 -0
- package/templates/nextjs-standalone/semantic/catalog.yml +62 -3
- package/templates/nextjs-standalone/semantic/entities/accounts.yml +162 -0
- package/templates/nextjs-standalone/semantic/entities/companies.yml +143 -0
- package/templates/nextjs-standalone/semantic/entities/people.yml +132 -0
- package/templates/nextjs-standalone/semantic/glossary.yml +116 -4
- package/templates/nextjs-standalone/semantic/metrics/accounts.yml +77 -0
- package/templates/nextjs-standalone/semantic/metrics/companies.yml +63 -0
- package/templates/nextjs-standalone/src/api/__tests__/admin.test.ts +7 -7
- package/templates/nextjs-standalone/src/api/__tests__/health-plugin.test.ts +7 -0
- package/templates/nextjs-standalone/src/api/__tests__/health.test.ts +30 -8
- package/templates/nextjs-standalone/src/api/routes/admin.ts +549 -8
- package/templates/nextjs-standalone/src/api/routes/chat.ts +5 -20
- package/templates/nextjs-standalone/src/api/routes/health.ts +39 -27
- package/templates/nextjs-standalone/src/api/routes/openapi.ts +1329 -74
- package/templates/nextjs-standalone/src/api/routes/query.ts +2 -1
- package/templates/nextjs-standalone/src/api/server.ts +27 -0
- package/templates/nextjs-standalone/src/app/api/[...route]/route.ts +2 -2
- package/templates/nextjs-standalone/src/app/globals.css +13 -12
- package/templates/nextjs-standalone/src/app/layout.tsx +9 -2
- package/templates/nextjs-standalone/src/components/ui/alert-dialog.tsx +196 -0
- package/templates/nextjs-standalone/src/components/ui/badge.tsx +48 -0
- package/templates/nextjs-standalone/src/components/ui/button.tsx +64 -0
- package/templates/nextjs-standalone/src/components/ui/card.tsx +92 -0
- package/templates/nextjs-standalone/src/components/ui/collapsible.tsx +33 -0
- package/templates/nextjs-standalone/src/components/ui/command.tsx +184 -0
- package/templates/nextjs-standalone/src/components/ui/dialog.tsx +158 -0
- package/templates/nextjs-standalone/src/components/ui/dropdown-menu.tsx +257 -0
- package/templates/nextjs-standalone/src/components/ui/input.tsx +21 -0
- package/templates/nextjs-standalone/src/components/ui/scroll-area.tsx +58 -0
- package/templates/nextjs-standalone/src/components/ui/select.tsx +190 -0
- package/templates/nextjs-standalone/src/components/ui/separator.tsx +28 -0
- package/templates/nextjs-standalone/src/components/ui/sheet.tsx +143 -0
- package/templates/nextjs-standalone/src/components/ui/sidebar.tsx +726 -0
- package/templates/nextjs-standalone/src/components/ui/skeleton.tsx +13 -0
- package/templates/nextjs-standalone/src/components/ui/table.tsx +116 -0
- package/templates/nextjs-standalone/src/components/ui/tabs.tsx +91 -0
- package/templates/nextjs-standalone/src/components/ui/toggle-group.tsx +83 -0
- package/templates/nextjs-standalone/src/components/ui/toggle.tsx +47 -0
- package/templates/nextjs-standalone/src/components/ui/tooltip.tsx +57 -0
- package/templates/nextjs-standalone/src/hooks/use-mobile.ts +19 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-cache.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-dialect.test.ts +17 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-health-annotations.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-integration.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/__tests__/config.test.ts +69 -19
- package/templates/nextjs-standalone/src/lib/__tests__/plugin-aware-validation.test.ts +321 -0
- package/templates/nextjs-standalone/src/lib/__tests__/providers.test.ts +32 -1
- package/templates/nextjs-standalone/src/lib/__tests__/startup-actions.test.ts +9 -0
- package/templates/nextjs-standalone/src/lib/__tests__/startup-first-run.test.ts +429 -0
- package/templates/nextjs-standalone/src/lib/__tests__/startup.test.ts +5 -0
- package/templates/nextjs-standalone/src/lib/agent-query.ts +5 -23
- package/templates/nextjs-standalone/src/lib/agent.ts +32 -112
- package/templates/nextjs-standalone/src/lib/auth/__tests__/migrate.test.ts +5 -3
- package/templates/nextjs-standalone/src/lib/auth/middleware.ts +30 -4
- package/templates/nextjs-standalone/src/lib/auth/migrate.ts +97 -0
- package/templates/nextjs-standalone/src/lib/auth/server.ts +12 -1
- package/templates/nextjs-standalone/src/lib/config.ts +37 -39
- package/templates/nextjs-standalone/src/lib/db/__tests__/connection.test.ts +89 -14
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry-health.test.ts +1 -18
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -19
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry.test.ts +11 -208
- package/templates/nextjs-standalone/src/lib/db/connection.ts +87 -265
- package/templates/nextjs-standalone/src/lib/db/internal.ts +6 -1
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks-integration.test.ts +3 -1
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks.test.ts +2 -2
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/migrate.test.ts +355 -1
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/registry.test.ts +32 -5
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/wiring.test.ts +228 -14
- package/templates/nextjs-standalone/src/lib/plugins/index.ts +4 -1
- package/templates/nextjs-standalone/src/lib/plugins/migrate.ts +103 -0
- package/templates/nextjs-standalone/src/lib/plugins/registry.ts +12 -6
- package/templates/nextjs-standalone/src/lib/plugins/wiring.ts +113 -4
- package/templates/nextjs-standalone/src/lib/providers.ts +6 -1
- package/templates/nextjs-standalone/src/lib/security.ts +24 -0
- package/templates/nextjs-standalone/src/lib/semantic.ts +2 -0
- package/templates/nextjs-standalone/src/lib/sidecar-types.ts +12 -1
- package/templates/nextjs-standalone/src/lib/startup.ts +71 -101
- package/templates/nextjs-standalone/src/lib/tools/__tests__/custom-validation.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-nsjail.test.ts +32 -18
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-plugin.test.ts +14 -14
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sidecar.test.ts +5 -3
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python-nsjail.test.ts +515 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sandbox.test.ts +397 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sidecar.test.ts +365 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python.test.ts +331 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/registry-actions.test.ts +1 -13
- package/templates/nextjs-standalone/src/lib/tools/__tests__/registry.test.ts +38 -31
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-audit.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-ratelimit.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql.test.ts +5 -308
- package/templates/nextjs-standalone/src/lib/tools/explore-nsjail.ts +17 -12
- package/templates/nextjs-standalone/src/lib/tools/explore-sidecar.ts +25 -0
- package/templates/nextjs-standalone/src/lib/tools/explore.ts +28 -32
- package/templates/nextjs-standalone/src/lib/tools/python-nsjail.ts +396 -0
- package/templates/nextjs-standalone/src/lib/tools/python-sandbox.ts +476 -0
- package/templates/nextjs-standalone/src/lib/tools/python-sidecar.ts +150 -0
- package/templates/nextjs-standalone/src/lib/tools/python.ts +367 -0
- package/templates/nextjs-standalone/src/lib/tools/registry.ts +49 -22
- package/templates/nextjs-standalone/src/lib/tools/sql.ts +88 -88
- package/templates/nextjs-standalone/src/ui/components/admin/admin-layout.tsx +77 -8
- package/templates/nextjs-standalone/src/ui/components/admin/admin-sidebar.tsx +25 -17
- package/templates/nextjs-standalone/src/ui/components/admin/change-password-dialog.tsx +128 -0
- package/templates/nextjs-standalone/src/ui/components/admin/entity-detail.tsx +3 -3
- package/templates/nextjs-standalone/src/ui/components/admin/semantic-file-tree.tsx +159 -0
- package/templates/nextjs-standalone/src/ui/components/atlas-chat.tsx +64 -12
- package/templates/nextjs-standalone/src/ui/components/chart/result-chart.tsx +25 -15
- package/templates/nextjs-standalone/src/ui/components/chat/markdown.tsx +88 -42
- package/templates/nextjs-standalone/src/ui/components/chat/python-result-card.tsx +244 -0
- package/templates/nextjs-standalone/src/ui/components/chat/sql-block.tsx +39 -15
- package/templates/nextjs-standalone/src/ui/components/chat/sql-result-card.tsx +6 -1
- package/templates/nextjs-standalone/src/ui/components/chat/tool-part.tsx +12 -3
- package/templates/nextjs-standalone/src/ui/components/chat/typing-indicator.tsx +5 -2
- package/templates/nextjs-standalone/src/ui/components/conversations/conversation-item.tsx +25 -20
- package/templates/nextjs-standalone/src/ui/context.tsx +1 -1
- package/templates/nextjs-standalone/src/ui/hooks/use-conversations.ts +3 -3
- package/templates/nextjs-standalone/src/ui/hooks/use-dark-mode.ts +17 -10
- package/templates/nextjs-standalone/tsconfig.json +0 -1
- package/templates/nextjs-standalone/vercel.json +4 -1
- package/templates/docker/render.yaml +0 -34
- package/templates/docker/semantic/entities/.gitkeep +0 -0
- package/templates/docker/semantic/metrics/.gitkeep +0 -0
- package/templates/docker/src/lib/db/__tests__/duckdb.test.ts +0 -141
- package/templates/docker/src/lib/db/__tests__/salesforce.test.ts +0 -339
- package/templates/docker/src/lib/db/__tests__/snowflake.test.ts +0 -217
- package/templates/docker/src/lib/db/duckdb.ts +0 -122
- package/templates/docker/src/lib/db/salesforce.ts +0 -342
- package/templates/docker/src/lib/tools/__tests__/salesforce-tool.test.ts +0 -154
- package/templates/docker/src/lib/tools/__tests__/soql-validation.test.ts +0 -303
- package/templates/docker/src/lib/tools/__tests__/sql-duckdb.test.ts +0 -233
- package/templates/docker/src/lib/tools/salesforce.ts +0 -138
- package/templates/docker/src/lib/tools/soql-validation.ts +0 -172
- package/templates/nextjs-standalone/semantic/entities/.gitkeep +0 -0
- package/templates/nextjs-standalone/semantic/metrics/.gitkeep +0 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/duckdb.test.ts +0 -141
- package/templates/nextjs-standalone/src/lib/db/__tests__/salesforce.test.ts +0 -339
- package/templates/nextjs-standalone/src/lib/db/__tests__/snowflake.test.ts +0 -217
- package/templates/nextjs-standalone/src/lib/db/duckdb.ts +0 -122
- package/templates/nextjs-standalone/src/lib/db/salesforce.ts +0 -342
- package/templates/nextjs-standalone/src/lib/tools/__tests__/salesforce-tool.test.ts +0 -154
- package/templates/nextjs-standalone/src/lib/tools/__tests__/soql-validation.test.ts +0 -303
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-duckdb.test.ts +0 -233
- package/templates/nextjs-standalone/src/lib/tools/salesforce.ts +0 -138
- package/templates/nextjs-standalone/src/lib/tools/soql-validation.ts +0 -172
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Component, type ReactNode, type ErrorInfo, useContext, useState } from "react";
|
|
4
|
+
import { getToolArgs, getToolResult, isToolComplete } from "../../lib/helpers";
|
|
5
|
+
import { DarkModeContext } from "../../hooks/use-dark-mode";
|
|
6
|
+
import dynamic from "next/dynamic";
|
|
7
|
+
import { LoadingCard } from "./loading-card";
|
|
8
|
+
import { DataTable } from "./data-table";
|
|
9
|
+
import type { ChartDetectionResult } from "../chart/chart-detection";
|
|
10
|
+
|
|
11
|
+
const ResultChart = dynamic(
|
|
12
|
+
() => import("../chart/result-chart").then((m) => ({ default: m.ResultChart })),
|
|
13
|
+
{ ssr: false, loading: () => <div className="h-64 animate-pulse rounded-lg bg-zinc-100 dark:bg-zinc-800" /> },
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
interface RechartsChartConfig {
|
|
17
|
+
type: "line" | "bar" | "pie";
|
|
18
|
+
data: Record<string, unknown>[];
|
|
19
|
+
categoryKey: string;
|
|
20
|
+
valueKeys: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface PythonChart {
|
|
24
|
+
base64: string;
|
|
25
|
+
mimeType: "image/png";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const ALLOWED_IMAGE_MIME = new Set(["image/png", "image/jpeg"]);
|
|
29
|
+
|
|
30
|
+
/* ------------------------------------------------------------------ */
|
|
31
|
+
/* Error boundary */
|
|
32
|
+
/* ------------------------------------------------------------------ */
|
|
33
|
+
|
|
34
|
+
class PythonErrorBoundary extends Component<
|
|
35
|
+
{ children: ReactNode },
|
|
36
|
+
{ hasError: boolean; error?: Error }
|
|
37
|
+
> {
|
|
38
|
+
constructor(props: { children: ReactNode }) {
|
|
39
|
+
super(props);
|
|
40
|
+
this.state = { hasError: false };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static getDerivedStateFromError(error: Error) {
|
|
44
|
+
return { hasError: true, error };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
componentDidCatch(error: Error, info: ErrorInfo) {
|
|
48
|
+
console.error("PythonResultCard rendering failed:", error, info.componentStack);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
render() {
|
|
52
|
+
if (this.state.hasError) {
|
|
53
|
+
return (
|
|
54
|
+
<div className="my-2 rounded-lg border border-red-300 bg-red-50 px-3 py-2 text-xs text-red-700 dark:border-red-900/50 dark:bg-red-950/20 dark:text-red-400">
|
|
55
|
+
Python result could not be rendered: {this.state.error?.message ?? "unknown error"}
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
return this.props.children;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* ------------------------------------------------------------------ */
|
|
64
|
+
/* Main component */
|
|
65
|
+
/* ------------------------------------------------------------------ */
|
|
66
|
+
|
|
67
|
+
export function PythonResultCard({ part }: { part: unknown }) {
|
|
68
|
+
return (
|
|
69
|
+
<PythonErrorBoundary>
|
|
70
|
+
<PythonResultCardInner part={part} />
|
|
71
|
+
</PythonErrorBoundary>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function PythonResultCardInner({ part }: { part: unknown }) {
|
|
76
|
+
const dark = useContext(DarkModeContext);
|
|
77
|
+
const args = getToolArgs(part);
|
|
78
|
+
const raw = getToolResult(part);
|
|
79
|
+
const done = isToolComplete(part);
|
|
80
|
+
const [open, setOpen] = useState(true);
|
|
81
|
+
|
|
82
|
+
if (!done) return <LoadingCard label="Running Python..." />;
|
|
83
|
+
|
|
84
|
+
// Structural validation — result must be a non-null object
|
|
85
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
86
|
+
return (
|
|
87
|
+
<div className="my-2 rounded-lg border border-yellow-300 bg-yellow-50 px-3 py-2 text-xs text-yellow-700 dark:border-yellow-900/50 dark:bg-yellow-950/20 dark:text-yellow-400">
|
|
88
|
+
Python executed but returned an unexpected result format.
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const result = raw as Record<string, unknown>;
|
|
94
|
+
|
|
95
|
+
if (!result.success) {
|
|
96
|
+
return (
|
|
97
|
+
<div className="my-2 overflow-hidden rounded-lg border border-red-300 bg-red-50 dark:border-red-900/50 dark:bg-red-950/20">
|
|
98
|
+
<div className="px-3 py-2 text-xs font-medium text-red-700 dark:text-red-400">
|
|
99
|
+
Python execution failed
|
|
100
|
+
</div>
|
|
101
|
+
<pre className="border-t border-red-200 px-3 py-2 text-xs whitespace-pre-wrap text-red-600 dark:border-red-900/50 dark:text-red-300">
|
|
102
|
+
{String(result.error ?? "Unknown error")}
|
|
103
|
+
</pre>
|
|
104
|
+
{!!result.output && (
|
|
105
|
+
<pre className="border-t border-red-200 px-3 py-2 text-xs whitespace-pre-wrap text-red-500 dark:border-red-900/50 dark:text-red-400">
|
|
106
|
+
{String(result.output)}
|
|
107
|
+
</pre>
|
|
108
|
+
)}
|
|
109
|
+
</div>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const output = result.output ? String(result.output) : null;
|
|
114
|
+
const table = result.table as { columns: string[]; rows: unknown[][] } | undefined;
|
|
115
|
+
const charts = Array.isArray(result.charts) ? (result.charts as PythonChart[]) : undefined;
|
|
116
|
+
const rechartsCharts = Array.isArray(result.rechartsCharts) ? (result.rechartsCharts as RechartsChartConfig[]) : undefined;
|
|
117
|
+
|
|
118
|
+
const hasTable = table && Array.isArray(table.columns) && Array.isArray(table.rows)
|
|
119
|
+
&& table.columns.length > 0 && table.rows.length > 0;
|
|
120
|
+
const hasCharts = charts && charts.length > 0;
|
|
121
|
+
const hasRechartsCharts = rechartsCharts && rechartsCharts.length > 0;
|
|
122
|
+
|
|
123
|
+
// Filter charts to only safe image MIME types
|
|
124
|
+
const safeCharts = hasCharts
|
|
125
|
+
? charts.filter((c) => c.base64 && ALLOWED_IMAGE_MIME.has(c.mimeType))
|
|
126
|
+
: [];
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<div className="my-2 overflow-hidden rounded-lg border border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-900">
|
|
130
|
+
<button
|
|
131
|
+
onClick={() => setOpen(!open)}
|
|
132
|
+
className="flex w-full items-center gap-2 px-3 py-2 text-left text-xs transition-colors hover:bg-zinc-100/60 dark:hover:bg-zinc-800/60"
|
|
133
|
+
>
|
|
134
|
+
<span className="rounded bg-emerald-100 px-1.5 py-0.5 font-medium text-emerald-700 dark:bg-emerald-600/20 dark:text-emerald-400">
|
|
135
|
+
Python
|
|
136
|
+
</span>
|
|
137
|
+
<span className="flex-1 truncate text-zinc-500 dark:text-zinc-400">
|
|
138
|
+
{String(args.explanation ?? "Python result")}
|
|
139
|
+
</span>
|
|
140
|
+
<span className="text-zinc-400 dark:text-zinc-600">{open ? "\u25BE" : "\u25B8"}</span>
|
|
141
|
+
</button>
|
|
142
|
+
|
|
143
|
+
{open && (
|
|
144
|
+
<div className="space-y-2 border-t border-zinc-100 px-3 py-2 dark:border-zinc-800">
|
|
145
|
+
{output && (
|
|
146
|
+
<pre className="rounded-md bg-zinc-100 px-3 py-2 text-xs whitespace-pre-wrap text-zinc-700 dark:bg-zinc-800 dark:text-zinc-300">
|
|
147
|
+
{output}
|
|
148
|
+
</pre>
|
|
149
|
+
)}
|
|
150
|
+
|
|
151
|
+
{hasTable && <DataTable columns={table.columns} rows={table.rows} />}
|
|
152
|
+
|
|
153
|
+
{hasRechartsCharts &&
|
|
154
|
+
rechartsCharts.map((chart, i) => (
|
|
155
|
+
<RechartsChartSection key={i} chart={chart} dark={dark} />
|
|
156
|
+
))}
|
|
157
|
+
|
|
158
|
+
{safeCharts.length > 0 &&
|
|
159
|
+
safeCharts.map((chart, i) => (
|
|
160
|
+
<ChartImage key={i} chart={chart} index={i} />
|
|
161
|
+
))}
|
|
162
|
+
</div>
|
|
163
|
+
)}
|
|
164
|
+
</div>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* ------------------------------------------------------------------ */
|
|
169
|
+
/* Chart image with error handling */
|
|
170
|
+
/* ------------------------------------------------------------------ */
|
|
171
|
+
|
|
172
|
+
function ChartImage({ chart, index }: { chart: PythonChart; index: number }) {
|
|
173
|
+
const [failed, setFailed] = useState(false);
|
|
174
|
+
|
|
175
|
+
if (failed) {
|
|
176
|
+
return (
|
|
177
|
+
<div className="rounded-lg border border-yellow-300 bg-yellow-50 px-3 py-2 text-xs text-yellow-700 dark:border-yellow-900/50 dark:bg-yellow-950/20 dark:text-yellow-400">
|
|
178
|
+
Chart {index + 1} failed to render.
|
|
179
|
+
</div>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
// eslint-disable-next-line @next/next/no-img-element -- base64 data URL, cannot use next/image optimization
|
|
185
|
+
<img
|
|
186
|
+
src={`data:${chart.mimeType};base64,${chart.base64}`}
|
|
187
|
+
alt={`Python chart ${index + 1}`}
|
|
188
|
+
className="max-w-full rounded-lg border border-zinc-200 dark:border-zinc-700"
|
|
189
|
+
onError={() => setFailed(true)}
|
|
190
|
+
/>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* ------------------------------------------------------------------ */
|
|
195
|
+
/* Recharts section — bypasses auto-detection with synthetic result */
|
|
196
|
+
/* ------------------------------------------------------------------ */
|
|
197
|
+
|
|
198
|
+
function RechartsChartSection({ chart, dark }: { chart: RechartsChartConfig; dark: boolean }) {
|
|
199
|
+
if (!chart.categoryKey || !Array.isArray(chart.valueKeys) || !Array.isArray(chart.data)) {
|
|
200
|
+
return (
|
|
201
|
+
<div className="rounded-lg border border-yellow-300 bg-yellow-50 px-3 py-2 text-xs text-yellow-700 dark:border-yellow-900/50 dark:bg-yellow-950/20 dark:text-yellow-400">
|
|
202
|
+
Chart data is incomplete or malformed.
|
|
203
|
+
</div>
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const headers = [chart.categoryKey, ...chart.valueKeys];
|
|
208
|
+
const rows: string[][] = chart.data.map((row) =>
|
|
209
|
+
headers.map((key) => (row[key] == null ? "" : String(row[key]))),
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// Build a synthetic detection result so ResultChart uses the backend's
|
|
213
|
+
// chart config directly, bypassing auto-detection that might reject it.
|
|
214
|
+
const detectionResult: ChartDetectionResult = {
|
|
215
|
+
chartable: true,
|
|
216
|
+
columns: headers.map((h, i) => ({
|
|
217
|
+
header: h,
|
|
218
|
+
type: i === 0 ? "categorical" as const : "numeric" as const,
|
|
219
|
+
index: i,
|
|
220
|
+
uniqueCount: i === 0 ? chart.data.length : 0,
|
|
221
|
+
})),
|
|
222
|
+
recommendations: [{
|
|
223
|
+
type: chart.type,
|
|
224
|
+
categoryColumn: { header: chart.categoryKey, type: "categorical" as const, index: 0, uniqueCount: chart.data.length },
|
|
225
|
+
valueColumns: chart.valueKeys.map((k, i) => ({
|
|
226
|
+
header: k,
|
|
227
|
+
type: "numeric" as const,
|
|
228
|
+
index: i + 1,
|
|
229
|
+
uniqueCount: 0,
|
|
230
|
+
})) as [{ header: string; type: "numeric"; index: number; uniqueCount: number }, ...{ header: string; type: "numeric"; index: number; uniqueCount: number }[]],
|
|
231
|
+
reason: "Python-generated chart",
|
|
232
|
+
}],
|
|
233
|
+
data: chart.data.map((row) => {
|
|
234
|
+
const out: Record<string, string | number> = {};
|
|
235
|
+
for (const key of headers) {
|
|
236
|
+
const val = row[key];
|
|
237
|
+
out[key] = typeof val === "number" ? val : String(val ?? "");
|
|
238
|
+
}
|
|
239
|
+
return out;
|
|
240
|
+
}),
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
return <ResultChart headers={headers} rows={rows} dark={dark} detectionResult={detectionResult} />;
|
|
244
|
+
}
|
|
@@ -1,27 +1,51 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useContext } from "react";
|
|
4
|
-
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
|
5
|
-
import { oneDark, oneLight } from "react-syntax-highlighter/dist/esm/styles/prism";
|
|
3
|
+
import { useContext, useState, useEffect } from "react";
|
|
6
4
|
import { DarkModeContext } from "../../hooks/use-dark-mode";
|
|
7
5
|
import { CopyButton } from "./copy-button";
|
|
8
6
|
|
|
7
|
+
type SyntaxHighlighterModule = typeof import("react-syntax-highlighter");
|
|
8
|
+
type StyleModule = typeof import("react-syntax-highlighter/dist/esm/styles/prism");
|
|
9
|
+
|
|
10
|
+
let _cache: { Prism: SyntaxHighlighterModule["Prism"]; oneDark: StyleModule["oneDark"]; oneLight: StyleModule["oneLight"] } | null = null;
|
|
11
|
+
|
|
12
|
+
const SQL_BLOCK_STYLE = {
|
|
13
|
+
margin: 0,
|
|
14
|
+
borderRadius: "0.5rem",
|
|
15
|
+
fontSize: "0.75rem",
|
|
16
|
+
padding: "0.75rem 1rem",
|
|
17
|
+
} as const;
|
|
18
|
+
|
|
9
19
|
export function SQLBlock({ sql }: { sql: string }) {
|
|
10
20
|
const dark = useContext(DarkModeContext);
|
|
21
|
+
const [mod, setMod] = useState(_cache);
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (_cache) return;
|
|
25
|
+
Promise.all([
|
|
26
|
+
import("react-syntax-highlighter"),
|
|
27
|
+
import("react-syntax-highlighter/dist/esm/styles/prism"),
|
|
28
|
+
]).then(([sh, styles]) => {
|
|
29
|
+
_cache = { Prism: sh.Prism, oneDark: styles.oneDark, oneLight: styles.oneLight };
|
|
30
|
+
setMod(_cache);
|
|
31
|
+
});
|
|
32
|
+
}, []);
|
|
33
|
+
|
|
11
34
|
return (
|
|
12
35
|
<div className="relative">
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
36
|
+
{mod ? (
|
|
37
|
+
<mod.Prism
|
|
38
|
+
language="sql"
|
|
39
|
+
style={dark ? mod.oneDark : mod.oneLight}
|
|
40
|
+
customStyle={SQL_BLOCK_STYLE}
|
|
41
|
+
>
|
|
42
|
+
{sql}
|
|
43
|
+
</mod.Prism>
|
|
44
|
+
) : (
|
|
45
|
+
<pre className="overflow-x-auto rounded-lg bg-zinc-100 p-3 text-xs dark:bg-zinc-800">
|
|
46
|
+
<code>{sql}</code>
|
|
47
|
+
</pre>
|
|
48
|
+
)}
|
|
25
49
|
<div className="absolute right-2 top-2">
|
|
26
50
|
<CopyButton text={sql} label="Copy SQL" />
|
|
27
51
|
</div>
|
|
@@ -4,7 +4,12 @@ import { useContext, useMemo, useState } from "react";
|
|
|
4
4
|
import { getToolArgs, getToolResult, isToolComplete, downloadCSV, toCsvString } from "../../lib/helpers";
|
|
5
5
|
import { DarkModeContext } from "../../hooks/use-dark-mode";
|
|
6
6
|
import { detectCharts } from "../chart/chart-detection";
|
|
7
|
-
import
|
|
7
|
+
import dynamic from "next/dynamic";
|
|
8
|
+
|
|
9
|
+
const ResultChart = dynamic(
|
|
10
|
+
() => import("../chart/result-chart").then((m) => ({ default: m.ResultChart })),
|
|
11
|
+
{ ssr: false, loading: () => <div className="h-64 animate-pulse rounded-lg bg-zinc-100 dark:bg-zinc-800" /> },
|
|
12
|
+
);
|
|
8
13
|
import { LoadingCard } from "./loading-card";
|
|
9
14
|
import { DataTable } from "./data-table";
|
|
10
15
|
import { SQLBlock } from "./sql-block";
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { memo } from "react";
|
|
3
4
|
import { getToolName } from "ai";
|
|
4
|
-
import { getToolResult } from "../../lib/helpers";
|
|
5
|
+
import { getToolResult, isToolComplete } from "../../lib/helpers";
|
|
5
6
|
import { isActionToolResult } from "../../lib/action-types";
|
|
6
7
|
import { ExploreCard } from "./explore-card";
|
|
7
8
|
import { SQLResultCard } from "./sql-result-card";
|
|
8
9
|
import { ActionApprovalCard } from "../actions/action-approval-card";
|
|
10
|
+
import { PythonResultCard } from "./python-result-card";
|
|
9
11
|
|
|
10
|
-
export function ToolPart({ part }: { part: unknown }) {
|
|
12
|
+
export const ToolPart = memo(function ToolPart({ part }: { part: unknown }) {
|
|
11
13
|
let name: string;
|
|
12
14
|
try {
|
|
13
15
|
name = getToolName(part as Parameters<typeof getToolName>[0]);
|
|
@@ -25,6 +27,8 @@ export function ToolPart({ part }: { part: unknown }) {
|
|
|
25
27
|
return <ExploreCard part={part} />;
|
|
26
28
|
case "executeSQL":
|
|
27
29
|
return <SQLResultCard part={part} />;
|
|
30
|
+
case "executePython":
|
|
31
|
+
return <PythonResultCard part={part} />;
|
|
28
32
|
default: {
|
|
29
33
|
const result = getToolResult(part);
|
|
30
34
|
if (isActionToolResult(result)) {
|
|
@@ -37,4 +41,9 @@ export function ToolPart({ part }: { part: unknown }) {
|
|
|
37
41
|
);
|
|
38
42
|
}
|
|
39
43
|
}
|
|
40
|
-
}
|
|
44
|
+
}, (prev, next) => {
|
|
45
|
+
// Once a tool part is complete, its output won't change — skip re-renders.
|
|
46
|
+
// This prevents the Recharts render tree from contributing to React's update depth limit.
|
|
47
|
+
if (isToolComplete(prev.part) && isToolComplete(next.part)) return true;
|
|
48
|
+
return false;
|
|
49
|
+
});
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
const DELAY_1 = { animationDelay: "150ms" } as const;
|
|
4
|
+
const DELAY_2 = { animationDelay: "300ms" } as const;
|
|
5
|
+
|
|
3
6
|
export function TypingIndicator() {
|
|
4
7
|
return (
|
|
5
8
|
<div className="flex justify-start">
|
|
@@ -7,11 +10,11 @@ export function TypingIndicator() {
|
|
|
7
10
|
<span className="h-1.5 w-1.5 animate-bounce rounded-full bg-zinc-400 dark:bg-zinc-500" />
|
|
8
11
|
<span
|
|
9
12
|
className="h-1.5 w-1.5 animate-bounce rounded-full bg-zinc-400 dark:bg-zinc-500"
|
|
10
|
-
style={
|
|
13
|
+
style={DELAY_1}
|
|
11
14
|
/>
|
|
12
15
|
<span
|
|
13
16
|
className="h-1.5 w-1.5 animate-bounce rounded-full bg-zinc-400 dark:bg-zinc-500"
|
|
14
|
-
style={
|
|
17
|
+
style={DELAY_2}
|
|
15
18
|
/>
|
|
16
19
|
</div>
|
|
17
20
|
</div>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useState } from "react";
|
|
4
|
+
import { Star, Trash2 } from "lucide-react";
|
|
5
|
+
import { Button } from "@/components/ui/button";
|
|
4
6
|
import type { Conversation } from "../../lib/types";
|
|
5
7
|
import { DeleteConfirmation } from "./delete-confirmation";
|
|
6
8
|
|
|
@@ -52,7 +54,6 @@ export function ConversationItem({
|
|
|
52
54
|
if (success) {
|
|
53
55
|
setConfirmDelete(false);
|
|
54
56
|
}
|
|
55
|
-
// On failure, keep confirmation dialog open so user sees something is wrong
|
|
56
57
|
}}
|
|
57
58
|
/>
|
|
58
59
|
</div>
|
|
@@ -60,9 +61,17 @@ export function ConversationItem({
|
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
return (
|
|
63
|
-
<
|
|
64
|
+
<div
|
|
65
|
+
role="button"
|
|
66
|
+
tabIndex={0}
|
|
64
67
|
onClick={onSelect}
|
|
65
|
-
|
|
68
|
+
onKeyDown={(e) => {
|
|
69
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
70
|
+
e.preventDefault();
|
|
71
|
+
onSelect();
|
|
72
|
+
}
|
|
73
|
+
}}
|
|
74
|
+
className={`group flex w-full cursor-pointer items-center gap-2 rounded-lg px-3 py-2 text-left text-sm transition-colors ${
|
|
66
75
|
isActive
|
|
67
76
|
? "bg-blue-50 text-blue-700 dark:bg-blue-600/10 dark:text-blue-400"
|
|
68
77
|
: "text-zinc-700 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-800"
|
|
@@ -77,7 +86,9 @@ export function ConversationItem({
|
|
|
77
86
|
</p>
|
|
78
87
|
</div>
|
|
79
88
|
<div className="flex shrink-0 items-center gap-0.5">
|
|
80
|
-
<
|
|
89
|
+
<Button
|
|
90
|
+
variant="ghost"
|
|
91
|
+
size="icon"
|
|
81
92
|
onClick={async (e) => {
|
|
82
93
|
e.stopPropagation();
|
|
83
94
|
if (starPending) return;
|
|
@@ -86,35 +97,29 @@ export function ConversationItem({
|
|
|
86
97
|
setStarPending(false);
|
|
87
98
|
}}
|
|
88
99
|
disabled={starPending}
|
|
89
|
-
className={`
|
|
100
|
+
className={`h-6 w-6 transition-all ${
|
|
90
101
|
conversation.starred
|
|
91
102
|
? "text-amber-400 opacity-100 hover:text-amber-500 dark:text-amber-400 dark:hover:text-amber-300"
|
|
92
103
|
: "text-zinc-400 opacity-0 hover:text-amber-400 group-hover:opacity-100 dark:hover:text-amber-400"
|
|
93
104
|
} ${starPending ? "opacity-50" : ""}`}
|
|
94
105
|
aria-label={conversation.starred ? "Unstar conversation" : "Star conversation"}
|
|
95
106
|
>
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
)}
|
|
102
|
-
</svg>
|
|
103
|
-
</button>
|
|
104
|
-
<button
|
|
107
|
+
<Star className="h-3.5 w-3.5" fill={conversation.starred ? "currentColor" : "none"} />
|
|
108
|
+
</Button>
|
|
109
|
+
<Button
|
|
110
|
+
variant="ghost"
|
|
111
|
+
size="icon"
|
|
105
112
|
onClick={(e) => {
|
|
106
113
|
e.stopPropagation();
|
|
107
114
|
setConfirmDelete(true);
|
|
108
115
|
}}
|
|
109
116
|
disabled={deleting}
|
|
110
|
-
className="
|
|
117
|
+
className="h-6 w-6 shrink-0 text-zinc-400 opacity-0 transition-all hover:bg-red-50 hover:text-red-500 group-hover:opacity-100 dark:hover:bg-red-950/20 dark:hover:text-red-400"
|
|
111
118
|
aria-label="Delete conversation"
|
|
112
119
|
>
|
|
113
|
-
<
|
|
114
|
-
|
|
115
|
-
</svg>
|
|
116
|
-
</button>
|
|
120
|
+
<Trash2 className="h-3.5 w-3.5" />
|
|
121
|
+
</Button>
|
|
117
122
|
</div>
|
|
118
|
-
</
|
|
123
|
+
</div>
|
|
119
124
|
);
|
|
120
125
|
}
|
|
@@ -14,7 +14,7 @@ export interface AtlasAuthClient {
|
|
|
14
14
|
email: (opts: { email: string; password: string; name: string }) => Promise<{ error?: { message?: string } | null }>;
|
|
15
15
|
};
|
|
16
16
|
signOut: () => Promise<unknown>;
|
|
17
|
-
useSession: () => { data?: { user?: { email?: string } } | null };
|
|
17
|
+
useSession: () => { data?: { user?: { email?: string } } | null; isPending?: boolean };
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export interface AtlasUIConfig {
|
|
@@ -64,12 +64,12 @@ export function useConversations(opts: UseConversationsOptions): UseConversation
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
if (!res.ok) {
|
|
67
|
-
const
|
|
68
|
-
if (
|
|
67
|
+
const errorBody = await res.json().catch(() => null);
|
|
68
|
+
if (errorBody?.code === "not_available") {
|
|
69
69
|
setAvailable(false);
|
|
70
70
|
return;
|
|
71
71
|
}
|
|
72
|
-
console.warn(`fetchList: HTTP ${res.status}`,
|
|
72
|
+
console.warn(`fetchList: HTTP ${res.status}`, errorBody);
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { createContext,
|
|
3
|
+
import { createContext, useSyncExternalStore } from "react";
|
|
4
4
|
|
|
5
5
|
export const DarkModeContext = createContext(false);
|
|
6
6
|
|
|
7
|
+
/** SSR-safe snapshot: reads current prefers-color-scheme without hydration flicker. */
|
|
8
|
+
function subscribeToColorScheme(onChange: () => void) {
|
|
9
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
10
|
+
mq.addEventListener("change", onChange);
|
|
11
|
+
return () => mq.removeEventListener("change", onChange);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getSnapshot() {
|
|
15
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getServerSnapshot() {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
7
22
|
export function useDarkMode() {
|
|
8
|
-
|
|
9
|
-
useEffect(() => {
|
|
10
|
-
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
11
|
-
setDark(mq.matches);
|
|
12
|
-
const handler = (e: MediaQueryListEvent) => setDark(e.matches);
|
|
13
|
-
mq.addEventListener("change", handler);
|
|
14
|
-
return () => mq.removeEventListener("change", handler);
|
|
15
|
-
}, []);
|
|
16
|
-
return dark;
|
|
23
|
+
return useSyncExternalStore(subscribeToColorScheme, getSnapshot, getServerSnapshot);
|
|
17
24
|
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# Render Blueprint — deploy Atlas with "New > Blueprint" pointing at this repo.
|
|
2
|
-
# https://render.com/docs/infrastructure-as-code
|
|
3
|
-
|
|
4
|
-
services:
|
|
5
|
-
- type: web
|
|
6
|
-
name: %PROJECT_NAME%
|
|
7
|
-
runtime: docker
|
|
8
|
-
dockerfilePath: ./Dockerfile
|
|
9
|
-
plan: starter
|
|
10
|
-
autoDeploy: false
|
|
11
|
-
healthCheckPath: /api/health
|
|
12
|
-
healthCheckTimeout: 15
|
|
13
|
-
envVars:
|
|
14
|
-
- key: ATLAS_DATASOURCE_URL
|
|
15
|
-
sync: false
|
|
16
|
-
- key: ATLAS_PROVIDER
|
|
17
|
-
value: anthropic
|
|
18
|
-
- key: ANTHROPIC_API_KEY
|
|
19
|
-
sync: false
|
|
20
|
-
- key: ATLAS_SANDBOX_URL
|
|
21
|
-
value: # Set to sidecar's internal URL after deploy
|
|
22
|
-
- key: SIDECAR_AUTH_TOKEN
|
|
23
|
-
sync: false
|
|
24
|
-
|
|
25
|
-
- type: worker
|
|
26
|
-
name: %PROJECT_NAME%-sidecar
|
|
27
|
-
runtime: docker
|
|
28
|
-
dockerfilePath: ./sidecar/Dockerfile
|
|
29
|
-
dockerContext: .
|
|
30
|
-
plan: starter
|
|
31
|
-
autoDeploy: false
|
|
32
|
-
envVars:
|
|
33
|
-
- key: SIDECAR_AUTH_TOKEN
|
|
34
|
-
sync: false
|
|
File without changes
|
|
File without changes
|