+1
.vscode/settings.json
+1
.vscode/settings.json
+42
-19
index.html
+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
+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
+
}