diff --git a/CHANGELOG.md b/CHANGELOG.md index 64c8374..200adf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,23 @@ ### Changed — BREAKING +- **Serves its own HTTP routes, so the bar works without an admin (#127).** `source`, `commit` and + `fields` are at `/api/inline-edit/content//...` (core gates them: signed-in editor + holding `pages.update`, same-origin writes), with the TSX and page-ownership rules the admin's + routes enforced. The bar is injected for any such editor, including a site user on a site with no + admin, and no longer requires an `admin.prefix` provider. The bar no longer renders "Edit source" + or "Open in admin": the admin contributes those through `bar.actions`. `commit()` and + `patchFields()` stay on the manager object but are not part of core's `InlineEditManager`. + `onPageUpdate` carries the editor's name as `actor`. + +- **The bar declares its own extension point, and reads what it needs per request (#127).** + `bar.actions` is declared here, as the typed key `BAR_ACTIONS`, with its types `BarAction` and + `BarActionContext` (renamed from core's `AdminBarAction` / `AdminBarActionContext`, which + left core's contract; the context no longer carries `adminPrefix`). A plugin that adds a + button imports the types and `BAR_ACTIONS` from `@dune/plugin-inline-edit` (or recreates the key from + the name `bar.actions`). `transformResponse` reads the + contributions and the `admin.prefix` slot from `ResponseTransformContext.capabilities` + instead of `barActions` / `adminPrefix`. - **Provides the `editor.inline` capability and reads `bar.actions`, instead of `adminServices` and `ctx.plugins` (duneorg/dune#97, `@dune/core` 0.36).** The manager is declared in `setup()` with `api.capabilities.provide("editor.inline", ...)`; the admin diff --git a/README.md b/README.md index 5e8a593..12512f8 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,9 @@ point in `transformResponse`: manager, and the admin's content endpoints use it for commits and presence. Any plugin can provide the same slot to replace it. - **Renders `bar.actions`** — buttons that other plugins contribute with - `api.capabilities.contribute("bar.actions", ({ page, adminPrefix }) => [...])` - show up in the admin bar; this plugin does not know what they do. + `api.capabilities.contribute("bar.actions", ({ page }) => [...])` show up in the + bar; this plugin does not know what they do. It declares the point, so import + `BarAction` / `BarActionContext` from `@dune/plugin-inline-edit`. Admin identity comes from whichever plugin provides `auth.session` (the admin plugin does); with none, no bar is injected. diff --git a/src/bar-actions.ts b/src/bar-actions.ts new file mode 100644 index 0000000..8642d46 --- /dev/null +++ b/src/bar-actions.ts @@ -0,0 +1,68 @@ +/** + * The bar's extension point. + * + * This plugin renders the editing bar and is the only reader of `bar.actions`, + * so it declares the key (and its types) itself instead of core's contract + * carrying it. A plugin that adds a button imports the key and the types from + * `@dune/plugin-inline-edit` (or recreates the key from the name `bar.actions`): + * + * ```ts + * import { BAR_ACTIONS } from "@dune/plugin-inline-edit"; + * api.capabilities.contribute(BAR_ACTIONS, ({ page }) => + * page ? [{ id: "pdf:download", label: "PDF", href: `/pdf${page.route}` }] : []); + * ``` + * + * @module + */ + +import { type CapabilityKey, capabilityKey } from "@dune/core/plugin-api"; + +/** + * Context passed to a `bar.actions` contribution: what belongs on the bar for + * this response. A plugin that links to its own pages builds the URLs itself + * (the admin knows its prefix); the bar passes none. + * + * @since 3.0.0 + */ +export interface BarActionContext { + /** Content page matching the current URL, or null for non-content routes. */ + page: { sourcePath: string; route: string; title: string | null; template: string } | null; +} + +/** + * A button contributed to the editing bar through the `bar.actions` + * contribution point. This plugin owns the bar; the contributing plugin controls + * only the button's appearance and behavior, not its position or styling. + * + * @since 3.0.0 + */ +export interface BarAction { + /** + * Unique id — namespace by plugin name to avoid collisions across + * plugins, e.g. `"pdf-export:download"`. + */ + id: string; + /** Button label. */ + label: string; + /** Optional emoji/icon prefix. */ + icon?: string; + /** + * Renders as a plain link (``) when set — the common case for + * downloads and navigation. Mutually exclusive with `onClick`; `href` + * takes precedence if both are set. + */ + href?: string; + /** + * Renders as a `