[READ-ONLY] Mirror of https://github.com/jphastings/webtiles.byjp.me. A proof-of-concept for Atproto powered Webtile views.
0

Configure Feed

Select the types of activity you want to include in your feed.

HTML 66.4%
TypeScript 23.4%
JavaScript 6.7%
CSS 3.4%
Other 0.1%
13 1 0

Clone this repository

https://git.vm.fail/byjp.me/webtiles.byjp.me https://git.vm.fail/did:plc:deeoaa4cst7gf7gc6xeqglwt
ssh://git@knot1.tangled.sh:2222/byjp.me/webtiles.byjp.me ssh://git@knot1.tangled.sh:2222/did:plc:deeoaa4cst7gf7gc6xeqglwt

For self-hosted knots, clone URLs may differ based on your setup.


README.md

Webtiles as view#

An experiment into sharing WebTiles on the AT Protocol wrapped in context which declares the tile as being able to render a given NSID/collection of AT Protocol records.

eg. "This Webtile can render a Bluesky post" or "This Webtile can render a Video game playthrough".

Sketch#

CAUTION

This is highly speculative, something to experiment with so I can explore it and discuss its strengths and weaknesses.

A record published to at://byjp.me/es.webtil.eView/some-key declares that it's intended to render records in the games.gamesgamesgamesgames.experimental.actor.play collection like this one).

It might look like this:

{
  // This webtile is intended to render this Atproto collection
  "collection": "games.gamesgamesgamesgames.experimental.actor.play",
  // This webtile is intended to render records that match *all* of these JSONPath queries
  "where": [
    "$[?@.game == 'at://did:web:gamesgamesgamesgames.games/games.gamesgamesgamesgames.game/3mghhtroxpn2d']",
  ],
  // Here is the *embedded* webtile
  "tile": {
    "$type": "ing.dasl.masl",
    // The rest of the Webtile
  },
}

A site wanting to display a g.gggg.actor.play record would use the <webtile-view> component to:

  1. Retrieve the record to be displayed
  2. Retrieve the Webtile View(s) that can display it (open question: discovery?)
  3. Filter out Webtile Views that have a where JSONPath clause marking them as incompatible with the provided record
  4. Render the Webtile
  5. Send a postMessage({ kind: "render", record, uri }) to the Webtile, and answer the { kind: "xrpc", … } data requests it posts back
  6. (Optional — for self-sizing tiles such as full-page views) let the Webtile drive its own height with the iframe-resizer v4 wire protocol

Upon which the Webtile will update using the given data. It may request additional records or identity lookups by posting XRPC reads for the host to broker — e.g. postMessage({"kind": "xrpc", "nsid": "com.atproto.repo.getRecord", …}) and postMessage({"kind": "xrpc", "nsid": "com.atproto.repo.describeRepo", …}). The host routes each allowlisted call to the repo's PDS.

A Webtile opts into self-sizing by reporting its height — either by dropping in @iframe-resizer/child or by posting the [iFrameSizer]…:resize message itself. The <webtile-view> element handles the host side automatically, applying the reported height — growing the iframe so the host page scrolls — only when it carries the autoheight attribute.

Potential challenges#

  • How do we support discovery of webtile views for a given lexicon?

Architecture#

Three layers. The middle one — <webtile-view> — is the trust and network boundary: it is the only code that talks to atproto, and the Webtile is sandboxed with no network of its own. The host page just configures and styles the element; it never sees atproto. (In the DOM the Webtile is an iframe inside <webtile-view>, which sits in the host page.)

sequenceDiagram
    autonumber
    participant H as Host page
    participant V as <webtile-view>
    participant T as Webtile iFrame
    participant N as Atproto network
    H->>V: Record URI + prioritised webtile sources
    V->>N: Resolve identity, fetch record + 'webtile view'
    N->>V: identity, records, 'webtile view'
    V->>T: render(record, uri)

    opt Retrieve blob (eg. image)
      T->>N: PDS /xrpc/com.atproto.sync.getBlob
      N->>T: Blob
    end

    opt Retrieving a Record
      T->>V: postMessage({"kind": "xrpc", "nsid": "com.atproto.repo.getRecord", …})
      V->>T: getRecord response
    end

    opt Resolving an identity
      T->>V: postMessage({"kind": "xrpc", "nsid": "com.atproto.repo.describeRepo", …})
      V->>T: describeRepo response (handle, didDoc, …)
    end

    opt Height negotiation (iframe-resizer v4)
      T->>V: [iFrameResizerChild]Ready
      V->>T: [iFrameSizer]<config>
      T->>V: [iFrameSizer]<id>:<height>:<width>:<type>
      Note over V: grows iframe when autoheight is set
    end

<webtile-view> picks the first tile whose NSID and where clauses accept the record, sandboxes it, then brokers each allowlisted XRPC read the tile asks for — so the tile never touches atproto itself. Blobs are the one exception: the tile loads them straight from the PDS.