- Harness: kintex-mobile-dev agent + kintex-mobile-orchestrator skill (WISE mobile ref, Stitch-first design rule, dual app targets B2B/B2C) - design.md v2.1: full 84-screen inventory (web 51 / admin 10 / public 8 / mobile 15) with Stitch prompts incl. ticketing (SCR-P7/P8, M14/M15) - PLANNING v3.1: unified account + split signup tracks (2FA required for staff, light signup/guest for visitors), one codebase / two app targets - Deliverables: dev plan (21s), user/operator/developer guides (17/14/15s), program spec (44s, 65 programs, 8 flowcharts), DA (DB design 14s + table spec xlsx 35 tables/299 cols) - Benchmark: ticketing-app-benchmark.md (7 apps) -> IMPLEMENTATION_BACKLOG Phase F (14 items) - Stitch: 23 generated screens saved (mobile 10, admin 6, web core 5, ticket 2) - mobile/: Expo scaffold (SDK 51, expo-router, secure store JWT) - frontend: SCR-13~17 QA fixes, icons.tsx, kintexEvents, V10 seed migration - ci/: KINTEX CI logo assets Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
71 lines
2.8 KiB
Python
71 lines
2.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""공용 docx 헬퍼 — 한글 폰트/표/제목 스타일. 각 문서 생성기가 import."""
|
|
from docx.shared import Pt, Cm, RGBColor
|
|
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
|
from docx.enum.table import WD_TABLE_ALIGNMENT
|
|
from docx.oxml.ns import qn
|
|
from docx.oxml import OxmlElement
|
|
|
|
NAVY = RGBColor(0x1F, 0x29, 0x3A)
|
|
ACCENT = RGBColor(0x11, 0x6D, 0xC3)
|
|
GREY = RGBColor(0x55, 0x5F, 0x6B)
|
|
KFONT = "맑은 고딕"
|
|
|
|
def setup(doc):
|
|
st = doc.styles["Normal"]; st.font.name = KFONT; st.font.size = Pt(10.5)
|
|
_rf(st.element.get_or_add_rPr())
|
|
for m in ("Heading 1","Heading 2","Heading 3"):
|
|
doc.styles[m].font.name = KFONT; _rf(doc.styles[m].element.get_or_add_rPr())
|
|
s = doc.sections[0]
|
|
s.top_margin = Cm(2.2); s.bottom_margin = Cm(2.0); s.left_margin = Cm(2.3); s.right_margin = Cm(2.3)
|
|
|
|
def _rf(rpr):
|
|
rf = rpr.find(qn('w:rFonts'))
|
|
if rf is None:
|
|
rf = OxmlElement('w:rFonts'); rpr.append(rf)
|
|
for a in ('w:ascii','w:hAnsi','w:eastAsia','w:cs'):
|
|
rf.set(qn(a), KFONT)
|
|
|
|
def shade(cell, hexcolor):
|
|
tcpr = cell._tc.get_or_add_tcPr()
|
|
sh = OxmlElement('w:shd'); sh.set(qn('w:val'),'clear'); sh.set(qn('w:fill'),hexcolor)
|
|
tcpr.append(sh)
|
|
|
|
def h(doc, level, text):
|
|
p = doc.add_heading(text, level=level)
|
|
for run in p.runs:
|
|
run.font.color.rgb = NAVY if level == 1 else ACCENT
|
|
return p
|
|
|
|
def para(doc, text, size=10.5, bold=False, italic=False, color=None, align=None, space_after=6):
|
|
p = doc.add_paragraph(); r = p.add_run(text); r.bold = bold; r.italic = italic
|
|
r.font.size = Pt(size)
|
|
if color: r.font.color.rgb = color
|
|
if align: p.alignment = align
|
|
p.paragraph_format.space_after = Pt(space_after)
|
|
return p
|
|
|
|
def bullets(doc, items, style="List Bullet"):
|
|
for it in items:
|
|
p = doc.add_paragraph(it, style=style); p.paragraph_format.space_after = Pt(2)
|
|
|
|
def table(doc, headers, rows, widths=None, header_fill="1F293A", first_col_bold=False, fontsize=9.5):
|
|
t = doc.add_table(rows=1, cols=len(headers)); t.style = "Table Grid"; t.alignment = WD_TABLE_ALIGNMENT.CENTER
|
|
hd = t.rows[0].cells
|
|
for i, htext in enumerate(headers):
|
|
hd[i].text = ""; run = hd[i].paragraphs[0].add_run(htext)
|
|
run.bold = True; run.font.size = Pt(fontsize); run.font.color.rgb = RGBColor(0xFF,0xFF,0xFF)
|
|
shade(hd[i], header_fill)
|
|
for row in rows:
|
|
cells = t.add_row().cells
|
|
for i, val in enumerate(row):
|
|
cells[i].text = ""; run = cells[i].paragraphs[0].add_run(str(val)); run.font.size = Pt(fontsize)
|
|
if first_col_bold and i == 0: run.bold = True
|
|
cells[i].paragraphs[0].paragraph_format.space_after = Pt(1)
|
|
if widths:
|
|
for r_ in t.rows:
|
|
for i, w in enumerate(widths):
|
|
r_.cells[i].width = Cm(w)
|
|
doc.add_paragraph().paragraph_format.space_after = Pt(4)
|
|
return t
|