/* ============================================================================= * M01_MasterData/logic/M01_Logic_Equation_Model.ts * 수식 탭 계산(796-2 · 화면 · fetch 없음) — 줄 → 고침 자리 · 식 칸 아이콘 끼움 · 옵션 값 꼴 · 변수 고침 요청. * 서버 고침 꼴 = `master_equation_edit.py` 머리(할일 식 · 더함 · 지움 · 변수 · 표시) — 여기서는 요청 목록만 만듦 * 셈 · 검사는 서버 한 벌(③ 서버 단독) — 브라우저는 모양만 * ========================================================================== */ import type { EqLine } from "@ui/ui_template_equation"; export interface EqEdit { 할일: "식" | "더함" | "지움" | "변수" | "표시"; [key: string]: unknown; } /** 식 줄의 고침 자리(`중간:<열쇠>` · `결과` · `호표:` · `덧줄:`) */ export const spotOf = (line: EqLine): string => line.갈래 === "호표" || line.갈래 === "덧줄" ? `${line.갈래}:${line.차례 ?? 0}` : line.갈래 === "결과" ? "결과" : `중간:${line.열쇠}`; export const formulaEdit = (line: EqLine, text: string): EqEdit => ({ 할일: "식", 자리: spotOf(line), 식: text, }); /** 아이콘 줄 — 식 칸 글에 끼우는 꼴 */ export type InsertKind = "frac" | "paren" | "sub" | "sup" | "percent"; /** 선택 영역 `[from, to)` 를 아이콘 꼴로 바꾼 글 · 커서 자리 */ export function insertInto( kind: InsertKind, text: string, from: number, to: number, ): { text: string; caret: number } { const head = text.slice(0, from); const pick = text.slice(from, to); const tail = text.slice(to); const put = (mid: string, caret: number): { text: string; caret: number } => ({ text: head + mid + tail, caret: head.length + caret, }); switch (kind) { case "frac": // 분수 = (위) / (아래) — 고른 글이 위 · 커서는 아래 괄호 안 return pick ? put(`(${pick}) / ()`, pick.length + 6) : put("() / ()", 1); case "paren": return pick ? put(`(${pick})`, pick.length + 2) : put("()", 1); case "sub": return put(`${pick}_`, pick.length + 1); case "sup": return put(`${pick}^`, pick.length + 1); case "percent": return put(`${pick} / 100`, pick.length + 6); } } /** 옵션 값 — 수 꼴 글은 수로(엔진 입력 꼴) · 빈 값은 안 보냄(자동값) */ export function inputsOf(values: Record): Record { const out: Record = {}; for (const [name, raw] of Object.entries(values)) { if (raw === "") continue; out[name] = /^-?\d+(\.\d+)?$/.test(raw) ? Number(raw) : raw; } return out; } export interface VarForm { 열쇠: string; 이름: string; 기호: string; 위: string; 아래: string; 단위: string; 갈래?: "입력" | "중간"; 설명?: string; 기본값?: string; 식?: string; /** 보이는 자리 글(중간 줄 · 빈 글 = 전체) */ 표시?: string; } /** 변수 고침 창 → 요청 목록(바꾼 칸만) — `보이는 자리`(중간 줄) = 0~6 · 빈 칸 = 전체 자리(`표시` 지움) */ export function varEdits(before: VarForm, now: VarForm): EqEdit[] { const out: EqEdit = { 할일: "변수", 열쇠: before.열쇠 }; let changed = false; for (const k of ["이름", "기호", "위", "아래", "단위"] as const) if (now[k] !== before[k]) { out[k] = now[k]; changed = true; } const list: EqEdit[] = changed ? [out] : []; if (now.표시 !== before.표시) { const digits = now.표시 === "" || now.표시 === undefined ? null : Number(now.표시); list.push({ 할일: "표시", 열쇠: before.열쇠, 표시: digits }); } return list; } /** 변수 더함 창 → 요청 — 빈 칸은 안 보냄 */ export function addEdit(form: VarForm, after?: string): EqEdit { const out: EqEdit = { 할일: "더함", 갈래: form.갈래 ?? "입력", 열쇠: form.열쇠.trim() }; for (const k of ["이름", "기호", "위", "아래", "단위", "설명"] as const) { const v = (form[k] ?? "").trim(); if (v) out[k] = v; } if (form.갈래 === "중간") { out.식 = form.식 || "0"; if (after !== undefined) out.뒤 = after; } else if (form.기본값) out.기본값 = form.기본값; return out; }