synthos 0.7.1 → 0.8.0

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 (263) hide show
  1. package/README.md +215 -65
  2. package/default-pages/application.json +1 -0
  3. package/default-pages/json_tools.json +1 -1
  4. package/default-pages/oregon_trail.html +321 -0
  5. package/default-pages/oregon_trail.json +12 -0
  6. package/default-pages/sidebar_page.json +1 -0
  7. package/default-pages/solar_explorer.html +10 -18
  8. package/default-pages/solar_explorer.json +2 -2
  9. package/default-pages/two-panel_page.json +1 -0
  10. package/default-pages/us_map.html +192 -0
  11. package/default-pages/us_map.json +12 -0
  12. package/default-pages/us_map_1850.html +325 -0
  13. package/default-pages/us_map_1850.json +12 -0
  14. package/default-pages/western_cities_1850.html +526 -0
  15. package/default-pages/western_cities_1850.json +12 -0
  16. package/default-themes/{nebula-dawn.css → nebula-dawn.v2.css} +24 -0
  17. package/default-themes/{nebula-dusk.css → nebula-dusk.v2.css} +24 -0
  18. package/dist/agents/a2a/a2aProvider.d.ts +3 -0
  19. package/dist/agents/a2a/a2aProvider.d.ts.map +1 -0
  20. package/dist/agents/a2a/a2aProvider.js +126 -0
  21. package/dist/agents/a2a/a2aProvider.js.map +1 -0
  22. package/dist/agents/discovery.d.ts +30 -0
  23. package/dist/agents/discovery.d.ts.map +1 -0
  24. package/dist/agents/discovery.js +52 -0
  25. package/dist/agents/discovery.js.map +1 -0
  26. package/dist/agents/index.d.ts +7 -0
  27. package/dist/agents/index.d.ts.map +1 -0
  28. package/dist/agents/index.js +19 -0
  29. package/dist/agents/index.js.map +1 -0
  30. package/dist/agents/openclaw/gatewayManager.d.ts +113 -0
  31. package/dist/agents/openclaw/gatewayManager.d.ts.map +1 -0
  32. package/dist/agents/openclaw/gatewayManager.js +470 -0
  33. package/dist/agents/openclaw/gatewayManager.js.map +1 -0
  34. package/dist/agents/openclaw/openclawProvider.d.ts +3 -0
  35. package/dist/agents/openclaw/openclawProvider.d.ts.map +1 -0
  36. package/dist/agents/openclaw/openclawProvider.js +239 -0
  37. package/dist/agents/openclaw/openclawProvider.js.map +1 -0
  38. package/dist/agents/openclaw/sshTunnelManager.d.ts +23 -0
  39. package/dist/agents/openclaw/sshTunnelManager.d.ts.map +1 -0
  40. package/dist/agents/openclaw/sshTunnelManager.js +340 -0
  41. package/dist/agents/openclaw/sshTunnelManager.js.map +1 -0
  42. package/dist/agents/types.d.ts +64 -0
  43. package/dist/agents/types.d.ts.map +1 -0
  44. package/dist/agents/types.js +6 -0
  45. package/dist/agents/types.js.map +1 -0
  46. package/dist/connectors/airtable/connector.json +27 -0
  47. package/dist/connectors/alpha-vantage/connector.json +26 -0
  48. package/dist/connectors/brave-search/connector.json +26 -0
  49. package/dist/connectors/cloudinary/connector.json +27 -0
  50. package/dist/connectors/deepl/connector.json +28 -0
  51. package/dist/connectors/elevenlabs/connector.json +30 -0
  52. package/dist/connectors/giphy/connector.json +27 -0
  53. package/dist/connectors/github/connector.json +29 -0
  54. package/dist/connectors/huggingface/connector.json +27 -0
  55. package/dist/connectors/imgur/connector.json +29 -0
  56. package/dist/connectors/index.d.ts +1 -1
  57. package/dist/connectors/index.d.ts.map +1 -1
  58. package/dist/connectors/instagram/connector.json +43 -0
  59. package/dist/connectors/jira/connector.json +28 -0
  60. package/dist/connectors/mapbox/connector.json +26 -0
  61. package/dist/connectors/nasa/connector.json +27 -0
  62. package/dist/connectors/newsapi/connector.json +27 -0
  63. package/dist/connectors/notion/connector.json +28 -0
  64. package/dist/connectors/open-exchange-rates/connector.json +27 -0
  65. package/dist/connectors/openweathermap/connector.json +26 -0
  66. package/dist/connectors/pexels/connector.json +27 -0
  67. package/dist/connectors/registry.d.ts.map +1 -1
  68. package/dist/connectors/registry.js +42 -96
  69. package/dist/connectors/registry.js.map +1 -1
  70. package/dist/connectors/resend/connector.json +29 -0
  71. package/dist/connectors/rss2json/connector.json +27 -0
  72. package/dist/connectors/sendgrid/connector.json +27 -0
  73. package/dist/connectors/spoonacular/connector.json +28 -0
  74. package/dist/connectors/stability-ai/connector.json +27 -0
  75. package/dist/connectors/twilio/connector.json +28 -0
  76. package/dist/connectors/types.d.ts +23 -0
  77. package/dist/connectors/types.d.ts.map +1 -1
  78. package/dist/connectors/unsplash/connector.json +27 -0
  79. package/dist/connectors/wolfram-alpha/connector.json +26 -0
  80. package/dist/connectors/youtube-data/connector.json +30 -0
  81. package/dist/files.d.ts +1 -0
  82. package/dist/files.d.ts.map +1 -1
  83. package/dist/files.js +16 -1
  84. package/dist/files.js.map +1 -1
  85. package/dist/init.d.ts.map +1 -1
  86. package/dist/init.js +28 -0
  87. package/dist/init.js.map +1 -1
  88. package/dist/migrations.d.ts +3 -2
  89. package/dist/migrations.d.ts.map +1 -1
  90. package/dist/migrations.js +122 -138
  91. package/dist/migrations.js.map +1 -1
  92. package/dist/models/anthropic.d.ts +22 -0
  93. package/dist/models/anthropic.d.ts.map +1 -0
  94. package/dist/models/anthropic.js +76 -0
  95. package/dist/models/anthropic.js.map +1 -0
  96. package/dist/models/chainOfThought.d.ts +12 -0
  97. package/dist/models/chainOfThought.d.ts.map +1 -0
  98. package/dist/models/chainOfThought.js +45 -0
  99. package/dist/models/chainOfThought.js.map +1 -0
  100. package/dist/models/fireworksai.d.ts +30 -0
  101. package/dist/models/fireworksai.d.ts.map +1 -0
  102. package/dist/models/fireworksai.js +133 -0
  103. package/dist/models/fireworksai.js.map +1 -0
  104. package/dist/models/index.d.ts +7 -1
  105. package/dist/models/index.d.ts.map +1 -1
  106. package/dist/models/index.js +19 -1
  107. package/dist/models/index.js.map +1 -1
  108. package/dist/models/logCompletePrompt.d.ts +3 -0
  109. package/dist/models/logCompletePrompt.d.ts.map +1 -0
  110. package/dist/models/logCompletePrompt.js +23 -0
  111. package/dist/models/logCompletePrompt.js.map +1 -0
  112. package/dist/models/openai.d.ts +24 -0
  113. package/dist/models/openai.d.ts.map +1 -0
  114. package/dist/models/openai.js +80 -0
  115. package/dist/models/openai.js.map +1 -0
  116. package/dist/models/providers.d.ts +1 -0
  117. package/dist/models/providers.d.ts.map +1 -1
  118. package/dist/models/providers.js +12 -4
  119. package/dist/models/providers.js.map +1 -1
  120. package/dist/models/types.d.ts +34 -2
  121. package/dist/models/types.d.ts.map +1 -1
  122. package/dist/models/types.js +16 -0
  123. package/dist/models/types.js.map +1 -1
  124. package/dist/models/utils.d.ts +6 -0
  125. package/dist/models/utils.d.ts.map +1 -0
  126. package/dist/models/utils.js +21 -0
  127. package/dist/models/utils.js.map +1 -0
  128. package/dist/scripts.d.ts +2 -1
  129. package/dist/scripts.d.ts.map +1 -1
  130. package/dist/scripts.js +4 -3
  131. package/dist/scripts.js.map +1 -1
  132. package/dist/service/createCompletePrompt.d.ts +1 -1
  133. package/dist/service/createCompletePrompt.d.ts.map +1 -1
  134. package/dist/service/createCompletePrompt.js +9 -6
  135. package/dist/service/createCompletePrompt.js.map +1 -1
  136. package/dist/service/generateImage.d.ts +1 -1
  137. package/dist/service/generateImage.d.ts.map +1 -1
  138. package/dist/service/generateImage.js +3 -3
  139. package/dist/service/generateImage.js.map +1 -1
  140. package/dist/service/server.d.ts.map +1 -1
  141. package/dist/service/server.js +3 -0
  142. package/dist/service/server.js.map +1 -1
  143. package/dist/service/transformPage.d.ts +4 -2
  144. package/dist/service/transformPage.d.ts.map +1 -1
  145. package/dist/service/transformPage.js +74 -6
  146. package/dist/service/transformPage.js.map +1 -1
  147. package/dist/service/useAgentRoutes.d.ts +4 -0
  148. package/dist/service/useAgentRoutes.d.ts.map +1 -0
  149. package/dist/service/useAgentRoutes.js +389 -0
  150. package/dist/service/useAgentRoutes.js.map +1 -0
  151. package/dist/service/useApiRoutes.d.ts.map +1 -1
  152. package/dist/service/useApiRoutes.js +157 -16
  153. package/dist/service/useApiRoutes.js.map +1 -1
  154. package/dist/service/useConnectorRoutes.d.ts.map +1 -1
  155. package/dist/service/useConnectorRoutes.js +14 -3
  156. package/dist/service/useConnectorRoutes.js.map +1 -1
  157. package/dist/service/useGatewayRoutes.d.ts +4 -0
  158. package/dist/service/useGatewayRoutes.d.ts.map +1 -0
  159. package/dist/service/useGatewayRoutes.js +168 -0
  160. package/dist/service/useGatewayRoutes.js.map +1 -0
  161. package/dist/service/usePageRoutes.d.ts.map +1 -1
  162. package/dist/service/usePageRoutes.js +16 -5
  163. package/dist/service/usePageRoutes.js.map +1 -1
  164. package/dist/settings.d.ts +2 -1
  165. package/dist/settings.d.ts.map +1 -1
  166. package/dist/settings.js +4 -8
  167. package/dist/settings.js.map +1 -1
  168. package/dist/themes.d.ts +14 -0
  169. package/dist/themes.d.ts.map +1 -1
  170. package/dist/themes.js +86 -13
  171. package/dist/themes.js.map +1 -1
  172. package/package.json +10 -5
  173. package/page-scripts/helpers-v2.js +222 -0
  174. package/page-scripts/page-v2.js +656 -0
  175. package/required-pages/builder.html +1 -27
  176. package/required-pages/pages.html +745 -22
  177. package/required-pages/settings.html +819 -21
  178. package/required-pages/synthos_apis.html +56 -1
  179. package/src/agents/a2a/a2aProvider.ts +110 -0
  180. package/src/agents/discovery.ts +74 -0
  181. package/src/agents/index.ts +6 -0
  182. package/src/agents/openclaw/gatewayManager.ts +559 -0
  183. package/src/agents/openclaw/openclawProvider.ts +261 -0
  184. package/src/agents/openclaw/sshTunnelManager.ts +385 -0
  185. package/src/agents/types.ts +82 -0
  186. package/src/connectors/airtable/connector.json +27 -0
  187. package/src/connectors/alpha-vantage/connector.json +26 -0
  188. package/src/connectors/brave-search/connector.json +26 -0
  189. package/src/connectors/cloudinary/connector.json +27 -0
  190. package/src/connectors/deepl/connector.json +28 -0
  191. package/src/connectors/elevenlabs/connector.json +30 -0
  192. package/src/connectors/giphy/connector.json +27 -0
  193. package/src/connectors/github/connector.json +29 -0
  194. package/src/connectors/huggingface/connector.json +27 -0
  195. package/src/connectors/imgur/connector.json +29 -0
  196. package/src/connectors/index.ts +2 -0
  197. package/src/connectors/instagram/connector.json +43 -0
  198. package/src/connectors/jira/connector.json +28 -0
  199. package/src/connectors/mapbox/connector.json +26 -0
  200. package/src/connectors/nasa/connector.json +27 -0
  201. package/src/connectors/newsapi/connector.json +27 -0
  202. package/src/connectors/notion/connector.json +28 -0
  203. package/src/connectors/open-exchange-rates/connector.json +27 -0
  204. package/src/connectors/openweathermap/connector.json +26 -0
  205. package/src/connectors/pexels/connector.json +27 -0
  206. package/src/connectors/registry.ts +21 -97
  207. package/src/connectors/resend/connector.json +29 -0
  208. package/src/connectors/rss2json/connector.json +27 -0
  209. package/src/connectors/sendgrid/connector.json +27 -0
  210. package/src/connectors/spoonacular/connector.json +28 -0
  211. package/src/connectors/stability-ai/connector.json +27 -0
  212. package/src/connectors/twilio/connector.json +28 -0
  213. package/src/connectors/types.ts +25 -0
  214. package/src/connectors/unsplash/connector.json +27 -0
  215. package/src/connectors/wolfram-alpha/connector.json +26 -0
  216. package/src/connectors/youtube-data/connector.json +30 -0
  217. package/src/files.ts +14 -0
  218. package/src/init.ts +27 -0
  219. package/src/migrations.ts +121 -138
  220. package/src/models/anthropic.ts +89 -0
  221. package/src/models/chainOfThought.ts +56 -0
  222. package/src/models/fireworksai.ts +136 -0
  223. package/src/models/index.ts +7 -1
  224. package/src/models/logCompletePrompt.ts +25 -0
  225. package/src/models/openai.ts +90 -0
  226. package/src/models/providers.ts +12 -3
  227. package/src/models/types.ts +67 -2
  228. package/src/models/utils.ts +16 -0
  229. package/src/scripts.ts +2 -2
  230. package/src/service/createCompletePrompt.ts +3 -1
  231. package/src/service/generateImage.ts +2 -2
  232. package/src/service/server.ts +4 -0
  233. package/src/service/transformPage.ts +81 -8
  234. package/src/service/useAgentRoutes.ts +423 -0
  235. package/src/service/useApiRoutes.ts +173 -18
  236. package/src/service/useConnectorRoutes.ts +14 -3
  237. package/src/service/usePageRoutes.ts +20 -6
  238. package/src/settings.ts +6 -10
  239. package/src/themes.ts +84 -12
  240. package/tests/README.md +12 -0
  241. package/tests/anthropic.spec.ts +84 -0
  242. package/tests/chainOfThought.spec.ts +108 -0
  243. package/tests/ensureScripts.spec.ts +82 -0
  244. package/tests/files.spec.ts +233 -0
  245. package/tests/fireworksai.spec.ts +92 -0
  246. package/tests/logCompletePrompt.spec.ts +74 -0
  247. package/tests/migrations.spec.ts +169 -0
  248. package/tests/openai.spec.ts +71 -0
  249. package/tests/pages.spec.ts +328 -0
  250. package/tests/providers.spec.ts +144 -0
  251. package/tests/scripts.spec.ts +209 -0
  252. package/tests/transformPage.spec.ts +931 -0
  253. package/tests/types.spec.ts +23 -0
  254. package/default-pages/app_builder.json +0 -1
  255. package/default-pages/sidebar_builder.json +0 -1
  256. package/default-pages/two-panel_builder.json +0 -1
  257. package/images/home.png +0 -0
  258. package/images/page-management.png +0 -0
  259. package/images/settings.png +0 -0
  260. package/images/synthos-square.png +0 -0
  261. /package/default-pages/{app_builder.html → application.html} +0 -0
  262. /package/default-pages/{sidebar_builder.html → sidebar_page.html} +0 -0
  263. /package/default-pages/{two-panel_builder.html → two-panel_page.html} +0 -0
@@ -0,0 +1,321 @@
1
+ <!DOCTYPE html><html lang="en"><head>
2
+ <meta charset="UTF-8">
3
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
4
+ <title>SynthOS</title>
5
+ <script id="theme-info" src="/api/theme-info.js" data-locked="true"></script>
6
+ <link id="theme-css" rel="stylesheet" href="/api/theme.css" data-locked="true">
7
+ <style>.idle-container{position:absolute;width:100%;height:100%;pointer-events:none;opacity:1;transition:opacity 1s ease-out}.idle-container.hidden{opacity:0}.breathing-orb{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:80px;height:80px;border-radius:50%;background:radial-gradient(circle,rgba(102,126,234,.15) 0,transparent 70%);animation:4s ease-in-out infinite breathe}.breathing-orb::before{content:'';position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:8px;height:8px;border-radius:50%;background:rgba(183,148,246,.6);box-shadow:0 0 20px rgba(183,148,246,.4);animation:4s ease-in-out infinite core-pulse}@keyframes breathe{0%,100%{width:80px;height:80px;opacity:.3}50%{width:120px;height:120px;opacity:.6}}@keyframes core-pulse{0%,100%{opacity:.4;box-shadow:0 0 20px rgba(183,148,246,.3)}50%{opacity:.8;box-shadow:0 0 30px rgba(183,148,246,.5)}}.orbit-ring{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:200px;height:200px;border:1px solid rgba(102,126,234,.1);border-radius:50%;animation:20s linear infinite orbit-rotate}.orbit-ring::after{content:'';position:absolute;top:-3px;left:50%;transform:translateX(-50%);width:6px;height:6px;background:rgba(240,147,251,.5);border-radius:50%;box-shadow:0 0 10px rgba(240,147,251,.3)}@keyframes orbit-rotate{from{transform:translate(-50%,-50%) rotate(0)}to{transform:translate(-50%,-50%) rotate(360deg)}}</style>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/14.1.1/marked.min.js"></script>
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.1.0/mermaid.min.js"></script>
11
+ <script id="page-info" src="/api/page-info.js?page=builder"></script>
12
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.min.js" id="topojson-lib"></script><style id="us-map-styles">#us-map-container{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;position:relative;overflow:hidden}#us-map-header{min-height:var(--header-min-height);padding:var(--header-padding-vertical) var(--header-padding-horizontal);line-height:var(--header-line-height);display:flex;align-items:center;justify-content:center;box-sizing:border-box;font-size:1.25rem;font-weight:600;color:var(--text-primary);width:100%;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0;position:relative}#us-map-svg-wrap{flex:1;width:100%;display:flex;align-items:center;justify-content:center;padding:16px;box-sizing:border-box;position:relative}#us-map-svg-wrap svg{width:100%;height:100%;position:absolute;top:0;left:0}.region-path{cursor:pointer;transition:filter .2s ease;stroke-linejoin:round}.region-path:hover{filter:brightness(1.25)}.region-state{fill:rgba(46,125,50,0.5);stroke:#1b5e20;stroke-width:1.2}.region-oregon{fill:rgba(230,81,0,0.45);stroke:#bf360c;stroke-width:1.5}.region-minnesota{fill:rgba(255,152,0,0.45);stroke:#e65100;stroke-width:1.5}.region-utah{fill:rgba(255,111,0,0.4);stroke:#bf360c;stroke-width:1.5}.region-newmexico{fill:rgba(239,108,0,0.45);stroke:#bf360c;stroke-width:1.5}.region-unorganized{fill:rgba(141,110,99,0.4);stroke:#4e342e;stroke-width:1.5}.region-indian{fill:rgba(121,85,72,0.45);stroke:#3e2723;stroke-width:1.5}.light-mode .region-state{fill:rgba(46,125,50,0.5);stroke:#1b5e20}.light-mode .region-oregon{fill:rgba(230,81,0,0.45);stroke:#bf360c}.light-mode .region-minnesota{fill:rgba(255,152,0,0.45);stroke:#e65100}.light-mode .region-utah{fill:rgba(255,111,0,0.4);stroke:#bf360c}.light-mode .region-newmexico{fill:rgba(239,108,0,0.45);stroke:#bf360c}.light-mode .region-unorganized{fill:rgba(141,110,99,0.4);stroke:#4e342e}.light-mode .region-indian{fill:rgba(121,85,72,0.45);stroke:#3e2723}#map-tooltip{position:absolute;pointer-events:none;background:var(--bg-tertiary);color:var(--text-primary);border:1px solid var(--border-color);border-radius:6px;padding:6px 12px;font-size:0.85rem;font-weight:500;box-shadow:0 2px 8px rgba(0,0,0,0.12);opacity:0;transition:opacity .15s ease;z-index:10;white-space:nowrap}#hist-legend{position:absolute;bottom:16px;left:16px;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;padding:10px 14px;font-size:0.75rem;color:var(--text-primary);z-index:5;box-shadow:0 2px 8px rgba(0,0,0,0.1)}#hist-legend .legend-item{display:flex;align-items:center;gap:8px;margin-bottom:4px}#hist-legend .legend-item:last-child{margin-bottom:0}#hist-legend .legend-swatch{width:14px;height:14px;border-radius:3px;flex-shrink:0}.trail-path{fill:none;stroke:#d32f2f;stroke-width:3;stroke-dasharray:8,4;stroke-linecap:round;stroke-linejoin:round;filter:drop-shadow(0 0 3px rgba(211,47,47,0.5))}.trail-path-glow{fill:none;stroke:rgba(211,47,47,0.25);stroke-width:8;stroke-linecap:round;stroke-linejoin:round}.trail-stop{cursor:pointer}.trail-stop circle.stop-outer{fill:rgba(211,47,47,0.2);stroke:#d32f2f;stroke-width:1.5;transition:r .2s ease, fill .2s ease}.trail-stop circle.stop-inner{fill:#d32f2f;transition:r .2s ease}.trail-stop:hover circle.stop-outer{fill:rgba(211,47,47,0.4)}.trail-stop.active circle.stop-outer{fill:rgba(211,47,47,0.5);stroke-width:2.5;r:9}.trail-stop.active circle.stop-inner{r:4}.trail-label{font-size:8px;font-weight:600;fill:#b71c1c;text-anchor:middle;pointer-events:none;paint-order:stroke;stroke:rgba(255,255,255,0.8);stroke-width:2.5px;stroke-linecap:round;stroke-linejoin:round}.light-mode .trail-path{stroke:#c62828}.light-mode .trail-label{fill:#b71c1c;stroke:rgba(255,255,255,0.85)}#trail-detail-card{position:absolute;top:16px;right:16px;width:340px;max-height:calc(100% - 40px);background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:12px;box-shadow:0 8px 32px rgba(0,0,0,0.15);z-index:20;opacity:0;transform:translateY(-10px) scale(0.97);pointer-events:none;transition:opacity .25s ease,transform .25s ease;overflow:hidden;display:flex;flex-direction:column}#trail-detail-card.visible{opacity:1;transform:translateY(0) scale(1);pointer-events:auto}#trail-detail-card .card-header{display:flex;align-items:center;justify-content:space-between;padding:12px 14px 8px;border-bottom:1px solid var(--border-color);background:linear-gradient(135deg,rgba(211,47,47,0.08),rgba(211,47,47,0.02));flex-shrink:0}#trail-detail-card .card-header h3{margin:0;font-size:0.95rem;font-weight:700;color:var(--text-primary);line-height:1.3}#trail-detail-card .card-header .card-close{background:none;border:none;font-size:1.3rem;cursor:pointer;color:var(--text-secondary);padding:0 2px;line-height:1;border-radius:4px;transition:background .15s ease}#trail-detail-card .card-header .card-close:hover{background:rgba(0,0,0,0.08)}#trail-detail-card .card-subtitle{font-size:0.72rem;color:var(--text-secondary);margin:2px 0 0;font-weight:500}#trail-detail-card .card-tabs{display:flex;border-bottom:1px solid var(--border-color);flex-shrink:0}#trail-detail-card .card-tab{flex:1;padding:8px 10px;font-size:0.78rem;font-weight:600;text-align:center;cursor:pointer;border:none;background:none;color:var(--text-secondary);transition:all .15s ease;border-bottom:2px solid transparent;position:relative}#trail-detail-card .card-tab:hover{color:var(--text-primary);background:rgba(0,0,0,0.03)}#trail-detail-card .card-tab.active{color:#c62828;border-bottom-color:#c62828;background:rgba(211,47,47,0.05)}#trail-detail-card .card-tab-icon{margin-right:4px}#trail-detail-card .tab-content{flex:1;overflow:hidden;display:flex;flex-direction:column;min-height:0}#trail-detail-card .tab-pane{display:none;flex:1;overflow-y:auto;flex-direction:column;min-height:0}#trail-detail-card .tab-pane.active{display:flex}#trail-detail-card .facts-pane{padding:12px 14px;overflow-y:auto}#trail-detail-card .fact-row{display:flex;gap:8px;margin-bottom:8px;align-items:flex-start}#trail-detail-card .fact-icon{font-size:1rem;flex-shrink:0;width:22px;text-align:center;margin-top:1px}#trail-detail-card .fact-content{flex:1}#trail-detail-card .fact-label{font-size:0.65rem;text-transform:uppercase;letter-spacing:0.5px;color:var(--text-secondary);font-weight:600;margin-bottom:1px}#trail-detail-card .fact-value{font-size:0.8rem;color:var(--text-primary);line-height:1.4}#trail-detail-card .card-divider{height:1px;background:var(--border-color);margin:4px 0 8px}#trail-detail-card .card-description{font-size:0.78rem;color:var(--text-primary);line-height:1.5;margin-top:2px}#trail-detail-card .mile-marker{display:inline-flex;align-items:center;gap:4px;background:rgba(211,47,47,0.1);color:#c62828;font-size:0.68rem;font-weight:600;padding:2px 7px;border-radius:12px;margin-bottom:8px}.light-mode #trail-detail-card .mile-marker{background:rgba(211,47,47,0.1);color:#b71c1c}.chat-pane{display:flex;flex-direction:column;min-height:0;flex:1}.chat-pane .chat-messages-area{flex:1;overflow-y:auto;padding:10px 12px;display:flex;flex-direction:column;gap:8px;min-height:120px}.chat-pane .chat-welcome{text-align:center;padding:16px 8px;color:var(--text-secondary);font-size:0.78rem;line-height:1.5}.chat-pane .chat-welcome .welcome-emoji{font-size:1.8rem;margin-bottom:6px;display:block}.chat-pane .chat-welcome strong{color:var(--text-primary)}.chat-pane .chat-bubble{max-width:92%;padding:8px 11px;border-radius:12px;font-size:0.78rem;line-height:1.45;word-wrap:break-word;animation:bubbleIn .2s ease}.chat-pane .chat-bubble.user-bubble{align-self:flex-end;background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border-bottom-right-radius:4px}.chat-pane .chat-bubble.ai-bubble{align-self:flex-start;background:var(--bg-secondary);color:var(--text-primary);border:1px solid var(--border-color);border-bottom-left-radius:4px}.chat-pane .chat-bubble.ai-bubble.loading{opacity:0.7}.chat-pane .typing-dots{display:inline-flex;gap:3px;padding:2px 0}.chat-pane .typing-dots span{width:5px;height:5px;border-radius:50%;background:var(--text-secondary);animation:dotBounce .6s ease-in-out infinite}.chat-pane .typing-dots span:nth-child(2){animation-delay:.15s}.chat-pane .typing-dots span:nth-child(3){animation-delay:.3s}@keyframes dotBounce{0%,100%{transform:translateY(0)}50%{transform:translateY(-4px)}}@keyframes bubbleIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.chat-pane .chat-input-area{display:flex;gap:6px;padding:8px 10px;border-top:1px solid var(--border-color);background:var(--bg-tertiary);flex-shrink:0;border-radius:0 0 12px 12px}.chat-pane .chat-input-field{flex:1;border:1px solid var(--border-color);border-radius:18px;padding:7px 12px;font-size:0.78rem;background:var(--bg-primary);color:var(--text-primary);outline:none;transition:border-color .15s ease;font-family:inherit}.chat-pane .chat-input-field:focus{border-color:var(--accent-primary)}.chat-pane .chat-input-field::placeholder{color:var(--text-secondary);opacity:0.7}.chat-pane .chat-send-btn{width:32px;height:32px;border-radius:50%;border:none;background:linear-gradient(135deg,#d32f2f,#b71c1c);color:#fff;font-size:0.9rem;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:transform .1s ease,opacity .15s ease;flex-shrink:0}.chat-pane .chat-send-btn:hover{transform:scale(1.08)}.chat-pane .chat-send-btn:disabled{opacity:0.4;cursor:not-allowed;transform:none}.chat-pane .suggested-questions{display:flex;flex-wrap:wrap;gap:4px;padding:0 12px 8px}.chat-pane .suggested-q{font-size:0.68rem;padding:4px 8px;border-radius:12px;border:1px solid var(--border-color);background:var(--bg-primary);color:var(--text-secondary);cursor:pointer;transition:all .15s ease;white-space:nowrap}.chat-pane .suggested-q:hover{background:rgba(211,47,47,0.08);color:#c62828;border-color:rgba(211,47,47,0.3)}</style></head>
13
+ <body>
14
+ <div class="chat-panel" data-locked="true">
15
+ <div class="chat-header" data-locked="true">SynthOS</div>
16
+ <div class="chat-messages" id="chatMessages" data-locked="true">
17
+ <div class="chat-message" id="defaultGreeting"><p><strong>SynthOS:</strong> What can I create for you? Ask "what can you do?" or "how does this work?" to learn more. Remember to save often!</p></div>
18
+ <div class="chat-message"><p><strong>User:</strong> create a map of the united states</p></div><div class="chat-message"><p><strong>SynthOS:</strong> Here's an interactive map of the United States! Hover over any state to see its name. The map is built with D3.js and uses an Albers USA projection that includes Alaska and Hawaii.</p></div><div class="chat-message"><p><strong>User:</strong> now in a second layer can you overlay that with a map of the usa circa 1850s</p></div><div class="chat-message"><p><strong>SynthOS:</strong> I've overlaid the modern map with an 1850s historical layer! The overlay shows the political divisions of the United States around 1850 (after the Compromise of 1850):</p><ul><li><strong style="color:#2e7d32;">Green</strong> — States admitted to the Union by 1850</li><li><strong style="color:#e65100;">Orange</strong> — Organized territories</li><li><strong style="color:#8d6e63;">Brown</strong> — Unorganized territory</li></ul><p>You can toggle the historical overlay on and off with the button in the top-right corner. The modern state boundaries show through underneath for reference.</p></div><div class="chat-message"><p><strong>User:</strong> remove the current state boundaries and draw new boundaries for how the territories were organized</p></div><div class="chat-message"><p><strong>SynthOS:</strong> I've replaced the modern state boundaries with historically-grouped 1850s territorial boundaries. The map now shows the approximate political divisions of the United States circa 1850, with internal state lines dissolved within each territory:</p><ul><li><strong style="color:#2e7d32;">Green</strong> — Individual states (31 states admitted by 1850)</li><li><strong style="color:#e65100;">Orange</strong> — Oregon Territory, Minnesota Territory, Utah Territory, New Mexico Territory</li><li><strong style="color:#8d6e63;">Brown</strong> — Unorganized Territory &amp; Indian Territory</li></ul><p>Note: Since this uses modern state shapes as approximations, the boundaries aren't perfectly historically accurate (e.g., territories that split modern states), but it gives a good sense of how the country was organized. Hover over any region to see its 1850s designation.</p></div><div class="chat-message"><p><strong>User:</strong> now overlay that with the oregon trail</p></div><div class="chat-message"><p><strong>SynthOS:</strong> I've overlaid the Oregon Trail on the 1850s map! The trail is shown as a <strong style="color:#d32f2f;">red dashed line</strong> running from <strong>Independence, Missouri</strong> to <strong>Oregon City, Oregon</strong> — roughly 2,000 miles.</p><p>Key stops along the trail are marked with dots and labels:</p><ul><li><strong>Independence, MO</strong> — the main jumping-off point</li><li><strong>Fort Kearny</strong> — first major military outpost</li><li><strong>Chimney Rock</strong> — famous landmark</li><li><strong>Fort Laramie</strong> — key resupply point</li><li><strong>Independence Rock</strong> — "Register of the Desert"</li><li><strong>South Pass</strong> — crossing the Continental Divide</li><li><strong>Fort Bridger</strong> — trading post</li><li><strong>Fort Hall</strong> — Hudson's Bay Company post</li><li><strong>Fort Boise</strong> — another HBC post</li><li><strong>The Dalles</strong> — end of the overland route</li><li><strong>Oregon City</strong> — final destination</li></ul><p>Hover over any stop for its name. The trail follows the approximate historical route through the Platte River valley, over the Rockies at South Pass, and along the Snake River into Oregon.</p></div><div class="chat-message"><p><strong>User:</strong> create a detail card that tells me facts about key stops along the way</p></div><div class="chat-message"><p><strong>SynthOS:</strong> I've added an interactive detail card! Click on any trail stop marker on the map to see historical facts about that location, including when it was established, its elevation, who used it, and why it mattered to Oregon Trail emigrants. Click the × button or click elsewhere to dismiss the card.</p></div><div class="chat-message"><p><strong>User:</strong> can you add an AI powered chat interface to that card that lets me ask questions relative to that location and the journey as a whole. the target audience should be 8-12 year olds. track the last 10 turns for follow up questions. reorganize the card if you need to</p></div><div class="chat-message"><p><strong>SynthOS:</strong> I've added an AI-powered chat to the detail card! Here's what's new:</p><ul><li><strong>Two tabs</strong> — "📋 Facts" shows the historical info, and "💬 Ask Me!" opens the chat</li><li><strong>Meet Dusty 🤠</strong> — your friendly trail guide who answers questions in fun, kid-friendly language (perfect for ages 8–12)</li><li><strong>Suggested questions</strong> — each stop has starter questions to tap, or kids can type their own</li><li><strong>Conversation memory</strong> — Dusty remembers the last 10 exchanges so kids can ask follow-up questions</li><li><strong>Per-stop context</strong> — switching to a new stop resets the chat with fresh context about that location</li></ul><p>Click any trail stop, then hit the "Ask Me!" tab to start chatting with Dusty!</p></div></div>
19
+ <div class="link-group" data-locked="true">
20
+ <a href="#" id="saveLink" data-locked="true">Save</a>
21
+ <a href="/pages" id="pagesLink" data-locked="true">Pages</a>
22
+ <a href="#" id="resetLink" data-locked="true">Reset</a>
23
+ </div>
24
+ <form action="/" method="POST" id="chatForm" data-locked="true">
25
+ <input type="text" class="chat-input" id="chatInput" name="message" placeholder="Type a message..." data-locked="true">
26
+ <button type="submit" class="chat-submit" data-locked="true">Send</button>
27
+ </form>
28
+ </div>
29
+ <div class="viewer-panel" id="viewerPanel"><div id="us-map-container"><div id="us-map-header">United States — Circa 1850 — with Oregon Trail</div><div id="us-map-svg-wrap"><div id="hist-legend"><div style="font-weight:600;margin-bottom:6px;">Circa 1850</div><div class="legend-item"><div class="legend-swatch" style="background:rgba(46,125,50,0.6);border:1px solid #1b5e20;"></div><span>States (31)</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(230,81,0,0.55);border:1px solid #bf360c;"></div><span>Oregon Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(255,152,0,0.55);border:1px solid #e65100;"></div><span>Minnesota Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(255,111,0,0.5);border:1px solid #bf360c;"></div><span>Utah Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(239,108,0,0.55);border:1px solid #bf360c;"></div><span>New Mexico Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(141,110,99,0.5);border:1px solid #4e342e;"></div><span>Unorganized Territory</span></div><div class="legend-item"><div class="legend-swatch" style="background:rgba(121,85,72,0.55);border:1px solid #3e2723;"></div><span>Indian Territory</span></div><div style="border-top:1px solid var(--border-color);margin-top:6px;padding-top:6px;"></div><div class="legend-item"><div class="legend-swatch" style="background:#d32f2f;border:1px solid #b71c1c;border-radius:50%;"></div><span style="font-weight:600;">Oregon Trail</span></div></div><div id="trail-detail-card"><div class="card-header"><div><h3 id="card-title"></h3><div class="card-subtitle" id="card-subtitle"></div></div><button class="card-close" id="card-close-btn" aria-label="Close">×</button></div><div class="card-tabs"><button class="card-tab active" data-tab="facts"><span class="card-tab-icon">📋</span>Facts</button><button class="card-tab" data-tab="chat"><span class="card-tab-icon">💬</span>Ask Me!</button></div><div class="tab-content"><div class="tab-pane facts-pane active" id="facts-pane"><div id="card-body"></div></div><div class="tab-pane chat-pane" id="chat-pane"><div class="chat-messages-area" id="trail-chat-messages"><div class="chat-welcome"><span class="welcome-emoji">🤠</span>Howdy, explorer! <strong>Click a trail stop</strong> and ask me anything about life on the Oregon Trail!</div></div><div class="suggested-questions" id="trail-suggested-q"></div><div class="chat-input-area"><input type="text" class="chat-input-field" id="trail-chat-input" placeholder="Ask me anything..." autocomplete="off"><button class="chat-send-btn" id="trail-chat-send" aria-label="Send">➤</button></div></div></div></div></div><div id="map-tooltip"></div></div><div id="loadingOverlay" class="loading-overlay"><div class="spinner"></div></div></div>
30
+ <div id="instructions" style="display: none;" data-locked="true"></div>
31
+ <div id="thoughts" style="display: none;" data-locked="true">User wants an AI-powered chat interface inside the trail detail card so kids (8-12) can ask questions about each stop and the Oregon Trail journey. I need to:
32
+
33
+ 1. Reorganize the detail card to have tabs or sections - facts + chat
34
+ 2. Add a chat input and message area inside the card
35
+ 3. Use synthos.generate.completion() to power the AI responses
36
+ 4. Track last 10 conversation turns for context
37
+ 5. Style it kid-friendly
38
+ 6. The card needs to be taller/scrollable to accommodate both facts and chat
39
+
40
+ Plan:
41
+ - Restructure the card to have two tabs: "Facts" and "Ask Me!"
42
+ - The chat tab has a scrollable message area and input
43
+ - Store conversation history per stop (or globally with stop context)
44
+ - Use completion API with a kid-friendly system prompt
45
+ - Track last 10 turns in a JS array
46
+
47
+ I'll update the CSS, the card HTML structure, and the JS to handle all of this.</div>
48
+ <script id="idle-animation">function hideIdleAnimation(){const idleContainer=document.getElementById("idleAnimation");idleContainer&&(idleContainer.classList.add("hidden"),setTimeout(()=>{idleContainer.style.display="none"},1e3))}function showIdleAnimation(){const idleContainer=document.getElementById("idleAnimation");idleContainer&&(idleContainer.style.display="block",setTimeout(()=>{idleContainer.classList.remove("hidden")},10))}</script>
49
+ <button class="chat-toggle" aria-label="Toggle chat panel">
50
+ <span class="chat-toggle-dots">
51
+ <span class="chat-toggle-dot"></span>
52
+ <span class="chat-toggle-dot"></span>
53
+ <span class="chat-toggle-dot"></span>
54
+ </span>
55
+ </button>
56
+
57
+
58
+ <script id="us-map-script">(function(){var FIPS_TO_NAME={1:'Alabama',2:'Alaska',4:'Arizona',5:'Arkansas',6:'California',8:'Colorado',9:'Connecticut',10:'Delaware',11:'District of Columbia',12:'Florida',13:'Georgia',15:'Hawaii',16:'Idaho',17:'Illinois',18:'Indiana',19:'Iowa',20:'Kansas',21:'Kentucky',22:'Louisiana',23:'Maine',24:'Maryland',25:'Massachusetts',26:'Michigan',27:'Minnesota',28:'Mississippi',29:'Missouri',30:'Montana',31:'Nebraska',32:'Nevada',33:'New Hampshire',34:'New Jersey',35:'New Mexico',36:'New York',37:'North Carolina',38:'North Dakota',39:'Ohio',40:'Oklahoma',41:'Oregon',42:'Pennsylvania',44:'Rhode Island',45:'South Carolina',46:'South Dakota',47:'Tennessee',48:'Texas',49:'Utah',50:'Vermont',51:'Virginia',53:'Washington',54:'West Virginia',55:'Wisconsin',56:'Wyoming'};
59
+ var STATES_1850=[9,10,13,24,25,33,34,36,37,42,44,45,51,50,21,47,39,22,18,28,17,1,23,29,5,26,12,48,19,55,6,54,11];
60
+ var OREGON_TERR=[41,53,16];
61
+ var MINNESOTA_TERR=[27];
62
+ var UTAH_TERR=[49,32];
63
+ var NEWMEXICO_TERR=[35,4];
64
+ var UNORGANIZED=[31,20,38,46,56,30,8];
65
+ var INDIAN_TERR=[40];
66
+ var TERRITORY_GROUPS=[{fips:OREGON_TERR,name:'Oregon Territory',cls:'region-oregon'},{fips:MINNESOTA_TERR,name:'Minnesota Territory',cls:'region-minnesota'},{fips:UTAH_TERR,name:'Utah Territory',cls:'region-utah'},{fips:NEWMEXICO_TERR,name:'New Mexico Territory',cls:'region-newmexico'},{fips:UNORGANIZED,name:'Unorganized Territory',cls:'region-unorganized'},{fips:INDIAN_TERR,name:'Indian Territory',cls:'region-indian'}];
67
+ var TRAIL_POINTS=[[-94.41,39.09],[-95.20,39.05],[-95.68,39.05],[-96.50,39.20],[-97.30,39.50],[-98.00,40.00],[-99.00,40.65],[-99.80,40.85],[-100.77,41.12],[-101.80,41.40],[-103.00,41.70],[-103.70,41.83],[-104.52,42.21],[-105.50,42.50],[-106.31,42.87],[-107.13,42.49],[-108.00,42.40],[-108.80,42.33],[-109.50,42.00],[-110.39,41.32],[-110.90,42.00],[-111.60,42.66],[-112.43,42.92],[-113.50,42.70],[-114.46,42.56],[-115.30,43.00],[-116.20,43.62],[-117.00,44.05],[-117.80,44.80],[-118.50,45.30],[-119.50,45.55],[-120.50,45.60],[-121.18,45.60],[-121.80,45.50],[-122.61,45.36]];
68
+ var TRAIL_STOPS=[{coords:[-94.41,39.09],name:'Independence, MO',labelPos:'below'},{coords:[-99.00,40.65],name:'Fort Kearny',labelPos:'above'},{coords:[-103.00,41.70],name:'Chimney Rock',labelPos:'above'},{coords:[-104.52,42.21],name:'Fort Laramie',labelPos:'above'},{coords:[-107.13,42.49],name:'Independence Rock',labelPos:'above'},{coords:[-108.80,42.33],name:'South Pass',labelPos:'below'},{coords:[-110.39,41.32],name:'Fort Bridger',labelPos:'below'},{coords:[-112.43,42.92],name:'Fort Hall',labelPos:'above'},{coords:[-116.20,43.62],name:'Fort Boise',labelPos:'above'},{coords:[-121.18,45.60],name:'The Dalles',labelPos:'above'},{coords:[-122.61,45.36],name:'Oregon City',labelPos:'below'}];
69
+ var STOP_FACTS={'Independence, MO':{subtitle:'The Gateway to the West',established:'Settled 1827',elevation:'830 ft (253 m)',miles:'Mile 0 — Starting Point',territory:'State of Missouri',facts:[{icon:'🏪',label:'Role',value:'Primary "jumping-off" point for westward emigrants'},{icon:'📅',label:'Peak Years',value:'1843–1860, with up to 50,000 emigrants departing annually in peak years'},{icon:'🛒',label:'Supplies',value:'Emigrants stocked up on flour, bacon, coffee, sugar, and oxen — a typical family spent $600–$1,000 outfitting'},{icon:'👥',label:'Population',value:'About 3,000 residents by 1850, swelling each spring with emigrants'}],description:'Independence was the most popular starting point for the Oregon, California, and Santa Fe trails. Wagon trains typically departed between April and May to cross the mountains before winter snows.'},'Fort Kearny':{subtitle:'First Military Outpost on the Trail',established:'Est. 1848',elevation:'2,146 ft (654 m)',miles:'Mile 319 — ~2 weeks from Independence',territory:'Unorganized Territory (present-day Nebraska)',facts:[{icon:'⚔️',label:'Purpose',value:'U.S. Army post built to protect emigrants on the Oregon Trail'},{icon:'📬',label:'Services',value:'Post office, blacksmith shop, and supply store for travelers'},{icon:'🦬',label:'Wildlife',value:'Located in prime buffalo country — emigrants often saw herds of thousands'},{icon:'🪖',label:'Garrison',value:'Typically 200–400 soldiers stationed here'}],description:'Fort Kearny served as the first major rest stop and resupply point. It was also where the trail from St. Joseph merged with the Independence route. Soldiers here helped repair wagons and provided medical aid.'},'Chimney Rock':{subtitle:'The Most Famous Landmark on the Trail',established:'Named by fur traders, 1820s',elevation:'4,228 ft (1,289 m)',miles:'Mile 561 — ~5 weeks from Independence',territory:'Unorganized Territory (present-day Nebraska)',facts:[{icon:'🏔️',label:'Height',value:'Spire rises ~325 ft above the North Platte River valley'},{icon:'📖',label:'Mentions',value:'Referenced in more emigrant journals than any other landmark on the trail'},{icon:'⚡',label:'Erosion',value:'Was significantly taller in the 1840s — lightning and erosion have reduced it'},{icon:'🎯',label:'Significance',value:'Marked the transition from prairies to the rugged terrain of the West'}],description:'Chimney Rock was visible for days before emigrants reached it, serving as a beacon of progress. Many carved their names into its soft clay base. It told travelers they were about one-third of the way to Oregon.'},'Fort Laramie':{subtitle:'Crossroads of the Frontier',established:'Est. 1834 (fur trade); U.S. Army 1849',elevation:'4,519 ft (1,377 m)',miles:'Mile 650 — ~6 weeks from Independence',territory:'Unorganized Territory (present-day Wyoming)',facts:[{icon:'🏛️',label:'Origin',value:'Started as a fur trading post called Fort William, later Fort John'},{icon:'🤝',label:'Treaty',value:'Site of the 1851 Fort Laramie Treaty with Plains Indian nations'},{icon:'🔧',label:'Services',value:'Blacksmith, trading post, and the last reliable resupply before the Rockies'},{icon:'⚖️',label:'Lightening Loads',value:'Emigrants often abandoned heavy items here — the fort was surrounded by discarded goods'}],description:'Fort Laramie was the most important stop on the entire trail. Here emigrants rested, repaired wagons, traded worn-out oxen, and prepared for the difficult mountain crossings ahead. Many wrote letters home from here.'},'Independence Rock':{subtitle:'The Register of the Desert',established:'Named by fur trappers, ~1824',elevation:'5,900 ft (1,798 m)',miles:'Mile 838 — ~8 weeks from Independence',territory:'Unorganized Territory (present-day Wyoming)',facts:[{icon:'🪨',label:'Size',value:'Granite dome ~1,900 ft long, 700 ft wide, and 128 ft high'},{icon:'✍️',label:'Names',value:'Over 5,000 names were carved or painted on the rock by passing emigrants'},{icon:'📅',label:'Timing',value:'Emigrants aimed to reach it by July 4th (Independence Day) to stay on schedule'},{icon:'🎆',label:'Celebrations',value:'Many wagon trains held Fourth of July celebrations here'}],description:'This massive granite outcrop became the most famous "guest book" in American history. Reaching it by Independence Day meant you were on pace to cross the mountains before winter. Father De Smet called it the "Register of the Desert" in 1840.'},'South Pass':{subtitle:'Crossing the Continental Divide',established:'Discovered by Europeans ~1812; widely used from 1840s',elevation:'7,550 ft (2,301 m)',miles:'Mile 914 — ~9 weeks from Independence',territory:'Unorganized Territory (present-day Wyoming)',facts:[{icon:'⛰️',label:'Geography',value:'A broad, gentle 20-mile-wide saddle through the Rocky Mountains'},{icon:'💧',label:'Divide',value:'Waters east flow to the Atlantic; waters west flow to the Pacific'},{icon:'😮',label:'Surprise',value:'So gradual that many emigrants didn\'t realize they\'d crossed the Rockies'},{icon:'🗺️',label:'Importance',value:'The only feasible wagon route through the Rockies for 2,000 miles'}],description:'South Pass was the key to the entire Oregon Trail. Without this gentle crossing of the Continental Divide, wagon travel to Oregon would have been nearly impossible. Despite being at 7,550 feet, the pass was so wide and gradual that it felt like rolling prairie.'},'Fort Bridger':{subtitle:'Mountain Man\'s Trading Post',established:'Est. 1843 by Jim Bridger',elevation:'6,665 ft (2,031 m)',miles:'Mile 1,026 — ~10 weeks from Independence',territory:'Unorganized Territory (present-day Wyoming)',facts:[{icon:'🧔',label:'Founder',value:'Jim Bridger — legendary mountain man, fur trapper, and scout'},{icon:'🔀',label:'Junction',value:'Where the Oregon Trail and the Mormon Trail to Salt Lake diverged'},{icon:'🐎',label:'Trade',value:'Emigrants traded tired animals for fresh ones with local Shoshone people'},{icon:'🏚️',label:'Condition',value:'Many emigrants were disappointed — described it as a shabby collection of log huts'}],description:'Jim Bridger and Louis Vasquez established this trading post on Black\'s Fork of the Green River. Despite its rough appearance, it was a critical decision point where emigrants chose between routes. The Donner Party fatefully took the Hastings Cutoff from near here in 1846.'},'Fort Hall':{subtitle:'Hudson\'s Bay Company Outpost',established:'Est. 1834 by Nathaniel Wyeth',elevation:'4,400 ft (1,341 m)',miles:'Mile 1,217 — ~12 weeks from Independence',territory:'Oregon Territory (present-day Idaho)',facts:[{icon:'🇬🇧',label:'Ownership',value:'Sold to Britain\'s Hudson\'s Bay Company in 1837'},{icon:'⚠️',label:'Discouragement',value:'HBC agents often tried to discourage American emigrants from continuing to Oregon'},{icon:'🔀',label:'Split',value:'Where the California Trail branched south from the Oregon Trail'},{icon:'🛞',label:'Wagons',value:'Early emigrants were told to abandon wagons here — the first to take wagons through to Oregon did so in 1843'}],description:'Fort Hall sat at a strategic crossroads. The Hudson\'s Bay Company, wanting to keep Americans out of Oregon, often told emigrants the route ahead was impassable by wagon. Despite this, thousands pushed through. After 1843, it became the split point for California-bound gold seekers.'},'Fort Boise':{subtitle:'Snake River Oasis',established:'Est. 1834 by Hudson\'s Bay Company',elevation:'2,730 ft (832 m)',miles:'Mile 1,450 — ~14 weeks from Independence',territory:'Oregon Territory (present-day Idaho)',facts:[{icon:'🏊',label:'River Crossing',value:'Emigrants had to ford the dangerous Boise River and Snake River nearby'},{icon:'🌿',label:'Relief',value:'Green river valley was a welcome sight after the harsh Snake River Plain'},{icon:'💀',label:'Danger',value:'The Snake River crossings near here claimed many lives — swift current and hidden holes'},{icon:'🐟',label:'Food',value:'Emigrants could trade with local Shoshone for salmon and camas root'}],description:'After weeks of dusty, barren travel along the Snake River Plain, Fort Boise offered shade, fresh water, and a chance to trade. The river crossings in this area were among the most dangerous on the entire trail, with many drownings recorded in emigrant diaries.'},'The Dalles':{subtitle:'End of the Overland Road',established:'Methodist mission est. 1838',elevation:'200 ft (61 m)',miles:'Mile 1,880 — ~18 weeks from Independence',territory:'Oregon Territory (present-day Oregon)',facts:[{icon:'🌊',label:'Challenge',value:'The Columbia River Gorge ahead was too narrow and dangerous for wagons'},{icon:'🛶',label:'River Route',value:'Many emigrants built rafts or hired Native canoes to float to Oregon City'},{icon:'🛤️',label:'Barlow Road',value:'After 1846, Sam Barlow\'s toll road over Mt. Hood offered an alternative ($5 per wagon)'},{icon:'⛪',label:'Mission',value:'A Methodist mission here served as a waystation for weary travelers'}],description:'The Dalles marked the end of the overland trail and the beginning of the final, often harrowing leg of the journey. The Columbia River route through the gorge was treacherous — many lost everything on the rapids. The Barlow Road, though steep and muddy, became the preferred route after 1846.'},'Oregon City':{subtitle:'Journey\'s End — The Promised Land',established:'Founded 1829; incorporated 1844',elevation:'55 ft (17 m)',miles:'Mile 2,000 — ~5 months from Independence',territory:'Oregon Territory',facts:[{icon:'🏛️',label:'Capital',value:'First incorporated city west of the Rockies; capital of Oregon Territory 1848–1852'},{icon:'🌊',label:'Falls',value:'Built at Willamette Falls — the falls powered sawmills and flour mills'},{icon:'📜',label:'Land Claims',value:'Under the Donation Land Claim Act (1850), settlers could claim 320–640 acres free'},{icon:'👨‍👩‍👧‍👦',label:'Arrivals',value:'~53,000 emigrants reached Oregon by wagon between 1840–1860'}],description:'Oregon City was the official end of the Oregon Trail and the heart of the new American settlement in the Pacific Northwest. Exhausted emigrants arrived here after five months and 2,000 miles to claim their new lives. The fertile Willamette Valley beyond offered the rich farmland that had drawn them west.'}};
70
+
71
+ var SUGGESTED_QUESTIONS={'Independence, MO':['What did kids do on the trail?','What food did they bring?','How long was the trip?'],'Fort Kearny':['Did kids go to school?','Were there really buffalo?','What was a typical day like?'],'Chimney Rock':['How tall is it really?','Did people climb it?','What did it look like back then?'],'Fort Laramie':['What did they trade?','Who lived at the fort?','Was it dangerous here?'],'Independence Rock':['Why did people carve names?','Can you still see the names?','What games did kids play?'],'South Pass':['How cold was it up there?','Did wagons ever get stuck?','What animals lived here?'],'Fort Bridger':['Who was Jim Bridger?','What happened to the Donner Party?','What did they eat here?'],'Fort Hall':['Why did the British want Oregon?','How did they cross rivers?','Were there any kids on the trail?'],'Fort Boise':['How dangerous were river crossings?','What fish could they catch?','Did anyone get lost?'],'The Dalles':['What were the rapids like?','How much was the toll road?','Did anyone\'s raft sink?'],'Oregon City':['What happened when they arrived?','How big was the free land?','What did they build first?']};
72
+
73
+ var activeStop=null;
74
+ var chatHistory=[];
75
+ var currentStopName=null;
76
+
77
+ function showDetailCard(stopName){
78
+ var card=document.getElementById('trail-detail-card');
79
+ var facts=STOP_FACTS[stopName];
80
+ if(!facts||!card)return;
81
+ document.getElementById('card-title').textContent=stopName;
82
+ document.getElementById('card-subtitle').textContent=facts.subtitle;
83
+ var body=document.getElementById('card-body');
84
+ var html='';
85
+ html+='<span class="mile-marker">📍 '+facts.miles+'</span>';
86
+ html+='<div class="fact-row"><div class="fact-icon">🗓️</div><div class="fact-content"><div class="fact-label">Established</div><div class="fact-value">'+facts.established+'</div></div></div>';
87
+ html+='<div class="fact-row"><div class="fact-icon">📏</div><div class="fact-content"><div class="fact-label">Elevation</div><div class="fact-value">'+facts.elevation+'</div></div></div>';
88
+ html+='<div class="fact-row"><div class="fact-icon">🗺️</div><div class="fact-content"><div class="fact-label">Territory</div><div class="fact-value">'+facts.territory+'</div></div></div>';
89
+ html+='<div class="card-divider"></div>';
90
+ facts.facts.forEach(function(f){html+='<div class="fact-row"><div class="fact-icon">'+f.icon+'</div><div class="fact-content"><div class="fact-label">'+f.label+'</div><div class="fact-value">'+f.value+'</div></div></div>';});
91
+ html+='<div class="card-divider"></div>';
92
+ html+='<div class="card-description">'+facts.description+'</div>';
93
+ body.innerHTML=html;
94
+
95
+ if(currentStopName!==stopName){
96
+ currentStopName=stopName;
97
+ chatHistory=[];
98
+ var msgArea=document.getElementById('trail-chat-messages');
99
+ msgArea.innerHTML='<div class="chat-welcome"><span class="welcome-emoji">🤠</span>Howdy, explorer! You\'re at <strong>'+stopName+'</strong>! Ask me anything about this place or the Oregon Trail journey!</div>';
100
+ }
101
+ updateSuggestedQuestions(stopName);
102
+
103
+ activeStop=stopName;
104
+ card.classList.add('visible');
105
+ setActiveTab('facts');
106
+ d3.selectAll('.trail-stop').classed('active',false);
107
+ d3.selectAll('.trail-stop').each(function(){var el=d3.select(this);if(el.attr('data-stop-name')===stopName)el.classed('active',true);});
108
+ }
109
+
110
+ function hideDetailCard(){
111
+ var card=document.getElementById('trail-detail-card');
112
+ if(card)card.classList.remove('visible');
113
+ activeStop=null;
114
+ d3.selectAll('.trail-stop').classed('active',false);
115
+ }
116
+
117
+ function setActiveTab(tabName){
118
+ document.querySelectorAll('#trail-detail-card .card-tab').forEach(function(t){
119
+ t.classList.toggle('active',t.getAttribute('data-tab')===tabName);
120
+ });
121
+ document.querySelectorAll('#trail-detail-card .tab-pane').forEach(function(p){
122
+ var id=p.id;
123
+ if(tabName==='facts')p.classList.toggle('active',id==='facts-pane');
124
+ else p.classList.toggle('active',id==='chat-pane');
125
+ });
126
+ if(tabName==='chat'){
127
+ var input=document.getElementById('trail-chat-input');
128
+ if(input)setTimeout(function(){input.focus();},100);
129
+ scrollChatToBottom();
130
+ }
131
+ }
132
+
133
+ function updateSuggestedQuestions(stopName){
134
+ var container=document.getElementById('trail-suggested-q');
135
+ var questions=SUGGESTED_QUESTIONS[stopName]||['What was it like here?','Tell me something cool!','Was it dangerous?'];
136
+ container.innerHTML='';
137
+ questions.forEach(function(q){
138
+ var btn=document.createElement('button');
139
+ btn.className='suggested-q';
140
+ btn.textContent=q;
141
+ btn.addEventListener('click',function(){sendTrailChat(q);});
142
+ container.appendChild(btn);
143
+ });
144
+ }
145
+
146
+ function scrollChatToBottom(){
147
+ var area=document.getElementById('trail-chat-messages');
148
+ if(area)area.scrollTop=area.scrollHeight;
149
+ }
150
+
151
+ function addChatBubble(text,isUser){
152
+ var area=document.getElementById('trail-chat-messages');
153
+ var welcome=area.querySelector('.chat-welcome');
154
+ if(welcome)welcome.remove();
155
+ var bubble=document.createElement('div');
156
+ bubble.className='chat-bubble '+(isUser?'user-bubble':'ai-bubble');
157
+ bubble.textContent=text;
158
+ area.appendChild(bubble);
159
+ scrollChatToBottom();
160
+ return bubble;
161
+ }
162
+
163
+ function addLoadingBubble(){
164
+ var area=document.getElementById('trail-chat-messages');
165
+ var bubble=document.createElement('div');
166
+ bubble.className='chat-bubble ai-bubble loading';
167
+ bubble.id='trail-loading-bubble';
168
+ bubble.innerHTML='<div class="typing-dots"><span></span><span></span><span></span></div>';
169
+ area.appendChild(bubble);
170
+ scrollChatToBottom();
171
+ return bubble;
172
+ }
173
+
174
+ function removeLoadingBubble(){
175
+ var el=document.getElementById('trail-loading-bubble');
176
+ if(el)el.remove();
177
+ }
178
+
179
+ function buildPrompt(userQuestion){
180
+ var stopData=STOP_FACTS[currentStopName];
181
+ var contextParts=[];
182
+ contextParts.push('You are a friendly, enthusiastic trail guide named "Dusty" who teaches kids (ages 8-12) about the Oregon Trail and the 1850s American frontier.');
183
+ contextParts.push('You are currently at: '+currentStopName+' — '+stopData.subtitle);
184
+ contextParts.push('Key facts about this location:');
185
+ contextParts.push('- Established: '+stopData.established);
186
+ contextParts.push('- Elevation: '+stopData.elevation);
187
+ contextParts.push('- Position on trail: '+stopData.miles);
188
+ contextParts.push('- Territory: '+stopData.territory);
189
+ contextParts.push('- Description: '+stopData.description);
190
+ stopData.facts.forEach(function(f){contextParts.push('- '+f.label+': '+f.value);});
191
+ contextParts.push('');
192
+ contextParts.push('RULES:');
193
+ contextParts.push('- Use simple, fun language appropriate for 8-12 year olds');
194
+ contextParts.push('- Keep answers to 2-4 short paragraphs max');
195
+ contextParts.push('- Use vivid descriptions and comparisons kids can relate to');
196
+ contextParts.push('- Include fun facts when possible');
197
+ contextParts.push('- If you don\'t know something, say so honestly');
198
+ contextParts.push('- You can reference other stops on the Oregon Trail for context');
199
+ contextParts.push('- Use occasional emojis but don\'t overdo it');
200
+ contextParts.push('- Never break character — you are Dusty the trail guide');
201
+ contextParts.push('');
202
+
203
+ if(chatHistory.length>0){
204
+ contextParts.push('Recent conversation:');
205
+ var recentHistory=chatHistory.slice(-10);
206
+ recentHistory.forEach(function(turn){
207
+ contextParts.push((turn.role==='user'?'Kid: ':'Dusty: ')+turn.text);
208
+ });
209
+ contextParts.push('');
210
+ }
211
+
212
+ contextParts.push('Kid\'s question: '+userQuestion);
213
+ contextParts.push('');
214
+ contextParts.push('Respond as Dusty the trail guide:');
215
+ return contextParts.join('\n');
216
+ }
217
+
218
+ async function sendTrailChat(message){
219
+ if(!message||!message.trim()||!currentStopName)return;
220
+ var input=document.getElementById('trail-chat-input');
221
+ var sendBtn=document.getElementById('trail-chat-send');
222
+ message=message.trim();
223
+ input.value='';
224
+ sendBtn.disabled=true;
225
+
226
+ setActiveTab('chat');
227
+ addChatBubble(message,true);
228
+ chatHistory.push({role:'user',text:message});
229
+
230
+ addLoadingBubble();
231
+
232
+ try{
233
+ var prompt=buildPrompt(message);
234
+ var result=await synthos.generate.completion({prompt:prompt,temperature:0.7});
235
+ removeLoadingBubble();
236
+ var answer=result.answer||'Hmm, my trail map got a little dusty! Try asking me again. 🤠';
237
+ addChatBubble(answer,false);
238
+ chatHistory.push({role:'assistant',text:answer});
239
+ if(chatHistory.length>20)chatHistory=chatHistory.slice(-20);
240
+ }catch(err){
241
+ removeLoadingBubble();
242
+ addChatBubble('Whoops! My wagon wheel got stuck. 🛞 Try asking again in a moment!',false);
243
+ console.error('Trail chat error:',err);
244
+ }
245
+ sendBtn.disabled=false;
246
+ input.focus();
247
+ }
248
+
249
+ window._showTrailDetail=showDetailCard;
250
+ window._hideTrailDetail=hideDetailCard;
251
+
252
+ document.getElementById('card-close-btn').addEventListener('click',function(e){e.stopPropagation();hideDetailCard();});
253
+
254
+ document.querySelectorAll('#trail-detail-card .card-tab').forEach(function(tab){
255
+ tab.addEventListener('click',function(e){
256
+ e.stopPropagation();
257
+ setActiveTab(tab.getAttribute('data-tab'));
258
+ });
259
+ });
260
+
261
+ document.getElementById('trail-chat-send').addEventListener('click',function(e){
262
+ e.stopPropagation();
263
+ var input=document.getElementById('trail-chat-input');
264
+ sendTrailChat(input.value);
265
+ });
266
+
267
+ document.getElementById('trail-chat-input').addEventListener('keydown',function(e){
268
+ if(e.key==='Enter'){e.preventDefault();e.stopPropagation();sendTrailChat(this.value);}
269
+ });
270
+
271
+ document.getElementById('trail-chat-input').addEventListener('click',function(e){e.stopPropagation();});
272
+
273
+ function init(){
274
+ var wrap=document.getElementById('us-map-svg-wrap');
275
+ if(!wrap)return;
276
+ var tooltip=document.getElementById('map-tooltip');
277
+ wrap.querySelectorAll('svg').forEach(function(s){s.remove();});
278
+ var svg=d3.select(wrap).append('svg').attr('id','hist-map-svg').attr('viewBox','0 0 960 600').attr('preserveAspectRatio','xMidYMid meet').style('padding','16px').style('box-sizing','border-box');
279
+ var projection=d3.geoAlbersUsa().scale(1280).translate([480,300]);
280
+ var path=d3.geoPath().projection(projection);
281
+ d3.json('https://cdn.jsdelivr.net/npm/us-atlas@3/states-10m.json').then(function(us){
282
+ var statesGeo=us.objects.states;
283
+ var allGeometries=statesGeo.geometries;
284
+ function geomsForFips(fipsList){return allGeometries.filter(function(g){return fipsList.indexOf(+g.id)!==-1;});}
285
+ var stateGeoms=geomsForFips(STATES_1850);
286
+ stateGeoms.forEach(function(geom){
287
+ var feature=topojson.feature(us,geom);
288
+ svg.append('path').datum(feature).attr('class','region-path region-state').attr('d',path)
289
+ .on('mousemove',function(event){var name=FIPS_TO_NAME[+geom.id]||'Unknown';tooltip.textContent=name+' (State)';tooltip.style.opacity='1';var rect=wrap.getBoundingClientRect();var x=event.clientX-rect.left+12;var y=event.clientY-rect.top-30;if(x+180>rect.width)x=x-200;if(y<0)y=event.clientY-rect.top+18;tooltip.style.left=x+'px';tooltip.style.top=y+'px';}).on('mouseleave',function(){tooltip.style.opacity='0';}).on('click',function(){hideDetailCard();});
290
+ });
291
+ TERRITORY_GROUPS.forEach(function(group){
292
+ var geoms=geomsForFips(group.fips);
293
+ if(geoms.length===0)return;
294
+ var merged=topojson.merge(us,geoms);
295
+ svg.append('path').datum(merged).attr('class','region-path '+group.cls).attr('d',path)
296
+ .on('mousemove',function(event){tooltip.textContent=group.name;tooltip.style.opacity='1';var rect=wrap.getBoundingClientRect();var x=event.clientX-rect.left+12;var y=event.clientY-rect.top-30;if(x+180>rect.width)x=x-200;if(y<0)y=event.clientY-rect.top+18;tooltip.style.left=x+'px';tooltip.style.top=y+'px';}).on('mouseleave',function(){tooltip.style.opacity='0';}).on('click',function(){hideDetailCard();});
297
+ });
298
+ var stateGeoms2=geomsForFips(STATES_1850);
299
+ svg.append('path').datum(topojson.mesh(us,{type:'GeometryCollection',geometries:stateGeoms2},function(a,b){return a!==b&&STATES_1850.indexOf(+a.id)!==-1&&STATES_1850.indexOf(+b.id)!==-1;})).attr('fill','none').attr('stroke','rgba(27,94,32,0.4)').attr('stroke-width','0.5').attr('d',path).style('pointer-events','none');
300
+ var trailGroup=svg.append('g').attr('class','trail-group');
301
+ var projectedPoints=[];
302
+ TRAIL_POINTS.forEach(function(p){var pt=projection(p);if(pt)projectedPoints.push(pt);});
303
+ if(projectedPoints.length>1){
304
+ var lineGen=d3.line().x(function(d){return d[0];}).y(function(d){return d[1];}).curve(d3.curveCatmullRom.alpha(0.5));
305
+ trailGroup.append('path').attr('class','trail-path-glow').attr('d',lineGen(projectedPoints));
306
+ trailGroup.append('path').attr('class','trail-path').attr('d',lineGen(projectedPoints));
307
+ }
308
+ TRAIL_STOPS.forEach(function(stop){
309
+ var pt=projection(stop.coords);
310
+ if(!pt)return;
311
+ var g=trailGroup.append('g').attr('class','trail-stop').attr('transform','translate('+pt[0]+','+pt[1]+')').attr('data-stop-name',stop.name);
312
+ g.append('circle').attr('class','stop-outer').attr('r',6);
313
+ g.append('circle').attr('class','stop-inner').attr('r',2.5);
314
+ var labelY=stop.labelPos==='above'?-10:14;
315
+ g.append('text').attr('class','trail-label').attr('y',labelY).text(stop.name);
316
+ g.on('mousemove',function(event){tooltip.textContent='Oregon Trail: '+stop.name;tooltip.style.opacity='1';var rect=wrap.getBoundingClientRect();var x=event.clientX-rect.left+12;var y=event.clientY-rect.top-30;if(x+180>rect.width)x=x-200;if(y<0)y=event.clientY-rect.top+18;tooltip.style.left=x+'px';tooltip.style.top=y+'px';}).on('mouseleave',function(){tooltip.style.opacity='0';}).on('click',function(event){event.stopPropagation();showDetailCard(stop.name);});
317
+ });
318
+ }).catch(function(err){console.error('Failed to load US map data:',err);wrap.innerHTML='<p style="color:var(--text-secondary);text-align:center;">Failed to load map data. Please try again.</p>';});
319
+ }
320
+ if(typeof topojson!=='undefined'){init();}else{var check=setInterval(function(){if(typeof topojson!=='undefined'){clearInterval(check);init();}},100);setTimeout(function(){clearInterval(check);},10000);}
321
+ })();</script><script id="page-helpers" src="/api/page-helpers.js?v=2" data-locked="true"></script><script id="page-script" src="/api/page-script.js?v=2" data-locked="true"></script></body></html>
@@ -0,0 +1,12 @@
1
+ {
2
+ "title": "Oregon Trail",
3
+ "categories": [
4
+ "Educational"
5
+ ],
6
+ "pinned": false,
7
+ "showInAll": false,
8
+ "createdDate": "2026-02-16T13:52:41.911Z",
9
+ "lastModified": "2026-02-16T13:52:41.911Z",
10
+ "pageVersion": 2,
11
+ "mode": "locked"
12
+ }
@@ -0,0 +1 @@
1
+ { "title": "Sidebar Page", "categories": ["Starters"], "pinned": false, "showInAll": false, "pageVersion": 2, "mode": "unlocked" }
@@ -163,18 +163,7 @@
163
163
  color: #f093fb;
164
164
  }
165
165
  </style><style id="settings-modal-styles">
166
- .settings-overlay {
167
- position: fixed;
168
- top: 0; left: 0; right: 0; bottom: 0;
169
- background: rgba(0,0,0,0.6);
170
- z-index: 9999;
171
- display: none;
172
- align-items: center;
173
- justify-content: center;
174
- }
175
- .settings-overlay.active {
176
- display: flex;
177
- }
166
+ /* .settings-overlay extends .modal-overlay from theme CSS */
178
167
  .settings-modal {
179
168
  background: rgba(15, 15, 35, 0.95);
180
169
  backdrop-filter: blur(16px);
@@ -535,7 +524,7 @@
535
524
  <button id="openSettings">⚙️ Settings</button>
536
525
  </div>
537
526
  <div id="loadingOverlay" class="loading-overlay"><div class="spinner"></div></div>
538
- <div id="settingsOverlay" class="settings-overlay">
527
+ <div id="settingsOverlay" class="modal-overlay settings-overlay">
539
528
  <div class="settings-modal">
540
529
  <h2>⚙️ Explorer Settings</h2>
541
530
 
@@ -1845,24 +1834,27 @@ animate();
1845
1834
  // Always reload from server when opening to ensure fresh data
1846
1835
  await loadSettings();
1847
1836
  applySettingsToUI();
1848
- overlay.classList.add('active');
1837
+ overlay.classList.add('show');
1849
1838
  });
1850
1839
 
1851
1840
  cancelBtn.addEventListener('click', function() {
1852
- overlay.classList.remove('active');
1841
+ overlay.classList.remove('show');
1853
1842
  applySettingsToUI(); // revert UI to saved
1854
1843
  });
1855
1844
 
1845
+ var overlayMouseDownTarget = null;
1846
+ overlay.addEventListener('mousedown', function(e) { overlayMouseDownTarget = e.target; });
1856
1847
  overlay.addEventListener('click', function(e) {
1857
- if (e.target === overlay) {
1858
- overlay.classList.remove('active');
1848
+ if (e.target === overlay && overlayMouseDownTarget === overlay) {
1849
+ overlay.classList.remove('show');
1859
1850
  applySettingsToUI();
1860
1851
  }
1852
+ overlayMouseDownTarget = null;
1861
1853
  });
1862
1854
 
1863
1855
  saveBtn.addEventListener('click', async function() {
1864
1856
  await saveSettings();
1865
- overlay.classList.remove('active');
1857
+ overlay.classList.remove('show');
1866
1858
  });
1867
1859
 
1868
1860
  // Helper to build audience instruction for prompts
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "title": "Solar Explorer",
3
3
  "categories": [
4
- "Apps"
4
+ "Educational"
5
5
  ],
6
6
  "pinned": false,
7
- "showInAll": true,
7
+ "showInAll": false,
8
8
  "createdDate": "2026-02-15T11:12:27.724Z",
9
9
  "lastModified": "2026-02-15T11:12:27.724Z",
10
10
  "pageVersion": 2,
@@ -0,0 +1 @@
1
+ { "title": "Two-Panel Page", "categories": ["Starters"], "pinned": false, "showInAll": false, "pageVersion": 2, "mode": "unlocked" }