initial commit 🎭

This commit is contained in:
dab 2025-08-20 16:32:55 +00:00
commit 6a4a0a2c28
3 changed files with 171 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
node_modules
bun.lock

9
package.json Normal file
View file

@ -0,0 +1,9 @@
{
"name": "stage_d",
"version": "0.1.0",
"type": "module",
"main": "src/index.js",
"dependencies": {
"framerock": "git+https://git.daemons.my/dab/framerock.git#7c46e53d086af1b5093d308df5b52e88cb666e8d"
}
}

160
src/index.js Normal file
View file

@ -0,0 +1,160 @@
import { isAsyncFunction } from 'node:util/types'
import { async_run } from 'framerock'
function bytesToBase64 (bytes) {
return btoa(Array.from(bytes, function (byte) {return String.fromCodePoint(byte);}).join(''))
}
const async_stage = async function (role_defs, { framerock_config }) {
const jsbuild_app_frontend = async function () {
const page_params = bytesToBase64(new TextEncoder().encode(JSON.stringify({ role_keys: [ ...Object.keys(role_defs) ] })))
return `
function base64ToBytes (base64) {
return Uint8Array.from(atob(base64), function (m) {return m.codePointAt(0);})
}
const page_params = JSON.parse(new TextDecoder().decode(base64ToBytes("${page_params}")))
const result_handlers = new Map()
const setup_dom = function () {
document.head.insertAdjacentHTML('beforeend', '<style type="text/css">html, body{ height: 100%; width: 100%; margin: 0px; padding: 0px; overflow: hidden;}</style>')
const elem_root = document.createElement('div')
elem_root.style = 'height: 100%; width: 100%; font-family: sans-serif; position: relative; font-size: 16px;'
const elem_topbar = document.createElement('div')
elem_topbar.style = 'height: 100px; background-color: #333; color: #FFF; display: flex; align-items: center; overflow: hidden;'
const elem_title = document.createElement('div')
elem_title.style = 'font-size: 32px; font-weight: bold; user-select: none; margin-left: 32px;'
elem_title.textContent = 'stage_d'
const elem_view = document.createElement('div')
elem_view.style = 'position: absolute; top: 100px; left: 0px; right: 0px; bottom: 0px; background-color: #000; color: #FFF; overflow: auto;'
const elems_roledefs = page_params.role_keys.map((role_key) => {
const elem_role = document.createElement('div')
elem_role.style = 'border: 1px solid #FFF; padding: 10px; box-sizing: border-box;'
const elem_roletitle = document.createElement('div')
elem_roletitle.textContent = role_key
const elem_results = document.createElement('div')
elem_results.style = 'max-height: 200px; overflow: auto; font-family: monospace; font-size: 14px;'
const elem_btninitcue = document.createElement('button')
elem_btninitcue.textContent = 'initiate cue'
const func_click_rolecue = () => {
elem_btninitcue.disabled = true
initiate_cue_handle_result(role_key, { foo: 'bar' }, (result_obj) => {
elem_btninitcue.disabled = false
const elem_resultrow = document.createElement('div')
elem_resultrow.style = 'border: 1px solid blue; padding: 10px; box-sizing: border-box;'
elem_resultrow.textContent = JSON.stringify(result_obj)
elem_results.appendChild(elem_resultrow)
return
})
return
}
elem_btninitcue.addEventListener('click', func_click_rolecue)
elem_role.appendChild(elem_roletitle)
elem_role.appendChild(elem_btninitcue)
elem_role.appendChild(elem_results)
return elem_role
})
elem_topbar.appendChild(elem_title)
elems_roledefs.forEach((elem)=>elem_view.appendChild(elem))
elem_root.appendChild(elem_topbar)
elem_root.appendChild(elem_view)
document.body.appendChild(elem_root)
return
}
const initiate_cue_handle_result = function (role_key, payload, func_onresult) {
const uid = crypto.randomUUID()
result_handlers.set(uid, func_onresult)
FRAMEROCK_UTILS.transport_send_bytes(new TextEncoder().encode(JSON.stringify([ 'HANDLE_CUE_INIT', { uid, role_key, payload } ])))
return
}
const on_open = function () {
return
}
const on_message = function (event) {
const [ evt_type, evt_data ] = JSON.parse(new TextDecoder().decode(event.data))
if (evt_type === 'HANDLE_CUE_RESULT') {
const [ uid, result_obj ] = evt_data
const func_onresult = result_handlers.get(uid)
if (func_onresult === undefined) {
console.error(['Unexpected: func_onresult is undefined', { uid }])
}
else {
result_handlers.delete(uid)
func_onresult(result_obj)
}
}
return
}
setup_dom()
FRAMEROCK_UTILS.setup_transport({ on_open, on_message })
`.trim()
}
const handle_transport_bytes = function (utils, message) {
const [ evt_type, evt_data ] = JSON.parse(new TextDecoder().decode(message))
if (evt_type === 'HANDLE_CUE_INIT') {
const { uid, role_key, payload } = evt_data
const send_result = (result_obj) => {
utils.transport_send_bytes(new TextEncoder().encode(JSON.stringify([ 'HANDLE_CUE_RESULT', [ uid, result_obj ] ])))
return
}
const on_cue = role_defs[role_key]
if (on_cue === undefined) {
console.error(['Unexpected: on_cue is undefined', { role_key }])
}
else {
if (isAsyncFunction(on_cue)) {
on_cue(payload).then((result_complete) => {
send_result({ result_complete })
}).catch((err) => {
console.error(err)
send_result({ result_error: true })
})
}
else {
let result_complete
let did_error = false
try {
result_complete = on_cue(payload)
}
catch (err) {
did_error = true
console.error(err)
}
if (did_error === false) {
send_result({ result_complete })
}
else {
send_result({ result_error: true })
}
}
}
}
return
}
await async_run({ config: framerock_config, jsbuild_app_frontend, handle_transport_bytes })
return
}
export { async_stage }