- 편집 띠를 CAD 위 절대배치에서 도면 목록 하단 액션 칸으로 옮김 —
1행 편집 띠, 2행 [도각 편집]·[현재 도면 확정] (리본과 겹쳐 문구·버튼이 찌그러지던 문제)
- `drawImage` 가 'broken' 상태 그림에서 던지던 예외로 렌더 루프가 끊겨
이후 모든 도면이 백지로 남던 결함 해소 — 못 읽은 그림은 건너뜀
- `ImageEntity` 가 자리표시자(`{{회사로고}}`)를 절대 URL 로 굳히던 저장 경로 수정 —
원본 주소를 보관해 그대로 돌려주고, 자리표시자는 요청조차 하지 않음
- 버튼 줌(ZOOMIN·ZOOMOUT)이 배율만 바꿔 도면이 화면 밖으로 밀리던 것을
화면 중심 고정으로 수정
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- screenCanvas.drawController.drawImage 가 worldToTarget 결과를 그대로 써서 그림이
거울상 자리(캔버스 밖)에 놓였음. 선·글자처럼 canvasSize.y - y 로 뒤집고 회전 부호도 반대로
- vitest 추가: 그림 중심의 뒤집힌 y 자리, 회전 부호·원상 복구
- 검증(공용 브라우저, 종단면도): 로고 상자 비배경 픽셀 0 → 14708(파랑 11661),
서명 상자 498 → 2810(빨강 852). 뷰는 187% 로 정확히 복귀
사용자 지시(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>
파일마다 포맷 폭이 달라(≈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>
- `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>
잠금 필터가 공용 함수 없이 호출부마다 들어가 있어 넣은 곳은 막히고 안 넣은
곳은 샜다. Ctrl+A는 도각 103개·원지반 16개까지 전부 선택했고, 선택 삭제에는
잠금 검사가 없어 Ctrl+A → Delete 한 번에 도각이 사라졌다. 자르기와 트림·모따기
계열, 커서 강조도 도각을 집었다.
state.ts에 getPickableEntities()(잠금 도면층 제외)를 두고 집기 경로가 그걸
쓴다. 선택은 setSelectedEntityIds() 한 곳에서 막아, 앞으로 생길 선택 경로도
자동으로 잠금 객체를 담지 못한다.
화면맞춤(zoomToFitScreen)도 같은 목록에 맞춘다 — 도각까지 넣으면 A1 한 장
전체가 잡혀 화면의 40%가 여백으로 갔다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
화면 좌표는 아래에서 위로 재는 규약인데(getCanvasPoint·drawLineScreen), 화면 사각형
채우기만 캔버스 y로 옮길 때 높이를 빼지 않았다. 그래서 아래 변에 놓아야 할 사각형이
높이만큼 아래로 내려가, 선택 그립이 선보다 8px 밑에 찍혔다. 화면 픽셀 단위 어긋남이라
줌을 해도 간격이 그대로였다.
커서 옆 입력칸은 같은 함수를 쓰므로 자리가 유지되도록 높이만큼 내려 잡았다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
표가 DXF의 표로 나가야 한다(사용자 확정). 지금까지 도면의 표는 선과 문자 뭉치라
내보낼 때 고를 수 있는 길이 하나뿐이었다.
- TableEntity: 열별 폭·행별 높이·칸 문자·병합을 한 객체가 들고 있다. 격자선은 담지
않고 병합 자리에서 선을 끊는 규칙을 표가 스스로 안다(helpers/table-geometry.ts).
회전·대칭은 지원하지 않는다 — 표는 축에 붙어 있다.
- 명령: TABLE을 표 객체 생성으로 다시 쓰고 TABLEEDIT(칸 문자)·TABLEROW·TABLECOL·
TABLEMERGE·TABLEUNMERGE를 더했다. EXPLODE는 표를 선과 문자로 흩는다.
- 그립: 좌측 상단으로 표를 옮기고, 열·행 경계로 폭·높이를 바꾼다.
- 백엔드: 유역 정보표와 횡단 수량 산출표를 표 객체로 낸다. 횡단표는 머리행이 폭
8등분, 본문이 11열 가중치로 격자가 서로 달라 두 경계를 합친 18열로 만들고 병합으로
원래 칸을 되살렸다 — 손으로 하던 가로선 끊기가 사라졌다.
- 수량 역추출: 값 Text의 결정적 id로 읽던 것을 칸에 실은 key로 읽도록 옮겼다. 이미
저장된 도면을 위해 옛 방식을 폴백으로 남겼다.
토적도·종단표는 값이 칸이 아니라 측점 위치에 놓이는 성격이라 이관하지 않았다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
조사표 8~13절 검토에서 "기본 기능"으로 고른 것을 반영한다.
- 좌표 입력: 절대·상대 좌표가 양수만 받아 `@-100,50`을 거부했다. 부호를 허용하고
극좌표 `@거리<각도`·`거리<각도`를 더했다. `-`·`+`가 확대·축소 단축키로 먼저
잡혀 음수의 첫 글자를 먹고 있어 그 두 단축키를 뺐다(줌은 휠·뷰 막대·명령).
- 그립 편집: 선택 객체에 그립을 그리고 집어서 옮긴다. 선 끝점·중점, 폴리선 정점,
사각형 모서리, 원 중심·반지름, 문자·점 기준점. 폴리선은 세그먼트 중점을 끌면
정점이 늘고 정점 위 Ctrl+클릭이면 준다. 형상 필드가 private이라 공개 생성자로
다시 만들어 바꿔 끼우고 id·도면층·색·그룹을 물려받는다.
- 자동 백업·복구: 5초 디바운스로 복구 전용 키에 저장하고, 시작할 때 백업이 있으면
눌러서 되살리는 안내를 띄운다. 저장(QSAVE)에 성공하면 백업을 지운다.
- 선택 순환: 같은 자리를 다시 클릭하면 겹친 후보를 차례로 돌린다.
- 상태막대: `극좌표 추적` 버튼이 `직교`와 같은 onClick이라 같은 일을 하고 있었다.
각각 45°·90°를 켜고 끄도록 고치고, 스냅 추적 토글과 F3·F7·F8·F10을 붙였다.
- 문자 굵게·기울임을 캔버스·SVG·JSON·스타일 패널에 연결했다.
- 조사표: 이미 되어 있던 5건의 표기를 정정하고, 출력·내보내기(9절)는 결재창 이후
PDF·DXF·DWG로 반영할 것이라 보류(P)로 구분해 사유를 남겼다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
저장소 사고로 잃은 4개 커밋(38eb7cd4·b8a11756·281528bf·f60b488d)의 작업물을
하나로 다시 담았다. 내용은 동일하다.
■ 구조
- commands/: 명령 정의(id·AutoCAD 별칭·글리프·도구/즉시실행)를 단일 소스로 두고
리본·명령행·단축키가 모두 이 레지스트리를 읽는다. tools/tool.consts.ts 폐지.
- ribbon/: 탭→패널→명령 데이터(ribbon.config.ts)와 범용 렌더러 분리.
AutoCAD 배치(홈·삽입·주석·뷰·출력)와 패널 확장(▾)을 따른다.
- tools/factories/sequence-tool.ts: 점·숫자·문자·객체·선택 단계를 선언하면
xstate 머신을 만들어 주는 공장. 명령당 170줄 보일러플레이트 제거.
- helpers/geometry/: 3점 호·정다각형·타원·스플라인·구름형·평행이동·해치 스캔선·
점렬 샘플링 등 상태 없는 순수 함수.
- Toolbar 483줄을 QuickAccessBar/Ribbon/InspectorPanel/StatusBar/CommandLine/
ViewControls/PropertiesEditor/QuickProperties로 분해.
■ 명령 (조사표 기준)
- 1절 그리기 21건, 2절 수정 27건, 3절 도면층·특성·그룹·유틸리티 39건 전부 반영.
- 5절 주석 34건 중 26건 반영(문자·치수 16종·지시선·표·구름형·주석 축척).
- HatchEntity 추가(solid·pattern·cross·gradient) + JSON 왕복, Layer에 색·선가중치·
선종류·동결·투명도 필드 추가, 그리기 루프가 동결·숨김·투명도를 반영.
■ 화면 실측에서 고친 결함
- 명령행 포커스 상태에서 ENTER가 도구로 가지 않던 문제
- 명령행 문자 입력이 접두사가 같은 명령으로 실행되던 문제
- 명령이 끝나도 입력을 계속 먹던 문제(점 입력 명령만 반복, 나머지는 선택 도구 복귀)
- 시퀀스 단계 인덱스 오사용 9건 + 단계 값 종류 검사 추가
- 해치 내부를 클릭해도 선택되지 않던 문제
미반영 8건(맞춤법 검사·꺾기 치수·치수 끊기/재연관/검사 치수·기하공차·지시선 수집·
축척 리스트 편집)은 조사표 `반영` 열과 PLAN.md에 사유를 적었다.
확대가 이벤트 1회당 10% 고정이라 트랙패드처럼 잔 델타를 초당 수십 번 보내는
입력에서 튀었고, 두 손가락 이동은 팬이 아니라 확대로 먹혔다.
- zoomScreen을 부호 기반에서 델타 비례 지수식 exp(-delta*지수)로 변경.
마우스 휠 한 칸(deltaY 100)은 종전과 같은 10%, 트랙패드 잔 델타는 그만큼만
- 트랙패드 두 손가락 이동은 panScreen으로 — 마우스 휠은 한 칸이 100/120px로
딱 떨어지고 가로 델타가 없다는 성질로 기기를 가른다
- 핀치(ctrl+휠)는 기기 무관 확대, 전용 지수로 천천히
- deltaMode(줄·쪽 단위) 픽셀 환산 추가
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
iframe 안 CAD가 다크 색을 하드코딩하고 있어 앱의 화이트/블랙 토글과 따로 놀던
것을 고친다. 색 정의처는 앱과 같은 ui_template_theme.css 하나로 모은다.
- openwebcad가 ui_template_theme.css를 직접 import — App.css의 하드코딩 색
88개를 var(--color-*)로 전량 치환, CAD 골격용 별칭 토큰만 새로 붙임
- src/theme.ts 신설: 동일 오리진 localStorage(frd_theme) + storage 이벤트로
부모 토글을 리로드 없이 추종(부모 코드 수정 없음), 시스템 폴백 유지
- 캔버스 배경·그리드는 토큰 경유, 저장된 도면 색은 라이트 모드에서 그릴 때만
휘도 보정(paintColor) — 저장 데이터는 불변
- 테마 변경 시 bumpSceneVersion()으로 정적 장면 캐시 비트맵 재생성
- Button/LayerManager의 tailwind 색 유틸 제거, index.html의 bg-* 고정 제거
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>