@drawnagency/primitives 0.1.20 → 0.1.22

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 +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;AAkC9B,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,2CAqkBP"}
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;AAkC9B,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,7 +2,9 @@ type BuildState = "idle" | "building" | "ready" | "error";
2
2
  interface BuildStatusResult {
3
3
  state: BuildState;
4
4
  deployUrl: string | null;
5
+ elapsedSeconds: number;
5
6
  startTracking: () => void;
7
+ dismiss: () => void;
6
8
  }
7
9
  export declare function useBuildStatus(): BuildStatusResult;
8
10
  export {};
@@ -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,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAID,wBAAgB,cAAc,IAAI,iBAAiB,CA0FlD"}
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,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAKD,wBAAgB,cAAc,IAAI,iBAAiB,CAsHlD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawnagency/primitives",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./package.json": "./package.json",
@@ -47,7 +47,7 @@ import { SplitButton } from "../shared/SplitButton";
47
47
  import { IconButton } from "../shared/IconButton";
48
48
  import { SegmentedControl } from "../shared/SegmentedControl";
49
49
  import { SettingsIcon } from "../shared/icons";
50
- import { ImageIcon } from "lucide-react";
50
+ import { ImageIcon, X } from "lucide-react";
51
51
  import { ErrorBoundary } from "../shared/ErrorBoundary";
52
52
 
53
53
  export { useMediaLibrary } from "./MediaLibraryContext";
@@ -585,6 +585,8 @@ export default function EditorShell({
585
585
  }}
586
586
  processingItems={mediaPipeline.processingItems}
587
587
  buildState={buildStatus.state}
588
+ buildElapsed={buildStatus.elapsedSeconds}
589
+ onBuildDismiss={buildStatus.dismiss}
588
590
  />
589
591
 
590
592
  <EditorContent
@@ -871,29 +873,71 @@ function GlobalModal() {
871
873
  );
872
874
  }
873
875
 
876
+ function LoadingEllipsis() {
877
+ const [dots, setDots] = useState(0);
878
+ useEffect(() => {
879
+ const id = setInterval(() => setDots((d) => (d + 1) % 4), 400);
880
+ return () => clearInterval(id);
881
+ }, []);
882
+ return <span className="absolute">{".".repeat(dots)}</span>;
883
+ }
884
+
885
+ function formatElapsed(seconds: number): string {
886
+ const m = Math.floor(seconds / 60);
887
+ const s = seconds % 60;
888
+ return `${m}:${s.toString().padStart(2, "0")}`;
889
+ }
890
+
891
+ function DismissButton({ onClick }: { onClick: () => void }) {
892
+ return (
893
+ <button type="button" onClick={onClick} className="text-base-content/30 hover:text-base-content/60 cursor-pointer" aria-label="Dismiss">
894
+ <X size={12} />
895
+ </button>
896
+ );
897
+ }
898
+
874
899
  function StatusText({
875
900
  publishAction,
876
901
  publishFeedback,
877
902
  buildState,
903
+ buildElapsed,
904
+ onDismiss,
878
905
  }: {
879
906
  publishAction: "idle" | "saving" | "publishing";
880
907
  publishFeedback: string | null;
881
908
  buildState: "idle" | "building" | "ready" | "error";
909
+ buildElapsed: number;
910
+ onDismiss: () => void;
882
911
  }) {
883
912
  if (publishAction === "saving") {
884
- return <span className="text-xs font-medium text-base-content/60">Saving changes...</span>;
885
- }
886
- if (publishAction === "publishing") {
887
- return <span className="text-xs font-medium text-base-content/60">Publishing...</span>;
913
+ return (
914
+ <span className="relative text-xs font-medium text-base-content/60">
915
+ Saving changes<LoadingEllipsis />
916
+ </span>
917
+ );
888
918
  }
889
- if (buildState === "building") {
890
- return <span className="text-xs font-medium text-orange-600">Publishing...</span>;
919
+ if (publishAction === "publishing" || buildState === "building") {
920
+ return (
921
+ <span className="text-xs font-medium text-orange-600">
922
+ Publishing ({formatElapsed(buildElapsed)})
923
+ </span>
924
+ );
891
925
  }
892
926
  if (buildState === "ready") {
893
- return <span className="text-xs font-medium text-green-600">Published</span>;
927
+ return (
928
+ <span className="inline-flex items-center gap-1.5 text-xs font-medium text-green-600">
929
+ Published in {formatElapsed(buildElapsed)}
930
+ <DismissButton onClick={onDismiss} />
931
+ </span>
932
+ );
894
933
  }
895
934
  if (buildState === "error") {
896
- return <span className="text-xs font-medium text-red-600">Publish failed</span>;
935
+ return (
936
+ <span className="inline-flex items-center gap-1.5 text-xs font-medium text-red-600">
937
+ Publish failed
938
+ <DismissButton onClick={onDismiss} />
939
+ </span>
940
+ );
897
941
  }
898
942
  if (publishFeedback) {
899
943
  return (
@@ -921,6 +965,8 @@ function EditorToolbar({
921
965
  onMediaClick,
922
966
  processingItems,
923
967
  buildState,
968
+ buildElapsed,
969
+ onBuildDismiss,
924
970
  }: {
925
971
  buttonState: "synced" | "publish" | "saveAndPublish";
926
972
  localChangesExist: boolean;
@@ -934,6 +980,8 @@ function EditorToolbar({
934
980
  onMediaClick: () => void;
935
981
  processingItems: QueueItem[];
936
982
  buildState: "idle" | "building" | "ready" | "error";
983
+ buildElapsed: number;
984
+ onBuildDismiss: () => void;
937
985
  }) {
938
986
  const { isEditMode, viewBranch, setViewBranch, toggleEditMode } = useEditorContext();
939
987
 
@@ -981,6 +1029,8 @@ function EditorToolbar({
981
1029
  publishAction={publishAction}
982
1030
  publishFeedback={publishFeedback}
983
1031
  buildState={buildState}
1032
+ buildElapsed={buildElapsed}
1033
+ onDismiss={onBuildDismiss}
984
1034
  />
985
1035
  </div>
986
1036
  <div className="flex items-center justify-end gap-2">
@@ -12,17 +12,33 @@ interface BuildStatusResponse {
12
12
  interface BuildStatusResult {
13
13
  state: BuildState;
14
14
  deployUrl: string | null;
15
+ elapsedSeconds: number;
15
16
  startTracking: () => void;
17
+ dismiss: () => void;
16
18
  }
17
19
 
18
20
  const POLL_INTERVAL = 5000;
21
+ const AUTO_CLEAR_DELAY = 5000;
19
22
 
20
23
  export function useBuildStatus(): BuildStatusResult {
21
24
  const [state, setState] = useState<BuildState>("idle");
22
25
  const [deployUrl, setDeployUrl] = useState<string | null>(null);
26
+ const [elapsedSeconds, setElapsedSeconds] = useState(0);
23
27
  const pollRef = useRef<ReturnType<typeof setInterval> | null>(null);
28
+ const clearRef = useRef<ReturnType<typeof setTimeout> | null>(null);
29
+ const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
24
30
  const isPolling = useRef(false);
25
31
 
32
+ const stopTimer = useCallback(() => {
33
+ if (timerRef.current) { clearInterval(timerRef.current); timerRef.current = null; }
34
+ }, []);
35
+
36
+ const startTimer = useCallback(() => {
37
+ stopTimer();
38
+ setElapsedSeconds(0);
39
+ timerRef.current = setInterval(() => setElapsedSeconds((s) => s + 1), 1000);
40
+ }, [stopTimer]);
41
+
26
42
  const stopPolling = useCallback(() => {
27
43
  if (pollRef.current) {
28
44
  clearInterval(pollRef.current);
@@ -63,6 +79,10 @@ export function useBuildStatus(): BuildStatusResult {
63
79
 
64
80
  if (data.state === "ready" || data.state === "error") {
65
81
  stopPolling();
82
+ stopTimer();
83
+ if (data.state === "ready") {
84
+ clearRef.current = setTimeout(() => setState("idle"), AUTO_CLEAR_DELAY);
85
+ }
66
86
  }
67
87
  },
68
88
  [stopPolling],
@@ -97,14 +117,25 @@ export function useBuildStatus(): BuildStatusResult {
97
117
  return () => {
98
118
  cancelled = true;
99
119
  stopPolling();
120
+ stopTimer();
121
+ if (clearRef.current) clearTimeout(clearRef.current);
100
122
  };
101
123
  }, [fetchStatus, handleStatusUpdate, startPolling, stopPolling]);
102
124
 
103
125
  const startTracking = useCallback(() => {
126
+ if (clearRef.current) { clearTimeout(clearRef.current); clearRef.current = null; }
104
127
  setState("building");
105
128
  setDeployUrl(null);
129
+ startTimer();
106
130
  startPolling();
107
- }, [startPolling]);
131
+ }, [startPolling, startTimer]);
132
+
133
+ const dismiss = useCallback(() => {
134
+ if (clearRef.current) { clearTimeout(clearRef.current); clearRef.current = null; }
135
+ setState("idle");
136
+ stopPolling();
137
+ stopTimer();
138
+ }, [stopPolling, stopTimer]);
108
139
 
109
- return { state, deployUrl, startTracking };
140
+ return { state, deployUrl, elapsedSeconds, startTracking, dismiss };
110
141
  }