@@ -2,19 +2,23 @@ import { Input } from "@chakra-ui/react"
22import type React from "react"
33import { useEffect , useRef , useState } from "react"
44import { useEditorStore } from "../../store"
5+ import { formatTemplateNameForUI } from "../../utils"
56
67const UNTITLED = "Untitled Template"
8+ const InputAny = Input as unknown as React . ElementType
79
810export 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