my own indieAuth provider! indiko.dunkirk.sh/docs
indieauth oauth2-server
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: use custom highlighters

+144 -49
+19 -21
src/client/docs.ts
··· 1 + // JSON syntax highlighter 2 + function highlightJSON(json: string): string { 3 + return json 4 + .replace(/&/g, '&amp;') 5 + .replace(/</g, '&lt;') 6 + .replace(/>/g, '&gt;') 7 + .replace(/"([^"]+)":/g, '<span class="json-key">"$1"</span>:') 8 + .replace(/: "([^"]*)"/g, ': <span class="json-string">"$1"</span>') 9 + .replace(/: (\d+\.?\d*)/g, ': <span class="json-number">$1</span>') 10 + .replace(/: (true|false|null)/g, ': <span class="json-boolean">$1</span>'); 11 + } 12 + 1 13 // PKCE helper functions 2 14 function generateRandomString(length: number): string { 3 15 const array = new Uint8Array(length); ··· 180 192 // Extract and parse JSON from success message 181 193 const jsonStart = text.indexOf('{'); 182 194 const jsonStr = text.substring(jsonStart); 183 - const prefix = text.substring(0, jsonStart); 195 + const prefix = text.substring(0, jsonStart).trim(); 184 196 185 197 try { 186 198 const data = JSON.parse(jsonStr); 187 - resultDiv.innerHTML = `${prefix}<pre style="margin: 0; font-family: 'Space Grotesk', monospace;">${syntaxHighlightJSON(data)}</pre>`; 199 + const formattedJson = JSON.stringify(data, null, 2); 200 + 201 + // Apply custom JSON syntax highlighting 202 + const highlightedJson = highlightJSON(formattedJson); 203 + 204 + resultDiv.innerHTML = `<strong style="color: var(--berry-crush); font-size: 1.125rem; display: block; margin-bottom: 1rem;">${prefix}</strong><pre style="margin: 0;"><code>${highlightedJson}</code></pre>`; 188 205 } catch { 189 206 resultDiv.textContent = text; 190 207 } ··· 193 210 } 194 211 resultDiv.className = `result show ${type}`; 195 212 } 196 - 197 - function syntaxHighlightJSON(obj: any): string { 198 - const json = JSON.stringify(obj, null, 2); 199 - return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, (match) => { 200 - let cls = 'json-number'; 201 - if (/^"/.test(match)) { 202 - if (/:$/.test(match)) { 203 - cls = 'json-key'; 204 - } else { 205 - cls = 'json-string'; 206 - } 207 - } else if (/true|false/.test(match)) { 208 - cls = 'json-boolean'; 209 - } else if (/null/.test(match)) { 210 - cls = 'json-null'; 211 - } 212 - return `<span class="${cls}">${match}</span>`; 213 - }); 214 - }
+125 -28
src/html/docs.html
··· 127 127 background: none; 128 128 padding: 0; 129 129 font-size: 0.875rem; 130 + color: inherit; 131 + } 132 + 133 + /* Override Prism theme to match our colors */ 134 + pre[class*="language-"] { 135 + background: rgba(12, 23, 19, 0.8); 136 + border: 1px solid var(--rosewood); 137 + } 138 + 139 + /* HTTP syntax highlighting */ 140 + .http-method { 141 + color: var(--berry-crush); 142 + font-weight: 700; 143 + } 144 + 145 + .http-url { 146 + color: #a5d6a7; 147 + } 148 + 149 + .http-header { 150 + color: var(--old-rose); 151 + } 152 + 153 + .http-param { 154 + color: #81c784; 155 + } 156 + 157 + /* JSON syntax highlighting */ 158 + .json-key { 159 + color: var(--berry-crush); 160 + } 161 + 162 + .json-string { 163 + color: #a5d6a7; 164 + } 165 + 166 + .json-number { 167 + color: #81c784; 168 + } 169 + 170 + .json-boolean { 171 + color: var(--old-rose); 172 + } 173 + 174 + .token.property, 175 + .token.tag, 176 + .token.boolean, 177 + .token.number, 178 + .token.constant, 179 + .token.symbol { 180 + color: #81c784; 181 + } 182 + 183 + .token.selector, 184 + .token.attr-name, 185 + .token.string, 186 + .token.char, 187 + .token.builtin { 188 + color: #a5d6a7; 189 + } 190 + 191 + .token.punctuation { 192 + color: var(--lavender); 193 + } 194 + 195 + .token.operator, 196 + .token.entity, 197 + .token.url, 198 + .language-css .token.string, 199 + .style .token.string { 200 + color: var(--old-rose); 130 201 } 131 202 132 203 ul, ol { ··· 257 328 .checkbox-group label { 258 329 display: flex; 259 330 align-items: center; 260 - gap: 0.5rem; 331 + gap: 0.75rem; 261 332 text-transform: none; 262 333 font-weight: 400; 263 334 margin-bottom: 0.75rem; 264 335 cursor: pointer; 336 + padding: 0.5rem; 337 + transition: background 0.2s; 338 + } 339 + 340 + .checkbox-group label:hover { 341 + background: rgba(188, 141, 160, 0.1); 265 342 } 266 343 267 344 input[type="checkbox"] { 268 - width: 1.25rem; 269 - height: 1.25rem; 345 + appearance: none; 346 + width: 1.5rem; 347 + height: 1.5rem; 348 + border: 2px solid var(--old-rose); 349 + background: rgba(12, 23, 19, 0.6); 270 350 cursor: pointer; 351 + flex-shrink: 0; 352 + position: relative; 353 + transition: all 0.2s; 354 + } 355 + 356 + input[type="checkbox"]:checked { 357 + background: var(--berry-crush); 358 + border-color: var(--berry-crush); 359 + } 360 + 361 + input[type="checkbox"]:checked::after { 362 + content: "✓"; 363 + position: absolute; 364 + top: 50%; 365 + left: 50%; 366 + transform: translate(-50%, -50%); 367 + color: var(--lavender); 368 + font-size: 1rem; 369 + font-weight: 700; 271 370 } 272 371 273 372 button { ··· 505 604 <h2>authorization flow</h2> 506 605 507 606 <h3>1. redirect to authorization endpoint</h3> 508 - <pre><code>GET /auth/authorize? 509 - response_type=code 510 - &client_id=https://myapp.example.com 511 - &redirect_uri=https://myapp.example.com/callback 512 - &state=random_state_string 513 - &code_challenge=base64url_encoded_challenge 514 - &code_challenge_method=S256 515 - &scope=profile email</code></pre> 607 + <pre><code><span class="http-method">GET</span> <span class="http-url">/auth/authorize</span>?<span class="http-param">response_type</span>=code 608 + &<span class="http-param">client_id</span>=https://myapp.example.com 609 + &<span class="http-param">redirect_uri</span>=https://myapp.example.com/callback 610 + &<span class="http-param">state</span>=random_state_string 611 + &<span class="http-param">code_challenge</span>=base64url_encoded_challenge 612 + &<span class="http-param">code_challenge_method</span>=S256 613 + &<span class="http-param">scope</span>=profile email</code></pre> 516 614 517 615 <div class="info-box"> 518 616 <strong>PKCE is required:</strong> ··· 530 628 </ul> 531 629 532 630 <h3>3. redirect back with code</h3> 533 - <pre><code>https://myapp.example.com/callback? 534 - code=short_lived_authorization_code 535 - &state=random_state_string</code></pre> 631 + <pre><code><span class="http-url">https://myapp.example.com/callback</span>?<span class="http-param">code</span>=short_lived_authorization_code 632 + &<span class="http-param">state</span>=random_state_string</code></pre> 536 633 537 634 <h3>4. exchange code for token</h3> 538 - <pre><code>POST /auth/token 539 - Content-Type: application/x-www-form-urlencoded 635 + <pre><code><span class="http-method">POST</span> <span class="http-url">/auth/token</span> 636 + <span class="http-header">Content-Type</span>: application/x-www-form-urlencoded 540 637 541 - grant_type=authorization_code 542 - &code=authorization_code 543 - &client_id=https://myapp.example.com 544 - &redirect_uri=https://myapp.example.com/callback 545 - &code_verifier=original_code_verifier</code></pre> 638 + <span class="http-param">grant_type</span>=authorization_code 639 + &<span class="http-param">code</span>=authorization_code 640 + &<span class="http-param">client_id</span>=https://myapp.example.com 641 + &<span class="http-param">redirect_uri</span>=https://myapp.example.com/callback 642 + &<span class="http-param">code_verifier</span>=original_code_verifier</code></pre> 546 643 547 644 <h3>5. receive user profile</h3> 548 645 <pre><code>{ 549 - "me": "https://indiko.example.com/u/username", 550 - "profile": { 551 - "name": "Jane Doe", 552 - "email": "jane@example.com", 553 - "photo": "https://example.com/photo.jpg", 554 - "url": "https://jane.example.com" 646 + <span class="json-key">"me"</span>: <span class="json-string">"https://indiko.example.com/u/username"</span>, 647 + <span class="json-key">"profile"</span>: { 648 + <span class="json-key">"name"</span>: <span class="json-string">"Jane Doe"</span>, 649 + <span class="json-key">"email"</span>: <span class="json-string">"jane@example.com"</span>, 650 + <span class="json-key">"photo"</span>: <span class="json-string">"https://example.com/photo.jpg"</span>, 651 + <span class="json-key">"url"</span>: <span class="json-string">"https://jane.example.com"</span> 555 652 }, 556 - "scope": "profile email" 653 + <span class="json-key">"scope"</span>: <span class="json-string">"profile email"</span> 557 654 }</code></pre> 558 655 </section> 559 656