synthos 0.10.0 → 0.11.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 (312) hide show
  1. package/README.md +5 -5
  2. package/default-pages/elevenlabs_effects_studio/chat-history.json +1 -0
  3. package/default-pages/elevenlabs_effects_studio/page.html +1345 -1363
  4. package/default-pages/elevenlabs_effects_studio/page.json +13 -11
  5. package/default-pages/elevenlabs_voice_studio/chat-history.json +1 -0
  6. package/default-pages/elevenlabs_voice_studio/page.html +782 -801
  7. package/default-pages/elevenlabs_voice_studio/page.json +13 -11
  8. package/default-pages/json_tools/chat-history.json +1 -0
  9. package/default-pages/json_tools/page.html +70 -90
  10. package/default-pages/json_tools/page.json +12 -10
  11. package/default-pages/my_notes/chat-history.json +1 -0
  12. package/default-pages/my_notes/page.html +115 -131
  13. package/default-pages/my_notes/page.json +14 -12
  14. package/default-pages/neon_asteroids/chat-history.json +1 -0
  15. package/default-pages/neon_asteroids/page.html +1777 -1803
  16. package/default-pages/neon_asteroids/page.json +14 -12
  17. package/default-pages/oregon_trail/chat-history.json +1 -0
  18. package/default-pages/oregon_trail/page.html +290 -307
  19. package/default-pages/oregon_trail/page.json +14 -12
  20. package/default-pages/solar_explorer/chat-history.json +1 -0
  21. package/default-pages/solar_explorer/page.html +1929 -1951
  22. package/default-pages/solar_explorer/page.json +14 -12
  23. package/default-pages/solar_tutorial/chat-history.json +1 -0
  24. package/default-pages/solar_tutorial/page.html +464 -478
  25. package/default-pages/solar_tutorial/page.json +12 -10
  26. package/default-pages/us_map/chat-history.json +1 -0
  27. package/default-pages/us_map/page.html +170 -193
  28. package/default-pages/us_map/page.json +14 -12
  29. package/default-pages/us_map/page.light.png +0 -0
  30. package/default-pages/us_map_1850/chat-history.json +1 -0
  31. package/default-pages/us_map_1850/page.html +302 -326
  32. package/default-pages/us_map_1850/page.json +14 -12
  33. package/default-pages/western_cities_1850/chat-history.json +1 -0
  34. package/default-pages/western_cities_1850/page.html +503 -527
  35. package/default-pages/western_cities_1850/page.json +14 -12
  36. package/default-themes/aurora-dawn.v3.css +15 -14
  37. package/default-themes/aurora-dusk.v3.css +26 -26
  38. package/default-themes/cosmos-dawn.v3.css +15 -14
  39. package/default-themes/cosmos-dusk.v3.css +26 -26
  40. package/default-themes/elemental-dawn.v3.css +200 -0
  41. package/default-themes/nebula-dawn.v3.css +15 -14
  42. package/default-themes/nebula-dusk.v3.css +24 -24
  43. package/default-themes/solar-flare-dawn.v3.css +15 -14
  44. package/default-themes/solar-flare-dusk.v3.css +26 -26
  45. package/dist/builders/anthropic.d.ts +26 -2
  46. package/dist/builders/anthropic.d.ts.map +1 -1
  47. package/dist/builders/anthropic.js +132 -31
  48. package/dist/builders/anthropic.js.map +1 -1
  49. package/dist/builders/claudecode.d.ts +13 -0
  50. package/dist/builders/claudecode.d.ts.map +1 -0
  51. package/dist/builders/claudecode.js +253 -0
  52. package/dist/builders/claudecode.js.map +1 -0
  53. package/dist/builders/index.d.ts +2 -1
  54. package/dist/builders/index.d.ts.map +1 -1
  55. package/dist/builders/index.js +8 -1
  56. package/dist/builders/index.js.map +1 -1
  57. package/dist/builders/openai.js +2 -1
  58. package/dist/builders/openai.js.map +1 -1
  59. package/dist/builders/types.d.ts +31 -7
  60. package/dist/builders/types.d.ts.map +1 -1
  61. package/dist/builders/types.js +60 -28
  62. package/dist/builders/types.js.map +1 -1
  63. package/dist/connectors/types.d.ts +8 -0
  64. package/dist/connectors/types.d.ts.map +1 -1
  65. package/dist/init.d.ts.map +1 -1
  66. package/dist/init.js +13 -6
  67. package/dist/init.js.map +1 -1
  68. package/dist/migrations.d.ts.map +1 -1
  69. package/dist/migrations.js +161 -14
  70. package/dist/migrations.js.map +1 -1
  71. package/dist/models/anthropic.d.ts +1 -0
  72. package/dist/models/anthropic.d.ts.map +1 -1
  73. package/dist/models/anthropic.js +129 -29
  74. package/dist/models/anthropic.js.map +1 -1
  75. package/dist/models/chainOfThought.d.ts.map +1 -1
  76. package/dist/models/chainOfThought.js +32 -19
  77. package/dist/models/chainOfThought.js.map +1 -1
  78. package/dist/models/index.d.ts +2 -2
  79. package/dist/models/index.d.ts.map +1 -1
  80. package/dist/models/index.js +2 -1
  81. package/dist/models/index.js.map +1 -1
  82. package/dist/models/providers.d.ts +1 -0
  83. package/dist/models/providers.d.ts.map +1 -1
  84. package/dist/models/providers.js +12 -4
  85. package/dist/models/providers.js.map +1 -1
  86. package/dist/models/types.d.ts +15 -1
  87. package/dist/models/types.d.ts.map +1 -1
  88. package/dist/models/types.js.map +1 -1
  89. package/dist/pages.d.ts +57 -8
  90. package/dist/pages.d.ts.map +1 -1
  91. package/dist/pages.js +258 -45
  92. package/dist/pages.js.map +1 -1
  93. package/dist/service/createCompletePrompt.d.ts.map +1 -1
  94. package/dist/service/createCompletePrompt.js +5 -0
  95. package/dist/service/createCompletePrompt.js.map +1 -1
  96. package/dist/service/mediaCache.d.ts +36 -0
  97. package/dist/service/mediaCache.d.ts.map +1 -0
  98. package/dist/service/mediaCache.js +182 -0
  99. package/dist/service/mediaCache.js.map +1 -0
  100. package/dist/service/pageValidator.d.ts +25 -0
  101. package/dist/service/pageValidator.d.ts.map +1 -0
  102. package/dist/service/pageValidator.js +315 -0
  103. package/dist/service/pageValidator.js.map +1 -0
  104. package/dist/service/server.d.ts.map +1 -1
  105. package/dist/service/server.js +4 -0
  106. package/dist/service/server.js.map +1 -1
  107. package/dist/service/sharedTableSchema.d.ts +73 -0
  108. package/dist/service/sharedTableSchema.d.ts.map +1 -0
  109. package/dist/service/sharedTableSchema.js +206 -0
  110. package/dist/service/sharedTableSchema.js.map +1 -0
  111. package/dist/service/transformPage.d.ts +49 -11
  112. package/dist/service/transformPage.d.ts.map +1 -1
  113. package/dist/service/transformPage.js +354 -241
  114. package/dist/service/transformPage.js.map +1 -1
  115. package/dist/service/useApiRoutes.d.ts.map +1 -1
  116. package/dist/service/useApiRoutes.js +288 -34
  117. package/dist/service/useApiRoutes.js.map +1 -1
  118. package/dist/service/useConnectorRoutes.d.ts.map +1 -1
  119. package/dist/service/useConnectorRoutes.js +170 -32
  120. package/dist/service/useConnectorRoutes.js.map +1 -1
  121. package/dist/service/useDataRoutes.d.ts.map +1 -1
  122. package/dist/service/useDataRoutes.js +59 -2
  123. package/dist/service/useDataRoutes.js.map +1 -1
  124. package/dist/service/useExtractRoutes.d.ts +4 -0
  125. package/dist/service/useExtractRoutes.d.ts.map +1 -0
  126. package/dist/service/useExtractRoutes.js +304 -0
  127. package/dist/service/useExtractRoutes.js.map +1 -0
  128. package/dist/service/usePageRoutes.d.ts +17 -0
  129. package/dist/service/usePageRoutes.d.ts.map +1 -1
  130. package/dist/service/usePageRoutes.js +1385 -483
  131. package/dist/service/usePageRoutes.js.map +1 -1
  132. package/dist/service/useSharedDataRoutes.d.ts.map +1 -1
  133. package/dist/service/useSharedDataRoutes.js +54 -2
  134. package/dist/service/useSharedDataRoutes.js.map +1 -1
  135. package/dist/settings.d.ts +27 -0
  136. package/dist/settings.d.ts.map +1 -1
  137. package/dist/settings.js +40 -1
  138. package/dist/settings.js.map +1 -1
  139. package/dist/themes.d.ts +0 -5
  140. package/dist/themes.d.ts.map +1 -1
  141. package/dist/themes.js +3 -95
  142. package/dist/themes.js.map +1 -1
  143. package/migration-rules/v2-to-v3.md +277 -119
  144. package/package.json +5 -1
  145. package/{default-pages/application → required-pages/_shell}/page.html +56 -42
  146. package/required-pages/_shell/page.json +14 -0
  147. package/required-pages/_starters/page.html +534 -0
  148. package/required-pages/_starters/page.json +12 -0
  149. package/required-pages/builder/page.html +353 -43
  150. package/required-pages/builder/page.json +12 -10
  151. package/required-pages/pages/page.html +697 -924
  152. package/required-pages/pages/page.json +12 -10
  153. package/required-pages/settings/page.html +1879 -1753
  154. package/required-pages/settings/page.json +12 -10
  155. package/required-pages/synthos_apis/page.html +834 -845
  156. package/required-pages/synthos_apis/page.json +12 -10
  157. package/required-pages/synthos_scripts/page.html +74 -88
  158. package/required-pages/synthos_scripts/page.json +12 -10
  159. package/scripts/append-instructions.py +90 -0
  160. package/scripts/audit-instructions.py +76 -0
  161. package/scripts/cleanup-shell-markup.mjs +112 -0
  162. package/service-connectors/buffer/connector.json +46 -0
  163. package/service-connectors/canva/connector.json +67 -0
  164. package/service-connectors/elevenlabs/connector.json +1 -1
  165. package/src/builders/anthropic.ts +155 -30
  166. package/src/builders/claudecode.ts +310 -0
  167. package/src/builders/index.ts +7 -1
  168. package/src/builders/openai.ts +2 -1
  169. package/src/builders/types.ts +93 -32
  170. package/src/connectors/types.ts +8 -0
  171. package/src/init.ts +13 -7
  172. package/src/migrations.ts +187 -16
  173. package/src/models/anthropic.ts +140 -30
  174. package/src/models/chainOfThought.ts +33 -18
  175. package/src/models/index.ts +2 -2
  176. package/src/models/providers.ts +12 -3
  177. package/src/models/types.ts +21 -1
  178. package/src/pages.ts +271 -35
  179. package/src/service/createCompletePrompt.ts +6 -0
  180. package/src/service/mediaCache.ts +206 -0
  181. package/src/service/pageValidator.ts +337 -0
  182. package/src/service/server.ts +4 -0
  183. package/src/service/sharedTableSchema.ts +236 -0
  184. package/src/service/transformPage.ts +370 -260
  185. package/src/service/useApiRoutes.ts +282 -32
  186. package/src/service/useConnectorRoutes.ts +189 -34
  187. package/src/service/useDataRoutes.ts +198 -116
  188. package/src/service/useExtractRoutes.ts +331 -0
  189. package/src/service/usePageRoutes.ts +1411 -394
  190. package/src/service/useSharedDataRoutes.ts +184 -109
  191. package/src/settings.ts +65 -0
  192. package/src/themes.ts +78 -180
  193. package/starters/blank_starter/chat-history.json +1 -0
  194. package/starters/blank_starter/page.dark.png +0 -0
  195. package/starters/blank_starter/page.html +47 -0
  196. package/starters/blank_starter/page.json +13 -0
  197. package/starters/blank_starter/page.light.png +0 -0
  198. package/starters/calculator_starter/chat-history.json +1 -0
  199. package/starters/calculator_starter/page.dark.png +0 -0
  200. package/starters/calculator_starter/page.html +232 -0
  201. package/starters/calculator_starter/page.json +13 -0
  202. package/starters/calculator_starter/page.light.png +0 -0
  203. package/starters/calendar_starter/chat-history.json +1 -0
  204. package/starters/calendar_starter/page.dark.png +0 -0
  205. package/starters/calendar_starter/page.html +495 -0
  206. package/starters/calendar_starter/page.json +13 -0
  207. package/starters/calendar_starter/page.light.png +0 -0
  208. package/starters/chat_starter/chat-history.json +1 -0
  209. package/starters/chat_starter/page.dark.png +0 -0
  210. package/starters/chat_starter/page.html +351 -0
  211. package/starters/chat_starter/page.json +13 -0
  212. package/starters/chat_starter/page.light.png +0 -0
  213. package/starters/checklist_starter/chat-history.json +1 -0
  214. package/starters/checklist_starter/page.dark.png +0 -0
  215. package/starters/checklist_starter/page.html +437 -0
  216. package/starters/checklist_starter/page.json +13 -0
  217. package/starters/checklist_starter/page.light.png +0 -0
  218. package/starters/dashboard_starter/chat-history.json +1 -0
  219. package/starters/dashboard_starter/page.dark.png +0 -0
  220. package/starters/dashboard_starter/page.html +195 -0
  221. package/starters/dashboard_starter/page.json +13 -0
  222. package/starters/dashboard_starter/page.light.png +0 -0
  223. package/starters/form_starter/chat-history.json +1 -0
  224. package/starters/form_starter/page.dark.png +0 -0
  225. package/starters/form_starter/page.html +313 -0
  226. package/starters/form_starter/page.json +13 -0
  227. package/starters/form_starter/page.light.png +0 -0
  228. package/starters/gallery_starter/chat-history.json +1 -0
  229. package/starters/gallery_starter/page.dark.png +0 -0
  230. package/starters/gallery_starter/page.html +418 -0
  231. package/starters/gallery_starter/page.json +13 -0
  232. package/starters/gallery_starter/page.light.png +0 -0
  233. package/starters/generator_starter/chat-history.json +1 -0
  234. package/starters/generator_starter/page.dark.png +0 -0
  235. package/starters/generator_starter/page.html +261 -0
  236. package/starters/generator_starter/page.json +13 -0
  237. package/starters/generator_starter/page.light.png +0 -0
  238. package/starters/index.html +538 -0
  239. package/starters/kanban_starter/chat-history.json +1 -0
  240. package/starters/kanban_starter/page.dark.png +0 -0
  241. package/starters/kanban_starter/page.html +432 -0
  242. package/starters/kanban_starter/page.json +13 -0
  243. package/starters/kanban_starter/page.light.png +0 -0
  244. package/starters/presentation_builder/chat-history.json +1 -0
  245. package/starters/presentation_builder/page.dark.png +0 -0
  246. package/starters/presentation_builder/page.html +970 -0
  247. package/starters/presentation_builder/page.json +15 -0
  248. package/starters/presentation_builder/page.light.png +0 -0
  249. package/starters/presentation_builder/presentation_voice/voice_config.json +9 -0
  250. package/starters/pulse_starter/chat-history.json +1 -0
  251. package/starters/pulse_starter/page.dark.png +0 -0
  252. package/starters/pulse_starter/page.html +698 -0
  253. package/starters/pulse_starter/page.json +13 -0
  254. package/starters/pulse_starter/page.light.png +0 -0
  255. package/starters/quiz_starter/chat-history.json +1 -0
  256. package/starters/quiz_starter/page.dark.png +0 -0
  257. package/starters/quiz_starter/page.html +292 -0
  258. package/starters/quiz_starter/page.json +13 -0
  259. package/starters/quiz_starter/page.light.png +0 -0
  260. package/starters/reference_starter/chat-history.json +1 -0
  261. package/starters/reference_starter/page.dark.png +0 -0
  262. package/starters/reference_starter/page.html +250 -0
  263. package/starters/reference_starter/page.json +13 -0
  264. package/starters/reference_starter/page.light.png +0 -0
  265. package/starters/retro_game_starter/chat-history.json +1 -0
  266. package/starters/retro_game_starter/page.dark.png +0 -0
  267. package/{default-pages → starters}/retro_game_starter/page.html +1281 -1308
  268. package/starters/retro_game_starter/page.json +15 -0
  269. package/starters/retro_game_starter/page.light.png +0 -0
  270. package/starters/roster_starter/chat-history.json +1 -0
  271. package/starters/roster_starter/page.dark.png +0 -0
  272. package/starters/roster_starter/page.html +600 -0
  273. package/starters/roster_starter/page.json +13 -0
  274. package/starters/roster_starter/page.light.png +0 -0
  275. package/starters/server.js +182 -0
  276. package/starters/start.cmd +1 -0
  277. package/starters/timeline_starter/chat-history.json +1 -0
  278. package/starters/timeline_starter/page.dark.png +0 -0
  279. package/starters/timeline_starter/page.html +446 -0
  280. package/starters/timeline_starter/page.json +13 -0
  281. package/starters/timeline_starter/page.light.png +0 -0
  282. package/starters/tutorial_starter/chat-history.json +1 -0
  283. package/starters/tutorial_starter/page.dark.png +0 -0
  284. package/starters/tutorial_starter/page.html +283 -0
  285. package/starters/tutorial_starter/page.json +13 -0
  286. package/starters/tutorial_starter/page.light.png +0 -0
  287. package/static-files/agent.v3.js +122 -0
  288. package/static-files/connector.v3.js +48 -0
  289. package/static-files/extract.v3.js +188 -0
  290. package/static-files/helpers.v3.js +50 -6
  291. package/static-files/page-bridge.js +114 -0
  292. package/static-files/page.v3.js +1292 -1290
  293. package/static-files/script.v3.js +32 -0
  294. package/static-files/server.v3.js +89 -0
  295. package/static-files/shell-bridge.v3.js +174 -0
  296. package/static-files/shell-modals.v3.js +521 -0
  297. package/static-files/{shell.css → shell.v3.css} +271 -22
  298. package/static-files/shell.v3.js +1865 -0
  299. package/static-files/storage.v3.js +176 -0
  300. package/tests/anthropic.spec.ts +42 -7
  301. package/tests/builders.spec.ts +72 -4
  302. package/tests/pageValidator.spec.ts +548 -0
  303. package/tests/profiles.spec.ts +122 -0
  304. package/tests/providers.spec.ts +1 -1
  305. package/tests/sharedTableSchema.spec.ts +242 -0
  306. package/tests/transformPage.spec.ts +62 -81
  307. package/default-pages/application/page.json +0 -10
  308. package/default-pages/retro_game_starter/page.json +0 -12
  309. package/default-pages/sidebar_page/page.html +0 -51
  310. package/default-pages/sidebar_page/page.json +0 -10
  311. package/default-pages/two-panel_page/page.html +0 -68
  312. package/default-pages/two-panel_page/page.json +0 -10
@@ -0,0 +1,521 @@
1
+ /**
2
+ * shell-modals.v3.js — Shared dialog components for the SynthOS shell chrome.
3
+ *
4
+ * Runs in the parent frame alongside shell.v3.js. Must load BEFORE shell.v3.js so
5
+ * the open* functions are available when the toolbar wires up its click
6
+ * handlers.
7
+ *
8
+ * Exposes on window:
9
+ * __synthOSOpenSaveModal() — save-as-new-page (brainstorm-style)
10
+ * __synthOSOpenCopyModal(page) — copy-to-new-page (brainstorm-style)
11
+ * __synthOSOpenEditModal(page, onDone) — edit page metadata (FluentLM)
12
+ * __synthOSShowError(msg) — shared error dialog
13
+ *
14
+ * Both gallery (/pages context menu) and shell toolbar use these via
15
+ * postMessage shell:modal events — keeps all modal UI in one place.
16
+ */
17
+ (function() {
18
+ 'use strict';
19
+
20
+ // Read window.__shellInit lazily via getShellInit() — capturing it at IIFE
21
+ // load time risks racing the shell-init script injection.
22
+
23
+ // --- Helpers -------------------------------------------------------------
24
+
25
+ function extractHistory() {
26
+ var messages = [];
27
+ var chatMsgs = document.querySelectorAll('#chatMessages .chat-message');
28
+ for (var i = 0; i < chatMsgs.length; i++) {
29
+ var el = chatMsgs[i];
30
+ var strong = el.querySelector('strong');
31
+ if (!strong) continue;
32
+ var roleName = strong.textContent.trim().replace(/:$/, '');
33
+ var role = (roleName === 'User') ? 'user' : 'assistant';
34
+ var clone = el.cloneNode(true);
35
+ var s = clone.querySelector('strong');
36
+ if (s) s.remove();
37
+ var chipsEl = clone.querySelector('.suggestion-chips');
38
+ if (chipsEl) chipsEl.remove();
39
+ messages.push({ role: role, content: clone.textContent.trim() });
40
+ }
41
+ return messages;
42
+ }
43
+
44
+ function makeEl(html) {
45
+ var tmp = document.createElement('div');
46
+ tmp.innerHTML = html.trim();
47
+ return tmp.firstChild;
48
+ }
49
+
50
+ function wireMousedownDismiss(overlay, closeFn) {
51
+ var md = null;
52
+ overlay.addEventListener('mousedown', function(e) { md = e.target; });
53
+ overlay.addEventListener('click', function(e) {
54
+ if (e.target === overlay && md === overlay) closeFn();
55
+ md = null;
56
+ });
57
+ }
58
+
59
+ // --- Error Modal (shared, brainstorm-style) ------------------------------
60
+
61
+ var errorModal = makeEl(
62
+ '<div id="synthos-errorModal" class="modal-overlay">' +
63
+ '<div class="modal-content" style="max-width:400px;">' +
64
+ '<div class="modal-header">' +
65
+ '<span>Error</span>' +
66
+ '<button type="button" class="brainstorm-close-btn" id="synthos-errorCloseBtn">&times;</button>' +
67
+ '</div>' +
68
+ '<div class="modal-body" style="padding:16px 20px;">' +
69
+ '<p id="synthos-errorMessage" style="margin:0;color:var(--bodyText);"></p>' +
70
+ '</div>' +
71
+ '<div class="modal-footer" style="display:flex;justify-content:flex-end;padding:12px 20px;">' +
72
+ '<button type="button" class="brainstorm-send-btn" id="synthos-errorOkBtn">OK</button>' +
73
+ '</div>' +
74
+ '</div>' +
75
+ '</div>'
76
+ );
77
+ document.body.appendChild(errorModal);
78
+
79
+ function showError(msg) {
80
+ document.getElementById('synthos-errorMessage').textContent = msg;
81
+ errorModal.classList.add('show');
82
+ }
83
+ function closeError() { errorModal.classList.remove('show'); }
84
+ document.getElementById('synthos-errorCloseBtn').addEventListener('click', closeError);
85
+ document.getElementById('synthos-errorOkBtn').addEventListener('click', closeError);
86
+ wireMousedownDismiss(errorModal, closeError);
87
+
88
+ // --- Shared styles for Save/Copy advanced-options expando ----------------
89
+
90
+ var ADV_TOGGLE_STYLE = 'background:none;border:none;color:var(--bodySubtext);font-size:13px;cursor:pointer;padding:8px 0;display:flex;align-items:center;gap:6px;';
91
+ var ADV_ICON_STYLE = 'font-size:10px;transition:transform .2s;display:inline-block;';
92
+ var ADV_CONTENT_STYLE = 'display:none;padding-left:16px;border-left:2px solid var(--bodyDivider);margin-left:4px;';
93
+ var ADV_INNER_STACK_STYLE = 'display:flex;flex-direction:column;gap:12px;margin-top:12px;';
94
+
95
+ // --- Save Modal (brainstorm-style with Advanced Options expando) ---------
96
+
97
+ var saveModal = makeEl(
98
+ '<div id="synthos-saveModal" class="modal-overlay">' +
99
+ '<div class="modal-content" style="max-width:480px;">' +
100
+ '<div class="modal-header">' +
101
+ '<span>Save Page</span>' +
102
+ '<button type="button" class="brainstorm-close-btn" id="synthos-saveCloseBtn">&times;</button>' +
103
+ '</div>' +
104
+ '<div class="modal-body" style="display:flex;flex-direction:column;gap:12px;padding:16px 20px;">' +
105
+ '<div>' +
106
+ '<label style="display:block;margin-bottom:4px;font-size:13px;color:var(--bodySubtext);">Display Title <span style="color:var(--themePrimary);">*</span></label>' +
107
+ '<input type="text" id="synthos-saveTitleInput" class="brainstorm-input" placeholder="Enter a display title..." style="width:100%;box-sizing:border-box;">' +
108
+ '<div id="synthos-saveTitleError" style="color:#ff6b6b;font-size:12px;margin-top:4px;display:none;">Title is required</div>' +
109
+ '</div>' +
110
+ '<div>' +
111
+ '<label style="display:block;margin-bottom:4px;font-size:13px;color:var(--bodySubtext);">Categories <span style="color:var(--themePrimary);">*</span></label>' +
112
+ '<input type="text" id="synthos-saveCategoriesInput" class="brainstorm-input" placeholder="e.g. Tool, Game, Utility" style="width:100%;box-sizing:border-box;">' +
113
+ '<div id="synthos-saveCategoriesError" style="color:#ff6b6b;font-size:12px;margin-top:4px;display:none;">At least one category is required</div>' +
114
+ '</div>' +
115
+ '<div class="synthos-adv">' +
116
+ '<button type="button" id="synthos-saveAdvToggle" style="' + ADV_TOGGLE_STYLE + '">' +
117
+ '<span id="synthos-saveAdvIcon" style="' + ADV_ICON_STYLE + '">&#9654;</span> Advanced Options' +
118
+ '</button>' +
119
+ '<div id="synthos-saveAdvContent" style="' + ADV_CONTENT_STYLE + '">' +
120
+ '<div style="' + ADV_INNER_STACK_STYLE + '">' +
121
+ '<div>' +
122
+ '<label style="display:block;margin-bottom:4px;font-size:13px;color:var(--bodySubtext);">Greeting <span style="font-size:11px;opacity:0.7;">(optional)</span></label>' +
123
+ '<input type="text" id="synthos-saveGreetingInput" class="brainstorm-input" placeholder="Enter a custom greeting..." style="width:100%;box-sizing:border-box;">' +
124
+ '<div style="font-size:11px;color:var(--bodySubtext);margin-top:4px;opacity:0.7;">Initial message shown when the page loads.</div>' +
125
+ '</div>' +
126
+ '</div>' +
127
+ '</div>' +
128
+ '</div>' +
129
+ '</div>' +
130
+ '<div class="modal-footer" style="display:flex;justify-content:flex-end;gap:8px;padding:12px 20px;">' +
131
+ '<button type="button" class="brainstorm-send-btn" id="synthos-saveCancelBtn" style="background:transparent;border:1px solid var(--bodyDivider);color:var(--bodySubtext);">Cancel</button>' +
132
+ '<button type="button" class="brainstorm-send-btn" id="synthos-saveConfirmBtn">Save</button>' +
133
+ '</div>' +
134
+ '</div>' +
135
+ '</div>'
136
+ );
137
+ document.body.appendChild(saveModal);
138
+
139
+ var saveTitleInput = document.getElementById('synthos-saveTitleInput');
140
+ var saveCategoriesInput = document.getElementById('synthos-saveCategoriesInput');
141
+ var saveGreetingInput = document.getElementById('synthos-saveGreetingInput');
142
+ var saveTitleError = document.getElementById('synthos-saveTitleError');
143
+ var saveCategoriesError = document.getElementById('synthos-saveCategoriesError');
144
+ var saveConfirmBtn = document.getElementById('synthos-saveConfirmBtn');
145
+ var saveAdvContent = document.getElementById('synthos-saveAdvContent');
146
+ var saveAdvIcon = document.getElementById('synthos-saveAdvIcon');
147
+
148
+ function getShellInit() { return window.__shellInit || {}; }
149
+ function getPageName() { return getShellInit().page || ''; }
150
+
151
+ function openSaveModal() {
152
+ var si = getShellInit();
153
+ var isBuilder = si.isBuilder || false;
154
+ saveTitleInput.value = isBuilder ? '' : (si.pageTitle || '');
155
+ saveCategoriesInput.value = isBuilder ? '' : (si.pageCategories || []).join(', ');
156
+ saveGreetingInput.value = isBuilder ? '' : (si.greeting || '');
157
+ saveTitleError.style.display = 'none';
158
+ saveCategoriesError.style.display = 'none';
159
+ saveConfirmBtn.disabled = false;
160
+ saveConfirmBtn.textContent = 'Save';
161
+ // Advanced Options always collapsed on open
162
+ saveAdvContent.style.display = 'none';
163
+ saveAdvIcon.style.transform = '';
164
+ saveModal.classList.add('show');
165
+ setTimeout(function() { saveTitleInput.focus(); }, 0);
166
+ }
167
+ function closeSaveModal() { saveModal.classList.remove('show'); }
168
+
169
+ function submitSave() {
170
+ var title = saveTitleInput.value.trim();
171
+ var cats = saveCategoriesInput.value.trim();
172
+ var greeting = saveGreetingInput.value.trim();
173
+ var valid = true;
174
+ if (!title) { saveTitleError.style.display = 'block'; valid = false; } else { saveTitleError.style.display = 'none'; }
175
+ if (!cats) { saveCategoriesError.style.display = 'block'; valid = false; } else { saveCategoriesError.style.display = 'none'; }
176
+ if (!valid) return;
177
+ var categories = cats.split(',').map(function(c) { return c.trim(); }).filter(Boolean);
178
+
179
+ saveConfirmBtn.disabled = true;
180
+ saveConfirmBtn.textContent = 'Saving...';
181
+
182
+ var body = { title: title, categories: categories };
183
+ if (greeting) body.greeting = greeting;
184
+ body.history = extractHistory();
185
+
186
+ var activePageName = getPageName();
187
+ var activeShellInit = getShellInit();
188
+ fetch('/' + activePageName + '/save', {
189
+ method: 'POST',
190
+ headers: { 'Content-Type': 'application/json' },
191
+ body: JSON.stringify(body)
192
+ })
193
+ .then(function(res) { return res.json().then(function(data) { return { ok: res.ok, data: data }; }); })
194
+ .then(function(result) {
195
+ if (result.ok && result.data.redirect) {
196
+ if (activeShellInit.noPersistHistory) {
197
+ fetch('/' + activePageName + '/reset', { method: 'POST' })
198
+ .finally(function() { window.location.href = result.data.redirect; });
199
+ } else {
200
+ window.location.href = result.data.redirect;
201
+ }
202
+ } else {
203
+ closeSaveModal();
204
+ showError(result.data.error || 'An unknown error occurred');
205
+ saveConfirmBtn.disabled = false;
206
+ saveConfirmBtn.textContent = 'Save';
207
+ }
208
+ })
209
+ .catch(function(err) {
210
+ closeSaveModal();
211
+ showError('Network error: ' + err.message);
212
+ saveConfirmBtn.disabled = false;
213
+ saveConfirmBtn.textContent = 'Save';
214
+ });
215
+ }
216
+
217
+ document.getElementById('synthos-saveCloseBtn').addEventListener('click', closeSaveModal);
218
+ document.getElementById('synthos-saveCancelBtn').addEventListener('click', closeSaveModal);
219
+ saveConfirmBtn.addEventListener('click', submitSave);
220
+ wireMousedownDismiss(saveModal, closeSaveModal);
221
+ document.getElementById('synthos-saveAdvToggle').addEventListener('click', function() {
222
+ if (saveAdvContent.style.display === 'none') {
223
+ saveAdvContent.style.display = 'block';
224
+ saveAdvIcon.style.transform = 'rotate(90deg)';
225
+ } else {
226
+ saveAdvContent.style.display = 'none';
227
+ saveAdvIcon.style.transform = '';
228
+ }
229
+ });
230
+ saveTitleInput.addEventListener('keydown', function(e) { if (e.key === 'Enter') { e.preventDefault(); submitSave(); } });
231
+ saveCategoriesInput.addEventListener('keydown', function(e) { if (e.key === 'Enter') { e.preventDefault(); submitSave(); } });
232
+ saveGreetingInput.addEventListener('keydown', function(e) { if (e.key === 'Enter') { e.preventDefault(); submitSave(); } });
233
+
234
+ // --- Copy Modal (brainstorm-style — matches Save dialog) -----------------
235
+
236
+ var copyModal = makeEl(
237
+ '<div id="synthos-copyModal" class="modal-overlay">' +
238
+ '<div class="modal-content" style="max-width:480px;">' +
239
+ '<div class="modal-header">' +
240
+ '<span>Copy to New Page</span>' +
241
+ '<button type="button" class="brainstorm-close-btn" id="synthos-copyCloseBtn">&times;</button>' +
242
+ '</div>' +
243
+ '<div class="modal-body" style="display:flex;flex-direction:column;gap:12px;padding:16px 20px;">' +
244
+ '<div>' +
245
+ '<label style="display:block;margin-bottom:4px;font-size:13px;color:var(--bodySubtext);">Display Title <span style="color:var(--themePrimary);">*</span></label>' +
246
+ '<input type="text" id="synthos-copyTitle" class="brainstorm-input" placeholder="Enter a display title..." style="width:100%;box-sizing:border-box;">' +
247
+ '<div style="font-size:11px;color:var(--bodySubtext);margin-top:4px;opacity:0.7;">Source: <span id="synthos-copySource"></span></div>' +
248
+ '<div id="synthos-copyTitleError" style="color:#ff6b6b;font-size:12px;margin-top:4px;display:none;">Title is required</div>' +
249
+ '</div>' +
250
+ '<div>' +
251
+ '<label style="display:block;margin-bottom:4px;font-size:13px;color:var(--bodySubtext);">Categories <span style="color:var(--themePrimary);">*</span></label>' +
252
+ '<input type="text" id="synthos-copyCategories" class="brainstorm-input" placeholder="e.g. Tool, Game, Utility" style="width:100%;box-sizing:border-box;">' +
253
+ '<div id="synthos-copyCategoriesError" style="color:#ff6b6b;font-size:12px;margin-top:4px;display:none;">At least one category is required</div>' +
254
+ '</div>' +
255
+ '<div class="synthos-adv">' +
256
+ '<button type="button" id="synthos-copyAdvToggle" style="' + ADV_TOGGLE_STYLE + '">' +
257
+ '<span id="synthos-copyAdvIcon" style="' + ADV_ICON_STYLE + '">&#9654;</span> Advanced Options' +
258
+ '</button>' +
259
+ '<div id="synthos-copyAdvContent" style="' + ADV_CONTENT_STYLE + '">' +
260
+ '<div style="' + ADV_INNER_STACK_STYLE + '">' +
261
+ '<div>' +
262
+ '<label style="display:block;margin-bottom:4px;font-size:13px;color:var(--bodySubtext);">Custom Page ID <span style="font-size:11px;opacity:0.7;">(optional)</span></label>' +
263
+ '<input type="text" id="synthos-copyPageId" class="brainstorm-input" placeholder="Auto-generated from title..." style="width:100%;box-sizing:border-box;">' +
264
+ '<div style="font-size:11px;color:var(--bodySubtext);margin-top:4px;opacity:0.7;">Used in the URL. Leave blank to auto-generate from title.</div>' +
265
+ '</div>' +
266
+ '<div>' +
267
+ '<label style="display:block;margin-bottom:4px;font-size:13px;color:var(--bodySubtext);">Greeting <span style="font-size:11px;opacity:0.7;">(optional)</span></label>' +
268
+ '<input type="text" id="synthos-copyGreeting" class="brainstorm-input" placeholder="Initial chat message..." style="width:100%;box-sizing:border-box;">' +
269
+ '<div style="font-size:11px;color:var(--bodySubtext);margin-top:4px;opacity:0.7;">Shown when the new page first loads.</div>' +
270
+ '</div>' +
271
+ '</div>' +
272
+ '</div>' +
273
+ '</div>' +
274
+ '</div>' +
275
+ '<div class="modal-footer" style="display:flex;justify-content:flex-end;gap:8px;padding:12px 20px;">' +
276
+ '<button type="button" class="brainstorm-send-btn" id="synthos-copyCancelBtn" style="background:transparent;border:1px solid var(--bodyDivider);color:var(--bodySubtext);">Cancel</button>' +
277
+ '<button type="button" class="brainstorm-send-btn" id="synthos-copyConfirmBtn">Create Copy</button>' +
278
+ '</div>' +
279
+ '</div>' +
280
+ '</div>'
281
+ );
282
+ document.body.appendChild(copyModal);
283
+
284
+ var copyTitleInput = document.getElementById('synthos-copyTitle');
285
+ var copyCategoriesInput = document.getElementById('synthos-copyCategories');
286
+ var copyPageIdInput = document.getElementById('synthos-copyPageId');
287
+ var copyGreetingInput = document.getElementById('synthos-copyGreeting');
288
+ var copySourceDisplay = document.getElementById('synthos-copySource');
289
+ var copyTitleError = document.getElementById('synthos-copyTitleError');
290
+ var copyCategoriesError = document.getElementById('synthos-copyCategoriesError');
291
+ var copyConfirmBtn = document.getElementById('synthos-copyConfirmBtn');
292
+ var copyAdvContent = document.getElementById('synthos-copyAdvContent');
293
+ var copyAdvIcon = document.getElementById('synthos-copyAdvIcon');
294
+ var copySource = null;
295
+
296
+ function openCopyModal(pageRecord) {
297
+ copySource = pageRecord || {};
298
+ copySourceDisplay.textContent = copySource.title || copySource.name || '';
299
+ copyTitleInput.value = '';
300
+ copyCategoriesInput.value = (copySource.categories || []).join(', ');
301
+ copyPageIdInput.value = '';
302
+ copyGreetingInput.value = copySource.greeting || '';
303
+ copyTitleError.style.display = 'none';
304
+ copyCategoriesError.style.display = 'none';
305
+ copyAdvContent.style.display = 'none';
306
+ copyAdvIcon.style.transform = '';
307
+ copyConfirmBtn.disabled = false;
308
+ copyConfirmBtn.textContent = 'Create Copy';
309
+ copyModal.classList.add('show');
310
+ setTimeout(function() { copyTitleInput.focus(); }, 0);
311
+ }
312
+ function closeCopyModal() {
313
+ copyModal.classList.remove('show');
314
+ copySource = null;
315
+ }
316
+
317
+ function submitCopy() {
318
+ if (!copySource || !copySource.name) return;
319
+ var newTitle = copyTitleInput.value.trim();
320
+ var newName = copyPageIdInput.value.trim();
321
+ var greeting = copyGreetingInput.value.trim();
322
+ var cats = copyCategoriesInput.value.trim();
323
+ var valid = true;
324
+ if (!newTitle && !newName) { copyTitleError.style.display = 'block'; valid = false; } else { copyTitleError.style.display = 'none'; }
325
+ if (!cats) { copyCategoriesError.style.display = 'block'; valid = false; } else { copyCategoriesError.style.display = 'none'; }
326
+ if (!valid) return;
327
+ if (!newName) newName = newTitle;
328
+ newName = newName.toLowerCase().replace(/\s+/g, '_').replace(/[^a-z0-9_-]/g, '');
329
+ if (!newName) { showError('Page name must contain at least one valid character (letters, numbers, hyphens, or underscores).'); copyTitleInput.focus(); return; }
330
+ var newCategories = cats.split(',').map(function(c) { return c.trim(); }).filter(Boolean);
331
+
332
+ copyConfirmBtn.disabled = true;
333
+ copyConfirmBtn.textContent = 'Copying...';
334
+
335
+ var body = { name: newName, title: newTitle, categories: newCategories };
336
+ if (greeting) body.greeting = greeting;
337
+
338
+ fetch('/api/pages/' + encodeURIComponent(copySource.name) + '/copy', {
339
+ method: 'POST',
340
+ headers: { 'Content-Type': 'application/json' },
341
+ body: JSON.stringify(body)
342
+ })
343
+ .then(function(res) { return res.json().then(function(data) { return { ok: res.ok, data: data }; }); })
344
+ .then(function(result) {
345
+ if (!result.ok) {
346
+ copyConfirmBtn.disabled = false;
347
+ copyConfirmBtn.textContent = 'Create Copy';
348
+ showError(result.data.error || 'Failed to copy page');
349
+ return;
350
+ }
351
+ closeCopyModal();
352
+ window.location.href = '/' + newName;
353
+ })
354
+ .catch(function(err) {
355
+ copyConfirmBtn.disabled = false;
356
+ copyConfirmBtn.textContent = 'Create Copy';
357
+ showError('Failed to copy page: ' + err.message);
358
+ });
359
+ }
360
+
361
+ document.getElementById('synthos-copyCloseBtn').addEventListener('click', closeCopyModal);
362
+ document.getElementById('synthos-copyCancelBtn').addEventListener('click', closeCopyModal);
363
+ copyConfirmBtn.addEventListener('click', submitCopy);
364
+ wireMousedownDismiss(copyModal, closeCopyModal);
365
+ document.getElementById('synthos-copyAdvToggle').addEventListener('click', function() {
366
+ if (copyAdvContent.style.display === 'none') {
367
+ copyAdvContent.style.display = 'block';
368
+ copyAdvIcon.style.transform = 'rotate(90deg)';
369
+ } else {
370
+ copyAdvContent.style.display = 'none';
371
+ copyAdvIcon.style.transform = '';
372
+ }
373
+ });
374
+ [copyTitleInput, copyCategoriesInput, copyPageIdInput, copyGreetingInput].forEach(function(el) {
375
+ el.addEventListener('keydown', function(e) { if (e.key === 'Enter') { e.preventDefault(); submitCopy(); } });
376
+ });
377
+
378
+ // --- Edit Modal (FluentLM — used by /pages gallery) ----------------------
379
+
380
+ var editModal = makeEl(
381
+ '<div id="synthos-editModal" class="flm-dialog-overlay">' +
382
+ '<div class="flm-dialog">' +
383
+ '<div class="flm-dialog-header">' +
384
+ '<h2 class="flm-dialog-title">Edit Page Definition</h2>' +
385
+ '<button class="flm-dialog-close" data-icon="Cancel" aria-label="Close" id="synthos-editCloseBtn"></button>' +
386
+ '</div>' +
387
+ '<div class="flm-dialog-body">' +
388
+ '<div class="flm-stack" style="gap: var(--spacingM);">' +
389
+ '<div class="flm-textfield">' +
390
+ '<label class="flm-label" for="synthos-editTitle">Display Title</label>' +
391
+ '<input class="flm-textfield-input" id="synthos-editTitle" placeholder="Enter display title...">' +
392
+ '<span class="flm-text flm-text--small flm-text--secondary">Page name: <span id="synthos-editName"></span></span>' +
393
+ '</div>' +
394
+ '<div class="flm-textfield">' +
395
+ '<label class="flm-label" for="synthos-editCategories">Categories (comma-separated)</label>' +
396
+ '<input class="flm-textfield-input" id="synthos-editCategories" placeholder="e.g. Tools, Games, Utilities">' +
397
+ '</div>' +
398
+ '<label class="flm-checkbox">' +
399
+ '<input type="checkbox" class="flm-checkbox-input" id="synthos-editPinned">' +
400
+ '<span class="flm-checkbox-mark"></span>' +
401
+ '<span class="flm-checkbox-label">Pinned to Favorites</span>' +
402
+ '</label>' +
403
+ '<div>' +
404
+ '<label class="flm-checkbox">' +
405
+ '<input type="checkbox" class="flm-checkbox-input" id="synthos-editLocked">' +
406
+ '<span class="flm-checkbox-mark"></span>' +
407
+ '<span class="flm-checkbox-label">Locked</span>' +
408
+ '</label>' +
409
+ '<span class="flm-text flm-text--small flm-text--secondary flm-text--block" style="margin-top:4px;padding-left:26px;">Locked pages are read-only and cannot be modified through chat.</span>' +
410
+ '</div>' +
411
+ '<div>' +
412
+ '<label class="flm-checkbox">' +
413
+ '<input type="checkbox" class="flm-checkbox-input" id="synthos-editShowInAll" checked>' +
414
+ '<span class="flm-checkbox-mark"></span>' +
415
+ '<span class="flm-checkbox-label">Show in All</span>' +
416
+ '</label>' +
417
+ '<span class="flm-text flm-text--small flm-text--secondary flm-text--block" style="margin-top:4px;padding-left:26px;">When unchecked, this page is hidden from the "All" filter and only appears under its category.</span>' +
418
+ '</div>' +
419
+ '</div>' +
420
+ '</div>' +
421
+ '<div class="flm-dialog-footer">' +
422
+ '<div style="flex:1;"></div>' +
423
+ '<button class="flm-button" id="synthos-editCancelBtn">Cancel</button>' +
424
+ '<button class="flm-button flm-button--primary" id="synthos-editSaveBtn">Save Changes</button>' +
425
+ '</div>' +
426
+ '</div>' +
427
+ '</div>'
428
+ );
429
+ document.body.appendChild(editModal);
430
+
431
+ var editTitleInput = document.getElementById('synthos-editTitle');
432
+ var editCategoriesInput = document.getElementById('synthos-editCategories');
433
+ var editPinnedInput = document.getElementById('synthos-editPinned');
434
+ var editLockedInput = document.getElementById('synthos-editLocked');
435
+ var editShowInAllInput = document.getElementById('synthos-editShowInAll');
436
+ var editNameDisplay = document.getElementById('synthos-editName');
437
+ var editSaveBtn = document.getElementById('synthos-editSaveBtn');
438
+ var editTarget = null;
439
+ var editCallback = null;
440
+
441
+ function openEditModal(pageRecord, onSuccess) {
442
+ editTarget = pageRecord || {};
443
+ editCallback = typeof onSuccess === 'function' ? onSuccess : null;
444
+ editNameDisplay.textContent = editTarget.name || '';
445
+ editTitleInput.value = editTarget.title || '';
446
+ editCategoriesInput.value = (editTarget.categories || []).join(', ');
447
+ editPinnedInput.checked = !!editTarget.pinned;
448
+ editLockedInput.checked = editTarget.mode === 'locked';
449
+ editShowInAllInput.checked = editTarget.showInAll !== false;
450
+ editSaveBtn.disabled = false;
451
+ editSaveBtn.textContent = 'Save Changes';
452
+ editModal.classList.add('flm-dialog-overlay--open');
453
+ setTimeout(function() { editTitleInput.focus(); }, 0);
454
+ }
455
+ function closeEditModal() {
456
+ editModal.classList.remove('flm-dialog-overlay--open');
457
+ editTarget = null;
458
+ editCallback = null;
459
+ }
460
+
461
+ function submitEdit() {
462
+ if (!editTarget || !editTarget.name) return;
463
+ var newTitle = editTitleInput.value.trim();
464
+ var newCategories = editCategoriesInput.value.split(',').map(function(c) { return c.trim(); }).filter(Boolean);
465
+ var body = {
466
+ title: newTitle || undefined,
467
+ categories: newCategories,
468
+ pinned: editPinnedInput.checked,
469
+ showInAll: editShowInAllInput.checked,
470
+ mode: editLockedInput.checked ? 'locked' : 'unlocked'
471
+ };
472
+
473
+ editSaveBtn.disabled = true;
474
+ editSaveBtn.textContent = 'Saving...';
475
+
476
+ var cb = editCallback;
477
+ fetch('/api/pages/' + encodeURIComponent(editTarget.name), {
478
+ method: 'POST',
479
+ headers: { 'Content-Type': 'application/json' },
480
+ body: JSON.stringify(body)
481
+ })
482
+ .then(function(res) {
483
+ if (!res.ok) return res.text().then(function(t) { throw new Error(t || 'Failed to update page'); });
484
+ return null;
485
+ })
486
+ .then(function() {
487
+ closeEditModal();
488
+ if (cb) { try { cb(); } catch(_) {} }
489
+ })
490
+ .catch(function(err) {
491
+ editSaveBtn.disabled = false;
492
+ editSaveBtn.textContent = 'Save Changes';
493
+ showError('Failed to save changes: ' + err.message);
494
+ });
495
+ }
496
+
497
+ document.getElementById('synthos-editCloseBtn').addEventListener('click', closeEditModal);
498
+ document.getElementById('synthos-editCancelBtn').addEventListener('click', closeEditModal);
499
+ editSaveBtn.addEventListener('click', submitEdit);
500
+ wireMousedownDismiss(editModal, closeEditModal);
501
+ [editTitleInput, editCategoriesInput].forEach(function(el) {
502
+ el.addEventListener('keydown', function(e) { if (e.key === 'Enter') { e.preventDefault(); submitEdit(); } });
503
+ });
504
+
505
+ // --- Escape closes whichever modal is open -------------------------------
506
+
507
+ document.addEventListener('keydown', function(e) {
508
+ if (e.key !== 'Escape') return;
509
+ if (errorModal.classList.contains('show')) { closeError(); return; }
510
+ if (saveModal.classList.contains('show')) { closeSaveModal(); return; }
511
+ if (copyModal.classList.contains('show')) { closeCopyModal(); return; }
512
+ if (editModal.classList.contains('flm-dialog-overlay--open')) { closeEditModal(); return; }
513
+ });
514
+
515
+ // --- Public API ----------------------------------------------------------
516
+
517
+ window.__synthOSOpenSaveModal = openSaveModal;
518
+ window.__synthOSOpenCopyModal = openCopyModal;
519
+ window.__synthOSOpenEditModal = openEditModal;
520
+ window.__synthOSShowError = showError;
521
+ })();