Ctrl K
ComunidadGuías…

mockups/pr_files.html

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Files changed · Add OAuth scopes to access tokens · PR #341 · mkappworks/cloudzilla — Cloudzilla</title>
<link rel="stylesheet" href="/assets/tailwind.css" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500&family=Geist:wght@400;500;600&display=swap" rel="stylesheet" />
</head>
<body class="min-h-screen flex flex-col antialiased font-sans">
<a href="#main-content" class="skip-link">Skip to main content</a>

<div id="cz-header" data-repo-tab="pull_requests"></div>

<main id="main-content" tabindex="-1" class="max-w-[1400px] mx-auto px-6 pt-6 pb-20 flex-1 w-full">
  <div>
    <section class="mb-3 border-b border-border pb-3" aria-labelledby="pull-title">
      <p class="font-mono text-[11px] text-muted-foreground uppercase tracking-wider mb-2">
        <a href="repo.html" class="hover:underline">cloudzilla</a>
        <span aria-hidden="true">·</span>
        <a href="pulls.html" class="hover:underline">Pull requests</a>
        <span aria-hidden="true">·</span>
        <span aria-current="page">#341</span>
      </p>
      <div class="flex flex-wrap items-start justify-between gap-3 mb-3">
        <h1 id="pull-title" class="text-2xl tracking-tight font-medium min-w-0">
          <span id="pr-title-text">Add OAuth scopes to access tokens</span>
          <span class="text-muted-foreground font-normal" aria-label="Pull request number 341">#341</span>
        </h1>
        <div class="flex items-center gap-1.5 shrink-0">
          <button type="button" id="edit-pr-btn" class="h-8 px-3 text-[13px] border border-border rounded-md hover:bg-accent">Edit</button>
          <div class="relative inline-block" data-dropdown>
            <button type="button" class="h-8 px-3 text-[13px] border border-border rounded-md hover:bg-accent inline-flex items-center gap-1.5" aria-haspopup="menu" aria-expanded="false">
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true" focusable="false"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg>
              Code
              <svg class="text-muted-foreground" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true" focusable="false"><path d="M7 10l5 5 5-5"></path></svg>
            </button>
            <div role="menu" class="hidden absolute right-0 top-full mt-1.5 z-50 min-w-[12rem] rounded-md border border-border bg-popover text-popover-foreground shadow-md p-1 text-sm">
              <div class="px-2 py-1.5 text-[11px] uppercase tracking-wider text-muted-foreground">Check out — daisy/oauth-scopes</div>
              <pre class="font-mono text-[11px] text-muted-foreground m-1 p-2 rounded-sm border border-border bg-muted overflow-x-auto whitespace-pre" onclick="event.stopPropagation()">git fetch origin
git checkout daisy/oauth-scopes</pre>
              <div role="separator" class="my-1 h-px bg-border"></div>
              <a href="tree.html" role="menuitem" class="flex items-center gap-2 px-2 py-1.5 rounded-xs hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-hidden whitespace-nowrap">Browse branch code</a>
            </div>
          </div>
        </div>
      </div>
      <div class="flex flex-wrap items-center gap-3">
        <span class="inline-flex items-center gap-1.5 h-[22px] px-2 rounded-full border text-[11px] font-medium bg-success/15 text-success border-success/30">Open</span>
        <p class="text-[13px] text-muted-foreground">
          <a href="profile.html" class="font-medium text-foreground underline underline-offset-2 decoration-muted-foreground/50 hover:decoration-foreground">@daisy</a>
          wants to merge
          <strong class="text-foreground font-medium">3 commits</strong>
          into
          <span class="font-mono px-1.5 py-0.5 bg-muted border border-border rounded-sm text-[12px]">main</span>
          from
          <span class="font-mono px-1.5 py-0.5 bg-muted border border-border rounded-sm text-[12px]">daisy/oauth-scopes</span>
        </p>
      </div>
    </section>
    <nav class="border-b border-border mb-6 py-1.5 flex flex-wrap items-center gap-1 text-sm" aria-label="Pull request views">
      <a href="pr.html" class="px-3 py-1.5 rounded-md text-muted-foreground hover:text-foreground hover:bg-accent inline-flex items-center gap-2 whitespace-nowrap">
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true" focusable="false"><path d="M21 11.5a8.38 8.38 0 01-.9 3.8 8.5 8.5 0 01-7.6 4.7 8.38 8.38 0 01-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 01-.9-3.8 8.5 8.5 0 014.7-7.6 8.38 8.38 0 013.8-.9h.5a8.48 8.48 0 018 8v.5z"></path></svg>
        Conversation
        <span class="font-mono text-[11px] text-muted-foreground">6</span>
      </a>
      <a href="pr_commits.html" class="px-3 py-1.5 rounded-md text-muted-foreground hover:text-foreground hover:bg-accent inline-flex items-center gap-2 whitespace-nowrap">
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true" focusable="false"><circle cx="12" cy="6" r="3"></circle><circle cx="12" cy="18" r="3"></circle><path d="M12 9v6"></path></svg>
        Commits
        <span class="font-mono text-[11px] text-muted-foreground">3</span>
      </a>
      <a href="pr_checks.html" class="px-3 py-1.5 rounded-md text-muted-foreground hover:text-foreground hover:bg-accent inline-flex items-center gap-2 whitespace-nowrap">
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true" focusable="false"><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="6" r="3"></circle><circle cx="18" cy="18" r="3"></circle><path d="M9 12h6M15 6L9 10M15 18l-6-4"></path></svg>
        Checks
        <span class="inline-flex items-center gap-1 h-[18px] px-1.5 rounded-full text-[10px] font-medium bg-success/15 text-success">✓ 6</span>
      </a>
      <a href="pr_files.html" class="px-3 py-1.5 rounded-md bg-accent text-foreground font-medium inline-flex items-center gap-2 whitespace-nowrap" aria-current="page">
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true" focusable="false"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>
        Files changed
        <span class="font-mono text-[11px] text-muted-foreground">4</span>
        <span class="font-mono text-[10px] font-medium text-success">+41</span>
        <span class="font-mono text-[10px] font-medium text-destructive">−5</span>
      </a>
    </nav>
    <section class="mb-4 flex items-center justify-between gap-3 flex-wrap" aria-label="Diff summary">
      <div class="flex items-center gap-2 text-[12.5px] text-muted-foreground">
        <span><strong class="text-foreground">4</strong> changed files</span>
        <span aria-hidden="true" class="opacity-50">·</span>
        <span class="font-mono text-success">+41</span>
        <span class="font-mono text-destructive">−5</span>
      </div>
      <div class="flex items-center gap-1.5">
        <div class="inline-flex items-center border border-border rounded-md overflow-hidden" role="group" aria-label="Diff layout">
          <button type="button" id="diff-view-unified" data-view="unified" aria-pressed="true" class="h-8 px-3 text-[12.5px] inline-flex items-center gap-1.5 hover:bg-accent bg-muted">
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true"><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>
            Unified
          </button>
          <button type="button" id="diff-view-split" data-view="split" aria-pressed="false" class="h-8 px-3 text-[12.5px] inline-flex items-center gap-1.5 hover:bg-accent text-muted-foreground border-l border-border">
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true"><rect x="3" y="3" width="8" height="18" rx="1"></rect><rect x="13" y="3" width="8" height="18" rx="1"></rect></svg>
            Split
          </button>
        </div>
        <button type="button" id="diff-ws-toggle" aria-pressed="false" class="h-8 px-3 text-[12.5px] rounded-md border border-border hover:bg-accent text-muted-foreground inline-flex items-center gap-1.5">
          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true"><path d="M17.94 17.94A10.07 10.07 0 0112 20C5 20 1 12 1 12a18.45 18.45 0 015.06-5.94M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19m-6.72-1.07a3 3 0 11-4.24-4.24"></path><line x1="1" y1="1" x2="23" y2="23"></line></svg>
          Hide whitespace
        </button>
        <button type="button" id="diff-single-file" data-state="off" aria-pressed="false" class="h-8 px-3 rounded-md border border-border hover:bg-accent text-muted-foreground text-[12.5px] inline-flex items-center gap-1.5">
          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>
          Single file
        </button>
        <button type="button" id="commit-suggestions" data-when="batched" data-needs-write hidden class="inline-flex items-center justify-center gap-1.5 rounded-md font-medium transition-colors border border-input bg-transparent hover:bg-accent hover:text-accent-foreground h-8 px-3 text-[13px]">
          Commit suggestions
          <span class="font-mono text-[11px] text-muted-foreground">1</span>
        </button>
        <button type="button" data-needs-write onclick="document.getElementById('review-dialog').showModal()" class="inline-flex items-center justify-center gap-1.5 rounded-md font-medium transition-colors bg-success text-success-foreground hover:bg-success/90 h-8 px-3 text-[13px]">
          Review changes
          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="M7 10l5 5 5-5"></path></svg>
        </button>
      </div>
    </section>
    <dialog id="review-dialog" aria-labelledby="review-dialog-title" class="rounded-lg border border-border bg-popover text-popover-foreground p-0 w-[480px] max-w-[calc(100vw-2rem)] max-h-[calc(100dvh-2rem)] overflow-y-auto mt-[28vh] mb-auto translate-y-[min(0px,calc(100dvh-28vh-1rem-100%))] backdrop:bg-black/60 backdrop:backdrop-blur-xs">
      <div class="sticky top-0 z-10 bg-popover flex flex-col gap-1 p-5 border-b border-border">
        <h2 id="review-dialog-title" class="text-base font-semibold leading-tight">Review changes</h2>
        <p class="text-[13px] text-muted-foreground leading-snug">Line comments you add on the Files tab are posted as you write them. This adds your summary and verdict to <strong class="text-foreground">#341</strong>.</p>
      </div>
      <form id="review-form" onsubmit="document.getElementById('review-dialog').close(); czToast('Review submitted'); return false">
        <div class="p-5 text-sm">
          <label for="review-body" class="sr-only">Review summary</label>
          <div class="rounded-md border border-border overflow-hidden">
            <textarea id="review-body" name="body" rows="4" required placeholder="Leave a summary — Markdown supported" class="w-full px-3 py-3 text-[13px] font-mono leading-relaxed bg-transparent resize-none focus:outline-hidden placeholder:text-muted-foreground"></textarea>
          </div>
          <div role="radiogroup" aria-label="Review type" class="grid gap-3 mt-4">
            <label class="flex items-start gap-3 cursor-pointer">
              <input type="radio" name="state" value="commented" checked class="h-4 w-4 shrink-0 border border-input bg-transparent accent-ring checked:border-ring disabled:cursor-not-allowed disabled:opacity-50 mt-0.5" />
              <span class="text-sm">
                <span class="font-medium block">Comment</span>
                <span class="text-muted-foreground text-[12px]">General feedback, without approving or blocking.</span>
              </span>
            </label>
            <label class="flex items-start gap-3 cursor-pointer">
              <input type="radio" name="state" value="approved" class="h-4 w-4 shrink-0 border border-input bg-transparent accent-ring checked:border-ring disabled:cursor-not-allowed disabled:opacity-50 mt-0.5" />
              <span class="text-sm">
                <span class="font-medium block">Approve</span>
                <span class="text-muted-foreground text-[12px]">Counts toward the required approvals for main.</span>
              </span>
            </label>
            <label class="flex items-start gap-3 cursor-pointer">
              <input type="radio" name="state" value="changes_requested" class="h-4 w-4 shrink-0 border border-input bg-transparent accent-ring checked:border-ring disabled:cursor-not-allowed disabled:opacity-50 mt-0.5" />
              <span class="text-sm">
                <span class="font-medium block">Request changes</span>
                <span class="text-muted-foreground text-[12px]">Blocks merging until you approve or your review is dismissed.</span>
              </span>
            </label>
          </div>
        </div>
      </form>
      <div class="sticky bottom-0 z-10 bg-popover flex items-center justify-end gap-2 p-5 border-t border-border">
        <form method="dialog" class="contents">
          <button type="submit" class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors border border-input bg-transparent hover:bg-accent hover:text-accent-foreground h-9 px-4">Cancel</button>
        </form>
        <button type="submit" form="review-form" class="inline-flex items-center justify-center gap-1.5 rounded-md font-medium transition-colors bg-success text-success-foreground hover:bg-success/90 h-9 px-4 text-sm">Submit review</button>
      </div>
    </dialog>
    <div id="diff-root" data-view="unified" class="grid lg:grid-cols-[260px_1fr] gap-4">
      <aside class="border border-border rounded-md bg-card p-3 lg:sticky lg:top-4 self-start max-h-[80vh] overflow-auto" aria-label="Files in this pull request">
        <div class="flex items-center justify-between gap-2 mb-2 px-1">
          <p class="font-mono text-[10px] text-muted-foreground uppercase tracking-wider">Changed files</p>
          <div class="inline-flex items-center border border-border rounded-sm overflow-hidden" role="group" aria-label="File list layout">
            <button type="button" id="diff-files-list-btn" aria-pressed="true" title="List view" aria-label="List view" class="px-1.5 py-1 inline-flex hover:bg-accent bg-muted">
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true"><line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><line x1="3" y1="6" x2="3.01" y2="6"></line><line x1="3" y1="12" x2="3.01" y2="12"></line><line x1="3" y1="18" x2="3.01" y2="18"></line></svg>
            </button>
            <button type="button" id="diff-files-tree-btn" aria-pressed="false" title="Tree view" aria-label="Tree view" class="px-1.5 py-1 inline-flex hover:bg-accent text-muted-foreground border-l border-border">
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true"><path d="M21 12h-8"></path><path d="M21 6H8"></path><path d="M21 18h-8"></path><path d="M3 6v4c0 1.1.9 2 2 2h3"></path><path d="M3 10v6c0 1.1.9 2 2 2h3"></path></svg>
            </button>
          </div>
        </div>
        <ul id="diff-files-list" class="text-[12.5px] space-y-0.5">
          <li>
            <a href="#diff-0" class="flex items-center gap-1.5 px-2 py-1 rounded-md hover:bg-accent text-muted-foreground hover:text-foreground">
              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="shrink-0 opacity-70" aria-hidden="true"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>
              <span class="truncate flex-1 font-mono">docs/api-reference.md</span>
              <span class="font-mono text-[10px] text-success">+9</span>
            </a>
          </li>
          <li>
            <a href="#diff-1" class="flex items-center gap-1.5 px-2 py-1 rounded-md hover:bg-accent text-muted-foreground hover:text-foreground">
              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="shrink-0 opacity-70" aria-hidden="true"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>
              <span class="truncate flex-1 font-mono">internal/db/migrations/0042_access_token_scopes.sql</span>
              <span class="font-mono text-[10px] text-success">+12</span>
            </a>
          </li>
          <li>
            <a href="#diff-2" class="flex items-center gap-1.5 px-2 py-1 rounded-md hover:bg-accent text-muted-foreground hover:text-foreground">
              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="shrink-0 opacity-70" aria-hidden="true"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>
              <span class="truncate flex-1 font-mono">internal/middleware/auth.go</span>
              <span class="font-mono text-[10px] text-success">+14</span><span class="font-mono text-[10px] text-destructive">−5</span>
            </a>
          </li>
          <li>
            <a href="#diff-3" class="flex items-center gap-1.5 px-2 py-1 rounded-md hover:bg-accent text-muted-foreground hover:text-foreground">
              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="shrink-0 opacity-70" aria-hidden="true"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>
              <span class="truncate flex-1 font-mono">internal/store/token_store.go</span>
              <span class="font-mono text-[10px] text-success">+6</span>
            </a>
          </li>
        </ul>
        <ul id="diff-files-tree" class="text-[12.5px] space-y-0.5 hidden">
          <li data-open="true">
            <button type="button" class="diff-tree-folder w-full flex items-center gap-1.5 px-2 py-1 rounded-md hover:bg-accent text-muted-foreground hover:text-foreground" style="padding-left: 8px">
              <svg class="diff-tree-caret shrink-0 transition-transform" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" aria-hidden="true"><polyline points="6 9 12 15 18 9"></polyline></svg>
              <span class="truncate font-mono">docs</span>
            </button>
            <ul>
          <li>
            <a href="#diff-0" class="flex items-center gap-1.5 px-2 py-1 rounded-md hover:bg-accent text-muted-foreground hover:text-foreground" style="padding-left: 20px">
              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="shrink-0 opacity-70" aria-hidden="true"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>
              <span class="truncate flex-1 font-mono">api-reference.md</span>
              <span class="font-mono text-[10px] text-success">+9</span>
            </a>
          </li>
            </ul>
          </li>
          <li data-open="true">
            <button type="button" class="diff-tree-folder w-full flex items-center gap-1.5 px-2 py-1 rounded-md hover:bg-accent text-muted-foreground hover:text-foreground" style="padding-left: 8px">
              <svg class="diff-tree-caret shrink-0 transition-transform" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" aria-hidden="true"><polyline points="6 9 12 15 18 9"></polyline></svg>
              <span class="truncate font-mono">internal</span>
            </button>
            <ul>
          <li data-open="true">
            <button type="button" class="diff-tree-folder w-full flex items-center gap-1.5 px-2 py-1 rounded-md hover:bg-accent text-muted-foreground hover:text-foreground" style="padding-left: 20px">
              <svg class="diff-tree-caret shrink-0 transition-transform" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" aria-hidden="true"><polyline points="6 9 12 15 18 9"></polyline></svg>
              <span class="truncate font-mono">db</span>
            </button>
            <ul>
          <li data-open="true">
            <button type="button" class="diff-tree-folder w-full flex items-center gap-1.5 px-2 py-1 rounded-md hover:bg-accent text-muted-foreground hover:text-foreground" style="padding-left: 32px">
              <svg class="diff-tree-caret shrink-0 transition-transform" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" aria-hidden="true"><polyline points="6 9 12 15 18 9"></polyline></svg>
              <span class="truncate font-mono">migrations</span>
            </button>
            <ul>
          <li>
            <a href="#diff-1" class="flex items-center gap-1.5 px-2 py-1 rounded-md hover:bg-accent text-muted-foreground hover:text-foreground" style="padding-left: 44px">
              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="shrink-0 opacity-70" aria-hidden="true"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>
              <span class="truncate flex-1 font-mono">0042_access_token_scopes.sql</span>
              <span class="font-mono text-[10px] text-success">+12</span>
            </a>
          </li>
            </ul>
          </li>
            </ul>
          </li>
          <li data-open="true">
            <button type="button" class="diff-tree-folder w-full flex items-center gap-1.5 px-2 py-1 rounded-md hover:bg-accent text-muted-foreground hover:text-foreground" style="padding-left: 20px">
              <svg class="diff-tree-caret shrink-0 transition-transform" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" aria-hidden="true"><polyline points="6 9 12 15 18 9"></polyline></svg>
              <span class="truncate font-mono">middleware</span>
            </button>
            <ul>
          <li>
            <a href="#diff-2" class="flex items-center gap-1.5 px-2 py-1 rounded-md hover:bg-accent text-muted-foreground hover:text-foreground" style="padding-left: 32px">
              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="shrink-0 opacity-70" aria-hidden="true"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>
              <span class="truncate flex-1 font-mono">auth.go</span>
              <span class="font-mono text-[10px] text-success">+14</span><span class="font-mono text-[10px] text-destructive">−5</span>
            </a>
          </li>
            </ul>
          </li>
          <li data-open="true">
            <button type="button" class="diff-tree-folder w-full flex items-center gap-1.5 px-2 py-1 rounded-md hover:bg-accent text-muted-foreground hover:text-foreground" style="padding-left: 20px">
              <svg class="diff-tree-caret shrink-0 transition-transform" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" aria-hidden="true"><polyline points="6 9 12 15 18 9"></polyline></svg>
              <span class="truncate font-mono">store</span>
            </button>
            <ul>
          <li>
            <a href="#diff-3" class="flex items-center gap-1.5 px-2 py-1 rounded-md hover:bg-accent text-muted-foreground hover:text-foreground" style="padding-left: 32px">
              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" class="shrink-0 opacity-70" aria-hidden="true"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>
              <span class="truncate flex-1 font-mono">token_store.go</span>
              <span class="font-mono text-[10px] text-success">+6</span>
            </a>
          </li>
            </ul>
          </li>
            </ul>
          </li>
        </ul>
      </aside>
      <div class="space-y-6 min-w-0">
        <section id="diff-0" class="diff-file border border-border rounded-md overflow-hidden scroll-mt-6">
          <header class="diff-file-header px-3 py-2 border-b border-border bg-muted/40 flex items-center gap-2 text-[12.5px] cursor-pointer select-none focus-visible:outline-solid focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring" role="button" tabindex="0" aria-expanded="true">
            <svg class="diff-file-caret shrink-0 text-muted-foreground transition-transform" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"></polyline></svg>
            <span class="font-mono font-medium truncate">docs/api-reference.md</span>
            <span class="ml-auto font-mono text-[11px] shrink-0">
              <span class="text-success">+9</span>
              
            </span>
            <a href="blob.html" class="text-[12px] text-muted-foreground hover:text-foreground shrink-0">View file →</a>
          </header>
          <div class="diff-file-body">
            <div class="diff-unified">
<div class="hl overflow-x-auto">
<table class="w-full border-collapse text-xs font-mono">
<tbody>
<tr><td colspan="2" class="bg-primary/5 text-muted-foreground px-4 py-1 select-none border-y border-border">@@ -118,6 +118,14 @@ ## Access tokens</td></tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">118<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> Personal access tokens authenticate API requests with a bearer header.</td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">119<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> </td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">120<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+### Scopes</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">121<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">122<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td id="sugg-target" class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+A token carries a set of scopes. A request outside the token&#x27;s scopes is rejected with `403`.</td>
</tr>
<tr data-thread class="bg-primary/10 border-t border-primary/20">
<td colspan="2" class="px-4 py-0">
<div class="flex items-start gap-2 py-2 border-b border-primary/20 last:border-0">
<span class="flex shrink-0 mt-0.5"><span class="inline-grid place-items-center rounded-full bg-muted border border-border font-medium text-muted-foreground h-6 w-6 text-[10px]" aria-hidden="true">Y</span></span>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2">
<a href="profile.html" class="text-xs font-semibold hover:underline">yumi</a>
<span class="text-xs text-muted-foreground">Nov 6, 2026</span>
</div>
<div class="text-xs text-foreground mt-0.5 prose prose-xs dark:prose-invert max-w-none"><p>Token already has a helper for this, and it handles the wildcard.</p></div>
<div class="mt-2 mb-1 rounded-md border border-border bg-card overflow-hidden font-sans">
<div class="px-3 py-1.5 border-b border-border bg-muted/40 flex items-center gap-2 text-[11px] text-muted-foreground">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true"><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 113 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
Suggested change
<span data-when="applied" hidden class="inline-flex items-center gap-1.5 h-[22px] px-2 rounded-full border text-[11px] font-medium bg-success/15 text-success border-success/30">Applied</span>
<span data-when="batched" hidden class="inline-flex items-center gap-1.5 h-[22px] px-2 rounded-full border text-[11px] font-medium bg-secondary text-secondary-foreground border-transparent">In batch</span>
<span class="ml-auto font-mono">line 122</span>
</div>
<table class="w-full border-collapse text-xs font-mono">
<tbody>
<tr class="bg-destructive/10"><td class="w-10 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5">122</td><td class="pl-3 pr-4 py-0.5 whitespace-pre text-destructive">-A token carries a set of scopes. A request outside the token&#x27;s scopes is rejected with `403`.</td></tr>
<tr class="bg-success/10"><td class="w-10 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5">122</td><td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+A token carries a set of scopes (`*` grants all). A request outside them is rejected with `403`.</td></tr>
</tbody>
</table>
<div data-when="applied" hidden class="px-3 py-2 border-t border-border">
<p class="text-[12px] text-muted-foreground">
<span class="text-foreground">daisy</span> applied this in
<a href="commit.html" class="font-mono text-foreground hover:underline">4e1c0d2</a>
· Nov 7, 2026
</p>
</div>
<div data-when="pending" data-needs-write class="px-3 py-2 border-t border-border flex flex-wrap items-center gap-2">
<button type="button" data-set="applied" class="inline-flex items-center justify-center gap-1.5 rounded-md font-medium transition-colors bg-success text-success-foreground hover:bg-success/90 h-8 px-3 text-[13px]">Apply suggestion</button>
<button type="button" data-set="batched" class="inline-flex items-center justify-center gap-1.5 rounded-md font-medium transition-colors border border-input bg-transparent hover:bg-accent hover:text-accent-foreground h-8 px-3 text-[13px]">Add to batch</button>
</div>
<div data-when="batched" data-needs-write hidden class="px-3 py-2 border-t border-border flex flex-wrap items-center gap-2">
<button type="button" data-set="pending" class="inline-flex items-center justify-center gap-1.5 rounded-md font-medium transition-colors border border-input bg-transparent hover:bg-accent hover:text-accent-foreground h-8 px-3 text-[13px]">Remove from batch</button>
<p class="text-[12px] text-muted-foreground">Committed together with the other batched suggestions.</p>
</div>
</div>
</div>
<button type="button" class="text-muted-foreground/60 hover:text-destructive text-xs shrink-0" aria-label="Delete comment">&times;</button>
</div>
</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">123<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">124<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+| Scope        | Grants                          |</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">125<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+| ------------ | ------------------------------- |</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">126<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+| `repo:read`  | Read repositories and metadata  |</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">127<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+| `repo:write` | Push, open and merge pull requests |</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">128<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+</td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">129<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> Tokens created before scopes existed are migrated to `scopes=*`.</td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">130<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> </td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">131<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> ## Errors</td>
</tr>
</tbody>
</table>
</div>
            </div>
            <div class="diff-split">
<div class="hl overflow-x-auto">
<table class="w-full table-fixed border-collapse text-xs font-mono">
<colgroup><col class="w-10"/><col/><col class="w-10"/><col/></colgroup>
<tbody>
<tr><td colspan="4" class="bg-primary/5 text-muted-foreground px-4 py-1 select-none border-y border-border">@@ -118,6 +118,14 @@ ## Access tokens</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">118</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> Personal access tokens authenticate API requests with a bearer header.</td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">118</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> Personal access tokens authenticate API requests with a bearer header.</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">119</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> </td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">119</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> </td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">120</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+### Scopes</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">121</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">122</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+A token carries a set of scopes. A request outside the token&#x27;s scopes is rejected with `403`.</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">123</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">124</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+| Scope        | Grants                          |</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">125</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+| ------------ | ------------------------------- |</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">126</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+| `repo:read`  | Read repositories and metadata  |</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">127</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+| `repo:write` | Push, open and merge pull requests |</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">128</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">120</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> Tokens created before scopes existed are migrated to `scopes=*`.</td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">129</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> Tokens created before scopes existed are migrated to `scopes=*`.</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">121</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> </td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">130</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> </td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">122</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> ## Errors</td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">131</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> ## Errors</td></tr>
</tbody>
</table>
</div>
            </div>
          </div>
        </section>
        <section id="diff-1" class="diff-file border border-border rounded-md overflow-hidden scroll-mt-6">
          <header class="diff-file-header px-3 py-2 border-b border-border bg-muted/40 flex items-center gap-2 text-[12.5px] cursor-pointer select-none focus-visible:outline-solid focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring" role="button" tabindex="0" aria-expanded="true">
            <svg class="diff-file-caret shrink-0 text-muted-foreground transition-transform" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"></polyline></svg>
            <span class="font-mono font-medium truncate">internal/db/migrations/0042_access_token_scopes.sql</span>
            <span class="ml-auto font-mono text-[11px] shrink-0">
              <span class="text-success">+12</span>
              
            </span>
            <a href="blob.html" class="text-[12px] text-muted-foreground hover:text-foreground shrink-0">View file →</a>
          </header>
          <div class="diff-file-body">
            <div class="diff-unified">
<div class="hl overflow-x-auto">
<table class="w-full border-collapse text-xs font-mono">
<tbody>
<tr><td colspan="2" class="bg-primary/5 text-muted-foreground px-4 py-1 select-none border-y border-border">@@ -0,0 +1,12 @@</td></tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">1<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+CREATE TABLE access_token_scopes (</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">2<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+    id              BIGSERIAL PRIMARY KEY,</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">3<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+    access_token_id BIGINT NOT NULL REFERENCES access_tokens (id) ON DELETE CASCADE,</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">4<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+    scope           TEXT   NOT NULL,</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">5<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+    UNIQUE (access_token_id, scope)</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">6<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+);</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">7<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">8<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+INSERT INTO access_token_scopes (access_token_id, scope)</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">9<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+SELECT id, &#x27;*&#x27; FROM access_tokens;</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">10<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">11<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+CREATE INDEX idx_access_token_scopes_token ON access_token_scopes (access_token_id);</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">12<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+</td>
</tr>
</tbody>
</table>
</div>
            </div>
            <div class="diff-split">
<div class="hl overflow-x-auto">
<table class="w-full border-collapse text-xs font-mono">
<tbody>
<tr><td colspan="2" class="bg-primary/5 text-muted-foreground px-4 py-1 select-none border-y border-border">@@ -0,0 +1,12 @@</td></tr>
<tr class="bg-success/6 dark:bg-success/10"><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">1</td><td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+CREATE TABLE access_token_scopes (</td></tr>
<tr class="bg-success/6 dark:bg-success/10"><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">2</td><td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+    id              BIGSERIAL PRIMARY KEY,</td></tr>
<tr class="bg-success/6 dark:bg-success/10"><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">3</td><td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+    access_token_id BIGINT NOT NULL REFERENCES access_tokens (id) ON DELETE CASCADE,</td></tr>
<tr class="bg-success/6 dark:bg-success/10"><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">4</td><td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+    scope           TEXT   NOT NULL,</td></tr>
<tr class="bg-success/6 dark:bg-success/10"><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">5</td><td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+    UNIQUE (access_token_id, scope)</td></tr>
<tr class="bg-success/6 dark:bg-success/10"><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">6</td><td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+);</td></tr>
<tr class="bg-success/6 dark:bg-success/10"><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">7</td><td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+</td></tr>
<tr class="bg-success/6 dark:bg-success/10"><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">8</td><td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+INSERT INTO access_token_scopes (access_token_id, scope)</td></tr>
<tr class="bg-success/6 dark:bg-success/10"><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">9</td><td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+SELECT id, &#x27;*&#x27; FROM access_tokens;</td></tr>
<tr class="bg-success/6 dark:bg-success/10"><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">10</td><td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+</td></tr>
<tr class="bg-success/6 dark:bg-success/10"><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">11</td><td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+CREATE INDEX idx_access_token_scopes_token ON access_token_scopes (access_token_id);</td></tr>
<tr class="bg-success/6 dark:bg-success/10"><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">12</td><td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+</td></tr>
</tbody>
</table>
</div>
            </div>
          </div>
        </section>
        <section id="diff-2" class="diff-file border border-border rounded-md overflow-hidden scroll-mt-6">
          <header class="diff-file-header px-3 py-2 border-b border-border bg-muted/40 flex items-center gap-2 text-[12.5px] cursor-pointer select-none focus-visible:outline-solid focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring" role="button" tabindex="0" aria-expanded="true">
            <svg class="diff-file-caret shrink-0 text-muted-foreground transition-transform" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"></polyline></svg>
            <span class="font-mono font-medium truncate">internal/middleware/auth.go</span>
            <span class="ml-auto font-mono text-[11px] shrink-0">
              <span class="text-success">+14</span>
              <span class="text-destructive ml-1">−5</span>
            </span>
            <a href="blob.html" class="text-[12px] text-muted-foreground hover:text-foreground shrink-0">View file →</a>
          </header>
          <div class="diff-file-body">
            <div class="diff-unified">
<div class="hl overflow-x-auto">
<table class="w-full border-collapse text-xs font-mono">
<tbody>
<tr><td colspan="2" class="bg-primary/5 text-muted-foreground px-4 py-1 select-none border-y border-border">@@ -68,12 +68,24 @@ func RequireAuth(next http.Handler) http.Handler {</td></tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">68<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> 			return</td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">69<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> 		}</td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">70<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> </td>
</tr>
<tr class="diff-line ws-only bg-destructive/6 dark:bg-destructive/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">71</td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-destructive">-		next.ServeHTTP(w, r)</td>
</tr>
<tr class="diff-line ws-only bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">71<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+			next.ServeHTTP(w, r)</td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">72<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> 	})</td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">73<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> }</td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">74<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> </td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">75<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+// RequireScope rejects requests whose token lacks the scope.</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">76<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+func RequireScope(scope string) func(http.Handler) http.Handler {</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">77<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+	return func(next http.Handler) http.Handler {</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">78<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">79<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+			token := TokenFromContext(r.Context())</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">80<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+			if !slices.Contains(token.Scopes, scope) &amp;&amp; !slices.Contains(token.Scopes, &quot;*&quot;) {</td>
</tr>
<tr data-thread class="bg-primary/10 border-t border-primary/20">
<td colspan="2" class="px-4 py-0">
<div class="flex items-start gap-2 py-2 border-b border-primary/20 last:border-0">
<span class="flex shrink-0 mt-0.5"><span class="inline-grid place-items-center rounded-full bg-muted border border-border font-medium text-muted-foreground h-6 w-6 text-[10px]" aria-hidden="true">Y</span></span>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2">
<a href="profile.html" class="text-xs font-semibold hover:underline">yumi</a>
<span class="text-xs text-muted-foreground">May 13, 2026</span>
</div>
<div class="text-xs text-foreground mt-0.5 prose prose-xs dark:prose-invert max-w-none"><p>Unscoped legacy tokens now get <code>*</code> from the migration, so this branch is safe. Thanks for dropping <code>legacyAllScopes</code>.</p></div>
</div>
<button type="button" class="text-muted-foreground/60 hover:text-destructive text-xs shrink-0" aria-label="Delete comment">&times;</button>
</div>
</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">81<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+				http.Error(w, &quot;missing scope&quot;, http.StatusForbidden)</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">82<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+				return</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">83<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+			}</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">84<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+			next.ServeHTTP(w, r)</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">85<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+		})</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">86<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+	}</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">87<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+}</td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">88<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> </td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">89<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> func TokenFromContext(ctx context.Context) *model.AccessToken {</td>
</tr>
<tr><td colspan="2" class="bg-primary/5 text-muted-foreground px-4 py-1 select-none border-y border-border">@@ -88,7 +100,3 @@ func TokenFromContext(ctx context.Context) *model.AccessToken {</td></tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">100<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> 	return t</td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">101<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> }</td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">102<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> </td>
</tr>
<tr class="diff-line bg-destructive/6 dark:bg-destructive/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">91</td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-destructive">-// legacyAllScopes treated a token with no scopes as unrestricted.</td>
</tr>
<tr class="diff-line bg-destructive/6 dark:bg-destructive/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">92</td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-destructive">-func legacyAllScopes(t *model.AccessToken) bool {</td>
</tr>
<tr class="diff-line bg-destructive/6 dark:bg-destructive/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">93</td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-destructive">-	return len(t.Scopes) == 0</td>
</tr>
<tr class="diff-line bg-destructive/6 dark:bg-destructive/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">94</td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-destructive">-}</td>
</tr>
</tbody>
</table>
</div>
            </div>
            <div class="diff-split">
<div class="hl overflow-x-auto">
<table class="w-full table-fixed border-collapse text-xs font-mono">
<colgroup><col class="w-10"/><col/><col class="w-10"/><col/></colgroup>
<tbody>
<tr><td colspan="4" class="bg-primary/5 text-muted-foreground px-4 py-1 select-none border-y border-border">@@ -68,12 +68,24 @@ func RequireAuth(next http.Handler) http.Handler {</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">68</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> 			return</td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">68</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> 			return</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">69</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> 		}</td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">69</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> 		}</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">70</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> </td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">70</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> </td></tr>
<tr class="ws-only"><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">71</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden bg-destructive/6 dark:bg-destructive/10 text-destructive">-		next.ServeHTTP(w, r)</td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">71</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+			next.ServeHTTP(w, r)</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">72</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> 	})</td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">72</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> 	})</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">73</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> }</td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">73</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> }</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">74</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> </td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">74</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> </td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">75</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+// RequireScope rejects requests whose token lacks the scope.</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">76</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+func RequireScope(scope string) func(http.Handler) http.Handler {</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">77</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+	return func(next http.Handler) http.Handler {</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">78</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">79</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+			token := TokenFromContext(r.Context())</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">80</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+			if !slices.Contains(token.Scopes, scope) &amp;&amp; !slices.Contains(token.Scopes, &quot;*&quot;) {</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">81</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+				http.Error(w, &quot;missing scope&quot;, http.StatusForbidden)</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">82</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+				return</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">83</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+			}</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">84</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+			next.ServeHTTP(w, r)</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">85</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+		})</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">86</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+	}</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">87</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+}</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">75</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> </td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">88</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> </td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">76</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> func TokenFromContext(ctx context.Context) *model.AccessToken {</td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">89</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> func TokenFromContext(ctx context.Context) *model.AccessToken {</td></tr>
<tr><td colspan="4" class="bg-primary/5 text-muted-foreground px-4 py-1 select-none border-y border-border">@@ -88,7 +100,3 @@ func TokenFromContext(ctx context.Context) *model.AccessToken {</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">88</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> 	return t</td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">100</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> 	return t</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">89</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> }</td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">101</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> }</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">90</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> </td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">102</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> </td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">91</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden bg-destructive/6 dark:bg-destructive/10 text-destructive">-// legacyAllScopes treated a token with no scopes as unrestricted.</td><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30 border-l border-border"></td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">92</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden bg-destructive/6 dark:bg-destructive/10 text-destructive">-func legacyAllScopes(t *model.AccessToken) bool {</td><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30 border-l border-border"></td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">93</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden bg-destructive/6 dark:bg-destructive/10 text-destructive">-	return len(t.Scopes) == 0</td><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30 border-l border-border"></td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">94</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden bg-destructive/6 dark:bg-destructive/10 text-destructive">-}</td><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30 border-l border-border"></td></tr>
</tbody>
</table>
</div>
            </div>
          </div>
        </section>
        <section id="diff-3" class="diff-file border border-border rounded-md overflow-hidden scroll-mt-6">
          <header class="diff-file-header px-3 py-2 border-b border-border bg-muted/40 flex items-center gap-2 text-[12.5px] cursor-pointer select-none focus-visible:outline-solid focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring" role="button" tabindex="0" aria-expanded="true">
            <svg class="diff-file-caret shrink-0 text-muted-foreground transition-transform" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"></polyline></svg>
            <span class="font-mono font-medium truncate">internal/store/token_store.go</span>
            <span class="ml-auto font-mono text-[11px] shrink-0">
              <span class="text-success">+6</span>
              
            </span>
            <a href="blob.html" class="text-[12px] text-muted-foreground hover:text-foreground shrink-0">View file →</a>
          </header>
          <div class="diff-file-body">
            <div class="diff-unified">
<div class="hl overflow-x-auto">
<table class="w-full border-collapse text-xs font-mono">
<tbody>
<tr><td colspan="2" class="bg-primary/5 text-muted-foreground px-4 py-1 select-none border-y border-border">@@ -40,6 +40,13 @@ func (s *TokenStore) Create(ctx context.Context, t model.AccessToken) error {</td></tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">40<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> 	err := s.db.QueryRowContext(ctx, `INSERT INTO access_tokens (user_id, name, hash)</td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">41<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> 		VALUES ($1, $2, $3) RETURNING id`, t.UserID, t.Name, t.Hash).Scan(&amp;t.ID)</td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">42<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> 	if err != nil {</td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">43<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> 		return err</td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">44<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> 	}</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">45<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+	for _, scope := range t.Scopes {</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">46<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+		if _, err := s.db.ExecContext(ctx, `INSERT INTO access_token_scopes (access_token_id, scope)</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">47<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+			VALUES ($1, $2)`, t.ID, scope); err != nil {</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">48<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+			return err</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">49<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+		}</td>
</tr>
<tr class="diff-line bg-success/6 dark:bg-success/10">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">50<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-success">+	}</td>
</tr>
<tr class="diff-line">
<td class="group w-14 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">51<button type="button" data-comment class="ml-1 text-muted-foreground/40 group-hover:text-primary" aria-label="Comment on this line">+</button></td>
<td class="pl-3 pr-4 py-0.5 whitespace-pre text-foreground/85"> 	return nil</td>
</tr>
</tbody>
</table>
</div>
            </div>
            <div class="diff-split">
<div class="hl overflow-x-auto">
<table class="w-full table-fixed border-collapse text-xs font-mono">
<colgroup><col class="w-10"/><col/><col class="w-10"/><col/></colgroup>
<tbody>
<tr><td colspan="4" class="bg-primary/5 text-muted-foreground px-4 py-1 select-none border-y border-border">@@ -40,6 +40,13 @@ func (s *TokenStore) Create(ctx context.Context, t model.AccessToken) error {</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">40</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> 	err := s.db.QueryRowContext(ctx, `INSERT INTO access_tokens (user_id, name, hash)</td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">40</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> 	err := s.db.QueryRowContext(ctx, `INSERT INTO access_tokens (user_id, name, hash)</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">41</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> 		VALUES ($1, $2, $3) RETURNING id`, t.UserID, t.Name, t.Hash).Scan(&amp;t.ID)</td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">41</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> 		VALUES ($1, $2, $3) RETURNING id`, t.UserID, t.Name, t.Hash).Scan(&amp;t.ID)</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">42</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> 	if err != nil {</td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">42</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> 	if err != nil {</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">43</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> 		return err</td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">43</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> 		return err</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">44</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> 	}</td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">44</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> 	}</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">45</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+	for _, scope := range t.Scopes {</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">46</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+		if _, err := s.db.ExecContext(ctx, `INSERT INTO access_token_scopes (access_token_id, scope)</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">47</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+			VALUES ($1, $2)`, t.ID, scope); err != nil {</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">48</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+			return err</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">49</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+		}</td></tr>
<tr><td class="w-12 bg-muted/30 border-r border-border"></td><td class="px-3 py-0.5 bg-muted/30"></td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">50</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border bg-success/6 dark:bg-success/10 text-success">+	}</td></tr>
<tr><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">45</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden text-foreground/85"> 	return nil</td><td class="w-12 text-right pr-2 text-muted-foreground select-none border-r border-border py-0.5 align-top">51</td><td class="pl-3 pr-4 py-0.5 whitespace-pre overflow-hidden border-l border-border text-foreground/85"> 	return nil</td></tr>
</tbody>
</table>
</div>
            </div>
          </div>
        </section>
      </div>
    </div>
  </div>
</main>

<div id="cz-footer"></div>
<script src="shell.js"></script>
<script>
  (function () {
    var btn = document.getElementById('edit-pr-btn');
    var txt = document.getElementById('pr-title-text');
    var original = txt.textContent;
    var editing = ['rounded-sm', 'bg-muted', 'px-1', 'outline-solid', 'outline-1', 'outline-border'];
    btn.addEventListener('click', function () {
      if (txt.isContentEditable) {
        var next = txt.textContent.trim();
        txt.contentEditable = 'false';
        txt.classList.remove.apply(txt.classList, editing);
        btn.textContent = 'Edit';
        if (next) original = next; else txt.textContent = original;
      } else {
        txt.contentEditable = 'true';
        txt.classList.add.apply(txt.classList, editing);
        btn.textContent = 'Save';
        txt.focus();
      }
    });
  })();
</script>
<script>
  (function () {
    var root = document.getElementById('diff-root');
    var sections = Array.prototype.slice.call(document.querySelectorAll('.diff-file'));
    var singleBtn = document.getElementById('diff-single-file');
    var activeId = sections.length ? sections[0].id : null;
    var fileLinks = {};

    document.querySelectorAll('#diff-files-list a[href^="#diff-"], #diff-files-tree a[href^="#diff-"]').forEach(function (a) {
      var id = a.getAttribute('href').slice(1);
      (fileLinks[id] = fileLinks[id] || []).push(a);
    });

    function setCollapsed(section, collapsed) {
      section.querySelector('.diff-file-body').classList.toggle('hidden', collapsed);
      section.querySelector('.diff-file-caret').style.transform = collapsed ? 'rotate(-90deg)' : '';
      section.querySelector('.diff-file-header').setAttribute('aria-expanded', collapsed ? 'false' : 'true');
    }
    document.querySelectorAll('.diff-file-header').forEach(function (h) {
      function toggle() {
        var s = h.closest('.diff-file');
        setCollapsed(s, !s.querySelector('.diff-file-body').classList.contains('hidden'));
      }
      h.addEventListener('click', function (e) {
        if (e.target.closest('a')) return;
        toggle();
      });
      h.addEventListener('keydown', function (e) {
        if (e.target !== h || (e.key !== 'Enter' && e.key !== ' ')) return;
        e.preventDefault();
        toggle();
      });
    });

    function singleOn() { return singleBtn.dataset.state === 'on'; }
    function highlight(id) {
      Object.keys(fileLinks).forEach(function (k) {
        fileLinks[k].forEach(function (a) {
          a.classList.toggle('bg-accent', k === id);
          a.classList.toggle('text-foreground', k === id);
        });
      });
    }
    function applySingle() {
      var on = singleOn();
      sections.forEach(function (s) { s.classList.toggle('hidden', on && s.id !== activeId); });
      if (on) window.scrollTo(0, 0);
    }
    function setActive(id) { activeId = id; highlight(id); applySingle(); }
    Object.keys(fileLinks).forEach(function (id) {
      fileLinks[id].forEach(function (a) {
        a.addEventListener('click', function (e) {
          if (singleOn()) e.preventDefault();
          setActive(id);
        });
      });
    });
    singleBtn.addEventListener('click', function () {
      var on = !singleOn();
      singleBtn.dataset.state = on ? 'on' : 'off';
      singleBtn.setAttribute('aria-pressed', on);
      singleBtn.classList.toggle('bg-muted', on);
      singleBtn.classList.toggle('text-muted-foreground', !on);
      applySingle();
    });
    if ('IntersectionObserver' in window) {
      var spy = new IntersectionObserver(function (entries) {
        if (singleOn()) return;
        entries.forEach(function (e) { if (e.isIntersecting) { activeId = e.target.id; highlight(activeId); } });
      }, { rootMargin: '-8% 0px -82% 0px' });
      sections.forEach(function (s) { spy.observe(s); });
    }
    highlight(activeId);

    function pair(a, b, first) {
      a.setAttribute('aria-pressed', first);
      b.setAttribute('aria-pressed', !first);
      a.classList.toggle('bg-muted', first);
      a.classList.toggle('text-muted-foreground', !first);
      b.classList.toggle('bg-muted', !first);
      b.classList.toggle('text-muted-foreground', first);
    }
    var unifiedBtn = document.getElementById('diff-view-unified');
    var splitBtn = document.getElementById('diff-view-split');
    unifiedBtn.addEventListener('click', function () { root.dataset.view = 'unified'; pair(unifiedBtn, splitBtn, true); });
    splitBtn.addEventListener('click', function () { root.dataset.view = 'split'; pair(unifiedBtn, splitBtn, false); });

    var wsBtn = document.getElementById('diff-ws-toggle');
    wsBtn.addEventListener('click', function () {
      var on = !root.classList.contains('hide-ws');
      root.classList.toggle('hide-ws', on);
      wsBtn.setAttribute('aria-pressed', on);
      wsBtn.classList.toggle('bg-muted', on);
      wsBtn.classList.toggle('text-muted-foreground', !on);
    });

    var listUl = document.getElementById('diff-files-list');
    var treeUl = document.getElementById('diff-files-tree');
    var listBtn = document.getElementById('diff-files-list-btn');
    var treeBtn = document.getElementById('diff-files-tree-btn');
    listBtn.addEventListener('click', function () { listUl.classList.remove('hidden'); treeUl.classList.add('hidden'); pair(listBtn, treeBtn, true); });
    treeBtn.addEventListener('click', function () { listUl.classList.add('hidden'); treeUl.classList.remove('hidden'); pair(listBtn, treeBtn, false); });
    treeUl.querySelectorAll('.diff-tree-folder').forEach(function (b) {
      b.addEventListener('click', function () {
        var li = b.closest('li');
        var open = li.dataset.open !== 'false';
        li.dataset.open = open ? 'false' : 'true';
        b.querySelector('.diff-tree-caret').style.transform = open ? 'rotate(-90deg)' : '';
        li.querySelector(':scope > ul').classList.toggle('hidden', open);
      });
    });

    var FORM = '<td colspan="2" class="p-0"><form class="p-2 space-y-2">' +
      '<textarea name="body" rows="2" placeholder="Leave a comment..." required class="w-full border border-border rounded-sm px-2 py-1 text-xs focus:outline-hidden focus:ring-1 focus-visible:outline-ring resize-none"></textarea>' +
      '<div class="flex gap-2"><button type="submit" class="px-3 py-1 bg-primary text-background rounded-sm text-xs font-medium hover:bg-primary">Comment</button>' +
      '<button type="button" data-cancel class="px-3 py-1 bg-background border border-border text-muted-foreground rounded-sm text-xs hover:bg-muted/40">Cancel</button></div></form></td>';
    var POSTED = '<td colspan="2" class="px-4 py-0"><div class="flex items-start gap-2 py-2"><span class="flex shrink-0 mt-0.5"><span class="inline-grid place-items-center rounded-full bg-muted border border-border font-medium text-muted-foreground h-6 w-6 text-[10px]" aria-hidden="true">M</span></span>' +
      '<div class="flex-1 min-w-0"><div class="flex items-center gap-2"><a href="profile.html" class="text-xs font-semibold hover:underline">malith</a><span class="text-xs text-muted-foreground">just now</span></div>' +
      '<div class="text-xs text-foreground mt-0.5" data-body></div></div></div></td>';
    document.querySelectorAll('[data-comment]').forEach(function (b) {
      b.addEventListener('click', function () {
        var tr = b.closest('tr');
        var next = tr.nextElementSibling;
        if (next && next.hasAttribute('data-cform')) return;
        var row = document.createElement('tr');
        row.setAttribute('data-cform', '');
        row.innerHTML = FORM;
        tr.after(row);
        var form = row.querySelector('form');
        row.querySelector('textarea').focus();
        row.querySelector('[data-cancel]').addEventListener('click', function () { row.remove(); });
        form.addEventListener('submit', function (e) {
          e.preventDefault();
          var text = form.elements.body.value.trim();
          if (!text) return;
          row.className = 'bg-primary/10 border-t border-primary/20';
          row.removeAttribute('data-cform');
          row.innerHTML = POSTED;
          row.querySelector('[data-body]').textContent = text;
        });
      });
    });
  })();
</script>
<script>
  (function () {
    var ORIGINAL = document.getElementById('sugg-target').textContent;
    var UPDATED = '+A token carries a set of scopes (`*` grants all). A request outside them is rejected with `403`.';
    function params() {
      var h = location.hash.slice(1).split('&'), out = {};
      h.forEach(function (p) { var kv = p.split('='); out[kv[0]] = kv[1]; });
      return out;
    }
    function render() {
      var p = params();
      var state = ['pending', 'batched', 'applied'].indexOf(p.sugg) >= 0 ? p.sugg : 'pending';
      var readonly = p.viewer === 'readonly';
      document.querySelectorAll('[data-when]').forEach(function (el) {
        el.hidden = el.getAttribute('data-when').split(' ').indexOf(state) < 0 || (readonly && el.hasAttribute('data-needs-write'));
      });
      document.querySelectorAll('[data-needs-write]:not([data-when])').forEach(function (el) { el.hidden = readonly; });
      document.getElementById('sugg-target').textContent = state === 'applied' ? UPDATED : ORIGINAL;
    }
    function set(state) {
      var p = params();
      location.hash = 'sugg=' + state + (p.viewer ? '&viewer=' + p.viewer : '');
    }
    document.querySelectorAll('[data-set]').forEach(function (b) {
      b.addEventListener('click', function () { set(b.getAttribute('data-set')); });
    });
    document.getElementById('commit-suggestions').addEventListener('click', function () { set('applied'); });
    var body = document.getElementById('review-body');
    document.querySelectorAll('#review-form input[name=state]').forEach(function (r) {
      r.addEventListener('change', function () { body.required = r.value !== 'approved'; });
    });
    window.addEventListener('hashchange', render);
    render();
  })();
</script>
<script>
  document.addEventListener('click', function (e) {
    var b = e.target.closest('button[aria-label="Delete comment"]');
    if (!b) return;
    var thread = b.closest('tr[data-thread]');
    b.parentElement.remove();
    if (!thread.querySelector('button[aria-label="Delete comment"]')) thread.remove();
    czToast('Comment deleted');
  });
</script>
</body>
</html>