Repository navigation
feat(plugins): open a plugin page from buzz://plugin links - #2053
Conversation
Add `buzz://plugin?id=<plugin>[&page=<page>][&route.<key>=<value>...]`. Without `page` the plugin's primary page opens, otherwise its first page. `route.*` values are run through the page's `route.validate`; when the page has no route or validation fails, the page opens without params. Links follow the existing grammar: token-shaped ids, duplicate known params are rejected, unknown params are ignored, and malformed links resolve to `invalid-target`. A plugin that is not installed, disabled or has no matching page fails with the ordinary "This page isn't available" notice. Links also work when written in message content. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
It renders 25 bodies of 19,000 words and runs past Vitest's 5s default on the first flake-check iteration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
kazemek21
left a comment
There was a problem hiding this comment.
I reviewed the PR and went through the Nexus review-tracker notes. The inline size-boundary comment is non-blocking.
Posted by Codex (gpt-6.1-sol) on behalf of kylez
| return parseOpenTarget({ | ||
| version: 1, | ||
| kind: "conversation", | ||
| kind: link.format === "plugin" ? "plugin" : "conversation", |
There was a problem hiding this comment.
[P2] Handle target overflow after binding the scope
Plugin links are validated before scope is added, but this second parse can exceed the 8,192-byte limit. I reproduced an accepted 8,155-byte target becoming 8,287 bytes after binding. ConversationHost.open does not catch the exception, so clicking the message link skips both navigation and the unavailable notice. Could this failure return through the normal unsuccessful-open path instead?
Posted by Codex (gpt-6.1-sol) on behalf of kylez
There was a problem hiding this comment.
🤖 Fixed in 4cf35e3. buzzLinkTarget now catches the second parseOpenTarget and returns null, so a link that only goes over the limit once scoped takes ConversationHost.open's normal unsuccessful path. The new test builds the largest plugin link that still fits with the test scope, then checks that a longer community origin gives null instead of throwing.
A plugin link is size-checked before the receiving community's scope is added, so binding the scope could push the target past the limit and throw out of ConversationHost.open, skipping both navigation and the unavailable notice. Return null so it takes the normal unsuccessful-open path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Picks up the month-boundary StatusEditor test fix (#2064). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * origin/main: (56 commits) perf(sidebar): subscribe to selected live and list facts (#1966) feat(desktop): align the sign-in callback page with web branding (#2038) Show linked PRs in Solo headers and move idle huddles to Chat actions (#1968) feat(messages): mark edited messages with "(edited)" (#2020) feat(desktop): bundle the buzz CLI in every package (#2027) feat(plugins): open a plugin page from buzz://plugin links (#2053) feat(plugins): managed plugin sources (#2052) test(browser): prevent sampled clock pause races (#1955) test: fix month-boundary and Sonner mount races in StatusEditor and RelayStaff tests (#2064) test: stop tests leaving temp dirs and a flake-check fixture behind (#1977) fix(messages): release Virtua's held range when scrolling stops (#2032) test(mcp): allow protection checks in bundled help fixture (#2061) fix(emoji): recover transient image failures without reopening chats (#2000) test: wait for dialog focus, reach a reading position, re-sign pair() (#1975) feat(plugins): sign builderlab in through the native fi session in fi builds (#1461) feat(agents): offer bundled Buzz help (#2033) fix(github): show pull request status for anchors and subpages (#2055) fix(dialogs): focus safe controls and task inputs (#2021) fix(composer): truncate workspace names at narrow widths (#2043) AIDA-2287: Refresh public channel search incrementally (#2023) ... Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
…isibility * origin/main: (23 commits) feat(profiles): show NIP-FI verified names (BUZZ-433) (#2005) feat(identity): sign in to the organization when Buzz opens (#2086) perf(identity-names): stop rerunning name policy for live member churn (#2092) fix(sidebar): keep the channel drag preview under the pointer (#2019) fix(identity): use xyz.block.buzz.desktop federated-session keychain service (#2094) perf(relay): read each channel's remote access once per accepted batch (#2091) fix(styles): keep page status :has() on each rule's subject (#2081) fix(navigation): show a notice for invalid deep links (#2054) fix(messages): preserve held readers through equal-height resizes (#1963) perf(sidebar): subscribe to selected live and list facts (#1966) feat(desktop): align the sign-in callback page with web branding (#2038) Show linked PRs in Solo headers and move idle huddles to Chat actions (#1968) feat(messages): mark edited messages with "(edited)" (#2020) feat(desktop): bundle the buzz CLI in every package (#2027) feat(plugins): open a plugin page from buzz://plugin links (#2053) feat(plugins): managed plugin sources (#2052) test(browser): prevent sampled clock pause races (#1955) test: fix month-boundary and Sonner mount races in StatusEditor and RelayStaff tests (#2064) test: stop tests leaving temp dirs and a flake-check fixture behind (#1977) fix(messages): release Virtua's held range when scrolling stops (#2032) ... # Conflicts: # src/bundled/channels/ChannelMemberAvatars.tsx
Summary
Adds
buzz://plugin?id=<plugin id>[&page=<page id>][&route.<key>=<value>...], which opens a page contributed by an installed, enabled plugin. Closes #2051Behavior
page: the primary page, else the first page.route.<key>values become string params only if the page'sroute.validateaccepts them; otherwise the page opens without params (never partial ones).id/page/route.<key>rejected, unknown params ignored, bounds of 16 params, 64-char keys and 1,024-char values, Windowsplugin/?root slash accepted, malformed links fail asinvalid-target.kind: "plugin"OpenTargetresolved inuseAppNavigation, because the registry is not available where message links are parsed.Safety
A link is not authorization; the page still guards its own data. No new native surface: Rust still only queues raw
buzz:URLs and all parsing stays in TypeScript.Testing
pnpm typecheck,pnpm check, fullpnpm exec vitest run, and the browserbuzz-linksspec.docs/deep-links.md,docs/plugin-architecture.md.🤖 Generated with Claude Code