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: add an button to the docs

+217 -135
+120 -60
src/client/docs.ts
··· 10 10 .replace(/: (true|false|null)/g, ': <span class="json-boolean">$1</span>'); 11 11 } 12 12 13 + // HTML/CSS syntax highlighter 14 + function highlightHTMLCSS(code: string): string { 15 + // First escape HTML entities 16 + let highlighted = code 17 + .replace(/&/g, '&amp;') 18 + .replace(/</g, '&lt;') 19 + .replace(/>/g, '&gt;'); 20 + 21 + // HTML comments 22 + highlighted = highlighted.replace(/&lt;!--(.*?)--&gt;/g, '<span class="html-comment">&lt;!--$1--&gt;</span>'); 23 + 24 + // Split by <style> tags to handle CSS separately 25 + const parts = highlighted.split(/(&lt;style&gt;[\s\S]*?&lt;\/style&gt;)/g); 26 + 27 + highlighted = parts.map((part, index) => { 28 + // Even indices are HTML, odd indices are CSS blocks 29 + if (index % 2 === 0) { 30 + // Process HTML 31 + return part.replace(/&lt;(\/?)([\w-]+)([\s\S]*?)&gt;/g, (_match, slash, tag, attrs) => { 32 + let result = `&lt;${slash}<span class="html-tag">${tag}</span>`; 33 + 34 + if (attrs) { 35 + attrs = attrs.replace(/([\w-]+)="([^"]*)"/g, '<span class="html-attr">$1</span>="<span class="html-string">$2</span>"'); 36 + attrs = attrs.replace(/(?<=\s)([\w-]+)(?=\s|$)/g, '<span class="html-attr">$1</span>'); 37 + } 38 + 39 + result += attrs + '&gt;'; 40 + return result; 41 + }); 42 + } else { 43 + // Process CSS (inside <style> tags) 44 + return part 45 + .replace(/&lt;style&gt;/g, '&lt;<span class="html-tag">style</span>&gt;') 46 + .replace(/&lt;\/style&gt;/g, '&lt;/<span class="html-tag">style</span>&gt;') 47 + // CSS selectors (anything before { including pseudo-selectors) 48 + .replace(/^(\s*)([\w.-]+(?::+[\w-]+(?:\([^)]*\))?)*)\s*\{/gm, '$1<span class="css-selector">$2</span> {') 49 + // CSS properties (word followed by colon, but not :: for pseudo-elements) 50 + .replace(/^(\s+)([\w-]+):\s+/gm, '$1<span class="css-property">$2</span>: ') 51 + // CSS values (everything between property: and ;) 52 + .replace(/(<span class="css-property">[\w-]+<\/span>:\s+)([^;]+);/g, (_match, prop, value) => { 53 + const highlightedValue = value 54 + .replace(/(#[0-9a-fA-F]{3,6})/g, '<span class="css-value">$1</span>') 55 + .replace(/([\d.]+(?:px|rem|em|s|%))/g, '<span class="css-value">$1</span>') 56 + .replace(/('.*?')/g, '<span class="css-value">$1</span>') 57 + .replace(/([\w-]+\([^)]*\))/g, '<span class="css-value">$1</span>'); 58 + return `${prop}${highlightedValue};`; 59 + }); 60 + } 61 + }).join(''); 62 + 63 + return highlighted; 64 + } 65 + 13 66 // PKCE helper functions 14 67 function generateRandomString(length: number): string { 15 68 const array = new Uint8Array(length); ··· 43 96 const copyMarkdownBtn = document.getElementById('copyMarkdownBtn') as HTMLButtonElement; 44 97 const copyButtonCodeBtn = document.getElementById('copyButtonCode') as HTMLButtonElement; 45 98 const demoButton = document.getElementById('demoButton') as HTMLAnchorElement; 99 + const buttonCodeEl = document.getElementById('buttonCode') as HTMLElement; 100 + 101 + // Populate and highlight button code 102 + const buttonCodeRaw = `<!-- Add Google Fonts to your <head> --> 103 + <link rel="preconnect" href="https://fonts.googleapis.com"> 104 + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 105 + <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300..700&display=swap" rel="stylesheet"> 106 + 107 + <!-- Button HTML --> 108 + <a href="https://your-indiko-server.com/auth/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&state=RANDOM_STATE&code_challenge=CODE_CHALLENGE&code_challenge_method=S256&scope=profile%20email" class="indiko-button"> 109 + Sign in with Indiko 110 + </a> 111 + 112 + <style> 113 + .indiko-button { 114 + position: relative; 115 + display: inline-block; 116 + padding: 1rem 2rem; 117 + background: #ab4967; 118 + color: #d9d0de; 119 + border: 4px solid #26242b; 120 + font-size: 1rem; 121 + font-weight: 700; 122 + text-decoration: none; 123 + font-family: 'Space Grotesk', sans-serif; 124 + text-transform: uppercase; 125 + letter-spacing: 0.1rem; 126 + box-shadow: 6px 6px 0 #26242b; 127 + transition: all 0.15s ease; 128 + } 129 + 130 + .indiko-button::before { 131 + content: ''; 132 + position: absolute; 133 + top: -4px; 134 + left: -4px; 135 + right: -4px; 136 + bottom: -4px; 137 + background: transparent; 138 + border: 4px solid #a04668; 139 + pointer-events: none; 140 + transition: all 0.15s ease; 141 + } 142 + 143 + .indiko-button:hover { 144 + transform: translate(3px, 3px); 145 + box-shadow: 3px 3px 0 #26242b; 146 + } 147 + 148 + .indiko-button:hover::before { 149 + top: -7px; 150 + left: -7px; 151 + right: -7px; 152 + bottom: -7px; 153 + } 154 + 155 + .indiko-button:active { 156 + transform: translate(6px, 6px); 157 + box-shadow: 0 0 0 #26242b; 158 + } 159 + </style>`; 160 + 161 + if (buttonCodeEl) { 162 + const highlighted = highlightHTMLCSS(buttonCodeRaw); 163 + buttonCodeEl.innerHTML = highlighted; 164 + } 46 165 47 166 // Auto-fill redirect URI with current page URL 48 167 const currentUrl = window.location.origin + window.location.pathname; ··· 393 512 394 513 // Copy button code to clipboard 395 514 copyButtonCodeBtn.addEventListener('click', async () => { 396 - const buttonCode = `<!-- Add Google Fonts to your <head> --> 397 - <link rel="preconnect" href="https://fonts.googleapis.com"> 398 - <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 399 - <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300..700&display=swap" rel="stylesheet"> 400 - 401 - <!-- Button HTML --> 402 - <a href="YOUR_OAUTH_URL_HERE" class="indiko-button"> 403 - Sign in with Indiko 404 - </a> 405 - 406 - <style> 407 - .indiko-button { 408 - position: relative; 409 - display: inline-block; 410 - padding: 1rem 2rem; 411 - background: #ab4967; 412 - color: #d9d0de; 413 - border: 4px solid #26242b; 414 - font-size: 1rem; 415 - font-weight: 700; 416 - text-decoration: none; 417 - font-family: 'Space Grotesk', sans-serif; 418 - text-transform: uppercase; 419 - letter-spacing: 0.1rem; 420 - box-shadow: 6px 6px 0 #26242b; 421 - transition: all 0.15s ease; 422 - } 423 - 424 - .indiko-button::before { 425 - content: ''; 426 - position: absolute; 427 - top: -4px; 428 - left: -4px; 429 - right: -4px; 430 - bottom: -4px; 431 - background: transparent; 432 - border: 4px solid #a04668; 433 - pointer-events: none; 434 - transition: all 0.15s ease; 435 - } 436 - 437 - .indiko-button:hover { 438 - transform: translate(3px, 3px); 439 - box-shadow: 3px 3px 0 #26242b; 440 - } 441 - 442 - .indiko-button:hover::before { 443 - top: -7px; 444 - left: -7px; 445 - right: -7px; 446 - bottom: -7px; 447 - } 448 - 449 - .indiko-button:active { 450 - transform: translate(6px, 6px); 451 - box-shadow: 0 0 0 #26242b; 452 - } 453 - </style>`; 454 - 455 515 try { 456 - await navigator.clipboard.writeText(buttonCode); 516 + await navigator.clipboard.writeText(buttonCodeRaw); 457 517 copyButtonCodeBtn.textContent = 'copied! ✓'; 458 518 setTimeout(() => { 459 519 copyButtonCodeBtn.textContent = 'copy button code';
+97 -75
src/html/docs.html
··· 182 182 color: var(--old-rose); 183 183 } 184 184 185 + /* HTML/CSS syntax highlighting */ 186 + .html-tag { 187 + color: var(--berry-crush); 188 + } 189 + 190 + .html-attr { 191 + color: var(--old-rose); 192 + } 193 + 194 + .html-string { 195 + color: #a5d6a7; 196 + } 197 + 198 + .html-comment { 199 + color: #7a7a7a; 200 + font-style: italic; 201 + } 202 + 203 + .css-selector { 204 + color: var(--berry-crush); 205 + } 206 + 207 + .css-property { 208 + color: var(--old-rose); 209 + } 210 + 211 + .css-value { 212 + color: #a5d6a7; 213 + } 214 + 215 + .css-unit { 216 + color: #81c784; 217 + } 218 + 185 219 .token.property, 186 220 .token.tag, 187 221 .token.boolean, ··· 467 501 border-color: var(--rosewood); 468 502 background: rgba(160, 70, 104, 0.1); 469 503 } 504 + 505 + /* Demo button styles */ 506 + .demo-button-wrapper { 507 + background: rgba(12, 23, 19, 0.6); 508 + padding: 2rem; 509 + margin: 1.5rem 0; 510 + display: flex; 511 + justify-content: center; 512 + } 513 + 514 + .indiko-demo-button { 515 + position: relative; 516 + display: inline-block; 517 + padding: 1rem 2rem; 518 + background: var(--berry-crush); 519 + color: var(--lavender); 520 + border: 4px solid var(--mahogany); 521 + font-size: 1rem; 522 + font-weight: 700; 523 + text-decoration: none; 524 + font-family: "Space Grotesk", sans-serif; 525 + text-transform: uppercase; 526 + letter-spacing: 0.1rem; 527 + box-shadow: 6px 6px 0 var(--mahogany); 528 + transition: all 0.15s ease; 529 + } 530 + 531 + .indiko-demo-button::before { 532 + content: ''; 533 + position: absolute; 534 + top: -4px; 535 + left: -4px; 536 + right: -4px; 537 + bottom: -4px; 538 + background: transparent; 539 + border: 4px solid var(--rosewood); 540 + pointer-events: none; 541 + transition: all 0.15s ease; 542 + } 543 + 544 + .indiko-demo-button:hover { 545 + transform: translate(3px, 3px); 546 + box-shadow: 3px 3px 0 var(--mahogany); 547 + } 548 + 549 + .indiko-demo-button:hover::before { 550 + top: -7px; 551 + left: -7px; 552 + right: -7px; 553 + bottom: -7px; 554 + } 555 + 556 + .indiko-demo-button:active { 557 + transform: translate(6px, 6px); 558 + box-shadow: 0 0 0 var(--mahogany); 559 + } 560 + 561 + .indiko-demo-button:hover { 562 + text-decoration: none; 563 + } 470 564 </style> 471 565 </head> 472 566 ··· 552 646 Copy this themed button for your app's login page. It matches Indiko's visual style: 553 647 </p> 554 648 555 - <div style="background: rgba(12, 23, 19, 0.6); padding: 2rem; margin: 1.5rem 0; display: flex; justify-content: center;"> 556 - <a href="#" id="demoButton" style=" 557 - position: relative; 558 - display: inline-block; 559 - padding: 1rem 2rem; 560 - background: #ab4967; 561 - color: #d9d0de; 562 - border: 4px solid #26242b; 563 - font-size: 1rem; 564 - font-weight: 700; 565 - text-decoration: none; 566 - font-family: 'Space Grotesk', sans-serif; 567 - text-transform: uppercase; 568 - letter-spacing: 0.1rem; 569 - box-shadow: 6px 6px 0 #26242b; 570 - transition: all 0.15s ease; 571 - ">Sign in with Indiko</a> 649 + <div class="demo-button-wrapper"> 650 + <a href="#" id="demoButton" class="indiko-demo-button">Sign in with Indiko</a> 572 651 </div> 573 652 574 653 <h3>HTML + CSS</h3> 575 - <pre><code>&lt;!-- Add Google Fonts to your &lt;head&gt; --&gt; 576 - &lt;link rel="preconnect" href="https://fonts.googleapis.com"&gt; 577 - &lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin&gt; 578 - &lt;link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300..700&amp;display=swap" rel="stylesheet"&gt; 579 - 580 - &lt;!-- Button HTML --&gt; 581 - &lt;a href="YOUR_OAUTH_URL_HERE" class="indiko-button"&gt; 582 - Sign in with Indiko 583 - &lt;/a&gt; 584 - 585 - &lt;style&gt; 586 - .indiko-button { 587 - position: relative; 588 - display: inline-block; 589 - padding: 1rem 2rem; 590 - background: #ab4967; 591 - color: #d9d0de; 592 - border: 4px solid #26242b; 593 - font-size: 1rem; 594 - font-weight: 700; 595 - text-decoration: none; 596 - font-family: 'Space Grotesk', sans-serif; 597 - text-transform: uppercase; 598 - letter-spacing: 0.1rem; 599 - box-shadow: 6px 6px 0 #26242b; 600 - transition: all 0.15s ease; 601 - } 602 - 603 - .indiko-button::before { 604 - content: ''; 605 - position: absolute; 606 - top: -4px; 607 - left: -4px; 608 - right: -4px; 609 - bottom: -4px; 610 - background: transparent; 611 - border: 4px solid #a04668; 612 - pointer-events: none; 613 - transition: all 0.15s ease; 614 - } 615 - 616 - .indiko-button:hover { 617 - transform: translate(3px, 3px); 618 - box-shadow: 3px 3px 0 #26242b; 619 - } 620 - 621 - .indiko-button:hover::before { 622 - top: -7px; 623 - left: -7px; 624 - right: -7px; 625 - bottom: -7px; 626 - } 627 - 628 - .indiko-button:active { 629 - transform: translate(6px, 6px); 630 - box-shadow: 0 0 0 #26242b; 631 - } 632 - &lt;/style&gt;</code></pre> 654 + <pre><code id="buttonCode"></code></pre> 633 655 634 656 <button id="copyButtonCode" class="copy-btn">copy button code</button> 635 657