scriptorium 0.6.1 → 0.7.2

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 (358) hide show
  1. checksums.yaml +4 -4
  2. data/assets/icons/social/reddit.png +0 -0
  3. data/assets/icons/social/x-logo.png +0 -0
  4. data/assets/imagenotfound.jpg +0 -0
  5. data/bin/sblog +84 -5
  6. data/bin/scriptorium +1 -0
  7. data/doc/anti-amnesia/20250727-054000-scriptorium-overview.md +0 -1
  8. data/doc/anti-amnesia/20250727-123000-anti-amnesia-conventions.md +0 -29
  9. data/doc/anti-amnesia/20250727-172600-cursor-rbenv-ruby-version-mystery.md +0 -19
  10. data/doc/anti-amnesia/20250727-172900-ai-cognitive-assessment-capabilities.md +1 -1
  11. data/doc/anti-amnesia/20250728-124243-aaa-syntax-clarification.md +1 -1
  12. data/doc/anti-amnesia/20250729-210000-reddit-autopost-integration-complete.md +1 -1
  13. data/doc/anti-amnesia/20250804-190500-cognitive-loop-bug.md +0 -10
  14. data/doc/anti-amnesia/20250804-190700-anti-amnesia-timestamping-fix.md +1 -4
  15. data/doc/anti-amnesia/20250901-211714-codemirror-integration-and-web-tests.md +172 -0
  16. data/doc/anti-amnesia/20250902-002402-backup-restore-system.md +126 -0
  17. data/doc/anti-amnesia/20250907-203339-backup-metadata-implementation.md +66 -0
  18. data/doc/imported/0001-elixir-conf-2014/metadata.txt +7 -0
  19. data/doc/imported/0001-elixir-conf-2014/post.html +37 -0
  20. data/doc/imported/0001-elixir-conf-2014/source.lt3 +22 -0
  21. data/doc/imported/0002-programmers-and-word-processing/metadata.txt +7 -0
  22. data/doc/imported/0002-programmers-and-word-processing/post.html +192 -0
  23. data/doc/imported/0002-programmers-and-word-processing/source.lt3 +146 -0
  24. data/doc/imported/0003-how-to-turn-your-brain-sideways/metadata.txt +7 -0
  25. data/doc/imported/0003-how-to-turn-your-brain-sideways/post.html +60 -0
  26. data/doc/imported/0003-how-to-turn-your-brain-sideways/source.lt3 +40 -0
  27. data/doc/imported/0004-upcoming-lone-star-ruby-conference/metadata.txt +7 -0
  28. data/doc/imported/0004-upcoming-lone-star-ruby-conference/post.html +42 -0
  29. data/doc/imported/0004-upcoming-lone-star-ruby-conference/source.lt3 +24 -0
  30. data/doc/imported/0005-elixir-conf-2015-announced/metadata.txt +7 -0
  31. data/doc/imported/0005-elixir-conf-2015-announced/post.html +30 -0
  32. data/doc/imported/0005-elixir-conf-2015-announced/source.lt3 +16 -0
  33. data/doc/imported/0006-ruby-for-dinosaurs/metadata.txt +7 -0
  34. data/doc/imported/0006-ruby-for-dinosaurs/post.html +43 -0
  35. data/doc/imported/0006-ruby-for-dinosaurs/source.lt3 +27 -0
  36. data/doc/imported/0007-phoenix-isnt-rails/metadata.txt +7 -0
  37. data/doc/imported/0007-phoenix-isnt-rails/post.html +116 -0
  38. data/doc/imported/0007-phoenix-isnt-rails/source.lt3 +87 -0
  39. data/doc/imported/0008-concerning-the-term-monkeypatching/metadata.txt +7 -0
  40. data/doc/imported/0008-concerning-the-term-monkeypatching/post.html +129 -0
  41. data/doc/imported/0008-concerning-the-term-monkeypatching/source.lt3 +92 -0
  42. data/doc/imported/0009-announcement-coming-soon/metadata.txt +7 -0
  43. data/doc/imported/0009-announcement-coming-soon/post.html +33 -0
  44. data/doc/imported/0009-announcement-coming-soon/source.lt3 +19 -0
  45. data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/metadata.txt +7 -0
  46. data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/post.html +175 -0
  47. data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/source.lt3 +139 -0
  48. data/doc/imported/0011-computer-science-as-a-lost-art/metadata.txt +7 -0
  49. data/doc/imported/0011-computer-science-as-a-lost-art/post.html +139 -0
  50. data/doc/imported/0011-computer-science-as-a-lost-art/source.lt3 +104 -0
  51. data/doc/imported/0012-ruby-day-in-turin-italy/metadata.txt +7 -0
  52. data/doc/imported/0012-ruby-day-in-turin-italy/post.html +42 -0
  53. data/doc/imported/0012-ruby-day-in-turin-italy/source.lt3 +24 -0
  54. data/doc/imported/0013-rubyday-was-a-success/metadata.txt +7 -0
  55. data/doc/imported/0013-rubyday-was-a-success/post.html +44 -0
  56. data/doc/imported/0013-rubyday-was-a-success/source.lt3 +27 -0
  57. data/doc/imported/0014-working-on-the-blogging-software/metadata.txt +7 -0
  58. data/doc/imported/0014-working-on-the-blogging-software/post.html +63 -0
  59. data/doc/imported/0014-working-on-the-blogging-software/source.lt3 +41 -0
  60. data/doc/imported/0015-ok-its-not-really-a-lost-art/metadata.txt +7 -0
  61. data/doc/imported/0015-ok-its-not-really-a-lost-art/post.html +172 -0
  62. data/doc/imported/0015-ok-its-not-really-a-lost-art/source.lt3 +134 -0
  63. data/doc/imported/0016-an-in-operator-for-ruby/metadata.txt +7 -0
  64. data/doc/imported/0016-an-in-operator-for-ruby/post.html +155 -0
  65. data/doc/imported/0016-an-in-operator-for-ruby/source.lt3 +106 -0
  66. data/doc/imported/0017-the-forgotten-mathematician/metadata.txt +7 -0
  67. data/doc/imported/0017-the-forgotten-mathematician/post.html +161 -0
  68. data/doc/imported/0017-the-forgotten-mathematician/source.lt3 +119 -0
  69. data/doc/imported/0018-ruby-puns/metadata.txt +7 -0
  70. data/doc/imported/0018-ruby-puns/post.html +46 -0
  71. data/doc/imported/0018-ruby-puns/source.lt3 +28 -0
  72. data/doc/imported/0019-custom-exceptions-via-metaprogramming/metadata.txt +7 -0
  73. data/doc/imported/0019-custom-exceptions-via-metaprogramming/post.html +138 -0
  74. data/doc/imported/0019-custom-exceptions-via-metaprogramming/source.lt3 +101 -0
  75. data/doc/imported/0020-fffff/metadata.txt +7 -0
  76. data/doc/imported/0020-fffff/post.html +24 -0
  77. data/doc/imported/0020-fffff/source.lt3 +12 -0
  78. data/doc/imported/0021-trying-ror-yet-again/metadata.txt +7 -0
  79. data/doc/imported/0021-trying-ror-yet-again/post.html +26 -0
  80. data/doc/imported/0021-trying-ror-yet-again/source.lt3 +12 -0
  81. data/doc/imported/0023-doctor-sleep/metadata.txt +7 -0
  82. data/doc/imported/0023-doctor-sleep/post.html +63 -0
  83. data/doc/imported/0023-doctor-sleep/source.lt3 +44 -0
  84. data/doc/imported/0024-just-a-test/metadata.txt +7 -0
  85. data/doc/imported/0024-just-a-test/post.html +24 -0
  86. data/doc/imported/0024-just-a-test/source.lt3 +12 -0
  87. data/doc/imported/import_summary.txt +98 -0
  88. data/doc/livetext-informal-spec.txt +65 -0
  89. data/doc/myuserdoc/ch-0.lt3 +31 -0
  90. data/doc/myuserdoc/ch-1.lt3 +37 -0
  91. data/doc/myuserdoc/ch-10.lt3 +22 -0
  92. data/doc/myuserdoc/ch-2.lt3 +37 -0
  93. data/doc/myuserdoc/ch-3.lt3 +19 -0
  94. data/doc/myuserdoc/ch-4.lt3 +43 -0
  95. data/doc/myuserdoc/ch-5.lt3 +22 -0
  96. data/doc/myuserdoc/ch-6.lt3 +19 -0
  97. data/doc/myuserdoc/ch-7.lt3 +16 -0
  98. data/doc/myuserdoc/ch-8.lt3 +13 -0
  99. data/doc/myuserdoc/ch-9.lt3 +19 -0
  100. data/doc/myuserdoc/tweak.rb +18 -0
  101. data/doc/{userdoc-toc.txt → myuserdoc/userdoc-toc.txt} +27 -27
  102. data/doc/old-posts/0001-elixir-conf-2014.lt3 +24 -0
  103. data/doc/old-posts/0002-programmers-and-word-processing.lt3 +150 -0
  104. data/doc/old-posts/0003-how-to-turn-your-brain-sideways.lt3 +43 -0
  105. data/doc/old-posts/0004-upcoming-lone-star-ruby-conference.lt3 +26 -0
  106. data/doc/old-posts/0005-elixir-conf-2015-announced.lt3 +17 -0
  107. data/doc/old-posts/0006-ruby-for-dinosaurs.lt3 +30 -0
  108. data/doc/old-posts/0007-phoenix-isnt-rails.lt3 +90 -0
  109. data/doc/old-posts/0008-concerning-the-term-monkeypatching.lt3 +105 -0
  110. data/doc/old-posts/0009-announcement-coming-soon.lt3 +20 -0
  111. data/doc/old-posts/0010-immutable-data-ditching-the-wax-tablet.lt3 +142 -0
  112. data/doc/old-posts/0011-computer-science-as-a-lost-art.lt3 +117 -0
  113. data/doc/old-posts/0012-ruby-day-in-turin-italy.lt3 +26 -0
  114. data/doc/old-posts/0013-rubyday-was-a-success.lt3 +28 -0
  115. data/doc/old-posts/0014-working-on-the-blogging-software.lt3 +42 -0
  116. data/doc/old-posts/0015-ok-its-not-really-a-lost-art.lt3 +137 -0
  117. data/doc/old-posts/0016-an-in-operator-for-ruby.lt3 +142 -0
  118. data/doc/old-posts/0017-the-forgotten-mathematician.lt3 +129 -0
  119. data/doc/old-posts/0018-ruby-puns.lt3 +31 -0
  120. data/doc/old-posts/0019-custom-exceptions-via-metaprogramming.lt3 +116 -0
  121. data/doc/old-posts/0021-trying-ror-yet-again.lt3 +35 -0
  122. data/doc/old-posts/0023-doctor-sleep.lt3 +43 -0
  123. data/doc/old-posts/0024-just-a-test.lt3 +12 -0
  124. data/doc/old-posts/0025-trying-another-post.lt3 +12 -0
  125. data/doc/old-repo +1 -0
  126. data/doc/reddit_integration.md +2 -2
  127. data/doc/user.lt3 +0 -3
  128. data/lib/scriptorium/api.rb +1811 -78
  129. data/lib/scriptorium/banner_svg.rb +55 -68
  130. data/lib/scriptorium/contract.rb +3 -2
  131. data/lib/scriptorium/exceptions.rb +133 -102
  132. data/lib/scriptorium/helpers.rb +282 -82
  133. data/lib/scriptorium/post.rb +81 -17
  134. data/lib/scriptorium/reddit.rb +1 -1
  135. data/lib/scriptorium/repo.rb +478 -164
  136. data/lib/scriptorium/standard_files.rb +30 -396
  137. data/lib/scriptorium/support/common_js/clipboard.js +35 -0
  138. data/lib/scriptorium/support/common_js/content-loader.js +187 -0
  139. data/lib/scriptorium/support/common_js/navigation.js +52 -0
  140. data/lib/scriptorium/support/common_js/syntax-highlighting.js +27 -0
  141. data/lib/scriptorium/support/config/reddit_template.txt +17 -0
  142. data/{test/scriptorium-TEST-1754622690-146/views/sample → lib/scriptorium/support}/config/social.txt +1 -0
  143. data/lib/scriptorium/support/highlight/css.txt +2 -0
  144. data/lib/scriptorium/support/highlight/custom.css +119 -0
  145. data/lib/scriptorium/support/highlight/js.txt +1 -0
  146. data/lib/scriptorium/support/post_index/config.txt +15 -0
  147. data/lib/scriptorium/support/post_index/style.css +55 -0
  148. data/lib/scriptorium/support/templates/index_entry.lt3 +16 -0
  149. data/{test/scriptorium-TEST-1754622690-146/themes/standard/initial/post.lt3 → lib/scriptorium/support/templates/initial_post.lt3} +5 -5
  150. data/lib/scriptorium/support/templates/post.lt3 +104 -0
  151. data/{test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/header.txt → lib/scriptorium/support/theme/header.lt3} +1 -1
  152. data/lib/scriptorium/theme.rb +83 -70
  153. data/lib/scriptorium/version.rb +2 -2
  154. data/lib/scriptorium/view.rb +194 -149
  155. data/lib/scriptorium.rb +24 -1
  156. data/lib/skeleton.rb +4 -1
  157. data/scriptorium.gemspec +2 -1
  158. data/test/WEB_INTEGRATION_README.md +196 -0
  159. data/test/all +40 -0
  160. data/test/banner_svg/unit.rb +267 -35
  161. data/test/config/deployment.txt +5 -0
  162. data/test/integration/integration_test.rb +7 -7
  163. data/test/integration/preview_flow_test.rb +94 -0
  164. data/test/livetext_plugin_test.rb +453 -182
  165. data/test/manual/banner-tests/test01.html +82 -18
  166. data/test/manual/banner-tests/test02.html +82 -18
  167. data/test/manual/banner-tests/test03.html +82 -18
  168. data/test/manual/banner-tests/test04.html +89 -25
  169. data/test/manual/banner-tests/test05.html +89 -25
  170. data/test/manual/banner-tests/test06.html +89 -25
  171. data/test/manual/banner-tests/test07.html +89 -25
  172. data/test/manual/banner-tests/test08.html +82 -18
  173. data/test/manual/banner-tests/test09.html +82 -18
  174. data/test/manual/banner-tests/test10.html +82 -18
  175. data/test/manual/banner-tests/test11.html +82 -18
  176. data/test/manual/banner-tests/test12.html +82 -18
  177. data/test/manual/banner-tests/test13.html +82 -18
  178. data/test/manual/banner-tests/test14.html +82 -18
  179. data/test/manual/banner-tests/test15.html +82 -18
  180. data/test/manual/banner-tests/test16.html +82 -18
  181. data/test/manual/banner-tests/test17.html +82 -18
  182. data/test/manual/banner-tests/test18.html +90 -26
  183. data/test/manual/banner-tests/test19.html +90 -26
  184. data/test/manual/banner-tests/test20.html +90 -26
  185. data/test/manual/banner-tests/test21.html +90 -26
  186. data/test/manual/banner-tests/test22.html +90 -26
  187. data/test/manual/banner-tests/test23.html +90 -26
  188. data/test/manual/banner-tests/test24.html +90 -26
  189. data/test/manual/banner-tests/test25.html +89 -25
  190. data/test/manual/banner_environment.rb +15 -2
  191. data/test/manual/codemirror_demo.html +773 -0
  192. data/test/manual/create_posts_for_web.rb +114 -0
  193. data/test/manual/preview_manual_test.rb +129 -0
  194. data/test/manual/test_banner_features.rb +14 -14
  195. data/test/manual/test_banner_integration.rb +115 -0
  196. data/test/manual/test_banner_radial.rb +87 -0
  197. data/test/manual/test_syntax_highlighting.rb +60 -40
  198. data/test/support/preview_utils.rb +88 -0
  199. data/test/test_gem_assets.rb +48 -0
  200. data/test/test_helpers.rb +10 -0
  201. data/test/tui_editor_integration_test.rb +15 -15
  202. data/test/tui_integration_test.rb +687 -441
  203. data/test/unit/api.rb +757 -37
  204. data/test/unit/asset_management.rb +195 -221
  205. data/test/unit/backup_test.rb +451 -0
  206. data/test/unit/contract_test.rb +1 -23
  207. data/test/unit/core.rb +415 -61
  208. data/test/unit/deploy_config_test.rb +248 -0
  209. data/test/unit/deploy_test.rb +312 -21
  210. data/test/unit/edit_post_test.rb +168 -0
  211. data/test/unit/gem_asset_management.rb +36 -42
  212. data/test/unit/livetext_basic.rb +23 -35
  213. data/test/unit/livetext_compatibility.rb +7 -14
  214. data/test/unit/parse_cmd_test.rb +260 -0
  215. data/test/unit/{symlink_test.rb → permalink_copy_test.rb} +47 -49
  216. data/test/unit/post.rb +91 -26
  217. data/test/unit/post_index_config_test.rb +258 -0
  218. data/test/unit/post_state_helpers_test.rb +137 -0
  219. data/test/unit/read_commented_file_test.rb +8 -6
  220. data/test/unit/repo.rb +75 -54
  221. data/test/unit/social_test.rb +41 -44
  222. data/test/unit/syntax_highlighting.rb +70 -0
  223. data/test/unit/theme_management_test.rb +91 -0
  224. data/test/unit/view.rb +79 -12
  225. data/test/unit/widgets.rb +8 -8
  226. data/test/web_integration_test.rb +231 -0
  227. data/test/web_test_helper.rb +218 -0
  228. data/test/web_workflow_test.rb +527 -0
  229. data/ui/tui/bin/scriptorium +885 -415
  230. data/ui/web/app/app.rb +1398 -176
  231. data/ui/web/app/assets/livetext_mode.js +244 -0
  232. data/ui/web/app/error_helpers.rb +16 -16
  233. data/ui/web/app/views/advanced_config.erb +8 -2
  234. data/ui/web/app/views/asset_management.erb +56 -0
  235. data/ui/web/app/views/backup_management.erb +238 -0
  236. data/ui/web/app/views/config_widget.erb +232 -0
  237. data/ui/web/app/views/dashboard.erb +64 -72
  238. data/ui/web/app/views/deploy_config.erb +3 -0
  239. data/ui/web/app/views/edit_pages.erb +170 -2
  240. data/ui/web/app/views/edit_post.erb +130 -9
  241. data/ui/web/app/views/edit_theme.erb +73 -0
  242. data/ui/web/app/views/edit_theme_file.erb +74 -0
  243. data/ui/web/app/views/theme_management.erb +130 -0
  244. data/ui/web/app/views/view_dashboard.erb +666 -25
  245. data/ui/web/app/views/widgets.erb +249 -0
  246. data/ui/web/bin/scriptorium-web +35 -24
  247. data/ui/web/tmp/timing.log +17 -0
  248. data/ui/web/tmp/web_server.log +0 -5
  249. metadata +190 -116
  250. data/assets/back-icon.png +0 -0
  251. data/assets/icons/facebook.svg +0 -1
  252. data/assets/icons/github.svg +0 -1
  253. data/assets/icons/instagram.svg +0 -1
  254. data/assets/icons/reddit.svg +0 -1
  255. data/assets/icons/x.svg +0 -1
  256. data/assets/icons/youtube.svg +0 -1
  257. data/bin/scriptorium +0 -1511
  258. data/doc/anti-amnesia/20250727-060000-api-design-tui-planning.md +0 -34
  259. data/doc/anti-amnesia/20250727-061000-runeblog-tui-analysis.md +0 -50
  260. data/doc/anti-amnesia/20250727-154000-livetext-plugin-file-stats.md +0 -73
  261. data/doc/anti-amnesia/20250727-172600-unified-minitest-framework.md +0 -70
  262. data/doc/anti-amnesia/20250727-173000-widget-testing-achievement.md +0 -110
  263. data/doc/anti-amnesia/20250727-180000-post-id-num-refactoring.md +0 -73
  264. data/doc/anti-amnesia/20250728-124421-conversation-summary-concise.md +0 -124
  265. data/doc/anti-amnesia/20250729-190000-scriptorium-tui-testing-complete.md +0 -46
  266. data/doc/anti-amnesia/20250729-200000-scriptorium-tui-testing-edit-file-workflow.md +0 -97
  267. data/doc/anti-amnesia/20250729-211500-dependency-management-system.md +0 -211
  268. data/doc/anti-amnesia/20250729-213000-python-virtual-environment-setup.md +0 -141
  269. data/doc/anti-amnesia/20250729-214500-theme-management-commands.md +0 -211
  270. data/doc/anti-amnesia/20250729-215000-version-update-to-0.6.0.md +0 -134
  271. data/doc/anti-amnesia/20250729-220000-user-guide-complete.md +0 -41
  272. data/doc/anti-amnesia/20250804-213700-publishing-test-fix.md +0 -49
  273. data/doc/anti-amnesia/20250804-214400-additional-test-fixes.md +0 -46
  274. data/doc/anti-amnesia/20250804-220000-asset-function-logic-clarification.md +0 -41
  275. data/doc/anti-amnesia/20250806-202032-asset-function-logic-clarification.md +0 -41
  276. data/doc/anti-amnesia/20250813-082428-syntax-highlighting-and-navigation-improvements.md +0 -256
  277. data/lib/scriptorium/syntax_highlighter.rb +0 -234
  278. data/test/manual/deploy_symlink_demo.rb +0 -142
  279. data/test/manual/symlink_demo.rb +0 -117
  280. data/test/manual/test2.rb +0 -12
  281. data/test/manual/test_banner_from_file.rb +0 -150
  282. data/test/manual/test_banner_in_header.rb +0 -35
  283. data/test/manual/test_code_highlighting.rb +0 -68
  284. data/test/manual/test_complex_header.rb +0 -74
  285. data/test/manual/test_empty_header.rb +0 -32
  286. data/test/manual/test_radial_custom.rb +0 -58
  287. data/test/manual/test_radial_large_radius.rb +0 -52
  288. data/test/manual/test_svg_debug.rb +0 -47
  289. data/test/pages-demo/config/currentview.txt +0 -1
  290. data/test/pages-demo/views/demo/config/common.js +0 -57
  291. data/test/pages-demo/views/demo/config/footer.txt +0 -1
  292. data/test/pages-demo/views/demo/config/global-head.txt +0 -8
  293. data/test/pages-demo/views/demo/config/header.txt +0 -1
  294. data/test/pages-demo/views/demo/config/layout.txt +0 -1
  295. data/test/pages-demo/views/demo/config/left.txt +0 -1
  296. data/test/pages-demo/views/demo/config/main.txt +0 -1
  297. data/test/pages-demo/views/demo/config/right.txt +0 -1
  298. data/test/pages-demo/views/demo/config.txt +0 -3
  299. data/test/pages-demo/views/demo/output/panes/footer.html +0 -1
  300. data/test/pages-demo/views/demo/output/panes/header.html +0 -1
  301. data/test/pages-demo/views/demo/output/panes/left.html +0 -1
  302. data/test/pages-demo/views/demo/output/panes/main.html +0 -1
  303. data/test/pages-demo/views/demo/output/panes/right.html +0 -1
  304. data/test/scriptorium-TEST-1754622690-146/config/bootstrap_css.txt +0 -5
  305. data/test/scriptorium-TEST-1754622690-146/config/bootstrap_js.txt +0 -4
  306. data/test/scriptorium-TEST-1754622690-146/config/common.js +0 -57
  307. data/test/scriptorium-TEST-1754622690-146/config/currentview.txt +0 -1
  308. data/test/scriptorium-TEST-1754622690-146/config/global-head.txt +0 -9
  309. data/test/scriptorium-TEST-1754622690-146/config/last_post_num.txt +0 -1
  310. data/test/scriptorium-TEST-1754622690-146/config/os_helpers.rb +0 -4
  311. data/test/scriptorium-TEST-1754622690-146/config/widgets.txt +0 -3
  312. data/test/scriptorium-TEST-1754622690-146/posts/0001/meta.txt +0 -8
  313. data/test/scriptorium-TEST-1754622690-146/posts/0001/source.lt3 +0 -6
  314. data/test/scriptorium-TEST-1754622690-146/themes/standard/README.txt +0 -1
  315. data/test/scriptorium-TEST-1754622690-146/themes/standard/config.txt +0 -1
  316. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/gen/text.css +0 -1
  317. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/index.lt3 +0 -1
  318. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/index_entry.lt3 +0 -14
  319. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/post.lt3 +0 -13
  320. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/widget.lt3 +0 -1
  321. data/test/scriptorium-TEST-1754622690-146/views/sample/config/bootstrap_css.txt +0 -5
  322. data/test/scriptorium-TEST-1754622690-146/views/sample/config/bootstrap_js.txt +0 -4
  323. data/test/scriptorium-TEST-1754622690-146/views/sample/config/common.js +0 -57
  324. data/test/scriptorium-TEST-1754622690-146/views/sample/config/deploy.txt +0 -5
  325. data/test/scriptorium-TEST-1754622690-146/views/sample/config/footer.txt +0 -2
  326. data/test/scriptorium-TEST-1754622690-146/views/sample/config/global-head.txt +0 -9
  327. data/test/scriptorium-TEST-1754622690-146/views/sample/config/header.txt +0 -4
  328. data/test/scriptorium-TEST-1754622690-146/views/sample/config/layout.txt +0 -5
  329. data/test/scriptorium-TEST-1754622690-146/views/sample/config/left.txt +0 -3
  330. data/test/scriptorium-TEST-1754622690-146/views/sample/config/main.txt +0 -5
  331. data/test/scriptorium-TEST-1754622690-146/views/sample/config/right.txt +0 -3
  332. data/test/scriptorium-TEST-1754622690-146/views/sample/config/status.txt +0 -7
  333. data/test/scriptorium-TEST-1754622690-146/views/sample/config.txt +0 -3
  334. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/footer.html +0 -3
  335. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/header.html +0 -3
  336. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/left.html +0 -3
  337. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/main.html +0 -3
  338. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/right.html +0 -3
  339. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/footer.html +0 -1
  340. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/header.html +0 -1
  341. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/left.html +0 -1
  342. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/main.html +0 -1
  343. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/right.html +0 -1
  344. data/ui/web/tmp/web_server.pid +0 -1
  345. /data/{test/pages-demo/views/demo/config/bootstrap_css.txt → lib/scriptorium/support/bootstrap/css.txt} +0 -0
  346. /data/{test/pages-demo/views/demo/config/bootstrap_js.txt → lib/scriptorium/support/bootstrap/js.txt} +0 -0
  347. /data/{test/scriptorium-TEST-1754622690-146/views/sample → lib/scriptorium/support}/config/reddit.txt +0 -0
  348. /data/{test/scriptorium-TEST-1754622690-146/themes/standard/layout → lib/scriptorium/support/templates}/layout.txt +0 -0
  349. /data/{test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/footer.txt → lib/scriptorium/support/theme/footer.lt3} +0 -0
  350. /data/{test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/left.txt → lib/scriptorium/support/theme/left.lt3} +0 -0
  351. /data/{test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/main.txt → lib/scriptorium/support/theme/main.lt3} +0 -0
  352. /data/{test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/right.txt → lib/scriptorium/support/theme/right.lt3} +0 -0
  353. /data/test/manual/banner-tests/{config.txt → svg.txt} +0 -0
  354. /data/test/manual/{test6.rb → test_advanced_widgets.rb} +0 -0
  355. /data/test/manual/{test1.rb → test_basic_posts.rb} +0 -0
  356. /data/test/manual/{test4.rb → test_layout_widgets.rb} +0 -0
  357. /data/test/manual/{test5.rb → test_pagination.rb} +0 -0
  358. /data/test/manual/{test3.rb → test_random_posts.rb} +0 -0
@@ -0,0 +1,232 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Configure <%= @widget_name.capitalize %> Widget - Scriptorium</title>
5
+ <style>
6
+ body {
7
+ font-family: Arial, sans-serif;
8
+ margin: 0;
9
+ padding: 20px;
10
+ background-color: #f5f5f5;
11
+ }
12
+ .container {
13
+ max-width: 1000px;
14
+ margin: 0 auto;
15
+ background: white;
16
+ border-radius: 8px;
17
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
18
+ overflow: hidden;
19
+ }
20
+ .header {
21
+ background: #007bff;
22
+ color: white;
23
+ padding: 20px;
24
+ }
25
+ .header h1 {
26
+ margin: 0;
27
+ font-size: 24px;
28
+ }
29
+ .content {
30
+ padding: 20px;
31
+ }
32
+ .message {
33
+ padding: 10px;
34
+ border-radius: 4px;
35
+ margin-bottom: 20px;
36
+ }
37
+ .message.success {
38
+ background: #d4edda;
39
+ color: #155724;
40
+ border: 1px solid #c3e6cb;
41
+ }
42
+ .message.error {
43
+ background: #f8d7da;
44
+ color: #721c24;
45
+ border: 1px solid #f5c6cb;
46
+ }
47
+ .section {
48
+ margin-bottom: 30px;
49
+ border: 1px solid #ddd;
50
+ border-radius: 6px;
51
+ overflow: hidden;
52
+ }
53
+ .section-header {
54
+ background: #f8f9fa;
55
+ padding: 15px 20px;
56
+ border-bottom: 1px solid #ddd;
57
+ font-weight: bold;
58
+ font-size: 18px;
59
+ }
60
+ .section-content {
61
+ padding: 20px;
62
+ }
63
+ .btn {
64
+ padding: 8px 16px;
65
+ border: none;
66
+ border-radius: 4px;
67
+ cursor: pointer;
68
+ font-size: 14px;
69
+ margin-right: 10px;
70
+ text-decoration: none;
71
+ display: inline-block;
72
+ }
73
+ .btn-primary {
74
+ background: #007bff;
75
+ color: white;
76
+ }
77
+ .btn-primary:hover {
78
+ background: #0056b3;
79
+ }
80
+ .btn-secondary {
81
+ background: #6c757d;
82
+ color: white;
83
+ }
84
+ .btn-secondary:hover {
85
+ background: #545b62;
86
+ }
87
+ .btn-success {
88
+ background: #28a745;
89
+ color: white;
90
+ }
91
+ .btn-success:hover {
92
+ background: #218838;
93
+ }
94
+ .form-group {
95
+ margin-bottom: 20px;
96
+ }
97
+ .form-group label {
98
+ display: block;
99
+ margin-bottom: 5px;
100
+ font-weight: bold;
101
+ color: #333;
102
+ }
103
+ .form-group textarea {
104
+ width: 100%;
105
+ min-height: 300px;
106
+ padding: 10px;
107
+ border: 1px solid #ddd;
108
+ border-radius: 4px;
109
+ font-family: monospace;
110
+ font-size: 14px;
111
+ line-height: 1.4;
112
+ }
113
+ .form-group textarea:focus {
114
+ outline: none;
115
+ border-color: #007bff;
116
+ box-shadow: 0 0 0 2px rgba(0,123,255,0.25);
117
+ }
118
+ .instructions {
119
+ background: #f8f9fa;
120
+ border: 1px solid #dee2e6;
121
+ border-radius: 4px;
122
+ padding: 15px;
123
+ margin-bottom: 20px;
124
+ }
125
+ .instructions h4 {
126
+ margin: 0 0 10px 0;
127
+ color: #495057;
128
+ }
129
+ .instructions ul {
130
+ margin: 0;
131
+ padding-left: 20px;
132
+ }
133
+ .instructions li {
134
+ margin-bottom: 5px;
135
+ }
136
+ .back-link {
137
+ margin-bottom: 20px;
138
+ }
139
+ .actions {
140
+ margin-top: 20px;
141
+ padding-top: 20px;
142
+ border-top: 1px solid #dee2e6;
143
+ }
144
+ </style>
145
+ </head>
146
+ <body>
147
+ <div class="container">
148
+ <div class="header">
149
+ <h1>Configure <%= @widget_name.capitalize %> Widget</h1>
150
+ </div>
151
+
152
+ <div class="content">
153
+ <div class="back-link">
154
+ <a href="/widgets" class="btn btn-secondary">← Back to Widget Management</a>
155
+ </div>
156
+
157
+ <% if @error %>
158
+ <div class="message error">
159
+ <strong>Error:</strong> <%= @error %>
160
+ </div>
161
+ <% end %>
162
+
163
+ <% if @message %>
164
+ <div class="message success">
165
+ <strong>Success:</strong> <%= @message %>
166
+ </div>
167
+ <% end %>
168
+
169
+ <div class="section">
170
+ <div class="section-header">
171
+ Widget Instructions
172
+ </div>
173
+ <div class="section-content">
174
+ <div class="instructions">
175
+ <% case @widget_name %>
176
+ <% when 'links' %>
177
+ <h4>Links Widget Configuration</h4>
178
+ <p>Add links in the format: <code>URL, Title</code></p>
179
+ <ul>
180
+ <li>One link per line</li>
181
+ <li>Separate URL and title with a comma</li>
182
+ <li>Example: <code>https://example.com, Example Site</code></li>
183
+ <li>Spaces around the comma are optional</li>
184
+ </ul>
185
+ <% when 'pages' %>
186
+ <h4>Pages Widget Configuration</h4>
187
+ <p>Add page filenames (without extensions):</p>
188
+ <ul>
189
+ <li>One filename per line</li>
190
+ <li>Don't include file extensions</li>
191
+ <li>Example: <code>about</code> (for about.html)</li>
192
+ <li>Pages will be loaded from the <code>pages/</code> directory</li>
193
+ </ul>
194
+ <% when 'featured_posts' %>
195
+ <h4>Featured Posts Widget Configuration</h4>
196
+ <p>Add post IDs to feature:</p>
197
+ <ul>
198
+ <li>One post ID per line</li>
199
+ <li>Use numeric post IDs (e.g., <code>1</code>, <code>5</code>)</li>
200
+ <li>Posts will be displayed in the order listed</li>
201
+ <li>Invalid post IDs will be ignored</li>
202
+ </ul>
203
+ <% else %>
204
+ <h4>Widget Configuration</h4>
205
+ <p>Edit the widget data below. The format depends on the widget type.</p>
206
+ <% end %>
207
+ </div>
208
+ </div>
209
+ </div>
210
+
211
+ <div class="section">
212
+ <div class="section-header">
213
+ Edit Widget Data
214
+ </div>
215
+ <div class="section-content">
216
+ <form method="post" action="/update_widget/<%= @widget_name %>">
217
+ <div class="form-group">
218
+ <label for="widget_data">Widget Data:</label>
219
+ <textarea name="widget_data" id="widget_data" placeholder="Enter widget data here..."><%= @widget_data %></textarea>
220
+ </div>
221
+
222
+ <div class="actions">
223
+ <button type="submit" class="btn btn-success">Save and Generate Widget</button>
224
+ <a href="/widgets" class="btn btn-secondary">Cancel</a>
225
+ </div>
226
+ </form>
227
+ </div>
228
+ </div>
229
+ </div>
230
+ </div>
231
+ </body>
232
+ </html>
@@ -20,24 +20,49 @@
20
20
  .posts-table .post-id { text-align: right !important; font-family: monospace; min-width: 40px; }
21
21
  </style>
22
22
  <script>
23
+ function ensureOverlay(id, text, bg) {
24
+ let el = document.getElementById(id);
25
+ if (!el) {
26
+ el = document.createElement('div');
27
+ el.id = id;
28
+ el.style.cssText = 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 16px 22px; border-radius: 8px; z-index: 9999; color: white; font-weight: bold; box-shadow: 0 4px 12px rgba(0,0,0,0.3);';
29
+ document.body.appendChild(el);
30
+ }
31
+ el.style.background = bg;
32
+ el.textContent = text;
33
+ el.style.display = 'block';
34
+ return el;
35
+ }
36
+ function showGeneratingPopup() { ensureOverlay('gen-popup', 'Generating view...', '#007bff'); }
37
+ function showPreviewPopup() { ensureOverlay('preview-popup', 'Opening preview...', '#17a2b8'); }
23
38
  function previewView(viewName) {
24
- // Create a form to submit the POST request
25
- var form = document.createElement('form');
26
- form.method = 'POST';
27
- form.action = '/preview_view';
28
- form.target = '_blank'; // Open in new tab/window
29
-
30
- // Add the view_name parameter
31
- var input = document.createElement('input');
32
- input.type = 'hidden';
33
- input.name = 'view_name';
34
- input.value = viewName;
35
- form.appendChild(input);
39
+ // Show lightweight popup while the new tab loads
40
+ try { showPreviewPopup(); } catch(e) {}
41
+ // Open preview in new tab with GET request
42
+ window.open('/preview', '_blank');
43
+ }
44
+
45
+ function changeView(viewName) {
46
+ // Submit form via AJAX to avoid URL change
47
+ const formData = new FormData();
48
+ formData.append('view_name', viewName);
36
49
 
37
- // Submit the form
38
- document.body.appendChild(form);
39
- form.submit();
40
- document.body.removeChild(form);
50
+ fetch('/change_view', {
51
+ method: 'POST',
52
+ body: formData
53
+ })
54
+ .then(response => {
55
+ if (response.ok) {
56
+ // Reload the page to show the new view
57
+ window.location.reload();
58
+ } else {
59
+ alert('Failed to change view');
60
+ }
61
+ })
62
+ .catch(error => {
63
+ console.error('Error:', error);
64
+ alert('Error changing view');
65
+ });
41
66
  }
42
67
  </script>
43
68
  </head>
@@ -70,36 +95,36 @@
70
95
  <th>Title <em style="font-size: 0.8em; font-weight: normal;">(Click to configure)</em></th>
71
96
  </tr>
72
97
  <% @views.each do |view| %>
73
- <tr style="<%= 'background-color: #e3f2fd;' if @current_view && view.name == @current_view.name %>">
74
- <td>
75
- <form method="post" action="/change_view" style="display: inline;">
76
- <input type="hidden" name="view_name" value="<%= view.name %>">
77
- <button type="submit" style="background: none; border: none; color: #007cba; cursor: pointer; font-family: monospace; font-size: inherit;" title="Change to <%= view.name %>">
78
- <%= view.name %>
79
- </button>
80
- </form>
81
- </td>
82
- <td>
83
- <a href="/configure_view/<%= view.name %>" style="color: #007cba; text-decoration: none; cursor: pointer;" title="Configure <%= view.name %>">
84
- <%= view.title %><%= ' (Current)' if @current_view && view.name == @current_view.name %>
85
- </a>
86
- </td>
87
- </tr>
98
+ <% if @current_view && view.name == @current_view.name %>
99
+ <!-- Current view - entire row clickable but does nothing -->
100
+ <tr style="background-color: #e3f2fd; cursor: pointer;" onclick="void(0)" title="Current view (clicking does nothing)">
101
+ <td style="color: #007cba; font-family: monospace;">
102
+ <%= view.name %>
103
+ </td>
104
+ <td style="color: #007cba;">
105
+ <%= view.title %> (Current)
106
+ </td>
107
+ </tr>
108
+ <% else %>
109
+ <!-- Other views - entire row clickable -->
110
+ <tr style="cursor: pointer;" onclick="changeView('<%= view.name %>')" title="Change to <%= view.name %>">
111
+ <td style="color: #007cba; font-family: monospace;">
112
+ <%= view.name %>
113
+ </td>
114
+ <td style="color: #007cba;">
115
+ <%= view.title %>
116
+ </td>
117
+ </tr>
118
+ <% end %>
88
119
  <% end %>
89
120
  </table>
90
121
 
91
122
  <% if @current_view %>
92
123
  <div style="margin-top: 15px; padding: 10px; background-color: #f0f8ff; border: 1px solid #b0d4f1; border-radius: 3px;">
93
124
  <strong>Current view:</strong> <%= @current_view.name %>
94
- <form method="post" action="/generate_view" style="display: inline; margin-left: 10px;">
95
- <input type="hidden" name="view_name" value="<%= @current_view.name %>">
96
- <button type="submit" class="button">Generate</button>
97
- </form>
98
- <button type="button" class="button" onclick="previewView('<%= @current_view.name %>')" style="margin-left: 5px;">Preview</button>
99
- <a href="/banner_config" class="button" style="margin-left: 5px; text-decoration: none;">Banner Config</a>
100
- <a href="/navbar_config" class="button" style="margin-left: 5px; text-decoration: none;">Navbar Config</a>
125
+ <a href="/view/<%= @current_view.name %>" class="button" style="margin-left: 10px; text-decoration: none;">View Dashboard</a>
101
126
  <a href="/asset_management" class="button" style="margin-left: 5px; text-decoration: none;">Asset Management</a>
102
- <a href="/view/<%= @current_view.name %>" class="button" style="margin-left: 5px; text-decoration: none;">View Dashboard</a>
127
+ <a href="/theme_management" class="button" style="margin-left: 5px; text-decoration: none;">Theme Management</a>
103
128
  </div>
104
129
  <% end %>
105
130
  <% else %>
@@ -114,40 +139,7 @@
114
139
  </form>
115
140
  </div>
116
141
 
117
- <% if @current_view %>
118
- <div class="section">
119
- <h3>Create new post in <%= @current_view.name %></h3>
120
- <form method="post" action="/create_post">
121
- <input type="text" name="title" placeholder="Post title" required>
122
- <button type="submit" class="button">Create new post</button>
123
- </form>
124
- </div>
125
142
 
126
- <% if @posts && !@posts.empty? %>
127
- <div class="section">
128
- <table class="posts-table">
129
- <tr>
130
- <th>ID</th>
131
- <th>Date</th>
132
- <th>Title <em style="font-size: 0.8em; font-weight: normal;">(Click to edit)</em></th>
133
- <th>Views</th>
134
- </tr>
135
- <% @posts.each do |post| %>
136
- <tr>
137
- <td class="post-id"><%= post.id %></td>
138
- <td><%= post.pubdate&.split(' ')&.first %></td>
139
- <td>
140
- <a href="/edit_post/<%= post.id %>" style="color: #007cba; text-decoration: none; cursor: pointer;" title="Edit <%= post.title %>">
141
- <%= post.title %>
142
- </a>
143
- </td>
144
- <td><%= post.views_array.join(', ') %></td>
145
- </tr>
146
- <% end %>
147
- </table>
148
- </div>
149
- <% end %>
150
- <% end %>
151
143
  </div>
152
144
  <% else %>
153
145
  <div class="section">
@@ -126,6 +126,9 @@
126
126
  </div>
127
127
 
128
128
  <form method="post" action="/deploy_config">
129
+ <% if params[:from_deploy] == "1" %>
130
+ <input type="hidden" name="from_deploy" value="1">
131
+ <% end %>
129
132
  <div class="form-group">
130
133
  <label for="deploy_config">Deployment Configuration:</label>
131
134
  <textarea id="deploy_config" name="deploy_config" placeholder="user root
@@ -158,11 +158,11 @@
158
158
 
159
159
  <div id="edit-form" class="edit-form">
160
160
  <h4 id="edit-filename"></h4>
161
- <form method="post" action="/edit_pages/save">
161
+ <form id="page-form" method="post" action="/edit_pages/save">
162
162
  <input type="hidden" name="filename" id="edit-filename-input">
163
163
  <textarea name="content" id="edit-content" placeholder="Enter page content..."></textarea>
164
164
  <div class="buttons">
165
- <button type="submit" class="btn btn-primary">Save Page</button>
165
+ <button type="button" class="btn btn-primary" onclick="savePage()">Save Page</button>
166
166
  <button type="button" class="btn btn-secondary" onclick="hideEditForm()">Cancel</button>
167
167
  </div>
168
168
  </form>
@@ -176,20 +176,188 @@
176
176
  </div>
177
177
  </div>
178
178
 
179
+ <!-- CodeMirror CSS -->
180
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.css">
181
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/theme/monokai.min.css">
182
+
183
+ <!-- CodeMirror JavaScript -->
184
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.js"></script>
179
185
  <script>
186
+ // LiveText mode for CodeMirror
187
+ CodeMirror.defineMode("livetext", function() {
188
+ return {
189
+ startState: function() {
190
+ return {
191
+ inRaw: false,
192
+ inComment: false,
193
+ inDef: false,
194
+ inFunc: false,
195
+ inCode: false,
196
+ codeLanguage: null
197
+ };
198
+ },
199
+
200
+ token: function(stream, state) {
201
+ // Raw blocks
202
+ if (stream.match(/^\.raw\b/)) {
203
+ state.inRaw = true;
204
+ return "keyword";
205
+ }
206
+ if (state.inRaw && stream.match(/^__RAW__/)) {
207
+ state.inRaw = false;
208
+ return "keyword";
209
+ }
210
+ if (state.inRaw) {
211
+ stream.skipToEnd();
212
+ return "string";
213
+ }
214
+
215
+ // Comment blocks
216
+ if (stream.match(/^\.comment\b/)) {
217
+ state.inComment = true;
218
+ return "keyword";
219
+ }
220
+ if (state.inComment && stream.match(/^\.end\b/)) {
221
+ state.inComment = false;
222
+ return "keyword";
223
+ }
224
+ if (state.inComment) {
225
+ stream.skipToEnd();
226
+ return "comment";
227
+ }
228
+
229
+ // Code blocks
230
+ if (stream.match(/^\.code\s+(ruby|elixir|bash)\b/)) {
231
+ state.inCode = true;
232
+ state.codeLanguage = stream.match(/^\.code\s+(ruby|elixir|bash)/)[1];
233
+ return "keyword";
234
+ }
235
+ if (state.inCode && stream.match(/^\.end\b/)) {
236
+ state.inCode = false;
237
+ state.codeLanguage = null;
238
+ return "keyword";
239
+ }
240
+ if (state.inCode) {
241
+ stream.skipToEnd();
242
+ return "string";
243
+ }
244
+
245
+ // Dot commands
246
+ if (stream.match(/^\.\w+/)) {
247
+ return "keyword";
248
+ }
249
+
250
+ // Variables
251
+ if (stream.match(/^\$\w+/)) {
252
+ return "variable";
253
+ }
254
+
255
+ // Functions
256
+ if (stream.match(/^\$\$\w+/)) {
257
+ return "function";
258
+ }
259
+
260
+ // Markers
261
+ if (stream.match(/^[\*_`~]/)) {
262
+ return "formatting";
263
+ }
264
+
265
+ // Default
266
+ stream.next();
267
+ return null;
268
+ }
269
+ };
270
+ });
271
+ </script>
272
+
273
+ <script>
274
+ let editor = null;
275
+
180
276
  function editPage(filename, content) {
277
+ alert('editPage called with filename: ' + filename);
278
+ console.log('editPage called with filename:', filename, 'content length:', content.length);
181
279
  document.getElementById('edit-filename').textContent = filename;
182
280
  document.getElementById('edit-filename-input').value = filename;
183
281
  document.getElementById('edit-content').value = content;
184
282
  document.getElementById('edit-form').classList.add('active');
283
+ console.log('Edit form activated');
284
+
285
+ // Initialize CodeMirror if not already done
286
+ if (!editor) {
287
+ console.log('Initializing CodeMirror...');
288
+ editor = CodeMirror.fromTextArea(document.getElementById('edit-content'), {
289
+ mode: 'livetext',
290
+ theme: 'monokai',
291
+ lineNumbers: true,
292
+ lineWrapping: false,
293
+ indentUnit: 2,
294
+ tabSize: 2,
295
+ indentWithTabs: false,
296
+ scrollbarStyle: 'native',
297
+ viewportMargin: Infinity
298
+ });
299
+ console.log('CodeMirror initialized');
300
+
301
+ // Hide the original textarea
302
+ document.getElementById('edit-content').style.display = 'none';
303
+ } else {
304
+ console.log('Updating existing CodeMirror content');
305
+ // Update content in existing editor
306
+ editor.setValue(content);
307
+ }
185
308
 
186
309
  // Scroll to edit form
310
+ document.getElementById('edit-form').classList.add('active');
187
311
  document.getElementById('edit-form').scrollIntoView({ behavior: 'smooth' });
312
+ console.log('Edit form should now be visible');
188
313
  }
189
314
 
190
315
  function hideEditForm() {
191
316
  document.getElementById('edit-form').classList.remove('active');
192
317
  }
318
+
319
+ function savePage() {
320
+ alert('savePage called');
321
+ console.log('savePage called');
322
+
323
+ // Sync CodeMirror content back to the hidden textarea
324
+ if (editor) {
325
+ const content = editor.getValue();
326
+ console.log('CodeMirror content length:', content.length);
327
+ document.getElementById('edit-content').value = content;
328
+ console.log('Textarea value set, length:', document.getElementById('edit-content').value.length);
329
+ } else {
330
+ console.log('No editor instance found');
331
+ }
332
+
333
+ // Get form data
334
+ const form = document.getElementById('page-form');
335
+ const formData = new FormData(form);
336
+
337
+ console.log('Form data:');
338
+ for (let [key, value] of formData.entries()) {
339
+ console.log(`${key}: ${value}`);
340
+ }
341
+
342
+ // Submit the form
343
+ console.log('Submitting form...');
344
+ form.submit();
345
+ }
346
+
347
+ function syncCodeMirrorContent() {
348
+ console.log('syncCodeMirrorContent called');
349
+ if (editor) {
350
+ // Sync CodeMirror content back to the hidden textarea
351
+ const content = editor.getValue();
352
+ console.log('CodeMirror content length:', content.length);
353
+ document.getElementById('edit-content').value = content;
354
+ console.log('Textarea value set, length:', document.getElementById('edit-content').value.length);
355
+ } else {
356
+ console.log('No editor instance found');
357
+ }
358
+ console.log('Form submission proceeding...');
359
+ return true; // Allow form submission
360
+ }
193
361
  </script>
194
362
  </body>
195
363
  </html>