@motiadev/workbench 0.1.0-beta.9 → 0.2.0-beta.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/index.html +2 -1
  2. package/dist/src/components/app-sidebar.js +2 -2
  3. package/dist/src/components/endpoints/endpoint-badge.d.ts +10 -0
  4. package/dist/src/components/endpoints/endpoint-badge.js +22 -0
  5. package/dist/src/components/endpoints/endpoint-call.d.ts +8 -0
  6. package/dist/src/components/endpoints/endpoint-call.js +55 -0
  7. package/dist/src/components/endpoints/endpoints.d.ts +1 -0
  8. package/dist/src/components/endpoints/endpoints.js +34 -0
  9. package/dist/src/components/endpoints/hooks/use-get-endpoints.d.ts +15 -0
  10. package/dist/src/components/endpoints/hooks/use-get-endpoints.js +19 -0
  11. package/dist/src/components/endpoints/hooks/use-json-schema-to-json.d.ts +4 -0
  12. package/dist/src/components/endpoints/hooks/use-json-schema-to-json.js +11 -0
  13. package/dist/src/components/endpoints/hooks/use-path-params.d.ts +1 -0
  14. package/dist/src/components/endpoints/hooks/use-path-params.js +4 -0
  15. package/dist/src/components/endpoints/hooks/utils.d.ts +1 -0
  16. package/dist/src/components/endpoints/hooks/utils.js +29 -0
  17. package/dist/src/components/endpoints/response-body.d.ts +7 -0
  18. package/dist/src/components/endpoints/response-body.js +6 -0
  19. package/dist/src/components/endpoints/selected-endpoint.d.ts +7 -0
  20. package/dist/src/components/endpoints/selected-endpoint.js +7 -0
  21. package/dist/src/components/logs/log-level-dot.d.ts +4 -0
  22. package/dist/src/components/logs/log-level-dot.js +17 -0
  23. package/dist/src/components/logs/logs.js +3 -3
  24. package/dist/src/components/ui/sidebar.js +2 -2
  25. package/dist/src/components/ui/table.js +1 -1
  26. package/dist/src/index.css +75 -63
  27. package/dist/src/main.js +2 -1
  28. package/dist/src/publicComponents/api-node.js +1 -1
  29. package/dist/src/publicComponents/base-node.js +3 -3
  30. package/dist/src/publicComponents/event-node.js +1 -1
  31. package/dist/src/routes/endpoints-page.d.ts +1 -0
  32. package/dist/src/routes/endpoints-page.js +5 -0
  33. package/dist/src/routes/states-page.js +1 -1
  34. package/dist/src/views/flow/flow-view.js +2 -2
  35. package/dist/src/views/flow/hooks/use-get-flow-state.js +1 -1
  36. package/dist/src/views/flow/nodes/language-indicator.js +4 -4
  37. package/dist/tailwind.config.js +0 -14
  38. package/dist/tsconfig.app.tsbuildinfo +1 -1
  39. package/dist/tsconfig.node.tsbuildinfo +1 -1
  40. package/package.json +1 -1
package/dist/index.html CHANGED
@@ -3,9 +3,10 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
 
6
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
6
7
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
7
8
  <link
8
- href="https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap"
9
+ href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap"
9
10
  rel="stylesheet"
10
11
  />
11
12
 
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useListFlows } from '@/hooks/use-list-flows';
3
- import { File, Logs, Workflow } from 'lucide-react';
3
+ import { File, Link2, Logs, Workflow } from 'lucide-react';
4
4
  import { Link, useLocation } from 'react-router';
5
5
  import { Sidebar, SidebarButton, SidebarGroup } from './ui/sidebar';
6
6
  import { Badge } from './ui/badge';
@@ -10,5 +10,5 @@ export const AppSidebar = () => {
10
10
  const { pathname } = useLocation();
11
11
  const isActive = (flowId) => pathname.includes(`/flow/${flowId}`);
12
12
  const unreadLogsCount = useLogs((state) => state.unreadLogsCount);
13
- return (_jsxs(Sidebar, { children: [_jsxs(SidebarGroup, { title: "Motia", children: [_jsx(Link, { to: "/logs", children: _jsxs(SidebarButton, { isActive: pathname === '/logs', icon: _jsx(Logs, { className: "w-4 h-4" }), children: ["Logs", pathname !== '/logs' && unreadLogsCount > 0 && _jsx(Badge, { variant: "red-rounded", children: unreadLogsCount })] }) }), _jsx(Link, { to: "/states", children: _jsx(SidebarButton, { isActive: pathname === '/states', icon: _jsx(File, { className: "w-4 h-4" }), children: "States" }) })] }), _jsx(SidebarGroup, { title: "Flows", children: flows.map((flow) => (_jsx(Link, { to: `/flow/${flow.id}`, children: _jsx(SidebarButton, { isActive: isActive(flow.id), icon: _jsx(Workflow, { className: "w-4 h-4" }), children: flow.name }) }, flow.id))) })] }));
13
+ return (_jsxs(Sidebar, { children: [_jsxs(SidebarGroup, { title: "Motia", children: [_jsx(Link, { to: "/logs", children: _jsxs(SidebarButton, { isActive: pathname === '/logs', icon: _jsx(Logs, { className: "w-4 h-4" }), children: ["Logs", pathname !== '/logs' && unreadLogsCount > 0 && _jsx(Badge, { variant: "red-rounded", children: unreadLogsCount })] }) }), _jsx(Link, { to: "/states", children: _jsx(SidebarButton, { isActive: pathname === '/states', icon: _jsx(File, { className: "w-4 h-4" }), children: "States" }) }), _jsx(Link, { to: "/endpoints", children: _jsx(SidebarButton, { isActive: pathname === '/endpoints', icon: _jsx(Link2, { className: "w-4 h-4" }), children: "Endpoints" }) })] }), _jsx(SidebarGroup, { title: "Flows", children: flows.map((flow) => (_jsx(Link, { to: `/flow/${flow.id}`, children: _jsx(SidebarButton, { isActive: isActive(flow.id), icon: _jsx(Workflow, { className: "w-4 h-4" }), children: flow.name }) }, flow.id))) })] }));
14
14
  };
@@ -0,0 +1,10 @@
1
+ import * as React from 'react';
2
+ import { type VariantProps } from 'class-variance-authority';
3
+ declare const badgeVariants: (props?: ({
4
+ variant?: "POST" | "GET" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS" | null | undefined;
5
+ defaultVariants?: "variant" | null | undefined;
6
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
+ interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
8
+ }
9
+ export declare function EndpointBadge({ className, variant, ...props }: BadgeProps): import("react/jsx-runtime").JSX.Element;
10
+ export {};
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cva } from 'class-variance-authority';
3
+ import { cn } from '@/lib/utils';
4
+ const badgeVariants = cva('inline-flex items-center rounded-lg border px-2 py-1 text-xs font-bold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', {
5
+ variants: {
6
+ variant: {
7
+ POST: 'bg-sky-500/50 text-sky-100',
8
+ GET: 'bg-lime-500/50 text-lime-100',
9
+ PUT: 'bg-yellow-500/50 text-yellow-100',
10
+ DELETE: 'bg-red-500/50 text-red-100',
11
+ PATCH: 'bg-yellow-500/50 text-yellow-100',
12
+ HEAD: 'bg-blue-500/50 text-blue-100',
13
+ OPTIONS: 'bg-purple-500/50 text-purple-100',
14
+ },
15
+ defaultVariants: {
16
+ variant: 'bg-blue-500/50 text-blue-100',
17
+ },
18
+ },
19
+ });
20
+ export function EndpointBadge({ className, variant, ...props }) {
21
+ return _jsx("div", { className: cn(badgeVariants({ variant }), className), ...props });
22
+ }
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import { ApiEndpoint } from './hooks/use-get-endpoints';
3
+ type Props = {
4
+ endpoint: ApiEndpoint;
5
+ onClose: () => void;
6
+ };
7
+ export declare const EndpointCall: React.FC<Props>;
8
+ export {};
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useState } from 'react';
3
+ import { Loader2, Play, X } from 'lucide-react';
4
+ import { Button } from '../ui/button';
5
+ import { Input } from '../ui/input';
6
+ import { Textarea } from '../ui/textarea';
7
+ import { EndpointBadge } from './endpoint-badge';
8
+ import { useJsonSchemaToJson } from './hooks/use-json-schema-to-json';
9
+ import { usePathParams } from './hooks/use-path-params';
10
+ export const EndpointCall = ({ endpoint, onClose }) => {
11
+ const shouldHaveBody = ['post', 'put', 'patch'].includes(endpoint.method.toLowerCase());
12
+ const [isRequestLoading, setIsRequestLoading] = useState(false);
13
+ const [responseCode, setResponseCode] = useState();
14
+ const [responseBody, setResponseBody] = useState();
15
+ const [executionTime, setExecutionTime] = useState();
16
+ const { body, setBody } = useJsonSchemaToJson(endpoint.bodySchema);
17
+ const pathParams = usePathParams(endpoint.path);
18
+ const [pathParamsValues, setPathParamsValues] = useState(pathParams?.reduce((acc, param) => ({ ...acc, [param]: '' }), {}));
19
+ const [queryParamsValues, setQueryParamsValues] = useState(endpoint.queryParams?.reduce((acc, param) => ({ ...acc, [param.name]: '' }), {}) ?? {});
20
+ const isPlayEnabled = useMemo(() => {
21
+ if (!pathParams)
22
+ return true;
23
+ return pathParams?.every((param) => pathParamsValues[param]);
24
+ }, [pathParams, pathParamsValues]);
25
+ const onPathParamChange = (param, value) => {
26
+ setPathParamsValues((prev) => ({ ...prev, [param]: value }));
27
+ };
28
+ const onQueryParamChange = (param, value) => {
29
+ setQueryParamsValues((prev) => ({ ...prev, [param]: value }));
30
+ };
31
+ const handleRequest = async () => {
32
+ setIsRequestLoading(true);
33
+ const startTime = Date.now();
34
+ const path = new URL(window.location.origin +
35
+ pathParams.reduce((acc, param) => {
36
+ return acc.replace(`:${param}`, pathParamsValues[param]);
37
+ }, endpoint.path));
38
+ for (const [key, value] of Object.entries(queryParamsValues)) {
39
+ path.searchParams.set(key, value);
40
+ }
41
+ const response = await fetch(path.toString(), {
42
+ method: endpoint.method,
43
+ headers: { 'Content-Type': 'application/json' },
44
+ body: endpoint.method === 'GET' ? null : body,
45
+ });
46
+ const endTime = Date.now();
47
+ const executionTime = endTime - startTime;
48
+ const json = await response.json();
49
+ setResponseCode(response.status);
50
+ setResponseBody(json);
51
+ setExecutionTime(executionTime);
52
+ setIsRequestLoading(false);
53
+ };
54
+ return (_jsxs("div", { className: "flex flex-col gap-2 overflow-y-auto", children: [_jsxs("div", { className: "text-xs flex flex-row gap-2 items-center justify-between w-full", children: [_jsx("span", { className: "font-bold", children: "Request" }), _jsx("div", { className: "flex flex-row gap-2 items-center hover:bg-white/10 rounded-md p-1", children: _jsx(X, { className: "cursor-pointer w-4 h-4", onClick: onClose }) })] }), _jsxs("div", { className: "flex flex-row gap-2 items-center", children: [_jsx(EndpointBadge, { variant: endpoint.method, children: endpoint.method.toUpperCase() }), _jsx("span", { className: "text-md font-bold", children: endpoint.path })] }), _jsx("span", { className: "text-xs text-muted-foreground", children: endpoint.description }), !!pathParams.length && (_jsxs("div", { className: "flex flex-col gap-2 p-4 rounded-lg bg-muted", children: [_jsx("span", { className: "text-xs font-bold", children: "Path Params" }), _jsx("div", { className: "flex flex-col gap-4", children: pathParams.map((param) => (_jsxs("div", { className: "text-xs", children: [_jsx("div", { className: "font-bold mb-2", children: param }), _jsx(Input, { className: "w-full", value: pathParamsValues[param], onChange: (e) => onPathParamChange(param, e.target.value) })] }, param))) })] })), !!endpoint.queryParams?.length && (_jsxs("div", { className: "flex flex-col gap-2 p-4 rounded-lg bg-muted", children: [_jsx("span", { className: "text-xs font-bold", children: "Query Params" }), _jsx("div", { className: "flex flex-col gap-4", children: endpoint.queryParams.map((param) => (_jsxs("div", { className: "text-xs", children: [_jsx("div", { className: "font-bold mb-2", children: param.name }), _jsx(Input, { className: "w-full", value: queryParamsValues[param.name], onChange: (e) => onQueryParamChange(param.name, e.target.value) })] }, param.name))) })] })), shouldHaveBody && (_jsxs("div", { className: "flex flex-col gap-2 p-4 rounded-lg bg-muted", children: [_jsx("span", { className: "text-xs font-bold", children: "Body" }), _jsx(Textarea, { className: "w-full font-mono font-medium min-h-[200px]", value: body, onChange: (e) => setBody(e.target.value) })] })), _jsxs(Button, { className: "w-fit", onClick: handleRequest, disabled: isRequestLoading || !isPlayEnabled, children: [isRequestLoading ? _jsx(Loader2, { className: "animate-spin" }) : _jsx(Play, {}), " Play"] }), responseCode !== undefined && (_jsxs("div", { className: "flex flex-col gap-2 p-4 rounded-lg bg-muted", children: [_jsxs("span", { className: "text-xs font-bold", children: [_jsx(EndpointBadge, { variant: responseCode >= 400 ? 'DELETE' : 'GET', children: responseCode }), " Execution time: ", _jsxs("span", { className: "text-muted-foreground", children: [executionTime, "ms"] })] }), _jsx("span", { className: "text-xs font-mono font-bold bg-black/50 p-2 rounded-lg whitespace-pre-wrap", children: JSON.stringify(responseBody, null, 2) })] }))] }));
55
+ };
@@ -0,0 +1 @@
1
+ export declare const Endpoints: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cva } from 'class-variance-authority';
3
+ import { useEffect, useState } from 'react';
4
+ import { EndpointBadge } from './endpoint-badge';
5
+ import { EndpointCall } from './endpoint-call';
6
+ import { useGetEndpoints } from './hooks/use-get-endpoints';
7
+ import { SelectedEndpoint } from './selected-endpoint';
8
+ const endpointVariants = cva('flex flex-col gap-2 font-mono p-2 rounded-lg cursor-pointer', {
9
+ variants: {
10
+ method: {
11
+ GET: 'bg-lime-500/20',
12
+ POST: 'bg-blue-500/20',
13
+ PUT: 'bg-yellow-500/20',
14
+ DELETE: 'bg-red-500/20',
15
+ PATCH: 'bg-yellow-500/20',
16
+ HEAD: 'bg-blue-500/20',
17
+ OPTIONS: 'bg-purple-500/20',
18
+ },
19
+ },
20
+ defaultVariants: { method: 'GET' },
21
+ });
22
+ export const Endpoints = () => {
23
+ const endpoints = useGetEndpoints();
24
+ const [selectedEndpoint, setSelectedEndpoint] = useState(null);
25
+ useEffect(() => {
26
+ setSelectedEndpoint((selected) => {
27
+ if (!selected)
28
+ return null;
29
+ const endpoint = endpoints.find((endpoint) => endpoint.method === selected.method && endpoint.path === selected.path);
30
+ return endpoint ?? null;
31
+ });
32
+ }, [endpoints]);
33
+ return (_jsxs("div", { className: "flex flex-row w-full h-screen", children: [_jsxs("div", { className: "flex flex-col gap-2 flex-1 m-4 mr-2 max-h-full overflow-y-auto", children: [_jsxs("header", { children: [_jsx("h1", { className: "text-2xl font-bold", children: "API Endpoints" }), _jsx("span", { className: "text-sm text-zinc-400", children: "Check all API endpoints" })] }), endpoints.map((endpoint) => (_jsxs("div", { className: endpointVariants({ method: endpoint.method }), onClick: () => setSelectedEndpoint(endpoint), children: [_jsxs("div", { className: "flex flex-row gap-2 items-center", children: [_jsx(EndpointBadge, { variant: endpoint.method, children: endpoint.method.toUpperCase() }), _jsx("span", { className: "text-md font-bold", children: endpoint.path }), !selectedEndpoint && _jsx("span", { className: "text-xs text-muted-foreground", children: endpoint.description })] }), selectedEndpoint && _jsx("span", { className: "text-xs text-muted-foreground", children: endpoint.description }), selectedEndpoint === endpoint && _jsx(SelectedEndpoint, { endpoint: selectedEndpoint })] }, `${endpoint.method} ${endpoint.path}`)))] }), selectedEndpoint && (_jsx("div", { className: "flex flex-col gap-2 flex-1 m-4 ml-2 p-4 rounded-lg bg-muted", children: _jsx(EndpointCall, { endpoint: selectedEndpoint, onClose: () => setSelectedEndpoint(null) }) }))] }));
34
+ };
@@ -0,0 +1,15 @@
1
+ type ApiRouteMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD';
2
+ type QueryParam = {
3
+ name: string;
4
+ description: string;
5
+ };
6
+ export type ApiEndpoint = {
7
+ method: ApiRouteMethod;
8
+ path: string;
9
+ description?: string;
10
+ queryParams?: QueryParam[];
11
+ responseSchema?: Record<string, any>;
12
+ bodySchema?: Record<string, Record<string, any>>;
13
+ };
14
+ export declare const useGetEndpoints: () => ApiEndpoint[];
15
+ export {};
@@ -0,0 +1,19 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ import { useSocket } from '../../../hooks/use-socket';
3
+ export const useGetEndpoints = () => {
4
+ const { socket } = useSocket();
5
+ const [endpoints, setEndpoints] = useState([]);
6
+ const fetchEndpoints = useCallback(() => {
7
+ fetch(`/api-endpoints`)
8
+ .then((res) => res.json())
9
+ .then((endpoints) => setEndpoints(endpoints));
10
+ }, []);
11
+ useEffect(() => {
12
+ fetchEndpoints();
13
+ socket.on('api-endpoint-changed', fetchEndpoints);
14
+ return () => {
15
+ socket.off('api-endpoint-changed', fetchEndpoints);
16
+ };
17
+ }, [socket, fetchEndpoints]);
18
+ return endpoints;
19
+ };
@@ -0,0 +1,4 @@
1
+ export declare const useJsonSchemaToJson: (schema: Record<string, any> | undefined) => {
2
+ body: string;
3
+ setBody: import("react").Dispatch<import("react").SetStateAction<string>>;
4
+ };
@@ -0,0 +1,11 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { convertJsonSchemaToJson } from './utils';
3
+ export const useJsonSchemaToJson = (schema) => {
4
+ const [body, setBody] = useState('');
5
+ useEffect(() => {
6
+ if (schema) {
7
+ setBody(JSON.stringify(convertJsonSchemaToJson(schema), null, 2));
8
+ }
9
+ }, [schema]);
10
+ return { body, setBody };
11
+ };
@@ -0,0 +1 @@
1
+ export declare const usePathParams: (path: string) => string[];
@@ -0,0 +1,4 @@
1
+ export const usePathParams = (path) => {
2
+ const pathParams = path.match(/:(\w+)/g);
3
+ return pathParams?.map((param) => param.slice(1)) ?? [];
4
+ };
@@ -0,0 +1 @@
1
+ export declare const convertJsonSchemaToJson: (schema: Record<string, any>) => any;
@@ -0,0 +1,29 @@
1
+ export const convertJsonSchemaToJson = (schema) => {
2
+ if (!schema)
3
+ return {};
4
+ if (schema.type === 'object') {
5
+ const result = {};
6
+ if (schema.properties) {
7
+ Object.entries(schema.properties).forEach(([key, value]) => {
8
+ result[key] = convertJsonSchemaToJson(value);
9
+ });
10
+ }
11
+ return result;
12
+ }
13
+ switch (schema.type) {
14
+ case 'array':
15
+ return [convertJsonSchemaToJson(schema.items)];
16
+ case 'string':
17
+ return schema.description ?? 'string';
18
+ case 'number':
19
+ return schema.description ?? 0;
20
+ case 'integer':
21
+ return 0;
22
+ case 'boolean':
23
+ return schema.description ?? false;
24
+ case 'null':
25
+ return schema.description ?? null;
26
+ default:
27
+ return undefined;
28
+ }
29
+ };
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ type Props = {
3
+ status: string;
4
+ body: Record<string, any>;
5
+ };
6
+ export declare const ResponseBody: React.FC<Props>;
7
+ export {};
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useJsonSchemaToJson } from './hooks/use-json-schema-to-json';
3
+ export const ResponseBody = ({ status, body }) => {
4
+ const { body: responseBody } = useJsonSchemaToJson(body);
5
+ return (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-xs font-bold", children: status }), _jsx("span", { className: "text-xs font-mono bg-black/50 p-2 rounded-lg whitespace-pre-wrap", children: responseBody })] }));
6
+ };
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import { ApiEndpoint } from './hooks/use-get-endpoints';
3
+ type Props = {
4
+ endpoint: ApiEndpoint;
5
+ };
6
+ export declare const SelectedEndpoint: React.FC<Props>;
7
+ export {};
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useJsonSchemaToJson } from './hooks/use-json-schema-to-json';
3
+ import { ResponseBody } from './response-body';
4
+ export const SelectedEndpoint = ({ endpoint }) => {
5
+ const { body: requestBody } = useJsonSchemaToJson(endpoint.bodySchema);
6
+ return (_jsxs(_Fragment, { children: [endpoint.queryParams && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-xs font-bold", children: "Query Params" }), _jsx("div", { className: "flex flex-col gap-2 flex-1 p-2 rounded-lg bg-muted table", children: endpoint.queryParams.map((param) => (_jsxs("span", { className: "text-xs table-row", children: [_jsx("span", { className: "font-bold table-cell", children: param.name }), _jsx("span", { className: "text-xs table-cell", children: param.description })] }, param.name))) })] })), requestBody && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-xs font-bold", children: "Request Body" }), _jsx("span", { className: "text-xs font-mono bg-black/50 p-2 rounded-lg whitespace-pre-wrap", children: requestBody })] })), endpoint.responseSchema && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-xs font-bold", children: "Response" }), Object.entries(endpoint.responseSchema).map(([status, schema]) => (_jsx(ResponseBody, { status: status, body: schema }, status)))] }))] }));
7
+ };
@@ -0,0 +1,4 @@
1
+ import * as React from 'react';
2
+ export declare const LogLevelDot: React.FC<{
3
+ level: string;
4
+ }>;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cva } from 'class-variance-authority';
3
+ const badgeVariants = cva('text-xs font-medium tracking-wide w-2 h-2 rounded-full mx-1', {
4
+ variants: {
5
+ variant: {
6
+ info: 'bg-sky-500',
7
+ trace: 'bg-sky-500',
8
+ debug: 'bg-sky-500',
9
+ error: 'bg-rose-500',
10
+ fatal: 'bg-rose-500',
11
+ warn: 'bg-amber-500',
12
+ },
13
+ },
14
+ });
15
+ export const LogLevelDot = ({ level }) => {
16
+ return _jsx("div", { className: badgeVariants({ variant: level }) });
17
+ };
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
2
+ import { Table, TableBody, TableCell, TableRow } from '@/components/ui/table';
3
3
  import { useLogs } from '@/stores/use-logs';
4
- import { LogLevelBadge } from './log-level-badge';
5
4
  import { useState } from 'react';
6
5
  import { LogDetail } from './log-detail';
6
+ import { LogLevelDot } from './log-level-dot';
7
7
  const timestamp = (time) => {
8
8
  const date = new Date(Number(time));
9
9
  return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
@@ -14,5 +14,5 @@ export const Logs = () => {
14
14
  const handleLogClick = (log) => {
15
15
  setSelectedLog(log);
16
16
  };
17
- return (_jsxs("div", { className: "overflow-y-auto h-full text-bold p-4", children: [_jsx(LogDetail, { log: selectedLog, onClose: () => setSelectedLog(undefined) }), _jsxs(Table, { children: [_jsx(TableHeader, { className: "sticky top-0", children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Time" }), _jsx(TableHead, { children: "Level" }), _jsx(TableHead, { children: "Trace" }), _jsx(TableHead, { children: "Flow" }), _jsx(TableHead, { children: "Step" }), _jsx(TableHead, { children: "Message" })] }) }), _jsx(TableBody, { className: "text-md font-mono", children: logs.map((log, index) => (_jsxs(TableRow, { className: "text-white border-b border-zinc-800/50", onClick: () => handleLogClick(log), children: [_jsx(TableCell, { className: "whitespace-nowrap", children: timestamp(log.time) }), _jsx(TableCell, { children: _jsx(LogLevelBadge, { level: log.level }) }), _jsx(TableCell, { children: log.traceId }), _jsx(TableCell, { children: log.flows?.join?.(', ') }), _jsx(TableCell, { children: log.step }), _jsx(TableCell, { children: log.msg })] }, index))) })] })] }));
17
+ return (_jsxs("div", { className: "overflow-y-auto h-full text-bold p-4", children: [_jsx(LogDetail, { log: selectedLog, onClose: () => setSelectedLog(undefined) }), _jsx(Table, { children: _jsx(TableBody, { className: "text-md font-mono", children: logs.map((log, index) => (_jsxs(TableRow, { className: "cursor-pointer even:bg-muted/50 border-0", onClick: () => handleLogClick(log), children: [_jsxs(TableCell, { className: "whitespace-nowrap flex items-center gap-2", children: [_jsx(LogLevelDot, { level: log.level }), timestamp(log.time)] }), _jsx(TableCell, { className: "whitespace-nowrap text-md font-mono cursor-pointer hover:text-primary text-muted-foreground text-xs font-mono", children: log.traceId }), _jsx(TableCell, { className: "whitespace-nowrap text-md font-mono", children: log.step }), _jsx(TableCell, { className: "whitespace-nowrap text-md font-mono max-w-[500px] truncate w-full", children: log.msg })] }, index))) }) })] }));
18
18
  };
@@ -8,8 +8,8 @@ export const Sidebar = ({ children }) => {
8
8
  return (_jsxs("div", { className: cn('max-h-screen overflow-y-auto transition-[width] duration-300 border-r border-zinc-800 border-solid', isCollapsed ? 'w-[50px]' : 'w-[250px]'), children: [_jsx("div", { className: "flex items-center justify-end gap-2 px-4 py-3", children: _jsx(Button, { variant: "ghost", size: "icon", onClick: () => setIsCollapsed(!isCollapsed), children: isCollapsed ? _jsx(PanelLeftOpen, { className: "w-4 h-4" }) : _jsx(PanelLeftClose, { className: "w-4 h-4" }) }) }), !isCollapsed && _jsx("div", { className: "overflow-y-auto w-[250px]", children: children })] }));
9
9
  };
10
10
  export const SidebarGroup = ({ children, title }) => {
11
- return (_jsxs("div", { className: "flex flex-col", children: [_jsx("h2", { className: "text-lg text-white/60 px-4 py-2 uppercase", children: title }), children] }));
11
+ return (_jsxs("div", { className: "flex flex-col", children: [_jsx("h2", { className: "text-xs font-bold text-muted-foreground px-4 py-2 uppercase", children: title }), children] }));
12
12
  };
13
13
  export const SidebarButton = ({ children, isActive, icon }) => {
14
- return (_jsxs("div", { className: cn('flex items-center gap-2 px-4 py-3 ', isActive && 'bg-[#242036]'), children: [_jsx("div", { className: "text-gray-500", children: icon }), children] }));
14
+ return (_jsxs("div", { className: cn('flex text-sm font-medium items-center gap-2 px-4 py-3 ', isActive && 'bg-muted'), children: [_jsx("div", { className: "text-muted-foreground", children: icon }), children] }));
15
15
  };
@@ -11,7 +11,7 @@ const TableFooter = React.forwardRef(({ className, ...props }, ref) => (_jsx("tf
11
11
  TableFooter.displayName = 'TableFooter';
12
12
  const TableRow = React.forwardRef(({ className, ...props }, ref) => (_jsx("tr", { ref: ref, className: cn('border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted', className), ...props })));
13
13
  TableRow.displayName = 'TableRow';
14
- const TableHead = React.forwardRef(({ className, ...props }, ref) => (_jsx("th", { ref: ref, className: cn('h-10 px-2 text-left align-middle text-lg bg-[#252234] text-muted-foreground first:rounded-l-lg last:rounded-r-lg [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]', className), ...props })));
14
+ const TableHead = React.forwardRef(({ className, ...props }, ref) => (_jsx("th", { ref: ref, className: cn('h-10 px-2 text-left align-middle text-md font-medium bg-muted text-muted-foreground first:rounded-l-lg last:rounded-r-lg [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]', className), ...props })));
15
15
  TableHead.displayName = 'TableHead';
16
16
  const TableCell = React.forwardRef(({ className, ...props }, ref) => (_jsx("td", { ref: ref, className: cn('p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]', className), ...props })));
17
17
  TableCell.displayName = 'TableCell';
@@ -133,9 +133,7 @@ table {
133
133
  font-size: 16px;
134
134
 
135
135
  color-scheme: light dark;
136
- color: rgba(255, 255, 255, 0.87);
137
- background-color: #242424;
138
-
136
+ font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");
139
137
  font-synthesis: none;
140
138
  text-rendering: optimizeLegibility;
141
139
  -webkit-font-smoothing: antialiased;
@@ -149,7 +147,7 @@ table {
149
147
  text-rendering: optimizeLegibility;
150
148
  -webkit-font-smoothing: antialiased;
151
149
  -moz-osx-font-smoothing: grayscale;
152
- font-family: 'DM Sans', serif;
150
+ font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");
153
151
  font-optical-sizing: auto;
154
152
  }
155
153
 
@@ -162,7 +160,7 @@ body {
162
160
 
163
161
  button,
164
162
  textarea {
165
- font-family: 'DM Sans', serif;
163
+ font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");
166
164
  }
167
165
 
168
166
  strong {
@@ -182,72 +180,72 @@ body,
182
180
  @layer base {
183
181
  :root {
184
182
  --background: 0 0% 100%;
185
- --foreground: 224 71.4% 4.1%;
183
+ --foreground: 0 0% 4%;
186
184
  --card: 0 0% 100%;
187
- --card-foreground: 224 71.4% 4.1%;
185
+ --card-foreground: 0 0% 4%;
188
186
  --popover: 0 0% 100%;
189
- --popover-foreground: 224 71.4% 4.1%;
190
- --primary: 220.9 39.3% 11%;
191
- --primary-foreground: 210 20% 98%;
192
- --secondary: 220 14.3% 95.9%;
193
- --secondary-foreground: 220.9 39.3% 11%;
194
- --muted: 220 14.3% 95.9%;
195
- --muted-foreground: 220 8.9% 46.1%;
196
- --accent: 220 14.3% 95.9%;
197
- --accent-foreground: 220.9 39.3% 11%;
198
- --destructive: 0 84.2% 60.2%;
199
- --destructive-foreground: 210 20% 98%;
200
- --border: 220 13% 91%;
201
- --input: 220 13% 91%;
202
- --ring: 224 71.4% 4.1%;
203
- --chart-1: 12 76% 61%;
204
- --chart-2: 173 58% 39%;
205
- --chart-3: 197 37% 24%;
206
- --chart-4: 43 74% 66%;
207
- --chart-5: 27 87% 67%;
187
+ --popover-foreground: 0 0% 4%;
188
+ --primary: 0 0% 11%;
189
+ --primary-foreground: 0 0% 98%;
190
+ --secondary: 0 0% 96%;
191
+ --secondary-foreground: 0 0% 11%;
192
+ --muted: 0 0% 96%;
193
+ --muted-foreground: 0 0% 46%;
194
+ --accent: 0 0% 96%;
195
+ --accent-foreground: 0 0% 11%;
196
+ --destructive: 0 0% 60%;
197
+ --destructive-foreground: 0 0% 98%;
198
+ --border: 0 0% 91%;
199
+ --input: 0 0% 91%;
200
+ --ring: 0 0% 4%;
201
+ --chart-1: 0 0% 70%;
202
+ --chart-2: 0 0% 55%;
203
+ --chart-3: 0 0% 40%;
204
+ --chart-4: 0 0% 25%;
205
+ --chart-5: 0 0% 10%;
208
206
  --radius: 0.5rem;
209
207
  --sidebar-background: 0 0% 98%;
210
- --sidebar-foreground: 240 5.3% 26.1%;
211
- --sidebar-primary: 240 5.9% 10%;
208
+ --sidebar-foreground: 0 0% 26%;
209
+ --sidebar-primary: 0 0% 10%;
212
210
  --sidebar-primary-foreground: 0 0% 98%;
213
- --sidebar-accent: 240 4.8% 95.9%;
214
- --sidebar-accent-foreground: 240 5.9% 10%;
215
- --sidebar-border: 220 13% 91%;
216
- --sidebar-ring: 217.2 91.2% 59.8%;
211
+ --sidebar-accent: 0 0% 96%;
212
+ --sidebar-accent-foreground: 0 0% 10%;
213
+ --sidebar-border: 0 0% 91%;
214
+ --sidebar-ring: 0 0% 60%;
217
215
  }
218
216
  .dark {
219
- --background: 249 23% 11%;
220
- --border: 240 18% 18%;
221
- --foreground: 210 20% 98%;
222
- --card: 224 71.4% 4.1%;
223
- --card-foreground: 210 20% 98%;
224
- --popover: 224 71.4% 4.1%;
225
- --popover-foreground: 210 20% 98%;
226
- --primary: 210 20% 98%;
227
- --primary-foreground: 220.9 39.3% 11%;
228
- --secondary: 215 27.9% 16.9%;
229
- --secondary-foreground: 210 20% 98%;
230
- --muted: 215 27.9% 16.9%;
231
- --muted-foreground: 217.9 10.6% 64.9%;
232
- --accent: 215 27.9% 16.9%;
233
- --accent-foreground: 210 20% 98%;
234
- --destructive: 0 62.8% 30.6%;
235
- --destructive-foreground: 210 20% 98%;
236
- --input: 215 27.9% 16.9%;
237
- --ring: 216 12.2% 83.9%;
238
- --chart-1: 220 70% 50%;
239
- --chart-2: 160 60% 45%;
240
- --chart-3: 30 80% 55%;
241
- --chart-4: 280 65% 60%;
242
- --chart-5: 340 75% 55%;
243
- --sidebar-background: 240 5.9% 10%;
244
- --sidebar-foreground: 240 4.8% 95.9%;
245
- --sidebar-primary: 224.3 76.3% 48%;
217
+ --background: 0 0% 1%;
218
+ --border: 0 0% 18%;
219
+ --foreground: 0 0% 98%;
220
+ --card: 0 0% 4%;
221
+ --card-foreground: 0 0% 98%;
222
+ --popover: 0 0% 4%;
223
+ --popover-foreground: 0 0% 98%;
224
+ --primary: 0 0% 98%;
225
+ --primary-foreground: 0 0% 11%;
226
+ --secondary: 0 0% 17%;
227
+ --secondary-foreground: 0 0% 98%;
228
+ --muted: 0 0% 8%;
229
+ --muted-foreground: 0 0% 65%;
230
+ --accent: 0 0% 17%;
231
+ --accent-foreground: 0 0% 98%;
232
+ --destructive: 0 0% 31%;
233
+ --destructive-foreground: 0 0% 98%;
234
+ --input: 0 0% 17%;
235
+ --ring: 0 0% 84%;
236
+ --chart-1: 0 0% 80%;
237
+ --chart-2: 0 0% 65%;
238
+ --chart-3: 0 0% 50%;
239
+ --chart-4: 0 0% 35%;
240
+ --chart-5: 0 0% 20%;
241
+ --sidebar-background: 0 0% 10%;
242
+ --sidebar-foreground: 0 0% 96%;
243
+ --sidebar-primary: 0 0% 48%;
246
244
  --sidebar-primary-foreground: 0 0% 100%;
247
- --sidebar-accent: 240 3.7% 15.9%;
248
- --sidebar-accent-foreground: 240 4.8% 95.9%;
249
- --sidebar-border: 240 3.7% 15.9%;
250
- --sidebar-ring: 217.2 91.2% 59.8%;
245
+ --sidebar-accent: 0 0% 16%;
246
+ --sidebar-accent-foreground: 0 0% 96%;
247
+ --sidebar-border: 0 0% 16%;
248
+ --sidebar-ring: 0 0% 60%;
251
249
  }
252
250
 
253
251
  .text-md {
@@ -294,3 +292,17 @@ body,
294
292
  .resize-handle.dragging {
295
293
  background-color: rgba(74, 222, 128, 0.4);
296
294
  }
295
+
296
+ .bg-muted {
297
+ background-color: rgba(120, 120, 120, 0.10);
298
+ }
299
+
300
+ .text-muted {
301
+ color: rgba(120, 120, 120, 0.5);
302
+ }
303
+
304
+ .text-muted-foreground {
305
+ color: rgba(255, 255, 255, 0.8);
306
+ }
307
+
308
+
package/dist/src/main.js CHANGED
@@ -9,9 +9,10 @@ import { RouteWrapper } from './route-wrapper';
9
9
  import { LogsPage } from './routes/logs-page';
10
10
  import { RootMotia } from './components/root-motia';
11
11
  import { StatesPage } from './routes/states-page';
12
+ import { EndpointsPage } from './routes/endpoints-page';
12
13
  // Render the app
13
14
  const rootElement = document.getElementById('root');
14
15
  if (!rootElement.innerHTML) {
15
16
  const root = createRoot(rootElement);
16
- root.render(_jsx(StrictMode, { children: _jsx(BrowserRouter, { children: _jsx(RootMotia, { children: _jsx(RouteWrapper, { children: _jsxs(Routes, { children: [_jsx(Route, { path: "/", element: _jsx(Index, {}) }), _jsx(Route, { path: "/flow/:id", element: _jsx(Flow, {}) }), _jsx(Route, { path: "/logs", element: _jsx(LogsPage, {}) }), _jsx(Route, { path: "/states", element: _jsx(StatesPage, {}) })] }) }) }) }) }));
17
+ root.render(_jsx(StrictMode, { children: _jsx(BrowserRouter, { children: _jsx(RootMotia, { children: _jsx(RouteWrapper, { children: _jsxs(Routes, { children: [_jsx(Route, { path: "/", element: _jsx(Index, {}) }), _jsx(Route, { path: "/flow/:id", element: _jsx(Flow, {}) }), _jsx(Route, { path: "/logs", element: _jsx(LogsPage, {}) }), _jsx(Route, { path: "/states", element: _jsx(StatesPage, {}) }), _jsx(Route, { path: "/endpoints", element: _jsx(EndpointsPage, {}) })] }) }) }) }) }));
17
18
  }
@@ -3,5 +3,5 @@ import { Webhook } from 'lucide-react';
3
3
  import { BaseNode } from './base-node';
4
4
  import { DetailItem, NodeDetails } from './node-details';
5
5
  export const ApiNode = ({ data, children }) => {
6
- return (_jsxs(BaseNode, { variant: "api", title: data.name, language: data.language, disableSourceHandle: !data.emits?.length && !data.virtualEmits?.length, disableTargetHandle: !data.subscribes?.length && !data.virtualSubscribes?.length, children: [data.description && _jsx("div", { className: "text-sm text-white/60", children: data.description }), children, data.webhookUrl && (_jsxs("div", { className: "flex gap-1 items-center text-xs text-white/60", children: [_jsx(Webhook, { className: "w-3 h-3 text-white/40" }), _jsx("div", { className: "font-mono", children: data.webhookUrl })] })), _jsx(NodeDetails, { type: "api", name: data.name, subscribes: data.subscribes, emits: data.emits, description: data.description, language: data.language, children: _jsx(DetailItem, { label: "Webhook URL", children: _jsxs("div", { className: "flex gap-1 items-center text-xs text-white/60", children: [_jsx(Webhook, { className: "w-3 h-3 text-white/40" }), _jsx("div", { className: "font-mono", children: data.webhookUrl })] }) }) })] }));
6
+ return (_jsxs(BaseNode, { variant: "api", title: data.name, language: data.language, disableSourceHandle: !data.emits?.length && !data.virtualEmits?.length, disableTargetHandle: !data.subscribes?.length && !data.virtualSubscribes?.length, children: [data.description && _jsx("div", { className: "text-sm text-muted-foreground", children: data.description }), children, data.webhookUrl && (_jsxs("div", { className: "flex gap-1 items-center text-xs text-muted-foreground", children: [_jsx(Webhook, { className: "w-3 h-3 text-muted-foreground/40" }), _jsx("div", { className: "font-mono", children: data.webhookUrl })] })), _jsx(NodeDetails, { type: "api", name: data.name, subscribes: data.subscribes, emits: data.emits, description: data.description, language: data.language, children: _jsx(DetailItem, { label: "Webhook URL", children: _jsxs("div", { className: "flex gap-1 items-center text-xs text-white/60", children: [_jsx(Webhook, { className: "w-3 h-3 text-white/40" }), _jsx("div", { className: "font-mono", children: data.webhookUrl })] }) }) })] }));
7
7
  };
@@ -19,7 +19,7 @@ const Dot = ({ variant }) => (_jsx("div", { className: cn(baseDot({ variant }))
19
19
  const HeaderBar = ({ text, variant, children, }) => (_jsxs("div", { className: "text-sm text-white flex justify-between items-center gap-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Dot, { variant: variant }), _jsx("span", { children: text })] }), children] }));
20
20
  export const BaseNode = (props) => {
21
21
  const { title, variant, children, disableSourceHandle, disableTargetHandle, language } = props;
22
- return (_jsx("div", { className: "p-[1px] rounded-lg shadow-[0px_7px_14px_0px_rgba(7,0,23,0.98)] max-w-[350px] ", style: {
23
- background: `linear-gradient(100.74deg, rgba(0, 71, 255, 0) -2.15%, ${colorMap[variant]} 45.08%, rgba(0, 71, 255, 0) 96.79%)`,
24
- }, children: _jsx("div", { className: "rounded-lg bg-[#060014] p-4", children: _jsxs("div", { className: "group relative", children: [_jsx(HeaderBar, { text: title, variant: variant, children: _jsx(LanguageIndicator, { language: language }) }), _jsx("div", { className: "pt-4 space-y-3", children: children }), !disableTargetHandle && _jsx(BaseHandle, { type: "target", position: Position.Top, variant: variant }), !disableSourceHandle && _jsx(BaseHandle, { type: "source", position: Position.Bottom, variant: variant }), _jsx("div", { className: "absolute inset-0 -z-10 translate-y-1 translate-x-1 bg-black/20 rounded-md border border-white/5" })] }) }) }));
22
+ return (_jsx("div", { className: "p-[1px] rounded-lg max-w-[350px] ", children: _jsx("div", { className: "rounded-lg bg-background p-4 border border-muted border-solid", style: {
23
+ borderColor: colorMap[variant],
24
+ }, children: _jsxs("div", { className: "group relative", children: [_jsx(HeaderBar, { text: title, variant: variant, children: _jsx(LanguageIndicator, { language: language }) }), _jsx("div", { className: "pt-4 space-y-3", children: children }), !disableTargetHandle && _jsx(BaseHandle, { type: "target", position: Position.Top, variant: variant }), !disableSourceHandle && _jsx(BaseHandle, { type: "source", position: Position.Bottom, variant: variant }), _jsx("div", { className: "absolute inset-0 -z-10 translate-y-1 translate-x-1 bg-background rounded-md border border-white/5" })] }) }) }));
25
25
  };
@@ -3,5 +3,5 @@ import { BaseNode } from './base-node';
3
3
  import { NodeDetails } from './node-details';
4
4
  export const EventNode = (props) => {
5
5
  const { data, children } = props;
6
- return (_jsxs(BaseNode, { variant: "event", title: data.name, language: data.language, disableSourceHandle: !data.emits?.length && !data.virtualEmits?.length, disableTargetHandle: !data.subscribes?.length && !data.virtualSubscribes?.length, children: [children, _jsx(NodeDetails, { type: "event", name: data.name, subscribes: data.subscribes, emits: data.emits, description: data.description, language: data.language })] }));
6
+ return (_jsxs(BaseNode, { variant: "event", title: data.name, language: data.language, disableSourceHandle: !data.emits?.length && !data.virtualEmits?.length, disableTargetHandle: !data.subscribes?.length && !data.virtualSubscribes?.length, children: [_jsx("div", { className: "text-sm text-muted-foreground", children: data.description }), children, _jsx(NodeDetails, { type: "event", name: data.name, subscribes: data.subscribes, emits: data.emits, description: data.description, language: data.language })] }));
7
7
  };
@@ -0,0 +1 @@
1
+ export declare const EndpointsPage: () => import("react/jsx-runtime").JSX.Element;