540 Commits
Author SHA1 Message Date
kimandClaude Code 03a462d72e fix: 작업실 채팅의 링크가 클릭 불가 평문이던 문제
- 작업실 채팅 renderMd에 링크 지원이 아예 없었다(굵게·표·리스트만 구현) — 모델이
  표에 넣어준 출처 링크([바로가기](https://www.printables.com/model/145094))를
  클릭해도 아무 일 없었음(2026-09-25 사용자 실측).
- [라벨](URL) → <a target=_blank> 변환 추가 + 벗겨진 벌주 URL도 자동 링크.
  이스케이프 이후 처리라 URL의 &가 &amp;로 href에 들어가는 것까지 확인.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-25 20:36:47 +09:00
kimandClaude Code b4f9a3df9f feat: ACTION-PROMISE 가드 — 실행 예고만 하고 도구 0개로 끝난 턴 1회 넛지
- 2026-09-25 작업실 실측(로봇 프로젝트 채팅): 모델이 '먼저 GitHub에서 관련
  리포지토리를 찾겠습니다'만 내놓고 도구 0개로 턴 종료, 사용자가 '?'로 되물어도
  사과+예고만 반복. deferral 넛지는 '잠시만 기다려'류만 잡고 실행형 요청은 아예
  제외라 이 케이스가 두 망 다 통과했다.
- isActionPromiseNoTool: 답 끝부분(마지막 220자)이 행동 동사+미래 서약
  (~하겠습니다/~해 보겠습니다/~시작합니다) 또는 let me / I'll … 로 끝나는지.
  과거형·의문형·의례적 맺음말은 제외, 800자 이상 완성 답변도 제외.
- handleChat: 이번 턴 도구 호출 0개일 때만 1회 넛지(라운드 소진 방지), 로그줄
  '[v2] ACTION-PROMISE POST-CHECK'가 발동 증거. 테스트 3건(실측 문구 포함) 추가.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-25 20:30:16 +09:00
kimandClaude Code a61086d93c feat: 도면 버튼 — 작업실 STL 카드·STL 편집기에서 3면도 즉시 생성
- 작업실 파일 카드(STL)에 📐 버튼: /api/cad/render?view=three 를 fetch→blob으로
  생성해 새 탭에 표시(Authorization 헤더 필요라 단순 링크 불가). 생성 중 표시 후
  결과로 교체. 카드 하단 좌측 오버레이 버튼.
- STL 편집기 파일 행에 📐 버튼(로드 없이 선택만으로 도면 가능).
- 3면도 실루엣 판정 수정: 옛 '모서리 색과의 차이' 방식은 원통처럼 뷰가 사각형을
  못 채우는 부품에서 bbox 모서리에 옛 배경(cream)이 사각형으로 남았다 — 흰 시트
  기준 임계로 판정하고 크롭 내부 잔여 배경도 흰색으로 통일(2026-09-25 turntable
  실측). 참고: PIL floodfill은 fill 값과 배경이 thresh 이내면 실행 자체를 안 함.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-25 19:45:12 +09:00
kimandClaude Code 3761390d8e feat: stl_cad에 transform 액션 — 외부 STL도 채팅으로 크기변경/회전/거울/이동
- 소스 없는 다운로드 STL은 구멍/컷/불리언만 채팅 편집이 되고 크기·회전은 웹
  편집기에 의존했음(2026-09-25 사용자 요청으로 추가).
- transformStl: OpenSCAD import+변환 체인으로 재출력 — 법선·와인딩(거울 시 면
  방향)을 OpenSCAD가 올바르게 재생성. 회전·거울·배율은 bbox 중심 기준이라 모델이
  흩어지지 않음. 제자리 덮어쓰기(output_path 생략)도 임시파일→rename 원자적 처리.
  align_bottom 옵션으로 바닥 z=0 정렬.
- 함정 기록: OpenSCAD mirror([x,y,z])는 축이 아니라 법선 벡터 — [-1,1,1]은 x축
  거울이 아니라 기울어진 평면 반전(테스트로 발견·수정, 축 거울은 [1,0,0] 형태).
- 테스트 6건 추가.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-25 19:30:48 +09:00
kimandClaude Code 1125f3afb9 feat: 부품 바닥 z=0 규칙 — 컴파일 자동 정렬 + 편집기 정렬 버튼
- 계기: 로봇팔 부품들이 center=true 모델링이라 바닥이 z<0으로 내려가 편집기에서
  파묻혀 보이고 좌표 감각이 어긋남(2026-09-25 사용자 지적, CAD 전부 실측 확인).
- .scad 작성 규칙에 7번 추가: 인쇄용 부품 바닥은 z=0(프롬프트 규칙문+SHORT).
- scad_to_stl/compile: 컴파일 후 bbox min z<-0.05면 바이너리 z 평행이동으로
  자동 정렬하고 안내 문구 출력(translateStlZInPlace, 원자적 재기록).
- STL 편집기에 '바닥 z=0' 버튼 — 외부 STL도 처리(현재 회전·배율 상태의
  월드 bbox 최저점을 0으로). 테스트 3건 추가.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-25 18:58:14 +09:00
kimandClaude Code 7e22a3074f feat: 작업실 파일·폴더 삭제를 영구 rm → 휴지통 이동으로 변경
- 계기: 작업실 화면에서 CAD/ 폴더를 지우면 되돌릴 수 없었다(LeRobot SO-101
  프로젝트 CAD/ 소실, 2026-09-25 실측).
- 삭제 시 workshop/.trash/<projectId>/<타임스탬프-접미사>/ 로 rename만
  (같은 볼륨이라 즉시, UI 변경 없음). 14일 지난 항목은 다음 삭제 시 자동 정리.
- 프로젝트 삭제 시 첨부 폴더도 휴지통으로(기존엔 통째 rm).
- 휴지통 이동 실패 시에만 기존 rm으로 폴백. 스탬프에 고유 접미사로 연속
  삭제 덮어쓰기 방지. 테스트 5건 추가.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-25 18:25:54 +09:00
kimandClaude Code 2a72388f9c fix: 웹에서 mp4/mp3 재생이 아니라 다운로드로 떨어지던 문제
- /api/files 라우트가 영상·오디오를 attachment + application/octet-stream으로
  서빙해서 브라우저가 재생 대신 다운로드만 했던 것(2026-09-25 사용자 실측).
- 영상·오디오 MIME 추가(video/mp4, video/webm, audio/mpeg 등) + inline 처리.
- Range 요청(206 Partial) 지원 — 영상 시킹 시 처음부터 전체 재수신 안 함.
- 회귀 확인: PDF inline 뷰어, PPTX 강제 다운로드 기존 동작 유지.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-25 18:01:35 +09:00
kimandClaude Code 875b08e70a feat: STL 3면도 + 외곽 치수선 자동 생성 (C안)
- stl-three-view.ts: OpenSCAD ortho 렌더 3뷰(정면/평면/우측면)를 실루엣 크롭→공통
  축척 리샘플로 제3각법 합성, 외곽 치수선(연장선·화살표·수치)+제목란까지 PIL 합성.
  동시실행 직렬화, 임시디렉토리 정리, ASCII STL 명확 에러.
- stl_cad 도구에 three_view 액션, /api/cad/render?view=three 라우트 추가.
- 테스트 5건(fmtMm 단위 + 실렌더 통합/픽셀검증/정리/에러).

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-25 17:50:54 +09:00
kimandClaude Sonnet 5 cedf881f2d feat: 조립 설명서 호환 .scad 작성 규칙을 코드에 하드코딩 (규칙 문구·lint·SCAD/ 경로 보정)
배경: 테스트 프로젝트 로봇팔은 부품이 로컬 좌표 모듈이고 조립 자세를 robot_arm() 하나가 translate/rotate로 만드는
구조라, 조립 초안이 전체+하위 모듈을 중복으로 잡고 하위 모듈이 원점에 그려져 단계 강조가 안 보였다.

규칙(src/tools/scad-conventions.ts):
 1) 부품마다 조립 자세 모듈 asm_<이름>()(모듈 안에서 translate/rotate)  2) 파일 끝 조립 블록 PART == "all" — 모듈 호출만, 조립 순서대로
 3) 참고용 형상은 ref_ 접두어(조립 설명서에서 제외)  4) 모듈 위 주석 한 줄 = 한국어 부품 이름  5) 새 .scad는 SCAD/, STL은 CAD/
적용 지점:
 - 작업실 채팅 컨텍스트에 규칙 전문, write_scad 도구 설명(모델이 보는 정의 2곳)에 요약
 - write_scad가 저장 직후 lint(조립 블록 없음 / 블록 안에 변환·형상·반복문 / 부품 1개뿐)를 응답에 덧붙여 모델이 스스로 고치게 함
 - 프로젝트 안의 새 .scad를 SCAD/ 로 자동 보정(CAD/·루트·print/ → SCAD/, 사용자 폴더는 존중)하고 이동 사실을 안내
파서: 조립 블록 이름 별칭(all/assembly/full/whole/complete), ref_ 고스트 접두어, 한 줄에 압축된 module 인식, getAssemblyRegion 분리
초안 실패/경고 문구에 "채팅에 조립 블록을 만들어줘" 안내
테스트 24건 추가(lint 표, 경로 보정, 별칭, write_scad 실제 OpenSCAD 통합)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 11:18:29 +09:00
kimandClaude Sonnet 5 2bdc5563dc feat: .scad 소스를 프로젝트 SCAD/ 폴더로 분리 (STL은 CAD/ 유지) + 렌더 미리보기는 사진/
사용자 지시(".scad 폴더를 따로"): 변형 .scad가 많은 프로젝트(로봇팔 등)에서 STL과 섞이면 고르기 어려움
- 규칙: workshop/<id>/SCAD/(.scad 소스만) · CAD/(STL 등 모델, CAD/참고, CAD/다운로드) · 출력/ · 사진/ · 영상/ · 조립설명서/
  옛 "STL을 .scad와 같은 폴더" 규칙은 폐지. 사용자가 만든 폴더의 .scad는 건드리지 않음
- 이동 스크립트가 .scad→SCAD/ 처리(조립 명세의 scad 경로도 갱신). 09-25 papa 적용: .scad 12개 + 렌더 PNG 5개 이동, 무손실 검증
- stl_cad 렌더 미리보기 PNG를 프로젝트 STL이면 사진/ 에 저장(CAD/에 이미지가 다시 쌓이던 문제)
- 채팅 프롬프트/도구 안내: 새 .scad는 SCAD/, STL 출력은 CAD/. 파일 탭 권장 폴더에 SCAD 추가
- 테스트: 규칙 표 갱신, apply/undo 왕복, previewPngPath

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 10:38:41 +09:00
kimandClaude Sonnet 5 86d541d928 fix: .scad 선택 시 "새 경로 + 옛 부품" 어긋난 상태로 렌더가 실패하던 문제
- 원인: 다른 .scad를 고르면 경로만 먼저 바꾸고 부품은 나중에(또는 취소하면 영영) 안 바뀌어, 그 사이 저장 타이머/자동 렌더가
  새 파일에 없는 모듈(옛 파일의 motor())로 렌더 → "부품 motor: .scad에 module motor이(가) 없습니다"가 화면에 남음
- 확인창을 먼저 띄우고, 자동 초안이 성공한 뒤에만 경로+부품을 한 번에 교체. 취소/초안 실패 시 아무것도 바꾸지 않고 선택 상자만 원복
  (경로만 바꾸려면 "경로 직접 입력")
- 편집하면 이전 렌더 오류 문구를 지움. 서버 오류 문구에 파일 이름과 해결 방법(자동 초안으로 부품 재생성/모듈 이름 수정) 안내
- 회귀 하네스: 취소/초안 실패/도중 어긋난 상태 저장 감시 (옛 코드에서 4개 항목 실패 확인)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 10:31:53 +09:00
kimandClaude Sonnet 5 b611c045b6 fix: 조립 탭 ".scad 파일 선택이 안 된다" — datalist를 선택 상자로 교체
- 원인: <input list=datalist>의 후보(서버 조회)가 화면을 그린 뒤 도착하는데 목록을 다시 안 그려 항상 비어 있었고(옵션 0개),
  입력칸에 값이 있으면 브라우저가 그 값과 일치하는 항목만 보여줘 다른 파일을 고를 수 없었다
- <select>로 교체(최근 수정 순, 현재 파일 선택 표시, 후보 도착 시 제자리 갱신), 직접 입력칸은 아래에 유지(목록 밖 경로는 "(목록 밖)")
- 다른 .scad를 고르면: 부품이 비어 있으면 자동 초안+렌더, 이미 있으면 교체할지 확인(취소하면 경로만 변경)
- 회귀 하네스 tests/manual/scad-select-check.ts (11개 항목)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 10:21:00 +09:00
kimandClaude Sonnet 5 f976182e0f chore: 작업실 분리 파일 머리말 주석의 옛 파일명 정정
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 10:04:29 +09:00
kimandClaude Sonnet 5 d235ee94f3 refactor: 작업실 핵심 스크립트를 기능별 파일로 분리 (core/parts/tasks/files/chat/init)
- workshop-app.js(83KB) → workshop-core.js(30KB: 인증·이스케이프·상태/저장/충돌/폴링·탭·지연 로더·모달/이력),
  workshop-parts.js(17KB), workshop-tasks.js(6KB: 작업+메모·개요), workshop-files.js(13KB: 파일·슬라이서),
  workshop-chat.js(18KB: 채팅·사진 첨부·리사이즈·모델 pill), workshop-init.js(1KB)
- 원래 한 파일의 섹션 순서 그대로 로드(core→parts→tasks→files→chat→init)해 로딩 순서 문제를 만들지 않음.
  escHtml/safeUrl은 여러 모듈이 쓰므로 core로 이동
- 무손실 검증: 분리 전후 코드 줄 비교(사라진 코드 줄 0, 함수 선언 118개 동일), 실브라우저 45개 항목 + 실서버 읽기 전용 확인
- vm 저장 로직 테스트는 workshop-core.js를 읽도록 변경

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 09:59:12 +09:00
kimandClaude Sonnet 5 0b7057f975 refactor: 작업실 스크립트에서 조립·장비 탭을 별도 모듈로 분리하고 지연 로딩
- workshop-app.js 114KB → 83KB(핵심), workshop-assembly.js(22KB)·workshop-equip.js(12KB)는 해당 탭을 처음 열 때만 로드
- 코드 내용 변경 없음(구간을 잘라 옮김). 모듈 밖에서 부르던 이름은 refreshAssemblyPane(shim)과
  startEquipTimers/stopEquipTimers 뿐이라 가드된 로더(loadWorkshopModule/withLazyModule)로 감쌈
- 로드 중엔 패널을 흐리게(.rb-loading)+클릭 차단, 로드 중 다른 탭으로 옮기면 실행 안 함,
  실패 시 안내 표시 + 다시 열면 재시도(같은 모듈은 한 번만 받음)
- 브라우저 하네스에 지연 로딩 5개 항목 추가(시작 시 미로드/탭별 로드/재요청 없음/실패 안내/재시도), 전체 43개 항목 통과

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 09:53:49 +09:00
kimandClaude Sonnet 5 6119be592d refactor: 작업실 HTML에서 CSS·JS를 별도 파일로 분리 — 내용 변경 없음
- web-ui/html/workshop-app.html 147KB → 9KB (구조만)
- web-ui/css/workshop-app.css(24KB), web-ui/js/app/workshop-app.js(114KB, classic script라 인라인 onclick 전역 핸들러 그대로)
- 분리 내용은 바이트 단위로 원문과 동일함을 검증, 실브라우저 하네스 38개 항목 통과
- vm 저장 로직 테스트는 분리된 JS를 읽도록 변경, 조립 하네스 기본 경로를 폴더 정돈 후 위치로 갱신

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 09:45:28 +09:00
kimandClaude Sonnet 5 ba193eec3c feat: 작업실 파일 폴더 규칙 통일 — STL을 .scad와 같은 폴더에, 프로젝트별 제각각 구조 정돈
규칙(workshop-layout.ts 한 곳에 정의, 도구·스크립트가 공유):
  workshop/<id>/CAD/(.scad+STL 같은 폴더) · CAD/참고/(비인쇄 참고 모델) · CAD/다운로드/ · 출력/ · 사진/ · 영상/ · 조립설명서/
  print3d/K2웹슬라이서/ · 모델검색/ · 일회성/ · profiles/
인쇄용 STL은 .scad 옆에 두되 참고용은 CAD/참고/로 분리 유지(09-22 "참고용을 인쇄 대상으로 착각" 방지)

- 이동 스크립트 scripts/migrate-workshop-layout.ts: 기본 dry-run, --apply(이동만·덮어쓰기 없음·충돌은 건너뛰고 보고),
  --undo <manifest>. .scad 경로 참조(조립 명세)와 문서 문구(CAD/print→CAD 등) 갱신, 빈 폴더만 정리. 멱등
- 도구가 같은 규칙으로 저장: print3d_model(모델→CAD/다운로드, G-code→출력, 프로젝트 없으면 print3d/일회성),
  model_download(CAD/다운로드 또는 print3d/모델검색), K2 웹 슬라이서(print3d/K2웹슬라이서). 옛 print3d/<프로젝트이름>/ 제거
- 채팅 프롬프트: STL은 .scad와 같은 폴더(옛 09-22 CAD/print 하위폴더 규칙 폐지), 프로젝트 폴더 규칙 안내
- 파일 탭: 권장 폴더를 비어 있어도 업로드 대상으로 표시하고 규칙 순서로 정렬
- 테스트 34건(규칙 표, 멱등, 충돌 미덮어쓰기, apply→undo 왕복 무손실, saveDirs, print3d 계획)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 09:37:55 +09:00
kimandClaude Sonnet 5 c4807c9400 fix: 작업실 전체 점검 — 저장 경쟁 상태(가짜 충돌)·팔레트 PNG 썸네일·빈 조립 명세·중복 응답 루프
- 저장 직렬화: 자동 흐름(초안→렌더의 flushNow)과 debounce 타이머가 saveProject를 동시에 불러 같은 기준 버전으로
  PUT → 두 번째가 자기 자신의 첫 저장과 어긋나 가짜 409("렌더 실패: 저장 충돌을 먼저 해결하세요"). 큐로 직렬화하고,
  409여도 서버 내용이 내 내용과 같으면 충돌로 보지 않는다. 회귀 테스트(이전 코드에서 재현 확인)
- 비전 썸네일: 팔레트(P) 모드 PNG가 "cannot write mode P as JPEG"로 실패해 큰 이미지를 모델이 못 보던 문제 → RGB 변환
- 빈 조립 명세(경로/부품/단계 없음)는 저장하지 않음(조립 탭을 열기만 해도 프로젝트에 남던 것)
- .scad만 연결돼 있고 부품/단계가 없는 프로젝트도 조립 탭에서 초안 자동 시도(중단된 자동 연결 복구)
- 중복 add 응답에 "이미 충족됨, 반복 호출 금지" 안내 — 모델이 같은 add_task를 반복하던 LOOP WARN 완화

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 09:20:34 +09:00
kimandClaude Sonnet 5 c625826325 feat: 조립 설명서 .scad 자동 연동 — 자동 연결·자동 초안·변경 시 자동 재렌더
- 자동 연결: 조립 탭을 열면 명세가 비어 있을 때 프로젝트에서 가장 최근 수정한 .scad를 자동 연결
- 자동 초안: .scad의 조립 블록(if (PART == "all") { … } 또는 최상위)이 호출하는 모듈을 부품으로 삼고,
  변수 인자(camera(+CAM_OFFSET))는 원본을 include한 probe로 값(80)으로 풀고, 부품별 bbox로 분해 offset
  (수평 중심 바깥 0.5배·수직 윗쪽 높이 기준 0.9배+층 간 여유)와 조립 순서(.scad에 나열된 호출 순서, 같은
  모듈은 첫 등장 위치에 한 단계)를 정한다. %/* 수식자·변환 붙은 호출·이름에 ghost/fov/axis/plane…·isGhost=true
  인자는 고스트로 제외하고 안내. 자동 흐름은 조립 블록이 있는 파일만 채워 데모/애니메이션 파일로 엉뚱한
  명세가 만들어지지 않게 한다(수동 ✨ 버튼은 허용)
- 자동 재렌더: write_scad가 .scad를 바꾸면 그 파일을 쓰는(렌더한 적 있는) 프로젝트 그림을 백그라운드로
  갱신(프로젝트별 합치기), 조립 탭을 열었을 때 stale이면 자동 재렌더. autoRender 체크박스로 끌 수 있음
- 채팅 도구 draft_assembly(apply=true면 저장), 라우트 POST /api/workshop/assembly-draft
- 테스트: 파싱/이름/실행(OpenSCAD)/자동 재렌더/도구 + 실브라우저 자동 연동 시나리오

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 09:06:48 +09:00
kimandClaude Sonnet 5 8677a090d5 feat: 작업실 "조립 설명서" — .scad에서 단계별 조립도·분해도·부품표를 자동 생성
- 명세(project.assembly): 부품(call=조립 모듈 호출, 분해 offset, BOM 연결, 수량) + 조립 단계(부품/체결부품/공구/설명).
  프로젝트 데이터라 이력·충돌감지·복제가 그대로 적용된다
- 렌더러: 원본 .scad를 수정하지 않고 `use <>` wrapper로 부품을 골라 OpenSCAD ortho 렌더. 안쪽 color가 우선이라
  색을 못 덮는 제약을 "전체 렌더 vs 부품 뺀 렌더"의 픽셀 차이로 해결 → 이번 단계 부품만 원색+주황 외곽선,
  나머지는 옅게, 부품 번호 풍선은 그 부품이 실제 보이는 영역에 배치(가림 처리 정확). 모든 그림 같은 카메라
  (bbox 기반 fit, 투영식 실측 검증), 공통 여백 크롭으로 단계 간 축척 유지
- call 검증: 모듈 1개 호출만(따옴표/세미콜론/중괄호/주석/import·surface 등 금지) + .scad 안에 있는 module만
- 대시보드 "조립" 탭: .scad 스캔→부품 추가, 분해 offset, 단계 편집, 그림 렌더, 작업 탭으로 가져오기
- 설명서 페이지 assembly-manual.html: 완성/분해도, 부품표(BOM 구매상태), 체결부품·공구 합계, STEP 카드, A4 인쇄/PDF
- 채팅 도구: scan_scad / get_assembly / set_assembly / render_assembly ("조립 설명서 만들어줘" → AI가 초안)
- 테스트: 순수함수·실제 OpenSCAD/PIL 렌더(가장자리 잘림 없음 검증)·라우트 HTTP 통합·실브라우저 하네스

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 23:03:43 +09:00
kimandClaude Sonnet 5 37630be619 feat: 작업실 대규모 업그레이드 — 이력/충돌감지/도구 확장/표·게이지·진행률/출력 감시 외
안전망
- 변경 이력: 저장 때마다 바뀌기 전 상태를 프로젝트당 30개까지 보존(45초 합치기), 대시보드 "🕘 이력"에서
  되돌리기, 삭제된 프로젝트 복구(첨부파일 제외). 되돌리기 직전 상태도 이력에 남는다
- 낙관적 락: 프로젝트 updatedAt으로 다른 탭/채팅 도구가 먼저 고친 프로젝트 저장을 409로 보류하고
  충돌 배너(내 변경으로 덮어쓰기/서버 버전 불러오기) 제공. 15초 폴링으로 외부 변경 반영(편집 중엔 미룸)
- 명령 주입 수정: resolveToolImageContent가 파일명을 셸에 그대로 넘기던 execSync → execFileSync

채팅 도구(workshop_project)
- set_notes(메모 전체 교체), update_task/delete_task, rename/delete_phase, add_parts(일괄), rename/
  archive/unarchive/duplicate/delete_project(confirm 필수), set_budget, check_price(다나와),
  list_history/restore_history, view_image(첨부 사진을 모델이 직접 봄)
- 프롬프트에 개요·진행률·남은 작업·예산 현황 주입

대시보드
- 부품 표 보기·상태 필터·정렬, CSV/MD 내보내기, 목표 예산 게이지, 작업 진행률·기한(지남/임박),
  프로젝트 복제(템플릿)/보관, 부품 시세 조회, STL 썸네일(OpenSCAD, 캐시), 채팅 사진 첨부(📷)

장비 연동
- print3d_model start 시 작업실 프로젝트/작업을 등록 → K2 출력 완료/실패를 감시해 작업 자동 완료 +
  메모 기록 + 텔레그램 알림(workshop-print-watch.ts, job 없으면 폴링 안 함)

테스트: 서버/도구/감시/프런트 vm 시뮬레이션 + 라우트 통합(HTTP) + 실브라우저 수동 검증 하네스(tests/manual)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 11:53:21 +09:00
kimandClaude Sonnet 5 8e9226e117 fix: 작업실 앱 전수 검토 결함 일괄 수정 — 채팅 저장 덮어쓰기·업로드 크래시·XSS 외
- 저장: saveDebounce 미해제로 채팅 도구 저장 내용이 새로고침 때 화면 옛 상태로 덮이던 버그.
  서버 동기 스냅샷 기반 dirty(바뀐 프로젝트만 PUT), 실패 시 재시도, 무변경 PUT 제거
- 업로드: 파일명 %(100%.stl)로 decodeURIComponent가 'end' 콜백에서 던져 프로세스가 죽던 버그 +
  수신 중 크기 제한, 콜백 예외 응답
- XSS: escAttr가 따옴표만 처리해 부품/프로젝트/파일명이 innerHTML에서 실행되던 것,
  항목 id 화이트리스트, 링크 href safeUrl
- 헛보고 가드: JS \b가 한글에 안 먹어 "저장했어/했다"가 통과하던 정규식, 명사 없는 후속 확인 질문
- 도구: 정확일치 우선 검색, 중복 생성/추가 방지, 세션(ws_<id>) 프로젝트 기본 대상
- 프로젝트 삭제 시 첨부파일·채팅 세션 정리, 손상 JSON 백업(.corrupt), 옛 전체문서 PUT 제거,
  PUT 본문 검증, 백그라운드 탭 장비 폴링 중지, 전송 중 프로젝트 전환 잠금
- 회귀 테스트 16건 추가(프런트 저장 로직은 vm 시뮬레이션)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 11:26:19 +09:00
kimandClaude Code eaa34714cf feat: WORKSHOP SAVE 가드 — 도구 없이 "저장했습니다" 헛보고를 강제 재유도로 차단
09-24 실측 2건: get(읽기)만 하고 add_note 없이 "메모탭에 저장했습니다" 보고.
프롬프트 규칙(b327eef)으로도 안 잡혀 MESSAGING POST-CHECK과 같은 패턴으로
가드 승격. ws_ 세션에서 저장 요청(메모/개요/부품+변경동사) + 답변이 저장
클레임(종결접미사 필수 — "저장했너?"/"저장했던" 제외) + 이번 턴에 변경
action(get/list 제외) 없음 → 1회 강제 재유도.

"이미 이전 턴에 저장돼 있습니다"는 재유도 답변에서 명시적으로 허용해
오탐(이전 턴 저장 상태 보고)이 무한루프 안 걸리게 함. 회귀테스트 7건.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-24 09:15:03 +09:00
kimandClaude Code b327eef57e fix: 작업실 프롬프트에 저장 액션 실호 규칙 — 도구 없이 "저장했습니다" 헛보고 금지
09-24 실측: "메모에 저장" 요청에 도구 0건인데 "메모탭에 저장했습니다"라고 보고,
사용자가 "메모탭에 저장했너?"에 물어서야 실제 호출로 이어짐(2차 시도엔 실제
저장 확인 — notes 795자). 09-23 BOM 주입(4d6afc1)과 같은 계열 — ws_ 세션
프롬프트에 "모든 저장 액션은 이번 턴에 workshop_project를 실제 호출했을 때만
성립, 먼저 호출하고 그 결과로 보고" 규칙 추가.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-24 09:07:22 +09:00
kimandClaude Code 01c9de8374 fix: 작업실 채팅에 KaTeX 도입 — 앱 중 유일하게 수식 렌더러가 빠져 있었음
09-24 실측: "12MP $\times$ 60fps $\approx$ 수 Gbps"가 그대로 노출. 다른 앱들
(의사·법률·투자 등)은 전부 vendor katex + katex-inline.js를 실어 다음 앱들은
정상인데 작업실만 없었다. doctor-app과 동일 구성(스타일시트+3스크립트+
katex-inline.js)으로 도입하고 addMsg/스트리밍 완료 시 renderKatexIn 호출.

renderMd의 유니코드 폴백(\times→× 등, 가격 "$19..$29" 보호)은 KaTeX가 없는
환경에서만 발동 — KaTeX 있으면 $...$를 그대로 남겨 renderKatexIn이 맡는다.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-24 08:42:32 +09:00
kimandClaude Code 12f717c626 fix: 작업실 채팅 renderMd에 마크다운 표 지원 추가
모델 출력의 표는 세션에 줄바꿈까지 정상이었는데 renderMd에 표 처리가 없어
파이프 문자열이 평문 <p>로 뭉개져(HTML 줄바꿈 접힘) 한 줄로 합쳐 보였다
(09-24 실측: 카메라 모듈 스펙 표가 파이프soup로 복사됨). 구분행 |---| 있는
연속 파이프 라인만 <table>로 변환, 없으면 무사통과. 셀 안 굵게 유지,
단락 분기의 블록태그 검사에 table 추가.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-24 08:36:38 +09:00
kimandClaude Code e88e9c5885 fix: web_search에 검색실패 프로토콜 규칙 — web_fetch 우선·쿼리 재구성·기억 답변 금지
09-24 채팅 실측 2라운드: 쿼리 분리 규칙은 적용됐으나(콤마 나열 소멸 확인)
①모델이 잡동사니 1위 결과만 보고 web_fetch를 안 쓰고 실패 선언 — 같은 목록에
정답 페이지("2MP Mira220 USB3 Evaluation Kit - Arducam Wiki")가 있었음
②일반어("resolution specifications") 붙은 쿼리가 쇼핑/매뉴얼 페이지만
돌려오는데 "<product> datasheet" 재구성이면 datasheet 소스가 나옴(직접 실측)
③실패 선언 후 기억으로 스펙 표 작성 → 1.92MP 환각(실측 1456×1088≈1.58MP),
같은 대화에서 모델 자신의 정답과 모순.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-24 08:32:40 +09:00
kimandClaude Code ee7a0ac604 fix: web_search에 콤마 나열 다중제품 쿼리 금지 규칙 — "vs" 분리 규칙의 확장
세션 ws_proj_scan_intraoral 실측: "해상도는 얼마나 되지?"에 3번 연속 콤마 나열
복합 쿼리("Arducam IMX296 resolution, Basler dart M resolution, ...")를 쏴서
searxng가 잡동사니만 돌려줌(Google 홈페이지·Telegram·쿠팡). NUMERIC-GROUNDING은
결과 커버리지 14%로 stand down했고 모델이 기억으로 답해 자기 1턴 전 1.58MP 주장과
모순되는 수치를 냄. 08-10의 "vs" 분리 규칙(실측 검증 936/736 GB/s)에 콤마 나열
다중제품 쿼리 금지를 같은 실측 패턴으로 추가 — 제품 1개당 쿼리 1개.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-24 08:23:36 +09:00
kimandClaude Code 9f236063bf fix: 작업실 개요 이동 요청의 도구회피 실측 수정 — readme 경로 인지 + 읽기 우선 규칙
세션 ws_proj_scan_intraoral 실측: "readme.md 파일 내용을 개요에 옮겨줘"에 모델이
도구 호출을 하나도 안 하고 workshop/proj_scan_intraoral/readme.md만 생각해
"존재하지 않아"로 답함. 실제 파일은 workshop/<id>/개요/readme.md(옛 자동생성
readme)에 있음. workshop_project 도구 설명(registry/build-tools 이중등록 둘 다)에
① 첨부파일 경로 규칙(workshop/<id>/, readme는 <id>/개요/) ② 파일을 실제로 읽은 뒤
set_description으로 넣을 것 ③ 추측으로 단정 금지 규칙 추가.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-24 08:13:14 +09:00
kimandClaude Code e01b92966a feat: 작업실 장비 탭 밀링 카드에도 라이브 뷰
- ▶ 라이브 버튼 → go2rtc video-stream 커스텀 엘리먼트(WebRTC→MSE→MJPEG 자동폴백),
  nvr-app 라이브 모달과 동일 패턴. 릴레이 스트림 "cam"(config nvr.extraCameras)
- go2rtc Origin 검사를 통과하도록 같은 오리진 WS 프록시(/api/nvr/go2rtc-ws) 경유,
  LAN IP 하드코딩 없이 현재 오리진 기준 상대경로(https mixed-content 회피)
- 라이브 중엔 20초 스냅샷 폴링을 건너뛴다, 장비 탭을 벗어나면 연결 정리
  (stopEquipTimers → stopMillLive), 상태줄에 "● 실시간" 표시

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-24 07:52:38 +09:00
kimandClaude Code 75b887d295 feat: 작업실 장비 탭 K2 카드에 라이브 뷰
- ▶ 라이브 버튼 → 카드 안에서 상시 WebRTC 스트리밍(k2-app.html의 검증된 연결
  로직 이식: STUN 필수·오퍼 null-candidate/1.5초 중 빠른 쪽 퀴크 포함)
- 라이브 중엔 20초 스냅샷 그랩(WebRTC 풀협상)을 건너뛴다 — 비디오가 이미
  실시간이고 짧은 연결이 간섭하므로. 상태 조회(Moonraker)는 계속, 상태줄에
  "● 실시간" 표시
- 장비 탭을 벗어나면 stopEquipTimers가 라이브 연결을 닫는다(안 보는 동안
  자원 안 쓰기), ICE failed면 자동 종료 후 안내문

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-24 07:45:29 +09:00
kimandClaude Code eb516e2b74 feat: 작업실 저장 개편 — 전체문서 PUT을 프로젝트 단위 PUT으로 + LOW 2건
저장 개편(리뷰 #4 근본치료):
- PUT /api/workshop/project/:id — 서버가 디스크의 현재 문서에 해당 프로젝트만
  upsert. 대시보드 저장과 채팅 도구 저장이 겹쳐도 서로 다른 프로젝트 편집이
  통째로 사라지지 않음. DELETE /api/workshop/project/:id,
  PUT /api/workshop/active(포인터 단독) 신설. 옛 전체문서 PUT은 스테일 탭
  호환으로 유지
- 클라이언트: dirtyProjects 추적 → saveProject는 바뀐 프로젝트만 순차 PUT,
  활성 포인터는 putActive() 즉시 저장, 프로젝트 삭제는 DELETE 직접 호출,
  beforeunload flush도 프로젝트 단위 keepalive PUT으로 전환
- upsertProject/removeProject를 순수함수로 추출, 단위테스트 4건

LOW 2건:
- loadFiles에 요청 일련번호 — 빠른 프로젝트 전환 시 옛 응답이 나중에 도착해
  이전 프로젝트 파일을 새 판에 그리는 레이스 제거
- 슬라이서 인계 ?load=를 JSON 배열 인코딩으로 전환(파일명에 쉼표 있으면
  옛 쉼표 join이 깨지던 문제). 슬라이서 쪽은 JSON 우선, 옛 형식 폴백 유지

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-24 07:38:53 +09:00
kimandClaude Code f42a09b038 fix: 작업실 앱 리뷰 HIGH·XSS 수정 5건
- 새 프로젝트 생성 직전 syncLocalToProject — debounce 미완료 편집 유실 방지
- softRefreshWorkshop: 활성 프로젝트가 서버쪽에서 바뀐 경우에만 clearStlSelection/
  refreshChatForProject/loadFiles — 채팅에서 전환 시 옛 STL이 슬라이서로 오전송되던 문제
- beforeunload/pagehide에서 debounce 저장 flush(keepalive fetch) — 600ms 안에 탭 닫으면
  마지막 편집이 사라지던 문제
- XSS: escJs에 "·<·> 이스케이프 추가(속성 경계 탈출 차단), chatGreeting 프로젝트명/
  모델변경 실패 메시지 escHtml, 부품 참고링크 safeUrl로 javascript:/data: 스킴 차단

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-24 07:31:07 +09:00
kimandClaude Code c3da185ade fix: workshop_project set_description — 파라미터 이름 혼동 실측 수정
모델이 액션 이름을 그대로 파라미터 이름으로 씀(set_description=텍스트) → description
undefined → 기본 '' 로 개요가 조용히 지워짐(테스트 프로젝트 실측, ws_proj_h70rk5i5).
- 별칭 수용: description ?? set_description ?? overview
- 어떤 파라미터도 안 오면 ''로 덮지 말고 에러로 끝냄(덮어쓰기 방지)
- 스키마 설명에서 "(set_description 필수)"가 파라미터 이름처럼 읽히는 문제 정정 —
  "(set_description 액션에서 필수 — 파라미터 이름은 description이다)"
- 지워진 테스트 프로젝트 개요는 세션 이력에서 복구해 백필

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 17:09:59 +09:00
kimandClaude Code 2da7b9fd98 feat: 작업실 개요를 파일 폴더 대신 개요 탭으로 + /api/files 텍스트 charset 수정
- workshop-app: 메모 탭과 동일 구조의 "개요" 탭(textarea) 추가 — project.description
  직접 편집, 채팅 set_description으로 쓴 내용은 응답 후 softRefresh로 갱신
- readme.md 자동생성(syncProjectReadmes) 철회 — 파일탭이 산만해져서 사용자 지정으로
  탭 방식으로 전환, 생성된 개요 폴더들도 삭제. description/set_description은 유지
- server.ts IMAGE_TYPES: 텍스트 계열(.md/.txt/.csv 등) charset=utf-8 명시 — charset
  없이 text/plain으로 뱉으면 브라우저가 latin1로 해석해 한글이 전부 깨짐(실측)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 16:48:12 +09:00
kimandClaude Code 91ed19bda0 feat: 작업실 프로젝트별 개요 폴더 + readme.md 자동생성
- workshop/<프로젝트id>/개요/readme.md — 대시보드 데이터(개요/예산/부품/작업/메모)에서
  자동 생성, saveWorkshop과 대시보드 data GET에서 내용 변화 때만 동기화(기존 프로젝트 백필 포함)
- WorkshopProject.description 신설 + workshop_project set_description 액션 — 채팅 모델이
  서술형 개요를 쓰면 readme 맨 위 개요 섹션으로 들어감
- formatProject/budgetSummary/fmtWon을 storage로 이동(도구 get과 readme가 같은 문서 소스)
- 파일탭에는 개요 폴더 그룹으로 자동 표시(walkCaseFiles)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 16:32:45 +09:00
kimandClaude Code c13a688e17 feat: python_eval 다중파일 다운로드 루틴 가이드 — 순차수신+타임아웃 재시도+이어받기, 폴더 구조 평평하게
SO-101 워크숍 실측: 모델이 전부 한 번에 받으려다 전체가 타임아웃으로 물러나고,
GitHub 미러 구조(stls/STL/SO101/... 식)를 그대로 따와 폴더가 산만해짐.
- 순차 다운로드(파일당 requests.get timeout=15 + try/except 1회 재시도)
- 도구 timeout 60 권장, 남으면 다음 턴에서 이어받기
- 워크숍은 workshop/<프로젝트id>/stl/ 아래 평평하게, 서브폴더는 부품군 하나까지만

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 13:13:17 +09:00
kimandClaude Code f89b61f15f fix: python_eval — os.makedirs/mkdir 허용(폴더 생성은 파괴 연산 아님) + 파일 다운로드 가이드 설명 삽입
SO-101 워크숍 채팅(ws_proj_1osbd78g)에서 폴더 생성이 차단돼 STL 다운로드 작업이 죽고,
모델이 차단을 '권한 문제'로 오독해 파일 수령을 포기하는 실측 사례 수정.
- BLOCKED에서 makedirs/mkdir 분리 — remove/rename/rmtree 등 파괴 연산은 계속 차단
- python_eval 설명에 다운로드 가이드 인접 삽입: GitHub API로 경로 먼저 확인,
  받은 개수 실측 보고, 기대보다 적으면 실패로 보고

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 13:03:53 +09:00
kimandClaude Code a815721976 docs: 게이트웨이 입력게이트·도구게이팅·출력가드 전체 지도 도표 (guard-map.html)
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 12:16:27 +09:00
kimandClaude Code 9283df99d8 fix: MakerWorld 다운로드 profileId 선택 — hits[].id는 인스턴스 id라 iot-service 400
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 12:07:35 +09:00
kimandClaude Code 5a571f592e feat: 3D 모델 저장소 연동 — model_search/model_download (Printables·MakerWorld·Thingiverse)
- model_search: 3사이트 통합 검색(Thingiverse는 토큰 필요, Printables/MakerWorld는 익명 검색 실측 확인)
- model_download: 슬라이싱 가능한 STL/3MF 중 최대 파일 1개 워크스페이스 저장(Printables getDownloadLink 익명 동작, Thingiverse /v2/files download, MakerWorld는 Bambu 토큰 필요)
- print3d_model에 stl_path 추가 — model_download로 받아둔 파일 바로 슬라이싱
- 토큰은 vault secret(print3d.*_token/cookie), Thingiverse App Token 등록 완료
- 게이트: ws_ 세션 항상 + 메인 채팅은 3D 키워드

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 11:43:03 +09:00
kimandClaude Code 4d6afc10c8 feat: 작업실 세션 프롬프트에 활성 프로젝트 BOM(부품 목록) 주입
2026-09-23 실측: ws_proj 세션에서 "608ZZ 베어링 1개만 있으면 되나?"에 모델이
①도구 호출 없이 "조회하겠습니다"만 말고 턴 종료(BOM이 프롬프트에 없어 도구를
써야 하는데 안 씀) → 사용자 "??" ②다음 턴엔 도구 0건으로 "확인해 보니 608ZZ가
등록 안 됐다"고 지어냄 — 실제 BOM엔 608ZZ ×4(J2/J3 감속기 출력축 지지용)가
등록돼 있어 정반대의 사실이었다.

workshopProjectCtx에 부품 목록(이름·수량·단가·상태·메모)을 턴마다 새로 읽어
주입한다. 목록이 프롬프트에 있으면 부품 질문에 도구 왕복 없이 정확히 답한다.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 11:03:55 +09:00
kimandClaude Code b00db7c064 feat: models.profiles[<model>].think — 모델별 think 모드 오버라이드
glm-5.3-flash:cloud 실측(2026-09-22): 오케스트레이터 턴(multiAgentActive)에서
think:true로 돌며 라운드당 reasoning 3~6K 토큰 소모(뉴스 요약 턴 최종 호출
5,868토큰, 72초) — gemma4는 같은 턴을 ~100토큰에 처리. completion 중앙값 331 vs
93(3.5배). 플래시인데도 체감이 느린 진범은 속도가 아니라 출력량.

- server.ts에 getModelProfileThink 신설(forceToolChoice/fixedNumCtx와 같은
  프로파일 패턴) — boolean 또는 high/medium/low
- handle-chat.ts의 think 결정에 프로파일 오버라이드 적용(라운드 루프 안에서
  effectiveModel 기준으로 판정)
- config: glm-5.3-flash:cloud에 think:false 세팅

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 01:05:36 +09:00
kimandClaude Code f8d1c9f9df feat: thinking 분리를 thinking-separation.ts로 추출 + GLM 영어 셀프토크 구조 기반 분리
- server.ts의 separateThinkingFromContent를 chat/thinking-separation.ts로 추출 (단위 테스트 가능하게)
- stripEnglishScratchPrefix 신설: 영어 런 40자+ + 문장부호 뒤 무공백 한글 봉합점(seam)을
  구조로 탐지 — 키워드가 아니라 구조라 한글 조각이 섞인 영어 계획 단락도 처리
- 스크래치 증거 게이트(메타 라벨 또는 한글 없는 40자+ 단락 2개)로 오탐 방지,
  정상 혼합문("Open-Meteo 모델은")은 보존
- think 태그 내용을 버리지 않고 thinking으로 보존하던 손실 수정
- 실측 표본 2건(09-22 추석 날씨 세션)을 단위 테스트로 고정, 6/6 통과

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 00:41:49 +09:00
kimandClaude Code fe934cbd36 fix: scad_animate/write_scad 설명에 작업실 프로젝트 폴더 접두어 규칙 명시
모델이 'robot_arm_2min.mp4'처럼 접두어 없이 호출해 워크스페이스 루트에
쓰여 파일 탭에 안 보이는 사고 반복(2026-09-22 실측). 설명에
'workshop/<프로젝트id>/...' 접두어 필수 규칙 추가.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 23:27:27 +09:00
kimandClaude Code bbe9599637 feat: scad_animate 프레임 상한 120→600
모델이 '2분짜리' 요청에 도구 상한 120프레임에 막혀 fps1로만 가능하다고
보고한 실측 사례 반영. 렌더 120프레임 실측 ~3초로 여유 충분.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 23:22:50 +09:00
kimandClaude Code 6046f3fb15 fix: scad_animate — 소스의 최상위 $t 할당 자동 제거 가드
모델이 scad 소스에 '$t = 0;'을 직접 쓰면 --animate 엔진의 $t 구동을
덮어써 프레임이 전부 동일 이미지(정지 화면)가 되는 실측 사고 방지.
렌더 전 최상위 $t 할당 줄을 걷어낸 사본으로 렌더하고 결과 안내에 표시.
도구 설명에도 모델용 경고 추가.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 22:10:04 +09:00
kimandClaude Code 7377ec154f fix: scad_animate를 --animate 네이티브 방식으로 전환
OpenSCAD 2021.01 CLI에서 -D '$t=x' 오버라이드가 최상위 변수 할당에
반영되지 않아 프레임 48장이 전부 동일 이미지가 되는 문제 수정.

- 프레임 렌더를 프레임별 -D 루프 → --animate N 단일 프로세스로 전환
  (엔진이 $t를 직접 구동, frame00000.png~ 5자리 0-인덱스로 export)
- 프루브도 -D '$t=0.5' 단일 포즈 → --animate 4 포즈 STL bbox 합집합으로
  카메라 자동 프레이밍(관절이 벌어지는 전 범위 커버)
- 렌더 타임아웃을 프레임 수 기준으로 재조정(60s + 프레임당 20s, 상한 900s)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 21:54:11 +09:00
kimandClaude Code b0a3d427aa fix: scad_animate 카메라 자동 프레이밍 — autocenter/viewall 의존 제거
--autocenter --viewall이 오프스크린 CLI 렌더에서 모델을 프레임에
신뢰성 있게 못 담는다(실측: 구석에 작게 나오거나 아예 밖으로 나가
"노란 빈 화면" MP4가 됨). renderStlPng와 동일 방식으로, 대표 프레임
($t=0.5)을 STL로 컴파일해 실측 바운딩박스 중심+최대길이×3 거리의
명시적 김발 카메라를 계산해서 프레임마다 넘긴다. 사용자가 camera를
주면 그대로 쓰고, 프루브 컴파일 실패 시에만 옛 autocenter 폴백.
배경색도 기본 Cornfield(노란색 — "빈 화면"으로 오독됨) 대신
Tomorrow로 통일.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 21:23:36 +09:00
kimandClaude Code 2ee133b183 feat: scad_animate 도구 — OpenSCAD $t 프레임 렌더 → ffmpeg MP4 합성
워크숍 채팅에서 "애니메이션으로 굴려보자" 요청이 python_eval subprocess
차단(scad_to_stl 설명의 "직접 실행하지 말 것" 안내)에 걸려 Pillow 투영
폴백으로 떨어지는 게 실측됨. OpenSCAD CLI는 runOpenscad으로 실제로 잘
돌아가므로, 프레임 렌더+합성 전용 단일목적 도구를 추가.

- 프레임은 /tmp 임시디렉터리에서 렌더해 워크스페이스엔 mp4 하나만 남김
  (PNG 수십 장이 파일탭에 쏟아지는 사고 방지)
- $t는 frames 기준 균등 간격(0~1-1/N)으로 -D 전달, defines 오버라이드
  scad_to_stl과 동일 패턴. camera는 김발 7값 CSV 검증 후 --camera 통과,
  생략 시 --autocenter --viewall
- ffmpeg libx264/yuv420p(+faststart) — 브라우저 <video> 호환
- stl_cad 3종과 동일하게 ws_ 세션 전용 게이트(tool-scope), 스키마 사본은
  build-tools에도 추가(둘 다 갱신 원칙). 숫자 그라운딩 게이트 면제 대상에
  scad_animate 추가(프레임 수/fps 등 방금 만든 파일 실측값이라 AUTO-RECOVER
  오탐 방지 — stl_cad/scad_to_stl과 동일 취지)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-22 21:10:08 +09:00