# v0.3.2 — Cut G Design (사이드바 + notebook 카테고리)

**작성일:** 2026-05-09
**선행 문서:**

- `docs/superpowers/specs/2026-04-25-dogfood-feedback.md` (F25)
- `docs/superpowers/strategy/v028plus-roadmap.md` Cut G

**Cut 라벨:** v0.3.2

---

## 1. Cut 정체성

inbox layout 재구성 — 사이드바 + 메모 카테고리 (notebook). single-pane → two-pane. 단일 DB 안 `notebook_id` 컬럼 (옵션 B — 1주 scope, 다중 profile 옵션 A 는 v0.4+ 후보).

---

## 2. 범위

| 항목 | 결정 |
|---|---|
| **F25 저장소 정의** | B — 카테고리/폴더 (notebook_id, 단일 DB 안 그룹화) |
| **사이드바 가시성** | 사용자 토글 + last state 보존 (settings) |
| **사이드바 내용** | 상단 notebook 목록 + 하단 메모 list (compact view) |

---

## 3. Schema 마이그레이션 (m007)

```sql
CREATE TABLE notebooks (
  id TEXT PRIMARY KEY,
  name TEXT NOT NULL,
  color TEXT,                  -- accent color for UI (옵션)
  created_at TEXT NOT NULL,
  position INTEGER NOT NULL DEFAULT 0
);

INSERT INTO notebooks (id, name, created_at, position)
  VALUES ('default', '기본', '2026-05-09T00:00:00Z', 0);

ALTER TABLE notes ADD COLUMN notebook_id TEXT NOT NULL DEFAULT 'default'
  REFERENCES notebooks(id) ON DELETE RESTRICT;

CREATE INDEX idx_notes_notebook_status ON notes(notebook_id, status, created_at DESC);
```

기존 모든 notes → notebook_id='default'. 사용자가 새 notebook 생성 후 메모 이동 가능.

`ON DELETE RESTRICT` — notebook 삭제 시 노트 잔류해야 함. notebook 삭제 흐름은 사용자가 명시 (메모 이동 후 삭제).

---

## 4. NotebookRepository

```ts
class NotebookRepository {
  list(): Notebook[];
  get(id: string): Notebook | undefined;
  create(name: string, color?: string): Notebook;
  rename(id: string, name: string): void;
  delete(id: string): void;  // notebook 안 메모 0건일 때만 (RESTRICT 위반 시 throw)
  reorder(ids: string[]): void;  // position 갱신
  countNotes(id: string, opts?: { status?: NoteStatus }): number;
}
```

NoteRepository 의 모든 query 에 `notebook_id` filter 추가:

```ts
listByStatus(status: NoteStatus, opts: { notebookId?: string; limit?: number }): Note[];
moveToNotebook(noteId: string, notebookId: string): void;
```

---

## 5. UI — 사이드바

### 5-1. layout

```
┌──────────────┬───────────────────────────────────┐
│ [≡] Inkling  │ [Inbox(N) 완료(N) 보관(N) 휴지통(N)] [🔍 search]  [⚙] │
├──────────────┼───────────────────────────────────┤
│ 노트북        │                                   │
│ • 기본 (12)   │   NoteCard list (current view)    │
│ • 회사 (5)    │                                   │
│ • 학습 (3)    │                                   │
│ + 새 노트북    │                                   │
├──────────────┤                                   │
│ 메모 빠른 list│                                   │
│ - title 1    │                                   │
│ - title 2    │                                   │
│ - title 3    │                                   │
│ ...          │                                   │
└──────────────┴───────────────────────────────────┘
```

폭: 240px (settings 의 `sidebar_width` 사용자 조정 가능, default 240, min 180, max 400).

### 5-2. 토글

헤더 좌측 햄버거 (`≡`) 버튼 → `useInbox.sidebarVisible` toggle. last state 저장 (`settings.sidebar_visible`).

키보드 shortcut: `Ctrl+B` (또는 `Cmd+B` macOS) — 빠른 토글.

### 5-3. Notebook 목록

상단 panel — `NotebookRepository.list()` + 각 notebook 의 active 메모 count.

- 클릭 → `useInbox.selectedNotebookId` 갱신 → main pane 의 NoteCard list 가 해당 notebook 만 표시.
- 우클릭 → context menu: 이름 변경 / 색 변경 / 삭제 (메모 0건일 때만).
- "+ 새 노트북" 버튼 → modal: name 입력 + color picker (선택사항) → create.

### 5-4. 메모 빠른 list

하단 panel — selected notebook + selected status (Inbox/완료/보관/휴지통 탭) 의 NoteCard 들의 compact view.

- title + tag chip 1-2 개 + 시간 (relative — "2시간 전")
- 클릭 → main pane 가 해당 NoteCard 위치로 scroll (또는 강조)

main pane 의 NoteCard grid 와 사이드 빠른 list 는 동일 데이터 — 단지 view 다름. 사이드는 navigation, main 은 detail.

### 5-5. NoteCard 갱신 — notebook 이동

NoteCard 액션 메뉴 (Cut B 의 status 메뉴 옆):

- "다른 노트북으로 이동" → notebook 목록 dropdown → 선택 → `moveToNotebook` IPC

---

## 6. store 갱신

```ts
interface InboxState {
  // 기존
  view: 'inbox' | 'completed' | 'archived' | 'trash' | 'review-daily' | 'review-weekly' | 'review-monthly' | 'settings';
  
  // 신규 (Cut G)
  notebooks: Notebook[];
  selectedNotebookId: string;
  sidebarVisible: boolean;
  loadNotebooks: () => Promise<void>;
  selectNotebook: (id: string) => void;
  createNotebook: (name: string, color?: string) => Promise<void>;
  renameNotebook: (id: string, name: string) => Promise<void>;
  deleteNotebook: (id: string) => Promise<void>;
  toggleSidebar: () => void;
}
```

`refreshMeta` / `loadInitial` 가 notebooks 도 함께 fetch.

---

## 7. IPC

```ts
'inbox:list-notebooks': () => Promise<Notebook[]>
'inbox:create-notebook': (name: string, color?: string) => Promise<Notebook>
'inbox:rename-notebook': (id: string, name: string) => Promise<{ ok: true }>
'inbox:delete-notebook': (id: string) => Promise<{ ok: true } | { ok: false; reason: string }>
'inbox:move-to-notebook': (noteId: string, notebookId: string) => Promise<{ ok: true }>
'inbox:reorder-notebooks': (ids: string[]) => Promise<{ ok: true }>
```

---

## 8. F19 search 와 결합 (Cut D 후)

search box — 사이드바 도입 후 위치 검토:

- (a) **inbox 헤더 잔류** (Cut D 결정) — 단순. 사이드바 토글 무관.
- (b) **사이드바 안 상단** — 사이드바 visible 일 때만 search. hidden 시 inbox 헤더 fallback.

추천: (a) — Cut D 결정 보존, 사이드바 토글 무관. UX 일관.

search 결과 — current selectedNotebookId 안만 또는 모든 notebook? settings 토글 또는 search options dropdown. 추천: 기본 current notebook 안 검색 + "모든 노트북에서 검색" 옵션.

---

## 9. 테스트 전략

| 영역 | 단위 |
|---|---|
| m007 마이그레이션 | notebooks 테이블 + 'default' INSERT + notes.notebook_id backfill |
| `NotebookRepository.list/create/rename/delete/reorder` | 각 메서드 |
| `delete` RESTRICT | 메모 잔류 시 throw |
| `moveToNotebook` | notebook_id 갱신 + 카운트 영향 |
| 사이드바 토글 | store action + settings 저장 |
| Notebook 목록 렌더 | count badge + 클릭 → selectedNotebookId 갱신 |
| 메모 빠른 list | selectedNotebook + selectedView 필터 |
| Notebook 생성 modal | name 입력 + color picker → create |
| Notebook 삭제 | 메모 잔류 시 error 표시 |
| search + notebook scope | 'current notebook' / 'all' 옵션별 필터 |

**목표**: 단위 575 → 약 600 (+25), typecheck 0.

---

## 10. Risk

| Risk | 대응 |
|---|---|
| 사이드바 폭이 좁은 화면 (1280×720) 에서 너무 큼 | default hidden 옵션? settings 의 width 조정 + 좁은 화면 시 자동 hide |
| Notebook 삭제 시 RESTRICT error UX | error message + "메모 N건 이동 후 다시 시도" 안내 |
| 다중 notebook 시 search default scope 혼란 | search box 옆 'current/all' 토글 + 기본 current |
| F21 sync (Cut E) 와 결합 시 notebook 정합성 | sync markdown export 가 notebook_id 도 frontmatter 에 포함 — Cut E ImportService 갱신 (미리 spec 잔류 — Cut G 머지 시 ImportService 갱신 commit 포함) |
| 다중 profile 옵션 A 로 진화 시 notebook → profile 마이그레이션 | v0.4+ 영역. 본 cut 은 단일 profile + notebook 다 |

---

## 11. v0.3.2 후

**v0.4 후보** (사용자 dogfood metric 충족 후 외부 확장):

- F25 옵션 A (다중 profile 분리 DB) — 외부 user 확장 시
- F19 옵션 B (context-based recall — 시간/태그/요일)
- F19 옵션 E (spaced repetition)
- F25 옵션 C (다중 sync remote)

**dogfood verify**:

1. 사이드바 사용 빈도 (열린 채로 유지 / 토글 자주)
2. notebook 갯수 (본인 dogfood — 1개 vs N개)
3. notebook 간 메모 이동 빈도 (분류 욕구 측정)
