A WhiteWind blog to Leaflet publication conversion tool

more refinement.

ewancroft.uk 81c2de57 caa9502b

verified
Changed files
+84 -28
.vscode
+1
.vscode/settings.json
··· 9 9 "colours", 10 10 "Customise", 11 11 "donotpresent", 12 + "Ewan", 12 13 "fseventsd", 13 14 "icns", 14 15 "myblog",
+42 -19
index.html
··· 1 1 <!DOCTYPE html> 2 2 <html lang="en"> 3 + 3 4 <head> 4 5 <meta charset="UTF-8"> 5 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> ··· 7 8 <link rel="stylesheet" href="./variables.css"> 8 9 <link rel="stylesheet" href="./styles.css"> 9 10 </head> 11 + 12 + <header> 13 + <h1>๐Ÿƒ WhiteWind โ†’ Leaflet Converter</h1> 14 + <p>Convert your WhiteWind blog entries to Leaflet publication format</p> 15 + </header> 16 + 10 17 <body> 11 18 <div class="container"> 12 - <header class="header"> 13 - <h1>๐Ÿƒ WhiteWind โ†’ Leaflet Converter</h1> 14 - <p>Convert your WhiteWind blog entries to Leaflet publication format</p> 15 - </header> 16 - 17 19 <main class="main-content"> 18 20 <section class="step"> 19 21 <h2><span class="step-number">1</span>Publication Setup</h2> ··· 48 50 </div> 49 51 </div> 50 52 </section> 51 - 53 + 52 54 <section class="step"> 53 55 <h2><span class="step-number">2</span>Theme Configuration</h2> 54 56 <div class="grid"> ··· 75 77 </div> 76 78 </div> 77 79 </section> 78 - 80 + 79 81 <section class="step"> 80 82 <h2><span class="step-number">3</span>WhiteWind Blog Entries</h2> 81 83 <div class="warning"> 82 - <strong>Note:</strong> Paste a JSON array of your WhiteWind blog entries below. The converter will automatically handle markdown parsing, AT-URI conversion, and schema transformation for all entries. 84 + <strong>Note:</strong> Paste a JSON array of your WhiteWind blog entries below. The converter will 85 + automatically handle markdown parsing, AT-URI conversion, and schema transformation for all entries. 83 86 </div> 84 87 <div class="form-group"> 85 88 <label for="whitewindJson">WhiteWind Entries JSON*</label> 86 - <textarea id="whitewindJson" class="textarea-large" placeholder='Paste your WhiteWind entries JSON array here...' required></textarea> 89 + <textarea id="whitewindJson" class="textarea-large" 90 + placeholder='Paste your WhiteWind entries JSON array here...' required></textarea> 87 91 <div class="example"> 88 - Example: [{"content": "# Post 1\n\nContent...", "title": "My First Post"}, {"content": "# Post 2\n\nMore content...", "title": "My Second Post"}] 92 + Example: [{"content": "# Post 1\n\nContent...", "title": "My First Post"}, {"content": "# Post 93 + 2\n\nMore content...", "title": "My Second Post"}] 89 94 </div> 90 95 </div> 91 - 96 + 92 97 <div class="form-group"> 93 98 <label for="authorDid">Author DID*</label> 94 99 <input type="text" id="authorDid" placeholder="did:plc:..." required> ··· 96 101 Format: did:plc:example123... or did:web:example.com 97 102 </div> 98 103 </div> 99 - 104 + 100 105 <button onclick="convertEntries()" id="convertBtn" class="btn-primary">๐Ÿ”„ Convert to Leaflet</button> 101 106 </section> 102 - 107 + 103 108 <section class="step" id="outputSection" style="display: none;"> 104 109 <h2><span class="step-number">4</span>Converted Output</h2> 105 110 <div class="success" id="successMessage" style="display: none;"> ··· 109 114 <h3>Publication Record:</h3> 110 115 <pre id="publicationOutput"></pre> 111 116 <div class="action-buttons"> 112 - <button class="btn-secondary" onclick="copyToClipboard('publicationOutput')">๐Ÿ“‹ Copy Publication</button> 113 - <button class="btn-secondary" onclick="downloadFile('publicationOutput', 'publication.json')">โฌ‡๏ธ Download Publication</button> 117 + <button class="btn-secondary" onclick="copyToClipboard('publicationOutput')">๐Ÿ“‹ Copy 118 + Publication</button> 119 + <button class="btn-secondary" onclick="downloadFile('publicationOutput', 'publication.json')">โฌ‡๏ธ 120 + Download Publication</button> 114 121 </div> 115 122 </div> 116 123 <div class="output-card"> 117 124 <h3>Document Records:</h3> 118 125 <pre id="documentOutput"></pre> 119 126 <div class="action-buttons"> 120 - <button class="btn-secondary" onclick="copyToClipboard('documentOutput')">๐Ÿ“‹ Copy Documents</button> 121 - <button class="btn-secondary" onclick="downloadFile('documentOutput', 'documents.json')">โฌ‡๏ธ Download Documents</button> 127 + <button class="btn-secondary" onclick="copyToClipboard('documentOutput')">๐Ÿ“‹ Copy 128 + Documents</button> 129 + <button class="btn-secondary" onclick="downloadFile('documentOutput', 'documents.json')">โฌ‡๏ธ 130 + Download Documents</button> 122 131 </div> 123 132 </div> 124 133 <div class="output-card"> 125 134 <h3>Download as Zip:</h3> 126 - <p>Download all files (publication and documents) as a single ZIP archive, with files named `00.json` for the publication and `1.json`, `2.json`, etc., for each document.</p> 135 + <p>Download all files (publication and documents) as a single ZIP archive, with files named 136 + `00.json` for the publication and `1.json`, `2.json`, etc., for each document.</p> 127 137 <div class="action-buttons"> 128 - <button class="btn-secondary" id="zipDownloadBtn" onclick="downloadZip()">โฌ‡๏ธ Download ZIP</button> 138 + <button class="btn-secondary" id="zipDownloadBtn" onclick="downloadZip()">โฌ‡๏ธ Download 139 + ZIP</button> 129 140 </div> 130 141 </div> 131 142 </section> ··· 136 147 <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script> 137 148 <script src="./script.js"></script> 138 149 </body> 150 + 151 + <footer> 152 + <p> 153 + Built with ๐Ÿƒ by <a href="https://ewancroft.uk" target="_blank" rel="noopener">Ewan</a> โ€ข 154 + <a href="https://github.com/ewanc26/whtwnd-to-leaflet" target="_blank" rel="noopener">Source Code</a> (GPL-3.0) 155 + </p> 156 + <p> 157 + Not affiliated with <a href="https://whtwnd.com" target="_blank" rel="noopener">WhiteWind</a> or 158 + <a href="https://leaflet.pub" target="_blank" rel="noopener">Leaflet</a>. 159 + </p> 160 + </footer> 161 + 139 162 </html>
+41 -9
styles.css
··· 22 22 margin: 0 auto; 23 23 min-height: 100vh; 24 24 background-color: var(--background-color); 25 + 26 + /* Sticky footer setup */ 27 + display: flex; 28 + flex-direction: column; 29 + } 30 + 31 + .main-content { 32 + padding: 2rem; 33 + 34 + /* Allow content to push footer down */ 35 + flex: 1; 25 36 } 26 37 27 38 /* Header */ 28 - .header { 39 + header { 29 40 background-color: var(--header-footer-bg); 30 41 padding: 2rem 1rem; 31 42 text-align: center; 32 43 border-bottom: 1px solid var(--border-color); 33 44 } 34 45 35 - .header h1 { 46 + header h1 { 36 47 font-size: 2.5rem; 37 48 font-weight: 700; 38 49 margin-bottom: 0.5rem; 39 50 color: var(--text-color); 40 51 } 41 52 42 - .header p { 53 + header p { 43 54 opacity: 0.8; 44 55 font-size: 1.1rem; 45 56 color: var(--text-color); 46 - } 47 - 48 - /* Main content */ 49 - .main-content { 50 - padding: 2rem; 51 57 } 52 58 53 59 /* Steps/sections */ ··· 281 287 background: var(--button-hover-bg); 282 288 } 283 289 290 + /* Footer */ 291 + footer { 292 + text-align: center; 293 + font-size: 0.9rem; 294 + padding: 1.5rem 1rem; 295 + margin-top: 2rem; 296 + border-top: 1px solid var(--border-color); 297 + background: var(--header-footer-bg); /* match header background for consistency */ 298 + color: var(--text-color); /* same readable colour as rest of the page */ 299 + line-height: 1.6; 300 + } 301 + 302 + footer p + p { 303 + margin-top: 0.25rem; 304 + } 305 + 306 + footer a { 307 + color: var(--link-color); /* use the same link colour as main body */ 308 + font-weight: 500; 309 + } 310 + 311 + footer a:hover { 312 + color: var(--link-hover-color); 313 + text-decoration: underline; 314 + } 315 + 284 316 /* Responsive design */ 285 317 @media (max-width: 768px) { 286 318 .container { ··· 311 343 .action-buttons button { 312 344 width: 100%; 313 345 } 314 - } 346 + }