Skip to content

Repository files navigation

HermesShare

Native iMessage cards, described by JSON, rendered by SwiftUI.

HermesShare is an open-source iMessage App Extension that turns structured JSON into rich, interactive cards inside Messages — package tracking, flight boards, trip plans, polls, hotel catalogs, dashboards, and more. An AI agent (or any backend) sends a HermesLayout document per message; a fixed native renderer draws it on device.

Screenshots

Real received cards opened in Messages on an iPhone, captured from a September 16, 2026 screen recording. These are not simulator renders or mockups. Crops exclude notification banners, chat headers and unrelated messages.

Learning notebook Weekly schedule Native practice feedback
Learning card on iPhone with native tabs, rich text and a syntax-highlighted HTML example Weekly schedule card with a proportional timetable and collapsible day groups Native quiz with a selected answer and an explanatory correct-answer callout

In the conversation

A text-free graphic stays compact in the transcript. Tapping it opens the full native card.

Schedule thumbnail Learning thumbnail
Received schedule message with a text-free timetable thumbnail and a separate caption Received learning message with a text-free source-and-page illustration and a separate caption

View all six full-resolution screenshots and capture details. The schedule is historical demo content from the recording, not a live calendar.

Build 4: learning cards and fast authoring

The native vocabulary now includes richText, codeBlock, webPreview, tabs, tree, callout, resources, quiz, and taskList. Markdown supports real headings, lists, blockquotes and fenced code. HTML/CSS previews are sealed: no JavaScript, network loads or navigation.

Reusable notebook/taskboard composers, a proportional schedule composer, text-free thumbnails, private Linq uploads and cached native previews avoid rebuilding an iOS app for every content edit. Validation and fresh-content rendering still run; message sends and delivery checks are never cached.

python3 -m pip install -r requirements-cards.txt
python3 scripts/fast_card.py --template notebook --input examples/notebook-input.json \
  --repo "$PWD" --out-dir .build/my-card
# With a booted simulator, add --preview --all-tabs --simulator YOUR_UDID.

Why not just run SwiftUI from the server?

Apple does not let apps download and execute arbitrary Swift or SwiftUI at runtime. Code that runs on iOS must be compiled, signed, and shipped inside an App Store–reviewed binary — you cannot push new UI logic over the air the way a web app loads JavaScript. That is why you cannot simply "send SwiftUI source" in an iMessage and have it render.

HermesShare works around that constraint the same way Scriptable and Widgy do: the app ships a fixed, Apple-signed renderer, and incoming messages carry declarative JSON that selects from a known vocabulary of native views. The JSON describes what to show (a flight board, a checklist, a map preview) — it never executes code. The extension interprets the tree and builds real SwiftUI. The transcript bubble stays a native thumbnail; no downloaded native code or eval.

What you CAN'T do          What HermesShare does
─────────────────          ───────────────────
Download SwiftUI code   →    Send HermesLayout JSON
Run unsigned UI logic →    Map JSON → signed renderer → native SwiftUI

Why HermesShare

Problem HermesShare
Long markdown walls in iMessage Structured cards with native UI
Web-view mini-apps feel disconnected Real MSMessageTemplateLayout bubbles
Apple blocks arbitrary runtime UI code JSON + signed renderer (Scriptable model)
Agent replies are plain text Tap-to-reply actions insert real messages back into the thread

How it works

Agent / backend                iMessage                    Device
─────────────                  ────────                    ──────
HermesLayout JSON  ──send──►  MSMessage bubble  ──tap──►  HermesLayoutRenderer
(base64url in URL)             (thumbnail + caption)        (native SwiftUI tree)
  1. SchemaHermesLayout is a JSON document: metadata + recursive HermesNode tree.
  2. Transport — use the canonical Linq sender or an explicitly selected Photon transport. Linq uses an encoded JSON URL; Photon uses base64url in an HTTPS URL. Both create a thumbnail bubble that expands into the native card.
  3. Renderer — the signed app interprets each JSON node type and builds native SwiftUI (fixed vocabulary — no downloaded code, no runtime compilation).
  4. Actionshermesshare://action?... buttons insert reply messages into the thread.

Full JSON reference: docs/LAYOUT.md
Sending guide: docs/SENDING.md

Repository layout

HermesShare/
├── Shared/                    Swift package — schema, Codable, renderer, samples
├── HermesShare/               Host app (debug harness + live agent dashboard)
├── HermesShareExtension/      iMessage App Extension (MessagesViewController)
├── HermesAgentWidget/         Live Activity widget (lock screen / Dynamic Island)
├── harness/                   Agent harness server + hermes-harness CLI (see harness/README.md)
├── docs/
│   ├── LAYOUT.md              HermesLayout authoring guide
│   ├── SENDING.md             Linq / Photon transport instructions
│   ├── fast-native-preview.md Cached native authoring workflow
│   ├── learning-components.md Learning and document node schema
│   └── screenshots/device-20260916/  Recorded iPhone screenshots (this README)
├── scripts/                   Thumbnail helper, batch send, screenshot tools
└── project.yml                XcodeGen project definition

Live agent harness

The host app doubles as a live monitor for a running Hermes agent: pair the phone with the harness server (harness/), and the Agent tab shows the agent's todo list, current task, current action, and a live preview screenshot — mirrored into a Live Activity on the lock screen and Dynamic Island. See harness/README.md.

Requirements

  • macOS with Xcode 26+
  • iOS 26+ device or Simulator
  • XcodeGen: brew install xcodegen
  • Apple Development signing identity and App Group-enabled profiles for physical device builds
  • Simulator tests can use CODE_SIGNING_ALLOWED=NO

Quick start

1. Clone and generate the Xcode project

git clone https://github.com/time-attack/HermesShare.git
cd HermesShare
xcodegen generate
open HermesShare.xcodeproj

2. Configure code signing

In project.yml, set DEVELOPMENT_TEAM under settings.base to your Apple team ID, then regenerate:

xcodegen generate

Configure your team for HermesShare, HermesShareExtension, and HermesAgentWidget. For device builds, register and enable the App Group group.com.hermesshare.app on the corresponding app identifiers and provisioning profiles. The source entitlements use this group. When changing the group identifier, also update the shared storage suite names. Never commit private provisioning profiles or signing keys.

3. Build and run on Simulator

# Pick a simulator UDID
xcrun simctl list devices available | grep iPhone

xcodebuild -project HermesShare.xcodeproj -scheme HermesShare \
  -destination 'platform=iOS Simulator,id=YOUR_UDID' \
  -derivedDataPath build/DD build

Install and launch the host app, or run from Xcode (⌘R).

4. Try the debug harness (fastest iteration loop)

Open the HermesShare app in Simulator. Use the segmented control to flip between sample layouts, or tap {} to paste/edit live JSON and watch it render with inline validation errors.

5. Try the iMessage extension

  1. Run/install HermesShare on Simulator (embeds the extension).
  2. Open Messages → any conversation → tap + → App Store icon → HermesShare.
  3. In Debug Simulator builds, a compose gallery inserts sample cards into the thread.
  4. Tap a bubble to expand; action buttons insert reply messages.

6. Run tests

xcodebuild -project HermesShare.xcodeproj -scheme HermesShare \
  -destination 'platform=iOS Simulator,id=YOUR_UDID' \
  -derivedDataPath build/DD test

Covers schema round-trip, transport encoding, routing logic, and render smoke tests.

Python tooling regression suite (no real message sends):

HERMESSHARE_REPO="$PWD" python3 -m unittest discover -s scripts -p "test_*.py"

Sending cards from your agent

See docs/SENDING.md. After preparing and inspecting a card:

python3 scripts/send_card.py --chat-id "$LINQ_CHAT_ID" \
  --layout-file .build/my-card/card.json --image-file .build/my-card/thumbnail.jpg

Use an existing thread ID when known. Upload previews privately through the selected provider, not GitHub Pages. Do not switch providers or resend a pending message automatically.

Copy scripts/send_card_photon.mjs into your Photon sidecar directory (or run from a folder with npm install spectrum-ts), set HERMES_TEAM_ID, and send.

Example JSON

{
  "version": 1,
  "title": "Package Out for Delivery",
  "subtitle": "Order #HS-48213",
  "accentColorHex": "#34C759",
  "root": {
    "type": "vstack", "spacing": 12,
    "children": [
      { "type": "statusBadge", "label": "Out for delivery", "colorHex": "#34C759" },
      { "type": "progressBar", "value": 0.78, "colorHex": "#34C759" },
      { "type": "keyValueRow", "key": "Carrier", "value": "UPS Ground" }
    ]
  },
  "actions": [
    { "id": "track", "label": "View full tracking", "systemImage": "location.fill",
      "deepLinkURL": "hermesshare://action?id=track" }
  ]
}

More examples live in Shared/Sources/HermesShared/HermesSampleLayouts.swift and Shared/Tests/HermesSharedTests/Fixtures/.

Contributing

Contributions welcome — especially new HermesNode types, renderer polish, and fixture cards. Open an issue before large schema changes. See CONTRIBUTING.md.

License

MIT — use freely, attribution appreciated.

Acknowledgments

Built for Hermes agent-driven iMessage via Photon. Inspired by Scriptable and Widgy's declarative native UI model.

About

No description, website, or topics provided.

Resources

Contributing

Stars

43 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages