239 lines
15 KiB
PowerShell
239 lines
15 KiB
PowerShell
# UBGitea 소개 PPT 생성 (PowerPoint COM). 사용: powershell -File docs\presentation\build-pptx.ps1
|
||
# 좌표는 HTML 슬라이드와 같은 1920x1080 px 기준, 0.5배 해서 pt로 넣는다.
|
||
$ErrorActionPreference = 'Stop'
|
||
Add-Type -AssemblyName System.Drawing
|
||
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||
$img = Join-Path $root 'images'
|
||
$out = Join-Path $root 'UBGitea_소개.pptx'
|
||
$pdf = Join-Path $root 'UBGitea_소개.pdf'
|
||
$KR = '맑은 고딕'; $MONO = 'Consolas'
|
||
$BR = [string][char]11
|
||
|
||
function C($hex) { $r=[Convert]::ToInt32($hex.Substring(0,2),16); $g=[Convert]::ToInt32($hex.Substring(2,2),16); $b=[Convert]::ToInt32($hex.Substring(4,2),16); return $r + $g*256 + $b*65536 }
|
||
function P($v) { return [double]$v * 0.5 }
|
||
|
||
function T($s, $x, $y, $w, $h, $text, $size, $color, $bold=$false, $font=$KR, $align=1, $spacing=1.15) {
|
||
$b = $s.Shapes.AddTextbox(1, (P $x), (P $y), (P $w), (P $h))
|
||
$tf = $b.TextFrame; $tf.WordWrap = -1; $tf.AutoSize = 0
|
||
$tf.MarginLeft = 0; $tf.MarginRight = 0; $tf.MarginTop = 0; $tf.MarginBottom = 0
|
||
$r = $tf.TextRange; $r.Text = $text
|
||
$r.Font.Name = $font; $r.Font.NameFarEast = $KR; $r.Font.Size = (P $size)
|
||
$r.Font.Color.RGB = (C $color); $r.Font.Bold = $(if ($bold) { -1 } else { 0 })
|
||
$r.ParagraphFormat.Alignment = $align
|
||
$r.ParagraphFormat.LineRuleWithin = -1; $r.ParagraphFormat.SpaceWithin = $spacing
|
||
return $b
|
||
}
|
||
function B($s, $x, $y, $w, $h, $items, $size=26, $color='3E5068') {
|
||
$b = T $s $x $y $w $h ($items -join "`r") $size $color $false $KR 1 1.3
|
||
$pf = $b.TextFrame.TextRange.ParagraphFormat
|
||
$pf.Bullet.Visible = -1; $pf.Bullet.Character = 8226
|
||
$pf.LineRuleAfter = 0; $pf.SpaceAfter = 6
|
||
$b.TextFrame.Ruler.Levels.Item(1).FirstMargin = 0
|
||
$b.TextFrame.Ruler.Levels.Item(1).LeftMargin = 14
|
||
return $b
|
||
}
|
||
function Box($s, $x, $y, $w, $h, $fill, $line=$null, $round=$false) {
|
||
$shape = $s.Shapes.AddShape($(if ($round) { 5 } else { 1 }), (P $x), (P $y), (P $w), (P $h))
|
||
if ($round) { $shape.Adjustments.Item(1) = 0.06 }
|
||
$shape.Fill.ForeColor.RGB = (C $fill)
|
||
if ($line) { $shape.Line.ForeColor.RGB = (C $line); $shape.Line.Weight = 1 } else { $shape.Line.Visible = 0 }
|
||
$shape.Shadow.Visible = 0
|
||
return $shape
|
||
}
|
||
function I($s, $name, $x, $y, $w, $h, $border='C9DCF2') {
|
||
$path = Join-Path $img "$name.png"
|
||
$im = [System.Drawing.Image]::FromFile($path); $iw = $im.Width; $ih = $im.Height; $im.Dispose()
|
||
$k = [Math]::Min($w / $iw, $h / $ih); $pw = $iw * $k; $ph = $ih * $k
|
||
$px = $x + ($w - $pw) / 2; $py = $y + ($h - $ph) / 2
|
||
$p = $s.Shapes.AddPicture($path, 0, -1, (P $px), (P $py), (P $pw), (P $ph))
|
||
if ($border) { $p.Line.Visible = -1; $p.Line.ForeColor.RGB = (C $border); $p.Line.Weight = 1 }
|
||
return $p
|
||
}
|
||
function NewSlide($bg, $notes) {
|
||
$s = $pres.Slides.Add($pres.Slides.Count + 1, 12)
|
||
$s.FollowMasterBackground = 0; $s.Background.Fill.Solid(); $s.Background.Fill.ForeColor.RGB = (C $bg)
|
||
$s.NotesPage.Shapes.Placeholders.Item(2).TextFrame.TextRange.Text = $notes
|
||
return $s
|
||
}
|
||
function Eyebrow($s, $x, $y, $text, $color='1565C0', $size=26) { T $s $x $y 900 40 $text $size $color $true $MONO | Out-Null }
|
||
function H2($s, $x, $y, $w, $h, $text, $size=56, $color='10243F') { T $s $x $y $w $h $text $size $color $true $KR 1 1.1 | Out-Null }
|
||
|
||
# 왼쪽 글 + 오른쪽 폰 화면 3장
|
||
function ThreeShots($bg, $eyebrow, $title, $bullets, $shots, $notes) {
|
||
$s = NewSlide $bg $notes
|
||
Eyebrow $s 128 250 $eyebrow
|
||
H2 $s 128 296 440 160 $title
|
||
B $s 128 480 440 420 $bullets | Out-Null
|
||
$x = 648
|
||
foreach ($sh in $shots) {
|
||
I $s $sh[0] $x 130 360 760 | Out-Null
|
||
T $s $x 906 360 40 $sh[1] 24 '3E5068' $false $KR 2 | Out-Null
|
||
$x += 392
|
||
}
|
||
}
|
||
# 왼쪽 글 + 오른쪽 펼친 화면 1장
|
||
function WideShot($bg, $eyebrow, $title, $bullets, $shot, $notes) {
|
||
$s = NewSlide $bg $notes
|
||
Eyebrow $s 128 280 $eyebrow
|
||
H2 $s 128 326 760 160 $title
|
||
B $s 128 500 760 420 $bullets | Out-Null
|
||
I $s $shot 968 130 824 821 | Out-Null
|
||
}
|
||
|
||
$app = New-Object -ComObject PowerPoint.Application
|
||
$pres = $app.Presentations.Add(0)
|
||
$pres.PageSetup.SlideWidth = 960; $pres.PageSetup.SlideHeight = 540
|
||
|
||
# 1 표지
|
||
$s = NewSlide 'F4F8FD' '회사 Gitea 서버 조회용 안드로이드 앱. 순서: 개발 배경 → 주요 화면 → 기술 · 개발 방식 → 현황.'
|
||
Box $s 1180 0 740 1080 'DCEAF9' | Out-Null
|
||
I $s 'logo' 128 250 300 90 $null | Out-Null
|
||
$s.Shapes.Item($s.Shapes.Count).Left = (P 128)
|
||
Eyebrow $s 128 384 'UBGitea · Android · v0.2.6' '1565C0' 28
|
||
T $s 128 444 960 230 ("사내 Gitea" + $BR + "모바일 앱") 96 '10243F' $true $KR 1 1.0 | Out-Null
|
||
T $s 128 700 900 60 '팀 활동 · 코드 · 변경 내용 모바일 조회용 사내 Git 앱' 32 '3E5068' | Out-Null
|
||
T $s 128 790 900 40 '2026년 10월 · 사내 공유용' 24 '5A6B80' | Out-Null
|
||
I $s 'home' 1340 96 420 887 'B9D3F0' | Out-Null
|
||
|
||
# 2 개발 배경
|
||
$s = NewSlide '10243F' '웹 기능 전체 이식 아님. 1순위 팀 활동 확인, 2순위 코드 · 변경 내용 조회. 소스 수정 · 병합 · 삭제 기능 의도적 제외.'
|
||
Eyebrow $s 128 128 '개발 배경' '7DB9F5'
|
||
H2 $s 128 176 1500 180 ("목적: 이석 중에도" + $BR + "팀 작업 현황 모바일 확인") 64 'F4F8FD'
|
||
$cards = @(
|
||
@('팀 활동 우선', 'Push 이력 · 열린 PR · 이슈 · 내 확인 필요 항목 첫 화면 표시', '4FA3F7'),
|
||
@('필요 부분만 코드 조회', '목차 · 클래스 · 함수 단위 이동, 두 파일 좌우 비교. 팀 주력 언어 C# · C++ 기준', '4FA3F7'),
|
||
@('읽기 중심 · 보안', '회사 Gitea 계정 로그인. 쓰기 기능은 저장소 즐겨찾기(별표)만 허용', 'F0A04B'))
|
||
$x = 128
|
||
foreach ($c in $cards) {
|
||
Box $s $x 620 533 332 '1A3558' | Out-Null
|
||
Box $s $x 620 533 6 $c[2] | Out-Null
|
||
T $s ($x+40) 666 453 50 $c[0] 34 'F4F8FD' $true | Out-Null
|
||
T $s ($x+40) 736 453 190 $c[1] 26 'D3E2F4' $false $KR 1 1.4 | Out-Null
|
||
$x += 565
|
||
}
|
||
|
||
# 3 탭 구성
|
||
$s = NewSlide 'F4F8FD' '우측: Z Fold 펼친 화면. 넓은 화면에서 탭은 좌측 레일로 이동, 홈은 팀 활동 · 타임라인 2단 표시.'
|
||
Eyebrow $s 128 220 '화면 구성'
|
||
H2 $s 128 266 760 80 '탭 4개 구성'
|
||
$rows = @(@('홈','오늘의 팀 활동과 타임라인, 내 확인이 필요한 항목'), @('저장소','전체·즐겨찾기 저장소, 브랜치, 파일, 커밋'), @('작업','팀 전체·리뷰 요청·나에게 할당된 이슈와 PR'), @('알림','Gitea 알림과 즐겨찾기 저장소의 Push 알림'))
|
||
$y = 380
|
||
foreach ($r in $rows) {
|
||
Box $s 128 $y 760 2 'C9DCF2' | Out-Null
|
||
T $s 128 ($y+26) 150 50 $r[0] 34 '1565C0' $true | Out-Null
|
||
T $s 306 ($y+32) 582 80 $r[1] 26 '3E5068' | Out-Null
|
||
$y += 104
|
||
}
|
||
Box $s 128 $y 760 2 'C9DCF2' | Out-Null
|
||
T $s 128 ($y+36) 760 40 '접은 화면: 하단 탭 / 펼친 화면 · 가로 화면: 좌측 레일' 24 '5A6B80' | Out-Null
|
||
I $s 'home_wide' 968 130 824 821 | Out-Null
|
||
|
||
# 4 홈
|
||
$s = NewSlide 'FBFCFE' '상단 숫자: 당일 Push, 열린 PR, 신규 이슈. 즐겨찾기 선택 시 별표 저장소 활동만 표시. 하단 타임라인: Push · PR · 이슈 시간순.'
|
||
Eyebrow $s 128 250 '홈 · 팀 활동'
|
||
H2 $s 128 296 1178 160 ("첫 화면:" + $BR + "당일 팀 활동 요약")
|
||
$stats = @(@('Push','당일 커밋'), @('PR','열린 요청'), @('이슈','신규 이슈'))
|
||
$x = 128
|
||
foreach ($st in $stats) {
|
||
Box $s $x 486 368 150 'E6F0FB' $null $true | Out-Null
|
||
T $s ($x+28) 508 320 60 $st[0] 44 '1565C0' $true | Out-Null
|
||
T $s ($x+28) 582 320 40 $st[1] 24 '3E5068' | Out-Null
|
||
$x += 392
|
||
}
|
||
B $s 128 676 1178 240 @('조회 범위: 내 피드 · 즐겨찾기 · 조직별 (선택 범위 저장)', '활동 저장소별 최근 브랜치 · 활동량 그래프', '타임라인: 작성자 · 브랜치 · Push 내용') 28 | Out-Null
|
||
I $s 'home' 1402 128 390 824 | Out-Null
|
||
|
||
# 5 저장소
|
||
ThreeShots 'F4F8FD' '저장소' ("저장소 조회 ·" + $BR + "즐겨찾기") @('조직별 저장소 목록', '별표 저장소: 즐겨찾기 탭 · 홈에 표시', '브랜치 · 태그 목록', '저장소 목록 기기 저장, 오프라인 조회') @(@('repos','전체 저장소'), @('favorites','즐겨찾기'), @('branch','브랜치 · 태그')) '계정 접근 가능 저장소 조직별 표시. 별표는 Gitea 별표와 동기화. 앱 내 유일한 쓰기 작업.'
|
||
|
||
# 6 코드 보기
|
||
ThreeShots 'F4F8FD' '코드 보기' ("함수 단위" + $BR + "빠른 탐색") @('Visual Studio 기본 색상 기준 C# · C++ 강조', '.h 선언 / .cpp 구현 탭 전환', '목차에서 함수 선택 후 이동', '폴더 구조 · 클래스 · 함수 옆 창 표시') @(@('code','코드 · 선언/구현 탭'), @('outline','목차'), @('side_pane','폴더 · 클래스 옆 창')) '파일 전체 열람보다 함수 탐색 위주 설계. 하단 도구 막대: 검색, 목차, 줄바꿈, 글자 크기, 복사, 탭 저장, 비교.'
|
||
|
||
# 7 펼친 화면 코드
|
||
WideShot 'FBFCFE' '펼친 화면 · 코드' ("펼친 화면:" + $BR + "구조 · 코드 동시 표시") @('Z Fold 펼침 · 가로 화면 대응', '좌측: 폴더 구조 · 클래스 · 함수 목록 / 우측: 코드', '옆 창 파일 선택 시 즉시 열림', '클래스 · 함수 선택 시 해당 줄 이동', '두 칸 위아래 순서 변경') 'code_wide' '펼친 화면에서 옆 창 상시 표시. 예시: C# 파일.'
|
||
|
||
# 8 파일 비교
|
||
WideShot 'F4F8FD' '파일 비교' ("저장소 간" + $BR + "동일 파일 비교") @('다른 저장소 · 브랜치 파일 간 좌우 비교', '예시: OC6 PRI 저장소(main) ↔ OC6 저장소(SubMain)', '차이 개수 표시, 이전 · 다음 차이 이동', '추가 · 삭제 · 변경 줄 색상 구분') 'compare_repos' '동일 장비 PRI 저장소 · 현장 저장소의 WeldingRecipeManager.cs 비교. 차이 5곳 중 첫 번째 위치. 사용 순서: 파일에서 비교 → 왼쪽 배치 → 다른 파일에서 비교 실행.'
|
||
|
||
# 9 커밋
|
||
ThreeShots 'FBFCFE' '커밋' ("커밋별 변경 내용" + $BR + "줄 단위 확인") @('브랜치별 커밋 목록', '커밋 선택: 변경 파일 · 추가/삭제 줄 수', '파일 선택: 변경 내용 표시') @(@('commits','커밋 목록'), @('commit_detail','바뀐 파일'), @('diff','변경 내용')) '이동 중 팀원 커밋 즉시 확인. 접은 화면: 상하 배치 / 펼친 화면: 좌우 배치.'
|
||
|
||
# 10 펼친 화면 변경 내용
|
||
WideShot 'F4F8FD' '펼친 화면 · 변경 내용' ("펼친 화면:" + $BR + "변경 전 · 후 좌우 비교") @('PC 비교 도구 방식: 좌측 변경 전 / 우측 변경 후', '삭제: 빨강 / 추가: 초록', '코드 화면과 동일한 문법 강조') 'diff_wide' '코드 리뷰 요청 시 PC 없이 변경 내용 확인 가능.'
|
||
|
||
# 11 작업 · 알림
|
||
ThreeShots 'FBFCFE' '작업 · 알림' ("이슈 · PR ·" + $BR + "알림 통합 조회") @('필터: 팀 전체 · 리뷰 요청 · 나에게 할당 · 내가 작성', '상태 배지: 열림 · 병합됨 · 닫힘', '이슈 · PR 본문 및 댓글 조회', 'Gitea 알림: 읽지 않음 · 전체') @(@('work','작업 탭'), @('issue_detail','이슈 상세'), @('notifications','알림')) '리뷰 요청 · 할당 업무 일괄 확인. 읽기 전용으로 댓글 작성 · 병합은 PC Gitea에서 처리.'
|
||
|
||
# 12 파일 보기
|
||
ThreeShots 'F4F8FD' '파일 보기' ("코드 외" + $BR + "파일 조회") @('이미지', 'CSV 표', 'PDF 문서', '별도 앱 없이 앱 내 열람') @(@('image','이미지'), @('csv','CSV 표'), @('pdf','PDF')) '장비 문서 · 설정 표 등 코드와 함께 관리하는 파일 모바일 확인.'
|
||
|
||
# 13 설정
|
||
$s = NewSlide 'FBFCFE' '신규 버전은 팀 Gitea 릴리스 기준. 업데이트 알림 시 설정에서 바로 설치.'
|
||
Eyebrow $s 128 320 '설정'
|
||
H2 $s 128 366 772 80 '설정 항목'
|
||
B $s 128 470 772 420 @('테마: 시스템 · 라이트 · 다크', '코드 자동 줄바꿈, 글자 크기 11–16sp', '즐겨찾기 저장소 Push 알림', '인증 실패 시 재시도', '팀 Gitea 기반 앱 업데이트') | Out-Null
|
||
I $s 'settings' 980 104 390 824 | Out-Null
|
||
T $s 980 940 390 40 '설정' 24 '3E5068' $false $KR 2 | Out-Null
|
||
I $s 'dark_home' 1402 104 390 824 | Out-Null
|
||
T $s 1402 940 390 40 '다크 테마' 24 '3E5068' $false $KR 2 | Out-Null
|
||
|
||
# 14 기술 · 보안
|
||
$s = NewSlide 'F4F8FD' '로그인은 브라우저에서 Gitea 직접 인증, 앱은 발급 토큰만 보관. 저장소 목록 기기 저장으로 오프라인 조회 가능.'
|
||
Eyebrow $s 128 128 '기술 · 보안'
|
||
H2 $s 128 176 1664 80 '회사 계정 인증 · 읽기 중심 설계'
|
||
$rows = @(@('로그인','팀 Gitea OAuth 2.0 + PKCE. 앱 내 비밀번호 입력 없음'), @('토큰 보관','Android Keystore 암호화, 기기 내 저장'), @('권한','읽기 전용. 쓰기는 즐겨찾기 별표만'), @('만든 기술','Kotlin · Jetpack Compose · Material 3'), @('지원 기기','Android 9 (API 28) 이상, 폴더블 · 가로 화면 대응'), @('배포','팀 Gitea 릴리스 배포, 앱 내 업데이트'))
|
||
$y = 310
|
||
foreach ($r in $rows) {
|
||
Box $s 128 $y 1664 2 'C9DCF2' | Out-Null
|
||
T $s 128 ($y+24) 320 50 $r[0] 30 '1565C0' $true | Out-Null
|
||
$f = $(if ($r[0] -eq '만든 기술') { $MONO } else { $KR })
|
||
T $s 488 ($y+28) 1304 50 $r[1] 26 '3E5068' $false $f | Out-Null
|
||
$y += 96
|
||
}
|
||
Box $s 128 $y 1664 2 'C9DCF2' | Out-Null
|
||
|
||
# 15 개발 방식
|
||
$s = NewSlide '10243F' '작업별 완료 기준 파일 선작성 → AI 에이전트 구현 → 별도 세션 리뷰. 전 과정 docs/development-log.md 기록.'
|
||
Eyebrow $s 128 128 '만든 방법' 'F0A04B'
|
||
H2 $s 128 176 1664 80 'AI 에이전트 활용 개발 · 7주' 56 'FFFFFF'
|
||
$stats = @(@('77','작업 (TASK-001 → 077)'), @('7주','2026-08-25 ~ 10-12'), @('104','개발 기록 항목'), @('0.2.6','현재 버전'))
|
||
$x = 128
|
||
foreach ($st in $stats) {
|
||
Box $s $x 320 392 190 '1A3558' $null $true | Out-Null
|
||
T $s ($x+32) 346 340 90 $st[0] 60 'F0A04B' $true $MONO | Out-Null
|
||
T $s ($x+32) 448 340 40 $st[1] 24 'C9DCF2' | Out-Null
|
||
$x += 424
|
||
}
|
||
$steps = @(@('1','작업 정의','완료 기준 선작성'), @('2','AI 구현','Codex · Claude'), @('3','별도 세션 리뷰','구현과 별도 에이전트'), @('4','빌드 · 테스트','실기기 확인'), @('5','배포','팀 Gitea 릴리스'))
|
||
$x = 128
|
||
foreach ($st in $steps) {
|
||
Box $s $x 600 320 4 'F0A04B' | Out-Null
|
||
T $s $x 626 320 34 $st[0] 22 'F0A04B' $false $MONO | Out-Null
|
||
T $s $x 670 320 40 $st[1] 26 'FFFFFF' $true | Out-Null
|
||
T $s $x 716 320 36 $st[2] 22 'C9DCF2' | Out-Null
|
||
$x += 336
|
||
}
|
||
|
||
# 16 현황
|
||
$s = NewSlide 'F4F8FD' '개발 완료, 현재 버전 0.2.6. 설치 파일 팀 Gitea 릴리스 제공, 이후 신규 버전 앱 내 알림.'
|
||
Eyebrow $s 128 370 '현황'
|
||
H2 $s 128 418 640 170 ("개발 완료 ·" + $BR + "사내 배포") 64
|
||
T $s 128 610 640 50 '현재 버전 v0.2.6' 28 '3E5068' | Out-Null
|
||
$items = @(@('설치','팀 Gitea 릴리스 설치 파일'), @('로그인','회사 Gitea 계정'), @('업데이트','신규 버전 앱 내 알림'))
|
||
$y = 330
|
||
foreach ($it in $items) {
|
||
Box $s 864 $y 928 116 'FFFFFF' 'C9DCF2' $true | Out-Null
|
||
T $s 896 ($y+36) 160 50 $it[0] 30 '1565C0' $true | Out-Null
|
||
T $s 1084 ($y+36) 680 50 $it[1] 30 '10243F' | Out-Null
|
||
$y += 140
|
||
}
|
||
|
||
$pres.SaveAs($out, 24)
|
||
$pres.SaveAs($pdf, 32)
|
||
$n = $pres.Slides.Count
|
||
$pres.Close(); $app.Quit()
|
||
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($app) | Out-Null
|
||
"slides=$n"
|
||
"pptx=$out"
|
||
"pdf=$pdf"
|