diff --git a/cli/hero.v b/cli/hero.v index b32042ad..4e26b040 100644 --- a/cli/hero.v +++ b/cli/hero.v @@ -85,7 +85,7 @@ fn do() ! { herocmds.cmd_git(mut cmd) herocmds.cmd_generator(mut cmd) herocmds.cmd_docusaurus(mut cmd) - // herocmds.cmd_web(mut cmd) + herocmds.cmd_web(mut cmd) cmd.setup() cmd.parse(os.args) diff --git a/examples/web/ui_demo.vsh b/examples/web/ui_demo.vsh index 50711018..10f395eb 100755 --- a/examples/web/ui_demo.vsh +++ b/examples/web/ui_demo.vsh @@ -1,13 +1,13 @@ #!/usr/bin/env -S v -n -w -gc none -cg -cc tcc -d use_openssl -enable-globals run -// import freeflowuniverse.herolib.web.ui +import freeflowuniverse.herolib.web.ui -// fn main() { -// println('Starting UI test server on port 8080...') -// println('Visit http://localhost:8080 to see the admin interface') +fn main() { + println('Starting UI test server on port 8080...') + println('Visit http://localhost:8080 to see the admin interface') -// ui.start( -// title: 'Test Admin Panel' -// port: 8080 -// )! -// } + ui.start( + title: 'Test Admin Panel' + port: 8080 + )! +} diff --git a/lib/core/herocmds/web.v b/lib/core/herocmds/web.v index 5ee52fd9..805c68f9 100644 --- a/lib/core/herocmds/web.v +++ b/lib/core/herocmds/web.v @@ -1,110 +1,110 @@ module herocmds -// import freeflowuniverse.herolib.ui.console -// import freeflowuniverse.herolib.web.ui -// import os -// import cli { Command, Flag } -// import time +import freeflowuniverse.herolib.ui.console +import freeflowuniverse.herolib.web.ui +import os +import cli { Command, Flag } +import time -// pub fn cmd_web(mut cmdroot Command) Command { -// mut cmd_run := Command{ -// name: 'web' -// description: 'Run the Heroprompt UI (located in lib/web/heroprompt).' -// required_args: 0 -// execute: cmd_web_execute -// } +pub fn cmd_web(mut cmdroot Command) Command { + mut cmd_run := Command{ + name: 'web' + description: 'Run the Heroprompt UI (located in lib/web/heroprompt).' + required_args: 0 + execute: cmd_web_execute + } -// cmd_run.add_flag(Flag{ -// flag: .bool -// required: false -// name: 'open' -// abbrev: 'o' -// description: 'Open the UI in the default browser after starting the server.' -// }) + cmd_run.add_flag(Flag{ + flag: .bool + required: false + name: 'open' + abbrev: 'o' + description: 'Open the UI in the default browser after starting the server.' + }) -// cmd_run.add_flag(Flag{ -// flag: .string -// required: false -// name: 'host' -// abbrev: 'h' -// description: 'Host to bind the server to (default: localhost).' -// }) + cmd_run.add_flag(Flag{ + flag: .string + required: false + name: 'host' + abbrev: 'h' + description: 'Host to bind the server to (default: localhost).' + }) -// cmd_run.add_flag(Flag{ -// flag: .int -// required: false -// name: 'port' -// abbrev: 'p' -// description: 'Port to bind the server to (default: 8080).' -// }) + cmd_run.add_flag(Flag{ + flag: .int + required: false + name: 'port' + abbrev: 'p' + description: 'Port to bind the server to (default: 8080).' + }) -// cmdroot.add_command(cmd_run) -// return cmdroot -// } + cmdroot.add_command(cmd_run) + return cmdroot +} -// fn cmd_web_execute(cmd Command) ! { -// // ---------- FLAGS ---------- -// mut open_ := cmd.flags.get_bool('open') or { false } -// mut host := cmd.flags.get_string('host') or { 'localhost' } -// mut port := cmd.flags.get_int('port') or { 8080 } +fn cmd_web_execute(cmd Command) ! { + // ---------- FLAGS ---------- + mut open_ := cmd.flags.get_bool('open') or { false } + mut host := cmd.flags.get_string('host') or { 'localhost' } + mut port := cmd.flags.get_int('port') or { 8080 } -// // Set defaults if not provided -// if host == '' { -// host = 'localhost' -// } -// if port == 0 { -// port = 8080 -// } + // Set defaults if not provided + if host == '' { + host = 'localhost' + } + if port == 0 { + port = 8080 + } -// console.print_header('Starting Heroprompt...') + console.print_header('Starting Heroprompt...') -// // Prepare arguments for the UI factory -// mut factory_args := ui.FactoryArgs{ -// title: 'Hero Admin Panel' -// host: host -// port: port -// } + // Prepare arguments for the UI factory + mut factory_args := ui.FactoryArgs{ + title: 'Hero Admin Panel' + host: host + port: port + } -// // ---------- START WEB SERVER ---------- -// console.print_header('Starting Heroprompt server...') + // ---------- START WEB SERVER ---------- + console.print_header('Starting Heroprompt server...') -// // Start the server in a separate thread to allow for browser opening -// spawn fn [factory_args] () { -// ui.start(factory_args) or { -// console.print_stderr('Failed to start Heroprompt server: ${err}') -// return -// } -// }() + // Start the server in a separate thread to allow for browser opening + spawn fn [factory_args] () { + ui.start(factory_args) or { + console.print_stderr('Failed to start Heroprompt server: ${err}') + return + } + }() -// // Give the server a moment to start -// time.sleep(2 * time.second) -// url := 'http://${factory_args.host}:${factory_args.port}' + // Give the server a moment to start + time.sleep(2 * time.second) + url := 'http://${factory_args.host}:${factory_args.port}' -// console.print_green('Heroprompt server is running on ${url}') + console.print_green('Heroprompt server is running on ${url}') -// if open_ { -// mut cmd_str := '' -// $if macos { -// cmd_str = 'open ${url}' -// } $else $if linux { -// cmd_str = 'xdg-open ${url}' -// } $else $if windows { -// cmd_str = 'start ${url}' -// } + if open_ { + mut cmd_str := '' + $if macos { + cmd_str = 'open ${url}' + } $else $if linux { + cmd_str = 'xdg-open ${url}' + } $else $if windows { + cmd_str = 'start ${url}' + } -// if cmd_str != '' { -// result := os.execute(cmd_str) -// if result.exit_code == 0 { -// console.print_green('Opened Heroprompt in default browser.') -// } else { -// console.print_stderr('Failed to open browser: ${result.output}') -// } -// } -// } + if cmd_str != '' { + result := os.execute(cmd_str) + if result.exit_code == 0 { + console.print_green('Opened Heroprompt in default browser.') + } else { + console.print_stderr('Failed to open browser: ${result.output}') + } + } + } -// // Keep the process alive while the server runs -// console.print_header('Press Ctrl+C to stop the server') -// for { -// time.sleep(1 * time.second) -// } -// } + // Keep the process alive while the server runs + console.print_header('Press Ctrl+C to stop the server') + for { + time.sleep(1 * time.second) + } +} diff --git a/lib/web/heroprompt/endpoints.v b/lib/web/heroprompt/endpoints.v deleted file mode 100644 index a48a45ad..00000000 --- a/lib/web/heroprompt/endpoints.v +++ /dev/null @@ -1,315 +0,0 @@ -module heroprompt - -import veb -import os -import json -import time -import freeflowuniverse.herolib.develop.heroprompt as hp - -// Types for directory listing -struct DirItem { - name string - typ string @[json: 'type'] -} - -struct DirResp { - path string -mut: - items []DirItem -} - -// HTML routes -@['/heroprompt'; get] -pub fn (app &App) page_index(mut ctx Context) veb.Result { - return ctx.html(render_index(app)) -} - -// API routes (thin wrappers over develop.heroprompt) -@['/api/heroprompt/workspaces'; get] -pub fn (app &App) api_list(mut ctx Context) veb.Result { - mut names := []string{} - ws := hp.list(fromdb: true) or { []&hp.Workspace{} } - for w in ws { - names << w.name - } - ctx.set_content_type('application/json') - return ctx.text(json.encode(names)) -} - -@['/api/heroprompt/workspaces'; post] -pub fn (app &App) api_create(mut ctx Context) veb.Result { - name := ctx.form['name'] or { 'default' } - base_path_in := ctx.form['base_path'] or { '' } - if base_path_in.len == 0 { - return ctx.text('{"error":"base_path required"}') - } - mut base_path := base_path_in - // Expand tilde to user home - if base_path.starts_with('~') { - home := os.home_dir() - base_path = os.join_path(home, base_path.all_after('~')) - } - _ := hp.get(name: name, create: true, path: base_path) or { - return ctx.text('{"error":"create failed"}') - } - ctx.set_content_type('application/json') - return ctx.text(json.encode({ - 'name': name - 'base_path': base_path - })) -} - -@['/api/heroprompt/directory'; get] -pub fn (app &App) api_directory(mut ctx Context) veb.Result { - wsname := ctx.query['name'] or { 'default' } - path_q := ctx.query['path'] or { '' } - mut wsp := hp.get(name: wsname, create: false) or { - return ctx.text('{"error":"workspace not found"}') - } - // Use workspace list method; empty path means base_path - items_w := if path_q.len > 0 { wsp.list() or { - return ctx.text('{"error":"cannot list directory"}')} } else { wsp.list() or { - return ctx.text('{"error":"cannot list directory"}')} } - ctx.set_content_type('application/json') - mut resp := DirResp{ - path: if path_q.len > 0 { path_q } else { wsp.base_path } - } - for it in items_w { - resp.items << DirItem{ - name: it.name - typ: it.typ - } - } - return ctx.text(json.encode(resp)) -} - -// -------- File content endpoint -------- -struct FileResp { - language string - content string -} - -@['/api/heroprompt/file'; get] -pub fn (app &App) api_file(mut ctx Context) veb.Result { - wsname := ctx.query['name'] or { 'default' } - path_q := ctx.query['path'] or { '' } - if path_q.len == 0 { - return ctx.text('{"error":"path required"}') - } - mut base := '' - if wsp := hp.get(name: wsname, create: false) { - base = wsp.base_path - } - mut file_path := if !os.is_abs_path(path_q) && base.len > 0 { - os.join_path(base, path_q) - } else { - path_q - } - if !os.is_file(file_path) { - return ctx.text('{"error":"not a file"}') - } - // limit read to 1MB to avoid huge responses - max_size := i64(1_000_000) - sz := os.file_size(file_path) - if sz > max_size { - return ctx.text('{"error":"file too large"}') - } - content := os.read_file(file_path) or { return ctx.text('{"error":"failed to read"}') } - lang := detect_lang(file_path) - ctx.set_content_type('application/json') - return ctx.text(json.encode(FileResp{ language: lang, content: content })) -} - -fn detect_lang(path string) string { - ext := os.file_ext(path).trim_left('.') - return match ext.to_lower() { - 'v' { 'v' } - 'js' { 'javascript' } - 'ts' { 'typescript' } - 'py' { 'python' } - 'rs' { 'rust' } - 'go' { 'go' } - 'java' { 'java' } - 'c', 'h' { 'c' } - 'cpp', 'hpp', 'cc', 'hh' { 'cpp' } - 'sh', 'bash' { 'bash' } - 'json' { 'json' } - 'yaml', 'yml' { 'yaml' } - 'html', 'htm' { 'html' } - 'css' { 'css' } - 'md' { 'markdown' } - else { 'text' } - } -} - -// -------- Filename search endpoint -------- -struct SearchItem { - path string - typ string @[json: 'type'] -} - -@['/api/heroprompt/search'; get] -pub fn (app &App) api_search(mut ctx Context) veb.Result { - wsname := ctx.query['name'] or { 'default' } - q := ctx.query['q'] or { '' } - if q.len == 0 { - return ctx.text('{"error":"q required"}') - } - mut base := '' - if wsp := hp.get(name: wsname, create: false) { - base = wsp.base_path - } - if base.len == 0 { - return ctx.text('{"error":"workspace base_path not set"}') - } - max := (ctx.query['max'] or { '200' }).int() - mut results := []SearchItem{} - walk_search(base, q, max, mut results) - ctx.set_content_type('application/json') - return ctx.text(json.encode(results)) -} - -// Workspace details -@['/api/heroprompt/workspaces/:name'; get] -pub fn (app &App) api_workspace_get(mut ctx Context, name string) veb.Result { - wsp := hp.get(name: name, create: false) or { - return ctx.text('{"error":"workspace not found"}') - } - ctx.set_content_type('application/json') - return ctx.text(json.encode({ - 'name': wsp.name - 'base_path': wsp.base_path - })) -} - -@['/api/heroprompt/workspaces/:name'; delete] -pub fn (app &App) api_workspace_delete(mut ctx Context, name string) veb.Result { - wsp := hp.get(name: name, create: false) or { - return ctx.text('{"error":"workspace not found"}') - } - wsp.delete_workspace() or { return ctx.text('{"error":"delete failed"}') } - return ctx.text('{"ok":true}') -} - -@['/api/heroprompt/workspaces/:name'; patch] -pub fn (app &App) api_workspace_patch(mut ctx Context, name string) veb.Result { - wsp := hp.get(name: name, create: false) or { - return ctx.text('{"error":"workspace not found"}') - } - new_name := ctx.form['name'] or { '' } - mut base_path := ctx.form['base_path'] or { '' } - if base_path.len > 0 && base_path.starts_with('~') { - home := os.home_dir() - base_path = os.join_path(home, base_path.all_after('~')) - } - updated := wsp.update_workspace(name: new_name, base_path: base_path) or { - return ctx.text('{"error":"update failed"}') - } - ctx.set_content_type('application/json') - return ctx.text(json.encode({ - 'name': updated.name - 'base_path': updated.base_path - })) -} - -// -------- Path validation endpoint -------- -struct PathValidationResp { - is_abs bool - exists bool - is_dir bool - expanded string -} - -@['/api/heroprompt/validate_path'; get] -pub fn (app &App) api_validate_path(mut ctx Context) veb.Result { - p_in := ctx.query['path'] or { '' } - mut p := p_in - if p.starts_with('~') { - home := os.home_dir() - p = os.join_path(home, p.all_after('~')) - } - is_abs := if p != '' { os.is_abs_path(p) } else { false } - exists := if p != '' { os.exists(p) } else { false } - isdir := if exists { os.is_dir(p) } else { false } - ctx.set_content_type('application/json') - resp := PathValidationResp{ - is_abs: is_abs - exists: exists - is_dir: isdir - expanded: p - } - return ctx.text(json.encode(resp)) -} - -fn walk_search(root string, q string, max int, mut out []SearchItem) { - if out.len >= max { - return - } - entries := os.ls(root) or { return } - for e in entries { - if e in ['.git', 'node_modules', 'build', 'dist', '.v'] { - continue - } - p := os.join_path(root, e) - if os.is_dir(p) { - if out.len >= max { - return - } - if e.to_lower().contains(q.to_lower()) { - out << SearchItem{ - path: p - typ: 'directory' - } - } - walk_search(p, q, max, mut out) - } else if os.is_file(p) { - if e.to_lower().contains(q.to_lower()) { - out << SearchItem{ - path: p - typ: 'file' - } - } - } - if out.len >= max { - return - } - } -} - -// -------- Selection and prompt endpoints -------- -@['/api/heroprompt/workspaces/:name/files'; post] -pub fn (app &App) api_add_file(mut ctx Context, name string) veb.Result { - path := ctx.form['path'] or { '' } - if path.len == 0 { - return ctx.text('{"error":"path required"}') - } - mut wsp := hp.get(name: name, create: false) or { - return ctx.text('{"error":"workspace not found"}') - } - wsp.add_file(path: path) or { return ctx.text('{"error":"' + err.msg() + '"}') } - return ctx.text('{"ok":true}') -} - -@['/api/heroprompt/workspaces/:name/dirs'; post] -pub fn (app &App) api_add_dir(mut ctx Context, name string) veb.Result { - path := ctx.form['path'] or { '' } - if path.len == 0 { - return ctx.text('{"error":"path required"}') - } - mut wsp := hp.get(name: name, create: false) or { - return ctx.text('{"error":"workspace not found"}') - } - wsp.add_dir(path: path) or { return ctx.text('{"error":"' + err.msg() + '"}') } - return ctx.text('{"ok":true}') -} - -@['/api/heroprompt/workspaces/:name/prompt'; post] -pub fn (app &App) api_generate_prompt(mut ctx Context, name string) veb.Result { - text := ctx.form['text'] or { '' } - mut wsp := hp.get(name: name, create: false) or { - return ctx.text('{"error":"workspace not found"}') - } - prompt := wsp.prompt(text: text) - ctx.set_content_type('text/plain') - return ctx.text(prompt) -} diff --git a/lib/web/heroprompt/server.v b/lib/web/heroprompt/server.v deleted file mode 100644 index 8ba215cf..00000000 --- a/lib/web/heroprompt/server.v +++ /dev/null @@ -1,71 +0,0 @@ -module heroprompt - -import veb -import os - -// Public Context type for veb -pub struct Context { - veb.Context -} - -// Factory args for starting the server -@[params] -pub struct FactoryArgs { -pub mut: - host string = 'localhost' - port int = 8090 - title string = 'Heroprompt' -} - -// App holds server state and config -pub struct App { - veb.StaticHandler -pub mut: - title string - port int - base_path string // absolute path to this module directory -} - -// Create a new App instance (does not start the server) -pub fn new(args FactoryArgs) !&App { - base := os.dir(@FILE) - mut app := App{ - title: args.title - port: args.port - base_path: base - } - // Serve static assets from this module at /static - app.mount_static_folder_at(os.join_path(base, 'static'), '/static')! - return &app -} - -// Start the webserver (blocking) -pub fn start(args FactoryArgs) ! { - mut app := new(args)! - veb.run[App, Context](mut app, app.port) -} - -// Routes - -@['/'; get] -pub fn (app &App) index(mut ctx Context) veb.Result { - return ctx.html(render_index(app)) -} - -// Rendering helpers -fn render_index(app &App) string { - tpl := os.join_path(app.base_path, 'templates', 'index.html') - content := os.read_file(tpl) or { return render_index_fallback(app) } - return render_template(content, { - 'title': app.title - }) -} - -fn render_index_fallback(app &App) string { - return - '\n
Heroprompt server is running.
HeroScript editor template not found. Please check the template files.
-// Back to Admin -//', '
') + return result +} diff --git a/lib/web/ui/templates/css/chat.css b/lib/web/ui/chat/static/css/chat.css similarity index 100% rename from lib/web/ui/templates/css/chat.css rename to lib/web/ui/chat/static/css/chat.css diff --git a/lib/web/ui/templates/js/chat.js b/lib/web/ui/chat/static/js/chat.js similarity index 100% rename from lib/web/ui/templates/js/chat.js rename to lib/web/ui/chat/static/js/chat.js diff --git a/lib/web/ui/templates/chat.html b/lib/web/ui/chat/templates/chat.html similarity index 100% rename from lib/web/ui/templates/chat.html rename to lib/web/ui/chat/templates/chat.html diff --git a/lib/web/ui/chat/utils.v b/lib/web/ui/chat/utils.v new file mode 100644 index 00000000..127b3671 --- /dev/null +++ b/lib/web/ui/chat/utils.v @@ -0,0 +1,4 @@ +module chat + +// Placeholder for chat-specific utilities + diff --git a/lib/web/ui/endpoints.v b/lib/web/ui/endpoints.v deleted file mode 100644 index 91d694c8..00000000 --- a/lib/web/ui/endpoints.v +++ /dev/null @@ -1,244 +0,0 @@ -module ui - -// import veb -// import freeflowuniverse.herolib.develop.heroprompt -// import os -// import json - -// // Directory browsing and file read endpoints for Heroprompt.js compatibility -// struct DirItem { -// name string -// typ string @[json: 'type'] -// } - -// struct DirResp { -// path string -// mut: -// items []DirItem -// } - -// @['/api/heroprompt/directory'; get] -// pub fn (app &App) api_heroprompt_directory(mut ctx Context) veb.Result { -// // Optional workspace name, defaults to 'default' -// wsname := ctx.query['name'] or { 'default' } -// path_q := ctx.query['path'] or { '' } -// if path_q.len == 0 { -// return ctx.text('{"error":"path required"}') -// } -// // Try to resolve against workspace base_path if available, but do not require it -// mut base := '' -// if wsp := heroprompt.get(name: wsname, create: false) { -// base = wsp.base_path -// } -// // Resolve path: if absolute, use as-is; else join with base -// mut dir_path := path_q -// if !os.is_abs_path(dir_path) && base.len > 0 { -// dir_path = os.join_path(base, dir_path) -// } -// // List entries -// entries := os.ls(dir_path) or { return ctx.text('{"error":"cannot list directory"}') } -// mut items := []map[string]string{} -// for e in entries { -// full := os.join_path(dir_path, e) -// if os.is_dir(full) { -// items << { -// 'name': e -// 'type': 'directory' -// } -// } else if os.is_file(full) { -// items << { -// 'name': e -// 'type': 'file' -// } -// } -// } -// ctx.set_content_type('application/json') -// // Encode strongly typed JSON response -// mut resp := DirResp{ -// path: dir_path -// } -// for it in items { -// resp.items << DirItem{ -// name: it['name'] or { '' } -// typ: it['type'] or { '' } -// } -// } -// return ctx.text(json.encode(resp)) -// } - -// @['/api/heroprompt/file'; get] -// pub fn (app &App) api_heroprompt_file(mut ctx Context) veb.Result { -// wsname := ctx.query['name'] or { 'default' } -// path_q := ctx.query['path'] or { '' } -// if path_q.len == 0 { -// return ctx.text('{"error":"path required"}') -// } -// // Try to resolve against workspace base_path if available, but do not require it -// mut base := '' -// if wsp := heroprompt.get(name: wsname, create: false) { -// base = wsp.base_path -// } -// mut file_path := path_q -// if !os.is_abs_path(file_path) && base.len > 0 { -// file_path = os.join_path(base, file_path) -// } -// content := os.read_file(file_path) or { return ctx.text('{"error":"failed to read"}') } -// lang := detect_lang(file_path) -// ctx.set_content_type('application/json') -// return ctx.text(json.encode({ -// 'language': lang -// 'content': content -// })) -// } - -// fn detect_lang(path string) string { -// ext := os.file_ext(path).trim_left('.') -// return match ext.to_lower() { -// 'v' { 'v' } -// 'js' { 'javascript' } -// 'ts' { 'typescript' } -// 'py' { 'python' } -// 'rs' { 'rust' } -// 'go' { 'go' } -// 'java' { 'java' } -// 'c', 'h' { 'c' } -// 'cpp', 'hpp', 'cc', 'hh' { 'cpp' } -// 'sh', 'bash' { 'bash' } -// 'json' { 'json' } -// 'yaml', 'yml' { 'yaml' } -// 'html', 'htm' { 'html' } -// 'css' { 'css' } -// 'md' { 'markdown' } -// else { 'text' } -// } -// } - -// // Heroprompt API: list workspaces -// @['/api/heroprompt/workspaces'; get] -// pub fn (app &App) api_heroprompt_list(mut ctx Context) veb.Result { -// mut names := []string{} -// ws := heroprompt.list(fromdb: true) or { []&heroprompt.Workspace{} } -// for w in ws { -// names << w.name -// } -// ctx.set_content_type('application/json') -// return ctx.text(json.encode(names)) -// } - -// // Heroprompt API: create/get workspace -// @['/api/heroprompt/workspaces'; post] -// pub fn (app &App) api_heroprompt_create(mut ctx Context) veb.Result { -// name := ctx.form['name'] or { '' } -// base_path := ctx.form['base_path'] or { '' } - -// if base_path.len == 0 { -// return ctx.text('{"error":"base_path required"}') -// } - -// mut wsp := heroprompt.get(name: name, create: true, path: base_path) or { -// return ctx.text('{"error":"create failed"}') -// } - -// ctx.set_content_type('application/json') -// return ctx.text(json.encode({ -// 'name': name -// 'base_path': base_path -// })) -// } - -// // Heroprompt API: add directory to workspace -// @['/api/heroprompt/workspaces/:name/dirs'; post] -// pub fn (app &App) api_heroprompt_add_dir(mut ctx Context, name string) veb.Result { -// path := ctx.form['path'] or { '' } -// if path.len == 0 { -// return ctx.text('{"error":"path required"}') -// } -// mut wsp := heroprompt.get(name: name, create: true) or { -// return ctx.text('{"error":"workspace not found"}') -// } -// wsp.add_dir(path: path) or { return ctx.text('{"error":"' + err.msg() + '"}') } -// ctx.set_content_type('application/json') -// return ctx.text('{"ok":true}') -// } - -// // Heroprompt API: add file to workspace -// @['/api/heroprompt/workspaces/:name/files'; post] -// pub fn (app &App) api_heroprompt_add_file(mut ctx Context, name string) veb.Result { -// path := ctx.form['path'] or { '' } -// if path.len == 0 { -// return ctx.text('{"error":"path required"}') -// } -// mut wsp := heroprompt.get(name: name, create: true) or { -// return ctx.text('{"error":"workspace not found"}') -// } -// wsp.add_file(path: path) or { return ctx.text('{"error":"' + err.msg() + '"}') } -// ctx.set_content_type('application/json') -// return ctx.text('{"ok":true}') -// } - -// // Heroprompt API: generate prompt -// @['/api/heroprompt/workspaces/:name/prompt'; post] -// pub fn (app &App) api_heroprompt_prompt(mut ctx Context, name string) veb.Result { -// text := ctx.form['text'] or { '' } -// mut wsp := heroprompt.get(name: name, create: false) or { -// return ctx.text('{"error":"workspace not found"}') -// } -// prompt := wsp.prompt(text: text) -// ctx.set_content_type('text/plain') -// return ctx.text(prompt) -// } - -// // Heroprompt API: get workspace details -// @['/api/heroprompt/workspaces/:name'; get] -// pub fn (app &App) api_heroprompt_get(mut ctx Context, name string) veb.Result { -// wsp := heroprompt.get(name: name, create: false) or { -// return ctx.text('{"error":"workspace not found"}') -// } -// mut children := []map[string]string{} -// for ch in wsp.children { -// children << { -// 'name': ch.name -// 'path': ch.path.path -// 'type': if ch.path.cat == .dir { 'directory' } else { 'file' } -// } -// } -// ctx.set_content_type('application/json') -// return ctx.text(json.encode({ -// 'name': wsp.name -// 'base_path': wsp.base_path -// 'children': json.encode(children) -// })) -// } - -// // Heroprompt API: delete workspace -// @['/api/heroprompt/workspaces/:name'; delete] -// pub fn (app &App) api_heroprompt_delete(mut ctx Context, name string) veb.Result { -// wsp := heroprompt.get(name: name, create: false) or { -// return ctx.text('{"error":"workspace not found"}') -// } -// wsp.delete_workspace() or { return ctx.text('{"error":"delete failed"}') } -// ctx.set_content_type('application/json') -// return ctx.text('{"ok":true}') -// } - -// // Heroprompt API: remove directory -// @['/api/heroprompt/workspaces/:name/dirs/remove'; post] -// pub fn (app &App) api_heroprompt_remove_dir(mut ctx Context, name string) veb.Result { -// path := ctx.form['path'] or { '' } -// mut wsp := heroprompt.get(name: name, create: false) or { -// return ctx.text('{"error":"workspace not found"}') -// } -// wsp.remove_dir(path: path, name: '') or { return ctx.text('{"error":"' + err.msg() + '"}') } -// return ctx.text('{"ok":true}') -// } - -// // Heroprompt API: remove file -// @['/api/heroprompt/workspaces/:name/files/remove'; post] -// pub fn (app &App) api_heroprompt_remove_file(mut ctx Context, name string) veb.Result { -// path := ctx.form['path'] or { '' } -// mut wsp := heroprompt.get(name: name, create: false) or { -// return ctx.text('{"error":"workspace not found"}') -// } -// wsp.remove_file(path: path, name: '') or { return ctx.text('{"error":"' + err.msg() + '"}') } -// return ctx.text('{"ok":true}') -// } diff --git a/lib/web/ui/factory.v b/lib/web/ui/factory.v deleted file mode 100644 index 384b1055..00000000 --- a/lib/web/ui/factory.v +++ /dev/null @@ -1,471 +0,0 @@ -module ui - -// import veb -// import os - -// // Public Context type for veb -// pub struct Context { -// veb.Context -// } - -// // Simple tree menu structure -// pub struct MenuItem { -// pub: -// title string -// href string -// children []MenuItem -// } - -// // Factory args -// @[params] -// pub struct WebArgs { -// pub mut: -// name string = 'default' -// host string = 'localhost' -// port int = 8080 -// title string = 'Admin' -// menu []MenuItem -// open bool -// } - -// // The App holds server state and config -// pub struct App { -// veb.StaticHandler -// pub mut: -// title string = 'default' -// menu []MenuItem -// port int = 7711 -// } - -// // Start the webserver (blocking) -// pub fn start(args WebArgs) ! { -// mut app := App{ -// title: args.title -// menu: args.menu -// port: args.port -// } -// veb.run[App, Context](mut app, app.port) -// } - -// // Routes - -// // Redirect root to /admin -// @['/'; get] -// pub fn (app &App) root(mut ctx Context) veb.Result { -// return ctx.redirect('/admin') -// } - -// // Admin home page -// @['/admin'; get] -// pub fn (app &App) admin_index(mut ctx Context) veb.Result { -// return ctx.html(app.render_admin('/', 'Welcome')) -// } - -// // HeroScript editor page -// @['/admin/heroscript'; get] -// pub fn (app &App) admin_heroscript(mut ctx Context) veb.Result { -// return ctx.html(app.render_heroscript()) -// } - -// // Chat page -// @['/admin/chat'; get] -// pub fn (app &App) admin_chat(mut ctx Context) veb.Result { -// return ctx.html(app.render_chat()) -// } - -// // Static CSS files -// @['/static/css/colors.css'; get] -// pub fn (app &App) serve_colors_css(mut ctx Context) veb.Result { -// css_path := os.join_path(os.dir(@FILE), 'templates', 'css', 'colors.css') -// css_content := os.read_file(css_path) or { return ctx.text('/* CSS file not found */') } -// ctx.set_content_type('text/css') -// return ctx.text(css_content) -// } - -// @['/static/css/main.css'; get] -// pub fn (app &App) serve_main_css(mut ctx Context) veb.Result { -// css_path := os.join_path(os.dir(@FILE), 'templates', 'css', 'main.css') -// css_content := os.read_file(css_path) or { return ctx.text('/* CSS file not found */') } -// ctx.set_content_type('text/css') -// return ctx.text(css_content) -// } - -// // Static JS files -// @['/static/js/theme.js'; get] -// pub fn (app &App) serve_theme_js(mut ctx Context) veb.Result { -// js_path := os.join_path(os.dir(@FILE), 'templates', 'js', 'theme.js') -// js_content := os.read_file(js_path) or { return ctx.text('/* JS file not found */') } -// ctx.set_content_type('application/javascript') -// return ctx.text(js_content) -// } - -// @['/static/js/heroscript.js'; get] -// pub fn (app &App) serve_heroscript_js(mut ctx Context) veb.Result { -// js_path := os.join_path(os.dir(@FILE), 'templates', 'js', 'heroscript.js') -// js_content := os.read_file(js_path) or { return ctx.text('/* JS file not found */') } -// ctx.set_content_type('application/javascript') -// return ctx.text(js_content) -// } - -// @['/static/js/chat.js'; get] -// pub fn (app &App) serve_chat_js(mut ctx Context) veb.Result { -// js_path := os.join_path(os.dir(@FILE), 'templates', 'js', 'chat.js') -// js_content := os.read_file(js_path) or { return ctx.text('/* JS file not found */') } -// ctx.set_content_type('application/javascript') -// return ctx.text(js_content) -// } - -// @['/static/css/heroscript.css'; get] -// pub fn (app &App) serve_heroscript_css(mut ctx Context) veb.Result { -// css_path := os.join_path(os.dir(@FILE), 'templates', 'css', 'heroscript.css') -// css_content := os.read_file(css_path) or { return ctx.text('/* CSS file not found */') } -// ctx.set_content_type('text/css') -// return ctx.text(css_content) -// } - -// @['/static/css/chat.css'; get] -// pub fn (app &App) serve_chat_css(mut ctx Context) veb.Result { -// css_path := os.join_path(os.dir(@FILE), 'templates', 'css', 'chat.css') -// css_content := os.read_file(css_path) or { return ctx.text('/* CSS file not found */') } -// ctx.set_content_type('text/css') -// return ctx.text(css_content) -// } - -// // Catch-all content under /admin/* -// @['/admin/:path...'; get] -// pub fn (app &App) admin_section(mut ctx Context, path string) veb.Result { -// // Render current path in the main content -// return ctx.html(app.render_admin(path, 'Content')) -// } - -// // View rendering using external template - -// fn (app &App) render_admin(path string, heading string) string { -// // Get the template file path relative to the module -// template_path := os.join_path(os.dir(@FILE), 'templates', 'admin_layout.html') - -// // Read the template file -// template_content := os.read_file(template_path) or { -// // Fallback to inline template if file not found -// return app.render_admin_fallback(path, heading) -// } - -// // Generate menu HTML -// menu_content := menu_html(app.menu, 0, 'm') - -// // Simple template variable replacement -// mut result := template_content -// result = result.replace('{{.title}}', app.title) -// result = result.replace('{{.heading}}', heading) -// result = result.replace('{{.path}}', path) -// result = result.replace('{{.menu_html}}', menu_content) -// result = result.replace('{{.css_colors_url}}', '/static/css/colors.css') -// result = result.replace('{{.css_main_url}}', '/static/css/main.css') -// result = result.replace('{{.js_theme_url}}', '/static/js/theme.js') - -// return result -// } - -// // HeroScript editor rendering using external template -// fn (app &App) render_heroscript() string { -// // Get the template file path relative to the module -// template_path := os.join_path(os.dir(@FILE), 'templates', 'heroscript_editor.html') - -// // Read the template file -// template_content := os.read_file(template_path) or { -// // Fallback to basic template if file not found -// return app.render_heroscript_fallback() -// } - -// // Generate menu HTML -// menu_content := menu_html(app.menu, 0, 'm') - -// // Simple template variable replacement -// mut result := template_content -// result = result.replace('{{.title}}', app.title) -// result = result.replace('{{.menu_html}}', menu_content) -// result = result.replace('{{.css_colors_url}}', '/static/css/colors.css') -// result = result.replace('{{.css_main_url}}', '/static/css/main.css') -// result = result.replace('{{.css_heroscript_url}}', '/static/css/heroscript.css') -// result = result.replace('{{.js_theme_url}}', '/static/js/theme.js') -// result = result.replace('{{.js_heroscript_url}}', '/static/js/heroscript.js') - -// return result -// } - -// // Chat rendering using external template -// fn (app &App) render_chat() string { -// // Get the template file path relative to the module -// template_path := os.join_path(os.dir(@FILE), 'templates', 'chat.html') - -// // Read the template file -// template_content := os.read_file(template_path) or { -// // Fallback to basic template if file not found -// return app.render_chat_fallback() -// } - -// // Generate menu HTML -// menu_content := menu_html(app.menu, 0, 'm') - -// // Simple template variable replacement -// mut result := template_content -// result = result.replace('{{.title}}', app.title) -// result = result.replace('{{.menu_html}}', menu_content) -// result = result.replace('{{.css_colors_url}}', '/static/css/colors.css') -// result = result.replace('{{.css_main_url}}', '/static/css/main.css') -// result = result.replace('{{.css_chat_url}}', '/static/css/chat.css') -// result = result.replace('{{.js_theme_url}}', '/static/js/theme.js') -// result = result.replace('{{.js_chat_url}}', '/static/js/chat.js') - -// return result -// } - -// // Fallback HeroScript rendering method -// fn (app &App) render_heroscript_fallback() string { -// return ' -// -// -//
-// -// -//
-// -// -//
-//