All notable changes to Tayori are documented in this file.

The format follows Keep a Changelog, and Tayori follows Semantic Versioning.

[0.1.0] - unreleased

The first release.

Added

  • Entries in the app's database, on PostgreSQL and SQLite, in versioned migrations (Tayori.Migrations, mix tayori.gen.migration; a forgotten version says so at boot): a title, a summary and a Kotoba body in each language, an optional cover apart from the body, categories, segments, a slug, a pin and a publication time.
  • Categories, managed in the admin: names in each language, a colour, an order.
  • The admin (tayori_admin), mounted with PhoenixMountable behind the app's own pipeline, with its own layout, script and styles, in Tayori's theme or Sumi's, light or dark: the entries, with tabs, counts, a search and each entry's views and reactions; the editor, with a preview as the page, the panel or the announcement, and the publication, cover, details, audience, announcement, email and statistics beside it; scheduling in the admin's time zone; the categories; the settings. Tayori.Resolver says who the admin is and what they may do.
  • Readers (Tayori.Reader) from the app's own users: segments, a language, what each has not seen.
  • The news in LiveViews: Tayori.Live and Tayori.Components, a button and a panel with category filters, cards, and each entry whole inside it, live over PubSub and across tabs.
  • The news for any frontend: a JSON API (tayori_api) with entries, categories, reactions and the announcement, and <tayori-news>, the LiveView panel as a web component: the API renders the same Tayori.Components for it (its /ui routes), and the element answers their clicks and keys as LiveView would.
  • scheduler: false, for an app's tests: no scheduler queries the repo outside a test's sandbox, and Tayori.Test.announce!/0 announces.
  • The public page (tayori_page): filters, a search, pinned entries first, each entry's page with the previous and next, Atom and JSON feeds (of all or one category), an SEO title and description of each entry, Open Graph, Twitter cards, hreflang, JSON-LD and a sitemap, and preview links of drafts and scheduled entries, signed, for a week. The admin and the API find it in the same router by themselves. In the app's own layout (:layout), which gets the page's language, description, canonical address and its tags rendered (Tayori.Page.head/1); its title, description and eyebrow by locale; for a visitor, the app's own choice of language (Tayori.Resolver.resolve_locale/1) before the browser's.
  • Opt-in features (features: [...] of Tayori.Config), each with no route, page or process when it is off:
    • :reactions: emoji reactions, one for each reader and emoji;
    • :announcements: an entry as a popup, a toast or a banner in the app, with a button, until a date, dismissed on every device;
    • :analytics: the views of each entry by day and by where it was read, with no address, device or cookie kept;
    • :subscriptions: readers subscribe by email on the public page, with double opt-in and one-click unsubscribing, through the app's Swoosh mailer;
    • :webhooks: signed webhooks, Slack and Discord, told of each entry, their addresses and secrets kept encrypted;
    • :embed: the news on another origin, with CORS for the app's sites and a reader token the app signs (Tayori.Embed.token/1).
  • Each entry announced once when it is published, to the open pages, the subscribers, the webhooks and the app's Tayori.Notifier: as jobs of the app's Oban (:oban), retried through deploys, or from tasks (Tayori.Jobs).
  • Writing with AI, with the app's own key, kept encrypted by the app's Cloak vault (:vault) or with its secret_key_base: drafts, translations, and rewrites of a passage, as Kotoba suggestions.
  • Automation: an inbox of what shipped (Tayori.Inbox), from a Keep a Changelog file and a folder of fragments, Changesets' and towncrier's among them (Tayori.Sources), read at each deploy; from CI, with a token (POST <api>/changes, mix tayori.ingest); from GitHub's webhook, signed, for merged pull requests and published releases (Tayori.GitHub); and from the app's code. Changes for the team stay out of the news. Digests make entries of it, plain or written by the model, with translations, as drafts or published, when the admin asks, as changes come, or each week, month or n days (Tayori.Digest). The emails go for each entry, or as one email of a period (:emails). The admin's inbox, the Automation card of Settings, and the changes behind an entry in the editor.
  • An MCP server (Tayori.MCP), mounted with Sake in the app's own, beside its other libraries': the news, the inbox and the categories to read; changes to add, entries to draft, write, publish and unpublish, under the admin's own access; screenshots from a browser agent, uploaded once with a URL that lasts ten minutes, as figures in an entry.
  • The words around the news in eight languages (Tayori.Messages).
  • Tayori.Test, the installer (mix igniter.install tayori, which mounts the admin and the public page and starts Tayori, with the app's Oban when it has one), and the themes tayori.css and tayori-sumi.css, with icons from Lucide (see NOTICE).