6f1c7367e0
feat( #79 ): IMP-51 image_overrides axis (u1~u11 backend stamp+CLI+CSS inject + frontend drag/resize+persistence + tests)
2026-05-22 21:54:38 +09:00
bd8bcf748b
feat( #81 ): IMP-54 frontend zone editing UI (u1~u4 edit-mode body-drag + emerald highlight + pure drag-math helper + vitest)
...
u1: 4 perimeter edge strips (~8px) + top-left grip chip at zone wrapper
provide an edit-mode pointer-event surface (zIndex 25) so wrapper-level
handleZoneMouseDown becomes reachable in edit mode. Wrapper stays
pointerEvents:none and iframe stays pointerEvents:auto to preserve
text-edit reachability (A8 guardrail). Resize handles (z-30) win in
overlap regions. Iframe pointer-events temporarily forced none during
drag to prevent mouseup leak.
u2: Edit-mode isSelected branch reuses selectedZoneId with emerald visual
(border-emerald-500 / bg-emerald-500/10) distinct from pendingLayout
blue, decorative-only (pointerEvents:none inherits via wrapper rules).
u3: Pure drag math extracted to slideCanvasDragMath.ts — DRAG_THRESHOLD_PX,
crossedDragThreshold(dx, dy) strict Math.hypot > 5, and clampZoneMove
pixel→fraction conversion with x∈[0, 1-w] / y∈[0, 1-h] clamp.
Resize math (makeResizeHandler) untouched.
u4: Vitest coverage (12 tests, 3 describe blocks) on the pure helper:
threshold strict boundary at (3,4)/(5,0)/(0,5), above-threshold,
negative-symmetric, clamp negative→0, max-edge → 1-w / 1-h, per-axis
independence, non-square 500×250 slide-body, return-shape {x,y} only.
Stage 4 verify: pnpm exec vitest run client/src/components/slideCanvasDragMath.test.ts → 12/12 PASS.
Scope: edit-mode UX only. No HTML text modification, no automatic frame swap, no MDX touched.
Depends on: #9 IMP-09 (--override-zone-geometry backend wire), #80 IMP-52 (user_overrides.json zone_geometries persistence).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-22 13:35:34 +09:00
9388e25e76
feat( #80 ): IMP-52 user_overrides.json persistence (u1~u10 backend + frontend + tests)
...
4-axis MDX-stem keyed persistence so layout / zone_geometries / zone_sections / frames
survive across `/api/run` sessions. Auto-restore on MDX reopen; CLI > file precedence
on backend pipeline entry; 300ms-debounced PUT flushed before Generate.
u1 src/user_overrides_io.py — load/save/validate_key (MDX-stem regex), 4-axis schema,
miss={}, corrupt warning+{}, atomic tmp+rename, foreign-key preserve.
u2 src/phase_z2_pipeline.py — post-argparse fallback fills only missing axes.
u3 Front/vite.config.ts — GET /api/user-overrides/:key (200 {} on miss, 400 traversal).
u4 Front/vite.config.ts — PUT /api/user-overrides/:key, 4-axis allowlist, partial merge.
u5 Front/client/src/services/userOverridesApi.ts — typed get/save + flushUserOverrides
with 300ms debounce and mutated-axis partial payloads.
u6 Front/client/src/pages/Home.tsx + slidePlanUtils.ts — restore on MDX upload (non-frame
axes immediately, frames remapped post-loadRun unit_id → region.id).
u7 Home.tsx — persist on 4 mutation handlers (section drop, layout select, zone resize,
frame select); zone_sizes and Generate excluded.
u8 tests/test_user_overrides_io.py — round-trip, unknown-key passthrough, missing/corrupt,
invalid keys (26 tests).
u9 tests/test_user_overrides_pipeline_fallback.py — per-axis fill, CLI-wins, no-file noop,
corrupt warning+skip (16 tests).
u10 Home.tsx + user_overrides_write.test.ts — await flushUserOverrides() before runPipeline
in handleGenerate try-block head; source-pattern regression assertions (20 → 22 tests).
Backend pytest 42/42 green. Frontend vitest 113/113 green (endpoint 42 / restore 21 /
service 28 / write 22). HEAD baseline ee97f4f; no spillover to phase_z2 templates /
families / frames / pipeline orchestration outside the IMP-52 surface.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-22 11:47:11 +09:00
2ef02f5f18
feat( #76 ): IMP-47B u11 frontend human_review surfacing (hunk-split from IMP-41)
...
- AiRepairStatus interface mirrors backend step20 u8 schema
- formatAiRepairHumanReviewMessage(): pure helper for the three failure axes
(error / coverage_violated / unsupported_kind) — null on success/no-AI
- Home.tsx: toast.error(aiReviewMsg) after run completion
- FramePanel.tsx: reject-click window.confirm guard ("frame 유지 + AI 재구성")
- imp47b_human_review_toast.test.tsx: 6 vitest cases (null/false/3 axes/other)
Verification (frontend node_modules junction from main worktree):
- vitest imp47b_human_review_toast.test.tsx: 6/6 passed
- vitest full suite: 19/19 passed (imp41_application_mode 13 + u11 6, zero regression)
Hunk-split rationale:
- stash@{0} (imp47b-frontend-u11-pre-rebase, captured before IMP-41 merged)
contained inline IMP-41 helpers alongside u11 changes
- HEAD already has IMP-41 helper-based implementation (buildBadgeTitle /
mergeApplicationCandidates from services/applicationMode.ts, f358604 )
- This commit adds ONLY the u11 surface on top of HEAD's IMP-41 baseline
- No IMP-41 hunk regression: buildBadgeTitle / mergeApplicationCandidates /
applicationMode forwarding preserved verbatim
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com >
2026-05-22 00:34:32 +09:00
f358604fb3
feat( #70 ): IMP-41 application_mode forwarding to FramePanel V4 badge tooltip (u1~u5)
...
Forward backend Step 9 `unit.application_candidates[]` (application_mode /
auto_applicable / delegated_to) onto FrameCandidate and surface the
application_mode as a Korean consequence phrase in the FramePanel V4-label
inline badge tooltip. Deterministic frontend-only refactor; no LLM call,
no V4-label color change, no outer composedTitle change.
u1: types/designAgent.ts — add optional applicationMode / autoApplicable /
delegatedTo on FrameCandidate (legacy fixtures keep undefined).
u2: services/applicationMode.ts (new) — pure helper exporting
ApplicationMode union, APPLICATION_MODE_TOOLTIP_KR (keyed by backend
mode VALUE, NOT V4 label), buildBadgeTitle, mergeApplicationCandidates.
u3: tests/imp41_application_mode.test.ts (new) — 13 Vitest cases pinning
composite output per mode, undefined/unknown→legacy fallback, merge by
template_id, skip missing/empty/non-string keys, first-wins on dupes,
empty/null/non-array input.
u4: services/designAgentApi.ts — bridge consumes mergeApplicationCandidates
and forwards three fields onto FrameCandidate while preserving
LABEL_PRIORITY sort and TOP_N_FRAMES slicing.
u5: components/FramePanel.tsx — V4-label badge `title` now calls
`buildBadgeTitle(candidate.label, candidate.applicationMode)`;
badge color className map preserved verbatim; outer composedTitle
untouched.
Scope-qualified verification (5 files, IMP-41 axis only):
- Vitest: client/tests/imp41_application_mode.test.ts — 13/13 PASS.
- Diff↔Plan parity: 5 files match Stage 2 plan, no scope creep.
- AI-isolation contract honored: tooltip values originate from backend
enum; no frontend re-derivation from V4 label.
- No spacing/font shrink; clipping resolution stays at layout/zone/frame
layer (feedback_phase_z_spacing_direction).
Pre-existing unrelated diagnostics (BottomActions.tsx,
imp47b_human_review_toast.test.tsx) remain open on their own axes and are
not gated by this commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-22 00:17:32 +09:00
15ef7c65e9
fix( #75 ): IMP-47A mdx03 frontend execution stabilization (u1~u4)
...
u1: SlideCanvas iframe sandbox += allow-scripts (allow-same-origin preserved)
→ embedded-mode script in slide_base.html now applies html.embedded
→ standalone CSS reset deactivates inside iframe; no clipping
u2: designAgentApi.loadRun merges candidate_evidence + v4_all_judgments
+ v4_candidates via Map<template_id|id|frame_id> dedup,
LABEL_PRIORITY (use_as_is<light_edit<restructure<reject) then
confidence desc, capped TOP_N_FRAMES=6
u3: Home.handleGenerate useCallback deps = [uploadedFile, slidePlan,
userSelection, pendingZones, pendingLayout] (5-tuple, stale-closure fix)
u4: tests/manual/imp47a_e2e.md — mdx03 manual e2e spec (5 axes)
Frontend-only. Backend src/ untouched. No template/catalog edits.
Determinism preserved (no LLM in frontend merge logic).
Baseline: pytest -q tests → 623 passed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-21 14:56:56 +09:00
b4872ba6ce
feat( #38 ): IMP-29 frontend zone-level evidence bridge (candidate_evidence reader + types + UI)
2026-05-20 21:53:47 +09:00
7a52cebfaa
feat(IMP-14): A-4 — slide_base embedded vs standalone mode contract
...
Step 13 owns iframe-vs-standalone CSS contract in slide_base.html via
3-valued embedded_mode enum (auto / embedded / standalone). Removes
SlideCanvas.tsx runtime CSS injection workaround; frontend now passes
?embedded=1 query so auto-mode script attaches html.embedded class and
scopes the standalone body centering/min-height/padding reset.
- templates/phase_z2/slide_base.html: conditional html.embedded class +
CSP-safe auto-mode <script> + additive html.embedded body/.slide rules
- src/phase_z2_pipeline.py: render_slide gains keyword-only embedded_mode
("auto" default) + ValueError guard; 3 existing call sites unchanged
- Front/client/src/components/SlideCanvas.tsx: derive embeddedSrc with
?embedded=1 (query-preserving), drop reset CSS injection block
- tests/phase_z2/test_slide_base_embedded_mode.py: 6 cases — auto script,
CSS rules, embedded/standalone explicit modes, byte-determinism,
invalid-mode guard
2026-05-18 07:21:31 +09:00
a79bd8bc43
feat(IMP-11): D-2 — frame min_height_px hint (backend → UI)
...
Step 9 v4_all_judgments[] now exposes per-candidate min_height_px from
catalog frame_contracts.visual_hints.min_height_px (None when contract
unregistered). SlideCanvas pendingLayout zones render a red ring + 'min H
Npx' badge when zone height falls below the active frame's threshold.
Visual hint only; resize clamp (minSize=0.05) unchanged.
5 axes (single commit per Stage 5 plan):
- u1 backend: src/phase_z2_pipeline.py — Step 9 builder adds min_height_px
via single get_contract(c.template_id) lookup; reuses _contract for
catalog_registered (no double-lookup).
- u2 type: Front/client/src/types/designAgent.ts — FrameCandidate gains
optional minHeightPx?: number.
- u3 mapper: Front/client/src/services/designAgentApi.ts — maps snake-case
min_height_px → camelCase minHeightPx on v4_all_judgments path;
v4_candidates fallback remains undefined (graceful).
- u4 active-frame lookup: Front/client/src/components/SlideCanvas.tsx —
activeFrameId = overrideFrameId ?? defaultFrameId; activeCandidate via
region.frame_candidates.find.
- u5 hint render: Front/client/src/components/SlideCanvas.tsx —
zoneHeightPx = height * SLIDE_H (logical px, no double-apply); compare
against activeCandidate.minHeightPx in pendingLayout mode only; red
border + badge when below.
Tests: 5/5 pass in tests/test_phase_z2_step9_v4_all_judgments_min_height.py
(source-string + catalog-shape guards + None propagation, registered and
unregistered template_ids).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-17 22:29:17 +09:00
0fb168befc
feat(IMP-10): D-1 — filtered_section_reasons UI (read-only)
...
Surface step20_slide_status.json.data.filtered_section_reasons in the
frontend Home header. Verbatim mirror of backend payload — no enum
redefinition, no translation, no auto-classification.
Units:
- u1: FilteredSectionReason interface mirroring src/phase_z2_pipeline.py
:2217-2278 (10 fields incl. override-uncovered source/position variant).
- u2: RunMeta extension + loadRun() mapping with ?? [] back-compat defaults.
- u3: Header badge + <details> disclosure adjacent to existing status
badge; hidden when filtered_section_ids.length === 0; renders all 10
schema fields + filter_reasons[] verbatim.
Scope:
- Frontend-only, read-only. No backend / sync script / Kei·AI panel
changes. Files: Front/client/src/services/designAgentApi.ts (+20),
Front/client/src/pages/Home.tsx (+25).
Refs: gitea issue #10 (IMP-10 D-1 filtered_section_reasons UI)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-17 19:43:13 +09:00
ab2764c8d0
feat(IMP-08): U3 — frontend wire (zoneSections override)
...
Wires the frontend drag/drop zone assignment through to the backend
--override-section-assignment CLI flag.
PipelineOverrides gains an optional zoneSections field
(Record<string, string[]>) carrying canonical ordinal section ids
(e.g., "top": ["04-2-sub-1"]).
Vite middleware /api/run accepts overrides.zoneSections and forwards
each non-empty zone as `--override-section-assignment ZONE=sid[,sid]`.
Empty arrays and non-string sids are filtered to avoid bogus
assignments from a partially-built UI state.
Home.tsx builds the override with a diff-vs-default guard per Codex
Stage 3 R3 B3 fix : createInitialUserSelection seeds zone_sections with
the auto plan, so a literal copy would pollute backend assignment-source
provenance even on a fresh re-render. The diff compares each zone's
section list against sourcePlan.zones[].section_ids and only emits zones
that differ. Toast summary now reports zoneSections=N when forwarded.
Smoke verification : python -m src.phase_z2_pipeline samples/mdx_batch/04.mdx
test_imp08_smoke --override-section-assignment primary=04-2-sub-1 produces
section_assignment_plan with assignment_source=cli_override and
v4_selector_trace.candidates populated via the U1 alias resolver
(04-2-sub-1 -> 04-2.1 V4 entry).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-15 22:36:16 +09:00
0f0d3fa91f
feat(frontend): add Front/ — Vite/React frontend with backend pipeline integration
...
Mirror of design_agent_front/design-agent/ for shipping alongside backend.
Vite plugin (vitePluginPhaseZApi) endpoints :
- POST /api/run — spawn `python -m src.phase_z2_pipeline` with overrides
- GET /api/sample-mdx?mdx=03/04/05 — fixed sample MDX
- GET /frame-preview/{n} — figma preview thumbnails
- GET /data/runs/{run_id}/{path} — pipeline artifacts (final.html, step*.json, ...)
Env toggle forward (보고용) :
PHASE_Z_ALLOW_RESTRUCTURE / PHASE_Z_ALLOW_REJECT / PHASE_Z_MAX_RANK=32
Components :
- LeftMdxPanel (03/04/05 fix list + section tree)
- SlideCanvas (iframe + slideOverrideCss prop for inline CSS inject)
- FramePanel (label priority + confidence sort)
- LayoutPanel
README with mermaid diagrams covering the 5-step demo flow.
node_modules / dist / .manus-logs / .env excluded via .gitignore.
2026-05-14 14:48:42 +09:00