Commit graph

10 commits

Author SHA1 Message Date
Kayshen-X 0cb56ecd9e refactor: extract op-codegen crate
Relocate codegen.rs + codegen_targets.rs out of openpencil-shell-core
into a dedicated op-codegen crate. The code generators only depend on
jian-ops-schema + op-editor-core, so they form a clean leaf crate.
codegen.rs becomes the crate's lib.rs; crate::codegen:: paths in
codegen_targets.rs collapse to crate::. Pure relocation, no behaviour
change; tests move with their code.
2026-05-16 23:14:45 +08:00
Kayshen-X fa6fe21ed5 refactor(codegen): port codegen onto PenDocument 2026-05-16 17:15:44 +08:00
Kayshen-X 01731d6af8 refactor(document): migrate test code to string NodeId 2026-05-16 15:42:46 +08:00
Kayshen-X 5063c636f2 feat(shell-core/variables): stroke_refs + set_active_theme — #5 toward 100%
Two final pieces of the Variables/Themes data layer:

  - `stroke_refs: BTreeMap<NodeId, String>` parallels `fill_refs`.
    Paint reads `var_table.stroke_color_for(node.id)` first, falls
    back to `node.stroke.color` otherwise. Both fields registered in
    the canonical loader when it sees a `$ref` on the stroke
    descriptor in `.op` files.
  - `set_active_theme(axis, value)` + `clear_active_axis(axis)`
    mutators give the future theme-picker widget a one-line entry
    point. `clear_active_axis` removes the axis from the active
    map so subsequent resolutions fall back to the variable's
    `theme: None` default — matches TS theme-axis reset behavior.

Tests (2 new):
  - `stroke_color_for_resolves_registered_ref` — register a Color
    variable, register a stroke ref, resolve through the table,
    verify blue channel matches `#0000ff`.
  - `set_active_theme_round_trips_through_axis_picker` — flip an
    axis, add a second, clear one, assert state matches.

#5 Variables/Themes now ~98% — only the panel widget UI is
pending. Plumbing for paint, mutation, loader integration, fill
and stroke ref resolution all working with structural tests.

3 existing codegen test fixtures patched to seed `stroke_refs:
BTreeMap::new()` alongside `fill_refs`.

Tests total: 241 shell-core (+2). Wasm32 build clean.
2026-05-14 15:47:56 +08:00
Kayshen-X c68c1de920 feat(shell-core/variables): fill_refs side-table — paint-time $ref groundwork
#5 Variables advances toward 90% with `VariableTable.fill_refs:
BTreeMap<NodeId, String>` — node-id → variable-name map for fills
that should resolve through the variable table instead of using
`node.fill` directly. Avoids touching `Node`'s shape (which would
invalidate every Node literal across test fixtures + builders) by
piggybacking on the same VariableTable the canonical loader fills.

API additions on `VariableTable`:
  - `set_fill_ref(node_id, ref_name)` — register a node's fill ref
  - `fill_for(node_id) -> Option<Color>` — resolve through the
    current `active_theme`; falls back to None when no ref is
    registered or the variable doesn't resolve. Canvas paint will
    call this first, fall through to `node.fill` on None.

Side-derivation: `NodeId` now derives `PartialOrd + Ord` so it
can key into `BTreeMap`. The existing 3-codegen-test fixtures
gain a `fill_refs: BTreeMap::new()` initializer.

Tests (2 new):
  - `fill_for_resolves_registered_node_ref_to_themed_color`:
    register a Themed Color variable + a node ref, flip
    `active_theme`, verify the resolved Color tracks the theme
  - `fill_for_returns_none_when_no_ref_registered`: unknown
    NodeId returns None so paint falls back to direct fill

Canvas-side `paint_node` integration (`let fill =
doc.var_table.fill_for(node.id).or(node.fill);`) lands when the
canvas refactor for that path arrives next.

Tests total: 229 shell-core (+2). Wasm32 build clean.
2026-05-14 15:36:20 +08:00
Kayshen-X 819fb36ce7 feat(shell-core/codegen): Compose + React Native — all 9 generators shipped
Completes #10 on the TS-parity roadmap. All nine targets the TS
`pen-codegen` package exposes now have Rust equivalents behind the
shared `Codegen` trait:

  1. CssVariables — design tokens → `:root { --name: value }`
  2. Html         — absolute-positioned `<div>` / `<span>` tree
  3. Vue          — SFC `<template>` + `<style scoped>`
  4. Svelte       — `<script>` + markup + `<style>`
  5. React        — JSX functional component, inline style object
  6. Flutter      — `Stack(children: [Positioned(Container/Text)])`
  7. SwiftUI      — `ZStack { Rectangle/Ellipse/Text.frame.position }`
  8. Compose      — `@Composable Box(Modifier.offset.size)`
  9. ReactNative  — `<View>` / `<Text>` with `position: 'absolute'`

Compose specifics: `Modifier.offset(x.dp, y.dp).size(width.dp,
height.dp).background(Color(r,g,b,a))` per node, default-export
`@Composable fun Page()`. Text nodes wrap as `Text(text =
"...")`.

React Native specifics: default-exported functional component
that returns a wrapping `<View>` flexed to fill, with each node
as `<View>` / `<Text>` at `position: 'absolute'`. Color uses CSS
`rgb()`/`rgba()` (RN accepts both).

Tests (2 new):
  - Compose: composable annotation + offset/size/color modifiers
  - RN: import statement + functional component + absolute style

Tests total: 220 shell-core (+2) + 20 native + 8 desktop = 248
total. Wasm32 build clean. #10 Codegen status: 100%.
2026-05-14 15:30:35 +08:00
Kayshen-X 7c08e1b45b feat(shell-core/codegen): React + Flutter + SwiftUI — 7 of 9 generators
- `React` — JSX functional component named `Page` wrapping nodes in a
    fragment; inline `style={{}}` objects with camelCase keys.
  - `Flutter` — `Stack(children: [Positioned(left, top, child:
    Container/Text)])`. Color emits as `Color.fromARGB`.
  - `SwiftUI` — `ZStack { Rectangle()/Ellipse()/Text(...) .frame.position
    }`. Color emits as `Color(red, green, blue, opacity)`.

All three reuse the established `Codegen` trait + walk
`doc.pages[active].children` the same way the HTML / Vue / Svelte
emitters do — `hidden` nodes are skipped, children recurse, text
bodies escape special chars (HTML targets) or use Dart/Swift
string-literal-escaping (`{:?}` debug-fmt) for compiled targets.

Tests (4 new):
  - React: import statement + component declaration + JSX fragment
  - Flutter: Stack wrapper + Positioned/Container shape + ARGB color
  - SwiftUI: ZStack + Rectangle + .frame modifier
  - SwiftUI ellipse: NodeKind::Ellipse → `Ellipse()` view

Remaining 2 generators (Compose, React Native) ship in a follow-up
commit — both follow the same trait-per-target shape with their
specific framework's geometry primitives.

Tests total: 218 shell-core (+4) + 20 native + 8 desktop. Wasm32
build clean.
2026-05-14 15:29:37 +08:00
Kayshen-X f4700af062 feat(shell-core/codegen): Vue + Svelte targets — 4 of 9 generators
Adds `Vue` and `Svelte` codegen targets alongside the existing
`CssVariables` and `Html`. Both reuse `emit_node_html` for markup
and embed the `CssVariables` generator's output verbatim in their
`<style>` block, so design tokens flow through into the framework
output as CSS custom properties.

  - `Vue` — Vue 3 SFC: `<template>` (node markup) + `<script setup
    lang="ts">` placeholder + `<style scoped>` (variables).
  - `Svelte` — Svelte SFC: `<script lang="ts">` placeholder + bare
    markup (no wrapping template tag, per Svelte convention) +
    `<style>` (variables). Script-then-style order enforced by
    test.

Tests (3 new):
  - `vue_emits_template_script_style_blocks` — all three SFC
    sections present
  - `svelte_emits_script_then_markup_then_style` — script appears
    before style in output (positional check)
  - `vue_includes_variable_css_in_style_block` — variables flow
    through into the scoped style block

5 generators remain (React + Tailwind, Flutter, SwiftUI, Compose,
React Native). The HTML-derivable group is done; the remaining 5
are framework-specific component models that need their own
emitters.

Tests total: 214 shell-core (+3) + 20 native + 8 desktop. Wasm32
build clean.
2026-05-14 15:27:51 +08:00
Kayshen-X 24df393c36 feat(shell-core/codegen): HTML generator — 2 of 9 targets shipped
Adds `codegen::Html` alongside the existing `CssVariables` generator.
Walks `doc.pages[active].children` and emits absolute-positioned
`<div>` per Rect/Frame/Group + `<span>` per Text, with inline-style
position/size, RGB fill, stroke as border, corner radius (50% for
ellipses), and rotation transform. Body text is HTML-escaped.

API stays identical — both generators implement the same
`Codegen` trait, so a future CLI / Property-panel codegen
dispatcher fans out by trait object.

Tests (4 new):
  - DOCTYPE + body wrapper + generator-attribution comment present
  - Rect emits `<div>` with left/top/width/height + `rgb(r,g,b)` fill
  - Text emits `<span>` and `&` / `<` / `>` in body get HTML-escaped
  - `node.hidden = true` skipped entirely (no orphan markup)

7 generators remain to port (React + Tailwind, Vue, Svelte,
Flutter, SwiftUI, Compose, React Native) — each as a focused
follow-up commit. The trait + dispatcher shape doesn't change.

Tests total: 211 shell-core (+4) + 20 native + 8 desktop. Wasm32
build clean.
2026-05-14 15:25:58 +08:00
Kayshen-X 8bb72524cd feat(shell): codegen::CssVariables — first of 9 generators ships
#10 on the TS-parity roadmap starts. User directive (memory
project_op_rust_gap_priority) is "codegen last"; CSS Variables is
the simplest and complements the #5 Variables/Themes work already
shipped this session — it emits whatever lands in
`doc.var_table` as a stylesheet without requiring the rest of the
node tree.

API:
  - `crate::codegen::Codegen` trait — `target_label()` +
    `generate(&Document) -> String`. Pure: no file I/O.
  - `CssVariables` impl — walks `doc.var_table.variables` and emits
    `:root { --name: value; }` for scalar entries, plus
    `:root[data-axis="value"] { ... }` blocks for each themed
    combination. CSS ident sanitisation maps non-alphanum chars to
    `-` so `primary.color` → `--primary-color`.

Tests (4):
  - emits scalars (`#0066ff`, `12`) under a `:root` block
  - per-theme variables emit one block per axis combo, both light
    and dark CSS variables present
  - non-ident chars sanitised (`primary.color` → `primary-color`)
  - empty doc emits only the generator header comment

Remaining 8 generators (React + Tailwind, HTML, Vue, Svelte,
Flutter, SwiftUI, Compose, React Native) ship in follow-up
commits; the `Codegen` trait means each is a focused new file.

Tests total: 198 shell-core (+4). Wasm32 build clean.
2026-05-14 15:07:42 +08:00