scriptorium 0.0.3 → 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 (353) hide show
  1. checksums.yaml +4 -4
  2. data/README.lt3 +324 -0
  3. data/README.md +3155 -1
  4. data/assets/.DS_Store +0 -0
  5. data/assets/README.md +44 -0
  6. data/assets/icons/social/reddit.png +0 -0
  7. data/assets/icons/social/x-logo.png +0 -0
  8. data/assets/icons/ui/.DS_Store +0 -0
  9. data/assets/icons/ui/back.png +0 -0
  10. data/assets/icons/ui/copy.png +0 -0
  11. data/assets/icons/ui/down.png +0 -0
  12. data/assets/icons/ui/end.png +0 -0
  13. data/assets/icons/ui/exit.png +0 -0
  14. data/assets/icons/ui/foo +10 -0
  15. data/assets/icons/ui/home.png +0 -0
  16. data/assets/icons/ui/left.png +0 -0
  17. data/assets/icons/ui/next.png +0 -0
  18. data/assets/icons/ui/right.png +0 -0
  19. data/assets/icons/ui/start.png +0 -0
  20. data/assets/icons/ui/up.png +0 -0
  21. data/assets/imagenotfound.jpg +0 -0
  22. data/assets/samples/placeholder.svg +9 -0
  23. data/assets/themes/standard/favicon.svg +6 -0
  24. data/bin/sblog +84 -5
  25. data/bin/scriptorium +1 -0
  26. data/doc/README.txt +6 -0
  27. data/doc/anti-amnesia/20250727-054000-scriptorium-overview.md +94 -0
  28. data/doc/anti-amnesia/20250727-123000-anti-amnesia-conventions.md +2 -0
  29. data/doc/anti-amnesia/20250727-172600-cursor-rbenv-ruby-version-mystery.md +45 -0
  30. data/doc/anti-amnesia/20250727-172900-ai-cognitive-assessment-capabilities.md +40 -0
  31. data/doc/anti-amnesia/20250728-124243-aaa-syntax-clarification.md +46 -0
  32. data/doc/anti-amnesia/20250729-210000-reddit-autopost-integration-complete.md +158 -0
  33. data/doc/anti-amnesia/20250804-190500-cognitive-loop-bug.md +35 -0
  34. data/doc/anti-amnesia/20250804-190700-anti-amnesia-timestamping-fix.md +27 -0
  35. data/doc/anti-amnesia/20250807-213025.md +116 -0
  36. data/doc/anti-amnesia/20250901-211714-codemirror-integration-and-web-tests.md +172 -0
  37. data/doc/anti-amnesia/20250902-002402-backup-restore-system.md +126 -0
  38. data/doc/anti-amnesia/20250907-203339-backup-metadata-implementation.md +66 -0
  39. data/doc/banner_svg_config.md +114 -0
  40. data/doc/contrib.lt3 +8 -0
  41. data/doc/dependencies.md +281 -0
  42. data/doc/hacker.lt3 +5 -0
  43. data/doc/imported/0001-elixir-conf-2014/metadata.txt +7 -0
  44. data/doc/imported/0001-elixir-conf-2014/post.html +37 -0
  45. data/doc/imported/0001-elixir-conf-2014/source.lt3 +22 -0
  46. data/doc/imported/0002-programmers-and-word-processing/metadata.txt +7 -0
  47. data/doc/imported/0002-programmers-and-word-processing/post.html +192 -0
  48. data/doc/imported/0002-programmers-and-word-processing/source.lt3 +146 -0
  49. data/doc/imported/0003-how-to-turn-your-brain-sideways/metadata.txt +7 -0
  50. data/doc/imported/0003-how-to-turn-your-brain-sideways/post.html +60 -0
  51. data/doc/imported/0003-how-to-turn-your-brain-sideways/source.lt3 +40 -0
  52. data/doc/imported/0004-upcoming-lone-star-ruby-conference/metadata.txt +7 -0
  53. data/doc/imported/0004-upcoming-lone-star-ruby-conference/post.html +42 -0
  54. data/doc/imported/0004-upcoming-lone-star-ruby-conference/source.lt3 +24 -0
  55. data/doc/imported/0005-elixir-conf-2015-announced/metadata.txt +7 -0
  56. data/doc/imported/0005-elixir-conf-2015-announced/post.html +30 -0
  57. data/doc/imported/0005-elixir-conf-2015-announced/source.lt3 +16 -0
  58. data/doc/imported/0006-ruby-for-dinosaurs/metadata.txt +7 -0
  59. data/doc/imported/0006-ruby-for-dinosaurs/post.html +43 -0
  60. data/doc/imported/0006-ruby-for-dinosaurs/source.lt3 +27 -0
  61. data/doc/imported/0007-phoenix-isnt-rails/metadata.txt +7 -0
  62. data/doc/imported/0007-phoenix-isnt-rails/post.html +116 -0
  63. data/doc/imported/0007-phoenix-isnt-rails/source.lt3 +87 -0
  64. data/doc/imported/0008-concerning-the-term-monkeypatching/metadata.txt +7 -0
  65. data/doc/imported/0008-concerning-the-term-monkeypatching/post.html +129 -0
  66. data/doc/imported/0008-concerning-the-term-monkeypatching/source.lt3 +92 -0
  67. data/doc/imported/0009-announcement-coming-soon/metadata.txt +7 -0
  68. data/doc/imported/0009-announcement-coming-soon/post.html +33 -0
  69. data/doc/imported/0009-announcement-coming-soon/source.lt3 +19 -0
  70. data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/metadata.txt +7 -0
  71. data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/post.html +175 -0
  72. data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/source.lt3 +139 -0
  73. data/doc/imported/0011-computer-science-as-a-lost-art/metadata.txt +7 -0
  74. data/doc/imported/0011-computer-science-as-a-lost-art/post.html +139 -0
  75. data/doc/imported/0011-computer-science-as-a-lost-art/source.lt3 +104 -0
  76. data/doc/imported/0012-ruby-day-in-turin-italy/metadata.txt +7 -0
  77. data/doc/imported/0012-ruby-day-in-turin-italy/post.html +42 -0
  78. data/doc/imported/0012-ruby-day-in-turin-italy/source.lt3 +24 -0
  79. data/doc/imported/0013-rubyday-was-a-success/metadata.txt +7 -0
  80. data/doc/imported/0013-rubyday-was-a-success/post.html +44 -0
  81. data/doc/imported/0013-rubyday-was-a-success/source.lt3 +27 -0
  82. data/doc/imported/0014-working-on-the-blogging-software/metadata.txt +7 -0
  83. data/doc/imported/0014-working-on-the-blogging-software/post.html +63 -0
  84. data/doc/imported/0014-working-on-the-blogging-software/source.lt3 +41 -0
  85. data/doc/imported/0015-ok-its-not-really-a-lost-art/metadata.txt +7 -0
  86. data/doc/imported/0015-ok-its-not-really-a-lost-art/post.html +172 -0
  87. data/doc/imported/0015-ok-its-not-really-a-lost-art/source.lt3 +134 -0
  88. data/doc/imported/0016-an-in-operator-for-ruby/metadata.txt +7 -0
  89. data/doc/imported/0016-an-in-operator-for-ruby/post.html +155 -0
  90. data/doc/imported/0016-an-in-operator-for-ruby/source.lt3 +106 -0
  91. data/doc/imported/0017-the-forgotten-mathematician/metadata.txt +7 -0
  92. data/doc/imported/0017-the-forgotten-mathematician/post.html +161 -0
  93. data/doc/imported/0017-the-forgotten-mathematician/source.lt3 +119 -0
  94. data/doc/imported/0018-ruby-puns/metadata.txt +7 -0
  95. data/doc/imported/0018-ruby-puns/post.html +46 -0
  96. data/doc/imported/0018-ruby-puns/source.lt3 +28 -0
  97. data/doc/imported/0019-custom-exceptions-via-metaprogramming/metadata.txt +7 -0
  98. data/doc/imported/0019-custom-exceptions-via-metaprogramming/post.html +138 -0
  99. data/doc/imported/0019-custom-exceptions-via-metaprogramming/source.lt3 +101 -0
  100. data/doc/imported/0020-fffff/metadata.txt +7 -0
  101. data/doc/imported/0020-fffff/post.html +24 -0
  102. data/doc/imported/0020-fffff/source.lt3 +12 -0
  103. data/doc/imported/0021-trying-ror-yet-again/metadata.txt +7 -0
  104. data/doc/imported/0021-trying-ror-yet-again/post.html +26 -0
  105. data/doc/imported/0021-trying-ror-yet-again/source.lt3 +12 -0
  106. data/doc/imported/0023-doctor-sleep/metadata.txt +7 -0
  107. data/doc/imported/0023-doctor-sleep/post.html +63 -0
  108. data/doc/imported/0023-doctor-sleep/source.lt3 +44 -0
  109. data/doc/imported/0024-just-a-test/metadata.txt +7 -0
  110. data/doc/imported/0024-just-a-test/post.html +24 -0
  111. data/doc/imported/0024-just-a-test/source.lt3 +12 -0
  112. data/doc/imported/import_summary.txt +98 -0
  113. data/doc/livetext-informal-spec.txt +65 -0
  114. data/doc/myuserdoc/ch-0.lt3 +31 -0
  115. data/doc/myuserdoc/ch-1.lt3 +37 -0
  116. data/doc/myuserdoc/ch-10.lt3 +22 -0
  117. data/doc/myuserdoc/ch-2.lt3 +37 -0
  118. data/doc/myuserdoc/ch-3.lt3 +19 -0
  119. data/doc/myuserdoc/ch-4.lt3 +43 -0
  120. data/doc/myuserdoc/ch-5.lt3 +22 -0
  121. data/doc/myuserdoc/ch-6.lt3 +19 -0
  122. data/doc/myuserdoc/ch-7.lt3 +16 -0
  123. data/doc/myuserdoc/ch-8.lt3 +13 -0
  124. data/doc/myuserdoc/ch-9.lt3 +19 -0
  125. data/doc/myuserdoc/tweak.rb +18 -0
  126. data/doc/myuserdoc/userdoc-toc.txt +88 -0
  127. data/doc/old-posts/0001-elixir-conf-2014.lt3 +24 -0
  128. data/doc/old-posts/0002-programmers-and-word-processing.lt3 +150 -0
  129. data/doc/old-posts/0003-how-to-turn-your-brain-sideways.lt3 +43 -0
  130. data/doc/old-posts/0004-upcoming-lone-star-ruby-conference.lt3 +26 -0
  131. data/doc/old-posts/0005-elixir-conf-2015-announced.lt3 +17 -0
  132. data/doc/old-posts/0006-ruby-for-dinosaurs.lt3 +30 -0
  133. data/doc/old-posts/0007-phoenix-isnt-rails.lt3 +90 -0
  134. data/doc/old-posts/0008-concerning-the-term-monkeypatching.lt3 +105 -0
  135. data/doc/old-posts/0009-announcement-coming-soon.lt3 +20 -0
  136. data/doc/old-posts/0010-immutable-data-ditching-the-wax-tablet.lt3 +142 -0
  137. data/doc/old-posts/0011-computer-science-as-a-lost-art.lt3 +117 -0
  138. data/doc/old-posts/0012-ruby-day-in-turin-italy.lt3 +26 -0
  139. data/doc/old-posts/0013-rubyday-was-a-success.lt3 +28 -0
  140. data/doc/old-posts/0014-working-on-the-blogging-software.lt3 +42 -0
  141. data/doc/old-posts/0015-ok-its-not-really-a-lost-art.lt3 +137 -0
  142. data/doc/old-posts/0016-an-in-operator-for-ruby.lt3 +142 -0
  143. data/doc/old-posts/0017-the-forgotten-mathematician.lt3 +129 -0
  144. data/doc/old-posts/0018-ruby-puns.lt3 +31 -0
  145. data/doc/old-posts/0019-custom-exceptions-via-metaprogramming.lt3 +116 -0
  146. data/doc/old-posts/0021-trying-ror-yet-again.lt3 +35 -0
  147. data/doc/old-posts/0023-doctor-sleep.lt3 +43 -0
  148. data/doc/old-posts/0024-just-a-test.lt3 +12 -0
  149. data/doc/old-posts/0025-trying-another-post.lt3 +12 -0
  150. data/doc/old-repo +1 -0
  151. data/doc/reddit_credentials_template.json +8 -0
  152. data/doc/reddit_integration.md +207 -0
  153. data/doc/user.lt3 +35 -0
  154. data/doc/user_guide_section_1.md +137 -0
  155. data/doc/user_guide_section_10.md +515 -0
  156. data/doc/user_guide_section_11.md +708 -0
  157. data/doc/user_guide_section_2.md +233 -0
  158. data/doc/user_guide_section_3.md +5 -0
  159. data/doc/user_guide_section_4.md +221 -0
  160. data/doc/user_guide_section_5.md +243 -0
  161. data/doc/user_guide_section_6.md +147 -0
  162. data/doc/user_guide_section_7.md +311 -0
  163. data/doc/user_guide_section_8.md +224 -0
  164. data/doc/user_guide_section_9.md +375 -0
  165. data/lib/rouge/lexers/livetext.rb +74 -0
  166. data/lib/scriptorium/api.rb +2373 -0
  167. data/lib/scriptorium/banner_svg.rb +729 -0
  168. data/lib/scriptorium/contract.rb +34 -0
  169. data/lib/scriptorium/exceptions.rb +201 -1
  170. data/lib/scriptorium/helpers.rb +675 -0
  171. data/lib/scriptorium/post.rb +259 -0
  172. data/lib/scriptorium/reddit.rb +83 -0
  173. data/lib/scriptorium/repo.rb +938 -0
  174. data/lib/scriptorium/standard_files.rb +149 -0
  175. data/lib/scriptorium/support/bootstrap/css.txt +5 -0
  176. data/lib/scriptorium/support/bootstrap/js.txt +4 -0
  177. data/lib/scriptorium/support/common_js/clipboard.js +35 -0
  178. data/lib/scriptorium/support/common_js/content-loader.js +187 -0
  179. data/lib/scriptorium/support/common_js/navigation.js +52 -0
  180. data/lib/scriptorium/support/common_js/syntax-highlighting.js +27 -0
  181. data/lib/scriptorium/support/config/reddit.txt +10 -0
  182. data/lib/scriptorium/support/config/reddit_template.txt +17 -0
  183. data/lib/scriptorium/support/config/social.txt +8 -0
  184. data/lib/scriptorium/support/highlight/css.txt +2 -0
  185. data/lib/scriptorium/support/highlight/custom.css +119 -0
  186. data/lib/scriptorium/support/highlight/js.txt +1 -0
  187. data/lib/scriptorium/support/post_index/config.txt +15 -0
  188. data/lib/scriptorium/support/post_index/style.css +55 -0
  189. data/lib/scriptorium/support/templates/index_entry.lt3 +16 -0
  190. data/lib/scriptorium/support/templates/initial_post.lt3 +12 -0
  191. data/lib/scriptorium/support/templates/layout.txt +5 -0
  192. data/lib/scriptorium/support/templates/post.lt3 +104 -0
  193. data/lib/scriptorium/support/theme/footer.lt3 +2 -0
  194. data/lib/scriptorium/support/theme/header.lt3 +4 -0
  195. data/lib/scriptorium/support/theme/left.lt3 +3 -0
  196. data/lib/scriptorium/support/theme/main.lt3 +5 -0
  197. data/lib/scriptorium/support/theme/right.lt3 +3 -0
  198. data/lib/scriptorium/theme.rb +192 -0
  199. data/lib/scriptorium/version.rb +1 -1
  200. data/lib/scriptorium/view.rb +1021 -0
  201. data/lib/scriptorium/widgets/featured_posts.rb +149 -0
  202. data/lib/scriptorium/widgets/links.rb +112 -0
  203. data/lib/scriptorium/widgets/pages.rb +133 -0
  204. data/lib/scriptorium/widgets/widget.rb +133 -0
  205. data/lib/scriptorium.rb +38 -34
  206. data/lib/skeleton.rb +10 -1
  207. data/scriptorium.gemspec +17 -5
  208. data/test/README.md +69 -0
  209. data/test/WEB_INTEGRATION_README.md +196 -0
  210. data/test/all +83 -0
  211. data/test/api_demo.rb +99 -0
  212. data/test/assets/imagenotfound.jpg +0 -0
  213. data/test/assets/images/.DS_Store +0 -0
  214. data/test/assets/images/README.md +27 -0
  215. data/test/assets/images/odd_aspect.png +0 -0
  216. data/test/assets/images/perfect.png +0 -0
  217. data/test/assets/images/small.png +0 -0
  218. data/test/assets/images/tall.png +0 -0
  219. data/test/assets/images/very_tall.png +0 -0
  220. data/test/assets/images/very_wide.png +0 -0
  221. data/test/assets/images/wide.png +0 -0
  222. data/test/assets/testbanner.jpg +0 -0
  223. data/test/banner_svg/simple_helpers.rb +13 -0
  224. data/test/banner_svg/unit.rb +1000 -0
  225. data/test/config/deployment.txt +5 -0
  226. data/test/ed_test.rb +204 -0
  227. data/test/integration/cursor_banner_combinations.rb +193 -0
  228. data/test/integration/cursor_banner_features.rb +374 -0
  229. data/test/integration/integration_test.rb +326 -0
  230. data/test/integration/preview_flow_test.rb +94 -0
  231. data/test/livetext_plugin_test.rb +500 -0
  232. data/test/manual/asset_mgmt.rb +67 -0
  233. data/test/manual/banner-tests/index.html +45 -0
  234. data/test/manual/banner-tests/svg.txt +3 -0
  235. data/test/manual/banner-tests/test01.html +122 -0
  236. data/test/manual/banner-tests/test02.html +122 -0
  237. data/test/manual/banner-tests/test03.html +122 -0
  238. data/test/manual/banner-tests/test04.html +129 -0
  239. data/test/manual/banner-tests/test05.html +129 -0
  240. data/test/manual/banner-tests/test06.html +129 -0
  241. data/test/manual/banner-tests/test07.html +129 -0
  242. data/test/manual/banner-tests/test08.html +123 -0
  243. data/test/manual/banner-tests/test09.html +123 -0
  244. data/test/manual/banner-tests/test10.html +123 -0
  245. data/test/manual/banner-tests/test11.html +123 -0
  246. data/test/manual/banner-tests/test12.html +123 -0
  247. data/test/manual/banner-tests/test13.html +123 -0
  248. data/test/manual/banner-tests/test14.html +123 -0
  249. data/test/manual/banner-tests/test15.html +122 -0
  250. data/test/manual/banner-tests/test16.html +122 -0
  251. data/test/manual/banner-tests/test17.html +122 -0
  252. data/test/manual/banner-tests/test18.html +132 -0
  253. data/test/manual/banner-tests/test19.html +132 -0
  254. data/test/manual/banner-tests/test20.html +132 -0
  255. data/test/manual/banner-tests/test21.html +132 -0
  256. data/test/manual/banner-tests/test22.html +132 -0
  257. data/test/manual/banner-tests/test23.html +132 -0
  258. data/test/manual/banner-tests/test24.html +132 -0
  259. data/test/manual/banner-tests/test25.html +131 -0
  260. data/test/manual/banner_environment.rb +205 -0
  261. data/test/manual/codemirror_demo.html +773 -0
  262. data/test/manual/create_posts_for_web.rb +114 -0
  263. data/test/manual/environment.rb +67 -0
  264. data/test/manual/make_banner.rb +153 -0
  265. data/test/manual/preview_manual_test.rb +129 -0
  266. data/test/manual/sample_banner_config.txt +12 -0
  267. data/test/manual/test_advanced_widgets.rb +73 -0
  268. data/test/manual/test_banner_combinations.rb +120 -0
  269. data/test/manual/test_banner_features.rb +306 -0
  270. data/test/manual/test_banner_integration.rb +115 -0
  271. data/test/manual/test_banner_radial.rb +87 -0
  272. data/test/manual/test_basic_posts.rb +47 -0
  273. data/test/manual/test_layout_widgets.rb +40 -0
  274. data/test/manual/test_pagination.rb +24 -0
  275. data/test/manual/test_random_posts.rb +38 -0
  276. data/test/manual/test_syntax_highlighting.rb +167 -0
  277. data/test/rubytext/rubytext_comprehensive_test.rb +307 -0
  278. data/test/rubytext/rubytext_demo_test.rb +42 -0
  279. data/test/rubytext/rubytext_testing_guide.md +277 -0
  280. data/test/run_automated_tests.rb +45 -0
  281. data/test/staging/.DS_Store +0 -0
  282. data/test/support/preview_utils.rb +88 -0
  283. data/test/syntax_highlighting_test.lt3 +124 -0
  284. data/test/test_gem_assets.rb +48 -0
  285. data/test/test_helpers.rb +240 -0
  286. data/test/tui_editor_integration_test.rb +296 -0
  287. data/test/tui_integration_test.rb +883 -0
  288. data/test/unit/api.rb +1776 -0
  289. data/test/unit/asset_management.rb +219 -0
  290. data/test/unit/backup_test.rb +451 -0
  291. data/test/unit/clipboard_test.rb +60 -0
  292. data/test/unit/contract_test.rb +69 -0
  293. data/test/unit/core.rb +1211 -0
  294. data/test/unit/deploy_config_test.rb +248 -0
  295. data/test/unit/deploy_test.rb +478 -0
  296. data/test/unit/edit_post_test.rb +168 -0
  297. data/test/unit/gem_asset_management.rb +183 -0
  298. data/test/unit/livetext_basic.rb +57 -0
  299. data/test/unit/livetext_compatibility.rb +82 -0
  300. data/test/unit/parse_cmd_test.rb +260 -0
  301. data/test/unit/permalink_copy_test.rb +211 -0
  302. data/test/unit/post.rb +309 -0
  303. data/test/unit/post_index_config_test.rb +258 -0
  304. data/test/unit/post_state_helpers_test.rb +137 -0
  305. data/test/unit/read_commented_file_test.rb +278 -0
  306. data/test/unit/reddit_test.rb +235 -0
  307. data/test/unit/repo.rb +569 -0
  308. data/test/unit/social_test.rb +366 -0
  309. data/test/unit/syntax_highlighting.rb +70 -0
  310. data/test/unit/theme_management_test.rb +91 -0
  311. data/test/unit/view.rb +498 -0
  312. data/test/unit/widgets.rb +669 -0
  313. data/test/web_integration_test.rb +231 -0
  314. data/test/web_test_helper.rb +218 -0
  315. data/test/web_workflow_test.rb +527 -0
  316. data/test/wizard_test.rb +123 -0
  317. data/ui/README.md +67 -0
  318. data/ui/common/lib/ui_common.rb +8 -0
  319. data/ui/rubytext/README.md +191 -0
  320. data/ui/rubytext/bin/scriptorium-rubytext +402 -0
  321. data/ui/rubytext/lib/rubytext_ui.rb +300 -0
  322. data/ui/tui/bin/scriptorium +1890 -0
  323. data/ui/tui/test/tui_test.rb +23 -0
  324. data/ui/web/app/app.rb +2600 -0
  325. data/ui/web/app/assets/livetext_mode.js +244 -0
  326. data/ui/web/app/error_helpers.rb +150 -0
  327. data/ui/web/app/views/advanced_config.erb +196 -0
  328. data/ui/web/app/views/asset_management.erb +645 -0
  329. data/ui/web/app/views/backup_management.erb +238 -0
  330. data/ui/web/app/views/banner_config.erb +200 -0
  331. data/ui/web/app/views/config_widget.erb +232 -0
  332. data/ui/web/app/views/configure_view.erb +401 -0
  333. data/ui/web/app/views/dashboard.erb +154 -0
  334. data/ui/web/app/views/deploy_config.erb +149 -0
  335. data/ui/web/app/views/edit_pages.erb +363 -0
  336. data/ui/web/app/views/edit_post.erb +175 -0
  337. data/ui/web/app/views/edit_theme.erb +73 -0
  338. data/ui/web/app/views/edit_theme_file.erb +74 -0
  339. data/ui/web/app/views/error_page.erb +29 -0
  340. data/ui/web/app/views/header_config.erb +155 -0
  341. data/ui/web/app/views/layout_config.erb +147 -0
  342. data/ui/web/app/views/navbar_config.erb +411 -0
  343. data/ui/web/app/views/theme_management.erb +130 -0
  344. data/ui/web/app/views/view_dashboard.erb +779 -0
  345. data/ui/web/app/views/widgets.erb +249 -0
  346. data/ui/web/bin/scriptorium-web +164 -0
  347. data/ui/web/test/web_basic_test.rb +38 -0
  348. data/ui/web/test_navbar.txt +7 -0
  349. data/ui/web/tmp/timing.log +17 -0
  350. data/ui/web/tmp/web_server.log +0 -0
  351. metadata +434 -8
  352. data/lib/scriptorium/engine.rb +0 -22
  353. data/test/engine/unit.rb +0 -44
@@ -0,0 +1,366 @@
1
+ require 'minitest/autorun'
2
+ require_relative '../../lib/scriptorium'
3
+ require_relative '../test_helpers'
4
+
5
+ class SocialTest < Minitest::Test
6
+ include Scriptorium::Exceptions
7
+ include Scriptorium::Helpers
8
+ include TestHelpers
9
+
10
+ def setup
11
+ @test_dir = "test/social_test_files"
12
+ make_dir(@test_dir)
13
+ @repo = Scriptorium::Repo.create("test/scriptorium-TEST", testmode: true)
14
+ @view = @repo.create_view("test", "Test View", "A test view")
15
+ end
16
+
17
+ def teardown
18
+ FileUtils.rm_rf(@test_dir) if Dir.exist?(@test_dir)
19
+ Scriptorium::Repo.destroy if Scriptorium::Repo.testing
20
+ end
21
+
22
+ def test_001_social_config_file_created
23
+ social_config_file = @view.dir/:config/"social.txt"
24
+ assert File.exist?(social_config_file), "Social config file should be created"
25
+
26
+ content = read_file(social_config_file)
27
+ assert_includes content, "facebook"
28
+ assert_includes content, "twitter"
29
+ end
30
+
31
+ def test_002_social_meta_tags_generated_when_enabled
32
+ # Enable social features
33
+ social_config = <<~EOS
34
+ facebook
35
+ twitter
36
+ EOS
37
+ write_file(@view.dir/:config/"social.txt", social_config)
38
+
39
+ # Generate meta tags
40
+ meta_tags = @view.generate_social_meta_tags
41
+
42
+ # Check for Open Graph tags
43
+ assert_includes meta_tags, 'property="og:title"'
44
+ assert_includes meta_tags, 'property="og:type"'
45
+ assert_includes meta_tags, 'property="og:url"'
46
+ assert_includes meta_tags, 'property="og:description"'
47
+ assert_includes meta_tags, 'property="og:site_name"'
48
+
49
+ # Check for Twitter Card tags
50
+ assert_includes meta_tags, 'name="twitter:card"'
51
+ assert_includes meta_tags, 'name="twitter:title"'
52
+ assert_includes meta_tags, 'name="twitter:description"'
53
+ assert_includes meta_tags, 'name="twitter:url"'
54
+ end
55
+
56
+ def test_003_social_meta_tags_not_generated_when_disabled
57
+ # Disable social features
58
+ social_config = <<~EOS
59
+ # facebook
60
+ # twitter
61
+ EOS
62
+ write_file(@view.dir/:config/"social.txt", social_config)
63
+
64
+ # Generate meta tags
65
+ meta_tags = @view.generate_social_meta_tags
66
+
67
+ # Should be empty when disabled
68
+ assert_empty meta_tags
69
+ end
70
+
71
+ def test_004_post_specific_meta_tags
72
+ # Enable social features
73
+ social_config = <<~EOS
74
+ facebook
75
+ twitter
76
+ EOS
77
+ write_file(@view.dir/:config/"social.txt", social_config)
78
+
79
+ # Create post data
80
+ post_data = {
81
+ :"post.id" => "1",
82
+ :"post.title" => "Test Post",
83
+ :"post.body" => "This is a test post body",
84
+ :"post.blurb" => "Test post blurb",
85
+ :"post.pubdate" => "2024-01-01 12:00:00",
86
+ :"post.slug" => "test-post.html"
87
+ }
88
+
89
+ # Generate meta tags for post
90
+ meta_tags = @view.generate_social_meta_tags(nil, post_data)
91
+
92
+ # Check for post-specific content
93
+ assert_includes meta_tags, 'content="Test Post"'
94
+ assert_includes meta_tags, 'content="article"'
95
+ assert_includes meta_tags, 'content="posts/test-post.html"'
96
+ assert_includes meta_tags, 'content="Test post blurb"'
97
+ assert_includes meta_tags, 'content="2024-01-01 12:00:00"'
98
+ end
99
+
100
+ def test_005_complete_post_html_generation
101
+ # Enable social features
102
+ social_config = <<~EOS
103
+ facebook
104
+ twitter
105
+ EOS
106
+ write_file(@view.dir/:config/"social.txt", social_config)
107
+
108
+ # Create post data
109
+ post_data = {
110
+ :"post.id" => "1",
111
+ :"post.title" => "Test Post",
112
+ :"post.body" => "<p>This is a test post body</p>",
113
+ :"post.pubdate" => "2024-01-01 12:00:00",
114
+ :"post.tags" => "test, example"
115
+ }
116
+
117
+ # Generate social meta tags (this method exists)
118
+ meta_tags = @view.generate_social_meta_tags(nil, post_data)
119
+
120
+ # Check for social meta tags
121
+ assert_includes meta_tags, 'property="og:title"'
122
+ assert_includes meta_tags, 'name="twitter:card"'
123
+ end
124
+
125
+ # Reddit Button Tests
126
+
127
+ def test_006_reddit_config_file_created
128
+ reddit_config_file = @view.dir/:config/"reddit.txt"
129
+ assert File.exist?(reddit_config_file), "Reddit config file should be created"
130
+
131
+ content = read_file(reddit_config_file)
132
+ assert_includes content, "button true"
133
+ assert_includes content, "subreddit"
134
+ assert_includes content, "hover_text"
135
+ end
136
+
137
+ def test_007_reddit_button_not_generated_when_reddit_not_enabled
138
+ # Disable Reddit in social config
139
+ social_config = <<~EOS
140
+ facebook
141
+ twitter
142
+ EOS
143
+ write_file(@view.dir/:config/"social.txt", social_config)
144
+
145
+ # Enable Reddit button in reddit config
146
+ reddit_config = <<~EOS
147
+ button true
148
+ subreddit
149
+ hover_text
150
+ EOS
151
+ write_file(@view.dir/:config/"reddit.txt", reddit_config)
152
+
153
+ # Generate Reddit button
154
+ button_html = @view.generate_reddit_button
155
+
156
+ # Should be empty when Reddit not enabled in social config
157
+ assert_empty button_html
158
+ end
159
+
160
+ def test_008_reddit_button_not_generated_when_button_disabled
161
+ # Enable Reddit in social config
162
+ social_config = <<~EOS
163
+ facebook
164
+ twitter
165
+ reddit
166
+ EOS
167
+ write_file(@view.dir/:config/"social.txt", social_config)
168
+
169
+ # Disable Reddit button in reddit config
170
+ reddit_config = <<~EOS
171
+ button false
172
+ subreddit
173
+ hover_text
174
+ EOS
175
+ write_file(@view.dir/:config/"reddit.txt", reddit_config)
176
+
177
+ # Generate Reddit button
178
+ button_html = @view.generate_reddit_button
179
+
180
+ # Should be empty when button disabled
181
+ assert_empty button_html
182
+ end
183
+
184
+ def test_009_reddit_button_generated_when_enabled
185
+ # Enable Reddit in social config
186
+ social_config = <<~EOS
187
+ facebook
188
+ twitter
189
+ reddit
190
+ EOS
191
+ write_file(@view.dir/:config/"social.txt", social_config)
192
+
193
+ # Enable Reddit button in reddit config
194
+ reddit_config = <<~EOS
195
+ button true
196
+ subreddit
197
+ hover_text
198
+ EOS
199
+ write_file(@view.dir/:config/"reddit.txt", reddit_config)
200
+
201
+ # Generate Reddit button
202
+ button_html = @view.generate_reddit_button
203
+
204
+ # Should contain Reddit button HTML
205
+ assert_includes button_html, 'href="https://reddit.com/submit'
206
+ assert_includes button_html, 'title="Share on Reddit"'
207
+ assert_includes button_html, 'src="../../assets/icons/social/reddit.png"'
208
+ assert_includes button_html, 'alt="Share on Reddit"'
209
+ end
210
+
211
+ def test_010_reddit_button_with_subreddit
212
+ # Enable Reddit in social config
213
+ social_config = <<~EOS
214
+ facebook
215
+ twitter
216
+ reddit
217
+ EOS
218
+ write_file(@view.dir/:config/"social.txt", social_config)
219
+
220
+ # Enable Reddit button with subreddit
221
+ reddit_config = <<~EOS
222
+ button true
223
+ subreddit RubyElixirEtc
224
+ hover_text
225
+ EOS
226
+ write_file(@view.dir/:config/"reddit.txt", reddit_config)
227
+
228
+ # Generate Reddit button
229
+ button_html = @view.generate_reddit_button
230
+
231
+ # Should contain subreddit-specific URL and hover text
232
+ assert_includes button_html, 'href="https://reddit.com/r/RubyElixirEtc/submit'
233
+ assert_includes button_html, 'title="Share on r/RubyElixirEtc"'
234
+ end
235
+
236
+ def test_011_reddit_button_with_custom_hover_text
237
+ # Enable Reddit in social config
238
+ social_config = <<~EOS
239
+ facebook
240
+ twitter
241
+ reddit
242
+ EOS
243
+ write_file(@view.dir/:config/"social.txt", social_config)
244
+
245
+ # Enable Reddit button with custom hover text
246
+ reddit_config = <<~EOS
247
+ button true
248
+ subreddit
249
+ hover_text Share this awesome post!
250
+ EOS
251
+ write_file(@view.dir/:config/"reddit.txt", reddit_config)
252
+
253
+ # Generate Reddit button
254
+ button_html = @view.generate_reddit_button
255
+
256
+ # Should contain custom hover text
257
+ assert_includes button_html, 'title="Share this awesome post!"'
258
+ end
259
+
260
+ def test_012_reddit_button_with_post_data
261
+ # Enable Reddit in social config
262
+ social_config = <<~EOS
263
+ facebook
264
+ twitter
265
+ reddit
266
+ EOS
267
+ write_file(@view.dir/:config/"social.txt", social_config)
268
+
269
+ # Enable Reddit button
270
+ reddit_config = <<~EOS
271
+ button true
272
+ subreddit
273
+ hover_text
274
+ EOS
275
+ write_file(@view.dir/:config/"reddit.txt", reddit_config)
276
+
277
+ # Create post data
278
+ post_data = {
279
+ :"post.id" => "123",
280
+ :"post.title" => "My Test Post",
281
+ :"post.slug" => "my-test-post"
282
+ }
283
+
284
+ # Generate Reddit button with post data
285
+ button_html = @view.generate_reddit_button(post_data)
286
+
287
+ # Should contain post-specific URL and title
288
+ assert_includes button_html, 'url=posts/my-test-post.html'
289
+ assert_includes button_html, 'title=My+Test+Post'
290
+ end
291
+
292
+ def test_013_reddit_button_integration_with_post_generation
293
+ # Enable Reddit in social config
294
+ social_config = <<~EOS
295
+ facebook
296
+ twitter
297
+ reddit
298
+ EOS
299
+ write_file(@view.dir/:config/"social.txt", social_config)
300
+
301
+ # Enable Reddit button
302
+ reddit_config = <<~EOS
303
+ button true
304
+ subreddit
305
+ hover_text
306
+ EOS
307
+ write_file(@view.dir/:config/"reddit.txt", reddit_config)
308
+
309
+ # Create and generate a post
310
+ draft_name = @repo.create_draft(title: "Reddit Test Post", tags: %w[test reddit])
311
+ body = "This is a test post for Reddit button integration."
312
+ text = read_file(draft_name)
313
+ text.sub!(/BEGIN HERE.../, body)
314
+ write_file(draft_name, text)
315
+ post_id = @repo.finish_draft(draft_name)
316
+ @repo.generate_post(post_id)
317
+
318
+ # Check that the generated post contains Reddit button
319
+ post_file = @repo.root/:views/:test/:output/:posts/"#{d4(post_id)}-reddit-test-post.html"
320
+ assert File.exist?(post_file), "Generated post should exist"
321
+
322
+ post_content = read_file(post_file)
323
+ assert_includes_concise_string post_content, 'href="https://reddit.com/submit', "Post should contain Reddit submit link"
324
+ assert_includes_concise_string post_content, 'src="../../assets/icons/social/reddit.png"', "Post should contain Reddit icon"
325
+ end
326
+
327
+ def test_014_reddit_button_missing_config_file_handling
328
+ # Enable Reddit in social config
329
+ social_config = <<~EOS
330
+ facebook
331
+ twitter
332
+ reddit
333
+ EOS
334
+ write_file(@view.dir/:config/"social.txt", social_config)
335
+
336
+ # Remove reddit.txt config file
337
+ reddit_config_file = @view.dir/:config/"reddit.txt"
338
+ File.delete(reddit_config_file) if File.exist?(reddit_config_file)
339
+
340
+ # Generate Reddit button
341
+ button_html = @view.generate_reddit_button
342
+
343
+ # Should be empty when config file missing
344
+ assert_empty button_html
345
+ end
346
+
347
+ def test_015_reddit_button_missing_social_config_file_handling
348
+ # Remove social.txt config file
349
+ social_config_file = @view.dir/:config/"social.txt"
350
+ File.delete(social_config_file) if File.exist?(social_config_file)
351
+
352
+ # Enable Reddit button in reddit config
353
+ reddit_config = <<~EOS
354
+ button true
355
+ subreddit
356
+ hover_text
357
+ EOS
358
+ write_file(@view.dir/:config/"reddit.txt", reddit_config)
359
+
360
+ # Generate Reddit button
361
+ button_html = @view.generate_reddit_button
362
+
363
+ # Should be empty when social config file missing
364
+ assert_empty button_html
365
+ end
366
+ end
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'minitest/autorun'
4
+ require 'fileutils'
5
+ require_relative '../../lib/scriptorium'
6
+ require_relative '../test_helpers'
7
+
8
+ class TestSyntaxHighlighting < Minitest::Test
9
+ include TestHelpers
10
+
11
+ def setup
12
+ FileUtils.rm_rf('test/scriptorium-TEST') if Dir.exist?('test/scriptorium-TEST')
13
+ @repo = create_test_repo
14
+ @view = 'syntax_view'
15
+ @repo.create_view(@view, 'Syntax View', 'Highlight.js test view')
16
+ # Ensure highlight is enabled in head
17
+ head = @repo.root/:views/@view/:config/"global-head.txt"
18
+ content = File.exist?(head) ? read_file(head) : ''
19
+ additions = []
20
+ additions << "highlight" unless content.include?("\nhighlight")
21
+ additions << "highlight_custom" unless content.include?("\nhighlight_custom")
22
+ File.open(head, 'a') { |f| additions.each { |ln| f.puts ln } } unless additions.empty?
23
+ # Ensure an index is generated for head assertions
24
+ @repo.generate_front_page(@view)
25
+ end
26
+
27
+ def teardown
28
+ FileUtils.rm_rf('test/scriptorium-TEST') if Dir.exist?('test/scriptorium-TEST')
29
+ end
30
+
31
+ def test_001_post_contains_raw_code_blocks_for_hljs
32
+ body = <<~LT
33
+ .title HLJS Test
34
+ .views #{@view}
35
+ .blurb
36
+ Testing Highlight.js only
37
+ .end
38
+
39
+ ## Ruby
40
+ .code ruby
41
+ def hello
42
+ puts 'hi'
43
+ end
44
+ .end
45
+
46
+ ## JS
47
+ .code javascript
48
+ console.log('x');
49
+ .end
50
+ LT
51
+ name = @repo.create_draft(title: 'HLJS Test', views: [@view], body: body)
52
+ num = @repo.finish_draft(name)
53
+ @repo.generate_post(num)
54
+ @repo.generate_front_page(@view)
55
+
56
+ post = @repo.root/:views/@view/:output/:posts/"#{d4(num)}-hljs-test.html"
57
+ html = read_file(post)
58
+
59
+ assert_includes html, '<pre><code class="language-ruby">'
60
+ assert_includes html, '<pre><code class="language-javascript">'
61
+ refute_match(/class=\"token\s+/, html)
62
+ end
63
+
64
+ def test_002_head_includes_hljs_assets
65
+ index = @repo.root/:views/@view/:output/"index.html"
66
+ html = read_file(index)
67
+
68
+ assert_includes html, 'cdnjs.cloudflare.com/ajax/libs/highlight.js'
69
+ end
70
+ end
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../../lib/scriptorium'
5
+ require_relative '../test_helpers'
6
+
7
+ class ThemeManagementTest < Minitest::Test
8
+ include TestHelpers
9
+
10
+ def setup
11
+ @repo = create_test_repo
12
+ @api = Scriptorium::API.new(testmode: true)
13
+ @api.open_repo(@repo.root)
14
+ end
15
+
16
+ def teardown
17
+ system("rm -rf test/scriptorium-TEST")
18
+ end
19
+
20
+ def test_001_themes_available_returns_list
21
+ themes = @api.themes_available
22
+ assert themes.is_a?(Array), "themes_available should return an array"
23
+ assert_includes themes, "standard", "Should include standard theme"
24
+ end
25
+
26
+ def test_002_system_themes_returns_list
27
+ system_themes = @api.system_themes
28
+ assert system_themes.is_a?(Array), "system_themes should return an array"
29
+ assert_includes system_themes, "standard", "Should include standard theme"
30
+ end
31
+
32
+ def test_003_user_themes_returns_list
33
+ user_themes = @api.user_themes
34
+ assert user_themes.is_a?(Array), "user_themes should return an array"
35
+ # Initially should be empty or contain only user-created themes
36
+ end
37
+
38
+ def test_004_theme_exists_checks_correctly
39
+ assert @api.theme_exists?("standard"), "Standard theme should exist"
40
+ assert !@api.theme_exists?("nonexistent"), "Nonexistent theme should not exist"
41
+ end
42
+
43
+ def test_005_clone_theme_creates_new_theme
44
+ # Clone standard theme to a new user theme
45
+ new_theme = @api.clone_theme("standard", "my-custom-theme")
46
+ assert_equal "my-custom-theme", new_theme
47
+
48
+ # Check that it exists
49
+ assert @api.theme_exists?("my-custom-theme"), "Cloned theme should exist"
50
+ assert_includes @api.user_themes, "my-custom-theme", "Cloned theme should be in user themes"
51
+
52
+ # Check that theme directory was created
53
+ theme_dir = @repo.root/:themes/"my-custom-theme"
54
+ assert Dir.exist?(theme_dir), "Theme directory should exist"
55
+ end
56
+
57
+ def test_006_clone_theme_validates_source
58
+ assert_raises(ThemeNotFound) do
59
+ @api.clone_theme("nonexistent", "new-theme")
60
+ end
61
+ end
62
+
63
+ def test_007_clone_theme_validates_new_name
64
+ assert_raises(ThemeAlreadyExists) do
65
+ @api.clone_theme("standard", "standard")
66
+ end
67
+
68
+ assert_raises(ThemeNameInvalid) do
69
+ @api.clone_theme("standard", "invalid name!")
70
+ end
71
+ end
72
+
73
+ def test_008_clone_theme_copies_files
74
+ # Clone standard theme
75
+ @api.clone_theme("standard", "test-theme")
76
+
77
+ # Check that key files were copied
78
+ theme_dir = @repo.root/:themes/"test-theme"
79
+
80
+ # Check for layout.txt in the correct location
81
+ layout_file = theme_dir/:layout/"layout.txt"
82
+ assert File.exist?(layout_file), "layout.txt should be copied to #{layout_file}"
83
+
84
+ # Check for config files in layout/config/
85
+ header_file = theme_dir/:layout/:config/"header.txt"
86
+ footer_file = theme_dir/:layout/:config/"footer.txt"
87
+
88
+ assert File.exist?(header_file), "header.txt should be copied to #{header_file}"
89
+ assert File.exist?(footer_file), "footer.txt should be copied to #{footer_file}"
90
+ end
91
+ end