@@ -219,8 +219,9 @@ export function App() {
219219 await window . openWhisp . showMainWindow ( ) ;
220220 return ;
221221 }
222- if ( current . settings . enhancementLevel !== 'none' && ( ! current . ollamaReachable || ! modelInstalled ) ) {
223- pushStatus ( { phase : 'error' , title : 'Text model unavailable' , detail : 'Start Ollama and install the rewrite model, or set enhancement to No Filter.' } ) ;
222+ const needsOllama = current . settings . enhancementLevel !== 'none' && current . settings . rewriteMode === 'local' ;
223+ if ( needsOllama && ( ! current . ollamaReachable || ! modelInstalled ) ) {
224+ pushStatus ( { phase : 'error' , title : 'Text model unavailable' , detail : 'Start Ollama, install the rewrite model, or switch to Cloud rewrite.' } ) ;
224225 await window . openWhisp . showMainWindow ( ) ;
225226 return ;
226227 }
@@ -846,18 +847,44 @@ function ModelsPage({ bootstrap, busyAction, onAction }: { bootstrap: BootstrapS
846847 < button className = "btn btn-link" onClick = { ( ) => void onAction ( 'refresh' , ( ) => window . openWhisp . refreshOllama ( ) ) } > Refresh</ button >
847848 </ div >
848849 < div className = "setting-row" >
849- < label className = "setting-label" htmlFor = "model-select" > Rewrite model</ label >
850- < select id = "model-select" className = "setting-select" value = { bootstrap . settings . textModel } onChange = { ( e ) => void onAction ( 'settings' , ( ) => window . openWhisp . updateSettings ( { textModel : e . target . value } ) ) } >
851- { bootstrap . ollamaModels . length === 0
852- ? < option value = { bootstrap . settings . textModel } > { bootstrap . settings . textModel } </ option >
853- : bootstrap . ollamaModels . map ( ( m ) => < option key = { m . name } value = { m . name } > { m . name } ({ formatBytes ( m . size ) } )</ option > )
854- }
855- </ select >
850+ < label className = "setting-label" > Rewrite via</ label >
851+ < div className = "source-selector" >
852+ < button className = { `source-btn${ bootstrap . settings . rewriteMode === 'cloud' ? ' source-btn-active' : '' } ` } onClick = { ( ) => void onAction ( 'settings' , ( ) => window . openWhisp . updateSettings ( { rewriteMode : 'cloud' } ) ) } > Cloud</ button >
853+ < button className = { `source-btn${ bootstrap . settings . rewriteMode === 'local' ? ' source-btn-active' : '' } ` } onClick = { ( ) => void onAction ( 'settings' , ( ) => window . openWhisp . updateSettings ( { rewriteMode : 'local' } ) ) } > Local</ button >
854+ </ div >
856855 </ div >
857- { ! bootstrap . recommendedModelInstalled && bootstrap . ollamaReachable && (
858- < button className = "btn btn-sm btn-primary" style = { { marginTop : 10 } } disabled = { busyAction === 'model' } onClick = { ( ) => void onAction ( 'model' , ( ) => window . openWhisp . pullRecommendedModel ( ) ) } >
859- { busyAction === 'model' ? 'Downloading...' : `Download ${ RECOMMENDED_TEXT_MODEL } ` }
860- </ button >
856+ < div className = "setting-hint" >
857+ { bootstrap . settings . rewriteMode === 'cloud' ? 'Fast rewrite via Groq cloud API. Falls back to Ollama when offline.' : 'Local rewrite via Ollama. No data leaves your Mac.' }
858+ </ div >
859+ { bootstrap . settings . rewriteMode === 'cloud' && (
860+ < div className = "setting-row" style = { { marginTop : 12 } } >
861+ < label className = "setting-label" htmlFor = "cloud-rewrite-model" > Cloud model</ label >
862+ < select id = "cloud-rewrite-model" className = "setting-select" value = { bootstrap . settings . cloudRewriteModel } onChange = { ( e ) => void onAction ( 'settings' , ( ) => window . openWhisp . updateSettings ( { cloudRewriteModel : e . target . value } ) ) } >
863+ < option value = "openai/gpt-oss-20b" > GPT-OSS 20B (1000 t/s)</ option >
864+ < option value = "openai/gpt-oss-120b" > GPT-OSS 120B (500 t/s)</ option >
865+ < option value = "qwen/qwen3-32b" > Qwen3 32B (400 t/s)</ option >
866+ < option value = "llama-3.3-70b-versatile" > Llama 3.3 70B (280 t/s)</ option >
867+ < option value = "llama-3.1-8b-instant" > Llama 3.1 8B (560 t/s)</ option >
868+ </ select >
869+ </ div >
870+ ) }
871+ { bootstrap . settings . rewriteMode === 'local' && (
872+ < >
873+ < div className = "setting-row" style = { { marginTop : 12 } } >
874+ < label className = "setting-label" htmlFor = "model-select" > Ollama model</ label >
875+ < select id = "model-select" className = "setting-select" value = { bootstrap . settings . textModel } onChange = { ( e ) => void onAction ( 'settings' , ( ) => window . openWhisp . updateSettings ( { textModel : e . target . value } ) ) } >
876+ { bootstrap . ollamaModels . length === 0
877+ ? < option value = { bootstrap . settings . textModel } > { bootstrap . settings . textModel } </ option >
878+ : bootstrap . ollamaModels . map ( ( m ) => < option key = { m . name } value = { m . name } > { m . name } ({ formatBytes ( m . size ) } )</ option > )
879+ }
880+ </ select >
881+ </ div >
882+ { ! bootstrap . recommendedModelInstalled && bootstrap . ollamaReachable && (
883+ < button className = "btn btn-sm btn-primary" style = { { marginTop : 10 } } disabled = { busyAction === 'model' } onClick = { ( ) => void onAction ( 'model' , ( ) => window . openWhisp . pullRecommendedModel ( ) ) } >
884+ { busyAction === 'model' ? 'Downloading...' : `Download ${ RECOMMENDED_TEXT_MODEL } ` }
885+ </ button >
886+ ) }
887+ </ >
861888 ) }
862889 </ div >
863890
0 commit comments