From 013ea5e25e7af21e5fb7fc2cbd14c80b99e92fd5 Mon Sep 17 00:00:00 2001 From: catpea Date: Sun, 5 Jan 2025 15:23:54 -0500 Subject: [PATCH] :bug: Scene Travelsal Support --- browser.js | 235 ++++++++++++++-------- components/console/Console.js | 23 +-- components/pipe/Pipe.js | 53 ++++- components/scene/Scene.js | 5 + components/toolbar/Toolbar.js | 65 ++++-- components/window/Window.js | 8 + index.html | 1 + library/commander/commands/Command.js | 7 +- library/commander/commands/SceneSelect.js | 9 + library/transcend/transcend.js | 12 ++ src/UI.js | 57 +++++- 11 files changed, 336 insertions(+), 139 deletions(-) create mode 100644 library/commander/commands/SceneSelect.js create mode 100644 library/transcend/transcend.js diff --git a/browser.js b/browser.js index bb137c2..14efa7b 100644 --- a/browser.js +++ b/browser.js @@ -2,18 +2,17 @@ import Signal from 'signal'; import Branch from 'branch'; import Commander from 'commander'; +console.log('------------------------------SYSTEM START!------------------------------') + class Project extends Branch { commander; - - active = new Signal('main'); + activeScene = new Signal('main'); constructor(...a) { super(...a) this.commander = new Commander(this); } - get activeScene() { - return this.get( this.active.value); - } + } class Scene extends Branch { @@ -26,118 +25,196 @@ const project = new Project('project'); import UI from './src/UI.js'; const ui = new UI(project); -await ui.start(); + + const mainScene = new Scene('main'); mainScene.onStart = async () => console.log('ASYNC START GRRR') -mainScene.once('stop', ()=>console.log('Main scene Branch got stoppppp...')) -const uppercaseScene = new Scene('uppercase'); +mainScene.once('stop', () => { + console.log('Main scene Branch got stoppppp...') +}); + +const upperScene = new Scene('upper'); const teeScene = new Scene('tee'); project.create(mainScene); -project.create(uppercaseScene); +project.create(upperScene); project.create(teeScene); +{ + const mainInput = new Branch('mainInput', 'window'); + mainInput.dataset.set('title', 'Main Input'); + mainScene.create(mainInput) + const uppercaseInput = new Branch('uppercaseInput', 'window'); + uppercaseInput.dataset.set('title', 'Transducer'); + uppercaseInput.dataset.set('left', 300); + uppercaseInput.dataset.set('top', 300); + mainScene.create(uppercaseInput) + const uppercaseOutput = new Branch('uppercaseOutput', 'window'); + uppercaseOutput.dataset.set('title', 'Output Branch'); + uppercaseOutput.dataset.set('left', 600); + uppercaseOutput.dataset.set('top', 600); + mainScene.create(uppercaseOutput) + const pipe1 = new Branch('pipe1', 'pipe'); + pipe1.dataset.set('from', 'mainInput:out'); + pipe1.dataset.set('to', 'uppercaseInput:in'); + mainScene.create(pipe1); + const pipe2 = new Branch('pipe2', 'pipe'); + pipe2.dataset.set('from', 'uppercaseInput:out'); + pipe2.dataset.set('to', 'uppercaseOutput:in'); + mainScene.create(pipe2); +} -const mainInput = new Branch('mainInput', 'window'); -mainInput.dataset.set('title', 'Main Input'); -mainScene.create(mainInput) -const uppercaseInput = new Branch('uppercaseInput', 'window'); -uppercaseInput.dataset.set('title', 'Transducer'); -uppercaseInput.dataset.set('left', 300); -uppercaseInput.dataset.set('top', 300); -mainScene.create(uppercaseInput) +{ + const mainInput = new Branch('mainInput', 'window'); + mainInput.dataset.set('title', 'Main Input 1'); + mainInput.dataset.set('top', 100); -let isIdle = true; -let lastMoveTime = Date.now() -10000; -let idleTimeout = 5000; // Time in milliseconds to consider as idle (5 seconds in this case) + upperScene.create(mainInput) -function checkMouseIdle() { - const currentTime = Date.now(); - if (currentTime - lastMoveTime >= idleTimeout) { - console.log("Mouse is idle"); - isIdle = true; - } else { - console.log("Mouse is active"); - isIdle = false; - } -} + const mainInput2 = new Branch('mainInput2', 'window'); + mainInput2.dataset.set('title', 'Main Input 2'); + mainInput2.dataset.set('top', 300); -// Listen for mousemove event to track activity -document.addEventListener("mousemove", () => { - lastMoveTime = Date.now(); -}); + upperScene.create(mainInput2) -// Periodically check if the mouse is idle (you can adjust the interval as needed) -setInterval(checkMouseIdle, 100); // Check every 1 second + const uppercaseInput1 = new Branch('uppercaseInput', 'window'); + uppercaseInput1.dataset.set('title', 'Transducer2'); + uppercaseInput1.dataset.set('left', 300); + uppercaseInput1.dataset.set('top', 300); + upperScene.create(uppercaseInput1) -function getMinuteHandCoordinates(angleDegrees) { + const uppercaseOutput = new Branch('uppercaseOutput', 'window'); + uppercaseOutput.dataset.set('title', 'Output Branch2'); + uppercaseOutput.dataset.set('left', 600); + uppercaseOutput.dataset.set('top', 600); + upperScene.create(uppercaseOutput) - // Convert the angle to radians - const angleRadians = angleDegrees * (Math.PI / 180); + const pipe0 = new Branch('pipe0', 'pipe'); + pipe0.dataset.set('from', 'mainInput2:out'); + pipe0.dataset.set('to', 'uppercaseInput:in'); + upperScene.create(pipe0); - // Calculate the x and y coordinates - const x = Math.cos(angleRadians); - const y = Math.sin(angleRadians); + const pipe1 = new Branch('pipe1', 'pipe'); + pipe1.dataset.set('from', 'mainInput:out'); + pipe1.dataset.set('to', 'uppercaseInput:in'); + upperScene.create(pipe1); - return [ x, y ]; + const pipe2 = new Branch('pipe2', 'pipe'); + pipe2.dataset.set('from', 'uppercaseInput:out'); + pipe2.dataset.set('to', 'uppercaseOutput:in'); + upperScene.create(pipe2); } -const uppercaseOutput = new Branch('uppercaseOutput', 'window'); -uppercaseOutput.dataset.set('title', 'Output Branch'); -uppercaseOutput.dataset.set('left', 600); -uppercaseOutput.dataset.set('top', 600); -let angleDegrees = 0; -setInterval(() => { - if (!isIdle) { - return; - } - mainInput.dataset.set('left', 100 + (55 * getMinuteHandCoordinates(angleDegrees)[1])); - uppercaseInput.dataset.set('left', 333 + (11 * getMinuteHandCoordinates(angleDegrees)[1])); - uppercaseOutput.dataset.set('left', 666 + (22 * getMinuteHandCoordinates(angleDegrees)[0])); - mainInput.dataset.set('width', 320 + (55 * getMinuteHandCoordinates(angleDegrees)[0])); - uppercaseInput.dataset.set('width', 420 + (22 * getMinuteHandCoordinates(angleDegrees)[1])); - uppercaseOutput.dataset.set('width', 300 + (11 * getMinuteHandCoordinates(angleDegrees)[0])); - angleDegrees += 1; - if (angleDegrees > 360) angleDegrees = 0; -}, 1_000/60) - -mainScene.create(uppercaseOutput) - -const pipe1 = new Branch('pipe1', 'pipe'); -pipe1.dataset.set('from', 'mainInput:out'); -pipe1.dataset.set('to', 'uppercaseInput:in'); -mainScene.create(pipe1); - -const pipe2 = new Branch('pipe2', 'pipe'); -pipe2.dataset.set('from', 'uppercaseInput:out'); -pipe2.dataset.set('to', 'uppercaseOutput:in'); -mainScene.create(pipe2); - -setInterval(() => { - mainInput.dataset.set('date', (new Date()).toISOString()); -}, 1_000); + + + + + + + + + +// let isIdle = true; +// let lastMoveTime = Date.now() -10000; +// let idleTimeout = 5000; // Time in milliseconds to consider as idle (5 seconds in this case) + +// function checkMouseIdle() { +// const currentTime = Date.now(); +// if (currentTime - lastMoveTime >= idleTimeout) { +// console.log("Mouse is idle"); +// isIdle = true; +// } else { +// console.log("Mouse is active"); +// isIdle = false; +// } +// } + +// // Listen for mousemove event to track activity +// document.addEventListener("mousemove", () => { +// lastMoveTime = Date.now(); +// }); + +// // Periodically check if the mouse is idle (you can adjust the interval as needed) +// setInterval(checkMouseIdle, 100); // Check every 1 second + + + + +// function getMinuteHandCoordinates(angleDegrees) { + + +// // Convert the angle to radians +// const angleRadians = angleDegrees * (Math.PI / 180); + +// // Calculate the x and y coordinates +// const x = Math.cos(angleRadians); +// const y = Math.sin(angleRadians); + +// return [ x, y ]; +// } + + + +// let angleDegrees = 0; +// setInterval(() => { +// if (!isIdle) { + +// return; +// } +// mainInput.dataset.set('left', 100 + (55 * getMinuteHandCoordinates(angleDegrees)[1])); +// uppercaseInput.dataset.set('left', 333 + (11 * getMinuteHandCoordinates(angleDegrees)[1])); +// uppercaseOutput.dataset.set('left', 666 + (22 * getMinuteHandCoordinates(angleDegrees)[0])); +// mainInput.dataset.set('width', 320 + (55 * getMinuteHandCoordinates(angleDegrees)[0])); +// uppercaseInput.dataset.set('width', 420 + (22 * getMinuteHandCoordinates(angleDegrees)[1])); +// uppercaseOutput.dataset.set('width', 300 + (11 * getMinuteHandCoordinates(angleDegrees)[0])); +// angleDegrees += 1; +// if (angleDegrees > 360) angleDegrees = 0; +// }, 1_000/60) + + + + + + + + + + + + + + + + + +// setInterval(() => { +// mainInput.dataset.set('date', (new Date()).toISOString()); +// }, 1_000); await project.load(); await project.start(); +await ui.start(); // WARN: must come after the tree has fully loaded, otherwise the watcher will begin adding nodes, that are yet to be loaded. + console.log(`Startup at ${new Date().toISOString()}`); window.addEventListener('beforeunload', function(event) { - ui.stop(); - project.stop(); + console.log('beforeunload was triggered!') + // ui.stop(); + // project.stop(); }); diff --git a/components/console/Console.js b/components/console/Console.js index 32c6feb..2b81e14 100644 --- a/components/console/Console.js +++ b/components/console/Console.js @@ -2,6 +2,7 @@ import Signal from 'signal'; import Forms from 'forms'; import lol from 'lol'; +import transcend from 'transcend'; export default class Console extends HTMLElement { @@ -42,7 +43,7 @@ export default class Console extends HTMLElement { connectedCallback() { // const application = this.parentNode.parentNode.host.closest(`x-application`); - const application = this.transcend(`x-application`); + const application = transcend(this, `x-application`); if(!application) throw new Error('Unable to locate applicaion!') @@ -68,6 +69,8 @@ export default class Console extends HTMLElement { publishExecutedCommand(executedCommandEvent){ + const application = transcend(this, `x-application`); + if(!application) throw new Error('Unable to locate applicaion!') const forms = new Forms({gc: this.gc}); @@ -102,7 +105,7 @@ export default class Console extends HTMLElement { commandForm.appendChild(lol.button({class:'btn btn-outline-secondary btn-sm float-end'}, 'Adjust')) - const progressBar = lol.div({ class: 'progress-bar', style: {width: '100%'} },) + const progressBar = lol.div({ class: 'progress-bar opacity-25', style: {width: '100%'} },) let progressBarWidth = 100; const progressBarWidthIntervalId = setInterval(() => { progressBarWidth = progressBarWidth - 1; @@ -118,7 +121,7 @@ export default class Console extends HTMLElement { progressBar.style.display = 'none'; }); - commandContainer.appendChild(lol.div({ class: 'progress position-absolute top-100 start-50 translate-middle', style: {marginTop: '-1px', height:'1px', width: '98%', background:'transparent'} }, progressBar) ) + commandContainer.appendChild(lol.div({ class: 'progress position-absolute top-100 start-50 translate-middle', style: { marginTop: '-6px', height:'1px', width: '98%', background:'transparent'} }, progressBar) ) const consoleContainer = this.shadowRoot.querySelector('.console'); consoleContainer.insertBefore(commandContainer, consoleContainer.firstChild); @@ -133,19 +136,5 @@ export default class Console extends HTMLElement { - transcend(selector, element = this) { - // Check if the element exists and is not the document or window - if (element && element !== document && element !== window) { - // Try to find the closest element matching the selector - const closestElement = element.closest(selector); - - if (closestElement) { - return closestElement; - } - } - - // If not found, traverse into the shadow DOM host and search there - return this.transcend(selector, element.getRootNode().host); - } } diff --git a/components/pipe/Pipe.js b/components/pipe/Pipe.js index ad62a7c..4c681f7 100644 --- a/components/pipe/Pipe.js +++ b/components/pipe/Pipe.js @@ -19,15 +19,22 @@ export default class Pipe extends HTMLElement { this.dataset2 = new Dataset(); this.status = new Signal('loading'); this.status.subscribe(v => console.log('PIPE STATUS: ', v) ); + this.status.subscribe(v => { + if (v == 'unloaded') { + console.log('Removing Pipe Component', this); + this.remove(); + } + }); } + connectedCallback() { const scene = this.closest(`x-scene`); this.observer = new MutationObserver(this.#handleAttributeMutations.bind(this)); this.observer.observe(this, { attributes: true }); - this.gc = ()=> observer.disconnect(); + this.gc = ()=> this.observer.disconnect(); // SEED DATASET2 for (const {name, value} of this.attributes) { @@ -44,7 +51,16 @@ export default class Pipe extends HTMLElement { const dependencies = new Signal(); dependencies.addDependency(fromDecoder); dependencies.addDependency(toDecoder); - this.gc = dependencies.subscribe((_, a, b) => { if (a === 'ready' && a === b) this.status.value = 'ready' }); + this.gc = dependencies.subscribe((_, a, b) => { + console.log('STATUS', a,b) + if (a === 'ready' && a === b) { + this.status.value = 'ready' + + }else if(a === 'unloaded' && a === b){ + this.status.value = 'unloaded' + } + + }); // CREATE AND SUBSCRIBE LINES let actualStroke = this.#strokeWidth; @@ -62,9 +78,17 @@ export default class Pipe extends HTMLElement { } // WHENEVER FROM OR TO WINDOW CHANGES SIZE, RECALCULATE LINE POSISTION + this.gc = this.status.subscribe(v => { if (v === 'ready') { - // NOTE: Not interested in window size + + scene.getWindow(this.dataset2.get('from').value) + scene.getWindow(this.dataset2.get('to').value) + const dependencies = new Signal(); + + dependencies.addDependency(fromDecoder); + dependencies.addDependency(toDecoder); + dependencies.addDependency(scene.getWindow(this.dataset2.get('from').value).sizeSignal); dependencies.addDependency(scene.getWindow(this.dataset2.get('to').value).sizeSignal); @@ -73,18 +97,27 @@ export default class Pipe extends HTMLElement { dependencies.addDependency(scene.getWindow(this.dataset2.get('to').value).dataset2.get('left')); dependencies.addDependency(scene.getWindow(this.dataset2.get('to').value).dataset2.get('top')); - this.gc = dependencies.subscribe(() => { - const [x1, y1] = scene.calculateCentralCoordinates(scene.getDecal(this.dataset2.get('from').value)); - const [x2, y2] = scene.calculateCentralCoordinates(scene.getDecal(this.dataset2.get('to').value)); - this.#x1.value = x1; - this.#y1.value = y1; - this.#x2.value = x2; - this.#y2.value = y2; + this.gc = dependencies.subscribe((_, a, b) => { + if (a === 'ready' && a === b) { + const [x1, y1] = scene.calculateCentralCoordinates(scene.getDecal(this.dataset2.get('from').value)); + const [x2, y2] = scene.calculateCentralCoordinates(scene.getDecal(this.dataset2.get('to').value)); + this.#x1.value = x1; + this.#y1.value = y1; + this.#x2.value = x2; + this.#y2.value = y2; + } else { + // component exited + } + }); }}); } + disconnectedCallback() { + this.collectGarbage(); + } + #handleAttributeMutations(mutationsList) { for (let mutation of mutationsList) { if (mutation.type === 'attributes' && mutation.attributeName.startsWith('data-')) { diff --git a/components/scene/Scene.js b/components/scene/Scene.js index be8f0e8..ad989d2 100644 --- a/components/scene/Scene.js +++ b/components/scene/Scene.js @@ -99,6 +99,11 @@ export default class Scene extends HTMLElement { getDecal(colonAddress) { const [elementId, portId] = colonAddress.split(/\W/, 2); const element = this.getElementById(elementId); + if (!element) { + + throw new Error(`Element ${elementId} not found`) + } + const port = element.getPortElement(portId); const decal = port.getDecal(); return decal; diff --git a/components/toolbar/Toolbar.js b/components/toolbar/Toolbar.js index 099361e..fb1485d 100644 --- a/components/toolbar/Toolbar.js +++ b/components/toolbar/Toolbar.js @@ -1,5 +1,6 @@ import Signal from 'signal'; import lol from 'lol'; +import transcend from 'transcend'; export default class Console extends HTMLElement { @@ -20,38 +21,60 @@ export default class Console extends HTMLElement { const shadow = this.attachShadow({ mode: 'open' }); shadow.adoptedStyleSheets = [...document.adoptedStyleSheets, localCss]; - const container = lol.div({ class: 'toolbar' }); - container.innerHTML = ` -