镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

feat(plugins): open a plugin page from buzz://plugin links - #2053

Merged
joahg merged 3 commits into
block:mainfrom
joahg:joah/plugin-page-links
Oct 31, 2026
Merged

joahg merged 3 commits into
block:mainfrom
joahg:joah/plugin-page-links

Conversation

@joahg

@joahg joahg commented Oct 30, 2026

Copy link
Copy Markdown
Member

Summary

Adds buzz://plugin?id=<plugin id>[&page=<page id>][&route.<key>=<value>...], which opens a page contributed by an installed, enabled plugin. Closes #2051

Behavior

  • No page: the primary page, else the first page.
  • route.<key> values become string params only if the page's route.validate accepts them; otherwise the page opens without params (never partial ones).
  • Follows the existing grammar: token ids, duplicate id/page/route.<key> rejected, unknown params ignored, bounds of 16 params, 64-char keys and 1,024-char values, Windows plugin/? root slash accepted, malformed links fail as invalid-target.
  • Not installed, disabled or no such page: the ordinary "This page isn't available" notice; Try again reopens the link once the plugin is available. A cold-start link waits for the plugin runtime to settle.
  • Works from the OS and in message content. The link binds to the selected community and viewer like the other navigation links.
  • Implemented as a new kind: "plugin" OpenTarget resolved in useAppNavigation, 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

  • Unit: link parser and target bounds, deep-link ingress, message-link recognition and click handling, and an app-level test for primary/named/first page, refused validator, missing plugin and page.
  • pnpm typecheck, pnpm check, full pnpm exec vitest run, and the browser buzz-links spec.
  • Docs: docs/deep-links.md, docs/plugin-architecture.md.

🤖 Generated with Claude Code

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>
@joahg
joahg marked this pull request as ready for review October 30, 2026 22:30
@joahg
joahg requested a review from a team as a code owner October 30, 2026 22:30
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 kazemek21 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread src/features/navigation/buzz-links.ts Outdated
return parseOpenTarget({
version: 1,
kind: "conversation",
kind: link.format === "plugin" ? "plugin" : "conversation",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 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>
@joahg
joahg merged commit 2e84e30 into block:main Oct 31, 2026
40 checks passed
shellz-n-stuff added a commit that referenced this pull request Oct 31, 2026
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>
morgmart added a commit that referenced this pull request Oct 31, 2026
…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add buzz://plugin links that open a plugin page

2 participants