@@ -6,6 +6,7 @@ import { posthog } from "@/shared/analytics/posthog";
66
77import useWallSessionQuery from "./utils/WallSessionQuery" ;
88import { usePlacementStore } from "./store" ;
9+ import { useHistoryStore } from "./history" ;
910import type { SessionHoldInstance } from "./store" ;
1011import { useHandleLoadSession } from "./utils/HandleLoadSession" ;
1112
@@ -33,6 +34,8 @@ function EditorApp() {
3334 const setColoredTexture = usePlacementStore ( ( s ) => s . setColoredTexture ) ;
3435 const setHasUnsavedChanges = usePlacementStore ( ( s ) => s . setHasUnsavedChanges ) ;
3536 const hasUnsavedChanges = usePlacementStore ( ( s ) => s . hasUnsavedChanges ) ;
37+ const canUndo = useHistoryStore ( ( s ) => s . past . length > 0 ) ;
38+ const canRedo = useHistoryStore ( ( s ) => s . future . length > 0 ) ;
3639
3740 const { data, isLoading, error, isError } = useQuery ( {
3841 queryKey : [ "wallsession" , wallId ] ,
@@ -53,9 +56,34 @@ function EditorApp() {
5356 setHoldColors ( { } ) ;
5457 setColoredTexture ( true ) ;
5558 setHasUnsavedChanges ( false ) ;
59+ useHistoryStore . getState ( ) . clear ( ) ;
5660 }
5761 } , [ wallId , setObjects , setWallColors , setHoldColors , setColoredTexture , setHasUnsavedChanges ] ) ;
5862
63+ useEffect ( ( ) => {
64+ const handleKeyDown = ( e : KeyboardEvent ) => {
65+ const target = e . target as HTMLElement | null ;
66+ if (
67+ target &&
68+ ( target . tagName === "INPUT" ||
69+ target . tagName === "TEXTAREA" ||
70+ target . isContentEditable )
71+ ) {
72+ return ;
73+ }
74+ if ( ( e . ctrlKey || e . metaKey ) && e . key . toLowerCase ( ) === "z" ) {
75+ e . preventDefault ( ) ;
76+ if ( e . shiftKey ) {
77+ useHistoryStore . getState ( ) . redo ( ) ;
78+ } else {
79+ useHistoryStore . getState ( ) . undo ( ) ;
80+ }
81+ }
82+ } ;
83+ window . addEventListener ( "keydown" , handleKeyDown ) ;
84+ return ( ) => window . removeEventListener ( "keydown" , handleKeyDown ) ;
85+ } , [ ] ) ;
86+
5987 useEffect ( ( ) => {
6088 if ( session_data ?. id && wallModels . length > 0 && ! sessionOpenedRef . current ) {
6189 sessionOpenedRef . current = true ;
@@ -132,6 +160,31 @@ function EditorApp() {
132160 < div className = "w-fit max-w-full rounded-xl bg-surface-low/90 p-2 shadow-[0_8px_32px_0_rgba(0,0,0,0.5)] backdrop-blur-md" >
133161 < FileManager session_data = { session_data } />
134162 </ div >
163+ < div className = "flex w-fit gap-1 self-start rounded-xl bg-surface-low/90 p-1 shadow-[0_8px_32px_0_rgba(0,0,0,0.5)] backdrop-blur-md" >
164+ { ( [
165+ { id : "undo" , icon : "undo" , label : t ( "editor.undo" ) , hint : "Ctrl+Z" , enabled : canUndo , action : ( ) => useHistoryStore . getState ( ) . undo ( ) } ,
166+ { id : "redo" , icon : "redo" , label : t ( "editor.redo" ) , hint : "Ctrl+Shift+Z" , enabled : canRedo , action : ( ) => useHistoryStore . getState ( ) . redo ( ) } ,
167+ ] ) . map ( ( tool ) => (
168+ < div key = { tool . id } className = "group relative" >
169+ < button
170+ disabled = { ! tool . enabled }
171+ onClick = { tool . action }
172+ className = { `w-10 h-10 flex shrink-0 items-center justify-center rounded-lg transition-all ${
173+ tool . enabled
174+ ? "text-on-surface-variant hover:text-on-surface hover:bg-surface-high active:scale-95 cursor-pointer"
175+ : "text-on-surface-variant opacity-40 cursor-not-allowed"
176+ } `}
177+ >
178+ < span className = "material-symbols-outlined" > { tool . icon } </ span >
179+ </ button >
180+ < div className = "absolute left-full top-1/2 ml-2 -translate-y-1/2 whitespace-nowrap rounded-lg bg-surface-high px-2.5 py-1.5 text-xs text-on-surface-variant opacity-0 shadow-[0_4px_16px_0_rgba(0,0,0,0.4)] transition-opacity group-hover:opacity-100 pointer-events-none" >
181+ < span className = "font-medium text-on-surface" > { tool . label } </ span >
182+ < span className = "mx-1.5 opacity-40" > ·</ span >
183+ { tool . hint }
184+ </ div >
185+ </ div >
186+ ) ) }
187+ </ div >
135188 < div className = "flex w-fit flex-col gap-1 self-start rounded-xl bg-surface-low/90 p-1 shadow-[0_8px_32px_0_rgba(0,0,0,0.5)] backdrop-blur-md" >
136189 { transformTools . map ( ( tool ) => (
137190 < div key = { tool . id } className = "group relative" >
0 commit comments