Commit graph

4 commits

Author SHA1 Message Date
Fini d435fc53a7 Merge branch 'v0.8.0' of github.com:ZSeven-W/openpencil into v0.8.0 2026-05-10 21:37:11 +08:00
Kayshen-X 484c6032b8 feat(shell): step 4-6 chrome — TS-equivalent editor UI + interactions
Step 4 (visual lift):
- Theme tokens (shadcn-dark palette) in shell-core
- Lucide-style icons via stroke_svg_path (skia parse_path::from_svg)
- Vertical Toolbar / sectioned LayerPanel (Pages + Layers) /
  TopBar / floating StatusBar / floating AIChatPanel widgets
- Native + web backends: stroke_line / fill_round_rect /
  stroke_round_rect / stroke_svg_path primitives
- CJK fallback typeface: cached PingFang/Noto-CJK on native via
  match_family_style_character; embedded NotoSansCJK-Subset
  (8.7 KB) on web alongside Roboto

Step 5 (infinite canvas + AI chat input):
- Document.viewport (pan + zoom 10–800%) with cursor-centered
  zoom_at + Hand-tool drag pan + dotted background grid
- Trackpad PixelDelta → pan, LineDelta / pinch / Cmd+swipe →
  zoom (winit MouseScrollDelta + PinchGesture + Modifiers)
- Document.chat (input / messages / focused / collapsed /
  4-corner anchor) — WidgetHost wires apply_text /
  apply_backspace / apply_send + DOM keydown listener
- AI chat panel drag → 4-corner snap via ChatAnchor::nearest
- Collapsed mode: compact pill (MessageSquare + "New Chat" +
  ChevronUp), entire pill click expands

Step 6 (RightPanel + chrome polish):
- PropertyPanel rewrite: 设计/代码 tabs, 创建组件, 位置, 弹性布局,
  尺寸, 图层, 填充, 描边, 效果, 导出 — file split into
  property_panel.rs + property_panel_sections.rs (under 800 ea.)
- Node::aggregate_bounds for Group / unbounded containers so
  the panel reports child-union W/H instead of 0×0
- TopBar PanelLeft button toggles Document.ui.sidebar_open
- Click empty canvas clears selection (collapses RightPanel)
- Native font cache (Roboto + system CJK typeface) bypasses
  jian-skia textlayout: chrome paint 605 ms → sub-ms

Hit-test order = paint order reversed (chat → toolbar → layer
panel → canvas) so the topmost overlay always wins, plus
toolbar bounding-rect consumes gap clicks so they don't fall
through.

64 lib tests + 21 widgets_static green; native + web
cargo check clean. Web wasm rebuild gated on EMSDK
(tools/check-wasm-bundle.sh runs the bundle ceiling guard).
2026-05-10 17:07:59 +08:00
Fini 5a841f011f fix(ai): contrast detector skips effectively-transparent wrapper fills
Codex stop-hook review caught: the detectTextBgContrast ancestor walk
treated any wrapper with a solid `fill` entry as the bg color, even
when the fill was effectively invisible. The classic miss case:

  page { fill: cream }
    └─ wrapper { fill: [{ type: 'solid', color: '#FFFFFF', opacity: 0 }] }
        └─ text { fill: cream }

Without the guard, the detector picked the wrapper's white fill as bg
and reported a healthy contrast ratio against the cream text — masking
the real cream-on-cream failure that lives one level up.

firstSolidColor() now skips fills with `opacity === 0` and 8-hex colors
whose alpha byte is `00` (e.g. `#FFFFFF00`). Both produce no visible
color, so the ancestor walk continues past them to the real bg.

Semi-transparent fills (opacity 0.5, 8-hex alpha 80, etc.) are out of
scope — the detector still treats them as opaque rather than trying to
math the layered composite. Tests pin both: opacity=0.5 + alpha=80
stay treated as bg.

4 new test cases cover the fix plus the boundary (opacity=0.5, alpha=80
should NOT be skipped). Full corpus replay shows 14 hits unchanged on
the 470-row corpus — no false-positive regression introduced.
2026-05-10 14:55:00 +08:00
Fini 31d45b79be chore(ab-corpus): add shape + per-category issue inspectors
Two complement scripts that ride alongside replay-detectors.ts:

- inspect-shape.ts: bucket every applied row's root by (width-bucket /
  height-bucket / aspect-ratio / child-count). Used 2026-05-10 to
  diagnose why detectEdgeSectionPadding scored 0 hits on a 220-row
  mobile subset — turned out 49% of mobile rows produce roots with a
  string-typed width ("fill_container" / "fit_content") because the
  element-tools path emits component fragments, not pages. The
  detector predicate `typeof width === 'number'` correctly skips them
  → 0 hits is the EVAL HARNESS coverage gap, not a detector bug.

- inspect-issue-category.ts: print every issue in a chosen category
  with row id + node id + reason. Generic version of inspect-contrast-
  hits.ts. 2026-05-10 used it to validate
  excessive-frame-effects (4/4 TP — blur 48 cards + OTP slot spread)
  and the two borderline mixed-sibling hits (header padding outlier,
  spacer cornerRadius outlier — known role-aware limitation, 0.2%
  noise rate, accepted).

Together with replay-detectors.ts these three give a fast empirical
loop for tuning a detector against real corpus output without burning
fresh API tokens.
2026-05-10 14:50:00 +08:00