{"ok":true,"site":{"host":"viberoom.sh","url":"https://viberoom.sh/","type":"live","desc":"A RAW photo library and editor that runs entirely in your browser — your photos never leave your machine.","tools":[{"name":"list_images","kind":"answer","impl":"imperative","description":"List images in the open library with optional filters. Returns each image's id, which all other tools take.","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{"rating_gte":{"type":"integer","minimum":0,"maximum":5,"description":"Only images rated >= this (0-5)"},"flag":{"type":["string","null"],"enum":["pick","reject","none",null],"description":"'pick', 'reject', or 'none' for unflagged"},"keyword":{"type":"string","description":"Exact keyword, case-insensitive"},"camera":{"type":"string","description":"Substring match on camera model"},"lens":{"type":"string","description":"Substring match on lens"},"iso_gte":{"type":"integer","description":"Minimum ISO"},"iso_lte":{"type":"integer","description":"Maximum ISO"},"taken_after":{"type":"string","description":"'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' against EXIF capture time"},"taken_before":{"type":"string","description":"'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' against EXIF capture time"},"q":{"type":"string","description":"Substring match on filename"},"folder":{"type":"string","description":"Relative-path prefix"},"ext":{"type":"string","description":"File extension, e.g. \"cr2\""},"has_edits":{"type":"boolean","description":"Only images with (or without) an edit recipe"},"sort":{"type":"string","enum":["filename","mtime","rating","taken_at"],"default":"filename"},"order":{"type":"string","enum":["asc","desc"],"default":"asc"},"limit":{"type":"integer","minimum":1,"maximum":1000,"default":200},"offset":{"type":"integer","minimum":0,"default":0}}},"hasHandler":true,"hasExecute":true,"__source":"polyfill","executable":true,"handlerField":"execute"},{"name":"get_image","kind":"answer","impl":"imperative","description":"Full metadata for one image: path, EXIF, rating, flag, edit status.","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{"image_id":{"type":"string","description":"Image id, as returned by list_images"}},"required":["image_id"]},"hasHandler":true,"hasExecute":true,"__source":"polyfill","executable":true,"handlerField":"execute"},{"name":"get_current_image","kind":"answer","impl":"imperative","description":"The image the user currently has selected/open in Viberoom (or image_id: null if nothing is selected). Use this when the user says 'this image' / 'my current image'.","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{}},"hasHandler":true,"hasExecute":true,"__source":"polyfill","executable":true,"handlerField":"execute"},{"name":"set_rating","kind":"act","impl":"imperative","description":"Set the star rating, 0 (unrated) to 5.","inputSchema":{"type":"object","properties":{"image_id":{"type":"string","description":"Image id, as returned by list_images"},"rating":{"type":"integer","minimum":0,"maximum":5,"description":"Stars, 0-5"}},"required":["image_id","rating"]},"hasHandler":true,"hasExecute":true,"__source":"polyfill","executable":true,"handlerField":"execute"},{"name":"set_flag","kind":"act","impl":"imperative","description":"Flag an image as 'pick' or 'reject', or null to unflag.","inputSchema":{"type":"object","properties":{"image_id":{"type":"string","description":"Image id, as returned by list_images"},"flag":{"type":["string","null"],"enum":["pick","reject",null]}},"required":["image_id","flag"]},"hasHandler":true,"hasExecute":true,"__source":"polyfill","executable":true,"handlerField":"execute"},{"name":"get_recipe","kind":"answer","impl":"imperative","description":"Get the image's current non-destructive edit recipe (JSON).","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{"image_id":{"type":"string","description":"Image id, as returned by list_images"}},"required":["image_id"]},"hasHandler":true,"hasExecute":true,"__source":"polyfill","executable":true,"handlerField":"execute"},{"name":"update_recipe","kind":"act","impl":"imperative","description":"Merge a partial recipe into the image's edit recipe (the usual way to edit). Only supply the fields you want to change; everything else keeps its value. Lists (toneCurve points, masks) replace wholesale, so send the full masks array when editing masks. Ranges (Lightroom-style):\n- whiteBalance: {temp: 2000-50000 Kelvin (null=as-shot), tint: -150..150 (+magenta)}\n- tone: {exposure: -5..5 EV, contrast/highlights/shadows/whites/blacks: -100..100,\n         texture/clarity/dehaze: -100..100,\n         toneCurve: {points: [[in,out],...] 0-255 increasing; red/green/blue: same}}\n- color: {saturation/vibrance: -100..100,\n          hsl: {red|orange|yellow|green|aqua|blue|purple|magenta:\n                {hue/saturation/luminance: -100..100}},\n          grading: {shadows|midtones|highlights: {hue: 0-360, saturation: 0-100,\n                     luminance: -100..100}, blending: 0-100, balance: -100..100}}\n- detail: {sharpening: {amount: 0-150, radius: 0.5-3, detail: 0-100},\n           noiseReduction: {luminance: 0-100, color: 0-100}}\n- geometry: {rotate: -45..45 deg, orientation: 0|90|180|270, flipH/flipV: bool,\n             perspective: {vertical/horizontal: -100..100, scale: 50-150},\n             crop: {left,top,right,bottom: 0-1 normalized}}\n- lens: {distortion: -100..100, vignette: -100..100, caRed/caBlue: -100..100,\n         defringe: {amount: 0-100}} — applied first, pre-crop\n- effects: {vignette: {amount: -100..100 (negative darkens), midpoint/feather: 0-100,\n            roundness: -100..100}, grain: {amount: 0-100, size: 0-100}}\n- masks: local adjustments; a list of {type, ...geometry, invert, opacity: 0-100,\n         adjustments: {exposure: -5..5, contrast/highlights/shadows/temp/tint/\n         saturation/clarity/dehaze/sharpness: -100..100}}. Coordinates are\n         normalized 0-1 in the RENDERED (post-crop) frame. Types:\n         {type: 'linear', start: [x,y], end: [x,y]}\n         {type: 'radial', center: [x,y], radiusX/radiusY: 0-2, feather: 0-100}\n         {type: 'luminance', lumMin/lumMax: 0-100, feather: 0-100}\n         {type: 'color', hue: 0-360, range: 5-180}\n         {type: 'brush', strokes: [{points: [[x,y],...], radius: 0-0.5,\n          feather/flow: 0-100, erase: bool}]}\nNot every field renders in the browser yet — render_preview reports which engine drew the pixels it returns.","inputSchema":{"type":"object","properties":{"image_id":{"type":"string","description":"Image id, as returned by list_images"},"patch":{"type":"object","description":"Partial recipe to merge; see the tool description for ranges"}},"required":["image_id","patch"]},"hasHandler":true,"hasExecute":true,"__source":"polyfill","executable":true,"handlerField":"execute"},{"name":"set_recipe","kind":"act","impl":"imperative","description":"Replace the entire edit recipe. Omitted fields reset to defaults, so this discards any edit not present in `recipe` — prefer update_recipe unless you mean to start over. Asks the user to confirm.","annotations":{"destructiveHint":true},"inputSchema":{"type":"object","properties":{"image_id":{"type":"string","description":"Image id, as returned by list_images"},"recipe":{"type":"object","description":"The complete replacement recipe"}},"required":["image_id","recipe"]},"hasHandler":true,"hasExecute":true,"__source":"polyfill","executable":true,"handlerField":"execute"},{"name":"reset_recipe","kind":"act","impl":"imperative","description":"Remove all edits, restoring the image to its unedited state. Asks the user to confirm.","annotations":{"destructiveHint":true},"inputSchema":{"type":"object","properties":{"image_id":{"type":"string","description":"Image id, as returned by list_images"}},"required":["image_id"]},"hasHandler":true,"hasExecute":true,"__source":"polyfill","executable":true,"handlerField":"execute"},{"name":"render_preview","kind":"act","impl":"imperative","description":"Render the image WITH its current edits applied and return the JPEG (base64) so you can visually inspect the result. size = longest edge in px (256-4096). `rendered` says which engine drew it: 'server' (full recipe), 'gpu' (what the browser shader supports), or 'original' (the untouched file, when nothing could be applied).","annotations":{"readOnlyHint":true},"inputSchema":{"type":"object","properties":{"image_id":{"type":"string","description":"Image id, as returned by list_images"},"size":{"type":"integer","minimum":256,"maximum":4096,"default":1024}},"required":["image_id"]},"hasHandler":true,"hasExecute":true,"__source":"polyfill","executable":true,"handlerField":"execute"}],"category":"Media & Personal","favicon":"/favicons/viberoom.sh.png","apiSurface":"polyfill","toolCount":10}}