Plan: Add TOC Sidebar to Prose by Default
Implement the following plan:
Plan: Add TOC Sidebar to Prose by Default
Current State
Prose— simple centered article wrapper, no TOC. Used by: blog posts, design page, terminology, syllabus fallback.ProseWithToc— flex layout with TOC sidebar +ViewAsStudentWrapper+ optionalsidebarContent. Used by: syllabus, meetings.
Approach
Add toc and sidebarContent props to Prose. When toc is enabled (the default), Prose renders the flex layout with a TableOfContents sidebar. Refactor ProseWithToc to compose Prose and pass its course-specific sidebar extras.
Step 1: Modify Prose component
File: apps/web/components/prose.tsx
- Add
toc?: booleanprop (defaulttrue) - Add
sidebarContent?: React.ReactNodeprop for extra sidebar items below the TOC - When
toc={true}: flex layout with<article>+<aside>containingTableOfContents+ optionalsidebarContent - When
toc={false}: current simple centered layout (no aside)
Step 2: Simplify ProseWithToc
File: apps/web/components/prose-with-toc.tsx
Refactor to compose Prose, passing ViewAsStudentWrapper and any extra sidebarContent through:
export function ProseWithToc({ children, className, sidebarContent }) {
return (
<Prose
className={className}
sidebarContent={
<>
<div className="border-border mt-4 border-t pt-4">
<ViewAsStudentWrapper />
</div>
{sidebarContent && (
<div className="border-border mt-4 border-t pt-4">{sidebarContent}</div>
)}
</>
}
>
{children}
</Prose>
);
}
Step 3: Add toc={false} to fallback/placeholder pages
Pages with minimal content (no h2/h3 headings) should opt out:
apps/web/app/[semester]/syllabus/page.tsx— catch fallback uses<Prose toc={false}>apps/web/app/[semester]/terminology/page.tsx— catch fallback uses<Prose toc={false}>
All other Prose consumers (blog posts, design page, terminology main) get TOC automatically with no changes.
Files Modified
apps/web/components/prose.tsxapps/web/components/prose-with-toc.tsxapps/web/app/[semester]/syllabus/page.tsx—toc={false}on fallback onlyapps/web/app/[semester]/terminology/page.tsx—toc={false}on fallback only
Verification
- Blog post TOC:
curl -sk https://[local-hostname]/blog/2026-01-21-claude-coinstructor | grep -i "table-of-contents" - Design page TOC:
curl -sk https://[local-hostname]/design/assessments | grep -i "table-of-contents" - Syllabus still has TOC + ViewAsStudentWrapper: visit
/spring2026/syllabus - TypeScript:
bunx turbo tsc --filter=@repo/web
If you need specific details from before exiting plan mode (like exact code snippets, error messages, or content you generated), read the full transcript at: ~/.claude/projects/-Users-challen-www-usingandunderstanding-ai/df14212e-895a-4baa-8a2c-dd1464698f36.jsonl
I'll start by reading the current files to understand the exact code before making changes.
Now let me also check the TableOfContents import path and the data-sidenote-margin attribute usage to make sure I preserve things correctly.
Good. Now I have all the context. Let me create the task list and implement the changes.
Now add toc={false} to the fallback blocks in both pages.
TypeScript compiles cleanly. Let me also verify the dev server is running and check a couple of pages.
Both pages have the TOC. Let me also check the terminology page (which should have a TOC since it has real content) and the design page.
All done. Here's a summary of the changes:
apps/web/components/prose.tsx — Added toc (default true) and sidebarContent props. When toc is enabled, renders the flex layout with a TableOfContents aside. When disabled, renders the original simple centered layout.
apps/web/components/prose-with-toc.tsx — Simplified to compose Prose, passing ViewAsStudentWrapper and any extra sidebarContent through the new prop. Removed duplicated layout/styling code.
apps/web/app/[semester]/syllabus/page.tsx — Added toc={false} on the catch fallback <Prose> (the "coming soon" placeholder).
apps/web/app/[semester]/terminology/page.tsx — Added toc={false} on the catch fallback <Prose>.
Verified: TypeScript compiles cleanly, and the blog post, syllabus, terminology, and design pages all render with a TOC sidebar.
It would be nice to have a /blog link in the blog sidebar to return to the index.
(no content)
Let me find how blog posts use Prose to understand where to add the sidebar link.
Done. Added an "All Posts" link with a back arrow in the blog post sidebar, below the table of contents. It links to /blog and uses the same muted mono style as other sidebar elements.
Your task
First, run git status, git diff, and git log --oneline -10 to understand the current state.
Then add and commit all modified files with a descriptive message. Break the changes into multiple commits if appropriate. If there are untracked files, ask if they should be added or added to the appropriate .gitignore file. The user may also want to take over at this point, so provide an abort option as well. Always check changes for unnecessary debugging statements or logging that might have been accidentally included. Prompt the user if you find any, and offer to remove each one by one. Do not use interactive git staging (git add -p) as it does not work.
No debugging statements or unnecessary logging in the changes. The diff is clean.
There are 2 untracked files from before this session:
.claude/commands/geoff-post.mdcontent/blog/2026-01-21-claude-coinstructor.mdx
Committed as b98c489. The 5 modified files are committed; the 2 pre-existing untracked files remain as-is.
Wait, no, please add those! Sorry.
No problem.
Done — committed as d100d1d. Working tree is clean.
Is the Turing Test assessment available to non-logged-in users?
(no content)
No. All assessment pages redirect unauthenticated users to /login:
apps/web/app/assessments/page.tsx:22-28— checksauth.api.getSession(), redirects to/login?returnTo=/assessmentsif no sessionapps/web/app/assessments/[slug]/page.tsx:50-56— same auth check on the individual assessment pageapps/web/app/assessments/[slug]/[attemptId]/page.tsx:56-62— same auth check on the attempt page
The API layer also enforces this — all endpoints except the listing endpoint (GET /api/assessments) require authMiddleware, which returns 401 without a session token. The listing endpoint uses optionalAuthMiddleware and would return the Turing Test (since it has visibility: public in content/assessments/turing-test.md:6), but that only reveals the assessment exists — you can't view details or start an attempt without being authenticated.
Good, I think that's right. OK! Next, I'd like to think about allowing logins from non-Illinois users. This would require a new login provider and flow. Does Better Auth support email-based login using a one-time token? If so we could try that using the outbound server that's already configured.
(no content)
Let me read the existing plan file and then explore the current auth setup and Better Auth's capabilities.
This is a completely different task. Let me explore the current auth setup and research Better Auth's email/magic link support.
(no content)
Let me get the exact plugin API details before designing the plan.
Now let me check the login page and the mail sender to understand the full picture.
I have all the information I need. Let me design the plan.
(no content)
Let me verify a few details before finalizing.
Good — MAIL_* is already in turbo.json's globalPassThroughEnv, nodemailer is 7.0.13 / @types/nodemailer is 7.0.9, and the Input component exists. Now let me write the plan.
Open sign-up it is. The plan already reflects that (disableSignUp: false). No changes needed.