@knkcs/contenthub-ui 0.3.1 → 0.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.0](https://github.com/knkCS/contenthub/compare/contenthub-ui-v0.3.1...contenthub-ui-v0.4.0) (2026-10-14)
4
+
5
+
6
+ ### Features
7
+
8
+ * add editing settings for the workspace and per blueprint ([#271](https://github.com/knkCS/contenthub/issues/271)) ([f1d3cbb](https://github.com/knkCS/contenthub/commit/f1d3cbb8d4e5f3b355abb3740b770e9c565cea77))
9
+ * correct a release's dates in one request with change validity ([#274](https://github.com/knkCS/contenthub/issues/274)) ([a75dbdf](https://github.com/knkCS/contenthub/commit/a75dbdfc94c0aa969f8161e5904437ddd6136886))
10
+ * edit, save, publish and discard undated content directly ([#264](https://github.com/knkCS/contenthub/issues/264)) ([#276](https://github.com/knkCS/contenthub/issues/276)) ([6307d3c](https://github.com/knkCS/contenthub/commit/6307d3c7d4172c4ab461e3d1ddc82217f4609058))
11
+
3
12
  ## [0.3.1](https://github.com/knkCS/contenthub/compare/contenthub-ui-v0.3.0...contenthub-ui-v0.3.1) (2026-10-13)
4
13
 
5
14
 
package/README.md CHANGED
@@ -4,12 +4,13 @@ contenthub's screens for knk hosts (contenthub ADR 0020, knkcms/core ADR 0003).
4
4
 
5
5
  The package exports screens only, never routes:
6
6
 
7
- - `ContenthubSettings`: the workspace's configuration on one settings page of three tabs, chosen by `?tab=` (`required-checks`, `block-reasons`, `collections`), so it needs a router. Required Checks are declared and edited in a Modal (name, scope, Blueprints by accessor or every one, independent); who records each is read from contenthub (every member, until narrowed) and narrowed from its row to named members from the host's members, adding oneself (break-glass) included, or opened to every member again; in `authz.mode: workspace` every member records every check, and the tab says so and offers no recorders editor. Block Reasons are listed, created, renamed and removed. Collections are the tree in order, created, renamed, moved and removed; restricted (making the admin its owner) and unrestricted, each confirmed, the unrestrict as an acknowledged disclosure; and a Restricted Collection's grants are managed from the host's members, granting oneself (break-glass) included. In `authz.mode: workspace` contenthub refuses restriction, and the tab then says it needs guardian mode.
7
+ - `ContenthubSettings`: the workspace's configuration on one settings page of four tabs, chosen by `?tab=` (`required-checks`, `block-reasons`, `editing`, `collections`), so it needs a router. Required Checks are declared and edited in a Modal (name, scope, Blueprints by accessor or every one, independent); who records each is read from contenthub (every member, until narrowed) and narrowed from its row to named members from the host's members, adding oneself (break-glass) included, or opened to every member again; in `authz.mode: workspace` every member records every check, and the tab says so and offers no recorders editor. Block Reasons are listed, created, renamed and removed. Editing shows the workspace's Editing Mode and Editing Block (ADR 0022), changed in a Modal, above a table of the per-Blueprint overrides, each Blueprint with the values its Content gets and a field left to the workspace marked as its default; overrides are added by accessor, changed and removed. Collections are the tree in order, created, renamed, moved and removed; restricted (making the admin its owner) and unrestricted, each confirmed, the unrestrict as an acknowledged disclosure; and a Restricted Collection's grants are managed from the host's members, granting oneself (break-glass) included. In `authz.mode: workspace` contenthub refuses restriction, and the tab then says it needs guardian mode.
8
8
  - `BlockReasonsSettings`: the Block Reasons on a page of their own, for a host that mounts them apart from `ContenthubSettings`.
9
9
  - `ContentLibrary`: the Collection tree in `SubNavLayout.Nav` beside the Content table, narrowing together with the toolbar's search, view (Main, In Force, a Change Set) and Blueprint filters; the selected Collection is the URL's `?collection=`, so it needs a router. Rows call `onOpenContent({ contentId, changeSetId? })`; New Content creates a Content in an open or new Change Set and calls `onOpenBranch`.
10
- - `ContentPage`: one Content, read on Main or (`changeSetId`) inside the Change Set it was born in, in tabs Data (read-only, through fieldkit's read mode, loaded lazily), Releases, Revisions (with Compare), References, Change Sets and Filing. The header's one Save sets Filing (Collections, assignees) and never writes a Revision. Its rail offers Start editing, Duplicate, Repeal or Withdraw, Block or Unblock, and Archive (only once nothing is In Force today or later) or Unarchive; Start editing and Duplicate end in `onOpenBranch`, and `onOpenContent` and `onOpenChangeSet` open the rows of its References and Change Sets tabs. It needs `@knkcs/fieldkit` as a peer.
11
- - `ChangeSetsIndex`: the workspace's Change Sets, narrowed by state and assignee, created in a Modal; `onOpenChangeSet(id)` opens one, a new one too.
12
- - `ChangeSetPage`: one Change Set, its General tab (name and Source while it is open, status label, assignees, dependencies) saved from the header's one Save; its Contents tab, whose rows call `onOpenBranch({ changeSetId, contentId, branchId })`; its Releases tab (Planned Releases, proposals read-only, the Releases they cover) and its Checks tab (each Required Check's latest result, stale or not, and Record). Its rail holds Publish, Update from Main and Discard, and an About section with the readiness tile (`ValidatePublish` at the current heads), the status label and the Source; Publish opens a review that publishes exactly the heads it shows. `onPublished` and `onDiscarded` tell the host. Its leave guard needs a data router (react-router's `useBlocker`).
10
+ - `ContentPage`: one Content, read on Main or (`changeSetId`) inside the Change Set it was born in, in tabs Data (read-only, through fieldkit's read mode, loaded lazily), Releases (on Dated Content each Release offers Change validity, which corrects its dates in one request, wherever the Blueprint's effective Editing Mode allows a Direct Edit), Revisions (with Compare), References, Change Sets and Filing. The header's one Save sets Filing (Collections, assignees) and never writes a Revision. Its rail offers Edit and Start editing as the Editing Mode in effect for the Content's Blueprint says (ADR 0022: Edit alone, both, or Start editing alone; Dated Content gets Start editing until a Direct Edit takes a date), Duplicate, Repeal or Withdraw, Block or Unblock, and Archive (only once nothing is In Force today or later) or Unarchive; Start editing and Duplicate end in `onOpenBranch`; Edit opens the caller's Direct Edit, or finds it open, and ends in `onOpenChangeSet`, so Edit is offered only with it. While the caller has an open Direct Edit of the Content the page says so and leads back to it. The Change Sets tab names each open Direct Edit's editor, and `onOpenContent` and `onOpenChangeSet` open the rows of its References and Change Sets tabs. It needs `@knkcs/fieldkit` as a peer.
11
+ - `ChangeSetsIndex`: the workspace's Change Sets, narrowed by kind (Direct Edits hidden until asked for), state and assignee, created in a Modal; `onOpenChangeSet(id)` opens one, a new one too.
12
+ - `ChangeSetPage`: one Change Set, its General tab (name and Source while it is open, status label, assignees, dependencies) saved from the header's one Save; its Contents tab, whose rows call `onOpenBranch({ changeSetId, contentId, branchId })`; its Releases tab (Planned Releases, proposals read-only, the Releases they cover) and its Checks tab (each Required Check's latest result, stale or not, and Record). Its rail holds Publish, Update from Main and Discard, and an About section with the readiness tile (`ValidatePublish` at the current heads), the status label and the Source; Publish opens a review that publishes exactly the heads it shows. `onPublished` and `onDiscarded` tell the host. Its leave guard needs a data router (react-router's `useBlocker`). A Change Set that is a Direct Edit shows as `DirectEditPage` instead.
13
+ - `DirectEditPage`: a Direct Edit (ADR 0022), as its editor sees it: the Content's fields in the Branch editor's form with the header's one Save, Publish and Discard in the rail (Publish asks for unsaved edits to be saved first, and lists every blocker of a refused Publish; Discard asks first), and no Change Set, Chain, Branch or Release tab. `onPublished` and `onDiscarded` tell the host. `ChangeSetPage` renders it for a Direct Edit, so a host needs no route of its own.
13
14
  - `BranchEditor`: one Content inside a Change Set at one Branch (`changeSetId`, `contentId`, `branchId`), its header tabs chosen by `?tab=`. It reads the Content at that Branch's own head (`GetContent` with `branch_id`); only the Chain's last Branch is edited, and an earlier one shows read-only. The Content tab renders the Blueprint Release's form through fieldkit's `SpecForm`, with knkeditor for rich text, both loaded lazily; reference fields search contenthub, media fields use the `MediaSource` port. The forms above the tabs are the drafts (fieldkit's "Consumer-owned Save across unmounting tabs"): the Content's, and the Release tab's Planned Release (Validity Period, supersedes, Release Spec values; the Change Set's proposals beside it, read-only). The header's one Save persists every changed tab: one `SaveRevision` on the head the editor loaded, with a field mask of what changed, then `PlanRelease` or `UpdatePlannedRelease`; a refused save sends no plan, and a refused plan leaves only the Release tab changed. A moved head is refused with Reload, never merged; the leave guard needs a data router. Compare shows the head against its fork or Main, References its outgoing edges and outdated pins. The rail offers Update from Main and the merge resolution it may leave, Compare to the fork, Preview, Append a Branch (`onOpenBranch` opens it) and Drop Chain (`onDeleted` leaves), and shows the fork Release, the effective date, the Chain position and the head. It needs `@knkcs/fieldkit` (0.18), `@knkcms/knkeditor-editor` and `@knkcms/knkeditor-vocabulary` (GitHub Packages) as peers.
14
15
  - `MediaSource`, the port a host fills with mediahub, in fieldkit's media-adapter shape (ADR 0020). Without it a media field offers nothing to pick.
15
16
  - `StatusSource`, the port a host fills with statushub for Change Set status labels (ADR 0020). Without it the screens show the bare slug and offer no status picker. A status it answers with `isDefault` (statushub's default) is what a Change Set without a status reads as (ADR 0014 D9); a host should pass it on.
@@ -1,4 +1,4 @@
1
- import { r as e } from "./content-library-DoiD_FOJ.js";
1
+ import { r as e } from "./content-library-DFIk09bD.js";
2
2
  import { useMemo as t } from "react";
3
3
  import { jsx as n } from "react/jsx-runtime";
4
4
  import { FieldKitProvider as r, SpecForm as i } from "@knkcs/fieldkit/renderer";
@@ -83,4 +83,4 @@ function d({ client: e, changeSetId: a, branchId: o, fields: s, parts: c, labels
83
83
  //#endregion
84
84
  export { d as default };
85
85
 
86
- //# sourceMappingURL=branch-content-fields-CiAVF2F3.js.map
86
+ //# sourceMappingURL=branch-content-fields-BQG6U0yl.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"branch-content-fields-CiAVF2F3.js","names":["contentLabel","MAX_PAGE","itemOf","data","id","blueprintId","branchAdapters","wanted","page","page_size","list","client","query","blueprintIds","excluded","excludeIds","seen","items","c","start","total","ids","detail","contentId","mediaSource","file","filter","plugins","builtInFieldTypes","p","knkRichTextPlugin","BranchContentFields","adapters","useMemo","changeSetId","FieldKitProvider","SpecForm","fields","branchId"],"sources":["../src/screens/branch-adapters.ts","../src/screens/branch-content-fields.tsx"],"sourcesContent":["// fieldkit's adapters for the Branch editor (ADR 0020): references from\n// contenthub's own reads, media through the host's MediaSource port. Types\n// only from fieldkit, so this module adds nothing to the root bundle.\nimport type { FieldKitAdapters, PinTarget, ReferenceItem } from \"@knkcs/fieldkit/renderer\";\nimport type { ContenthubClient } from \"../client/contenthub-client\";\nimport type { MediaSource } from \"../types\";\nimport { contentLabel } from \"./content-library\";\n\n/** The most rows one search asks for: ListContents' own cap. */\nconst MAX_PAGE = 200;\n\n/** What a picker shows for a Content: its label, or its id when its data has none. */\nfunction itemOf(id: string, blueprintId: string, data: Record<string, unknown>): ReferenceItem {\n\treturn { id, display_name: contentLabel(data) || id, blueprint_id: blueprintId };\n}\n\n/**\n * The adapters a Branch's form gets: `reference` searches contenthub's\n * Editor Search on Main (which reads open Branch heads too, so Content born\n * in an open Change Set is found), names a referenced Content from its read\n * and offers its Releases to pin; `media` is the host's MediaSource, absent\n * when the host has none, which leaves a media field nothing to pick.\n */\nexport function branchAdapters({\n\tclient,\n\tchangeSetId,\n\tmediaSource,\n}: {\n\tclient: ContenthubClient;\n\tchangeSetId: string;\n\tmediaSource?: MediaSource;\n}): FieldKitAdapters {\n\treturn {\n\t\treference: {\n\t\t\tasync search({ query, blueprintIds, excludeIds, page, page_size }) {\n\t\t\t\tconst wanted = Math.min(MAX_PAGE, Math.max(1, page) * Math.max(1, page_size));\n\t\t\t\t// Every target Blueprint at once: the service narrows the page, so\n\t\t\t\t// it holds only what the Field may point at, and paging stays exact.\n\t\t\t\tconst list = await client.listContents({\n\t\t\t\t\tsearch: query.trim() || undefined,\n\t\t\t\t\tblueprintIds: blueprintIds.length > 0 ? blueprintIds : undefined,\n\t\t\t\t\tpageSize: wanted,\n\t\t\t\t});\n\t\t\t\tconst excluded = new Set(excludeIds ?? []);\n\t\t\t\tconst seen = new Set<string>();\n\t\t\t\tconst items: ReferenceItem[] = [];\n\t\t\t\t// A search lists a Content once per head that matched: one row each.\n\t\t\t\tfor (const c of list.contents) {\n\t\t\t\t\tif (seen.has(c.id) || excluded.has(c.id)) continue;\n\t\t\t\t\tseen.add(c.id);\n\t\t\t\t\titems.push(itemOf(c.id, c.blueprintId, c.data));\n\t\t\t\t}\n\t\t\t\tconst start = (Math.max(1, page) - 1) * page_size;\n\t\t\t\t// The total is exact on the last page; otherwise there is at least one more.\n\t\t\t\tconst total = list.nextPageToken ? items.length + 1 : items.length;\n\t\t\t\treturn { items: items.slice(start, start + page_size), total };\n\t\t\t},\n\t\t\tasync fetch(ids) {\n\t\t\t\treturn Promise.all(\n\t\t\t\t\tids.map(async (id) => {\n\t\t\t\t\t\t// Published Content reads on Main; Content born in this\n\t\t\t\t\t\t// Change Set exists only there.\n\t\t\t\t\t\tconst detail = await client\n\t\t\t\t\t\t\t.getContent({ id })\n\t\t\t\t\t\t\t.catch(() => client.getContent({ id, changeSetId }))\n\t\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\t\treturn detail\n\t\t\t\t\t\t\t? itemOf(id, detail.content.blueprintId, detail.content.data)\n\t\t\t\t\t\t\t: { id, display_name: id };\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t},\n\t\t\t// A Pin names a Release (the one pinning kind fieldkit has), newest first.\n\t\t\tasync listPinTargets(contentId) {\n\t\t\t\tconst timeline = await client.getTimeline(contentId);\n\t\t\t\treturn [...timeline.entries].reverse().map(\n\t\t\t\t\t(e): PinTarget => ({\n\t\t\t\t\t\tid: e.releaseId,\n\t\t\t\t\t\tlabel: `#${e.ordinal}`,\n\t\t\t\t\t\tdescription: e.validityPeriod\n\t\t\t\t\t\t\t? `${e.validityPeriod.firstDay} – ${e.validityPeriod.lastDay || \"…\"}`\n\t\t\t\t\t\t\t: e.publishTime.slice(0, 10),\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t},\n\t\t},\n\t\tmedia: mediaSource\n\t\t\t? {\n\t\t\t\t\tupload: (file) => mediaSource.upload(file),\n\t\t\t\t\tbrowse: (filter) => mediaSource.browse(filter),\n\t\t\t\t}\n\t\t\t: undefined,\n\t};\n}\n","// fieldkit's renderer with knkeditor for rich text, behind a lazily loaded\n// module: both load the first time a Branch's Content tab is shown, never\n// with the rest of the package (spec #56; branch-editor-bundle.test.ts).\nimport { FieldKitProvider, SpecForm, type SpecFormLabels } from \"@knkcs/fieldkit/renderer\";\nimport { knkRichTextPlugin } from \"@knkcs/fieldkit/rich-text\";\nimport { builtInFieldTypes, type FieldTypePlugin, type Schema } from \"@knkcs/fieldkit/schema\";\nimport { useMemo } from \"react\";\nimport type { ContenthubClient } from \"../client/contenthub-client\";\nimport type { MediaSource, ResolvedField } from \"../types\";\nimport { branchAdapters } from \"./branch-adapters\";\n\n// rich_text through knkeditor (fieldkit ADR-0026); every other type as built in.\nconst plugins: FieldTypePlugin[] = builtInFieldTypes.map((p) =>\n\tp.id === \"rich_text\" ? (knkRichTextPlugin as FieldTypePlugin) : p,\n);\n\nexport interface BranchContentFieldsProps {\n\tclient: ContenthubClient;\n\tchangeSetId: string;\n\t/** The record the form holds, so SpecForm's remembered section is this Branch's. */\n\tbranchId: string;\n\tfields: ResolvedField[];\n\t/** The Resolved Release's parts: the Text Types its rich_text fields pin. */\n\tparts: Record<string, Record<string, unknown>>;\n\tlabels: SpecFormLabels;\n\treadOnly: boolean;\n\tmediaSource?: MediaSource;\n}\n\n/**\n * The Branch's data as the Blueprint Release lays it out, editable: its\n * sections, orientation and cards as fieldkit draws them, over the form the\n * editor holds above its tabs (fieldkit's spec-form.mdx, \"A Consumer-owned\n * Save across unmounting tabs\"). SpecForm keeps no draft of its own.\n */\nexport default function BranchContentFields({\n\tclient,\n\tchangeSetId,\n\tbranchId,\n\tfields,\n\tparts,\n\tlabels,\n\treadOnly,\n\tmediaSource,\n}: BranchContentFieldsProps) {\n\tconst adapters = useMemo(\n\t\t() => branchAdapters({ client, changeSetId, mediaSource }),\n\t\t[client, changeSetId, mediaSource],\n\t);\n\treturn (\n\t\t<FieldKitProvider plugins={plugins} adapters={adapters} parts={parts}>\n\t\t\t<SpecForm\n\t\t\t\tschema={fields as unknown as Schema}\n\t\t\t\tlabels={labels}\n\t\t\t\treadOnly={readOnly}\n\t\t\t\trecordKey={branchId}\n\t\t\t/>\n\t\t</FieldKitProvider>\n\t);\n}\n"],"mappings":";;;;;;;AASA,IAAMC,IAAW;AAGjB,SAASC,EAAO,GAAY,GAAqB,GAA8C;CAC9F,OAAO;EAAE;EAAI,cAAcF,EAAaG,CAAI,KAAKC;EAAI,cAAcC;CAAY;AAChF;AASA,SAAgBC,EAAe,EAC9B,WACA,gBACA,kBAKoB;CACpB,OAAO;EACN,WAAW;GACV,MAAM,OAAO,EAAE,UAAO,iBAAc,eAAY,SAAM,gBAAa;IAClE,IAAMC,IAAS,KAAK,IAAIN,GAAU,KAAK,IAAI,GAAGO,CAAI,IAAI,KAAK,IAAI,GAAGC,CAAS,CAAC,GAGtEC,IAAO,MAAMC,EAAO,aAAa;KACtC,QAAQC,EAAM,KAAK,KAAK,KAAA;KACxB,cAAcC,EAAa,SAAS,IAAIA,IAAe,KAAA;KACvD,UAAUN;IACX,CAAC,GACKO,IAAW,IAAI,IAAIC,KAAc,CAAC,CAAC,GACnCC,oBAAO,IAAI,IAAY,GACvBC,IAAyB,CAAC;IAEhC,KAAK,IAAMC,KAAKR,EAAK,UACpB,AAAIM,EAAK,IAAIE,EAAE,EAAE,KAAKJ,EAAS,IAAII,EAAE,EAAE,MACvCF,EAAK,IAAIE,EAAE,EAAE,GACbD,EAAM,KAAKf,EAAOgB,EAAE,IAAIA,EAAE,aAAaA,EAAE,IAAI,CAAC;IAE/C,IAAMC,KAAS,KAAK,IAAI,GAAGX,CAAI,IAAI,KAAKC,GAElCW,IAAQV,EAAK,gBAAgBO,EAAM,SAAS,IAAIA,EAAM;IAC5D,OAAO;KAAE,OAAOA,EAAM,MAAME,GAAOA,IAAQV,CAAS;KAAG;IAAM;GAC9D;GACA,MAAM,MAAM,GAAK;IAChB,OAAO,QAAQ,IACdY,EAAI,IAAI,OAAO,MAAO;KAGrB,IAAMC,IAAS,MAAMX,EACnB,WAAW,EAAE,MAAG,CAAC,CAAC,CAClB,YAAYA,EAAO,WAAW;MAAE;MAAI;KAAY,CAAC,CAAC,CAAC,CACnD,YAAY,KAAA,CAAS;KACvB,OAAOW,IACJpB,EAAOE,GAAIkB,EAAO,QAAQ,aAAaA,EAAO,QAAQ,IAAI,IAC1D;MAAE;MAAI,cAAclB;KAAG;IAC3B,CAAC,CACF;GACD;GAEA,MAAM,eAAe,GAAW;IAE/B,OAAO,CAAC,IAAG,MADYO,EAAO,YAAYY,CAAS,EAAA,CAC/B,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,KACrC,OAAkB;KAClB,IAAI,EAAE;KACN,OAAO,IAAI,EAAE;KACb,aAAa,EAAE,iBACZ,GAAG,EAAE,eAAe,SAAS,KAAK,EAAE,eAAe,WAAW,QAC9D,EAAE,YAAY,MAAM,GAAG,EAAE;IAC7B,EACD;GACD;EACD;EACA,OAAOC,IACJ;GACA,SAAS,MAASA,EAAY,OAAOC,CAAI;GACzC,SAAS,MAAWD,EAAY,OAAOE,CAAM;EAC9C,IACC,KAAA;CACJ;AACD;;;ACjFA,IAAMC,IAA6BC,EAAkB,KAAK,MACzDC,EAAE,OAAO,cAAeC,IAAwCD,CACjE;AAqBA,SAAwBE,EAAoB,EAC3C,WACA,gBACA,aACA,WACA,UACA,WACA,aACA,kBAC4B;CAC5B,IAAMC,IAAWC,QACV3B,EAAe;EAAE;EAAQ;EAAa;CAAY,CAAC,GACzD;EAACK;EAAQuB;EAAaV;CAAW,CAClC;CACA,OACC,kBAACW,GAAD;EAA2B;EAAmB;EAAiB;EAC9D,UAAA,kBAACC,GAAD;GACC,QAAQC;GACA;GACE;GACV,WAAWC;EACX,CAAA;CACgB,CAAA;AAEpB"}
1
+ {"version":3,"file":"branch-content-fields-BQG6U0yl.js","names":["contentLabel","MAX_PAGE","itemOf","data","id","blueprintId","branchAdapters","wanted","page","page_size","list","client","query","blueprintIds","excluded","excludeIds","seen","items","c","start","total","ids","detail","contentId","mediaSource","file","filter","plugins","builtInFieldTypes","p","knkRichTextPlugin","BranchContentFields","adapters","useMemo","changeSetId","FieldKitProvider","SpecForm","fields","branchId"],"sources":["../src/screens/branch-adapters.ts","../src/screens/branch-content-fields.tsx"],"sourcesContent":["// fieldkit's adapters for the Branch editor (ADR 0020): references from\n// contenthub's own reads, media through the host's MediaSource port. Types\n// only from fieldkit, so this module adds nothing to the root bundle.\nimport type { FieldKitAdapters, PinTarget, ReferenceItem } from \"@knkcs/fieldkit/renderer\";\nimport type { ContenthubClient } from \"../client/contenthub-client\";\nimport type { MediaSource } from \"../types\";\nimport { contentLabel } from \"./content-library\";\n\n/** The most rows one search asks for: ListContents' own cap. */\nconst MAX_PAGE = 200;\n\n/** What a picker shows for a Content: its label, or its id when its data has none. */\nfunction itemOf(id: string, blueprintId: string, data: Record<string, unknown>): ReferenceItem {\n\treturn { id, display_name: contentLabel(data) || id, blueprint_id: blueprintId };\n}\n\n/**\n * The adapters a Branch's form gets: `reference` searches contenthub's\n * Editor Search on Main (which reads open Branch heads too, so Content born\n * in an open Change Set is found), names a referenced Content from its read\n * and offers its Releases to pin; `media` is the host's MediaSource, absent\n * when the host has none, which leaves a media field nothing to pick.\n */\nexport function branchAdapters({\n\tclient,\n\tchangeSetId,\n\tmediaSource,\n}: {\n\tclient: ContenthubClient;\n\tchangeSetId: string;\n\tmediaSource?: MediaSource;\n}): FieldKitAdapters {\n\treturn {\n\t\treference: {\n\t\t\tasync search({ query, blueprintIds, excludeIds, page, page_size }) {\n\t\t\t\tconst wanted = Math.min(MAX_PAGE, Math.max(1, page) * Math.max(1, page_size));\n\t\t\t\t// Every target Blueprint at once: the service narrows the page, so\n\t\t\t\t// it holds only what the Field may point at, and paging stays exact.\n\t\t\t\tconst list = await client.listContents({\n\t\t\t\t\tsearch: query.trim() || undefined,\n\t\t\t\t\tblueprintIds: blueprintIds.length > 0 ? blueprintIds : undefined,\n\t\t\t\t\tpageSize: wanted,\n\t\t\t\t});\n\t\t\t\tconst excluded = new Set(excludeIds ?? []);\n\t\t\t\tconst seen = new Set<string>();\n\t\t\t\tconst items: ReferenceItem[] = [];\n\t\t\t\t// A search lists a Content once per head that matched: one row each.\n\t\t\t\tfor (const c of list.contents) {\n\t\t\t\t\tif (seen.has(c.id) || excluded.has(c.id)) continue;\n\t\t\t\t\tseen.add(c.id);\n\t\t\t\t\titems.push(itemOf(c.id, c.blueprintId, c.data));\n\t\t\t\t}\n\t\t\t\tconst start = (Math.max(1, page) - 1) * page_size;\n\t\t\t\t// The total is exact on the last page; otherwise there is at least one more.\n\t\t\t\tconst total = list.nextPageToken ? items.length + 1 : items.length;\n\t\t\t\treturn { items: items.slice(start, start + page_size), total };\n\t\t\t},\n\t\t\tasync fetch(ids) {\n\t\t\t\treturn Promise.all(\n\t\t\t\t\tids.map(async (id) => {\n\t\t\t\t\t\t// Published Content reads on Main; Content born in this\n\t\t\t\t\t\t// Change Set exists only there.\n\t\t\t\t\t\tconst detail = await client\n\t\t\t\t\t\t\t.getContent({ id })\n\t\t\t\t\t\t\t.catch(() => client.getContent({ id, changeSetId }))\n\t\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\t\treturn detail\n\t\t\t\t\t\t\t? itemOf(id, detail.content.blueprintId, detail.content.data)\n\t\t\t\t\t\t\t: { id, display_name: id };\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t},\n\t\t\t// A Pin names a Release (the one pinning kind fieldkit has), newest first.\n\t\t\tasync listPinTargets(contentId) {\n\t\t\t\tconst timeline = await client.getTimeline(contentId);\n\t\t\t\treturn [...timeline.entries].reverse().map(\n\t\t\t\t\t(e): PinTarget => ({\n\t\t\t\t\t\tid: e.releaseId,\n\t\t\t\t\t\tlabel: `#${e.ordinal}`,\n\t\t\t\t\t\tdescription: e.validityPeriod\n\t\t\t\t\t\t\t? `${e.validityPeriod.firstDay} – ${e.validityPeriod.lastDay || \"…\"}`\n\t\t\t\t\t\t\t: e.publishTime.slice(0, 10),\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t},\n\t\t},\n\t\tmedia: mediaSource\n\t\t\t? {\n\t\t\t\t\tupload: (file) => mediaSource.upload(file),\n\t\t\t\t\tbrowse: (filter) => mediaSource.browse(filter),\n\t\t\t\t}\n\t\t\t: undefined,\n\t};\n}\n","// fieldkit's renderer with knkeditor for rich text, behind a lazily loaded\n// module: both load the first time a Branch's Content tab is shown, never\n// with the rest of the package (spec #56; branch-editor-bundle.test.ts).\nimport { FieldKitProvider, SpecForm, type SpecFormLabels } from \"@knkcs/fieldkit/renderer\";\nimport { knkRichTextPlugin } from \"@knkcs/fieldkit/rich-text\";\nimport { builtInFieldTypes, type FieldTypePlugin, type Schema } from \"@knkcs/fieldkit/schema\";\nimport { useMemo } from \"react\";\nimport type { ContenthubClient } from \"../client/contenthub-client\";\nimport type { MediaSource, ResolvedField } from \"../types\";\nimport { branchAdapters } from \"./branch-adapters\";\n\n// rich_text through knkeditor (fieldkit ADR-0026); every other type as built in.\nconst plugins: FieldTypePlugin[] = builtInFieldTypes.map((p) =>\n\tp.id === \"rich_text\" ? (knkRichTextPlugin as FieldTypePlugin) : p,\n);\n\nexport interface BranchContentFieldsProps {\n\tclient: ContenthubClient;\n\tchangeSetId: string;\n\t/** The record the form holds, so SpecForm's remembered section is this Branch's. */\n\tbranchId: string;\n\tfields: ResolvedField[];\n\t/** The Resolved Release's parts: the Text Types its rich_text fields pin. */\n\tparts: Record<string, Record<string, unknown>>;\n\tlabels: SpecFormLabels;\n\treadOnly: boolean;\n\tmediaSource?: MediaSource;\n}\n\n/**\n * The Branch's data as the Blueprint Release lays it out, editable: its\n * sections, orientation and cards as fieldkit draws them, over the form the\n * editor holds above its tabs (fieldkit's spec-form.mdx, \"A Consumer-owned\n * Save across unmounting tabs\"). SpecForm keeps no draft of its own.\n */\nexport default function BranchContentFields({\n\tclient,\n\tchangeSetId,\n\tbranchId,\n\tfields,\n\tparts,\n\tlabels,\n\treadOnly,\n\tmediaSource,\n}: BranchContentFieldsProps) {\n\tconst adapters = useMemo(\n\t\t() => branchAdapters({ client, changeSetId, mediaSource }),\n\t\t[client, changeSetId, mediaSource],\n\t);\n\treturn (\n\t\t<FieldKitProvider plugins={plugins} adapters={adapters} parts={parts}>\n\t\t\t<SpecForm\n\t\t\t\tschema={fields as unknown as Schema}\n\t\t\t\tlabels={labels}\n\t\t\t\treadOnly={readOnly}\n\t\t\t\trecordKey={branchId}\n\t\t\t/>\n\t\t</FieldKitProvider>\n\t);\n}\n"],"mappings":";;;;;;;AASA,IAAMC,IAAW;AAGjB,SAASC,EAAO,GAAY,GAAqB,GAA8C;CAC9F,OAAO;EAAE;EAAI,cAAcF,EAAaG,CAAI,KAAKC;EAAI,cAAcC;CAAY;AAChF;AASA,SAAgBC,EAAe,EAC9B,WACA,gBACA,kBAKoB;CACpB,OAAO;EACN,WAAW;GACV,MAAM,OAAO,EAAE,UAAO,iBAAc,eAAY,SAAM,gBAAa;IAClE,IAAMC,IAAS,KAAK,IAAIN,GAAU,KAAK,IAAI,GAAGO,CAAI,IAAI,KAAK,IAAI,GAAGC,CAAS,CAAC,GAGtEC,IAAO,MAAMC,EAAO,aAAa;KACtC,QAAQC,EAAM,KAAK,KAAK,KAAA;KACxB,cAAcC,EAAa,SAAS,IAAIA,IAAe,KAAA;KACvD,UAAUN;IACX,CAAC,GACKO,IAAW,IAAI,IAAIC,KAAc,CAAC,CAAC,GACnCC,oBAAO,IAAI,IAAY,GACvBC,IAAyB,CAAC;IAEhC,KAAK,IAAMC,KAAKR,EAAK,UACpB,AAAIM,EAAK,IAAIE,EAAE,EAAE,KAAKJ,EAAS,IAAII,EAAE,EAAE,MACvCF,EAAK,IAAIE,EAAE,EAAE,GACbD,EAAM,KAAKf,EAAOgB,EAAE,IAAIA,EAAE,aAAaA,EAAE,IAAI,CAAC;IAE/C,IAAMC,KAAS,KAAK,IAAI,GAAGX,CAAI,IAAI,KAAKC,GAElCW,IAAQV,EAAK,gBAAgBO,EAAM,SAAS,IAAIA,EAAM;IAC5D,OAAO;KAAE,OAAOA,EAAM,MAAME,GAAOA,IAAQV,CAAS;KAAG;IAAM;GAC9D;GACA,MAAM,MAAM,GAAK;IAChB,OAAO,QAAQ,IACdY,EAAI,IAAI,OAAO,MAAO;KAGrB,IAAMC,IAAS,MAAMX,EACnB,WAAW,EAAE,MAAG,CAAC,CAAC,CAClB,YAAYA,EAAO,WAAW;MAAE;MAAI;KAAY,CAAC,CAAC,CAAC,CACnD,YAAY,KAAA,CAAS;KACvB,OAAOW,IACJpB,EAAOE,GAAIkB,EAAO,QAAQ,aAAaA,EAAO,QAAQ,IAAI,IAC1D;MAAE;MAAI,cAAclB;KAAG;IAC3B,CAAC,CACF;GACD;GAEA,MAAM,eAAe,GAAW;IAE/B,OAAO,CAAC,IAAG,MADYO,EAAO,YAAYY,CAAS,EAAA,CAC/B,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,KACrC,OAAkB;KAClB,IAAI,EAAE;KACN,OAAO,IAAI,EAAE;KACb,aAAa,EAAE,iBACZ,GAAG,EAAE,eAAe,SAAS,KAAK,EAAE,eAAe,WAAW,QAC9D,EAAE,YAAY,MAAM,GAAG,EAAE;IAC7B,EACD;GACD;EACD;EACA,OAAOC,IACJ;GACA,SAAS,MAASA,EAAY,OAAOC,CAAI;GACzC,SAAS,MAAWD,EAAY,OAAOE,CAAM;EAC9C,IACC,KAAA;CACJ;AACD;;;ACjFA,IAAMC,IAA6BC,EAAkB,KAAK,MACzDC,EAAE,OAAO,cAAeC,IAAwCD,CACjE;AAqBA,SAAwBE,EAAoB,EAC3C,WACA,gBACA,aACA,WACA,UACA,WACA,aACA,kBAC4B;CAC5B,IAAMC,IAAWC,QACV3B,EAAe;EAAE;EAAQ;EAAa;CAAY,CAAC,GACzD;EAACK;EAAQuB;EAAaV;CAAW,CAClC;CACA,OACC,kBAACW,GAAD;EAA2B;EAAmB;EAAiB;EAC9D,UAAA,kBAACC,GAAD;GACC,QAAQC;GACA;GACE;GACV,WAAWC;EACX,CAAA;CACgB,CAAA;AAEpB"}
@@ -1,4 +1,4 @@
1
- import { C as e, S as t, h as n, y as r } from "./heads-dBmqNcOB.js";
1
+ import { T as e, h as t, w as n, y as r } from "./heads-DqHmAW0n.js";
2
2
  import { useCallback as i, useEffect as a, useMemo as o, useRef as s, useState as c } from "react";
3
3
  import { Fragment as l, jsx as u, jsxs as d } from "react/jsx-runtime";
4
4
  import { Button as f, EmptyState as p } from "@knkcs/anker/atoms";
@@ -45,6 +45,12 @@ var W = {
45
45
  "recorders",
46
46
  e
47
47
  ],
48
+ editingSettings: () => [...W.all, "editing-settings"],
49
+ effectiveEditingSettings: (e) => [
50
+ ...W.editingSettings(),
51
+ "effective",
52
+ e
53
+ ],
48
54
  collections: () => [...W.all, "collections"],
49
55
  collectionGrants: (e) => [
50
56
  ...W.collections(),
@@ -127,10 +133,11 @@ var W = {
127
133
  settings: {
128
134
  section: "Einstellungen",
129
135
  title: "Inhaltseinstellungen",
130
- subtitle: "Was die Veröffentlichung verlangt, warum Inhalte gesperrt werden und wie Inhalte abgelegt werden.",
136
+ subtitle: "Was die Veröffentlichung verlangt, warum Inhalte gesperrt werden, wie Inhalte geändert werden dürfen und wie sie abgelegt werden.",
131
137
  tabs: {
132
138
  requiredChecks: "Pflichtprüfungen",
133
139
  blockReasons: "Sperrgründe",
140
+ editing: "Bearbeitung",
134
141
  collections: "Sammlungen"
135
142
  }
136
143
  },
@@ -169,6 +176,63 @@ var W = {
169
176
  failed: "Der Sperrgrund konnte nicht entfernt werden. Versuche es erneut."
170
177
  }
171
178
  },
179
+ editing: {
180
+ mode: {
181
+ direct_edits: "Nur Direktbearbeitung",
182
+ change_sets: "Nur Change Sets",
183
+ both: "Direktbearbeitung und Change Sets"
184
+ },
185
+ block: {
186
+ on: "An",
187
+ off: "Aus"
188
+ },
189
+ workspace: {
190
+ title: "Vorgaben des Arbeitsbereichs",
191
+ description: "Wie Inhalte geändert werden dürfen: in einer Direktbearbeitung, vom Inhalt aus geöffnet, in einem Change Set oder beides, und ob eine Direktbearbeitung andere Direktbearbeitungen mit einer Bearbeitungssperre fernhält. Jeder Blueprint ohne Abweichung folgt diesen Vorgaben. Eine Änderung gilt sofort und lässt offene Arbeit, wie sie ist.",
192
+ edit: "Vorgaben ändern"
193
+ },
194
+ columns: {
195
+ blueprint: "Blueprint",
196
+ mode: "Bearbeitungsmodus",
197
+ block: "Bearbeitungssperre",
198
+ actions: "Aktionen"
199
+ },
200
+ fromWorkspace: "{{value}} (Vorgabe des Arbeitsbereichs)",
201
+ createOverride: "Neue Abweichung",
202
+ editOverride: "Bearbeiten",
203
+ removeOverride: "Entfernen",
204
+ count_one: "{{count}} Blueprint-Abweichung",
205
+ count_other: "{{count}} Blueprint-Abweichungen",
206
+ empty: "Noch keine Blueprint-Abweichungen",
207
+ emptyHint: "Jeder Blueprint folgt den Vorgaben des Arbeitsbereichs. Weichen Sie für einen Blueprint ab, dessen Inhalte anders geändert werden, etwa Gesetze nur in Change Sets.",
208
+ loadError: "Die Bearbeitungseinstellungen konnten nicht geladen werden.",
209
+ form: {
210
+ workspaceTitle: "Vorgaben des Arbeitsbereichs",
211
+ createOverrideTitle: "Neue Blueprint-Abweichung",
212
+ editOverrideTitle: "Blueprint-Abweichung bearbeiten",
213
+ accessor: "Blueprint-Accessor",
214
+ accessorHelp: "Der Blueprint, für dessen Inhalte dies gilt, als Accessor.",
215
+ accessorRequired: "Geben Sie einen Blueprint-Accessor ein.",
216
+ accessorTooLong: "Höchstens {{max}} Zeichen.",
217
+ accessorTaken: "Für „{{name}}“ gibt es bereits eine Abweichung: Bearbeiten Sie diese.",
218
+ mode: "Bearbeitungsmodus",
219
+ modeHelp: "Nur „Nur Change Sets“ wird durchgesetzt: Eine Direktbearbeitung solcher Inhalte wird abgelehnt. Importe, Abläufe und Blueprint-Upgrades nutzen immer Change Sets.",
220
+ modeRequired: "Wählen Sie einen Bearbeitungsmodus.",
221
+ block: "Bearbeitungssperre",
222
+ blockHelp: "Ob das Öffnen einer Direktbearbeitung die Direktbearbeitungen anderer fernhält, bis sie veröffentlicht oder verworfen ist. Change Sets kommen weiter durch.",
223
+ follow: "Vorgabe des Arbeitsbereichs ({{value}})",
224
+ overridesNothing: "Weichen Sie beim Bearbeitungsmodus, bei der Bearbeitungssperre oder bei beidem ab. Um in beidem dem Arbeitsbereich zu folgen, entfernen Sie die Abweichung.",
225
+ save: "Speichern",
226
+ denied: "Nur ein Admin des Arbeitsbereichs ändert die Bearbeitungseinstellungen.",
227
+ failed: "Die Bearbeitungseinstellungen konnten nicht gespeichert werden. Versuchen Sie es erneut."
228
+ },
229
+ removeDialog: {
230
+ title: "Abweichung entfernen",
231
+ message: "Die Abweichung für „{{name}}“ entfernen? Ihre Inhalte folgen dann wieder den Vorgaben des Arbeitsbereichs.",
232
+ confirm: "Entfernen",
233
+ failed: "Die Abweichung konnte nicht entfernt werden. Versuchen Sie es erneut."
234
+ }
235
+ },
172
236
  requiredChecks: {
173
237
  create: "Neue Pflichtprüfung",
174
238
  columns: {
@@ -367,7 +431,9 @@ var W = {
367
431
  anyState: "Jeder Zustand",
368
432
  assignee: "Zuständig",
369
433
  anyone: "Alle",
370
- me: "Mir zugewiesen"
434
+ me: "Mir zugewiesen",
435
+ kind: "Art",
436
+ anyKind: "Change Sets und Direktbearbeitungen"
371
437
  },
372
438
  count_one: "{{count}} Change Set",
373
439
  count_other: "{{count}} Change Sets",
@@ -390,7 +456,11 @@ var W = {
390
456
  failed: "Das Change Set konnte nicht angelegt werden. Versuche es erneut.",
391
457
  createSubmit: "Anlegen"
392
458
  },
393
- more: "Mehr anzeigen"
459
+ more: "Mehr anzeigen",
460
+ kind: {
461
+ change_set: "Change Sets",
462
+ direct_edit: "Direktbearbeitungen"
463
+ }
394
464
  },
395
465
  branch: {
396
466
  loading: "Inhalt wird geladen…",
@@ -642,7 +712,7 @@ var W = {
642
712
  archived: "Archiviert am"
643
713
  },
644
714
  actions: {
645
- startEditing: "Bearbeiten",
715
+ startEditing: "In Change Set bearbeiten",
646
716
  openBranch: "Branch öffnen",
647
717
  duplicate: "Duplizieren",
648
718
  repeal: "Aufheben",
@@ -650,7 +720,8 @@ var W = {
650
720
  block: "Sperren",
651
721
  unblock: "Entsperren",
652
722
  archive: "Archivieren",
653
- unarchive: "Aus dem Archiv holen"
723
+ unarchive: "Aus dem Archiv holen",
724
+ edit: "Bearbeiten"
654
725
  },
655
726
  data: {
656
727
  readOnMain: "Stand von Main, Revision {{revision}}. Nur lesbar: Zum Ändern in einem Branch auf „Bearbeiten“ klicken.",
@@ -670,7 +741,8 @@ var W = {
670
741
  published: "Veröffentlicht",
671
742
  inForce: "Heute",
672
743
  changeSet: "Change Set",
673
- proposed: "Herkunft"
744
+ proposed: "Herkunft",
745
+ actions: "Aktionen"
674
746
  },
675
747
  kind: {
676
748
  revision: "Revision",
@@ -689,7 +761,8 @@ var W = {
689
761
  noPlanned: "Kein offenes Change Set plant ein Release davon",
690
762
  proposed: "Vorgeschlagen",
691
763
  planned: "Geplant",
692
- loadError: "Die Zeitleiste konnte nicht geladen werden."
764
+ loadError: "Die Zeitleiste konnte nicht geladen werden.",
765
+ changeValidity: "Geltung ändern"
693
766
  },
694
767
  revisions: {
695
768
  columns: {
@@ -773,7 +846,8 @@ var W = {
773
846
  opened: "Geöffnet",
774
847
  empty: "Kein offenes Change Set ändert ihn",
775
848
  emptyHint: "„Bearbeiten“ öffnet eines oder nimmt ihn in ein offenes auf.",
776
- loadError: "Die Change Sets konnten nicht geladen werden."
849
+ loadError: "Die Change Sets konnten nicht geladen werden.",
850
+ directEditBy: "Direktbearbeitung von {{who}}"
777
851
  },
778
852
  filing: {
779
853
  title: "Ablage",
@@ -841,17 +915,69 @@ var W = {
841
915
  title: "Diesen Content archivieren?",
842
916
  message: "Archivierter Content verschwindet aus der Bibliothek, jeder Auswahl und der Bearbeitung. Seine Releases bleiben lesbar, und „Aus dem Archiv holen“ bringt ihn zurück.",
843
917
  confirm: "Archivieren"
918
+ },
919
+ directEdit: {
920
+ unpublished: "Sie haben unveröffentlichte Änderungen",
921
+ continue: "Weiter bearbeiten"
922
+ },
923
+ changeValidity: {
924
+ title: "Geltung ändern",
925
+ hint: "Korrigiert nur die Daten dieses Releases. Ein Release nur mit Datum auf derselben Revision ersetzt es und wird sofort veröffentlicht; die benachbarten Releases werden angepasst.",
926
+ release: "Release {{release}}, bisher {{period}}",
927
+ firstDay: "Erster Tag in Kraft",
928
+ firstDayHelp: "Der erste Tag des korrigierten Geltungszeitraums.",
929
+ firstDayRequired: "Wählen Sie den ersten Tag.",
930
+ lastDay: "Letzter Tag in Kraft",
931
+ lastDayHelp: "Leer lassen, solange kein Ende bekannt ist.",
932
+ lastDayBefore: "Der letzte Tag kann nicht vor dem ersten liegen.",
933
+ submit: "Geltung ändern",
934
+ done: "Fertig",
935
+ published_one: "Die Daten sind korrigiert: {{count}} Release wurde veröffentlicht.",
936
+ published_other: "Die Daten sind korrigiert: {{count}} Releases wurden veröffentlicht.",
937
+ editingMode: "Dieser Content wird nur in Change Sets geändert. Korrigieren Sie seine Daten in einem Change Set.",
938
+ covers: "Der neue Zeitraum würde {{releases}} vollständig überdecken. Ob es ersetzt wird, entscheidet ein Change Set.",
939
+ publishRefused: "Nichts wurde veröffentlicht:",
940
+ denied: "Nur wer veröffentlichen darf, kann die Geltung eines Releases ändern."
844
941
  }
942
+ },
943
+ directEdit: {
944
+ badge: "Direktbearbeitung",
945
+ loading: "Direktbearbeitung wird geladen…",
946
+ notFound: "Diese Direktbearbeitung gibt es nicht.",
947
+ loadError: "Die Direktbearbeitung konnte nicht geladen werden.",
948
+ publish: "Veröffentlichen",
949
+ discard: "Verwerfen",
950
+ closed: {
951
+ open: "Diese Direktbearbeitung ist offen.",
952
+ published: "Veröffentlicht: das neue Release ist in Kraft.",
953
+ discarded: "Verworfen: die Änderungen sind weg, der Inhalt ist unverändert."
954
+ },
955
+ discardConfirm: {
956
+ title: "Änderungen verwerfen?",
957
+ message: "Ihre gespeicherten Änderungen an {{content}} werden verworfen, nichts wird veröffentlicht. Das lässt sich nicht rückgängig machen.",
958
+ failed: "Die Direktbearbeitung konnte nicht verworfen werden."
959
+ },
960
+ refused_one: "Veröffentlichen wurde aus {{count}} Grund abgelehnt",
961
+ refused_other: "Veröffentlichen wurde aus {{count}} Gründen abgelehnt",
962
+ refusedBecause: {
963
+ dated_content: "Dieser Inhalt ist datiert: ändern Sie ihn in einem Change Set, das sagt, ab wann die Änderung gilt.",
964
+ one_branch: "Eine Direktbearbeitung ändert nur einen Inhalt: verwenden Sie ein Change Set.",
965
+ not_a_direct_edit: "Dieses Change Set ist keine Direktbearbeitung.",
966
+ unknown: "Die Direktbearbeitung wurde abgelehnt."
967
+ },
968
+ saveFirst: "Speichern Sie Ihre Änderungen zuerst: Veröffentlicht wird, was gespeichert ist.",
969
+ changeSetsOnly: "Dieser Inhalt wird nur über Change Sets geändert: bearbeiten Sie ihn in einem Change Set."
845
970
  }
846
971
  }, fe = {
847
972
  common: { cancel: "Cancel" },
848
973
  settings: {
849
974
  section: "Settings",
850
975
  title: "Content settings",
851
- subtitle: "What Publish requires, why Content is blocked, and how Content is filed.",
976
+ subtitle: "What Publish requires, why Content is blocked, how Content may be changed, and how it is filed.",
852
977
  tabs: {
853
978
  requiredChecks: "Required Checks",
854
979
  blockReasons: "Block Reasons",
980
+ editing: "Editing",
855
981
  collections: "Collections"
856
982
  }
857
983
  },
@@ -890,6 +1016,63 @@ var W = {
890
1016
  failed: "The Block Reason could not be removed. Try again."
891
1017
  }
892
1018
  },
1019
+ editing: {
1020
+ mode: {
1021
+ direct_edits: "Direct Edits only",
1022
+ change_sets: "Change Sets only",
1023
+ both: "Direct Edits and Change Sets"
1024
+ },
1025
+ block: {
1026
+ on: "On",
1027
+ off: "Off"
1028
+ },
1029
+ workspace: {
1030
+ title: "Workspace defaults",
1031
+ description: "How Content may be changed: in a Direct Edit, opened from the Content, in a Change Set, or both, and whether a Direct Edit keeps other Direct Edits out with an Editing Block. Every Blueprint without an override follows these. A change takes effect at once and leaves open work as it is.",
1032
+ edit: "Change defaults"
1033
+ },
1034
+ columns: {
1035
+ blueprint: "Blueprint",
1036
+ mode: "Editing Mode",
1037
+ block: "Editing Block",
1038
+ actions: "Actions"
1039
+ },
1040
+ fromWorkspace: "{{value}} (workspace default)",
1041
+ createOverride: "New override",
1042
+ editOverride: "Edit",
1043
+ removeOverride: "Remove",
1044
+ count_one: "{{count}} Blueprint override",
1045
+ count_other: "{{count}} Blueprint overrides",
1046
+ empty: "No Blueprint overrides yet",
1047
+ emptyHint: "Every Blueprint follows the workspace defaults. Override them for a Blueprint whose Content is changed differently, such as statutes only in Change Sets.",
1048
+ loadError: "The editing settings could not be loaded.",
1049
+ form: {
1050
+ workspaceTitle: "Workspace defaults",
1051
+ createOverrideTitle: "New Blueprint override",
1052
+ editOverrideTitle: "Edit Blueprint override",
1053
+ accessor: "Blueprint accessor",
1054
+ accessorHelp: "The Blueprint whose Content this applies to, by accessor.",
1055
+ accessorRequired: "Enter a Blueprint accessor.",
1056
+ accessorTooLong: "At most {{max}} characters.",
1057
+ accessorTaken: "“{{name}}” has an override already: edit it instead.",
1058
+ mode: "Editing Mode",
1059
+ modeHelp: "Only “Change Sets only” is enforced: a Direct Edit on such Content is refused. Imports, flows and Blueprint upgrades always use Change Sets.",
1060
+ modeRequired: "Choose an Editing Mode.",
1061
+ block: "Editing Block",
1062
+ blockHelp: "Whether opening a Direct Edit keeps other editors' Direct Edits out until it is published or discarded. Change Sets still pass it.",
1063
+ follow: "Workspace default ({{value}})",
1064
+ overridesNothing: "Override the Editing Mode, the Editing Block or both. To follow the workspace in both, remove the override.",
1065
+ save: "Save",
1066
+ denied: "Only a workspace admin changes the editing settings.",
1067
+ failed: "The editing settings could not be saved. Try again."
1068
+ },
1069
+ removeDialog: {
1070
+ title: "Remove override",
1071
+ message: "Remove the override of “{{name}}”? Its Content follows the workspace defaults again.",
1072
+ confirm: "Remove",
1073
+ failed: "The override could not be removed. Try again."
1074
+ }
1075
+ },
893
1076
  requiredChecks: {
894
1077
  create: "New Required Check",
895
1078
  columns: {
@@ -1088,7 +1271,9 @@ var W = {
1088
1271
  anyState: "Any state",
1089
1272
  assignee: "Assignee",
1090
1273
  anyone: "Anyone",
1091
- me: "Assigned to me"
1274
+ me: "Assigned to me",
1275
+ kind: "Kind",
1276
+ anyKind: "Change Sets and Direct Edits"
1092
1277
  },
1093
1278
  count_one: "{{count}} Change Set",
1094
1279
  count_other: "{{count}} Change Sets",
@@ -1111,7 +1296,11 @@ var W = {
1111
1296
  failed: "The Change Set could not be created. Try again.",
1112
1297
  createSubmit: "Create"
1113
1298
  },
1114
- more: "Show more"
1299
+ more: "Show more",
1300
+ kind: {
1301
+ change_set: "Change Sets",
1302
+ direct_edit: "Direct Edits"
1303
+ }
1115
1304
  },
1116
1305
  branch: {
1117
1306
  loading: "Loading Content…",
@@ -1608,7 +1797,8 @@ var W = {
1608
1797
  block: "Block",
1609
1798
  unblock: "Unblock",
1610
1799
  archive: "Archive",
1611
- unarchive: "Unarchive"
1800
+ unarchive: "Unarchive",
1801
+ edit: "Edit"
1612
1802
  },
1613
1803
  data: {
1614
1804
  readOnMain: "Main's head, Revision {{revision}}. Read-only: Start editing to change it in a Branch.",
@@ -1628,7 +1818,8 @@ var W = {
1628
1818
  published: "Published",
1629
1819
  inForce: "Today",
1630
1820
  changeSet: "Change Set",
1631
- proposed: "Origin"
1821
+ proposed: "Origin",
1822
+ actions: "Actions"
1632
1823
  },
1633
1824
  kind: {
1634
1825
  revision: "Revision",
@@ -1647,7 +1838,8 @@ var W = {
1647
1838
  noPlanned: "No open Change Set plans a Release of it",
1648
1839
  proposed: "Proposed",
1649
1840
  planned: "Planned",
1650
- loadError: "The Timeline could not be loaded."
1841
+ loadError: "The Timeline could not be loaded.",
1842
+ changeValidity: "Change validity"
1651
1843
  },
1652
1844
  revisions: {
1653
1845
  columns: {
@@ -1731,7 +1923,8 @@ var W = {
1731
1923
  opened: "Opened",
1732
1924
  empty: "No open Change Set changes it",
1733
1925
  emptyHint: "Start editing opens one, or adds it to one that is open.",
1734
- loadError: "The Change Sets could not be loaded."
1926
+ loadError: "The Change Sets could not be loaded.",
1927
+ directEditBy: "Direct Edit by {{who}}"
1735
1928
  },
1736
1929
  filing: {
1737
1930
  title: "Filing",
@@ -1799,7 +1992,58 @@ var W = {
1799
1992
  title: "Archive this Content?",
1800
1993
  message: "Archived Content leaves the Library, every picker and editing. Its Releases stay readable, and Unarchive brings it back.",
1801
1994
  confirm: "Archive"
1995
+ },
1996
+ directEdit: {
1997
+ unpublished: "You have unpublished changes",
1998
+ continue: "Continue editing"
1999
+ },
2000
+ changeValidity: {
2001
+ title: "Change validity",
2002
+ hint: "Corrects only this Release's dates. A date-only Release on the same Revision replaces it, published at once; the Releases next to it are cut to fit.",
2003
+ release: "Release {{release}}, now {{period}}",
2004
+ firstDay: "First day in force",
2005
+ firstDayHelp: "The first day of the corrected Validity Period.",
2006
+ firstDayRequired: "Choose the first day.",
2007
+ lastDay: "Last day in force",
2008
+ lastDayHelp: "Leave it empty while no end is known.",
2009
+ lastDayBefore: "The last day can’t be before the first.",
2010
+ submit: "Change validity",
2011
+ done: "Done",
2012
+ published_one: "The dates are corrected: {{count}} Release was published.",
2013
+ published_other: "The dates are corrected: {{count}} Releases were published.",
2014
+ editingMode: "This Content is changed in Change Sets only. Correct its dates in a Change Set.",
2015
+ covers: "The new period would cover {{releases}} entirely. Whether to supersede it is decided in a Change Set.",
2016
+ publishRefused: "Nothing was published:",
2017
+ denied: "Only a publisher may change a Release's validity."
1802
2018
  }
2019
+ },
2020
+ directEdit: {
2021
+ badge: "Direct Edit",
2022
+ loading: "Loading the Direct Edit…",
2023
+ notFound: "This Direct Edit doesn't exist.",
2024
+ loadError: "The Direct Edit could not be loaded.",
2025
+ publish: "Publish",
2026
+ discard: "Discard",
2027
+ closed: {
2028
+ open: "This Direct Edit is open.",
2029
+ published: "Published: the new Release is In Force.",
2030
+ discarded: "Discarded: its changes are gone, and the Content is as it was."
2031
+ },
2032
+ discardConfirm: {
2033
+ title: "Discard your changes?",
2034
+ message: "Your saved changes to {{content}} are dropped, and nothing is published. This cannot be undone.",
2035
+ failed: "The Direct Edit could not be discarded."
2036
+ },
2037
+ refused_one: "Publish was refused for {{count}} reason",
2038
+ refused_other: "Publish was refused for {{count}} reasons",
2039
+ refusedBecause: {
2040
+ dated_content: "This Content is Dated: change it in a Change Set, which says when the change takes effect.",
2041
+ one_branch: "A Direct Edit changes one Content only: use a Change Set.",
2042
+ not_a_direct_edit: "This Change Set is not a Direct Edit.",
2043
+ unknown: "The Direct Edit was refused."
2044
+ },
2045
+ saveFirst: "Save your changes first: Publish releases what is saved.",
2046
+ changeSetsOnly: "This Content is changed through Change Sets only: start editing it in a Change Set."
1803
2047
  }
1804
2048
  }, K = "contenthub-ui", q = {
1805
2049
  en: { [K]: fe },
@@ -1917,7 +2161,7 @@ var Z = {
1917
2161
  sourceDate: "",
1918
2162
  sourceReference: ""
1919
2163
  }, ye = "contenthub-create-change-set", Q = /^[^\u0000-\u001f\u007f]*$/;
1920
- function be({ open: e, onClose: n, onSubmit: r }) {
2164
+ function be({ open: e, onClose: t, onSubmit: r }) {
1921
2165
  let { t: i } = I(K), s = o(() => U.object({
1922
2166
  name: U.string().trim().min(1, i("changeSets.form.nameRequired")).max(200, i("changeSets.form.tooLong", { max: 200 })).regex(Q, i("changeSets.form.oneLine")),
1923
2167
  sourceDate: U.string().trim(),
@@ -1950,12 +2194,12 @@ function be({ open: e, onClose: n, onSubmit: r }) {
1950
2194
  }
1951
2195
  });
1952
2196
  } catch (e) {
1953
- m(e instanceof t ? e.message : i("changeSets.form.failed"));
2197
+ m(e instanceof n ? e.message : i("changeSets.form.failed"));
1954
2198
  }
1955
2199
  });
1956
2200
  return /* @__PURE__ */ u(g, {
1957
2201
  open: e,
1958
- onClose: n,
2202
+ onClose: t,
1959
2203
  lazyMount: !0,
1960
2204
  unmountOnExit: !0,
1961
2205
  size: "md",
@@ -1965,7 +2209,7 @@ function be({ open: e, onClose: n, onSubmit: r }) {
1965
2209
  children: [/* @__PURE__ */ u(f, {
1966
2210
  size: "sm",
1967
2211
  variant: "ghost",
1968
- onClick: n,
2212
+ onClick: t,
1969
2213
  children: i("common.cancel")
1970
2214
  }), /* @__PURE__ */ u(f, {
1971
2215
  type: "submit",
@@ -2165,7 +2409,7 @@ function Ee({ open: e, onClose: l, client: p, blueprintSource: m, initialChangeS
2165
2409
  let t = e.reasons.map(Te).join(", ");
2166
2410
  return v("library.create.refused", { reasons: t });
2167
2411
  }
2168
- return e instanceof n ? v("library.create.changeSetClosed") : e instanceof t ? e.message : v("library.create.failed");
2412
+ return e instanceof t ? v("library.create.changeSetClosed") : e instanceof n ? e.message : v("library.create.failed");
2169
2413
  }
2170
2414
  return /* @__PURE__ */ u(g, {
2171
2415
  open: e,
@@ -2279,8 +2523,8 @@ var De = [
2279
2523
  function Ne(e) {
2280
2524
  return Object.values(e).filter((e) => typeof e == "string" && e.trim() !== "" && e.length <= 200).slice(0, 2).join(" ");
2281
2525
  }
2282
- function Pe({ client: e, blueprintSource: n, onOpenContent: r, onOpenBranch: s }) {
2283
- let { t: l } = I(K), g = he(e, n), [v, y] = de(), b = v.get("collection") ?? "", [x, S] = c("main"), [ee, C] = c(""), [w, T] = c(""), [E, ae] = c(""), [F, se] = c(""), [L, R] = c(""), [z, B] = c(!1), { userId: V, members: le } = ue();
2526
+ function Pe({ client: e, blueprintSource: t, onOpenContent: r, onOpenBranch: s }) {
2527
+ let { t: l } = I(K), g = he(e, t), [v, y] = de(), b = v.get("collection") ?? "", [x, S] = c("main"), [ee, C] = c(""), [w, T] = c(""), [E, ae] = c(""), [F, se] = c(""), [L, R] = c(""), [z, B] = c(!1), { userId: V, members: le } = ue();
2284
2528
  a(() => {
2285
2529
  let e = setTimeout(() => ae(w), ke);
2286
2530
  return () => clearTimeout(e);
@@ -2387,7 +2631,7 @@ function Pe({ client: e, blueprintSource: n, onOpenContent: r, onOpenBranch: s }
2387
2631
  variant: "solid",
2388
2632
  onClick: () => B(!0),
2389
2633
  children: [/* @__PURE__ */ u(ce, { size: 16 }), l("library.create.button")]
2390
- }), we = J.error instanceof t ? J.error.message : l("library.loadError"), $ = b ? _e(b, U.data ?? []).map((e) => e.name).join(" / ") || b : l("library.allContent");
2634
+ }), we = J.error instanceof n ? J.error.message : l("library.loadError"), $ = b ? _e(b, U.data ?? []).map((e) => e.name).join(" / ") || b : l("library.allContent");
2391
2635
  return /* @__PURE__ */ d(ne, {
2392
2636
  title: l("library.title"),
2393
2637
  breadcrumbs: [{ label: l("library.title") }],
@@ -2546,4 +2790,4 @@ Pe.displayName = "ContentLibrary";
2546
2790
  //#endregion
2547
2791
  export { xe as a, _e as c, K as d, pe as f, Ce as i, ge as l, W as m, Pe as n, Se as o, q as p, Ne as r, be as s, Me as t, he as u };
2548
2792
 
2549
- //# sourceMappingURL=content-library-DoiD_FOJ.js.map
2793
+ //# sourceMappingURL=content-library-DFIk09bD.js.map