laporan-generator 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/.agents/skills/laporan-generator/SKILL.md +132 -0
  2. package/.agents/skills/laporan-generator/agents/openai.yaml +3 -0
  3. package/.agents/skills/laporan-generator/references/campus-guide.md +120 -0
  4. package/.agents/skills/laporan-generator/references/metadata-schema.md +76 -0
  5. package/.agents/skills/laporan-generator/references/preset-schema.md +79 -0
  6. package/.agents/skills/laporan-generator/references/syntax-cheatsheet.md +122 -0
  7. package/.agents/skills/laporan-generator/references/template-guide.md +100 -0
  8. package/.agents/skills/laporan-generator/references/troubleshooting.md +105 -0
  9. package/.agents/skills/laporan-generator/resources/Makefile +90 -0
  10. package/.agents/skills/laporan-generator/resources/apa.csl +2273 -0
  11. package/.agents/skills/laporan-generator/resources/build-docx.sh +122 -0
  12. package/.agents/skills/laporan-generator/resources/build.sh +102 -0
  13. package/.agents/skills/laporan-generator/resources/chapters/bab1-pendahuluan.md +60 -0
  14. package/.agents/skills/laporan-generator/resources/chapters/bab2-tinjauan-pustaka.md +83 -0
  15. package/.agents/skills/laporan-generator/resources/chapters/bab3-metodologi.md +116 -0
  16. package/.agents/skills/laporan-generator/resources/chapters/bab4-hasil-dan-pembahasan.md +93 -0
  17. package/.agents/skills/laporan-generator/resources/chapters/bab5-penutup.md +27 -0
  18. package/.agents/skills/laporan-generator/resources/cover.md +50 -0
  19. package/.agents/skills/laporan-generator/resources/docx.lua +416 -0
  20. package/.agents/skills/laporan-generator/resources/laporan +464 -0
  21. package/.agents/skills/laporan-generator/resources/laporan.ps1 +552 -0
  22. package/.agents/skills/laporan-generator/resources/logo.jpg +0 -0
  23. package/.agents/skills/laporan-generator/resources/metadata.yml +35 -0
  24. package/.agents/skills/laporan-generator/resources/presets/itb-ta.yml +27 -0
  25. package/.agents/skills/laporan-generator/resources/presets/its-skripsi.yml +26 -0
  26. package/.agents/skills/laporan-generator/resources/presets/skripsi-4433.yml +26 -0
  27. package/.agents/skills/laporan-generator/resources/presets/standard.yml +26 -0
  28. package/.agents/skills/laporan-generator/resources/presets/ugm-skripsi.yml +26 -0
  29. package/.agents/skills/laporan-generator/resources/presets/ui-skripsi.yml +26 -0
  30. package/.agents/skills/laporan-generator/resources/presets/unpad-skripsi.yml +27 -0
  31. package/.agents/skills/laporan-generator/resources/reference.docx +0 -0
  32. package/.agents/skills/laporan-generator/resources/references.bib +115 -0
  33. package/.agents/skills/laporan-generator/resources/scripts/bundle.py +75 -0
  34. package/.agents/skills/laporan-generator/resources/scripts/docx-pagenum.py +192 -0
  35. package/.agents/skills/laporan-generator/resources/scripts/finalize-docx.py +291 -0
  36. package/.agents/skills/laporan-generator/resources/scripts/make-reference-docx.py +670 -0
  37. package/.agents/skills/laporan-generator/resources/scripts/report-doctor.py +211 -0
  38. package/.agents/skills/laporan-generator/resources/scripts/report-stats.py +149 -0
  39. package/.agents/skills/laporan-generator/resources/scripts/scan-preset.py +311 -0
  40. package/.agents/skills/laporan-generator/resources/scripts/setup-deps.ps1 +152 -0
  41. package/.agents/skills/laporan-generator/resources/scripts/setup-deps.sh +113 -0
  42. package/.agents/skills/laporan-generator/resources/scripts/test_scripts.py +128 -0
  43. package/.agents/skills/laporan-generator/resources/scripts/validate-preset.py +166 -0
  44. package/.agents/skills/laporan-generator/resources/template.typ +394 -0
  45. package/.agents/skills/laporan-generator/scripts/build.sh +78 -0
  46. package/.agents/skills/laporan-generator/scripts/bundle.py +75 -0
  47. package/.agents/skills/laporan-generator/scripts/docx-pagenum.py +192 -0
  48. package/.agents/skills/laporan-generator/scripts/finalize-docx.py +152 -0
  49. package/.agents/skills/laporan-generator/scripts/laporan +447 -0
  50. package/.agents/skills/laporan-generator/scripts/laporan.ps1 +529 -0
  51. package/.agents/skills/laporan-generator/scripts/make-reference-docx.py +630 -0
  52. package/.agents/skills/laporan-generator/scripts/report-doctor.py +211 -0
  53. package/.agents/skills/laporan-generator/scripts/report-stats.py +149 -0
  54. package/.agents/skills/laporan-generator/scripts/scan-preset.py +311 -0
  55. package/.agents/skills/laporan-generator/scripts/setup-deps.ps1 +152 -0
  56. package/.agents/skills/laporan-generator/scripts/setup-deps.sh +113 -0
  57. package/.agents/skills/laporan-generator/scripts/test_scripts.py +103 -0
  58. package/.agents/skills/laporan-generator/scripts/validate-preset.py +166 -0
  59. package/.agents/workflows/laporan-generator.md +8 -0
  60. package/LICENSE +21 -0
  61. package/Makefile +90 -0
  62. package/README.md +389 -0
  63. package/apa.csl +2273 -0
  64. package/bin/laporan-generator.js +71 -0
  65. package/build-docx.sh +122 -0
  66. package/build.sh +102 -0
  67. package/chapters/bab1-pendahuluan.md +60 -0
  68. package/chapters/bab2-tinjauan-pustaka.md +83 -0
  69. package/chapters/bab3-metodologi.md +116 -0
  70. package/chapters/bab4-hasil-dan-pembahasan.md +93 -0
  71. package/chapters/bab5-penutup.md +27 -0
  72. package/cover.md +50 -0
  73. package/docs/campus-guide.md +120 -0
  74. package/docs/metadata-schema.md +76 -0
  75. package/docs/preset-schema.md +79 -0
  76. package/docs/syntax-cheatsheet.md +122 -0
  77. package/docs/template-guide.md +100 -0
  78. package/docs/troubleshooting.md +105 -0
  79. package/docx.lua +416 -0
  80. package/laporan +464 -0
  81. package/laporan.ps1 +552 -0
  82. package/lib/init.js +118 -0
  83. package/lib/setup.js +33 -0
  84. package/lib/sync-hosts.js +194 -0
  85. package/lib/uninstall.js +123 -0
  86. package/logo.jpg +0 -0
  87. package/metadata.yml +35 -0
  88. package/package.json +65 -0
  89. package/presets/itb-ta.yml +27 -0
  90. package/presets/its-skripsi.yml +26 -0
  91. package/presets/skripsi-4433.yml +26 -0
  92. package/presets/standard.yml +26 -0
  93. package/presets/ugm-skripsi.yml +26 -0
  94. package/presets/ui-skripsi.yml +26 -0
  95. package/presets/unpad-skripsi.yml +27 -0
  96. package/reference.docx +0 -0
  97. package/references.bib +115 -0
  98. package/scripts/bundle.py +75 -0
  99. package/scripts/docx-pagenum.py +192 -0
  100. package/scripts/finalize-docx.py +291 -0
  101. package/scripts/make-reference-docx.py +670 -0
  102. package/scripts/report-doctor.py +211 -0
  103. package/scripts/report-stats.py +149 -0
  104. package/scripts/scan-preset.py +311 -0
  105. package/scripts/setup-deps.ps1 +152 -0
  106. package/scripts/setup-deps.sh +113 -0
  107. package/scripts/test_scripts.py +128 -0
  108. package/scripts/validate-preset.py +166 -0
  109. package/template.typ +394 -0
@@ -0,0 +1,291 @@
1
+ #!/usr/bin/env python3
2
+ """Finalisasi document.xml hasil pandoc agar penomoran halaman DOCX
3
+ mengikuti PDF:
4
+
5
+ - Cover : tanpa nomor halaman (footerReference dihapus)
6
+ - KATA PENGANTAR + ISI : i, ii (lowerRoman, start 1)
7
+ - BAB I : 1, 2, 3 ... (decimal, start 1)
8
+ - BAB II dst + PUSTAKA : lanjut (tanpa restart)
9
+
10
+ Pandoc otomatis menyisipkan paragraf `w:sectPr` (berisi footerReference
11
+ dari reference.docx) sebelum setiap Heading1. Script ini mengklasifikasikan
12
+ setiap sectPr berdasarkan heading berikutnya dan menyesuaikan isinya.
13
+
14
+ Usage:
15
+ python3 scripts/finalize-docx.py input.docx output.docx
16
+ """
17
+
18
+ import re
19
+ import sys
20
+ import zipfile
21
+
22
+ if sys.platform == "win32":
23
+ try:
24
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace")
25
+ sys.stderr.reconfigure(encoding="utf-8", errors="replace")
26
+ except Exception:
27
+ pass
28
+
29
+ SECT_IN_PPR = re.compile(r"<w:pPr><w:sectPr>(.*?)</w:sectPr></w:pPr>", re.S)
30
+ FOOTER_REF = re.compile(r"<w:footerReference[^/]*/>")
31
+
32
+
33
+ def next_heading_text(doc, start):
34
+ nxt = doc[start:]
35
+ m = re.search(r'<w:pStyle w:val="Heading1"[^>]*/>', nxt)
36
+ if not m:
37
+ return ""
38
+ hdr = nxt[m.end():]
39
+ t = re.search(r"<w:t[^>]*>([^<]*)", hdr)
40
+ return t.group(1) if t else ""
41
+
42
+
43
+ def is_front_matter_heading(t):
44
+ norm = t.strip().upper()
45
+ return norm in (
46
+ "KATA PENGANTAR",
47
+ "PRAKATA",
48
+ "ABSTRAK",
49
+ "ABSTRACT",
50
+ "DAFTAR ISI",
51
+ "DAFTAR TABEL",
52
+ "DAFTAR GAMBAR",
53
+ "DAFTAR LAMPIRAN",
54
+ )
55
+
56
+
57
+ def is_chapter_1(t):
58
+ norm = t.strip().upper()
59
+ if re.match(r"^BAB\s+(?:I|1|01)\b", norm):
60
+ return True
61
+ if re.match(r"^(?:1\.|1\s+|I\.)", norm):
62
+ return True
63
+ if norm.startswith("PENDAHULUAN"):
64
+ return True
65
+ return False
66
+
67
+
68
+ def is_chapter_2(t):
69
+ norm = t.strip().upper()
70
+ if re.match(r"^BAB\s+(?:II|2|02)\b", norm):
71
+ return True
72
+ if re.match(r"^(?:2\.|2\s+|II\.)", norm):
73
+ return True
74
+ if any(
75
+ norm.startswith(k)
76
+ for k in (
77
+ "PEMBAHASAN",
78
+ "TINJAUAN PUSTAKA",
79
+ "LANDASAN TEORI",
80
+ "METODOLOGI",
81
+ "METODE PENELITIAN",
82
+ )
83
+ ):
84
+ return True
85
+ return False
86
+
87
+
88
+ def classify(text):
89
+ t = text.strip()
90
+ if is_front_matter_heading(t):
91
+ return "cover"
92
+ if is_chapter_1(t):
93
+ return "roman"
94
+ if is_chapter_2(t):
95
+ return "decimal-start"
96
+ return "body"
97
+
98
+
99
+ def add_pgnum(sect, fmt, start):
100
+ if "<w:pgNumType" in sect:
101
+ return sect
102
+ tag = f'<w:pgNumType w:fmt="{fmt}" w:start="{start}"/>'
103
+ return sect + tag
104
+
105
+
106
+ def process(doc):
107
+ matches = list(SECT_IN_PPR.finditer(doc))
108
+ if not matches:
109
+ return doc, 0
110
+ out = []
111
+ last = 0
112
+ changed = 0
113
+ for m in matches:
114
+ out.append(doc[last:m.start()])
115
+ sect = m.group(1)
116
+ kind = classify(next_heading_text(doc, m.end()))
117
+ if kind == "cover":
118
+ sect = FOOTER_REF.sub("", sect)
119
+ changed += 1
120
+ elif kind == "roman":
121
+ sect = add_pgnum(sect, "lowerRoman", "1")
122
+ changed += 1
123
+ elif kind == "decimal-start":
124
+ sect = add_pgnum(sect, "decimal", "1")
125
+ changed += 1
126
+ out.append(f"<w:pPr><w:sectPr>{sect}</w:sectPr></w:pPr>")
127
+ last = m.end()
128
+ out.append(doc[last:])
129
+ return "".join(out), changed
130
+
131
+
132
+ def get_tab_pos(doc):
133
+ sz = re.search(r'<w:pgSz\b[^>]*w:w="(\d+)"', doc)
134
+ mar_left = re.search(r'<w:pgMar\b[^>]*w:left="(\d+)"', doc)
135
+ mar_right = re.search(r'<w:pgMar\b[^>]*w:right="(\d+)"', doc)
136
+ if sz and mar_left and mar_right:
137
+ try:
138
+ w = int(sz.group(1))
139
+ left = int(mar_left.group(1))
140
+ right = int(mar_right.group(1))
141
+ pos = w - left - right
142
+ if pos > 1000:
143
+ return pos
144
+ except (ValueError, TypeError):
145
+ pass
146
+ return 9072
147
+
148
+
149
+ def fix_daftar_isi_title(doc):
150
+ pattern = re.compile(
151
+ r'(<w:p\b[^>]*><w:pPr>(?:(?!</w:p>).)*?<w:pStyle\s+w:val=["\x27])Heading1(["\x27](?:(?!</w:p>).)*?<w:t[^>]*>\s*DAFTAR ISI\s*</w:t>(?:(?!</w:p>).)*?</w:p>)',
152
+ re.S,
153
+ )
154
+ return pattern.sub(r"\g<1>TOCHeading\g<2>", doc)
155
+
156
+
157
+ def fix_toc_styles(styles, tab_pos):
158
+ tnr = '<w:rFonts w:ascii="Times New Roman" w:hAnsi="Times New Roman" w:cs="Times New Roman"/>'
159
+ defs = {
160
+ "TOCHeading": f"""<w:style w:type="paragraph" w:styleId="TOCHeading">
161
+ <w:name w:val="TOC Heading"/>
162
+ <w:basedOn w:val="Normal"/>
163
+ <w:next w:val="Normal"/>
164
+ <w:uiPriority w:val="39"/>
165
+ <w:qFormat/>
166
+ <w:pPr>
167
+ <w:spacing w:before="360" w:after="360" w:line="240" w:lineRule="auto"/>
168
+ <w:jc w:val="center"/>
169
+ <w:outlineLvl w:val="9"/>
170
+ </w:pPr>
171
+ <w:rPr>
172
+ {tnr}
173
+ <w:color w:val="000000"/>
174
+ <w:sz w:val="28"/>
175
+ <w:szCs w:val="28"/>
176
+ <w:b/>
177
+ <w:bCs/>
178
+ </w:rPr>
179
+ </w:style>""",
180
+ "TOC1": f"""<w:style w:type="paragraph" w:styleId="TOC1">
181
+ <w:name w:val="toc 1"/>
182
+ <w:basedOn w:val="Normal"/>
183
+ <w:next w:val="Normal"/>
184
+ <w:qFormat/>
185
+ <w:pPr>
186
+ <w:tabs>
187
+ <w:tab w:val="right" w:leader="dot" w:pos="{tab_pos}"/>
188
+ </w:tabs>
189
+ <w:spacing w:before="120" w:after="40" w:line="260" w:lineRule="auto"/>
190
+ <w:ind w:left="0" w:right="0"/>
191
+ <w:jc w:val="left"/>
192
+ </w:pPr>
193
+ <w:rPr>
194
+ {tnr}
195
+ <w:color w:val="000000"/>
196
+ <w:sz w:val="24"/>
197
+ <w:szCs w:val="24"/>
198
+ <w:b/>
199
+ <w:bCs/>
200
+ </w:rPr>
201
+ </w:style>""",
202
+ "TOC2": f"""<w:style w:type="paragraph" w:styleId="TOC2">
203
+ <w:name w:val="toc 2"/>
204
+ <w:basedOn w:val="TOC1"/>
205
+ <w:next w:val="Normal"/>
206
+ <w:qFormat/>
207
+ <w:pPr>
208
+ <w:tabs>
209
+ <w:tab w:val="right" w:leader="dot" w:pos="{tab_pos}"/>
210
+ </w:tabs>
211
+ <w:spacing w:before="20" w:after="20" w:line="240" w:lineRule="auto"/>
212
+ <w:ind w:left="360" w:right="0"/>
213
+ <w:jc w:val="left"/>
214
+ </w:pPr>
215
+ <w:rPr>
216
+ {tnr}
217
+ <w:color w:val="000000"/>
218
+ <w:sz w:val="24"/>
219
+ <w:szCs w:val="24"/>
220
+ <w:b w:val="0"/>
221
+ <w:bCs w:val="0"/>
222
+ </w:rPr>
223
+ </w:style>""",
224
+ "TOC3": f"""<w:style w:type="paragraph" w:styleId="TOC3">
225
+ <w:name w:val="toc 3"/>
226
+ <w:basedOn w:val="TOC2"/>
227
+ <w:next w:val="Normal"/>
228
+ <w:qFormat/>
229
+ <w:pPr>
230
+ <w:tabs>
231
+ <w:tab w:val="right" w:leader="dot" w:pos="{tab_pos}"/>
232
+ </w:tabs>
233
+ <w:spacing w:before="0" w:after="0" w:line="240" w:lineRule="auto"/>
234
+ <w:ind w:left="720" w:right="0"/>
235
+ <w:jc w:val="left"/>
236
+ </w:pPr>
237
+ <w:rPr>
238
+ {tnr}
239
+ <w:color w:val="000000"/>
240
+ <w:sz w:val="24"/>
241
+ <w:szCs w:val="24"/>
242
+ <w:b w:val="0"/>
243
+ <w:bCs w:val="0"/>
244
+ </w:rPr>
245
+ </w:style>""",
246
+ }
247
+ missing = []
248
+ for sid, block in defs.items():
249
+ pattern = re.compile(rf'<w:style\b[^>]*w:styleId="{sid}"[^>]*>.*?</w:style>', re.S)
250
+ if pattern.search(styles):
251
+ styles = pattern.sub(block, styles)
252
+ else:
253
+ missing.append(block)
254
+ if missing:
255
+ styles = styles.replace("</w:styles>", "".join(missing) + "</w:styles>")
256
+ return styles
257
+
258
+
259
+ def main():
260
+ src = sys.argv[1]
261
+ dst = sys.argv[2]
262
+
263
+ with zipfile.ZipFile(src, "r") as zin:
264
+ names = zin.namelist()
265
+ items = {n: zin.read(n) for n in names}
266
+
267
+ doc = items["word/document.xml"].decode("utf-8")
268
+ tab_pos = get_tab_pos(doc)
269
+ doc = fix_daftar_isi_title(doc)
270
+ # Sesuaikan tab leader pada cached TOC entries agar tepat di margin kanan
271
+ doc = re.sub(
272
+ r'(<w:pStyle w:val="TOC[123]"/>.*?<w:tab w:val="right" w:leader="dot" w:pos=)"\d+"',
273
+ rf'\g<1>"{tab_pos}"',
274
+ doc,
275
+ )
276
+ new_doc, changed = process(doc)
277
+ items["word/document.xml"] = new_doc.encode("utf-8")
278
+
279
+ if "word/styles.xml" in items:
280
+ styles = items["word/styles.xml"].decode("utf-8")
281
+ items["word/styles.xml"] = fix_toc_styles(styles, tab_pos).encode("utf-8")
282
+
283
+ with zipfile.ZipFile(dst, "w", zipfile.ZIP_DEFLATED) as zout:
284
+ for n in items:
285
+ zout.writestr(n, items[n])
286
+
287
+ print(f"[OK] finalize-docx: {changed} sectPr disesuaikan (tab={tab_pos}) -> {dst}")
288
+
289
+
290
+ if __name__ == "__main__":
291
+ main()