feat: configure canvas pages sent for note analysis
Canvas analysis previously sent every page. Add a Magic Notes setting for 1-8 canvas pages, defaulting to 1 for new and historical settings. Select the first N pages in current canvas order before capture and use the same text/image range for saved entries, drafts and canvas-source todos. Keep full 50-page saving/export, expose the same setting through config tools, and use the shared settings select with an inline help icon. Include pagination, persistence, production IPC and isolated Electron regressions, including text-only input and scaled page boundaries. Validation: focused feature tests and isolated Electron regressions passed; the final compact UI passed 14 tests. Typecheck and lint were reported passing by the coordinating agent. No full-suite pass is claimed because the completed full test command exposed no summary. No live model calls were made for this commit preparation. Release note: 魔法笔记新增“发送画布页数”设置,可选 1~8 页,默认 1 页,按当前画布顺序发送前 N 页的文字和图片;标题旁问号可查看说明,保存和导出完整画布不受影响。
This commit is contained in:
@@ -26,6 +26,7 @@ export const magicNoteCommentModeSchema = z.enum([
|
||||
])
|
||||
|
||||
export type MagicNoteCommentMode = z.infer<typeof magicNoteCommentModeSchema>
|
||||
export const magicNoteCanvasPageCountSchema = z.number().int().min(1).max(8)
|
||||
|
||||
export const updateSourceSchema = z.enum(['github', 'mirror'])
|
||||
export type UpdateSource = z.infer<typeof updateSourceSchema>
|
||||
@@ -59,7 +60,8 @@ const applicationPreferencesSchema = z
|
||||
magicNotesEnabled: z.boolean().default(true),
|
||||
magicNotesShowIncompleteTodoCount: z.boolean().default(true),
|
||||
magicNoteCommentMode: magicNoteCommentModeSchema.default('immediate'),
|
||||
magicNoteCommentFormat: magicNoteCommentFormatSchema.default('combined')
|
||||
magicNoteCommentFormat: magicNoteCommentFormatSchema.default('combined'),
|
||||
magicNoteCanvasPageCount: magicNoteCanvasPageCountSchema.default(1)
|
||||
})
|
||||
.strict()
|
||||
|
||||
@@ -77,7 +79,8 @@ export const applicationSettingsUpdateSchema = applicationPreferencesSchema
|
||||
magicNotesEnabled: z.boolean().optional(),
|
||||
magicNotesShowIncompleteTodoCount: z.boolean().optional(),
|
||||
magicNoteCommentMode: magicNoteCommentModeSchema.optional(),
|
||||
magicNoteCommentFormat: magicNoteCommentFormatSchema.optional()
|
||||
magicNoteCommentFormat: magicNoteCommentFormatSchema.optional(),
|
||||
magicNoteCanvasPageCount: magicNoteCanvasPageCountSchema.optional()
|
||||
})
|
||||
.refine((input) => Object.keys(input).length > 0, {
|
||||
message: 'At least one application setting is required'
|
||||
|
||||
@@ -62,7 +62,8 @@ describe('GoodBuddy configuration contracts', () => {
|
||||
operation: 'application.update',
|
||||
updates: {
|
||||
magicNotesEnabled: true,
|
||||
magicNoteCommentMode: 'after-save-manual'
|
||||
magicNoteCommentMode: 'after-save-manual',
|
||||
magicNoteCanvasPageCount: 8
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -161,7 +162,8 @@ describe('GoodBuddy configuration contracts', () => {
|
||||
magicNotesEnabled: true,
|
||||
magicNotesShowIncompleteTodoCount: true,
|
||||
magicNoteCommentMode: 'immediate',
|
||||
magicNoteCommentFormat: 'combined'
|
||||
magicNoteCommentFormat: 'combined',
|
||||
magicNoteCanvasPageCount: 1
|
||||
},
|
||||
skills: [],
|
||||
mcpServers: [
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export function selectMagicNotePages<T>(pages: readonly T[], limit?: number): T[]
|
||||
@@ -0,0 +1,6 @@
|
||||
export function selectMagicNotePages(pages, limit = 1) {
|
||||
if (!Number.isInteger(limit) || limit < 1 || limit > 8) {
|
||||
throw new RangeError('Canvas analysis page count must be an integer from 1 to 8');
|
||||
}
|
||||
return pages.slice(0, limit);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { expect, it } from 'vitest'
|
||||
import { selectMagicNotePages } from './magic-note-pages.mjs'
|
||||
|
||||
it('selects the current prefix, defaulting to one, without changing the saved page list', () => {
|
||||
const pages = Array.from({ length: 50 }, (_, index) => ({ id: `page-${50 - index}` }))
|
||||
expect(selectMagicNotePages(pages)).toEqual([pages[0]])
|
||||
expect(selectMagicNotePages(pages, 8)).toEqual(pages.slice(0, 8))
|
||||
expect(selectMagicNotePages(pages.slice(0, 2), 8)).toEqual(pages.slice(0, 2))
|
||||
expect(selectMagicNotePages([])).toEqual([])
|
||||
expect(pages).toHaveLength(50)
|
||||
expect(selectMagicNotePages(pages)[0]).toBe(pages[0])
|
||||
})
|
||||
|
||||
it.each([0, 9, -1, 1.5, NaN, Infinity])('rejects invalid analysis page limits: %s', limit => {
|
||||
expect(() => selectMagicNotePages([], limit)).toThrow(RangeError)
|
||||
})
|
||||
@@ -360,6 +360,7 @@ export const magicNoteAnalysisOptionsSchema = z
|
||||
requestId: z.string().uuid(),
|
||||
direction: magicNoteCommentDirectionSchema,
|
||||
format: magicNoteCommentFormatSchema,
|
||||
canvasPageText: z.array(z.object({ pageId: z.string().min(1), text: z.string().max(20_001) }).strict()).max(8).optional(),
|
||||
canvasImages: z.array(z.object({ pageId: z.string().min(1), dataUrl: z.string().regex(/^data:image\/(?:jpeg|png);base64,[A-Za-z0-9+/]+={0,2}$/) }).strict()).max(50).optional()
|
||||
})
|
||||
.strict()
|
||||
@@ -374,8 +375,8 @@ export const magicNoteAnalyzeSchema = z
|
||||
...magicNoteAnalysisOptionsSchema.shape
|
||||
})
|
||||
.strict()
|
||||
.refine((input) => !input.canvasImages?.length || input.expectedRevision !== undefined, {
|
||||
message: '画布截图必须提供记录版本', path: ['expectedRevision']
|
||||
.refine((input) => !(input.canvasImages?.length || input.canvasPageText?.length) || input.expectedRevision !== undefined, {
|
||||
message: '画布截图或分页文字必须提供记录版本', path: ['expectedRevision']
|
||||
})
|
||||
|
||||
export type MagicNoteEntryAnalysisOptions = MagicNoteAnalysisOptions & { expectedRevision?: number }
|
||||
@@ -394,8 +395,8 @@ export const magicTodoIdSchema = z
|
||||
...magicNoteAnalysisOptionsSchema.shape
|
||||
})
|
||||
.strict()
|
||||
.refine((input) => !input.canvasImages?.length || input.sourceEntryRevision !== undefined, {
|
||||
message: '画布截图必须提供来源记录版本', path: ['sourceEntryRevision']
|
||||
.refine((input) => !(input.canvasImages?.length || input.canvasPageText?.length) || input.sourceEntryRevision !== undefined, {
|
||||
message: '画布截图或分页文字必须提供来源记录版本', path: ['sourceEntryRevision']
|
||||
})
|
||||
|
||||
export type MagicTodoAnalysisOptions = MagicNoteAnalysisOptions & { sourceEntryRevision?: number }
|
||||
|
||||
Reference in New Issue
Block a user