Merge pull request #299 from jonvdveen/feat/resolution-condition

feat: Resolution condition for workflow IF/SWITCH nodes (megapixels, short/long edge)
This commit is contained in:
Varun Raj Manoharan
2026-07-10 19:35:14 +05:30
committed by GitHub
4 changed files with 47 additions and 1 deletions
@@ -27,6 +27,7 @@ const conditionTypeLabels: Record<ConditionType, string> = {
asset_type: "Asset Type",
iso_range: "ISO Range",
focal_length: "Focal Length",
resolution: "Resolution",
rating: "Rating",
is_favorited: "Favorited",
file_size: "File Size",
@@ -197,6 +198,25 @@ function ConditionFields({ condition, onChange }: { condition: ICondition; onCha
<Input className="h-7 text-xs w-16" type="number" min={1} max={5} placeholder="Max" value={condition.max ?? ""} onChange={(e) => onChange({ ...condition, max: parseInt(e.target.value) || undefined })} />
</div>
);
case "resolution": {
const unit = (condition.metric || "megapixels") === "megapixels" ? "MP" : "px";
return (
<div className="space-y-2">
<Select value={condition.metric || "megapixels"} onValueChange={(v) => onChange({ ...condition, metric: v })}>
<SelectTrigger className="h-7 text-xs"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="megapixels">Megapixels</SelectItem>
<SelectItem value="short_edge">Short Edge (px)</SelectItem>
<SelectItem value="long_edge">Long Edge (px)</SelectItem>
</SelectContent>
</Select>
<div className="flex items-center gap-2">
<Input className="h-7 text-xs" type="number" min={0} step="any" placeholder={`Min ${unit}`} value={condition.min ?? ""} onChange={(e) => onChange({ ...condition, min: e.target.value === "" ? undefined : parseFloat(e.target.value) })} />
<Input className="h-7 text-xs" type="number" min={0} step="any" placeholder={`Max ${unit}`} value={condition.max ?? ""} onChange={(e) => onChange({ ...condition, max: e.target.value === "" ? undefined : parseFloat(e.target.value) })} />
</div>
</div>
);
}
case "is_favorited":
return (
<Select value={condition.value === false ? "false" : "true"} onValueChange={(v) => onChange({ ...condition, value: v === "true" })}>
@@ -16,6 +16,7 @@ const conditionTypeLabels: Record<ConditionType, string> = {
asset_type: "Asset Type",
iso_range: "ISO Range",
focal_length: "Focal Length",
resolution: "Resolution",
rating: "Rating",
is_favorited: "Favorited",
file_size: "File Size",
@@ -100,6 +101,16 @@ export function formatConditionSummary(c: ICondition): string {
if (c.max != null) return `${label}: ≤${c.max}`;
return label;
}
case "resolution": {
const metricNames: Record<string, string> = { megapixels: "MP", short_edge: "short edge", long_edge: "long edge" };
const metric = c.metric || "megapixels";
const unit = metric === "megapixels" ? "MP" : "px";
const name = metric === "megapixels" ? "" : `${metricNames[metric]} `;
if (c.min != null && c.max != null) return `${label}: ${name}${c.min}–${c.max}${unit}`;
if (c.min != null) return `${label}: ${name}≥${c.min}${unit}`;
if (c.max != null) return `${label}: ${name}≤${c.max}${unit}`;
return label;
}
case "is_favorited":
return c.value === false ? "Not Favorited" : "Favorited";
case "file_size": {
+15
View File
@@ -92,6 +92,21 @@ function buildSingleCondition(c: ICondition): SQL | undefined {
return parts.length > 0 ? and(...parts)! : undefined;
}
case "resolution": {
// Short/long edge are orientation-agnostic; megapixels = width × height / 1e6.
const metric = c.metric || "megapixels";
const value =
metric === "short_edge"
? sql`LEAST(${exif.exifImageWidth}, ${exif.exifImageHeight})`
: metric === "long_edge"
? sql`GREATEST(${exif.exifImageWidth}, ${exif.exifImageHeight})`
: sql`(${exif.exifImageWidth}::bigint * ${exif.exifImageHeight}) / 1000000.0`;
const parts: SQL[] = [];
if (c.min !== undefined && c.min !== null) parts.push(sql`${value} >= ${c.min}`);
if (c.max !== undefined && c.max !== null) parts.push(sql`${value} <= ${c.max}`);
return parts.length > 0 ? and(...parts)! : undefined;
}
case "file_size": {
// min/max in megabytes (fractional allowed)
const parts: SQL[] = [];
+1 -1
View File
@@ -54,7 +54,7 @@ export type ConditionType =
| "date_range" | "date_relative" | "day_of_week"
| "camera_make" | "camera_model" | "lens"
| "asset_type" | "iso_range" | "focal_length"
| "rating" | "is_favorited"
| "rating" | "is_favorited" | "resolution"
| "file_size" | "filename" | "file_extension"
| "face_count" | "time_of_day"
| "not_in_album" | "not_in_specific_album";