laporan-generator 2.7.0 → 2.9.0
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.
- package/.agents/skills/laporan-generator/SKILL.md +30 -15
- package/.agents/skills/laporan-generator/references/campus-guide.md +8 -2
- package/.agents/skills/laporan-generator/references/metadata-schema.md +39 -11
- package/.agents/skills/laporan-generator/references/template-guide.md +20 -14
- package/.agents/skills/laporan-generator/resources/cover.md +18 -14
- package/.agents/skills/laporan-generator/resources/docx.lua +208 -5
- package/.agents/skills/laporan-generator/resources/laporan +2 -2
- package/.agents/skills/laporan-generator/resources/laporan.ps1 +55 -25
- package/.agents/skills/laporan-generator/resources/metadata.yml +13 -16
- package/.agents/skills/laporan-generator/resources/presets/ipb-ppki.yml +27 -0
- package/.agents/skills/laporan-generator/resources/presets/its-skripsi.yml +6 -6
- package/.agents/skills/laporan-generator/resources/presets/telkom-ta.yml +26 -0
- package/.agents/skills/laporan-generator/resources/presets/ub-skripsi.yml +26 -0
- package/.agents/skills/laporan-generator/resources/presets/ui-skripsi.yml +6 -6
- package/.agents/skills/laporan-generator/resources/presets/unair-skripsi.yml +26 -0
- package/.agents/skills/laporan-generator/resources/presets/undip-skripsi.yml +26 -0
- package/.agents/skills/laporan-generator/resources/scripts/bundle.py +7 -1
- package/.agents/skills/laporan-generator/resources/scripts/docx-pagenum.py +6 -2
- package/.agents/skills/laporan-generator/resources/scripts/finalize-docx.py +13 -0
- package/.agents/skills/laporan-generator/resources/scripts/setup-deps.sh +9 -3
- package/.agents/skills/laporan-generator/resources/scripts/test_scripts.py +130 -0
- package/.agents/skills/laporan-generator/resources/template.typ +3 -0
- package/.agents/skills/laporan-generator/scripts/build-docx.sh +122 -0
- package/.agents/skills/laporan-generator/scripts/build.sh +32 -8
- package/.agents/skills/laporan-generator/scripts/bundle.py +7 -1
- package/.agents/skills/laporan-generator/scripts/docx-pagenum.py +6 -2
- package/.agents/skills/laporan-generator/scripts/finalize-docx.py +13 -0
- package/.agents/skills/laporan-generator/scripts/laporan +55 -5
- package/.agents/skills/laporan-generator/scripts/laporan.ps1 +628 -529
- package/.agents/skills/laporan-generator/scripts/setup-deps.sh +9 -3
- package/.agents/skills/laporan-generator/scripts/test_scripts.py +130 -0
- package/README.md +29 -19
- package/bin/laporan-generator.js +34 -6
- package/cover.md +18 -14
- package/docs/campus-guide.md +8 -2
- package/docs/metadata-schema.md +39 -11
- package/docs/template-guide.md +20 -14
- package/docx.lua +208 -5
- package/laporan +2 -2
- package/laporan.ps1 +55 -25
- package/lib/init.js +28 -3
- package/lib/mcp.js +21 -9
- package/lib/sync-hosts.js +26 -2
- package/metadata.yml +13 -16
- package/package.json +3 -2
- package/presets/ipb-ppki.yml +27 -0
- package/presets/its-skripsi.yml +6 -6
- package/presets/telkom-ta.yml +26 -0
- package/presets/ub-skripsi.yml +26 -0
- package/presets/ui-skripsi.yml +6 -6
- package/presets/unair-skripsi.yml +26 -0
- package/presets/undip-skripsi.yml +26 -0
- package/scripts/bundle.py +7 -1
- package/scripts/docx-pagenum.py +6 -2
- package/scripts/finalize-docx.py +13 -0
- package/scripts/setup-deps.sh +9 -3
- package/scripts/test_scripts.py +130 -0
- package/template.typ +3 -0
|
@@ -31,12 +31,12 @@ When available in your environment, **always prefer calling the native MCP tools
|
|
|
31
31
|
|
|
32
32
|
| Action | Native MCP Tool | CLI Fallback |
|
|
33
33
|
| :--- | :--- | :--- |
|
|
34
|
-
| **Initialize Project** | `laporan_init({ preset, title, author_name, institution })` | `npx laporan-generator init` |
|
|
34
|
+
| **Initialize Project** | `laporan_init({ preset, title, author_name, author_id, institution, daftar_gambar, daftar_tabel })` | `npx laporan-generator init` |
|
|
35
35
|
| **Audit & Health Check** | `laporan_doctor({ target_dir })` | `./laporan doctor` or `npx laporan-generator doctor --json` |
|
|
36
36
|
| **Document Stats & Metrics** | `laporan_stats({ target_dir })` | `./laporan stats` or `npx laporan-generator stats --json` |
|
|
37
37
|
| **Inspect Campus Presets** | `laporan_presets({ action: "list" \| "get", preset_name })` | `./laporan preset list` |
|
|
38
|
-
| **Search & Add Citations** | `laporan_citations({ action: "search_doi" \| "add_citation", query, doi })` | Manual edit `references.bib` |
|
|
39
|
-
| **Compile PDF & DOCX** | `laporan_build({ format: "all" \| "pdf" \| "docx" })` | `./laporan build` or `
|
|
38
|
+
| **Search & Add Citations** | `laporan_citations({ action: "search_doi" \| "get_bibtex" \| "add_citation" \| "validate", query, doi, bibtex })` | Manual edit `references.bib` |
|
|
39
|
+
| **Compile PDF & DOCX** | `laporan_build({ target_dir, format: "all" \| "pdf" \| "docx" })` | `./laporan build` or `npx laporan-generator build` |
|
|
40
40
|
|
|
41
41
|
--------------------------------------------------------------------------------
|
|
42
42
|
|
|
@@ -51,22 +51,37 @@ Identify the document type and target university:
|
|
|
51
51
|
3. **Makalah Kuliah / Paper Ilmiah**: Focus on research problem, methodology, findings, and discussion. (In Indonesia, course papers use standard Roman Bab headings: `BAB I`, `BAB II`, etc.).
|
|
52
52
|
4. **Target University Preset**:
|
|
53
53
|
- `skripsi-4433`: National Standard (Top 4cm, Left 4cm, Bottom 3cm, Right 3cm).
|
|
54
|
-
- `ui-skripsi`: Universitas Indonesia (
|
|
55
|
-
- `itb-ta`: Institut Teknologi Bandung (Pedoman TA ITB).
|
|
56
|
-
- `ugm-skripsi`: Universitas Gadjah Mada (Format Skripsi UGM).
|
|
57
|
-
- `its-skripsi`: Institut Teknologi Sepuluh Nopember.
|
|
58
|
-
- `unpad-skripsi`: Universitas Padjadjaran.
|
|
59
|
-
- `
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
-
|
|
64
|
-
2.
|
|
54
|
+
- `ui-skripsi`: Universitas Indonesia (Left 4cm, Top/Right/Bottom 3cm).
|
|
55
|
+
- `itb-ta`: Institut Teknologi Bandung (Pedoman TA ITB 4-4-3-3, Minimalist Cover).
|
|
56
|
+
- `ugm-skripsi`: Universitas Gadjah Mada (Format Skripsi UGM 4-4-3-3).
|
|
57
|
+
- `its-skripsi`: Institut Teknologi Sepuluh Nopember (Top 3cm, Left 3cm, Bottom 2.5cm, Right 2cm).
|
|
58
|
+
- `unpad-skripsi`: Universitas Padjadjaran (4-4-3-3).
|
|
59
|
+
- `ub-skripsi`: Universitas Brawijaya (4-4-3-3).
|
|
60
|
+
- `undip-skripsi`: Universitas Diponegoro (4-4-3-3).
|
|
61
|
+
- `unair-skripsi`: Universitas Airlangga (4-4-3-3).
|
|
62
|
+
- `ipb-ppki`: IPB University - Pedoman PPKI (Left 4cm, Top/Right/Bottom 3cm, 1cm indent).
|
|
63
|
+
- `telkom-ta`: Telkom University (Left 4cm, Top/Right/Bottom 3cm).
|
|
64
|
+
- `standard`: General University (Top 2cm, Bottom 3cm, Left 2.5cm, Right 2.5cm).
|
|
65
|
+
|
|
66
|
+
### Stage 2: Document Structure Confirmation & Scaffolding
|
|
67
|
+
1. **Confirm Document Structure**:
|
|
68
|
+
- The universal default structure is minimal: **Cover -> Kata Pengantar -> Daftar Isi -> Batang Tubuh (BAB I..n) -> Daftar Pustaka**.
|
|
69
|
+
- **MANDATORY INQUIRY**: Ask the user before scaffolding if they want to activate optional components:
|
|
70
|
+
- Abstrak / Abstract (`abstract_id` / `abstract_en`)
|
|
71
|
+
- Lembar Pengesahan (`approval`)
|
|
72
|
+
- Daftar Gambar (`daftar_gambar: true`)
|
|
73
|
+
- Daftar Tabel (`daftar_tabel: true`)
|
|
74
|
+
- Lampiran
|
|
75
|
+
2. **Initialize the workspace**:
|
|
76
|
+
- Call MCP tool `laporan_init` (e.g. `laporan_init({ preset, title, daftar_gambar, daftar_tabel })`) or run `npx laporan-generator init`.
|
|
77
|
+
3. **Configure `metadata.yml`**:
|
|
65
78
|
- `title`: Clear, concise, capitalized (rendered as inverted pyramid).
|
|
66
79
|
- `author`: Author array with `name` and `nim` (Student ID).
|
|
67
80
|
- `preset`: Selected university preset (e.g. `skripsi-4433`, `itb-ta`).
|
|
81
|
+
- `daftar_gambar: false`, `daftar_tabel: false` (set to `true` only if opted-in).
|
|
82
|
+
- `abstract_id`: Kept empty or commented out unless user requested Abstrak.
|
|
68
83
|
- `institution`, `faculty`, `department`, `year`.
|
|
69
|
-
|
|
84
|
+
4. **Configure `cover.md`**: Set formal title, institutional affiliations, and kata pengantar.
|
|
70
85
|
|
|
71
86
|
### Stage 3: Rigorous Academic Writing Standard
|
|
72
87
|
When authoring content into `chapters/bab*.md`, strictly enforce **Indonesian Standard Academic Tone**:
|
|
@@ -29,10 +29,16 @@ Atau gunakan bantuan CLI interaktif:
|
|
|
29
29
|
|---|---|---|---|
|
|
30
30
|
| `standard` | Standar Modern | 2.0 cm - 3.0 cm - 2.5 cm - 2.5 cm | Format default tugas akhir, magang, praktikum |
|
|
31
31
|
| `skripsi-4433` | Skripsi Tradisional | 4.0 cm - 3.0 cm - 4.0 cm - 3.0 cm | Hardcover jilid tebal margin kiri 4cm |
|
|
32
|
-
| `ui-skripsi` | Universitas Indonesia (UI) |
|
|
32
|
+
| `ui-skripsi` | Universitas Indonesia (UI) | 3.0 cm - 3.0 cm - 4.0 cm - 3.0 cm | Sesuai Pedoman Teknis Tugas Akhir UI |
|
|
33
33
|
| `itb-ta` | Institut Teknologi Bandung (ITB) | 4.0 cm - 3.0 cm - 4.0 cm - 3.0 cm | Cover minimalis (tanpa dosen pembimbing di cover luar) |
|
|
34
34
|
| `ugm-skripsi` | Universitas Gadjah Mada (UGM) | 4.0 cm - 3.0 cm - 4.0 cm - 3.0 cm | Full academic cover |
|
|
35
|
-
| `its-skripsi` | Institut Teknologi Sepuluh Nopember (ITS) |
|
|
35
|
+
| `its-skripsi` | Institut Teknologi Sepuluh Nopember (ITS) | 3.0 cm - 2.5 cm - 3.0 cm - 2.0 cm | Sesuai Buku Pedoman TA & Tesis ITS |
|
|
36
|
+
| `unpad-skripsi` | Universitas Padjadjaran (UNPAD) | 4.0 cm - 3.0 cm - 4.0 cm - 3.0 cm | Format skripsi & karya ilmiah UNPAD |
|
|
37
|
+
| `ub-skripsi` | Universitas Brawijaya (UB) | 4.0 cm - 3.0 cm - 4.0 cm - 3.0 cm | Pedoman penulisan skripsi UB |
|
|
38
|
+
| `undip-skripsi` | Universitas Diponegoro (UNDIP) | 4.0 cm - 3.0 cm - 4.0 cm - 3.0 cm | Pedoman akademik skripsi UNDIP |
|
|
39
|
+
| `unair-skripsi` | Universitas Airlangga (UNAIR) | 4.0 cm - 3.0 cm - 4.0 cm - 3.0 cm | Pedoman penulisan karya ilmiah UNAIR |
|
|
40
|
+
| `ipb-ppki` | IPB University (PPKI) | 3.0 cm - 3.0 cm - 4.0 cm - 3.0 cm | Pedoman PPKI IPB (Indent 1cm, sub-bab tanpa titik akhir) |
|
|
41
|
+
| `telkom-ta` | Telkom University (Tel-U) | 3.0 cm - 3.0 cm - 4.0 cm - 3.0 cm | Pedoman Buku Tugas Akhir Telkom University |
|
|
36
42
|
|
|
37
43
|
---
|
|
38
44
|
|
|
@@ -20,14 +20,33 @@ lecturer_label: "Dosen Pengampu:" # Opsional: "Dosen Pembimbing:", "Pembimbing
|
|
|
20
20
|
course: "Pengolahan Citra Digital dan Pembelajaran Mesin"
|
|
21
21
|
institution: "Universitas Gadjah Mada"
|
|
22
22
|
faculty: "Departemen Teknik Elektro dan Teknologi Informasi"
|
|
23
|
+
department: "Teknologi Informasi"
|
|
24
|
+
city: "Yogyakarta"
|
|
23
25
|
year: "2025/2026"
|
|
24
26
|
date: "Juli 2026"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
preset: "ugm-skripsi"
|
|
28
|
+
daftar_gambar: false
|
|
29
|
+
daftar_tabel: false
|
|
30
|
+
# Komponen opsional (aktifkan sesuai kebutuhan):
|
|
31
|
+
# approval:
|
|
32
|
+
# city: "Yogyakarta"
|
|
33
|
+
# date: "20 Juli 2026"
|
|
34
|
+
# supervisors:
|
|
35
|
+
# - name: "Dr. Ir. Hendra Wijaya, M.T."
|
|
36
|
+
# nip: "197501012000031001"
|
|
37
|
+
# role: "Pembimbing Utama"
|
|
38
|
+
# dean:
|
|
39
|
+
# name: "Prof. Dr. Eng. Dekan Fakultas, M.T."
|
|
40
|
+
# nip: "196803031992031003"
|
|
41
|
+
# role: "Dekan Fakultas Teknik"
|
|
42
|
+
# abstract_id: |
|
|
43
|
+
# Laporan ini membahas pipeline otomatisasi dokumen akademik
|
|
44
|
+
# menggunakan Markdown, Pandoc, Typst, dan Bash.
|
|
45
|
+
# keywords_id: ["Pandoc", "Typst", "OpenXML", "Otomatisasi Dokumen"]
|
|
46
|
+
# abstract_en: |
|
|
47
|
+
# This report discusses an automated academic document pipeline
|
|
48
|
+
# built with Markdown, Pandoc, Typst, and Bash.
|
|
49
|
+
# keywords_en: ["Pandoc", "Typst", "OpenXML", "Document Automation"]
|
|
31
50
|
...
|
|
32
51
|
```
|
|
33
52
|
|
|
@@ -37,19 +56,28 @@ abstract: |
|
|
|
37
56
|
|
|
38
57
|
| Field | Tipe Data | Wajib? | Keterangan |
|
|
39
58
|
|---|---|---|---|
|
|
40
|
-
| `title` | String | **Ya** | Judul utama laporan (tampil tebal di halaman sampul). |
|
|
59
|
+
| `title` | String | **Ya** | Judul utama laporan (tampil tebal piramida terbalik di halaman sampul). |
|
|
41
60
|
| `subtitle` | String | Opsional | Sub-judul atau keterangan tambahan laporan. |
|
|
42
61
|
| `author` | List of Objects | **Ya** | Daftar penulis. Setiap entri memiliki `name` dan `nim`. |
|
|
43
62
|
| `author[].name` | String | **Ya** | Nama lengkap mahasiswa / penyusun. |
|
|
44
63
|
| `author[].nim` | String | **Ya** | Nomor Induk Mahasiswa (NIM / NPM / NISN). |
|
|
45
64
|
| `lecturer` | String | **Ya** | Nama lengkap dan gelar Dosen / Guru Pengampu / Pembimbing. |
|
|
46
65
|
| `lecturer_label` | String | Opsional | Label peran dosen (default: `"Dosen Pengampu:"`, bisa `"Dosen Pembimbing:"`). |
|
|
47
|
-
| `course` | String | **Ya** | Nama Mata Kuliah atau
|
|
66
|
+
| `course` | String | **Ya** | Nama Mata Kuliah atau Jenis Karya Tulis. |
|
|
48
67
|
| `institution` | String | **Ya** | Nama Universitas, Institut, Politeknik, atau Sekolah. |
|
|
49
|
-
| `faculty` | String | **Ya** | Nama Fakultas,
|
|
50
|
-
| `
|
|
68
|
+
| `faculty` | String | **Ya** | Nama Fakultas, Sekolah, atau Departemen. |
|
|
69
|
+
| `department` | String | Opsional | Nama Program Studi atau Jurusan. |
|
|
70
|
+
| `city` | String | Opsional | Kota tempat institusi berada (digunakan pada lembar pengesahan). |
|
|
71
|
+
| `year` | String | **Ya** | Tahun Ajaran atau Tahun Kelulusan (contoh: `2026` atau `2025/2026`). |
|
|
51
72
|
| `date` | String | Opsional | Bulan dan Tahun pembuatan (contoh: `Juli 2026`). |
|
|
52
|
-
| `
|
|
73
|
+
| `preset` | String | Opsional | ID preset format kampus (`standard`, `skripsi-4433`, `ui-skripsi`, `itb-ta`, `ugm-skripsi`, `its-skripsi`, `unpad-skripsi`, `ub-skripsi`, `undip-skripsi`, `unair-skripsi`, `ipb-ppki`, `telkom-ta`). |
|
|
74
|
+
| `daftar_gambar` | Boolean | Opsional | Aktifkan halaman DAFTAR GAMBAR (`true`/`false`, default: `false`). |
|
|
75
|
+
| `daftar_tabel` | Boolean | Opsional | Aktifkan halaman DAFTAR TABEL (`true`/`false`, default: `false`). |
|
|
76
|
+
| `approval` | Object | Opsional | Konfigurasi Lembar Pengesahan (`city`, `date`, `supervisors[]`, `dean`). |
|
|
77
|
+
| `abstract_id` | Multiline String | Opsional | Ringkasan/Abstrak laporan dalam Bahasa Indonesia (Opt-In). |
|
|
78
|
+
| `keywords_id` | List of Strings | Opsional | Daftar kata kunci Bahasa Indonesia. |
|
|
79
|
+
| `abstract_en` | Multiline String | Opsional | Ringkasan/Abstract laporan dalam Bahasa Inggris (Opt-In). |
|
|
80
|
+
| `keywords_en` | List of Strings | Opsional | Daftar kata kunci Bahasa Inggris (Keywords). |
|
|
53
81
|
|
|
54
82
|
---
|
|
55
83
|
|
|
@@ -17,16 +17,15 @@ File `template.typ` merupakan jantung dari pemformatan visual PDF pada **Laporan
|
|
|
17
17
|
```typst
|
|
18
18
|
#set page(
|
|
19
19
|
paper: "a4",
|
|
20
|
-
margin:
|
|
20
|
+
margin: doc-margin,
|
|
21
|
+
numbering: none,
|
|
21
22
|
)
|
|
22
23
|
#set text(font: "Libertinus Serif", size: 12pt)
|
|
23
|
-
#set par(justify: true, leading: 1.
|
|
24
|
+
#set par(justify: true, leading: 0.75em, first-line-indent: 1.25cm)
|
|
24
25
|
```
|
|
25
|
-
* Margin Kiri
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* Spasi Antar Baris: 1.5 spasi (*one half spacing*)
|
|
29
|
-
* Paragraf rata kanan-kiri (*justify*)
|
|
26
|
+
* Margin Default (`standard`): Atas 2.0 cm, Bawah 3.0 cm, Kiri 2.5 cm, Kanan 2.5 cm (atau mengikuti `presets/*.yml` aktif).
|
|
27
|
+
* Spasi Antar Baris: `0.75em` (setara 1.5 spasi di Typst).
|
|
28
|
+
* Paragraf rata kanan-kiri (*justify*) dengan indentasi baris pertama `1.25cm`.
|
|
30
29
|
|
|
31
30
|
---
|
|
32
31
|
|
|
@@ -36,28 +35,35 @@ File `template.typ` merupakan jantung dari pemformatan visual PDF pada **Laporan
|
|
|
36
35
|
```typst
|
|
37
36
|
#set heading(numbering: (..ns) => {
|
|
38
37
|
if ns.len() == 1 {
|
|
39
|
-
|
|
38
|
+
if chapter-prefix != "" {
|
|
39
|
+
chapter-prefix + format-chapter-num(ns.at(0))
|
|
40
|
+
} else {
|
|
41
|
+
format-chapter-num(ns.at(0))
|
|
42
|
+
}
|
|
40
43
|
} else if ns.len() == 2 {
|
|
41
|
-
numbering("1.1", ..ns)
|
|
44
|
+
numbering("1.1.", ..ns)
|
|
42
45
|
} else {
|
|
43
46
|
numbering("1.1.1", ..ns)
|
|
44
47
|
}
|
|
45
48
|
})
|
|
46
49
|
```
|
|
47
|
-
* **Bab**: Diformat dengan Angka Romawi (`BAB I`, `BAB II`, `BAB III`, dst)
|
|
48
|
-
* **Sub-bab**: Diformat dengan Angka Arab (`1.1
|
|
50
|
+
* **Bab**: Diformat dengan Angka Romawi (`BAB I`, `BAB II`, `BAB III`, dst) atau Angka Arab sesuai preset (`heading_chapter_num_format`). Nomor bab dan judul dipisahkan oleh *line break* pada `#show heading.where(level: 1)`.
|
|
51
|
+
* **Sub-bab**: Diformat dengan Angka Arab (`1.1.`, `1.2.`, `2.1.`) sesuai flag `heading_sub_dot`.
|
|
49
52
|
* **Sub-sub-bab**: Diformat dengan Angka Arab 3 tingkat (`1.1.1`, `1.1.2`).
|
|
50
53
|
* **Heading tanpa nomor** (Kata Pengantar, Daftar Isi, Daftar Pustaka): menggunakan `{-}` di Markdown yang diterjemahkan Pandoc menjadi `#heading(level: 1, numbering: none)`.
|
|
51
54
|
|
|
52
55
|
> **Catatan**: Jangan menulis pola `"BAB I"` langsung sebagai format string `numbering(...)`. Typst menginterpretasikan rangkaian huruf `BAB` sebagai pola huruf dan memotong angka Romawinya. Selalu concat literal secara manual: `"BAB " + numbering("I", n)`.
|
|
53
56
|
|
|
54
57
|
### 2. Penomoran Halaman (frontmatter vs mainmatter)
|
|
55
|
-
* **Bagian Depan** (Kata Pengantar, Daftar Isi): Menggunakan penomoran Romawi
|
|
58
|
+
* **Bagian Depan** (Lembar Pengesahan, Abstrak, Kata Pengantar, Daftar Isi): Menggunakan penomoran Romawi kecil (`i`, `ii`, `iii`) di bagian tengah bawah. Diatur di `template.typ` tepat setelah sampul depan:
|
|
56
59
|
```typst
|
|
57
|
-
#
|
|
60
|
+
#pagebreak()
|
|
61
|
+
#set page(numbering: "i")
|
|
62
|
+
#counter(page).update(1)
|
|
58
63
|
```
|
|
59
|
-
* **Bagian Utama** (BAB I hingga BAB V): Menggunakan penomoran Angka Arab (`1`, `2`, `3`) berurutan, di-reset ulang
|
|
64
|
+
* **Bagian Utama** (BAB I hingga BAB V & Daftar Pustaka): Menggunakan penomoran Angka Arab (`1`, `2`, `3`) berurutan, di-reset ulang di akhir `cover.md`:
|
|
60
65
|
```typst
|
|
66
|
+
#pagebreak()
|
|
61
67
|
#set page(numbering: "1")
|
|
62
68
|
#counter(page).update(1)
|
|
63
69
|
```
|
|
@@ -22,21 +22,25 @@ Penulis menyadari bahwa laporan ini masih jauh dari sempurna. Oleh karena itu, k
|
|
|
22
22
|
depth: 3,
|
|
23
23
|
)
|
|
24
24
|
#context {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
if opt-daftar-gambar {
|
|
26
|
+
let imgs = query(figure.where(kind: image))
|
|
27
|
+
if imgs.len() > 0 {
|
|
28
|
+
pagebreak()
|
|
29
|
+
outline(
|
|
30
|
+
title: align(center)[#text(size: 14pt, weight: "bold")[DAFTAR GAMBAR]],
|
|
31
|
+
target: figure.where(kind: image),
|
|
32
|
+
)
|
|
33
|
+
}
|
|
32
34
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
if opt-daftar-tabel {
|
|
36
|
+
let tbls = query(figure.where(kind: table))
|
|
37
|
+
if tbls.len() > 0 {
|
|
38
|
+
pagebreak()
|
|
39
|
+
outline(
|
|
40
|
+
title: align(center)[#text(size: 14pt, weight: "bold")[DAFTAR TABEL]],
|
|
41
|
+
target: figure.where(kind: table),
|
|
42
|
+
)
|
|
43
|
+
}
|
|
40
44
|
}
|
|
41
45
|
}
|
|
42
46
|
#pagebreak()
|
|
@@ -14,6 +14,28 @@ local subsub = 0
|
|
|
14
14
|
local toc_entries = {}
|
|
15
15
|
local chapter_prefix = "BAB "
|
|
16
16
|
local chapter_num_fmt = "roman"
|
|
17
|
+
local sub_dot = true
|
|
18
|
+
local subsub_dot = false
|
|
19
|
+
local doc_margins = { top = 1134, right = 1417, bottom = 1701, left = 1417 }
|
|
20
|
+
|
|
21
|
+
local function dim_to_twips(val, fallback)
|
|
22
|
+
if not val or val == "" then return fallback end
|
|
23
|
+
local num, unit = val:match("^%s*([0-9]+%.?[0-9]*)%s*([a-zA-Z]+)%s*$")
|
|
24
|
+
if not num then return fallback end
|
|
25
|
+
local n = tonumber(num)
|
|
26
|
+
if not n then return fallback end
|
|
27
|
+
unit = unit:lower()
|
|
28
|
+
if unit == "cm" then
|
|
29
|
+
return math.floor(n * 566.929133858 + 0.5)
|
|
30
|
+
elseif unit == "mm" then
|
|
31
|
+
return math.floor(n * 56.6929133858 + 0.5)
|
|
32
|
+
elseif unit == "in" then
|
|
33
|
+
return math.floor(n * 1440 + 0.5)
|
|
34
|
+
elseif unit == "pt" then
|
|
35
|
+
return math.floor(n * 20 + 0.5)
|
|
36
|
+
end
|
|
37
|
+
return fallback
|
|
38
|
+
end
|
|
17
39
|
|
|
18
40
|
local function is_unnumbered(el)
|
|
19
41
|
for _, cls in ipairs(el.attr.classes or {}) do
|
|
@@ -138,9 +160,24 @@ local function meta_str(meta, key)
|
|
|
138
160
|
if v == nil then
|
|
139
161
|
return ""
|
|
140
162
|
end
|
|
163
|
+
if type(v) == "boolean" then
|
|
164
|
+
return tostring(v)
|
|
165
|
+
end
|
|
141
166
|
return pandoc.utils.stringify(v)
|
|
142
167
|
end
|
|
143
168
|
|
|
169
|
+
local function meta_bool(meta, key)
|
|
170
|
+
local v = meta[key]
|
|
171
|
+
if v == nil then
|
|
172
|
+
return false
|
|
173
|
+
end
|
|
174
|
+
if type(v) == "boolean" then
|
|
175
|
+
return v
|
|
176
|
+
end
|
|
177
|
+
local s = meta_str(meta, key):lower():gsub("%s+", "")
|
|
178
|
+
return s == "true" or s == "1" or s == "yes"
|
|
179
|
+
end
|
|
180
|
+
|
|
144
181
|
local function format_chapter_number(n)
|
|
145
182
|
if chapter_num_fmt == "arabic" then
|
|
146
183
|
return tostring(n)
|
|
@@ -165,6 +202,20 @@ local function Meta(meta)
|
|
|
165
202
|
if fmt ~= "" then
|
|
166
203
|
chapter_num_fmt = fmt
|
|
167
204
|
end
|
|
205
|
+
if meta["heading_sub_dot"] ~= nil then
|
|
206
|
+
sub_dot = meta_bool(meta, "heading_sub_dot")
|
|
207
|
+
end
|
|
208
|
+
if meta["heading_subsub_dot"] ~= nil then
|
|
209
|
+
subsub_dot = meta_bool(meta, "heading_subsub_dot")
|
|
210
|
+
end
|
|
211
|
+
local m_preset = meta_str(meta, "margin_preset")
|
|
212
|
+
if m_preset == "skripsi-4433" or m_preset == "4-4-3-3" then
|
|
213
|
+
doc_margins = { top = 2268, right = 1701, bottom = 1701, left = 2268 }
|
|
214
|
+
end
|
|
215
|
+
doc_margins.top = dim_to_twips(meta_str(meta, "margin_top"), doc_margins.top)
|
|
216
|
+
doc_margins.bottom = dim_to_twips(meta_str(meta, "margin_bottom"), doc_margins.bottom)
|
|
217
|
+
doc_margins.left = dim_to_twips(meta_str(meta, "margin_left"), doc_margins.left)
|
|
218
|
+
doc_margins.right = dim_to_twips(meta_str(meta, "margin_right"), doc_margins.right)
|
|
168
219
|
end
|
|
169
220
|
|
|
170
221
|
function Header(el)
|
|
@@ -213,20 +264,22 @@ function Header(el)
|
|
|
213
264
|
elseif el.level == 2 then
|
|
214
265
|
sub = sub + 1
|
|
215
266
|
subsub = 0
|
|
267
|
+
local sep2 = sub_dot and ". " or " "
|
|
216
268
|
toc_entries[#toc_entries + 1] = {
|
|
217
269
|
level = 2,
|
|
218
270
|
id = el.identifier,
|
|
219
|
-
text = bab .. "." .. sub ..
|
|
271
|
+
text = bab .. "." .. sub .. sep2 .. title,
|
|
220
272
|
}
|
|
221
|
-
el.content = pandoc.Str(bab .. "." .. sub ..
|
|
273
|
+
el.content = pandoc.Str(bab .. "." .. sub .. sep2 .. title)
|
|
222
274
|
elseif el.level == 3 then
|
|
223
275
|
subsub = subsub + 1
|
|
276
|
+
local sep3 = subsub_dot and ". " or " "
|
|
224
277
|
toc_entries[#toc_entries + 1] = {
|
|
225
278
|
level = 3,
|
|
226
279
|
id = el.identifier,
|
|
227
|
-
text = bab .. "." .. sub .. "." .. subsub ..
|
|
280
|
+
text = bab .. "." .. sub .. "." .. subsub .. sep3 .. title,
|
|
228
281
|
}
|
|
229
|
-
el.content = pandoc.Str(bab .. "." .. sub .. "." .. subsub ..
|
|
282
|
+
el.content = pandoc.Str(bab .. "." .. sub .. "." .. subsub .. sep3 .. title)
|
|
230
283
|
end
|
|
231
284
|
return el
|
|
232
285
|
end
|
|
@@ -280,6 +333,44 @@ local function daftar_isi()
|
|
|
280
333
|
return out
|
|
281
334
|
end
|
|
282
335
|
|
|
336
|
+
local function daftar_gambar()
|
|
337
|
+
local out = pandoc.List{}
|
|
338
|
+
out[#out + 1] = pagebreak()
|
|
339
|
+
out[#out + 1] = pandoc.RawBlock(
|
|
340
|
+
"openxml",
|
|
341
|
+
'<w:p><w:pPr><w:pStyle w:val="TOCHeading"/><w:jc w:val="center"/><w:outlineLvl w:val="9"/></w:pPr>' ..
|
|
342
|
+
'<w:r><w:rPr><w:b/><w:sz w:val="28"/><w:szCs w:val="28"/></w:rPr><w:t>DAFTAR GAMBAR</w:t></w:r></w:p>'
|
|
343
|
+
)
|
|
344
|
+
out[#out + 1] = pandoc.RawBlock(
|
|
345
|
+
"openxml",
|
|
346
|
+
'<w:p><w:pPr><w:pStyle w:val="TOC1"/></w:pPr>' ..
|
|
347
|
+
'<w:r><w:fldChar w:fldCharType="begin" w:dirty="true"/></w:r>' ..
|
|
348
|
+
'<w:r><w:instrText xml:space="preserve"> TOC \\c "Gambar" \\h \\z </w:instrText></w:r>' ..
|
|
349
|
+
'<w:r><w:fldChar w:fldCharType="separate"/></w:r>' ..
|
|
350
|
+
'<w:r><w:fldChar w:fldCharType="end"/></w:r></w:p>'
|
|
351
|
+
)
|
|
352
|
+
return out
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
local function daftar_tabel()
|
|
356
|
+
local out = pandoc.List{}
|
|
357
|
+
out[#out + 1] = pagebreak()
|
|
358
|
+
out[#out + 1] = pandoc.RawBlock(
|
|
359
|
+
"openxml",
|
|
360
|
+
'<w:p><w:pPr><w:pStyle w:val="TOCHeading"/><w:jc w:val="center"/><w:outlineLvl w:val="9"/></w:pPr>' ..
|
|
361
|
+
'<w:r><w:rPr><w:b/><w:sz w:val="28"/><w:szCs w:val="28"/></w:rPr><w:t>DAFTAR TABEL</w:t></w:r></w:p>'
|
|
362
|
+
)
|
|
363
|
+
out[#out + 1] = pandoc.RawBlock(
|
|
364
|
+
"openxml",
|
|
365
|
+
'<w:p><w:pPr><w:pStyle w:val="TOC1"/></w:pPr>' ..
|
|
366
|
+
'<w:r><w:fldChar w:fldCharType="begin" w:dirty="true"/></w:r>' ..
|
|
367
|
+
'<w:r><w:instrText xml:space="preserve"> TOC \\c "Tabel" \\h \\z </w:instrText></w:r>' ..
|
|
368
|
+
'<w:r><w:fldChar w:fldCharType="separate"/></w:r>' ..
|
|
369
|
+
'<w:r><w:fldChar w:fldCharType="end"/></w:r></w:p>'
|
|
370
|
+
)
|
|
371
|
+
return out
|
|
372
|
+
end
|
|
373
|
+
|
|
283
374
|
function Pandoc(doc)
|
|
284
375
|
if FORMAT ~= "docx" then
|
|
285
376
|
return doc
|
|
@@ -292,6 +383,8 @@ function Pandoc(doc)
|
|
|
292
383
|
local faculty = meta_str(meta, "faculty")
|
|
293
384
|
local institution = meta_str(meta, "institution")
|
|
294
385
|
local year = meta_str(meta, "year")
|
|
386
|
+
local show_dg = (meta_bool(meta, "daftar_gambar") or meta_bool(meta, "list_of_figures"))
|
|
387
|
+
local show_dt = (meta_bool(meta, "daftar_tabel") or meta_bool(meta, "list_of_tables"))
|
|
295
388
|
|
|
296
389
|
local blocks = pandoc.List{}
|
|
297
390
|
|
|
@@ -379,9 +472,109 @@ function Pandoc(doc)
|
|
|
379
472
|
-- Section break (Next Page) penutup cover: tanpa footer/nomor halaman
|
|
380
473
|
blocks[#blocks + 1] = pandoc.RawBlock(
|
|
381
474
|
"openxml",
|
|
382
|
-
|
|
475
|
+
string.format(
|
|
476
|
+
'<w:p><w:pPr><w:sectPr><w:footnotePr><w:numRestart w:val="eachSect"/></w:footnotePr><w:pgSz w:w="11906" w:h="16838"/><w:pgMar w:top="%d" w:right="%d" w:bottom="%d" w:left="%d" w:header="720" w:footer="720" w:gutter="0"/></w:sectPr></w:pPr></w:p>',
|
|
477
|
+
doc_margins.top, doc_margins.right, doc_margins.bottom, doc_margins.left
|
|
478
|
+
)
|
|
383
479
|
)
|
|
384
480
|
|
|
481
|
+
-- Optional Lembar Pengesahan (approval)
|
|
482
|
+
if meta["approval"] ~= nil and type(meta["approval"]) == "table" then
|
|
483
|
+
local app = meta["approval"]
|
|
484
|
+
local app_city = meta_str(app, "city")
|
|
485
|
+
local app_date = meta_str(app, "date")
|
|
486
|
+
if app_date == "" then app_date = meta_str(meta, "date") end
|
|
487
|
+
blocks[#blocks + 1] = pandoc.Header(1, { pandoc.Str("LEMBAR PENGESAHAN") }, pandoc.Attr("lembar-pengesahan", { "unnumbered" }, {}))
|
|
488
|
+
if title ~= "" then
|
|
489
|
+
blocks[#blocks + 1] = para({ pandoc.Strong(pandoc.Str(string.upper(title))) }, "CoverLine")
|
|
490
|
+
end
|
|
491
|
+
local date_line = (app_city ~= "" and (app_city .. ", ") or "") .. app_date
|
|
492
|
+
if date_line ~= "" then
|
|
493
|
+
blocks[#blocks + 1] = para({ pandoc.Str("Telah diperiksa dan disetujui pada: " .. date_line) }, "CoverLine")
|
|
494
|
+
end
|
|
495
|
+
if app["supervisors"] ~= nil and type(app["supervisors"]) == "table" then
|
|
496
|
+
for _, sup in ipairs(app["supervisors"]) do
|
|
497
|
+
local s_role = pandoc.utils.stringify(sup["role"] or "Pembimbing")
|
|
498
|
+
local s_name = pandoc.utils.stringify(sup["name"] or "")
|
|
499
|
+
local s_nip = pandoc.utils.stringify(sup["nip"] or "")
|
|
500
|
+
blocks[#blocks + 1] = spacer(360)
|
|
501
|
+
blocks[#blocks + 1] = para({ pandoc.Str(s_role) }, "CoverLine")
|
|
502
|
+
blocks[#blocks + 1] = spacer(480)
|
|
503
|
+
blocks[#blocks + 1] = para({ pandoc.Strong(pandoc.Str(s_name)) }, "CoverName")
|
|
504
|
+
if s_nip ~= "" then
|
|
505
|
+
blocks[#blocks + 1] = para({ pandoc.Str("NIP. " .. s_nip) }, "CoverLine")
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
end
|
|
509
|
+
if app["dean"] ~= nil and type(app["dean"]) == "table" then
|
|
510
|
+
local d_role = pandoc.utils.stringify(app["dean"]["role"] or "Dekan")
|
|
511
|
+
local d_name = pandoc.utils.stringify(app["dean"]["name"] or "")
|
|
512
|
+
local d_nip = pandoc.utils.stringify(app["dean"]["nip"] or "")
|
|
513
|
+
blocks[#blocks + 1] = spacer(360)
|
|
514
|
+
blocks[#blocks + 1] = para({ pandoc.Str("Mengetahui, " .. d_role) }, "CoverLine")
|
|
515
|
+
blocks[#blocks + 1] = spacer(480)
|
|
516
|
+
blocks[#blocks + 1] = para({ pandoc.Strong(pandoc.Str(d_name)) }, "CoverName")
|
|
517
|
+
if d_nip ~= "" then
|
|
518
|
+
blocks[#blocks + 1] = para({ pandoc.Str("NIP. " .. d_nip) }, "CoverLine")
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
blocks[#blocks + 1] = pagebreak()
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
-- Optional Abstrak Bahasa Indonesia (abstract_id)
|
|
525
|
+
local abs_id = meta_str(meta, "abstract_id")
|
|
526
|
+
if abs_id ~= "" then
|
|
527
|
+
blocks[#blocks + 1] = pandoc.Header(1, { pandoc.Str("ABSTRAK") }, pandoc.Attr("abstrak", { "unnumbered" }, {}))
|
|
528
|
+
if title ~= "" then
|
|
529
|
+
blocks[#blocks + 1] = para({ pandoc.Strong(pandoc.Str(string.upper(title))) }, "CoverLine")
|
|
530
|
+
end
|
|
531
|
+
blocks[#blocks + 1] = pandoc.Para({ pandoc.Str(abs_id) })
|
|
532
|
+
if meta["keywords_id"] ~= nil then
|
|
533
|
+
local kw_list = {}
|
|
534
|
+
if type(meta["keywords_id"]) == "table" and meta["keywords_id"][1] ~= nil then
|
|
535
|
+
for _, k in ipairs(meta["keywords_id"]) do
|
|
536
|
+
kw_list[#kw_list + 1] = pandoc.utils.stringify(k)
|
|
537
|
+
end
|
|
538
|
+
else
|
|
539
|
+
kw_list[#kw_list + 1] = meta_str(meta, "keywords_id")
|
|
540
|
+
end
|
|
541
|
+
if #kw_list > 0 then
|
|
542
|
+
blocks[#blocks + 1] = pandoc.Para({
|
|
543
|
+
pandoc.Strong(pandoc.Str("Kata Kunci: ")),
|
|
544
|
+
pandoc.Emph(pandoc.Str(table.concat(kw_list, ", ")))
|
|
545
|
+
})
|
|
546
|
+
end
|
|
547
|
+
end
|
|
548
|
+
blocks[#blocks + 1] = pagebreak()
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
-- Optional Abstract English (abstract_en)
|
|
552
|
+
local abs_en = meta_str(meta, "abstract_en")
|
|
553
|
+
if abs_en ~= "" then
|
|
554
|
+
blocks[#blocks + 1] = pandoc.Header(1, { pandoc.Str("ABSTRACT") }, pandoc.Attr("abstract", { "unnumbered" }, {}))
|
|
555
|
+
if title ~= "" then
|
|
556
|
+
blocks[#blocks + 1] = para({ pandoc.Strong(pandoc.Emph(pandoc.Str(string.upper(title)))) }, "CoverLine")
|
|
557
|
+
end
|
|
558
|
+
blocks[#blocks + 1] = pandoc.Para({ pandoc.Emph(pandoc.Str(abs_en)) })
|
|
559
|
+
if meta["keywords_en"] ~= nil then
|
|
560
|
+
local kw_en = {}
|
|
561
|
+
if type(meta["keywords_en"]) == "table" and meta["keywords_en"][1] ~= nil then
|
|
562
|
+
for _, k in ipairs(meta["keywords_en"]) do
|
|
563
|
+
kw_en[#kw_en + 1] = pandoc.utils.stringify(k)
|
|
564
|
+
end
|
|
565
|
+
else
|
|
566
|
+
kw_en[#kw_en + 1] = meta_str(meta, "keywords_en")
|
|
567
|
+
end
|
|
568
|
+
if #kw_en > 0 then
|
|
569
|
+
blocks[#blocks + 1] = pandoc.Para({
|
|
570
|
+
pandoc.Strong(pandoc.Str("Keywords: ")),
|
|
571
|
+
pandoc.Emph(pandoc.Str(table.concat(kw_en, ", ")))
|
|
572
|
+
})
|
|
573
|
+
end
|
|
574
|
+
end
|
|
575
|
+
blocks[#blocks + 1] = pagebreak()
|
|
576
|
+
end
|
|
577
|
+
|
|
385
578
|
meta.title = nil
|
|
386
579
|
meta.subtitle = nil
|
|
387
580
|
meta.date = nil
|
|
@@ -401,6 +594,16 @@ function Pandoc(doc)
|
|
|
401
594
|
for _, b in ipairs(daftar_isi()) do
|
|
402
595
|
body[#body + 1] = b
|
|
403
596
|
end
|
|
597
|
+
if show_dg then
|
|
598
|
+
for _, b in ipairs(daftar_gambar()) do
|
|
599
|
+
body[#body + 1] = b
|
|
600
|
+
end
|
|
601
|
+
end
|
|
602
|
+
if show_dt then
|
|
603
|
+
for _, b in ipairs(daftar_tabel()) do
|
|
604
|
+
body[#body + 1] = b
|
|
605
|
+
end
|
|
606
|
+
end
|
|
404
607
|
inserted = true
|
|
405
608
|
end
|
|
406
609
|
body[#body + 1] = blk
|
|
@@ -27,7 +27,7 @@ export PYTHONUTF8=1
|
|
|
27
27
|
show_banner() {
|
|
28
28
|
echo -e "${CYAN}${BOLD}"
|
|
29
29
|
echo " ========================================================"
|
|
30
|
-
echo " LAPORAN GENERATOR CLI v2.
|
|
30
|
+
echo " LAPORAN GENERATOR CLI v2.9.0 "
|
|
31
31
|
echo " Otomatisasi Dokumen Akademik (Typst + DOCX Engine) "
|
|
32
32
|
echo " ========================================================"
|
|
33
33
|
echo -e "${NC}"
|
|
@@ -345,7 +345,7 @@ cmd_build() {
|
|
|
345
345
|
./build.sh
|
|
346
346
|
echo ""
|
|
347
347
|
echo -e "${BLUE}${BOLD}2. Membangun Dokumen Microsoft Word (DOCX Multi-Pass)...${NC}"
|
|
348
|
-
|
|
348
|
+
./build-docx.sh
|
|
349
349
|
echo ""
|
|
350
350
|
echo -e "${GREEN}${BOLD}Dokumen PDF dan DOCX berhasil dibuat!${NC}"
|
|
351
351
|
echo -e " - ${CYAN}Laporan.pdf${NC}"
|