@ventually/ui 0.0.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.
@@ -0,0 +1,273 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import "./index.css";
3
+ import { useEffect, useRef, useState, } from "react";
4
+ import { useEventuallyUI } from "./context.js";
5
+ const LIVE_EVENTS_MIN_HEIGHT = 144;
6
+ const LIVE_EVENTS_MAX_HEIGHT = 480;
7
+ function formatRelative(value) {
8
+ const delta = Math.max(0, Date.now() - value);
9
+ if (delta < 1_000)
10
+ return `${delta}ms`;
11
+ if (delta < 60_000)
12
+ return `${Math.floor(delta / 1_000)}s ago`;
13
+ return `${Math.floor(delta / 60_000)}m ago`;
14
+ }
15
+ function formatDuration(job) {
16
+ if (job.state === "delayed") {
17
+ const remaining = Math.max(0, job.availableAt - Date.now());
18
+ if (remaining < 60_000)
19
+ return `in ${Math.ceil(remaining / 1_000)}s`;
20
+ const m = Math.floor(remaining / 60_000);
21
+ const s = Math.floor((remaining % 60_000) / 1_000);
22
+ return `in ${m}m ${s}s`;
23
+ }
24
+ if (job.finishedAt && job.processedAt)
25
+ return `${job.finishedAt - job.processedAt}ms`;
26
+ if (job.processedAt)
27
+ return `${Math.max(0, Date.now() - job.processedAt)}ms`;
28
+ return "—";
29
+ }
30
+ const STATE_STYLES = {
31
+ active: {
32
+ dot: "bg-amber-400",
33
+ badge: "bg-amber-400/10 text-amber-400 ring-1 ring-amber-400/25",
34
+ label: "active",
35
+ },
36
+ waiting: {
37
+ dot: "bg-blue-400",
38
+ badge: "bg-blue-400/10 text-blue-400 ring-1 ring-blue-400/25",
39
+ label: "waiting",
40
+ },
41
+ completed: {
42
+ dot: "bg-emerald-400",
43
+ badge: "bg-emerald-400/10 text-emerald-400 ring-1 ring-emerald-400/25",
44
+ label: "completed",
45
+ },
46
+ failed: {
47
+ dot: "bg-red-400",
48
+ badge: "bg-red-400/10 text-red-400 ring-1 ring-red-400/25",
49
+ label: "failed",
50
+ },
51
+ delayed: {
52
+ dot: "bg-violet-400",
53
+ badge: "bg-violet-400/10 text-violet-400 ring-1 ring-violet-400/25",
54
+ label: "delayed",
55
+ },
56
+ blocked: {
57
+ dot: "bg-violet-400",
58
+ badge: "bg-violet-400/10 text-violet-400 ring-1 ring-violet-400/25",
59
+ label: "blocked",
60
+ },
61
+ };
62
+ const QUEUE_ACCENT = [
63
+ "#f59e0b",
64
+ "#34d399",
65
+ "#60a5fa",
66
+ "#a78bfa",
67
+ "#f87171",
68
+ "#fb923c",
69
+ ];
70
+ /* ── small reusable components ── */
71
+ function Badge({ state }) {
72
+ const s = STATE_STYLES[state];
73
+ return (_jsxs("span", { className: `inline-flex items-center gap-1.5 px-2 py-0.5 rounded text-[10px] font-mono font-medium tracking-wide ${s.badge}`, children: [_jsx("span", { className: `w-1.5 h-1.5 rounded-full ${s.dot} ${state === "active" ? "animate-pulse" : ""}` }), s.label] }));
74
+ }
75
+ function Stat({ label, value, color, }) {
76
+ return (_jsxs("div", { className: "flex flex-col gap-1 min-w-0", children: [_jsx("span", { className: "text-[9px] uppercase tracking-[0.12em] text-zinc-500 font-medium", children: label }), _jsx("span", { className: `text-2xl font-mono font-semibold leading-none ${color}`, children: value })] }));
77
+ }
78
+ /* ── Config Modal ── */
79
+ function ConfigModal({ config, workerPaused, queueName, onClose, }) {
80
+ const backdropRef = useRef(null);
81
+ useEffect(() => {
82
+ const handler = (e) => {
83
+ if (e.key === "Escape")
84
+ onClose();
85
+ };
86
+ window.addEventListener("keydown", handler);
87
+ return () => window.removeEventListener("keydown", handler);
88
+ }, [onClose]);
89
+ const rows = [
90
+ ["Purpose", String(config.description ?? "—")],
91
+ ["Concurrency", String(config.concurrency ?? "—")],
92
+ [
93
+ "Poll Interval",
94
+ config.pollIntervalMs != null ? `${config.pollIntervalMs}ms` : "—",
95
+ ],
96
+ ["Attempts", String(config.attempts ?? "—")],
97
+ ["Keep Completed", String(config.removeOnCompleteCount ?? "—")],
98
+ ["Keep Failed", String(config.removeOnFailCount ?? "—")],
99
+ ["Recurring", config.recurringEnabled ? "enabled" : "off"],
100
+ ["Worker", workerPaused ? "paused" : "running"],
101
+ ];
102
+ return (_jsx("div", { ref: backdropRef, className: "fixed inset-0 z-50 flex items-center justify-center p-4", style: { background: "rgba(0,0,0,0.65)", backdropFilter: "blur(4px)" }, onClick: (e) => {
103
+ if (e.target === backdropRef.current)
104
+ onClose();
105
+ }, children: _jsxs("div", { className: "w-full max-w-lg bg-zinc-900 border border-zinc-800 rounded-xl shadow-2xl overflow-hidden", children: [_jsxs("div", { className: "flex items-center justify-between px-5 py-4 border-b border-zinc-800", children: [_jsxs("div", { children: [_jsx("p", { className: "text-xs uppercase tracking-[0.12em] text-zinc-500 font-medium mb-0.5", children: "Configuration" }), _jsx("h2", { className: "text-sm font-mono font-semibold text-zinc-100", children: queueName })] }), _jsx("button", { type: "button", onClick: onClose, className: "w-7 h-7 flex items-center justify-center rounded text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800 transition-colors", children: _jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: _jsx("path", { d: "M1 1l12 12M13 1L1 13" }) }) })] }), _jsx("div", { className: "p-5 grid grid-cols-2 gap-3", children: rows.map(([label, value], i) => (_jsxs("div", { className: `${i === 0 ? "col-span-2" : ""} bg-zinc-800/50 border border-zinc-800 rounded-lg px-4 py-3`, children: [_jsx("p", { className: "text-[9px] uppercase tracking-[0.12em] text-zinc-500 font-medium mb-1.5", children: label }), _jsx("p", { className: "text-xs font-mono text-zinc-200 leading-relaxed", children: value })] }, label))) })] }) }));
106
+ }
107
+ /* ── Add Job Modal ── */
108
+ function AddJobModal({ queueName, onClose, onSubmit, }) {
109
+ const [payload, setPayload] = useState("{\n \n}");
110
+ const [error, setError] = useState(null);
111
+ const [loading, setLoading] = useState(false);
112
+ const backdropRef = useRef(null);
113
+ useEffect(() => {
114
+ const handler = (e) => {
115
+ if (e.key === "Escape")
116
+ onClose();
117
+ };
118
+ window.addEventListener("keydown", handler);
119
+ return () => window.removeEventListener("keydown", handler);
120
+ }, [onClose]);
121
+ async function handleSubmit() {
122
+ setLoading(true);
123
+ const err = await onSubmit(payload);
124
+ setLoading(false);
125
+ if (err)
126
+ setError(err);
127
+ }
128
+ return (_jsx("div", { ref: backdropRef, className: "fixed inset-0 z-50 flex items-center justify-center p-4", style: { background: "rgba(0,0,0,0.65)", backdropFilter: "blur(4px)" }, onClick: (e) => {
129
+ if (e.target === backdropRef.current)
130
+ onClose();
131
+ }, children: _jsxs("div", { className: "w-full max-w-md bg-zinc-900 border border-zinc-800 rounded-xl shadow-2xl overflow-hidden", children: [_jsxs("div", { className: "flex items-center justify-between px-5 py-4 border-b border-zinc-800", children: [_jsxs("div", { children: [_jsx("p", { className: "text-xs uppercase tracking-[0.12em] text-zinc-500 font-medium mb-0.5", children: "Enqueue job" }), _jsx("h2", { className: "text-sm font-mono font-semibold text-zinc-100", children: queueName })] }), _jsx("button", { type: "button", onClick: onClose, className: "w-7 h-7 flex items-center justify-center rounded text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800 transition-colors", children: _jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: _jsx("path", { d: "M1 1l12 12M13 1L1 13" }) }) })] }), _jsxs("div", { className: "p-5 space-y-3", children: [_jsx("p", { className: "text-xs text-zinc-500", children: "Provide a JSON payload for the new job." }), _jsx("textarea", { className: "w-full h-44 resize-y bg-zinc-950 border border-zinc-800 focus:border-zinc-600 rounded-lg text-xs font-mono text-zinc-200 p-3 outline-none transition-colors", value: payload, onChange: (e) => {
132
+ setPayload(e.target.value);
133
+ setError(null);
134
+ }, spellCheck: false }), error && (_jsx("p", { className: "text-[11px] text-red-400 bg-red-400/8 border border-red-400/20 rounded-lg px-3 py-2", children: error })), _jsxs("div", { className: "flex justify-end gap-2 pt-1", children: [_jsx("button", { type: "button", onClick: onClose, className: "px-4 py-1.5 rounded text-xs font-mono text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 border border-zinc-800 hover:border-zinc-700 transition-colors", children: "Cancel" }), _jsx("button", { type: "button", onClick: () => void handleSubmit(), disabled: loading, className: "px-4 py-1.5 rounded text-xs font-mono font-medium bg-zinc-100 text-zinc-900 hover:bg-white disabled:opacity-40 transition-colors", children: loading ? "Running…" : "Run job" })] })] })] }) }));
135
+ }
136
+ /* ── Main Dashboard ── */
137
+ export function EventuallyDashboard() {
138
+ const { error, pending, perform, snapshot, stale } = useEventuallyUI();
139
+ const [selectedQueue, setSelectedQueue] = useState(null);
140
+ const [sidebarOpen, setSidebarOpen] = useState(typeof window !== "undefined" ? window.innerWidth >= 768 : true);
141
+ const [configOpen, setConfigOpen] = useState(false);
142
+ const [addJobOpen, setAddJobOpen] = useState(false);
143
+ const [liveEventsHeight, setLiveEventsHeight] = useState(220);
144
+ const liveEventsResizeRef = useRef(null);
145
+ /* close sidebar by default on small screens */
146
+ useEffect(() => {
147
+ const mq = window.matchMedia("(max-width: 767px)");
148
+ const handle = (e) => setSidebarOpen(!e.matches);
149
+ mq.addEventListener("change", handle);
150
+ return () => mq.removeEventListener("change", handle);
151
+ }, []);
152
+ /* deselect queue if it disappears */
153
+ useEffect(() => {
154
+ if (!selectedQueue)
155
+ return;
156
+ if (!snapshot?.queues.some((q) => q.name === selectedQueue))
157
+ setSelectedQueue(null);
158
+ }, [selectedQueue, snapshot]);
159
+ useEffect(() => {
160
+ const handlePointerMove = (event) => {
161
+ const resizeState = liveEventsResizeRef.current;
162
+ if (!resizeState)
163
+ return;
164
+ const delta = resizeState.startY - event.clientY;
165
+ const maxHeight = Math.min(window.innerHeight * 0.6, LIVE_EVENTS_MAX_HEIGHT);
166
+ setLiveEventsHeight(Math.round(Math.max(LIVE_EVENTS_MIN_HEIGHT, Math.min(maxHeight, resizeState.startHeight + delta))));
167
+ };
168
+ const handlePointerUp = (event) => {
169
+ if (liveEventsResizeRef.current?.pointerId !== event.pointerId)
170
+ return;
171
+ liveEventsResizeRef.current = null;
172
+ document.body.style.userSelect = "";
173
+ document.body.style.cursor = "";
174
+ };
175
+ window.addEventListener("pointermove", handlePointerMove);
176
+ window.addEventListener("pointerup", handlePointerUp);
177
+ return () => {
178
+ window.removeEventListener("pointermove", handlePointerMove);
179
+ window.removeEventListener("pointerup", handlePointerUp);
180
+ document.body.style.userSelect = "";
181
+ document.body.style.cursor = "";
182
+ };
183
+ }, []);
184
+ const queue = selectedQueue
185
+ ? (snapshot?.queues.find((q) => q.name === selectedQueue) ?? null)
186
+ : null;
187
+ const worker = snapshot?.workers.find((w) => w.queue === queue?.name);
188
+ const queueConfig = queue?.config;
189
+ const queueEvents = queue
190
+ ? (snapshot?.events ?? []).filter((e) => e.queue === queue.name)
191
+ : [];
192
+ /* throughput bars (12 slots) */
193
+ const bars = (() => {
194
+ const raw = queueEvents.slice(0, 12).map((ev, i) => {
195
+ const base = 30 + ((queueEvents.length - i) % 6) * 10;
196
+ if (ev.type === "completed")
197
+ return Math.min(90, base + 22);
198
+ if (ev.type === "active")
199
+ return Math.min(90, base + 10);
200
+ return Math.max(20, base);
201
+ });
202
+ while (raw.length < 12)
203
+ raw.push(30 + (raw.length % 5) * 8);
204
+ return raw;
205
+ })();
206
+ async function handleAddJob(payload) {
207
+ if (!queue)
208
+ return null;
209
+ try {
210
+ const parsed = JSON.parse(payload);
211
+ await perform({ action: "enqueue-job", queue: queue.name, payload: parsed }, `run ${queue.name}`);
212
+ setAddJobOpen(false);
213
+ return null;
214
+ }
215
+ catch (err) {
216
+ return err instanceof Error ? err.message : "Invalid JSON payload";
217
+ }
218
+ }
219
+ function handleLiveEventsResizeStart(event) {
220
+ liveEventsResizeRef.current = {
221
+ pointerId: event.pointerId,
222
+ startY: event.clientY,
223
+ startHeight: liveEventsHeight,
224
+ };
225
+ document.body.style.userSelect = "none";
226
+ document.body.style.cursor = "ns-resize";
227
+ }
228
+ return (_jsxs("div", { className: "flex h-screen bg-zinc-950 text-zinc-300 font-mono overflow-hidden", children: [_jsxs("aside", { className: `
229
+ flex-shrink-0 flex flex-col border-r border-zinc-800/60 bg-zinc-950
230
+ transition-all duration-200 overflow-hidden
231
+ ${sidebarOpen ? "w-52" : "w-0 md:w-12"}
232
+ `, children: [sidebarOpen && (_jsxs("div", { className: `flex flex-col flex-1 min-h-0 ${sidebarOpen ? "opacity-100" : "opacity-0 md:opacity-0 pointer-events-none"} transition-opacity duration-150`, children: [_jsx("div", { className: "px-4 pt-4 pb-2", children: _jsx("p", { className: "text-[9px] uppercase tracking-[0.14em] text-zinc-600 font-medium", children: "Queues" }) }), _jsx("nav", { className: "flex flex-col gap-0.5 px-2 overflow-y-auto flex-1 pb-4", children: (snapshot?.queues ?? []).map((item, idx) => {
233
+ const accent = QUEUE_ACCENT[idx % QUEUE_ACCENT.length];
234
+ const isActive = item.name === queue?.name;
235
+ return (_jsxs("button", { type: "button", onClick: () => {
236
+ setSelectedQueue(item.name);
237
+ if (window.innerWidth < 768)
238
+ setSidebarOpen(false);
239
+ }, className: `
240
+ w-full flex items-center gap-2.5 px-3 py-2 rounded text-left text-xs transition-colors truncate
241
+ ${isActive
242
+ ? "bg-zinc-800 text-zinc-100"
243
+ : "text-zinc-500 hover:text-zinc-300 hover:bg-zinc-900"}
244
+ `, children: [_jsx("span", { className: "w-2 h-2 rounded-full flex-shrink-0", style: { background: accent } }), _jsx("span", { className: "truncate", children: item.name })] }, item.name));
245
+ }) })] })), !sidebarOpen && (_jsx("div", { className: "hidden md:flex flex-col items-center gap-2 py-4 px-1", children: (snapshot?.queues ?? []).slice(0, 8).map((item, idx) => {
246
+ const accent = QUEUE_ACCENT[idx % QUEUE_ACCENT.length];
247
+ const isActive = item.name === queue?.name;
248
+ return (_jsx("button", { type: "button", title: item.name, onClick: () => setSelectedQueue(item.name), className: `w-6 h-6 rounded flex items-center justify-center transition-colors ${isActive ? "bg-zinc-800" : "hover:bg-zinc-900"}`, children: _jsx("span", { className: "w-2 h-2 rounded-full", style: { background: accent } }) }, item.name));
249
+ }) }))] }), _jsxs("div", { className: "flex flex-col flex-1 min-w-0 min-h-0", children: [_jsxs("header", { className: "flex items-center gap-3 px-4 py-3 border-b border-zinc-800/60 flex-shrink-0 bg-zinc-950 z-10", children: [_jsx("button", { type: "button", onClick: () => setSidebarOpen((v) => !v), className: "w-7 h-7 flex items-center justify-center rounded text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800 transition-colors flex-shrink-0", "aria-label": "Toggle sidebar", children: _jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: _jsx("path", { d: "M1 2h12M1 7h12M1 12h12" }) }) }), queue ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-sm font-semibold text-zinc-100 truncate", children: queue.name }), _jsx(Badge, { state: worker?.paused ? "delayed" : "active" }), _jsxs("div", { className: "flex items-center gap-2 ml-auto flex-shrink-0", children: [_jsx("button", { type: "button", onClick: () => void perform({
250
+ action: worker?.paused
251
+ ? "resume-worker"
252
+ : "pause-worker",
253
+ queue: queue.name,
254
+ }, `${worker?.paused ? "resume" : "pause"} ${queue.name}`), className: "px-3 py-1.5 rounded text-[11px] text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 border border-zinc-800 hover:border-zinc-700 transition-colors", children: worker?.paused ? "Resume" : "Pause" }), queueConfig && (_jsx("button", { type: "button", onClick: () => setConfigOpen(true), className: "px-3 py-1.5 rounded text-[11px] text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 border border-zinc-800 hover:border-zinc-700 transition-colors", children: "Config" })), _jsx("button", { type: "button", onClick: () => setAddJobOpen(true), className: "px-3 py-1.5 rounded text-[11px] text-zinc-100 bg-zinc-800 hover:bg-zinc-700 border border-zinc-700 hover:border-zinc-600 transition-colors font-medium", children: "+ Add job" })] }), _jsxs("span", { className: "hidden lg:block text-[10px] text-zinc-600 flex-shrink-0", children: [pending ? `${pending} pending · ` : "", queue.jobs.length, " jobs \u00B7 ", queueEvents.length, " events", stale ? " · offline" : ""] })] })) : (_jsx("span", { className: "text-sm text-zinc-600", children: "Select a queue" }))] }), _jsx("main", { className: "flex-1 overflow-hidden min-h-0", children: queue ? (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsxs("div", { className: "grid grid-cols-5 border-b border-zinc-800/60 divide-x divide-zinc-800/60", children: [_jsx("div", { className: "px-5 py-5", children: _jsx(Stat, { label: "Waiting", value: queue.counts.waiting, color: "text-blue-400" }) }), _jsx("div", { className: "px-5 py-5", children: _jsx(Stat, { label: "Active", value: queue.counts.active, color: "text-amber-400" }) }), _jsx("div", { className: "px-5 py-5", children: _jsx(Stat, { label: "Completed", value: queue.counts.completed, color: "text-emerald-400" }) }), _jsx("div", { className: "px-5 py-5", children: _jsx(Stat, { label: "Failed", value: queue.counts.failed, color: "text-red-400" }) }), _jsx("div", { className: "px-5 py-5", children: _jsx(Stat, { label: "Delayed", value: queue.counts.delayed, color: "text-violet-400" }) })] }), _jsxs("div", { className: "px-5 pt-5 pb-4 border-b border-zinc-800/60", children: [_jsxs("div", { className: "flex items-center justify-between mb-3", children: [_jsx("span", { className: "text-[9px] uppercase tracking-[0.14em] text-zinc-600 font-medium", children: "Throughput \u00B7 last 12 min" }), _jsxs("span", { className: "text-[10px] text-zinc-500", children: ["avg ", Math.max(1, Math.round(queueEvents.length / 3)), " ", "jobs/min"] })] }), _jsx("div", { className: "flex items-end gap-1 h-12", children: bars.map((h, i) => (_jsx("div", { className: "flex-1 bg-zinc-700 rounded-sm", style: {
255
+ height: `${h}%`,
256
+ opacity: 0.5 + (i / bars.length) * 0.5,
257
+ } }, i))) })] }), _jsxs("div", { className: "flex flex-1 min-h-0 flex-col px-0", children: [_jsx("div", { className: "flex flex-shrink-0 items-center px-5 py-3 border-b border-zinc-800/60", children: _jsxs("span", { className: "text-[9px] uppercase tracking-[0.14em] text-zinc-600 font-medium", children: ["Jobs \u00B7 ", queue.jobs.length] }) }), _jsxs("div", { className: "min-h-0 flex-1 overflow-auto", children: [_jsxs("table", { className: "w-full text-xs", children: [_jsx("thead", { children: _jsx("tr", { className: "border-b border-zinc-800/60", children: [
258
+ "ID",
259
+ "Name",
260
+ "Status",
261
+ "Attempts",
262
+ "Age",
263
+ "Duration",
264
+ ].map((h) => (_jsx("th", { className: "px-5 py-2.5 text-left text-[9px] uppercase tracking-[0.14em] text-zinc-600 font-medium whitespace-nowrap", children: h }, h))) }) }), _jsx("tbody", { className: "divide-y divide-zinc-800/40", children: queue.jobs
265
+ .slice()
266
+ .sort((a, b) => b.createdAt - a.createdAt)
267
+ .slice(0, 10)
268
+ .map((job) => (_jsxs("tr", { className: "group hover:bg-zinc-900/50 transition-colors", children: [_jsx("td", { className: "px-5 py-3 text-zinc-600 font-mono", children: job.id.slice(0, 8) }), _jsx("td", { className: "px-5 py-3 text-zinc-200 font-medium", children: job.name }), _jsx("td", { className: "px-5 py-3", children: _jsx(Badge, { state: job.state }) }), _jsxs("td", { className: "px-5 py-3 text-zinc-500", children: [job.attempt, "/", job.attempts] }), _jsx("td", { className: "px-5 py-3 text-zinc-500 whitespace-nowrap", children: formatRelative(job.createdAt) }), _jsx("td", { className: "px-5 py-3 text-zinc-500 whitespace-nowrap", children: formatDuration(job) })] }, job.id))) })] }), queue.jobs.length === 0 && (_jsx("div", { className: "px-5 py-10 text-center text-xs text-zinc-700", children: "No jobs in queue" }))] })] }), _jsxs("div", { className: "border-t border-zinc-800/60 flex-shrink-0 min-h-0", style: { height: liveEventsHeight }, children: [_jsxs("button", { type: "button", onPointerDown: handleLiveEventsResizeStart, className: "group relative flex h-3 w-full items-center justify-center touch-none", "aria-label": "Resize live events panel", children: [_jsx("span", { className: "h-px w-full bg-zinc-800/60 transition-colors group-hover:bg-zinc-700" }), _jsx("span", { className: "absolute rounded-full border border-zinc-700 bg-zinc-900 px-2 py-0.5 text-[9px] uppercase tracking-[0.14em] text-zinc-500 opacity-0 transition-opacity group-hover:opacity-100", children: "Resize" })] }), _jsxs("div", { className: "flex h-[calc(100%-0.75rem)] flex-col px-5 pb-4", children: [_jsx("p", { className: "text-[9px] uppercase tracking-[0.14em] text-zinc-600 font-medium mb-3", children: "Live Events" }), queueEvents.length === 0 ? (_jsx("p", { className: "text-xs text-zinc-700", children: "No recent events" })) : (_jsx("div", { className: "flex min-h-0 flex-col gap-1 overflow-y-auto pr-1", children: queueEvents.map((ev) => (_jsxs("div", { className: "grid text-[11px] py-2 px-3 rounded bg-zinc-900 border border-zinc-800/60 hover:border-zinc-700 transition-colors", style: { gridTemplateColumns: "120px 80px 1fr" }, children: [_jsx("span", { className: "text-zinc-300 font-medium truncate", children: ev.queue }), _jsx("span", { className: "text-zinc-500 uppercase text-[9px] tracking-wider flex items-center", children: ev.type }), _jsx("span", { className: "text-zinc-600 truncate", children: ev.detail })] }, ev.id))) }))] })] })] })) : (
269
+ /* ── Empty state ── */
270
+ _jsxs("div", { className: "flex flex-col items-center justify-center h-full text-center px-8 py-16", children: [_jsx("div", { className: "w-10 h-10 rounded-xl border border-zinc-800 flex items-center justify-center mb-5", children: _jsxs("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", strokeWidth: "1.25", className: "text-zinc-600", children: [_jsx("rect", { x: "2", y: "2", width: "14", height: "14", rx: "2" }), _jsx("path", { d: "M6 9h6M9 6v6" })] }) }), _jsx("h3", { className: "text-sm font-semibold text-zinc-400 mb-2", children: "Select a queue" }), _jsx("p", { className: "text-xs text-zinc-700 max-w-xs leading-relaxed", children: snapshot?.queues.length
271
+ ? `${snapshot.queues.length} queue${snapshot.queues.length !== 1 ? "s" : ""} available · ${snapshot.queues.reduce((n, q) => n + q.jobs.length, 0)} total jobs`
272
+ : "Choose a queue from the sidebar to inspect jobs, metrics, and run payloads." }), stale && _jsx("p", { className: "mt-3 text-xs text-red-500/70", children: error })] })) })] }), configOpen && queueConfig && queue && (_jsx(ConfigModal, { config: queueConfig, workerPaused: !!worker?.paused, queueName: queue.name, onClose: () => setConfigOpen(false) })), addJobOpen && queue && (_jsx(AddJobModal, { queueName: queue.name, onClose: () => setAddJobOpen(false), onSubmit: handleAddJob }))] }));
273
+ }
package/dist/index.cjs ADDED
@@ -0,0 +1,15 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("react");var q={exports:{}},C={};var Z;function ie(){if(Z)return C;Z=1;var r=Symbol.for("react.transitional.element"),s=Symbol.for("react.fragment");function p(o,m,d){var u=null;if(d!==void 0&&(u=""+d),m.key!==void 0&&(u=""+m.key),"key"in m){d={};for(var x in m)x!=="key"&&(d[x]=m[x])}else d=m;return m=d.ref,{$$typeof:r,type:o,key:u,ref:m!==void 0?m:null,props:d}}return C.Fragment=s,C.jsx=p,C.jsxs=p,C}var P={};var K;function oe(){return K||(K=1,process.env.NODE_ENV!=="production"&&(function(){function r(t){if(t==null)return null;if(typeof t=="function")return t.$$typeof===D?null:t.displayName||t.name||null;if(typeof t=="string")return t;switch(t){case i:return"Fragment";case A:return"Profiler";case S:return"StrictMode";case Y:return"Suspense";case n:return"SuspenseList";case E:return"Activity"}if(typeof t=="object")switch(typeof t.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),t.$$typeof){case w:return"Portal";case $:return t.displayName||"Context";case R:return(t._context.displayName||"Context")+".Consumer";case U:var a=t.render;return t=t.displayName,t||(t=a.displayName||a.name||"",t=t!==""?"ForwardRef("+t+")":"ForwardRef"),t;case l:return a=t.displayName||null,a!==null?a:r(t.type)||"Memo";case y:a=t._payload,t=t._init;try{return r(t(a))}catch{}}return null}function s(t){return""+t}function p(t){try{s(t);var a=!1}catch{a=!0}if(a){a=console;var f=a.error,h=typeof Symbol=="function"&&Symbol.toStringTag&&t[Symbol.toStringTag]||t.constructor.name||"Object";return f.call(a,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",h),s(t)}}function o(t){if(t===i)return"<>";if(typeof t=="object"&&t!==null&&t.$$typeof===y)return"<...>";try{var a=r(t);return a?"<"+a+">":"<...>"}catch{return"<...>"}}function m(){var t=O.A;return t===null?null:t.getOwner()}function d(){return Error("react-stack-top-frame")}function u(t){if(H.call(t,"key")){var a=Object.getOwnPropertyDescriptor(t,"key").get;if(a&&a.isReactWarning)return!1}return t.key!==void 0}function x(t,a){function f(){V||(V=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",a))}f.isReactWarning=!0,Object.defineProperty(t,"key",{get:f,configurable:!0})}function N(){var t=r(this.type);return G[t]||(G[t]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),t=this.props.ref,t!==void 0?t:null}function _(t,a,f,h,I,W){var b=f.ref;return t={$$typeof:j,type:t,key:a,props:f,_owner:h},(b!==void 0?b:null)!==null?Object.defineProperty(t,"ref",{enumerable:!1,get:N}):Object.defineProperty(t,"ref",{enumerable:!1,value:null}),t._store={},Object.defineProperty(t._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(t,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(t,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:I}),Object.defineProperty(t,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:W}),Object.freeze&&(Object.freeze(t.props),Object.freeze(t)),t}function z(t,a,f,h,I,W){var b=a.children;if(b!==void 0)if(h)if(se(b)){for(h=0;h<b.length;h++)v(b[h]);Object.freeze&&Object.freeze(b)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else v(b);if(H.call(a,"key")){b=r(t);var T=Object.keys(a).filter(function(ae){return ae!=="key"});h=0<T.length?"{key: someKey, "+T.join(": ..., ")+": ...}":"{key: someKey}",Q[b+h]||(T=0<T.length?"{"+T.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
2
+ let props = %s;
3
+ <%s {...props} />
4
+ React keys must be passed directly to JSX without using spread:
5
+ let props = %s;
6
+ <%s key={someKey} {...props} />`,h,b,T,b),Q[b+h]=!0)}if(b=null,f!==void 0&&(p(f),b=""+f),u(a)&&(p(a.key),b=""+a.key),"key"in a){f={};for(var J in a)J!=="key"&&(f[J]=a[J])}else f=a;return b&&x(f,typeof t=="function"?t.displayName||t.name||"Unknown":t),_(t,b,f,m(),I,W)}function v(t){k(t)?t._store&&(t._store.validated=1):typeof t=="object"&&t!==null&&t.$$typeof===y&&(t._payload.status==="fulfilled"?k(t._payload.value)&&t._payload.value._store&&(t._payload.value._store.validated=1):t._store&&(t._store.validated=1))}function k(t){return typeof t=="object"&&t!==null&&t.$$typeof===j}var g=c,j=Symbol.for("react.transitional.element"),w=Symbol.for("react.portal"),i=Symbol.for("react.fragment"),S=Symbol.for("react.strict_mode"),A=Symbol.for("react.profiler"),R=Symbol.for("react.consumer"),$=Symbol.for("react.context"),U=Symbol.for("react.forward_ref"),Y=Symbol.for("react.suspense"),n=Symbol.for("react.suspense_list"),l=Symbol.for("react.memo"),y=Symbol.for("react.lazy"),E=Symbol.for("react.activity"),D=Symbol.for("react.client.reference"),O=g.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,H=Object.prototype.hasOwnProperty,se=Array.isArray,F=console.createTask?console.createTask:function(){return null};g={react_stack_bottom_frame:function(t){return t()}};var V,G={},B=g.react_stack_bottom_frame.bind(g,d)(),X=F(o(d)),Q={};P.Fragment=i,P.jsx=function(t,a,f){var h=1e4>O.recentlyCreatedOwnerStacks++;return z(t,a,f,!1,h?Error("react-stack-top-frame"):B,h?F(o(t)):X)},P.jsxs=function(t,a,f){var h=1e4>O.recentlyCreatedOwnerStacks++;return z(t,a,f,!0,h?Error("react-stack-top-frame"):B,h?F(o(t)):X)}})()),P}var ee;function le(){return ee||(ee=1,process.env.NODE_ENV==="production"?q.exports=ie():q.exports=oe()),q.exports}var e=le();const ne=c.createContext(null);function ce({children:r,endpoint:s="http://localhost:3210/api/playground",refreshInterval:p=1200,initialSnapshot:o=null}){const[m,d]=c.useState(o),[u,x]=c.useState(null),[N,_]=c.useState(null);async function z(){const g=await fetch(s,{cache:"no-store"});if(!g.ok)throw new Error(`Eventually UI refresh failed with ${g.status}`);const j=await g.json();x(null),c.startTransition(()=>{d(j)})}async function v(g,j=g.action){_(j);try{const w=await fetch(s,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(g)});if(!w.ok)throw new Error(`Eventually UI action failed with ${w.status}`);const i=await w.json();x(null),c.startTransition(()=>{d(i)})}catch(w){throw x(w instanceof Error?w.message:"Eventually UI request failed"),w}finally{_(null)}}c.useEffect(()=>{z().catch(j=>{x(j instanceof Error?j.message:"Eventually UI refresh failed")});const g=window.setInterval(()=>{z().catch(j=>{x(j instanceof Error?j.message:"Eventually UI refresh failed")})},p);return()=>window.clearInterval(g)},[s,p]);const k=c.useMemo(()=>({endpoint:s,error:u,pending:N,snapshot:m,stale:u!==null,refresh:z,perform:v}),[s,u,N,m]);return e.jsx(ne.Provider,{value:k,children:r})}function re(){const r=c.useContext(ne);if(!r)throw new Error("useEventuallyUI must be used inside EventuallyUIProvider.");return r}const de=144,ue=480;function xe(r){const s=Math.max(0,Date.now()-r);return s<1e3?`${s}ms`:s<6e4?`${Math.floor(s/1e3)}s ago`:`${Math.floor(s/6e4)}m ago`}function me(r){if(r.state==="delayed"){const s=Math.max(0,r.availableAt-Date.now());if(s<6e4)return`in ${Math.ceil(s/1e3)}s`;const p=Math.floor(s/6e4),o=Math.floor(s%6e4/1e3);return`in ${p}m ${o}s`}return r.finishedAt&&r.processedAt?`${r.finishedAt-r.processedAt}ms`:r.processedAt?`${Math.max(0,Date.now()-r.processedAt)}ms`:"—"}const fe={active:{dot:"bg-amber-400",badge:"bg-amber-400/10 text-amber-400 ring-1 ring-amber-400/25",label:"active"},waiting:{dot:"bg-blue-400",badge:"bg-blue-400/10 text-blue-400 ring-1 ring-blue-400/25",label:"waiting"},completed:{dot:"bg-emerald-400",badge:"bg-emerald-400/10 text-emerald-400 ring-1 ring-emerald-400/25",label:"completed"},failed:{dot:"bg-red-400",badge:"bg-red-400/10 text-red-400 ring-1 ring-red-400/25",label:"failed"},delayed:{dot:"bg-violet-400",badge:"bg-violet-400/10 text-violet-400 ring-1 ring-violet-400/25",label:"delayed"},blocked:{dot:"bg-violet-400",badge:"bg-violet-400/10 text-violet-400 ring-1 ring-violet-400/25",label:"blocked"}},L=["#f59e0b","#34d399","#60a5fa","#a78bfa","#f87171","#fb923c"];function te({state:r}){const s=fe[r];return e.jsxs("span",{className:`inline-flex items-center gap-1.5 px-2 py-0.5 rounded text-[10px] font-mono font-medium tracking-wide ${s.badge}`,children:[e.jsx("span",{className:`w-1.5 h-1.5 rounded-full ${s.dot} ${r==="active"?"animate-pulse":""}`}),s.label]})}function M({label:r,value:s,color:p}){return e.jsxs("div",{className:"flex flex-col gap-1 min-w-0",children:[e.jsx("span",{className:"text-[9px] uppercase tracking-[0.12em] text-zinc-500 font-medium",children:r}),e.jsx("span",{className:`text-2xl font-mono font-semibold leading-none ${p}`,children:s})]})}function pe({config:r,workerPaused:s,queueName:p,onClose:o}){const m=c.useRef(null);c.useEffect(()=>{const u=x=>{x.key==="Escape"&&o()};return window.addEventListener("keydown",u),()=>window.removeEventListener("keydown",u)},[o]);const d=[["Purpose",String(r.description??"—")],["Concurrency",String(r.concurrency??"—")],["Poll Interval",r.pollIntervalMs!=null?`${r.pollIntervalMs}ms`:"—"],["Attempts",String(r.attempts??"—")],["Keep Completed",String(r.removeOnCompleteCount??"—")],["Keep Failed",String(r.removeOnFailCount??"—")],["Recurring",r.recurringEnabled?"enabled":"off"],["Worker",s?"paused":"running"]];return e.jsx("div",{ref:m,className:"fixed inset-0 z-50 flex items-center justify-center p-4",style:{background:"rgba(0,0,0,0.65)",backdropFilter:"blur(4px)"},onClick:u=>{u.target===m.current&&o()},children:e.jsxs("div",{className:"w-full max-w-lg bg-zinc-900 border border-zinc-800 rounded-xl shadow-2xl overflow-hidden",children:[e.jsxs("div",{className:"flex items-center justify-between px-5 py-4 border-b border-zinc-800",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-xs uppercase tracking-[0.12em] text-zinc-500 font-medium mb-0.5",children:"Configuration"}),e.jsx("h2",{className:"text-sm font-mono font-semibold text-zinc-100",children:p})]}),e.jsx("button",{type:"button",onClick:o,className:"w-7 h-7 flex items-center justify-center rounded text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800 transition-colors",children:e.jsx("svg",{width:"14",height:"14",viewBox:"0 0 14 14",fill:"none",stroke:"currentColor",strokeWidth:"1.5",children:e.jsx("path",{d:"M1 1l12 12M13 1L1 13"})})})]}),e.jsx("div",{className:"p-5 grid grid-cols-2 gap-3",children:d.map(([u,x],N)=>e.jsxs("div",{className:`${N===0?"col-span-2":""} bg-zinc-800/50 border border-zinc-800 rounded-lg px-4 py-3`,children:[e.jsx("p",{className:"text-[9px] uppercase tracking-[0.12em] text-zinc-500 font-medium mb-1.5",children:u}),e.jsx("p",{className:"text-xs font-mono text-zinc-200 leading-relaxed",children:x})]},u))})]})})}function he({queueName:r,onClose:s,onSubmit:p}){const[o,m]=c.useState(`{
7
+
8
+ }`),[d,u]=c.useState(null),[x,N]=c.useState(!1),_=c.useRef(null);c.useEffect(()=>{const v=k=>{k.key==="Escape"&&s()};return window.addEventListener("keydown",v),()=>window.removeEventListener("keydown",v)},[s]);async function z(){N(!0);const v=await p(o);N(!1),v&&u(v)}return e.jsx("div",{ref:_,className:"fixed inset-0 z-50 flex items-center justify-center p-4",style:{background:"rgba(0,0,0,0.65)",backdropFilter:"blur(4px)"},onClick:v=>{v.target===_.current&&s()},children:e.jsxs("div",{className:"w-full max-w-md bg-zinc-900 border border-zinc-800 rounded-xl shadow-2xl overflow-hidden",children:[e.jsxs("div",{className:"flex items-center justify-between px-5 py-4 border-b border-zinc-800",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-xs uppercase tracking-[0.12em] text-zinc-500 font-medium mb-0.5",children:"Enqueue job"}),e.jsx("h2",{className:"text-sm font-mono font-semibold text-zinc-100",children:r})]}),e.jsx("button",{type:"button",onClick:s,className:"w-7 h-7 flex items-center justify-center rounded text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800 transition-colors",children:e.jsx("svg",{width:"14",height:"14",viewBox:"0 0 14 14",fill:"none",stroke:"currentColor",strokeWidth:"1.5",children:e.jsx("path",{d:"M1 1l12 12M13 1L1 13"})})})]}),e.jsxs("div",{className:"p-5 space-y-3",children:[e.jsx("p",{className:"text-xs text-zinc-500",children:"Provide a JSON payload for the new job."}),e.jsx("textarea",{className:"w-full h-44 resize-y bg-zinc-950 border border-zinc-800 focus:border-zinc-600 rounded-lg text-xs font-mono text-zinc-200 p-3 outline-none transition-colors",value:o,onChange:v=>{m(v.target.value),u(null)},spellCheck:!1}),d&&e.jsx("p",{className:"text-[11px] text-red-400 bg-red-400/8 border border-red-400/20 rounded-lg px-3 py-2",children:d}),e.jsxs("div",{className:"flex justify-end gap-2 pt-1",children:[e.jsx("button",{type:"button",onClick:s,className:"px-4 py-1.5 rounded text-xs font-mono text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 border border-zinc-800 hover:border-zinc-700 transition-colors",children:"Cancel"}),e.jsx("button",{type:"button",onClick:()=>{z()},disabled:x,className:"px-4 py-1.5 rounded text-xs font-mono font-medium bg-zinc-100 text-zinc-900 hover:bg-white disabled:opacity-40 transition-colors",children:x?"Running…":"Run job"})]})]})]})})}function be(){const{error:r,pending:s,perform:p,snapshot:o,stale:m}=re(),[d,u]=c.useState(null),[x,N]=c.useState(typeof window<"u"?window.innerWidth>=768:!0),[_,z]=c.useState(!1),[v,k]=c.useState(!1),[g,j]=c.useState(220),w=c.useRef(null);c.useEffect(()=>{const n=window.matchMedia("(max-width: 767px)"),l=y=>N(!y.matches);return n.addEventListener("change",l),()=>n.removeEventListener("change",l)},[]),c.useEffect(()=>{d&&(o?.queues.some(n=>n.name===d)||u(null))},[d,o]),c.useEffect(()=>{const n=y=>{const E=w.current;if(!E)return;const D=E.startY-y.clientY,O=Math.min(window.innerHeight*.6,ue);j(Math.round(Math.max(de,Math.min(O,E.startHeight+D))))},l=y=>{w.current?.pointerId===y.pointerId&&(w.current=null,document.body.style.userSelect="",document.body.style.cursor="")};return window.addEventListener("pointermove",n),window.addEventListener("pointerup",l),()=>{window.removeEventListener("pointermove",n),window.removeEventListener("pointerup",l),document.body.style.userSelect="",document.body.style.cursor=""}},[]);const i=d?o?.queues.find(n=>n.name===d)??null:null,S=o?.workers.find(n=>n.queue===i?.name),A=i?.config,R=i?(o?.events??[]).filter(n=>n.queue===i.name):[],$=(()=>{const n=R.slice(0,12).map((l,y)=>{const E=30+(R.length-y)%6*10;return l.type==="completed"?Math.min(90,E+22):l.type==="active"?Math.min(90,E+10):Math.max(20,E)});for(;n.length<12;)n.push(30+n.length%5*8);return n})();async function U(n){if(!i)return null;try{const l=JSON.parse(n);return await p({action:"enqueue-job",queue:i.name,payload:l},`run ${i.name}`),k(!1),null}catch(l){return l instanceof Error?l.message:"Invalid JSON payload"}}function Y(n){w.current={pointerId:n.pointerId,startY:n.clientY,startHeight:g},document.body.style.userSelect="none",document.body.style.cursor="ns-resize"}return e.jsxs("div",{className:"flex h-screen bg-zinc-950 text-zinc-300 font-mono overflow-hidden",children:[e.jsxs("aside",{className:`
9
+ flex-shrink-0 flex flex-col border-r border-zinc-800/60 bg-zinc-950
10
+ transition-all duration-200 overflow-hidden
11
+ ${x?"w-52":"w-0 md:w-12"}
12
+ `,children:[x&&e.jsxs("div",{className:`flex flex-col flex-1 min-h-0 ${x?"opacity-100":"opacity-0 md:opacity-0 pointer-events-none"} transition-opacity duration-150`,children:[e.jsx("div",{className:"px-4 pt-4 pb-2",children:e.jsx("p",{className:"text-[9px] uppercase tracking-[0.14em] text-zinc-600 font-medium",children:"Queues"})}),e.jsx("nav",{className:"flex flex-col gap-0.5 px-2 overflow-y-auto flex-1 pb-4",children:(o?.queues??[]).map((n,l)=>{const y=L[l%L.length],E=n.name===i?.name;return e.jsxs("button",{type:"button",onClick:()=>{u(n.name),window.innerWidth<768&&N(!1)},className:`
13
+ w-full flex items-center gap-2.5 px-3 py-2 rounded text-left text-xs transition-colors truncate
14
+ ${E?"bg-zinc-800 text-zinc-100":"text-zinc-500 hover:text-zinc-300 hover:bg-zinc-900"}
15
+ `,children:[e.jsx("span",{className:"w-2 h-2 rounded-full flex-shrink-0",style:{background:y}}),e.jsx("span",{className:"truncate",children:n.name})]},n.name)})})]}),!x&&e.jsx("div",{className:"hidden md:flex flex-col items-center gap-2 py-4 px-1",children:(o?.queues??[]).slice(0,8).map((n,l)=>{const y=L[l%L.length],E=n.name===i?.name;return e.jsx("button",{type:"button",title:n.name,onClick:()=>u(n.name),className:`w-6 h-6 rounded flex items-center justify-center transition-colors ${E?"bg-zinc-800":"hover:bg-zinc-900"}`,children:e.jsx("span",{className:"w-2 h-2 rounded-full",style:{background:y}})},n.name)})})]}),e.jsxs("div",{className:"flex flex-col flex-1 min-w-0 min-h-0",children:[e.jsxs("header",{className:"flex items-center gap-3 px-4 py-3 border-b border-zinc-800/60 flex-shrink-0 bg-zinc-950 z-10",children:[e.jsx("button",{type:"button",onClick:()=>N(n=>!n),className:"w-7 h-7 flex items-center justify-center rounded text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800 transition-colors flex-shrink-0","aria-label":"Toggle sidebar",children:e.jsx("svg",{width:"14",height:"14",viewBox:"0 0 14 14",fill:"none",stroke:"currentColor",strokeWidth:"1.5",children:e.jsx("path",{d:"M1 2h12M1 7h12M1 12h12"})})}),i?e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"text-sm font-semibold text-zinc-100 truncate",children:i.name}),e.jsx(te,{state:S?.paused?"delayed":"active"}),e.jsxs("div",{className:"flex items-center gap-2 ml-auto flex-shrink-0",children:[e.jsx("button",{type:"button",onClick:()=>{p({action:S?.paused?"resume-worker":"pause-worker",queue:i.name},`${S?.paused?"resume":"pause"} ${i.name}`)},className:"px-3 py-1.5 rounded text-[11px] text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 border border-zinc-800 hover:border-zinc-700 transition-colors",children:S?.paused?"Resume":"Pause"}),A&&e.jsx("button",{type:"button",onClick:()=>z(!0),className:"px-3 py-1.5 rounded text-[11px] text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 border border-zinc-800 hover:border-zinc-700 transition-colors",children:"Config"}),e.jsx("button",{type:"button",onClick:()=>k(!0),className:"px-3 py-1.5 rounded text-[11px] text-zinc-100 bg-zinc-800 hover:bg-zinc-700 border border-zinc-700 hover:border-zinc-600 transition-colors font-medium",children:"+ Add job"})]}),e.jsxs("span",{className:"hidden lg:block text-[10px] text-zinc-600 flex-shrink-0",children:[s?`${s} pending · `:"",i.jobs.length," jobs · ",R.length," events",m?" · offline":""]})]}):e.jsx("span",{className:"text-sm text-zinc-600",children:"Select a queue"})]}),e.jsx("main",{className:"flex-1 overflow-hidden min-h-0",children:i?e.jsxs("div",{className:"flex h-full min-h-0 flex-col",children:[e.jsxs("div",{className:"grid grid-cols-5 border-b border-zinc-800/60 divide-x divide-zinc-800/60",children:[e.jsx("div",{className:"px-5 py-5",children:e.jsx(M,{label:"Waiting",value:i.counts.waiting,color:"text-blue-400"})}),e.jsx("div",{className:"px-5 py-5",children:e.jsx(M,{label:"Active",value:i.counts.active,color:"text-amber-400"})}),e.jsx("div",{className:"px-5 py-5",children:e.jsx(M,{label:"Completed",value:i.counts.completed,color:"text-emerald-400"})}),e.jsx("div",{className:"px-5 py-5",children:e.jsx(M,{label:"Failed",value:i.counts.failed,color:"text-red-400"})}),e.jsx("div",{className:"px-5 py-5",children:e.jsx(M,{label:"Delayed",value:i.counts.delayed,color:"text-violet-400"})})]}),e.jsxs("div",{className:"px-5 pt-5 pb-4 border-b border-zinc-800/60",children:[e.jsxs("div",{className:"flex items-center justify-between mb-3",children:[e.jsx("span",{className:"text-[9px] uppercase tracking-[0.14em] text-zinc-600 font-medium",children:"Throughput · last 12 min"}),e.jsxs("span",{className:"text-[10px] text-zinc-500",children:["avg ",Math.max(1,Math.round(R.length/3))," ","jobs/min"]})]}),e.jsx("div",{className:"flex items-end gap-1 h-12",children:$.map((n,l)=>e.jsx("div",{className:"flex-1 bg-zinc-700 rounded-sm",style:{height:`${n}%`,opacity:.5+l/$.length*.5}},l))})]}),e.jsxs("div",{className:"flex flex-1 min-h-0 flex-col px-0",children:[e.jsx("div",{className:"flex flex-shrink-0 items-center px-5 py-3 border-b border-zinc-800/60",children:e.jsxs("span",{className:"text-[9px] uppercase tracking-[0.14em] text-zinc-600 font-medium",children:["Jobs · ",i.jobs.length]})}),e.jsxs("div",{className:"min-h-0 flex-1 overflow-auto",children:[e.jsxs("table",{className:"w-full text-xs",children:[e.jsx("thead",{children:e.jsx("tr",{className:"border-b border-zinc-800/60",children:["ID","Name","Status","Attempts","Age","Duration"].map(n=>e.jsx("th",{className:"px-5 py-2.5 text-left text-[9px] uppercase tracking-[0.14em] text-zinc-600 font-medium whitespace-nowrap",children:n},n))})}),e.jsx("tbody",{className:"divide-y divide-zinc-800/40",children:i.jobs.slice().sort((n,l)=>l.createdAt-n.createdAt).slice(0,10).map(n=>e.jsxs("tr",{className:"group hover:bg-zinc-900/50 transition-colors",children:[e.jsx("td",{className:"px-5 py-3 text-zinc-600 font-mono",children:n.id.slice(0,8)}),e.jsx("td",{className:"px-5 py-3 text-zinc-200 font-medium",children:n.name}),e.jsx("td",{className:"px-5 py-3",children:e.jsx(te,{state:n.state})}),e.jsxs("td",{className:"px-5 py-3 text-zinc-500",children:[n.attempt,"/",n.attempts]}),e.jsx("td",{className:"px-5 py-3 text-zinc-500 whitespace-nowrap",children:xe(n.createdAt)}),e.jsx("td",{className:"px-5 py-3 text-zinc-500 whitespace-nowrap",children:me(n)})]},n.id))})]}),i.jobs.length===0&&e.jsx("div",{className:"px-5 py-10 text-center text-xs text-zinc-700",children:"No jobs in queue"})]})]}),e.jsxs("div",{className:"border-t border-zinc-800/60 flex-shrink-0 min-h-0",style:{height:g},children:[e.jsxs("button",{type:"button",onPointerDown:Y,className:"group relative flex h-3 w-full items-center justify-center touch-none","aria-label":"Resize live events panel",children:[e.jsx("span",{className:"h-px w-full bg-zinc-800/60 transition-colors group-hover:bg-zinc-700"}),e.jsx("span",{className:"absolute rounded-full border border-zinc-700 bg-zinc-900 px-2 py-0.5 text-[9px] uppercase tracking-[0.14em] text-zinc-500 opacity-0 transition-opacity group-hover:opacity-100",children:"Resize"})]}),e.jsxs("div",{className:"flex h-[calc(100%-0.75rem)] flex-col px-5 pb-4",children:[e.jsx("p",{className:"text-[9px] uppercase tracking-[0.14em] text-zinc-600 font-medium mb-3",children:"Live Events"}),R.length===0?e.jsx("p",{className:"text-xs text-zinc-700",children:"No recent events"}):e.jsx("div",{className:"flex min-h-0 flex-col gap-1 overflow-y-auto pr-1",children:R.map(n=>e.jsxs("div",{className:"grid text-[11px] py-2 px-3 rounded bg-zinc-900 border border-zinc-800/60 hover:border-zinc-700 transition-colors",style:{gridTemplateColumns:"120px 80px 1fr"},children:[e.jsx("span",{className:"text-zinc-300 font-medium truncate",children:n.queue}),e.jsx("span",{className:"text-zinc-500 uppercase text-[9px] tracking-wider flex items-center",children:n.type}),e.jsx("span",{className:"text-zinc-600 truncate",children:n.detail})]},n.id))})]})]})]}):e.jsxs("div",{className:"flex flex-col items-center justify-center h-full text-center px-8 py-16",children:[e.jsx("div",{className:"w-10 h-10 rounded-xl border border-zinc-800 flex items-center justify-center mb-5",children:e.jsxs("svg",{width:"18",height:"18",viewBox:"0 0 18 18",fill:"none",stroke:"currentColor",strokeWidth:"1.25",className:"text-zinc-600",children:[e.jsx("rect",{x:"2",y:"2",width:"14",height:"14",rx:"2"}),e.jsx("path",{d:"M6 9h6M9 6v6"})]})}),e.jsx("h3",{className:"text-sm font-semibold text-zinc-400 mb-2",children:"Select a queue"}),e.jsx("p",{className:"text-xs text-zinc-700 max-w-xs leading-relaxed",children:o?.queues.length?`${o.queues.length} queue${o.queues.length!==1?"s":""} available · ${o.queues.reduce((n,l)=>n+l.jobs.length,0)} total jobs`:"Choose a queue from the sidebar to inspect jobs, metrics, and run payloads."}),m&&e.jsx("p",{className:"mt-3 text-xs text-red-500/70",children:r})]})})]}),_&&A&&i&&e.jsx(pe,{config:A,workerPaused:!!S?.paused,queueName:i.name,onClose:()=>z(!1)}),v&&i&&e.jsx(he,{queueName:i.name,onClose:()=>k(!1),onSubmit:U})]})}exports.EventuallyDashboard=be;exports.EventuallyUIProvider=ce;exports.useEventuallyUI=re;
package/dist/index.css ADDED
@@ -0,0 +1 @@
1
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-x-reverse:0;--tw-border-style:solid;--tw-divide-y-reverse:0;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-duration:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-amber-400:oklch(82.8% .189 84.429);--color-emerald-400:oklch(76.5% .177 163.223);--color-blue-400:oklch(70.7% .165 254.624);--color-violet-400:oklch(70.2% .183 293.541);--color-zinc-100:oklch(96.7% .001 286.375);--color-zinc-200:oklch(92% .004 286.32);--color-zinc-300:oklch(87.1% .006 286.286);--color-zinc-400:oklch(70.5% .015 286.067);--color-zinc-500:oklch(55.2% .016 285.938);--color-zinc-600:oklch(44.2% .017 285.786);--color-zinc-700:oklch(37% .013 285.805);--color-zinc-800:oklch(27.4% .006 286.033);--color-zinc-900:oklch(21% .006 285.885);--color-zinc-950:oklch(14.1% .005 285.823);--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-md:28rem;--container-lg:32rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--tracking-wider:.05em;--leading-relaxed:1.625;--radius-sm:.25rem;--radius-lg:.5rem;--radius-xl:.75rem;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;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");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.inset-0{inset:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.z-10{z-index:10}.z-50{z-index:50}.col-span-2{grid-column:span 2/span 2}.mt-3{margin-top:calc(var(--spacing) * 3)}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.ml-auto{margin-left:auto}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.table{display:table}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-3{height:calc(var(--spacing) * 3)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-10{height:calc(var(--spacing) * 10)}.h-12{height:calc(var(--spacing) * 12)}.h-44{height:calc(var(--spacing) * 44)}.h-\[calc\(100\%-0\.75rem\)\]{height:calc(100% - .75rem)}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.min-h-0{min-height:calc(var(--spacing) * 0)}.w-0{width:calc(var(--spacing) * 0)}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-10{width:calc(var(--spacing) * 10)}.w-52{width:calc(var(--spacing) * 52)}.w-full{width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.animate-pulse{animation:var(--animate-pulse)}.touch-none{touch-action:none}.resize-y{resize:vertical}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.flex-col{flex-direction:column}.items-center{align-items:center}.items-end{align-items:flex-end}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-x>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(1px * var(--tw-divide-x-reverse));border-inline-end-width:calc(1px * calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-zinc-800\/40>:not(:last-child)){border-color:#27272a66}@supports (color:color-mix(in lab,red,red)){:where(.divide-zinc-800\/40>:not(:last-child)){border-color:color-mix(in oklab,var(--color-zinc-800) 40%,transparent)}}:where(.divide-zinc-800\/60>:not(:last-child)){border-color:#27272a99}@supports (color:color-mix(in lab,red,red)){:where(.divide-zinc-800\/60>:not(:last-child)){border-color:color-mix(in oklab,var(--color-zinc-800) 60%,transparent)}}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-red-400\/20{border-color:#ff656833}@supports (color:color-mix(in lab,red,red)){.border-red-400\/20{border-color:color-mix(in oklab,var(--color-red-400) 20%,transparent)}}.border-zinc-700{border-color:var(--color-zinc-700)}.border-zinc-800{border-color:var(--color-zinc-800)}.border-zinc-800\/60{border-color:#27272a99}@supports (color:color-mix(in lab,red,red)){.border-zinc-800\/60{border-color:color-mix(in oklab,var(--color-zinc-800) 60%,transparent)}}.bg-amber-400{background-color:var(--color-amber-400)}.bg-amber-400\/10{background-color:#fcbb001a}@supports (color:color-mix(in lab,red,red)){.bg-amber-400\/10{background-color:color-mix(in oklab,var(--color-amber-400) 10%,transparent)}}.bg-blue-400{background-color:var(--color-blue-400)}.bg-blue-400\/10{background-color:#54a2ff1a}@supports (color:color-mix(in lab,red,red)){.bg-blue-400\/10{background-color:color-mix(in oklab,var(--color-blue-400) 10%,transparent)}}.bg-emerald-400{background-color:var(--color-emerald-400)}.bg-emerald-400\/10{background-color:#00d2941a}@supports (color:color-mix(in lab,red,red)){.bg-emerald-400\/10{background-color:color-mix(in oklab,var(--color-emerald-400) 10%,transparent)}}.bg-red-400{background-color:var(--color-red-400)}.bg-red-400\/8{background-color:#ff656814}@supports (color:color-mix(in lab,red,red)){.bg-red-400\/8{background-color:color-mix(in oklab,var(--color-red-400) 8%,transparent)}}.bg-red-400\/10{background-color:#ff65681a}@supports (color:color-mix(in lab,red,red)){.bg-red-400\/10{background-color:color-mix(in oklab,var(--color-red-400) 10%,transparent)}}.bg-violet-400{background-color:var(--color-violet-400)}.bg-violet-400\/10{background-color:#a685ff1a}@supports (color:color-mix(in lab,red,red)){.bg-violet-400\/10{background-color:color-mix(in oklab,var(--color-violet-400) 10%,transparent)}}.bg-zinc-100{background-color:var(--color-zinc-100)}.bg-zinc-700{background-color:var(--color-zinc-700)}.bg-zinc-800{background-color:var(--color-zinc-800)}.bg-zinc-800\/50{background-color:#27272a80}@supports (color:color-mix(in lab,red,red)){.bg-zinc-800\/50{background-color:color-mix(in oklab,var(--color-zinc-800) 50%,transparent)}}.bg-zinc-800\/60{background-color:#27272a99}@supports (color:color-mix(in lab,red,red)){.bg-zinc-800\/60{background-color:color-mix(in oklab,var(--color-zinc-800) 60%,transparent)}}.bg-zinc-900{background-color:var(--color-zinc-900)}.bg-zinc-950{background-color:var(--color-zinc-950)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.px-0{padding-inline:calc(var(--spacing) * 0)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-10{padding-block:calc(var(--spacing) * 10)}.py-16{padding-block:calc(var(--spacing) * 16)}.pt-1{padding-top:calc(var(--spacing) * 1)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pt-5{padding-top:calc(var(--spacing) * 5)}.pr-1{padding-right:calc(var(--spacing) * 1)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[0\.12em\]{--tw-tracking:.12em;letter-spacing:.12em}.tracking-\[0\.14em\]{--tw-tracking:.14em;letter-spacing:.14em}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.whitespace-nowrap{white-space:nowrap}.text-amber-400{color:var(--color-amber-400)}.text-blue-400{color:var(--color-blue-400)}.text-emerald-400{color:var(--color-emerald-400)}.text-red-400{color:var(--color-red-400)}.text-red-500\/70{color:#fb2c36b3}@supports (color:color-mix(in lab,red,red)){.text-red-500\/70{color:color-mix(in oklab,var(--color-red-500) 70%,transparent)}}.text-violet-400{color:var(--color-violet-400)}.text-zinc-100{color:var(--color-zinc-100)}.text-zinc-200{color:var(--color-zinc-200)}.text-zinc-300{color:var(--color-zinc-300)}.text-zinc-400{color:var(--color-zinc-400)}.text-zinc-500{color:var(--color-zinc-500)}.text-zinc-600{color:var(--color-zinc-600)}.text-zinc-700{color:var(--color-zinc-700)}.text-zinc-900{color:var(--color-zinc-900)}.uppercase{text-transform:uppercase}.opacity-0{opacity:0}.opacity-100{opacity:1}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-amber-400\/25{--tw-ring-color:#fcbb0040}@supports (color:color-mix(in lab,red,red)){.ring-amber-400\/25{--tw-ring-color:color-mix(in oklab, var(--color-amber-400) 25%, transparent)}}.ring-blue-400\/25{--tw-ring-color:#54a2ff40}@supports (color:color-mix(in lab,red,red)){.ring-blue-400\/25{--tw-ring-color:color-mix(in oklab, var(--color-blue-400) 25%, transparent)}}.ring-emerald-400\/25{--tw-ring-color:#00d29440}@supports (color:color-mix(in lab,red,red)){.ring-emerald-400\/25{--tw-ring-color:color-mix(in oklab, var(--color-emerald-400) 25%, transparent)}}.ring-red-400\/25{--tw-ring-color:#ff656840}@supports (color:color-mix(in lab,red,red)){.ring-red-400\/25{--tw-ring-color:color-mix(in oklab, var(--color-red-400) 25%, transparent)}}.ring-violet-400\/25{--tw-ring-color:#a685ff40}@supports (color:color-mix(in lab,red,red)){.ring-violet-400\/25{--tw-ring-color:color-mix(in oklab, var(--color-violet-400) 25%, transparent)}}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.outline-none{--tw-outline-style:none;outline-style:none}@media(hover:hover){.group-hover\:bg-zinc-700:is(:where(.group):hover *){background-color:var(--color-zinc-700)}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.hover\:border-zinc-600:hover{border-color:var(--color-zinc-600)}.hover\:border-zinc-700:hover{border-color:var(--color-zinc-700)}.hover\:bg-white:hover{background-color:var(--color-white)}.hover\:bg-zinc-700:hover{background-color:var(--color-zinc-700)}.hover\:bg-zinc-800:hover{background-color:var(--color-zinc-800)}.hover\:bg-zinc-900:hover{background-color:var(--color-zinc-900)}.hover\:bg-zinc-900\/50:hover{background-color:#18181b80}@supports (color:color-mix(in lab,red,red)){.hover\:bg-zinc-900\/50:hover{background-color:color-mix(in oklab,var(--color-zinc-900) 50%,transparent)}}.hover\:text-zinc-200:hover{color:var(--color-zinc-200)}.hover\:text-zinc-300:hover{color:var(--color-zinc-300)}}.focus\:border-zinc-600:focus{border-color:var(--color-zinc-600)}.disabled\:opacity-40:disabled{opacity:.4}@media(min-width:48rem){.md\:flex{display:flex}.md\:w-12{width:calc(var(--spacing) * 12)}.md\:opacity-0{opacity:0}}@media(min-width:64rem){.lg\:block{display:block}}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-duration{syntax:"*";inherits:false}@keyframes pulse{50%{opacity:.5}}
@@ -0,0 +1,4 @@
1
+ export { EventuallyUIProvider, useEventuallyUI } from "./context.js";
2
+ export { EventuallyDashboard } from "./dashboard.js";
3
+ export type { EventuallyUIAction, EventuallyUIEvent, EventuallyUIJob, EventuallyUIJobState, EventuallyUIQueueSnapshot, EventuallyUISnapshot, EventuallyUIWorker, } from "./shared-types.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,oBAAoB,EACpB,yBAAyB,EACzB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { EventuallyUIProvider, useEventuallyUI } from "./context.js";
2
+ export { EventuallyDashboard } from "./dashboard.js";
@@ -0,0 +1,47 @@
1
+ import { FlowProducer, Queue, Scheduler, Worker } from "@ventually/core";
2
+ import { MemoryAdapter } from "@ventually/memory";
3
+ import type { EventuallyUISnapshot } from "./types.js";
4
+ type EmailJob = {
5
+ to: string;
6
+ subject: string;
7
+ html: string;
8
+ };
9
+ type EmailResult = {
10
+ messageId: string;
11
+ };
12
+ type EventEntry = EventuallyUISnapshot["events"][number];
13
+ export declare class EventuallyUIRuntime {
14
+ private offsetMs;
15
+ readonly adapter: MemoryAdapter;
16
+ readonly events: EventEntry[];
17
+ readonly workers: Map<string, Worker<unknown, unknown, unknown> & {
18
+ __paused?: boolean;
19
+ }>;
20
+ readonly scheduleHandles: Map<string, {
21
+ cancel(): void;
22
+ }>;
23
+ readonly emailQueue: Queue<EmailJob, EmailResult, number>;
24
+ readonly scheduler: Scheduler<EmailJob, EmailResult, number>;
25
+ readonly flowProducer: FlowProducer;
26
+ constructor();
27
+ start(): void;
28
+ snapshot(): EventuallyUISnapshot;
29
+ enqueueEmail(kind: "welcome" | "digest" | "failure"): Promise<void>;
30
+ enqueueFlow(): Promise<void>;
31
+ createRecurringEmail(): string;
32
+ cancelSchedule(id: string): void;
33
+ pauseWorker(queue: string): void;
34
+ resumeWorker(queue: string): void;
35
+ advanceTime(ms: number): void;
36
+ private toQueueSnapshot;
37
+ private registerWorker;
38
+ private pushEvent;
39
+ private now;
40
+ private sleep;
41
+ }
42
+ declare global {
43
+ var __eventuallyUIRuntime: EventuallyUIRuntime | undefined;
44
+ }
45
+ export declare function getEventuallyUIRuntime(): EventuallyUIRuntime;
46
+ export {};
47
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,KAAK,EACL,SAAS,EAET,MAAM,EACP,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,aAAa,EAA4B,MAAM,mBAAmB,CAAC;AAE5E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,KAAK,QAAQ,GAAG;IACd,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAMF,KAAK,UAAU,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAkDzD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAK;IACrB,QAAQ,CAAC,OAAO,gBAAuB;IACvC,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,CAAM;IACnC,QAAQ,CAAC,OAAO;mBAEmC,OAAO;OACtD;IACJ,QAAQ,CAAC,eAAe;kBAA+B,IAAI;OAAM;IACjE,QAAQ,CAAC,UAAU,uCAQhB;IACH,QAAQ,CAAC,SAAS,2CAOf;IACH,QAAQ,CAAC,YAAY,eAGlB;;IAuEH,KAAK,IAAI,IAAI;IAMb,QAAQ,IAAI,oBAAoB;IAgB1B,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBnE,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IA6BlC,oBAAoB,IAAI,MAAM;IAsB9B,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKhC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAShC,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IASjC,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAK7B,OAAO,CAAC,eAAe;IA+BvB,OAAO,CAAC,cAAc;IAuDtB,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,GAAG;YAIG,KAAK;CAGpB;AAED,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,qBAAqB,EAAE,mBAAmB,GAAG,SAAS,CAAC;CAC5D;AAED,wBAAgB,sBAAsB,IAAI,mBAAmB,CAI5D"}