@@ -652,6 +652,9 @@ export function App() {
652652 const [ shortcutDraft , setShortcutDraft ] = useState ( "CommandOrControl+Shift+Space" ) ;
653653 const [ activeModelDraft , setActiveModelDraft ] = useState ( modelKey ( "search" , "" ) ) ;
654654 const [ providerKeyDrafts , setProviderKeyDrafts ] = useState < Record < CloudProviderId , string > > ( ( ) => buildProviderDrafts ( ) ) ;
655+ const [ braveSearchKeyDraft , setBraveSearchKeyDraft ] = useState ( "" ) ;
656+ const [ toolFetchUrlEnabled , setToolFetchUrlEnabled ] = useState ( true ) ;
657+ const [ toolWebSearchEnabled , setToolWebSearchEnabled ] = useState ( true ) ;
655658 const [ selectedCloudModelsDraft , setSelectedCloudModelsDraft ] = useState < Record < CloudProviderId , string [ ] > > ( ( ) => normalizeSelectedCloudModels ( ) ) ;
656659 const [ openRouterModelDraft , setOpenRouterModelDraft ] = useState ( "" ) ;
657660 const [ customLocalModelDraft , setCustomLocalModelDraft ] = useState ( "" ) ;
@@ -825,6 +828,8 @@ export function App() {
825828 setSelectedCloudModelsDraft ( nextStatus . selectedCloudModels ) ;
826829 setOllamaStatus ( nextOllamaStatus ) ;
827830 setShortcutDraft ( nextStatus . shortcut ) ;
831+ setToolFetchUrlEnabled ( nextStatus . toolToggles . fetchUrl ) ;
832+ setToolWebSearchEnabled ( nextStatus . toolToggles . webSearch ) ;
828833 setSettingsMode ( nextStatus . preferredMode === "local" ? "local" : "cloud" ) ;
829834 const hasActiveCloudKey = Boolean ( nextStatus . cloudProviderKeys ?. [ nextStatus . activeCloudProvider ] ) ;
830835 setActiveModelDraft (
@@ -1191,6 +1196,14 @@ export function App() {
11911196 pendingDeltasRef . current . clear ( ) ;
11921197 } , [ ] ) ;
11931198
1199+ useEffect ( ( ) => {
1200+ if ( ! activeNoteId ) return ;
1201+ const timer = setTimeout ( ( ) => {
1202+ void saveNote ( activeNoteId , noteTitleDraft , noteContentDraft ) ;
1203+ } , 2000 ) ;
1204+ return ( ) => clearTimeout ( timer ) ;
1205+ } , [ activeNoteId , noteTitleDraft , noteContentDraft ] ) ;
1206+
11941207 async function stopPendingResponse ( nextError ?: string , refreshAfterStop = true ) {
11951208 streamSequenceRef . current += 1 ;
11961209 const activeStreamId = activeStreamIdRef . current ;
@@ -1355,6 +1368,15 @@ export function App() {
13551368 } ) ;
13561369 }
13571370
1371+ async function saveBraveSearchKey ( ) {
1372+ await persistConfig ( { braveSearchApiKey : braveSearchKeyDraft } ) ;
1373+ setBraveSearchKeyDraft ( "" ) ; // Clear after save since we don't show the actual key
1374+ }
1375+
1376+ async function saveToolToggles ( patch : { fetchUrl ?: boolean ; webSearch ?: boolean } ) {
1377+ await persistConfig ( { toolToggles : patch } ) ;
1378+ }
1379+
13581380 async function toggleSelectedCloudModel ( providerId : CloudProviderId , modelId : string ) {
13591381 const current = selectedCloudModelsDraft [ providerId ] ?? [ ] ;
13601382 const next = current . includes ( modelId )
@@ -1783,6 +1805,7 @@ export function App() {
17831805 }
17841806
17851807 async function deleteThread ( id : string ) {
1808+ if ( ! confirm ( "Delete this conversation?" ) ) return ;
17861809 try {
17871810 setError ( null ) ;
17881811 await getRobinBridge ( ) . chat . deleteThread ( id ) ;
@@ -1990,9 +2013,6 @@ export function App() {
19902013 onClick = { ( ) => { void selectThread ( thread . id ) ; setSidebarTab ( "chats" ) ; setScreen ( "chat" ) ; } }
19912014 onContextMenu = { ( event ) => {
19922015 event . preventDefault ( ) ;
1993- if ( ! window . confirm ( "Delete this chat?" ) ) {
1994- return ;
1995- }
19962016 void deleteThread ( thread . id ) ;
19972017 } }
19982018 >
@@ -2112,6 +2132,45 @@ export function App() {
21122132 } ) }
21132133 </ div >
21142134
2135+ < p className = "setting-title" style = { { marginTop : 20 } } > Tools</ p >
2136+ < div className = "provider-list" >
2137+ < article className = "provider-row" >
2138+ < div className = "provider-row-label" >
2139+ < p className = "provider-name" > Brave Search</ p >
2140+ </ div >
2141+ < input
2142+ className = "field-input provider-key-input"
2143+ type = "text"
2144+ placeholder = { status ?. braveSearchKeyConfigured ? "Key configured ✓" : "API key for web search" }
2145+ value = { braveSearchKeyDraft }
2146+ autoCapitalize = "off"
2147+ autoCorrect = "off"
2148+ spellCheck = { false }
2149+ onChange = { ( e ) => setBraveSearchKeyDraft ( e . target . value ) }
2150+ onBlur = { ( ) => { if ( braveSearchKeyDraft . trim ( ) ) void saveBraveSearchKey ( ) ; } }
2151+ onKeyDown = { ( e ) => { if ( e . key === "Enter" ) { e . preventDefault ( ) ; if ( braveSearchKeyDraft . trim ( ) ) void saveBraveSearchKey ( ) ; } } }
2152+ />
2153+ </ article >
2154+ < article className = "provider-row" >
2155+ < div className = "provider-row-label" >
2156+ < p className = "provider-name" > Fetch URL</ p >
2157+ </ div >
2158+ < label className = "toggle-switch" >
2159+ < input type = "checkbox" checked = { toolFetchUrlEnabled } onChange = { ( e ) => { setToolFetchUrlEnabled ( e . target . checked ) ; void saveToolToggles ( { fetchUrl : e . target . checked } ) ; } } />
2160+ < span className = "toggle-slider" />
2161+ </ label >
2162+ </ article >
2163+ < article className = "provider-row" >
2164+ < div className = "provider-row-label" >
2165+ < p className = "provider-name" > Web Search</ p >
2166+ </ div >
2167+ < label className = "toggle-switch" >
2168+ < input type = "checkbox" checked = { toolWebSearchEnabled } onChange = { ( e ) => { setToolWebSearchEnabled ( e . target . checked ) ; void saveToolToggles ( { webSearch : e . target . checked } ) ; } } />
2169+ < span className = "toggle-slider" />
2170+ </ label >
2171+ </ article >
2172+ </ div >
2173+
21152174 < div className = "settings-cloud-models-panel" >
21162175 < p className = "setting-title setting-title-sub" > Models Visible In Chat</ p >
21172176 < div className = "settings-cloud-provider-groups" >
0 commit comments