@gmb/bitmark-parser-generator 5.23.0 → 5.25.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/browser/bitmark-parser-generator.min.js +1 -1
- package/dist/browser/bundle-report.html +2 -2
- package/dist/browser/cjs/index.cjs +2897 -167
- package/dist/browser/cjs/index.cjs.map +1 -1
- package/dist/browser/cjs/index.d.cts +195 -1
- package/dist/browser/esm/index.d.ts +195 -1
- package/dist/browser/esm/index.js +2895 -167
- package/dist/browser/esm/index.js.map +1 -1
- package/dist/cli/main.js +3039 -229
- package/dist/cli/main.js.map +1 -1
- package/dist/index.cjs +2924 -171
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +195 -1
- package/dist/index.d.ts +195 -1
- package/dist/index.js +2922 -171
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
|
@@ -34,6 +34,8 @@ __export(index_exports, {
|
|
|
34
34
|
BoundingBoxIndex: () => BoundingBoxIndex,
|
|
35
35
|
Builder: () => Builder,
|
|
36
36
|
CardSetVersion: () => CardSetVersion,
|
|
37
|
+
HtmlTableGenerator: () => HtmlTableGenerator2,
|
|
38
|
+
HtmlTableParser: () => HtmlTableParser2,
|
|
37
39
|
InfoFormat: () => InfoFormat,
|
|
38
40
|
InfoType: () => InfoType,
|
|
39
41
|
Input: () => Input,
|
|
@@ -279,6 +281,7 @@ var BitType2 = {
|
|
|
279
281
|
dangerAlt: "danger-alt",
|
|
280
282
|
dangerCollapsible: "danger-collapsible",
|
|
281
283
|
definitionList: "definition-list",
|
|
284
|
+
definitionListCollapsible: "definition-list-collapsible",
|
|
282
285
|
definitionTerm: "definition-term",
|
|
283
286
|
deleted: "deleted",
|
|
284
287
|
details: "details",
|
|
@@ -559,6 +562,8 @@ var BitType2 = {
|
|
|
559
562
|
smartStandardArticleNonNormativeCollapsible: "smart-standard-article-non-normative-collapsible",
|
|
560
563
|
smartStandardArticleNormative: "smart-standard-article-normative",
|
|
561
564
|
smartStandardArticleNormativeCollapsible: "smart-standard-article-normative-collapsible",
|
|
565
|
+
smartStandardDefinitionListNonNormative: "smart-standard-definition-list-non-normative",
|
|
566
|
+
smartStandardDefinitionListNormative: "smart-standard-definition-list-normative",
|
|
562
567
|
smartStandardExampleNonNormative: "smart-standard-example-non-normative",
|
|
563
568
|
smartStandardExampleNonNormativeCollapsible: "smart-standard-example-non-normative-collapsible",
|
|
564
569
|
smartStandardExampleNormative: "smart-standard-example-normative",
|
|
@@ -609,6 +614,8 @@ var BitType2 = {
|
|
|
609
614
|
smartStandardTableNormativeCollapsible: "smart-standard-table-normative-collapsible",
|
|
610
615
|
standardArticleNonNormative: "standard-article-non-normative",
|
|
611
616
|
standardArticleNormative: "standard-article-normative",
|
|
617
|
+
standardDefinitionListNonNormative: "standard-definition-list-non-normative",
|
|
618
|
+
standardDefinitionListNormative: "standard-definition-list-normative",
|
|
612
619
|
standardExampleNonNormative: "standard-example-non-normative",
|
|
613
620
|
standardExampleNormative: "standard-example-normative",
|
|
614
621
|
standardImageFigureNonNormative: "standard-image-figure-non-normative",
|
|
@@ -778,10 +785,13 @@ var TextFormat2 = {
|
|
|
778
785
|
var import_superenum7 = require("@ncoderz/superenum");
|
|
779
786
|
var CardSetConfigKey = {
|
|
780
787
|
flashcard: "flashcard",
|
|
788
|
+
flashcard1: "flashcard1",
|
|
781
789
|
definitionList: "definitionList",
|
|
790
|
+
definitionListPlain: "definitionListPlain",
|
|
782
791
|
elements: "elements",
|
|
783
792
|
statements: "statements",
|
|
784
793
|
quiz: "quiz",
|
|
794
|
+
quizResponses: "quizResponses",
|
|
785
795
|
feedback: "feedback",
|
|
786
796
|
questions: "questions",
|
|
787
797
|
matchPairs: "matchPairs",
|
|
@@ -793,6 +803,7 @@ var CardSetConfigKey = {
|
|
|
793
803
|
pronunciationTable: "pronunciationTable",
|
|
794
804
|
botActionResponses: "botActionResponses",
|
|
795
805
|
exampleBitList: "exampleBitList",
|
|
806
|
+
pageFooterSections: "pageFooterSections",
|
|
796
807
|
clozeList: "clozeList",
|
|
797
808
|
ingredients: "ingredients",
|
|
798
809
|
// DEPRECATED - TO BE REMOVED IN THE FUTURE
|
|
@@ -1209,12 +1220,17 @@ var groupKeys = {
|
|
|
1209
1220
|
group_standardAllBits: "group_standardAllBits",
|
|
1210
1221
|
group_standardItemLead: "group_standardItemLead",
|
|
1211
1222
|
group_standardItemLeadInstructionHint: "group_standardItemLeadInstructionHint",
|
|
1223
|
+
group_standardInstructionHint: "group_standardInstructionHint",
|
|
1212
1224
|
group_standardTags: "group_standardTags",
|
|
1225
|
+
group_standardTagsNoItemLead: "group_standardTagsNoItemLead",
|
|
1213
1226
|
group_imageSource: "group_imageSource",
|
|
1214
1227
|
group_technicalTerm: "group_technicalTerm",
|
|
1215
1228
|
group_person: "group_person",
|
|
1216
1229
|
group_gap: "group_gap",
|
|
1217
1230
|
group_trueFalse: "group_trueFalse",
|
|
1231
|
+
group_trueFalseInlineHighlight: "group_trueFalseInlineHighlight",
|
|
1232
|
+
group_trueFalseInlineSelect: "group_trueFalseInlineSelect",
|
|
1233
|
+
group_trueFalseResponses: "group_trueFalseResponses",
|
|
1218
1234
|
group_markConfig: "group_markConfig",
|
|
1219
1235
|
group_mark: "group_mark",
|
|
1220
1236
|
group_bookCommon: "group_bookCommon",
|
|
@@ -1222,14 +1238,18 @@ var groupKeys = {
|
|
|
1222
1238
|
group_advertisingCommon: "group_advertisingCommon",
|
|
1223
1239
|
group_platformStylesCommon: "group_platformStylesCommon",
|
|
1224
1240
|
group_quizCommon: "group_quizCommon",
|
|
1241
|
+
group_backgroundWallpaper: "group_backgroundWallpaper",
|
|
1242
|
+
group_imageNoZoom: "group_imageNoZoom",
|
|
1225
1243
|
group_resourceBitTags: "group_resourceBitTags",
|
|
1226
1244
|
group_resourceCommon: "group_resourceCommon",
|
|
1227
1245
|
group_resourceImageCommon: "group_resourceImageCommon",
|
|
1246
|
+
group_resourceImageCommonNoZoom: "group_resourceImageCommonNoZoom",
|
|
1228
1247
|
group_resourceAudioCommon: "group_resourceAudioCommon",
|
|
1229
1248
|
group_resourceVideoCommon: "group_resourceVideoCommon",
|
|
1230
1249
|
group_resourceIcon: "group_resourceIcon",
|
|
1231
1250
|
// Alias for image
|
|
1232
1251
|
group_resourceImage: "group_resourceImage",
|
|
1252
|
+
group_resourceImageNoZoom: "group_resourceImageNoZoom",
|
|
1233
1253
|
group_resourceImageEmbed: "group_resourceImageEmbed",
|
|
1234
1254
|
group_resourceImageLink: "group_resourceImageLink",
|
|
1235
1255
|
group_resourceImageResponsive: "group_resourceImageResponsive",
|
|
@@ -1381,6 +1401,7 @@ var propertyKeys = {
|
|
|
1381
1401
|
property_enforceUpdateOverUserInput: "@enforceUpdateOverUserInput",
|
|
1382
1402
|
property_extractorExtractionTimestamp: "@extractorExtractionTimestamp",
|
|
1383
1403
|
property_isCaseSensitive: "@isCaseSensitive",
|
|
1404
|
+
property_isCollapsible: "@isCollapsible",
|
|
1384
1405
|
property_isInfoOnly: "@isInfoOnly",
|
|
1385
1406
|
property_isPublic: "@isPublic",
|
|
1386
1407
|
property_isTemplate: "@isTemplate",
|
|
@@ -1506,6 +1527,7 @@ var propertyKeys = {
|
|
|
1506
1527
|
property_src4x: "@src4x",
|
|
1507
1528
|
property_srcAlt: "@srcAlt",
|
|
1508
1529
|
property_stripePricingTableId: "@stripePricingTableId",
|
|
1530
|
+
property_stripePricingTableDescriptiveName: "@stripePricingTableDescriptiveName",
|
|
1509
1531
|
property_stripePublishableKey: "@stripePublishableKey",
|
|
1510
1532
|
property_subject: "@subject",
|
|
1511
1533
|
property_subtype: "@subtype",
|
|
@@ -2257,7 +2279,7 @@ var CARDSETS = {
|
|
|
2257
2279
|
variants: [
|
|
2258
2280
|
{
|
|
2259
2281
|
jsonKey: "question.text",
|
|
2260
|
-
exportJsonKey: { question: { text: "$" } },
|
|
2282
|
+
exportJsonKey: [{ "@absent": { question: {} } }, { question: { text: "$" } }],
|
|
2261
2283
|
tags: [
|
|
2262
2284
|
{
|
|
2263
2285
|
key: ConfigKey.group_standardTags,
|
|
@@ -2273,6 +2295,136 @@ var CARDSETS = {
|
|
|
2273
2295
|
description: "Icon resource for the flashcard.",
|
|
2274
2296
|
jsonKey: "question.icon|resource(type=image, key=image)",
|
|
2275
2297
|
exportJsonKey: { question: { icon: { type: "image", image: { src: "$" } } } }
|
|
2298
|
+
},
|
|
2299
|
+
{
|
|
2300
|
+
key: ConfigKey.property_example,
|
|
2301
|
+
exportJsonKey: [
|
|
2302
|
+
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2303
|
+
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2304
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2305
|
+
],
|
|
2306
|
+
description: "Example marker for the flashcard question.",
|
|
2307
|
+
format: TagFormat.bitmarkText,
|
|
2308
|
+
nullable: true
|
|
2309
|
+
}
|
|
2310
|
+
]
|
|
2311
|
+
}
|
|
2312
|
+
]
|
|
2313
|
+
},
|
|
2314
|
+
{
|
|
2315
|
+
name: "answer",
|
|
2316
|
+
variants: [
|
|
2317
|
+
{
|
|
2318
|
+
jsonKey: "answer.text",
|
|
2319
|
+
exportJsonKey: [{ "@absent": { answer: {} } }, { answer: { text: "$" } }],
|
|
2320
|
+
tags: [
|
|
2321
|
+
{
|
|
2322
|
+
key: ConfigKey.group_standardTags,
|
|
2323
|
+
description: "Standard tags for the flashcard."
|
|
2324
|
+
},
|
|
2325
|
+
{
|
|
2326
|
+
exportJsonKey: { title: "$" },
|
|
2327
|
+
key: ConfigKey.tag_title,
|
|
2328
|
+
description: "Title of the flashcard."
|
|
2329
|
+
},
|
|
2330
|
+
{
|
|
2331
|
+
key: ConfigKey.group_resourceIcon,
|
|
2332
|
+
description: "Icon resource for the flashcard.",
|
|
2333
|
+
jsonKey: "answer.icon|resource(type=image, key=image)",
|
|
2334
|
+
exportJsonKey: { answer: { icon: { type: "image", image: { src: "$" } } } }
|
|
2335
|
+
},
|
|
2336
|
+
{
|
|
2337
|
+
key: ConfigKey.property_example,
|
|
2338
|
+
exportJsonKey: [
|
|
2339
|
+
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2340
|
+
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2341
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2342
|
+
],
|
|
2343
|
+
description: "Example marker for the flashcard answer.",
|
|
2344
|
+
format: TagFormat.bitmarkText,
|
|
2345
|
+
nullable: true
|
|
2346
|
+
}
|
|
2347
|
+
]
|
|
2348
|
+
},
|
|
2349
|
+
{
|
|
2350
|
+
jsonKey: "alternativeAnswers[].text",
|
|
2351
|
+
exportJsonKey: { alternativeAnswers: [{ text: "$" }] },
|
|
2352
|
+
tags: [
|
|
2353
|
+
{
|
|
2354
|
+
key: ConfigKey.group_standardTags,
|
|
2355
|
+
description: "Standard tags for the flashcard."
|
|
2356
|
+
},
|
|
2357
|
+
{
|
|
2358
|
+
exportJsonKey: { title: "$" },
|
|
2359
|
+
key: ConfigKey.tag_title,
|
|
2360
|
+
description: "Title of the flashcard."
|
|
2361
|
+
},
|
|
2362
|
+
{
|
|
2363
|
+
key: ConfigKey.group_resourceIcon,
|
|
2364
|
+
description: "Icon resource for the flashcard.",
|
|
2365
|
+
jsonKey: "alternativeAnswers[].icon|resource(type=image, key=image)",
|
|
2366
|
+
exportJsonKey: {
|
|
2367
|
+
alternativeAnswers: [{ icon: { type: "image", image: { src: "$" } } }]
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
],
|
|
2371
|
+
repeatCount: Count.infinity
|
|
2372
|
+
}
|
|
2373
|
+
]
|
|
2374
|
+
}
|
|
2375
|
+
]
|
|
2376
|
+
},
|
|
2377
|
+
//
|
|
2378
|
+
// flashcard1 — single-card variant of flashcard. Same shape, but capped
|
|
2379
|
+
// at one card via `sections.default.maxCount = 1` (PLAN-085). Used by
|
|
2380
|
+
// the `flashcard-1` bit type; the multi-card `flashcard` and
|
|
2381
|
+
// `q-and-a-card` bit types continue to use the unbounded `flashcard`
|
|
2382
|
+
// cardset.
|
|
2383
|
+
//
|
|
2384
|
+
[CardSetConfigKey.flashcard1]: {
|
|
2385
|
+
jsonKey: "cards",
|
|
2386
|
+
exportJsonKey: { cards: "$" },
|
|
2387
|
+
sections: {
|
|
2388
|
+
default: {
|
|
2389
|
+
jsonKey: "cards",
|
|
2390
|
+
exportJsonKey: { cards: "$" },
|
|
2391
|
+
isDefault: true,
|
|
2392
|
+
maxCount: 1
|
|
2393
|
+
}
|
|
2394
|
+
},
|
|
2395
|
+
sides: [
|
|
2396
|
+
{
|
|
2397
|
+
name: "question",
|
|
2398
|
+
variants: [
|
|
2399
|
+
{
|
|
2400
|
+
jsonKey: "question.text",
|
|
2401
|
+
exportJsonKey: [{ "@absent": { question: {} } }, { question: { text: "$" } }],
|
|
2402
|
+
tags: [
|
|
2403
|
+
{
|
|
2404
|
+
key: ConfigKey.group_standardTags,
|
|
2405
|
+
description: "Standard tags for the flashcard."
|
|
2406
|
+
},
|
|
2407
|
+
{
|
|
2408
|
+
exportJsonKey: { title: "$" },
|
|
2409
|
+
key: ConfigKey.tag_title,
|
|
2410
|
+
description: "Title of the flashcard."
|
|
2411
|
+
},
|
|
2412
|
+
{
|
|
2413
|
+
key: ConfigKey.group_resourceIcon,
|
|
2414
|
+
description: "Icon resource for the flashcard.",
|
|
2415
|
+
jsonKey: "question.icon|resource(type=image, key=image)",
|
|
2416
|
+
exportJsonKey: { question: { icon: { type: "image", image: { src: "$" } } } }
|
|
2417
|
+
},
|
|
2418
|
+
{
|
|
2419
|
+
key: ConfigKey.property_example,
|
|
2420
|
+
exportJsonKey: [
|
|
2421
|
+
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2422
|
+
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2423
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2424
|
+
],
|
|
2425
|
+
description: "Example marker for the flashcard question.",
|
|
2426
|
+
format: TagFormat.bitmarkText,
|
|
2427
|
+
nullable: true
|
|
2276
2428
|
}
|
|
2277
2429
|
]
|
|
2278
2430
|
}
|
|
@@ -2283,7 +2435,7 @@ var CARDSETS = {
|
|
|
2283
2435
|
variants: [
|
|
2284
2436
|
{
|
|
2285
2437
|
jsonKey: "answer.text",
|
|
2286
|
-
exportJsonKey: { answer: { text: "$" } },
|
|
2438
|
+
exportJsonKey: [{ "@absent": { answer: {} } }, { answer: { text: "$" } }],
|
|
2287
2439
|
tags: [
|
|
2288
2440
|
{
|
|
2289
2441
|
key: ConfigKey.group_standardTags,
|
|
@@ -2299,6 +2451,17 @@ var CARDSETS = {
|
|
|
2299
2451
|
description: "Icon resource for the flashcard.",
|
|
2300
2452
|
jsonKey: "answer.icon|resource(type=image, key=image)",
|
|
2301
2453
|
exportJsonKey: { answer: { icon: { type: "image", image: { src: "$" } } } }
|
|
2454
|
+
},
|
|
2455
|
+
{
|
|
2456
|
+
key: ConfigKey.property_example,
|
|
2457
|
+
exportJsonKey: [
|
|
2458
|
+
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2459
|
+
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2460
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2461
|
+
],
|
|
2462
|
+
description: "Example marker for the flashcard answer.",
|
|
2463
|
+
format: TagFormat.bitmarkText,
|
|
2464
|
+
nullable: true
|
|
2302
2465
|
}
|
|
2303
2466
|
]
|
|
2304
2467
|
},
|
|
@@ -2342,7 +2505,7 @@ var CARDSETS = {
|
|
|
2342
2505
|
variants: [
|
|
2343
2506
|
{
|
|
2344
2507
|
jsonKey: "term.text",
|
|
2345
|
-
exportJsonKey: { term: { text: "$" } },
|
|
2508
|
+
exportJsonKey: [{ "@absent": { term: {} } }, { term: { text: "$" } }],
|
|
2346
2509
|
tags: [
|
|
2347
2510
|
{
|
|
2348
2511
|
key: ConfigKey.group_standardTags,
|
|
@@ -2361,6 +2524,17 @@ var CARDSETS = {
|
|
|
2361
2524
|
format: TagFormat.plainText,
|
|
2362
2525
|
jsonKey: "term.icon|resource(type=image, key=image)",
|
|
2363
2526
|
exportJsonKey: { term: { icon: { type: "image", image: { src: "$" } } } }
|
|
2527
|
+
},
|
|
2528
|
+
{
|
|
2529
|
+
key: ConfigKey.property_example,
|
|
2530
|
+
exportJsonKey: [
|
|
2531
|
+
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2532
|
+
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2533
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2534
|
+
],
|
|
2535
|
+
description: "Example marker for the definition term.",
|
|
2536
|
+
format: TagFormat.bitmarkText,
|
|
2537
|
+
nullable: true
|
|
2364
2538
|
}
|
|
2365
2539
|
]
|
|
2366
2540
|
}
|
|
@@ -2371,7 +2545,7 @@ var CARDSETS = {
|
|
|
2371
2545
|
variants: [
|
|
2372
2546
|
{
|
|
2373
2547
|
jsonKey: "definition.text",
|
|
2374
|
-
exportJsonKey: { definition: { text: "$" } },
|
|
2548
|
+
exportJsonKey: [{ "@absent": { definition: {} } }, { definition: { text: "$" } }],
|
|
2375
2549
|
tags: [
|
|
2376
2550
|
{
|
|
2377
2551
|
key: ConfigKey.group_standardTags,
|
|
@@ -2389,6 +2563,17 @@ var CARDSETS = {
|
|
|
2389
2563
|
description: "Icon resource for the definition.",
|
|
2390
2564
|
jsonKey: "definition.icon|resource(type=image, key=image)",
|
|
2391
2565
|
exportJsonKey: { definition: { icon: { type: "image", image: { src: "$" } } } }
|
|
2566
|
+
},
|
|
2567
|
+
{
|
|
2568
|
+
key: ConfigKey.property_example,
|
|
2569
|
+
exportJsonKey: [
|
|
2570
|
+
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2571
|
+
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2572
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2573
|
+
],
|
|
2574
|
+
description: "Example marker for the definition.",
|
|
2575
|
+
format: TagFormat.bitmarkText,
|
|
2576
|
+
nullable: true
|
|
2392
2577
|
}
|
|
2393
2578
|
]
|
|
2394
2579
|
},
|
|
@@ -2421,6 +2606,126 @@ var CARDSETS = {
|
|
|
2421
2606
|
]
|
|
2422
2607
|
},
|
|
2423
2608
|
//
|
|
2609
|
+
// definition-list-plain
|
|
2610
|
+
//
|
|
2611
|
+
// Same shape as `definitionList`, but every variant body is rendered as
|
|
2612
|
+
// plain text (string), not bitmark+ AST. Used by
|
|
2613
|
+
// `meta-search-default-terms` / `meta-search-default-topics`, which need
|
|
2614
|
+
// term/definition text emitted verbatim instead of as a ProseMirror tree.
|
|
2615
|
+
//
|
|
2616
|
+
[CardSetConfigKey.definitionListPlain]: {
|
|
2617
|
+
jsonKey: "definitions",
|
|
2618
|
+
exportJsonKey: { definitions: "$" },
|
|
2619
|
+
sides: [
|
|
2620
|
+
{
|
|
2621
|
+
name: "term",
|
|
2622
|
+
variants: [
|
|
2623
|
+
{
|
|
2624
|
+
jsonKey: "term.text",
|
|
2625
|
+
exportJsonKey: [{ "@absent": { term: {} } }, { term: { text: "$" } }],
|
|
2626
|
+
format: TextFormat2.plainText,
|
|
2627
|
+
tags: [
|
|
2628
|
+
{
|
|
2629
|
+
key: ConfigKey.group_standardTags,
|
|
2630
|
+
description: "Standard tags for the definition."
|
|
2631
|
+
},
|
|
2632
|
+
{
|
|
2633
|
+
key: ConfigKey.tag_title,
|
|
2634
|
+
description: "Title of the definition.",
|
|
2635
|
+
format: TagFormat.plainText,
|
|
2636
|
+
jsonKey: "^heading.forKeys",
|
|
2637
|
+
exportJsonKey: { "@bit": { heading: { forKeys: "$" } } }
|
|
2638
|
+
},
|
|
2639
|
+
{
|
|
2640
|
+
key: ConfigKey.group_resourceIcon,
|
|
2641
|
+
description: "Icon resource for the definition.",
|
|
2642
|
+
format: TagFormat.plainText,
|
|
2643
|
+
jsonKey: "term.icon|resource(type=image, key=image)",
|
|
2644
|
+
exportJsonKey: { term: { icon: { type: "image", image: { src: "$" } } } }
|
|
2645
|
+
},
|
|
2646
|
+
{
|
|
2647
|
+
key: ConfigKey.property_example,
|
|
2648
|
+
exportJsonKey: [
|
|
2649
|
+
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2650
|
+
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2651
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2652
|
+
],
|
|
2653
|
+
description: "Example marker for the definition term.",
|
|
2654
|
+
format: TagFormat.bitmarkText,
|
|
2655
|
+
nullable: true
|
|
2656
|
+
}
|
|
2657
|
+
]
|
|
2658
|
+
}
|
|
2659
|
+
]
|
|
2660
|
+
},
|
|
2661
|
+
{
|
|
2662
|
+
name: "definition",
|
|
2663
|
+
variants: [
|
|
2664
|
+
{
|
|
2665
|
+
jsonKey: "definition.text",
|
|
2666
|
+
exportJsonKey: [{ "@absent": { definition: {} } }, { definition: { text: "$" } }],
|
|
2667
|
+
format: TextFormat2.plainText,
|
|
2668
|
+
tags: [
|
|
2669
|
+
{
|
|
2670
|
+
key: ConfigKey.group_standardTags,
|
|
2671
|
+
description: "Standard tags for the definition."
|
|
2672
|
+
},
|
|
2673
|
+
{
|
|
2674
|
+
key: ConfigKey.tag_title,
|
|
2675
|
+
description: "Title of the definition.",
|
|
2676
|
+
format: TagFormat.plainText,
|
|
2677
|
+
jsonKey: "^heading.forValues",
|
|
2678
|
+
exportJsonKey: { "@bit": { heading: { forValues: "$" } } }
|
|
2679
|
+
},
|
|
2680
|
+
{
|
|
2681
|
+
key: ConfigKey.group_resourceIcon,
|
|
2682
|
+
description: "Icon resource for the definition.",
|
|
2683
|
+
jsonKey: "definition.icon|resource(type=image, key=image)",
|
|
2684
|
+
exportJsonKey: { definition: { icon: { type: "image", image: { src: "$" } } } }
|
|
2685
|
+
},
|
|
2686
|
+
{
|
|
2687
|
+
key: ConfigKey.property_example,
|
|
2688
|
+
exportJsonKey: [
|
|
2689
|
+
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2690
|
+
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2691
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2692
|
+
],
|
|
2693
|
+
description: "Example marker for the definition.",
|
|
2694
|
+
format: TagFormat.bitmarkText,
|
|
2695
|
+
nullable: true
|
|
2696
|
+
}
|
|
2697
|
+
]
|
|
2698
|
+
},
|
|
2699
|
+
{
|
|
2700
|
+
jsonKey: "alternativeDefinitions[].text",
|
|
2701
|
+
exportJsonKey: { alternativeDefinitions: [{ text: "$" }] },
|
|
2702
|
+
format: TextFormat2.plainText,
|
|
2703
|
+
tags: [
|
|
2704
|
+
{
|
|
2705
|
+
key: ConfigKey.group_standardTags,
|
|
2706
|
+
description: "Standard tags for the definition."
|
|
2707
|
+
},
|
|
2708
|
+
{
|
|
2709
|
+
exportJsonKey: { title: "$" },
|
|
2710
|
+
key: ConfigKey.tag_title,
|
|
2711
|
+
description: "Title of the definition."
|
|
2712
|
+
},
|
|
2713
|
+
{
|
|
2714
|
+
key: ConfigKey.group_resourceIcon,
|
|
2715
|
+
description: "Icon resource for the definition.",
|
|
2716
|
+
jsonKey: "alternativeDefinitions[].icon|resource(type=image, key=image)",
|
|
2717
|
+
exportJsonKey: {
|
|
2718
|
+
alternativeDefinitions: [{ icon: { type: "image", image: { src: "$" } } }]
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
],
|
|
2722
|
+
repeatCount: Count.infinity
|
|
2723
|
+
}
|
|
2724
|
+
]
|
|
2725
|
+
}
|
|
2726
|
+
]
|
|
2727
|
+
},
|
|
2728
|
+
//
|
|
2424
2729
|
// match-pairs
|
|
2425
2730
|
//
|
|
2426
2731
|
[CardSetConfigKey.matchPairs]: {
|
|
@@ -2433,11 +2738,51 @@ var CARDSETS = {
|
|
|
2433
2738
|
{
|
|
2434
2739
|
jsonKey: "key",
|
|
2435
2740
|
exportJsonKey: { key: "$" },
|
|
2741
|
+
format: TextFormat2.plainText,
|
|
2436
2742
|
tags: [
|
|
2437
2743
|
{
|
|
2438
2744
|
key: ConfigKey.group_standardTags,
|
|
2439
2745
|
description: "Standard tags for the match pair."
|
|
2440
2746
|
},
|
|
2747
|
+
{
|
|
2748
|
+
// Pair-level example: emit `isExample` alongside `example`
|
|
2749
|
+
// (matches OLD parser's per-pair allow-list behaviour).
|
|
2750
|
+
//
|
|
2751
|
+
// PLAN-074: a valued cascade source (`[@example:V]` anywhere
|
|
2752
|
+
// in the cardset) fills each pair's `example` with `V`
|
|
2753
|
+
// once per pair from the keys-side cascade fire (single
|
|
2754
|
+
// parsed_variant per pair — no per-value duplication).
|
|
2755
|
+
//
|
|
2756
|
+
// PLAN-076 NOTE: matchPairs (TAG 415) KEEPS `example: '$'`
|
|
2757
|
+
// at the pair-object level. BPG `parseMatchPairs`
|
|
2758
|
+
// (`CardContentProcessor.ts:714-723`) emits ONE example
|
|
2759
|
+
// per PAIR (the `exampleCard` accumulator), placed at the
|
|
2760
|
+
// pair object. Unlike matchMatrix (per-cell example),
|
|
2761
|
+
// matchPairs has no nested per-cell layer, so the pair-key
|
|
2762
|
+
// `[@example:V]` must double-emit as the visible field.
|
|
2763
|
+
// Confirmed via `chaining` fixture: `match-solution-grouped`
|
|
2764
|
+
// expects `example: "packen"` at pair-object level from
|
|
2765
|
+
// `die Koffer[%1][@example:packen]`. Original PLAN-076
|
|
2766
|
+
// draft applied to both keys-side tags; the matchPairs
|
|
2767
|
+
// half was reverted after fixture parity testing.
|
|
2768
|
+
key: ConfigKey.property_example,
|
|
2769
|
+
exportJsonKey: [
|
|
2770
|
+
{ "@keyonly": { isExample: true, "@bit": { isExample: true } } },
|
|
2771
|
+
{
|
|
2772
|
+
predicates: ["@absent", { $cascade: "*" }],
|
|
2773
|
+
rule: {
|
|
2774
|
+
isExample: true,
|
|
2775
|
+
example: "$cascade",
|
|
2776
|
+
"@bit": { isExample: true }
|
|
2777
|
+
}
|
|
2778
|
+
},
|
|
2779
|
+
{ "@absent": { isExample: true } },
|
|
2780
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2781
|
+
],
|
|
2782
|
+
description: "Example marker / value on the match pair.",
|
|
2783
|
+
format: TagFormat.bitmarkText,
|
|
2784
|
+
nullable: true
|
|
2785
|
+
},
|
|
2441
2786
|
{
|
|
2442
2787
|
key: ConfigKey.tag_title,
|
|
2443
2788
|
description: "Title of the match pair.",
|
|
@@ -2446,9 +2791,19 @@ var CARDSETS = {
|
|
|
2446
2791
|
exportJsonKey: { "@bit": { heading: { forKeys: "$" } } }
|
|
2447
2792
|
},
|
|
2448
2793
|
{
|
|
2794
|
+
// PLAN-084: gate the key-side `@absent → $ancestor` cascade
|
|
2795
|
+
// synth with `@variantLacksTag=isCaseSensitive` so the rule
|
|
2796
|
+
// only fires when NO variant in the active card explicitly
|
|
2797
|
+
// carries `[@isCaseSensitive]`. When a value-side variant
|
|
2798
|
+
// carries the tag (e.g. `[@isCaseSensitive:false]`), the
|
|
2799
|
+
// synth is skipped here; the value-side explicit fire owns
|
|
2800
|
+
// the pair-level emission.
|
|
2449
2801
|
key: ConfigKey.property_isCaseSensitive,
|
|
2450
2802
|
exportJsonKey: [
|
|
2451
|
-
{
|
|
2803
|
+
{
|
|
2804
|
+
predicates: ["@absent", { "@variantLacksTag": "@isCaseSensitive" }],
|
|
2805
|
+
rule: { isCaseSensitive: "$ancestor" }
|
|
2806
|
+
},
|
|
2452
2807
|
{ isCaseSensitive: "$" }
|
|
2453
2808
|
],
|
|
2454
2809
|
description: "Property to indicate if the match is case sensitive.",
|
|
@@ -2477,10 +2832,85 @@ var CARDSETS = {
|
|
|
2477
2832
|
{
|
|
2478
2833
|
jsonKey: "values[]",
|
|
2479
2834
|
exportJsonKey: { values: ["$"] },
|
|
2835
|
+
format: TextFormat2.plainText,
|
|
2480
2836
|
tags: [
|
|
2481
2837
|
{
|
|
2482
|
-
|
|
2483
|
-
|
|
2838
|
+
// PLAN-084: value-side excludes item/lead/pageNumber/marginNumber.
|
|
2839
|
+
// BPG `parseMatchPairs` does `delete tags.item; delete tags.lead;`
|
|
2840
|
+
// for sideIdx > 0; the schema-side equivalent is to use a
|
|
2841
|
+
// group_standardTags variant that omits group_standardItemLead.
|
|
2842
|
+
key: ConfigKey.group_standardTagsNoItemLead,
|
|
2843
|
+
description: "Standard tags for the match pair (values side, no item/lead)."
|
|
2844
|
+
},
|
|
2845
|
+
{
|
|
2846
|
+
// PLAN-072 + ALIGN-EXAMPLE-CASCADE §3.6: cascade-fired
|
|
2847
|
+
// `@example` from the bit header fills each pair's
|
|
2848
|
+
// `example` with the pair's first value (mirrors BPG's
|
|
2849
|
+
// `fillStringExample(pairs, __defaultExample=values[0])`).
|
|
2850
|
+
//
|
|
2851
|
+
// PLAN-074: the cascade-source-VALUE branch is handled by
|
|
2852
|
+
// TAG 415 (keys side) which fires once per pair — keeping
|
|
2853
|
+
// it here would emit `example` twice per pair (one per
|
|
2854
|
+
// values-side parsed_variant) and merge them into a
|
|
2855
|
+
// multi-paragraph array.
|
|
2856
|
+
//
|
|
2857
|
+
// PLAN-075: bare `$parent` reads the parent variant fire's
|
|
2858
|
+
// source `$` value (the current `++`-iteration's value
|
|
2859
|
+
// text). For single-value pairs this matches the old
|
|
2860
|
+
// `$parent.values[0]`; for multi-value pairs with
|
|
2861
|
+
// `[@example]` attached to a non-first value, it correctly
|
|
2862
|
+
// resolves to that value.
|
|
2863
|
+
//
|
|
2864
|
+
// PLAN-077: dual `[@absent, { '$cascade': '*' }]` rule
|
|
2865
|
+
// routes pair-key `[@example:V]` (a per-pair cascade
|
|
2866
|
+
// source, bound via per-card CascadeCounters scope) and
|
|
2867
|
+
// bit-header `[@example:V]` (cardset cascade) into the
|
|
2868
|
+
// pair-level `example` via `$cascade`. The aggregator in
|
|
2869
|
+
// `populate_variants` fires this rule ONCE per pair (BPG
|
|
2870
|
+
// `parseMatchPairs` `exampleCard` precedence), avoiding
|
|
2871
|
+
// per-iter pollution.
|
|
2872
|
+
key: ConfigKey.property_example,
|
|
2873
|
+
exportJsonKey: [
|
|
2874
|
+
// PLAN-084: bare `[@example]` on a values-side variant fires
|
|
2875
|
+
// once per pair (first-wins, mirrors BPG `parseMatchPairs`
|
|
2876
|
+
// `exampleCard` + `pushDownTree` set-if-null semantic).
|
|
2877
|
+
// `maxEmits: 1` enforced inside the per-pair cascade-counter
|
|
2878
|
+
// scope opened by `build_card_item`.
|
|
2879
|
+
{
|
|
2880
|
+
predicates: ["@keyonly"],
|
|
2881
|
+
maxEmits: 1,
|
|
2882
|
+
rule: {
|
|
2883
|
+
isExample: true,
|
|
2884
|
+
example: "$parent",
|
|
2885
|
+
"@bit": { isExample: true }
|
|
2886
|
+
}
|
|
2887
|
+
},
|
|
2888
|
+
{
|
|
2889
|
+
predicates: ["@absent", { $cascade: "*" }],
|
|
2890
|
+
rule: {
|
|
2891
|
+
isExample: true,
|
|
2892
|
+
example: "$cascade",
|
|
2893
|
+
"@bit": { isExample: true }
|
|
2894
|
+
}
|
|
2895
|
+
},
|
|
2896
|
+
{
|
|
2897
|
+
"@absent": {
|
|
2898
|
+
isExample: true,
|
|
2899
|
+
example: "$parent",
|
|
2900
|
+
"@bit": { isExample: true }
|
|
2901
|
+
}
|
|
2902
|
+
},
|
|
2903
|
+
// PLAN-084: same cap applies to the explicit-value `[@example:V]`
|
|
2904
|
+
// form so per-`++`-iter values-side fires also first-win.
|
|
2905
|
+
{
|
|
2906
|
+
predicates: [],
|
|
2907
|
+
maxEmits: 1,
|
|
2908
|
+
rule: { isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2909
|
+
}
|
|
2910
|
+
],
|
|
2911
|
+
description: "Example marker / value on the match pair (values side).",
|
|
2912
|
+
format: TagFormat.bitmarkText,
|
|
2913
|
+
nullable: true
|
|
2484
2914
|
},
|
|
2485
2915
|
{
|
|
2486
2916
|
key: ConfigKey.tag_title,
|
|
@@ -2490,9 +2920,13 @@ var CARDSETS = {
|
|
|
2490
2920
|
exportJsonKey: { "@bit": { heading: { forValues: "$" } } }
|
|
2491
2921
|
},
|
|
2492
2922
|
{
|
|
2923
|
+
// PLAN-084: symmetric @variantLacksTag gating with the key-side.
|
|
2493
2924
|
key: ConfigKey.property_isCaseSensitive,
|
|
2494
2925
|
exportJsonKey: [
|
|
2495
|
-
{
|
|
2926
|
+
{
|
|
2927
|
+
predicates: ["@absent", { "@variantLacksTag": "@isCaseSensitive" }],
|
|
2928
|
+
rule: { isCaseSensitive: "$ancestor" }
|
|
2929
|
+
},
|
|
2496
2930
|
{ isCaseSensitive: "$" }
|
|
2497
2931
|
],
|
|
2498
2932
|
description: "Property to indicate if the match is case sensitive.",
|
|
@@ -2518,6 +2952,7 @@ var CARDSETS = {
|
|
|
2518
2952
|
{
|
|
2519
2953
|
jsonKey: "key",
|
|
2520
2954
|
exportJsonKey: { key: "$" },
|
|
2955
|
+
format: TextFormat2.plainText,
|
|
2521
2956
|
tags: [
|
|
2522
2957
|
{
|
|
2523
2958
|
key: ConfigKey.group_standardTags,
|
|
@@ -2547,10 +2982,12 @@ var CARDSETS = {
|
|
|
2547
2982
|
{
|
|
2548
2983
|
jsonKey: "values[]",
|
|
2549
2984
|
exportJsonKey: { values: ["$"] },
|
|
2985
|
+
format: TextFormat2.plainText,
|
|
2550
2986
|
tags: [
|
|
2551
2987
|
{
|
|
2552
|
-
|
|
2553
|
-
|
|
2988
|
+
// PLAN-084: value-side excludes item/lead/pageNumber/marginNumber.
|
|
2989
|
+
key: ConfigKey.group_standardTagsNoItemLead,
|
|
2990
|
+
description: "Standard tags for the audio match pair (values side, no item/lead)."
|
|
2554
2991
|
},
|
|
2555
2992
|
{
|
|
2556
2993
|
key: ConfigKey.tag_title,
|
|
@@ -2583,6 +3020,7 @@ var CARDSETS = {
|
|
|
2583
3020
|
{
|
|
2584
3021
|
jsonKey: "key",
|
|
2585
3022
|
exportJsonKey: { key: "$" },
|
|
3023
|
+
format: TextFormat2.plainText,
|
|
2586
3024
|
tags: [
|
|
2587
3025
|
{
|
|
2588
3026
|
key: ConfigKey.group_standardTags,
|
|
@@ -2612,10 +3050,12 @@ var CARDSETS = {
|
|
|
2612
3050
|
{
|
|
2613
3051
|
jsonKey: "values[]",
|
|
2614
3052
|
exportJsonKey: { values: ["$"] },
|
|
3053
|
+
format: TextFormat2.plainText,
|
|
2615
3054
|
tags: [
|
|
2616
3055
|
{
|
|
2617
|
-
|
|
2618
|
-
|
|
3056
|
+
// PLAN-084: value-side excludes item/lead/pageNumber/marginNumber.
|
|
3057
|
+
key: ConfigKey.group_standardTagsNoItemLead,
|
|
3058
|
+
description: "Standard tags for the image match pair (values side, no item/lead)."
|
|
2619
3059
|
},
|
|
2620
3060
|
{
|
|
2621
3061
|
key: ConfigKey.tag_title,
|
|
@@ -2648,20 +3088,49 @@ var CARDSETS = {
|
|
|
2648
3088
|
{
|
|
2649
3089
|
jsonKey: "key",
|
|
2650
3090
|
exportJsonKey: { key: "$" },
|
|
3091
|
+
format: TextFormat2.plainText,
|
|
2651
3092
|
tags: [
|
|
2652
3093
|
{
|
|
2653
3094
|
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
2654
3095
|
description: "Standard tags for lead, instruction, and hint."
|
|
2655
3096
|
},
|
|
2656
3097
|
{
|
|
3098
|
+
// PLAN-076: drop `example: '$'` from valued rule — pair-key
|
|
3099
|
+
// `[@example:V]` is consumed as the per-pair cascade source.
|
|
3100
|
+
// PLAN-077 R14 follow-up: investigation showed the fixture
|
|
3101
|
+
// reference is INCONSISTENT — bits without a cardset-level
|
|
3102
|
+
// cascade (e.g. bit 3 `[@example:Y]should be...`) expect
|
|
3103
|
+
// pair-level `example: V` at pair scope, but bits WITH a
|
|
3104
|
+
// cardset cascade (e.g. bit 8 `[@example:99]` in title-row
|
|
3105
|
+
// PLUS pair-key `[@example:Y]`) expect NO pair-level
|
|
3106
|
+
// example. Emitting `example: $` here satisfies bit 3 but
|
|
3107
|
+
// breaks bit 8; dropping it satisfies bit 8 but breaks bit
|
|
3108
|
+
// 3. Conditional emission would require runtime gating on
|
|
3109
|
+
// the cardset cascade scope state. Left dropped pending a
|
|
3110
|
+
// dedicated plan — accepted divergence on bit 3's pair-
|
|
3111
|
+
// level emission (cells still get the per-cell `example`
|
|
3112
|
+
// correctly via PLAN-077 §4.2 + §4.3).
|
|
2657
3113
|
key: ConfigKey.property_example,
|
|
2658
3114
|
exportJsonKey: [
|
|
2659
|
-
{
|
|
2660
|
-
|
|
2661
|
-
|
|
3115
|
+
{
|
|
3116
|
+
"@keyonly": {
|
|
3117
|
+
isExample: true,
|
|
3118
|
+
"@bit": { isExample: true },
|
|
3119
|
+
"@card": { isExample: true }
|
|
3120
|
+
}
|
|
3121
|
+
},
|
|
3122
|
+
{
|
|
3123
|
+
"@absent": {
|
|
3124
|
+
isExample: true,
|
|
3125
|
+
"@bit": { isExample: true },
|
|
3126
|
+
"@card": { isExample: true }
|
|
3127
|
+
}
|
|
3128
|
+
},
|
|
3129
|
+
{ isExample: true, "@bit": { isExample: true }, "@card": { isExample: true } }
|
|
2662
3130
|
],
|
|
2663
3131
|
description: "Example text for the match matrix.",
|
|
2664
|
-
format: TagFormat.plainText
|
|
3132
|
+
format: TagFormat.plainText,
|
|
3133
|
+
nullable: true
|
|
2665
3134
|
},
|
|
2666
3135
|
{
|
|
2667
3136
|
key: ConfigKey.tag_title,
|
|
@@ -2689,35 +3158,96 @@ var CARDSETS = {
|
|
|
2689
3158
|
variants: [
|
|
2690
3159
|
{
|
|
2691
3160
|
jsonKey: "cells[{s}].values[]",
|
|
2692
|
-
exportJsonKey:
|
|
3161
|
+
exportJsonKey: [
|
|
3162
|
+
// PLAN-085 / R14: the positional-blank emission for an
|
|
3163
|
+
// effectively-absent cell side fires ONLY when the active
|
|
3164
|
+
// card has at least one `[#]` variant fire. This matches
|
|
3165
|
+
// BPG `extractHeadingCard`'s `isHeading` gate: when no
|
|
3166
|
+
// `[#]` exists anywhere in the title card, BPG returns
|
|
3167
|
+
// undefined and no `heading` is emitted. The phantom
|
|
3168
|
+
// `forValues:['']` placeholder is appropriate only when
|
|
3169
|
+
// the card is being treated as a title-row.
|
|
3170
|
+
{
|
|
3171
|
+
predicates: ["@absent", { "@variantHasTag": "#" }],
|
|
3172
|
+
rule: { "@bit": { heading: { forValues: [""] } } }
|
|
3173
|
+
},
|
|
3174
|
+
{ cells: { $s: { values: ["$"] } } }
|
|
3175
|
+
],
|
|
3176
|
+
format: TextFormat2.plainText,
|
|
2693
3177
|
tags: [
|
|
2694
3178
|
{
|
|
2695
3179
|
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
2696
3180
|
description: "Standard tags for lead, instruction, and hint."
|
|
2697
3181
|
},
|
|
2698
3182
|
{
|
|
3183
|
+
// PLAN-075: bare `$parent` reads the parent variant fire's
|
|
3184
|
+
// source `$` value (the current `++`-iteration's cell text).
|
|
3185
|
+
// Replaces `$parent.values[0]` which pinned to position 0
|
|
3186
|
+
// regardless of which `++`-value `[@example]` was on.
|
|
3187
|
+
//
|
|
3188
|
+
// PLAN-077: dual `[@absent, { '$cascade': '*' }]` rule
|
|
3189
|
+
// routes pair-key `[@example:V]` (a per-pair cascade
|
|
3190
|
+
// source, bound via per-card CascadeCounters scope) and
|
|
3191
|
+
// bit-header `[@example:V]` (cardset cascade) into the
|
|
3192
|
+
// per-cell `example` via `$cascade`. The aggregator in
|
|
3193
|
+
// `populate_variants` fires this rule ONCE per cell (BPG
|
|
3194
|
+
// `parseMatchMatrix` `exampleSide` precedence), avoiding
|
|
3195
|
+
// per-iter pollution.
|
|
3196
|
+
// @card scope-shift writes matrix-row container isExample.
|
|
2699
3197
|
key: ConfigKey.property_example,
|
|
2700
3198
|
exportJsonKey: [
|
|
2701
3199
|
{
|
|
2702
3200
|
"@keyonly": {
|
|
2703
|
-
cells: { $s: { isExample: true, example: "$parent
|
|
2704
|
-
"@bit": { isExample: true }
|
|
3201
|
+
cells: { $s: { isExample: true, example: "$parent" } },
|
|
3202
|
+
"@bit": { isExample: true },
|
|
3203
|
+
"@card": { isExample: true }
|
|
2705
3204
|
}
|
|
2706
3205
|
},
|
|
2707
3206
|
{
|
|
2708
|
-
"@absent"
|
|
3207
|
+
predicates: ["@absent", { $cascade: "*" }],
|
|
3208
|
+
rule: {
|
|
3209
|
+
cells: { $s: { isExample: true, example: "$cascade" } },
|
|
3210
|
+
"@bit": { isExample: true },
|
|
3211
|
+
"@card": { isExample: true }
|
|
3212
|
+
}
|
|
2709
3213
|
},
|
|
2710
|
-
{
|
|
3214
|
+
{
|
|
3215
|
+
"@absent": {
|
|
3216
|
+
cells: { $s: { isExample: true, example: "$parent" } },
|
|
3217
|
+
"@bit": { isExample: true },
|
|
3218
|
+
"@card": { isExample: true }
|
|
3219
|
+
}
|
|
3220
|
+
},
|
|
3221
|
+
{
|
|
3222
|
+
cells: { $s: { isExample: true, example: "$" } },
|
|
3223
|
+
"@bit": { isExample: true },
|
|
3224
|
+
"@card": { isExample: true }
|
|
3225
|
+
}
|
|
2711
3226
|
],
|
|
2712
3227
|
description: "Example text for the match matrix.",
|
|
2713
|
-
format: TagFormat.
|
|
3228
|
+
format: TagFormat.bitmarkText,
|
|
3229
|
+
nullable: true
|
|
2714
3230
|
},
|
|
2715
3231
|
{
|
|
3232
|
+
// PLAN-085 / R14: bare `[#]` on a value-side emits the
|
|
3233
|
+
// empty-string positional blank into `forValues` so the
|
|
3234
|
+
// array stays aligned with the cell positions (matches BPG
|
|
3235
|
+
// `extractHeadingCard`'s "all value-side headings present"
|
|
3236
|
+
// shape). The `@keyonly` rule is required for two reasons:
|
|
3237
|
+
// 1) it provides an emission for bare `[#]` (the `$` sigil
|
|
3238
|
+
// in the unconditional rule has no source for bare); and
|
|
3239
|
+
// 2) the presence of any `@keyonly` rule disables the
|
|
3240
|
+
// optimised-mode `should_suppress_outcome` strip (see
|
|
3241
|
+
// `forward_emit.rs`) so the resulting `forValues: [""]`
|
|
3242
|
+
// survives the all-natural-defaults check.
|
|
2716
3243
|
key: ConfigKey.tag_title,
|
|
2717
3244
|
description: "Title of the match matrix.",
|
|
2718
3245
|
format: TagFormat.plainText,
|
|
2719
3246
|
jsonKey: "^heading.forValues",
|
|
2720
|
-
exportJsonKey:
|
|
3247
|
+
exportJsonKey: [
|
|
3248
|
+
{ "@keyonly": { "@bit": { heading: { forValues: [""] } } } },
|
|
3249
|
+
{ "@bit": { heading: { forValues: ["$"] } } }
|
|
3250
|
+
]
|
|
2721
3251
|
},
|
|
2722
3252
|
{
|
|
2723
3253
|
key: ConfigKey.property_isCaseSensitive,
|
|
@@ -2766,6 +3296,35 @@ var CARDSETS = {
|
|
|
2766
3296
|
{
|
|
2767
3297
|
key: ConfigKey.group_standardTags,
|
|
2768
3298
|
description: "Standard tags for the statement."
|
|
3299
|
+
},
|
|
3300
|
+
{
|
|
3301
|
+
// PLAN-072: cascade-fired `@example` from the bit header
|
|
3302
|
+
// fills each statement entry's `example` with the entry's
|
|
3303
|
+
// own `isCorrect`, mirroring BPG's `pushExampleDownTree`
|
|
3304
|
+
// for the statements cardset. Per-array-item strip removes
|
|
3305
|
+
// `example: false` from incorrect entries so only correct
|
|
3306
|
+
// entries retain `example: true`.
|
|
3307
|
+
key: ConfigKey.property_example,
|
|
3308
|
+
exportJsonKey: [
|
|
3309
|
+
{
|
|
3310
|
+
"@keyonly": {
|
|
3311
|
+
isExample: true,
|
|
3312
|
+
example: true,
|
|
3313
|
+
"@bit": { isExample: true }
|
|
3314
|
+
}
|
|
3315
|
+
},
|
|
3316
|
+
{
|
|
3317
|
+
"@absent": {
|
|
3318
|
+
isExample: true,
|
|
3319
|
+
example: "$parent.isCorrect",
|
|
3320
|
+
"@bit": { isExample: true }
|
|
3321
|
+
}
|
|
3322
|
+
},
|
|
3323
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
3324
|
+
],
|
|
3325
|
+
description: "Example marker on the statement. Bare `[@example]` emits the boolean marker; valued `[@example:true|false]` emits the literal value. Cascade from bit-header fills `example` with the entry\u2019s own `isCorrect`.",
|
|
3326
|
+
format: TagFormat.boolean,
|
|
3327
|
+
nullable: true
|
|
2769
3328
|
}
|
|
2770
3329
|
],
|
|
2771
3330
|
bodyAllowed: false
|
|
@@ -2794,6 +3353,71 @@ var CARDSETS = {
|
|
|
2794
3353
|
{
|
|
2795
3354
|
key: ConfigKey.group_standardTags,
|
|
2796
3355
|
description: "Standard tags for the quiz."
|
|
3356
|
+
},
|
|
3357
|
+
{
|
|
3358
|
+
// ALIGN-EXAMPLE-CASCADE §3.2: variant-level cascade for
|
|
3359
|
+
// quiz choices is delegated to group_trueFalse's
|
|
3360
|
+
// chain-child `@example` (`+` chain with maxEmits:1 +
|
|
3361
|
+
// `@parent.isCorrect=true`). Variant-level `@absent`
|
|
3362
|
+
// would over-fire on subsequent correct choices and on
|
|
3363
|
+
// incorrect ones — drop it.
|
|
3364
|
+
key: ConfigKey.property_example,
|
|
3365
|
+
exportJsonKey: [
|
|
3366
|
+
{ "@keyonly": { isExample: true, "@bit": { isExample: true } } },
|
|
3367
|
+
{ isExample: true, "@bit": { isExample: true } }
|
|
3368
|
+
],
|
|
3369
|
+
description: "Example marker on the quiz choice (entry-local only; cascade handled by group_trueFalse chain-children).",
|
|
3370
|
+
format: TagFormat.boolean,
|
|
3371
|
+
nullable: true
|
|
3372
|
+
}
|
|
3373
|
+
],
|
|
3374
|
+
bodyAllowed: false
|
|
3375
|
+
}
|
|
3376
|
+
]
|
|
3377
|
+
}
|
|
3378
|
+
]
|
|
3379
|
+
},
|
|
3380
|
+
//
|
|
3381
|
+
// quiz (responses)
|
|
3382
|
+
//
|
|
3383
|
+
[CardSetConfigKey.quizResponses]: {
|
|
3384
|
+
jsonKey: "quizzes",
|
|
3385
|
+
exportJsonKey: { quizzes: "$" },
|
|
3386
|
+
sides: [
|
|
3387
|
+
{
|
|
3388
|
+
name: "responses",
|
|
3389
|
+
variants: [
|
|
3390
|
+
{
|
|
3391
|
+
jsonKey: null,
|
|
3392
|
+
tags: [
|
|
3393
|
+
{
|
|
3394
|
+
key: ConfigKey.group_trueFalseResponses,
|
|
3395
|
+
description: "Group for true/false questions."
|
|
3396
|
+
},
|
|
3397
|
+
{
|
|
3398
|
+
key: ConfigKey.group_standardTags,
|
|
3399
|
+
description: "Standard tags for the quiz."
|
|
3400
|
+
},
|
|
3401
|
+
{
|
|
3402
|
+
// PLAN-072: cascade-fired `@example` from the bit header
|
|
3403
|
+
// fills each response entry's `example` with the entry's
|
|
3404
|
+
// own `isCorrect`, mirroring BPG's `pushExampleDownTree`
|
|
3405
|
+
// for the quiz-responses cardset (statements-style).
|
|
3406
|
+
key: ConfigKey.property_example,
|
|
3407
|
+
exportJsonKey: [
|
|
3408
|
+
{ "@keyonly": { isExample: true, "@bit": { isExample: true } } },
|
|
3409
|
+
{
|
|
3410
|
+
"@absent": {
|
|
3411
|
+
isExample: true,
|
|
3412
|
+
example: "$parent.isCorrect",
|
|
3413
|
+
"@bit": { isExample: true }
|
|
3414
|
+
}
|
|
3415
|
+
},
|
|
3416
|
+
{ isExample: true, "@bit": { isExample: true } }
|
|
3417
|
+
],
|
|
3418
|
+
description: "Example marker on the quiz response; cascade from bit-header fills `example` with the entry\u2019s own `isCorrect`.",
|
|
3419
|
+
format: TagFormat.boolean,
|
|
3420
|
+
nullable: true
|
|
2797
3421
|
}
|
|
2798
3422
|
],
|
|
2799
3423
|
bodyAllowed: false
|
|
@@ -2819,6 +3443,73 @@ var CARDSETS = {
|
|
|
2819
3443
|
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
2820
3444
|
description: "Standard tags for lead, instruction, and hint."
|
|
2821
3445
|
},
|
|
3446
|
+
{
|
|
3447
|
+
// Feedback choices: `[+]` / `[-]` map to choice + `requireReason`
|
|
3448
|
+
// (NOT `isCorrect` like trueFalse). Shadow the inherited
|
|
3449
|
+
// `tag_true` / `tag_false` from `group_trueFalse` to emit the
|
|
3450
|
+
// feedback-specific shape.
|
|
3451
|
+
//
|
|
3452
|
+
// Chain @example: `[+ X][@example]` writes
|
|
3453
|
+
// `isExample/example` onto the X choice only. BPG's
|
|
3454
|
+
// `setIsExampleFlags` (Builder.ts:4041-4172) does NOT
|
|
3455
|
+
// iterate `cardNode.feedbacks` — so unlike multipleChoice,
|
|
3456
|
+
// there is no bubble to bit-level or feedbacks-level
|
|
3457
|
+
// isExample. Pattern omits `@bit` scope-shift accordingly.
|
|
3458
|
+
// No `@absent`/`@parent.isCorrect` cascade rules either:
|
|
3459
|
+
// BPG's `pushExampleDownTree` (Builder.ts:3777-3849) has
|
|
3460
|
+
// no feedback dispatch, so bit-header `[@example]` does
|
|
3461
|
+
// not flow into feedback choices.
|
|
3462
|
+
key: ConfigKey.tag_true,
|
|
3463
|
+
exportJsonKey: { choices: [{ choice: "$", requireReason: true }] },
|
|
3464
|
+
description: "Reason-required choice for feedback (`[+]`).",
|
|
3465
|
+
maxCount: Count.infinity,
|
|
3466
|
+
format: TagFormat.plainText,
|
|
3467
|
+
chain: [
|
|
3468
|
+
{
|
|
3469
|
+
key: ConfigKey.property_example,
|
|
3470
|
+
exportJsonKey: [
|
|
3471
|
+
{ "@keyonly": { isExample: true, example: true } },
|
|
3472
|
+
{ isExample: true, example: "$" }
|
|
3473
|
+
],
|
|
3474
|
+
description: "Per-choice example marker for feedback `[+]`.",
|
|
3475
|
+
format: TagFormat.boolean,
|
|
3476
|
+
maxCount: 1,
|
|
3477
|
+
nullable: true
|
|
3478
|
+
},
|
|
3479
|
+
{
|
|
3480
|
+
// `[+ X][! foo]` chains `!` (instruction) onto the
|
|
3481
|
+
// choice; same for `[?]` (hint), `[%]` (item),
|
|
3482
|
+
// `[%2]`/`[%3]`/`[%4]` (lead/pageNumber/marginNumber).
|
|
3483
|
+
// Mirrors group_trueFalse's `+` chain shape.
|
|
3484
|
+
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
3485
|
+
description: "Item, lead, page number, margin number, instruction and hint chained onto the feedback choice."
|
|
3486
|
+
}
|
|
3487
|
+
]
|
|
3488
|
+
},
|
|
3489
|
+
{
|
|
3490
|
+
key: ConfigKey.tag_false,
|
|
3491
|
+
exportJsonKey: { choices: [{ choice: "$", requireReason: false }] },
|
|
3492
|
+
description: "Reason-not-required choice for feedback (`[-]`).",
|
|
3493
|
+
maxCount: Count.infinity,
|
|
3494
|
+
format: TagFormat.plainText,
|
|
3495
|
+
chain: [
|
|
3496
|
+
{
|
|
3497
|
+
key: ConfigKey.property_example,
|
|
3498
|
+
exportJsonKey: [
|
|
3499
|
+
{ "@keyonly": { isExample: true, example: true } },
|
|
3500
|
+
{ isExample: true, example: "$" }
|
|
3501
|
+
],
|
|
3502
|
+
description: "Per-choice example marker for feedback `[-]`.",
|
|
3503
|
+
format: TagFormat.boolean,
|
|
3504
|
+
maxCount: 1,
|
|
3505
|
+
nullable: true
|
|
3506
|
+
},
|
|
3507
|
+
{
|
|
3508
|
+
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
3509
|
+
description: "Item, lead, page number, margin number, instruction and hint chained onto the feedback choice."
|
|
3510
|
+
}
|
|
3511
|
+
]
|
|
3512
|
+
},
|
|
2822
3513
|
{
|
|
2823
3514
|
key: ConfigKey.group_trueFalse,
|
|
2824
3515
|
description: "Group for true/false feedback."
|
|
@@ -2839,31 +3530,71 @@ var CARDSETS = {
|
|
|
2839
3530
|
name: "reason",
|
|
2840
3531
|
variants: [
|
|
2841
3532
|
{
|
|
3533
|
+
// PLAN: feedback.reason — Object-only variant pattern. Per
|
|
3534
|
+
// SYNTAX.md §8.1, sibling tag emissions do NOT auto-wrap
|
|
3535
|
+
// under `reason.*`; each tag here spells out the full path.
|
|
3536
|
+
// Body is a plain string (BPG `parseFeedback` uses
|
|
3537
|
+
// `cardBodyStr ?? ''` for `reason.text`).
|
|
2842
3538
|
jsonKey: "reason.text",
|
|
2843
3539
|
exportJsonKey: { reason: { text: "$" } },
|
|
3540
|
+
format: TextFormat2.plainText,
|
|
2844
3541
|
tags: [
|
|
2845
3542
|
{
|
|
2846
|
-
|
|
2847
|
-
|
|
3543
|
+
// Instruction lands under reason. Hint/item/lead/etc.
|
|
3544
|
+
// omitted: not exercised by any fixture and BPG's
|
|
3545
|
+
// `parseFeedback` spreads only the tags actually present
|
|
3546
|
+
// — adding the rest can be done lazily as fixtures
|
|
3547
|
+
// require them, with the same `{reason: {…}}` wrapper.
|
|
3548
|
+
key: ConfigKey.tag_instruction,
|
|
3549
|
+
exportJsonKey: { reason: { instruction: "$" } },
|
|
3550
|
+
description: "Instruction for the feedback reason.",
|
|
3551
|
+
format: TagFormat.bitmarkText
|
|
2848
3552
|
},
|
|
2849
3553
|
{
|
|
2850
3554
|
key: ConfigKey.property_reasonableNumOfChars,
|
|
2851
3555
|
exportJsonKey: [
|
|
2852
|
-
{ "@absent": { reasonableNumOfChars: "$ancestor" } },
|
|
2853
|
-
{ reasonableNumOfChars: "$" }
|
|
3556
|
+
{ "@absent": { reason: { reasonableNumOfChars: "$ancestor" } } },
|
|
3557
|
+
{ reason: { reasonableNumOfChars: "$" } }
|
|
2854
3558
|
],
|
|
2855
3559
|
description: "Property for reasonable number of characters.",
|
|
2856
3560
|
format: TagFormat.number
|
|
2857
3561
|
},
|
|
2858
3562
|
{
|
|
3563
|
+
// ALIGN-EXAMPLE-CASCADE §3.9: `feedback.reason` is NOT in
|
|
3564
|
+
// BPG's `pushExampleDownTree` dispatch — no bit-header
|
|
3565
|
+
// cascade. Local emission only.
|
|
3566
|
+
//
|
|
3567
|
+
// Both `reason.example` and `feedbacks[].example` are
|
|
3568
|
+
// `TextAst` per BPG's schema — paragraph arrays. Format
|
|
3569
|
+
// here is `bitmarkText` so `$` coerces to a paragraph
|
|
3570
|
+
// array at both write sites.
|
|
2859
3571
|
key: ConfigKey.property_example,
|
|
2860
3572
|
exportJsonKey: [
|
|
2861
|
-
{
|
|
2862
|
-
|
|
2863
|
-
|
|
3573
|
+
{
|
|
3574
|
+
// Bare `[@example]`: reason.example is TextAst — BPG
|
|
3575
|
+
// coerces the boolean default to a paragraph
|
|
3576
|
+
// containing the string "true". feedbacks[].example
|
|
3577
|
+
// is not TextAst, stays a boolean. Two literal writes.
|
|
3578
|
+
"@keyonly": {
|
|
3579
|
+
reason: {
|
|
3580
|
+
isExample: true,
|
|
3581
|
+
example: [{ type: "paragraph", content: [{ text: "true", type: "text" }] }]
|
|
3582
|
+
},
|
|
3583
|
+
isExample: true,
|
|
3584
|
+
example: true,
|
|
3585
|
+
"@bit": { isExample: true }
|
|
3586
|
+
}
|
|
3587
|
+
},
|
|
3588
|
+
{
|
|
3589
|
+
reason: { isExample: true, example: "$" },
|
|
3590
|
+
isExample: true,
|
|
3591
|
+
example: "$",
|
|
3592
|
+
"@bit": { isExample: true }
|
|
3593
|
+
}
|
|
2864
3594
|
],
|
|
2865
|
-
description: "Example text for the feedback.",
|
|
2866
|
-
format: TagFormat.
|
|
3595
|
+
description: "Example text for the feedback reason (entry-local only; no bit-header cascade).",
|
|
3596
|
+
format: TagFormat.bitmarkText,
|
|
3597
|
+
nullable: true
|
|
2867
3598
|
},
|
|
2868
3599
|
{
|
|
2869
3600
|
key: ConfigKey.tag_title,
|
|
@@ -2927,6 +3658,39 @@ var CARDSETS = {
|
|
|
2927
3658
|
{
|
|
2928
3659
|
key: ConfigKey.group_standardTags,
|
|
2929
3660
|
description: "Standard tags for the question."
|
|
3661
|
+
},
|
|
3662
|
+
{
|
|
3663
|
+
// ALIGN-EXAMPLE-CASCADE §3.10: `questions` is NOT in BPG's
|
|
3664
|
+
// `pushExampleDownTree` dispatch — bit-header `[@example]`
|
|
3665
|
+
// does not propagate `example`/`isExample` onto question
|
|
3666
|
+
// entries. Only entry-local `[@example]` fires.
|
|
3667
|
+
//
|
|
3668
|
+
// BPG's `BaseBuilder.toExample` for interview questions
|
|
3669
|
+
// (Builder.ts:2575,2588) uses `data.__sampleSolutionAst`
|
|
3670
|
+
// as `defaultExample` — bare `[@example]` (which sets
|
|
3671
|
+
// `__isDefaultExample=true, example=undefined`) emits the
|
|
3672
|
+
// parsed sample-solution AST in the `example` slot. The
|
|
3673
|
+
// `@keyonly` rule mirrors that runtime behaviour via
|
|
3674
|
+
// `$parent.sampleSolution` — the chain-walker's post-walk
|
|
3675
|
+
// retry resolves this once the sibling `[@sampleSolution]`
|
|
3676
|
+
// chain has merged into the shared scope (interview's
|
|
3677
|
+
// `[@example]` typically precedes `[@sampleSolution]` in
|
|
3678
|
+
// source). Format coercion turns the resolved string
|
|
3679
|
+
// value into a PM tree.
|
|
3680
|
+
key: ConfigKey.property_example,
|
|
3681
|
+
exportJsonKey: [
|
|
3682
|
+
{
|
|
3683
|
+
"@keyonly": {
|
|
3684
|
+
isExample: true,
|
|
3685
|
+
example: "$parent.sampleSolution",
|
|
3686
|
+
"@bit": { isExample: true }
|
|
3687
|
+
}
|
|
3688
|
+
},
|
|
3689
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
3690
|
+
],
|
|
3691
|
+
description: "Example marker for the question (entry-local only; no bit-header cascade).",
|
|
3692
|
+
format: TagFormat.bitmarkText,
|
|
3693
|
+
nullable: true
|
|
2930
3694
|
}
|
|
2931
3695
|
]
|
|
2932
3696
|
}
|
|
@@ -2953,14 +3717,16 @@ var CARDSETS = {
|
|
|
2953
3717
|
description: "Standard tags for lead, instruction, and hint."
|
|
2954
3718
|
},
|
|
2955
3719
|
{
|
|
3720
|
+
// ALIGN-EXAMPLE-CASCADE §3.11: `elements` is NOT in BPG's
|
|
3721
|
+
// `pushExampleDownTree` dispatch — no bit-header cascade.
|
|
2956
3722
|
key: ConfigKey.property_example,
|
|
2957
3723
|
exportJsonKey: [
|
|
2958
3724
|
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2959
|
-
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2960
3725
|
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2961
3726
|
],
|
|
2962
|
-
description: "Example text for the element.",
|
|
2963
|
-
format: TagFormat.plainText
|
|
3727
|
+
description: "Example text for the element (entry-local only; no bit-header cascade).",
|
|
3728
|
+
format: TagFormat.plainText,
|
|
3729
|
+
nullable: true
|
|
2964
3730
|
}
|
|
2965
3731
|
],
|
|
2966
3732
|
repeatCount: Count.infinity
|
|
@@ -2972,9 +3738,57 @@ var CARDSETS = {
|
|
|
2972
3738
|
//
|
|
2973
3739
|
// table
|
|
2974
3740
|
//
|
|
3741
|
+
// PLAN-079: supports BOTH source formats and collapses to the same basic
|
|
3742
|
+
// output (`{ columns?, data, columnWidths? }`):
|
|
3743
|
+
//
|
|
3744
|
+
// 1. Basic format — first card carries `[#X]` markers (fire `tag_title`
|
|
3745
|
+
// → `^table.columns[]`); subsequent cards are body rows. Cards have
|
|
3746
|
+
// no qualifier, so they route to the `table-body` (default) section.
|
|
3747
|
+
//
|
|
3748
|
+
// 2. Extended format — `==== table-header ====` / `==== table-footer ====`
|
|
3749
|
+
// qualifiers. The `table-header` section uses `@cardIndex=0` to route
|
|
3750
|
+
// the first header card's cells to `columns` and demote remaining
|
|
3751
|
+
// header cards (rows) into `data`. Per-cell `[@tableRowSpan]` /
|
|
3752
|
+
// `[@tableScope]` / `[@tableCellType]` tags are valid-but-unmapped
|
|
3753
|
+
// here (no per-cell metadata in basic shape) — declared on the cell
|
|
3754
|
+
// variant below.
|
|
3755
|
+
//
|
|
2975
3756
|
[CardSetConfigKey.table]: {
|
|
2976
|
-
jsonKey:
|
|
2977
|
-
exportJsonKey:
|
|
3757
|
+
jsonKey: null,
|
|
3758
|
+
exportJsonKey: null,
|
|
3759
|
+
sections: {
|
|
3760
|
+
"table-header": {
|
|
3761
|
+
// First header card's row of cells → `columns`. Subsequent header
|
|
3762
|
+
// cards' rows → `data` (per-card append; `[$]` shape because
|
|
3763
|
+
// per-card emission fires once per card, multiple fires deep_merge-
|
|
3764
|
+
// append). PLAN-079 + PLAN-080: predicates inside exportJsonKey
|
|
3765
|
+
// both ROUTE cards to this section (when no explicit `==== table-
|
|
3766
|
+
// header ====` qualifier) and SELECT the rule body once routed.
|
|
3767
|
+
// First rule = first card with any `[#]` → `columns`; second rule
|
|
3768
|
+
// = subsequent cards in this section → `data` (per-row append).
|
|
3769
|
+
jsonKey: "table.columns",
|
|
3770
|
+
exportJsonKey: [
|
|
3771
|
+
{
|
|
3772
|
+
predicates: [{ "@cardIndex": 0 }, { "@variantHasTag": "#" }],
|
|
3773
|
+
rule: { "@bit": { table: { columns: "$" } } }
|
|
3774
|
+
},
|
|
3775
|
+
{ "@cardIndex=0": { "@bit": { table: { columns: "$" } } } },
|
|
3776
|
+
{ "@bit": { table: { data: ["$"] } } }
|
|
3777
|
+
]
|
|
3778
|
+
},
|
|
3779
|
+
"table-body": {
|
|
3780
|
+
// Batched emission: `$` is the entire 2D array of body card rows.
|
|
3781
|
+
// This is the default section (no qualifier → cards land here).
|
|
3782
|
+
jsonKey: "table.data",
|
|
3783
|
+
exportJsonKey: { "@bit": { table: { data: "$" } } },
|
|
3784
|
+
isDefault: true
|
|
3785
|
+
},
|
|
3786
|
+
"table-footer": {
|
|
3787
|
+
// Batched emission: `$` is the entire 2D array of footer rows.
|
|
3788
|
+
jsonKey: "table.data",
|
|
3789
|
+
exportJsonKey: { "@bit": { table: { data: "$" } } }
|
|
3790
|
+
}
|
|
3791
|
+
},
|
|
2978
3792
|
sides: [
|
|
2979
3793
|
{
|
|
2980
3794
|
name: "cell",
|
|
@@ -3015,6 +3829,8 @@ var CARDSETS = {
|
|
|
3015
3829
|
},
|
|
3016
3830
|
{
|
|
3017
3831
|
key: ConfigKey.property_tableColWidth,
|
|
3832
|
+
jsonKey: "^table.columnWidths[{s}]",
|
|
3833
|
+
exportJsonKey: { "@bit": { table: { columnWidths: { $s: "$" } } } },
|
|
3018
3834
|
description: "Width for the column.",
|
|
3019
3835
|
format: TagFormat.number
|
|
3020
3836
|
}
|
|
@@ -3033,7 +3849,22 @@ var CARDSETS = {
|
|
|
3033
3849
|
sections: {
|
|
3034
3850
|
"table-header": {
|
|
3035
3851
|
jsonKey: "table.header.rows",
|
|
3036
|
-
|
|
3852
|
+
// PLAN-080: section dispatch via predicates inside exportJsonKey.
|
|
3853
|
+
// Cards with no explicit `==== table-header ====` qualifier
|
|
3854
|
+
// route here if FIRST CARD contains any `[#]` tag in any
|
|
3855
|
+
// variant. Explicit-qualifier cards fall through to the
|
|
3856
|
+
// unconditional fallback rule. Both rules use the per-card
|
|
3857
|
+
// append shape `rows: ['$']` — the runtime triggers per-card
|
|
3858
|
+
// emission whenever the pattern carries predicates, and each
|
|
3859
|
+
// fire appends a single-element array (multi-fire deep-merge
|
|
3860
|
+
// concatenates).
|
|
3861
|
+
exportJsonKey: [
|
|
3862
|
+
{
|
|
3863
|
+
predicates: [{ "@cardIndex": 0 }, { "@variantHasTag": "#" }],
|
|
3864
|
+
rule: { table: { header: { rows: ["$"] } } }
|
|
3865
|
+
},
|
|
3866
|
+
{ table: { header: { rows: ["$"] } } }
|
|
3867
|
+
],
|
|
3037
3868
|
sideJsonKey: "cells[{s}]|set(title=true)",
|
|
3038
3869
|
sideExportJsonKey: { cells: { $s: { title: true, $: "$" } } }
|
|
3039
3870
|
},
|
|
@@ -3066,13 +3897,13 @@ var CARDSETS = {
|
|
|
3066
3897
|
},
|
|
3067
3898
|
{
|
|
3068
3899
|
key: ConfigKey.tag_title,
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
},
|
|
3075
|
-
description: "Title of the table cell."
|
|
3900
|
+
// PLAN-080: the `[#]` tag writes its text to the cell's
|
|
3901
|
+
// `content` field. When the card is in the `table-header`
|
|
3902
|
+
// section, the section's sideExportJsonKey wraps each
|
|
3903
|
+
// cell with `title: true` automatically.
|
|
3904
|
+
jsonKey: "content",
|
|
3905
|
+
exportJsonKey: { content: "$" },
|
|
3906
|
+
description: "Title text of the table cell (header rows)."
|
|
3076
3907
|
},
|
|
3077
3908
|
{
|
|
3078
3909
|
key: ConfigKey.property_tableCellType,
|
|
@@ -3104,6 +3935,9 @@ var CARDSETS = {
|
|
|
3104
3935
|
},
|
|
3105
3936
|
{
|
|
3106
3937
|
key: ConfigKey.property_tableColWidth,
|
|
3938
|
+
// PLAN-080: write to the cell's `colwidth` field.
|
|
3939
|
+
jsonKey: "colwidth",
|
|
3940
|
+
exportJsonKey: { colwidth: "$" },
|
|
3107
3941
|
description: "Width for the column.",
|
|
3108
3942
|
format: TagFormat.number
|
|
3109
3943
|
}
|
|
@@ -3141,7 +3975,8 @@ var CARDSETS = {
|
|
|
3141
3975
|
{
|
|
3142
3976
|
key: ConfigKey.resource_audio,
|
|
3143
3977
|
description: "Audio resource for the pronunciation table.",
|
|
3144
|
-
jsonKey: "audio"
|
|
3978
|
+
jsonKey: "audio",
|
|
3979
|
+
exportJsonKey: { audio: { type: "audio", audio: { src: "$" } } }
|
|
3145
3980
|
}
|
|
3146
3981
|
],
|
|
3147
3982
|
repeatCount: Count.infinity
|
|
@@ -3212,14 +4047,17 @@ var CARDSETS = {
|
|
|
3212
4047
|
format: TagFormat.plainText
|
|
3213
4048
|
},
|
|
3214
4049
|
{
|
|
4050
|
+
// ALIGN-EXAMPLE-CASCADE §3.12: `bot-action-responses` is
|
|
4051
|
+
// NOT in BPG's `pushExampleDownTree` dispatch — no bit-
|
|
4052
|
+
// header cascade.
|
|
3215
4053
|
key: ConfigKey.property_example,
|
|
3216
4054
|
exportJsonKey: [
|
|
3217
4055
|
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
3218
|
-
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
3219
4056
|
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
3220
4057
|
],
|
|
3221
|
-
description: "Example text for the bot action response.",
|
|
3222
|
-
format: TagFormat.plainText
|
|
4058
|
+
description: "Example text for the bot action response (entry-local only; no bit-header cascade).",
|
|
4059
|
+
format: TagFormat.plainText,
|
|
4060
|
+
nullable: true
|
|
3223
4061
|
}
|
|
3224
4062
|
]
|
|
3225
4063
|
}
|
|
@@ -3285,6 +4123,38 @@ var CARDSETS = {
|
|
|
3285
4123
|
]
|
|
3286
4124
|
},
|
|
3287
4125
|
//
|
|
4126
|
+
// page-footer-sections — mirrors example-bit-list but emits cards under
|
|
4127
|
+
// `sections` instead of `listItems`. BPG's JsonGenerator special-cases
|
|
4128
|
+
// BitType.pageFooter in code; this dedicated cardSet expresses the same
|
|
4129
|
+
// routing as pure config so the Rust serializer can pick it up.
|
|
4130
|
+
//
|
|
4131
|
+
[CardSetConfigKey.pageFooterSections]: {
|
|
4132
|
+
jsonKey: "sections",
|
|
4133
|
+
exportJsonKey: { sections: "$" },
|
|
4134
|
+
sides: [
|
|
4135
|
+
{
|
|
4136
|
+
name: "item",
|
|
4137
|
+
variants: [
|
|
4138
|
+
{
|
|
4139
|
+
jsonKey: "body",
|
|
4140
|
+
exportJsonKey: { body: "$" },
|
|
4141
|
+
tags: [
|
|
4142
|
+
{
|
|
4143
|
+
key: ConfigKey.group_standardTags,
|
|
4144
|
+
description: "Standard tags for page-footer section bits."
|
|
4145
|
+
},
|
|
4146
|
+
{
|
|
4147
|
+
exportJsonKey: { title: "$" },
|
|
4148
|
+
key: ConfigKey.tag_title,
|
|
4149
|
+
description: "Title of the page-footer section."
|
|
4150
|
+
}
|
|
4151
|
+
]
|
|
4152
|
+
}
|
|
4153
|
+
]
|
|
4154
|
+
}
|
|
4155
|
+
]
|
|
4156
|
+
},
|
|
4157
|
+
//
|
|
3288
4158
|
// ingredients
|
|
3289
4159
|
//
|
|
3290
4160
|
[CardSetConfigKey.ingredients]: {
|
|
@@ -3615,7 +4485,11 @@ var GROUPS = {
|
|
|
3615
4485
|
{
|
|
3616
4486
|
key: ConfigKey.property_groupTag,
|
|
3617
4487
|
jsonKey: "groupTag.name",
|
|
3618
|
-
|
|
4488
|
+
// PLAN-063: starter-array shape with `@id` declares entity-merge
|
|
4489
|
+
// semantics — repeated `[@groupTag: name]` occurrences with the
|
|
4490
|
+
// same value fold into one entry; chained `@tag` children's
|
|
4491
|
+
// arrays merge as sets.
|
|
4492
|
+
exportJsonKey: { groupTag: [{ "@id": "name", name: "$" }] },
|
|
3619
4493
|
description: "The group tag(s) for the bit",
|
|
3620
4494
|
format: TagFormat.plainText,
|
|
3621
4495
|
maxCount: Count.infinity,
|
|
@@ -3788,6 +4662,24 @@ var GROUPS = {
|
|
|
3788
4662
|
}
|
|
3789
4663
|
]
|
|
3790
4664
|
},
|
|
4665
|
+
[ConfigKey.group_standardInstructionHint]: {
|
|
4666
|
+
type: GroupConfigType.standard,
|
|
4667
|
+
description: "Standard group for instruction and hint tags only (no item/lead/pageNumber/marginNumber chain). Used by sides where item/lead are owned by another side (e.g. matchPairs value-side, which BPG strips item/lead from at sideIdx > 0).",
|
|
4668
|
+
tags: [
|
|
4669
|
+
{
|
|
4670
|
+
exportJsonKey: { instruction: "$" },
|
|
4671
|
+
key: ConfigKey.tag_instruction,
|
|
4672
|
+
name: "Instruction",
|
|
4673
|
+
description: "The instruction for the bit"
|
|
4674
|
+
},
|
|
4675
|
+
{
|
|
4676
|
+
exportJsonKey: { hint: "$" },
|
|
4677
|
+
key: ConfigKey.tag_hint,
|
|
4678
|
+
name: "Hint",
|
|
4679
|
+
description: "The hint for the bit"
|
|
4680
|
+
}
|
|
4681
|
+
]
|
|
4682
|
+
},
|
|
3791
4683
|
[ConfigKey.group_standardTags]: {
|
|
3792
4684
|
type: GroupConfigType.standard,
|
|
3793
4685
|
description: "Standard tags which apply to MOST (but not all) bits",
|
|
@@ -3801,17 +4693,45 @@ var GROUPS = {
|
|
|
3801
4693
|
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
3802
4694
|
},
|
|
3803
4695
|
{
|
|
3804
|
-
// Bit-level example tag
|
|
3805
|
-
//
|
|
3806
|
-
//
|
|
4696
|
+
// Bit-level example tag (default — non-`isTopLevelExample` bits):
|
|
4697
|
+
// - bare `[@example]`: consume only (records cascade marker, emits
|
|
4698
|
+
// nothing at bit-level — OLD parser scrubs bit-level isExample
|
|
4699
|
+
// for bits NOT in its `isTopLevelExample` allow-list).
|
|
4700
|
+
// - synthesized (descendant cascade): still emits `isExample: true`
|
|
4701
|
+
// at the descendant scope.
|
|
4702
|
+
// - valued `[@example:text]`: emit the example value only; bit-level
|
|
4703
|
+
// `isExample` is left to bubble from descendants.
|
|
4704
|
+
// Bits in the OLD parser's allow-list (flashcard, definitionList,
|
|
4705
|
+
// sequence, multipleChoice, trueFalse, cloze*, mark, interview,
|
|
4706
|
+
// matchMatrix, match, essay, etc.) override this entry with the
|
|
4707
|
+
// legacy emit-isExample shape.
|
|
3807
4708
|
key: ConfigKey.property_example,
|
|
3808
|
-
exportJsonKey: [
|
|
3809
|
-
{ "@keyonly": { isExample: true } },
|
|
3810
|
-
{ "@absent": { isExample: true } },
|
|
3811
|
-
{ isExample: true, example: "$" }
|
|
3812
|
-
],
|
|
4709
|
+
exportJsonKey: [{ "@keyonly": {} }, { "@absent": { isExample: true } }, { example: "$" }],
|
|
3813
4710
|
description: "The example(s) for the bit",
|
|
3814
|
-
format: TagFormat.bitmarkText
|
|
4711
|
+
format: TagFormat.bitmarkText,
|
|
4712
|
+
nullable: true
|
|
4713
|
+
}
|
|
4714
|
+
]
|
|
4715
|
+
},
|
|
4716
|
+
[ConfigKey.group_standardTagsNoItemLead]: {
|
|
4717
|
+
type: GroupConfigType.standard,
|
|
4718
|
+
description: "Standard tags mirror of group_standardTags but excluding the item/lead/pageNumber/marginNumber chain. Used by sides where item/lead are owned by another side (e.g. matchPairs value-side \u2014 BPG `parseMatchPairs` strips tags.item / tags.lead for sideIdx > 0).",
|
|
4719
|
+
tags: [
|
|
4720
|
+
{
|
|
4721
|
+
key: ConfigKey.group_standardAllBits,
|
|
4722
|
+
description: "All standard tags which apply to all bits"
|
|
4723
|
+
},
|
|
4724
|
+
{
|
|
4725
|
+
key: ConfigKey.group_standardInstructionHint,
|
|
4726
|
+
description: "Instruction and hint tags only (no item/lead chain)."
|
|
4727
|
+
},
|
|
4728
|
+
{
|
|
4729
|
+
// Mirror of group_standardTags property_example entry.
|
|
4730
|
+
key: ConfigKey.property_example,
|
|
4731
|
+
exportJsonKey: [{ "@keyonly": {} }, { "@absent": { isExample: true } }, { example: "$" }],
|
|
4732
|
+
description: "The example(s) for the bit",
|
|
4733
|
+
format: TagFormat.bitmarkText,
|
|
4734
|
+
nullable: true
|
|
3815
4735
|
}
|
|
3816
4736
|
]
|
|
3817
4737
|
},
|
|
@@ -3876,8 +4796,8 @@ var GROUPS = {
|
|
|
3876
4796
|
tags: [
|
|
3877
4797
|
{
|
|
3878
4798
|
key: ConfigKey.property_person,
|
|
3879
|
-
jsonKey: "
|
|
3880
|
-
exportJsonKey: {
|
|
4799
|
+
jsonKey: "person.name",
|
|
4800
|
+
exportJsonKey: { person: { name: "$" } },
|
|
3881
4801
|
description: "A person",
|
|
3882
4802
|
format: TagFormat.plainText,
|
|
3883
4803
|
chain: [
|
|
@@ -3897,8 +4817,8 @@ var GROUPS = {
|
|
|
3897
4817
|
{
|
|
3898
4818
|
// Deprecated (parter renamed to person)
|
|
3899
4819
|
key: ConfigKey.property_partner,
|
|
3900
|
-
jsonKey: "
|
|
3901
|
-
exportJsonKey: {
|
|
4820
|
+
jsonKey: "person.name",
|
|
4821
|
+
exportJsonKey: { person: { name: "$" } },
|
|
3902
4822
|
description: "A partner",
|
|
3903
4823
|
format: TagFormat.plainText,
|
|
3904
4824
|
chain: [
|
|
@@ -3922,7 +4842,9 @@ var GROUPS = {
|
|
|
3922
4842
|
description: "Gap chain",
|
|
3923
4843
|
tags: [
|
|
3924
4844
|
{
|
|
3925
|
-
exportJsonKey: {
|
|
4845
|
+
exportJsonKey: {
|
|
4846
|
+
"@body-inline": { type: "gap", attrs: { solutions: ["$"] } }
|
|
4847
|
+
},
|
|
3926
4848
|
key: ConfigKey.tag_gap,
|
|
3927
4849
|
description: "The value of a gap in the content",
|
|
3928
4850
|
maxCount: Count.infinity,
|
|
@@ -3941,27 +4863,37 @@ var GROUPS = {
|
|
|
3941
4863
|
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
3942
4864
|
},
|
|
3943
4865
|
{
|
|
4866
|
+
// BPG ExampleTagContentProcessor.ts:91-95 reads
|
|
4867
|
+
// `__solutionsAst[length - 1]` — the solution closest to the
|
|
4868
|
+
// `[@example]` tag at parse time. Our chain accumulator is built
|
|
4869
|
+
// incrementally (scope.rs `parent.merge` after each tag), so at
|
|
4870
|
+
// the moment `[@example]` fires, `solutions` only contains the
|
|
4871
|
+
// preceding `[_x]` entries — `[-1]` resolves to the closest one.
|
|
4872
|
+
// E.g. `[_cloze][_gap][@example]` → solutions=["cloze","gap"],
|
|
4873
|
+
// `[-1]`="gap"; `[_solution][@example][_problem]` →
|
|
4874
|
+
// solutions=["solution"], `[-1]`="solution".
|
|
3944
4875
|
key: ConfigKey.property_example,
|
|
3945
4876
|
jsonKey: "example",
|
|
3946
4877
|
exportJsonKey: [
|
|
3947
4878
|
{
|
|
3948
4879
|
"@keyonly": {
|
|
3949
4880
|
isExample: true,
|
|
3950
|
-
example: "$parent.solutions[
|
|
4881
|
+
example: "$parent.solutions[-1]",
|
|
3951
4882
|
"@bit": { isExample: true }
|
|
3952
4883
|
}
|
|
3953
4884
|
},
|
|
3954
4885
|
{
|
|
3955
4886
|
"@absent": {
|
|
3956
4887
|
isExample: true,
|
|
3957
|
-
example: "$parent.solutions[
|
|
4888
|
+
example: "$parent.solutions[-1]",
|
|
3958
4889
|
"@bit": { isExample: true }
|
|
3959
4890
|
}
|
|
3960
4891
|
},
|
|
3961
4892
|
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
3962
4893
|
],
|
|
3963
4894
|
description: "An example for the gap",
|
|
3964
|
-
format: TagFormat.bitmarkText
|
|
4895
|
+
format: TagFormat.bitmarkText,
|
|
4896
|
+
nullable: true
|
|
3965
4897
|
},
|
|
3966
4898
|
{
|
|
3967
4899
|
key: ConfigKey.property_isCaseSensitive,
|
|
@@ -4010,6 +4942,99 @@ var GROUPS = {
|
|
|
4010
4942
|
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
4011
4943
|
},
|
|
4012
4944
|
{
|
|
4945
|
+
// PLAN-072 + ALIGN-EXAMPLE-CASCADE §3.2: cascade-fired
|
|
4946
|
+
// `@example` from the bit header marks EVERY choice with
|
|
4947
|
+
// `isExample: true` (matches BPG output), but only the
|
|
4948
|
+
// FIRST correct choice gets `example: true` (BPG
|
|
4949
|
+
// `fillBooleanExample(..., firstCorrectOnly=true)`).
|
|
4950
|
+
//
|
|
4951
|
+
// Two `@absent` rules, picked first-match-wins:
|
|
4952
|
+
// 1. Verbose form gated on `@parent.isCorrect=true` with
|
|
4953
|
+
// `maxEmits:1` — fires once for the first correct.
|
|
4954
|
+
// 2. Plain `@absent` — fires on every remaining entry
|
|
4955
|
+
// (subsequent corrects and all incorrects), emitting
|
|
4956
|
+
// `isExample` only.
|
|
4957
|
+
// The `serialize_cards` cascade-counter scope holds the cap
|
|
4958
|
+
// across the whole cardset.
|
|
4959
|
+
// @card scope-shift writes choice-container isExample (e.g.
|
|
4960
|
+
// `quizzes[i].isExample`) — was previously provided by the
|
|
4961
|
+
// universal `bubble_is_example` post-walk, which was removed
|
|
4962
|
+
// in favour of explicit per-tag config. SYNTAX.md §8.
|
|
4963
|
+
key: ConfigKey.property_example,
|
|
4964
|
+
jsonKey: "example",
|
|
4965
|
+
exportJsonKey: [
|
|
4966
|
+
{
|
|
4967
|
+
"@keyonly": {
|
|
4968
|
+
isExample: true,
|
|
4969
|
+
example: "$parent.isCorrect",
|
|
4970
|
+
"@bit": { isExample: true },
|
|
4971
|
+
"@card": { isExample: true }
|
|
4972
|
+
}
|
|
4973
|
+
},
|
|
4974
|
+
{
|
|
4975
|
+
predicates: ["@absent", { "@parent.isCorrect": true }],
|
|
4976
|
+
maxEmits: 1,
|
|
4977
|
+
rule: {
|
|
4978
|
+
isExample: true,
|
|
4979
|
+
example: true,
|
|
4980
|
+
"@bit": { isExample: true },
|
|
4981
|
+
"@card": { isExample: true }
|
|
4982
|
+
}
|
|
4983
|
+
},
|
|
4984
|
+
{
|
|
4985
|
+
"@absent": {
|
|
4986
|
+
isExample: true,
|
|
4987
|
+
"@bit": { isExample: true },
|
|
4988
|
+
"@card": { isExample: true }
|
|
4989
|
+
}
|
|
4990
|
+
},
|
|
4991
|
+
{
|
|
4992
|
+
isExample: true,
|
|
4993
|
+
example: "$",
|
|
4994
|
+
"@bit": { isExample: true },
|
|
4995
|
+
"@card": { isExample: true }
|
|
4996
|
+
}
|
|
4997
|
+
],
|
|
4998
|
+
description: "An example for the true/false statement/question",
|
|
4999
|
+
format: TagFormat.boolean,
|
|
5000
|
+
nullable: true
|
|
5001
|
+
}
|
|
5002
|
+
]
|
|
5003
|
+
},
|
|
5004
|
+
{
|
|
5005
|
+
exportJsonKey: { choices: [{ choice: "$", isCorrect: false }] },
|
|
5006
|
+
key: ConfigKey.tag_false,
|
|
5007
|
+
description: "False value for a true/false statement/question",
|
|
5008
|
+
maxCount: Count.infinity,
|
|
5009
|
+
format: TagFormat.plainText,
|
|
5010
|
+
chain: [
|
|
5011
|
+
{
|
|
5012
|
+
key: ConfigKey.tag_true,
|
|
5013
|
+
jsonKey: "choices[]|set(isCorrect=true)",
|
|
5014
|
+
exportJsonKey: { choices: [{ choice: "$", isCorrect: true }] },
|
|
5015
|
+
description: "True values for a true/false statement/question",
|
|
5016
|
+
maxCount: Count.infinity,
|
|
5017
|
+
format: TagFormat.plainText
|
|
5018
|
+
},
|
|
5019
|
+
{
|
|
5020
|
+
key: ConfigKey.tag_false,
|
|
5021
|
+
jsonKey: "choices[]|set(isCorrect=false)",
|
|
5022
|
+
exportJsonKey: { choices: [{ choice: "$", isCorrect: false }] },
|
|
5023
|
+
description: "False values for a true/false statement/question",
|
|
5024
|
+
maxCount: Count.infinity,
|
|
5025
|
+
format: TagFormat.plainText
|
|
5026
|
+
},
|
|
5027
|
+
{
|
|
5028
|
+
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
5029
|
+
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
5030
|
+
},
|
|
5031
|
+
{
|
|
5032
|
+
// PLAN-072 + ALIGN-EXAMPLE-CASCADE §3.2: cascade-fired
|
|
5033
|
+
// `@example` on a `-` (incorrect choice) emits
|
|
5034
|
+
// `isExample: true` only — no `example` (BPG fixtures show
|
|
5035
|
+
// `isExample` on every choice, but `example` only on the
|
|
5036
|
+
// first correct via firstCorrectOnly cascade).
|
|
5037
|
+
// @card scope-shift writes choice-container isExample.
|
|
4013
5038
|
key: ConfigKey.property_example,
|
|
4014
5039
|
jsonKey: "example",
|
|
4015
5040
|
exportJsonKey: [
|
|
@@ -4017,25 +5042,99 @@ var GROUPS = {
|
|
|
4017
5042
|
"@keyonly": {
|
|
4018
5043
|
isExample: true,
|
|
4019
5044
|
example: "$parent.isCorrect",
|
|
4020
|
-
"@bit": { isExample: true }
|
|
5045
|
+
"@bit": { isExample: true },
|
|
5046
|
+
"@card": { isExample: true }
|
|
4021
5047
|
}
|
|
4022
5048
|
},
|
|
4023
5049
|
{
|
|
4024
5050
|
"@absent": {
|
|
5051
|
+
isExample: true,
|
|
5052
|
+
"@bit": { isExample: true },
|
|
5053
|
+
"@card": { isExample: true }
|
|
5054
|
+
}
|
|
5055
|
+
},
|
|
5056
|
+
{
|
|
5057
|
+
isExample: true,
|
|
5058
|
+
example: "$",
|
|
5059
|
+
"@bit": { isExample: true },
|
|
5060
|
+
"@card": { isExample: true }
|
|
5061
|
+
}
|
|
5062
|
+
],
|
|
5063
|
+
description: "An example for the true/false statement/question (incorrect entry \u2014 isExample only on cascade)",
|
|
5064
|
+
format: TagFormat.boolean,
|
|
5065
|
+
nullable: true
|
|
5066
|
+
}
|
|
5067
|
+
]
|
|
5068
|
+
}
|
|
5069
|
+
]
|
|
5070
|
+
},
|
|
5071
|
+
[ConfigKey.group_trueFalseResponses]: {
|
|
5072
|
+
type: GroupConfigType.standard,
|
|
5073
|
+
description: "True/False Responses chain",
|
|
5074
|
+
tags: [
|
|
5075
|
+
{
|
|
5076
|
+
exportJsonKey: { responses: [{ response: "$", isCorrect: true }] },
|
|
5077
|
+
key: ConfigKey.tag_true,
|
|
5078
|
+
description: "True value for a true/false statement/question",
|
|
5079
|
+
maxCount: Count.infinity,
|
|
5080
|
+
format: TagFormat.plainText,
|
|
5081
|
+
chain: [
|
|
5082
|
+
{
|
|
5083
|
+
key: ConfigKey.tag_true,
|
|
5084
|
+
jsonKey: "responses[]|set(isCorrect=true)",
|
|
5085
|
+
exportJsonKey: { responses: [{ response: "$", isCorrect: true }] },
|
|
5086
|
+
description: "True values for a true/false statement/question",
|
|
5087
|
+
maxCount: Count.infinity,
|
|
5088
|
+
format: TagFormat.plainText
|
|
5089
|
+
},
|
|
5090
|
+
{
|
|
5091
|
+
key: ConfigKey.tag_false,
|
|
5092
|
+
jsonKey: "responses[]|set(isCorrect=false)",
|
|
5093
|
+
exportJsonKey: { responses: [{ response: "$", isCorrect: false }] },
|
|
5094
|
+
description: "False values for a true/false statement/question",
|
|
5095
|
+
maxCount: Count.infinity,
|
|
5096
|
+
format: TagFormat.plainText
|
|
5097
|
+
},
|
|
5098
|
+
{
|
|
5099
|
+
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
5100
|
+
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
5101
|
+
},
|
|
5102
|
+
{
|
|
5103
|
+
// @card scope-shift writes response-container isExample.
|
|
5104
|
+
key: ConfigKey.property_example,
|
|
5105
|
+
jsonKey: "example",
|
|
5106
|
+
exportJsonKey: [
|
|
5107
|
+
{
|
|
5108
|
+
"@keyonly": {
|
|
4025
5109
|
isExample: true,
|
|
4026
5110
|
example: "$parent.isCorrect",
|
|
4027
|
-
"@bit": { isExample: true }
|
|
5111
|
+
"@bit": { isExample: true },
|
|
5112
|
+
"@card": { isExample: true }
|
|
4028
5113
|
}
|
|
4029
5114
|
},
|
|
4030
|
-
{
|
|
5115
|
+
{
|
|
5116
|
+
"@absent": {
|
|
5117
|
+
isExample: "$parent.isCorrect",
|
|
5118
|
+
example: "$parent.isCorrect",
|
|
5119
|
+
"@bit": { isExample: "$parent.isCorrect" },
|
|
5120
|
+
"@card": { isExample: "$parent.isCorrect" }
|
|
5121
|
+
}
|
|
5122
|
+
},
|
|
5123
|
+
{
|
|
5124
|
+
isExample: true,
|
|
5125
|
+
example: "$",
|
|
5126
|
+
"@bit": { isExample: true },
|
|
5127
|
+
"@card": { isExample: true }
|
|
5128
|
+
}
|
|
4031
5129
|
],
|
|
4032
5130
|
description: "An example for the true/false statement/question",
|
|
4033
|
-
format: TagFormat.
|
|
5131
|
+
format: TagFormat.boolean,
|
|
5132
|
+
nullable: true
|
|
4034
5133
|
}
|
|
4035
5134
|
]
|
|
4036
5135
|
},
|
|
4037
5136
|
{
|
|
4038
|
-
exportJsonKey: {
|
|
5137
|
+
exportJsonKey: { responses: [{ response: "$", isCorrect: false }] },
|
|
4039
5138
|
key: ConfigKey.tag_false,
|
|
4040
5139
|
description: "False value for a true/false statement/question",
|
|
4041
5140
|
maxCount: Count.infinity,
|
|
@@ -4043,16 +5142,16 @@ var GROUPS = {
|
|
|
4043
5142
|
chain: [
|
|
4044
5143
|
{
|
|
4045
5144
|
key: ConfigKey.tag_true,
|
|
4046
|
-
jsonKey: "
|
|
4047
|
-
exportJsonKey: {
|
|
5145
|
+
jsonKey: "responses[]|set(isCorrect=true)",
|
|
5146
|
+
exportJsonKey: { responses: [{ response: "$", isCorrect: true }] },
|
|
4048
5147
|
description: "True values for a true/false statement/question",
|
|
4049
5148
|
maxCount: Count.infinity,
|
|
4050
5149
|
format: TagFormat.plainText
|
|
4051
5150
|
},
|
|
4052
5151
|
{
|
|
4053
5152
|
key: ConfigKey.tag_false,
|
|
4054
|
-
jsonKey: "
|
|
4055
|
-
exportJsonKey: {
|
|
5153
|
+
jsonKey: "responses[]|set(isCorrect=false)",
|
|
5154
|
+
exportJsonKey: { responses: [{ response: "$", isCorrect: false }] },
|
|
4056
5155
|
description: "False values for a true/false statement/question",
|
|
4057
5156
|
maxCount: Count.infinity,
|
|
4058
5157
|
format: TagFormat.plainText
|
|
@@ -4062,6 +5161,7 @@ var GROUPS = {
|
|
|
4062
5161
|
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
4063
5162
|
},
|
|
4064
5163
|
{
|
|
5164
|
+
// @card scope-shift writes response-container isExample.
|
|
4065
5165
|
key: ConfigKey.property_example,
|
|
4066
5166
|
jsonKey: "example",
|
|
4067
5167
|
exportJsonKey: [
|
|
@@ -4069,20 +5169,333 @@ var GROUPS = {
|
|
|
4069
5169
|
"@keyonly": {
|
|
4070
5170
|
isExample: true,
|
|
4071
5171
|
example: "$parent.isCorrect",
|
|
4072
|
-
"@bit": { isExample: true }
|
|
5172
|
+
"@bit": { isExample: true },
|
|
5173
|
+
"@card": { isExample: true }
|
|
4073
5174
|
}
|
|
4074
5175
|
},
|
|
4075
5176
|
{
|
|
4076
5177
|
"@absent": {
|
|
4077
5178
|
isExample: true,
|
|
4078
5179
|
example: "$parent.isCorrect",
|
|
4079
|
-
"@bit": { isExample: true }
|
|
5180
|
+
"@bit": { isExample: true },
|
|
5181
|
+
"@card": { isExample: true }
|
|
4080
5182
|
}
|
|
4081
5183
|
},
|
|
4082
|
-
{
|
|
5184
|
+
{
|
|
5185
|
+
isExample: true,
|
|
5186
|
+
example: "$",
|
|
5187
|
+
"@bit": { isExample: true },
|
|
5188
|
+
"@card": { isExample: true }
|
|
5189
|
+
}
|
|
4083
5190
|
],
|
|
4084
5191
|
description: "An example for the true/false statement/question",
|
|
4085
|
-
format: TagFormat.
|
|
5192
|
+
format: TagFormat.boolean,
|
|
5193
|
+
nullable: true
|
|
5194
|
+
}
|
|
5195
|
+
]
|
|
5196
|
+
}
|
|
5197
|
+
]
|
|
5198
|
+
},
|
|
5199
|
+
[ConfigKey.group_trueFalseInlineSelect]: {
|
|
5200
|
+
type: GroupConfigType.standard,
|
|
5201
|
+
description: "True/False chain composed inline (select body-bit). +/- tags are consume-only at bit level.",
|
|
5202
|
+
tags: [
|
|
5203
|
+
{
|
|
5204
|
+
exportJsonKey: {
|
|
5205
|
+
"@body-inline": {
|
|
5206
|
+
type: "select",
|
|
5207
|
+
attrs: { options: [{ text: "$", isCorrect: true }] }
|
|
5208
|
+
}
|
|
5209
|
+
},
|
|
5210
|
+
key: ConfigKey.tag_true,
|
|
5211
|
+
description: "True value for an inline select body bit",
|
|
5212
|
+
maxCount: Count.infinity,
|
|
5213
|
+
format: TagFormat.plainText,
|
|
5214
|
+
chain: [
|
|
5215
|
+
{
|
|
5216
|
+
key: ConfigKey.tag_true,
|
|
5217
|
+
exportJsonKey: {},
|
|
5218
|
+
description: "True values for an inline select body bit",
|
|
5219
|
+
maxCount: Count.infinity,
|
|
5220
|
+
format: TagFormat.plainText
|
|
5221
|
+
},
|
|
5222
|
+
{
|
|
5223
|
+
key: ConfigKey.tag_false,
|
|
5224
|
+
exportJsonKey: {},
|
|
5225
|
+
description: "False values for an inline select body bit",
|
|
5226
|
+
maxCount: Count.infinity,
|
|
5227
|
+
format: TagFormat.plainText
|
|
5228
|
+
},
|
|
5229
|
+
{
|
|
5230
|
+
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
5231
|
+
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
5232
|
+
},
|
|
5233
|
+
{
|
|
5234
|
+
// PLAN-083: cascade-fired `@example` from bit header fires
|
|
5235
|
+
// `isExample:true, example:true` on the FIRST CORRECT option
|
|
5236
|
+
// only (BPG `fillBooleanExample(select.options,
|
|
5237
|
+
// firstCorrectOnly=true)`). Mirrors group_trueFalseInlineHighlight.
|
|
5238
|
+
// Every rule also bubbles `@bit:{isExample:true}` (idempotent
|
|
5239
|
+
// OR — any cascade ⇒ bit marked) and
|
|
5240
|
+
// `@body-inline:{attrs:{isExample:true}}` (node-level marker
|
|
5241
|
+
// on the select node).
|
|
5242
|
+
key: ConfigKey.property_example,
|
|
5243
|
+
jsonKey: "example",
|
|
5244
|
+
exportJsonKey: [
|
|
5245
|
+
{
|
|
5246
|
+
"@keyonly": {
|
|
5247
|
+
isExample: true,
|
|
5248
|
+
example: "$parent.isCorrect",
|
|
5249
|
+
"@bit": { isExample: true },
|
|
5250
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5251
|
+
}
|
|
5252
|
+
},
|
|
5253
|
+
{
|
|
5254
|
+
predicates: ["@absent", { "@parent.isCorrect": true }],
|
|
5255
|
+
maxEmits: 1,
|
|
5256
|
+
rule: {
|
|
5257
|
+
isExample: true,
|
|
5258
|
+
example: true,
|
|
5259
|
+
"@bit": { isExample: true },
|
|
5260
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5261
|
+
}
|
|
5262
|
+
},
|
|
5263
|
+
{
|
|
5264
|
+
isExample: true,
|
|
5265
|
+
example: "$",
|
|
5266
|
+
"@bit": { isExample: true },
|
|
5267
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5268
|
+
}
|
|
5269
|
+
],
|
|
5270
|
+
description: "An example for the inline select option (first correct only)",
|
|
5271
|
+
format: TagFormat.boolean,
|
|
5272
|
+
nullable: true
|
|
5273
|
+
}
|
|
5274
|
+
]
|
|
5275
|
+
},
|
|
5276
|
+
{
|
|
5277
|
+
exportJsonKey: {
|
|
5278
|
+
"@body-inline": {
|
|
5279
|
+
type: "select",
|
|
5280
|
+
attrs: { options: [{ text: "$", isCorrect: false }] }
|
|
5281
|
+
}
|
|
5282
|
+
},
|
|
5283
|
+
key: ConfigKey.tag_false,
|
|
5284
|
+
description: "False value for an inline select body bit",
|
|
5285
|
+
maxCount: Count.infinity,
|
|
5286
|
+
format: TagFormat.plainText,
|
|
5287
|
+
chain: [
|
|
5288
|
+
{
|
|
5289
|
+
key: ConfigKey.tag_true,
|
|
5290
|
+
exportJsonKey: {},
|
|
5291
|
+
description: "True values for an inline select body bit",
|
|
5292
|
+
maxCount: Count.infinity,
|
|
5293
|
+
format: TagFormat.plainText
|
|
5294
|
+
},
|
|
5295
|
+
{
|
|
5296
|
+
key: ConfigKey.tag_false,
|
|
5297
|
+
exportJsonKey: {},
|
|
5298
|
+
description: "False values for an inline select body bit",
|
|
5299
|
+
maxCount: Count.infinity,
|
|
5300
|
+
format: TagFormat.plainText
|
|
5301
|
+
},
|
|
5302
|
+
{
|
|
5303
|
+
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
5304
|
+
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
5305
|
+
},
|
|
5306
|
+
{
|
|
5307
|
+
// PLAN-083: identical to the `+` lead's chain @example —
|
|
5308
|
+
// cascade reaches the first correct option regardless of
|
|
5309
|
+
// which lead (+/-) opens the chain. Every firing rule bubbles
|
|
5310
|
+
// `@bit:{isExample:true}` and `@body-inline:{attrs:{isExample:true}}`.
|
|
5311
|
+
key: ConfigKey.property_example,
|
|
5312
|
+
jsonKey: "example",
|
|
5313
|
+
exportJsonKey: [
|
|
5314
|
+
{
|
|
5315
|
+
"@keyonly": {
|
|
5316
|
+
isExample: true,
|
|
5317
|
+
example: "$parent.isCorrect",
|
|
5318
|
+
"@bit": { isExample: true },
|
|
5319
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5320
|
+
}
|
|
5321
|
+
},
|
|
5322
|
+
{
|
|
5323
|
+
predicates: ["@absent", { "@parent.isCorrect": true }],
|
|
5324
|
+
maxEmits: 1,
|
|
5325
|
+
rule: {
|
|
5326
|
+
isExample: true,
|
|
5327
|
+
example: true,
|
|
5328
|
+
"@bit": { isExample: true },
|
|
5329
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5330
|
+
}
|
|
5331
|
+
},
|
|
5332
|
+
{
|
|
5333
|
+
isExample: true,
|
|
5334
|
+
example: "$",
|
|
5335
|
+
"@bit": { isExample: true },
|
|
5336
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5337
|
+
}
|
|
5338
|
+
],
|
|
5339
|
+
description: "An example for the inline select option (first correct only)",
|
|
5340
|
+
format: TagFormat.boolean,
|
|
5341
|
+
nullable: true
|
|
5342
|
+
}
|
|
5343
|
+
]
|
|
5344
|
+
}
|
|
5345
|
+
]
|
|
5346
|
+
},
|
|
5347
|
+
[ConfigKey.group_trueFalseInlineHighlight]: {
|
|
5348
|
+
type: GroupConfigType.standard,
|
|
5349
|
+
description: 'True/False chain composed inline (highlight body-bit). +/- tags emit `{type:"highlight", attrs:{texts:[{text,isCorrect?}]}}` inline body nodes. Used by highlight-text bit.',
|
|
5350
|
+
tags: [
|
|
5351
|
+
{
|
|
5352
|
+
exportJsonKey: {
|
|
5353
|
+
"@body-inline": {
|
|
5354
|
+
type: "highlight",
|
|
5355
|
+
attrs: { texts: [{ text: "$", isCorrect: true }] }
|
|
5356
|
+
}
|
|
5357
|
+
},
|
|
5358
|
+
key: ConfigKey.tag_true,
|
|
5359
|
+
description: "Highlighted span in the body (correct)",
|
|
5360
|
+
maxCount: Count.infinity,
|
|
5361
|
+
format: TagFormat.plainText,
|
|
5362
|
+
chain: [
|
|
5363
|
+
{
|
|
5364
|
+
key: ConfigKey.tag_true,
|
|
5365
|
+
exportJsonKey: {},
|
|
5366
|
+
description: "Highlighted span values (correct)",
|
|
5367
|
+
maxCount: Count.infinity,
|
|
5368
|
+
format: TagFormat.plainText
|
|
5369
|
+
},
|
|
5370
|
+
{
|
|
5371
|
+
key: ConfigKey.tag_false,
|
|
5372
|
+
exportJsonKey: {},
|
|
5373
|
+
description: "Highlighted span values (incorrect)",
|
|
5374
|
+
maxCount: Count.infinity,
|
|
5375
|
+
format: TagFormat.plainText
|
|
5376
|
+
},
|
|
5377
|
+
{
|
|
5378
|
+
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
5379
|
+
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
5380
|
+
},
|
|
5381
|
+
{
|
|
5382
|
+
// PLAN-072 + ALIGN-EXAMPLE-CASCADE §3.13: cascade-fired
|
|
5383
|
+
// `@example` from the bit header fires `example: true` +
|
|
5384
|
+
// `isExample: true` on the FIRST correct highlight only
|
|
5385
|
+
// (BPG `fillBooleanExample(highlight.texts,
|
|
5386
|
+
// firstCorrectOnly=true)`). Incorrect highlights get
|
|
5387
|
+
// nothing (unlike choices, the highlight-text fixture
|
|
5388
|
+
// shows no `isExample` on incorrect entries).
|
|
5389
|
+
key: ConfigKey.property_example,
|
|
5390
|
+
jsonKey: "example",
|
|
5391
|
+
exportJsonKey: [
|
|
5392
|
+
{
|
|
5393
|
+
"@keyonly": {
|
|
5394
|
+
isExample: true,
|
|
5395
|
+
example: "$parent.isCorrect",
|
|
5396
|
+
"@bit": { isExample: true },
|
|
5397
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5398
|
+
}
|
|
5399
|
+
},
|
|
5400
|
+
{
|
|
5401
|
+
predicates: ["@absent", { "@parent.isCorrect": true }],
|
|
5402
|
+
maxEmits: 1,
|
|
5403
|
+
rule: {
|
|
5404
|
+
isExample: true,
|
|
5405
|
+
example: true,
|
|
5406
|
+
"@bit": { isExample: true },
|
|
5407
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5408
|
+
}
|
|
5409
|
+
},
|
|
5410
|
+
{
|
|
5411
|
+
isExample: true,
|
|
5412
|
+
example: "$",
|
|
5413
|
+
"@bit": { isExample: true },
|
|
5414
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5415
|
+
}
|
|
5416
|
+
],
|
|
5417
|
+
description: "An example for the highlighted span (first correct only)",
|
|
5418
|
+
format: TagFormat.boolean,
|
|
5419
|
+
nullable: true
|
|
5420
|
+
}
|
|
5421
|
+
]
|
|
5422
|
+
},
|
|
5423
|
+
{
|
|
5424
|
+
// PLAN-083 follow-up (2026-05-22): mirror the select `-` lead shape
|
|
5425
|
+
// by writing `isCorrect: false`. Without it, `$parent.isCorrect` in
|
|
5426
|
+
// chain-child @example rules resolves to ParentPathMissing for `-`
|
|
5427
|
+
// entries (e.g. `[-fish][@example]`), so the @keyonly rule falls
|
|
5428
|
+
// through and nothing fires. The natural-default strip removes
|
|
5429
|
+
// `isCorrect: false` from the final texts entries (matches fixture).
|
|
5430
|
+
exportJsonKey: {
|
|
5431
|
+
"@body-inline": {
|
|
5432
|
+
type: "highlight",
|
|
5433
|
+
attrs: { texts: [{ text: "$", isCorrect: false }] }
|
|
5434
|
+
}
|
|
5435
|
+
},
|
|
5436
|
+
key: ConfigKey.tag_false,
|
|
5437
|
+
description: "Highlighted span in the body (incorrect)",
|
|
5438
|
+
maxCount: Count.infinity,
|
|
5439
|
+
format: TagFormat.plainText,
|
|
5440
|
+
chain: [
|
|
5441
|
+
{
|
|
5442
|
+
key: ConfigKey.tag_true,
|
|
5443
|
+
exportJsonKey: {},
|
|
5444
|
+
description: "Highlighted span values (correct)",
|
|
5445
|
+
maxCount: Count.infinity,
|
|
5446
|
+
format: TagFormat.plainText
|
|
5447
|
+
},
|
|
5448
|
+
{
|
|
5449
|
+
key: ConfigKey.tag_false,
|
|
5450
|
+
exportJsonKey: {},
|
|
5451
|
+
description: "Highlighted span values (incorrect)",
|
|
5452
|
+
maxCount: Count.infinity,
|
|
5453
|
+
format: TagFormat.plainText
|
|
5454
|
+
},
|
|
5455
|
+
{
|
|
5456
|
+
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
5457
|
+
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
5458
|
+
},
|
|
5459
|
+
{
|
|
5460
|
+
// PLAN-072 + ALIGN-EXAMPLE-CASCADE §3.13: cascade-fired
|
|
5461
|
+
// `@example` on a `-` (incorrect highlight) emits nothing
|
|
5462
|
+
// (highlight-text fixture has no `isExample` on incorrect
|
|
5463
|
+
// entries; firstCorrectOnly cascade skips them entirely).
|
|
5464
|
+
// No `@absent` rule.
|
|
5465
|
+
// PLAN-083: identical to the `+` lead's chain @example —
|
|
5466
|
+
// cascade reaches the first correct option regardless of
|
|
5467
|
+
// which lead (+/-) opens the chain.
|
|
5468
|
+
key: ConfigKey.property_example,
|
|
5469
|
+
jsonKey: "example",
|
|
5470
|
+
exportJsonKey: [
|
|
5471
|
+
{
|
|
5472
|
+
"@keyonly": {
|
|
5473
|
+
isExample: true,
|
|
5474
|
+
example: "$parent.isCorrect",
|
|
5475
|
+
"@bit": { isExample: true },
|
|
5476
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5477
|
+
}
|
|
5478
|
+
},
|
|
5479
|
+
{
|
|
5480
|
+
predicates: ["@absent", { "@parent.isCorrect": true }],
|
|
5481
|
+
maxEmits: 1,
|
|
5482
|
+
rule: {
|
|
5483
|
+
isExample: true,
|
|
5484
|
+
example: true,
|
|
5485
|
+
"@bit": { isExample: true },
|
|
5486
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5487
|
+
}
|
|
5488
|
+
},
|
|
5489
|
+
{
|
|
5490
|
+
isExample: true,
|
|
5491
|
+
example: "$",
|
|
5492
|
+
"@bit": { isExample: true },
|
|
5493
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5494
|
+
}
|
|
5495
|
+
],
|
|
5496
|
+
description: "An example for the highlighted span (first correct only)",
|
|
5497
|
+
format: TagFormat.boolean,
|
|
5498
|
+
nullable: true
|
|
4086
5499
|
}
|
|
4087
5500
|
]
|
|
4088
5501
|
}
|
|
@@ -4095,7 +5508,11 @@ var GROUPS = {
|
|
|
4095
5508
|
{
|
|
4096
5509
|
key: ConfigKey.property_mark,
|
|
4097
5510
|
jsonKey: "marks.mark",
|
|
4098
|
-
|
|
5511
|
+
// `emphasis: 'underline'` is the per-entry default for mark
|
|
5512
|
+
// configs — mirrors the hardcoded default in BPG
|
|
5513
|
+
// `MarkConfigChainContentProcessor.ts:55-58`. Chained
|
|
5514
|
+
// `[@emphasis:…]` overrides via deep-merge into `marks[-1]`.
|
|
5515
|
+
exportJsonKey: { marks: [{ mark: "$", emphasis: "underline" }] },
|
|
4099
5516
|
description: "The mark configuration",
|
|
4100
5517
|
format: TagFormat.plainText,
|
|
4101
5518
|
maxCount: Count.infinity,
|
|
@@ -4119,7 +5536,9 @@ var GROUPS = {
|
|
|
4119
5536
|
description: "Mark chain",
|
|
4120
5537
|
tags: [
|
|
4121
5538
|
{
|
|
4122
|
-
exportJsonKey: {
|
|
5539
|
+
exportJsonKey: {
|
|
5540
|
+
"@body-inline": { type: "mark", attrs: { solution: "$" } }
|
|
5541
|
+
},
|
|
4123
5542
|
key: ConfigKey.tag_mark,
|
|
4124
5543
|
description: "The marked content",
|
|
4125
5544
|
maxCount: Count.infinity,
|
|
@@ -4128,19 +5547,30 @@ var GROUPS = {
|
|
|
4128
5547
|
{
|
|
4129
5548
|
key: ConfigKey.property_mark,
|
|
4130
5549
|
jsonKey: "mark",
|
|
5550
|
+
exportJsonKey: { mark: "$" },
|
|
4131
5551
|
description: "The mark configuration",
|
|
4132
5552
|
format: TagFormat.plainText
|
|
4133
5553
|
},
|
|
4134
5554
|
{
|
|
4135
5555
|
key: ConfigKey.property_example,
|
|
4136
5556
|
jsonKey: "example",
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
5557
|
+
// Per-mark `@example` is a boolean toggle. BPG's runtime
|
|
5558
|
+
// `ExampleTagContentProcessor.handleDefaultOnlyExample`
|
|
5559
|
+
// (lines 147-161) normalises ANY `[@example]` or
|
|
5560
|
+
// `[@example:V]` on a mark body-bit to
|
|
5561
|
+
// `__isDefaultExample=true, example=undefined`; the
|
|
5562
|
+
// downstream `toExample(_, _, defaultExample=true)` in
|
|
5563
|
+
// `BaseBuilder.ts:80-87` then emits `example: true`
|
|
5564
|
+
// regardless of source. Declare a single unconditional
|
|
5565
|
+
// boolean-shape rule so the exported schema matches that
|
|
5566
|
+
// runtime semantic instead of the misleading `example: $`
|
|
5567
|
+
// shape (which would only fire if `handleDefaultOnlyExample`
|
|
5568
|
+
// were removed). `@bit: {isExample: true}` cascades the
|
|
5569
|
+
// marker — but NOT the `example` value — to bit root.
|
|
5570
|
+
exportJsonKey: { isExample: true, example: true, "@bit": { isExample: true } },
|
|
4142
5571
|
description: "An example for the marked content",
|
|
4143
|
-
format: TagFormat.
|
|
5572
|
+
format: TagFormat.boolean,
|
|
5573
|
+
nullable: true
|
|
4144
5574
|
}
|
|
4145
5575
|
]
|
|
4146
5576
|
}
|
|
@@ -4569,6 +5999,42 @@ var GROUPS = {
|
|
|
4569
5999
|
}
|
|
4570
6000
|
]
|
|
4571
6001
|
},
|
|
6002
|
+
[ConfigKey.group_backgroundWallpaper]: {
|
|
6003
|
+
type: GroupConfigType.standard,
|
|
6004
|
+
description: "Background wallpaper tags",
|
|
6005
|
+
tags: [
|
|
6006
|
+
{
|
|
6007
|
+
key: ConfigKey.resource_backgroundWallpaper,
|
|
6008
|
+
exportJsonKey: { backgroundWallpaper: { type: "image", image: { src: "$" } } },
|
|
6009
|
+
description: "Background wallpaper for the image, used to set a background for the image",
|
|
6010
|
+
chain: [
|
|
6011
|
+
{
|
|
6012
|
+
key: ConfigKey.group_resourceImageCommon,
|
|
6013
|
+
description: "Common resource image tags for images"
|
|
6014
|
+
}
|
|
6015
|
+
]
|
|
6016
|
+
}
|
|
6017
|
+
]
|
|
6018
|
+
},
|
|
6019
|
+
[ConfigKey.group_imageNoZoom]: {
|
|
6020
|
+
type: GroupConfigType.standard,
|
|
6021
|
+
description: "Image bit tags",
|
|
6022
|
+
tags: [
|
|
6023
|
+
{
|
|
6024
|
+
key: ConfigKey.group_backgroundWallpaper,
|
|
6025
|
+
description: "Background wallpaper tags for images, used to define background properties for images"
|
|
6026
|
+
},
|
|
6027
|
+
{
|
|
6028
|
+
key: ConfigKey.group_resourceBitTags,
|
|
6029
|
+
description: "Resource bit tags for images, used to define additional properties for images"
|
|
6030
|
+
},
|
|
6031
|
+
{
|
|
6032
|
+
key: ConfigKey.group_resourceImageNoZoom,
|
|
6033
|
+
description: "Resource image tags for images, used to attach images to the bit",
|
|
6034
|
+
minCount: 1
|
|
6035
|
+
}
|
|
6036
|
+
]
|
|
6037
|
+
},
|
|
4572
6038
|
//
|
|
4573
6039
|
// Resource groups
|
|
4574
6040
|
//
|
|
@@ -4688,6 +6154,58 @@ var GROUPS = {
|
|
|
4688
6154
|
}
|
|
4689
6155
|
]
|
|
4690
6156
|
},
|
|
6157
|
+
[ConfigKey.group_resourceImageCommonNoZoom]: {
|
|
6158
|
+
type: GroupConfigType.standard,
|
|
6159
|
+
description: "Common properties for image resources where @zoomDisabled defaults to true",
|
|
6160
|
+
tags: [
|
|
6161
|
+
{
|
|
6162
|
+
key: ConfigKey.group_resourceCommon,
|
|
6163
|
+
description: "Common resource properties"
|
|
6164
|
+
},
|
|
6165
|
+
{
|
|
6166
|
+
key: ConfigKey.property_src1x,
|
|
6167
|
+
description: "The source URL for the image at 1x resolution",
|
|
6168
|
+
format: TagFormat.plainText
|
|
6169
|
+
},
|
|
6170
|
+
{
|
|
6171
|
+
key: ConfigKey.property_src2x,
|
|
6172
|
+
description: "The source URL for the image at 2x resolution",
|
|
6173
|
+
format: TagFormat.plainText
|
|
6174
|
+
},
|
|
6175
|
+
{
|
|
6176
|
+
key: ConfigKey.property_src3x,
|
|
6177
|
+
description: "The source URL for the image at 3x resolution",
|
|
6178
|
+
format: TagFormat.plainText
|
|
6179
|
+
},
|
|
6180
|
+
{
|
|
6181
|
+
key: ConfigKey.property_src4x,
|
|
6182
|
+
description: "The source URL for the image at 4x resolution",
|
|
6183
|
+
format: TagFormat.plainText
|
|
6184
|
+
},
|
|
6185
|
+
{
|
|
6186
|
+
key: ConfigKey.property_width,
|
|
6187
|
+
description: "The width of the image",
|
|
6188
|
+
format: TagFormat.number
|
|
6189
|
+
},
|
|
6190
|
+
{
|
|
6191
|
+
key: ConfigKey.property_height,
|
|
6192
|
+
description: "The height of the image",
|
|
6193
|
+
format: TagFormat.number
|
|
6194
|
+
},
|
|
6195
|
+
{
|
|
6196
|
+
key: ConfigKey.property_alt,
|
|
6197
|
+
description: "The alternative text for the image",
|
|
6198
|
+
format: TagFormat.plainText
|
|
6199
|
+
},
|
|
6200
|
+
// @zoomDisabled with a true default
|
|
6201
|
+
{
|
|
6202
|
+
key: ConfigKey.property_zoomDisabled,
|
|
6203
|
+
description: "If true, zooming is disabled for the image (defaults to true)",
|
|
6204
|
+
format: TagFormat.boolean,
|
|
6205
|
+
defaultValue: "true"
|
|
6206
|
+
}
|
|
6207
|
+
]
|
|
6208
|
+
},
|
|
4691
6209
|
[ConfigKey.group_resourceAudioCommon]: {
|
|
4692
6210
|
type: GroupConfigType.standard,
|
|
4693
6211
|
description: "Common properties for audio resources",
|
|
@@ -4777,22 +6295,26 @@ var GROUPS = {
|
|
|
4777
6295
|
{
|
|
4778
6296
|
key: ConfigKey.property_src1x,
|
|
4779
6297
|
description: "The source URL for the video at 1x resolution",
|
|
4780
|
-
format: TagFormat.plainText
|
|
6298
|
+
format: TagFormat.plainText,
|
|
6299
|
+
exportJsonKey: { thumbnails: [{ src: "$" }] }
|
|
4781
6300
|
},
|
|
4782
6301
|
{
|
|
4783
6302
|
key: ConfigKey.property_src2x,
|
|
4784
6303
|
description: "The source URL for the video at 2x resolution",
|
|
4785
|
-
format: TagFormat.plainText
|
|
6304
|
+
format: TagFormat.plainText,
|
|
6305
|
+
exportJsonKey: { thumbnails: [{ src: "$" }] }
|
|
4786
6306
|
},
|
|
4787
6307
|
{
|
|
4788
6308
|
key: ConfigKey.property_src3x,
|
|
4789
6309
|
description: "The source URL for the video at 3x resolution",
|
|
4790
|
-
format: TagFormat.plainText
|
|
6310
|
+
format: TagFormat.plainText,
|
|
6311
|
+
exportJsonKey: { thumbnails: [{ src: "$" }] }
|
|
4791
6312
|
},
|
|
4792
6313
|
{
|
|
4793
6314
|
key: ConfigKey.property_src4x,
|
|
4794
6315
|
description: "The source URL for the video at 4x resolution",
|
|
4795
|
-
format: TagFormat.plainText
|
|
6316
|
+
format: TagFormat.plainText,
|
|
6317
|
+
exportJsonKey: { thumbnails: [{ src: "$" }] }
|
|
4796
6318
|
}
|
|
4797
6319
|
]
|
|
4798
6320
|
},
|
|
@@ -4805,6 +6327,7 @@ var GROUPS = {
|
|
|
4805
6327
|
tags: [
|
|
4806
6328
|
{
|
|
4807
6329
|
key: ConfigKey.resource_icon,
|
|
6330
|
+
exportJsonKey: { resource: { type: "icon", icon: { src: "$" } } },
|
|
4808
6331
|
description: "The icon resource",
|
|
4809
6332
|
chain: [
|
|
4810
6333
|
{
|
|
@@ -4832,12 +6355,30 @@ var GROUPS = {
|
|
|
4832
6355
|
}
|
|
4833
6356
|
]
|
|
4834
6357
|
},
|
|
6358
|
+
[ConfigKey.group_resourceImageNoZoom]: {
|
|
6359
|
+
type: GroupConfigType.resource,
|
|
6360
|
+
description: "Image resource where @zoomDisabled defaults to true",
|
|
6361
|
+
tags: [
|
|
6362
|
+
{
|
|
6363
|
+
key: ConfigKey.resource_image,
|
|
6364
|
+
exportJsonKey: { resource: { type: "image", image: { src: "$" } } },
|
|
6365
|
+
description: "The image resource (no-zoom default)",
|
|
6366
|
+
chain: [
|
|
6367
|
+
{
|
|
6368
|
+
key: ConfigKey.group_resourceImageCommonNoZoom,
|
|
6369
|
+
description: "Common image properties with @zoomDisabled defaulting to true"
|
|
6370
|
+
}
|
|
6371
|
+
]
|
|
6372
|
+
}
|
|
6373
|
+
]
|
|
6374
|
+
},
|
|
4835
6375
|
[ConfigKey.group_resourceImagePortrait]: {
|
|
4836
6376
|
type: GroupConfigType.resource,
|
|
4837
6377
|
description: "Portrait image resource",
|
|
4838
6378
|
tags: [
|
|
4839
6379
|
{
|
|
4840
6380
|
key: ConfigKey.resource_imagePortrait,
|
|
6381
|
+
exportJsonKey: { resource: { type: "image-portrait", imagePortrait: { src: "$" } } },
|
|
4841
6382
|
description: "The portrait image resource",
|
|
4842
6383
|
chain: [
|
|
4843
6384
|
{
|
|
@@ -4854,6 +6395,7 @@ var GROUPS = {
|
|
|
4854
6395
|
tags: [
|
|
4855
6396
|
{
|
|
4856
6397
|
key: ConfigKey.resource_imageLandscape,
|
|
6398
|
+
exportJsonKey: { resource: { type: "image-landscape", imageLandscape: { src: "$" } } },
|
|
4857
6399
|
description: "The landscape image resource",
|
|
4858
6400
|
chain: [
|
|
4859
6401
|
{
|
|
@@ -4870,6 +6412,7 @@ var GROUPS = {
|
|
|
4870
6412
|
tags: [
|
|
4871
6413
|
{
|
|
4872
6414
|
key: ConfigKey.resource_imageEmbed,
|
|
6415
|
+
exportJsonKey: { resource: { type: "image-embed", imageEmbed: { src: "$" } } },
|
|
4873
6416
|
description: "The embedded image resource",
|
|
4874
6417
|
chain: [
|
|
4875
6418
|
{
|
|
@@ -5043,6 +6586,7 @@ var GROUPS = {
|
|
|
5043
6586
|
tags: [
|
|
5044
6587
|
{
|
|
5045
6588
|
key: ConfigKey.resource_article,
|
|
6589
|
+
exportJsonKey: { resource: { type: "article", article: { body: "$" } } },
|
|
5046
6590
|
description: "The article resource",
|
|
5047
6591
|
chain: [
|
|
5048
6592
|
{
|
|
@@ -5059,6 +6603,7 @@ var GROUPS = {
|
|
|
5059
6603
|
tags: [
|
|
5060
6604
|
{
|
|
5061
6605
|
key: ConfigKey.resource_articleEmbed,
|
|
6606
|
+
exportJsonKey: { resource: { type: "article-embed", articleEmbed: { url: "$" } } },
|
|
5062
6607
|
description: "The embedded article resource",
|
|
5063
6608
|
chain: [
|
|
5064
6609
|
{
|
|
@@ -5075,6 +6620,7 @@ var GROUPS = {
|
|
|
5075
6620
|
tags: [
|
|
5076
6621
|
{
|
|
5077
6622
|
key: ConfigKey.resource_articleLink,
|
|
6623
|
+
exportJsonKey: { resource: { type: "article-link", articleLink: { url: "$" } } },
|
|
5078
6624
|
description: "The link to the article resource",
|
|
5079
6625
|
chain: [
|
|
5080
6626
|
{
|
|
@@ -5345,7 +6891,12 @@ var ConfigHydrator = class {
|
|
|
5345
6891
|
isDefault: v.isDefault,
|
|
5346
6892
|
sideJsonKey: v.sideJsonKey,
|
|
5347
6893
|
sideExportJsonKey: v.sideExportJsonKey,
|
|
5348
|
-
hasSideExportJsonKey: Object.prototype.hasOwnProperty.call(v, "sideExportJsonKey")
|
|
6894
|
+
hasSideExportJsonKey: Object.prototype.hasOwnProperty.call(v, "sideExportJsonKey"),
|
|
6895
|
+
// PLAN-085: cardinality fields pass through; defaults applied
|
|
6896
|
+
// downstream by the config consumer (Rust treats `0` / undefined
|
|
6897
|
+
// as unbounded).
|
|
6898
|
+
minCount: v.minCount,
|
|
6899
|
+
maxCount: v.maxCount
|
|
5349
6900
|
};
|
|
5350
6901
|
}
|
|
5351
6902
|
return out;
|
|
@@ -5560,7 +7111,8 @@ var BITS = {
|
|
|
5560
7111
|
{ isExample: true, example: "$" }
|
|
5561
7112
|
],
|
|
5562
7113
|
description: "The example text for the bit",
|
|
5563
|
-
format: TagFormat.plainText
|
|
7114
|
+
format: TagFormat.plainText,
|
|
7115
|
+
nullable: true
|
|
5564
7116
|
}
|
|
5565
7117
|
],
|
|
5566
7118
|
rootExampleType: ExampleType.string
|
|
@@ -5899,7 +7451,8 @@ var BITS = {
|
|
|
5899
7451
|
{ isExample: true, example: "$" }
|
|
5900
7452
|
],
|
|
5901
7453
|
description: "The example text for the bit",
|
|
5902
|
-
format: TagFormat.plainText
|
|
7454
|
+
format: TagFormat.plainText,
|
|
7455
|
+
nullable: true
|
|
5903
7456
|
}
|
|
5904
7457
|
],
|
|
5905
7458
|
rootExampleType: ExampleType.string
|
|
@@ -6062,7 +7615,8 @@ var BITS = {
|
|
|
6062
7615
|
{ isExample: true, example: "$" }
|
|
6063
7616
|
],
|
|
6064
7617
|
description: "The example text for the bit",
|
|
6065
|
-
format: TagFormat.plainText
|
|
7618
|
+
format: TagFormat.plainText,
|
|
7619
|
+
nullable: true
|
|
6066
7620
|
}
|
|
6067
7621
|
],
|
|
6068
7622
|
rootExampleType: ExampleType.string
|
|
@@ -6213,7 +7767,8 @@ var BITS = {
|
|
|
6213
7767
|
{ isExample: true, example: "$" }
|
|
6214
7768
|
],
|
|
6215
7769
|
description: "The example text for the bit",
|
|
6216
|
-
format: TagFormat.plainText
|
|
7770
|
+
format: TagFormat.plainText,
|
|
7771
|
+
nullable: true
|
|
6217
7772
|
}
|
|
6218
7773
|
],
|
|
6219
7774
|
rootExampleType: ExampleType.string
|
|
@@ -6790,6 +8345,12 @@ var BITS = {
|
|
|
6790
8345
|
description: "The progress of the chapter, used for tracking reading progress",
|
|
6791
8346
|
format: TagFormat.boolean,
|
|
6792
8347
|
defaultValue: "true"
|
|
8348
|
+
},
|
|
8349
|
+
{
|
|
8350
|
+
key: ConfigKey.property_isCollapsible,
|
|
8351
|
+
description: "If true, the chapter is collapsible",
|
|
8352
|
+
format: TagFormat.boolean,
|
|
8353
|
+
defaultValue: "false"
|
|
6793
8354
|
}
|
|
6794
8355
|
]
|
|
6795
8356
|
},
|
|
@@ -6808,8 +8369,31 @@ var BITS = {
|
|
|
6808
8369
|
description: "Tags for gaps in cloze and multiple choice text bits"
|
|
6809
8370
|
},
|
|
6810
8371
|
{
|
|
6811
|
-
key: ConfigKey.
|
|
8372
|
+
key: ConfigKey.group_trueFalseInlineSelect,
|
|
6812
8373
|
description: "Tags for true/false questions in cloze and multiple choice text bits"
|
|
8374
|
+
},
|
|
8375
|
+
{
|
|
8376
|
+
// Bit-level @isCaseSensitive — empty exportJsonKey (no bit-level emission);
|
|
8377
|
+
// defaultValue 'true' phantom-fires onto the ancestor stack so the gap chain's
|
|
8378
|
+
// `@absent: $ancestor` rule resolves to true. Mirrors BPG Builder.ts:1643-1652
|
|
8379
|
+
// `pushDownTree(... 'isCaseSensitive', data.isCaseSensitive ?? true)` for cloze bits.
|
|
8380
|
+
key: ConfigKey.property_isCaseSensitive,
|
|
8381
|
+
exportJsonKey: {},
|
|
8382
|
+
description: "If the cloze answers are case sensitive",
|
|
8383
|
+
format: TagFormat.boolean,
|
|
8384
|
+
defaultValue: "true"
|
|
8385
|
+
},
|
|
8386
|
+
{
|
|
8387
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
8388
|
+
key: ConfigKey.property_example,
|
|
8389
|
+
exportJsonKey: [
|
|
8390
|
+
{ "@keyonly": { isExample: true } },
|
|
8391
|
+
{ "@absent": { isExample: true } },
|
|
8392
|
+
{ isExample: true, example: "$" }
|
|
8393
|
+
],
|
|
8394
|
+
description: "The example(s) for the bit",
|
|
8395
|
+
format: TagFormat.bitmarkText,
|
|
8396
|
+
nullable: true
|
|
6813
8397
|
}
|
|
6814
8398
|
]
|
|
6815
8399
|
},
|
|
@@ -6848,18 +8432,56 @@ var BITS = {
|
|
|
6848
8432
|
{
|
|
6849
8433
|
key: ConfigKey.group_gap,
|
|
6850
8434
|
description: "Tags for gaps in cloze bits"
|
|
8435
|
+
},
|
|
8436
|
+
{
|
|
8437
|
+
// Override inherited @example: cloze is in OLD parser's
|
|
8438
|
+
// `isTopLevelExample` allow-list, so [@example] at bit-header
|
|
8439
|
+
// emits `isExample: true`.
|
|
8440
|
+
key: ConfigKey.property_example,
|
|
8441
|
+
exportJsonKey: [
|
|
8442
|
+
{ "@keyonly": { isExample: true } },
|
|
8443
|
+
{ "@absent": { isExample: true } },
|
|
8444
|
+
{ isExample: true, example: "$" }
|
|
8445
|
+
],
|
|
8446
|
+
description: "The example(s) for the bit",
|
|
8447
|
+
format: TagFormat.bitmarkText,
|
|
8448
|
+
nullable: true
|
|
6851
8449
|
}
|
|
6852
8450
|
]
|
|
6853
8451
|
},
|
|
6854
8452
|
[BitType2.clozeInstructionGrouped]: {
|
|
6855
8453
|
since: "1.3.0",
|
|
6856
8454
|
baseBitType: BitType2.cloze,
|
|
6857
|
-
description: "Cloze instruction grouped bit, used for cloze questions with grouped instructions"
|
|
8455
|
+
description: "Cloze instruction grouped bit, used for cloze questions with grouped instructions",
|
|
8456
|
+
tags: [
|
|
8457
|
+
{
|
|
8458
|
+
key: ConfigKey.property_quizCountItems,
|
|
8459
|
+
description: "The number of items in the cloze quiz (instruction-grouped default: true)",
|
|
8460
|
+
format: TagFormat.boolean,
|
|
8461
|
+
defaultValue: "true"
|
|
8462
|
+
}
|
|
8463
|
+
// Note: quizStrikethroughSolutions defaults to false for instruction-grouped per OLD
|
|
8464
|
+
// JsonGenerator.ts:1689-1690, which equals the Boolean natural default → no override needed.
|
|
8465
|
+
]
|
|
6858
8466
|
},
|
|
6859
8467
|
[BitType2.clozeSolutionGrouped]: {
|
|
6860
8468
|
since: "1.3.0",
|
|
6861
8469
|
baseBitType: BitType2.cloze,
|
|
6862
|
-
description: "Cloze solution grouped bit, used for cloze questions with grouped solutions"
|
|
8470
|
+
description: "Cloze solution grouped bit, used for cloze questions with grouped solutions",
|
|
8471
|
+
tags: [
|
|
8472
|
+
{
|
|
8473
|
+
key: ConfigKey.property_quizCountItems,
|
|
8474
|
+
description: "The number of items in the cloze quiz (solution-grouped default: true)",
|
|
8475
|
+
format: TagFormat.boolean,
|
|
8476
|
+
defaultValue: "true"
|
|
8477
|
+
},
|
|
8478
|
+
{
|
|
8479
|
+
key: ConfigKey.property_quizStrikethroughSolutions,
|
|
8480
|
+
description: "If the cloze solutions should be strikethrough (solution-grouped default: true)",
|
|
8481
|
+
format: TagFormat.boolean,
|
|
8482
|
+
defaultValue: "true"
|
|
8483
|
+
}
|
|
8484
|
+
]
|
|
6863
8485
|
},
|
|
6864
8486
|
[BitType2.clozeSeveral]: {
|
|
6865
8487
|
since: "3.5.0",
|
|
@@ -6895,6 +8517,17 @@ var BITS = {
|
|
|
6895
8517
|
{
|
|
6896
8518
|
key: ConfigKey.group_quizCommon,
|
|
6897
8519
|
description: "Common tags for quiz bits"
|
|
8520
|
+
},
|
|
8521
|
+
{
|
|
8522
|
+
// PLAN-084 (R12): bit-level @isCaseSensitive — empty exportJsonKey
|
|
8523
|
+
// (no bit-level emission); defaultValue 'true' phantom-fires onto
|
|
8524
|
+
// the ancestor stack so each listItem's gap chain `@absent: $ancestor`
|
|
8525
|
+
// rule resolves to true. Mirrors the cloze bit (bits.ts:1326-1336).
|
|
8526
|
+
key: ConfigKey.property_isCaseSensitive,
|
|
8527
|
+
exportJsonKey: {},
|
|
8528
|
+
description: "If the cloze answers are case sensitive",
|
|
8529
|
+
format: TagFormat.boolean,
|
|
8530
|
+
defaultValue: "true"
|
|
6898
8531
|
}
|
|
6899
8532
|
],
|
|
6900
8533
|
cardSet: CardSetConfigKey.clozeList
|
|
@@ -7061,6 +8694,7 @@ var BITS = {
|
|
|
7061
8694
|
tags: [
|
|
7062
8695
|
{
|
|
7063
8696
|
key: ConfigKey.group_person,
|
|
8697
|
+
exportJsonKey: { partner: { name: "$" } },
|
|
7064
8698
|
description: "Tags for the person in the conversation"
|
|
7065
8699
|
}
|
|
7066
8700
|
]
|
|
@@ -7491,7 +9125,12 @@ var BITS = {
|
|
|
7491
9125
|
maxCount: Count.infinity
|
|
7492
9126
|
},
|
|
7493
9127
|
{
|
|
7494
|
-
// Override inherited @example from group_standardTags:
|
|
9128
|
+
// Override inherited @example from group_standardTags: emits both
|
|
9129
|
+
// `isExample` (bool) AND `example` (paragraph array). Declared as
|
|
9130
|
+
// bitmarkText because BPG `JsonGenerator.toExample` renders the
|
|
9131
|
+
// root-example value through the bit's textFormat (not this tag's
|
|
9132
|
+
// declared format) — `TagFormat.plainText` here would be ignored
|
|
9133
|
+
// at runtime and is misleading.
|
|
7495
9134
|
key: ConfigKey.property_example,
|
|
7496
9135
|
exportJsonKey: [
|
|
7497
9136
|
{ "@keyonly": { isExample: true, example: "true" } },
|
|
@@ -7499,7 +9138,8 @@ var BITS = {
|
|
|
7499
9138
|
{ isExample: true, example: "$" }
|
|
7500
9139
|
],
|
|
7501
9140
|
description: "The example text for the essay bit",
|
|
7502
|
-
format: TagFormat.
|
|
9141
|
+
format: TagFormat.bitmarkText,
|
|
9142
|
+
nullable: true
|
|
7503
9143
|
}
|
|
7504
9144
|
],
|
|
7505
9145
|
rootExampleType: ExampleType.string
|
|
@@ -7533,7 +9173,8 @@ var BITS = {
|
|
|
7533
9173
|
{ isExample: true, example: "$" }
|
|
7534
9174
|
],
|
|
7535
9175
|
description: "The example text for the example bit",
|
|
7536
|
-
format: TagFormat.plainText
|
|
9176
|
+
format: TagFormat.plainText,
|
|
9177
|
+
nullable: true
|
|
7537
9178
|
}
|
|
7538
9179
|
],
|
|
7539
9180
|
rootExampleType: ExampleType.string
|
|
@@ -7619,7 +9260,19 @@ var BITS = {
|
|
|
7619
9260
|
[BitType2.articleAi]: {
|
|
7620
9261
|
since: "1.3.0",
|
|
7621
9262
|
baseBitType: BitType2.article,
|
|
7622
|
-
description: "Article AI bit, used to create AI-generated articles"
|
|
9263
|
+
description: "Article AI bit, used to create AI-generated articles",
|
|
9264
|
+
tags: [
|
|
9265
|
+
{
|
|
9266
|
+
// Shadows the inherited @aiGenerated from the standard group: this
|
|
9267
|
+
// bit type forces aiGenerated:true regardless of source value or
|
|
9268
|
+
// absence. Literal `true` in the export pattern ignores `$`.
|
|
9269
|
+
key: ConfigKey.property_aiGenerated,
|
|
9270
|
+
description: "AI-generated flag, forced true for article-ai bits",
|
|
9271
|
+
format: TagFormat.boolean,
|
|
9272
|
+
defaultValue: "true",
|
|
9273
|
+
exportJsonKey: { aiGenerated: true }
|
|
9274
|
+
}
|
|
9275
|
+
]
|
|
7623
9276
|
},
|
|
7624
9277
|
[BitType2.articleAttachment]: {
|
|
7625
9278
|
since: "1.3.0",
|
|
@@ -7944,7 +9597,19 @@ var BITS = {
|
|
|
7944
9597
|
[BitType2.noteAi]: {
|
|
7945
9598
|
since: "1.3.0",
|
|
7946
9599
|
baseBitType: BitType2.note,
|
|
7947
|
-
description: "Note AI bit, used to create AI-generated notes in articles or books"
|
|
9600
|
+
description: "Note AI bit, used to create AI-generated notes in articles or books",
|
|
9601
|
+
tags: [
|
|
9602
|
+
{
|
|
9603
|
+
// Shadows the inherited @aiGenerated from the standard group: this
|
|
9604
|
+
// bit type forces aiGenerated:true regardless of source value or
|
|
9605
|
+
// absence. Literal `true` in the export pattern ignores `$`.
|
|
9606
|
+
key: ConfigKey.property_aiGenerated,
|
|
9607
|
+
description: "AI-generated flag, forced true for article-ai bits",
|
|
9608
|
+
format: TagFormat.boolean,
|
|
9609
|
+
defaultValue: "true",
|
|
9610
|
+
exportJsonKey: { aiGenerated: true }
|
|
9611
|
+
}
|
|
9612
|
+
]
|
|
7948
9613
|
},
|
|
7949
9614
|
[BitType2.notebookArticle]: {
|
|
7950
9615
|
since: "1.3.0",
|
|
@@ -8069,7 +9734,19 @@ var BITS = {
|
|
|
8069
9734
|
[BitType2.summaryAi]: {
|
|
8070
9735
|
since: "1.3.0",
|
|
8071
9736
|
baseBitType: BitType2.summary,
|
|
8072
|
-
description: "AI-generated summary bit"
|
|
9737
|
+
description: "AI-generated summary bit",
|
|
9738
|
+
tags: [
|
|
9739
|
+
{
|
|
9740
|
+
// Shadows the inherited @aiGenerated from the standard group: this
|
|
9741
|
+
// bit type forces aiGenerated:true regardless of source value or
|
|
9742
|
+
// absence. Literal `true` in the export pattern ignores `$`.
|
|
9743
|
+
key: ConfigKey.property_aiGenerated,
|
|
9744
|
+
description: "AI-generated flag, forced true for article-ai bits",
|
|
9745
|
+
format: TagFormat.boolean,
|
|
9746
|
+
defaultValue: "true",
|
|
9747
|
+
exportJsonKey: { aiGenerated: true }
|
|
9748
|
+
}
|
|
9749
|
+
]
|
|
8073
9750
|
},
|
|
8074
9751
|
[BitType2.videoTranscript]: {
|
|
8075
9752
|
since: "1.3.0",
|
|
@@ -8343,7 +10020,7 @@ var BITS = {
|
|
|
8343
10020
|
description: "Product ID for the module product, used to link to a specific product",
|
|
8344
10021
|
format: TagFormat.plainText,
|
|
8345
10022
|
minCount: 1,
|
|
8346
|
-
maxCount:
|
|
10023
|
+
maxCount: 1
|
|
8347
10024
|
}
|
|
8348
10025
|
]
|
|
8349
10026
|
},
|
|
@@ -8396,10 +10073,13 @@ var BITS = {
|
|
|
8396
10073
|
description: "Resource bit tags for logo grave images, used to define additional properties"
|
|
8397
10074
|
},
|
|
8398
10075
|
{
|
|
8399
|
-
// Image resource
|
|
8400
|
-
|
|
10076
|
+
// Image resource — fully-expanded shape so each `&image` emission
|
|
10077
|
+
// produces a complete `{type, image: {src}}` object inside the
|
|
10078
|
+
// `images[]` array. Chain attrs like `[@zoomDisabled]` fold under
|
|
10079
|
+
// `images[].image` via the `[]` last-element-merge semantic.
|
|
10080
|
+
key: ConfigKey.group_resourceImageNoZoom,
|
|
8401
10081
|
description: "Resource image tags for logo grave images, used to attach images",
|
|
8402
|
-
exportJsonKey: { images: "$" },
|
|
10082
|
+
exportJsonKey: { images: [{ type: "image", image: { src: "$" } }] },
|
|
8403
10083
|
minCount: 1,
|
|
8404
10084
|
maxCount: Count.infinity
|
|
8405
10085
|
}
|
|
@@ -8689,6 +10369,10 @@ var BITS = {
|
|
|
8689
10369
|
tags: [
|
|
8690
10370
|
{
|
|
8691
10371
|
key: ConfigKey.property_book,
|
|
10372
|
+
// Starter-array shape: each `[@book:value]` chain appends a fresh
|
|
10373
|
+
// `{ book, reference }` entry. The chained `►` (tag_reference) lands
|
|
10374
|
+
// inside the most-recently-appended entry as `reference: $`.
|
|
10375
|
+
exportJsonKey: { book: [{ book: "$" }] },
|
|
8692
10376
|
description: "Book reference for the page, used to link to a specific book",
|
|
8693
10377
|
maxCount: Count.infinity,
|
|
8694
10378
|
chain: [
|
|
@@ -8756,7 +10440,9 @@ var BITS = {
|
|
|
8756
10440
|
format: TagFormat.plainText
|
|
8757
10441
|
}
|
|
8758
10442
|
],
|
|
8759
|
-
|
|
10443
|
+
// page-footer emits cards under `sections` (vs `listItems` for the
|
|
10444
|
+
// sibling exampleList / assignmentList bits) — see JsonGenerator.ts.
|
|
10445
|
+
cardSet: CardSetConfigKey.pageFooterSections
|
|
8760
10446
|
},
|
|
8761
10447
|
[BitType2.legend]: {
|
|
8762
10448
|
since: "3.12.0",
|
|
@@ -8798,13 +10484,52 @@ var BITS = {
|
|
|
8798
10484
|
since: "1.34.0",
|
|
8799
10485
|
baseBitType: BitType2._standard,
|
|
8800
10486
|
description: "Definition list bit, used to create lists of definitions in articles or books",
|
|
10487
|
+
tags: [
|
|
10488
|
+
{
|
|
10489
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
10490
|
+
key: ConfigKey.property_example,
|
|
10491
|
+
exportJsonKey: [
|
|
10492
|
+
{ "@keyonly": { isExample: true } },
|
|
10493
|
+
{ "@absent": { isExample: true } },
|
|
10494
|
+
{ isExample: true, example: "$" }
|
|
10495
|
+
],
|
|
10496
|
+
description: "The example(s) for the bit",
|
|
10497
|
+
format: TagFormat.bitmarkText,
|
|
10498
|
+
nullable: true
|
|
10499
|
+
}
|
|
10500
|
+
],
|
|
8801
10501
|
cardSet: CardSetConfigKey.definitionList
|
|
8802
10502
|
},
|
|
10503
|
+
[BitType2.definitionListCollapsible]: {
|
|
10504
|
+
since: "5.25.0",
|
|
10505
|
+
baseBitType: BitType2.definitionList,
|
|
10506
|
+
description: "Collapsible definition list bit, used to create collapsible lists of definitions in articles or books"
|
|
10507
|
+
},
|
|
10508
|
+
[BitType2.standardDefinitionListNormative]: {
|
|
10509
|
+
since: "5.24.0",
|
|
10510
|
+
baseBitType: BitType2.definitionList,
|
|
10511
|
+
description: "Standard normative definition list bit, used to create normative standard definition lists in articles or books"
|
|
10512
|
+
},
|
|
10513
|
+
[BitType2.standardDefinitionListNonNormative]: {
|
|
10514
|
+
since: "5.24.0",
|
|
10515
|
+
baseBitType: BitType2.definitionList,
|
|
10516
|
+
description: "Standard non-normative definition list bit, used to create non-normative standard definition lists in articles or books"
|
|
10517
|
+
},
|
|
10518
|
+
[BitType2.smartStandardDefinitionListNormative]: {
|
|
10519
|
+
since: "5.24.0",
|
|
10520
|
+
baseBitType: BitType2.standardDefinitionListNormative,
|
|
10521
|
+
description: "Smart standard normative definition list bit, used to create normative smart standard definition lists in articles or books"
|
|
10522
|
+
},
|
|
10523
|
+
[BitType2.smartStandardDefinitionListNonNormative]: {
|
|
10524
|
+
since: "5.24.0",
|
|
10525
|
+
baseBitType: BitType2.standardDefinitionListNonNormative,
|
|
10526
|
+
description: "Smart standard non-normative definition list bit, used to create non-normative smart standard definition lists in articles or books"
|
|
10527
|
+
},
|
|
8803
10528
|
[BitType2.metaSearchDefaultTerms]: {
|
|
8804
10529
|
since: "3.12.0",
|
|
8805
10530
|
baseBitType: BitType2._standard,
|
|
8806
10531
|
description: "Meta search default terms bit, used to define default search terms in articles",
|
|
8807
|
-
cardSet: CardSetConfigKey.
|
|
10532
|
+
cardSet: CardSetConfigKey.definitionListPlain
|
|
8808
10533
|
},
|
|
8809
10534
|
[BitType2.metaSearchDefaultTopics]: {
|
|
8810
10535
|
since: "3.12.0",
|
|
@@ -8820,6 +10545,18 @@ var BITS = {
|
|
|
8820
10545
|
{
|
|
8821
10546
|
key: ConfigKey.group_quizCommon,
|
|
8822
10547
|
description: "Common quiz tags for flashcards"
|
|
10548
|
+
},
|
|
10549
|
+
{
|
|
10550
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
10551
|
+
key: ConfigKey.property_example,
|
|
10552
|
+
exportJsonKey: [
|
|
10553
|
+
{ "@keyonly": { isExample: true } },
|
|
10554
|
+
{ "@absent": { isExample: true } },
|
|
10555
|
+
{ isExample: true, example: "$" }
|
|
10556
|
+
],
|
|
10557
|
+
description: "The example(s) for the bit",
|
|
10558
|
+
format: TagFormat.bitmarkText,
|
|
10559
|
+
nullable: true
|
|
8823
10560
|
}
|
|
8824
10561
|
],
|
|
8825
10562
|
cardSet: CardSetConfigKey.flashcard
|
|
@@ -8827,12 +10564,20 @@ var BITS = {
|
|
|
8827
10564
|
[BitType2.flashcard1]: {
|
|
8828
10565
|
since: "1.3.0",
|
|
8829
10566
|
baseBitType: BitType2.flashcard,
|
|
8830
|
-
description: "Flashcard 1 bit"
|
|
10567
|
+
description: "Flashcard 1 bit",
|
|
10568
|
+
// PLAN-085: use the flashcard1 cardset (sections.default.maxCount=1)
|
|
10569
|
+
// instead of inheriting `flashcard`'s unbounded cardset, since this
|
|
10570
|
+
// bit type structurally allows only a single card.
|
|
10571
|
+
cardSet: CardSetConfigKey.flashcard1
|
|
8831
10572
|
},
|
|
8832
10573
|
[BitType2.qAndACard]: {
|
|
8833
10574
|
since: "3.25.0",
|
|
8834
10575
|
baseBitType: BitType2.flashcard1,
|
|
8835
|
-
description: "Q&A card bit, used to create question and answer cards in articles or books"
|
|
10576
|
+
description: "Q&A card bit, used to create question and answer cards in articles or books",
|
|
10577
|
+
// PLAN-085: q-and-a-card permits multiple cards (some fixtures carry
|
|
10578
|
+
// two). Override the inherited `flashcard1` (max=1) cardset back to
|
|
10579
|
+
// the unbounded `flashcard` cardset.
|
|
10580
|
+
cardSet: CardSetConfigKey.flashcard
|
|
8836
10581
|
},
|
|
8837
10582
|
[BitType2.focusImage]: {
|
|
8838
10583
|
since: "1.3.0",
|
|
@@ -8862,8 +10607,24 @@ var BITS = {
|
|
|
8862
10607
|
description: "Common quiz tags for highlighted text"
|
|
8863
10608
|
},
|
|
8864
10609
|
{
|
|
8865
|
-
|
|
8866
|
-
|
|
10610
|
+
// Dedicated highlight inline-select chain: tag_true / tag_false
|
|
10611
|
+
// emit `{type:"highlight", attrs:{texts:[{text,isCorrect?}]}}`
|
|
10612
|
+
// inline body nodes via @body-inline. Bit shares no tag IDs with
|
|
10613
|
+
// multiple-choice / multiple-choice-1 (which use group_trueFalse).
|
|
10614
|
+
key: ConfigKey.group_trueFalseInlineHighlight,
|
|
10615
|
+
description: "True/False quiz tags for highlighted text (inline body nodes)"
|
|
10616
|
+
},
|
|
10617
|
+
{
|
|
10618
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
10619
|
+
key: ConfigKey.property_example,
|
|
10620
|
+
exportJsonKey: [
|
|
10621
|
+
{ "@keyonly": { isExample: true } },
|
|
10622
|
+
{ "@absent": { isExample: true } },
|
|
10623
|
+
{ isExample: true, example: "$" }
|
|
10624
|
+
],
|
|
10625
|
+
description: "The example(s) for the bit",
|
|
10626
|
+
format: TagFormat.bitmarkText,
|
|
10627
|
+
nullable: true
|
|
8867
10628
|
}
|
|
8868
10629
|
]
|
|
8869
10630
|
},
|
|
@@ -8873,15 +10634,8 @@ var BITS = {
|
|
|
8873
10634
|
description: "Image bit, used to create images in articles or books",
|
|
8874
10635
|
tags: [
|
|
8875
10636
|
{
|
|
8876
|
-
key: ConfigKey.
|
|
8877
|
-
|
|
8878
|
-
description: "Background wallpaper for the image, used to set a background for the image",
|
|
8879
|
-
chain: [
|
|
8880
|
-
{
|
|
8881
|
-
key: ConfigKey.group_resourceImageCommon,
|
|
8882
|
-
description: "Common resource image tags for images"
|
|
8883
|
-
}
|
|
8884
|
-
]
|
|
10637
|
+
key: ConfigKey.group_backgroundWallpaper,
|
|
10638
|
+
description: "Background wallpaper tags for images, used to define background properties for images"
|
|
8885
10639
|
},
|
|
8886
10640
|
{
|
|
8887
10641
|
key: ConfigKey.group_resourceBitTags,
|
|
@@ -8984,8 +10738,15 @@ var BITS = {
|
|
|
8984
10738
|
},
|
|
8985
10739
|
[BitType2.imageSeparator]: {
|
|
8986
10740
|
since: "1.4.15",
|
|
8987
|
-
baseBitType: BitType2.
|
|
8988
|
-
description: "Image separator bit, used to create separators in articles or books"
|
|
10741
|
+
baseBitType: BitType2._standard,
|
|
10742
|
+
description: "Image separator bit, used to create separators in articles or books",
|
|
10743
|
+
tags: [
|
|
10744
|
+
{
|
|
10745
|
+
key: ConfigKey.group_imageNoZoom,
|
|
10746
|
+
description: "Image no zoom tags for images, used to define whether images should have zoom functionality"
|
|
10747
|
+
}
|
|
10748
|
+
],
|
|
10749
|
+
resourceAttachmentAllowed: false
|
|
8989
10750
|
},
|
|
8990
10751
|
[BitType2.imageSeparatorAlt]: {
|
|
8991
10752
|
since: "1.16.0",
|
|
@@ -9024,15 +10785,20 @@ var BITS = {
|
|
|
9024
10785
|
},
|
|
9025
10786
|
[BitType2.pageBanner]: {
|
|
9026
10787
|
since: "1.4.3",
|
|
9027
|
-
baseBitType: BitType2.
|
|
10788
|
+
baseBitType: BitType2._standard,
|
|
9028
10789
|
description: "Page banner bit, used to create banners for pages in articles or books",
|
|
9029
10790
|
tags: [
|
|
9030
10791
|
{
|
|
9031
10792
|
key: ConfigKey.property_slug,
|
|
9032
10793
|
description: "Slug for the page banner, used to identify the banner in the system",
|
|
9033
10794
|
format: TagFormat.plainText
|
|
10795
|
+
},
|
|
10796
|
+
{
|
|
10797
|
+
key: ConfigKey.group_imageNoZoom,
|
|
10798
|
+
description: "Image no zoom tags for images, used to define whether images should have zoom functionality"
|
|
9034
10799
|
}
|
|
9035
|
-
]
|
|
10800
|
+
],
|
|
10801
|
+
resourceAttachmentAllowed: false
|
|
9036
10802
|
},
|
|
9037
10803
|
[BitType2.pageHero]: {
|
|
9038
10804
|
since: "1.11.0",
|
|
@@ -9047,7 +10813,7 @@ var BITS = {
|
|
|
9047
10813
|
},
|
|
9048
10814
|
[BitType2.tableImage]: {
|
|
9049
10815
|
since: "1.5.15",
|
|
9050
|
-
baseBitType: BitType2.
|
|
10816
|
+
baseBitType: BitType2.tableExtended,
|
|
9051
10817
|
description: "Table image bit, used to create images in tables in articles or books",
|
|
9052
10818
|
tags: [
|
|
9053
10819
|
{
|
|
@@ -9056,15 +10822,8 @@ var BITS = {
|
|
|
9056
10822
|
format: TagFormat.bitmarkText
|
|
9057
10823
|
},
|
|
9058
10824
|
{
|
|
9059
|
-
key: ConfigKey.
|
|
9060
|
-
|
|
9061
|
-
description: "Background wallpaper for the image, used to set a background for the image",
|
|
9062
|
-
chain: [
|
|
9063
|
-
{
|
|
9064
|
-
key: ConfigKey.group_resourceImageCommon,
|
|
9065
|
-
description: "Common resource image tags for images"
|
|
9066
|
-
}
|
|
9067
|
-
]
|
|
10825
|
+
key: ConfigKey.group_backgroundWallpaper,
|
|
10826
|
+
description: "Background wallpaper tags for images, used to define background properties for images"
|
|
9068
10827
|
},
|
|
9069
10828
|
{
|
|
9070
10829
|
key: ConfigKey.group_resourceBitTags,
|
|
@@ -9207,10 +10966,10 @@ var BITS = {
|
|
|
9207
10966
|
description: "Resource bit tags for logo grave images, used to define additional properties"
|
|
9208
10967
|
},
|
|
9209
10968
|
{
|
|
9210
|
-
// Image resource
|
|
9211
|
-
key: ConfigKey.
|
|
10969
|
+
// Image resource — fully-expanded shape (see BitType.extractorImage).
|
|
10970
|
+
key: ConfigKey.group_resourceImageNoZoom,
|
|
9212
10971
|
description: "Resource image tags for logo grave images, used to attach images",
|
|
9213
|
-
exportJsonKey: { logos: "$" },
|
|
10972
|
+
exportJsonKey: { logos: [{ type: "image", image: { src: "$" } }] },
|
|
9214
10973
|
minCount: 1,
|
|
9215
10974
|
maxCount: Count.infinity
|
|
9216
10975
|
}
|
|
@@ -9220,7 +10979,17 @@ var BITS = {
|
|
|
9220
10979
|
[BitType2.prototypeImages]: {
|
|
9221
10980
|
since: "1.6.1",
|
|
9222
10981
|
baseBitType: BitType2.imagesLogoGrave,
|
|
9223
|
-
description: "Prototype images bit, used to create prototype images in articles or books"
|
|
10982
|
+
description: "Prototype images bit, used to create prototype images in articles or books",
|
|
10983
|
+
tags: [
|
|
10984
|
+
{
|
|
10985
|
+
// Override inherited `logos` wrapper with `images` for this bit type.
|
|
10986
|
+
key: ConfigKey.group_resourceImageNoZoom,
|
|
10987
|
+
description: "Resource image tags for prototype images",
|
|
10988
|
+
exportJsonKey: { images: [{ type: "image", image: { src: "$" } }] },
|
|
10989
|
+
minCount: 1,
|
|
10990
|
+
maxCount: Count.infinity
|
|
10991
|
+
}
|
|
10992
|
+
]
|
|
9224
10993
|
},
|
|
9225
10994
|
[BitType2.internalLink]: {
|
|
9226
10995
|
since: "1.3.0",
|
|
@@ -9250,6 +11019,18 @@ var BITS = {
|
|
|
9250
11019
|
exportJsonKey: {},
|
|
9251
11020
|
description: "Reasonable number of characters for the interview, used to limit input size",
|
|
9252
11021
|
format: TagFormat.number
|
|
11022
|
+
},
|
|
11023
|
+
{
|
|
11024
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
11025
|
+
key: ConfigKey.property_example,
|
|
11026
|
+
exportJsonKey: [
|
|
11027
|
+
{ "@keyonly": { isExample: true } },
|
|
11028
|
+
{ "@absent": { isExample: true } },
|
|
11029
|
+
{ isExample: true, example: "$" }
|
|
11030
|
+
],
|
|
11031
|
+
description: "The example(s) for the bit",
|
|
11032
|
+
format: TagFormat.bitmarkText,
|
|
11033
|
+
nullable: true
|
|
9253
11034
|
}
|
|
9254
11035
|
],
|
|
9255
11036
|
cardSet: CardSetConfigKey.questions
|
|
@@ -9608,6 +11389,18 @@ var BITS = {
|
|
|
9608
11389
|
{
|
|
9609
11390
|
key: ConfigKey.group_mark,
|
|
9610
11391
|
description: "Mark tags, used to define the content of marks"
|
|
11392
|
+
},
|
|
11393
|
+
{
|
|
11394
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
11395
|
+
key: ConfigKey.property_example,
|
|
11396
|
+
exportJsonKey: [
|
|
11397
|
+
{ "@keyonly": { isExample: true } },
|
|
11398
|
+
{ "@absent": { isExample: true } },
|
|
11399
|
+
{ isExample: true, example: "$" }
|
|
11400
|
+
],
|
|
11401
|
+
description: "The example(s) for the bit",
|
|
11402
|
+
format: TagFormat.bitmarkText,
|
|
11403
|
+
nullable: true
|
|
9611
11404
|
}
|
|
9612
11405
|
]
|
|
9613
11406
|
},
|
|
@@ -9627,6 +11420,23 @@ var BITS = {
|
|
|
9627
11420
|
description: "Case sensitivity for matching pairs, used to define if matches are case-sensitive",
|
|
9628
11421
|
format: TagFormat.boolean,
|
|
9629
11422
|
defaultValue: "true"
|
|
11423
|
+
},
|
|
11424
|
+
{
|
|
11425
|
+
// Override inherited @example: in `isTopLevelExample` allow-list
|
|
11426
|
+
// (covers match, matchAll, matchReverse, matchAllReverse,
|
|
11427
|
+
// matchSolutionGrouped, matchMatrix, matchAudio, matchPicture).
|
|
11428
|
+
// Bit-level fires emit only `isExample`; the @example value (when
|
|
11429
|
+
// provided) is recorded in the ancestor stack and consumed by
|
|
11430
|
+
// per-pair/per-cell variant overrides — not surfaced at bit scope.
|
|
11431
|
+
key: ConfigKey.property_example,
|
|
11432
|
+
exportJsonKey: [
|
|
11433
|
+
{ "@keyonly": { isExample: true } },
|
|
11434
|
+
{ "@absent": { isExample: true } },
|
|
11435
|
+
{ isExample: true }
|
|
11436
|
+
],
|
|
11437
|
+
description: "The example(s) for the bit",
|
|
11438
|
+
format: TagFormat.bitmarkText,
|
|
11439
|
+
nullable: true
|
|
9630
11440
|
}
|
|
9631
11441
|
],
|
|
9632
11442
|
cardSet: CardSetConfigKey.matchPairs
|
|
@@ -9711,6 +11521,18 @@ var BITS = {
|
|
|
9711
11521
|
{
|
|
9712
11522
|
key: ConfigKey.group_trueFalse,
|
|
9713
11523
|
description: "True/False tags for multiple choice 1 questions"
|
|
11524
|
+
},
|
|
11525
|
+
{
|
|
11526
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
11527
|
+
key: ConfigKey.property_example,
|
|
11528
|
+
exportJsonKey: [
|
|
11529
|
+
{ "@keyonly": { isExample: true } },
|
|
11530
|
+
{ "@absent": { isExample: true } },
|
|
11531
|
+
{ isExample: true, example: "$" }
|
|
11532
|
+
],
|
|
11533
|
+
description: "The example(s) for the bit",
|
|
11534
|
+
format: TagFormat.bitmarkText,
|
|
11535
|
+
nullable: true
|
|
9714
11536
|
}
|
|
9715
11537
|
]
|
|
9716
11538
|
},
|
|
@@ -9732,8 +11554,20 @@ var BITS = {
|
|
|
9732
11554
|
{
|
|
9733
11555
|
key: ConfigKey.group_trueFalse,
|
|
9734
11556
|
description: "True/False tags for multiple choice questions"
|
|
9735
|
-
}
|
|
11557
|
+
},
|
|
9736
11558
|
// This is actually for multiple-choice-1, but we support it here as well (as many bits are wrong)
|
|
11559
|
+
{
|
|
11560
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
11561
|
+
key: ConfigKey.property_example,
|
|
11562
|
+
exportJsonKey: [
|
|
11563
|
+
{ "@keyonly": { isExample: true } },
|
|
11564
|
+
{ "@absent": { isExample: true } },
|
|
11565
|
+
{ isExample: true, example: "$" }
|
|
11566
|
+
],
|
|
11567
|
+
description: "The example(s) for the bit",
|
|
11568
|
+
format: TagFormat.bitmarkText,
|
|
11569
|
+
nullable: true
|
|
11570
|
+
}
|
|
9737
11571
|
],
|
|
9738
11572
|
cardSet: CardSetConfigKey.quiz
|
|
9739
11573
|
},
|
|
@@ -9753,8 +11587,20 @@ var BITS = {
|
|
|
9753
11587
|
description: "Common quiz tags for multiple choice text questions"
|
|
9754
11588
|
},
|
|
9755
11589
|
{
|
|
9756
|
-
key: ConfigKey.
|
|
11590
|
+
key: ConfigKey.group_trueFalseInlineSelect,
|
|
9757
11591
|
description: "True/False tags for multiple choice text questions"
|
|
11592
|
+
},
|
|
11593
|
+
{
|
|
11594
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
11595
|
+
key: ConfigKey.property_example,
|
|
11596
|
+
exportJsonKey: [
|
|
11597
|
+
{ "@keyonly": { isExample: true } },
|
|
11598
|
+
{ "@absent": { isExample: true } },
|
|
11599
|
+
{ isExample: true, example: "$" }
|
|
11600
|
+
],
|
|
11601
|
+
description: "The example(s) for the bit",
|
|
11602
|
+
format: TagFormat.bitmarkText,
|
|
11603
|
+
nullable: true
|
|
9758
11604
|
}
|
|
9759
11605
|
]
|
|
9760
11606
|
},
|
|
@@ -9779,8 +11625,20 @@ var BITS = {
|
|
|
9779
11625
|
description: "Common quiz tags for multiple response 1 questions"
|
|
9780
11626
|
},
|
|
9781
11627
|
{
|
|
9782
|
-
key: ConfigKey.
|
|
11628
|
+
key: ConfigKey.group_trueFalseResponses,
|
|
9783
11629
|
description: "True/False tags for multiple response 1 questions"
|
|
11630
|
+
},
|
|
11631
|
+
{
|
|
11632
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
11633
|
+
key: ConfigKey.property_example,
|
|
11634
|
+
exportJsonKey: [
|
|
11635
|
+
{ "@keyonly": { isExample: true } },
|
|
11636
|
+
{ "@absent": { isExample: true } },
|
|
11637
|
+
{ isExample: true, example: "$" }
|
|
11638
|
+
],
|
|
11639
|
+
description: "The example(s) for the bit",
|
|
11640
|
+
format: TagFormat.bitmarkText,
|
|
11641
|
+
nullable: true
|
|
9784
11642
|
}
|
|
9785
11643
|
]
|
|
9786
11644
|
},
|
|
@@ -9800,12 +11658,24 @@ var BITS = {
|
|
|
9800
11658
|
description: "Common quiz tags for multiple response questions"
|
|
9801
11659
|
},
|
|
9802
11660
|
{
|
|
9803
|
-
key: ConfigKey.
|
|
11661
|
+
key: ConfigKey.group_trueFalseResponses,
|
|
9804
11662
|
description: "True/False tags for multiple response questions"
|
|
9805
|
-
}
|
|
11663
|
+
},
|
|
9806
11664
|
// This is actually for multiple-response-1, but we support it here as well (as many bits are wrong)
|
|
11665
|
+
{
|
|
11666
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
11667
|
+
key: ConfigKey.property_example,
|
|
11668
|
+
exportJsonKey: [
|
|
11669
|
+
{ "@keyonly": { isExample: true } },
|
|
11670
|
+
{ "@absent": { isExample: true } },
|
|
11671
|
+
{ isExample: true, example: "$" }
|
|
11672
|
+
],
|
|
11673
|
+
description: "The example(s) for the bit",
|
|
11674
|
+
format: TagFormat.bitmarkText,
|
|
11675
|
+
nullable: true
|
|
11676
|
+
}
|
|
9807
11677
|
],
|
|
9808
|
-
cardSet: CardSetConfigKey.
|
|
11678
|
+
cardSet: CardSetConfigKey.quizResponses
|
|
9809
11679
|
},
|
|
9810
11680
|
[BitType2.coachSelfReflectionMultipleResponse]: {
|
|
9811
11681
|
since: "1.3.0",
|
|
@@ -10055,6 +11925,8 @@ var BITS = {
|
|
|
10055
11925
|
},
|
|
10056
11926
|
{
|
|
10057
11927
|
key: ConfigKey.resource_platformIcon,
|
|
11928
|
+
jsonKey: "platformIcon|resource(type=image, key=image)",
|
|
11929
|
+
exportJsonKey: { platformIcon: { type: "image", image: { src: "$" } } },
|
|
10058
11930
|
description: "The platform icon",
|
|
10059
11931
|
chain: [
|
|
10060
11932
|
{
|
|
@@ -10090,6 +11962,8 @@ var BITS = {
|
|
|
10090
11962
|
tags: [
|
|
10091
11963
|
{
|
|
10092
11964
|
key: ConfigKey.resource_platformLogo,
|
|
11965
|
+
jsonKey: "platformLogo|resource(type=image, key=image)",
|
|
11966
|
+
exportJsonKey: { platformLogo: { type: "image", image: { src: "$" } } },
|
|
10093
11967
|
description: "The platform logo",
|
|
10094
11968
|
chain: [
|
|
10095
11969
|
{
|
|
@@ -10301,7 +12175,8 @@ var BITS = {
|
|
|
10301
12175
|
{ isExample: true, example: "$" }
|
|
10302
12176
|
],
|
|
10303
12177
|
description: "The example text for the review note bit",
|
|
10304
|
-
format: TagFormat.plainText
|
|
12178
|
+
format: TagFormat.plainText,
|
|
12179
|
+
nullable: true
|
|
10305
12180
|
}
|
|
10306
12181
|
],
|
|
10307
12182
|
rootExampleType: ExampleType.string
|
|
@@ -10388,7 +12263,8 @@ var BITS = {
|
|
|
10388
12263
|
{ isExample: true, example: "$" }
|
|
10389
12264
|
],
|
|
10390
12265
|
description: "The example flag for the bit (boolean)",
|
|
10391
|
-
format: TagFormat.
|
|
12266
|
+
format: TagFormat.boolean,
|
|
12267
|
+
nullable: true
|
|
10392
12268
|
}
|
|
10393
12269
|
],
|
|
10394
12270
|
cardSet: CardSetConfigKey.elements,
|
|
@@ -10662,15 +12538,8 @@ var BITS = {
|
|
|
10662
12538
|
format: TagFormat.bitmarkText
|
|
10663
12539
|
},
|
|
10664
12540
|
{
|
|
10665
|
-
key: ConfigKey.
|
|
10666
|
-
|
|
10667
|
-
description: "Background wallpaper for the image, used to set a background for the image",
|
|
10668
|
-
chain: [
|
|
10669
|
-
{
|
|
10670
|
-
key: ConfigKey.group_resourceImageCommon,
|
|
10671
|
-
description: "Common resource image tags for images"
|
|
10672
|
-
}
|
|
10673
|
-
]
|
|
12541
|
+
key: ConfigKey.group_backgroundWallpaper,
|
|
12542
|
+
description: "Background wallpaper tags for images, used to define background properties for images"
|
|
10674
12543
|
},
|
|
10675
12544
|
{
|
|
10676
12545
|
key: ConfigKey.group_resourceBitTags,
|
|
@@ -11047,16 +12916,20 @@ var BITS = {
|
|
|
11047
12916
|
format: TagFormat.plainText
|
|
11048
12917
|
},
|
|
11049
12918
|
{
|
|
11050
|
-
|
|
12919
|
+
// PLAN-067 Edit #1: true-false-1's `+` / `-` emit a flat
|
|
12920
|
+
// `{ statement, isCorrect }` at bit root (single-statement bit),
|
|
12921
|
+
// not a `choices[]` array. Format is bitmarkText so the statement
|
|
12922
|
+
// body renders as a ProseMirror tree, matching reference fixtures.
|
|
12923
|
+
exportJsonKey: { statement: "$", isCorrect: true },
|
|
11051
12924
|
key: ConfigKey.tag_true,
|
|
11052
12925
|
description: "Tag for the true option in the true/false question",
|
|
11053
|
-
format: TagFormat.
|
|
12926
|
+
format: TagFormat.bitmarkText
|
|
11054
12927
|
},
|
|
11055
12928
|
{
|
|
11056
|
-
exportJsonKey: {
|
|
12929
|
+
exportJsonKey: { statement: "$", isCorrect: false },
|
|
11057
12930
|
key: ConfigKey.tag_false,
|
|
11058
12931
|
description: "Tag for the false option in the true/false question",
|
|
11059
|
-
format: TagFormat.
|
|
12932
|
+
format: TagFormat.bitmarkText
|
|
11060
12933
|
},
|
|
11061
12934
|
{
|
|
11062
12935
|
// Override inherited @example from group_standardTags: boolean root
|
|
@@ -11067,14 +12940,24 @@ var BITS = {
|
|
|
11067
12940
|
// sufficient for `[+...]` (correct) statements which dominate the
|
|
11068
12941
|
// fixtures; `[-...]` (incorrect) bits with bare `[@example]` would
|
|
11069
12942
|
// need either a runtime extension or an explicit `[@example:false]`.
|
|
12943
|
+
//
|
|
12944
|
+
// Format is `boolean` so `[@example:false]` coerces to JSON `false`
|
|
12945
|
+
// (a natural default that gets stripped in optimised mode, matching
|
|
12946
|
+
// BPG's `rootExampleType: boolean`). PlainText would emit the
|
|
12947
|
+
// STRING `"false"` which doesn't strip.
|
|
11070
12948
|
key: ConfigKey.property_example,
|
|
11071
12949
|
exportJsonKey: [
|
|
11072
12950
|
{ "@keyonly": { isExample: true, example: true } },
|
|
11073
12951
|
{ "@absent": { isExample: true, example: true } },
|
|
12952
|
+
// `[@example:false]`: emit `isExample: true` only — `example: false`
|
|
12953
|
+
// is the natural default for boolean, stripped in optimised mode.
|
|
12954
|
+
// Mirrors BPG `removeUnwantedProperties` `ignoreAllFalse: true`.
|
|
12955
|
+
{ "@value=false": { isExample: true } },
|
|
11074
12956
|
{ isExample: true, example: "$" }
|
|
11075
12957
|
],
|
|
11076
12958
|
description: "The example flag for the bit (boolean)",
|
|
11077
|
-
format: TagFormat.
|
|
12959
|
+
format: TagFormat.boolean,
|
|
12960
|
+
nullable: true
|
|
11078
12961
|
}
|
|
11079
12962
|
],
|
|
11080
12963
|
rootExampleType: ExampleType.boolean
|
|
@@ -11098,6 +12981,18 @@ var BITS = {
|
|
|
11098
12981
|
key: ConfigKey.property_labelFalse,
|
|
11099
12982
|
description: "Label for the false option in the true/false question",
|
|
11100
12983
|
format: TagFormat.plainText
|
|
12984
|
+
},
|
|
12985
|
+
{
|
|
12986
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
12987
|
+
key: ConfigKey.property_example,
|
|
12988
|
+
exportJsonKey: [
|
|
12989
|
+
{ "@keyonly": { isExample: true } },
|
|
12990
|
+
{ "@absent": { isExample: true } },
|
|
12991
|
+
{ isExample: true, example: "$" }
|
|
12992
|
+
],
|
|
12993
|
+
description: "The example(s) for the bit",
|
|
12994
|
+
format: TagFormat.bitmarkText,
|
|
12995
|
+
nullable: true
|
|
11101
12996
|
}
|
|
11102
12997
|
],
|
|
11103
12998
|
cardSet: CardSetConfigKey.statements
|
|
@@ -11287,6 +13182,11 @@ var BITS = {
|
|
|
11287
13182
|
baseBitType: BitType2.vendorStripePricingTable,
|
|
11288
13183
|
description: "Stripe pricing table bit with print-on-request customer/product IDs, used to embed Stripe pricing tables tied to a specific print-on-request customer and product",
|
|
11289
13184
|
tags: [
|
|
13185
|
+
{
|
|
13186
|
+
key: ConfigKey.property_stripePricingTableDescriptiveName,
|
|
13187
|
+
description: "Descriptive name for the Stripe pricing table",
|
|
13188
|
+
format: TagFormat.plainText
|
|
13189
|
+
},
|
|
11290
13190
|
{
|
|
11291
13191
|
key: ConfigKey.property_printOnRequestCustomerId,
|
|
11292
13192
|
description: "Print-on-request customer ID, used to identify the customer for the print-on-request flow",
|
|
@@ -11763,7 +13663,7 @@ var instance2 = new Config();
|
|
|
11763
13663
|
// src/generated/package_info.ts
|
|
11764
13664
|
var PACKAGE_INFO = {
|
|
11765
13665
|
"name": "@gmb/bitmark-parser-generator",
|
|
11766
|
-
"version": "5.
|
|
13666
|
+
"version": "5.25.0",
|
|
11767
13667
|
"author": "Get More Brain Ltd",
|
|
11768
13668
|
"license": "ISC",
|
|
11769
13669
|
"description": "A bitmark parser and generator using Peggy.js"
|
|
@@ -12409,6 +14309,8 @@ var NodeType = {
|
|
|
12409
14309
|
internalPrintPdf: "internalPrintPdf",
|
|
12410
14310
|
internalPrintPdfValue: "internalPrintPdfValue",
|
|
12411
14311
|
isCaseSensitive: "isCaseSensitive",
|
|
14312
|
+
isCollapsible: "isCollapsible",
|
|
14313
|
+
isCollapsibleValue: "isCollapsibleValue",
|
|
12412
14314
|
isCommented: "isCommented",
|
|
12413
14315
|
isCorrect: "isCorrect",
|
|
12414
14316
|
isEditable: "isEditable",
|
|
@@ -12728,6 +14630,8 @@ var NodeType = {
|
|
|
12728
14630
|
stillImageFilmLink: "stillImageFilmLink",
|
|
12729
14631
|
stripePricingTableId: "stripePricingTableId",
|
|
12730
14632
|
stripePricingTableIdValue: "stripePricingTableIdValue",
|
|
14633
|
+
stripePricingTableDescriptiveName: "stripePricingTableDescriptiveName",
|
|
14634
|
+
stripePricingTableDescriptiveNameValue: "stripePricingTableDescriptiveNameValue",
|
|
12731
14635
|
stripePublishableKey: "stripePublishableKey",
|
|
12732
14636
|
stripePublishableKeyValue: "stripePublishableKeyValue",
|
|
12733
14637
|
subject: "subject",
|
|
@@ -26153,6 +28057,12 @@ var Builder = class extends BaseBuilder {
|
|
|
26153
28057
|
data.stripePricingTableId,
|
|
26154
28058
|
options
|
|
26155
28059
|
),
|
|
28060
|
+
stripePricingTableDescriptiveName: this.toAstProperty(
|
|
28061
|
+
bitType,
|
|
28062
|
+
ConfigKey.property_stripePricingTableDescriptiveName,
|
|
28063
|
+
data.stripePricingTableDescriptiveName,
|
|
28064
|
+
options
|
|
28065
|
+
),
|
|
26156
28066
|
stripePublishableKey: this.toAstProperty(
|
|
26157
28067
|
bitType,
|
|
26158
28068
|
ConfigKey.property_stripePublishableKey,
|
|
@@ -26639,6 +28549,12 @@ var Builder = class extends BaseBuilder {
|
|
|
26639
28549
|
level: instance6.asNumber(data.level),
|
|
26640
28550
|
toc: this.toAstProperty(bitType, ConfigKey.property_toc, data.toc, options),
|
|
26641
28551
|
progress: this.toAstProperty(bitType, ConfigKey.property_progress, data.progress, options),
|
|
28552
|
+
isCollapsible: this.toAstProperty(
|
|
28553
|
+
bitType,
|
|
28554
|
+
ConfigKey.property_isCollapsible,
|
|
28555
|
+
data.isCollapsible,
|
|
28556
|
+
options
|
|
28557
|
+
),
|
|
26642
28558
|
anchor: data.anchor,
|
|
26643
28559
|
reference: data.reference,
|
|
26644
28560
|
referenceEnd: data.referenceEnd,
|
|
@@ -32185,6 +34101,7 @@ var JsonGenerator = class extends AstWalkerGenerator {
|
|
|
32185
34101
|
if (instance2.isOfBitType(bitType, BitType2.chapter)) {
|
|
32186
34102
|
if (bitJson.toc == null) bitJson.toc = true;
|
|
32187
34103
|
if (bitJson.progress == null) bitJson.progress = true;
|
|
34104
|
+
if (bitJson.isCollapsible == null) bitJson.isCollapsible = false;
|
|
32188
34105
|
if (bitJson.level == null) bitJson.level = 1;
|
|
32189
34106
|
if (bitJson.body == null) bitJson.body = this.bodyDefault;
|
|
32190
34107
|
}
|
|
@@ -34380,7 +36297,9 @@ function buildCards(context, bitType, textFormat, parsedCardSet, statementV1, st
|
|
|
34380
36297
|
const cardSetType = bitConfig.cardSet?.configKey;
|
|
34381
36298
|
switch (cardSetType) {
|
|
34382
36299
|
case CardSetConfigKey.flashcard:
|
|
36300
|
+
case CardSetConfigKey.flashcard1:
|
|
34383
36301
|
case CardSetConfigKey.definitionList:
|
|
36302
|
+
case CardSetConfigKey.definitionListPlain:
|
|
34384
36303
|
result = parseFlashcardOrDefinitionList(context, bitType, processedCardSet);
|
|
34385
36304
|
break;
|
|
34386
36305
|
case CardSetConfigKey.elements:
|
|
@@ -34393,6 +36312,7 @@ function buildCards(context, bitType, textFormat, parsedCardSet, statementV1, st
|
|
|
34393
36312
|
result = parseFeedback(context, bitType, processedCardSet);
|
|
34394
36313
|
break;
|
|
34395
36314
|
case CardSetConfigKey.quiz:
|
|
36315
|
+
case CardSetConfigKey.quizResponses:
|
|
34396
36316
|
result = parseQuiz(context, bitType, processedCardSet, choicesV1, responsesV1);
|
|
34397
36317
|
break;
|
|
34398
36318
|
case CardSetConfigKey.questions:
|
|
@@ -34419,6 +36339,7 @@ function buildCards(context, bitType, textFormat, parsedCardSet, statementV1, st
|
|
|
34419
36339
|
break;
|
|
34420
36340
|
case CardSetConfigKey.clozeList:
|
|
34421
36341
|
case CardSetConfigKey.exampleBitList:
|
|
36342
|
+
case CardSetConfigKey.pageFooterSections:
|
|
34422
36343
|
case CardSetConfigKey.bookReferenceList:
|
|
34423
36344
|
result = parseCardBits(context, bitType, textFormat, processedCardSet);
|
|
34424
36345
|
break;
|
|
@@ -40445,7 +42366,11 @@ var InputFormat = {
|
|
|
40445
42366
|
/**
|
|
40446
42367
|
* Input is plain text
|
|
40447
42368
|
*/
|
|
40448
|
-
plainText: "plainText"
|
|
42369
|
+
plainText: "plainText",
|
|
42370
|
+
/**
|
|
42371
|
+
* Input is HTML (used by convertHtmlTable)
|
|
42372
|
+
*/
|
|
42373
|
+
html: "html"
|
|
40449
42374
|
};
|
|
40450
42375
|
var BitmarkParserGenerator = class {
|
|
40451
42376
|
constructor() {
|
|
@@ -40980,6 +42905,87 @@ var BitmarkParserGenerator = class {
|
|
|
40980
42905
|
const res = generator.generate(data);
|
|
40981
42906
|
return res;
|
|
40982
42907
|
}
|
|
42908
|
+
/**
|
|
42909
|
+
* Convert between HTML tables and bitmark tables.
|
|
42910
|
+
*
|
|
42911
|
+
* Direction is auto-detected (override with `options.inputFormat`):
|
|
42912
|
+
* - HTML input: every `<table>` is extracted and emitted as a `[.table-extended]` (or `[.table]`)
|
|
42913
|
+
* bit; output as bitmark (default), JSON, AST, or HTML (re-emit).
|
|
42914
|
+
* - bitmark / JSON / AST input: each `table` / `table-extended` bit is rendered as an HTML
|
|
42915
|
+
* `<table>` fragment.
|
|
42916
|
+
*
|
|
42917
|
+
* Cell content is reproduced verbatim except inline conversion to bitmark marks (bold, italic,
|
|
42918
|
+
* superscript, etc.), with lists and images mapped to their bitmark equivalents.
|
|
42919
|
+
*
|
|
42920
|
+
* @param input - HTML, bitmark, JSON or AST string; or a parsed JSON/AST value; or a file path.
|
|
42921
|
+
* @param options - the conversion options
|
|
42922
|
+
* @returns the converted output as a string (or object for JSON/AST), or void when writing a file
|
|
42923
|
+
* @throws Error if any error occurs
|
|
42924
|
+
*/
|
|
42925
|
+
convertHtmlTable(input, options) {
|
|
42926
|
+
const opts = Object.assign({}, options);
|
|
42927
|
+
if (env.isBrowser && opts.outputFile) {
|
|
42928
|
+
throw new Error("Cannot write to file in browser environment");
|
|
42929
|
+
}
|
|
42930
|
+
let inStr = input;
|
|
42931
|
+
const inputIsString = typeof input === "string";
|
|
42932
|
+
if (env.isNode && inputIsString && fs.existsSync(inStr)) {
|
|
42933
|
+
inStr = fs.readFileSync(inStr, { encoding: "utf8" });
|
|
42934
|
+
}
|
|
42935
|
+
const isHtmlString = (s) => {
|
|
42936
|
+
const t = s.replace(/^\s+/, "");
|
|
42937
|
+
return t.startsWith("<") && /<table/i.test(t);
|
|
42938
|
+
};
|
|
42939
|
+
let htmlInput;
|
|
42940
|
+
if (opts.inputFormat === InputFormat.html) {
|
|
42941
|
+
htmlInput = true;
|
|
42942
|
+
} else if (opts.inputFormat != null) {
|
|
42943
|
+
htmlInput = false;
|
|
42944
|
+
} else {
|
|
42945
|
+
htmlInput = inputIsString && isHtmlString(inStr);
|
|
42946
|
+
}
|
|
42947
|
+
if (htmlInput) {
|
|
42948
|
+
const parser2 = new HtmlTableParser();
|
|
42949
|
+
const bits = parser2.parse(inStr, {
|
|
42950
|
+
tableFormat: opts.tableFormat ?? "table-extended",
|
|
42951
|
+
keepUnknownTags: opts.keepUnknownTags,
|
|
42952
|
+
onWarning: (m) => console.warn(m)
|
|
42953
|
+
});
|
|
42954
|
+
const outputFormat2 = opts.outputFormat ?? Output.bitmark;
|
|
42955
|
+
if (outputFormat2 === "html") {
|
|
42956
|
+
const json2 = this.convert(bits, { outputFormat: Output.json });
|
|
42957
|
+
return this.outputHtml(new HtmlTableGenerator().generate(json2), opts);
|
|
42958
|
+
}
|
|
42959
|
+
return this.convert(bits, {
|
|
42960
|
+
outputFormat: (0, import_superenum50.Enum)(Output).fromValue(outputFormat2),
|
|
42961
|
+
outputFile: opts.outputFile,
|
|
42962
|
+
fileOptions: opts.fileOptions,
|
|
42963
|
+
jsonOptions: opts.jsonOptions,
|
|
42964
|
+
bitmarkOptions: opts.bitmarkOptions
|
|
42965
|
+
});
|
|
42966
|
+
}
|
|
42967
|
+
const outputFormat = opts.outputFormat ?? "html";
|
|
42968
|
+
if (outputFormat !== "html") {
|
|
42969
|
+
throw new Error(
|
|
42970
|
+
"convertHtmlTable: non-HTML input can only be converted to HTML. Use convert() for bitmark/JSON/AST conversions."
|
|
42971
|
+
);
|
|
42972
|
+
}
|
|
42973
|
+
const json = this.convert(inputIsString ? inStr : input, { outputFormat: Output.json });
|
|
42974
|
+
return this.outputHtml(new HtmlTableGenerator().generate(json), opts);
|
|
42975
|
+
}
|
|
42976
|
+
/**
|
|
42977
|
+
* Write an HTML string to the configured output file, or return it.
|
|
42978
|
+
*/
|
|
42979
|
+
outputHtml(html, opts) {
|
|
42980
|
+
if (opts.outputFile) {
|
|
42981
|
+
const output = opts.outputFile.toString();
|
|
42982
|
+
const flag = opts.fileOptions?.append ? "a" : "w";
|
|
42983
|
+
fs.ensureDirSync(path.dirname(output));
|
|
42984
|
+
fs.writeFileSync(output, html, { flag });
|
|
42985
|
+
return;
|
|
42986
|
+
}
|
|
42987
|
+
return html;
|
|
42988
|
+
}
|
|
40983
42989
|
/**
|
|
40984
42990
|
* Breakscape bitmark text.
|
|
40985
42991
|
*
|
|
@@ -41258,6 +43264,728 @@ var BoundingBoxIndex = class _BoundingBoxIndex {
|
|
|
41258
43264
|
}
|
|
41259
43265
|
};
|
|
41260
43266
|
|
|
43267
|
+
// src/generator/html/HtmlTableGenerator.ts
|
|
43268
|
+
var MARK_TO_TAG = {
|
|
43269
|
+
bold: "b",
|
|
43270
|
+
italic: "i",
|
|
43271
|
+
superscript: "sup",
|
|
43272
|
+
subscript: "sub",
|
|
43273
|
+
strike: "s",
|
|
43274
|
+
del: "del",
|
|
43275
|
+
ins: "ins",
|
|
43276
|
+
underline: "u",
|
|
43277
|
+
highlight: "mark",
|
|
43278
|
+
code: "code"
|
|
43279
|
+
};
|
|
43280
|
+
var HtmlTableGenerator2 = class {
|
|
43281
|
+
constructor() {
|
|
43282
|
+
__publicField(this, "indentUnit", " ");
|
|
43283
|
+
}
|
|
43284
|
+
/**
|
|
43285
|
+
* Generate HTML table fragments from a string or JSON value.
|
|
43286
|
+
* @returns the concatenated `<table>` fragments, blank-line separated (empty string if none).
|
|
43287
|
+
*/
|
|
43288
|
+
generate(input) {
|
|
43289
|
+
let data = input;
|
|
43290
|
+
if (typeof data === "string") {
|
|
43291
|
+
try {
|
|
43292
|
+
data = JSON.parse(data);
|
|
43293
|
+
} catch {
|
|
43294
|
+
return "";
|
|
43295
|
+
}
|
|
43296
|
+
}
|
|
43297
|
+
const bits = this.collectBits(data);
|
|
43298
|
+
const fragments = [];
|
|
43299
|
+
for (const bit of bits) {
|
|
43300
|
+
const html = this.renderTableBit(bit);
|
|
43301
|
+
if (html) fragments.push(html);
|
|
43302
|
+
}
|
|
43303
|
+
return fragments.join("\n\n");
|
|
43304
|
+
}
|
|
43305
|
+
// --- bit collection -------------------------------------------------------
|
|
43306
|
+
collectBits(data) {
|
|
43307
|
+
const out = [];
|
|
43308
|
+
const visit = (value) => {
|
|
43309
|
+
if (value == null || typeof value !== "object") return;
|
|
43310
|
+
if (Array.isArray(value)) {
|
|
43311
|
+
value.forEach(visit);
|
|
43312
|
+
return;
|
|
43313
|
+
}
|
|
43314
|
+
const obj = value;
|
|
43315
|
+
if ("bit" in obj && obj.bit && typeof obj.bit === "object") {
|
|
43316
|
+
out.push(obj.bit);
|
|
43317
|
+
} else if ("type" in obj) {
|
|
43318
|
+
out.push(obj);
|
|
43319
|
+
}
|
|
43320
|
+
};
|
|
43321
|
+
visit(data);
|
|
43322
|
+
return out;
|
|
43323
|
+
}
|
|
43324
|
+
// --- table rendering ------------------------------------------------------
|
|
43325
|
+
renderTableBit(bit) {
|
|
43326
|
+
const table = bit.table;
|
|
43327
|
+
if (!table) return "";
|
|
43328
|
+
if (bit.type !== "table" && bit.type !== "table-extended") return "";
|
|
43329
|
+
const extended = this.toExtended(table);
|
|
43330
|
+
const lines = [];
|
|
43331
|
+
lines.push("<table>");
|
|
43332
|
+
if (bit.caption != null) {
|
|
43333
|
+
const caption = this.cellContentToHtml(this.asTextAst(bit.caption));
|
|
43334
|
+
if (caption) lines.push(`${this.indentUnit}<caption>${caption}</caption>`);
|
|
43335
|
+
}
|
|
43336
|
+
this.renderSection(lines, "thead", extended.header, "header");
|
|
43337
|
+
this.renderSection(lines, "tbody", extended.body, "body");
|
|
43338
|
+
this.renderSection(lines, "tfoot", extended.footer, "footer");
|
|
43339
|
+
lines.push("</table>");
|
|
43340
|
+
return lines.join("\n");
|
|
43341
|
+
}
|
|
43342
|
+
toExtended(table) {
|
|
43343
|
+
const t = table;
|
|
43344
|
+
if (t.header || t.body || t.footer) return table;
|
|
43345
|
+
return convertBasicToExtendedTableFormat(table);
|
|
43346
|
+
}
|
|
43347
|
+
renderSection(lines, tag, section, key) {
|
|
43348
|
+
if (!section || !section.rows || section.rows.length === 0) return;
|
|
43349
|
+
lines.push(`${this.indentUnit}<${tag}>`);
|
|
43350
|
+
for (const row of section.rows) {
|
|
43351
|
+
lines.push(`${this.indentUnit.repeat(2)}<tr>`);
|
|
43352
|
+
for (const cell of row.cells ?? []) {
|
|
43353
|
+
lines.push(`${this.indentUnit.repeat(3)}${this.renderCell(cell, key)}`);
|
|
43354
|
+
}
|
|
43355
|
+
lines.push(`${this.indentUnit.repeat(2)}</tr>`);
|
|
43356
|
+
}
|
|
43357
|
+
lines.push(`${this.indentUnit}</${tag}>`);
|
|
43358
|
+
}
|
|
43359
|
+
renderCell(cell, _key) {
|
|
43360
|
+
const tag = cell.title === true ? "th" : "td";
|
|
43361
|
+
const attrs = [];
|
|
43362
|
+
if (cell.rowspan && cell.rowspan > 1) attrs.push(`rowspan="${cell.rowspan}"`);
|
|
43363
|
+
if (cell.colspan && cell.colspan > 1) attrs.push(`colspan="${cell.colspan}"`);
|
|
43364
|
+
if (cell.scope) attrs.push(`scope="${cell.scope}"`);
|
|
43365
|
+
if (cell.colwidth && cell.colwidth > 0) attrs.push(`width="${cell.colwidth}"`);
|
|
43366
|
+
const open = attrs.length > 0 ? `<${tag} ${attrs.join(" ")}>` : `<${tag}>`;
|
|
43367
|
+
return `${open}${this.cellContentToHtml(this.asTextAst(cell.content))}</${tag}>`;
|
|
43368
|
+
}
|
|
43369
|
+
// --- cell content (TextAst -> HTML) ---------------------------------------
|
|
43370
|
+
asTextAst(content) {
|
|
43371
|
+
if (content == null) return [];
|
|
43372
|
+
if (typeof content === "string") return [{ type: "text", text: content }];
|
|
43373
|
+
return content;
|
|
43374
|
+
}
|
|
43375
|
+
cellContentToHtml(blocks) {
|
|
43376
|
+
if (blocks.length === 0) return "";
|
|
43377
|
+
if (blocks.length === 1 && blocks[0].type === "paragraph") {
|
|
43378
|
+
return this.inlineToHtml(blocks[0].content ?? []);
|
|
43379
|
+
}
|
|
43380
|
+
return blocks.map((b) => this.blockToHtml(b)).join("");
|
|
43381
|
+
}
|
|
43382
|
+
blockToHtml(node) {
|
|
43383
|
+
switch (node.type) {
|
|
43384
|
+
case "paragraph":
|
|
43385
|
+
return `<p>${this.inlineToHtml(node.content ?? [])}</p>`;
|
|
43386
|
+
case "bulletList":
|
|
43387
|
+
case "noBulletList":
|
|
43388
|
+
case "orderedList":
|
|
43389
|
+
case "orderedListRoman":
|
|
43390
|
+
case "orderedListRomanLower":
|
|
43391
|
+
case "letteredList":
|
|
43392
|
+
case "letteredListLower":
|
|
43393
|
+
case "taskList":
|
|
43394
|
+
return this.listToHtml(node);
|
|
43395
|
+
case "image":
|
|
43396
|
+
case "imageInline":
|
|
43397
|
+
return this.imageToHtml(node);
|
|
43398
|
+
default:
|
|
43399
|
+
return this.inlineToHtml([node]);
|
|
43400
|
+
}
|
|
43401
|
+
}
|
|
43402
|
+
inlineToHtml(nodes) {
|
|
43403
|
+
return nodes.map((n) => this.inlineNodeToHtml(n)).join("");
|
|
43404
|
+
}
|
|
43405
|
+
inlineNodeToHtml(node) {
|
|
43406
|
+
if (node.type === "hardBreak") return "<br>";
|
|
43407
|
+
if (node.type === "image" || node.type === "imageInline") return this.imageToHtml(node);
|
|
43408
|
+
if (node.type === "text") {
|
|
43409
|
+
return this.wrapMarks(this.escapeText(node.text ?? ""), node.marks);
|
|
43410
|
+
}
|
|
43411
|
+
if (node.content) return this.inlineToHtml(node.content);
|
|
43412
|
+
return this.escapeText(node.text ?? "");
|
|
43413
|
+
}
|
|
43414
|
+
wrapMarks(text, marks) {
|
|
43415
|
+
if (!marks || marks.length === 0) return text;
|
|
43416
|
+
let open = "";
|
|
43417
|
+
let close = "";
|
|
43418
|
+
for (const mark of marks) {
|
|
43419
|
+
if (mark.type === "link") {
|
|
43420
|
+
const href = mark.attrs?.href ?? "";
|
|
43421
|
+
open += `<a href="${this.escapeAttr(href)}">`;
|
|
43422
|
+
close = `</a>${close}`;
|
|
43423
|
+
} else {
|
|
43424
|
+
const tag = MARK_TO_TAG[mark.type];
|
|
43425
|
+
if (tag) {
|
|
43426
|
+
open += `<${tag}>`;
|
|
43427
|
+
close = `</${tag}>${close}`;
|
|
43428
|
+
}
|
|
43429
|
+
}
|
|
43430
|
+
}
|
|
43431
|
+
return `${open}${text}${close}`;
|
|
43432
|
+
}
|
|
43433
|
+
// --- lists ----------------------------------------------------------------
|
|
43434
|
+
listToHtml(node) {
|
|
43435
|
+
const { tag, attrs } = this.listTag(node);
|
|
43436
|
+
const items = (node.content ?? []).map((item) => this.listItemToHtml(item, node.type === "taskList")).join("");
|
|
43437
|
+
return `<${tag}${attrs}>${items}</${tag}>`;
|
|
43438
|
+
}
|
|
43439
|
+
listTag(node) {
|
|
43440
|
+
const start = node.attrs?.start;
|
|
43441
|
+
const startAttr = typeof start === "number" && start !== 1 ? ` start="${start}"` : "";
|
|
43442
|
+
switch (node.type) {
|
|
43443
|
+
case "orderedList":
|
|
43444
|
+
return { tag: "ol", attrs: startAttr };
|
|
43445
|
+
case "orderedListRoman":
|
|
43446
|
+
return { tag: "ol", attrs: ` type="I"${startAttr}` };
|
|
43447
|
+
case "orderedListRomanLower":
|
|
43448
|
+
return { tag: "ol", attrs: ` type="i"${startAttr}` };
|
|
43449
|
+
case "letteredList":
|
|
43450
|
+
return { tag: "ol", attrs: ` type="A"${startAttr}` };
|
|
43451
|
+
case "letteredListLower":
|
|
43452
|
+
return { tag: "ol", attrs: ` type="a"${startAttr}` };
|
|
43453
|
+
case "noBulletList":
|
|
43454
|
+
return { tag: "ul", attrs: ' style="list-style:none"' };
|
|
43455
|
+
case "taskList":
|
|
43456
|
+
case "bulletList":
|
|
43457
|
+
default:
|
|
43458
|
+
return { tag: "ul", attrs: "" };
|
|
43459
|
+
}
|
|
43460
|
+
}
|
|
43461
|
+
listItemToHtml(item, task) {
|
|
43462
|
+
const inner = (item.content ?? []).map((b) => {
|
|
43463
|
+
if (b.type === "paragraph") return this.inlineToHtml(b.content ?? []);
|
|
43464
|
+
return this.blockToHtml(b);
|
|
43465
|
+
}).join("");
|
|
43466
|
+
if (task) {
|
|
43467
|
+
const checked = item.attrs?.checked ? " checked" : "";
|
|
43468
|
+
return `<li><input type="checkbox"${checked}>${inner}</li>`;
|
|
43469
|
+
}
|
|
43470
|
+
return `<li>${inner}</li>`;
|
|
43471
|
+
}
|
|
43472
|
+
// --- images ---------------------------------------------------------------
|
|
43473
|
+
imageToHtml(node) {
|
|
43474
|
+
const attrs = node.attrs ?? {};
|
|
43475
|
+
const parts = [];
|
|
43476
|
+
const src = attrs.src;
|
|
43477
|
+
parts.push(`src="${this.escapeAttr(typeof src === "string" ? src : "")}"`);
|
|
43478
|
+
if (typeof attrs.alt === "string") parts.push(`alt="${this.escapeAttr(attrs.alt)}"`);
|
|
43479
|
+
if (typeof attrs.title === "string") parts.push(`title="${this.escapeAttr(attrs.title)}"`);
|
|
43480
|
+
if (attrs.width != null) parts.push(`width="${attrs.width}"`);
|
|
43481
|
+
if (attrs.height != null) parts.push(`height="${attrs.height}"`);
|
|
43482
|
+
if (typeof attrs.class === "string" && attrs.class) {
|
|
43483
|
+
parts.push(`class="${this.escapeAttr(attrs.class)}"`);
|
|
43484
|
+
}
|
|
43485
|
+
return `<img ${parts.join(" ")}>`;
|
|
43486
|
+
}
|
|
43487
|
+
// --- escaping -------------------------------------------------------------
|
|
43488
|
+
escapeText(text) {
|
|
43489
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
43490
|
+
}
|
|
43491
|
+
escapeAttr(text) {
|
|
43492
|
+
return this.escapeText(text).replace(/"/g, """);
|
|
43493
|
+
}
|
|
43494
|
+
};
|
|
43495
|
+
|
|
43496
|
+
// src/parser/html/HtmlDom.ts
|
|
43497
|
+
var VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
43498
|
+
"area",
|
|
43499
|
+
"base",
|
|
43500
|
+
"br",
|
|
43501
|
+
"col",
|
|
43502
|
+
"embed",
|
|
43503
|
+
"hr",
|
|
43504
|
+
"img",
|
|
43505
|
+
"input",
|
|
43506
|
+
"link",
|
|
43507
|
+
"meta",
|
|
43508
|
+
"param",
|
|
43509
|
+
"source",
|
|
43510
|
+
"track",
|
|
43511
|
+
"wbr"
|
|
43512
|
+
]);
|
|
43513
|
+
var IMPLIED_CLOSE = {
|
|
43514
|
+
li: /* @__PURE__ */ new Set(["li"]),
|
|
43515
|
+
td: /* @__PURE__ */ new Set(["td", "th"]),
|
|
43516
|
+
th: /* @__PURE__ */ new Set(["td", "th"]),
|
|
43517
|
+
tr: /* @__PURE__ */ new Set(["td", "th", "tr"]),
|
|
43518
|
+
thead: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
|
|
43519
|
+
tbody: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
|
|
43520
|
+
tfoot: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
|
|
43521
|
+
p: /* @__PURE__ */ new Set(["p"]),
|
|
43522
|
+
option: /* @__PURE__ */ new Set(["option"])
|
|
43523
|
+
};
|
|
43524
|
+
var TAG_RE = /<(\/)?([a-zA-Z][a-zA-Z0-9-]*)((?:\s+(?:"[^"]*"|'[^']*'|[^<>"'])*)?)(\/)?>/g;
|
|
43525
|
+
var ATTR_RE = /([a-zA-Z_:][a-zA-Z0-9:._-]*)(?:\s*=\s*("[^"]*"|'[^']*'|[^\s"'>]+))?/g;
|
|
43526
|
+
function parseAttrs(raw) {
|
|
43527
|
+
const attrs = {};
|
|
43528
|
+
if (!raw) return attrs;
|
|
43529
|
+
ATTR_RE.lastIndex = 0;
|
|
43530
|
+
let m;
|
|
43531
|
+
while ((m = ATTR_RE.exec(raw)) !== null) {
|
|
43532
|
+
const name = m[1].toLowerCase();
|
|
43533
|
+
let value = m[2] ?? "";
|
|
43534
|
+
if (value.length >= 2 && (value[0] === '"' || value[0] === "'")) {
|
|
43535
|
+
value = value.slice(1, -1);
|
|
43536
|
+
}
|
|
43537
|
+
attrs[name] = value;
|
|
43538
|
+
}
|
|
43539
|
+
return attrs;
|
|
43540
|
+
}
|
|
43541
|
+
function stripNonContent(html) {
|
|
43542
|
+
return html.replace(/<!--[\s\S]*?-->/g, "").replace(/<!\[CDATA\[[\s\S]*?\]\]>/g, "").replace(/<!doctype[^>]*>/gi, "").replace(/<script\b[\s\S]*?<\/script>/gi, "").replace(/<style\b[\s\S]*?<\/style>/gi, "");
|
|
43543
|
+
}
|
|
43544
|
+
function tokenize(html) {
|
|
43545
|
+
const tokens = [];
|
|
43546
|
+
const clean = stripNonContent(html);
|
|
43547
|
+
TAG_RE.lastIndex = 0;
|
|
43548
|
+
let lastIndex = 0;
|
|
43549
|
+
let m;
|
|
43550
|
+
while ((m = TAG_RE.exec(clean)) !== null) {
|
|
43551
|
+
if (m.index > lastIndex) {
|
|
43552
|
+
tokens.push({ kind: "text", text: clean.slice(lastIndex, m.index) });
|
|
43553
|
+
}
|
|
43554
|
+
lastIndex = TAG_RE.lastIndex;
|
|
43555
|
+
const isClose = m[1] === "/";
|
|
43556
|
+
const name = m[2].toLowerCase();
|
|
43557
|
+
if (isClose) {
|
|
43558
|
+
tokens.push({ kind: "close", name });
|
|
43559
|
+
} else {
|
|
43560
|
+
tokens.push({
|
|
43561
|
+
kind: "open",
|
|
43562
|
+
name,
|
|
43563
|
+
attrs: parseAttrs(m[3]),
|
|
43564
|
+
selfClose: m[4] === "/"
|
|
43565
|
+
});
|
|
43566
|
+
}
|
|
43567
|
+
}
|
|
43568
|
+
if (lastIndex < clean.length) {
|
|
43569
|
+
tokens.push({ kind: "text", text: clean.slice(lastIndex) });
|
|
43570
|
+
}
|
|
43571
|
+
return tokens;
|
|
43572
|
+
}
|
|
43573
|
+
function parseHtml(html) {
|
|
43574
|
+
const root = { type: "element", name: "#root", attrs: {}, children: [] };
|
|
43575
|
+
const stack = [root];
|
|
43576
|
+
const top = () => stack[stack.length - 1];
|
|
43577
|
+
for (const token of tokenize(html)) {
|
|
43578
|
+
if (token.kind === "text") {
|
|
43579
|
+
if (token.text) top().children.push({ type: "text", value: token.text });
|
|
43580
|
+
continue;
|
|
43581
|
+
}
|
|
43582
|
+
if (token.kind === "open") {
|
|
43583
|
+
const name = token.name;
|
|
43584
|
+
const closes = IMPLIED_CLOSE[name];
|
|
43585
|
+
if (closes) {
|
|
43586
|
+
while (stack.length > 1 && closes.has(top().name)) stack.pop();
|
|
43587
|
+
}
|
|
43588
|
+
const el = {
|
|
43589
|
+
type: "element",
|
|
43590
|
+
name,
|
|
43591
|
+
attrs: token.attrs ?? {},
|
|
43592
|
+
children: []
|
|
43593
|
+
};
|
|
43594
|
+
top().children.push(el);
|
|
43595
|
+
if (!token.selfClose && !VOID_ELEMENTS.has(name)) {
|
|
43596
|
+
stack.push(el);
|
|
43597
|
+
}
|
|
43598
|
+
continue;
|
|
43599
|
+
}
|
|
43600
|
+
for (let i = stack.length - 1; i > 0; i--) {
|
|
43601
|
+
if (stack[i].name === token.name) {
|
|
43602
|
+
stack.length = i;
|
|
43603
|
+
break;
|
|
43604
|
+
}
|
|
43605
|
+
}
|
|
43606
|
+
}
|
|
43607
|
+
return root.children;
|
|
43608
|
+
}
|
|
43609
|
+
function childElements(el, names) {
|
|
43610
|
+
return el.children.filter((c) => c.type === "element" && names.has(c.name));
|
|
43611
|
+
}
|
|
43612
|
+
|
|
43613
|
+
// src/parser/html/HtmlTableParser.ts
|
|
43614
|
+
var MARK_TAGS = {
|
|
43615
|
+
b: "bold",
|
|
43616
|
+
strong: "bold",
|
|
43617
|
+
i: "italic",
|
|
43618
|
+
em: "italic",
|
|
43619
|
+
sup: "superscript",
|
|
43620
|
+
sub: "subscript",
|
|
43621
|
+
s: "strike",
|
|
43622
|
+
strike: "strike",
|
|
43623
|
+
del: "del",
|
|
43624
|
+
ins: "ins",
|
|
43625
|
+
u: "underline",
|
|
43626
|
+
mark: "highlight",
|
|
43627
|
+
code: "code"
|
|
43628
|
+
};
|
|
43629
|
+
var SECTION_TAGS = /* @__PURE__ */ new Set(["thead", "tbody", "tfoot"]);
|
|
43630
|
+
var ROW_TAGS = /* @__PURE__ */ new Set(["tr"]);
|
|
43631
|
+
var CELL_TAGS = /* @__PURE__ */ new Set(["td", "th"]);
|
|
43632
|
+
var LI_TAGS = /* @__PURE__ */ new Set(["li"]);
|
|
43633
|
+
var VALID_SCOPES = /* @__PURE__ */ new Set(["row", "col", "rowgroup", "colgroup"]);
|
|
43634
|
+
var NAMED_ENTITIES = {
|
|
43635
|
+
amp: "&",
|
|
43636
|
+
lt: "<",
|
|
43637
|
+
gt: ">",
|
|
43638
|
+
quot: '"',
|
|
43639
|
+
apos: "'",
|
|
43640
|
+
nbsp: "\xA0"
|
|
43641
|
+
};
|
|
43642
|
+
function decodeEntities(text) {
|
|
43643
|
+
return text.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z][a-zA-Z0-9]*);/g, (whole, body) => {
|
|
43644
|
+
if (body[0] === "#") {
|
|
43645
|
+
const code = body[1] === "x" || body[1] === "X" ? Number.parseInt(body.slice(2), 16) : Number.parseInt(body.slice(1), 10);
|
|
43646
|
+
if (Number.isFinite(code) && code >= 0 && code <= 1114111) {
|
|
43647
|
+
try {
|
|
43648
|
+
return String.fromCodePoint(code);
|
|
43649
|
+
} catch {
|
|
43650
|
+
return whole;
|
|
43651
|
+
}
|
|
43652
|
+
}
|
|
43653
|
+
return whole;
|
|
43654
|
+
}
|
|
43655
|
+
return NAMED_ENTITIES[body] ?? whole;
|
|
43656
|
+
});
|
|
43657
|
+
}
|
|
43658
|
+
function intAttr(value) {
|
|
43659
|
+
if (value == null) return void 0;
|
|
43660
|
+
const n = Number.parseInt(value, 10);
|
|
43661
|
+
return Number.isInteger(n) ? n : void 0;
|
|
43662
|
+
}
|
|
43663
|
+
function isElement(node) {
|
|
43664
|
+
return node.type === "element";
|
|
43665
|
+
}
|
|
43666
|
+
var HtmlTableParser2 = class {
|
|
43667
|
+
constructor() {
|
|
43668
|
+
__publicField(this, "keepUnknownTags", false);
|
|
43669
|
+
}
|
|
43670
|
+
/**
|
|
43671
|
+
* Parse an HTML string and return one table bit per top-level `<table>`.
|
|
43672
|
+
*/
|
|
43673
|
+
parse(html, options) {
|
|
43674
|
+
this.keepUnknownTags = options?.keepUnknownTags ?? false;
|
|
43675
|
+
const tableFormat = options?.tableFormat ?? "table-extended";
|
|
43676
|
+
const onWarning = options?.onWarning;
|
|
43677
|
+
const tables = this.collectTopLevelTables(parseHtml(html));
|
|
43678
|
+
const bits = [];
|
|
43679
|
+
for (const table of tables) {
|
|
43680
|
+
const bit = this.tableToBit(table, tableFormat, onWarning);
|
|
43681
|
+
if (bit) bits.push(bit);
|
|
43682
|
+
}
|
|
43683
|
+
return bits;
|
|
43684
|
+
}
|
|
43685
|
+
// --- table extraction -----------------------------------------------------
|
|
43686
|
+
/** Collect every `<table>` element, NOT descending into nested tables. */
|
|
43687
|
+
collectTopLevelTables(nodes) {
|
|
43688
|
+
const tables = [];
|
|
43689
|
+
const visit = (node) => {
|
|
43690
|
+
if (!isElement(node)) return;
|
|
43691
|
+
if (node.name === "table") {
|
|
43692
|
+
tables.push(node);
|
|
43693
|
+
return;
|
|
43694
|
+
}
|
|
43695
|
+
node.children.forEach(visit);
|
|
43696
|
+
};
|
|
43697
|
+
nodes.forEach(visit);
|
|
43698
|
+
return tables;
|
|
43699
|
+
}
|
|
43700
|
+
tableToBit(table, tableFormat, onWarning) {
|
|
43701
|
+
const header = [];
|
|
43702
|
+
const body = [];
|
|
43703
|
+
const footer = [];
|
|
43704
|
+
let caption;
|
|
43705
|
+
for (const child of table.children) {
|
|
43706
|
+
if (!isElement(child)) continue;
|
|
43707
|
+
if (child.name === "caption") {
|
|
43708
|
+
const capContent = this.inlineNodes(child.children, []);
|
|
43709
|
+
const trimmed = this.trimInline(capContent);
|
|
43710
|
+
if (trimmed.length > 0) caption = trimmed;
|
|
43711
|
+
} else if (SECTION_TAGS.has(child.name)) {
|
|
43712
|
+
const target = child.name === "thead" ? header : child.name === "tfoot" ? footer : body;
|
|
43713
|
+
for (const tr of childElements(child, ROW_TAGS)) {
|
|
43714
|
+
target.push(this.rowToCells(tr));
|
|
43715
|
+
}
|
|
43716
|
+
} else if (ROW_TAGS.has(child.name)) {
|
|
43717
|
+
body.push(this.rowToCells(child));
|
|
43718
|
+
}
|
|
43719
|
+
}
|
|
43720
|
+
if (header.length === 0 && body.length === 0 && footer.length === 0) {
|
|
43721
|
+
return void 0;
|
|
43722
|
+
}
|
|
43723
|
+
const tableExtended = {};
|
|
43724
|
+
if (header.length > 0) tableExtended.header = { rows: header };
|
|
43725
|
+
if (body.length > 0) tableExtended.body = { rows: body };
|
|
43726
|
+
if (footer.length > 0) tableExtended.footer = { rows: footer };
|
|
43727
|
+
let tableData = tableExtended;
|
|
43728
|
+
if (tableFormat === "table") {
|
|
43729
|
+
if (this.isLossyAsBasic(tableExtended)) {
|
|
43730
|
+
onWarning?.(
|
|
43731
|
+
"Converting an HTML table to [.table] loses rowspan/colspan/scope and multi-section structure; use --tableFormat table-extended to preserve them."
|
|
43732
|
+
);
|
|
43733
|
+
}
|
|
43734
|
+
tableData = convertExtendedToBasicTableFormat(tableExtended);
|
|
43735
|
+
}
|
|
43736
|
+
const bit = {
|
|
43737
|
+
type: tableFormat,
|
|
43738
|
+
format: "bitmark++",
|
|
43739
|
+
table: tableData
|
|
43740
|
+
};
|
|
43741
|
+
if (caption) bit.caption = caption;
|
|
43742
|
+
return bit;
|
|
43743
|
+
}
|
|
43744
|
+
rowToCells(tr) {
|
|
43745
|
+
const cells = [];
|
|
43746
|
+
for (const cell of childElements(tr, CELL_TAGS)) {
|
|
43747
|
+
cells.push(this.cellToJson(cell));
|
|
43748
|
+
}
|
|
43749
|
+
return { cells };
|
|
43750
|
+
}
|
|
43751
|
+
cellToJson(cell) {
|
|
43752
|
+
const json = {
|
|
43753
|
+
content: this.blockNodes(cell.children)
|
|
43754
|
+
};
|
|
43755
|
+
if (cell.name === "th") json.title = true;
|
|
43756
|
+
const rowspan = intAttr(cell.attrs.rowspan);
|
|
43757
|
+
if (rowspan != null && rowspan >= 2) json.rowspan = rowspan;
|
|
43758
|
+
const colspan = intAttr(cell.attrs.colspan);
|
|
43759
|
+
if (colspan != null && colspan >= 2) json.colspan = colspan;
|
|
43760
|
+
const scope = cell.attrs.scope?.toLowerCase();
|
|
43761
|
+
if (scope && VALID_SCOPES.has(scope)) {
|
|
43762
|
+
json.scope = scope;
|
|
43763
|
+
}
|
|
43764
|
+
const width = this.widthFromCell(cell);
|
|
43765
|
+
if (width != null) json.colwidth = width;
|
|
43766
|
+
return json;
|
|
43767
|
+
}
|
|
43768
|
+
widthFromCell(cell) {
|
|
43769
|
+
const attrWidth = intAttr(cell.attrs.width);
|
|
43770
|
+
if (attrWidth != null && attrWidth > 0) return attrWidth;
|
|
43771
|
+
const style = cell.attrs.style;
|
|
43772
|
+
if (style) {
|
|
43773
|
+
const m = style.match(/width\s*:\s*(\d+)\s*px/i);
|
|
43774
|
+
if (m) {
|
|
43775
|
+
const n = Number.parseInt(m[1], 10);
|
|
43776
|
+
if (Number.isInteger(n) && n > 0) return n;
|
|
43777
|
+
}
|
|
43778
|
+
}
|
|
43779
|
+
return void 0;
|
|
43780
|
+
}
|
|
43781
|
+
isLossyAsBasic(table) {
|
|
43782
|
+
if (table.footer) return true;
|
|
43783
|
+
if (table.header && table.header.rows.length > 1) return true;
|
|
43784
|
+
const sections = [table.header, table.body, table.footer];
|
|
43785
|
+
for (const section of sections) {
|
|
43786
|
+
if (!section) continue;
|
|
43787
|
+
for (const row of section.rows) {
|
|
43788
|
+
for (const cell of row.cells) {
|
|
43789
|
+
if (cell.rowspan || cell.colspan || cell.scope) return true;
|
|
43790
|
+
}
|
|
43791
|
+
}
|
|
43792
|
+
}
|
|
43793
|
+
return false;
|
|
43794
|
+
}
|
|
43795
|
+
// --- content conversion (HTML -> TextAst) ---------------------------------
|
|
43796
|
+
/**
|
|
43797
|
+
* Convert a cell/li's child nodes into block-level TextAst nodes (paragraphs, lists, images).
|
|
43798
|
+
*/
|
|
43799
|
+
blockNodes(nodes) {
|
|
43800
|
+
const blocks = [];
|
|
43801
|
+
let inline = [];
|
|
43802
|
+
const flush = () => {
|
|
43803
|
+
const trimmed = this.trimInline(inline);
|
|
43804
|
+
inline = [];
|
|
43805
|
+
if (trimmed.length === 0) return;
|
|
43806
|
+
if (trimmed.length === 1 && trimmed[0].type === "imageInline") {
|
|
43807
|
+
blocks.push({ ...trimmed[0], type: "image" });
|
|
43808
|
+
} else {
|
|
43809
|
+
blocks.push({ type: "paragraph", content: trimmed, attrs: {} });
|
|
43810
|
+
}
|
|
43811
|
+
};
|
|
43812
|
+
for (const node of nodes) {
|
|
43813
|
+
if (!isElement(node)) {
|
|
43814
|
+
inline.push(...this.inlineText(node.value, []));
|
|
43815
|
+
continue;
|
|
43816
|
+
}
|
|
43817
|
+
const name = node.name;
|
|
43818
|
+
if (name === "p") {
|
|
43819
|
+
flush();
|
|
43820
|
+
const content = this.trimInline(this.inlineNodes(node.children, []));
|
|
43821
|
+
if (content.length > 0) {
|
|
43822
|
+
blocks.push({ type: "paragraph", content, attrs: {} });
|
|
43823
|
+
}
|
|
43824
|
+
} else if (this.isListTag(name)) {
|
|
43825
|
+
flush();
|
|
43826
|
+
const list = this.listNode(node);
|
|
43827
|
+
if (list) blocks.push(list);
|
|
43828
|
+
} else {
|
|
43829
|
+
inline.push(...this.inlineFromElement(node, []));
|
|
43830
|
+
}
|
|
43831
|
+
}
|
|
43832
|
+
flush();
|
|
43833
|
+
return blocks;
|
|
43834
|
+
}
|
|
43835
|
+
/** Convert child nodes to a flat list of inline TextNodes, applying the given marks. */
|
|
43836
|
+
inlineNodes(nodes, marks) {
|
|
43837
|
+
const out = [];
|
|
43838
|
+
for (const node of nodes) {
|
|
43839
|
+
if (!isElement(node)) {
|
|
43840
|
+
out.push(...this.inlineText(node.value, marks));
|
|
43841
|
+
} else {
|
|
43842
|
+
out.push(...this.inlineFromElement(node, marks));
|
|
43843
|
+
}
|
|
43844
|
+
}
|
|
43845
|
+
return out;
|
|
43846
|
+
}
|
|
43847
|
+
inlineFromElement(el, marks) {
|
|
43848
|
+
const name = el.name;
|
|
43849
|
+
if (name === "br") {
|
|
43850
|
+
return [{ type: "hardBreak" }];
|
|
43851
|
+
}
|
|
43852
|
+
if (name === "img") {
|
|
43853
|
+
return [this.imageNode(el, true)];
|
|
43854
|
+
}
|
|
43855
|
+
const markType = MARK_TAGS[name];
|
|
43856
|
+
if (markType) {
|
|
43857
|
+
return this.inlineNodes(el.children, [...marks, { type: markType }]);
|
|
43858
|
+
}
|
|
43859
|
+
if (name === "a") {
|
|
43860
|
+
const linkMark = {
|
|
43861
|
+
type: "link",
|
|
43862
|
+
attrs: {
|
|
43863
|
+
href: el.attrs.href ?? "",
|
|
43864
|
+
...el.attrs.target ? { target: el.attrs.target } : {}
|
|
43865
|
+
}
|
|
43866
|
+
};
|
|
43867
|
+
return this.inlineNodes(el.children, [...marks, linkMark]);
|
|
43868
|
+
}
|
|
43869
|
+
if (this.keepUnknownTags) {
|
|
43870
|
+
const open = this.serializeOpenTag(el);
|
|
43871
|
+
const close = `</${name}>`;
|
|
43872
|
+
return [
|
|
43873
|
+
...this.inlineText(open, marks),
|
|
43874
|
+
...this.inlineNodes(el.children, marks),
|
|
43875
|
+
...this.inlineText(close, marks)
|
|
43876
|
+
];
|
|
43877
|
+
}
|
|
43878
|
+
return this.inlineNodes(el.children, marks);
|
|
43879
|
+
}
|
|
43880
|
+
inlineText(raw, marks) {
|
|
43881
|
+
const text = decodeEntities(raw).replace(/\s+/g, " ");
|
|
43882
|
+
if (text === "") return [];
|
|
43883
|
+
const node = { text, type: "text" };
|
|
43884
|
+
if (marks.length > 0) node.marks = marks.map((m) => ({ ...m }));
|
|
43885
|
+
return [node];
|
|
43886
|
+
}
|
|
43887
|
+
serializeOpenTag(el) {
|
|
43888
|
+
const attrs = Object.entries(el.attrs).map(([k, v]) => v === "" ? ` ${k}` : ` ${k}="${v}"`).join("");
|
|
43889
|
+
return `<${el.name}${attrs}>`;
|
|
43890
|
+
}
|
|
43891
|
+
// --- lists ----------------------------------------------------------------
|
|
43892
|
+
isListTag(name) {
|
|
43893
|
+
return name === "ul" || name === "ol";
|
|
43894
|
+
}
|
|
43895
|
+
listNode(el) {
|
|
43896
|
+
const items = childElements(el, LI_TAGS);
|
|
43897
|
+
if (items.length === 0) return void 0;
|
|
43898
|
+
const isTask = el.name === "ul" && items.some((li) => {
|
|
43899
|
+
const firstEl = li.children.find(isElement);
|
|
43900
|
+
return firstEl?.name === "input" && firstEl.attrs.type?.toLowerCase() === "checkbox";
|
|
43901
|
+
});
|
|
43902
|
+
if (isTask) {
|
|
43903
|
+
return {
|
|
43904
|
+
type: "taskList",
|
|
43905
|
+
content: items.map((li) => this.taskItemNode(li))
|
|
43906
|
+
};
|
|
43907
|
+
}
|
|
43908
|
+
const type = this.listType(el);
|
|
43909
|
+
const node = {
|
|
43910
|
+
type,
|
|
43911
|
+
content: items.map((li) => this.listItemNode(li))
|
|
43912
|
+
};
|
|
43913
|
+
if (el.name === "ol") {
|
|
43914
|
+
const start = intAttr(el.attrs.start);
|
|
43915
|
+
if (start != null) {
|
|
43916
|
+
node.attrs = { start };
|
|
43917
|
+
}
|
|
43918
|
+
}
|
|
43919
|
+
return node;
|
|
43920
|
+
}
|
|
43921
|
+
listType(el) {
|
|
43922
|
+
if (el.name === "ol") {
|
|
43923
|
+
switch (el.attrs.type) {
|
|
43924
|
+
case "i":
|
|
43925
|
+
return "orderedListRomanLower";
|
|
43926
|
+
case "I":
|
|
43927
|
+
return "orderedListRoman";
|
|
43928
|
+
case "a":
|
|
43929
|
+
return "letteredListLower";
|
|
43930
|
+
case "A":
|
|
43931
|
+
return "letteredList";
|
|
43932
|
+
default:
|
|
43933
|
+
return "orderedList";
|
|
43934
|
+
}
|
|
43935
|
+
}
|
|
43936
|
+
const style = el.attrs.style ?? "";
|
|
43937
|
+
if (/list-style(-type)?\s*:\s*none/i.test(style)) return "noBulletList";
|
|
43938
|
+
return "bulletList";
|
|
43939
|
+
}
|
|
43940
|
+
listItemNode(li) {
|
|
43941
|
+
let content = this.blockNodes(li.children);
|
|
43942
|
+
if (content.length === 0) {
|
|
43943
|
+
content = [{ type: "paragraph", content: [], attrs: {} }];
|
|
43944
|
+
}
|
|
43945
|
+
return { type: "listItem", content };
|
|
43946
|
+
}
|
|
43947
|
+
taskItemNode(li) {
|
|
43948
|
+
const checkbox = li.children.find(
|
|
43949
|
+
(c) => isElement(c) && c.name === "input" && c.attrs.type?.toLowerCase() === "checkbox"
|
|
43950
|
+
);
|
|
43951
|
+
const checked = checkbox ? "checked" in checkbox.attrs : false;
|
|
43952
|
+
const rest = li.children.filter((c) => c !== checkbox);
|
|
43953
|
+
let content = this.blockNodes(rest);
|
|
43954
|
+
if (content.length === 0) {
|
|
43955
|
+
content = [{ type: "paragraph", content: [], attrs: {} }];
|
|
43956
|
+
}
|
|
43957
|
+
return { type: "taskItem", attrs: { checked }, content };
|
|
43958
|
+
}
|
|
43959
|
+
// --- images ---------------------------------------------------------------
|
|
43960
|
+
imageNode(el, inline) {
|
|
43961
|
+
const attrs = { src: el.attrs.src ?? "" };
|
|
43962
|
+
attrs.alt = el.attrs.alt ?? null;
|
|
43963
|
+
attrs.title = el.attrs.title ?? null;
|
|
43964
|
+
const width = intAttr(el.attrs.width);
|
|
43965
|
+
if (width != null) attrs.width = width;
|
|
43966
|
+
const height = intAttr(el.attrs.height);
|
|
43967
|
+
if (height != null) attrs.height = height;
|
|
43968
|
+
if (el.attrs.class) attrs.class = el.attrs.class;
|
|
43969
|
+
return { type: inline ? "imageInline" : "image", attrs };
|
|
43970
|
+
}
|
|
43971
|
+
// --- whitespace -----------------------------------------------------------
|
|
43972
|
+
/** Trim leading/trailing whitespace from inline content (edges only). */
|
|
43973
|
+
trimInline(nodes) {
|
|
43974
|
+
const out = nodes.slice();
|
|
43975
|
+
const isWs = (n) => n.type === "text" && /^\s*$/.test(n.text ?? "");
|
|
43976
|
+
while (out.length > 0 && isWs(out[0])) out.shift();
|
|
43977
|
+
if (out.length > 0 && out[0].type === "text") {
|
|
43978
|
+
out[0] = { ...out[0], text: (out[0].text ?? "").replace(/^\s+/, "") };
|
|
43979
|
+
}
|
|
43980
|
+
while (out.length > 0 && isWs(out[out.length - 1])) out.pop();
|
|
43981
|
+
if (out.length > 0 && out[out.length - 1].type === "text") {
|
|
43982
|
+
const last = out.length - 1;
|
|
43983
|
+
out[last] = { ...out[last], text: (out[last].text ?? "").replace(/\s+$/, "") };
|
|
43984
|
+
}
|
|
43985
|
+
return out.filter((n) => !(n.type === "text" && n.text === ""));
|
|
43986
|
+
}
|
|
43987
|
+
};
|
|
43988
|
+
|
|
41261
43989
|
// src/index.ts
|
|
41262
43990
|
init();
|
|
41263
43991
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -41274,6 +44002,8 @@ init();
|
|
|
41274
44002
|
BoundingBoxIndex,
|
|
41275
44003
|
Builder,
|
|
41276
44004
|
CardSetVersion,
|
|
44005
|
+
HtmlTableGenerator,
|
|
44006
|
+
HtmlTableParser,
|
|
41277
44007
|
InfoFormat,
|
|
41278
44008
|
InfoType,
|
|
41279
44009
|
Input,
|