@bendyline/squisq 2.8.0 → 2.9.0
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/dist/{chunk-KEAU5NEM.js → chunk-HEYR3TPL.js} +1 -1
- package/dist/{chunk-PTK2SW2V.js → chunk-KIAEJDZB.js} +271 -3
- package/dist/{chunk-V6NV4GDE.js → chunk-W2GIFNLN.js} +1890 -1796
- package/dist/doc/index.d.ts +81 -3
- package/dist/doc/index.js +10 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +11 -3
- package/dist/narration/index.js +2 -2
- package/package.json +1 -1
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
templateRegistry,
|
|
31
31
|
writeCustomTemplatesToFrontmatter,
|
|
32
32
|
writeCustomThemesToFrontmatter
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-W2GIFNLN.js";
|
|
34
34
|
import {
|
|
35
35
|
iconMarker
|
|
36
36
|
} from "./chunk-7ZAAICW4.js";
|
|
@@ -3286,8 +3286,8 @@ function dedupeLeadingTitleBlock(candidates, titleText) {
|
|
|
3286
3286
|
if (!firstTitle || firstTitle !== titleText.trim().toLowerCase()) return candidates;
|
|
3287
3287
|
const template = first.template;
|
|
3288
3288
|
const titleShaped = template === "title" || template === "sectionHeader" || template === "cover";
|
|
3289
|
-
const
|
|
3290
|
-
return titleShaped || !
|
|
3289
|
+
const hasBody2 = Array.isArray(first.contents) && first.contents.length > 0;
|
|
3290
|
+
return titleShaped || !hasBody2 ? candidates.slice(1) : candidates;
|
|
3291
3291
|
}
|
|
3292
3292
|
function buildTitleLayers(titleText, bandViewport, slot, theme, canvasViewport, margin) {
|
|
3293
3293
|
const context = createTemplateContext(theme, 0, 1, canvasViewport);
|
|
@@ -3632,6 +3632,272 @@ function composeDashboardLayers(materialization) {
|
|
|
3632
3632
|
return layers;
|
|
3633
3633
|
}
|
|
3634
3634
|
|
|
3635
|
+
// src/doc/flashcards/materializeFlashcards.ts
|
|
3636
|
+
var GENERIC_ANSWER_TITLES = /* @__PURE__ */ new Set([
|
|
3637
|
+
"answer",
|
|
3638
|
+
"back",
|
|
3639
|
+
"solution",
|
|
3640
|
+
"explanation",
|
|
3641
|
+
"correct answer",
|
|
3642
|
+
"correct",
|
|
3643
|
+
"distractor",
|
|
3644
|
+
"option"
|
|
3645
|
+
]);
|
|
3646
|
+
function normalizeMarker(value) {
|
|
3647
|
+
return typeof value === "string" ? value.trim().toLowerCase().replace(/[\s_-]+/g, "") : "";
|
|
3648
|
+
}
|
|
3649
|
+
function markerFromValue(value) {
|
|
3650
|
+
const normalized = normalizeMarker(value);
|
|
3651
|
+
if (normalized === "flashcard" || normalized === "card" || normalized === "basicflashcard") {
|
|
3652
|
+
return "basic";
|
|
3653
|
+
}
|
|
3654
|
+
if (normalized === "multiplechoiceflashcard" || normalized === "multiplechoice" || normalized === "quiz") {
|
|
3655
|
+
return "multiple-choice";
|
|
3656
|
+
}
|
|
3657
|
+
if (normalized === "group" || normalized === "flashcardgroup" || normalized === "deck") {
|
|
3658
|
+
return "group";
|
|
3659
|
+
}
|
|
3660
|
+
return void 0;
|
|
3661
|
+
}
|
|
3662
|
+
function resolveFlashcardMarker(block) {
|
|
3663
|
+
const metadataMarker = markerFromValue(block.metadata?.study) ?? markerFromValue(block.metadata?.["study-mode"]);
|
|
3664
|
+
if (metadataMarker) return metadataMarker;
|
|
3665
|
+
for (const className of block.classes ?? []) {
|
|
3666
|
+
const classMarker = markerFromValue(className);
|
|
3667
|
+
if (classMarker) return classMarker;
|
|
3668
|
+
}
|
|
3669
|
+
return markerFromValue(block.template);
|
|
3670
|
+
}
|
|
3671
|
+
function hasTitle(block) {
|
|
3672
|
+
return typeof block.title === "string" && block.title.trim().length > 0;
|
|
3673
|
+
}
|
|
3674
|
+
function hasBody(block) {
|
|
3675
|
+
return (block.contents?.length ?? 0) > 0 || (block.media?.length ?? 0) > 0 || (block.layers?.length ?? 0) > 0;
|
|
3676
|
+
}
|
|
3677
|
+
function hasOwnContent(block) {
|
|
3678
|
+
return hasTitle(block) || hasBody(block);
|
|
3679
|
+
}
|
|
3680
|
+
function isGenericAnswerBlock(block) {
|
|
3681
|
+
return hasTitle(block) && GENERIC_ANSWER_TITLES.has(block.title.trim().toLowerCase());
|
|
3682
|
+
}
|
|
3683
|
+
function selfOnly(block) {
|
|
3684
|
+
const { children: _children, ...self } = block;
|
|
3685
|
+
return self;
|
|
3686
|
+
}
|
|
3687
|
+
function titleOnly(block) {
|
|
3688
|
+
const {
|
|
3689
|
+
children: _children,
|
|
3690
|
+
contents: _contents,
|
|
3691
|
+
media: _media,
|
|
3692
|
+
layers: _layers,
|
|
3693
|
+
...title2
|
|
3694
|
+
} = block;
|
|
3695
|
+
return title2;
|
|
3696
|
+
}
|
|
3697
|
+
function bodyOnly(block) {
|
|
3698
|
+
const { children: _children, title: _title, ...body } = block;
|
|
3699
|
+
return body;
|
|
3700
|
+
}
|
|
3701
|
+
function face(...blocks) {
|
|
3702
|
+
return { blocks };
|
|
3703
|
+
}
|
|
3704
|
+
function isTrue(value) {
|
|
3705
|
+
if (value === true) return true;
|
|
3706
|
+
if (typeof value !== "string") return false;
|
|
3707
|
+
return ["true", "yes", "on", "1", "correct"].includes(value.trim().toLowerCase());
|
|
3708
|
+
}
|
|
3709
|
+
function isCorrectChoice(block) {
|
|
3710
|
+
return isTrue(block.metadata?.correct) || isTrue(block.metadata?.answer) || (block.classes ?? []).some((className) => normalizeMarker(className) === "correct");
|
|
3711
|
+
}
|
|
3712
|
+
function looksLikeCompleteCard(block) {
|
|
3713
|
+
const marker = resolveFlashcardMarker(block);
|
|
3714
|
+
if (marker === "group") return false;
|
|
3715
|
+
if (marker === "basic" || marker === "multiple-choice") return true;
|
|
3716
|
+
if (isGenericAnswerBlock(block)) return false;
|
|
3717
|
+
const children = block.children ?? [];
|
|
3718
|
+
if (children.length === 0) return hasTitle(block) && hasBody(block);
|
|
3719
|
+
if (children.length === 1) {
|
|
3720
|
+
return hasOwnContent(block) && hasOwnContent(children[0]);
|
|
3721
|
+
}
|
|
3722
|
+
return children.every(hasOwnContent);
|
|
3723
|
+
}
|
|
3724
|
+
function hasExplicitCardDescendant(block) {
|
|
3725
|
+
for (const child of block.children ?? []) {
|
|
3726
|
+
const marker = resolveFlashcardMarker(child);
|
|
3727
|
+
if (marker === "basic" || marker === "multiple-choice") return true;
|
|
3728
|
+
if (hasExplicitCardDescendant(child)) return true;
|
|
3729
|
+
}
|
|
3730
|
+
return false;
|
|
3731
|
+
}
|
|
3732
|
+
function shouldTreatAsImplicitGroup(block) {
|
|
3733
|
+
const children = block.children ?? [];
|
|
3734
|
+
if (children.length === 0) return false;
|
|
3735
|
+
if (hasExplicitCardDescendant(block)) return true;
|
|
3736
|
+
if (hasBody(block)) return false;
|
|
3737
|
+
if (children.length === 1) {
|
|
3738
|
+
return looksLikeCompleteCard(children[0]) && !isGenericAnswerBlock(children[0]);
|
|
3739
|
+
}
|
|
3740
|
+
return children.filter(looksLikeCompleteCard).length >= 2;
|
|
3741
|
+
}
|
|
3742
|
+
function explanationFor(block) {
|
|
3743
|
+
return hasBody(block) ? face(bodyOnly(block)) : void 0;
|
|
3744
|
+
}
|
|
3745
|
+
function materializeBasic(block, diagnostics) {
|
|
3746
|
+
const children = block.children ?? [];
|
|
3747
|
+
let front;
|
|
3748
|
+
let back;
|
|
3749
|
+
let label;
|
|
3750
|
+
let explanation;
|
|
3751
|
+
if (children.length === 0) {
|
|
3752
|
+
front = hasTitle(block) ? face(titleOnly(block)) : face();
|
|
3753
|
+
back = hasBody(block) ? face(bodyOnly(block)) : face();
|
|
3754
|
+
} else if (children.length === 1) {
|
|
3755
|
+
front = hasOwnContent(block) ? face(selfOnly(block)) : face();
|
|
3756
|
+
back = hasOwnContent(children[0]) ? face(children[0]) : face();
|
|
3757
|
+
} else {
|
|
3758
|
+
front = hasOwnContent(children[0]) ? face(children[0]) : face();
|
|
3759
|
+
back = face(...children.slice(1).filter(hasOwnContent));
|
|
3760
|
+
label = hasTitle(block) ? block.title.trim() : void 0;
|
|
3761
|
+
explanation = explanationFor(block);
|
|
3762
|
+
}
|
|
3763
|
+
if (front.blocks.length === 0) {
|
|
3764
|
+
diagnostics.push({
|
|
3765
|
+
severity: "error",
|
|
3766
|
+
code: "empty-front",
|
|
3767
|
+
message: `Flashcard "${block.id}" has no content for its front`,
|
|
3768
|
+
blockId: block.id
|
|
3769
|
+
});
|
|
3770
|
+
}
|
|
3771
|
+
if (back.blocks.length === 0) {
|
|
3772
|
+
diagnostics.push({
|
|
3773
|
+
severity: "error",
|
|
3774
|
+
code: "empty-back",
|
|
3775
|
+
message: `Flashcard "${block.id}" has no content for its answer`,
|
|
3776
|
+
blockId: block.id
|
|
3777
|
+
});
|
|
3778
|
+
}
|
|
3779
|
+
if (front.blocks.length === 0 || back.blocks.length === 0) return void 0;
|
|
3780
|
+
return {
|
|
3781
|
+
id: block.id,
|
|
3782
|
+
sourceBlockId: block.id,
|
|
3783
|
+
kind: "basic",
|
|
3784
|
+
...label ? { label } : {},
|
|
3785
|
+
front,
|
|
3786
|
+
back,
|
|
3787
|
+
...explanation ? { explanation } : {}
|
|
3788
|
+
};
|
|
3789
|
+
}
|
|
3790
|
+
function materializeMultipleChoice(block, diagnostics) {
|
|
3791
|
+
const children = block.children ?? [];
|
|
3792
|
+
const question = children[0];
|
|
3793
|
+
const answerBlocks = children.slice(1).filter(hasOwnContent);
|
|
3794
|
+
if (!question || !hasOwnContent(question)) {
|
|
3795
|
+
diagnostics.push({
|
|
3796
|
+
severity: "error",
|
|
3797
|
+
code: "empty-front",
|
|
3798
|
+
message: `Multiple-choice flashcard "${block.id}" has no question block`,
|
|
3799
|
+
blockId: block.id
|
|
3800
|
+
});
|
|
3801
|
+
}
|
|
3802
|
+
if (answerBlocks.length === 0) {
|
|
3803
|
+
diagnostics.push({
|
|
3804
|
+
severity: "error",
|
|
3805
|
+
code: "empty-back",
|
|
3806
|
+
message: `Multiple-choice flashcard "${block.id}" has no answer choices`,
|
|
3807
|
+
blockId: block.id
|
|
3808
|
+
});
|
|
3809
|
+
}
|
|
3810
|
+
if (!question || !hasOwnContent(question) || answerBlocks.length === 0) return void 0;
|
|
3811
|
+
const explicitlyCorrect = answerBlocks.filter(isCorrectChoice);
|
|
3812
|
+
if (explicitlyCorrect.length > 1) {
|
|
3813
|
+
diagnostics.push({
|
|
3814
|
+
severity: "warning",
|
|
3815
|
+
code: "multiple-correct-answers",
|
|
3816
|
+
message: `Multiple-choice flashcard "${block.id}" marks more than one answer correct; the first marker wins`,
|
|
3817
|
+
blockId: block.id
|
|
3818
|
+
});
|
|
3819
|
+
}
|
|
3820
|
+
const correctBlock = explicitlyCorrect[0] ?? answerBlocks[0];
|
|
3821
|
+
if (answerBlocks.length < 2) {
|
|
3822
|
+
diagnostics.push({
|
|
3823
|
+
severity: "warning",
|
|
3824
|
+
code: "multiple-choice-needs-distractor",
|
|
3825
|
+
message: `Multiple-choice flashcard "${block.id}" needs at least one distractor`,
|
|
3826
|
+
blockId: block.id
|
|
3827
|
+
});
|
|
3828
|
+
}
|
|
3829
|
+
const choices = answerBlocks.map((answer, index) => ({
|
|
3830
|
+
id: `${block.id}-choice-${index + 1}`,
|
|
3831
|
+
sourceBlockId: answer.id,
|
|
3832
|
+
content: face(answer),
|
|
3833
|
+
correct: answer === correctBlock
|
|
3834
|
+
}));
|
|
3835
|
+
const explanation = explanationFor(block);
|
|
3836
|
+
return {
|
|
3837
|
+
id: block.id,
|
|
3838
|
+
sourceBlockId: block.id,
|
|
3839
|
+
kind: "multiple-choice",
|
|
3840
|
+
...hasTitle(block) ? { label: block.title.trim() } : {},
|
|
3841
|
+
front: face(question),
|
|
3842
|
+
back: face(correctBlock),
|
|
3843
|
+
choices,
|
|
3844
|
+
...explanation ? { explanation } : {}
|
|
3845
|
+
};
|
|
3846
|
+
}
|
|
3847
|
+
function resolveDeckTitle(doc) {
|
|
3848
|
+
const frontmatterTitle = doc.frontmatter?.title;
|
|
3849
|
+
if (typeof frontmatterTitle === "string" && frontmatterTitle.trim()) {
|
|
3850
|
+
return frontmatterTitle.trim();
|
|
3851
|
+
}
|
|
3852
|
+
return doc.startBlock?.title?.trim() || void 0;
|
|
3853
|
+
}
|
|
3854
|
+
function materializeFlashcards(doc, options = {}) {
|
|
3855
|
+
const source = options.source ?? "auto";
|
|
3856
|
+
const cards = [];
|
|
3857
|
+
const diagnostics = [];
|
|
3858
|
+
const visit = (block) => {
|
|
3859
|
+
const marker = resolveFlashcardMarker(block);
|
|
3860
|
+
if (marker === "group") {
|
|
3861
|
+
for (const child of block.children ?? []) visit(child);
|
|
3862
|
+
return;
|
|
3863
|
+
}
|
|
3864
|
+
if (marker === "basic") {
|
|
3865
|
+
const card = materializeBasic(block, diagnostics);
|
|
3866
|
+
if (card) cards.push(card);
|
|
3867
|
+
return;
|
|
3868
|
+
}
|
|
3869
|
+
if (marker === "multiple-choice") {
|
|
3870
|
+
const card = materializeMultipleChoice(block, diagnostics);
|
|
3871
|
+
if (card) cards.push(card);
|
|
3872
|
+
return;
|
|
3873
|
+
}
|
|
3874
|
+
if (source === "explicit") {
|
|
3875
|
+
for (const child of block.children ?? []) visit(child);
|
|
3876
|
+
return;
|
|
3877
|
+
}
|
|
3878
|
+
if (shouldTreatAsImplicitGroup(block)) {
|
|
3879
|
+
for (const child of block.children ?? []) visit(child);
|
|
3880
|
+
return;
|
|
3881
|
+
}
|
|
3882
|
+
if (looksLikeCompleteCard(block)) {
|
|
3883
|
+
const card = materializeBasic(block, diagnostics);
|
|
3884
|
+
if (card) cards.push(card);
|
|
3885
|
+
return;
|
|
3886
|
+
}
|
|
3887
|
+
for (const child of block.children ?? []) visit(child);
|
|
3888
|
+
};
|
|
3889
|
+
for (const block of doc.blocks) visit(block);
|
|
3890
|
+
const resolvedTitle = resolveDeckTitle(doc);
|
|
3891
|
+
const authoredTitle = typeof doc.frontmatter?.title === "string" ? doc.frontmatter.title.trim() : "";
|
|
3892
|
+
const firstCardTitle = cards[0]?.front.blocks[0]?.title?.trim();
|
|
3893
|
+
const title2 = resolvedTitle && (authoredTitle || resolvedTitle !== firstCardTitle) ? resolvedTitle : void 0;
|
|
3894
|
+
return {
|
|
3895
|
+
...title2 ? { title: title2 } : {},
|
|
3896
|
+
cards,
|
|
3897
|
+
diagnostics
|
|
3898
|
+
};
|
|
3899
|
+
}
|
|
3900
|
+
|
|
3635
3901
|
// src/doc/applyNarrationTiming.ts
|
|
3636
3902
|
var MIN_BLOCK_SIMILARITY = 0.5;
|
|
3637
3903
|
var MIN_V1_SCRIPT_SIMILARITY = 0.8;
|
|
@@ -5603,6 +5869,8 @@ export {
|
|
|
5603
5869
|
resolveDashboardSettings,
|
|
5604
5870
|
materializeDashboard,
|
|
5605
5871
|
composeDashboardLayers,
|
|
5872
|
+
resolveFlashcardMarker,
|
|
5873
|
+
materializeFlashcards,
|
|
5606
5874
|
applyNarrationTiming,
|
|
5607
5875
|
scoreTextSimilarity,
|
|
5608
5876
|
resolveAudioMapping,
|