11import { skipToken } from '@reduxjs/toolkit/query' ;
2- import { useState } from " react" ;
3- import { Alert , Button , Form } from " react-bootstrap" ;
4- import ListFiltersHeader from " ../../shared/ListFiltersHeader" ;
2+ import { useState } from ' react' ;
3+ import { Alert , Button , Form } from ' react-bootstrap' ;
4+ import ListFiltersHeader from ' ../../shared/ListFiltersHeader' ;
55import useAppParams from '../../shared/hooks/useAppParams' ;
66import {
77 useGetDoNotLoadConfigOnNextBootQuery ,
88 useSetDoNotLoadConfigOnNextBootMutation ,
99 useSetLoadConfigMutation ,
1010 useSetRestartMutation
11- } from " ../../store/apiSlice" ;
11+ } from ' ../../store/apiSlice' ;
1212import { selectSearchText } from '../../store/debugConsole/debugConsoleSelectors' ;
1313import { debugConsoleActions } from '../../store/debugConsole/debugConsoleSlice' ;
1414import { useAppDispatch , useAppSelector } from '../../store/hooks' ;
1515import type { RootState } from '../../store/store' ;
16- import ConsoleWindow from " ./ConsoleWindow" ;
17- import { DebugFilters } from " ./DebugFilters" ;
16+ import ConsoleWindow from ' ./ConsoleWindow' ;
17+ import { DebugFilters } from ' ./DebugFilters' ;
1818import MinimumLogLevelDropdown from './MinimumLogLevelDropdown' ;
19- import RestartConfirmModal from " ./RestartConfirmModal" ;
20- import { useFilteredMessages } from " ./hooks/useFilteredMessages" ;
19+ import RestartConfirmModal from ' ./RestartConfirmModal' ;
20+ import { useFilteredMessages } from ' ./hooks/useFilteredMessages' ;
2121
2222const DebugConsole = ( { isConnected, join, stop, clear} : DebugConsoleProps ) => {
2323 //* HOOKS ***********************************************************/
@@ -41,6 +41,23 @@ const DebugConsole = ({isConnected, join, stop, clear}: DebugConsoleProps) => {
4141 //* EFFECTS *********************************************************/
4242 const filteredItems = useFilteredMessages ( messages ) ;
4343
44+ const exportFilteredItems = ( ) => {
45+ const content = filteredItems
46+ . map ( ( item ) => `${ item . Timestamp } [${ item . Level } ]${ item . Properties ?. Key ? ` [${ item . Properties . Key } ]` : '' } ${ item . RenderedMessage } ` )
47+ . join ( '\n' ) ;
48+ const blob = new Blob ( [ content ] , { type : 'text/plain' } ) ;
49+ const url = URL . createObjectURL ( blob ) ;
50+ const a = document . createElement ( 'a' ) ;
51+ a . href = url ;
52+ a . download = `debug-log-${ new Date ( ) . toISOString ( ) . replace ( / [: .] / g, '-' ) } .log` ;
53+ document . body . appendChild ( a ) ;
54+ a . click ( ) ;
55+ setTimeout ( ( ) => {
56+ document . body . removeChild ( a ) ;
57+ URL . revokeObjectURL ( url ) ;
58+ } , 0 ) ;
59+ } ;
60+
4461 const clickRestart = ( ) => {
4562 setShowModal ( true ) ;
4663 } ;
@@ -104,6 +121,15 @@ const DebugConsole = ({isConnected, join, stop, clear}: DebugConsoleProps) => {
104121 >
105122 Clear Console Trace
106123 </ Button >
124+ < Button
125+ className = "mx-1"
126+ variant = "primary"
127+ size = "sm"
128+ onClick = { exportFilteredItems }
129+ disabled = { filteredItems . length === 0 }
130+ >
131+ Export Log
132+ </ Button >
107133 < Button
108134 className = "mx-1"
109135 variant = "primary"
0 commit comments