fix(global-sections): block structural edits inside a global-section instance

syncInstances rebuilds an instance's subtree from the master, so a node
dropped, pasted, duplicated or moved into an instance vanished from every page
on the next master edit. Chosen approach: BLOCK (not preserve-on-sync):
- every resolver component's Craft canMoveIn/canMoveOut refuses a canvas inside
  an instance (canvas DnD, Layers DnD, actions.move); synthetic shell-inner
  nodes now carry the component's rules instead of hardcoded allow-all
- insertBlock (BlocksPanel / EmptyContainerAdd / palette), paste (menu +
  shortcut), duplicate (menu + shortcut) and Sitesmith patch/section replace
  redirect to after the instance or refuse; no empty-container '+' inside one
The instance root still moves/duplicates as a whole; content overrides are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shadowdaoandClaude Opus 5.5 committed 2026-10-07 13:32:34 -07:00
1 parent ca500d36a9
commit 6d22aabdc0
11 files changed
+271 -17

No files matched your search

+25
View File
@@ -41,6 +41,7 @@ import { OpeningHours } from './business/OpeningHours';
import { StickyCallBar } from './business/StickyCallBar';
import { ReviewsStrip } from './business/ReviewsStrip';
import { BookingEmbed } from './business/BookingEmbed';
import { canvasInInstance } from '../utils/global-sections';
export const componentResolver = {
Container,
@@ -87,3 +88,27 @@ export const componentResolver = {
ReviewsStrip,
BookingEmbed,
};
/**
* #57 finding 4: no drop / move / reorder into, inside or out of a global
* section instance (its structure is rebuilt from the master on every sync).
* Wraps every component's Craft `canMoveIn` / `canMoveOut` -- the rules
* Craft's `isDroppable` checks for canvas DnD, Layers DnD and
* `actions.move` -- keeping each component's own rule.
*/
type RuleFn = (nodes: any, self: any, helper: any) => boolean;
for (const comp of Object.values(componentResolver) as any[]) {
const craft = (comp.craft ??= {});
const rules = craft.rules ?? {};
if (rules.__instanceGuard) continue;
const prevIn: RuleFn | undefined = rules.canMoveIn;
const prevOut: RuleFn | undefined = rules.canMoveOut;
craft.rules = {
...rules,
canMoveIn: (incoming: any, self: any, helper: any) =>
!canvasInInstance(self, helper) && (prevIn ? prevIn(incoming, self, helper) : true),
canMoveOut: (outgoing: any, self: any, helper: any) =>
!canvasInInstance(self, helper) && (prevOut ? prevOut(outgoing, self, helper) : true),
__instanceGuard: true,
};
}