@qkix/strapi-plugin-rewind 0.1.3 → 0.2.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/README.md CHANGED
@@ -34,7 +34,7 @@ Strapi sells a feature called **Content History** on its Growth and Enterprise
34
34
  plans. Rewind is **not that feature**, is not a drop-in replacement for it, and
35
35
  does not unlock it.
36
36
 
37
- It is the poor cousin. If you pay for Growth, use Strapi's — it is deeper, it is
37
+ It is the poor cousin. If you pay for Growth, use Strapi's - it is deeper, it is
38
38
  supported by the people who wrote the CMS, and it will stay in step with Strapi
39
39
  in a way a third-party plugin cannot promise. Rewind exists for everyone on
40
40
  Community Edition, who today has nothing at all: no way to see what a document
@@ -56,7 +56,7 @@ cheap to add once the plumbing existed:
56
56
  - **Writes from outside the Content Manager can be versioned** (opt-in), where
57
57
  Strapi only ever records admin-panel edits.
58
58
 
59
- And a good deal it does _not_ do — see [Limits](#limits).
59
+ And a good deal it does _not_ do - see [Limits](#limits).
60
60
 
61
61
  ### On not touching the paywall
62
62
 
@@ -64,8 +64,8 @@ Rewind never reads or writes `strapi.ee`, never inspects a licence, and never
64
64
  enables a gated feature. It is an independent implementation writing to its own
65
65
  table, and it works the same whether or not you have a Strapi licence. Strapi's
66
66
  history code is MIT-licensed and was read while working out what the problem
67
- actually is — the way you would read any open-source implementation before
68
- writing your own — but the code here is written for this plugin.
67
+ actually is - the way you would read any open-source implementation before
68
+ writing your own - but the code here is written for this plugin.
69
69
 
70
70
  ## Install
71
71
 
@@ -94,7 +94,7 @@ the first disk alert.
94
94
 
95
95
  Six actions on a tracked content type: `create`, `update`, `clone`, `publish`,
96
96
  `unpublish` and `discardDraft`. Each version stores the document's content, its
97
- relations, and a snapshot of the schema at the time — the last of which is what
97
+ relations, and a snapshot of the schema at the time - the last of which is what
98
98
  lets a restore tell "this field was empty" apart from "this field did not exist
99
99
  yet".
100
100
 
@@ -106,7 +106,7 @@ A save that is rolled back records nothing.
106
106
  <img src="https://raw.githubusercontent.com/qkix/strapi-plugins/main/packages/strapi-plugin-rewind/docs/panel.png" alt="History entries labelled Replaced by restore, Draft discarded and Edited" width="320" />
107
107
 
108
108
  The badge says what happened to the document. **Replaced by restore** marks the
109
- state a restore was about to overwrite — the undo point for that restore.
109
+ state a restore was about to overwrite - the undo point for that restore.
110
110
 
111
111
  ## Restoring
112
112
 
@@ -125,7 +125,7 @@ part of a restore is what it will _not_ do:
125
125
  - fields no longer in the model are skipped
126
126
  - links to documents or media that have since been deleted are dropped, and
127
127
  reported
128
- - **fields that are not translated per locale change in every locale at once** —
128
+ - **fields that are not translated per locale change in every locale at once** -
129
129
  restoring the Polish version of a document rewrites a shared field for every
130
130
  other language too. This is not a choice the plugin makes; a non-localised
131
131
  field physically has one value. The dialog names those fields and counts the
@@ -138,14 +138,14 @@ with the one saved immediately before it.
138
138
 
139
139
  <img src="https://raw.githubusercontent.com/qkix/strapi-plugins/main/packages/strapi-plugin-rewind/docs/changes.png" alt="A word-level diff: 'put back' struck through and replaced by 'restored in one click', and 'ten' by 'twelve'" width="760" />
140
140
 
141
- Scalars are shown as a plain before and after. Prose — including rich text
142
- stored as JSON — is compared word by word, with the unchanged stretches
141
+ Scalars are shown as a plain before and after. Prose - including rich text
142
+ stored as JSON - is compared word by word, with the unchanged stretches
143
143
  collapsed, so a one-word edit in a long article reads as one word rather than
144
144
  as the whole article with something green in it somewhere. Relations are
145
145
  reported as linked and unlinked.
146
146
 
147
- Where a field's stored value changed but its readable text did not — a mark
148
- applied, blocks reordered — it says so, rather than showing an empty diff.
147
+ Where a field's stored value changed but its readable text did not - a mark
148
+ applied, blocks reordered - it says so, rather than showing an empty diff.
149
149
 
150
150
  ### Rendering a field type properly
151
151
 
@@ -156,7 +156,7 @@ package that owns the format can show that, so the mapping is a registry:
156
156
  ```ts
157
157
  import { registerDiffRenderer } from '@qkix/strapi-plugin-rewind/strapi-admin';
158
158
 
159
- import { MyBlocksDiff } from './MyBlocksDiff'; // yours — see below
159
+ import { MyBlocksDiff } from './MyBlocksDiff'; // yours - see below
160
160
 
161
161
  registerDiffRenderer('plugin::better-blocks.better-blocks', MyBlocksDiff);
162
162
  ```
@@ -165,13 +165,24 @@ Keyed by a custom field's uid, or by an attribute `type`. A uid wins over a
165
165
  type, since every custom field stores itself as `json`.
166
166
 
167
167
  > **Bring your own renderer.** Rewind ships the registry, not the renderers. No
168
- > `@qkix` package currently exports a block-aware diff component — including
169
- > Better Blocks — so the component above is one you write. A renderer receives
168
+ > `@qkix` package currently exports a block-aware diff component - including
169
+ > Better Blocks - so the component above is one you write. A renderer receives
170
170
  > the two values and returns the `FieldChange` shape exported alongside
171
171
  > `registerDiffRenderer`; without one registered, a field falls back to the
172
172
  > generic text diff. A ready-made renderer for Better Blocks documents is
173
173
  > wanted, and not yet written.
174
174
 
175
+ ## Pinning a version
176
+
177
+ Every entry has a pin. A pinned version is never thinned, whatever the retention
178
+ settings say and however old it gets - the state the site launched in, the draft
179
+ before a rewrite, the version somebody will ask about in a year.
180
+
181
+ Pinning is the one thing in this panel that writes to the history rather than to
182
+ the document, and it is the whole of it: a version is pinned or it is not.
183
+ Nothing else about the version changes, and unpinning hands it straight back to
184
+ the next prune.
185
+
175
186
  ## Configuration
176
187
 
177
188
  | Option | Default | Meaning |
@@ -185,8 +196,8 @@ type, since every custom field stores itself as `json`.
185
196
  | `retention.keepAnchors` | `true` | Never thin publish, unpublish, discard or restore versions, whatever their age. |
186
197
  | `cron` | `'0 3 * * *'` | When the thinning runs, in the server's timezone. |
187
198
 
188
- The three windows must widen in order — `keepAllDays <= dailyUntilDays <=
189
- maxAgeDays` — and the plugin refuses to boot rather than delete on a guess if
199
+ The three windows must widen in order - `keepAllDays <= dailyUntilDays <=
200
+ maxAgeDays` - and the plugin refuses to boot rather than delete on a guess if
190
201
  they do not. Pinned versions are never thinned either, independently of
191
202
  `keepAnchors`.
192
203
 
@@ -202,8 +213,6 @@ Worth knowing before you install it, not after:
202
213
  - **Only the Document Service is visible.** Writes made through
203
214
  `strapi.db.query()` or the legacy entity service bypass the middleware
204
215
  entirely and cannot be captured at any setting.
205
- - **Nothing pins a version from the panel yet.** The column is there and prune
206
- honours it, but there is no button; pin by setting `pinned` on the row.
207
216
  - **Deleted relation targets lose their names.** A version stores which document
208
217
  a relation pointed at, not what it was called, so a link to something since
209
218
  deleted shows as missing rather than as "the article it used to be".
@@ -213,7 +222,7 @@ Worth knowing before you install it, not after:
213
222
  ## Keeping your history when the plugin is off
214
223
 
215
224
  Strapi's schema sync drops tables belonging to content types it no longer sees.
216
- Disable this plugin for a single boot and the history would go with it — so on
225
+ Disable this plugin for a single boot and the history would go with it - so on
217
226
  boot Rewind adds its table to Strapi's `persisted_tables` list, which is the
218
227
  same mechanism Strapi uses to protect its own. Nothing to configure.
219
228
 
@@ -221,6 +230,11 @@ same mechanism Strapi uses to protect its own. Nothing to configure.
221
230
 
222
231
  Strapi 5, Node 20 or 22.
223
232
 
233
+ ## Roadmap
234
+
235
+ What is worth building next, what is deliberately not planned, and why:
236
+ [ROADMAP.md](https://github.com/qkix/strapi-plugins/blob/main/packages/strapi-plugin-rewind/ROADMAP.md).
237
+
224
238
  ## License
225
239
 
226
240
  MIT.
@@ -1 +1 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("react/jsx-runtime"),q=require("react"),E=require("@strapi/admin/strapi-admin"),t=require("@strapi/design-system");function H(n){if(n&&n.__esModule)return n;const r=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const i in n)if(i!=="default"){const a=Object.getOwnPropertyDescriptor(n,i);Object.defineProperty(r,i,a.get?a:{enumerable:!0,get:()=>n[i]})}}return r.default=n,Object.freeze(r)}const l=H(q),G=(n,r,i)=>{const a=n[r];return a?typeof a=="function"?a():Promise.resolve(a):new Promise((j,d)=>{(typeof queueMicrotask=="function"?queueMicrotask:setTimeout)(d.bind(null,new Error("Unknown variable dynamic import: "+r+(r.split("/").length!==i?". Note that variables only represent file names one level deep.":""))))})},D=new Map,W=(n,r)=>{D.set(n,r)},b=n=>D.get(n),z=({spans:n})=>e.jsx(t.Typography,{variant:"omega",tag:"p",children:n.map((r,i)=>r.op==="equal"?e.jsx(t.Typography,{variant:"omega",textColor:"neutral600",children:r.value},i):e.jsx(t.Typography,{variant:"omega",textColor:r.op==="added"?"success600":"danger600",style:r.op==="removed"?{textDecoration:"line-through"}:{fontWeight:600},children:r.value},i))}),S=n=>n==null||n===""?"(empty)":typeof n=="object"?JSON.stringify(n):String(n),U=({change:n})=>{const r=(n.customField?b(n.customField):void 0)??b(n.type);return r?e.jsx(r,{change:n}):n.spans?e.jsx(z,{spans:n.spans}):n.linked||n.unlinked?e.jsxs(t.Flex,{direction:"column",alignItems:"flex-start",gap:1,children:[n.linked?.length?e.jsxs(t.Typography,{variant:"pi",textColor:"success600",children:["+ ",n.linked.length," linked"]}):null,n.unlinked?.length?e.jsxs(t.Typography,{variant:"pi",textColor:"danger600",children:["− ",n.unlinked.length," unlinked"]}):null]}):n.before!==void 0||n.after!==void 0?e.jsxs(t.Flex,{gap:2,alignItems:"center",wrap:"wrap",children:[e.jsx(t.Typography,{variant:"omega",textColor:"danger600",style:{textDecoration:"line-through"},children:S(n.before)}),e.jsx(t.Typography,{variant:"omega",textColor:"neutral500",children:"→"}),e.jsx(t.Typography,{variant:"omega",textColor:"success600",children:S(n.after)})]}):e.jsx(t.Typography,{variant:"pi",textColor:"neutral600",children:"Formatting or structure changed; the text is the same."})},K=({diff:n,loading:r,onClose:i})=>e.jsx(t.Modal.Root,{open:!0,onOpenChange:i,children:e.jsxs(t.Modal.Content,{children:[e.jsx(t.Modal.Header,{children:e.jsx(t.Modal.Title,{children:"What changed"})}),e.jsx(t.Modal.Body,{children:r?e.jsx(t.Flex,{justifyContent:"center",padding:4,children:e.jsx(t.Loader,{small:!0,children:"Comparing"})}):n?e.jsxs(t.Flex,{direction:"column",alignItems:"stretch",gap:4,children:[e.jsx(t.Typography,{variant:"pi",textColor:"neutral600",children:n.from?"Compared with the version saved just before it.":"This is the earliest version of the document, so everything in it is new."}),n.identical?e.jsx(t.Typography,{textColor:"neutral600",children:"Nothing changed between these two versions."}):n.changes.map(a=>e.jsxs(t.Box,{children:[e.jsx(t.Typography,{variant:"sigma",textColor:"neutral600",children:a.field}),e.jsx(t.Box,{paddingTop:1,children:e.jsx(U,{change:a})})]},a.field))]}):e.jsx(t.Typography,{textColor:"danger600",children:"Could not compare these versions."})}),e.jsx(t.Modal.Footer,{children:e.jsx(t.Button,{variant:"tertiary",onClick:i,children:"Close"})})]})}),u="rewind",V={create:"Created",update:"Edited",clone:"Cloned",publish:"Published",unpublish:"Unpublished",discardDraft:"Draft discarded",restore:"Replaced by restore"},k=new Set(["publish","unpublish","discardDraft","restore"]),J=10,Z=n=>{const r=new Date(n),i=Math.round((Date.now()-r.getTime())/6e4);return i<1?"just now":i<60?`${i} min ago`:i<60*24?`${Math.round(i/60)} h ago`:r.toLocaleString()},Q=({model:n,documentId:r,updatedAt:i})=>{const{get:a,post:j}=E.useFetchClient(),[d,R]=l.useState([]),[F,I]=l.useState(0),[B,L]=l.useState(1),[f,m]=l.useState(!0),[h,p]=l.useState(null),[o,g]=l.useState(null),[v,C]=l.useState(!1),[M,w]=l.useState(!1),[P,x]=l.useState(null),[O,T]=l.useState(!1),y=l.useCallback(async s=>{m(!0),p(null);try{const{data:c}=await a(`/${u}/versions`,{params:{contentType:n,documentId:r,page:s,pageSize:J}});R(A=>s===1?c.data:[...A,...c.data]),I(c.meta.total),L(s)}catch{p("Could not load the history for this document.")}finally{m(!1)}},[a,n,r]);l.useEffect(()=>{y(1)},[y,i]);const _=async s=>{w(!0),T(!0),x(null);try{const{data:c}=await a(`/${u}/versions/${s.id}/diff`);x(c.data)}catch{x(null)}finally{T(!1)}},N=async s=>{try{const{data:c}=await a(`/${u}/versions/${s.id}/preview`);g({version:s,preview:c.data})}catch{p("Could not work out what restoring this version would change.")}},$=async()=>{if(o){C(!0);try{await j(`/${u}/versions/${o.version.id}/restore`,{}),window.location.reload()}catch{p("The restore failed. Nothing was changed."),C(!1),g(null)}}};return f&&d.length===0?e.jsx(t.Flex,{justifyContent:"center",padding:4,children:e.jsx(t.Loader,{small:!0,children:"Loading history"})}):h&&d.length===0?e.jsx(t.Typography,{variant:"pi",textColor:"danger600",children:h}):d.length===0?e.jsx(t.Typography,{variant:"pi",textColor:"neutral600",children:"No versions yet. The next time this document is saved, a version will appear here."}):e.jsxs(t.Flex,{direction:"column",alignItems:"stretch",gap:2,width:"100%",children:[d.map(s=>e.jsxs(t.Box,{paddingBottom:2,children:[e.jsxs(t.Flex,{justifyContent:"space-between",alignItems:"center",gap:2,children:[e.jsxs(t.Flex,{direction:"column",alignItems:"flex-start",gap:1,grow:1,minWidth:0,children:[e.jsxs(t.Flex,{gap:2,alignItems:"center",children:[e.jsx(t.Badge,{backgroundColor:k.has(s.origin)?"primary100":"neutral150",textColor:k.has(s.origin)?"primary600":"neutral600",children:V[s.origin]??s.origin}),s.locale?e.jsx(t.Typography,{variant:"pi",textColor:"neutral500",children:s.locale}):null]}),s.label?e.jsx(t.Box,{width:"100%",children:e.jsx(t.Typography,{variant:"omega",textColor:"neutral800",ellipsis:!0,children:s.label})}):null,e.jsxs(t.Typography,{variant:"pi",textColor:"neutral600",children:[Z(s.createdAt),s.user?` · ${s.user.name}`:""]}),e.jsx(t.TextButton,{onClick:()=>_(s),children:"What changed"})]}),e.jsx(t.Box,{shrink:0,children:e.jsx(t.Button,{size:"S",variant:"tertiary",onClick:()=>N(s),children:"Restore"})})]}),e.jsx(t.Box,{paddingTop:2,children:e.jsx(t.Divider,{})})]},s.id)),d.length<F?e.jsx(t.Button,{size:"S",variant:"tertiary",loading:f,onClick:()=>y(B+1),children:"Show older versions"}):null,h?e.jsx(t.Typography,{variant:"pi",textColor:"danger600",children:h}):null,M?e.jsx(K,{diff:P,loading:O,onClose:()=>w(!1)}):null,e.jsx(t.Dialog.Root,{open:o!==null,onOpenChange:()=>g(null),children:e.jsxs(t.Dialog.Content,{children:[e.jsx(t.Dialog.Header,{children:"Restore this version?"}),e.jsx(t.Dialog.Body,{children:e.jsxs(t.Flex,{direction:"column",alignItems:"flex-start",gap:2,children:[e.jsx(t.Typography,{children:"The current draft is saved as a version first, so this can be undone. The published version is not touched."}),o?.preview.crossLocaleFields.length?e.jsxs(t.Typography,{textColor:"danger600",children:[o.preview.crossLocaleFields.join(", ")," ",o.preview.crossLocaleFields.length===1?"is":"are"," not translated per locale, so restoring"," ",o.preview.crossLocaleFields.length===1?"it":"them"," changes"," ",o.preview.affectedLocales.length," locales at once:"," ",o.preview.affectedLocales.join(", "),"."]}):null,o?.preview.fieldsKeptAsIs.length?e.jsxs(t.Typography,{variant:"pi",textColor:"neutral600",children:["Added since this version and left untouched:"," ",o.preview.fieldsKeptAsIs.join(", "),"."]}):null,o?.preview.fieldsDropped.length?e.jsxs(t.Typography,{variant:"pi",textColor:"neutral600",children:["No longer in the model and skipped: ",o.preview.fieldsDropped.join(", "),"."]}):null,o?.preview.brokenRelations.length||o?.preview.missingMedia.length?e.jsxs(t.Typography,{variant:"pi",textColor:"warning600",children:[(o.preview.brokenRelations.length??0)+(o.preview.missingMedia.length??0)," ","linked item(s) no longer exist and will be left out."]}):null]})}),e.jsxs(t.Dialog.Footer,{children:[e.jsx(t.Dialog.Cancel,{children:e.jsx(t.Button,{variant:"tertiary",disabled:v,children:"Cancel"})}),e.jsx(t.Button,{onClick:$,loading:v,children:"Restore"})]})]})})]})},X=({model:n,documentId:r,document:i})=>r?{title:"History",content:e.jsx(Q,{model:n,documentId:r,updatedAt:i?.updatedAt})}:null,Y={register(n){n.registerPlugin({id:u,name:u,isReady:!0})},bootstrap(n){n.getPlugin("content-manager").apis.addEditViewSidePanel([X])},async registerTrads({locales:n}){return Promise.all(n.map(async r=>{try{const{default:i}=await G(Object.assign({"./translations/en.json":()=>Promise.resolve().then(()=>require("../_chunks/en-BHfx2OWt.js"))}),`./translations/${r}.json`,3);return{data:i,locale:r}}catch{return{data:{},locale:r}}}))}};exports.PLUGIN_ID=u;exports.default=Y;exports.registerDiffRenderer=W;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("react/jsx-runtime"),W=require("react"),z=require("@strapi/admin/strapi-admin"),t=require("@strapi/design-system"),G=require("@strapi/icons");function U(n){if(n&&n.__esModule)return n;const r=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const s in n)if(s!=="default"){const a=Object.getOwnPropertyDescriptor(n,s);Object.defineProperty(r,s,a.get?a:{enumerable:!0,get:()=>n[s]})}}return r.default=n,Object.freeze(r)}const d=U(W),K=(n,r,s)=>{const a=n[r];return a?typeof a=="function"?a():Promise.resolve(a):new Promise((C,y)=>{(typeof queueMicrotask=="function"?queueMicrotask:setTimeout)(y.bind(null,new Error("Unknown variable dynamic import: "+r+(r.split("/").length!==s?". Note that variables only represent file names one level deep.":""))))})},P=new Map,V=(n,r)=>{P.set(n,r)},R=n=>P.get(n),J=({spans:n})=>e.jsx(t.Typography,{variant:"omega",tag:"p",children:n.map((r,s)=>r.op==="equal"?e.jsx(t.Typography,{variant:"omega",textColor:"neutral600",children:r.value},s):e.jsx(t.Typography,{variant:"omega",textColor:r.op==="added"?"success600":"danger600",style:r.op==="removed"?{textDecoration:"line-through"}:{fontWeight:600},children:r.value},s))}),F=n=>n==null||n===""?"(empty)":typeof n=="object"?JSON.stringify(n):String(n),Z=({change:n})=>{const r=(n.customField?R(n.customField):void 0)??R(n.type);return r?e.jsx(r,{change:n}):n.spans?e.jsx(J,{spans:n.spans}):n.linked||n.unlinked?e.jsxs(t.Flex,{direction:"column",alignItems:"flex-start",gap:1,children:[n.linked?.length?e.jsxs(t.Typography,{variant:"pi",textColor:"success600",children:["+ ",n.linked.length," linked"]}):null,n.unlinked?.length?e.jsxs(t.Typography,{variant:"pi",textColor:"danger600",children:["− ",n.unlinked.length," unlinked"]}):null]}):n.before!==void 0||n.after!==void 0?e.jsxs(t.Flex,{gap:2,alignItems:"center",wrap:"wrap",children:[e.jsx(t.Typography,{variant:"omega",textColor:"danger600",style:{textDecoration:"line-through"},children:F(n.before)}),e.jsx(t.Typography,{variant:"omega",textColor:"neutral500",children:"→"}),e.jsx(t.Typography,{variant:"omega",textColor:"success600",children:F(n.after)})]}):e.jsx(t.Typography,{variant:"pi",textColor:"neutral600",children:"Formatting or structure changed; the text is the same."})},Q=({diff:n,loading:r,onClose:s})=>e.jsx(t.Modal.Root,{open:!0,onOpenChange:s,children:e.jsxs(t.Modal.Content,{children:[e.jsx(t.Modal.Header,{children:e.jsx(t.Modal.Title,{children:"What changed"})}),e.jsx(t.Modal.Body,{children:r?e.jsx(t.Flex,{justifyContent:"center",padding:4,children:e.jsx(t.Loader,{small:!0,children:"Comparing"})}):n?e.jsxs(t.Flex,{direction:"column",alignItems:"stretch",gap:4,children:[e.jsx(t.Typography,{variant:"pi",textColor:"neutral600",children:n.from?"Compared with the version saved just before it.":"This is the earliest version of the document, so everything in it is new."}),n.identical?e.jsx(t.Typography,{textColor:"neutral600",children:"Nothing changed between these two versions."}):n.changes.map(a=>e.jsxs(t.Box,{children:[e.jsx(t.Typography,{variant:"sigma",textColor:"neutral600",children:a.field}),e.jsx(t.Box,{paddingTop:1,children:e.jsx(Z,{change:a})})]},a.field))]}):e.jsx(t.Typography,{textColor:"danger600",children:"Could not compare these versions."})}),e.jsx(t.Modal.Footer,{children:e.jsx(t.Button,{variant:"tertiary",onClick:s,children:"Close"})})]})}),c="rewind",X={create:"Created",update:"Edited",clone:"Cloned",publish:"Published",unpublish:"Unpublished",discardDraft:"Draft discarded",restore:"Replaced by restore"},I=new Set(["publish","unpublish","discardDraft","restore"]),Y=10,ee=n=>{const r=new Date(n),s=Math.round((Date.now()-r.getTime())/6e4);return s<1?"just now":s<60?`${s} min ago`:s<60*24?`${Math.round(s/60)} h ago`:r.toLocaleString()},te=({model:n,documentId:r,updatedAt:s})=>{const{get:a,post:C,put:y}=z.useFetchClient(),[p,j]=d.useState([]),[B,L]=d.useState(0),[M,O]=d.useState(1),[w,T]=d.useState(!0),[x,h]=d.useState(null),[o,f]=d.useState(null),[b,S]=d.useState(!1),[_,k]=d.useState(!1),[$,m]=d.useState(null),[N,D]=d.useState(!1),v=d.useCallback(async i=>{T(!0),h(null);try{const{data:l}=await a(`/${c}/versions`,{params:{contentType:n,documentId:r,page:i,pageSize:Y}});j(g=>i===1?l.data:[...g,...l.data]),L(l.meta.total),O(i)}catch{h("Could not load the history for this document.")}finally{T(!1)}},[a,n,r]);d.useEffect(()=>{v(1)},[v,s]);const A=async i=>{k(!0),D(!0),m(null);try{const{data:l}=await a(`/${c}/versions/${i.id}/diff`);m(l.data)}catch{m(null)}finally{D(!1)}},q=async i=>{const l=!i.pinned;j(g=>g.map(u=>u.id===i.id?{...u,pinned:l}:u));try{await y(`/${c}/versions/${i.id}/pin`,{pinned:l})}catch{j(g=>g.map(u=>u.id===i.id?{...u,pinned:!l}:u)),h(l?"Could not pin that version.":"Could not unpin that version.")}},E=async i=>{try{const{data:l}=await a(`/${c}/versions/${i.id}/preview`);f({version:i,preview:l.data})}catch{h("Could not work out what restoring this version would change.")}},H=async()=>{if(o){S(!0);try{await C(`/${c}/versions/${o.version.id}/restore`,{}),window.location.reload()}catch{h("The restore failed. Nothing was changed."),S(!1),f(null)}}};return w&&p.length===0?e.jsx(t.Flex,{justifyContent:"center",padding:4,children:e.jsx(t.Loader,{small:!0,children:"Loading history"})}):x&&p.length===0?e.jsx(t.Typography,{variant:"pi",textColor:"danger600",children:x}):p.length===0?e.jsx(t.Typography,{variant:"pi",textColor:"neutral600",children:"No versions yet. The next time this document is saved, a version will appear here."}):e.jsxs(t.Flex,{direction:"column",alignItems:"stretch",gap:2,width:"100%",children:[p.map(i=>e.jsxs(t.Box,{paddingBottom:2,children:[e.jsxs(t.Flex,{justifyContent:"space-between",alignItems:"center",gap:2,children:[e.jsxs(t.Flex,{direction:"column",alignItems:"flex-start",gap:1,grow:1,minWidth:0,children:[e.jsxs(t.Flex,{gap:2,alignItems:"center",children:[e.jsx(t.Badge,{backgroundColor:I.has(i.origin)?"primary100":"neutral150",textColor:I.has(i.origin)?"primary600":"neutral600",children:X[i.origin]??i.origin}),i.locale?e.jsx(t.Typography,{variant:"pi",textColor:"neutral500",children:i.locale}):null]}),i.label?e.jsx(t.Box,{width:"100%",children:e.jsx(t.Typography,{variant:"omega",textColor:"neutral800",ellipsis:!0,children:i.label})}):null,e.jsxs(t.Typography,{variant:"pi",textColor:"neutral600",children:[ee(i.createdAt),i.user?` · ${i.user.name}`:"",i.pinned?e.jsx(t.Typography,{variant:"pi",textColor:"primary600",fontWeight:"bold",children:" · Pinned"}):null]}),e.jsx(t.TextButton,{onClick:()=>A(i),children:"What changed"})]}),e.jsxs(t.Flex,{shrink:0,gap:1,alignItems:"center",children:[e.jsx(t.IconButton,{size:"S",variant:"ghost",label:i.pinned?"Unpin this version, so tidying up can remove it":"Pin this version, so tidying up never removes it",onClick:()=>q(i),children:e.jsx(G.Pin,{fill:i.pinned?"primary600":"neutral500"})}),e.jsx(t.Button,{size:"S",variant:"tertiary",onClick:()=>E(i),children:"Restore"})]})]}),e.jsx(t.Box,{paddingTop:2,children:e.jsx(t.Divider,{})})]},i.id)),p.length<B?e.jsx(t.Button,{size:"S",variant:"tertiary",loading:w,onClick:()=>v(M+1),children:"Show older versions"}):null,x?e.jsx(t.Typography,{variant:"pi",textColor:"danger600",children:x}):null,_?e.jsx(Q,{diff:$,loading:N,onClose:()=>k(!1)}):null,e.jsx(t.Dialog.Root,{open:o!==null,onOpenChange:()=>f(null),children:e.jsxs(t.Dialog.Content,{children:[e.jsx(t.Dialog.Header,{children:"Restore this version?"}),e.jsx(t.Dialog.Body,{children:e.jsxs(t.Flex,{direction:"column",alignItems:"flex-start",gap:2,children:[e.jsx(t.Typography,{children:"The current draft is saved as a version first, so this can be undone. The published version is not touched."}),o?.preview.crossLocaleFields.length?e.jsxs(t.Typography,{textColor:"danger600",children:[o.preview.crossLocaleFields.join(", ")," ",o.preview.crossLocaleFields.length===1?"is":"are"," not translated per locale, so restoring"," ",o.preview.crossLocaleFields.length===1?"it":"them"," changes"," ",o.preview.affectedLocales.length," locales at once:"," ",o.preview.affectedLocales.join(", "),"."]}):null,o?.preview.fieldsKeptAsIs.length?e.jsxs(t.Typography,{variant:"pi",textColor:"neutral600",children:["Added since this version and left untouched:"," ",o.preview.fieldsKeptAsIs.join(", "),"."]}):null,o?.preview.fieldsDropped.length?e.jsxs(t.Typography,{variant:"pi",textColor:"neutral600",children:["No longer in the model and skipped: ",o.preview.fieldsDropped.join(", "),"."]}):null,o?.preview.brokenRelations.length||o?.preview.missingMedia.length?e.jsxs(t.Typography,{variant:"pi",textColor:"warning600",children:[(o.preview.brokenRelations.length??0)+(o.preview.missingMedia.length??0)," ","linked item(s) no longer exist and will be left out."]}):null]})}),e.jsxs(t.Dialog.Footer,{children:[e.jsx(t.Dialog.Cancel,{children:e.jsx(t.Button,{variant:"tertiary",disabled:b,children:"Cancel"})}),e.jsx(t.Button,{onClick:H,loading:b,children:"Restore"})]})]})})]})},ne=({model:n,documentId:r,document:s})=>r?{title:"History",content:e.jsx(te,{model:n,documentId:r,updatedAt:s?.updatedAt})}:null,re={register(n){n.registerPlugin({id:c,name:c,isReady:!0})},bootstrap(n){n.getPlugin("content-manager").apis.addEditViewSidePanel([ne])},async registerTrads({locales:n}){return Promise.all(n.map(async r=>{try{const{default:s}=await K(Object.assign({"./translations/en.json":()=>Promise.resolve().then(()=>require("../_chunks/en-BHfx2OWt.js"))}),`./translations/${r}.json`,3);return{data:s,locale:r}}catch{return{data:{},locale:r}}}))}};exports.PLUGIN_ID=c;exports.default=re;exports.registerDiffRenderer=V;
@@ -1,71 +1,72 @@
1
1
  import { jsx as t, jsxs as o } from "react/jsx-runtime";
2
- import * as d from "react";
3
- import { useFetchClient as K } from "@strapi/admin/strapi-admin";
4
- import { Modal as g, Flex as c, Loader as $, Typography as r, Box as f, Button as v, Badge as V, TextButton as J, Divider as Z, Dialog as p } from "@strapi/design-system";
5
- const Q = (e, n, a) => {
6
- const s = e[n];
7
- return s ? typeof s == "function" ? s() : Promise.resolve(s) : new Promise((k, u) => {
2
+ import * as c from "react";
3
+ import { useFetchClient as Z } from "@strapi/admin/strapi-admin";
4
+ import { Modal as g, Flex as u, Loader as O, Typography as i, Box as w, Button as y, Badge as Q, TextButton as X, IconButton as Y, Divider as ee, Dialog as m } from "@strapi/design-system";
5
+ import { Pin as te } from "@strapi/icons";
6
+ const ne = (e, r, a) => {
7
+ const s = e[r];
8
+ return s ? typeof s == "function" ? s() : Promise.resolve(s) : new Promise((T, b) => {
8
9
  (typeof queueMicrotask == "function" ? queueMicrotask : setTimeout)(
9
- u.bind(
10
+ b.bind(
10
11
  null,
11
12
  new Error(
12
- "Unknown variable dynamic import: " + n + (n.split("/").length !== a ? ". Note that variables only represent file names one level deep." : "")
13
+ "Unknown variable dynamic import: " + r + (r.split("/").length !== a ? ". Note that variables only represent file names one level deep." : "")
13
14
  )
14
15
  )
15
16
  );
16
17
  });
17
- }, N = /* @__PURE__ */ new Map(), de = (e, n) => {
18
- N.set(e, n);
19
- }, L = (e) => N.get(e), X = ({ spans: e }) => /* @__PURE__ */ t(r, { variant: "omega", tag: "p", children: e.map((n, a) => n.op === "equal" ? /* @__PURE__ */ t(r, { variant: "omega", textColor: "neutral600", children: n.value }, a) : /* @__PURE__ */ t(
20
- r,
18
+ }, E = /* @__PURE__ */ new Map(), me = (e, r) => {
19
+ E.set(e, r);
20
+ }, N = (e) => E.get(e), re = ({ spans: e }) => /* @__PURE__ */ t(i, { variant: "omega", tag: "p", children: e.map((r, a) => r.op === "equal" ? /* @__PURE__ */ t(i, { variant: "omega", textColor: "neutral600", children: r.value }, a) : /* @__PURE__ */ t(
21
+ i,
21
22
  {
22
23
  variant: "omega",
23
- textColor: n.op === "added" ? "success600" : "danger600",
24
- style: n.op === "removed" ? { textDecoration: "line-through" } : { fontWeight: 600 },
25
- children: n.value
24
+ textColor: r.op === "added" ? "success600" : "danger600",
25
+ style: r.op === "removed" ? { textDecoration: "line-through" } : { fontWeight: 600 },
26
+ children: r.value
26
27
  },
27
28
  a
28
- )) }), F = (e) => e == null || e === "" ? "(empty)" : typeof e == "object" ? JSON.stringify(e) : String(e), Y = ({ change: e }) => {
29
- const n = (e.customField ? L(e.customField) : void 0) ?? L(e.type);
30
- return n ? /* @__PURE__ */ t(n, { change: e }) : e.spans ? /* @__PURE__ */ t(X, { spans: e.spans }) : e.linked || e.unlinked ? /* @__PURE__ */ o(c, { direction: "column", alignItems: "flex-start", gap: 1, children: [
31
- e.linked?.length ? /* @__PURE__ */ o(r, { variant: "pi", textColor: "success600", children: [
29
+ )) }), A = (e) => e == null || e === "" ? "(empty)" : typeof e == "object" ? JSON.stringify(e) : String(e), ie = ({ change: e }) => {
30
+ const r = (e.customField ? N(e.customField) : void 0) ?? N(e.type);
31
+ return r ? /* @__PURE__ */ t(r, { change: e }) : e.spans ? /* @__PURE__ */ t(re, { spans: e.spans }) : e.linked || e.unlinked ? /* @__PURE__ */ o(u, { direction: "column", alignItems: "flex-start", gap: 1, children: [
32
+ e.linked?.length ? /* @__PURE__ */ o(i, { variant: "pi", textColor: "success600", children: [
32
33
  "+ ",
33
34
  e.linked.length,
34
35
  " linked"
35
36
  ] }) : null,
36
- e.unlinked?.length ? /* @__PURE__ */ o(r, { variant: "pi", textColor: "danger600", children: [
37
+ e.unlinked?.length ? /* @__PURE__ */ o(i, { variant: "pi", textColor: "danger600", children: [
37
38
  "− ",
38
39
  e.unlinked.length,
39
40
  " unlinked"
40
41
  ] }) : null
41
- ] }) : e.before !== void 0 || e.after !== void 0 ? /* @__PURE__ */ o(c, { gap: 2, alignItems: "center", wrap: "wrap", children: [
42
+ ] }) : e.before !== void 0 || e.after !== void 0 ? /* @__PURE__ */ o(u, { gap: 2, alignItems: "center", wrap: "wrap", children: [
42
43
  /* @__PURE__ */ t(
43
- r,
44
+ i,
44
45
  {
45
46
  variant: "omega",
46
47
  textColor: "danger600",
47
48
  style: { textDecoration: "line-through" },
48
- children: F(e.before)
49
+ children: A(e.before)
49
50
  }
50
51
  ),
51
- /* @__PURE__ */ t(r, { variant: "omega", textColor: "neutral500", children: "→" }),
52
- /* @__PURE__ */ t(r, { variant: "omega", textColor: "success600", children: F(e.after) })
53
- ] }) : /* @__PURE__ */ t(r, { variant: "pi", textColor: "neutral600", children: "Formatting or structure changed; the text is the same." });
54
- }, ee = ({
52
+ /* @__PURE__ */ t(i, { variant: "omega", textColor: "neutral500", children: "→" }),
53
+ /* @__PURE__ */ t(i, { variant: "omega", textColor: "success600", children: A(e.after) })
54
+ ] }) : /* @__PURE__ */ t(i, { variant: "pi", textColor: "neutral600", children: "Formatting or structure changed; the text is the same." });
55
+ }, oe = ({
55
56
  diff: e,
56
- loading: n,
57
+ loading: r,
57
58
  onClose: a
58
59
  }) => /* @__PURE__ */ t(g.Root, { open: !0, onOpenChange: a, children: /* @__PURE__ */ o(g.Content, { children: [
59
60
  /* @__PURE__ */ t(g.Header, { children: /* @__PURE__ */ t(g.Title, { children: "What changed" }) }),
60
- /* @__PURE__ */ t(g.Body, { children: n ? /* @__PURE__ */ t(c, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ t($, { small: !0, children: "Comparing" }) }) : e ? /* @__PURE__ */ o(c, { direction: "column", alignItems: "stretch", gap: 4, children: [
61
- /* @__PURE__ */ t(r, { variant: "pi", textColor: "neutral600", children: e.from ? "Compared with the version saved just before it." : "This is the earliest version of the document, so everything in it is new." }),
62
- e.identical ? /* @__PURE__ */ t(r, { textColor: "neutral600", children: "Nothing changed between these two versions." }) : e.changes.map((s) => /* @__PURE__ */ o(f, { children: [
63
- /* @__PURE__ */ t(r, { variant: "sigma", textColor: "neutral600", children: s.field }),
64
- /* @__PURE__ */ t(f, { paddingTop: 1, children: /* @__PURE__ */ t(Y, { change: s }) })
61
+ /* @__PURE__ */ t(g.Body, { children: r ? /* @__PURE__ */ t(u, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ t(O, { small: !0, children: "Comparing" }) }) : e ? /* @__PURE__ */ o(u, { direction: "column", alignItems: "stretch", gap: 4, children: [
62
+ /* @__PURE__ */ t(i, { variant: "pi", textColor: "neutral600", children: e.from ? "Compared with the version saved just before it." : "This is the earliest version of the document, so everything in it is new." }),
63
+ e.identical ? /* @__PURE__ */ t(i, { textColor: "neutral600", children: "Nothing changed between these two versions." }) : e.changes.map((s) => /* @__PURE__ */ o(w, { children: [
64
+ /* @__PURE__ */ t(i, { variant: "sigma", textColor: "neutral600", children: s.field }),
65
+ /* @__PURE__ */ t(w, { paddingTop: 1, children: /* @__PURE__ */ t(ie, { change: s }) })
65
66
  ] }, s.field))
66
- ] }) : /* @__PURE__ */ t(r, { textColor: "danger600", children: "Could not compare these versions." }) }),
67
- /* @__PURE__ */ t(g.Footer, { children: /* @__PURE__ */ t(v, { variant: "tertiary", onClick: a, children: "Close" }) })
68
- ] }) }), m = "rewind", te = {
67
+ ] }) : /* @__PURE__ */ t(i, { textColor: "danger600", children: "Could not compare these versions." }) }),
68
+ /* @__PURE__ */ t(g.Footer, { children: /* @__PURE__ */ t(y, { variant: "tertiary", onClick: a, children: "Close" }) })
69
+ ] }) }), p = "rewind", ae = {
69
70
  create: "Created",
70
71
  update: "Edited",
71
72
  clone: "Cloned",
@@ -73,12 +74,12 @@ const Q = (e, n, a) => {
73
74
  unpublish: "Unpublished",
74
75
  discardDraft: "Draft discarded",
75
76
  restore: "Replaced by restore"
76
- }, P = /* @__PURE__ */ new Set(["publish", "unpublish", "discardDraft", "restore"]), ne = 10, re = (e) => {
77
- const n = new Date(e), a = Math.round((Date.now() - n.getTime()) / 6e4);
78
- return a < 1 ? "just now" : a < 60 ? `${a} min ago` : a < 60 * 24 ? `${Math.round(a / 60)} h ago` : n.toLocaleString();
79
- }, ie = ({
77
+ }, B = /* @__PURE__ */ new Set(["publish", "unpublish", "discardDraft", "restore"]), le = 10, se = (e) => {
78
+ const r = new Date(e), a = Math.round((Date.now() - r.getTime()) / 6e4);
79
+ return a < 1 ? "just now" : a < 60 ? `${a} min ago` : a < 60 * 24 ? `${Math.round(a / 60)} h ago` : r.toLocaleString();
80
+ }, de = ({
80
81
  model: e,
81
- documentId: n,
82
+ documentId: r,
82
83
  /**
83
84
  * Changes whenever the Content Manager re-reads the document, which is what
84
85
  * it does after a save. Used purely as a refresh signal: without it the panel
@@ -87,102 +88,125 @@ const Q = (e, n, a) => {
87
88
  */
88
89
  updatedAt: a
89
90
  }) => {
90
- const { get: s, post: k } = K(), [u, A] = d.useState([]), [O, B] = d.useState(0), [E, M] = d.useState(1), [S, R] = d.useState(!0), [C, w] = d.useState(null), [l, y] = d.useState(null), [T, D] = d.useState(!1), [_, I] = d.useState(!1), [H, x] = d.useState(null), [W, j] = d.useState(!1), b = d.useCallback(
91
- async (i) => {
92
- R(!0), w(null);
91
+ const { get: s, post: T, put: b } = Z(), [f, k] = c.useState([]), [M, _] = c.useState(0), [H, W] = c.useState(1), [D, j] = c.useState(!0), [x, C] = c.useState(null), [l, S] = c.useState(null), [L, P] = c.useState(!1), [z, $] = c.useState(!1), [G, R] = c.useState(null), [U, F] = c.useState(!1), I = c.useCallback(
92
+ async (n) => {
93
+ j(!0), C(null);
93
94
  try {
94
- const { data: h } = await s(`/${m}/versions`, {
95
+ const { data: d } = await s(`/${p}/versions`, {
95
96
  params: {
96
97
  contentType: e,
97
- documentId: n,
98
- page: i,
99
- pageSize: ne
98
+ documentId: r,
99
+ page: n,
100
+ pageSize: le
100
101
  }
101
102
  });
102
- A((U) => i === 1 ? h.data : [...U, ...h.data]), B(h.meta.total), M(i);
103
+ k((v) => n === 1 ? d.data : [...v, ...d.data]), _(d.meta.total), W(n);
103
104
  } catch {
104
- w("Could not load the history for this document.");
105
+ C("Could not load the history for this document.");
105
106
  } finally {
106
- R(!1);
107
+ j(!1);
107
108
  }
108
109
  },
109
- [s, e, n]
110
+ [s, e, r]
110
111
  );
111
- d.useEffect(() => {
112
- b(1);
113
- }, [b, a]);
114
- const G = async (i) => {
115
- I(!0), j(!0), x(null);
112
+ c.useEffect(() => {
113
+ I(1);
114
+ }, [I, a]);
115
+ const q = async (n) => {
116
+ $(!0), F(!0), R(null);
116
117
  try {
117
- const { data: h } = await s(
118
- `/${m}/versions/${i.id}/diff`
118
+ const { data: d } = await s(
119
+ `/${p}/versions/${n.id}/diff`
119
120
  );
120
- x(h.data);
121
+ R(d.data);
121
122
  } catch {
122
- x(null);
123
+ R(null);
123
124
  } finally {
124
- j(!1);
125
+ F(!1);
125
126
  }
126
- }, q = async (i) => {
127
+ }, K = async (n) => {
128
+ const d = !n.pinned;
129
+ k((v) => v.map((h) => h.id === n.id ? { ...h, pinned: d } : h));
127
130
  try {
128
- const { data: h } = await s(
129
- `/${m}/versions/${i.id}/preview`
131
+ await b(`/${p}/versions/${n.id}/pin`, { pinned: d });
132
+ } catch {
133
+ k(
134
+ (v) => v.map((h) => h.id === n.id ? { ...h, pinned: !d } : h)
135
+ ), C(d ? "Could not pin that version." : "Could not unpin that version.");
136
+ }
137
+ }, V = async (n) => {
138
+ try {
139
+ const { data: d } = await s(
140
+ `/${p}/versions/${n.id}/preview`
130
141
  );
131
- y({ version: i, preview: h.data });
142
+ S({ version: n, preview: d.data });
132
143
  } catch {
133
- w("Could not work out what restoring this version would change.");
144
+ C("Could not work out what restoring this version would change.");
134
145
  }
135
- }, z = async () => {
146
+ }, J = async () => {
136
147
  if (l) {
137
- D(!0);
148
+ P(!0);
138
149
  try {
139
- await k(`/${m}/versions/${l.version.id}/restore`, {}), window.location.reload();
150
+ await T(`/${p}/versions/${l.version.id}/restore`, {}), window.location.reload();
140
151
  } catch {
141
- w("The restore failed. Nothing was changed."), D(!1), y(null);
152
+ C("The restore failed. Nothing was changed."), P(!1), S(null);
142
153
  }
143
154
  }
144
155
  };
145
- return S && u.length === 0 ? /* @__PURE__ */ t(c, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ t($, { small: !0, children: "Loading history" }) }) : C && u.length === 0 ? /* @__PURE__ */ t(r, { variant: "pi", textColor: "danger600", children: C }) : u.length === 0 ? /* @__PURE__ */ t(r, { variant: "pi", textColor: "neutral600", children: "No versions yet. The next time this document is saved, a version will appear here." }) : /* @__PURE__ */ o(c, { direction: "column", alignItems: "stretch", gap: 2, width: "100%", children: [
146
- u.map((i) => /* @__PURE__ */ o(f, { paddingBottom: 2, children: [
147
- /* @__PURE__ */ o(c, { justifyContent: "space-between", alignItems: "center", gap: 2, children: [
148
- /* @__PURE__ */ o(c, { direction: "column", alignItems: "flex-start", gap: 1, grow: 1, minWidth: 0, children: [
149
- /* @__PURE__ */ o(c, { gap: 2, alignItems: "center", children: [
156
+ return D && f.length === 0 ? /* @__PURE__ */ t(u, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ t(O, { small: !0, children: "Loading history" }) }) : x && f.length === 0 ? /* @__PURE__ */ t(i, { variant: "pi", textColor: "danger600", children: x }) : f.length === 0 ? /* @__PURE__ */ t(i, { variant: "pi", textColor: "neutral600", children: "No versions yet. The next time this document is saved, a version will appear here." }) : /* @__PURE__ */ o(u, { direction: "column", alignItems: "stretch", gap: 2, width: "100%", children: [
157
+ f.map((n) => /* @__PURE__ */ o(w, { paddingBottom: 2, children: [
158
+ /* @__PURE__ */ o(u, { justifyContent: "space-between", alignItems: "center", gap: 2, children: [
159
+ /* @__PURE__ */ o(u, { direction: "column", alignItems: "flex-start", gap: 1, grow: 1, minWidth: 0, children: [
160
+ /* @__PURE__ */ o(u, { gap: 2, alignItems: "center", children: [
150
161
  /* @__PURE__ */ t(
151
- V,
162
+ Q,
152
163
  {
153
- backgroundColor: P.has(i.origin) ? "primary100" : "neutral150",
154
- textColor: P.has(i.origin) ? "primary600" : "neutral600",
155
- children: te[i.origin] ?? i.origin
164
+ backgroundColor: B.has(n.origin) ? "primary100" : "neutral150",
165
+ textColor: B.has(n.origin) ? "primary600" : "neutral600",
166
+ children: ae[n.origin] ?? n.origin
156
167
  }
157
168
  ),
158
- i.locale ? /* @__PURE__ */ t(r, { variant: "pi", textColor: "neutral500", children: i.locale }) : null
169
+ n.locale ? /* @__PURE__ */ t(i, { variant: "pi", textColor: "neutral500", children: n.locale }) : null
159
170
  ] }),
160
- i.label ? /* @__PURE__ */ t(f, { width: "100%", children: /* @__PURE__ */ t(r, { variant: "omega", textColor: "neutral800", ellipsis: !0, children: i.label }) }) : null,
161
- /* @__PURE__ */ o(r, { variant: "pi", textColor: "neutral600", children: [
162
- re(i.createdAt),
163
- i.user ? ` · ${i.user.name}` : ""
171
+ n.label ? /* @__PURE__ */ t(w, { width: "100%", children: /* @__PURE__ */ t(i, { variant: "omega", textColor: "neutral800", ellipsis: !0, children: n.label }) }) : null,
172
+ /* @__PURE__ */ o(i, { variant: "pi", textColor: "neutral600", children: [
173
+ se(n.createdAt),
174
+ n.user ? ` · ${n.user.name}` : "",
175
+ n.pinned ? /* @__PURE__ */ t(i, { variant: "pi", textColor: "primary600", fontWeight: "bold", children: " · Pinned" }) : null
164
176
  ] }),
165
- /* @__PURE__ */ t(J, { onClick: () => G(i), children: "What changed" })
177
+ /* @__PURE__ */ t(X, { onClick: () => q(n), children: "What changed" })
166
178
  ] }),
167
- /* @__PURE__ */ t(f, { shrink: 0, children: /* @__PURE__ */ t(v, { size: "S", variant: "tertiary", onClick: () => q(i), children: "Restore" }) })
179
+ /* @__PURE__ */ o(u, { shrink: 0, gap: 1, alignItems: "center", children: [
180
+ /* @__PURE__ */ t(
181
+ Y,
182
+ {
183
+ size: "S",
184
+ variant: "ghost",
185
+ label: n.pinned ? "Unpin this version, so tidying up can remove it" : "Pin this version, so tidying up never removes it",
186
+ onClick: () => K(n),
187
+ children: /* @__PURE__ */ t(te, { fill: n.pinned ? "primary600" : "neutral500" })
188
+ }
189
+ ),
190
+ /* @__PURE__ */ t(y, { size: "S", variant: "tertiary", onClick: () => V(n), children: "Restore" })
191
+ ] })
168
192
  ] }),
169
- /* @__PURE__ */ t(f, { paddingTop: 2, children: /* @__PURE__ */ t(Z, {}) })
170
- ] }, i.id)),
171
- u.length < O ? /* @__PURE__ */ t(v, { size: "S", variant: "tertiary", loading: S, onClick: () => b(E + 1), children: "Show older versions" }) : null,
172
- C ? /* @__PURE__ */ t(r, { variant: "pi", textColor: "danger600", children: C }) : null,
173
- _ ? /* @__PURE__ */ t(
174
- ee,
193
+ /* @__PURE__ */ t(w, { paddingTop: 2, children: /* @__PURE__ */ t(ee, {}) })
194
+ ] }, n.id)),
195
+ f.length < M ? /* @__PURE__ */ t(y, { size: "S", variant: "tertiary", loading: D, onClick: () => I(H + 1), children: "Show older versions" }) : null,
196
+ x ? /* @__PURE__ */ t(i, { variant: "pi", textColor: "danger600", children: x }) : null,
197
+ z ? /* @__PURE__ */ t(
198
+ oe,
175
199
  {
176
- diff: H,
177
- loading: W,
178
- onClose: () => I(!1)
200
+ diff: G,
201
+ loading: U,
202
+ onClose: () => $(!1)
179
203
  }
180
204
  ) : null,
181
- /* @__PURE__ */ t(p.Root, { open: l !== null, onOpenChange: () => y(null), children: /* @__PURE__ */ o(p.Content, { children: [
182
- /* @__PURE__ */ t(p.Header, { children: "Restore this version?" }),
183
- /* @__PURE__ */ t(p.Body, { children: /* @__PURE__ */ o(c, { direction: "column", alignItems: "flex-start", gap: 2, children: [
184
- /* @__PURE__ */ t(r, { children: "The current draft is saved as a version first, so this can be undone. The published version is not touched." }),
185
- l?.preview.crossLocaleFields.length ? /* @__PURE__ */ o(r, { textColor: "danger600", children: [
205
+ /* @__PURE__ */ t(m.Root, { open: l !== null, onOpenChange: () => S(null), children: /* @__PURE__ */ o(m.Content, { children: [
206
+ /* @__PURE__ */ t(m.Header, { children: "Restore this version?" }),
207
+ /* @__PURE__ */ t(m.Body, { children: /* @__PURE__ */ o(u, { direction: "column", alignItems: "flex-start", gap: 2, children: [
208
+ /* @__PURE__ */ t(i, { children: "The current draft is saved as a version first, so this can be undone. The published version is not touched." }),
209
+ l?.preview.crossLocaleFields.length ? /* @__PURE__ */ o(i, { textColor: "danger600", children: [
186
210
  l.preview.crossLocaleFields.join(", "),
187
211
  " ",
188
212
  l.preview.crossLocaleFields.length === 1 ? "is" : "are",
@@ -197,58 +221,58 @@ const Q = (e, n, a) => {
197
221
  l.preview.affectedLocales.join(", "),
198
222
  "."
199
223
  ] }) : null,
200
- l?.preview.fieldsKeptAsIs.length ? /* @__PURE__ */ o(r, { variant: "pi", textColor: "neutral600", children: [
224
+ l?.preview.fieldsKeptAsIs.length ? /* @__PURE__ */ o(i, { variant: "pi", textColor: "neutral600", children: [
201
225
  "Added since this version and left untouched:",
202
226
  " ",
203
227
  l.preview.fieldsKeptAsIs.join(", "),
204
228
  "."
205
229
  ] }) : null,
206
- l?.preview.fieldsDropped.length ? /* @__PURE__ */ o(r, { variant: "pi", textColor: "neutral600", children: [
230
+ l?.preview.fieldsDropped.length ? /* @__PURE__ */ o(i, { variant: "pi", textColor: "neutral600", children: [
207
231
  "No longer in the model and skipped: ",
208
232
  l.preview.fieldsDropped.join(", "),
209
233
  "."
210
234
  ] }) : null,
211
- l?.preview.brokenRelations.length || l?.preview.missingMedia.length ? /* @__PURE__ */ o(r, { variant: "pi", textColor: "warning600", children: [
235
+ l?.preview.brokenRelations.length || l?.preview.missingMedia.length ? /* @__PURE__ */ o(i, { variant: "pi", textColor: "warning600", children: [
212
236
  (l.preview.brokenRelations.length ?? 0) + (l.preview.missingMedia.length ?? 0),
213
237
  " ",
214
238
  "linked item(s) no longer exist and will be left out."
215
239
  ] }) : null
216
240
  ] }) }),
217
- /* @__PURE__ */ o(p.Footer, { children: [
218
- /* @__PURE__ */ t(p.Cancel, { children: /* @__PURE__ */ t(v, { variant: "tertiary", disabled: T, children: "Cancel" }) }),
219
- /* @__PURE__ */ t(v, { onClick: z, loading: T, children: "Restore" })
241
+ /* @__PURE__ */ o(m.Footer, { children: [
242
+ /* @__PURE__ */ t(m.Cancel, { children: /* @__PURE__ */ t(y, { variant: "tertiary", disabled: L, children: "Cancel" }) }),
243
+ /* @__PURE__ */ t(y, { onClick: J, loading: L, children: "Restore" })
220
244
  ] })
221
245
  ] }) })
222
246
  ] });
223
- }, oe = ({
247
+ }, ce = ({
224
248
  model: e,
225
- documentId: n,
249
+ documentId: r,
226
250
  document: a
227
- }) => n ? {
251
+ }) => r ? {
228
252
  title: "History",
229
- content: /* @__PURE__ */ t(ie, { model: e, documentId: n, updatedAt: a?.updatedAt })
230
- } : null, ce = {
253
+ content: /* @__PURE__ */ t(de, { model: e, documentId: r, updatedAt: a?.updatedAt })
254
+ } : null, fe = {
231
255
  register(e) {
232
- e.registerPlugin({ id: m, name: m, isReady: !0 });
256
+ e.registerPlugin({ id: p, name: p, isReady: !0 });
233
257
  },
234
258
  bootstrap(e) {
235
- e.getPlugin("content-manager").apis.addEditViewSidePanel([oe]);
259
+ e.getPlugin("content-manager").apis.addEditViewSidePanel([ce]);
236
260
  },
237
261
  async registerTrads({ locales: e }) {
238
262
  return Promise.all(
239
- e.map(async (n) => {
263
+ e.map(async (r) => {
240
264
  try {
241
- const { default: a } = await Q(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("../_chunks/en-DCctmu-a.mjs") }), `./translations/${n}.json`, 3);
242
- return { data: a, locale: n };
265
+ const { default: a } = await ne(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("../_chunks/en-DCctmu-a.mjs") }), `./translations/${r}.json`, 3);
266
+ return { data: a, locale: r };
243
267
  } catch {
244
- return { data: {}, locale: n };
268
+ return { data: {}, locale: r };
245
269
  }
246
270
  })
247
271
  );
248
272
  }
249
273
  };
250
274
  export {
251
- m as PLUGIN_ID,
252
- ce as default,
253
- de as registerDiffRenderer
275
+ p as PLUGIN_ID,
276
+ fe as default,
277
+ me as registerDiffRenderer
254
278
  };
@@ -42,7 +42,7 @@ export type DiffRenderer = React.ComponentType<{
42
42
  * Keyed by the attribute's `type`, or by a custom field's uid.
43
43
  *
44
44
  * `MyBlocksDiff` above is the caller's own component. No @qkix package exports
45
- * one today — this is the hook for writing it, not a wiring instruction for
45
+ * one today - this is the hook for writing it, not a wiring instruction for
46
46
  * something that already exists. Without a renderer registered, the field falls
47
47
  * back to the generic text diff.
48
48
  */