synthos 0.7.2 → 0.9.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 (380) hide show
  1. package/README.md +215 -65
  2. package/default-pages/application/page.html +42 -0
  3. package/default-pages/application/page.json +10 -0
  4. package/default-pages/elevenlabs_effects_studio/page.html +1363 -0
  5. package/default-pages/elevenlabs_effects_studio/page.json +11 -0
  6. package/default-pages/elevenlabs_voice_studio/page.html +801 -0
  7. package/default-pages/elevenlabs_voice_studio/page.json +11 -0
  8. package/default-pages/{json_tools.html → json_tools/page.html} +13 -11
  9. package/default-pages/json_tools/page.json +10 -0
  10. package/default-pages/my_notes/notes/a1b2c3d4-e5f6-7890-abcd-ef1234567890.json +5 -0
  11. package/default-pages/my_notes/page.html +132 -0
  12. package/default-pages/{my_notes.json → my_notes/page.json} +2 -2
  13. package/default-pages/neon_asteroids/files/Ambient_Space.mp3 +0 -0
  14. package/default-pages/neon_asteroids/files/Ambient_Space2.mp3 +0 -0
  15. package/default-pages/neon_asteroids/files/Ambient_Space3.mp3 +0 -0
  16. package/default-pages/neon_asteroids/files/Asteroid_Explosion.mp3 +0 -0
  17. package/default-pages/neon_asteroids/files/Hyperspace_Jump.mp3 +0 -0
  18. package/default-pages/neon_asteroids/files/Laser_Fire.mp3 +0 -0
  19. package/default-pages/neon_asteroids/files/Menu_Navigate.mp3 +0 -0
  20. package/default-pages/neon_asteroids/files/Power_Up_Collect.mp3 +0 -0
  21. package/default-pages/neon_asteroids/files/Saucer_Alert.mp3 +0 -0
  22. package/default-pages/neon_asteroids/files/Ship_Thrust.mp3 +0 -0
  23. package/default-pages/neon_asteroids/files/effects.json +74 -0
  24. package/default-pages/neon_asteroids/page.html +1822 -0
  25. package/default-pages/{neon_asteroids.json → neon_asteroids/page.json} +3 -3
  26. package/default-pages/oregon_trail/page.html +323 -0
  27. package/default-pages/oregon_trail/page.json +12 -0
  28. package/default-pages/retro_game_starter/page.html +1308 -0
  29. package/default-pages/retro_game_starter/page.json +12 -0
  30. package/default-pages/{sidebar_builder.html → sidebar_page/page.html} +12 -10
  31. package/default-pages/sidebar_page/page.json +10 -0
  32. package/default-pages/{solar_explorer.html → solar_explorer/page.html} +24 -29
  33. package/default-pages/{solar_explorer.json → solar_explorer/page.json} +4 -4
  34. package/default-pages/{solar_tutorial.html → solar_tutorial/page.html} +12 -10
  35. package/default-pages/solar_tutorial/page.json +10 -0
  36. package/default-pages/{two-panel_builder.html → two-panel_page/page.html} +13 -11
  37. package/default-pages/two-panel_page/page.json +10 -0
  38. package/default-pages/us_map/page.html +193 -0
  39. package/default-pages/us_map/page.json +12 -0
  40. package/default-pages/us_map_1850/page.html +326 -0
  41. package/default-pages/us_map_1850/page.json +12 -0
  42. package/default-pages/western_cities_1850/page.html +527 -0
  43. package/default-pages/western_cities_1850/page.json +12 -0
  44. package/default-themes/aurora-dawn.json +19 -0
  45. package/default-themes/aurora-dawn.v3.css +198 -0
  46. package/default-themes/aurora-dusk.json +19 -0
  47. package/default-themes/aurora-dusk.v3.css +200 -0
  48. package/default-themes/cosmos-dawn.json +19 -0
  49. package/default-themes/cosmos-dawn.v3.css +198 -0
  50. package/default-themes/cosmos-dusk.json +19 -0
  51. package/default-themes/cosmos-dusk.v3.css +200 -0
  52. package/default-themes/high-contrast-dark.json +19 -0
  53. package/default-themes/high-contrast-dark.v3.css +200 -0
  54. package/default-themes/high-contrast-light.json +19 -0
  55. package/default-themes/high-contrast-light.v3.css +198 -0
  56. package/default-themes/{nebula-dawn.css → nebula-dawn.v2.css} +134 -0
  57. package/default-themes/nebula-dawn.v3.css +199 -0
  58. package/default-themes/{nebula-dusk.css → nebula-dusk.v2.css} +128 -0
  59. package/default-themes/nebula-dusk.v3.css +201 -0
  60. package/default-themes/solar-flare-dawn.json +19 -0
  61. package/default-themes/solar-flare-dawn.v3.css +198 -0
  62. package/default-themes/solar-flare-dusk.json +19 -0
  63. package/default-themes/solar-flare-dusk.v3.css +200 -0
  64. package/dist/agents/a2a/a2aProvider.d.ts.map +1 -0
  65. package/dist/agents/a2a/a2aProvider.js +126 -0
  66. package/dist/agents/a2a/a2aProvider.js.map +1 -0
  67. package/dist/agents/discovery.d.ts.map +1 -0
  68. package/dist/agents/discovery.js +52 -0
  69. package/dist/agents/discovery.js.map +1 -0
  70. package/dist/agents/index.d.ts +7 -0
  71. package/dist/agents/index.d.ts.map +1 -0
  72. package/dist/agents/index.js +20 -0
  73. package/dist/agents/index.js.map +1 -0
  74. package/dist/agents/openclaw/gatewayManager.d.ts +117 -0
  75. package/dist/agents/openclaw/gatewayManager.d.ts.map +1 -0
  76. package/dist/agents/openclaw/gatewayManager.js +486 -0
  77. package/dist/agents/openclaw/gatewayManager.js.map +1 -0
  78. package/dist/agents/openclaw/openclawProvider.d.ts.map +1 -0
  79. package/dist/agents/openclaw/openclawProvider.js +237 -0
  80. package/dist/agents/openclaw/openclawProvider.js.map +1 -0
  81. package/dist/agents/openclaw/sshTunnelManager.d.ts +25 -0
  82. package/dist/agents/openclaw/sshTunnelManager.d.ts.map +1 -0
  83. package/dist/agents/openclaw/sshTunnelManager.js +359 -0
  84. package/dist/agents/openclaw/sshTunnelManager.js.map +1 -0
  85. package/dist/agents/types.d.ts.map +1 -0
  86. package/dist/agents/types.js +6 -0
  87. package/dist/agents/types.js.map +1 -0
  88. package/dist/builders/anthropic.d.ts +31 -0
  89. package/dist/builders/anthropic.d.ts.map +1 -0
  90. package/dist/builders/anthropic.js +227 -0
  91. package/dist/builders/anthropic.js.map +1 -0
  92. package/dist/builders/fireworksai.d.ts +9 -0
  93. package/dist/builders/fireworksai.d.ts.map +1 -0
  94. package/dist/builders/fireworksai.js +57 -0
  95. package/dist/builders/fireworksai.js.map +1 -0
  96. package/dist/builders/index.d.ts +13 -0
  97. package/dist/builders/index.d.ts.map +1 -0
  98. package/dist/builders/index.js +31 -0
  99. package/dist/builders/index.js.map +1 -0
  100. package/dist/builders/openai.d.ts +8 -0
  101. package/dist/builders/openai.d.ts.map +1 -0
  102. package/dist/builders/openai.js +87 -0
  103. package/dist/builders/openai.js.map +1 -0
  104. package/dist/builders/types.d.ts +54 -0
  105. package/dist/builders/types.d.ts.map +1 -0
  106. package/dist/builders/types.js +211 -0
  107. package/dist/builders/types.js.map +1 -0
  108. package/dist/connectors/index.d.ts.map +1 -1
  109. package/dist/connectors/index.js +3 -2
  110. package/dist/connectors/index.js.map +1 -1
  111. package/dist/connectors/registry.d.ts +2 -1
  112. package/dist/connectors/registry.d.ts.map +1 -1
  113. package/dist/connectors/registry.js +65 -96
  114. package/dist/connectors/registry.js.map +1 -1
  115. package/dist/connectors/types.d.ts.map +1 -1
  116. package/dist/customizer/Customizer.d.ts +57 -0
  117. package/dist/customizer/Customizer.d.ts.map +1 -0
  118. package/dist/customizer/Customizer.js +124 -0
  119. package/dist/customizer/Customizer.js.map +1 -0
  120. package/dist/customizer/index.d.ts.map +1 -0
  121. package/dist/customizer/index.js +9 -0
  122. package/dist/customizer/index.js.map +1 -0
  123. package/dist/files.d.ts +17 -0
  124. package/dist/files.d.ts.map +1 -1
  125. package/dist/files.js +75 -1
  126. package/dist/files.js.map +1 -1
  127. package/dist/index.d.ts.map +1 -1
  128. package/dist/index.js +1 -0
  129. package/dist/index.js.map +1 -1
  130. package/dist/init.d.ts +10 -6
  131. package/dist/init.d.ts.map +1 -1
  132. package/dist/init.js +97 -86
  133. package/dist/init.js.map +1 -1
  134. package/dist/migrations.d.ts.map +1 -1
  135. package/dist/migrations.js +142 -145
  136. package/dist/migrations.js.map +1 -1
  137. package/dist/models/anthropic.d.ts +24 -0
  138. package/dist/models/anthropic.d.ts.map +1 -0
  139. package/dist/models/anthropic.js +103 -0
  140. package/dist/models/anthropic.js.map +1 -0
  141. package/dist/models/chainOfThought.d.ts.map +1 -0
  142. package/dist/models/chainOfThought.js +45 -0
  143. package/dist/models/chainOfThought.js.map +1 -0
  144. package/dist/models/fireworksai.d.ts.map +1 -0
  145. package/dist/models/fireworksai.js +141 -0
  146. package/dist/models/fireworksai.js.map +1 -0
  147. package/dist/models/index.d.ts +7 -1
  148. package/dist/models/index.d.ts.map +1 -1
  149. package/dist/models/index.js +20 -1
  150. package/dist/models/index.js.map +1 -1
  151. package/dist/models/logCompletePrompt.d.ts.map +1 -0
  152. package/dist/models/logCompletePrompt.js +23 -0
  153. package/dist/models/logCompletePrompt.js.map +1 -0
  154. package/dist/models/openai.d.ts +24 -0
  155. package/dist/models/openai.d.ts.map +1 -0
  156. package/dist/models/openai.js +101 -0
  157. package/dist/models/openai.js.map +1 -0
  158. package/dist/models/providers.d.ts.map +1 -1
  159. package/dist/models/providers.js +12 -4
  160. package/dist/models/providers.js.map +1 -1
  161. package/dist/models/types.d.ts +53 -2
  162. package/dist/models/types.d.ts.map +1 -1
  163. package/dist/models/types.js +21 -0
  164. package/dist/models/types.js.map +1 -1
  165. package/dist/models/utils.d.ts.map +1 -0
  166. package/dist/models/utils.js +21 -0
  167. package/dist/models/utils.js.map +1 -0
  168. package/dist/pages.d.ts +30 -7
  169. package/dist/pages.d.ts.map +1 -1
  170. package/dist/pages.js +177 -55
  171. package/dist/pages.js.map +1 -1
  172. package/dist/scripts.d.ts.map +1 -1
  173. package/dist/scripts.js +4 -3
  174. package/dist/scripts.js.map +1 -1
  175. package/dist/service/createCompletePrompt.d.ts.map +1 -1
  176. package/dist/service/createCompletePrompt.js +9 -6
  177. package/dist/service/createCompletePrompt.js.map +1 -1
  178. package/dist/service/generateImage.d.ts.map +1 -1
  179. package/dist/service/generateImage.js +3 -3
  180. package/dist/service/generateImage.js.map +1 -1
  181. package/dist/service/server.d.ts.map +1 -1
  182. package/dist/service/server.js +39 -7
  183. package/dist/service/server.js.map +1 -1
  184. package/dist/service/transformPage.d.ts +47 -18
  185. package/dist/service/transformPage.d.ts.map +1 -1
  186. package/dist/service/transformPage.js +559 -270
  187. package/dist/service/transformPage.js.map +1 -1
  188. package/dist/service/useAgentRoutes.d.ts +5 -0
  189. package/dist/service/useAgentRoutes.d.ts.map +1 -0
  190. package/dist/service/useAgentRoutes.js +392 -0
  191. package/dist/service/useAgentRoutes.js.map +1 -0
  192. package/dist/service/useApiRoutes.d.ts.map +1 -1
  193. package/dist/service/useApiRoutes.js +380 -138
  194. package/dist/service/useApiRoutes.js.map +1 -1
  195. package/dist/service/useConnectorRoutes.d.ts.map +1 -1
  196. package/dist/service/useConnectorRoutes.js +20 -9
  197. package/dist/service/useConnectorRoutes.js.map +1 -1
  198. package/dist/service/useFileRoutes.d.ts +4 -0
  199. package/dist/service/useFileRoutes.d.ts.map +1 -0
  200. package/dist/service/useFileRoutes.js +122 -0
  201. package/dist/service/useFileRoutes.js.map +1 -0
  202. package/dist/service/usePageRoutes.d.ts.map +1 -1
  203. package/dist/service/usePageRoutes.js +660 -68
  204. package/dist/service/usePageRoutes.js.map +1 -1
  205. package/dist/service/useSharedDataRoutes.d.ts +4 -0
  206. package/dist/service/useSharedDataRoutes.d.ts.map +1 -0
  207. package/dist/service/useSharedDataRoutes.js +104 -0
  208. package/dist/service/useSharedDataRoutes.js.map +1 -0
  209. package/dist/service/useSharedFileRoutes.d.ts +4 -0
  210. package/dist/service/useSharedFileRoutes.d.ts.map +1 -0
  211. package/dist/service/useSharedFileRoutes.js +121 -0
  212. package/dist/service/useSharedFileRoutes.js.map +1 -0
  213. package/dist/settings.d.ts +3 -1
  214. package/dist/settings.d.ts.map +1 -1
  215. package/dist/settings.js +5 -8
  216. package/dist/settings.js.map +1 -1
  217. package/dist/synthos-cli.d.ts.map +1 -1
  218. package/dist/synthos-cli.js +4 -3
  219. package/dist/synthos-cli.js.map +1 -1
  220. package/dist/themes.d.ts +15 -0
  221. package/dist/themes.d.ts.map +1 -1
  222. package/dist/themes.js +106 -20
  223. package/dist/themes.js.map +1 -1
  224. package/migration-rules/v1-to-v2.md +193 -0
  225. package/migration-rules/v2-to-v3.md +481 -0
  226. package/package.json +15 -11
  227. package/required-pages/builder/page.html +43 -0
  228. package/required-pages/builder/page.json +10 -0
  229. package/required-pages/pages/page.html +924 -0
  230. package/required-pages/pages/page.json +10 -0
  231. package/required-pages/settings/page.html +1753 -0
  232. package/required-pages/settings/page.json +10 -0
  233. package/required-pages/synthos_apis/page.html +846 -0
  234. package/required-pages/synthos_apis/page.json +10 -0
  235. package/required-pages/{synthos_scripts.html → synthos_scripts/page.html} +13 -11
  236. package/required-pages/synthos_scripts/page.json +10 -0
  237. package/service-connectors/airtable/connector.json +27 -0
  238. package/service-connectors/alpha-vantage/connector.json +26 -0
  239. package/service-connectors/brave-search/connector.json +26 -0
  240. package/service-connectors/cloudinary/connector.json +27 -0
  241. package/service-connectors/deepl/connector.json +28 -0
  242. package/service-connectors/elevenlabs/connector.json +30 -0
  243. package/service-connectors/giphy/connector.json +27 -0
  244. package/service-connectors/github/connector.json +29 -0
  245. package/service-connectors/huggingface/connector.json +27 -0
  246. package/service-connectors/imgur/connector.json +29 -0
  247. package/service-connectors/instagram/connector.json +43 -0
  248. package/service-connectors/jira/connector.json +28 -0
  249. package/service-connectors/mapbox/connector.json +26 -0
  250. package/service-connectors/nasa/connector.json +27 -0
  251. package/service-connectors/newsapi/connector.json +27 -0
  252. package/service-connectors/notion/connector.json +28 -0
  253. package/service-connectors/open-exchange-rates/connector.json +27 -0
  254. package/service-connectors/openweathermap/connector.json +26 -0
  255. package/service-connectors/pexels/connector.json +27 -0
  256. package/service-connectors/resend/connector.json +29 -0
  257. package/service-connectors/rss2json/connector.json +27 -0
  258. package/service-connectors/sendgrid/connector.json +27 -0
  259. package/service-connectors/spoonacular/connector.json +28 -0
  260. package/service-connectors/stability-ai/connector.json +27 -0
  261. package/service-connectors/twilio/connector.json +28 -0
  262. package/service-connectors/unsplash/connector.json +27 -0
  263. package/service-connectors/wolfram-alpha/connector.json +26 -0
  264. package/service-connectors/youtube-data/connector.json +30 -0
  265. package/src/agents/a2a/a2aProvider.ts +110 -0
  266. package/src/agents/discovery.ts +74 -0
  267. package/src/agents/index.ts +6 -0
  268. package/src/agents/openclaw/gatewayManager.ts +570 -0
  269. package/src/agents/openclaw/openclawProvider.ts +259 -0
  270. package/src/agents/openclaw/sshTunnelManager.ts +393 -0
  271. package/src/agents/types.ts +82 -0
  272. package/src/builders/anthropic.ts +283 -0
  273. package/src/builders/fireworksai.ts +59 -0
  274. package/src/builders/index.ts +33 -0
  275. package/src/builders/openai.ts +89 -0
  276. package/src/builders/types.ts +261 -0
  277. package/src/connectors/index.ts +3 -1
  278. package/src/connectors/registry.ts +40 -96
  279. package/src/connectors/types.ts +25 -0
  280. package/src/customizer/Customizer.ts +151 -0
  281. package/src/customizer/index.ts +5 -0
  282. package/src/files.ts +71 -0
  283. package/src/index.ts +2 -1
  284. package/src/init.ts +138 -97
  285. package/src/migrations.ts +148 -145
  286. package/src/models/anthropic.ts +119 -0
  287. package/src/models/chainOfThought.ts +56 -0
  288. package/src/models/fireworksai.ts +143 -0
  289. package/src/models/index.ts +7 -1
  290. package/src/models/logCompletePrompt.ts +25 -0
  291. package/src/models/openai.ts +110 -0
  292. package/src/models/providers.ts +12 -3
  293. package/src/models/types.ts +97 -2
  294. package/src/models/utils.ts +16 -0
  295. package/src/pages.ts +176 -54
  296. package/src/scripts.ts +2 -2
  297. package/src/service/createCompletePrompt.ts +3 -1
  298. package/src/service/generateImage.ts +2 -2
  299. package/src/service/server.ts +39 -8
  300. package/src/service/transformPage.ts +605 -301
  301. package/src/service/useAgentRoutes.ts +428 -0
  302. package/src/service/useApiRoutes.ts +309 -45
  303. package/src/service/useConnectorRoutes.ts +21 -10
  304. package/src/service/useFileRoutes.ts +127 -0
  305. package/src/service/usePageRoutes.ts +736 -75
  306. package/src/service/useSharedDataRoutes.ts +106 -0
  307. package/src/service/useSharedFileRoutes.ts +126 -0
  308. package/src/settings.ts +8 -10
  309. package/src/synthos-cli.ts +4 -3
  310. package/src/themes.ts +103 -20
  311. package/static-files/favicon.svg +12 -0
  312. package/static-files/fluentlm-instructions.llmd +868 -0
  313. package/static-files/fluentlm-instructions.md +1595 -0
  314. package/static-files/fluentlm.css +4844 -0
  315. package/static-files/fluentlm.js +3602 -0
  316. package/static-files/fluentlm.min.css +1 -0
  317. package/static-files/fluentlm.min.js +1 -0
  318. package/static-files/helpers.v3.js +304 -0
  319. package/static-files/page.v3.js +1290 -0
  320. package/static-files/recommended-frameworks.llmd +81 -0
  321. package/static-files/recommended-frameworks.md +137 -0
  322. package/static-files/retro-game.js +877 -0
  323. package/static-files/shell.css +797 -0
  324. package/static-files/theme-dark.css +169 -0
  325. package/static-files/theme-light.css +169 -0
  326. package/tests/anthropic.spec.ts +84 -0
  327. package/tests/builders.spec.ts +139 -0
  328. package/tests/chainOfThought.spec.ts +108 -0
  329. package/tests/ensureScripts.spec.ts +82 -0
  330. package/tests/files.spec.ts +233 -0
  331. package/tests/fireworksai.spec.ts +92 -0
  332. package/tests/logCompletePrompt.spec.ts +74 -0
  333. package/tests/migrations.spec.ts +79 -1
  334. package/tests/openai.spec.ts +71 -0
  335. package/tests/pages.spec.ts +226 -1
  336. package/tests/providers.spec.ts +144 -0
  337. package/tests/scripts.spec.ts +209 -0
  338. package/tests/transformPage.spec.ts +456 -0
  339. package/tests/types.spec.ts +23 -0
  340. package/default-pages/app_builder.html +0 -40
  341. package/default-pages/app_builder.json +0 -1
  342. package/default-pages/json_tools.json +0 -1
  343. package/default-pages/my_notes.html +0 -33
  344. package/default-pages/neon_asteroids.html +0 -77
  345. package/default-pages/sidebar_builder.json +0 -1
  346. package/default-pages/solar_tutorial.json +0 -1
  347. package/default-pages/two-panel_builder.json +0 -1
  348. package/dist/connectors/index.d.ts +0 -3
  349. package/dist/connectors/types.d.ts +0 -61
  350. package/dist/index.d.ts +0 -7
  351. package/dist/migrations.d.ts +0 -11
  352. package/dist/models/providers.d.ts +0 -7
  353. package/dist/scripts.d.ts +0 -14
  354. package/dist/service/createCompletePrompt.d.ts +0 -5
  355. package/dist/service/debugLog.d.ts +0 -11
  356. package/dist/service/generateImage.d.ts +0 -32
  357. package/dist/service/index.d.ts +0 -8
  358. package/dist/service/modelInstructions.d.ts +0 -7
  359. package/dist/service/requiresSettings.d.ts +0 -3
  360. package/dist/service/server.d.ts +0 -4
  361. package/dist/service/useApiRoutes.d.ts +0 -4
  362. package/dist/service/useConnectorRoutes.d.ts +0 -4
  363. package/dist/service/useDataRoutes.d.ts +0 -4
  364. package/dist/service/usePageRoutes.d.ts +0 -5
  365. package/dist/synthos-cli.d.ts +0 -2
  366. package/images/home.png +0 -0
  367. package/images/page-management.png +0 -0
  368. package/images/settings.png +0 -0
  369. package/images/synthos-square.png +0 -0
  370. package/page-scripts/helpers-v2.js +0 -121
  371. package/page-scripts/page-v2.js +0 -615
  372. package/required-pages/builder.html +0 -74
  373. package/required-pages/builder.json +0 -1
  374. package/required-pages/pages.html +0 -196
  375. package/required-pages/pages.json +0 -1
  376. package/required-pages/settings.html +0 -841
  377. package/required-pages/settings.json +0 -1
  378. package/required-pages/synthos_apis.html +0 -272
  379. package/required-pages/synthos_apis.json +0 -1
  380. package/required-pages/synthos_scripts.json +0 -1
@@ -1,841 +0,0 @@
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 - Settings</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>.settings-title{font-size:22px;font-weight:700;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;background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;border-radius:12px 12px 0 0;width:100%;box-shadow:0 6px 25px var(--accent-glow);letter-spacing:2px;text-shadow:0 2px 10px rgba(0,0,0,.3)}.settings-container{display:flex;flex-direction:column;width:100%;flex-grow:1;background:rgba(15,15,35,.8);border-radius:0 0 12px 12px;border:1px solid rgba(138,43,226,.2);border-top:none;overflow:hidden}.accordion-section{display:flex;flex-direction:column;flex-shrink:0;overflow:hidden;border-bottom:1px solid var(--border-color)}.accordion-section:last-child{border-bottom:none}.accordion-section.active{flex-shrink:1;flex-grow:1;min-height:0}.accordion-header{display:flex;justify-content:space-between;align-items:center;width:100%;padding:16px 25px;background:none;border:none;color:var(--text-primary);font-size:15px;font-weight:600;cursor:pointer;transition:background .3s;letter-spacing:.5px;flex-shrink:0}.accordion-header:hover{background:rgba(138,43,226,.05)}.accordion-section.active .accordion-header{background:rgba(138,43,226,.08)}.accordion-chevron{font-size:11px;color:var(--text-secondary);transition:transform .3s}.accordion-section.active .accordion-chevron{transform:rotate(180deg)}.accordion-body{display:none;flex-direction:column;flex-grow:1;min-height:0;overflow:hidden}.accordion-section.active .accordion-body{display:flex}.accordion-content{display:flex;flex-direction:column;gap:15px;overflow-y:auto;padding:20px 25px;flex-grow:1}.button-row{display:flex;justify-content:flex-end;padding:15px 25px;border-top:1px solid var(--border-color);flex-shrink:0}.apply-btn{padding:12px 30px;border:none;border-radius:12px;font-size:15px;background:linear-gradient(135deg,var(--accent-primary) 0,var(--accent-secondary) 50%,var(--accent-tertiary) 100%);color:#fff;cursor:pointer;transition:.3s;font-weight:600;letter-spacing:1px;box-shadow:0 4px 20px rgba(102,126,234,.4)}.apply-btn:hover{transform:translateY(-2px);box-shadow:0 6px 25px rgba(102,126,234,.6)}.apply-btn:active{transform:translateY(0)}.form-group{display:flex;flex-direction:column;width:100%}.form-group label{display:block;margin-bottom:8px;color:var(--text-secondary);font-weight:600;font-size:14px}.form-group input,.form-group select,.form-group textarea{width:100%;padding:14px 18px;border-radius:12px;border:1px solid var(--border-color);background:rgba(15,15,35,.8);color:var(--text-primary);font-size:14px;transition:.3s;box-shadow:inset 0 2px 10px rgba(0,0,0,.3)}.form-group input:focus,.form-group select:focus,.form-group textarea:focus{outline:0;border-color:rgba(183,148,246,.6);box-shadow:inset 0 2px 10px rgba(0,0,0,.3),0 0 20px var(--accent-glow)}.form-group input::placeholder,.form-group textarea::placeholder{color:rgba(183,148,246,.5)}.form-group textarea{resize:vertical;min-height:100px}.form-group select{cursor:pointer;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23b794f6' d='M6 8L1 3h10z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 15px center}.form-group select option{background:var(--bg-tertiary);color:var(--text-primary);padding:10px}.info-text{color:rgba(183,148,246,.7);font-size:13px;line-height:1.6;text-align:center;padding:15px;background:rgba(15,15,35,.4);border-radius:10px;border:1px solid var(--border-color)}.info-text a{color:var(--accent-tertiary);text-decoration:none;transition:.3s;font-weight:500}.info-text a:hover{color:var(--text-secondary);text-shadow:0 0 10px var(--accent-glow)}.config-required-banner{padding:12px 20px;background:rgba(255,180,50,.12);border:1px solid rgba(255,180,50,.3);border-radius:10px;color:rgba(255,200,100,.9);font-size:13px;font-weight:500;text-align:center;line-height:1.5}.model-card{border:1px solid var(--border-color);border-radius:12px;padding:18px;display:flex;flex-direction:column;gap:12px;background:rgba(15,15,35,.3)}.model-card-title{font-size:14px;font-weight:700;color:var(--text-primary);letter-spacing:.5px;margin:0}.filter-bar{display:flex;align-items:center;gap:8px;flex-wrap:nowrap}.filter-btn{padding:6px 14px;border-radius:20px;border:1px solid var(--border-color);background:0 0;color:var(--text-secondary);font-size:13px;cursor:pointer;transition:.2s;white-space:nowrap;flex-shrink:0}.filter-btn:hover{border-color:var(--accent-primary);color:var(--accent-primary)}.filter-btn.active{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;border-color:transparent}.services-grid{display:grid;grid-template-columns:1fr;gap:12px}.service-card{border-radius:12px;border:1px solid var(--border-color);background:rgba(15,15,35,.5);padding:18px;transition:.3s}.service-card:hover{border-color:rgba(138,43,226,.3)}.service-card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}.service-card-name{font-size:15px;font-weight:600;color:var(--text-primary)}.service-card-category{font-size:11px;color:var(--text-secondary);background:rgba(138,43,226,.15);padding:2px 8px;border-radius:10px}.service-card-desc{font-size:13px;color:var(--text-secondary);line-height:1.5;margin-bottom:12px}.service-card-fields{display:flex;flex-direction:column;gap:8px}.service-card-fields input{width:100%;padding:10px 14px;border-radius:8px;border:1px solid var(--border-color);background:rgba(15,15,35,.8);color:var(--text-primary);font-size:13px;transition:.3s;box-shadow:inset 0 2px 8px rgba(0,0,0,.2)}.service-card-fields input:focus{outline:0;border-color:rgba(183,148,246,.6);box-shadow:inset 0 2px 8px rgba(0,0,0,.2),0 0 15px var(--accent-glow)}.service-card-fields input::placeholder{color:rgba(183,148,246,.5)}.toggle-switch{position:relative;width:44px;height:24px;flex-shrink:0}.toggle-switch input{opacity:0;width:0;height:0}.toggle-slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background:rgba(100,100,100,.4);border-radius:24px;transition:.3s}.toggle-slider:before{content:"";position:absolute;height:18px;width:18px;left:3px;bottom:3px;background:#fff;border-radius:50%;transition:.3s}.toggle-switch input:checked+.toggle-slider{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary))}.toggle-switch input:checked+.toggle-slider:before{transform:translateX(20px)}.light-mode .settings-container{background:rgba(255,255,255,.8)}.light-mode .accordion-header:hover{background:rgba(118,75,162,.05)}.light-mode .accordion-section.active .accordion-header{background:rgba(118,75,162,.06)}.light-mode .form-group input,.light-mode .form-group select,.light-mode .form-group textarea{background:rgba(255,255,255,.8);box-shadow:inset 0 2px 10px rgba(118,75,162,.05)}.light-mode .form-group input:focus,.light-mode .form-group select:focus,.light-mode .form-group textarea:focus{box-shadow:inset 0 2px 10px rgba(118,75,162,.05),0 0 20px var(--accent-glow)}.light-mode .form-group input::placeholder,.light-mode .form-group textarea::placeholder{color:rgba(107,79,138,.5)}.light-mode .info-text{color:rgba(107,79,138,.7);background:rgba(255,255,255,.4)}.light-mode .config-required-banner{background:rgba(200,150,50,.1);border-color:rgba(200,150,50,.3);color:rgba(160,120,30,.9)}.light-mode .model-card{background:rgba(255,255,255,.3)}.light-mode .service-card{background:rgba(255,255,255,.5)}.light-mode .service-card-fields input{background:rgba(255,255,255,.8);box-shadow:inset 0 2px 8px rgba(118,75,162,.05)}.light-mode .service-card-fields input:focus{box-shadow:inset 0 2px 8px rgba(118,75,162,.05),0 0 15px var(--accent-glow)}.light-mode .service-card-fields input::placeholder{color:rgba(107,79,138,.5)}.model-card.disabled{opacity:0.35;pointer-events:none;user-select:none;transition:opacity .3s}.wizard-hidden{display:none !important}.more-settings-link{display:inline-block;color:var(--accent-tertiary);font-size:13px;cursor:pointer;text-decoration:none;padding:4px 0;transition:.3s;user-select:none}.more-settings-link:hover{color:var(--text-secondary);text-shadow:0 0 10px var(--accent-glow)}.light-mode .model-card.disabled{opacity:0.35}.accordion-section.disabled .accordion-header{opacity:0.35;pointer-events:none;cursor:default}.apply-btn:disabled{opacity:0.35;pointer-events:none;cursor:default}.connector-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:12px}.connector-tile{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:20px 12px;border-radius:14px;border:1px solid var(--border-color);background:linear-gradient(160deg,rgba(30,30,60,.6),rgba(20,20,50,.8));cursor:pointer;transition:transform .2s,box-shadow .2s,border-color .2s;gap:8px}.connector-tile:hover{transform:translateY(-3px);box-shadow:0 6px 20px var(--accent-glow);border-color:var(--accent-primary)}.connector-tile.configured{background:linear-gradient(160deg,rgba(60,30,90,.6),rgba(40,20,70,.8));border-color:var(--accent-secondary);box-shadow:0 2px 12px var(--accent-glow)}.connector-tile-name{font-size:14px;font-weight:600;color:var(--text-primary)}.connector-category{font-size:11px;color:var(--text-secondary);background:rgba(138,43,226,.15);padding:2px 8px;border-radius:10px}.search-input{flex:1;min-width:100px;padding:6px 14px;border-radius:20px;border:1px solid var(--border-color);background:rgba(15,15,35,.8);color:var(--text-primary);font-size:13px;transition:.3s}.search-input:focus{outline:0;border-color:rgba(183,148,246,.6)}.search-input::placeholder{color:rgba(183,148,246,.5)}.filter-buttons-container{display:flex;gap:8px;flex-wrap:nowrap}.conn-modal-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.6);display:flex;align-items:center;justify-content:center;z-index:1000}.conn-modal-content{background:rgba(20,20,50,.95);border:1px solid var(--border-color);border-radius:16px;padding:28px;max-width:420px;width:90%;display:flex;flex-direction:column;gap:16px;box-shadow:0 8px 40px rgba(0,0,0,.5)}.conn-modal-title{font-size:18px;font-weight:700;color:var(--text-primary)}.conn-modal-desc{font-size:13px;color:var(--text-secondary);line-height:1.5}.conn-modal-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:8px}.conn-modal-btn{padding:12px 24px;border:none;border-radius:12px;font-size:14px;background:linear-gradient(135deg,var(--accent-primary) 0,var(--accent-secondary) 50%,var(--accent-tertiary) 100%);color:#fff;cursor:pointer;transition:.3s;font-weight:600;letter-spacing:.5px;box-shadow:0 4px 20px rgba(102,126,234,.4)}.conn-modal-btn:hover{transform:translateY(-2px);box-shadow:0 6px 25px rgba(102,126,234,.6)}.conn-modal-btn.cancel{background:rgba(100,100,100,.4);box-shadow:none}.conn-modal-btn.cancel:hover{background:rgba(100,100,100,.6);transform:translateY(-1px)}.conn-modal-btn.remove{background:rgba(180,50,50,.6);box-shadow:none}.conn-modal-btn.remove:hover{background:rgba(200,60,60,.8);transform:translateY(-1px)}.light-mode .connector-tile{background:linear-gradient(160deg,rgba(240,235,255,.8),rgba(250,248,255,.9))}.light-mode .connector-tile.configured{background:linear-gradient(160deg,rgba(220,200,255,.8),rgba(235,220,255,.9))}.light-mode .search-input{background:rgba(255,255,255,.8)}.light-mode .search-input::placeholder{color:rgba(107,79,138,.5)}.light-mode .conn-modal-content{background:rgba(250,248,255,.98)}</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
- </head>
12
- <body>
13
- <div class="chat-panel" data-locked="true">
14
- <div class="chat-header" data-locked="true">SynthOS</div>
15
- <div class="chat-messages" id="chatMessages" data-locked="true">
16
- <div class="chat-message" id="defaultGreeting">
17
- <p><strong>SynthOS:</strong> Configure your settings below. Use the accordion sections to navigate between General settings, Model configuration (Page Builder &amp; Chat Completion), and Additional Features.</p>
18
- <p>The <strong>Page Builder Model</strong> is used when building pages via chat. The <strong>Chat Model</strong> is used by pages that call <code>synthos.generate.completion()</code>.</p>
19
- </div>
20
- <div class="chat-message" id="firstRunGreeting" style="display:none;">
21
- <p><strong>SynthOS:</strong> Welcome to SynthOS! We're glad you're here.</p>
22
- <p>Before you can start building, we need to connect to an AI provider. Just pick your provider below, paste in your API key, and you'll be ready to go.</p>
23
- <p>You can always come back to this page later to change your theme or enable additional features.</p>
24
- </div>
25
- </div>
26
- <div class="link-group" data-locked="true">
27
- <a href="#" id="saveLink" data-locked="true">Save</a>
28
- <a href="/pages" id="pagesLink" data-locked="true">Pages</a>
29
- <a href="#" id="resetLink" data-locked="true">Reset</a>
30
- </div>
31
- <form action="/" method="POST" id="chatForm" data-locked="true">
32
- <input type="text" class="chat-input" id="chatInput" name="message" placeholder="Type a message..." data-locked="true">
33
- <button type="submit" class="chat-submit" data-locked="true">Send</button>
34
- </form>
35
- </div>
36
- <div class="viewer-panel" id="viewerPanel" style="justify-content: flex-start; align-items: stretch;">
37
- <div class="settings-title" style="max-width: none; border-radius: 12px 12px 0 0;">Settings</div>
38
- <div class="settings-container" style="max-width: none; border-radius: 0; max-height: none; flex-grow: 1;">
39
-
40
- <div class="accordion-section active" data-section="general">
41
- <button class="accordion-header">
42
- <span>General</span>
43
- <span class="accordion-chevron">&#9662;</span>
44
- </button>
45
- <div class="accordion-body">
46
- <div class="accordion-content">
47
- <div class="form-group">
48
- <label for="theme">Theme</label>
49
- <select id="theme">
50
- <option value="">Loading themes...</option>
51
- </select>
52
- </div>
53
- </div>
54
- <div class="button-row">
55
- <button class="apply-btn" data-apply="general">Apply</button>
56
- </div>
57
- </div>
58
- </div>
59
-
60
- <div class="accordion-section" data-section="models">
61
- <button class="accordion-header">
62
- <span>Page Building &amp; Chat</span>
63
- <span class="accordion-chevron">&#9662;</span>
64
- </button>
65
- <div class="accordion-body">
66
- <div class="accordion-content">
67
- <div id="configBanner" class="config-required-banner" style="display:none;">
68
- Model configuration is required before you can use SynthOS. Please fill in the Page Builder fields below and click Apply.
69
- </div>
70
-
71
- <div class="model-card" id="builderCard">
72
- <div class="model-card-title">Page Builder Model</div>
73
- <div class="form-group">
74
- <label for="provider-builder">Provider</label>
75
- <select id="provider-builder" required="">
76
- <option value="">Select a provider</option>
77
- </select>
78
- </div>
79
- <div class="form-group" id="fg-apikey-builder">
80
- <label for="serviceApiKey-builder">API Key</label>
81
- <input type="password" id="serviceApiKey-builder" placeholder="Enter your API Key" required="">
82
- <div id="providerInstructions" class="info-text" style="display:none;margin-top:8px;"></div>
83
- </div>
84
- <div class="form-group" id="fg-model-builder">
85
- <label for="model-builder">Model</label>
86
- <select id="model-builder" required="">
87
- <option value="">Select a model</option>
88
- </select>
89
- </div>
90
- <a id="moreSettingsLink" class="more-settings-link">&#9662; More settings</a>
91
- <div class="form-group" id="fg-maxTokens-builder">
92
- <label for="maxTokens-builder">Max Output Tokens</label>
93
- <input type="number" id="maxTokens-builder" placeholder="Enter max token count" required="">
94
- </div>
95
- <div class="form-group" id="fg-instructions-builder">
96
- <label for="instructions-builder">Additional Instructions</label>
97
- <textarea id="instructions-builder" placeholder="Enter any additional instructions"></textarea>
98
- </div>
99
- </div>
100
-
101
- <div class="model-card" id="chatCard">
102
- <div class="model-card-title">Chat Model</div>
103
- <div class="form-group" id="fg-provider-chat">
104
- <label for="provider-chat">Provider</label>
105
- <select id="provider-chat" required="">
106
- <option value="">Select a provider</option>
107
- </select>
108
- </div>
109
- <div class="form-group" id="fg-apikey-chat">
110
- <label for="serviceApiKey-chat">API Key</label>
111
- <input type="password" id="serviceApiKey-chat" placeholder="Enter your API Key" required="">
112
- </div>
113
- <div class="form-group" id="fg-model-chat">
114
- <label for="model-chat">Model</label>
115
- <select id="model-chat" required="">
116
- <option value="">Select a model</option>
117
- </select>
118
- </div>
119
- <a id="moreSettingsLinkChat" class="more-settings-link">&#9662; More settings</a>
120
- <div class="form-group" id="fg-maxTokens-chat">
121
- <label for="maxTokens-chat">Max Output Tokens</label>
122
- <input type="number" id="maxTokens-chat" placeholder="Enter max token count" required="">
123
- </div>
124
- <div class="form-group" id="fg-instructions-chat">
125
- <label for="instructions-chat">Additional Instructions</label>
126
- <textarea id="instructions-chat" placeholder="Enter any additional instructions"></textarea>
127
- </div>
128
- </div>
129
-
130
- </div>
131
- <div class="button-row">
132
- <button class="apply-btn" data-apply="models">Apply</button>
133
- </div>
134
- </div>
135
- </div>
136
-
137
- <div class="accordion-section" data-section="connectors">
138
- <button class="accordion-header">
139
- <span>Connectors</span>
140
- <span class="accordion-chevron">&#9662;</span>
141
- </button>
142
- <div class="accordion-body">
143
- <div class="accordion-content">
144
- <div class="filter-bar" id="connectorFilterBar">
145
- <div class="filter-buttons-container" id="connectorFilterButtons"></div>
146
- <input type="text" class="search-input" id="connectorSearch" placeholder="Search connectors...">
147
- </div>
148
- <div class="connector-grid" id="connectorGrid"></div>
149
- </div>
150
- </div>
151
- </div>
152
-
153
- </div>
154
- <div id="loadingOverlay" class="loading-overlay"><div class="spinner"></div></div>
155
- </div>
156
- <div class="conn-modal-overlay" id="connectorModal" style="display:none;">
157
- <div class="conn-modal-content">
158
- <div class="conn-modal-title" id="connectorModalTitle"></div>
159
- <div class="conn-modal-desc" id="connectorModalDesc"></div>
160
- <div id="connectorApiKeyGroup" class="form-group">
161
- <label for="connectorApiKey">API Key</label>
162
- <input type="password" id="connectorApiKey" placeholder="Enter your API key">
163
- </div>
164
- <div id="connectorOAuthGroup" style="display:none;">
165
- <div id="connectorOAuthStatus" style="margin-bottom:12px;font-size:13px;line-height:1.5;"></div>
166
- <div id="connectorOAuthModeToggle" style="display:flex;gap:8px;margin-bottom:12px;">
167
- <button class="filter-btn active" id="oauthModeManual">Manual Token</button>
168
- <button class="filter-btn" id="oauthModeApp">OAuth App</button>
169
- </div>
170
- <div id="connectorManualGroup">
171
- <div class="form-group" style="margin-bottom:10px;">
172
- <label for="oauth-field-accessToken">Access Token</label>
173
- <input type="password" id="oauth-field-accessToken" placeholder="Paste token from Graph API Explorer">
174
- </div>
175
- <div class="form-group" style="margin-bottom:10px;">
176
- <label for="oauth-field-userId">IG User ID <span style="color:var(--text-secondary);font-weight:400;">(optional)</span></label>
177
- <input type="text" id="oauth-field-userId" placeholder="Instagram Business Account ID">
178
- </div>
179
- </div>
180
- <div id="connectorAppGroup" style="display:none;">
181
- <div class="form-group" id="connectorOAuthFields"></div>
182
- <button class="conn-modal-btn" id="connectorConnectBtn" style="width:100%;margin-bottom:8px;">Connect with OAuth</button>
183
- </div>
184
- <button class="conn-modal-btn remove" id="connectorDisconnectBtn" style="width:100%;display:none;">Disconnect</button>
185
- </div>
186
- <div class="form-group" style="flex-direction:row;align-items:center;gap:12px;">
187
- <label for="connectorEnabled" style="margin-bottom:0;">Enabled</label>
188
- <label class="toggle-switch">
189
- <input type="checkbox" id="connectorEnabled">
190
- <span class="toggle-slider"></span>
191
- </label>
192
- </div>
193
- <div class="conn-modal-actions">
194
- <button class="conn-modal-btn" id="connectorSaveBtn">Save</button>
195
- <button class="conn-modal-btn cancel" id="connectorCancelBtn">Cancel</button>
196
- <button class="conn-modal-btn remove" id="connectorRemoveBtn">Remove</button>
197
- </div>
198
- </div>
199
- </div>
200
- <div id="instructionsHidden" style="display: none;" data-locked="true"></div>
201
- <div id="thoughts" style="display: none;" data-locked="true"></div>
202
- <script id="settings-logic">
203
- // --- Accordion logic ---
204
- function openSection(sectionName) {
205
- document.querySelectorAll('.accordion-section').forEach(function(s) {
206
- s.classList.remove('active');
207
- });
208
- var target = document.querySelector('.accordion-section[data-section="' + sectionName + '"]');
209
- if (target) target.classList.add('active');
210
- }
211
-
212
- document.querySelectorAll('.accordion-header').forEach(function(header) {
213
- header.addEventListener('click', function() {
214
- var section = header.closest('.accordion-section');
215
- if (section.classList.contains('disabled')) return;
216
- var sectionName = section.dataset.section;
217
- var isActive = section.classList.contains('active');
218
- document.querySelectorAll('.accordion-section').forEach(function(s) {
219
- s.classList.remove('active');
220
- });
221
- if (!isActive) {
222
- section.classList.add('active');
223
- // Update URL without reload
224
- var url = new URL(window.location);
225
- url.searchParams.set('tab', sectionName);
226
- history.replaceState(null, '', url);
227
- }
228
- });
229
- });
230
-
231
- // --- URL param: open requested tab ---
232
- var params = new URLSearchParams(window.location.search);
233
- var tabParam = params.get('tab');
234
- if (tabParam && ['general', 'models', 'connectors'].indexOf(tabParam) !== -1) {
235
- openSection(tabParam);
236
- }
237
-
238
- // --- Connectors logic ---
239
- var connectorList = [], activeConnectorCategory = 'All', currentConnectorId = null;
240
-
241
- function loadConnectors() {
242
- fetch('/api/connectors').then(function(r) { return r.json(); }).then(function(list) {
243
- connectorList = list;
244
- renderConnectorFilterBar();
245
- renderConnectorGrid();
246
- });
247
- }
248
-
249
- function renderConnectorFilterBar() {
250
- var container = document.getElementById('connectorFilterButtons');
251
- container.innerHTML = '';
252
- var cats = new Set();
253
- connectorList.forEach(function(c) { cats.add(c.category); });
254
- var sorted = Array.from(cats).sort();
255
- ['All'].concat(sorted).forEach(function(cat) {
256
- var btn = document.createElement('button');
257
- btn.className = 'filter-btn' + (activeConnectorCategory === cat ? ' active' : '');
258
- btn.textContent = cat;
259
- btn.addEventListener('click', function() {
260
- activeConnectorCategory = cat;
261
- renderConnectorFilterBar();
262
- renderConnectorGrid();
263
- });
264
- container.appendChild(btn);
265
- });
266
- }
267
-
268
- function renderConnectorGrid() {
269
- var grid = document.getElementById('connectorGrid');
270
- var searchTerm = (document.getElementById('connectorSearch').value || '').toLowerCase();
271
- grid.innerHTML = '';
272
- connectorList.filter(function(c) {
273
- if (activeConnectorCategory !== 'All' && c.category !== activeConnectorCategory) return false;
274
- if (searchTerm && c.name.toLowerCase().indexOf(searchTerm) === -1) return false;
275
- return true;
276
- }).forEach(function(c) {
277
- var tile = document.createElement('div');
278
- tile.className = 'connector-tile' + (c.configured ? ' configured' : '');
279
- tile.addEventListener('click', function() { openConnectorModal(c.id); });
280
-
281
- var name = document.createElement('div');
282
- name.className = 'connector-tile-name';
283
- name.textContent = c.name;
284
-
285
- var cat = document.createElement('div');
286
- cat.className = 'connector-category';
287
- cat.textContent = c.category;
288
-
289
- tile.appendChild(name);
290
- tile.appendChild(cat);
291
- grid.appendChild(tile);
292
- });
293
- }
294
-
295
- var currentConnectorDetail = null;
296
-
297
- function openConnectorModal(id) {
298
- currentConnectorId = id;
299
- fetch('/api/connectors/' + encodeURIComponent(id)).then(function(r) { return r.json(); }).then(function(detail) {
300
- currentConnectorDetail = detail;
301
- document.getElementById('connectorModalTitle').textContent = detail.name;
302
- document.getElementById('connectorModalDesc').textContent = detail.description;
303
- document.getElementById('connectorEnabled').checked = detail.enabled;
304
- document.getElementById('connectorRemoveBtn').style.display = (detail.configured || detail.hasKey) ? '' : 'none';
305
-
306
- var isOAuth = detail.authStrategy === 'oauth2';
307
- document.getElementById('connectorApiKeyGroup').style.display = isOAuth ? 'none' : '';
308
- document.getElementById('connectorOAuthGroup').style.display = isOAuth ? '' : 'none';
309
-
310
- if (isOAuth) {
311
- // Render OAuth App fields (App ID, App Secret)
312
- var fieldsContainer = document.getElementById('connectorOAuthFields');
313
- fieldsContainer.innerHTML = '';
314
- (detail.fields || []).forEach(function(f) {
315
- var wrap = document.createElement('div');
316
- wrap.className = 'form-group';
317
- wrap.style.marginBottom = '10px';
318
- var lbl = document.createElement('label');
319
- lbl.textContent = f.label;
320
- lbl.setAttribute('for', 'oauth-field-' + f.name);
321
- var inp = document.createElement('input');
322
- inp.type = f.type;
323
- inp.id = 'oauth-field-' + f.name;
324
- inp.placeholder = detail.hasKey ? '(saved — leave blank to keep)' : 'Enter ' + f.label;
325
- wrap.appendChild(lbl);
326
- wrap.appendChild(inp);
327
- fieldsContainer.appendChild(wrap);
328
- });
329
-
330
- // Reset manual token fields
331
- document.getElementById('oauth-field-accessToken').value = '';
332
- document.getElementById('oauth-field-accessToken').placeholder = detail.connected ? '(token saved — leave blank to keep)' : 'Paste token from Graph API Explorer';
333
- document.getElementById('oauth-field-userId').value = '';
334
- document.getElementById('oauth-field-userId').placeholder = detail.connected ? (detail.accountName || 'Instagram Business Account ID') : 'Instagram Business Account ID';
335
-
336
- // Default to manual mode
337
- setOAuthMode('manual');
338
-
339
- // Show connected status
340
- var statusEl = document.getElementById('connectorOAuthStatus');
341
- var disconnectBtn = document.getElementById('connectorDisconnectBtn');
342
- var modeToggle = document.getElementById('connectorOAuthModeToggle');
343
- if (detail.connected) {
344
- var displayName = detail.accountName || detail.name;
345
- statusEl.innerHTML = '<span style="color:var(--accent-tertiary);">Connected as <strong>' + displayName + '</strong></span>';
346
- statusEl.style.display = '';
347
- disconnectBtn.style.display = '';
348
- modeToggle.style.display = 'none';
349
- } else {
350
- statusEl.innerHTML = '';
351
- statusEl.style.display = 'none';
352
- disconnectBtn.style.display = 'none';
353
- modeToggle.style.display = 'flex';
354
- }
355
- } else {
356
- document.getElementById('connectorApiKey').value = '';
357
- document.getElementById('connectorApiKey').placeholder = detail.hasKey ? '(key saved — leave blank to keep)' : 'Enter your API key';
358
- }
359
-
360
- document.getElementById('connectorModal').style.display = 'flex';
361
- });
362
- }
363
-
364
- var currentOAuthMode = 'manual';
365
-
366
- function setOAuthMode(mode) {
367
- currentOAuthMode = mode;
368
- document.getElementById('connectorManualGroup').style.display = mode === 'manual' ? '' : 'none';
369
- document.getElementById('connectorAppGroup').style.display = mode === 'app' ? '' : 'none';
370
- document.getElementById('oauthModeManual').className = 'filter-btn' + (mode === 'manual' ? ' active' : '');
371
- document.getElementById('oauthModeApp').className = 'filter-btn' + (mode === 'app' ? ' active' : '');
372
- }
373
-
374
- document.getElementById('oauthModeManual').addEventListener('click', function() { setOAuthMode('manual'); });
375
- document.getElementById('oauthModeApp').addEventListener('click', function() { setOAuthMode('app'); });
376
-
377
- function closeConnectorModal() {
378
- document.getElementById('connectorModal').style.display = 'none';
379
- currentConnectorId = null;
380
- }
381
-
382
- document.getElementById('connectorCancelBtn').addEventListener('click', closeConnectorModal);
383
-
384
- document.getElementById('connectorModal').addEventListener('click', function(e) {
385
- if (e.target === this) closeConnectorModal();
386
- });
387
-
388
- document.getElementById('connectorSaveBtn').addEventListener('click', function() {
389
- if (!currentConnectorId) return;
390
- var isOAuth = currentConnectorDetail && currentConnectorDetail.authStrategy === 'oauth2';
391
- var body;
392
- if (isOAuth) {
393
- body = { enabled: document.getElementById('connectorEnabled').checked };
394
- if (currentOAuthMode === 'manual') {
395
- // Manual token entry
396
- var token = document.getElementById('oauth-field-accessToken').value;
397
- if (token) body.accessToken = token;
398
- var uid = document.getElementById('oauth-field-userId').value;
399
- if (uid) body.userId = uid;
400
- } else {
401
- // OAuth app credentials
402
- (currentConnectorDetail.fields || []).forEach(function(f) {
403
- var el = document.getElementById('oauth-field-' + f.name);
404
- if (el && el.value) body[f.name] = el.value;
405
- });
406
- }
407
- } else {
408
- body = {
409
- apiKey: document.getElementById('connectorApiKey').value,
410
- enabled: document.getElementById('connectorEnabled').checked
411
- };
412
- }
413
- fetch('/api/connectors/' + encodeURIComponent(currentConnectorId), {
414
- method: 'POST',
415
- headers: { 'Content-Type': 'application/json' },
416
- body: JSON.stringify(body)
417
- }).then(function(r) {
418
- if (r.ok) { closeConnectorModal(); loadConnectors(); }
419
- });
420
- });
421
-
422
- document.getElementById('connectorConnectBtn').addEventListener('click', function() {
423
- if (!currentConnectorId || !currentConnectorDetail) return;
424
- // Save credentials first, then redirect to authorize
425
- var body = { enabled: true };
426
- (currentConnectorDetail.fields || []).forEach(function(f) {
427
- var el = document.getElementById('oauth-field-' + f.name);
428
- if (el && el.value) body[f.name] = el.value;
429
- });
430
- fetch('/api/connectors/' + encodeURIComponent(currentConnectorId), {
431
- method: 'POST',
432
- headers: { 'Content-Type': 'application/json' },
433
- body: JSON.stringify(body)
434
- }).then(function(r) {
435
- if (r.ok) {
436
- window.location.href = '/api/connectors/' + encodeURIComponent(currentConnectorId) + '/authorize';
437
- }
438
- });
439
- });
440
-
441
- document.getElementById('connectorDisconnectBtn').addEventListener('click', function() {
442
- if (!currentConnectorId) return;
443
- fetch('/api/connectors/' + encodeURIComponent(currentConnectorId), { method: 'DELETE' }).then(function(r) {
444
- if (r.ok) { closeConnectorModal(); loadConnectors(); }
445
- });
446
- });
447
-
448
- document.getElementById('connectorRemoveBtn').addEventListener('click', function() {
449
- if (!currentConnectorId) return;
450
- fetch('/api/connectors/' + encodeURIComponent(currentConnectorId), { method: 'DELETE' }).then(function(r) {
451
- if (r.ok) { closeConnectorModal(); loadConnectors(); }
452
- });
453
- });
454
-
455
- document.getElementById('connectorSearch').addEventListener('input', renderConnectorGrid);
456
-
457
- // Detect OAuth success/error from URL params
458
- var connectedParam = params.get('connected');
459
- var errorParam = params.get('error');
460
- if (connectedParam) {
461
- // Open the connectors tab and show success
462
- openSection('connectors');
463
- setTimeout(function() { openConnectorModal(connectedParam); }, 500);
464
- // Clean URL
465
- var cleanUrl = new URL(window.location);
466
- cleanUrl.searchParams.delete('connected');
467
- history.replaceState(null, '', cleanUrl);
468
- }
469
- if (errorParam) {
470
- openSection('connectors');
471
- alert('OAuth error: ' + errorParam);
472
- var cleanUrl2 = new URL(window.location);
473
- cleanUrl2.searchParams.delete('error');
474
- history.replaceState(null, '', cleanUrl2);
475
- }
476
-
477
- // --- Save logic ---
478
- function saveAllSettings() {
479
- var builderApiKey = document.getElementById('serviceApiKey-builder').value;
480
- var builderModel = document.getElementById('model-builder').value;
481
- var builderMaxTokens = document.getElementById('maxTokens-builder').value;
482
- var builderProvider = document.getElementById('provider-builder').value;
483
-
484
- if (!builderProvider || !builderApiKey || !builderModel || !builderMaxTokens) {
485
- alert('Please fill in all required Page Builder fields (Provider, API Key, Model, Max Output Tokens).');
486
- openSection('models');
487
- return;
488
- }
489
-
490
- var models = [
491
- {
492
- use: 'builder',
493
- provider: builderProvider,
494
- configuration: {
495
- apiKey: builderApiKey,
496
- model: builderModel,
497
- maxTokens: builderMaxTokens
498
- },
499
- imageQuality: 'standard',
500
- instructions: document.getElementById('instructions-builder').value
501
- },
502
- {
503
- use: 'chat',
504
- provider: document.getElementById('provider-chat').value,
505
- configuration: {
506
- apiKey: document.getElementById('serviceApiKey-chat').value,
507
- model: document.getElementById('model-chat').value,
508
- maxTokens: document.getElementById('maxTokens-chat').value
509
- },
510
- imageQuality: 'standard',
511
- instructions: document.getElementById('instructions-chat').value
512
- }
513
- ];
514
-
515
- var body = {
516
- version: 2,
517
- theme: document.getElementById('theme').value,
518
- models: models,
519
- features: []
520
- };
521
-
522
- fetch('/api/settings', {
523
- method: 'POST',
524
- headers: { 'Content-Type': 'application/json' },
525
- body: JSON.stringify(body)
526
- }).then(function(response) {
527
- if (response.ok || response.redirected) {
528
- if (wizardActive) {
529
- window.location.href = '/builder?firstRun=true';
530
- } else {
531
- window.location.reload();
532
- }
533
- } else {
534
- alert('Failed to save settings. Please try again.');
535
- }
536
- }).catch(function(err) {
537
- console.error('Error saving settings:', err);
538
- alert('Failed to save settings. Please try again.');
539
- });
540
- }
541
-
542
- document.querySelectorAll('.apply-btn').forEach(function(btn) {
543
- btn.addEventListener('click', saveAllSettings);
544
- });
545
-
546
- // --- Provider helpers ---
547
- var providersData = [];
548
-
549
- function populateProviderDropdowns() {
550
- var providerOptions = providersData.map(function(p) {
551
- return '<option value="' + p.name + '">' + p.name + '</option>';
552
- }).join('');
553
- document.getElementById('provider-builder').innerHTML = providerOptions;
554
- document.getElementById('provider-chat').innerHTML = providerOptions;
555
- }
556
-
557
- function populateModelDropdown(use, providerName) {
558
- var provider = providersData.find(function(p) { return p.name === providerName; });
559
- if (!provider) return;
560
- var models = use === 'builder' ? provider.builderModels : provider.chatModels;
561
- var options = models.map(function(m) {
562
- return '<option value="' + m + '">' + m + '</option>';
563
- }).join('');
564
- document.getElementById('model-' + use).innerHTML = options;
565
- }
566
-
567
- document.getElementById('provider-builder').addEventListener('change', function() {
568
- populateModelDropdown('builder', this.value);
569
- });
570
- document.getElementById('provider-chat').addEventListener('change', function() {
571
- populateModelDropdown('chat', this.value);
572
- });
573
-
574
- // --- Provider signup instructions ---
575
- var providerInstructions = {
576
- 'Anthropic': 'Sign up at <a href="https://platform.claude.com" target="_blank">platform.claude.com</a>, then get your key at <a href="https://console.anthropic.com/settings/keys" target="_blank">console.anthropic.com/settings/keys</a>.',
577
- 'OpenAI': 'Sign up at <a href="https://auth.openai.com/create-account" target="_blank">auth.openai.com/create-account</a>, then get your key at <a href="https://platform.openai.com/api-keys" target="_blank">platform.openai.com/api-keys</a>.'
578
- };
579
-
580
- // --- "More settings" toggle state (works for all users) ---
581
- var builderAdvanced = false;
582
- var chatAdvanced = false;
583
-
584
- function applyAdvancedToggle() {
585
- document.getElementById('fg-maxTokens-builder').classList.toggle('wizard-hidden', !builderAdvanced);
586
- document.getElementById('fg-instructions-builder').classList.toggle('wizard-hidden', !builderAdvanced);
587
- document.getElementById('fg-maxTokens-chat').classList.toggle('wizard-hidden', !chatAdvanced);
588
- document.getElementById('fg-instructions-chat').classList.toggle('wizard-hidden', !chatAdvanced);
589
- }
590
-
591
- document.getElementById('moreSettingsLink').addEventListener('click', function(e) {
592
- e.preventDefault();
593
- builderAdvanced = !builderAdvanced;
594
- this.innerHTML = builderAdvanced ? '&#9652; Less settings' : '&#9662; More settings';
595
- applyAdvancedToggle();
596
- });
597
-
598
- document.getElementById('moreSettingsLinkChat').addEventListener('click', function(e) {
599
- e.preventDefault();
600
- chatAdvanced = !chatAdvanced;
601
- this.innerHTML = chatAdvanced ? '&#9652; Less settings' : '&#9662; More settings';
602
- applyAdvancedToggle();
603
- });
604
-
605
- // Hide advanced fields by default on load
606
- applyAdvancedToggle();
607
-
608
- // --- Wizard state ---
609
- var wizardActive = false;
610
- var builderStep = 0; // 0 = provider only, 1 = + API key, 2 = fully configured
611
-
612
- function updateWizardVisibility() {
613
- if (!wizardActive) return;
614
- var fgApiKey = document.getElementById('fg-apikey-builder');
615
- var fgModel = document.getElementById('fg-model-builder');
616
- var moreLinkBuilder = document.getElementById('moreSettingsLink');
617
- var chatCardEl = document.getElementById('chatCard');
618
- var moreLinkChat = document.getElementById('moreSettingsLinkChat');
619
-
620
- // Step 0: only provider visible
621
- fgApiKey.classList.toggle('wizard-hidden', builderStep < 1);
622
-
623
- // Model dropdown visible at step 2 (auto-selected)
624
- fgModel.classList.toggle('wizard-hidden', builderStep < 2);
625
-
626
- // Builder "More settings" hidden until step 2
627
- moreLinkBuilder.style.display = builderStep >= 2 ? 'inline-block' : 'none';
628
- if (builderStep < 2) {
629
- document.getElementById('fg-maxTokens-builder').classList.add('wizard-hidden');
630
- document.getElementById('fg-instructions-builder').classList.add('wizard-hidden');
631
- } else {
632
- // Let the toggle state drive visibility
633
- applyAdvancedToggle();
634
- }
635
-
636
- // Chat card: before step 2, show only title grayed out
637
- if (builderStep < 2) {
638
- ['fg-provider-chat', 'fg-apikey-chat', 'fg-model-chat', 'fg-maxTokens-chat', 'fg-instructions-chat'].forEach(function(id) {
639
- document.getElementById(id).classList.add('wizard-hidden');
640
- });
641
- moreLinkChat.style.display = 'none';
642
- chatCardEl.classList.add('disabled');
643
- } else {
644
- // Step 2: show chat card populated — provider, key, model visible; advanced behind toggle
645
- ['fg-provider-chat', 'fg-apikey-chat', 'fg-model-chat'].forEach(function(id) {
646
- document.getElementById(id).classList.remove('wizard-hidden');
647
- });
648
- moreLinkChat.style.display = 'inline-block';
649
- applyAdvancedToggle();
650
- chatCardEl.classList.remove('disabled');
651
- }
652
-
653
- updateApplyButton();
654
- }
655
-
656
- function updateApplyButton() {
657
- var btns = document.querySelectorAll('.apply-btn');
658
- if (!wizardActive) {
659
- btns.forEach(function(b) { b.disabled = false; });
660
- return;
661
- }
662
- var hasProvider = !!document.getElementById('provider-builder').value;
663
- var hasKey = !!document.getElementById('serviceApiKey-builder').value;
664
- var hasModel = !!document.getElementById('model-builder').value;
665
- var hasTokens = !!document.getElementById('maxTokens-builder').value;
666
- var ready = hasProvider && hasKey && hasModel && hasTokens;
667
- btns.forEach(function(b) { b.disabled = !ready; });
668
- }
669
-
670
- function autoPopulateChatCard() {
671
- var providerVal = document.getElementById('provider-builder').value;
672
- var apiKeyVal = document.getElementById('serviceApiKey-builder').value;
673
-
674
- document.getElementById('provider-chat').value = providerVal;
675
- populateModelDropdown('chat', providerVal);
676
-
677
- var chatModelSelect = document.getElementById('model-chat');
678
- if (chatModelSelect.options.length > 0) {
679
- chatModelSelect.selectedIndex = 0;
680
- }
681
-
682
- document.getElementById('serviceApiKey-chat').value = apiKeyVal;
683
- document.getElementById('maxTokens-chat').value = '32000';
684
- }
685
-
686
- // --- Wizard event listeners ---
687
- document.getElementById('provider-builder').addEventListener('change', function() {
688
- if (!wizardActive) return;
689
- var val = this.value;
690
- if (!val) return;
691
- if (builderStep < 1) builderStep = 1;
692
- var instrEl = document.getElementById('providerInstructions');
693
- if (providerInstructions[val]) {
694
- instrEl.innerHTML = providerInstructions[val];
695
- instrEl.style.display = 'block';
696
- } else {
697
- instrEl.style.display = 'none';
698
- }
699
- updateWizardVisibility();
700
- });
701
-
702
- document.getElementById('serviceApiKey-builder').addEventListener('input', function() {
703
- if (!wizardActive) return;
704
- if (this.value.length > 0 && builderStep < 2) {
705
- builderStep = 2;
706
- document.getElementById('providerInstructions').style.display = 'none';
707
- document.getElementById('maxTokens-builder').value = '32000';
708
- autoPopulateChatCard();
709
- updateWizardVisibility();
710
- }
711
- updateApplyButton();
712
- });
713
-
714
- // --- Load data ---
715
- var isConfigured = false;
716
-
717
- Promise.all([
718
- fetch('/api/settings').then(function(r) { return r.json(); }),
719
- fetch('/api/themes').then(function(r) { return r.json(); })
720
- ]).then(function(results) {
721
- var data = results[0], themes = results[1];
722
-
723
- // Store providers data
724
- providersData = data.providers || [];
725
-
726
- // Find builder and chat entries from models array
727
- var models = data.models || [];
728
- var builder = models.find(function(m) { return m.use === 'builder'; }) || {};
729
- var chat = models.find(function(m) { return m.use === 'chat'; }) || {};
730
-
731
- var builderConfig = builder.configuration || {};
732
- var chatConfig = chat.configuration || {};
733
- var builderApiKey = builderConfig.apiKey || '';
734
- var builderModel = builderConfig.model || '';
735
- var builderMaxTokens = builderConfig.maxTokens || '';
736
- isConfigured = builderApiKey && builderModel && builderMaxTokens;
737
-
738
- // Populate provider dropdowns
739
- populateProviderDropdowns();
740
-
741
- // Builder fields
742
- document.getElementById('provider-builder').value = builder.provider || 'Anthropic';
743
- populateModelDropdown('builder', builder.provider || 'Anthropic');
744
- document.getElementById('serviceApiKey-builder').value = builderApiKey;
745
- document.getElementById('model-builder').value = builderModel;
746
- document.getElementById('maxTokens-builder').value = builderMaxTokens;
747
- document.getElementById('instructions-builder').value = builder.instructions || '';
748
-
749
- // Chat fields
750
- document.getElementById('provider-chat').value = chat.provider || 'Anthropic';
751
- populateModelDropdown('chat', chat.provider || 'Anthropic');
752
- document.getElementById('serviceApiKey-chat').value = chatConfig.apiKey || '';
753
- document.getElementById('model-chat').value = chatConfig.model || '';
754
- document.getElementById('maxTokens-chat').value = chatConfig.maxTokens || '';
755
- document.getElementById('instructions-chat').value = chat.instructions || '';
756
-
757
- var currentTheme = data.theme || 'nebula-dusk';
758
- document.getElementById('theme').innerHTML = themes.map(function(t) {
759
- return '<option value="' + t + '">' + t + '</option>';
760
- }).join('');
761
- document.getElementById('theme').value = currentTheme;
762
-
763
- loadConnectors();
764
-
765
- var isFirstRun = params.get('firstRun') === '1';
766
-
767
- if (!isConfigured) {
768
- // Disable chat and navigation
769
- document.getElementById('chatInput').disabled = true;
770
- document.getElementById('chatInput').classList.add('disabled');
771
- var pagesLink = document.getElementById('pagesLink');
772
- pagesLink.style.opacity = '0.4';
773
- pagesLink.style.pointerEvents = 'none';
774
-
775
- // Show config required banner and open models section
776
- document.getElementById('configBanner').style.display = 'block';
777
- openSection('models');
778
- }
779
-
780
- if (isFirstRun) {
781
- // Activate wizard — always treat as unconfigured when firstRun=1
782
- wizardActive = true;
783
- builderStep = 0;
784
-
785
- // Disable chat and navigation (even if somehow configured)
786
- document.getElementById('chatInput').disabled = true;
787
- document.getElementById('chatInput').classList.add('disabled');
788
-
789
- // Disable Copy, Pages, and Reload links
790
- ['saveLink', 'pagesLink', 'resetLink'].forEach(function(id) {
791
- var el = document.getElementById(id);
792
- el.style.opacity = '0.4';
793
- el.style.pointerEvents = 'none';
794
- });
795
-
796
- // Show firstRun greeting, hide default
797
- document.getElementById('defaultGreeting').style.display = 'none';
798
- document.getElementById('firstRunGreeting').style.display = '';
799
-
800
- // Show banner and lock to models tab
801
- document.getElementById('configBanner').style.display = 'block';
802
- openSection('models');
803
-
804
- // Disable other accordion tabs
805
- document.querySelector('.accordion-section[data-section="general"]').classList.add('disabled');
806
- document.querySelector('.accordion-section[data-section="connectors"]').classList.add('disabled');
807
-
808
- // Clear any existing values so wizard starts fresh
809
- document.getElementById('provider-builder').value = '';
810
- document.getElementById('serviceApiKey-builder').value = '';
811
- document.getElementById('model-builder').innerHTML = '<option value="">Select a model</option>';
812
- document.getElementById('maxTokens-builder').value = '';
813
- document.getElementById('instructions-builder').value = '';
814
- document.getElementById('provider-chat').value = '';
815
- document.getElementById('serviceApiKey-chat').value = '';
816
- document.getElementById('model-chat').innerHTML = '<option value="">Select a model</option>';
817
- document.getElementById('maxTokens-chat').value = '';
818
- document.getElementById('instructions-chat').value = '';
819
-
820
- // Add placeholder option to builder provider dropdown
821
- var builderProviderSelect = document.getElementById('provider-builder');
822
- var placeholder = document.createElement('option');
823
- placeholder.value = '';
824
- placeholder.textContent = 'Select a provider';
825
- placeholder.disabled = true;
826
- placeholder.selected = true;
827
- builderProviderSelect.insertBefore(placeholder, builderProviderSelect.firstChild);
828
- builderProviderSelect.value = '';
829
-
830
- // Disable Apply buttons until configured
831
- document.querySelectorAll('.apply-btn').forEach(function(b) { b.disabled = true; });
832
-
833
- updateWizardVisibility();
834
- }
835
- }).catch(function(error) {
836
- console.error('Error fetching settings:', error);
837
- });
838
- </script>
839
- <script id="page-helpers" src="/api/page-helpers.js?v=2" data-locked="true"></script>
840
- <script id="page-script" src="/api/page-script.js?v=2" data-locked="true"></script>
841
- </body></html>