// Visual filler iconography rendered through ComfyUI. Distinct from // role_scenes.ts (which renders portraits) — these are object/badge // style renders that fill dead space on worker cards: cert pills, // role-prop chips, hazard indicators, empty-state heroes. // // Layout on disk: // data/icons_pool/{category}/{slug}.webp // // Cache invalidation: // ICONS_VERSION mixes into the on-disk filename (slug includes // version). Bump it after editing a recipe so prior renders are // ignored on next view. export type IconCategory = "cert" | "role_prop" | "status" | "hazard" | "empty"; export interface IconRecipe { slug: string; category: IconCategory; // Text label that appears next to / under the icon. The front-end // already renders this text in cert pills; the icon is supplementary. display: string; // Full diffusion prompt. Style guidance baked in. SDXL Turbo at 8 // steps reliably produces clean macro photography, so default to // photographic prop shots over flat-vector illustrations (the model // hallucinates noise into flat-vector geometry at low step counts). prompt: string; // Negative prompt — what NOT to render. Crucial for icons because // SDXL likes to add hands/text/people unprompted. negative?: string; } // Default negative prompt baked into every icon render unless the // recipe overrides. Empirically, these terms are the top SDXL Turbo // off-style failures. export const DEFAULT_NEGATIVE = "people, hands, faces, blurry, low quality, watermark, signature, " + "logos, copyright, distorted text, garbled letters, multiple objects"; // TODO J — review and tune the prompts here. Each one is what diffusion // sees verbatim. The visual decision: photographic prop shots (macro // photo of an actual badge / placard / sticker) vs flat-icon vector // style. Default below is photographic — matches the worker headshot // aesthetic. Flip a recipe to flat-vector by replacing "macro photograph" // with "flat icon illustration on solid color background, minimal vector". // // Visual cues that work well in SDXL Turbo at 8 steps: // - "macro photograph", "isolated on plain background", "studio lighting" // - Concrete colors ("orange and black warning diamond") not adjectives // - Avoid: small text in the prompt (model garbles it), specific brand // names (creates fake logos), detailed scene composition const CERT_ICONS: IconRecipe[] = [ { slug: "osha-10", category: "cert", display: "OSHA-10", prompt: "macro photograph of a circular yellow safety badge with a black hard hat icon at center, isolated on neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, { slug: "osha-30", category: "cert", display: "OSHA-30", prompt: "macro photograph of a circular orange safety badge with a black hard hat icon at center, isolated on neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, { slug: "first-aid-cpr", category: "cert", display: "First Aid/CPR", prompt: "macro photograph of a small enamel pin badge featuring a bold red cross on a white circular background, isolated on neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, { slug: "hazmat", category: "cert", display: "Hazmat", prompt: "macro photograph of a HAZMAT warning placard, bold orange and black diamond shape with a flame icon, isolated on neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, { slug: "forklift", category: "cert", display: "Forklift", prompt: "macro photograph of a yellow industrial forklift safety badge with a forklift silhouette icon, isolated on neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, { slug: "reach-truck", category: "cert", display: "Reach Truck", prompt: "macro photograph of a navy blue industrial certification badge with a warehouse reach-truck silhouette icon, isolated on neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, { slug: "order-picker", category: "cert", display: "Order Picker", prompt: "macro photograph of a green industrial certification badge with a warehouse order-picker silhouette icon, isolated on neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, { slug: "lockout-tagout", category: "cert", display: "Lockout/Tagout", prompt: "macro photograph of a bright red padlock tag with a danger warning, hanging on a metal industrial valve, isolated on neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, { slug: "msds", category: "cert", display: "MSDS", prompt: "macro photograph of a folded chemical safety data sheet booklet with chemical hazard pictograms visible on cover, isolated on neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, { slug: "confined-space", category: "cert", display: "Confined Space", prompt: "macro photograph of a yellow confined space warning sign featuring a manhole entry icon, isolated on neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, { slug: "servsafe", category: "cert", display: "ServSafe", prompt: "macro photograph of a dark green food safety certification badge featuring a stylized chef hat icon, isolated on neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, { slug: "fire-safety", category: "cert", display: "Fire Safety", prompt: "macro photograph of a red enamel pin badge featuring a flame icon and a fire extinguisher silhouette, isolated on neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, { slug: "iso-9001", category: "cert", display: "ISO 9001", prompt: "macro photograph of a deep blue circular quality-management certification seal with embossed metallic ring, isolated on neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, ]; // Role-band visual chips — small icons that go in the role pill area. // One per band, optional inline supplement to the existing colored pill. const ROLE_PROP_ICONS: IconRecipe[] = [ { slug: "warehouse", category: "role_prop", display: "Warehouse", prompt: "macro photograph of a yellow hard hat with a high-visibility safety vest folded behind it, isolated on neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, { slug: "production", category: "role_prop", display: "Production", prompt: "macro photograph of a navy blue work shirt and protective safety glasses on neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, { slug: "trades", category: "role_prop", display: "Trades", prompt: "macro photograph of a leather work glove and a small adjustable wrench on a neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, { slug: "driver", category: "role_prop", display: "Driver", prompt: "macro photograph of a navy delivery driver baseball cap and a clipboard manifest on a neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, { slug: "lead", category: "role_prop", display: "Lead", prompt: "macro photograph of a tablet showing a bar chart and a high-vis vest folded beside it on neutral grey backdrop, photorealistic, sharp focus, studio lighting" }, ]; export const ICONS: Record = Object.fromEntries( [...CERT_ICONS, ...ROLE_PROP_ICONS].map((r) => [`${r.category}/${r.slug}`, r]), ); // v2 — 256×256 canvas, intended to be displayed monochrome via CSS // `filter: grayscale(1)`. Smaller canvas, tighter crops, crisper at // 14px display size. export const ICONS_VERSION = "v2"; // Map a free-form cert string from the data ("First Aid/CPR", // "OSHA-10", "Lockout/Tagout") to the canonical slug used here. // Returns null if no recipe matches. export function certToSlug(cert: string): string | null { const c = (cert || "").trim().toLowerCase().replace(/\s+/g, "-"); if (c === "osha-10") return "osha-10"; if (c === "osha-30") return "osha-30"; if (c.startsWith("first") || c.includes("cpr")) return "first-aid-cpr"; if (c === "hazmat" || c.startsWith("hazwoper")) return "hazmat"; if (c === "forklift" || c.startsWith("pit")) return "forklift"; if (c.startsWith("reach")) return "reach-truck"; if (c.startsWith("order")) return "order-picker"; if (c.startsWith("lockout") || c.includes("tagout")) return "lockout-tagout"; if (c === "msds" || c.startsWith("ghs")) return "msds"; if (c.startsWith("confined")) return "confined-space"; if (c === "servsafe") return "servsafe"; if (c.startsWith("fire")) return "fire-safety"; if (c.startsWith("iso")) return "iso-9001"; return null; }