@cosmicdrift/kumiko-bundled-features 0.182.1 → 0.183.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-bundled-features",
3
- "version": "0.182.1",
3
+ "version": "0.183.0",
4
4
  "description": "Built-in features — tenant, user, auth, delivery. The stuff you'd rewrite anyway, already typed.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -121,12 +121,12 @@
121
121
  "./step-dispatcher": "./src/step-dispatcher/index.ts"
122
122
  },
123
123
  "dependencies": {
124
- "@cosmicdrift/kumiko-dispatcher-live": "0.182.1",
125
- "@cosmicdrift/kumiko-framework": "0.182.1",
126
- "@cosmicdrift/kumiko-headless": "0.182.1",
127
- "@cosmicdrift/kumiko-renderer": "0.182.1",
128
- "@cosmicdrift/kumiko-renderer-web": "0.182.1",
129
- "@cosmicdrift/kumiko-types": "0.182.1",
124
+ "@cosmicdrift/kumiko-dispatcher-live": "0.183.0",
125
+ "@cosmicdrift/kumiko-framework": "0.183.0",
126
+ "@cosmicdrift/kumiko-headless": "0.183.0",
127
+ "@cosmicdrift/kumiko-renderer": "0.183.0",
128
+ "@cosmicdrift/kumiko-renderer-web": "0.183.0",
129
+ "@cosmicdrift/kumiko-types": "0.183.0",
130
130
  "@mollie/api-client": "^4.5.0",
131
131
  "imapflow": "^1.3.3",
132
132
  "mailparser": "^3.9.8",
@@ -42,7 +42,14 @@ export function createTemplateResolverFeature(opts: TemplateResolverOptions = {}
42
42
  const hasUserOwned = collections.some((c) => c.ownership === "user");
43
43
  return defineFeature("template-resolver", (r) => {
44
44
  r.describe(
45
- "The one content store: notification and mail templates, PDF document templates, AI prompts and plain editable text blocks all live here as one entity, distinguished by `kind` (`notification`, `mail-html`, `document-pdf`, `ai-prompt`, `text-block`, `image-snapshot`). Resolution uses a 4-level fallback: tenant+locale \u2192 system+locale \u2192 tenant+fallback-locale \u2192 system+fallback-locale, so tenants can override system defaults without touching application code. Call `ctx.templateResolver.resolveTemplate({ tenantId, slug, kind, locale })` at render time; manage templates via the `upsertSystem`, `upsertTenant`, `publish` and `archive` write handlers. Apps that want an editable collection in their navigation declare it at mount: `createTemplateResolverFeature({ collections: [{ id, kind, access: { roles }, nav }] })` \u2014 `access` belongs to the mount because a bundled feature does not know the host's role vocabulary. Each collection gets its own `<id>-list` / `<id>-item` / `<id>-set` handlers carrying that collection's access rule, so the dispatcher enforces the separation. A collection is either tenant-wide (default) or `ownership: \"user\"`, where every user keeps their own entries (mail signatures) in the separate `user-content-entry` entity — that one carries `userOwned` content, so mounting it also requires the `template-resolver-user-data` feature and an app-side migration. Replaces the former `text-content` feature, whose blocks now live here as kind `text-block`.",
45
+ [
46
+ "Every piece of editable text lives here, in one entity: mail bodies, notification texts, PDF document templates, AI prompts and plain text blocks. What a record is used for is the `kind` (`notification`, `mail-html`, `document-pdf`, `ai-prompt`, `text-block`, `image-snapshot`).",
47
+ "Reading is one call — `ctx.templateResolver.resolveTemplate({ tenantId, slug, kind, locale })`. It walks four levels: tenant+locale, system+locale, tenant+fallback-locale, system+fallback-locale. A tenant overrides a system default by simply having its own record; no application code changes.",
48
+ "Text an editor should be able to change belongs in a collection, declared at mount: `createTemplateResolverFeature({ collections: [{ id, kind, access: { roles }, nav }] })`. It appears in the navigation, and `access` is part of the mount because a bundled feature cannot know the host's roles. Each collection gets its own `<id>-list` / `<id>-item` / `<id>-set` handlers, so the dispatcher enforces the separation.",
49
+ "How a collection is edited follows from `contentFormat`: `plain` gives a text area, `rich` a small WYSIWYG (bold, italic, headings, lists, links). Both offer the collection's `variableSchema` as insertable chips and a preview rendered with sample data — an editor sees what `{{firstName}}` becomes without sending a mail. An app can register its own editor for a format and wins over the built-in one.",
50
+ 'A collection is tenant-wide by default. With `ownership: "user"` every user keeps their own entries — mail signatures being the obvious case. Those rows live in the separate `user-content-entry` entity and count as user data, so mounting one also requires the `template-resolver-user-data` feature and a migration on the app side.',
51
+ "Replaces the former `text-content` feature; its blocks now live here as kind `text-block`.",
52
+ ].join("\n\n"),
46
53
  );
47
54
  r.uiHints({
48
55
  displayLabel: "Template Resolver",
@@ -28,7 +28,11 @@ import {
28
28
  useQuery,
29
29
  useTranslation,
30
30
  } from "@cosmicdrift/kumiko-renderer";
31
- import { type ClientFeatureDefinition, PlainContentEditor } from "@cosmicdrift/kumiko-renderer-web";
31
+ import {
32
+ type ClientFeatureDefinition,
33
+ PlainContentEditor,
34
+ RichContentEditor,
35
+ } from "@cosmicdrift/kumiko-renderer-web";
32
36
  import { type FormEvent, type ReactNode, useEffect, useState } from "react";
33
37
  import {
34
38
  collectionHandlerName,
@@ -480,6 +484,7 @@ export function textBlocksClient(opts?: {
480
484
  // another clientFeature still wins, last-wins same as columnRenderers.
481
485
  contentEditors: {
482
486
  plain: PlainContentEditor,
487
+ rich: RichContentEditor,
483
488
  // ponytail: same textarea+chips as "plain" — markdown is stored as
484
489
  // text either way. Shares PlainContentEditor's fixed-DOM-id ceiling
485
490
  // (see its own file comment); swap for a live-preview widget or a