Files
Aislo/M02_MasterTemplete/StructOptions/M02_SO_Def_Spec_Model.ts
T
eomsangdonandClaude Opus 5.5 281c71b55b refactor(logic): 348-1 순수 계산 TS 49 파일 <페이지>/logic/ 로 옮김 — 이름 앞머리 그대로 · 동작 고침 없음
- 옮김: B04 2 · B05 23 · B06 11 · A00_Common/design 13 (9,299 줄) · 이미 logic/ 에 있던 16 파일 그대로
- 안 옮김: 묶음 입구(package.json build:*) · 파이썬 · 순수지만 계산 아닌 8 (보기 결선 · 콜백 · API 타입 · 갈아끼움 알림판)
- import 266 줄(142 파일) · 시험 · 주석 · 짝 표시 문자열 경로 고침 · B05 · B06 CLAUDE.md 짝 줄 logic/
- 시험 도우미 새 경로: 가짜 require 열쇠 · B05()/B06() 부름 · 형제 import 자리 · tsc 묶어 돌리는 시험 · 정규식 · rglob
- design_view_cross 줄 한도 시험 685 → 688 (긴 import 경로 줄바꿈 3줄)
- 확인: typecheck 0 · 서버 묶음 넷(corridor · server-calc · b07-extent · b07-note) 다시 지어 코드 같음(//#region 경로 주석만 다름) · 관련 시험 270 파일 — 남은 실패 19 는 옮기기 전과 같음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014utrjfoW831Vc5wJUd13kL
2026-10-16 13:44:50 +09:00

158 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =============================================================================
* M02_SO_Def_Spec_Model.ts
* 구조물 명세(PLAN 52 · `design_structure_spec`) 이름 고치기(52-11)의 자료 손질 — 화면 그리기 없음.
*
* · 명세 = 구조 정본(키 · 컨테이너 · 필수/선택 · 꼴) — 여기서는 **이름만** 고침
* · 이름 자리 = 정의 `종류.{종류}.이름`(키 `{컨테이너}` · `{컨테이너}.{항목}` · `{컨테이너}.{항목}={값}`)
* · 필수 컨테이너 제목 = 이름 표에 없으면 정의 그룹(`그룹.{종류}` 의 `id = link`) 이름 — B05 그리기
* (`containerTitle`)와 같은 차례로 읽고 그 자리에 씀
* · 명세 없는 종류(정의 그룹 꼴 · 52-12) = 그룹 제목(`그룹.이름`) › 칸 이름(옵션 `label`) ›
* 선택지 이름(이름 표 키 `{옵션}={값}` · 값은 그대로) — 그룹 밖 칸은 맨 앞 머리 줄 아래
* import 는 꼴만 — 시험이 이 파일만 옮겨 돌림.
* ========================================================================== */
import type { StructureSpec } from "../../A00_Common/design/logic/design_structure_spec";
import type { StructDef, TypeDef } from "./M02_SO_Def_Model";
export interface SpecNameRow {
/** 0 = 컨테이너 · 1 = 항목 · 2 = 값 */
level: 0 | 1 | 2;
/** 이름 표 키 — 빈 글 = 고칠 이름 없는 머리 줄(그룹 밖 칸) */
nameKey: string;
/** 읽기만 표시 — 컨테이너 id · 항목 id · 값 */
key: string;
/** 지금 이름 */
label: string;
/** 읽기만 표시 — 필수/선택 · 꼴 */
tag: "required" | "optional" | "select" | "number" | "auto" | "text" | "bool" | null;
/** 필수 컨테이너 제목이 정의 그룹 이름에서 옴 — 고치면 그 그룹 이름에 씀 */
group: string | null;
/** 정의 옵션 이름(`label`)에서 옴 — 고치면 그 옵션 이름에 씀 */
option: string | null;
}
/** 명세 차례대로 이름 줄 — 같은 이름 키(갈래마다 겹친 값)는 한 줄. */
export function specNameRows(def: StructDef, spec: StructureSpec): SpecNameRow[] {
const names = def.종류[spec.typeId]?.이름 ?? {};
const groups = def.그룹[spec.typeId] ?? [];
const rows: SpecNameRow[] = [];
const seen = new Set<string>();
const push = (row: SpecNameRow): void => {
if (seen.has(row.nameKey)) return;
seen.add(row.nameKey);
rows.push(row);
};
for (const c of spec.containers) {
const group =
c.required && names[c.id] === undefined ? groups.find((g) => g.id === c.link) : undefined;
push({
level: 0,
nameKey: c.id,
key: c.id,
label: names[c.id] ?? group?.이름 ?? c.title,
tag: c.required ? "required" : "optional",
group: group ? group.id : null,
option: null,
});
for (const item of c.items) {
const itemKey = `${c.id}.${item.id}`;
push({
level: 1,
nameKey: itemKey,
key: item.id,
label: names[itemKey] ?? item.label,
tag: item.kind,
group: null,
option: null,
});
const choices = [
...(item.choices ?? []),
...Object.values(item.by?.map ?? {}).flatMap((b) => b.choices ?? []),
];
for (const ch of choices) {
const choiceKey = `${itemKey}=${ch.value}`;
push({
level: 2,
nameKey: choiceKey,
key: ch.value,
label: names[choiceKey] ?? ch.label,
tag: null,
group: null,
option: null,
});
}
}
}
return rows;
}
/** 정의 그룹 꼴 종류(명세 없음) 이름 줄 — 그룹 밖 칸 · 그룹마다 제목 › 칸 › 선택지(B05 그리기 차례). */
export function groupNameRows(def: StructDef, type: TypeDef): SpecNameRow[] {
const names = def.종류[type.type_id]?.이름 ?? {};
const options = type.options.filter((o) => o.enabled !== false);
const groups = def.그룹[type.type_id] ?? [];
const inGroup = new Set(groups.flatMap((g) => g.옵션));
const rows: SpecNameRow[] = [];
const optionRows = (keys: readonly string[]): void => {
for (const o of options.filter((x) => keys.includes(x.key))) {
rows.push({
level: 1,
nameKey: o.key,
key: o.key,
label: o.label,
tag: o.input,
group: null,
option: o.key,
});
for (const ch of o.input === "select" ? (o.choices ?? []) : []) {
const nameKey = `${o.key}=${ch}`;
rows.push({
level: 2,
nameKey,
key: ch,
label: names[nameKey] ?? ch,
tag: null,
group: null,
option: null,
});
}
}
};
const loose = options.filter((o) => !inGroup.has(o.key)).map((o) => o.key);
if (loose.length) {
rows.push({ level: 0, nameKey: "", key: "", label: "", tag: null, group: null, option: null });
optionRows(loose);
}
for (const g of groups) {
rows.push({
level: 0,
nameKey: `#${g.id}`,
key: g.id,
label: g.이름,
tag: null,
group: g.id,
option: null,
});
optionRows(g.옵션);
}
return rows;
}
/** 이름 하나를 사본에 씀 — 그룹에서 온 제목은 그 그룹 이름 · 옵션 칸은 옵션 이름 · 나머지는 이름 표. */
export function setSpecName(def: StructDef, typeId: string, row: SpecNameRow, value: string): void {
const option = row.option
? def.types.find((t) => t.type_id === typeId)?.options.find((o) => o.key === row.option)
: undefined;
if (option) {
option.label = value;
return;
}
const group = row.group ? def.그룹[typeId]?.find((g) => g.id === row.group) : undefined;
if (group) {
group.이름 = value;
return;
}
const kind = (def.종류[typeId] ??= {});
(kind.이름 ??= {})[row.nameKey] = value;
}