A design prototype for an AI assistant that acts on your behalf and shows its work. The premise is that once an assistant can touch your calendar and your inbox, the screens that matter aren't the chat — they're the permission list and the audit log, so this mock-up gives all three equal weight.
Everything in here is mock data. It's three static files — index.html, styles.css and app.js — with no backend, no model, no API calls and no storage. Every message, tool call, timestamp and duration is hardcoded in the markup. The "Claude 3.7 Sonnet · 1.2s" captions are illustrative strings, not measurements — the model name is a label in the mock data, not an integration, and it's written two different ways ("Claude 3.7 Sonnet" on the phone, "Sonnet 3.7" in the panel's table). The assistant doesn't respond to anything you type.
The page is laid out as a fixed 1920×1080 presentation canvas: a 390×844 phone mock-up on the left, and a dark desktop panel on the right showing the same session in a denser form. The phone is a browser drawing of an iOS-style UI — there is no native app in this repo.
Chat is a scripted exchange: the user asks for a 30-minute slot after lunch tomorrow, and the assistant's replies are interleaved with three tool-call cards. Two succeed — a calendar read that reports 4 events found, and an event creation that blocks out "Focus time" — each naming the tool, what it touched, and a model/duration footer. (Only the calendar read has a separate result line; the create card states its outcome in the body.) The third is a Send email card rendered in red: permission is off, so the card says the action was blocked, its footer reads "Action blocked" with no model or duration, and the assistant offers to draft the reply instead. There's a composer at the bottom and a three-tab bar.
Permissions lists ten capabilities in four groups — Calendar (read / create / modify), Email (read / send / delete), Messages (read / send) and Files (read / create & modify) — each with an iOS-style toggle and a subtitle giving last use ("Last used 3 min ago · Work calendar") or "Never used". Five are on by default. Email → Send messages carries the "Blocked 9:22 AM" note that the chat thread refers back to.
Activity is the audit log: seven entries grouped under Today, Yesterday and Friday, each with a timestamp, the model name, an execution time, a green Done or red Denied status, and a detail line naming the object touched (Q3_Report.pdf · Documents folder). The blocked email send appears here too, at 0.1s.
It follows whichever screen the phone is on, and clicking its footer nav drives the phone back — tapping a phone tab switches the panel, and the panel's buttons switch the phone. (The two start out of step: the phone opens on Chat while the panel opens on Permissions. They lock together after the first click.)
The permissions view is a condensed cut rather than a literal mirror — eight icon rows under three headings, with Enabled / Off / Blocked labels, leaving out Email → Delete and Messages → Send. The activity view becomes a five-column table (Time, Action, Model, Duration, Status) with status pills, carrying all seven entries. The chat view summarises the session's three tool calls, plus a short note on what the assistant can and can't see and the model name. A caption in the footer swaps with each view — "6 capabilities · 1 blocked", "7 actions in 3 days" — as fixed strings, not counts computed from the page.
Open index.html in a browser. There's nothing to install and nothing to build — no npm install, no package.json, no dev server.
index.html pulls in the other two files, styles.css and app.js, as same-directory relative links, so double-clicking the file still works. Those two are the only local requests; the only ones that leave your machine are to Google Fonts for DM Sans. Offline it falls back to the system sans-serif and looks slightly off but works fine.
Hand-written HTML, CSS and JavaScript, split across three files and totalling about 54 KB — index.html (~34 KB) is markup only, styles.css (~16 KB) holds the styling, and app.js (~4 KB) the behaviour. No framework, no bundler, no dependencies, no build step. CSS custom properties for the palette, flexbox throughout, CSS grid for the log table, 26 inline SVGs and no image files. app.js is 123 lines of vanilla JS handling tab switching, panel sync, the toggles and hash updates.
The toggles are cosmetic. Flipping one animates the switch and replaces the subtitle with "Just enabled" / "Just disabled", but it doesn't unblock the email card in the chat thread, add a row to the activity log, change the matching Enabled / Off / Blocked label in the right-hand panel, or restore the original subtitle if you flip it back — permission state isn't wired to anything downstream.
The composer clears the field when you hit send but doesn't append your message; the thread is fixed, and Enter does nothing — only the button is wired.
The three chips in the panel's header (Permissions / Activity log / Chat) are status indicators, not controls. Only the footer buttons switch views.
Hash routing is partial. Switching tabs pushes #view-permissions and friends with the History API, and pressing Back does move the view, but the handler that restores a view clicks the tab, which pushes another entry — so the history stack doesn't walk cleanly. Opening the page at a hash from a cold load doesn't select that tab at all, because the handler is only bound to hashchange and popstate and never runs on first paint.
Because the canvas is a fixed 1920×1080 with overflow: hidden on the body, anything narrower clips rather than reflows — zoom out to see the whole composition. The <meta name="viewport"> tag is also malformed (the attributes were written outside a content=""), which is harmless given the fixed canvas but would need fixing before this became a real responsive page.