upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-12-02 12:58:45 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-12-02 13:02:31 +0000
commit6e1b4620d26051da4aec8b808c346d17bb1889df (patch)
tree7ddbb876ddf200af15fa3cdd200a0a8efc395435 /src
parent59dbbf0f2986e8d969cc30b57d70f76984a272e3 (diff)
improve landing page copy and styles
Diffstat (limited to 'src')
-rw-r--r--src/http/landing.rs519
1 files changed, 225 insertions, 294 deletions
diff --git a/src/http/landing.rs b/src/http/landing.rs
index ddde09a..6f2bfdd 100644
--- a/src/http/landing.rs
+++ b/src/http/landing.rs
@@ -3,14 +3,55 @@
3/// Generates HTML landing page for the Nostr relay. 3/// Generates HTML landing page for the Nostr relay.
4use crate::config::Config; 4use crate::config::Config;
5 5
6/// Generate the common base CSS used across all pages
7fn get_base_css() -> &'static str {
8 r#":root {
9 --brand: #4434FF;
10 --brand-light: #6b5fff;
11 --bg: #0a0a0f;
12 --surface: #12121a;
13 --border: #1e1e2e;
14 --text: #e4e4eb;
15 --text-muted: #a8a8bd;
16 --error: #ff4444;
17 --success: #22c55e;
18 }
19 * { margin: 0; padding: 0; box-sizing: border-box; }
20 body {
21 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', sans-serif;
22 line-height: 1.6;
23 background: var(--bg);
24 color: var(--text);
25 min-height: 100vh;
26 }
27 a { color: var(--brand-light); text-decoration: none; }
28 a:hover { text-decoration: underline; }
29 code {
30 background: var(--border);
31 padding: 4px 8px;
32 border-radius: 4px;
33 font-family: 'SF Mono', 'Consolas', monospace;
34 font-size: 0.875rem;
35 color: var(--brand-light);
36 }
37 .footer {
38 margin-top: 48px;
39 padding-top: 24px;
40 border-top: 1px solid var(--border);
41 text-align: center;
42 color: var(--text-muted);
43 font-size: 0.875rem;
44 }"#
45}
46
6/// Generate the HTML landing page 47/// Generate the HTML landing page
7pub fn get_html(config: &Config) -> String { 48pub fn get_html(config: &Config) -> String {
8 format!( 49 format!(
9 include_str!("../../templates/landing.html"), 50 include_str!("../../templates/landing.html"),
51 base_css = get_base_css(),
10 relay_name = config.relay_name, 52 relay_name = config.relay_name,
11 relay_description = config.relay_description, 53 relay_description = config.relay_description,
12 domain = config.domain, 54 domain = config.domain,
13 bind_address = config.bind_address,
14 ) 55 )
15} 56}
16 57
@@ -19,102 +60,63 @@ pub fn get_html(config: &Config) -> String {
19/// Used for any path that doesn't match a known route 60/// Used for any path that doesn't match a known route
20pub fn get_generic_404_html(config: &Config, path: &str) -> String { 61pub fn get_generic_404_html(config: &Config, path: &str) -> String {
21 format!( 62 format!(
22 r#"<!DOCTYPE html> 63 r##"<!DOCTYPE html>
23<html lang="en"> 64<html lang="en">
24<head> 65<head>
25 <meta charset="UTF-8"> 66 <meta charset="UTF-8">
26 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 67 <meta name="viewport" content="width=device-width, initial-scale=1.0">
27 <title>Not Found - {relay_name}</title> 68 <title>Not Found - {relay_name}</title>
28 <style> 69 <style>
29 * {{ 70 {base_css}
30 margin: 0;
31 padding: 0;
32 box-sizing: border-box;
33 }}
34 body {{ 71 body {{
35 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
36 line-height: 1.6;
37 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
38 color: #333;
39 min-height: 100vh;
40 display: flex; 72 display: flex;
41 align-items: center; 73 align-items: center;
42 justify-content: center; 74 justify-content: center;
43 padding: 20px; 75 padding: 24px;
44 }} 76 }}
45 .container {{ 77 .container {{ max-width: 480px; text-align: center; }}
46 max-width: 600px; 78 .error-code {{
47 background: white; 79 font-size: 6rem;
48 padding: 40px; 80 font-weight: 700;
49 border-radius: 12px; 81 color: var(--error);
50 box-shadow: 0 20px 60px rgba(0,0,0,0.3); 82 line-height: 1;
51 text-align: center; 83 margin-bottom: 8px;
52 }} 84 }}
53 h1 {{ 85 h2 {{ font-size: 1.5rem; font-weight: 500; margin-bottom: 16px; }}
54 color: #e74c3c; 86 p {{ color: var(--text-muted); margin-bottom: 24px; }}
55 margin-bottom: 10px;
56 font-size: 4em;
57 }}
58 h2 {{
59 color: #333;
60 margin-bottom: 20px;
61 font-size: 1.5em;
62 }}
63 .path-info {{ 87 .path-info {{
64 background: #f9f9f9; 88 background: var(--surface);
65 padding: 15px; 89 border: 1px solid var(--border);
66 border-radius: 8px; 90 border-radius: 8px;
67 margin: 20px 0; 91 padding: 16px;
68 border-left: 4px solid #e74c3c; 92 margin-bottom: 32px;
69 }} 93 }}
70 code {{ 94 .path-label {{
71 background: #f4f4f4; 95 font-size: 0.75rem;
72 padding: 3px 8px; 96 text-transform: uppercase;
73 border-radius: 4px; 97 letter-spacing: 0.1em;
74 font-family: 'Courier New', monospace; 98 color: var(--text-muted);
75 color: #667eea; 99 margin-bottom: 8px;
76 font-size: 0.85em; 100 }}
77 word-break: break-all; 101 code {{ word-break: break-all; }}
78 }} 102 .footer {{ margin-top: 48px; }}
79 .back-link {{
80 margin-top: 20px;
81 }}
82 a {{
83 color: #667eea;
84 text-decoration: none;
85 }}
86 a:hover {{
87 text-decoration: underline;
88 }}
89 .footer {{
90 margin-top: 30px;
91 padding-top: 20px;
92 border-top: 1px solid #eee;
93 color: #999;
94 font-size: 0.9em;
95 }}
96 </style> 103 </style>
97</head> 104</head>
98<body> 105<body>
99 <div class="container"> 106 <div class="container">
100 <h1>404</h1> 107 <div class="error-code">404</div>
101 <h2>Not Found</h2> 108 <h2>Page Not Found</h2>
102 <p>The page you're looking for doesn't exist.</p> 109 <p>The page you're looking for doesn't exist.</p>
103
104 <div class="path-info"> 110 <div class="path-info">
105 <p><strong>Requested path:</strong> <code>{path}</code></p> 111 <div class="path-label">Requested Path</div>
106 </div> 112 <code>{path}</code>
107
108 <div class="back-link">
109 <a href="/">← Back to {relay_name}</a>
110 </div>
111
112 <div class="footer">
113 <p>Powered by <strong>ngit-grasp</strong></p>
114 </div> 113 </div>
114 <a href="/">&larr; Back to {relay_name}</a>
115 <div class="footer">Powered by <strong>ngit-grasp</strong></div>
115 </div> 116 </div>
116</body> 117</body>
117</html>"#, 118</html>"##,
119 base_css = get_base_css(),
118 relay_name = config.relay_name, 120 relay_name = config.relay_name,
119 path = path, 121 path = path,
120 ) 122 )
@@ -125,105 +127,87 @@ pub fn get_generic_404_html(config: &Config, path: &str) -> String {
125/// GRASP-01: "...and a 404 page for repositories it doesn't host" 127/// GRASP-01: "...and a 404 page for repositories it doesn't host"
126pub fn get_404_html(config: &Config, npub: &str, identifier: &str) -> String { 128pub fn get_404_html(config: &Config, npub: &str, identifier: &str) -> String {
127 format!( 129 format!(
128 r#"<!DOCTYPE html> 130 r##"<!DOCTYPE html>
129<html lang="en"> 131<html lang="en">
130<head> 132<head>
131 <meta charset="UTF-8"> 133 <meta charset="UTF-8">
132 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 134 <meta name="viewport" content="width=device-width, initial-scale=1.0">
133 <title>Repository Not Found - {relay_name}</title> 135 <title>Repository Not Found - {relay_name}</title>
134 <style> 136 <style>
135 * {{ 137 {base_css}
136 margin: 0;
137 padding: 0;
138 box-sizing: border-box;
139 }}
140 body {{ 138 body {{
141 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
142 line-height: 1.6;
143 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
144 color: #333;
145 min-height: 100vh;
146 display: flex; 139 display: flex;
147 align-items: center; 140 align-items: center;
148 justify-content: center; 141 justify-content: center;
149 padding: 20px; 142 padding: 24px;
150 }} 143 }}
151 .container {{ 144 .container {{ max-width: 480px; text-align: center; }}
152 max-width: 600px; 145 .error-code {{
153 background: white; 146 font-size: 6rem;
154 padding: 40px; 147 font-weight: 700;
155 border-radius: 12px; 148 color: var(--error);
156 box-shadow: 0 20px 60px rgba(0,0,0,0.3); 149 line-height: 1;
157 text-align: center; 150 margin-bottom: 8px;
158 }} 151 }}
159 h1 {{ 152 h2 {{ font-size: 1.5rem; font-weight: 500; margin-bottom: 16px; }}
160 color: #e74c3c; 153 p {{ color: var(--text-muted); margin-bottom: 24px; }}
161 margin-bottom: 10px;
162 font-size: 4em;
163 }}
164 h2 {{
165 color: #333;
166 margin-bottom: 20px;
167 font-size: 1.5em;
168 }}
169 .repo-info {{ 154 .repo-info {{
170 background: #f9f9f9; 155 background: var(--surface);
171 padding: 15px; 156 border: 1px solid var(--border);
172 border-radius: 8px; 157 border-radius: 8px;
173 margin: 20px 0; 158 padding: 16px;
174 border-left: 4px solid #e74c3c; 159 margin-bottom: 16px;
160 text-align: left;
161 }}
162 .info-row {{
163 display: flex;
164 justify-content: space-between;
165 align-items: center;
166 padding: 8px 0;
175 }} 167 }}
168 .info-row + .info-row {{ border-top: 1px solid var(--border); }}
169 .info-label {{ font-size: 0.875rem; color: var(--text-muted); }}
176 code {{ 170 code {{
177 background: #f4f4f4; 171 font-size: 0.75rem;
178 padding: 3px 8px;
179 border-radius: 4px;
180 font-family: 'Courier New', monospace;
181 color: #667eea;
182 font-size: 0.85em;
183 word-break: break-all; 172 word-break: break-all;
173 max-width: 200px;
174 overflow: hidden;
175 text-overflow: ellipsis;
184 }} 176 }}
185 .back-link {{ 177 .hint {{
186 margin-top: 20px; 178 background: var(--surface);
187 }} 179 border: 1px solid var(--border);
188 a {{ 180 border-radius: 8px;
189 color: #667eea; 181 padding: 16px;
190 text-decoration: none; 182 margin-bottom: 32px;
191 }} 183 font-size: 0.875rem;
192 a:hover {{ 184 color: var(--text-muted);
193 text-decoration: underline;
194 }}
195 .footer {{
196 margin-top: 30px;
197 padding-top: 20px;
198 border-top: 1px solid #eee;
199 color: #999;
200 font-size: 0.9em;
201 }} 185 }}
186 .footer {{ margin-top: 48px; }}
202 </style> 187 </style>
203</head> 188</head>
204<body> 189<body>
205 <div class="container"> 190 <div class="container">
206 <h1>404</h1> 191 <div class="error-code">404</div>
207 <h2>Repository Not Found</h2> 192 <h2>Repository Not Found</h2>
208 <p>The repository you're looking for doesn't exist on this GRASP server.</p> 193 <p>This repository doesn't exist on this GRASP server.</p>
209
210 <div class="repo-info"> 194 <div class="repo-info">
211 <p><strong>Owner:</strong> <code>{npub}</code></p> 195 <div class="info-row">
212 <p><strong>Repository:</strong> <code>{identifier}</code></p> 196 <span class="info-label">Owner</span>
213 </div> 197 <code>{npub}</code>
214 198 </div>
215 <p>This repository may not have been announced to this server, or the URL may be incorrect.</p> 199 <div class="info-row">
216 200 <span class="info-label">Repository</span>
217 <div class="back-link"> 201 <code>{identifier}</code>
218 <a href="/">← Back to {relay_name}</a> 202 </div>
219 </div>
220
221 <div class="footer">
222 <p>Powered by <strong>ngit-grasp</strong></p>
223 </div> 203 </div>
204 <div class="hint">The repository may not have been announced to this server, or the URL may be incorrect.</div>
205 <a href="/">&larr; Back to {relay_name}</a>
206 <div class="footer">Powered by <strong>ngit-grasp</strong></div>
224 </div> 207 </div>
225</body> 208</body>
226</html>"#, 209</html>"##,
210 base_css = get_base_css(),
227 relay_name = config.relay_name, 211 relay_name = config.relay_name,
228 npub = npub, 212 npub = npub,
229 identifier = identifier, 213 identifier = identifier,
@@ -241,182 +225,129 @@ pub fn get_repo_html(config: &Config, npub: &str, identifier: &str) -> String {
241 ); 225 );
242 226
243 format!( 227 format!(
244 r#"<!DOCTYPE html> 228 r##"<!DOCTYPE html>
245<html lang="en"> 229<html lang="en">
246<head> 230<head>
247 <meta charset="UTF-8"> 231 <meta charset="UTF-8">
248 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 232 <meta name="viewport" content="width=device-width, initial-scale=1.0">
249 <title>{identifier} - {relay_name}</title> 233 <title>{identifier} - {relay_name}</title>
250 <style> 234 <style>
251 * {{ 235 {base_css}
252 margin: 0; 236 .container {{ max-width: 720px; margin: 0 auto; padding: 60px 24px; }}
253 padding: 0; 237 .back-link {{ margin-bottom: 32px; }}
254 box-sizing: border-box; 238 .header {{ display: flex; align-items: center; gap: 16px; margin-bottom: 8px; }}
239 .logo {{ width: 40px; height: 40px; }}
240 h1 {{ font-size: 1.75rem; font-weight: 600; letter-spacing: -0.02em; }}
241 .subtitle {{ color: var(--text-muted); margin-bottom: 40px; }}
242 .section {{ margin-bottom: 32px; }}
243 .section-title {{
244 font-size: 0.75rem;
245 font-weight: 600;
246 text-transform: uppercase;
247 letter-spacing: 0.1em;
248 color: var(--text);
249 margin-bottom: 12px;
250 }}
251 .card {{
252 background: var(--surface);
253 border: 1px solid var(--border);
254 border-radius: 12px;
255 padding: 16px 20px;
255 }} 256 }}
256 body {{ 257 .card + .card {{ margin-top: 8px; }}
257 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif; 258 .info-row {{
258 line-height: 1.6;
259 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
260 color: #333;
261 min-height: 100vh;
262 display: flex; 259 display: flex;
260 justify-content: space-between;
263 align-items: center; 261 align-items: center;
264 justify-content: center; 262 padding: 8px 0;
265 padding: 20px;
266 }}
267 .container {{
268 max-width: 800px;
269 background: white;
270 padding: 40px;
271 border-radius: 12px;
272 box-shadow: 0 20px 60px rgba(0,0,0,0.3);
273 }}
274 h1 {{
275 color: #667eea;
276 margin-bottom: 10px;
277 font-size: 2em;
278 }}
279 h2 {{
280 color: #764ba2;
281 margin-top: 25px;
282 margin-bottom: 15px;
283 font-size: 1.3em;
284 border-bottom: 2px solid #667eea;
285 padding-bottom: 8px;
286 }}
287 .subtitle {{
288 color: #666;
289 margin-bottom: 25px;
290 font-size: 1em;
291 }}
292 .repo-info {{
293 background: #f9f9f9;
294 padding: 15px;
295 border-radius: 8px;
296 margin: 15px 0;
297 border-left: 4px solid #667eea;
298 }}
299 code {{
300 background: #f4f4f4;
301 padding: 3px 8px;
302 border-radius: 4px;
303 font-family: 'Courier New', monospace;
304 color: #667eea;
305 font-size: 0.85em;
306 word-break: break-all;
307 }} 263 }}
264 .info-row + .info-row {{ border-top: 1px solid var(--border); }}
265 .info-label {{ font-size: 0.875rem; color: var(--text-muted); }}
266 code {{ font-size: 0.8rem; word-break: break-all; }}
308 .clone-box {{ 267 .clone-box {{
309 background: #2d3748; 268 background: var(--bg);
310 color: #e2e8f0; 269 border: 1px solid var(--border);
311 padding: 15px;
312 border-radius: 8px; 270 border-radius: 8px;
313 margin: 15px 0; 271 padding: 16px;
314 font-family: 'Courier New', monospace; 272 font-family: 'SF Mono', 'Consolas', monospace;
315 font-size: 0.9em; 273 font-size: 0.875rem;
274 color: var(--text);
316 overflow-x: auto; 275 overflow-x: auto;
317 }} 276 }}
318 .clone-box code {{ 277 .clone-box .cmd {{ color: var(--text-muted); }}
319 background: transparent; 278 .clone-box .url {{ color: var(--success); }}
320 color: #68d391; 279 .client-card {{ display: flex; justify-content: space-between; align-items: center; }}
321 padding: 0; 280 .client-info {{ display: flex; flex-direction: column; }}
322 }} 281 .client-name {{ font-weight: 500; }}
323 ul {{ 282 .client-desc {{ font-size: 0.875rem; color: var(--text-muted); }}
324 margin: 15px 0;
325 padding-left: 25px;
326 }}
327 li {{
328 margin: 10px 0;
329 }}
330 a {{
331 color: #667eea;
332 text-decoration: none;
333 }}
334 a:hover {{
335 text-decoration: underline;
336 }}
337 .client-list {{
338 display: grid;
339 gap: 10px;
340 margin: 15px 0;
341 }}
342 .client-item {{
343 background: #f9f9f9;
344 padding: 12px 15px;
345 border-radius: 8px;
346 display: flex;
347 justify-content: space-between;
348 align-items: center;
349 }}
350 .badge {{
351 display: inline-block;
352 background: #667eea;
353 color: white;
354 padding: 4px 10px;
355 border-radius: 12px;
356 font-size: 0.8em;
357 }}
358 .footer {{
359 margin-top: 30px;
360 padding-top: 20px;
361 border-top: 1px solid #eee;
362 text-align: center;
363 color: #999;
364 font-size: 0.9em;
365 }}
366 .back-link {{
367 margin-bottom: 20px;
368 }}
369 </style> 283 </style>
370</head> 284</head>
371<body> 285<body>
372 <div class="container"> 286 <div class="container">
373 <div class="back-link"> 287 <div class="back-link">
374 <a href="/">← Back to {relay_name}</a> 288 <a href="/">&larr; {relay_name}</a>
289 </div>
290 <div class="header">
291 <svg class="logo" viewBox="0 0 38 38" fill="none" xmlns="http://www.w3.org/2000/svg">
292 <rect width="38" height="38" rx="12" fill="#4434FF"/>
293 <path d="M10.6731 30.6348C8.83687 30.6346 7.34885 29.1458 7.34885 27.3096C7.34891 26.2473 7.84783 25.303 8.62326 24.6943C8.21265 23.3055 7.86571 22.049 7.45334 20.6758C6.90247 18.8412 7.4492 16.8197 8.93576 15.5605L15.7512 9.78906C15.6931 9.54286 15.6614 9.28642 15.6613 9.02246C15.6613 7.51617 16.6628 6.24465 18.0363 5.83594L18.0363 -1.11215e-06C18.511 0.000462658 18.4612 0.000975391 18.9856 0.000975533C19.5102 0.000975578 19.5802 -1.11589e-06 19.9367 -9.46012e-07L19.9367 5.83594C21.3097 6.24503 22.3108 7.5166 22.3108 9.02246C22.3107 9.29118 22.2792 9.55249 22.219 9.80273L29.0783 15.6123C30.5229 16.8359 31.1022 18.8013 30.5539 20.6133L29.3254 24.6758C30.1142 25.2837 30.6232 26.2367 30.6233 27.3096C30.6233 29.1459 29.1344 30.6348 27.2981 30.6348C25.4619 30.6346 23.9738 29.1458 23.9738 27.3096C23.974 25.4734 25.4619 23.9846 27.2981 23.9844C27.3814 23.9844 27.4643 23.9891 27.5461 23.9951L28.7356 20.0625C29.0645 18.9753 28.7166 17.7966 27.8498 17.0625L21.2424 11.4648C20.8746 11.8048 20.4294 12.0622 19.9367 12.209L19.9367 18.9258C21.0425 19.3175 21.836 20.3694 21.8362 21.6094C21.8362 23.1834 20.5596 24.46 18.9856 24.46C17.4117 24.4598 16.136 23.1833 16.136 21.6094C16.1361 20.3689 16.93 19.3172 18.0363 18.9258L18.0363 12.21C17.5395 12.0622 17.0916 11.801 16.7219 11.457L10.1643 17.0107C9.27919 17.7605 8.93068 18.9867 9.27365 20.1289C9.68708 21.5056 10.0175 22.7009 10.3986 23.998C10.4892 23.9906 10.5806 23.9844 10.6731 23.9844C12.5093 23.9844 13.9981 25.4733 13.9983 27.3096C13.9983 29.1459 12.5094 30.6348 10.6731 30.6348Z" fill="white"/>
294 </svg>
295 <h1>{identifier}</h1>
375 </div> 296 </div>
376
377 <h1>📦 {identifier}</h1>
378 <p class="subtitle">Git repository hosted on {relay_name}</p> 297 <p class="subtitle">Git repository hosted on {relay_name}</p>
379 298 <div class="section">
380 <h2>📋 Repository Information</h2> 299 <div class="section-title">Repository Info</div>
381 <div class="repo-info"> 300 <div class="card">
382 <p><strong>Owner:</strong> <code>{npub}</code></p> 301 <div class="info-row">
383 <p><strong>Repository:</strong> <code>{identifier}</code></p> 302 <span class="info-label">Owner</span>
303 <code>{npub}</code>
304 </div>
305 <div class="info-row">
306 <span class="info-label">Identifier</span>
307 <code>{identifier}</code>
308 </div>
309 </div>
384 </div> 310 </div>
385 311 <div class="section">
386 <h2>🔗 Clone this Repository</h2> 312 <div class="section-title">Clone</div>
387 <div class="clone-box"> 313 <div class="card">
388 git clone <code>{clone_url}</code> 314 <div class="clone-box">
315 <span class="cmd">git clone</span> <span class="url">{clone_url}</span>
316 </div>
317 </div>
389 </div> 318 </div>
390 319 <div class="section">
391 <h2>🌐 Browse with Git Nostr Clients</h2> 320 <div class="section-title">Browse with Git Nostr Clients</div>
392 <p>You can browse this repository using these Git Nostr clients:</p> 321 <div class="card client-card">
393 <div class="client-list"> 322 <div class="client-info">
394 <div class="client-item"> 323 <span class="client-name">gitworkshop.dev</span>
395 <span><strong>gitworkshop.dev</strong> - Web-based repository browser</span> 324 <span class="client-desc">Web-based repository browser</span>
396 <a href="https://gitworkshop.dev" target="_blank">Visit →</a> 325 </div>
326 <a href="https://gitworkshop.dev" target="_blank">Visit &rarr;</a>
397 </div> 327 </div>
398 <div class="client-item"> 328 <div class="card client-card">
399 <span><strong>ngit</strong> - Command-line Git + Nostr tool</span> 329 <div class="client-info">
400 <a href="https://github.com/DanConwayDev/ngit-cli" target="_blank">GitHub →</a> 330 <span class="client-name">ngit</span>
331 <span class="client-desc">Command-line Git + Nostr tool</span>
332 </div>
333 <a href="https://github.com/DanConwayDev/ngit-cli" target="_blank">GitHub &rarr;</a>
401 </div> 334 </div>
402 </div> 335 </div>
403 336 <div class="section">
404 <h2>📚 About GRASP</h2> 337 <div class="section-title">Documentation</div>
405 <p>This repository is hosted using the <strong>GRASP</strong> (Git Relays Authorized via Signed-Nostr Proofs) protocol.</p> 338 <div class="card">
406 <ul> 339 <p style="margin-bottom: 12px;"><a href="https://gitworkshop.dev/r/naddr1qvzqqqrhnypzqfngzhsvjggdlgeycm96x4emzjlwf8dyyzdfg4hefp89zpkdgz99qy2hwumn8ghj7un9d3shjtnyv9kh2uewd9hj7qghwaehxw309aex2mrp0yhxummnw3ezucnpdejz7q3ql2vyh47mk2p0qlsku7hg0vn29faehy9hy34ygaclpn66ukqp3afqxpqqqqqqz6pwefu" target="_blank">GRASP Specification</a></p>
407 <li><a href="https://gitworkshop.dev/repo/grasp/01.md" target="_blank">GRASP-01 Specification</a></li> 340 <p><a href="https://github.com/nostr-protocol/nips/blob/master/34.md" target="_blank">NIP-34: Git Stuff</a></p>
408 <li><a href="https://github.com/nostr-protocol/nips/blob/master/34.md" target="_blank">NIP-34: Git Stuff</a></li> 341 </div>
409 </ul>
410
411 <div class="footer">
412 <p>Powered by <strong>ngit-grasp</strong></p>
413 </div> 342 </div>
343 <div class="footer">Powered by <strong>ngit-grasp</strong></div>
414 </div> 344 </div>
415</body> 345</body>
416</html>"#, 346</html>"##,
347 base_css = get_base_css(),
417 relay_name = config.relay_name, 348 relay_name = config.relay_name,
418 npub = npub, 349 npub = npub,
419 identifier = identifier, 350 identifier = identifier,
420 clone_url = clone_url, 351 clone_url = clone_url,
421 ) 352 )
422} 353} \ No newline at end of file