Skip to content

Commit 5cbb689

Browse files
authored
Merge pull request #103 from eric861129/dev
Dev
2 parents 91b91e3 + df421ae commit 5cbb689

10 files changed

Lines changed: 270 additions & 208 deletions

File tree

docs/AI_GENERATION_GUIDE.md

Lines changed: 25 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# AI Generation & Conversion Guide (AI 輔助生成與轉換指南)
22

3-
本文件旨在提供給大型語言模型 (LLM) 閱讀,以便精確地將現有的內容轉換為 **MD2DOC-Evolution** 專屬格式。
3+
本文件旨在提供給大型語言模型 (LLM) 閱讀,以便精確地將現有的內容轉換為 **MD2DOC-Evolution** 專屬格式,並符合專業出版社的寫作規範
44

55
---
66

@@ -23,33 +23,43 @@
2323
- **禁止使用 H4, H5, H6**:本專案僅支援 `#`, `##`, `###`。若原始稿件有更深層級,請將其轉換為 `**粗體項目**`
2424
- **目錄標籤**:在 Frontmatter 結束後的下一行,必須插入 `[TOC]`
2525

26-
### 3. 程式碼區塊 (Code Blocks)
26+
### 3. 出版級文字規範 (Publishing Standards)
27+
- **中英文空格**:中文與英文、數字之間 **不需要空格**。 (例:`使用 VS Code` 而非 `使用 VS Code`)
28+
- **標點符號**:中文句子中夾雜英文時,必須使用 **中文標點符號**。 (例:`開設 FB、IG 帳號。` 而非 `開設 FB, IG 帳號。`)
29+
- **UI 強調**:介紹軟體介面操作時,使用 `「」` 符號加以強調。 (例:完成後按 「Test Connection」)
30+
31+
### 4. 圖片與圖號 (Images & Figures)
32+
- **自動編號**:系統會自動根據出現順序編號為「圖 X」。
33+
- **圖名語法**:使用 `![圖名](url)`
34+
- **全頁圖片**:若圖片需放整頁 (13x18cm),請在 Alt 文字中加入 `full-page` 標記。 (例:`![這是全頁圖 full-page](url)`)
35+
- **截圖規範**:截圖請務必使用 **淺色亮底** 主題,並在關鍵步驟加上醒目框線。
36+
- **寬度限制**:系統會自動將圖片限制在 **13 cm** 寬度內。
37+
38+
### 5. 程式碼區塊 (Code Blocks)
2739
- **語法**:```語言[:ln|:no-ln]
2840
- **細節**
2941
- 預設會顯示行號。
3042
- 若為短小的設定檔,請強制標註 `:no-ln`
3143
- **範例**:```json:no-ln
3244

33-
### 4. 提示區塊 (Callouts)
45+
### 6. 提示區塊 (Callouts)
3446
- **格式**:必須使用 `> [!標記]`
3547
- **類型限制**:僅支援 `TIP`, `NOTE`, `WARNING`
3648
- **轉換邏輯**
3749
- 「注意」、「補充」 -> `> [!NOTE]`
3850
- 「技巧」、「建議」 -> `> [!TIP]`
3951
- 「警告」、「重要」 -> `> [!WARNING]`
4052

41-
### 5. 角色對話 (Chat Dialogues) - **極重要**
42-
這是 AI 最容易出錯的地方,請嚴格執行:
43-
- **左側 (AI/講者)**`角色名稱 ":: 對話內容` (注意引號位置)
44-
- **右側 (User/讀者)**`對話內容 ::" 角色`
45-
- **範例**
46-
- `GPT ":: 您好!有什麼我能幫您的?`
47-
- `請幫我寫一段程式碼 ::" 使用者`
53+
### 7. 角色對話 (Chat Dialogues)
54+
- **左側 (AI/他人)**`角色名稱 "::` (引號在冒號前)
55+
- **右側 (User/作者)**`角色名稱 ::"` (引號在冒號後)
56+
- **置中 (System/旁白)**`角色名稱 :":` (引號在中間)
4857

49-
### 6. 行內樣式轉換表
58+
### 8. 行內樣式轉換表
5059
| 原始內容 | 轉換後格式 | 說明 |
5160
| :--- | :--- | :--- |
52-
| 「點擊設定」 | `【設定】` | 所有 UI 按鈕、選單項目 |
61+
| 「點擊設定」 | `「設定」` | UI 元素強調 (建議優先使用) |
62+
| 【點擊設定】 | `【設定】` | UI 按鈕、選單項目 (帶底色) |
5363
| Ctrl+C | `[Ctrl]`+`[C]` | 所有實體按鍵 |
5464
| 《深入淺出》 | `『深入淺出』` | 所有書名、軟體專案名 |
5565
| [連結](url) | `[連結](url)` | 保持原樣,系統會自動轉 QR Code |
@@ -59,132 +69,6 @@
5969
## 負面約束 (Negative Constraints)
6070
- **不要** 使用 HTML 標籤(如 `<u>`, `<br>`)。
6171
- **不要** 在 Callout 內嵌套另一個 Callout。
62-
- **不要** 自行發明 Callout 標籤(如 `[!DANGER]` 是不支援的)。
63-
- **不要** 改變 Mermaid 的標準語法。
64-
65-
---
66-
# AI Generation Guide for MD2DOC-Evolution
67-
68-
本指南定義了將 Markdown 轉換為 MD2DOC-Evolution 格式的標準規範。AI 模型應嚴格遵守以下語法規則。
69-
70-
## 1. Frontmatter (YAML)
71-
文件必須以標準 YAML 格式開頭。
72-
73-
### ✅ 正確範例
74-
```markdown
75-
---
76-
title: 第2章:工具箱——打造你的數位軍火庫
77-
author: ChiYu
78-
---
79-
80-
[TOC]
81-
82-
# 第2章:工具箱——打造你的數位軍火庫
83-
84-
```
85-
86-
### ❌ 錯誤範例 (禁止)
87-
88-
* ❌ 缺少結尾的 `---`
89-
* ❌ 在 YAML 中使用 `##` 標題
90-
* ❌ 將 `[TOC]` 放進 YAML 區塊中
91-
* 如果有需要目錄才添加[TOC]
92-
93-
---
94-
95-
## 2. 列表 (Lists)
96-
97-
子列表必須使用 **2個空白 (Spaces)** 進行縮排,以確保層級正確。
98-
99-
### ✅ 正確範例
100-
101-
```markdown
102-
* **『Python (Microsoft)』**
103-
* 這是什麼:微軟官方出品的 Python 語言支援包。
104-
* 為什麼必裝:裝上它,你的『VS Code』才會真正「看懂」Python。
105-
* **『Prettier』**
106-
* 這是什麼:你的程式碼專屬造型師。
107-
108-
```
109-
110-
### ❌ 錯誤範例 (扁平化)
111-
112-
```markdown
113-
* **『Python (Microsoft)』**
114-
* 這是什麼:微軟官方出品的 Python 語言支援包。 (❌ 錯誤:沒有縮排,被視為同一層級)
115-
116-
```
117-
118-
---
119-
120-
## 3. 對話 (Chat)
121-
122-
使用特定的前綴語法來表示對話氣泡。
123-
124-
### 語法
125-
126-
`角色名稱 ::"` 換行後接續對話內容。
127-
128-
### ✅ 正確範例
129-
130-
```markdown
131-
讀者 ::"
132-
ChiYu,既然我們已經決定用 Python 來開發 RPG 遊戲的後端了,為什麼現在又要裝一個叫 Node.js 的東西?
133-
134-
ChiYu ::"
135-
這是一個很棒的問題!我們要區分清楚「產品」與「工具」的差別。
136-
137-
```
138-
139-
### ❌ 錯誤範例
140-
141-
*`ChiYu "::` (錯誤的引號位置)
142-
*`::" ChiYu` (不需要結尾標籤)
143-
144-
---
145-
146-
## 4. 行內樣式 (Inline Styles)
147-
148-
請根據語意選擇正確的括號樣式。
149-
150-
| 類型 | 語法 | 範例 |
151-
| --- | --- | --- |
152-
| **UI 元素 / 按鈕** | `【】` | 請點擊【確定】按鈕、查看【檔案總管】。 |
153-
| **快捷鍵** | `[]` | 按下 [Ctrl] + [C] 複製。 |
154-
| **書名 / 專案名 / 強調物件** | `『』` | 本書使用『VS Code』進行開發。 |
155-
| **一般強調** | `**` | 這是 **非常重要** 的觀念。 |
156-
157-
---
158-
159-
## 5. 提示區塊 (Callouts)
160-
161-
將筆記或警告轉換為 GitHub 風味的 Blockquotes。
162-
163-
### ✅ 正確範例
164-
165-
```markdown
166-
> [!NOTE]
167-
> 这是一个補充說明。
168-
169-
> [!WARNING]
170-
> 【Windows 使用者請注意】:請務必勾選 Add to PATH。
171-
172-
```
173-
174-
---
175-
176-
## 6. 標題與結構
177-
178-
* 僅使用 H1 (#), H2 (##), H3 (###)。
179-
* H4 以下請轉換為粗體文字或列表項目。
180-
* 確保程式碼區塊包含語言標籤 (例如 ````python`)。
181-
182-
```
183-
184-
### 修改重點說明:
185-
186-
1. **YAML 修正**:在 Prompt 的 `Rule #1` 中,我特別強調了「頭尾都要有 `---`」以及「嚴禁使用 `#` 符號」,這能直接解決 AI 生成 `## title:` 這種錯誤格式的問題。
187-
2. **列表縮排**:在 Prompt 的 `Rule #4` 和 Guide 的 `Section 2` 中,我明確要求了「2個空白縮排」,並給出了「扁平化錯誤」的負面範例,這對 AI 理解結構非常有幫助。
188-
3. **Chat 語法簡化**:根據您的指示,將語法統一為 `角色 ::"`。我在 Prompt 中加入了一條「禁止使用結尾標籤」的規則,以防止 AI 因為過度熱心而自行閉合標籤。
189-
190-
```
72+
- **不要** 自行發明 Callout 標籤。
73+
- **不要** 忽略關鍵步驟,避免用「這個大家應該都知道」為前提。
74+
- **不要** 忘記專有名詞第一次出現時要簡短解釋。

services/docx/builders/common.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ export const parseInlineStyles = async (text: string, config?: DocxConfig): Prom
101101
shading: { fill: COLORS.BG_BUTTON, type: ShadingType.CLEAR, color: "auto" }
102102
}));
103103
break;
104+
case InlineStyleType.UI_EMPHASIS:
105+
runs.push(new TextRun({
106+
...baseConfig,
107+
bold: true
108+
}));
109+
break;
104110
case InlineStyleType.SHORTCUT:
105111
runs.push(new TextRun({
106112
...baseConfig,

services/docx/builders/image.ts

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* Licensed under the MIT License.
55
*/
66

7-
import { Paragraph, ImageRun, AlignmentType } from "docx";
7+
import { Paragraph, ImageRun, AlignmentType, TextRun } from "docx";
88
import { DocxConfig } from "../types";
99
import { WORD_THEME } from "../../../constants/theme";
1010

@@ -60,13 +60,39 @@ export const createImageBlock = async (src: string, alt: string, config: DocxCon
6060
const dims = await getImageDimensions(realSrc);
6161
if (dims.width === 0) return [];
6262

63-
// Force image to fill the maximum page width
64-
const maxWidthPx = (config.widthCm - 4) * 37.8; // Use slightly larger width (less margin)
63+
// Increment Figure Counter
64+
if (config.counters) {
65+
config.counters.figure++;
66+
}
67+
const figNum = config.counters ? config.counters.figure : 0;
68+
69+
// Publisher Requirement 09 & 10
70+
// Max width is 13cm. Full page is 13x18cm.
71+
const isFullPage = alt.includes('full-page');
72+
const cleanAlt = alt.replace('full-page', '').trim();
6573

66-
// Calculate target height to maintain aspect ratio
67-
const ratio = maxWidthPx / dims.width;
68-
const targetWidth = maxWidthPx;
69-
const targetHeight = dims.height * ratio;
74+
const MAX_WIDTH_CM = 13;
75+
const MAX_HEIGHT_CM = isFullPage ? 18 : 20; // Limit height too to avoid breaking layout
76+
77+
const maxWidthPx = MAX_WIDTH_CM * 37.8; // 1cm approx 37.8px (96dpi)
78+
const maxHeightPx = MAX_HEIGHT_CM * 37.8;
79+
80+
let targetWidth = dims.width;
81+
let targetHeight = dims.height;
82+
83+
// Scale to fit max width
84+
if (targetWidth > maxWidthPx) {
85+
const ratio = maxWidthPx / targetWidth;
86+
targetWidth = maxWidthPx;
87+
targetHeight = targetHeight * ratio;
88+
}
89+
90+
// Scale to fit max height if still too large
91+
if (targetHeight > maxHeightPx) {
92+
const ratio = maxHeightPx / targetHeight;
93+
targetHeight = maxHeightPx;
94+
targetWidth = targetWidth * ratio;
95+
}
7096

7197
// 4. Create Paragraphs
7298
const imagePara = new Paragraph({
@@ -86,21 +112,18 @@ export const createImageBlock = async (src: string, alt: string, config: DocxCon
86112

87113
const result = [imagePara];
88114

89-
// 4. Add Caption if Alt text exists
90-
if (alt) {
91-
result.push(new Paragraph({
92-
alignment: AlignmentType.CENTER,
93-
children: [
94-
new TextRun({
95-
text: ` ▲ ${alt}`,
96-
italics: true,
97-
size: 18, // 9pt
98-
color: "666666"
99-
})
100-
],
101-
spacing: { before: 0, after: 200 },
102-
}));
103-
}
115+
// 4. Add Caption with Figure Number (Requirement 05)
116+
result.push(new Paragraph({
117+
alignment: AlignmentType.CENTER,
118+
children: [
119+
new TextRun({
120+
text: `圖 ${figNum} ${cleanAlt}`,
121+
bold: true,
122+
size: 20, // 10pt
123+
})
124+
],
125+
spacing: { before: 0, after: 200 },
126+
}));
104127

105128
return result;
106129
} catch (e) {

services/docx/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,8 @@ export interface DocxConfig {
1313
showLineNumbers?: boolean;
1414
meta?: DocumentMeta;
1515
imageRegistry?: Record<string, string>;
16+
counters?: {
17+
figure: number;
18+
qr: number;
19+
};
1620
}

services/docxGenerator.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ export const generateDocx = async (
2828
config: DocxConfig = { widthCm: 17, heightCm: 23 }
2929
): Promise<Blob> => {
3030

31+
// Initialize counters for automatic numbering (Figures, QRs)
32+
config.counters = {
33+
figure: 0,
34+
qr: 0
35+
};
36+
3137
const docChildren: (Paragraph | Table)[] = [];
3238

3339
for (const block of blocks) {

0 commit comments

Comments
 (0)