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,515 @@
1
+ class Scriptorium::StandardFiles
2
+
3
+ # This class generates standard file content for new posts, views, etc.
4
+
5
+ include Scriptorium::Helpers
6
+
7
+ def initialize # remove?
8
+ end
9
+
10
+ def html_head_content(view = nil)
11
+ line1global = "# This global file supplies the default values for all views."
12
+ line2view = "# This view-specific file supplies the default values for this view."
13
+ line1 = view ? line2view : line1global
14
+ str = <<~EOS
15
+ #{line1}
16
+ # title is omitted - filled in at generation
17
+ charset UTF-8
18
+ desc A blog powered by Scriptorium. This is default text intended to be changed by the user.
19
+ viewport width=device-width initial-scale=1.0
20
+ robots index follow
21
+ javascript # See common.js
22
+ bootstrap # See bootstrap.txt
23
+ social # See social.txt for configuration
24
+ EOS
25
+ end
26
+
27
+ def common_js
28
+ <<~EOS
29
+ // Handle the back button (or JavaScript history.go(-1))
30
+ window.onpopstate = function(event) {
31
+ console.log('onpopstate event:', event); // Log the event object
32
+ if (event.state && event.state.slug) {
33
+ console.log('Navigating to slug:', event.state.slug); // Log the slug
34
+ load_main(event.state.slug); // Load the post for the previous history state
35
+ }
36
+ };
37
+
38
+ // Initialize with the front page when navigating via the back button or similar
39
+ window.onload = function() {
40
+ // Check if the URL has a post parameter and load it automatically
41
+ const urlParams = new URLSearchParams(window.location.search);
42
+ const postParam = urlParams.get('post');
43
+
44
+ if (postParam) {
45
+ // URL has a post parameter, load the post content
46
+ console.log('Auto-loading post from URL parameter:', postParam);
47
+ load_main('index.html?post=' + postParam);
48
+ return;
49
+ }
50
+
51
+ // Check if the initial state exists, if not, set it
52
+ if (!history.state) {
53
+ // Don't try to load post_index.html if there are no posts
54
+ // The "No posts yet!" message is already in the main container
55
+ history.replaceState({ slug: "index.html" }, "", "index.html");
56
+ }
57
+
58
+
59
+ };
60
+
61
+ // Load the main content and other page containers (header, footer, left, right)
62
+ function load_main(slug) {
63
+ // Get all container elements (header, footer, left, right, and main)
64
+ const contentDiv = document.getElementById("main");
65
+ const headerDiv = document.querySelector("header");
66
+ const footerDiv = document.querySelector("footer");
67
+ const leftDiv = document.querySelector(".left");
68
+ const rightDiv = document.querySelector(".right");
69
+ console.log('Loading main with slug:', slug); // Log the slug
70
+
71
+ // Check if this is a post parameter request
72
+ if (slug.includes('?post=')) {
73
+ const postSlug = slug.split('?post=')[1];
74
+ console.log('Loading post:', postSlug);
75
+
76
+ // Load the post content - add .html extension if it's missing
77
+ const postFile = postSlug.endsWith('.html') ? postSlug : postSlug + '.html';
78
+ fetch('posts/' + postFile)
79
+ .then(response => {
80
+ if (response.ok) {
81
+ return response.text();
82
+ } else {
83
+ console.error('Failed to load post:', response.status);
84
+ return 'Post not found';
85
+ }
86
+ })
87
+ .then(content => {
88
+ contentDiv.innerHTML = content;
89
+ history.pushState({slug: 'index.html?post=' + postSlug}, "", 'index.html?post=' + postSlug);
90
+ })
91
+ .catch(error => {
92
+ console.log("Error loading post:", error);
93
+ contentDiv.innerHTML = 'Error loading post';
94
+ });
95
+ return;
96
+ }
97
+
98
+ // Check if this is a static page request (pages/, assets/, etc.)
99
+ if (slug.startsWith('pages/') || slug.startsWith('assets/') || slug.includes('/')) {
100
+ console.log('Loading static page:', slug);
101
+ // Simple approach: always go back to index.html directory and fetch from there
102
+ fetch(slug)
103
+ .then(response => {
104
+ if (response.ok) {
105
+ return response.text();
106
+ } else {
107
+ console.error('Failed to load static page:', response.status);
108
+ return 'Page not found';
109
+ }
110
+ })
111
+ .then(content => {
112
+ contentDiv.innerHTML = content;
113
+ // Don't change the URL for static pages to avoid path resolution issues
114
+ history.pushState({slug: slug}, "", window.location.pathname);
115
+ })
116
+ .catch(error => {
117
+ console.log("Error loading static page:", error);
118
+ contentDiv.innerHTML = 'Error loading page';
119
+ });
120
+ return;
121
+ }
122
+
123
+ fetch(slug)
124
+ .then(response => {
125
+ if (response.ok) {
126
+ console.log('Response is ok');
127
+ return response.text();
128
+ } else {
129
+ console.error('Failed to load:', response.status); // Log the failed response
130
+ }
131
+ })
132
+ .then(content => {
133
+ console.log('Loaded content into div'); // Log successful content insertion
134
+
135
+ // Now, reload the content into the respective containers:
136
+ // Main section
137
+ contentDiv.innerHTML = content;
138
+
139
+ // Re-insert header, footer, left, and right (if necessary)
140
+ // If you want the static layout to be kept, you can preserve these parts
141
+ // with additional logic or predefined structure (here it's assumed
142
+ // that header/footer/left/right are already statically included).
143
+
144
+ // You can also replace the other parts (left, right, header, footer) if needed.
145
+ history.pushState({slug: slug}, "", slug); // Update browser history
146
+
147
+
148
+ })
149
+ .catch(error => {
150
+ console.log("Error loading content:", error); // Log any errors during fetch
151
+ });
152
+ }
153
+
154
+ // Copy permalink to clipboard functionality
155
+ function copyPermalinkToClipboard() {
156
+ // Get the current post slug from the URL or construct it
157
+ const currentUrl = window.location.href;
158
+ let permalinkUrl;
159
+
160
+ if (currentUrl.includes('?post=')) {
161
+ // We're on the main blog page, construct the permalink URL
162
+ const postSlug = currentUrl.split('?post=')[1];
163
+ const baseUrl = window.location.origin + window.location.pathname.replace(/\/[^\/]*$/, '');
164
+ permalinkUrl = baseUrl + '/permalink/' + postSlug;
165
+ } else {
166
+ // We're already on a permalink page, use current URL
167
+ permalinkUrl = currentUrl;
168
+ }
169
+
170
+ navigator.clipboard.writeText(permalinkUrl).then(function() {
171
+ // Change button text temporarily to show success
172
+ const button = event.target;
173
+ const originalText = button.textContent;
174
+ button.textContent = 'Copied!';
175
+ button.style.background = '#28a745';
176
+ setTimeout(function() {
177
+ button.textContent = originalText;
178
+ button.style.background = '#007bff';
179
+ }, 2000);
180
+ }).catch(function(err) {
181
+ console.error('Failed to copy: ', err);
182
+ alert('Failed to copy link to clipboard');
183
+ });
184
+ }
185
+ EOS
186
+ end
187
+
188
+
189
+ def bootstrap_css
190
+ <<~EOS
191
+ href https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css
192
+ rel stylesheet
193
+ # integrity has a problem - compute instead?
194
+ # integrity sha384-KyZXEJ04F5o1v7V5b7ZMjGhGjxA8yQmBfvZwzI1r+0gEv+9KnQIMJxWwzD0u8nZ7
195
+ # crossorigin anonymous
196
+ EOS
197
+ end
198
+
199
+ def bootstrap_js
200
+ <<~EOS
201
+ src https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js
202
+ # integrity has a problem - compute instead?
203
+ # integrity sha384-pzjw8f+ua7Kw1TIq0+v5+GZkR6P/6v03cI0myXcJU22Hc5p5BY5/X93HmaJXjm4C
204
+ crossorigin anonymous
205
+ EOS
206
+ end
207
+
208
+ def available_widgets
209
+ <<~EOS
210
+ links
211
+ pages
212
+ featuredposts
213
+ EOS
214
+ end
215
+
216
+ def social_config
217
+ <<~EOS
218
+ # Available platforms: facebook twitter linkedin reddit
219
+ # List one platform per line to enable social meta tags
220
+ facebook
221
+ twitter
222
+
223
+ # Note: No Facebook App ID or Twitter username required for basic meta tags
224
+ # These are only needed if you want to add social sharing buttons later
225
+ EOS
226
+ end
227
+
228
+ def reddit_config
229
+ <<~EOS
230
+ # Reddit sharing button configuration
231
+ # Set to true to show Reddit share button on posts
232
+ button true
233
+
234
+ # Optional: specify a subreddit for direct posting
235
+ # Leave empty or omit to let users choose subreddit
236
+ subreddit
237
+
238
+ # Optional: custom hover text (defaults to "Share on Reddit" or "Share on [subreddit]")
239
+ hover_text
240
+ EOS
241
+ end
242
+
243
+ def initial_post(mode = :filled, num: "0", title: nil, blurb: nil, views: nil,
244
+ tags: nil, body: nil)
245
+ # FIXME - screwed up?
246
+ title ||= "ADD TITLE HERE"
247
+ blurb ||= "ADD BLURB HERE"
248
+ views ||= %w[sample]
249
+ tags ||= %w[sample tags]
250
+ body ||= "BEGIN HERE..."
251
+
252
+ str =
253
+ <<~EOS
254
+ . Initial file created by StandardFiles#post_template(num)
255
+
256
+ .id %{num}
257
+ .created %{created}
258
+
259
+ .title %{title}
260
+ .blurb %{blurb}
261
+
262
+ .views %{views}
263
+ .tags %{tags}
264
+
265
+ #{body}
266
+ EOS
267
+
268
+ return str if mode == :raw
269
+ mytags = tags
270
+ mytags = tags.join(", ") if tags.is_a?(Array)
271
+ str2 = str % {num: d4(num.to_i), created: ymdhms, title: title, blurb: blurb,
272
+ views: views.join(" "), tags: mytags}
273
+ return str2
274
+ end
275
+
276
+ def initial_post_content(title: nil, blurb: nil, views: nil, tags: nil, body: nil)
277
+ title ||= "ADD TITLE HERE"
278
+ blurb ||= "ADD BLURB HERE"
279
+ views ||= %w[sample]
280
+ tags ||= %w[sample tags]
281
+ body ||= "BEGIN HERE..."
282
+
283
+ mytags = tags
284
+ mytags = tags.join(", ") if tags.is_a?(Array)
285
+
286
+ <<~EOS
287
+ .title #{title}
288
+ .blurb #{blurb}
289
+ .views #{views.join(" ")}
290
+ .tags #{mytags}
291
+
292
+ #{body}
293
+ EOS
294
+ end
295
+
296
+ def initial_post_metadata(num: "0", title: nil, blurb: nil, views: nil, tags: nil)
297
+ title ||= "ADD TITLE HERE"
298
+ blurb ||= "ADD BLURB HERE"
299
+ views ||= %w[sample]
300
+ tags ||= %w[sample tags]
301
+
302
+ mytags = tags
303
+ mytags = tags.join(", ") if tags.is_a?(Array)
304
+
305
+ <<~EOS
306
+ post.id #{d4(num.to_i)}
307
+ post.created #{ymdhms}
308
+ post.published no
309
+ post.deployed no
310
+ post.title #{title}
311
+ post.blurb #{blurb}
312
+ post.views #{views.join(" ")}
313
+ post.tags #{mytags}
314
+ EOS
315
+ end
316
+
317
+ def post_template(theme)
318
+ str = <<~EOS
319
+ <!-- theme: #{theme} -->
320
+
321
+ <div align='right'><a style="text-decoration: none" href="index.html">
322
+ <img src="assets/back-icon.png" width=24 height=24 alt="Go to Index"></img></a>
323
+ </div>
324
+ <div style="display: flex; justify-content: space-between; align-items: baseline;">
325
+ <span style="text-align: left; font-size: 1.5em;">%{post.title}</span>
326
+ <span style="text-align: right; font-size: 0.9em;">%{reddit_button}%{post.pubdate}</span>
327
+ </div>
328
+ <hr>
329
+ %{post.body}
330
+ <hr>
331
+ <div style="text-align: right; font-size: 0.8em;">%{post.tags}</div>
332
+ <div style="text-align: center; margin-top: 20px;">
333
+ <button onclick="copyPermalinkToClipboard()" style="padding: 8px 16px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer;">Copy link</button>
334
+ </div>
335
+ EOS
336
+ end
337
+
338
+ def layout_text
339
+ <<~EOS
340
+ header # Top (banner? title? navbar? etc.)
341
+ left 15% # Left sidebar, 15% width
342
+ main # Main (center) container - posts/etc.
343
+ right 15% # Right sidebar, 15% width
344
+ footer # Footer (copyright? mail? social media? etc.)
345
+ EOS
346
+ end
347
+
348
+ def post_index_style # Not really a file
349
+ <<~EOS
350
+ <style>
351
+ body { font-family: verdana }
352
+
353
+ .recent-title a {
354
+ color: #010101;
355
+ font-family: verdana;
356
+ font-size: 28px;
357
+ float: right;
358
+ display: inline-block;
359
+ text-align: top;
360
+ text-decoration: none;
361
+ }
362
+
363
+ .recent-title a:hover {
364
+ text-decoration: none;
365
+ }
366
+
367
+ .recent-title-text a {
368
+ color: #0101a1;
369
+ font-family: verdana;
370
+ font-size: 22px;
371
+ display: block;
372
+ text-decoration: none;
373
+ }
374
+
375
+ .recent-title-text a:hover {
376
+ text-decoration: none;
377
+ }
378
+
379
+ .recent-date {
380
+ color: #9a9a9a;
381
+ font-family: verdana;
382
+ font-size: 15px;
383
+ display: block;
384
+ float: left;
385
+ text-align: top;
386
+ }
387
+
388
+ .mydrop {
389
+ color: #444444;
390
+ float: left;
391
+ text-align: top;
392
+ # font-family: Verdana;
393
+ font-size: 38px;
394
+ line-height: 38px;
395
+ # padding-top: 0px;
396
+ padding-right: 8px;
397
+ padding-left: 3px;
398
+ }
399
+
400
+ .thumbnail img {
401
+ max-height: 100%;
402
+ max-width: 100%;
403
+ }
404
+ </style>
405
+ EOS
406
+ end
407
+
408
+ # <!-- posts/%{post.slug}" -->
409
+
410
+ def index_entry
411
+ # Note the use of %% to escape the % in the flex-basis attribute!
412
+ <<~EOS
413
+ <div class="index-entry" style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 20px;">
414
+ <!-- Left Side: Date (right aligned) -->
415
+ <div style="text-align: right; font-size: 0.7em; flex-basis: 10%%; padding-top: 3px;">
416
+ <div>%{post.pubdate.month} %{post.pubdate.day}</div>
417
+ <div>%{post.pubdate.year}</div>
418
+ </div>
419
+ <!-- Right Side: Title and Blurb (left aligned) -->
420
+ <div style="font-size: 1.2em; margin-left: 10px; flex-grow: 1; padding-top: 0;">
421
+ <div><a href="javascript:void(0)"
422
+ style="text-decoration: none;"
423
+ onclick="load_main('index.html?post=%{post.slug}')">%{post.title}</a></div>
424
+ <div style="font-size: 0.9em;">%{post.blurb}</div>
425
+ </div>
426
+ </div>
427
+ EOS
428
+ end
429
+
430
+ def theme_header # Add theme name to these files??
431
+ <<~EOS
432
+ # <!-- Section: header -->
433
+ # Contents of header
434
+ # (may include banner, title, navbar, ...)
435
+ title
436
+ EOS
437
+ end
438
+
439
+ def theme_footer
440
+ <<~EOS
441
+ # <!-- Section: footer -->
442
+ # Contents of footer
443
+ EOS
444
+ end
445
+
446
+ def theme_left
447
+ <<~EOS
448
+ # <!-- Section: left -->
449
+ # Contents of left sidebar
450
+ # (may be widgets or whatever)
451
+ EOS
452
+ end
453
+
454
+ def theme_right
455
+ <<~EOS
456
+ # <!-- Section: right -->
457
+ # Contents of right sidebar
458
+ # (may be widgets or whatever)
459
+ EOS
460
+ end
461
+
462
+ def theme_main
463
+ <<~EOS
464
+ # <!-- Section: main -->
465
+ # Contents of center pane
466
+ # This may be empty, as it is "usually" populated
467
+ # by Javascript (list of recent posts, content from
468
+ # a widget, or whatever)
469
+ EOS
470
+ end
471
+
472
+ def svg_txt
473
+ <<~EOS
474
+ aspect 7.0
475
+ text.font verdana
476
+ title.color #cccccc
477
+ subtitle.color #cccccc
478
+ text.justify left
479
+
480
+ title.scale 0.7
481
+ subtitle.scale 0.3
482
+
483
+ title.style bold
484
+ subtitle.style bold italic
485
+ title.xy 15 60
486
+ subtitle.xy 15 85
487
+
488
+ back.linear #0000cc #000077 lr
489
+
490
+
491
+ EOS
492
+ end
493
+
494
+ def deploy_text
495
+ <<~EOS
496
+ user root
497
+ server %{domain}
498
+ docroot /var/www/html
499
+ path %{view}
500
+ proto https
501
+ EOS
502
+ end
503
+
504
+ def status_txt
505
+ <<~EOS
506
+ header n
507
+ banner n
508
+ navbar n
509
+ left n
510
+ right n
511
+ pages n
512
+ deploy n
513
+ EOS
514
+ end
515
+ end