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