larkup 0.1.14
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/.env +2 -0
- package/app/actions/vercel.ts +417 -0
- package/app/api/chat/route.ts +192 -0
- package/app/api/chat/status/route.ts +55 -0
- package/app/api/chat/suggestions/route.ts +115 -0
- package/app/api/config/route.ts +73 -0
- package/app/api/config/test/route.ts +67 -0
- package/app/api/config/test-embedding/route.ts +40 -0
- package/app/api/config/test-llm/route.ts +47 -0
- package/app/api/config/test-provider/route.ts +36 -0
- package/app/api/demo/route.ts +176 -0
- package/app/api/deploy/ssh/route.ts +340 -0
- package/app/api/documents/route.ts +90 -0
- package/app/api/firecrawl/local/route.ts +46 -0
- package/app/api/index/route.ts +113 -0
- package/app/api/jobs/[id]/route.ts +44 -0
- package/app/api/jobs/route.ts +116 -0
- package/app/api/parse-file/route.ts +39 -0
- package/app/api/search/google/route.ts +162 -0
- package/app/api/search/route.ts +29 -0
- package/app/api/server/download/route.ts +112 -0
- package/app/api/server/generate/route.ts +46 -0
- package/app/api/server/local/route.ts +42 -0
- package/app/api/servers/route.ts +137 -0
- package/app/api/vector-stores/install/route.ts +65 -0
- package/app/api/vector-stores/status/route.ts +26 -0
- package/app/chat/page.tsx +15 -0
- package/app/configure/page.tsx +53 -0
- package/app/data/page.tsx +15 -0
- package/app/demo/page.tsx +15 -0
- package/app/favicon.ico +0 -0
- package/app/globals.css +190 -0
- package/app/index-data/page.tsx +15 -0
- package/app/layout.tsx +51 -0
- package/app/page.tsx +34 -0
- package/app/server/page.tsx +22 -0
- package/app/simple/chat/page.tsx +17 -0
- package/app/simple/docs/page.tsx +21 -0
- package/app/simple/layout.tsx +9 -0
- package/app/simple/settings/page.tsx +627 -0
- package/app/template.tsx +16 -0
- package/app/themes.css +733 -0
- package/components/alerts/generic-alert.tsx +74 -0
- package/components/app-sidebar.tsx +187 -0
- package/components/app-topnav.tsx +195 -0
- package/components/chat/chat-settings-modal.tsx +209 -0
- package/components/chat/chat-workspace.tsx +348 -0
- package/components/chat/knowledge-base-result.tsx +97 -0
- package/components/chat/message-item.tsx +103 -0
- package/components/client-layout-wrapper.tsx +182 -0
- package/components/configure/configure-form.tsx +1683 -0
- package/components/configure/store-fields.tsx +152 -0
- package/components/data/corpus-panel.tsx +499 -0
- package/components/data/data-workspace.tsx +272 -0
- package/components/data/firecrawl-notice.tsx +236 -0
- package/components/data/jobs-panel.tsx +485 -0
- package/components/data/paste-panel.tsx +79 -0
- package/components/data/scrape-panel.tsx +964 -0
- package/components/data/upload-panel.tsx +714 -0
- package/components/demo/demo-workspace.tsx +419 -0
- package/components/global-settings.tsx +286 -0
- package/components/index/index-workspace.tsx +482 -0
- package/components/onboarding/simple-setup.tsx +702 -0
- package/components/onboarding/tech-setup.tsx +121 -0
- package/components/onboarding/welcome-screen.tsx +183 -0
- package/components/page-header.tsx +44 -0
- package/components/server/code-viewer.tsx +87 -0
- package/components/server/deploy-button.tsx +1390 -0
- package/components/server/server-workspace.tsx +392 -0
- package/components/simple/sdk-connect-dialog.tsx +201 -0
- package/components/simple/simple-index-button.tsx +83 -0
- package/components/simple/simple-sidebar.tsx +161 -0
- package/components/theme-customizer-provider.tsx +171 -0
- package/components/theme-switcher.tsx +445 -0
- package/components/ui/alert-dialog.tsx +187 -0
- package/components/ui/alert.tsx +76 -0
- package/components/ui/badge.tsx +52 -0
- package/components/ui/button.tsx +59 -0
- package/components/ui/card.tsx +103 -0
- package/components/ui/checkbox.tsx +28 -0
- package/components/ui/collapsible.tsx +21 -0
- package/components/ui/dialog.tsx +159 -0
- package/components/ui/dropdown-menu.tsx +281 -0
- package/components/ui/input.tsx +22 -0
- package/components/ui/label.tsx +20 -0
- package/components/ui/popover.tsx +90 -0
- package/components/ui/progress.tsx +83 -0
- package/components/ui/provider-icon.tsx +94 -0
- package/components/ui/scroll-area.tsx +55 -0
- package/components/ui/select.tsx +248 -0
- package/components/ui/separator.tsx +25 -0
- package/components/ui/sheet.tsx +138 -0
- package/components/ui/skeleton.tsx +13 -0
- package/components/ui/sonner.tsx +63 -0
- package/components/ui/switch.tsx +32 -0
- package/components/ui/table.tsx +116 -0
- package/components/ui/tabs.tsx +84 -0
- package/components/ui/textarea.tsx +18 -0
- package/components/ui/tooltip.tsx +66 -0
- package/components/workspace/delete-server-dialog.tsx +71 -0
- package/components/workspace/server-form-dialog.tsx +103 -0
- package/components/workspace/server-switcher.tsx +163 -0
- package/components/workspace/workspace-provider.tsx +177 -0
- package/components/workspace/workspace-top-bar.tsx +23 -0
- package/components.json +21 -0
- package/lib/error-formatter.ts +15 -0
- package/lib/utils.ts +6 -0
- package/next-env.d.ts +6 -0
- package/next.config.mjs +18 -0
- package/package.json +65 -0
- package/postcss.config.mjs +8 -0
- package/public/docker.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/github.svg +3 -0
- package/public/hetzner.svg +1 -0
- package/public/icons/aws.svg +38 -0
- package/public/icons/azure.svg +1 -0
- package/public/icons/chroma.png +0 -0
- package/public/icons/cohere.svg +30 -0
- package/public/icons/digital-ocean.webp +0 -0
- package/public/icons/firecrawl2.png +0 -0
- package/public/icons/gcp.svg +1 -0
- package/public/icons/gemini.svg +1 -0
- package/public/icons/jina.svg +1 -0
- package/public/icons/lancedb2.png +0 -0
- package/public/icons/mistral.svg +469 -0
- package/public/icons/nomic.png +0 -0
- package/public/icons/openai.svg +1 -0
- package/public/icons/pgvector2.png +0 -0
- package/public/icons/pinecone.png +0 -0
- package/public/icons/qdrant.svg +35 -0
- package/public/icons/supabase.png +0 -0
- package/public/icons/vercel copy.svg +1 -0
- package/public/icons/vercel.svg +1 -0
- package/public/icons/voyage-light.png +0 -0
- package/public/icons/weaviate.webp +0 -0
- package/public/logo-light.png +0 -0
- package/public/logo.png +0 -0
- package/public/logo9.png +0 -0
- package/public/vercel.svg +1 -0
- package/tsconfig.json +33 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { exec } from "node:child_process";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
|
|
5
|
+
const execAsync = promisify(exec);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* POST /api/vector-stores/install
|
|
9
|
+
*
|
|
10
|
+
* Install an optional vector store package on demand.
|
|
11
|
+
* Only works in local development — not on serverless (Vercel) production.
|
|
12
|
+
*
|
|
13
|
+
* Body: { "storeId": "chroma" }
|
|
14
|
+
*/
|
|
15
|
+
export async function POST(request: Request) {
|
|
16
|
+
// Block in production — pnpm add can't run on Vercel
|
|
17
|
+
if (process.env.NODE_ENV === "production") {
|
|
18
|
+
return NextResponse.json(
|
|
19
|
+
{
|
|
20
|
+
error:
|
|
21
|
+
'Package installation is only available in local development. For production, add "chromadb" to your dependencies before deploying.',
|
|
22
|
+
},
|
|
23
|
+
{ status: 403 },
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const body = await request.json();
|
|
28
|
+
const { storeId } = body;
|
|
29
|
+
|
|
30
|
+
// Only Chroma is installable for now
|
|
31
|
+
const INSTALLABLE: Record<string, { pkg: string; version: string }> = {
|
|
32
|
+
chroma: { pkg: "chromadb", version: "^1.9.0" },
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const entry = INSTALLABLE[storeId];
|
|
36
|
+
if (!entry) {
|
|
37
|
+
return NextResponse.json(
|
|
38
|
+
{ error: `Store "${storeId}" is not installable or not recognized.` },
|
|
39
|
+
{ status: 400 },
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const cmd = `pnpm add ${entry.pkg}@${entry.version} --filter @larkup/vector-stores`;
|
|
45
|
+
const { stdout, stderr } = await execAsync(cmd, {
|
|
46
|
+
cwd: process.cwd(),
|
|
47
|
+
timeout: 120_000, // 2 minute timeout
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return NextResponse.json({
|
|
51
|
+
success: true,
|
|
52
|
+
package: entry.pkg,
|
|
53
|
+
version: entry.version,
|
|
54
|
+
output: stdout || stderr,
|
|
55
|
+
});
|
|
56
|
+
} catch (err: any) {
|
|
57
|
+
return NextResponse.json(
|
|
58
|
+
{
|
|
59
|
+
error: `Failed to install ${entry.pkg}: ${err.message}`,
|
|
60
|
+
output: err.stderr || err.stdout,
|
|
61
|
+
},
|
|
62
|
+
{ status: 500 },
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GET /api/vector-stores/status
|
|
5
|
+
*
|
|
6
|
+
* Checks whether optional vector store packages are importable.
|
|
7
|
+
* Returns a map of storeId → { installed: boolean }.
|
|
8
|
+
*/
|
|
9
|
+
export async function GET() {
|
|
10
|
+
const status: Record<string, { installed: boolean }> = {};
|
|
11
|
+
|
|
12
|
+
// Check chromadb by reading the vector-stores package.json
|
|
13
|
+
// Dynamic import caching in Next.js might fail even after installation.
|
|
14
|
+
try {
|
|
15
|
+
const fs = await import("fs");
|
|
16
|
+
const path = await import("path");
|
|
17
|
+
const pkgPath = path.join(process.cwd(), "../../packages/vector-stores/package.json");
|
|
18
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
19
|
+
const hasChroma = pkg.dependencies?.chromadb || pkg.devDependencies?.chromadb || pkg.optionalDependencies?.chromadb;
|
|
20
|
+
status.chroma = { installed: !!hasChroma };
|
|
21
|
+
} catch {
|
|
22
|
+
status.chroma = { installed: false };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return NextResponse.json({ stores: status });
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PageHeader } from "@/components/page-header";
|
|
2
|
+
import { ChatWorkspace } from "@/components/chat/chat-workspace";
|
|
3
|
+
|
|
4
|
+
export default function ChatPage() {
|
|
5
|
+
return (
|
|
6
|
+
<div className="flex flex-col overflow-hidden" style={{ height: "100%" }}>
|
|
7
|
+
<PageHeader
|
|
8
|
+
eyebrow="Step 6 · Chat"
|
|
9
|
+
title="Chat with your knowledge base"
|
|
10
|
+
className="gap-0 pb-0"
|
|
11
|
+
/>
|
|
12
|
+
<ChatWorkspace />
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback } from "react";
|
|
4
|
+
import { PageHeader } from "@/components/page-header";
|
|
5
|
+
import {
|
|
6
|
+
ConfigureForm,
|
|
7
|
+
type ConfigureFormHandle,
|
|
8
|
+
} from "@/components/configure/configure-form";
|
|
9
|
+
import { Button } from "@/components/ui/button";
|
|
10
|
+
import { Loader2, Save, Check } from "lucide-react";
|
|
11
|
+
|
|
12
|
+
export default function ConfigurePage() {
|
|
13
|
+
const [handle, setHandle] = useState<ConfigureFormHandle | null>(null);
|
|
14
|
+
|
|
15
|
+
const onHandleReady = useCallback((h: ConfigureFormHandle) => {
|
|
16
|
+
setHandle({ ...h });
|
|
17
|
+
}, []);
|
|
18
|
+
|
|
19
|
+
const dirty = handle?.dirty ?? false;
|
|
20
|
+
const saving = handle?.saving ?? false;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div className="flex min-h-full flex-col">
|
|
24
|
+
<PageHeader
|
|
25
|
+
eyebrow="Step 1 · Configure"
|
|
26
|
+
title="Pipeline configuration"
|
|
27
|
+
description="Choose your embedding model, indexing strategy, and vector store. "
|
|
28
|
+
actions={
|
|
29
|
+
<Button
|
|
30
|
+
size="lg"
|
|
31
|
+
variant={dirty ? "default" : "default"}
|
|
32
|
+
disabled={saving || !dirty}
|
|
33
|
+
onClick={() => handle?.requestSave()}
|
|
34
|
+
className="min-w-[110px] px-3.5 h-9.5 rounded-md transition-all text-white/90 text-[13px]"
|
|
35
|
+
>
|
|
36
|
+
{saving ? (
|
|
37
|
+
<>
|
|
38
|
+
<Loader2 className="size-4 animate-spin" />
|
|
39
|
+
Saving…
|
|
40
|
+
</>
|
|
41
|
+
) : (
|
|
42
|
+
<>
|
|
43
|
+
<Save className="size-4" />
|
|
44
|
+
Save Configuration
|
|
45
|
+
</>
|
|
46
|
+
)}
|
|
47
|
+
</Button>
|
|
48
|
+
}
|
|
49
|
+
/>
|
|
50
|
+
<ConfigureForm onHandleReady={onHandleReady} />
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PageHeader } from "@/components/page-header";
|
|
2
|
+
import { DataWorkspace } from "@/components/data/data-workspace";
|
|
3
|
+
|
|
4
|
+
export default function DataPage() {
|
|
5
|
+
return (
|
|
6
|
+
<div className="flex min-h-full flex-col">
|
|
7
|
+
<PageHeader
|
|
8
|
+
eyebrow="Step 2 · Data"
|
|
9
|
+
title="Load & scrape data"
|
|
10
|
+
description="Manage your corpus and ETL jobs. Load new data via web scraping, pasting, or uploading files."
|
|
11
|
+
/>
|
|
12
|
+
<DataWorkspace />
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PageHeader } from "@/components/page-header"
|
|
2
|
+
import { DemoWorkspace } from "@/components/demo/demo-workspace"
|
|
3
|
+
|
|
4
|
+
export default function DemoPage() {
|
|
5
|
+
return (
|
|
6
|
+
<div className="flex min-h-full flex-col">
|
|
7
|
+
<PageHeader
|
|
8
|
+
eyebrow="Step 5 · Demo"
|
|
9
|
+
title="Query your RAG pipeline"
|
|
10
|
+
description="Send a test question and inspect the top-k documents your pipeline returns. If a generated server is running, the query hits it directly; otherwise it runs in-process against your store."
|
|
11
|
+
/>
|
|
12
|
+
<DemoWorkspace />
|
|
13
|
+
</div>
|
|
14
|
+
)
|
|
15
|
+
}
|
package/app/favicon.ico
ADDED
|
Binary file
|
package/app/globals.css
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@import "tw-animate-css";
|
|
3
|
+
@import "shadcn/tailwind.css";
|
|
4
|
+
@import "./themes.css";
|
|
5
|
+
|
|
6
|
+
@theme inline {
|
|
7
|
+
--font-heading: var(--font-sans);
|
|
8
|
+
--font-sans: var(--font-geist-sans), "Geist Fallback";
|
|
9
|
+
--font-mono: var(--font-geist-mono), "Geist Mono Fallback";
|
|
10
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
11
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
12
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
13
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
14
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
15
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
16
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
17
|
+
--color-sidebar: var(--sidebar);
|
|
18
|
+
--color-chart-5: var(--chart-5);
|
|
19
|
+
--color-chart-4: var(--chart-4);
|
|
20
|
+
--color-chart-3: var(--chart-3);
|
|
21
|
+
--color-chart-2: var(--chart-2);
|
|
22
|
+
--color-chart-1: var(--chart-1);
|
|
23
|
+
--color-ring: var(--ring);
|
|
24
|
+
--color-input: var(--input);
|
|
25
|
+
--color-border: var(--border);
|
|
26
|
+
--color-destructive: var(--destructive);
|
|
27
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
28
|
+
--color-accent: var(--accent);
|
|
29
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
30
|
+
--color-muted: var(--muted);
|
|
31
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
32
|
+
--color-secondary: var(--secondary);
|
|
33
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
34
|
+
--color-primary: var(--primary);
|
|
35
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
36
|
+
--color-popover: var(--popover);
|
|
37
|
+
--color-card-foreground: var(--card-foreground);
|
|
38
|
+
--color-card: var(--card);
|
|
39
|
+
--color-panel: var(--panel);
|
|
40
|
+
--color-panel-foreground: var(--panel-foreground);
|
|
41
|
+
--color-foreground: var(--foreground);
|
|
42
|
+
--color-background: var(--background);
|
|
43
|
+
--radius-sm: calc(var(--radius) * 0.6);
|
|
44
|
+
--radius-md: calc(var(--radius) * 0.8);
|
|
45
|
+
--radius-lg: var(--radius);
|
|
46
|
+
--radius-xl: calc(var(--radius) * 1.4);
|
|
47
|
+
--radius-2xl: calc(var(--radius) * 1.8);
|
|
48
|
+
--radius-3xl: calc(var(--radius) * 2.2);
|
|
49
|
+
--radius-4xl: calc(var(--radius) * 2.6);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
:root {
|
|
53
|
+
--background: #FAFAFA;
|
|
54
|
+
--foreground: #171717;
|
|
55
|
+
--card: #FFFFFF;
|
|
56
|
+
--card-foreground: #171717;
|
|
57
|
+
--panel: #F4F4F4;
|
|
58
|
+
--panel-foreground: #171717;
|
|
59
|
+
--popover: #FFFFFF;
|
|
60
|
+
--popover-foreground: #171717;
|
|
61
|
+
--primary: #000000;
|
|
62
|
+
--primary-foreground: #ffffff;
|
|
63
|
+
--secondary: #F5F5F5;
|
|
64
|
+
--secondary-foreground: #000000;
|
|
65
|
+
--muted: #F5F5F5;
|
|
66
|
+
--muted-foreground: #737373;
|
|
67
|
+
--accent: #F5F5F5;
|
|
68
|
+
--accent-foreground: #000000;
|
|
69
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
70
|
+
--destructive-foreground: oklch(0.577 0.245 27.325);
|
|
71
|
+
--border: #E5E5E5;
|
|
72
|
+
--input: #E5E5E5;
|
|
73
|
+
--ring: #000000;
|
|
74
|
+
--sidebar: #F4F4F4;
|
|
75
|
+
--sidebar-foreground: #525252;
|
|
76
|
+
--sidebar-primary: #000000;
|
|
77
|
+
--sidebar-primary-foreground: #ffffff;
|
|
78
|
+
--sidebar-border: #E5E5E5;
|
|
79
|
+
--sidebar-accent: #F5F5F5;
|
|
80
|
+
--sidebar-accent-foreground: #000000;
|
|
81
|
+
--sidebar-ring: #000000;
|
|
82
|
+
--radius: 0.625rem;
|
|
83
|
+
--chart-1: #6e56cf;
|
|
84
|
+
--chart-2: oklch(0.6 0.13 220);
|
|
85
|
+
--chart-3: oklch(0.68 0.15 145);
|
|
86
|
+
--chart-4: oklch(0.7 0.16 60);
|
|
87
|
+
--chart-5: oklch(0.62 0.2 25);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.dark {
|
|
91
|
+
--background: #0A0A0A;
|
|
92
|
+
--foreground: #FAFAFA;
|
|
93
|
+
--card: #1C1C1C;
|
|
94
|
+
--card-foreground: #FAFAFA;
|
|
95
|
+
--panel: #141414;
|
|
96
|
+
--panel-foreground: #FAFAFA;
|
|
97
|
+
--popover: #1C1C1C;
|
|
98
|
+
--popover-foreground: #FAFAFA;
|
|
99
|
+
--primary: #FFFFFF;
|
|
100
|
+
--primary-foreground: #000000;
|
|
101
|
+
--secondary: #1A1A1A;
|
|
102
|
+
--secondary-foreground: #FAFAFA;
|
|
103
|
+
--muted: #1A1A1A;
|
|
104
|
+
--muted-foreground: #A1A1A1;
|
|
105
|
+
--accent: #1A1A1A;
|
|
106
|
+
--accent-foreground: #FAFAFA;
|
|
107
|
+
--border: #2E2E2E;
|
|
108
|
+
--input: #2E2E2E;
|
|
109
|
+
--ring: #FFFFFF;
|
|
110
|
+
--sidebar: #141414;
|
|
111
|
+
--sidebar-foreground: #A1A1A1;
|
|
112
|
+
--sidebar-primary: #FFFFFF;
|
|
113
|
+
--sidebar-primary-foreground: #000000;
|
|
114
|
+
--sidebar-border: #2E2E2E;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@layer base {
|
|
118
|
+
* {
|
|
119
|
+
@apply border-border outline-ring/50;
|
|
120
|
+
}
|
|
121
|
+
body {
|
|
122
|
+
@apply bg-background text-foreground;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
html {
|
|
126
|
+
@apply font-sans;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* Darker, thinner scrollbar for dropdown/select popups. */
|
|
131
|
+
@layer base {
|
|
132
|
+
[data-slot="dropdown-menu-content"],
|
|
133
|
+
[data-slot="select-content"] {
|
|
134
|
+
scrollbar-width: thin;
|
|
135
|
+
scrollbar-color: var(--muted-foreground) transparent;
|
|
136
|
+
}
|
|
137
|
+
[data-slot="dropdown-menu-content"]::-webkit-scrollbar,
|
|
138
|
+
[data-slot="select-content"]::-webkit-scrollbar {
|
|
139
|
+
width: 6px;
|
|
140
|
+
height: 6px;
|
|
141
|
+
}
|
|
142
|
+
[data-slot="dropdown-menu-content"]::-webkit-scrollbar-track,
|
|
143
|
+
[data-slot="select-content"]::-webkit-scrollbar-track {
|
|
144
|
+
background: transparent;
|
|
145
|
+
}
|
|
146
|
+
[data-slot="dropdown-menu-content"]::-webkit-scrollbar-thumb,
|
|
147
|
+
[data-slot="select-content"]::-webkit-scrollbar-thumb {
|
|
148
|
+
border-radius: 9999px;
|
|
149
|
+
background-color: color-mix(in oklch, var(--foreground) 45%, transparent);
|
|
150
|
+
}
|
|
151
|
+
[data-slot="dropdown-menu-content"]::-webkit-scrollbar-thumb:hover,
|
|
152
|
+
[data-slot="select-content"]::-webkit-scrollbar-thumb:hover {
|
|
153
|
+
background-color: color-mix(in oklch, var(--foreground) 65%, transparent);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* ── Mesh gradient + noise utility (same as landing) ── */
|
|
158
|
+
|
|
159
|
+
/* Apply on the layout root div to get the mesh gradient backdrop */
|
|
160
|
+
.bg-mesh-light {
|
|
161
|
+
position: relative;
|
|
162
|
+
background-image:
|
|
163
|
+
radial-gradient(
|
|
164
|
+
ellipse 55% 45% at 8% 4%,
|
|
165
|
+
color-mix(in oklch, var(--primary) 6%, transparent),
|
|
166
|
+
transparent 70%
|
|
167
|
+
),
|
|
168
|
+
radial-gradient(
|
|
169
|
+
ellipse 45% 40% at 94% 92%,
|
|
170
|
+
color-mix(in oklch, oklch(0.62 0.08 30) 5%, transparent),
|
|
171
|
+
transparent 65%
|
|
172
|
+
),
|
|
173
|
+
radial-gradient(
|
|
174
|
+
ellipse 70% 55% at 50% 50%,
|
|
175
|
+
color-mix(in oklch, var(--primary) 2%, transparent),
|
|
176
|
+
transparent 80%
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.dark .bg-mesh-light {
|
|
181
|
+
background-image: radial-gradient(
|
|
182
|
+
circle at 20% 0%,
|
|
183
|
+
color-mix(in oklch, var(--primary) 8%, transparent),
|
|
184
|
+
transparent 40%
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
[data-sonner-toaster] {
|
|
189
|
+
z-index: 999999 !important;
|
|
190
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PageHeader } from "@/components/page-header"
|
|
2
|
+
import { IndexWorkspace } from "@/components/index/index-workspace"
|
|
3
|
+
|
|
4
|
+
export default function IndexPage() {
|
|
5
|
+
return (
|
|
6
|
+
<div className="flex min-h-full flex-col">
|
|
7
|
+
<PageHeader
|
|
8
|
+
eyebrow="Step 3 · Index"
|
|
9
|
+
title="Chunk, embed & store"
|
|
10
|
+
description="Split your corpus into chunks, embed them with your selected model, and write the vectors into your vector store. Progress streams live as the run works through the pipeline."
|
|
11
|
+
/>
|
|
12
|
+
<IndexWorkspace />
|
|
13
|
+
</div>
|
|
14
|
+
)
|
|
15
|
+
}
|
package/app/layout.tsx
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Analytics } from "@vercel/analytics/next";
|
|
2
|
+
import type { Metadata } from "next";
|
|
3
|
+
import { Geist, Geist_Mono } from "next/font/google";
|
|
4
|
+
import "./globals.css";
|
|
5
|
+
import { AppSidebar } from "@/components/app-sidebar";
|
|
6
|
+
import { Toaster } from "@/components/ui/sonner";
|
|
7
|
+
import { WorkspaceProvider } from "@/components/workspace/workspace-provider";
|
|
8
|
+
import { WorkspaceTopBar } from "@/components/workspace/workspace-top-bar";
|
|
9
|
+
import { ThemeCustomizerProvider } from "@/components/theme-customizer-provider";
|
|
10
|
+
import { ClientLayoutWrapper } from "@/components/client-layout-wrapper";
|
|
11
|
+
|
|
12
|
+
const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"] });
|
|
13
|
+
const geistMono = Geist_Mono({
|
|
14
|
+
variable: "--font-geist-mono",
|
|
15
|
+
subsets: ["latin"],
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const metadata: Metadata = {
|
|
19
|
+
title: "larkup",
|
|
20
|
+
description:
|
|
21
|
+
"larkup — a dual-mode (Web + CLI) toolkit to build, index, and serve a lightweight, deployable RAG pipeline.",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default function RootLayout({
|
|
25
|
+
children,
|
|
26
|
+
}: Readonly<{
|
|
27
|
+
children: React.ReactNode;
|
|
28
|
+
}>) {
|
|
29
|
+
return (
|
|
30
|
+
<html
|
|
31
|
+
lang="en"
|
|
32
|
+
suppressHydrationWarning
|
|
33
|
+
className={`${geistSans.variable} ${geistMono.variable} bg-transparent`}
|
|
34
|
+
>
|
|
35
|
+
<body suppressHydrationWarning className="font-sans antialiased ">
|
|
36
|
+
<script
|
|
37
|
+
dangerouslySetInnerHTML={{
|
|
38
|
+
__html: `(function(){try{var t=localStorage.getItem('app-theme');var b=localStorage.getItem('app-background');var r=localStorage.getItem('app-radius');var d=document.body;if(t&&t!=='default')d.classList.add(t);if(b)d.classList.add(b);if(r&&r!=='radius-default')d.classList.add(r);}catch(e){}})();`,
|
|
39
|
+
}}
|
|
40
|
+
/>
|
|
41
|
+
<ThemeCustomizerProvider>
|
|
42
|
+
<WorkspaceProvider>
|
|
43
|
+
<ClientLayoutWrapper>{children}</ClientLayoutWrapper>
|
|
44
|
+
<Toaster position="bottom-right" />
|
|
45
|
+
</WorkspaceProvider>
|
|
46
|
+
</ThemeCustomizerProvider>
|
|
47
|
+
{process.env.NODE_ENV === "production" && <Analytics />}
|
|
48
|
+
</body>
|
|
49
|
+
</html>
|
|
50
|
+
);
|
|
51
|
+
}
|
package/app/page.tsx
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useEffect } from "react"
|
|
4
|
+
import { useRouter } from "next/navigation"
|
|
5
|
+
import { useWorkspace } from "@/components/workspace/workspace-provider"
|
|
6
|
+
import { Loader2 } from "lucide-react"
|
|
7
|
+
|
|
8
|
+
export default function Page() {
|
|
9
|
+
const router = useRouter()
|
|
10
|
+
const { mode, isLoading, isFirstRun } = useWorkspace()
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (isLoading) return
|
|
14
|
+
// If it's first run, the ClientLayoutWrapper will show the welcome screen
|
|
15
|
+
if (isFirstRun || !mode) return
|
|
16
|
+
|
|
17
|
+
if (mode === "simple") {
|
|
18
|
+
router.replace("/simple/chat")
|
|
19
|
+
} else {
|
|
20
|
+
router.replace("/configure")
|
|
21
|
+
}
|
|
22
|
+
}, [mode, isLoading, isFirstRun, router])
|
|
23
|
+
|
|
24
|
+
// Show a minimal loading state while determining the redirect
|
|
25
|
+
if (isLoading) {
|
|
26
|
+
return (
|
|
27
|
+
<div className="flex h-[50vh] items-center justify-center">
|
|
28
|
+
<Loader2 className="size-6 animate-spin text-muted-foreground" />
|
|
29
|
+
</div>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { PageHeader } from "@/components/page-header";
|
|
5
|
+
import { ServerWorkspace } from "@/components/server/server-workspace";
|
|
6
|
+
import { DeployButton } from "@/components/server/deploy-button";
|
|
7
|
+
|
|
8
|
+
export default function ServerPage() {
|
|
9
|
+
const [serverId, setServerId] = useState<string>("default");
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div className="flex min-h-full flex-col">
|
|
13
|
+
<PageHeader
|
|
14
|
+
eyebrow="Step 4 · Server"
|
|
15
|
+
title="Generate & launch your RAG server"
|
|
16
|
+
description="Emit a standalone, deployable RAG server tailored to your selected vector store"
|
|
17
|
+
actions={<DeployButton serverId={serverId} />}
|
|
18
|
+
/>
|
|
19
|
+
<ServerWorkspace onServerId={setServerId} />
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ChatWorkspace } from "@/components/chat/chat-workspace"
|
|
2
|
+
|
|
3
|
+
export default function SimpleChatPage() {
|
|
4
|
+
return (
|
|
5
|
+
<div className="flex flex-col overflow-hidden" style={{ height: "100%" }}>
|
|
6
|
+
<div className="px-6 pt-5 pb-1 md:px-8">
|
|
7
|
+
<h1 className="text-lg font-semibold tracking-tight text-foreground">
|
|
8
|
+
Chat
|
|
9
|
+
</h1>
|
|
10
|
+
<p className="text-sm text-muted-foreground">
|
|
11
|
+
Ask questions and get AI-powered answers from your documents.
|
|
12
|
+
</p>
|
|
13
|
+
</div>
|
|
14
|
+
<ChatWorkspace />
|
|
15
|
+
</div>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { DataWorkspace } from "@/components/data/data-workspace"
|
|
2
|
+
import { SimpleIndexButton } from "@/components/simple/simple-index-button"
|
|
3
|
+
|
|
4
|
+
export default function SimpleDocsPage() {
|
|
5
|
+
return (
|
|
6
|
+
<div className="flex min-h-full flex-col">
|
|
7
|
+
<div className="flex items-center justify-between px-6 pt-5 pb-4 md:px-8">
|
|
8
|
+
<div>
|
|
9
|
+
<h1 className="text-lg font-semibold tracking-tight text-foreground">
|
|
10
|
+
Documents
|
|
11
|
+
</h1>
|
|
12
|
+
<p className="text-sm text-muted-foreground">
|
|
13
|
+
Upload files, paste text, or scrape the web to build your knowledge base.
|
|
14
|
+
</p>
|
|
15
|
+
</div>
|
|
16
|
+
<SimpleIndexButton />
|
|
17
|
+
</div>
|
|
18
|
+
<DataWorkspace />
|
|
19
|
+
</div>
|
|
20
|
+
)
|
|
21
|
+
}
|