Files
eranos/.agents/skills/capacitor-compat/SKILL.md
T
Alex Gleason bd68a32708 Split AGENTS.md into skills; compress to 358 lines
Extract eleven topic areas into loadable skills so AGENTS.md can serve
as a scannable overview instead of a specification dump. The file
shrinks from 1480 to 358 lines (~76%) while keeping every concrete
rule, critical code pattern, and pointer that an agent needs on first
read.

New Ditto-specific skills:
- nostr-kinds: NIP-vs-custom-kind decision framework, kind ranges,
  tag design, content-vs-tags, NIP.md update rule, and Ditto's
  seven-location UI registration checklist for new kinds (NoteCard,
  PostDetailPage, extraKinds.ts, KIND_LABELS/KIND_ICONS in
  CommentContext, WELL_KNOWN_KIND_LABELS in ExternalContentHeader,
  EmbeddedNote/EmbeddedNaddr, ReplyComposeModal).
- nostr-publishing: useNostrPublish, the read-modify-write pattern
  via fetchFreshEvent + prev for replaceable/addressable events,
  published_at contract, and d-tag collision prevention.
- nostr-queries: the standard useNostr + useQuery pattern,
  combining kinds into one filter to avoid rate limits, and the
  NIP-52 validator walkthrough.
- theming: @fontsource install flow, the Ditto runtime font-loader
  path (sanitizeUrl + sanitizeCssString), color scheme variables,
  useTheme toggle, and the isolate + negative-z-index gotcha.
- ci-cd-publishing: Zapstore NIP-46 bunker auth (zsp +
  nip46-auth.mjs), nsite deploys (nsyte nbunksec + configured
  relays/servers), and Google Play AAB uploads via fastlane supply
  (service-account JSON base64 encoding and rotation).
- capacitor-compat: WKWebView/WebView limitations, the
  downloadTextFile / openUrl helpers in src/lib/downloadFile.ts,
  platform detection, and the full plugin list.
- git-workflow: pre-commit validation order and the Regression-of:
  trailer convention used by the release skill's changelog
  generator.

Ported from mkstack, lightly adapted where needed:
- nip19-routing: root-level /:nip19 routing and filter construction
  patterns (adapted to reference Ditto's existing NIP19Page).
- nostr-relay-pools: nostr.relay() and nostr.group() for targeted
  queries.
- nostr-encryption: NIP-44 / NIP-04 via the user's signer.
- file-uploads: useUploadFile + Blossom + NIP-94 imeta tag
  construction.

AGENTS.md itself now follows mkstack's density — concrete rules inline,
one code example per section, pointer to the matching skill for details.
The enumerations that previously bloated it (every shadcn primitive,
every hook, every Capacitor plugin, the full NostrMetadata type dump,
the NIP-19 prefix reference table, etc.) are either removed in favor
of "ls the directory" or moved into their skill.
2026-04-26 23:13:30 -05:00

3.4 KiB

name, description
name description
capacitor-compat Browser-API gotchas inside Capacitor's WKWebView (iOS) and Android WebView — which common web APIs silently fail, the downloadTextFile/openUrl helpers that bridge web and native, platform detection, and the installed Capacitor plugins. Load when writing code that interacts with file downloads, external URLs, or platform-specific behavior.

Capacitor Compatibility

Ditto runs inside Capacitor's WKWebView on iOS and WebView on Android. Several common web APIs do not work in this environment. Always account for native platforms when writing code that interacts with browser-specific features.

What Doesn't Work in WKWebView (iOS)

  • <a download> file downloads — programmatically creating an anchor with a.download and clicking it silently fails. WKWebView ignores the download attribute entirely.
  • <a target="_blank"> new tabs — programmatic clicks on anchors with target="_blank" are blocked. There are no tabs in a native app.
  • window.open() — may be blocked or behave unexpectedly without user-gesture context.

For a deeper list of Apple Lockdown Mode restrictions that also affect WKWebView, load the lockdown-mode skill.

File Downloads and URL Opening

src/lib/downloadFile.ts provides two utilities that handle the web/native split automatically. Always use these instead of manually constructing anchors.

downloadTextFile(filename, content)

Saves a text file to the user's device. On web it uses the <a download> pattern. On native it writes to the Capacitor cache directory via @capacitor/filesystem and presents the native share sheet via @capacitor/share.

import { downloadTextFile } from '@/lib/downloadFile';

await downloadTextFile('backup.txt', fileContents);

openUrl(url)

Opens a URL in a new browser tab on web, or presents the native share sheet on Capacitor.

import { openUrl } from '@/lib/downloadFile';

await openUrl('https://example.com/image.jpg');

CRITICAL: Never use document.createElement('a') with .click() for downloads or opening URLs. The utilities above work correctly on all platforms; manual anchors silently fail on iOS.

Detecting Native Platforms

Use Capacitor.isNativePlatform() from @capacitor/core when you need platform-specific behavior:

import { Capacitor } from '@capacitor/core';

if (Capacitor.isNativePlatform()) {
  // iOS or Android
} else {
  // Web browser
}

Reserve platform forks for cases where behavior genuinely differs (share sheets, secure storage, haptics). Most UI code should stay platform-agnostic.

Installed Capacitor Plugins

  • @capacitor/app — app lifecycle events (deep links, back button)
  • @capacitor/core — core runtime and platform detection
  • @capacitor/filesystem — read/write files on the native filesystem
  • @capacitor/haptics — native haptics
  • @capacitor/keyboard — keyboard control (hide accessory bar, etc.)
  • @capacitor/local-notifications — schedule local push notifications
  • @capacitor/share — native share sheet
  • @capacitor/status-bar — control the native status-bar style
  • @capgo/capacitor-autofill-save-password — iOS keychain autofill for nsec
  • capacitor-secure-storage-plugin — OS-level secure storage (iOS Keychain / Android KeyStore)

After adding or removing plugins, run npm run cap:sync to update the native projects.