document . addEventListener ( "DOMContentLoaded" , async function ( ) {
const patternSelector = document . getElementById ( "patternSelector" ) ;
const modelSelector = document . getElementById ( "modelSelector" ) ;
const userInput = document . getElementById ( "userInput" ) ;
const submitButton = document . getElementById ( "submit" ) ;
const responseContainer = document . getElementById ( "responseContainer" ) ;
const themeChanger = document . getElementById ( "themeChanger" ) ;
const configButton = document . getElementById ( "configButton" ) ;
const configSection = document . getElementById ( "configSection" ) ;
const saveApiKeyButton = document . getElementById ( "saveApiKey" ) ;
const openaiApiKeyInput = document . getElementById ( "apiKeyInput" ) ;
const claudeApiKeyInput = document . getElementById ( "claudeApiKeyInput" ) ;
const updatePatternsButton = document . getElementById ( "updatePatternsButton" ) ;
const copyButton = document . createElement ( "button" ) ;
window . electronAPI . on ( "patterns-ready" , ( ) => {
console . log ( "Patterns are ready. Refreshing the pattern list." ) ;
loadPatterns ( ) ;
} ) ;
window . electronAPI . on ( "request-api-key" , ( ) => {
configSection . classList . remove ( "hidden" ) ;
} ) ;
copyButton . textContent = "Copy" ;
copyButton . id = "copyButton" ;
document . addEventListener ( "click" , function ( e ) {
if ( e . target && e . target . id === "copyButton" ) {
copyToClipboard ( ) ;
}
} ) ;
window . electronAPI . on ( "no-api-key" , ( ) => {
alert ( "API key is missing. Please enter your OpenAI API key." ) ;
} ) ;
window . electronAPI . on ( "patterns-updated" , ( ) => {
alert ( "Patterns updated. Refreshing the pattern list." ) ;
loadPatterns ( ) ;
} ) ;
function htmlToPlainText ( html ) {
var tempDiv = document . createElement ( "div" ) ;
tempDiv . innerHTML = html ;
tempDiv . querySelectorAll ( "br" ) . forEach ( ( br ) => br . replaceWith ( "\n" ) ) ;
tempDiv . querySelectorAll ( "p, div" ) . forEach ( ( block ) => {
block . prepend ( "\n" ) ;
block . replaceWith ( ... block . childNodes ) ;
} ) ;
return tempDiv . textContent . trim ( ) ;
}
async function submitQuery ( userInputValue ) {
userInput . value = "" ; // Clear the input after submitting
const systemCommand = await window . electronAPI . invoke (
"get-pattern-content" ,
patternSelector . value
) ;
const selectedModel = modelSelector . value ;
responseContainer . innerHTML = "" ; // Clear previous responses
if ( responseContainer . classList . contains ( "hidden" ) ) {
responseContainer . classList . remove ( "hidden" ) ;
responseContainer . appendChild ( copyButton ) ;
}
window . electronAPI . send (
"start-query" ,
systemCommand ,
userInputValue ,
selectedModel
) ;
}
function copyToClipboard ( ) {
const containerClone = responseContainer . cloneNode ( true ) ;
const copyButtonClone = containerClone . querySelector ( "#copyButton" ) ;
if ( copyButtonClone ) {
copyButtonClone . parentNode . removeChild ( copyButtonClone ) ;
}
const plainText = htmlToPlainText ( containerClone . innerHTML ) ;
const textArea = document . createElement ( "textarea" ) ;
textArea . style . position = "absolute" ;
textArea . style . left = "-9999px" ;
textArea . setAttribute ( "aria-hidden" , "true" ) ;
textArea . value = plainText ;
document . body . appendChild ( textArea ) ;
textArea . select ( ) ;
try {
document . execCommand ( "copy" ) ;
console . log ( "Text successfully copied to clipboard" ) ;
} catch ( err ) {
console . error ( "Failed to copy text: " , err ) ;
}
document . body . removeChild ( textArea ) ;
}
async function loadPatterns ( ) {
try {
const patterns = await window . electronAPI . invoke ( "get-patterns" ) ;
patternSelector . innerHTML = "" ; // Clear existing options first
patterns . forEach ( ( pattern ) => {
const option = document . createElement ( "option" ) ;
option . value = pattern ;
option . textContent = pattern ;
patternSelector . appendChild ( option ) ;
} ) ;
} catch ( error ) {
console . error ( "Failed to load patterns:" , error ) ;
}
}
async function loadModels ( ) {
try {
const models = await window . electronAPI . invoke ( "get-models" ) ;
modelSelector . innerHTML = "" ; // Clear existing options first
models . gptModels . forEach ( ( model ) => {
const option = document . createElement ( "option" ) ;
option . value = model . id ;
option . textContent = model . id ;
modelSelector . appendChild ( option ) ;
} ) ;
models . claudeModels . forEach ( ( model ) => {
const option = document . createElement ( "option" ) ;
option . value = model ;
option . textContent = model ;
modelSelector . appendChild ( option ) ;
} ) ;
models . ollamaModels . forEach ( ( model ) => {
const option = document . createElement ( "option" ) ;
option . value = model ;
option . textContent = model ;
modelSelector . appendChild ( option ) ;
} ) ;
} catch ( error ) {
console . error ( "Failed to load models:" , error ) ;
}
}
updatePatternsButton . addEventListener ( "click" , ( ) => {
window . electronAPI . send ( "update-patterns" ) ;
} ) ;
// Load patterns and models on startup
loadPatterns ( ) ;
loadModels ( ) ;
// Listen for model responses
window . electronAPI . on ( "model-response" , ( message ) => {
const formattedMessage = message . replace ( /\n/g , "<br>" ) ;
responseContainer . innerHTML += formattedMessage ; // Append new data as it arrives
} ) ;
window . electronAPI . on ( "model-response-end" , ( message ) => {
// Handle the end of the model response if needed
} ) ;
window . electronAPI . on ( "model-response-error" , ( message ) => {
alert ( message ) ;
} ) ;
window . electronAPI . on ( "file-response" , ( message ) => {
if ( message . startsWith ( "Error" ) ) {
alert ( message ) ;
return ;
}
submitQuery ( message ) ;
} ) ;
window . electronAPI . on ;
"reload-app" ,
( ) => {
// Reload the app
loadModels ( ) ;
location . reload ( ) ;
} ;
// Submit button click handler
submitButton . addEventListener ( "click" , async ( ) => {
const userInputValue = userInput . value ;
submitQuery ( userInputValue ) ;
} ) ;
// Theme changer click handler
themeChanger . addEventListener ( "click" , function ( e ) {
e . preventDefault ( ) ;
document . body . classList . toggle ( "light-theme" ) ;
themeChanger . innerText =
themeChanger . innerText === "Dark" ? "Light" : "Dark" ;
} ) ;
// Config button click handler - toggles the config section visibility
configButton . addEventListener ( "click" , function ( e ) {
e . preventDefault ( ) ;
configSection . classList . toggle ( "hidden" ) ;
} ) ;
// Save API Key button click handler
saveApiKeyButton . addEventListener ( "click" , ( ) => {
const openAIKey = openaiApiKeyInput . value ;
const claudeKey = claudeApiKeyInput . value ;
window . electronAPI
. invoke ( "save-api-keys" , { openAIKey , claudeKey } )
. then ( ( ) => {
alert ( "API Keys saved successfully." ) ;
configSection . classList . add ( "hidden" ) ;
openaiApiKeyInput . value = "" ;
claudeApiKeyInput . value = "" ;
// Reload the models
loadModels ( ) ;
} )
. catch ( ( err ) => {
console . error ( "Error saving API keys:" , err ) ;
alert ( "Failed to save API Keys." ) ;
} ) ;
} ) ;
// Handler for pattern selection change
patternSelector . addEventListener ( "change" , async ( ) => {
const selectedPattern = patternSelector . value ;
const systemCommand = await window . electronAPI . invoke (
"get-pattern-content" ,
selectedPattern
) ;
// Use systemCommand as part of the input for querying the model
} ) ;
// drag and drop
userInput . addEventListener ( "dragover" , ( event ) => {
event . stopPropagation ( ) ;
event . preventDefault ( ) ;
// Add some visual feedback
userInput . classList . add ( "drag-over" ) ;
userInput . placeholder = "Drop file here" ;
} ) ;
userInput . addEventListener ( "dragleave" , ( event ) => {
event . stopPropagation ( ) ;
event . preventDefault ( ) ;
// Remove visual feedback
userInput . classList . remove ( "drag-over" ) ;
userInput . placeholder = originalPlaceholder ;
} ) ;
userInput . addEventListener ( "drop" , ( event ) => {
event . stopPropagation ( ) ;
event . preventDefault ( ) ;
const file = event . dataTransfer . files [ 0 ] ;
userInput . classList . remove ( "drag-over" ) ;
userInput . placeholder = originalPlaceholder ;
processFile ( file ) ;
} ) ;
function processFile ( file ) {
const fileType = file . type ;
const reader = new FileReader ( ) ;
let content = "" ;
reader . onload = ( event ) => {
content = event . target . result ;
userInput . value = content ;
submitQuery ( content ) ;
} ;
if ( fileType === "text/plain" || fileType === "image/svg+xml" ) {
reader . readAsText ( file ) ;
} else if (
fileType === "application/pdf" ||
fileType . match ( /wordprocessingml/ )
) {
// For PDF and DOCX, we need to handle them in the main process due to complexity
window . electronAPI . send ( "process-complex-file" , file . path ) ;
} else {
console . error ( "Unsupported file type" ) ;
}
}
} ) ;