169 lines
7.4 KiB
Markdown
169 lines
7.4 KiB
Markdown
# 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=<q>&limit=<n>`
|
|
- Download SVG: `https://api.iconify.design/<prefix>/<name>.svg?color=%23<hex>`
|
|
|
|
```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.
|