1- import React , { useState , useEffect } from 'react' ;
2- import { Container , Paper , Box , Button , Typography , Tabs , Tab , Link as MuiLink , Tooltip , IconButton , Snackbar , Grid , Accordion , AccordionSummary , AccordionDetails , Modal } from '@mui/material' ;
1+ import React , { useState } from 'react' ;
2+ import { Container , Paper , Box , Button , Typography , Link as MuiLink , Snackbar , IconButton , Grid , Accordion , AccordionSummary , AccordionDetails } from '@mui/material' ;
33import { ThemeProvider } from '@mui/material/styles' ;
4- import DescriptionIcon from '@mui/icons-material/Description' ;
5- import LibraryBooksIcon from '@mui/icons-material/LibraryBooks' ;
6- import BlockIcon from '@mui/icons-material/Block' ;
7- import ContentCopyIcon from '@mui/icons-material/ContentCopy' ;
8- import DownloadIcon from '@mui/icons-material/Download' ;
94import ExpandMoreIcon from '@mui/icons-material/ExpandMore' ;
105import CloseIcon from '@mui/icons-material/Close' ;
116import GitHubIcon from '@mui/icons-material/GitHub' ;
12- import { BrowserRouter as Router , Routes , Route , Link , useLocation } from 'react-router-dom' ;
7+ import { BrowserRouter as Router , Routes , Route } from 'react-router-dom' ;
138import theme from './theme' ;
149import ContextForm from './components/ContextForm' ;
1510import ContextDocsForm from './components/ContextDocsForm' ;
1611import ContextIgnoreForm from './components/ContextIgnoreForm' ;
12+ import PromptViewer from './components/PromptViewer' ;
13+ import NavTabs from './components/NavTabs' ;
14+ import SpecificationModal from './components/SpecificationModal' ;
15+ import ActionButtons from './components/ActionButtons' ;
16+ import { handleCopyToClipboard , handleDownload } from './utils/helpers' ;
17+ import usePrompts from './hooks/usePrompts' ;
18+ import useSnackbar from './hooks/useSnackbar' ;
1719import './App.css' ;
1820
1921// Conditional base path for GitHub Pages
2022const BASE_PATH = process . env . NODE_ENV === 'production' ? '/codebase-context-spec' : '' ;
2123
22- const PromptViewer : React . FC < { title : string , content : string , onCopy : ( ) => void } > = ( { title, content, onCopy } ) => {
23- return (
24- < Box mb = { 2 } >
25- < Box display = "flex" justifyContent = "space-between" alignItems = "center" mb = { 1 } >
26- < Typography variant = "h6" > { title } </ Typography >
27- < Button startIcon = { < ContentCopyIcon /> } onClick = { onCopy } >
28- Copy
29- </ Button >
30- </ Box >
31- < Box className = "code-preview" style = { { maxHeight : '300px' , overflow : 'auto' } } >
32- < pre style = { { whiteSpace : 'pre-wrap' , wordBreak : 'break-word' } } >
33- { content }
34- </ pre >
35- </ Box >
36- </ Box >
37- ) ;
38- } ;
39-
40- const SpecificationViewer : React . FC < { content : string , onCopy : ( ) => void } > = ( { content, onCopy } ) => {
41- return (
42- < Box height = "100%" display = "flex" flexDirection = "column" >
43- < Box display = "flex" justifyContent = "flex-end" mb = { 2 } >
44- < Button startIcon = { < ContentCopyIcon /> } onClick = { onCopy } >
45- Copy Specification
46- </ Button >
47- </ Box >
48- < Box flexGrow = { 1 } overflow = "auto" >
49- < pre style = { { whiteSpace : 'pre-wrap' , wordBreak : 'break-word' , margin : 0 } } >
50- { content }
51- </ pre >
52- </ Box >
53- </ Box >
54- ) ;
55- } ;
56-
5724const App : React . FC = ( ) => {
5825 const [ generatedContent , setGeneratedContent ] = useState ( '' ) ;
59- const [ snackbarOpen , setSnackbarOpen ] = useState ( false ) ;
60- const [ snackbarMessage , setSnackbarMessage ] = useState ( '' ) ;
61- const [ codebaseContextContent , setCodebaseContextContent ] = useState ( '' ) ;
62- const [ codingAssistantPromptContent , setCodingAssistantPromptContent ] = useState ( '' ) ;
63- const [ generateContextPromptContent , setGenerateContextPromptContent ] = useState ( '' ) ;
6426 const [ specModalOpen , setSpecModalOpen ] = useState ( false ) ;
6527
66- useEffect ( ( ) => {
67- const fetchPrompts = async ( ) => {
68- try {
69- const [ codebaseContext , codingAssistantPrompt , generateContextPrompt ] = await Promise . all ( [
70- fetch ( 'https://raw.githubusercontent.com/Agentic-Insights/codebase-context-spec/main/CODEBASE-CONTEXT.md' ) . then ( res => res . text ( ) ) ,
71- fetch ( 'https://raw.githubusercontent.com/Agentic-Insights/codebase-context-spec/main/CODING-ASSISTANT-PROMPT.md' ) . then ( res => res . text ( ) ) ,
72- fetch ( 'https://raw.githubusercontent.com/Agentic-Insights/codebase-context-spec/main/GENERATE-CONTEXT-PROMPT.md' ) . then ( res => res . text ( ) )
73- ] ) ;
74-
75- setCodebaseContextContent ( codebaseContext ) ;
76- setCodingAssistantPromptContent ( codingAssistantPrompt ) ;
77- setGenerateContextPromptContent ( generateContextPrompt ) ;
78- } catch ( error ) {
79- console . error ( 'Error fetching prompts:' , error ) ;
80- setSnackbarMessage ( 'Error fetching prompts' ) ;
81- setSnackbarOpen ( true ) ;
82- }
83- } ;
84-
85- fetchPrompts ( ) ;
86- } , [ ] ) ;
28+ const { snackbarOpen, snackbarMessage, showSnackbar, closeSnackbar } = useSnackbar ( ) ;
29+ const { codebaseContext, generateContextPrompt, codingAssistantPrompt } = usePrompts (
30+ showSnackbar ,
31+ ( open : boolean ) => open ? showSnackbar ( '' ) : closeSnackbar ( )
32+ ) ;
8733
8834 const handleFormSubmit = ( content : string ) => {
8935 setGeneratedContent ( content ) ;
9036 } ;
9137
92- const handleCopyToClipboard = ( content : string ) => {
93- navigator . clipboard . writeText ( content ) . then ( ( ) => {
94- setSnackbarMessage ( 'Copied to clipboard!' ) ;
95- setSnackbarOpen ( true ) ;
96- } , ( err ) => {
97- console . error ( 'Could not copy text: ' , err ) ;
98- } ) ;
99- } ;
100-
101- const handleDownload = ( ) => {
102- const element = document . createElement ( 'a' ) ;
103- const file = new Blob ( [ generatedContent ] , { type : 'text/plain' } ) ;
104- element . href = URL . createObjectURL ( file ) ;
105- element . download = 'generated_context.md' ;
106- document . body . appendChild ( element ) ;
107- element . click ( ) ;
108- document . body . removeChild ( element ) ;
109- setSnackbarMessage ( 'File downloaded!' ) ;
110- setSnackbarOpen ( true ) ;
111- } ;
112-
113- const handleCloseSnackbar = ( event ?: React . SyntheticEvent | Event , reason ?: string ) => {
114- if ( reason === 'clickaway' ) {
115- return ;
116- }
117- setSnackbarOpen ( false ) ;
118- } ;
119-
120- const handleOpenSpecModal = ( ) => {
121- setSpecModalOpen ( true ) ;
122- } ;
123-
124- const handleCloseSpecModal = ( ) => {
125- setSpecModalOpen ( false ) ;
126- } ;
127-
12838 return (
12939 < ThemeProvider theme = { theme } >
13040 < Router basename = { BASE_PATH } >
@@ -135,7 +45,7 @@ const App: React.FC = () => {
13545 Codebase Context Editor
13646 </ Typography >
13747 < Box display = "flex" alignItems = "center" >
138- < Button variant = "outlined" onClick = { handleOpenSpecModal } sx = { { mr : 2 } } >
48+ < Button variant = "outlined" onClick = { ( ) => setSpecModalOpen ( true ) } sx = { { mr : 2 } } >
13949 View Latest Specification
14050 </ Button >
14151 < MuiLink href = "https://github.com/Agentic-Insights/codebase-context-spec" target = "_blank" rel = "noopener noreferrer" >
@@ -154,16 +64,20 @@ const App: React.FC = () => {
15464 < Grid container spacing = { 2 } >
15565 < Grid item xs = { 12 } md = { 6 } >
15666 < PromptViewer
157- title = "CODING-ASSISTANT-PROMPT.md"
158- content = { codingAssistantPromptContent }
159- onCopy = { ( ) => handleCopyToClipboard ( codingAssistantPromptContent ) }
67+ title = "Generate Context Prompt"
68+ subtitle = "GENERATE-CONTEXT-PROMPT.md"
69+ explanation = { generateContextPrompt . explanation }
70+ content = { generateContextPrompt . content }
71+ onCopy = { ( ) => handleCopyToClipboard ( generateContextPrompt . content , showSnackbar , ( ) => showSnackbar ( '' ) ) }
16072 />
16173 </ Grid >
16274 < Grid item xs = { 12 } md = { 6 } >
16375 < PromptViewer
164- title = "GENERATE-CONTEXT-PROMPT.md"
165- content = { generateContextPromptContent }
166- onCopy = { ( ) => handleCopyToClipboard ( generateContextPromptContent ) }
76+ title = "Coding Assistant Prompt"
77+ subtitle = "CODING-ASSISTANT-PROMPT.md"
78+ explanation = { codingAssistantPrompt . explanation }
79+ content = { codingAssistantPrompt . content }
80+ onCopy = { ( ) => handleCopyToClipboard ( codingAssistantPrompt . content , showSnackbar , ( ) => showSnackbar ( '' ) ) }
16781 />
16882 </ Grid >
16983 </ Grid >
@@ -177,66 +91,19 @@ const App: React.FC = () => {
17791 < Route path = "/contextignore" element = { < ContextIgnoreForm onSubmit = { handleFormSubmit } /> } />
17892 </ Routes >
17993 </ Box >
180- < Box className = "action-buttons" >
181- < Tooltip title = "Copy to clipboard" arrow >
182- < Button
183- onClick = { ( ) => handleCopyToClipboard ( generatedContent ) }
184- variant = "contained"
185- startIcon = { < ContentCopyIcon /> }
186- disabled = { ! generatedContent }
187- >
188- Copy
189- </ Button >
190- </ Tooltip >
191- < Tooltip title = "Download file" arrow >
192- < Button
193- onClick = { handleDownload }
194- variant = "contained"
195- startIcon = { < DownloadIcon /> }
196- disabled = { ! generatedContent }
197- >
198- Download
199- </ Button >
200- </ Tooltip >
201- </ Box >
94+ < ActionButtons
95+ generatedContent = { generatedContent }
96+ onCopy = { ( ) => handleCopyToClipboard ( generatedContent , showSnackbar , ( ) => showSnackbar ( '' ) ) }
97+ onDownload = { ( ) => handleDownload ( generatedContent , 'generated_context.md' , showSnackbar , ( ) => showSnackbar ( '' ) ) }
98+ />
20299 </ Paper >
203100
204- < Modal
101+ < SpecificationModal
205102 open = { specModalOpen }
206- onClose = { handleCloseSpecModal }
207- aria-labelledby = "spec-modal-title"
208- >
209- < Box
210- sx = { {
211- position : 'absolute' ,
212- top : '50%' ,
213- left : '50%' ,
214- transform : 'translate(-50%, -50%)' ,
215- width : '80%' ,
216- height : '80%' ,
217- bgcolor : 'background.paper' ,
218- boxShadow : 24 ,
219- p : 4 ,
220- display : 'flex' ,
221- flexDirection : 'column' ,
222- } }
223- >
224- < Box display = "flex" justifyContent = "space-between" alignItems = "center" mb = { 2 } >
225- < Typography id = "spec-modal-title" variant = "h6" component = "h2" >
226- Latest Specification (CODEBASE-CONTEXT.md)
227- </ Typography >
228- < IconButton onClick = { handleCloseSpecModal } size = "small" >
229- < CloseIcon />
230- </ IconButton >
231- </ Box >
232- < Box flexGrow = { 1 } overflow = "hidden" >
233- < SpecificationViewer
234- content = { codebaseContextContent }
235- onCopy = { ( ) => handleCopyToClipboard ( codebaseContextContent ) }
236- />
237- </ Box >
238- </ Box >
239- </ Modal >
103+ onClose = { ( ) => setSpecModalOpen ( false ) }
104+ content = { codebaseContext . content }
105+ onCopy = { ( ) => handleCopyToClipboard ( codebaseContext . content , showSnackbar , ( ) => showSnackbar ( '' ) ) }
106+ />
240107
241108 < Snackbar
242109 anchorOrigin = { {
@@ -245,14 +112,14 @@ const App: React.FC = () => {
245112 } }
246113 open = { snackbarOpen }
247114 autoHideDuration = { 2000 }
248- onClose = { handleCloseSnackbar }
115+ onClose = { closeSnackbar }
249116 message = { snackbarMessage }
250117 action = {
251118 < IconButton
252119 size = "small"
253120 aria-label = "close"
254121 color = "inherit"
255- onClick = { handleCloseSnackbar }
122+ onClick = { closeSnackbar }
256123 >
257124 < CloseIcon fontSize = "small" />
258125 </ IconButton >
@@ -264,46 +131,4 @@ const App: React.FC = () => {
264131 ) ;
265132} ;
266133
267- const NavTabs : React . FC = ( ) => {
268- const location = useLocation ( ) ;
269- const currentForm = getCurrentForm ( location . pathname ) ;
270-
271- return (
272- < Tabs value = { currentForm } aria-label = "file type tabs" variant = "fullWidth" >
273- < Tab
274- icon = { < DescriptionIcon /> }
275- label = ".context.md"
276- component = { Link }
277- to = "/"
278- sx = { { textTransform : 'lowercase' } }
279- />
280- < Tab
281- icon = { < LibraryBooksIcon /> }
282- label = ".contextdocs.md"
283- component = { Link }
284- to = "/contextdocs"
285- sx = { { textTransform : 'lowercase' } }
286- />
287- < Tab
288- icon = { < BlockIcon /> }
289- label = ".contextignore"
290- component = { Link }
291- to = "/contextignore"
292- sx = { { textTransform : 'lowercase' } }
293- />
294- </ Tabs >
295- ) ;
296- } ;
297-
298- const getCurrentForm = ( pathname : string ) => {
299- switch ( pathname ) {
300- case '/contextdocs' :
301- return 1 ;
302- case '/contextignore' :
303- return 2 ;
304- default :
305- return 0 ;
306- }
307- } ;
308-
309134export default App ;
0 commit comments