@liiift-studio/deploy-vercel-from-sanity 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -35,7 +35,6 @@ var import_icons5 = require("@sanity/icons");
35
35
 
36
36
  // src/components/DeployItem.tsx
37
37
  var import_react2 = require("react");
38
- var import_react_dom = require("react-dom");
39
38
  var import_ui3 = require("@sanity/ui");
40
39
  var import_icons2 = require("@sanity/icons");
41
40
 
@@ -289,6 +288,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
289
288
  const [deployments, setDeployments] = (0, import_react2.useState)([]);
290
289
  const [loadingInitial, setLoadingInitial] = (0, import_react2.useState)(true);
291
290
  const [triggering, setTriggering] = (0, import_react2.useState)(false);
291
+ const [clicked, setClicked] = (0, import_react2.useState)(false);
292
292
  const [canceling, setCanceling] = (0, import_react2.useState)(false);
293
293
  const [deployError, setDeployError] = (0, import_react2.useState)(null);
294
294
  const [showHistory, setShowHistory] = (0, import_react2.useState)(false);
@@ -321,6 +321,9 @@ function DeployItem({ target, token, onDelete, onEdit }) {
321
321
  (0, import_react2.useEffect)(() => {
322
322
  if (triggering && latest && latest.state !== void 0) setTriggering(false);
323
323
  }, [triggering, latest]);
324
+ (0, import_react2.useEffect)(() => {
325
+ if (triggering) setClicked(false);
326
+ }, [triggering]);
324
327
  const prevStateRef = (0, import_react2.useRef)(void 0);
325
328
  (0, import_react2.useEffect)(() => {
326
329
  const current = latest?.state;
@@ -357,16 +360,20 @@ function DeployItem({ target, token, onDelete, onEdit }) {
357
360
  if (timerRef.current) clearInterval(timerRef.current);
358
361
  };
359
362
  }, [isActive, latest?.created]);
360
- const deploy = (0, import_react2.useCallback)(async () => {
363
+ const deploy = (0, import_react2.useCallback)(() => {
361
364
  setDeployError(null);
362
- (0, import_react_dom.flushSync)(() => setTriggering(true));
363
- try {
364
- await triggerDeploy(target.url);
365
- setTimeout(fetchDeployments, 2e3);
366
- } catch (err) {
367
- setTriggering(false);
368
- setDeployError(err instanceof Error ? err.message : "Deploy failed");
369
- }
365
+ setClicked(true);
366
+ setTriggering(true);
367
+ void (async () => {
368
+ try {
369
+ await triggerDeploy(target.url);
370
+ setTimeout(fetchDeployments, 2e3);
371
+ } catch (err) {
372
+ setTriggering(false);
373
+ setClicked(false);
374
+ setDeployError(err instanceof Error ? err.message : "Deploy failed");
375
+ }
376
+ })();
370
377
  }, [target.url, fetchDeployments]);
371
378
  const cancel = (0, import_react2.useCallback)(async () => {
372
379
  if (!latest?.uid) return;
@@ -433,7 +440,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
433
440
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", style: { marginLeft: "-0.1em", opacity: 0.5 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M5 3.25a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0zm0 2.122a2.25 2.25 0 1 0-1.5 0v.878A2.25 2.25 0 0 0 5.75 8.5h1.5v2.128a2.251 2.251 0 1 0 1.5 0V8.5h1.5a2.25 2.25 0 0 0 2.25-2.25v-.878a2.25 2.25 0 1 0-1.5 0v.878a.75.75 0 0 1-.75.75h-4.5A.75.75 0 0 1 5 6.25v-.878zm3.75 7.378a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0zm3-8.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0z" }) })
434
441
  ] }) }),
435
442
  token && !loadingInitial && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
436
- triggering ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui3.Badge, { tone: "caution", padding: 2, children: "Triggering\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatusBadge, { state: latest?.state, showSpinner: true }),
443
+ triggering ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui3.Badge, { tone: "caution", padding: 2, children: "Triggering\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatusBadge, { state: latest?.state }),
437
444
  isActiveState(latest?.state) && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
438
445
  import_ui3.Button,
439
446
  {
@@ -446,7 +453,10 @@ function DeployItem({ target, token, onDelete, onEdit }) {
446
453
  style: { cursor: "pointer" }
447
454
  }
448
455
  ),
449
- isActive && elapsed > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui3.Text, { size: 1, muted: true, children: formatDuration(elapsed) }) : !isActive && deployedAt ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui3.Text, { size: 1, muted: true, children: deployedAt }) : null
456
+ isActive && elapsed > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui3.Flex, { align: "center", gap: 1, children: [
457
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui3.Spinner, { muted: true, style: { marginLeft: 4 } }),
458
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui3.Text, { size: 1, muted: true, children: formatDuration(elapsed) })
459
+ ] }) : !isActive && deployedAt ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui3.Text, { size: 1, muted: true, children: deployedAt }) : null
450
460
  ] })
451
461
  ] }),
452
462
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -545,8 +555,9 @@ function DeployItem({ target, token, onDelete, onEdit }) {
545
555
  creator
546
556
  ] }),
547
557
  latest?.state === "READY" && latest.ready && latest.created && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui3.Text, { size: 1, muted: true, children: [
548
- "\xB7 ",
549
- formatDuration(Math.floor((latest.ready - latest.created) / 1e3))
558
+ "Took ",
559
+ formatDuration(Math.floor((latest.ready - latest.created) / 1e3)),
560
+ " to build"
550
561
  ] }),
551
562
  latest?.url && latest.state === "READY" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
552
563
  import_ui3.Tooltip,
@@ -689,7 +700,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
689
700
  text: "Deploy",
690
701
  tone: "primary",
691
702
  loading: triggering,
692
- disabled: isActive,
703
+ disabled: isActive || clicked,
693
704
  onClick: deploy,
694
705
  style: {
695
706
  flex: 1,
package/dist/index.mjs CHANGED
@@ -21,7 +21,6 @@ import { TokenIcon, TrashIcon as TrashIcon2, WarningOutlineIcon as WarningOutlin
21
21
 
22
22
  // src/components/DeployItem.tsx
23
23
  import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2, useRef } from "react";
24
- import { flushSync } from "react-dom";
25
24
  import {
26
25
  Card as Card2,
27
26
  Box as Box2,
@@ -311,6 +310,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
311
310
  const [deployments, setDeployments] = useState2([]);
312
311
  const [loadingInitial, setLoadingInitial] = useState2(true);
313
312
  const [triggering, setTriggering] = useState2(false);
313
+ const [clicked, setClicked] = useState2(false);
314
314
  const [canceling, setCanceling] = useState2(false);
315
315
  const [deployError, setDeployError] = useState2(null);
316
316
  const [showHistory, setShowHistory] = useState2(false);
@@ -343,6 +343,9 @@ function DeployItem({ target, token, onDelete, onEdit }) {
343
343
  useEffect2(() => {
344
344
  if (triggering && latest && latest.state !== void 0) setTriggering(false);
345
345
  }, [triggering, latest]);
346
+ useEffect2(() => {
347
+ if (triggering) setClicked(false);
348
+ }, [triggering]);
346
349
  const prevStateRef = useRef(void 0);
347
350
  useEffect2(() => {
348
351
  const current = latest?.state;
@@ -379,16 +382,20 @@ function DeployItem({ target, token, onDelete, onEdit }) {
379
382
  if (timerRef.current) clearInterval(timerRef.current);
380
383
  };
381
384
  }, [isActive, latest?.created]);
382
- const deploy = useCallback2(async () => {
385
+ const deploy = useCallback2(() => {
383
386
  setDeployError(null);
384
- flushSync(() => setTriggering(true));
385
- try {
386
- await triggerDeploy(target.url);
387
- setTimeout(fetchDeployments, 2e3);
388
- } catch (err) {
389
- setTriggering(false);
390
- setDeployError(err instanceof Error ? err.message : "Deploy failed");
391
- }
387
+ setClicked(true);
388
+ setTriggering(true);
389
+ void (async () => {
390
+ try {
391
+ await triggerDeploy(target.url);
392
+ setTimeout(fetchDeployments, 2e3);
393
+ } catch (err) {
394
+ setTriggering(false);
395
+ setClicked(false);
396
+ setDeployError(err instanceof Error ? err.message : "Deploy failed");
397
+ }
398
+ })();
392
399
  }, [target.url, fetchDeployments]);
393
400
  const cancel = useCallback2(async () => {
394
401
  if (!latest?.uid) return;
@@ -455,7 +462,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
455
462
  /* @__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" }) })
456
463
  ] }) }),
457
464
  token && !loadingInitial && /* @__PURE__ */ jsxs3(Fragment, { children: [
458
- triggering ? /* @__PURE__ */ jsx3(Badge3, { tone: "caution", padding: 2, children: "Triggering\u2026" }) : /* @__PURE__ */ jsx3(StatusBadge, { state: latest?.state, showSpinner: true }),
465
+ triggering ? /* @__PURE__ */ jsx3(Badge3, { tone: "caution", padding: 2, children: "Triggering\u2026" }) : /* @__PURE__ */ jsx3(StatusBadge, { state: latest?.state }),
459
466
  isActiveState(latest?.state) && /* @__PURE__ */ jsx3(
460
467
  Button2,
461
468
  {
@@ -468,7 +475,10 @@ function DeployItem({ target, token, onDelete, onEdit }) {
468
475
  style: { cursor: "pointer" }
469
476
  }
470
477
  ),
471
- isActive && elapsed > 0 ? /* @__PURE__ */ jsx3(Text2, { size: 1, muted: true, children: formatDuration(elapsed) }) : !isActive && deployedAt ? /* @__PURE__ */ jsx3(Text2, { size: 1, muted: true, children: deployedAt }) : null
478
+ isActive && elapsed > 0 ? /* @__PURE__ */ jsxs3(Flex3, { align: "center", gap: 1, children: [
479
+ /* @__PURE__ */ jsx3(Spinner3, { muted: true, style: { marginLeft: 4 } }),
480
+ /* @__PURE__ */ jsx3(Text2, { size: 1, muted: true, children: formatDuration(elapsed) })
481
+ ] }) : !isActive && deployedAt ? /* @__PURE__ */ jsx3(Text2, { size: 1, muted: true, children: deployedAt }) : null
472
482
  ] })
473
483
  ] }),
474
484
  /* @__PURE__ */ jsx3(
@@ -567,8 +577,9 @@ function DeployItem({ target, token, onDelete, onEdit }) {
567
577
  creator
568
578
  ] }),
569
579
  latest?.state === "READY" && latest.ready && latest.created && /* @__PURE__ */ jsxs3(Text2, { size: 1, muted: true, children: [
570
- "\xB7 ",
571
- formatDuration(Math.floor((latest.ready - latest.created) / 1e3))
580
+ "Took ",
581
+ formatDuration(Math.floor((latest.ready - latest.created) / 1e3)),
582
+ " to build"
572
583
  ] }),
573
584
  latest?.url && latest.state === "READY" && /* @__PURE__ */ jsx3(Fragment, { children: /* @__PURE__ */ jsx3(
574
585
  Tooltip,
@@ -711,7 +722,7 @@ function DeployItem({ target, token, onDelete, onEdit }) {
711
722
  text: "Deploy",
712
723
  tone: "primary",
713
724
  loading: triggering,
714
- disabled: isActive,
725
+ disabled: isActive || clicked,
715
726
  onClick: deploy,
716
727
  style: {
717
728
  flex: 1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liiift-studio/deploy-vercel-from-sanity",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Sanity Studio plugin — trigger and monitor Vercel deployments with full status, history, and build logs. Supports v3, v4, and v5.",
5
5
  "license": "MIT",
6
6
  "author": "Liiift Studio",
@@ -1,6 +1,5 @@
1
1
  // Per-deploy-target card — shows status, build timer, history, cancel, deploy, copy URL, and error logs
2
2
  import { useState, useEffect, useCallback, useRef } from 'react'
3
- import { flushSync } from 'react-dom'
4
3
  import {
5
4
  Card, Box, Stack, Flex, Text, Button, Tooltip, Badge, Spinner,
6
5
  MenuButton, Menu, MenuItem, Code, useToast,
@@ -32,6 +31,7 @@ export function DeployItem({ target, token, onDelete, onEdit }: DeployItemProps)
32
31
  const [deployments, setDeployments] = useState<VercelDeployment[]>([])
33
32
  const [loadingInitial, setLoadingInitial] = useState(true)
34
33
  const [triggering, setTriggering] = useState(false)
34
+ const [clicked, setClicked] = useState(false)
35
35
  const [canceling, setCanceling] = useState(false)
36
36
  const [deployError, setDeployError] = useState<string | null>(null)
37
37
  const [showHistory, setShowHistory] = useState(false)
@@ -71,6 +71,11 @@ export function DeployItem({ target, token, onDelete, onEdit }: DeployItemProps)
71
71
  if (triggering && latest && latest.state !== undefined) setTriggering(false)
72
72
  }, [triggering, latest])
73
73
 
74
+ // Clear clicked once triggering has propagated — loading animation takes over
75
+ useEffect(() => {
76
+ if (triggering) setClicked(false)
77
+ }, [triggering])
78
+
74
79
  // ── Deploy-complete toast ─────────────────────────────────────────────────
75
80
  const prevStateRef = useRef<string | undefined>(undefined)
76
81
  useEffect(() => {
@@ -111,16 +116,20 @@ export function DeployItem({ target, token, onDelete, onEdit }: DeployItemProps)
111
116
  }, [isActive, latest?.created])
112
117
 
113
118
  // ── Actions ───────────────────────────────────────────────────────────────
114
- const deploy = useCallback(async () => {
119
+ const deploy = useCallback(() => {
115
120
  setDeployError(null)
116
- flushSync(() => setTriggering(true))
117
- try {
118
- await triggerDeploy(target.url)
119
- setTimeout(fetchDeployments, 2000)
120
- } catch (err) {
121
- setTriggering(false)
122
- setDeployError(err instanceof Error ? err.message : 'Deploy failed')
123
- }
121
+ setClicked(true)
122
+ setTriggering(true)
123
+ void (async () => {
124
+ try {
125
+ await triggerDeploy(target.url)
126
+ setTimeout(fetchDeployments, 2000)
127
+ } catch (err) {
128
+ setTriggering(false)
129
+ setClicked(false)
130
+ setDeployError(err instanceof Error ? err.message : 'Deploy failed')
131
+ }
132
+ })()
124
133
  }, [target.url, fetchDeployments])
125
134
 
126
135
  const cancel = useCallback(async () => {
@@ -217,7 +226,7 @@ export function DeployItem({ target, token, onDelete, onEdit }: DeployItemProps)
217
226
  {triggering ? (
218
227
  <Badge tone="caution" padding={2}>Triggering…</Badge>
219
228
  ) : (
220
- <StatusBadge state={latest?.state} showSpinner />
229
+ <StatusBadge state={latest?.state} />
221
230
  )}
222
231
  {isActiveState(latest?.state) && (
223
232
  <Button
@@ -231,7 +240,10 @@ export function DeployItem({ target, token, onDelete, onEdit }: DeployItemProps)
231
240
  />
232
241
  )}
233
242
  {isActive && elapsed > 0 ? (
234
- <Text size={1} muted>{formatDuration(elapsed)}</Text>
243
+ <Flex align="center" gap={1}>
244
+ <Spinner muted style={{ marginLeft: 4 }} />
245
+ <Text size={1} muted>{formatDuration(elapsed)}</Text>
246
+ </Flex>
235
247
  ) : (!isActive && deployedAt) ? (
236
248
  <Text size={1} muted>{deployedAt}</Text>
237
249
  ) : null}
@@ -354,7 +366,7 @@ export function DeployItem({ target, token, onDelete, onEdit }: DeployItemProps)
354
366
 
355
367
  {/* Build duration — only shown when READY and ready timestamp is available */}
356
368
  {latest?.state === 'READY' && latest.ready && latest.created && (
357
- <Text size={1} muted {formatDuration(Math.floor((latest.ready - latest.created) / 1000))}</Text>
369
+ <Text size={1} muted>Took {formatDuration(Math.floor((latest.ready - latest.created) / 1000))} to build</Text>
358
370
  )}
359
371
 
360
372
  {/* Visit link + copy URL */}
@@ -556,7 +568,7 @@ export function DeployItem({ target, token, onDelete, onEdit }: DeployItemProps)
556
568
  text="Deploy"
557
569
  tone="primary"
558
570
  loading={triggering}
559
- disabled={isActive}
571
+ disabled={isActive || clicked}
560
572
  onClick={deploy}
561
573
  style={{
562
574
  flex: 1,