diff --git a/public/ChatManager.js b/public/ChatManager.js index e624184..65f5bd6 100644 --- a/public/ChatManager.js +++ b/public/ChatManager.js @@ -276,6 +276,7 @@ export class ChatManager { async setTargetFile(targetFile) { this.targetFileInput.value = targetFile; + ctx.targetFile = targetFile; } async loadConversationsList() { diff --git a/public/LLMSettingsManager.js b/public/LLMSettingsManager.js index 6db890d..cbb9842 100644 --- a/public/LLMSettingsManager.js +++ b/public/LLMSettingsManager.js @@ -16,6 +16,25 @@ export class LLMSettingsManager { this.addRefreshButton(); this.llmSettings = await this.fetchSettings(); this.createSettingsDiv(); + // check if there is an active LLM + const activeLLM = await this.getActiveLLM(); + // if no LLMS are active swap to this tab + if (!activeLLM) { + await ctx.tabs.disableAllTabs(); + await ctx.tabs.enableTab('LLM Settings'); + + // add an element to the top of the container to notify the user + const notification = document.createElement('div'); + notification.textContent = 'You must select an active LLM and model'; + notification.style.backgroundColor = 'red'; + notification.style.color = 'white'; + notification.style.padding = '10px'; + notification.style.margin = '10px'; + this.container.insertBefore(notification, this.settingsDiv); + + }else{ + ctx.tabs.enableAllTabs(); + } } async addRefreshButton() { @@ -128,6 +147,15 @@ export class LLMSettingsManager { } } + async getActiveLLM() { + for (const llmDiv of this.settingsDiv.children) { + if (llmDiv.querySelector('input[type="checkbox"]').checked) { + return llmDiv.querySelector('h2').textContent; + } + } + return null; + } + async saveSettings() { const newSettings = {}; const activeModels = []; diff --git a/public/main.js b/public/main.js index dc04461..8702aac 100644 --- a/public/main.js +++ b/public/main.js @@ -23,13 +23,13 @@ async function setup() { const llmSettingsTab = tabs.createTab("LLM Settings","🧠"); ctx.llmSettings = new LLMSettingsManager(llmSettingsTab, ctx); + document.body.style.margin = "0"; document.body.style.height = "100vh"; document.body.style.display = "flex"; document.body.appendChild(tabs.getElement()); - window.ctx = ctx; } diff --git a/public/tabInterface.js b/public/tabInterface.js index 809160f..fa37e48 100644 --- a/public/tabInterface.js +++ b/public/tabInterface.js @@ -142,5 +142,45 @@ export class tabInterface { } }); } + + async disableTab(tabName) { + // make the tab not clickable and greyed out + const idx = this.tabs.findIndex(tab => tab.name.toLowerCase() === tabName.toLowerCase()); + if (idx !== -1) { + this.tabBar.childNodes[idx].style.pointerEvents = "none"; + this.tabBar.childNodes[idx].style.textDecoration = "line-through"; + } + } + + async enableTab(tabName) { + const idx = await this.tabs.findIndex(tab => tab.name.toLowerCase() === tabName.toLowerCase()); + + if (idx !== -1) { + this.tabBar.childNodes[idx].style.pointerEvents = "auto"; + this.tabBar.childNodes[idx].style.backgroundColor = this.colors.tabButtonBackground; + this.tabBar.childNodes[idx].style.textDecoration = "none"; + } + // set this tab as active + this.activeTab = idx; + return await this.showActiveTab(); + + } + + async disableAllTabs() { + for (let i = 0; i < this.tabBar.childNodes.length; i++) { + this.tabBar.childNodes[i].style.pointerEvents = "none"; + this.tabBar.childNodes[i].style.textDecoration = "line-through"; + } + return true; + } + + async enableAllTabs() { + for (let i = 0; i < this.tabBar.childNodes.length; i++) { + this.tabBar.childNodes[i].style.pointerEvents = "auto"; + this.tabBar.childNodes[i].style.backgroundColor = this.colors.tabButtonBackground; + this.tabBar.childNodes[i].style.textDecoration = "none"; + } + return true; + } } diff --git a/src/aiCoderApiFunctions.js b/src/aiCoderApiFunctions.js index b8585e4..9bcef74 100644 --- a/src/aiCoderApiFunctions.js +++ b/src/aiCoderApiFunctions.js @@ -98,7 +98,9 @@ export class aiCoderApiFunctions { } async getMethodsList(parsedBody) { + console.log(parsedBody); const response = await getMethodsWithArguments(await readFile(parsedBody.targetFile)); + console.log('getMethodsList', response); return response; } diff --git a/src/classListing.js b/src/classListing.js index 6bd5fc8..a1571e5 100644 --- a/src/classListing.js +++ b/src/classListing.js @@ -1,5 +1,5 @@ import { readFile, writeFile } from './fileIO.js'; -import * as esprima from 'esprima'; +import * as esprima from 'esprima-next'; export async function prependClassStructure(targetFile, onlyStubs = false) { // You can toggle onlyStubs = true or false as needed @@ -41,39 +41,44 @@ export async function prependClassStructure(targetFile, onlyStubs = false) { export function getMethodsWithArguments(code, onlyStubs = false) { - //console.log(code); - // Parse the code using esprima const ast = esprima.parseModule(code, { sourceType: 'module', tolerant: true, range: true, loc: true, - attachComment: true + attachComment: true, }); const classInfo = new Map(); - // Collect information on each class, its methods, and arguments ast.body.forEach(node => { - if (node.type === 'ClassDeclaration' && node.id && node.id.name) { - const className = node.id.name; - const parentClassName = node.superClass && node.superClass.name ? node.superClass.name : null; + console.log(node); + let classNode = null; + + if (node.type === 'ClassDeclaration') { + classNode = node; + } else if ( + node.type === 'ExportNamedDeclaration' && + node.declaration && + node.declaration.type === 'ClassDeclaration' + ) { + classNode = node.declaration; + } + + if (classNode && classNode.id && classNode.id.name) { + const className = classNode.id.name; + const parentClassName = classNode.superClass && classNode.superClass.name ? classNode.superClass.name : null; let methods = []; - node.body.body.forEach(classElement => { + classNode.body.body.forEach(classElement => { if (classElement.type === 'MethodDefinition' && classElement.key.type === 'Identifier') { const methodName = classElement.key.name; - const startCharacterLocation = classElement.start; - //console.log(classElement.loc); - const lineNumber = classElement.loc.start.line; - // Collect method arguments const args = classElement.value.params.map(param => { if (param.type === 'Identifier') return param.name; if (param.type === 'AssignmentPattern' && param.left.type === 'Identifier') return param.left.name; return 'unknown'; }); - // Check if method is a stub const methodBody = classElement.value.body?.body || []; const isStub = methodBody.length === 0 || @@ -81,11 +86,10 @@ export function getMethodsWithArguments(code, onlyStubs = false) { methodBody[0].type === 'ReturnStatement' && !methodBody[0].argument); - methods.push({ name: methodName, args, isStub, lineNumber }); + methods.push({ name: methodName, args, isStub }); } }); - // If onlyStubs is true, filter out non-stub methods if (onlyStubs) { methods = methods.filter(m => m.isStub); } @@ -94,7 +98,6 @@ export function getMethodsWithArguments(code, onlyStubs = false) { } }); - // Sort classes based on dependency hierarchy const sortedClasses = []; const processedClasses = new Set(); @@ -103,7 +106,6 @@ export function getMethodsWithArguments(code, onlyStubs = false) { const classData = classInfo.get(className); if (!classData) return; - // Add the parent class first if it exists if (classData.parentClassName && classInfo.has(classData.parentClassName)) { addClassAndSubclasses(classData.parentClassName); } @@ -112,18 +114,13 @@ export function getMethodsWithArguments(code, onlyStubs = false) { processedClasses.add(className); } - //console.log(classInfo); - // Process classes in the order they appear in the original code Array.from(classInfo.keys()).forEach(addClassAndSubclasses); - // Convert sorted classes into the final output format const result = {}; sortedClasses.forEach(({ className, methods }) => { result[className] = methods; }); - - return result; } @@ -147,24 +144,34 @@ export async function getStubMethods(code) { } -export async function getListOfFunctions(code){ - // we are getting the functions in the application. Not the classes or class methods +export async function getListOfFunctions(code) { const ast = esprima.parseModule(code, { sourceType: 'module', tolerant: true, range: true, loc: true, - attachComment: true + attachComment: true, }); const functionInfo = new Map(); - // Collect information on each function, its arguments ast.body.forEach(node => { - if (node.type === 'FunctionDeclaration' && node.id && node.id.name) { - const functionName = node.id.name; + let functionNode = null; + + if (node.type === 'FunctionDeclaration') { + functionNode = node; + } else if ( + node.type === 'ExportNamedDeclaration' && + node.declaration && + node.declaration.type === 'FunctionDeclaration' + ) { + functionNode = node.declaration; + } + + if (functionNode && functionNode.id && functionNode.id.name) { + const functionName = functionNode.id.name; let args = []; - node.params.forEach(param => { + functionNode.params.forEach(param => { if (param.type === 'Identifier') args.push(param.name); if (param.type === 'AssignmentPattern' && param.left.type === 'Identifier') args.push(param.left.name); }); @@ -174,4 +181,5 @@ export async function getListOfFunctions(code){ }); return functionInfo; -} \ No newline at end of file +} +