upgrade: adopt V8.32.0-dev.1 (theme preview iframe fix, editor simple mode)

Overlay 46 upstream items; full patch set re-applied (all patched files
reset by overlay regardless of upstream change status — lesson recorded).
Verified: theme suites 35/35; dual-baseline 418 passed vs pristine
389+29 ours, FAILED-set diff EMPTY (same 10 Windows env fails).
Live: container 8.32.0-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 11:18:45 +08:00
parent 8884e100f9
commit 95982fad23
25 changed files with 561 additions and 232 deletions
-200
View File
@@ -1,200 +0,0 @@
Files ./.github/workflows/ci.yml and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/.github/workflows/ci.yml differ
Files ./.gitignore and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/.gitignore differ
Files ./README.md and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/README.md differ
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1: README_V8_26.md
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1: README_V8_28.md
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1: README_V8_29.md
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1: README_V8_30.md
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1: README_V8_31.md
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1: README_V8_31_1.md
Only in .: SHA256SUMS.txt
Files ./app/blueprints.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/blueprints.py differ
Files ./app/builder.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/builder.py differ
Files ./app/core.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/core.py differ
Files ./app/factory_api.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/factory_api.py differ
Files ./app/main.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/main.py differ
Files ./app/multilingual.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/multilingual.py differ
Files ./app/reusables.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/reusables.py differ
Files ./app/site_design_v87.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/site_design_v87.py differ
Files ./app/static/admin.js and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/static/admin.js differ
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/static: auto31.js
Files ./app/static/editor-helpers.js and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/static/editor-helpers.js differ
Files ./app/static/editor.css and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/static/editor.css differ
Files ./app/static/editor.js and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/static/editor.js differ
Files ./app/static/factory-v2.js and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/static/factory-v2.js differ
Files ./app/static/pro.js and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/static/pro.js differ
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/static: studio-canvas-bridge.js
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/static: studio-experience-v829.js
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/static: workspace-guide.js
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/static: workspace-launch.js
Files ./app/static/workspace.css and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/static/workspace.css differ
Files ./app/static/workspace.js and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/static/workspace.js differ
Files ./app/templates/admin.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/templates/admin.html differ
Files ./app/templates/editor.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/templates/editor.html differ
Files ./app/templates/theme-gallery.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/templates/theme-gallery.html differ
Files ./app/templates/workspace.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/templates/workspace.html differ
Files ./app/theme_pro.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/theme_pro.py differ
Files ./app/theme_store.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/theme_store.py differ
Files ./app/v8_workflows.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/v8_workflows.py differ
Files ./app/version.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/app/version.py differ
Files ./compose.enterprise.yaml and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/compose.enterprise.yaml differ
Files ./compose.yaml and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/compose.yaml differ
Only in ./docs: COMPLETION_PLAN.md
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/docs: V8_22_ADVANCED_VISUAL_STUDIO.md
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/docs: V8_23_ADVANCED_STUDIO.md
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/docs: V8_24_DEEP_NESTED_STUDIO.md
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/docs: V8_25_SHARED_DESIGN_SYSTEM.md
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/docs: V8_26_GLOBAL_DESIGN.md
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/docs: V8_27_USER_FRIENDLY_WORKSPACE.md
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/docs: V8_28_GUIDED_LAUNCH_CENTER.md
Only in ./docs: superpowers
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/examples: Studio_V8_22_画布级可视化编辑器_交互演示.html
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/examples: Studio_V8_23_嵌套组件编辑器_可交互预览.html
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/examples: Studio_V8_24_深层嵌套可视化编辑器_交互演示.html
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/examples: V8_25_公共组件_版本与同步演示.html
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/examples: V8_26_全站设计与导航_交互预览.html
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/examples: V8_27_新手友好建站工作台_交互演示.html
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/examples: V8_28_简洁上线操作中心_交互演示.html
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/examples: V8_29_主题与可视化编辑_离线操作演示.html
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/examples: V8_30_四套主题_真实页面交互预览.html
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/examples: V8_31_1_汽车主题性能与语义化图标_离线演示.html
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/examples: V8_31_汽车主题_完整页面交互预览.html
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/examples: v830-built-sites
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: V8_22_验收报告.html
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: V8_22_验收报告.json
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: V8_22_验收报告.md
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: V8_23_集成验收.html
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: V8_25_summary.json
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: V8_25_验收报告.html
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: V8_25_验收报告.md
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: V8_30_mobile_browser_audit.json
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: V8_30_主题设计与兼容验收.html
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: V8_31_汽车主题验收报告.html
Only in ./reports: mobile-v8213-browser-console.log
Only in ./reports: v8213-final-regression_logs
Only in ./reports: v8213-regression_logs
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: v823-regression
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: v824-regression
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: v825-regression
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: v826
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: v826-regression
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: v827
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: v828
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: v829
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: v829-browser
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: v829-regression
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: v830-regression
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: v831
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: v8311
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: v8311-regression
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: v8311-regression-part1.log
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/reports: v8311_regression_run.log
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: browser_guide_v827.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: browser_launch_v828.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: browser_studio_v829.py
Files ./scripts/check.ps1 and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts/check.ps1 differ
Files ./scripts/check.sh and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts/check.sh differ
Files ./scripts/init_config.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts/init_config.py differ
Files ./scripts/install_into_art_design_pro.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts/install_into_art_design_pro.py differ
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: make_theme_demo_v830.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: make_theme_demo_v8311.py
Files ./scripts/regression_v821.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts/regression_v821.py differ
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: regression_v823.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: regression_v824.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: regression_v825.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: regression_v826.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: regression_v827.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: regression_v828.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: regression_v829.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: regression_v830.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: regression_v8311.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: report_v825.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: report_v828.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: studio_canvas_isolated_browser_v822.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: studio_canvas_smoke_v822.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: studio_nested_browser_v823.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts: studio_nested_browser_v824.py
Files ./scripts/verify_art_design_pro_v819.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts/verify_art_design_pro_v819.py differ
Files ./scripts/verify_release.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/scripts/verify_release.py differ
Files ./tests/test_core_v4.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests/test_core_v4.py differ
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests: test_deep_nested_v824.py
Files ./tests/test_editor_easy_v8212.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests/test_editor_easy_v8212.py differ
Files ./tests/test_factory_v2.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests/test_factory_v2.py differ
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests: test_nested_helpers_v823.cjs
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests: test_nested_v823.py
Files ./tests/test_theme_pro_v3.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests/test_theme_pro_v3.py differ
Only in ./tests: test_theme_registry.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests: test_v822_canvas.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests: test_v825_shared_components.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests: test_v826_global_design.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests: test_v827_guided_workspace.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests: test_v828_launch_center.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests: test_v829_theme_studio.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests: test_v830_real_pages.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests: test_v8311_performance_icons.py
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests: test_v831_automotive_ref.py
Files ./tests/test_workspace_v810.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests/test_workspace_v810.py differ
Files ./tests/test_workspace_v812.py and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests/test_workspace_v812.py differ
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests_js: deep_nested_v824.test.cjs
Files ./tests_js/editor_ux.test.cjs and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests_js/editor_ux.test.cjs differ
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests_js: studio-experience-v829.test.cjs
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests_js: workspace_guide_v827.test.cjs
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/tests_js: workspace_launch_v828.test.cjs
Files ./themes/_professional_components.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/_professional_components.html differ
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive: auto-icons.svg
Files ./themes/automotive/manifest.json and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/manifest.json differ
Files ./themes/automotive/pages/about.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/pages/about.html differ
Files ./themes/automotive/pages/article.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/pages/article.html differ
Files ./themes/automotive/pages/brand.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/pages/brand.html differ
Files ./themes/automotive/pages/case.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/pages/case.html differ
Files ./themes/automotive/pages/category.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/pages/category.html differ
Files ./themes/automotive/pages/contact.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/pages/contact.html differ
Files ./themes/automotive/pages/faq.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/pages/faq.html differ
Files ./themes/automotive/pages/home.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/pages/home.html differ
Files ./themes/automotive/pages/listing.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/pages/listing.html differ
Files ./themes/automotive/pages/notfound.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/pages/notfound.html differ
Files ./themes/automotive/pages/product.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/pages/product.html differ
Files ./themes/automotive/pages/service.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/pages/service.html differ
Files ./themes/automotive/pages/standard.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/pages/standard.html differ
Files ./themes/automotive/pro.css and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/pro.css differ
Files ./themes/automotive/pro.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/pro.html differ
Files ./themes/automotive/theme.css and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/theme.css differ
Files ./themes/automotive/versions/1.0.0/pro.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/versions/1.0.0/pro.html differ
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/versions: 1.1.0
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/versions: 1.2.0
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/versions: 1.3.0
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/automotive/versions: 1.4.0
Files ./themes/corporate/manifest.json and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/corporate/manifest.json differ
Files ./themes/corporate/pages/about.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/corporate/pages/about.html differ
Files ./themes/corporate/pages/article.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/corporate/pages/article.html differ
Files ./themes/corporate/pages/contact.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/corporate/pages/contact.html differ
Files ./themes/corporate/pages/home.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/corporate/pages/home.html differ
Files ./themes/corporate/pages/listing.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/corporate/pages/listing.html differ
Files ./themes/corporate/pages/product.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/corporate/pages/product.html differ
Files ./themes/corporate/pro.css and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/corporate/pro.css differ
Files ./themes/corporate/theme.css and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/corporate/theme.css differ
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/corporate/versions: 1.1.0
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/corporate/versions: 1.2.0
Files ./themes/industrial/manifest.json and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/industrial/manifest.json differ
Files ./themes/industrial/pages/about.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/industrial/pages/about.html differ
Files ./themes/industrial/pages/article.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/industrial/pages/article.html differ
Files ./themes/industrial/pages/contact.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/industrial/pages/contact.html differ
Files ./themes/industrial/pages/home.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/industrial/pages/home.html differ
Files ./themes/industrial/pages/listing.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/industrial/pages/listing.html differ
Files ./themes/industrial/pages/product.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/industrial/pages/product.html differ
Files ./themes/industrial/pro.css and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/industrial/pro.css differ
Files ./themes/industrial/theme.css and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/industrial/theme.css differ
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/industrial/versions: 1.1.0
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/industrial/versions: 1.2.0
Files ./themes/minimal/manifest.json and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/minimal/manifest.json differ
Files ./themes/minimal/pages/about.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/minimal/pages/about.html differ
Files ./themes/minimal/pages/article.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/minimal/pages/article.html differ
Files ./themes/minimal/pages/contact.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/minimal/pages/contact.html differ
Files ./themes/minimal/pages/home.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/minimal/pages/home.html differ
Files ./themes/minimal/pages/listing.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/minimal/pages/listing.html differ
Files ./themes/minimal/pages/product.html and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/minimal/pages/product.html differ
Files ./themes/minimal/pro.css and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/minimal/pro.css differ
Files ./themes/minimal/theme.css and C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/minimal/theme.css differ
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/minimal/versions: 1.1.0
Only in C:/Users/30997/Downloads/SiteForge_Factory_V5_Web_Docker/SiteForge_Enterprise_V8_31_1/themes/minimal/versions: 1.2.0
Only in ./themes: trade-pro
+6
View File
@@ -1,3 +1,9 @@
# SiteForge Enterprise V8.32.0-dev.1
本轮修复后台主题预览空白、改进主题图库失败提示,并将可视化编辑器调整为**默认简洁模式**。完整变更、验证与限制见 [README_V8_32.md](README_V8_32.md) 和 [验收报告](reports/v832/V8_32_主题预览与编辑器验收.html)。
---
# SiteForge Enterprise V8.31.0-dev.1
本轮汽车主题按 [Magic Bridge Auto](https://magicbridgeauto.com/) 的导航及选车体验进行行业化重设计,完整说明见 [`README_V8_31.md`](README_V8_31.md)。保留汽车旧主题快照及其他三套主题。
+55
View File
@@ -0,0 +1,55 @@
# SiteForge Enterprise V8.32.0-dev.1
## 本版定位
在 V8.31.1 **完整源码**基础上,修复后台主题预览空白,并让页面编辑器对非技术使用者更加直观。保留四套主题、历史版本、公共组件、迁移、SEO、审批、Worker 构建等既有功能。
### 一、主题预览故障与修复
**根因:** 后端此前对所有非编辑器页面统一返回 `X-Frame-Options: DENY`,但主题图库和编辑器弹窗使用 iframe 展示 `/api/factory/themes/<id>/preview`。接口即使返回 HTTP 200,浏览器仍可能拒绝展示。
**修复机制:**
- 只对匹配 `^/api/factory/themes/[A-Za-z0-9_-]+/preview$` 的只读预览页面返回 `X-Frame-Options: SAMEORIGIN`,并限定 `Content-Security-Policy: frame-ancestors 'self'`。其他后台页面仍保留 `DENY`。
- 编辑器弹窗采用接口响应的 `srcdoc` 展示:iframe 继续使用 `sandbox="allow-scripts"`,**不允许 `allow-same-origin`**,所以主题脚本无法读取编辑器页面的 DOM 或会话;同时减少重复获取完整 HTML。
- 主题图库按主题和页面加载,并先检查 HTTP 状态;失败时显示错误码、重试按钮及“单独打开预览”。提供每张主题卡片直达其预览的入口 `?theme=automotive` 等。
- 展示预览不改变主题版本锁定,也不保存、发布网站。
### 二、可视化编辑器易用性
- 默认仅显示 **内容** 属性;复杂的样式与平板/手机设置通过“**更多设计设置**”展开,随时可收回。
- 顶部动态提示当前最应该做的动作:选区块、添加内容、编辑区块或保存草稿。
- 图片型组件在内容编辑区直接支持“**从图片素材库选择**”与“**上传新图片**”,包含内层已选中的图片组件。
- 小屏增加“关闭”编辑面板按钮,并验证 390px 下可操作且无横向溢出。
- 维持保存草稿与发布设计的区别:**保存/发布设计不会直接部署线上站点**,上线仍需构建和部署。
### 三、普通使用者操作指南
1. 登录后台,进入网站工作台的 **外观与设计**,在主题卡片点击 **预览页面效果**。亦可访问 `/theme-gallery`。
2. 选择主题、页面类型、电脑/平板/手机进行比较。需要看交互功能时在完整图库中操作。
3. 确定后回到工作台选择 **使用此主题**。预览本身**不会应用主题**。
4. 进入 **编辑页面**;点击中间画布或左侧区块,在右侧改文字与图片。
5. 更多排版参数点击 **更多设计设置**;完成后点击 **保存草稿**,核对后再 **发布设计**。
6. 之后到构建/交付中心构建静态网站,完成审批及正式部署。
### 四、验证与已知限制
- 400 项 Python 测试:18 个独立隔离批次覆盖全部 53 个模块;批次 15 初次因新增按钮文案断言未同步而失败,修正后 17 项重新通过;原始日志保留。
- 39 项历史 JavaScript 测试通过;全静态脚本 `node --check` 及版本检查通过。
- HTTP + Worker 路径:登录/CSRF → 创建站点和页面 → Worker 构建 → ZIP 下载 → 预览,通过。
- Chromium 隔离交互:简洁/高级切换、主题弹窗成功/失败、390px 小屏、面板关闭和主题图库错误重试,通过,JS 错误 0。
- **不能宣称生产上线验收完成:** 当前 Chromium 运行环境无法直接访问本地 HTTP 后台地址(策略拦截),浏览器测试使用真实页面脚本配模拟接口。正式生产环境还需验证登录态、反向代理安全响应头、Art Design Pro 官方工程编译、真实 PostgreSQL/Redis 与发布回滚。
### 五、验收命令
```bash
python -m pytest -q tests/test_v832_preview_easy_editor.py
python -m pytest -q tests/test_theme_pro_v3.py tests/test_professional_ui_v811.py
node --test tests_js/*.test.cjs
python scripts/http_end_to_end.py
python scripts/browser_v832_editor.py
python scripts/browser_v832_gallery.py
python scripts/verify_release.py
```
验收记录、截图、单批次复测及安全说明位于 `reports/v832/`。
-1
View File
@@ -18,7 +18,6 @@ def discover_themes():
raise RuntimeError(f'themes directory missing: {themes_dir}')
found = []
for entry in sorted(themes_dir.iterdir()):
# Underscore prefix marks non-theme assets (e.g. _professional_components.html).
if not entry.is_dir() or entry.name.startswith('_'):
continue
try:
+7 -1
View File
@@ -133,7 +133,13 @@ async def require_login(request:Request, call_next):
data=_json.loads(raw)
response=JSONResponse([x for x in data if x['id'] in ids])
response.headers['X-Content-Type-Options']='nosniff'
response.headers['X-Frame-Options']='SAMEORIGIN' if path.startswith('/editor/') else 'DENY'
# Only read-only theme previews (and the existing editor) may be framed by this origin.
# All other application pages stay protected from clickjacking.
is_theme_preview=bool(__import__('re').fullmatch(r'/api/factory/themes/[A-Za-z0-9_-]+/preview',path))
response.headers['X-Frame-Options']='SAMEORIGIN' if (path.startswith('/editor/') or is_theme_preview) else 'DENY'
if is_theme_preview:
response.headers['Content-Security-Policy']="frame-ancestors 'self'"
response.headers['Cache-Control']='no-store'
response.headers['Referrer-Policy']='strict-origin-when-cross-origin'
response.headers['Cache-Control']='no-store' if path.startswith('/api/auth/') else response.headers.get('Cache-Control','no-cache')
return response
+15
View File
@@ -156,3 +156,18 @@ iframe{min-height:590px;height:calc(100vh - 224px)}
.studio-body .left-pane,.studio-body .right-pane{top:140px!important}
.studio-actions-menu{right:0;max-width:calc(100vw - 18px)}
}
/* V8.32: first-use focused studio without removing advanced capabilities. */
.sf-next-action{margin-left:auto;font-size:12px;color:#30516b;font-weight:650;background:#eef4f8;padding:6px 10px;border-radius:7px;white-space:normal}
.sf-inspector-mode{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:9px 14px 12px;border-bottom:1px solid #eef1f4;color:#66798a;font-size:11px}
.sf-inspector-mode button{background:#f5f8fb;border:1px solid #dce5ee;border-radius:7px;padding:8px 10px;color:#285276;font-weight:700;cursor:pointer;font-size:11px}
body:not(.sf-advanced-mode) #tabStyle,body:not(.sf-advanced-mode) #tabResponsive{display:none!important}
.sf-photo-action{border:1px solid #dbe9f5;background:#f6fafd;border-radius:10px;display:grid;gap:7px;padding:12px;margin:14px 0}
.sf-photo-action button{width:100%;justify-content:center}
.sf-photo-action small{font-size:11px;color:#64778b;line-height:1.5}
.sf-close-inspector{display:none;background:white;border:1px solid #d8e1ea;border-radius:8px;padding:7px 10px;color:#385872;cursor:pointer;font-size:11px}
.sf-steps{justify-content:flex-start}.sf-steps .sf-guide-link{margin-left:0}
@media(max-width:1060px){.sf-close-inspector{display:inline-flex}.sf-next-action{display:none}}
@media(max-width:720px){.sf-steps .sf-guide-link{margin-left:auto}.sf-inspector-mode{padding:8px 11px}}
.sf-photo-action strong{font-size:13px;color:#28516e}.sf-direct-upload{display:grid;gap:7px;color:#506b82;font-size:12px;font-weight:650}.sf-direct-upload input{font-size:11px;width:100%;max-width:100%}
+30 -8
View File
@@ -13,6 +13,7 @@ let libraryOpen=false;
let sharedPreview=null;
let siteNavDraft=[],siteDesignReviewToken=null;
let selectedChildId=null;
let advancedMode=false,themePreviewSeq=0,themePreviewAbort=null;
let inspectorTab='content',paletteSearch='',blockSearch='',previewRequest=0,previewController=null,savePromise=null,lastContinuousEdit=0,insertAfterId=null;
let snapshots=[],snapshotCursor=-1,draggedChild=null,draftRevision=0,siteDesign=null,siteDesignRevision=0,siteDesignPreview=null;
@@ -284,24 +285,40 @@ const Studio={
filterPalette(value){paletteSearch=String(value||'').slice(0,70);Studio.renderPalette()},
clearPaletteSearch(){paletteSearch='';const inp=$('#paletteSearch');if(inp){inp.value='';inp.focus()}Studio.renderPalette()},
showThemePreview(){const dialog=$('#themePreviewModal');if(!dialog)return;const current=['automotive','industrial','corporate','minimal'].includes(site?.theme)?site.theme:'corporate';$('#themePreviewTheme').value=current;if(!dialog.open)dialog.showModal();Studio.themeDevice('desktop');Studio.updateThemePreview()},
closeThemePreview(){const dialog=$('#themePreviewModal');if(dialog?.open)dialog.close();const frame=$('#themePreviewFrame');if(frame)frame.removeAttribute('src')},
closeThemePreview(){themePreviewSeq++;themePreviewAbort?.abort();const dialog=$('#themePreviewModal');if(dialog?.open)dialog.close();const frame=$('#themePreviewFrame');if(frame){frame.removeAttribute('src');frame.removeAttribute('srcdoc')}},
themeDevice(device){
const width={desktop:'100%',tablet:'780px',phone:'390px'}[device];if(!width)return;
$('#themePreviewFrameShell').style.width=width;
document.querySelectorAll('[data-theme-device]').forEach(btn=>btn.setAttribute('aria-pressed',String(btn.dataset.themeDevice===device)));
},
updateThemePreview(){
async updateThemePreview(){
const theme=$('#themePreviewTheme')?.value,page=$('#themePreviewPage')?.value;
const url=window.SiteForgeStudioExperience.previewUrl(theme,page);
if(!url)return;
const frame=$('#themePreviewFrame'),status=$('#themePreviewStatus');if(!frame)return;
status.textContent='正在加载主题示例…(不影响已保存的页面)';
frame.onload=()=>{if($('#themePreviewModal')?.open)status.textContent='已加载示例。可切换主题、页面类型和设备继续比较。'};
frame.onerror=()=>{status.textContent='示例加载失败,请稍后重试。'};
frame.src=url;
const frame=$('#themePreviewFrame'),status=$('#themePreviewStatus'),retry=$('#themePreviewRetry');if(!frame)return;
const seq=++themePreviewSeq;
themePreviewAbort?.abort();themePreviewAbort=new AbortController();
status.textContent='正在检查主题预览…';retry.hidden=true;frame.removeAttribute('src');frame.removeAttribute('srcdoc');
try{
const response=await fetch(url,{credentials:'same-origin',signal:themePreviewAbort.signal,cache:'no-store'});
if(!response.ok||!(response.headers.get('content-type')||'').includes('text/html'))throw Error(response.status===401?'登录已过期':`服务返回 ${response.status}`);
const content=await response.text();
if(seq!==themePreviewSeq||!$('#themePreviewModal')?.open)return;
// srcdoc keeps the preview in an opaque sandboxed origin: no allow-same-origin,
// no repeated HTML request, no frame-header conflict, and no access to editor data.
const base=new URL(url,['http:','https:'].includes(location.protocol)?location.href:'https://siteforge.invalid').href;
const safeBase=html(base);
const withBase=/<head(?:\s[^>]*)?>/i.test(content)?content.replace(/<head(\s[^>]*)?>/i,(match)=>match+`<base href=\"${safeBase}\">`):`<base href=\"${safeBase}\">`+content;
status.textContent='正在渲染主题页面…';
frame.onload=()=>{if(seq===themePreviewSeq&&$('#themePreviewModal')?.open)status.textContent='预览已显示 · 可切换主题和设备';};
frame.srcdoc=withBase;
}catch(error){
if(seq!==themePreviewSeq||error.name==='AbortError')return;
status.textContent='预览失败:'+error.message+'。请点击重新加载或在完整主题图库中打开。';retry.hidden=false;
}
}, toggleLibrary(force){libraryOpen=typeof force==='boolean'?force:!libraryOpen;const el=$('#sfLibraryPanel'),btn=$('#sfAddBlock');if(el)el.hidden=!libraryOpen;if(btn){btn.setAttribute('aria-expanded',String(libraryOpen));btn.textContent=libraryOpen?'× 收起区块库':'+ 添加内容区块'};Studio.updateGuidance()},
showHelp(){const dlg=$('#sfGuideDialog');if(dlg&&!dlg.open)dlg.showModal()},
updateGuidance(){const one=$('#sfStep1'),two=$('#sfStep2'),three=$('#sfStep3');if(!one||!two||!three)return;one.classList.toggle('active',selected<0||libraryOpen);two.classList.toggle('active',selected>=0&&!dirty&&!libraryOpen);three.classList.toggle('active',dirty);},
updateGuidance(){const one=$('#sfStep1'),two=$('#sfStep2'),three=$('#sfStep3');if(!one||!two||!three)return;one.classList.toggle('active',selected<0||libraryOpen);two.classList.toggle('active',selected>=0&&!dirty&&!libraryOpen);three.classList.toggle('active',dirty);const note=$('#sfNextAction');if(note)note.textContent=dirty?'更改尚未保存:点击右上角「保存草稿」':libraryOpen?'选择一种内容,添加后直接修改文字或图片':selected>=0?'正在编辑「'+(schema?.blocks?.[blocks[selected]?.type]?.name||'当前区块')+'」,右侧可直接修改':'先在左侧添加区块或选中页面内容';},
draw(){
const list=$('#blockList');$('#blockCount').textContent=blocks.length+' 区块';
const found=editorTools.filterBlocks(blocks,blockSearch,schema.blocks);
@@ -315,6 +332,8 @@ const Studio={
blockKeydown(event,i){if(event.target!==event.currentTarget)return;if(event.key==='Enter'||event.key===' '){event.preventDefault();Studio.select(i)}else if(event.altKey && (event.key==='ArrowUp'||event.key==='ArrowDown')){event.preventDefault();Studio.moveBlock(i,event.key==='ArrowUp'?-1:1)}},
changeInspectorTab(tab){if(!['content','style','responsive'].includes(tab))return;inspectorTab=tab;Studio.applyInspectorTab()},
applyInspectorTab(){for(const key of ['content','style','responsive']){const active=key===inspectorTab;const btn=$('#tab'+key.charAt(0).toUpperCase()+key.slice(1));btn?.setAttribute('aria-selected',String(active));const pane=$('[data-inspector-section="'+key+'"]');if(pane)pane.hidden=!active}},
toggleAdvanced(){advancedMode=!advancedMode;document.body.classList.toggle('sf-advanced-mode',advancedMode);const button=$('#studioAdvancedButton');button?.setAttribute('aria-pressed',String(advancedMode));if(button)button.textContent=advancedMode?'收起高级设置':'更多设计设置';const detail=$('#sfModeExplanation');if(detail)detail.textContent=advancedMode?'可调整布局、样式和手机显示':'当前显示常用设置';if(!advancedMode){inspectorTab='content';Studio.applyInspectorTab()}},
closePane(){document.body.classList.remove('show-inspector','show-structure')},
togglePane(which){const type=which==='left'?'show-structure':'show-inspector';document.body.classList.toggle(type);if(type==='show-structure')document.body.classList.remove('show-inspector');else document.body.classList.remove('show-structure');},
updateHistoryButtons(){const state=editorTools.historyState(snapshots,snapshotCursor);for(const id of ['#studioUndo','#quickUndo']){const el=$(id);if(el)el.disabled=!state.canUndo}for(const id of ['#studioRedo','#quickRedo']){const el=$(id);if(el)el.disabled=!state.canRedo}},
@@ -362,6 +381,9 @@ const Studio={
if(['title','subtitle','description','image','body'].includes(field))out+='<small class="prop-tip">可输入 {{site.name}}、{{page.title}} 等变量。</small>';
out+='</div>';
}
const mediaTarget=selectedChildId?editorTools.findChildInRoot(blocks,b.id,selectedChildId)?.child:b;
const mediaFields=schema.blocks[mediaTarget?.type]?.fields||[];
if(mediaTarget&&(['gallery','carousel'].includes(mediaTarget.type)||mediaFields.includes('image')))out+=`<div class="sf-photo-action"><strong>图片设置</strong><button class="ui-btn ghost" type="button" onclick="Studio.chooseMedia()">从图片素材库选择</button><label class="sf-direct-upload">或上传一张新图片<input aria-label="上传新图片" type="file" accept="image/png,image/jpeg,image/webp,image/gif,image/avif" onchange="Studio.uploadImage(this.files[0]);this.value=''"></label><small>预览先显示在草稿里,点击保存后才会保留。</small></div>`;
if(b.type==='layout')out+=Studio.childQuickEditor(b);
out+='</div><div data-inspector-section="responsive">';
if(b.type==='layout'){
+1 -1
View File
@@ -123,7 +123,7 @@ function content(){if(!currentSite())return noSite();let arr=state.entries.filte
(arr.length?`<div class="table-wrap"><table class="data-table"><thead><tr><th>名称</th><th>内容类型</th><th>页面地址</th><th>状态</th><th>操作</th></tr></thead><tbody>${arr.slice(0,200).map(e=>`<tr><td><strong>${esc(e.title)}</strong></td><td>${esc(typeName[e.type]||e.type)}</td><td class="muted">${esc(e.route)}</td><td><span class="pill ${e.status==='draft'?'warn':'success'}">${e.status==='draft'?'草稿':'已保存'}</span></td><td class="action-cell"><button class="btn btn-tiny" data-action="edit-entry" data-id="${esc(e.id)}">修改内容</button> <a class="btn btn-tiny" href="/editor/${encodeURIComponent(state.site.id)}/${encodeURIComponent(e.id)}">设计页面 ↗</a></td></tr>`).join('')}</tbody></table></div>`:emptyState('没有找到内容','可以清除筛选条件,或者添加新内容。',actionButton('+ 添加内容','new-entry')))+
`${arr.length>200?'<p class="muted">当前显示前 200 条,请使用搜索缩小范围。</p>':''}</div>`;
}
function design(){if(!currentSite())return noSite();const s=state.site;const cards=Object.entries(themeName).map(([id,name])=>`<div class="theme-card"><div class="theme-preview ${id}">${name}</div><div class="theme-desc"><h3>${name} ${s.theme===id?'<span class="pill success">当前使用</span>':''}</h3><p>${({automotive:'适合汽车出口、车型及品牌展示',industrial:'适合制造业、产品与技术参数',corporate:'适合集团公司、服务和案例',minimal:'适合作品展示和品牌形象','trade-pro':'适合外贸 B2B 英文站点与询盘'})[id]}</p><button class="btn btn-tiny ${s.theme===id?'btn-muted':'btn-primary'}" ${s.theme===id?'disabled':''} data-action="set-theme" data-theme="${id}">${s.theme===id?'正在使用':'使用此主题'}</button></div></div>`).join('');
function design(){if(!currentSite())return noSite();const s=state.site;const cards=Object.entries(themeName).map(([id,name])=>`<div class="theme-card"><div class="theme-preview ${id}">${name}</div><div class="theme-desc"><h3>${name} ${s.theme===id?'<span class="pill success">当前使用</span>':''}</h3><p>${({automotive:'适合汽车出口、车型及品牌展示',industrial:'适合制造业、产品与技术参数',corporate:'适合集团公司、服务和案例',minimal:'适合作品展示和品牌形象','trade-pro':'适合外贸 B2B 英文站点与询盘'})[id]}</p><button class="btn btn-tiny ${s.theme===id?'btn-muted':'btn-primary'}" ${s.theme===id?'disabled':''} data-action="set-theme" data-theme="${id}">${s.theme===id?'正在使用':'使用此主题'}</button><a class="btn btn-tiny btn-muted" href="/theme-gallery?theme=${encodeURIComponent(id)}" target="_blank" rel="noopener">预览页面效果</a></div></div>`).join('');
return htmlHeading('外观与设计','先选一套网站风格,再打开可视化编辑器修改图文、导航和手机端布局。')+
`<div class="section-heading"><div><h2>第一步 · 选择行业主题</h2><p>更换主题不会删除您已经录入的业务内容和 URL。</p></div><a href="/theme-gallery" class="btn btn-tiny">查看完整主题预览 ↗</a></div><div class="choice-cards">${cards}</div>`+
`<div class="section-heading"><div><h2>第二步 · 设计网页</h2><p>挑选要修改的页面;可在设计器中编辑导航、页眉、页脚及各设备布局。</p></div></div><div class="panel"><div class="panel-head"><div><h2>选择要设计的页面</h2><p>修改先保存为草稿,正式发布设计后才会参与下一次静态构建。</p></div></div><div class="grid-2 grid-equal">${state.entries.filter(e=>e.type==='page').slice(0,8).map(e=>`<a class="site-card" href="/editor/${encodeURIComponent(s.id)}/${encodeURIComponent(e.id)}"><span class="site-icon">▤</span><span><h3>${esc(e.title)}</h3><p>${esc(e.route)}</p></span><span class="arrow">›</span></a>`).join('')||'<div class="muted">尚无页面,请先添加内容。</div>'}</div></div><div class="help-hint" style="margin-top:19px"><strong>提示</strong>:网站设置用于公司信息和域名;可视化编辑器用于调整页面外观。两者不会混在同一表单里。</div>`;
+5 -5
View File
@@ -4,7 +4,7 @@
<header class="studio-top sf-easy-top">
<div class="brand"><a href="/" title="返回网站管理">◈</a><div><b>SiteForge</b><small>页面编辑</small></div></div>
<div class="file-name sf-easy-file"><small id="siteName">加载中</small><strong id="pageName">页面编辑器</strong></div>
<button type="button" id="studioThemePreviewButton" class="ui-btn ghost sf-theme-preview-entry" onclick="Studio.showThemePreview()" title="不影响当前网站,先比较四套主题">◫ 主题效果</button>
<button type="button" id="studioThemePreviewButton" class="ui-btn ghost sf-theme-preview-entry" onclick="Studio.showThemePreview()" title="不影响当前网站,先比较四套主题">主题预览</button>
<div class="sf-pane-controls"><button type="button" onclick="Studio.togglePane('left')">☰ 内容</button><button type="button" onclick="Studio.togglePane('right')">⚙ 编辑</button></div>
<div class="top-center sf-easy-device" role="group" aria-label="预览设备">
<button id="deviceDesktop" class="icon-btn on" onclick="Studio.device('desktop')" title="电脑预览" aria-label="电脑预览">电脑</button>
@@ -26,7 +26,7 @@
<button class="ui-btn ghost" onclick="Studio.discardDraft()">放弃草稿</button>
</div></details>
</header>
<div class="sf-steps" aria-label="编辑操作指引"><div class="sf-steps-label">只需 3 步</div><span class="sf-step" id="sfStep1">① 点击页面中的内容</span><span class="sf-step-separator">→</span><span class="sf-step" id="sfStep2">② 直接编辑或调整布局</span><span class="sf-step-separator">→</span><span class="sf-step" id="sfStep3">③ 保存并发布</span><button type="button" class="sf-guide-link" onclick="Studio.showHelp()">新手使用指南 ?</button></div>
<div class="sf-steps" aria-label="编辑操作指引"><div class="sf-steps-label">只需 3 步</div><span class="sf-step" id="sfStep1">① 点击页面中的内容</span><span class="sf-step-separator">→</span><span class="sf-step" id="sfStep2">② 直接编辑或调整布局</span><span class="sf-step-separator">→</span><span class="sf-step" id="sfStep3">③ 保存并发布</span><span id="sfNextAction" class="sf-next-action" role="status" aria-live="polite">点击左侧区块或画布内容开始编辑</span><button type="button" class="sf-guide-link" onclick="Studio.showHelp()">使用帮助</button></div>
<div class="studio-body">
<aside class="left-pane" aria-label="页面内容和区块">
<div class="pane-heading"><div><span class="overline">页面内容</span><h3>编辑页面</h3></div><span class="chip" id="blockCount">0 个区块</span></div>
@@ -47,8 +47,8 @@
</aside>
<section class="preview-workspace"><div class="preview-head"><span class="live-dot" id="previewDot"></span><span id="previewState" role="status" aria-live="polite">实时预览</span><code id="routeDisplay">/</code><span class="preview-note">单击区块或列内组件即可编辑 · 拖动 ⠿ 可排序 · 拖动列间分隔线可调宽</span></div><div class="stage"><div class="device-frame" id="deviceFrame"><iframe id="sitePreview" title="页面预览" sandbox="allow-scripts" referrerpolicy="no-referrer"></iframe></div></div>
<div class="stage-footer"><span id="deviceDetails">桌面端 · 自适应宽度</span><span>当前是草稿预览,不是线上网站</span></div></section>
<aside class="right-pane"><div class="pane-heading"><div><span class="overline">步骤 2 · 修改内容</span><h3>编辑选中的区块</h3></div><span class="chip" id="blockTypeLabel">页面</span></div>
<div class="inspector-nav" role="tablist" aria-label="属性编辑分类"><button type="button" role="tab" id="tabContent" aria-selected="true" onclick="Studio.changeInspectorTab('content')">内容</button><button type="button" role="tab" id="tabStyle" aria-selected="false" onclick="Studio.changeInspectorTab('style')">样式</button><button type="button" role="tab" id="tabResponsive" aria-selected="false" onclick="Studio.changeInspectorTab('responsive')">手机/平板</button></div>
<aside class="right-pane"><div class="pane-heading"><div><span class="overline">当前内容</span><h3>修改内容</h3></div><button type="button" id="sfCloseInspector" class="sf-close-inspector" onclick="Studio.closePane()" aria-label="关闭编辑面板">关闭</button><span class="chip" id="blockTypeLabel">页面</span></div>
<div class="sf-inspector-mode"><span id="sfModeExplanation">当前显示常用设置</span><button type="button" id="studioAdvancedButton" aria-pressed="false" onclick="Studio.toggleAdvanced()">更多设计设置</button></div><div class="inspector-nav" role="tablist" aria-label="属性编辑分类"><button type="button" role="tab" id="tabContent" aria-selected="true" onclick="Studio.changeInspectorTab('content')">内容</button><button type="button" role="tab" id="tabStyle" aria-selected="false" onclick="Studio.changeInspectorTab('style')">样式</button><button type="button" role="tab" id="tabResponsive" aria-selected="false" onclick="Studio.changeInspectorTab('responsive')">手机/平板</button></div>
<div class="inspector" id="inspector"><div class="no-selection">直接点击中间画布的任意区块,即可开始编辑。</div></div>
<details class="right-foot sf-advanced-foot"><summary>高级:动态数据变量</summary><p>支持 <code>{{site.name}}</code>、<code>{{page.title}}</code> 等变量;动态内容列表可以按网站内容类型绑定。</p></details>
</aside>
@@ -57,7 +57,7 @@
<div class="sf-showcase-head"><div><small>仅预览 · 不修改网站内容</small><h2 id="themePreviewTitle">选择适合网站的主题效果</h2><p>先比较布局和手机显示效果,再决定是否到「网站设计」中应用主题。</p></div><button type="button" class="ui-btn ghost" onclick="Studio.closeThemePreview()" aria-label="关闭主题预览">关闭 ×</button></div>
<div class="sf-showcase-tools"><label>主题风格<select id="themePreviewTheme" onchange="Studio.updateThemePreview()"><option value="automotive">汽车与出行</option><option value="industrial">工业与制造</option><option value="corporate">企业品牌</option><option value="minimal">简约展示</option></select></label><label>参考页面<select id="themePreviewPage" onchange="Studio.updateThemePreview()"><option value="home">首页</option><option value="listing">产品列表</option><option value="product">产品详情</option><option value="article">文章详情</option><option value="about">关于我们</option><option value="contact">联系我们</option></select></label><div class="sf-showcase-device" role="group" aria-label="主题参考设备"><button type="button" data-theme-device="desktop" aria-pressed="true" onclick="Studio.themeDevice('desktop')">电脑</button><button type="button" data-theme-device="tablet" aria-pressed="false" onclick="Studio.themeDevice('tablet')">平板</button><button type="button" data-theme-device="phone" aria-pressed="false" onclick="Studio.themeDevice('phone')">手机</button></div></div>
<p class="sf-showcase-status" id="themePreviewStatus" role="status" aria-live="polite">示例使用主题最新版本,可能与你网站锁定的主题版本不同。不会修改当前页面。</p><div class="sf-showcase-stage"><div id="themePreviewFrameShell" class="sf-showcase-frame"><iframe id="themePreviewFrame" title="主题示例页面" sandbox="allow-scripts" referrerpolicy="no-referrer" loading="lazy"></iframe></div></div>
<div class="sf-showcase-footer"><span>预览不会保存、更换或发布主题。确认效果后请到网站设计里操作。</span><a class="ui-btn ghost" href="/theme-gallery" target="_blank" rel="noopener">打开完整主题图库 ↗</a></div>
<div class="sf-showcase-footer"><span>编辑器仅查看外观;完整图库支持交互。预览不会保存或发布主题。<button type="button" id="themePreviewRetry" class="ui-btn ghost" onclick="Studio.updateThemePreview()" hidden>重新加载</button></span><a class="ui-btn ghost" href="/theme-gallery" target="_blank" rel="noopener">打开完整主题图库 ↗</a></div>
</dialog>
<dialog id="sfGuideDialog" class="sf-help-dialog" aria-labelledby="sfGuideTitle">
<div class="sf-help-head"><div><span class="overline">新手指南</span><h2 id="sfGuideTitle">3 分钟学会编辑页面</h2></div><button type="button" class="ui-btn ghost" onclick="document.querySelector('#sfGuideDialog').close()">关闭 ×</button></div>
+41 -6
View File
@@ -1,15 +1,50 @@
<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>SiteForge · 完整主题预览中心</title>
<style>*{box-sizing:border-box}body{margin:0;background:#eef1f5;font:14px/1.6 system-ui,'Microsoft YaHei',sans-serif;color:#172c42}header{display:flex;align-items:center;justify-content:space-between;padding:14px 26px;background:#fff;border-bottom:1px solid #dce3eb;gap:24px;flex-wrap:wrap}header strong{font-size:17px}header strong span{background:#15253e;color:#fff;border-radius:6px;padding:6px 8px;margin-right:8px}header a{color:#255b99;text-decoration:none}.controls{display:flex;align-items:end;flex-wrap:wrap;gap:12px;padding:18px 26px;border-bottom:1px solid #dce3eb;background:white}.controls label{font-weight:700;color:#5c7083;font-size:11px;display:grid;gap:6px}.controls select{border:1px solid #ccd5df;background:#fff;color:#192d41;padding:10px 14px;border-radius:7px;min-width:132px;font-size:13px}.devices{display:flex;gap:4px;margin-left:auto;background:#f0f3f8;border-radius:8px;padding:4px}.devices button{border:0;background:none;padding:9px 14px;border-radius:6px;cursor:pointer;color:#52697e}.devices button.active{background:#1e446c;color:white}.stage{padding:22px 18px 80px;display:flex;justify-content:center;overflow:auto}.frame{width:100%;height:calc(100vh - 210px);min-height:520px;background:white;box-shadow:0 16px 50px #12304720;border-radius:10px;overflow:hidden;transition:width .25s}.frame iframe{border:0;width:100%;height:100%;background:white}.help{text-align:center;color:#6d8296;font-size:12px;margin:-55px auto 28px}.error{color:#c14f45;display:none}@media(max-width:700px){header,.controls{padding:12px}header strong{font-size:14px}.devices{margin-left:0}.frame{min-height:450px}}</style><link rel="stylesheet" href="/ui/professional-ui.css"><script defer src="/ui/professional-ui.js"></script></head>
<style>*{box-sizing:border-box}body{margin:0;background:#eef1f5;font:14px/1.6 system-ui,'Microsoft YaHei',sans-serif;color:#172c42}header{display:flex;align-items:center;justify-content:space-between;padding:14px 26px;background:#fff;border-bottom:1px solid #dce3eb;gap:24px;flex-wrap:wrap}header strong{font-size:17px}header strong span{background:#15253e;color:#fff;border-radius:6px;padding:6px 8px;margin-right:8px}header a{color:#255b99;text-decoration:none}.controls{display:flex;align-items:end;flex-wrap:wrap;gap:12px;padding:18px 26px;border-bottom:1px solid #dce3eb;background:white}.controls label{font-weight:700;color:#5c7083;font-size:11px;display:grid;gap:6px}.controls select{border:1px solid #ccd5df;background:#fff;color:#192d41;padding:10px 14px;border-radius:7px;min-width:132px;font-size:13px}.devices{display:flex;gap:4px;margin-left:auto;background:#f0f3f8;border-radius:8px;padding:4px}.devices button{border:0;background:none;padding:9px 14px;border-radius:6px;cursor:pointer;color:#52697e}.devices button.active{background:#1e446c;color:white}.stage{padding:22px 18px 80px;display:flex;justify-content:center;overflow:auto}.frame{width:100%;height:calc(100vh - 210px);min-height:520px;background:white;box-shadow:0 16px 50px #12304720;border-radius:10px;overflow:hidden;transition:width .25s}.frame iframe{border:0;width:100%;height:100%;background:white}.help{text-align:center;color:#6d8296;font-size:12px;margin:-55px auto 28px}.sf-preview-status{font-size:12px;color:#566d83;max-width:350px}.sf-preview-status.error{color:#aa3434}.sf-preview-retry{padding:8px 12px;border:1px solid #cbd7e4;border-radius:7px;background:white;color:#28547f;cursor:pointer;text-decoration:none;font-size:12px}@media(max-width:700px){header,.controls{padding:12px}header strong{font-size:14px}.devices{margin-left:0}.frame{min-height:450px}}</style><link rel="stylesheet" href="/ui/professional-ui.css"><script defer src="/ui/professional-ui.js"></script></head>
<body data-sf-page="theme"><header><strong><span>SF</span> SiteForge · 主题预览中心</strong><a href="/factory-studio">返回网站工厂 →</a></header>
<section class="controls"><label>主题<select id="theme"><option value="automotive">Automotive · 汽车与出行</option><option value="industrial">Industrial · 工业制造</option><option value="corporate">Corporate · 企业品牌</option><option value="minimal">Minimal · 极简设计</option><option value="trade-pro">Trade Pro · International B2B</option></select></label>
<label>页面类型<select id="page"><option value="home">首页</option><option value="about">关于我们</option><option value="listing">产品 / 分类列表</option><option value="category">分类详情</option><option value="product">产品详情</option><option value="brand">品牌详情</option><option value="article">新闻详情</option><option value="case">案例详情</option><option value="service">服务详情</option><option value="contact">联系我们</option><option value="faq">常见问题</option><option value="standard">标准页面</option><option value="notfound">404</option></select></label>
<label>主题版本<select id="version"><option value="">最新</option></select></label><div class="devices"><button data-size="100%" class="active">PC</button><button data-size="820px">平板</button><button data-size="390px">手机</button></div><span class="error" id="error">预览失败,请确认已登录。</span></section>
<label>主题版本<select id="version"><option value="">最新</option></select></label><div class="devices"><button data-size="100%" class="active">PC</button><button data-size="820px">平板</button><button data-size="390px">手机</button></div><span id="previewStatus" role="status" aria-live="polite" class="sf-preview-status">准备加载预览…</span><button type="button" id="retryPreview" class="sf-preview-retry" hidden>重新加载</button><a id="openPreview" target="_blank" rel="noopener" class="sf-preview-retry" href="#">单独打开预览</a></section>
<div class="stage"><div class="frame" id="frame"><iframe id="preview" title="主题真实页面预览" loading="eager"></iframe></div></div>
<div class="help">预览使用真实页面模板与演示数据。实际网站构建后将使用你的品牌信息和内容。</div>
<script>
const theme=document.querySelector('#theme'),page=document.querySelector('#page'),version=document.querySelector('#version'),frame=document.querySelector('#frame'),iframe=document.querySelector('#preview');
function show(){iframe.src=`/api/factory/themes/${encodeURIComponent(theme.value)}/preview?page_type=${encodeURIComponent(page.value)}&version=${encodeURIComponent(version.value)}`;}
async function loadVersions(){const response=await fetch(`/api/factory/themes/${encodeURIComponent(theme.value)}/versions`,{credentials:'same-origin'});if(!response.ok)throw Error('无法读取主题版本');const arr=await response.json();version.replaceChildren();arr.forEach(x=>{const op=new Option('v'+x.version,x.version);version.append(op);});show();}
theme.addEventListener('change',()=>loadVersions().catch(()=>document.querySelector('#error').style.display='inline'));
[page,version].forEach(x=>x.addEventListener('change',show));document.querySelectorAll('.devices button').forEach(x=>x.addEventListener('click',()=>{document.querySelectorAll('.devices button').forEach(y=>y.classList.remove('active'));x.classList.add('active');frame.style.width=x.dataset.size;}));loadVersions().catch(()=>document.querySelector('#error').style.display='inline');
const status=document.querySelector('#previewStatus'),retry=document.querySelector('#retryPreview'),open=document.querySelector('#openPreview');
let sequence=0,controller=null;
function link(){return `/api/factory/themes/${encodeURIComponent(theme.value)}/preview?page_type=${encodeURIComponent(page.value)}&version=${encodeURIComponent(version.value)}`;}
async function show(){
const seq=++sequence;
controller?.abort();controller=new AbortController();
const url=link();open.href=url;retry.hidden=true;status.classList.remove('error');status.textContent='正在加载预览…';
iframe.removeAttribute('src');
try{
// A successful iframe load event can also mean a browser error page, so first verify HTTP.
const response=await fetch(url,{credentials:'same-origin',signal:controller.signal,cache:'no-store'});
if(!response.ok||!(response.headers.get('content-type')||'').includes('text/html'))throw Error(response.status===401?'登录已过期,请重新登录':`页面返回 ${response.status},无法预览`);
if(seq!==sequence)return;
iframe.onload=()=>{if(seq===sequence)status.textContent='预览已打开 · 可以切换页面或设备';};
iframe.src=url;
}catch(error){
if(seq!==sequence||error.name==='AbortError')return;
status.textContent=`加载失败:${error.message}。请重试或单独打开预览。`;
status.classList.add('error');retry.hidden=false;
}
}
async function loadVersions(){
const seq=++sequence;controller?.abort();
version.replaceChildren(new Option('最新版本',''));version.disabled=true;
try{
const response=await fetch(`/api/factory/themes/${encodeURIComponent(theme.value)}/versions`,{credentials:'same-origin'});
if(!response.ok)throw Error(`主题版本返回 ${response.status}`);
const versions=await response.json();
if(seq!==sequence)return;
versions.forEach(item=>version.add(new Option(`v${item.version}`,item.version)));
}catch(error){if(seq!==sequence)return;status.textContent=`版本列表暂时无法读取,将预览最新版本:${error.message}`;}
finally{if(seq===sequence){version.disabled=false;show();}}
}
theme.addEventListener('change',loadVersions);[page,version].forEach(control=>control.addEventListener('change',show));
retry.addEventListener('click',show);
document.querySelectorAll('.devices button').forEach(button=>button.addEventListener('click',()=>{document.querySelectorAll('.devices button').forEach(other=>other.classList.remove('active'));button.classList.add('active');frame.style.width=button.dataset.size;}));
const requestedTheme=new URLSearchParams(location.search).get('theme');
if(['automotive','industrial','corporate','minimal'].includes(requestedTheme))theme.value=requestedTheme;
loadVersions();
</script></body></html>
+1 -5
View File
@@ -21,11 +21,7 @@ LOADER=Environment(loader=FileSystemLoader(str(ROOT/'themes')),autoescape=select
_ENVS={}
def env_for(theme):
"""Per-theme Jinja env: theme dir first, shared themes/ root as fallback.
Lets a theme override shared macros (_professional_components.html) while
inheriting everything else. Cached per theme; degraded envs are not cached.
"""
"""Per-theme Jinja env: theme dir first, shared themes/ root as fallback."""
if theme not in THEMES:raise ValueError('未知主题')
env=_ENVS.get(theme)
if env is None:
+1 -1
View File
@@ -1,5 +1,5 @@
"""Single source of truth for the SiteForge platform release identity."""
import os
VERSION = "8.31.1-dev.1"
VERSION = "8.32.0-dev.1"
PLATFORM_NAME = "SiteForge Factory"
BUILD_SHA = os.environ.get("SITEFORGE_GIT_SHA", "unknown").strip()[:40] or "unknown"
+2 -2
View File
@@ -6,7 +6,7 @@ services:
dockerfile: Dockerfile.enterprise
args:
SITEFORGE_GIT_SHA: ${SITEFORGE_GIT_SHA:-unknown}
image: siteforge/web:8.31.0-dev.1
image: siteforge/web:8.32.0-dev.1
init: true
restart: unless-stopped
ports:
@@ -48,7 +48,7 @@ services:
retries: 3
start_period: 30s
worker:
image: siteforge/web:8.31.0-dev.1
image: siteforge/web:8.32.0-dev.1
command: ['python','-m','app.worker']
init: true
restart: unless-stopped
+204
View File
@@ -0,0 +1,204 @@
{
"version": "8.31.1-dev.1",
"generated_utc": "2026-10-26T02:28:20.128766+00:00",
"groups": [
{
"batch": 1,
"files": [
"test_commercial_v5.py",
"test_connectors_v84.py",
"test_connectors_v85.py"
],
"status": "passed",
"seconds": 5.72,
"summary": "................................... [100%]\n35 passed in 3.42s\n\n"
},
{
"batch": 2,
"files": [
"test_console_v819.py",
"test_core_v4.py",
"test_deep_nested_v824.py"
],
"status": "passed",
"seconds": 6.1,
"summary": "........................... [100%]\n27 passed in 3.88s\n\n"
},
{
"batch": 3,
"files": [
"test_editor_easy_v8212.py",
"test_editor_ux_v8211.py",
"test_enterprise.py"
],
"status": "passed",
"seconds": 4.48,
"summary": "........... [100%]\n11 passed in 2.12s\n\n"
},
{
"batch": 4,
"files": [
"test_enterprise_v7.py",
"test_factory.py",
"test_factory_v2.py"
],
"status": "passed",
"seconds": 8.45,
"summary": "................................... [100%]\n35 passed in 6.18s\n\n"
},
{
"batch": 5,
"files": [
"test_mobile_tabs_v8213.py",
"test_multilingual_v815.py",
"test_nested_v823.py"
],
"status": "passed",
"seconds": 5.45,
"summary": "........................ [100%]\n24 passed in 3.23s\n\n"
},
{
"batch": 6,
"files": [
"test_p0_71.py",
"test_p0_backup.py",
"test_p0_p4_completion_v88.py"
],
"status": "passed",
"seconds": 4.38,
"summary": "....................... [100%]\n23 passed in 2.12s\n\n"
},
{
"batch": 7,
"files": [
"test_p0_p4_completion_v89.py",
"test_p1_platform.py",
"test_production_v816.py"
],
"status": "passed",
"seconds": 6.61,
"summary": "..................... [100%]\n21 passed in 4.30s\n\n"
},
{
"batch": 8,
"files": [
"test_professional_ui_v811.py",
"test_regression_runner_v817.py",
"test_release_native_v814.py"
],
"status": "passed",
"seconds": 4.75,
"summary": "................... [100%]\n19 passed in 2.36s\n\n"
},
{
"batch": 9,
"files": [
"test_release_reviews_v7.py",
"test_release_v813.py",
"test_site_design_v87.py"
],
"status": "passed",
"seconds": 6.22,
"summary": "........................... [100%]\n27 passed in 3.92s\n\n"
},
{
"batch": 10,
"files": [
"test_siteforge_v817_audit.py",
"test_studio_v86.py",
"test_theme_pro_v3.py"
],
"status": "passed",
"seconds": 7.3,
"summary": "...................................... [100%]\n38 passed in 4.91s\n\n"
},
{
"batch": 11,
"files": [
"test_v818_pg_recovery.py",
"test_v818_sqlite_pg_transfer.py",
"test_v821_editor_history_and_mapping.py"
],
"status": "passed",
"seconds": 3.81,
"summary": "................. [100%]\n17 passed in 1.43s\n\n"
},
{
"batch": 12,
"files": [
"test_v822_canvas.py",
"test_v825_shared_components.py",
"test_v826_global_design.py"
],
"status": "passed",
"seconds": 5.22,
"summary": ".................. [100%]\n18 passed in 2.47s\n\n"
},
{
"batch": 13,
"files": [
"test_v827_guided_workspace.py",
"test_v828_launch_center.py",
"test_v829_theme_studio.py"
],
"status": "passed",
"seconds": 7.84,
"summary": "........... [100%]\n11 passed in 5.04s\n\n"
},
{
"batch": 14,
"files": [
"test_v830_real_pages.py",
"test_v8311_performance_icons.py",
"test_v831_automotive_ref.py"
],
"status": "passed",
"seconds": 7.86,
"summary": ".......... [100%]\n10 passed in 5.72s\n\n"
},
{
"batch": 15,
"files": [
"test_v832_preview_easy_editor.py",
"test_v8_art_installer.py",
"test_v8_workflows.py"
],
"status": "failed",
"seconds": 3.99,
"summary": "_gallery_and_studio_show_useful_fallback_and_easy_buttons - assert '选择图片素材' in '\\'use strict\\';\\nconst $=id=>document.querySelector(id);\\nconst html=s=>String(s??\\'\\').replace(/[&<>\"\\']/g,c=>({\\'&\\...ault();event.shiftKey?Studio.redo():Studio.undo()}\\n else if(key===\\'y\\'){event.preventDefault();Studio.redo()}\\n});\\n'\n1 failed, 16 passed in 1.67s\n\n"
},
{
"batch": 16,
"files": [
"test_wordpress_baseline.py",
"test_worker_queue_v6.py",
"test_workspace_v810.py"
],
"status": "passed",
"seconds": 7.24,
"summary": "................ [100%]\n16 passed in 4.77s\n\n"
},
{
"batch": 17,
"files": [
"test_workspace_v812.py",
"test_wp_complex_v820.py",
"test_wp_migration_v81.py"
],
"status": "passed",
"seconds": 4.78,
"summary": "............................... [100%]\n31 passed in 2.53s\n\n"
},
{
"batch": 18,
"files": [
"test_wp_migration_v82.py",
"test_wp_migration_v83.py"
],
"status": "passed",
"seconds": 5.82,
"summary": ".................... [100%]\n20 passed in 3.54s\n\n"
}
],
"all_passed": false
}
@@ -0,0 +1 @@
<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>SiteForge V8.32 验收报告</title><style>body{margin:0;background:#f6f7fa;color:#243347;font:15px/1.7 system-ui,-apple-system,"Microsoft YaHei",sans-serif}main{max-width:980px;margin:30px auto;padding:0 24px}h1{font-size:30px;letter-spacing:-.02em}h2{font-size:19px;margin-top:30px}.lead{font-size:16px;color:#627185}.panel{border:1px solid #e1e5ed;border-radius:14px;background:white;padding:24px;margin:18px 0}table{width:100%;border-collapse:collapse}td,th{padding:12px;border-bottom:1px solid #eef0f5;text-align:left}th{font-size:12px;color:#6b7a8b}.good{color:#157a55}.warn{color:#a16624}.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}.num{font-size:30px;font-weight:800}small{color:#68798a}a{color:#21628f}img{max-width:100%;border:1px solid #dfe6ee;border-radius:11px}@media(max-width:700px){.grid{grid-template-columns:1fr}main{padding:0 16px}}</style></head><body><main><h1>V8.32 · 主题预览修复与易用编辑器</h1><p class="lead">SiteForge Enterprise 8.32.0-dev.1 · 实际源码修改和集成验证</p><section class="grid"><div class="panel"><div class="num good">400</div><small>Python 测试,独立隔离批次 18 组</small></div><div class="panel"><div class="num good">39</div><small>JavaScript 历史测试</small></div><div class="panel"><div class="num good">0</div><small>隔离 Chromium 浏览器脚本错误</small></div></section><h2>故障根因</h2><div class="panel"><strong>全局 X-Frame-Options: DENY 阻断了内嵌主题页面。</strong><p>后端只对明确的只读主题预览接口允许 SAMEORIGIN,并同时使用 frame-ancestors 'self'。其他后台页面仍然 DENY。编辑器采用受限、非同源沙箱 srcdoc 渲染以保留原先的安全约束。</p></div><h2>编辑器改进</h2><table><tr><th>使用环节</th><th>修改内容</th></tr><tr><td>新手编辑</td><td>默认只显示文字与内容,复杂设置收起</td></tr><tr><td>图片</td><td>内容区域直接选择/上传图片</td></tr><tr><td>主题浏览</td><td>按主题/页面/设备切换,错误状态清晰</td></tr><tr><td>手机</td><td>390px 可用、无水平溢出,编辑面板能关闭</td></tr><tr><td>版本兼容</td><td>四套主题及已有数据/API/旧版本继续保留</td></tr></table><h2>隔离测试与限制</h2><div class="panel"><p class="good">HTTP + Worker 构建、ZIP 下载与预览链路通过。</p><p>首次集中回归完成 400 个测试但有两项禁止放宽 iframe 同源权限的安全断言失败;针对这两项改为 srcdoc 沙箱后已复测通过。第二次全集中运行在约 72% 处停滞,随后拆分运行 18 组;第 15 组因新增文案的测试预期不一致修正后重测 17 项通过。保留首次失败和停滞日志,不宣称单进程全量稳定。</p><p class="warn">Chromium 运行时不能直接访问本地 HTTP 后台,浏览器验证为真实 HTML/CSS/JS + 模拟 API 的隔离测试,非正式登录后的全链路浏览器验收;生产级 Art Design Pro、PostgreSQL/Redis 和远程部署仍待验证。</p></div><h2>实际编辑器浏览器截图</h2><img alt="V8.32 编辑器桌面端(模拟数据)" src="editor-desktop.png"><p><a href="../../README_V8_32.md">版本说明与使用流程</a> · <a href="V8_32_验收数据.json">机器可读验收数据</a></p></main></body></html>
+29
View File
@@ -0,0 +1,29 @@
{
"version": "8.32.0-dev.1",
"generated_utc": "2026-10-26T02:31:24.270437+00:00",
"python": {
"count": 400,
"batches": 18,
"batch_pass_after_retest": 18,
"initial_failed_batch": 15,
"initial_cause": "test wording assertion outdated after UI label changed",
"retested_batch_passed": 17,
"single_process_stalled": true
},
"javascript": {
"passed": 39
},
"http_worker": "passed",
"browser_isolated": {
"editor_desktop": true,
"editor_mobile_390": true,
"theme_preview_success_and_failure": true,
"gallery_error_retry": true,
"script_errors": 0
},
"production_verified": false,
"limitations": [
"real authenticated full Chromium against backend not verified: container policy blocks localhost",
"official Art Design Pro build and production PG/Redis not verified"
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 301 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

+1
View File
@@ -0,0 +1 @@
1
+1
View File
@@ -0,0 +1 @@
1
+81
View File
@@ -0,0 +1,81 @@
"""Exercise actual V8.32 editor HTML/CSS/JS with deterministic same-page mock data.
Not an authenticated back-end browser E2E (Chromium container blocks local network).
"""
from pathlib import Path
import json,re
from playwright.sync_api import sync_playwright
ROOT=Path(__file__).resolve().parents[1]
OUT=ROOT/'reports/v832'
OUT.mkdir(parents=True,exist_ok=True)
source=(ROOT/'app/templates/editor.html').read_text(encoding='utf-8')
source=re.sub(r'<script[^>]+src="[^"]+"[^>]*></script>','',source)
source=re.sub(r'<link[^>]+href="/ui/[^"]+"[^>]*>','',source)
js=(ROOT/'app/static/editor.js').read_text(encoding='utf-8').replace('const siteId=pathParts[1],initialPage=pathParts[2];',"const siteId='demo',initialPage='p1';")
schema={'blocks':{
'hero':{'name':'大图横幅','icon':'▧','description':'网站首页顶部横幅','fields':['title','subtitle','image'],'defaults':{'title':'HESU AUTO','subtitle':'连接中国汽车与全球市场','image':''}},
'richtext':{'name':'文字介绍','icon':'▤','description':'企业介绍文字','fields':['title','description'],'defaults':{'title':'关于我们','description':'车辆出口服务'}},
'collection':{'name':'车型列表','icon':'▦','description':'展示产品与车型','fields':['title'],'defaults':{'title':'精选车型'}},
'layout':{'name':'多列布局','icon':'◫','fields':['title'],'defaults':{'title':'两列布局','columns':[{'width':1,'blocks':[]},{'width':1,'blocks':[]}]}}
},'page_templates':{'standard':{'name':'标准页面'}}}
errors=[]
with sync_playwright() as p:
browser=p.chromium.launch(executable_path='/usr/bin/chromium',args=['--no-sandbox','--disable-dev-shm-usage'],headless=True)
page=browser.new_page(viewport={'width':1440,'height':900},device_scale_factor=1)
page.on('pageerror',lambda err:errors.append(str(err)))
page.set_content(source)
page.add_style_tag(content=(ROOT/'app/static/editor.css').read_text(encoding='utf-8'))
page.add_script_tag(content=(ROOT/'app/static/editor-helpers.js').read_text(encoding='utf-8'))
page.add_script_tag(content=(ROOT/'app/static/studio-experience-v829.js').read_text(encoding='utf-8'))
page.evaluate('''(schema)=>{
window.fetch=async function(url,options={}){
const path=String(url).split('?')[0];
if(path.startsWith('/api/factory/themes/')){
const ok=window.__previewOk!==false;
return {ok,status:ok?200:503,headers:{get:()=>ok?'text/html':'text/plain'},text:async()=>'<html><body><h1>主题预览</h1></body></html>'};
}
if(path.endsWith('/editor-preview'))return {ok:true,status:200,headers:{get:()=> 'text/html'},text:async()=>`<html><head><meta name=viewport content='width=device-width,initial-scale=1'><style>*{box-sizing:border-box}body{margin:0;font:15px/1.6 system-ui,sans-serif;color:#20354a;background:#fff}nav{display:flex;justify-content:space-between;align-items:center;gap:18px;padding:17px 5%;border-bottom:1px solid #e8eef3}nav b{color:#d56a2e;font-size:22px}nav span{font-size:12px;color:#64748b}.hero{background:linear-gradient(110deg,#eaf0f5,#f5f8fb);padding:62px 6%;border-bottom:1px solid #dde5ee}.eyebrow{color:#cd6b33;font-weight:800;letter-spacing:2px;font-size:11px}.hero h1{font-size:clamp(29px,4vw,54px);max-width:670px;line-height:1.18;margin:14px 0}.hero p{font-size:15px;color:#627588;max-width:560px}.hero button{border:0;background:#d87939;color:white;border-radius:8px;padding:12px 20px;margin-top:14px;font-weight:700}.cars{padding:26px 6%}.cars h2{font-size:25px}.cards{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}.car{border:1px solid #e1e8ef;border-radius:9px;overflow:hidden}.car .art{height:85px;background:linear-gradient(135deg,#d2dde6,#e8eef3)}.car b{font-size:12px;display:block;padding:9px 12px}.car span{display:block;font-size:10px;color:#6b7f8f;padding:0 12px 11px}@media(max-width:500px){nav{padding:13px 4%}nav span{font-size:10px}.hero{padding:39px 5%}.cards{grid-template-columns:repeat(2,minmax(0,1fr))}.car .art{height:66px}}</style></head><body><nav><b>HESU AUTO</b><span>车型展示 品牌 关于我们 联系我们</span></nav><section class=hero><div class=eyebrow>GLOBAL AUTO EXPORT</div><h1>中国汽车供应<br>连接全球市场</h1><p>从车型采购到国际物流与海外交付的一站式出口服务。</p><button>了解我们的车型</button></section><section class=cars><h2>精选车型</h2><div class=cards><div class=car><div class=art></div><b>ROX 01</b><span>Luxury SUV</span></div><div class=car><div class=art></div><b>DONGFENG Z9</b><span>Pickup Truck</span></div><div class=car><div class=art></div><b>BYD YUAN UP</b><span>Electric Vehicle</span></div></div></section></body></html>`};
const data={
'/api/auth/me':{csrf:'demo'},
'/api/sites/demo':{id:'demo',name:'汽车演示站',theme:'automotive'},
'/api/sites/demo/entries':[{id:'p1',title:'首页',route:'/'}],
'/api/editor/blocks':schema,
'/api/sites/demo/components':[],
'/api/sites/demo/pages/p1/studio-draft':{revision:0,blocks:[{id:'b1',type:'hero',title:'HESU AUTO',subtitle:'全球汽车出口',image:'',visible:true,style:{}},{id:'b2',type:'collection',title:'热门车型',visible:true,style:{}}],page_template:'standard',layout_mode:'append',entry:{title:'首页',route:'/'}},
}[path];
return {ok:!!data,status:data?200:404,headers:{get:()=> 'application/json'},json:async()=>data||{detail:'not available'},text:async()=>JSON.stringify(data)};
};
window.__previewOk=true;
}''',schema)
page.add_script_tag(content=js)
page.evaluate('Studio.init()')
page.wait_for_function("document.querySelector('#blockCount').textContent.includes('2')")
assert page.locator('#tabStyle').is_hidden(),'Beginner mode should hide style tab'
assert page.locator('#sfNextAction').inner_text()
page.screenshot(path=str(OUT/'editor-desktop.png'),full_page=True)
page.locator('#studioAdvancedButton').click()
assert page.locator('#tabStyle').is_visible()
page.locator('#tabStyle').click()
assert page.locator('#tabStyle').get_attribute('aria-selected')=='true'
page.locator('#studioAdvancedButton').click()
assert page.locator('#tabStyle').is_hidden()
assert page.locator('#tabContent').get_attribute('aria-selected')=='true'
assert page.locator('.sf-photo-action button').count()==1
page.locator('#studioThemePreviewButton').click()
page.wait_for_function("document.querySelector('#themePreviewStatus').textContent.includes('预览已显示')",timeout=5000)
page.evaluate('window.__previewOk=false')
page.locator('#themePreviewPage').select_option('product')
page.wait_for_function("document.querySelector('#themePreviewRetry').hidden===false",timeout=5000)
assert '503' in page.locator('#themePreviewStatus').inner_text()
page.locator('button[onclick="Studio.closeThemePreview()"]').click()
page.set_viewport_size({'width':390,'height':844})
page.wait_for_timeout(350) # allow the responsive drawers' CSS transition to finish
page.screenshot(path=str(OUT/'editor-mobile.png'),full_page=True)
overflow=page.evaluate('document.documentElement.scrollWidth>innerWidth+1')
assert not overflow, 'Mobile horizontal overflow'
page.locator('button[onclick="Studio.togglePane(\'right\')"]').click()
assert page.locator('body').evaluate("x=>x.classList.contains('show-inspector')")
page.locator('#sfCloseInspector').click()
assert not page.locator('body').evaluate("x=>x.classList.contains('show-inspector')")
assert not errors,errors
browser.close()
print(json.dumps({'browser':'isolated chromium','desktop':True,'mobile390':True,'preview_error_recovery':True,'advanced_mode':True,'js_errors':errors},ensure_ascii=False))
+32
View File
@@ -0,0 +1,32 @@
"""Chromium isolation: exercise actual gallery controls / AJAX fallback; no live server."""
from pathlib import Path
import re,json
from playwright.sync_api import sync_playwright
ROOT=Path(__file__).resolve().parents[1]
original=(ROOT/'app/templates/theme-gallery.html').read_text(encoding='utf-8')
script=re.search(r'<script>(.*?)</script>',original,re.S).group(1)
source=original.replace('<script>'+script+'</script>','')
errors=[]
with sync_playwright() as p:
browser=p.chromium.launch(executable_path='/usr/bin/chromium',headless=True,args=['--no-sandbox','--disable-dev-shm-usage'])
page=browser.new_page(viewport={'width':1440,'height':900})
page.on('pageerror',lambda e:errors.append(str(e)))
page.set_content(source)
page.evaluate('''()=>{window.__galleryOk=true;window.fetch=async(url)=>{
const isPreview=String(url).includes('/preview?'),success=!isPreview||window.__galleryOk;
const data=[{version:'1.4.1'},{version:'1.4.0'}];
return {ok:success,status:success?200:503,headers:{get:()=>isPreview?'text/html':'application/json'},json:async()=>data};
}}''')
page.add_script_tag(content=script)
page.wait_for_function("document.querySelector('#preview').getAttribute('src')?.includes('page_type=home')")
assert page.locator('#version option').count()==3
page.evaluate('window.__galleryOk=false')
page.locator('#page').select_option('product')
page.wait_for_function("document.querySelector('#retryPreview').hidden===false")
assert '503' in page.locator('#previewStatus').inner_text()
assert 'page_type=product' in page.locator('#openPreview').get_attribute('href')
assert not errors,errors
page.set_viewport_size({'width':390,'height':844})
assert not page.evaluate('document.documentElement.scrollWidth>innerWidth+1')
browser.close()
print(json.dumps({'gallery_controls':True,'versions':True,'retry_error':True,'mobile390':True,'javascript_errors':errors},ensure_ascii=False))
+1 -1
View File
@@ -6,7 +6,7 @@ import sys
ROOT=Path(__file__).resolve().parent.parent
sys.path.insert(0,str(ROOT))
from app.version import VERSION
assert VERSION=='8.31.0-dev.1'
assert VERSION=='8.32.0-dev.1'
api=(ROOT/'app'/'main.py').read_text(encoding='utf-8')
assert 'return builder.build(' not in api, 'A synchronous request-thread build has reappeared'
assert "@app.post('/api/sites/{site_id}/builds', status_code=202)" in api
+1 -1
View File
@@ -31,7 +31,7 @@ def test_four_complete_theme_families_have_distinct_markup(factory):
import re as _re
m=_re.search(r'class="((?:pro-home-hero|sf-tp-hero)[^"]*)"',html)
return m.group(1) if m else html[:40]
assert len(set(_hero_sig(s) for s in main))==5 # 5 themes, 5 distinct hero signatures (was 4 before trade-pro)
assert len(set(_hero_sig(s) for s in main))==5
def test_theme_build_derived_category_brand_search_and_seo(factory):
+46
View File
@@ -0,0 +1,46 @@
"""V8.32 regression: authenticated inline preview stays same-origin and other routes stay denied."""
from test_factory_v2 import factory,site
def test_theme_preview_is_frameable_only_on_same_origin(factory):
for theme in ('automotive','industrial','corporate','minimal'):
for page in ('home','listing','product','article','about','contact'):
result=factory.get(f'/api/factory/themes/{theme}/preview',params={'page_type':page})
assert result.status_code==200,(theme,page,result.text[:120])
assert result.headers['x-frame-options']=='SAMEORIGIN'
assert result.headers['content-security-policy']=="frame-ancestors 'self'"
assert result.headers['cache-control']=='no-store'
assert 'text/html' in result.headers['content-type']
assert factory.get('/theme-gallery').headers['x-frame-options']=='DENY'
assert factory.get('/operations').headers['x-frame-options']=='DENY'
assert factory.get('/api/factory/themes/automotive/preview.css').headers['x-frame-options']=='DENY'
def test_preview_requires_login_and_other_protections_remain(factory):
from tests.legacy import LegacyTestClient as Client
from app.main import app
with Client(app) as unauth:
result=unauth.get('/api/factory/themes/automotive/preview')
assert result.status_code==401
assert factory.get('/api/factory/themes/automotive/preview?page_type=invalid').status_code==400
def test_gallery_and_studio_show_useful_fallback_and_easy_buttons(factory):
gallery=factory.get('/theme-gallery').text
assert 'previewStatus' in gallery and 'retryPreview' in gallery and 'openPreview' in gallery
assert 'requestedTheme' in gallery and 'response.ok' in gallery
sid=site(factory)
pages=factory.get(f'/api/sites/{sid}/entries').json()
entry=pages[0]['id']
html=factory.get(f'/editor/{sid}/{entry}').text
assert 'studioAdvancedButton' in html
assert 'sfNextAction' in html and 'sfCloseInspector' in html
assert 'sandbox="allow-scripts"' in html and 'allow-same-origin' not in html
js=factory.get('/ui/editor.js').text
assert 'toggleAdvanced(){' in js
assert "response.ok" in js and 'themePreviewSeq' in js
assert '从图片素材库选择' in js and '上传新图片' in js
css=factory.get('/ui/editor.css').text
assert 'body:not(.sf-advanced-mode) #tabStyle' in css
workspace=factory.get('/ui/workspace.js').text
assert '预览页面效果' in workspace and '/theme-gallery?theme=' in workspace