diff --git a/src/snek/service/container.py b/src/snek/service/container.py index 0beaf2c..fba5484 100644 --- a/src/snek/service/container.py +++ b/src/snek/service/container.py @@ -22,6 +22,13 @@ class ContainerService(BaseService): self.event_listeners[name][event] = [] self.event_listeners[name][event].append(event_handler) + async def remove_event_listener(self, name, event, event_handler): + if name in self.event_listeners and event in self.event_listeners[name]: + try: + self.event_listeners[name][event].remove(event_handler) + except ValueError: + pass + async def container_event_handler(self, name, event, data): event_listeners = self.event_listeners.get(name, {}) handlers = event_listeners.get(event, []) diff --git a/src/snek/static/dumb-term.js b/src/snek/static/dumb-term.js index ecc930a..6558ee4 100644 --- a/src/snek/static/dumb-term.js +++ b/src/snek/static/dumb-term.js @@ -1,13 +1,32 @@ -class DumbTerminal extends HTMLElement { - constructor() { - super(); - this.attachShadow({ mode: "open" }); - } +import { NjetComponent } from "/njet.js"; +class WebTerminal extends NjetComponent { + + commands = { + "clear": () => { + this.outputEl.innerHTML = ""; + return ""; + }, + "help": () => { + return "Available commands: help, clear, date"; + }, + "date": () => { + return new Date().toString(); + } + }; + help = { + "clear": "Clear the terminal", + "help": "Show available commands", + "date": "Show the current date" + }; + + + connectedCallback() { - this.shadowRoot.innerHTML = ` + + this.innerHTML = ` - -
+
+
- > - + > + +
`; - - this.outputEl = this.shadowRoot.getElementById("output"); - this.inputEl = this.shadowRoot.getElementById("input"); + this.container = this.querySelector(".web-terminal"); + this.outputEl = this.querySelector(".web-terminal-output"); + this.inputEl = this.querySelector(".web-terminal-input"); this.history = []; this.historyIndex = -1; - this.inputEl.addEventListener("keydown", (e) => this.onKeyDown(e)); + this.inputEl.addEventListener("keyup", (e) => this.onKeyDown(e)); } onKeyDown(event) { @@ -116,17 +136,89 @@ class DumbTerminal extends HTMLElement { this.outputEl.scrollTop = this.outputEl.scrollHeight; } + parseCommand(input) { + const args = []; + let current = ''; + let inSingleQuote = false; + let inDoubleQuote = false; + let inTemplate = false; // For ${...} + let templateBuffer = ''; + + for (let i = 0; i < input.length; i++) { + const char = input[i]; + + // Handle template expressions + if (!inSingleQuote && !inDoubleQuote && char === '$' && input[i + 1] === '{') { + inTemplate = true; + i++; // Skip '{' + templateBuffer = '${'; + continue; + } + + if (inTemplate) { + templateBuffer += char; + if (char === '}') { + // End of template + args.push(eval(templateBuffer)); + inTemplate = false; + templateBuffer = ''; + } + continue; // Continue to next char + } + + // Handle quotes + if (char === "'" && !inDoubleQuote) { + inSingleQuote = !inSingleQuote; + continue; // Skip quote + } + + if (char === '"' && !inSingleQuote) { + inDoubleQuote = !inDoubleQuote; + continue; // Skip quote + } + + // Handle spaces outside quotes and templates + if (char === ' ' && !inSingleQuote && !inDoubleQuote) { + if (current.length > 0) { + args.push(current); + current = ''; + } + } else { + current += char; + } + } + + if (current.length > 0) { + args.push(current); + } + + return args; + } + executeScript(script) { + const scriptElement = document.createElement("script"); + scriptElement.textContent = script; + this.appendChild(scriptElement); + } mockExecute(command) { - switch (command.trim()) { - case "help": - return "Available commands: help, clear, date"; - case "date": - return new Date().toString(); - case "clear": - this.outputEl.innerHTML = ""; - return ""; - default: - return `Unknown command: ${command}`; + let args; + try { + args = this.parseCommand(command); + } catch (e) { + return e.toString(); + } + console.info({ef:this}) + console.info({af:this}) + console.info({gf:args}) + let cmdName = args.shift(); + let commandHandler = this.commands[cmdName]; + if (commandHandler) { + return commandHandler.apply(this, args); + } + try { + // Try to eval as JS + return this.executeScript(command); + } catch (e) { + return e.toString(); } } @@ -134,7 +226,7 @@ class DumbTerminal extends HTMLElement { * Static method to create a modal dialog with the terminal * @returns {HTMLDialogElement} */ - static createModal() { + show() { const dialog = document.createElement("dialog"); dialog.innerHTML = `
@@ -147,4 +239,12 @@ class DumbTerminal extends HTMLElement { } } +window.showTerm = function (options) { + const term = new WebTerminal(options); + term.show(); + return term; +} + customElements.define("web-terminal", WebTerminal); +export { WebTerminal }; + diff --git a/src/snek/static/njet.js b/src/snek/static/njet.js index a23aeb6..3739f7f 100644 --- a/src/snek/static/njet.js +++ b/src/snek/static/njet.js @@ -353,6 +353,38 @@ class NjetDialog extends Component { } Njet.registerComponent('njet-dialog', NjetDialog); +class NjetWindow extends Component { + render() { + this.innerHTML = ''; + const { title, content, primaryButton, secondaryButton } = this.config; + this.classList.add('njet-dialog'); + this.style.position = 'fixed'; + this.style.top = '50%'; + this.style.left = '50%'; + this.style.transform = 'translate(-50%, -50%)'; + this.style.padding = '20px'; + this.style.border = '1px solid #444'; + this.style.backgroundColor = '#fff'; + this.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.2)'; + this.style.minWidth = '300px'; + if (title) { + const header = document.createElement('h2'); + header.textContent = title; + this.appendChild(header); + } + + + } + + show(){ + document.body.appendChild(this) + } +} +Njet.registerComponent('njet-window', NjetWindow); + + + + class NjetGrid extends Component { render() { this.classList.add('njet-grid'); @@ -467,7 +499,12 @@ njet.showDialog = function(args){ dialog.show() return dialog } +njet.showWindow = function(args) { + const w = new NjetWindow(args) + w.show() + return w +} window.njet = njet -export { Njet, NjetButton, NjetPanel, NjetDialog, NjetGrid, NjetComponent, njet}; +export { Njet, NjetButton, NjetPanel, NjetDialog, NjetGrid, NjetComponent, njet, NjetWindow }; diff --git a/src/snek/templates/app.html b/src/snek/templates/app.html index 272bef4..be50cdf 100644 --- a/src/snek/templates/app.html +++ b/src/snek/templates/app.html @@ -23,6 +23,7 @@ + diff --git a/src/snek/templates/sandbox.html b/src/snek/templates/sandbox.html index cdb7896..4949247 100644 --- a/src/snek/templates/sandbox.html +++ b/src/snek/templates/sandbox.html @@ -2,6 +2,14 @@