History

pretty ui

Rhys Sullivan committed 7 months ago

Commit cb0197f1 parent 2d91da111 files · +655 −160Browse files

Unified · Split

11 files
  1. apps/web/index.html +3 −0
  2. apps/web/package.json +2 −0
  3. apps/web/src/main.tsx +1 −0
  4. apps/web/src/pages/secrets.tsx +41 −21
  5. apps/web/src/pages/sources.tsx +51 −23
  6. apps/web/src/pages/tools.tsx +59 −21
  7. apps/web/src/shell.tsx +134 −24
  8. apps/web/src/styles.css +128 −0
  9. apps/web/vite.config.ts +2 −0
  10. bun.lock +78 −0
  11. packages/plugins/openapi/src/react/AddOpenApiSource.tsx +156 −71
apps/web/index.html+3−0
@@ -4,6 +4,9 @@ · 3 unmodified lines
4 <meta charset="UTF-8" />4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <title>Executor</title>6 <title>Executor</title>
7 <link rel="preconnect" href="https://fonts.googleapis.com" />
8 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9 <link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300..700;1,9..40,300..700&family=Instrument+Serif&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
7 </head>10 </head>
8 <body>11 <body>
9 <div id="root"></div>12 <div id="root"></div>
apps/web/package.json+2−0
@@ -5,6 +5,7 @@ · 4 unmodified lines
5 "type": "module",5 "type": "module",
6 "dependencies": {6 "dependencies": {
7 "@executor/plugin-openapi": "workspace:*",7 "@executor/plugin-openapi": "workspace:*",
8 "tailwindcss": "^4.1.10",
8 "@executor/react": "workspace:*",9 "@executor/react": "workspace:*",
9 "@executor/server": "workspace:*",10 "@executor/server": "workspace:*",
10 "@tanstack/react-router": "^1.168.10",11 "@tanstack/react-router": "^1.168.10",
@@ -15,6 +16,7 @@ · 4 unmodified lines
15 "@types/react": "^19.1.0",16 "@types/react": "^19.1.0",
16 "@types/react-dom": "^19.1.0",17 "@types/react-dom": "^19.1.0",
17 "@types/node": "catalog:",18 "@types/node": "catalog:",
19 "@tailwindcss/vite": "^4.1.10",
18 "@vitejs/plugin-react": "^4.5.2",20 "@vitejs/plugin-react": "^4.5.2",
19 "typescript": "^5.9.3",21 "typescript": "^5.9.3",
20 "vite": "^6.3.5"22 "vite": "^6.3.5"
apps/web/src/main.tsx+1−0
@@ -1,5 +1,6 @@
1import React from "react";1import React from "react";
2import { createRoot } from "react-dom/client";2import { createRoot } from "react-dom/client";
3import "./styles.css";
3import { App } from "./App";4import { App } from "./App";
4 5
5const root = createRoot(document.getElementById("root")!);6const root = createRoot(document.getElementById("root")!);
apps/web/src/pages/secrets.tsx+41−21
@@ -3,25 +3,45 @@ import { useAtomValue, Result, secretsAtom } from "@executor/react"; · 2 unmodified lines
3export function SecretsPage() {3export function SecretsPage() {
4 const secrets = useAtomValue(secretsAtom());4 const secrets = useAtomValue(secretsAtom());
5 5
6 return Result.match(secrets, {6 return (
7 onInitial: () => <p>Loading secrets…</p>,7 <div>
8 onFailure: () => <p style={{ color: "red" }}>Failed to load secrets</p>,8 <h1 className="text-xl font-semibold text-foreground">Secrets</h1>
9 onSuccess: ({ value }) => (9
10 <div>10 {Result.match(secrets, {
11 <h2>Secrets ({value.length})</h2>11 onInitial: () => (
12 {value.length === 0 ? (12 <p className="mt-4 text-sm text-muted-foreground">Loading secrets…</p>
13 <p style={{ color: "#888" }}>No secrets stored yet.</p>13 ),
14 ) : (14 onFailure: () => (
15 <ul>15 <p className="mt-4 text-sm text-destructive">Failed to load secrets</p>
16 {value.map((s) => (16 ),
17 <li key={s.id}>17 onSuccess: ({ value }) =>
18 <strong>{s.name}</strong>18 value.length === 0 ? (
19 {s.purpose && <span> — {s.purpose}</span>}19 <p className="mt-4 text-sm text-muted-foreground">
20 </li>20 No secrets stored yet. Secrets are created when you configure authentication for a source.
21 ))}21 </p>
22 </ul>22 ) : (
23 )}23 <div className="mt-4 grid gap-2">
24 </div>24 {value.map((s) => (
25 ),25 <div
26 });26 key={s.id}
27 className="flex items-center justify-between rounded-lg border border-border bg-card p-3"
28 >
29 <div className="min-w-0">
30 <p className="text-sm font-medium text-card-foreground">{s.name}</p>
31 {s.purpose && (
32 <p className="mt-0.5 text-xs text-muted-foreground">{s.purpose}</p>
33 )}
34 </div>
35 {s.provider && (
36 <span className="rounded bg-secondary px-1.5 py-0.5 text-[10px] font-medium text-secondary-foreground">
37 {s.provider}
38 </span>
39 )}
40 </div>
41 ))}
42 </div>
43 ),
44 })}
45 </div>
46 );
27}47}
apps/web/src/pages/sources.tsx+51−23
@@ -18,12 +18,10 @@ export function SourcesPage() { · 17 unmodified lines
18 const tools = useAtomValue(toolsAtom());18 const tools = useAtomValue(toolsAtom());
19 const refreshTools = useAtomRefresh(toolsAtom());19 const refreshTools = useAtomRefresh(toolsAtom());
20 20
21 // Group tools by namespace (second tag after "openapi")
22 const sources = useMemo(() => {21 const sources = useMemo(() => {
23 if (tools._tag !== "Success") return [];22 if (tools._tag !== "Success") return [];
24 const namespaces = new Map<string, number>();23 const namespaces = new Map<string, number>();
25 for (const tool of tools.value) {24 for (const tool of tools.value) {
26 // Tools are tagged ["openapi", namespace, ...]
27 const ns = tool.tags.find(25 const ns = tool.tags.find(
28 (t) => t !== "openapi" && !sourcePlugins.some((p) => p.key === t),26 (t) => t !== "openapi" && !sourcePlugins.some((p) => p.key === t),
29 );27 );
@@ -44,7 +42,7 @@ export function SourcesPage() { · 14 unmodified lines
44 if (plugin) {42 if (plugin) {
45 const AddComponent = plugin.add;43 const AddComponent = plugin.add;
46 return (44 return (
47 <Suspense fallback={<p>Loading…</p>}>45 <Suspense fallback={<p className="text-sm text-muted-foreground">Loading…</p>}>
48 <AddComponent46 <AddComponent
49 onComplete={() => {47 onComplete={() => {
50 setAdding(null);48 setAdding(null);
@@ -58,35 +56,65 @@ export function SourcesPage() { · 7 unmodified lines
58 56
59 return (57 return (
60 <div>58 <div>
61 <h2>Sources</h2>59 <div className="flex items-center justify-between">
60 <h1 className="text-xl font-semibold text-foreground">Sources</h1>
61 <div className="flex gap-2">
62 {sourcePlugins.map((p) => (
63 <button
64 key={p.key}
65 onClick={() => setAdding(p.key)}
66 className="inline-flex items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90"
67 >
68 <svg viewBox="0 0 16 16" fill="none" className="size-3.5">
69 <path d="M8 3v10M3 8h10" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
70 </svg>
71 Add {p.label}
72 </button>
73 ))}
74 </div>
75 </div>
62 76
63 {Result.match(tools, {77 {Result.match(tools, {
64 onInitial: () => <p>Loading…</p>,78 onInitial: () => (
65 onFailure: () => <p style={{ color: "red" }}>Failed to load sources</p>,79 <p className="mt-4 text-sm text-muted-foreground">Loading…</p>
80 ),
81 onFailure: () => (
82 <p className="mt-4 text-sm text-destructive">Failed to load sources</p>
83 ),
66 onSuccess: () =>84 onSuccess: () =>
67 sources.length === 0 ? (85 sources.length === 0 ? (
68 <p style={{ color: "#888" }}>No sources configured yet.</p>86 <div className="mt-8 flex flex-col items-center justify-center text-center">
87 <div className="rounded-full bg-muted p-4">
88 <svg viewBox="0 0 24 24" fill="none" className="size-8 text-muted-foreground/50">
89 <path d="M12 6v12M6 12h12" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
90 </svg>
91 </div>
92 <p className="mt-3 text-sm font-medium text-foreground">No sources configured</p>
93 <p className="mt-1 text-xs text-muted-foreground">
94 Add an API source to start discovering tools.
95 </p>
96 </div>
69 ) : (97 ) : (
70 <ul>98 <div className="mt-4 grid gap-2">
71 {sources.map((s) => (99 {sources.map((s) => (
72 <li key={s.namespace}>100 <div
73 <strong>{s.namespace}</strong> — {s.toolCount} tools101 key={s.namespace}
74 </li>102 className="flex items-center justify-between rounded-lg border border-border bg-card p-4 transition-colors hover:bg-accent/50"
103 >
104 <div>
105 <p className="text-sm font-medium text-card-foreground">{s.namespace}</p>
106 <p className="mt-0.5 text-xs text-muted-foreground">
107 {s.toolCount} tool{s.toolCount !== 1 ? "s" : ""}
108 </p>
109 </div>
110 <span className="rounded bg-secondary px-1.5 py-0.5 text-[10px] font-medium text-secondary-foreground">
111 openapi
112 </span>
113 </div>
75 ))}114 ))}
76 </ul>115 </div>
77 ),116 ),
78 })}117 })}
79
80 <div style={{ marginTop: "1rem" }}>
81 <h3>Add a source</h3>
82 <div style={{ display: "flex", gap: "0.5rem" }}>
83 {sourcePlugins.map((p) => (
84 <button key={p.key} onClick={() => setAdding(p.key)}>
85 {p.label}
86 </button>
87 ))}
88 </div>
89 </div>
90 </div>118 </div>
91 );119 );
92}120}
apps/web/src/pages/tools.tsx+59−21
@@ -3,25 +3,63 @@ import { useAtomValue, Result, toolsAtom } from "@executor/react"; · 2 unmodified lines
3export function ToolsPage() {3export function ToolsPage() {
4 const tools = useAtomValue(toolsAtom());4 const tools = useAtomValue(toolsAtom());
5 5
6 return Result.match(tools, {6 return (
7 onInitial: () => <p>Loading tools…</p>,7 <div>
8 onFailure: () => <p style={{ color: "red" }}>Failed to load tools</p>,8 <h1 className="text-xl font-semibold text-foreground">Tools</h1>
9 onSuccess: ({ value }) => (9
10 <div>10 {Result.match(tools, {
11 <h2>Tools ({value.length})</h2>11 onInitial: () => (
12 {value.length === 0 ? (12 <p className="mt-4 text-sm text-muted-foreground">Loading tools…</p>
13 <p style={{ color: "#888" }}>No tools registered yet.</p>13 ),
14 ) : (14 onFailure: () => (
15 <ul>15 <p className="mt-4 text-sm text-destructive">Failed to load tools</p>
16 {value.map((t) => (16 ),
17 <li key={t.id}>17 onSuccess: ({ value }) =>
18 <strong>{t.name}</strong>18 value.length === 0 ? (
19 {t.description && <span> — {t.description}</span>}19 <p className="mt-4 text-sm text-muted-foreground">
20 </li>20 No tools registered yet. Add a source to get started.
21 ))}21 </p>
22 </ul>22 ) : (
23 )}23 <div className="mt-4">
24 </div>24 <p className="text-sm text-muted-foreground mb-3">
25 ),25 {value.length} tool{value.length !== 1 ? "s" : ""} available
26 });26 </p>
27 <div className="grid gap-2">
28 {value.map((t) => (
29 <div
30 key={t.id}
31 className="rounded-lg border border-border bg-card p-3 transition-colors hover:bg-accent/50"
32 >
33 <div className="flex items-start justify-between gap-2">
34 <div className="min-w-0">
35 <p className="text-sm font-medium text-card-foreground truncate">
36 {t.name}
37 </p>
38 {t.description && (
39 <p className="mt-0.5 text-xs text-muted-foreground line-clamp-2">
40 {t.description}
41 </p>
42 )}
43 </div>
44 {t.tags.length > 0 && (
45 <div className="flex shrink-0 gap-1">
46 {t.tags.slice(0, 2).map((tag) => (
47 <span
48 key={tag}
49 className="rounded bg-secondary px-1.5 py-0.5 text-[10px] font-medium text-secondary-foreground"
50 >
51 {tag}
52 </span>
53 ))}
54 </div>
55 )}
56 </div>
57 </div>
58 ))}
59 </div>
60 </div>
61 ),
62 })}
63 </div>
64 );
27}65}
apps/web/src/shell.tsx+134−24
@@ -1,31 +1,141 @@
1import { Link } from "@tanstack/react-router";1import { Link, useLocation } from "@tanstack/react-router";
2import { useMemo } from "react";
3import { useAtomValue, toolsAtom, Result } from "@executor/react";
2import type { ReactNode } from "react";4import type { ReactNode } from "react";
3 5
4export function Shell({ children }: { children: ReactNode }) {6// ---------------------------------------------------------------------------
7// Nav item
8// ---------------------------------------------------------------------------
9
10function NavItem(props: { to: string; label: string; active: boolean }) {
5 return (11 return (
6 <div style={{ display: "flex", minHeight: "100vh", fontFamily: "system-ui" }}>12 <Link
7 <nav13 to={props.to}
8 style={{14 className={[
9 width: 200,15 "flex items-center gap-2.5 rounded-md px-2.5 py-1.5 text-[13px] transition-colors",
10 padding: "1rem",16 props.active
11 borderRight: "1px solid #eee",17 ? "bg-sidebar-active text-foreground font-medium"
12 display: "flex",18 : "text-sidebar-foreground hover:bg-sidebar-active/60 hover:text-foreground",
13 flexDirection: "column",19 ].join(" ")}
14 gap: "0.5rem",20 >
15 }}21 {props.label}
16 >22 </Link>
17 <h2 style={{ margin: 0, fontSize: "1.2rem" }}>Executor</h2>23 );
18 <Link to="/tools" style={{ textDecoration: "none", color: "#333" }}>24}
19 Tools25
20 </Link>26// ---------------------------------------------------------------------------
21 <Link to="/sources" style={{ textDecoration: "none", color: "#333" }}>27// Source list in sidebar
22 Sources28// ---------------------------------------------------------------------------
23 </Link>29
24 <Link to="/secrets" style={{ textDecoration: "none", color: "#333" }}>30function SourceList() {
25 Secrets31 const tools = useAtomValue(toolsAtom());
32
33 const sources = useMemo(() => {
34 if (tools._tag !== "Success") return [];
35 const namespaces = new Map<string, number>();
36 for (const tool of tools.value) {
37 const ns = tool.tags.find((t) => t !== "openapi");
38 if (ns && ns !== tool.tags[0]) {
39 namespaces.set(ns, (namespaces.get(ns) ?? 0) + 1);
40 }
41 }
42 return [...namespaces.entries()].map(([ns, count]) => ({ namespace: ns, toolCount: count }));
43 }, [tools]);
44
45 if (tools._tag === "Initial" || tools.waiting) {
46 return <div className="px-2.5 py-2 text-[11px] text-muted-foreground/40">Loading…</div>;
47 }
48
49 if (sources.length === 0) {
50 return <div className="px-2.5 py-2 text-[11px] text-muted-foreground/40">No sources yet</div>;
51 }
52
53 return (
54 <div className="flex flex-col gap-px">
55 {sources.map((s) => (
56 <Link
57 key={s.namespace}
58 to="/sources"
59 className="group flex items-center gap-2 rounded-md px-2.5 py-1.5 text-[13px] text-sidebar-foreground hover:bg-sidebar-active/60 hover:text-foreground transition-colors"
60 >
61 <span className="flex-1 truncate">{s.namespace}</span>
62 <span className="text-[10px] tabular-nums text-muted-foreground/50">{s.toolCount}</span>
26 </Link>63 </Link>
27 </nav>64 ))}
28 <main style={{ flex: 1, padding: "1.5rem" }}>{children}</main>65 </div>
66 );
67}
68
69// ---------------------------------------------------------------------------
70// Shell
71// ---------------------------------------------------------------------------
72
73export function Shell({ children }: { children: ReactNode }) {
74 const location = useLocation();
75 const pathname = location.pathname;
76
77 return (
78 <div className="flex h-screen overflow-hidden">
79 {/* Sidebar */}
80 <aside className="hidden w-52 shrink-0 border-r border-sidebar-border bg-sidebar md:flex md:flex-col lg:w-56">
81 {/* Brand */}
82 <div className="flex h-12 shrink-0 items-center border-b border-sidebar-border px-4">
83 <Link to="/" className="flex items-center gap-1.5">
84 <span className="font-display text-base tracking-tight text-foreground">
85 executor
86 </span>
87 <span className="rounded bg-primary/15 px-1.5 py-0.5 text-[10px] font-medium text-primary">
88 v4
89 </span>
90 </Link>
91 </div>
92
93 {/* Nav */}
94 <nav className="flex flex-1 flex-col overflow-y-auto p-2">
95 <NavItem to="/" label="Tools" active={pathname === "/" || pathname === "/tools"} />
96 <NavItem to="/secrets" label="Secrets" active={pathname === "/secrets"} />
97
98 {/* Sources section */}
99 <div className="mt-5 mb-1 px-2.5 text-[10px] font-medium uppercase tracking-widest text-muted-foreground/50">
100 <div className="flex items-center justify-between gap-2">
101 <span>Sources</span>
102 <Link
103 to="/sources"
104 className="inline-flex items-center gap-1 rounded-md px-1.5 py-1 text-[10px] font-medium normal-case tracking-normal text-primary transition-colors hover:bg-sidebar-active hover:text-foreground"
105 >
106 <svg viewBox="0 0 16 16" fill="none" className="size-3">
107 <path d="M8 3v10M3 8h10" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
108 </svg>
109 Add
110 </Link>
111 </div>
112 </div>
113
114 <SourceList />
115 </nav>
116
117 {/* Footer */}
118 <div className="shrink-0 border-t border-sidebar-border px-4 py-2.5">
119 <div className="flex items-center justify-between text-[10px] leading-none">
120 <span className="text-muted-foreground/70 tabular-nums">v4.0.0-dev</span>
121 <a
122 href="https://github.com"
123 target="_blank"
124 rel="noreferrer"
125 className="text-muted-foreground/70 transition-colors hover:text-foreground"
126 >
127 GitHub
128 </a>
129 </div>
130 </div>
131 </aside>
132
133 {/* Main content */}
134 <main className="flex min-h-0 flex-1 flex-col min-w-0 overflow-auto">
135 <div className="flex-1 p-6 max-w-5xl">
136 {children}
137 </div>
138 </main>
29 </div>139 </div>
30 );140 );
31}141}
apps/web/src/styles.css+128−0
@@ -0,0 +1,128 @@
1@import "tailwindcss";
2
3@source "../../../packages/clients/react/src/**/*.{ts,tsx}";
4@source "../../../packages/plugins/openapi/src/react/**/*.{ts,tsx}";
5
6@theme inline {
7 --font-sans: "DM Sans", ui-sans-serif, system-ui, sans-serif;
8 --font-mono: "JetBrains Mono", ui-monospace, monospace;
9 --font-display: "Instrument Serif", Georgia, serif;
10
11 --color-background: var(--background);
12 --color-foreground: var(--foreground);
13 --color-card: var(--card);
14 --color-card-foreground: var(--card-foreground);
15 --color-primary: var(--primary);
16 --color-primary-foreground: var(--primary-foreground);
17 --color-secondary: var(--secondary);
18 --color-secondary-foreground: var(--secondary-foreground);
19 --color-muted: var(--muted);
20 --color-muted-foreground: var(--muted-foreground);
21 --color-accent: var(--accent);
22 --color-accent-foreground: var(--accent-foreground);
23 --color-destructive: var(--destructive);
24 --color-border: var(--border);
25 --color-input: var(--input);
26 --color-ring: var(--ring);
27
28 --color-sidebar: var(--sidebar);
29 --color-sidebar-foreground: var(--sidebar-foreground);
30 --color-sidebar-border: var(--sidebar-border);
31 --color-sidebar-active: var(--sidebar-active);
32
33 --radius-sm: calc(var(--radius) * 0.6);
34 --radius-md: calc(var(--radius) * 0.8);
35 --radius-lg: var(--radius);
36 --radius-xl: calc(var(--radius) * 1.4);
37}
38
39/* ——— Light theme ——— */
40:root {
41 color-scheme: light dark;
42 --radius: 0.625rem;
43
44 --background: oklch(0.985 0.002 260);
45 --foreground: oklch(0.145 0.005 260);
46 --card: oklch(0.99 0.001 260);
47 --card-foreground: oklch(0.145 0.005 260);
48 --primary: oklch(0.55 0.17 175);
49 --primary-foreground: oklch(0.98 0.005 175);
50 --secondary: oklch(0.94 0.005 260);
51 --secondary-foreground: oklch(0.32 0.008 260);
52 --muted: oklch(0.955 0.004 260);
53 --muted-foreground: oklch(0.50 0.01 250);
54 --accent: oklch(0.94 0.006 260);
55 --accent-foreground: oklch(0.145 0.005 260);
56 --destructive: oklch(0.55 0.22 25);
57 --border: oklch(0.88 0.006 260);
58 --input: oklch(0.88 0.006 260);
59 --ring: oklch(0.55 0.17 175);
60
61 --sidebar: oklch(0.97 0.003 260);
62 --sidebar-foreground: oklch(0.32 0.008 260);
63 --sidebar-border: oklch(0.90 0.005 260);
64 --sidebar-active: oklch(0.935 0.006 260);
65
66 --scrollbar-thumb: oklch(0.78 0.005 260);
67 --scrollbar-thumb-hover: oklch(0.68 0.005 260);
68}
69
70/* ——— Dark theme ——— */
71@media (prefers-color-scheme: dark) {
72 :root {
73 --background: oklch(0.115 0.005 260);
74 --foreground: oklch(0.90 0.008 250);
75 --card: oklch(0.145 0.005 260);
76 --card-foreground: oklch(0.90 0.008 250);
77 --primary: oklch(0.72 0.15 175);
78 --primary-foreground: oklch(0.12 0.01 175);
79 --secondary: oklch(0.195 0.007 260);
80 --secondary-foreground: oklch(0.78 0.008 250);
81 --muted: oklch(0.175 0.005 260);
82 --muted-foreground: oklch(0.56 0.01 250);
83 --accent: oklch(0.195 0.008 260);
84 --accent-foreground: oklch(0.90 0.008 250);
85 --destructive: oklch(0.62 0.22 25);
86 --border: oklch(0.235 0.007 260);
87 --input: oklch(0.195 0.007 260);
88 --ring: oklch(0.72 0.15 175);
89
90 --sidebar: oklch(0.105 0.005 260);
91 --sidebar-foreground: oklch(0.72 0.008 250);
92 --sidebar-border: oklch(0.20 0.006 260);
93 --sidebar-active: oklch(0.18 0.008 260);
94
95 --scrollbar-thumb: oklch(0.30 0.005 260);
96 --scrollbar-thumb-hover: oklch(0.38 0.005 260);
97 }
98}
99
100@layer base {
101 * {
102 @apply border-border;
103 }
104
105 html, body, #root {
106 min-height: 100%;
107 height: 100%;
108 }
109
110 body {
111 @apply bg-background font-sans text-foreground antialiased;
112 }
113
114 ::-webkit-scrollbar {
115 width: 6px;
116 height: 6px;
117 }
118 ::-webkit-scrollbar-track {
119 background: transparent;
120 }
121 ::-webkit-scrollbar-thumb {
122 background: var(--scrollbar-thumb);
123 border-radius: 3px;
124 }
125 ::-webkit-scrollbar-thumb:hover {
126 background: var(--scrollbar-thumb-hover);
127 }
128}
apps/web/vite.config.ts+2−0
@@ -1,5 +1,6 @@
1import { defineConfig } from "vite";1import { defineConfig } from "vite";
2import react from "@vitejs/plugin-react";2import react from "@vitejs/plugin-react";
3import tailwindcss from "@tailwindcss/vite";
3import { createApiHandler } from "@executor/server";4import { createApiHandler } from "@executor/server";
4 5
5const api = createApiHandler();6const api = createApiHandler();
@@ -7,6 +8,7 @@ const api = createApiHandler(); · 1 unmodified line
7export default defineConfig({8export default defineConfig({
8 plugins: [9 plugins: [
9 react(),10 react(),
11 tailwindcss(),
10 {12 {
11 name: "executor-api",13 name: "executor-api",
12 configureServer(server) {14 configureServer(server) {
bun.lock+78−0
@@ -37,8 +37,10 @@ · 36 unmodified lines
37 "@tanstack/react-router": "^1.168.10",37 "@tanstack/react-router": "^1.168.10",
38 "react": "^19.1.0",38 "react": "^19.1.0",
39 "react-dom": "^19.1.0",39 "react-dom": "^19.1.0",
40 "tailwindcss": "^4.1.10",
40 },41 },
41 "devDependencies": {42 "devDependencies": {
43 "@tailwindcss/vite": "^4.1.10",
42 "@types/node": "catalog:",44 "@types/node": "catalog:",
43 "@types/react": "^19.1.0",45 "@types/react": "^19.1.0",
44 "@types/react-dom": "^19.1.0",46 "@types/react-dom": "^19.1.0",
@@ -521,6 +523,36 @@ · 476 unmodified lines
521 523
522 "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],524 "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
523 525
526 "@tailwindcss/node": ["@tailwindcss/node@4.2.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.2" } }, "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA=="],
527
528 "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.2", "@tailwindcss/oxide-darwin-arm64": "4.2.2", "@tailwindcss/oxide-darwin-x64": "4.2.2", "@tailwindcss/oxide-freebsd-x64": "4.2.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", "@tailwindcss/oxide-linux-x64-musl": "4.2.2", "@tailwindcss/oxide-wasm32-wasi": "4.2.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg=="],
529
530 "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.2", "", { "os": "android", "cpu": "arm64" }, "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg=="],
531
532 "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg=="],
533
534 "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw=="],
535
536 "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ=="],
537
538 "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2", "", { "os": "linux", "cpu": "arm" }, "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ=="],
539
540 "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw=="],
541
542 "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag=="],
543
544 "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg=="],
545
546 "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ=="],
547
548 "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.2", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q=="],
549
550 "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ=="],
551
552 "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA=="],
553
554 "@tailwindcss/vite": ["@tailwindcss/vite@4.2.2", "", { "dependencies": { "@tailwindcss/node": "4.2.2", "@tailwindcss/oxide": "4.2.2", "tailwindcss": "4.2.2" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w=="],
555
524 "@tanstack/history": ["@tanstack/history@1.161.6", "", {}, "sha512-NaOGLRrddszbQj9upGat6HG/4TKvXLvu+osAIgfxPYA+eIvYKv8GKDJOrY2D3/U9MRnKfMWD7bU4jeD4xmqyIg=="],556 "@tanstack/history": ["@tanstack/history@1.161.6", "", {}, "sha512-NaOGLRrddszbQj9upGat6HG/4TKvXLvu+osAIgfxPYA+eIvYKv8GKDJOrY2D3/U9MRnKfMWD7bU4jeD4xmqyIg=="],
525 557
526 "@tanstack/react-router": ["@tanstack/react-router@1.168.10", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-store": "^0.9.3", "@tanstack/router-core": "1.168.9", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-/RmDlOwDkCug609KdPB3U+U1zmrtadJpvsmRg2zEn8TRCKRNri7dYZIjQZbNg8PgUiRL4T6njrZBV1ChzblNaA=="],558 "@tanstack/react-router": ["@tanstack/react-router@1.168.10", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-store": "^0.9.3", "@tanstack/router-core": "1.168.9", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-/RmDlOwDkCug609KdPB3U+U1zmrtadJpvsmRg2zEn8TRCKRNri7dYZIjQZbNg8PgUiRL4T6njrZBV1ChzblNaA=="],
@@ -623,6 +655,8 @@ · 96 unmodified lines
623 655
624 "electron-to-chromium": ["electron-to-chromium@1.5.330", "", {}, "sha512-jFNydB5kFtYUobh4IkWUnXeyDbjf/r9gcUEXe1xcrcUxIGfTdzPXA+ld6zBRbwvgIGVzDll/LTIiDztEtckSnA=="],656 "electron-to-chromium": ["electron-to-chromium@1.5.330", "", {}, "sha512-jFNydB5kFtYUobh4IkWUnXeyDbjf/r9gcUEXe1xcrcUxIGfTdzPXA+ld6zBRbwvgIGVzDll/LTIiDztEtckSnA=="],
625 657
658 "enhanced-resolve": ["enhanced-resolve@5.20.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA=="],
659
626 "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],660 "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
627 661
628 "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],662 "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
@@ -647,12 +681,16 @@ · 18 unmodified lines
647 681
648 "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],682 "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
649 683
684 "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
685
650 "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],686 "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
651 687
652 "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],688 "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
653 689
654 "isbot": ["isbot@5.1.37", "", {}, "sha512-5bcicX81xf6NlTEV8rWdg7Pk01LFizDetuYGHx6d/f6y3lR2/oo8IfxjzJqn1UdDEyCcwT9e7NRloj8DwCYujQ=="],690 "isbot": ["isbot@5.1.37", "", {}, "sha512-5bcicX81xf6NlTEV8rWdg7Pk01LFizDetuYGHx6d/f6y3lR2/oo8IfxjzJqn1UdDEyCcwT9e7NRloj8DwCYujQ=="],
655 691
692 "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
693
656 "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],694 "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
657 695
658 "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],696 "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
@@ -665,6 +703,30 @@ · 6 unmodified lines
665 703
666 "kubernetes-types": ["kubernetes-types@1.30.0", "", {}, "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q=="],704 "kubernetes-types": ["kubernetes-types@1.30.0", "", {}, "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q=="],
667 705
706 "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
707
708 "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="],
709
710 "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="],
711
712 "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="],
713
714 "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="],
715
716 "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="],
717
718 "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="],
719
720 "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="],
721
722 "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="],
723
724 "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="],
725
726 "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="],
727
728 "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
729
668 "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="],730 "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="],
669 731
670 "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],732 "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
@@ -737,6 +799,10 @@ · 66 unmodified lines
737 799
738 "strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="],800 "strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="],
739 801
802 "tailwindcss": ["tailwindcss@4.2.2", "", {}, "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q=="],
803
804 "tapable": ["tapable@2.3.2", "", {}, "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA=="],
805
740 "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],806 "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
741 807
742 "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],808 "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
@@ -781,6 +847,18 @@ · 38 unmodified lines
781 847
782 "@executor/sdk/typescript": ["typescript@6.0.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ=="],848 "@executor/sdk/typescript": ["typescript@6.0.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ=="],
783 849
850 "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="],
851
852 "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="],
853
854 "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="],
855
856 "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.2", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw=="],
857
858 "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
859
860 "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
861
784 "bun-types/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],862 "bun-types/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
785 863
786 "vite-node/vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],864 "vite-node/vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
packages/plugins/openapi/src/react/AddOpenApiSource.tsx+156−71
@@ -7,14 +7,14 @@ import { · 6 unmodified lines
7 Outlet,7 Outlet,
8 RouterProvider,8 RouterProvider,
9} from "@tanstack/react-router";9} from "@tanstack/react-router";
10import { useAtomSet, Result } from "@effect-atom/atom-react";10import { useAtomSet } from "@effect-atom/atom-react";
11import { Option } from "effect";11import { Option } from "effect";
12 12
13import { previewOpenApiSpec, addOpenApiSpec } from "./atoms";13import { previewOpenApiSpec, addOpenApiSpec } from "./atoms";
14import type { SpecPreview } from "../sdk/preview";14import type { SpecPreview } from "../sdk/preview";
15 15
16// ---------------------------------------------------------------------------16// ---------------------------------------------------------------------------
17// Shared state via context17// Context
18// ---------------------------------------------------------------------------18// ---------------------------------------------------------------------------
19 19
20type Navigate = (to: string) => void;20type Navigate = (to: string) => void;
@@ -24,9 +24,12 @@ const useNav = () => useContext(NavContext); · 3 unmodified lines
24interface AddState {24interface AddState {
25 specUrl: string;25 specUrl: string;
26 setSpecUrl: (v: string) => void;26 setSpecUrl: (v: string) => void;
27 preview: Result.Result<SpecPreview, unknown> | null;27 preview: SpecPreview | null;
28 doPreview: (spec: string) => void;28 setPreview: (p: SpecPreview | null) => void;
29 doAdd: (spec: string, baseUrl?: string, headers?: Record<string, unknown>) => void;29 loading: boolean;
30 setLoading: (v: boolean) => void;
31 error: string | null;
32 setError: (v: string | null) => void;
30 onComplete: () => void;33 onComplete: () => void;
31 onCancel: () => void;34 onCancel: () => void;
32}35}
@@ -39,30 +42,57 @@ const useAddState = () => useContext(AddStateContext); · 6 unmodified lines
39// ---------------------------------------------------------------------------42// ---------------------------------------------------------------------------
40 43
41function UrlStep() {44function UrlStep() {
42 const { specUrl, setSpecUrl, onCancel, doPreview } = useAddState();45 const { specUrl, setSpecUrl, onCancel, setPreview, setLoading, setError, loading } = useAddState();
43 const nav = useNav();46 const nav = useNav();
47 const doPreview = useAtomSet(previewOpenApiSpec, { mode: "promise" });
44 48
45 const handleNext = () => {49 const handleNext = async () => {
46 doPreview(specUrl);50 setLoading(true);
47 nav("/auth");51 setError(null);
52 try {
53 const result = await doPreview({
54 path: { scopeId: "default" as never },
55 payload: { spec: specUrl },
56 });
57 setPreview(result);
58 nav("/auth");
59 } catch (e) {
60 setError(e instanceof Error ? e.message : "Failed to parse spec");
61 } finally {
62 setLoading(false);
63 }
48 };64 };
49 65
50 return (66 return (
51 <div>67 <div className="space-y-4">
52 <label>68 <div>
53 Spec URL or paste JSON/YAML:69 <label className="block text-sm font-medium text-foreground mb-1.5">
70 OpenAPI Spec
71 </label>
54 <textarea72 <textarea
55 value={specUrl}73 value={specUrl}
56 onChange={(e) => setSpecUrl((e.target as HTMLTextAreaElement).value)}74 onChange={(e) => setSpecUrl((e.target as HTMLTextAreaElement).value)}
57 placeholder={'https://api.example.com/openapi.json\n\nor paste spec content here...'}75 placeholder={"https://api.example.com/openapi.json\n\nor paste spec content here..."}
58 rows={6}76 rows={6}
59 style={{ width: "100%", fontFamily: "monospace", fontSize: "0.85rem" }}77 className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring resize-y"
60 />78 />
61 </label>79 <p className="mt-1 text-xs text-muted-foreground">
62 <div style={{ marginTop: "0.5rem", display: "flex", gap: "0.5rem" }}>80 Paste a URL or the full OpenAPI JSON/YAML content.
63 <button onClick={onCancel}>Cancel</button>81 </p>
64 <button disabled={!specUrl.trim()} onClick={handleNext}>82 </div>
65 Next83 <div className="flex gap-2">
84 <button
85 onClick={onCancel}
86 className="rounded-md border border-border bg-background px-3 py-1.5 text-sm font-medium text-foreground transition-colors hover:bg-accent"
87 >
88 Cancel
89 </button>
90 <button
91 disabled={!specUrl.trim() || loading}
92 onClick={handleNext}
93 className="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed"
94 >
95 {loading ? "Analyzing…" : "Next"}
66 </button>96 </button>
67 </div>97 </div>
68 </div>98 </div>
@@ -70,64 +100,128 @@ function UrlStep() { · 1 unmodified line
70}100}
71 101
72function AuthStep() {102function AuthStep() {
73 const { preview } = useAddState();103 const { preview, error: stateError } = useAddState();
74 const nav = useNav();104 const nav = useNav();
75 105
76 return (106 return (
77 <div>107 <div className="space-y-4">
78 {preview && Result.match(preview, {108 {preview && (
79 onInitial: () => <p>Analyzing spec…</p>,109 <div className="rounded-lg border border-border bg-card p-4">
80 onSuccess: ({ value }) => (110 <div className="flex items-start justify-between">
81 <div>111 <div>
82 <h4>{Option.getOrElse(value.title, () => "API")} {Option.getOrElse(value.version, () => "")}</h4>112 <p className="text-sm font-medium text-card-foreground">
83 <p>{value.operationCount} operations found</p>113 {Option.getOrElse(preview.title, () => "API")}
84 {value.headerPresets.length > 0 && (114 </p>
85 <div>115 <p className="text-xs text-muted-foreground">
86 <p>Authentication options:</p>116 {Option.getOrElse(preview.version, () => "")} · {preview.operationCount} operations
87 <ul>117 </p>
88 {value.headerPresets.map((p, i) => (118 </div>
89 <li key={i}>{p.label}</li>
90 ))}
91 </ul>
92 </div>
93 )}
94 </div>119 </div>
95 ),120
96 onFailure: () => <p style={{ color: "red" }}>Failed to parse spec</p>,121 {preview.tags.length > 0 && (
97 })}122 <div className="mt-3 flex flex-wrap gap-1">
98 <div style={{ marginTop: "0.5rem", display: "flex", gap: "0.5rem" }}>123 {preview.tags.slice(0, 10).map((tag) => (
99 <button onClick={() => nav("/")}>Back</button>124 <span
100 <button onClick={() => nav("/confirm")}>Next</button>125 key={tag}
126 className="rounded bg-secondary px-1.5 py-0.5 text-[10px] font-medium text-secondary-foreground"
127 >
128 {tag}
129 </span>
130 ))}
131 {preview.tags.length > 10 && (
132 <span className="text-[10px] text-muted-foreground">
133 +{preview.tags.length - 10} more
134 </span>
135 )}
136 </div>
137 )}
138
139 {preview.headerPresets.length > 0 && (
140 <div className="mt-3 border-t border-border pt-3">
141 <p className="text-xs font-medium text-muted-foreground mb-1.5">
142 Authentication
143 </p>
144 {preview.headerPresets.map((p, i) => (
145 <div key={i} className="flex items-center gap-2 py-1">
146 <span className="size-1.5 rounded-full bg-primary/50" />
147 <span className="text-xs text-foreground">{p.label}</span>
148 </div>
149 ))}
150 </div>
151 )}
152 </div>
153 )}
154
155 {stateError && (
156 <p className="text-sm text-destructive">{stateError}</p>
157 )}
158
159 <div className="flex gap-2">
160 <button
161 onClick={() => nav("/")}
162 className="rounded-md border border-border bg-background px-3 py-1.5 text-sm font-medium text-foreground transition-colors hover:bg-accent"
163 >
164 Back
165 </button>
166 <button
167 onClick={() => nav("/confirm")}
168 className="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90"
169 >
170 Next
171 </button>
101 </div>172 </div>
102 </div>173 </div>
103 );174 );
104}175}
105 176
106function ConfirmStep() {177function ConfirmStep() {
107 const { specUrl, doAdd, onComplete } = useAddState();178 const { specUrl, preview, onComplete, setError, error } = useAddState();
108 const [adding, setAdding] = useState(false);
109 const [error, setError] = useState<string | null>(null);
110 const nav = useNav();179 const nav = useNav();
180 const [adding, setAdding] = useState(false);
181 const doAdd = useAtomSet(addOpenApiSpec, { mode: "promise" });
111 182
112 const handleAdd = async () => {183 const handleAdd = async () => {
113 setAdding(true);184 setAdding(true);
114 setError(null);185 setError(null);
115 try {186 try {
116 doAdd(specUrl);187 await doAdd({
188 path: { scopeId: "default" as never },
189 payload: { spec: specUrl },
190 });
117 onComplete();191 onComplete();
118 } catch (e) {192 } catch (e) {
119 setError(e instanceof Error ? e.message : String(e));193 setError(e instanceof Error ? e.message : "Failed to add source");
120 setAdding(false);194 setAdding(false);
121 }195 }
122 };196 };
123 197
124 return (198 return (
125 <div>199 <div className="space-y-4">
126 <p>Ready to add spec.</p>200 <div className="rounded-lg border border-border bg-card p-4">
127 {error && <p style={{ color: "red" }}>{error}</p>}201 <p className="text-sm font-medium text-card-foreground">Ready to add source</p>
128 <div style={{ marginTop: "0.5rem", display: "flex", gap: "0.5rem" }}>202 <p className="mt-1 text-xs text-muted-foreground">
129 <button onClick={() => nav("/auth")} disabled={adding}>Back</button>203 {preview
130 <button onClick={handleAdd} disabled={adding}>204 ? `${Option.getOrElse(preview.title, () => "API")} — ${preview.operationCount} operations will be registered as tools.`
205 : `Source from: ${specUrl.slice(0, 60)}${specUrl.length > 60 ? "…" : ""}`
206 }
207 </p>
208 </div>
209
210 {error && <p className="text-sm text-destructive">{error}</p>}
211
212 <div className="flex gap-2">
213 <button
214 onClick={() => nav("/auth")}
215 disabled={adding}
216 className="rounded-md border border-border bg-background px-3 py-1.5 text-sm font-medium text-foreground transition-colors hover:bg-accent disabled:opacity-50"
217 >
218 Back
219 </button>
220 <button
221 onClick={handleAdd}
222 disabled={adding}
223 className="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed"
224 >
131 {adding ? "Adding…" : "Add Source"}225 {adding ? "Adding…" : "Add Source"}
132 </button>226 </button>
133 </div>227 </div>
@@ -144,16 +238,15 @@ export default function AddOpenApiSource(props: { · 10 unmodified lines
144 onCancel: () => void;238 onCancel: () => void;
145}) {239}) {
146 const [specUrl, setSpecUrl] = useState("");240 const [specUrl, setSpecUrl] = useState("");
147 const [preview] = useState<Result.Result<SpecPreview, unknown> | null>(null);241 const [preview, setPreview] = useState<SpecPreview | null>(null);
148 242 const [loading, setLoading] = useState(false);
149 const doPreviewMutation = useAtomSet(previewOpenApiSpec, { mode: "promise" });243 const [error, setError] = useState<string | null>(null);
150 const doAddMutation = useAtomSet(addOpenApiSpec, { mode: "promise" });
151 244
152 const router = useMemo(() => {245 const router = useMemo(() => {
153 const rootRoute = createRootRoute({246 const rootRoute = createRootRoute({
154 component: () => (247 component: () => (
155 <div>248 <div>
156 <h3>Add OpenAPI Source</h3>249 <h1 className="text-xl font-semibold text-foreground mb-4">Add OpenAPI Source</h1>
157 <Outlet />250 <Outlet />
158 </div>251 </div>
159 ),252 ),
@@ -176,18 +269,10 @@ export default function AddOpenApiSource(props: { · 16 unmodified lines
176 };269 };
177 270
178 const state: AddState = {271 const state: AddState = {
179 specUrl,272 specUrl, setSpecUrl,
180 setSpecUrl,273 preview, setPreview,
181 preview,274 loading, setLoading,
182 doPreview: (spec) => {275 error, setError,
183 void doPreviewMutation({ path: { scopeId: "default" as never }, payload: { spec } });
184 },
185 doAdd: (spec, baseUrl, headers) => {
186 void doAddMutation({
187 path: { scopeId: "default" as never },
188 payload: { spec, baseUrl, headers },
189 });
190 },
191 onComplete: props.onComplete,276 onComplete: props.onComplete,
192 onCancel: props.onCancel,277 onCancel: props.onCancel,
193 };278 };

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t