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,277 @@
1
+ # RubyText Testing Guide
2
+
3
+ ## Overview
4
+
5
+ Testing curses-based applications like RubyText is challenging because they're inherently interactive and terminal-dependent. This guide outlines a comprehensive testing strategy that balances automation with practical reality.
6
+
7
+ ## Testing Challenges
8
+
9
+ ### **1. Terminal Dependencies**
10
+ - Different terminals behave differently
11
+ - Terminal capabilities vary (colors, Unicode, etc.)
12
+ - Screen sizes and resolutions differ
13
+
14
+ ### **2. Interactive Nature**
15
+ - User input is time-sensitive
16
+ - Screen state changes dynamically
17
+ - Hard to capture and verify visual output
18
+
19
+ ### **3. Platform Differences**
20
+ - Unix/Linux vs macOS vs Windows
21
+ - Different curses implementations
22
+ - Terminal emulator variations
23
+
24
+ ## Testing Strategy
25
+
26
+ ### **1. Component Testing (Recommended)**
27
+
28
+ Test individual components in isolation:
29
+
30
+ ```ruby
31
+ # Test menu logic without curses
32
+ def test_menu_logic
33
+ menu = RubyText::Menu.new(["Option 1", "Option 2"])
34
+ assert_equal 2, menu.options.length
35
+ assert_equal "Option 1", menu.options[0]
36
+ end
37
+
38
+ # Test screen buffer logic
39
+ def test_screen_buffer
40
+ screen = RubyText::Screen.new(80, 24)
41
+ screen.puts("Hello")
42
+ assert_includes screen.buffer, "Hello"
43
+ end
44
+ ```
45
+
46
+ ### **2. PTY-Based Integration Testing**
47
+
48
+ Use PTY for realistic terminal interaction:
49
+
50
+ ```ruby
51
+ def test_basic_interaction
52
+ PTY.spawn('ruby -e "require \"rubytext\"; RubyText.start { puts \"Hello\"; gets }"') do |read, write, pid|
53
+ begin
54
+ read.expect(/Hello/, 5)
55
+ write.puts "q"
56
+ Process.wait(pid)
57
+ assert_equal 0, $?.exitstatus
58
+ ensure
59
+ Process.kill('TERM', pid) rescue nil
60
+ end
61
+ end
62
+ end
63
+ ```
64
+
65
+ ### **3. Regression Testing with Screenshots**
66
+
67
+ Capture expected screen states:
68
+
69
+ ```ruby
70
+ def test_screen_regression
71
+ # Capture current screen
72
+ current_screen = capture_screen do
73
+ run_rubytext_app_with_input(["help", "quit"])
74
+ end
75
+
76
+ # Compare with expected
77
+ expected_screen = File.read("test/fixtures/expected_help_screen.txt")
78
+ assert_equal expected_screen, current_screen
79
+ end
80
+ ```
81
+
82
+ ### **4. Automated Demo Testing**
83
+
84
+ Automate your existing demo/slideshow:
85
+
86
+ ```ruby
87
+ def test_demo_automation
88
+ PTY.spawn('ruby demo.rb --automated') do |read, write, pid|
89
+ begin
90
+ # Navigate through slides
91
+ read.expect(/Slide 1/, 5)
92
+ write.puts "n" # next
93
+ read.expect(/Slide 2/, 5)
94
+ write.puts "q" # quit
95
+
96
+ Process.wait(pid)
97
+ assert_equal 0, $?.exitstatus
98
+ ensure
99
+ Process.kill('TERM', pid) rescue nil
100
+ end
101
+ end
102
+ end
103
+ ```
104
+
105
+ ## Test Categories
106
+
107
+ ### **Core Functionality**
108
+ - [ ] Menu creation and navigation
109
+ - [ ] Screen rendering and layout
110
+ - [ ] Input handling (keyboard, mouse)
111
+ - [ ] Color and style support
112
+ - [ ] Window management
113
+
114
+ ### **User Interface**
115
+ - [ ] Menu interactions
116
+ - [ ] Form input
117
+ - [ ] Dialog boxes
118
+ - [ ] Progress indicators
119
+ - [ ] Error messages
120
+
121
+ ### **Performance**
122
+ - [ ] Large content rendering
123
+ - [ ] Screen refresh speed
124
+ - [ ] Memory usage
125
+ - [ ] Input responsiveness
126
+
127
+ ### **Error Handling**
128
+ - [ ] Invalid input
129
+ - [ ] Terminal errors
130
+ - [ ] Resource limits
131
+ - [ ] Graceful degradation
132
+
133
+ ### **Platform Compatibility**
134
+ - [ ] Different terminals
135
+ - [ ] Screen sizes
136
+ - [ ] Color support
137
+ - [ ] Unicode support
138
+
139
+ ## Implementation Examples
140
+
141
+ ### **1. Menu Testing**
142
+
143
+ ```ruby
144
+ class MenuTest < Minitest::Test
145
+ def test_menu_creation
146
+ menu = RubyText::Menu.new(["Option 1", "Option 2"])
147
+ assert_equal 2, menu.options.length
148
+ end
149
+
150
+ def test_menu_interaction
151
+ PTY.spawn('ruby -e "require \"rubytext\"; RubyText.start { menu = RubyText::Menu.new([\"A\", \"B\"]); puts menu.show; gets }"') do |read, write, pid|
152
+ begin
153
+ read.expect(/A/, 5)
154
+ write.puts "\r" # Select first option
155
+ result = read.expect(/A/, 5)
156
+ assert result
157
+ ensure
158
+ Process.kill('TERM', pid) rescue nil
159
+ end
160
+ end
161
+ end
162
+ end
163
+ ```
164
+
165
+ ### **2. Screen Testing**
166
+
167
+ ```ruby
168
+ class ScreenTest < Minitest::Test
169
+ def test_screen_rendering
170
+ PTY.spawn('ruby -e "require \"rubytext\"; RubyText.start { puts \"Test\"; gets }"') do |read, write, pid|
171
+ begin
172
+ result = read.expect(/Test/, 5)
173
+ assert result, "Screen should display content"
174
+ ensure
175
+ Process.kill('TERM', pid) rescue nil
176
+ end
177
+ end
178
+ end
179
+ end
180
+ ```
181
+
182
+ ### **3. Input Testing**
183
+
184
+ ```ruby
185
+ class InputTest < Minitest::Test
186
+ def test_keyboard_input
187
+ PTY.spawn('ruby -e "require \"rubytext\"; RubyText.start { key = RubyText.getch; puts \"Key: #{key}\"; gets }"') do |read, write, pid|
188
+ begin
189
+ write.puts "a"
190
+ result = read.expect(/Key: a/, 5)
191
+ assert result, "Should detect key press"
192
+ ensure
193
+ Process.kill('TERM', pid) rescue nil
194
+ end
195
+ end
196
+ end
197
+ end
198
+ ```
199
+
200
+ ## Testing Tools
201
+
202
+ ### **1. PTY (Pseudo-Terminal)**
203
+ - Realistic terminal interaction
204
+ - Captures input/output
205
+ - Good for integration testing
206
+
207
+ ### **2. Screen Capture**
208
+ - Capture screen states
209
+ - Compare with expected output
210
+ - Good for regression testing
211
+
212
+ ### **3. Mock Terminals**
213
+ - Test logic without curses
214
+ - Faster execution
215
+ - Good for unit testing
216
+
217
+ ### **4. Automated Demos**
218
+ - Test real user workflows
219
+ - Verify end-to-end functionality
220
+ - Good for acceptance testing
221
+
222
+ ## Best Practices
223
+
224
+ ### **1. Test Structure**
225
+ - Separate unit tests from integration tests
226
+ - Use descriptive test names
227
+ - Group related tests together
228
+
229
+ ### **2. Error Handling**
230
+ - Test error conditions
231
+ - Verify graceful degradation
232
+ - Check error messages
233
+
234
+ ### **3. Performance**
235
+ - Test with realistic data sizes
236
+ - Monitor memory usage
237
+ - Check response times
238
+
239
+ ### **4. Platform Coverage**
240
+ - Test on multiple platforms
241
+ - Use CI/CD for automated testing
242
+ - Document platform-specific issues
243
+
244
+ ## Manual Testing Checklist
245
+
246
+ Since automated testing can't cover everything, maintain a manual testing checklist:
247
+
248
+ ### **Visual Verification**
249
+ - [ ] Colors display correctly
250
+ - [ ] Text alignment is proper
251
+ - [ ] Screen layout is clean
252
+ - [ ] Unicode characters render
253
+
254
+ ### **Interaction Testing**
255
+ - [ ] Menu navigation works
256
+ - [ ] Keyboard shortcuts function
257
+ - [ ] Mouse input responds
258
+ - [ ] Error messages are clear
259
+
260
+ ### **Performance Testing**
261
+ - [ ] Large content renders quickly
262
+ - [ ] Screen updates are smooth
263
+ - [ ] Input is responsive
264
+ - [ ] Memory usage is reasonable
265
+
266
+ ## Conclusion
267
+
268
+ Testing RubyText requires a multi-faceted approach:
269
+
270
+ 1. **Unit test the core logic** (menus, screens, input handling)
271
+ 2. **Integration test with PTY** (real terminal interaction)
272
+ 3. **Regression test with screenshots** (visual verification)
273
+ 4. **Manual test the complex interactions** (user experience)
274
+
275
+ The key is to **automate what you can** and **accept that some testing will always be manual**. Focus on testing the critical paths and user workflows, and use your existing demo/slideshow as a foundation for automated testing.
276
+
277
+ Remember: **Perfect automated testing of curses applications is impossible, but good testing is definitely achievable!**
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Set up environment
4
+ ENV['PATH'] = "#{ENV['HOME']}/.rbenv/shims:#{ENV['PATH']}"
5
+
6
+ puts "\n" + "="*60
7
+ puts "Running all automated tests..."
8
+ puts "="*60
9
+
10
+ # Run unit tests
11
+ puts "\n--- Running unit tests ---"
12
+ system("ruby test/all")
13
+
14
+ # Run manual tests in automated mode
15
+ puts "\n--- Running manual tests in automated mode ---"
16
+
17
+ manual_tests = [
18
+ "test/manual/test1.rb",
19
+ "test/manual/test2.rb",
20
+ "test/manual/test3.rb blog1",
21
+ "test/manual/test4.rb",
22
+ "test/manual/test5.rb",
23
+ "test/manual/test_banner_combinations.rb",
24
+ "test/manual/test_banner_features.rb",
25
+ "test/manual/test_complex_header.rb",
26
+ "test/manual/test_empty_header.rb",
27
+ "test/manual/test_banner_in_header.rb",
28
+ "test/manual/test_radial_custom.rb",
29
+ "test/manual/test_radial_large_radius.rb",
30
+ "test/manual/test_svg_debug.rb"
31
+ ]
32
+
33
+ manual_tests.each do |test|
34
+ puts "\nRunning: #{test}"
35
+ result = system("ruby #{test} --automated")
36
+ if result
37
+ puts "✓ PASSED"
38
+ else
39
+ puts "✗ FAILED"
40
+ end
41
+ end
42
+
43
+ puts "\n" + "="*60
44
+ puts "All automated tests completed!"
45
+ puts "="*60
Binary file
@@ -0,0 +1,88 @@
1
+ require 'fileutils'
2
+ require 'shellwords'
3
+
4
+ module PreviewUtils
5
+ DEFAULT_CONFIG = {
6
+ "posts.per.page" => "5",
7
+ "entry.title.size" => "1.1em",
8
+ "entry.blurb.size" => "0.75em",
9
+ "entry.date.size" => "0.7em",
10
+ "entry.date.width" => "14%",
11
+ "entry.cellpadding" => "6",
12
+ "entry.margin.bottom" => "6px",
13
+ "entry.title.color" => "#000000",
14
+ "entry.blurb.color" => "#666666",
15
+ "entry.date.color" => "#888888",
16
+ "entry.line.height" => "1.1",
17
+ "entry.date.alignment"=> "right",
18
+ "entry.date.spacing" => "0.1em",
19
+ "entry.date.format" => "month dd break yyyy",
20
+ "index.margin.top" => "10px"
21
+ }.freeze
22
+
23
+ def self.write_post_index_config(repo_path, view_name, overrides = {})
24
+ config = DEFAULT_CONFIG.merge(overrides.transform_keys(&:to_s))
25
+ cfg_dir = File.join(repo_path, 'views', view_name, 'config')
26
+ FileUtils.mkdir_p(cfg_dir)
27
+ lines = config.map { |k,v| "#{k} #{v}" }.join("\n") + "\n"
28
+ File.write(File.join(cfg_dir, 'post_index.txt'), lines)
29
+ end
30
+
31
+ def self.seed_posts(api, titles_and_blurbs)
32
+ Array(titles_and_blurbs).each do |tb|
33
+ if tb.is_a?(Array)
34
+ api.create_post(tb[0], tb[1] || "")
35
+ else
36
+ api.create_post(tb.to_s, "")
37
+ end
38
+ end
39
+ end
40
+
41
+ def self.reference_image_in_post(repo_path, post_id4, filename)
42
+ src = File.join(repo_path, 'posts', post_id4, 'source.lt3')
43
+ File.write(src, ".image #{filename}\n\nHello")
44
+ end
45
+
46
+ def self.paths(repo_path, view_name)
47
+ base = File.expand_path(File.join(repo_path, 'views', view_name))
48
+ index_file = File.join(base, 'output', 'index.html')
49
+ post_index = File.join(base, 'output', 'post_index.html')
50
+ page1 = File.join(base, 'output', 'page1.html')
51
+
52
+ # Try to find first post slug from 0001
53
+ slug = nil
54
+ meta = File.join(repo_path, 'posts', '0001', 'meta.txt')
55
+ if File.exist?(meta)
56
+ line = File.read(meta).lines.grep(/^post.slug\s+/).first
57
+ slug = line.split.last if line
58
+ end
59
+ post_html = slug ? File.join(base, 'output', 'posts', slug) : nil
60
+
61
+ {
62
+ index_file: index_file,
63
+ post_index: post_index,
64
+ page1: page1,
65
+ post_html: post_html,
66
+ preview_index_url: "http://localhost:4567/preview/#{view_name}/index.html",
67
+ preview_post_url: (slug ? "http://localhost:4567/preview/#{view_name}/posts/#{slug}" : nil)
68
+ }
69
+ end
70
+
71
+ def self.interactive_open(label:, paths: [], urls: [])
72
+ puts "\n[Manual] Opening #{label}..."
73
+ # Always open local files
74
+ paths.compact.each do |target|
75
+ next if target.to_s.empty?
76
+ system("open #{Shellwords.escape(target)}")
77
+ end
78
+ # Also open server URLs (user asked to issue open commands)
79
+ urls.compact.each do |u|
80
+ next if u.to_s.empty?
81
+ system("open #{Shellwords.escape(u)}")
82
+ end
83
+ puts "[Manual] Press Enter to continue..."
84
+ STDIN.gets
85
+ end
86
+ end
87
+
88
+
@@ -0,0 +1,124 @@
1
+ .title Syntax Highlighting Test - Ruby, Elixir, JavaScript
2
+ .blurb Testing Rouge syntax highlighting for multiple languages
3
+ .views testview
4
+ .tags syntax,rouge,highlighting,manual-test
5
+
6
+ .syntax_css
7
+
8
+ This post tests the integrated Rouge syntax highlighter with three different programming languages.
9
+
10
+ ## Ruby Code Example
11
+
12
+ .code ruby
13
+ class User
14
+ attr_accessor :name, :email
15
+
16
+ def initialize(name, email)
17
+ @name = name
18
+ @email = email
19
+ end
20
+
21
+ def greet
22
+ puts "Hello, #{@name}!"
23
+ end
24
+
25
+ def valid_email?
26
+ email.match?(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i)
27
+ end
28
+ end
29
+
30
+ user = User.new("Alice", "alice@example.com")
31
+ user.greet
32
+ .end
33
+
34
+ ## Elixir Code Example
35
+
36
+ .code elixir
37
+ defmodule Calculator do
38
+ def add(a, b), do: a + b
39
+ def subtract(a, b), do: a - b
40
+ def multiply(a, b), do: a * b
41
+ def divide(a, b) when b != 0, do: a / b
42
+ def divide(_, 0), do: {:error, "Division by zero"}
43
+
44
+ def calculate(operation, a, b) do
45
+ case operation do
46
+ :add -> add(a, b)
47
+ :subtract -> subtract(a, b)
48
+ :multiply -> multiply(a, b)
49
+ :divide -> divide(a, b)
50
+ _ -> {:error, "Unknown operation"}
51
+ end
52
+ end
53
+ end
54
+
55
+ # Test the calculator
56
+ result = Calculator.calculate(:add, 5, 3)
57
+ IO.puts("5 + 3 = #{result}")
58
+ .end
59
+
60
+ ## JavaScript Code Example
61
+
62
+ .code javascript
63
+ class TodoList {
64
+ constructor() {
65
+ this.todos = [];
66
+ this.nextId = 1;
67
+ }
68
+
69
+ addTodo(text, completed = false) {
70
+ const todo = {
71
+ id: this.nextId++,
72
+ text: text,
73
+ completed: completed,
74
+ createdAt: new Date()
75
+ };
76
+
77
+ this.todos.push(todo);
78
+ return todo;
79
+ }
80
+
81
+ toggleTodo(id) {
82
+ const todo = this.todos.find(t => t.id === id);
83
+ if (todo) {
84
+ todo.completed = !todo.completed;
85
+ }
86
+ return todo;
87
+ }
88
+
89
+ removeTodo(id) {
90
+ const index = this.todos.findIndex(t => t.id === id);
91
+ if (index > -1) {
92
+ return this.todos.splice(index, 1)[0];
93
+ }
94
+ return null;
95
+ }
96
+
97
+ getCompletedTodos() {
98
+ return this.todos.filter(t => t.completed);
99
+ }
100
+
101
+ getPendingTodos() {
102
+ return this.todos.filter(t => !t.completed);
103
+ }
104
+ }
105
+
106
+ // Usage example
107
+ const todoList = new TodoList();
108
+ todoList.addTodo("Learn Rouge syntax highlighting");
109
+ todoList.addTodo("Test multiple languages");
110
+ todoList.addTodo("Write documentation", true);
111
+
112
+ console.log("Pending todos:", todoList.getPendingTodos().length);
113
+ console.log("Completed todos:", todoList.getCompletedTodos().length);
114
+ .end
115
+
116
+ ## Test Results
117
+
118
+ Each code block above should display with proper syntax highlighting:
119
+
120
+ - **Ruby**: Keywords in red, strings in blue, variables in orange
121
+ - **Elixir**: Functions in purple, atoms in green, strings in blue
122
+ - **JavaScript**: Keywords in red, functions in purple, strings in blue
123
+
124
+ The highlighting should work automatically thanks to our Rouge integration!
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/scriptorium'
4
+
5
+ puts "=== Scriptorium Gem Asset Test ==="
6
+ puts "Gem version: #{Scriptorium::VERSION}"
7
+ puts
8
+
9
+ # Test that we can access the StandardFiles class
10
+ predef = Scriptorium::StandardFiles.new
11
+ puts "✅ StandardFiles class accessible"
12
+
13
+ # Test post template generation
14
+ template = predef.support_data('templates/post.lt3')
15
+ puts "✅ Post template generated (#{template.lines.count} lines)"
16
+
17
+ # Test that clipboard function is in common.js
18
+ common_js = predef.common_js
19
+ if common_js.include?('copyPermalinkToClipboard')
20
+ puts "✅ Clipboard function found in common.js"
21
+ else
22
+ puts "❌ Clipboard function NOT found in common.js"
23
+ end
24
+
25
+ # Test template with variables
26
+ vars = {
27
+ :'post.title' => 'Test Post',
28
+ :'post.pubdate' => '2025-08-14',
29
+ :'post.date' => '2025-08-14',
30
+ :'post.tags' => 'test, gem',
31
+ :'post.body' => 'This is a test post from the installed gem.',
32
+ :reddit_button => ''
33
+ }
34
+
35
+ result = template % vars
36
+ puts "✅ Template variable substitution works"
37
+ puts "✅ Generated HTML contains copy button: #{result.include?('copyPermalinkToClipboard')}"
38
+
39
+ # Test that the copy button is present
40
+ if result.include?('Copy link')
41
+ puts "✅ Copy link button present in generated HTML"
42
+ else
43
+ puts "❌ Copy link button NOT present in generated HTML"
44
+ end
45
+
46
+ puts
47
+ puts "=== Asset Test Complete ==="
48
+ puts "All gem assets are working correctly!"