synthos 0.8.0 → 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 (359) hide show
  1. package/README.md +1 -1
  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.html → oregon_trail/page.html} +14 -12
  27. package/default-pages/{oregon_trail.json → oregon_trail/page.json} +2 -2
  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_page.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} +14 -11
  33. package/default-pages/{solar_explorer.json → solar_explorer/page.json} +2 -2
  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_page.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.html → us_map/page.html} +193 -192
  39. package/default-pages/{us_map.json → us_map/page.json} +12 -12
  40. package/default-pages/{us_map_1850.html → us_map_1850/page.html} +326 -325
  41. package/default-pages/{us_map_1850.json → us_map_1850/page.json} +12 -12
  42. package/default-pages/{western_cities_1850.html → western_cities_1850/page.html} +527 -526
  43. package/default-pages/{western_cities_1850.json → western_cities_1850/page.json} +12 -12
  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.v2.css +110 -0
  57. package/default-themes/nebula-dawn.v3.css +199 -0
  58. package/default-themes/nebula-dusk.v2.css +104 -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/index.d.ts +1 -1
  65. package/dist/agents/index.d.ts.map +1 -1
  66. package/dist/agents/index.js +2 -1
  67. package/dist/agents/index.js.map +1 -1
  68. package/dist/agents/openclaw/gatewayManager.d.ts +4 -0
  69. package/dist/agents/openclaw/gatewayManager.d.ts.map +1 -1
  70. package/dist/agents/openclaw/gatewayManager.js +27 -11
  71. package/dist/agents/openclaw/gatewayManager.js.map +1 -1
  72. package/dist/agents/openclaw/openclawProvider.d.ts.map +1 -1
  73. package/dist/agents/openclaw/openclawProvider.js +2 -4
  74. package/dist/agents/openclaw/openclawProvider.js.map +1 -1
  75. package/dist/agents/openclaw/sshTunnelManager.d.ts +2 -0
  76. package/dist/agents/openclaw/sshTunnelManager.d.ts.map +1 -1
  77. package/dist/agents/openclaw/sshTunnelManager.js +31 -12
  78. package/dist/agents/openclaw/sshTunnelManager.js.map +1 -1
  79. package/dist/builders/anthropic.d.ts +31 -0
  80. package/dist/builders/anthropic.d.ts.map +1 -0
  81. package/dist/builders/anthropic.js +227 -0
  82. package/dist/builders/anthropic.js.map +1 -0
  83. package/dist/builders/fireworksai.d.ts +9 -0
  84. package/dist/builders/fireworksai.d.ts.map +1 -0
  85. package/dist/builders/fireworksai.js +57 -0
  86. package/dist/builders/fireworksai.js.map +1 -0
  87. package/dist/builders/index.d.ts +13 -0
  88. package/dist/builders/index.d.ts.map +1 -0
  89. package/dist/builders/index.js +31 -0
  90. package/dist/builders/index.js.map +1 -0
  91. package/dist/builders/openai.d.ts +8 -0
  92. package/dist/builders/openai.d.ts.map +1 -0
  93. package/dist/builders/openai.js +87 -0
  94. package/dist/builders/openai.js.map +1 -0
  95. package/dist/builders/types.d.ts +54 -0
  96. package/dist/builders/types.d.ts.map +1 -0
  97. package/dist/builders/types.js +211 -0
  98. package/dist/builders/types.js.map +1 -0
  99. package/dist/connectors/index.d.ts.map +1 -1
  100. package/dist/connectors/index.js +3 -2
  101. package/dist/connectors/index.js.map +1 -1
  102. package/dist/connectors/registry.d.ts +2 -1
  103. package/dist/connectors/registry.d.ts.map +1 -1
  104. package/dist/connectors/registry.js +31 -8
  105. package/dist/connectors/registry.js.map +1 -1
  106. package/dist/customizer/Customizer.d.ts +57 -0
  107. package/dist/customizer/Customizer.d.ts.map +1 -0
  108. package/dist/customizer/Customizer.js +124 -0
  109. package/dist/customizer/Customizer.js.map +1 -0
  110. package/dist/customizer/index.d.ts.map +1 -0
  111. package/dist/customizer/index.js +9 -0
  112. package/dist/customizer/index.js.map +1 -0
  113. package/dist/files.d.ts +16 -0
  114. package/dist/files.d.ts.map +1 -1
  115. package/dist/files.js +60 -1
  116. package/dist/files.js.map +1 -1
  117. package/dist/index.d.ts.map +1 -1
  118. package/dist/index.js +1 -0
  119. package/dist/index.js.map +1 -1
  120. package/dist/init.d.ts +10 -6
  121. package/dist/init.d.ts.map +1 -1
  122. package/dist/init.js +96 -113
  123. package/dist/init.js.map +1 -1
  124. package/dist/migrations.d.ts.map +1 -1
  125. package/dist/migrations.js +23 -10
  126. package/dist/migrations.js.map +1 -1
  127. package/dist/models/anthropic.d.ts +4 -2
  128. package/dist/models/anthropic.d.ts.map +1 -1
  129. package/dist/models/anthropic.js +33 -6
  130. package/dist/models/anthropic.js.map +1 -1
  131. package/dist/models/fireworksai.d.ts.map +1 -1
  132. package/dist/models/fireworksai.js +9 -1
  133. package/dist/models/fireworksai.js.map +1 -1
  134. package/dist/models/index.d.ts +1 -1
  135. package/dist/models/index.d.ts.map +1 -1
  136. package/dist/models/index.js +2 -1
  137. package/dist/models/index.js.map +1 -1
  138. package/dist/models/openai.d.ts +1 -1
  139. package/dist/models/openai.d.ts.map +1 -1
  140. package/dist/models/openai.js +24 -3
  141. package/dist/models/openai.js.map +1 -1
  142. package/dist/models/types.d.ts +20 -1
  143. package/dist/models/types.d.ts.map +1 -1
  144. package/dist/models/types.js +6 -1
  145. package/dist/models/types.js.map +1 -1
  146. package/dist/pages.d.ts +30 -7
  147. package/dist/pages.d.ts.map +1 -1
  148. package/dist/pages.js +177 -55
  149. package/dist/pages.js.map +1 -1
  150. package/dist/service/server.d.ts.map +1 -1
  151. package/dist/service/server.js +37 -8
  152. package/dist/service/server.js.map +1 -1
  153. package/dist/service/transformPage.d.ts +47 -20
  154. package/dist/service/transformPage.d.ts.map +1 -1
  155. package/dist/service/transformPage.js +514 -293
  156. package/dist/service/transformPage.js.map +1 -1
  157. package/dist/service/useAgentRoutes.d.ts +2 -1
  158. package/dist/service/useAgentRoutes.d.ts.map +1 -1
  159. package/dist/service/useAgentRoutes.js +5 -2
  160. package/dist/service/useAgentRoutes.js.map +1 -1
  161. package/dist/service/useApiRoutes.d.ts.map +1 -1
  162. package/dist/service/useApiRoutes.js +237 -136
  163. package/dist/service/useApiRoutes.js.map +1 -1
  164. package/dist/service/useConnectorRoutes.js +6 -6
  165. package/dist/service/useConnectorRoutes.js.map +1 -1
  166. package/dist/service/useFileRoutes.d.ts +4 -0
  167. package/dist/service/useFileRoutes.d.ts.map +1 -0
  168. package/dist/service/useFileRoutes.js +122 -0
  169. package/dist/service/useFileRoutes.js.map +1 -0
  170. package/dist/service/usePageRoutes.d.ts.map +1 -1
  171. package/dist/service/usePageRoutes.js +648 -67
  172. package/dist/service/usePageRoutes.js.map +1 -1
  173. package/dist/service/useSharedDataRoutes.d.ts +4 -0
  174. package/dist/service/useSharedDataRoutes.d.ts.map +1 -0
  175. package/dist/service/useSharedDataRoutes.js +104 -0
  176. package/dist/service/useSharedDataRoutes.js.map +1 -0
  177. package/dist/service/useSharedFileRoutes.d.ts +4 -0
  178. package/dist/service/useSharedFileRoutes.d.ts.map +1 -0
  179. package/dist/service/useSharedFileRoutes.js +121 -0
  180. package/dist/service/useSharedFileRoutes.js.map +1 -0
  181. package/dist/settings.d.ts +1 -0
  182. package/dist/settings.d.ts.map +1 -1
  183. package/dist/settings.js +1 -0
  184. package/dist/settings.js.map +1 -1
  185. package/dist/synthos-cli.d.ts.map +1 -1
  186. package/dist/synthos-cli.js +4 -3
  187. package/dist/synthos-cli.js.map +1 -1
  188. package/dist/themes.d.ts +1 -0
  189. package/dist/themes.d.ts.map +1 -1
  190. package/dist/themes.js +28 -15
  191. package/dist/themes.js.map +1 -1
  192. package/migration-rules/v1-to-v2.md +193 -0
  193. package/migration-rules/v2-to-v3.md +481 -0
  194. package/package.json +11 -10
  195. package/required-pages/builder/page.html +43 -0
  196. package/required-pages/builder/page.json +10 -0
  197. package/required-pages/{pages.html → pages/page.html} +238 -233
  198. package/required-pages/pages/page.json +10 -0
  199. package/required-pages/{settings.html → settings/page.html} +389 -275
  200. package/required-pages/settings/page.json +10 -0
  201. package/required-pages/synthos_apis/page.html +846 -0
  202. package/required-pages/synthos_apis/page.json +10 -0
  203. package/required-pages/{synthos_scripts.html → synthos_scripts/page.html} +13 -11
  204. package/required-pages/synthos_scripts/page.json +10 -0
  205. package/src/agents/index.ts +1 -1
  206. package/src/agents/openclaw/gatewayManager.ts +22 -11
  207. package/src/agents/openclaw/openclawProvider.ts +2 -4
  208. package/src/agents/openclaw/sshTunnelManager.ts +19 -11
  209. package/src/builders/anthropic.ts +283 -0
  210. package/src/builders/fireworksai.ts +59 -0
  211. package/src/builders/index.ts +33 -0
  212. package/src/builders/openai.ts +89 -0
  213. package/src/builders/types.ts +261 -0
  214. package/src/connectors/index.ts +1 -1
  215. package/src/connectors/registry.ts +28 -8
  216. package/src/customizer/Customizer.ts +151 -0
  217. package/src/customizer/index.ts +5 -0
  218. package/src/files.ts +57 -0
  219. package/src/index.ts +2 -1
  220. package/src/init.ts +137 -123
  221. package/src/migrations.ts +30 -10
  222. package/src/models/anthropic.ts +40 -10
  223. package/src/models/fireworksai.ts +9 -2
  224. package/src/models/index.ts +1 -1
  225. package/src/models/openai.ts +26 -6
  226. package/src/models/types.ts +31 -1
  227. package/src/pages.ts +176 -54
  228. package/src/service/server.ts +36 -9
  229. package/src/service/transformPage.ts +557 -326
  230. package/src/service/useAgentRoutes.ts +7 -2
  231. package/src/service/useApiRoutes.ts +150 -41
  232. package/src/service/useConnectorRoutes.ts +7 -7
  233. package/src/service/useFileRoutes.ts +127 -0
  234. package/src/service/usePageRoutes.ts +720 -73
  235. package/src/service/useSharedDataRoutes.ts +106 -0
  236. package/src/service/useSharedFileRoutes.ts +126 -0
  237. package/src/settings.ts +2 -0
  238. package/src/synthos-cli.ts +4 -3
  239. package/src/themes.ts +25 -14
  240. package/static-files/favicon.svg +12 -0
  241. package/static-files/fluentlm-instructions.llmd +868 -0
  242. package/static-files/fluentlm-instructions.md +1595 -0
  243. package/static-files/fluentlm.css +4844 -0
  244. package/static-files/fluentlm.js +3602 -0
  245. package/static-files/fluentlm.min.css +1 -0
  246. package/static-files/fluentlm.min.js +1 -0
  247. package/{page-scripts/helpers-v2.js → static-files/helpers.v3.js} +82 -0
  248. package/static-files/page.v3.js +1290 -0
  249. package/static-files/recommended-frameworks.llmd +81 -0
  250. package/static-files/recommended-frameworks.md +137 -0
  251. package/static-files/retro-game.js +877 -0
  252. package/static-files/shell.css +797 -0
  253. package/static-files/theme-dark.css +169 -0
  254. package/static-files/theme-light.css +169 -0
  255. package/tests/builders.spec.ts +139 -0
  256. package/tests/pages.spec.ts +8 -8
  257. package/tests/transformPage.spec.ts +299 -360
  258. package/default-pages/application.html +0 -40
  259. package/default-pages/application.json +0 -1
  260. package/default-pages/json_tools.json +0 -1
  261. package/default-pages/my_notes.html +0 -33
  262. package/default-pages/neon_asteroids.html +0 -77
  263. package/default-pages/sidebar_page.json +0 -1
  264. package/default-pages/solar_tutorial.json +0 -1
  265. package/default-pages/two-panel_page.json +0 -1
  266. package/dist/agents/a2a/a2aProvider.d.ts +0 -3
  267. package/dist/agents/discovery.d.ts +0 -30
  268. package/dist/agents/openclaw/openclawProvider.d.ts +0 -3
  269. package/dist/agents/types.d.ts +0 -64
  270. package/dist/connectors/index.d.ts +0 -3
  271. package/dist/connectors/types.d.ts +0 -84
  272. package/dist/index.d.ts +0 -7
  273. package/dist/migrations.d.ts +0 -12
  274. package/dist/models/chainOfThought.d.ts +0 -12
  275. package/dist/models/fireworksai.d.ts +0 -30
  276. package/dist/models/logCompletePrompt.d.ts +0 -3
  277. package/dist/models/providers.d.ts +0 -8
  278. package/dist/models/utils.d.ts +0 -6
  279. package/dist/scripts.d.ts +0 -15
  280. package/dist/service/createCompletePrompt.d.ts +0 -5
  281. package/dist/service/debugLog.d.ts +0 -11
  282. package/dist/service/generateImage.d.ts +0 -32
  283. package/dist/service/index.d.ts +0 -8
  284. package/dist/service/modelInstructions.d.ts +0 -7
  285. package/dist/service/requiresSettings.d.ts +0 -3
  286. package/dist/service/server.d.ts +0 -4
  287. package/dist/service/useApiRoutes.d.ts +0 -4
  288. package/dist/service/useConnectorRoutes.d.ts +0 -4
  289. package/dist/service/useDataRoutes.d.ts +0 -4
  290. package/dist/service/useGatewayRoutes.d.ts +0 -4
  291. package/dist/service/useGatewayRoutes.d.ts.map +0 -1
  292. package/dist/service/useGatewayRoutes.js +0 -168
  293. package/dist/service/useGatewayRoutes.js.map +0 -1
  294. package/dist/service/usePageRoutes.d.ts +0 -5
  295. package/dist/synthos-cli.d.ts +0 -2
  296. package/page-scripts/page-v2.js +0 -656
  297. package/required-pages/builder.html +0 -48
  298. package/required-pages/builder.json +0 -1
  299. package/required-pages/pages.json +0 -1
  300. package/required-pages/settings.json +0 -1
  301. package/required-pages/synthos_apis.html +0 -327
  302. package/required-pages/synthos_apis.json +0 -1
  303. package/required-pages/synthos_scripts.json +0 -1
  304. package/src/connectors/airtable/connector.json +0 -27
  305. package/src/connectors/alpha-vantage/connector.json +0 -26
  306. package/src/connectors/brave-search/connector.json +0 -26
  307. package/src/connectors/cloudinary/connector.json +0 -27
  308. package/src/connectors/deepl/connector.json +0 -28
  309. package/src/connectors/elevenlabs/connector.json +0 -30
  310. package/src/connectors/giphy/connector.json +0 -27
  311. package/src/connectors/github/connector.json +0 -29
  312. package/src/connectors/huggingface/connector.json +0 -27
  313. package/src/connectors/imgur/connector.json +0 -29
  314. package/src/connectors/instagram/connector.json +0 -43
  315. package/src/connectors/jira/connector.json +0 -28
  316. package/src/connectors/mapbox/connector.json +0 -26
  317. package/src/connectors/nasa/connector.json +0 -27
  318. package/src/connectors/newsapi/connector.json +0 -27
  319. package/src/connectors/notion/connector.json +0 -28
  320. package/src/connectors/open-exchange-rates/connector.json +0 -27
  321. package/src/connectors/openweathermap/connector.json +0 -26
  322. package/src/connectors/pexels/connector.json +0 -27
  323. package/src/connectors/resend/connector.json +0 -29
  324. package/src/connectors/rss2json/connector.json +0 -27
  325. package/src/connectors/sendgrid/connector.json +0 -27
  326. package/src/connectors/spoonacular/connector.json +0 -28
  327. package/src/connectors/stability-ai/connector.json +0 -27
  328. package/src/connectors/twilio/connector.json +0 -28
  329. package/src/connectors/unsplash/connector.json +0 -27
  330. package/src/connectors/wolfram-alpha/connector.json +0 -26
  331. package/src/connectors/youtube-data/connector.json +0 -30
  332. /package/{dist/connectors → service-connectors}/airtable/connector.json +0 -0
  333. /package/{dist/connectors → service-connectors}/alpha-vantage/connector.json +0 -0
  334. /package/{dist/connectors → service-connectors}/brave-search/connector.json +0 -0
  335. /package/{dist/connectors → service-connectors}/cloudinary/connector.json +0 -0
  336. /package/{dist/connectors → service-connectors}/deepl/connector.json +0 -0
  337. /package/{dist/connectors → service-connectors}/elevenlabs/connector.json +0 -0
  338. /package/{dist/connectors → service-connectors}/giphy/connector.json +0 -0
  339. /package/{dist/connectors → service-connectors}/github/connector.json +0 -0
  340. /package/{dist/connectors → service-connectors}/huggingface/connector.json +0 -0
  341. /package/{dist/connectors → service-connectors}/imgur/connector.json +0 -0
  342. /package/{dist/connectors → service-connectors}/instagram/connector.json +0 -0
  343. /package/{dist/connectors → service-connectors}/jira/connector.json +0 -0
  344. /package/{dist/connectors → service-connectors}/mapbox/connector.json +0 -0
  345. /package/{dist/connectors → service-connectors}/nasa/connector.json +0 -0
  346. /package/{dist/connectors → service-connectors}/newsapi/connector.json +0 -0
  347. /package/{dist/connectors → service-connectors}/notion/connector.json +0 -0
  348. /package/{dist/connectors → service-connectors}/open-exchange-rates/connector.json +0 -0
  349. /package/{dist/connectors → service-connectors}/openweathermap/connector.json +0 -0
  350. /package/{dist/connectors → service-connectors}/pexels/connector.json +0 -0
  351. /package/{dist/connectors → service-connectors}/resend/connector.json +0 -0
  352. /package/{dist/connectors → service-connectors}/rss2json/connector.json +0 -0
  353. /package/{dist/connectors → service-connectors}/sendgrid/connector.json +0 -0
  354. /package/{dist/connectors → service-connectors}/spoonacular/connector.json +0 -0
  355. /package/{dist/connectors → service-connectors}/stability-ai/connector.json +0 -0
  356. /package/{dist/connectors → service-connectors}/twilio/connector.json +0 -0
  357. /package/{dist/connectors → service-connectors}/unsplash/connector.json +0 -0
  358. /package/{dist/connectors → service-connectors}/wolfram-alpha/connector.json +0 -0
  359. /package/{dist/connectors → service-connectors}/youtube-data/connector.json +0 -0
@@ -4,55 +4,100 @@
4
4
  <title>SynthOS - Settings</title>
5
5
  <script id="theme-info" src="/api/theme-info.js" data-locked="true"></script>
6
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{margin-left:auto;width:180px;min-width:120px;flex-shrink:0;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-shrink:1;min-width:0;overflow:hidden;flex-wrap:nowrap}.conn-modal-content{background:rgba(20,20,50,.95);border:1px solid var(--border-color);border-radius:16px;padding:28px;max-width:520px;width:90%;max-height:85vh;display:flex;flex-direction:column;gap:16px;box-shadow:0 8px 40px rgba(0,0,0,.5);overflow-y:auto}.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)}.more-dropdown{position:relative;flex-shrink:0}.more-btn{position:relative}.more-menu{position:absolute;top:100%;left:0;margin-top:4px;min-width:150px;max-height:250px;overflow-y:auto;border-radius:8px;padding:4px 0;box-shadow:0 8px 30px rgba(0,0,0,.4);display:none;border:1px solid var(--border-color);background:rgba(30,30,50,.95);z-index:100}.more-menu.show{display:block}.more-menu-item{padding:8px 16px;font-size:13px;cursor:pointer;color:var(--text-primary);transition:background .15s;white-space:nowrap}.more-menu-item:hover{background:linear-gradient(135deg,rgba(102,126,234,.3) 0,rgba(118,75,162,.3) 100%)}.more-menu-item.active{background:linear-gradient(135deg,rgba(102,126,234,.2) 0,rgba(118,75,162,.2) 100%);color:var(--accent-tertiary)}.conn-tooltip{position:fixed;padding:8px 12px;background:var(--bg-tertiary,#0f0f23);color:var(--text-secondary,#b794f6);border:1px solid var(--border-color,rgba(138,43,226,0.3));border-radius:8px;font-size:12px;line-height:1.5;max-width:260px;pointer-events:none;z-index:10000;box-shadow:0 4px 16px rgba(0,0,0,.4);opacity:0;transition:opacity .15s}.conn-tooltip.visible{opacity:1}.light-mode .more-menu{background:rgba(255,255,255,.97);box-shadow:0 8px 30px rgba(0,0,0,.15)}.light-mode .conn-modal-content{background:rgba(250,248,255,.98)}.agent-toggle-row{display:flex;align-items:center;gap:10px}.agent-list{display:flex;flex-direction:column;gap:0;max-height:400px;overflow-y:auto;border:1px solid var(--border-color);border-radius:12px}.agent-row{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid var(--border-color);cursor:pointer;transition:background .15s}.agent-row:last-child{border-bottom:none}.agent-row:hover{background:rgba(138,43,226,.06)}.agent-row.selected{background:rgba(138,43,226,.1)}.agent-row-info{flex:1;min-width:0;display:flex;align-items:center;gap:10px}.agent-row-name{font-size:14px;font-weight:600;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.agent-row-desc{font-size:12px;color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0}.agent-row-badge{font-size:11px;color:var(--text-secondary);background:rgba(138,43,226,.15);padding:2px 8px;border-radius:10px;white-space:nowrap;flex-shrink:0}.agent-status-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}.agent-status-dot.connected{background:#4ade80}.agent-status-dot.disconnected{background:rgba(100,100,100,.5)}.agent-row-actions{display:flex;align-items:center;gap:8px;flex-shrink:0}.agent-chat-panel{border:1px solid var(--border-color);border-radius:12px;margin-top:12px;overflow:hidden;display:none}.agent-chat-panel.open{display:flex;flex-direction:column}.agent-chat-header{display:flex;justify-content:space-between;align-items:center;padding:10px 16px;background:rgba(138,43,226,.08);font-size:13px;font-weight:600;color:var(--text-primary)}.agent-chat-close{background:none;border:none;color:var(--text-secondary);cursor:pointer;font-size:16px;padding:0 4px}.agent-chat-close:hover{color:var(--text-primary)}.agent-chat-messages{max-height:250px;overflow-y:auto;padding:12px 16px;display:flex;flex-direction:column;gap:8px;font-size:13px}.agent-chat-msg{padding:8px 12px;border-radius:10px;max-width:85%;line-height:1.5;word-wrap:break-word;white-space:pre-wrap}.agent-chat-msg.user{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;align-self:flex-end}.agent-chat-msg.agent{background:rgba(15,15,35,.4);color:var(--text-primary);align-self:flex-start;border:1px solid var(--border-color)}.agent-chat-msg.error{background:rgba(180,50,50,.2);color:rgba(255,150,150,.9);align-self:center;font-size:12px}.agent-chat-msg.status{color:var(--text-secondary);align-self:center;font-size:12px;font-style:italic}.agent-chat-input-row{display:flex;gap:8px;padding:12px 16px;border-top:1px solid var(--border-color)}.agent-chat-input{flex:1;padding:10px 14px;border-radius:10px;border:1px solid var(--border-color);background:rgba(15,15,35,.8);color:var(--text-primary);font-size:13px}.agent-chat-input:focus{outline:0;border-color:rgba(183,148,246,.6)}.agent-chat-input::placeholder{color:rgba(183,148,246,.5)}.agent-chat-send{padding:10px 18px;border:none;border-radius:10px;font-size:13px;background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;cursor:pointer;font-weight:600;transition:.2s}.agent-chat-send:hover{transform:translateY(-1px)}.agent-chat-send:disabled{opacity:.5;pointer-events:none}.light-mode .agent-list{border-color:rgba(118,75,162,.15)}.light-mode .agent-row:hover{background:rgba(118,75,162,.05)}.light-mode .agent-row.selected{background:rgba(118,75,162,.08)}.light-mode .agent-chat-msg.agent{background:rgba(255,255,255,.6)}.light-mode .agent-chat-input{background:rgba(255,255,255,.8)}</style>
7
+ <style>
8
+ /* Layout-specific rules only — all component styling via FluentLM */
9
+ #settingsBtn { opacity: 0.4; pointer-events: none; }
10
+ .accordion-section { display: flex; flex-direction: column; flex-shrink: 0; overflow: hidden; border-bottom: 1px solid var(--neutralLight); }
11
+ .accordion-section:last-child { border-bottom: none; }
12
+ .accordion-section.active { flex-shrink: 1; flex-grow: 1; min-height: 0; }
13
+ .accordion-header { display: flex; justify-content: space-between; align-items: center; width: 100%; padding: 14px 20px; background: none; border: none; color: var(--bodyText); font-size: 15px; font-weight: 600; cursor: pointer; transition: background .2s; flex-shrink: 0; }
14
+ .accordion-header:hover { background: var(--defaultHoverBackground); }
15
+ .accordion-section.active .accordion-header { background: var(--defaultHoverBackground); }
16
+ .accordion-chevron { font-size: 11px; color: var(--bodySubtext); transition: transform .3s; }
17
+ .accordion-section.active .accordion-chevron { transform: rotate(180deg); }
18
+ .accordion-body { display: none; flex-direction: column; flex-grow: 1; min-height: 0; overflow: hidden; }
19
+ .accordion-section.active .accordion-body { display: flex; }
20
+ .accordion-content { display: flex; flex-direction: column; gap: 15px; overflow-y: auto; padding: 20px; flex-grow: 1; }
21
+ .button-row { display: flex; justify-content: flex-end; padding: 12px 20px; border-top: 1px solid var(--neutralLight); flex-shrink: 0; }
22
+ .wizard-hidden { display: none !important; }
23
+ .model-card { border: 1px solid var(--neutralLight); border-radius: 8px; padding: 16px; display: flex; flex-direction: column; gap: 12px; background: var(--defaultStateBackground); }
24
+ .model-card.disabled { opacity: 0.35; pointer-events: none; user-select: none; }
25
+ .accordion-section.disabled .accordion-header { opacity: 0.35; pointer-events: none; cursor: default; }
26
+ .connector-grid { display: flex; flex-wrap: wrap; gap: 12px; }
27
+ .connector-tile { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 18px 12px; border-radius: 12px; border: 1px solid var(--neutralLight); background: var(--defaultStateBackground); cursor: pointer; transition: background .2s, border-color .2s, box-shadow .2s, transform .15s; gap: 8px; min-width: 130px; }
28
+ .connector-tile:hover { background: var(--defaultHoverBackground); border-color: var(--themePrimary); box-shadow: 0 4px 12px rgba(0,0,0,.1); transform: translateY(-1px); }
29
+ .connector-tile.configured { border-color: var(--themePrimary); box-shadow: 0 2px 8px var(--themeLighter); }
30
+ .filter-bar { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; }
31
+ .filter-buttons-container { display: flex; gap: 8px; flex-shrink: 1; min-width: 0; overflow: hidden; flex-wrap: nowrap; }
32
+ .more-dropdown { position: relative; flex-shrink: 0; }
33
+ .more-menu { position: absolute; top: 100%; left: 0; margin-top: 4px; min-width: 150px; max-height: 250px; overflow-y: auto; display: none; z-index: 100; }
34
+ .agent-chat-panel { border: 1px solid var(--neutralLight); border-radius: 8px; margin-top: 12px; overflow: hidden; display: none; }
35
+ .agent-chat-panel.open { display: flex; flex-direction: column; }
36
+ .agent-chat-header { display: flex; justify-content: space-between; align-items: center; padding: 10px 16px; background: var(--defaultHoverBackground); font-size: 13px; font-weight: 600; color: var(--bodyText); }
37
+ .agent-chat-messages { max-height: 250px; overflow-y: auto; padding: 12px 16px; display: flex; flex-direction: column; gap: 8px; font-size: 13px; }
38
+ .agent-chat-msg { padding: 8px 12px; border-radius: 10px; max-width: 85%; line-height: 1.5; word-wrap: break-word; white-space: pre-wrap; }
39
+ .agent-chat-msg.user { background: var(--themePrimary); color: #fff; align-self: flex-end; }
40
+ .agent-chat-msg.agent { background: var(--defaultStateBackground); color: var(--bodyText); align-self: flex-start; border: 1px solid var(--neutralLight); }
41
+ .agent-chat-msg.error { background: var(--errorBackground); color: var(--errorText); align-self: center; font-size: 12px; }
42
+ .agent-chat-msg.status { color: var(--bodySubtext); align-self: center; font-size: 12px; font-style: italic; }
43
+ .agent-chat-input-row { display: flex; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--neutralLight); }
44
+ </style>
8
45
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
9
46
  <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/14.1.1/marked.min.js"></script>
10
47
  <script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.1.0/mermaid.min.js"></script>
48
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
11
49
  </head>
12
50
  <body>
51
+ <div class="shell-toolbar" data-locked="true">
52
+ <button class="shell-toolbar-btn" id="builderToggle" aria-label="Page Builder" data-locked="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M7 18.5H6.2c-1.77 0-3.2-1.43-3.2-3.2V7.7C3 5.93 4.43 4.5 6.2 4.5h11.6c1.77 0 3.2 1.43 3.2 3.2v7.6c0 1.77-1.43 3.2-3.2 3.2H12l-4.2 3.2c-.5.38-1.2.02-1.2-.6V18.5Z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><circle cx="8.5" cy="11.5" r="1" fill="currentColor"/><circle cx="12" cy="11.5" r="1" fill="currentColor"/><circle cx="15.5" cy="11.5" r="1" fill="currentColor"/></svg></button>
53
+ <button class="shell-toolbar-btn" id="pagesBtn" aria-label="View All Pages" data-locked="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none"><rect x="3" y="3" width="11" height="12" rx="1.5" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M6 7.5h5M6 10h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><rect x="18" y="3" width="11" height="12" rx="1.5" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M21 7.5h5M21 10h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><rect x="3" y="18" width="11" height="12" rx="1.5" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M6 22.5h5M6 25h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><rect x="18" y="18" width="11" height="12" rx="1.5" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M21 22.5h5M21 25h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg></button>
54
+ <button class="shell-toolbar-btn" id="saveBtn" aria-label="Save Page" data-locked="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2Z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M17 21v-8H7v8" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M7 3v5h8" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/></svg></button>
55
+ <div class="shell-toolbar-spacer" data-locked="true"></div>
56
+ <button class="shell-toolbar-btn" id="settingsBtn" aria-label="Settings" data-locked="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"><path d="M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z" stroke="currentColor" stroke-width="1.8"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></button>
57
+ </div>
13
58
  <div class="chat-panel" data-locked="true">
14
- <div class="chat-header" data-locked="true">SynthOS</div>
59
+ <div class="chat-header" data-locked="true"><span>Page Builder</span><button class="chat-header-close" id="builderClose" aria-label="Close builder" data-locked="true">&times;</button></div>
15
60
  <div class="chat-messages" id="chatMessages" data-locked="true">
16
61
  <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>
62
+ <p><strong>SynthOS:</strong> This is where you can customize your experience. Change your theme and toolbar layout under <strong>Appearance</strong>, configure AI models under <strong>Page Building &amp; Chat</strong>, or set up integrations under <strong>Connectors</strong> and <strong>Agents</strong>.</p>
63
+ <p>Click <strong>Apply</strong> when you're ready to save your changes.</p>
19
64
  </div>
20
65
  <div class="chat-message" id="firstRunGreeting" style="display:none;">
21
66
  <p><strong>SynthOS:</strong> Welcome to SynthOS! We're glad you're here.</p>
22
67
  <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
68
  <p>You can always come back to this page later to change your theme or enable additional features.</p>
24
69
  </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>
70
+ </div>
31
71
  <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>
72
+ <textarea class="chat-input" id="chatInput" name="message" rows="2" placeholder="Type a message..." data-locked="true"></textarea>
34
73
  </form>
35
74
  </div>
36
75
  <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;">
76
+ <div style="padding: 16px 20px; border-bottom: 1px solid var(--neutralLight);">
77
+ <span class="flm-text flm-text--xLarge flm-text--bold">Settings</span>
78
+ </div>
79
+ <div class="flm-stack" style="flex-grow: 1; overflow: hidden;">
39
80
 
40
- <div class="accordion-section active" data-section="general">
81
+ <div class="accordion-section active" data-section="appearance">
41
82
  <button class="accordion-header">
42
- <span>General</span>
83
+ <span>Appearance</span>
43
84
  <span class="accordion-chevron">&#9662;</span>
44
85
  </button>
45
86
  <div class="accordion-body">
46
87
  <div class="accordion-content">
47
- <div class="form-group">
48
- <label for="theme">Theme</label>
49
- <select id="theme">
88
+ <div class="flm-textfield">
89
+ <label class="flm-label" for="theme">Theme</label>
90
+ <select id="theme" class="flm-textfield-input">
50
91
  <option value="">Loading themes...</option>
51
92
  </select>
52
93
  </div>
53
- </div>
54
- <div class="button-row">
55
- <button class="apply-btn" data-apply="general">Apply</button>
94
+ <div class="flm-textfield">
95
+ <label class="flm-label" for="toolbarPosition">Toolbar Position</label>
96
+ <select id="toolbarPosition" class="flm-textfield-input">
97
+ <option value="left">Left</option>
98
+ <option value="right">Right</option>
99
+ </select>
100
+ </div>
56
101
  </div>
57
102
  </div>
58
103
  </div>
@@ -64,79 +109,76 @@
64
109
  </button>
65
110
  <div class="accordion-body">
66
111
  <div class="accordion-content">
67
- <div id="configBanner" class="config-required-banner" style="display:none;">
112
+ <div id="configBanner" class="flm-messagebar flm-messagebar--warning" style="display:none;">
68
113
  Model configuration is required before you can use SynthOS. Please fill in the Page Builder fields below and click Apply.
69
114
  </div>
70
115
 
71
116
  <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="">
117
+ <span class="flm-text flm-text--mediumPlus flm-text--bold">Page Builder Model</span>
118
+ <div class="flm-textfield">
119
+ <label class="flm-label" for="provider-builder">Provider</label>
120
+ <select id="provider-builder" class="flm-textfield-input" required="">
76
121
  <option value="">Select a provider</option>
77
122
  </select>
78
123
  </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>
124
+ <div class="flm-textfield" id="fg-apikey-builder">
125
+ <label class="flm-label" for="serviceApiKey-builder">API Key</label>
126
+ <input type="password" id="serviceApiKey-builder" class="flm-textfield-input" placeholder="Enter your API Key" required="">
127
+ <div id="providerInstructions" class="flm-messagebar flm-messagebar--info" style="display:none;margin-top:8px;"></div>
83
128
  </div>
84
- <div class="form-group" id="fg-model-builder">
85
- <label for="model-builder">Model</label>
86
- <select id="model-builder" required="">
129
+ <div class="flm-textfield" id="fg-model-builder">
130
+ <label class="flm-label" for="model-builder">Model</label>
131
+ <select id="model-builder" class="flm-textfield-input" required="">
87
132
  <option value="">Select a model</option>
88
133
  </select>
89
134
  </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="">
135
+ <a id="moreSettingsLink" class="flm-link" style="font-size:13px;">&#9662; More settings</a>
136
+ <div class="flm-textfield" id="fg-maxTokens-builder">
137
+ <label class="flm-label" for="maxTokens-builder">Max Output Tokens</label>
138
+ <input type="number" id="maxTokens-builder" class="flm-textfield-input" placeholder="Enter max token count" required="">
94
139
  </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>
140
+ <div class="flm-textfield" id="fg-instructions-builder">
141
+ <label class="flm-label" for="instructions-builder">Additional Instructions</label>
142
+ <textarea id="instructions-builder" class="flm-textfield-input" placeholder="Enter any additional instructions"></textarea>
98
143
  </div>
99
144
  </div>
100
145
 
101
146
  <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="">
147
+ <span class="flm-text flm-text--mediumPlus flm-text--bold">Chat Model</span>
148
+ <div class="flm-textfield" id="fg-provider-chat">
149
+ <label class="flm-label" for="provider-chat">Provider</label>
150
+ <select id="provider-chat" class="flm-textfield-input" required="">
106
151
  <option value="">Select a provider</option>
107
152
  </select>
108
153
  </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="">
154
+ <div class="flm-textfield" id="fg-apikey-chat">
155
+ <label class="flm-label" for="serviceApiKey-chat">API Key</label>
156
+ <input type="password" id="serviceApiKey-chat" class="flm-textfield-input" placeholder="Enter your API Key" required="">
112
157
  </div>
113
- <div class="form-group" id="fg-model-chat">
114
- <label for="model-chat">Model</label>
115
- <select id="model-chat" required="">
158
+ <div class="flm-textfield" id="fg-model-chat">
159
+ <label class="flm-label" for="model-chat">Model</label>
160
+ <select id="model-chat" class="flm-textfield-input" required="">
116
161
  <option value="">Select a model</option>
117
162
  </select>
118
163
  </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="">
164
+ <a id="moreSettingsLinkChat" class="flm-link" style="font-size:13px;">&#9662; More settings</a>
165
+ <div class="flm-textfield" id="fg-maxTokens-chat">
166
+ <label class="flm-label" for="maxTokens-chat">Max Output Tokens</label>
167
+ <input type="number" id="maxTokens-chat" class="flm-textfield-input" placeholder="Enter max token count" required="">
123
168
  </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>
169
+ <div class="flm-textfield" id="fg-instructions-chat">
170
+ <label class="flm-label" for="instructions-chat">Additional Instructions</label>
171
+ <textarea id="instructions-chat" class="flm-textfield-input" placeholder="Enter any additional instructions"></textarea>
127
172
  </div>
128
173
  </div>
129
174
 
130
175
  </div>
131
- <div class="button-row">
132
- <button class="apply-btn" data-apply="models">Apply</button>
133
- </div>
134
176
  </div>
135
177
  </div>
136
178
 
137
179
  <div class="accordion-section" data-section="connectors">
138
180
  <button class="accordion-header">
139
- <span>Connectors <span style="display:inline-block;margin-left:6px;padding:1px 7px;font-size:10px;font-weight:600;letter-spacing:.5px;border-radius:6px;background:var(--accent-tertiary, #8a2be2);color:#fff;vertical-align:middle;line-height:16px;text-transform:uppercase;">Preview</span></span>
181
+ <span>Connectors <span style="display:inline-block;margin-left:6px;padding:1px 7px;font-size:10px;font-weight:600;letter-spacing:.5px;border-radius:6px;background:var(--themePrimary);color:#fff;vertical-align:middle;line-height:16px;text-transform:uppercase;">Preview</span></span>
140
182
  <span class="accordion-chevron">&#9662;</span>
141
183
  </button>
142
184
  <div class="accordion-body">
@@ -144,10 +186,12 @@
144
186
  <div class="filter-bar" id="connectorFilterBar">
145
187
  <div class="filter-buttons-container" id="connectorFilterButtons"></div>
146
188
  <div class="more-dropdown" id="connMoreDropdown" style="display:none;">
147
- <button class="filter-btn more-btn" id="connMoreBtn">More &#9662;</button>
148
- <div class="more-menu" id="connMoreMenu"></div>
189
+ <button class="flm-button flm-button--subtle" id="connMoreBtn">More <i class="flm-icon" data-icon="ChevronDown"></i></button>
190
+ <div class="flm-contextmenu more-menu" id="connMoreMenu"></div>
191
+ </div>
192
+ <div class="flm-searchbox" id="connectorSearchBox">
193
+ <input class="flm-searchbox-input" type="text" id="connectorSearch" placeholder="Search connectors...">
149
194
  </div>
150
- <input type="text" class="search-input" id="connectorSearch" placeholder="Search connectors...">
151
195
  </div>
152
196
  <div class="connector-grid" id="connectorGrid"></div>
153
197
  </div>
@@ -156,28 +200,28 @@
156
200
 
157
201
  <div class="accordion-section" data-section="agents">
158
202
  <button class="accordion-header">
159
- <span>Agents <span style="display:inline-block;margin-left:6px;padding:1px 7px;font-size:10px;font-weight:600;letter-spacing:.5px;border-radius:6px;background:var(--accent-tertiary, #8a2be2);color:#fff;vertical-align:middle;line-height:16px;text-transform:uppercase;">Preview</span></span>
203
+ <span>Agents <span style="display:inline-block;margin-left:6px;padding:1px 7px;font-size:10px;font-weight:600;letter-spacing:.5px;border-radius:6px;background:var(--themePrimary);color:#fff;vertical-align:middle;line-height:16px;text-transform:uppercase;">Preview</span></span>
160
204
  <span class="accordion-chevron">&#9662;</span>
161
205
  </button>
162
206
  <div class="accordion-body">
163
207
  <div class="accordion-content">
164
- <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
165
- <div style="font-size:13px;color:var(--text-secondary);">Configure agents (A2A or OpenClaw) that your pages can communicate with.</div>
166
- <button class="conn-modal-btn" id="addAgentBtn" style="padding:8px 18px;font-size:13px;">+ Add Agent</button>
208
+ <div class="flm-stack flm-stack--horizontal flm-stack--space-between" style="align-items:center;margin-bottom:12px;">
209
+ <span class="flm-text flm-text--small flm-text--secondary">Configure agents (A2A or OpenClaw) that your pages can communicate with.</span>
210
+ <button class="flm-button flm-button--primary" id="addAgentBtn" style="white-space:nowrap;">+ Add Agent</button>
167
211
  </div>
168
- <div class="agent-list" id="agentList"></div>
169
- <div id="agentEmptyState" style="display:none;text-align:center;padding:30px;color:var(--text-secondary);font-size:14px;">
212
+ <div class="flm-list flm-list--bordered" id="agentList"></div>
213
+ <div id="agentEmptyState" style="display:none;text-align:center;padding:30px;color:var(--bodySubtext);font-size:14px;">
170
214
  No agents configured yet. Click "+ Add Agent" to get started.
171
215
  </div>
172
216
  <div class="agent-chat-panel" id="agentChatPanel">
173
217
  <div class="agent-chat-header">
174
218
  <span id="agentChatTitle">Chat with Agent</span>
175
- <button class="agent-chat-close" id="agentChatClose">&times;</button>
219
+ <button class="flm-button flm-button--subtle flm-button--icon" id="agentChatClose" aria-label="Close chat" data-icon="Cancel"></button>
176
220
  </div>
177
221
  <div class="agent-chat-messages" id="agentChatMessages"></div>
178
222
  <div class="agent-chat-input-row">
179
- <input type="text" class="agent-chat-input" id="agentChatInput" placeholder="Type a test message...">
180
- <button class="agent-chat-send" id="agentChatSendBtn">Send</button>
223
+ <input type="text" class="flm-textfield-input" id="agentChatInput" placeholder="Type a test message..." style="flex:1;">
224
+ <button class="flm-button flm-button--primary" id="agentChatSendBtn">Send</button>
181
225
  </div>
182
226
  </div>
183
227
  </div>
@@ -185,125 +229,152 @@
185
229
  </div>
186
230
 
187
231
  </div>
232
+ <div class="button-row" id="applyRow">
233
+ <button class="flm-button" id="applyBtn">Apply</button>
234
+ </div>
188
235
  <div id="loadingOverlay" class="loading-overlay"><div class="spinner"></div></div>
189
236
  </div>
190
- <div class="modal-overlay" id="agentModal" style="display:none;">
191
- <div class="conn-modal-content">
192
- <div class="conn-modal-title" id="agentModalTitle">Add Agent</div>
193
- <div id="agentFormGroup">
194
- <div class="form-group" style="margin-bottom:10px;">
195
- <label>Agent Type</label>
196
- <div style="display:flex;gap:8px;">
197
- <button class="filter-btn active" id="agentTypeA2A">A2A Agent</button>
198
- <button class="filter-btn" id="agentTypeOpenClaw">OpenClaw Agent</button>
237
+ <div class="flm-dialog-overlay" id="agentModal">
238
+ <div class="flm-dialog" style="max-width:520px;width:90%;max-height:85vh;overflow-y:auto;">
239
+ <div class="flm-dialog-header">
240
+ <h2 class="flm-dialog-title" id="agentModalTitle">Add Agent</h2>
241
+ </div>
242
+ <div class="flm-dialog-body">
243
+ <div class="flm-stack" style="gap:10px;" id="agentFormGroup">
244
+ <div class="flm-textfield">
245
+ <label class="flm-label">Agent Type</label>
246
+ <div class="flm-stack flm-stack--horizontal" style="gap:8px;">
247
+ <button class="flm-pivot-tab flm-pivot-tab--active" id="agentTypeA2A">A2A Agent</button>
248
+ <button class="flm-pivot-tab" id="agentTypeOpenClaw">OpenClaw Agent</button>
249
+ </div>
199
250
  </div>
200
- </div>
201
- <div class="form-group" style="margin-bottom:10px;">
202
- <label for="agentUrl">Agent URL</label>
203
- <div style="display:flex;gap:8px;">
204
- <input type="text" id="agentUrl" placeholder="https://example.com" style="flex:1;">
205
- <button class="conn-modal-btn" id="agentDiscoverBtn" style="padding:10px 16px;white-space:nowrap;font-size:13px;">Discover</button>
251
+ <div class="flm-textfield">
252
+ <label class="flm-label" for="agentUrl">Agent URL</label>
253
+ <div class="flm-stack flm-stack--horizontal" style="gap:8px;">
254
+ <input type="text" id="agentUrl" class="flm-textfield-input" placeholder="https://example.com" style="flex:1;">
255
+ <button class="flm-button flm-button--primary" id="agentDiscoverBtn" style="white-space:nowrap;">Discover</button>
256
+ </div>
257
+ <span id="agentUrlHint" class="flm-text flm-text--small flm-text--secondary" style="margin-top:4px;">Enter a URL and click Discover to auto-fill from the agent card, or fill in the fields manually.</span>
206
258
  </div>
207
- <div id="agentUrlHint" style="font-size:11px;color:var(--text-secondary);margin-top:4px;">Enter a URL and click Discover to auto-fill from the agent card, or fill in the fields manually.</div>
208
- </div>
209
- <div class="form-group" id="agentTokenGroup" style="margin-bottom:10px;display:none;">
210
- <label for="agentToken">Token</label>
211
- <input type="password" id="agentToken" placeholder="Gateway authentication token">
212
- </div>
213
- <div class="form-group" id="agentSessionKeyGroup" style="margin-bottom:10px;display:none;">
214
- <label for="agentSessionKey">Default Session Key</label>
215
- <input type="text" id="agentSessionKey" placeholder="e.g. agent:main:main">
216
- </div>
217
- <div id="agentSshTunnelGroup" style="display:none;margin-bottom:10px;">
218
- <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;cursor:pointer;" id="agentSshTunnelToggleHeader">
219
- <span style="font-size:12px;color:var(--text-secondary);" id="agentSshTunnelArrow">&#9654;</span>
220
- <label style="margin:0;cursor:pointer;font-size:13px;font-weight:600;color:var(--text-secondary);">SSH Tunnel</label>
221
- <label class="toggle-switch" style="margin-left:auto;" onclick="event.stopPropagation();">
222
- <input type="checkbox" id="agentSshTunnelEnabled">
223
- <span class="toggle-slider"></span>
224
- </label>
259
+ <div class="flm-textfield" id="agentTokenGroup" style="display:none;">
260
+ <label class="flm-label" for="agentToken">Token</label>
261
+ <input type="password" id="agentToken" class="flm-textfield-input" placeholder="Gateway authentication token">
225
262
  </div>
226
- <div id="agentSshTunnelFields" style="display:none;padding-left:4px;">
227
- <div class="form-group" style="margin-bottom:8px;">
228
- <label for="agentSshCommand" style="font-size:12px;">SSH Command</label>
229
- <input type="text" id="agentSshCommand" placeholder="ssh -p 22 -N -L 18789:127.0.0.1:43901 root@0.0.0.0">
263
+ <div class="flm-textfield" id="agentSessionKeyGroup" style="display:none;">
264
+ <label class="flm-label" for="agentSessionKey">Default Session Key</label>
265
+ <input type="text" id="agentSessionKey" class="flm-textfield-input" placeholder="e.g. agent:main:main">
266
+ </div>
267
+ <div id="agentSshTunnelGroup" style="display:none;">
268
+ <div class="flm-stack flm-stack--horizontal" style="align-items:center;gap:8px;cursor:pointer;margin-bottom:8px;" id="agentSshTunnelToggleHeader">
269
+ <span class="flm-text flm-text--small flm-text--secondary" id="agentSshTunnelArrow">&#9654;</span>
270
+ <label class="flm-label" style="margin:0;cursor:pointer;">SSH Tunnel</label>
271
+ <label class="flm-toggle flm-toggle--inline" style="margin-left:auto;" onclick="event.stopPropagation();">
272
+ <input type="checkbox" class="flm-toggle-input" id="agentSshTunnelEnabled">
273
+ <span class="flm-toggle-track"><span class="flm-toggle-thumb"></span></span>
274
+ </label>
230
275
  </div>
231
- <div class="form-group" style="margin-bottom:8px;">
232
- <label for="agentSshPassword" style="font-size:12px;">Password</label>
233
- <input type="password" id="agentSshPassword" placeholder="SSH password">
276
+ <div id="agentSshTunnelFields" style="display:none;padding-left:4px;" class="flm-stack" >
277
+ <div class="flm-textfield" style="margin-bottom:8px;">
278
+ <label class="flm-label" for="agentSshCommand" style="font-size:12px;">SSH Command</label>
279
+ <input type="text" id="agentSshCommand" class="flm-textfield-input" placeholder="ssh -p 22 -N -L 18789:127.0.0.1:43901 root@0.0.0.0">
280
+ </div>
281
+ <div class="flm-textfield" style="margin-bottom:8px;">
282
+ <label class="flm-label" for="agentSshPassword" style="font-size:12px;">Password</label>
283
+ <input type="password" id="agentSshPassword" class="flm-textfield-input" placeholder="SSH password">
284
+ </div>
234
285
  </div>
235
286
  </div>
236
- </div>
237
- <div class="form-group" style="margin-bottom:10px;">
238
- <label for="agentName">Name</label>
239
- <input type="text" id="agentName" placeholder="My Agent">
240
- </div>
241
- <div class="form-group" style="margin-bottom:10px;">
242
- <label for="agentDescription">Description</label>
243
- <textarea id="agentDescription" placeholder="Describe what this agent does and when to use it..." style="min-height:60px;"></textarea>
244
- </div>
245
- <div id="agentSkillsPreview" style="display:none;padding:10px;border-radius:8px;border:1px solid var(--border-color);background:rgba(15,15,35,.3);margin-bottom:10px;">
246
- <div style="font-size:12px;font-weight:600;color:var(--text-secondary);margin-bottom:4px;">Discovered Skills</div>
247
- <div id="agentSkillsList" style="font-size:12px;color:var(--text-secondary);"></div>
287
+ <div class="flm-textfield">
288
+ <label class="flm-label" for="agentName">Name</label>
289
+ <input type="text" id="agentName" class="flm-textfield-input" placeholder="My Agent">
290
+ </div>
291
+ <div class="flm-textfield">
292
+ <label class="flm-label" for="agentDescription">Description</label>
293
+ <textarea id="agentDescription" class="flm-textfield-input" placeholder="Describe what this agent does and when to use it..." style="min-height:60px;"></textarea>
294
+ </div>
295
+ <div id="agentSkillsPreview" style="display:none;padding:10px;border-radius:8px;border:1px solid var(--neutralLight);background:var(--defaultStateBackground);">
296
+ <span class="flm-text flm-text--small flm-text--semibold flm-text--secondary" style="margin-bottom:4px;display:block;">Discovered Skills</span>
297
+ <div id="agentSkillsList" class="flm-text flm-text--small flm-text--secondary"></div>
298
+ </div>
248
299
  </div>
249
300
  </div>
250
- <div class="conn-modal-actions">
251
- <button class="conn-modal-btn" id="agentSaveBtn">Save</button>
252
- <button class="conn-modal-btn cancel" id="agentCancelBtn">Cancel</button>
253
- <button class="conn-modal-btn remove" id="agentRemoveBtn" style="display:none;">Remove</button>
301
+ <div class="flm-dialog-footer">
302
+ <button class="flm-button flm-button--primary" id="agentSaveBtn">Save</button>
303
+ <button class="flm-button" id="agentCancelBtn">Cancel</button>
304
+ <button class="flm-button" id="agentRemoveBtn" style="display:none;color:var(--errorText);">Remove</button>
254
305
  </div>
255
306
  </div>
256
307
  </div>
257
- <div class="modal-overlay" id="connectorModal" style="display:none;">
258
- <div class="conn-modal-content">
259
- <div class="conn-modal-title" id="connectorModalTitle"></div>
260
- <div class="conn-modal-desc" id="connectorModalDesc"></div>
261
- <div id="connectorOnboarding" style="display:none;">
262
- <a id="connectorOnboardingLink" href="#" target="_blank"
263
- style="color:var(--accent-tertiary);font-size:13px;font-weight:500;text-decoration:none;transition:.3s;">
264
- Get your API key &#8594;
265
- </a>
266
- <ol id="connectorOnboardingSteps"
267
- style="font-size:13px;color:var(--text-secondary);margin:8px 0 0;padding-left:20px;line-height:1.8;">
268
- </ol>
308
+ <div class="flm-dialog-overlay" id="connectorModal">
309
+ <div class="flm-dialog" style="max-width:520px;width:90%;max-height:85vh;overflow-y:auto;">
310
+ <div class="flm-dialog-header">
311
+ <h2 class="flm-dialog-title" id="connectorModalTitle"></h2>
269
312
  </div>
270
- <div id="connectorApiKeyGroup" class="form-group">
271
- <label for="connectorApiKey">API Key</label>
272
- <input type="password" id="connectorApiKey" placeholder="Enter your API key">
273
- </div>
274
- <div id="connectorOAuthGroup" style="display:none;">
275
- <div id="connectorOAuthStatus" style="margin-bottom:12px;font-size:13px;line-height:1.5;"></div>
276
- <div id="connectorOAuthModeToggle" style="display:flex;gap:8px;margin-bottom:12px;">
277
- <button class="filter-btn active" id="oauthModeManual">Manual Token</button>
278
- <button class="filter-btn" id="oauthModeApp">OAuth App</button>
279
- </div>
280
- <div id="connectorManualGroup">
281
- <div class="form-group" style="margin-bottom:10px;">
282
- <label for="oauth-field-accessToken">Access Token</label>
283
- <input type="password" id="oauth-field-accessToken" placeholder="Paste token from Graph API Explorer">
313
+ <div class="flm-dialog-body">
314
+ <div class="flm-stack" style="gap:12px;">
315
+ <span class="flm-text flm-text--small flm-text--secondary" id="connectorModalDesc"></span>
316
+ <div id="connectorOnboarding" style="display:none;">
317
+ <a id="connectorOnboardingLink" class="flm-link" href="#" target="_blank">
318
+ Get your API key &#8594;
319
+ </a>
320
+ <ol id="connectorOnboardingSteps"
321
+ style="font-size:13px;color:var(--bodySubtext);margin:8px 0 0;padding-left:20px;line-height:1.8;">
322
+ </ol>
284
323
  </div>
285
- <div class="form-group" style="margin-bottom:10px;">
286
- <label for="oauth-field-userId">IG User ID <span style="color:var(--text-secondary);font-weight:400;">(optional)</span></label>
287
- <input type="text" id="oauth-field-userId" placeholder="Instagram Business Account ID">
324
+ <div id="connectorApiKeyGroup" class="flm-textfield">
325
+ <label class="flm-label" for="connectorApiKey">API Key</label>
326
+ <input type="password" id="connectorApiKey" class="flm-textfield-input" placeholder="Enter your API key">
327
+ </div>
328
+ <div id="connectorOAuthGroup" style="display:none;">
329
+ <div id="connectorOAuthStatus" style="margin-bottom:12px;font-size:13px;line-height:1.5;"></div>
330
+ <div id="connectorOAuthModeToggle" class="flm-stack flm-stack--horizontal" style="gap:8px;margin-bottom:12px;">
331
+ <button class="flm-pivot-tab flm-pivot-tab--active" id="oauthModeManual">Manual Token</button>
332
+ <button class="flm-pivot-tab" id="oauthModeApp">OAuth App</button>
333
+ </div>
334
+ <div id="connectorManualGroup">
335
+ <div class="flm-textfield" style="margin-bottom:10px;">
336
+ <label class="flm-label" for="oauth-field-accessToken">Access Token</label>
337
+ <input type="password" id="oauth-field-accessToken" class="flm-textfield-input" placeholder="Paste token from Graph API Explorer">
338
+ </div>
339
+ <div class="flm-textfield" style="margin-bottom:10px;">
340
+ <label class="flm-label" for="oauth-field-userId">IG User ID <span class="flm-text--secondary" style="font-weight:400;">(optional)</span></label>
341
+ <input type="text" id="oauth-field-userId" class="flm-textfield-input" placeholder="Instagram Business Account ID">
342
+ </div>
343
+ </div>
344
+ <div id="connectorAppGroup" style="display:none;">
345
+ <div id="connectorOAuthFields" class="flm-stack" style="gap:10px;"></div>
346
+ <button class="flm-button flm-button--primary" id="connectorConnectBtn" style="width:100%;margin-top:8px;margin-bottom:8px;">Connect with OAuth</button>
347
+ </div>
348
+ <button class="flm-button" id="connectorDisconnectBtn" style="width:100%;display:none;color:var(--errorText);">Disconnect</button>
349
+ </div>
350
+ <div class="flm-stack flm-stack--horizontal" style="align-items:center;gap:12px;">
351
+ <label class="flm-label" for="connectorEnabled" style="margin-bottom:0;">Enabled</label>
352
+ <label class="flm-toggle flm-toggle--inline">
353
+ <input type="checkbox" class="flm-toggle-input" id="connectorEnabled">
354
+ <span class="flm-toggle-track"><span class="flm-toggle-thumb"></span></span>
355
+ </label>
288
356
  </div>
289
357
  </div>
290
- <div id="connectorAppGroup" style="display:none;">
291
- <div class="form-group" id="connectorOAuthFields"></div>
292
- <button class="conn-modal-btn" id="connectorConnectBtn" style="width:100%;margin-bottom:8px;">Connect with OAuth</button>
293
- </div>
294
- <button class="conn-modal-btn remove" id="connectorDisconnectBtn" style="width:100%;display:none;">Disconnect</button>
295
358
  </div>
296
- <div class="form-group" style="flex-direction:row;align-items:center;gap:12px;">
297
- <label for="connectorEnabled" style="margin-bottom:0;">Enabled</label>
298
- <label class="toggle-switch">
299
- <input type="checkbox" id="connectorEnabled">
300
- <span class="toggle-slider"></span>
301
- </label>
359
+ <div class="flm-dialog-footer">
360
+ <button class="flm-button flm-button--primary" id="connectorSaveBtn">Save</button>
361
+ <button class="flm-button" id="connectorCancelBtn">Cancel</button>
362
+ <button class="flm-button" id="connectorRemoveBtn" style="color:var(--errorText);">Remove</button>
302
363
  </div>
303
- <div class="conn-modal-actions">
304
- <button class="conn-modal-btn" id="connectorSaveBtn">Save</button>
305
- <button class="conn-modal-btn cancel" id="connectorCancelBtn">Cancel</button>
306
- <button class="conn-modal-btn remove" id="connectorRemoveBtn">Remove</button>
364
+ </div>
365
+ </div>
366
+ <div class="flm-dialog-overlay" id="unsavedDialog" data-light-dismiss>
367
+ <div class="flm-dialog" style="max-width:400px;width:90%;">
368
+ <div class="flm-dialog-header">
369
+ <h2 class="flm-dialog-title">Unsaved Changes</h2>
370
+ </div>
371
+ <div class="flm-dialog-body">
372
+ <p class="flm-text">You have unsaved changes. Do you want to discard them and leave this page?</p>
373
+ </div>
374
+ <div class="flm-dialog-footer">
375
+ <div style="flex:1;"></div>
376
+ <button class="flm-button" id="unsavedStayBtn">Stay</button>
377
+ <button class="flm-button" id="unsavedLeaveBtn" style="color:var(--errorText);">Discard &amp; Leave</button>
307
378
  </div>
308
379
  </div>
309
380
  </div>
@@ -341,7 +412,7 @@ document.querySelectorAll('.accordion-header').forEach(function(header) {
341
412
  // --- URL param: open requested tab ---
342
413
  var params = new URLSearchParams(window.location.search);
343
414
  var tabParam = params.get('tab');
344
- if (tabParam && ['general', 'models', 'connectors', 'agents'].indexOf(tabParam) !== -1) {
415
+ if (tabParam && ['appearance', 'models', 'connectors', 'agents'].indexOf(tabParam) !== -1) {
345
416
  openSection(tabParam);
346
417
  }
347
418
 
@@ -371,7 +442,7 @@ function getAllConnectorCategories() {
371
442
  function calculateConnectorVisibleCats() {
372
443
  var allCats = getAllConnectorCategories();
373
444
  var filterBar = document.getElementById('connectorFilterBar');
374
- var searchEl = document.getElementById('connectorSearch');
445
+ var searchEl = document.getElementById('connectorSearchBox');
375
446
  var moreDropdown = document.getElementById('connMoreDropdown');
376
447
  if (!filterBar) return;
377
448
 
@@ -379,7 +450,7 @@ function calculateConnectorVisibleCats() {
379
450
 
380
451
  // Measure button widths
381
452
  var temp = document.createElement('button');
382
- temp.className = 'filter-btn';
453
+ temp.className = 'flm-pivot-tab';
383
454
  temp.style.visibility = 'hidden';
384
455
  temp.style.position = 'absolute';
385
456
  document.body.appendChild(temp);
@@ -456,14 +527,14 @@ function renderConnectorFilterBar() {
456
527
 
457
528
  // "All" button
458
529
  var allBtn = document.createElement('button');
459
- allBtn.className = 'filter-btn' + (activeConnectorCategory === 'All' ? ' active' : '');
530
+ allBtn.className = 'flm-pivot-tab' + (activeConnectorCategory === 'All' ? ' flm-pivot-tab--active' : '');
460
531
  allBtn.textContent = 'All';
461
532
  allBtn.addEventListener('click', function() { setConnectorCategory('All'); });
462
533
  container.appendChild(allBtn);
463
534
 
464
535
  // "Enabled" button (fixed, always visible)
465
536
  var enabledBtn = document.createElement('button');
466
- enabledBtn.className = 'filter-btn' + (activeConnectorCategory === 'Enabled' ? ' active' : '');
537
+ enabledBtn.className = 'flm-pivot-tab' + (activeConnectorCategory === 'Enabled' ? ' flm-pivot-tab--active' : '');
467
538
  enabledBtn.textContent = 'Enabled';
468
539
  enabledBtn.addEventListener('click', function() { setConnectorCategory('Enabled'); });
469
540
  container.appendChild(enabledBtn);
@@ -471,7 +542,7 @@ function renderConnectorFilterBar() {
471
542
  // Visible category buttons
472
543
  connVisibleCats.forEach(function(cat) {
473
544
  var btn = document.createElement('button');
474
- btn.className = 'filter-btn' + (activeConnectorCategory === cat ? ' active' : '');
545
+ btn.className = 'flm-pivot-tab' + (activeConnectorCategory === cat ? ' flm-pivot-tab--active' : '');
475
546
  btn.textContent = cat;
476
547
  btn.addEventListener('click', function() { setConnectorCategory(cat); });
477
548
  container.appendChild(btn);
@@ -480,14 +551,17 @@ function renderConnectorFilterBar() {
480
551
  // Overflow dropdown
481
552
  if (connOverflowCats.length > 0) {
482
553
  moreDropdown.style.display = '';
483
- moreBtn.className = 'filter-btn more-btn' + (connOverflowCats.indexOf(activeConnectorCategory) !== -1 ? ' active' : '');
554
+ moreBtn.className = 'flm-button flm-button--subtle' + (connOverflowCats.indexOf(activeConnectorCategory) !== -1 ? ' flm-pivot-tab--active' : '');
484
555
  connOverflowCats.forEach(function(cat) {
485
- var item = document.createElement('div');
486
- item.className = 'more-menu-item' + (activeConnectorCategory === cat ? ' active' : '');
487
- item.textContent = cat;
556
+ var item = document.createElement('button');
557
+ item.className = 'flm-contextmenu-item' + (activeConnectorCategory === cat ? ' flm-contextmenu-item--checked' : '');
558
+ var itemText = document.createElement('span');
559
+ itemText.className = 'flm-contextmenu-item-text';
560
+ itemText.textContent = cat;
561
+ item.appendChild(itemText);
488
562
  item.addEventListener('click', function() {
489
563
  setConnectorCategory(cat);
490
- moreMenu.classList.remove('show');
564
+ moreMenu.classList.remove('flm-contextmenu--visible');
491
565
  });
492
566
  moreMenu.appendChild(item);
493
567
  });
@@ -511,43 +585,6 @@ function loadConnectors() {
511
585
  });
512
586
  }
513
587
 
514
- // --- Connector tooltip ---
515
- var connTip = document.createElement('div');
516
- connTip.className = 'conn-tooltip';
517
- document.body.appendChild(connTip);
518
- var connTipTimer = null;
519
-
520
- function showConnTip(el, text) {
521
- clearTimeout(connTipTimer);
522
- connTip.textContent = text;
523
- connTip.style.display = 'block';
524
- connTip.classList.remove('visible');
525
- var r = el.getBoundingClientRect();
526
- var tw = connTip.offsetWidth;
527
- var th = connTip.offsetHeight;
528
- // Position below the tile by default
529
- var left = r.left + (r.width / 2) - (tw / 2);
530
- var top = r.bottom + 6;
531
- // Clamp horizontal
532
- if (left < 4) left = 4;
533
- if (left + tw > window.innerWidth - 4) left = window.innerWidth - tw - 4;
534
- // Flip above if it would go off bottom
535
- if (top + th > window.innerHeight - 4) {
536
- top = r.top - th - 6;
537
- }
538
- connTip.style.left = left + 'px';
539
- connTip.style.top = top + 'px';
540
- void connTip.offsetWidth;
541
- connTip.classList.add('visible');
542
- }
543
-
544
- function hideConnTip() {
545
- clearTimeout(connTipTimer);
546
- connTip.classList.remove('visible');
547
- connTip.style.display = 'none';
548
- }
549
- hideConnTip();
550
-
551
588
  function renderConnectorGrid() {
552
589
  var grid = document.getElementById('connectorGrid');
553
590
  var searchTerm = (document.getElementById('connectorSearch').value || '').toLowerCase();
@@ -563,19 +600,16 @@ function renderConnectorGrid() {
563
600
  }).forEach(function(c) {
564
601
  var tile = document.createElement('div');
565
602
  tile.className = 'connector-tile' + (c.configured ? ' configured' : '');
603
+ tile.setAttribute('data-tooltip', c.description);
566
604
  tile.addEventListener('click', function() { openConnectorModal(c.id); });
567
605
 
568
- tile.addEventListener('mouseenter', function() {
569
- connTipTimer = setTimeout(function() { showConnTip(tile, c.description); }, 400);
570
- });
571
- tile.addEventListener('mouseleave', hideConnTip);
572
-
573
606
  var name = document.createElement('div');
574
- name.className = 'connector-tile-name';
607
+ name.className = 'flm-text flm-text--semibold';
575
608
  name.textContent = c.name;
576
609
 
577
- var cat = document.createElement('div');
578
- cat.className = 'connector-category';
610
+ var cat = document.createElement('span');
611
+ cat.className = 'flm-text flm-text--small flm-text--secondary';
612
+ cat.style.cssText = 'background:var(--defaultHoverBackground);padding:2px 8px;border-radius:10px;';
579
613
  cat.textContent = c.category;
580
614
 
581
615
  tile.appendChild(name);
@@ -618,14 +652,15 @@ function openConnectorModal(id) {
618
652
  fieldsContainer.innerHTML = '';
619
653
  (detail.fields || []).forEach(function(f) {
620
654
  var wrap = document.createElement('div');
621
- wrap.className = 'form-group';
622
- wrap.style.marginBottom = '10px';
655
+ wrap.className = 'flm-textfield';
623
656
  var lbl = document.createElement('label');
657
+ lbl.className = 'flm-label';
624
658
  lbl.textContent = f.label;
625
659
  lbl.setAttribute('for', 'oauth-field-' + f.name);
626
660
  var inp = document.createElement('input');
627
661
  inp.type = f.type;
628
662
  inp.id = 'oauth-field-' + f.name;
663
+ inp.className = 'flm-textfield-input';
629
664
  inp.placeholder = detail.hasKey ? '(saved — leave blank to keep)' : 'Enter ' + f.label;
630
665
  wrap.appendChild(lbl);
631
666
  wrap.appendChild(inp);
@@ -647,7 +682,7 @@ function openConnectorModal(id) {
647
682
  var modeToggle = document.getElementById('connectorOAuthModeToggle');
648
683
  if (detail.connected) {
649
684
  var displayName = detail.accountName || detail.name;
650
- statusEl.innerHTML = '<span style="color:var(--accent-tertiary);">Connected as <strong>' + displayName + '</strong></span>';
685
+ statusEl.innerHTML = '<span style="color:var(--themePrimary);">Connected as <strong>' + displayName + '</strong></span>';
651
686
  statusEl.style.display = '';
652
687
  disconnectBtn.style.display = '';
653
688
  modeToggle.style.display = 'none';
@@ -662,7 +697,7 @@ function openConnectorModal(id) {
662
697
  document.getElementById('connectorApiKey').placeholder = detail.hasKey ? '(key saved — leave blank to keep)' : 'Enter your API key';
663
698
  }
664
699
 
665
- document.getElementById('connectorModal').style.display = 'flex';
700
+ document.getElementById('connectorModal').classList.add('flm-dialog-overlay--open');
666
701
  });
667
702
  }
668
703
 
@@ -672,15 +707,15 @@ function setOAuthMode(mode) {
672
707
  currentOAuthMode = mode;
673
708
  document.getElementById('connectorManualGroup').style.display = mode === 'manual' ? '' : 'none';
674
709
  document.getElementById('connectorAppGroup').style.display = mode === 'app' ? '' : 'none';
675
- document.getElementById('oauthModeManual').className = 'filter-btn' + (mode === 'manual' ? ' active' : '');
676
- document.getElementById('oauthModeApp').className = 'filter-btn' + (mode === 'app' ? ' active' : '');
710
+ document.getElementById('oauthModeManual').className = 'flm-pivot-tab' + (mode === 'manual' ? ' flm-pivot-tab--active' : '');
711
+ document.getElementById('oauthModeApp').className = 'flm-pivot-tab' + (mode === 'app' ? ' flm-pivot-tab--active' : '');
677
712
  }
678
713
 
679
714
  document.getElementById('oauthModeManual').addEventListener('click', function() { setOAuthMode('manual'); });
680
715
  document.getElementById('oauthModeApp').addEventListener('click', function() { setOAuthMode('app'); });
681
716
 
682
717
  function closeConnectorModal() {
683
- document.getElementById('connectorModal').style.display = 'none';
718
+ document.getElementById('connectorModal').classList.remove('flm-dialog-overlay--open');
684
719
  currentConnectorId = null;
685
720
  }
686
721
 
@@ -765,10 +800,10 @@ document.getElementById('connectorSearch').addEventListener('input', renderConne
765
800
  // More dropdown toggle
766
801
  document.getElementById('connMoreBtn').addEventListener('click', function(e) {
767
802
  e.stopPropagation();
768
- document.getElementById('connMoreMenu').classList.toggle('show');
803
+ document.getElementById('connMoreMenu').classList.toggle('flm-contextmenu--visible');
769
804
  });
770
805
  document.addEventListener('click', function() {
771
- document.getElementById('connMoreMenu').classList.remove('show');
806
+ document.getElementById('connMoreMenu').classList.remove('flm-contextmenu--visible');
772
807
  });
773
808
 
774
809
  // Resize observer — recalculate visible categories when filter bar resizes
@@ -821,26 +856,29 @@ function renderAgentList() {
821
856
 
822
857
  agentList.forEach(function(a) {
823
858
  var row = document.createElement('div');
824
- row.className = 'agent-row';
859
+ row.className = 'flm-list-item';
860
+ row.style.cssText = 'display:flex;align-items:center;gap:12px;padding:12px 16px;cursor:pointer;';
825
861
 
826
862
  // Status dot (openclaw only)
827
863
  if (a.provider === 'openclaw') {
828
864
  var dot = document.createElement('div');
829
- dot.className = 'agent-status-dot ' + (a.connected ? 'connected' : 'disconnected');
865
+ dot.style.cssText = 'width:8px;height:8px;border-radius:50%;flex-shrink:0;background:' + (a.connected ? 'var(--successText)' : 'var(--disabledText)');
830
866
  dot.title = a.connected ? 'Connected' : 'Disconnected';
831
867
  row.appendChild(dot);
832
868
  }
833
869
 
834
870
  // Info section
835
871
  var info = document.createElement('div');
836
- info.className = 'agent-row-info';
872
+ info.className = 'flm-list-item-content';
873
+ info.style.cssText = 'flex:1;min-width:0;display:flex;align-items:center;gap:10px;';
837
874
 
838
- var nameEl = document.createElement('div');
839
- nameEl.className = 'agent-row-name';
875
+ var nameEl = document.createElement('span');
876
+ nameEl.className = 'flm-list-item-primary flm-text--semibold flm-text--nowrap';
840
877
  nameEl.textContent = a.name;
841
878
 
842
- var descEl = document.createElement('div');
843
- descEl.className = 'agent-row-desc';
879
+ var descEl = document.createElement('span');
880
+ descEl.className = 'flm-list-item-secondary flm-text--nowrap';
881
+ descEl.style.cssText = 'flex:1;min-width:0;';
844
882
  descEl.textContent = a.description || '';
845
883
 
846
884
  info.appendChild(nameEl);
@@ -848,21 +886,21 @@ function renderAgentList() {
848
886
  row.appendChild(info);
849
887
 
850
888
  // Badge
851
- var badge = document.createElement('div');
852
- badge.className = 'agent-row-badge';
889
+ var badge = document.createElement('span');
890
+ badge.className = 'flm-text flm-text--small flm-text--secondary';
891
+ badge.style.cssText = 'background:var(--defaultHoverBackground);padding:2px 8px;border-radius:10px;white-space:nowrap;flex-shrink:0;';
853
892
  badge.textContent = a.provider === 'openclaw' ? 'OpenClaw' : 'A2A';
854
893
  row.appendChild(badge);
855
894
 
856
895
  // Actions
857
896
  var actions = document.createElement('div');
858
- actions.className = 'agent-row-actions';
897
+ actions.style.cssText = 'display:flex;align-items:center;gap:8px;flex-shrink:0;';
859
898
 
860
899
  // Chat or Reconnect button (depending on connection state)
861
900
  var isDisconnected = a.provider === 'openclaw' && a.enabled !== false && !a.connected;
862
901
  var chatBtn = document.createElement('button');
863
- chatBtn.className = 'filter-btn';
864
- chatBtn.style.fontSize = '12px';
865
- chatBtn.style.padding = '4px 10px';
902
+ chatBtn.className = 'flm-button flm-button--subtle';
903
+ chatBtn.style.cssText = 'font-size:12px;padding:4px 10px;';
866
904
  if (isDisconnected) {
867
905
  chatBtn.textContent = 'Reconnect';
868
906
  chatBtn.addEventListener('click', (function(agent) {
@@ -886,10 +924,9 @@ function renderAgentList() {
886
924
 
887
925
  // Edit button
888
926
  var editBtn = document.createElement('button');
889
- editBtn.className = 'filter-btn';
927
+ editBtn.className = 'flm-button flm-button--subtle';
890
928
  editBtn.textContent = 'Edit';
891
- editBtn.style.fontSize = '12px';
892
- editBtn.style.padding = '4px 10px';
929
+ editBtn.style.cssText = 'font-size:12px;padding:4px 10px;';
893
930
  editBtn.addEventListener('click', function(e) {
894
931
  e.stopPropagation();
895
932
  openAgentEditModal(a);
@@ -898,9 +935,10 @@ function renderAgentList() {
898
935
 
899
936
  // Enable/disable toggle
900
937
  var toggleLabel = document.createElement('label');
901
- toggleLabel.className = 'toggle-switch';
938
+ toggleLabel.className = 'flm-toggle flm-toggle--inline';
902
939
  var toggleInput = document.createElement('input');
903
940
  toggleInput.type = 'checkbox';
941
+ toggleInput.className = 'flm-toggle-input';
904
942
  toggleInput.checked = a.enabled !== false;
905
943
  toggleInput.addEventListener('click', function(e) { e.stopPropagation(); });
906
944
  toggleInput.addEventListener('change', (function(agentId, inp) {
@@ -912,10 +950,13 @@ function renderAgentList() {
912
950
  }).then(function() { loadAgents(); });
913
951
  };
914
952
  })(a.id, toggleInput));
915
- var toggleSlider = document.createElement('span');
916
- toggleSlider.className = 'toggle-slider';
953
+ var toggleTrack = document.createElement('span');
954
+ toggleTrack.className = 'flm-toggle-track';
955
+ var toggleThumb = document.createElement('span');
956
+ toggleThumb.className = 'flm-toggle-thumb';
957
+ toggleTrack.appendChild(toggleThumb);
917
958
  toggleLabel.appendChild(toggleInput);
918
- toggleLabel.appendChild(toggleSlider);
959
+ toggleLabel.appendChild(toggleTrack);
919
960
  actions.appendChild(toggleLabel);
920
961
 
921
962
  row.appendChild(actions);
@@ -1049,8 +1090,8 @@ document.getElementById('agentChatInput').addEventListener('keydown', function(e
1049
1090
 
1050
1091
  function setAgentType(type) {
1051
1092
  currentAgentType = type;
1052
- document.getElementById('agentTypeA2A').className = 'filter-btn' + (type === 'a2a' ? ' active' : '');
1053
- document.getElementById('agentTypeOpenClaw').className = 'filter-btn' + (type === 'openclaw' ? ' active' : '');
1093
+ document.getElementById('agentTypeA2A').className = 'flm-pivot-tab' + (type === 'a2a' ? ' flm-pivot-tab--active' : '');
1094
+ document.getElementById('agentTypeOpenClaw').className = 'flm-pivot-tab' + (type === 'openclaw' ? ' flm-pivot-tab--active' : '');
1054
1095
  document.getElementById('agentTokenGroup').style.display = type === 'openclaw' ? '' : 'none';
1055
1096
  document.getElementById('agentSessionKeyGroup').style.display = type === 'openclaw' ? '' : 'none';
1056
1097
  document.getElementById('agentSshTunnelGroup').style.display = type === 'openclaw' ? '' : 'none';
@@ -1083,7 +1124,7 @@ function resetAgentModal() {
1083
1124
  function openAgentAddModal() {
1084
1125
  resetAgentModal();
1085
1126
  document.getElementById('agentModalTitle').textContent = 'Add Agent';
1086
- document.getElementById('agentModal').style.display = 'flex';
1127
+ document.getElementById('agentModal').classList.add('flm-dialog-overlay--open');
1087
1128
  }
1088
1129
 
1089
1130
  function openAgentEditModal(agent) {
@@ -1110,11 +1151,11 @@ function openAgentEditModal(agent) {
1110
1151
  }
1111
1152
  }
1112
1153
  document.getElementById('agentRemoveBtn').style.display = '';
1113
- document.getElementById('agentModal').style.display = 'flex';
1154
+ document.getElementById('agentModal').classList.add('flm-dialog-overlay--open');
1114
1155
  }
1115
1156
 
1116
1157
  function closeAgentModal() {
1117
- document.getElementById('agentModal').style.display = 'none';
1158
+ document.getElementById('agentModal').classList.remove('flm-dialog-overlay--open');
1118
1159
  resetAgentModal();
1119
1160
  }
1120
1161
 
@@ -1130,7 +1171,7 @@ function renderSkillsList(skills) {
1130
1171
  container.innerHTML = skills.map(function(s) {
1131
1172
  var label = s.name || s.id || 'Skill';
1132
1173
  var desc = s.description ? ' — ' + s.description : '';
1133
- return '<span style="display:inline-block;padding:2px 8px;margin:2px;border-radius:8px;background:rgba(138,43,226,.15);font-size:11px;">' + label + desc + '</span>';
1174
+ return '<span style="display:inline-block;padding:2px 8px;margin:2px;border-radius:8px;background:var(--defaultHoverBackground);font-size:11px;">' + label + desc + '</span>';
1134
1175
  }).join('');
1135
1176
  }
1136
1177
 
@@ -1310,6 +1351,7 @@ function saveAllSettings() {
1310
1351
  var body = {
1311
1352
  version: 2,
1312
1353
  theme: document.getElementById('theme').value,
1354
+ toolbarPosition: document.getElementById('toolbarPosition').value,
1313
1355
  models: models,
1314
1356
  features: []
1315
1357
  };
@@ -1320,6 +1362,7 @@ function saveAllSettings() {
1320
1362
  body: JSON.stringify(body)
1321
1363
  }).then(function(response) {
1322
1364
  if (response.ok || response.redirected) {
1365
+ settingsDirty = false;
1323
1366
  if (wizardActive) {
1324
1367
  window.location.href = '/builder?firstRun=true';
1325
1368
  } else {
@@ -1334,8 +1377,65 @@ function saveAllSettings() {
1334
1377
  });
1335
1378
  }
1336
1379
 
1337
- document.querySelectorAll('.apply-btn').forEach(function(btn) {
1338
- btn.addEventListener('click', saveAllSettings);
1380
+ var settingsDirty = false;
1381
+ var applyBtn = document.getElementById('applyBtn');
1382
+
1383
+ function markDirty() {
1384
+ settingsDirty = true;
1385
+ applyBtn.classList.add('flm-button--primary');
1386
+ }
1387
+
1388
+ applyBtn.addEventListener('click', saveAllSettings);
1389
+
1390
+ // --- Unsaved-changes confirmation dialog ---
1391
+ var unsavedOverlay = document.getElementById('unsavedDialog');
1392
+ var pendingNavUrl = null;
1393
+
1394
+ function showUnsavedDialog(url) {
1395
+ pendingNavUrl = url;
1396
+ unsavedOverlay.classList.add('flm-dialog-overlay--open');
1397
+ }
1398
+
1399
+ function hideUnsavedDialog() {
1400
+ unsavedOverlay.classList.remove('flm-dialog-overlay--open');
1401
+ pendingNavUrl = null;
1402
+ }
1403
+
1404
+ document.getElementById('unsavedStayBtn').addEventListener('click', hideUnsavedDialog);
1405
+ document.getElementById('unsavedLeaveBtn').addEventListener('click', function() {
1406
+ var url = pendingNavUrl; // capture before hideUnsavedDialog nulls it
1407
+ settingsDirty = false; // disarm beforeunload
1408
+ hideUnsavedDialog();
1409
+ if (url) window.location.href = url;
1410
+ });
1411
+
1412
+ // Light-dismiss (click overlay backdrop)
1413
+ var unsavedMouseDownTarget = null;
1414
+ unsavedOverlay.addEventListener('mousedown', function(e) { unsavedMouseDownTarget = e.target; });
1415
+ unsavedOverlay.addEventListener('click', function(e) {
1416
+ if (e.target === this && unsavedMouseDownTarget === this) hideUnsavedDialog();
1417
+ unsavedMouseDownTarget = null;
1418
+ });
1419
+
1420
+ // Guard in-page navigation links
1421
+ function guardNavigation(el, url) {
1422
+ el.addEventListener('click', function(e) {
1423
+ if (!settingsDirty) return; // clean — let default behavior through
1424
+ e.preventDefault();
1425
+ e.stopImmediatePropagation(); // block page-script handlers from navigating
1426
+ showUnsavedDialog(url);
1427
+ }, true); // capture phase — run before other handlers
1428
+ }
1429
+
1430
+ guardNavigation(document.getElementById('pagesLink'), '/pages');
1431
+ guardNavigation(document.getElementById('pagesBtn'), '/pages');
1432
+
1433
+ // Fallback for browser close / address-bar navigation only.
1434
+ // Skip when the FluentLM dialog is already handling the confirmation.
1435
+ window.addEventListener('beforeunload', function(e) {
1436
+ if (settingsDirty && !unsavedOverlay.classList.contains('flm-dialog-overlay--open')) {
1437
+ e.preventDefault();
1438
+ }
1339
1439
  });
1340
1440
 
1341
1441
  // --- Provider helpers ---
@@ -1450,9 +1550,9 @@ function updateWizardVisibility() {
1450
1550
  }
1451
1551
 
1452
1552
  function updateApplyButton() {
1453
- var btns = document.querySelectorAll('.apply-btn');
1553
+ var btn = document.getElementById('applyBtn');
1454
1554
  if (!wizardActive) {
1455
- btns.forEach(function(b) { b.disabled = false; });
1555
+ btn.disabled = false;
1456
1556
  return;
1457
1557
  }
1458
1558
  var hasProvider = !!document.getElementById('provider-builder').value;
@@ -1460,7 +1560,8 @@ function updateApplyButton() {
1460
1560
  var hasModel = !!document.getElementById('model-builder').value;
1461
1561
  var hasTokens = !!document.getElementById('maxTokens-builder').value;
1462
1562
  var ready = hasProvider && hasKey && hasModel && hasTokens;
1463
- btns.forEach(function(b) { b.disabled = !ready; });
1563
+ btn.disabled = !ready;
1564
+ if (ready) btn.classList.add('flm-button--primary');
1464
1565
  }
1465
1566
 
1466
1567
  function autoPopulateChatCard() {
@@ -1555,10 +1656,23 @@ Promise.all([
1555
1656
  return '<option value="' + t + '">' + t + '</option>';
1556
1657
  }).join('');
1557
1658
  document.getElementById('theme').value = currentTheme;
1659
+ document.getElementById('toolbarPosition').value = data.toolbarPosition || 'left';
1558
1660
 
1559
1661
  loadConnectors();
1560
1662
  loadAgents();
1561
1663
 
1664
+ // Attach dirty-tracking listeners to all settings controls
1665
+ ['theme', 'toolbarPosition', 'provider-builder', 'serviceApiKey-builder', 'model-builder',
1666
+ 'maxTokens-builder', 'instructions-builder', 'provider-chat',
1667
+ 'serviceApiKey-chat', 'model-chat', 'maxTokens-chat', 'instructions-chat'
1668
+ ].forEach(function(id) {
1669
+ var el = document.getElementById(id);
1670
+ if (el) {
1671
+ el.addEventListener('input', markDirty);
1672
+ el.addEventListener('change', markDirty);
1673
+ }
1674
+ });
1675
+
1562
1676
  var isFirstRun = params.get('firstRun') === '1';
1563
1677
 
1564
1678
  if (!isConfigured) {
@@ -1599,7 +1713,7 @@ Promise.all([
1599
1713
  openSection('models');
1600
1714
 
1601
1715
  // Disable other accordion tabs
1602
- document.querySelector('.accordion-section[data-section="general"]').classList.add('disabled');
1716
+ document.querySelector('.accordion-section[data-section="appearance"]').classList.add('disabled');
1603
1717
  document.querySelector('.accordion-section[data-section="connectors"]').classList.add('disabled');
1604
1718
  document.querySelector('.accordion-section[data-section="agents"]').classList.add('disabled');
1605
1719
 
@@ -1625,8 +1739,8 @@ Promise.all([
1625
1739
  builderProviderSelect.insertBefore(placeholder, builderProviderSelect.firstChild);
1626
1740
  builderProviderSelect.value = '';
1627
1741
 
1628
- // Disable Apply buttons until configured
1629
- document.querySelectorAll('.apply-btn').forEach(function(b) { b.disabled = true; });
1742
+ // Disable Apply button until configured
1743
+ document.getElementById('applyBtn').disabled = true;
1630
1744
 
1631
1745
  updateWizardVisibility();
1632
1746
  }
@@ -1634,6 +1748,6 @@ Promise.all([
1634
1748
  console.error('Error fetching settings:', error);
1635
1749
  });
1636
1750
  </script>
1637
- <script id="page-helpers" src="/api/page-helpers.js?v=2" data-locked="true"></script>
1638
- <script id="page-script" src="/api/page-script.js?v=2" data-locked="true"></script>
1751
+ <script id="page-helpers" src="/api/page-helpers.js?v=3" data-locked="true"></script>
1752
+ <script id="page-script" src="/api/page-script.js?v=3" data-locked="true"></script>
1639
1753
  </body></html>