@liiift-studio/deploy-vercel-from-sanity 1.1.0 → 1.2.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/dist/index.js CHANGED
@@ -30,26 +30,40 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ vercelConfigSchema: () => vercelConfigSchema,
33
34
  vercelDeploy: () => vercelDeploy,
34
35
  vercelDeploySchema: () => vercelDeploySchema
35
36
  });
36
37
  module.exports = __toCommonJS(index_exports);
37
- var import_sanity5 = require("sanity");
38
+ var import_sanity6 = require("sanity");
38
39
 
39
40
  // src/icons.tsx
40
41
  var import_react = require("react");
42
+
43
+ // src/compat/resolve.ts
44
+ var sanityUi = __toESM(require("@sanity/ui"));
41
45
  var sanityIcons = __toESM(require("@sanity/icons"));
46
+ var UI = sanityUi;
47
+ var ICONS = sanityIcons;
48
+ function resolveExport(ns, name) {
49
+ const value = ns[name];
50
+ return typeof value === "function" || typeof value === "object" && value !== null ? value : void 0;
51
+ }
52
+ var STACK_USES_GAP = typeof UI.Stack === "function";
53
+
54
+ // src/icons.tsx
42
55
  var import_jsx_runtime = require("react/jsx-runtime");
43
- var INSTALLED = sanityIcons;
44
56
  var GLYPH = { width: "1em", height: "1em", viewBox: "0 0 25 25", fill: "none" };
45
57
  var MissingIcon = (0, import_react.forwardRef)(function MissingIcon2(props, ref) {
46
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { ...GLYPH, xmlns: "http://www.w3.org/2000/svg", ...props, ref });
58
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { ...GLYPH, xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", focusable: "false", ...props, ref });
47
59
  });
60
+ var SYMBOL_MAP = resolveExport(ICONS, "icons");
48
61
  function resolveIcon(name, symbol) {
49
- const named = INSTALLED[name];
62
+ const named = resolveExport(ICONS, name);
50
63
  if (named) return named;
51
- const Icon = INSTALLED.Icon;
64
+ const Icon = resolveExport(ICONS, "Icon");
52
65
  if (!Icon) return MissingIcon;
66
+ if (SYMBOL_MAP && !(symbol in SYMBOL_MAP)) return MissingIcon;
53
67
  const Resolved = (0, import_react.forwardRef)(function SanityIcon(props, ref) {
54
68
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { symbol, ...props, ref });
55
69
  });
@@ -68,51 +82,101 @@ var EditIcon = resolveIcon("EditIcon", "edit");
68
82
  var EllipsisVerticalIcon = resolveIcon("EllipsisVerticalIcon", "ellipsis-vertical");
69
83
  var LaunchIcon = resolveIcon("LaunchIcon", "launch");
70
84
  var RocketIcon = resolveIcon("RocketIcon", "rocket");
71
- var SchemaIcon = resolveIcon("SchemaIcon", "schema");
72
85
  var TokenIcon = resolveIcon("TokenIcon", "token");
73
86
  var TrashIcon = resolveIcon("TrashIcon", "trash");
74
87
  var WarningOutlineIcon = resolveIcon("WarningOutlineIcon", "warning-outline");
75
88
 
76
89
  // src/components/DeployTool.tsx
77
- var import_react7 = require("react");
90
+ var import_react10 = require("react");
78
91
  var import_sanity3 = require("sanity");
79
- var import_ui11 = require("@sanity/ui");
80
92
 
81
- // src/ui.tsx
93
+ // src/components/DeployItem.tsx
94
+ var import_react7 = require("react");
95
+ var import_react_dom = require("react-dom");
96
+
97
+ // src/compat/primitives.tsx
82
98
  var import_react2 = require("react");
83
- var sanityUi = __toESM(require("@sanity/ui"));
84
- var import_ui = require("@sanity/ui");
85
99
  var import_jsx_runtime2 = require("react/jsx-runtime");
86
- var INSTALLED2 = sanityUi;
87
- var IS_UI_V4_PLUS = !("useToast" in INSTALLED2);
100
+ function domFallback(tag) {
101
+ const Fallback = (0, import_react2.forwardRef)(function SanityUiFallback(props, ref) {
102
+ const { children, style, id, className, onClick, href, title, ...rest } = props;
103
+ const passthrough = { style, id, className, onClick, href, title, ref };
104
+ for (const key of ["role", "type", "value", "checked", "placeholder", "disabled", "onChange", "onKeyDown"]) {
105
+ if (key in rest) passthrough[key] = rest[key];
106
+ }
107
+ for (const key of Object.keys(rest)) {
108
+ if (key.startsWith("aria-") || key.startsWith("data-")) passthrough[key] = rest[key];
109
+ }
110
+ return (0, import_react2.createElement)(tag, passthrough, children);
111
+ });
112
+ Fallback.displayName = `SanityUiFallback(${tag})`;
113
+ return Fallback;
114
+ }
115
+ function primitive(name, tag) {
116
+ return resolveExport(UI, name) ?? domFallback(tag);
117
+ }
118
+ var Box = primitive("Box", "div");
119
+ var Card = primitive("Card", "div");
120
+ var Flex = primitive("Flex", "div");
121
+ var Grid = primitive("Grid", "div");
122
+ var Text = primitive("Text", "span");
123
+ var Heading = primitive("Heading", "h2");
124
+ var Label = primitive("Label", "label");
125
+ var Badge = primitive("Badge", "span");
126
+ var Spinner = primitive("Spinner", "span");
127
+ var Button = primitive("Button", "button");
128
+ var TextInput = primitive("TextInput", "input");
129
+ var Select = primitive("Select", "select");
130
+ var Switch = primitive("Switch", "input");
131
+ var Dialog = primitive("Dialog", "div");
132
+ var SanityStack = primitive("Stack", "div");
88
133
  function Stack({ space, children, ...rest }) {
89
- const spacing = space === void 0 ? {} : IS_UI_V4_PLUS ? { gap: space } : { space };
90
- const Component = import_ui.Stack;
91
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Component, { ...rest, ...spacing, children });
134
+ const spacing = space === void 0 ? {} : STACK_USES_GAP ? { gap: space } : { space };
135
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SanityStack, { ...rest, ...spacing, children });
92
136
  }
93
- var LOCAL_TOAST_MS = 5e3;
137
+
138
+ // src/compat/toast.tsx
139
+ var import_react3 = require("react");
140
+ var import_jsx_runtime3 = require("react/jsx-runtime");
141
+ var LOCAL_TOAST_MS = 6e3;
142
+ var MAX_VISIBLE_TOASTS = 4;
143
+ var TOAST_Z_INDEX = 1e6;
94
144
  var LOCAL_TOAST_TONE = {
95
145
  success: "positive",
96
146
  error: "critical",
97
147
  warning: "caution",
98
148
  info: "primary"
99
149
  };
150
+ var PERSISTENT_STATUSES = /* @__PURE__ */ new Set(["error", "warning"]);
100
151
  var nextToastId = 0;
101
152
  var localToasts = [];
102
153
  var toastListeners = /* @__PURE__ */ new Set();
154
+ var dismissTimers = /* @__PURE__ */ new Map();
103
155
  function emitToasts() {
104
156
  for (const listener of toastListeners) listener(localToasts);
105
157
  }
158
+ function removeLocalToast(id) {
159
+ const timer = dismissTimers.get(id);
160
+ if (timer) {
161
+ clearTimeout(timer);
162
+ dismissTimers.delete(id);
163
+ }
164
+ localToasts = localToasts.filter((t) => t.id !== id);
165
+ emitToasts();
166
+ }
167
+ function scheduleDismiss(id, status) {
168
+ if (PERSISTENT_STATUSES.has(status ?? "info")) return;
169
+ const existing = dismissTimers.get(id);
170
+ if (existing) clearTimeout(existing);
171
+ dismissTimers.set(id, setTimeout(() => removeLocalToast(id), LOCAL_TOAST_MS));
172
+ }
106
173
  function pushLocalToast(params) {
107
174
  const toast = { ...params, id: nextToastId++ };
108
- localToasts = [...localToasts, toast];
175
+ localToasts = [...localToasts, toast].slice(-MAX_VISIBLE_TOASTS);
109
176
  emitToasts();
110
- setTimeout(() => {
111
- localToasts = localToasts.filter((t) => t.id !== toast.id);
112
- emitToasts();
113
- }, LOCAL_TOAST_MS);
177
+ scheduleDismiss(toast.id, toast.status);
114
178
  }
115
- var installedUseToast = INSTALLED2.useToast;
179
+ var installedUseToast = resolveExport(UI, "useToast");
116
180
  var localToaster = { push: pushLocalToast };
117
181
  function useToast() {
118
182
  const real = installedUseToast;
@@ -120,160 +184,330 @@ function useToast() {
120
184
  return localToaster;
121
185
  }
122
186
  function ToastViewport() {
123
- const [toasts, setToasts] = (0, import_react2.useState)(localToasts);
124
- (0, import_react2.useEffect)(() => {
187
+ const [toasts, setToasts] = (0, import_react3.useState)(localToasts);
188
+ (0, import_react3.useEffect)(() => {
125
189
  if (installedUseToast) return;
126
190
  toastListeners.add(setToasts);
191
+ setToasts(localToasts);
127
192
  return () => {
128
193
  toastListeners.delete(setToasts);
129
194
  };
130
195
  }, []);
131
- if (installedUseToast || toasts.length === 0) return null;
132
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ui.Box, { style: { position: "fixed", bottom: 16, right: 16, zIndex: 1e6, maxWidth: 360 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Stack, { space: 2, children: toasts.map((toast) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
133
- import_ui.Card,
196
+ const hold = (0, import_react3.useCallback)((id) => {
197
+ const timer = dismissTimers.get(id);
198
+ if (timer) {
199
+ clearTimeout(timer);
200
+ dismissTimers.delete(id);
201
+ }
202
+ }, []);
203
+ if (installedUseToast) return null;
204
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
205
+ Box,
134
206
  {
135
- padding: 3,
136
- radius: 2,
137
- shadow: 3,
138
- tone: LOCAL_TOAST_TONE[toast.status ?? "info"] ?? "primary",
139
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Stack, { space: 2, children: [
140
- toast.title && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ui.Text, { size: 1, weight: "semibold", children: toast.title }),
141
- toast.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ui.Text, { size: 1, muted: true, children: toast.description })
142
- ] })
143
- },
144
- toast.id
145
- )) }) });
207
+ role: "status",
208
+ "aria-live": "polite",
209
+ "aria-atomic": false,
210
+ style: {
211
+ position: "fixed",
212
+ bottom: 16,
213
+ right: 16,
214
+ zIndex: TOAST_Z_INDEX,
215
+ width: "min(360px, calc(100vw - 32px))",
216
+ pointerEvents: "none"
217
+ },
218
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stack, { space: 2, children: toasts.map((toast) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
219
+ Card,
220
+ {
221
+ padding: 3,
222
+ radius: 2,
223
+ shadow: 3,
224
+ tone: LOCAL_TOAST_TONE[toast.status ?? "info"] ?? "primary",
225
+ style: { pointerEvents: "auto" },
226
+ onMouseEnter: () => hold(toast.id),
227
+ onFocusCapture: () => hold(toast.id),
228
+ onMouseLeave: () => scheduleDismiss(toast.id, toast.status),
229
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Flex, { align: "flex-start", gap: 3, children: [
230
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Stack, { space: 2, flex: 1, children: [
231
+ toast.title && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { size: 1, weight: "semibold", children: toast.title }),
232
+ toast.description && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { size: 1, muted: true, children: toast.description })
233
+ ] }),
234
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
235
+ Button,
236
+ {
237
+ mode: "bleed",
238
+ padding: 2,
239
+ icon: CloseIcon,
240
+ text: "",
241
+ "aria-label": "Dismiss notification",
242
+ onClick: () => removeLocalToast(toast.id)
243
+ }
244
+ )
245
+ ] })
246
+ },
247
+ toast.id
248
+ )) })
249
+ }
250
+ );
146
251
  }
147
- var InstalledTooltip = INSTALLED2.Tooltip;
252
+
253
+ // src/compat/tooltip.tsx
254
+ var import_react4 = require("react");
255
+ var import_jsx_runtime4 = require("react/jsx-runtime");
256
+ var InstalledTooltip = resolveExport(UI, "Tooltip");
148
257
  function Tooltip({ text, children }) {
258
+ const id = (0, import_react4.useId)();
259
+ const [visible, setVisible] = (0, import_react4.useState)(false);
149
260
  if (InstalledTooltip) {
150
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(InstalledTooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ui.Box, { padding: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ui.Text, { size: 1, children: text }) }), portal: true, children });
261
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(InstalledTooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box, { padding: 2, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { size: 1, children: text }) }), portal: true, children });
151
262
  }
152
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { title: text, style: { display: "inline-flex" }, children });
263
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
264
+ "span",
265
+ {
266
+ style: { position: "relative", display: "inline-flex" },
267
+ "aria-describedby": id,
268
+ onMouseEnter: () => setVisible(true),
269
+ onMouseLeave: () => setVisible(false),
270
+ onFocusCapture: () => setVisible(true),
271
+ onBlurCapture: () => setVisible(false),
272
+ onKeyDown: (e) => {
273
+ if (e.key === "Escape") setVisible(false);
274
+ },
275
+ children: [
276
+ children,
277
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
278
+ Card,
279
+ {
280
+ id,
281
+ role: "tooltip",
282
+ radius: 2,
283
+ shadow: 2,
284
+ padding: 2,
285
+ style: {
286
+ position: "absolute",
287
+ bottom: "100%",
288
+ left: "50%",
289
+ transform: "translateX(-50%)",
290
+ marginBottom: 4,
291
+ whiteSpace: "nowrap",
292
+ pointerEvents: "none",
293
+ zIndex: 1e3,
294
+ // Kept in the accessibility tree when hidden so `aria-describedby` still resolves.
295
+ visibility: visible ? "visible" : "hidden"
296
+ },
297
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { size: 1, children: text })
298
+ }
299
+ )
300
+ ]
301
+ }
302
+ );
153
303
  }
154
- var InstalledMenuButton = INSTALLED2.MenuButton;
155
- var InstalledMenu = INSTALLED2.Menu;
156
- var InstalledMenuItem = INSTALLED2.MenuItem;
157
- var HAS_INSTALLED_MENU = Boolean(InstalledMenuButton && InstalledMenu && InstalledMenuItem);
158
- function ActionMenu({ id, items, buttonIcon }) {
159
- const [open, setOpen] = (0, import_react2.useState)(false);
160
- const wrapRef = (0, import_react2.useRef)(null);
161
- (0, import_react2.useEffect)(() => {
162
- if (HAS_INSTALLED_MENU || !open) return;
304
+
305
+ // src/compat/menu.tsx
306
+ var import_react5 = require("react");
307
+ var import_jsx_runtime5 = require("react/jsx-runtime");
308
+ var InstalledMenuButton = resolveExport(UI, "MenuButton");
309
+ var InstalledMenu = resolveExport(UI, "Menu");
310
+ var InstalledMenuItem = resolveExport(UI, "MenuItem");
311
+ var INSTALLED_MENU = InstalledMenuButton && InstalledMenu && InstalledMenuItem ? { MenuButton: InstalledMenuButton, Menu: InstalledMenu, MenuItem: InstalledMenuItem } : null;
312
+ var itemKey = (item, index) => item.key ?? `${index}-${item.text}`;
313
+ function ActionMenu({ id, label, items, buttonIcon }) {
314
+ const menuId = (0, import_react5.useId)();
315
+ const [open, setOpen] = (0, import_react5.useState)(false);
316
+ const [activeIndex, setActiveIndex] = (0, import_react5.useState)(0);
317
+ const wrapRef = (0, import_react5.useRef)(null);
318
+ const triggerRef = (0, import_react5.useRef)(null);
319
+ const itemRefs = (0, import_react5.useRef)([]);
320
+ const [frozenItems, setFrozenItems] = (0, import_react5.useState)(items);
321
+ const shownItems = open ? frozenItems : items;
322
+ const close = (0, import_react5.useCallback)((returnFocus = true) => {
323
+ setOpen(false);
324
+ if (returnFocus) triggerRef.current?.focus();
325
+ }, []);
326
+ const openMenu = (0, import_react5.useCallback)((index) => {
327
+ setFrozenItems(items);
328
+ setActiveIndex(index);
329
+ setOpen(true);
330
+ }, [items]);
331
+ (0, import_react5.useEffect)(() => {
332
+ if (!open || INSTALLED_MENU) return;
333
+ itemRefs.current[activeIndex]?.focus();
334
+ }, [open, activeIndex]);
335
+ (0, import_react5.useEffect)(() => {
336
+ if (INSTALLED_MENU || !open) return;
163
337
  const onPointerDown = (e) => {
164
338
  if (!wrapRef.current?.contains(e.target)) setOpen(false);
165
339
  };
166
- const onKeyDown = (e) => {
167
- if (e.key === "Escape") setOpen(false);
168
- };
169
340
  document.addEventListener("mousedown", onPointerDown);
170
- document.addEventListener("keydown", onKeyDown);
171
- return () => {
172
- document.removeEventListener("mousedown", onPointerDown);
173
- document.removeEventListener("keydown", onKeyDown);
174
- };
341
+ return () => document.removeEventListener("mousedown", onPointerDown);
175
342
  }, [open]);
176
- const runAction = (0, import_react2.useCallback)((item) => {
177
- setOpen(false);
343
+ const runAction = (0, import_react5.useCallback)((item) => {
344
+ close();
178
345
  item.onClick?.();
179
- }, []);
180
- if (HAS_INSTALLED_MENU && InstalledMenuButton && InstalledMenu && InstalledMenuItem) {
181
- const MenuButton = InstalledMenuButton;
182
- const Menu = InstalledMenu;
183
- const MenuItem = InstalledMenuItem;
184
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
346
+ }, [close]);
347
+ if (INSTALLED_MENU) {
348
+ const { MenuButton, Menu, MenuItem } = INSTALLED_MENU;
349
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
185
350
  MenuButton,
186
351
  {
187
352
  id,
188
- button: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ui.Button, { mode: "ghost", icon: buttonIcon, padding: 2 }),
353
+ button: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Button, { mode: "ghost", icon: buttonIcon, padding: 2, "aria-label": label }),
189
354
  popover: { placement: "bottom-end" },
190
- menu: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Menu, { children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
355
+ menu: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Menu, { children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
191
356
  MenuItem,
192
357
  {
193
358
  text: item.text,
194
359
  icon: item.icon,
195
360
  tone: item.tone,
196
- ...item.href ? { as: "a", href: item.href, target: "_blank", rel: "noreferrer" } : { onClick: () => item.onClick?.() }
361
+ ...item.href ? { as: "a", href: item.href, target: "_blank", rel: "noreferrer" } : { onClick: item.onClick }
197
362
  },
198
- item.text
363
+ itemKey(item, i)
199
364
  )) })
200
365
  }
201
366
  );
202
367
  }
203
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { ref: wrapRef, style: { position: "relative" }, children: [
204
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
205
- import_ui.Button,
368
+ const onMenuKeyDown = (e) => {
369
+ const last = shownItems.length - 1;
370
+ if (e.key === "Escape") {
371
+ e.stopPropagation();
372
+ close();
373
+ return;
374
+ }
375
+ if (e.key === "Tab") {
376
+ close(false);
377
+ return;
378
+ }
379
+ if (e.key === "ArrowDown") {
380
+ e.preventDefault();
381
+ setActiveIndex((i) => i >= last ? 0 : i + 1);
382
+ return;
383
+ }
384
+ if (e.key === "ArrowUp") {
385
+ e.preventDefault();
386
+ setActiveIndex((i) => i <= 0 ? last : i - 1);
387
+ return;
388
+ }
389
+ if (e.key === "Home") {
390
+ e.preventDefault();
391
+ setActiveIndex(0);
392
+ return;
393
+ }
394
+ if (e.key === "End") {
395
+ e.preventDefault();
396
+ setActiveIndex(last);
397
+ return;
398
+ }
399
+ };
400
+ const onTriggerKeyDown = (e) => {
401
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
402
+ e.preventDefault();
403
+ openMenu(0);
404
+ } else if (e.key === "ArrowUp") {
405
+ e.preventDefault();
406
+ openMenu(items.length - 1);
407
+ }
408
+ };
409
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { ref: wrapRef, style: { position: "relative" }, children: [
410
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
411
+ Button,
206
412
  {
413
+ ref: triggerRef,
207
414
  mode: "ghost",
208
415
  icon: buttonIcon,
209
416
  padding: 2,
210
417
  id,
418
+ "aria-label": label,
211
419
  "aria-haspopup": "menu",
212
420
  "aria-expanded": open,
213
- onClick: () => setOpen((o) => !o)
421
+ "aria-controls": open ? menuId : void 0,
422
+ onClick: () => open ? close() : openMenu(0),
423
+ onKeyDown: onTriggerKeyDown
214
424
  }
215
425
  ),
216
- open && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
217
- import_ui.Card,
426
+ open && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
427
+ Card,
218
428
  {
429
+ id: menuId,
219
430
  radius: 2,
220
431
  shadow: 3,
221
432
  padding: 1,
222
433
  role: "menu",
223
- style: { position: "absolute", top: "100%", right: 0, marginTop: 4, zIndex: 1e3, minWidth: 180 },
224
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Stack, { space: 1, children: items.map((item) => {
434
+ "aria-labelledby": id,
435
+ onKeyDown: onMenuKeyDown,
436
+ style: { position: "absolute", top: "100%", right: 0, marginTop: 4, zIndex: 1e3, minWidth: 200 },
437
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Stack, { space: 1, role: "none", children: shownItems.map((item, i) => {
225
438
  const Icon = item.icon;
226
- const label = /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ui.Flex, { align: "center", gap: 2, paddingX: 2, paddingY: 2, children: [
227
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { width: "1em", height: "1em" }),
228
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ui.Text, { size: 1, children: item.text })
439
+ const row = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Flex, { align: "center", gap: 2, paddingX: 2, paddingY: 2, children: [
440
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Icon, { width: "1em", height: "1em", "aria-hidden": "true" }),
441
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { size: 1, children: item.text })
229
442
  ] });
230
- return item.href ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
443
+ const content = item.tone === "critical" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Card, { tone: "critical", radius: 1, children: row }) : row;
444
+ const shared = {
445
+ role: "menuitem",
446
+ // Roving tabindex — the menu is one tab stop, arrows move within it.
447
+ tabIndex: i === activeIndex ? 0 : -1,
448
+ ref: (el) => {
449
+ itemRefs.current[i] = el;
450
+ },
451
+ onMouseEnter: () => setActiveIndex(i),
452
+ style: {
453
+ display: "block",
454
+ width: "100%",
455
+ textAlign: "left",
456
+ cursor: "pointer",
457
+ borderRadius: 3,
458
+ background: "none",
459
+ border: 0,
460
+ padding: 0,
461
+ font: "inherit",
462
+ color: "inherit",
463
+ textDecoration: "none",
464
+ // Card suppresses its own focus ring, so the active item draws one explicitly.
465
+ outline: i === activeIndex ? "2px solid var(--card-focus-ring-color, currentColor)" : "none",
466
+ outlineOffset: -2
467
+ }
468
+ };
469
+ return item.href ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
231
470
  "a",
232
471
  {
233
- role: "menuitem",
472
+ ...shared,
234
473
  href: item.href,
235
474
  target: "_blank",
236
475
  rel: "noreferrer",
237
- onClick: () => setOpen(false),
238
- style: { color: "inherit", textDecoration: "none", display: "block" },
239
- children: label
476
+ onClick: () => close(false),
477
+ children: content
240
478
  },
241
- item.text
242
- ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
243
- import_ui.Card,
479
+ itemKey(item, i)
480
+ ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
481
+ "button",
244
482
  {
245
- as: "button",
246
- role: "menuitem",
247
- tone: item.tone === "critical" ? "critical" : "default",
248
- radius: 1,
483
+ ...shared,
484
+ type: "button",
249
485
  onClick: () => runAction(item),
250
- style: { display: "block", width: "100%", textAlign: "left", cursor: "pointer", border: 0, background: "none" },
251
- children: label
486
+ children: content
252
487
  },
253
- item.text
488
+ itemKey(item, i)
254
489
  );
255
490
  }) })
256
491
  }
257
492
  )
258
493
  ] });
259
494
  }
260
- var InstalledCode = INSTALLED2.Code;
495
+
496
+ // src/compat/code.tsx
497
+ var import_jsx_runtime6 = require("react/jsx-runtime");
498
+ var InstalledCode = resolveExport(UI, "Code");
261
499
  var FALLBACK_CODE_STYLE = {
500
+ display: "block",
262
501
  fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
263
502
  fontSize: "0.8125rem",
264
503
  lineHeight: 1.4,
265
504
  margin: 0
266
505
  };
267
506
  function Code({ style, children }) {
268
- if (InstalledCode) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(InstalledCode, { size: 1, style, children });
269
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { style: { ...FALLBACK_CODE_STYLE, ...style }, children });
507
+ if (InstalledCode) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(InstalledCode, { size: 1, style, children });
508
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { style: { ...FALLBACK_CODE_STYLE, ...style }, children });
270
509
  }
271
510
 
272
- // src/components/DeployItem.tsx
273
- var import_react4 = require("react");
274
- var import_react_dom = require("react-dom");
275
- var import_ui5 = require("@sanity/ui");
276
-
277
511
  // src/lib/api.ts
278
512
  var BASE = "https://api.vercel.com";
279
513
  var VERCEL_HOOK_RE = /^https:\/\/api\.vercel\.com\/v1\/integrations\/deploy\//;
@@ -306,10 +540,14 @@ async function listDeployments(opts) {
306
540
  return data.deployments ?? [];
307
541
  }
308
542
  async function cancelDeployment(opts) {
309
- const params = opts.teamId ? `?teamId=${opts.teamId}` : "";
310
- await vercelFetch(`/v12/deployments/${opts.deploymentId}/cancel${params}`, opts.token, {
311
- method: "PATCH"
312
- });
543
+ const params = new URLSearchParams();
544
+ if (opts.teamId) params.set("teamId", opts.teamId);
545
+ const query = params.toString() ? `?${params}` : "";
546
+ await vercelFetch(
547
+ `/v12/deployments/${encodeURIComponent(opts.deploymentId)}/cancel${query}`,
548
+ opts.token,
549
+ { method: "PATCH" }
550
+ );
313
551
  }
314
552
  async function triggerDeploy(hookUrl) {
315
553
  if (!VERCEL_HOOK_RE.test(hookUrl)) {
@@ -373,6 +611,11 @@ function safeHref(url) {
373
611
  return void 0;
374
612
  }
375
613
  }
614
+ function deploymentHref(host) {
615
+ if (!host) return void 0;
616
+ if (!/^[a-z0-9.-]+$/i.test(host)) return void 0;
617
+ return safeHref(`https://${host}`);
618
+ }
376
619
  function shortSha(sha) {
377
620
  return sha ? sha.slice(0, 7) : "";
378
621
  }
@@ -415,27 +658,25 @@ function projectHref(inspectorUrl) {
415
658
  }
416
659
 
417
660
  // src/components/StatusBadge.tsx
418
- var import_ui2 = require("@sanity/ui");
419
- var import_jsx_runtime3 = require("react/jsx-runtime");
661
+ var import_jsx_runtime7 = require("react/jsx-runtime");
420
662
  function StatusBadge({ state, showSpinner }) {
421
663
  const { label, tone } = stateLabel(state);
422
664
  const spinning = showSpinner && (state === "QUEUED" || state === "INITIALIZING" || state === "BUILDING");
423
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Flex, { align: "center", gap: 2, children: [
424
- spinning && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Spinner, { muted: true }),
425
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Badge, { tone, padding: 2, children: label })
665
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Flex, { align: "center", gap: 2, children: [
666
+ spinning && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Spinner, { muted: true }),
667
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Badge, { tone, padding: 2, children: label })
426
668
  ] });
427
669
  }
428
670
 
429
671
  // src/components/DeployHistory.tsx
430
- var import_react3 = require("react");
431
- var import_ui3 = require("@sanity/ui");
432
- var import_jsx_runtime4 = require("react/jsx-runtime");
672
+ var import_react6 = require("react");
673
+ var import_jsx_runtime8 = require("react/jsx-runtime");
433
674
  function DeployHistory({ target, token, onClose }) {
434
- const [deployments, setDeployments] = (0, import_react3.useState)([]);
435
- const [loading, setLoading] = (0, import_react3.useState)(true);
436
- const [error, setError] = (0, import_react3.useState)(null);
675
+ const [deployments, setDeployments] = (0, import_react6.useState)([]);
676
+ const [loading, setLoading] = (0, import_react6.useState)(true);
677
+ const [error, setError] = (0, import_react6.useState)(null);
437
678
  const { projectId, hookId } = parseHookUrl(target.url);
438
- const load = (0, import_react3.useCallback)(async () => {
679
+ const load = (0, import_react6.useCallback)(async () => {
439
680
  setLoading(true);
440
681
  setError(null);
441
682
  try {
@@ -447,64 +688,68 @@ function DeployHistory({ target, token, onClose }) {
447
688
  setLoading(false);
448
689
  }
449
690
  }, [projectId, hookId, token, target.teamId]);
450
- (0, import_react3.useEffect)(() => {
691
+ (0, import_react6.useEffect)(() => {
451
692
  load();
452
693
  }, [load]);
453
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
454
- import_ui3.Dialog,
694
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
695
+ Dialog,
455
696
  {
456
697
  header: `${target.name} \u2014 Deployment History`,
457
698
  id: "deploy-history",
458
699
  onClose,
459
700
  width: 2,
460
- footer: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Box, { padding: 3, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Button, { text: "Close", icon: CloseIcon, mode: "ghost", onClick: onClose }) }),
461
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ui3.Box, { padding: 4, children: [
462
- loading && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Flex, { justify: "center", padding: 6, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Spinner, { muted: true }) }),
463
- error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Card, { tone: "critical", padding: 4, radius: 2, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Text, { size: 1, children: error }) }),
464
- !loading && !error && deployments.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Card, { tone: "transparent", padding: 4, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Text, { size: 1, muted: true, align: "center", children: "No deployments found for this hook." }) }),
465
- !loading && deployments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Stack, { space: 2, children: [
466
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Card, { padding: 3, radius: 2, tone: "transparent", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ui3.Flex, { gap: 3, children: [
467
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Box, { flex: 2, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Text, { size: 0, weight: "semibold", muted: true, children: "Preview URL" }) }),
468
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Text, { size: 0, weight: "semibold", muted: true, children: "Status" }) }),
469
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Box, { flex: 2, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Text, { size: 0, weight: "semibold", muted: true, children: "Branch \xB7 Commit" }) }),
470
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Text, { size: 0, weight: "semibold", muted: true, children: "Deployed" }) }),
471
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Box, { style: { width: 64 }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Text, { size: 0, weight: "semibold", muted: true, children: "Logs" }) })
701
+ footer: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { padding: 3, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Button, { text: "Close", icon: CloseIcon, mode: "ghost", onClick: onClose }) }),
702
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box, { padding: 4, children: [
703
+ loading && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Flex, { justify: "center", padding: 6, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Spinner, { muted: true }) }),
704
+ error && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { tone: "critical", padding: 4, radius: 2, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 1, children: error }) }),
705
+ !loading && !error && deployments.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { tone: "transparent", padding: 4, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 1, muted: true, align: "center", children: "No deployments found for this hook." }) }),
706
+ !loading && deployments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Stack, { space: 2, children: [
707
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { padding: 3, radius: 2, tone: "transparent", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Flex, { gap: 3, children: [
708
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { flex: 2, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 0, weight: "semibold", muted: true, children: "Preview URL" }) }),
709
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 0, weight: "semibold", muted: true, children: "Status" }) }),
710
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { flex: 2, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 0, weight: "semibold", muted: true, children: "Branch \xB7 Commit" }) }),
711
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 0, weight: "semibold", muted: true, children: "Deployed" }) }),
712
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { style: { width: 64 }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 0, weight: "semibold", muted: true, children: "Logs" }) })
472
713
  ] }) }),
473
714
  deployments.map((d) => {
474
715
  const { label, tone } = stateLabel(d.state);
475
716
  const branch = d.meta?.githubCommitRef ?? "\u2014";
476
717
  const sha = shortSha(d.meta?.githubCommitSha);
477
718
  const message = d.meta?.githubCommitMessage?.split("\n")[0] ?? "";
478
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Card, { padding: 3, radius: 2, shadow: 1, tone: "default", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ui3.Flex, { gap: 3, align: "center", children: [
479
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Box, { flex: 2, style: { overflow: "hidden" }, children: d.url ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
719
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { padding: 3, radius: 2, shadow: 1, tone: "default", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Flex, { gap: 3, align: "center", children: [
720
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { flex: 2, style: { overflow: "hidden" }, children: deploymentHref(d.url) ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
480
721
  "a",
481
722
  {
482
- href: `https://${d.url}`,
723
+ href: deploymentHref(d.url),
483
724
  target: "_blank",
484
725
  rel: "noreferrer",
485
726
  style: { color: "inherit" },
486
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Text, { size: 1, style: { textDecoration: "underline" }, children: d.url.length > 36 ? `${d.url.slice(0, 36)}\u2026` : d.url })
727
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 1, style: { textDecoration: "underline" }, children: d.url.length > 36 ? `${d.url.slice(0, 36)}\u2026` : d.url })
487
728
  }
488
- ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Text, { size: 1, muted: true, children: "\u2014" }) }),
489
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Badge, { tone, children: label }) }),
490
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Box, { flex: 2, style: { overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Stack, { space: 1, children: [
491
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Text, { size: 1, children: branch }),
492
- sha && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ui3.Text, { size: 0, muted: true, children: [
729
+ ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 1, muted: true, children: "\u2014" }) }),
730
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Badge, { tone, children: label }) }),
731
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { flex: 2, style: { overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Stack, { space: 1, children: [
732
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 1, children: branch }),
733
+ sha && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { size: 0, muted: true, children: [
493
734
  sha,
494
735
  message ? ` \xB7 ${message.slice(0, 40)}${message.length > 40 ? "\u2026" : ""}` : ""
495
736
  ] })
496
737
  ] }) }),
497
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Text, { size: 1, muted: true, children: timeAgo(d.created) }) }),
498
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Box, { style: { width: 64 }, children: safeHref(d.inspectorUrl) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { href: safeHref(d.inspectorUrl), target: "_blank", rel: "noreferrer", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
499
- import_ui3.Button,
738
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 1, muted: true, children: timeAgo(d.created) }) }),
739
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box, { style: { width: 64 }, children: safeHref(d.inspectorUrl) ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
740
+ Button,
500
741
  {
742
+ as: "a",
743
+ href: safeHref(d.inspectorUrl),
744
+ target: "_blank",
745
+ rel: "noreferrer",
501
746
  text: "Logs",
502
747
  mode: "ghost",
503
748
  tone: "default",
504
749
  icon: LaunchIcon,
505
750
  style: { fontSize: "12px" }
506
751
  }
507
- ) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Text, { size: 1, muted: true, children: "\u2014" }) })
752
+ ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { size: 1, muted: true, children: "\u2014" }) })
508
753
  ] }) }, d.uid);
509
754
  })
510
755
  ] })
@@ -514,58 +759,69 @@ function DeployHistory({ target, token, onClose }) {
514
759
  }
515
760
 
516
761
  // src/components/DeployItem.tsx
517
- var import_jsx_runtime5 = require("react/jsx-runtime");
762
+ var import_jsx_runtime9 = require("react/jsx-runtime");
518
763
  var POLL_INTERVAL_MS = 5e3;
519
764
  var LABEL_WIDTH = 64;
520
765
  var PENDING_TIMEOUT_MS = 6e4;
521
766
  function DeployItem({ target, token, onDelete, onEdit }) {
522
767
  const { projectId, hookId } = parseHookUrl(target.url);
523
768
  const toast = useToast();
524
- const [deployments, setDeployments] = (0, import_react4.useState)([]);
525
- const [loadingInitial, setLoadingInitial] = (0, import_react4.useState)(true);
526
- const [pendingSince, setPendingSince] = (0, import_react4.useState)(null);
527
- const [canceling, setCanceling] = (0, import_react4.useState)(false);
528
- const [deployError, setDeployError] = (0, import_react4.useState)(null);
529
- const [showHistory, setShowHistory] = (0, import_react4.useState)(false);
530
- const [elapsed, setElapsed] = (0, import_react4.useState)(0);
531
- const [copied, setCopied] = (0, import_react4.useState)(false);
532
- const [showDetails, setShowDetails] = (0, import_react4.useState)(false);
533
- const [showErrorLogs, setShowErrorLogs] = (0, import_react4.useState)(false);
534
- const [errorLines, setErrorLines] = (0, import_react4.useState)([]);
535
- const [loadingLogs, setLoadingLogs] = (0, import_react4.useState)(false);
536
- const [logError, setLogError] = (0, import_react4.useState)(null);
537
- const triggeredFromUidRef = (0, import_react4.useRef)(void 0);
769
+ const [deployments, setDeployments] = (0, import_react7.useState)([]);
770
+ const [loadingInitial, setLoadingInitial] = (0, import_react7.useState)(true);
771
+ const [pendingSince, setPendingSince] = (0, import_react7.useState)(null);
772
+ const [canceling, setCanceling] = (0, import_react7.useState)(false);
773
+ const [deployError, setDeployError] = (0, import_react7.useState)(null);
774
+ const [showHistory, setShowHistory] = (0, import_react7.useState)(false);
775
+ const [elapsed, setElapsed] = (0, import_react7.useState)(0);
776
+ const [copied, setCopied] = (0, import_react7.useState)(false);
777
+ const [showDetails, setShowDetails] = (0, import_react7.useState)(false);
778
+ const [showErrorLogs, setShowErrorLogs] = (0, import_react7.useState)(false);
779
+ const [errorLines, setErrorLines] = (0, import_react7.useState)([]);
780
+ const [loadingLogs, setLoadingLogs] = (0, import_react7.useState)(false);
781
+ const [logError, setLogError] = (0, import_react7.useState)(null);
782
+ const [pollError, setPollError] = (0, import_react7.useState)(null);
783
+ const triggeredFromUidRef = (0, import_react7.useRef)(void 0);
784
+ const requestSeqRef = (0, import_react7.useRef)(0);
785
+ const mountedRef = (0, import_react7.useRef)(true);
786
+ (0, import_react7.useEffect)(() => () => {
787
+ mountedRef.current = false;
788
+ }, []);
538
789
  const latest = deployments[0];
539
790
  const isPending = pendingSince !== null;
540
791
  const isActive = isPending || isActiveState(latest?.state);
541
- const fetchDeployments = (0, import_react4.useCallback)(async () => {
792
+ const fetchDeployments = (0, import_react7.useCallback)(async () => {
542
793
  if (!projectId || !hookId || !token) return;
794
+ const seq = ++requestSeqRef.current;
543
795
  try {
544
796
  const data = await listDeployments({ projectId, hookId, token, teamId: target.teamId });
797
+ if (seq !== requestSeqRef.current || !mountedRef.current) return;
545
798
  setDeployments(data);
799
+ setPollError(null);
546
800
  } catch (err) {
547
- console.error("deploy-vercel-from-sanity: fetch error", err);
801
+ if (seq !== requestSeqRef.current || !mountedRef.current) return;
802
+ setPollError(err instanceof Error ? err.message : "Could not reach the Vercel API");
803
+ console.error("Deploy-vercel-from-sanity: fetch error", err);
548
804
  }
549
805
  }, [projectId, hookId, token, target.teamId]);
550
- (0, import_react4.useEffect)(() => {
806
+ (0, import_react7.useEffect)(() => {
551
807
  fetchDeployments().finally(() => setLoadingInitial(false));
552
808
  }, [fetchDeployments]);
553
- (0, import_react4.useEffect)(() => {
809
+ (0, import_react7.useEffect)(() => {
554
810
  if (!isActive) return;
555
811
  const id = setInterval(fetchDeployments, POLL_INTERVAL_MS);
556
812
  return () => clearInterval(id);
557
813
  }, [isActive, fetchDeployments]);
558
- (0, import_react4.useEffect)(() => {
814
+ (0, import_react7.useEffect)(() => {
559
815
  if (!isPending) return;
560
816
  if (latest?.uid && latest.uid !== triggeredFromUidRef.current) setPendingSince(null);
561
817
  }, [isPending, latest?.uid]);
562
- (0, import_react4.useEffect)(() => {
818
+ (0, import_react7.useEffect)(() => {
563
819
  if (!isPending) return;
564
820
  const id = setTimeout(() => setPendingSince(null), PENDING_TIMEOUT_MS);
565
821
  return () => clearTimeout(id);
566
822
  }, [isPending]);
567
- const prevStateRef = (0, import_react4.useRef)(void 0);
568
- (0, import_react4.useEffect)(() => {
823
+ const prevStateRef = (0, import_react7.useRef)(void 0);
824
+ (0, import_react7.useEffect)(() => {
569
825
  const current = latest?.state;
570
826
  const prev = prevStateRef.current;
571
827
  if (prev && isActiveState(prev) && current && !isActiveState(current)) {
@@ -579,13 +835,13 @@ function DeployItem({ target, token, onDelete, onEdit }) {
579
835
  }
580
836
  prevStateRef.current = current;
581
837
  }, [latest?.state, target.name, toast]);
582
- (0, import_react4.useEffect)(() => {
838
+ (0, import_react7.useEffect)(() => {
583
839
  setShowErrorLogs(false);
584
840
  setErrorLines([]);
585
841
  setLogError(null);
586
842
  }, [latest?.uid]);
587
- const timerRef = (0, import_react4.useRef)(null);
588
- (0, import_react4.useEffect)(() => {
843
+ const timerRef = (0, import_react7.useRef)(null);
844
+ (0, import_react7.useEffect)(() => {
589
845
  if (isActive) {
590
846
  const start = pendingSince ?? latest?.created ?? Date.now();
591
847
  setElapsed(Math.floor((Date.now() - start) / 1e3));
@@ -600,7 +856,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
600
856
  if (timerRef.current) clearInterval(timerRef.current);
601
857
  };
602
858
  }, [isActive, pendingSince, latest?.created]);
603
- const deploy = (0, import_react4.useCallback)(() => {
859
+ const deploy = (0, import_react7.useCallback)(() => {
604
860
  (0, import_react_dom.flushSync)(() => {
605
861
  setDeployError(null);
606
862
  triggeredFromUidRef.current = latest?.uid;
@@ -616,7 +872,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
616
872
  }
617
873
  })();
618
874
  }, [target.url, fetchDeployments, latest?.uid]);
619
- const cancel = (0, import_react4.useCallback)(async () => {
875
+ const cancel = (0, import_react7.useCallback)(async () => {
620
876
  if (!latest?.uid) return;
621
877
  setCanceling(true);
622
878
  try {
@@ -628,9 +884,9 @@ function DeployItem({ target, token, onDelete, onEdit }) {
628
884
  setCanceling(false);
629
885
  }
630
886
  }, [latest?.uid, token, target.teamId, fetchDeployments]);
631
- const copyUrl = (0, import_react4.useCallback)(() => {
887
+ const copyUrl = (0, import_react7.useCallback)(() => {
632
888
  if (!latest?.url) return;
633
- const fullUrl = `https://${latest.url}`;
889
+ const fullUrl = deploymentHref(latest.url) ?? "";
634
890
  navigator.clipboard.writeText(fullUrl).then(() => {
635
891
  setCopied(true);
636
892
  setTimeout(() => setCopied(false), 2e3);
@@ -638,7 +894,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
638
894
  window.prompt("Copy deployment URL:", fullUrl);
639
895
  });
640
896
  }, [latest?.url]);
641
- const fetchErrorLogs = (0, import_react4.useCallback)(async () => {
897
+ const fetchErrorLogs = (0, import_react7.useCallback)(async () => {
642
898
  if (!latest?.uid) return;
643
899
  setLoadingLogs(true);
644
900
  setLogError(null);
@@ -656,7 +912,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
656
912
  setLoadingLogs(false);
657
913
  }
658
914
  }, [latest?.uid, token, target.teamId]);
659
- const toggleErrorLogs = (0, import_react4.useCallback)(() => {
915
+ const toggleErrorLogs = (0, import_react7.useCallback)(() => {
660
916
  if (!showErrorLogs && errorLines.length === 0 && !logError) fetchErrorLogs();
661
917
  setShowErrorLogs((v) => !v);
662
918
  }, [showErrorLogs, errorLines.length, logError, fetchErrorLogs]);
@@ -664,26 +920,26 @@ function DeployItem({ target, token, onDelete, onEdit }) {
664
920
  const commitMsg = latest?.meta?.githubCommitMessage?.split("\n")[0];
665
921
  const sha = shortSha(latest?.meta?.githubCommitSha);
666
922
  const fullSha = latest?.meta?.githubCommitSha;
667
- const commitHref = githubCommitHref(latest?.meta);
923
+ const commitHref = safeHref(githubCommitHref(latest?.meta) ?? void 0);
668
924
  const creator = latest?.creator?.username;
669
925
  const deployedAt = latest?.created ? timeAgo(latest.created) : null;
670
926
  const vercelProjectUrl = projectHref(latest?.inspectorUrl);
671
927
  const isError = latest?.state === "ERROR";
672
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
673
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Card, { radius: 2, shadow: 1, tone: "default", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { align: "stretch", className: "dvfs-card-flex", children: [
674
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { direction: "column", flex: 1, style: { minWidth: 0 }, children: [
675
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Stack, { space: 3, padding: 3, style: { flex: 1 }, children: [
676
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { align: "center", justify: "space-between", gap: 2, children: [
677
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { align: "center", gap: 2, style: { minWidth: 0, flexWrap: "wrap" }, children: [
678
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 2, weight: "semibold", style: { flexShrink: 0 }, children: target.name }),
679
- branch && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Badge, { tone: "default", padding: 2, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { align: "center", gap: 1, children: [
928
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
929
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Card, { radius: 2, shadow: 1, tone: "default", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "stretch", className: "dvfs-card-flex", children: [
930
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { direction: "column", flex: 1, style: { minWidth: 0 }, children: [
931
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Stack, { space: 3, padding: 3, style: { flex: 1 }, children: [
932
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", justify: "space-between", gap: 2, children: [
933
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 2, style: { minWidth: 0, flexWrap: "wrap" }, children: [
934
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 2, weight: "semibold", style: { flexShrink: 0 }, children: target.name }),
935
+ branch && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Badge, { tone: "default", padding: 2, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 1, children: [
680
936
  branch,
681
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", style: { marginLeft: "-0.1em", opacity: 0.5 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M5 3.25a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0zm0 2.122a2.25 2.25 0 1 0-1.5 0v.878A2.25 2.25 0 0 0 5.75 8.5h1.5v2.128a2.251 2.251 0 1 0 1.5 0V8.5h1.5a2.25 2.25 0 0 0 2.25-2.25v-.878a2.25 2.25 0 1 0-1.5 0v.878a.75.75 0 0 1-.75.75h-4.5A.75.75 0 0 1 5 6.25v-.878zm3.75 7.378a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0zm3-8.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0z" }) })
937
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", style: { marginLeft: "-0.1em", opacity: 0.5 }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M5 3.25a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0zm0 2.122a2.25 2.25 0 1 0-1.5 0v.878A2.25 2.25 0 0 0 5.75 8.5h1.5v2.128a2.251 2.251 0 1 0 1.5 0V8.5h1.5a2.25 2.25 0 0 0 2.25-2.25v-.878a2.25 2.25 0 1 0-1.5 0v.878a.75.75 0 0 1-.75.75h-4.5A.75.75 0 0 1 5 6.25v-.878zm3.75 7.378a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0zm3-8.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0z" }) })
682
938
  ] }) }),
683
- token && !loadingInitial && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
684
- isPending ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StatusBadge, { state: "QUEUED" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StatusBadge, { state: latest?.state }),
685
- isActiveState(latest?.state) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
686
- import_ui5.Button,
939
+ token && !loadingInitial && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
940
+ isPending ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StatusBadge, { state: "QUEUED" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StatusBadge, { state: latest?.state }),
941
+ isActiveState(latest?.state) && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
942
+ Button,
687
943
  {
688
944
  text: "Cancel",
689
945
  mode: "ghost",
@@ -696,17 +952,18 @@ function DeployItem({ target, token, onDelete, onEdit }) {
696
952
  ),
697
953
  isPending ? (
698
954
  /* Optimistic — dimmed spinner only; no elapsed time until a real build exists */
699
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Spinner, { muted: true, style: { marginLeft: 4, opacity: 0.5 } })
700
- ) : isActive ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { align: "center", gap: 1, children: [
701
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Spinner, { muted: true, style: { marginLeft: 4 } }),
702
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 1, muted: true, children: formatDuration(elapsed) })
703
- ] }) : deployedAt ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 1, muted: true, children: deployedAt }) : null
955
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Spinner, { muted: true, style: { marginLeft: 4, opacity: 0.5 } })
956
+ ) : isActive ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 1, children: [
957
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Spinner, { muted: true, style: { marginLeft: 4 } }),
958
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, children: formatDuration(elapsed) })
959
+ ] }) : deployedAt ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, children: deployedAt }) : null
704
960
  ] })
705
961
  ] }),
706
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
962
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
707
963
  ActionMenu,
708
964
  {
709
965
  id: `menu-${target._id}`,
966
+ label: `Actions for ${target.name}`,
710
967
  buttonIcon: EllipsisVerticalIcon,
711
968
  items: [
712
969
  { text: "Edit target", icon: EditIcon, onClick: () => onEdit(target) },
@@ -718,43 +975,43 @@ function DeployItem({ target, token, onDelete, onEdit }) {
718
975
  }
719
976
  )
720
977
  ] }),
721
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("hr", { style: { border: "none", borderTop: "1px solid currentColor", opacity: 0.1, margin: 0 } }),
722
- !token ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 1, muted: true, children: "Connect a Vercel API token to see deployment status." }) : loadingInitial ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { align: "center", gap: 2, children: [
723
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Spinner, { muted: true }),
724
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 1, muted: true, children: "Loading\u2026" })
725
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Stack, { space: 2, children: [
726
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { align: "center", gap: 2, wrap: "wrap", children: [
727
- latest?.url && latest.state === "READY" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
978
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("hr", { style: { border: "none", borderTop: "1px solid currentColor", opacity: 0.1, margin: 0 } }),
979
+ !token ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, children: "Connect a Vercel API token to see deployment status." }) : loadingInitial ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 2, children: [
980
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Spinner, { muted: true }),
981
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, children: "Loading\u2026" })
982
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Stack, { space: 2, children: [
983
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 2, wrap: "wrap", children: [
984
+ latest?.url && latest.state === "READY" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
728
985
  "a",
729
986
  {
730
- href: `https://${latest.url}`,
987
+ href: deploymentHref(latest.url),
731
988
  target: "_blank",
732
989
  rel: "noreferrer",
733
990
  style: { color: "inherit" },
734
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Flex, { align: "center", gap: 1, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 1, children: latest.url }) })
991
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Flex, { align: "center", gap: 1, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, children: latest.url }) })
735
992
  }
736
993
  ) }),
737
- sha && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Tooltip, { text: commitMsg ?? sha, children: commitHref ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
994
+ sha && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Tooltip, { text: commitMsg ?? sha, children: commitHref ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
738
995
  "a",
739
996
  {
740
997
  href: commitHref,
741
998
  target: "_blank",
742
999
  rel: "noreferrer",
743
1000
  style: { color: "inherit", textDecoration: "none" },
744
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 1, muted: true, style: { cursor: "pointer", fontFamily: "monospace" }, children: sha })
1001
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, style: { cursor: "pointer", fontFamily: "monospace" }, children: sha })
745
1002
  }
746
- ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 1, muted: true, style: { cursor: "default", fontFamily: "monospace" }, children: sha }) }),
747
- creator && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Text, { size: 1, muted: true, children: [
1003
+ ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, style: { cursor: "default", fontFamily: "monospace" }, children: sha }) }),
1004
+ creator && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { size: 1, muted: true, children: [
748
1005
  "by ",
749
1006
  creator
750
1007
  ] }),
751
- latest?.state === "READY" && latest.ready && latest.created && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Text, { size: 1, muted: true, children: [
1008
+ latest?.state === "READY" && latest.ready && latest.created && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { size: 1, muted: true, children: [
752
1009
  "Took ",
753
1010
  formatDuration(Math.floor((latest.ready - latest.created) / 1e3)),
754
1011
  " to build"
755
1012
  ] }),
756
- latest?.url && latest.state === "READY" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Tooltip, { text: copied ? "Copied!" : "Copy URL", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
757
- import_ui5.Button,
1013
+ latest?.url && latest.state === "READY" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Tooltip, { text: copied ? "Copied!" : "Copy URL", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1014
+ Button,
758
1015
  {
759
1016
  mode: "ghost",
760
1017
  icon: copied ? CheckmarkIcon : CopyIcon,
@@ -765,9 +1022,9 @@ function DeployItem({ target, token, onDelete, onEdit }) {
765
1022
  }
766
1023
  ) }) })
767
1024
  ] }),
768
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { align: "center", gap: 2, wrap: "wrap", children: [
769
- commitMsg && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
770
- import_ui5.Text,
1025
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 2, wrap: "wrap", children: [
1026
+ commitMsg && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1027
+ Text,
771
1028
  {
772
1029
  size: 0,
773
1030
  muted: true,
@@ -775,10 +1032,11 @@ function DeployItem({ target, token, onDelete, onEdit }) {
775
1032
  children: commitMsg
776
1033
  }
777
1034
  ),
778
- isError && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Stack, { space: 2, children: [
779
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
780
- import_ui5.Button,
1035
+ isError && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Stack, { space: 2, children: [
1036
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1037
+ Button,
781
1038
  {
1039
+ "aria-expanded": showErrorLogs,
782
1040
  text: showErrorLogs ? "Hide error details" : "Show error details",
783
1041
  mode: "ghost",
784
1042
  tone: "critical",
@@ -789,37 +1047,45 @@ function DeployItem({ target, token, onDelete, onEdit }) {
789
1047
  style: { alignSelf: "flex-start", cursor: "pointer" }
790
1048
  }
791
1049
  ),
792
- showErrorLogs && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Card, { tone: "critical", radius: 2, padding: 3, children: [
793
- loadingLogs && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { align: "center", gap: 2, children: [
794
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Spinner, { muted: true }),
795
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 1, muted: true, children: "Loading logs\u2026" })
1050
+ showErrorLogs && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Card, { tone: "critical", radius: 2, padding: 3, children: [
1051
+ loadingLogs && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 2, children: [
1052
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Spinner, { muted: true }),
1053
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, muted: true, children: "Loading logs\u2026" })
796
1054
  ] }),
797
- logError && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Stack, { space: 2, children: [
798
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 1, children: logError }),
799
- safeHref(latest?.inspectorUrl) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { align: "center", gap: 1, children: [
800
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LaunchIcon, {}),
801
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 1, children: "View full logs in Vercel" })
1055
+ logError && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Stack, { space: 2, children: [
1056
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, children: logError }),
1057
+ safeHref(latest?.inspectorUrl) && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 1, children: [
1058
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LaunchIcon, {}),
1059
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, children: "View full logs in Vercel" })
802
1060
  ] }) })
803
1061
  ] }),
804
- !loadingLogs && !logError && errorLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Stack, { space: 2, children: [
805
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Box, { style: { maxHeight: 240, overflowY: "auto", fontFamily: "monospace", fontSize: 13, lineHeight: 1.6 }, children: errorLines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Code, { style: { display: "block", whiteSpace: "pre-wrap", wordBreak: "break-all" }, children: line }, i)) }),
806
- safeHref(latest?.inspectorUrl) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { align: "center", gap: 1, children: [
807
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LaunchIcon, {}),
808
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 1, children: "View full logs in Vercel" })
1062
+ !loadingLogs && !logError && errorLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Stack, { space: 2, children: [
1063
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box, { style: { maxHeight: 240, overflowY: "auto", fontFamily: "monospace", fontSize: 13, lineHeight: 1.6 }, children: errorLines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Code, { style: { display: "block", whiteSpace: "pre-wrap", wordBreak: "break-all" }, children: line }, i)) }),
1064
+ safeHref(latest?.inspectorUrl) && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 1, children: [
1065
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LaunchIcon, {}),
1066
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, children: "View full logs in Vercel" })
809
1067
  ] }) })
810
1068
  ] })
811
1069
  ] })
812
1070
  ] }),
813
- deployError && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Card, { tone: "critical", padding: 2, radius: 2, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 1, children: deployError }) })
1071
+ deployError && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Card, { tone: "critical", padding: 2, radius: 2, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 1, children: deployError }) })
814
1072
  ] })
815
- ] })
1073
+ ] }),
1074
+ pollError && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Card, { tone: "caution", padding: 3, radius: 2, role: "status", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 2, children: [
1075
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(WarningOutlineIcon, { "aria-hidden": "true" }),
1076
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { size: 1, children: [
1077
+ "Status updates paused \u2014 ",
1078
+ pollError
1079
+ ] })
1080
+ ] }) })
816
1081
  ] }),
817
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Box, { children: [
818
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
819
- import_ui5.Button,
1082
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box, { children: [
1083
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1084
+ Button,
820
1085
  {
821
1086
  mode: "ghost",
822
1087
  iconRight: showDetails ? ChevronUpIcon : ChevronDownIcon,
1088
+ "aria-expanded": showDetails,
823
1089
  text: "Details",
824
1090
  fontSize: 0,
825
1091
  padding: 3,
@@ -827,62 +1093,62 @@ function DeployItem({ target, token, onDelete, onEdit }) {
827
1093
  style: { width: "100%", justifyContent: "flex-start", borderRadius: 0, cursor: "pointer" }
828
1094
  }
829
1095
  ),
830
- showDetails && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Card, { tone: "primary", padding: 3, className: "dvfs-accordion-content", style: { borderRadius: 0, borderTop: "1px solid rgba(128,128,128,0.15)" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Stack, { space: 2, children: [
831
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { gap: 2, align: "center", children: [
832
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Project" }),
833
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: projectId || "\u2014" })
1096
+ showDetails && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Card, { tone: "primary", padding: 3, className: "dvfs-accordion-content", style: { borderRadius: 0, borderTop: "1px solid rgba(128,128,128,0.15)" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Stack, { space: 2, children: [
1097
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "center", children: [
1098
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Project" }),
1099
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: projectId || "\u2014" })
834
1100
  ] }),
835
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { gap: 2, align: "center", children: [
836
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Hook" }),
837
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: hookId || "\u2014" })
1101
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "center", children: [
1102
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Hook" }),
1103
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: hookId || "\u2014" })
838
1104
  ] }),
839
- target.teamId && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { gap: 2, align: "center", children: [
840
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Team" }),
841
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: target.teamId })
1105
+ target.teamId && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "center", children: [
1106
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Team" }),
1107
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: target.teamId })
842
1108
  ] }),
843
- fullSha && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { gap: 2, align: "flex-start", children: [
844
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Commit" }),
845
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, style: { fontFamily: "monospace", wordBreak: "break-all" }, children: fullSha })
1109
+ fullSha && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "flex-start", children: [
1110
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Commit" }),
1111
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace", wordBreak: "break-all" }, children: fullSha })
846
1112
  ] }),
847
- latest?.meta?.githubCommitAuthorName && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { gap: 2, align: "center", children: [
848
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Author" }),
849
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, children: latest.meta.githubCommitAuthorName })
1113
+ latest?.meta?.githubCommitAuthorName && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "center", children: [
1114
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Author" }),
1115
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, children: latest.meta.githubCommitAuthorName })
850
1116
  ] }),
851
- branch && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { gap: 2, align: "center", children: [
852
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Branch" }),
853
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: branch })
1117
+ branch && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "center", children: [
1118
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Branch" }),
1119
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: branch })
854
1120
  ] }),
855
- latest?.uid && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { gap: 2, align: "center", children: [
856
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Deploy ID" }),
857
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: latest.uid })
1121
+ latest?.uid && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "center", children: [
1122
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Deploy ID" }),
1123
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: latest.uid })
858
1124
  ] }),
859
- latest?.url && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { gap: 2, align: "flex-start", children: [
860
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "URL" }),
861
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, style: { fontFamily: "monospace", wordBreak: "break-all" }, children: latest.url })
1125
+ latest?.url && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "flex-start", children: [
1126
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "URL" }),
1127
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace", wordBreak: "break-all" }, children: latest.url })
862
1128
  ] }),
863
- safeHref(latest?.inspectorUrl) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { gap: 2, align: "center", children: [
864
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Inspector" }),
865
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { align: "center", gap: 1, children: [
866
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: "Open in Vercel" }),
867
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LaunchIcon, { style: { width: 10, height: 10 } })
1129
+ safeHref(latest?.inspectorUrl) && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "center", children: [
1130
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Inspector" }),
1131
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { align: "center", gap: 1, children: [
1132
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: "Open in Vercel" }),
1133
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LaunchIcon, { style: { width: 10, height: 10 } })
868
1134
  ] }) })
869
1135
  ] }),
870
- latest?.created && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Flex, { gap: 2, align: "center", children: [
871
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Created" }),
872
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Text, { size: 0, muted: true, children: new Date(latest.created).toLocaleString() })
1136
+ latest?.created && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Flex, { gap: 2, align: "center", children: [
1137
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Created" }),
1138
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { size: 0, muted: true, children: new Date(latest.created).toLocaleString() })
873
1139
  ] })
874
1140
  ] }) })
875
1141
  ] })
876
1142
  ] }),
877
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
878
- import_ui5.Flex,
1143
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1144
+ Flex,
879
1145
  {
880
1146
  direction: "column",
881
1147
  gap: 2,
882
1148
  className: "dvfs-deploy-col",
883
1149
  style: { flexShrink: 0, alignSelf: "stretch" },
884
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
885
- import_ui5.Button,
1150
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1151
+ Button,
886
1152
  {
887
1153
  text: "Deploy",
888
1154
  tone: "primary",
@@ -901,7 +1167,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
901
1167
  }
902
1168
  )
903
1169
  ] }) }),
904
- showHistory && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1170
+ showHistory && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
905
1171
  DeployHistory,
906
1172
  {
907
1173
  target,
@@ -913,17 +1179,17 @@ function DeployItem({ target, token, onDelete, onEdit }) {
913
1179
  }
914
1180
 
915
1181
  // src/components/TokenSetup.tsx
916
- var import_react5 = require("react");
1182
+ var import_react8 = require("react");
917
1183
  var import_sanity = require("sanity");
918
- var import_ui7 = require("@sanity/ui");
919
- var import_jsx_runtime6 = require("react/jsx-runtime");
1184
+ var import_jsx_runtime10 = require("react/jsx-runtime");
920
1185
  var TOKEN_DOC_ID = "config.vercelDeploy";
921
1186
  function TokenSetup({ onSaved, onCancel }) {
1187
+ const tokenId = (0, import_react8.useId)();
922
1188
  const client = (0, import_sanity.useClient)({ apiVersion: "2025-01-01" });
923
- const [token, setToken] = (0, import_react5.useState)("");
924
- const [saving, setSaving] = (0, import_react5.useState)(false);
925
- const [error, setError] = (0, import_react5.useState)(null);
926
- const save = (0, import_react5.useCallback)(async () => {
1189
+ const [token, setToken] = (0, import_react8.useState)("");
1190
+ const [saving, setSaving] = (0, import_react8.useState)(false);
1191
+ const [error, setError] = (0, import_react8.useState)(null);
1192
+ const save = (0, import_react8.useCallback)(async () => {
927
1193
  if (!token.trim()) return;
928
1194
  setSaving(true);
929
1195
  setError(null);
@@ -940,17 +1206,17 @@ function TokenSetup({ onSaved, onCancel }) {
940
1206
  setSaving(false);
941
1207
  }
942
1208
  }, [client, token, onSaved]);
943
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
944
- import_ui7.Dialog,
1209
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1210
+ Dialog,
945
1211
  {
946
1212
  header: "Connect Vercel API token",
947
1213
  id: "token-setup",
948
1214
  onClose: onCancel,
949
1215
  width: 1,
950
- footer: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ui7.Flex, { padding: 3, gap: 2, justify: "flex-end", children: [
951
- onCancel && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ui7.Button, { text: "Cancel", mode: "ghost", onClick: onCancel, style: { cursor: "pointer" } }),
952
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
953
- import_ui7.Button,
1216
+ footer: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Flex, { padding: 3, gap: 2, justify: "flex-end", children: [
1217
+ onCancel && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Button, { text: "Cancel", mode: "ghost", onClick: onCancel, style: { cursor: "pointer" } }),
1218
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1219
+ Button,
954
1220
  {
955
1221
  text: "Save and connect",
956
1222
  tone: "primary",
@@ -962,23 +1228,24 @@ function TokenSetup({ onSaved, onCancel }) {
962
1228
  }
963
1229
  )
964
1230
  ] }),
965
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Stack, { space: 4, padding: 4, children: [
966
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Stack, { space: 3, children: [
967
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ui7.Text, { size: 1, muted: true, children: "A Vercel API token lets this tool read deployment status, history, build logs, and branch metadata. Without it you can still trigger deploys \u2014 you just won't see any feedback." }),
968
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ui7.Text, { size: 1, muted: true, children: [
1231
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Stack, { space: 4, padding: 4, children: [
1232
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Stack, { space: 3, children: [
1233
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 1, muted: true, children: "A Vercel API token lets this tool read deployment status, history, build logs, and branch metadata. Without it you can still trigger deploys \u2014 you just won't see any feedback." }),
1234
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { size: 1, muted: true, children: [
969
1235
  "Create one at ",
970
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: "vercel.com \u2192 Settings \u2192 Tokens" }),
1236
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: "vercel.com \u2192 Settings \u2192 Tokens" }),
971
1237
  " with",
972
1238
  " ",
973
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: "Full Account" }),
1239
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: "Full Account" }),
974
1240
  " scope. The token is stored in your Sanity dataset and shared across all authenticated studio users."
975
1241
  ] })
976
1242
  ] }),
977
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Stack, { space: 2, children: [
978
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ui7.Text, { size: 1, weight: "semibold", children: "Vercel API Token" }),
979
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
980
- import_ui7.TextInput,
1243
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Stack, { space: 2, children: [
1244
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Label, { as: "label", size: 1, htmlFor: tokenId, children: "Vercel API Token" }),
1245
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1246
+ TextInput,
981
1247
  {
1248
+ id: tokenId,
982
1249
  value: token,
983
1250
  onChange: (e) => setToken(e.target.value),
984
1251
  placeholder: "xxxxxxxxxxxxxxxxxxxxxxxx",
@@ -986,30 +1253,36 @@ function TokenSetup({ onSaved, onCancel }) {
986
1253
  }
987
1254
  )
988
1255
  ] }),
989
- error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ui7.Card, { tone: "critical", padding: 3, radius: 2, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ui7.Text, { size: 1, children: error }) })
1256
+ error && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Card, { tone: "critical", padding: 3, radius: 2, role: "alert", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Text, { size: 1, children: error }) })
990
1257
  ] })
991
1258
  }
992
1259
  );
993
1260
  }
994
1261
 
995
1262
  // src/components/DeployTargetForm.tsx
996
- var import_react6 = require("react");
1263
+ var import_react9 = require("react");
997
1264
  var import_sanity2 = require("sanity");
998
- var import_ui9 = require("@sanity/ui");
999
- var import_jsx_runtime7 = require("react/jsx-runtime");
1265
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1000
1266
  var VERCEL_HOOK_RE2 = /^https:\/\/api\.vercel\.com\/v1\/integrations\/deploy\//;
1001
1267
  function DeployTargetForm({ initial, onSaved, onClose }) {
1002
1268
  const client = (0, import_sanity2.useClient)({ apiVersion: "2025-01-01" });
1003
1269
  const isEdit = Boolean(initial);
1004
- const [name, setName] = (0, import_react6.useState)(initial?.name ?? "");
1005
- const [url, setUrl] = (0, import_react6.useState)(initial?.url ?? "");
1006
- const [teamId, setTeamId] = (0, import_react6.useState)(initial?.teamId ?? "");
1007
- const [disableDelete, setDisableDelete] = (0, import_react6.useState)(initial?.disableDeleteAction ?? false);
1008
- const [saving, setSaving] = (0, import_react6.useState)(false);
1009
- const [error, setError] = (0, import_react6.useState)(null);
1270
+ const nameId = (0, import_react9.useId)();
1271
+ const urlId = (0, import_react9.useId)();
1272
+ const urlErrorId = (0, import_react9.useId)();
1273
+ const urlHelpId = (0, import_react9.useId)();
1274
+ const teamId_ = (0, import_react9.useId)();
1275
+ const teamHelpId = (0, import_react9.useId)();
1276
+ const disableId = (0, import_react9.useId)();
1277
+ const [name, setName] = (0, import_react9.useState)(initial?.name ?? "");
1278
+ const [url, setUrl] = (0, import_react9.useState)(initial?.url ?? "");
1279
+ const [teamId, setTeamId] = (0, import_react9.useState)(initial?.teamId ?? "");
1280
+ const [disableDelete, setDisableDelete] = (0, import_react9.useState)(initial?.disableDeleteAction ?? false);
1281
+ const [saving, setSaving] = (0, import_react9.useState)(false);
1282
+ const [error, setError] = (0, import_react9.useState)(null);
1010
1283
  const urlValid = !url || VERCEL_HOOK_RE2.test(url.trim());
1011
1284
  const canSave = name.trim() && url.trim() && urlValid;
1012
- const save = (0, import_react6.useCallback)(async () => {
1285
+ const save = (0, import_react9.useCallback)(async () => {
1013
1286
  if (!canSave) return;
1014
1287
  setSaving(true);
1015
1288
  setError(null);
@@ -1032,17 +1305,17 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1032
1305
  setSaving(false);
1033
1306
  }
1034
1307
  }, [canSave, isEdit, initial, client, name, url, teamId, disableDelete, onSaved]);
1035
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1036
- import_ui9.Dialog,
1308
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1309
+ Dialog,
1037
1310
  {
1038
1311
  header: isEdit ? `Edit "${initial?.name}"` : "Add deploy target",
1039
1312
  id: "deploy-target-form",
1040
1313
  onClose,
1041
1314
  width: 1,
1042
- footer: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ui9.Flex, { padding: 3, gap: 2, justify: "flex-end", children: [
1043
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui9.Button, { text: "Cancel", mode: "ghost", onClick: onClose, style: { cursor: "pointer" } }),
1044
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1045
- import_ui9.Button,
1315
+ footer: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Flex, { padding: 3, gap: 2, justify: "flex-end", children: [
1316
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Button, { text: "Cancel", mode: "ghost", onClick: onClose, style: { cursor: "pointer" } }),
1317
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1318
+ Button,
1046
1319
  {
1047
1320
  text: isEdit ? "Save changes" : "Add target",
1048
1321
  tone: "primary",
@@ -1054,95 +1327,105 @@ function DeployTargetForm({ initial, onSaved, onClose }) {
1054
1327
  }
1055
1328
  )
1056
1329
  ] }),
1057
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui9.Box, { padding: 4, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Stack, { space: 4, children: [
1058
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Stack, { space: 2, children: [
1059
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ui9.Label, { size: 1, children: [
1330
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box, { padding: 4, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Stack, { space: 4, children: [
1331
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Stack, { space: 2, children: [
1332
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Label, { as: "label", size: 1, htmlFor: nameId, children: [
1060
1333
  "Name ",
1061
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { color: "var(--card-fg-color)" }, children: "*" })
1334
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { "aria-hidden": "true", children: "*" })
1062
1335
  ] }),
1063
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1064
- import_ui9.TextInput,
1336
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1337
+ TextInput,
1065
1338
  {
1339
+ id: nameId,
1340
+ required: true,
1341
+ "aria-required": "true",
1066
1342
  value: name,
1067
1343
  onChange: (e) => setName(e.target.value),
1068
1344
  placeholder: "Production"
1069
1345
  }
1070
1346
  )
1071
1347
  ] }),
1072
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Stack, { space: 2, children: [
1073
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ui9.Label, { size: 1, children: [
1348
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Stack, { space: 2, children: [
1349
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Label, { as: "label", size: 1, htmlFor: urlId, children: [
1074
1350
  "Deploy hook URL ",
1075
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { color: "var(--card-fg-color)" }, children: "*" })
1351
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { "aria-hidden": "true", children: "*" })
1076
1352
  ] }),
1077
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1078
- import_ui9.TextInput,
1353
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1354
+ TextInput,
1079
1355
  {
1356
+ id: urlId,
1357
+ required: true,
1358
+ "aria-required": "true",
1359
+ "aria-invalid": Boolean(url) && !urlValid,
1360
+ "aria-describedby": `${urlHelpId}${url && !urlValid ? ` ${urlErrorId}` : ""}`,
1080
1361
  value: url,
1081
1362
  onChange: (e) => setUrl(e.target.value),
1082
1363
  placeholder: "https://api.vercel.com/v1/integrations/deploy/\u2026"
1083
1364
  }
1084
1365
  ),
1085
- url && !urlValid && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui9.Text, { size: 1, style: { color: "var(--card-critical-fg-color, red)" }, children: "Must be a Vercel deploy hook URL (api.vercel.com/v1/integrations/deploy/\u2026)" }),
1086
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui9.Text, { size: 0, muted: true, children: "Vercel dashboard \u2192 Project \u2192 Settings \u2192 Git \u2192 Deploy Hooks" })
1366
+ url && !urlValid && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Card, { tone: "critical", padding: 2, radius: 2, role: "alert", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { id: urlErrorId, size: 1, children: "Must be a Vercel deploy hook URL (api.vercel.com/v1/integrations/deploy/\u2026)" }) }),
1367
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { id: urlHelpId, size: 0, muted: true, children: "Vercel dashboard \u2192 Project \u2192 Settings \u2192 Git \u2192 Deploy Hooks" })
1087
1368
  ] }),
1088
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Stack, { space: 2, children: [
1089
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ui9.Label, { size: 1, children: [
1369
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Stack, { space: 2, children: [
1370
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Label, { as: "label", size: 1, htmlFor: teamId_, children: [
1090
1371
  "Team ID ",
1091
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { opacity: 0.5, fontWeight: "normal" }, children: "\u2014 optional" })
1372
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { opacity: 0.5, fontWeight: "normal" }, children: "\u2014 optional" })
1092
1373
  ] }),
1093
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1094
- import_ui9.TextInput,
1374
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1375
+ TextInput,
1095
1376
  {
1377
+ id: teamId_,
1378
+ "aria-describedby": teamHelpId,
1096
1379
  value: teamId,
1097
1380
  onChange: (e) => setTeamId(e.target.value),
1098
1381
  placeholder: "team_xxxxxxxx"
1099
1382
  }
1100
1383
  ),
1101
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ui9.Text, { size: 0, muted: true, children: [
1384
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { id: teamHelpId, size: 0, muted: true, children: [
1102
1385
  "Required for team-owned Vercel projects. Find it at Vercel \u2192 Settings \u2192 General \u2192 Team ID (starts with ",
1103
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { children: "team_" }),
1386
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("code", { children: "team_" }),
1104
1387
  ")."
1105
1388
  ] })
1106
1389
  ] }),
1107
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ui9.Flex, { align: "center", gap: 3, children: [
1108
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1109
- import_ui9.Switch,
1390
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Flex, { align: "center", gap: 3, children: [
1391
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1392
+ Switch,
1110
1393
  {
1111
1394
  checked: disableDelete,
1112
1395
  onChange: (e) => setDisableDelete(e.target.checked),
1113
- id: "disable-delete"
1396
+ id: disableId
1114
1397
  }
1115
1398
  ),
1116
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Stack, { space: 1, children: [
1117
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui9.Label, { as: "label", size: 1, htmlFor: "disable-delete", children: "Disable delete action" }),
1118
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui9.Text, { size: 0, muted: true, children: "Hides the delete button for this target in the studio." })
1399
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Stack, { space: 1, children: [
1400
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Label, { as: "label", size: 1, htmlFor: disableId, children: "Disable delete action" }),
1401
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { size: 0, muted: true, children: "Hides the delete button for this target in the studio." })
1119
1402
  ] })
1120
1403
  ] }),
1121
- error && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui9.Card, { tone: "critical", padding: 3, radius: 2, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui9.Text, { size: 1, children: error }) })
1404
+ error && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Card, { tone: "critical", padding: 3, radius: 2, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { size: 1, children: error }) })
1122
1405
  ] }) })
1123
1406
  }
1124
1407
  );
1125
1408
  }
1126
1409
 
1127
1410
  // src/version.ts
1128
- var VERSION = "1.1.0";
1411
+ var VERSION = "1.2.0";
1129
1412
 
1130
1413
  // src/components/DeployTool.tsx
1131
- var import_jsx_runtime8 = require("react/jsx-runtime");
1414
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1132
1415
  var TOKEN_QUERY = `*[_id == "config.vercelDeploy"][0].accessToken`;
1133
- var TARGETS_QUERY = `*[_type == "vercel_deploy"] | order(_createdAt asc)`;
1416
+ var TARGETS_QUERY = `*[_type == "vercel_deploy" && !(_id in path("drafts.**"))] | order(_createdAt asc)`;
1134
1417
  function DeployTool() {
1135
1418
  const client = (0, import_sanity3.useClient)({ apiVersion: "2025-01-01" });
1136
1419
  const toast = useToast();
1137
- const [token, setToken] = (0, import_react7.useState)(null);
1138
- const [targets, setTargets] = (0, import_react7.useState)([]);
1139
- const [loading, setLoading] = (0, import_react7.useState)(true);
1140
- const [showTokenSetup, setShowTokenSetup] = (0, import_react7.useState)(false);
1141
- const [showCreateForm, setShowCreateForm] = (0, import_react7.useState)(false);
1142
- const [pendingEdit, setPendingEdit] = (0, import_react7.useState)(null);
1143
- const [pendingDelete, setPendingDelete] = (0, import_react7.useState)(null);
1144
- const [deleting, setDeleting] = (0, import_react7.useState)(false);
1145
- const load = (0, import_react7.useCallback)(async () => {
1420
+ const [token, setToken] = (0, import_react10.useState)(null);
1421
+ const [targets, setTargets] = (0, import_react10.useState)([]);
1422
+ const [loading, setLoading] = (0, import_react10.useState)(true);
1423
+ const [showTokenSetup, setShowTokenSetup] = (0, import_react10.useState)(false);
1424
+ const [showCreateForm, setShowCreateForm] = (0, import_react10.useState)(false);
1425
+ const [pendingEdit, setPendingEdit] = (0, import_react10.useState)(null);
1426
+ const [pendingDelete, setPendingDelete] = (0, import_react10.useState)(null);
1427
+ const [deleting, setDeleting] = (0, import_react10.useState)(false);
1428
+ const load = (0, import_react10.useCallback)(async () => {
1146
1429
  setLoading(true);
1147
1430
  try {
1148
1431
  const [fetchedToken, fetchedTargets] = await Promise.all([
@@ -1157,11 +1440,20 @@ function DeployTool() {
1157
1440
  setLoading(false);
1158
1441
  }
1159
1442
  }, [client]);
1160
- (0, import_react7.useEffect)(() => {
1443
+ (0, import_react10.useEffect)(() => {
1161
1444
  load();
1162
1445
  }, [load]);
1163
- (0, import_react7.useEffect)(() => {
1164
- if (document.getElementById("dvfs-styles")) return;
1446
+ (0, import_react10.useEffect)(() => {
1447
+ const existing = document.getElementById("dvfs-styles");
1448
+ if (existing) {
1449
+ const n = Number(existing.dataset.refs ?? "1") + 1;
1450
+ existing.dataset.refs = String(n);
1451
+ return () => {
1452
+ const left = Number(existing.dataset.refs ?? "1") - 1;
1453
+ existing.dataset.refs = String(left);
1454
+ if (left <= 0) existing.remove();
1455
+ };
1456
+ }
1165
1457
  const style = document.createElement("style");
1166
1458
  style.id = "dvfs-styles";
1167
1459
  style.textContent = `
@@ -1182,11 +1474,14 @@ function DeployTool() {
1182
1474
  }
1183
1475
  `;
1184
1476
  document.head.appendChild(style);
1477
+ style.dataset.refs = "1";
1185
1478
  return () => {
1186
- document.getElementById("dvfs-styles")?.remove();
1479
+ const left = Number(style.dataset.refs ?? "1") - 1;
1480
+ style.dataset.refs = String(left);
1481
+ if (left <= 0) style.remove();
1187
1482
  };
1188
1483
  }, []);
1189
- (0, import_react7.useEffect)(() => {
1484
+ (0, import_react10.useEffect)(() => {
1190
1485
  const sub = client.listen(TARGETS_QUERY).subscribe(() => {
1191
1486
  client.fetch(TARGETS_QUERY).then(setTargets).catch((err) => {
1192
1487
  console.error("deploy-vercel-from-sanity: subscription refresh error", err);
@@ -1194,11 +1489,13 @@ function DeployTool() {
1194
1489
  });
1195
1490
  return () => sub.unsubscribe();
1196
1491
  }, [client]);
1197
- const confirmDelete = (0, import_react7.useCallback)(async () => {
1492
+ const confirmDelete = (0, import_react10.useCallback)(async () => {
1198
1493
  if (!pendingDelete) return;
1199
1494
  setDeleting(true);
1200
1495
  try {
1201
1496
  await client.delete(pendingDelete._id);
1497
+ await client.delete(`drafts.${pendingDelete._id}`).catch(() => {
1498
+ });
1202
1499
  setTargets((prev) => prev.filter((t) => t._id !== pendingDelete._id));
1203
1500
  toast.push({ status: "success", title: `Deleted "${pendingDelete.name}"` });
1204
1501
  } catch (err) {
@@ -1209,15 +1506,15 @@ function DeployTool() {
1209
1506
  }
1210
1507
  }, [client, pendingDelete, toast]);
1211
1508
  if (loading) {
1212
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ui11.Card, { height: "fill", tone: "transparent", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ui11.Flex, { align: "center", justify: "center", height: "fill", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ui11.Spinner, { muted: true }) }) });
1509
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Card, { height: "fill", tone: "transparent", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Flex, { align: "center", justify: "center", height: "fill", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Spinner, { muted: true }) }) });
1213
1510
  }
1214
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ui11.Card, { height: "fill", tone: "transparent", children: [
1215
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ui11.Box, { padding: 5, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Stack, { space: 5, children: [
1216
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ui11.Flex, { align: "center", justify: "space-between", className: "dvfs-header", children: [
1217
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ui11.Heading, { size: 2, children: "Deploy with Vercel" }),
1218
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ui11.Flex, { align: "center", gap: 3, className: "dvfs-header-actions", children: [
1219
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1220
- import_ui11.Button,
1511
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Card, { height: "fill", tone: "transparent", children: [
1512
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box, { padding: 5, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Stack, { space: 5, children: [
1513
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Flex, { align: "center", justify: "space-between", className: "dvfs-header", children: [
1514
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Heading, { size: 2, children: "Deploy with Vercel" }),
1515
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Flex, { align: "center", gap: 3, className: "dvfs-header-actions", children: [
1516
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1517
+ Button,
1221
1518
  {
1222
1519
  text: token ? "Token connected" : "Connect API token",
1223
1520
  mode: "ghost",
@@ -1228,8 +1525,8 @@ function DeployTool() {
1228
1525
  style: { cursor: "pointer" }
1229
1526
  }
1230
1527
  ),
1231
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1232
- import_ui11.Button,
1528
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1529
+ Button,
1233
1530
  {
1234
1531
  text: "Add target",
1235
1532
  mode: "ghost",
@@ -1241,13 +1538,13 @@ function DeployTool() {
1241
1538
  )
1242
1539
  ] })
1243
1540
  ] }),
1244
- !token && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ui11.Card, { padding: 4, radius: 2, tone: "caution", shadow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ui11.Flex, { align: "center", justify: "space-between", gap: 4, children: [
1245
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Stack, { space: 2, children: [
1246
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ui11.Text, { size: 1, weight: "semibold", children: "Deploy status is not connected" }),
1247
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ui11.Text, { size: 1, muted: true, children: "You can trigger deploys now. Connect a Vercel API token to also see deployment status, build logs, history, and commit metadata." })
1541
+ !token && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Card, { padding: 4, radius: 2, tone: "caution", shadow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Flex, { align: "center", justify: "space-between", gap: 4, children: [
1542
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Stack, { space: 2, children: [
1543
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { size: 1, weight: "semibold", children: "Deploy status is not connected" }),
1544
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { size: 1, muted: true, children: "You can trigger deploys now. Connect a Vercel API token to also see deployment status, build logs, history, and commit metadata." })
1248
1545
  ] }),
1249
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1250
- import_ui11.Button,
1546
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1547
+ Button,
1251
1548
  {
1252
1549
  text: "Connect",
1253
1550
  tone: "caution",
@@ -1257,16 +1554,16 @@ function DeployTool() {
1257
1554
  }
1258
1555
  )
1259
1556
  ] }) }),
1260
- targets.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ui11.Card, { padding: 5, radius: 2, tone: "transparent", shadow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Stack, { space: 4, style: { textAlign: "center" }, children: [
1261
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ui11.Text, { size: 2, weight: "semibold", children: "No deploy targets configured" }),
1262
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ui11.Text, { size: 1, muted: true, children: [
1557
+ targets.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Card, { padding: 5, radius: 2, tone: "transparent", shadow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Stack, { space: 4, style: { textAlign: "center" }, children: [
1558
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { size: 2, weight: "semibold", children: "No deploy targets configured" }),
1559
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { size: 1, muted: true, children: [
1263
1560
  "Add a deploy target using the button above, or create a",
1264
1561
  " ",
1265
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("code", { children: "vercel_deploy" }),
1562
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("code", { children: "vercel_deploy" }),
1266
1563
  " document directly in the dataset."
1267
1564
  ] }),
1268
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ui11.Flex, { justify: "center", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1269
- import_ui11.Button,
1565
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Flex, { justify: "center", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1566
+ Button,
1270
1567
  {
1271
1568
  text: "Add deploy target",
1272
1569
  tone: "primary",
@@ -1276,12 +1573,12 @@ function DeployTool() {
1276
1573
  }
1277
1574
  ) })
1278
1575
  ] }) }),
1279
- targets.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dvfs-grid", style: {
1576
+ targets.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dvfs-grid", style: {
1280
1577
  display: "grid",
1281
1578
  gridTemplateColumns: "repeat(auto-fill, minmax(min(540px, 100%), 1fr))",
1282
1579
  gap: "16px",
1283
1580
  alignItems: "start"
1284
- }, children: targets.map((target) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1581
+ }, children: targets.map((target) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1285
1582
  DeployItem,
1286
1583
  {
1287
1584
  target,
@@ -1292,8 +1589,8 @@ function DeployTool() {
1292
1589
  target._id
1293
1590
  )) })
1294
1591
  ] }) }),
1295
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1296
- import_ui11.Box,
1592
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1593
+ Box,
1297
1594
  {
1298
1595
  style: {
1299
1596
  position: "fixed",
@@ -1303,23 +1600,23 @@ function DeployTool() {
1303
1600
  pointerEvents: "none",
1304
1601
  userSelect: "none"
1305
1602
  },
1306
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ui11.Text, { size: 0, muted: true, children: [
1603
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { size: 0, muted: true, children: [
1307
1604
  "v",
1308
1605
  VERSION
1309
1606
  ] })
1310
1607
  }
1311
1608
  ),
1312
- showTokenSetup && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1609
+ showTokenSetup && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1313
1610
  TokenSetup,
1314
1611
  {
1315
1612
  onSaved: () => {
1316
1613
  setShowTokenSetup(false);
1317
1614
  load();
1318
1615
  },
1319
- onCancel: token ? () => setShowTokenSetup(false) : void 0
1616
+ onCancel: () => setShowTokenSetup(false)
1320
1617
  }
1321
1618
  ),
1322
- showCreateForm && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1619
+ showCreateForm && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1323
1620
  DeployTargetForm,
1324
1621
  {
1325
1622
  onSaved: () => {
@@ -1329,7 +1626,7 @@ function DeployTool() {
1329
1626
  onClose: () => setShowCreateForm(false)
1330
1627
  }
1331
1628
  ),
1332
- pendingEdit && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1629
+ pendingEdit && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1333
1630
  DeployTargetForm,
1334
1631
  {
1335
1632
  initial: pendingEdit,
@@ -1340,16 +1637,16 @@ function DeployTool() {
1340
1637
  onClose: () => setPendingEdit(null)
1341
1638
  }
1342
1639
  ),
1343
- pendingDelete && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1344
- import_ui11.Dialog,
1640
+ pendingDelete && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1641
+ Dialog,
1345
1642
  {
1346
1643
  header: "Delete deploy target?",
1347
1644
  id: "confirm-delete",
1348
1645
  onClose: () => setPendingDelete(null),
1349
1646
  width: 1,
1350
- footer: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ui11.Flex, { padding: 3, gap: 2, justify: "flex-end", children: [
1351
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1352
- import_ui11.Button,
1647
+ footer: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Flex, { padding: 3, gap: 2, justify: "flex-end", children: [
1648
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1649
+ Button,
1353
1650
  {
1354
1651
  text: "Cancel",
1355
1652
  mode: "ghost",
@@ -1357,8 +1654,8 @@ function DeployTool() {
1357
1654
  style: { cursor: "pointer" }
1358
1655
  }
1359
1656
  ),
1360
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1361
- import_ui11.Button,
1657
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1658
+ Button,
1362
1659
  {
1363
1660
  text: "Delete",
1364
1661
  tone: "critical",
@@ -1370,26 +1667,59 @@ function DeployTool() {
1370
1667
  }
1371
1668
  )
1372
1669
  ] }),
1373
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ui11.Box, { padding: 4, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Stack, { space: 3, children: [
1374
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ui11.Flex, { align: "center", gap: 2, children: [
1375
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(WarningOutlineIcon, {}),
1376
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ui11.Text, { size: 2, weight: "semibold", children: pendingDelete.name })
1670
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box, { padding: 4, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Stack, { space: 3, children: [
1671
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Flex, { align: "center", gap: 2, children: [
1672
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(WarningOutlineIcon, {}),
1673
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { size: 2, weight: "semibold", children: pendingDelete.name })
1377
1674
  ] }),
1378
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ui11.Text, { size: 1, muted: true, children: "This removes the deploy target from the dataset. The Vercel deploy hook itself is not affected." })
1675
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { size: 1, muted: true, children: "This removes the deploy target from the dataset. The Vercel deploy hook itself is not affected." })
1379
1676
  ] }) })
1380
1677
  }
1381
1678
  ),
1382
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ToastViewport, {})
1679
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ToastViewport, {})
1383
1680
  ] });
1384
1681
  }
1385
1682
 
1386
1683
  // src/schema/vercelDeploy.ts
1387
1684
  var import_sanity4 = require("sanity");
1685
+
1686
+ // src/schema/schemaIcon.tsx
1687
+ var import_react11 = require("react");
1688
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1689
+ var SchemaRocketIcon = (0, import_react11.forwardRef)(
1690
+ function SchemaRocketIcon2(props, ref) {
1691
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1692
+ "svg",
1693
+ {
1694
+ width: "1em",
1695
+ height: "1em",
1696
+ viewBox: "0 0 25 25",
1697
+ fill: "none",
1698
+ xmlns: "http://www.w3.org/2000/svg",
1699
+ "aria-hidden": "true",
1700
+ focusable: "false",
1701
+ ...props,
1702
+ ref,
1703
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1704
+ "path",
1705
+ {
1706
+ d: "M12.5 20.5L15.5 14M11 9.49999L4.5 12.5M9 14C9 14 7.54688 14.9531 6.5 16C5.5 17 4.5 20.5 4.5 20.5C4.5 20.5 8 19.5 9 18.5C10 17.5 11 16 11 16M9 14C9 14 10.1 9.9 12.5 7.5C15.5 4.5 20.5 4.5 20.5 4.5C20.5 4.5 20.5 9.5 17.5 12.5C15.7492 14.2508 11 16 11 16L9 14ZM16.5 9.99999C16.5 10.8284 15.8284 11.5 15 11.5C14.1716 11.5 13.5 10.8284 13.5 9.99999C13.5 9.17157 14.1716 8.49999 15 8.49999C15.8284 8.49999 16.5 9.17157 16.5 9.99999Z",
1707
+ stroke: "currentColor",
1708
+ strokeWidth: 1.2,
1709
+ strokeLinejoin: "round"
1710
+ }
1711
+ )
1712
+ }
1713
+ );
1714
+ }
1715
+ );
1716
+
1717
+ // src/schema/vercelDeploy.ts
1388
1718
  var vercelDeploySchema = (0, import_sanity4.defineType)({
1389
1719
  name: "vercel_deploy",
1390
1720
  title: "Deploy Target",
1391
1721
  type: "document",
1392
- icon: RocketIcon,
1722
+ icon: SchemaRocketIcon,
1393
1723
  fields: [
1394
1724
  (0, import_sanity4.defineField)({
1395
1725
  name: "name",
@@ -1430,13 +1760,37 @@ var vercelDeploySchema = (0, import_sanity4.defineType)({
1430
1760
  }
1431
1761
  });
1432
1762
 
1763
+ // src/schema/vercelConfig.ts
1764
+ var import_sanity5 = require("sanity");
1765
+ var vercelConfigSchema = (0, import_sanity5.defineType)({
1766
+ name: "vercelDeploy.config",
1767
+ title: "Vercel Deploy Configuration",
1768
+ type: "document",
1769
+ fields: [
1770
+ (0, import_sanity5.defineField)({
1771
+ name: "accessToken",
1772
+ title: "Vercel API Token",
1773
+ type: "string",
1774
+ description: "Readable by anyone who can read this dataset. Delete this document to revoke the stored token."
1775
+ })
1776
+ ],
1777
+ preview: {
1778
+ select: { token: "accessToken" },
1779
+ prepare: ({ token }) => ({
1780
+ title: "Vercel API Token",
1781
+ // Never render the secret itself in a preview.
1782
+ subtitle: token ? "Connected" : "Not set"
1783
+ })
1784
+ }
1785
+ });
1786
+
1433
1787
  // src/index.ts
1434
- var vercelDeploy = (0, import_sanity5.definePlugin)((options) => {
1788
+ var vercelDeploy = (0, import_sanity6.definePlugin)((options) => {
1435
1789
  const config = options ?? {};
1436
1790
  return {
1437
1791
  name: "deploy-vercel-from-sanity",
1438
1792
  schema: {
1439
- types: [vercelDeploySchema]
1793
+ types: [vercelDeploySchema, vercelConfigSchema]
1440
1794
  },
1441
1795
  tools: [
1442
1796
  {
@@ -1450,6 +1804,7 @@ var vercelDeploy = (0, import_sanity5.definePlugin)((options) => {
1450
1804
  });
1451
1805
  // Annotate the CommonJS export names for ESM import in node:
1452
1806
  0 && (module.exports = {
1807
+ vercelConfigSchema,
1453
1808
  vercelDeploy,
1454
1809
  vercelDeploySchema
1455
1810
  });