Skip to content

Commit abe2a27

Browse files
committed
feat: working commit - template status icons and switcher fixes
1 parent 8bc70e5 commit abe2a27

4 files changed

Lines changed: 87 additions & 17 deletions

File tree

packages/imagekit-editor-dev/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "imagekit-editor-dev",
3-
"version": "2.2.0-dev-13-04-2026.2",
3+
"version": "2.2.0-dev-24-04-2026.8",
44
"description": "AI Image Editor powered by ImageKit",
55
"scripts": {
66
"prepack": "yarn build",

packages/imagekit-editor-dev/src/components/header/TemplateStatus.tsx

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
Box,
3+
Button,
34
Flex,
45
Icon,
56
Popover,
@@ -16,6 +17,7 @@ import { MdSyncProblem } from "@react-icons/all-files/md/MdSyncProblem"
1617
import type React from "react"
1718
import { useEffect, useRef, useState } from "react"
1819
import { useTemplateStorage } from "../../context/TemplateStorageContext"
20+
import { useSaveTemplate } from "../../hooks/useSaveTemplate"
1921
import { useEditorStore } from "../../store"
2022

2123
const NOTIFICATION_DURATION_MS = 3000
@@ -35,12 +37,16 @@ export function TemplateStatus() {
3537
const syncStatus = useEditorStore((s) => s.syncStatus)
3638
const storageError = useEditorStore((s) => s.storageError)
3739
const isPristine = useEditorStore((s) => s.isPristine)
40+
const templateStorageWriteBlocked = useEditorStore(
41+
(s) => s.templateStorageWriteBlocked,
42+
)
3843
const hasPendingLocalWork = useEditorStore(
3944
(s) =>
4045
s.localChangeVersion !== s.lastSyncedVersion ||
4146
s.transformationConfigFormDirty,
4247
)
4348
const provider = useTemplateStorage()
49+
const { save } = useSaveTemplate()
4450

4551
const [notificationVisible, setNotificationVisible] = useState(false)
4652
const [lastSyncResult, setLastSyncResult] = useState<
@@ -115,10 +121,12 @@ export function TemplateStatus() {
115121
| "template-status-saved"
116122
| "template-status-error"
117123

124+
const isUnsavedState = hasPendingLocalWork || syncStatus === "unsaved"
125+
118126
if (notificationVisible) {
119127
// Unsynced edits take precedence over the last successful save result.
120128
// This prevents showing the green cloud when newer changes exist locally.
121-
if (hasPendingLocalWork || syncStatus === "unsaved") {
129+
if (isUnsavedState) {
122130
activeIcon = MdSync
123131
activeColor = "editorBattleshipGrey.500"
124132
notifText = "Unsaved local changes"
@@ -143,7 +151,7 @@ export function TemplateStatus() {
143151
if (hasPendingLocalWork) {
144152
activeIcon = MdSync
145153
activeColor = "editorBattleshipGrey.500"
146-
isInteractive = false
154+
isInteractive = true
147155
iconAriaLabel = "template-status-unsaved"
148156
} else {
149157
if (lastSyncResult === null) return null
@@ -157,10 +165,15 @@ export function TemplateStatus() {
157165
}
158166
}
159167

160-
const popupTitle =
161-
lastSyncResult === "success" ? "All changes saved" : "Sync failed"
162-
const popupBody =
163-
lastSyncResult === "success"
168+
const popupTitle = isUnsavedState
169+
? "Unsaved changes"
170+
: lastSyncResult === "success"
171+
? "All changes saved"
172+
: "Sync failed"
173+
174+
const popupBody = isUnsavedState
175+
? "Your current local changes haven't been synced to the library yet."
176+
: lastSyncResult === "success"
164177
? `Your changes are synced to ${providerName} successfully.`
165178
: (storageError ?? "Failed to save changes. Please try again.")
166179

@@ -215,6 +228,19 @@ export function TemplateStatus() {
215228
<TextAny2 fontSize="sm" color="editorBattleshipGrey.600">
216229
{popupBody}
217230
</TextAny2>
231+
{isUnsavedState && (
232+
<Box mt="3">
233+
<Button
234+
size="sm"
235+
colorScheme="blue"
236+
onClick={() => void save()}
237+
isLoading={syncStatus === "saving"}
238+
isDisabled={templateStorageWriteBlocked}
239+
>
240+
Save
241+
</Button>
242+
</Box>
243+
)}
218244
</PopoverBodyAny>
219245
</PopoverContentAny>
220246
</Popover>

packages/imagekit-editor-dev/src/components/header/TemplatesDropdown.tsx

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export function TemplatesDropdown({
6161
const [pinningId, setPinningId] = useState<string | null>(null)
6262
const searchRef = useRef<HTMLInputElement>(null)
6363
const cancelRef = useRef<HTMLButtonElement>(null)
64+
const [isSavingAndContinuing, setIsSavingAndContinuing] = useState(false)
6465

6566
const [pendingTemplate, setPendingTemplate] = useState<TemplateRecord | null>(
6667
null,
@@ -205,7 +206,11 @@ export function TemplatesDropdown({
205206
const handleSaveAndContinue = async () => {
206207
if (!provider || !pendingTemplate || templateStorageWriteBlocked) return
207208
try {
208-
await saveNow({ reason: "manual" })
209+
setIsSavingAndContinuing(true)
210+
const saved = await saveNow({ reason: "manual" })
211+
// saveNow can return null (e.g. another save is already in progress).
212+
// In that case, do not switch templates or we'd drop unsaved changes.
213+
if (!saved) return
209214
doLoadTemplate(pendingTemplate)
210215
} catch (err) {
211216
const { denyTemplateStorageAccess, setSyncStatus } =
@@ -221,6 +226,8 @@ export function TemplatesDropdown({
221226
"error",
222227
err instanceof Error ? err.message : "Failed to save",
223228
)
229+
} finally {
230+
setIsSavingAndContinuing(false)
224231
}
225232
}
226233

@@ -523,42 +530,79 @@ export function TemplatesDropdown({
523530
<AlertDialog
524531
isOpen={pendingTemplate !== null}
525532
leastDestructiveRef={cancelRef}
526-
onClose={() => setPendingTemplate(null)}
533+
onClose={() => {
534+
if (isSavingAndContinuing) return
535+
setPendingTemplate(null)
536+
}}
527537
isCentered
528538
>
529539
<AlertDialogOverlay>
530-
<AlertDialogContent>
531-
<AlertDialogHeader fontSize="md" fontWeight="semibold">
540+
<AlertDialogContent
541+
w="45vw"
542+
maxW="45vw"
543+
h="40vh"
544+
maxH="40vh"
545+
bg="white"
546+
borderRadius="xl"
547+
overflow="hidden"
548+
boxShadow="xl"
549+
display="flex"
550+
flexDirection="column"
551+
>
552+
<AlertDialogHeader
553+
fontSize="lg"
554+
fontWeight="semibold"
555+
color="editorGray.900"
556+
px="6"
557+
py="4"
558+
borderBottomWidth="0.5px"
559+
borderColor="editorGray.50"
560+
>
532561
Unsaved changes
533562
</AlertDialogHeader>
534-
<AlertDialogBody fontSize="sm">
563+
<AlertDialogBody
564+
fontSize="lg"
565+
color="editorGray.700"
566+
p="6"
567+
flex="1"
568+
overflowY="auto"
569+
>
535570
Your current changes haven't been saved yet. What would you like
536571
to do before switching to{" "}
537572
<Box as="span" fontWeight="semibold">
538573
{pendingTemplate?.name}
539574
</Box>
540575
?
541576
</AlertDialogBody>
542-
<AlertDialogFooter gap="2">
577+
<AlertDialogFooter
578+
p="6"
579+
borderTopWidth="0.5px"
580+
borderColor="editorGray.50"
581+
gap="3"
582+
>
543583
<Button
544584
ref={cancelRef}
545-
size="sm"
585+
size="md"
546586
variant="ghost"
547587
onClick={() => setPendingTemplate(null)}
588+
isDisabled={isSavingAndContinuing}
548589
>
549590
Cancel
550591
</Button>
551592
<Button
552-
size="sm"
593+
size="md"
553594
variant="outline"
554595
onClick={handleContinueWithoutSaving}
596+
isDisabled={isSavingAndContinuing}
555597
>
556598
Continue without saving
557599
</Button>
558600
<Button
559-
size="sm"
601+
size="md"
560602
colorScheme="blue"
561603
onClick={handleSaveAndContinue}
604+
isLoading={isSavingAndContinuing}
605+
isDisabled={templateStorageWriteBlocked}
562606
>
563607
Save and continue
564608
</Button>

packages/imagekit-editor/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@imagekit/editor",
3-
"version": "2.2.0-dev-13-04-2026.2",
3+
"version": "2.2.0-dev-24-04-2026.8",
44
"description": "Image Editor powered by ImageKit",
55
"main": "dist/index.cjs.js",
66
"module": "dist/index.es.js",

0 commit comments

Comments
 (0)