mirror of
https://github.com/JimLiu/baoyu-skills.git
synced 2026-07-10 13:11:30 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c5e81fd15 | |||
| 058d25d02c |
@@ -6,7 +6,7 @@
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Skills shared by Baoyu for improving daily work efficiency",
|
||||
"version": "1.25.2"
|
||||
"version": "1.25.3"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
English | [中文](./CHANGELOG.zh.md)
|
||||
|
||||
## 1.25.3 - 2026-01-28
|
||||
|
||||
### Features
|
||||
- `baoyu-format-markdown`: add content type detection with user confirmation for markdown files; add CJK punctuation handling to move paired punctuation outside emphasis markers.
|
||||
|
||||
## 1.25.2 - 2026-01-28
|
||||
|
||||
### Documentation
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
[English](./CHANGELOG.md) | 中文
|
||||
|
||||
## 1.25.3 - 2026-01-28
|
||||
|
||||
### 新功能
|
||||
- `baoyu-format-markdown`:新增内容类型检测,对已有 markdown 格式的文件提供用户确认选项;新增 CJK 配对标点处理,将括号、引号等标点移出加粗标记外。
|
||||
|
||||
## 1.25.2 - 2026-01-28
|
||||
|
||||
### 文档
|
||||
|
||||
@@ -58,6 +58,55 @@ Claude performs content analysis and formatting (Steps 1-6), then runs the scrip
|
||||
|
||||
Read the user-specified markdown or plain text file.
|
||||
|
||||
### Step 1.5: Detect Content Type & Confirm
|
||||
|
||||
**Content Type Detection:**
|
||||
|
||||
| Indicator | Classification |
|
||||
|-----------|----------------|
|
||||
| Has `---` YAML frontmatter | Markdown |
|
||||
| Has `#`, `##`, `###` headings | Markdown |
|
||||
| Has `**bold**`, `*italic*` | Markdown |
|
||||
| Has `- ` or `1. ` lists | Markdown |
|
||||
| Has ``` code blocks | Markdown |
|
||||
| Has `> ` blockquotes | Markdown |
|
||||
| None of above | Plain text |
|
||||
|
||||
**Decision Flow:**
|
||||
|
||||
┌─────────────────┬────────────────────────────────────────────────┐
|
||||
│ Content Type │ Action │
|
||||
├─────────────────┼────────────────────────────────────────────────┤
|
||||
│ Plain text │ Proceed to Step 2 (format to markdown) │
|
||||
├─────────────────┼────────────────────────────────────────────────┤
|
||||
│ Markdown │ Use AskUserQuestion to confirm optimization │
|
||||
└─────────────────┴────────────────────────────────────────────────┘
|
||||
|
||||
**If Markdown detected, ask user:**
|
||||
|
||||
```
|
||||
Detected existing markdown formatting. What would you like to do?
|
||||
|
||||
1. Optimize formatting (Recommended)
|
||||
- Add/improve frontmatter, headings, bold, lists
|
||||
- Run typography script (spacing, emphasis fixes)
|
||||
- Output: {filename}-formatted.md
|
||||
|
||||
2. Keep original formatting
|
||||
- Preserve existing markdown structure
|
||||
- Run typography script (spacing, emphasis fixes)
|
||||
- Output: {filename}-formatted.md
|
||||
|
||||
3. Typography fixes only
|
||||
- Run typography script on original file in-place
|
||||
- No copy created, modifies original file directly
|
||||
```
|
||||
|
||||
**Based on user choice:**
|
||||
- **Optimize**: Continue to Step 2-8 (full workflow)
|
||||
- **Keep original**: Skip Steps 2-5, copy file → Step 6-8 (run script on copy)
|
||||
- **Typography only**: Skip Steps 2-6, run Step 7 on original file directly
|
||||
|
||||
### Step 2: Analyze Content Structure
|
||||
|
||||
Identify:
|
||||
@@ -138,6 +187,10 @@ Examples:
|
||||
- `final.md` → `final-formatted.md`
|
||||
- `draft-v1.md` → `draft-v1-formatted.md`
|
||||
|
||||
**If user chose "Keep original formatting" (from Step 1.5):**
|
||||
- Copy original file to `{filename}-formatted.md` without modifications
|
||||
- Proceed to Step 7 for typography fixes only
|
||||
|
||||
**Backup existing file:**
|
||||
|
||||
If `{filename}-formatted.md` already exists, backup before overwriting:
|
||||
|
||||
@@ -11,6 +11,21 @@ export const CJK_CHAR_RE = new RegExp(`[${CJK_SCRIPTS}]`, "u");
|
||||
const PUNCT_OR_SYMBOL_RE = /[\p{P}\p{S}]/u;
|
||||
const WORD_CHAR_RE = /[\p{L}\p{N}]/u;
|
||||
|
||||
const CJK_PUNCT_PAIRS: Record<string, string> = {
|
||||
"“": "”",
|
||||
"‘": "’",
|
||||
"(": ")",
|
||||
"〔": "〕",
|
||||
"〖": "〗",
|
||||
"〘": "〙",
|
||||
"〚": "〛",
|
||||
"「": "」",
|
||||
"『": "』",
|
||||
"〈": "〉",
|
||||
"《": "》",
|
||||
"【": "】",
|
||||
};
|
||||
|
||||
function findInlineCodeRanges(text: string): Array<[number, number]> {
|
||||
const ranges: Array<[number, number]> = [];
|
||||
let i = 0;
|
||||
@@ -69,8 +84,120 @@ function isPunctuationOrSymbol(ch: string | undefined): boolean {
|
||||
return !!ch && PUNCT_OR_SYMBOL_RE.test(ch);
|
||||
}
|
||||
|
||||
function fixCjkEmphasisSpacingInBlock(block: string): string {
|
||||
function isMatchingCjkPunct(open: string, close: string): boolean {
|
||||
return CJK_PUNCT_PAIRS[open] === close;
|
||||
}
|
||||
|
||||
function mapNonCodeSegments(
|
||||
block: string,
|
||||
mapper: (segment: string) => string
|
||||
): string {
|
||||
const codeRanges = findInlineCodeRanges(block);
|
||||
if (codeRanges.length === 0) {
|
||||
return mapper(block);
|
||||
}
|
||||
|
||||
let result = "";
|
||||
let lastIndex = 0;
|
||||
for (const [start, end] of codeRanges) {
|
||||
if (start > lastIndex) {
|
||||
result += mapper(block.slice(lastIndex, start));
|
||||
}
|
||||
result += block.slice(start, end + 1);
|
||||
lastIndex = end + 1;
|
||||
}
|
||||
if (lastIndex < block.length) {
|
||||
result += mapper(block.slice(lastIndex));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function moveCjkPunctuationOutsideEmphasis(block: string): string {
|
||||
return mapNonCodeSegments(block, (segment) => {
|
||||
const delimiterPositions: number[] = [];
|
||||
let cursor = 0;
|
||||
while (cursor < segment.length - 1) {
|
||||
if (
|
||||
segment[cursor] === "*" &&
|
||||
segment[cursor + 1] === "*" &&
|
||||
segment[cursor - 1] !== "*" &&
|
||||
segment[cursor + 2] !== "*" &&
|
||||
!isEscaped(segment, cursor)
|
||||
) {
|
||||
delimiterPositions.push(cursor);
|
||||
cursor += 2;
|
||||
continue;
|
||||
}
|
||||
cursor += 1;
|
||||
}
|
||||
|
||||
if (delimiterPositions.length < 2) return segment;
|
||||
|
||||
const stack: number[] = [];
|
||||
const pairs: Array<{ open: number; close: number }> = [];
|
||||
for (const pos of delimiterPositions) {
|
||||
if (stack.length === 0) {
|
||||
stack.push(pos);
|
||||
} else {
|
||||
const open = stack.pop() as number;
|
||||
pairs.push({ open, close: pos });
|
||||
}
|
||||
}
|
||||
|
||||
const skip = new Set<number>();
|
||||
const insertBefore = new Map<number, string>();
|
||||
|
||||
for (const pair of pairs) {
|
||||
const openPunctPos = pair.open + 2;
|
||||
const closePunctPos = pair.close - 1;
|
||||
if (openPunctPos >= closePunctPos) continue;
|
||||
|
||||
const openPunct = segment[openPunctPos];
|
||||
const closePunct = segment[closePunctPos];
|
||||
if (!openPunct || !closePunct) continue;
|
||||
if (!CJK_OPENING_PUNCT_RE.test(openPunct)) continue;
|
||||
if (!CJK_CLOSING_PUNCT_RE.test(closePunct)) continue;
|
||||
if (!isMatchingCjkPunct(openPunct, closePunct)) continue;
|
||||
if (openPunctPos + 1 >= closePunctPos) continue;
|
||||
|
||||
const inner = segment.slice(openPunctPos + 1, closePunctPos);
|
||||
if (inner.length === 0) continue;
|
||||
|
||||
skip.add(openPunctPos);
|
||||
skip.add(closePunctPos);
|
||||
|
||||
insertBefore.set(pair.open, (insertBefore.get(pair.open) ?? "") + openPunct);
|
||||
const afterClose = pair.close + 2;
|
||||
insertBefore.set(
|
||||
afterClose,
|
||||
(insertBefore.get(afterClose) ?? "") + closePunct
|
||||
);
|
||||
}
|
||||
|
||||
if (skip.size === 0) return segment;
|
||||
|
||||
let result = "";
|
||||
for (let idx = 0; idx < segment.length; idx += 1) {
|
||||
const insert = insertBefore.get(idx);
|
||||
if (insert) {
|
||||
result += insert;
|
||||
}
|
||||
if (skip.has(idx)) {
|
||||
continue;
|
||||
}
|
||||
result += segment[idx];
|
||||
}
|
||||
const tailInsert = insertBefore.get(segment.length);
|
||||
if (tailInsert) {
|
||||
result += tailInsert;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
function fixCjkEmphasisSpacingInBlock(block: string): string {
|
||||
const normalized = moveCjkPunctuationOutsideEmphasis(block);
|
||||
const codeRanges = findInlineCodeRanges(normalized);
|
||||
let rangeIndex = 0;
|
||||
|
||||
const delimiters: Array<{
|
||||
@@ -79,7 +206,7 @@ function fixCjkEmphasisSpacingInBlock(block: string): string {
|
||||
canClose: boolean;
|
||||
}> = [];
|
||||
let cursor = 0;
|
||||
while (cursor < block.length - 1) {
|
||||
while (cursor < normalized.length - 1) {
|
||||
if (rangeIndex < codeRanges.length && cursor >= codeRanges[rangeIndex][0]) {
|
||||
if (cursor <= codeRanges[rangeIndex][1]) {
|
||||
cursor = codeRanges[rangeIndex][1] + 1;
|
||||
@@ -90,14 +217,14 @@ function fixCjkEmphasisSpacingInBlock(block: string): string {
|
||||
}
|
||||
|
||||
if (
|
||||
block[cursor] === "*" &&
|
||||
block[cursor + 1] === "*" &&
|
||||
block[cursor - 1] !== "*" &&
|
||||
block[cursor + 2] !== "*" &&
|
||||
!isEscaped(block, cursor)
|
||||
normalized[cursor] === "*" &&
|
||||
normalized[cursor + 1] === "*" &&
|
||||
normalized[cursor - 1] !== "*" &&
|
||||
normalized[cursor + 2] !== "*" &&
|
||||
!isEscaped(normalized, cursor)
|
||||
) {
|
||||
const before = block[cursor - 1];
|
||||
const after = block[cursor + 2];
|
||||
const before = normalized[cursor - 1];
|
||||
const after = normalized[cursor + 2];
|
||||
const beforeIsSpace = isWhitespaceChar(before);
|
||||
const afterIsSpace = isWhitespaceChar(after);
|
||||
const beforeIsPunct = isPunctuationOrSymbol(before);
|
||||
@@ -140,12 +267,12 @@ function fixCjkEmphasisSpacingInBlock(block: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
if (pairs.length === 0) return block;
|
||||
if (pairs.length === 0) return normalized;
|
||||
|
||||
const insertPositions = new Set<number>();
|
||||
for (const pair of pairs) {
|
||||
const insideLast = block[pair.close - 1];
|
||||
const afterClose = block[pair.close + 2];
|
||||
const insideLast = normalized[pair.close - 1];
|
||||
const afterClose = normalized[pair.close + 2];
|
||||
if (!afterClose) continue;
|
||||
if (
|
||||
CJK_CLOSING_PUNCT_RE.test(insideLast) &&
|
||||
@@ -155,16 +282,16 @@ function fixCjkEmphasisSpacingInBlock(block: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
if (insertPositions.size === 0) return block;
|
||||
if (insertPositions.size === 0) return normalized;
|
||||
|
||||
let result = "";
|
||||
for (let idx = 0; idx < block.length; idx += 1) {
|
||||
for (let idx = 0; idx < normalized.length; idx += 1) {
|
||||
if (insertPositions.has(idx)) {
|
||||
result += " ";
|
||||
}
|
||||
result += block[idx];
|
||||
result += normalized[idx];
|
||||
}
|
||||
if (insertPositions.has(block.length)) {
|
||||
if (insertPositions.has(normalized.length)) {
|
||||
result += " ";
|
||||
}
|
||||
return result;
|
||||
|
||||
Reference in New Issue
Block a user