(text "{% extends \"root.html\" %} {% block head %}") (title (text "{{ config.name }}")) (text "{% endblock %} {% block body %}") (div ("id" "panel") ("class" "flex flex-row gap-2") (a ("class" "button camo") ("href" "/") (icon (text "house"))) (button ("class" "lowered") ("onclick" "back()") (icon (text "arrow-left"))) (button ("class" "lowered") ("onclick" "forward()") (icon (text "arrow-right"))) (button ("class" "lowered") ("onclick" "reload()") (icon (text "rotate-cw"))) (form ("class" "w-full flex gap-1 flex-row") ("onsubmit" "event.preventDefault(); littleweb_navigate(event.target.uri.getAttribute('true_value'))") (input ("type" "uri") ("class" "w-full") ("true_value" "") ("name" "uri") ("id" "uri")) (button ("class" "lowered small square") (icon (text "arrow-right")))) (text "{% if user -%}") (div ("class" "dropdown") (button ("class" "flex-row camo") ("onclick" "trigger('atto::hooks::dropdown', [event])") ("exclude" "dropdown") ("style" "gap: var(--pad-1) !important") (text "{{ components::avatar(username=user.username, size=\"24px\") }}") (icon_class (text "chevron-down") (text "dropdown_arrow"))) (text "{{ components::user_menu() }}")) (text "{%- endif %}")) (iframe ("id" "browser_iframe") ("frameborder" "0") ("src" "{% if path -%} {{ config.lw_host }}/api/v1/net/{{ path }}?s={{ session }} {%- endif %}")) (style ("data-turbo-temporary" "true") (text ":root { --panel-height: 45px; } html, body { padding: 0; margin: 0; overflow: hidden; } #panel { width: 100dvw; height: var(--panel-height); padding: var(--pad-2); } #panel input { border: none; background: var(--color-lowered); transition: background 0.15s; } #panel input:focus { background: var(--color-super-lowered); } @media screen and (max-width: 900px) { #panel input:focus { position: fixed; width: calc(100dvw - (62px + var(--pad-2) * 2)) !important; left: var(--pad-2); z-index: 2; } } #panel button:not(.inner *), #panel a.button:not(.inner *), #panel input { --h: 28.2px; height: var(--h); min-height: var(--h); max-height: var(--h); font-size: 16px; } #panel button:not(.inner *), #panel a.button:not(.inner *) { padding: var(--pad-1) var(--pad-2); } iframe { width: 100dvw; height: calc(100dvh - var(--panel-height)); }")) (script (text "globalThis.SECRET_SESSION = \"{{ session }}\"; function littleweb_navigate(uri) { if (!uri.includes(\".html\")) { uri = `${uri}/index.html`; } // ... console.log(\"navigate\", uri); document.getElementById(\"browser_iframe\").src = `{{ config.lw_host|safe }}/api/v1/net/${uri}?s={{ session }}`; if (!uri.includes(\"atto://\")) { document.getElementById(\"uri\").setAttribute(\"true_value\", `atto://${uri}`); } else { document.getElementById(\"uri\").setAttribute(\"true_value\", uri); } document.getElementById(\"uri\").value = uri.replace(\"atto://\", \"\").split(\"/\")[0]; } document.getElementById(\"browser_iframe\").addEventListener(\"load\", (e) => { console.log(\"web content loaded\"); }); window.addEventListener(\"message\", (e) => { if (typeof e.data !== \"string\") { console.log(\"refuse message (bad type)\"); return; } const data = JSON.parse(e.data); if (!data.t) { console.log(\"refuse message (not for tetratto)\"); return; } console.log(\"received message\"); if (data.event === \"change_url\") { const uri = new URL(data.target).pathname.slice(\"/api/v1/net/\".length); window.history.pushState(null, null, `/net/${uri.replace(\"atto://\", \"\")}`); if (!uri.includes(\"atto://\")) { document.getElementById(\"uri\").setAttribute(\"true_value\", `atto://${uri}`); } else { document.getElementById(\"uri\").setAttribute(\"true_value\", uri); } document.getElementById(\"uri\").value = uri.replace(\"atto://\", \"\").split(\"/\")[0]; } }); function back() { post_message({ t: true, event: \"back\" }); } function forward() { post_message({ t: true, event: \"forward\" }); } function reload() { post_message({ t: true, event: \"reload\" }); } function post_message(data) { const origin = new URL(document.getElementById(\"browser_iframe\").src).origin; document.getElementById(\"browser_iframe\").contentWindow.postMessage(JSON.stringify(data), origin); } // handle dropdowns window.addEventListener(\"blur\", () => { trigger(\"atto::hooks::dropdown.close\"); }); // url bar focus document.getElementById(\"uri\").addEventListener(\"input\", (e) => { e.target.setAttribute(\"true_value\", e.target.value); }); let is_focused = false; document.getElementById(\"uri\").addEventListener(\"mouseenter\", (e) => { e.target.value = e.target.getAttribute(\"true_value\").replace(\"/index.html\", \"\"); }); document.getElementById(\"uri\").addEventListener(\"mouseleave\", (e) => { if (is_focused) { return; } e.target.value = e.target.getAttribute(\"true_value\").replace(\"atto://\", \"\").split(\"/\")[0]; }); document.getElementById(\"uri\").addEventListener(\"focus\", (e) => { e.target.value = e.target.getAttribute(\"true_value\").replace(\"/index.html\", \"\"); is_focused = true; }); document.getElementById(\"uri\").addEventListener(\"blur\", (e) => { e.target.value = e.target.getAttribute(\"true_value\").replace(\"atto://\", \"\").split(\"/\")[0]; is_focused = false; }); // navigate if ({{ path|length }} > 0) { littleweb_navigate(\"{{ path|safe }}\"); }")) (text "{% endblock %}")