feat(w4): ship v1.5.0 timeline, character arc, and compliance
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -9,7 +9,9 @@ import schemaV7 from './schema-v7.sql?raw'
|
||||
import schemaV8 from './schema-v8.sql?raw'
|
||||
import schemaV9 from './schema-v9.sql?raw'
|
||||
|
||||
const CURRENT_VERSION = 9
|
||||
import schemaV10 from './schema-v10.sql?raw'
|
||||
|
||||
const CURRENT_VERSION = 10
|
||||
|
||||
function tryAlter(db: SqliteDb, sql: string): void {
|
||||
try {
|
||||
@@ -98,4 +100,9 @@ export function migrate(db: SqliteDb): void {
|
||||
db.exec(schemaV9)
|
||||
db.prepare('INSERT INTO schema_version (version, description) VALUES (?, ?)').run(9, 'W1 foundation')
|
||||
}
|
||||
|
||||
if (current < 10) {
|
||||
db.exec(schemaV10)
|
||||
db.prepare('INSERT INTO schema_version (version, description) VALUES (?, ?)').run(10, 'W4 timeline')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import type { TimelineEntry } from '../../../shared/timeline'
|
||||
import type { SqliteDb } from '../types'
|
||||
|
||||
function mapRow(row: Record<string, unknown>): TimelineEntry {
|
||||
return {
|
||||
id: row.id as string,
|
||||
kind: 'manual',
|
||||
title: row.title as string,
|
||||
storyTime: (row.story_time as string) ?? null,
|
||||
sortOrder: row.sort_order as number,
|
||||
notes: (row.notes as string) ?? ''
|
||||
}
|
||||
}
|
||||
|
||||
export class TimelineRepository {
|
||||
constructor(private db: SqliteDb) {}
|
||||
|
||||
listManual(): TimelineEntry[] {
|
||||
return (
|
||||
this.db
|
||||
.prepare('SELECT * FROM timeline_events ORDER BY sort_order ASC, created_at ASC')
|
||||
.all() as Record<string, unknown>[]
|
||||
).map(mapRow)
|
||||
}
|
||||
|
||||
upsert(input: {
|
||||
id?: string
|
||||
title: string
|
||||
storyTime?: string | null
|
||||
sortOrder?: number
|
||||
notes?: string
|
||||
}): TimelineEntry {
|
||||
const id = input.id ?? randomUUID()
|
||||
const existing = this.db.prepare('SELECT id FROM timeline_events WHERE id = ?').get(id)
|
||||
if (existing) {
|
||||
this.db
|
||||
.prepare(
|
||||
`UPDATE timeline_events SET title = ?, story_time = ?, sort_order = ?, notes = ? WHERE id = ?`
|
||||
)
|
||||
.run(
|
||||
input.title,
|
||||
input.storyTime ?? null,
|
||||
input.sortOrder ?? 0,
|
||||
input.notes ?? '',
|
||||
id
|
||||
)
|
||||
} else {
|
||||
this.db
|
||||
.prepare(
|
||||
`INSERT INTO timeline_events (id, title, story_time, sort_order, notes) VALUES (?, ?, ?, ?, ?)`
|
||||
)
|
||||
.run(id, input.title, input.storyTime ?? null, input.sortOrder ?? 0, input.notes ?? '')
|
||||
}
|
||||
return mapRow(this.db.prepare('SELECT * FROM timeline_events WHERE id = ?').get(id) as Record<string, unknown>)
|
||||
}
|
||||
|
||||
delete(id: string): void {
|
||||
this.db.prepare('DELETE FROM timeline_events WHERE id = ?').run(id)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
CREATE TABLE IF NOT EXISTS timeline_events (
|
||||
id TEXT PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
story_time TEXT,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
notes TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
Reference in New Issue
Block a user