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
data/ui/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # Scriptorium UI Directory
2
+
3
+ This directory contains all user interface implementations for Scriptorium.
4
+
5
+ ## Structure
6
+
7
+ ```
8
+ ui/
9
+ ├── tui/ # Terminal User Interface
10
+ │ ├── bin/scriptorium # TUI executable
11
+ │ ├── lib/ # TUI-specific code
12
+ │ └── test/ # TUI-specific tests
13
+ ├── web/ # Web User Interface (future)
14
+ │ ├── app/ # Web app code
15
+ │ ├── public/ # Static assets
16
+ │ └── test/ # Web-specific tests
17
+ └── common/ # Shared UI utilities
18
+ ├── lib/ # Common UI libraries
19
+ └── test/ # Common UI tests
20
+ ```
21
+
22
+ ## TUI (Terminal User Interface)
23
+
24
+ The current TUI implementation provides a command-line interface for Scriptorium.
25
+
26
+ - **Location**: `ui/tui/bin/scriptorium`
27
+ - **Features**: Interactive commands, view management, post creation, deployment
28
+ - **Dependencies**: Readline (optional), core Scriptorium library
29
+
30
+ ## Web UI (Future)
31
+
32
+ Planned web interface for Scriptorium.
33
+
34
+ - **Status**: Not yet implemented
35
+ - **Planned Features**: Web-based post editing, view management, real-time preview
36
+ - **Technology**: To be determined (Sinatra, Rails, or other framework)
37
+
38
+ ## Common Utilities
39
+
40
+ Shared code that can be used by multiple UI implementations.
41
+
42
+ - **Location**: `ui/common/lib/`
43
+ - **Purpose**: API wrappers, validation, common UI patterns
44
+ - **Usage**: Imported by TUI, Web UI, and future interfaces
45
+
46
+ ## Development
47
+
48
+ ### Adding a new UI
49
+
50
+ 1. Create a new directory under `ui/`
51
+ 2. Follow the established structure (bin/, lib/, test/)
52
+ 3. Import common utilities from `ui/common/lib/`
53
+ 4. Update the gemspec to include the new UI files
54
+
55
+ ### Testing
56
+
57
+ Each UI has its own test directory for UI-specific tests. Common functionality is tested in `ui/common/test/`.
58
+
59
+ ### Dependencies
60
+
61
+ - **TUI**: Minimal dependencies, uses core Scriptorium library
62
+ - **Web UI**: Will have additional web framework dependencies
63
+ - **Common**: No additional dependencies beyond core Scriptorium
64
+
65
+ ## Migration Notes
66
+
67
+ The TUI was moved from `bin/scriptorium` to `ui/tui/bin/scriptorium` to better organize the codebase and prepare for multiple UI implementations.
@@ -0,0 +1,8 @@
1
+ # Common UI utilities that can be shared between different interfaces
2
+ module Scriptorium
3
+ module UI
4
+ module Common
5
+ # Shared utilities for all UI implementations
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,191 @@
1
+ # Scriptorium RubyText UI
2
+
3
+ ## Overview
4
+
5
+ The RubyText UI brings back the sophisticated interactive interface from Runeblog, providing a middle ground between the simple TUI and the web app. It offers:
6
+
7
+ - **Rich interactive menus** - File selection, configuration, widget management
8
+ - **Context-aware interface** - Shows current view, status, and available actions
9
+ - **Direct file editing** - Integrated editor support with syntax highlighting
10
+ - **Visual feedback** - Colors, bold text, clear screens, progress indicators
11
+ - **Widget management** - Manage pages, links, navbar, and other widgets
12
+ - **Real-time preview** - Live preview of changes as you make them
13
+
14
+ ## Architecture
15
+
16
+ ### Core Components
17
+
18
+ 1. **Main Interface** (`ui/rubytext/bin/scriptorium-rubytext`)
19
+ - Entry point with auto-discovery and setup
20
+ - Context display and command processing
21
+ - Error handling and recovery
22
+
23
+ 2. **Menu System** (`ui/rubytext/lib/menus.rb`)
24
+ - Interactive menus for file selection
25
+ - Configuration management
26
+ - Widget management interfaces
27
+
28
+ 3. **REPL System** (`ui/rubytext/lib/repl.rb`)
29
+ - Command processing and execution
30
+ - Context management
31
+ - Help system and documentation
32
+
33
+ 4. **Editor Integration** (`ui/rubytext/lib/editor.rb`)
34
+ - Direct file editing with syntax highlighting
35
+ - Editor selection and configuration
36
+ - File validation and error handling
37
+
38
+ 5. **Widget Management** (`ui/rubytext/lib/widgets.rb`)
39
+ - Page management
40
+ - Link management
41
+ - Navbar configuration
42
+ - Asset management
43
+
44
+ ## Key Features
45
+
46
+ ### Interactive Menus
47
+
48
+ ```ruby
49
+ # File selection menu
50
+ files = Dir.glob("*.rb")
51
+ num, selected = STDSCR.menu(title: "Select file:", items: files)
52
+ edit_file(selected) if selected
53
+
54
+ # Configuration menu
55
+ config_items = {
56
+ "General Settings" => "config/general.txt",
57
+ "View Settings" => "config/view.txt",
58
+ "Theme Settings" => "config/theme.txt"
59
+ }
60
+ num, target = STDSCR.menu(title: "Edit configuration:", items: config_items)
61
+ edit_file(target) if target
62
+ ```
63
+
64
+ ### Context-Aware Interface
65
+
66
+ ```ruby
67
+ # Shows current context in prompt
68
+ def display_context
69
+ view_name = @repo.current_view&.name || "No view"
70
+ print fx("[#{view_name}] ", Red, :bold)
71
+ end
72
+ ```
73
+
74
+ ### Direct File Editing
75
+
76
+ ```ruby
77
+ # Integrated editor with syntax highlighting
78
+ def edit_file(file, editor: nil)
79
+ editor ||= @repo.config.editor || "vim"
80
+ system("#{editor} #{file}")
81
+ refresh_screen
82
+ end
83
+ ```
84
+
85
+ ### Widget Management
86
+
87
+ ```ruby
88
+ # Manage pages widget
89
+ def manage_pages
90
+ pages_dir = @view.dir/"pages"
91
+ files = Dir.glob("#{pages_dir}/*.html")
92
+
93
+ menu_items = files.map { |f| File.basename(f) } + ["[New Page]"]
94
+ num, selected = STDSCR.menu(title: "Manage Pages:", items: menu_items)
95
+
96
+ case selected
97
+ when "[New Page]"
98
+ create_new_page
99
+ else
100
+ edit_page(selected)
101
+ end
102
+ end
103
+ ```
104
+
105
+ ## Implementation Plan
106
+
107
+ ### Phase 1: Core Interface
108
+ - [ ] Basic RubyText integration
109
+ - [ ] Context display and command processing
110
+ - [ ] Simple menu system
111
+ - [ ] File editing integration
112
+
113
+ ### Phase 2: Rich Menus
114
+ - [ ] Interactive file selection
115
+ - [ ] Configuration management
116
+ - [ ] Widget management interfaces
117
+ - [ ] Help system
118
+
119
+ ### Phase 3: Advanced Features
120
+ - [ ] Real-time preview
121
+ - [ ] Asset management
122
+ - [ ] Deployment integration
123
+ - [ ] Performance optimization
124
+
125
+ ## Usage Examples
126
+
127
+ ### Basic Usage
128
+
129
+ ```bash
130
+ # Start RubyText UI
131
+ scriptorium-rubytext
132
+
133
+ # Context-aware prompt
134
+ [sample] help # Show help
135
+ [sample] new post # Create new post
136
+ [sample] list posts # List posts in current view
137
+ [sample] config # Edit configuration
138
+ [sample] manage pages # Manage pages widget
139
+ ```
140
+
141
+ ### Interactive Menus
142
+
143
+ ```
144
+ Select file:
145
+ 1. config/general.txt
146
+ 2. config/view.txt
147
+ 3. config/theme.txt
148
+ 4. [Cancel]
149
+
150
+ Enter choice (1-4): 2
151
+ ```
152
+
153
+ ### Widget Management
154
+
155
+ ```
156
+ Manage Pages:
157
+ 1. about.html
158
+ 2. contact.html
159
+ 3. [New Page]
160
+ 4. [Cancel]
161
+
162
+ Enter choice (1-4): 3
163
+ ```
164
+
165
+ ## Benefits Over Current TUI
166
+
167
+ 1. **More Interactive** - Rich menus vs. command-line only
168
+ 2. **Better Visual Feedback** - Colors, bold text, clear screens
169
+ 3. **Direct File Editing** - Integrated editor support
170
+ 4. **Widget Management** - Visual management of pages, links, etc.
171
+ 5. **Context Awareness** - Always shows current view and status
172
+ 6. **Error Recovery** - Better error handling and recovery
173
+
174
+ ## Benefits Over Web App
175
+
176
+ 1. **Lighter Weight** - No web server, no browser needed
177
+ 2. **Faster** - Direct file system access
178
+ 3. **SSH Friendly** - Works over SSH connections
179
+ 4. **Offline** - No internet connection required
180
+ 5. **Familiar** - Terminal-based interface for command-line users
181
+
182
+ ## Dependencies
183
+
184
+ - `rubytext` gem - Curses-based UI framework
185
+ - `scriptorium` core - Core Scriptorium functionality
186
+ - `livetext` - Content processing and generation
187
+
188
+ ## Testing
189
+
190
+ See `test/rubytext/` for comprehensive testing strategy and implementation.
191
+
@@ -0,0 +1,402 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Scriptorium RubyText UI
4
+ # Brings back the sophisticated interactive interface from Runeblog
5
+
6
+ require 'rubytext'
7
+ require_relative '../../lib/scriptorium'
8
+ require_relative '../lib/rubytext_ui'
9
+
10
+ # Set up environment
11
+ ENV['DBC_DISABLED'] = 'true' if ENV['RACK_ENV'] == 'test'
12
+
13
+ class ScriptoriumRubyTextUI
14
+ include Scriptorium::Exceptions
15
+ include Scriptorium::Helpers
16
+
17
+ def initialize
18
+ @repo = nil
19
+ @current_view = nil
20
+ @editor = ENV['EDITOR'] || 'vim'
21
+ end
22
+
23
+ def start
24
+ setup_rubytext
25
+ check_repository
26
+ main_loop
27
+ rescue => e
28
+ handle_error(e)
29
+ ensure
30
+ cleanup
31
+ end
32
+
33
+ private
34
+
35
+ def setup_rubytext
36
+ RubyText.start(:_echo, :keypad, scroll: true, fg: :blue, bg: :white)
37
+ puts fx("Scriptorium RubyText UI", :bold)
38
+ puts fx("v #{Scriptorium::VERSION}", :red)
39
+ puts
40
+ end
41
+
42
+ def check_repository
43
+ if Scriptorium::Repo.exist?
44
+ @repo = Scriptorium::Repo.new
45
+ @current_view = @repo.current_view
46
+ puts fx("Repository found: #{@repo.root}", :green)
47
+ puts fx("Current view: #{@current_view&.name || 'None'}", :green)
48
+ else
49
+ puts fx("No repository found.", :yellow)
50
+ if yesno("Create new repository?")
51
+ create_repository
52
+ else
53
+ puts "Exiting..."
54
+ exit 0
55
+ end
56
+ end
57
+ end
58
+
59
+ def create_repository
60
+ puts fx("Creating new repository...", :bold)
61
+ @repo = Scriptorium::Repo.create
62
+ puts fx("Repository created successfully!", :green)
63
+
64
+ if yesno("Create your first view?")
65
+ create_first_view
66
+ end
67
+ end
68
+
69
+ def create_first_view
70
+ print "View name: "
71
+ view_name = gets.chomp.strip
72
+ return if view_name.empty?
73
+
74
+ print "View title: "
75
+ view_title = gets.chomp.strip
76
+ return if view_title.empty?
77
+
78
+ print "View subtitle (optional): "
79
+ view_subtitle = gets.chomp.strip
80
+
81
+ @current_view = @repo.create_view(view_name, view_title, view_subtitle)
82
+ puts fx("View '#{view_name}' created successfully!", :green)
83
+ end
84
+
85
+ def main_loop
86
+ loop do
87
+ display_context
88
+ command = gets.chomp.strip
89
+
90
+ case command.downcase
91
+ when 'q', 'quit', 'exit'
92
+ break
93
+ when 'h', 'help'
94
+ show_help
95
+ when 'v', 'version'
96
+ show_version
97
+ when 'new post'
98
+ create_post
99
+ when 'list posts'
100
+ list_posts
101
+ when 'list views'
102
+ list_views
103
+ when 'change view'
104
+ change_view
105
+ when 'config'
106
+ edit_config
107
+ when 'manage pages'
108
+ manage_pages
109
+ when 'manage links'
110
+ manage_links
111
+ when 'rebuild'
112
+ rebuild_view
113
+ when 'preview'
114
+ preview_view
115
+ when 'publish'
116
+ publish_view
117
+ when ''
118
+ # Empty command, continue
119
+ else
120
+ puts fx("Unknown command: #{command}", :red)
121
+ puts "Type 'help' for available commands"
122
+ end
123
+ end
124
+ end
125
+
126
+ def display_context
127
+ view_name = @current_view&.name || "No view"
128
+ print fx("[#{view_name}] ", :red, :bold)
129
+ end
130
+
131
+ def show_help
132
+ help_text = <<~HELP
133
+ Available Commands:
134
+
135
+ Basics:
136
+ h, help Show this help
137
+ q, quit, exit Exit the program
138
+ v, version Show version information
139
+
140
+ Posts:
141
+ new post Create a new post
142
+ list posts List posts in current view
143
+ edit <id> Edit a specific post
144
+
145
+ Views:
146
+ list views List all views
147
+ change view Change current view
148
+ new view Create a new view
149
+
150
+ Configuration:
151
+ config Edit configuration files
152
+ manage pages Manage pages widget
153
+ manage links Manage links widget
154
+
155
+ Generation:
156
+ rebuild Rebuild current view
157
+ preview Preview current view
158
+ publish Publish current view
159
+ HELP
160
+
161
+ puts help_text
162
+ end
163
+
164
+ def show_version
165
+ puts fx("Scriptorium", :bold), fx(" v #{Scriptorium::VERSION}", :red)
166
+ end
167
+
168
+ def create_post
169
+ return unless check_view
170
+
171
+ print "Post title: "
172
+ title = gets.chomp.strip
173
+ return if title.empty?
174
+
175
+ print "Post body (optional): "
176
+ body = gets.chomp.strip
177
+
178
+ draft_name = @repo.create_draft(title: title, body: body)
179
+ post_num = @repo.finish_draft(draft_name)
180
+
181
+ puts fx("Post ##{post_num} created successfully!", :green)
182
+ end
183
+
184
+ def list_posts
185
+ return unless check_view
186
+
187
+ posts = @repo.all_posts(@current_view)
188
+ if posts.empty?
189
+ puts "No posts in current view."
190
+ else
191
+ puts fx("Posts in #{@current_view.name}:", :bold)
192
+ posts.each do |post|
193
+ puts " #{post.id}: #{post.title}"
194
+ end
195
+ end
196
+ end
197
+
198
+ def list_views
199
+ views = @repo.views
200
+ if views.empty?
201
+ puts "No views found."
202
+ else
203
+ puts fx("Available views:", :bold)
204
+ views.each do |view|
205
+ current = view == @current_view ? " (current)" : ""
206
+ puts " #{view.name}#{current}"
207
+ end
208
+ end
209
+ end
210
+
211
+ def change_view
212
+ views = @repo.views
213
+ return puts("No views available.") if views.empty?
214
+
215
+ puts fx("Available views:", :bold)
216
+ views.each_with_index do |view, i|
217
+ current = view == @current_view ? " (current)" : ""
218
+ puts " #{i + 1}. #{view.name}#{current}"
219
+ end
220
+
221
+ print "Select view (1-#{views.length}): "
222
+ choice = gets.chomp.strip.to_i
223
+
224
+ if choice >= 1 && choice <= views.length
225
+ @current_view = views[choice - 1]
226
+ @repo.current_view = @current_view
227
+ puts fx("Switched to view: #{@current_view.name}", :green)
228
+ else
229
+ puts fx("Invalid choice.", :red)
230
+ end
231
+ end
232
+
233
+ def edit_config
234
+ config_items = {
235
+ "General Settings" => "config/general.txt",
236
+ "View Settings" => "config/view.txt",
237
+ "Theme Settings" => "config/theme.txt"
238
+ }
239
+
240
+ puts fx("Configuration files:", :bold)
241
+ config_items.each_with_index do |(name, path), i|
242
+ puts " #{i + 1}. #{name}"
243
+ end
244
+
245
+ print "Select file (1-#{config_items.length}): "
246
+ choice = gets.chomp.strip.to_i
247
+
248
+ if choice >= 1 && choice <= config_items.length
249
+ file_path = config_items.values[choice - 1]
250
+ edit_file(file_path)
251
+ else
252
+ puts fx("Invalid choice.", :red)
253
+ end
254
+ end
255
+
256
+ def manage_pages
257
+ return unless check_view
258
+
259
+ pages_dir = @current_view.dir/"pages"
260
+ files = Dir.glob("#{pages_dir}/*.html") if Dir.exist?(pages_dir)
261
+ files ||= []
262
+
263
+ menu_items = files.map { |f| File.basename(f) } + ["[New Page]", "[Cancel]"]
264
+
265
+ puts fx("Manage Pages:", :bold)
266
+ menu_items.each_with_index do |item, i|
267
+ puts " #{i + 1}. #{item}"
268
+ end
269
+
270
+ print "Select option (1-#{menu_items.length}): "
271
+ choice = gets.chomp.strip.to_i
272
+
273
+ if choice >= 1 && choice <= menu_items.length
274
+ selected = menu_items[choice - 1]
275
+ case selected
276
+ when "[New Page]"
277
+ create_new_page
278
+ when "[Cancel]"
279
+ return
280
+ else
281
+ edit_page(selected)
282
+ end
283
+ else
284
+ puts fx("Invalid choice.", :red)
285
+ end
286
+ end
287
+
288
+ def manage_links
289
+ return unless check_view
290
+
291
+ links_dir = @current_view.dir/"widgets"/"links"
292
+ list_file = links_dir/"list.txt" if Dir.exist?(links_dir)
293
+
294
+ if list_file && File.exist?(list_file)
295
+ edit_file(list_file)
296
+ else
297
+ puts "No links widget found. Create one first."
298
+ end
299
+ end
300
+
301
+ def rebuild_view
302
+ return unless check_view
303
+
304
+ puts fx("Rebuilding view...", :bold)
305
+ @repo.generate_view(@current_view)
306
+ puts fx("View rebuilt successfully!", :green)
307
+ end
308
+
309
+ def preview_view
310
+ return unless check_view
311
+
312
+ puts fx("Starting preview server...", :bold)
313
+ # Implementation would start a local server
314
+ puts "Preview available at: http://localhost:8000"
315
+ end
316
+
317
+ def publish_view
318
+ return unless check_view
319
+
320
+ puts fx("Publishing view...", :bold)
321
+ # Implementation would handle deployment
322
+ puts fx("View published successfully!", :green)
323
+ end
324
+
325
+ def create_new_page
326
+ print "Page title: "
327
+ title = gets.chomp.strip
328
+ return if title.empty?
329
+
330
+ print "Page filename (without .html): "
331
+ filename = gets.chomp.strip
332
+ return if filename.empty?
333
+
334
+ filename += ".html" unless filename.end_with?(".html")
335
+ page_path = @current_view.dir/"pages"/filename
336
+
337
+ content = <<~HTML
338
+ <!DOCTYPE html>
339
+ <html>
340
+ <head>
341
+ <title>#{title}</title>
342
+ </head>
343
+ <body>
344
+ <h1>#{title}</h1>
345
+ <p>Page content goes here.</p>
346
+ </body>
347
+ </html>
348
+ HTML
349
+
350
+ File.write(page_path, content)
351
+ puts fx("Page '#{filename}' created successfully!", :green)
352
+ end
353
+
354
+ def edit_page(filename)
355
+ page_path = @current_view.dir/"pages"/filename
356
+ if File.exist?(page_path)
357
+ edit_file(page_path)
358
+ else
359
+ puts fx("Page '#{filename}' not found.", :red)
360
+ end
361
+ end
362
+
363
+ def edit_file(file_path)
364
+ full_path = File.expand_path(file_path, @repo.root)
365
+ if File.exist?(full_path)
366
+ system("#{@editor} #{full_path}")
367
+ else
368
+ puts fx("File '#{file_path}' not found.", :red)
369
+ end
370
+ end
371
+
372
+ def check_view
373
+ unless @current_view
374
+ puts fx("No view selected. Use 'change view' first.", :red)
375
+ return false
376
+ end
377
+ true
378
+ end
379
+
380
+ def yesno(question)
381
+ print fx("#{question} (y/n): ", :bold)
382
+ response = gets.chomp.strip.downcase
383
+ response == 'y' || response == 'yes'
384
+ end
385
+
386
+ def handle_error(error)
387
+ puts fx("Error: #{error.message}", :red)
388
+ puts error.backtrace if ENV['DEBUG']
389
+ end
390
+
391
+ def cleanup
392
+ RubyText.stop
393
+ puts "\nGoodbye!"
394
+ end
395
+ end
396
+
397
+ # Main execution
398
+ if __FILE__ == $0
399
+ ui = ScriptoriumRubyTextUI.new
400
+ ui.start
401
+ end
402
+