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
+14 -3
View File
@@ -263,6 +263,7 @@ export interface NotePage {
title: string
content: string
position: number
section: string | null
updated_at: string
}
@@ -271,18 +272,18 @@ export async function fetchNotePages(): Promise<NotePage[]> {
return parseJson<NotePage[]>(res, 'ノート取得')
}
export async function createNotePage(title: string): Promise<NotePage> {
export async function createNotePage(title: string, section?: string | null): Promise<NotePage> {
const res = await fetch(`${BASE}/api/notes`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title }),
body: JSON.stringify({ title, section: section ?? null }),
})
return parseJson<NotePage>(res, 'ページ作成')
}
export async function updateNotePage(
pageId: number,
patch: { title?: string; content?: string },
patch: { title?: string; content?: string; section?: string | null },
): Promise<NotePage> {
const res = await fetch(`${BASE}/api/notes/${pageId}`, {
method: 'PATCH',
@@ -295,3 +296,13 @@ export async function updateNotePage(
export async function deleteNotePage(pageId: number): Promise<void> {
await fetch(`${BASE}/api/notes/${pageId}`, { method: 'DELETE' })
}
/** section = old のページ全体を section = new へ一括で付け替える。new が空/nullならグループ解除。 */
export async function renameNoteSection(oldName: string, newName: string | null): Promise<{ renamed: number }> {
const res = await fetch(`${BASE}/api/notes/sections/rename`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ old_name: oldName, new_name: newName }),
})
return parseJson<{ renamed: number }>(res, 'セクション名変更')
}