@liiift-studio/deploy-vercel-from-sanity 0.1.7 → 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: 5, radius: 2, shadow: 1, tone: "default", children: /* @__PURE__ */ jsxs3(Flex3, { gap: 4, align: "stretch", children: [
420
- /* @__PURE__ */ jsxs3(Stack2, { space: 3, 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,100 +545,120 @@ 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
- )
556
- ] })
557
- ] }),
558
- commitMsg && /* @__PURE__ */ jsx3(
559
- Text2,
560
- {
561
- size: 0,
562
- muted: true,
563
- style: {
564
- overflow: "hidden",
565
- textOverflow: "ellipsis",
566
- whiteSpace: "nowrap"
567
- },
568
- children: commitMsg
569
- }
570
- ),
571
- isError && /* @__PURE__ */ jsxs3(Stack2, { space: 2, children: [
572
- /* @__PURE__ */ jsx3(
573
- Button2,
574
- {
575
- text: showErrorLogs ? "Hide error details" : "Show error details",
576
- mode: "ghost",
577
- tone: "critical",
578
- icon: WarningOutlineIcon,
579
- fontSize: 1,
580
- padding: 2,
581
- onClick: toggleErrorLogs,
582
- style: { alignSelf: "flex-start" }
583
- }
584
- ),
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: [
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: [
607
566
  /* @__PURE__ */ jsx3(
608
- Box2,
567
+ Button2,
609
568
  {
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
- ))
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" }
626
577
  }
627
578
  ),
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: [
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: [
636
587
  /* @__PURE__ */ jsx3(LaunchIcon2, {}),
637
588
  /* @__PURE__ */ jsx3(Text2, { size: 1, children: "View full logs in Vercel" })
638
- ] })
639
- }
640
- )
641
- ] })
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 }) })
642
601
  ] })
643
- ] }),
644
- deployError && /* @__PURE__ */ jsx3(Card2, { tone: "critical", padding: 2, radius: 2, children: /* @__PURE__ */ jsx3(Text2, { size: 1, children: deployError }) })
602
+ ] })
603
+ ] }),
604
+ /* @__PURE__ */ jsxs3(Box2, { children: [
605
+ /* @__PURE__ */ jsx3(
606
+ Button2,
607
+ {
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" }
615
+ }
616
+ ),
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
+ ] }) })
645
662
  ] })
646
663
  ] }),
647
664
  /* @__PURE__ */ jsxs3(
@@ -649,6 +666,7 @@ function DeployItem({ target, token, onDelete }) {
649
666
  {
650
667
  direction: "column",
651
668
  gap: 2,
669
+ className: "dvfs-deploy-col",
652
670
  style: { flexShrink: 0, alignSelf: "stretch" },
653
671
  children: [
654
672
  isActiveState(latest?.state) && /* @__PURE__ */ jsx3(
@@ -659,7 +677,8 @@ function DeployItem({ target, token, onDelete }) {
659
677
  tone: "critical",
660
678
  loading: canceling,
661
679
  disabled: canceling,
662
- onClick: cancel
680
+ onClick: cancel,
681
+ style: { cursor: "pointer" }
663
682
  }
664
683
  ),
665
684
  /* @__PURE__ */ jsx3(
@@ -667,11 +686,16 @@ function DeployItem({ target, token, onDelete }) {
667
686
  {
668
687
  text: "Deploy",
669
688
  tone: "primary",
670
- icon: RocketIcon,
671
689
  loading: triggering,
672
690
  disabled: isActive,
673
691
  onClick: deploy,
674
- style: { flex: 1 }
692
+ style: {
693
+ flex: 1,
694
+ borderRadius: 0,
695
+ borderTopRightRadius: 3,
696
+ borderBottomRightRadius: 3,
697
+ cursor: "pointer"
698
+ }
675
699
  }
676
700
  )
677
701
  ]
@@ -692,19 +716,11 @@ function DeployItem({ target, token, onDelete }) {
692
716
  // src/components/TokenSetup.tsx
693
717
  import { useState as useState3, useCallback as useCallback3 } from "react";
694
718
  import { useClient } from "sanity";
695
- import {
696
- Card as Card3,
697
- Stack as Stack3,
698
- Text as Text3,
699
- Heading,
700
- TextInput,
701
- Button as Button3,
702
- Flex as Flex4
703
- } from "@sanity/ui";
704
- 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";
705
721
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
706
722
  var TOKEN_DOC_ID = "config.vercelDeploy";
707
- function TokenSetup({ onSaved }) {
723
+ function TokenSetup({ onSaved, onCancel }) {
708
724
  const client = useClient({ apiVersion: "2025-01-01" });
709
725
  const [token, setToken] = useState3("");
710
726
  const [saving, setSaving] = useState3(false);
@@ -726,68 +742,217 @@ function TokenSetup({ onSaved }) {
726
742
  setSaving(false);
727
743
  }
728
744
  }, [client, token, onSaved]);
729
- 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: [
730
- /* @__PURE__ */ jsxs4(Flex4, { align: "center", gap: 3, children: [
731
- /* @__PURE__ */ jsx4(Text3, { size: 3, children: /* @__PURE__ */ jsx4(TokenIcon, {}) }),
732
- /* @__PURE__ */ jsx4(Heading, { size: 2, children: "Connect to Vercel" })
733
- ] }),
734
- /* @__PURE__ */ jsxs4(Stack3, { space: 3, children: [
735
- /* @__PURE__ */ jsxs4(Text3, { size: 1, muted: true, children: [
736
- "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",
737
- " ",
738
- /* @__PURE__ */ jsx4("code", { children: "secrets.*" }),
739
- " 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
+ )
740
766
  ] }),
741
- /* @__PURE__ */ jsxs4(Text3, { size: 1, muted: true, children: [
742
- "Create a token at",
743
- " ",
744
- /* @__PURE__ */ jsx4("strong", { children: "vercel.com \u2192 Settings \u2192 Tokens" }),
745
- ". Choose ",
746
- /* @__PURE__ */ jsx4("strong", { children: "Full Account" }),
747
- " 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 }) })
748
792
  ] })
749
- ] }),
750
- /* @__PURE__ */ jsxs4(Stack3, { space: 3, children: [
751
- /* @__PURE__ */ jsx4(Text3, { size: 1, weight: "semibold", children: "Vercel API Token" }),
752
- /* @__PURE__ */ jsx4(
753
- TextInput,
754
- {
755
- value: token,
756
- onChange: (e) => setToken(e.target.value),
757
- placeholder: "xxxxxxxxxxxxxxxxxxxxxxxx",
758
- type: "password"
759
- }
760
- )
761
- ] }),
762
- error && /* @__PURE__ */ jsx4(Card3, { tone: "critical", padding: 3, radius: 2, children: /* @__PURE__ */ jsx4(Text3, { size: 1, children: error }) }),
763
- /* @__PURE__ */ jsx4(
764
- Button3,
765
- {
766
- text: "Save and connect",
767
- tone: "primary",
768
- icon: CheckmarkCircleIcon,
769
- loading: saving,
770
- disabled: !token.trim() || saving,
771
- 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 });
772
841
  }
773
- )
774
- ] }) }) }) });
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
+ );
775
935
  }
776
936
 
937
+ // src/version.ts
938
+ var VERSION = "0.2.0";
939
+
777
940
  // src/components/DeployTool.tsx
778
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
941
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
779
942
  var TOKEN_QUERY = `*[_id == "config.vercelDeploy"][0].accessToken`;
780
943
  var TARGETS_QUERY = `*[_type == "vercel_deploy"] | order(_createdAt asc)`;
781
944
  function DeployTool() {
782
- const client = useClient2({ apiVersion: "2025-01-01" });
945
+ const client = useClient3({ apiVersion: "2025-01-01" });
783
946
  const toast = useToast();
784
- const [token, setToken] = useState4(null);
785
- const [targets, setTargets] = useState4([]);
786
- const [loading, setLoading] = useState4(true);
787
- const [showTokenSetup, setShowTokenSetup] = useState4(false);
788
- const [pendingDelete, setPendingDelete] = useState4(null);
789
- const [deleting, setDeleting] = useState4(false);
790
- 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 () => {
791
956
  setLoading(true);
792
957
  try {
793
958
  const [fetchedToken, fetchedTargets] = await Promise.all([
@@ -805,6 +970,25 @@ function DeployTool() {
805
970
  useEffect3(() => {
806
971
  load();
807
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
+ }, []);
808
992
  useEffect3(() => {
809
993
  const sub = client.listen(TARGETS_QUERY).subscribe(() => {
810
994
  client.fetch(TARGETS_QUERY).then(setTargets).catch((err) => {
@@ -813,7 +997,7 @@ function DeployTool() {
813
997
  });
814
998
  return () => sub.unsubscribe();
815
999
  }, [client]);
816
- const confirmDelete = useCallback4(async () => {
1000
+ const confirmDelete = useCallback5(async () => {
817
1001
  if (!pendingDelete) return;
818
1002
  setDeleting(true);
819
1003
  try {
@@ -828,96 +1012,173 @@ function DeployTool() {
828
1012
  }
829
1013
  }, [client, pendingDelete, toast]);
830
1014
  if (loading) {
831
- 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 }) }) });
832
1016
  }
833
- if (!token || showTokenSetup) {
834
- return /* @__PURE__ */ jsx5(
835
- TokenSetup,
836
- {
837
- onSaved: () => {
838
- setShowTokenSetup(false);
839
- load();
840
- }
841
- }
842
- );
843
- }
844
- return /* @__PURE__ */ jsxs5(Card4, { height: "fill", tone: "transparent", children: [
845
- /* @__PURE__ */ jsx5(Box4, { padding: 5, children: /* @__PURE__ */ jsxs5(Stack4, { space: 5, children: [
846
- /* @__PURE__ */ jsxs5(Flex5, { align: "center", justify: "space-between", children: [
847
- /* @__PURE__ */ jsxs5(Flex5, { align: "center", gap: 3, children: [
848
- /* @__PURE__ */ jsx5(RocketIcon2, {}),
849
- /* @__PURE__ */ jsx5(Heading2, { size: 2, children: "Deploy" })
850
- ] }),
851
- /* @__PURE__ */ jsxs5(Flex5, { align: "center", gap: 3, children: [
852
- /* @__PURE__ */ jsx5(Badge4, { tone: "positive", mode: "outline", children: "Connected" }),
853
- /* @__PURE__ */ jsx5(
854
- 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,
855
1036
  {
856
- text: "Change Token",
1037
+ text: "Add target",
857
1038
  mode: "ghost",
858
- icon: TokenIcon2,
1039
+ icon: AddIcon,
859
1040
  fontSize: 1,
860
- onClick: () => setShowTokenSetup(true)
1041
+ onClick: () => setShowCreateForm(true),
1042
+ style: { cursor: "pointer" }
861
1043
  }
862
1044
  )
863
1045
  ] })
864
1046
  ] }),
865
- targets.length === 0 && /* @__PURE__ */ jsx5(Card4, { padding: 5, radius: 2, tone: "transparent", shadow: 1, children: /* @__PURE__ */ jsxs5(Stack4, { space: 3, style: { textAlign: "center" }, children: [
866
- /* @__PURE__ */ jsx5(Text4, { size: 2, weight: "semibold", children: "No deploy targets configured" }),
867
- /* @__PURE__ */ jsxs5(Text4, { size: 1, muted: true, children: [
868
- "Create a ",
869
- /* @__PURE__ */ jsx5("code", { children: "vercel_deploy" }),
870
- " document in the dataset with a Vercel deploy hook URL."
871
- ] })
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
+ ) })
872
1081
  ] }) }),
873
- targets.length > 0 && /* @__PURE__ */ jsx5("div", { style: {
1082
+ targets.length > 0 && /* @__PURE__ */ jsx6("div", { className: "dvfs-grid", style: {
874
1083
  display: "grid",
875
1084
  gridTemplateColumns: "repeat(auto-fill, minmax(540px, 1fr))",
876
- gap: "16px"
877
- }, children: targets.map((target) => /* @__PURE__ */ jsx5(
1085
+ gap: "16px",
1086
+ alignItems: "start"
1087
+ }, children: targets.map((target) => /* @__PURE__ */ jsx6(
878
1088
  DeployItem,
879
1089
  {
880
1090
  target,
881
- token,
882
- onDelete: setPendingDelete
1091
+ token: token ?? "",
1092
+ onDelete: setPendingDelete,
1093
+ onEdit: setPendingEdit
883
1094
  },
884
1095
  target._id
885
1096
  )) })
886
1097
  ] }) }),
887
- pendingDelete && /* @__PURE__ */ jsx5(
888
- 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,
889
1148
  {
890
1149
  header: "Delete deploy target?",
891
1150
  id: "confirm-delete",
892
1151
  onClose: () => setPendingDelete(null),
893
1152
  width: 1,
894
- footer: /* @__PURE__ */ jsxs5(Flex5, { padding: 3, gap: 2, justify: "flex-end", children: [
895
- /* @__PURE__ */ jsx5(
896
- Button4,
1153
+ footer: /* @__PURE__ */ jsxs6(Flex6, { padding: 3, gap: 2, justify: "flex-end", children: [
1154
+ /* @__PURE__ */ jsx6(
1155
+ Button5,
897
1156
  {
898
1157
  text: "Cancel",
899
1158
  mode: "ghost",
900
- onClick: () => setPendingDelete(null)
1159
+ onClick: () => setPendingDelete(null),
1160
+ style: { cursor: "pointer" }
901
1161
  }
902
1162
  ),
903
- /* @__PURE__ */ jsx5(
904
- Button4,
1163
+ /* @__PURE__ */ jsx6(
1164
+ Button5,
905
1165
  {
906
1166
  text: "Delete",
907
1167
  tone: "critical",
908
1168
  icon: TrashIcon2,
909
1169
  loading: deleting,
910
1170
  disabled: deleting,
911
- onClick: confirmDelete
1171
+ onClick: confirmDelete,
1172
+ style: { cursor: "pointer" }
912
1173
  }
913
1174
  )
914
1175
  ] }),
915
- children: /* @__PURE__ */ jsx5(Box4, { padding: 4, children: /* @__PURE__ */ jsxs5(Stack4, { space: 3, children: [
916
- /* @__PURE__ */ jsxs5(Flex5, { align: "center", gap: 2, children: [
917
- /* @__PURE__ */ jsx5(WarningOutlineIcon2, {}),
918
- /* @__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 })
919
1180
  ] }),
920
- /* @__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." })
921
1182
  ] }) })
922
1183
  }
923
1184
  )
@@ -926,12 +1187,12 @@ function DeployTool() {
926
1187
 
927
1188
  // src/schema/vercelDeploy.ts
928
1189
  import { defineField, defineType } from "sanity";
929
- import { RocketIcon as RocketIcon3 } from "@sanity/icons";
1190
+ import { RocketIcon } from "@sanity/icons";
930
1191
  var vercelDeploySchema = defineType({
931
1192
  name: "vercel_deploy",
932
1193
  title: "Deploy Target",
933
1194
  type: "document",
934
- icon: RocketIcon3,
1195
+ icon: RocketIcon,
935
1196
  fields: [
936
1197
  defineField({
937
1198
  name: "name",
@@ -984,7 +1245,7 @@ var vercelDeploy = definePlugin((options) => {
984
1245
  {
985
1246
  name: config.name ?? "vercel-deploy",
986
1247
  title: config.title ?? "Deploy",
987
- icon: config.icon ?? RocketIcon4,
1248
+ icon: config.icon ?? RocketIcon2,
988
1249
  component: DeployTool
989
1250
  }
990
1251
  ]