# Visual Asset Guidelines This skill cannot bundle helper scripts, so these guidelines show how to **perform each visual-asset ability inline** using public APIs and short, self-contained snippets you run at request time (scratch cell or terminal). How to use a guideline: 1. Pick the ability you need below. 2. Run the inline snippet (adjust inputs) in an ephemeral scratch file or terminal — do not save it into the skill, since the skill keeps only `references/`. 3. Place the returned local asset path into `layout_tree.objects` with `content.path`, `content.alt`, `bbox`, `z_index`, and `classification`. Shared rules: - Always return a **local file path** for any placed asset, plus `content.alt`. - Record provenance (`source`, `license`, `provider`, `model`) for audits. - On failure, write a failure manifest; never substitute a placeholder and call it generated. - Never request secrets in chat or a prompt dialog. For cloud auth use `.env` or `az login`. - Before a billable generation call or any request that sends user-provided or source material to a third party, disclose the provider/model, the material that will leave the machine, likely cost, and output path. Obtain explicit confirmation unless the user already authorized that exact operation. --- ## 1. Icon Search Use the public Iconify API — no key required. - Search: `https://api.iconify.design/search?query=&limit=` - Download SVG: `https://api.iconify.design//.svg?color=%23` ```python import json, urllib.parse, urllib.request from pathlib import Path def icon_search(query, limit=8, prefix=None, color=None, out_dir="assets/icons"): q = urllib.parse.quote(query) url = f"https://api.iconify.design/search?query={q}&limit={limit}" if prefix: url += f"&prefix={urllib.parse.quote(prefix)}" data = json.load(urllib.request.urlopen(url, timeout=15)) Path(out_dir).mkdir(parents=True, exist_ok=True) results = [] for icon_id in data.get("icons", []): pfx, name = icon_id.split(":", 1) svg_url = f"https://api.iconify.design/{pfx}/{name}.svg" if color: svg_url += f"?color=%23{color}" svg_path = Path(out_dir) / f"{pfx}_{name}.svg" svg_path.write_bytes(urllib.request.urlopen(svg_url, timeout=15).read()) results.append({"id": icon_id, "svg_path": str(svg_path), "license": "per-set (see iconify.design)"}) return {"query": query, "results": results} ``` - Prefer simple, single-color icons matching the theme accent. - Use icons as supporting cues, not replacements for required text. --- ## 2. Web Image Search Prefer the VS Code fetch tools (`fetch_webpage`) or an MCP image-search tool you have available. When you already have a direct image URL (from search results or the user), download it locally: ```python import urllib.request from pathlib import Path def download_image(url, out_path="assets/images/img1.jpg"): Path(out_path).parent.mkdir(parents=True, exist_ok=True) req = urllib.request.Request(url, headers={"User-Agent": "pptx-builder/1.0"}) Path(out_path).write_bytes(urllib.request.urlopen(req, timeout=20).read()) return {"url": url, "local_path": out_path} ``` - Capture `source` and `license` for attribution; verify usage rights before placing. - Reference the saved file via `content.path`; do not hotlink remote URLs into the deck. - Do not use image placeholders as fallback assets; select an approved asset or omit the object. --- ## 3. Vector Asset Decision Use a true SVG only when it is already a clean vector asset or when the source contains no essential text or data that needs editing. Do not wrap a raster in an SVG and treat the result as editable. - Do not trace a raster merely to satisfy an editability requirement. - If an illustration contains essential text, recreate the text with native PowerPoint objects. - Keep the original raster or a non-compliant SVG only as a supporting visual or hidden reference. --- ## 4. Text → Infographic Generate through a user-managed provider (OpenAI or Azure OpenAI). Read credentials from environment; never accept secrets via chat. Before running the snippet, obtain the external-call confirmation described in the shared rules. If `output_path` or its manifest already exists, choose a new path or obtain separate explicit overwrite confirmation; do not silently replace either file. ```python import base64, json, os from pathlib import Path from openai import OpenAI, AzureOpenAI # provided by the user's environment def text_to_infographic(prompt, output_path, provider="openai", model_or_deployment="gpt-image-1", size="1024x1024", confirmed=False, allow_overwrite=False): output = Path(output_path) manifest_path = output.with_suffix(".manifest.json") existing = [path for path in (output, manifest_path) if path.exists()] if existing and not allow_overwrite: raise FileExistsError(f"Refusing to overwrite existing paths: {existing}") manifest_path.parent.mkdir(parents=True, exist_ok=True) manifest = {"provider": provider, "model_or_deployment": model_or_deployment, "output_path": output_path} if not confirmed: manifest.update(status="cancelled", error="External generation was not confirmed") manifest_path.write_text(json.dumps(manifest, indent=2), encoding="utf-8") return manifest try: if provider == "azure-openai": client = AzureOpenAI( azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"], api_key=os.environ.get("AZURE_OPENAI_API_KEY"), api_version=os.environ.get("AZURE_OPENAI_API_VERSION", "2024-02-01"), ) else: client = OpenAI(api_key=os.environ["OPENAI_API_KEY"]) result = client.images.generate(model=model_or_deployment, prompt=prompt, size=size) output.write_bytes(base64.b64decode(result.data[0].b64_json)) manifest["status"] = "ok" except Exception as exc: # report, never fake-generate manifest.update(status="error", error=str(exc)) manifest_path.write_text(json.dumps(manifest, indent=2), encoding="utf-8") return manifest ``` - Collect missing values via `vscode_askQuestions`: provider, prompt, model/deployment, size, output path. - Before calling the function, disclose the provider/model, material leaving the machine, likely cost, and output path. Set `confirmed=True` only after the user explicitly authorizes that exact external request. Set `allow_overwrite=True` only after separate explicit approval to replace every existing output or manifest path. - Use `.env` or `az login` for auth; never ask for keys/tokens in chat or the dialog. - Use generated art as a supporting visual. Recreate essential text, labels, metrics, and steps with native PowerPoint objects. Add a vector asset only when it is a clean, editable source. --- ## 5. NotebookLM Infographic Bridge NotebookLM has no public generation API, so treat this as an optional, user-configured bridge. - If the user has a NotebookLM/MCP bridge tool configured, call it with `source_refs` + `prompt`, then save the returned image locally and record provenance. - If no bridge is configured, **fall back to Text → Infographic (section 4)** or omit the asset. - Apply the same confirmation, overwrite, provenance, and failure-manifest rules as the other generation guidelines.