Sync third-party and MCP marketplace plugins

Constraint: Public skills are published only by explicit administrator action unless they are tracked third-party market sources.
Confidence: high
Scope-risk: narrow
Directive: Keep private/internal skills out of the public marketplace and preserve normal incremental market Git history.
Tested: Marketplace validation passed.
This commit is contained in:
KeyInfo Bot
2026-06-28 00:01:27 +08:00
parent 7f2f7fc46d
commit ea75d66252
137 changed files with 936 additions and 1788 deletions
+10 -10
View File
@@ -15,8 +15,8 @@
"repo": "https://github.com/Yeachan-Heo/oh-my-codex.git", "repo": "https://github.com/Yeachan-Heo/oh-my-codex.git",
"ref": "main", "ref": "main",
"adapter": "codex-plugin", "adapter": "codex-plugin",
"commit": "e9e3bcc720d964d3673cadb8136394ac103cc091", "commit": "d96b698c5f2fabc85c890f7b8fcdc6e02f253ec7",
"syncedAt": "2026-06-25T16:00:00Z" "syncedAt": "2026-06-27T16:00:00Z"
}, },
{ {
"id": "ui-ux-pro-max", "id": "ui-ux-pro-max",
@@ -24,8 +24,8 @@
"repo": "https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git", "repo": "https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git",
"ref": "main", "ref": "main",
"adapter": "claude-skill", "adapter": "claude-skill",
"commit": "9fd25fe07e46ae444edc356e62fe913347ab9e23", "commit": "65e23199492fa911af32d9078e627ab4de01f4c8",
"syncedAt": "2026-06-26T15:59:59Z" "syncedAt": "2026-06-27T16:00:00Z"
}, },
{ {
"id": "shadcn", "id": "shadcn",
@@ -33,8 +33,8 @@
"repo": "https://github.com/shadcn-ui/ui.git", "repo": "https://github.com/shadcn-ui/ui.git",
"ref": "main", "ref": "main",
"adapter": "claude-skill", "adapter": "claude-skill",
"commit": "c520191cd4c36760ed425b650e139fe6d7e29038", "commit": "a72491cb9b5f9471392fdc179bbb61739e37ec72",
"syncedAt": "2026-06-26T15:59:59Z" "syncedAt": "2026-06-27T16:00:00Z"
}, },
{ {
"id": "frontend-slides", "id": "frontend-slides",
@@ -69,8 +69,8 @@
"repo": "https://github.com/hugohe3/ppt-master.git", "repo": "https://github.com/hugohe3/ppt-master.git",
"ref": "main", "ref": "main",
"adapter": "claude-skill", "adapter": "claude-skill",
"commit": "47f5051845777839ec29e8d67dd12ae68671854a", "commit": "13a25616e6354fe5abe16f41da18f532e51ac644",
"syncedAt": "2026-06-26T15:59:59Z" "syncedAt": "2026-06-27T16:00:00Z"
}, },
{ {
"id": "next-skills", "id": "next-skills",
@@ -78,8 +78,8 @@
"repo": "https://github.com/vercel/next.js.git", "repo": "https://github.com/vercel/next.js.git",
"ref": "canary", "ref": "canary",
"adapter": "skill-collection", "adapter": "skill-collection",
"commit": "6c22d02ce0c1ba3b8773b314756f388d8f7cbd1a", "commit": "4b164d765e16de0b277960a27d3938815ac03b97",
"syncedAt": "2026-06-26T15:59:59Z" "syncedAt": "2026-06-27T16:00:00Z"
} }
] ]
} }
@@ -3,5 +3,5 @@
"name": "playwright浏览器自动化操作", "name": "playwright浏览器自动化操作",
"version": "20260605", "version": "20260605",
"keySource": "none", "keySource": "none",
"syncedAt": "2026-06-26T16:01:08Z" "syncedAt": "2026-06-27T16:01:26Z"
} }
@@ -2,8 +2,8 @@
"sourceId": "next-skills", "sourceId": "next-skills",
"repo": "https://github.com/vercel/next.js.git", "repo": "https://github.com/vercel/next.js.git",
"ref": "canary", "ref": "canary",
"commit": "6c22d02ce0c1ba3b8773b314756f388d8f7cbd1a", "commit": "4b164d765e16de0b277960a27d3938815ac03b97",
"adapter": "skill-collection", "adapter": "skill-collection",
"sourcePath": "skills", "sourcePath": "skills",
"syncedAt": "2026-06-26T15:59:59Z" "syncedAt": "2026-06-27T16:00:00Z"
} }
@@ -27,7 +27,7 @@ The two views cross-check each other.
- Next.js **16.3+** with **Turbopack**`/_next/mcp` plus the - Next.js **16.3+** with **Turbopack**`/_next/mcp` plus the
proactive compile check via `get_compilation_issues`. proactive compile check via `get_compilation_issues`.
- `agent-browser` **>= 0.31.0** — React introspection, worktree-scoped - `agent-browser` **>= 0.31.1** — React introspection, worktree-scoped
`session id`, idempotent `--restore`, and launch flag reconciliation. `session id`, idempotent `--restore`, and launch flag reconciliation.
These are hard floors, not soft preferences. If anything is missing, These are hard floors, not soft preferences. If anything is missing,
@@ -1,6 +1,6 @@
{ {
"name": "oh-my-codex", "name": "oh-my-codex",
"version": "0.18.15", "version": "0.18.16",
"description": "oh-my-codex 是 Codex CLI 的多 Agent 编排、结构化工作流、插件级 hooks、MCP 和 HUD 扩展插件。", "description": "oh-my-codex 是 Codex CLI 的多 Agent 编排、结构化工作流、插件级 hooks、MCP 和 HUD 扩展插件。",
"author": { "author": {
"name": "Yeachan Heo", "name": "Yeachan Heo",
@@ -2,8 +2,8 @@
"sourceId": "oh-my-codex", "sourceId": "oh-my-codex",
"repo": "https://github.com/Yeachan-Heo/oh-my-codex.git", "repo": "https://github.com/Yeachan-Heo/oh-my-codex.git",
"ref": "main", "ref": "main",
"commit": "e9e3bcc720d964d3673cadb8136394ac103cc091", "commit": "d96b698c5f2fabc85c890f7b8fcdc6e02f253ec7",
"adapter": "codex-plugin", "adapter": "codex-plugin",
"sourcePath": "plugins/oh-my-codex", "sourcePath": "plugins/oh-my-codex",
"syncedAt": "2026-06-25T16:00:00Z" "syncedAt": "2026-06-27T16:00:00Z"
} }
+1 -1
View File
@@ -201,7 +201,7 @@ PPT Master runs in **any tool with agent capability** — read/write files, exec
> **Model recommendation**: for the best results, use **Claude Opus** with `gpt-image-2`; **Gemini 3.5 Flash** currently offers great overall value for money — notably fast and well worth a try. > **Model recommendation**: for the best results, use **Claude Opus** with `gpt-image-2`; **Gemini 3.5 Flash** currently offers great overall value for money — notably fast and well worth a try.
**🔑 Want to use Claude / GPT / Gemini but don't have access yet?** Project sponsors **[PackyCode](https://www.packyapi.com/register?aff=ppt-master)**, **[APIKEY.FUN](https://apikey.fun/register?aff=PPT-MASTER)** and **[RunAPI](https://runapi.co/register?aff=WMLJ)** can help — all offer pay-as-you-go access to Claude, GPT, Gemini and more, no subscription required. **PackyCode**: 10% off with promo code **`ppt-master`** at top-up. **APIKEY.FUN**: prices as low as **7% of official rates**; register via our link for an exclusive permanent discount of up to 5% on top-ups. **RunAPI**: 150+ models via one API Key at prices as low as **10% of official rates**; register and contact an administrator to claim **¥7 in free credit**. **🔑 Want to use Claude / GPT / Gemini but don't have access yet?** Project sponsors **[PackyCode](https://www.packyapi.com/register?aff=ppt-master)**, **[APIKEY.FUN](https://apikey.fun/register?aff=PPT-MASTER)** and **[RunAPI](https://runapi.co/register?aff=WMLJ)** offer pay-as-you-go access to Claude, GPT, Gemini and more no subscription required, with exclusive discounts for our users (details at the top of this page).
**🔀 Juggling several providers?** Once you hold keys from more than one of them, [cc-switch](https://github.com/farion1231/cc-switch) — a cross-platform desktop app — lets you one-click switch API providers for Claude Code, Codex, Gemini CLI and more, no manual config editing. **🔀 Juggling several providers?** Once you hold keys from more than one of them, [cc-switch](https://github.com/farion1231/cc-switch) — a cross-platform desktop app — lets you one-click switch API providers for Claude Code, Codex, Gemini CLI and more, no manual config editing.
@@ -2,8 +2,8 @@
"sourceId": "ppt-master", "sourceId": "ppt-master",
"repo": "https://github.com/hugohe3/ppt-master.git", "repo": "https://github.com/hugohe3/ppt-master.git",
"ref": "main", "ref": "main",
"commit": "47f5051845777839ec29e8d67dd12ae68671854a", "commit": "13a25616e6354fe5abe16f41da18f532e51ac644",
"adapter": "claude-skill", "adapter": "claude-skill",
"sourcePath": "skills/ppt-master", "sourcePath": "skills/ppt-master",
"syncedAt": "2026-06-26T15:59:59Z" "syncedAt": "2026-06-27T16:00:00Z"
} }
@@ -372,7 +372,7 @@ templates/
│ └── pixel_retro/ # Pixel retro / cyberpunk structure (no identity) │ └── pixel_retro/ # Pixel retro / cyberpunk structure (no identity)
└── decks/ └── decks/
├── 招商银行/ # China Merchants Bank full PPT replica ├── 招商银行/ # China Merchants Bank full PPT replica
└── 中国电建_常规/ # PowerChina full PPT replica └── 中国电建/ # PowerChina full PPT replica
``` ```
--- ---
@@ -28,6 +28,10 @@ import os
import sys import sys
from pathlib import Path from pathlib import Path
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
try: try:
from PIL import Image from PIL import Image
except ImportError: except ImportError:
@@ -28,6 +28,7 @@ _SCRIPTS_DIR = Path(__file__).resolve().parent
if str(_SCRIPTS_DIR) not in sys.path: if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR)) sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
from svg_to_pptx.animation_config import ( # noqa: E402 from svg_to_pptx.animation_config import ( # noqa: E402
build_group_listing, build_group_listing,
load_animation_config, load_animation_config,
@@ -35,6 +36,8 @@ from svg_to_pptx.animation_config import ( # noqa: E402
write_scaffold, write_scaffold,
) )
configure_utf8_stdio()
def build_parser() -> argparse.ArgumentParser: def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
@@ -16,6 +16,10 @@ import sys
from collections import defaultdict from collections import defaultdict
from pathlib import Path from pathlib import Path
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
try: try:
from project_utils import ( from project_utils import (
find_all_projects, find_all_projects,
@@ -38,10 +38,13 @@ _SCRIPTS_DIR = Path(__file__).resolve().parent
if str(_SCRIPTS_DIR) not in sys.path: if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR)) sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
from pptx_to_svg.color_resolver import ColorPalette # noqa: E402 from pptx_to_svg.color_resolver import ColorPalette # noqa: E402
from pptx_to_svg.emu_units import NS # noqa: E402 from pptx_to_svg.emu_units import NS # noqa: E402
from pptx_to_svg.ooxml_loader import OoxmlPackage # noqa: E402 from pptx_to_svg.ooxml_loader import OoxmlPackage # noqa: E402
configure_utf8_stdio()
def _font_pair(theme_root, font_tag: str) -> dict[str, str]: def _font_pair(theme_root, font_tag: str) -> dict[str, str]:
"""Read one <a:majorFont> / <a:minorFont> into {latin, ea} (skip empties).""" """Read one <a:majorFont> / <a:minorFont> into {latin, ea} (skip empties)."""
@@ -30,6 +30,10 @@ import sys
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
def _images_by_slide(manifest: list) -> dict[int, list[dict]]: def _images_by_slide(manifest: list) -> dict[int, list[dict]]:
"""Map slide_index -> [image entries on that slide], from ppt_to_md occurrences.""" """Map slide_index -> [image entries on that slide], from ppt_to_md occurrences."""
@@ -23,6 +23,10 @@ import xml.etree.ElementTree as ET
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
def scan_svg_file(svg_path: Path) -> list[dict]: def scan_svg_file(svg_path: Path) -> list[dict]:
"""Scan a single SVG file for edit annotations.""" """Scan a single SVG file for edit annotations."""
@@ -20,6 +20,10 @@ import os
from pathlib import Path from pathlib import Path
from typing import Dict, List, Optional, Any from typing import Dict, List, Optional, Any
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
# ============================================================ # ============================================================
# Path Configuration # Path Configuration
@@ -50,6 +50,7 @@ _SCRIPTS_DIR = Path(__file__).resolve().parent.parent
if str(_SCRIPTS_DIR) not in sys.path: if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR)) sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
from server_common import ( # noqa: E402 from server_common import ( # noqa: E402
claim_lock as _claim_lock, claim_lock as _claim_lock,
find_free_port as _find_free_port, find_free_port as _find_free_port,
@@ -58,6 +59,8 @@ from server_common import ( # noqa: E402
release_lock as _release_lock, release_lock as _release_lock,
) )
configure_utf8_stdio()
logger = logging.getLogger('confirm_ui') logger = logging.getLogger('confirm_ui')
# Per-project lock file. Lives at <project_path>/.confirm_ui.lock and matches # Per-project lock file. Lives at <project_path>/.confirm_ui.lock and matches
@@ -0,0 +1,27 @@
#!/usr/bin/env python3
"""Console encoding helpers for PPT Master CLI scripts."""
from __future__ import annotations
import io
import sys
from typing import TextIO
def _reconfigure_stream(stream: TextIO) -> TextIO:
try:
stream.reconfigure(encoding="utf-8", errors="replace")
return stream
except AttributeError:
buffer = getattr(stream, "buffer", None)
if buffer is None:
return stream
return io.TextIOWrapper(buffer, encoding="utf-8", errors="replace")
except (OSError, ValueError):
return stream
def configure_utf8_stdio() -> None:
"""Use UTF-8 for CLI stdout/stderr, including Windows non-UTF-8 locales."""
sys.stdout = _reconfigure_stream(sys.stdout)
sys.stderr = _reconfigure_stream(sys.stderr)
@@ -8,6 +8,10 @@ Provides user-friendly error messages and specific fix suggestions.
import argparse import argparse
from typing import Dict, List, Optional from typing import Dict, List, Optional
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
class ErrorHelper: class ErrorHelper:
"""Error message helper.""" """Error message helper."""
@@ -37,6 +37,10 @@ from pathlib import Path
from typing import Optional from typing import Optional
from xml.etree import ElementTree as ET from xml.etree import ElementTree as ET
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
SVG_NS = "http://www.w3.org/2000/svg" SVG_NS = "http://www.w3.org/2000/svg"
DRAWABLE = {"path", "polygon", "polyline", "rect", "circle", "ellipse", "line"} DRAWABLE = {"path", "polygon", "polyline", "rect", "circle", "ellipse", "line"}
SEMANTIC_CONTENT = {"text", "tspan", "foreignObject"} SEMANTIC_CONTENT = {"text", "tspan", "foreignObject"}
@@ -39,6 +39,10 @@ import shutil
import argparse import argparse
from pathlib import Path from pathlib import Path
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
# Import finalize helpers from the internal package. # Import finalize helpers from the internal package.
sys.path.insert(0, str(Path(__file__).parent)) sys.path.insert(0, str(Path(__file__).parent))
from svg_finalize.align_embed_images import ( from svg_finalize.align_embed_images import (
@@ -26,12 +26,16 @@ import sys
import argparse import argparse
from pathlib import Path from pathlib import Path
import numpy as np
from PIL import Image
# Import modules from the same directory # Import modules from the same directory
sys.path.insert(0, str(Path(__file__).parent)) sys.path.insert(0, str(Path(__file__).parent))
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
import numpy as np
from PIL import Image
# Algorithm parameters # Algorithm parameters
ALPHA_THRESHOLD = 0.002 # Alpha threshold; values below this are not processed ALPHA_THRESHOLD = 0.002 # Alpha threshold; values below this are not processed
MAX_ALPHA = 0.99 # Maximum alpha value to prevent division by zero MAX_ALPHA = 0.99 # Maximum alpha value to prevent division by zero
@@ -14,6 +14,10 @@ from pathlib import Path
from datetime import datetime from datetime import datetime
from collections import defaultdict from collections import defaultdict
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
try: try:
from project_utils import find_all_projects, get_project_info, CANVAS_FORMATS from project_utils import find_all_projects, get_project_info, CANVAS_FORMATS
except ImportError: except ImportError:
@@ -33,6 +33,10 @@ import sys
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
_LIB_ALIASES = {"chunk": "chunk-filled"} _LIB_ALIASES = {"chunk": "chunk-filled"}
_GLOBAL_ICONS_DIR = Path(__file__).resolve().parent.parent / "templates" / "icons" _GLOBAL_ICONS_DIR = Path(__file__).resolve().parent.parent / "templates" / "icons"
@@ -9,6 +9,15 @@ Configuration keys:
""" """
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -4,6 +4,15 @@ Shared helpers for image generation backends.
""" """
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -9,6 +9,15 @@ Configuration keys:
""" """
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -15,6 +15,15 @@ Dependencies:
""" """
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -9,6 +9,15 @@ Configuration keys:
""" """
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -9,6 +9,15 @@ Configuration keys:
""" """
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -22,6 +22,15 @@ Dependencies:
""" """
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -9,6 +9,15 @@ Configuration keys:
""" """
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -9,6 +9,15 @@ Configuration keys:
""" """
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -9,6 +9,15 @@ Configuration keys:
""" """
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -9,6 +9,15 @@ Configuration keys:
""" """
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -9,6 +9,15 @@ Configuration keys:
""" """
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -9,6 +9,15 @@ Configuration keys:
""" """
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -9,6 +9,15 @@ Configuration keys:
""" """
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -53,8 +53,11 @@ import threading
import time import time
from pathlib import Path from pathlib import Path
from console_encoding import configure_utf8_stdio
from config import load_prefixed_env_file, resolve_env_path from config import load_prefixed_env_file, resolve_env_path
configure_utf8_stdio()
ENV_PATH = resolve_env_path() ENV_PATH = resolve_env_path()
IMAGE_ENV_PREFIXES = ( IMAGE_ENV_PREFIXES = (
"IMAGE_", "IMAGE_",
@@ -57,6 +57,7 @@ _SCRIPTS_DIR = Path(__file__).resolve().parent
if str(_SCRIPTS_DIR) not in sys.path: if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR)) sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
from config import load_prefixed_env_file # noqa: E402 from config import load_prefixed_env_file # noqa: E402
from image_backends.backend_common import download_image # noqa: E402 from image_backends.backend_common import download_image # noqa: E402
from image_sources.provider_common import ( # noqa: E402 from image_sources.provider_common import ( # noqa: E402
@@ -68,6 +69,8 @@ from image_sources.provider_common import ( # noqa: E402
score_candidate, score_candidate,
) )
configure_utf8_stdio()
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Provider registry # Provider registry
@@ -17,6 +17,15 @@ the corresponding provider_<name>.py module and only imports from here.
from __future__ import annotations from __future__ import annotations
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -25,7 +34,6 @@ if __name__ == "__main__":
import re import re
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Optional from typing import Any, Optional
@@ -9,6 +9,15 @@ API docs: https://api.openverse.org/v1/
from __future__ import annotations from __future__ import annotations
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -10,6 +10,15 @@ API docs: https://www.pexels.com/api/documentation/
from __future__ import annotations from __future__ import annotations
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -10,6 +10,15 @@ API docs: https://pixabay.com/api/docs/
from __future__ import annotations from __future__ import annotations
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -13,6 +13,15 @@ API docs: https://www.mediawiki.org/wiki/API:Search
from __future__ import annotations from __future__ import annotations
import sys import sys
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
print(__doc__) print(__doc__)
@@ -32,12 +32,17 @@ import urllib.request
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from console_encoding import configure_utf8_stdio
try: try:
from PIL import Image from PIL import Image
except ImportError: except ImportError:
Image = None Image = None
configure_utf8_stdio()
DEFAULT_DPI = 300 DEFAULT_DPI = 300
DEFAULT_TRANSPARENT_TOLERANCE = 12 DEFAULT_TRANSPARENT_TOLERANCE = 12
DEFAULT_MANIFEST = "images/formula_manifest.json" DEFAULT_MANIFEST = "images/formula_manifest.json"
@@ -30,8 +30,11 @@ _SCRIPTS_DIR = Path(__file__).resolve().parent
if str(_SCRIPTS_DIR) not in sys.path: if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR)) sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
from native_narration_pptx import main # noqa: E402 from native_narration_pptx import main # noqa: E402
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
raise SystemExit(main()) raise SystemExit(main())
@@ -41,6 +41,7 @@ _SCRIPTS_DIR = Path(__file__).resolve().parent
if str(_SCRIPTS_DIR) not in sys.path: if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR)) sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
from pptx_animations import TRANSITIONS, create_transition_xml # noqa: E402 from pptx_animations import TRANSITIONS, create_transition_xml # noqa: E402
from svg_to_pptx.pptx_builder import ( # noqa: E402 from svg_to_pptx.pptx_builder import ( # noqa: E402
_add_default_content_type, _add_default_content_type,
@@ -65,6 +66,8 @@ from svg_to_pptx.pptx_notes import ( # noqa: E402
markdown_to_plain_text, markdown_to_plain_text,
) )
configure_utf8_stdio()
PROJECT_SCHEMA = "native_pptx_enhancement_project.v1" PROJECT_SCHEMA = "native_pptx_enhancement_project.v1"
LEGACY_PROJECT_SCHEMAS = {"native_narration_pptx_project.v1"} LEGACY_PROJECT_SCHEMAS = {"native_narration_pptx_project.v1"}
@@ -30,6 +30,7 @@ import sys
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from console_encoding import configure_utf8_stdio
from config import load_prefixed_env_file from config import load_prefixed_env_file
from tts_backends import ( from tts_backends import (
backend_cosyvoice, backend_cosyvoice,
@@ -39,6 +40,8 @@ from tts_backends import (
backend_qwen, backend_qwen,
) )
configure_utf8_stdio()
@dataclass(frozen=True) @dataclass(frozen=True)
class AudioBackend: class AudioBackend:
@@ -42,6 +42,10 @@ Usage:
import argparse import argparse
from typing import Optional, Dict, Any from typing import Optional, Dict, Any
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
# ============================================================================ # ============================================================================
# Transition effect definitions # Transition effect definitions
@@ -29,9 +29,12 @@ _SCRIPTS_DIR = Path(__file__).resolve().parent
if str(_SCRIPTS_DIR) not in sys.path: if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR)) sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
from beautify_identity import extract_identity # noqa: E402 from beautify_identity import extract_identity # noqa: E402
from template_fill_pptx.analyzer import analyze_pptx # noqa: E402 from template_fill_pptx.analyzer import analyze_pptx # noqa: E402
configure_utf8_stdio()
def _write_json(path: Path, payload: dict[str, Any]) -> None: def _write_json(path: Path, payload: dict[str, Any]) -> None:
path.parent.mkdir(parents=True, exist_ok=True) path.parent.mkdir(parents=True, exist_ok=True)
@@ -22,8 +22,11 @@ import argparse
import json import json
from pathlib import Path from pathlib import Path
from console_encoding import configure_utf8_stdio
from template_import.manifest import build_manifest from template_import.manifest import build_manifest
configure_utf8_stdio()
def parse_args() -> argparse.Namespace: def parse_args() -> argparse.Namespace:
"""Build the CLI argument parser for the import entry point.""" """Build the CLI argument parser for the import entry point."""
@@ -30,9 +30,12 @@ from pathlib import Path
# Allow running this script from anywhere # Allow running this script from anywhere
sys.path.insert(0, str(Path(__file__).resolve().parent)) sys.path.insert(0, str(Path(__file__).resolve().parent))
from console_encoding import configure_utf8_stdio
from pptx_to_svg import convert_pptx_to_svg from pptx_to_svg import convert_pptx_to_svg
from pptx_to_svg.converter import ConvertOptions from pptx_to_svg.converter import ConvertOptions
configure_utf8_stdio()
def parse_args() -> argparse.Namespace: def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
@@ -13,6 +13,7 @@ from __future__ import annotations
import argparse import argparse
import filecmp import filecmp
import json import json
import os
import re import re
import shutil import shutil
import subprocess import subprocess
@@ -21,6 +22,8 @@ from datetime import datetime
from pathlib import Path from pathlib import Path
from urllib.parse import urlparse from urllib.parse import urlparse
from console_encoding import configure_utf8_stdio
try: try:
from project_utils import ( from project_utils import (
CANVAS_FORMATS, CANVAS_FORMATS,
@@ -65,6 +68,9 @@ IMAGE_ASSET_SUFFIXES = {
} }
configure_utf8_stdio()
def _curl_cffi_available() -> bool: def _curl_cffi_available() -> bool:
"""Return whether curl_cffi is importable (enables Python TLS impersonation).""" """Return whether curl_cffi is importable (enables Python TLS impersonation)."""
try: try:
@@ -233,6 +239,9 @@ class ProjectManager:
return destination return destination
def _run_tool(self, args: list[str]) -> None: def _run_tool(self, args: list[str]) -> None:
child_env = os.environ.copy()
child_env["PYTHONUTF8"] = "1"
child_env["PYTHONIOENCODING"] = "utf-8:replace"
try: try:
result = subprocess.run( result = subprocess.run(
args, args,
@@ -242,6 +251,7 @@ class ProjectManager:
text=True, text=True,
encoding="utf-8", encoding="utf-8",
errors="replace", errors="replace",
env=child_env,
) )
except FileNotFoundError as exc: except FileNotFoundError as exc:
raise RuntimeError(f"Missing executable: {args[0]}") from exc raise RuntimeError(f"Missing executable: {args[0]}") from exc
@@ -12,6 +12,10 @@ from pathlib import Path
from datetime import datetime from datetime import datetime
from typing import Dict, List, Optional, Tuple from typing import Dict, List, Optional, Tuple
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
# Canvas format definitions (unified source) # Canvas format definitions (unified source)
try: try:
from config import CANVAS_FORMATS from config import CANVAS_FORMATS
@@ -39,12 +39,17 @@ import sys
from collections import OrderedDict from collections import OrderedDict
from pathlib import Path from pathlib import Path
from console_encoding import configure_utf8_stdio
try: try:
import yaml # type: ignore import yaml # type: ignore
except ImportError: except ImportError:
yaml = None yaml = None
configure_utf8_stdio()
SCRIPT_DIR = Path(__file__).resolve().parent SCRIPT_DIR = Path(__file__).resolve().parent
SKILL_DIR = SCRIPT_DIR.parent SKILL_DIR = SCRIPT_DIR.parent
TEMPLATES_DIR = SKILL_DIR / "templates" TEMPLATES_DIR = SKILL_DIR / "templates"
@@ -18,6 +18,11 @@ import os
import re import re
from pathlib import Path from pathlib import Path
from typing import List, Dict, Union, Any, Optional from typing import List, Dict, Union, Any, Optional
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
from PIL import Image, ExifTags from PIL import Image, ExifTags
@@ -33,6 +33,14 @@ from pathlib import Path
from urllib.parse import unquote, urlparse from urllib.parse import unquote, urlparse
from xml.etree import ElementTree as ET from xml.etree import ElementTree as ET
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
# ───────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────
# Format registry # Format registry
# ───────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────
@@ -20,6 +20,14 @@ from datetime import date, datetime, time
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
# ───────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────
# Format registry # Format registry
@@ -14,6 +14,14 @@ import sys
from pathlib import Path from pathlib import Path
from collections import Counter from collections import Counter
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
try: try:
import fitz # PyMuPDF import fitz # PyMuPDF
except ImportError: except ImportError:
@@ -34,11 +34,19 @@ from io import BytesIO
from pathlib import Path from pathlib import Path
from urllib.parse import quote from urllib.parse import quote
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
from pptx import Presentation from pptx import Presentation
from pptx.enum.action import PP_ACTION from pptx.enum.action import PP_ACTION
from pptx.enum.shapes import MSO_SHAPE_TYPE from pptx.enum.shapes import MSO_SHAPE_TYPE
from pptx.oxml.ns import qn from pptx.oxml.ns import qn
configure_utf8_stdio()
EMU_PER_INCH = 914400 EMU_PER_INCH = 914400
OFFICE_VECTOR_EXTENSIONS = {"emf", "wmf"} OFFICE_VECTOR_EXTENSIONS = {"emf", "wmf"}
@@ -35,8 +35,17 @@ import os
import re import re
import sys import sys
import time import time
from pathlib import Path
from urllib.parse import urljoin, urlparse from urllib.parse import urljoin, urlparse
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
try: try:
import requests import requests
from bs4 import BeautifulSoup, NavigableString, Tag from bs4 import BeautifulSoup, NavigableString, Tag
@@ -59,6 +59,7 @@ _ROOT_SCRIPTS_DIR = _SCRIPTS_DIR.parent
if str(_ROOT_SCRIPTS_DIR) not in sys.path: if str(_ROOT_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_ROOT_SCRIPTS_DIR)) sys.path.insert(0, str(_ROOT_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
from server_common import ( # noqa: E402 from server_common import ( # noqa: E402
claim_lock as _claim_lock, claim_lock as _claim_lock,
find_free_port as _find_free_port, find_free_port as _find_free_port,
@@ -67,6 +68,8 @@ from server_common import ( # noqa: E402
release_lock as _release_lock, release_lock as _release_lock,
) )
configure_utf8_stdio()
from annotations import ( # noqa: E402 from annotations import ( # noqa: E402
assign_temp_ids, assign_temp_ids,
is_editable_attr, is_editable_attr,
@@ -54,6 +54,14 @@ from typing import TYPE_CHECKING
from urllib.parse import unquote from urllib.parse import unquote
from xml.etree import ElementTree as ET from xml.etree import ElementTree as ET
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __package__ in {None, ''}: if __package__ in {None, ''}:
import types import types
@@ -24,6 +24,14 @@ from pathlib import Path
from xml.etree import ElementTree as ET from xml.etree import ElementTree as ET
from urllib.parse import unquote from urllib.parse import unquote
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
try: try:
from PIL import Image from PIL import Image
except ImportError: except ImportError:
@@ -51,6 +51,14 @@ import argparse
from pathlib import Path from pathlib import Path
from xml.etree import ElementTree as ET from xml.etree import ElementTree as ET
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
# Default icon directory # Default icon directory
DEFAULT_ICONS_DIR = Path(__file__).parent.parent.parent / 'templates' / 'icons' DEFAULT_ICONS_DIR = Path(__file__).parent.parent.parent / 'templates' / 'icons'
@@ -17,6 +17,15 @@ import base64
import re import re
import sys import sys
import argparse import argparse
from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
def get_mime_type(filename: str, file_bytes: bytes | None = None) -> str: def get_mime_type(filename: str, file_bytes: bytes | None = None) -> str:
@@ -31,6 +31,14 @@ import argparse
from pathlib import Path from pathlib import Path
from xml.etree import ElementTree as ET from xml.etree import ElementTree as ET
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
# Try to import PIL for getting image dimensions # Try to import PIL for getting image dimensions
try: try:
from PIL import Image from PIL import Image
@@ -2,8 +2,17 @@ import os
import sys import sys
import re import re
import argparse import argparse
from pathlib import Path
from xml.etree import ElementTree as ET from xml.etree import ElementTree as ET
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
SVG_NS = "http://www.w3.org/2000/svg" SVG_NS = "http://www.w3.org/2000/svg"
NSMAP = {"svg": SVG_NS} NSMAP = {"svg": SVG_NS}
@@ -26,6 +26,14 @@ from pathlib import Path
from typing import Any, Tuple from typing import Any, Tuple
from xml.etree import ElementTree as ET from xml.etree import ElementTree as ET
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
def rect_to_rounded_path( def rect_to_rounded_path(
x: float, x: float,
@@ -19,6 +19,10 @@ from typing import List, Dict, Tuple
from collections import Counter, defaultdict from collections import Counter, defaultdict
from xml.etree import ElementTree as ET from xml.etree import ElementTree as ET
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
try: try:
from project_utils import CANVAS_FORMATS from project_utils import CANVAS_FORMATS
from error_helper import ErrorHelper from error_helper import ErrorHelper
@@ -11,7 +11,10 @@ from pathlib import Path
# Ensure the scripts directory is on sys.path so the package can be found # Ensure the scripts directory is on sys.path so the package can be found
sys.path.insert(0, str(Path(__file__).resolve().parent)) sys.path.insert(0, str(Path(__file__).resolve().parent))
from console_encoding import configure_utf8_stdio
from svg_to_pptx import main from svg_to_pptx import main
configure_utf8_stdio()
if __name__ == '__main__': if __name__ == '__main__':
main() main()
@@ -9,6 +9,14 @@ import argparse
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __package__ in {None, ''}: if __package__ in {None, ''}:
import types import types
@@ -21,7 +21,10 @@ from pathlib import Path
# Ensure the scripts directory is on sys.path so the package can be found. # Ensure the scripts directory is on sys.path so the package can be found.
sys.path.insert(0, str(Path(__file__).resolve().parent)) sys.path.insert(0, str(Path(__file__).resolve().parent))
from console_encoding import configure_utf8_stdio
from template_fill_pptx import main from template_fill_pptx import main
configure_utf8_stdio()
if __name__ == "__main__": if __name__ == "__main__":
raise SystemExit(main()) raise SystemExit(main())
@@ -8,6 +8,14 @@ import sys
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
_SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from console_encoding import configure_utf8_stdio # noqa: E402
configure_utf8_stdio()
if __package__ in {None, ''}: if __package__ in {None, ''}:
import types import types
@@ -28,6 +28,10 @@ import argparse
import re import re
from pathlib import Path from pathlib import Path
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
HEADING_RE = re.compile(r'^(#{1,6})\s*(.+?)\s*$') HEADING_RE = re.compile(r'^(#{1,6})\s*(.+?)\s*$')
HR_RE = re.compile(r'^\s*[-*]{3,}\s*$') HR_RE = re.compile(r'^\s*[-*]{3,}\s*$')
@@ -15,6 +15,10 @@ import subprocess
import sys import sys
from pathlib import Path from pathlib import Path
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
TOOLS_DIR = Path(__file__).resolve().parent TOOLS_DIR = Path(__file__).resolve().parent
SKILL_DIR = TOOLS_DIR.parent SKILL_DIR = TOOLS_DIR.parent
@@ -27,6 +27,10 @@ import re
import sys import sys
from pathlib import Path from pathlib import Path
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
HEX_RE = re.compile(r"^#(?:[0-9A-Fa-f]{3,4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$") HEX_RE = re.compile(r"^#(?:[0-9A-Fa-f]{3,4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$")
FONT_FAMILY_RE = re.compile(r"""(font-family\s*=\s*)(["'])(.*?)\2""") FONT_FAMILY_RE = re.compile(r"""(font-family\s*=\s*)(["'])(.*?)\2""")
@@ -38,6 +38,10 @@ import urllib.request
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path from pathlib import Path
from console_encoding import configure_utf8_stdio
configure_utf8_stdio()
# Histogram threshold: PNG counts as "all background" if a single quantized # Histogram threshold: PNG counts as "all background" if a single quantized
# color bucket holds >= ALL_BG_THRESHOLD of pixels. Guards against blank # color bucket holds >= ALL_BG_THRESHOLD of pixels. Guards against blank
@@ -6,5 +6,13 @@
"google": { "google": {
"summary": "Google brand identity — multi-product corporate decks, developer events (Google I/O style), education and training in the Google ecosystem", "summary": "Google brand identity — multi-product corporate decks, developer events (Google I/O style), education and training in the Google ecosystem",
"primary_color": "#4285F4" "primary_color": "#4285F4"
},
"中国电建": {
"summary": "POWERCHINA brand identity for engineering project reports, technical proposals, business negotiations, and corporate promotion",
"primary_color": "#00418D"
},
"中汽研": {
"summary": "CATARC brand identity for product certification, evaluation reports, automotive technology promotion, and business visits",
"primary_color": "#004098"
} }
} }
@@ -0,0 +1,73 @@
---
brand_id: 中国电建
kind: brand
summary: POWERCHINA brand identity for engineering project reports, technical proposals, business negotiations, and corporate promotion
primary_color: "#00418D"
---
# POWERCHINA (中国电建) Brand Specification
> Identity-only preset. No SVG page roster; pages are composed freely under these brand constraints.
## I. Brand Overview
| Property | Value |
|---|---|
| Brand Name | POWERCHINA / 中国电建 |
| Use Cases | Engineering project reports, technical proposal presentations, business negotiations, corporate promotion, annual summaries |
| Tone | Professional, composed, international, state-owned enterprise style |
## II. Color Scheme
| Role | HEX | Provenance | Notes |
|---|---|---|---|
| primary | `#00418D` | approx | POWERCHINA Blue, inherited from the existing POWERCHINA deck templates |
| secondary | `#002B5C` | approx | Deep blue for chapter pages, dark headers, and gradient bases |
| accent (blue) | `#0066CC` | approx | Bright blue for charts, links, and secondary emphasis |
| accent (light) | `#4A90D9` | approx | Sky blue for tertiary highlights and decorative rules |
| accent (red) | `#C41E3A` | approx | China red for milestones, key data, and national-strength emphasis |
| accent (gold) | `#C9A227` | approx | Honors, achievements, and award moments |
| text | `#1A1A1A` | approx | Primary body text on light backgrounds |
| muted-text | `#4A5568` | approx | Secondary descriptions and annotations |
| bg | `#FFFFFF` | approx | Standard light presentation background |
| surface | `#F4F6F8` | approx | Light gray card and module surfaces |
## III. Typography
| Role | Family | Weight |
|---|---|---|
| title | `"Microsoft YaHei", "微软雅黑", "SimHei", Arial, sans-serif` | 700 |
| body | `"Microsoft YaHei", "微软雅黑", Arial, sans-serif` | 400 |
| data | `Arial, "Microsoft YaHei", sans-serif` | 600-700 |
Use PowerPoint-safe CJK fonts by default. Keep English technical labels in Arial when they function as engineering annotations or dashboard labels.
## IV. Logo
POWERCHINA templates may use the group brand logo or subsidiary logos depending on the presenting entity. Pick one primary logo per deck and avoid mixing several logos on every page.
| File | Form | Usage |
|---|---|---|
| `./电建logo.png` | POWERCHINA symbol / primary mark (234x236) | Cover, ending, and compact brand moments where the group identity is required |
| `./中国水务logo.png` | China Water logo lockup (1108x190) | Subsidiary-led water-business reports; cover or ending lockup |
| `./华东院logo.png` | East China Institute logo lockup (1438x300) | Institute-led technical reports and engineering proposals |
| `./水电三局logo.png` | Sinohydro Bureau 3 logo lockup (629x75) | Bureau-led project reports and construction updates |
- Cover / ending: use the selected primary logo at readable size.
- Per-page header: optional; use a compact logo only when it does not crowd the title bar.
- Dark backgrounds: prefer white/inverted treatment when available; otherwise place the logo on a white backing plate.
- Clearspace: leave at least 0.5x logo height around the mark or lockup.
## V. Voice & Tone
- Formality: formal
- Person: organization-first, "we / 我们" when speaking as the company
- Emoji: avoid
- Abbreviations: spell out Chinese organization names on first use; POWERCHINA is acceptable after first mention
- Style: emphasize engineering reliability, delivery capability, safety, scale, and public-sector credibility
## VI. Icon Style
- Preference: filled or sturdy stroke icons
- Recommended libraries: `tabler-filled`, `chunk-filled`, or `tabler-outline` with heavier stroke
- Avoid thin decorative icons for engineering / construction content; prefer solid symbols, process arrows, infrastructure metaphors, and chart-friendly pictograms.
@@ -0,0 +1,72 @@
---
brand_id: 中汽研
kind: brand
summary: CATARC brand identity for product certification, evaluation reports, automotive technology promotion, and business visits
primary_color: "#004098"
---
# CATARC (中汽研) Brand Specification
> Identity-only preset. No SVG page roster; pages are composed freely under these brand constraints.
## I. Brand Overview
| Property | Value |
|---|---|
| Brand Name | CATARC / 中汽研 |
| Use Cases | Product certification display, evaluation presentations, automotive technology promotion, business visits, high-end technical reporting |
| Tone | Professional, authoritative, trustworthy, technical-consulting style |
## II. Color Scheme
| Role | HEX | Provenance | Notes |
|---|---|---|---|
| primary | `#004098` | approx | CATARC deep blue, inherited from the standard CATARC deck template |
| secondary | `#003366` | approx | Dark business blue for covers, chapter pages, and formal section headers |
| accent (blue) | `#0050B3` | approx | Bright blue for technology emphasis and gradients |
| accent (tech) | `#1890FF` | approx | Modern tech blue for forward-looking automotive topics |
| accent (cyan) | `#00E5FF` | approx | High-energy data / future-tech highlights; use sparingly |
| accent (red) | `#CC0000` | approx | Certification warnings, failed checks, and key risk emphasis |
| text | `#333333` | approx | Primary body text on light backgrounds |
| muted-text | `#666666` | approx | Secondary descriptions and notes |
| bg | `#FFFFFF` | approx | Standard light presentation background |
| surface | `#F5F5F5` | approx | Secondary content blocks and cards |
| border | `#E0E0E0` | approx | Dividers, table borders, and technical grids |
## III. Typography
| Role | Family | Weight |
|---|---|---|
| title | `"Microsoft YaHei", "微软雅黑", "SimHei", Arial, Calibri, sans-serif` | 700 |
| body | `"Microsoft YaHei", "微软雅黑", Arial, Calibri, sans-serif` | 400 |
| data | `Arial, "Microsoft YaHei", sans-serif` | 600-700 |
Use clear PowerPoint-safe CJK fonts. Keep numbers, certification codes, and technical abbreviations in Arial for legibility.
## IV. Logo
CATARC uses separate large and header lockups. Choose the large logo for brand moments and the compact logo for page chrome.
| File | Form | Usage |
|---|---|---|
| `./大型 logo.png` | Large CATARC lockup (592x238) | Cover hero, chapter/ending brand moments, and dark page lockups |
| `./右上角 logo.png` | Compact header logo (113x50) | Upper-right page header, title bar, and navigation areas |
- Cover / ending: use `大型 logo.png` where the brand must read strongly.
- Content pages: use `右上角 logo.png` only when the layout has enough title-bar space.
- Watermarks: large logo may be used as a very faint watermark on content pages.
- Clearspace: leave at least 0.5x logo height around the mark or lockup.
## V. Voice & Tone
- Formality: formal-neutral
- Person: organization-first, "we / 我们" only when speaking as CATARC
- Emoji: avoid
- Abbreviations: CATARC is acceptable; spell out Chinese entity names on first use
- Style: emphasize certification credibility, testing rigor, technical evidence, automotive industry insight, and trusted advisory authority
## VI. Icon Style
- Preference: filled or clean stroke icons
- Recommended libraries: `tabler-filled`, `chunk-filled`, or `tabler-outline`
- Prefer automotive, certification, testing, dashboard, data, process, and compliance metaphors. Avoid playful or consumer-app icon styling.
@@ -5,36 +5,18 @@
"page_count": 5, "page_count": 5,
"primary_color": "#C00000" "primary_color": "#C00000"
}, },
"中国电建_常规": { "中国电建": {
"summary": "Engineering project reports, technical proposal presentations, business negotiations, corporate promotion, annual summaries.", "summary": "Engineering project reports, technical proposal presentations, business negotiations, corporate promotion, annual summaries.",
"canvas_format": "ppt169", "canvas_format": "ppt169",
"page_count": 5, "page_count": 5,
"primary_color": "#00418D" "primary_color": "#00418D"
}, },
"中国电建_现代": { "中汽研": {
"summary": "Major engineering reports, international market promotion, technology achievement showcases, high-end business negotiations.",
"canvas_format": "ppt169",
"page_count": 5,
"primary_color": "#00418D"
},
"中汽研_商务": {
"summary": "Product certification display, evaluation presentations, technology promotion, high-end business reporting.",
"canvas_format": "ppt169",
"page_count": 5,
"primary_color": "#003366"
},
"中汽研_常规": {
"summary": "Product certification display, evaluation presentations, technology promotion, business visits.", "summary": "Product certification display, evaluation presentations, technology promotion, business visits.",
"canvas_format": "ppt169", "canvas_format": "ppt169",
"page_count": 5, "page_count": 5,
"primary_color": "#004098" "primary_color": "#004098"
}, },
"中汽研_现代": {
"summary": "Forward-looking technology showcases, strategic releases, high-end business reporting.",
"canvas_format": "ppt169",
"page_count": 5,
"primary_color": "#001529"
},
"招商银行": { "招商银行": {
"summary": "交易银行产品介绍、销售收款方案汇报、客户案例拆解、分行培训材料.", "summary": "交易银行产品介绍、销售收款方案汇报、客户案例拆解、分行培训材料.",
"canvas_format": "ppt169", "canvas_format": "ppt169",
@@ -1,5 +1,5 @@
--- ---
deck_id: 中国电建_常规 deck_id: 中国电建
kind: deck kind: deck
summary: Engineering project reports, technical proposal presentations, business negotiations, corporate promotion, annual summaries. summary: Engineering project reports, technical proposal presentations, business negotiations, corporate promotion, annual summaries.
canvas_format: ppt169 canvas_format: ppt169
@@ -17,7 +17,7 @@ primary_color: "#00418D"
| Property | Description | | Property | Description |
| -------------- | ---------------------------------------------------------------- | | -------------- | ---------------------------------------------------------------- |
| **Template Name** | 中国电建_常规 (formerly powerchina) | | **Template Name** | 中国电建 |
| **Use Cases** | Engineering project reports, technical proposal presentations, business negotiations, corporate promotion, annual summaries | | **Use Cases** | Engineering project reports, technical proposal presentations, business negotiations, corporate promotion, annual summaries |
| **Design Tone** | Professional, composed, international, state-owned enterprise style | | **Design Tone** | Professional, composed, international, state-owned enterprise style |
| **Theme Mode** | Light theme (white background + POWERCHINA blue accent) | | **Theme Mode** | Light theme (white background + POWERCHINA blue accent) |
@@ -1,90 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="720" viewBox="0 0 1280 720">
<!-- 模板: 封面页 - 中国电建现代风 (POWERCHINA Modern) -->
<defs>
<!-- 主背景渐变: 深海蓝 -> 科技蓝 -->
<linearGradient id="mainGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#001F45;stop-opacity:1" />
<stop offset="50%" style="stop-color:#003270;stop-opacity:1" />
<stop offset="100%" style="stop-color:#00418D;stop-opacity:1" />
</linearGradient>
<!-- 装饰渐变: 亮蓝 -> 透明 -->
<linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#0066CC;stop-opacity:1" />
<stop offset="100%" style="stop-color:#00418D;stop-opacity:0" />
</linearGradient>
<!-- 地球经纬网格 (Global Grid) -->
<pattern id="grid" x="0" y="0" width="100" height="50" patternUnits="userSpaceOnUse">
<path d="M 100 0 L 0 0 0 50" fill="none" stroke="#FFFFFF" stroke-opacity="0.05" stroke-width="1"/>
</pattern>
</defs>
<!-- 背景层 -->
<rect width="1280" height="720" fill="url(#mainGrad)"/>
<rect width="1280" height="720" fill="url(#grid)"/>
<!-- 底部基石 (Foundation Block) -->
<!-- 象征大坝地基或坚实基础 -->
<path d="M0,580 L1280,500 L1280,720 L0,720 Z" fill="#001533" fill-opacity="0.6"/>
<rect x="0" y="680" width="1280" height="40" fill="#000F24"/>
<!-- 装饰线条 (Construction Lines) -->
<!-- 对角线切角,体现切割感 -->
<path d="M0,0 L400,0 L300,720 L0,720 Z" fill="#FFFFFF" fill-opacity="0.02"/>
<line x1="300" y1="0" x2="300" y2="720" stroke="#FFFFFF" stroke-opacity="0.05" stroke-dasharray="10,5"/>
<!-- 顶部 Logo 区域 (右上角) -->
<rect x="1000" y="0" width="280" height="80" fill="#FFFFFF" fill-opacity="0.1"/>
<text x="1140" y="50" text-anchor="middle" font-family="Arial, sans-serif" font-size="24" font-weight="bold" fill="#FFFFFF" letter-spacing="2">
POWERCHINA
</text>
<!-- 装饰红条 (点睛) -->
<rect x="1000" y="0" width="8" height="80" fill="#C41E3A"/>
<!-- 主标题区域 (居中偏左) -->
<g transform="translate(100, 260)">
<!-- 巨大装饰年份或序号 -->
<text x="-20" y="-60" font-family="Arial, sans-serif" font-size="180" font-weight="bold" fill="#FFFFFF" fill-opacity="0.03">
2026
</text>
<!-- 主标题 -->
<text x="0" y="0" font-family="Microsoft YaHei, SimHei, sans-serif" font-size="64" font-weight="bold" fill="#FFFFFF" letter-spacing="2">
{{TITLE}}
</text>
<!-- 下划线装饰 -->
<rect x="0" y="25" width="120" height="8" fill="#0066CC"/>
<rect x="120" y="25" width="800" height="2" fill="#FFFFFF" fill-opacity="0.3"/>
<!-- 副标题 -->
<text x="0" y="80" font-family="Microsoft YaHei, Arial, sans-serif" font-size="32" fill="#B0C4DE" font-weight="normal">
{{SUBTITLE}}
</text>
<!-- 汇报信息组 -->
<g transform="translate(0, 160)">
<!-- 左侧竖线 -->
<rect x="0" y="0" width="4" height="80" fill="#C41E3A"/>
<text x="20" y="30" font-family="Microsoft YaHei, sans-serif" font-size="24" fill="#FFFFFF">
{{AUTHOR}}
</text>
<text x="20" y="70" font-family="Microsoft YaHei, sans-serif" font-size="20" fill="#FFFFFF" fill-opacity="0.7">
汇报人:{{PRESENTER}} | {{DATE}}
</text>
</g>
</g>
<!-- 底部公司全称 -->
<text x="640" y="708" text-anchor="middle" font-family="Microsoft YaHei, sans-serif" font-size="14" fill="#FFFFFF" fill-opacity="0.4" letter-spacing="1">
中国电力建设集团有限公司 · POWERCHINA CONSTRUCTION CORPORATION
</text>
</svg>

Before

Width:  |  Height:  |  Size: 4.0 KiB

Some files were not shown because too many files have changed in this diff Show More