* feat(website): serve paseo.sh in Simplified Chinese
Every page of the website now has a Simplified Chinese version under
/zh-CN: the interface, docs, blog posts, comparison pages, legal pages,
all changelog releases and the homepage mockup. English URLs are unchanged.
The URL alone decides the language. There is no browser-language redirect
and no stored preference, because either would have to redirect the
unprefixed English URLs that people share and search engines index.
Internal links carry the page's language instead, and hreflang alternates
point Chinese search results at /zh-CN.
Translated headings pin the English anchor with {#id}, so section links
work in both languages. content-translations.test.ts fails when a page
has no translation or a translation's headings drift from the English.
Changelog releases and plugin listings without a translation render
whole in English under a notice, so a release never waits on one.
Plugin manifests and the registry gain optional translations and
OVERVIEW.zh-CN.md fields; older clients and registries ignore them.
* feat(website): replace the native language select with an EN/ZH menu
The header showed an OS-native select whose popup did not match the site.
The trigger now shows the language abbreviated (EN, ZH) and opens a menu in
the app's dropdown style that names each language in itself, with its name
in the page language underneath. Rows are real links to the same page in
that language; arrows, Escape, outside presses and taps behave as a menu
button, and the menu keeps itself on screen when a narrow header pushes the
trigger toward an edge.
Also drops the stray space after the full-width comma in the Chinese
homepage subtitle.
* docs(website): translate main's new docs and 0.12.0 release notes
Main added Keep run workspaces to the schedules docs, rewrote plugin icons
around inline SVG, and promoted 0.12.0 with new entries. Bring the Chinese
pages and changelog in line so no section falls back to English.
* fix(server): leave plugin listing translations to the registry
The daemon rejected manifests whose translations the registry would refuse,
so a plugin main installs could fail to load here. Paseo never reads
translations; the registry validates them at publish. Docs now say which
checks are automated at publish and which are human review.
* docs(plugins): describe the registry's final overview translation discovery
* style(server): format manifest test
* test(app): measure sidebar rows one at a time
After a reload the developer error toast appears while expectVerticalOrder
measures every row at once. Each concurrent boundingBox runs the toast
locator handler, and only one of them resumes, so the poll timed out on
every attempt since #7424. Measuring sequentially keeps one action in flight.
* test(app): return sidebar row measuring to main
#7480 hides the developer toast instead of running a locator handler, so
measuring the rows concurrently no longer races it. Reverts 41ac23016.
* test(app): wait for the compact shell before opening compact Changes
The shell survives the breakpoint, so right after the resize the wide
Explorer could still be visible: the test skipped the Explorer toggle and
the tab then disappeared, or toggled at the wrong time. Wait for the
compact-shell marker, as the composer helpers do since #7424.
* docs(website): translate main's plugin file panel docs
* test(app): edit the code font size after the font family remount
A committed appearance change remounts the settings screen through
AppearanceStyleBoundary, and the new form starts from the stored size.
Filling the size before that remount lost the edit in CI.
* docs(website): translate main's client/ui import docs
Follows #7474: one /client/ui entry, StreamedText instead of useRevealedText,
SettingsGroup dropped from the settings table, and the new
'UI imports in v0.13' migration section.
* test(app): wait for the previous layout's file panel picker to leave
After a resize the old layout's picker animates out while the new one is
already mounted, so the Use button matched two elements and the strict
locator failed. Wait for the shell's layout and a single picker first.
* fix(app): group native assistant prose into selectable runs
* fix(app): render selectable native prose with enriched markdown
* docs: explain native prose selection boundaries and tradeoffs
* fix(app): preserve markdown semantics and render limits in prose runs
* docs: record native prose compatibility fixes and heading limits
* fix(app): allow additive task lists and admonitions in native prose
* fix(app): preserve inline image formatting and reference scope
* fix(app): preserve markdown source and file actions in native prose
Prepare original source blocks with source-position edits from the assistant parser. Keep reference scope and live/history projection consistent, and retain link text when dispatching workspace file actions.
* fix(app): preserve native prose entities and links while bounding preparation cost
* fix(app): preserve provisional syntax at cached reveal boundaries
* fix(app): bound prose append work and preserve markdown edge cases
* fix(app): preserve home paths and normalized prose links
* fix(app): preserve parser-owned links and streaming tilde tails
* fix(app): keep growing markdown on the streaming block renderer
* fix(app): preserve bracketed IPv6 links in native prose
* fix(app): skip prose classification for blocks over the render cap
* fix(app): share selectable native prose across Markdown surfaces
* fix(app): preserve Markdown containers before grouping prose
* revert(app): remove shared prose-run renderer changes
Reverts ab4737bb2 and ac47e1063 before replacing the Markdown engine across all platforms.
* feat(markdown): use enriched renderer across all platforms
* test: verify library image controls and isolate website config
* fix(markdown): preserve file links and image revisions
* fix(markdown): restore diagrams and preserve link origin
* fix(markdown): retain diagram identity and reference scope
* fix(markdown): avoid empty web history measurements
* fix(markdown): preserve authored empty link titles
* test(markdown): exercise find across whole-message history
* fix(markdown): preserve encoded image paths after rebase
Keep the shared renderer image normalization and file-read regression coverage that originally lived in the main integration merge.
* fix(markdown): derive table and task list colours from the theme
* fix(markdown): restore selection highlight on web
* fix(markdown): restore file link hover tooltips on web
* fix(markdown): normalize inline HTML before rendering
* fix(markdown): create renderer types when applying package patch
The published nightly omits both MarkdownRenderers type files. Represent them as new files so patch-package succeeds on a fresh npm install, matching the already patched development package.
* fix(markdown): keep the renderer tree stable while images resolve
* fix(markdown): size inline images to the line and collapse failed loads
* fix(markdown): normalize only parsed HTML nodes
* fix(markdown): keep the first image reference definition
* fix(markdown): preserve complete assistant messages on native
* fix(markdown): classify web links explicitly and complete renderer types
* fix(markdown): preserve spacing between HTML elements
* fix(markdown): normalize HTML following code elements
* fix(markdown): preserve literal image fallback labels
* fix(markdown): retry transient image decode failures
* docs(markdown): record native selection and image limitations
* fix(markdown): preserve first definitions across Mermaid segments
* fix(markdown): reserve pending images and harden web rendering
* test(markdown): cover state retention during image arrival
* fix(markdown): preserve reader position while responses grow
Keep the same message component boundary when a live row becomes retained history, preserving Mermaid runtimes and selection nodes. Observe paced child text commits at the web viewport so sticky-bottom follows before the next frame.
* test(markdown): require growth while holding a streaming paragraph
* test(markdown): isolate image arrival from reload positioning
Mounted row totals change as history virtualizes, so observe the new response and tool directly. Hold the real PNG response after reload until the reading position settles, then verify its reserved height and viewport instead of attributing initial bottom-follow positioning to image decoding.
* fix(markdown): preserve adjacency around inline HTML
* fix(markdown): keep summary block boundaries outside bold text
* perf(markdown): scan HTML block boundaries incrementally
* test(markdown): gate delivery while establishing a held reader
* test(markdown): name shared renderer setup and file actions
* test(markdown): assert linear HTML boundary work without timing
* test(markdown): check large HTML table output without observing built-ins
* fix(markdown): restore accentBright link colour
* fix(markdown): require letters and known extensions in bare path recognition
* fix(markdown): match main's blockquote, bullet and h6 colours
* fix(android): keep inverted chat still during text selection
React Native decomposes Android list inversion into a 180-degree rotation with positive scaleY, so the existing visibility-scroll guard never recognized the inverted viewport. Inspect the composed matrix and reproduce the actual list transform through the native view manager in regression tests.
* fix(markdown): linked inline code keeps inline code size
* fix(markdown): reserve a placeholder box with an image glyph while images load
* fix(markdown): bare prose paths need an extension, line suffix or depth
Slash-separated English pairs were recognized as file links in prose. Require stronger path intent there while preserving the existing inline-code recognition rule.
* fix(markdown): horizontal blocks own their drags on compact layouts
* perf(markdown): keep native streaming updates in the unfinished tail
Native markdown reparses each updated view in full. Keep parser-closed blocks in a memoized head during paced reveal, then reunite completed prose for cross-paragraph selection. Preserve trailing spacing and source indentation at the split.
* perf(markdown): reuse Android measurements across view mounts
* test(markdown): bare Fabel/model and daemon/client stay plain text when copied
a502cfd0d stopped linking bare two-segment prose paths; the clipboard
expectation still carried the links from the earlier loose pattern and
failed on CI (playwright shard 1/4 on 60b4e2d05). main expects the plain
text, so restore that.
* perf(app): limit native chat mount batches
Whole-message Markdown rows measure synchronously during Fabric layout. Render fewer history rows per batch and retain a smaller offscreen window so opening a chat does not measure twelve history rows at once.
* fix(app): keep populated markdown in sync with native layout
Fabric can collapse streaming blocks before asynchronous rendering replaces the old text, producing a blank frame. Commit populated-view updates synchronously while retaining worker preparation for first mounts to avoid moving large history parsing onto the UI thread.
* perf(app): preserve unchanged sidebar workspace projections
Reopening a retained sidebar rebuilt its entry collection and invalidated every project row despite unchanged workspace data. Preserve collection identity as well as row identity, while keeping membership and ordering changes observable.
* Revert "perf(app): preserve unchanged sidebar workspace projections"
This reverts commit a3919faed4.
* feat(plugins): install reviewed artifacts from open registries
* fix(website): build protocol before deployment checks
* fix(website): make plugin pages discoverable to search engines
Resolve missing registry entries in route loaders to return HTTP 404. Serve a registry-backed sitemap so newly listed plugins and authors are discoverable without rebuilding the website.
* feat(website): keep plugin directory unlinked for coordinated launch
Preserve direct routes and SEO while retaining the partner directory link. Cover nested Git artifacts through registry installation and pinned website commands.
* fix(website): link plugin cards by registry owner and stub the clipboard in e2e
Plugin and author URLs come from the owner segment of the plugin ID, not the author's GitHub login, so a listed plugin always resolves. The Playwright spec records clipboard writes instead of granting a permission WebKit does not have.
The Cloud page pitched a hosted multi-user product and collected emails
through a signup form. The Hub is a self-hosted service in private beta
and the only way in is a Discord DM, so the signup server function and
its webhook are deleted rather than repointed. /cloud now 301s to /hub
from the worker entry, which already owns the site's permanent path
redirects, so old links and search results keep working.
The homepage also drops split panels, service proxy, voice, and keyboard
shortcuts. They had turned the page into a feature list; it should show
what Paseo enables at a high level.
Brand and provider icons move into shared modules so the Hub page, the
site header, and the landing page each use one copy instead of three.
* Rework docs navigation and move alternatives to top-level pages
- Add nested docs nav with categories, collapsible groups, breadcrumbs,
and a right-hand page outline with heading slugs.
- Move alternatives out of /docs/alternatives into /alternatives/*
marketing pages with 301 redirects.
- Add github-slugger for heading IDs and require explicit width on
SiteShell so new pages can't accidentally pick the narrow prose layout.
- Remove the best-practices doc from public-docs.
* Make docs content column prose-width
Addresses the high-volume issues from the Ahrefs audit:
- Worker entrypoint 301s every request not on https://paseo.sh
(www subdomain and http variants), killing the duplicate-page
errors and the HTTP→HTTPS internal-link notices.
- pageMeta() emits <link rel="canonical"> and og:url per route;
every head() call now passes its canonical path.
- /blog stops rewriting itself to /blog?drafts=false; the search
param only appears when explicitly true.
- Sitemap includes /blog and each post.
- Trim homepage title under SERP truncation, rewrite /agents meta
description under 160 chars, pad short descriptions on
/privacy, /download, /cloud, /changelog, /docs.
- app.paseo.sh gets <meta robots=noindex,nofollow> and a
Disallow-all robots.txt so the SPA shell stops polluting the
crawl with H1-missing / no-OG / low-word-count warnings.
- New /cloud page with signup form (email, name, company, role, message)
posting to a Discord webhook via a TanStack Start server function on
the Cloudflare Worker. Webhook URL read from DISCORD_WEBHOOK_URL secret.
- Extract SiteShell, SiteFooter, FAQItem from landing-page.
- Migrate /privacy, /download, /blog, /changelog, /cloud to SiteShell so
they share header + footer with consistent width.
- Add /cloud to header and footer nav.
- Make /docs sidebar full-width; bump footer text from xs to sm.
- Add Cloudflare Workers types and ignore .dev.vars.
* docs: rename to lowercase + drop leftover plans
Rename docs in docs/ to lowercase kebab-case for consistency and update
all references in CLAUDE.md, CONTRIBUTING.md, CHANGELOG.md,
packages/server/CLAUDE.md, and inter-doc links.
Drop two leftover design plan docs:
- docs/ATTACHMENT_BASED_REVIEW_CONTEXT_PLAN.md
- docs/plan-approval-normalization.md
* docs: drop stale uppercase entries from case-insensitive rename
* feat(website): power /docs from public-docs/ markdown tree
Move website docs out of TSX route components and into a root-level
public-docs/ directory of plain markdown files with frontmatter
(title, description, nav, order).
- Add packages/website/src/docs.ts loader using import.meta.glob with
?raw to compile the markdown into the bundle at build time.
- Replace the 9 hand-written docs/*.tsx routes with a single $.tsx
catch-all that renders any slug via react-markdown.
- Drive the docs sidebar nav from frontmatter order/nav.
- Auto-discover docs routes in vite.config.ts so the sitemap stays in
sync without manual edits.
* fix(website): bind dev server to 0.0.0.0 so port collisions trigger fallback
`host: "127.0.0.1"` (or unset) lets macOS coexist with another process
holding an IPv6 dual-stack `*:8082` socket, so Vite never sees
EADDRINUSE and silently binds alongside it. Forcing IPv4 wildcard
makes the conflict real, and Vite's default `strictPort: false`
falls through to the next free port.
* fix(website): restore docs page styling after markdown migration
Add a .docs-prose class that mirrors the styling the original
docs/*.tsx components hand-rolled (h1/h2/h3 sizes, paragraph/list
spacing, link colors, code blocks, callout-style blockquotes).
ReactMarkdown was emitting unstyled HTML because the previous
wrapper class only had inline-code rules — headings and code
blocks fell back to user-agent defaults.
Splits first-class and custom provider docs out of the Configuration
page onto /docs/providers. Introduces the first-class providers
(claude, codex, opencode, copilot, pi) and covers extending a provider,
Z.AI and Qwen plans, multiple profiles, custom binaries, ACP agents,
additionalModels, and disabling. Points to docs/CUSTOM-PROVIDERS.md on
GitHub as the source of truth.
Add a new Integrations section in desktop settings that lets users
install the Paseo CLI and orchestration skills directly from the app.
CLI install symlinks the bundled shim to ~/.local/bin and updates the
user's shell rc file if needed. Skills install copies SKILL.md files to
~/.agents/skills/, symlinks into ~/.claude/skills/ for Claude Code, and
copies to ~/.codex/skills/ for Codex. Both use a single status check
code path for install verification and on-load detection.
Also adds a /docs/skills page documenting all six orchestration skills,
extracts shared settings styles (section headers, rows) used across
the daemon, integrations, and shortcuts sections, and reorders the
settings sidebar to group integrations and daemon together.
Move release version resolution from build-time (vite.config.ts) to
runtime (server function on Cloudflare Workers). The server function
fetches GitHub releases, filters out prereleases/drafts, and only
returns a version that has all required assets (Mac DMG, Linux AppImage,
Windows exe). Uses Cloudflare's cf.cacheEverything with a 60s TTL to
avoid hitting GitHub on every request.
This fixes a race condition where the website would deploy and show
download links for a new release before the desktop build assets were
actually uploaded, resulting in 404s for users.
The download page was showing pre-release versions because it read
the version directly from package.json. Now fetches the latest
non-prerelease from the GitHub Releases API at build time, with
fallback to package.json for local dev.