1- import { useCallback , useEffect , useRef , useState } from "react" ;
1+ import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
22import { BackgroundGrid } from "./shared/components/BackgroundGrid" ;
33import { EditorSurface } from "./features/editor/components/EditorSurface" ;
44import { DocsDialog } from "./shared/components/DocsDialog" ;
55import { FooterLink } from "./shared/components/FooterLink" ;
66import { MenuBar } from "./shared/components/MenuBar" ;
77import { EngineErrorNotice } from "./shared/components/EngineErrorNotice" ;
88import { ExplorerSidebar } from "./features/explorer/components/ExplorerSidebar" ;
9+ import { CustomPromptDialog } from "./features/explorer/components/CustomPromptDialog" ;
910import { DesktopShortcut } from "./shared/components/DesktopShortcut" ;
1011import { localPacks } from "./content/localPacks" ;
1112import type { PromptPackItem } from "./content/types" ;
@@ -27,13 +28,18 @@ import { useSessionRestart } from "./features/session/hooks/useSessionRestart";
2728import { BottomStatsPanel } from "./features/stats/components/BottomStatsPanel" ;
2829import { appStyles } from "./app/App.styles" ;
2930import { useSyntaxThemeStore } from "./shared/syntax/themeStore" ;
31+ import { getCustomPrompts , deleteCustomPrompt } from "./features/explorer/storage/customPromptsRepository" ;
3032
3133const promptEntries : PromptPackItem [ ] = localPacks . flatMap ( ( pack ) => pack . items ) ;
3234
3335const fallbackEntry : PromptPackItem | null = promptEntries [ 0 ] ?? null ;
3436
3537export default function App ( ) {
3638 const [ isDocsOpen , setDocsOpen ] = useState ( false ) ;
39+ const [ customPrompts , setCustomPrompts ] = useState < PromptPackItem [ ] > ( [ ] ) ;
40+ const [ isCustomDialogOpen , setCustomDialogOpen ] = useState ( false ) ;
41+ const [ editingPrompt , setEditingPrompt ] = useState < PromptPackItem | null > ( null ) ;
42+
3743 const targetSnippet = useSessionStore ( ( state ) => state . targetSnippet ) ;
3844 const typed = useSessionStore ( ( state ) => state . typed ) ;
3945 const attempts = useSessionStore ( ( state ) => state . attempts ) ;
@@ -50,18 +56,49 @@ export default function App() {
5056
5157 const textareaRef = useRef < HTMLTextAreaElement | null > ( null ) ;
5258
59+ // Load custom prompts on mount
60+ useEffect ( ( ) => {
61+ setCustomPrompts ( getCustomPrompts ( ) ) ;
62+ } , [ ] ) ;
63+
64+ // Merge built-in and custom prompts
65+ const allPrompts = useMemo (
66+ ( ) => [ ...promptEntries , ...customPrompts ] ,
67+ [ customPrompts ]
68+ ) ;
69+
5370 const { selectedLanguage, setSelectedLanguage } = usePersistedLanguage (
54- promptEntries ,
71+ allPrompts ,
5572 fallbackEntry ?. language
5673 ) ;
5774
5875 const { selectedPromptId, handlePromptSelect : applyPromptSelection } = usePromptSelection ( {
59- prompts : promptEntries ,
76+ prompts : allPrompts ,
6077 language : selectedLanguage ,
6178 fallbackEntry,
6279 onPromptChange : setPrompt ,
6380 } ) ;
6481
82+ // Custom prompt handlers
83+ const handleAddCustomPrompt = useCallback ( ( ) => {
84+ setEditingPrompt ( null ) ;
85+ setCustomDialogOpen ( true ) ;
86+ } , [ ] ) ;
87+
88+ const handleEditCustomPrompt = useCallback ( ( prompt : PromptPackItem ) => {
89+ setEditingPrompt ( prompt ) ;
90+ setCustomDialogOpen ( true ) ;
91+ } , [ ] ) ;
92+
93+ const handleDeleteCustomPrompt = useCallback ( ( id : string ) => {
94+ deleteCustomPrompt ( id ) ;
95+ setCustomPrompts ( getCustomPrompts ( ) ) ;
96+ } , [ ] ) ;
97+
98+ const handleCustomPromptSave = useCallback ( ( ) => {
99+ setCustomPrompts ( getCustomPrompts ( ) ) ;
100+ } , [ ] ) ;
101+
65102 const { isReady : engineReady , error : engineError } = useEngineStatus ( ) ;
66103
67104 useEffect ( ( ) => {
@@ -202,6 +239,7 @@ export default function App() {
202239 < div className = { appStyles . explorerPane ( isExplorerOpen ) } >
203240 < ExplorerSidebar
204241 prompts = { promptEntries }
242+ customPrompts = { customPrompts }
205243 selectedLanguage = { selectedLanguage }
206244 selectedPromptId = { selectedPromptId }
207245 onOpenPrompt = { ( lang , id ) => {
@@ -212,6 +250,9 @@ export default function App() {
212250 applyPromptSelection ( id ) ;
213251 }
214252 } }
253+ onAddCustomPrompt = { handleAddCustomPrompt }
254+ onEditCustomPrompt = { handleEditCustomPrompt }
255+ onDeleteCustomPrompt = { handleDeleteCustomPrompt }
215256 />
216257 </ div >
217258 < BottomStatsPanel
@@ -236,6 +277,12 @@ export default function App() {
236277 < FooterLink href = { GITHUB_URL } onDocsClick = { handleDocsOpen } githubLabel = "Repository" docsLabel = "Documentation" />
237278 </ div >
238279 < DocsDialog open = { isDocsOpen } onClose = { handleDocsClose } />
280+ < CustomPromptDialog
281+ isOpen = { isCustomDialogOpen }
282+ onClose = { ( ) => setCustomDialogOpen ( false ) }
283+ onSave = { handleCustomPromptSave }
284+ existingPrompt = { editingPrompt ?? undefined }
285+ />
239286 </ >
240287 ) ;
241288}
0 commit comments