| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- #!/usr/bin/env python3
- """Generate a dark Less and Cosmos double-sided employee business card."""
- from __future__ import annotations
- import argparse
- import html
- import json
- import re
- from pathlib import Path
- SKILL_DIR = Path(__file__).resolve().parent.parent
- ASSETS_DIR = SKILL_DIR / "assets"
- def clean(value: str, field: str) -> str:
- value = " ".join(value.strip().split())
- if not value:
- raise ValueError(f"{field} must not be empty")
- return value
- def normalize_phone(value: str) -> str:
- value = clean(value, "phone")
- value = re.sub(r"\s+", " ", value)
- if value.startswith("+86"):
- return value
- if value.startswith("86 "):
- return "+" + value
- return "+86 " + value
- def split_title(value: str) -> tuple[str, str]:
- parts = re.split(r"\s*[//]\s*", value, maxsplit=1)
- if len(parts) == 1:
- return parts[0], ""
- return parts[0], parts[1]
- def render_template(template: str, values: dict[str, str]) -> str:
- for key, value in values.items():
- template = template.replace("{{" + key + "}}", html.escape(value))
- unresolved = sorted(set(re.findall(r"\{\{([A-Z_]+)\}\}", template)))
- if unresolved:
- raise ValueError("Unresolved template fields: " + ", ".join(unresolved))
- return template
- def build_preview(name: str) -> str:
- safe_name = html.escape(name)
- return f"""<!doctype html>
- <html lang=\"zh-CN\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">
- <title>{safe_name} · 纯粹星辰名片</title><style>
- *{{box-sizing:border-box}}body{{margin:0;background:#15171d;font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif}}
- main{{width:min(1480px,94vw);margin:0 auto;padding:5vw 0}}h1{{margin:0 0 28px;color:#c5c6c7;font-size:18px;font-weight:500;letter-spacing:2px}}
- .pair{{display:grid;grid-template-columns:1fr 1fr;gap:30px}}figure{{margin:0}}img{{display:block;width:100%;box-shadow:0 16px 46px rgba(25,28,31,.12)}}
- figcaption{{margin-top:12px;color:#8d9297;font-size:11px;letter-spacing:2px;text-transform:uppercase}}@media(max-width:800px){{.pair{{grid-template-columns:1fr}}}}
- </style></head><body><main><h1>{safe_name} · 纯粹星辰</h1><div class=\"pair\">
- <figure><img src=\"front.svg\" alt=\"名片正面\"><figcaption>Front</figcaption></figure>
- <figure><img src=\"back.svg\" alt=\"名片背面\"><figcaption>Back</figcaption></figure>
- </div></main></body></html>"""
- def main() -> None:
- parser = argparse.ArgumentParser(description=__doc__)
- parser.add_argument("--name", required=True, help="Chinese name")
- parser.add_argument("--title", required=True, help="Role, e.g. 联合创始人 / CMO")
- parser.add_argument("--email", required=True)
- parser.add_argument("--phone", required=True)
- parser.add_argument("--output-dir", required=True, type=Path)
- args = parser.parse_args()
- name = clean(args.name, "name")
- title = clean(args.title, "title")
- title_zh, title_en = split_title(title)
- email = clean(args.email, "email")
- if "@" not in email or email.startswith("@") or email.endswith("@"):
- raise ValueError("email does not look valid")
- phone = normalize_phone(args.phone)
- data = {
- "name": name,
- "title": title,
- "email": email,
- "phone": phone,
- "company": "杭州纯粹星辰人工智能科技有限公司",
- "address": "浙江省杭州市西湖区转塘街道云腾丝路中心5号楼商业208室",
- "website": "lessncosmos.com",
- "slogan": "构建能够持续学习与进化的企业级人工智能系统。",
- }
- args.output_dir.mkdir(parents=True, exist_ok=True)
- (args.output_dir / "front.svg").write_text(
- (ASSETS_DIR / "common-front.svg").read_text(encoding="utf-8"), encoding="utf-8"
- )
- template = (ASSETS_DIR / "back-template.svg").read_text(encoding="utf-8")
- replacements = {key.upper(): value for key, value in data.items()}
- replacements.update({"TITLE_ZH": title_zh, "TITLE_EN": title_en})
- (args.output_dir / "back.svg").write_text(
- render_template(template, replacements), encoding="utf-8"
- )
- (args.output_dir / "preview.html").write_text(build_preview(name), encoding="utf-8")
- (args.output_dir / "card-data.json").write_text(
- json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
- )
- print(f"Created business card in {args.output_dir.resolve()}")
- if __name__ == "__main__":
- main()
|