Add section grouping to shared note tabs

Notes can now be clustered into named sections in the tab bar: each tab
gets a 🏷 button to assign it to a section, and clicking a section's
label renames (or, given an empty name, ungroups) every page in it.

note_pages gains a `section` column. Since the app has no migration
framework, database.py adds the column to existing SQLite files at
startup via PRAGMA table_info + ALTER TABLE. core/notes.set_section()
keeps same-section pages contiguous by renumbering position whenever a
page's section changes, so the tab bar and the AI-facing title list
(now grouped under "## section" headings) can both treat "adjacent" as
"same group" without a separate ordering field.

Also fixes a bug in the tab rename input: ref="renameInput" sits inside
a nested v-for, which Vue 3 binds as an array rather than a single
element, so renameInput.value?.select() was throwing on every rename.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 00:28:36 +09:00
co-authored by Claude Opus 5
parent b1350eb8bc
commit 1c21fc3406
10 changed files with 388 additions and 68 deletions
+28 -4
View File
@@ -93,9 +93,10 @@ SSE でストリーミング応答を返す。
ユーザーと AI が共同編集するノート。ページのタイトル一覧は毎回システムプロンプトに注入され、
本文は AI が `read_note` / `write_note` ツールで読み書きする。
`section` を設定すると、同じセクション名のページはタブバー上で常に連続してグループ表示される。
### GET /api/notes
全ページを `position` 昇順で返す。
全ページを `position` 昇順で返す。同じ `section` のページは常に連続する(隣接=同一グループとして扱われる)。
**レスポンス**
```json
@@ -106,6 +107,7 @@ SSE でストリーミング応答を返す。
"content": "- 牛乳
- 卵",
"position": 0,
"section": "生活",
"updated_at": "2026-08-19T12:00:00"
}
]
@@ -115,21 +117,43 @@ SSE でストリーミング応答を返す。
### POST /api/notes
ページを新規作成し、`201 Created` で作成したページを返す。
`section` を指定すると、作成直後にそのセクションの末尾へ配置される(他ページの `position` も再採番される)。
**リクエスト**
```json
{ "title": "新しいページ", "content": "" }
{ "title": "新しいページ", "content": "", "section": null }
```
---
### PATCH /api/notes/{page_id}
ページのタイトル・本文を更新する。指定したフィールドだけが変更される。
ページのタイトル・本文・セクションを更新する。指定したフィールドだけが変更される。
タイトルを空文字にしようとした場合は `400`、ページが存在しない場合は `404` を返す。
`section` はリクエストボディに**キーとして含まれているかどうか**で判定する(`null` を明示的に送るとグループ解除)。
セクションを変更すると、そのページは新しいセクションの末尾(該当ページが無ければ末尾)へ移動し、
全ページの `position` が振り直される。
**リクエスト**
```json
{ "content": "更新後の本文" }
{ "content": "更新後の本文", "section": "生活" }
```
---
### POST /api/notes/sections/rename
`old_name` セクションに属する全ページを `new_name` セクションへ一括で付け替える。
`new_name` を空文字/未指定にすると、そのセクションの全ページのグループを解除する。
ページの並び順(`position`)は変更しない。
**リクエスト**
```json
{ "old_name": "生活", "new_name": "生活雑貨" }
```
**レスポンス**
```json
{ "renamed": 2 }
```
---