@elytracms/studio 0.0.101 → 0.0.103
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/convex/asset-meta-patch.test.ts +7 -27
- package/convex/assets.ts +6 -41
- package/convex/cms.ts +2 -29
- package/convex/delivery.ts +2 -2
- package/convex/ingest.test.ts +54 -21
- package/convex/ingest.ts +52 -15
- package/convex/record-functions.ts +15 -82
- package/convex/redirects.ts +11 -116
- package/convex/schema.ts +4 -23
- package/convex/triggers.ts +7 -14
- package/dist/components/ai/ai-chat-panel.js +8 -5
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/ai/translate-document-dialog.d.ts +43 -0
- package/dist/components/ai/translate-document-dialog.js +104 -36
- package/dist/components/ai/translate-document-dialog.js.map +1 -1
- package/dist/components/ai/translate-skipped.d.ts +19 -3
- package/dist/components/ai/translate-skipped.js +34 -7
- package/dist/components/ai/translate-skipped.js.map +1 -1
- package/dist/components/assets/asset-detail.js +1 -2
- package/dist/components/assets/asset-detail.js.map +1 -1
- package/dist/components/assets/asset-metadata.d.ts +2 -9
- package/dist/components/assets/asset-metadata.js +3 -10
- package/dist/components/assets/asset-metadata.js.map +1 -1
- package/dist/components/assets/asset-search.js +1 -1
- package/dist/components/assets/asset-search.js.map +1 -1
- package/dist/components/assets/asset-selection.js +2 -2
- package/dist/components/assets/asset-selection.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.d.ts +2 -3
- package/dist/components/assets/assets-collection-view-source.js +2 -3
- package/dist/components/assets/assets-collection-view-source.js.map +1 -1
- package/dist/components/builder/__fixtures__/reuse-race.d.ts +30 -0
- package/dist/components/builder/__fixtures__/reuse-race.js +77 -0
- package/dist/components/builder/__fixtures__/reuse-race.js.map +1 -0
- package/dist/components/builder/composition-field-editor.js +3 -3
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/reuse-context.d.ts +2 -1
- package/dist/components/builder/reuse-context.js +5 -20
- package/dist/components/builder/reuse-context.js.map +1 -1
- package/dist/components/builder/reuse.d.ts +18 -11
- package/dist/components/builder/reuse.js +12 -14
- package/dist/components/builder/reuse.js.map +1 -1
- package/dist/components/cms/asset-field.js +1 -3
- package/dist/components/cms/asset-field.js.map +1 -1
- package/dist/components/cms/cell.d.ts +2 -2
- package/dist/components/cms/cell.js +8 -18
- package/dist/components/cms/cell.js.map +1 -1
- package/dist/components/cms/detail-editor.js +2 -1
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/field-control.js +13 -1
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/lib/ai/chat-context.d.ts +6 -2
- package/dist/lib/ai/chat-context.js +11 -5
- package/dist/lib/ai/chat-context.js.map +1 -1
- package/dist/lib/ai/knowledge.d.ts +9 -9
- package/dist/lib/ai/knowledge.js +22 -0
- package/dist/lib/ai/knowledge.js.map +1 -1
- package/dist/lib/ai/tools.d.ts +8 -1
- package/dist/lib/ai/tools.js +33 -15
- package/dist/lib/ai/tools.js.map +1 -1
- package/dist/lib/ai/translate-apply.d.ts +69 -0
- package/dist/lib/ai/translate-apply.js +36 -0
- package/dist/lib/ai/translate-apply.js.map +1 -0
- package/dist/lib/ai/translate-document.d.ts +101 -7
- package/dist/lib/ai/translate-document.js +189 -54
- package/dist/lib/ai/translate-document.js.map +1 -1
- package/dist/lib/ai/update-fields.js +5 -12
- package/dist/lib/ai/update-fields.js.map +1 -1
- package/dist/lib/ai/writer-form.d.ts +93 -1
- package/dist/lib/ai/writer-form.js +389 -37
- package/dist/lib/ai/writer-form.js.map +1 -1
- package/dist/lib/assets/upload.d.ts +1 -2
- package/dist/lib/assets/upload.js +1 -2
- package/dist/lib/assets/upload.js.map +1 -1
- package/dist/lib/engine-version.d.ts +1 -1
- package/dist/lib/engine-version.js +1 -1
- package/dist/lib/engine-version.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +54 -4
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +44 -1
- package/dist/lib/i18n/messages.en.js +58 -4
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/cms-persistence.d.ts +20 -10
- package/dist/lib/operations/cms-persistence.js +11 -3
- package/dist/lib/operations/cms-persistence.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.js +7 -2
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/package.json +3 -3
- package/convex/asset-alt-migration.test.ts +0 -127
- package/convex/redirect-migration.test.ts +0 -205
- package/convex/version-kind-stamping.test.ts +0 -118
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composition-field-editor.js","sourceRoot":"","sources":["../../../src/components/builder/composition-field-editor.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAE5D,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AACnF,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAE9F,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAI5E,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAChH,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAA;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAElD,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA;AAE/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAEzD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AAEjE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE1C,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAEpG,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAA;AAEtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAE9C;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,aAAa,GAAG,kBAAkB,CAAA;AAexC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAc;IAClD,OAAO,eAAe,CAAC,KAAK,CAA0B,CAAA;AACxD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAA+B,EAC/B,SAAwC;IAExC,OAAO,eAAe,CAAC,KAAK,EAAE,SAAS,CAAC,CAAA;AAC1C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAqB;IACzD,0EAA0E;IAC1E,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,KAAK,CAAE,EAAE,CAAA;AACpE,CAAC;AAED;;;;;;GAMG;AACH,SAAS,kBAAkB,CAAC,KAA+B,EAAE,gBAAwB;IACnF,OAAO;QACL,EAAE,EAAE,aAAa;QACjB,WAAW,EAAE,gBAAgB;QAC7B,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,EAAE;KACtC,CAAA;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAA2B;IAC3D,MAAM,OAAO,GAAiB,EAAE,CAAA;IAChC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,aAAa,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAA;IAChF,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAA;IACzE,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,iBAAiB,CAAC,EAAE,OAAO,EAAsC;IAC/E,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACrC,OAAO,CACL,6BAAiB,qBAAqB,EAAC,SAAS,EAAC,6CAA6C,YAC3F,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACpB,MAAC,GAAG,IAAW,GAAG,EAAC,GAAG,EAAC,KAAK,EAAC,QAAQ,EAAC,SAAS,EAAC,aAAa,aAC3D,KAAC,aAAa,yBAAa,SAAS,EAAC,yCAAyC,GAAG,EACjF,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,YACzB,SAAS,CAAC,GAAG,CAAC,GACV,KAJC,GAAG,CAKP,CACP,CAAC,GACE,CACP,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO,CACL,KAAC,KAAK,mBAAa,iBAAiB,EAAC,SAAS,EAAC,qDAAqD,YAClG,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,KAAK,EAAC,QAAQ,EAAC,SAAS,EAAC,aAAa,aACjD,KAAC,aAAa,yBAAa,SAAS,EAAC,uCAAuC,GAAG,EAC/E,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,YACzB,SAAS,CAAC,uBAAuB,CAAC,GAC9B,IACH,GACA,CACT,CAAA;AACH,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,EACrC,KAAK,EACL,KAAK,EACL,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,aAAa,EACb,SAAS,GAoCV;IACC,4EAA4E;IAC5E,gFAAgF;IAChF,gFAAgF;IAChF,4EAA4E;IAC5E,2EAA2E;IAC3E,wEAAwE;IACxE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAA;IAC/D,2EAA2E;IAC3E,MAAM,QAAQ,GAAG,iBAAiB,EAAE,CAAC,QAAQ,CAAA;IAC7C,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,cAAc,CAAC,CAAA;IACpD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAA;IAC9B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAA;IACtE,MAAM,WAAW,GAAG,OAAO,CACzB,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAClE,CAAC,OAAO,EAAE,KAAK,CAAC,CACjB,CAAA;IACD,yEAAyE;IACzE,8EAA8E;IAC9E,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAA;IAEvE,IAAI,CAAC,WAAW;QAAE,OAAO,KAAC,mBAAmB,KAAG,CAAA;IAEhD,OAAO,CACL,MAAC,eAAe,IACd,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,OAAO,CAAC,SAAS,EAC5B,KAAK,EAAE,UAAU,EACjB,UAAU,EAAE,QAAQ,CAAC,MAAM,EAC3B,MAAM,EAAE,MAAM,aAEd,KAAC,eAAe,IAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,GAAI,EAC/F,KAAC,oBAAoB,IAAC,KAAK,EAAE,OAAO,IAAI,IAAI,YAC1C,KAAC,aAAa,cAGZ,KAAC,gBAAgB,cACf,KAAC,sBAAsB,IACrB,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,aAAa,EAC5B,WAAW,EAAE,OAAO,EACpB,SAAS,EAAE,SAAS,GACpB,GACe,GACL,GACK,IACP,CACnB,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,SAAS,eAAe,CAAC,EACvB,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,QAAQ,GAMT;IACC,MAAM,OAAO,GAAG,UAAU,EAAE,CAAA;IAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,YAAY,CAAC,IAAI,YAAY,CAAA;IACpE,MAAM,SAAS,GAAG,OAAO,CAAC,SAA0C,CAAA;IACpE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAA;IAC/B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAA;IACjC,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAA;IACpC,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAA;IAC9B,mFAAmF;IACnF,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,qBAAqB,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAA;IAEhH,SAAS,CAAC,GAAG,EAAE;QACb,8EAA8E;QAC9E,+CAA+C;QAC/C,IAAI,QAAQ,IAAI,CAAC,MAAM;YAAE,OAAM;QAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;QACnC,IAAI,MAAM;YAAE,QAAQ,CAAC,MAAM,CAAC,CAAA;IAC9B,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAA;IAE3B,SAAS,CAAC,GAAG,EAAE;QACb,2EAA2E;QAC3E,8EAA8E;QAC9E,yEAAyE;QACzE,0EAA0E;QAC1E,wEAAwE;QACxE,uBAAuB;QACvB,IAAI,QAAQ;YAAE,OAAM;QACpB,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,OAAO,CAAC,CAAA;QAC3E,IAAI,IAAI;YAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAA;IAE1C,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,YAAY,GAA6B,EAAE,CAAA;AAEjD;;;;kCAIkC;AAClC,SAAS,cAAc,CAAC,EAAE,MAAM,EAA8B;IAC5D,MAAM,OAAO,GAAG,UAAU,EAAE,CAAA;IAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,KAAK,MAAM,CAAC,GAAG,CAAA;IAC9C,OAAO,CACL,KAAC,MAAM,IACL,IAAI,EAAC,IAAI,EACT,OAAO,EAAC,OAAO,gBACH,SAAS,CAAC,oCAAoC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,kBACtE,MAAM,EACpB,KAAK,EACH,MAAM,CAAC,EAAE,KAAK,SAAS;YACrB,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,MAAM,MAAM,CAAC,EAAE,IAAI;YACpC,CAAC,CAAC,SAAS,CAAC,mCAAmC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,EAE7E,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC,EAC3E,SAAS,EAAE,EAAE,CAAC,cAAc,EAAE,MAAM,IAAI,kCAAkC,CAAC,YAE1E,MAAM,CAAC,KAAK,GACN,CACV,CAAA;AACH,CAAC;AAED;uFACuF;AACvF,SAAS,kBAAkB;IACzB,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,kBAAkB,EAAE,CAAA;IACjD,OAAO,CACL,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,KAAK,EAAC,QAAQ,aACzB,KAAC,YAAY,cACV,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CACpB,KAAC,cAAc,IAAa,MAAM,EAAE,CAAC,IAAhB,CAAC,CAAC,GAAG,CAAe,CAC1C,CAAC,GACW,EACd,KAAK,CAAC,CAAC,CAAC,CACP,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,KAAK,EAAC,QAAQ,EAAC,SAAS,EAAC,oCAAoC,EAAC,KAAK,EAAE,KAAK,aACrF,KAAC,aAAa,yBAAa,SAAS,EAAC,UAAU,GAAG,EAClD,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,YAAE,SAAS,CAAC,sCAAsC,CAAC,GAAQ,IACtE,CACP,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAA;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAS,sBAAsB,CAAC,EAC9B,QAAQ,EACR,aAAa,EACb,WAAW,EACX,SAAS,GASV;IACC,MAAM,OAAO,GAAG,UAAU,EAAE,CAAA;IAC5B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAA;IACjC,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAuB,IAAI,CAAC,CAAA;IAC9E,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IACnD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,EAAE,CAAA;IAEjC,0EAA0E;IAC1E,4EAA4E;IAC5E,4EAA4E;IAC5E,MAAM,cAAc,GAAG,OAAO,CAC5B,GAAG,EAAE,CAAC,CAAC;QACL,EAAE,EAAE,YAAY;QAChB,KAAK,EAAE,SAAS,CAAC,kCAAkC,CAAC;QACpD,IAAI,EAAE,KAAC,iBAAiB,IAAC,SAAS,EAAC,UAAU,GAAG;QAChD,MAAM,EAAE,GAAG,EAAE,CAAC,CACZ,cAAK,SAAS,EAAE,EAAE,CAAC,gCAAgC,EAAE,QAAQ,IAAI,gCAAgC,CAAC,YAChG,KAAC,iBAAiB,KAAG,GACjB,CACP;KACF,CAAC,EACF,CAAC,QAAQ,CAAC,CACX,CAAA;IACD,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC,cAAc,EAAE,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,EAChD,CAAC,cAAc,EAAE,aAAa,CAAC,CAChC,CAAA;IAED,yEAAyE;IACzE,4EAA4E;IAC5E,6EAA6E;IAC7E,6EAA6E;IAC7E,8EAA8E;IAC9E,6EAA6E;IAC7E,6EAA6E;IAC7E,2DAA2D;IAC3D,SAAS,UAAU,CAAC,MAAc;QAChC,kFAAkF;QAClF,kFAAkF;QAClF,MAAM,GAAG,GAAG,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QACjD,IAAI,CAAC,GAAG;YAAE,OAAM;QAChB,IAAI,MAAM,KAAK,OAAO,CAAC,UAAU;YAAE,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;QAC3E,gBAAgB,CAAC,GAAG,CAAC,CAAA;QACrB,aAAa,CAAC,IAAI,CAAC,CAAA;IACrB,CAAC;IAED,gFAAgF;IAChF,gFAAgF;IAChF,iFAAiF;IACjF,kFAAkF;IAClF,uCAAuC;IACvC,MAAM,aAAa,GAAG,OAAO,CAC3B,GAAG,EAAE,CAAC,CAAC;QACL,YAAY,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC;QACtE,MAAM,EAAE,CAAC,MAAc,EAAE,QAAwB,EAAE,EAAE;YACnD,MAAM,GAAG,GAAG,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAA;YAC7D,IAAI,CAAC,GAAG;gBAAE,OAAM;YAChB,IAAI,MAAM,KAAK,OAAO,CAAC,UAAU;gBAAE,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;YAC3E,gBAAgB,CAAC,GAAG,CAAC,CAAA;YACrB,aAAa,CAAC,IAAI,CAAC,CAAA;QACrB,CAAC;KACF,CAAC,EACF,CAAC,OAAO,EAAE,QAAQ,CAAC,CACpB,CAAA;IAED,OAAO,CACL,KAAC,wBAAwB,IAAC,KAAK,EAAE,aAAa,YAC5C,eAAK,SAAS,EAAC,8BAA8B,aAC3C,cAAK,SAAS,EAAC,iCAAiC,YAC9C,MAAC,OAAO,eACN,MAAC,YAAY,eACX,KAAC,MAAM,IACL,IAAI,EAAC,SAAS,EACd,OAAO,EAAC,OAAO,gBACH,SAAS,CAAC,qBAAqB,CAAC,EAC5C,KAAK,EAAE,SAAS,CAAC,qBAAqB,CAAC,EACvC,QAAQ,EAAE,QAAQ,IAAI,CAAC,OAAO,CAAC,OAAO,EACtC,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,YAEzC,KAAC,KAAK,KAAG,GACF,EACT,KAAC,MAAM,IACL,IAAI,EAAC,SAAS,EACd,OAAO,EAAC,OAAO,gBACH,SAAS,CAAC,qBAAqB,CAAC,EAC5C,KAAK,EAAE,SAAS,CAAC,qBAAqB,CAAC,EACvC,QAAQ,EAAE,QAAQ,IAAI,CAAC,OAAO,CAAC,OAAO,EACtC,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,YAEzC,KAAC,KAAK,KAAG,GACF,IACI,EACf,KAAC,gBAAgB,KAAG,EACpB,MAAC,MAAM,IACL,IAAI,EAAC,IAAI,EACT,OAAO,EAAC,OAAO,EACf,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,IAAI,MAAM,CAAC,aAEvD,KAAC,IAAI,KAAG,OAAE,SAAS,CAAC,uBAAuB,CAAC,IACrC,EAIT,KAAC,MAAM,KAAG,EACV,KAAC,kBAAkB,KAAG,EACtB,KAAC,MAAM,KAAG,IACF,GACN,EAEN,eAAK,SAAS,EAAC,8FAA8F,aAC3G,KAAC,KAAK,IAAC,SAAS,EAAE,EAAE,CAAC,uBAAuB,EAAE,QAAQ,IAAI,gCAAgC,CAAC,YACzF,KAAC,QAAQ,IAAC,UAAU,EAAE,UAAU,GAAI,GAC9B,EACR,eAAK,SAAS,EAAE,EAAE,CAAC,qCAAqC,EAAE,QAAQ,IAAI,gCAAgC,CAAC,aACpG,SAAS,CAAC,CAAC,CAAC,cAAK,SAAS,EAAC,qCAAqC,YAAE,SAAS,GAAO,CAAC,CAAC,CAAC,IAAI,EAE1F,KAAC,sBAAsB,IACrB,OAAO,EAAE,OAAO,CAAC,OAAO,EACxB,UAAU,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,EAC/B,SAAS,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,GACnD,EACF,KAAC,iBAAiB,IAAC,OAAO,EAAE,WAAW,IAAI,EAAE,GAAI,EAIjD,KAAC,aAAa,IAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,iBAAiB,GAAI,IACtE,EACN,KAAC,gBAAgB,IAAC,KAAK,EAAE,KAAK,GAAI,IAC9B,EAEN,KAAC,UAAU,IAAC,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,GAAI,IACjF,GACmB,CAC5B,CAAA;AACH,CAAC","sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { AlertTriangle, Plus, Redo2, SlidersHorizontal, Undo2 } from 'lucide-react'\nimport { DEFAULT_SLOT, mergeBlockRoots, splitBlockRoots } from '@elytracms/core/project-graph'\nimport type { ComponentNode, PreservedSequenceItem, SplitBlockRoots } from '@elytracms/core/project-graph'\nimport { DRAFT_PERSPECTIVE, fieldAllowList } from '@elytracms/core/cms-core'\nimport type { BlocksFieldDef } from '@elytracms/core/cms-core'\nimport type { ComponentRegistry } from '@elytracms/core/component-registry'\nimport type { BlockNode, BlockSequenceValue } from '@elytracms/core/rich-text'\nimport { Button, Panel, Row, Spacer, Text, Toolbar, ToolbarGroup, ToolbarSeparator } from '@elytracms/studio/ui'\nimport { cn } from '../../lib/utils'\nimport { translate } from '../../lib/i18n'\nimport type { MessageKey } from '../../lib/i18n'\nimport { getStudioRegistry } from '../../lib/registry'\nimport { BuilderProvider, useBuilder } from './builder-store'\nimport type { CompositionFieldScope } from './builder-store'\nimport { createCanvasSync } from './canvas-rebase'\nimport type { CanvasFieldChange, CanvasLift } from './canvas-rebase'\nimport { CanvasCommitProvider } from './canvas-commit-boundary'\nimport type { CanvasCommitChannel } from './canvas-commit-boundary'\nimport { useCanvasViewports } from './canvas-breakpoints'\nimport type { ViewportOption } from './canvas-breakpoints'\nimport { BuilderCanvas } from './canvas'\nimport { PageTree } from './page-tree'\nimport { PropertyInspector } from './property-inspector'\nimport { InspectorSidebar } from '../inspector/inspector-sidebar'\nimport type { InspectorPane } from '../inspector/inspector-sidebar'\nimport { InsertMenu } from './insert-menu'\nimport type { InsertContext } from './insert-menu'\nimport { insertablePositions, resolveInsertContext, resolveInsertContextAt } from './insert-context'\nimport type { InsertPosition } from './insert-context'\nimport { InsertAffordanceProvider } from './insert-affordance-context'\nimport type { InsertAffordanceApi } from './insert-affordance-context'\nimport { PropSyncProvider } from './prop-sync-context'\nimport { ReuseProvider } from './reuse-context'\nimport { PlacementRefusalNotice } from './placement-refusal'\nimport { fieldLabel } from '../cms/cms-labels'\n\n/**\n * EC-188 — the block-field surface: one canvas engine mounted on a composition\n * **field value** (AD-12), not the project graph.\n *\n * The composition tree is wrapped in a synthetic single-page graph and edited by\n * a SCOPED `BuilderProvider` with **no `projectId`** (so the graph persister is a\n * no-op — builder-store.tsx). Every committed edit is lifted out via `onChange`\n * into the CMS document draft; the document's own Save persists it (the tree is\n * stored as document data, `v.any()`).\n *\n * EC-547 (EC-500 D1–D3): the stored value is the converged v2 block sequence\n * `{ version: 2, content: BlockNode[] }`. The canvas edits the sequence's\n * `elytra/component` nodes; typography nodes survive the session byte-for-byte\n * and keep their order relative to components on write-back (see\n * {@link splitCompositionValue} / {@link mergeCompositionValue}). Legacy bare\n * node/array values are still ACCEPTED on read during the staging window, but\n * every write emits v2. This reuses the real canvas + inspector +\n * blocks panel unchanged — the same EC-158–164 mechanics — without touching the\n * project-wide builder store. Surfaced on a dedicated \"Canvas\" fieldset tab in\n * the CMS detail editor so the page builder gets full screen real estate rather\n * than being crammed into the field list.\n */\n\nconst SYNTH_ROOT_ID = 'composition.root'\n\n/**\n * A sequence item the canvas does not natively edit (EC-547 / EC-500 D3):\n * typography nodes, unknown/future node kinds, even entries the v2 schema would\n * reject — the editor is not a validator, so they are carried BYTE-FOR-BYTE and\n * re-merged on write-back rather than parsed, rewritten, or dropped.\n * `componentPrefix` records how many component nodes preceded the item in the\n * original sequence — the anchor {@link mergeCompositionValue} splices by.\n */\nexport type PreservedBlockNode = PreservedSequenceItem<BlockNode>\n\n/** A field value split for canvas editing: component roots + everything else. */\nexport type SplitCompositionValue = SplitBlockRoots<BlockNode>\n\n/**\n * Split a stored blocks-field value into the component nodes the canvas edits\n * and the sequence items it must preserve (EC-547, the EC-500 round-trip law:\n * a projection may only rewrite nodes it natively edits). Typography items are\n * deliberately NOT surfaced as editable canvas blocks in v1 (EC-500 D3) — they\n * survive the editing session and keep their order on write-back.\n *\n * EC-576: the logic is core's {@link splitBlockRoots} — the ONE reader every\n * blocks writer shares. This is the canvas's typed view of it: project-graph\n * sits below rich-text and cannot name {@link BlockNode}, so the carried items\n * arrive `unknown`; they are typed here, not checked (preservation over parsing).\n */\nexport function splitCompositionValue(value: unknown): SplitCompositionValue {\n return splitBlockRoots(value) as SplitCompositionValue\n}\n\n/**\n * Merge canvas-edited component roots back into the stored sequence (EC-547).\n * The merge rule — preserved items anchored to their ordinal position between\n * components, every root tagged, the write always v2 — is core's\n * {@link mergeBlockRoots} (EC-576: the same merge every blocks writer goes\n * through, via `editBlockRoots`). Typed here as the rich-text value.\n */\nexport function mergeCompositionValue(\n roots: readonly ComponentNode[],\n preserved: readonly PreservedBlockNode[],\n): BlockSequenceValue {\n return mergeBlockRoots(roots, preserved)\n}\n\n/**\n * The field scope the editor's store carries (EC-554): the field's label (named\n * by refusal notices) and its EFFECTIVE top-level vocabulary (EC-584 — read through\n * the per-FieldType definition, `fieldAllowList`: the declared `allow`, else the\n * `blocks` default, any canvas-eligible component).\n */\nexport function compositionFieldScope(field: BlocksFieldDef): CompositionFieldScope {\n // A blocks field always has a vocabulary (its type declares the default).\n return { label: fieldLabel(field), allow: fieldAllowList(field)! }\n}\n\n/**\n * Wrap the field value's component roots in a synthetic root container (EC-287).\n * The builder store edits this single root-bearing composition directly; its\n * children are the field's component blocks, lifted back out via `onChange`.\n * EC-591: the root is the component that fills the `layout.stack` role — the\n * project's own, from its component list — never a hardcoded engine id.\n */\nfunction buildSyntheticRoot(roots: readonly ComponentNode[], stackComponentId: string): ComponentNode {\n return {\n id: SYNTH_ROOT_ID,\n componentId: stackComponentId,\n slots: { [DEFAULT_SLOT]: [...roots] },\n }\n}\n\n/**\n * The canvas notices for roles nobody in the component list fills (EC-591) — each\n * names the gesture that is OFF because of it, so a missing role is visible, never\n * a silent dead affordance. Only gestures the canvas actually has are listed: the\n * `layout.stack` role is the canvas itself (see {@link CompositionFieldEditor}),\n * `layout.grid` the wrap-beside gesture, `text` the seeded layout presets.\n * (`richText` — the typography projection, EC-549 — and `image` — asset drop —\n * have no canvas gesture yet, so there is nothing to switch off.) Pure.\n */\nexport function canvasRoleNotices(registry: ComponentRegistry): MessageKey[] {\n const notices: MessageKey[] = []\n if (!registry.roleComponent('layout.grid')) notices.push('builder.roles.noGrid')\n if (!registry.roleComponent('text')) notices.push('builder.roles.noText')\n return notices\n}\n\n/** The role notices above the canvas (EC-591) — a standing truth about the component list. */\nexport function CanvasRoleNotices({ notices }: { notices: readonly MessageKey[] }) {\n if (notices.length === 0) return null\n return (\n <div data-testid=\"canvas-role-notices\" className=\"shrink-0 border-b border-border bg-muted/40\">\n {notices.map((key) => (\n <Row key={key} gap=\"2\" align=\"center\" className=\"px-3 py-1.5\">\n <AlertTriangle aria-hidden className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <Text size=\"xs\" tone=\"muted\">\n {translate(key)}\n </Text>\n </Row>\n ))}\n </div>\n )\n}\n\n/**\n * The canvas in place of the editor when nobody fills `layout.stack` (EC-591): the\n * blocks of a composition field compose inside that component, so there is no\n * canvas without it. The value is not touched — nothing mounts that could write.\n */\nexport function NoStackCanvasNotice() {\n return (\n <Panel data-testid=\"canvas-no-stack\" className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <Row gap=\"2\" align=\"center\" className=\"max-w-prose\">\n <AlertTriangle aria-hidden className=\"size-4 shrink-0 text-muted-foreground\" />\n <Text size=\"sm\" tone=\"muted\">\n {translate('builder.roles.noStack')}\n </Text>\n </Row>\n </Panel>\n )\n}\n\nexport function CompositionFieldEditor({\n field,\n value,\n revision,\n change,\n onChange,\n commits,\n readOnly,\n trailingPanes,\n conflicts,\n}: {\n field: BlocksFieldDef\n /** The field's value at mount (the store is the source of truth while open). */\n value: unknown\n /**\n * EC-647 — the revision the draft was at when {@link value} was read (its value and its\n * revision travel together): the revision the canvas's first edit is authored at.\n */\n revision: number | undefined\n /**\n * EC-630 — the latest change of the field made UNDERNEATH the canvas (the document\n * draft reports it); the store rebases onto it — and, EC-647, onto its revision.\n */\n change?: CanvasFieldChange\n /**\n * The store's value, lifted, with the value it was last agreed on with the draft — the\n * base the canvas writer's delta is measured from — and the revision that was agreed\n * at (EC-647: the edit's authoring revision).\n */\n onChange: (lift: CanvasLift) => void\n /**\n * EC-630 — the document's committer, for the inspector controls' commit boundaries\n * (focus / blur / Enter at a position): a burst of typing commits on its control kind's\n * policy, never per keystroke. Absent → every edit commits as the canvas writes it.\n */\n commits?: CanvasCommitChannel\n readOnly: boolean\n /**\n * EC-595 A2 — the field's per-field conflicts (a refused node patch: \"changed by X —\n * take theirs / keep mine\"), rendered above the canvas: the canvas IS the field.\n */\n conflicts?: ReactNode\n /** Inspector panes the document editor appends after the canvas-supplied\n * `Properties` pane (EC-224 — the frame-owned `Versions` pane). */\n trailingPanes?: readonly InspectorPane[]\n}) {\n // Split the value ONCE at mount (EC-547): component nodes become the canvas\n // roots; every other sequence item is preserved aside for write-back. The store\n // is the source of truth while open: it lifts its edits out via onChange, and a\n // value that changes UNDERNEATH it (EC-630 — a colleague's patch, a write's\n // canonical row) is REBASED into it by `CompositionSync`, never remounted.\n // (BuilderProvider reads `composition` only in its lazy initial state.)\n const initial = useMemo(() => splitCompositionValue(value), [])\n // EC-591: the roles come from the project's component list (the registry).\n const registry = getStudioRegistry().registry\n const stack = registry.roleComponent('layout.stack')\n const layout = registry.layout\n const notices = useMemo(() => canvasRoleNotices(registry), [registry])\n const initialRoot = useMemo(\n () => (stack ? buildSyntheticRoot(initial.roots, stack.id) : null),\n [initial, stack],\n )\n // EC-554: the field's `allow` travels WITH the store, so every placement\n // surface (canvas + tree DnD, paste, the insert offer) judges the same value.\n const fieldScope = useMemo(() => compositionFieldScope(field), [field])\n\n if (!initialRoot) return <NoStackCanvasNotice />\n\n return (\n <BuilderProvider\n composition={initialRoot}\n preserved={initial.preserved}\n field={fieldScope}\n components={registry.lookup}\n layout={layout}\n >\n <CompositionSync revision={revision} change={change} onChange={onChange} readOnly={readOnly} />\n <CanvasCommitProvider value={commits ?? null}>\n <ReuseProvider>\n {/* EC-408: the field↔component selection-sync channel wraps BOTH the canvas\n and the inspector (they reach each other through nothing else). */}\n <PropSyncProvider>\n <CompositionEditorShell\n readOnly={readOnly}\n trailingPanes={trailingPanes}\n roleNotices={notices}\n conflicts={conflicts}\n />\n </PropSyncProvider>\n </ReuseProvider>\n </CanvasCommitProvider>\n </BuilderProvider>\n )\n}\n\n/**\n * Keeps the scoped store and the CMS draft on ONE value, in both directions — the\n * React shell of {@link createCanvasSync} (EC-630).\n *\n * **Lift** (the store → the draft): opening the tab lifts nothing (the\n * container-normalized mount value is the first agreed value, so simply opening\n * never marks the document dirty); every later change to the block list OR to the\n * preserved typography (EC-549: a run deleted or edited, or an undo of either)\n * emits the merged v2 sequence value (EC-547) — the canvas roots spliced back\n * between the preserved typography items — with the value it was last agreed on,\n * which the draft measures the canvas writer's delta from. Cardinality\n * (`multiple`) no longer shapes the stored value: it is a validation constraint\n * over the one envelope (EC-500 D2).\n *\n * **Rebase** (the draft → the store): a change of the field made UNDERNEATH the\n * canvas (a colleague's patch by subscription, a write's canonical row, a resolved\n * conflict), reported by the draft as `change`, is dispatched as `rebase` — the\n * change lands, undo history and selection survive, undo never reverts it. This\n * replaced the remount (`canvasNonce` on every change of the value), which reset\n * both. The rebase effect is declared first: in one commit it runs before the lift.\n */\nfunction CompositionSync({\n revision,\n change,\n onChange,\n readOnly,\n}: {\n revision: number | undefined\n change: CanvasFieldChange | undefined\n onChange: (lift: CanvasLift) => void\n readOnly: boolean\n}) {\n const builder = useBuilder()\n const blocks = builder.present.slots?.[DEFAULT_SLOT] ?? EMPTY_BLOCKS\n const preserved = builder.preserved as readonly PreservedBlockNode[]\n const rebased = builder.rebased\n const dispatch = builder.dispatch\n const onChangeRef = useRef(onChange)\n onChangeRef.current = onChange\n // EC-647: the mount value and the revision the draft held it at — one agreed pair.\n const [sync] = useState(() => createCanvasSync(mergeCompositionValue(blocks, preserved), revision, change?.seq))\n\n useEffect(() => {\n // EC-226: a READ-ONLY canvas (the Published view) shows another perspective's\n // value — the draft's changes are not its own.\n if (readOnly || !change) return\n const action = sync.changed(change)\n if (action) dispatch(action)\n }, [change?.seq, readOnly])\n\n useEffect(() => {\n // EC-226: never lift edits out of a READ-ONLY canvas (the Published view).\n // The canvas's keyboard listeners (clipboard paste/cut) aren't blocked by the\n // visual `pointer-events-none`, so without this a Cmd+V could mutate the\n // graph; refusing to lift keeps the document draft intact regardless. The\n // editor remounts per perspective, so any transient in-canvas change is\n // discarded on switch.\n if (readOnly) return\n const lift = sync.stored(mergeCompositionValue(blocks, preserved), rebased)\n if (lift) onChangeRef.current(lift)\n }, [blocks, preserved, rebased, readOnly])\n\n return null\n}\n\nconst EMPTY_BLOCKS: readonly ComponentNode[] = []\n\n/** A breakpoint preview-width toggle (EC-211/EC-250). Switching the viewport\n * resizes only the preview frame (not an undoable edit, writes no graph command).\n * The label is the breakpoint name (`Full`/`sm`/`md`/…) with the real px width in\n * the tooltip — so the switcher reflects the site's actual screens, not a fixed\n * desktop/tablet/phone triple. */\nfunction ViewportButton({ option }: { option: ViewportOption }) {\n const builder = useBuilder()\n const active = builder.viewport === option.key\n return (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n aria-label={translate('builder.canvas.viewportPreviewAria', { label: option.label })}\n aria-pressed={active}\n title={\n option.px !== undefined\n ? `${option.label} · ${option.px}px`\n : translate('builder.canvas.viewportWidthTitle', { label: option.label })\n }\n onClick={() => builder.dispatch({ type: 'viewport', viewport: option.key })}\n className={cn('px-2 text-xs', active && 'bg-accent text-accent-foreground')}\n >\n {option.label}\n </Button>\n )\n}\n\n/** The centered breakpoint switcher (EC-250): the resolved viewport set, plus a\n * visible validation issue when a project declared breakpoints but none are usable. */\nfunction BreakpointSwitcher() {\n const { viewports, issue } = useCanvasViewports()\n return (\n <Row gap=\"1\" align=\"center\">\n <ToolbarGroup>\n {viewports.map((v) => (\n <ViewportButton key={v.key} option={v} />\n ))}\n </ToolbarGroup>\n {issue ? (\n <Row gap=\"1\" align=\"center\" className=\"text-amber-600 dark:text-amber-400\" title={issue}>\n <AlertTriangle aria-hidden className=\"size-3.5\" />\n <Text size=\"xs\">{translate('builder.canvas.breakpointsIssueLabel')}</Text>\n </Row>\n ) : null}\n </Row>\n )\n}\n\n/**\n * The slim editor surface — the canvas + layers + inspector, plus Insert and\n * undo/redo. It deliberately drops the project-builder *lifecycle* chrome\n * (publish, versions, AI, page SEO): the document owns the lifecycle, not the\n * field. It keeps spatial/preview controls — including the breakpoint\n * preview-width switcher (EC-211), a canvas concern, not a lifecycle one.\n */\nfunction CompositionEditorShell({\n readOnly,\n trailingPanes,\n roleNotices,\n conflicts,\n}: {\n readOnly: boolean\n /** EC-595 A2 — the field's conflict notices (see {@link CompositionFieldEditor}). */\n conflicts?: ReactNode\n /** Document-level panes appended after `Properties` (EC-224). */\n trailingPanes?: readonly InspectorPane[]\n /** Notices for the roles nobody fills (EC-591) — see {@link canvasRoleNotices}. */\n roleNotices?: readonly MessageKey[]\n}) {\n const builder = useBuilder()\n const dispatch = builder.dispatch\n const [insertContext, setInsertContext] = useState<InsertContext | null>(null)\n const [insertOpen, setInsertOpen] = useState(false)\n const rootId = builder.present.id\n\n // The canvas SUPPLIES the `Properties` pane (EC-224); the document editor\n // appends its own (`Versions`). Properties dims under read-only just as the\n // whole inspector column used to — but the appended panes stay interactive.\n const propertiesPane = useMemo<InspectorPane>(\n () => ({\n id: 'properties',\n label: translate('builder.inspector.propertiesPane'),\n icon: <SlidersHorizontal className=\"size-3.5\" />,\n render: () => (\n <div className={cn('h-full min-h-0 overflow-hidden', readOnly && 'pointer-events-none opacity-60')}>\n <PropertyInspector />\n </div>\n ),\n }),\n [readOnly],\n )\n const panes = useMemo<InspectorPane[]>(\n () => [propertiesPane, ...(trailingPanes ?? [])],\n [propertiesPane, trailingPanes],\n )\n\n // The single insert entry point for BOTH the toolbar and the page tree's\n // \"Add child\" (EC-383). It selects its target first so `builder.selectedId`\n // is the one source of truth for where an insert lands: the toolbar resolves\n // `builder.selectedId ?? rootId` while Add-child passes an explicit row, and\n // without this sync clicking Add-child (which otherwise leaves selection put)\n // could target a different node than the toolbar — surfacing as a mismatched\n // (even empty \"No allowed components match\") insert dialog. Both now resolve\n // the identical target + allow set for the same selection.\n function openInsert(nodeId: string) {\n // EC-584: the offer reads the store's placement scope — the field's vocabulary at\n // the composition root, each slot's below — the same scope the reducer judges by.\n const ctx = resolveInsertContext(builder, nodeId)\n if (!ctx) return\n if (nodeId !== builder.selectedId) dispatch({ type: 'select', id: nodeId })\n setInsertContext(ctx)\n setInsertOpen(true)\n }\n\n // EC-405: the in-canvas \"+\" affordances resolve the SAME slot rules through the\n // shared position-carrying insert-context resolution, and open the panel at the\n // clicked edge (an explicit position that survives to the insertion, rather than\n // the panel re-deriving it from the selection). Re-memoized on every store change\n // so the closures read the live graph.\n const affordanceApi = useMemo<InsertAffordanceApi>(\n () => ({\n positionsFor: (nodeId: string) => insertablePositions(builder, nodeId),\n openAt: (nodeId: string, position: InsertPosition) => {\n const ctx = resolveInsertContextAt(builder, nodeId, position)\n if (!ctx) return\n if (nodeId !== builder.selectedId) dispatch({ type: 'select', id: nodeId })\n setInsertContext(ctx)\n setInsertOpen(true)\n },\n }),\n [builder, dispatch],\n )\n\n return (\n <InsertAffordanceProvider value={affordanceApi}>\n <div className=\"flex h-full min-h-0 flex-col\">\n <div className=\"shrink-0 border-b border-border\">\n <Toolbar>\n <ToolbarGroup>\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label={translate('builder.canvas.undo')}\n title={translate('builder.canvas.undo')}\n disabled={readOnly || !builder.canUndo}\n onClick={() => dispatch({ type: 'undo' })}\n >\n <Undo2 />\n </Button>\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label={translate('builder.canvas.redo')}\n title={translate('builder.canvas.redo')}\n disabled={readOnly || !builder.canRedo}\n onClick={() => dispatch({ type: 'redo' })}\n >\n <Redo2 />\n </Button>\n </ToolbarGroup>\n <ToolbarSeparator />\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={readOnly}\n onClick={() => openInsert(builder.selectedId ?? rootId)}\n >\n <Plus /> {translate('builder.canvas.insert')}\n </Button>\n {/* Breakpoint preview-width switcher (EC-211/EC-250), centered over the\n canvas via flanking spacers. A preview control, not an edit — stays\n active even in read-only published preview. */}\n <Spacer />\n <BreakpointSwitcher />\n <Spacer />\n </Toolbar>\n </div>\n\n <div className=\"grid min-h-0 flex-1 grid-cols-[minmax(200px,240px)_1fr_minmax(280px,320px)] gap-px bg-border\">\n <Panel className={cn('rounded-none border-0', readOnly && 'pointer-events-none opacity-60')}>\n <PageTree onAddChild={openInsert} />\n </Panel>\n <div className={cn('flex min-h-0 flex-col bg-background', readOnly && 'pointer-events-none opacity-60')}>\n {conflicts ? <div className=\"shrink-0 border-b border-border p-2\">{conflicts}</div> : null}\n {/* EC-584: a placement the reducer refused, with its reason — never a silent no-op. */}\n <PlacementRefusalNotice\n refusal={builder.refusal}\n fieldLabel={builder.field.label}\n onDismiss={() => dispatch({ type: 'clearRefusal' })}\n />\n <CanvasRoleNotices notices={roleNotices ?? []} />\n {/* EC-300/AD-16: the read-only Published view resolves references against\n the published perspective (honest preview); the editable Draft view\n resolves drafts (a just-made reusable shows immediately). */}\n <BuilderCanvas perspective={readOnly ? 'published' : DRAFT_PERSPECTIVE} />\n </div>\n <InspectorSidebar panes={panes} />\n </div>\n\n <InsertMenu context={insertContext} open={insertOpen} onOpenChange={setInsertOpen} />\n </div>\n </InsertAffordanceProvider>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"composition-field-editor.js","sourceRoot":"","sources":["../../../src/components/builder/composition-field-editor.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { AlertTriangle, Plus, Redo2, SlidersHorizontal, Undo2 } from 'lucide-react'\nimport { DEFAULT_SLOT, mergeBlockRoots, splitBlockRoots } from '@elytracms/core/project-graph'\nimport type { ComponentNode, PreservedSequenceItem, SplitBlockRoots } from '@elytracms/core/project-graph'\nimport { DRAFT_PERSPECTIVE, fieldAllowList } from '@elytracms/core/cms-core'\nimport type { BlocksFieldDef } from '@elytracms/core/cms-core'\nimport type { ComponentRegistry } from '@elytracms/core/component-registry'\nimport type { BlockNode, BlockSequenceValue } from '@elytracms/core/rich-text'\nimport { Button, Panel, Row, Spacer, Text, Toolbar, ToolbarGroup, ToolbarSeparator } from '@elytracms/studio/ui'\nimport { cn } from '../../lib/utils'\nimport { translate } from '../../lib/i18n'\nimport type { MessageKey } from '../../lib/i18n'\nimport { getStudioRegistry } from '../../lib/registry'\nimport { BuilderProvider, useBuilder } from './builder-store'\nimport type { CompositionFieldScope } from './builder-store'\nimport { createCanvasSync } from './canvas-rebase'\nimport type { CanvasFieldChange, CanvasLift } from './canvas-rebase'\nimport { CanvasCommitProvider } from './canvas-commit-boundary'\nimport type { CanvasCommitChannel } from './canvas-commit-boundary'\nimport { useCanvasViewports } from './canvas-breakpoints'\nimport type { ViewportOption } from './canvas-breakpoints'\nimport { BuilderCanvas } from './canvas'\nimport { PageTree } from './page-tree'\nimport { PropertyInspector } from './property-inspector'\nimport { InspectorSidebar } from '../inspector/inspector-sidebar'\nimport type { InspectorPane } from '../inspector/inspector-sidebar'\nimport { InsertMenu } from './insert-menu'\nimport type { InsertContext } from './insert-menu'\nimport { insertablePositions, resolveInsertContext, resolveInsertContextAt } from './insert-context'\nimport type { InsertPosition } from './insert-context'\nimport { InsertAffordanceProvider } from './insert-affordance-context'\nimport type { InsertAffordanceApi } from './insert-affordance-context'\nimport { PropSyncProvider } from './prop-sync-context'\nimport { ReuseProvider } from './reuse-context'\nimport { PlacementRefusalNotice } from './placement-refusal'\nimport { fieldLabel } from '../cms/cms-labels'\n\n/**\n * EC-188 — the block-field surface: one canvas engine mounted on a composition\n * **field value** (AD-12), not the project graph.\n *\n * The composition tree is wrapped in a synthetic single-page graph and edited by\n * a SCOPED `BuilderProvider` with **no `projectId`** (so the graph persister is a\n * no-op — builder-store.tsx). Every committed edit is lifted out via `onChange`\n * into the CMS document draft; the document's own Save persists it (the tree is\n * stored as document data, `v.any()`).\n *\n * EC-547 (EC-500 D1–D3): the stored value is the converged v2 block sequence\n * `{ version: 2, content: BlockNode[] }`. The canvas edits the sequence's\n * `elytra/component` nodes; typography nodes survive the session byte-for-byte\n * and keep their order relative to components on write-back (see\n * {@link splitCompositionValue} / {@link mergeCompositionValue}). Every write\n * emits v2; a legacy bare node/array has no roots (EC-708 deleted the staging\n * read — validation names the value). This reuses the real canvas + inspector +\n * blocks panel unchanged — the same EC-158–164 mechanics — without touching the\n * project-wide builder store. Surfaced on a dedicated \"Canvas\" fieldset tab in\n * the CMS detail editor so the page builder gets full screen real estate rather\n * than being crammed into the field list.\n */\n\nconst SYNTH_ROOT_ID = 'composition.root'\n\n/**\n * A sequence item the canvas does not natively edit (EC-547 / EC-500 D3):\n * typography nodes, unknown/future node kinds, even entries the v2 schema would\n * reject — the editor is not a validator, so they are carried BYTE-FOR-BYTE and\n * re-merged on write-back rather than parsed, rewritten, or dropped.\n * `componentPrefix` records how many component nodes preceded the item in the\n * original sequence — the anchor {@link mergeCompositionValue} splices by.\n */\nexport type PreservedBlockNode = PreservedSequenceItem<BlockNode>\n\n/** A field value split for canvas editing: component roots + everything else. */\nexport type SplitCompositionValue = SplitBlockRoots<BlockNode>\n\n/**\n * Split a stored blocks-field value into the component nodes the canvas edits\n * and the sequence items it must preserve (EC-547, the EC-500 round-trip law:\n * a projection may only rewrite nodes it natively edits). Typography items are\n * deliberately NOT surfaced as editable canvas blocks in v1 (EC-500 D3) — they\n * survive the editing session and keep their order on write-back.\n *\n * EC-576: the logic is core's {@link splitBlockRoots} — the ONE reader every\n * blocks writer shares. This is the canvas's typed view of it: project-graph\n * sits below rich-text and cannot name {@link BlockNode}, so the carried items\n * arrive `unknown`; they are typed here, not checked (preservation over parsing).\n */\nexport function splitCompositionValue(value: unknown): SplitCompositionValue {\n return splitBlockRoots(value) as SplitCompositionValue\n}\n\n/**\n * Merge canvas-edited component roots back into the stored sequence (EC-547).\n * The merge rule — preserved items anchored to their ordinal position between\n * components, every root tagged, the write always v2 — is core's\n * {@link mergeBlockRoots} (EC-576: the same merge every blocks writer goes\n * through, via `editBlockRoots`). Typed here as the rich-text value.\n */\nexport function mergeCompositionValue(\n roots: readonly ComponentNode[],\n preserved: readonly PreservedBlockNode[],\n): BlockSequenceValue {\n return mergeBlockRoots(roots, preserved)\n}\n\n/**\n * The field scope the editor's store carries (EC-554): the field's label (named\n * by refusal notices) and its EFFECTIVE top-level vocabulary (EC-584 — read through\n * the per-FieldType definition, `fieldAllowList`: the declared `allow`, else the\n * `blocks` default, any canvas-eligible component).\n */\nexport function compositionFieldScope(field: BlocksFieldDef): CompositionFieldScope {\n // A blocks field always has a vocabulary (its type declares the default).\n return { label: fieldLabel(field), allow: fieldAllowList(field)! }\n}\n\n/**\n * Wrap the field value's component roots in a synthetic root container (EC-287).\n * The builder store edits this single root-bearing composition directly; its\n * children are the field's component blocks, lifted back out via `onChange`.\n * EC-591: the root is the component that fills the `layout.stack` role — the\n * project's own, from its component list — never a hardcoded engine id.\n */\nfunction buildSyntheticRoot(roots: readonly ComponentNode[], stackComponentId: string): ComponentNode {\n return {\n id: SYNTH_ROOT_ID,\n componentId: stackComponentId,\n slots: { [DEFAULT_SLOT]: [...roots] },\n }\n}\n\n/**\n * The canvas notices for roles nobody in the component list fills (EC-591) — each\n * names the gesture that is OFF because of it, so a missing role is visible, never\n * a silent dead affordance. Only gestures the canvas actually has are listed: the\n * `layout.stack` role is the canvas itself (see {@link CompositionFieldEditor}),\n * `layout.grid` the wrap-beside gesture, `text` the seeded layout presets.\n * (`richText` — the typography projection, EC-549 — and `image` — asset drop —\n * have no canvas gesture yet, so there is nothing to switch off.) Pure.\n */\nexport function canvasRoleNotices(registry: ComponentRegistry): MessageKey[] {\n const notices: MessageKey[] = []\n if (!registry.roleComponent('layout.grid')) notices.push('builder.roles.noGrid')\n if (!registry.roleComponent('text')) notices.push('builder.roles.noText')\n return notices\n}\n\n/** The role notices above the canvas (EC-591) — a standing truth about the component list. */\nexport function CanvasRoleNotices({ notices }: { notices: readonly MessageKey[] }) {\n if (notices.length === 0) return null\n return (\n <div data-testid=\"canvas-role-notices\" className=\"shrink-0 border-b border-border bg-muted/40\">\n {notices.map((key) => (\n <Row key={key} gap=\"2\" align=\"center\" className=\"px-3 py-1.5\">\n <AlertTriangle aria-hidden className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <Text size=\"xs\" tone=\"muted\">\n {translate(key)}\n </Text>\n </Row>\n ))}\n </div>\n )\n}\n\n/**\n * The canvas in place of the editor when nobody fills `layout.stack` (EC-591): the\n * blocks of a composition field compose inside that component, so there is no\n * canvas without it. The value is not touched — nothing mounts that could write.\n */\nexport function NoStackCanvasNotice() {\n return (\n <Panel data-testid=\"canvas-no-stack\" className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <Row gap=\"2\" align=\"center\" className=\"max-w-prose\">\n <AlertTriangle aria-hidden className=\"size-4 shrink-0 text-muted-foreground\" />\n <Text size=\"sm\" tone=\"muted\">\n {translate('builder.roles.noStack')}\n </Text>\n </Row>\n </Panel>\n )\n}\n\nexport function CompositionFieldEditor({\n field,\n value,\n revision,\n change,\n onChange,\n commits,\n readOnly,\n trailingPanes,\n conflicts,\n}: {\n field: BlocksFieldDef\n /** The field's value at mount (the store is the source of truth while open). */\n value: unknown\n /**\n * EC-647 — the revision the draft was at when {@link value} was read (its value and its\n * revision travel together): the revision the canvas's first edit is authored at.\n */\n revision: number | undefined\n /**\n * EC-630 — the latest change of the field made UNDERNEATH the canvas (the document\n * draft reports it); the store rebases onto it — and, EC-647, onto its revision.\n */\n change?: CanvasFieldChange\n /**\n * The store's value, lifted, with the value it was last agreed on with the draft — the\n * base the canvas writer's delta is measured from — and the revision that was agreed\n * at (EC-647: the edit's authoring revision).\n */\n onChange: (lift: CanvasLift) => void\n /**\n * EC-630 — the document's committer, for the inspector controls' commit boundaries\n * (focus / blur / Enter at a position): a burst of typing commits on its control kind's\n * policy, never per keystroke. Absent → every edit commits as the canvas writes it.\n */\n commits?: CanvasCommitChannel\n readOnly: boolean\n /**\n * EC-595 A2 — the field's per-field conflicts (a refused node patch: \"changed by X —\n * take theirs / keep mine\"), rendered above the canvas: the canvas IS the field.\n */\n conflicts?: ReactNode\n /** Inspector panes the document editor appends after the canvas-supplied\n * `Properties` pane (EC-224 — the frame-owned `Versions` pane). */\n trailingPanes?: readonly InspectorPane[]\n}) {\n // Split the value ONCE at mount (EC-547): component nodes become the canvas\n // roots; every other sequence item is preserved aside for write-back. The store\n // is the source of truth while open: it lifts its edits out via onChange, and a\n // value that changes UNDERNEATH it (EC-630 — a colleague's patch, a write's\n // canonical row) is REBASED into it by `CompositionSync`, never remounted.\n // (BuilderProvider reads `composition` only in its lazy initial state.)\n const initial = useMemo(() => splitCompositionValue(value), [])\n // EC-591: the roles come from the project's component list (the registry).\n const registry = getStudioRegistry().registry\n const stack = registry.roleComponent('layout.stack')\n const layout = registry.layout\n const notices = useMemo(() => canvasRoleNotices(registry), [registry])\n const initialRoot = useMemo(\n () => (stack ? buildSyntheticRoot(initial.roots, stack.id) : null),\n [initial, stack],\n )\n // EC-554: the field's `allow` travels WITH the store, so every placement\n // surface (canvas + tree DnD, paste, the insert offer) judges the same value.\n const fieldScope = useMemo(() => compositionFieldScope(field), [field])\n\n if (!initialRoot) return <NoStackCanvasNotice />\n\n return (\n <BuilderProvider\n composition={initialRoot}\n preserved={initial.preserved}\n field={fieldScope}\n components={registry.lookup}\n layout={layout}\n >\n <CompositionSync revision={revision} change={change} onChange={onChange} readOnly={readOnly} />\n <CanvasCommitProvider value={commits ?? null}>\n <ReuseProvider>\n {/* EC-408: the field↔component selection-sync channel wraps BOTH the canvas\n and the inspector (they reach each other through nothing else). */}\n <PropSyncProvider>\n <CompositionEditorShell\n readOnly={readOnly}\n trailingPanes={trailingPanes}\n roleNotices={notices}\n conflicts={conflicts}\n />\n </PropSyncProvider>\n </ReuseProvider>\n </CanvasCommitProvider>\n </BuilderProvider>\n )\n}\n\n/**\n * Keeps the scoped store and the CMS draft on ONE value, in both directions — the\n * React shell of {@link createCanvasSync} (EC-630).\n *\n * **Lift** (the store → the draft): opening the tab lifts nothing (the\n * container-normalized mount value is the first agreed value, so simply opening\n * never marks the document dirty); every later change to the block list OR to the\n * preserved typography (EC-549: a run deleted or edited, or an undo of either)\n * emits the merged v2 sequence value (EC-547) — the canvas roots spliced back\n * between the preserved typography items — with the value it was last agreed on,\n * which the draft measures the canvas writer's delta from. Cardinality\n * (`multiple`) no longer shapes the stored value: it is a validation constraint\n * over the one envelope (EC-500 D2).\n *\n * **Rebase** (the draft → the store): a change of the field made UNDERNEATH the\n * canvas (a colleague's patch by subscription, a write's canonical row, a resolved\n * conflict), reported by the draft as `change`, is dispatched as `rebase` — the\n * change lands, undo history and selection survive, undo never reverts it. This\n * replaced the remount (`canvasNonce` on every change of the value), which reset\n * both. The rebase effect is declared first: in one commit it runs before the lift.\n */\nfunction CompositionSync({\n revision,\n change,\n onChange,\n readOnly,\n}: {\n revision: number | undefined\n change: CanvasFieldChange | undefined\n onChange: (lift: CanvasLift) => void\n readOnly: boolean\n}) {\n const builder = useBuilder()\n const blocks = builder.present.slots?.[DEFAULT_SLOT] ?? EMPTY_BLOCKS\n const preserved = builder.preserved as readonly PreservedBlockNode[]\n const rebased = builder.rebased\n const dispatch = builder.dispatch\n const onChangeRef = useRef(onChange)\n onChangeRef.current = onChange\n // EC-647: the mount value and the revision the draft held it at — one agreed pair.\n const [sync] = useState(() => createCanvasSync(mergeCompositionValue(blocks, preserved), revision, change?.seq))\n\n useEffect(() => {\n // EC-226: a READ-ONLY canvas (the Published view) shows another perspective's\n // value — the draft's changes are not its own.\n if (readOnly || !change) return\n const action = sync.changed(change)\n if (action) dispatch(action)\n }, [change?.seq, readOnly])\n\n useEffect(() => {\n // EC-226: never lift edits out of a READ-ONLY canvas (the Published view).\n // The canvas's keyboard listeners (clipboard paste/cut) aren't blocked by the\n // visual `pointer-events-none`, so without this a Cmd+V could mutate the\n // graph; refusing to lift keeps the document draft intact regardless. The\n // editor remounts per perspective, so any transient in-canvas change is\n // discarded on switch.\n if (readOnly) return\n const lift = sync.stored(mergeCompositionValue(blocks, preserved), rebased)\n if (lift) onChangeRef.current(lift)\n }, [blocks, preserved, rebased, readOnly])\n\n return null\n}\n\nconst EMPTY_BLOCKS: readonly ComponentNode[] = []\n\n/** A breakpoint preview-width toggle (EC-211/EC-250). Switching the viewport\n * resizes only the preview frame (not an undoable edit, writes no graph command).\n * The label is the breakpoint name (`Full`/`sm`/`md`/…) with the real px width in\n * the tooltip — so the switcher reflects the site's actual screens, not a fixed\n * desktop/tablet/phone triple. */\nfunction ViewportButton({ option }: { option: ViewportOption }) {\n const builder = useBuilder()\n const active = builder.viewport === option.key\n return (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n aria-label={translate('builder.canvas.viewportPreviewAria', { label: option.label })}\n aria-pressed={active}\n title={\n option.px !== undefined\n ? `${option.label} · ${option.px}px`\n : translate('builder.canvas.viewportWidthTitle', { label: option.label })\n }\n onClick={() => builder.dispatch({ type: 'viewport', viewport: option.key })}\n className={cn('px-2 text-xs', active && 'bg-accent text-accent-foreground')}\n >\n {option.label}\n </Button>\n )\n}\n\n/** The centered breakpoint switcher (EC-250): the resolved viewport set, plus a\n * visible validation issue when a project declared breakpoints but none are usable. */\nfunction BreakpointSwitcher() {\n const { viewports, issue } = useCanvasViewports()\n return (\n <Row gap=\"1\" align=\"center\">\n <ToolbarGroup>\n {viewports.map((v) => (\n <ViewportButton key={v.key} option={v} />\n ))}\n </ToolbarGroup>\n {issue ? (\n <Row gap=\"1\" align=\"center\" className=\"text-amber-600 dark:text-amber-400\" title={issue}>\n <AlertTriangle aria-hidden className=\"size-3.5\" />\n <Text size=\"xs\">{translate('builder.canvas.breakpointsIssueLabel')}</Text>\n </Row>\n ) : null}\n </Row>\n )\n}\n\n/**\n * The slim editor surface — the canvas + layers + inspector, plus Insert and\n * undo/redo. It deliberately drops the project-builder *lifecycle* chrome\n * (publish, versions, AI, page SEO): the document owns the lifecycle, not the\n * field. It keeps spatial/preview controls — including the breakpoint\n * preview-width switcher (EC-211), a canvas concern, not a lifecycle one.\n */\nfunction CompositionEditorShell({\n readOnly,\n trailingPanes,\n roleNotices,\n conflicts,\n}: {\n readOnly: boolean\n /** EC-595 A2 — the field's conflict notices (see {@link CompositionFieldEditor}). */\n conflicts?: ReactNode\n /** Document-level panes appended after `Properties` (EC-224). */\n trailingPanes?: readonly InspectorPane[]\n /** Notices for the roles nobody fills (EC-591) — see {@link canvasRoleNotices}. */\n roleNotices?: readonly MessageKey[]\n}) {\n const builder = useBuilder()\n const dispatch = builder.dispatch\n const [insertContext, setInsertContext] = useState<InsertContext | null>(null)\n const [insertOpen, setInsertOpen] = useState(false)\n const rootId = builder.present.id\n\n // The canvas SUPPLIES the `Properties` pane (EC-224); the document editor\n // appends its own (`Versions`). Properties dims under read-only just as the\n // whole inspector column used to — but the appended panes stay interactive.\n const propertiesPane = useMemo<InspectorPane>(\n () => ({\n id: 'properties',\n label: translate('builder.inspector.propertiesPane'),\n icon: <SlidersHorizontal className=\"size-3.5\" />,\n render: () => (\n <div className={cn('h-full min-h-0 overflow-hidden', readOnly && 'pointer-events-none opacity-60')}>\n <PropertyInspector />\n </div>\n ),\n }),\n [readOnly],\n )\n const panes = useMemo<InspectorPane[]>(\n () => [propertiesPane, ...(trailingPanes ?? [])],\n [propertiesPane, trailingPanes],\n )\n\n // The single insert entry point for BOTH the toolbar and the page tree's\n // \"Add child\" (EC-383). It selects its target first so `builder.selectedId`\n // is the one source of truth for where an insert lands: the toolbar resolves\n // `builder.selectedId ?? rootId` while Add-child passes an explicit row, and\n // without this sync clicking Add-child (which otherwise leaves selection put)\n // could target a different node than the toolbar — surfacing as a mismatched\n // (even empty \"No allowed components match\") insert dialog. Both now resolve\n // the identical target + allow set for the same selection.\n function openInsert(nodeId: string) {\n // EC-584: the offer reads the store's placement scope — the field's vocabulary at\n // the composition root, each slot's below — the same scope the reducer judges by.\n const ctx = resolveInsertContext(builder, nodeId)\n if (!ctx) return\n if (nodeId !== builder.selectedId) dispatch({ type: 'select', id: nodeId })\n setInsertContext(ctx)\n setInsertOpen(true)\n }\n\n // EC-405: the in-canvas \"+\" affordances resolve the SAME slot rules through the\n // shared position-carrying insert-context resolution, and open the panel at the\n // clicked edge (an explicit position that survives to the insertion, rather than\n // the panel re-deriving it from the selection). Re-memoized on every store change\n // so the closures read the live graph.\n const affordanceApi = useMemo<InsertAffordanceApi>(\n () => ({\n positionsFor: (nodeId: string) => insertablePositions(builder, nodeId),\n openAt: (nodeId: string, position: InsertPosition) => {\n const ctx = resolveInsertContextAt(builder, nodeId, position)\n if (!ctx) return\n if (nodeId !== builder.selectedId) dispatch({ type: 'select', id: nodeId })\n setInsertContext(ctx)\n setInsertOpen(true)\n },\n }),\n [builder, dispatch],\n )\n\n return (\n <InsertAffordanceProvider value={affordanceApi}>\n <div className=\"flex h-full min-h-0 flex-col\">\n <div className=\"shrink-0 border-b border-border\">\n <Toolbar>\n <ToolbarGroup>\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label={translate('builder.canvas.undo')}\n title={translate('builder.canvas.undo')}\n disabled={readOnly || !builder.canUndo}\n onClick={() => dispatch({ type: 'undo' })}\n >\n <Undo2 />\n </Button>\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label={translate('builder.canvas.redo')}\n title={translate('builder.canvas.redo')}\n disabled={readOnly || !builder.canRedo}\n onClick={() => dispatch({ type: 'redo' })}\n >\n <Redo2 />\n </Button>\n </ToolbarGroup>\n <ToolbarSeparator />\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={readOnly}\n onClick={() => openInsert(builder.selectedId ?? rootId)}\n >\n <Plus /> {translate('builder.canvas.insert')}\n </Button>\n {/* Breakpoint preview-width switcher (EC-211/EC-250), centered over the\n canvas via flanking spacers. A preview control, not an edit — stays\n active even in read-only published preview. */}\n <Spacer />\n <BreakpointSwitcher />\n <Spacer />\n </Toolbar>\n </div>\n\n <div className=\"grid min-h-0 flex-1 grid-cols-[minmax(200px,240px)_1fr_minmax(280px,320px)] gap-px bg-border\">\n <Panel className={cn('rounded-none border-0', readOnly && 'pointer-events-none opacity-60')}>\n <PageTree onAddChild={openInsert} />\n </Panel>\n <div className={cn('flex min-h-0 flex-col bg-background', readOnly && 'pointer-events-none opacity-60')}>\n {conflicts ? <div className=\"shrink-0 border-b border-border p-2\">{conflicts}</div> : null}\n {/* EC-584: a placement the reducer refused, with its reason — never a silent no-op. */}\n <PlacementRefusalNotice\n refusal={builder.refusal}\n fieldLabel={builder.field.label}\n onDismiss={() => dispatch({ type: 'clearRefusal' })}\n />\n <CanvasRoleNotices notices={roleNotices ?? []} />\n {/* EC-300/AD-16: the read-only Published view resolves references against\n the published perspective (honest preview); the editable Draft view\n resolves drafts (a just-made reusable shows immediately). */}\n <BuilderCanvas perspective={readOnly ? 'published' : DRAFT_PERSPECTIVE} />\n </div>\n <InspectorSidebar panes={panes} />\n </div>\n\n <InsertMenu context={insertContext} open={insertOpen} onOpenChange={setInsertOpen} />\n </div>\n </InsertAffordanceProvider>\n )\n}\n"]}
|
|
@@ -17,7 +17,8 @@ import type { PromoteEffects, ReuseCapability } from './reuse.js';
|
|
|
17
17
|
* EC-728 — *Make reusable* is ONE canvas history entry that records the document it
|
|
18
18
|
* created. When an Undo or Redo crosses it, this provider performs the document side
|
|
19
19
|
* (`createPromoteHistory`): Undo deletes the document only when it is provably nobody
|
|
20
|
-
* else's (never published, referenced nowhere, unchanged since its create
|
|
20
|
+
* else's (never published, referenced nowhere, unchanged since its create — EC-739: all
|
|
21
|
+
* conditions of the one conditional delete, checked in the store's step), else keeps
|
|
21
22
|
* it and says why; Redo re-inserts what Undo removed.
|
|
22
23
|
*/
|
|
23
24
|
export interface ReuseGestures {
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { blockRootsOf, getNode, isStaticProp } from '@elytracms/core/project-graph';
|
|
4
|
-
import {
|
|
4
|
+
import { readDocumentRef } from '@elytracms/core/cms-core';
|
|
5
5
|
import { Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Field, Input, Stack, toast, } from '@elytracms/studio/ui';
|
|
6
6
|
import { getStudioRegistry } from '../../lib/registry/index.js';
|
|
7
7
|
import { getStudioOperations } from '../../lib/operations/index.js';
|
|
8
|
-
import { ensureReferenceIndex } from '../../lib/references/references-store.js';
|
|
9
8
|
import { PublishRefusedError } from '../../lib/operations/publish-with-revision.js';
|
|
10
9
|
import { resolveDeploymentReuse } from '../../lib/workspace/index.js';
|
|
11
10
|
import { localizedLabel, translate } from '../../lib/i18n/index.js';
|
|
@@ -85,6 +84,10 @@ export function ReuseProvider({ children }) {
|
|
|
85
84
|
projectRef.current = projectId;
|
|
86
85
|
const [promoteHistory] = useState(() => createPromoteHistory({
|
|
87
86
|
flush: () => cmsRef.current.flushPersistence(),
|
|
87
|
+
// EC-739: the ONE judge — every condition (unchanged, unreferenced at any strictness) is
|
|
88
|
+
// checked by the store in its delete step; no reference count is read before it.
|
|
89
|
+
deleteCreated: (ref) => cmsRef.current.deleteCreatedDocument(ref),
|
|
90
|
+
// Only words a `changed` refusal (published vs edited).
|
|
88
91
|
read: async (ref) => {
|
|
89
92
|
const project = projectRef.current;
|
|
90
93
|
if (project === undefined)
|
|
@@ -97,24 +100,6 @@ export function ReuseProvider({ children }) {
|
|
|
97
100
|
return undefined;
|
|
98
101
|
throw new Error(record.error.message);
|
|
99
102
|
},
|
|
100
|
-
referrers: async (ref) => {
|
|
101
|
-
const project = projectRef.current;
|
|
102
|
-
if (project === undefined)
|
|
103
|
-
throw new Error('no project');
|
|
104
|
-
const studio = await getStudioOperations();
|
|
105
|
-
// A settled index, as the delete pre-flight reads it: a provider that does not
|
|
106
|
-
// maintain the index in its write path updates it after the write's event.
|
|
107
|
-
await ensureReferenceIndex(studio, project);
|
|
108
|
-
await studio.references.flush();
|
|
109
|
-
const inbound = await studio.operations.references.inbound({
|
|
110
|
-
projectId: project,
|
|
111
|
-
target: { type: 'document', id: documentKey(ref) },
|
|
112
|
-
});
|
|
113
|
-
if (!inbound.ok)
|
|
114
|
-
throw new Error(inbound.error.message);
|
|
115
|
-
return inbound.value.length;
|
|
116
|
-
},
|
|
117
|
-
deleteCreated: (ref) => cmsRef.current.deleteCreatedDocument(ref),
|
|
118
103
|
insert: (document) => cmsRef.current.insertDocument(document),
|
|
119
104
|
// A kept document the editor can act on is the outcome of a successful Undo; a removal
|
|
120
105
|
// that FAILED stays in the issues tray until dismissed (EC-690's error channel).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reuse-context.js","sourceRoot":"","sources":["../../../src/components/builder/reuse-context.tsx"],"names":[],"mappings":"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","sourcesContent":["import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'\nimport type { FormEvent, ReactNode } from 'react'\nimport { blockRootsOf, getNode, isStaticProp } from '@elytracms/core/project-graph'\nimport { documentKey, readDocumentRef } from '@elytracms/core/cms-core'\nimport type { DocumentRef } from '@elytracms/core/cms-core'\nimport {\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Field,\n Input,\n Stack,\n toast,\n} from '@elytracms/studio/ui'\nimport { getStudioRegistry } from '../../lib/registry'\nimport { getStudioOperations } from '../../lib/operations'\nimport { ensureReferenceIndex } from '../../lib/references/references-store'\nimport { PublishRefusedError } from '../../lib/operations/publish-with-revision'\nimport { resolveDeploymentReuse } from '../../lib/workspace'\nimport { localizedLabel, translate } from '../../lib/i18n'\nimport { useCms } from '../cms/cms-store'\nimport { useProjectSession } from '../project-session'\nimport { useBuilder } from './builder-store'\nimport { createPromoteHistory, detachRefusal, resolveReuseCapability, runPromote } from './reuse'\nimport type { PromoteEffects, ReuseCapability, ReuseRefusal } from './reuse'\nimport { placementRefusalMessage } from './placement-refusal'\n\n/**\n * In-canvas reuse gestures (EC-282): \"Make reusable\" promotes a selected block to\n * a new reusable document and replaces it in place with a transcluding embed;\n * \"Detach to local copy\" inlines an embed's referenced blocks so a page can\n * diverge. Both round-trip through the existing persistence + in-Convex reference\n * index (EC-277), so where-used is correct immediately.\n *\n * The provider needs BOTH the builder store (the edited composition) and the CMS\n * store (to create/read the reusable document), so it mounts inside the\n * composition-field editor's `BuilderProvider`, itself inside the CMS document\n * editor. Where no provider is mounted (or the capability is absent — EC-728: the\n * project declares no `reuse` embed), the menu reads `null` and simply omits the gestures.\n *\n * EC-728 — *Make reusable* is ONE canvas history entry that records the document it\n * created. When an Undo or Redo crosses it, this provider performs the document side\n * (`createPromoteHistory`): Undo deletes the document only when it is provably nobody\n * else's (never published, referenced nowhere, unchanged since its create), else keeps\n * it and says why; Redo re-inserts what Undo removed.\n */\nexport interface ReuseGestures {\n capability: ReuseCapability | null\n canMakeReusable: (nodeId: string) => boolean\n canDetach: (nodeId: string) => boolean\n /** Open the name dialog for promoting `nodeId`. */\n startMakeReusable: (nodeId: string) => void\n /** Replace a `Reusable` embed with an editable inline copy of its target's blocks. */\n detach: (nodeId: string) => void\n}\n\n/**\n * EC-690 — the publish step of \"Make reusable\" and its failure notice, for ONE promote. The\n * store's `publishDocument` reports a refusal on the session (`persistError` — an error notice of\n * the issues tray, with the reason) AND rejects with `PublishRefusedError` (EC-585). The refusal\n * is remembered here, so {@link promotePublishFailureNotice} does not report it a second time;\n * any other throw propagates (`runPromote` reads it as \"not published\").\n */\nexport function promotePublishEffects(deps: {\n publishDocument(ref: DocumentRef): Promise<void>\n isPublished(ref: DocumentRef): Promise<boolean>\n notify: { success(message: string): void; error(message: string): void }\n}): Pick<PromoteEffects, 'publish' | 'publishFailed'> {\n let refusalHeld = false\n return {\n publish: async (ref) => {\n try {\n await deps.publishDocument(ref)\n } catch (error) {\n if (error instanceof PublishRefusedError) {\n refusalHeld = true\n return false\n }\n throw error\n }\n return deps.isPublished(ref)\n },\n publishFailed: (name) => {\n const notice = promotePublishFailureNotice(name, refusalHeld)\n deps.notify[notice.variant](notice.message)\n },\n }\n}\n\n/**\n * EC-690 — the notice \"Make reusable\" raises when its publish did not land. A publish REFUSAL is\n * already held by the session (`persistError` — an error notice of the issues tray that stays\n * until dismissed, with the refusal's reason), so the outcome here is NOT a second report of the\n * same failure: it is what the action did make — a reusable DRAFT, to publish from its document.\n * A failure nobody reported (the publish threw elsewhere, or resolved without a published\n * version) is the error notice, as before.\n */\nexport function promotePublishFailureNotice(\n name: string,\n refusalHeld: boolean,\n): { variant: 'success' | 'error'; message: string } {\n return refusalHeld\n ? { variant: 'success', message: translate('builder.reuse.madeReusableDraft', { name }) }\n : { variant: 'error', message: translate('builder.reuse.madeReusablePublishFailed', { name }) }\n}\n\nconst ReuseContext = createContext<ReuseGestures | null>(null)\n\nexport function useReuse(): ReuseGestures | null {\n return useContext(ReuseContext)\n}\n\n/** A refused reuse gesture's visible reason (EC-584) — the judge's verdict, worded once. */\nfunction toastRefusal(refusal: ReuseRefusal): void {\n toast.error(placementRefusalMessage(refusal.verdict, refusal.fieldLabel))\n}\n\nexport function ReuseProvider({ children }: { children: ReactNode }) {\n const builder = useBuilder()\n const cms = useCms()\n const { projectId } = useProjectSession()\n const [pending, setPending] = useState<{ nodeId: string; defaultName: string } | null>(null)\n\n const manifests = useMemo(() => getStudioRegistry().registry.list(), [])\n // EC-728: the `reuse` declaration is the only source of the capability (declared,\n // never inferred). The config holder is static per studio boot, so it is read\n // inside the memo (like `useStudioNav` reads the sidebar holder). Resolution\n // ISSUES are surfaced once, by the shell — this provider is mounted per\n // composition field, so reporting here would repeat them per field.\n const capability = useMemo(\n () => resolveReuseCapability(manifests, cms.collections, resolveDeploymentReuse()),\n [manifests, cms.collections],\n )\n\n // EC-728 — the document side of an Undo/Redo across a *Make reusable* step. The work runs\n // after the render that applied the step (the page's restore is already committed, the\n // flush awaits it), so it reads the latest stores through refs.\n const cmsRef = useRef(cms)\n cmsRef.current = cms\n const projectRef = useRef(projectId)\n projectRef.current = projectId\n const [promoteHistory] = useState(() =>\n createPromoteHistory({\n flush: () => cmsRef.current.flushPersistence(),\n read: async (ref) => {\n const project = projectRef.current\n if (project === undefined) throw new Error('no project')\n const { operations } = await getStudioOperations()\n const record = await operations.documents.get({ projectId: project, ref })\n if (record.ok) return { published: record.value.publishedVersion !== undefined }\n if (record.error.code === 'not-found') return undefined\n throw new Error(record.error.message)\n },\n referrers: async (ref) => {\n const project = projectRef.current\n if (project === undefined) throw new Error('no project')\n const studio = await getStudioOperations()\n // A settled index, as the delete pre-flight reads it: a provider that does not\n // maintain the index in its write path updates it after the write's event.\n await ensureReferenceIndex(studio, project)\n await studio.references.flush()\n const inbound = await studio.operations.references.inbound({\n projectId: project,\n target: { type: 'document', id: documentKey(ref) },\n })\n if (!inbound.ok) throw new Error(inbound.error.message)\n return inbound.value.length\n },\n deleteCreated: (ref) => cmsRef.current.deleteCreatedDocument(ref),\n insert: (document) => cmsRef.current.insertDocument(document),\n // A kept document the editor can act on is the outcome of a successful Undo; a removal\n // that FAILED stays in the issues tray until dismissed (EC-690's error channel).\n notify: (key, name) =>\n key === 'builder.reuse.undoKeptFailed'\n ? toast.error(translate(key, { name }))\n : toast.success(translate(key, { name })),\n }),\n )\n const handledSeq = useRef(builder.historyEffect?.seq ?? 0)\n const step = builder.historyEffect\n useEffect(() => {\n if (!step || step.seq <= handledSeq.current) return\n handledSeq.current = step.seq\n void promoteHistory.crossed(step)\n }, [step, promoteHistory])\n\n const canMakeReusable = useCallback(\n (nodeId: string): boolean => {\n if (!capability) return false\n const node = getNode(builder.present, nodeId)\n // Anything but the synthetic composition root can be promoted; nesting a\n // reusable inside a reusable is allowed (the renderer + bake are cycle-\n // guarded, EC-281), so an embed itself is still promotable.\n return node !== undefined && node.id !== builder.present.id\n },\n [capability, builder],\n )\n\n const canDetach = useCallback(\n (nodeId: string): boolean => {\n if (!capability) return false\n return getNode(builder.present, nodeId)?.componentId === capability.embedComponentId\n },\n [capability, builder],\n )\n\n const startMakeReusable = useCallback(\n (nodeId: string) => {\n if (!canMakeReusable(nodeId)) return\n const node = getNode(builder.present, nodeId)\n if (!node) return\n const manifestTitle = localizedLabel(manifests.find((m) => m.id === node.componentId)?.title) || undefined\n const collection = capability ? cms.getCollection(capability.collectionId) : undefined\n const defaultName =\n manifestTitle ?? (localizedLabel(collection?.form?.labelSingular) || localizedLabel(collection?.form?.label) || translate('builder.reuse.defaultElementName'))\n setPending({ nodeId, defaultName })\n },\n [canMakeReusable, builder, manifests, capability, cms],\n )\n\n const confirmMakeReusable = useCallback(\n (name: string, publish: boolean) => {\n const cap = capability\n if (!cap || !pending) return\n const node = getNode(builder.present, pending.nodeId)\n const collection = cms.getCollection(cap.collectionId)\n setPending(null)\n if (!node || !collection) return\n // EC-584 / EC-560: the placement preflight runs FIRST — a refused promote creates,\n // publishes and replaces nothing. Then (EC-595 A2 / EC-578) ONE insert-if-absent\n // create with a factory id, the in-place swap for the transcluding embed (the\n // action clones to a fresh id; the source `node` stays untouched in the new doc),\n // and the chosen publish. EC-300: PUBLISH keeps the section visible everywhere\n // immediately (the continuity-preserving default); SAVE DRAFT keeps it editable,\n // surfaced as a pending reference until published. No silent auto-publish (AD-16).\n void runPromote({\n root: builder.present,\n nodeId: pending.nodeId,\n node,\n name,\n publish,\n capability: cap,\n collection,\n existing: cms.documentsOf(cap.collectionId),\n locales: cms.localeConfig,\n placement: builder.placement,\n canvasFieldLabel: builder.field.label,\n effects: {\n newDocumentId: () => cms.newDocumentId(),\n insertDocument: (doc) => cms.insertDocument(doc),\n replaceInCanvas: (nodeId, embed, effect) =>\n builder.dispatch({ type: 'replace', id: nodeId, nodes: [embed], effect }),\n refused: toastRefusal,\n madeDraft: (label) => toast.success(translate('builder.reuse.madeReusableDraft', { name: label })),\n madePublished: (label) => toast.success(translate('builder.reuse.madeReusablePublished', { name: label })),\n // The publish and its failure notice (EC-690: a refusal the session already holds is\n // never reported twice) — see `promotePublishEffects`.\n ...promotePublishEffects({\n publishDocument: (ref) => cms.publishDocument(ref),\n // Success is the AUTHORITATIVE pointer after the publish settles (the same read the\n // store reconciles its badge from), never the resolution.\n isPublished: async (ref) => {\n if (projectId === undefined) return false\n const { operations } = await getStudioOperations()\n const record = await operations.documents.get({ projectId, ref })\n return record.ok && record.value.publishedVersion !== undefined\n },\n notify: { success: (message) => toast.success(message), error: (message) => toast.error(message) },\n }),\n },\n })\n },\n [capability, pending, builder, cms, projectId],\n )\n\n const detach = useCallback(\n (nodeId: string) => {\n const cap = capability\n if (!cap) return\n const node = getNode(builder.present, nodeId)\n if (!node || node.componentId !== cap.embedComponentId) return\n const prop = node.props?.[cap.relationProp]\n const ref = isStaticProp(prop) ? readDocumentRef(prop.value) : undefined\n if (!ref) {\n toast.error(translate('builder.reuse.noTargetToDetach'))\n return\n }\n const target = cms.getDocument(ref)\n const blocks = blockRootsOf(target?.values?.[cap.contentField])\n if (blocks.length === 0) {\n toast.error(translate('builder.reuse.noContentToDetach'))\n return\n }\n // EC-584 / EC-560: the inlined blocks land where the embed is — judged first, so\n // a refusal keeps the reference in place, says why, and dispatches nothing.\n const refusal = detachRefusal({\n root: builder.present,\n nodeId,\n blocks,\n placement: builder.placement,\n canvasFieldLabel: builder.field.label,\n })\n if (refusal) {\n toastRefusal(refusal)\n return\n }\n builder.dispatch({ type: 'replace', id: nodeId, nodes: blocks })\n toast.success(translate('builder.reuse.detachedToCopy'))\n },\n [capability, builder, cms],\n )\n\n const value = useMemo<ReuseGestures>(\n () => ({ capability, canMakeReusable, canDetach, startMakeReusable, detach }),\n [capability, canMakeReusable, canDetach, startMakeReusable, detach],\n )\n\n return (\n <ReuseContext.Provider value={value}>\n {children}\n <MakeReusableDialog\n open={pending !== null}\n defaultName={pending?.defaultName ?? ''}\n onConfirm={confirmMakeReusable}\n onCancel={() => setPending(null)}\n />\n </ReuseContext.Provider>\n )\n}\n\n/** Name-and-create dialog for the \"Make reusable\" gesture. */\nfunction MakeReusableDialog({\n open,\n defaultName,\n onConfirm,\n onCancel,\n}: {\n open: boolean\n defaultName: string\n /** `publish` chooses the new reusable's initial state (EC-300/AD-16). */\n onConfirm: (name: string, publish: boolean) => void\n onCancel: () => void\n}) {\n const [name, setName] = useState(defaultName)\n // Re-seed the field each time the dialog opens for a new selection.\n const [seededFor, setSeededFor] = useState<string | null>(null)\n if (open && seededFor !== defaultName) {\n setName(defaultName)\n setSeededFor(defaultName)\n }\n\n const empty = name.trim() === ''\n function commit(publish: boolean) {\n if (empty) return\n onConfirm(name.trim(), publish)\n setSeededFor(null)\n }\n\n return (\n <Dialog\n open={open}\n onOpenChange={(next) => {\n if (!next) {\n onCancel()\n setSeededFor(null)\n }\n }}\n >\n <DialogContent>\n <DialogHeader>\n <DialogTitle>{translate('builder.reuse.makeReusable')}</DialogTitle>\n <DialogDescription>\n {translate('builder.reuse.makeReusableDescription')}\n </DialogDescription>\n </DialogHeader>\n {/* Enter submits the primary action (Publish) — the continuity-preserving default. */}\n <form\n onSubmit={(event: FormEvent) => {\n event.preventDefault()\n commit(true)\n }}\n >\n <Stack gap=\"3\">\n <Field label={translate('builder.reuse.nameLabel')} htmlFor=\"make-reusable-name\" required>\n <Input\n id=\"make-reusable-name\"\n value={name}\n onChange={(event) => setName(event.target.value)}\n placeholder={translate('builder.reuse.namePlaceholder')}\n autoFocus\n required\n />\n </Field>\n </Stack>\n <DialogFooter className=\"mt-4\">\n {/* Cancel pinned left; the two commit actions group right. */}\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" className=\"mr-auto\" onClick={onCancel}>\n {translate('common.cancel')}\n </Button>\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n disabled={empty}\n onClick={() => commit(false)}\n data-testid=\"make-reusable-draft\"\n >\n {translate('builder.reuse.saveDraft')}\n </Button>\n <Button\n type=\"submit\"\n size=\"sm\"\n disabled={empty}\n data-testid=\"make-reusable-publish\"\n >\n {translate('builder.reuse.publish')}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"reuse-context.js","sourceRoot":"","sources":["../../../src/components/builder/reuse-context.tsx"],"names":[],"mappings":"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","sourcesContent":["import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'\nimport type { FormEvent, ReactNode } from 'react'\nimport { blockRootsOf, getNode, isStaticProp } from '@elytracms/core/project-graph'\nimport { readDocumentRef } from '@elytracms/core/cms-core'\nimport type { DocumentRef } from '@elytracms/core/cms-core'\nimport {\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Field,\n Input,\n Stack,\n toast,\n} from '@elytracms/studio/ui'\nimport { getStudioRegistry } from '../../lib/registry'\nimport { getStudioOperations } from '../../lib/operations'\nimport { PublishRefusedError } from '../../lib/operations/publish-with-revision'\nimport { resolveDeploymentReuse } from '../../lib/workspace'\nimport { localizedLabel, translate } from '../../lib/i18n'\nimport { useCms } from '../cms/cms-store'\nimport { useProjectSession } from '../project-session'\nimport { useBuilder } from './builder-store'\nimport { createPromoteHistory, detachRefusal, resolveReuseCapability, runPromote } from './reuse'\nimport type { PromoteEffects, ReuseCapability, ReuseRefusal } from './reuse'\nimport { placementRefusalMessage } from './placement-refusal'\n\n/**\n * In-canvas reuse gestures (EC-282): \"Make reusable\" promotes a selected block to\n * a new reusable document and replaces it in place with a transcluding embed;\n * \"Detach to local copy\" inlines an embed's referenced blocks so a page can\n * diverge. Both round-trip through the existing persistence + in-Convex reference\n * index (EC-277), so where-used is correct immediately.\n *\n * The provider needs BOTH the builder store (the edited composition) and the CMS\n * store (to create/read the reusable document), so it mounts inside the\n * composition-field editor's `BuilderProvider`, itself inside the CMS document\n * editor. Where no provider is mounted (or the capability is absent — EC-728: the\n * project declares no `reuse` embed), the menu reads `null` and simply omits the gestures.\n *\n * EC-728 — *Make reusable* is ONE canvas history entry that records the document it\n * created. When an Undo or Redo crosses it, this provider performs the document side\n * (`createPromoteHistory`): Undo deletes the document only when it is provably nobody\n * else's (never published, referenced nowhere, unchanged since its create — EC-739: all\n * conditions of the one conditional delete, checked in the store's step), else keeps\n * it and says why; Redo re-inserts what Undo removed.\n */\nexport interface ReuseGestures {\n capability: ReuseCapability | null\n canMakeReusable: (nodeId: string) => boolean\n canDetach: (nodeId: string) => boolean\n /** Open the name dialog for promoting `nodeId`. */\n startMakeReusable: (nodeId: string) => void\n /** Replace a `Reusable` embed with an editable inline copy of its target's blocks. */\n detach: (nodeId: string) => void\n}\n\n/**\n * EC-690 — the publish step of \"Make reusable\" and its failure notice, for ONE promote. The\n * store's `publishDocument` reports a refusal on the session (`persistError` — an error notice of\n * the issues tray, with the reason) AND rejects with `PublishRefusedError` (EC-585). The refusal\n * is remembered here, so {@link promotePublishFailureNotice} does not report it a second time;\n * any other throw propagates (`runPromote` reads it as \"not published\").\n */\nexport function promotePublishEffects(deps: {\n publishDocument(ref: DocumentRef): Promise<void>\n isPublished(ref: DocumentRef): Promise<boolean>\n notify: { success(message: string): void; error(message: string): void }\n}): Pick<PromoteEffects, 'publish' | 'publishFailed'> {\n let refusalHeld = false\n return {\n publish: async (ref) => {\n try {\n await deps.publishDocument(ref)\n } catch (error) {\n if (error instanceof PublishRefusedError) {\n refusalHeld = true\n return false\n }\n throw error\n }\n return deps.isPublished(ref)\n },\n publishFailed: (name) => {\n const notice = promotePublishFailureNotice(name, refusalHeld)\n deps.notify[notice.variant](notice.message)\n },\n }\n}\n\n/**\n * EC-690 — the notice \"Make reusable\" raises when its publish did not land. A publish REFUSAL is\n * already held by the session (`persistError` — an error notice of the issues tray that stays\n * until dismissed, with the refusal's reason), so the outcome here is NOT a second report of the\n * same failure: it is what the action did make — a reusable DRAFT, to publish from its document.\n * A failure nobody reported (the publish threw elsewhere, or resolved without a published\n * version) is the error notice, as before.\n */\nexport function promotePublishFailureNotice(\n name: string,\n refusalHeld: boolean,\n): { variant: 'success' | 'error'; message: string } {\n return refusalHeld\n ? { variant: 'success', message: translate('builder.reuse.madeReusableDraft', { name }) }\n : { variant: 'error', message: translate('builder.reuse.madeReusablePublishFailed', { name }) }\n}\n\nconst ReuseContext = createContext<ReuseGestures | null>(null)\n\nexport function useReuse(): ReuseGestures | null {\n return useContext(ReuseContext)\n}\n\n/** A refused reuse gesture's visible reason (EC-584) — the judge's verdict, worded once. */\nfunction toastRefusal(refusal: ReuseRefusal): void {\n toast.error(placementRefusalMessage(refusal.verdict, refusal.fieldLabel))\n}\n\nexport function ReuseProvider({ children }: { children: ReactNode }) {\n const builder = useBuilder()\n const cms = useCms()\n const { projectId } = useProjectSession()\n const [pending, setPending] = useState<{ nodeId: string; defaultName: string } | null>(null)\n\n const manifests = useMemo(() => getStudioRegistry().registry.list(), [])\n // EC-728: the `reuse` declaration is the only source of the capability (declared,\n // never inferred). The config holder is static per studio boot, so it is read\n // inside the memo (like `useStudioNav` reads the sidebar holder). Resolution\n // ISSUES are surfaced once, by the shell — this provider is mounted per\n // composition field, so reporting here would repeat them per field.\n const capability = useMemo(\n () => resolveReuseCapability(manifests, cms.collections, resolveDeploymentReuse()),\n [manifests, cms.collections],\n )\n\n // EC-728 — the document side of an Undo/Redo across a *Make reusable* step. The work runs\n // after the render that applied the step (the page's restore is already committed, the\n // flush awaits it), so it reads the latest stores through refs.\n const cmsRef = useRef(cms)\n cmsRef.current = cms\n const projectRef = useRef(projectId)\n projectRef.current = projectId\n const [promoteHistory] = useState(() =>\n createPromoteHistory({\n flush: () => cmsRef.current.flushPersistence(),\n // EC-739: the ONE judge — every condition (unchanged, unreferenced at any strictness) is\n // checked by the store in its delete step; no reference count is read before it.\n deleteCreated: (ref) => cmsRef.current.deleteCreatedDocument(ref),\n // Only words a `changed` refusal (published vs edited).\n read: async (ref) => {\n const project = projectRef.current\n if (project === undefined) throw new Error('no project')\n const { operations } = await getStudioOperations()\n const record = await operations.documents.get({ projectId: project, ref })\n if (record.ok) return { published: record.value.publishedVersion !== undefined }\n if (record.error.code === 'not-found') return undefined\n throw new Error(record.error.message)\n },\n insert: (document) => cmsRef.current.insertDocument(document),\n // A kept document the editor can act on is the outcome of a successful Undo; a removal\n // that FAILED stays in the issues tray until dismissed (EC-690's error channel).\n notify: (key, name) =>\n key === 'builder.reuse.undoKeptFailed'\n ? toast.error(translate(key, { name }))\n : toast.success(translate(key, { name })),\n }),\n )\n const handledSeq = useRef(builder.historyEffect?.seq ?? 0)\n const step = builder.historyEffect\n useEffect(() => {\n if (!step || step.seq <= handledSeq.current) return\n handledSeq.current = step.seq\n void promoteHistory.crossed(step)\n }, [step, promoteHistory])\n\n const canMakeReusable = useCallback(\n (nodeId: string): boolean => {\n if (!capability) return false\n const node = getNode(builder.present, nodeId)\n // Anything but the synthetic composition root can be promoted; nesting a\n // reusable inside a reusable is allowed (the renderer + bake are cycle-\n // guarded, EC-281), so an embed itself is still promotable.\n return node !== undefined && node.id !== builder.present.id\n },\n [capability, builder],\n )\n\n const canDetach = useCallback(\n (nodeId: string): boolean => {\n if (!capability) return false\n return getNode(builder.present, nodeId)?.componentId === capability.embedComponentId\n },\n [capability, builder],\n )\n\n const startMakeReusable = useCallback(\n (nodeId: string) => {\n if (!canMakeReusable(nodeId)) return\n const node = getNode(builder.present, nodeId)\n if (!node) return\n const manifestTitle = localizedLabel(manifests.find((m) => m.id === node.componentId)?.title) || undefined\n const collection = capability ? cms.getCollection(capability.collectionId) : undefined\n const defaultName =\n manifestTitle ?? (localizedLabel(collection?.form?.labelSingular) || localizedLabel(collection?.form?.label) || translate('builder.reuse.defaultElementName'))\n setPending({ nodeId, defaultName })\n },\n [canMakeReusable, builder, manifests, capability, cms],\n )\n\n const confirmMakeReusable = useCallback(\n (name: string, publish: boolean) => {\n const cap = capability\n if (!cap || !pending) return\n const node = getNode(builder.present, pending.nodeId)\n const collection = cms.getCollection(cap.collectionId)\n setPending(null)\n if (!node || !collection) return\n // EC-584 / EC-560: the placement preflight runs FIRST — a refused promote creates,\n // publishes and replaces nothing. Then (EC-595 A2 / EC-578) ONE insert-if-absent\n // create with a factory id, the in-place swap for the transcluding embed (the\n // action clones to a fresh id; the source `node` stays untouched in the new doc),\n // and the chosen publish. EC-300: PUBLISH keeps the section visible everywhere\n // immediately (the continuity-preserving default); SAVE DRAFT keeps it editable,\n // surfaced as a pending reference until published. No silent auto-publish (AD-16).\n void runPromote({\n root: builder.present,\n nodeId: pending.nodeId,\n node,\n name,\n publish,\n capability: cap,\n collection,\n existing: cms.documentsOf(cap.collectionId),\n locales: cms.localeConfig,\n placement: builder.placement,\n canvasFieldLabel: builder.field.label,\n effects: {\n newDocumentId: () => cms.newDocumentId(),\n insertDocument: (doc) => cms.insertDocument(doc),\n replaceInCanvas: (nodeId, embed, effect) =>\n builder.dispatch({ type: 'replace', id: nodeId, nodes: [embed], effect }),\n refused: toastRefusal,\n madeDraft: (label) => toast.success(translate('builder.reuse.madeReusableDraft', { name: label })),\n madePublished: (label) => toast.success(translate('builder.reuse.madeReusablePublished', { name: label })),\n // The publish and its failure notice (EC-690: a refusal the session already holds is\n // never reported twice) — see `promotePublishEffects`.\n ...promotePublishEffects({\n publishDocument: (ref) => cms.publishDocument(ref),\n // Success is the AUTHORITATIVE pointer after the publish settles (the same read the\n // store reconciles its badge from), never the resolution.\n isPublished: async (ref) => {\n if (projectId === undefined) return false\n const { operations } = await getStudioOperations()\n const record = await operations.documents.get({ projectId, ref })\n return record.ok && record.value.publishedVersion !== undefined\n },\n notify: { success: (message) => toast.success(message), error: (message) => toast.error(message) },\n }),\n },\n })\n },\n [capability, pending, builder, cms, projectId],\n )\n\n const detach = useCallback(\n (nodeId: string) => {\n const cap = capability\n if (!cap) return\n const node = getNode(builder.present, nodeId)\n if (!node || node.componentId !== cap.embedComponentId) return\n const prop = node.props?.[cap.relationProp]\n const ref = isStaticProp(prop) ? readDocumentRef(prop.value) : undefined\n if (!ref) {\n toast.error(translate('builder.reuse.noTargetToDetach'))\n return\n }\n const target = cms.getDocument(ref)\n const blocks = blockRootsOf(target?.values?.[cap.contentField])\n if (blocks.length === 0) {\n toast.error(translate('builder.reuse.noContentToDetach'))\n return\n }\n // EC-584 / EC-560: the inlined blocks land where the embed is — judged first, so\n // a refusal keeps the reference in place, says why, and dispatches nothing.\n const refusal = detachRefusal({\n root: builder.present,\n nodeId,\n blocks,\n placement: builder.placement,\n canvasFieldLabel: builder.field.label,\n })\n if (refusal) {\n toastRefusal(refusal)\n return\n }\n builder.dispatch({ type: 'replace', id: nodeId, nodes: blocks })\n toast.success(translate('builder.reuse.detachedToCopy'))\n },\n [capability, builder, cms],\n )\n\n const value = useMemo<ReuseGestures>(\n () => ({ capability, canMakeReusable, canDetach, startMakeReusable, detach }),\n [capability, canMakeReusable, canDetach, startMakeReusable, detach],\n )\n\n return (\n <ReuseContext.Provider value={value}>\n {children}\n <MakeReusableDialog\n open={pending !== null}\n defaultName={pending?.defaultName ?? ''}\n onConfirm={confirmMakeReusable}\n onCancel={() => setPending(null)}\n />\n </ReuseContext.Provider>\n )\n}\n\n/** Name-and-create dialog for the \"Make reusable\" gesture. */\nfunction MakeReusableDialog({\n open,\n defaultName,\n onConfirm,\n onCancel,\n}: {\n open: boolean\n defaultName: string\n /** `publish` chooses the new reusable's initial state (EC-300/AD-16). */\n onConfirm: (name: string, publish: boolean) => void\n onCancel: () => void\n}) {\n const [name, setName] = useState(defaultName)\n // Re-seed the field each time the dialog opens for a new selection.\n const [seededFor, setSeededFor] = useState<string | null>(null)\n if (open && seededFor !== defaultName) {\n setName(defaultName)\n setSeededFor(defaultName)\n }\n\n const empty = name.trim() === ''\n function commit(publish: boolean) {\n if (empty) return\n onConfirm(name.trim(), publish)\n setSeededFor(null)\n }\n\n return (\n <Dialog\n open={open}\n onOpenChange={(next) => {\n if (!next) {\n onCancel()\n setSeededFor(null)\n }\n }}\n >\n <DialogContent>\n <DialogHeader>\n <DialogTitle>{translate('builder.reuse.makeReusable')}</DialogTitle>\n <DialogDescription>\n {translate('builder.reuse.makeReusableDescription')}\n </DialogDescription>\n </DialogHeader>\n {/* Enter submits the primary action (Publish) — the continuity-preserving default. */}\n <form\n onSubmit={(event: FormEvent) => {\n event.preventDefault()\n commit(true)\n }}\n >\n <Stack gap=\"3\">\n <Field label={translate('builder.reuse.nameLabel')} htmlFor=\"make-reusable-name\" required>\n <Input\n id=\"make-reusable-name\"\n value={name}\n onChange={(event) => setName(event.target.value)}\n placeholder={translate('builder.reuse.namePlaceholder')}\n autoFocus\n required\n />\n </Field>\n </Stack>\n <DialogFooter className=\"mt-4\">\n {/* Cancel pinned left; the two commit actions group right. */}\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" className=\"mr-auto\" onClick={onCancel}>\n {translate('common.cancel')}\n </Button>\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n disabled={empty}\n onClick={() => commit(false)}\n data-testid=\"make-reusable-draft\"\n >\n {translate('builder.reuse.saveDraft')}\n </Button>\n <Button\n type=\"submit\"\n size=\"sm\"\n disabled={empty}\n data-testid=\"make-reusable-publish\"\n >\n {translate('builder.reuse.publish')}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n )\n}\n"]}
|
|
@@ -229,26 +229,33 @@ export declare function runPromote(args: {
|
|
|
229
229
|
* or the removal `failed`. The block is restored in every case (the canvas entry is a snapshot).
|
|
230
230
|
*/
|
|
231
231
|
export type PromoteUndoOutcome = 'deleted' | 'gone' | 'published' | 'edited' | 'referenced' | 'failed';
|
|
232
|
-
/** The
|
|
232
|
+
/** The one write Undo needs, and the read that words its refusal — injected, so every branch is testable. */
|
|
233
233
|
export interface PromoteUndoDeps {
|
|
234
234
|
/** Await every queued write: the page's restored value lands before anything is judged. */
|
|
235
235
|
flush(): Promise<void>;
|
|
236
|
-
/**
|
|
236
|
+
/**
|
|
237
|
+
* The conditional delete (`CmsPersister.deleteCreated`) — THE judge: unchanged since this
|
|
238
|
+
* session's create (so never published), and referenced by nothing, weak or strict, draft or
|
|
239
|
+
* published — every condition checked by the store in its one delete step (EC-739).
|
|
240
|
+
*/
|
|
241
|
+
deleteCreated(ref: DocumentRef): Promise<DeleteCreatedOutcome>;
|
|
242
|
+
/**
|
|
243
|
+
* The document's record now (`undefined`: it no longer exists) — read only to WORD a
|
|
244
|
+
* `changed` refusal (published, or edited); it decides nothing.
|
|
245
|
+
*/
|
|
237
246
|
read(ref: DocumentRef): Promise<{
|
|
238
247
|
published: boolean;
|
|
239
248
|
} | undefined>;
|
|
240
|
-
/** How many reference-index entries (draft or published, any document) point at it now. */
|
|
241
|
-
referrers(ref: DocumentRef): Promise<number>;
|
|
242
|
-
/** The conditional delete — at the revision this session's create landed at (`CmsPersister.deleteCreated`). */
|
|
243
|
-
deleteCreated(ref: DocumentRef): Promise<DeleteCreatedOutcome>;
|
|
244
249
|
}
|
|
245
250
|
/**
|
|
246
251
|
* EC-728 — Undo of *Make reusable*, the document side. The created document is deleted ONLY
|
|
247
|
-
* when
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
* checked by the store in its own step,
|
|
251
|
-
*
|
|
252
|
+
* when nobody else has it: unchanged since its create (hence never published — a publish
|
|
253
|
+
* moves the revision) and referenced by nothing — weak or strict, draft or published, after
|
|
254
|
+
* the page's own restore has landed. EC-739 (Codex F41): those are conditions of the ONE
|
|
255
|
+
* conditional delete, checked by the store in its own step, never a count read before it — a
|
|
256
|
+
* colleague's relation saved between a count and the delete adopted the document and was
|
|
257
|
+
* deleted out from under it. Anything else keeps it, with the reason. A delete that throws
|
|
258
|
+
* keeps it too: an unproven fact never deletes.
|
|
252
259
|
*/
|
|
253
260
|
export declare function undoPromote(effect: CanvasHistoryEffect, deps: PromoteUndoDeps): Promise<PromoteUndoOutcome>;
|
|
254
261
|
/** The catalog keys of the kept-document notices (EN + DE in the studio catalogs). */
|
|
@@ -304,31 +304,29 @@ export async function runPromote(args) {
|
|
|
304
304
|
}
|
|
305
305
|
/**
|
|
306
306
|
* EC-728 — Undo of *Make reusable*, the document side. The created document is deleted ONLY
|
|
307
|
-
* when
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
* checked by the store in its own step,
|
|
311
|
-
*
|
|
307
|
+
* when nobody else has it: unchanged since its create (hence never published — a publish
|
|
308
|
+
* moves the revision) and referenced by nothing — weak or strict, draft or published, after
|
|
309
|
+
* the page's own restore has landed. EC-739 (Codex F41): those are conditions of the ONE
|
|
310
|
+
* conditional delete, checked by the store in its own step, never a count read before it — a
|
|
311
|
+
* colleague's relation saved between a count and the delete adopted the document and was
|
|
312
|
+
* deleted out from under it. Anything else keeps it, with the reason. A delete that throws
|
|
313
|
+
* keeps it too: an unproven fact never deletes.
|
|
312
314
|
*/
|
|
313
315
|
export async function undoPromote(effect, deps) {
|
|
314
316
|
try {
|
|
315
317
|
await deps.flush();
|
|
316
|
-
const record = await deps.read(effect.ref);
|
|
317
|
-
if (!record)
|
|
318
|
-
return 'gone';
|
|
319
|
-
if (record.published)
|
|
320
|
-
return 'published';
|
|
321
|
-
if ((await deps.referrers(effect.ref)) > 0)
|
|
322
|
-
return 'referenced';
|
|
323
318
|
switch (await deps.deleteCreated(effect.ref)) {
|
|
324
319
|
case 'deleted':
|
|
325
320
|
return 'deleted';
|
|
326
321
|
case 'gone':
|
|
327
322
|
return 'gone';
|
|
328
|
-
case 'changed':
|
|
329
|
-
return 'edited';
|
|
330
323
|
case 'referenced':
|
|
331
324
|
return 'referenced';
|
|
325
|
+
case 'changed': {
|
|
326
|
+
// Kept either way; the record only says which notice is true. Unreadable: the delete's own answer.
|
|
327
|
+
const record = await deps.read(effect.ref).catch(() => undefined);
|
|
328
|
+
return record?.published ? 'published' : 'edited';
|
|
329
|
+
}
|
|
332
330
|
default:
|
|
333
331
|
return 'failed';
|
|
334
332
|
}
|