From f30bc9e82f60ebb8cfba6433dcc0e578a8824c67 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Fri, 2 Oct 2026 01:38:24 +0900 Subject: [PATCH] =?UTF-8?q?fix(M01):=20=EC=8B=9C=ED=97=98=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20=ED=95=9C=20=EB=B2=8C=20=E2=80=94=20=EC=99=BC?= =?UTF-8?q?=EC=AA=BD=20=EC=8B=9C=ED=97=98=20=EA=B3=84=EC=82=B0=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20=E2=86=94=20=ED=9D=90=EB=A6=84=20=EB=B3=80=EC=88=98?= =?UTF-8?q?=20=EC=98=88=EC=8B=9C=EA=B0=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 로직을 열 때 왼쪽 시험 계산 입력(자동값 포함)을 이름이 같은 흐름 변수 예시값으로 - 왼쪽 입력을 바꾸면 흐름 예시값도 · 흐름 검증 칸 예시값을 바꾸면 왼쪽 입력도 바꾸고 다시 셈 - 예시값만 바뀐 것은 고친 것(drafts)으로 안 셈(sameShape) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_016PSXsvyDxjNmufteN5bqmt --- M01_MasterData/M01_MasterData_UI_LogicLab.ts | 48 +++++++++++++++++-- .../M01_MasterData_UI_LogicLab_Flow.ts | 44 +++++++++++++++++ 2 files changed, 88 insertions(+), 4 deletions(-) diff --git a/M01_MasterData/M01_MasterData_UI_LogicLab.ts b/M01_MasterData/M01_MasterData_UI_LogicLab.ts index 14210902b..7b0ec9b4d 100644 --- a/M01_MasterData/M01_MasterData_UI_LogicLab.ts +++ b/M01_MasterData/M01_MasterData_UI_LogicLab.ts @@ -41,7 +41,10 @@ import { tl } from "./M01_MasterData_UI_LogicLab_Text"; import { buildSplit, emptyFlow, + examplesOf, fetchFlow, + sameShape, + withExamples, mountLogicFlow, tf, type LogicFlow, @@ -244,12 +247,16 @@ export async function mountM01LogicLab( split.right.replaceChildren(el("p", { className: "m01lab-flow__wait", text: tf("Waiting") })); const own = flowOf(row); const got = own || o.origKey === null ? null : await fetchFlow(o.origKey, o.file); - const flow = own ?? got?.flow ?? (await emptyFlow(row.이름, row.결과단위)); + // 시험 입력 한 벌 — 열 때 왼쪽 시험 계산 입력을 흐름 변수 예시값으로 + let base = withExamples( + own ?? got?.flow ?? (await emptyFlow(row.이름, row.결과단위)), + o.values, + ); if (opened !== o) return; try { const made = await mountLogicFlow({ split, - flow: clone(flow), + flow: clone(base), title: row.이름 || row.키, readOnly: !admin, notes: got?.notes ?? [], @@ -258,6 +265,12 @@ export async function mountM01LogicLab( onChange: (lg) => { if (!o.row || opened !== o) return; const was = flowOf(o.row); + if (sameShape(lg, was ?? base)) { + // 예시값만 바뀜 — 고친 것 아님 · 왼쪽 시험 계산 입력에 같은 이름으로 넣고 다시 셈 + base = lg; + syncLeft(o, examplesOf(lg)); + return; + } o.row.흐름 = lg; touch(); if (!was) drawEditor(); // 이제 흐름 로직 — 왼쪽을 흐름 보기로 @@ -265,6 +278,7 @@ export async function mountM01LogicLab( }); if (opened !== o) return made.destroy(); flowNow = made; + made.setExamples(o.values); // 띄우는 사이에 온 자동값까지 } catch (e) { split.right.replaceChildren( el("p", { className: "m01-logic__muted", text: tf("Failed", String(e)) }), @@ -272,6 +286,32 @@ export async function mountM01LogicLab( } }; + /** 흐름 예시값 → 왼쪽 시험 계산 입력(이름이 있는 칸만) · 바뀌면 왼쪽을 다시 그리고 셈 */ + const syncLeft = (o: Opened, ex: Record): void => { + const names = new Set((o.row?.입력 ?? []).map((x) => x.이름)); + const changed = Object.entries(ex).filter(([k, v]) => names.has(k) && o.values[k] !== v); + if (!changed.length) return; + quiet = true; + for (const [k, v] of changed) o.values[k] = v; + quiet = false; + drawCalc(); + calc.querySelector("[data-calc-run]")?.click(); + }; + /** 왼쪽 시험 계산 입력 — 칸이 바뀌면 흐름 예시값으로(이 로직이 열려 있을 때만) */ + let quiet = false; + const watchValues = (): Record => { + const self: Record = new Proxy({} as Record, { + set(t, k, v) { + const was = t[k as string]; + t[k as string] = v as string; + if (!quiet && was !== v && opened?.values === self) + flowNow?.setExamples({ [k as string]: v as string }); + return true; + }, + }); + return self; + }; + const show = (next: Opened | null): void => { flowNow?.destroy(); flowNow = null; @@ -294,7 +334,7 @@ export async function mountM01LogicLab( lines: null, text: null, ownHost: ownHost(), - values: {}, + values: watchValues(), flowHost: el("div"), }); return; @@ -314,7 +354,7 @@ export async function mountM01LogicLab( lines: null, text: null, ownHost: ownHost(), - values: {}, + values: watchValues(), flowHost: el("div"), }); } catch (error) { diff --git a/M01_MasterData/M01_MasterData_UI_LogicLab_Flow.ts b/M01_MasterData/M01_MasterData_UI_LogicLab_Flow.ts index 5f554f83e..55b7c00be 100644 --- a/M01_MasterData/M01_MasterData_UI_LogicLab_Flow.ts +++ b/M01_MasterData/M01_MasterData_UI_LogicLab_Flow.ts @@ -150,6 +150,40 @@ export async function emptyFlow(name: string, unit: string): Promise { return (await import("../M02_MasterTemplete/DataFlow/M02_DF_Model")).newLogic(name, unit); } +/* ── 시험 입력 한 벌 — 왼쪽 시험 계산 입력 ↔ 흐름 변수 예시값(이름이 같은 변수끼리) ── */ + +const varsOf = (lg: CfLogic) => + lg.boxes.flatMap((b) => [...(b.input?.variables ?? []), ...(b.func?.variables ?? [])]); + +/** 흐름 변수 예시값 — 이름 → 글(빈 값은 뺌) */ +export function examplesOf(lg: CfLogic): Record { + const out: Record = {}; + for (const v of varsOf(lg)) + if (v.example !== null && v.example !== undefined && v.example !== "") + out[v.name] = String(v.example); + return out; +} + +/** 예시값을 넣은 새 흐름 — 수처럼 생긴 글은 수로 · 빈 값은 그대로 둠 */ +export function withExamples(lg: CfLogic, vals: Record): CfLogic { + const next = JSON.parse(JSON.stringify(lg)) as CfLogic; + for (const v of varsOf(next)) { + const raw = (vals[v.name] ?? "").trim(); + if (raw) v.example = /^-?\d+(\.\d+)?$/.test(raw) ? Number(raw) : raw; + } + return next; +} + +/** 예시값만 다른가 — 그 밖이 같으면 고친 것으로 안 셈 */ +export function sameShape(a: CfLogic, b: CfLogic): boolean { + const strip = (lg: CfLogic): string => { + const c = JSON.parse(JSON.stringify(lg)) as CfLogic; + for (const v of varsOf(c)) v.example = null; + return JSON.stringify(c); + }; + return strip(a) === strip(b); +} + export interface LogicFlowOpts { split: SplitHandle; flow: CfLogic; @@ -168,6 +202,8 @@ export interface LogicFlow { set(lg: CfLogic): void; /** 왼쪽만 다시(왼쪽 화면을 새로 그린 뒤) */ redrawLeft(): void; + /** 왼쪽 시험 계산 입력이 바뀜 — 같은 이름 변수의 예시값으로(고친 것 아님) */ + setExamples(vals: Record): void; destroy(): void; } @@ -253,6 +289,14 @@ export async function mountLogicFlow(opts: LogicFlowOpts): Promise { refresh(); }, redrawLeft: left, + setExamples: (vals) => { + const next = withExamples(lg, vals); + if (JSON.stringify(next) === JSON.stringify(lg)) return; + lg = next; + panel.set(next); + left(); + refresh(); + }, destroy: () => { gone = true; panel.destroy();