Huashu Design 3.0 · 在 agent 里打一句话,拿回能交付的设计:网页 / App 原型 / 可编辑 PPTX / 动画 / 信息图 · 六个候选挑三个方向 · 84 种带样张的风格库 · 越用越懂你的外观账本与设计体检 · HTML-native design skill for Claude Code, Codex, Cursor & any agent
This report summarizes scanner evidence for alchaincyf/huashu-design, a HTML project with 28,295 GitHub stars. SynthScan examined 58,790 lines of code across 231 source files, recording 270 pattern matches distributed across 12 syntactic categories. The legacy adjusted pattern signal is 16.0 (Elevated pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Payload schema: 1.0.0 · Engine: 2.0.0 · Scoring: heuristic-density-v1 · Scan date: 2026-10-31.
Can maintainers explain intent, assumptions, and trade-offs? 58 finding(s).
Are imports declared and package/API names verified? 91 finding(s).
Are wrappers, guards, and repeated structures necessary? 149 finding(s).
Which changes need focused human review? 270 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 270 distinct pattern matches across 12 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | scripts/capabilities.py | 85 | print('截图与体检需要:pip install playwright pillow && playwright install chromium') | CODE |
| HIGH⚡ | scripts/design_lint.py | 45 | if (!s || s === 'transparent') return [0, 0, 0, 0]; | CODE |
| HIGH⚡ | scripts/design_lint.py | 52 | const raw = inner.includes('/') ? parts[parts.length - 1] : (/^(rgba|hsla)\(/.test(s) && parts.length === 4 ? parts[ | CODE |
| HIGH⚡ | scripts/design_lint.py | 59 | if (/^(IMG|VIDEO|CANVAS|PICTURE|IFRAME)$/.test(e.tagName) || e instanceof SVGElement) return null; | CODE |
| HIGH⚡ | scripts/design_lint.py | 61 | if (cs.backgroundImage && cs.backgroundImage !== 'none') return null; | CODE |
| HIGH⚡ | scripts/design_lint.py | 61 | if (cs.backgroundImage && cs.backgroundImage !== 'none') return null; | CODE |
| HIGH⚡ | scripts/design_lint.py | 62 | const c = rgba(cs.backgroundColor); return c[3] > 0.9 ? c : undefined; | CODE |
| HIGH⚡ | scripts/design_lint.py | 65 | const own = opaqueBg(el); if (own !== undefined) return own; // 按钮、标签自带的底色先算 | CODE |
| HIGH⚡ | scripts/design_lint.py | 67 | if (x >= 0 && y >= 0 && x < innerWidth && y < innerHeight) { | CODE |
| HIGH⚡ | scripts/design_lint.py | 69 | if (e === el || el.contains(e)) continue; | CODE |
| HIGH⚡ | scripts/design_lint.py | 70 | const b = opaqueBg(e); if (b !== undefined) return b; | CODE |
| HIGH⚡ | scripts/design_lint.py | 74 | for (let e = el.parentElement; e && e.nodeType === 1; e = e.parentElement) { const b = opaqueBg(e); if (b !== undefi | CODE |
| HIGH⚡ | scripts/design_lint.py | 74 | for (let e = el.parentElement; e && e.nodeType === 1; e = e.parentElement) { const b = opaqueBg(e); if (b !== undefi | CODE |
| HIGH⚡ | scripts/design_lint.py | 77 | const opacityOf = (el) => { let o = 1; for (let e = el; e && e.nodeType === 1; e = e.parentElement) o *= parseFloat(ge | CODE |
| HIGH⚡ | scripts/design_lint.py | 82 | return r.width > 0 && r.height > 0 && cs.visibility !== 'hidden' && cs.display !== 'none' && parseFloat(cs.opacity) | CODE |
| HIGH⚡ | scripts/design_lint.py | 88 | broken: [], fonts: {}, display: null, body: null, headlineSerif: false, textColors: {}, bg: null, | CODE |
| HIGH⚡ | scripts/design_lint.py | 93 | let displayEl = null, maxFs = 0; // 标题字体 = 页面上字号最大的那段文字用的字体 | CODE |
| HIGH⚡ | scripts/design_lint.py | 105 | if (fs < (window.__MIN_LABEL || 12)) out.small.push({ text: t.slice(0, 30), px: Math.round(fs) }); | CODE |
| HIGH⚡ | scripts/design_lint.py | 105 | if (fs < (window.__MIN_LABEL || 12)) out.small.push({ text: t.slice(0, 30), px: Math.round(fs) }); | CODE |
| HIGH⚡ | scripts/design_lint.py | 107 | if (t.length >= 28 && !el.closest(CAPTION) && fs < (window.__MIN_BODY || 14)) out.bodySmall.push({ text: t.slice(0 | CODE |
| HIGH⚡ | scripts/design_lint.py | 107 | if (t.length >= 28 && !el.closest(CAPTION) && fs < (window.__MIN_BODY || 14)) out.bodySmall.push({ text: t.slice(0 | CODE |
| HIGH⚡ | scripts/design_lint.py | 111 | const c = contrast(fg, bg); const large = fs >= 24 || (fs >= 18.66 && parseInt(cs.fontWeight) >= 700); | CODE |
| HIGH⚡ | scripts/design_lint.py | 112 | if (c < (large ? 3 : 4.5)) out.lowContrast.push({ text: t.slice(0, 30), ratio: Math.round(c * 100) / 100, px: fs | CODE |
| HIGH⚡ | scripts/design_lint.py | 114 | const caps = cs.textTransform === 'uppercase' || (latin.length >= 3 && latin === latin.toUpperCase()); | CODE |
| HIGH⚡ | scripts/design_lint.py | 116 | if (caps && latin.length >= 2 && ls >= 0.06 && fs <= 15) out.eyebrows.push(t.slice(0, 30)); | CODE |
| HIGH⚡ | scripts/design_lint.py | 116 | if (caps && latin.length >= 2 && ls >= 0.06 && fs <= 15) out.eyebrows.push(t.slice(0, 30)); | CODE |
| HIGH⚡ | scripts/design_lint.py | 120 | if (/^0\d([\s./—-]|$)/.test(t) && t.length <= 6) out.numbered.push(t); | CODE |
| HIGH⚡ | scripts/design_lint.py | 128 | if (r.width > 200 && ['Top', 'Bottom', 'Left', 'Right'].some((s) => cs['border' + s + 'Style'] !== 'none' && parseFl | CODE |
| HIGH⚡ | scripts/design_lint.py | 129 | if (r.width > 120 && r.height > 60 && (cs.backgroundColor !== 'rgba(0, 0, 0, 0)' || cs.borderStyle !== 'none')) { | CODE |
| HIGH⚡ | scripts/design_lint.py | 141 | if (n > 0 && n <= total * 0.4 && (cs.color !== hs.color || cs.fontStyle !== hs.fontStyle || cs.fontFamily !== hs.f | CODE |
| HIGH⚡ | scripts/design_lint.py | 141 | if (n > 0 && n <= total * 0.4 && (cs.color !== hs.color || cs.fontStyle !== hs.fontStyle || cs.fontFamily !== hs.f | CODE |
| HIGH⚡ | scripts/design_lint.py | 141 | if (n > 0 && n <= total * 0.4 && (cs.color !== hs.color || cs.fontStyle !== hs.fontStyle || cs.fontFamily !== hs.f | CODE |
| HIGH⚡ | scripts/design_lint.py | 153 | if (img.complete && img.naturalWidth === 0 && visible(img)) out.broken.push(src.slice(0, 80) || '(无 src)'); | CODE |
| HIGH⚡ | scripts/design_lint.py | 153 | if (img.complete && img.naturalWidth === 0 && visible(img)) out.broken.push(src.slice(0, 80) || '(无 src)'); | CODE |
| HIGH⚡ | scripts/design_lint.py | 154 | else if (src && !/^(data:|https?:|blob:)/.test(src)) out.relImgs.push(src.slice(0, 60)); | CODE |
| HIGH | scripts/design_lint.py | 250 | print('体检跳过:没装 Python 版 playwright。装上后再跑:pip install playwright pillow && playwright install chromium', | CODE |
| HIGH | scripts/design_lint.py | 20 | 依赖:pip install playwright pillow && playwright install chromium | STRING |
| HIGH⚡ | scripts/build_specimens.py | 78 | print('需要 Playwright:pip install playwright && python3 -m playwright install chromium') | CODE |
| HIGH⚡ | scripts/xhs_render.py | 47 | const rects = [...range.getClientRects()].filter(r => r.width > 1 && r.height > 1); | CODE |
| HIGH⚡ | scripts/xhs_render.py | 50 | let k = 1, p = el; while (p && p !== sec) { const tf = getComputedStyle(p).transform; if (tf && tf !== 'none') { c | CODE |
| HIGH⚡ | scripts/xhs_render.py | 53 | items.push({ el, t: t.slice(0, 24), len: t.length, lines, px: Math.round(px * 10) / 10, ok: !!el.closest('[data-ov | CODE |
| HIGH⚡ | scripts/xhs_render.py | 58 | if (it.px < minLabel) issues.push(['✗', `字号 ${it.px}px < 标签底线 ${minLabel}px`, it.t]); | CODE |
| HIGH⚡ | scripts/xhs_render.py | 59 | else if (it.lines >= 2 && it.len >= 16 && it.px < minBody) issues.push(['✗', `段落字号 ${it.px}px < 正文底线 ${minBody}px` | CODE |
| HIGH⚡ | scripts/xhs_render.py | 59 | else if (it.lines >= 2 && it.len >= 16 && it.px < minBody) issues.push(['✗', `段落字号 ${it.px}px < 正文底线 ${minBody}px` | CODE |
| HIGH⚡ | scripts/xhs_render.py | 61 | if (l < -2 || t < -2 || r > sr.width + 2 || b > sr.height + 2) { issues.push(['✗', '字出了页面', it.t]); break; } | CODE |
| HIGH⚡ | scripts/xhs_render.py | 61 | if (l < -2 || t < -2 || r > sr.width + 2 || b > sr.height + 2) { issues.push(['✗', '字出了页面', it.t]); break; } | CODE |
| HIGH⚡ | scripts/xhs_render.py | 64 | const seam = [...Array(n - 1)].map((_, j) => (j + 1) * 1242).find(x => l < x - 4 && r > x + 4); | CODE |
| HIGH⚡ | scripts/xhs_render.py | 65 | if (seam) { issues.push(['✗', `字跨第 ${seam / 1242} 道缝`, it.t]); break; } | CODE |
| HIGH⚡ | scripts/xhs_render.py | 73 | if (a.ok || b.ok || a.el.contains(b.el) || b.el.contains(a.el)) continue; // 同一段里的行内片段不算 | CODE |
| HIGH⚡ | scripts/xhs_render.py | 74 | if (a.rects.some(x => b.rects.some(y => hit(x, y)))) issues.push(['✗', '字压字', `${a.t} / ${b.t}`]); | CODE |
| HIGH⚡ | scripts/xhs_render.py | 79 | let found = null; | CODE |
| HIGH⚡ | scripts/xhs_render.py | 87 | if (!hitEl || hitEl === it.el || it.el.contains(hitEl) || hitEl.contains(it.el) || hitEl.closest('[data-overla | CODE |
| HIGH⚡ | scripts/xhs_render.py | 90 | if (px < hb.left || px > hb.right || py < hb.top || py > hb.bottom) continue; | CODE |
| HIGH⚡ | scripts/xhs_render.py | 95 | if (found) issues.push(['!', found.tagName === 'IMG' ? '字压在图片框里(透明边也算,看一眼)' : '字被别的元素盖住', it.t]); | CODE |
| HIGH⚡ | scripts/xhs_render.py | 100 | if (e.closest('[data-overlap-ok]') || e.textContent.trim() || e.children.length || e.closest('svg')) return false; | CODE |
| HIGH⚡ | scripts/xhs_render.py | 111 | return q.width > 0 && q.height > 0 && it.rects.some(x => hit(x, g)); | CODE |
| HIGH⚡ | scripts/xhs_render.py | 113 | if (hitShape) issues.push(['!', '字和图形挤在一起', it.t]); | CODE |
| HIGH⚡ | scripts/xhs_render.py | 115 | out.push({ index: si, n, w: Math.round(sr.width), h: Math.round(sr.height), issues }); | CODE |
| HIGH⚡ | scripts/xhs_render.py | 134 | sys.exit('需要 Playwright 和 Pillow:pip install playwright pillow && python3 -m playwright install chromium') | CODE |
| HIGH⚡ | scripts/pptx_from_rendered.py | 73 | out.push({ | CODE |
| 20 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM⚡ | references/gsap-recipes.md | 113 | // ── 五段 label,比例 15 / 15 / 40 / 20 / 10 ────────────────── | COMMENT |
| MEDIUM⚡ | references/gsap-recipes.md | 120 | // ── S1 触发(节奏慢:单个动作 + 大量留白)───────────────────── | COMMENT |
| MEDIUM⚡ | references/gsap-recipes.md | 124 | // ── S2 生成(一个明确的惊艳点,不堆动作)───────────────────── | COMMENT |
| MEDIUM⚡ | references/gsap-recipes.md | 132 | // ── S4 爆发(镜头级动作:拉远 / rotationX / 多元素涌现)─────── | COMMENT |
| MEDIUM⚡ | references/gsap-recipes.md | 136 | // ── S5 落幅(Logo 形变收束,见 §3.6;之后什么都不发生)──────── | COMMENT |
| MEDIUM | references/gsap-recipes.md | 691 | // ── 平面模式(纯 zoom + pan,无旋转)────────────────────────── | COMMENT |
| MEDIUM | references/animation-pitfalls.md | 222 | // ━━━━━━ state ━━━━━━ | COMMENT |
| MEDIUM | references/animation-pitfalls.md | 228 | // ━━━━━━ tick ━━━━━━ | COMMENT |
| MEDIUM | references/animation-pitfalls.md | 252 | // ━━━━━━ boot ━━━━━━ | COMMENT |
| MEDIUM | references/animation-pitfalls.md | 260 | // ━━━━━━ seek 接口(供 render-video 防御性矫正用)━━━━━━ | COMMENT |
| MEDIUM | scripts/render-video.js | 100 | // ── Phase 1: WARMUP (no recording, caches fonts/assets) ───────────── | COMMENT |
| MEDIUM | scripts/render-video.js | 113 | // ── Phase 2: RECORD (fresh context, animation from t=0) ───────────── | COMMENT |
| MEDIUM | scripts/mix-voiceover.sh | 94 | # ── ffmpeg filter graph ───────────────────────────────────── | COMMENT |
| MEDIUM | scripts/render-narration.sh | 111 | # ── Step 1: 录无声 MP4 ────────────────────── | COMMENT |
| MEDIUM | scripts/render-narration.sh | 133 | # ── Step 2: 混入人声 ────────────────────── | COMMENT |
| MEDIUM | scripts/pptx_from_rendered.py | 39 | # ───────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/pptx_from_rendered.py | 41 | # ───────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/pptx_from_rendered.py | 292 | # ── 文本 ────────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/pptx_from_rendered.py | 363 | # ── 形状 ────────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/pptx_from_rendered.py | 401 | # ── 图片 ────────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/pptx_from_rendered.py | 440 | # ── 装配 ────────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/pptx_from_rendered.py | 493 | # ───────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/pptx_from_rendered.py | 495 | # ───────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/pptx_from_rendered.py | 534 | # ───────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/pptx_from_rendered.py | 536 | # ───────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/pptx_from_rendered.py | 573 | # ───────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/pptx_from_rendered.py | 264 | # ───────────────────────────────────────────────────────────── | STRING |
| MEDIUM | scripts/pptx_from_rendered.py | 266 | # ───────────────────────────────────────────────────────────── | STRING |
| MEDIUM | scripts/add-music.sh | 38 | # ── Parse args ─────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/add-music.sh | 65 | # ── Resolve music source: --music wins, else --mood ───────────────── | COMMENT |
| MEDIUM | scripts/add-music.sh | 80 | # ── Resolve output path ───────────────────────────────────────────── | COMMENT |
| MEDIUM | scripts/add-music.sh | 85 | # ── Measure video duration, compute fade-out start ────────────────── | COMMENT |
| MEDIUM | demos/w1-brand-protocol.html | 546 | // ── Auto-scale stage to viewport ───────────────── | COMMENT |
| MEDIUM | demos/w1-brand-protocol.html | 557 | // ── Easing functions ───────────────── | COMMENT |
| MEDIUM | demos/w1-brand-protocol.html | 575 | // ── Timeline (total 12s) ───────────────── | COMMENT |
| MEDIUM | demos/c1-ios-prototype-en.html | 814 | // ── Scale to viewport (1920×1080 canvas) ───────────────────────── | COMMENT |
| MEDIUM | demos/c1-ios-prototype-en.html | 823 | // ── Easing ─────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | demos/c1-ios-prototype-en.html | 836 | // ── Elements ───────────────────────────────────────────────────── | COMMENT |
| MEDIUM | demos/c1-ios-prototype-en.html | 899 | // ── SFX via WebAudio ───────────────────────────────────────────── | COMMENT |
| MEDIUM | demos/c1-ios-prototype-en.html | 947 | // ── Timeline ───────────────────────────────────────────────────── | COMMENT |
| MEDIUM | demos/c1-ios-prototype-en.html | 975 | // ── Beat 1: 0-2s ───────────────────────────────────────── | COMMENT |
| MEDIUM | demos/c1-ios-prototype-en.html | 1016 | // ── Beat 2: 2-8s ───────────────────────────────────────── | COMMENT |
| MEDIUM | demos/c1-ios-prototype-en.html | 1050 | // ── Beat 3: 8-10s ──────────────────────────────────────── | COMMENT |
| MEDIUM | demos/c5-infographic-en.html | 631 | // ── Beat 1: 0-2s · JSON data appears, types in ───────── | COMMENT |
| MEDIUM | demos/c5-infographic-en.html | 648 | // ── Pipe arrow (1.8 → 2.2) ───────────────────────────── | COMMENT |
| MEDIUM | demos/c5-infographic-en.html | 654 | // ── Beat 2a: 2.0-3.2s · Infographic canvas arrives ───── | COMMENT |
| MEDIUM | demos/c5-infographic-en.html | 667 | // ── Beat 2b: 3.0-4.2s · Display headline appears ────── | COMMENT |
| MEDIUM | demos/c5-infographic-en.html | 688 | // ── Beat 2d: 5.2-6.4s · Comparison bars grow ───────── | COMMENT |
| MEDIUM | demos/c5-infographic-en.html | 732 | // ── Beat 3: 8.2-10s · Brand reveal ─────────────────── | COMMENT |
| MEDIUM | demos/c5-infographic.html | 628 | // ── Beat 1: 0-2s · JSON data appears, types in ───────── | COMMENT |
| MEDIUM | demos/c5-infographic.html | 645 | // ── Pipe arrow (1.8 → 2.2) ───────────────────────────── | COMMENT |
| MEDIUM | demos/c5-infographic.html | 651 | // ── Beat 2a: 2.0-3.2s · Infographic canvas arrives ───── | COMMENT |
| MEDIUM | demos/c5-infographic.html | 664 | // ── Beat 2b: 3.0-4.2s · Display headline appears ────── | COMMENT |
| MEDIUM | demos/c5-infographic.html | 685 | // ── Beat 2d: 5.2-6.4s · Comparison bars grow ───────── | COMMENT |
| MEDIUM | demos/c5-infographic.html | 729 | // ── Beat 3: 8.2-10s · Brand reveal ─────────────────── | COMMENT |
| MEDIUM | demos/c1-ios-prototype.html | 811 | // ── Scale to viewport (1920×1080 canvas) ───────────────────────── | COMMENT |
| MEDIUM | demos/c1-ios-prototype.html | 820 | // ── Easing ─────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | demos/c1-ios-prototype.html | 833 | // ── Elements ───────────────────────────────────────────────────── | COMMENT |
| MEDIUM | demos/c1-ios-prototype.html | 896 | // ── SFX via WebAudio ───────────────────────────────────────────── | COMMENT |
| MEDIUM | demos/c1-ios-prototype.html | 944 | // ── Timeline ───────────────────────────────────────────────────── | COMMENT |
| 36 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | scripts/tests/test_compare_failures.py | 28 | def test_all_unreadable_directions_are_not_a_success(self): | CODE |
| LOW⚡ | scripts/tests/test_compare_failures.py | 39 | def test_one_readable_direction_cannot_mask_a_failure(self): | CODE |
| LOW⚡ | scripts/tests/test_compare_failures.py | 49 | def test_two_distinct_directions_cannot_mask_a_failed_third(self): | CODE |
| LOW⚡ | scripts/tests/test_compare_failures.py | 59 | def test_complete_distinct_comparison_still_passes(self): | CODE |
| LOW⚡ | scripts/tests/test_compare_failures.py | 67 | def test_complete_colliding_comparison_still_fails(self): | CODE |
| LOW⚡ | scripts/tests/test_pptx_from_rendered.py | 36 | def test_bare_text_next_to_svg_kept(self): | CODE |
| LOW⚡ | scripts/tests/test_pptx_from_rendered.py | 40 | def test_bare_text_after_block_child_kept(self): | CODE |
| LOW⚡ | scripts/tests/test_pptx_from_rendered.py | 43 | def test_text_transform_applied(self): | CODE |
| LOW⚡ | scripts/tests/test_pptx_from_rendered.py | 46 | def test_fractional_opacity_reaches_shape_and_text(self): | CODE |
| LOW⚡ | scripts/tests/test_pptx_from_rendered.py | 53 | def test_default_run_reports_no_loss(self): | CODE |
| LOW⚡ | scripts/tests/test_pptx_from_rendered.py | 61 | def test_verify_reports_text_on_missing_slides(self): | CODE |
| LOW | scripts/tests/test_pptx_from_rendered.py | 74 | def test_verify_reports_text_when_no_slides_remain(self): | CODE |
| LOW | scripts/tests/test_pptx_from_rendered.py | 81 | def test_verify_reports_text_missing_from_pptx(self): | CODE |
| LOW⚡ | scripts/tests/test_design_lint.py | 39 | def test_clean_page_is_not_accused(self): | CODE |
| LOW⚡ | scripts/tests/test_design_lint.py | 44 | def test_broken_page_goes_red(self): | CODE |
| LOW⚡ | scripts/tests/test_design_lint.py | 51 | def test_reskin_is_caught_by_layout_not_colour(self): | CODE |
| LOW⚡ | scripts/tests/test_design_lint.py | 60 | def test_warm_cream_is_classified_warm(self): | CODE |
| LOW⚡ | scripts/tests/test_design_lint.py | 70 | def test_no_false_red_on_svg_sibling_plate_and_caption(self): | CODE |
| LOW⚡ | scripts/tests/test_design_lint.py | 75 | def test_deck_div_body_and_ghost_text_go_red(self): | CODE |
| LOW⚡ | scripts/tests/test_design_lint.py | 83 | def test_unreachable_stylesheet_does_not_kill_the_batch(self): | CODE |
| LOW⚡ | scripts/tests/test_design_lint.py | 89 | def test_glob_is_expanded_by_the_script(self): | CODE |
| LOW⚡ | scripts/tests/test_design_lint.py | 92 | def test_brand_mode_only_compares_skeleton(self): | CODE |
| LOW | scripts/tests/test_design_lint.py | 109 | def test_add_then_check_flags_the_same_look(self): | CODE |
| LOW | scripts/tests/test_design_lint.py | 129 | def test_refuses_entries_nobody_can_check(self): | CODE |
| LOW⚡ | scripts/tests/test_design_lint.py | 148 | def test_hand_edited_heading_without_trailing_dot_keeps_its_kind(self): | CODE |
| LOW⚡ | scripts/tests/test_design_lint.py | 153 | def test_topic_named_like_a_subcommand_can_be_recalled(self): | CODE |
| LOW⚡ | scripts/tests/test_design_lint.py | 159 | def test_blank_screenshot_counts_as_not_seen(self): | CODE |
| LOW | scripts/tests/test_design_lint.py | 173 | def test_local_image_goes_on_the_board(self): | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | scripts/capabilities.py | 52 | except Exception: | CODE |
| HIGH | scripts/capabilities.py | 65 | except Exception: | CODE |
| HIGH | scripts/design_lint.py | 511 | except Exception: | CODE |
| HIGH | scripts/design_lint.py | 270 | except Exception: | CODE |
| HIGH | scripts/build_specimens.py | 70 | except (AttributeError, ValueError): | CODE |
| HIGH | scripts/learn.py | 176 | except Exception: | CODE |
| HIGH | scripts/ref_board.py | 41 | except Exception: | CODE |
| HIGH | scripts/ref_board.py | 87 | except (AttributeError, ValueError): | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | scripts/fetch_images.py | 45 | except Exception as e: | CODE |
| LOW | scripts/fetch_images.py | 68 | except Exception as e: | CODE |
| LOW | scripts/capabilities.py | 52 | except Exception: | CODE |
| LOW | scripts/capabilities.py | 65 | except Exception: | CODE |
| LOW⚡ | scripts/design_lint.py | 266 | except Exception: | CODE |
| LOW⚡ | scripts/design_lint.py | 270 | except Exception: | CODE |
| LOW⚡ | scripts/design_lint.py | 277 | except Exception as e: | CODE |
| LOW | scripts/design_lint.py | 511 | except Exception: | CODE |
| LOW⚡ | scripts/build_specimens.py | 84 | except Exception as e: | CODE |
| LOW⚡ | scripts/build_specimens.py | 92 | except Exception as e: | CODE |
| LOW⚡ | scripts/build_specimens.py | 101 | except Exception as e: # 单张失败不拖垮整批 | CODE |
| LOW⚡ | scripts/xhs_render.py | 141 | except Exception as e: | CODE |
| LOW⚡ | scripts/xhs_render.py | 148 | except Exception: # 多半是字体外链在慢网或断网下没回来;按已加载的样子继续 | CODE |
| LOW | scripts/learn.py | 176 | except Exception: | CODE |
| LOW | scripts/ref_board.py | 41 | except Exception: | CODE |
| LOW | scripts/ref_board.py | 106 | except Exception as e: | CODE |
| LOW | scripts/ref_board.py | 125 | except Exception as e: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | scripts/mix-voiceover.sh | 1 | #!/usr/bin/env bash | COMMENT |
| LOW | scripts/convert-formats.sh | 1 | #!/bin/bash | COMMENT |
| LOW | scripts/verify-video.sh | 1 | #!/bin/bash | COMMENT |
| LOW | scripts/render-narration.sh | 1 | #!/usr/bin/env bash | COMMENT |
| LOW | scripts/render-narration.sh | 21 | # --keep-silent 保留中间产物(无声 MP4),便于 debug | COMMENT |
| LOW | scripts/sfx-cues.sh | 1 | #!/bin/bash | COMMENT |
| LOW | scripts/add-music.sh | 1 | #!/usr/bin/env bash | COMMENT |
| LOW | scripts/add-music.sh | 21 | # | COMMENT |
| LOW | scripts/design-gate-hook.sh | 1 | #!/bin/bash | COMMENT |
| LOW | demos/c6-expert-review-en.html | 721 | pointsG.appendChild(circle); | COMMENT |
| LOW | demos/w1-brand-protocol-en.html | 561 | } | COMMENT |
| LOW | demos/w2-junior-designer-en.html | 741 | // ──────────────────────────────────── | COMMENT |
| LOW | demos/w2-junior-designer.html | 741 | function seg(time, start, end) { | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | scripts/mix-voiceover.sh | 4 | # Usage: | COMMENT |
| LOW | scripts/convert-formats.sh | 4 | # Usage: | COMMENT |
| LOW | scripts/verify-video.sh | 7 | # Usage: | COMMENT |
| LOW | scripts/render-narration.sh | 9 | # Usage: | COMMENT |
| LOW | scripts/add-music.sh | 4 | # Usage: | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | scripts/capabilities.py | 31 | CODE | |
| LOW | scripts/design_lint.py | 246 | CODE | |
| LOW | scripts/build_specimens.py | 66 | CODE | |
| LOW | scripts/xhs_render.py | 121 | CODE | |
| LOW | scripts/pptx_from_rendered.py | 441 | CODE | |
| LOW | scripts/learn.py | 172 | CODE | |
| LOW | scripts/ref_board.py | 83 | CODE | |
| LOW | scripts/verify.py | 29 | CODE | |
| LOW | scripts/cloud/ai-review-video.py | 151 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | SKILL.md | 37 | | **文案** | 每一句话都为设计服务 | 用 Lorem ipsum 或「标题文字」占位交付 | | CODE |
| LOW | references/launch-film-director-notes.md | 144 | 「这部片不做的事」清单(紫渐变 / emoji / Lorem ipsum / Inter display / SVG 画人物 / 圆角卡 + 左 border accent)比「这部片做的事」清单更能保护质量。 | CODE |
| LOW | scripts/html2pptx.js | 764 | if (classAttr.indexOf('placeholder') !== -1) { | CODE |
| LOW | assets/director-notes-samples/launch-film-30s-sample.md | 142 | | 不用 Lorem ipsum | 每一段假文都用真正能读的内容(含「md is the source. Anything else is product.」这种 hook) | | CODE |
| LOW | assets/director-notes-samples/launch-film-30s-sample.md | 804 | - ✅ destination card 的「On Markdown」essay 内容是真的可读的英文哲学小段,不是 Lorem ipsum | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | references/voiceover-pipeline.md | 377 | DOUBAO_TTS_API_KEY=<your_api_key> | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | scripts/pptx_from_rendered.py | 35 | CODE | |
| LOW | scripts/verify.py | 19 | CODE | |
| LOW | scripts/verify.py | 20 | CODE | |
| LOW | scripts/tests/test_pptx_from_rendered.py | 6 | CODE | |
| LOW | scripts/tests/test_xhs_render.py | 17 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | assets/director-notes-samples/launch-film-30s-sample.md | 1642 | # Step 1: 加 BGM | COMMENT |
| LOW | assets/director-notes-samples/launch-film-30s-sample.md | 1648 | # Step 2: 加 SFX cues (按 Part II.6 SFX 字典逐 cue 加) | COMMENT |
| LOW | assets/director-notes-samples/launch-film-30s-sample.md | 1659 | # Step 3: 验证 audio stream | COMMENT |