11"use client" ;
22
33import { useTranslations } from "next-intl" ;
4- import { useState , useEffect , useRef , useMemo , type KeyboardEvent } from "react" ;
4+ import {
5+ useState ,
6+ useEffect ,
7+ useRef ,
8+ useMemo ,
9+ useCallback ,
10+ type KeyboardEvent as ReactKeyboardEvent ,
11+ } from "react" ;
512import { useParams } from "next/navigation" ;
613import { useSimulationStore , WorldData } from "@/stores/simulation" ;
714import { CommandPalette } from "@/components/CommandPalette" ;
@@ -54,6 +61,7 @@ export default function HomePage() {
5461 const fetchedCount = useRef ( 0 ) ;
5562 const { createWorld, autoWorlds, fetchAutoWorlds, worldTags, tagFilter, setTagFilter } = useSimulationStore ( ) ;
5663 const createHintText = locale === "ko" ? "팁: 빠르게 생성하려면 Ctrl/Cmd + Enter" : "Tip: Press Ctrl/Cmd + Enter to create quickly." ;
64+ const shortcutHintText = locale === "ko" ? "단축키: Ctrl/Cmd + B(북마크 열기), Ctrl/Cmd + R(예시 회전)" : "Shortcuts: Ctrl/Cmd + B (open bookmarks), Ctrl/Cmd + R (rotate example)." ;
5765 const { setDrawerOpen } = useBookmarkStore ( ) ;
5866 const { fetchNode } = useTaxonomyStore ( ) ;
5967 const [ taxonomyWorldFilter , setTaxonomyWorldFilter ] = useState < string | null > ( null ) ;
@@ -166,6 +174,10 @@ export default function HomePage() {
166174 return ( ) => clearInterval ( interval ) ;
167175 } , [ fetchAutoWorlds ] ) ;
168176
177+ const handleExampleRotate = useCallback ( ( ) => {
178+ setSeedPrompt ( examples [ exampleIndex % examples . length ] ) ;
179+ } , [ exampleIndex , examples ] ) ;
180+
169181 const handleCreate = async ( ) => {
170182 if ( ! seedPrompt . trim ( ) ) return ;
171183 setCreating ( true ) ;
@@ -191,17 +203,45 @@ export default function HomePage() {
191203 seedPromptInputRef . current ?. focus ( ) ;
192204 } ;
193205
194- const handleSeedKeyDown = ( event : KeyboardEvent < HTMLTextAreaElement > ) => {
206+ const handleSeedKeyDown = ( event : ReactKeyboardEvent < HTMLTextAreaElement > ) => {
195207 if ( event . key === "Enter" && ( event . ctrlKey || event . metaKey ) ) {
196208 event . preventDefault ( ) ;
197209 handleCreate ( ) ;
198210 }
199211 } ;
200212
201213 const handleExampleClick = ( ) => {
202- setSeedPrompt ( examples [ exampleIndex % examples . length ] ) ;
214+ handleExampleRotate ( ) ;
203215 } ;
204216
217+ const handleGlobalKeyDown = useCallback ( ( event : globalThis . KeyboardEvent ) => {
218+ const activeTag = ( event . target as HTMLElement | null ) ?. tagName ;
219+ const isEditing = activeTag ? [ "INPUT" , "TEXTAREA" , "SELECT" ] . includes ( activeTag ) : false ;
220+
221+ if ( isEditing ) return ;
222+
223+ if ( ( event . metaKey || event . ctrlKey ) && event . key . toLowerCase ( ) === "b" ) {
224+ event . preventDefault ( ) ;
225+ setDrawerOpen ( true ) ;
226+ setToast ( "Bookmarks drawer opened" ) ;
227+ setTimeout ( ( ) => setToast ( null ) , 1600 ) ;
228+ }
229+
230+ if ( event . key . toLowerCase ( ) === "r" && ( event . metaKey || event . ctrlKey ) ) {
231+ event . preventDefault ( ) ;
232+ handleExampleRotate ( ) ;
233+ setToast ( "Example rotated" ) ;
234+ setTimeout ( ( ) => setToast ( null ) , 1000 ) ;
235+ }
236+ } , [ handleExampleRotate , setDrawerOpen , setToast ] ) ;
237+
238+ useEffect ( ( ) => {
239+ if ( typeof document === "undefined" ) return ;
240+
241+ document . addEventListener ( "keydown" , handleGlobalKeyDown ) ;
242+ return ( ) => document . removeEventListener ( "keydown" , handleGlobalKeyDown ) ;
243+ } , [ handleGlobalKeyDown ] ) ;
244+
205245 const isSeedNearLimit = seedPrompt . length >= Math . floor ( MAX_SEED_PROMPT_LENGTH * 0.9 ) ;
206246
207247 const seedCounterLabel =
@@ -416,6 +456,9 @@ export default function HomePage() {
416456 < p className = "text-[11px] text-hud-label" >
417457 { createHintText }
418458 </ p >
459+ < p className = "text-[11px] text-hud-label" >
460+ { shortcutHintText }
461+ </ p >
419462 < div className = "flex flex-wrap gap-2" >
420463 < button
421464 onClick = { handleCreate }
0 commit comments