scriptorium 0.0.3 → 0.6.1

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 (292) 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/back-icon.png +0 -0
  7. data/assets/icons/facebook.svg +1 -0
  8. data/assets/icons/github.svg +1 -0
  9. data/assets/icons/instagram.svg +1 -0
  10. data/assets/icons/reddit.svg +1 -0
  11. data/assets/icons/ui/.DS_Store +0 -0
  12. data/assets/icons/ui/back.png +0 -0
  13. data/assets/icons/ui/copy.png +0 -0
  14. data/assets/icons/ui/down.png +0 -0
  15. data/assets/icons/ui/end.png +0 -0
  16. data/assets/icons/ui/exit.png +0 -0
  17. data/assets/icons/ui/foo +10 -0
  18. data/assets/icons/ui/home.png +0 -0
  19. data/assets/icons/ui/left.png +0 -0
  20. data/assets/icons/ui/next.png +0 -0
  21. data/assets/icons/ui/right.png +0 -0
  22. data/assets/icons/ui/start.png +0 -0
  23. data/assets/icons/ui/up.png +0 -0
  24. data/assets/icons/x.svg +1 -0
  25. data/assets/icons/youtube.svg +1 -0
  26. data/assets/samples/placeholder.svg +9 -0
  27. data/assets/themes/standard/favicon.svg +6 -0
  28. data/bin/scriptorium +1511 -0
  29. data/doc/README.txt +6 -0
  30. data/doc/anti-amnesia/20250727-054000-scriptorium-overview.md +95 -0
  31. data/doc/anti-amnesia/20250727-060000-api-design-tui-planning.md +34 -0
  32. data/doc/anti-amnesia/20250727-061000-runeblog-tui-analysis.md +50 -0
  33. data/doc/anti-amnesia/20250727-123000-anti-amnesia-conventions.md +31 -0
  34. data/doc/anti-amnesia/20250727-154000-livetext-plugin-file-stats.md +73 -0
  35. data/doc/anti-amnesia/20250727-172600-cursor-rbenv-ruby-version-mystery.md +64 -0
  36. data/doc/anti-amnesia/20250727-172600-unified-minitest-framework.md +70 -0
  37. data/doc/anti-amnesia/20250727-172900-ai-cognitive-assessment-capabilities.md +40 -0
  38. data/doc/anti-amnesia/20250727-173000-widget-testing-achievement.md +110 -0
  39. data/doc/anti-amnesia/20250727-180000-post-id-num-refactoring.md +73 -0
  40. data/doc/anti-amnesia/20250728-124243-aaa-syntax-clarification.md +46 -0
  41. data/doc/anti-amnesia/20250728-124421-conversation-summary-concise.md +124 -0
  42. data/doc/anti-amnesia/20250729-190000-scriptorium-tui-testing-complete.md +46 -0
  43. data/doc/anti-amnesia/20250729-200000-scriptorium-tui-testing-edit-file-workflow.md +97 -0
  44. data/doc/anti-amnesia/20250729-210000-reddit-autopost-integration-complete.md +158 -0
  45. data/doc/anti-amnesia/20250729-211500-dependency-management-system.md +211 -0
  46. data/doc/anti-amnesia/20250729-213000-python-virtual-environment-setup.md +141 -0
  47. data/doc/anti-amnesia/20250729-214500-theme-management-commands.md +211 -0
  48. data/doc/anti-amnesia/20250729-215000-version-update-to-0.6.0.md +134 -0
  49. data/doc/anti-amnesia/20250729-220000-user-guide-complete.md +41 -0
  50. data/doc/anti-amnesia/20250804-190500-cognitive-loop-bug.md +45 -0
  51. data/doc/anti-amnesia/20250804-190700-anti-amnesia-timestamping-fix.md +30 -0
  52. data/doc/anti-amnesia/20250804-213700-publishing-test-fix.md +49 -0
  53. data/doc/anti-amnesia/20250804-214400-additional-test-fixes.md +46 -0
  54. data/doc/anti-amnesia/20250804-220000-asset-function-logic-clarification.md +41 -0
  55. data/doc/anti-amnesia/20250806-202032-asset-function-logic-clarification.md +41 -0
  56. data/doc/anti-amnesia/20250807-213025.md +116 -0
  57. data/doc/anti-amnesia/20250813-082428-syntax-highlighting-and-navigation-improvements.md +256 -0
  58. data/doc/banner_svg_config.md +114 -0
  59. data/doc/contrib.lt3 +8 -0
  60. data/doc/dependencies.md +281 -0
  61. data/doc/hacker.lt3 +5 -0
  62. data/doc/reddit_credentials_template.json +8 -0
  63. data/doc/reddit_integration.md +207 -0
  64. data/doc/user.lt3 +38 -0
  65. data/doc/user_guide_section_1.md +137 -0
  66. data/doc/user_guide_section_10.md +515 -0
  67. data/doc/user_guide_section_11.md +708 -0
  68. data/doc/user_guide_section_2.md +233 -0
  69. data/doc/user_guide_section_3.md +5 -0
  70. data/doc/user_guide_section_4.md +221 -0
  71. data/doc/user_guide_section_5.md +243 -0
  72. data/doc/user_guide_section_6.md +147 -0
  73. data/doc/user_guide_section_7.md +311 -0
  74. data/doc/user_guide_section_8.md +224 -0
  75. data/doc/user_guide_section_9.md +375 -0
  76. data/doc/userdoc-toc.txt +88 -0
  77. data/lib/rouge/lexers/livetext.rb +74 -0
  78. data/lib/scriptorium/api.rb +640 -0
  79. data/lib/scriptorium/banner_svg.rb +742 -0
  80. data/lib/scriptorium/contract.rb +33 -0
  81. data/lib/scriptorium/exceptions.rb +170 -1
  82. data/lib/scriptorium/helpers.rb +475 -0
  83. data/lib/scriptorium/post.rb +195 -0
  84. data/lib/scriptorium/reddit.rb +83 -0
  85. data/lib/scriptorium/repo.rb +624 -0
  86. data/lib/scriptorium/standard_files.rb +515 -0
  87. data/lib/scriptorium/syntax_highlighter.rb +234 -0
  88. data/lib/scriptorium/theme.rb +179 -0
  89. data/lib/scriptorium/version.rb +2 -2
  90. data/lib/scriptorium/view.rb +976 -0
  91. data/lib/scriptorium/widgets/featured_posts.rb +149 -0
  92. data/lib/scriptorium/widgets/links.rb +112 -0
  93. data/lib/scriptorium/widgets/pages.rb +133 -0
  94. data/lib/scriptorium/widgets/widget.rb +133 -0
  95. data/lib/scriptorium.rb +21 -40
  96. data/lib/skeleton.rb +8 -2
  97. data/scriptorium.gemspec +15 -4
  98. data/test/README.md +69 -0
  99. data/test/all +43 -0
  100. data/test/api_demo.rb +99 -0
  101. data/test/assets/imagenotfound.jpg +0 -0
  102. data/test/assets/images/.DS_Store +0 -0
  103. data/test/assets/images/README.md +27 -0
  104. data/test/assets/images/odd_aspect.png +0 -0
  105. data/test/assets/images/perfect.png +0 -0
  106. data/test/assets/images/small.png +0 -0
  107. data/test/assets/images/tall.png +0 -0
  108. data/test/assets/images/very_tall.png +0 -0
  109. data/test/assets/images/very_wide.png +0 -0
  110. data/test/assets/images/wide.png +0 -0
  111. data/test/assets/testbanner.jpg +0 -0
  112. data/test/banner_svg/simple_helpers.rb +13 -0
  113. data/test/banner_svg/unit.rb +768 -0
  114. data/test/ed_test.rb +204 -0
  115. data/test/integration/cursor_banner_combinations.rb +193 -0
  116. data/test/integration/cursor_banner_features.rb +374 -0
  117. data/test/integration/integration_test.rb +326 -0
  118. data/test/livetext_plugin_test.rb +229 -0
  119. data/test/manual/asset_mgmt.rb +67 -0
  120. data/test/manual/banner-tests/config.txt +3 -0
  121. data/test/manual/banner-tests/index.html +45 -0
  122. data/test/manual/banner-tests/test01.html +58 -0
  123. data/test/manual/banner-tests/test02.html +58 -0
  124. data/test/manual/banner-tests/test03.html +58 -0
  125. data/test/manual/banner-tests/test04.html +65 -0
  126. data/test/manual/banner-tests/test05.html +65 -0
  127. data/test/manual/banner-tests/test06.html +65 -0
  128. data/test/manual/banner-tests/test07.html +65 -0
  129. data/test/manual/banner-tests/test08.html +59 -0
  130. data/test/manual/banner-tests/test09.html +59 -0
  131. data/test/manual/banner-tests/test10.html +59 -0
  132. data/test/manual/banner-tests/test11.html +59 -0
  133. data/test/manual/banner-tests/test12.html +59 -0
  134. data/test/manual/banner-tests/test13.html +59 -0
  135. data/test/manual/banner-tests/test14.html +59 -0
  136. data/test/manual/banner-tests/test15.html +58 -0
  137. data/test/manual/banner-tests/test16.html +58 -0
  138. data/test/manual/banner-tests/test17.html +58 -0
  139. data/test/manual/banner-tests/test18.html +68 -0
  140. data/test/manual/banner-tests/test19.html +68 -0
  141. data/test/manual/banner-tests/test20.html +68 -0
  142. data/test/manual/banner-tests/test21.html +68 -0
  143. data/test/manual/banner-tests/test22.html +68 -0
  144. data/test/manual/banner-tests/test23.html +68 -0
  145. data/test/manual/banner-tests/test24.html +68 -0
  146. data/test/manual/banner-tests/test25.html +67 -0
  147. data/test/manual/banner_environment.rb +192 -0
  148. data/test/manual/deploy_symlink_demo.rb +142 -0
  149. data/test/manual/environment.rb +67 -0
  150. data/test/manual/make_banner.rb +153 -0
  151. data/test/manual/sample_banner_config.txt +12 -0
  152. data/test/manual/symlink_demo.rb +117 -0
  153. data/test/manual/test1.rb +47 -0
  154. data/test/manual/test2.rb +12 -0
  155. data/test/manual/test3.rb +38 -0
  156. data/test/manual/test4.rb +40 -0
  157. data/test/manual/test5.rb +24 -0
  158. data/test/manual/test6.rb +73 -0
  159. data/test/manual/test_banner_combinations.rb +120 -0
  160. data/test/manual/test_banner_features.rb +306 -0
  161. data/test/manual/test_banner_from_file.rb +150 -0
  162. data/test/manual/test_banner_in_header.rb +35 -0
  163. data/test/manual/test_code_highlighting.rb +68 -0
  164. data/test/manual/test_complex_header.rb +74 -0
  165. data/test/manual/test_empty_header.rb +32 -0
  166. data/test/manual/test_radial_custom.rb +58 -0
  167. data/test/manual/test_radial_large_radius.rb +52 -0
  168. data/test/manual/test_svg_debug.rb +47 -0
  169. data/test/manual/test_syntax_highlighting.rb +147 -0
  170. data/test/pages-demo/config/currentview.txt +1 -0
  171. data/test/pages-demo/views/demo/config/bootstrap_css.txt +5 -0
  172. data/test/pages-demo/views/demo/config/bootstrap_js.txt +4 -0
  173. data/test/pages-demo/views/demo/config/common.js +57 -0
  174. data/test/pages-demo/views/demo/config/footer.txt +1 -0
  175. data/test/pages-demo/views/demo/config/global-head.txt +8 -0
  176. data/test/pages-demo/views/demo/config/header.txt +1 -0
  177. data/test/pages-demo/views/demo/config/layout.txt +1 -0
  178. data/test/pages-demo/views/demo/config/left.txt +1 -0
  179. data/test/pages-demo/views/demo/config/main.txt +1 -0
  180. data/test/pages-demo/views/demo/config/right.txt +1 -0
  181. data/test/pages-demo/views/demo/config.txt +3 -0
  182. data/test/pages-demo/views/demo/output/panes/footer.html +1 -0
  183. data/test/pages-demo/views/demo/output/panes/header.html +1 -0
  184. data/test/pages-demo/views/demo/output/panes/left.html +1 -0
  185. data/test/pages-demo/views/demo/output/panes/main.html +1 -0
  186. data/test/pages-demo/views/demo/output/panes/right.html +1 -0
  187. data/test/rubytext/rubytext_comprehensive_test.rb +307 -0
  188. data/test/rubytext/rubytext_demo_test.rb +42 -0
  189. data/test/rubytext/rubytext_testing_guide.md +277 -0
  190. data/test/run_automated_tests.rb +45 -0
  191. data/test/scriptorium-TEST-1754622690-146/config/bootstrap_css.txt +5 -0
  192. data/test/scriptorium-TEST-1754622690-146/config/bootstrap_js.txt +4 -0
  193. data/test/scriptorium-TEST-1754622690-146/config/common.js +57 -0
  194. data/test/scriptorium-TEST-1754622690-146/config/currentview.txt +1 -0
  195. data/test/scriptorium-TEST-1754622690-146/config/global-head.txt +9 -0
  196. data/test/scriptorium-TEST-1754622690-146/config/last_post_num.txt +1 -0
  197. data/test/scriptorium-TEST-1754622690-146/config/os_helpers.rb +4 -0
  198. data/test/scriptorium-TEST-1754622690-146/config/widgets.txt +3 -0
  199. data/test/scriptorium-TEST-1754622690-146/posts/0001/meta.txt +8 -0
  200. data/test/scriptorium-TEST-1754622690-146/posts/0001/source.lt3 +6 -0
  201. data/test/scriptorium-TEST-1754622690-146/themes/standard/README.txt +1 -0
  202. data/test/scriptorium-TEST-1754622690-146/themes/standard/config.txt +1 -0
  203. data/test/scriptorium-TEST-1754622690-146/themes/standard/initial/post.lt3 +12 -0
  204. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/footer.txt +2 -0
  205. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/header.txt +4 -0
  206. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/left.txt +3 -0
  207. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/main.txt +5 -0
  208. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/right.txt +3 -0
  209. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/gen/text.css +1 -0
  210. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/layout.txt +5 -0
  211. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/index.lt3 +1 -0
  212. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/index_entry.lt3 +14 -0
  213. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/post.lt3 +13 -0
  214. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/widget.lt3 +1 -0
  215. data/test/scriptorium-TEST-1754622690-146/views/sample/config/bootstrap_css.txt +5 -0
  216. data/test/scriptorium-TEST-1754622690-146/views/sample/config/bootstrap_js.txt +4 -0
  217. data/test/scriptorium-TEST-1754622690-146/views/sample/config/common.js +57 -0
  218. data/test/scriptorium-TEST-1754622690-146/views/sample/config/deploy.txt +5 -0
  219. data/test/scriptorium-TEST-1754622690-146/views/sample/config/footer.txt +2 -0
  220. data/test/scriptorium-TEST-1754622690-146/views/sample/config/global-head.txt +9 -0
  221. data/test/scriptorium-TEST-1754622690-146/views/sample/config/header.txt +4 -0
  222. data/test/scriptorium-TEST-1754622690-146/views/sample/config/layout.txt +5 -0
  223. data/test/scriptorium-TEST-1754622690-146/views/sample/config/left.txt +3 -0
  224. data/test/scriptorium-TEST-1754622690-146/views/sample/config/main.txt +5 -0
  225. data/test/scriptorium-TEST-1754622690-146/views/sample/config/reddit.txt +10 -0
  226. data/test/scriptorium-TEST-1754622690-146/views/sample/config/right.txt +3 -0
  227. data/test/scriptorium-TEST-1754622690-146/views/sample/config/social.txt +7 -0
  228. data/test/scriptorium-TEST-1754622690-146/views/sample/config/status.txt +7 -0
  229. data/test/scriptorium-TEST-1754622690-146/views/sample/config.txt +3 -0
  230. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/footer.html +3 -0
  231. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/header.html +3 -0
  232. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/left.html +3 -0
  233. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/main.html +3 -0
  234. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/right.html +3 -0
  235. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/footer.html +1 -0
  236. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/header.html +1 -0
  237. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/left.html +1 -0
  238. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/main.html +1 -0
  239. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/right.html +1 -0
  240. data/test/staging/.DS_Store +0 -0
  241. data/test/syntax_highlighting_test.lt3 +124 -0
  242. data/test/test_helpers.rb +230 -0
  243. data/test/tui_editor_integration_test.rb +296 -0
  244. data/test/tui_integration_test.rb +637 -0
  245. data/test/unit/api.rb +1056 -0
  246. data/test/unit/asset_management.rb +245 -0
  247. data/test/unit/clipboard_test.rb +60 -0
  248. data/test/unit/contract_test.rb +91 -0
  249. data/test/unit/core.rb +857 -0
  250. data/test/unit/deploy_test.rb +187 -0
  251. data/test/unit/gem_asset_management.rb +189 -0
  252. data/test/unit/livetext_basic.rb +69 -0
  253. data/test/unit/livetext_compatibility.rb +89 -0
  254. data/test/unit/post.rb +244 -0
  255. data/test/unit/read_commented_file_test.rb +276 -0
  256. data/test/unit/reddit_test.rb +235 -0
  257. data/test/unit/repo.rb +548 -0
  258. data/test/unit/social_test.rb +369 -0
  259. data/test/unit/symlink_test.rb +213 -0
  260. data/test/unit/view.rb +431 -0
  261. data/test/unit/widgets.rb +669 -0
  262. data/test/wizard_test.rb +123 -0
  263. data/ui/README.md +67 -0
  264. data/ui/common/lib/ui_common.rb +8 -0
  265. data/ui/rubytext/README.md +191 -0
  266. data/ui/rubytext/bin/scriptorium-rubytext +402 -0
  267. data/ui/rubytext/lib/rubytext_ui.rb +300 -0
  268. data/ui/tui/bin/scriptorium +1420 -0
  269. data/ui/tui/test/tui_test.rb +23 -0
  270. data/ui/web/app/app.rb +1378 -0
  271. data/ui/web/app/error_helpers.rb +150 -0
  272. data/ui/web/app/views/advanced_config.erb +190 -0
  273. data/ui/web/app/views/asset_management.erb +589 -0
  274. data/ui/web/app/views/banner_config.erb +200 -0
  275. data/ui/web/app/views/configure_view.erb +401 -0
  276. data/ui/web/app/views/dashboard.erb +162 -0
  277. data/ui/web/app/views/deploy_config.erb +146 -0
  278. data/ui/web/app/views/edit_pages.erb +195 -0
  279. data/ui/web/app/views/edit_post.erb +54 -0
  280. data/ui/web/app/views/error_page.erb +29 -0
  281. data/ui/web/app/views/header_config.erb +155 -0
  282. data/ui/web/app/views/layout_config.erb +147 -0
  283. data/ui/web/app/views/navbar_config.erb +411 -0
  284. data/ui/web/app/views/view_dashboard.erb +138 -0
  285. data/ui/web/bin/scriptorium-web +153 -0
  286. data/ui/web/test/web_basic_test.rb +38 -0
  287. data/ui/web/test_navbar.txt +7 -0
  288. data/ui/web/tmp/web_server.log +5 -0
  289. data/ui/web/tmp/web_server.pid +1 -0
  290. metadata +359 -7
  291. data/lib/scriptorium/engine.rb +0 -22
  292. data/test/engine/unit.rb +0 -44
@@ -0,0 +1,234 @@
1
+ require 'rouge'
2
+
3
+ module Scriptorium
4
+ class SyntaxHighlighter
5
+ # Default theme - can be customized
6
+ DEFAULT_THEME = {
7
+ 'keyword' => 'color: #07a; font-weight: bold;',
8
+ 'string' => 'color: #690;',
9
+ 'comment' => 'color: #708090; font-style: italic;',
10
+ 'function' => 'color: #dd4a68;',
11
+ 'number' => 'color: #905;',
12
+ 'symbol' => 'color: #905;',
13
+ 'operator' => 'color: #9a6e3a;',
14
+ 'class-name' => 'color: #dd4a68;',
15
+ 'regex' => 'color: #e90;',
16
+ 'variable' => 'color: #e90;',
17
+ 'interpolation' => 'color: #690;', # same as string
18
+ 'error' => 'color: #d73a49; font-weight: bold;',
19
+ 'whitespace' => 'color: transparent;',
20
+ 'other' => 'color: #24292e;'
21
+ }
22
+
23
+ def initialize(theme = nil)
24
+ @theme = theme || DEFAULT_THEME
25
+ @formatter = Rouge::Formatters::HTML.new
26
+ end
27
+
28
+ # Highlight code with language detection
29
+ def highlight(code, language = nil)
30
+ return escape_html(code) if language.nil? || language.empty?
31
+
32
+ language = language.downcase.strip
33
+
34
+ # Map common language names to Rouge lexers
35
+ lexer_name = map_language(language)
36
+ return escape_html(code) unless lexer_name
37
+
38
+ begin
39
+ lexer = Rouge::Lexer.find(lexer_name)
40
+ puts "DEBUG: Found lexer: #{lexer.class}"
41
+ highlighted = @formatter.format(lexer.lex(code))
42
+ puts "DEBUG: Rouge output length: #{highlighted.length}"
43
+ puts "DEBUG: Raw Rouge output (first 500 chars): #{highlighted[0..500]}"
44
+ puts "DEBUG: HTML contains <div>: #{highlighted.include?('<div')}"
45
+ puts "DEBUG: HTML contains <br>: #{highlighted.include?('<br')}"
46
+ puts "DEBUG: HTML contains \\n: #{highlighted.include?("\n")}"
47
+
48
+ # Massage the output to use our CSS classes
49
+ massage_output(highlighted, language)
50
+ rescue => e
51
+ # Fallback to escaped HTML if highlighting fails
52
+ puts "DEBUG: Rouge highlighting failed for #{language}: #{e.message}"
53
+ puts "DEBUG: Backtrace: #{e.backtrace.first(3).join(', ')}"
54
+ escape_html(code)
55
+ end
56
+ end
57
+
58
+ # Generate CSS for the theme
59
+ def generate_css
60
+ css = []
61
+ css << "/* Syntax highlighting styles */"
62
+ css << "pre {"
63
+ css << " background: #f5f5f5;"
64
+ css << " padding: 15px;"
65
+ css << " border-radius: 5px;"
66
+ css << " overflow-x: auto;"
67
+ css << "}"
68
+ css << ""
69
+ css << "pre code {"
70
+ css << " font-family: 'Consolas', 'Monaco', 'Andale Mono', monospace;"
71
+ css << " font-size: 14px;"
72
+ css << "}"
73
+ css << ""
74
+ css << "/* Override Bootstrap height constraints for syntax highlighting */"
75
+ css << "pre code[class*=\"language-\"] {"
76
+ css << " height: auto !important;"
77
+ css << " max-height: none !important;"
78
+ css << " min-height: auto !important;"
79
+ css << " overflow: visible !important;"
80
+ css << "}"
81
+ css << ""
82
+ css << "pre:has(code[class*=\"language-\"]) {"
83
+ css << " height: auto !important;"
84
+ css << " max-height: none !important;"
85
+ css << " min-height: auto !important;"
86
+ css << " overflow: visible !important;"
87
+ css << "}"
88
+
89
+ @theme.each do |token, style|
90
+ css << ".token.#{token} { #{style} }"
91
+ end
92
+
93
+ css.join("\n")
94
+ end
95
+
96
+ private
97
+
98
+ # Map common language names to Rouge lexer names
99
+ def map_language(language)
100
+ language_map = {
101
+ 'ruby' => 'ruby',
102
+ 'rb' => 'ruby',
103
+ 'javascript' => 'javascript',
104
+ 'js' => 'javascript',
105
+ 'elixir' => 'elixir',
106
+ 'python' => 'python',
107
+ 'py' => 'python',
108
+ 'html' => 'html',
109
+ 'css' => 'css',
110
+ 'sql' => 'sql',
111
+ 'bash' => 'bash',
112
+ 'shell' => 'bash',
113
+ 'sh' => 'bash',
114
+ 'yaml' => 'yaml',
115
+ 'yml' => 'yaml',
116
+ 'json' => 'json',
117
+ 'xml' => 'xml',
118
+ 'markdown' => 'markdown',
119
+ 'md' => 'markdown',
120
+ 'go' => 'go',
121
+ 'rust' => 'rust',
122
+ 'java' => 'java',
123
+ 'cpp' => 'cpp',
124
+ 'c' => 'c',
125
+ 'csharp' => 'csharp',
126
+ 'cs' => 'csharp',
127
+ 'php' => 'php',
128
+ 'r' => 'r',
129
+ 'scala' => 'scala',
130
+ 'swift' => 'swift',
131
+ 'kotlin' => 'kotlin',
132
+ 'dart' => 'dart'
133
+ }
134
+
135
+ language_map[language]
136
+ end
137
+
138
+ # Massage Rouge output to use our CSS classes
139
+ def massage_output(html, language)
140
+ # Rouge outputs classes like 'k' for keyword, 's' for string, etc.
141
+ # We want to map these to our semantic class names
142
+ class_mapping = {
143
+ 'k' => 'keyword', # keyword
144
+ 's' => 'string', # string
145
+ 'c' => 'comment', # comment
146
+ 'f' => 'function', # function
147
+ 'n' => 'number', # number
148
+ 'o' => 'operator', # operator
149
+ 'c1' => 'comment', # comment
150
+ 's1' => 'string', # string
151
+ 's2' => 'string', # string
152
+ 'nb' => 'function', # function name
153
+ 'na' => 'function', # function argument
154
+ 'nc' => 'class-name', # class name
155
+ 'nd' => 'function', # function definition
156
+ 'ne' => 'variable', # variable
157
+ 'nf' => 'function', # function
158
+ 'ni' => 'variable', # variable
159
+ 'nl' => 'number', # number literal
160
+ 'nn' => 'class-name', # class name
161
+ 'no' => 'constant', # constant
162
+ 'nt' => 'tag', # tag name
163
+ 'nv' => 'variable', # variable
164
+ 'ow' => 'operator', # operator
165
+ 'p' => 'punctuation', # punctuation
166
+ 'pi' => 'doctype', # doctype
167
+ # JavaScript-specific mappings
168
+ 'kd' => 'keyword', # keyword declaration (function, var, etc.)
169
+ 'nx' => 'variable', # variable name
170
+ 'dl' => 'string', # string delimiter
171
+ 'cm' => 'comment', # comment
172
+ 'cp' => 'comment', # comment
173
+ 'cs' => 'comment', # comment
174
+ 'ge' => 'comment', # comment
175
+ 'gh' => 'comment', # comment
176
+ 'gp' => 'comment', # comment
177
+ 'gs' => 'comment', # comment
178
+ 'gu' => 'comment', # comment
179
+ 'il' => 'number', # number literal
180
+ 'm' => 'number', # number
181
+ 'mf' => 'number', # number
182
+ 'mh' => 'number', # number
183
+ 'mi' => 'number', # number
184
+ 'mo' => 'number', # number
185
+ 'sb' => 'string', # string
186
+ 'sc' => 'string', # string
187
+ 'sd' => 'string', # string
188
+ 'se' => 'string', # string
189
+ 'sh' => 'string', # string
190
+ 'si' => 'string', # string
191
+ 'sr' => 'string', # string
192
+ 'ss' => 'string', # string
193
+ 'vi' => 'variable', # instance variable (@name)
194
+ 'vc' => 'variable', # class variable (@@name)
195
+ 'vg' => 'variable', # global variable ($name)
196
+ 'bp' => 'variable', # built-in pseudo
197
+ 'err' => 'error', # error
198
+ 'w' => 'whitespace', # whitespace
199
+ 'x' => 'other', # other
200
+ 'kc' => 'constant' # constant (true, false, null)
201
+ }
202
+
203
+ # Replace Rouge's short class names with our semantic names
204
+ class_mapping.each do |rouge_class, semantic_class|
205
+ html.gsub!(/class="#{rouge_class}"/, "class=\"token #{semantic_class}\"")
206
+ html.gsub!(/class='#{rouge_class}'/, "class='token #{semantic_class}'")
207
+ end
208
+
209
+ # Add 'token' class to all spans that don't have it
210
+ html.gsub!(/<span class="([^"]*?)">/, '<span class="token \1">')
211
+ html.gsub!(/<span class='([^']*?)'>/, "<span class='token \\1'>")
212
+
213
+ # Clean up duplicate 'token token' classes
214
+ html.gsub!(/class="token token ([^"]*)"/, 'class="token \1"')
215
+ html.gsub!(/class='token token ([^']*)'/, "class='token \\1'")
216
+
217
+ html
218
+ end
219
+
220
+
221
+
222
+ # Simple HTML escaping
223
+ def escape_html(text)
224
+ text.to_s
225
+ .gsub('&', '&amp;')
226
+ .gsub('<', '&lt;')
227
+ .gsub('>', '&gt;')
228
+ .gsub('"', '&quot;')
229
+ .gsub("'", '&#39;')
230
+ end
231
+
232
+
233
+ end
234
+ end
@@ -0,0 +1,179 @@
1
+ class Scriptorium::Theme
2
+
3
+ include Scriptorium::Helpers
4
+ extend Scriptorium::Helpers
5
+ include Scriptorium::Exceptions
6
+ include Scriptorium::Contract
7
+ extend Scriptorium::Contract
8
+
9
+ attr_accessor :name
10
+
11
+ # Invariants
12
+ def define_invariants
13
+ invariant { @root.is_a?(String) && !@root.empty? }
14
+ invariant { @name.is_a?(String) && !@name.empty? }
15
+ end
16
+
17
+ def self.create_standard(root)
18
+ assume { root.is_a?(String) && !root.empty? }
19
+
20
+ make_tree(root/:themes, <<~EOS)
21
+ standard/
22
+ ├── README.txt
23
+ ├── assets/
24
+ ├── config.txt
25
+ ├── header/
26
+ ├── initial/
27
+ │ └── post.lt3
28
+ ├── layout/
29
+ │ ├── config/
30
+ │ │ ├── footer.txt
31
+ │ │ ├── header.txt
32
+ │ │ ├── left.txt
33
+ │ │ ├── main.txt
34
+ │ │ └── right.txt
35
+ │ ├── gen/
36
+ │ │ └── text.css
37
+ │ └── layout.txt
38
+ └── templates/
39
+ ├── index.lt3
40
+ ├── post.lt3
41
+ ├── index_entry.lt3
42
+ └── widget.lt3
43
+ EOS
44
+ write_file("/tmp/ttree.txt", `tree`)
45
+ predef = Scriptorium::StandardFiles.new
46
+ std = root/:themes/:standard
47
+ write_file(std/:initial/"post.lt3", predef.initial_post(:raw))
48
+ write_file(std/:templates/"post.lt3", predef.post_template("standard"))
49
+ write_file(std/:templates/"index_entry.lt3", predef.index_entry)
50
+ layout_text = std/:layout/"layout.txt"
51
+ write_file(layout_text, predef.layout_text)
52
+ config, gen = std/:layout/:config, std/:layout/:gen
53
+ write_file(config/"header.txt", predef.theme_header)
54
+ write_file(config/"footer.txt", predef.theme_footer)
55
+ write_file(config/"left.txt", predef.theme_left)
56
+ write_file(config/"right.txt", predef.theme_right)
57
+ write_file(config/"main.txt", predef.theme_main)
58
+
59
+ # Copy gem assets to standard theme
60
+ copy_gem_assets_to_theme(std)
61
+
62
+ verify { Dir.exist?(root/:themes/"standard") }
63
+ end
64
+
65
+ def self.copy_gem_assets_to_theme(theme_dir)
66
+ # Try to find gem assets and copy only theme-specific assets to the theme
67
+ begin
68
+ gem_spec = Gem.loaded_specs['scriptorium']
69
+ if gem_spec
70
+ gem_assets_dir = "#{gem_spec.full_gem_path}/assets"
71
+ else
72
+ # Development environment - use the working path
73
+ gem_assets_dir = File.expand_path("assets")
74
+ end
75
+
76
+ if Dir.exist?(gem_assets_dir)
77
+ # Copy only theme-specific assets to theme assets directory
78
+ theme_assets_dir = theme_dir/"assets"
79
+ FileUtils.mkdir_p(theme_assets_dir) unless Dir.exist?(theme_assets_dir)
80
+
81
+ # Copy theme-specific assets (themes/ directory)
82
+ theme_gem_dir = "#{gem_assets_dir}/themes"
83
+ if Dir.exist?(theme_gem_dir)
84
+ FileUtils.cp_r("#{theme_gem_dir}/.", theme_assets_dir)
85
+ end
86
+
87
+ # Copy theme-specific icons (icons/ui/ and icons/social/ - these could be theme-specific)
88
+ icons_gem_dir = "#{gem_assets_dir}/icons"
89
+ if Dir.exist?(icons_gem_dir)
90
+ # Create icons directory in theme assets
91
+ theme_icons_dir = theme_assets_dir/"icons"
92
+ FileUtils.mkdir_p(theme_icons_dir)
93
+
94
+ # Copy UI icons (could be theme-specific)
95
+ ui_icons_dir = "#{icons_gem_dir}/ui"
96
+ if Dir.exist?(ui_icons_dir)
97
+ theme_ui_dir = theme_icons_dir/"ui"
98
+ FileUtils.mkdir_p(theme_ui_dir)
99
+ FileUtils.cp_r("#{ui_icons_dir}/.", theme_ui_dir)
100
+ end
101
+
102
+ # Copy social icons (could be theme-specific)
103
+ social_icons_dir = "#{icons_gem_dir}/social"
104
+ if Dir.exist?(social_icons_dir)
105
+ theme_social_dir = theme_icons_dir/"social"
106
+ FileUtils.mkdir_p(theme_social_dir)
107
+ FileUtils.cp_r("#{social_icons_dir}/.", theme_social_dir)
108
+ end
109
+ end
110
+ end
111
+ rescue => e
112
+ # If gem lookup fails, continue without copying gem assets
113
+ # This is expected in development/testing environments
114
+ end
115
+ end
116
+
117
+ def self.copy_gem_assets_to_library(root)
118
+ # Try to find gem assets and copy application-wide assets to the library
119
+ begin
120
+ gem_spec = Gem.loaded_specs['scriptorium']
121
+ if gem_spec
122
+ gem_assets_dir = "#{gem_spec.full_gem_path}/assets"
123
+ else
124
+ # Development environment - use the working path
125
+ gem_assets_dir = File.expand_path("assets")
126
+ end
127
+
128
+ if Dir.exist?(gem_assets_dir)
129
+ # Copy application-wide assets to library directory
130
+ library_dir = root/"assets"/"library"
131
+ FileUtils.mkdir_p(library_dir) unless Dir.exist?(library_dir)
132
+
133
+ # Copy sample assets (application-wide)
134
+ samples_gem_dir = "#{gem_assets_dir}/samples"
135
+ if Dir.exist?(samples_gem_dir)
136
+ FileUtils.cp_r("#{samples_gem_dir}/.", library_dir)
137
+ end
138
+ end
139
+ rescue => e
140
+ # If gem lookup fails, continue without copying gem assets
141
+ # This is expected in development/testing environments
142
+ end
143
+ end
144
+
145
+ def file(portion)
146
+ check_invariants
147
+ assume { portion.is_a?(String) && !portion.empty? }
148
+
149
+ paths = Find.find(@root/:themes/name)
150
+ found = paths.find_all {|x| x.include?(portion) }
151
+ case
152
+ when found.size == 1
153
+ result = found[0]
154
+ verify { result.is_a?(String) && File.exist?(result) }
155
+ check_invariants
156
+ return result
157
+ when found.size > 1
158
+ # puts "Search for #{portion} found"
159
+ # found.each {|x| puts " #{x}"}
160
+ raise MoreThanOneResult(portion)
161
+ else
162
+ raise ThemeFileNotFound(portion)
163
+ end
164
+ end
165
+
166
+ def initialize(root, name)
167
+ assume { root.is_a?(String) && !root.empty? }
168
+ assume { name.is_a?(String) && !name.empty? }
169
+
170
+ @root = root
171
+ @name = name
172
+
173
+ define_invariants
174
+ verify { @root == root }
175
+ verify { @name == name }
176
+ check_invariants
177
+ end
178
+
179
+ end
@@ -1,5 +1,5 @@
1
- class Scriptorium
1
+ module Scriptorium
2
2
 
3
- VERSION = "0.0.3"
3
+ VERSION = "0.6.1"
4
4
 
5
5
  end