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.
- checksums.yaml +4 -4
- data/README.lt3 +324 -0
- data/README.md +3155 -1
- data/assets/.DS_Store +0 -0
- data/assets/README.md +44 -0
- data/assets/back-icon.png +0 -0
- data/assets/icons/facebook.svg +1 -0
- data/assets/icons/github.svg +1 -0
- data/assets/icons/instagram.svg +1 -0
- data/assets/icons/reddit.svg +1 -0
- data/assets/icons/ui/.DS_Store +0 -0
- data/assets/icons/ui/back.png +0 -0
- data/assets/icons/ui/copy.png +0 -0
- data/assets/icons/ui/down.png +0 -0
- data/assets/icons/ui/end.png +0 -0
- data/assets/icons/ui/exit.png +0 -0
- data/assets/icons/ui/foo +10 -0
- data/assets/icons/ui/home.png +0 -0
- data/assets/icons/ui/left.png +0 -0
- data/assets/icons/ui/next.png +0 -0
- data/assets/icons/ui/right.png +0 -0
- data/assets/icons/ui/start.png +0 -0
- data/assets/icons/ui/up.png +0 -0
- data/assets/icons/x.svg +1 -0
- data/assets/icons/youtube.svg +1 -0
- data/assets/samples/placeholder.svg +9 -0
- data/assets/themes/standard/favicon.svg +6 -0
- data/bin/scriptorium +1511 -0
- data/doc/README.txt +6 -0
- data/doc/anti-amnesia/20250727-054000-scriptorium-overview.md +95 -0
- data/doc/anti-amnesia/20250727-060000-api-design-tui-planning.md +34 -0
- data/doc/anti-amnesia/20250727-061000-runeblog-tui-analysis.md +50 -0
- data/doc/anti-amnesia/20250727-123000-anti-amnesia-conventions.md +31 -0
- data/doc/anti-amnesia/20250727-154000-livetext-plugin-file-stats.md +73 -0
- data/doc/anti-amnesia/20250727-172600-cursor-rbenv-ruby-version-mystery.md +64 -0
- data/doc/anti-amnesia/20250727-172600-unified-minitest-framework.md +70 -0
- data/doc/anti-amnesia/20250727-172900-ai-cognitive-assessment-capabilities.md +40 -0
- data/doc/anti-amnesia/20250727-173000-widget-testing-achievement.md +110 -0
- data/doc/anti-amnesia/20250727-180000-post-id-num-refactoring.md +73 -0
- data/doc/anti-amnesia/20250728-124243-aaa-syntax-clarification.md +46 -0
- data/doc/anti-amnesia/20250728-124421-conversation-summary-concise.md +124 -0
- data/doc/anti-amnesia/20250729-190000-scriptorium-tui-testing-complete.md +46 -0
- data/doc/anti-amnesia/20250729-200000-scriptorium-tui-testing-edit-file-workflow.md +97 -0
- data/doc/anti-amnesia/20250729-210000-reddit-autopost-integration-complete.md +158 -0
- data/doc/anti-amnesia/20250729-211500-dependency-management-system.md +211 -0
- data/doc/anti-amnesia/20250729-213000-python-virtual-environment-setup.md +141 -0
- data/doc/anti-amnesia/20250729-214500-theme-management-commands.md +211 -0
- data/doc/anti-amnesia/20250729-215000-version-update-to-0.6.0.md +134 -0
- data/doc/anti-amnesia/20250729-220000-user-guide-complete.md +41 -0
- data/doc/anti-amnesia/20250804-190500-cognitive-loop-bug.md +45 -0
- data/doc/anti-amnesia/20250804-190700-anti-amnesia-timestamping-fix.md +30 -0
- data/doc/anti-amnesia/20250804-213700-publishing-test-fix.md +49 -0
- data/doc/anti-amnesia/20250804-214400-additional-test-fixes.md +46 -0
- data/doc/anti-amnesia/20250804-220000-asset-function-logic-clarification.md +41 -0
- data/doc/anti-amnesia/20250806-202032-asset-function-logic-clarification.md +41 -0
- data/doc/anti-amnesia/20250807-213025.md +116 -0
- data/doc/anti-amnesia/20250813-082428-syntax-highlighting-and-navigation-improvements.md +256 -0
- data/doc/banner_svg_config.md +114 -0
- data/doc/contrib.lt3 +8 -0
- data/doc/dependencies.md +281 -0
- data/doc/hacker.lt3 +5 -0
- data/doc/reddit_credentials_template.json +8 -0
- data/doc/reddit_integration.md +207 -0
- data/doc/user.lt3 +38 -0
- data/doc/user_guide_section_1.md +137 -0
- data/doc/user_guide_section_10.md +515 -0
- data/doc/user_guide_section_11.md +708 -0
- data/doc/user_guide_section_2.md +233 -0
- data/doc/user_guide_section_3.md +5 -0
- data/doc/user_guide_section_4.md +221 -0
- data/doc/user_guide_section_5.md +243 -0
- data/doc/user_guide_section_6.md +147 -0
- data/doc/user_guide_section_7.md +311 -0
- data/doc/user_guide_section_8.md +224 -0
- data/doc/user_guide_section_9.md +375 -0
- data/doc/userdoc-toc.txt +88 -0
- data/lib/rouge/lexers/livetext.rb +74 -0
- data/lib/scriptorium/api.rb +640 -0
- data/lib/scriptorium/banner_svg.rb +742 -0
- data/lib/scriptorium/contract.rb +33 -0
- data/lib/scriptorium/exceptions.rb +170 -1
- data/lib/scriptorium/helpers.rb +475 -0
- data/lib/scriptorium/post.rb +195 -0
- data/lib/scriptorium/reddit.rb +83 -0
- data/lib/scriptorium/repo.rb +624 -0
- data/lib/scriptorium/standard_files.rb +515 -0
- data/lib/scriptorium/syntax_highlighter.rb +234 -0
- data/lib/scriptorium/theme.rb +179 -0
- data/lib/scriptorium/version.rb +2 -2
- data/lib/scriptorium/view.rb +976 -0
- data/lib/scriptorium/widgets/featured_posts.rb +149 -0
- data/lib/scriptorium/widgets/links.rb +112 -0
- data/lib/scriptorium/widgets/pages.rb +133 -0
- data/lib/scriptorium/widgets/widget.rb +133 -0
- data/lib/scriptorium.rb +21 -40
- data/lib/skeleton.rb +8 -2
- data/scriptorium.gemspec +15 -4
- data/test/README.md +69 -0
- data/test/all +43 -0
- data/test/api_demo.rb +99 -0
- data/test/assets/imagenotfound.jpg +0 -0
- data/test/assets/images/.DS_Store +0 -0
- data/test/assets/images/README.md +27 -0
- data/test/assets/images/odd_aspect.png +0 -0
- data/test/assets/images/perfect.png +0 -0
- data/test/assets/images/small.png +0 -0
- data/test/assets/images/tall.png +0 -0
- data/test/assets/images/very_tall.png +0 -0
- data/test/assets/images/very_wide.png +0 -0
- data/test/assets/images/wide.png +0 -0
- data/test/assets/testbanner.jpg +0 -0
- data/test/banner_svg/simple_helpers.rb +13 -0
- data/test/banner_svg/unit.rb +768 -0
- data/test/ed_test.rb +204 -0
- data/test/integration/cursor_banner_combinations.rb +193 -0
- data/test/integration/cursor_banner_features.rb +374 -0
- data/test/integration/integration_test.rb +326 -0
- data/test/livetext_plugin_test.rb +229 -0
- data/test/manual/asset_mgmt.rb +67 -0
- data/test/manual/banner-tests/config.txt +3 -0
- data/test/manual/banner-tests/index.html +45 -0
- data/test/manual/banner-tests/test01.html +58 -0
- data/test/manual/banner-tests/test02.html +58 -0
- data/test/manual/banner-tests/test03.html +58 -0
- data/test/manual/banner-tests/test04.html +65 -0
- data/test/manual/banner-tests/test05.html +65 -0
- data/test/manual/banner-tests/test06.html +65 -0
- data/test/manual/banner-tests/test07.html +65 -0
- data/test/manual/banner-tests/test08.html +59 -0
- data/test/manual/banner-tests/test09.html +59 -0
- data/test/manual/banner-tests/test10.html +59 -0
- data/test/manual/banner-tests/test11.html +59 -0
- data/test/manual/banner-tests/test12.html +59 -0
- data/test/manual/banner-tests/test13.html +59 -0
- data/test/manual/banner-tests/test14.html +59 -0
- data/test/manual/banner-tests/test15.html +58 -0
- data/test/manual/banner-tests/test16.html +58 -0
- data/test/manual/banner-tests/test17.html +58 -0
- data/test/manual/banner-tests/test18.html +68 -0
- data/test/manual/banner-tests/test19.html +68 -0
- data/test/manual/banner-tests/test20.html +68 -0
- data/test/manual/banner-tests/test21.html +68 -0
- data/test/manual/banner-tests/test22.html +68 -0
- data/test/manual/banner-tests/test23.html +68 -0
- data/test/manual/banner-tests/test24.html +68 -0
- data/test/manual/banner-tests/test25.html +67 -0
- data/test/manual/banner_environment.rb +192 -0
- data/test/manual/deploy_symlink_demo.rb +142 -0
- data/test/manual/environment.rb +67 -0
- data/test/manual/make_banner.rb +153 -0
- data/test/manual/sample_banner_config.txt +12 -0
- data/test/manual/symlink_demo.rb +117 -0
- data/test/manual/test1.rb +47 -0
- data/test/manual/test2.rb +12 -0
- data/test/manual/test3.rb +38 -0
- data/test/manual/test4.rb +40 -0
- data/test/manual/test5.rb +24 -0
- data/test/manual/test6.rb +73 -0
- data/test/manual/test_banner_combinations.rb +120 -0
- data/test/manual/test_banner_features.rb +306 -0
- data/test/manual/test_banner_from_file.rb +150 -0
- data/test/manual/test_banner_in_header.rb +35 -0
- data/test/manual/test_code_highlighting.rb +68 -0
- data/test/manual/test_complex_header.rb +74 -0
- data/test/manual/test_empty_header.rb +32 -0
- data/test/manual/test_radial_custom.rb +58 -0
- data/test/manual/test_radial_large_radius.rb +52 -0
- data/test/manual/test_svg_debug.rb +47 -0
- data/test/manual/test_syntax_highlighting.rb +147 -0
- data/test/pages-demo/config/currentview.txt +1 -0
- data/test/pages-demo/views/demo/config/bootstrap_css.txt +5 -0
- data/test/pages-demo/views/demo/config/bootstrap_js.txt +4 -0
- data/test/pages-demo/views/demo/config/common.js +57 -0
- data/test/pages-demo/views/demo/config/footer.txt +1 -0
- data/test/pages-demo/views/demo/config/global-head.txt +8 -0
- data/test/pages-demo/views/demo/config/header.txt +1 -0
- data/test/pages-demo/views/demo/config/layout.txt +1 -0
- data/test/pages-demo/views/demo/config/left.txt +1 -0
- data/test/pages-demo/views/demo/config/main.txt +1 -0
- data/test/pages-demo/views/demo/config/right.txt +1 -0
- data/test/pages-demo/views/demo/config.txt +3 -0
- data/test/pages-demo/views/demo/output/panes/footer.html +1 -0
- data/test/pages-demo/views/demo/output/panes/header.html +1 -0
- data/test/pages-demo/views/demo/output/panes/left.html +1 -0
- data/test/pages-demo/views/demo/output/panes/main.html +1 -0
- data/test/pages-demo/views/demo/output/panes/right.html +1 -0
- data/test/rubytext/rubytext_comprehensive_test.rb +307 -0
- data/test/rubytext/rubytext_demo_test.rb +42 -0
- data/test/rubytext/rubytext_testing_guide.md +277 -0
- data/test/run_automated_tests.rb +45 -0
- data/test/scriptorium-TEST-1754622690-146/config/bootstrap_css.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/config/bootstrap_js.txt +4 -0
- data/test/scriptorium-TEST-1754622690-146/config/common.js +57 -0
- data/test/scriptorium-TEST-1754622690-146/config/currentview.txt +1 -0
- data/test/scriptorium-TEST-1754622690-146/config/global-head.txt +9 -0
- data/test/scriptorium-TEST-1754622690-146/config/last_post_num.txt +1 -0
- data/test/scriptorium-TEST-1754622690-146/config/os_helpers.rb +4 -0
- data/test/scriptorium-TEST-1754622690-146/config/widgets.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/posts/0001/meta.txt +8 -0
- data/test/scriptorium-TEST-1754622690-146/posts/0001/source.lt3 +6 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/README.txt +1 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/config.txt +1 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/initial/post.lt3 +12 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/footer.txt +2 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/header.txt +4 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/left.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/main.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/right.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/gen/text.css +1 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/layout.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/index.lt3 +1 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/index_entry.lt3 +14 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/post.lt3 +13 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/widget.lt3 +1 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/bootstrap_css.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/bootstrap_js.txt +4 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/common.js +57 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/deploy.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/footer.txt +2 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/global-head.txt +9 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/header.txt +4 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/layout.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/left.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/main.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/reddit.txt +10 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/right.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/social.txt +7 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/status.txt +7 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/layout/footer.html +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/layout/header.html +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/layout/left.html +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/layout/main.html +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/layout/right.html +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/footer.html +1 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/header.html +1 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/left.html +1 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/main.html +1 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/right.html +1 -0
- data/test/staging/.DS_Store +0 -0
- data/test/syntax_highlighting_test.lt3 +124 -0
- data/test/test_helpers.rb +230 -0
- data/test/tui_editor_integration_test.rb +296 -0
- data/test/tui_integration_test.rb +637 -0
- data/test/unit/api.rb +1056 -0
- data/test/unit/asset_management.rb +245 -0
- data/test/unit/clipboard_test.rb +60 -0
- data/test/unit/contract_test.rb +91 -0
- data/test/unit/core.rb +857 -0
- data/test/unit/deploy_test.rb +187 -0
- data/test/unit/gem_asset_management.rb +189 -0
- data/test/unit/livetext_basic.rb +69 -0
- data/test/unit/livetext_compatibility.rb +89 -0
- data/test/unit/post.rb +244 -0
- data/test/unit/read_commented_file_test.rb +276 -0
- data/test/unit/reddit_test.rb +235 -0
- data/test/unit/repo.rb +548 -0
- data/test/unit/social_test.rb +369 -0
- data/test/unit/symlink_test.rb +213 -0
- data/test/unit/view.rb +431 -0
- data/test/unit/widgets.rb +669 -0
- data/test/wizard_test.rb +123 -0
- data/ui/README.md +67 -0
- data/ui/common/lib/ui_common.rb +8 -0
- data/ui/rubytext/README.md +191 -0
- data/ui/rubytext/bin/scriptorium-rubytext +402 -0
- data/ui/rubytext/lib/rubytext_ui.rb +300 -0
- data/ui/tui/bin/scriptorium +1420 -0
- data/ui/tui/test/tui_test.rb +23 -0
- data/ui/web/app/app.rb +1378 -0
- data/ui/web/app/error_helpers.rb +150 -0
- data/ui/web/app/views/advanced_config.erb +190 -0
- data/ui/web/app/views/asset_management.erb +589 -0
- data/ui/web/app/views/banner_config.erb +200 -0
- data/ui/web/app/views/configure_view.erb +401 -0
- data/ui/web/app/views/dashboard.erb +162 -0
- data/ui/web/app/views/deploy_config.erb +146 -0
- data/ui/web/app/views/edit_pages.erb +195 -0
- data/ui/web/app/views/edit_post.erb +54 -0
- data/ui/web/app/views/error_page.erb +29 -0
- data/ui/web/app/views/header_config.erb +155 -0
- data/ui/web/app/views/layout_config.erb +147 -0
- data/ui/web/app/views/navbar_config.erb +411 -0
- data/ui/web/app/views/view_dashboard.erb +138 -0
- data/ui/web/bin/scriptorium-web +153 -0
- data/ui/web/test/web_basic_test.rb +38 -0
- data/ui/web/test_navbar.txt +7 -0
- data/ui/web/tmp/web_server.log +5 -0
- data/ui/web/tmp/web_server.pid +1 -0
- metadata +359 -7
- data/lib/scriptorium/engine.rb +0 -22
- data/test/engine/unit.rb +0 -44
@@ -0,0 +1,200 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Banner Configuration - Scriptorium</title>
|
5
|
+
<style>
|
6
|
+
body {
|
7
|
+
font-family: Arial, sans-serif;
|
8
|
+
margin: 0;
|
9
|
+
padding: 20px;
|
10
|
+
background: #f5f5f5;
|
11
|
+
}
|
12
|
+
.container {
|
13
|
+
max-width: 1200px;
|
14
|
+
margin: 0 auto;
|
15
|
+
background: white;
|
16
|
+
border-radius: 8px;
|
17
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
18
|
+
overflow: hidden;
|
19
|
+
}
|
20
|
+
.header {
|
21
|
+
background: #007cba;
|
22
|
+
color: white;
|
23
|
+
padding: 20px;
|
24
|
+
margin: 0;
|
25
|
+
}
|
26
|
+
.content {
|
27
|
+
padding: 20px;
|
28
|
+
}
|
29
|
+
.banner-preview {
|
30
|
+
margin-bottom: 30px;
|
31
|
+
border: 2px solid #ddd;
|
32
|
+
border-radius: 8px;
|
33
|
+
overflow: hidden;
|
34
|
+
}
|
35
|
+
.banner-preview h3 {
|
36
|
+
margin: 0;
|
37
|
+
padding: 15px;
|
38
|
+
background: #f8f9fa;
|
39
|
+
border-bottom: 1px solid #ddd;
|
40
|
+
}
|
41
|
+
.banner-svg {
|
42
|
+
width: 100%;
|
43
|
+
max-width: 800px;
|
44
|
+
margin: 0 auto;
|
45
|
+
display: block;
|
46
|
+
}
|
47
|
+
.config-section {
|
48
|
+
margin-bottom: 30px;
|
49
|
+
}
|
50
|
+
.config-section h3 {
|
51
|
+
margin-top: 0;
|
52
|
+
color: #333;
|
53
|
+
}
|
54
|
+
.config-textarea {
|
55
|
+
width: 100%;
|
56
|
+
height: 400px;
|
57
|
+
font-family: monospace;
|
58
|
+
font-size: 14px;
|
59
|
+
padding: 15px;
|
60
|
+
border: 1px solid #ddd;
|
61
|
+
border-radius: 4px;
|
62
|
+
resize: vertical;
|
63
|
+
}
|
64
|
+
.button-group {
|
65
|
+
margin-top: 20px;
|
66
|
+
}
|
67
|
+
.btn {
|
68
|
+
padding: 10px 20px;
|
69
|
+
border: none;
|
70
|
+
border-radius: 4px;
|
71
|
+
cursor: pointer;
|
72
|
+
font-size: 14px;
|
73
|
+
margin-right: 10px;
|
74
|
+
}
|
75
|
+
.btn-primary {
|
76
|
+
background: #007cba;
|
77
|
+
color: white;
|
78
|
+
}
|
79
|
+
.btn-secondary {
|
80
|
+
background: #6c757d;
|
81
|
+
color: white;
|
82
|
+
}
|
83
|
+
.btn:hover {
|
84
|
+
opacity: 0.8;
|
85
|
+
}
|
86
|
+
.message {
|
87
|
+
padding: 15px;
|
88
|
+
margin-bottom: 20px;
|
89
|
+
border-radius: 4px;
|
90
|
+
}
|
91
|
+
.message.success {
|
92
|
+
background: #d4edda;
|
93
|
+
color: #155724;
|
94
|
+
border: 1px solid #c3e6cb;
|
95
|
+
}
|
96
|
+
.message.error {
|
97
|
+
background: #f8d7da;
|
98
|
+
color: #721c24;
|
99
|
+
border: 1px solid #f5c6cb;
|
100
|
+
}
|
101
|
+
.help-text {
|
102
|
+
background: #e8f4fd;
|
103
|
+
padding: 20px;
|
104
|
+
border-radius: 8px;
|
105
|
+
margin-bottom: 30px;
|
106
|
+
font-size: 14px;
|
107
|
+
border-left: 4px solid #007cba;
|
108
|
+
}
|
109
|
+
.help-text h4 {
|
110
|
+
margin-top: 0;
|
111
|
+
margin-bottom: 15px;
|
112
|
+
color: #333;
|
113
|
+
font-size: 16px;
|
114
|
+
}
|
115
|
+
.help-text p {
|
116
|
+
margin-bottom: 15px;
|
117
|
+
line-height: 1.5;
|
118
|
+
}
|
119
|
+
.help-text table {
|
120
|
+
border-collapse: collapse;
|
121
|
+
}
|
122
|
+
.help-text td {
|
123
|
+
padding: 2px 0;
|
124
|
+
vertical-align: top;
|
125
|
+
}
|
126
|
+
.help-text td:first-child {
|
127
|
+
padding-right: 15px;
|
128
|
+
white-space: nowrap;
|
129
|
+
width: 1%;
|
130
|
+
}
|
131
|
+
.help-text code {
|
132
|
+
background: #f8f9fa;
|
133
|
+
padding: 3px 6px;
|
134
|
+
border-radius: 4px;
|
135
|
+
font-family: monospace;
|
136
|
+
font-size: 13px;
|
137
|
+
border: 1px solid #e9ecef;
|
138
|
+
}
|
139
|
+
</style>
|
140
|
+
</head>
|
141
|
+
<body>
|
142
|
+
<div class="container">
|
143
|
+
<div class="header">
|
144
|
+
<h1>Banner Configuration</h1>
|
145
|
+
<p>Configure the SVG banner for view: <strong><%= @current_view.name %></strong></p>
|
146
|
+
</div>
|
147
|
+
|
148
|
+
<div class="content">
|
149
|
+
<% if params[:message] %>
|
150
|
+
<div class="message success">
|
151
|
+
<%= params[:message] %>
|
152
|
+
</div>
|
153
|
+
<% end %>
|
154
|
+
|
155
|
+
<% if params[:error] %>
|
156
|
+
<div class="message error">
|
157
|
+
<%= params[:error] %>
|
158
|
+
</div>
|
159
|
+
<% end %>
|
160
|
+
|
161
|
+
<div class="banner-preview">
|
162
|
+
<h3>Current Banner Preview</h3>
|
163
|
+
<div class="banner-svg">
|
164
|
+
<%= @banner_svg %>
|
165
|
+
</div>
|
166
|
+
</div>
|
167
|
+
|
168
|
+
<div class="help-text">
|
169
|
+
<h4>Banner Configuration Help</h4>
|
170
|
+
<p>Configure your banner using these options:</p>
|
171
|
+
<table>
|
172
|
+
<tr><td><code>aspect 7.0</code></td><td>Aspect ratio (width:height)</td></tr>
|
173
|
+
<tr><td><code>text.font verdana</code></td><td>Font family</td></tr>
|
174
|
+
<tr><td><code>title.color #cccccc</code></td><td>Title color</td></tr>
|
175
|
+
<tr><td><code>subtitle.color #cccccc</code></td><td>Subtitle color</td></tr>
|
176
|
+
<tr><td><code>title.scale 0.7</code></td><td>Title size multiplier</td></tr>
|
177
|
+
<tr><td><code>subtitle.scale 0.3</code></td><td>Subtitle size multiplier</td></tr>
|
178
|
+
<tr><td><code>title.style bold</code></td><td>Title style (bold, italic, etc.)</td></tr>
|
179
|
+
<tr><td><code>subtitle.style bold italic</code></td><td>Subtitle style</td></tr>
|
180
|
+
<tr><td><code>title.xy 15 60</code></td><td>Title position (x y)</td></tr>
|
181
|
+
<tr><td><code>subtitle.xy 15 85</code></td><td>Subtitle position (x y)</td></tr>
|
182
|
+
<tr><td><code>back.linear #0000cc #000077 lr</code></td><td>Linear gradient background</td></tr>
|
183
|
+
</table>
|
184
|
+
</div>
|
185
|
+
|
186
|
+
<form method="post" action="/banner_config">
|
187
|
+
<div class="config-section">
|
188
|
+
<h3>SVG Configuration</h3>
|
189
|
+
<textarea name="svg_config" class="config-textarea" placeholder="Enter your banner configuration here..."><%= @svg_config %></textarea>
|
190
|
+
</div>
|
191
|
+
|
192
|
+
<div class="button-group">
|
193
|
+
<button type="submit" class="btn btn-primary">Update Banner</button>
|
194
|
+
<a href="/" class="btn btn-secondary">Back to Dashboard</a>
|
195
|
+
</div>
|
196
|
+
</form>
|
197
|
+
</div>
|
198
|
+
</div>
|
199
|
+
</body>
|
200
|
+
</html>
|
@@ -0,0 +1,401 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Configure View - Scriptorium Web UI</title>
|
5
|
+
<style>
|
6
|
+
body { font-family: Arial, sans-serif; margin: 20px; }
|
7
|
+
.header { border-bottom: 1px solid #ccc; padding-bottom: 10px; margin-bottom: 20px; }
|
8
|
+
.status { padding: 8px; margin: 8px 0; border-radius: 3px; }
|
9
|
+
.error { background-color: #ffebee; color: #c62828; border: 1px solid #ffcdd2; }
|
10
|
+
.success { background-color: #e8f5e8; color: #2e7d32; border: 1px solid #c8e6c9; }
|
11
|
+
.button { background: #007cba; color: white; padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer; margin-right: 10px; }
|
12
|
+
.button:hover { background: #005a87; }
|
13
|
+
.button.secondary { background: #6c757d; }
|
14
|
+
.button.secondary:hover { background: #545b62; }
|
15
|
+
.button:disabled { background: #ccc; cursor: not-allowed; }
|
16
|
+
.section { margin: 20px 0; }
|
17
|
+
.section h2 { margin: 0 0 15px 0; font-size: 18px; }
|
18
|
+
textarea { width: 100%; height: 200px; font-family: 'Courier New', monospace; font-size: 14px; padding: 10px; border: 1px solid #ddd; border-radius: 4px; }
|
19
|
+
input[type="text"], select { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; margin-bottom: 10px; }
|
20
|
+
.view-info { background: #f8f9fa; padding: 15px; border-radius: 4px; margin-bottom: 20px; }
|
21
|
+
.view-info h2 { margin: 0 0 10px 0; }
|
22
|
+
.view-info p { margin: 5px 0; }
|
23
|
+
|
24
|
+
/* Step Navigation */
|
25
|
+
.step-nav { display: flex; margin-bottom: 30px; border-bottom: 2px solid #eee; }
|
26
|
+
.step { flex: 1; text-align: center; padding: 15px; cursor: pointer; border-bottom: 3px solid transparent; }
|
27
|
+
.step.active { border-bottom-color: #007cba; background: #f0f8ff; }
|
28
|
+
.step.completed { border-bottom-color: #28a745; }
|
29
|
+
.step-number { display: inline-block; width: 30px; height: 30px; line-height: 30px; border-radius: 50%; background: #ccc; color: white; margin-right: 10px; }
|
30
|
+
.step.active .step-number { background: #007cba; }
|
31
|
+
.step.completed .step-number { background: #28a745; }
|
32
|
+
|
33
|
+
/* Step Content */
|
34
|
+
.step-content { display: none; }
|
35
|
+
.step-content.active { display: block; }
|
36
|
+
|
37
|
+
/* Layout Builder */
|
38
|
+
.layout-builder { border: 1px solid #ddd; padding: 20px; border-radius: 4px; }
|
39
|
+
.container-option { margin: 10px 0; padding: 10px; border: 1px solid #eee; border-radius: 4px; }
|
40
|
+
.container-option label { display: flex; align-items: center; cursor: pointer; }
|
41
|
+
.container-option input[type="checkbox"] { margin-right: 10px; }
|
42
|
+
.container-option input[type="text"] { width: 80px; margin-left: 10px; }
|
43
|
+
.layout-preview { margin-top: 20px; padding: 15px; background: #f8f9fa; border-radius: 4px; }
|
44
|
+
.layout-preview-grid { display: grid; gap: 5px; }
|
45
|
+
.layout-preview-grid.header { grid-template-columns: 1fr; }
|
46
|
+
.layout-preview-grid.body { grid-template-columns: 1fr 2fr 1fr; }
|
47
|
+
.layout-preview-grid.footer { grid-template-columns: 1fr; }
|
48
|
+
.layout-preview-cell { padding: 10px; background: #e9ecef; border-radius: 3px; text-align: center; font-size: 12px; }
|
49
|
+
|
50
|
+
/* Container Tabs */
|
51
|
+
.container-tabs { border-bottom: 1px solid #ddd; margin-bottom: 20px; }
|
52
|
+
.container-tab { display: inline-block; padding: 10px 20px; cursor: pointer; border: 1px solid transparent; border-bottom: none; margin-bottom: -1px; }
|
53
|
+
.container-tab.active { background: white; border-color: #ddd; border-radius: 4px 4px 0 0; }
|
54
|
+
.container-content { display: none; }
|
55
|
+
.container-content.active { display: block; }
|
56
|
+
|
57
|
+
/* Form Navigation */
|
58
|
+
.form-nav { margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee; }
|
59
|
+
.form-nav .button { margin-right: 10px; }
|
60
|
+
</style>
|
61
|
+
</head>
|
62
|
+
<body>
|
63
|
+
<div class="header">
|
64
|
+
<h1>Configure View: <%= @view.name %></h1>
|
65
|
+
<p>Step-by-step view configuration wizard</p>
|
66
|
+
</div>
|
67
|
+
|
68
|
+
<% if params[:error] %>
|
69
|
+
<div class="status error">
|
70
|
+
<strong>Error:</strong> <%= params[:error] %>
|
71
|
+
<% if params[:suggestion] %>
|
72
|
+
<br><em>Suggestion:</em> <%= params[:suggestion] %>
|
73
|
+
<% end %>
|
74
|
+
</div>
|
75
|
+
<% end %>
|
76
|
+
|
77
|
+
<div class="view-info">
|
78
|
+
<h2><%= @view.title %></h2>
|
79
|
+
<p><strong>Name:</strong> <%= @view.name %></p>
|
80
|
+
<p><strong>Theme:</strong> <%= @view.theme %></p>
|
81
|
+
<p><strong>Path:</strong> views/<%= @view.name %></p>
|
82
|
+
</div>
|
83
|
+
|
84
|
+
<!-- Step Navigation -->
|
85
|
+
<div class="step-nav">
|
86
|
+
<div class="step active" onclick="showStep(1)">
|
87
|
+
<span class="step-number">1</span>
|
88
|
+
<span>Basic Info</span>
|
89
|
+
</div>
|
90
|
+
<div class="step" onclick="showStep(2)">
|
91
|
+
<span class="step-number">2</span>
|
92
|
+
<span>Layout</span>
|
93
|
+
</div>
|
94
|
+
<div class="step" onclick="showStep(3)">
|
95
|
+
<span class="step-number">3</span>
|
96
|
+
<span>Containers</span>
|
97
|
+
</div>
|
98
|
+
</div>
|
99
|
+
|
100
|
+
<form method="post" action="/save_view_config/<%= @view.name %>" id="configForm">
|
101
|
+
<!-- Step 1: Basic Information -->
|
102
|
+
<div id="step1" class="step-content active">
|
103
|
+
<div class="section">
|
104
|
+
<h2>Basic View Information</h2>
|
105
|
+
<p>Configure the basic properties of your view.</p>
|
106
|
+
|
107
|
+
<label for="view_title">View Title:</label>
|
108
|
+
<input type="text" id="view_title" name="view_title" value="<%= @view.title %>" required>
|
109
|
+
|
110
|
+
<label for="view_subtitle">Subtitle:</label>
|
111
|
+
<input type="text" id="view_subtitle" name="view_subtitle" value="<%= @view.subtitle %>">
|
112
|
+
|
113
|
+
<label for="view_theme">Theme:</label>
|
114
|
+
<select id="view_theme" name="view_theme">
|
115
|
+
<option value="standard" <%= 'selected' if @view.theme == 'standard' %>>Standard</option>
|
116
|
+
<!-- Add more themes as they become available -->
|
117
|
+
</select>
|
118
|
+
</div>
|
119
|
+
</div>
|
120
|
+
|
121
|
+
<!-- Step 2: Layout Definition -->
|
122
|
+
<div id="step2" class="step-content">
|
123
|
+
<div class="section">
|
124
|
+
<h2>Layout Definition</h2>
|
125
|
+
<p>Choose which containers to include in your layout and set their properties.</p>
|
126
|
+
|
127
|
+
<div class="layout-builder">
|
128
|
+
<div class="container-option">
|
129
|
+
<label>
|
130
|
+
<input type="checkbox" id="container_header" name="containers[]" value="header" checked>
|
131
|
+
Header (top section for banner, title, navigation)
|
132
|
+
</label>
|
133
|
+
</div>
|
134
|
+
|
135
|
+
<div class="container-option">
|
136
|
+
<label>
|
137
|
+
<input type="checkbox" id="container_left" name="containers[]" value="left">
|
138
|
+
Left Sidebar
|
139
|
+
<input type="text" id="left_width" name="left_width" value="15%" placeholder="15%">
|
140
|
+
</label>
|
141
|
+
</div>
|
142
|
+
|
143
|
+
<div class="container-option">
|
144
|
+
<label>
|
145
|
+
<input type="checkbox" id="container_main" name="containers[]" value="main" checked>
|
146
|
+
Main Content (posts, content)
|
147
|
+
</label>
|
148
|
+
</div>
|
149
|
+
|
150
|
+
<div class="container-option">
|
151
|
+
<label>
|
152
|
+
<input type="checkbox" id="container_right" name="containers[]" value="right">
|
153
|
+
Right Sidebar
|
154
|
+
<input type="text" id="right_width" name="right_width" value="15%" placeholder="15%">
|
155
|
+
</label>
|
156
|
+
</div>
|
157
|
+
|
158
|
+
<div class="container-option">
|
159
|
+
<label>
|
160
|
+
<input type="checkbox" id="container_footer" name="containers[]" value="footer" checked>
|
161
|
+
Footer (bottom section for copyright, links)
|
162
|
+
</label>
|
163
|
+
</div>
|
164
|
+
</div>
|
165
|
+
|
166
|
+
<div class="layout-preview">
|
167
|
+
<h3>Layout Preview</h3>
|
168
|
+
<div id="layoutPreview">
|
169
|
+
<!-- Dynamic preview will be generated here -->
|
170
|
+
</div>
|
171
|
+
</div>
|
172
|
+
</div>
|
173
|
+
</div>
|
174
|
+
|
175
|
+
<!-- Step 3: Container Configuration -->
|
176
|
+
<div id="step3" class="step-content">
|
177
|
+
<div class="section">
|
178
|
+
<h2>Container Configuration</h2>
|
179
|
+
<p>Configure the content for each container in your layout.</p>
|
180
|
+
|
181
|
+
<div class="container-tabs" id="containerTabs">
|
182
|
+
<!-- Dynamic tabs will be generated here -->
|
183
|
+
</div>
|
184
|
+
|
185
|
+
<div id="containerContents">
|
186
|
+
<!-- Dynamic container content will be generated here -->
|
187
|
+
</div>
|
188
|
+
</div>
|
189
|
+
</div>
|
190
|
+
|
191
|
+
<!-- Form Navigation -->
|
192
|
+
<div class="form-nav">
|
193
|
+
<button type="button" class="button secondary" onclick="previousStep()" id="prevBtn" style="display: none;">Previous</button>
|
194
|
+
<button type="button" class="button" onclick="nextStep()" id="nextBtn">Next</button>
|
195
|
+
<button type="submit" class="button" id="saveBtn" style="display: none;">Save Configuration</button>
|
196
|
+
<a href="/" class="button secondary" style="text-decoration: none;">Cancel</a>
|
197
|
+
</div>
|
198
|
+
</form>
|
199
|
+
|
200
|
+
<script>
|
201
|
+
let currentStep = 1;
|
202
|
+
const totalSteps = 3;
|
203
|
+
|
204
|
+
function showStep(step) {
|
205
|
+
// Hide all step contents
|
206
|
+
for (let i = 1; i <= totalSteps; i++) {
|
207
|
+
document.getElementById(`step${i}`).classList.remove('active');
|
208
|
+
document.querySelector(`.step:nth-child(${i})`).classList.remove('active');
|
209
|
+
}
|
210
|
+
|
211
|
+
// Show selected step
|
212
|
+
document.getElementById(`step${step}`).classList.add('active');
|
213
|
+
document.querySelector(`.step:nth-child(${step})`).classList.add('active');
|
214
|
+
|
215
|
+
currentStep = step;
|
216
|
+
updateNavigation();
|
217
|
+
|
218
|
+
if (step === 2) {
|
219
|
+
updateLayoutPreview();
|
220
|
+
} else if (step === 3) {
|
221
|
+
generateContainerTabs();
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
function nextStep() {
|
226
|
+
if (currentStep < totalSteps) {
|
227
|
+
showStep(currentStep + 1);
|
228
|
+
}
|
229
|
+
}
|
230
|
+
|
231
|
+
function previousStep() {
|
232
|
+
if (currentStep > 1) {
|
233
|
+
showStep(currentStep - 1);
|
234
|
+
}
|
235
|
+
}
|
236
|
+
|
237
|
+
function updateNavigation() {
|
238
|
+
const prevBtn = document.getElementById('prevBtn');
|
239
|
+
const nextBtn = document.getElementById('nextBtn');
|
240
|
+
const saveBtn = document.getElementById('saveBtn');
|
241
|
+
|
242
|
+
prevBtn.style.display = currentStep > 1 ? 'inline-block' : 'none';
|
243
|
+
nextBtn.style.display = currentStep < totalSteps ? 'inline-block' : 'none';
|
244
|
+
saveBtn.style.display = currentStep === totalSteps ? 'inline-block' : 'none';
|
245
|
+
}
|
246
|
+
|
247
|
+
function updateLayoutPreview() {
|
248
|
+
const containers = [];
|
249
|
+
const checkboxes = document.querySelectorAll('input[name="containers[]"]:checked');
|
250
|
+
|
251
|
+
checkboxes.forEach(checkbox => {
|
252
|
+
const container = checkbox.value;
|
253
|
+
let width = '';
|
254
|
+
|
255
|
+
if (container === 'left') {
|
256
|
+
width = document.getElementById('left_width').value;
|
257
|
+
} else if (container === 'right') {
|
258
|
+
width = document.getElementById('right_width').value;
|
259
|
+
}
|
260
|
+
|
261
|
+
containers.push({ name: container, width: width });
|
262
|
+
});
|
263
|
+
|
264
|
+
const preview = document.getElementById('layoutPreview');
|
265
|
+
let html = '';
|
266
|
+
|
267
|
+
// Header
|
268
|
+
const header = containers.find(c => c.name === 'header');
|
269
|
+
if (header) {
|
270
|
+
html += '<div class="layout-preview-grid header"><div class="layout-preview-cell">Header</div></div>';
|
271
|
+
}
|
272
|
+
|
273
|
+
// Body (left, main, right)
|
274
|
+
const bodyContainers = containers.filter(c => ['left', 'main', 'right'].includes(c.name));
|
275
|
+
if (bodyContainers.length > 0) {
|
276
|
+
const columns = bodyContainers.map(c => {
|
277
|
+
if (c.name === 'left' || c.name === 'right') {
|
278
|
+
return c.width || '1fr';
|
279
|
+
}
|
280
|
+
return '2fr'; // main gets more space
|
281
|
+
}).join(' ');
|
282
|
+
|
283
|
+
html += `<div class="layout-preview-grid body" style="grid-template-columns: ${columns}">`;
|
284
|
+
bodyContainers.forEach(c => {
|
285
|
+
html += `<div class="layout-preview-cell">${c.name.charAt(0).toUpperCase() + c.name.slice(1)}</div>`;
|
286
|
+
});
|
287
|
+
html += '</div>';
|
288
|
+
}
|
289
|
+
|
290
|
+
// Footer
|
291
|
+
const footer = containers.find(c => c.name === 'footer');
|
292
|
+
if (footer) {
|
293
|
+
html += '<div class="layout-preview-grid footer"><div class="layout-preview-cell">Footer</div></div>';
|
294
|
+
}
|
295
|
+
|
296
|
+
preview.innerHTML = html;
|
297
|
+
}
|
298
|
+
|
299
|
+
function generateContainerTabs() {
|
300
|
+
const containers = [];
|
301
|
+
const checkboxes = document.querySelectorAll('input[name="containers[]"]:checked');
|
302
|
+
|
303
|
+
checkboxes.forEach(checkbox => {
|
304
|
+
containers.push(checkbox.value);
|
305
|
+
});
|
306
|
+
|
307
|
+
const tabsContainer = document.getElementById('containerTabs');
|
308
|
+
const contentsContainer = document.getElementById('containerContents');
|
309
|
+
|
310
|
+
// Generate tabs
|
311
|
+
let tabsHtml = '';
|
312
|
+
let contentsHtml = '';
|
313
|
+
|
314
|
+
containers.forEach((container, index) => {
|
315
|
+
const isActive = index === 0 ? 'active' : '';
|
316
|
+
const containerName = container.charAt(0).toUpperCase() + container.slice(1);
|
317
|
+
|
318
|
+
tabsHtml += `<div class="container-tab ${isActive}" onclick="showContainerTab('${container}')">${containerName}</div>`;
|
319
|
+
contentsHtml += `<div id="container_${container}" class="container-content ${isActive}">`;
|
320
|
+
contentsHtml += generateContainerContent(container);
|
321
|
+
contentsHtml += '</div>';
|
322
|
+
});
|
323
|
+
|
324
|
+
tabsContainer.innerHTML = tabsHtml;
|
325
|
+
contentsContainer.innerHTML = contentsHtml;
|
326
|
+
}
|
327
|
+
|
328
|
+
function showContainerTab(containerName) {
|
329
|
+
// Hide all container contents
|
330
|
+
const contents = document.querySelectorAll('.container-content');
|
331
|
+
contents.forEach(content => content.classList.remove('active'));
|
332
|
+
|
333
|
+
// Remove active class from all tabs
|
334
|
+
const tabs = document.querySelectorAll('.container-tab');
|
335
|
+
tabs.forEach(tab => tab.classList.remove('active'));
|
336
|
+
|
337
|
+
// Show selected container content
|
338
|
+
document.getElementById(`container_${containerName}`).classList.add('active');
|
339
|
+
|
340
|
+
// Add active class to clicked tab
|
341
|
+
event.target.classList.add('active');
|
342
|
+
}
|
343
|
+
|
344
|
+
function generateContainerContent(container) {
|
345
|
+
switch (container) {
|
346
|
+
case 'header':
|
347
|
+
return `
|
348
|
+
<h3>Header Configuration</h3>
|
349
|
+
<p>Configure the header section (banner, title, navigation).</p>
|
350
|
+
<label for="header_content">Header Content:</label>
|
351
|
+
<textarea name="header_content" placeholder="Enter header configuration (e.g., 'banner svg', 'title', 'title subtitle')...">banner svg</textarea>
|
352
|
+
<small class="form-text text-muted">Note: When using 'banner svg', the title and subtitle are automatically included from the view settings.</small>
|
353
|
+
`;
|
354
|
+
case 'main':
|
355
|
+
return `
|
356
|
+
<h3>Main Content Configuration</h3>
|
357
|
+
<p>Configure the main content area (usually populated by JavaScript with posts).</p>
|
358
|
+
<label for="main_content">Main Content:</label>
|
359
|
+
<textarea name="main_content" placeholder="Enter main content configuration..."># Main content (usually populated by JavaScript)</textarea>
|
360
|
+
`;
|
361
|
+
case 'left':
|
362
|
+
return `
|
363
|
+
<h3>Left Sidebar Configuration</h3>
|
364
|
+
<p>Configure the left sidebar content.</p>
|
365
|
+
<label for="left_content">Left Sidebar Content:</label>
|
366
|
+
<textarea name="left_content" placeholder="Enter left sidebar configuration..."># Left sidebar content</textarea>
|
367
|
+
`;
|
368
|
+
case 'right':
|
369
|
+
return `
|
370
|
+
<h3>Right Sidebar Configuration</h3>
|
371
|
+
<p>Configure the right sidebar content.</p>
|
372
|
+
<label for="right_content">Right Sidebar Content:</label>
|
373
|
+
<textarea name="right_content" placeholder="Enter right sidebar configuration..."># Right sidebar content</textarea>
|
374
|
+
`;
|
375
|
+
case 'footer':
|
376
|
+
return `
|
377
|
+
<h3>Footer Configuration</h3>
|
378
|
+
<p>Configure the footer section (copyright, links, etc.).</p>
|
379
|
+
<label for="footer_content">Footer Content:</label>
|
380
|
+
<textarea name="footer_content" placeholder="Enter footer configuration..."># Footer content</textarea>
|
381
|
+
`;
|
382
|
+
default:
|
383
|
+
return `<p>Configuration for ${container} container.</p>`;
|
384
|
+
}
|
385
|
+
}
|
386
|
+
|
387
|
+
// Event listeners for layout changes
|
388
|
+
document.addEventListener('DOMContentLoaded', function() {
|
389
|
+
const checkboxes = document.querySelectorAll('input[name="containers[]"]');
|
390
|
+
checkboxes.forEach(checkbox => {
|
391
|
+
checkbox.addEventListener('change', updateLayoutPreview);
|
392
|
+
});
|
393
|
+
|
394
|
+
const widthInputs = document.querySelectorAll('input[id$="_width"]');
|
395
|
+
widthInputs.forEach(input => {
|
396
|
+
input.addEventListener('input', updateLayoutPreview);
|
397
|
+
});
|
398
|
+
});
|
399
|
+
</script>
|
400
|
+
</body>
|
401
|
+
</html>
|