opencode-config/.opencode/tools/draw-image.ts
Sergey 56b67d2d5c
feat(draw-image): SVG template renderer for on-brand covers (#134)
* feat(draw-image): add SVG template render engine

* feat(draw-image): add opencode plugin and tool tests

* test(draw-image): add vitest unit integration e2e suite

* chore(draw-image): wire CI dependabot docs and ADR

* docs(handoff): set PR number

* docs(handoff): fix PR number placeholder in frontmatter

* fix(ci): use tempfile and trailing newline in draw-image tests

---------

Co-authored-by: opencode-agent <agent@opencode.local>
2026-07-29 23:42:50 +03:00

32 lines
No EOL
1.6 KiB
TypeScript

import { spawnSync } from "child_process"
import { tool } from "@opencode-ai/plugin"
import path from "path"
export default tool({
description: "Render an on-brand cover PNG from an SVG template. Resolves Lucide icons and brand logos into slots, applies brand palette, renders via sharp. Returns { path, hash, status }.",
args: {
template: tool.schema.string().describe("Template name (file in templates/ without .svg extension, e.g. 'cover')"),
title: tool.schema.string().describe("Title text rendered at the bottom of the cover"),
subtitle: tool.schema.string().optional().describe("Optional subtitle text below the title"),
slots: tool.schema.string().optional().describe("Comma-separated slot assignments, e.g. 'icon=mic,sub-icon=opencode,badge=./x.svg'"),
out: tool.schema.string().optional().describe("Output PNG path (default: ./assets/cover.png)"),
},
async execute(args, context) {
const drawImageDir = path.resolve(import.meta.dir, "..", "draw-image")
const cliPath = path.join(drawImageDir, "cli.ts")
const cliArgs = ["render", args.template, "--title", args.title]
if (args.subtitle) cliArgs.push("--subtitle", args.subtitle)
if (args.slots) cliArgs.push("--slots", args.slots)
if (args.out) cliArgs.push("--out", args.out)
const r = spawnSync("node", ["--experimental-strip-types", cliPath, ...cliArgs], {
encoding: "utf-8",
cwd: context.worktree,
})
if (r.status !== 0) {
return `⚠️ draw-image failed (exit ${r.status}): ${r.stderr || r.stdout}`
}
return r.stdout.trim()
},
})