dsh-plugin-lookatstudy 0.25.1 → 0.25.3
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.
- package/lib/client.js +292 -6
- package/lib/client.js.map +1 -1
- package/lib/index.mjs +51 -9
- package/package.json +1 -1
package/lib/index.mjs
CHANGED
|
@@ -1130,6 +1130,21 @@ function normalizeSpeechText(md) {
|
|
|
1130
1130
|
s = s.replace(/`[^`\n]*`/g, "");
|
|
1131
1131
|
s = s.replace(/!\[[^\]]*\]\([^)\n]*\)/g, "");
|
|
1132
1132
|
s = s.replace(/\[([^\]]+)\]\([^)\n]*\)/g, "$1");
|
|
1133
|
+
s = s.replace(/\[\]\([^)\n]*\)/g, "");
|
|
1134
|
+
s = s.replace(/<br\s*\/?>/gi, "\n");
|
|
1135
|
+
s = s.replace(/<https?:\/\/[^>\s]+>/g, "");
|
|
1136
|
+
s = s.replace(/https?:\/\/[^\s)\]]+/g, "");
|
|
1137
|
+
s = s.replace(/<(script|style)\b[^>]*>[\s\S]*?<\/\1>/gi, "\n");
|
|
1138
|
+
s = s.replace(/<[^>\n]+>/g, "");
|
|
1139
|
+
s = s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, "\"").replace(/�?39;/g, "'").replace(/ /g, " ").replace(/&#(\d+);/g, (_m, d) => {
|
|
1140
|
+
try {
|
|
1141
|
+
return String.fromCodePoint(Number(d));
|
|
1142
|
+
} catch {
|
|
1143
|
+
return "";
|
|
1144
|
+
}
|
|
1145
|
+
}).replace(/&[a-z]+;/gi, "");
|
|
1146
|
+
s = s.replace(/\[([^\]]+)\]\[[^\]]*\]/g, "$1");
|
|
1147
|
+
s = s.replace(/\[\^[^\]]*\]/g, "");
|
|
1133
1148
|
s = s.replace(/^[ \t]{0,3}#{1,6}[ \t]+/gm, "");
|
|
1134
1149
|
s = s.replace(/^[ \t]*(?:[-*+]|\d+\.)[ \t]+/gm, "");
|
|
1135
1150
|
s = s.replace(/^[ \t]*>[ \t]?/gm, "");
|
|
@@ -3122,6 +3137,7 @@ function workbenchState(state, now) {
|
|
|
3122
3137
|
speechText: normalizeSpeechText(normalizeMathNotation(ref.lesson.body)),
|
|
3123
3138
|
...ref.lesson.translation !== void 0 ? {
|
|
3124
3139
|
translationHtml: renderMarkdown(normalizeMathNotation(ref.lesson.translation)),
|
|
3140
|
+
translationSpeechText: normalizeSpeechText(normalizeMathNotation(ref.lesson.translation)),
|
|
3125
3141
|
translationLang: ref.lesson.translationLang ?? ""
|
|
3126
3142
|
} : {}
|
|
3127
3143
|
};
|
|
@@ -6582,12 +6598,19 @@ function validateDesign(design, validFiles) {
|
|
|
6582
6598
|
};
|
|
6583
6599
|
}
|
|
6584
6600
|
/**
|
|
6585
|
-
* Heading similarity
|
|
6586
|
-
*
|
|
6587
|
-
*
|
|
6588
|
-
*
|
|
6589
|
-
*
|
|
6590
|
-
*
|
|
6601
|
+
* Heading similarity — the translation misalignment guard. Ported from
|
|
6602
|
+
* upstream import-pipeline.ts (their audit F16, 2026-09-13): ordinal
|
|
6603
|
+
* alignment assumes both heading lists share structure; a machine-translated
|
|
6604
|
+
* file with a dropped header shifts every ordinal, and A's translation would
|
|
6605
|
+
* silently land on B's node — overlap too low skips the segment (宁缺毋错).
|
|
6606
|
+
* Empty titles cannot be checked and pass conservatively.
|
|
6607
|
+
*
|
|
6608
|
+
* 0.25.2 (owner real-import catch): upstream's word overlap normalizes on
|
|
6609
|
+
* whitespace, and a CJK title is ONE token — paraphrased Chinese headings
|
|
6610
|
+
* (课程总览与环境搭建指南 vs 课程总览) could never overlap and the guard
|
|
6611
|
+
* skipped every non-verbatim segment (38/110 paired on the owner's import;
|
|
6612
|
+
* the 38 were exact matches). CJK strings now compare by character
|
|
6613
|
+
* containment first, then character-bigram overlap ≥ 0.3.
|
|
6591
6614
|
*/
|
|
6592
6615
|
function headingsSimilar(a, b) {
|
|
6593
6616
|
const norm = (s) => s.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim().split(/\s+/).filter(Boolean);
|
|
@@ -6595,6 +6618,24 @@ function headingsSimilar(a, b) {
|
|
|
6595
6618
|
const B = norm(b);
|
|
6596
6619
|
if (A.length === 0 || B.length === 0) return true;
|
|
6597
6620
|
if (A.join(" ") === B.join(" ")) return true;
|
|
6621
|
+
const han = (s) => s.toLowerCase().replace(/[^\p{Script=Han}\p{N}]/gu, "");
|
|
6622
|
+
const ca = han(a);
|
|
6623
|
+
const cb = han(b);
|
|
6624
|
+
if (ca.length >= 2 || cb.length >= 2) {
|
|
6625
|
+
if (ca === "" || cb === "") return true;
|
|
6626
|
+
if (ca.includes(cb) || cb.includes(ca)) return true;
|
|
6627
|
+
const bigramsOf = (s) => {
|
|
6628
|
+
const out = /* @__PURE__ */ new Set();
|
|
6629
|
+
const chars = Array.from(s);
|
|
6630
|
+
for (let i = 0; i + 1 < chars.length; i++) out.add(chars[i] + chars[i + 1]);
|
|
6631
|
+
return out;
|
|
6632
|
+
};
|
|
6633
|
+
const ba = bigramsOf(ca);
|
|
6634
|
+
const bb = bigramsOf(cb);
|
|
6635
|
+
let hit = 0;
|
|
6636
|
+
for (const g of ba) if (bb.has(g)) hit++;
|
|
6637
|
+
return hit / Math.max(1, ba.size) >= .3;
|
|
6638
|
+
}
|
|
6598
6639
|
const setB = new Set(B);
|
|
6599
6640
|
return A.filter((w) => setB.has(w)).length / A.length >= .3;
|
|
6600
6641
|
}
|
|
@@ -6675,7 +6716,8 @@ function buildCourseFromDesign(courseTitle, validated, contents, translations) {
|
|
|
6675
6716
|
let tIndex = titleIndex;
|
|
6676
6717
|
let keep = true;
|
|
6677
6718
|
if (lesson.anchor === null || titleIndex === -1) tIndex = -1;
|
|
6678
|
-
else if (titleIndex >= tHeadings.length
|
|
6719
|
+
else if (titleIndex >= tHeadings.length) keep = false;
|
|
6720
|
+
else if (!(tHeadings.length === headings.length && tHeadings.every((h, i) => h.level === headings[i].level)) && !headingsSimilar(lesson.title, tHeadings[titleIndex].title)) keep = false;
|
|
6679
6721
|
if (keep) translation = replaceImagesByPosition(sliceLessonBody(pair.content, tHeadings, tIndex, isFirst), originalImagesOf(body));
|
|
6680
6722
|
}
|
|
6681
6723
|
return {
|
|
@@ -7739,7 +7781,7 @@ ${p.text}`));
|
|
|
7739
7781
|
translations: {
|
|
7740
7782
|
type: "integer",
|
|
7741
7783
|
required: true,
|
|
7742
|
-
description: "Lessons that landed with a paired translation (the interface-language mirror existed and its anchor matched) — their teach tab offers
|
|
7784
|
+
description: "Lessons that landed with a paired translation (the interface-language mirror existed and its anchor matched) — their teach tab offers 原文/译文."
|
|
7743
7785
|
},
|
|
7744
7786
|
created: {
|
|
7745
7787
|
type: "boolean",
|
|
@@ -7754,7 +7796,7 @@ ${p.text}`));
|
|
|
7754
7796
|
},
|
|
7755
7797
|
render: (_args, value) => [{
|
|
7756
7798
|
type: "text",
|
|
7757
|
-
text: value.created === false ? `“${value.title}” (id ${value.courseId}) was ALREADY imported — the design did not create a new course. Continue with the existing course, or delete it first (study_delete_course) to rebuild from this design.` : `Imported designed course “${value.title}” (${value.sections} sections, ${value.lessons} lessons${value.droppedLessons > 0 ? `, ${value.droppedLessons} hallucinated lesson(s) dropped` : ""})${typeof value.translations === "number" && value.translations > 0 ? ` — ${value.translations} lesson(s) carry the learner's-language translation (
|
|
7799
|
+
text: value.created === false ? `“${value.title}” (id ${value.courseId}) was ALREADY imported — the design did not create a new course. Continue with the existing course, or delete it first (study_delete_course) to rebuild from this design.` : `Imported designed course “${value.title}” (${value.sections} sections, ${value.lessons} lessons${value.droppedLessons > 0 ? `, ${value.droppedLessons} hallucinated lesson(s) dropped` : ""})${typeof value.translations === "number" && value.translations > 0 ? ` — ${value.translations} lesson(s) carry the learner's-language translation (原文/译文 in the teach tab)` : ""}${typeof value.languageTarget === "string" ? ` — language course (${value.languageTarget}): keep reading passages, example sentences, and quiz language material in the target language` : ""}. First lesson: “${value.firstLessonTitle}” (id ${value.firstLessonId}). Present the course map to the learner.`
|
|
7758
7800
|
}]
|
|
7759
7801
|
},
|
|
7760
7802
|
async execute(args, exec) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-lookatstudy",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.3",
|
|
4
4
|
"packageManager": "pnpm@11.7.0",
|
|
5
5
|
"description": "Turn any markdown, local folder, or GitHub learning repo into a guided course inside DeepSeek Harness: gated skill-tree progression, BKT mastery tracking, SM-2 spaced repetition.",
|
|
6
6
|
"type": "module",
|