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
+31 -3
View File
@@ -5,6 +5,7 @@ import {
createNotePage,
updateNotePage,
deleteNotePage,
renameNoteSection,
type NotePage,
} from '../api/client'
@@ -44,17 +45,42 @@ export const useNotesStore = defineStore('notes', () => {
activeId.value = id
}
async function addPage(title = '新しいページ'): Promise<void> {
async function addPage(title = '新しいページ', section: string | null = null): Promise<void> {
error.value = null
try {
const page = await createNotePage(title)
pages.value.push(page)
const page = await createNotePage(title, section)
// サーバー側でセクションの並びに合わせて再配置されている可能性があるため一覧を取り直す
await load()
activeId.value = page.id
} catch (e) {
error.value = e instanceof Error ? e.message : 'ページの作成に失敗しました'
}
}
/** ページの所属セクションを変更する。section が空/nullならグループ解除。並び順もサーバー側で調整されるため再取得する。 */
async function setSection(id: number, section: string | null): Promise<void> {
error.value = null
flush()
try {
await updateNotePage(id, { section })
await load()
} catch (e) {
error.value = e instanceof Error ? e.message : 'セクションの変更に失敗しました'
}
}
/** oldName セクションに属する全ページを newName セクションへ一括で付け替える。newName が空ならグループ解除。 */
async function renameSection(oldName: string, newName: string | null): Promise<void> {
error.value = null
flush()
try {
await renameNoteSection(oldName, newName)
await load()
} catch (e) {
error.value = e instanceof Error ? e.message : 'セクション名の変更に失敗しました'
}
}
async function removePage(id: number): Promise<void> {
error.value = null
try {
@@ -118,6 +144,8 @@ export const useNotesStore = defineStore('notes', () => {
addPage,
removePage,
edit,
setSection,
renameSection,
flush,
}
})