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,773 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>CodeMirror + LiveText Demo</title>
7
+
8
+ <!-- CodeMirror CDN -->
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.js"></script>
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.css">
11
+
12
+ <!-- CodeMirror themes -->
13
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/theme/monokai.min.css">
14
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/theme/eclipse.min.css">
15
+
16
+ <style>
17
+ body {
18
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
19
+ margin: 20px;
20
+ background: #f5f5f5;
21
+ }
22
+
23
+ .container {
24
+ max-width: 1200px;
25
+ margin: 0 auto;
26
+ background: white;
27
+ border-radius: 8px;
28
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
29
+ overflow: hidden;
30
+ }
31
+
32
+ .header {
33
+ background: #2c3e50;
34
+ color: white;
35
+ padding: 20px;
36
+ }
37
+
38
+ .header h1 {
39
+ margin: 0;
40
+ font-size: 24px;
41
+ }
42
+
43
+ .controls {
44
+ background: #ecf0f1;
45
+ padding: 15px;
46
+ border-bottom: 1px solid #bdc3c7;
47
+ }
48
+
49
+ .control-group {
50
+ display: inline-block;
51
+ margin-right: 20px;
52
+ }
53
+
54
+ .control-group label {
55
+ font-weight: bold;
56
+ margin-right: 8px;
57
+ }
58
+
59
+ .control-group select, .control-group button {
60
+ padding: 5px 10px;
61
+ border: 1px solid #bdc3c7;
62
+ border-radius: 4px;
63
+ background: white;
64
+ }
65
+
66
+ .macro-toolbar {
67
+ background: #f8f9fa;
68
+ padding: 10px 15px;
69
+ border-bottom: 1px solid #dee2e6;
70
+ display: flex;
71
+ align-items: center;
72
+ gap: 10px;
73
+ }
74
+
75
+ .toolbar-label {
76
+ font-weight: bold;
77
+ color: #495057;
78
+ margin-right: 10px;
79
+ }
80
+
81
+ .macro-btn {
82
+ padding: 8px 12px;
83
+ border: 1px solid #ced4da;
84
+ border-radius: 4px;
85
+ background: white;
86
+ cursor: pointer;
87
+ font-size: 16px;
88
+ transition: all 0.2s;
89
+ }
90
+
91
+ .macro-btn:hover {
92
+ background: #e9ecef;
93
+ border-color: #adb5bd;
94
+ transform: translateY(-1px);
95
+ }
96
+
97
+ .macro-btn:active {
98
+ transform: translateY(0);
99
+ background: #dee2e6;
100
+ }
101
+
102
+ .editor-container {
103
+ padding: 20px;
104
+ }
105
+
106
+ .CodeMirror {
107
+ height: 500px;
108
+ border: 1px solid #ddd;
109
+ border-radius: 4px;
110
+ font-size: 14px;
111
+ }
112
+
113
+ /* LiveText syntax highlighting */
114
+ .cm-strong { color: #e74c3c; font-weight: bold; }
115
+ .cm-em { color: #8e44ad; font-style: italic; }
116
+ .cm-string-2 { background: #f8f9fa; color: #495057; font-family: monospace; }
117
+ .cm-strikethrough { color: #95a5a6; text-decoration: line-through; }
118
+ .cm-variable { color: #3498db; font-weight: bold; }
119
+ .cm-function { color: #27ae60; font-weight: bold; }
120
+ .cm-keyword { color: #e67e22; font-weight: bold; }
121
+ .cm-comment { color: #7f8c8d; font-style: italic; }
122
+ .cm-bracket { color: #f39c12; font-weight: bold; }
123
+
124
+ .info {
125
+ background: #e8f4fd;
126
+ border: 1px solid #bee5eb;
127
+ border-radius: 4px;
128
+ padding: 15px;
129
+ margin: 20px;
130
+ }
131
+
132
+ .info h3 {
133
+ margin-top: 0;
134
+ color: #0c5460;
135
+ }
136
+
137
+ .features {
138
+ display: grid;
139
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
140
+ gap: 20px;
141
+ margin: 20px;
142
+ }
143
+
144
+ .feature {
145
+ background: #f8f9fa;
146
+ border: 1px solid #dee2e6;
147
+ border-radius: 4px;
148
+ padding: 15px;
149
+ }
150
+
151
+ .feature h4 {
152
+ margin-top: 0;
153
+ color: #495057;
154
+ }
155
+ </style>
156
+ </head>
157
+ <body>
158
+ <div class="container">
159
+ <div class="header">
160
+ <h1>CodeMirror + LiveText Demo</h1>
161
+ </div>
162
+
163
+ <div class="controls">
164
+ <div class="control-group">
165
+ <label>Theme:</label>
166
+ <select id="theme-selector">
167
+ <option value="default">Default</option>
168
+ <option value="monokai">Monokai (Dark)</option>
169
+ <option value="eclipse">Eclipse</option>
170
+ </select>
171
+ </div>
172
+
173
+ <div class="control-group">
174
+ <label>Mode:</label>
175
+ <select id="mode-selector">
176
+ <option value="livetext">LiveText</option>
177
+ <option value="markdown">Markdown</option>
178
+ <option value="html">HTML</option>
179
+ <option value="css">CSS</option>
180
+ <option value="javascript">JavaScript</option>
181
+ </select>
182
+ </div>
183
+
184
+ <div class="control-group">
185
+ <button id="toggle-linenumbers">Toggle Line Numbers</button>
186
+ </div>
187
+
188
+ <div class="control-group">
189
+ <button id="toggle-wordwrap">Toggle Word Wrap</button>
190
+ </div>
191
+ </div>
192
+
193
+ <div class="macro-toolbar">
194
+ <span class="toolbar-label">Macros:</span>
195
+ <button onclick="runMacro('format_list')" title="Format as list" class="macro-btn">📋</button>
196
+ <button onclick="runMacro('add_metadata')" title="Add metadata block" class="macro-btn">📝</button>
197
+ <button onclick="runMacro('wrap_code')" title="Wrap in code block" class="macro-btn">💻</button>
198
+ <button onclick="runMacro('bold_selection')" title="Make bold" class="macro-btn">**</button>
199
+ <button onclick="runMacro('italic_selection')" title="Make italic" class="macro-btn">__</button>
200
+ <button onclick="runMacro('code_selection')" title="Make code" class="macro-btn">``</button>
201
+ </div>
202
+
203
+ <div class="editor-container">
204
+ <textarea id="editor" style="display: block; height: 500px; font-family: monospace; font-size: 14px; width: 100%; border: 1px solid #ddd; border-radius: 4px; padding: 10px;">
205
+ .h1 Sample LiveText Post
206
+
207
+ This is a sample LiveText post that demonstrates how CodeMirror could enhance the editing experience in Scriptorium.
208
+
209
+ .h3 Features Demonstrated
210
+
211
+ .list
212
+ *Syntax highlighting for LiveText syntax
213
+ **Line numbers for easy navigation
214
+ `Multiple themes (light/dark)
215
+ ~Word wrap toggle
216
+ **Search and replace (Ctrl+F/Cmd+F)
217
+ **Multiple editing modes
218
+ .end
219
+
220
+ .h3 Variables and Functions
221
+
222
+ The current user is $username and their role is $user.role.
223
+ We can call $$max($count, 10) to get the maximum value.
224
+ Use $$link:https://scriptorium.dev for the main site.
225
+ Learn $$index[functional programming] today!
226
+
227
+ .h3 Dot Commands
228
+
229
+ .banner Welcome to Scriptorium!
230
+ .index web development
231
+ .comment This is a block comment that won't be processed
232
+ .end
233
+ $.banner This is an indented banner command
234
+
235
+ .h3 Code and Formatting
236
+
237
+ This is `inline code and this is **bold text.
238
+ Here's some _italic content and ~~strikethrough text.
239
+
240
+ You can also use brackets for multi-word formatting:
241
+ *[This entire phrase is bold] and _[this entire phrase is italic].
242
+ For code: `[multiple words of code] and ~[strikethrough text].
243
+
244
+ For longer code blocks, we can use brackets:
245
+ `[def create_post(title, content)
246
+ post = Post.new
247
+ post.title = title
248
+ post.content = content
249
+ post.save
250
+ end]`
251
+
252
+ .h3 Lists and Structure
253
+
254
+ .list
255
+ **First item with *emphasis
256
+ `Second item with code
257
+ Third item with $variable reference
258
+ .end
259
+
260
+ .list
261
+ Numbered item with $$function call
262
+ Another item with _italic text
263
+ .end
264
+
265
+ .h3 Metadata
266
+
267
+ .list
268
+ **Author: $current_user
269
+ **Created: $$date
270
+ **Status: Draft
271
+ **Tags: demo, codemirror, livetext
272
+ .end
273
+
274
+ .h3 Content
275
+
276
+ This is the main content area where you would write your blog post. CodeMirror provides:
277
+
278
+ .list
279
+ **Better editing experience than basic textareas
280
+ *Syntax highlighting for LiveText syntax
281
+ `Line numbers for easy reference
282
+ **Multiple themes for different preferences
283
+ **Keyboard shortcuts for power users
284
+ **Search and replace functionality
285
+ **Word wrap for long lines
286
+ **Multiple language modes for different content types
287
+ .end
288
+
289
+ The editor automatically handles indentation, provides visual feedback, and makes editing much more pleasant than a basic HTML textarea.
290
+
291
+ .comment This is a comment line that shows comment highlighting
292
+
293
+ .raw
294
+ This is raw content that won't be processed.
295
+ It can contain $variables and $$functions that won't expand.
296
+ It can even have .banner commands that won't execute.
297
+ Even .code ruby blocks are treated as plain text here.
298
+ __RAW__
299
+
300
+ .h3 Code Examples
301
+
302
+ .def
303
+ def create_post(title, content)
304
+ post = Post.new
305
+ post.title = title
306
+ post.content = content
307
+ post.save
308
+ end
309
+ .end
310
+
311
+ .code elixir
312
+ defmodule Post do
313
+ def create(title, content) do
314
+ %Post{title: title, content: content}
315
+ end
316
+ end
317
+ .end
318
+
319
+ .code bash
320
+ #!/bin/bash
321
+ for file in *.lt3; do
322
+ echo "Processing $file"
323
+ ./process.sh "$file"
324
+ done
325
+ .end
326
+ </textarea>
327
+ </div>
328
+
329
+ <div class="info">
330
+ <h3>What You're Seeing</h3>
331
+ <p>This is a standalone demo of CodeMirror integrated with LiveText-like content. It shows exactly what you'd get if you added CodeMirror to your Scriptorium web interface.</p>
332
+ </div>
333
+
334
+ <div class="features">
335
+ <div class="feature">
336
+ <h4>Immediate Benefits</h4>
337
+ <ul>
338
+ <li>Better editing experience</li>
339
+ <li>Syntax highlighting</li>
340
+ <li>Line numbers</li>
341
+ <li>Multiple themes</li>
342
+ </ul>
343
+ </div>
344
+
345
+ <div class="feature">
346
+ <h4>Advanced Features</h4>
347
+ <ul>
348
+ <li>Search & replace</li>
349
+ <li>Keyboard shortcuts</li>
350
+ <li>Word wrap</li>
351
+ <li>Multiple modes</li>
352
+ </ul>
353
+ </div>
354
+
355
+ <div class="feature">
356
+ <h4>Integration Points</h4>
357
+ <ul>
358
+ <li>Post editing</li>
359
+ <li>Configuration editing</li>
360
+ <li>Template editing</li>
361
+ <li>Asset editing</li>
362
+ </ul>
363
+ </div>
364
+ </div>
365
+ </div>
366
+
367
+ <script>
368
+ // Custom LiveText syntax highlighting mode
369
+ CodeMirror.defineMode("livetext", function() {
370
+ return {
371
+ startState: function() {
372
+ return {
373
+ inRaw: false,
374
+ inCodeBody: false,
375
+ codeLanguage: null,
376
+ inBracket: false,
377
+ bracketDepth: 0
378
+ };
379
+ },
380
+
381
+ token: function(stream, state) {
382
+ // Skip whitespace
383
+ if (stream.eatSpace()) return null;
384
+
385
+ // Raw blocks override everything else
386
+ if (state.inRaw) {
387
+ if (stream.match(/^__RAW__/) || stream.match(/^\.end$/)) {
388
+ state.inRaw = false;
389
+ return "keyword";
390
+ }
391
+ stream.skipToEnd();
392
+ return "comment"; // Plain text in raw blocks
393
+ }
394
+
395
+ // Check for .raw command
396
+ if (stream.match(/^\.raw$/)) {
397
+ state.inRaw = true;
398
+ return "keyword";
399
+ }
400
+
401
+ // Check for .comment command
402
+ if (stream.match(/^\.comment$/)) {
403
+ state.inRaw = true; // Use same state as raw for plain text
404
+ return "keyword";
405
+ }
406
+
407
+ // Code body blocks
408
+ if (state.inCodeBody) {
409
+ if (stream.match(/^\.end$/)) {
410
+ state.inCodeBody = false;
411
+ state.codeLanguage = null;
412
+ return "keyword";
413
+ }
414
+
415
+ // Use appropriate language highlighting
416
+ if (state.codeLanguage === "ruby") {
417
+ // Ruby syntax highlighting
418
+ if (stream.match(/^#/)) {
419
+ stream.skipToEnd();
420
+ return "comment";
421
+ }
422
+ if (stream.match(/^def\b/)) return "def";
423
+ if (stream.match(/^end\b/)) return "keyword";
424
+ if (stream.match(/^class\b/)) return "def";
425
+ if (stream.match(/^module\b/)) return "def";
426
+ if (stream.match(/^require\b/)) return "keyword";
427
+ if (stream.match(/^puts\b/)) return "keyword";
428
+ if (stream.match(/^[A-Z][a-zA-Z0-9_]*/)) return "variable-2"; // Constants
429
+ if (stream.match(/^[a-z_][a-zA-Z0-9_]*/)) return "variable"; // Variables
430
+ if (stream.match(/^[0-9]+/)) return "number";
431
+ if (stream.match(/^["']/)) {
432
+ stream.skipTo(/["']/);
433
+ return "string";
434
+ }
435
+ } else if (state.codeLanguage === "elixir") {
436
+ // Elixir syntax highlighting
437
+ if (stream.match(/^#/)) {
438
+ stream.skipToEnd();
439
+ return "comment";
440
+ }
441
+ if (stream.match(/^def\b/)) return "def";
442
+ if (stream.match(/^end\b/)) return "keyword";
443
+ if (stream.match(/^defmodule\b/)) return "def";
444
+ if (stream.match(/^defp\b/)) return "def";
445
+ if (stream.match(/^IO\.puts\b/)) return "keyword";
446
+ if (stream.match(/^[A-Z][a-zA-Z0-9_]*/)) return "variable-2"; // Modules
447
+ if (stream.match(/^[a-z_][a-zA-Z0-9_]*/)) return "keyword"; // Functions
448
+ if (stream.match(/^[0-9]+/)) return "number";
449
+ if (stream.match(/^["']/)) {
450
+ stream.skipTo(/["']/);
451
+ return "string";
452
+ }
453
+ } else if (state.codeLanguage === "bash") {
454
+ // Bash syntax highlighting
455
+ if (stream.match(/^#/)) {
456
+ stream.skipToEnd();
457
+ return "comment";
458
+ }
459
+ if (stream.match(/^if\b/)) return "keyword";
460
+ if (stream.match(/^then\b/)) return "keyword";
461
+ if (stream.match(/^fi\b/)) return "keyword";
462
+ if (stream.match(/^for\b/)) return "keyword";
463
+ if (stream.match(/^do\b/)) return "keyword";
464
+ if (stream.match(/^done\b/)) return "keyword";
465
+ if (stream.match(/^\$[A-Z_]+/)) return "variable"; // Environment variables
466
+ if (stream.match(/^[0-9]+/)) return "number";
467
+ if (stream.match(/^["']/)) {
468
+ stream.skipTo(/["']/);
469
+ return "string";
470
+ }
471
+ }
472
+
473
+ stream.next();
474
+ return null;
475
+ }
476
+
477
+ // Check for code body commands
478
+ if (stream.match(/^\.(def|func)$/)) {
479
+ state.inCodeBody = true;
480
+ state.codeLanguage = "ruby";
481
+ return "keyword";
482
+ }
483
+
484
+ let codeMatch = stream.match(/^\.code\s+(ruby|elixir|bash)/);
485
+ if (codeMatch) {
486
+ state.inCodeBody = true;
487
+ state.codeLanguage = codeMatch[1];
488
+ return "keyword";
489
+ }
490
+
491
+ // One-line comments (. comment)
492
+ if (stream.match(/^\.\s/)) {
493
+ stream.skipToEnd();
494
+ return "comment";
495
+ }
496
+
497
+ // Dot commands (.command or $.command)
498
+ if (stream.match(/^\.\w+/)) {
499
+ return "keyword";
500
+ }
501
+ if (stream.match(/^\s+\$\.[\w.]+/)) {
502
+ return "keyword";
503
+ }
504
+
505
+ // Variables ($foo, $foo.bar) - stop at invalid characters
506
+ if (stream.match(/^\$/)) {
507
+ let name = "";
508
+ let ch = stream.next();
509
+ if (ch && /[a-zA-Z]/.test(ch)) {
510
+ name += ch;
511
+ while (stream.peek() && /[\w.]/.test(stream.peek())) {
512
+ ch = stream.next();
513
+ name += ch;
514
+ }
515
+ // Check if next character is invalid for a name
516
+ if (stream.peek() && !/[\w.]/.test(stream.peek())) {
517
+ return "variable";
518
+ }
519
+ }
520
+ return "variable";
521
+ }
522
+
523
+ // Functions ($$foo, $$foo:param, $$foo[param])
524
+ if (stream.match(/^\$\$/)) {
525
+ let name = "";
526
+ let ch = stream.next();
527
+ if (ch && /[a-zA-Z]/.test(ch)) {
528
+ name += ch;
529
+ while (stream.peek() && /[\w.]/.test(stream.peek())) {
530
+ ch = stream.next();
531
+ name += ch;
532
+ }
533
+ // Check for colon parameter
534
+ if (stream.peek() === ":") {
535
+ stream.next();
536
+ stream.skipToEnd();
537
+ }
538
+ return "function";
539
+ }
540
+ return "function";
541
+ }
542
+
543
+ // Bracketed content [content]
544
+ if (stream.match(/^\[/)) {
545
+ state.inBracket = true;
546
+ state.bracketDepth = 1;
547
+ return "bracket";
548
+ }
549
+
550
+ if (state.inBracket) {
551
+ if (stream.match(/^\]/)) {
552
+ state.inBracket = false;
553
+ state.bracketDepth = 0;
554
+ return "bracket";
555
+ }
556
+ // Continue reading bracket content
557
+ stream.next();
558
+ return "string";
559
+ }
560
+
561
+ // Bracketed formatting: *[text] _[text] `[text] ~[text]
562
+ if (stream.match(/^(\*|_|`|~)\[/)) {
563
+ state.inBracket = true;
564
+ state.bracketDepth = 1;
565
+ return "strong"; // All bracketed content gets same treatment for now
566
+ }
567
+
568
+ // Markers (* _ ` ~) - single and doubled
569
+ // Note: LiveText markers only initiate, never terminate
570
+ if (stream.match(/^\*\*/)) {
571
+ // Doubled marker - read until space, comma, or period
572
+ stream.eatWhile(/[^\s,.]/);
573
+ return "strong";
574
+ }
575
+ if (stream.match(/^__/)) {
576
+ stream.eatWhile(/[^\s,.]/);
577
+ return "em";
578
+ }
579
+ if (stream.match(/^``/)) {
580
+ stream.eatWhile(/[^\s,.]/);
581
+ return "string-2";
582
+ }
583
+ if (stream.match(/^~~/)) {
584
+ stream.eatWhile(/[^\s,.]/);
585
+ return "strikethrough";
586
+ }
587
+
588
+ // Single markers
589
+ if (stream.match(/^\*/)) {
590
+ stream.eatWhile(/[^\s]/);
591
+ return "strong";
592
+ }
593
+ if (stream.match(/^_/)) {
594
+ stream.eatWhile(/[^\s]/);
595
+ return "em";
596
+ }
597
+ if (stream.match(/^`/)) {
598
+ stream.eatWhile(/[^\s]/);
599
+ return "string-2";
600
+ }
601
+ if (stream.match(/^~/)) {
602
+ stream.eatWhile(/[^\s]/);
603
+ return "strikethrough";
604
+ }
605
+
606
+ // Continue reading
607
+ stream.next();
608
+ return null;
609
+ }
610
+ };
611
+ });
612
+
613
+ // Initialize CodeMirror
614
+ let editor;
615
+ try {
616
+ editor = CodeMirror.fromTextArea(document.getElementById('editor'), {
617
+ mode: 'livetext', // Use our custom LiveText mode
618
+ theme: 'default',
619
+ lineNumbers: true,
620
+ lineWrapping: false,
621
+ indentUnit: 2,
622
+ tabSize: 2,
623
+ indentWithTabs: false,
624
+ scrollbarStyle: 'native',
625
+ viewportMargin: Infinity,
626
+ extraKeys: {
627
+ "Ctrl-S": function(cm) {
628
+ // Simulate save
629
+ console.log("Save triggered");
630
+ },
631
+ "Cmd-S": function(cm) {
632
+ // Simulate save on Mac
633
+ console.log("Save triggered");
634
+ }
635
+ }
636
+ });
637
+ console.log("CodeMirror initialized successfully");
638
+ // Hide the original textarea since CodeMirror is now handling it
639
+ document.getElementById('editor').style.display = 'none';
640
+ } catch (error) {
641
+ console.error("CodeMirror initialization failed:", error);
642
+ // Fallback: show the textarea content
643
+ document.getElementById('editor').style.display = 'block';
644
+ document.getElementById('editor').style.height = '500px';
645
+ document.getElementById('editor').style.fontFamily = 'monospace';
646
+ document.getElementById('editor').style.fontSize = '14px';
647
+ }
648
+
649
+ // Theme selector
650
+ document.getElementById('theme-selector').addEventListener('change', function() {
651
+ console.log('Theme changed to:', this.value);
652
+ editor.setOption('theme', this.value);
653
+ });
654
+
655
+ // Mode selector
656
+ document.getElementById('mode-selector').addEventListener('change', function() {
657
+ let mode = this.value;
658
+ console.log('Mode changed to:', mode);
659
+ editor.setOption('mode', mode);
660
+ });
661
+
662
+ // Toggle line numbers
663
+ document.getElementById('toggle-linenumbers').addEventListener('click', function() {
664
+ let current = editor.getOption('lineNumbers');
665
+ console.log('Line numbers:', current, '->', !current);
666
+ editor.setOption('lineNumbers', !current);
667
+ });
668
+
669
+ // Toggle word wrap
670
+ document.getElementById('toggle-wordwrap').addEventListener('click', function() {
671
+ let current = editor.getOption('lineWrapping');
672
+ console.log('Word wrap:', current, '->', !current);
673
+ editor.setOption('lineWrapping', !current);
674
+ });
675
+
676
+ // Set initial size
677
+ editor.setSize('100%', '500px');
678
+
679
+ // Macro system
680
+ const macros = {
681
+ format_list: function(editor) {
682
+ const selection = editor.getSelection();
683
+ if (selection) {
684
+ const lines = selection.split('\n').filter(line => line.trim());
685
+ const formatted = lines.map(line => `*${line.trim()}`).join('\n');
686
+ editor.replaceSelection(formatted);
687
+ } else {
688
+ // If no selection, format current line
689
+ const line = editor.getLine(editor.getCursor().line);
690
+ if (line.trim()) {
691
+ editor.replaceRange(`*${line.trim()}`,
692
+ {line: editor.getCursor().line, ch: 0},
693
+ {line: editor.getCursor().line, ch: line.length});
694
+ }
695
+ }
696
+ },
697
+
698
+ add_metadata: function(editor) {
699
+ const metadata = `.metadata\n**Author: $username\n**Date: $$date\n**Status: Draft\n**Tags: \n.end`;
700
+ editor.replaceSelection(metadata);
701
+ },
702
+
703
+ wrap_code: function(editor) {
704
+ const selection = editor.getSelection();
705
+ if (selection) {
706
+ editor.replaceSelection(`\`[${selection}]`);
707
+ } else {
708
+ // If no selection, wrap current word
709
+ const cursor = editor.getCursor();
710
+ const word = editor.findWordAt(cursor);
711
+ if (word) {
712
+ const text = editor.getRange(word.anchor, word.head);
713
+ editor.replaceRange(`\`[${text}]`, word.anchor, word.head);
714
+ }
715
+ }
716
+ },
717
+
718
+ bold_selection: function(editor) {
719
+ const selection = editor.getSelection();
720
+ if (selection) {
721
+ editor.replaceSelection(`*[${selection}]`);
722
+ } else {
723
+ const cursor = editor.getCursor();
724
+ const word = editor.findWordAt(cursor);
725
+ if (word) {
726
+ const text = editor.getRange(word.anchor, word.head);
727
+ editor.replaceRange(`*[${text}]`, word.anchor, word.head);
728
+ }
729
+ }
730
+ },
731
+
732
+ italic_selection: function(editor) {
733
+ const selection = editor.getSelection();
734
+ if (selection) {
735
+ editor.replaceSelection(`_[${selection}]`);
736
+ } else {
737
+ const cursor = editor.getCursor();
738
+ const word = editor.findWordAt(cursor);
739
+ if (word) {
740
+ const text = editor.getRange(word.anchor, word.head);
741
+ editor.replaceRange(`_[${text}]`, word.anchor, word.head);
742
+ }
743
+ }
744
+ },
745
+
746
+ code_selection: function(editor) {
747
+ const selection = editor.getSelection();
748
+ if (selection) {
749
+ editor.replaceSelection(`\`[${selection}]`);
750
+ } else {
751
+ const cursor = editor.getCursor();
752
+ const word = editor.findWordAt(cursor);
753
+ if (word) {
754
+ const text = editor.getRange(word.anchor, word.head);
755
+ editor.replaceRange(`\`[${text}]`, word.anchor, word.head);
756
+ }
757
+ }
758
+ }
759
+ };
760
+
761
+ function runMacro(macroName) {
762
+ const macro = macros[macroName];
763
+ if (macro) {
764
+ macro(editor);
765
+ editor.focus(); // Return focus to editor
766
+ }
767
+ }
768
+
769
+ // Focus the editor
770
+ editor.focus();
771
+ </script>
772
+ </body>
773
+ </html>