Skip to content

feat(perry/system): #917 — shareText/shareUrl system share sheet#972

Merged
proggeramlug merged 1 commit into
mainfrom
feat/917-share-sheet
May 18, 2026
Merged

feat(perry/system): #917 — shareText/shareUrl system share sheet#972
proggeramlug merged 1 commit into
mainfrom
feat/917-share-sheet

Conversation

@proggeramlug
Copy link
Copy Markdown
Contributor

Closes #917 (MVP — see follow-up matrix).

Summary

Two perry/system entry points wrapping the native share picker:

import { shareText, shareUrl } from "perry/system";
shareText("hello world");
shareUrl("https://example.com", "My link");

Use case: "share my invitation code" / "share this listing" flows where the OS share sheet hands the value to Messages / Mail / WhatsApp / etc. Wishare's invite-friends screen had to fall back to clipboardWrite + manual paste; native apps already expose this surface.

Cross-platform coverage

Platform Status
macOS Real implNSSharingServicePicker anchored to key window
iOS Stub + #917 follow-up warning (UIActivityViewController)
tvOS Stub (tvOS doesn't expose a programmatic share sheet)
watchOS Stub (WatchKit doesn't expose a public API)
visionOS Stub + #917 follow-up (window-anchored UIActivityViewController)
Android Stub + #917 follow-up (Intent.ACTION_SEND chooser)
Windows Stub + #917 follow-up (DataTransferManager.ShowShareUI)
Linux/GTK4 Stub + #917 follow-up (XDG share portal)
WASM Stub via dispatch table fallback
HarmonyOS Stub auto-generated by perry-runtime/build.rs

Every stub funnels through perry_runtime::stub_diag::perry_stub_warn, so first call per process per symbol prints a [perry] warning line naming the platform and the #917 tracker.

macOS implementation detail

NSSharingServicePicker initWithItemsshowRelativeToRect:ofView:preferredEdge: anchored to the key window's content-view bounds (NSRectEdgeMinY so the popover renders above the view). shareText wraps the value as NSString; shareUrl wraps as NSURL so the picker offers Safari / Reading List / Bookmarks alongside Messages / Mail / Notes. Malformed URLs fall back to plain-text sharing.

The title argument is currently dropped on macOS (Cocoa's picker derives its label from the item type); kept in the signature for cross-platform symmetry with iOS/Android where title is meaningful.

Plumbing (mirrors #918's takeScreenshot pattern)

  • API manifest, dispatch table, every per-platform UI crate, testkit feature matrix, HarmonyOS auto-stub via build.rs. WASM falls through to ui_method_to_runtime() so no parallel edit there.

Smoke test

Compiled App({body}) + shareText() + shareUrl() against release perry; symbols resolve end-to-end, link succeeds, binary writes cleanly.

What's deferred (#917 follow-ups)

  • iOS / Android / Windows / Linux / visionOS native impls — issue maps each native API.
  • shareItems({text?, url?, title?}) structured variant — dispatch is forward-compatible; a new MethodRow will land it without changing the two convenience entry points.

Notes

No Cargo.toml version bump, no CLAUDE.md touch, no CHANGELOG.md entry — maintainer folds those in at merge time.

Exposes two perry/system entry points wrapping the native share
picker:

  import { shareText, shareUrl } from "perry/system";
  shareText("hello world");
  shareUrl("https://example.com", "My link");

Use case: "share my invitation code" / "share this listing" flows
that the OS share sheet hands to Messages / Mail / WhatsApp / etc.
Wishare's invite-friends screen had to fall back to clipboardWrite +
manual paste; native apps already expose this surface.

## Cross-platform coverage

| Platform   | Status                                                    |
|------------|-----------------------------------------------------------|
| **macOS**  | Real impl: NSSharingServicePicker anchored to key window |
| iOS        | Stub + #917 follow-up warning                             |
| tvOS       | Stub (tvOS doesn't expose a programmatic share sheet)    |
| watchOS    | Stub (WatchKit doesn't expose a public API)               |
| visionOS   | Stub + #917 follow-up (window-anchored UIActivityViewController) |
| Android    | Stub + #917 follow-up (Intent.ACTION_SEND chooser)        |
| Windows    | Stub + #917 follow-up (DataTransferManager.ShowShareUI)   |
| Linux/GTK4 | Stub + #917 follow-up (XDG share portal)                  |
| WASM       | Stub via dispatch table fallback                          |
| HarmonyOS  | Stub auto-generated by perry-runtime/build.rs            |

Every stub funnels through perry_runtime::stub_diag::perry_stub_warn,
so first call per process per symbol prints a [perry] warning naming
the platform and the #917 tracker.

## macOS implementation detail

NSSharingServicePicker initWithItems → showRelativeToRect:ofView:preferredEdge:
anchored to the key window's content view bounds, preferredEdge =
NSRectEdgeMinY so the popover renders above the view.

shareText wraps the value as NSString; shareUrl wraps as NSURL so
the picker offers Safari / Reading List / Bookmarks alongside the
Messages / Mail / Notes options. Malformed URLs fall back to
plain-text sharing.

The  argument is currently dropped on macOS (Cocoa's picker
derives its label from the item type); kept in the signature for
cross-platform symmetry with iOS/Android where title is meaningful.

## Plumbing

- perry-api-manifest/src/entries.rs — shareText + shareUrl rows.
- perry-dispatch/src/lib.rs — two MethodRows with [Str, Str] args +
  ReturnKind::Void. WASM falls through to ui_method_to_runtime() in
  perry-codegen-wasm/src/emit.rs — gets symbol mapping for free.
- Per-platform exports: perry-ui-macos (real), and stubs in
  perry-ui-ios, perry-ui-tvos, perry-ui-watchos, perry-ui-visionos,
  perry-ui-android, perry-ui-gtk4, perry-ui-windows.
- perry-ui-test/src/lib.rs — Feature rows for both symbols across
  every platform.
- HarmonyOS is auto-stubbed by perry-runtime/build.rs from the
  dispatch table.

## Smoke test

Compiled App({body: VStack([Text("share me")])}) + shareText() +
shareUrl() against release perry; symbols resolve end-to-end, link
succeeds, binary writes cleanly. (Interactive picker pops on real
run; CI smoke verifies the link path.)

## What's deferred for #917 follow-ups

- iOS / Android / Windows / Linux / visionOS native impls (the issue
  maps them out).
- shareItems({text?, url?, title?}) structured variant — the
  dispatch is forward-compatible; will add a new MethodRow without
  changing the two convenience entry points.
@proggeramlug proggeramlug force-pushed the feat/917-share-sheet branch from 144dcef to 4176b75 Compare May 18, 2026 10:07
@proggeramlug proggeramlug merged commit aa2fdeb into main May 18, 2026
@proggeramlug proggeramlug deleted the feat/917-share-sheet branch May 18, 2026 10:07
proggeramlug added a commit that referenced this pull request May 18, 2026
…Rs (#1019)

#981 (PERRY_SANDBOX_BUILDRS), #988 (--emit-attest), and #969
(perry.permissions) each landed via admin-bypass with their
SUMMARY.md entries intact but without the actual .md content files
(or, for #969, without any docs entry at all). docs/src/cli/lockdown.md
and docs/src/cli/emit-sandbox.md did make it into main and are
fine; the others left dead links.

Separately, #976 / #972 / #974 added perry/system runtime methods
(getOSVersion, shareText, shareUrl, appGroupSet/Get/Delete) but
never updated the hand-maintained types/perry/system/index.d.ts.
TypeScript users importing those APIs from `perry/system` get a
type error today.

This PR:
- Creates docs/src/cli/sandbox-buildrs.md (#505)
- Creates docs/src/cli/emit-attest.md (#504)
- Creates docs/src/cli/capabilities.md (#501) and adds the SUMMARY.md entry
- Adds the six new perry/system signatures to types/perry/system/index.d.ts

The auto-generated docs/api/perry.d.ts + docs/src/api/reference.md
were regenerated during the original PRs and are already current.

Pure docs-only diff. No code changes.
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.

perry/system: native iOS share sheet (UIActivityViewController)

1 participant