Commit Graph

952 Commits

Author SHA1 Message Date
Tommy D. Rossi f673205c97 chore: update pnpm lockfile playwriter@0.0.105 2026-04-07 22:55:14 +02:00
Tommy D. Rossi 2839ee4305 release: block extension publish until cli is on npm
Add a root release guard that fetches the latest published `playwriter` version from npm and compares it against the local workspace CLI version before packaging the extension.

If the local CLI is newer, the release exits early with `extension must be released with matching cli version released`, preventing a Chrome Web Store upload that depends on an unpublished relay/CLI version.
2026-04-07 21:55:10 +02:00
Tommy D. Rossi 7e633d5ac0 fix: prevent cross-browser extension takeover loops
Persist a per-install fallback identity for the extension so Chromium-family browsers without chrome.identity profile data no longer collapse to browser-only relay keys and replace each other.

Also stop replaced workers from reclaiming the relay slot while the replacement connection is merely idle, add a regression test that launches a second Chromium context against the same relay, and bump the shipped package versions/changelogs for the extension and playwriter packages.
extension@0.0.81
2026-04-07 21:43:46 +02:00
Tommy D. Rossi 91c69b54cf remove extension-outdated version check — extension auto-updates 2026-04-07 11:55:17 +02:00
Tommy D. Rossi 129fff896c feat: auto-relocate popup windows into source tab's window
Chrome popup windows (window.open with features, OAuth login popups)
could not be controlled by Playwriter because chrome.debugger does not
attach to separate popup windows. Agents had to work around this with
cmd+click (`{ modifiers: ['Meta'] }`) to coerce new tabs instead.

The extension now listens for chrome.windows.onCreated with
type === 'popup' and relocates the tab(s) into the source tab's window,
then auto-attaches Playwriter so the new tab appears in context.pages().

Gating: the popup is only relocated when the source tab is already
Playwriter-connected. Otherwise the popup is left alone so unrelated
sites keep their normal Chrome popup behavior. Source tab is discovered
via chrome.webNavigation.onCreatedNavigationTarget because
chrome.tabs.Tab.openerTabId is unreliable for window.open popups on
Chromium 145 (leaves it null).

Focus is not stolen — the relocated tab is appended at the end of the
source tab's strip (index: -1) but the user's active tab stays active.

Executor changes
- Replaced setupPopupDetection with setupNewPageLogging. Warning
  message changed from "Popup window detected ... cannot be controlled"
  to "New page opened from current page (index N, initial url: ...)"
  pointing the agent at the controllable tab.
- Added MIN_EXTENSION_PLAYWRITER_VERSION check so agents running with
  old extensions see a targeted warning to update via chrome://extensions.
- enqueueWarning is now called from warnIfExtensionOutdated so the LLM
  sees version-skew messages in its warning stream, not just stdout.

Extension changes
- New `webNavigation` permission for the source-tab correlation map.
- connectTab now verifies `chrome.tabs.get(tabId)` before writing an
  `error` state, preventing dead tabs from leaking into store.tabs /
  badge counts / group sync when a tab closes mid-attach.
- popupSourceTabMap entries are cleaned up both on a 10s timeout and
  when chrome.tabs.onRemoved fires, capping memory for plain-new-tab
  cases that never trigger windows.onCreated.

Tests
- popup-relocation.test.ts verifies both paths: when a Playwriter tab
  is connected the popup is relocated into the main window and appears
  in context.pages(); when no tab is connected the popup-type window
  remains (extension does not interfere).

Skill docs
- Removed section #10's cmd+click workaround; added a note about
  auto-relocation under "working with pages".

Versions
- extension 0.0.79 -> 0.0.80
- playwriter 0.0.103 -> 0.0.104
2026-04-05 14:22:21 +02:00
Tommy D. Rossi adc7125b47 fix: skip auto-printing Playwright handles in CLI execute output (#82)
When a single-expression code snippet auto-returns a Playwright object
(e.g. `await page.goto(url)` returning a Response), the CLI previously
called util.inspect() at depth 4 on the handle, producing 400K+ chars
that included every process.env variable (API keys, tokens, secrets).

Even with the playwright-core util.inspect fix (@xmorse/playwright-core@1.59.7),
these handles are useless output — they're programmatic references with no
display value. Agents and users get nothing meaningful from seeing:

  [return value] Response@response@abc123 { request: Channel<...>, ... }

Fix: add isPlaywrightChannelOwner() duck-type check in the executor return
value path. Handles are silently skipped (duck: _type + _guid + _connection).
The existing 'Code executed successfully (no output)' fallback applies when
there's no other output.

To see data, users must explicitly:
  return response.url()      → prints the URL string
  return response.status()   → prints 200
  console.log(response)      → prints safe summary via playwright-core

Also includes:
- Regression test (channel-owner-inspect.test.ts) for inspect handler
- isPlaywrightChannelOwner() unit tests (executor.unit.test.ts)
- Stale color-scheme snapshot fix (relay-core.test.ts)
- MEMORY.md with learnings (Node util.inspect bypass of Proxy traps)
playwriter@0.0.103
2026-04-05 11:57:30 +02:00
Tommy D. Rossi 0ad5219fd8 Add 'vs Built-in Chrome CDP' comparison to README and website
Chrome's --remote-debugging-port shows a persistent automation infobar
that agents cannot dismiss, interrupting users mid-workflow. Playwriter
runs silently with no blocking dialogs, enabling fully autonomous agent
operation without user intervention.

Added to both README.md (markdown table + blockquote) and
website/src/content/index.mdx (ComparisonTable component).
2026-04-03 12:32:57 +02:00
Tommy D. Rossi 5daea69581 test: add mdx-processor tests + use github-slugger for heading dedup
17 tests covering processMdx (frontmatter, headings, image srcs,
dedup, edge cases) and rewriteMdxImages (markdown→JSX conversion,
mixed paragraphs, JSX attr injection, code block safety, frontmatter
preservation).

Replaced hand-rolled slugify with github-slugger — duplicate headings
now get -1 postfix matching GitHub behavior (usage, usage-1).
2026-04-01 17:11:05 +02:00
Tommy D. Rossi c8f5c7b69f fix: AST-based image rewriting instead of unsafe replaceAll
Rewrites image nodes by mutating the mdast tree directly, then serializes
back to MDX with mdast-util-to-markdown + mdast-util-mdx.

Fixes from oracle review:
- Markdown images converted to JSX PixelatedImage with width/height/placeholder
- No more global string replaceAll that could corrupt code blocks/prose
- Each image node handled individually via AST traversal
- Old cache format guard: cached.mdx must be truthy
- Per-image try/catch: one bad image logs warning, doesn't fail build
- Paragraph containing single image → replaced with JSX block element

New deps: mdast-util-to-markdown, mdast-util-mdx, mdast-util-frontmatter
2026-04-01 16:34:30 +02:00
Tommy D. Rossi 66eb0097ff refactor: replace hand-rolled JSONC parser with tiny-jsonc regex approach
Ported from tiny-jsonc: regex captures strings as $1 and matches
comments as non-captured groups, so replacement keeps only strings
intact. Fast path tries JSON.parse first (zero overhead for valid JSON),
slow path strips comments + trailing commas.

Renamed strip-jsonc.ts → lib/jsonc.ts, exports parseJsonc().
2026-04-01 15:54:20 +02:00
Tommy D. Rossi 95c438c900 fix: address 7 oracle review findings
1. JSONC parser: character-by-character string-aware stripper — no longer
   corrupts URLs like https://... inside JSON string values
2. pagesDir: thread resolved prefix into virtual:holocron-config as
   pagesDirPrefix, used by loadMdxContent instead of hardcoded /pages
3. Stale images: re-resolve image SHAs on cached pages — if image content
   changed but MDX didn't, rewrites are refreshed and files recopied
4. configPath option: wired through readConfig + resolveConfigPath + HMR
5. Windows: use url.fileURLToPath(import.meta.url) instead of .pathname
6. Listener leak: process.once('beforeExit') instead of process.on
7. Normalizer guards: Array.isArray checks on tabs, groups, pages, anchors
   before casting — prevents crash on malformed configs
2026-04-01 15:48:15 +02:00
Tommy D. Rossi ad72046b22 feat: single-file image cache + relative image resolution
Image cache rewritten:
- Single dist/holocron-images.json instead of per-image JSON files
- Git blob SHA for invalidation (deterministic, works across CI)
- In-memory Record during build, flushed once on process exit
- Concurrent requests for same image deduplicated via inflight promises

Relative images in MDX:
- Images next to MDX files (./screenshot.png) are now supported
- During sync: detected, copied to public/_holocron/images/<hash>-<name>
- Content hash in filename for dedup + cache busting
- imageRewrites stored on NavPage, applied at render time via replaceAll
- Works like mintlify: images anywhere in repo, not just public/
2026-04-01 15:33:38 +02:00
Tommy D. Rossi 99519eb144 fix: normalize all config types to eliminate unions
readConfig() now collapses every docs.json variant into one canonical shape.
Consuming code (app-factory, sync) accesses fields directly with no type
discrimination.

- navigation: handle {tab,href} (→anchor), {tab,pages} (→group-wrapped),
  {tab,groups} (→kept), root {groups}, root {pages}, array formats
- anchors: merge from both global.anchors and root navigation.anchors
- navbar labels: TYPE_LABELS map (github→GitHub, discord→Discord) instead
  of broken capitalize()
- favicon: string|{light,dark} → always {light,dark}
- logo: string|{light,dark,href} → always {light,dark,href?}
- Navigation type: NavTab[] only (was NavTab[]|NavGroup[])
- findFirstPageInTab: typed with NavTab/NavPage/isNavPage (was ad-hoc)
- removed unused isConfigNavGroup import from sync.ts
- removed getTabs, isNavTabArray, normalizeNavigation exports
2026-04-01 15:04:23 +02:00
Tommy D. Rossi d1344f7d31 feat: add @holocron.so/vite package — drop-in Mintlify replacement as Vite plugin
Holocron is a documentation website framework that reads MDX files + a
holocron.jsonc (or docs.json) config file and generates a full docs site
via a single Vite plugin.

**Architecture:**
- Vite plugin wraps spiceflow, tailwind, tsconfig-paths
- Virtual modules for lazy MDX loading (import.meta.glob) and config
- SHA-based cache in dist/holocron-cache.json for instant rebuilds
- Editorial components copied from website/ (same design system)

**Config (docs.json compatible):**
- navigation with tabs, groups, pages, global.anchors
- navbar.links for header, anchors rendered as tab bar items
- Schema saved locally from https://mintlify.com/docs.json

**Core modules:**
- config.ts — JSONC reader, type normalization for 3 navigation formats
- navigation.ts — enriched tree types + getTabs/findPage/flattenForSidebar
- lib/sync.ts — MDX→cache with git blob SHA deduplication
- lib/mdx-processor.ts — frontmatter + heading extraction via safe-mdx
- app-factory.tsx — Spiceflow app with layout, catch-all page, search API
2026-04-01 14:34:37 +02:00
Tommy D. Rossi 1a8daa3b2f simpler max width. support for full page width 2026-04-01 12:21:41 +02:00
Tommy D. Rossi 2a2db6be25 fix: correct test assertions for screenshot format and color scheme
- snapshot-tools.test.ts: fix wrong assertion expecting 'jpg' when screenshot returns 'png' (contradicted the mimeType assertion on the same screenshot)
- relay-core.test.ts: update inline snapshot for color scheme test to match actual system preference (light mode)
2026-04-01 12:16:31 +02:00
Tommy D. Rossi 612473b578 add justify-between to editorial grid layout
Distribute the three grid columns (TOC, content, aside) with
justify-content: space-between on the hero and content grids.
The gap-x is preserved as minimum spacing. When max-width is
increased later, sidebars will push toward screen edges while
content stays centered.
2026-04-01 11:47:51 +02:00
Tommy D. Rossi 9864c1487b fix: resolve publicDir via spiceflow virtual module for Vercel compat
The website was using `import.meta.dirname` with relative paths to
resolve `publicDir` and `cacheDir`, which broke on Vercel because the
deployment directory structure differs from source layout:

- Source: `src/pages/index.tsx` → `../../public` → `website/public/`
- Vercel: `rsc/index.js` → `../../public` → `/var/public` (ENOENT)

Now uses spiceflow's `publicDir` export (from `virtual:spiceflow-dirs`)
which resolves correctly in both dev and Vercel deployments.

Also makes cache writes optional with try/catch since Vercel's
filesystem is read-only — images regenerate in-memory on each request.

- **spiceflow** bumped to `1.18.0-rsc.22` (adds `publicDir`/`distDir`)
- **image-cache.ts** wraps `mkdirSync`/`writeFileSync` in try/catch
- **index.tsx** imports `publicDir` from spiceflow instead of computing it
2026-03-31 02:43:00 +02:00
Tommy D. Rossi 7d54c4b6a0 fix: handle chrome-extension:// iframes from other extensions (#18)
Chrome's chrome.debugger.attach API refuses to attach to tabs containing
chrome-extension:// iframes from other extensions (LastPass, SurfingKeys,
password managers, etc). This causes the extension to immediately disconnect
after clicking the icon.

Two-layer fix:

1. Pre-attachment (removeRestrictedIframes): when chrome.debugger.attach fails
   with the chrome-extension:// error, use chrome.scripting.executeScript to
   remove restricted iframes from the DOM, then retry attachment. Requires
   new 'scripting' permission.

2. Runtime filtering (onDebuggerEvent): when Target.attachedToTarget arrives
   for a restricted child target (injected after debugger attached), filter it
   out immediately — don't track, don't forward to relay, detach the child
   target. Defense-in-depth against runtime re-injection by other extensions'
   content scripts.

Test: loads a fixture extension alongside playwriter, creates a page with a
chrome-extension:// iframe, enables the debugger, and verifies it stays
connected and can execute JS.

Closes #18
2026-03-29 23:26:26 +02:00
Tommy D. Rossi d82f82f978 add server-side automatic image placeholder generation with JSON cache
Previously, placeholder images for PixelatedImage had to be manually generated
by running a separate script (generate-placeholders.ts) before each build, and
passed explicitly as static Vite imports. The placeholder prop was never actually
wired up in the MDX content, so the fade-in effect wasn't working.

This replaces the entire approach with on-demand server-side processing:

**Architecture**

index.tsx is now a server component (no more 'use client'). During each render,
it calls buildImageManifest() which walks the mdast AST, finds all image srcs
(both <PixelatedImage src=...> JSX nodes and markdown ![alt](url) nodes), and
for each local public/ image:

1. Checks .cache/images/<name>.json — if present and mtime matches, returns cached data
2. On cache miss: reads dimensions with image-size, generates 64px-wide placeholder
   PNG with sharp, encodes as base64 data URI, writes JSON cache

Placeholder injection uses two mechanisms:
- mdxComponents mapping: wraps PixelatedImage with PixelatedImageWithPlaceholder
  that injects placeholder + dimensions from the manifest via closure. This handles
  JSX elements like <PixelatedImage src=...> in MDX, including when nested inside
  other components like <Bleed>. renderNode is NOT called for nested JSX children
  in safe-mdx, so the component mapping is the correct injection point.
- renderNode: converts markdown images ![alt](url) to PixelatedImage with placeholder

**Cache format** (.cache/images/, gitignored)

  { mtime: 1234567890, width: 1280, height: 800, placeholder: 'data:image/png;base64,...' }

Invalidation is mtime-based — any change to the source image regenerates the cache.
On warm runs (no source change), the manifest is built entirely from fast JSON reads.

**Files**

- website/src/lib/image-cache.ts (new): buildImageManifest(), collectImageSrcs(),
  getJsxAttr(), getAttrStringValue() — pure server utilities, no React
- website/src/pages/index.tsx: remove 'use client', make IndexPage async, move
  rendering logic inside component body, add PixelatedImageWithPlaceholder wrapper
- website/src/components/markdown.tsx: update PixelatedImage JSDoc to reflect
  the new auto-injection pattern
- website/package.json: add image-size dep, simplify build script (no more
  generate-placeholders.ts pre-step)
- deleted: website/scripts/generate-placeholders.ts
- deleted: website/src/assets/placeholders/placeholder-*.png (6 files)
2026-03-29 16:44:27 +02:00
Tommy D. Rossi 962e4f83b5 move flattenTocTree and TOC types from markdown.tsx to toc-tree.ts
markdown.tsx is a 'use client' module. Any function exported from it cannot
be called in a server component (RSC boundary). flattenTocTree and the TOC
type definitions (TocNodeType, VisualLevel, TocTreeNode, FlatTocItem) are
pure functions with no hooks or browser APIs, so they belong in toc-tree.ts
which has no 'use client' directive.

Changes:
- toc-tree.ts: add all TOC types and flattenTocTree (self-contained, no imports
  from markdown.tsx to break the circular dep)
- markdown.tsx: re-export the types from toc-tree.ts for backward compatibility,
  remove the now-duplicated definitions
- toc-tree.test.ts: update import to come from toc-tree.ts directly; also update
  6 stale inline snapshots (prefix spacing normalization from the prefix fix in
  addPrefixes — '└ ' → '└─ ')
2026-03-29 16:39:07 +02:00
Tommy D. Rossi 22ee8b3cf4 rename CSS variable --code-bg to --border-subtle
--code-bg was being used for both code block backgrounds and border colors,
which created semantic confusion. The usages in TocLink hover background,
SectionRow aside border, and SidebarBanner border are really border/surface
accents, not code backgrounds.

Rename to --border-subtle with adjusted values:
- light: rgba(0,0,0,0.02) → rgba(0,0,0,0.08) (more visible border)
- dark:  rgba(255,255,255,0.05) → rgba(255,255,255,0.12) (more visible border)

Note: --code-bg as a name still exists as a concept in the codebase where it
refers to actual code block backgrounds, those usages are separate.
2026-03-29 16:38:20 +02:00
Tommy D. Rossi 7a11505cbf self-host Inter and JetBrainsMono fonts instead of fetching from GitHub
Inter variable font v4.1 (roman, 344KB woff2) and JetBrainsMono Nerd Font
Mono v3.3.0 (regular, 2.3MB ttf) are now served from /fonts/ in public.

Removes runtime dependency on raw.githubusercontent.com for font loading.
2026-03-29 14:05:11 +02:00
Tommy D. Rossi 19d575c7e3 fix(ci): use pnpm/action-setup@v4 instead of @master
@master was installing pnpm 11.0.0-beta.3 instead of 10.18.1, which
doesn't support onlyBuiltDependencies from pnpm-workspace.yaml.
2026-03-29 14:01:48 +02:00
Tommy D. Rossi 778b332320 fix(ci): use --allow-build flags and add debug output 2026-03-29 14:00:37 +02:00
Tommy D. Rossi ab514dc1f2 fix(ci): pass onlyBuiltDependencies directly via CLI config flags
Config from pnpm-workspace.yaml is not being read in CI. Pass build
dependency settings directly on the command line as --config flags.
2026-03-29 13:59:18 +02:00
Tommy D. Rossi 59ce3b8416 fix(ci): pin pnpm to 10.18.1 to match local version
pnpm v10 early versions may not read onlyBuiltDependencies from
pnpm-workspace.yaml correctly. Pinning to 10.18.1 which is confirmed
to work locally.
2026-03-29 13:57:20 +02:00
Tommy D. Rossi 5415aa4c33 fix(ci): move onlyBuiltDependencies to pnpm-workspace.yaml
pnpm v10 reads onlyBuiltDependencies from pnpm-workspace.yaml, not
package.json. This was why CI kept failing with ERR_PNPM_IGNORED_BUILDS
despite the config being set.
2026-03-29 13:54:08 +02:00
Tommy D. Rossi ebd5634eee fix(ci): replace deprecated actions and fix pnpm v10 build script errors
- upgrade actions/checkout and actions/setup-node from v3 to v4
- replace covbot/pnpm-install-with-cache@v1 with standard pnpm store
  caching via actions/cache@v4 + direct pnpm install
- add ignoredBuiltDependencies for electron (playwright submodule
  devDep, not needed in CI)
2026-03-29 13:50:05 +02:00
Tommy D. Rossi 55b5d3788b fix: add onlyBuiltDependencies to resolve pnpm v10 ERR_PNPM_IGNORED_BUILDS in CI
pnpm v10 errors (exit code 1) when packages have build scripts that
aren't explicitly approved. Adding bufferutil, esbuild, and sharp to
onlyBuiltDependencies makes pnpm build those and silently warn about
electron (playwright submodule devDep, not needed in CI).
2026-03-29 13:46:47 +02:00
Tommy D. Rossi a746f895da release: playwriter@0.0.102 playwriter@0.0.102 2026-03-29 13:25:52 +02:00
Tommy D. Rossi 66e456e440 restore platform-specific Chrome launch instructions in skill.md
These were removed when browser start was introduced. Now that
browser start is deprecated, restore the original manual Chrome
launch commands and tab-capture flags documentation.
2026-03-29 13:23:21 +02:00
Tommy D. Rossi a55191c37a remove browser start references from skill.md
Command is deprecated and hidden from --help, so agent-facing
docs should no longer mention it.
2026-03-29 13:19:13 +02:00
Tommy D. Rossi 9253b57238 Update skill.md 2026-03-29 13:17:38 +02:00
Tommy D. Rossi f1187472d8 deprecate browser start command, hidden from --help
Update goke to 6.3.2 which adds .hidden() for commands.
browser start still works when called directly but no longer
appears in --help output.
2026-03-29 13:15:53 +02:00
Tommy D. Rossi 1588381c7d expose browser object in sandbox, document --direct profile access
- Add 'browser' to VM sandbox context so user code can call
  browser.contexts() to access pages from different Chrome profiles
- Update resetPlaywright to also refresh browser reference
- Document in skill.md: --direct gives access to all profiles via
  browser.contexts(), add browser to context variables list
2026-03-29 13:15:20 +02:00
Tommy D. Rossi b7397f877c simplify PLAYWRITER_DIRECT to only accept '1' for auto-discovery
Remove 'auto' and 'true' aliases — '1' is the standard boolean env var
pattern. Explicit ws:// endpoints still work as before.
2026-03-29 13:08:32 +02:00
Tommy D. Rossi e920c5c723 docs: update --direct references for Chrome 136+ and move to session new only
- skill.md: remove 'playwriter --direct' from MCP examples, use env var only
- MCP.md: add Direct CDP section with PLAYWRITER_DIRECT config example
- CHANGELOG.md: add 0.0.101 entry, fix stale 'playwriter --direct' in 0.0.98
- Bump version to 0.0.101
2026-03-29 13:01:01 +02:00
Tommy D. Rossi f2ee02079b fix(playwriter): support Chrome 136+ direct CDP discovery (404 on /json/version)
Chrome 136+ with chrome://inspect debugging returns 404 on all HTTP
endpoints (/json/version, /json, /json/list) but still accepts WebSocket
connections on /devtools/browser/*. The first WS connection triggers a
user-approval dialog in Chrome.

Previously, discoverChromeInstances() and resolveDirectInput() treated
HTTP 404 as 'blocked' and either returned nothing or threw an error,
making --direct unusable without manually constructing a ws:// URL.

Changes:
- discoverChromeInstances() now returns a synthetic instance for 'blocked'
  probes with a base ws:// URL, so browser list shows detected Chrome
- resolveDirectInput() returns a ws URL instead of throwing on 'blocked'
- appendSessionToWsUrl() appends session ID to bare /devtools/browser/
  paths for unique WS connections; leaves full GUID paths (from live
  probes) untouched
- Relay appends CLI session ID, MCP appends crypto.randomUUID()
- Removed --direct from root CLI command (only on session new now);
  MCP users configure via PLAYWRITER_DIRECT env var
- browser list shows connect hint when direct instances are detected

Discovery never triggers a WebSocket connection, so Chrome's approval
dialog only appears when the user explicitly runs a command.
2026-03-29 12:59:31 +02:00
Tommy D. Rossi 874fb5f060 fix(playwriter): default resizeImageForAgent format to PNG instead of JPEG
resizeImageForAgent() defaulted to JPEG when no format was specified,
causing image/png vs image/jpeg mismatch errors when MCP clients
(like Anthropic API) validated the media type against the actual image data.

All playwriter images are now consistently PNG unless explicitly
overridden with format: 'jpeg'.
2026-03-29 11:12:35 +02:00
Tommy D. Rossi 23bca69782 style(website): increase vertical padding for non-root TOC tree nodes
Non-root tree items (visualLevel > 0) now use 4px vertical padding
instead of 2px, giving nested headings more breathing room while
keeping root-level items compact.
2026-03-29 11:12:22 +02:00
Tommy D. Rossi ad134d3d71 feat(website): add FullWidth MDX component + TOC expand fix + styling tweaks
- Add FullWidth marker component that spans both content and aside grid
  columns (lg:col-[2/-1]). Extracted at parse time like Aside/Hero.
  FullWidth sections get my-5 vertical margin to match content rhythm.

- Fix auto-expand: clicking an expandable TOC item now expands it.
  Previously only ancestors were expanded, not the item itself.

- TOC tree prefix characters use 1.2em font size with negative margin
  so they don't inflate line height. Prefix indentation reduced to 1
  char per level.

- Use FullWidth for the hero screenshot in index.mdx as demo.
2026-03-28 19:19:30 +01:00
Tommy D. Rossi a896fd69cb nicer unicode tree chars 2026-03-28 18:48:57 +01:00
Tommy D. Rossi 41908cd6ad style(website): restyle TOC sidebar — chevron icons, spacing, font weight
- Add SVG ChevronIcon component for expandable items, shown on the right
- Toggle expand only on chevron click, not the whole row
- Remove width: fit-content from aside so it fills its column
- Add 6px margin between root-level TOC groups
- Use regular font weight (400) for all tree items
- Remove lowercase text-transform from tree items
- Remove line-height override from TocLink (inherits from parent)
2026-03-28 18:28:46 +01:00
Tommy D. Rossi d0802128f3 style(website): move font-size from proseStyle to slot-main so asides use toc size
Removed fontSize from the shared proseStyle object and added it to all
slot-main containers instead. Prose elements (P, OL, List) now inherit
font-size from their parent container rather than setting it explicitly,
so inside the aside they respect --type-toc-size (13px) instead of
overriding it with --type-body-size (14px).
2026-03-28 18:11:43 +01:00
Tommy D. Rossi b2adc1caaa style(website): replace aside background with 1px border and match body font size
- SectionRow aside and SidebarBanner now use a border instead of bg fill
- font size changed from --type-toc-size (13px) to --type-body-size (14px)
- line height aligned with prose style (1.6)
2026-03-28 18:07:53 +01:00
Tommy D. Rossi c183e1ba99 style: increase hero top/bottom margin on landing page
Changes Hero className from my-6 to my-16 for more breathing room around
the hero section on the website homepage.
2026-03-28 16:39:07 +01:00
Tommy D. Rossi 65922f1bbf chore: upgrade vite to v8.0.3 across all packages
Bumps vite from various versions (5.x, 6.x, 7.x) to v8.0.3 uniformly:

- root package.json: ^7.2.2 → ^8.0.3
- extension/package.json: ^5.4.21 → ^8.0.3
- website/package.json: ^7.3.0 → ^8.0.3
- playwright submodule package.json: ^6.4.1 → ^8.0.3
2026-03-28 16:39:04 +01:00
Tommy D. Rossi b835e3b907 feat(playwriter): add Kitty Graphics Protocol support to CLI + JPEG→PNG migration
When AGENT_GRAPHICS=kitty is set, the CLI now emits screenshots and resized
images as Kitty Graphics Protocol APC escape sequences to stdout. Agents with
kitty-graphics-agent (or compatible parsers) automatically extract PNG images
and pass them to the LLM as media parts — no extra tool call needed.

Key changes:

- **Screenshots → PNG**: screenshotWithAccessibilityLabels() now captures PNG
  instead of JPEG. PNG is lossless and the only format extracted by the Kitty
  Graphics Protocol (f=100). resizeImage() gains a format option ('jpeg'|'png').

- **kitty-graphics.ts**: New utility with canEmitKittyGraphics() (checks
  AGENT_GRAPHICS env var) and emitKittyImage() (writes chunked APC escape
  sequences to stdout at 4096 bytes per chunk per the Kitty spec).

- **resizeImage → resizeImageForAgent**: Renamed to clarify intent. The wrapper
  in executor.ts now collects resized images into result.images so they are
  emitted via Kitty Graphics in the CLI and included as image parts in MCP.
  Old resizeImage name kept as backward-compatible alias in the sandbox.

- **CLI deduplication**: Uses a Set<base64> to avoid emitting the same image
  twice (screenshots appear in both result.screenshots and result.images).

- **10 new tests** including round-trip validation with kitty-graphics-agent
  parser to verify emitted sequences can be parsed back correctly.

Version 0.0.98 → 0.0.99
2026-03-28 16:37:24 +01:00
Tommy D. Rossi 86773f4cfd Replace TOC search debounce with useTransition for non-blocking input
The 80ms setTimeout debounce blocked the input from reflecting keystrokes
while the TOC re-render was in progress. useTransition splits the work:
setQuery is urgent (input stays responsive), while setSearchState is
deferred and interruptible — new keystrokes cancel in-progress renders.

Input opacity drops to 0.5 during isPending as a subtle loading signal.
2026-03-28 15:09:39 +01:00