Skip to content
pipe1osPublic

About

In-app changelog and feature hotspots, drafted from your merged PRs. Markdown in git, no backend, <10 KB web component. Open-source Beamer/Headway alternative.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Latest commit

 

History

59 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🇪🇸 Leer en español

shipdot

Announce new features right where they live.
In-app changelog and feature hotspots generated from your pull requests. Zero backend, tiny web component.

CI MIT license npm version Bundle size

Docs · Playground · Getting started · Changelog

A demo analytics app beside the Markdown entry that announces its new feature: the entry's target: #btn-export line sits level with a pulsing red hotspot on the app's Export button, whose tooltip announces Export to Excel, and shipdot build output sits below the entry


shipdot shows your users what changed, inside your app: a bell with an unread count, a "What's new" panel, and hotspots, pulsing dots on the exact buttons a new feature lives in. Updates are Markdown files in your repository, drafted from your merged pull requests and reviewed like code. There is no server, database, account or tracking.

Note

shipdot is young (0.x). The shipdot.json format is versioned and stable; the widget, CLI and Action may still change between minor versions. Every change is in the changelog.

Why shipdot

  • Hotspots on the real UI. An entry with target: "#btn-export" puts a pulsing dot on that button. Telling users a feature exists is good; pointing at it is better. Hotspots follow their target on scroll and resize, wait for elements rendered late in SPAs, and hide when the target is hidden.
  • Git-native, reviewed by a human. shipdot draft turns merged pull requests into notes written for end users and opens a pull request. You fix the wording, merge, ship.
  • Multi-language from day one. entry.es.md next to entry.md. The widget picks the user's language, falls back per entry, and ships its UI strings in English, Spanish and Portuguese.
  • Nothing to run or pay for per user. A static shipdot.json and a web component of about 7 KB min+gzip. No backend and no telemetry: your announcements stay in your repository, and read state stays in your users' browsers.

Features

  • Feed panel as a popover, drawer or modal, with an unread badge.
  • Drafts from pull requests: offline template mode, or rewrite and translate with your own LLM key (Anthropic or any OpenAI-compatible API).
  • GitHub Action that opens a pull request with new drafts every week.
  • Zero runtime dependencies, Shadow DOM, and a 10 KB min+gzip budget enforced in CI.
  • Accessible: keyboard navigation, focus management, WCAG 2.1 AA contrast in light and dark themes, checked with axe.
  • llms.txt and llms-full.txt so AI assistants know what changed.
  • Themeable with CSS custom properties, ::part() and your own trigger.

Quick start

You need Node.js 24 or newer for the CLI. The widget runs in any modern browser.

1. Create the config and a first entry

npx @shipdot/cli init

2. Write entries and build the feed

Each update is a Markdown file in .shipdot/entries/, for example .shipdot/entries/2026-10-14-export-excel.md:

---
date: 2026-10-14
type: new             # new | improved | fixed
title: Export to Excel
target: "#btn-export" # optional: hotspot on this element
---
You can now export any report to Excel with one click.
npx @shipdot/cli build   # → public/shipdot.json, public/llms.txt, public/llms-full.txt

3. Add the widget

<script type="module" src="https://unpkg.com/shipdot@0.2"></script>
<ship-dot src="/shipdot.json"></ship-dot>

Or npm install shipdot and import 'shipdot' once on the client. Try it without installing anything in the playground.

The bell starts without a badge: on a first visit, existing entries count as read. To see the badge and hotspots, add a new entry and run build again, or set first-visit="show-latest" while you try it out.

How it works

flowchart LR
  A[Merged pull requests] -->|shipdot draft| B[Pull request with draft entries]
  B -->|human review and merge| C[".shipdot/entries/*.md"]
  C -->|shipdot build| D[shipdot.json]
  C -->|shipdot build| E[llms.txt]
  D --> F[ship-dot widget in your app]
  F --> G[Bell and unread badge]
  F --> H[Hotspots on target elements]
Loading

HTML is sanitized once, at build time, with a strict allowlist, so the widget stays tiny and never parses untrusted Markdown. The decisions behind the design are in docs/adr.

Widget

The What's new panel open in a demo analytics app, listing three updates tagged New, Improved and Fixed

<ship-dot
  src="/shipdot.json"
  mode="popover"
  locale="es"
  theme="auto"
  max-hotspots="3"
  first-visit="mark-read"
  storage-key="default"
></ship-dot>
Attribute Default Description
src required URL of shipdot.json. The only request the widget makes.
mode popover popover, drawer or modal.
locale navigator.languages Language to show, with fallback to the feed's default.
theme auto light, dark or auto.
hotspots on off disables hotspots.
max-hotspots 3 Hotspots visible at once.
first-visit mark-read mark-read (existing entries count as read) or show-latest.
storage-key default Read state key: localStorage["shipdot:<key>"].

Methods: open(), close(), markAllRead(), markRead(ids), replayLatest(), refresh(). Events (bubbling, composed CustomEvents): shipdot:ready, shipdot:open, shipdot:close, shipdot:read, shipdot:hotspot-click, shipdot:error. Styling: CSS custom properties (--shipdot-accent, --shipdot-hotspot, --shipdot-bg…), ::part() on every key piece, and a default slot for your own trigger.

Full reference: widget · theming · hotspots.

CLI

Command What it does
shipdot init Creates shipdot.config.json and an example entry. Never overwrites.
shipdot build Validates entries, renders sanitized HTML, writes shipdot.json, llms.txt, llms-full.txt and copies relative images.
shipdot validate Prints file:line:column errors (duplicate IDs, bad dates, unknown types, orphan translations, undeclared locales…). Exit code 1 on errors.
shipdot draft Drafts entries from PRs merged since the last tag. --dry-run, --since, --base, --repo, --no-llm.

Full reference: CLI · config · feed format.

GitHub Action

# .github/workflows/shipdot.yml
name: shipdot
on:
  workflow_dispatch:
  schedule:
    - cron: '0 9 * * 1' # Mondays 09:00 UTC
permissions:
  contents: read
jobs:
  draft:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    steps:
      - uses: actions/checkout@v7
      - uses: pipe1os/shipdot@v0
        with:
          command: draft
          # llm-provider: anthropic
          # llm-api-key: ${{ secrets.SHIPDOT_LLM_API_KEY }}

Important

Enable Settings → Actions → General → Allow GitHub Actions to create and approve pull requests so the Action can open the draft pull request.

Input Default Description
command draft draft opens a pull request with new drafts; build writes shipdot.json and llms.txt.
github-token ${{ github.token }} Token for the GitHub API.
base-branch config, then repo default Branch the pull requests are merged into.
llm-provider, llm-api-key, llm-model, llm-base-url — Optional LLM mode. Pass the key from a secret; it is masked in logs.
working-directory . Folder with shipdot.config.json.

Outputs: entries, pull-request-url, pull-request-number. More in the Action guide.

Writing notes in pull requests

Add a ### shipdot section to your PR template and the note is written by the person who knows the change best:

### shipdot
title: Export to Excel
type: new
target: "#btn-export"

You can now export any report to Excel with one click.

The note ends at the next heading or at a horizontal rule (---), so keep the section above any footer that tools add to the description. Write skip to opt a PR out. PRs labeled skip-shipdot, bot PRs and chore/ci/test/docs/refactor PRs are skipped automatically.

LLM mode and privacy

Template mode works offline. To let a model rewrite drafts in user language and translate them, bring your own key:

SHIPDOT_LLM_PROVIDER=anthropic SHIPDOT_LLM_API_KEY=... npx @shipdot/cli draft

SHIPDOT_LLM_PROVIDER is anthropic or openai-compatible (with SHIPDOT_LLM_BASE_URL and SHIPDOT_LLM_MODEL, for example a local Ollama). Requests use plain fetch, no SDKs.

Only the pull request title and body are sent, never diffs, code, file names, comments or author data. PR text is treated as untrusted: it is wrapped in escaped delimiters, the system prompt forbids following instructions inside it, and the reply must pass schema validation or the PR falls back to template mode. Every draft is reviewed by a human before it ships. Details: Drafting guide.

Internationalization

{ "defaultLocale": "en", "locales": ["en", "es", "pt"] }

Add 2026-10-14-export-excel.es.md with a translated title and body. The widget uses the locale attribute or navigator.languages, matches es-CL → es, and falls back to the default locale per entry. i18n guide.

Frameworks

Works anywhere custom elements do. Guides and typed JSX/template support for React, Vue, Svelte, Astro, Next.js and vanilla JS. Runnable apps live in examples/.

import 'shipdot'
import type {} from 'shipdot/react' // or shipdot/vue, shipdot/svelte, shipdot/astro

Browser support

Chrome and Edge 123+, Firefox 125+, Safari 17.5+ (Popover API, <dialog>, light-dark()). Importing the module during server-side rendering is safe.

FAQ

Why is nothing unread on my first visit?

A first visit marks existing entries as read, so new users aren't greeted by your whole history. Use first-visit="show-latest" or clear localStorage["shipdot:default"].

Is the HTML in entries safe?

shipdot build sanitizes it with an allowlist (no scripts, iframes, styles, event handlers or javascript: URLs), tested against common XSS vectors. The widget trusts shipdot.json, so serve it from your own site.

Can I use it without GitHub?

Yes. init, build and validate only read local files. Only draft and the Action talk to GitHub.

Can I open the panel from my own menu?

Put your own element inside <ship-dot> (it replaces the bell) or call document.querySelector('ship-dot').open().

More in the FAQ.

Roadmap

Not in the MVP, planned as opt-in features: analytics, segmentation by user or plan, A/B tests, RSS/Atom output, publishing to Slack, Discord and email, a web admin panel, and read-state sync across devices. See PLAN.md and share ideas in Discussions.

Contributing

Contributions are welcome! Read CONTRIBUTING.md for the setup (pnpm install && pnpm build && pnpm test), conventions and review process. Please follow the Code of Conduct and report security issues privately as described in SECURITY.md.

License

MIT © 2026 pipe1os and shipdot contributors

About

In-app changelog and feature hotspots, drafted from your merged PRs. Markdown in git, no backend, <10 KB web component. Open-source Beamer/Headway alternative.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages