Commit graph

31 commits

Author SHA1 Message Date
Fini 8a39bc7b03 Merge branch 'v0.8.0-new' of github.com:ZSeven-W/openpencil into v0.8.0-new 2026-05-24 00:39:09 +08:00
Kayshen-X 0948e97de9 feat(panels,canvas): editable gradients/effects + SVG/image import + locale-aware dialogs
Continues the gradient + property-panel polish from the previous
commit and rounds out two new flows the TS app already has:

Gradient stops + effects:
- ColorTarget gains GradientStop(i) + EffectColor(i); HSV picker
  preserves alpha across hue/SV drags so a transparent stop stays
  transparent. Hex pill stays 6-char; alpha is reattached at commit
  and the swatch sits on a 2x2 alpha checker so #00000000 reads as
  transparent rather than empty.
- Effects section reflowed into card-style blocks (image #9 spec):
  title + minus, X/Y and Blur/Spread 2-col grids, color row with
  swatch + rgba(...) text; clicking the swatch opens an HSV picker
  bound to that effect index via SetEffectColor.
- Press dispatch on both hosts anchors picker overlays at the
  clicked y so they pop adjacent to the swatch instead of the top.

Image + SVG import (toolbar + Fill section "图片" row):
- New FileAction::ImportImageOrSvg / PickFillImage; persistence_image
  pops rfd, decodes raster as data: URL, inserts an Image node or
  rewrites the selected node's primary fill.
- ImageNode actually renders on the canvas: NodePayload + SceneNode
  carry image_src, canvas_viewport_paint.rs decodes the data URL
  once and hands raw bytes to RenderBackend::draw_image with a
  src-hash cache id. Grey placeholder paints only when decode fails
  so transparent PNGs don't get a grey matte underneath.
- SVG import ported to TS-parity (packages/pen-engine svg-parser):
  recursive <g> tree walk with inherited fill/stroke/style="...",
  viewBox-aware scaling with maxDim cap, multi-subpath split, raw
  d preserved on PathNode. Imports land wrapped in a Group named
  after the source file.

Locale-aware first run:
- settings_io detects the OS locale (LC_ALL/LANG/LC_MESSAGES with
  zh-Hans/zh-Hant heuristics) and seeds editor_ui.locale before
  settings.json is read; persisted user choice still wins.
- macOS bundle declares CFBundleLocalizations + AllowMixedLocalizations
  so NSOpenPanel / NSSavePanel render in the same language as the
  rest of the chrome.

Web host kept exhaustive across the new variants (PickFillImage,
OpenEffectColorPicker, ColorTarget::EffectColor, GradientStop). Two
new files: persistence_image.rs (file-pick handlers, ≤120 lines) and
svg_path_data.rs (path-d tokenizer + bbox + normaliser, split from
svg_import.rs to stay under the 800-line cap). 277 op-editor-core
tests pass.
2026-05-23 23:11:38 +08:00
Fini edfa599006 feat(orchestrator): visual_ref — execute_visual_ref_orchestration 5-stage flow
Port executeVisualRefOrchestration from visual-ref-orchestrator.ts:45-166.
5-stage flow: design-system seed → HTML codegen → screenshot → enhanced
prompt → Orchestrator::run. Emits VisualRefStarted/DesignSystem/HtmlGenerated/
ScreenshotReady/Fallback progress variants. Falls back to plain Orchestrator::run
on empty HTML or None screenshot. Abort checked before each stage.
2026-05-23 20:31:05 +08:00
Fini bb6c980983 feat(orchestrator): visual_ref — generate_design_code + structure summary + enhanced prompt
S4 B2: port three HTML helper functions from the deleted TS visual-ref
pipeline. generate_design_code uses design-code + design-principles skills
as system prompt. extract_structure_summary uses a hand-rolled byte scanner
(no regex crate dep) to extract sections/headings/CTAs. build_enhanced_prompt
produces the byte-exact TS prompt template. 20 new tests; 569 total green.
2026-05-23 20:21:05 +08:00
Fini 5c14dc3597 feat(orchestrator): design_system — generator + variable seeding + prompt context
Task B1 of S4. Adds three functions to design_system.rs:
- generate_design_system(): async LLM call with design-system skill as system
  prompt; parses response via parse_design_system; falls back to DEFAULT on
  any failure (port of design-system-generator.ts:20-29).
- design_system_to_seed_commands(): emits SetVariableColor/SetVariableScalar
  for 8 palette tokens (color-*), 6 spacing steps (spacing-xs..2xl), 3 radius
  steps (radius-sm/md/lg), 2 font strings (font-heading/font-body), and 6 type
  scale steps (font-size-1..6) — 25 commands for DEFAULT (port of TS:134-156).
- design_system_to_prompt_context(): byte-exact port of the TS template prose
  "DESIGN SYSTEM (use these values consistently):\nColors: bg ... Style: ..."
  (port of TS:161-170).

11 new TDD tests; full suite 550 green (baseline was 539).
2026-05-23 20:13:53 +08:00
Fini 8444d4355c feat(orchestrator): types — VisualRefProvider trait + SkippedVisualRefProvider stub 2026-05-23 20:03:55 +08:00
Fini 1a601eca70 feat(orchestrator): design_system — types + parse + defaults + Progress + visual_ref_enabled
Port TS design-system-generator.ts (deleted in 0f12b6e9) into Rust as Task A1 of S4:

- Add design_system.rs: DesignSystem struct (palette BTreeMap + Typography + Spacing + radius Vec<f64> + aesthetic), default_design_system() OnceLock accessor, parse_design_system() 4-stage fallback chain (direct → code-fence strip → brace-extract → DEFAULT).
- Add 5 Progress::VisualRef* variants to types.rs: VisualRefStarted, VisualRefDesignSystem{var_count}, VisualRefHtmlGenerated{byte_len}, VisualRefScreenshotReady{skipped}, VisualRefFallback{reason}.
- Add DesignRequest.visual_ref_enabled: bool with #[serde(default="default_visual_ref_enabled")] = false.
- Update all 35 DesignRequest struct literals across the crate to include the new field.
- 17 new tests covering all 4 parse stages, DEFAULT exact values, serde round-trip, and variant compilation.
2026-05-23 19:57:30 +08:00
Fini 8711641c56 feat(orchestrator,host): production-visible Skipped*Provider + unblock chat_orchestrator compile
Move the three S3c validation stub providers (SkippedPreValidator,
SkippedScreenshotProvider, SkippedVisionLlmClient) from cfg-test-only
test_support to a new pub module 'stub_providers'. Test code keeps
working via a pub(crate) re-export; host code can now construct a
no-op ValidationProviders bundle without re-implementing the trait
impls per host.

chat_orchestrator.rs (predecessor of task #27 'wire intent gate into
chat runtime') compiles again after S3b-2/S3b-4/S3c added required
fields + a required Orchestrator::run arg:

- DesignRequest literal grew append_context: None, concurrency: 1,
  validation_enabled: false (conservative — validation gated off
  until host wires real screenshot / vision LLM).
- Orchestrator::run takes a 6th &ValidationProviders<'_> arg; we pass
  a bundle of the three Skipped*Provider stubs.

The whole module is marked #![allow(dead_code)] because no caller has
yet been added to chat_runtime.rs — that's #27 proper. Delete that
allow in the same PR as #27 真接线.
2026-05-23 18:53:22 +08:00
Fini 3ff8d92601 Merge branch 'v0.8.0-new' of github.com:ZSeven-W/openpencil into v0.8.0-new 2026-05-23 14:29:26 +08:00
Kayshen-X 2efca5aa3b feat(topbar): sun/moon theme toggle icon + traffic-light reposition fixes
Theme-toggle button now paints a Sun glyph in dark mode (click → light) and a Moon glyph in light mode (click → dark); the Sun icon was hardcoded before. Adds Icon::Moon (lucide crescent) and threads theme_mode into TopBar.

Bumps the casement submodule with a fix for the native macOS traffic-light reposition: idempotent absolute placement against resize, baseline invalidation on fullscreen exit, and a poison guard so a transitional re-capture can't drop the lights below their default position.
2026-05-23 12:59:02 +08:00
Fini 2a709d6ed4 feat(orchestrator): validation — parse_response + per-round vision call
C1 of S3c: creates validation.rs with parse_validation_response (JSON
cleaning, tool_use block stripping, fix/structural-fix filtering, quality
score clamping) and validate_design_screenshot_with_image (message
builder, timeout-doubling rule when reference screenshot present, trait
dispatch, skipped propagation). 18 unit tests in validation_tests_c1.rs.
2026-05-23 09:04:11 +08:00
Fini e7ba3696a5 feat(orchestrator): validation_fixes — addChild structural + extract_name_base lowercase
Port buildNodeFromSpec (TS:307-372) + the addChild branch of applyValidationFixes
into Rust.  When a StructuralFix::AddChild arrives, build a PenNode from the
vision-LLM spec (frame/text/rectangle/ellipse/path), insert it via
EditorCommand::InsertSubtree under the parent, then call
auto_fix_parent_layout_after_add_child.  Status-bar parents are protected.

Also fixes the B2 nit: extract_name_base now lowercases the name before
extracting the last word, matching the TS toLowerCase() call so "Nav ITEM"
and "Tab item" share the same base "item".

Icon resolution is a stub (d/iconId left None); a TODO marks the gap for
a future IconResolver trait.  11 new tests added; full suite 480 green.
2026-05-23 08:49:57 +08:00
Fini deb99624d4 feat(orchestrator): validation_fixes — whitelist + validators
Port SAFE_FIX_PROPERTIES (17 entries), is_valid_fix_value, and
is_valid_structural_fix from design-validation-fixes.ts:67-157.
Adds 65 tests covering all property kinds + structural fix shapes.
2026-05-23 08:13:17 +08:00
Fini 4882277b7f feat(orchestrator): validation_dump — node tree dump + count 2026-05-23 08:06:10 +08:00
Fini c098effac6 feat(orchestrator): types — vision validation traits + Progress + config
Add the S3c type layer: 3 injected traits (PreValidator / ScreenshotProvider /
VisionLlmClient), supporting types (PreValidationResult / VisionCallRequest /
VisionResponse), 5 new Progress::Validation* variants, DesignRequest.validation_enabled
field (serde default true), and validation_config.rs constants faithful to
ai-runtime-config.ts:119-123 (node-count threshold 30, max rounds 3, quality
threshold 8, timeout 180 000 ms). Stub impls (Skipped*) added to test_support.rs.
All 35 existing DesignRequest literals updated with the new field. 366 tests pass.
2026-05-23 07:52:59 +08:00
Fini efcb345faa feat(orchestrator): append — apply_append_context_to_plan + status-bar filter 2026-05-23 07:11:25 +08:00
Fini 3484bd3783 feat(orchestrator): run — wire dashboard column layout
S3b-3 Task C3: branch `run()` to `run_dashboard_path` when
`should_use_dashboard_columns` is true for sequential requests.
Extracts the dashboard implementation into `run_dashboard.rs` to
keep `run.rs` under the 800-line ceiling (606 lines post-split).

- Logical-to-live id resolution: walks live document tree after
  `InsertSubtree` via `collect_name_id_map`; resolves Sidebar /
  Main Content / "{label} Slot" by name.
- Sets `subtask.generated_root_id` to the slot live id (ports
  TS `orchestrator.ts:541` `generatedRootId` fallback).
- Sidebar subtasks → `live_sidebar_id`; others → slot live id.
- 3-attempt tier-gated retry ladder identical to sequential path.
- Post-loop: `reorder_dashboard_main_children` + `run_cleanup_passes`
  on both `live_sidebar_id` and `live_main_id`.
- Removes `#![allow(dead_code)]` / `#[allow(dead_code)]` from
  `dashboard_columns.rs` and `scaffold_dashboard.rs` (all symbols
  now live-reachable).
- 4 new tests in `run_tests_c3.rs`: happy path verifies Sidebar /
  Main Content children in live document; non-dashboard regression;
  zero-node NoContent; abort-mid Aborted.

329 tests pass; clippy -D warnings clean; fmt check clean.
2026-05-23 06:34:50 +08:00
Fini ed817af042 feat(orchestrator): dashboard column scaffold
Add `build_scaffold_dashboard` in new sibling `scaffold_dashboard.rs`
(S3b-3 Task C2). Builds the horizontal root + 260px sidebar +
fill_container main column scaffold; calls
`assign_dashboard_main_parents` to synthesize row/slot frames and
**embeds them as nested children of main** in the same `InsertSubtree`
(necessary because `cmd_insert_subtree` remaps every node id —
references between row / slot / main would break across separate
commands). Returns
`(cmds, sidebar_id, main_id, scaffold_baseline)` where the baseline
matches `descendant_count(state, root_id)` after apply (mirrors TS
`scaffoldCounts.set(rootId, countDescendants(root))` in
`orchestrator.ts:1038-1053`).

Port of `orchestrator.ts:283-320` (createDashboardColumnFrames) +
`orchestrator.ts:954-1019` (useDashboardColumns scaffold branch).
`cleanup::count_descendants` upgraded to `pub(crate)` so the scaffold
can pre-compute the same baseline value. Existing sequential +
concurrent scaffold functions unchanged.

12 new tests in sibling `scaffold_tests_c2.rs`; 325 total green.
2026-05-23 06:15:09 +08:00
Fini a1a0ffca12 feat(orchestrator): dashboard_columns — detection predicates 2026-05-23 04:48:12 +08:00
Fini 3643d1fa0e feat(orchestrator): concurrency decision + screen grouping
Adds DesignRequest.concurrency, group_subtasks_by_screen + ScreenGroup,
effective_concurrency, and clamp_concurrency in a new concurrent.rs module.
Faithful port of orchestrator.ts:780-810 (minus S3b-4 append gate).
All 17 DesignRequest literals updated; 183 tests green.
2026-05-23 03:22:04 +08:00
Fini b3c462cd38 feat(orchestrator): subagent — reduced_complexity + minimal_skills skill gating
Port of the TS `executeSubAgent` skill-filtering branches and the
`retryAllowed` set from `orchestrator-sub-agent-compact.ts`.

- New `compact_skills.rs`: `apply_skill_filter` + `SkillNamed` trait.
  - `minimal_skills=true` → keep only `schema`+`jsonl-format`/`-simplified`.
  - `reduced_complexity=true` + `ModelTier::Basic` → keep the 8-skill
    `retryAllowed` set (drops `elements`, `overflow`, `icon-catalog`, etc.).
  - Standard/Full tier: `reduced_complexity` is a no-op (full set through).
- `build_subagent_prompt` gains `reduced_complexity: bool` + `minimal_skills: bool`
  params; resolves tier from the request model and applies the filter after
  `resolve_generation_skills`.
- `run_subtask` gains the same two params and threads them into `build_subagent_prompt`.
- All existing callers in `run.rs` and tests pass `false, false` (single-attempt path).
2026-05-23 02:36:23 +08:00
Fini a161b9ea6b feat(orchestrator): plan_repair — value coercers 2026-05-23 02:04:50 +08:00
Fini e416ee1cf6 feat(orchestrator): timeouts — per-bucket/tier timeout profiles
Port ORCHESTRATOR_TIMEOUT_PROFILES, SUB_AGENT_TIMEOUT_PROFILES, and
builtin-planning timeouts from the TS ai-runtime-config / prompt-optimizer /
model-profiles trio into crates/op-orchestrator/src/timeouts.rs.

- Timeouts { hard, no_text, first_text } + PromptBucket enum
- bucket_for(len): Short(<2200) / Medium(<4200) / Long
- orchestrator_timeouts, sub_agent_timeouts (Basic-tier clamps), builtin_planning_timeouts
- apply_profile_to_timeouts (multiplier scaling, Math.round parity)
- CallRequest gains no_text_timeout/first_text_timeout: Option<Duration>
  (all existing literals updated to None)
2026-05-23 01:59:27 +08:00
Fini 56c99f5aa8 feat(orchestrator): retry — attempt_modes + is_non_retryable 2026-05-23 01:52:57 +08:00
Fini deec760175 feat(orchestrator): mode-aware build_orchestrator_prompt + PlanningPrompt
- Add PlanningPrompt to types.rs (carries CallRequest + forced_style_guide_name + mode)
- Rewrite build_orchestrator_prompt: Rich/Minimal resolve_skills + style-guide context
  + suffix; Compact delegates to build_compact_planning_prompt
- Add RICH_SUFFIX / MINIMAL_SUFFIX / planning_suffix / filter_planning_skills_for_prompt
- Delete PLAN_FORMAT const (replaced by the suffix approach)
- Update run.rs phase-1 caller to PlanningMode::Rich + .call_request
- Re-export build_orchestrator_prompt from lib.rs
- Remove blanket #![allow(dead_code)] from style_guide_context.rs; replace with
  per-field #[allow(dead_code)] on diagnostic fields pending S3b-1b consumption
2026-05-22 22:56:28 +08:00
Fini 96b014c3b1 feat(orchestrator): compact_prompt — build_compact_planning_prompt 2026-05-22 22:56:26 +08:00
Fini 97519ea3b5 feat(orchestrator): style_guide_context — infer_tags_from_prompt 2026-05-22 22:56:22 +08:00
Fini af84103780 feat(orchestrator): design_md_policy — design.md planner helpers 2026-05-22 22:56:21 +08:00
Fini a654ed6800 feat(orchestrator): design_type — detect_design_type + presets 2026-05-22 22:56:19 +08:00
Fini 55a88bdfda feat(orchestrator): model_profile — MODEL_PROFILES tier table 2026-05-22 22:56:18 +08:00
Fini 5d015ee570 feat(orchestrator): op-orchestrator crate — S3a design orchestrator
New crate restoring the design orchestrator (TS orchestrator.ts
phases 1-4, single-screen sequential) that the Rust migration
dropped. Pure crate — no winit/casement/agent dependency.

- types.rs: DocSink + LlmClient traits, CallRequest/LlmChunk/
  Progress/RunSummary and friends
- intent.rs: classify_intent (design vs chat)
- plan.rs: OrchestratorPlan parse + heuristic fallback
- parse.rs: LLM output -> canonical PenNode trees
- plan_normalize.rs: single-screen plan normalization
- variables.rs: plan-derived $color-* seed/snapshot/rollback
- prompt.rs: planning + sub-agent prompt assembly via op-ai-skills
- scaffold.rs: single-screen canvas scaffold (InsertSubtree)
- subagent.rs: sequential sub-agent execution
- cleanup.rs: descendant_count + run_cleanup_passes (reusable for S3b)
- run.rs: Orchestrator::run() — 4-phase spine, spec §6 error/abort/
  zero-content semantics
- test_support.rs: VecDocSink + ScriptedLlm test stubs

NOT verified locally: op-orchestrator depends on op-editor-core,
which does not currently build — vendor/jian is pinned to unpushed
commit 80121906 (see prior commit). Unit tests written throughout;
they run once the jian build is restored.

S3a Plan B + Plan C (orchestrator modules).
2026-05-22 22:56:11 +08:00