launch huashu-design v1.0: 9 demos + marketing-first README

- 新增 9 个能力 demo(workflow + capability),每个 MP4 + GIF + 60fps
- README 重写:营销 hook 开头 · 功能优先结构 · 自然嵌入 GIF
- 修复 render-video.js Google Fonts networkidle 超时
- 修复 c1-ios-prototype 刘海/Home Indicator 背景色
- 补充 references/editable-pptx.md 和 scripts/export_deck_stage_pdf.mjs

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
alchainandClaude Opus 4.7 committed 2026-04-20 18:19:48 +08:00
1 parent f3692aac10
commit b127f8baa3
24 files changed
+8206 -281

No files matched your search

+11 -2
View File
@@ -162,6 +162,12 @@
const startTimeRef = useRef(performance.now());
const canvasRef = useRef(null);
// Recording mode: render-video.js injects window.__recording = true before goto.
// When set, force loop=false so the export ends on the final frame instead of
// wrapping back to t=0 and capturing the start of the next cycle.
// (Browsers viewing manually still loop because __recording is undefined there.)
const effectiveLoop = (typeof window !== 'undefined' && window.__recording) ? false : loop;
useEffect(() => {
function updateScale() {
const vw = window.innerWidth;
@@ -195,7 +201,10 @@
setTime(prev => {
const next = prev + delta;
if (next >= duration) {
return loop ? 0 : duration;
// effectiveLoop honors window.__recording (forced non-loop during export).
// Stop just shy of duration so the final-frame state stays rendered
// (avoids exiting all Sprites that end exactly at `duration`).
return effectiveLoop ? 0 : duration - 0.001;
}
return next;
});
@@ -218,7 +227,7 @@
cancelled = true;
cancelAnimationFrame(rafRef.current);
};
}, [playing, duration, loop]);
}, [playing, duration, effectiveLoop]);
const handleScrub = useCallback((e) => {
const rect = e.currentTarget.getBoundingClientRect();
+20 -5
View File
@@ -43,12 +43,27 @@
this._width = parseInt(this.getAttribute('width')) || 1920;
this._height = parseInt(this.getAttribute('height')) || 1080;
// Shadow DOM 先渲染(独立于子节点,不受 parser 时机影响)
this._render();
this._collectSlides();
this._setupEventListeners();
this._restoreSlide();
this._updateDisplay();
this._setupPrintStyles();
// 防御:若 script 放在 <head> 里(而非 </deck-stage> 之后),
// parser 此刻可能还没处理完子 <section>,querySelectorAll 会返回空。
// 延迟到下一个事件循环,确保子节点都已 parse 完毕。
const init = () => {
this._collectSlides();
this._setupEventListeners();
this._restoreSlide();
this._updateDisplay();
this._setupPrintStyles();
};
if (this.ownerDocument.readyState === 'loading') {
// 文档还在 parse,等 DOMContentLoaded 一次搞定所有 section
this.ownerDocument.addEventListener('DOMContentLoaded', init, { once: true });
} else {
// 文档已 parse 完(script 在 body 底部或 defer),下一帧收集即可
requestAnimationFrame(init);
}
}
_render() {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"_meta": {
"description": "个人素材索引模板 — 复制此文件并填入你的真实数据",
"how_to_use": "1. 复制此文件到你的 agent 私有 memory 目录(Claude Code: ~/.claude/memory/personal-asset-index.json;其他 agent 按其约定) 2. 填入你的真实信息 3. huashu-design skill 在需要个人素材/锚定当前品牌时会自动读取",
"how_to_use": "1. 复制此文件到 ~/.claude/memory/personal-asset-index.json 2. 填入你的真实信息 3. design-philosophy skill 会自动读取",
"note": "真实数据文件不要放在 skill 目录内,避免随 skill 分发泄露隐私"
},