返回 oh-my-ppt
schema.ts
根目录 / src / main / db / schema.ts
1 import { sqliteTable, text, integer, real, index, uniqueIndex } from 'drizzle-orm/sqlite-core'
2
3 export const sessions = sqliteTable('sessions', {
4 id: text('id').primaryKey(),
5 title: text('title').notNull(),
6 topic: text('topic'),
7 styleId: text('style_id'),
8 pageCount: integer('page_count'),
9 slideSizeId: text('slide_size_id').notNull().default('wide-16-9'),
10 slideWidth: integer('slide_width').notNull().default(1600),
11 slideHeight: integer('slide_height').notNull().default(900),
12 referenceDocumentPath: text('reference_document_path'),
13 status: text('status').notNull().default('active'),
14 provider: text('provider').notNull(),
15 model: text('model').notNull(),
16 createdAt: integer('created_at').notNull(),
17 updatedAt: integer('updated_at').notNull(),
18 metadata: text('metadata'),
19 designContract: text('design_contract'),
20 currentOperationId: text('current_operation_id'),
21 currentCommit: text('current_commit')
22 })
23
24 export const messages = sqliteTable('messages', {
25 id: text('id').primaryKey(),
26 sessionId: text('session_id').notNull(),
27 chatScope: text('chat_scope').notNull().default('main'),
28 pageId: text('page_id'),
29 selector: text('selector'),
30 imagePaths: text('image_paths'),
31 videoPaths: text('video_paths'),
32 role: text('role').notNull(),
33 content: text('content').notNull(),
34 type: text('type'),
35 toolName: text('tool_name'),
36 toolCallId: text('tool_call_id'),
37 tokenCount: integer('token_count'),
38 runModel: text('run_model'),
39 createdAt: integer('created_at').notNull()
40 })
41
42 export const modelUsageEvents = sqliteTable(
43 'model_usage_events',
44 {
45 id: text('id').primaryKey(),
46 provider: text('provider').notNull(),
47 model: text('model').notNull(),
48 modelConfigId: text('model_config_id'),
49 inputTokens: integer('input_tokens').notNull().default(0),
50 outputTokens: integer('output_tokens').notNull().default(0),
51 totalTokens: integer('total_tokens').notNull().default(0),
52 usageSource: text('usage_source').notNull().default('provider'),
53 createdAt: integer('created_at').notNull()
54 },
55 (table) => ({
56 modelUsageCreatedIdx: index('idx_model_usage_events_created').on(table.createdAt),
57 modelUsageModelIdx: index('idx_model_usage_events_model').on(
58 table.provider,
59 table.model,
60 table.createdAt
61 )
62 })
63 )
64
65 export const projects = sqliteTable('projects', {
66 id: text('id').primaryKey(),
67 sessionId: text('session_id').notNull(),
68 title: text('title').notNull(),
69 outputPath: text('output_path').notNull(),
70 rootPath: text('root_path'),
71 fileCount: integer('file_count').default(0),
72 totalSize: integer('total_size').default(0),
73 status: text('status').notNull().default('draft'),
74 createdAt: integer('created_at').notNull(),
75 updatedAt: integer('updated_at').notNull()
76 })
77
78 export const generationRuns = sqliteTable('generation_runs', {
79 id: text('id').primaryKey(),
80 sessionId: text('session_id')
81 .notNull()
82 .references(() => sessions.id, { onDelete: 'cascade' }),
83 mode: text('mode').notNull().default('generate'),
84 status: text('status').notNull().default('running'),
85 totalPages: integer('total_pages').notNull().default(0),
86 error: text('error'),
87 metadata: text('metadata'),
88 animationPreferences: text('animation_preferences'),
89 modelConfigId: text('model_config_id'),
90 createdAt: integer('created_at').notNull(),
91 updatedAt: integer('updated_at').notNull()
92 })
93
94 export const sessionJobs = sqliteTable(
95 'session_jobs',
96 {
97 id: text('id')
98 .primaryKey()
99 .references(() => generationRuns.id, { onDelete: 'cascade' }),
100 sessionId: text('session_id')
101 .notNull()
102 .references(() => sessions.id, { onDelete: 'cascade' }),
103 kind: text('kind').notNull(),
104 previousSessionStatus: text('previous_session_status').notNull(),
105 targetPageId: text('target_page_id'),
106 targetPageNumber: integer('target_page_number'),
107 selector: text('selector'),
108 totalPages: integer('total_pages'),
109 status: text('status').notNull(),
110 abortReason: text('abort_reason'),
111 createdAt: integer('created_at').notNull(),
112 activatedAt: integer('activated_at'),
113 updatedAt: integer('updated_at').notNull(),
114 finishedAt: integer('finished_at')
115 },
116 (table) => ({
117 sessionJobsSessionStatusIdx: index('idx_session_jobs_session_status').on(
118 table.sessionId,
119 table.status,
120 table.updatedAt
121 ),
122 sessionJobsStatusIdx: index('idx_session_jobs_status').on(table.status, table.updatedAt)
123 })
124 )
125
126 export const generationPages = sqliteTable('generation_pages', {
127 id: text('id').primaryKey(),
128 runId: text('run_id')
129 .notNull()
130 .references(() => generationRuns.id, { onDelete: 'cascade' }),
131 sessionId: text('session_id')
132 .notNull()
133 .references(() => sessions.id, { onDelete: 'cascade' }),
134 pageId: text('page_id').notNull(),
135 pageNumber: integer('page_number').notNull(),
136 title: text('title').notNull(),
137 contentOutline: text('content_outline'),
138 layoutIntent: text('layout_intent'),
139 htmlPath: text('html_path'),
140 status: text('status').notNull().default('pending'),
141 error: text('error'),
142 retryCount: integer('retry_count').notNull().default(0),
143 createdAt: integer('created_at').notNull(),
144 updatedAt: integer('updated_at').notNull()
145 })
146
147 export const sessionPages = sqliteTable(
148 'session_pages',
149 {
150 id: text('id').primaryKey(),
151 sessionId: text('session_id').notNull(),
152 legacyPageId: text('legacy_page_id'),
153 fileSlug: text('file_slug').notNull(),
154 pageNumber: integer('page_number').notNull(),
155 title: text('title').notNull(),
156 htmlPath: text('html_path').notNull(),
157 status: text('status').notNull().default('pending'),
158 error: text('error'),
159 createdAt: integer('created_at').notNull(),
160 updatedAt: integer('updated_at').notNull(),
161 deletedAt: integer('deleted_at')
162 },
163 (table) => ({
164 sessionPageNumberIdx: index('idx_session_pages_session_number').on(
165 table.sessionId,
166 table.pageNumber
167 )
168 })
169 )
170
171 export const sourcePageSkeletons = sqliteTable(
172 'source_page_skeletons',
173 {
174 id: text('id').primaryKey(),
175 sessionId: text('session_id')
176 .notNull()
177 .references(() => sessions.id, { onDelete: 'cascade' }),
178 pageNumber: integer('page_number').notNull(),
179 title: text('title').notNull(),
180 role: text('role').notNull().default('content'),
181 sourceDocumentPath: text('source_document_path').notNull(),
182 sourceDocumentName: text('source_document_name'),
183 sourceHeading: text('source_heading').notNull(),
184 headingLevel: integer('heading_level').notNull(),
185 lineStart: integer('line_start').notNull(),
186 lineEnd: integer('line_end').notNull(),
187 reason: text('reason'),
188 confidence: text('confidence').notNull().default('high'),
189 createdAt: integer('created_at').notNull(),
190 updatedAt: integer('updated_at').notNull()
191 },
192 (table) => ({
193 sourcePageSkeletonSessionIdx: index('idx_source_page_skeletons_session').on(
194 table.sessionId,
195 table.pageNumber
196 )
197 })
198 )
199
200 export const settings = sqliteTable('settings', {
201 key: text('key').primaryKey(),
202 value: text('value').notNull(),
203 updatedAt: integer('updated_at').notNull()
204 })
205
206 export const modelConfigs = sqliteTable('model_configs', {
207 id: text('id').primaryKey(),
208 name: text('name').notNull(),
209 provider: text('provider').notNull(),
210 model: text('model').notNull(),
211 apiKey: text('api_key').notNull().default(''),
212 baseUrl: text('base_url').notNull().default(''),
213 maxTokens: integer('max_tokens').notNull().default(4096),
214 disableTemperature: integer('disable_temperature').notNull().default(0),
215 thinkingParameterMode: text('thinking_parameter_mode').notNull().default('auto'),
216 active: integer('active').notNull().default(0),
217 createdAt: integer('created_at').notNull(),
218 updatedAt: integer('updated_at').notNull()
219 })
220
221 export const imageModelConfigs = sqliteTable('image_model_configs', {
222 id: text('id').primaryKey(),
223 name: text('name').notNull(),
224 provider: text('provider').notNull(),
225 modelConfig: text('model_config').notNull().default('{}'),
226 active: integer('active').notNull().default(0),
227 createdAt: integer('created_at').notNull(),
228 updatedAt: integer('updated_at').notNull()
229 })
230
231 export const imageGenerationHistories = sqliteTable(
232 'image_generation_histories',
233 {
234 id: text('id').primaryKey(),
235 sessionId: text('session_id').notNull(),
236 pageId: text('page_id').notNull(),
237 prompt: text('prompt').notNull(),
238 imagePaths: text('image_paths').notNull().default('[]'),
239 modelConfigId: text('model_config_id').notNull(),
240 provider: text('provider').notNull(),
241 model: text('model').notNull(),
242 createdAt: integer('created_at').notNull()
243 },
244 (table) => ({
245 imageGenerationHistoriesSessionIdx: index('idx_image_generation_histories_session').on(
246 table.sessionId,
247 table.createdAt
248 ),
249 imageGenerationHistoriesPageIdx: index('idx_image_generation_histories_page').on(
250 table.sessionId,
251 table.pageId,
252 table.createdAt
253 )
254 })
255 )
256
257 export const memorySummaries = sqliteTable('memory_summaries', {
258 id: text('id').primaryKey(),
259 sessionId: text('session_id').notNull(),
260 messageRangeStart: integer('message_range_start').notNull(),
261 messageRangeEnd: integer('message_range_end').notNull(),
262 summary: text('summary').notNull(),
263 tokenCount: integer('token_count'),
264 createdAt: integer('created_at').notNull()
265 })
266
267 export const userPreferences = sqliteTable('user_preferences', {
268 key: text('key').primaryKey(),
269 value: text('value').notNull(),
270 confidence: real('confidence').default(1.0),
271 sourceSessions: text('source_sessions'),
272 createdAt: integer('created_at').notNull(),
273 updatedAt: integer('updated_at').notNull(),
274 lastUsedAt: integer('last_used_at')
275 })
276
277 export const styles = sqliteTable('styles', {
278 id: text('id').primaryKey(),
279 style: text('style').notNull().unique(),
280 styleName: text('style_name').notNull(),
281 styleNameZh: text('style_name_zh').notNull().default(''),
282 styleNameEn: text('style_name_en').notNull().default(''),
283 description: text('description').notNull().default(''),
284 category: text('category').notNull().default(''),
285 aliases: text('aliases').notNull().default('[]'),
286 source: text('source').notNull().default('custom'),
287 styleSkill: text('style_skill').notNull().default(''),
288 version: text('version').notNull().default('1.0.0'),
289 styleCase: text('style_case').notNull().default(''),
290 packageDir: text('package_dir').notNull().default(''),
291 active: integer('active', { mode: 'boolean' }).notNull().default(true),
292 favoriteAt: integer('favorite_at'),
293 createdAt: integer('created_at').notNull(),
294 updatedAt: integer('updated_at').notNull()
295 })
296
297 export const thumbnails = sqliteTable(
298 'thumbnails',
299 {
300 key: text('key').primaryKey(),
301 resourceType: text('resource_type').notNull(),
302 resourceId: text('resource_id').notNull(),
303 variant: text('variant').notNull().default('default'),
304 sourcePath: text('source_path').notNull(),
305 sourceMtimeMs: integer('source_mtime_ms').notNull().default(0),
306 signature: text('signature').notNull().default(''),
307 thumbnailPath: text('thumbnail_path').notNull().default(''),
308 status: text('status').notNull().default('queued'),
309 error: text('error'),
310 createdAt: integer('created_at').notNull(),
311 updatedAt: integer('updated_at').notNull()
312 },
313 (table) => ({
314 resourceVariantUniqueIdx: uniqueIndex('thumbnails_resource_variant_unique').on(
315 table.resourceType,
316 table.resourceId,
317 table.variant
318 ),
319 statusIdx: index('thumbnails_status_idx').on(table.status, table.updatedAt)
320 })
321 )
322
323 export const sessionStyleSnapshots = sqliteTable(
324 'session_style_snapshots',
325 {
326 id: text('id').primaryKey(),
327 sessionId: text('session_id')
328 .notNull()
329 .references(() => sessions.id, { onDelete: 'cascade' }),
330 styleId: text('style_id').notNull(),
331 styleKey: text('style_key').notNull(),
332 styleName: text('style_name').notNull(),
333 styleNameZh: text('style_name_zh').notNull().default(''),
334 styleNameEn: text('style_name_en').notNull().default(''),
335 description: text('description').notNull().default(''),
336 category: text('category').notNull().default(''),
337 aliases: text('aliases').notNull().default('[]'),
338 source: text('source').notNull(),
339 version: text('version').notNull().default('1.0.0'),
340 styleCase: text('style_case').notNull().default(''),
341 packageDir: text('package_dir').notNull().default(''),
342 styleSkill: text('style_skill').notNull().default(''),
343 createdAt: integer('created_at').notNull()
344 },
345 (table) => ({
346 sessionIdUniqueIdx: uniqueIndex('session_style_snapshots_session_id_unique').on(table.sessionId)
347 })
348 )
349
350 export const sessionOperations = sqliteTable('session_operations', {
351 id: text('id').primaryKey(),
352 sessionId: text('session_id')
353 .notNull()
354 .references(() => sessions.id, { onDelete: 'cascade' }),
355 type: text('type').notNull(),
356 status: text('status').notNull().default('completed'),
357 scope: text('scope'),
358 prompt: text('prompt'),
359 parentOperationId: text('parent_operation_id'),
360 beforeCommit: text('before_commit'),
361 afterCommit: text('after_commit'),
362 targetOperationId: text('target_operation_id'),
363 targetCommit: text('target_commit'),
364 changedFilesJson: text('changed_files_json').notNull().default('[]'),
365 changedPagesJson: text('changed_pages_json').notNull().default('[]'),
366 trackedFilesJson: text('tracked_files_json').notNull().default('[]'),
367 metadataJson: text('metadata_json').notNull().default('{}'),
368 createdAt: integer('created_at').notNull(),
369 completedAt: integer('completed_at')
370 })
371
372 export const sessionOperationPages = sqliteTable(
373 'session_operation_pages',
374 {
375 id: text('id').primaryKey(),
376 operationId: text('operation_id')
377 .notNull()
378 .references(() => sessionOperations.id, { onDelete: 'cascade' }),
379 sessionId: text('session_id')
380 .notNull()
381 .references(() => sessions.id, { onDelete: 'cascade' }),
382 pageId: text('page_id').notNull(),
383 legacyPageId: text('legacy_page_id'),
384 fileSlug: text('file_slug').notNull(),
385 pageNumber: integer('page_number').notNull(),
386 title: text('title').notNull(),
387 htmlPath: text('html_path').notNull(),
388 status: text('status').notNull().default('pending'),
389 error: text('error'),
390 createdAt: integer('created_at').notNull(),
391 updatedAt: integer('updated_at').notNull()
392 },
393 (table) => ({
394 sessionOperationPagesOrderIdx: index('idx_session_operation_pages_order').on(
395 table.operationId,
396 table.pageNumber
397 ),
398 sessionOperationPagesSessionIdx: index('idx_session_operation_pages_session').on(
399 table.sessionId,
400 table.operationId
401 )
402 })
403 )
404
405 export const htmlEditDocuments = sqliteTable('html_edit_documents', {
406 id: text('id').primaryKey(),
407 title: text('title').notNull().default(''),
408 sourcePath: text('source_path'),
409 htmlPath: text('html_path').notNull(),
410 designWidth: integer('design_width').notNull().default(1280),
411 createdAt: integer('created_at').notNull(),
412 updatedAt: integer('updated_at').notNull()
413 })
414
415 export const htmlEditMessages = sqliteTable(
416 'html_edit_messages',
417 {
418 id: text('id').primaryKey(),
419 docId: text('doc_id')
420 .notNull()
421 .references(() => htmlEditDocuments.id, { onDelete: 'cascade' }),
422 role: text('role').notNull(),
423 content: text('content').notNull(),
424 intent: text('intent'),
425 planJson: text('plan_json'),
426 requiresConfirmation: integer('requires_confirmation').notNull().default(0),
427 selectedSelector: text('selected_selector'),
428 selectedLabel: text('selected_label'),
429 selectedElementTag: text('selected_element_tag'),
430 selectedElementText: text('selected_element_text'),
431 createdAt: integer('created_at').notNull()
432 },
433 (table) => ({
434 htmlEditMessagesDocIdx: index('idx_html_edit_messages_doc').on(table.docId, table.createdAt)
435 })
436 )
437
438 export const htmlEditVersions = sqliteTable(
439 'html_edit_versions',
440 {
441 id: text('id').primaryKey(),
442 docId: text('doc_id')
443 .notNull()
444 .references(() => htmlEditDocuments.id, { onDelete: 'cascade' }),
445 commitSha: text('commit_sha').notNull(),
446 message: text('message').notNull().default(''),
447 createdAt: integer('created_at').notNull()
448 },
449 (table) => ({
450 htmlEditVersionsDocIdx: index('idx_html_edit_versions_doc').on(table.docId, table.createdAt)
451 })
452 )
453
454 export type Session = typeof sessions.$inferSelect
455 export type Message = typeof messages.$inferSelect
456 export type ModelUsageEvent = typeof modelUsageEvents.$inferSelect
457 export type Project = typeof projects.$inferSelect
458 export type GenerationRun = typeof generationRuns.$inferSelect
459 export type GenerationPage = typeof generationPages.$inferSelect
460 export type SessionPage = typeof sessionPages.$inferSelect
461 export type SourcePageSkeleton = typeof sourcePageSkeletons.$inferSelect
462 export type ModelConfig = typeof modelConfigs.$inferSelect
463 export type ImageGenerationHistory = typeof imageGenerationHistories.$inferSelect
464 export type MemorySummary = typeof memorySummaries.$inferSelect
465 export type UserPreference = typeof userPreferences.$inferSelect
466 export type SessionOperation = typeof sessionOperations.$inferSelect
467 export type SessionOperationPage = typeof sessionOperationPages.$inferSelect
468 export type HtmlEditDocument = typeof htmlEditDocuments.$inferSelect
469 export type HtmlEditMessage = typeof htmlEditMessages.$inferSelect
470 export type HtmlEditVersion = typeof htmlEditVersions.$inferSelect
471
472 export type SessionStatus = 'active' | 'completed' | 'failed' | 'archived'
473 export type MessageRole = 'user' | 'assistant' | 'system' | 'tool'
474 export type MessageType = 'text' | 'tool_call' | 'tool_result' | 'stream_chunk'
475 export type ChatScope = 'main' | 'page'
476 export type GenerationRunStatus = 'running' | 'completed' | 'failed' | 'partial'
477 export type GenerationRunMode =
478 | 'generate'
479 | 'retry'
480 | 'edit'
481 | 'import'
482 | 'addPage'
483 | 'retrySinglePage'
484 | 'page-beautify'
485 export type GenerationPageStatus = 'pending' | 'running' | 'completed' | 'failed'
486 export type SessionPageStatus = 'completed' | 'failed' | 'pending'
487 export type SessionOperationType =
488 | 'generate'
489 | 'edit'
490 | 'addPage'
491 | 'retry'
492 | 'import'
493 | 'rollback'
494 | 'reorder'
495 | 'delete'
496 export type SessionOperationScope = 'session' | 'deck' | 'page' | 'selector' | 'shell'
497 export type SessionOperationStatus = 'committing' | 'completed' | 'failed' | 'noop'
498
498 lines TYPESCRIPT