Skip to content

FE-1669: Share Petrinaut snapshots through self-contained URLs - #9680

Open
kube wants to merge 1 commit into
codex/fe-1660-readonly-feedbackfrom
codex/fe-1669-snapshot-sharing
Open

FE-1669: Share Petrinaut snapshots through self-contained URLs#9680
kube wants to merge 1 commit into
codex/fe-1660-readonly-feedbackfrom
codex/fe-1669-snapshot-sharing

Conversation

@kube

@kube kube commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Local document URLs identify files saved in one browser. This adds Share to create a link containing a fixed copy of the complete net, with an option to include the current editor view.

Recipients open the snapshot read-only and can make an editable local copy. Later edits to the sender's document do not change the link.

Links

Blocked by

Changes

  • Encode compact document JSON with Brotli quality 11 in a versioned /share#v1.br.<payload> fragment
  • Preserve the optional editor location through view changes, browser history, and local copying
  • Run compression in a worker with cancellation, a timeout, and limits on link length and decompressed size
  • Offer YAML download for large snapshots and manual copying when clipboard access fails
  • Remove snapshot fragments from Sentry events, breadcrumbs, transactions, and spans
  • Add the sharing guide and register it in the AI documentation catalog, with patch changesets for both published packages

Test coverage

  • Codec tests round-trip every core example and check Unicode, independent Brotli compatibility, malformed and unsupported links, truncated data, and size limits
  • Route tests cover opening with empty storage, read-only handles, view restoration, Back/Forward, replacing a snapshot, and copying to a local UUID
  • Dialog and worker tests cover fixed capture, optional view inclusion, clipboard failure, file fallback, cancellation, startup errors, and timeouts
  • Telemetry tests verify removal of valid, invalid, and future snapshot fragments
  • Builds, unit tests, type checks, lint, formatting, and architecture checks pass for the affected packages
  • Manually verified the development and production builds: shared an example and an edited local document, opened the link on a separate origin, restored the Scenarios view, triggered the read-only drag notice, and made a local copy

How to test

  • Open the website preview
  • Select Simulate > Scenarios, then Share
    • Choose Copy snapshot link
  • Open the link in another browser

    The complete snapshot opens in the Scenarios view without requiring the sender's local storage.

  • Select Edit and try dragging a place

    A read-only notice offers Make a local copy.

  • Choose Make a local copy

    An editable copy opens at a new /local/<UUID> URL.

  • Choose Share and clear Include current view

    The link opens the snapshot at its default view.

@kube kube self-assigned this Sep 11, 2026
@vercel

vercel Bot commented Sep 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
hash Ready Ready Preview Sep 11, 2026 3:05pm UTC
petrinaut Ready Ready Preview Sep 11, 2026 3:05pm UTC
petrinaut-docs Ready Ready Preview Sep 11, 2026 3:05pm UTC
1 Skipped Deployment
Project Deployment Actions Updated
hashdotdesign-tokens Ignored Ignored Preview Sep 11, 2026 3:05pm UTC

Request Review

@github-actions github-actions Bot added area/deps Relates to third-party dependencies (area) area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team area/apps labels Sep 11, 2026
@kube
kube added this pull request to stack #9663 September 11, 2026 14:51
): Uint8Array => {
if (hash.length > maxSnapshotHashLength) throw new SnapshotError("too-large");
if (!hash.startsWith(prefix)) {
throw new SnapshotError(/^v\d+\./u.test(hash) ? "unsupported" : "invalid");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Semgrep identified an issue in your code:
Ensure that the regex used to compare with user supplied input is safe from regular expression denial of service.

To resolve this comment:

🔧 No guidance has been designated for this issue. Fix according to your organization's approved methods.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by regex_dos.

You can view more details about this finding in the Semgrep AppSec Platform.

): Uint8Array => {
if (hash.length > maxSnapshotHashLength) throw new SnapshotError("too-large");
if (!hash.startsWith(prefix)) {
throw new SnapshotError(/^v\d+\./u.test(hash) ? "unsupported" : "invalid");
@cursor

cursor Bot commented Sep 11, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
New client-side codec and routing handle untrusted URL fragments with explicit bounds, but links embed full document payloads and Sentry stripping must stay complete to avoid leaking nets in telemetry.

Overview
Adds self-contained snapshot sharing on the demo site: a top-bar Share control builds /share#v1.br.<payload> links (optional query params for the current view), and a new /share route opens the net read-only without touching local storage, with Make a local copy to fork into /local/<uuid>.

Snapshot data is canonical JSON, Brotli-compressed in a dedicated worker (brotli-wasm) with size/hash limits, cancellation, and a 30s timeout; oversized nets fall back to YAML download in the Share dialog. Sentry hooks strip snapshot fragments from URLs and nested telemetry.

Read-only example and snapshot UIs are consolidated in ReadonlyDocumentPage (examples delegate to it). User-facing sharing.md and AI readPetrinautDoc catalog entries document the flow.

Reviewed by Cursor Bugbot for commit a47a5f7. Bugbot is set up for automated code reviews on this repo. Configure here.

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

Clipboard completion can report a stale URL as copied, and the critical oversized-snapshot download fallback is not exercised.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds self-contained, read-only Petrinaut snapshot sharing with optional editor-view restoration, local copying, and file fallback.

Changes:

  • Adds versioned Brotli snapshot encoding through cancellable workers.
  • Adds sharing UI, /share routing, local-copy behavior, and Sentry redaction.
  • Adds comprehensive documentation, tests, dependency updates, and changesets.
File summaries
File Description
yarn.lock Locks the Brotli dependency.
libs/@hashintel/petrinaut/src/ui/views/Editor/panels/ai-assistant-panel/petrinaut-docs-content.ts Registers sharing documentation.
libs/@hashintel/petrinaut/docs/sharing.md Documents snapshot sharing.
libs/@hashintel/petrinaut/docs/README.md Links the sharing guide.
libs/@hashintel/petrinaut-core/src/ai.ts Exposes sharing documentation to AI.
apps/petrinaut-website/src/sharing/snapshot.ts Serializes and validates snapshots.
apps/petrinaut-website/src/sharing/snapshot.test.ts Tests snapshot codec behavior.
apps/petrinaut-website/src/sharing/snapshot-worker.ts Runs Brotli operations in a worker.
apps/petrinaut-website/src/sharing/snapshot-worker-protocol.ts Defines worker messages.
apps/petrinaut-website/src/sharing/snapshot-codec.ts Implements bounded Brotli encoding.
apps/petrinaut-website/src/sharing/snapshot-client.ts Manages worker lifecycle and URLs.
apps/petrinaut-website/src/sharing/snapshot-client.test.ts Tests cancellation and failures.
apps/petrinaut-website/src/sharing/share-snapshot-button.tsx Adds the sharing dialog.
apps/petrinaut-website/src/sharing/share-snapshot-button.test.tsx Tests sharing interactions.
apps/petrinaut-website/src/sentry/strip-snapshot-links.ts Redacts snapshot payloads.
apps/petrinaut-website/src/sentry/strip-snapshot-links.test.ts Tests telemetry redaction.
apps/petrinaut-website/src/sentry/instrument.ts Applies redaction to Sentry hooks.
apps/petrinaut-website/src/routes/share.tsx Adds the snapshot route.
apps/petrinaut-website/src/routes/-share.test.tsx Tests snapshot navigation and copying.
apps/petrinaut-website/src/main/app/readonly-document-page.tsx Extracts shared read-only document UI.
apps/petrinaut-website/src/main/app/local-storage-demo/local-storage-demo-app.tsx Adds sharing to local documents.
apps/petrinaut-website/src/examples/full-example-page.tsx Reuses the read-only document page.
apps/petrinaut-website/README.md Documents snapshot architecture.
apps/petrinaut-website/package.json Adds brotli-wasm.
.changeset/snapshot-sharing-guide.md Records the Petrinaut documentation change.
.changeset/snapshot-sharing-doc-catalog.md Records the core catalog change.
Review details
  • Files reviewed: 25/26 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +73 to +80
const copy = async () => {
if (url === null) return;
try {
await navigator.clipboard.writeText(url);
setCopyState("copied");
} catch {
setCopyState("failed");
}
Comment on lines +90 to +93
expect(screen.getByRole("button", { name: "Download file" })).toHaveProperty(
"disabled",
false,
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/apps area/deps Relates to third-party dependencies (area) area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team

Development

Successfully merging this pull request may close these issues.

3 participants