## 2026-05-04 — 8방향 플로팅 윈도우 리사이즈 + Windows 폴더 피커 모더나이즈 + 설정 모달 버전 표시

### 배경

세션 플로팅 윈도우의 리사이즈가 우측 하단 모서리만 가능해 데스크톱 OS의 8방향 그랩에 비해 어색했고, 새 프로젝트 모달의 폴더 피커가 Windows에서 Win98 시절 WinForms `FolderBrowserDialog`(트리 전용, 주소창/빵부스러기/검색/빠른 액세스 전부 없음)였다. 두 UI 모두 Vista+/macOS+ 표준 동작에 맞춰 모더나이즈. 함께 Settings 모달 사이드바 하단에 현재 빌드 버전을 표기해서 사용자가 어떤 버전을 돌리는지 즉시 확인 가능하도록.

### 주요 변경

#### 1. 플로팅 세션 윈도우 8방향 리사이즈 (`9e63ff5`)

`SessionWindow`의 리사이즈가 우측 하단(SE) 코너에서만 동작하던 걸 4 엣지 + 4 코너 = 8방향으로 일반화. 같은 직접-DOM-mutation-during-gesture + commit-to-React-on-mouseup 패턴이 모든 방향에서 동작.

- **`src/client/src/components/SessionWindow.tsx`**:
  - `ResizeDir` type 추가 + `onResizeMouseDown(dir)` 커리. 같은 핸들러가 4 엣지 + 4 코너 커버.
  - West/North는 위치와 사이즈를 함께 이동(반대편 엣지 고정), East/South는 사이즈만.
  - 7개 새 hit-area 렌더 (기존 SE 코너 옆): 4개 thin 엣지(4px, 투명, ns/ew 커서) + 3개 small 코너(10×10, 투명, nwse/nesw 커서). SE 코너는 기존 14×14 그라데이션 삼각형을 visual affordance로 유지.
  - 코너(z-index 4)를 엣지(z-index 3) 위에 stack해 overlap 영역에서 코너가 이김. 기존 chrome-drag, Aero 엣지 스냅, 이웃 sticky 스냅, dock overlay 흐름은 변경 없음(리사이즈는 별도 gesture context, mousedown에 stopPropagation).

#### 2. Windows 폴더 피커 IFileOpenDialog로 교체 + macOS 인젝션 가드 (`c82f250`)

새 프로젝트 모달의 폴더 피커가 Windows에서 Vista+ Common Item Dialog(`IFileOpenDialog`) — File Explorer가 쓰는 그 모던 피커 — 로 바뀌었고, 함께 macOS 분기의 AppleScript 문자열 보간 인젝션 위험을 막고 default location 신뢰성을 높임.

- **`src/server/routes/projects.ts` (또는 native picker 모듈)**:
  - **Windows**: `FolderBrowserDialog` → `IFileOpenDialog` COM 피커로 교체.
    - 인라인 C# (COM 인터페이스 + `SHCreateItemFromParsingName` P/Invoke)을 PowerShell `Add-Type`으로 컴파일해 사용.
    - 주소창에 경로 붙여넣기, 빵부스러기 네비게이션, 빠른 액세스 사이드바, 폴더 검색 모두 지원.
    - 임시 `.ps1`에 UTF-8 BOM + `[Console]::OutputEncoding=UTF8`로 한글 경로 왕복 보장. Node 측에서 stdout BOM strip.
    - legacy hidden-form-as-owner 트릭 제거 (Common Item Dialog가 자체 foreground 처리).
  - **macOS**: AppleScript 인젝션 가드 + POSIX file 보정.
    - `initialDir`의 `\`와 `"`를 escape해서 문자열 리터럴 안에 안전하게 가둠.
    - default location을 `(POSIX file "...")`로 감싸 macOS 버전 간 동작 일관성 확보.
    - `with prompt "폴더 선택"` 추가로 Windows 다이얼로그 타이틀과 라벨 통일.

#### 3. 설정 모달에 앱 버전 표시 (`38ab065`)

사용자가 어떤 CLITrigger 버전을 돌리는지 `package.json`을 직접 열지 않고도 확인할 수 있게, root `package.json`의 version을 client 빌드 시 Vite define으로 주입해 Settings 모달 사이드바 하단에 muted footer로 표기.

- **`src/client/vite.config.ts`** — config-load 시점에 root `package.json` version을 읽어 bundle에 `__APP_VERSION__` global string constant로 expose.
- **`src/client/src/global.d.ts`** (신규) — `__APP_VERSION__: string` declare로 타입체크 통과.
- **`src/client/src/components/SettingsModal.tsx`** — 사이드바 `<aside>`를 `flex flex-col`로 전환하고 muted `v<version>` 라벨을 `mt-auto`로 하단 pin.

### 수정된 주요 파일

| 파일 | 변경 내용 |
|------|----------|
| `src/client/src/components/SessionWindow.tsx` | 8방향 리사이즈 — `ResizeDir` 타입 + 커리된 핸들러, 4 엣지/4 코너 hit-area, 코너 z-index 우선 |
| `src/server/routes/projects.ts` | Windows: `IFileOpenDialog` COM 피커 (PowerShell `Add-Type` C#), UTF-8 BOM stdout 처리. macOS: AppleScript 이스케이프 가드 + `(POSIX file "...")` |
| `src/client/vite.config.ts` | root `package.json` version → `__APP_VERSION__` global define |
| `src/client/src/global.d.ts` | NEW — `__APP_VERSION__: string` declare |
| `src/client/src/components/SettingsModal.tsx` | 사이드바를 flex column으로 전환 + 하단 muted `v<version>` 라벨 |

### 아키텍처 결정

1. **8방향 리사이즈는 같은 패턴 일반화**: SE 코너 핸들러를 그대로 두고 `ResizeDir` 타입과 커리된 `onResizeMouseDown(dir)`로 7개 신규 hit-area에 동일 패턴 적용. anchor 계산만 dir별로 분기(N/W는 좌상단도 함께 이동), 나머지(stopPropagation, 직접 DOM mutation, mouseup commit)는 그대로. 새 gesture context를 만들지 않아 chrome-drag/Aero 스냅/dock overlay와 자연스럽게 공존.
2. **Windows COM 피커는 인라인 C# + Add-Type**: `IFileOpenDialog` 호출에 별도 .NET 어셈블리/실행파일 추가 없이 PowerShell `Add-Type`으로 인라인 C#을 컴파일해 주입. 한글 경로 왕복은 임시 `.ps1`에 UTF-8 BOM + `[Console]::OutputEncoding=UTF8`을 박고 Node 측에서 stdout BOM을 strip하는 식으로 안정화.
3. **AppleScript 인젝션 가드**: `initialDir`이 사용자 입력 경로일 수 있으므로 `\`와 `"`를 escape해서 문자열 리터럴 안에 가둠. default location을 `(POSIX file "...")`로 감싸는 건 macOS 버전 간 path 인식 차이를 흡수.
4. **버전은 빌드 타임 inject**: 런타임 fetch가 아니라 `vite.config.ts`의 `define`으로 주입해 zero-cost. 사이드바 `mt-auto`가 자동으로 footer 위치에 pin해 별도 spacer 불필요.

---
