@wsxjs/wsx-press 0.0.25 → 0.0.27

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.
@@ -135,7 +135,8 @@ export default class DocTOC extends LightComponent {
135
135
  }
136
136
 
137
137
  /**
138
- * 为文档中的标题元素设置 ID
138
+ * 收集文档中的标题元素(使用已有的 ID
139
+ * 注意:不再重新设置 ID,而是使用 Heading 组件已经设置好的 ID
139
140
  */
140
141
  private setupHeadingIds(): void {
141
142
  const docContent = document.querySelector(".doc-content");
@@ -143,36 +144,52 @@ export default class DocTOC extends LightComponent {
143
144
  return;
144
145
  }
145
146
 
146
- // 查找所有标题元素(包括 wsx-marked-heading 和标准 h1-h6)
147
- const headings = docContent.querySelectorAll("h1, h2, h3, h4, h5, h6, wsx-marked-heading");
148
147
  this.headingElements.clear();
149
148
 
150
- headings.forEach((heading) => {
151
- // 获取标题文本
152
- const text = heading.textContent?.trim() || "";
153
- if (!text) return;
154
-
155
- // 生成 ID
156
- const id = this.generateId(text);
149
+ // 查找 wsx-marked-heading 组件内的标题元素(它们已经有正确的 ID)
150
+ const markedHeadings = docContent.querySelectorAll("wsx-marked-heading");
151
+ markedHeadings.forEach((wrapper) => {
152
+ // 获取内部的 h1-h6 元素(Heading 组件渲染的带 ID 的元素)
153
+ const heading = wrapper.querySelector("h1, h2, h3, h4, h5, h6");
154
+ if (heading instanceof HTMLElement && heading.id) {
155
+ this.headingElements.set(heading.id, heading);
156
+ }
157
+ });
157
158
 
158
- // 设置 ID
159
+ // 也支持直接的 h1-h6 元素(非 wsx-marked-heading 的情况)
160
+ const directHeadings = docContent.querySelectorAll(
161
+ ":scope > h1, :scope > h2, :scope > h3, :scope > h4, :scope > h5, :scope > h6"
162
+ );
163
+ directHeadings.forEach((heading) => {
159
164
  if (heading instanceof HTMLElement) {
160
- heading.id = id;
161
- this.headingElements.set(id, heading);
165
+ // 如果已经有 ID,直接使用
166
+ if (heading.id) {
167
+ this.headingElements.set(heading.id, heading);
168
+ } else {
169
+ // 否则生成 ID 并设置
170
+ const text = heading.textContent?.trim() || "";
171
+ if (text) {
172
+ const id = this.generateId(text);
173
+ heading.id = id;
174
+ this.headingElements.set(id, heading);
175
+ }
176
+ }
162
177
  }
163
178
  });
164
179
  }
165
180
 
166
181
  /**
167
182
  * 生成锚点 ID
183
+ * 必须与服务端 toc.ts 和 marked-utils.ts 中的 generateId 保持一致
184
+ * 保留中文等 Unicode 字符,只移除特殊符号
168
185
  */
169
186
  private generateId(text: string): string {
170
187
  return text
171
188
  .toLowerCase()
172
- .replace(/[^\w\s-]/g, "")
173
- .replace(/\s+/g, "-")
174
- .replace(/-+/g, "-")
175
- .trim();
189
+ .replace(/\s+/g, "-") // 空格转连字符
190
+ .replace(/[^\p{L}\p{N}-]/gu, "") // 保留字母、数字、连字符(Unicode-aware)
191
+ .replace(/-+/g, "-") // 合并多个连字符
192
+ .replace(/^-+|-+$/g, ""); // 移除首尾连字符
176
193
  }
177
194
 
178
195
  /**
@@ -214,11 +231,21 @@ export default class DocTOC extends LightComponent {
214
231
  */
215
232
  private handleTOCClick = (id: string, e: Event): void => {
216
233
  e.preventDefault();
217
- const element = this.headingElements.get(id);
234
+ // 优先从缓存中获取,如果没有则通过 DOM 查找
235
+ let element = this.headingElements.get(id);
236
+ if (!element) {
237
+ element = document.getElementById(id) as HTMLElement | null;
238
+ if (element) {
239
+ // 缓存找到的元素
240
+ this.headingElements.set(id, element);
241
+ }
242
+ }
218
243
  if (element) {
219
244
  element.scrollIntoView({ behavior: "smooth", block: "start" });
220
245
  // 更新 URL hash(不触发滚动)
221
246
  window.history.replaceState(null, "", `#${id}`);
247
+ } else {
248
+ logger.warn(`Heading element not found for id: ${id}`);
222
249
  }
223
250
  };
224
251
 
package/src/node/toc.ts CHANGED
@@ -64,7 +64,7 @@ export function extractTOCFromMarkdown(markdown: string): TOCItem[] {
64
64
 
65
65
  const item: TOCItem = {
66
66
  level,
67
- text,
67
+ text: text.trim(),
68
68
  id,
69
69
  children: [],
70
70
  };
@@ -112,14 +112,15 @@ function extractTextFromTokens(tokens: Token[]): string {
112
112
 
113
113
  /**
114
114
  * 生成锚点 ID
115
+ * 保留中文等 Unicode 字符,只移除特殊符号
115
116
  */
116
117
  function generateId(text: string): string {
117
118
  return text
118
119
  .toLowerCase()
119
- .replace(/[^\w\s-]/g, "")
120
- .replace(/\s+/g, "-")
121
- .replace(/-+/g, "-")
122
- .trim();
120
+ .replace(/\s+/g, "-") // 空格转连字符
121
+ .replace(/[^\p{L}\p{N}-]/gu, "") // 保留字母、数字、连字符(Unicode-aware)
122
+ .replace(/-+/g, "-") // 合并多个连字符
123
+ .replace(/^-+|-+$/g, ""); // 移除首尾连字符
123
124
  }
124
125
 
125
126
  /**