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:
浅念
2026-10-26 09:43:17 +08:00
parent a63548c645
commit 8884e100f9
647 changed files with 20853 additions and 271 deletions
+32
View File
@@ -42,3 +42,35 @@ test('numeric style input is clamped',()=>{
assert.equal(clampNumber(-5,0,120,10),0);
assert.equal(clampNumber('not-number',0,120,10),10);
});
const {findTopIndex,reorderById,adjacentWidths,safeCanvasCommand}=require('../app/static/editor-helpers.js');
test('canvas top-level ID commands reorder only known blocks',()=>{
const rows=[{id:'one',type:'hero'},{id:'two',type:'layout',columns:[{width:1},{width:1}]},{id:'three',type:'cta'}];
assert.equal(findTopIndex(rows,'three'),2);
assert.deepEqual(reorderById(rows,'one','three').map(x=>x.id),['two','three','one']);
assert.equal(reorderById(rows,'one','missing'),null);
assert.deepEqual(rows.map(x=>x.id),['one','two','three']);
});
test('canvas validates width limits, list length and types',()=>{
const layout={id:'L',type:'layout',columns:[{width:1},{width:1}]};
assert.deepEqual(adjacentWidths(layout,[1.6,.4]),null);
assert.deepEqual(adjacentWidths(layout,[1.6,.5]),[1.6,.5]);
assert.equal(adjacentWidths(layout,[1]),null);
assert.equal(adjacentWidths(layout,[1,Infinity]),null);
assert.equal(adjacentWidths({type:'hero'},[1,1]),null);
});
test('canvas rejects invented block ids, actions and unsafe text operations',()=>{
const rows=[{id:'hello',type:'hero'},{id:'cols',type:'layout',columns:[{width:1},{width:1}]}];
const fields={hero:{fields:['title','description','image']},layout:{fields:['columns']}};
const ok=(action,id='hello',rest={})=>safeCanvasCommand({action,blockId:id,...rest},rows,fields);
assert.equal(ok('select'),true);
assert.equal(ok('duplicate'),true);
assert.equal(ok('delete','fake'),false);
assert.equal(ok('execute'),false);
assert.equal(ok('reorder','hello',{targetId:'cols'}),true);
assert.equal(ok('reorder','hello',{targetId:'missing'}),false);
assert.equal(ok('resize-columns','cols',{widths:[2,1]}),true);
assert.equal(ok('resize-columns','cols',{widths:[5,1]}),false);
assert.equal(ok('inline-text','hello',{field:'title',value:'test'}),true);
assert.equal(ok('inline-text','hello',{field:'body',value:'<script>x</script>'}),false);
assert.equal(ok('inline-text','hello',{field:'title',value:'x'.repeat(501)}),false);
});