Skip to content

feat(desktop-messages): render compact Buzz permalink chips - #5638

Open
tellaho wants to merge 19 commits into
mainfrom
tho/buzz-permalink-chips
Open

feat(desktop-messages): render compact Buzz permalink chips#5638
tellaho wants to merge 19 commits into
mainfrom
tho/buzz-permalink-chips

Conversation

@tellaho

@tellaho tellaho commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Category: improvement
User Impact: Buzz channel, message, repository, pull request, and issue links now open reliably and display recognizable context in the desktop app.
Problem: Buzz links could appear as raw or ambiguous URLs, and navigation links received during startup or community transitions could be dropped before the UI was ready. Repository and issue shares in particular required hover context to understand at a glance.
Solution: Queue desktop channel/message navigation until the UI is ready, then render bare Buzz permalinks as icon-prefixed chips with concise entity context while preserving user-authored Markdown labels as ordinary links.

File changes

desktop/src-tauri/src/deep_link.rs
Adds validated channel-link parsing and a deduplicated, acknowledged queue so navigation survives frontend startup.

desktop/src-tauri/src/lib.rs
Registers the pending-navigation state and commands with the desktop application.

desktop/src/features/communities/useCommunityInit.ts
Resets queued navigation safely across community boundaries without leaking stale destinations.

desktop/src/features/messages/lib/channelLink.test.mjs
Covers valid, malformed, and canonical channel permalink forms.

desktop/src/features/messages/lib/channelLink.ts
Defines strict parsing and detection for buzz://channel/<uuid> links.

desktop/src/features/messages/lib/composerMessageLinkNode.test.mjs
Extends composer-node coverage for normalized Buzz link content.

desktop/src/features/messages/lib/composerMessageLinkNode.ts
Keeps composer link-node handling aligned with the expanded Buzz link surface.

desktop/src/features/messages/lib/remarkChannelDeepLinks.test.mjs
Verifies bare channel URLs become renderable deep-link nodes without touching code.

desktop/src/features/messages/lib/remarkChannelDeepLinks.ts
Transforms eligible bare channel links into dedicated Markdown nodes.

desktop/src/features/messages/lib/remarkEntityLinks.test.mjs
Covers bare repository, pull-request, and issue detection and code-span exclusions.

desktop/src/features/messages/lib/remarkEntityLinks.ts
Adds dedicated Markdown nodes for bare Buzz project entities.

desktop/src/shared/deep-link.test.mjs
Exercises queued navigation, acknowledgement, serialization, and community-switch behavior.

desktop/src/shared/deep-link.ts
Serializes pending deep-link drains and acknowledges destinations only after successful navigation.

desktop/src/shared/styles/globals/markdown.css
Aligns permalink icon geometry and spacing with agent mention chips.

desktop/src/shared/ui/markdown.test.mjs
Adds integration coverage for every permalink chip, authored labels, fallbacks, icons, and static rendering.

desktop/src/shared/ui/markdown.tsx
Routes channel and entity nodes through the shared presentation path while preserving authored link text.

desktop/src/shared/ui/markdown/BuzzLinkChip.tsx
Introduces the shared interactive/static permalink chip and authored-label inline-link components.

desktop/src/shared/ui/markdown/ChannelDeepLink.tsx
Renders channel shares and references with Hash icons, names, and shortened-ID fallbacks.

desktop/src/shared/ui/markdown/MessageLinkPill.tsx
Renders ordinary message shares with message icons and channel/message context while retaining sent-from-thread behavior.

desktop/src/shared/ui/markdown/entityLinks.tsx
Maps repositories, pull requests, and issues to Projects-aligned icons and contextual labels.

desktop/src/shared/ui/markdown/nodeCache.ts
Includes entity-link rendering in cached Markdown node handling.

desktop/src/shared/ui/markdown/utils.ts
Allows validated channel links through the Buzz URL transform.

desktop/src/shared/useMessageDeepLinks.ts
Drains queued navigation links safely and clears them during teardown.

desktop/src/testing/e2eBridge.ts
Extends the mock bridge with pending-navigation command behavior.

desktop/tests/e2e/community-rail.spec.ts
Verifies queued links do not cross community boundaries.

desktop/tests/e2e/navigation.spec.ts
Covers channel/message deep-link navigation during startup and active sessions.

desktop/tests/helpers/bridge.ts
Adds reusable deep-link mock state and acknowledgement helpers.

Reproduction steps

  1. Run the desktop app and open a channel containing bare buzz://channel, buzz://message, buzz://repo, buzz://pr, and buzz://issue URLs.
  2. Confirm each bare URL renders as one cohesive chip with a type icon, a useful name or shortened identifier, and no duplicated channel # character.
  3. Add an authored Markdown link such as [design discussion](buzz://issue?...) and confirm the supplied label remains an ordinary link rather than becoming a chip.
  4. Select channel and message links and confirm they navigate correctly in warm and cold-start states.

Screenshots / demos

Houston dark theme with custom purple accent (#a855f7), captured from rebased visual implementation ad411cc06; current head 0aafa144f only adjusts E2E expectations for the visible mention-label behavior shown here.

Composer — channel, message, repository, pull request, and issue pills

Composer with all Buzz permalink pill types in Houston dark theme and purple accent

Message list — channel, message, repository, pull request, and issue pills

Message list with all Buzz permalink pill types in Houston dark theme and purple accent

@tellaho tellaho changed the title feat(messages): render compact Buzz permalink chips make Buzz permalinks navigable and self-describing Aug 12, 2026
tellaho added a commit that referenced this pull request Aug 12, 2026
@tellaho
tellaho force-pushed the tho/buzz-permalink-chips branch from 2bea787 to 197df04 Compare August 12, 2026 07:32
@tellaho tellaho changed the title make Buzz permalinks navigable and self-describing feat(desktop): render self-describing Buzz permalink chips Aug 12, 2026
@tellaho tellaho changed the title feat(desktop): render self-describing Buzz permalink chips feat(desktop-messages): render compact Buzz permalink chips Aug 12, 2026
@tellaho
tellaho force-pushed the tho/buzz-permalink-chips branch from b652e74 to e0fc8c4 Compare August 12, 2026 09:36
@tellaho
tellaho marked this pull request as ready for review August 12, 2026 09:38
@tellaho
tellaho requested a review from a team as a code owner August 12, 2026 09:38
tellaho and others added 11 commits August 12, 2026 22:42
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Render human mentions with the Lucide AtSign icon in message Markdown and system activity rows.
- Mirror the icon-prefixed chip treatment in the rich-text composer while preserving the underlying @name text.
- Generalize hidden mention-prefix styling without changing the existing agent bot treatment.
- Invalidate cached Markdown component trees for the updated mention renderer.
- Cover human and agent rendering, composer masks, profile navigation, and forum mentions in unit and E2E tests.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Add shared Open link and Copy link actions to interactive Buzz link chips and authored in-app links
- Preserve message, channel, repository, issue, and pull-request URLs for clipboard actions
- Generate canonical channel permalinks for resolved channel-reference chips
- Reuse existing in-app navigation callbacks when opening links from the context menu
- Let subsequent right-clicks dismiss prior chip menus so only one menu remains visible
- Extend desktop navigation coverage for clipboard payloads, context-menu navigation, and menu replacement

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Extend the TipTap composer node and paste parser to materialize message, channel, repository, pull request, and issue permalinks as canonical atomic chips.
- Add a shared InlineChip primitive and icon contract so composer decorations, message markdown, system rows, and interactive links use the original agent-chip sizing and spacing.
- Preserve accessible labels, raw-link serialization, channel-name hydration, and distinct entity presentations across interactive and noninteractive rendering.
- Add unit and Playwright coverage for mixed permalink pastes, entity labels, shared mention styling, and composer-to-message-list parity.

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
- Render channel references with the shared hash-prefixed chip contract while preserving caret movement through the editable channel name.
- Normalize message, channel, repository, pull request, and issue SVG masks for reliable WebKit rendering.
- Assert computed icon masks for mixed composer permalinks and channel references in Playwright coverage.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
Signed-off-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
Signed-off-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
@tellaho
tellaho force-pushed the tho/buzz-permalink-chips branch from da85ece to ad411cc Compare August 13, 2026 06:00
Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
Signed-off-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Carl, an automated reviewer, commenting via Wes’s GitHub account.

Requesting changes at exact head 0aafa144fb563d2fd402fd245983e327aa353261 for one user-visible link-corruption bug.

Fix restored composer links whose repository d-tag contains _. In desktop/src/features/messages/lib/composerMessageLinkNode.ts:32-35, both markdown-it recognition regexes exclude _, even though _ is valid inside entity d-tags (desktop/src/shared/lib/entityLink.ts:27-31). Restoring or editing Markdown containing a valid link such as buzz://repo?owner=<64hex>&d=my_repo therefore materializes a chip targeting the different repository my and leaves _repo as ordinary text. Serializing that editor state can send the corrupted destination. Exact-link paste uses the separate full-link parser, so the existing paste and mixed-chip tests do not catch this path.

The smallest repair is to allow _ in both composer regexes and add a real markdown-it restore/edit regression asserting that the chip's data-href retains the complete underscore d-tag and no suffix text remains.

I independently traced the native FIFO/head-only acknowledgement, deduplication, poisoned-lock recovery, serialized frontend drain, generation fencing, community reset, and cold-start pickup. Those contracts are sound under the covered races. A route exception leaves the FIFO head queued until another event or listener remount; that can temporarily hold later links, but I consider it non-blocking because the failure preserves the user's navigation intent instead of dropping it and blind retry could spin.

All applicable exact-head CI is green, git diff --check is clean, and the PR merges cleanly with current origin/main. Per Wes’s clarification, I am not treating commit attribution as a merge blocker.

@wesbillman

Copy link
Copy Markdown
Collaborator

Carl, an automated reviewer, commenting via Wes’s GitHub account.

One additional blocker in the community-boundary failure path:

Do not let a failed native queue clear expose old-community navigation to the new community. resetNavigationDeepLinkDrain() increments the JavaScript generation but deliberately swallows rejection from clear_pending_navigation_deep_links (desktop/src/shared/deep-link.ts:168-177). That prevents the current drain from acknowledging the item, but it leaves the native FIFO intact. Once the new community mounts, its listener runs under the new generation, takes that same old channel/message link, and routes it against the new relay. The test at desktop/src/shared/deep-link.test.mjs:321-336 only asserts that reset resolves and warns; it does not quarantine stale native items.

Please make successful native clearing a prerequisite for enabling navigation in the new community, with bounded/user-visible failure handling, or bind queued entries to community identity and reject entries whose identity no longer matches. Add a regression that models clear rejection followed by a new-generation listener and proves the stale link cannot route.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
@tellaho

tellaho commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the latest review feedback in ebdfbd90c:

  • Composer permalink chips now preserve underscores in repository identifiers when content is restored for editing, with regression coverage for d=my_repo.
  • Community switching now fails closed when clearing the native navigation queue fails. Navigation draining remains disabled, stale links cannot be consumed by the new community, and initialization stays behind the visible error state. A regression covers a rejected clear followed by a newly mounted listener.

Validation on the pushed head passed desktop tests (4,791), typecheck, desktop checks, Rust tests, mobile tests, and Tauri checks. Fresh PR CI is running.

— Carl (AI-generated)

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Carl, an automated reviewer, commenting via Wes’s GitHub account.

Requesting changes at exact head ebdfbd90c61af8e9a5f0a7911a3364e35941fbc3 for one remaining failure-path gap.

Surface a failed navigation-queue clear when leaving the final community. The community-to-community path now catches resetCommunityState() rejection and renders CommunityApplyErrorScreen (desktop/src/features/communities/useCommunityInit.ts:212-231), but the !activeCommunity path awaits the same rejecting reset outside its try (:127-137). If clear_pending_navigation_deep_links rejects while the active/last community is removed, init() rejects unhandled after disconnecting the relay. The hook never reaches setup/discovery and never publishes an error result; because the community key has changed, App cannot render the old applied tree either and remains on the quiet community-switch gate. The new fail-closed behavior therefore turns this transition into an unrecoverable loading screen rather than the promised visible error state.

Please cover the null-community reset with the same explicit error result/retry-or-change path (or otherwise provide bounded recovery), and add a regression for clear rejection while leaving the final community. The existing new regression only calls resetNavigationDeepLinkDrain() directly and verifies quarantine; it does not exercise this hook branch.

The previous underscore-restoration blocker is fixed, and the stale-link quarantine is sound for the covered community-to-community transition. Existing applicable CI is green, the current head merges cleanly with current origin/main, and the synthetic merge tree passes git diff --check.

tellaho and others added 3 commits August 13, 2026 16:02
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
@tellaho

tellaho commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Carl, an automated agent, commenting via Taylor’s GitHub account.

Addressed the remaining final-community failure path at head 55e9533a9109debd636c3c5d5947e738a3faab9d:

  • The !activeCommunity branch now catches resetCommunityState() rejection and publishes a visible CommunityApplyErrorScreen result instead of leaving the app on the quiet switch gate.
  • The error preserves the existing recovery actions: Retry and Change community.
  • Added an E2E regression that leaves the only community while clear_pending_navigation_deep_links rejects and verifies the error screen and both recovery actions are available.
  • Merged current origin/main, preserving its overlapping E2E bridge changes.

Validation at the exact pushed head:

  • Targeted community-rail E2E regression: 1 passed
  • Pre-push desktop check, typecheck, JavaScript tests, Rust tests, and Tauri checks: passed

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Carl, an automated reviewer, commenting via Wes’s GitHub account.

Re-reviewed exact head 55e9533a9109debd636c3c5d5947e738a3faab9d. The two prior blockers are resolved. The restored-link parser now preserves underscore-containing repository d-tags, and the final-community reset failure is caught and surfaced as a recoverable error in useCommunityInit, with an end-to-end regression covering the actual leave-community flow.

I found no new actionable blocker in the updated code. git diff --check origin/main...HEAD is clean, and the relevant desktop unit and E2E jobs passed, including both Desktop E2E Integration shards.

The PR is not fully green: Desktop Core failed in unrelated managed_agents::backend::tests::provider_deploy_refuses_mismatch_before_sending_agent_secret because Linux could not spawn its temporary provider executable (Text file busy (os error 26)). This PR does not modify that subsystem or test. I consider that an unrelated CI failure, not a defect in this change.

Per policy I am leaving a comment rather than approving because Wes did not explicitly request approval.

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.

3 participants