first-commit
ci / Validate workspace (push) Has been cancelled
landing-page-ci / Validate landing page (push) Has been cancelled
landing-page-deploy / Deploy landing page (push) Has been cancelled
github-metrics / Generate repository metrics SVG (push) Has been cancelled
refresh-contributors-wall / Refresh contributors wall cache bust (push) Waiting to run
ci / Validate workspace (push) Has been cancelled
landing-page-ci / Validate landing page (push) Has been cancelled
landing-page-deploy / Deploy landing page (push) Has been cancelled
github-metrics / Generate repository metrics SVG (push) Has been cancelled
refresh-contributors-wall / Refresh contributors wall cache bust (push) Waiting to run
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Package a validated atlas as a local Codex pet."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image
|
||||
|
||||
ATLAS_SIZE = (1536, 1872)
|
||||
|
||||
|
||||
def default_codex_home() -> Path:
|
||||
return Path(os.environ.get("CODEX_HOME") or "~/.codex").expanduser().resolve()
|
||||
|
||||
|
||||
def slugify(value: str) -> str:
|
||||
value = value.strip().lower()
|
||||
value = re.sub(r"[^a-z0-9]+", "-", value)
|
||||
value = re.sub(r"-{2,}", "-", value)
|
||||
return value.strip("-")
|
||||
|
||||
|
||||
def validate_spritesheet(path: Path) -> str:
|
||||
with Image.open(path) as image:
|
||||
if image.size != ATLAS_SIZE:
|
||||
raise SystemExit(
|
||||
f"expected {ATLAS_SIZE[0]}x{ATLAS_SIZE[1]}, got {image.width}x{image.height}"
|
||||
)
|
||||
if image.format not in {"PNG", "WEBP"}:
|
||||
raise SystemExit(f"expected PNG or WebP, got {image.format}")
|
||||
return str(image.format)
|
||||
|
||||
|
||||
def write_webp_spritesheet(source: Path, target: Path, source_format: str) -> None:
|
||||
if source_format == "WEBP":
|
||||
shutil.copy2(source, target)
|
||||
return
|
||||
with Image.open(source) as image:
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
image.convert("RGBA").save(
|
||||
target,
|
||||
format="WEBP",
|
||||
lossless=True,
|
||||
quality=100,
|
||||
method=6,
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--pet-name", default="")
|
||||
parser.add_argument("--display-name", default="")
|
||||
parser.add_argument("--description", required=True)
|
||||
parser.add_argument("--spritesheet", required=True)
|
||||
parser.add_argument("--codex-home", default=str(default_codex_home()))
|
||||
parser.add_argument(
|
||||
"--output-dir",
|
||||
help="Exact pet package directory. Defaults to ${CODEX_HOME:-$HOME/.codex}/pets/<pet-name>.",
|
||||
)
|
||||
parser.add_argument("--force", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
raw_pet_name = (args.pet_name or args.display_name).strip()
|
||||
if not raw_pet_name:
|
||||
raise SystemExit("pet name is required")
|
||||
pet_id = slugify(raw_pet_name)
|
||||
if not pet_id:
|
||||
raise SystemExit("pet name must contain at least one letter or digit")
|
||||
display_name = (args.display_name or raw_pet_name).strip()
|
||||
|
||||
source = Path(args.spritesheet).expanduser().resolve()
|
||||
source_format = validate_spritesheet(source)
|
||||
target_dir = (
|
||||
Path(args.output_dir).expanduser().resolve()
|
||||
if args.output_dir
|
||||
else Path(args.codex_home).expanduser().resolve() / "pets" / pet_id
|
||||
)
|
||||
target_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
target_sheet = target_dir / "spritesheet.webp"
|
||||
manifest_path = target_dir / "pet.json"
|
||||
if not args.force and (target_sheet.exists() or manifest_path.exists()):
|
||||
raise SystemExit(f"{target_dir} already contains pet files; pass --force to overwrite")
|
||||
|
||||
write_webp_spritesheet(source, target_sheet, source_format)
|
||||
manifest = {
|
||||
"id": pet_id,
|
||||
"displayName": display_name,
|
||||
"description": args.description,
|
||||
"spritesheetPath": target_sheet.name,
|
||||
}
|
||||
manifest_path.write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
|
||||
|
||||
print(
|
||||
json.dumps(
|
||||
{"ok": True, "pet_dir": str(target_dir), "manifest": str(manifest_path)}, indent=2
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user