@drawnagency/primitives 0.1.15 → 0.1.16

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.
@@ -1,9 +1,7 @@
1
1
  interface BuildStatusIndicatorProps {
2
2
  state: "idle" | "building" | "ready" | "error";
3
3
  deployUrl: string | null;
4
- visible: boolean;
5
- onDismiss: () => void;
6
4
  }
7
- export declare function BuildStatusIndicator({ state, deployUrl, visible, onDismiss, }: BuildStatusIndicatorProps): import("react/jsx-runtime").JSX.Element | null;
5
+ export declare function BuildStatusIndicator({ state, deployUrl, }: BuildStatusIndicatorProps): import("react/jsx-runtime").JSX.Element | null;
8
6
  export {};
9
7
  //# sourceMappingURL=BuildStatusIndicator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BuildStatusIndicator.d.ts","sourceRoot":"","sources":["../../../src/components/shell/BuildStatusIndicator.tsx"],"names":[],"mappings":"AAGA,UAAU,yBAAyB;IACjC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;IAC/C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAoBD,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,SAAS,EACT,OAAO,EACP,SAAS,GACV,EAAE,yBAAyB,kDAiC3B"}
1
+ {"version":3,"file":"BuildStatusIndicator.d.ts","sourceRoot":"","sources":["../../../src/components/shell/BuildStatusIndicator.tsx"],"names":[],"mappings":"AAGA,UAAU,yBAAyB;IACjC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;IAC/C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AA6BD,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,SAAS,GACV,EAAE,yBAAyB,kDAoB3B"}
@@ -1 +1 @@
1
- {"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorShell.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAYjD,OAAO,sBAAsB,CAAC;AAmC9B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAcxD,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,YAAY,EAAE;QACZ,KAAK,EAAE,OAAO,CAAC;QACf,aAAa,EAAE,OAAO,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;QACtB,cAAc,EAAE,OAAO,CAAC;QACxB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;KAAE,GAAG,IAAI,CAAC;CACjE;AAED,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,OAAO,EACP,MAAM,EACN,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EACZ,WAAW,GACZ,EAAE,KAAK,2CAykBP"}
1
+ {"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorShell.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAYjD,OAAO,sBAAsB,CAAC;AAmC9B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAcxD,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,YAAY,EAAE;QACZ,KAAK,EAAE,OAAO,CAAC;QACf,aAAa,EAAE,OAAO,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;QACtB,cAAc,EAAE,OAAO,CAAC;QACxB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;KAAE,GAAG,IAAI,CAAC;CACjE;AAED,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,OAAO,EACP,MAAM,EACN,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EACZ,WAAW,GACZ,EAAE,KAAK,2CAukBP"}
@@ -2,8 +2,6 @@ type BuildState = "idle" | "building" | "ready" | "error";
2
2
  interface BuildStatusResult {
3
3
  state: BuildState;
4
4
  deployUrl: string | null;
5
- visible: boolean;
6
- dismiss: () => void;
7
5
  startTracking: () => void;
8
6
  }
9
7
  export declare function useBuildStatus(): BuildStatusResult;
@@ -1 +1 @@
1
- {"version":3,"file":"useBuildStatus.d.ts","sourceRoot":"","sources":["../../src/hooks/useBuildStatus.ts"],"names":[],"mappings":"AAEA,KAAK,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;AAS1D,UAAU,iBAAiB;IACzB,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAKD,wBAAgB,cAAc,IAAI,iBAAiB,CAoHlD"}
1
+ {"version":3,"file":"useBuildStatus.d.ts","sourceRoot":"","sources":["../../src/hooks/useBuildStatus.ts"],"names":[],"mappings":"AAEA,KAAK,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;AAS1D,UAAU,iBAAiB;IACzB,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAID,wBAAgB,cAAc,IAAI,iBAAiB,CA0FlD"}
@@ -18,8 +18,9 @@ interface PublishDeps {
18
18
  onShasUpdated: (savedSha: string | null, mainSha: string | null) => void;
19
19
  onPublishComplete?: () => void;
20
20
  }
21
+ export type PublishAction = "idle" | "saving" | "publishing";
21
22
  export declare function useEditorPublish({ flushNow, cancelPendingFlush, isConfigDirty, clearConfigDirty, siteIndexRef, siteConfig, sections, deletedSectionIds, onSuccess, mediaManifest, pendingMediaItems, pendingMediaDeletions, onMediaPublished, onShasUpdated, onPublishComplete, }: PublishDeps): {
22
- isPublishing: boolean;
23
+ publishAction: PublishAction;
23
24
  publishFeedback: string | null;
24
25
  handleSave: () => Promise<void>;
25
26
  handlePublish: () => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"useEditorPublish.d.ts","sourceRoot":"","sources":["../../src/hooks/useEditorPublish.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAe/D,UAAU,WAAW;IACnB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,OAAO,CAAC;IAC7B,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,aAAa,EAAE,aAAa,CAAC;IAC7B,iBAAiB,EAAE,SAAS,EAAE,CAAC;IAC/B,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,gBAAgB,EAAE,CAAC,cAAc,EAAE,SAAS,EAAE,EAAE,kBAAkB,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACtF,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzE,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;CAChC;AASD,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,iBAAiB,EACjB,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,GAClB,EAAE,WAAW;;;;;;EAuSb"}
1
+ {"version":3,"file":"useEditorPublish.d.ts","sourceRoot":"","sources":["../../src/hooks/useEditorPublish.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAe/D,UAAU,WAAW;IACnB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,OAAO,CAAC;IAC7B,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,aAAa,EAAE,aAAa,CAAC;IAC7B,iBAAiB,EAAE,SAAS,EAAE,CAAC;IAC/B,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,gBAAgB,EAAE,CAAC,cAAc,EAAE,SAAS,EAAE,EAAE,kBAAkB,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACtF,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzE,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;CAChC;AASD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,YAAY,CAAC;AAE7D,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,iBAAiB,EACjB,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,GAClB,EAAE,WAAW;;;;;;EAuSb"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawnagency/primitives",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./package.json": "./package.json",
@@ -1,27 +1,34 @@
1
1
  import { cn } from "../../lib/cn";
2
- import { X } from "lucide-react";
2
+ import { useEffect, useState } from "react";
3
3
 
4
4
  interface BuildStatusIndicatorProps {
5
5
  state: "idle" | "building" | "ready" | "error";
6
6
  deployUrl: string | null;
7
- visible: boolean;
8
- onDismiss: () => void;
7
+ }
8
+
9
+ function LoadingEllipsis() {
10
+ const [dots, setDots] = useState(0);
11
+ useEffect(() => {
12
+ const id = setInterval(() => setDots((d) => (d + 1) % 4), 400);
13
+ return () => clearInterval(id);
14
+ }, []);
15
+ return <>{".".repeat(dots)}</>;
9
16
  }
10
17
 
11
18
  const stateConfig = {
12
19
  building: {
13
- label: "Deploying...",
14
- className: "border-orange-500 text-orange-600 hover:bg-orange-50",
20
+ label: "Publishing",
21
+ className: "border-orange-500 text-orange-600",
15
22
  dotClassName: "bg-orange-500 animate-pulse",
16
23
  },
17
24
  ready: {
18
- label: "Live",
19
- className: "border-green-600 text-green-600 hover:bg-green-50",
25
+ label: "Published",
26
+ className: "border-green-600 text-green-600",
20
27
  dotClassName: "bg-green-600",
21
28
  },
22
29
  error: {
23
- label: "Deploy failed",
24
- className: "border-red-600 text-red-600 hover:bg-red-50",
30
+ label: "Publish failed",
31
+ className: "border-red-600 text-red-600",
25
32
  dotClassName: "bg-red-600",
26
33
  },
27
34
  } as const;
@@ -29,39 +36,24 @@ const stateConfig = {
29
36
  export function BuildStatusIndicator({
30
37
  state,
31
38
  deployUrl,
32
- visible,
33
- onDismiss,
34
39
  }: BuildStatusIndicatorProps) {
35
- if (!visible || state === "idle") return null;
40
+ if (state === "idle") return null;
36
41
 
37
42
  const config = stateConfig[state];
38
43
 
39
44
  return (
40
- <span className="inline-flex items-center gap-0">
41
- <a
42
- href={deployUrl ?? "#"}
43
- target="_blank"
44
- rel="noopener noreferrer"
45
- aria-label={config.label}
46
- className={cn(
47
- "inline-flex items-center gap-1.5 rounded-l px-3 py-1.5 text-xs font-medium border transition-colors",
48
- config.className,
49
- )}
50
- >
51
- <span className={cn("h-1.5 w-1.5 rounded-full", config.dotClassName)} />
52
- {config.label}
53
- </a>
54
- <button
55
- type="button"
56
- onClick={onDismiss}
57
- aria-label="Dismiss build status"
58
- className={cn(
59
- "inline-flex items-center rounded-r border border-l-0 px-1.5 py-1.5 transition-colors cursor-pointer",
60
- config.className,
61
- )}
62
- >
63
- <X size={12} />
64
- </button>
65
- </span>
45
+ <a
46
+ href={deployUrl ?? "#"}
47
+ target="_blank"
48
+ rel="noopener noreferrer"
49
+ aria-label={config.label}
50
+ className={cn(
51
+ "inline-flex items-center gap-1.5 rounded px-3 py-1.5 text-xs font-medium border transition-colors",
52
+ config.className,
53
+ )}
54
+ >
55
+ <span className={cn("h-1.5 w-1.5 rounded-full", config.dotClassName)} />
56
+ {config.label}{state === "building" && <LoadingEllipsis />}
57
+ </a>
66
58
  );
67
59
  }
@@ -127,7 +127,7 @@ export default function EditorShell({
127
127
 
128
128
  const buildStatus = useBuildStatus();
129
129
 
130
- const { isPublishing, publishFeedback, handleSave, handlePublish, handleSaveAndPublish } = useEditorPublish({
130
+ const { publishAction, publishFeedback, handleSave, handlePublish, handleSaveAndPublish } = useEditorPublish({
131
131
  flushNow: persistence.flushNow,
132
132
  cancelPendingFlush: persistence.cancelPendingFlush,
133
133
  isConfigDirty: persistence.isConfigDirty,
@@ -574,7 +574,7 @@ export default function EditorShell({
574
574
  <EditorToolbar
575
575
  buttonState={buttonState}
576
576
  localChangesExist={localChangesExist}
577
- isPublishing={isPublishing}
577
+ publishAction={publishAction}
578
578
  publishFeedback={publishFeedback}
579
579
  onSave={handleSave}
580
580
  onPublish={handlePublish}
@@ -588,8 +588,6 @@ export default function EditorShell({
588
588
  processingItems={mediaPipeline.processingItems}
589
589
  buildState={buildStatus.state}
590
590
  buildDeployUrl={buildStatus.deployUrl}
591
- buildVisible={buildStatus.visible}
592
- onBuildDismiss={buildStatus.dismiss}
593
591
  />
594
592
 
595
593
  <EditorContent
@@ -597,7 +595,7 @@ export default function EditorShell({
597
595
  audiences={audiences}
598
596
  dirtySectionIds={dirtySectionIds}
599
597
  deletedSections={deletedSections}
600
- isPublishing={isPublishing}
598
+ isPublishing={publishAction !== "idle"}
601
599
  onSectionChange={onSectionChange}
602
600
  onAddSection={onAddSection}
603
601
  onDeleteSection={onDeleteSection}
@@ -879,7 +877,7 @@ function GlobalModal() {
879
877
  function EditorToolbar({
880
878
  buttonState,
881
879
  localChangesExist,
882
- isPublishing,
880
+ publishAction,
883
881
  publishFeedback,
884
882
  onSave,
885
883
  onPublish,
@@ -890,12 +888,10 @@ function EditorToolbar({
890
888
  processingItems,
891
889
  buildState,
892
890
  buildDeployUrl,
893
- buildVisible,
894
- onBuildDismiss,
895
891
  }: {
896
892
  buttonState: "synced" | "publish" | "saveAndPublish";
897
893
  localChangesExist: boolean;
898
- isPublishing: boolean;
894
+ publishAction: "idle" | "saving" | "publishing";
899
895
  publishFeedback: string | null;
900
896
  onSave: () => void;
901
897
  onPublish: () => void;
@@ -906,8 +902,6 @@ function EditorToolbar({
906
902
  processingItems: QueueItem[];
907
903
  buildState: "idle" | "building" | "ready" | "error";
908
904
  buildDeployUrl: string | null;
909
- buildVisible: boolean;
910
- onBuildDismiss: () => void;
911
905
  }) {
912
906
  const { isEditMode, viewBranch, setViewBranch, toggleEditMode } = useEditorContext();
913
907
 
@@ -919,7 +913,7 @@ function EditorToolbar({
919
913
  {publishFeedback && (
920
914
  <span className={cn(
921
915
  "text-xs font-medium",
922
- publishFeedback === "Published" || publishFeedback === "Saved"
916
+ publishFeedback === "Saved"
923
917
  ? "text-green-600"
924
918
  : "text-red-600",
925
919
  )}>
@@ -930,8 +924,8 @@ function EditorToolbar({
930
924
  <SplitButton
931
925
  label="Save & Publish"
932
926
  onClick={onSaveAndPublish}
933
- isLoading={isPublishing}
934
- loadingLabel="Publishing..."
927
+ isLoading={publishAction !== "idle"}
928
+ loadingLabel={publishAction === "saving" ? "Saving..." : "Publishing..."}
935
929
  options={[{ label: "Save", onClick: onSave }]}
936
930
  />
937
931
  )}
@@ -939,7 +933,7 @@ function EditorToolbar({
939
933
  <SplitButton
940
934
  label="Publish"
941
935
  onClick={onPublish}
942
- isLoading={isPublishing}
936
+ isLoading={publishAction !== "idle"}
943
937
  loadingLabel="Publishing..."
944
938
  options={[]}
945
939
  />
@@ -952,12 +946,11 @@ function EditorToolbar({
952
946
  options={[]}
953
947
  />
954
948
  )}
955
- {localChangesExist && !isPublishing && (
949
+ {localChangesExist && publishAction === "idle" && (
956
950
  <Button
957
951
  type="button"
958
952
  variant="destructive"
959
953
  onClick={onDiscardClick}
960
- disabled={isPublishing}
961
954
  >
962
955
  Discard Changes
963
956
  </Button>
@@ -965,8 +958,6 @@ function EditorToolbar({
965
958
  <BuildStatusIndicator
966
959
  state={buildState}
967
960
  deployUrl={buildDeployUrl}
968
- visible={buildVisible}
969
- onDismiss={onBuildDismiss}
970
961
  />
971
962
  </div>
972
963
  <div className="flex items-center gap-2">
@@ -12,20 +12,15 @@ interface BuildStatusResponse {
12
12
  interface BuildStatusResult {
13
13
  state: BuildState;
14
14
  deployUrl: string | null;
15
- visible: boolean;
16
- dismiss: () => void;
17
15
  startTracking: () => void;
18
16
  }
19
17
 
20
18
  const POLL_INTERVAL = 5000;
21
- const AUTO_DISMISS_DELAY = 10000;
22
19
 
23
20
  export function useBuildStatus(): BuildStatusResult {
24
21
  const [state, setState] = useState<BuildState>("idle");
25
22
  const [deployUrl, setDeployUrl] = useState<string | null>(null);
26
- const [dismissed, setDismissed] = useState(false);
27
23
  const pollRef = useRef<ReturnType<typeof setInterval> | null>(null);
28
- const dismissRef = useRef<ReturnType<typeof setTimeout> | null>(null);
29
24
  const isPolling = useRef(false);
30
25
 
31
26
  const stopPolling = useCallback(() => {
@@ -65,16 +60,9 @@ export function useBuildStatus(): BuildStatusResult {
65
60
 
66
61
  setState(data.state);
67
62
  setDeployUrl(data.deployUrl);
68
- setDismissed(false);
69
63
 
70
64
  if (data.state === "ready" || data.state === "error") {
71
65
  stopPolling();
72
-
73
- if (data.state === "ready") {
74
- dismissRef.current = setTimeout(() => {
75
- setDismissed(true);
76
- }, AUTO_DISMISS_DELAY);
77
- }
78
66
  }
79
67
  },
80
68
  [stopPolling],
@@ -109,31 +97,14 @@ export function useBuildStatus(): BuildStatusResult {
109
97
  return () => {
110
98
  cancelled = true;
111
99
  stopPolling();
112
- if (dismissRef.current) clearTimeout(dismissRef.current);
113
100
  };
114
101
  }, [fetchStatus, handleStatusUpdate, startPolling, stopPolling]);
115
102
 
116
- const dismiss = useCallback(() => {
117
- setDismissed(true);
118
- stopPolling();
119
- if (dismissRef.current) {
120
- clearTimeout(dismissRef.current);
121
- dismissRef.current = null;
122
- }
123
- }, [stopPolling]);
124
-
125
103
  const startTracking = useCallback(() => {
126
104
  setState("building");
127
105
  setDeployUrl(null);
128
- setDismissed(false);
129
- if (dismissRef.current) {
130
- clearTimeout(dismissRef.current);
131
- dismissRef.current = null;
132
- }
133
106
  startPolling();
134
107
  }, [startPolling]);
135
108
 
136
- const visible = state !== "idle" && !dismissed;
137
-
138
- return { state, deployUrl, visible, dismiss, startTracking };
109
+ return { state, deployUrl, startTracking };
139
110
  }
@@ -41,6 +41,8 @@ interface GatheredMedia {
41
41
  hasMediaChanges: boolean;
42
42
  }
43
43
 
44
+ export type PublishAction = "idle" | "saving" | "publishing";
45
+
44
46
  export function useEditorPublish({
45
47
  flushNow,
46
48
  cancelPendingFlush,
@@ -58,7 +60,7 @@ export function useEditorPublish({
58
60
  onShasUpdated,
59
61
  onPublishComplete,
60
62
  }: PublishDeps) {
61
- const [isPublishing, setIsPublishing] = useState(false);
63
+ const [publishAction, setPublishAction] = useState<PublishAction>("idle");
62
64
  const [publishFeedback, setPublishFeedback] = useState<string | null>(null);
63
65
  const feedbackTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
64
66
 
@@ -136,7 +138,7 @@ export function useEditorPublish({
136
138
  const handleSave = useCallback(async () => {
137
139
  if (!siteConfig) return;
138
140
 
139
- setIsPublishing(true);
141
+ setPublishAction("saving");
140
142
  setPublishFeedback(null);
141
143
 
142
144
  try {
@@ -147,7 +149,7 @@ export function useEditorPublish({
147
149
  const hasMediaChanges = pendingMediaItems.length > 0 || pendingMediaDeletions.length > 0;
148
150
  const hasDeletedSections = (deletedSectionIds?.length ?? 0) > 0;
149
151
  if (!hasChanges && !isConfigDirty() && !hasMediaChanges && !hasDeletedSections) {
150
- setIsPublishing(false);
152
+ setPublishAction("idle");
151
153
  return;
152
154
  }
153
155
 
@@ -216,12 +218,12 @@ export function useEditorPublish({
216
218
  console.error("Save failed:", error);
217
219
  showFeedback("Save failed", 5000);
218
220
  } finally {
219
- setIsPublishing(false);
221
+ setPublishAction("idle");
220
222
  }
221
223
  }, [flushNow, cancelPendingFlush, isConfigDirty, clearConfigDirty, siteIndexRef, siteConfig, sections, deletedSectionIds, onSuccess, mediaManifest, pendingMediaItems, pendingMediaDeletions, onMediaPublished, onShasUpdated, showFeedback]);
222
224
 
223
225
  const handlePublish = useCallback(async () => {
224
- setIsPublishing(true);
226
+ setPublishAction("publishing");
225
227
  setPublishFeedback(null);
226
228
 
227
229
  try {
@@ -238,20 +240,19 @@ export function useEditorPublish({
238
240
  const { sha } = await response.json();
239
241
 
240
242
  onShasUpdated(null, sha);
241
- showFeedback("Published", 3000);
242
243
  onPublishComplete?.();
243
244
  } catch (error) {
244
245
  console.error("Publish failed:", error);
245
246
  showFeedback("Publish failed", 5000);
246
247
  } finally {
247
- setIsPublishing(false);
248
+ setPublishAction("idle");
248
249
  }
249
250
  }, [onShasUpdated, showFeedback, onPublishComplete]);
250
251
 
251
252
  const handleSaveAndPublish = useCallback(async () => {
252
253
  if (!siteConfig) return;
253
254
 
254
- setIsPublishing(true);
255
+ setPublishAction("saving");
255
256
  setPublishFeedback(null);
256
257
 
257
258
  try {
@@ -316,6 +317,8 @@ export function useEditorPublish({
316
317
  }
317
318
  }
318
319
 
320
+ setPublishAction("publishing");
321
+
319
322
  const publishResponse = await fetch("/api/publish", {
320
323
  method: "POST",
321
324
  headers: { "Content-Type": "application/json" },
@@ -341,15 +344,14 @@ export function useEditorPublish({
341
344
  }
342
345
 
343
346
  onShasUpdated(null, sha);
344
- showFeedback("Published", 3000);
345
347
  onPublishComplete?.();
346
348
  } catch (error) {
347
349
  console.error("Publish failed:", error);
348
350
  showFeedback("Publish failed", 5000);
349
351
  } finally {
350
- setIsPublishing(false);
352
+ setPublishAction("idle");
351
353
  }
352
354
  }, [flushNow, cancelPendingFlush, isConfigDirty, clearConfigDirty, siteIndexRef, siteConfig, sections, deletedSectionIds, onSuccess, mediaManifest, pendingMediaItems, pendingMediaDeletions, onMediaPublished, onShasUpdated, showFeedback, onPublishComplete]);
353
355
 
354
- return { isPublishing, publishFeedback, handleSave, handlePublish, handleSaveAndPublish };
356
+ return { publishAction, publishFeedback, handleSave, handlePublish, handleSaveAndPublish };
355
357
  }