@aigne/afs-ui 1.11.0-beta.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/LICENSE.md +26 -0
  2. package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +10 -0
  4. package/dist/aup-protocol.cjs +235 -0
  5. package/dist/aup-protocol.d.cts +78 -0
  6. package/dist/aup-protocol.d.cts.map +1 -0
  7. package/dist/aup-protocol.d.mts +78 -0
  8. package/dist/aup-protocol.d.mts.map +1 -0
  9. package/dist/aup-protocol.mjs +235 -0
  10. package/dist/aup-protocol.mjs.map +1 -0
  11. package/dist/aup-registry.cjs +2489 -0
  12. package/dist/aup-registry.mjs +2487 -0
  13. package/dist/aup-registry.mjs.map +1 -0
  14. package/dist/aup-spec.cjs +1467 -0
  15. package/dist/aup-spec.mjs +1466 -0
  16. package/dist/aup-spec.mjs.map +1 -0
  17. package/dist/aup-types.cjs +165 -0
  18. package/dist/aup-types.d.cts +157 -0
  19. package/dist/aup-types.d.cts.map +1 -0
  20. package/dist/aup-types.d.mts +157 -0
  21. package/dist/aup-types.d.mts.map +1 -0
  22. package/dist/aup-types.mjs +157 -0
  23. package/dist/aup-types.mjs.map +1 -0
  24. package/dist/backend.cjs +14 -0
  25. package/dist/backend.d.cts +104 -0
  26. package/dist/backend.d.cts.map +1 -0
  27. package/dist/backend.d.mts +104 -0
  28. package/dist/backend.d.mts.map +1 -0
  29. package/dist/backend.mjs +13 -0
  30. package/dist/backend.mjs.map +1 -0
  31. package/dist/degradation.cjs +85 -0
  32. package/dist/degradation.d.cts +17 -0
  33. package/dist/degradation.d.cts.map +1 -0
  34. package/dist/degradation.d.mts +17 -0
  35. package/dist/degradation.d.mts.map +1 -0
  36. package/dist/degradation.mjs +84 -0
  37. package/dist/degradation.mjs.map +1 -0
  38. package/dist/index.cjs +36 -0
  39. package/dist/index.d.cts +12 -0
  40. package/dist/index.d.mts +12 -0
  41. package/dist/index.mjs +13 -0
  42. package/dist/runtime.cjs +117 -0
  43. package/dist/runtime.d.cts +59 -0
  44. package/dist/runtime.d.cts.map +1 -0
  45. package/dist/runtime.d.mts +59 -0
  46. package/dist/runtime.d.mts.map +1 -0
  47. package/dist/runtime.mjs +118 -0
  48. package/dist/runtime.mjs.map +1 -0
  49. package/dist/session.cjs +159 -0
  50. package/dist/session.d.cts +80 -0
  51. package/dist/session.d.cts.map +1 -0
  52. package/dist/session.d.mts +80 -0
  53. package/dist/session.d.mts.map +1 -0
  54. package/dist/session.mjs +159 -0
  55. package/dist/session.mjs.map +1 -0
  56. package/dist/snapshot.cjs +162 -0
  57. package/dist/snapshot.mjs +163 -0
  58. package/dist/snapshot.mjs.map +1 -0
  59. package/dist/term-page.cjs +264 -0
  60. package/dist/term-page.mjs +264 -0
  61. package/dist/term-page.mjs.map +1 -0
  62. package/dist/term.cjs +295 -0
  63. package/dist/term.d.cts +84 -0
  64. package/dist/term.d.cts.map +1 -0
  65. package/dist/term.d.mts +84 -0
  66. package/dist/term.d.mts.map +1 -0
  67. package/dist/term.mjs +296 -0
  68. package/dist/term.mjs.map +1 -0
  69. package/dist/tty.cjs +136 -0
  70. package/dist/tty.d.cts +53 -0
  71. package/dist/tty.d.cts.map +1 -0
  72. package/dist/tty.d.mts +53 -0
  73. package/dist/tty.d.mts.map +1 -0
  74. package/dist/tty.mjs +135 -0
  75. package/dist/tty.mjs.map +1 -0
  76. package/dist/ui-provider.cjs +4615 -0
  77. package/dist/ui-provider.d.cts +307 -0
  78. package/dist/ui-provider.d.cts.map +1 -0
  79. package/dist/ui-provider.d.mts +307 -0
  80. package/dist/ui-provider.d.mts.map +1 -0
  81. package/dist/ui-provider.mjs +4616 -0
  82. package/dist/ui-provider.mjs.map +1 -0
  83. package/dist/web-page/core.cjs +1388 -0
  84. package/dist/web-page/core.mjs +1387 -0
  85. package/dist/web-page/core.mjs.map +1 -0
  86. package/dist/web-page/css.cjs +1699 -0
  87. package/dist/web-page/css.mjs +1698 -0
  88. package/dist/web-page/css.mjs.map +1 -0
  89. package/dist/web-page/icons.cjs +248 -0
  90. package/dist/web-page/icons.mjs +248 -0
  91. package/dist/web-page/icons.mjs.map +1 -0
  92. package/dist/web-page/overlay-themes.cjs +514 -0
  93. package/dist/web-page/overlay-themes.mjs +513 -0
  94. package/dist/web-page/overlay-themes.mjs.map +1 -0
  95. package/dist/web-page/renderers/action.cjs +72 -0
  96. package/dist/web-page/renderers/action.mjs +72 -0
  97. package/dist/web-page/renderers/action.mjs.map +1 -0
  98. package/dist/web-page/renderers/broadcast.cjs +160 -0
  99. package/dist/web-page/renderers/broadcast.mjs +160 -0
  100. package/dist/web-page/renderers/broadcast.mjs.map +1 -0
  101. package/dist/web-page/renderers/calendar.cjs +137 -0
  102. package/dist/web-page/renderers/calendar.mjs +137 -0
  103. package/dist/web-page/renderers/calendar.mjs.map +1 -0
  104. package/dist/web-page/renderers/canvas.cjs +173 -0
  105. package/dist/web-page/renderers/canvas.mjs +173 -0
  106. package/dist/web-page/renderers/canvas.mjs.map +1 -0
  107. package/dist/web-page/renderers/cdn-loader.cjs +25 -0
  108. package/dist/web-page/renderers/cdn-loader.mjs +25 -0
  109. package/dist/web-page/renderers/cdn-loader.mjs.map +1 -0
  110. package/dist/web-page/renderers/chart.cjs +101 -0
  111. package/dist/web-page/renderers/chart.mjs +101 -0
  112. package/dist/web-page/renderers/chart.mjs.map +1 -0
  113. package/dist/web-page/renderers/deck.cjs +390 -0
  114. package/dist/web-page/renderers/deck.mjs +390 -0
  115. package/dist/web-page/renderers/deck.mjs.map +1 -0
  116. package/dist/web-page/renderers/device.cjs +1015 -0
  117. package/dist/web-page/renderers/device.mjs +1015 -0
  118. package/dist/web-page/renderers/device.mjs.map +1 -0
  119. package/dist/web-page/renderers/editor.cjs +127 -0
  120. package/dist/web-page/renderers/editor.mjs +127 -0
  121. package/dist/web-page/renderers/editor.mjs.map +1 -0
  122. package/dist/web-page/renderers/finance-chart.cjs +178 -0
  123. package/dist/web-page/renderers/finance-chart.mjs +178 -0
  124. package/dist/web-page/renderers/finance-chart.mjs.map +1 -0
  125. package/dist/web-page/renderers/frame.cjs +274 -0
  126. package/dist/web-page/renderers/frame.mjs +274 -0
  127. package/dist/web-page/renderers/frame.mjs.map +1 -0
  128. package/dist/web-page/renderers/globe.cjs +119 -0
  129. package/dist/web-page/renderers/globe.mjs +119 -0
  130. package/dist/web-page/renderers/globe.mjs.map +1 -0
  131. package/dist/web-page/renderers/input.cjs +137 -0
  132. package/dist/web-page/renderers/input.mjs +137 -0
  133. package/dist/web-page/renderers/input.mjs.map +1 -0
  134. package/dist/web-page/renderers/list.cjs +1243 -0
  135. package/dist/web-page/renderers/list.mjs +1243 -0
  136. package/dist/web-page/renderers/list.mjs.map +1 -0
  137. package/dist/web-page/renderers/map.cjs +126 -0
  138. package/dist/web-page/renderers/map.mjs +126 -0
  139. package/dist/web-page/renderers/map.mjs.map +1 -0
  140. package/dist/web-page/renderers/media.cjs +106 -0
  141. package/dist/web-page/renderers/media.mjs +106 -0
  142. package/dist/web-page/renderers/media.mjs.map +1 -0
  143. package/dist/web-page/renderers/moonphase.cjs +105 -0
  144. package/dist/web-page/renderers/moonphase.mjs +105 -0
  145. package/dist/web-page/renderers/moonphase.mjs.map +1 -0
  146. package/dist/web-page/renderers/natal-chart.cjs +222 -0
  147. package/dist/web-page/renderers/natal-chart.mjs +222 -0
  148. package/dist/web-page/renderers/natal-chart.mjs.map +1 -0
  149. package/dist/web-page/renderers/overlay.cjs +531 -0
  150. package/dist/web-page/renderers/overlay.mjs +531 -0
  151. package/dist/web-page/renderers/overlay.mjs.map +1 -0
  152. package/dist/web-page/renderers/table.cjs +74 -0
  153. package/dist/web-page/renderers/table.mjs +74 -0
  154. package/dist/web-page/renderers/table.mjs.map +1 -0
  155. package/dist/web-page/renderers/terminal.cjs +30 -0
  156. package/dist/web-page/renderers/terminal.mjs +30 -0
  157. package/dist/web-page/renderers/terminal.mjs.map +1 -0
  158. package/dist/web-page/renderers/text.cjs +109 -0
  159. package/dist/web-page/renderers/text.mjs +109 -0
  160. package/dist/web-page/renderers/text.mjs.map +1 -0
  161. package/dist/web-page/renderers/ticker.cjs +133 -0
  162. package/dist/web-page/renderers/ticker.mjs +133 -0
  163. package/dist/web-page/renderers/ticker.mjs.map +1 -0
  164. package/dist/web-page/renderers/time.cjs +69 -0
  165. package/dist/web-page/renderers/time.mjs +69 -0
  166. package/dist/web-page/renderers/time.mjs.map +1 -0
  167. package/dist/web-page/renderers/unknown.cjs +20 -0
  168. package/dist/web-page/renderers/unknown.mjs +20 -0
  169. package/dist/web-page/renderers/unknown.mjs.map +1 -0
  170. package/dist/web-page/renderers/view.cjs +161 -0
  171. package/dist/web-page/renderers/view.mjs +161 -0
  172. package/dist/web-page/renderers/view.mjs.map +1 -0
  173. package/dist/web-page/renderers/wm.cjs +669 -0
  174. package/dist/web-page/renderers/wm.mjs +669 -0
  175. package/dist/web-page/renderers/wm.mjs.map +1 -0
  176. package/dist/web-page/skeleton.cjs +103 -0
  177. package/dist/web-page/skeleton.mjs +103 -0
  178. package/dist/web-page/skeleton.mjs.map +1 -0
  179. package/dist/web-page.cjs +114 -0
  180. package/dist/web-page.d.cts +19 -0
  181. package/dist/web-page.d.cts.map +1 -0
  182. package/dist/web-page.d.mts +19 -0
  183. package/dist/web-page.d.mts.map +1 -0
  184. package/dist/web-page.mjs +115 -0
  185. package/dist/web-page.mjs.map +1 -0
  186. package/dist/web.cjs +827 -0
  187. package/dist/web.d.cts +144 -0
  188. package/dist/web.d.cts.map +1 -0
  189. package/dist/web.d.mts +144 -0
  190. package/dist/web.d.mts.map +1 -0
  191. package/dist/web.mjs +828 -0
  192. package/dist/web.mjs.map +1 -0
  193. package/dist/wm-state.cjs +172 -0
  194. package/dist/wm-state.mjs +171 -0
  195. package/dist/wm-state.mjs.map +1 -0
  196. package/package.json +59 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.mjs","names":[],"sources":["../../src/web-page/core.ts"],"sourcesContent":["export const CORE_HEAD_JS = `\n // ── XSS protection ──\n function _escapeHtml(str) {\n var div = document.createElement(\"div\");\n div.appendChild(document.createTextNode(str));\n return div.innerHTML;\n }\n\n function escapeAttr(str) {\n return str.replace(/&/g,\"&amp;\").replace(/\"/g,\"&quot;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\");\n }\n\n function sanitizeHtml(html) {\n if (typeof DOMPurify !== \"undefined\") return DOMPurify.sanitize(html);\n // Fallback: strip all tags when DOMPurify unavailable\n return _escapeHtml(html);\n }\n\n // ── i18n ──\n var LOCALES = {\n en: { title: \"AFS\", connected: \"Connected\", disconnected: \"Disconnected\",\n connecting: \"Connecting...\", send: \"Send\", backToChat: \"Back\",\n inputPlaceholder: \"Type a message... (Enter to send, Shift+Enter for newline)\",\n aupDisplay: \"AUP Display\" },\n zh: { title: \"AFS\", connected: \"\\\\u5df2\\\\u8fde\\\\u63a5\", disconnected: \"\\\\u5df2\\\\u65ad\\\\u5f00\",\n connecting: \"\\\\u8fde\\\\u63a5\\\\u4e2d...\", send: \"\\\\u53d1\\\\u9001\", backToChat: \"\\\\u8fd4\\\\u56de\",\n inputPlaceholder: \"\\\\u8f93\\\\u5165\\\\u6d88\\\\u606f... (Enter \\\\u53d1\\\\u9001, Shift+Enter \\\\u6362\\\\u884c)\",\n aupDisplay: \"AUP \\\\u663e\\\\u793a\" },\n ja: { title: \"AFS\", connected: \"\\\\u63a5\\\\u7d9a\\\\u6e08\\\\u307f\", disconnected: \"\\\\u5207\\\\u65ad\",\n connecting: \"\\\\u63a5\\\\u7d9a\\\\u4e2d...\", send: \"\\\\u9001\\\\u4fe1\", backToChat: \"\\\\u623b\\\\u308b\",\n inputPlaceholder: \"\\\\u30e1\\\\u30c3\\\\u30bb\\\\u30fc\\\\u30b8\\\\u3092\\\\u5165\\\\u529b... (Enter\\\\u3067\\\\u9001\\\\u4fe1)\",\n aupDisplay: \"AUP \\\\u8868\\\\u793a\" }\n };\n var currentLocale = \"en\";\n function t(key) { return (LOCALES[currentLocale] || LOCALES.en)[key] || LOCALES.en[key] || key; }\n function setLocale(loc) {\n if (LOCALES[loc]) { currentLocale = loc; }\n else { var base = loc.split(\"-\")[0]; currentLocale = LOCALES[base] ? base : \"en\"; }\n var h1 = document.querySelector(\"header h1\"); if (h1) h1.textContent = t(\"title\");\n var send = document.getElementById(\"btn-send\"); if (send) send.textContent = t(\"send\");\n var inp = document.getElementById(\"input\"); if (inp) inp.placeholder = t(\"inputPlaceholder\");\n var back1 = document.getElementById(\"back-to-chat\"); if (back1) back1.textContent = t(\"backToChat\");\n var back2 = document.getElementById(\"aup-back-to-chat\"); if (back2) back2.textContent = t(\"backToChat\");\n var sel = document.getElementById(\"locale-select\"); if (sel) sel.value = currentLocale;\n }\n (function initLocale() {\n var nav = navigator.language || \"en\"; setLocale(nav);\n var sel = document.getElementById(\"locale-select\");\n if (sel) sel.onchange = function() { setLocale(sel.value); };\n })();\n\n // ── Style Manager (two axes: style + mode) ──\n var currentStyle = \"midnight\";\n var currentModeChoice = \"auto\"; // \"auto\" | \"dark\" | \"light\"\n var _userSetStyle = false; // true once user manually picks a style\n var _userSetMode = false; // true once user manually picks a mode\n\n function resolveMode() {\n if (currentModeChoice === \"auto\") {\n return window.matchMedia(\"(prefers-color-scheme: light)\").matches ? \"light\" : \"dark\";\n }\n return currentModeChoice;\n }\n function applyStyleMode() {\n document.documentElement.setAttribute(\"data-theme\", currentStyle);\n document.documentElement.setAttribute(\"data-mode\", resolveMode());\n }\n function setStyle(name, fromUser) {\n if (name) currentStyle = name;\n if (fromUser) _userSetStyle = true;\n applyStyleMode();\n var sel = document.getElementById(\"style-select\");\n if (sel) sel.value = currentStyle;\n }\n function setMode(mode, fromUser) {\n currentModeChoice = mode || \"auto\";\n if (fromUser) _userSetMode = true;\n applyStyleMode();\n var sel = document.getElementById(\"mode-select\");\n if (sel) sel.value = currentModeChoice;\n }\n // Legacy compat: setTheme(\"glass\") from aup_render still works\n function setTheme(name) { setStyle(name); }\n\n (function initStyleMode() {\n var styleSel = document.getElementById(\"style-select\");\n var modeSel = document.getElementById(\"mode-select\");\n // Apply defaults immediately\n applyStyleMode();\n if (styleSel) styleSel.onchange = function() { setStyle(styleSel.value, true); };\n if (modeSel) modeSel.onchange = function() { setMode(modeSel.value, true); };\n // Listen for system mode changes\n window.matchMedia(\"(prefers-color-scheme: light)\").addEventListener(\"change\", function() {\n if (currentModeChoice === \"auto\") applyStyleMode();\n });\n })();\n\n // ── Markdown renderer ──\n if (typeof marked !== \"undefined\" && typeof markedHighlight !== \"undefined\" && typeof hljs !== \"undefined\") {\n marked.use(markedHighlight.markedHighlight({\n langPrefix: \"hljs language-\",\n highlight: function(code, lang) {\n if (lang && hljs.getLanguage(lang)) return hljs.highlight(code, { language: lang }).value;\n return hljs.highlightAuto(code).value;\n }\n }));\n marked.use({ breaks: true, gfm: true });\n }\n\n function renderMarkdown(raw) {\n if (typeof marked !== \"undefined\") {\n var html = marked.parse(raw);\n return typeof DOMPurify !== \"undefined\" ? DOMPurify.sanitize(html) : _escapeHtml(raw);\n }\n return _escapeHtml(raw);\n }\n\n // ── DOM refs (chat/session UI — not present in snapshot mode) ──\n if (typeof _SNAPSHOT_MODE !== \"undefined\" && _SNAPSHOT_MODE) {\n // Skip all chat/session/WebSocket initialization in snapshot mode.\n // Jump straight to AUP Runtime section below.\n } else {\n var messagesEl = document.getElementById(\"messages\");\n var inputEl = document.getElementById(\"input\");\n var btnSend = document.getElementById(\"btn-send\");\n var dotEl = document.getElementById(\"dot\");\n var statusEl = document.getElementById(\"status\");\n var promptArea = document.getElementById(\"prompt-area\");\n var promptMsg = document.getElementById(\"prompt-msg\");\n var promptOptions = document.getElementById(\"prompt-options\");\n var pageView = document.getElementById(\"page-view\");\n var pageContent = document.getElementById(\"page-content\");\n var pageTitle = document.getElementById(\"page-title\");\n var backBtn = document.getElementById(\"back-to-chat\");\n var inputBar = document.getElementById(\"input-bar\");\n var desktopSplash = document.getElementById(\"desktop-splash\");\n var splashDot = document.getElementById(\"splash-dot\");\n var splashStatus = document.getElementById(\"splash-status\");\n var sessionBadge = document.getElementById(\"session-badge\");\n var sessionDot = document.getElementById(\"session-dot\");\n var sessionIdEl = document.getElementById(\"session-id\");\n var chromeToolbar = document.getElementById(\"chrome-toolbar\");\n var ws = null;\n var currentPrompt = null;\n\n // ── Live channel detection ──\n var _liveChannelId = null;\n (function() {\n var m = location.pathname.match(/^\\\\/live\\\\/([^/]+)/);\n if (m) {\n try { _liveChannelId = decodeURIComponent(m[1]); } catch(_) {}\n }\n })();\n\n // ── Session stickiness: read ?sid= from URL ──\n var _urlSessionId = null;\n (function() {\n var params = new URLSearchParams(location.search);\n var sid = params.get(\"sid\");\n if (sid) _urlSessionId = sid;\n })();\n var _storedSessionId = null;\n var _storedSessionToken = null;\n var _sessionStoreKey = \"afs:web:session:\" + location.pathname;\n (function() {\n if (_liveChannelId) return;\n try {\n var raw = sessionStorage.getItem(_sessionStoreKey);\n if (!raw) return;\n var parsed = JSON.parse(raw);\n if (\n parsed &&\n typeof parsed.sid === \"string\" &&\n parsed.sid &&\n typeof parsed.st === \"string\" &&\n parsed.st\n ) {\n _storedSessionId = parsed.sid;\n _storedSessionToken = parsed.st;\n }\n } catch (_) {}\n })();\n\n // ── Copy button ──\n function createCopyBtn(getText) {\n var btn = document.createElement(\"button\");\n btn.className = \"copy-btn\";\n btn.textContent = \"\\\\u2398\";\n btn.title = \"Copy\";\n btn.onclick = function(e) {\n e.stopPropagation();\n navigator.clipboard.writeText(getText()).then(function() {\n btn.classList.add(\"copied\");\n btn.textContent = \"\\\\u2713\";\n setTimeout(function() { btn.classList.remove(\"copied\"); btn.textContent = \"\\\\u2398\"; }, 1500);\n });\n };\n return btn;\n }\n\n // ── Component rendering ──\n function renderComponent(content, component, props) {\n props = props || {};\n switch (component) {\n case \"code-block\": {\n var lang = props.language || \"\";\n var highlighted = content;\n if (typeof hljs !== \"undefined\" && lang && hljs.getLanguage(lang)) {\n highlighted = hljs.highlight(content, { language: lang }).value;\n } else if (typeof hljs !== \"undefined\") {\n highlighted = hljs.highlightAuto(content).value;\n }\n return '<pre><code class=\"hljs' + (lang ? \" language-\" + lang : \"\") + '\">' + highlighted + \"</code></pre>\";\n }\n case \"table\": {\n var headers = props.headers || [];\n var rows = props.rows || [];\n var html = '<table class=\"component-table\"><thead><tr>';\n headers.forEach(function(h) { html += \"<th>\" + _escapeHtml(String(h)) + \"</th>\"; });\n html += \"</tr></thead><tbody>\";\n rows.forEach(function(row) {\n html += \"<tr>\";\n (row || []).forEach(function(cell) { html += \"<td>\" + _escapeHtml(String(cell)) + \"</td>\"; });\n html += \"</tr>\";\n });\n html += \"</tbody></table>\";\n return html;\n }\n case \"image\": {\n var src = escapeAttr(props.src || \"\");\n var alt = escapeAttr(props.alt || \"\");\n return '<img class=\"component-image\" src=\"' + src + '\" alt=\"' + alt + '\">';\n }\n default:\n return \"<pre>\" + _escapeHtml(content) + \"</pre>\";\n }\n }\n\n // ── Message rendering ──\n function addMsg(content, cls, format, meta) {\n var el = document.createElement(\"div\");\n el.className = \"msg \" + cls;\n format = format || \"text\";\n meta = meta || {};\n\n switch (format) {\n case \"html\":\n el.innerHTML = sanitizeHtml(content);\n break;\n case \"markdown\":\n el.innerHTML = renderMarkdown(content);\n break;\n case \"component\":\n el.innerHTML = renderComponent(content, meta.component, meta.componentProps);\n break;\n default:\n el.textContent = content;\n break;\n }\n\n if (cls === \"user\" || cls === \"assistant\") {\n el.dataset.rawText = content;\n el.appendChild(createCopyBtn(function() { return el.dataset.rawText; }));\n }\n\n messagesEl.appendChild(el);\n messagesEl.scrollTop = messagesEl.scrollHeight;\n return el;\n }\n\n // ── AFS Client Proxy ──\n var _afsReqId = 0;\n var _afsPending = {}; // reqId → { resolve, reject, timer }\n var _afsSubs = {}; // subId → callback\n var _afsSubId = 0;\n var _afsSessionId = null;\n var _afsSessionToken = null;\n var _aupTreeVersion = 0;\n // ── Kind Template Registry ──\n var _aupKindTemplates = {};\n window.aup = window.aup || {};\n window.aup.registerKindTemplate = function(kind, templateNode) {\n _aupKindTemplates[kind] = templateNode;\n };\n window.aup.getKindTemplate = function(kind) {\n if (!kind) return null;\n if (_aupKindTemplates[kind]) return _aupKindTemplates[kind];\n var colonIdx = kind.indexOf(\":\");\n if (colonIdx >= 0) {\n var prefix = kind.substring(0, colonIdx) + \":*\";\n if (_aupKindTemplates[prefix]) return _aupKindTemplates[prefix];\n }\n return null;\n };\n\n if (_storedSessionId) _afsSessionId = _storedSessionId;\n if (_storedSessionToken) _afsSessionToken = _storedSessionToken;\n\n function _persistSessionAuth() {\n if (_liveChannelId) return;\n try {\n if (_afsSessionId && _afsSessionToken) {\n sessionStorage.setItem(\n _sessionStoreKey,\n JSON.stringify({ sid: _afsSessionId, st: _afsSessionToken }),\n );\n } else {\n sessionStorage.removeItem(_sessionStoreKey);\n }\n } catch (_) {}\n }\n\n function _afsSend(msg) {\n if (ws && ws.readyState === 1) ws.send(JSON.stringify(msg));\n }\n\n function _afsRequest(type, params) {\n return new Promise(function(resolve, reject) {\n var reqId = \"r\" + (++_afsReqId);\n var timer = setTimeout(function() {\n delete _afsPending[reqId];\n reject(new Error(\"AFS request timeout: \" + type));\n }, 30000);\n _afsPending[reqId] = { resolve: resolve, reject: reject, timer: timer };\n var msg = { type: type, reqId: reqId };\n for (var k in params) { if (params.hasOwnProperty(k)) msg[k] = params[k]; }\n _afsSend(msg);\n });\n }\n\n function _handleAfsResult(msg) {\n var p = _afsPending[msg.reqId];\n if (p) {\n clearTimeout(p.timer);\n delete _afsPending[msg.reqId];\n p.resolve(msg.data);\n }\n }\n\n function _handleAfsError(msg) {\n var p = _afsPending[msg.reqId];\n if (p) {\n clearTimeout(p.timer);\n delete _afsPending[msg.reqId];\n p.reject(new Error(msg.error || \"AFS error\"));\n }\n }\n\n function _handleAfsEvent(msg) {\n var cb = _afsSubs[msg.subId];\n if (cb) cb(msg.event);\n }\n\n // Global AFS object — browser becomes an AFS peer\n window.afs = {\n read: function(path) { return _afsRequest(\"afs_read\", { path: path }); },\n list: function(path, options) {\n var params = { path: path };\n if (options) params.options = options;\n return _afsRequest(\"afs_list\", params);\n },\n write: function(path, content, meta) {\n var params = { path: path };\n if (content !== undefined) params.content = content;\n if (meta !== undefined) params.meta = meta;\n return _afsRequest(\"afs_write\", params);\n },\n exec: function(path, args) { return _afsRequest(\"afs_exec\", { path: path, args: args || {} }); },\n stat: function(path) { return _afsRequest(\"afs_stat\", { path: path }); },\n subscribe: function(filter, callback) {\n var subId = \"s\" + (++_afsSubId);\n _afsSubs[subId] = callback;\n _afsRequest(\"afs_subscribe\", { subId: subId, filter: filter });\n return function() {\n delete _afsSubs[subId];\n _afsSend({ type: \"afs_unsubscribe\", reqId: \"r\" + (++_afsReqId), subId: subId });\n };\n },\n get sessionId() { return _afsSessionId; },\n get channelId() { return _liveChannelId; }\n };\n\n // ── src binding helper: loading/error lifecycle ──\n // Usage: _aupSrcBind(el, node.src, function(data) { ... })\n // Inserts a loading skeleton, fetches data, removes skeleton on success, shows error on fail.\n // Returns unsubscribe function (or null).\n function _aupSrcBind(el, srcPath, onData) {\n if (!srcPath || !window.afs) return null;\n // Loading skeleton\n var skel = document.createElement(\"div\");\n skel.className = \"aup-src-loading\";\n skel.innerHTML = '<div class=\"aup-src-loading-bar\"></div>';\n el.insertBefore(skel, el.firstChild);\n var removed = false;\n function removeSkel() { if (!removed && skel.parentNode) { skel.parentNode.removeChild(skel); removed = true; } }\n function showError(msg) {\n removeSkel();\n var existing = el.querySelector(\".aup-src-error\");\n if (existing) existing.parentNode.removeChild(existing);\n var errEl = document.createElement(\"div\");\n errEl.className = \"aup-src-error\";\n errEl.textContent = msg || \"Failed to load data\";\n el.insertBefore(errEl, el.firstChild);\n }\n // Initial read\n window.afs.read(srcPath).then(function(result) {\n removeSkel();\n var existing = el.querySelector(\".aup-src-error\");\n if (existing) existing.parentNode.removeChild(existing);\n onData(result);\n }).catch(function(err) {\n showError(err.message || \"Load failed\");\n });\n // Subscribe for live updates\n var unsub = window.afs.subscribe({ type: \"afs:write\", path: srcPath }, function(event) {\n var existing = el.querySelector(\".aup-src-error\");\n if (existing) existing.parentNode.removeChild(existing);\n if (event && event.data) onData(event.data);\n });\n return unsub;\n }\n\n // ── Connection ──\n var _connState = \"disconnected\";\n function setConnected(ok) {\n _connState = ok ? \"connected\" : \"disconnected\";\n dotEl.className = \"dot \" + (ok ? \"on\" : \"off\");\n statusEl.textContent = ok ? t(\"connected\") : t(\"disconnected\");\n inputEl.disabled = !ok;\n btnSend.disabled = !ok;\n if (splashDot) splashDot.className = \"dot \" + (ok ? \"on\" : \"off\");\n if (splashStatus) splashStatus.textContent = ok ? \"connected — waiting for content\" : \"connecting...\";\n if (sessionDot) sessionDot.className = _connState;\n _updateBadgeTooltip();\n }\n function _updateBadgeTooltip() {\n if (!sessionBadge) return;\n sessionBadge.title = \"AFS v\" + (typeof _AFS_VERSION !== \"undefined\" ? _AFS_VERSION : \"?\") + \"\\\\n\" + _connState;\n }\n\n function connect() {\n _connState = \"connecting\";\n if (sessionDot) sessionDot.className = \"connecting\";\n _updateBadgeTooltip();\n var proto = location.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n ws = new WebSocket(proto + \"//\" + location.host);\n\n ws.onopen = function() {\n setConnected(true);\n if (_liveChannelId) {\n ws.send(JSON.stringify({ type: \"join_channel\", channelId: _liveChannelId }));\n } else {\n var handshake = { type: \"join_session\" };\n if (_afsSessionId || _urlSessionId) handshake.sessionId = _afsSessionId || _urlSessionId;\n if (_afsSessionToken) handshake.sessionToken = _afsSessionToken;\n if (_aupTreeVersion > 0) handshake.treeVersion = _aupTreeVersion;\n ws.send(JSON.stringify(handshake));\n // Send pending deep link request if any\n if (window._aupPendingDeepLink) {\n var dl = window._aupPendingDeepLink;\n delete window._aupPendingDeepLink;\n if (dl.type === \"page\") {\n ws.send(JSON.stringify({ type: \"navigate_request\", pageId: dl.pageId }));\n }\n }\n inputEl.focus();\n }\n };\n ws.onclose = function() { setConnected(false); ws = null; setTimeout(connect, 2000); };\n ws.onerror = function() {};\n\n ws.onmessage = function(e) {\n var msg;\n try { msg = JSON.parse(e.data); } catch(_ex) { return; }\n handleMessage(msg);\n };\n }\n\n function handleMessage(msg) {\n switch (msg.type) {\n case \"session\":\n _afsSessionId = msg.sessionId;\n _afsSessionToken = msg.sessionToken || null;\n _persistSessionAuth();\n if (sessionIdEl && msg.sessionId) sessionIdEl.textContent = msg.sessionId;\n // Persist session ID in URL for stickiness across refreshes\n if (msg.sessionId && !_liveChannelId) {\n var u = new URL(location.href);\n if (u.searchParams.get(\"sid\") !== msg.sessionId) {\n u.searchParams.set(\"sid\", msg.sessionId);\n history.replaceState(null, \"\", u.toString());\n }\n }\n break;\n case \"channel\":\n if (sessionIdEl && msg.channelId) sessionIdEl.textContent = \"live: \" + msg.channelId;\n if (splashStatus) splashStatus.textContent = \"live channel — waiting for content\";\n // Hide input bar for live viewers (read-only)\n if (inputBar) inputBar.style.display = \"none\";\n break;\n case \"afs_result\":\n _handleAfsResult(msg);\n break;\n case \"afs_error\":\n _handleAfsError(msg);\n break;\n case \"afs_event\":\n _handleAfsEvent(msg);\n break;\n case \"write\":\n showChat();\n addMsg(msg.content, \"assistant\", msg.format, msg);\n break;\n case \"prompt\":\n showChat();\n showPrompt(msg);\n break;\n case \"clear\":\n messagesEl.innerHTML = \"\";\n break;\n case \"notify\":\n showChat();\n addMsg(msg.message, \"notify\");\n break;\n case \"navigate\":\n showPage(msg.pageId, msg.content, msg.format, msg.layout);\n break;\n case \"aup\":\n handleAup(msg);\n break;\n case \"aup_event_result\":\n handleAupEventResult(msg);\n break;\n }\n }\n\n function showPrompt(msg) {\n currentPrompt = msg;\n promptMsg.textContent = msg.message;\n promptOptions.innerHTML = \"\";\n\n if (msg.promptType === \"confirm\") {\n [\"Yes\", \"No\"].forEach(function(label) {\n var btn = document.createElement(\"button\");\n btn.textContent = label;\n btn.onclick = function() { sendPromptResponse(label === \"Yes\"); };\n promptOptions.appendChild(btn);\n });\n promptArea.style.display = \"block\";\n } else if ((msg.promptType === \"select\" || msg.promptType === \"multiselect\") && msg.options) {\n msg.options.forEach(function(opt) {\n var btn = document.createElement(\"button\");\n btn.textContent = opt;\n btn.onclick = function() { sendPromptResponse(opt); };\n promptOptions.appendChild(btn);\n });\n promptArea.style.display = \"block\";\n } else {\n addMsg(msg.message, \"system\");\n if (msg.promptType === \"password\") inputEl.type = \"password\";\n inputEl.focus();\n }\n }\n\n function sendPromptResponse(value) {\n if (ws && ws.readyState === 1) {\n ws.send(JSON.stringify({ type: \"prompt_response\", value: value }));\n }\n promptArea.style.display = \"none\";\n currentPrompt = null;\n }\n\n function showPage(pageId, content, format, layout) {\n dismissSplash();\n pageTitle.textContent = pageId;\n if (format === \"layout\" && layout) {\n var html = '<div class=\"layout-page\">';\n if (layout.header) html += '<div class=\"layout-header\">' + _escapeHtml(layout.header) + '</div>';\n if (layout.sidebar) html += '<div class=\"layout-sidebar\">' + _escapeHtml(layout.sidebar) + '</div>';\n html += '<div class=\"layout-main\">' + _escapeHtml(layout.main || \"\") + '</div>';\n if (layout.footer) html += '<div class=\"layout-footer\">' + _escapeHtml(layout.footer) + '</div>';\n html += '</div>';\n pageContent.innerHTML = html;\n } else {\n pageContent.innerHTML = sanitizeHtml(content);\n }\n messagesEl.style.display = \"none\";\n inputBar.style.display = \"none\";\n promptArea.style.display = \"none\";\n pageView.style.display = \"flex\";\n }\n\n function dismissSplash() {\n if (desktopSplash && !desktopSplash.classList.contains(\"hidden\")) {\n desktopSplash.classList.add(\"hidden\");\n }\n }\n\n function showChat() {\n dismissSplash();\n pageView.style.display = \"none\";\n messagesEl.style.display = \"flex\";\n inputBar.style.display = \"flex\";\n }\n\n backBtn.onclick = showChat;\n } // end if (!_SNAPSHOT_MODE) — chat/session block\n\n // ── AUP Runtime (AUI) ──\n var aupDisplayEl = document.getElementById(\"aup-display\");\n var aupRootEl = document.getElementById(\"aup-root\");\n var aupBackBtn = document.getElementById(\"aup-back-to-chat\");\n var aupStatusEl = document.getElementById(\"aup-status\");\n var aupNodeTree = null; // current node tree (JSON)\n var aupFullPage = false;\n var headerEl = document.querySelector(\"header\");\n var aupToolbar = document.querySelector(\".aup-toolbar\");\n\n if (aupBackBtn) aupBackBtn.onclick = function() {\n aupFullPage = false;\n aupDisplayEl.classList.remove(\"active\", \"full-page\");\n if (headerEl) headerEl.style.display = \"none\";\n if (aupToolbar) aupToolbar.style.display = \"\";\n if (chromeToolbar && _aupChrome) chromeToolbar.classList.add(\"visible\");\n // Return to desktop splash\n if (desktopSplash) desktopSplash.classList.remove(\"hidden\");\n messagesEl.style.display = \"none\";\n inputBar.style.display = \"none\";\n document.title = \"AFS\";\n };\n\n var _aupChrome = false;\n\n // ── Scene Buffer State (Stage-to-Live dual buffer) ──\n var _aupSceneBuffers = {}; // sceneId → DOM element\n var _aupActiveSceneId = null;\n\n function _cleanupTickerIntervals(root) {\n if (!root || !root.querySelectorAll) return;\n var tickers = root.querySelectorAll(\".aup-ticker\");\n for (var i = 0; i < tickers.length; i++) {\n var t = tickers[i];\n if (typeof t._flipTimer === \"number\") {\n clearInterval(t._flipTimer);\n t._flipTimer = null;\n }\n }\n }\n\n function _untrackFrameWindowsIn(root) {\n if (!root || !root.querySelectorAll || typeof _unregisterFrameWindow !== \"function\") return;\n var frames = root.querySelectorAll(\"iframe\");\n for (var i = 0; i < frames.length; i++) {\n try { _unregisterFrameWindow(frames[i]); } catch (_ex) {}\n }\n }\n\n function _aupStageScene(sceneId, root, msg) {\n var buf = _aupSceneBuffers[sceneId];\n if (!buf) {\n buf = document.createElement(\"div\");\n buf.className = \"aup-buffer staged\";\n buf.setAttribute(\"data-scene-id\", sceneId);\n // Insert after aupRootEl inside the display container\n aupRootEl.parentNode.insertBefore(buf, aupRootEl.nextSibling);\n _aupSceneBuffers[sceneId] = buf;\n }\n // Render tree into buffer (off-screen via .staged visibility:hidden)\n _cleanupTickerIntervals(buf);\n _untrackFrameWindowsIn(buf);\n buf.innerHTML = \"\";\n var el = renderAupNode(root);\n if (el) buf.appendChild(el);\n // Apply style/theme/locale\n if (msg.style && !_userSetStyle) setStyle(msg.style);\n if (msg.theme && !_userSetMode) setMode(msg.theme);\n if (msg.locale) setLocale(msg.locale);\n }\n\n function _aupTakeScene(sceneId, transition, duration) {\n var buf = _aupSceneBuffers[sceneId];\n if (!buf) return;\n // Deactivate current active buffer\n if (_aupActiveSceneId && _aupSceneBuffers[_aupActiveSceneId]) {\n _aupSceneBuffers[_aupActiveSceneId].classList.remove(\"active\");\n _aupSceneBuffers[_aupActiveSceneId].classList.add(\"staged\");\n }\n // Hide the original aupRootEl if it was showing\n aupRootEl.classList.add(\"aup-buffer-hidden\");\n // Activate new\n buf.classList.remove(\"staged\");\n buf.classList.add(\"active\");\n // Transition animation\n if (transition === \"dissolve\") {\n buf.style.animation = \"aup-scene-fade-in \" + (duration || 300) + \"ms ease\";\n }\n _aupActiveSceneId = sceneId;\n // Ensure AUP display is visible\n showAupDisplay();\n }\n\n function _aupReleaseScene(sceneId) {\n var buf = _aupSceneBuffers[sceneId];\n if (buf) {\n _cleanupTickerIntervals(buf);\n _untrackFrameWindowsIn(buf);\n buf.remove();\n delete _aupSceneBuffers[sceneId];\n }\n }\n\n function handleAup(msg) {\n if (msg.treeVersion) _aupTreeVersion = msg.treeVersion;\n if (msg.action === \"render\") {\n aupNodeTree = msg.root;\n aupFullPage = !!msg.fullPage;\n _aupChrome = !!msg.chrome;\n // Only apply server-sent style/mode if user hasn't manually overridden\n if (msg.style && !_userSetStyle) setStyle(msg.style);\n if (msg.theme && !_userSetMode) setMode(msg.theme);\n if (msg.locale) setLocale(msg.locale);\n renderAupTree();\n showAupDisplay();\n } else if (msg.action === \"patch\") {\n if (aupNodeTree && msg.ops) {\n applyAupPatches(msg.ops);\n applyAupDomPatches(msg.ops);\n }\n } else if (msg.action === \"stage\") {\n _aupStageScene(msg.sceneId, msg.root, msg);\n // Ensure display container is visible so buffer can pre-render\n showAupDisplay();\n } else if (msg.action === \"take\") {\n _aupTakeScene(msg.sceneId, msg.transition, msg.duration);\n } else if (msg.action === \"release\") {\n _aupReleaseScene(msg.sceneId);\n } else if (msg.action === \"surface-update\") {\n // Targeted surface content replacement — only touches one surface's DOM.\n // All other panels, dividers, scroll positions preserved.\n if (aupNodeTree && msg.surfaceId) {\n var surfNode = findAupNode(aupNodeTree, msg.surfaceId);\n if (surfNode) {\n surfNode.children = msg.children || [];\n }\n }\n if (aupRootEl && msg.surfaceId) {\n var surfEl = aupRootEl.querySelector('[data-aup-id=\"' + msg.surfaceId + '\"]');\n if (surfEl) {\n var contentEl = surfEl.querySelector('.wm-surface-content');\n if (contentEl) {\n _cleanupTickerIntervals(contentEl);\n _untrackFrameWindowsIn(contentEl);\n contentEl.innerHTML = \"\";\n var children = msg.children || [];\n for (var ci = 0; ci < children.length; ci++) {\n var rendered = renderAupNode(children[ci]);\n if (rendered) contentEl.appendChild(rendered);\n }\n }\n }\n }\n }\n }\n\n function handleAupEventResult(_msg) {\n // Future: show loading state resolved, etc.\n }\n\n function showAupDisplay() {\n dismissSplash();\n messagesEl.style.display = \"none\";\n inputBar.style.display = \"none\";\n pageView.style.display = \"none\";\n promptArea.style.display = \"none\";\n aupDisplayEl.classList.add(\"active\");\n\n // Always full-page: web endpoint is a pure AUP rendering surface\n aupDisplayEl.classList.add(\"full-page\");\n if (headerEl) headerEl.style.display = \"none\";\n if (aupToolbar) aupToolbar.style.display = \"none\";\n // Chrome toolbar only shown when app explicitly requests it\n if (chromeToolbar) {\n if (_aupChrome) chromeToolbar.classList.add(\"visible\");\n else chromeToolbar.classList.remove(\"visible\");\n }\n var title = findAupTitle(aupNodeTree);\n document.title = title || \"AUP App\";\n }\n\n function findAupTitle(node) {\n if (!node) return null;\n if (node.type === \"text\" && node.props && node.props.level === 1) return node.props.content;\n if (node.children) {\n for (var i = 0; i < node.children.length; i++) {\n var t = findAupTitle(node.children[i]);\n if (t) return t;\n }\n }\n return null;\n }\n\n function renderAupTree() {\n if (!aupNodeTree || !aupRootEl) return;\n _cleanupTickerIntervals(aupRootEl);\n // Remove body-mounted overlay frames from previous render\n var oldOverlays = document.querySelectorAll(\"[data-aup-overlay]\");\n for (var oi = 0; oi < oldOverlays.length; oi++) {\n _untrackFrameWindowsIn(oldOverlays[oi]);\n oldOverlays[oi].remove();\n }\n _untrackFrameWindowsIn(aupRootEl);\n aupRootEl.innerHTML = \"\";\n var el = renderAupNode(aupNodeTree);\n if (el) aupRootEl.appendChild(el);\n // Page transition animation\n aupRootEl.classList.remove(\"aup-animating\");\n void aupRootEl.offsetWidth; // force reflow\n aupRootEl.classList.add(\"aup-animating\");\n }\n\n // ── URL Navigation: deep links & history ──\n var _aupCurrentRoute = null; // current route string (e.g., \"#/aup\" or \"#/page/mypage\")\n var _aupSuppressPopstate = false;\n\n function _aupPushRoute(route, replace) {\n if (_aupCurrentRoute === route) return;\n _aupCurrentRoute = route;\n _aupSuppressPopstate = true;\n try {\n if (replace) { history.replaceState({ aupRoute: route }, \"\", route); }\n else { history.pushState({ aupRoute: route }, \"\", route); }\n } catch(ex) {}\n _aupSuppressPopstate = false;\n }\n\n // Patch showPage to push URL state\n var _origShowPage = showPage;\n showPage = function(pageId, content, format, layout) {\n _origShowPage(pageId, content, format, layout);\n _aupPushRoute(\"#/page/\" + encodeURIComponent(pageId));\n _aupDisplayPushed = false; // reset so returning to AUP creates new history entry\n };\n\n // Patch showAupDisplay to push URL state (only first time; subsequent calls use replaceState)\n var _origShowAupDisplay = showAupDisplay;\n var _aupDisplayPushed = false;\n showAupDisplay = function() {\n _origShowAupDisplay();\n _aupPushRoute(\"#/aup\", _aupDisplayPushed);\n _aupDisplayPushed = true;\n };\n\n // Handle browser back/forward\n window.addEventListener(\"popstate\", function(e) {\n if (_aupSuppressPopstate) return;\n var route = (e.state && e.state.aupRoute) || location.hash || \"\";\n _aupCurrentRoute = route;\n if (route.indexOf(\"#/page/\") === 0) {\n var pageId = decodeURIComponent(route.slice(7));\n // Request page from server\n if (ws && ws.readyState === 1) {\n ws.send(JSON.stringify({ type: \"navigate_request\", pageId: pageId }));\n }\n } else if (route === \"#/aup\") {\n // Re-show AUP display if available\n if (aupNodeTree) {\n _origShowAupDisplay();\n }\n }\n });\n\n // On initial load, check hash for deep link\n (function() {\n var hash = location.hash || \"\";\n if (hash.indexOf(\"#/page/\") === 0) {\n var pageId = decodeURIComponent(hash.slice(7));\n // Will be sent after WS connects — store for later\n window._aupPendingDeepLink = { type: \"page\", pageId: pageId };\n } else if (hash === \"#/aup\") {\n window._aupPendingDeepLink = { type: \"aup\" };\n }\n })();\n\n function renderAupNode(node) {\n if (!node || !node.type) return null;\n var el;\n switch (node.type) {\n case \"view\": el = renderAupView(node); break;\n case \"text\": el = renderAupText(node); break;\n case \"action\": el = renderAupAction(node); break;\n case \"input\": el = renderAupInput(node); break;\n case \"media\": el = renderAupMedia(node); break;\n case \"overlay\": el = renderAupOverlay(node); break;\n case \"broadcast\": el = renderAupBroadcast(node); break;\n case \"table\": el = renderAupTable(node); break;\n case \"time\": el = renderAupTime(node); break;\n case \"chart\": {\n var cv = (node.props || {}).variant || \"\";\n el = ([\"candlestick\",\"ohlc\",\"trading-line\",\"trading-area\",\"baseline\",\"volume\"].indexOf(cv) >= 0) ? renderAupFinanceChart(node) : renderAupChart(node);\n break;\n }\n case \"map\": el = (node.props && node.props.variant === \"globe\") ? renderAupGlobe(node) : renderAupMap(node); break;\n case \"calendar\": el = renderAupCalendar(node); break;\n case \"moonphase\": el = renderAupMoonPhase(node); break;\n case \"natal-chart\": el = renderAupNatalChart(node); break;\n case \"terminal\": el = renderAupTerminal(node); break;\n case \"editor\": el = renderAupEditor(node); break;\n case \"frame\": el = renderAupFrame(node); break;\n case \"canvas\": el = renderAupCanvas(node); break;\n case \"deck\": el = renderAupDeck(node); break;\n case \"ticker\": el = renderAupTicker(node); break;\n case \"afs-list\": el = renderAupList(node); break;\n case \"device\": el = renderAupDevice(node); break;\n case \"wm\": el = renderAupWm(node); break;\n default: el = renderAupUnknown(node); break;\n }\n if (el && node.id) el.setAttribute(\"data-aup-id\", node.id);\n // Universal region prop — for overlay-grid placement\n if (el) {\n var region = (node.props && node.props.region) || (node.state && node.state.region);\n if (region) el.setAttribute(\"data-region\", region);\n var role = node.props && node.props.role;\n if (role) el.setAttribute(\"data-role\", role);\n }\n return el;\n }\n`;\n\nexport const CORE_TAIL_JS = `\n // ── AUP Patch application (client-side) ──\n function applyAupPatches(ops) {\n if (!aupNodeTree) return;\n for (var i = 0; i < ops.length; i++) {\n applyAupPatchOp(ops[i]);\n }\n }\n\n function applyAupPatchOp(op) {\n switch (op.op) {\n case \"create\": {\n var parent = findAupNode(aupNodeTree, op.parentId);\n if (!parent) return;\n if (!parent.children) parent.children = [];\n var node = Object.assign({}, op.node, { id: op.id });\n if (typeof op.index === \"number\") {\n parent.children.splice(op.index, 0, node);\n } else {\n parent.children.push(node);\n }\n break;\n }\n case \"update\": {\n var target = findAupNode(aupNodeTree, op.id);\n if (!target) return;\n if (op.props) target.props = Object.assign(target.props || {}, op.props);\n if (op.state) target.state = Object.assign(target.state || {}, op.state);\n if (op.events !== undefined) target.events = op.events;\n break;\n }\n case \"remove\": {\n removeAupNode(aupNodeTree, op.id);\n break;\n }\n case \"reorder\": {\n var rParent = findAupNode(aupNodeTree, op.parentId);\n if (!rParent || !rParent.children) return;\n var idx = -1;\n for (var j = 0; j < rParent.children.length; j++) {\n if (rParent.children[j].id === op.id) { idx = j; break; }\n }\n if (idx < 0) return;\n var moved = rParent.children.splice(idx, 1)[0];\n rParent.children.splice(op.index, 0, moved);\n break;\n }\n }\n }\n\n function findAupNode(node, id) {\n if (!node) return null;\n if (node.id === id) return node;\n if (node.children) {\n for (var i = 0; i < node.children.length; i++) {\n var found = findAupNode(node.children[i], id);\n if (found) return found;\n }\n }\n return null;\n }\n\n function removeAupNode(root, id) {\n if (root.id === id) { aupNodeTree = null; return; }\n if (!root.children) return;\n for (var i = 0; i < root.children.length; i++) {\n if (root.children[i].id === id) { root.children.splice(i, 1); return; }\n removeAupNode(root.children[i], id);\n }\n }\n\n // ── Targeted DOM patching — avoids full re-render to preserve stateful nodes like afs-list ──\n function applyAupDomPatches(ops) {\n if (!aupRootEl) return;\n for (var i = 0; i < ops.length; i++) {\n var op = ops[i];\n if (op.op === \"remove\") {\n var domEl = aupRootEl.querySelector('[data-aup-id=\"' + op.id + '\"]');\n if (domEl) {\n _cleanupTickerIntervals(domEl);\n _untrackFrameWindowsIn(domEl);\n domEl.remove();\n }\n } else if (op.op === \"create\") {\n var parentEl = aupRootEl.querySelector('[data-aup-id=\"' + op.parentId + '\"]');\n if (!parentEl) continue;\n var createdNode = findAupNode(aupNodeTree, op.id);\n if (!createdNode) continue;\n var newEl = renderAupNode(createdNode);\n if (!newEl) continue;\n if (typeof op.index === \"number\" && parentEl.children[op.index]) {\n parentEl.insertBefore(newEl, parentEl.children[op.index]);\n } else {\n parentEl.appendChild(newEl);\n }\n } else if (op.op === \"update\") {\n // For update, re-render the specific node in place\n var updDomEl = aupRootEl.querySelector('[data-aup-id=\"' + op.id + '\"]');\n var updNode = findAupNode(aupNodeTree, op.id);\n if (!updDomEl || !updNode) continue;\n var updNewEl = renderAupNode(updNode);\n if (updNewEl) {\n _cleanupTickerIntervals(updDomEl);\n _untrackFrameWindowsIn(updDomEl);\n updDomEl.replaceWith(updNewEl);\n }\n } else if (op.op === \"reorder\") {\n // Fallback to full re-render for reorder\n renderAupTree();\n return;\n }\n }\n }\n\n // ── IntersectionObserver for standalone animate triggers ──\n var _animateObserver = null;\n function _setupAnimateObserver() {\n if (_animateObserver) _animateObserver.disconnect();\n _animateObserver = new IntersectionObserver(function(entries) {\n entries.forEach(function(entry) {\n if (entry.isIntersecting) {\n var el = entry.target;\n // Skip if inside a deck (deck handles its own animations)\n if (el.closest(\".aup-deck\")) return;\n el.classList.add(\"aup-animated\");\n // Trigger count-up if applicable\n if (el.getAttribute(\"data-animate\") === \"count-up\" && typeof _startCountUp === \"function\") {\n _startCountUp(el);\n }\n _animateObserver.unobserve(el);\n }\n });\n }, { threshold: 0.15 });\n // Observe all [data-animate] elements outside decks\n var animatables = document.querySelectorAll(\"[data-animate]:not(.aup-deck [data-animate])\");\n for (var i = 0; i < animatables.length; i++) _animateObserver.observe(animatables[i]);\n }\n\n // Hook into renderAupTree to re-setup observer after each render\n var _origRenderAupTree = renderAupTree;\n renderAupTree = function() {\n _origRenderAupTree();\n _setupAnimateObserver();\n // Also hook deck slide animations for count-up\n var deckAnimated = document.querySelectorAll(\".aup-deck [data-animate='count-up'].aup-animated\");\n for (var i = 0; i < deckAnimated.length; i++) {\n if (typeof _startCountUp === \"function\") _startCountUp(deckAnimated[i]);\n }\n };\n\n // ── Global Keyboard Shortcuts (from shortcut:* events in tree) ──\n var _prevShortcutHandler = null;\n var _origRenderForShortcuts = renderAupTree;\n renderAupTree = function() {\n _origRenderForShortcuts();\n if (_prevShortcutHandler) document.removeEventListener(\"keydown\", _prevShortcutHandler);\n _prevShortcutHandler = null;\n var shortcuts = [];\n (function walkShortcuts(n) {\n if (!n) return;\n if (n.events) {\n for (var k in n.events) {\n if (k.indexOf(\"shortcut:\") === 0) shortcuts.push({ spec: k.slice(9), nodeId: n.id, event: k });\n }\n }\n if (n.children) n.children.forEach(walkShortcuts);\n })(aupNodeTree);\n if (shortcuts.length === 0) return;\n _prevShortcutHandler = function(e) {\n for (var i = 0; i < shortcuts.length; i++) {\n if (_matchShortcut(e, shortcuts[i].spec)) {\n e.preventDefault();\n if (ws && ws.readyState === 1) ws.send(JSON.stringify({\n type: \"aup_event\", nodeId: shortcuts[i].nodeId, event: shortcuts[i].event\n }));\n return;\n }\n }\n };\n document.addEventListener(\"keydown\", _prevShortcutHandler);\n };\n function _matchShortcut(e, spec) {\n var parts = spec.toLowerCase().split(\"+\");\n var key = parts.pop();\n var wantMeta = false, wantCtrl = false, wantShift = false, wantAlt = false;\n for (var i = 0; i < parts.length; i++) {\n if (parts[i] === \"meta\") wantMeta = true;\n else if (parts[i] === \"ctrl\") wantCtrl = true;\n else if (parts[i] === \"shift\") wantShift = true;\n else if (parts[i] === \"alt\") wantAlt = true;\n }\n var isMac = /Mac|iPhone|iPad/.test(navigator.platform || \"\");\n // meta = Cmd on macOS, Ctrl on other platforms\n var metaPressed = wantMeta ? (isMac ? e.metaKey : e.ctrlKey) : false;\n var ctrlPressed = wantCtrl ? e.ctrlKey : false;\n if (wantMeta && !metaPressed) return false;\n if (wantCtrl && !ctrlPressed) return false;\n if (!wantMeta && (isMac ? e.metaKey : false)) return false;\n if (!wantCtrl && !wantMeta && e.ctrlKey) return false;\n if (wantShift !== e.shiftKey) return false;\n if (wantAlt !== e.altKey) return false;\n return e.key.toLowerCase() === key;\n }\n\n // ── MutationObserver for deck-triggered count-up ──\n var _countUpMo = new MutationObserver(function(mutations) {\n for (var i = 0; i < mutations.length; i++) {\n var m = mutations[i];\n if (m.type === \"attributes\" && m.attributeName === \"class\") {\n var el = m.target;\n if (el.classList.contains(\"aup-animated\") &&\n el.getAttribute(\"data-animate\") === \"count-up\" &&\n typeof _startCountUp === \"function\") {\n _startCountUp(el);\n }\n }\n }\n });\n _countUpMo.observe(document.body, { subtree: true, attributes: true, attributeFilter: [\"class\"] });\n\n // ── Frame Bridge postMessage handler ──\n var _frameBridgeMsgCount = {};\n var _frameBridgeLastReset = Date.now();\n\n function _isMountedFrameWindow(win) {\n if (!win) return false;\n var frames = document.querySelectorAll(\".aup-frame iframe, .aup-frame-overlay iframe\");\n for (var i = 0; i < frames.length; i++) {\n try {\n if (frames[i].contentWindow === win) return true;\n } catch (_ex) {}\n }\n return false;\n }\n\n window.addEventListener(\"message\", function(e) {\n if (!e.data || typeof e.data.type !== \"string\") return;\n\n // Source check: only accept from known iframe contentWindows\n if (typeof _aupFrameWindows !== \"undefined\") {\n if (!_aupFrameWindows.has(e.source)) return;\n if (!_isMountedFrameWindow(e.source)) {\n _aupFrameWindows.delete(e.source);\n if (typeof _aupBridgeWindows !== \"undefined\") _aupBridgeWindows.delete(e.source);\n if (typeof _aupBridgeOriginByWindow !== \"undefined\" && _aupBridgeOriginByWindow.delete) {\n _aupBridgeOriginByWindow.delete(e.source);\n }\n return;\n }\n }\n var isBridgeMessage =\n e.data.type === \"aup_event\" ||\n e.data.type === \"aup_navigate\" ||\n e.data.type === \"aup_toast\" ||\n e.data.type === \"aup_fetch_request\" ||\n e.data.type === \"aup_bridge_read\" ||\n e.data.type === \"aup_bridge_list\" ||\n e.data.type === \"aup_bridge_write\" ||\n e.data.type === \"aup_bridge_exec\" ||\n e.data.type === \"aup_bridge_subscribe\" ||\n e.data.type === \"aup_bridge_unsubscribe\";\n // Bridge check: only accept from explicitly bridge-enabled frames.\n if (isBridgeMessage) {\n if (typeof _aupBridgeWindows === \"undefined\" || !_aupBridgeWindows.has(e.source)) return;\n if (typeof _aupBridgeOriginByWindow !== \"undefined\" && _aupBridgeOriginByWindow.get) {\n var expectedOrigin = _aupBridgeOriginByWindow.get(e.source);\n if (!expectedOrigin || e.origin !== expectedOrigin) return;\n }\n }\n\n // Rate limiting: max 60 messages per second per source\n var now = Date.now();\n if (now - _frameBridgeLastReset > 1000) {\n _frameBridgeMsgCount = {};\n _frameBridgeLastReset = now;\n }\n var srcKey = e.origin || \"unknown\";\n _frameBridgeMsgCount[srcKey] = (_frameBridgeMsgCount[srcKey] || 0) + 1;\n if (_frameBridgeMsgCount[srcKey] > 60) return;\n\n switch (e.data.type) {\n case \"aup_event\": {\n // Route to handleAupEvent same as button clicks\n if (ws && ws.readyState === 1) {\n ws.send(JSON.stringify({\n type: \"aup_event\",\n nodeId: \"__bridge__\",\n event: e.data.event || \"message\",\n data: e.data.data\n }));\n }\n break;\n }\n case \"aup_navigate\": {\n var path = e.data.path || \"\";\n // If path starts with /pages/, update the iframe src\n if (path.indexOf(\"/pages/\") === 0) {\n var frames = document.querySelectorAll(\".aup-frame iframe\");\n if (frames.length > 0 && e.source) {\n for (var i = 0; i < frames.length; i++) {\n try {\n if (frames[i].contentWindow === e.source) {\n var pageId = path.replace(/^\\\\/pages\\\\//, \"\");\n var newSrc = location.origin + \"/p/\" + encodeURIComponent(pageId);\n if (_afsSessionId) newSrc += \"?sid=\" + encodeURIComponent(_afsSessionId);\n if (_afsSessionToken) {\n newSrc += (_afsSessionId ? \"&\" : \"?\") + \"st=\" + encodeURIComponent(_afsSessionToken);\n }\n frames[i].src = newSrc;\n break;\n }\n } catch(ex) {}\n }\n }\n }\n break;\n }\n case \"aup_toast\": {\n // Create a toast overlay via AUP patch\n if (ws && ws.readyState === 1) {\n ws.send(JSON.stringify({\n type: \"aup_event\",\n nodeId: \"__bridge__\",\n event: \"toast\",\n data: { message: e.data.message || \"\", intent: e.data.intent || \"info\" }\n }));\n }\n break;\n }\n case \"aup_fetch_request\": {\n var reqPath = typeof e.data.path === \"string\" ? e.data.path : \"/\";\n var reqId = typeof e.data.id === \"string\" ? e.data.id : \"\";\n var source = e.source;\n if (!reqPath || reqPath.charAt(0) !== \"/\" || reqPath.indexOf(\"..\") >= 0) return;\n var respOrigin = e.origin;\n if (!respOrigin || respOrigin === \"null\") return;\n if (window.afs && window.afs.read) {\n window.afs.read(reqPath).then(function(result) {\n if (source) {\n source.postMessage({ type: \"aup_fetch_response\", id: reqId, payload: result }, respOrigin);\n }\n }).catch(function(err) {\n if (source) {\n source.postMessage(\n { type: \"aup_fetch_response\", id: reqId, payload: null, error: err.message },\n respOrigin,\n );\n }\n });\n }\n break;\n }\n case \"aup_bridge_read\":\n case \"aup_bridge_list\":\n case \"aup_bridge_write\":\n case \"aup_bridge_exec\": {\n var bp = e.data.params || {};\n var bId = typeof e.data.id === \"string\" ? e.data.id : \"\";\n var bSource = e.source;\n var bOrigin = e.origin;\n var bPath = typeof bp.path === \"string\" ? bp.path : \"/\";\n if (!bPath || bPath.charAt(0) !== \"/\" || bPath.indexOf(\"..\") >= 0) return;\n if (!bOrigin || bOrigin === \"null\") return;\n if (!window.afs) return;\n var bPromise;\n switch (e.data.type) {\n case \"aup_bridge_read\": bPromise = window.afs.read(bPath); break;\n case \"aup_bridge_list\": bPromise = window.afs.list(bPath, bp.options || {}); break;\n case \"aup_bridge_write\": bPromise = window.afs.write(bPath, bp.content, bp.meta); break;\n case \"aup_bridge_exec\": bPromise = window.afs.exec(bPath, bp.args || {}); break;\n }\n if (bPromise && bPromise.then) {\n bPromise.then(function(result) {\n if (bSource) bSource.postMessage({ type: \"aup_bridge_response\", id: bId, payload: result }, bOrigin);\n }).catch(function(err) {\n if (bSource) bSource.postMessage({ type: \"aup_bridge_response\", id: bId, payload: null, error: err.message }, bOrigin);\n });\n }\n break;\n }\n case \"aup_bridge_subscribe\": {\n var subFilter = e.data.filter;\n var subId = e.data.subId;\n var subSource = e.source;\n var subOrigin = e.origin;\n if (!subId || !subOrigin || subOrigin === \"null\" || !window.afs || !window.afs.subscribe) return;\n var unsub = window.afs.subscribe(subFilter, function(payload) {\n if (subSource) {\n try { subSource.postMessage({ type: \"aup_subscribe_event\", subId: subId, payload: payload }, subOrigin); } catch(ex) {}\n }\n });\n if (!window._aupBridgeSubs) window._aupBridgeSubs = {};\n window._aupBridgeSubs[subId] = unsub;\n // Track which window owns this subscription for cleanup\n if (!window._aupBridgeSubsByWindow) window._aupBridgeSubsByWindow = new Map();\n if (!window._aupBridgeSubsByWindow.has(subSource)) window._aupBridgeSubsByWindow.set(subSource, new Set());\n window._aupBridgeSubsByWindow.get(subSource).add(subId);\n break;\n }\n case \"aup_bridge_unsubscribe\": {\n var uSubId = e.data.subId;\n if (window._aupBridgeSubs && window._aupBridgeSubs[uSubId]) {\n window._aupBridgeSubs[uSubId]();\n delete window._aupBridgeSubs[uSubId];\n }\n // Clean up reverse tracking\n if (window._aupBridgeSubsByWindow && e.source) {\n var uSubs = window._aupBridgeSubsByWindow.get(e.source);\n if (uSubs) {\n uSubs.delete(uSubId);\n if (uSubs.size === 0) window._aupBridgeSubsByWindow.delete(e.source);\n }\n }\n break;\n }\n }\n });\n\n // ── Send + Connect (skipped in snapshot mode) ──\n if (typeof _SNAPSHOT_MODE === \"undefined\" || !_SNAPSHOT_MODE) {\n function send() {\n var text = inputEl.value.trim();\n if (!text && !currentPrompt) return;\n var val = inputEl.value;\n inputEl.value = \"\";\n inputEl.style.height = \"auto\";\n\n if (currentPrompt) {\n addMsg(val, \"user\");\n sendPromptResponse(val);\n } else {\n addMsg(val, \"user\");\n if (ws && ws.readyState === 1) {\n ws.send(JSON.stringify({ type: \"input\", content: val }));\n }\n }\n }\n\n if (btnSend) btnSend.onclick = send;\n\n if (inputEl) {\n inputEl.onkeydown = function(e) {\n if (e.key === \"Enter\" && !e.shiftKey) { e.preventDefault(); send(); }\n };\n\n inputEl.oninput = function() {\n inputEl.style.height = \"auto\";\n inputEl.style.height = Math.min(inputEl.scrollHeight, 120) + \"px\";\n };\n }\n\n connect();\n } // end if (!_SNAPSHOT_MODE) — send/connect block\n})();\n`;\n"],"mappings":";AAAA,MAAa,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAg6B5B,MAAa,eAAe"}