Skip to content

feat(preview): open Note links in platform-appropriate tabs - #191

Merged
cxxxxxn (cxxxxxn) merged 2 commits into
microsoft:mainfrom
cxxxxxn:fix/note-preview-link-cursor
Sep 16, 2026
Merged

cxxxxxn (cxxxxxn) merged 2 commits into
microsoft:mainfrom
cxxxxxn:fix/note-preview-link-cursor

Conversation

@cxxxxxn

Copy link
Copy Markdown
Contributor

Summary

  • Show a pointer cursor and follow plain clicks in expanded Note previews while preserving text drag selection; other editable Milkdown surfaces retain their existing behavior.
  • In the web app, open links in a native browser tab. In Electron, open a URL tab in the existing Preview Workspace, preserving the source Note and activating an existing matching URL across groups.
  • Add a compact address toolbar and external-open icon action. Reuse the existing tab lifecycle and shared HTTP(S) validation; do not create Canvas nodes or Chat threads for URLs.
  • Scope pointer gesture tracking to opted-in editors and reduce repeated URL parsing. Retain only URL type parsing in storage restoration, without additional speculative orphan/duplicate repair.

Security and scope

  • URL frames use sandbox="allow-scripts allow-forms" and referrerPolicy="no-referrer", without same-origin, popup, top-navigation, or host bridge privileges.
  • External opening remains available regardless of frame loading. Websites may refuse framing or require unsupported capabilities. No reader extraction, server fetching, or automatic embed-failure detection is added.
  • URL tabs use existing local layout persistence and are unmounted when inactive rather than retaining hidden iframe activity.

Validation

  • Root pnpm typecheck, pnpm format, and pnpm lint:fix pass (lint: 0 errors, 202 warnings).
  • Full web suite: 1328 tests passed across 172 files.
  • Coverage includes platform routing, safe/unsafe URLs, gesture opt-in and selection behavior, URL identity, tab interactions, restoration, and external opening.
  • Browser inspection of the toolbar and Chromium gesture checks were performed during implementation. The final Electron application was not exercised end-to-end; Electron routing is covered by tests with environment detection mocked.
  • Happy-dom emits non-failing iframe/network teardown diagnostics.

Show pointer cursors and follow plain clicks from expanded Notes. Open browser tabs on the web and canonical URL workspace tabs on desktop, preserving text selection and source Notes.

Add a compact sandboxed webpage preview with external opening, reuse shared URL validation and existing tab lifecycle, and cover platform routing, gestures, persistence, and tab interactions.

Copilot AI 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.

🟡 Changes recommended

Unresolved critical security and moderate link-gesture issues remain.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds platform-aware Note-link handling, opening links externally on the web and in Preview Workspace URL tabs on Electron.

Changes:

  • Adds canonical HTTP(S) URL targets, persistence, deduplication, and lifecycle handling.
  • Adds sandboxed URL previews with toolbar and external-open controls.
  • Adds scoped gesture handling, documentation, and tests.
File summaries
File Reviewed changes
docs/architecture/preview-workspace.md Documents URL tabs and rendering lifecycle.
docs/architecture/note-node.md Documents platform-specific link activation.
apps/web/src/utils/safeLink.ts Centralizes HTTP(S) URL validation.
apps/web/src/utils/safeLink.test.ts Tests URL validation and normalization.
apps/web/src/store/previewWorkspace/scrollMemory.ts Adds URL target identity keys.
apps/web/src/store/previewWorkspace/persistence.ts Restores canonical URL targets.
apps/web/src/store/previewWorkspace/persistence.test.ts Tests URL persistence and repair.
apps/web/src/store/previewWorkspace/model.ts Adds URL identity and lifecycle support.
apps/web/src/store/previewWorkspace/model.test.ts Tests URL workspace behavior.
apps/web/src/store/previewWorkspace/actions.ts Adds desktop URL-opening support.
apps/web/src/components/Panels/PreviewWorkspace/UrlPreview.tsx Renders sandboxed URL previews.

critical (1 vote): The HTML sandbox does not by itself remove Electron's preload bridge from a child frame. This BrowserWindow attaches apps/desktop/src/preload.ts, which exposes IPC-backed electronBridge without a main-frame guard; a remote page in this iframe can therefore reach host APIs despite the restricted sandbox, contradicting the stated no-host-bridge security boundary. Guard bridge exposure for subframes or render URL tabs in a webContents without the Huabu preload before shipping.
apps/web/src/components/Panels/PreviewWorkspace/PreviewWorkspace.tsx Excludes URL frames from scroll restoration.
apps/web/src/components/Panels/PreviewWorkspace/PreviewWorkspace.test.tsx Tests URL tab interactions.
apps/web/src/components/Panels/PreviewWorkspace/PreviewTab.tsx Adds URL tab presentation.
apps/web/src/components/Panels/PreviewWorkspace/PreviewRenderer.tsx Routes URL targets to UrlPreview.
apps/web/src/components/Panels/PreviewWorkspace/PreviewGroup.tsx Prevents inactive URL retention.
apps/web/src/components/Nodes/note/NotePreview.tsx Routes Note links by platform.
apps/web/src/components/Nodes/note/NotePreview.links.test.tsx Tests link routing and cursor behavior.
apps/web/src/components/Milkdown/MilkdownEditor.tsx Exposes optional link callbacks.
apps/web/src/components/Milkdown/milkdown-overrides.css Adds scoped link cursor styling.
apps/web/src/components/Milkdown/createMilkdown.ts Adds opt-in gesture-aware link handling.

moderate (2 votes): The first click in a real double-click sequence has detail === 1, so this branch invokes onLinkClick before the later detail === 2 click can be suppressed. Double-clicking a link to select text therefore opens the URL (and in Electron creates a URL tab) instead of preserving selection; prevent the default synchronously but defer the callback until the single-vs-double-click window has elapsed, cancelling it when the second click arrives.
apps/web/src/components/Milkdown/__tests__/blockCommands.test.ts Tests link gestures and selection behavior.
Review details
  • Files reviewed: 22/22 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

src={href}
title={href}
className="min-h-0 w-full flex-1 border-0"
sandbox="allow-scripts allow-forms"
Comment on lines +1642 to +1646
if (onLinkClick && isPlainClick) {
mouseEvent.preventDefault();
// Only the current gesture matters; an older selection must not block a click.
if (selecting) return false;
onLinkClick(href);
Exercise complete double/triple-click sequences and assert synchronous first activation without subsequent duplicate opening. Document navigation-first behavior instead of promising selection-only double clicks.
@cxxxxxn

Copy link
Copy Markdown
Contributor Author

Review follow-up

The multi-click tests now exercise the complete detail=1 then detail=2/3 sequence: the first click invokes navigation synchronously, and subsequent clicks do not invoke it again. The documentation explicitly states that double-clicking a link is not a selection-only gesture. This preserves the agreed navigation-first interaction without adding a timer; drag suppression and fresh clicks after prior selections remain covered. No production interaction code changed in this follow-up.

The preload finding was independently checked against the configured Electron 43.1.1 runtime and its version-pinned source. The BrowserWindow omits nodeIntegrationInSubFrames (default false); ordinary subframes do not execute the preload under that configuration. UrlPreview also omits allow-same-origin, preventing parent-bridge access. An isolated Electron probe using an inert diagnostic bridge covered same-origin, cross-origin, and redirects to the app origin; the exact URL sandbox had no own bridge and parent access raised SecurityError. Enabling nodeIntegrationInSubFrames in a positive control exposed the diagnostic bridge, confirming that HTML sandboxing alone is not sufficient. This does not certify unrelated desktop security surfaces or exercise the entire packaged application.

References: https://github.com/electron/electron/blob/v43.1.1/shell/renderer/renderer_client_base.cc#L207-L219 and https://www.electronjs.org/docs/latest/api/structures/web-preferences . No desktop production change is made for that finding.

Validation after this follow-up: root typecheck, format, and lint:fix pass (0 lint errors, 202 warnings); 94 focused tests pass. The prior full web run passed 1328 tests.

@cxxxxxn
cxxxxxn (cxxxxxn) merged commit 4079385 into microsoft:main Sep 16, 2026
2 checks passed
@cxxxxxn
cxxxxxn (cxxxxxn) deleted the fix/note-preview-link-cursor branch September 16, 2026 02:48
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.

2 participants