upgrade: adopt V8.31.1 (theme 1.3/1.4 redesigns, editor deep-canvas, guided launch, component library)
Overlay 190 upstream items (V8.22→8.31) preserving trade-pro theme, registry tests, and design docs. Re-applied full patch set on identical anchors (discovery/env_for/blocks.css/type-mapping/locale-override/ theme_color/frontend entries/test_theme_pro_v3 mods) plus compose pins. Verified: theme suites 35/35; dual-baseline full run 415 passed vs pristine 386+29 ours, FAILED-set diff EMPTY (same 10 Windows env fails). Live: container 8.31.1-dev.1 healthy, hesupipe 192 entries intact, trade-pro build 203 pages 0 errors. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -35,5 +35,119 @@
|
||||
function shouldAcceptPreview(requestedPage,requestedSeq,currentPage,currentSeq){
|
||||
return requestedPage===currentPage&&requestedSeq===currentSeq;
|
||||
}
|
||||
return {reorder,duplicate,filterBlocks,historyState,clampNumber,shouldAcceptPreview};
|
||||
function findTopIndex(rows,id){return Array.isArray(rows)&&typeof id==='string'?rows.findIndex(b=>b.id===id):-1;}
|
||||
function reorderById(rows,sourceId,targetId){
|
||||
const from=findTopIndex(rows,sourceId),to=findTopIndex(rows,targetId);
|
||||
return from<0||to<0||from===to?null:reorder(rows,from,to);
|
||||
}
|
||||
function adjacentWidths(block,widths){
|
||||
if(!block||block.type!=='layout'||!Array.isArray(block.columns)||!Array.isArray(widths)||widths.length!==block.columns.length)return null;
|
||||
const validated=widths.map(v=>Number(v));
|
||||
if(validated.some(v=>!Number.isFinite(v)||v<.5||v>4))return null;
|
||||
// Keep the same type and field shape expected by blueprints.validate_blocks.
|
||||
return validated.map(v=>Math.round(v*100)/100);
|
||||
}
|
||||
function safeCanvasCommand(command,rows,types){
|
||||
if(!command||typeof command!=='object'||typeof command.blockId!=='string'||command.blockId.length>150)return false;
|
||||
const i=findTopIndex(rows,command.blockId);
|
||||
if(i<0)return false;
|
||||
const actions=['select','edit','up','down','add-after','duplicate','delete','reorder','resize-columns','inline-text'];
|
||||
if(!actions.includes(command.action))return false;
|
||||
if(command.action==='reorder')return findTopIndex(rows,command.targetId)>=0;
|
||||
if(command.action==='resize-columns')return !!adjacentWidths(rows[i],command.widths);
|
||||
if(command.action==='inline-text')return (command.field==='title'||command.field==='description')&&
|
||||
typeof command.value==='string'&&command.value.length<=500&&types?.[rows[i].type]?.fields?.includes(command.field);
|
||||
return true;
|
||||
}
|
||||
// V8.24: resolve the actual parent of a nested child from the validated tree.
|
||||
// A client message supplies stable IDs, never trusted indexes or an editable path.
|
||||
// Renderer accepts at most two layout levels; a bounded walk avoids malformed cycles.
|
||||
function findNestedChild(rows,parentId,childId){
|
||||
if(typeof parentId!=='string'||typeof childId!=='string'||!Array.isArray(rows)||
|
||||
!parentId||!childId||parentId===childId)return null;
|
||||
for(let rootIndex=0;rootIndex<rows.length;rootIndex++){
|
||||
const root=rows[rootIndex];
|
||||
function visit(layout,depth,ancestors){
|
||||
if(!layout||layout.type!=='layout'||!Array.isArray(layout.columns)||depth>2)return null;
|
||||
for(let ci=0;ci<layout.columns.length;ci++){
|
||||
const children=layout.columns[ci]?.blocks;
|
||||
if(!Array.isArray(children))continue;
|
||||
for(let ri=0;ri<children.length;ri++){
|
||||
const child=children[ri];
|
||||
if(!child||typeof child!=='object')continue;
|
||||
if(layout.id===parentId&&child.id===childId)
|
||||
return {parentIndex:rootIndex,root,parent:layout,columnIndex:ci,
|
||||
childIndex:ri,child,depth,ancestors:[...ancestors,layout.id]};
|
||||
if(child.type==='layout'&&depth<2){
|
||||
const found=visit(child,depth+1,[...ancestors,layout.id]);
|
||||
if(found)return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const found=visit(root,0,[]);
|
||||
if(found)return found;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function findChildInRoot(rows,rootId,childId){
|
||||
const rootIndex=findTopIndex(rows,rootId);
|
||||
if(rootIndex<0||typeof childId!=='string')return null;
|
||||
const root=rows[rootIndex];
|
||||
if(root.type!=='layout')return null;
|
||||
function visit(layout,depth){
|
||||
if(depth>2)return null;
|
||||
for(const col of layout.columns||[]){
|
||||
for(const child of col.blocks||[]){
|
||||
if(child?.id===childId)return findNestedChild(rows,layout.id,childId);
|
||||
if(child?.type==='layout'){
|
||||
const match=visit(child,depth+1);
|
||||
if(match)return match;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return visit(root,0);
|
||||
}
|
||||
function findLayout(rows,layoutId){
|
||||
if(typeof layoutId!=='string'||!Array.isArray(rows))return null;
|
||||
const topIndex=findTopIndex(rows,layoutId);
|
||||
if(topIndex>=0)return rows[topIndex].type==='layout'?{layout:rows[topIndex],rootIndex:topIndex,depth:0}:null;
|
||||
for(let i=0;i<rows.length;i++){
|
||||
const root=rows[i];
|
||||
if(root.type!=='layout')continue;
|
||||
for(const col of root.columns||[])for(const child of col.blocks||[]){
|
||||
if(child?.id===layoutId && child.type==='layout')return {layout:child,rootIndex:i,depth:1};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function safeNestedCanvasCommand(command,rows,types){
|
||||
if(!command||typeof command!=='object')return false;
|
||||
const found=findNestedChild(rows,command.blockId,command.childId);
|
||||
if(!found)return false;
|
||||
const actions=['child-select','child-edit','child-up','child-down','child-duplicate','child-delete','child-inline-text'];
|
||||
if(!actions.includes(command.action))return false;
|
||||
if(command.action==='child-inline-text')return ['title','subtitle','description'].includes(command.field)
|
||||
&& types?.[found.child.type]?.fields?.includes(command.field)
|
||||
&& typeof command.value==='string'&&command.value.length<=500;
|
||||
return true;
|
||||
}
|
||||
function layoutPreset(block,preset){
|
||||
if(!block||block.type!=='layout'||!Array.isArray(block.columns))return null;
|
||||
const count=block.columns.length;if(count<1||count>4)return null;
|
||||
if(preset==='equal')return Array(count).fill(1);
|
||||
if(count!==2)return null;
|
||||
if(preset==='left-wide')return [2,1];
|
||||
if(preset==='right-wide')return [1,2];
|
||||
return null;
|
||||
}
|
||||
function moveNested(block,columnIndex,childIndex,direction){
|
||||
if(!block||block.type!=='layout'||![-1,1].includes(direction))return null;
|
||||
const list=block.columns?.[columnIndex]?.blocks;
|
||||
return Array.isArray(list)?reorder(list,childIndex,childIndex+direction):null;
|
||||
}
|
||||
return {reorder,duplicate,filterBlocks,historyState,clampNumber,shouldAcceptPreview,findTopIndex,reorderById,adjacentWidths,safeCanvasCommand,findNestedChild,findChildInRoot,findLayout,safeNestedCanvasCommand,layoutPreset,moveNested};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user