@liiift-studio/deploy-vercel-from-sanity 0.1.6 → 0.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
@@ -1,24 +1,23 @@
1
1
  // src/index.ts
2
2
  import { definePlugin } from "sanity";
3
- import { RocketIcon as RocketIcon4 } from "@sanity/icons";
3
+ import { RocketIcon as RocketIcon2 } from "@sanity/icons";
4
4
 
5
5
  // src/components/DeployTool.tsx
6
- import { useState as useState4, useEffect as useEffect3, useCallback as useCallback4 } from "react";
7
- import { useClient as useClient2 } from "sanity";
6
+ import { useState as useState5, useEffect as useEffect3, useCallback as useCallback5 } from "react";
7
+ import { useClient as useClient3 } from "sanity";
8
8
  import {
9
- Card as Card4,
9
+ Card as Card5,
10
10
  Box as Box4,
11
- Stack as Stack4,
12
- Flex as Flex5,
13
- Text as Text4,
14
- Heading as Heading2,
11
+ Stack as Stack5,
12
+ Flex as Flex6,
13
+ Text as Text5,
14
+ Heading,
15
15
  Spinner as Spinner4,
16
- Button as Button4,
17
- Badge as Badge4,
18
- Dialog as Dialog2,
16
+ Button as Button5,
17
+ Dialog as Dialog4,
19
18
  useToast
20
19
  } from "@sanity/ui";
21
- import { RocketIcon as RocketIcon2, TokenIcon as TokenIcon2, TrashIcon as TrashIcon2, WarningOutlineIcon as WarningOutlineIcon2 } from "@sanity/icons";
20
+ import { TokenIcon, TrashIcon as TrashIcon2, WarningOutlineIcon as WarningOutlineIcon2, AddIcon } from "@sanity/icons";
22
21
 
23
22
  // src/components/DeployItem.tsx
24
23
  import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2, useRef } from "react";
@@ -38,14 +37,16 @@ import {
38
37
  Code
39
38
  } from "@sanity/ui";
40
39
  import {
41
- RocketIcon,
42
40
  ClockIcon,
43
41
  TrashIcon,
44
42
  EllipsisVerticalIcon,
45
43
  LaunchIcon as LaunchIcon2,
46
44
  CopyIcon,
47
45
  CheckmarkIcon,
48
- WarningOutlineIcon
46
+ WarningOutlineIcon,
47
+ ChevronDownIcon,
48
+ ChevronUpIcon,
49
+ EditIcon
49
50
  } from "@sanity/icons";
50
51
 
51
52
  // src/lib/api.ts
@@ -190,7 +191,7 @@ function StatusBadge({ state, showSpinner }) {
190
191
  const spinning = showSpinner && (state === "QUEUED" || state === "INITIALIZING" || state === "BUILDING");
191
192
  return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
192
193
  spinning && /* @__PURE__ */ jsx(Spinner, { muted: true }),
193
- /* @__PURE__ */ jsx(Badge, { tone, mode: "outline", children: label })
194
+ /* @__PURE__ */ jsx(Badge, { tone, padding: 2, children: label })
194
195
  ] });
195
196
  }
196
197
 
@@ -265,7 +266,7 @@ function DeployHistory({ target, token, onClose }) {
265
266
  children: /* @__PURE__ */ jsx2(Text, { size: 1, style: { textDecoration: "underline" }, children: d.url.length > 36 ? `${d.url.slice(0, 36)}\u2026` : d.url })
266
267
  }
267
268
  ) : /* @__PURE__ */ jsx2(Text, { size: 1, muted: true, children: "\u2014" }) }),
268
- /* @__PURE__ */ jsx2(Box, { flex: 1, children: /* @__PURE__ */ jsx2(Badge2, { tone, mode: "outline", children: label }) }),
269
+ /* @__PURE__ */ jsx2(Box, { flex: 1, children: /* @__PURE__ */ jsx2(Badge2, { tone, children: label }) }),
269
270
  /* @__PURE__ */ jsx2(Box, { flex: 2, style: { overflow: "hidden" }, children: /* @__PURE__ */ jsxs2(Stack, { space: 1, children: [
270
271
  /* @__PURE__ */ jsx2(Text, { size: 1, children: branch }),
271
272
  sha && /* @__PURE__ */ jsxs2(Text, { size: 0, muted: true, children: [
@@ -295,7 +296,8 @@ function DeployHistory({ target, token, onClose }) {
295
296
  // src/components/DeployItem.tsx
296
297
  import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
297
298
  var POLL_INTERVAL_MS = 5e3;
298
- function DeployItem({ target, token, onDelete }) {
299
+ var LABEL_WIDTH = 64;
300
+ function DeployItem({ target, token, onDelete, onEdit }) {
299
301
  const { projectId, hookId } = parseHookUrl(target.url);
300
302
  const [deployments, setDeployments] = useState2([]);
301
303
  const [loadingInitial, setLoadingInitial] = useState2(true);
@@ -305,6 +307,7 @@ function DeployItem({ target, token, onDelete }) {
305
307
  const [showHistory, setShowHistory] = useState2(false);
306
308
  const [elapsed, setElapsed] = useState2(0);
307
309
  const [copied, setCopied] = useState2(false);
310
+ const [showDetails, setShowDetails] = useState2(false);
308
311
  const [showErrorLogs, setShowErrorLogs] = useState2(false);
309
312
  const [errorLines, setErrorLines] = useState2([]);
310
313
  const [loadingLogs, setLoadingLogs] = useState2(false);
@@ -403,140 +406,134 @@ function DeployItem({ target, token, onDelete }) {
403
406
  }
404
407
  }, [latest?.uid, token, target.teamId]);
405
408
  const toggleErrorLogs = useCallback2(() => {
406
- if (!showErrorLogs && errorLines.length === 0 && !logError) {
407
- fetchErrorLogs();
408
- }
409
+ if (!showErrorLogs && errorLines.length === 0 && !logError) fetchErrorLogs();
409
410
  setShowErrorLogs((v) => !v);
410
411
  }, [showErrorLogs, errorLines.length, logError, fetchErrorLogs]);
411
412
  const branch = latest?.meta?.githubCommitRef;
412
413
  const commitMsg = latest?.meta?.githubCommitMessage?.split("\n")[0];
413
414
  const sha = shortSha(latest?.meta?.githubCommitSha);
415
+ const fullSha = latest?.meta?.githubCommitSha;
414
416
  const creator = latest?.creator?.username;
415
417
  const deployedAt = latest?.created ? timeAgo(latest.created) : null;
416
418
  const vercelProjectUrl = projectHref(latest?.inspectorUrl);
417
419
  const isError = latest?.state === "ERROR";
418
420
  return /* @__PURE__ */ jsxs3(Fragment, { children: [
419
- /* @__PURE__ */ jsx3(Card2, { padding: 4, radius: 2, shadow: 1, tone: "default", children: /* @__PURE__ */ jsxs3(Flex3, { gap: 4, align: "center", children: [
420
- /* @__PURE__ */ jsxs3(Stack2, { space: 2, flex: 1, style: { minWidth: 0 }, children: [
421
- /* @__PURE__ */ jsxs3(Flex3, { align: "center", justify: "space-between", gap: 2, children: [
422
- /* @__PURE__ */ jsx3(Text2, { size: 2, weight: "semibold", children: target.name }),
423
- /* @__PURE__ */ jsx3(
424
- MenuButton,
425
- {
426
- button: /* @__PURE__ */ jsx3(Button2, { mode: "ghost", icon: EllipsisVerticalIcon, padding: 2 }),
427
- id: `menu-${target._id}`,
428
- menu: /* @__PURE__ */ jsxs3(Menu, { children: [
429
- /* @__PURE__ */ jsx3(
430
- MenuItem,
431
- {
432
- text: "History",
433
- icon: ClockIcon,
434
- onClick: () => setShowHistory(true)
435
- }
436
- ),
437
- safeHref(latest?.inspectorUrl) && /* @__PURE__ */ jsx3(
438
- MenuItem,
439
- {
440
- text: "Build logs",
441
- icon: LaunchIcon2,
442
- as: "a",
443
- href: safeHref(latest?.inspectorUrl),
444
- target: "_blank",
445
- rel: "noreferrer"
446
- }
447
- ),
448
- vercelProjectUrl && /* @__PURE__ */ jsx3(
449
- MenuItem,
450
- {
451
- text: "Open in Vercel",
452
- icon: LaunchIcon2,
453
- as: "a",
454
- href: vercelProjectUrl,
455
- target: "_blank",
456
- rel: "noreferrer"
457
- }
458
- ),
459
- !target.disableDeleteAction && /* @__PURE__ */ jsx3(
460
- MenuItem,
461
- {
462
- text: "Delete",
463
- icon: TrashIcon,
464
- tone: "critical",
465
- onClick: () => onDelete(target)
466
- }
467
- )
468
- ] }),
469
- popover: { placement: "bottom-end" }
470
- }
471
- )
472
- ] }),
473
- /* @__PURE__ */ jsxs3(Flex3, { gap: 2, wrap: "wrap", children: [
474
- /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, children: projectId ? `${projectId.slice(0, 18)}\u2026` : "\u2014" }),
475
- /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, children: "\xB7" }),
476
- /* @__PURE__ */ jsxs3(Text2, { size: 0, muted: true, children: [
477
- "Hook: ",
478
- hookId || "\u2014"
479
- ] }),
480
- target.teamId && /* @__PURE__ */ jsxs3(Fragment, { children: [
481
- /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, children: "\xB7" }),
482
- /* @__PURE__ */ jsxs3(Text2, { size: 0, muted: true, children: [
483
- "Team: ",
484
- target.teamId
485
- ] })
486
- ] })
487
- ] }),
488
- loadingInitial ? /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 2, children: [
489
- /* @__PURE__ */ jsx3(Spinner3, { muted: true }),
490
- /* @__PURE__ */ jsx3(Text2, { size: 1, muted: true, children: "Loading\u2026" })
491
- ] }) : /* @__PURE__ */ jsxs3(Stack2, { space: 2, children: [
492
- /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 2, wrap: "wrap", children: [
493
- triggering ? /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 2, children: [
494
- /* @__PURE__ */ jsx3(Spinner3, { muted: true }),
495
- /* @__PURE__ */ jsx3(Badge3, { tone: "caution", mode: "outline", children: "Triggering\u2026" })
496
- ] }) : /* @__PURE__ */ jsx3(StatusBadge, { state: latest?.state, showSpinner: true }),
497
- isActive && elapsed > 0 ? /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 1, children: [
498
- /* @__PURE__ */ jsx3(ClockIcon, {}),
499
- /* @__PURE__ */ jsx3(Text2, { size: 1, muted: true, children: formatDuration(elapsed) })
500
- ] }) : !isActive && deployedAt ? /* @__PURE__ */ jsx3(Text2, { size: 1, muted: true, children: deployedAt }) : null,
501
- branch && /* @__PURE__ */ jsxs3(Fragment, { children: [
502
- /* @__PURE__ */ jsx3(Text2, { size: 1, muted: true, children: "\xB7" }),
503
- /* @__PURE__ */ jsx3(Badge3, { tone: "default", mode: "outline", children: branch })
421
+ /* @__PURE__ */ jsx3(Card2, { radius: 2, shadow: 1, tone: "default", children: /* @__PURE__ */ jsxs3(Flex3, { align: "stretch", className: "dvfs-card-flex", children: [
422
+ /* @__PURE__ */ jsxs3(Flex3, { direction: "column", flex: 1, style: { minWidth: 0 }, children: [
423
+ /* @__PURE__ */ jsxs3(Stack2, { space: 3, padding: 3, style: { flex: 1 }, children: [
424
+ /* @__PURE__ */ jsxs3(Flex3, { align: "center", justify: "space-between", gap: 2, children: [
425
+ /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 2, style: { minWidth: 0, flexWrap: "wrap" }, children: [
426
+ /* @__PURE__ */ jsx3(Text2, { size: 2, weight: "semibold", style: { flexShrink: 0 }, children: target.name }),
427
+ branch && /* @__PURE__ */ jsx3(Badge3, { tone: "default", padding: 2, children: /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 1, children: [
428
+ branch,
429
+ /* @__PURE__ */ jsx3("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", style: { marginLeft: "-0.1em", opacity: 0.5 }, children: /* @__PURE__ */ jsx3("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" }) })
430
+ ] }) }),
431
+ token && !loadingInitial && /* @__PURE__ */ jsxs3(Fragment, { children: [
432
+ triggering ? /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 1, children: [
433
+ /* @__PURE__ */ jsx3(Spinner3, { muted: true }),
434
+ /* @__PURE__ */ jsx3(Badge3, { tone: "caution", padding: 2, children: "Triggering\u2026" })
435
+ ] }) : /* @__PURE__ */ jsx3(StatusBadge, { state: latest?.state, showSpinner: true }),
436
+ isActive && elapsed > 0 ? /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 1, children: [
437
+ /* @__PURE__ */ jsx3(ClockIcon, {}),
438
+ /* @__PURE__ */ jsx3(Text2, { size: 1, muted: true, children: formatDuration(elapsed) })
439
+ ] }) : !isActive && deployedAt ? /* @__PURE__ */ jsx3(Text2, { size: 1, muted: true, children: deployedAt }) : null
440
+ ] })
504
441
  ] }),
505
- sha && /* @__PURE__ */ jsx3(
506
- Tooltip,
442
+ /* @__PURE__ */ jsx3(
443
+ MenuButton,
507
444
  {
508
- content: /* @__PURE__ */ jsx3(Box2, { padding: 2, children: /* @__PURE__ */ jsx3(Text2, { size: 1, children: commitMsg ?? sha }) }),
509
- portal: true,
510
- children: /* @__PURE__ */ jsx3(
511
- Text2,
512
- {
513
- size: 1,
514
- muted: true,
515
- style: { cursor: "default", fontFamily: "monospace" },
516
- children: sha
517
- }
518
- )
445
+ button: /* @__PURE__ */ jsx3(Button2, { mode: "ghost", icon: EllipsisVerticalIcon, padding: 2 }),
446
+ id: `menu-${target._id}`,
447
+ menu: /* @__PURE__ */ jsxs3(Menu, { children: [
448
+ /* @__PURE__ */ jsx3(
449
+ MenuItem,
450
+ {
451
+ text: "Edit target",
452
+ icon: EditIcon,
453
+ onClick: () => onEdit(target)
454
+ }
455
+ ),
456
+ /* @__PURE__ */ jsx3(
457
+ MenuItem,
458
+ {
459
+ text: "History",
460
+ icon: ClockIcon,
461
+ onClick: () => setShowHistory(true)
462
+ }
463
+ ),
464
+ safeHref(latest?.inspectorUrl) && /* @__PURE__ */ jsx3(
465
+ MenuItem,
466
+ {
467
+ text: "Build logs",
468
+ icon: LaunchIcon2,
469
+ as: "a",
470
+ href: safeHref(latest?.inspectorUrl),
471
+ target: "_blank",
472
+ rel: "noreferrer"
473
+ }
474
+ ),
475
+ vercelProjectUrl && /* @__PURE__ */ jsx3(
476
+ MenuItem,
477
+ {
478
+ text: "Open in Vercel",
479
+ icon: LaunchIcon2,
480
+ as: "a",
481
+ href: vercelProjectUrl,
482
+ target: "_blank",
483
+ rel: "noreferrer"
484
+ }
485
+ ),
486
+ !target.disableDeleteAction && /* @__PURE__ */ jsx3(
487
+ MenuItem,
488
+ {
489
+ text: "Delete",
490
+ icon: TrashIcon,
491
+ tone: "critical",
492
+ onClick: () => onDelete(target)
493
+ }
494
+ )
495
+ ] }),
496
+ popover: { placement: "bottom-end" }
519
497
  }
520
- ),
521
- creator && /* @__PURE__ */ jsxs3(Text2, { size: 1, muted: true, children: [
522
- "by ",
523
- creator
524
- ] }),
525
- latest?.url && latest.state === "READY" && /* @__PURE__ */ jsxs3(Fragment, { children: [
526
- /* @__PURE__ */ jsx3(
498
+ )
499
+ ] }),
500
+ /* @__PURE__ */ jsx3("hr", { style: { border: "none", borderTop: "1px solid currentColor", opacity: 0.1, margin: 0 } }),
501
+ !token ? /* @__PURE__ */ jsx3(Text2, { size: 1, muted: true, children: "Connect a Vercel API token to see deployment status." }) : loadingInitial ? /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 2, children: [
502
+ /* @__PURE__ */ jsx3(Spinner3, { muted: true }),
503
+ /* @__PURE__ */ jsx3(Text2, { size: 1, muted: true, children: "Loading\u2026" })
504
+ ] }) : /* @__PURE__ */ jsxs3(Stack2, { space: 2, children: [
505
+ /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 2, wrap: "wrap", children: [
506
+ latest?.url && latest.state === "READY" && /* @__PURE__ */ jsx3(Fragment, { children: /* @__PURE__ */ jsx3(
527
507
  "a",
528
508
  {
529
509
  href: `https://${latest.url}`,
530
510
  target: "_blank",
531
511
  rel: "noreferrer",
532
512
  style: { color: "inherit" },
533
- children: /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 1, children: [
534
- /* @__PURE__ */ jsx3(LaunchIcon2, {}),
535
- /* @__PURE__ */ jsx3(Text2, { size: 1, children: "Preview" })
536
- ] })
513
+ children: /* @__PURE__ */ jsx3(Flex3, { align: "center", gap: 1, children: /* @__PURE__ */ jsx3(Text2, { size: 1, children: latest.url }) })
514
+ }
515
+ ) }),
516
+ sha && /* @__PURE__ */ jsx3(
517
+ Tooltip,
518
+ {
519
+ content: /* @__PURE__ */ jsx3(Box2, { padding: 2, children: /* @__PURE__ */ jsx3(Text2, { size: 1, children: commitMsg ?? sha }) }),
520
+ portal: true,
521
+ children: /* @__PURE__ */ jsx3(
522
+ Text2,
523
+ {
524
+ size: 1,
525
+ muted: true,
526
+ style: { cursor: "default", fontFamily: "monospace" },
527
+ children: sha
528
+ }
529
+ )
537
530
  }
538
531
  ),
539
- /* @__PURE__ */ jsx3(
532
+ creator && /* @__PURE__ */ jsxs3(Text2, { size: 1, muted: true, children: [
533
+ "by ",
534
+ creator
535
+ ] }),
536
+ latest?.url && latest.state === "READY" && /* @__PURE__ */ jsx3(Fragment, { children: /* @__PURE__ */ jsx3(
540
537
  Tooltip,
541
538
  {
542
539
  content: /* @__PURE__ */ jsx3(Box2, { padding: 2, children: /* @__PURE__ */ jsx3(Text2, { size: 1, children: copied ? "Copied!" : "Copy URL" }) }),
@@ -548,126 +545,162 @@ function DeployItem({ target, token, onDelete }) {
548
545
  icon: copied ? CheckmarkIcon : CopyIcon,
549
546
  padding: 1,
550
547
  tone: copied ? "positive" : "default",
551
- onClick: copyUrl
548
+ onClick: copyUrl,
549
+ style: { cursor: "pointer" }
552
550
  }
553
551
  )
554
552
  }
555
- )
553
+ ) })
554
+ ] }),
555
+ /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 2, wrap: "wrap", children: [
556
+ commitMsg && /* @__PURE__ */ jsx3(
557
+ Text2,
558
+ {
559
+ size: 0,
560
+ muted: true,
561
+ style: { fontStyle: "italic" },
562
+ children: commitMsg
563
+ }
564
+ ),
565
+ isError && /* @__PURE__ */ jsxs3(Stack2, { space: 2, children: [
566
+ /* @__PURE__ */ jsx3(
567
+ Button2,
568
+ {
569
+ text: showErrorLogs ? "Hide error details" : "Show error details",
570
+ mode: "ghost",
571
+ tone: "critical",
572
+ icon: WarningOutlineIcon,
573
+ fontSize: 1,
574
+ padding: 2,
575
+ onClick: toggleErrorLogs,
576
+ style: { alignSelf: "flex-start", cursor: "pointer" }
577
+ }
578
+ ),
579
+ showErrorLogs && /* @__PURE__ */ jsxs3(Card2, { tone: "critical", radius: 2, padding: 3, children: [
580
+ loadingLogs && /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 2, children: [
581
+ /* @__PURE__ */ jsx3(Spinner3, { muted: true }),
582
+ /* @__PURE__ */ jsx3(Text2, { size: 1, muted: true, children: "Loading logs\u2026" })
583
+ ] }),
584
+ logError && /* @__PURE__ */ jsxs3(Stack2, { space: 2, children: [
585
+ /* @__PURE__ */ jsx3(Text2, { size: 1, children: logError }),
586
+ safeHref(latest?.inspectorUrl) && /* @__PURE__ */ jsx3("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 1, children: [
587
+ /* @__PURE__ */ jsx3(LaunchIcon2, {}),
588
+ /* @__PURE__ */ jsx3(Text2, { size: 1, children: "View full logs in Vercel" })
589
+ ] }) })
590
+ ] }),
591
+ !loadingLogs && !logError && errorLines.length > 0 && /* @__PURE__ */ jsxs3(Stack2, { space: 2, children: [
592
+ /* @__PURE__ */ jsx3(Box2, { style: { maxHeight: 240, overflowY: "auto", fontFamily: "monospace", fontSize: 11, lineHeight: 1.6 }, children: errorLines.map((line, i) => /* @__PURE__ */ jsx3(Code, { size: 1, style: { display: "block", whiteSpace: "pre-wrap", wordBreak: "break-all" }, children: line }, i)) }),
593
+ safeHref(latest?.inspectorUrl) && /* @__PURE__ */ jsx3("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 1, children: [
594
+ /* @__PURE__ */ jsx3(LaunchIcon2, {}),
595
+ /* @__PURE__ */ jsx3(Text2, { size: 1, children: "View full logs in Vercel" })
596
+ ] }) })
597
+ ] })
598
+ ] })
599
+ ] }),
600
+ deployError && /* @__PURE__ */ jsx3(Card2, { tone: "critical", padding: 2, radius: 2, children: /* @__PURE__ */ jsx3(Text2, { size: 1, children: deployError }) })
556
601
  ] })
557
- ] }),
558
- commitMsg && /* @__PURE__ */ jsx3(
559
- Text2,
602
+ ] })
603
+ ] }),
604
+ /* @__PURE__ */ jsxs3(Box2, { children: [
605
+ /* @__PURE__ */ jsx3(
606
+ Button2,
560
607
  {
561
- size: 0,
562
- muted: true,
563
- style: {
564
- overflow: "hidden",
565
- textOverflow: "ellipsis",
566
- whiteSpace: "nowrap"
567
- },
568
- children: commitMsg
608
+ mode: "ghost",
609
+ iconRight: showDetails ? ChevronUpIcon : ChevronDownIcon,
610
+ text: "Details",
611
+ fontSize: 0,
612
+ padding: 3,
613
+ onClick: () => setShowDetails((v) => !v),
614
+ style: { width: "100%", justifyContent: "flex-start", borderRadius: 0, cursor: "pointer" }
569
615
  }
570
616
  ),
571
- isError && /* @__PURE__ */ jsxs3(Stack2, { space: 2, children: [
572
- /* @__PURE__ */ jsx3(
617
+ showDetails && /* @__PURE__ */ jsx3(Card2, { tone: "primary", padding: 3, style: { borderRadius: 0 }, children: /* @__PURE__ */ jsxs3(Stack2, { space: 2, children: [
618
+ /* @__PURE__ */ jsxs3(Flex3, { gap: 2, align: "center", children: [
619
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Project" }),
620
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: projectId || "\u2014" })
621
+ ] }),
622
+ /* @__PURE__ */ jsxs3(Flex3, { gap: 2, align: "center", children: [
623
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Hook" }),
624
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: hookId || "\u2014" })
625
+ ] }),
626
+ target.teamId && /* @__PURE__ */ jsxs3(Flex3, { gap: 2, align: "center", children: [
627
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Team" }),
628
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: target.teamId })
629
+ ] }),
630
+ fullSha && /* @__PURE__ */ jsxs3(Flex3, { gap: 2, align: "flex-start", children: [
631
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Commit" }),
632
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, style: { fontFamily: "monospace", wordBreak: "break-all" }, children: fullSha })
633
+ ] }),
634
+ latest?.meta?.githubCommitAuthorName && /* @__PURE__ */ jsxs3(Flex3, { gap: 2, align: "center", children: [
635
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Author" }),
636
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, children: latest.meta.githubCommitAuthorName })
637
+ ] }),
638
+ branch && /* @__PURE__ */ jsxs3(Flex3, { gap: 2, align: "center", children: [
639
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Branch" }),
640
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: branch })
641
+ ] }),
642
+ latest?.uid && /* @__PURE__ */ jsxs3(Flex3, { gap: 2, align: "center", children: [
643
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Deploy ID" }),
644
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: latest.uid })
645
+ ] }),
646
+ latest?.url && /* @__PURE__ */ jsxs3(Flex3, { gap: 2, align: "flex-start", children: [
647
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "URL" }),
648
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, style: { fontFamily: "monospace", wordBreak: "break-all" }, children: latest.url })
649
+ ] }),
650
+ safeHref(latest?.inspectorUrl) && /* @__PURE__ */ jsxs3(Flex3, { gap: 2, align: "center", children: [
651
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Inspector" }),
652
+ /* @__PURE__ */ jsx3("a", { href: safeHref(latest?.inspectorUrl), target: "_blank", rel: "noreferrer", style: { color: "inherit" }, children: /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 1, children: [
653
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, style: { fontFamily: "monospace" }, children: "Open in Vercel" }),
654
+ /* @__PURE__ */ jsx3(LaunchIcon2, { style: { width: 10, height: 10 } })
655
+ ] }) })
656
+ ] }),
657
+ latest?.created && /* @__PURE__ */ jsxs3(Flex3, { gap: 2, align: "center", children: [
658
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, weight: "semibold", style: { minWidth: LABEL_WIDTH }, children: "Created" }),
659
+ /* @__PURE__ */ jsx3(Text2, { size: 0, muted: true, children: new Date(latest.created).toLocaleString() })
660
+ ] })
661
+ ] }) })
662
+ ] })
663
+ ] }),
664
+ /* @__PURE__ */ jsxs3(
665
+ Flex3,
666
+ {
667
+ direction: "column",
668
+ gap: 2,
669
+ className: "dvfs-deploy-col",
670
+ style: { flexShrink: 0, alignSelf: "stretch" },
671
+ children: [
672
+ isActiveState(latest?.state) && /* @__PURE__ */ jsx3(
573
673
  Button2,
574
674
  {
575
- text: showErrorLogs ? "Hide error details" : "Show error details",
675
+ text: "Cancel",
576
676
  mode: "ghost",
577
677
  tone: "critical",
578
- icon: WarningOutlineIcon,
579
- fontSize: 1,
580
- padding: 2,
581
- onClick: toggleErrorLogs,
582
- style: { alignSelf: "flex-start" }
678
+ loading: canceling,
679
+ disabled: canceling,
680
+ onClick: cancel,
681
+ style: { cursor: "pointer" }
583
682
  }
584
683
  ),
585
- showErrorLogs && /* @__PURE__ */ jsxs3(Card2, { tone: "critical", radius: 2, padding: 3, children: [
586
- loadingLogs && /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 2, children: [
587
- /* @__PURE__ */ jsx3(Spinner3, { muted: true }),
588
- /* @__PURE__ */ jsx3(Text2, { size: 1, muted: true, children: "Loading logs\u2026" })
589
- ] }),
590
- logError && /* @__PURE__ */ jsxs3(Stack2, { space: 2, children: [
591
- /* @__PURE__ */ jsx3(Text2, { size: 1, children: logError }),
592
- safeHref(latest?.inspectorUrl) && /* @__PURE__ */ jsx3(
593
- "a",
594
- {
595
- href: safeHref(latest?.inspectorUrl),
596
- target: "_blank",
597
- rel: "noreferrer",
598
- style: { color: "inherit" },
599
- children: /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 1, children: [
600
- /* @__PURE__ */ jsx3(LaunchIcon2, {}),
601
- /* @__PURE__ */ jsx3(Text2, { size: 1, children: "View full logs in Vercel" })
602
- ] })
603
- }
604
- )
605
- ] }),
606
- !loadingLogs && !logError && errorLines.length > 0 && /* @__PURE__ */ jsxs3(Stack2, { space: 2, children: [
607
- /* @__PURE__ */ jsx3(
608
- Box2,
609
- {
610
- style: {
611
- maxHeight: 240,
612
- overflowY: "auto",
613
- fontFamily: "monospace",
614
- fontSize: 11,
615
- lineHeight: 1.6
616
- },
617
- children: errorLines.map((line, i) => /* @__PURE__ */ jsx3(
618
- Code,
619
- {
620
- size: 1,
621
- style: { display: "block", whiteSpace: "pre-wrap", wordBreak: "break-all" },
622
- children: line
623
- },
624
- i
625
- ))
626
- }
627
- ),
628
- safeHref(latest?.inspectorUrl) && /* @__PURE__ */ jsx3(
629
- "a",
630
- {
631
- href: safeHref(latest?.inspectorUrl),
632
- target: "_blank",
633
- rel: "noreferrer",
634
- style: { color: "inherit" },
635
- children: /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 1, children: [
636
- /* @__PURE__ */ jsx3(LaunchIcon2, {}),
637
- /* @__PURE__ */ jsx3(Text2, { size: 1, children: "View full logs in Vercel" })
638
- ] })
639
- }
640
- )
641
- ] })
642
- ] })
643
- ] }),
644
- deployError && /* @__PURE__ */ jsx3(Card2, { tone: "critical", padding: 2, radius: 2, children: /* @__PURE__ */ jsx3(Text2, { size: 1, children: deployError }) })
645
- ] })
646
- ] }),
647
- /* @__PURE__ */ jsxs3(Stack2, { space: 2, style: { flexShrink: 0 }, children: [
648
- isActiveState(latest?.state) && /* @__PURE__ */ jsx3(
649
- Button2,
650
- {
651
- text: "Cancel",
652
- mode: "ghost",
653
- tone: "critical",
654
- loading: canceling,
655
- disabled: canceling,
656
- onClick: cancel
657
- }
658
- ),
659
- /* @__PURE__ */ jsx3(
660
- Button2,
661
- {
662
- text: "Deploy",
663
- tone: "primary",
664
- icon: RocketIcon,
665
- loading: triggering,
666
- disabled: isActive,
667
- onClick: deploy
668
- }
669
- )
670
- ] })
684
+ /* @__PURE__ */ jsx3(
685
+ Button2,
686
+ {
687
+ text: "Deploy",
688
+ tone: "primary",
689
+ loading: triggering,
690
+ disabled: isActive,
691
+ onClick: deploy,
692
+ style: {
693
+ flex: 1,
694
+ borderRadius: 0,
695
+ borderTopRightRadius: 3,
696
+ borderBottomRightRadius: 3,
697
+ cursor: "pointer"
698
+ }
699
+ }
700
+ )
701
+ ]
702
+ }
703
+ )
671
704
  ] }) }),
672
705
  showHistory && /* @__PURE__ */ jsx3(
673
706
  DeployHistory,
@@ -683,19 +716,11 @@ function DeployItem({ target, token, onDelete }) {
683
716
  // src/components/TokenSetup.tsx
684
717
  import { useState as useState3, useCallback as useCallback3 } from "react";
685
718
  import { useClient } from "sanity";
686
- import {
687
- Card as Card3,
688
- Stack as Stack3,
689
- Text as Text3,
690
- Heading,
691
- TextInput,
692
- Button as Button3,
693
- Flex as Flex4
694
- } from "@sanity/ui";
695
- import { TokenIcon, CheckmarkCircleIcon } from "@sanity/icons";
719
+ import { Stack as Stack3, Text as Text3, TextInput, Button as Button3, Card as Card3, Dialog as Dialog2, Flex as Flex4 } from "@sanity/ui";
720
+ import { CheckmarkCircleIcon } from "@sanity/icons";
696
721
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
697
722
  var TOKEN_DOC_ID = "config.vercelDeploy";
698
- function TokenSetup({ onSaved }) {
723
+ function TokenSetup({ onSaved, onCancel }) {
699
724
  const client = useClient({ apiVersion: "2025-01-01" });
700
725
  const [token, setToken] = useState3("");
701
726
  const [saving, setSaving] = useState3(false);
@@ -717,68 +742,217 @@ function TokenSetup({ onSaved }) {
717
742
  setSaving(false);
718
743
  }
719
744
  }, [client, token, onSaved]);
720
- return /* @__PURE__ */ jsx4(Card3, { height: "fill", tone: "transparent", children: /* @__PURE__ */ jsx4(Flex4, { align: "center", justify: "center", height: "fill", padding: 6, children: /* @__PURE__ */ jsx4(Card3, { padding: 5, radius: 3, shadow: 1, style: { maxWidth: 480, width: "100%" }, children: /* @__PURE__ */ jsxs4(Stack3, { space: 5, children: [
721
- /* @__PURE__ */ jsxs4(Flex4, { align: "center", gap: 3, children: [
722
- /* @__PURE__ */ jsx4(Text3, { size: 3, children: /* @__PURE__ */ jsx4(TokenIcon, {}) }),
723
- /* @__PURE__ */ jsx4(Heading, { size: 2, children: "Connect to Vercel" })
724
- ] }),
725
- /* @__PURE__ */ jsxs4(Stack3, { space: 3, children: [
726
- /* @__PURE__ */ jsxs4(Text3, { size: 1, muted: true, children: [
727
- "A Vercel API token is required to read deployment status, history, and build logs. Your token is stored securely in the Sanity dataset under a",
728
- " ",
729
- /* @__PURE__ */ jsx4("code", { children: "secrets.*" }),
730
- " document ID that is not publicly readable."
745
+ return /* @__PURE__ */ jsx4(
746
+ Dialog2,
747
+ {
748
+ header: "Connect Vercel API token",
749
+ id: "token-setup",
750
+ onClose: onCancel,
751
+ width: 1,
752
+ footer: /* @__PURE__ */ jsxs4(Flex4, { padding: 3, gap: 2, justify: "flex-end", children: [
753
+ onCancel && /* @__PURE__ */ jsx4(Button3, { text: "Cancel", mode: "ghost", onClick: onCancel, style: { cursor: "pointer" } }),
754
+ /* @__PURE__ */ jsx4(
755
+ Button3,
756
+ {
757
+ text: "Save and connect",
758
+ tone: "primary",
759
+ icon: CheckmarkCircleIcon,
760
+ loading: saving,
761
+ disabled: !token.trim() || saving,
762
+ onClick: save,
763
+ style: { cursor: "pointer" }
764
+ }
765
+ )
731
766
  ] }),
732
- /* @__PURE__ */ jsxs4(Text3, { size: 1, muted: true, children: [
733
- "Create a token at",
734
- " ",
735
- /* @__PURE__ */ jsx4("strong", { children: "vercel.com \u2192 Settings \u2192 Tokens" }),
736
- ". Choose ",
737
- /* @__PURE__ */ jsx4("strong", { children: "Full Account" }),
738
- " scope."
767
+ children: /* @__PURE__ */ jsxs4(Stack3, { space: 4, padding: 4, children: [
768
+ /* @__PURE__ */ jsxs4(Stack3, { space: 3, children: [
769
+ /* @__PURE__ */ jsx4(Text3, { 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." }),
770
+ /* @__PURE__ */ jsxs4(Text3, { size: 1, muted: true, children: [
771
+ "Create one at ",
772
+ /* @__PURE__ */ jsx4("strong", { children: "vercel.com \u2192 Settings \u2192 Tokens" }),
773
+ " with",
774
+ " ",
775
+ /* @__PURE__ */ jsx4("strong", { children: "Full Account" }),
776
+ " scope. The token is stored in your Sanity dataset and shared across all authenticated studio users."
777
+ ] })
778
+ ] }),
779
+ /* @__PURE__ */ jsxs4(Stack3, { space: 2, children: [
780
+ /* @__PURE__ */ jsx4(Text3, { size: 1, weight: "semibold", children: "Vercel API Token" }),
781
+ /* @__PURE__ */ jsx4(
782
+ TextInput,
783
+ {
784
+ value: token,
785
+ onChange: (e) => setToken(e.target.value),
786
+ placeholder: "xxxxxxxxxxxxxxxxxxxxxxxx",
787
+ type: "password"
788
+ }
789
+ )
790
+ ] }),
791
+ error && /* @__PURE__ */ jsx4(Card3, { tone: "critical", padding: 3, radius: 2, children: /* @__PURE__ */ jsx4(Text3, { size: 1, children: error }) })
739
792
  ] })
740
- ] }),
741
- /* @__PURE__ */ jsxs4(Stack3, { space: 3, children: [
742
- /* @__PURE__ */ jsx4(Text3, { size: 1, weight: "semibold", children: "Vercel API Token" }),
743
- /* @__PURE__ */ jsx4(
744
- TextInput,
745
- {
746
- value: token,
747
- onChange: (e) => setToken(e.target.value),
748
- placeholder: "xxxxxxxxxxxxxxxxxxxxxxxx",
749
- type: "password"
750
- }
751
- )
752
- ] }),
753
- error && /* @__PURE__ */ jsx4(Card3, { tone: "critical", padding: 3, radius: 2, children: /* @__PURE__ */ jsx4(Text3, { size: 1, children: error }) }),
754
- /* @__PURE__ */ jsx4(
755
- Button3,
756
- {
757
- text: "Save and connect",
758
- tone: "primary",
759
- icon: CheckmarkCircleIcon,
760
- loading: saving,
761
- disabled: !token.trim() || saving,
762
- onClick: save
793
+ }
794
+ );
795
+ }
796
+
797
+ // src/components/DeployTargetForm.tsx
798
+ import { useState as useState4, useCallback as useCallback4 } from "react";
799
+ import { useClient as useClient2 } from "sanity";
800
+ import {
801
+ Dialog as Dialog3,
802
+ Box as Box3,
803
+ Stack as Stack4,
804
+ Flex as Flex5,
805
+ Text as Text4,
806
+ TextInput as TextInput2,
807
+ Button as Button4,
808
+ Switch,
809
+ Label,
810
+ Card as Card4
811
+ } from "@sanity/ui";
812
+ import { CheckmarkCircleIcon as CheckmarkCircleIcon2 } from "@sanity/icons";
813
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
814
+ var VERCEL_HOOK_RE2 = /^https:\/\/api\.vercel\.com\/v1\/integrations\/deploy\//;
815
+ function DeployTargetForm({ initial, onSaved, onClose }) {
816
+ const client = useClient2({ apiVersion: "2025-01-01" });
817
+ const isEdit = Boolean(initial);
818
+ const [name, setName] = useState4(initial?.name ?? "");
819
+ const [url, setUrl] = useState4(initial?.url ?? "");
820
+ const [teamId, setTeamId] = useState4(initial?.teamId ?? "");
821
+ const [disableDelete, setDisableDelete] = useState4(initial?.disableDeleteAction ?? false);
822
+ const [saving, setSaving] = useState4(false);
823
+ const [error, setError] = useState4(null);
824
+ const urlValid = !url || VERCEL_HOOK_RE2.test(url.trim());
825
+ const canSave = name.trim() && url.trim() && urlValid;
826
+ const save = useCallback4(async () => {
827
+ if (!canSave) return;
828
+ setSaving(true);
829
+ setError(null);
830
+ const fields = {
831
+ name: name.trim(),
832
+ url: url.trim(),
833
+ ...teamId.trim() ? { teamId: teamId.trim() } : { teamId: null },
834
+ disableDeleteAction: disableDelete
835
+ };
836
+ try {
837
+ if (isEdit && initial) {
838
+ await client.patch(initial._id).set(fields).commit();
839
+ } else {
840
+ await client.create({ _type: "vercel_deploy", ...fields });
763
841
  }
764
- )
765
- ] }) }) }) });
842
+ onSaved();
843
+ } catch (err) {
844
+ setError(err instanceof Error ? err.message : "Save failed");
845
+ } finally {
846
+ setSaving(false);
847
+ }
848
+ }, [canSave, isEdit, initial, client, name, url, teamId, disableDelete, onSaved]);
849
+ return /* @__PURE__ */ jsx5(
850
+ Dialog3,
851
+ {
852
+ header: isEdit ? `Edit "${initial?.name}"` : "Add deploy target",
853
+ id: "deploy-target-form",
854
+ onClose,
855
+ width: 1,
856
+ footer: /* @__PURE__ */ jsxs5(Flex5, { padding: 3, gap: 2, justify: "flex-end", children: [
857
+ /* @__PURE__ */ jsx5(Button4, { text: "Cancel", mode: "ghost", onClick: onClose, style: { cursor: "pointer" } }),
858
+ /* @__PURE__ */ jsx5(
859
+ Button4,
860
+ {
861
+ text: isEdit ? "Save changes" : "Add target",
862
+ tone: "primary",
863
+ icon: CheckmarkCircleIcon2,
864
+ loading: saving,
865
+ disabled: !canSave || saving,
866
+ onClick: save,
867
+ style: { cursor: "pointer" }
868
+ }
869
+ )
870
+ ] }),
871
+ children: /* @__PURE__ */ jsx5(Box3, { padding: 4, children: /* @__PURE__ */ jsxs5(Stack4, { space: 4, children: [
872
+ /* @__PURE__ */ jsxs5(Stack4, { space: 2, children: [
873
+ /* @__PURE__ */ jsxs5(Label, { size: 1, children: [
874
+ "Name ",
875
+ /* @__PURE__ */ jsx5("span", { style: { color: "var(--card-fg-color)" }, children: "*" })
876
+ ] }),
877
+ /* @__PURE__ */ jsx5(
878
+ TextInput2,
879
+ {
880
+ value: name,
881
+ onChange: (e) => setName(e.target.value),
882
+ placeholder: "Production"
883
+ }
884
+ )
885
+ ] }),
886
+ /* @__PURE__ */ jsxs5(Stack4, { space: 2, children: [
887
+ /* @__PURE__ */ jsxs5(Label, { size: 1, children: [
888
+ "Deploy hook URL ",
889
+ /* @__PURE__ */ jsx5("span", { style: { color: "var(--card-fg-color)" }, children: "*" })
890
+ ] }),
891
+ /* @__PURE__ */ jsx5(
892
+ TextInput2,
893
+ {
894
+ value: url,
895
+ onChange: (e) => setUrl(e.target.value),
896
+ placeholder: "https://api.vercel.com/v1/integrations/deploy/\u2026"
897
+ }
898
+ ),
899
+ url && !urlValid && /* @__PURE__ */ jsx5(Text4, { 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)" }),
900
+ /* @__PURE__ */ jsx5(Text4, { size: 0, muted: true, children: "Vercel dashboard \u2192 Project \u2192 Settings \u2192 Git \u2192 Deploy Hooks" })
901
+ ] }),
902
+ /* @__PURE__ */ jsxs5(Stack4, { space: 2, children: [
903
+ /* @__PURE__ */ jsxs5(Label, { size: 1, children: [
904
+ "Team ID ",
905
+ /* @__PURE__ */ jsx5("span", { style: { opacity: 0.5, fontWeight: "normal" }, children: "\u2014 optional" })
906
+ ] }),
907
+ /* @__PURE__ */ jsx5(
908
+ TextInput2,
909
+ {
910
+ value: teamId,
911
+ onChange: (e) => setTeamId(e.target.value),
912
+ placeholder: "team_xxxxxxxx"
913
+ }
914
+ ),
915
+ /* @__PURE__ */ jsx5(Text4, { size: 0, muted: true, children: "Required for team-owned Vercel projects." })
916
+ ] }),
917
+ /* @__PURE__ */ jsxs5(Flex5, { align: "center", gap: 3, children: [
918
+ /* @__PURE__ */ jsx5(
919
+ Switch,
920
+ {
921
+ checked: disableDelete,
922
+ onChange: (e) => setDisableDelete(e.target.checked),
923
+ id: "disable-delete"
924
+ }
925
+ ),
926
+ /* @__PURE__ */ jsxs5(Stack4, { space: 1, children: [
927
+ /* @__PURE__ */ jsx5(Label, { size: 1, htmlFor: "disable-delete", children: "Disable delete action" }),
928
+ /* @__PURE__ */ jsx5(Text4, { size: 0, muted: true, children: "Hides the delete button for this target in the studio." })
929
+ ] })
930
+ ] }),
931
+ error && /* @__PURE__ */ jsx5(Card4, { tone: "critical", padding: 3, radius: 2, children: /* @__PURE__ */ jsx5(Text4, { size: 1, children: error }) })
932
+ ] }) })
933
+ }
934
+ );
766
935
  }
767
936
 
937
+ // src/version.ts
938
+ var VERSION = "0.2.0";
939
+
768
940
  // src/components/DeployTool.tsx
769
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
941
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
770
942
  var TOKEN_QUERY = `*[_id == "config.vercelDeploy"][0].accessToken`;
771
943
  var TARGETS_QUERY = `*[_type == "vercel_deploy"] | order(_createdAt asc)`;
772
944
  function DeployTool() {
773
- const client = useClient2({ apiVersion: "2025-01-01" });
945
+ const client = useClient3({ apiVersion: "2025-01-01" });
774
946
  const toast = useToast();
775
- const [token, setToken] = useState4(null);
776
- const [targets, setTargets] = useState4([]);
777
- const [loading, setLoading] = useState4(true);
778
- const [showTokenSetup, setShowTokenSetup] = useState4(false);
779
- const [pendingDelete, setPendingDelete] = useState4(null);
780
- const [deleting, setDeleting] = useState4(false);
781
- const load = useCallback4(async () => {
947
+ const [token, setToken] = useState5(null);
948
+ const [targets, setTargets] = useState5([]);
949
+ const [loading, setLoading] = useState5(true);
950
+ const [showTokenSetup, setShowTokenSetup] = useState5(false);
951
+ const [showCreateForm, setShowCreateForm] = useState5(false);
952
+ const [pendingEdit, setPendingEdit] = useState5(null);
953
+ const [pendingDelete, setPendingDelete] = useState5(null);
954
+ const [deleting, setDeleting] = useState5(false);
955
+ const load = useCallback5(async () => {
782
956
  setLoading(true);
783
957
  try {
784
958
  const [fetchedToken, fetchedTargets] = await Promise.all([
@@ -796,6 +970,25 @@ function DeployTool() {
796
970
  useEffect3(() => {
797
971
  load();
798
972
  }, [load]);
973
+ useEffect3(() => {
974
+ if (document.getElementById("dvfs-styles")) return;
975
+ const style = document.createElement("style");
976
+ style.id = "dvfs-styles";
977
+ style.textContent = `
978
+ @media (max-width: 600px) {
979
+ .dvfs-header { flex-wrap: wrap !important; row-gap: 8px !important; }
980
+ .dvfs-header-actions { width: 100% !important; flex-wrap: wrap !important; justify-content: flex-start !important; }
981
+ .dvfs-grid { grid-template-columns: 1fr !important; }
982
+ .dvfs-card-flex { flex-direction: column !important; }
983
+ .dvfs-deploy-col { width: 100% !important; align-self: auto !important; }
984
+ .dvfs-deploy-col button { border-radius: 3px !important; }
985
+ }
986
+ `;
987
+ document.head.appendChild(style);
988
+ return () => {
989
+ document.getElementById("dvfs-styles")?.remove();
990
+ };
991
+ }, []);
799
992
  useEffect3(() => {
800
993
  const sub = client.listen(TARGETS_QUERY).subscribe(() => {
801
994
  client.fetch(TARGETS_QUERY).then(setTargets).catch((err) => {
@@ -804,7 +997,7 @@ function DeployTool() {
804
997
  });
805
998
  return () => sub.unsubscribe();
806
999
  }, [client]);
807
- const confirmDelete = useCallback4(async () => {
1000
+ const confirmDelete = useCallback5(async () => {
808
1001
  if (!pendingDelete) return;
809
1002
  setDeleting(true);
810
1003
  try {
@@ -819,96 +1012,173 @@ function DeployTool() {
819
1012
  }
820
1013
  }, [client, pendingDelete, toast]);
821
1014
  if (loading) {
822
- return /* @__PURE__ */ jsx5(Card4, { height: "fill", tone: "transparent", children: /* @__PURE__ */ jsx5(Flex5, { align: "center", justify: "center", height: "fill", children: /* @__PURE__ */ jsx5(Spinner4, { muted: true }) }) });
1015
+ return /* @__PURE__ */ jsx6(Card5, { height: "fill", tone: "transparent", children: /* @__PURE__ */ jsx6(Flex6, { align: "center", justify: "center", height: "fill", children: /* @__PURE__ */ jsx6(Spinner4, { muted: true }) }) });
823
1016
  }
824
- if (!token || showTokenSetup) {
825
- return /* @__PURE__ */ jsx5(
826
- TokenSetup,
827
- {
828
- onSaved: () => {
829
- setShowTokenSetup(false);
830
- load();
831
- }
832
- }
833
- );
834
- }
835
- return /* @__PURE__ */ jsxs5(Card4, { height: "fill", tone: "transparent", children: [
836
- /* @__PURE__ */ jsx5(Box4, { padding: 5, children: /* @__PURE__ */ jsxs5(Stack4, { space: 5, children: [
837
- /* @__PURE__ */ jsxs5(Flex5, { align: "center", justify: "space-between", children: [
838
- /* @__PURE__ */ jsxs5(Flex5, { align: "center", gap: 3, children: [
839
- /* @__PURE__ */ jsx5(RocketIcon2, {}),
840
- /* @__PURE__ */ jsx5(Heading2, { size: 2, children: "Deploy" })
841
- ] }),
842
- /* @__PURE__ */ jsxs5(Flex5, { align: "center", gap: 3, children: [
843
- /* @__PURE__ */ jsx5(Badge4, { tone: "positive", mode: "outline", children: "Connected" }),
844
- /* @__PURE__ */ jsx5(
845
- Button4,
1017
+ return /* @__PURE__ */ jsxs6(Card5, { height: "fill", tone: "transparent", children: [
1018
+ /* @__PURE__ */ jsx6(Box4, { padding: 5, children: /* @__PURE__ */ jsxs6(Stack5, { space: 5, children: [
1019
+ /* @__PURE__ */ jsxs6(Flex6, { align: "center", justify: "space-between", className: "dvfs-header", children: [
1020
+ /* @__PURE__ */ jsx6(Heading, { size: 2, children: "Deploy with Vercel" }),
1021
+ /* @__PURE__ */ jsxs6(Flex6, { align: "center", gap: 3, className: "dvfs-header-actions", children: [
1022
+ /* @__PURE__ */ jsx6(
1023
+ Button5,
1024
+ {
1025
+ text: token ? "Token connected" : "Connect API token",
1026
+ mode: "ghost",
1027
+ icon: TokenIcon,
1028
+ fontSize: 1,
1029
+ tone: token ? "positive" : "caution",
1030
+ onClick: () => setShowTokenSetup(true),
1031
+ style: { cursor: "pointer" }
1032
+ }
1033
+ ),
1034
+ /* @__PURE__ */ jsx6(
1035
+ Button5,
846
1036
  {
847
- text: "Change Token",
1037
+ text: "Add target",
848
1038
  mode: "ghost",
849
- icon: TokenIcon2,
1039
+ icon: AddIcon,
850
1040
  fontSize: 1,
851
- onClick: () => setShowTokenSetup(true)
1041
+ onClick: () => setShowCreateForm(true),
1042
+ style: { cursor: "pointer" }
852
1043
  }
853
1044
  )
854
1045
  ] })
855
1046
  ] }),
856
- targets.length === 0 && /* @__PURE__ */ jsx5(Card4, { padding: 5, radius: 2, tone: "transparent", shadow: 1, children: /* @__PURE__ */ jsxs5(Stack4, { space: 3, style: { textAlign: "center" }, children: [
857
- /* @__PURE__ */ jsx5(Text4, { size: 2, weight: "semibold", children: "No deploy targets configured" }),
858
- /* @__PURE__ */ jsxs5(Text4, { size: 1, muted: true, children: [
859
- "Create a ",
860
- /* @__PURE__ */ jsx5("code", { children: "vercel_deploy" }),
861
- " document in the dataset with a Vercel deploy hook URL."
862
- ] })
1047
+ !token && /* @__PURE__ */ jsx6(Card5, { padding: 4, radius: 2, tone: "caution", shadow: 1, children: /* @__PURE__ */ jsxs6(Flex6, { align: "center", justify: "space-between", gap: 4, children: [
1048
+ /* @__PURE__ */ jsxs6(Stack5, { space: 2, children: [
1049
+ /* @__PURE__ */ jsx6(Text5, { size: 1, weight: "semibold", children: "Deploy status is not connected" }),
1050
+ /* @__PURE__ */ jsx6(Text5, { 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." })
1051
+ ] }),
1052
+ /* @__PURE__ */ jsx6(
1053
+ Button5,
1054
+ {
1055
+ text: "Connect",
1056
+ tone: "caution",
1057
+ fontSize: 1,
1058
+ onClick: () => setShowTokenSetup(true),
1059
+ style: { cursor: "pointer", flexShrink: 0 }
1060
+ }
1061
+ )
1062
+ ] }) }),
1063
+ targets.length === 0 && /* @__PURE__ */ jsx6(Card5, { padding: 5, radius: 2, tone: "transparent", shadow: 1, children: /* @__PURE__ */ jsxs6(Stack5, { space: 4, style: { textAlign: "center" }, children: [
1064
+ /* @__PURE__ */ jsx6(Text5, { size: 2, weight: "semibold", children: "No deploy targets configured" }),
1065
+ /* @__PURE__ */ jsxs6(Text5, { size: 1, muted: true, children: [
1066
+ "Add a deploy target using the button above, or create a",
1067
+ " ",
1068
+ /* @__PURE__ */ jsx6("code", { children: "vercel_deploy" }),
1069
+ " document directly in the dataset."
1070
+ ] }),
1071
+ /* @__PURE__ */ jsx6(Flex6, { justify: "center", children: /* @__PURE__ */ jsx6(
1072
+ Button5,
1073
+ {
1074
+ text: "Add deploy target",
1075
+ tone: "primary",
1076
+ icon: AddIcon,
1077
+ onClick: () => setShowCreateForm(true),
1078
+ style: { cursor: "pointer" }
1079
+ }
1080
+ ) })
863
1081
  ] }) }),
864
- targets.length > 0 && /* @__PURE__ */ jsx5("div", { style: {
1082
+ targets.length > 0 && /* @__PURE__ */ jsx6("div", { className: "dvfs-grid", style: {
865
1083
  display: "grid",
866
- gridTemplateColumns: "repeat(auto-fill, minmax(380px, 1fr))",
867
- gap: "12px"
868
- }, children: targets.map((target) => /* @__PURE__ */ jsx5(
1084
+ gridTemplateColumns: "repeat(auto-fill, minmax(540px, 1fr))",
1085
+ gap: "16px",
1086
+ alignItems: "start"
1087
+ }, children: targets.map((target) => /* @__PURE__ */ jsx6(
869
1088
  DeployItem,
870
1089
  {
871
1090
  target,
872
- token,
873
- onDelete: setPendingDelete
1091
+ token: token ?? "",
1092
+ onDelete: setPendingDelete,
1093
+ onEdit: setPendingEdit
874
1094
  },
875
1095
  target._id
876
1096
  )) })
877
1097
  ] }) }),
878
- pendingDelete && /* @__PURE__ */ jsx5(
879
- Dialog2,
1098
+ /* @__PURE__ */ jsx6(
1099
+ Box4,
1100
+ {
1101
+ style: {
1102
+ position: "fixed",
1103
+ bottom: 12,
1104
+ right: 16,
1105
+ opacity: 0.25,
1106
+ pointerEvents: "none",
1107
+ userSelect: "none"
1108
+ },
1109
+ children: /* @__PURE__ */ jsxs6(Text5, { size: 0, muted: true, children: [
1110
+ "v",
1111
+ VERSION
1112
+ ] })
1113
+ }
1114
+ ),
1115
+ showTokenSetup && /* @__PURE__ */ jsx6(
1116
+ TokenSetup,
1117
+ {
1118
+ onSaved: () => {
1119
+ setShowTokenSetup(false);
1120
+ load();
1121
+ },
1122
+ onCancel: token ? () => setShowTokenSetup(false) : void 0
1123
+ }
1124
+ ),
1125
+ showCreateForm && /* @__PURE__ */ jsx6(
1126
+ DeployTargetForm,
1127
+ {
1128
+ onSaved: () => {
1129
+ setShowCreateForm(false);
1130
+ toast.push({ status: "success", title: "Deploy target added" });
1131
+ },
1132
+ onClose: () => setShowCreateForm(false)
1133
+ }
1134
+ ),
1135
+ pendingEdit && /* @__PURE__ */ jsx6(
1136
+ DeployTargetForm,
1137
+ {
1138
+ initial: pendingEdit,
1139
+ onSaved: () => {
1140
+ setPendingEdit(null);
1141
+ toast.push({ status: "success", title: "Deploy target updated" });
1142
+ },
1143
+ onClose: () => setPendingEdit(null)
1144
+ }
1145
+ ),
1146
+ pendingDelete && /* @__PURE__ */ jsx6(
1147
+ Dialog4,
880
1148
  {
881
1149
  header: "Delete deploy target?",
882
1150
  id: "confirm-delete",
883
1151
  onClose: () => setPendingDelete(null),
884
1152
  width: 1,
885
- footer: /* @__PURE__ */ jsxs5(Flex5, { padding: 3, gap: 2, justify: "flex-end", children: [
886
- /* @__PURE__ */ jsx5(
887
- Button4,
1153
+ footer: /* @__PURE__ */ jsxs6(Flex6, { padding: 3, gap: 2, justify: "flex-end", children: [
1154
+ /* @__PURE__ */ jsx6(
1155
+ Button5,
888
1156
  {
889
1157
  text: "Cancel",
890
1158
  mode: "ghost",
891
- onClick: () => setPendingDelete(null)
1159
+ onClick: () => setPendingDelete(null),
1160
+ style: { cursor: "pointer" }
892
1161
  }
893
1162
  ),
894
- /* @__PURE__ */ jsx5(
895
- Button4,
1163
+ /* @__PURE__ */ jsx6(
1164
+ Button5,
896
1165
  {
897
1166
  text: "Delete",
898
1167
  tone: "critical",
899
1168
  icon: TrashIcon2,
900
1169
  loading: deleting,
901
1170
  disabled: deleting,
902
- onClick: confirmDelete
1171
+ onClick: confirmDelete,
1172
+ style: { cursor: "pointer" }
903
1173
  }
904
1174
  )
905
1175
  ] }),
906
- children: /* @__PURE__ */ jsx5(Box4, { padding: 4, children: /* @__PURE__ */ jsxs5(Stack4, { space: 3, children: [
907
- /* @__PURE__ */ jsxs5(Flex5, { align: "center", gap: 2, children: [
908
- /* @__PURE__ */ jsx5(WarningOutlineIcon2, {}),
909
- /* @__PURE__ */ jsx5(Text4, { size: 2, weight: "semibold", children: pendingDelete.name })
1176
+ children: /* @__PURE__ */ jsx6(Box4, { padding: 4, children: /* @__PURE__ */ jsxs6(Stack5, { space: 3, children: [
1177
+ /* @__PURE__ */ jsxs6(Flex6, { align: "center", gap: 2, children: [
1178
+ /* @__PURE__ */ jsx6(WarningOutlineIcon2, {}),
1179
+ /* @__PURE__ */ jsx6(Text5, { size: 2, weight: "semibold", children: pendingDelete.name })
910
1180
  ] }),
911
- /* @__PURE__ */ jsx5(Text4, { size: 1, muted: true, children: "This removes the deploy target from the dataset. The Vercel deploy hook itself is not affected." })
1181
+ /* @__PURE__ */ jsx6(Text5, { size: 1, muted: true, children: "This removes the deploy target from the dataset. The Vercel deploy hook itself is not affected." })
912
1182
  ] }) })
913
1183
  }
914
1184
  )
@@ -917,12 +1187,12 @@ function DeployTool() {
917
1187
 
918
1188
  // src/schema/vercelDeploy.ts
919
1189
  import { defineField, defineType } from "sanity";
920
- import { RocketIcon as RocketIcon3 } from "@sanity/icons";
1190
+ import { RocketIcon } from "@sanity/icons";
921
1191
  var vercelDeploySchema = defineType({
922
1192
  name: "vercel_deploy",
923
1193
  title: "Deploy Target",
924
1194
  type: "document",
925
- icon: RocketIcon3,
1195
+ icon: RocketIcon,
926
1196
  fields: [
927
1197
  defineField({
928
1198
  name: "name",
@@ -975,7 +1245,7 @@ var vercelDeploy = definePlugin((options) => {
975
1245
  {
976
1246
  name: config.name ?? "vercel-deploy",
977
1247
  title: config.title ?? "Deploy",
978
- icon: config.icon ?? RocketIcon4,
1248
+ icon: config.icon ?? RocketIcon2,
979
1249
  component: DeployTool
980
1250
  }
981
1251
  ]