Skip to content

[RFC] Commit embeds #1521

Open
Open

Description

@jubianchi

TL;DR

For users

  • Show a /dev/null commit on your own site, blog, docs or Notion page, as a card that looks like /dev/null: the same graph rail, formatting, code, images, polls and players.
  • From a commit's "…" menu, Embed commit lets you pick what the card shows (the commit, the rest of its thread, replies down to three levels or all), its height and theme, then copy HTML, Markdown or a link.
  • The card stays live: a commit that is deleted, rejected or hidden disappears from every page that embeds it. It is read-only: starring, voting and replying happen on /dev/null.
  • Creating an embed needs an account; reading one doesn't.
  • You decide: turn embeds off in Settings → Profile (organization admins in the organization's settings), and existing cards then read [embedding disabled]. Embeds are on by default.
  • No tracking: /dev/null doesn't count views or record where a card is shown.

For maintainers

  • New public surfaces: GET /embed/{uuid} (the card, framable, sessionless, always the guest view), GET /oembed and a static embed.js; one members-only endpoint, GET /commits/{commit}/embed-link, signs the card's URL for the dialog.
  • Members-only creation without storage: embed URLs carry a relative HMAC signature over the commit and its settings; unsigned or tampered URLs get no card.
  • Abuse: per-IP and per-card rate limits, signature checked before any query, settings normalized to a closed set, at most 40 commits per card, no writes; a 30-second invalidated cache is planned.
  • Data: one boolean, allows_embeds, on users and organizations, on by default, exported and erased with the account.
  • PreventFraming keeps every other page out of frames; the card is its only exception.
  • The card reads without JavaScript: it is rendered on the server like every page, and only the resize script and a few controls need scripts.
  • A partial implementation, draft PR feat: let members embed commits on other sites #1522, produced the screenshots in this RFC. It leaves out the cache, the 429 fallback, a constant-query tree and shared rail components.
  • To weigh: the name (embed, or the git-flavored cherry-pick), and the rollout in six pull requests.

Summary

Let members show a public commit on another website as a card that looks and reads like /dev/null: the commit, optionally the rest of its author's thread, and its replies, down to several levels. The card is an iframe served by /dev/null, so it always reflects the commit's current state, and an oEmbed endpoint lets blogging platforms build it from the dialog's signed link.

Motivation

Developers may quote /dev/null threads in blog posts, newsletters and docs with screenshots. A screenshot loses the links, the code, the polls and the conversation, and it survives the commit's deletion. An embed keeps the content live, keeps the author in control, and brings readers back to the thread.

A /dev/null thread embedded in a blog post: the commit, its code block, and replies hanging off the graph rail

Goals and non-goals

Goals:

  • Embed a single commit, a thread, and replies to replies, chosen by whoever creates the embed.
  • Render everything a commit can hold, the same way /dev/null does: text formatting, code blocks, emoji, mentions, hashtags, GitHub and commit links, quote blocks, color swatches, images, link cards, embedded players, polls.
  • Draw the same graph rail as /dev/null.
  • Respect deletion, moderation and the author's choice on every page that embeds the commit.

Non-goals:

  • Interacting from the card: starring, voting, replying and translating stay on /dev/null.
  • Measuring embeds: no view counts, no referrers, no tracking.
  • Embedding anything other than commits (profiles, logs, Remotes).

Product rules

Who can embed what

  • Only signed-in members can create an embed, for any approved, public commit. Readers of the page that embeds it don't need an account: the card is public once created.
  • Members-only creation is enforced, not just hidden: every embed URL is signed (see Signed URLs), so a guest can't build one by hand.
  • Authors can turn embeds off in Settings → Profile. Embeds are allowed by default.
  • For a commit posted as an organization, the organization's setting applies. Embeds are allowed by default, and only the organization's admins can change it.
  • Turning embeds off also affects embeds that already exist, since the card is served live.
User Settings → Profile: the Embeds section, a title, two sentences and a switch, like Pastéis mode
Organization An organization's settings, Profile tab: its color, then the same Embeds section, for admins

Entry point

  • Embed commit sits in the commit's "…" menu, right after Send in Remotes.
  • It is hidden from guests, and hidden when the commit can't be embedded: not approved, deleted, or its author (or organization) turned embeds off. This includes your own commits when you turned embeds off.
A commit's open menu: Copy link, Send in Remotes, then Embed commit

The modal

The /dev/null dialog (floating close button, title, description, outline Cancel and a primary action), widened to fit a preview. Its settings follow the conventions of the Settings pages: sections separated by hairlines, a title and a description, a switch for a yes/no setting, a segmented control for a choice.

Section Control Default Shown when
The rest of the thread Switch On The commit starts a self-thread
Thread (text only) None: "Starts from the root commit" The commit is a reply
Replies Switch On The commit has replies
Per level Segmented: 1, 3, 5, 10 3 Replies is on
Levels Segmented: 1, 2, 3, All 3 Replies is on
Max height Segmented: 480px, 640px, 800px, None 640px Always
Theme Segmented: Light, Dark, Auto (with icons) Auto Always
Thread & replies The Embed commit dialog: switches and segmented controls on the left, the live card on the right, the HTML code below
Replies The dialog on a reply: a Thread note replaces the switch, and the preview starts from the root commit

A live preview of the card sits next to the settings, and under them on phones. The footer holds three tabs, each showing its code with the CodeBlock component of commits (highlighting, Wrap lines, copy control), highlighted on the server by the same CodeSpans:

  • HTML: the iframe, one attribute per line so it reads without scrolling, and the resize script, for any site that accepts HTML. Copy code prints a mono toast [embed <hash>] copied, in the voice of the [log <hash>] <body> toast after committing.
  • Markdown: Markdown renderers (GitHub READMEs, docs) leave frames out, so this tab gives the commit's preview image (og.png) linked to the commit: [![Commit <hash> by @handle on /dev/null](…/og.png)](…/commits/<uuid>). Where Markdown accepts HTML, the HTML tab gives the live card. Copy Markdown prints [embed <hash>] Markdown copied.
  • Link: the commit URL with the settings and their signature as parameters, for sites that support oEmbed. Copy link prints [embed <hash>] link copied.
HTML HTML tab: the iframe, one attribute per line, and the script
Markdown Markdown tab: the commit's preview image linked to the commit
Link Link tab: the commit's signed link for oEmbed

Every frame (the preview, the HTML code, oEmbed's) carries color-scheme: normal, like the card: a frame whose color scheme differs from its document's gets an opaque backdrop, which would show around the card's rounded corners on a dark page.

A failed copy prints fatal: could not copy embed code for <hash>, like the existing Copy link action.

Naming

The feature is called embed, in the UI (Embed commit) and in code (/embed, allows_embeds). PRODUCT.md stops the git metaphor at commit and log outside Remotes, and wants labels anyone understands, so the action keeps a plain word. The git voice lives in the copy instead: the [embed <hash>] copied toast.

Alternative: cherry-pick

If the maintainers want the feature itself to carry a git word, cherry-pick is the proposed alternative: picking a commit and bringing it somewhere else is what an embed author does, and every developer knows the word.

Where Embed (proposed) Cherry-pick (alternative)
Menu entry Embed commit Cherry-pick commit
Modal title Embed commit Cherry-pick commit
Toast [embed <hash>] copied [cherry-pick <hash>] copied
Setting Embeds Cherry-picks
Route /embed/{commit} /cherry-pick/{commit}
Column allows_embeds allows_cherry_picks

It comes with costs:

  • PRODUCT.md would need an explicit exception to "the git metaphor stops at those two nouns", next to the one Remotes already has.
  • Principle 5 ("Wit in the copy, clarity in the action") asks for labels anyone understands. Cherry-pick commit says less than Embed commit to someone who doesn't use git, and doesn't say the result goes on another website.
  • In git, a cherry-pick is an independent copy that no longer follows its source. A /dev/null embed follows its source: a deletion, a rejection or an opt-out shows on the third-party page too. The word suggests the opposite of that guarantee.

For these reasons the RFC keeps embed, and lists cherry-pick for the maintainers to weigh.

What the card shows

  • A top-level commit: the commit, then the rest of its self-thread when enabled (1/3, 2/3…), then its replies when enabled. When the thread is shown, the replies branch off the solid trunk between 1/3 and 2/3, like on the commit's page.
  • A reply: always the whole chain from the root commit down to the reply, then the reply's own replies. The card opens at the top.
  • Replies: the first N direct replies, in order, at each level, down to the chosen depth. Each truncated level ends with N more replies on /dev/null on a hollow node, like Show the last 2. Quotes are listed among the replies with the quoted label.
  • Commits read like on /dev/null: the same header (name, handle, date, then the 1/3 thread badge, and the hash on the right), the same body, code blocks and quotes. Dates are short and absolute (Nov 6), since relative times age badly in an article.
  • The embedded commit is drawn like the focused commit on its page: its body in 17px, its hash in the commit page's mono line, 14:32 · Nov 6, 2026 · commit 9f3c2a1, rather than in its header.
  • Actions: /dev/null's actions row, read-only. The star with its count and › 5 replies both open the commit on /dev/null; there is no Quote, Translate or Reply.
  • Polls: while open, the options link to /dev/null to vote, with the time left, and no results; once closed, the final results.
  • Text: always the original. No translation and no Translate control.
  • Embedded players: same behavior as on /dev/null (click to play, only on the link that gets the card).
  • Footer: the /dev/null logo and wordmark, and View on /dev/null →.
  • Max height: past it, the thread scrolls inside the card and the footer stays in view.
A reply, from the root An embedded reply: its ancestors from the root on the solid trunk, then its own replies
A single commit A single commit, without thread or replies
A narrow page The card on a phone-width page, with the narrow rail geometry

Tombstones

Each commit in the card that can't be shown keeps its place, without author or body, so the rest of the thread still reads:

Tombstone When
[deleted] The commit was deleted
[embedding disabled] Its author, or its organization, turned embeds off
[unavailable] The commit is pending or rejected

This applies to the embedded commit itself: ancestors stay above it and replies below it. A tombstone uses the authorless rail of today's [deleted] placeholder: a dashed Rail Gray avatar.

An embedded commit that is still pending: the card shows the [unavailable] tombstone

The card is always rendered as a guest sees /dev/null: no session, so no mutes or blocks to apply.

Freshness

An embed never shows a deleted, rejected or opted-out commit for longer than /dev/null itself does.

Design

The card reuses the commit page's components rather than copying them, so the rail stays identical:

  • 72px rail (64px under 460px of card width), the 40px avatar as the node, a 2px lane ring offset from the surface.
  • A solid trunk down to the dot on the actions row, then a dashed line in the author's lane.
  • Rounded dashed elbows in the parent's lane to 28px reply avatars (24px narrow), 38px of indent per level (32px narrow), flattened past three levels (maxIndentDepth).
  • Tokens, type and copy from DESIGN.md; the card follows Light, Dark or the reader's system.

DESIGN.md gains a Commit Embed component section once the design is settled.

Technical design

Overview

sequenceDiagram
    autonumber
    participant Author as Embed author
    participant App as dev-null.sh
    participant Site as Third-party page
    participant Reader as Reader's browser

    Author->>App: "…" → Embed commit, pick settings
    App-->>Author: iframe + embed.js, or commit link with settings
    Author->>Site: Paste code or link
    alt Link on an oEmbed consumer
        Site->>App: GET /oembed?url=…
        App-->>Site: rich oEmbed JSON with the iframe
    end
    Reader->>Site: Open the page
    Site-->>Reader: Page with the iframe
    Reader->>App: GET /embed/{uuid}?…
    App-->>Reader: Server-rendered card, framable, no cookies
    Reader->>Reader: Card posts its height, embed.js sizes the iframe
Loading

Routes

Method Path Purpose
GET /embed/{commit} The card, framable, whereUuid('commit')
GET /embed.js Resize script, static and long-cached
GET /oembed oEmbed provider, JSON
GET /commits/{commit}/embed-link Members only: the card's signed URL, the commit link that stands for it, and the HTML, Markdown and Link code, for the dialog
PUT /settings/embeds The user's setting, UserEmbedsController
PUT /organizations/{organization}/settings/embeds The organization's setting, admins only, OrganizationEmbedsController

The commit page, when opened from a signed embed link (?embed&…&signature=…), gets an oEmbed discovery link in its <head>, built from the raw request URI so the query keeps the order it was signed in:

<link rel="alternate" type="application/json+oembed"
      href="https://dev-null.sh/oembed?url=…" title="…">

Embed parameters

Shared by /embed/{commit} and the commit URL (Link tab, behind an embed flag):

Parameter Values Default without parameters
thread 1 Off
replies 1, 3, 5, 10 Off
depth 1, 2, 3, all 3 when replies is set
theme light, dark Auto
max_height 480, 640, 800, none 640

Unknown or out-of-range values fall back to their default. The commit is addressed by its UUID, never by its short hash.

Signed URLs

Every embed URL, the iframe's and the Link tab's, carries a signature parameter: an HMAC of the card's path and normalized query, keyed with the application key, the way Laravel's relative signed routes work (URL::signedRoute(absolute: false) and signed:relative, without expiry). Leaving the scheme and host out keeps cards working behind a proxy that terminates TLS, or on another domain of the app; the spike found every card answering 403 through a tunnel with absolute signatures.

  • Only the modal, behind authentication, produces signatures. /embed and /oembed answer 403 (401 for oEmbed, as the spec expects) to a missing or wrong signature, so the card and the oEmbed fall back to a plain link.
  • A bare commit link pasted into an oEmbed consumer therefore gives no card: only the Link tab's signed link does.
  • Nothing is stored and nothing ties an embed to the member who created it: the signature covers the commit and the settings only.
  • Since settings are part of what is signed, nobody can derive other cards from an existing embed, which also closes off cache-busting variants.
  • Rotating the application key invalidates every existing embed. That is acceptable for a key rotation, and worth noting in the runbook.

The embed page

  • Middleware: outside the web group's session and CSRF handling, the way the cached public group in routes/web.php already is, so the response sets no cookie and always renders the guest view. Excluded from PreventFraming; it sends its own policy instead: Content-Security-Policy: frame-ancestors * and no X-Frame-Options.
  • Query: builds the tree once from the embedded commit (ancestors to the root, self-thread, N replies per level down to the depth), with eager loading, and marks each node as visible or as one of the three tombstones. Visibility uses the existing guest rules (CommitStatus::Approved, deleted_at), plus the author's or organization's embed setting.
  • Bundle: its own client entry point (resources/js/embed.tsx), which loads only the card's page, none of the app's live updates, analytics or service worker. On the server, the card renders through the app's SSR bundle; a dedicated SSR entry could slim it further.
  • Rendering: a server-rendered Inertia page with a bare layout (no app shell), reusing GraphRail, the thread elbows, CommitBody, CommitImages, CommitPoll (read-only), LinkPreview and LinkEmbed. Read-only props turn off every control that writes.
  • Abuse: the page is public and anonymous by design, so it is protected by rate limits, a short cache and bounded work. See Abuse and load.

Without JavaScript

Two scripts are involved, and missing either one leaves a readable card:

Missing When What the reader gets
embed.js, on the framing page The site strips <script> but keeps frames (many CMSs, sanitized Markdown) The card, at the height written in the code: max_height plus the footer (685px by default). A long thread scrolls inside the card; a short one leaves room below it. With None, the frame itself scrolls.
The card's own JavaScript, inside the frame The reader turned JavaScript off, or the platform sandboxes the frame without allow-scripts The whole card, rendered on the server like every page: text, highlighted code, images, links, the rail, closed polls, tombstones.
  • The card is part of the SSR bundle, which renders every page (ssr.tsx resolves ./pages/**/*.tsx); a unit test renders it the same way and reads the whole thread in the server's HTML.
  • Links open outside the frame with <base target="_blank">, so they work without the card's click handler.
  • What only JavaScript adds: the code blocks' copy and Wrap lines controls, click-to-play players, and the height message for embed.js.
  • Without JavaScript, an explicit Light or Dark holds, since the server sets it; Auto stays light, since only the browser knows the reader's preference. Times read in the server's time zone rather than the reader's.
  • A site that strips <iframe> too gets nothing from the HTML tab: that's what the Markdown tab and oEmbed are for.
The card with JavaScript turned off: the commit, its code block, the replies and the rail, all from the server

Resize protocol

The card posts its content height to its parent whenever it changes (ResizeObserver):

parent.postMessage({ type: 'devnull:embed:resize', id, height }, '*')

embed.js listens for that message, accepts it only from the origin it was itself loaded from (https://dev-null.sh), resizes the one frame whose window sent it (event.source), and listens once even when a page includes it several times. With a max height, the card caps itself and scrolls inside, so the iframe never grows past max_height plus the footer. Without embed.js, the iframe keeps the height written in the snippet and still works.

oEmbed

GET /oembed?url={commit URL}&maxwidth=&maxheight=&format=json returns a rich response:

{
  "version": "1.0",
  "type": "rich",
  "provider_name": "/dev/null",
  "provider_url": "https://dev-null.sh",
  "author_name": "Mira Okafor",
  "author_url": "https://dev-null.sh/@mira",
  "html": "<iframe src=\"https://dev-null.sh/embed/…\" … style=\"border:0;max-width:100%;color-scheme:normal\"></iframe><script async src=\"https://dev-null.sh/embed.js\"></script>",
  "width": 550,
  "height": 685,
  "cache_age": 3600
}
  • The embed parameters on url carry over to the iframe; the height is max_height plus the 45px footer (640px when there is no cap), and the width follows maxwidth, up to 550px.
  • 404 for a commit that doesn't exist, and 401 when the link isn't signed or the commit can't be embedded (unapproved, deleted or opted out), as the oEmbed spec expects, so consumers fall back to a plain link.
  • format=xml returns 501.
  • author_name is the profile's name, an organization's for its commits (the member who posted is never revealed).
  • Platforms with an allowlist (WordPress core, for instance) need /dev/null registered with them; the oEmbed providers registry is the first step.

The card in a Notion page, built from the Link tab's signed link

Data

One boolean per profile owner that can turn embeds off:

Table Column Default Why it's needed Who sees it When it's deleted
users allows_embeds true The author's choice is the feature The user With the account
organizations allows_embeds true Same, for commits posted as the org The org's admins With the organization
  • Hidden from serialization except to the owner (and the organization's admins).
  • Included in CreateExport; removed with the row by DeleteUser.
  • Nothing else is stored: no embed records, no referrers, no counters.

Settings

  • Settings → Profile gains an Embeds section in the same shape as Pastéis mode: a title, a description, and a switch.
  • The organization's settings gain the same section, editable by admins only (Organization::isAdmin()).

Abuse and load

Every reader of an article loads the card anonymously, so /embed and /oembed are the cheapest way to make /dev/null do work without an account. They must stay cheap, and cost an attacker more than they cost the server.

  • Rate limits, in AppServiceProvider next to the existing limiters:
    • embeds: per IP and per commit ("{$ip}|{$commit}"), like activity-graph, so a page with several embeds still loads, and generous enough for readers behind a shared address. A global per-IP ceiling on top stops one address from walking every commit.
    • oembed: per IP, lower, since consumers fetch it once and cache it.
    • A throttled card answers 429 with Retry-After; the iframe shows a plain fallback with a link to the commit instead of an error page.
  • Bounded work:
    • The signature is checked first, before any query: an unsigned or forged URL costs one HMAC.
    • Parameters are normalized to their closed set of values, and unknown parameters are dropped. A commit therefore has a few dozen possible cards at most, and only signed ones can be requested, so random query strings can't bypass the cache.
    • A card holds at most a fixed number of commits (40, to tune). Past it, the remaining levels end with N more replies on /dev/null, whatever the depth asked for. depth=all never walks an unbounded thread.
    • The tree is built in a constant number of queries, with eager loading, whatever its size.
    • An unknown or malformed UUID is rejected by the route constraint before any query.
  • Short server cache: the rendered tree is cached per commit and normalized parameters, holding public data only, for 30 seconds, the interval at which /dev/null pages pick up changes. It is forgotten as soon as a commit in the thread is deleted, moderated, or its author changes their embed setting, so the card is never staler than /dev/null (decision 20). Misses for unknown commits are cached too, for the same short time.
  • No writes: serving a card never writes to the database or the session.
  • Static script: embed.js is a versioned static asset with a long cache, served without touching PHP.
  • Edge protection: if Laravel Cloud's edge or a WAF sits in front of the app, /embed and /oembed are the routes to give a dedicated rule. That is the maintainers' call and out of scope for the implementation.

Security and privacy

  • The embed page is the only framable page. Everything else keeps PreventFraming.
  • Embed URLs are signed, so only members create cards (see Signed URLs).
  • No cookies on the embed page: no session to ride on, no clickjacking of signed-in actions, nothing written from the card.
  • The resize message carries a height only; embed.js checks its origin before acting on it.
  • The card shows only what a guest sees. Pending, rejected and opted-out content never leaves /dev/null; an organization's commit never reveals the member who posted it.
  • Third-party players load only after the reader presses play, as today.

Screenshots

Captured on the partial implementation in #1522, running locally, and shown in the sections above:

Screenshot Files Section
The card in a blog post 08-site-light.png, 08-site-dark.png TL;DR
Settings → Profile, Embeds 01-settings-profile-embeds-{light,dark}.png Who can embed what
Organization settings, Embeds (admins only) 02-organization-settings-embeds-{light,dark}.png Who can embed what
The "…" menu with Embed commit 03-commit-menu-{light,dark}.png Entry point
The dialog, on a top-level commit and on a reply 04-modal-thread-{light,dark}.png, 07-modal-reply-{light,dark}.png The modal
The HTML, Markdown and Link tabs 05-modal-{html,markdown,link}-tab-{light,dark}.png The modal
A reply from the root, a single commit, a narrow page 08-site-{reply,single,narrow}-{light,dark}.png What the card shows
The [unavailable] tombstone 09-tombstone-unavailable-{light,dark}.png Tombstones
The card in Notion, through oEmbed 10-notion-embed.png (light only) oEmbed
The card with JavaScript off 11-card-without-javascript-{light,dark}.png Without JavaScript

Not captured: a throttled card, since the spike answers Laravel's plain 429 and the fallback isn't built.

What the spike leaves out, compared to this RFC: the 30-second cache and its invalidation, the 429 fallback, building the tree in a constant number of queries (it queries each node's replies), and sharing the rail components with the commit page (the card draws the same geometry with its own code).

Rollout

Each step is a pull request of its own. Commit titles follow the upstream rule, without a scope (feat: …).

  1. Embed setting: migration, user and organization settings sections, export, deletion, policies.
  2. Embed page: route, middleware exclusions, tree query with tombstones, bare SSR page reusing the thread components in read-only mode.
  3. Resize script: embed.js and the card's height messages.
  4. oEmbed: endpoint and discovery link on the commit page.
  5. Menu entry and modal: Embed commit in the "…" menu, the signing endpoint, the modal with its live preview, the HTML, Markdown and Link tabs.
  6. Documentation: KNOWLEDGE_BASE.md (a Commit embeds section), DESIGN.md (the Commit Embed component), PRODUCT.md (embeds among the surfaces).

Decisions

# Topic Decision
1 Delivery iframe served by /dev/null, plus oEmbed
2 Interaction Read-only; interacting happens on /dev/null
3 Replies First N per level, N in 1, 3, 5, 10
4 Nesting 1 to 3 levels, or all, flattened past three like /dev/null
5 Who can embed Signed-in members, for any public commit, unless the author opted out; anyone can read a card
6 Rail Identical to /dev/null, shared components
7 Dates Absolute
8 Open polls Options link to /dev/null, results only once closed
9 Entry point The commit's "…" menu, hidden when the commit can't be embedded
10 Translations Original text only
11 Measurement None
12 Height Capped (640px by default), then scrolls inside the card
13 Embedding a reply Always from the root commit, then the reply's own replies
14 Actions /dev/null's actions row, read-only: the star and › N replies open the commit on /dev/null
15 Quotes Listed among replies with quoted
16 Unavailable content [deleted], [embedding disabled], [unavailable] tombstones
17 Embedded players Same as /dev/null
18 Opt-out Settings → Profile, allowed by default, exported, erased with the account
19 Organizations Organization-level setting, allowed by default, admins only
20 Freshness Never staler than /dev/null
21 Modal /dev/null dialog with Settings conventions and a live preview
22 Link tab Settings and signature as URL parameters; a bare link gives no card
23 Identifier The commit's UUID
24 Column allows_embeds on users and organizations
25 Setting endpoints Dedicated controllers, like UserPasteisModeController
26 Bundle A client entry point of its own, with the card's page only; the app's SSR bundle on the server
27 Naming "Embed" for the action and in code; the git voice stays in the copy; "cherry-pick" proposed as the alternative
28 Members only Every embed URL is signed by the modal; unsigned URLs get no card
29 Abuse Per-IP and per-commit rate limits, 30s invalidated cache, capped tree, no writes
30 Code in the dialog CodeBlock like commits, highlighted on the server; HTML one attribute per line
31 Markdown A Markdown tab with the commit's preview image linked to the commit
32 Signatures Relative (path and query), so cards work behind proxies and on other domains
33 Without JavaScript Server-rendered card, links through <base target="_blank">; scripts only resize and enhance

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions