claude-code-workflow 6.3.24 → 6.3.26

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 (75) hide show
  1. package/.claude/commands/issue/discover-by-prompt.md +764 -0
  2. package/.claude/skills/text-formatter/SKILL.md +196 -0
  3. package/.claude/skills/text-formatter/phases/01-input-collection.md +111 -0
  4. package/.claude/skills/text-formatter/phases/02-content-analysis.md +248 -0
  5. package/.claude/skills/text-formatter/phases/03-format-transform.md +245 -0
  6. package/.claude/skills/text-formatter/phases/04-output-preview.md +183 -0
  7. package/.claude/skills/text-formatter/specs/callout-types.md +293 -0
  8. package/.claude/skills/text-formatter/specs/element-mapping.md +226 -0
  9. package/.claude/skills/text-formatter/specs/format-rules.md +273 -0
  10. package/.claude/skills/text-formatter/templates/bbcode-template.md +350 -0
  11. package/ccw/dist/core/routes/help-routes.d.ts.map +1 -1
  12. package/ccw/dist/core/routes/help-routes.js +43 -7
  13. package/ccw/dist/core/routes/help-routes.js.map +1 -1
  14. package/ccw/dist/core/routes/litellm-api-routes.d.ts.map +1 -1
  15. package/ccw/dist/core/routes/litellm-api-routes.js +31 -5
  16. package/ccw/dist/core/routes/litellm-api-routes.js.map +1 -1
  17. package/ccw/dist/core/routes/memory-routes.d.ts.map +1 -1
  18. package/ccw/dist/core/routes/memory-routes.js +73 -0
  19. package/ccw/dist/core/routes/memory-routes.js.map +1 -1
  20. package/ccw/dist/core/routes/status-routes.d.ts.map +1 -1
  21. package/ccw/dist/core/routes/status-routes.js +36 -4
  22. package/ccw/dist/core/routes/status-routes.js.map +1 -1
  23. package/ccw/dist/core/server.d.ts.map +1 -1
  24. package/ccw/dist/core/server.js +58 -0
  25. package/ccw/dist/core/server.js.map +1 -1
  26. package/ccw/dist/core/services/api-key-tester.d.ts.map +1 -1
  27. package/ccw/dist/core/services/api-key-tester.js +8 -3
  28. package/ccw/dist/core/services/api-key-tester.js.map +1 -1
  29. package/ccw/dist/tools/claude-cli-tools.d.ts +7 -0
  30. package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -1
  31. package/ccw/dist/tools/claude-cli-tools.js +11 -1
  32. package/ccw/dist/tools/claude-cli-tools.js.map +1 -1
  33. package/ccw/dist/tools/cli-executor-core.d.ts +11 -0
  34. package/ccw/dist/tools/cli-executor-core.d.ts.map +1 -1
  35. package/ccw/dist/tools/cli-executor-core.js +89 -2
  36. package/ccw/dist/tools/cli-executor-core.js.map +1 -1
  37. package/ccw/dist/tools/codex-lens.d.ts +2 -1
  38. package/ccw/dist/tools/codex-lens.d.ts.map +1 -1
  39. package/ccw/dist/tools/codex-lens.js +51 -8
  40. package/ccw/dist/tools/codex-lens.js.map +1 -1
  41. package/ccw/dist/tools/index.d.ts.map +1 -1
  42. package/ccw/dist/tools/index.js +2 -0
  43. package/ccw/dist/tools/index.js.map +1 -1
  44. package/ccw/dist/tools/litellm-client.d.ts +6 -0
  45. package/ccw/dist/tools/litellm-client.d.ts.map +1 -1
  46. package/ccw/dist/tools/litellm-client.js +22 -1
  47. package/ccw/dist/tools/litellm-client.js.map +1 -1
  48. package/ccw/dist/tools/litellm-executor.js +2 -2
  49. package/ccw/dist/tools/litellm-executor.js.map +1 -1
  50. package/ccw/dist/tools/memory-update-queue.d.ts +172 -0
  51. package/ccw/dist/tools/memory-update-queue.d.ts.map +1 -0
  52. package/ccw/dist/tools/memory-update-queue.js +431 -0
  53. package/ccw/dist/tools/memory-update-queue.js.map +1 -0
  54. package/ccw/src/core/routes/help-routes.ts +46 -7
  55. package/ccw/src/core/routes/litellm-api-routes.ts +35 -4
  56. package/ccw/src/core/routes/memory-routes.ts +84 -0
  57. package/ccw/src/core/routes/status-routes.ts +39 -4
  58. package/ccw/src/core/server.ts +62 -0
  59. package/ccw/src/core/services/api-key-tester.ts +9 -3
  60. package/ccw/src/templates/dashboard-css/21-cli-toolmgmt.css +45 -0
  61. package/ccw/src/templates/dashboard-js/components/cli-status.js +36 -5
  62. package/ccw/src/templates/dashboard-js/components/hook-manager.js +42 -81
  63. package/ccw/src/templates/dashboard-js/components/mcp-manager.js +170 -28
  64. package/ccw/src/templates/dashboard-js/components/notifications.js +14 -4
  65. package/ccw/src/templates/dashboard-js/i18n.js +26 -0
  66. package/ccw/src/templates/dashboard-js/views/cli-manager.js +72 -2
  67. package/ccw/src/templates/dashboard-js/views/codexlens-manager.js +11 -1
  68. package/ccw/src/tools/claude-cli-tools.ts +17 -1
  69. package/ccw/src/tools/cli-executor-core.ts +103 -2
  70. package/ccw/src/tools/codex-lens.ts +63 -8
  71. package/ccw/src/tools/index.ts +2 -0
  72. package/ccw/src/tools/litellm-client.ts +25 -3
  73. package/ccw/src/tools/litellm-executor.ts +2 -2
  74. package/ccw/src/tools/memory-update-queue.js +499 -0
  75. package/package.json +91 -91
@@ -0,0 +1,245 @@
1
+ # Phase 3: Format Transform
2
+
3
+ 将内容转换为 BBCode + Markdown 混合格式(论坛优化)。
4
+
5
+ ## Objective
6
+
7
+ - 根据分析结果转换内容
8
+ - 应用像素级字号规则
9
+ - 处理 Callout/标注语法
10
+ - 生成论坛兼容的输出
11
+
12
+ ## Input
13
+
14
+ - 依赖: `input-config.json`, `analysis.json`
15
+ - 规范: `specs/format-rules.md`, `specs/element-mapping.md`
16
+
17
+ ## Format Specification
18
+
19
+ ### Size Hierarchy (Pixels)
20
+
21
+ | Element | Size | Color | Usage |
22
+ |---------|------|-------|-------|
23
+ | **H1** | 150 | #2196F3 | 文档主标题 |
24
+ | **H2** | 120 | #2196F3 | 章节标题 |
25
+ | **H3** | 100 | #333 | 子标题 |
26
+ | **H4+** | (默认) | - | 仅加粗 |
27
+ | **Notes** | 80 | gray | 备注/元数据 |
28
+
29
+ ### Unsupported Tags (禁止使用)
30
+
31
+ | Tag | Reason | Alternative |
32
+ |-----|--------|-------------|
33
+ | `[align]` | 不渲染 | 删除,使用默认左对齐 |
34
+ | `[hr]` | 显示为文本 | 使用 Markdown `---` |
35
+ | `[table]` | 支持有限 | 转为列表或代码块 |
36
+ | HTML tags | 不支持 | 仅使用 BBCode |
37
+
38
+ ## Execution Steps
39
+
40
+ ### Step 1: 加载配置和分析
41
+
42
+ ```javascript
43
+ const config = JSON.parse(Read(`${workDir}/input-config.json`));
44
+ const analysis = JSON.parse(Read(`${workDir}/analysis.json`));
45
+ const content = config.original_content;
46
+ ```
47
+
48
+ ### Step 2: Callout 配置
49
+
50
+ ```javascript
51
+ // Callout 类型映射(像素级字号)
52
+ const CALLOUT_CONFIG = {
53
+ // 信息类
54
+ note: { icon: '📝', color: '#2196F3', label: '注意' },
55
+ info: { icon: 'ℹ️', color: '#2196F3', label: '信息' },
56
+ abstract: { icon: '📄', color: '#2196F3', label: '摘要' },
57
+ summary: { icon: '📄', color: '#2196F3', label: '摘要' },
58
+ tldr: { icon: '📄', color: '#2196F3', label: '摘要' },
59
+
60
+ // 成功/提示类
61
+ tip: { icon: '💡', color: '#4CAF50', label: '提示' },
62
+ hint: { icon: '💡', color: '#4CAF50', label: '提示' },
63
+ success: { icon: '✅', color: '#4CAF50', label: '成功' },
64
+ check: { icon: '✅', color: '#4CAF50', label: '完成' },
65
+ done: { icon: '✅', color: '#4CAF50', label: '完成' },
66
+
67
+ // 警告类
68
+ warning: { icon: '⚠️', color: '#FF9800', label: '警告' },
69
+ caution: { icon: '⚠️', color: '#FF9800', label: '注意' },
70
+ attention: { icon: '⚠️', color: '#FF9800', label: '注意' },
71
+ question: { icon: '❓', color: '#FF9800', label: '问题' },
72
+ help: { icon: '❓', color: '#FF9800', label: '帮助' },
73
+ faq: { icon: '❓', color: '#FF9800', label: 'FAQ' },
74
+ todo: { icon: '📋', color: '#FF9800', label: '待办' },
75
+
76
+ // 错误/危险类
77
+ danger: { icon: '❌', color: '#F44336', label: '危险' },
78
+ error: { icon: '❌', color: '#F44336', label: '错误' },
79
+ bug: { icon: '🐛', color: '#F44336', label: 'Bug' },
80
+ important: { icon: '⭐', color: '#F44336', label: '重要' },
81
+
82
+ // 其他
83
+ example: { icon: '📋', color: '#9C27B0', label: '示例' },
84
+ quote: { icon: '💬', color: 'gray', label: '引用' },
85
+ cite: { icon: '💬', color: 'gray', label: '引用' }
86
+ };
87
+
88
+ // Callout 检测正则 (支持 +/- 折叠标记)
89
+ const CALLOUT_PATTERN = /^>\s*\[!(\w+)\][+-]?(?:\s+(.+))?$/;
90
+ ```
91
+
92
+ ### Step 3: Callout 解析器
93
+
94
+ ```javascript
95
+ function parseCallouts(text) {
96
+ const lines = text.split('\n');
97
+ const result = [];
98
+ let i = 0;
99
+
100
+ while (i < lines.length) {
101
+ const match = lines[i].match(CALLOUT_PATTERN);
102
+ if (match) {
103
+ const type = match[1].toLowerCase();
104
+ const title = match[2] || null;
105
+ const content = [];
106
+ i++;
107
+
108
+ // 收集 Callout 内容行
109
+ while (i < lines.length && lines[i].startsWith('>')) {
110
+ content.push(lines[i].replace(/^>\s*/, ''));
111
+ i++;
112
+ }
113
+
114
+ result.push({
115
+ isCallout: true,
116
+ type,
117
+ title,
118
+ content: content.join('\n')
119
+ });
120
+ } else {
121
+ result.push({ isCallout: false, line: lines[i] });
122
+ i++;
123
+ }
124
+ }
125
+
126
+ return result;
127
+ }
128
+ ```
129
+
130
+ ### Step 4: BBCode+MD 转换器
131
+
132
+ ```javascript
133
+ function formatBBCodeMD(text) {
134
+ let result = text;
135
+
136
+ // ===== 标题转换 (像素级字号) =====
137
+ result = result.replace(/^######\s*(.+)$/gm, '[b]$1[/b]');
138
+ result = result.replace(/^#####\s*(.+)$/gm, '[b]$1[/b]');
139
+ result = result.replace(/^####\s*(.+)$/gm, '[b]$1[/b]');
140
+ result = result.replace(/^###\s*(.+)$/gm, '[size=100][color=#333][b]$1[/b][/color][/size]');
141
+ result = result.replace(/^##\s*(.+)$/gm, '[size=120][color=#2196F3][b]$1[/b][/color][/size]');
142
+ result = result.replace(/^#\s*(.+)$/gm, '[size=150][color=#2196F3][b]$1[/b][/color][/size]');
143
+
144
+ // ===== 文本样式 =====
145
+ result = result.replace(/\*\*\*(.+?)\*\*\*/g, '[b][i]$1[/i][/b]');
146
+ result = result.replace(/\*\*(.+?)\*\*/g, '[b]$1[/b]');
147
+ result = result.replace(/__(.+?)__/g, '[b]$1[/b]');
148
+ result = result.replace(/\*(.+?)\*/g, '[i]$1[/i]');
149
+ result = result.replace(/_(.+?)_/g, '[i]$1[/i]');
150
+ result = result.replace(/~~(.+?)~~/g, '[s]$1[/s]');
151
+ result = result.replace(/==(.+?)==/g, '[color=yellow]$1[/color]');
152
+
153
+ // ===== HTML 转 BBCode =====
154
+ result = result.replace(/<mark>(.+?)<\/mark>/g, '[color=yellow]$1[/color]');
155
+ result = result.replace(/<u>(.+?)<\/u>/g, '[u]$1[/u]');
156
+ result = result.replace(/<details>\s*<summary>(.+?)<\/summary>\s*([\s\S]*?)<\/details>/g,
157
+ '[spoiler=$1]$2[/spoiler]');
158
+
159
+ // ===== 代码 =====
160
+ result = result.replace(/```(\w*)\n([\s\S]*?)```/g, '[code]$2[/code]');
161
+ // 行内代码保持原样 (部分论坛不支持 font=monospace)
162
+
163
+ // ===== 链接和图片 =====
164
+ result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '[url=$2]$1[/url]');
165
+ result = result.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '[img]$2[/img]');
166
+
167
+ // ===== 引用 (非 Callout) =====
168
+ result = result.replace(/^>\s+(.+)$/gm, '[quote]$1[/quote]');
169
+
170
+ // ===== 列表 (使用 • 符号) =====
171
+ result = result.replace(/^[-*+]\s+(.+)$/gm, '• $1');
172
+
173
+ // ===== 分隔线 (保持 Markdown 语法) =====
174
+ // `---` 在混合格式中通常可用,不转换为 [hr]
175
+
176
+ return result.trim();
177
+ }
178
+ ```
179
+
180
+ ### Step 5: Callout 转换
181
+
182
+ ```javascript
183
+ function convertCallouts(text) {
184
+ const parsed = parseCallouts(text);
185
+
186
+ return parsed.map(item => {
187
+ if (item.isCallout) {
188
+ const cfg = CALLOUT_CONFIG[item.type] || CALLOUT_CONFIG.note;
189
+ const displayTitle = item.title || cfg.label;
190
+
191
+ // 使用 [quote] 包裹,标题使用 size=100
192
+ return `[quote]
193
+ [size=100][color=${cfg.color}][b]${cfg.icon} ${displayTitle}[/b][/color][/size]
194
+
195
+ ${item.content}
196
+ [/quote]`;
197
+ }
198
+ return item.line;
199
+ }).join('\n');
200
+ }
201
+ ```
202
+
203
+ ### Step 6: 执行转换
204
+
205
+ ```javascript
206
+ // 1. 先处理 Callouts
207
+ let formattedContent = convertCallouts(content);
208
+
209
+ // 2. 再进行通用 BBCode+MD 转换
210
+ formattedContent = formatBBCodeMD(formattedContent);
211
+
212
+ // 3. 清理多余空行
213
+ formattedContent = formattedContent.replace(/\n{3,}/g, '\n\n');
214
+ ```
215
+
216
+ ### Step 7: 保存转换结果
217
+
218
+ ```javascript
219
+ const outputFile = 'output.bbcode.txt';
220
+ Write(`${workDir}/${outputFile}`, formattedContent);
221
+
222
+ // 更新配置
223
+ config.output_file = outputFile;
224
+ config.formatted_content = formattedContent;
225
+ Write(`${workDir}/input-config.json`, JSON.stringify(config, null, 2));
226
+ ```
227
+
228
+ ## Output
229
+
230
+ - **File**: `output.bbcode.txt`
231
+ - **Format**: BBCode + Markdown 混合格式
232
+
233
+ ## Quality Checklist
234
+
235
+ - [ ] 标题使用像素值 (150/120/100)
236
+ - [ ] 未使用 `[align]` 标签
237
+ - [ ] 未使用 `[hr]` 标签
238
+ - [ ] 分隔线使用 `---`
239
+ - [ ] Callout 正确转换为 [quote]
240
+ - [ ] 颜色值使用 hex 格式
241
+ - [ ] 内容完整无丢失
242
+
243
+ ## Next Phase
244
+
245
+ → [Phase 4: Output & Preview](04-output-preview.md)
@@ -0,0 +1,183 @@
1
+ # Phase 4: Output & Preview
2
+
3
+ 输出最终结果并提供预览。
4
+
5
+ ## Objective
6
+
7
+ - 保存格式化后的内容到文件
8
+ - 提供预览功能
9
+ - 显示转换统计信息
10
+
11
+ ## Input
12
+
13
+ - 依赖: `input-config.json`, `output.*`
14
+ - 配置: `{workDir}/input-config.json`
15
+
16
+ ## Execution Steps
17
+
18
+ ### Step 1: 加载结果
19
+
20
+ ```javascript
21
+ const config = JSON.parse(Read(`${workDir}/input-config.json`));
22
+ const analysis = JSON.parse(Read(`${workDir}/analysis.json`));
23
+ const outputFile = `${workDir}/${config.output_file}`;
24
+ const formattedContent = Read(outputFile);
25
+ ```
26
+
27
+ ### Step 2: 生成统计摘要
28
+
29
+ ```javascript
30
+ const summary = {
31
+ input: {
32
+ method: config.input_method,
33
+ original_length: config.original_content.length,
34
+ word_count: config.original_content.split(/\s+/).length
35
+ },
36
+ output: {
37
+ format: config.target_format,
38
+ file: outputFile,
39
+ length: formattedContent.length
40
+ },
41
+ elements: analysis.stats,
42
+ reading_time: analysis.semantics?.estimated_reading_time || 1
43
+ };
44
+
45
+ console.log(`
46
+ ╔════════════════════════════════════════════════════════════════╗
47
+ ║ Text Formatter Summary ║
48
+ ╠════════════════════════════════════════════════════════════════╣
49
+ ║ Input: ${summary.input.word_count} words (${summary.input.original_length} chars)
50
+ ║ Output: ${summary.output.format} → ${summary.output.file}
51
+ ║ Elements Converted:
52
+ ║ • Headings: ${summary.elements.headings}
53
+ ║ • Paragraphs: ${summary.elements.paragraphs}
54
+ ║ • Lists: ${summary.elements.lists}
55
+ ║ • Code Blocks: ${summary.elements.code_blocks}
56
+ ║ • Links: ${summary.elements.links}
57
+ ║ Estimated Reading Time: ${summary.reading_time} min
58
+ ╚════════════════════════════════════════════════════════════════╝
59
+ `);
60
+ ```
61
+
62
+ ### Step 3: HTML 预览(如适用)
63
+
64
+ ```javascript
65
+ if (config.target_format === 'HTML') {
66
+ // 生成完整 HTML 文件用于预览
67
+ const previewHtml = `<!DOCTYPE html>
68
+ <html lang="zh-CN">
69
+ <head>
70
+ <meta charset="UTF-8">
71
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
72
+ <title>Text Formatter Preview</title>
73
+ <style>
74
+ body {
75
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
76
+ line-height: 1.6;
77
+ max-width: 800px;
78
+ margin: 0 auto;
79
+ padding: 2rem;
80
+ background: #f5f5f5;
81
+ }
82
+ .content {
83
+ background: white;
84
+ padding: 2rem;
85
+ border-radius: 8px;
86
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
87
+ }
88
+ h1, h2, h3, h4, h5, h6 { color: #333; margin-top: 1.5em; }
89
+ code { background: #f0f0f0; padding: 2px 6px; border-radius: 3px; }
90
+ pre { background: #282c34; color: #abb2bf; padding: 1rem; border-radius: 6px; overflow-x: auto; }
91
+ pre code { background: none; padding: 0; }
92
+ blockquote { border-left: 4px solid #ddd; margin: 0; padding-left: 1rem; color: #666; }
93
+ a { color: #0066cc; }
94
+ img { max-width: 100%; }
95
+ hr { border: none; border-top: 1px solid #ddd; margin: 2rem 0; }
96
+ </style>
97
+ </head>
98
+ <body>
99
+ <div class="content">
100
+ ${formattedContent}
101
+ </div>
102
+ </body>
103
+ </html>`;
104
+
105
+ Write(`${workDir}/preview.html`, previewHtml);
106
+
107
+ // 可选:在浏览器中打开预览
108
+ // Bash(`start "${workDir}/preview.html"`); // Windows
109
+ // Bash(`open "${workDir}/preview.html"`); // macOS
110
+ }
111
+ ```
112
+
113
+ ### Step 4: 显示输出内容
114
+
115
+ ```javascript
116
+ // 显示格式化后的内容
117
+ console.log('\n=== Formatted Content ===\n');
118
+ console.log(formattedContent);
119
+ console.log('\n=========================\n');
120
+
121
+ // 提示用户
122
+ console.log(`
123
+ 📁 Output saved to: ${outputFile}
124
+ ${config.target_format === 'HTML' ? '🌐 Preview available: ' + workDir + '/preview.html' : ''}
125
+
126
+ 💡 Tips:
127
+ - Copy the content above for immediate use
128
+ - Or access the saved file at the path shown
129
+ `);
130
+ ```
131
+
132
+ ### Step 5: 询问后续操作
133
+
134
+ ```javascript
135
+ const nextAction = await AskUserQuestion({
136
+ questions: [
137
+ {
138
+ question: "需要执行什么操作?",
139
+ header: "后续操作",
140
+ multiSelect: false,
141
+ options: [
142
+ { label: "完成", description: "结束格式化流程" },
143
+ { label: "转换为其他格式", description: "选择另一种输出格式" },
144
+ { label: "重新编辑", description: "修改原始内容后重新格式化" }
145
+ ]
146
+ }
147
+ ]
148
+ });
149
+
150
+ if (nextAction["后续操作"] === "转换为其他格式") {
151
+ // 返回 Phase 1 选择新格式
152
+ console.log('请重新运行 /text-formatter 选择其他格式');
153
+ }
154
+ ```
155
+
156
+ ## Output
157
+
158
+ - **File**: `output.{ext}` (最终输出)
159
+ - **File**: `preview.html` (HTML 预览,仅 HTML 格式)
160
+ - **Console**: 统计摘要和格式化内容
161
+
162
+ ## Final Output Structure
163
+
164
+ ```
165
+ {workDir}/
166
+ ├── input-config.json # 配置信息
167
+ ├── analysis.json # 分析结果
168
+ ├── output.md # Markdown 输出(如选择)
169
+ ├── output.bbcode.txt # BBCode 输出(如选择)
170
+ ├── output.html # HTML 输出(如选择)
171
+ └── preview.html # HTML 预览页面
172
+ ```
173
+
174
+ ## Quality Checklist
175
+
176
+ - [ ] 输出文件已保存
177
+ - [ ] 统计信息正确显示
178
+ - [ ] 预览功能可用(HTML)
179
+ - [ ] 用户可访问输出内容
180
+
181
+ ## Completion
182
+
183
+ 此为最终阶段,格式化流程完成。
@@ -0,0 +1,293 @@
1
+ # Callout Types
2
+
3
+ Obsidian 风格的 Callout/Admonition 类型定义和转换规则。
4
+
5
+ ## When to Use
6
+
7
+ | Phase | Usage | Section |
8
+ |-------|-------|---------|
9
+ | Phase 2 | 检测 Callout | Detection patterns |
10
+ | Phase 3 | 格式转换 | Conversion rules |
11
+
12
+ ---
13
+
14
+ ## Callout 语法
15
+
16
+ ### Obsidian 原生语法
17
+
18
+ ```markdown
19
+ > [!TYPE] 可选标题
20
+ > 内容行1
21
+ > 内容行2
22
+ ```
23
+
24
+ ### 支持的类型
25
+
26
+ | Type | Alias | Icon | Color | 用途 |
27
+ |------|-------|------|-------|------|
28
+ | `note` | - | 📝 | blue | 普通提示 |
29
+ | `info` | - | ℹ️ | blue | 信息说明 |
30
+ | `tip` | `hint` | 💡 | green | 技巧提示 |
31
+ | `success` | `check`, `done` | ✅ | green | 成功状态 |
32
+ | `warning` | `caution`, `attention` | ⚠️ | orange | 警告信息 |
33
+ | `danger` | `error` | ❌ | red | 危险/错误 |
34
+ | `bug` | - | 🐛 | red | Bug 说明 |
35
+ | `example` | - | 📋 | purple | 示例内容 |
36
+ | `quote` | `cite` | 💬 | gray | 引用内容 |
37
+ | `abstract` | `summary`, `tldr` | 📄 | cyan | 摘要 |
38
+ | `question` | `help`, `faq` | ❓ | yellow | 问题/FAQ |
39
+ | `todo` | - | 📌 | orange | 待办事项 |
40
+
41
+ ---
42
+
43
+ ## 检测 Pattern
44
+
45
+ ```javascript
46
+ // Callout 检测正则
47
+ const CALLOUT_PATTERN = /^>\s*\[!(\w+)\](?:\s+(.+))?$/;
48
+
49
+ // 检测函数
50
+ function detectCallout(line) {
51
+ const match = line.match(CALLOUT_PATTERN);
52
+ if (match) {
53
+ return {
54
+ type: match[1].toLowerCase(),
55
+ title: match[2] || null
56
+ };
57
+ }
58
+ return null;
59
+ }
60
+
61
+ // 解析完整 Callout 块
62
+ function parseCalloutBlock(lines, startIndex) {
63
+ const firstLine = lines[startIndex];
64
+ const calloutInfo = detectCallout(firstLine);
65
+
66
+ if (!calloutInfo) return null;
67
+
68
+ const content = [];
69
+ let i = startIndex + 1;
70
+
71
+ while (i < lines.length && lines[i].startsWith('>')) {
72
+ content.push(lines[i].replace(/^>\s*/, ''));
73
+ i++;
74
+ }
75
+
76
+ return {
77
+ ...calloutInfo,
78
+ content: content.join('\n'),
79
+ endIndex: i - 1
80
+ };
81
+ }
82
+ ```
83
+
84
+ ---
85
+
86
+ ## 转换规则
87
+
88
+ ### BBCode 转换
89
+
90
+ ```javascript
91
+ const CALLOUT_BBCODE = {
92
+ note: {
93
+ icon: '📝',
94
+ color: '#2196F3',
95
+ label: '注意'
96
+ },
97
+ info: {
98
+ icon: 'ℹ️',
99
+ color: '#2196F3',
100
+ label: '信息'
101
+ },
102
+ tip: {
103
+ icon: '💡',
104
+ color: '#4CAF50',
105
+ label: '提示'
106
+ },
107
+ success: {
108
+ icon: '✅',
109
+ color: '#4CAF50',
110
+ label: '成功'
111
+ },
112
+ warning: {
113
+ icon: '⚠️',
114
+ color: '#FF9800',
115
+ label: '警告'
116
+ },
117
+ danger: {
118
+ icon: '❌',
119
+ color: '#F44336',
120
+ label: '危险'
121
+ },
122
+ bug: {
123
+ icon: '🐛',
124
+ color: '#F44336',
125
+ label: 'Bug'
126
+ },
127
+ example: {
128
+ icon: '📋',
129
+ color: '#9C27B0',
130
+ label: '示例'
131
+ },
132
+ quote: {
133
+ icon: '💬',
134
+ color: '#9E9E9E',
135
+ label: '引用'
136
+ },
137
+ question: {
138
+ icon: '❓',
139
+ color: '#FFEB3B',
140
+ label: '问题'
141
+ }
142
+ };
143
+
144
+ function calloutToBBCode(type, title, content, style = 'forum') {
145
+ const config = CALLOUT_BBCODE[type] || CALLOUT_BBCODE.note;
146
+ const displayTitle = title || config.label;
147
+
148
+ if (style === 'compact') {
149
+ return `[quote][b]${config.icon} ${displayTitle}[/b]
150
+ ${content}[/quote]`;
151
+ }
152
+
153
+ // Forum style - more visual
154
+ return `[quote]
155
+ [color=${config.color}][size=4][b]${config.icon} ${displayTitle}[/b][/size][/color]
156
+
157
+ ${content}
158
+ [/quote]`;
159
+ }
160
+ ```
161
+
162
+ ### HTML 转换
163
+
164
+ ```javascript
165
+ function calloutToHTML(type, title, content) {
166
+ const config = CALLOUT_BBCODE[type] || CALLOUT_BBCODE.note;
167
+ const displayTitle = title || config.label;
168
+
169
+ return `<div class="callout callout-${type}">
170
+ <div class="callout-title">
171
+ <span class="callout-icon">${config.icon}</span>
172
+ <span class="callout-title-text">${displayTitle}</span>
173
+ </div>
174
+ <div class="callout-content">
175
+ ${content}
176
+ </div>
177
+ </div>`;
178
+ }
179
+ ```
180
+
181
+ ### Hybrid 转换
182
+
183
+ ```javascript
184
+ function calloutToHybrid(type, title, content) {
185
+ const config = CALLOUT_BBCODE[type] || CALLOUT_BBCODE.note;
186
+ const displayTitle = title || config.label;
187
+
188
+ // HTML container + BBCode styling + MD content
189
+ return `<div class="callout ${type}">
190
+
191
+ [color=${config.color}][b]${config.icon} ${displayTitle}[/b][/color]
192
+
193
+ ${content}
194
+
195
+ </div>`;
196
+ }
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Callout CSS 样式
202
+
203
+ ```css
204
+ /* Base callout styles */
205
+ .callout {
206
+ padding: 1rem;
207
+ margin: 1rem 0;
208
+ border-left: 4px solid;
209
+ border-radius: 4px;
210
+ background: #f8f9fa;
211
+ }
212
+
213
+ .callout-title {
214
+ display: flex;
215
+ align-items: center;
216
+ gap: 0.5rem;
217
+ font-weight: 600;
218
+ margin-bottom: 0.5rem;
219
+ }
220
+
221
+ .callout-icon {
222
+ font-size: 1.2em;
223
+ }
224
+
225
+ /* Type-specific colors */
226
+ .callout-note, .callout-info {
227
+ border-color: #2196F3;
228
+ background: #E3F2FD;
229
+ }
230
+
231
+ .callout-tip, .callout-success {
232
+ border-color: #4CAF50;
233
+ background: #E8F5E9;
234
+ }
235
+
236
+ .callout-warning {
237
+ border-color: #FF9800;
238
+ background: #FFF3E0;
239
+ }
240
+
241
+ .callout-danger, .callout-bug {
242
+ border-color: #F44336;
243
+ background: #FFEBEE;
244
+ }
245
+
246
+ .callout-example {
247
+ border-color: #9C27B0;
248
+ background: #F3E5F5;
249
+ }
250
+
251
+ .callout-quote {
252
+ border-color: #9E9E9E;
253
+ background: #FAFAFA;
254
+ }
255
+
256
+ .callout-question {
257
+ border-color: #FFC107;
258
+ background: #FFFDE7;
259
+ }
260
+ ```
261
+
262
+ ---
263
+
264
+ ## 折叠 Callout
265
+
266
+ 支持可折叠的 Callout 语法:
267
+
268
+ ```markdown
269
+ > [!NOTE]+ 默认展开
270
+ > 内容
271
+
272
+ > [!NOTE]- 默认折叠
273
+ > 内容
274
+ ```
275
+
276
+ ### BBCode 折叠
277
+
278
+ ```bbcode
279
+ [collapse=📝 注意]
280
+ 内容
281
+ [/collapse]
282
+ ```
283
+
284
+ ### HTML 折叠
285
+
286
+ ```html
287
+ <details class="callout callout-note">
288
+ <summary>📝 注意</summary>
289
+ <div class="callout-content">
290
+ 内容
291
+ </div>
292
+ </details>
293
+ ```