app.py:

from fasthtml.common import *
import time
import sys
import os
import asyncio
import logging
from starlette.responses import JSONResponse, HTMLResponse, FileResponse
from dominate.tags import i

sys.path.append(os.path.abspath('.'))
# from cli_integration import (
# web_chat_with_model,
# web_reset_conversation_context,
# web_view_conversation_context,
# web_extract_and_save_code,
# web_switch_model,
# web_smart_edit
# )

# Configure logging
logging.basicConfig(level=logging.ERROR)

# Define ROOT_DIR globally
ROOT_DIR = os.path.abspath('/Users/barry/arc-engineer/')

hdrs = (HighlightJS(langs=['python', 'javascript', 'html', 'css']),)

app, rt = fast_app(pico=False, hdrs=hdrs)

def is_code_file(filename):
code_extensions = ['.py', '.js', '.html', '.css', '.java', '.cpp', '.c', '.go', '.rs', '.ts']
return any(filename.endswith(ext) for ext in code_extensions)

def detect_language(file_path):
extension = os.path.splitext(file_path)[1].lower()
language_map = {
'.py': 'python',
'.js': 'javascript',
'.html': 'html',
'.css': 'css',
'.java': 'java',
'.cpp': 'text/x-c++src',
'.c': 'text/x-csrc',
'.go': 'text/x-go',
'.rs': 'rust',
'.ts': 'javascript'
}
return language_map.get(extension, 'plaintext')

def get_absolute_path(file_path):
# Construct the absolute path
abs_path = os.path.abspath(os.path.join(ROOT_DIR, file_path))
# Ensure the path is within ROOT_DIR
if not abs_path.startswith(ROOT_DIR):
raise ValueError("Accessing paths outside of the root directory is not allowed")
return abs_path

def get_file_icon(file_name):
extension = os.path.splitext(file_name)[1].lower()
icon_classes = {
'.py': 'fab fa-python',
'.js': 'fab fa-js-square',
'.html': 'fab fa-html5',
'.css': 'fab fa-css3-alt',
'.java': 'fab fa-java',
'.cpp': 'fas fa-file-code',
'.c': 'fas fa-file-code',
'.go': 'fas fa-golang',
'.rs': 'fas fa-rust',
'.ts': 'fas fa-file-code',
# Add additional mappings as needed
}
return icon_classes.get(extension, 'fas fa-file-alt') # Default icon

@rt("/")
def get(session):
current_model = session.get('current_model', 'llama')
return Titled(
"Workbench",
Link(rel="stylesheet", href="/static/style.css"),
# CodeMirror CSS
Link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/codemirror.min.css"),
# CodeMirror Theme
Link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/theme/material-darker.min.css"),
# PrismJS and CodeMirror Scripts
Script(src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/prism.min.js"),
Script(src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/components/prism-python.min.js"),
Script(src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/codemirror.min.js"),
Script(src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/mode/python/python.min.js"),
Script(src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/addon/edit/closebrackets.min.js"),
Script(src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/addon/hint/show-hint.min.js"),
Link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/addon/hint/show-hint.min.css"),
Link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"),
# Split.js for resizable panes
Script(src="https://cdnjs.cloudflare.com/ajax/libs/split.js/1.6.0/split.min.js"),
# Custom Script
Script(src="/static/script.js"),
# Top Navigation
Div(
Button("Open Project", id="open-project-btn"),
Button(
I(cls="fas fa-moon"), # Default icon; JavaScript will update if needed
id="mode-toggle-btn",
cls="mode-toggle-btn",
title="Toggle light/dark mode"
),
cls="top-nav"
),
# Header
Div(
H2("Workbench UI"),
cls="header"
),
# Main Grid with Two Columns
Div(
Div( # Left Column
# File Browser Container
Div(
Div(
H2("File Browser", cls="file-browser-header"),
Button(
I(cls="fas fa-chevron-left"),
id="toggle-file-browser-btn",
cls="toggle-btn",
title="Toggle File Browser"
),
cls="file-browser-header-container"
),
Div(
Div(id="file-browser", cls="file-browser"),
cls="file-browser-content"
),
cls="file-browser-container"
),
# Conversation History
H2("Conversation History"),
Div(id="conversation-history", cls="scrollable conversation-history"),
cls="left-column" # Ensure class name is exactly 'left-column'
),
Div( # Right Column
# Model Selection Form
Form(
Label("Select Model:", **{'for': "model-select"}),
Select(
Option("llama", value="llama", selected=(current_model == 'llama')),
Option("gpt", value="gpt", selected=(current_model == 'gpt')),
Option("sonnet", value="sonnet", selected=(current_model == 'sonnet')),
id="model-select",
name="new_model",
hx_post="/switch_model",
hx_trigger="change",
hx_swap="innerHTML"
),
cls="model-selection-form"
),
# Prompt Input
Form(
Label("Enter your prompt:", **{'for': "prompt-input"}),
Textarea(id="prompt-input", name="prompt", placeholder="Enter your prompt here..."),
Div(id="char-counter", cls="char-counter"),
Div(
Button("Preview", id="preview-prompt-btn", type="button"),
Button("Submit", type="submit"),
Button("Streaming", id="streaming-btn", type="button"),
cls="button-group"
),
id="prompt-form",
hx_post="/submit",
hx_target="#conversation-history",
hx_swap="beforeend"
),
# Code Editor
H2("Code Output"),
Div(
Textarea("", id="code-editor"),
Div(
Button("Copy Code", id="copy-code-btn"),
Button("Save Code", id="save-code-btn"),
cls="code-buttons"
),
cls="code-container"
),
cls="right-column" # Ensure class name is exactly 'right-column'
),
cls="main-grid" # Ensure this container is correctly styled in CSS
)
)

@rt("/browse", methods=["POST"])
def browse_files(path: str = "."):
try:
abs_path = get_absolute_path(path)
parent_path = os.path.dirname(abs_path)
items = os.listdir(abs_path)
directories = [item for item in items if os.path.isdir(os.path.join(abs_path, item))]
code_files = [item for item in items if os.path.isfile(os.path.join(abs_path, item)) and is_code_file(item)]

# Generate breadcrumbs
breadcrumbs = []
relative_path = os.path.relpath(abs_path, ROOT_DIR)
path_parts = [] if relative_path == '.' else relative_path.split(os.sep)

# Root breadcrumb
breadcrumbs.append(
A(
"Root",
href="javascript:browseDirectory('')",
cls="breadcrumb-link"
)
)

# Build breadcrumbs for each part of the path
cumulative_path = ''
for part in path_parts:
cumulative_path = os.path.join(cumulative_path, part)
breadcrumbs.append(
Span(
I(cls="fas fa-chevron-right breadcrumb-separator") # Separator icon
)
)
breadcrumbs.append(
A(
part,
href=f"javascript:browseDirectory('{cumulative_path.replace(os.sep, '/')}')",
cls="breadcrumb-link"
)
)

breadcrumb_navigation = Div(
breadcrumbs,
cls="breadcrumb"
)

# Relative path for the parent directory
parent_rel_path = os.path.relpath(parent_path, ROOT_DIR)
parent_rel_path = '' if parent_rel_path == '.' else parent_rel_path.replace(os.sep, '/')
at_root = os.path.abspath(abs_path) == os.path.abspath(ROOT_DIR)

# Generate the file list with icons
file_list = Ul(
# For parent directory link
Li(
A(
I(cls="fas fa-chevron-up"), # Up chevron icon
I(cls="fas fa-level-up-alt", style="margin-left: 5px;"), # Up directory icon
Span(" ..", style="margin-left: 5px;"),
href=f"javascript:browseDirectory('{parent_rel_path}')",
cls="file-browser-link parent-dir"
)
) if not at_root else '', # Include only if not at root
# For directories
[
Li(
A(
I(cls="fas fa-chevron-right"), # Chevron icon
I(cls="fas fa-folder", style="margin-left: 5px;"), # Folder icon
Span(f" {directory}", style="margin-left: 5px;"),
href=f"javascript:browseDirectory('{os.path.join(path, directory).replace(os.sep, '/')}')",
cls="file-browser-link directory"
)
)
for directory in directories
],
# For files
[
Li(
A(
I(cls=get_file_icon(file)),
f" {file}",
href=f"javascript:viewFile('{os.path.join(path, file).replace(os.sep, '/')}')",
cls="file-browser-link file"
)
)
for file in code_files
],
cls="file-list"
)

return Div(
breadcrumb_navigation,
file_list,
id="file-browser"
)
except Exception as e:
logging.exception("Error in /browse route")
return Div(f"Error browsing files: {str(e)}", id="file-browser", cls="error-message")

@rt("/view_file", methods=["POST"])
def view_file(file_path: str):
try:
abs_file_path = get_absolute_path(file_path)
if not os.path.isfile(abs_file_path):
raise FileNotFoundError(f"File not found: {file_path}")

with open(abs_file_path, 'r') as file:
content = file.read()
language = detect_language(abs_file_path)
response_data = {
'content': content,
'language': language
}
return JSONResponse(response_data)
except FileNotFoundError as fnf_error:
logging.error(fnf_error)
return JSONResponse({'error': str(fnf_error)}, status_code=404)
except Exception as e:
logging.exception("Error in /view_file route")
return JSONResponse({'error': str(e)}, status_code=500)

@rt("/save_file", methods=["POST"])
def save_file(file_path: str, content: str):
try:
abs_file_path = get_absolute_path(file_path)
with open(abs_file_path, 'w') as file:
file.write(content)
return JSONResponse({'status': 'success'})
except Exception as e:
logging.exception("Error in /save_file route")
return JSONResponse({'error': str(e)}, status_code=500)

# Other routes remain unchanged (For Phase 1)
@rt("/submit", methods=["POST"])
async def submit(prompt: str, session):
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
user_message = Div(f"[{timestamp}] User: {prompt}", cls="user-message message", data_content=prompt)

try:
response = await web_chat_with_model(prompt, session.get('current_model', 'llama'))
system_message = Div(f"[{timestamp}] Assistant: {response}", cls="assistant-message message", data_content=response)
# Update conversation history in session
conversation_history = session.get('conversation_history', [])
conversation_history.append(f"User: {prompt}")
conversation_history.append(f"Assistant: {response}")
session['conversation_history'] = conversation_history
return user_message + system_message
except Exception as e:
logging.exception("Error in /submit route")
error_message = Div(f"Error: {str(e)}", cls="error-message")
return user_message + error_message

@rt("/switch_model", methods=["POST"])
def switch_model(new_model: str, session):
try:
current_model = session.get('current_model', 'llama')
session['current_model'] = web_switch_model(current_model, new_model)
return Div(f"Switched to model: {session['current_model']}", cls="info-message")
except Exception as e:
logging.exception("Error in /switch_model route")
return Div(f"Error: {str(e)}", cls="error-message")

@rt("/reset_context", methods=["POST"])
def reset_context(session):
try:
result = web_reset_conversation_context(session)
session['conversation_history'] = []
return Div(result, cls="info-message")
except Exception as e:
logging.exception("Error in /reset_context route")
return Div(f"Error: {str(e)}", cls="error-message")

@rt("/view_context", methods=["GET"])
def view_context(session):
try:
context = web_view_conversation_context(session)
return Div(Pre(Code(context)), cls="context-display")
except Exception as e:
logging.exception("Error in /view_context route")
return Div(f"Error: {str(e)}", cls="error-message")

@rt("/preview", methods=["POST"])
def preview(prompt: str):
# Simple tokenization
tokens = prompt.split()
preview_info = f"Tokenized preview:\n{' | '.join(tokens)}\nToken count: {len(tokens)}"
return Div(Pre(Code(preview_info, cls="language-plaintext")), cls="preview-info")

@rt("/stream", methods=["GET"])
async def stream(prompt: str, session):
async def event_stream():
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
yield f"data: {Div(f'[{timestamp}] User: {prompt}', cls='user-message')}\n\n"
try:
response = await web_chat_with_model(prompt, session.get('current_model', 'llama'))
# Simulate streaming by splitting the response
for chunk in response.split('\n'):
await asyncio.sleep(0.1)
yield f"data: {Div(f'{chunk}', cls='assistant-message')}\n\n"
yield "data: [DONE]\n\n"
except Exception as e:
logging.exception("Error in /stream route")
yield f"data: {Div(f'Error: {str(e)}', cls='error-message')}\n\n"
return EventStream(event_stream())


if name == "main":
serve()

script.js:

// document.addEventListener('DOMContentLoaded', function() {
// // Element references
// const modeToggle = document.getElementById('dark-mode-toggle');
// const body = document.body;
// const promptInput = document.getElementById('prompt-input');
// const charCounter = document.getElementById('char-counter');
// const copyCodeBtn = document.getElementById('copy-code-btn');
// const saveCodeBtn = document.getElementById('save-code-btn');
// const openProjectBtn = document.getElementById('open-project-btn');
// const streamingBtn = document.getElementById('streaming-btn');
// const previewPromptBtn = document.getElementById('preview-prompt-btn');
// const resetContextBtn = document.getElementById('reset-context-btn');
// const viewContextBtn = document.getElementById('view-context-btn');
// const smartEditForm = document.getElementById('smart-edit-form');
// let currentFilePath = null; // To keep track of the current file path

// // const toggleFileBrowserBtn = document.getElementById('toggle-file-browser-btn');
// // const fileBrowserContent = document.querySelector('.file-browser-content');

// // if (toggleFileBrowserBtn && fileBrowserContent) {
// // toggleFileBrowserBtn.textContent = '☰'; // Set initial button icon

// // toggleFileBrowserBtn.addEventListener('click', function() {
// // if (fileBrowserContent.classList.contains('collapsed')) {
// // // Expand the content
// // fileBrowserContent.classList.remove('collapsed');
// // toggleFileBrowserBtn.textContent = '☰';
// // // Animate expansion
// // fileBrowserContent.style.maxHeight = fileBrowserContent.scrollHeight + 'px';
// // setTimeout(() => {
// // fileBrowserContent.style.maxHeight = '';
// // }, 300); // Match CSS transition duration
// // } else {
// // // Collapse the content
// // fileBrowserContent.style.maxHeight = fileBrowserContent.scrollHeight + 'px'; // Set to current height to enable transition
// // // Force repaint to ensure the transition occurs
// // fileBrowserContent.offsetHeight;
// // fileBrowserContent.style.maxHeight = '0';
// // fileBrowserContent.classList.add('collapsed');
// // toggleFileBrowserBtn.textContent = '▶';
// // }
// // });
// // } else {
// // console.error('Toggle button or file browser content not found.');
// // }

// const toggleFileBrowserBtn = document.getElementById('toggle-file-browser-btn');
// const fileBrowserContent = document.querySelector('.file-browser-content');

// if (toggleFileBrowserBtn && fileBrowserContent) {
// const icon = toggleFileBrowserBtn.querySelector('i');

// toggleFileBrowserBtn.addEventListener('click', function() {
// if (fileBrowserContent.classList.contains('collapsed')) {
// // Expand the content
// fileBrowserContent.classList.remove('collapsed');
// icon.classList.remove('fa-chevron-right');
// icon.classList.add('fa-chevron-left');
// // Animate expansion
// fileBrowserContent.style.maxHeight = fileBrowserContent.scrollHeight + 'px';
// setTimeout(() => {
// fileBrowserContent.style.maxHeight = '';
// }, 300); // Match CSS transition duration
// } else {
// // Collapse the content
// fileBrowserContent.style.maxHeight = fileBrowserContent.scrollHeight + 'px';
// // Force repaint to ensure the transition occurs
// fileBrowserContent.offsetHeight;
// fileBrowserContent.style.maxHeight = '0';
// fileBrowserContent.classList.add('collapsed');
// icon.classList.remove('fa-chevron-left');
// icon.classList.add('fa-chevron-right');
// }
// });
// } else {
// console.error('Toggle button or file browser content not found.');
// }

// // Light/Dark mode toggle
// if (localStorage.getItem('lightMode') === 'enabled') {
// body.classList.add('light-mode');
// modeToggle.checked = true;
// }

// modeToggle.addEventListener('change', function() {
// if (this.checked) {
// body.classList.add('light-mode');
// localStorage.setItem('lightMode', 'enabled');
// } else {
// body.classList.remove('light-mode');
// localStorage.setItem('lightMode', 'disabled');
// }
// });

// // Character counter
// promptInput.addEventListener('input', function() {
// charCounter.textContent = ${this.value.length} / 500; // Fixed template literal syntax
// });

// // Initialize CodeMirror editor
// const codeEditor = CodeMirror.fromTextArea(document.getElementById('code-editor'), {
// mode: 'python',
// lineNumbers: true,
// theme: 'material-darker',
// autoCloseBrackets: true,
// matchBrackets: true,
// extraKeys: {"Ctrl-Space": "autocomplete"},
// });

// // Function to update the code editor content
// function updateCodeEditorContent(content, mode='python') {
// codeEditor.setOption('mode', mode);
// codeEditor.setValue(content);
// codeEditor.refresh();
// }

// // Copy code button
// copyCodeBtn.addEventListener('click', function() {
// const codeContent = codeEditor.getValue();
// navigator.clipboard.writeText(codeContent).then(() => {
// copyCodeBtn.textContent = 'Copied!';
// }).catch(err => {
// console.error('Failed to copy: ', err);
// copyCodeBtn.textContent = 'Copy failed';
// });
// setTimeout(() => {
// copyCodeBtn.textContent = 'Copy Code';
// }, 2000);
// });

// // Save code button
// saveCodeBtn.addEventListener('click', function() {
// const codeContent = codeEditor.getValue();
// const filePath = currentFilePath || prompt("Enter the filename to save:", "untitled.py");
// if (filePath) {
// fetch('/save_file', {
// method: 'POST',
// headers: {'Content-Type': 'application/x-www-form-urlencoded'},
// body: file_path=${encodeURIComponent(filePath)}&content=${encodeURIComponent(codeContent)}
// })
// .then(response => response.json())
// .then(data => {
// if (data.status === 'success') {
// alert('Code saved successfully.');
// currentFilePath = filePath;
// } else {
// console.error('Error:', data.error);
// alert('Error saving code: ' + data.error);
// }
// });
// }
// });

// // Open Project button
// openProjectBtn.addEventListener('click', function() {
// const currentPath = '.'; // Update as needed based on your logic
// const path = prompt("Enter the path to your project:", currentPath);
// if (path) {
// browseDirectory(path);
// }
// });

// // Streaming button
// streamingBtn.addEventListener('click', function() {
// const promptValue = promptInput.value;
// const eventSource = new EventSource(/stream?prompt=${encodeURIComponent(promptValue)}); // Fixed by adding backticks
// eventSource.onmessage = function(event) {
// if (event.data === "[DONE]") {
// eventSource.close();
// return;
// }
// document.getElementById('conversation-history').innerHTML += event.data;
// };
// eventSource.onerror = function() {
// eventSource.close();
// };
// });

// // Preview button
// previewPromptBtn.addEventListener('click', function() {
// const promptValue = promptInput.value;
// fetch('/preview', {
// method: 'POST',
// headers: {'Content-Type': 'application/x-www-form-urlencoded'},
// body: prompt=${encodeURIComponent(promptValue)}
// })
// .then(response => response.text())
// .then(html => {
// updateCodeEditorContent(html, 'plaintext');
// });
// });

// // Reset Context button
// if (resetContextBtn) {
// resetContextBtn.addEventListener('click', function() {
// fetch('/reset_context', {
// method: 'POST'
// })
// .then(response => response.text())
// .then(html => {
// alert('Conversation context has been reset.');
// });
// });
// }

// // View Context button
// if (viewContextBtn) {
// viewContextBtn.addEventListener('click', function() {
// fetch('/view_context', {
// method: 'GET'
// })
// .then(response => response.text())
// .then(html => {
// document.getElementById('conversation-history').innerHTML += html;
// });
// });
// }

// // Clear the form after submission
// document.body.addEventListener('htmx:afterOnLoad', function(event) {
// if (event.detail && event.detail.elt && event.detail.elt.id === 'prompt-form') {
// event.detail.elt.reset();
// charCounter.textContent = '0 / 500';
// }
// });

// // Initial file browse on page load
// browseDirectory('.');

// function browseDirectory(path) {
// fetch('/browse', {
// method: 'POST',
// headers: {'Content-Type': 'application/x-www-form-urlencoded'},
// body: path=${encodeURIComponent(path)}
// })
// .then(response => response.text())
// .then(html => {
// document.getElementById('file-browser').innerHTML = html;
// currentPath = path; // Update currentPath for use in file actions
// });
// }

// function viewFile(filePath) {
// fetch('/view_file', {
// method: 'POST',
// headers: {'Content-Type': 'application/x-www-form-urlencoded'},
// body: file_path=${encodeURIComponent(filePath)}
// })
// .then(response => response.json())
// .then(data => {
// if (data.error) {
// console.error('Error:', data.error);
// alert('Error: ' + data.error);
// } else {
// const codeContent = data.content;
// const language = data.language || 'python';
// updateCodeEditorContent(codeContent, language);
// currentFilePath = filePath;
// }
// });
// }

// // Expose viewFile and browseDirectory globally for inline onclick handlers
// window.viewFile = viewFile;
// window.browseDirectory = browseDirectory;

// // Initialize Split.js for resizable columns with bidirectional resizing
// const savedSizes = JSON.parse(localStorage.getItem('split-sizes'));
// Split(['.left-column', '.right-column'], {
// sizes: savedSizes || [33, 67], // Initial sizes: 1/3 left, 2/3 right
// minSize: [200, 300], // Minimum widths for left and right panels
// gutterSize: 10,
// cursor: 'col-resize',
// direction: 'horizontal',
// elementStyle: function (dimension, size, gutterSize) {
// return {
// 'flex-basis': calc(${size}% - ${gutterSize}px), // Fixed by using backticks
// };
// },
// gutterStyle: function (dimension, gutterSize) {
// return {
// 'flex-basis': ${gutterSize}px, // Fixed by using backticks
// };
// },
// onDragEnd: function (sizes) {
// console.log('Columns resized:', sizes);
// localStorage.setItem('split-sizes', JSON.stringify(sizes));
// }
// });
// });





document.addEventListener('DOMContentLoaded', function() {
// Element references
const body = document.body;
const promptInput = document.getElementById('prompt-input');
const charCounter = document.getElementById('char-counter');
const copyCodeBtn = document.getElementById('copy-code-btn');
const saveCodeBtn = document.getElementById('save-code-btn');
const openProjectBtn = document.getElementById('open-project-btn');
const streamingBtn = document.getElementById('streaming-btn');
const previewPromptBtn = document.getElementById('preview-prompt-btn');
let currentFilePath = null; // To keep track of the current file path
let currentPath = '.'; // Initialize currentPath

const toggleFileBrowserBtn = document.getElementById('toggle-file-browser-btn');
const fileBrowserContent = document.querySelector('.file-browser-content');

if (toggleFileBrowserBtn && fileBrowserContent) {
const icon = toggleFileBrowserBtn.querySelector('i');

toggleFileBrowserBtn.addEventListener('click', function() {
if (fileBrowserContent.classList.contains('collapsed')) {
// Expand the content
fileBrowserContent.classList.remove('collapsed');
icon.classList.remove('fa-chevron-right');
icon.classList.add('fa-chevron-left');
// Animate expansion
fileBrowserContent.style.maxHeight = fileBrowserContent.scrollHeight + 'px';
setTimeout(() => {
fileBrowserContent.style.maxHeight = '';
}, 300); // Match CSS transition duration
} else {
// Collapse the content
fileBrowserContent.style.maxHeight = fileBrowserContent.scrollHeight + 'px';
// Force repaint to ensure the transition occurs
fileBrowserContent.offsetHeight;
fileBrowserContent.style.maxHeight = '0';
fileBrowserContent.classList.add('collapsed');
icon.classList.remove('fa-chevron-left');
icon.classList.add('fa-chevron-right');
}
});
} else {
console.error('Toggle button or file browser content not found.');
}

// Mode Toggle Button Functionality
const modeToggleBtn = document.getElementById('mode-toggle-btn');
const codeEditor = CodeMirror.fromTextArea(document.getElementById('code-editor'), {
mode: 'python',
lineNumbers: true,
theme: 'default', // Start with the default theme
autoCloseBrackets: true,
matchBrackets: true,
extraKeys: {"Ctrl-Space": "autocomplete"},
});

// Initialize theme based on localStorage
const currentTheme = localStorage.getItem('theme') || 'light';
const modeIcon = modeToggleBtn.querySelector('i');

if (currentTheme === 'dark') {
body.classList.add('dark-mode');
modeIcon.classList.remove('fa-moon');
modeIcon.classList.add('fa-sun');
codeEditor.setOption('theme', 'material-darker'); // Dark theme
} else {
body.classList.remove('dark-mode');
modeIcon.classList.remove('fa-sun');
modeIcon.classList.add('fa-moon');
codeEditor.setOption('theme', 'default'); // Light theme
}

// Event listener for the mode toggle button
modeToggleBtn.addEventListener('click', function() {
if (body.classList.contains('dark-mode')) {
// Switch to light mode
body.classList.remove('dark-mode');
localStorage.setItem('theme', 'light');
modeIcon.classList.remove('fa-sun');
modeIcon.classList.add('fa-moon');
codeEditor.setOption('theme', 'default'); // Light theme
} else {
// Switch to dark mode
body.classList.add('dark-mode');
localStorage.setItem('theme', 'dark');
modeIcon.classList.remove('fa-moon');
modeIcon.classList.add('fa-sun');
codeEditor.setOption('theme', 'material-darker'); // Dark theme
}
});

// Character counter
promptInput.addEventListener('input', function() {
charCounter.textContent = ${this.value.length} / 500;
});

// Copy code button
copyCodeBtn.addEventListener('click', function() {
const codeContent = codeEditor.getValue();

if (navigator.clipboard && navigator.clipboard.writeText) {
// Use the Clipboard API available in modern browsers
navigator.clipboard.writeText(codeContent).then(() => {
copyCodeBtn.textContent = 'Copied!';
}).catch(err => {
console.error('Failed to copy: ', err);
copyCodeBtn.textContent = 'Copy failed';
});
} else {
// Fallback method for older browsers
const textarea = document.createElement('textarea');
textarea.value = codeContent;
// Move the textarea off-screen (optional)
textarea.style.position = 'fixed';
textarea.style.top = '-1000px';
document.body.appendChild(textarea);
textarea.select();
try {
const successful = document.execCommand('copy');
if (successful) {
copyCodeBtn.textContent = 'Copied!';
} else {
console.error('Failed to copy using fallback method.');
copyCodeBtn.textContent = 'Copy failed';
}
} catch (err) {
console.error('Fallback copy failed: ', err);
copyCodeBtn.textContent = 'Copy failed';
}
document.body.removeChild(textarea);
}

setTimeout(() => {
copyCodeBtn.textContent = 'Copy Code';
}, 2000);
});

// Save code button
saveCodeBtn.addEventListener('click', function() {
const codeContent = codeEditor.getValue();
const filePath = currentFilePath || prompt("Enter the filename to save:", "untitled.py");
if (filePath) {
fetch('/save_file', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: file_path=${encodeURIComponent(filePath)}&content=${encodeURIComponent(codeContent)}
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
alert('Code saved successfully.');
currentFilePath = filePath;
} else {
console.error('Error:', data.error);
alert('Error saving code: ' + data.error);
}
});
}
});

// Open Project button
openProjectBtn.addEventListener('click', function() {
const currentPath = '.'; // Update as needed based on your logic
const path = prompt("Enter the path to your project:", currentPath);
if (path) {
browseDirectory(path);
}
});

// Streaming button
streamingBtn.addEventListener('click', function() {
const promptValue = promptInput.value;
const eventSource = new EventSource(/stream?prompt=${encodeURIComponent(promptValue)});
eventSource.onmessage = function(event) {
if (event.data === "[DONE]") {
eventSource.close();
return;
}
document.getElementById('conversation-history').innerHTML += event.data;
};
eventSource.onerror = function() {
eventSource.close();
};
});

// Preview button
previewPromptBtn.addEventListener('click', function() {
const promptValue = promptInput.value;
fetch('/preview', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: prompt=${encodeURIComponent(promptValue)}
})
.then(response => response.text())
.then(html => {
updateCodeEditorContent(html, 'plaintext');
});
});

// Function to update the code editor content
function updateCodeEditorContent(content, mode='python') {
codeEditor.setOption('mode', mode);
codeEditor.setValue(content);
codeEditor.refresh();
}


// Reintroduced Functions

function browseDirectory(path) {
fetch('/browse', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: path=${encodeURIComponent(path)}
})
.then(response => response.text())
.then(html => {
document.getElementById('file-browser').innerHTML = html;
currentPath = path; // Update currentPath for use in file actions
});
}

function viewFile(filePath) {
fetch('/view_file', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: file_path=${encodeURIComponent(filePath)}
})
.then(response => response.json())
.then(data => {
if (data.error) {
console.error('Error:', data.error);
alert('Error: ' + data.error);
} else {
const codeContent = data.content;
const language = data.language || 'python';
updateCodeEditorContent(codeContent, language);
currentFilePath = filePath;
}
});
}

// Expose viewFile and browseDirectory globally for inline onclick handlers
window.viewFile = viewFile;
window.browseDirectory = browseDirectory;

// Initialize Split.js for resizable columns with bidirectional resizing
const savedSizes = JSON.parse(localStorage.getItem('split-sizes'));
Split(['.left-column', '.right-column'], {
sizes: savedSizes || [33, 67], // Initial sizes: 1/3 left, 2/3 right
minSize: [200, 300], // Minimum widths for left and right panels
gutterSize: 10,
cursor: 'col-resize',
direction: 'horizontal',
elementStyle: function (dimension, size, gutterSize) {
return {
'flex-basis': calc(${size}% - ${gutterSize}px),
};
},
gutterStyle: function (dimension, gutterSize) {
return {
'flex-basis': ${gutterSize}px,
};
},
onDragEnd: function (sizes) {
console.log('Columns resized:', sizes);
localStorage.setItem('split-sizes', JSON.stringify(sizes));
}
});


// Initial file browse on page load
browseDirectory('.');
});
style.css
/ Default (Light Mode) /
:root {
--bg-color: #ffffff;
--text-color: #4f4f4f;
--primary-color: #0078d4;
--secondary-bg: #f5f5f5;
--border-color: #e0e0e0;
--directory-color: #ff9800;
--file-color: #4caf50;
--parent-dir-color: #ff5722;
--font-family: 'Roboto', sans-serif;
--heading-color: #333333;
--link-color: #0078d4;
--accent-color: #0078d4;
--button-bg-color: #0078d4;
--button-text-color: #ffffff;
--button-hover-bg-color: #005a9e;
--error-color: #ff6b6b;
--info-color: #2196f3;
--user-message-bg-color: #0078d4; / Primary color for user messages /
--assistant-message-bg-color: #f1f1f1; / Light background for assistant messages /
}


/ Dark Mode /
body.dark-mode {
--bg-color: #1e1e1e;
--text-color: #d4d4d4;
--primary-color: #0a84ff;
--secondary-bg: #2e2e2e;
--border-color: #3a3a3a;
--directory-color: #ff9800;
--file-color: #4caf50;
--parent-dir-color: #ff5722;
--heading-color: #ffffff;
--link-color: #0a84ff;
--accent-color: #0a84ff;
--button-bg-color: #0a84ff;
--button-text-color: #ffffff;
--button-hover-bg-color: #005999;
--error-color: #ff6b6b;
--info-color: #0a84ff;
--user-message-bg-color: #0a84ff; / Primary color for user messages /
--assistant-message-bg-color: #2e2e2e; / Dark background for assistant messages /
}

/ Global styles /
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: var(--font-family);
margin: 0;
padding: 20px;
transition: background-color 0.3s, color 0.3s;
font-size: 16px;
}

/ body.light-mode {
--bg-color: #ffffff;
--text-color: #000000;
--secondary-bg: #f9f9f9;
--border-color: #cccccc;
--directory-color: #ff9800;
--file-color: #4caf50;
--parent-dir-color: #ff5722;
--assistant-message-bg-color: #e0e0e0;
} /

h2 {
color: var(--heading-color);
font-family: var(--font-family);
font-weight: 500;
margin-top: 0;
margin-bottom: 10px;
}

p, label {
color: var(--text-color);
font-family: var(--font-family);
margin-bottom: 10px;
line-height: 1.6;
}

a {
color: var(--link-color);
text-decoration: none;
}

a:hover {
text-decoration: underline;
}

button {
background-color: var(--button-bg-color);
color: var(--button-text-color);
border: none;
padding: 10px 15px;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s;
font-size: 16px;
}

button:hover {
background-color: var(--button-hover-bg-color);
}

input, textarea, select {
font-family: var(--font-family);
font-size: 16px;
color: var(--text-color);
padding: 10px;
border: 1px solid var(--border-color);
border-radius: 5px;
background-color: var(--secondary-bg);
width: 100%;
box-sizing: border-box;
margin-bottom: 10px;
}

input::placeholder, textarea::placeholder {
color: #9e9e9e;
}

/ Navigation and header /
.top-nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background-color: var(--secondary-bg);
margin-bottom: 20px;
border-bottom: 1px solid var(--border-color);
}

.mode-toggle {
display: flex;
align-items: center;
}

.mode-toggle input {
margin-right: 5px;
}


.mode-toggle-btn {
background: none;
border: none;
cursor: pointer;
font-size: 24px;
color: var(--text-color);
margin-left: 10px;
}

.mode-toggle-btn:hover {
color: var(--accent-color);
}

.header {
text-align: center;
margin-bottom: 20px;
}

/ Flexbox Layout for Main Grid /
.main-grid {
display: flex;
height: calc(100vh - 80px); / Adjust height based on header and nav /
width: 100%; / Ensure it spans the full width /
}

/ / / Resizable Panels / / /
.left-column, .right-column {
overflow: auto;
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0;
min-width: 200px; / Ensure minimum width /
height: 100%; / Ensure full height /
}

/ Initial widths for Split.js /
/ These are now handled by Split.js via flex properties /

/ File Browser /
.file-browser {
max-height: 200px;
overflow-y: auto;
margin-bottom: 20px;
border: 1px solid var(--border-color);
border-radius: 5px;
padding: 10px;
}

.file-browser .file-list {
padding: 0;
margin: 0;
list-style: none;
}


.file-browser .file-list li {
margin-bottom: 0;
border-bottom: 1px solid var(--border-color);
}

.file-browser .file-list li:last-child {
border-bottom: none;
}

.file-browser .file-list li:nth-child(even) {
background-color: var(--secondary-bg);
}



/ File List Items /
.file-browser .file-list a.file-browser-link {
display: flex;
align-items: center;
padding: 8px;
border-radius: 3px;
transition: background-color 0.2s, color 0.2s;
color: var(--text-color);
text-decoration: none;
}

.file-browser .file-list a.file-browser-link:hover {
background-color: var(--button-hover-bg-color) !important;
color: var(--button-text-color);
}

.file-browser .file-list a.file-browser-link i {
margin-right: 8px;
font-size: 16px;
}

.file-browser .file-list a.file-browser-link.parent-dir {
color: var(--parent-dir-color);
font-weight: bold;
}

.file-browser .file-list a.file-browser-link.directory {
position: relative;
color: var(--directory-color);
}

.file-browser .file-list a.file-browser-link.file {
color: var(--file-color);
}

/ / / Scrollable content / / /
.scrollable {
max-height: 300px;
overflow-y: auto;
margin-bottom: 20px;
padding: 10px;
border: 1px solid var(--border-color);
border-radius: 5px;
background-color: var(--secondary-bg);
}

/ / / Prompt form / / /
#prompt-form {
display: flex;
flex-direction: column;
}

#prompt-input {
resize: vertical; / Allow only vertical resizing /
min-height: 100px;
overflow: hidden; / Hide overflow to maintain layout /
}

.no-resize {
resize: none;
}

.button-group {
display: flex;
flex-wrap: wrap;
gap: 10px;
}

.context-buttons {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 10px;
}

.user-message, .assistant-message {
margin-bottom: 10px;
padding: 10px;
border-radius: 5px;
word-wrap: break-word;
font-family: var(--font-family);
font-size: 16px;
}

.user-message {
background-color: var(--user-message-bg-color);
color: white;
}

.assistant-message {
background-color: var(--assistant-message-bg-color);
color: var(--text-color);
border: 1px solid var(--border-color);
}

/ / / CodeMirror editor styling / / /
.code-container {
position: relative;
margin-top: 20px;
max-width: 100%;
max-height: 600px;
display: flex;
flex-direction: column;
border: 1px solid var(--border-color);
border-radius: 5px;
overflow: hidden;
}

.CodeMirror {
flex-grow: 1;
max-height: 500px;
overflow: hidden; / Hidden to manage scroll within CodeMirror-scroll /
width: 100%;
font-size: 14px;
}

.CodeMirror-scroll {
overflow-x: auto;
overflow-y: auto;
}

.CodeMirror pre {
word-wrap: normal; / Prevent wrapping of code lines /
}

.cm-s-material-darker.CodeMirror {
background-color: #263238;
color: #ECEFF1;
}

.cm-s-material-darker .CodeMirror-gutters {
background-color: #263238;
border-right: 0;
}

.cm-s-material-darker .CodeMirror-linenumber {
color: #546E7A;
}

.cm-s-material-darker .CodeMirror-cursor {
border-left: 1px solid #FFCC00;
}

/ / / Code buttons / / /
.code-buttons {
display: flex;
justify-content: flex-end;
padding: 10px;
}

.code-buttons button {
margin-left: 10px;
}

/ / / Error and info messages / / /
.error-message {
color: white;
background-color: var(--error-color);
padding: 10px;
border-radius: 5px;
margin-top: 10px;
}

.info-message {
background-color: var(--info-color);
color: white;
padding: 10px;
border-radius: 5px;
margin-top: 10px;
}

.context-display {
background-color: var(--secondary-bg);
padding: 10px;
border-radius: 5px;
margin-top: 10px;
border: 1px solid var(--border-color);
}

/ / / Responsive Adjustments / / /
@media (max-width: 1199px) {
.main-grid {
flex-direction: column;
}
.left-column, .right-column {
width: 100%;
}
}

@media (max-width: 767px) {
.main-grid {
flex-direction: column;
}
.left-column, .right-column {
width: 100%;
}
.button-group, .context-buttons {
flex-direction: column;
}
button {
width: 100%;
}
.code-container {
max-height: 300px;
}
}

.file-browser-container {
transition: width 0.3s ease;
overflow: hidden;
position: relative;
width: 250px; / Expanded width /
display: flex;
flex-direction: column;
}

.file-browser-container.collapsed {
width: 50px; / Only show the header and toggle button /
}

.file-browser-content {
overflow: hidden;
transition: opacity 0.3s ease;
opacity: 1;
}

.file-browser-container.collapsed .file-browser-content {
opacity: 0;
pointer-events: none;
}


.toggle-btn {
background: none;
border: none;
cursor: pointer;
color: var(--text-color);
font-size: 18px;
}

.file-browser-container.collapsed .toggle-btn {
transform: rotate(180deg);
}

.file-browser-header-container {
display: flex;
align-items: center;
justify-content: space-between;
}

.file-browser-header {
font-size: 18px;
margin: 0;
}


/ / / Gutter styling for Split.js / /
.gutter {
background-color: var(--border-color);
background-repeat: no-repeat;
background-position: center;
background-size: 100% 100%;
cursor: col-resize;
width: 10px; / Match gutterSize in Split.js /
height: 100%;
z-index: 1; / Ensure it's above other elements */
}

.gutter:hover {
background-color: var(--accent-color);
}


.breadcrumb {
display: flex;
flex-wrap: wrap;
align-items: center;
margin-bottom: 10px;
font-size: 14px;
}

.breadcrumb-link {
color: var(--link-color);
text-decoration: none;
margin-right: 5px;
display: flex;
align-items: center;
}


.breadcrumb-link:hover {
text-decoration: underline;
}

.breadcrumb-separator {
margin-right: 5px;
color: var(--text-color);
}

here is my current functionality pay close attention to how all the UI features fit together and the critical areas. This is a constant evolution building out more and more relevant enhancements improving the screen usage, the look, the beauty, the functionality. I will provide you a targeted area to focus on next after your broad analysis. Really absorb its current state feel how it is being evolved and my intent. Some of the pieces may not make complete sense yet but their are hints at where it is going. Look ahead based on my high level. feel how i am incrementally steering it to be beautiful functional enhanced. Then I will provide you the section I am immediately interested in!