Skip to content

Commit ab05224

Browse files
committed
fix: handle special characters in the UI; backend was okay before
1 parent c1830b3 commit ab05224

8 files changed

Lines changed: 370 additions & 197 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-24-04-2026.9",
3+
"version": "2.2.0-dev-29-04-2026.5",
44
"description": "AI Image Editor powered by ImageKit",
55
"scripts": {
66
"prepack": "yarn build",

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { useTemplateStorage } from "../../context/TemplateStorageContext"
2020
import { useTemplateSync } from "../../hooks/useTemplateSync"
2121
import { applyTemplateStorageAccessFailure } from "../../storage/templateAccessError"
2222
import { useEditorStore } from "../../store"
23+
import { formatTemplateNameForUI } from "../../utils"
2324

2425
const FlexAny = Flex as unknown as React.FC<Record<string, unknown>>
2526

@@ -58,7 +59,9 @@ export function SettingsModal({ onClose, knownIsPrivate }: SettingsModalProps) {
5859
onCloseRef.current = onClose
5960
})
6061

61-
const [localName, setLocalName] = useState(templateName)
62+
const [localName, setLocalName] = useState(() =>
63+
formatTemplateNameForUI(templateName),
64+
)
6265
const [localVisibility, setLocalVisibility] = useState<"everyone" | "onlyMe">(
6366
() => visibilityFromKnownPrivate(knownIsPrivate),
6467
)
@@ -67,7 +70,7 @@ export function SettingsModal({ onClose, knownIsPrivate }: SettingsModalProps) {
6770
const [isSaving, setIsSaving] = useState(false)
6871

6972
useEffect(() => {
70-
setLocalName(templateName)
73+
setLocalName(formatTemplateNameForUI(templateName))
7174
}, [templateName])
7275

7376
useEffect(() => {

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

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,23 @@ import { Input } from "@chakra-ui/react"
22
import type React from "react"
33
import { useEffect, useRef, useState } from "react"
44
import { useEditorStore } from "../../store"
5+
import { formatTemplateNameForUI } from "../../utils"
56

67
const UNTITLED = "Untitled Template"
8+
const InputAny = Input as unknown as React.ElementType
79

810
export function TemplateNameInput() {
9-
const templateName = useEditorStore((s) => s.templateName)
11+
const templateNameRaw = useEditorStore((s) => s.templateName)
1012
const templateId = useEditorStore((s) => s.templateId)
1113
const isPristine = useEditorStore((s) => s.isPristine)
1214
const setTemplateName = useEditorStore((s) => s.setTemplateName)
1315

14-
const [localValue, setLocalValue] = useState(templateName)
16+
const templateNameUI = formatTemplateNameForUI(templateNameRaw)
17+
const [localValue, setLocalValue] = useState(templateNameUI)
1518
const localValueRef = useRef(localValue)
1619
const inputRef = useRef<HTMLInputElement>(null)
1720
const isFocusedRef = useRef(false)
21+
const didUserEditRef = useRef(false)
1822
const prevIsPristineRef = useRef(isPristine)
1923

2024
localValueRef.current = localValue
@@ -23,9 +27,10 @@ export function TemplateNameInput() {
2327
// template from the dropdown) update the input without overwriting in-progress edits.
2428
useEffect(() => {
2529
if (!isFocusedRef.current) {
26-
setLocalValue(templateName)
30+
didUserEditRef.current = false
31+
setLocalValue(formatTemplateNameForUI(templateNameRaw))
2732
}
28-
}, [templateName])
33+
}, [templateNameRaw])
2934

3035
// Focus the input when starting a new unsaved template (reset → pristine with no id).
3136
// Do not focus when transitioning to pristine after a successful save (id stays set).
@@ -43,6 +48,12 @@ export function TemplateNameInput() {
4348
if (!trimmed) {
4449
setLocalValue(UNTITLED)
4550
}
51+
52+
// Avoid mutating store just because the persisted name was HTML-encoded.
53+
if (!didUserEditRef.current && finalName === templateNameUI) {
54+
return
55+
}
56+
4657
// setTemplateName only marks isPristine:false when the name actually changed,
4758
// which is what gates the auto-save in useAutoSaveTemplate.
4859
setTemplateName(finalName)
@@ -63,18 +74,22 @@ export function TemplateNameInput() {
6374
inputRef.current?.blur()
6475
}
6576
if (e.key === "Escape") {
66-
setLocalValue(templateName)
77+
didUserEditRef.current = false
78+
setLocalValue(templateNameUI)
6779
inputRef.current?.blur()
6880
}
6981
}
7082

7183
const isDefault = localValue === UNTITLED
7284

7385
return (
74-
<Input
86+
<InputAny
7587
ref={inputRef}
7688
value={localValue}
77-
onChange={(e) => setLocalValue(e.target.value)}
89+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
90+
didUserEditRef.current = true
91+
setLocalValue(e.target.value)
92+
}}
7893
onBlur={handleBlur}
7994
onFocus={handleFocus}
8095
onKeyDown={handleKeyDown}

0 commit comments

Comments
 (0)