sync: 4환경 수렴본 main 반영 (커밋 34건) #10

Merged
eomsangdon merged 34 commits from main_laptop_1 into main 2026-09-02 17:24:19 +09:00
34 Commits
Author SHA1 Message Date
eomsangdon fa0624a7df Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-02 16:52:45 +09:00
eomsangdon e7b0d62060 Merge remote-tracking branch 'origin/main_desktop_1' into main_laptop_1 2026-09-02 16:52:43 +09:00
eomsangdon 9b1c816992 auto: 2026-09-02 16:52 (ESD_LAPTOP) 2026-09-02 16:52:38 +09:00
eomsangdon 397a135073 auto: 2026-09-02 16:48 (EOMSANGDON-HOME) 2026-09-02 16:48:22 +09:00
eomsangdon d93bd1cba8 refactor(B05): 페이지 버튼 동작 3종 분리 (819→679줄)
- `B05_Profile_UI_Page_Actions.ts`(216줄) 신설 — [경로 계산]·[임시저장]·[초기화].
  페이지는 상태 창구(`PageActionContext`)만 넘기고 화면 조립·로딩 순서만 맡음.
- 화면 검증: B05 [저장] 실제 클릭 — `PUT /drainage/pipe-points` 200 →
  `POST /route/confirm?mark_stage_complete=false` 200 → `GET /route/latest` 200 →
  `PUT /routes/111/corridor` 200 순서 그대로, 콘솔 오류 0.
  (앞서 분리한 Lifecycle 라우터의 `/route/confirm` 도 이 경로로 함께 확인됨.)
- `tsc --noEmit` 0, prettier 적용, pytest 359 passed.
2026-09-02 16:48:01 +09:00
eomsangdon 6809f8a148 refactor(B06): 측점 제어기 세션 맵 통합·구간값 제어 분리 (843→643줄)
- `createSessionMap` 을 `_Page_Link_Session.ts` 에 추가 — 반폭·기슭막이 4축·유입
  형식·집수정·추가 벽 수·단별 구간값 6쌍이 같은 모양(읽기 → 항목 검증 → 담기 /
  통째 직렬화)이라 제어기마다 두던 load/persist 짝을 하나로 모음. 항목 검증과
  구형식 호환(숫자 x·d=0)은 `accept` 로 넘어감.
- `B06_Section_UI_Page_Span_Control.ts`(130줄) 신설 — 다단 단별 구간값과 유입·유출·
  집수정 구간값 제어를 세션·캐시·정본 큐와 함께 한 덩어리로 이동.
- 화면 검증: B06 카드 렌더 후 콘솔 오류 0, 조정창 DOM(구간 69·단 23) 그대로.
  표시 반폭 − 조작으로 세션 `b06:crossw` 에 13 기록, ⤢ 로 12 복귀 후 키 제거.
- `tsc --noEmit` 0, prettier 적용, pytest 359 passed.
2026-09-02 16:43:57 +09:00
eomsangdon cc2c788122 refactor(B06): 횡단 페이지 저장 흐름·관 옵션 반영 분리 (926→680줄)
- `B06_Section_UI_Page_Persist.ts`(231줄) 신설 — 암 경계선 세션 저장소
  (`createRockBoundaryStore`), 편집분 수집, [저장]·[확정].
- `B06_Section_UI_Page_Pipe_Options.ts`(130줄) 신설 — 폼에서 바꾼 관 옵션을
  횡단 캐시·조정창 제어기에 얹는 경로.
- 페이지는 상태 창구(`SectionPersistContext`·`PipeOptionsContext`)만 넘김.
- 화면 검증: B06 진입 카드 280장·암 버튼 69개·콘솔 오류 0. 암 경계선 ▲ 조작으로
  표시 -0.5m → -0.4m + 세션키 기록, ↺ 로 -0.5m 복귀. [저장] 클릭 시
  `POST /sections/111/save` 200.
- `tsc --noEmit` 0, prettier 적용, pytest 359 passed.
2026-09-02 16:37:48 +09:00
eomsangdon b4c7a7eedb auto: 2026-09-02 16:31 (EOMSANGDON-HOME) 2026-09-02 16:31:23 +09:00
eomsangdon 4e074dcfbe refactor(B05): 3D 뷰어 포인터 입력 분리 (803→678줄)
- `B05_Profile_UI_Viewer_Marker_Input.ts`(196줄) 신설 — 지형 지점 픽킹, 마커 끌기·
  고르기, 끌어놓기(drop), 리스너 등록·해제를 `bindMarkerPointerControls` 로 묶음.
- 뷰어는 `markerInput.blocked()`(회전 중심 양보 판정)·`terrainPointAt`(공개 API
  `modelPointAt`)·`beginMoveSelected` 만 받아 쓰고, dispose 에서 함께 해제.
- 화면 검증: 공용 브라우저에서 BP 마커를 실제 클릭 — 선택 배율 1 → 1.35,
  빈 지형 클릭으로 해제 1.0 복귀. 마커군 명중 시 이벤트 전파 차단도 유지됨.
- `tsc --noEmit` 0, prettier 통과, pytest 359 passed.
2026-09-02 16:30:59 +09:00
eomsangdon 3a161aa18d refactor(B05): 경로 확정·초기화 엔드포인트 분리 (722→491줄)
- `B05_Profile_Router_Lifecycle.py`(256줄) 신설 — `/route/confirm` · `/route/reset`
  두 엔드포인트를 자체 APIRouter 로 옮김. URL·응답 스키마 불변.
- `main.py` 가 새 라우터를 함께 등록 (`b05_route_lifecycle_router`).
- 자동설계 체인이 쓰던 `B05_Profile_Router.confirm_latest_route` 경로는 재수출로 유지.
- 검증: 라우트 8개 그대로(openapi.json 실측), 백엔드 재기동 후 200, pytest 359 passed.
2026-09-02 16:20:17 +09:00
eomsangdon acdd2cef4e refactor(B05): 능선-계곡 길찾기 그래프 뼈대 분리 (787→552줄)
- `B05_Profile_Engine_RidgeValley_Graph.py`(257줄) 신설 — 격자 조회(`_Grid`),
  노드 수집, 구획 마스크, 정속경사 세그먼트 판정, 엣지·연결 후보, 교각 계산.
- 원본에는 탐색(`_search_segment`)·fillet 선형·진입점 계산만 남김.
- 엣지 후보 상수(MAX_EDGE_LEN_M 등)도 함께 이동 — 쓰는 쪽이 전부 새 모듈.
- 외부 호출부 불변(`solve_ridge_valley_route` 경로 동일), pytest 359 passed.
2026-09-02 16:15:25 +09:00
eomsangdon fc59cef976 feat(B05): 3D 화면 검증용 카메라 이동 창구 추가
- `__corridorScene.lookAt(x, y, z, distance, view)` — 모델 좌표 한 점으로 카메라를
  바로 보냄. 측점 확인을 마우스 휠·드래그로 하면 한 번에 20~30초가 걸리고
  우클릭이 구조물 메뉴를 여는 문제 해소.
- `fit()` 의 카메라 배치를 `placeCamera()` 로 공용화 — 뷰 4종(iso/top/front/side)
  위치·근평면·직교 반높이 계산이 한 곳.
2026-09-02 16:10:29 +09:00
eomsangdon 1250cabe71 fix(B07): CAD 그림 그리기의 y 뒤집기 누락 수정 — 도각 로고·서명이 화면에 안 보이던 원인
- screenCanvas.drawController.drawImage 가 worldToTarget 결과를 그대로 써서 그림이
  거울상 자리(캔버스 밖)에 놓였음. 선·글자처럼 canvasSize.y - y 로 뒤집고 회전 부호도 반대로
- vitest 추가: 그림 중심의 뒤집힌 y 자리, 회전 부호·원상 복구
- 검증(공용 브라우저, 종단면도): 로고 상자 비배경 픽셀 0 → 14708(파랑 11661),
  서명 상자 498 → 2810(빨강 852). 뷰는 187% 로 정확히 복귀
2026-09-02 12:24:26 +09:00
eomsangdonandClaude Opus 5 cb06e40f9c fix(B05): 잘린 자리 윤곽의 시작 모서리 유실 수정 — dropCollinear 닫힘점 처리
- `_Corridor_Cut.ts` `dropCollinear`: 추적 고리의 닫힘 중복점(머리=꼬리)을 떼고 순환
  다듬기 뒤 다시 붙임. 그대로 두면 머리 점의 이웃이 자기 자신이 되어 `away=0` 으로
  지워지고 양옆 일직선 점까지 빠져 8고리 전부 시작 모서리가 대각선으로 잘림(63셀 구멍).
- 공선 판정의 앞점을 마지막으로 남긴 점 기준으로 변경 — 완만한 곡선에서 누적 편차로
  현이 곡선 안쪽으로 파고들던 2셀 제거(`_Region.ts` `simplifyLoop` 과 같은 규칙).
- `BUILD_VERSION` 89→90 으로 저장본 만료.
- 실측: 되살릴 영역 미포함 셀 63→0(우 7886/7886·좌 7023/7023), 8고리 닫힘,
  `tmp/tests/test_b05_cut_merged_loop.py` 3건 포함 359 passed.
- PLAN.md 해당 항목 완료 표시 + 자체검증 기록(검증 대기).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 12:06:54 +09:00
eomsangdonandClaude Opus 5 00d30587b3 feat(B01,B07): 담당자 회사 구성원 지정 + 로고·서명 회사 공유 자산 모달
- B01 프로젝트 수정: 과업책임자·분야별책임자·설계자를 회사 구성원 목록(select)에서
  지정. 회사 로고·설계자 서명 칸은 누르면 등록 목록 모달(선택·삭제·신규 추가 업로드).
- 백엔드: `company_assets`(013) CRUD 라우트 신설 — 목록·업로드(Form+File, 2MB·확장자
  검사)·수정·소프트 삭제·파일 서빙. `_scope_company` 로 SYSTEM_ADMIN 만 타 회사 지정,
  `_check_project_refs` 로 담당자·자산이 같은 회사·같은 종류인지 검사(400).
- projects 조회·수정에 pm/field_lead/designer/logo_asset/signature_asset 5칸 합류.
  중복 3곳을 `_project_rows` 로 묶어 700줄 제한 유지.
- B07 표제란: 로고·서명 출처를 `companies.logo_path`/`users.signature_path` 에서
  프로젝트가 고른 `company_assets` 로 전환(삭제된 자산은 그림째 제외).
- 검증: pytest 143 통과(신규 4), tsc·ruff·prettier 통과. 공용 브라우저 사용자 경로
  (업로드→선택→저장→재열기) 및 종단도 API 표제란 글자 4건·그림 2건 수치 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 12:02:49 +09:00
eomsangdon 0ea0fe2ba8 Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-02 11:42:11 +09:00
eomsangdonandClaude Opus 5 2d07b990a4 feat(B05): 3D 평면 스케치를 최종 결과물에서 숨긴다 (디버깅용으로 되켤 수 있게)
투영 커브(구조물 노랑·날개 주황·투영선 빨강·잘린 자리 라임 등)는 구조물이 어디를
지우고 어디를 다시 채우는지 눈으로 좇으려고 만든 표기라 설계 산출물이 아니다.
사용자 지시로 **기본은 숨김**. 다만 절취·패치 기하를 다시 볼 때 쓰므로 지우지 않고
묶음째 감춘다(2026-09-02 사용자: "나중에 디버깅을 위해 재사용 가능성 있음").

- `_Corridor_Mesh.ts` — 커브를 `corridor-plan-sketch` 묶음에 담고 `visible` 을
  브라우저 스위치(`frd_debug_plan_curves`, 기본 꺼짐)로 정한다.
- `_Profile_UI_Viewer.ts` — `__corridorPlanCurves(true|false)` 로 켜고 끈다. 선택은
  브라우저에 남아 다음에 열 때도 유지된다. 인자 없이 부르면 지금 상태를 돌려준다.

검증(wdw · 3D 실측, 같은 화면에서 켜고 끄며 스케치 색 픽셀을 셈):
  숨김(기본) — 주황 1 · 노랑 0 · 빨강 0 · 라임 0
  켬        — 주황 89 · 노랑 260 · 빨강 6 · 라임 195
`__corridorPlanCurves()` 기본 false → true → false 왕복 확인.
`tsc --noEmit` 통과, `pytest tmp/tests/ -q` 356 passed / 0 failed, prettier 정합.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 11:41:41 +09:00
eomsangdonandClaude Opus 5 313a7dde43 feat(B01,B07): 설계일자 사용자 지정 입력 + 도면번호 자동 부여
사용자 확정(2026-09-02): 설계일자는 확정일 자동이 아니라 **사용자가 넣는다**. 도면번호는
Claude 판단으로 목록 순번을 쓴다.

설계일자
- `013_company_assets.sql` 로 `projects.design_date DATE` 추가(같은 마이그레이션에
  로고·서명 공유 테이블 자리도 함께 만들었음 — 배선은 다음 커밋).
- B01 프로젝트 수정 모달에 날짜 칸. 공용 `createInputField` 가 `type="date"` 를 받게 넓힘.
- 표제란에는 도면 표기 관행대로 `2026. 09. 02.` 꼴로 적는다.

도면번호
- 단건 조회는 목록 순서를 모른다 — 알려면 횡단 장 계획을 다시 계산해야 하고 도면을 열
  때마다 그러면 비싸다. **목록이 매긴 번호를 manifest 에 적어 두고** 단건 조회가 읽는다.
  목록은 화면 진입 때 늘 먼저 뜨므로 성립하고, 목록이 바뀌면 다음 조회에서 다시 적힌다.
- `add_title_fields()` 신설 — 도면을 읽는 도중에 아는 값을 문맥에 얹는 창구.

검증: `pytest tmp/tests/ -q` 139 passed / 0 failed, 루트 `tsc --noEmit` 통과.
실사용자 경로(5174, wdw) — 대시보드 [수정] 모달 라벨 10개(설계일자 칸 추가 확인) →
`2026-09-02` 입력 → [확인] → 종단면도 표제란에 `2026. 09. 02.`.
도면번호는 목록 13건 기준 종단 1 · 유역도 6 · 용지도 13 이 도각에 그대로 실림
(표지는 도각을 두르지 않아 번호 칸이 없다).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 11:38:50 +09:00
eomsangdonandClaude Opus 5 7498d6a2ec refactor(B06): API 타입 선언을 별도 파일로 분리 (700줄 제한)
`B06_Section_Api_Fetch.ts` 704줄 중 486줄이 타입 선언이었다. 타입을
`B06_Section_Api_Types.ts` 로 옮기고 `_Api_Fetch` 는 요청 함수만 남긴다.

호출 코드는 손대지 않았다 — `_Api_Fetch` 가 `export type *` 로 그대로 다시 내보내므로
기존 import 경로가 전부 유효하다. 계약이 아니라 파일만 나눈 것이다.

결과: 704줄 → 236줄(요청 함수) + 492줄(타입). 둘 다 700줄 이내.
검증: `tsc --noEmit` 통과, `pytest tmp/tests/ -q` 356 passed / 0 failed, prettier 정합.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 11:32:51 +09:00
eomsangdonandClaude Opus 5 dc91a5e831 fix(B05): BOX암거 패치도 절취 영역 안쪽으로 자른다
패치 리본을 절취 영역 안쪽만 남기는 규칙(`patchClip`)이 세월교에만 걸려 있었다.
`_Corridor_Cut.ts` 가 표식 없는 패치는 손대지 않고 그대로 돌려주므로 BOX암거 패치는
한 번도 안 잘려 절취 영역 밖으로 넘쳤다. BOX도 `wing-box` 커브로 절취 영역을 내므로
같은 반대 규칙이 성립한다. 기슭막이(배수관 세트)는 날개 사다리꼴이 없어 제외.

기하가 바뀌므로 `BUILD_VERSION` 88 → 89 (형식은 안 바뀌어 `ENVELOPE_VERSION` 은 그대로).

검증(wdw · 저장본 실측):
- 수정 전 — BOX 패치 리본 두 벌(198.4~203.5m)에 셀 마스크 없음. `patchClip` 3개.
- 수정 후 — 두 벌 모두 마스크 생김(남은 셀 46/282 · 43/282). `patchClip` 5개.
- 3D 화면(TOP, 지형 끔) — BOX 자리 성토면이 구조물에 맞물려 끊기고, 넘침도 구멍도 없음.
- `tsc --noEmit` 통과, `pytest tmp/tests/ -q` 356 passed / 0 failed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 11:27:25 +09:00
eomsangdon a218d08589 Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-02 10:51:04 +09:00
eomsangdonandClaude Opus 5 475fb73505 fix(B05): 코리도 저장본이 패치 리본 표식을 잃던 것 + 원복된 트림 도구 제거
- 저장본 왕복에서 `patch`·`patchClip` 두 표식이 사라졌음. 직렬화 대상에 없어
  저장본으로 다시 연 코리도는 패치인 줄 몰랐고, 뷰어의 지형 스냅 제외 가드
  (`B05_Profile_UI_Viewer.ts` `if (ribbon.patch) return;`)가 풀려 패치 리본
  바깥 끝이 원지반까지 끌려 내려갔음(2026-08-27 실측 세로 지느러미 4.1m).
  두 표식을 `serialize`·`deserialize` 에 싣고 `ENVELOPE_VERSION` 을 6으로 올림 —
  `fetchStored` 가 버전이 다른 저장본을 버리므로 기존 v5 저장본은 다시 만들어짐.
- `_Corridor_Carve_Wing.ts` 의 「주황 바깥선 트림」 도구 4개(`WingTrimLine`·
  `wingOuterLines`·`wingTrimAt`·`clipRunToRange`)를 제거. 버전 76~78용인데 그 판이
  사용자 지시로 원복된 뒤 `BUILD_VERSION 88` 까지 다시 배선되지 않아 파일 밖 참조가
  0이었음. 이력은 `_Corridor.ts` 버전 주석에 남김.

검증: 저장본 3건 실측 — 전부 `version=5`, `patch`·`patchClip` 0건으로 결함 확인.
회귀 방지 `tmp/tests/test_b05_corridor_envelope_flags.py` 7건 신규(표식 왕복·버전 상향·
뷰어 가드 존속·선택 필드 대칭·죽은 심볼 부재·살아 있는 도구 존속).
`pytest tmp/tests/ -q` 356 passed / 17 skipped / 0 failed, `tsc --noEmit` 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 10:50:10 +09:00
eomsangdonandClaude Opus 5 56aed2f2aa feat(B01,B07): 표지 연도·기번·사업량을 사람이 넣는 자리 신설
표제란에 남아 있던 빈칸 중 연도·기번과 사업량은 프로그램이 지어낼 수 없는 값이라
입력 자리를 만든다. 값의 출처를 정하지 않고 받을 칸만 세운다.

DB (`012_title_block_inputs.sql`, ADD COLUMN·NULL 허용)
- `projects.project_number` — 표지 연도·기번(실무문서 폴더명 관행:
  `2024년 간선임도(기번3-울진.대흥)`)
- `projects.work_amount` — 표지 사업량. 단위·표기가 사업 종류마다 달라 자유 문자열.

B01 프로젝트 수정
- 수정 모달에 시행청·연도기번·사업량 3칸 추가. 011 로 만든 시행청도 여기서 처음 입력
  가능해짐(그전에는 스크립트로만 넣었음).
- 목록·단건 조회 4곳과 UPDATE 에 세 칸을 함께 실음. USER 권한은 다른 칸과 같이 잠금.

B07
- `_title_block_fields` 가 연도기번·사업량도 실어 보냄. 비어 있으면 종전대로 빈칸.

검증: `pytest tmp/tests/ -q` 139 passed / 0 failed(표지 값 1건 신규), 루트 `tsc --noEmit`
통과. 실사용자 경로 실측(5174) — 대시보드 wdw 행 [수정] 클릭 → 모달 라벨 9개에 새 3칸
확인 → 연도기번·사업량 입력 → [확인] 저장 → 표지 도면 Text 7개 **빈칸 0 · `{{` 잔존 0**,
화면에도 연도기번·`wdw 설계도`·위치·사업량·시행청이 모두 그려짐.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 10:28:25 +09:00
eomsangdonandClaude Opus 5 07926e3867 fix(B07): 빈 도면의 "작도 영역을 넘습니다" 경고 제거
빈 도면 7종이 도각만 두르는데도 경고를 냈음 — `build_blank_drawing()` 이 콘텐츠
bbox 로 작도 영역(`_A1_INNER`, 770×520mm)을 그대로 넘겼고, 검사 기준은 여백을 뺀
수용 한도(739×499mm)라 담을 내용이 없는데도 넘친 것으로 잡혔음.

- `usable_bbox()` 신설 — 작도 영역 한가운데의 수용 한도 크기 bbox. 중심이 같아
  도각 배치(이동량 0)는 그대로.
- 한도와 **같은** 크기는 넘친 것이 아니므로 비교에 부동소수 여유(1e-6)를 둠.
  한도를 그대로 넘기면 마지막 자리 오차로 경고가 났음.

검증: `pytest tmp/tests/ -q` 138 passed / 0 failed(경고 없음을 잠그는 1건 신규).
백엔드 재시작 뒤 화면 실측(5174, wdw): 빈 도면 7종 전부 200, 새 서버 로그의
"작도 영역을 넘습니다" **0건**.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 10:02:35 +09:00
eomsangdonandClaude Opus 5 63428f2516 feat(B07): 표제란 축척 칸을 도면 축척 상수에서 채움
축척은 지어내는 값이 아니라 이미 정해져 있던 것 — `config_system` 의 도면별 축척
상수를 그대로 표제란에 싣는다. 도각이 `A1 = 1 :` 를 이미 찍으므로 **분모만** 낸다.

- `scale_fields()` 신설 — A3 는 A1 도면을 절반으로 뽑는 종이라 분모가 2배. 가로·세로가
  다른 도면(종단·토적)은 이름표를 붙여 함께 적는다.
- 종단면도 `1000(H) · 200(V)` / 횡단면도 `100` / 유역도 `6000` / 토적도 `2000(H)`.
  A3 는 각각 2배. 담을 내용이 없는 빈 도면·표지는 축척이 없어 빈칸 그대로.

검증: `pytest tmp/tests/ -q` 135 passed / 0 failed(축척 2건 신규 — 상수 유래·A3 2배,
호출부가 넘긴 값이 도각에 실림). 백엔드 재시작 뒤 화면 실측(5174, wdw): 종단
`1000(H) · 200(V)`/`2000(H) · 400(V)`, 횡단 `100`/`200`, 유역도 `6000`/`12000`,
토적도 `2000(H)`/`4000(H)`, 표준도 빈칸.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 09:54:16 +09:00
eomsangdonandClaude Opus 5 a5fba04778 fix(B07): 그림 테두리는 집었을 때만 그림
도각의 로고·서명 자리에 흰 사각형이 남던 것 — `ImageEntity.draw()` 가 폴리곤 네 변을
선택·강조 여부와 무관하게 늘 그렸음. 출력·내보내기가 같은 `draw()` 를 다른
DrawController 로 타므로 화면만의 문제가 아니라 산출물에도 실렸음.

- 도각층만 특례로 두지 않고 `ImageEntity` 전체에서 뺌. 일반 CAD 관행대로 **집었을
  때만**(강조·선택) 테두리를 보임 — 앞으로 붙일 그림(현황사진 등)에도 같은 규칙.
- 자리 표시가 필요하면 도각 편집 화면에서 집으면 보임.

검증: `ImageEntity.test.ts` 신규 2건 — 안 집은 그림은 `drawLine` 0회·`drawImage` 1회,
집은 그림은 4회·1회. `npx vitest run` 89 passed / 0 failed, check-types·build 통과.
화면 실측(5174, wdw 표준도): 두 슬롯 사각형 안 흰 픽셀(RGB>200) **0**,
그림 픽셀은 그대로(로고 56 · 서명 106).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 08:50:02 +09:00
eomsangdonandClaude Opus 5 b1593bce83 feat(B07): 표제란에 로고·서명 자리 신설 + 시행청·담당자 DB 확장
사용자 지시(2026-09-02) — 로고·서명을 임의로 만들어 표제란에 넣고, 위치·크기는
도각을 고치면 따라오게 하며, 시행청·과업책임자 등은 DB를 넓혀 채울 것.

도각(표제란)
- `00_template_A1.json` 에 그림 자리 2개 추가 — 회사 로고는 용역회사 칸 왼쪽
  (316~348 × 20~36mm), 설계자 서명은 설계 칸 아래(638~680 × 17.5~25.5mm).
  자리·크기가 **템플릿 좌표로만** 정해지므로 도각을 고치면 그대로 따라옴.
- `_fill_placeholders` 가 그림 자리(`imageData`)의 `{{키}}` 도 채움. 값을 못 구하면
  빈 문자열을 남기지 않고 **엔티티째 제거** — 빈 값은 CAD 가 깨진 그림으로 그림.
- `_transform_entity` 가 `points` 배열도 옮김. 그림(로고·서명)과 띠(Hatch)가 이 키를
  쓰는데 여태 변환 대상이 아니라 도각을 옮기면 제자리에 남았음.

DB (`011_title_block.sql`, 전부 ADD COLUMN·NULL 허용)
- `projects` — `client_org`(시행청), `pm_user_id`·`field_lead_user_id`·`designer_user_id`
- `companies.logo_path` · `users.signature_path` — 그림은 파일로 두고 경로만 담음
  (기존 `storage_path`·`input_files` 와 같은 방식)
- FK 는 걸지 않음 — 사유를 파일 머리말에 적음(소프트 삭제·LEFT JOIN·4환경 공유).

배선
- `_title_block_fields` 가 시행청·과업책임자·분야별책임자·설계자(배정 없으면 소유자)와
  로고·서명 data URL 까지 실어 보냄.
- `read_stored_asset()` 신설 — `storage/` 기준 상대 경로의 **파일**을 읽음.
  `resolve_stored_project_path()` 는 폴더를 만드는 프로젝트 루트용이라 파일에 못 씀.

CAD 결함 1건 (이번 작업에서 드러남)
- `screenCanvas.drawController.drawImage` 가 `(width, height)` 를 좌표처럼 변환해
  화면 오프셋과 y 뒤집기가 섞여 들어갔음 — 그림이 제 자리를 벗어나고 비율이 무너짐.
  크기는 배율만 곱하고 자리는 세계 중심으로 잡도록 고침(SVG 컨트롤러와 같은 방식).
- 검증 창구 `__aisloCad.screen(x, y)` 추가 — 세계→화면 좌표. 그림·글자가 제 자리에
  그려졌는지 픽셀로 판정할 때 씀.

검증: `pytest tmp/tests/ -q` 135 passed / 0 failed(그림 자리 3건 신규),
`npx vitest run` 87 passed / 0 failed, `check-types`·`build` 통과.
화면 실측(5174, wdw): 표준도 API Text 24개 `{{` 잔존 0, Image 2개가 도각 좌표
(316,20)-(348,36)·(638,18)-(680,26)에 실림. 캔버스 픽셀 판정 — 두 자리 모두 배경색
외 픽셀이 그려짐(로고 88px·서명 64px), 로고 파랑(20,70,140)이 슬롯 안에서만 검출.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 08:35:25 +09:00
eomsangdonandClaude Opus 5 23fea9846b fix(B07): 표지도 표제란 값을 받게 함
교차검증에서 표지만 빈칸으로 나갔음 — 문맥값을 합치는 자리가 `frame_entities` 안에
있어, 도각을 두르지 않는 표지 경로(`build_cover_drawing`)가 비껴갔음.

- 합치는 자리를 치환 함수 `_fill_placeholders` 한 곳으로 옮김. 도각을 쓰든 안 쓰든
  `{{키}}` 를 치환하는 모든 경로가 같은 값을 받음 — 앞으로 생길 경로도 자동으로 닿음.
- 표지 docstring 의 "값 공급은 다음 판 몫" 문구 정정.

검증: `tmp/tests/test_b07_title_block_fields.py` 에 표지 케이스 추가(공사명·위치가
표지에 실리고 `{{` 잔존 0). `pytest tmp/tests/ -q` 132 passed / 0 failed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 08:11:56 +09:00
eomsangdonandClaude Opus 5 793e1aad37 fix(B07): 표제란 값 공급 통로 + CAD 회귀 6건 해소
표제란(도각) 값 공급
- `use_title_fields()` 문맥 변수 신설 — 회사 도각 폴더와 같은 방식이라 작도 엔진
  6개의 서명을 고치지 않음. `frame_entities` 가 문맥값 위에 호출부 값(도면명)을 얹음.
- 라우터가 도면 한 건을 그리기 전에 DB 값을 세움: 공사명·위치(projects),
  용역회사(companies), 설계자(users). 못 채운 자리는 빈칸 — 도각 원본의 남의 값이
  도면으로 나가지 않음.
- 미공급 항목과 사유를 `_title_block_fields` 주석에 명시: 시행청·과업책임자·
  분야별책임자는 DB 칸 부재(B02 마이그레이션 대기), 축척·사업량·연도기번은 임의 수치
  금지, 설계일자는 채울 시점 정의 미결.

CAD 회귀 6건 (87건 중 81 passed → 87 passed)
- `window` 부재 5건: `notifyWindow()` 한 곳으로 모으고 `window` 없는 Node 시험에서는
  통지를 건너뜀. `triggerReactUpdate` 가 이미 같은 이유로 시험을 건너뛰던 것과 같은 결.
- `find-closest-entity`: 목의 호가 90°까지만 돌아 클릭점에서 147px 떨어져 있었음 —
  시험 의도(호가 가장 가깝다)대로 3/4바퀴로 되돌림.
- 선색 기대값 4건: 앱 기본 선색이 흰 종이 배경에 맞춰 검정으로 바뀐 뒤 상류 기대값
  `#fff` 가 남아 있었음 — `getActiveLineColor()` 대조로 교체.
- 좌표 NaN: 없어진 `TOOLBAR_WIDTH` 를 시험 보조가 아직 참조 — 입력 컨트롤러가 캔버스
  bounding rect 를 쓰도록 바뀐 현행에 맞춤.
- 호 각도 비교: 같은 각의 음수·2π 표기 차이라 [0, 2π) 정규화 후 대조.

검증: `npx vitest run` 87 passed / 0 failed, `npm run check-types`·`npm run build` 통과,
`pytest tmp/tests/ -q` 131 passed / 0 failed(신규 `test_b07_title_block_fields.py` 4건 포함).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 08:00:39 +09:00
eomsangdonandClaude Opus 5 4cb9b15939 style: 저장소 전체 포맷터 일괄 적용 (prettier·biome·ruff)
파일마다 포맷 폭이 달라(≈80 대 100) 한 줄만 고쳐도 포맷터가 무관한 줄을 대량
재포맷했음. 사용자 지시로 전체를 한 번에 맞춤. 코드 동작 변경 없음 — 포맷만.

- 프론트엔드 `.ts/.css/.html` → 저장소 prettier (`.prettierrc`, printWidth 100)
- `B07_DesignDetail/openwebcad/**` → 자체 biome (tab 들여쓰기·single quote·lineWidth 100).
  `biome format` 만 사용 — `biome lint --write` 는 포맷 아닌 코드 수정까지 하므로 제외
- 파이썬 → `ruff format` (엔진 코드는 이미 정합, resources·scratch 스크립트 24개만 변경)

두 포맷터가 서로 되돌리지 않도록 `.prettierignore` 신규 — openwebcad 와 빌드·산출물
폴더를 prettier 대상에서 뺌. `.prettierrc` 에 `endOfLine: "auto"` 추가 — 기본값 `lf` 가
`core.autocrlf=true` 로 받은 CRLF 파일을 매번 전부 다시 써서 `--list-different` 가
실제 포맷 차이를 가리고 있었음.

검증: `tsc --noEmit` 통과(루트·openwebcad 둘 다), pytest 349 passed / 17 skipped /
0 failed, CAD vitest 87건 중 81 passed / 6 failed(laptop-sub 기준선과 동일, 회귀 없음).
포맷터 재실행 시 prettier·biome 모두 변경 0건.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 07:08:24 +09:00
eomsangdon 43f23c38ef Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1 2026-09-02 06:44:11 +09:00
eomsangdonandClaude Opus 5 a321f0f9a6 fix(B04,B05): 3D 화면맞춤 노선 포함·계획선 출처 basis 분리
- B04 지표면 3D 뷰어가 지표면 범위만으로 카메라 거리를 정해, 라이다가 노선의
  일부만 덮으면 나머지가 화면 밖으로 잘렸음. 카메라 타깃이 늘 지표면 중심이라
  한쪽만 넓혀서는 소용이 없어, 중심에서 가장 먼 노선 정점까지를 반폭으로 잡아
  대칭으로 넓힌 `fitBounds()` 를 `fitCamera()` 가 씀. 노선이 없으면 종전 동작 유지.
- 계획선 1차(배관 정착)와 2차(직선 분할 폴백)가 같은 `_profile_entry()` 를 써서
  저장본 `basis` 가 둘 다 `station_alignment` 로 나갔음. `PIPE_ANCHORED_BASIS`
  신규 + `_profile_entry(basis=)` 인자로 1차만 값을 가름. 2차는 종전 값 유지,
  편집 재구성은 저장본 값 승계.

검증: 용화 프로젝트 공용 브라우저 실측 — 노선색 픽셀 x 범위 48~399(우측 끝에 닿음)
→ 145~386(양끝 캔버스 안). pytest 349 passed / 17 skipped / 0 failed, tsc 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 06:44:05 +09:00
eomsangdonandClaude Opus 5 946d61f8df feat(B07): GROUP 묶음 JSON 저장·복원, 줌 로그 제거
- `groupId` 를 도면 JSON 에 실어 [저장] 뒤 다시 연 도면에서도 묶음 유지.
  엔티티 13종의 `toJson` 대신 직렬화 한 곳(export/import)에서 붙이고,
  복원은 순번이 아니라 id 로 — `compact` 가 null 을 걷어 index 가 어긋남.
- `setScreenScale` 의 매 호출 `console.log` 제거 — 줌·팬마다 콘솔을 덮었음.
- 검증: `group-id-roundtrip.test.ts` 신규, `npx vitest run` 87건 중 81 passed,
  실패 6건은 작업 전과 동일한 기존 결함. `npm run build` 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 06:33:19 +09:00
eomsangdonandClaude Opus 5 019a742948 feat(config): vite 개발 포트를 환경변수로 분리
보조 워크트리가 `npm run dev` 맨손 실행에서 메인의 5173 을 뺏던 문제.
`server.port` 를 `FRONTEND_DEV_PORT` 로 읽고 기본값 5173 유지 — main.py 의
CLI `--port` 가 덮는 경로는 그대로임.

포트 판별은 `.claude/dev_up.py`(git 밖)가 담당 — 링크 워크트리는 `.git` 이
파일이라 그것으로 메인(8000·5173)·보조(8001·5174)를 가르고, 자식에
SERVER_PORT·FRONTEND_DEV_PORT·AISLO_API_PORT·AISLO_VITE_PORT 4개 주입.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 21:38:40 +09:00