loopable-cli 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/nitro.json +1 -1
- package/.output/public/assets/_connectorId-CDCuSgpN.js +1 -0
- package/.output/public/assets/_loopId-DOnIsVTW.js +1 -0
- package/.output/public/assets/_taskId-BO5MZnlZ.js +1 -0
- package/.output/public/assets/agents-CBozi7oi.js +1 -0
- package/.output/public/assets/alert-Bn_wjgTX.js +1 -0
- package/.output/public/assets/button-K3ypIhAZ.js +1 -0
- package/.output/public/assets/card-DiVnaTIE.js +1 -0
- package/.output/public/assets/chevron-right-BrctLtFK.js +1 -0
- package/.output/public/assets/connection-status-DKWelvLO.js +1 -0
- package/.output/public/assets/connector-icon-CEbdE71G.js +1 -0
- package/.output/public/assets/connectors-JyX3IzJj.js +1 -0
- package/.output/public/assets/gaps-PjmcbaWo.js +1 -0
- package/.output/public/assets/inbox-CujRR0Cz.js +1 -0
- package/.output/public/assets/index-C9SQ_Baq.js +16 -0
- package/.output/public/assets/loop-form-Dw3cuTzw.js +1 -0
- package/.output/public/assets/loops-DcEfZa2_.js +1 -0
- package/.output/public/assets/manifests-yJpevyye.js +1 -0
- package/.output/public/assets/new-CwsnNuRW.js +1 -0
- package/.output/public/assets/page-bvnfRMum.js +1 -0
- package/.output/public/assets/play-yHLs4DjK.js +1 -0
- package/.output/public/assets/plus-Ber25lrU.js +1 -0
- package/.output/public/assets/preload-helper-BoU_7EQC.js +3 -0
- package/.output/public/assets/{refresh-cw-C3xBnKfy.js → refresh-cw-BzSY7M8I.js} +1 -1
- package/.output/public/assets/rotate-ccw-DLv9EfCX.js +1 -0
- package/.output/public/assets/runners-DzX-RU3i.js +2 -0
- package/.output/public/assets/select-C9grIFwG.js +1 -0
- package/.output/public/assets/setting-fields-DUc3npgh.js +3 -0
- package/.output/public/assets/styles-B9B98zLk.css +2 -0
- package/.output/public/assets/switch-DwABVI6d.js +1 -0
- package/.output/public/assets/task-list-Ddk8-qt7.js +1 -0
- package/.output/public/assets/textarea-Ds-gIYUQ.js +1 -0
- package/.output/public/assets/time-goBWd-5J.js +1 -0
- package/.output/public/assets/use-live-tasks-DZNNuECy.js +1 -0
- package/.output/public/assets/useCompositeListItem-P4Nmmn7j.js +1 -0
- package/.output/public/assets/usePositioner-B7i9Ejzh.js +1 -0
- package/.output/server/_chunks/ssr-renderer.mjs +1 -1
- package/.output/server/{_connectorId-C9yydJrC.mjs → _connectorId-COuQN02a.mjs} +198 -188
- package/.output/server/_libs/@base-ui/react+[...].mjs +1410 -804
- package/.output/server/_libs/@tanstack/react-router+[...].mjs +1 -1
- package/.output/server/_libs/lucide-react.mjs +93 -84
- package/.output/server/_libs/next-themes.mjs +1 -1
- package/.output/server/_libs/sonner.mjs +1 -1
- package/.output/server/_loopId-CZh6_Yhr.mjs +310 -0
- package/.output/server/_ssr/{agents-DG643yoJ.mjs → agents-B_qo1gpL.mjs} +195 -6
- package/.output/server/_ssr/{agents-C7B6RJ6-.mjs → agents-Bz10gz_m.mjs} +8 -8
- package/.output/server/_ssr/agents-CjbOUp3Z.mjs +206 -0
- package/.output/server/_ssr/{alert-DQvzE7bu.mjs → alert-C2WBWfzo.mjs} +2 -2
- package/.output/server/_ssr/card-r1cV-PCN.mjs +36 -0
- package/.output/server/_ssr/{client-BtVRVhQ2.mjs → client-Dk3SD8Il.mjs} +5 -1
- package/.output/server/_ssr/connection-status-BogAL-Fj.mjs +32 -0
- package/.output/server/_ssr/{connections-Df-aDpfJ.mjs → connections-Cqvb6Gw2.mjs} +372 -325
- package/.output/server/_ssr/connector-icon-DrhVbhs6.mjs +190 -0
- package/.output/server/_ssr/connectors-BfS5tQKr.mjs +81 -0
- package/.output/server/_ssr/{connectors-e0mf2pMs.mjs → connectors-DJI-rzPU.mjs} +10 -10
- package/.output/server/_ssr/{createServerRpc-DPDrnFdH.mjs → createServerRpc-DDrp2nLT.mjs} +2 -2
- package/.output/server/_ssr/{gaps-Cz76ZicM.mjs → gaps-CcvHiLcj.mjs} +15 -7
- package/.output/server/_ssr/inbox-D1ef761v.mjs +178 -0
- package/.output/server/_ssr/loop-form-DWwOR05R.mjs +346 -0
- package/.output/server/_ssr/{loops-D8WuK7Ke.mjs → loops-6WhUe_lc.mjs} +37 -27
- package/.output/server/_ssr/loops-DHPjzG-t.mjs +217 -0
- package/.output/server/_ssr/new-ChKBdcXJ.mjs +245 -0
- package/.output/server/_ssr/page-DZVxKvdn.mjs +120 -0
- package/.output/server/_ssr/{router-54IDWTVz.mjs → router-DdwREiOf.mjs} +162 -489
- package/.output/server/_ssr/runners-CK9Cx9fS.mjs +217 -0
- package/.output/server/_ssr/runners-CrznFPHt.mjs +140 -0
- package/.output/server/_ssr/{runners-Cj6z9Mqh.mjs → runners-W-pgo5yo.mjs} +5 -5
- package/.output/server/_ssr/{secrets-CRnpwGBh.mjs → secrets-CV5scFlt.mjs} +2 -2
- package/.output/server/_ssr/select-BtndHhHb.mjs +76 -0
- package/.output/server/_ssr/{setting-fields-DzBlslan.mjs → setting-fields-LNgywAW7.mjs} +25 -20
- package/.output/server/_ssr/ssr.mjs +43 -42
- package/.output/server/_ssr/{start-Vq3Oan3v.mjs → start-C_j-e4vF.mjs} +9 -4
- package/.output/server/_ssr/{switch-1pnd_bPJ.mjs → switch-odxNO_S4.mjs} +3 -3
- package/.output/server/_ssr/task-list-Dnmq_WeE.mjs +150 -0
- package/.output/server/_ssr/{tasks-Qtml5TlK.mjs → tasks-D-kXLgVP.mjs} +13 -13
- package/.output/server/_ssr/{tasks-BDqznm57.mjs → tasks-D7aUvA33.mjs} +44 -17
- package/.output/server/_ssr/{textarea-CHJTKtsJ.mjs → textarea-D2c_vTIC.mjs} +2 -2
- package/.output/server/_ssr/time-Cy8TZP0Z.mjs +13 -0
- package/.output/server/_ssr/use-live-tasks-BzhTkEOL.mjs +40 -0
- package/.output/server/_tanstack-start-manifest_v-2HUdn-2E.mjs +166 -0
- package/.output/server/_taskId-D3KkNJW1.mjs +263 -0
- package/.output/server/index.mjs +199 -164
- package/README.md +36 -6
- package/dist/cli.mjs +161 -8
- package/dist/dispatcher.mjs +671 -285
- package/dist/runner.mjs +333 -46
- package/drizzle/0025_prompt_tasks_done.sql +2 -0
- package/drizzle/0026_guidance_into_prompt.sql +3 -0
- package/drizzle/0027_relative_folders.sql +5 -0
- package/drizzle/0028_runner_version.sql +5 -0
- package/drizzle/meta/_journal.json +29 -1
- package/package.json +4 -2
- package/.output/public/assets/_connectorId-CkygNLny.js +0 -1
- package/.output/public/assets/_loopId-Cshlc5Ja.js +0 -1
- package/.output/public/assets/_taskId-BKDIJ_Fg.js +0 -1
- package/.output/public/assets/alert-BT2EVMJP.js +0 -1
- package/.output/public/assets/arrow-left-CWUZt6k2.js +0 -1
- package/.output/public/assets/arrow-right-dy_oOg_c.js +0 -1
- package/.output/public/assets/connection-status-DCqqmY_6.js +0 -1
- package/.output/public/assets/connector-icon-BUoK6Cru.js +0 -1
- package/.output/public/assets/connectors-CeLBtEtg.js +0 -1
- package/.output/public/assets/external-link-DC11orFA.js +0 -1
- package/.output/public/assets/gaps-BtFoD7mF.js +0 -1
- package/.output/public/assets/inbox-BYNjkNDJ.js +0 -1
- package/.output/public/assets/index-0yP3Jn_G.js +0 -18
- package/.output/public/assets/loader-B4Epchm_.js +0 -1
- package/.output/public/assets/loop-form-DnSJNc1k.js +0 -1
- package/.output/public/assets/loops-1CvbqO6m.js +0 -1
- package/.output/public/assets/new-m5NlqYQT.js +0 -1
- package/.output/public/assets/play-DD4xQgvi.js +0 -1
- package/.output/public/assets/preload-helper-CfdAYKD7.js +0 -3
- package/.output/public/assets/rolldown-runtime-CbXtAM7H.js +0 -1
- package/.output/public/assets/runners-D8QVl-Y6.js +0 -1
- package/.output/public/assets/select-Cppu-hti.js +0 -1
- package/.output/public/assets/setting-fields-bJ_gTG7Q.js +0 -3
- package/.output/public/assets/styles-ClTGaXU0.css +0 -2
- package/.output/public/assets/switch-hRIbNGOj.js +0 -1
- package/.output/public/assets/task-list-BAxjcVgr.js +0 -1
- package/.output/public/assets/textarea-Fyuzi6Vp.js +0 -1
- package/.output/public/assets/trash-2-BXZzBsoV.js +0 -1
- package/.output/public/assets/useValueChanged-VVyq4hb9.js +0 -1
- package/.output/server/_loopId-B0VG-eBC.mjs +0 -301
- package/.output/server/_ssr/connection-status-BZr3Y-IC.mjs +0 -31
- package/.output/server/_ssr/connector-icon-WvxwZ5TF.mjs +0 -56
- package/.output/server/_ssr/connectors-BU0IiRge.mjs +0 -107
- package/.output/server/_ssr/inbox-CAUBa1I3.mjs +0 -114
- package/.output/server/_ssr/loop-form-FqSaXiHl.mjs +0 -413
- package/.output/server/_ssr/loops-Dqoebe8q.mjs +0 -222
- package/.output/server/_ssr/new-bBsVaWD6.mjs +0 -169
- package/.output/server/_ssr/runners-D2UAdLyj.mjs +0 -106
- package/.output/server/_ssr/runners-DGWaVvWi.mjs +0 -116
- package/.output/server/_ssr/task-list-_qgBS9LU.mjs +0 -187
- package/.output/server/_tanstack-start-manifest_v-CvheE7ZF.mjs +0 -131
- package/.output/server/_taskId-V4QLBO2N.mjs +0 -256
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { i as __toESM } from "../_runtime.mjs";
|
|
2
|
+
import { K as require_jsx_runtime, a as MenuPortal, i as MenuPositioner, n as MenuTrigger, o as MenuPopup, q as require_react, r as MenuRoot, s as MenuItem, t as Separator } from "../_libs/@base-ui/react+[...].mjs";
|
|
3
|
+
import { f as Link, m as useRouter } from "../_libs/@tanstack/react-router+[...].mjs";
|
|
4
|
+
import { d as destinationLabel, l as connectorWorkflow, s as connectorManifest } from "./connections-Cqvb6Gw2.mjs";
|
|
5
|
+
import { n as agentManifest } from "./agents-B_qo1gpL.mjs";
|
|
6
|
+
import { B as ArrowUp, C as Ellipsis, N as CircleAlert, V as ArrowDown, i as Trash2, l as Plus } from "../_libs/lucide-react.mjs";
|
|
7
|
+
import { n as toast } from "../_libs/sonner.mjs";
|
|
8
|
+
import { P as cn, _ as Badge, b as removeLoop, s as Route$11, v as Button$1, w as toggleLoop, x as reorderLoop } from "./router-DdwREiOf.mjs";
|
|
9
|
+
import { t as ConnectorIcon } from "./connector-icon-DrhVbhs6.mjs";
|
|
10
|
+
import { a as List, o as PageHeader, t as Empty } from "./page-DZVxKvdn.mjs";
|
|
11
|
+
import { t as Switch$1 } from "./switch-odxNO_S4.mjs";
|
|
12
|
+
import { n as AlertDescription, r as AlertTitle, t as Alert } from "./alert-C2WBWfzo.mjs";
|
|
13
|
+
import { n as useLiveRefresh } from "./use-live-tasks-BzhTkEOL.mjs";
|
|
14
|
+
import { n as issuesFor, t as gapsFor } from "./gaps-CcvHiLcj.mjs";
|
|
15
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/loops-DHPjzG-t.js
|
|
16
|
+
var import_react = /* @__PURE__ */ __toESM(require_react());
|
|
17
|
+
var import_jsx_runtime = require_jsx_runtime();
|
|
18
|
+
function DropdownMenu({ ...props }) {
|
|
19
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuRoot, {
|
|
20
|
+
"data-slot": "dropdown-menu",
|
|
21
|
+
...props
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function DropdownMenuTrigger({ ...props }) {
|
|
25
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuTrigger, {
|
|
26
|
+
"data-slot": "dropdown-menu-trigger",
|
|
27
|
+
...props
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function DropdownMenuContent({ align = "start", alignOffset = 0, side = "bottom", sideOffset = 4, className, ...props }) {
|
|
31
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuPortal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuPositioner, {
|
|
32
|
+
className: "isolate z-50 outline-none",
|
|
33
|
+
align,
|
|
34
|
+
alignOffset,
|
|
35
|
+
side,
|
|
36
|
+
sideOffset,
|
|
37
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuPopup, {
|
|
38
|
+
"data-slot": "dropdown-menu-content",
|
|
39
|
+
className: cn("z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95", className),
|
|
40
|
+
...props
|
|
41
|
+
})
|
|
42
|
+
}) });
|
|
43
|
+
}
|
|
44
|
+
function DropdownMenuItem({ className, inset, variant = "default", ...props }) {
|
|
45
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MenuItem, {
|
|
46
|
+
"data-slot": "dropdown-menu-item",
|
|
47
|
+
"data-inset": inset,
|
|
48
|
+
"data-variant": variant,
|
|
49
|
+
className: cn("group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive", className),
|
|
50
|
+
...props
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function DropdownMenuSeparator({ className, ...props }) {
|
|
54
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Separator, {
|
|
55
|
+
"data-slot": "dropdown-menu-separator",
|
|
56
|
+
className: cn("-mx-1 my-1 h-px bg-border", className),
|
|
57
|
+
...props
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function LoopsPage() {
|
|
61
|
+
const { loops, readiness } = Route$11.useLoaderData();
|
|
62
|
+
useLiveRefresh();
|
|
63
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
64
|
+
className: "flex flex-col gap-6",
|
|
65
|
+
children: [
|
|
66
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageHeader, {
|
|
67
|
+
title: "Loops",
|
|
68
|
+
description: "When two loops match the same thing, the higher one runs.",
|
|
69
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
|
|
70
|
+
nativeButton: false,
|
|
71
|
+
render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Link, { to: "/loops/new" }),
|
|
72
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Plus, {}), "New loop"]
|
|
73
|
+
})
|
|
74
|
+
}),
|
|
75
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Gaps, {
|
|
76
|
+
readiness,
|
|
77
|
+
loops
|
|
78
|
+
}),
|
|
79
|
+
loops.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Empty, {
|
|
80
|
+
action: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
|
|
81
|
+
variant: "outline",
|
|
82
|
+
nativeButton: false,
|
|
83
|
+
render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Link, { to: "/loops/new" }),
|
|
84
|
+
children: "New loop"
|
|
85
|
+
}),
|
|
86
|
+
children: "No loops yet."
|
|
87
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(List, { children: loops.map((loop, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoopRow, {
|
|
88
|
+
loop,
|
|
89
|
+
readiness,
|
|
90
|
+
position: index + 1,
|
|
91
|
+
first: index === 0,
|
|
92
|
+
last: index === loops.length - 1
|
|
93
|
+
}, loop.id)) })
|
|
94
|
+
]
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/** What still stands between these loops and running. */
|
|
98
|
+
function Gaps({ readiness, loops }) {
|
|
99
|
+
const missing = gapsFor(readiness, loops);
|
|
100
|
+
if (missing.length === 0) return null;
|
|
101
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Alert, { children: [
|
|
102
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CircleAlert, {}),
|
|
103
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertTitle, { children: "Nothing will run yet" }),
|
|
104
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDescription, {
|
|
105
|
+
className: "flex flex-col gap-0.5",
|
|
106
|
+
children: missing.map((line) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: line }, line))
|
|
107
|
+
})
|
|
108
|
+
] });
|
|
109
|
+
}
|
|
110
|
+
/** Where this loop answers and with what, in a few words. */
|
|
111
|
+
function summaryOf(loop) {
|
|
112
|
+
if (!connectorWorkflow(loop.connectorId, loop.workflowId)) return "Workflow no longer offered";
|
|
113
|
+
const agent = loop.agentId ? agentManifest(loop.agentId) : void 0;
|
|
114
|
+
return [destinationLabel(loop.actionConnectorId, loop.actionId), agent?.name ?? "Default agent"].join(" · ");
|
|
115
|
+
}
|
|
116
|
+
function LoopRow({ loop, readiness, position, first, last }) {
|
|
117
|
+
const router = useRouter();
|
|
118
|
+
const manifest = connectorManifest(loop.connectorId);
|
|
119
|
+
const issues = issuesFor(readiness, loop);
|
|
120
|
+
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
121
|
+
const run = async (work, message) => {
|
|
122
|
+
setBusy(true);
|
|
123
|
+
try {
|
|
124
|
+
await work();
|
|
125
|
+
await router.invalidate();
|
|
126
|
+
if (message) toast.success(message);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
toast.error(error instanceof Error ? error.message : String(error));
|
|
129
|
+
} finally {
|
|
130
|
+
setBusy(false);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
134
|
+
className: "flex items-center gap-3 px-4 py-3",
|
|
135
|
+
children: [
|
|
136
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
137
|
+
className: "w-4 shrink-0 text-center text-xs text-muted-foreground tabular-nums",
|
|
138
|
+
children: position
|
|
139
|
+
}),
|
|
140
|
+
manifest ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConnectorIcon, {
|
|
141
|
+
icon: manifest.icon,
|
|
142
|
+
accent: manifest.accent,
|
|
143
|
+
className: loop.enabled ? "size-8" : "size-8 opacity-50"
|
|
144
|
+
}) : null,
|
|
145
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Link, {
|
|
146
|
+
to: "/loops/$loopId",
|
|
147
|
+
params: { loopId: loop.id },
|
|
148
|
+
className: "group min-w-0 flex-1",
|
|
149
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
150
|
+
className: "flex items-center gap-2",
|
|
151
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
152
|
+
className: "truncate text-sm font-medium group-hover:underline",
|
|
153
|
+
children: loop.name
|
|
154
|
+
}), loop.enabled ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
|
|
155
|
+
variant: "secondary",
|
|
156
|
+
children: "Off"
|
|
157
|
+
})]
|
|
158
|
+
}), issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
|
|
159
|
+
className: "mt-0.5 flex items-center gap-1 truncate text-xs text-amber-700 dark:text-amber-500",
|
|
160
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CircleAlert, { className: "size-3 shrink-0" }), issues[0]]
|
|
161
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
162
|
+
className: "mt-0.5 truncate text-xs text-muted-foreground",
|
|
163
|
+
children: summaryOf(loop)
|
|
164
|
+
})]
|
|
165
|
+
}),
|
|
166
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Switch$1, {
|
|
167
|
+
checked: loop.enabled,
|
|
168
|
+
"aria-label": loop.enabled ? "Turn off" : "Turn on",
|
|
169
|
+
disabled: busy,
|
|
170
|
+
onCheckedChange: (enabled) => run(() => toggleLoop({ data: {
|
|
171
|
+
id: loop.id,
|
|
172
|
+
enabled
|
|
173
|
+
} }), enabled ? `${loop.name} is on` : `${loop.name} is off`)
|
|
174
|
+
}),
|
|
175
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenu, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenuTrigger, {
|
|
176
|
+
render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
|
|
177
|
+
variant: "ghost",
|
|
178
|
+
size: "icon-sm",
|
|
179
|
+
"aria-label": "More",
|
|
180
|
+
disabled: busy
|
|
181
|
+
}),
|
|
182
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Ellipsis, {})
|
|
183
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuContent, {
|
|
184
|
+
align: "end",
|
|
185
|
+
children: [
|
|
186
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuItem, {
|
|
187
|
+
disabled: first,
|
|
188
|
+
onClick: () => run(() => reorderLoop({ data: {
|
|
189
|
+
id: loop.id,
|
|
190
|
+
direction: "up"
|
|
191
|
+
} })),
|
|
192
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowUp, {}), "Move up"]
|
|
193
|
+
}),
|
|
194
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuItem, {
|
|
195
|
+
disabled: last,
|
|
196
|
+
onClick: () => run(() => reorderLoop({ data: {
|
|
197
|
+
id: loop.id,
|
|
198
|
+
direction: "down"
|
|
199
|
+
} })),
|
|
200
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowDown, {}), "Move down"]
|
|
201
|
+
}),
|
|
202
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenuSeparator, {}),
|
|
203
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenuItem, {
|
|
204
|
+
variant: "destructive",
|
|
205
|
+
onClick: () => {
|
|
206
|
+
if (!window.confirm(`Delete ${loop.name}? Its runs are deleted with it.`)) return;
|
|
207
|
+
run(() => removeLoop({ data: { id: loop.id } }), `Deleted ${loop.name}`);
|
|
208
|
+
},
|
|
209
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Trash2, {}), "Delete"]
|
|
210
|
+
})
|
|
211
|
+
]
|
|
212
|
+
})] })
|
|
213
|
+
]
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
//#endregion
|
|
217
|
+
export { LoopsPage as component };
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { K as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
|
|
2
|
+
import { f as Link, p as useNavigate } from "../_libs/@tanstack/react-router+[...].mjs";
|
|
3
|
+
import { l as connectorWorkflow, n as allTriggers, r as allWorkflows, s as connectorManifest } from "./connections-Cqvb6Gw2.mjs";
|
|
4
|
+
import { f as PencilLine } from "../_libs/lucide-react.mjs";
|
|
5
|
+
import { a as Route$9 } from "./router-DdwREiOf.mjs";
|
|
6
|
+
import { t as ConnectorIcon } from "./connector-icon-DrhVbhs6.mjs";
|
|
7
|
+
import { n as Field, o as PageHeader } from "./page-DZVxKvdn.mjs";
|
|
8
|
+
import { a as SelectValue, i as SelectTrigger, n as SelectContent, r as SelectItem, t as Select$1 } from "./select-BtndHhHb.mjs";
|
|
9
|
+
import { t as LoopForm } from "./loop-form-DWwOR05R.mjs";
|
|
10
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/new-ChKBdcXJ.js
|
|
11
|
+
var import_jsx_runtime = require_jsx_runtime();
|
|
12
|
+
/**
|
|
13
|
+
* One of a connector's workflows, as a loop nobody has saved yet.
|
|
14
|
+
*
|
|
15
|
+
* Nothing here touches the database, which is the point: choosing a workflow
|
|
16
|
+
* used to create the loop, so a look around the page left a live loop behind,
|
|
17
|
+
* enabled and with none of its questions answered. Now choosing only fills a
|
|
18
|
+
* form in, and the loop begins at Save.
|
|
19
|
+
*
|
|
20
|
+
* Deliberately client-safe, so the form can be filled in without a round trip
|
|
21
|
+
* and without anything on the server having to remember a half-made loop.
|
|
22
|
+
*/
|
|
23
|
+
function draftForWorkflow(connectorId, workflowId) {
|
|
24
|
+
const workflow = connectorWorkflow(connectorId, workflowId);
|
|
25
|
+
if (!workflow) throw new Error(`Unknown workflow: ${workflowId}`);
|
|
26
|
+
return {
|
|
27
|
+
id: null,
|
|
28
|
+
name: workflow.name,
|
|
29
|
+
connectorId,
|
|
30
|
+
workflowId,
|
|
31
|
+
prompt: workflow.prompt,
|
|
32
|
+
guidance: null,
|
|
33
|
+
agentId: null,
|
|
34
|
+
settings: {},
|
|
35
|
+
actionConnectorId: workflow.actionConnectorId ?? connectorId,
|
|
36
|
+
actionId: workflow.actionId,
|
|
37
|
+
actionTarget: workflow.actionTarget ?? {},
|
|
38
|
+
pollEveryMs: workflow.pollEveryMs ?? null,
|
|
39
|
+
enabled: true
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** The trigger a custom loop starts on before anything is picked. */
|
|
43
|
+
var CUSTOM_DEFAULT = {
|
|
44
|
+
connector: "slack",
|
|
45
|
+
workflow: "slack.ask"
|
|
46
|
+
};
|
|
47
|
+
function NewLoopPage() {
|
|
48
|
+
const { connector, workflow, custom } = Route$9.useSearch();
|
|
49
|
+
const chosen = connector && workflow && connectorWorkflow(connector, workflow) ? {
|
|
50
|
+
connector,
|
|
51
|
+
workflow
|
|
52
|
+
} : null;
|
|
53
|
+
if (custom) {
|
|
54
|
+
const start = chosen ?? CUSTOM_DEFAULT;
|
|
55
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CustomLoop, {
|
|
56
|
+
connectorId: start.connector,
|
|
57
|
+
workflowId: start.workflow
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return chosen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShapeLoop, {
|
|
61
|
+
connectorId: chosen.connector,
|
|
62
|
+
workflowId: chosen.workflow
|
|
63
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChooseWorkflow, {});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The second step, and the one that makes a loop. Nothing is written down
|
|
67
|
+
* until the form is saved.
|
|
68
|
+
*/
|
|
69
|
+
function ShapeLoop({ connectorId, workflowId }) {
|
|
70
|
+
const workflow = connectorWorkflow(connectorId, workflowId);
|
|
71
|
+
const connector = connectorManifest(connectorId);
|
|
72
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
73
|
+
className: "flex flex-col gap-8",
|
|
74
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageHeader, {
|
|
75
|
+
back: {
|
|
76
|
+
to: "/loops/new",
|
|
77
|
+
label: "New loop"
|
|
78
|
+
},
|
|
79
|
+
icon: connector ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConnectorIcon, {
|
|
80
|
+
icon: connector.icon,
|
|
81
|
+
accent: connector.accent
|
|
82
|
+
}) : null,
|
|
83
|
+
title: workflow.name,
|
|
84
|
+
description: workflow.summary
|
|
85
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoopForm, { loop: draftForWorkflow(connectorId, workflowId) })]
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* A loop with your own prompt. Underneath it is still one of the workflows,
|
|
90
|
+
* since a workflow is what knows how to watch for something and read the
|
|
91
|
+
* answer, but it starts with an empty prompt and a trigger you pick.
|
|
92
|
+
*/
|
|
93
|
+
function CustomLoop({ connectorId, workflowId }) {
|
|
94
|
+
const navigate = useNavigate();
|
|
95
|
+
const draft = {
|
|
96
|
+
...draftForWorkflow(connectorId, workflowId),
|
|
97
|
+
name: "Custom loop",
|
|
98
|
+
prompt: ""
|
|
99
|
+
};
|
|
100
|
+
const picker = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, {
|
|
101
|
+
label: "Starts when",
|
|
102
|
+
htmlFor: "custom-trigger",
|
|
103
|
+
className: "sm:max-w-sm",
|
|
104
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
|
|
105
|
+
value: `${connectorId}::${workflowId}`,
|
|
106
|
+
onValueChange: (value) => {
|
|
107
|
+
if (!value) return;
|
|
108
|
+
const [connector, workflow] = value.split("::");
|
|
109
|
+
navigate({
|
|
110
|
+
to: "/loops/new",
|
|
111
|
+
search: {
|
|
112
|
+
custom: true,
|
|
113
|
+
connector,
|
|
114
|
+
workflow
|
|
115
|
+
},
|
|
116
|
+
replace: true
|
|
117
|
+
});
|
|
118
|
+
},
|
|
119
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectTrigger, {
|
|
120
|
+
id: "custom-trigger",
|
|
121
|
+
className: "w-full",
|
|
122
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectValue, { children: () => {
|
|
123
|
+
return connectorWorkflow(connectorId, workflowId)?.when ?? workflowId;
|
|
124
|
+
} })
|
|
125
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectContent, { children: allTriggers().map(({ connector, trigger }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectItem, {
|
|
126
|
+
value: `${connector.id}::${trigger.id}`,
|
|
127
|
+
children: trigger.when
|
|
128
|
+
}, `${connector.id}::${trigger.id}`)) })]
|
|
129
|
+
})
|
|
130
|
+
});
|
|
131
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
132
|
+
className: "flex flex-col gap-8",
|
|
133
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageHeader, {
|
|
134
|
+
back: {
|
|
135
|
+
to: "/loops/new",
|
|
136
|
+
label: "New loop"
|
|
137
|
+
},
|
|
138
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CustomIcon, {}),
|
|
139
|
+
title: "Custom loop",
|
|
140
|
+
description: "Pick what starts it, then say what the agent should do."
|
|
141
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoopForm, {
|
|
142
|
+
loop: draft,
|
|
143
|
+
trigger: picker
|
|
144
|
+
}, workflowId)]
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
function CustomIcon() {
|
|
148
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
149
|
+
className: "flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground",
|
|
150
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PencilLine, { className: "size-5" })
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/** How many loops already use a workflow, said as a sentence rather than a count. */
|
|
154
|
+
function inUse(loops, workflowId) {
|
|
155
|
+
const count = loops.filter((loop) => loop.workflowId === workflowId).length;
|
|
156
|
+
if (count === 0) return null;
|
|
157
|
+
return count === 1 ? "You have 1 loop like this" : `You have ${count} loops like this`;
|
|
158
|
+
}
|
|
159
|
+
function ChoiceCard({ icon, title, summary, note, ...link }) {
|
|
160
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Link, {
|
|
161
|
+
to: "/loops/new",
|
|
162
|
+
search: link.search,
|
|
163
|
+
className: "flex items-start gap-3 rounded-xl bg-card p-4 ring-1 ring-foreground/10 transition-colors hover:bg-muted/50",
|
|
164
|
+
children: [icon, /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
165
|
+
className: "min-w-0 flex-1",
|
|
166
|
+
children: [
|
|
167
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
168
|
+
className: "text-sm font-medium",
|
|
169
|
+
children: title
|
|
170
|
+
}),
|
|
171
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
172
|
+
className: "mt-1 text-xs text-muted-foreground",
|
|
173
|
+
children: summary
|
|
174
|
+
}),
|
|
175
|
+
note ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
176
|
+
className: "mt-2 text-xs text-muted-foreground",
|
|
177
|
+
children: note
|
|
178
|
+
}) : null
|
|
179
|
+
]
|
|
180
|
+
})]
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
function workflowCard(connector, workflow, loops) {
|
|
184
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChoiceCard, {
|
|
185
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConnectorIcon, {
|
|
186
|
+
icon: connector.icon,
|
|
187
|
+
accent: connector.accent,
|
|
188
|
+
className: "size-8"
|
|
189
|
+
}),
|
|
190
|
+
title: workflow.name,
|
|
191
|
+
summary: workflow.summary,
|
|
192
|
+
note: inUse(loops, workflow.id),
|
|
193
|
+
search: {
|
|
194
|
+
connector: connector.id,
|
|
195
|
+
workflow: workflow.id
|
|
196
|
+
}
|
|
197
|
+
}, workflow.id);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Every loop is one of the workflows a connector offers, or a custom one
|
|
201
|
+
* built on a workflow's trigger. Custom and Schedule come first: they are the
|
|
202
|
+
* two not tied to something arriving from a service.
|
|
203
|
+
*/
|
|
204
|
+
function ChooseWorkflow() {
|
|
205
|
+
const { loops } = Route$9.useLoaderData();
|
|
206
|
+
const all = allWorkflows();
|
|
207
|
+
const scheduled = all.filter(({ connector }) => connector.id === "schedule");
|
|
208
|
+
const rest = all.filter(({ connector }) => connector.id !== "schedule");
|
|
209
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
210
|
+
className: "flex flex-col gap-6",
|
|
211
|
+
children: [
|
|
212
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageHeader, {
|
|
213
|
+
back: {
|
|
214
|
+
to: "/loops",
|
|
215
|
+
label: "Loops"
|
|
216
|
+
},
|
|
217
|
+
title: "New loop"
|
|
218
|
+
}),
|
|
219
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
220
|
+
className: "grid gap-3 sm:grid-cols-2",
|
|
221
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChoiceCard, {
|
|
222
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
223
|
+
className: "flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted",
|
|
224
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PencilLine, { className: "size-4" })
|
|
225
|
+
}),
|
|
226
|
+
title: "Custom loop",
|
|
227
|
+
summary: "Pick a trigger and write your own prompt.",
|
|
228
|
+
search: { custom: true }
|
|
229
|
+
}), scheduled.map(({ connector, workflow }) => workflowCard(connector, workflow, loops))]
|
|
230
|
+
}),
|
|
231
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
232
|
+
className: "flex flex-col gap-3",
|
|
233
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
|
|
234
|
+
className: "text-sm font-medium text-muted-foreground",
|
|
235
|
+
children: "From a connector"
|
|
236
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
237
|
+
className: "grid gap-3 sm:grid-cols-2",
|
|
238
|
+
children: rest.map(({ connector, workflow }) => workflowCard(connector, workflow, loops))
|
|
239
|
+
})]
|
|
240
|
+
})
|
|
241
|
+
]
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
//#endregion
|
|
245
|
+
export { NewLoopPage as component };
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import "../_runtime.mjs";
|
|
2
|
+
import { K as require_jsx_runtime, q as require_react } from "../_libs/@base-ui/react+[...].mjs";
|
|
3
|
+
import { f as Link } from "../_libs/@tanstack/react-router+[...].mjs";
|
|
4
|
+
import { I as ChevronLeft } from "../_libs/lucide-react.mjs";
|
|
5
|
+
import { P as cn } from "./router-DdwREiOf.mjs";
|
|
6
|
+
require_react();
|
|
7
|
+
var import_jsx_runtime = require_jsx_runtime();
|
|
8
|
+
function Label({ className, ...props }) {
|
|
9
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", {
|
|
10
|
+
"data-slot": "label",
|
|
11
|
+
className: cn("flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", className),
|
|
12
|
+
...props
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The top of every page: where it sits, what it is called, and what can be
|
|
17
|
+
* done from here. One shape everywhere, so pages differ in what they show
|
|
18
|
+
* rather than in how far the title is from the edge.
|
|
19
|
+
*/
|
|
20
|
+
function PageHeader({ title, description, back, icon, actions, children }) {
|
|
21
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
|
|
22
|
+
className: "flex flex-col gap-3",
|
|
23
|
+
children: [back ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Link, {
|
|
24
|
+
to: back.to,
|
|
25
|
+
params: back.params,
|
|
26
|
+
search: back.search,
|
|
27
|
+
className: "-ml-1 flex w-fit items-center gap-0.5 text-sm text-muted-foreground hover:text-foreground",
|
|
28
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronLeft, { className: "size-4" }), back.label]
|
|
29
|
+
}) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
30
|
+
className: "flex flex-wrap items-start gap-3",
|
|
31
|
+
children: [
|
|
32
|
+
icon,
|
|
33
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
34
|
+
className: "min-w-0 flex-1 basis-56",
|
|
35
|
+
children: [
|
|
36
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
|
|
37
|
+
className: "text-xl font-semibold tracking-tight",
|
|
38
|
+
children: title
|
|
39
|
+
}),
|
|
40
|
+
description ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
41
|
+
className: "mt-1 text-sm text-muted-foreground",
|
|
42
|
+
children: description
|
|
43
|
+
}) : null,
|
|
44
|
+
children
|
|
45
|
+
]
|
|
46
|
+
}),
|
|
47
|
+
actions ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
48
|
+
className: "flex shrink-0 items-center gap-2",
|
|
49
|
+
children: actions
|
|
50
|
+
}) : null
|
|
51
|
+
]
|
|
52
|
+
})]
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/** A labelled control, with at most one short line of help under it. */
|
|
56
|
+
function Field({ label, htmlFor, hint, className, children }) {
|
|
57
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
58
|
+
className: cn("flex flex-col gap-1.5", className),
|
|
59
|
+
children: [
|
|
60
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, {
|
|
61
|
+
htmlFor,
|
|
62
|
+
children: label
|
|
63
|
+
}),
|
|
64
|
+
children,
|
|
65
|
+
hint ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
66
|
+
className: "text-xs text-muted-foreground",
|
|
67
|
+
children: hint
|
|
68
|
+
}) : null
|
|
69
|
+
]
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* One titled block of a form or a detail page. Title on the left and fields
|
|
74
|
+
* on the right on a wide screen, stacked on a narrow one.
|
|
75
|
+
*/
|
|
76
|
+
function Section({ title, description, children, className }) {
|
|
77
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
|
|
78
|
+
className: cn("grid gap-4 border-t py-6 first:border-t-0 first:pt-0 md:grid-cols-[11rem_1fr] md:gap-8", className),
|
|
79
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
|
|
80
|
+
className: "text-sm font-medium",
|
|
81
|
+
children: title
|
|
82
|
+
}), description ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
83
|
+
className: "mt-1 text-xs text-muted-foreground",
|
|
84
|
+
children: description
|
|
85
|
+
}) : null] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
86
|
+
className: "flex min-w-0 flex-col gap-4",
|
|
87
|
+
children
|
|
88
|
+
})]
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/** A list of rows inside one bordered box, rather than a stack of cards. */
|
|
92
|
+
function List({ className, children }) {
|
|
93
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
94
|
+
className: cn("divide-y overflow-hidden rounded-xl bg-card ring-1 ring-foreground/10", className),
|
|
95
|
+
children
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/** What a list says when it has nothing in it. */
|
|
99
|
+
function Empty({ children, action }) {
|
|
100
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
101
|
+
className: "flex flex-col items-center gap-3 rounded-xl border border-dashed px-6 py-10 text-center text-sm text-muted-foreground",
|
|
102
|
+
children: [children, action]
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/** Tabs that are links, so a tab can be bookmarked and shared. */
|
|
106
|
+
function LinkTabs({ items }) {
|
|
107
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("nav", {
|
|
108
|
+
className: "flex gap-4 border-b",
|
|
109
|
+
children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Link, {
|
|
110
|
+
to: item.to,
|
|
111
|
+
params: item.params,
|
|
112
|
+
search: item.search,
|
|
113
|
+
replace: true,
|
|
114
|
+
className: cn("-mb-px flex items-center gap-1.5 border-b-2 pb-2 text-sm transition-colors", item.active ? "border-foreground font-medium text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"),
|
|
115
|
+
children: item.label
|
|
116
|
+
}, index))
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
export { List as a, LinkTabs as i, Field as n, PageHeader as o, Label as r, Section as s, Empty as t };
|