Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@

* correct SVG attribute casing in Logo component ([01a213c](https://github.com/nicepkg/ctxport/commit/01a213c69eb7980fb8d0b48c2c879b7069a47039))
* correct zip output path in release workflow ([56be20f](https://github.com/nicepkg/ctxport/commit/56be20f2c67834f5128711c2b393988638aaf792))
* **doubao:** fix copy button not appearing due to fragile DOM selector ([ab1f1d0](../../commit/ab1f1d0))
* **doubao:** fix heading level conflict — content headings now dynamically demoted to nest below `## User`/`## Assistant` section headers ([ab1f1d0](../../commit/ab1f1d0))
16 changes: 15 additions & 1 deletion apps/browser-extension/src/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,14 @@ export default function App() {
if (!plugin) return;

if (plugin.injector) {
let copyButtonRendered = false;

plugin.injector.inject(
{ url, document },
{
renderCopyButton: (container) => {
copyButtonRendered = true;
setShowFloatingCopy(false);
const root = createRoot(container);
root.render(<CopyButton onToast={showToast} />);
},
Expand All @@ -53,7 +57,17 @@ export default function App() {
},
);

cleanupRef.current = () => plugin.injector?.cleanup();
// Safety net: if copy button isn't injected after 8s, show floating button
const fallbackTimer = setTimeout(() => {
if (!copyButtonRendered) {
setShowFloatingCopy(true);
}
}, 8000);

cleanupRef.current = () => {
clearTimeout(fallbackTimer);
plugin.injector?.cleanup();
};
} else {
// No injector — show floating copy button as fallback
setShowFloatingCopy(true);
Expand Down
49 changes: 49 additions & 0 deletions docs/others/doubao-heading-demotion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# 豆包 Heading 降级策略

## 问题

豆包 AI 的回答中常包含 Markdown 标题(如 `# 解答`、`### 证明`、`#### 步骤1`),而 CtxPort 输出格式使用 `## User` / `## Assistant`(二级标题)作为会话角色分隔符。

旧方案盲降一级:`# → ##`,导致原本的一级标题降为二级后与 `## Assistant` 同级,文档层级错乱。

## 策略

**动态检测 + 精确降级**:先扫描内容找到最高级别标题,计算需要降多少级才能让所有内容标题严格嵌套在 `##` 下方。

```
内容最高标题 降级量 效果
──────────────────────────────────────────
# (一级) +2 # → ###, ## → ####, ### → #####
## (二级) +1 ## → ###, ### → ####
### (三级及以上) 0 不变
```

核心实现位于 `packages/core-plugins/src/plugins/doubao/plugin.ts`:

1. `findMinHeadingLevel(text)` — 扫描非代码块区域,返回最高(数字最小)标题级别
2. `demoteHeadings(text)` — 计算 `shift = SECTION_LEVEL + 1 - minLevel`,对所有 ATX 标题应用偏移
3. 代码块(fenced code block)内的 `#` 字符不受影响

## 示例

输入:
```markdown
# 💡解答
### 证明:
已知条件...

#### 步骤1:选取合适的 ε
```

输出(在 `## Assistant` 下方):
```markdown
## Assistant

### 💡解答
##### 证明:
已知条件...

###### 步骤1:选取合适的 ε
```

所有内容标题从 `###` 开始,正确嵌套在二级标题下。
Loading
Loading