163 lines
9.2 KiB
Markdown
163 lines
9.2 KiB
Markdown
---
|
||
name: ubisam-ppt-make
|
||
description: >-
|
||
Ubisam SW 설계서 디자인 시스템으로 .pptx 덱을 만든다. 모든 페이지에 UbiSam 로고,
|
||
고정 폰트(한글=굴림 / 영문=Arial), 파란 accent 컬러. JSON spec을 받아 python-pptx로 렌더링한다.
|
||
"Ubisam 설계서 PPT 만들어줘", "유비삼 스타일 덱", "이 양식으로 발표자료", "/ubisam-ppt-make"
|
||
요청 시 사용. `ubisam-deliverables`·`ubisam-deliverable-rebuild`가 PPTX 렌더링을 위임할 때도
|
||
반드시 이 스킬을 쓴다 — 다른 곳에서 python-pptx로 **덱을 새로 만들지** 않는다.
|
||
(예외: `ui_layout_image`가 **기존 pptx에 이미지를 삽입·교체**하는 것은 렌더링이 아니므로 허용.)
|
||
구분 — 기획·조사부터 필요한 일반 발표자료는 `ppt_Agent`, 이 스킬은 **Ubisam 브랜드 spec→pptx 렌더링 전용**.
|
||
맨손 "업데이트"·"다시 실행"만으로는 이 스킬을 고르지 않는다 — 무엇을 만들지 먼저 확인한다.
|
||
---
|
||
|
||
# ubisam-ppt-make — Ubisam SW 설계서 deck builder
|
||
|
||
Deterministic PPT builder following the design system extracted from
|
||
`P-PRJ_BFHB_UBISAM_Ver1.4.pptx` and the user's edited layout showcase
|
||
(UbiSam logo placed on every page). Takes a JSON spec and emits a `.pptx`
|
||
via python-pptx.
|
||
|
||
## When to use
|
||
|
||
- The user types `/ubisam-ppt-make`
|
||
- Requests like "Ubisam 설계서 PPT 만들어줘" / "유비삼 스타일 덱" / "this format으로"
|
||
- New design-document presentations that must keep Ubisam branding
|
||
- **`ubisam-deliverables` 스킬이 PPTX 생성을 위임할 때** — deliverables는 spec JSON만 준비하고 렌더링은 반드시 이 스킬의 `build_deck.py`로 위임한다
|
||
|
||
> **역할 경계**: `ubisam-ppt-make`는 PPTX 렌더링 엔진. JSON spec → `.pptx` 변환만 담당.
|
||
> 산출물 구조·MD·xlsx 관리는 `ubisam-deliverables` 스킬이 담당.
|
||
|
||
## Design system (fixed)
|
||
|
||
- Slide: 16:9 (13.333 × 7.5 in)
|
||
- **슬라이드 마스터 배경** (모든 pptx 자동 적용):
|
||
- 캔버스: **`WHITE`** — `_apply_master_background()`가 마스터와 전 레이아웃에 흰색을 적용한다
|
||
(`build_deck.py:55-73`). `BG_SOFT`(#F4F7FA)는 **패널·표 짝수행 fill 전용**이지 배경색이 아니다
|
||
- 하단 accent 바: `#4A8DCB` h=0.15in @ y=7.35in (마스터 공통)
|
||
- cover·divider·closing은 자체 full rect로 덮음
|
||
- 캔버스 배경은 `_apply_master_background()`가 `WHITE`로 고정한다. spec JSON으로 재정의할 수 없다
|
||
- Accent: `#4A8DCB` (blue), dark `#2F5E8C`; warn `#E53E3E`
|
||
- Ink `#262A2E`, sub-gray `#6B7278`, soft panel `#F4F7FA`
|
||
- **Font rule (do not change): 한글=굴림, 영문=Arial** — applied to every run
|
||
- **UbiSam logo on every page**: cover = bottom-right, all others = top-right
|
||
- Header pattern: `0X.` badge + title + KO subtitle + split accent bar
|
||
- Footer: `Ubisam | <doc_title>` + page number (content pages only)
|
||
|
||
## 12 layout types (`slide.type`)
|
||
|
||
Document layouts:
|
||
|
||
| type | purpose | key fields |
|
||
|------|---------|-----------|
|
||
| `cover` | 표지 | `tag, title, subtitles[], team, contacts[]` |
|
||
| `history` | 변경 이력 표 | `rows[] = [No,Version,날짜,변경자,내용]` |
|
||
| `agenda` | 목차 그리드(≤6) | `items[] = {no,title,sub}` |
|
||
| `divider` | 섹션 간지(큰 숫자) | `no, title, subtitle` |
|
||
| `content` | 4분할 패널 | `no, title, subtitle, panels[] = {title,body}` |
|
||
| `flow` | 시퀀스 플로우차트 | `no, title, subtitle, steps[]={title,code,kind}, branch{title,sub}` |
|
||
| `table` | 스펙/IO 표 | `no, title, headers[], widths[], center_cols[], rows[][]` |
|
||
| `closing` | 마무리 | `title, message, team, contacts[]` |
|
||
|
||
Manual layouts (HMI Manual / 27번):
|
||
|
||
| type | purpose | key fields |
|
||
|------|---------|-----------|
|
||
| `screen_guide` | 화면 설명(캡처+번호 콜아웃) | `no, title, caption, image(PNG경로), items[]={title,desc,pos:[x,y]}` |
|
||
| `procedure` | 조작 절차(번호 단계) | `no, title, steps[]={title,desc}` |
|
||
| `icon_ref` | 버튼/아이콘 설명(3열 카드, ≤6) | `no, title, items[]={icon,name,func}` |
|
||
| `alarm` | 알람·조치(주의 배너+표) | `no, title, notice, rows[]=[코드,알람명,원인,조치]` |
|
||
|
||
`kind` for flow steps: `"proc"` (process box) or `"dec"` (decision, blue outline).
|
||
`pos` in `screen_guide` items = callout marker offset (inch) inside the capture box.
|
||
Page numbers auto-increment over content/manual pages; cover/divider/closing excluded.
|
||
`divider`/`closing` use a white background with a `#0070C0` / `#2F5E8C` block.
|
||
|
||
## Choosing a layout for each piece of data
|
||
|
||
Match every chunk of source/analysis data to the layout whose intent fits it —
|
||
do not force everything into `table`. Full rules + disambiguation in
|
||
[`reference/layout_selection.md`](reference/layout_selection.md). Cheat sheet:
|
||
|
||
- ordered **human** steps → `procedure` · machine sequence **with branches** → `flow`
|
||
- 2–4 facets, one line each → `content` · repeating rows/cols → `table`
|
||
- annotate ONE screenshot → `screen_guide` · button/icon legend → `icon_ref`
|
||
- fault → cause → action (+safety) → `alarm` · revision log → `history`
|
||
|
||
## Workflow
|
||
|
||
1. **Gather info** — topic, doc title, sections, slide contents, contacts.
|
||
내용이 없으면 **무엇을 만들지 되묻는다.** 샘플 showcase는 사용자가 **명시적으로 샘플/레이아웃
|
||
예시를 요청했을 때만** 만든다 — 그냥 만들면 원하지 않은 13장 덱이 나온다.
|
||
2. **Classify each data chunk → layout** using the guide above, then
|
||
**write the spec JSON** — see `examples/showcase.json` and the type tables.
|
||
Reuse the original BFHB sample text only when the user wants the showcase.
|
||
3. **Run the builder**:
|
||
저장소 루트에서 한 줄로 실행한다 (`cd` 하지 않는다 — 출력이 스킬 폴더로 새는 것을 막기 위함):
|
||
```bash
|
||
PYTHONUTF8=1 py .claude/skills/ubisam-ppt-make/build_deck.py --input <spec.json> --output <출력 .pptx 절대·상대 경로>
|
||
```
|
||
`PYTHONUTF8=1`은 반드시 **같은 줄** 접두로 둔다. 별도 줄에 두면 적용되지 않아 한글이 깨진다.
|
||
4. **Verify** — render to PNG and eyeball every slide (see Verification).
|
||
5. **Report** — output path + slide summary.
|
||
|
||
## Spec schema (minimal)
|
||
|
||
```json
|
||
{
|
||
"doc_title": "{문서 제목}",
|
||
"slides": [
|
||
{ "type": "cover", "title": "...", "subtitles": ["..."],
|
||
"team": "{조직명}", "contacts": ["{이름} {직급} {email}"] },
|
||
{ "type": "divider", "no": "01", "title": "...", "subtitle": "..." },
|
||
{ "type": "content", "no": "02.", "title": "...", "subtitle": "...",
|
||
"panels": [ {"title": "...", "body": "..."} ] }
|
||
]
|
||
}
|
||
```
|
||
|
||
A full working spec is in [`examples/showcase.json`](examples/showcase.json).
|
||
|
||
## Verification
|
||
|
||
**가용성을 먼저 확인한다.** PNG 렌더 검증은 PowerPoint COM이 있어야 한다 —
|
||
`powershell -c "New-Object -ComObject PowerPoint.Application"` 이 실패하면 렌더 검증을 생략하고
|
||
보고에 **`PNG 검증 미수행 — PowerPoint 부재`** 로 명시한다(검증했다고 하지 않는다).
|
||
이 경우 아래 **폰트 감사**만 수행한다 — 그건 zipfile만으로 되므로 항상 가능하다.
|
||
|
||
PowerPoint가 있으면: Export every slide to PNG and inspect; check no overflow/overlap, logo on
|
||
every page, and 한글=굴림 / 영문=Arial on all runs.
|
||
|
||
```powershell
|
||
# PowerPoint COM export (Windows, PowerPoint installed)
|
||
$ppt = New-Object -ComObject PowerPoint.Application
|
||
$pres = $ppt.Presentations.Open($pptx, $true, $false, $false)
|
||
$i=1; foreach ($s in $pres.Slides) { $s.Export("$dir/slide$i.png","PNG",1280,720); $i++ }
|
||
$pres.Close(); $ppt.Quit()
|
||
```
|
||
|
||
Font audit (every `rPr` must show `ea=굴림`, `latin=Arial`):
|
||
```bash
|
||
PYTHONUTF8=1 py -c "import zipfile,re,sys;from collections import Counter;z=zipfile.ZipFile(sys.argv[1]);\
|
||
[print(Counter(re.findall(r'<a:ea typeface=\"([^\"]*)\"',z.read(n).decode('utf-8','ignore')))) \
|
||
for n in z.namelist() if re.match(r'ppt/slides/slide\\d+',n)]"
|
||
```
|
||
|
||
## Notes
|
||
|
||
- python-pptx sets only the latin font via `run.font.name`; `_set_font()` also
|
||
writes `<a:ea>`/`<a:cs>` so Korean renders in 굴림. Always go through it.
|
||
- Logo asset: `assets/Ubisam_logo_transparent.png`. On dark divider/closing the
|
||
colored logo has lower contrast but matches the user's original placement.
|
||
- Keep `.md` files under 200 lines (`rules.md` #4); split if it grows.
|
||
- **생성·덮어쓸 `.pptx` 경로를 먼저 제시하고 승인받는다** (`rules.md` #2). 같은 이름이 이미 있으면
|
||
덮어쓰기 전에 무엇이 사라지는지 한 줄로 알린다 — 렌더링은 되돌릴 수 없다.
|
||
- ⚠ **`build_deck.py` 의 `__main__` 데모와 `examples/showcase.json` 은 타 프로젝트(P-PRJ BFHB) 실데이터다.**
|
||
조직명·담당자·이력 행을 **그대로 복사하지 않는다.** 값은 `_meta/project.yaml` 또는 사용자 입력에서 온다.
|
||
- **`table` 높이 상한**: 표는 `_TABLE_MAX_H = 5.3"` (y 1.7" ~ 7.0")로 자동 제한.
|
||
행이 많아도 푸터(`Ubisam | <doc_title>`, y=7.05")를 절대 침범하지 않는다.
|
||
행 높이는 `min(0.5×rows, 5.3) / rows`로 균등 압축된다.
|
||
|
||
> 보고는 `rules.md` #11~13 출력 형식을 따르고(수정 줄=초록 화살표, 단어=주황, 미변경=기본색),
|
||
> 단계 전환마다 진행을 한 줄로 알리며(#10), 끝에 관련 `.md` 갱신 필요 여부를 묻는다(#6).
|