import { z } from 'zod'; import { webScreenshotArtifactSchema } from '../webScreenshot.js'; const sampling = { step_id:z.string().min(1).max(80), frames:z.number().int().min(0).max(24).default(1), interval_ms:z.number().int().min(16).max(500).default(50) }; const target = {selector:z.string().min(1).max(500),index:z.number().int().min(0).max(100).optional()}; export const electronVisualProbeInputSchema = z.strictObject({ include_fonts:z.boolean().default(false), include_message_content:z.boolean().default(false), media_selectors:z.array(z.string().trim().min(1).max(500)).max(4).default([]), track_selectors:z.array(z.string().min(1).max(500)).max(12).default([]), actions:z.array(z.discriminatedUnion('kind',[ z.strictObject({...sampling,...target,kind:z.literal('hover')}), z.strictObject({...sampling,...target,kind:z.literal('click'),restore:z.boolean().default(false)}), z.strictObject({...sampling,...target,kind:z.literal('text'),text:z.string().max(2048).refine(value=>!/[\u0000-\u001f\u007f]/u.test(value),'Single-line text only')}), z.strictObject({...sampling,...target,kind:z.literal('scroll'),delta_y:z.number().min(-3000).max(3000),restore:z.boolean().default(false)}), z.strictObject({...sampling,kind:z.literal('key'),selector:z.string().min(1).max(500).optional(),index:z.number().int().min(0).max(100).optional(),key:z.enum(['Escape','Enter','Tab','ArrowDown','ArrowUp','ArrowLeft','ArrowRight','Home','End',' ','+','-','0']),modifiers:z.number().int().min(0).max(15).default(0)}), z.strictObject({...sampling,kind:z.literal('wait'),duration_ms:z.number().int().min(0).max(2000)}), z.strictObject({...sampling,kind:z.literal('viewport'),width:z.number().int().min(160).max(3840),height:z.number().int().min(160).max(2160)}) ,z.strictObject({...sampling,kind:z.literal('media'),reduced_motion:z.enum(['reduce','no-preference'])}) ])).max(12).default([]) }).superRefine((value,ctx)=>{ if(value.actions.reduce((n,a)=>n+a.frames,0)>64)ctx.addIssue({code:'custom',message:'At most 64 scenario frames'}); if(value.actions.reduce((n,a)=>n+a.frames*a.interval_ms+(a.duration_ms??0),0)>15000)ctx.addIssue({code:'custom',message:'Declared sampling and waits must fit 15 seconds'}); if(new Set(value.actions.map(a=>a.step_id)).size!==value.actions.length)ctx.addIssue({code:'custom',message:'Step IDs must be unique'}); if(new Set(value.media_selectors).size!==value.media_selectors.length)ctx.addIssue({code:'custom',message:'Media selectors must be unique'}); }); const mediaIdentity = { selector:z.string(),frame_id:z.string(),node_index:z.number().int().optional(),backend_node_id:z.number().int().optional(), selected_node:z.object({node_name:z.literal('IMG'),attributes:z.record(z.string(),z.string()),current_source_url:z.string().nullable()}).optional(), visibility:z.object({status:z.enum(['observed','covered','unknown']),reason:z.string().optional()}).optional(), visibility_samples:z.array(z.object({x:z.number(),y:z.number(),hit_backend_node_id:z.number().int().optional(),hit_node_index:z.number().int().optional(),hit_node_name:z.string().optional(),hit_class:z.string().optional()})).max(9).optional(), source:z.object({url:z.string().nullable(),url_sha256:z.string().regex(/^[a-f0-9]{64}$/),selection:z.enum(['current_source_url','src']),mime_type:z.string()}).optional() }; export const electronMediaAssetsSchema = z.object({ items:z.array(z.discriminatedUnion('status',[ z.object({...mediaIdentity,status:z.literal('exported'),artifact:z.object({mime_type:z.enum(['image/png','image/jpeg','image/gif','image/webp']),bytes:z.number().int().positive().max(2097152),sha256:z.string().regex(/^[a-f0-9]{64}$/),data_base64:z.string().max(2796204)})}), z.object({...mediaIdentity,status:z.literal('unknown'),reason:z.string()}) ])).max(4), total_bytes:z.number().int().min(0).max(8388608), limits:z.object({max_assets:z.literal(4),max_asset_bytes:z.literal(2097152),max_total_bytes:z.literal(8388608)}), limitations:z.array(z.string()) }); export const platformFontSampleSchema = z.object({category:z.string(),text:z.string().max(256),fontFamily:z.string(),codeContext:z.enum(['inline','block']).optional(),platformFonts:z.array(z.object({familyName:z.string(),postScriptName:z.string(),isCustomFont:z.boolean(),glyphCount:z.number().int().min(0)}))}); export const visualProbeOutputSchema = z.object({ status:z.enum(['completed','failed']), steps:z.array(z.object({step_id:z.string(),kind:z.string(),status:z.enum(['completed','failed']),error:z.string().nullable(),action_completed_at_ms:z.number().optional()})), frames:z.array(z.object({step_id:z.string(),at_ms:z.number(),artifact:webScreenshotArtifactSchema,regions:z.array(z.object({selector:z.string(),bounds:z.tuple([z.number(),z.number(),z.number(),z.number()]).nullable()}))})), restoration:z.array(z.object({kind:z.string(),status:z.enum(['completed','failed']),error:z.string().nullable()})), limitations:z.array(z.string()) });