Chat Auto Page Context Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Silently attach the current page path to every chat message automatically, removing the user-facing page/general context selector entirely.
Architecture: Replace the structured { collection, id, title } context with a simple { path: string } read from window.location.pathname at send time. The server validates the path with a regex before storing/broadcasting. No UI indicator — context is invisible to the sender.
Tech Stack: Astro, TypeScript, Cloudflare Workers / Durable Objects, WebSocket
Task 1: Update MessageContext type in the API
Files:
- Modify:
api/src/types.ts:3-7
Step 1: Update the MessageContext interface
Replace lines 3–7:
export interface MessageContext { path: string;}Step 2: Verify ClientChatData still compiles (no change needed, it uses MessageContext by reference)
Check api/src/types.ts:42-45 — context?: MessageContext already uses the type, no edit needed.
Step 3: Run API type-check
just api::typecheckExpected: no errors (or command not found — skip if unavailable, TypeScript errors will surface in next task)
Task 2: Update server-side context validation in chat-room.ts
Files:
- Modify:
api/src/chat-room.ts:237-244
Step 1: Replace the context inclusion check
Current code at line 243:
...(context?.collection && context?.id && context?.title ? { context } : {}),Replace with:
...(context?.path && /^\/[a-z0-9\-._/]*$/.test(context.path) ? { context } : {}),The full updated message object block (lines 237–244) becomes:
const message: ChatMessage = { type: SERVER_MESSAGE_TYPE.MESSAGE, id: uuidv7(), username: info.username, text, timestamp: Date.now(), ...(context?.path && /^\/[a-z0-9\-._/]*$/.test(context.path) ? { context } : {}),};Step 2: Run API type-check
just api::typecheckExpected: no errors
Step 3: Commit
git add api/src/types.ts api/src/chat-room.tsgit commit -m "feat: simplify MessageContext to { path } with server-side validation"Task 3: Update chat-client.ts — types and message display
Files:
- Modify:
app/src/components/Chat/chat-client.ts:22-26(localMessageContextinterface) - Modify:
app/src/components/Chat/chat-client.ts:28-52(ServerMessage union — context shape) - Modify:
app/src/components/Chat/chat-client.ts:107-134(appendMessage)
Step 1: Update the local MessageContext interface (lines 22–26)
Replace:
interface MessageContext { collection: string; id: string; title: string;}With:
interface MessageContext { path: string;}Step 2: Update appendMessage context rendering (lines 115–126)
Current:
if (msg.context) { const contextLine = document.createElement("div"); contextLine.style.fontSize = "0.85em"; contextLine.style.opacity = "0.7"; const label = document.createTextNode("on: "); const link = document.createElement("a"); link.href = `/${msg.context.collection}/${msg.context.id}`; link.textContent = msg.context.title; contextLine.appendChild(label); contextLine.appendChild(link); div.appendChild(contextLine);}Replace with:
if (msg.context) { const contextLine = document.createElement("div"); contextLine.style.fontSize = "0.85em"; contextLine.style.opacity = "0.7"; const label = document.createTextNode("on: "); const link = document.createElement("a"); link.href = msg.context.path; link.textContent = msg.context.path; contextLine.appendChild(label); contextLine.appendChild(link); div.appendChild(contextLine);}Step 3: Run app type-check
just app::typecheckExpected: no errors (some may still exist from removed vars — that’s fine, those are removed in Task 4)
Task 4: Update chat-client.ts — remove context UI, always send path
Files:
- Modify:
app/src/components/Chat/chat-client.ts
Step 1: Remove currentContext module-level variable (line 71)
Delete the line:
let currentContext: MessageContext | null = null;Step 2: Remove contextEl and contextSelect DOM references (lines 80–81)
Delete both lines:
const contextEl = document.getElementById("js-chat-context") as HTMLSpanElement;const contextSelect = document.getElementById("js-chat-context-select") as HTMLSelectElement;Step 3: Update sendMessage() to always attach path context (lines 254–263)
Current:
function sendMessage(): void { const text = inputEl.value.trim(); if (!text || !ws || ws.readyState !== WebSocket.OPEN) return;
const data: { text: string; context?: MessageContext } = { text }; if (currentContext && contextSelect.value === "page") data.context = currentContext;
ws.send(JSON.stringify({ type: CLIENT_MESSAGE_TYPE.MESSAGE, data })); inputEl.value = "";}Replace with:
function sendMessage(): void { const text = inputEl.value.trim(); if (!text || !ws || ws.readyState !== WebSocket.OPEN) return;
const data: { text: string; context: MessageContext } = { text, context: { path: window.location.pathname }, };
ws.send(JSON.stringify({ type: CLIENT_MESSAGE_TYPE.MESSAGE, data })); inputEl.value = "";}Step 4: Remove updatePlaceholder() function and its listener (lines 325–354)
Delete the entire updatePlaceholder function:
function updatePlaceholder(): void { if (currentContext && contextSelect.value === "page") { inputEl.placeholder = `Message about ${currentContext.title}...`; } else { inputEl.placeholder = "Type a message..."; }}Also delete the listener line:
contextSelect.addEventListener("change", updatePlaceholder);Step 5: Remove readPageContext() and related listeners (lines 333–357)
Delete the entire readPageContext function:
function readPageContext(): void { const meta = document.querySelector<HTMLMetaElement>('meta[name="page-context"]'); if (meta?.content) { try { const ctx = JSON.parse(meta.content) as MessageContext; currentContext = ctx; const pageOption = contextSelect.querySelector<HTMLOptionElement>('option[value="page"]'); if (pageOption) pageOption.textContent = ctx.title; contextSelect.value = "page"; contextEl.hidden = false; } catch { currentContext = null; contextEl.hidden = true; } } else { currentContext = null; contextEl.hidden = true; } updatePlaceholder();}Also delete these two lines:
readPageContext();document.addEventListener("astro:after-swap", readPageContext);Step 6: Run app type-check
just app::typecheckExpected: no errors
Step 7: Commit
git add app/src/components/Chat/chat-client.tsgit commit -m "feat: auto-attach current page path as chat context, remove context selector UI"Task 5: Remove context selector from Chat.astro
Files:
- Modify:
app/src/components/Chat/Chat.astro:55-61
Step 1: Remove the <span id="js-chat-context"> block
Delete lines 55–61:
<span id="js-chat-context" hidden> about <select id="js-chat-context-select"> <option value="page"></option> <option value="general">general</option> </select></span>Step 2: Commit
git add app/src/components/Chat/Chat.astrogit commit -m "chore: remove chat context selector from UI"Task 6: Remove page-context meta tag from BaseLayout.astro
Files:
- Modify:
app/src/layouts/BaseLayout/BaseLayout.astro:43 - Modify:
app/src/layouts/BaseLayout/BaseLayout.astro:10-21(Props interface — removepageContext)
Step 1: Remove the meta tag (line 43)
Delete:
{pageContext && <meta name="page-context" content={JSON.stringify(pageContext)} />}Step 2: Remove pageContext from the Props interface (lines 14–18)
Remove:
pageContext?: { collection: string; id: string; title: string;} | null;Step 3: Remove pageContext from the destructured props (line 21)
Current:
const { title = "thalida", activePage, activeCollection, pageContext = null } = Astro.props;Replace with:
const { title = "thalida", activePage, activeCollection } = Astro.props;Step 4: Run app type-check
just app::typecheckExpected: errors from pages that still pass pageContext to BaseLayout — fix those in the next step.
Task 7: Remove pageContext prop from all page files
Files:
- Modify:
app/src/pages/about.astro - Modify:
app/src/pages/[collection]/post/[...id].astro
Step 1: Check which pages pass pageContext
grep -rn "pageContext" app/src/pages/Step 2: Remove pageContext prop from about.astro
Find the <BaseLayout ... pageContext={...}> call and remove the pageContext={...} prop entirely.
Step 3: Remove pageContext prop from [collection]/post/[...id].astro
Find the <BaseLayout ... pageContext={...}> call and remove the pageContext={...} prop. Also remove any variable that was only used for that prop (e.g. pageTitle if it was only used for context — check first).
Step 4: Run app type-check
just app::typecheckExpected: no errors
Step 5: Build to verify no build errors
just app::buildExpected: build succeeds with no errors
Step 6: Commit
git add app/src/layouts/BaseLayout/BaseLayout.astro app/src/pages/about.astro "app/src/pages/[collection]/post/[...id].astro"git commit -m "chore: remove pageContext prop from BaseLayout and pages"Task 8: Mark TODO complete
Files:
- Modify:
TODO.md
Step 1: Check the TODO item
Open TODO.md and find - [ ] chat page mapping (line 6). Mark it complete:
- [x] chat page mappingStep 2: Commit
git add TODO.mdgit commit -m "chore: mark chat page mapping TODO complete"