@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
package/dist/index.cjs
CHANGED
|
@@ -46,6 +46,8 @@ __export(index_exports, {
|
|
|
46
46
|
Builder: () => Builder,
|
|
47
47
|
CardSetVersion: () => CardSetVersion,
|
|
48
48
|
FileWriter: () => FileWriter,
|
|
49
|
+
HtmlTableGenerator: () => HtmlTableGenerator,
|
|
50
|
+
HtmlTableParser: () => HtmlTableParser,
|
|
49
51
|
InfoFormat: () => InfoFormat,
|
|
50
52
|
InfoType: () => InfoType,
|
|
51
53
|
Input: () => Input,
|
|
@@ -293,6 +295,7 @@ var BitType = {
|
|
|
293
295
|
dangerAlt: "danger-alt",
|
|
294
296
|
dangerCollapsible: "danger-collapsible",
|
|
295
297
|
definitionList: "definition-list",
|
|
298
|
+
definitionListCollapsible: "definition-list-collapsible",
|
|
296
299
|
definitionTerm: "definition-term",
|
|
297
300
|
deleted: "deleted",
|
|
298
301
|
details: "details",
|
|
@@ -573,6 +576,8 @@ var BitType = {
|
|
|
573
576
|
smartStandardArticleNonNormativeCollapsible: "smart-standard-article-non-normative-collapsible",
|
|
574
577
|
smartStandardArticleNormative: "smart-standard-article-normative",
|
|
575
578
|
smartStandardArticleNormativeCollapsible: "smart-standard-article-normative-collapsible",
|
|
579
|
+
smartStandardDefinitionListNonNormative: "smart-standard-definition-list-non-normative",
|
|
580
|
+
smartStandardDefinitionListNormative: "smart-standard-definition-list-normative",
|
|
576
581
|
smartStandardExampleNonNormative: "smart-standard-example-non-normative",
|
|
577
582
|
smartStandardExampleNonNormativeCollapsible: "smart-standard-example-non-normative-collapsible",
|
|
578
583
|
smartStandardExampleNormative: "smart-standard-example-normative",
|
|
@@ -623,6 +628,8 @@ var BitType = {
|
|
|
623
628
|
smartStandardTableNormativeCollapsible: "smart-standard-table-normative-collapsible",
|
|
624
629
|
standardArticleNonNormative: "standard-article-non-normative",
|
|
625
630
|
standardArticleNormative: "standard-article-normative",
|
|
631
|
+
standardDefinitionListNonNormative: "standard-definition-list-non-normative",
|
|
632
|
+
standardDefinitionListNormative: "standard-definition-list-normative",
|
|
626
633
|
standardExampleNonNormative: "standard-example-non-normative",
|
|
627
634
|
standardExampleNormative: "standard-example-normative",
|
|
628
635
|
standardImageFigureNonNormative: "standard-image-figure-non-normative",
|
|
@@ -792,10 +799,13 @@ var TextFormat = {
|
|
|
792
799
|
var import_superenum7 = require("@ncoderz/superenum");
|
|
793
800
|
var CardSetConfigKey = {
|
|
794
801
|
flashcard: "flashcard",
|
|
802
|
+
flashcard1: "flashcard1",
|
|
795
803
|
definitionList: "definitionList",
|
|
804
|
+
definitionListPlain: "definitionListPlain",
|
|
796
805
|
elements: "elements",
|
|
797
806
|
statements: "statements",
|
|
798
807
|
quiz: "quiz",
|
|
808
|
+
quizResponses: "quizResponses",
|
|
799
809
|
feedback: "feedback",
|
|
800
810
|
questions: "questions",
|
|
801
811
|
matchPairs: "matchPairs",
|
|
@@ -807,6 +817,7 @@ var CardSetConfigKey = {
|
|
|
807
817
|
pronunciationTable: "pronunciationTable",
|
|
808
818
|
botActionResponses: "botActionResponses",
|
|
809
819
|
exampleBitList: "exampleBitList",
|
|
820
|
+
pageFooterSections: "pageFooterSections",
|
|
810
821
|
clozeList: "clozeList",
|
|
811
822
|
ingredients: "ingredients",
|
|
812
823
|
// DEPRECATED - TO BE REMOVED IN THE FUTURE
|
|
@@ -1223,12 +1234,17 @@ var groupKeys = {
|
|
|
1223
1234
|
group_standardAllBits: "group_standardAllBits",
|
|
1224
1235
|
group_standardItemLead: "group_standardItemLead",
|
|
1225
1236
|
group_standardItemLeadInstructionHint: "group_standardItemLeadInstructionHint",
|
|
1237
|
+
group_standardInstructionHint: "group_standardInstructionHint",
|
|
1226
1238
|
group_standardTags: "group_standardTags",
|
|
1239
|
+
group_standardTagsNoItemLead: "group_standardTagsNoItemLead",
|
|
1227
1240
|
group_imageSource: "group_imageSource",
|
|
1228
1241
|
group_technicalTerm: "group_technicalTerm",
|
|
1229
1242
|
group_person: "group_person",
|
|
1230
1243
|
group_gap: "group_gap",
|
|
1231
1244
|
group_trueFalse: "group_trueFalse",
|
|
1245
|
+
group_trueFalseInlineHighlight: "group_trueFalseInlineHighlight",
|
|
1246
|
+
group_trueFalseInlineSelect: "group_trueFalseInlineSelect",
|
|
1247
|
+
group_trueFalseResponses: "group_trueFalseResponses",
|
|
1232
1248
|
group_markConfig: "group_markConfig",
|
|
1233
1249
|
group_mark: "group_mark",
|
|
1234
1250
|
group_bookCommon: "group_bookCommon",
|
|
@@ -1236,14 +1252,18 @@ var groupKeys = {
|
|
|
1236
1252
|
group_advertisingCommon: "group_advertisingCommon",
|
|
1237
1253
|
group_platformStylesCommon: "group_platformStylesCommon",
|
|
1238
1254
|
group_quizCommon: "group_quizCommon",
|
|
1255
|
+
group_backgroundWallpaper: "group_backgroundWallpaper",
|
|
1256
|
+
group_imageNoZoom: "group_imageNoZoom",
|
|
1239
1257
|
group_resourceBitTags: "group_resourceBitTags",
|
|
1240
1258
|
group_resourceCommon: "group_resourceCommon",
|
|
1241
1259
|
group_resourceImageCommon: "group_resourceImageCommon",
|
|
1260
|
+
group_resourceImageCommonNoZoom: "group_resourceImageCommonNoZoom",
|
|
1242
1261
|
group_resourceAudioCommon: "group_resourceAudioCommon",
|
|
1243
1262
|
group_resourceVideoCommon: "group_resourceVideoCommon",
|
|
1244
1263
|
group_resourceIcon: "group_resourceIcon",
|
|
1245
1264
|
// Alias for image
|
|
1246
1265
|
group_resourceImage: "group_resourceImage",
|
|
1266
|
+
group_resourceImageNoZoom: "group_resourceImageNoZoom",
|
|
1247
1267
|
group_resourceImageEmbed: "group_resourceImageEmbed",
|
|
1248
1268
|
group_resourceImageLink: "group_resourceImageLink",
|
|
1249
1269
|
group_resourceImageResponsive: "group_resourceImageResponsive",
|
|
@@ -1395,6 +1415,7 @@ var propertyKeys = {
|
|
|
1395
1415
|
property_enforceUpdateOverUserInput: "@enforceUpdateOverUserInput",
|
|
1396
1416
|
property_extractorExtractionTimestamp: "@extractorExtractionTimestamp",
|
|
1397
1417
|
property_isCaseSensitive: "@isCaseSensitive",
|
|
1418
|
+
property_isCollapsible: "@isCollapsible",
|
|
1398
1419
|
property_isInfoOnly: "@isInfoOnly",
|
|
1399
1420
|
property_isPublic: "@isPublic",
|
|
1400
1421
|
property_isTemplate: "@isTemplate",
|
|
@@ -1520,6 +1541,7 @@ var propertyKeys = {
|
|
|
1520
1541
|
property_src4x: "@src4x",
|
|
1521
1542
|
property_srcAlt: "@srcAlt",
|
|
1522
1543
|
property_stripePricingTableId: "@stripePricingTableId",
|
|
1544
|
+
property_stripePricingTableDescriptiveName: "@stripePricingTableDescriptiveName",
|
|
1523
1545
|
property_stripePublishableKey: "@stripePublishableKey",
|
|
1524
1546
|
property_subject: "@subject",
|
|
1525
1547
|
property_subtype: "@subtype",
|
|
@@ -2271,7 +2293,7 @@ var CARDSETS = {
|
|
|
2271
2293
|
variants: [
|
|
2272
2294
|
{
|
|
2273
2295
|
jsonKey: "question.text",
|
|
2274
|
-
exportJsonKey: { question: { text: "$" } },
|
|
2296
|
+
exportJsonKey: [{ "@absent": { question: {} } }, { question: { text: "$" } }],
|
|
2275
2297
|
tags: [
|
|
2276
2298
|
{
|
|
2277
2299
|
key: ConfigKey.group_standardTags,
|
|
@@ -2287,6 +2309,136 @@ var CARDSETS = {
|
|
|
2287
2309
|
description: "Icon resource for the flashcard.",
|
|
2288
2310
|
jsonKey: "question.icon|resource(type=image, key=image)",
|
|
2289
2311
|
exportJsonKey: { question: { icon: { type: "image", image: { src: "$" } } } }
|
|
2312
|
+
},
|
|
2313
|
+
{
|
|
2314
|
+
key: ConfigKey.property_example,
|
|
2315
|
+
exportJsonKey: [
|
|
2316
|
+
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2317
|
+
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2318
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2319
|
+
],
|
|
2320
|
+
description: "Example marker for the flashcard question.",
|
|
2321
|
+
format: TagFormat.bitmarkText,
|
|
2322
|
+
nullable: true
|
|
2323
|
+
}
|
|
2324
|
+
]
|
|
2325
|
+
}
|
|
2326
|
+
]
|
|
2327
|
+
},
|
|
2328
|
+
{
|
|
2329
|
+
name: "answer",
|
|
2330
|
+
variants: [
|
|
2331
|
+
{
|
|
2332
|
+
jsonKey: "answer.text",
|
|
2333
|
+
exportJsonKey: [{ "@absent": { answer: {} } }, { answer: { text: "$" } }],
|
|
2334
|
+
tags: [
|
|
2335
|
+
{
|
|
2336
|
+
key: ConfigKey.group_standardTags,
|
|
2337
|
+
description: "Standard tags for the flashcard."
|
|
2338
|
+
},
|
|
2339
|
+
{
|
|
2340
|
+
exportJsonKey: { title: "$" },
|
|
2341
|
+
key: ConfigKey.tag_title,
|
|
2342
|
+
description: "Title of the flashcard."
|
|
2343
|
+
},
|
|
2344
|
+
{
|
|
2345
|
+
key: ConfigKey.group_resourceIcon,
|
|
2346
|
+
description: "Icon resource for the flashcard.",
|
|
2347
|
+
jsonKey: "answer.icon|resource(type=image, key=image)",
|
|
2348
|
+
exportJsonKey: { answer: { icon: { type: "image", image: { src: "$" } } } }
|
|
2349
|
+
},
|
|
2350
|
+
{
|
|
2351
|
+
key: ConfigKey.property_example,
|
|
2352
|
+
exportJsonKey: [
|
|
2353
|
+
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2354
|
+
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2355
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2356
|
+
],
|
|
2357
|
+
description: "Example marker for the flashcard answer.",
|
|
2358
|
+
format: TagFormat.bitmarkText,
|
|
2359
|
+
nullable: true
|
|
2360
|
+
}
|
|
2361
|
+
]
|
|
2362
|
+
},
|
|
2363
|
+
{
|
|
2364
|
+
jsonKey: "alternativeAnswers[].text",
|
|
2365
|
+
exportJsonKey: { alternativeAnswers: [{ text: "$" }] },
|
|
2366
|
+
tags: [
|
|
2367
|
+
{
|
|
2368
|
+
key: ConfigKey.group_standardTags,
|
|
2369
|
+
description: "Standard tags for the flashcard."
|
|
2370
|
+
},
|
|
2371
|
+
{
|
|
2372
|
+
exportJsonKey: { title: "$" },
|
|
2373
|
+
key: ConfigKey.tag_title,
|
|
2374
|
+
description: "Title of the flashcard."
|
|
2375
|
+
},
|
|
2376
|
+
{
|
|
2377
|
+
key: ConfigKey.group_resourceIcon,
|
|
2378
|
+
description: "Icon resource for the flashcard.",
|
|
2379
|
+
jsonKey: "alternativeAnswers[].icon|resource(type=image, key=image)",
|
|
2380
|
+
exportJsonKey: {
|
|
2381
|
+
alternativeAnswers: [{ icon: { type: "image", image: { src: "$" } } }]
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
],
|
|
2385
|
+
repeatCount: Count.infinity
|
|
2386
|
+
}
|
|
2387
|
+
]
|
|
2388
|
+
}
|
|
2389
|
+
]
|
|
2390
|
+
},
|
|
2391
|
+
//
|
|
2392
|
+
// flashcard1 — single-card variant of flashcard. Same shape, but capped
|
|
2393
|
+
// at one card via `sections.default.maxCount = 1` (PLAN-085). Used by
|
|
2394
|
+
// the `flashcard-1` bit type; the multi-card `flashcard` and
|
|
2395
|
+
// `q-and-a-card` bit types continue to use the unbounded `flashcard`
|
|
2396
|
+
// cardset.
|
|
2397
|
+
//
|
|
2398
|
+
[CardSetConfigKey.flashcard1]: {
|
|
2399
|
+
jsonKey: "cards",
|
|
2400
|
+
exportJsonKey: { cards: "$" },
|
|
2401
|
+
sections: {
|
|
2402
|
+
default: {
|
|
2403
|
+
jsonKey: "cards",
|
|
2404
|
+
exportJsonKey: { cards: "$" },
|
|
2405
|
+
isDefault: true,
|
|
2406
|
+
maxCount: 1
|
|
2407
|
+
}
|
|
2408
|
+
},
|
|
2409
|
+
sides: [
|
|
2410
|
+
{
|
|
2411
|
+
name: "question",
|
|
2412
|
+
variants: [
|
|
2413
|
+
{
|
|
2414
|
+
jsonKey: "question.text",
|
|
2415
|
+
exportJsonKey: [{ "@absent": { question: {} } }, { question: { text: "$" } }],
|
|
2416
|
+
tags: [
|
|
2417
|
+
{
|
|
2418
|
+
key: ConfigKey.group_standardTags,
|
|
2419
|
+
description: "Standard tags for the flashcard."
|
|
2420
|
+
},
|
|
2421
|
+
{
|
|
2422
|
+
exportJsonKey: { title: "$" },
|
|
2423
|
+
key: ConfigKey.tag_title,
|
|
2424
|
+
description: "Title of the flashcard."
|
|
2425
|
+
},
|
|
2426
|
+
{
|
|
2427
|
+
key: ConfigKey.group_resourceIcon,
|
|
2428
|
+
description: "Icon resource for the flashcard.",
|
|
2429
|
+
jsonKey: "question.icon|resource(type=image, key=image)",
|
|
2430
|
+
exportJsonKey: { question: { icon: { type: "image", image: { src: "$" } } } }
|
|
2431
|
+
},
|
|
2432
|
+
{
|
|
2433
|
+
key: ConfigKey.property_example,
|
|
2434
|
+
exportJsonKey: [
|
|
2435
|
+
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2436
|
+
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2437
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2438
|
+
],
|
|
2439
|
+
description: "Example marker for the flashcard question.",
|
|
2440
|
+
format: TagFormat.bitmarkText,
|
|
2441
|
+
nullable: true
|
|
2290
2442
|
}
|
|
2291
2443
|
]
|
|
2292
2444
|
}
|
|
@@ -2297,7 +2449,7 @@ var CARDSETS = {
|
|
|
2297
2449
|
variants: [
|
|
2298
2450
|
{
|
|
2299
2451
|
jsonKey: "answer.text",
|
|
2300
|
-
exportJsonKey: { answer: { text: "$" } },
|
|
2452
|
+
exportJsonKey: [{ "@absent": { answer: {} } }, { answer: { text: "$" } }],
|
|
2301
2453
|
tags: [
|
|
2302
2454
|
{
|
|
2303
2455
|
key: ConfigKey.group_standardTags,
|
|
@@ -2313,6 +2465,17 @@ var CARDSETS = {
|
|
|
2313
2465
|
description: "Icon resource for the flashcard.",
|
|
2314
2466
|
jsonKey: "answer.icon|resource(type=image, key=image)",
|
|
2315
2467
|
exportJsonKey: { answer: { icon: { type: "image", image: { src: "$" } } } }
|
|
2468
|
+
},
|
|
2469
|
+
{
|
|
2470
|
+
key: ConfigKey.property_example,
|
|
2471
|
+
exportJsonKey: [
|
|
2472
|
+
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2473
|
+
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2474
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2475
|
+
],
|
|
2476
|
+
description: "Example marker for the flashcard answer.",
|
|
2477
|
+
format: TagFormat.bitmarkText,
|
|
2478
|
+
nullable: true
|
|
2316
2479
|
}
|
|
2317
2480
|
]
|
|
2318
2481
|
},
|
|
@@ -2356,7 +2519,7 @@ var CARDSETS = {
|
|
|
2356
2519
|
variants: [
|
|
2357
2520
|
{
|
|
2358
2521
|
jsonKey: "term.text",
|
|
2359
|
-
exportJsonKey: { term: { text: "$" } },
|
|
2522
|
+
exportJsonKey: [{ "@absent": { term: {} } }, { term: { text: "$" } }],
|
|
2360
2523
|
tags: [
|
|
2361
2524
|
{
|
|
2362
2525
|
key: ConfigKey.group_standardTags,
|
|
@@ -2375,6 +2538,17 @@ var CARDSETS = {
|
|
|
2375
2538
|
format: TagFormat.plainText,
|
|
2376
2539
|
jsonKey: "term.icon|resource(type=image, key=image)",
|
|
2377
2540
|
exportJsonKey: { term: { icon: { type: "image", image: { src: "$" } } } }
|
|
2541
|
+
},
|
|
2542
|
+
{
|
|
2543
|
+
key: ConfigKey.property_example,
|
|
2544
|
+
exportJsonKey: [
|
|
2545
|
+
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2546
|
+
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2547
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2548
|
+
],
|
|
2549
|
+
description: "Example marker for the definition term.",
|
|
2550
|
+
format: TagFormat.bitmarkText,
|
|
2551
|
+
nullable: true
|
|
2378
2552
|
}
|
|
2379
2553
|
]
|
|
2380
2554
|
}
|
|
@@ -2385,7 +2559,7 @@ var CARDSETS = {
|
|
|
2385
2559
|
variants: [
|
|
2386
2560
|
{
|
|
2387
2561
|
jsonKey: "definition.text",
|
|
2388
|
-
exportJsonKey: { definition: { text: "$" } },
|
|
2562
|
+
exportJsonKey: [{ "@absent": { definition: {} } }, { definition: { text: "$" } }],
|
|
2389
2563
|
tags: [
|
|
2390
2564
|
{
|
|
2391
2565
|
key: ConfigKey.group_standardTags,
|
|
@@ -2403,6 +2577,17 @@ var CARDSETS = {
|
|
|
2403
2577
|
description: "Icon resource for the definition.",
|
|
2404
2578
|
jsonKey: "definition.icon|resource(type=image, key=image)",
|
|
2405
2579
|
exportJsonKey: { definition: { icon: { type: "image", image: { src: "$" } } } }
|
|
2580
|
+
},
|
|
2581
|
+
{
|
|
2582
|
+
key: ConfigKey.property_example,
|
|
2583
|
+
exportJsonKey: [
|
|
2584
|
+
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2585
|
+
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2586
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2587
|
+
],
|
|
2588
|
+
description: "Example marker for the definition.",
|
|
2589
|
+
format: TagFormat.bitmarkText,
|
|
2590
|
+
nullable: true
|
|
2406
2591
|
}
|
|
2407
2592
|
]
|
|
2408
2593
|
},
|
|
@@ -2435,6 +2620,126 @@ var CARDSETS = {
|
|
|
2435
2620
|
]
|
|
2436
2621
|
},
|
|
2437
2622
|
//
|
|
2623
|
+
// definition-list-plain
|
|
2624
|
+
//
|
|
2625
|
+
// Same shape as `definitionList`, but every variant body is rendered as
|
|
2626
|
+
// plain text (string), not bitmark+ AST. Used by
|
|
2627
|
+
// `meta-search-default-terms` / `meta-search-default-topics`, which need
|
|
2628
|
+
// term/definition text emitted verbatim instead of as a ProseMirror tree.
|
|
2629
|
+
//
|
|
2630
|
+
[CardSetConfigKey.definitionListPlain]: {
|
|
2631
|
+
jsonKey: "definitions",
|
|
2632
|
+
exportJsonKey: { definitions: "$" },
|
|
2633
|
+
sides: [
|
|
2634
|
+
{
|
|
2635
|
+
name: "term",
|
|
2636
|
+
variants: [
|
|
2637
|
+
{
|
|
2638
|
+
jsonKey: "term.text",
|
|
2639
|
+
exportJsonKey: [{ "@absent": { term: {} } }, { term: { text: "$" } }],
|
|
2640
|
+
format: TextFormat.plainText,
|
|
2641
|
+
tags: [
|
|
2642
|
+
{
|
|
2643
|
+
key: ConfigKey.group_standardTags,
|
|
2644
|
+
description: "Standard tags for the definition."
|
|
2645
|
+
},
|
|
2646
|
+
{
|
|
2647
|
+
key: ConfigKey.tag_title,
|
|
2648
|
+
description: "Title of the definition.",
|
|
2649
|
+
format: TagFormat.plainText,
|
|
2650
|
+
jsonKey: "^heading.forKeys",
|
|
2651
|
+
exportJsonKey: { "@bit": { heading: { forKeys: "$" } } }
|
|
2652
|
+
},
|
|
2653
|
+
{
|
|
2654
|
+
key: ConfigKey.group_resourceIcon,
|
|
2655
|
+
description: "Icon resource for the definition.",
|
|
2656
|
+
format: TagFormat.plainText,
|
|
2657
|
+
jsonKey: "term.icon|resource(type=image, key=image)",
|
|
2658
|
+
exportJsonKey: { term: { icon: { type: "image", image: { src: "$" } } } }
|
|
2659
|
+
},
|
|
2660
|
+
{
|
|
2661
|
+
key: ConfigKey.property_example,
|
|
2662
|
+
exportJsonKey: [
|
|
2663
|
+
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2664
|
+
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2665
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2666
|
+
],
|
|
2667
|
+
description: "Example marker for the definition term.",
|
|
2668
|
+
format: TagFormat.bitmarkText,
|
|
2669
|
+
nullable: true
|
|
2670
|
+
}
|
|
2671
|
+
]
|
|
2672
|
+
}
|
|
2673
|
+
]
|
|
2674
|
+
},
|
|
2675
|
+
{
|
|
2676
|
+
name: "definition",
|
|
2677
|
+
variants: [
|
|
2678
|
+
{
|
|
2679
|
+
jsonKey: "definition.text",
|
|
2680
|
+
exportJsonKey: [{ "@absent": { definition: {} } }, { definition: { text: "$" } }],
|
|
2681
|
+
format: TextFormat.plainText,
|
|
2682
|
+
tags: [
|
|
2683
|
+
{
|
|
2684
|
+
key: ConfigKey.group_standardTags,
|
|
2685
|
+
description: "Standard tags for the definition."
|
|
2686
|
+
},
|
|
2687
|
+
{
|
|
2688
|
+
key: ConfigKey.tag_title,
|
|
2689
|
+
description: "Title of the definition.",
|
|
2690
|
+
format: TagFormat.plainText,
|
|
2691
|
+
jsonKey: "^heading.forValues",
|
|
2692
|
+
exportJsonKey: { "@bit": { heading: { forValues: "$" } } }
|
|
2693
|
+
},
|
|
2694
|
+
{
|
|
2695
|
+
key: ConfigKey.group_resourceIcon,
|
|
2696
|
+
description: "Icon resource for the definition.",
|
|
2697
|
+
jsonKey: "definition.icon|resource(type=image, key=image)",
|
|
2698
|
+
exportJsonKey: { definition: { icon: { type: "image", image: { src: "$" } } } }
|
|
2699
|
+
},
|
|
2700
|
+
{
|
|
2701
|
+
key: ConfigKey.property_example,
|
|
2702
|
+
exportJsonKey: [
|
|
2703
|
+
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2704
|
+
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2705
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2706
|
+
],
|
|
2707
|
+
description: "Example marker for the definition.",
|
|
2708
|
+
format: TagFormat.bitmarkText,
|
|
2709
|
+
nullable: true
|
|
2710
|
+
}
|
|
2711
|
+
]
|
|
2712
|
+
},
|
|
2713
|
+
{
|
|
2714
|
+
jsonKey: "alternativeDefinitions[].text",
|
|
2715
|
+
exportJsonKey: { alternativeDefinitions: [{ text: "$" }] },
|
|
2716
|
+
format: TextFormat.plainText,
|
|
2717
|
+
tags: [
|
|
2718
|
+
{
|
|
2719
|
+
key: ConfigKey.group_standardTags,
|
|
2720
|
+
description: "Standard tags for the definition."
|
|
2721
|
+
},
|
|
2722
|
+
{
|
|
2723
|
+
exportJsonKey: { title: "$" },
|
|
2724
|
+
key: ConfigKey.tag_title,
|
|
2725
|
+
description: "Title of the definition."
|
|
2726
|
+
},
|
|
2727
|
+
{
|
|
2728
|
+
key: ConfigKey.group_resourceIcon,
|
|
2729
|
+
description: "Icon resource for the definition.",
|
|
2730
|
+
jsonKey: "alternativeDefinitions[].icon|resource(type=image, key=image)",
|
|
2731
|
+
exportJsonKey: {
|
|
2732
|
+
alternativeDefinitions: [{ icon: { type: "image", image: { src: "$" } } }]
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
],
|
|
2736
|
+
repeatCount: Count.infinity
|
|
2737
|
+
}
|
|
2738
|
+
]
|
|
2739
|
+
}
|
|
2740
|
+
]
|
|
2741
|
+
},
|
|
2742
|
+
//
|
|
2438
2743
|
// match-pairs
|
|
2439
2744
|
//
|
|
2440
2745
|
[CardSetConfigKey.matchPairs]: {
|
|
@@ -2447,11 +2752,51 @@ var CARDSETS = {
|
|
|
2447
2752
|
{
|
|
2448
2753
|
jsonKey: "key",
|
|
2449
2754
|
exportJsonKey: { key: "$" },
|
|
2755
|
+
format: TextFormat.plainText,
|
|
2450
2756
|
tags: [
|
|
2451
2757
|
{
|
|
2452
2758
|
key: ConfigKey.group_standardTags,
|
|
2453
2759
|
description: "Standard tags for the match pair."
|
|
2454
2760
|
},
|
|
2761
|
+
{
|
|
2762
|
+
// Pair-level example: emit `isExample` alongside `example`
|
|
2763
|
+
// (matches OLD parser's per-pair allow-list behaviour).
|
|
2764
|
+
//
|
|
2765
|
+
// PLAN-074: a valued cascade source (`[@example:V]` anywhere
|
|
2766
|
+
// in the cardset) fills each pair's `example` with `V`
|
|
2767
|
+
// once per pair from the keys-side cascade fire (single
|
|
2768
|
+
// parsed_variant per pair — no per-value duplication).
|
|
2769
|
+
//
|
|
2770
|
+
// PLAN-076 NOTE: matchPairs (TAG 415) KEEPS `example: '$'`
|
|
2771
|
+
// at the pair-object level. BPG `parseMatchPairs`
|
|
2772
|
+
// (`CardContentProcessor.ts:714-723`) emits ONE example
|
|
2773
|
+
// per PAIR (the `exampleCard` accumulator), placed at the
|
|
2774
|
+
// pair object. Unlike matchMatrix (per-cell example),
|
|
2775
|
+
// matchPairs has no nested per-cell layer, so the pair-key
|
|
2776
|
+
// `[@example:V]` must double-emit as the visible field.
|
|
2777
|
+
// Confirmed via `chaining` fixture: `match-solution-grouped`
|
|
2778
|
+
// expects `example: "packen"` at pair-object level from
|
|
2779
|
+
// `die Koffer[%1][@example:packen]`. Original PLAN-076
|
|
2780
|
+
// draft applied to both keys-side tags; the matchPairs
|
|
2781
|
+
// half was reverted after fixture parity testing.
|
|
2782
|
+
key: ConfigKey.property_example,
|
|
2783
|
+
exportJsonKey: [
|
|
2784
|
+
{ "@keyonly": { isExample: true, "@bit": { isExample: true } } },
|
|
2785
|
+
{
|
|
2786
|
+
predicates: ["@absent", { $cascade: "*" }],
|
|
2787
|
+
rule: {
|
|
2788
|
+
isExample: true,
|
|
2789
|
+
example: "$cascade",
|
|
2790
|
+
"@bit": { isExample: true }
|
|
2791
|
+
}
|
|
2792
|
+
},
|
|
2793
|
+
{ "@absent": { isExample: true } },
|
|
2794
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2795
|
+
],
|
|
2796
|
+
description: "Example marker / value on the match pair.",
|
|
2797
|
+
format: TagFormat.bitmarkText,
|
|
2798
|
+
nullable: true
|
|
2799
|
+
},
|
|
2455
2800
|
{
|
|
2456
2801
|
key: ConfigKey.tag_title,
|
|
2457
2802
|
description: "Title of the match pair.",
|
|
@@ -2460,9 +2805,19 @@ var CARDSETS = {
|
|
|
2460
2805
|
exportJsonKey: { "@bit": { heading: { forKeys: "$" } } }
|
|
2461
2806
|
},
|
|
2462
2807
|
{
|
|
2808
|
+
// PLAN-084: gate the key-side `@absent → $ancestor` cascade
|
|
2809
|
+
// synth with `@variantLacksTag=isCaseSensitive` so the rule
|
|
2810
|
+
// only fires when NO variant in the active card explicitly
|
|
2811
|
+
// carries `[@isCaseSensitive]`. When a value-side variant
|
|
2812
|
+
// carries the tag (e.g. `[@isCaseSensitive:false]`), the
|
|
2813
|
+
// synth is skipped here; the value-side explicit fire owns
|
|
2814
|
+
// the pair-level emission.
|
|
2463
2815
|
key: ConfigKey.property_isCaseSensitive,
|
|
2464
2816
|
exportJsonKey: [
|
|
2465
|
-
{
|
|
2817
|
+
{
|
|
2818
|
+
predicates: ["@absent", { "@variantLacksTag": "@isCaseSensitive" }],
|
|
2819
|
+
rule: { isCaseSensitive: "$ancestor" }
|
|
2820
|
+
},
|
|
2466
2821
|
{ isCaseSensitive: "$" }
|
|
2467
2822
|
],
|
|
2468
2823
|
description: "Property to indicate if the match is case sensitive.",
|
|
@@ -2491,10 +2846,85 @@ var CARDSETS = {
|
|
|
2491
2846
|
{
|
|
2492
2847
|
jsonKey: "values[]",
|
|
2493
2848
|
exportJsonKey: { values: ["$"] },
|
|
2849
|
+
format: TextFormat.plainText,
|
|
2494
2850
|
tags: [
|
|
2495
2851
|
{
|
|
2496
|
-
|
|
2497
|
-
|
|
2852
|
+
// PLAN-084: value-side excludes item/lead/pageNumber/marginNumber.
|
|
2853
|
+
// BPG `parseMatchPairs` does `delete tags.item; delete tags.lead;`
|
|
2854
|
+
// for sideIdx > 0; the schema-side equivalent is to use a
|
|
2855
|
+
// group_standardTags variant that omits group_standardItemLead.
|
|
2856
|
+
key: ConfigKey.group_standardTagsNoItemLead,
|
|
2857
|
+
description: "Standard tags for the match pair (values side, no item/lead)."
|
|
2858
|
+
},
|
|
2859
|
+
{
|
|
2860
|
+
// PLAN-072 + ALIGN-EXAMPLE-CASCADE §3.6: cascade-fired
|
|
2861
|
+
// `@example` from the bit header fills each pair's
|
|
2862
|
+
// `example` with the pair's first value (mirrors BPG's
|
|
2863
|
+
// `fillStringExample(pairs, __defaultExample=values[0])`).
|
|
2864
|
+
//
|
|
2865
|
+
// PLAN-074: the cascade-source-VALUE branch is handled by
|
|
2866
|
+
// TAG 415 (keys side) which fires once per pair — keeping
|
|
2867
|
+
// it here would emit `example` twice per pair (one per
|
|
2868
|
+
// values-side parsed_variant) and merge them into a
|
|
2869
|
+
// multi-paragraph array.
|
|
2870
|
+
//
|
|
2871
|
+
// PLAN-075: bare `$parent` reads the parent variant fire's
|
|
2872
|
+
// source `$` value (the current `++`-iteration's value
|
|
2873
|
+
// text). For single-value pairs this matches the old
|
|
2874
|
+
// `$parent.values[0]`; for multi-value pairs with
|
|
2875
|
+
// `[@example]` attached to a non-first value, it correctly
|
|
2876
|
+
// resolves to that value.
|
|
2877
|
+
//
|
|
2878
|
+
// PLAN-077: dual `[@absent, { '$cascade': '*' }]` rule
|
|
2879
|
+
// routes pair-key `[@example:V]` (a per-pair cascade
|
|
2880
|
+
// source, bound via per-card CascadeCounters scope) and
|
|
2881
|
+
// bit-header `[@example:V]` (cardset cascade) into the
|
|
2882
|
+
// pair-level `example` via `$cascade`. The aggregator in
|
|
2883
|
+
// `populate_variants` fires this rule ONCE per pair (BPG
|
|
2884
|
+
// `parseMatchPairs` `exampleCard` precedence), avoiding
|
|
2885
|
+
// per-iter pollution.
|
|
2886
|
+
key: ConfigKey.property_example,
|
|
2887
|
+
exportJsonKey: [
|
|
2888
|
+
// PLAN-084: bare `[@example]` on a values-side variant fires
|
|
2889
|
+
// once per pair (first-wins, mirrors BPG `parseMatchPairs`
|
|
2890
|
+
// `exampleCard` + `pushDownTree` set-if-null semantic).
|
|
2891
|
+
// `maxEmits: 1` enforced inside the per-pair cascade-counter
|
|
2892
|
+
// scope opened by `build_card_item`.
|
|
2893
|
+
{
|
|
2894
|
+
predicates: ["@keyonly"],
|
|
2895
|
+
maxEmits: 1,
|
|
2896
|
+
rule: {
|
|
2897
|
+
isExample: true,
|
|
2898
|
+
example: "$parent",
|
|
2899
|
+
"@bit": { isExample: true }
|
|
2900
|
+
}
|
|
2901
|
+
},
|
|
2902
|
+
{
|
|
2903
|
+
predicates: ["@absent", { $cascade: "*" }],
|
|
2904
|
+
rule: {
|
|
2905
|
+
isExample: true,
|
|
2906
|
+
example: "$cascade",
|
|
2907
|
+
"@bit": { isExample: true }
|
|
2908
|
+
}
|
|
2909
|
+
},
|
|
2910
|
+
{
|
|
2911
|
+
"@absent": {
|
|
2912
|
+
isExample: true,
|
|
2913
|
+
example: "$parent",
|
|
2914
|
+
"@bit": { isExample: true }
|
|
2915
|
+
}
|
|
2916
|
+
},
|
|
2917
|
+
// PLAN-084: same cap applies to the explicit-value `[@example:V]`
|
|
2918
|
+
// form so per-`++`-iter values-side fires also first-win.
|
|
2919
|
+
{
|
|
2920
|
+
predicates: [],
|
|
2921
|
+
maxEmits: 1,
|
|
2922
|
+
rule: { isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2923
|
+
}
|
|
2924
|
+
],
|
|
2925
|
+
description: "Example marker / value on the match pair (values side).",
|
|
2926
|
+
format: TagFormat.bitmarkText,
|
|
2927
|
+
nullable: true
|
|
2498
2928
|
},
|
|
2499
2929
|
{
|
|
2500
2930
|
key: ConfigKey.tag_title,
|
|
@@ -2504,9 +2934,13 @@ var CARDSETS = {
|
|
|
2504
2934
|
exportJsonKey: { "@bit": { heading: { forValues: "$" } } }
|
|
2505
2935
|
},
|
|
2506
2936
|
{
|
|
2937
|
+
// PLAN-084: symmetric @variantLacksTag gating with the key-side.
|
|
2507
2938
|
key: ConfigKey.property_isCaseSensitive,
|
|
2508
2939
|
exportJsonKey: [
|
|
2509
|
-
{
|
|
2940
|
+
{
|
|
2941
|
+
predicates: ["@absent", { "@variantLacksTag": "@isCaseSensitive" }],
|
|
2942
|
+
rule: { isCaseSensitive: "$ancestor" }
|
|
2943
|
+
},
|
|
2510
2944
|
{ isCaseSensitive: "$" }
|
|
2511
2945
|
],
|
|
2512
2946
|
description: "Property to indicate if the match is case sensitive.",
|
|
@@ -2532,6 +2966,7 @@ var CARDSETS = {
|
|
|
2532
2966
|
{
|
|
2533
2967
|
jsonKey: "key",
|
|
2534
2968
|
exportJsonKey: { key: "$" },
|
|
2969
|
+
format: TextFormat.plainText,
|
|
2535
2970
|
tags: [
|
|
2536
2971
|
{
|
|
2537
2972
|
key: ConfigKey.group_standardTags,
|
|
@@ -2561,10 +2996,12 @@ var CARDSETS = {
|
|
|
2561
2996
|
{
|
|
2562
2997
|
jsonKey: "values[]",
|
|
2563
2998
|
exportJsonKey: { values: ["$"] },
|
|
2999
|
+
format: TextFormat.plainText,
|
|
2564
3000
|
tags: [
|
|
2565
3001
|
{
|
|
2566
|
-
|
|
2567
|
-
|
|
3002
|
+
// PLAN-084: value-side excludes item/lead/pageNumber/marginNumber.
|
|
3003
|
+
key: ConfigKey.group_standardTagsNoItemLead,
|
|
3004
|
+
description: "Standard tags for the audio match pair (values side, no item/lead)."
|
|
2568
3005
|
},
|
|
2569
3006
|
{
|
|
2570
3007
|
key: ConfigKey.tag_title,
|
|
@@ -2597,6 +3034,7 @@ var CARDSETS = {
|
|
|
2597
3034
|
{
|
|
2598
3035
|
jsonKey: "key",
|
|
2599
3036
|
exportJsonKey: { key: "$" },
|
|
3037
|
+
format: TextFormat.plainText,
|
|
2600
3038
|
tags: [
|
|
2601
3039
|
{
|
|
2602
3040
|
key: ConfigKey.group_standardTags,
|
|
@@ -2626,10 +3064,12 @@ var CARDSETS = {
|
|
|
2626
3064
|
{
|
|
2627
3065
|
jsonKey: "values[]",
|
|
2628
3066
|
exportJsonKey: { values: ["$"] },
|
|
3067
|
+
format: TextFormat.plainText,
|
|
2629
3068
|
tags: [
|
|
2630
3069
|
{
|
|
2631
|
-
|
|
2632
|
-
|
|
3070
|
+
// PLAN-084: value-side excludes item/lead/pageNumber/marginNumber.
|
|
3071
|
+
key: ConfigKey.group_standardTagsNoItemLead,
|
|
3072
|
+
description: "Standard tags for the image match pair (values side, no item/lead)."
|
|
2633
3073
|
},
|
|
2634
3074
|
{
|
|
2635
3075
|
key: ConfigKey.tag_title,
|
|
@@ -2662,20 +3102,49 @@ var CARDSETS = {
|
|
|
2662
3102
|
{
|
|
2663
3103
|
jsonKey: "key",
|
|
2664
3104
|
exportJsonKey: { key: "$" },
|
|
3105
|
+
format: TextFormat.plainText,
|
|
2665
3106
|
tags: [
|
|
2666
3107
|
{
|
|
2667
3108
|
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
2668
3109
|
description: "Standard tags for lead, instruction, and hint."
|
|
2669
3110
|
},
|
|
2670
3111
|
{
|
|
3112
|
+
// PLAN-076: drop `example: '$'` from valued rule — pair-key
|
|
3113
|
+
// `[@example:V]` is consumed as the per-pair cascade source.
|
|
3114
|
+
// PLAN-077 R14 follow-up: investigation showed the fixture
|
|
3115
|
+
// reference is INCONSISTENT — bits without a cardset-level
|
|
3116
|
+
// cascade (e.g. bit 3 `[@example:Y]should be...`) expect
|
|
3117
|
+
// pair-level `example: V` at pair scope, but bits WITH a
|
|
3118
|
+
// cardset cascade (e.g. bit 8 `[@example:99]` in title-row
|
|
3119
|
+
// PLUS pair-key `[@example:Y]`) expect NO pair-level
|
|
3120
|
+
// example. Emitting `example: $` here satisfies bit 3 but
|
|
3121
|
+
// breaks bit 8; dropping it satisfies bit 8 but breaks bit
|
|
3122
|
+
// 3. Conditional emission would require runtime gating on
|
|
3123
|
+
// the cardset cascade scope state. Left dropped pending a
|
|
3124
|
+
// dedicated plan — accepted divergence on bit 3's pair-
|
|
3125
|
+
// level emission (cells still get the per-cell `example`
|
|
3126
|
+
// correctly via PLAN-077 §4.2 + §4.3).
|
|
2671
3127
|
key: ConfigKey.property_example,
|
|
2672
3128
|
exportJsonKey: [
|
|
2673
|
-
{
|
|
2674
|
-
|
|
2675
|
-
|
|
3129
|
+
{
|
|
3130
|
+
"@keyonly": {
|
|
3131
|
+
isExample: true,
|
|
3132
|
+
"@bit": { isExample: true },
|
|
3133
|
+
"@card": { isExample: true }
|
|
3134
|
+
}
|
|
3135
|
+
},
|
|
3136
|
+
{
|
|
3137
|
+
"@absent": {
|
|
3138
|
+
isExample: true,
|
|
3139
|
+
"@bit": { isExample: true },
|
|
3140
|
+
"@card": { isExample: true }
|
|
3141
|
+
}
|
|
3142
|
+
},
|
|
3143
|
+
{ isExample: true, "@bit": { isExample: true }, "@card": { isExample: true } }
|
|
2676
3144
|
],
|
|
2677
3145
|
description: "Example text for the match matrix.",
|
|
2678
|
-
format: TagFormat.plainText
|
|
3146
|
+
format: TagFormat.plainText,
|
|
3147
|
+
nullable: true
|
|
2679
3148
|
},
|
|
2680
3149
|
{
|
|
2681
3150
|
key: ConfigKey.tag_title,
|
|
@@ -2703,35 +3172,96 @@ var CARDSETS = {
|
|
|
2703
3172
|
variants: [
|
|
2704
3173
|
{
|
|
2705
3174
|
jsonKey: "cells[{s}].values[]",
|
|
2706
|
-
exportJsonKey:
|
|
3175
|
+
exportJsonKey: [
|
|
3176
|
+
// PLAN-085 / R14: the positional-blank emission for an
|
|
3177
|
+
// effectively-absent cell side fires ONLY when the active
|
|
3178
|
+
// card has at least one `[#]` variant fire. This matches
|
|
3179
|
+
// BPG `extractHeadingCard`'s `isHeading` gate: when no
|
|
3180
|
+
// `[#]` exists anywhere in the title card, BPG returns
|
|
3181
|
+
// undefined and no `heading` is emitted. The phantom
|
|
3182
|
+
// `forValues:['']` placeholder is appropriate only when
|
|
3183
|
+
// the card is being treated as a title-row.
|
|
3184
|
+
{
|
|
3185
|
+
predicates: ["@absent", { "@variantHasTag": "#" }],
|
|
3186
|
+
rule: { "@bit": { heading: { forValues: [""] } } }
|
|
3187
|
+
},
|
|
3188
|
+
{ cells: { $s: { values: ["$"] } } }
|
|
3189
|
+
],
|
|
3190
|
+
format: TextFormat.plainText,
|
|
2707
3191
|
tags: [
|
|
2708
3192
|
{
|
|
2709
3193
|
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
2710
3194
|
description: "Standard tags for lead, instruction, and hint."
|
|
2711
3195
|
},
|
|
2712
3196
|
{
|
|
3197
|
+
// PLAN-075: bare `$parent` reads the parent variant fire's
|
|
3198
|
+
// source `$` value (the current `++`-iteration's cell text).
|
|
3199
|
+
// Replaces `$parent.values[0]` which pinned to position 0
|
|
3200
|
+
// regardless of which `++`-value `[@example]` was on.
|
|
3201
|
+
//
|
|
3202
|
+
// PLAN-077: dual `[@absent, { '$cascade': '*' }]` rule
|
|
3203
|
+
// routes pair-key `[@example:V]` (a per-pair cascade
|
|
3204
|
+
// source, bound via per-card CascadeCounters scope) and
|
|
3205
|
+
// bit-header `[@example:V]` (cardset cascade) into the
|
|
3206
|
+
// per-cell `example` via `$cascade`. The aggregator in
|
|
3207
|
+
// `populate_variants` fires this rule ONCE per cell (BPG
|
|
3208
|
+
// `parseMatchMatrix` `exampleSide` precedence), avoiding
|
|
3209
|
+
// per-iter pollution.
|
|
3210
|
+
// @card scope-shift writes matrix-row container isExample.
|
|
2713
3211
|
key: ConfigKey.property_example,
|
|
2714
3212
|
exportJsonKey: [
|
|
2715
3213
|
{
|
|
2716
3214
|
"@keyonly": {
|
|
2717
|
-
cells: { $s: { isExample: true, example: "$parent
|
|
2718
|
-
"@bit": { isExample: true }
|
|
3215
|
+
cells: { $s: { isExample: true, example: "$parent" } },
|
|
3216
|
+
"@bit": { isExample: true },
|
|
3217
|
+
"@card": { isExample: true }
|
|
2719
3218
|
}
|
|
2720
3219
|
},
|
|
2721
3220
|
{
|
|
2722
|
-
"@absent"
|
|
3221
|
+
predicates: ["@absent", { $cascade: "*" }],
|
|
3222
|
+
rule: {
|
|
3223
|
+
cells: { $s: { isExample: true, example: "$cascade" } },
|
|
3224
|
+
"@bit": { isExample: true },
|
|
3225
|
+
"@card": { isExample: true }
|
|
3226
|
+
}
|
|
2723
3227
|
},
|
|
2724
|
-
{
|
|
3228
|
+
{
|
|
3229
|
+
"@absent": {
|
|
3230
|
+
cells: { $s: { isExample: true, example: "$parent" } },
|
|
3231
|
+
"@bit": { isExample: true },
|
|
3232
|
+
"@card": { isExample: true }
|
|
3233
|
+
}
|
|
3234
|
+
},
|
|
3235
|
+
{
|
|
3236
|
+
cells: { $s: { isExample: true, example: "$" } },
|
|
3237
|
+
"@bit": { isExample: true },
|
|
3238
|
+
"@card": { isExample: true }
|
|
3239
|
+
}
|
|
2725
3240
|
],
|
|
2726
3241
|
description: "Example text for the match matrix.",
|
|
2727
|
-
format: TagFormat.
|
|
3242
|
+
format: TagFormat.bitmarkText,
|
|
3243
|
+
nullable: true
|
|
2728
3244
|
},
|
|
2729
3245
|
{
|
|
3246
|
+
// PLAN-085 / R14: bare `[#]` on a value-side emits the
|
|
3247
|
+
// empty-string positional blank into `forValues` so the
|
|
3248
|
+
// array stays aligned with the cell positions (matches BPG
|
|
3249
|
+
// `extractHeadingCard`'s "all value-side headings present"
|
|
3250
|
+
// shape). The `@keyonly` rule is required for two reasons:
|
|
3251
|
+
// 1) it provides an emission for bare `[#]` (the `$` sigil
|
|
3252
|
+
// in the unconditional rule has no source for bare); and
|
|
3253
|
+
// 2) the presence of any `@keyonly` rule disables the
|
|
3254
|
+
// optimised-mode `should_suppress_outcome` strip (see
|
|
3255
|
+
// `forward_emit.rs`) so the resulting `forValues: [""]`
|
|
3256
|
+
// survives the all-natural-defaults check.
|
|
2730
3257
|
key: ConfigKey.tag_title,
|
|
2731
3258
|
description: "Title of the match matrix.",
|
|
2732
3259
|
format: TagFormat.plainText,
|
|
2733
3260
|
jsonKey: "^heading.forValues",
|
|
2734
|
-
exportJsonKey:
|
|
3261
|
+
exportJsonKey: [
|
|
3262
|
+
{ "@keyonly": { "@bit": { heading: { forValues: [""] } } } },
|
|
3263
|
+
{ "@bit": { heading: { forValues: ["$"] } } }
|
|
3264
|
+
]
|
|
2735
3265
|
},
|
|
2736
3266
|
{
|
|
2737
3267
|
key: ConfigKey.property_isCaseSensitive,
|
|
@@ -2780,6 +3310,35 @@ var CARDSETS = {
|
|
|
2780
3310
|
{
|
|
2781
3311
|
key: ConfigKey.group_standardTags,
|
|
2782
3312
|
description: "Standard tags for the statement."
|
|
3313
|
+
},
|
|
3314
|
+
{
|
|
3315
|
+
// PLAN-072: cascade-fired `@example` from the bit header
|
|
3316
|
+
// fills each statement entry's `example` with the entry's
|
|
3317
|
+
// own `isCorrect`, mirroring BPG's `pushExampleDownTree`
|
|
3318
|
+
// for the statements cardset. Per-array-item strip removes
|
|
3319
|
+
// `example: false` from incorrect entries so only correct
|
|
3320
|
+
// entries retain `example: true`.
|
|
3321
|
+
key: ConfigKey.property_example,
|
|
3322
|
+
exportJsonKey: [
|
|
3323
|
+
{
|
|
3324
|
+
"@keyonly": {
|
|
3325
|
+
isExample: true,
|
|
3326
|
+
example: true,
|
|
3327
|
+
"@bit": { isExample: true }
|
|
3328
|
+
}
|
|
3329
|
+
},
|
|
3330
|
+
{
|
|
3331
|
+
"@absent": {
|
|
3332
|
+
isExample: true,
|
|
3333
|
+
example: "$parent.isCorrect",
|
|
3334
|
+
"@bit": { isExample: true }
|
|
3335
|
+
}
|
|
3336
|
+
},
|
|
3337
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
3338
|
+
],
|
|
3339
|
+
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`.",
|
|
3340
|
+
format: TagFormat.boolean,
|
|
3341
|
+
nullable: true
|
|
2783
3342
|
}
|
|
2784
3343
|
],
|
|
2785
3344
|
bodyAllowed: false
|
|
@@ -2808,6 +3367,71 @@ var CARDSETS = {
|
|
|
2808
3367
|
{
|
|
2809
3368
|
key: ConfigKey.group_standardTags,
|
|
2810
3369
|
description: "Standard tags for the quiz."
|
|
3370
|
+
},
|
|
3371
|
+
{
|
|
3372
|
+
// ALIGN-EXAMPLE-CASCADE §3.2: variant-level cascade for
|
|
3373
|
+
// quiz choices is delegated to group_trueFalse's
|
|
3374
|
+
// chain-child `@example` (`+` chain with maxEmits:1 +
|
|
3375
|
+
// `@parent.isCorrect=true`). Variant-level `@absent`
|
|
3376
|
+
// would over-fire on subsequent correct choices and on
|
|
3377
|
+
// incorrect ones — drop it.
|
|
3378
|
+
key: ConfigKey.property_example,
|
|
3379
|
+
exportJsonKey: [
|
|
3380
|
+
{ "@keyonly": { isExample: true, "@bit": { isExample: true } } },
|
|
3381
|
+
{ isExample: true, "@bit": { isExample: true } }
|
|
3382
|
+
],
|
|
3383
|
+
description: "Example marker on the quiz choice (entry-local only; cascade handled by group_trueFalse chain-children).",
|
|
3384
|
+
format: TagFormat.boolean,
|
|
3385
|
+
nullable: true
|
|
3386
|
+
}
|
|
3387
|
+
],
|
|
3388
|
+
bodyAllowed: false
|
|
3389
|
+
}
|
|
3390
|
+
]
|
|
3391
|
+
}
|
|
3392
|
+
]
|
|
3393
|
+
},
|
|
3394
|
+
//
|
|
3395
|
+
// quiz (responses)
|
|
3396
|
+
//
|
|
3397
|
+
[CardSetConfigKey.quizResponses]: {
|
|
3398
|
+
jsonKey: "quizzes",
|
|
3399
|
+
exportJsonKey: { quizzes: "$" },
|
|
3400
|
+
sides: [
|
|
3401
|
+
{
|
|
3402
|
+
name: "responses",
|
|
3403
|
+
variants: [
|
|
3404
|
+
{
|
|
3405
|
+
jsonKey: null,
|
|
3406
|
+
tags: [
|
|
3407
|
+
{
|
|
3408
|
+
key: ConfigKey.group_trueFalseResponses,
|
|
3409
|
+
description: "Group for true/false questions."
|
|
3410
|
+
},
|
|
3411
|
+
{
|
|
3412
|
+
key: ConfigKey.group_standardTags,
|
|
3413
|
+
description: "Standard tags for the quiz."
|
|
3414
|
+
},
|
|
3415
|
+
{
|
|
3416
|
+
// PLAN-072: cascade-fired `@example` from the bit header
|
|
3417
|
+
// fills each response entry's `example` with the entry's
|
|
3418
|
+
// own `isCorrect`, mirroring BPG's `pushExampleDownTree`
|
|
3419
|
+
// for the quiz-responses cardset (statements-style).
|
|
3420
|
+
key: ConfigKey.property_example,
|
|
3421
|
+
exportJsonKey: [
|
|
3422
|
+
{ "@keyonly": { isExample: true, "@bit": { isExample: true } } },
|
|
3423
|
+
{
|
|
3424
|
+
"@absent": {
|
|
3425
|
+
isExample: true,
|
|
3426
|
+
example: "$parent.isCorrect",
|
|
3427
|
+
"@bit": { isExample: true }
|
|
3428
|
+
}
|
|
3429
|
+
},
|
|
3430
|
+
{ isExample: true, "@bit": { isExample: true } }
|
|
3431
|
+
],
|
|
3432
|
+
description: "Example marker on the quiz response; cascade from bit-header fills `example` with the entry\u2019s own `isCorrect`.",
|
|
3433
|
+
format: TagFormat.boolean,
|
|
3434
|
+
nullable: true
|
|
2811
3435
|
}
|
|
2812
3436
|
],
|
|
2813
3437
|
bodyAllowed: false
|
|
@@ -2833,6 +3457,73 @@ var CARDSETS = {
|
|
|
2833
3457
|
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
2834
3458
|
description: "Standard tags for lead, instruction, and hint."
|
|
2835
3459
|
},
|
|
3460
|
+
{
|
|
3461
|
+
// Feedback choices: `[+]` / `[-]` map to choice + `requireReason`
|
|
3462
|
+
// (NOT `isCorrect` like trueFalse). Shadow the inherited
|
|
3463
|
+
// `tag_true` / `tag_false` from `group_trueFalse` to emit the
|
|
3464
|
+
// feedback-specific shape.
|
|
3465
|
+
//
|
|
3466
|
+
// Chain @example: `[+ X][@example]` writes
|
|
3467
|
+
// `isExample/example` onto the X choice only. BPG's
|
|
3468
|
+
// `setIsExampleFlags` (Builder.ts:4041-4172) does NOT
|
|
3469
|
+
// iterate `cardNode.feedbacks` — so unlike multipleChoice,
|
|
3470
|
+
// there is no bubble to bit-level or feedbacks-level
|
|
3471
|
+
// isExample. Pattern omits `@bit` scope-shift accordingly.
|
|
3472
|
+
// No `@absent`/`@parent.isCorrect` cascade rules either:
|
|
3473
|
+
// BPG's `pushExampleDownTree` (Builder.ts:3777-3849) has
|
|
3474
|
+
// no feedback dispatch, so bit-header `[@example]` does
|
|
3475
|
+
// not flow into feedback choices.
|
|
3476
|
+
key: ConfigKey.tag_true,
|
|
3477
|
+
exportJsonKey: { choices: [{ choice: "$", requireReason: true }] },
|
|
3478
|
+
description: "Reason-required choice for feedback (`[+]`).",
|
|
3479
|
+
maxCount: Count.infinity,
|
|
3480
|
+
format: TagFormat.plainText,
|
|
3481
|
+
chain: [
|
|
3482
|
+
{
|
|
3483
|
+
key: ConfigKey.property_example,
|
|
3484
|
+
exportJsonKey: [
|
|
3485
|
+
{ "@keyonly": { isExample: true, example: true } },
|
|
3486
|
+
{ isExample: true, example: "$" }
|
|
3487
|
+
],
|
|
3488
|
+
description: "Per-choice example marker for feedback `[+]`.",
|
|
3489
|
+
format: TagFormat.boolean,
|
|
3490
|
+
maxCount: 1,
|
|
3491
|
+
nullable: true
|
|
3492
|
+
},
|
|
3493
|
+
{
|
|
3494
|
+
// `[+ X][! foo]` chains `!` (instruction) onto the
|
|
3495
|
+
// choice; same for `[?]` (hint), `[%]` (item),
|
|
3496
|
+
// `[%2]`/`[%3]`/`[%4]` (lead/pageNumber/marginNumber).
|
|
3497
|
+
// Mirrors group_trueFalse's `+` chain shape.
|
|
3498
|
+
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
3499
|
+
description: "Item, lead, page number, margin number, instruction and hint chained onto the feedback choice."
|
|
3500
|
+
}
|
|
3501
|
+
]
|
|
3502
|
+
},
|
|
3503
|
+
{
|
|
3504
|
+
key: ConfigKey.tag_false,
|
|
3505
|
+
exportJsonKey: { choices: [{ choice: "$", requireReason: false }] },
|
|
3506
|
+
description: "Reason-not-required choice for feedback (`[-]`).",
|
|
3507
|
+
maxCount: Count.infinity,
|
|
3508
|
+
format: TagFormat.plainText,
|
|
3509
|
+
chain: [
|
|
3510
|
+
{
|
|
3511
|
+
key: ConfigKey.property_example,
|
|
3512
|
+
exportJsonKey: [
|
|
3513
|
+
{ "@keyonly": { isExample: true, example: true } },
|
|
3514
|
+
{ isExample: true, example: "$" }
|
|
3515
|
+
],
|
|
3516
|
+
description: "Per-choice example marker for feedback `[-]`.",
|
|
3517
|
+
format: TagFormat.boolean,
|
|
3518
|
+
maxCount: 1,
|
|
3519
|
+
nullable: true
|
|
3520
|
+
},
|
|
3521
|
+
{
|
|
3522
|
+
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
3523
|
+
description: "Item, lead, page number, margin number, instruction and hint chained onto the feedback choice."
|
|
3524
|
+
}
|
|
3525
|
+
]
|
|
3526
|
+
},
|
|
2836
3527
|
{
|
|
2837
3528
|
key: ConfigKey.group_trueFalse,
|
|
2838
3529
|
description: "Group for true/false feedback."
|
|
@@ -2853,31 +3544,71 @@ var CARDSETS = {
|
|
|
2853
3544
|
name: "reason",
|
|
2854
3545
|
variants: [
|
|
2855
3546
|
{
|
|
3547
|
+
// PLAN: feedback.reason — Object-only variant pattern. Per
|
|
3548
|
+
// SYNTAX.md §8.1, sibling tag emissions do NOT auto-wrap
|
|
3549
|
+
// under `reason.*`; each tag here spells out the full path.
|
|
3550
|
+
// Body is a plain string (BPG `parseFeedback` uses
|
|
3551
|
+
// `cardBodyStr ?? ''` for `reason.text`).
|
|
2856
3552
|
jsonKey: "reason.text",
|
|
2857
3553
|
exportJsonKey: { reason: { text: "$" } },
|
|
3554
|
+
format: TextFormat.plainText,
|
|
2858
3555
|
tags: [
|
|
2859
3556
|
{
|
|
2860
|
-
|
|
2861
|
-
|
|
3557
|
+
// Instruction lands under reason. Hint/item/lead/etc.
|
|
3558
|
+
// omitted: not exercised by any fixture and BPG's
|
|
3559
|
+
// `parseFeedback` spreads only the tags actually present
|
|
3560
|
+
// — adding the rest can be done lazily as fixtures
|
|
3561
|
+
// require them, with the same `{reason: {…}}` wrapper.
|
|
3562
|
+
key: ConfigKey.tag_instruction,
|
|
3563
|
+
exportJsonKey: { reason: { instruction: "$" } },
|
|
3564
|
+
description: "Instruction for the feedback reason.",
|
|
3565
|
+
format: TagFormat.bitmarkText
|
|
2862
3566
|
},
|
|
2863
3567
|
{
|
|
2864
3568
|
key: ConfigKey.property_reasonableNumOfChars,
|
|
2865
3569
|
exportJsonKey: [
|
|
2866
|
-
{ "@absent": { reasonableNumOfChars: "$ancestor" } },
|
|
2867
|
-
{ reasonableNumOfChars: "$" }
|
|
3570
|
+
{ "@absent": { reason: { reasonableNumOfChars: "$ancestor" } } },
|
|
3571
|
+
{ reason: { reasonableNumOfChars: "$" } }
|
|
2868
3572
|
],
|
|
2869
3573
|
description: "Property for reasonable number of characters.",
|
|
2870
3574
|
format: TagFormat.number
|
|
2871
3575
|
},
|
|
2872
3576
|
{
|
|
3577
|
+
// ALIGN-EXAMPLE-CASCADE §3.9: `feedback.reason` is NOT in
|
|
3578
|
+
// BPG's `pushExampleDownTree` dispatch — no bit-header
|
|
3579
|
+
// cascade. Local emission only.
|
|
3580
|
+
//
|
|
3581
|
+
// Both `reason.example` and `feedbacks[].example` are
|
|
3582
|
+
// `TextAst` per BPG's schema — paragraph arrays. Format
|
|
3583
|
+
// here is `bitmarkText` so `$` coerces to a paragraph
|
|
3584
|
+
// array at both write sites.
|
|
2873
3585
|
key: ConfigKey.property_example,
|
|
2874
3586
|
exportJsonKey: [
|
|
2875
|
-
{
|
|
2876
|
-
|
|
2877
|
-
|
|
3587
|
+
{
|
|
3588
|
+
// Bare `[@example]`: reason.example is TextAst — BPG
|
|
3589
|
+
// coerces the boolean default to a paragraph
|
|
3590
|
+
// containing the string "true". feedbacks[].example
|
|
3591
|
+
// is not TextAst, stays a boolean. Two literal writes.
|
|
3592
|
+
"@keyonly": {
|
|
3593
|
+
reason: {
|
|
3594
|
+
isExample: true,
|
|
3595
|
+
example: [{ type: "paragraph", content: [{ text: "true", type: "text" }] }]
|
|
3596
|
+
},
|
|
3597
|
+
isExample: true,
|
|
3598
|
+
example: true,
|
|
3599
|
+
"@bit": { isExample: true }
|
|
3600
|
+
}
|
|
3601
|
+
},
|
|
3602
|
+
{
|
|
3603
|
+
reason: { isExample: true, example: "$" },
|
|
3604
|
+
isExample: true,
|
|
3605
|
+
example: "$",
|
|
3606
|
+
"@bit": { isExample: true }
|
|
3607
|
+
}
|
|
2878
3608
|
],
|
|
2879
|
-
description: "Example text for the feedback.",
|
|
2880
|
-
format: TagFormat.
|
|
3609
|
+
description: "Example text for the feedback reason (entry-local only; no bit-header cascade).",
|
|
3610
|
+
format: TagFormat.bitmarkText,
|
|
3611
|
+
nullable: true
|
|
2881
3612
|
},
|
|
2882
3613
|
{
|
|
2883
3614
|
key: ConfigKey.tag_title,
|
|
@@ -2941,6 +3672,39 @@ var CARDSETS = {
|
|
|
2941
3672
|
{
|
|
2942
3673
|
key: ConfigKey.group_standardTags,
|
|
2943
3674
|
description: "Standard tags for the question."
|
|
3675
|
+
},
|
|
3676
|
+
{
|
|
3677
|
+
// ALIGN-EXAMPLE-CASCADE §3.10: `questions` is NOT in BPG's
|
|
3678
|
+
// `pushExampleDownTree` dispatch — bit-header `[@example]`
|
|
3679
|
+
// does not propagate `example`/`isExample` onto question
|
|
3680
|
+
// entries. Only entry-local `[@example]` fires.
|
|
3681
|
+
//
|
|
3682
|
+
// BPG's `BaseBuilder.toExample` for interview questions
|
|
3683
|
+
// (Builder.ts:2575,2588) uses `data.__sampleSolutionAst`
|
|
3684
|
+
// as `defaultExample` — bare `[@example]` (which sets
|
|
3685
|
+
// `__isDefaultExample=true, example=undefined`) emits the
|
|
3686
|
+
// parsed sample-solution AST in the `example` slot. The
|
|
3687
|
+
// `@keyonly` rule mirrors that runtime behaviour via
|
|
3688
|
+
// `$parent.sampleSolution` — the chain-walker's post-walk
|
|
3689
|
+
// retry resolves this once the sibling `[@sampleSolution]`
|
|
3690
|
+
// chain has merged into the shared scope (interview's
|
|
3691
|
+
// `[@example]` typically precedes `[@sampleSolution]` in
|
|
3692
|
+
// source). Format coercion turns the resolved string
|
|
3693
|
+
// value into a PM tree.
|
|
3694
|
+
key: ConfigKey.property_example,
|
|
3695
|
+
exportJsonKey: [
|
|
3696
|
+
{
|
|
3697
|
+
"@keyonly": {
|
|
3698
|
+
isExample: true,
|
|
3699
|
+
example: "$parent.sampleSolution",
|
|
3700
|
+
"@bit": { isExample: true }
|
|
3701
|
+
}
|
|
3702
|
+
},
|
|
3703
|
+
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
3704
|
+
],
|
|
3705
|
+
description: "Example marker for the question (entry-local only; no bit-header cascade).",
|
|
3706
|
+
format: TagFormat.bitmarkText,
|
|
3707
|
+
nullable: true
|
|
2944
3708
|
}
|
|
2945
3709
|
]
|
|
2946
3710
|
}
|
|
@@ -2967,14 +3731,16 @@ var CARDSETS = {
|
|
|
2967
3731
|
description: "Standard tags for lead, instruction, and hint."
|
|
2968
3732
|
},
|
|
2969
3733
|
{
|
|
3734
|
+
// ALIGN-EXAMPLE-CASCADE §3.11: `elements` is NOT in BPG's
|
|
3735
|
+
// `pushExampleDownTree` dispatch — no bit-header cascade.
|
|
2970
3736
|
key: ConfigKey.property_example,
|
|
2971
3737
|
exportJsonKey: [
|
|
2972
3738
|
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2973
|
-
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
2974
3739
|
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
2975
3740
|
],
|
|
2976
|
-
description: "Example text for the element.",
|
|
2977
|
-
format: TagFormat.plainText
|
|
3741
|
+
description: "Example text for the element (entry-local only; no bit-header cascade).",
|
|
3742
|
+
format: TagFormat.plainText,
|
|
3743
|
+
nullable: true
|
|
2978
3744
|
}
|
|
2979
3745
|
],
|
|
2980
3746
|
repeatCount: Count.infinity
|
|
@@ -2986,9 +3752,57 @@ var CARDSETS = {
|
|
|
2986
3752
|
//
|
|
2987
3753
|
// table
|
|
2988
3754
|
//
|
|
3755
|
+
// PLAN-079: supports BOTH source formats and collapses to the same basic
|
|
3756
|
+
// output (`{ columns?, data, columnWidths? }`):
|
|
3757
|
+
//
|
|
3758
|
+
// 1. Basic format — first card carries `[#X]` markers (fire `tag_title`
|
|
3759
|
+
// → `^table.columns[]`); subsequent cards are body rows. Cards have
|
|
3760
|
+
// no qualifier, so they route to the `table-body` (default) section.
|
|
3761
|
+
//
|
|
3762
|
+
// 2. Extended format — `==== table-header ====` / `==== table-footer ====`
|
|
3763
|
+
// qualifiers. The `table-header` section uses `@cardIndex=0` to route
|
|
3764
|
+
// the first header card's cells to `columns` and demote remaining
|
|
3765
|
+
// header cards (rows) into `data`. Per-cell `[@tableRowSpan]` /
|
|
3766
|
+
// `[@tableScope]` / `[@tableCellType]` tags are valid-but-unmapped
|
|
3767
|
+
// here (no per-cell metadata in basic shape) — declared on the cell
|
|
3768
|
+
// variant below.
|
|
3769
|
+
//
|
|
2989
3770
|
[CardSetConfigKey.table]: {
|
|
2990
|
-
jsonKey:
|
|
2991
|
-
exportJsonKey:
|
|
3771
|
+
jsonKey: null,
|
|
3772
|
+
exportJsonKey: null,
|
|
3773
|
+
sections: {
|
|
3774
|
+
"table-header": {
|
|
3775
|
+
// First header card's row of cells → `columns`. Subsequent header
|
|
3776
|
+
// cards' rows → `data` (per-card append; `[$]` shape because
|
|
3777
|
+
// per-card emission fires once per card, multiple fires deep_merge-
|
|
3778
|
+
// append). PLAN-079 + PLAN-080: predicates inside exportJsonKey
|
|
3779
|
+
// both ROUTE cards to this section (when no explicit `==== table-
|
|
3780
|
+
// header ====` qualifier) and SELECT the rule body once routed.
|
|
3781
|
+
// First rule = first card with any `[#]` → `columns`; second rule
|
|
3782
|
+
// = subsequent cards in this section → `data` (per-row append).
|
|
3783
|
+
jsonKey: "table.columns",
|
|
3784
|
+
exportJsonKey: [
|
|
3785
|
+
{
|
|
3786
|
+
predicates: [{ "@cardIndex": 0 }, { "@variantHasTag": "#" }],
|
|
3787
|
+
rule: { "@bit": { table: { columns: "$" } } }
|
|
3788
|
+
},
|
|
3789
|
+
{ "@cardIndex=0": { "@bit": { table: { columns: "$" } } } },
|
|
3790
|
+
{ "@bit": { table: { data: ["$"] } } }
|
|
3791
|
+
]
|
|
3792
|
+
},
|
|
3793
|
+
"table-body": {
|
|
3794
|
+
// Batched emission: `$` is the entire 2D array of body card rows.
|
|
3795
|
+
// This is the default section (no qualifier → cards land here).
|
|
3796
|
+
jsonKey: "table.data",
|
|
3797
|
+
exportJsonKey: { "@bit": { table: { data: "$" } } },
|
|
3798
|
+
isDefault: true
|
|
3799
|
+
},
|
|
3800
|
+
"table-footer": {
|
|
3801
|
+
// Batched emission: `$` is the entire 2D array of footer rows.
|
|
3802
|
+
jsonKey: "table.data",
|
|
3803
|
+
exportJsonKey: { "@bit": { table: { data: "$" } } }
|
|
3804
|
+
}
|
|
3805
|
+
},
|
|
2992
3806
|
sides: [
|
|
2993
3807
|
{
|
|
2994
3808
|
name: "cell",
|
|
@@ -3029,6 +3843,8 @@ var CARDSETS = {
|
|
|
3029
3843
|
},
|
|
3030
3844
|
{
|
|
3031
3845
|
key: ConfigKey.property_tableColWidth,
|
|
3846
|
+
jsonKey: "^table.columnWidths[{s}]",
|
|
3847
|
+
exportJsonKey: { "@bit": { table: { columnWidths: { $s: "$" } } } },
|
|
3032
3848
|
description: "Width for the column.",
|
|
3033
3849
|
format: TagFormat.number
|
|
3034
3850
|
}
|
|
@@ -3047,7 +3863,22 @@ var CARDSETS = {
|
|
|
3047
3863
|
sections: {
|
|
3048
3864
|
"table-header": {
|
|
3049
3865
|
jsonKey: "table.header.rows",
|
|
3050
|
-
|
|
3866
|
+
// PLAN-080: section dispatch via predicates inside exportJsonKey.
|
|
3867
|
+
// Cards with no explicit `==== table-header ====` qualifier
|
|
3868
|
+
// route here if FIRST CARD contains any `[#]` tag in any
|
|
3869
|
+
// variant. Explicit-qualifier cards fall through to the
|
|
3870
|
+
// unconditional fallback rule. Both rules use the per-card
|
|
3871
|
+
// append shape `rows: ['$']` — the runtime triggers per-card
|
|
3872
|
+
// emission whenever the pattern carries predicates, and each
|
|
3873
|
+
// fire appends a single-element array (multi-fire deep-merge
|
|
3874
|
+
// concatenates).
|
|
3875
|
+
exportJsonKey: [
|
|
3876
|
+
{
|
|
3877
|
+
predicates: [{ "@cardIndex": 0 }, { "@variantHasTag": "#" }],
|
|
3878
|
+
rule: { table: { header: { rows: ["$"] } } }
|
|
3879
|
+
},
|
|
3880
|
+
{ table: { header: { rows: ["$"] } } }
|
|
3881
|
+
],
|
|
3051
3882
|
sideJsonKey: "cells[{s}]|set(title=true)",
|
|
3052
3883
|
sideExportJsonKey: { cells: { $s: { title: true, $: "$" } } }
|
|
3053
3884
|
},
|
|
@@ -3080,13 +3911,13 @@ var CARDSETS = {
|
|
|
3080
3911
|
},
|
|
3081
3912
|
{
|
|
3082
3913
|
key: ConfigKey.tag_title,
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
},
|
|
3089
|
-
description: "Title of the table cell."
|
|
3914
|
+
// PLAN-080: the `[#]` tag writes its text to the cell's
|
|
3915
|
+
// `content` field. When the card is in the `table-header`
|
|
3916
|
+
// section, the section's sideExportJsonKey wraps each
|
|
3917
|
+
// cell with `title: true` automatically.
|
|
3918
|
+
jsonKey: "content",
|
|
3919
|
+
exportJsonKey: { content: "$" },
|
|
3920
|
+
description: "Title text of the table cell (header rows)."
|
|
3090
3921
|
},
|
|
3091
3922
|
{
|
|
3092
3923
|
key: ConfigKey.property_tableCellType,
|
|
@@ -3118,6 +3949,9 @@ var CARDSETS = {
|
|
|
3118
3949
|
},
|
|
3119
3950
|
{
|
|
3120
3951
|
key: ConfigKey.property_tableColWidth,
|
|
3952
|
+
// PLAN-080: write to the cell's `colwidth` field.
|
|
3953
|
+
jsonKey: "colwidth",
|
|
3954
|
+
exportJsonKey: { colwidth: "$" },
|
|
3121
3955
|
description: "Width for the column.",
|
|
3122
3956
|
format: TagFormat.number
|
|
3123
3957
|
}
|
|
@@ -3155,7 +3989,8 @@ var CARDSETS = {
|
|
|
3155
3989
|
{
|
|
3156
3990
|
key: ConfigKey.resource_audio,
|
|
3157
3991
|
description: "Audio resource for the pronunciation table.",
|
|
3158
|
-
jsonKey: "audio"
|
|
3992
|
+
jsonKey: "audio",
|
|
3993
|
+
exportJsonKey: { audio: { type: "audio", audio: { src: "$" } } }
|
|
3159
3994
|
}
|
|
3160
3995
|
],
|
|
3161
3996
|
repeatCount: Count.infinity
|
|
@@ -3226,14 +4061,17 @@ var CARDSETS = {
|
|
|
3226
4061
|
format: TagFormat.plainText
|
|
3227
4062
|
},
|
|
3228
4063
|
{
|
|
4064
|
+
// ALIGN-EXAMPLE-CASCADE §3.12: `bot-action-responses` is
|
|
4065
|
+
// NOT in BPG's `pushExampleDownTree` dispatch — no bit-
|
|
4066
|
+
// header cascade.
|
|
3229
4067
|
key: ConfigKey.property_example,
|
|
3230
4068
|
exportJsonKey: [
|
|
3231
4069
|
{ "@keyonly": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
3232
|
-
{ "@absent": { isExample: true, example: true, "@bit": { isExample: true } } },
|
|
3233
4070
|
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
3234
4071
|
],
|
|
3235
|
-
description: "Example text for the bot action response.",
|
|
3236
|
-
format: TagFormat.plainText
|
|
4072
|
+
description: "Example text for the bot action response (entry-local only; no bit-header cascade).",
|
|
4073
|
+
format: TagFormat.plainText,
|
|
4074
|
+
nullable: true
|
|
3237
4075
|
}
|
|
3238
4076
|
]
|
|
3239
4077
|
}
|
|
@@ -3299,6 +4137,38 @@ var CARDSETS = {
|
|
|
3299
4137
|
]
|
|
3300
4138
|
},
|
|
3301
4139
|
//
|
|
4140
|
+
// page-footer-sections — mirrors example-bit-list but emits cards under
|
|
4141
|
+
// `sections` instead of `listItems`. BPG's JsonGenerator special-cases
|
|
4142
|
+
// BitType.pageFooter in code; this dedicated cardSet expresses the same
|
|
4143
|
+
// routing as pure config so the Rust serializer can pick it up.
|
|
4144
|
+
//
|
|
4145
|
+
[CardSetConfigKey.pageFooterSections]: {
|
|
4146
|
+
jsonKey: "sections",
|
|
4147
|
+
exportJsonKey: { sections: "$" },
|
|
4148
|
+
sides: [
|
|
4149
|
+
{
|
|
4150
|
+
name: "item",
|
|
4151
|
+
variants: [
|
|
4152
|
+
{
|
|
4153
|
+
jsonKey: "body",
|
|
4154
|
+
exportJsonKey: { body: "$" },
|
|
4155
|
+
tags: [
|
|
4156
|
+
{
|
|
4157
|
+
key: ConfigKey.group_standardTags,
|
|
4158
|
+
description: "Standard tags for page-footer section bits."
|
|
4159
|
+
},
|
|
4160
|
+
{
|
|
4161
|
+
exportJsonKey: { title: "$" },
|
|
4162
|
+
key: ConfigKey.tag_title,
|
|
4163
|
+
description: "Title of the page-footer section."
|
|
4164
|
+
}
|
|
4165
|
+
]
|
|
4166
|
+
}
|
|
4167
|
+
]
|
|
4168
|
+
}
|
|
4169
|
+
]
|
|
4170
|
+
},
|
|
4171
|
+
//
|
|
3302
4172
|
// ingredients
|
|
3303
4173
|
//
|
|
3304
4174
|
[CardSetConfigKey.ingredients]: {
|
|
@@ -3629,7 +4499,11 @@ var GROUPS = {
|
|
|
3629
4499
|
{
|
|
3630
4500
|
key: ConfigKey.property_groupTag,
|
|
3631
4501
|
jsonKey: "groupTag.name",
|
|
3632
|
-
|
|
4502
|
+
// PLAN-063: starter-array shape with `@id` declares entity-merge
|
|
4503
|
+
// semantics — repeated `[@groupTag: name]` occurrences with the
|
|
4504
|
+
// same value fold into one entry; chained `@tag` children's
|
|
4505
|
+
// arrays merge as sets.
|
|
4506
|
+
exportJsonKey: { groupTag: [{ "@id": "name", name: "$" }] },
|
|
3633
4507
|
description: "The group tag(s) for the bit",
|
|
3634
4508
|
format: TagFormat.plainText,
|
|
3635
4509
|
maxCount: Count.infinity,
|
|
@@ -3802,6 +4676,24 @@ var GROUPS = {
|
|
|
3802
4676
|
}
|
|
3803
4677
|
]
|
|
3804
4678
|
},
|
|
4679
|
+
[ConfigKey.group_standardInstructionHint]: {
|
|
4680
|
+
type: GroupConfigType.standard,
|
|
4681
|
+
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).",
|
|
4682
|
+
tags: [
|
|
4683
|
+
{
|
|
4684
|
+
exportJsonKey: { instruction: "$" },
|
|
4685
|
+
key: ConfigKey.tag_instruction,
|
|
4686
|
+
name: "Instruction",
|
|
4687
|
+
description: "The instruction for the bit"
|
|
4688
|
+
},
|
|
4689
|
+
{
|
|
4690
|
+
exportJsonKey: { hint: "$" },
|
|
4691
|
+
key: ConfigKey.tag_hint,
|
|
4692
|
+
name: "Hint",
|
|
4693
|
+
description: "The hint for the bit"
|
|
4694
|
+
}
|
|
4695
|
+
]
|
|
4696
|
+
},
|
|
3805
4697
|
[ConfigKey.group_standardTags]: {
|
|
3806
4698
|
type: GroupConfigType.standard,
|
|
3807
4699
|
description: "Standard tags which apply to MOST (but not all) bits",
|
|
@@ -3815,17 +4707,45 @@ var GROUPS = {
|
|
|
3815
4707
|
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
3816
4708
|
},
|
|
3817
4709
|
{
|
|
3818
|
-
// Bit-level example tag
|
|
3819
|
-
//
|
|
3820
|
-
//
|
|
4710
|
+
// Bit-level example tag (default — non-`isTopLevelExample` bits):
|
|
4711
|
+
// - bare `[@example]`: consume only (records cascade marker, emits
|
|
4712
|
+
// nothing at bit-level — OLD parser scrubs bit-level isExample
|
|
4713
|
+
// for bits NOT in its `isTopLevelExample` allow-list).
|
|
4714
|
+
// - synthesized (descendant cascade): still emits `isExample: true`
|
|
4715
|
+
// at the descendant scope.
|
|
4716
|
+
// - valued `[@example:text]`: emit the example value only; bit-level
|
|
4717
|
+
// `isExample` is left to bubble from descendants.
|
|
4718
|
+
// Bits in the OLD parser's allow-list (flashcard, definitionList,
|
|
4719
|
+
// sequence, multipleChoice, trueFalse, cloze*, mark, interview,
|
|
4720
|
+
// matchMatrix, match, essay, etc.) override this entry with the
|
|
4721
|
+
// legacy emit-isExample shape.
|
|
3821
4722
|
key: ConfigKey.property_example,
|
|
3822
|
-
exportJsonKey: [
|
|
3823
|
-
{ "@keyonly": { isExample: true } },
|
|
3824
|
-
{ "@absent": { isExample: true } },
|
|
3825
|
-
{ isExample: true, example: "$" }
|
|
3826
|
-
],
|
|
4723
|
+
exportJsonKey: [{ "@keyonly": {} }, { "@absent": { isExample: true } }, { example: "$" }],
|
|
3827
4724
|
description: "The example(s) for the bit",
|
|
3828
|
-
format: TagFormat.bitmarkText
|
|
4725
|
+
format: TagFormat.bitmarkText,
|
|
4726
|
+
nullable: true
|
|
4727
|
+
}
|
|
4728
|
+
]
|
|
4729
|
+
},
|
|
4730
|
+
[ConfigKey.group_standardTagsNoItemLead]: {
|
|
4731
|
+
type: GroupConfigType.standard,
|
|
4732
|
+
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).",
|
|
4733
|
+
tags: [
|
|
4734
|
+
{
|
|
4735
|
+
key: ConfigKey.group_standardAllBits,
|
|
4736
|
+
description: "All standard tags which apply to all bits"
|
|
4737
|
+
},
|
|
4738
|
+
{
|
|
4739
|
+
key: ConfigKey.group_standardInstructionHint,
|
|
4740
|
+
description: "Instruction and hint tags only (no item/lead chain)."
|
|
4741
|
+
},
|
|
4742
|
+
{
|
|
4743
|
+
// Mirror of group_standardTags property_example entry.
|
|
4744
|
+
key: ConfigKey.property_example,
|
|
4745
|
+
exportJsonKey: [{ "@keyonly": {} }, { "@absent": { isExample: true } }, { example: "$" }],
|
|
4746
|
+
description: "The example(s) for the bit",
|
|
4747
|
+
format: TagFormat.bitmarkText,
|
|
4748
|
+
nullable: true
|
|
3829
4749
|
}
|
|
3830
4750
|
]
|
|
3831
4751
|
},
|
|
@@ -3890,8 +4810,8 @@ var GROUPS = {
|
|
|
3890
4810
|
tags: [
|
|
3891
4811
|
{
|
|
3892
4812
|
key: ConfigKey.property_person,
|
|
3893
|
-
jsonKey: "
|
|
3894
|
-
exportJsonKey: {
|
|
4813
|
+
jsonKey: "person.name",
|
|
4814
|
+
exportJsonKey: { person: { name: "$" } },
|
|
3895
4815
|
description: "A person",
|
|
3896
4816
|
format: TagFormat.plainText,
|
|
3897
4817
|
chain: [
|
|
@@ -3911,8 +4831,8 @@ var GROUPS = {
|
|
|
3911
4831
|
{
|
|
3912
4832
|
// Deprecated (parter renamed to person)
|
|
3913
4833
|
key: ConfigKey.property_partner,
|
|
3914
|
-
jsonKey: "
|
|
3915
|
-
exportJsonKey: {
|
|
4834
|
+
jsonKey: "person.name",
|
|
4835
|
+
exportJsonKey: { person: { name: "$" } },
|
|
3916
4836
|
description: "A partner",
|
|
3917
4837
|
format: TagFormat.plainText,
|
|
3918
4838
|
chain: [
|
|
@@ -3936,7 +4856,9 @@ var GROUPS = {
|
|
|
3936
4856
|
description: "Gap chain",
|
|
3937
4857
|
tags: [
|
|
3938
4858
|
{
|
|
3939
|
-
exportJsonKey: {
|
|
4859
|
+
exportJsonKey: {
|
|
4860
|
+
"@body-inline": { type: "gap", attrs: { solutions: ["$"] } }
|
|
4861
|
+
},
|
|
3940
4862
|
key: ConfigKey.tag_gap,
|
|
3941
4863
|
description: "The value of a gap in the content",
|
|
3942
4864
|
maxCount: Count.infinity,
|
|
@@ -3955,27 +4877,37 @@ var GROUPS = {
|
|
|
3955
4877
|
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
3956
4878
|
},
|
|
3957
4879
|
{
|
|
4880
|
+
// BPG ExampleTagContentProcessor.ts:91-95 reads
|
|
4881
|
+
// `__solutionsAst[length - 1]` — the solution closest to the
|
|
4882
|
+
// `[@example]` tag at parse time. Our chain accumulator is built
|
|
4883
|
+
// incrementally (scope.rs `parent.merge` after each tag), so at
|
|
4884
|
+
// the moment `[@example]` fires, `solutions` only contains the
|
|
4885
|
+
// preceding `[_x]` entries — `[-1]` resolves to the closest one.
|
|
4886
|
+
// E.g. `[_cloze][_gap][@example]` → solutions=["cloze","gap"],
|
|
4887
|
+
// `[-1]`="gap"; `[_solution][@example][_problem]` →
|
|
4888
|
+
// solutions=["solution"], `[-1]`="solution".
|
|
3958
4889
|
key: ConfigKey.property_example,
|
|
3959
4890
|
jsonKey: "example",
|
|
3960
4891
|
exportJsonKey: [
|
|
3961
4892
|
{
|
|
3962
4893
|
"@keyonly": {
|
|
3963
4894
|
isExample: true,
|
|
3964
|
-
example: "$parent.solutions[
|
|
4895
|
+
example: "$parent.solutions[-1]",
|
|
3965
4896
|
"@bit": { isExample: true }
|
|
3966
4897
|
}
|
|
3967
4898
|
},
|
|
3968
4899
|
{
|
|
3969
4900
|
"@absent": {
|
|
3970
4901
|
isExample: true,
|
|
3971
|
-
example: "$parent.solutions[
|
|
4902
|
+
example: "$parent.solutions[-1]",
|
|
3972
4903
|
"@bit": { isExample: true }
|
|
3973
4904
|
}
|
|
3974
4905
|
},
|
|
3975
4906
|
{ isExample: true, example: "$", "@bit": { isExample: true } }
|
|
3976
4907
|
],
|
|
3977
4908
|
description: "An example for the gap",
|
|
3978
|
-
format: TagFormat.bitmarkText
|
|
4909
|
+
format: TagFormat.bitmarkText,
|
|
4910
|
+
nullable: true
|
|
3979
4911
|
},
|
|
3980
4912
|
{
|
|
3981
4913
|
key: ConfigKey.property_isCaseSensitive,
|
|
@@ -4024,6 +4956,99 @@ var GROUPS = {
|
|
|
4024
4956
|
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
4025
4957
|
},
|
|
4026
4958
|
{
|
|
4959
|
+
// PLAN-072 + ALIGN-EXAMPLE-CASCADE §3.2: cascade-fired
|
|
4960
|
+
// `@example` from the bit header marks EVERY choice with
|
|
4961
|
+
// `isExample: true` (matches BPG output), but only the
|
|
4962
|
+
// FIRST correct choice gets `example: true` (BPG
|
|
4963
|
+
// `fillBooleanExample(..., firstCorrectOnly=true)`).
|
|
4964
|
+
//
|
|
4965
|
+
// Two `@absent` rules, picked first-match-wins:
|
|
4966
|
+
// 1. Verbose form gated on `@parent.isCorrect=true` with
|
|
4967
|
+
// `maxEmits:1` — fires once for the first correct.
|
|
4968
|
+
// 2. Plain `@absent` — fires on every remaining entry
|
|
4969
|
+
// (subsequent corrects and all incorrects), emitting
|
|
4970
|
+
// `isExample` only.
|
|
4971
|
+
// The `serialize_cards` cascade-counter scope holds the cap
|
|
4972
|
+
// across the whole cardset.
|
|
4973
|
+
// @card scope-shift writes choice-container isExample (e.g.
|
|
4974
|
+
// `quizzes[i].isExample`) — was previously provided by the
|
|
4975
|
+
// universal `bubble_is_example` post-walk, which was removed
|
|
4976
|
+
// in favour of explicit per-tag config. SYNTAX.md §8.
|
|
4977
|
+
key: ConfigKey.property_example,
|
|
4978
|
+
jsonKey: "example",
|
|
4979
|
+
exportJsonKey: [
|
|
4980
|
+
{
|
|
4981
|
+
"@keyonly": {
|
|
4982
|
+
isExample: true,
|
|
4983
|
+
example: "$parent.isCorrect",
|
|
4984
|
+
"@bit": { isExample: true },
|
|
4985
|
+
"@card": { isExample: true }
|
|
4986
|
+
}
|
|
4987
|
+
},
|
|
4988
|
+
{
|
|
4989
|
+
predicates: ["@absent", { "@parent.isCorrect": true }],
|
|
4990
|
+
maxEmits: 1,
|
|
4991
|
+
rule: {
|
|
4992
|
+
isExample: true,
|
|
4993
|
+
example: true,
|
|
4994
|
+
"@bit": { isExample: true },
|
|
4995
|
+
"@card": { isExample: true }
|
|
4996
|
+
}
|
|
4997
|
+
},
|
|
4998
|
+
{
|
|
4999
|
+
"@absent": {
|
|
5000
|
+
isExample: true,
|
|
5001
|
+
"@bit": { isExample: true },
|
|
5002
|
+
"@card": { isExample: true }
|
|
5003
|
+
}
|
|
5004
|
+
},
|
|
5005
|
+
{
|
|
5006
|
+
isExample: true,
|
|
5007
|
+
example: "$",
|
|
5008
|
+
"@bit": { isExample: true },
|
|
5009
|
+
"@card": { isExample: true }
|
|
5010
|
+
}
|
|
5011
|
+
],
|
|
5012
|
+
description: "An example for the true/false statement/question",
|
|
5013
|
+
format: TagFormat.boolean,
|
|
5014
|
+
nullable: true
|
|
5015
|
+
}
|
|
5016
|
+
]
|
|
5017
|
+
},
|
|
5018
|
+
{
|
|
5019
|
+
exportJsonKey: { choices: [{ choice: "$", isCorrect: false }] },
|
|
5020
|
+
key: ConfigKey.tag_false,
|
|
5021
|
+
description: "False value for a true/false statement/question",
|
|
5022
|
+
maxCount: Count.infinity,
|
|
5023
|
+
format: TagFormat.plainText,
|
|
5024
|
+
chain: [
|
|
5025
|
+
{
|
|
5026
|
+
key: ConfigKey.tag_true,
|
|
5027
|
+
jsonKey: "choices[]|set(isCorrect=true)",
|
|
5028
|
+
exportJsonKey: { choices: [{ choice: "$", isCorrect: true }] },
|
|
5029
|
+
description: "True values for a true/false statement/question",
|
|
5030
|
+
maxCount: Count.infinity,
|
|
5031
|
+
format: TagFormat.plainText
|
|
5032
|
+
},
|
|
5033
|
+
{
|
|
5034
|
+
key: ConfigKey.tag_false,
|
|
5035
|
+
jsonKey: "choices[]|set(isCorrect=false)",
|
|
5036
|
+
exportJsonKey: { choices: [{ choice: "$", isCorrect: false }] },
|
|
5037
|
+
description: "False values for a true/false statement/question",
|
|
5038
|
+
maxCount: Count.infinity,
|
|
5039
|
+
format: TagFormat.plainText
|
|
5040
|
+
},
|
|
5041
|
+
{
|
|
5042
|
+
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
5043
|
+
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
5044
|
+
},
|
|
5045
|
+
{
|
|
5046
|
+
// PLAN-072 + ALIGN-EXAMPLE-CASCADE §3.2: cascade-fired
|
|
5047
|
+
// `@example` on a `-` (incorrect choice) emits
|
|
5048
|
+
// `isExample: true` only — no `example` (BPG fixtures show
|
|
5049
|
+
// `isExample` on every choice, but `example` only on the
|
|
5050
|
+
// first correct via firstCorrectOnly cascade).
|
|
5051
|
+
// @card scope-shift writes choice-container isExample.
|
|
4027
5052
|
key: ConfigKey.property_example,
|
|
4028
5053
|
jsonKey: "example",
|
|
4029
5054
|
exportJsonKey: [
|
|
@@ -4031,25 +5056,99 @@ var GROUPS = {
|
|
|
4031
5056
|
"@keyonly": {
|
|
4032
5057
|
isExample: true,
|
|
4033
5058
|
example: "$parent.isCorrect",
|
|
4034
|
-
"@bit": { isExample: true }
|
|
5059
|
+
"@bit": { isExample: true },
|
|
5060
|
+
"@card": { isExample: true }
|
|
4035
5061
|
}
|
|
4036
5062
|
},
|
|
4037
5063
|
{
|
|
4038
5064
|
"@absent": {
|
|
5065
|
+
isExample: true,
|
|
5066
|
+
"@bit": { isExample: true },
|
|
5067
|
+
"@card": { isExample: true }
|
|
5068
|
+
}
|
|
5069
|
+
},
|
|
5070
|
+
{
|
|
5071
|
+
isExample: true,
|
|
5072
|
+
example: "$",
|
|
5073
|
+
"@bit": { isExample: true },
|
|
5074
|
+
"@card": { isExample: true }
|
|
5075
|
+
}
|
|
5076
|
+
],
|
|
5077
|
+
description: "An example for the true/false statement/question (incorrect entry \u2014 isExample only on cascade)",
|
|
5078
|
+
format: TagFormat.boolean,
|
|
5079
|
+
nullable: true
|
|
5080
|
+
}
|
|
5081
|
+
]
|
|
5082
|
+
}
|
|
5083
|
+
]
|
|
5084
|
+
},
|
|
5085
|
+
[ConfigKey.group_trueFalseResponses]: {
|
|
5086
|
+
type: GroupConfigType.standard,
|
|
5087
|
+
description: "True/False Responses chain",
|
|
5088
|
+
tags: [
|
|
5089
|
+
{
|
|
5090
|
+
exportJsonKey: { responses: [{ response: "$", isCorrect: true }] },
|
|
5091
|
+
key: ConfigKey.tag_true,
|
|
5092
|
+
description: "True value for a true/false statement/question",
|
|
5093
|
+
maxCount: Count.infinity,
|
|
5094
|
+
format: TagFormat.plainText,
|
|
5095
|
+
chain: [
|
|
5096
|
+
{
|
|
5097
|
+
key: ConfigKey.tag_true,
|
|
5098
|
+
jsonKey: "responses[]|set(isCorrect=true)",
|
|
5099
|
+
exportJsonKey: { responses: [{ response: "$", isCorrect: true }] },
|
|
5100
|
+
description: "True values for a true/false statement/question",
|
|
5101
|
+
maxCount: Count.infinity,
|
|
5102
|
+
format: TagFormat.plainText
|
|
5103
|
+
},
|
|
5104
|
+
{
|
|
5105
|
+
key: ConfigKey.tag_false,
|
|
5106
|
+
jsonKey: "responses[]|set(isCorrect=false)",
|
|
5107
|
+
exportJsonKey: { responses: [{ response: "$", isCorrect: false }] },
|
|
5108
|
+
description: "False values for a true/false statement/question",
|
|
5109
|
+
maxCount: Count.infinity,
|
|
5110
|
+
format: TagFormat.plainText
|
|
5111
|
+
},
|
|
5112
|
+
{
|
|
5113
|
+
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
5114
|
+
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
5115
|
+
},
|
|
5116
|
+
{
|
|
5117
|
+
// @card scope-shift writes response-container isExample.
|
|
5118
|
+
key: ConfigKey.property_example,
|
|
5119
|
+
jsonKey: "example",
|
|
5120
|
+
exportJsonKey: [
|
|
5121
|
+
{
|
|
5122
|
+
"@keyonly": {
|
|
4039
5123
|
isExample: true,
|
|
4040
5124
|
example: "$parent.isCorrect",
|
|
4041
|
-
"@bit": { isExample: true }
|
|
5125
|
+
"@bit": { isExample: true },
|
|
5126
|
+
"@card": { isExample: true }
|
|
4042
5127
|
}
|
|
4043
5128
|
},
|
|
4044
|
-
{
|
|
5129
|
+
{
|
|
5130
|
+
"@absent": {
|
|
5131
|
+
isExample: "$parent.isCorrect",
|
|
5132
|
+
example: "$parent.isCorrect",
|
|
5133
|
+
"@bit": { isExample: "$parent.isCorrect" },
|
|
5134
|
+
"@card": { isExample: "$parent.isCorrect" }
|
|
5135
|
+
}
|
|
5136
|
+
},
|
|
5137
|
+
{
|
|
5138
|
+
isExample: true,
|
|
5139
|
+
example: "$",
|
|
5140
|
+
"@bit": { isExample: true },
|
|
5141
|
+
"@card": { isExample: true }
|
|
5142
|
+
}
|
|
4045
5143
|
],
|
|
4046
5144
|
description: "An example for the true/false statement/question",
|
|
4047
|
-
format: TagFormat.
|
|
5145
|
+
format: TagFormat.boolean,
|
|
5146
|
+
nullable: true
|
|
4048
5147
|
}
|
|
4049
5148
|
]
|
|
4050
5149
|
},
|
|
4051
5150
|
{
|
|
4052
|
-
exportJsonKey: {
|
|
5151
|
+
exportJsonKey: { responses: [{ response: "$", isCorrect: false }] },
|
|
4053
5152
|
key: ConfigKey.tag_false,
|
|
4054
5153
|
description: "False value for a true/false statement/question",
|
|
4055
5154
|
maxCount: Count.infinity,
|
|
@@ -4057,16 +5156,16 @@ var GROUPS = {
|
|
|
4057
5156
|
chain: [
|
|
4058
5157
|
{
|
|
4059
5158
|
key: ConfigKey.tag_true,
|
|
4060
|
-
jsonKey: "
|
|
4061
|
-
exportJsonKey: {
|
|
5159
|
+
jsonKey: "responses[]|set(isCorrect=true)",
|
|
5160
|
+
exportJsonKey: { responses: [{ response: "$", isCorrect: true }] },
|
|
4062
5161
|
description: "True values for a true/false statement/question",
|
|
4063
5162
|
maxCount: Count.infinity,
|
|
4064
5163
|
format: TagFormat.plainText
|
|
4065
5164
|
},
|
|
4066
5165
|
{
|
|
4067
5166
|
key: ConfigKey.tag_false,
|
|
4068
|
-
jsonKey: "
|
|
4069
|
-
exportJsonKey: {
|
|
5167
|
+
jsonKey: "responses[]|set(isCorrect=false)",
|
|
5168
|
+
exportJsonKey: { responses: [{ response: "$", isCorrect: false }] },
|
|
4070
5169
|
description: "False values for a true/false statement/question",
|
|
4071
5170
|
maxCount: Count.infinity,
|
|
4072
5171
|
format: TagFormat.plainText
|
|
@@ -4076,6 +5175,7 @@ var GROUPS = {
|
|
|
4076
5175
|
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
4077
5176
|
},
|
|
4078
5177
|
{
|
|
5178
|
+
// @card scope-shift writes response-container isExample.
|
|
4079
5179
|
key: ConfigKey.property_example,
|
|
4080
5180
|
jsonKey: "example",
|
|
4081
5181
|
exportJsonKey: [
|
|
@@ -4083,20 +5183,333 @@ var GROUPS = {
|
|
|
4083
5183
|
"@keyonly": {
|
|
4084
5184
|
isExample: true,
|
|
4085
5185
|
example: "$parent.isCorrect",
|
|
4086
|
-
"@bit": { isExample: true }
|
|
5186
|
+
"@bit": { isExample: true },
|
|
5187
|
+
"@card": { isExample: true }
|
|
4087
5188
|
}
|
|
4088
5189
|
},
|
|
4089
5190
|
{
|
|
4090
5191
|
"@absent": {
|
|
4091
5192
|
isExample: true,
|
|
4092
5193
|
example: "$parent.isCorrect",
|
|
4093
|
-
"@bit": { isExample: true }
|
|
5194
|
+
"@bit": { isExample: true },
|
|
5195
|
+
"@card": { isExample: true }
|
|
4094
5196
|
}
|
|
4095
5197
|
},
|
|
4096
|
-
{
|
|
5198
|
+
{
|
|
5199
|
+
isExample: true,
|
|
5200
|
+
example: "$",
|
|
5201
|
+
"@bit": { isExample: true },
|
|
5202
|
+
"@card": { isExample: true }
|
|
5203
|
+
}
|
|
4097
5204
|
],
|
|
4098
5205
|
description: "An example for the true/false statement/question",
|
|
4099
|
-
format: TagFormat.
|
|
5206
|
+
format: TagFormat.boolean,
|
|
5207
|
+
nullable: true
|
|
5208
|
+
}
|
|
5209
|
+
]
|
|
5210
|
+
}
|
|
5211
|
+
]
|
|
5212
|
+
},
|
|
5213
|
+
[ConfigKey.group_trueFalseInlineSelect]: {
|
|
5214
|
+
type: GroupConfigType.standard,
|
|
5215
|
+
description: "True/False chain composed inline (select body-bit). +/- tags are consume-only at bit level.",
|
|
5216
|
+
tags: [
|
|
5217
|
+
{
|
|
5218
|
+
exportJsonKey: {
|
|
5219
|
+
"@body-inline": {
|
|
5220
|
+
type: "select",
|
|
5221
|
+
attrs: { options: [{ text: "$", isCorrect: true }] }
|
|
5222
|
+
}
|
|
5223
|
+
},
|
|
5224
|
+
key: ConfigKey.tag_true,
|
|
5225
|
+
description: "True value for an inline select body bit",
|
|
5226
|
+
maxCount: Count.infinity,
|
|
5227
|
+
format: TagFormat.plainText,
|
|
5228
|
+
chain: [
|
|
5229
|
+
{
|
|
5230
|
+
key: ConfigKey.tag_true,
|
|
5231
|
+
exportJsonKey: {},
|
|
5232
|
+
description: "True values for an inline select body bit",
|
|
5233
|
+
maxCount: Count.infinity,
|
|
5234
|
+
format: TagFormat.plainText
|
|
5235
|
+
},
|
|
5236
|
+
{
|
|
5237
|
+
key: ConfigKey.tag_false,
|
|
5238
|
+
exportJsonKey: {},
|
|
5239
|
+
description: "False values for an inline select body bit",
|
|
5240
|
+
maxCount: Count.infinity,
|
|
5241
|
+
format: TagFormat.plainText
|
|
5242
|
+
},
|
|
5243
|
+
{
|
|
5244
|
+
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
5245
|
+
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
5246
|
+
},
|
|
5247
|
+
{
|
|
5248
|
+
// PLAN-083: cascade-fired `@example` from bit header fires
|
|
5249
|
+
// `isExample:true, example:true` on the FIRST CORRECT option
|
|
5250
|
+
// only (BPG `fillBooleanExample(select.options,
|
|
5251
|
+
// firstCorrectOnly=true)`). Mirrors group_trueFalseInlineHighlight.
|
|
5252
|
+
// Every rule also bubbles `@bit:{isExample:true}` (idempotent
|
|
5253
|
+
// OR — any cascade ⇒ bit marked) and
|
|
5254
|
+
// `@body-inline:{attrs:{isExample:true}}` (node-level marker
|
|
5255
|
+
// on the select node).
|
|
5256
|
+
key: ConfigKey.property_example,
|
|
5257
|
+
jsonKey: "example",
|
|
5258
|
+
exportJsonKey: [
|
|
5259
|
+
{
|
|
5260
|
+
"@keyonly": {
|
|
5261
|
+
isExample: true,
|
|
5262
|
+
example: "$parent.isCorrect",
|
|
5263
|
+
"@bit": { isExample: true },
|
|
5264
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5265
|
+
}
|
|
5266
|
+
},
|
|
5267
|
+
{
|
|
5268
|
+
predicates: ["@absent", { "@parent.isCorrect": true }],
|
|
5269
|
+
maxEmits: 1,
|
|
5270
|
+
rule: {
|
|
5271
|
+
isExample: true,
|
|
5272
|
+
example: true,
|
|
5273
|
+
"@bit": { isExample: true },
|
|
5274
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5275
|
+
}
|
|
5276
|
+
},
|
|
5277
|
+
{
|
|
5278
|
+
isExample: true,
|
|
5279
|
+
example: "$",
|
|
5280
|
+
"@bit": { isExample: true },
|
|
5281
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5282
|
+
}
|
|
5283
|
+
],
|
|
5284
|
+
description: "An example for the inline select option (first correct only)",
|
|
5285
|
+
format: TagFormat.boolean,
|
|
5286
|
+
nullable: true
|
|
5287
|
+
}
|
|
5288
|
+
]
|
|
5289
|
+
},
|
|
5290
|
+
{
|
|
5291
|
+
exportJsonKey: {
|
|
5292
|
+
"@body-inline": {
|
|
5293
|
+
type: "select",
|
|
5294
|
+
attrs: { options: [{ text: "$", isCorrect: false }] }
|
|
5295
|
+
}
|
|
5296
|
+
},
|
|
5297
|
+
key: ConfigKey.tag_false,
|
|
5298
|
+
description: "False value for an inline select body bit",
|
|
5299
|
+
maxCount: Count.infinity,
|
|
5300
|
+
format: TagFormat.plainText,
|
|
5301
|
+
chain: [
|
|
5302
|
+
{
|
|
5303
|
+
key: ConfigKey.tag_true,
|
|
5304
|
+
exportJsonKey: {},
|
|
5305
|
+
description: "True values for an inline select body bit",
|
|
5306
|
+
maxCount: Count.infinity,
|
|
5307
|
+
format: TagFormat.plainText
|
|
5308
|
+
},
|
|
5309
|
+
{
|
|
5310
|
+
key: ConfigKey.tag_false,
|
|
5311
|
+
exportJsonKey: {},
|
|
5312
|
+
description: "False values for an inline select body bit",
|
|
5313
|
+
maxCount: Count.infinity,
|
|
5314
|
+
format: TagFormat.plainText
|
|
5315
|
+
},
|
|
5316
|
+
{
|
|
5317
|
+
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
5318
|
+
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
5319
|
+
},
|
|
5320
|
+
{
|
|
5321
|
+
// PLAN-083: identical to the `+` lead's chain @example —
|
|
5322
|
+
// cascade reaches the first correct option regardless of
|
|
5323
|
+
// which lead (+/-) opens the chain. Every firing rule bubbles
|
|
5324
|
+
// `@bit:{isExample:true}` and `@body-inline:{attrs:{isExample:true}}`.
|
|
5325
|
+
key: ConfigKey.property_example,
|
|
5326
|
+
jsonKey: "example",
|
|
5327
|
+
exportJsonKey: [
|
|
5328
|
+
{
|
|
5329
|
+
"@keyonly": {
|
|
5330
|
+
isExample: true,
|
|
5331
|
+
example: "$parent.isCorrect",
|
|
5332
|
+
"@bit": { isExample: true },
|
|
5333
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5334
|
+
}
|
|
5335
|
+
},
|
|
5336
|
+
{
|
|
5337
|
+
predicates: ["@absent", { "@parent.isCorrect": true }],
|
|
5338
|
+
maxEmits: 1,
|
|
5339
|
+
rule: {
|
|
5340
|
+
isExample: true,
|
|
5341
|
+
example: true,
|
|
5342
|
+
"@bit": { isExample: true },
|
|
5343
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5344
|
+
}
|
|
5345
|
+
},
|
|
5346
|
+
{
|
|
5347
|
+
isExample: true,
|
|
5348
|
+
example: "$",
|
|
5349
|
+
"@bit": { isExample: true },
|
|
5350
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5351
|
+
}
|
|
5352
|
+
],
|
|
5353
|
+
description: "An example for the inline select option (first correct only)",
|
|
5354
|
+
format: TagFormat.boolean,
|
|
5355
|
+
nullable: true
|
|
5356
|
+
}
|
|
5357
|
+
]
|
|
5358
|
+
}
|
|
5359
|
+
]
|
|
5360
|
+
},
|
|
5361
|
+
[ConfigKey.group_trueFalseInlineHighlight]: {
|
|
5362
|
+
type: GroupConfigType.standard,
|
|
5363
|
+
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.',
|
|
5364
|
+
tags: [
|
|
5365
|
+
{
|
|
5366
|
+
exportJsonKey: {
|
|
5367
|
+
"@body-inline": {
|
|
5368
|
+
type: "highlight",
|
|
5369
|
+
attrs: { texts: [{ text: "$", isCorrect: true }] }
|
|
5370
|
+
}
|
|
5371
|
+
},
|
|
5372
|
+
key: ConfigKey.tag_true,
|
|
5373
|
+
description: "Highlighted span in the body (correct)",
|
|
5374
|
+
maxCount: Count.infinity,
|
|
5375
|
+
format: TagFormat.plainText,
|
|
5376
|
+
chain: [
|
|
5377
|
+
{
|
|
5378
|
+
key: ConfigKey.tag_true,
|
|
5379
|
+
exportJsonKey: {},
|
|
5380
|
+
description: "Highlighted span values (correct)",
|
|
5381
|
+
maxCount: Count.infinity,
|
|
5382
|
+
format: TagFormat.plainText
|
|
5383
|
+
},
|
|
5384
|
+
{
|
|
5385
|
+
key: ConfigKey.tag_false,
|
|
5386
|
+
exportJsonKey: {},
|
|
5387
|
+
description: "Highlighted span values (incorrect)",
|
|
5388
|
+
maxCount: Count.infinity,
|
|
5389
|
+
format: TagFormat.plainText
|
|
5390
|
+
},
|
|
5391
|
+
{
|
|
5392
|
+
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
5393
|
+
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
5394
|
+
},
|
|
5395
|
+
{
|
|
5396
|
+
// PLAN-072 + ALIGN-EXAMPLE-CASCADE §3.13: cascade-fired
|
|
5397
|
+
// `@example` from the bit header fires `example: true` +
|
|
5398
|
+
// `isExample: true` on the FIRST correct highlight only
|
|
5399
|
+
// (BPG `fillBooleanExample(highlight.texts,
|
|
5400
|
+
// firstCorrectOnly=true)`). Incorrect highlights get
|
|
5401
|
+
// nothing (unlike choices, the highlight-text fixture
|
|
5402
|
+
// shows no `isExample` on incorrect entries).
|
|
5403
|
+
key: ConfigKey.property_example,
|
|
5404
|
+
jsonKey: "example",
|
|
5405
|
+
exportJsonKey: [
|
|
5406
|
+
{
|
|
5407
|
+
"@keyonly": {
|
|
5408
|
+
isExample: true,
|
|
5409
|
+
example: "$parent.isCorrect",
|
|
5410
|
+
"@bit": { isExample: true },
|
|
5411
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5412
|
+
}
|
|
5413
|
+
},
|
|
5414
|
+
{
|
|
5415
|
+
predicates: ["@absent", { "@parent.isCorrect": true }],
|
|
5416
|
+
maxEmits: 1,
|
|
5417
|
+
rule: {
|
|
5418
|
+
isExample: true,
|
|
5419
|
+
example: true,
|
|
5420
|
+
"@bit": { isExample: true },
|
|
5421
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5422
|
+
}
|
|
5423
|
+
},
|
|
5424
|
+
{
|
|
5425
|
+
isExample: true,
|
|
5426
|
+
example: "$",
|
|
5427
|
+
"@bit": { isExample: true },
|
|
5428
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5429
|
+
}
|
|
5430
|
+
],
|
|
5431
|
+
description: "An example for the highlighted span (first correct only)",
|
|
5432
|
+
format: TagFormat.boolean,
|
|
5433
|
+
nullable: true
|
|
5434
|
+
}
|
|
5435
|
+
]
|
|
5436
|
+
},
|
|
5437
|
+
{
|
|
5438
|
+
// PLAN-083 follow-up (2026-05-22): mirror the select `-` lead shape
|
|
5439
|
+
// by writing `isCorrect: false`. Without it, `$parent.isCorrect` in
|
|
5440
|
+
// chain-child @example rules resolves to ParentPathMissing for `-`
|
|
5441
|
+
// entries (e.g. `[-fish][@example]`), so the @keyonly rule falls
|
|
5442
|
+
// through and nothing fires. The natural-default strip removes
|
|
5443
|
+
// `isCorrect: false` from the final texts entries (matches fixture).
|
|
5444
|
+
exportJsonKey: {
|
|
5445
|
+
"@body-inline": {
|
|
5446
|
+
type: "highlight",
|
|
5447
|
+
attrs: { texts: [{ text: "$", isCorrect: false }] }
|
|
5448
|
+
}
|
|
5449
|
+
},
|
|
5450
|
+
key: ConfigKey.tag_false,
|
|
5451
|
+
description: "Highlighted span in the body (incorrect)",
|
|
5452
|
+
maxCount: Count.infinity,
|
|
5453
|
+
format: TagFormat.plainText,
|
|
5454
|
+
chain: [
|
|
5455
|
+
{
|
|
5456
|
+
key: ConfigKey.tag_true,
|
|
5457
|
+
exportJsonKey: {},
|
|
5458
|
+
description: "Highlighted span values (correct)",
|
|
5459
|
+
maxCount: Count.infinity,
|
|
5460
|
+
format: TagFormat.plainText
|
|
5461
|
+
},
|
|
5462
|
+
{
|
|
5463
|
+
key: ConfigKey.tag_false,
|
|
5464
|
+
exportJsonKey: {},
|
|
5465
|
+
description: "Highlighted span values (incorrect)",
|
|
5466
|
+
maxCount: Count.infinity,
|
|
5467
|
+
format: TagFormat.plainText
|
|
5468
|
+
},
|
|
5469
|
+
{
|
|
5470
|
+
key: ConfigKey.group_standardItemLeadInstructionHint,
|
|
5471
|
+
description: "Item, lead, page number, margin number, instruction and hint tags"
|
|
5472
|
+
},
|
|
5473
|
+
{
|
|
5474
|
+
// PLAN-072 + ALIGN-EXAMPLE-CASCADE §3.13: cascade-fired
|
|
5475
|
+
// `@example` on a `-` (incorrect highlight) emits nothing
|
|
5476
|
+
// (highlight-text fixture has no `isExample` on incorrect
|
|
5477
|
+
// entries; firstCorrectOnly cascade skips them entirely).
|
|
5478
|
+
// No `@absent` rule.
|
|
5479
|
+
// PLAN-083: identical to the `+` lead's chain @example —
|
|
5480
|
+
// cascade reaches the first correct option regardless of
|
|
5481
|
+
// which lead (+/-) opens the chain.
|
|
5482
|
+
key: ConfigKey.property_example,
|
|
5483
|
+
jsonKey: "example",
|
|
5484
|
+
exportJsonKey: [
|
|
5485
|
+
{
|
|
5486
|
+
"@keyonly": {
|
|
5487
|
+
isExample: true,
|
|
5488
|
+
example: "$parent.isCorrect",
|
|
5489
|
+
"@bit": { isExample: true },
|
|
5490
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5491
|
+
}
|
|
5492
|
+
},
|
|
5493
|
+
{
|
|
5494
|
+
predicates: ["@absent", { "@parent.isCorrect": true }],
|
|
5495
|
+
maxEmits: 1,
|
|
5496
|
+
rule: {
|
|
5497
|
+
isExample: true,
|
|
5498
|
+
example: true,
|
|
5499
|
+
"@bit": { isExample: true },
|
|
5500
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5501
|
+
}
|
|
5502
|
+
},
|
|
5503
|
+
{
|
|
5504
|
+
isExample: true,
|
|
5505
|
+
example: "$",
|
|
5506
|
+
"@bit": { isExample: true },
|
|
5507
|
+
"@body-inline": { attrs: { isExample: true } }
|
|
5508
|
+
}
|
|
5509
|
+
],
|
|
5510
|
+
description: "An example for the highlighted span (first correct only)",
|
|
5511
|
+
format: TagFormat.boolean,
|
|
5512
|
+
nullable: true
|
|
4100
5513
|
}
|
|
4101
5514
|
]
|
|
4102
5515
|
}
|
|
@@ -4109,7 +5522,11 @@ var GROUPS = {
|
|
|
4109
5522
|
{
|
|
4110
5523
|
key: ConfigKey.property_mark,
|
|
4111
5524
|
jsonKey: "marks.mark",
|
|
4112
|
-
|
|
5525
|
+
// `emphasis: 'underline'` is the per-entry default for mark
|
|
5526
|
+
// configs — mirrors the hardcoded default in BPG
|
|
5527
|
+
// `MarkConfigChainContentProcessor.ts:55-58`. Chained
|
|
5528
|
+
// `[@emphasis:…]` overrides via deep-merge into `marks[-1]`.
|
|
5529
|
+
exportJsonKey: { marks: [{ mark: "$", emphasis: "underline" }] },
|
|
4113
5530
|
description: "The mark configuration",
|
|
4114
5531
|
format: TagFormat.plainText,
|
|
4115
5532
|
maxCount: Count.infinity,
|
|
@@ -4133,7 +5550,9 @@ var GROUPS = {
|
|
|
4133
5550
|
description: "Mark chain",
|
|
4134
5551
|
tags: [
|
|
4135
5552
|
{
|
|
4136
|
-
exportJsonKey: {
|
|
5553
|
+
exportJsonKey: {
|
|
5554
|
+
"@body-inline": { type: "mark", attrs: { solution: "$" } }
|
|
5555
|
+
},
|
|
4137
5556
|
key: ConfigKey.tag_mark,
|
|
4138
5557
|
description: "The marked content",
|
|
4139
5558
|
maxCount: Count.infinity,
|
|
@@ -4142,19 +5561,30 @@ var GROUPS = {
|
|
|
4142
5561
|
{
|
|
4143
5562
|
key: ConfigKey.property_mark,
|
|
4144
5563
|
jsonKey: "mark",
|
|
5564
|
+
exportJsonKey: { mark: "$" },
|
|
4145
5565
|
description: "The mark configuration",
|
|
4146
5566
|
format: TagFormat.plainText
|
|
4147
5567
|
},
|
|
4148
5568
|
{
|
|
4149
5569
|
key: ConfigKey.property_example,
|
|
4150
5570
|
jsonKey: "example",
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
5571
|
+
// Per-mark `@example` is a boolean toggle. BPG's runtime
|
|
5572
|
+
// `ExampleTagContentProcessor.handleDefaultOnlyExample`
|
|
5573
|
+
// (lines 147-161) normalises ANY `[@example]` or
|
|
5574
|
+
// `[@example:V]` on a mark body-bit to
|
|
5575
|
+
// `__isDefaultExample=true, example=undefined`; the
|
|
5576
|
+
// downstream `toExample(_, _, defaultExample=true)` in
|
|
5577
|
+
// `BaseBuilder.ts:80-87` then emits `example: true`
|
|
5578
|
+
// regardless of source. Declare a single unconditional
|
|
5579
|
+
// boolean-shape rule so the exported schema matches that
|
|
5580
|
+
// runtime semantic instead of the misleading `example: $`
|
|
5581
|
+
// shape (which would only fire if `handleDefaultOnlyExample`
|
|
5582
|
+
// were removed). `@bit: {isExample: true}` cascades the
|
|
5583
|
+
// marker — but NOT the `example` value — to bit root.
|
|
5584
|
+
exportJsonKey: { isExample: true, example: true, "@bit": { isExample: true } },
|
|
4156
5585
|
description: "An example for the marked content",
|
|
4157
|
-
format: TagFormat.
|
|
5586
|
+
format: TagFormat.boolean,
|
|
5587
|
+
nullable: true
|
|
4158
5588
|
}
|
|
4159
5589
|
]
|
|
4160
5590
|
}
|
|
@@ -4583,6 +6013,42 @@ var GROUPS = {
|
|
|
4583
6013
|
}
|
|
4584
6014
|
]
|
|
4585
6015
|
},
|
|
6016
|
+
[ConfigKey.group_backgroundWallpaper]: {
|
|
6017
|
+
type: GroupConfigType.standard,
|
|
6018
|
+
description: "Background wallpaper tags",
|
|
6019
|
+
tags: [
|
|
6020
|
+
{
|
|
6021
|
+
key: ConfigKey.resource_backgroundWallpaper,
|
|
6022
|
+
exportJsonKey: { backgroundWallpaper: { type: "image", image: { src: "$" } } },
|
|
6023
|
+
description: "Background wallpaper for the image, used to set a background for the image",
|
|
6024
|
+
chain: [
|
|
6025
|
+
{
|
|
6026
|
+
key: ConfigKey.group_resourceImageCommon,
|
|
6027
|
+
description: "Common resource image tags for images"
|
|
6028
|
+
}
|
|
6029
|
+
]
|
|
6030
|
+
}
|
|
6031
|
+
]
|
|
6032
|
+
},
|
|
6033
|
+
[ConfigKey.group_imageNoZoom]: {
|
|
6034
|
+
type: GroupConfigType.standard,
|
|
6035
|
+
description: "Image bit tags",
|
|
6036
|
+
tags: [
|
|
6037
|
+
{
|
|
6038
|
+
key: ConfigKey.group_backgroundWallpaper,
|
|
6039
|
+
description: "Background wallpaper tags for images, used to define background properties for images"
|
|
6040
|
+
},
|
|
6041
|
+
{
|
|
6042
|
+
key: ConfigKey.group_resourceBitTags,
|
|
6043
|
+
description: "Resource bit tags for images, used to define additional properties for images"
|
|
6044
|
+
},
|
|
6045
|
+
{
|
|
6046
|
+
key: ConfigKey.group_resourceImageNoZoom,
|
|
6047
|
+
description: "Resource image tags for images, used to attach images to the bit",
|
|
6048
|
+
minCount: 1
|
|
6049
|
+
}
|
|
6050
|
+
]
|
|
6051
|
+
},
|
|
4586
6052
|
//
|
|
4587
6053
|
// Resource groups
|
|
4588
6054
|
//
|
|
@@ -4702,6 +6168,58 @@ var GROUPS = {
|
|
|
4702
6168
|
}
|
|
4703
6169
|
]
|
|
4704
6170
|
},
|
|
6171
|
+
[ConfigKey.group_resourceImageCommonNoZoom]: {
|
|
6172
|
+
type: GroupConfigType.standard,
|
|
6173
|
+
description: "Common properties for image resources where @zoomDisabled defaults to true",
|
|
6174
|
+
tags: [
|
|
6175
|
+
{
|
|
6176
|
+
key: ConfigKey.group_resourceCommon,
|
|
6177
|
+
description: "Common resource properties"
|
|
6178
|
+
},
|
|
6179
|
+
{
|
|
6180
|
+
key: ConfigKey.property_src1x,
|
|
6181
|
+
description: "The source URL for the image at 1x resolution",
|
|
6182
|
+
format: TagFormat.plainText
|
|
6183
|
+
},
|
|
6184
|
+
{
|
|
6185
|
+
key: ConfigKey.property_src2x,
|
|
6186
|
+
description: "The source URL for the image at 2x resolution",
|
|
6187
|
+
format: TagFormat.plainText
|
|
6188
|
+
},
|
|
6189
|
+
{
|
|
6190
|
+
key: ConfigKey.property_src3x,
|
|
6191
|
+
description: "The source URL for the image at 3x resolution",
|
|
6192
|
+
format: TagFormat.plainText
|
|
6193
|
+
},
|
|
6194
|
+
{
|
|
6195
|
+
key: ConfigKey.property_src4x,
|
|
6196
|
+
description: "The source URL for the image at 4x resolution",
|
|
6197
|
+
format: TagFormat.plainText
|
|
6198
|
+
},
|
|
6199
|
+
{
|
|
6200
|
+
key: ConfigKey.property_width,
|
|
6201
|
+
description: "The width of the image",
|
|
6202
|
+
format: TagFormat.number
|
|
6203
|
+
},
|
|
6204
|
+
{
|
|
6205
|
+
key: ConfigKey.property_height,
|
|
6206
|
+
description: "The height of the image",
|
|
6207
|
+
format: TagFormat.number
|
|
6208
|
+
},
|
|
6209
|
+
{
|
|
6210
|
+
key: ConfigKey.property_alt,
|
|
6211
|
+
description: "The alternative text for the image",
|
|
6212
|
+
format: TagFormat.plainText
|
|
6213
|
+
},
|
|
6214
|
+
// @zoomDisabled with a true default
|
|
6215
|
+
{
|
|
6216
|
+
key: ConfigKey.property_zoomDisabled,
|
|
6217
|
+
description: "If true, zooming is disabled for the image (defaults to true)",
|
|
6218
|
+
format: TagFormat.boolean,
|
|
6219
|
+
defaultValue: "true"
|
|
6220
|
+
}
|
|
6221
|
+
]
|
|
6222
|
+
},
|
|
4705
6223
|
[ConfigKey.group_resourceAudioCommon]: {
|
|
4706
6224
|
type: GroupConfigType.standard,
|
|
4707
6225
|
description: "Common properties for audio resources",
|
|
@@ -4791,22 +6309,26 @@ var GROUPS = {
|
|
|
4791
6309
|
{
|
|
4792
6310
|
key: ConfigKey.property_src1x,
|
|
4793
6311
|
description: "The source URL for the video at 1x resolution",
|
|
4794
|
-
format: TagFormat.plainText
|
|
6312
|
+
format: TagFormat.plainText,
|
|
6313
|
+
exportJsonKey: { thumbnails: [{ src: "$" }] }
|
|
4795
6314
|
},
|
|
4796
6315
|
{
|
|
4797
6316
|
key: ConfigKey.property_src2x,
|
|
4798
6317
|
description: "The source URL for the video at 2x resolution",
|
|
4799
|
-
format: TagFormat.plainText
|
|
6318
|
+
format: TagFormat.plainText,
|
|
6319
|
+
exportJsonKey: { thumbnails: [{ src: "$" }] }
|
|
4800
6320
|
},
|
|
4801
6321
|
{
|
|
4802
6322
|
key: ConfigKey.property_src3x,
|
|
4803
6323
|
description: "The source URL for the video at 3x resolution",
|
|
4804
|
-
format: TagFormat.plainText
|
|
6324
|
+
format: TagFormat.plainText,
|
|
6325
|
+
exportJsonKey: { thumbnails: [{ src: "$" }] }
|
|
4805
6326
|
},
|
|
4806
6327
|
{
|
|
4807
6328
|
key: ConfigKey.property_src4x,
|
|
4808
6329
|
description: "The source URL for the video at 4x resolution",
|
|
4809
|
-
format: TagFormat.plainText
|
|
6330
|
+
format: TagFormat.plainText,
|
|
6331
|
+
exportJsonKey: { thumbnails: [{ src: "$" }] }
|
|
4810
6332
|
}
|
|
4811
6333
|
]
|
|
4812
6334
|
},
|
|
@@ -4819,6 +6341,7 @@ var GROUPS = {
|
|
|
4819
6341
|
tags: [
|
|
4820
6342
|
{
|
|
4821
6343
|
key: ConfigKey.resource_icon,
|
|
6344
|
+
exportJsonKey: { resource: { type: "icon", icon: { src: "$" } } },
|
|
4822
6345
|
description: "The icon resource",
|
|
4823
6346
|
chain: [
|
|
4824
6347
|
{
|
|
@@ -4846,12 +6369,30 @@ var GROUPS = {
|
|
|
4846
6369
|
}
|
|
4847
6370
|
]
|
|
4848
6371
|
},
|
|
6372
|
+
[ConfigKey.group_resourceImageNoZoom]: {
|
|
6373
|
+
type: GroupConfigType.resource,
|
|
6374
|
+
description: "Image resource where @zoomDisabled defaults to true",
|
|
6375
|
+
tags: [
|
|
6376
|
+
{
|
|
6377
|
+
key: ConfigKey.resource_image,
|
|
6378
|
+
exportJsonKey: { resource: { type: "image", image: { src: "$" } } },
|
|
6379
|
+
description: "The image resource (no-zoom default)",
|
|
6380
|
+
chain: [
|
|
6381
|
+
{
|
|
6382
|
+
key: ConfigKey.group_resourceImageCommonNoZoom,
|
|
6383
|
+
description: "Common image properties with @zoomDisabled defaulting to true"
|
|
6384
|
+
}
|
|
6385
|
+
]
|
|
6386
|
+
}
|
|
6387
|
+
]
|
|
6388
|
+
},
|
|
4849
6389
|
[ConfigKey.group_resourceImagePortrait]: {
|
|
4850
6390
|
type: GroupConfigType.resource,
|
|
4851
6391
|
description: "Portrait image resource",
|
|
4852
6392
|
tags: [
|
|
4853
6393
|
{
|
|
4854
6394
|
key: ConfigKey.resource_imagePortrait,
|
|
6395
|
+
exportJsonKey: { resource: { type: "image-portrait", imagePortrait: { src: "$" } } },
|
|
4855
6396
|
description: "The portrait image resource",
|
|
4856
6397
|
chain: [
|
|
4857
6398
|
{
|
|
@@ -4868,6 +6409,7 @@ var GROUPS = {
|
|
|
4868
6409
|
tags: [
|
|
4869
6410
|
{
|
|
4870
6411
|
key: ConfigKey.resource_imageLandscape,
|
|
6412
|
+
exportJsonKey: { resource: { type: "image-landscape", imageLandscape: { src: "$" } } },
|
|
4871
6413
|
description: "The landscape image resource",
|
|
4872
6414
|
chain: [
|
|
4873
6415
|
{
|
|
@@ -4884,6 +6426,7 @@ var GROUPS = {
|
|
|
4884
6426
|
tags: [
|
|
4885
6427
|
{
|
|
4886
6428
|
key: ConfigKey.resource_imageEmbed,
|
|
6429
|
+
exportJsonKey: { resource: { type: "image-embed", imageEmbed: { src: "$" } } },
|
|
4887
6430
|
description: "The embedded image resource",
|
|
4888
6431
|
chain: [
|
|
4889
6432
|
{
|
|
@@ -5057,6 +6600,7 @@ var GROUPS = {
|
|
|
5057
6600
|
tags: [
|
|
5058
6601
|
{
|
|
5059
6602
|
key: ConfigKey.resource_article,
|
|
6603
|
+
exportJsonKey: { resource: { type: "article", article: { body: "$" } } },
|
|
5060
6604
|
description: "The article resource",
|
|
5061
6605
|
chain: [
|
|
5062
6606
|
{
|
|
@@ -5073,6 +6617,7 @@ var GROUPS = {
|
|
|
5073
6617
|
tags: [
|
|
5074
6618
|
{
|
|
5075
6619
|
key: ConfigKey.resource_articleEmbed,
|
|
6620
|
+
exportJsonKey: { resource: { type: "article-embed", articleEmbed: { url: "$" } } },
|
|
5076
6621
|
description: "The embedded article resource",
|
|
5077
6622
|
chain: [
|
|
5078
6623
|
{
|
|
@@ -5089,6 +6634,7 @@ var GROUPS = {
|
|
|
5089
6634
|
tags: [
|
|
5090
6635
|
{
|
|
5091
6636
|
key: ConfigKey.resource_articleLink,
|
|
6637
|
+
exportJsonKey: { resource: { type: "article-link", articleLink: { url: "$" } } },
|
|
5092
6638
|
description: "The link to the article resource",
|
|
5093
6639
|
chain: [
|
|
5094
6640
|
{
|
|
@@ -5359,7 +6905,12 @@ var ConfigHydrator = class {
|
|
|
5359
6905
|
isDefault: v.isDefault,
|
|
5360
6906
|
sideJsonKey: v.sideJsonKey,
|
|
5361
6907
|
sideExportJsonKey: v.sideExportJsonKey,
|
|
5362
|
-
hasSideExportJsonKey: Object.prototype.hasOwnProperty.call(v, "sideExportJsonKey")
|
|
6908
|
+
hasSideExportJsonKey: Object.prototype.hasOwnProperty.call(v, "sideExportJsonKey"),
|
|
6909
|
+
// PLAN-085: cardinality fields pass through; defaults applied
|
|
6910
|
+
// downstream by the config consumer (Rust treats `0` / undefined
|
|
6911
|
+
// as unbounded).
|
|
6912
|
+
minCount: v.minCount,
|
|
6913
|
+
maxCount: v.maxCount
|
|
5363
6914
|
};
|
|
5364
6915
|
}
|
|
5365
6916
|
return out;
|
|
@@ -5574,7 +7125,8 @@ var BITS = {
|
|
|
5574
7125
|
{ isExample: true, example: "$" }
|
|
5575
7126
|
],
|
|
5576
7127
|
description: "The example text for the bit",
|
|
5577
|
-
format: TagFormat.plainText
|
|
7128
|
+
format: TagFormat.plainText,
|
|
7129
|
+
nullable: true
|
|
5578
7130
|
}
|
|
5579
7131
|
],
|
|
5580
7132
|
rootExampleType: ExampleType.string
|
|
@@ -5913,7 +7465,8 @@ var BITS = {
|
|
|
5913
7465
|
{ isExample: true, example: "$" }
|
|
5914
7466
|
],
|
|
5915
7467
|
description: "The example text for the bit",
|
|
5916
|
-
format: TagFormat.plainText
|
|
7468
|
+
format: TagFormat.plainText,
|
|
7469
|
+
nullable: true
|
|
5917
7470
|
}
|
|
5918
7471
|
],
|
|
5919
7472
|
rootExampleType: ExampleType.string
|
|
@@ -6076,7 +7629,8 @@ var BITS = {
|
|
|
6076
7629
|
{ isExample: true, example: "$" }
|
|
6077
7630
|
],
|
|
6078
7631
|
description: "The example text for the bit",
|
|
6079
|
-
format: TagFormat.plainText
|
|
7632
|
+
format: TagFormat.plainText,
|
|
7633
|
+
nullable: true
|
|
6080
7634
|
}
|
|
6081
7635
|
],
|
|
6082
7636
|
rootExampleType: ExampleType.string
|
|
@@ -6227,7 +7781,8 @@ var BITS = {
|
|
|
6227
7781
|
{ isExample: true, example: "$" }
|
|
6228
7782
|
],
|
|
6229
7783
|
description: "The example text for the bit",
|
|
6230
|
-
format: TagFormat.plainText
|
|
7784
|
+
format: TagFormat.plainText,
|
|
7785
|
+
nullable: true
|
|
6231
7786
|
}
|
|
6232
7787
|
],
|
|
6233
7788
|
rootExampleType: ExampleType.string
|
|
@@ -6804,6 +8359,12 @@ var BITS = {
|
|
|
6804
8359
|
description: "The progress of the chapter, used for tracking reading progress",
|
|
6805
8360
|
format: TagFormat.boolean,
|
|
6806
8361
|
defaultValue: "true"
|
|
8362
|
+
},
|
|
8363
|
+
{
|
|
8364
|
+
key: ConfigKey.property_isCollapsible,
|
|
8365
|
+
description: "If true, the chapter is collapsible",
|
|
8366
|
+
format: TagFormat.boolean,
|
|
8367
|
+
defaultValue: "false"
|
|
6807
8368
|
}
|
|
6808
8369
|
]
|
|
6809
8370
|
},
|
|
@@ -6822,8 +8383,31 @@ var BITS = {
|
|
|
6822
8383
|
description: "Tags for gaps in cloze and multiple choice text bits"
|
|
6823
8384
|
},
|
|
6824
8385
|
{
|
|
6825
|
-
key: ConfigKey.
|
|
8386
|
+
key: ConfigKey.group_trueFalseInlineSelect,
|
|
6826
8387
|
description: "Tags for true/false questions in cloze and multiple choice text bits"
|
|
8388
|
+
},
|
|
8389
|
+
{
|
|
8390
|
+
// Bit-level @isCaseSensitive — empty exportJsonKey (no bit-level emission);
|
|
8391
|
+
// defaultValue 'true' phantom-fires onto the ancestor stack so the gap chain's
|
|
8392
|
+
// `@absent: $ancestor` rule resolves to true. Mirrors BPG Builder.ts:1643-1652
|
|
8393
|
+
// `pushDownTree(... 'isCaseSensitive', data.isCaseSensitive ?? true)` for cloze bits.
|
|
8394
|
+
key: ConfigKey.property_isCaseSensitive,
|
|
8395
|
+
exportJsonKey: {},
|
|
8396
|
+
description: "If the cloze answers are case sensitive",
|
|
8397
|
+
format: TagFormat.boolean,
|
|
8398
|
+
defaultValue: "true"
|
|
8399
|
+
},
|
|
8400
|
+
{
|
|
8401
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
8402
|
+
key: ConfigKey.property_example,
|
|
8403
|
+
exportJsonKey: [
|
|
8404
|
+
{ "@keyonly": { isExample: true } },
|
|
8405
|
+
{ "@absent": { isExample: true } },
|
|
8406
|
+
{ isExample: true, example: "$" }
|
|
8407
|
+
],
|
|
8408
|
+
description: "The example(s) for the bit",
|
|
8409
|
+
format: TagFormat.bitmarkText,
|
|
8410
|
+
nullable: true
|
|
6827
8411
|
}
|
|
6828
8412
|
]
|
|
6829
8413
|
},
|
|
@@ -6862,18 +8446,56 @@ var BITS = {
|
|
|
6862
8446
|
{
|
|
6863
8447
|
key: ConfigKey.group_gap,
|
|
6864
8448
|
description: "Tags for gaps in cloze bits"
|
|
8449
|
+
},
|
|
8450
|
+
{
|
|
8451
|
+
// Override inherited @example: cloze is in OLD parser's
|
|
8452
|
+
// `isTopLevelExample` allow-list, so [@example] at bit-header
|
|
8453
|
+
// emits `isExample: true`.
|
|
8454
|
+
key: ConfigKey.property_example,
|
|
8455
|
+
exportJsonKey: [
|
|
8456
|
+
{ "@keyonly": { isExample: true } },
|
|
8457
|
+
{ "@absent": { isExample: true } },
|
|
8458
|
+
{ isExample: true, example: "$" }
|
|
8459
|
+
],
|
|
8460
|
+
description: "The example(s) for the bit",
|
|
8461
|
+
format: TagFormat.bitmarkText,
|
|
8462
|
+
nullable: true
|
|
6865
8463
|
}
|
|
6866
8464
|
]
|
|
6867
8465
|
},
|
|
6868
8466
|
[BitType.clozeInstructionGrouped]: {
|
|
6869
8467
|
since: "1.3.0",
|
|
6870
8468
|
baseBitType: BitType.cloze,
|
|
6871
|
-
description: "Cloze instruction grouped bit, used for cloze questions with grouped instructions"
|
|
8469
|
+
description: "Cloze instruction grouped bit, used for cloze questions with grouped instructions",
|
|
8470
|
+
tags: [
|
|
8471
|
+
{
|
|
8472
|
+
key: ConfigKey.property_quizCountItems,
|
|
8473
|
+
description: "The number of items in the cloze quiz (instruction-grouped default: true)",
|
|
8474
|
+
format: TagFormat.boolean,
|
|
8475
|
+
defaultValue: "true"
|
|
8476
|
+
}
|
|
8477
|
+
// Note: quizStrikethroughSolutions defaults to false for instruction-grouped per OLD
|
|
8478
|
+
// JsonGenerator.ts:1689-1690, which equals the Boolean natural default → no override needed.
|
|
8479
|
+
]
|
|
6872
8480
|
},
|
|
6873
8481
|
[BitType.clozeSolutionGrouped]: {
|
|
6874
8482
|
since: "1.3.0",
|
|
6875
8483
|
baseBitType: BitType.cloze,
|
|
6876
|
-
description: "Cloze solution grouped bit, used for cloze questions with grouped solutions"
|
|
8484
|
+
description: "Cloze solution grouped bit, used for cloze questions with grouped solutions",
|
|
8485
|
+
tags: [
|
|
8486
|
+
{
|
|
8487
|
+
key: ConfigKey.property_quizCountItems,
|
|
8488
|
+
description: "The number of items in the cloze quiz (solution-grouped default: true)",
|
|
8489
|
+
format: TagFormat.boolean,
|
|
8490
|
+
defaultValue: "true"
|
|
8491
|
+
},
|
|
8492
|
+
{
|
|
8493
|
+
key: ConfigKey.property_quizStrikethroughSolutions,
|
|
8494
|
+
description: "If the cloze solutions should be strikethrough (solution-grouped default: true)",
|
|
8495
|
+
format: TagFormat.boolean,
|
|
8496
|
+
defaultValue: "true"
|
|
8497
|
+
}
|
|
8498
|
+
]
|
|
6877
8499
|
},
|
|
6878
8500
|
[BitType.clozeSeveral]: {
|
|
6879
8501
|
since: "3.5.0",
|
|
@@ -6909,6 +8531,17 @@ var BITS = {
|
|
|
6909
8531
|
{
|
|
6910
8532
|
key: ConfigKey.group_quizCommon,
|
|
6911
8533
|
description: "Common tags for quiz bits"
|
|
8534
|
+
},
|
|
8535
|
+
{
|
|
8536
|
+
// PLAN-084 (R12): bit-level @isCaseSensitive — empty exportJsonKey
|
|
8537
|
+
// (no bit-level emission); defaultValue 'true' phantom-fires onto
|
|
8538
|
+
// the ancestor stack so each listItem's gap chain `@absent: $ancestor`
|
|
8539
|
+
// rule resolves to true. Mirrors the cloze bit (bits.ts:1326-1336).
|
|
8540
|
+
key: ConfigKey.property_isCaseSensitive,
|
|
8541
|
+
exportJsonKey: {},
|
|
8542
|
+
description: "If the cloze answers are case sensitive",
|
|
8543
|
+
format: TagFormat.boolean,
|
|
8544
|
+
defaultValue: "true"
|
|
6912
8545
|
}
|
|
6913
8546
|
],
|
|
6914
8547
|
cardSet: CardSetConfigKey.clozeList
|
|
@@ -7075,6 +8708,7 @@ var BITS = {
|
|
|
7075
8708
|
tags: [
|
|
7076
8709
|
{
|
|
7077
8710
|
key: ConfigKey.group_person,
|
|
8711
|
+
exportJsonKey: { partner: { name: "$" } },
|
|
7078
8712
|
description: "Tags for the person in the conversation"
|
|
7079
8713
|
}
|
|
7080
8714
|
]
|
|
@@ -7505,7 +9139,12 @@ var BITS = {
|
|
|
7505
9139
|
maxCount: Count.infinity
|
|
7506
9140
|
},
|
|
7507
9141
|
{
|
|
7508
|
-
// Override inherited @example from group_standardTags:
|
|
9142
|
+
// Override inherited @example from group_standardTags: emits both
|
|
9143
|
+
// `isExample` (bool) AND `example` (paragraph array). Declared as
|
|
9144
|
+
// bitmarkText because BPG `JsonGenerator.toExample` renders the
|
|
9145
|
+
// root-example value through the bit's textFormat (not this tag's
|
|
9146
|
+
// declared format) — `TagFormat.plainText` here would be ignored
|
|
9147
|
+
// at runtime and is misleading.
|
|
7509
9148
|
key: ConfigKey.property_example,
|
|
7510
9149
|
exportJsonKey: [
|
|
7511
9150
|
{ "@keyonly": { isExample: true, example: "true" } },
|
|
@@ -7513,7 +9152,8 @@ var BITS = {
|
|
|
7513
9152
|
{ isExample: true, example: "$" }
|
|
7514
9153
|
],
|
|
7515
9154
|
description: "The example text for the essay bit",
|
|
7516
|
-
format: TagFormat.
|
|
9155
|
+
format: TagFormat.bitmarkText,
|
|
9156
|
+
nullable: true
|
|
7517
9157
|
}
|
|
7518
9158
|
],
|
|
7519
9159
|
rootExampleType: ExampleType.string
|
|
@@ -7547,7 +9187,8 @@ var BITS = {
|
|
|
7547
9187
|
{ isExample: true, example: "$" }
|
|
7548
9188
|
],
|
|
7549
9189
|
description: "The example text for the example bit",
|
|
7550
|
-
format: TagFormat.plainText
|
|
9190
|
+
format: TagFormat.plainText,
|
|
9191
|
+
nullable: true
|
|
7551
9192
|
}
|
|
7552
9193
|
],
|
|
7553
9194
|
rootExampleType: ExampleType.string
|
|
@@ -7633,7 +9274,19 @@ var BITS = {
|
|
|
7633
9274
|
[BitType.articleAi]: {
|
|
7634
9275
|
since: "1.3.0",
|
|
7635
9276
|
baseBitType: BitType.article,
|
|
7636
|
-
description: "Article AI bit, used to create AI-generated articles"
|
|
9277
|
+
description: "Article AI bit, used to create AI-generated articles",
|
|
9278
|
+
tags: [
|
|
9279
|
+
{
|
|
9280
|
+
// Shadows the inherited @aiGenerated from the standard group: this
|
|
9281
|
+
// bit type forces aiGenerated:true regardless of source value or
|
|
9282
|
+
// absence. Literal `true` in the export pattern ignores `$`.
|
|
9283
|
+
key: ConfigKey.property_aiGenerated,
|
|
9284
|
+
description: "AI-generated flag, forced true for article-ai bits",
|
|
9285
|
+
format: TagFormat.boolean,
|
|
9286
|
+
defaultValue: "true",
|
|
9287
|
+
exportJsonKey: { aiGenerated: true }
|
|
9288
|
+
}
|
|
9289
|
+
]
|
|
7637
9290
|
},
|
|
7638
9291
|
[BitType.articleAttachment]: {
|
|
7639
9292
|
since: "1.3.0",
|
|
@@ -7958,7 +9611,19 @@ var BITS = {
|
|
|
7958
9611
|
[BitType.noteAi]: {
|
|
7959
9612
|
since: "1.3.0",
|
|
7960
9613
|
baseBitType: BitType.note,
|
|
7961
|
-
description: "Note AI bit, used to create AI-generated notes in articles or books"
|
|
9614
|
+
description: "Note AI bit, used to create AI-generated notes in articles or books",
|
|
9615
|
+
tags: [
|
|
9616
|
+
{
|
|
9617
|
+
// Shadows the inherited @aiGenerated from the standard group: this
|
|
9618
|
+
// bit type forces aiGenerated:true regardless of source value or
|
|
9619
|
+
// absence. Literal `true` in the export pattern ignores `$`.
|
|
9620
|
+
key: ConfigKey.property_aiGenerated,
|
|
9621
|
+
description: "AI-generated flag, forced true for article-ai bits",
|
|
9622
|
+
format: TagFormat.boolean,
|
|
9623
|
+
defaultValue: "true",
|
|
9624
|
+
exportJsonKey: { aiGenerated: true }
|
|
9625
|
+
}
|
|
9626
|
+
]
|
|
7962
9627
|
},
|
|
7963
9628
|
[BitType.notebookArticle]: {
|
|
7964
9629
|
since: "1.3.0",
|
|
@@ -8083,7 +9748,19 @@ var BITS = {
|
|
|
8083
9748
|
[BitType.summaryAi]: {
|
|
8084
9749
|
since: "1.3.0",
|
|
8085
9750
|
baseBitType: BitType.summary,
|
|
8086
|
-
description: "AI-generated summary bit"
|
|
9751
|
+
description: "AI-generated summary bit",
|
|
9752
|
+
tags: [
|
|
9753
|
+
{
|
|
9754
|
+
// Shadows the inherited @aiGenerated from the standard group: this
|
|
9755
|
+
// bit type forces aiGenerated:true regardless of source value or
|
|
9756
|
+
// absence. Literal `true` in the export pattern ignores `$`.
|
|
9757
|
+
key: ConfigKey.property_aiGenerated,
|
|
9758
|
+
description: "AI-generated flag, forced true for article-ai bits",
|
|
9759
|
+
format: TagFormat.boolean,
|
|
9760
|
+
defaultValue: "true",
|
|
9761
|
+
exportJsonKey: { aiGenerated: true }
|
|
9762
|
+
}
|
|
9763
|
+
]
|
|
8087
9764
|
},
|
|
8088
9765
|
[BitType.videoTranscript]: {
|
|
8089
9766
|
since: "1.3.0",
|
|
@@ -8357,7 +10034,7 @@ var BITS = {
|
|
|
8357
10034
|
description: "Product ID for the module product, used to link to a specific product",
|
|
8358
10035
|
format: TagFormat.plainText,
|
|
8359
10036
|
minCount: 1,
|
|
8360
|
-
maxCount:
|
|
10037
|
+
maxCount: 1
|
|
8361
10038
|
}
|
|
8362
10039
|
]
|
|
8363
10040
|
},
|
|
@@ -8410,10 +10087,13 @@ var BITS = {
|
|
|
8410
10087
|
description: "Resource bit tags for logo grave images, used to define additional properties"
|
|
8411
10088
|
},
|
|
8412
10089
|
{
|
|
8413
|
-
// Image resource
|
|
8414
|
-
|
|
10090
|
+
// Image resource — fully-expanded shape so each `&image` emission
|
|
10091
|
+
// produces a complete `{type, image: {src}}` object inside the
|
|
10092
|
+
// `images[]` array. Chain attrs like `[@zoomDisabled]` fold under
|
|
10093
|
+
// `images[].image` via the `[]` last-element-merge semantic.
|
|
10094
|
+
key: ConfigKey.group_resourceImageNoZoom,
|
|
8415
10095
|
description: "Resource image tags for logo grave images, used to attach images",
|
|
8416
|
-
exportJsonKey: { images: "$" },
|
|
10096
|
+
exportJsonKey: { images: [{ type: "image", image: { src: "$" } }] },
|
|
8417
10097
|
minCount: 1,
|
|
8418
10098
|
maxCount: Count.infinity
|
|
8419
10099
|
}
|
|
@@ -8703,6 +10383,10 @@ var BITS = {
|
|
|
8703
10383
|
tags: [
|
|
8704
10384
|
{
|
|
8705
10385
|
key: ConfigKey.property_book,
|
|
10386
|
+
// Starter-array shape: each `[@book:value]` chain appends a fresh
|
|
10387
|
+
// `{ book, reference }` entry. The chained `►` (tag_reference) lands
|
|
10388
|
+
// inside the most-recently-appended entry as `reference: $`.
|
|
10389
|
+
exportJsonKey: { book: [{ book: "$" }] },
|
|
8706
10390
|
description: "Book reference for the page, used to link to a specific book",
|
|
8707
10391
|
maxCount: Count.infinity,
|
|
8708
10392
|
chain: [
|
|
@@ -8770,7 +10454,9 @@ var BITS = {
|
|
|
8770
10454
|
format: TagFormat.plainText
|
|
8771
10455
|
}
|
|
8772
10456
|
],
|
|
8773
|
-
|
|
10457
|
+
// page-footer emits cards under `sections` (vs `listItems` for the
|
|
10458
|
+
// sibling exampleList / assignmentList bits) — see JsonGenerator.ts.
|
|
10459
|
+
cardSet: CardSetConfigKey.pageFooterSections
|
|
8774
10460
|
},
|
|
8775
10461
|
[BitType.legend]: {
|
|
8776
10462
|
since: "3.12.0",
|
|
@@ -8812,13 +10498,52 @@ var BITS = {
|
|
|
8812
10498
|
since: "1.34.0",
|
|
8813
10499
|
baseBitType: BitType._standard,
|
|
8814
10500
|
description: "Definition list bit, used to create lists of definitions in articles or books",
|
|
10501
|
+
tags: [
|
|
10502
|
+
{
|
|
10503
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
10504
|
+
key: ConfigKey.property_example,
|
|
10505
|
+
exportJsonKey: [
|
|
10506
|
+
{ "@keyonly": { isExample: true } },
|
|
10507
|
+
{ "@absent": { isExample: true } },
|
|
10508
|
+
{ isExample: true, example: "$" }
|
|
10509
|
+
],
|
|
10510
|
+
description: "The example(s) for the bit",
|
|
10511
|
+
format: TagFormat.bitmarkText,
|
|
10512
|
+
nullable: true
|
|
10513
|
+
}
|
|
10514
|
+
],
|
|
8815
10515
|
cardSet: CardSetConfigKey.definitionList
|
|
8816
10516
|
},
|
|
10517
|
+
[BitType.definitionListCollapsible]: {
|
|
10518
|
+
since: "5.25.0",
|
|
10519
|
+
baseBitType: BitType.definitionList,
|
|
10520
|
+
description: "Collapsible definition list bit, used to create collapsible lists of definitions in articles or books"
|
|
10521
|
+
},
|
|
10522
|
+
[BitType.standardDefinitionListNormative]: {
|
|
10523
|
+
since: "5.24.0",
|
|
10524
|
+
baseBitType: BitType.definitionList,
|
|
10525
|
+
description: "Standard normative definition list bit, used to create normative standard definition lists in articles or books"
|
|
10526
|
+
},
|
|
10527
|
+
[BitType.standardDefinitionListNonNormative]: {
|
|
10528
|
+
since: "5.24.0",
|
|
10529
|
+
baseBitType: BitType.definitionList,
|
|
10530
|
+
description: "Standard non-normative definition list bit, used to create non-normative standard definition lists in articles or books"
|
|
10531
|
+
},
|
|
10532
|
+
[BitType.smartStandardDefinitionListNormative]: {
|
|
10533
|
+
since: "5.24.0",
|
|
10534
|
+
baseBitType: BitType.standardDefinitionListNormative,
|
|
10535
|
+
description: "Smart standard normative definition list bit, used to create normative smart standard definition lists in articles or books"
|
|
10536
|
+
},
|
|
10537
|
+
[BitType.smartStandardDefinitionListNonNormative]: {
|
|
10538
|
+
since: "5.24.0",
|
|
10539
|
+
baseBitType: BitType.standardDefinitionListNonNormative,
|
|
10540
|
+
description: "Smart standard non-normative definition list bit, used to create non-normative smart standard definition lists in articles or books"
|
|
10541
|
+
},
|
|
8817
10542
|
[BitType.metaSearchDefaultTerms]: {
|
|
8818
10543
|
since: "3.12.0",
|
|
8819
10544
|
baseBitType: BitType._standard,
|
|
8820
10545
|
description: "Meta search default terms bit, used to define default search terms in articles",
|
|
8821
|
-
cardSet: CardSetConfigKey.
|
|
10546
|
+
cardSet: CardSetConfigKey.definitionListPlain
|
|
8822
10547
|
},
|
|
8823
10548
|
[BitType.metaSearchDefaultTopics]: {
|
|
8824
10549
|
since: "3.12.0",
|
|
@@ -8834,6 +10559,18 @@ var BITS = {
|
|
|
8834
10559
|
{
|
|
8835
10560
|
key: ConfigKey.group_quizCommon,
|
|
8836
10561
|
description: "Common quiz tags for flashcards"
|
|
10562
|
+
},
|
|
10563
|
+
{
|
|
10564
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
10565
|
+
key: ConfigKey.property_example,
|
|
10566
|
+
exportJsonKey: [
|
|
10567
|
+
{ "@keyonly": { isExample: true } },
|
|
10568
|
+
{ "@absent": { isExample: true } },
|
|
10569
|
+
{ isExample: true, example: "$" }
|
|
10570
|
+
],
|
|
10571
|
+
description: "The example(s) for the bit",
|
|
10572
|
+
format: TagFormat.bitmarkText,
|
|
10573
|
+
nullable: true
|
|
8837
10574
|
}
|
|
8838
10575
|
],
|
|
8839
10576
|
cardSet: CardSetConfigKey.flashcard
|
|
@@ -8841,12 +10578,20 @@ var BITS = {
|
|
|
8841
10578
|
[BitType.flashcard1]: {
|
|
8842
10579
|
since: "1.3.0",
|
|
8843
10580
|
baseBitType: BitType.flashcard,
|
|
8844
|
-
description: "Flashcard 1 bit"
|
|
10581
|
+
description: "Flashcard 1 bit",
|
|
10582
|
+
// PLAN-085: use the flashcard1 cardset (sections.default.maxCount=1)
|
|
10583
|
+
// instead of inheriting `flashcard`'s unbounded cardset, since this
|
|
10584
|
+
// bit type structurally allows only a single card.
|
|
10585
|
+
cardSet: CardSetConfigKey.flashcard1
|
|
8845
10586
|
},
|
|
8846
10587
|
[BitType.qAndACard]: {
|
|
8847
10588
|
since: "3.25.0",
|
|
8848
10589
|
baseBitType: BitType.flashcard1,
|
|
8849
|
-
description: "Q&A card bit, used to create question and answer cards in articles or books"
|
|
10590
|
+
description: "Q&A card bit, used to create question and answer cards in articles or books",
|
|
10591
|
+
// PLAN-085: q-and-a-card permits multiple cards (some fixtures carry
|
|
10592
|
+
// two). Override the inherited `flashcard1` (max=1) cardset back to
|
|
10593
|
+
// the unbounded `flashcard` cardset.
|
|
10594
|
+
cardSet: CardSetConfigKey.flashcard
|
|
8850
10595
|
},
|
|
8851
10596
|
[BitType.focusImage]: {
|
|
8852
10597
|
since: "1.3.0",
|
|
@@ -8876,8 +10621,24 @@ var BITS = {
|
|
|
8876
10621
|
description: "Common quiz tags for highlighted text"
|
|
8877
10622
|
},
|
|
8878
10623
|
{
|
|
8879
|
-
|
|
8880
|
-
|
|
10624
|
+
// Dedicated highlight inline-select chain: tag_true / tag_false
|
|
10625
|
+
// emit `{type:"highlight", attrs:{texts:[{text,isCorrect?}]}}`
|
|
10626
|
+
// inline body nodes via @body-inline. Bit shares no tag IDs with
|
|
10627
|
+
// multiple-choice / multiple-choice-1 (which use group_trueFalse).
|
|
10628
|
+
key: ConfigKey.group_trueFalseInlineHighlight,
|
|
10629
|
+
description: "True/False quiz tags for highlighted text (inline body nodes)"
|
|
10630
|
+
},
|
|
10631
|
+
{
|
|
10632
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
10633
|
+
key: ConfigKey.property_example,
|
|
10634
|
+
exportJsonKey: [
|
|
10635
|
+
{ "@keyonly": { isExample: true } },
|
|
10636
|
+
{ "@absent": { isExample: true } },
|
|
10637
|
+
{ isExample: true, example: "$" }
|
|
10638
|
+
],
|
|
10639
|
+
description: "The example(s) for the bit",
|
|
10640
|
+
format: TagFormat.bitmarkText,
|
|
10641
|
+
nullable: true
|
|
8881
10642
|
}
|
|
8882
10643
|
]
|
|
8883
10644
|
},
|
|
@@ -8887,15 +10648,8 @@ var BITS = {
|
|
|
8887
10648
|
description: "Image bit, used to create images in articles or books",
|
|
8888
10649
|
tags: [
|
|
8889
10650
|
{
|
|
8890
|
-
key: ConfigKey.
|
|
8891
|
-
|
|
8892
|
-
description: "Background wallpaper for the image, used to set a background for the image",
|
|
8893
|
-
chain: [
|
|
8894
|
-
{
|
|
8895
|
-
key: ConfigKey.group_resourceImageCommon,
|
|
8896
|
-
description: "Common resource image tags for images"
|
|
8897
|
-
}
|
|
8898
|
-
]
|
|
10651
|
+
key: ConfigKey.group_backgroundWallpaper,
|
|
10652
|
+
description: "Background wallpaper tags for images, used to define background properties for images"
|
|
8899
10653
|
},
|
|
8900
10654
|
{
|
|
8901
10655
|
key: ConfigKey.group_resourceBitTags,
|
|
@@ -8998,8 +10752,15 @@ var BITS = {
|
|
|
8998
10752
|
},
|
|
8999
10753
|
[BitType.imageSeparator]: {
|
|
9000
10754
|
since: "1.4.15",
|
|
9001
|
-
baseBitType: BitType.
|
|
9002
|
-
description: "Image separator bit, used to create separators in articles or books"
|
|
10755
|
+
baseBitType: BitType._standard,
|
|
10756
|
+
description: "Image separator bit, used to create separators in articles or books",
|
|
10757
|
+
tags: [
|
|
10758
|
+
{
|
|
10759
|
+
key: ConfigKey.group_imageNoZoom,
|
|
10760
|
+
description: "Image no zoom tags for images, used to define whether images should have zoom functionality"
|
|
10761
|
+
}
|
|
10762
|
+
],
|
|
10763
|
+
resourceAttachmentAllowed: false
|
|
9003
10764
|
},
|
|
9004
10765
|
[BitType.imageSeparatorAlt]: {
|
|
9005
10766
|
since: "1.16.0",
|
|
@@ -9038,15 +10799,20 @@ var BITS = {
|
|
|
9038
10799
|
},
|
|
9039
10800
|
[BitType.pageBanner]: {
|
|
9040
10801
|
since: "1.4.3",
|
|
9041
|
-
baseBitType: BitType.
|
|
10802
|
+
baseBitType: BitType._standard,
|
|
9042
10803
|
description: "Page banner bit, used to create banners for pages in articles or books",
|
|
9043
10804
|
tags: [
|
|
9044
10805
|
{
|
|
9045
10806
|
key: ConfigKey.property_slug,
|
|
9046
10807
|
description: "Slug for the page banner, used to identify the banner in the system",
|
|
9047
10808
|
format: TagFormat.plainText
|
|
10809
|
+
},
|
|
10810
|
+
{
|
|
10811
|
+
key: ConfigKey.group_imageNoZoom,
|
|
10812
|
+
description: "Image no zoom tags for images, used to define whether images should have zoom functionality"
|
|
9048
10813
|
}
|
|
9049
|
-
]
|
|
10814
|
+
],
|
|
10815
|
+
resourceAttachmentAllowed: false
|
|
9050
10816
|
},
|
|
9051
10817
|
[BitType.pageHero]: {
|
|
9052
10818
|
since: "1.11.0",
|
|
@@ -9061,7 +10827,7 @@ var BITS = {
|
|
|
9061
10827
|
},
|
|
9062
10828
|
[BitType.tableImage]: {
|
|
9063
10829
|
since: "1.5.15",
|
|
9064
|
-
baseBitType: BitType.
|
|
10830
|
+
baseBitType: BitType.tableExtended,
|
|
9065
10831
|
description: "Table image bit, used to create images in tables in articles or books",
|
|
9066
10832
|
tags: [
|
|
9067
10833
|
{
|
|
@@ -9070,15 +10836,8 @@ var BITS = {
|
|
|
9070
10836
|
format: TagFormat.bitmarkText
|
|
9071
10837
|
},
|
|
9072
10838
|
{
|
|
9073
|
-
key: ConfigKey.
|
|
9074
|
-
|
|
9075
|
-
description: "Background wallpaper for the image, used to set a background for the image",
|
|
9076
|
-
chain: [
|
|
9077
|
-
{
|
|
9078
|
-
key: ConfigKey.group_resourceImageCommon,
|
|
9079
|
-
description: "Common resource image tags for images"
|
|
9080
|
-
}
|
|
9081
|
-
]
|
|
10839
|
+
key: ConfigKey.group_backgroundWallpaper,
|
|
10840
|
+
description: "Background wallpaper tags for images, used to define background properties for images"
|
|
9082
10841
|
},
|
|
9083
10842
|
{
|
|
9084
10843
|
key: ConfigKey.group_resourceBitTags,
|
|
@@ -9221,10 +10980,10 @@ var BITS = {
|
|
|
9221
10980
|
description: "Resource bit tags for logo grave images, used to define additional properties"
|
|
9222
10981
|
},
|
|
9223
10982
|
{
|
|
9224
|
-
// Image resource
|
|
9225
|
-
key: ConfigKey.
|
|
10983
|
+
// Image resource — fully-expanded shape (see BitType.extractorImage).
|
|
10984
|
+
key: ConfigKey.group_resourceImageNoZoom,
|
|
9226
10985
|
description: "Resource image tags for logo grave images, used to attach images",
|
|
9227
|
-
exportJsonKey: { logos: "$" },
|
|
10986
|
+
exportJsonKey: { logos: [{ type: "image", image: { src: "$" } }] },
|
|
9228
10987
|
minCount: 1,
|
|
9229
10988
|
maxCount: Count.infinity
|
|
9230
10989
|
}
|
|
@@ -9234,7 +10993,17 @@ var BITS = {
|
|
|
9234
10993
|
[BitType.prototypeImages]: {
|
|
9235
10994
|
since: "1.6.1",
|
|
9236
10995
|
baseBitType: BitType.imagesLogoGrave,
|
|
9237
|
-
description: "Prototype images bit, used to create prototype images in articles or books"
|
|
10996
|
+
description: "Prototype images bit, used to create prototype images in articles or books",
|
|
10997
|
+
tags: [
|
|
10998
|
+
{
|
|
10999
|
+
// Override inherited `logos` wrapper with `images` for this bit type.
|
|
11000
|
+
key: ConfigKey.group_resourceImageNoZoom,
|
|
11001
|
+
description: "Resource image tags for prototype images",
|
|
11002
|
+
exportJsonKey: { images: [{ type: "image", image: { src: "$" } }] },
|
|
11003
|
+
minCount: 1,
|
|
11004
|
+
maxCount: Count.infinity
|
|
11005
|
+
}
|
|
11006
|
+
]
|
|
9238
11007
|
},
|
|
9239
11008
|
[BitType.internalLink]: {
|
|
9240
11009
|
since: "1.3.0",
|
|
@@ -9264,6 +11033,18 @@ var BITS = {
|
|
|
9264
11033
|
exportJsonKey: {},
|
|
9265
11034
|
description: "Reasonable number of characters for the interview, used to limit input size",
|
|
9266
11035
|
format: TagFormat.number
|
|
11036
|
+
},
|
|
11037
|
+
{
|
|
11038
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
11039
|
+
key: ConfigKey.property_example,
|
|
11040
|
+
exportJsonKey: [
|
|
11041
|
+
{ "@keyonly": { isExample: true } },
|
|
11042
|
+
{ "@absent": { isExample: true } },
|
|
11043
|
+
{ isExample: true, example: "$" }
|
|
11044
|
+
],
|
|
11045
|
+
description: "The example(s) for the bit",
|
|
11046
|
+
format: TagFormat.bitmarkText,
|
|
11047
|
+
nullable: true
|
|
9267
11048
|
}
|
|
9268
11049
|
],
|
|
9269
11050
|
cardSet: CardSetConfigKey.questions
|
|
@@ -9622,6 +11403,18 @@ var BITS = {
|
|
|
9622
11403
|
{
|
|
9623
11404
|
key: ConfigKey.group_mark,
|
|
9624
11405
|
description: "Mark tags, used to define the content of marks"
|
|
11406
|
+
},
|
|
11407
|
+
{
|
|
11408
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
11409
|
+
key: ConfigKey.property_example,
|
|
11410
|
+
exportJsonKey: [
|
|
11411
|
+
{ "@keyonly": { isExample: true } },
|
|
11412
|
+
{ "@absent": { isExample: true } },
|
|
11413
|
+
{ isExample: true, example: "$" }
|
|
11414
|
+
],
|
|
11415
|
+
description: "The example(s) for the bit",
|
|
11416
|
+
format: TagFormat.bitmarkText,
|
|
11417
|
+
nullable: true
|
|
9625
11418
|
}
|
|
9626
11419
|
]
|
|
9627
11420
|
},
|
|
@@ -9641,6 +11434,23 @@ var BITS = {
|
|
|
9641
11434
|
description: "Case sensitivity for matching pairs, used to define if matches are case-sensitive",
|
|
9642
11435
|
format: TagFormat.boolean,
|
|
9643
11436
|
defaultValue: "true"
|
|
11437
|
+
},
|
|
11438
|
+
{
|
|
11439
|
+
// Override inherited @example: in `isTopLevelExample` allow-list
|
|
11440
|
+
// (covers match, matchAll, matchReverse, matchAllReverse,
|
|
11441
|
+
// matchSolutionGrouped, matchMatrix, matchAudio, matchPicture).
|
|
11442
|
+
// Bit-level fires emit only `isExample`; the @example value (when
|
|
11443
|
+
// provided) is recorded in the ancestor stack and consumed by
|
|
11444
|
+
// per-pair/per-cell variant overrides — not surfaced at bit scope.
|
|
11445
|
+
key: ConfigKey.property_example,
|
|
11446
|
+
exportJsonKey: [
|
|
11447
|
+
{ "@keyonly": { isExample: true } },
|
|
11448
|
+
{ "@absent": { isExample: true } },
|
|
11449
|
+
{ isExample: true }
|
|
11450
|
+
],
|
|
11451
|
+
description: "The example(s) for the bit",
|
|
11452
|
+
format: TagFormat.bitmarkText,
|
|
11453
|
+
nullable: true
|
|
9644
11454
|
}
|
|
9645
11455
|
],
|
|
9646
11456
|
cardSet: CardSetConfigKey.matchPairs
|
|
@@ -9725,6 +11535,18 @@ var BITS = {
|
|
|
9725
11535
|
{
|
|
9726
11536
|
key: ConfigKey.group_trueFalse,
|
|
9727
11537
|
description: "True/False tags for multiple choice 1 questions"
|
|
11538
|
+
},
|
|
11539
|
+
{
|
|
11540
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
11541
|
+
key: ConfigKey.property_example,
|
|
11542
|
+
exportJsonKey: [
|
|
11543
|
+
{ "@keyonly": { isExample: true } },
|
|
11544
|
+
{ "@absent": { isExample: true } },
|
|
11545
|
+
{ isExample: true, example: "$" }
|
|
11546
|
+
],
|
|
11547
|
+
description: "The example(s) for the bit",
|
|
11548
|
+
format: TagFormat.bitmarkText,
|
|
11549
|
+
nullable: true
|
|
9728
11550
|
}
|
|
9729
11551
|
]
|
|
9730
11552
|
},
|
|
@@ -9746,8 +11568,20 @@ var BITS = {
|
|
|
9746
11568
|
{
|
|
9747
11569
|
key: ConfigKey.group_trueFalse,
|
|
9748
11570
|
description: "True/False tags for multiple choice questions"
|
|
9749
|
-
}
|
|
11571
|
+
},
|
|
9750
11572
|
// This is actually for multiple-choice-1, but we support it here as well (as many bits are wrong)
|
|
11573
|
+
{
|
|
11574
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
11575
|
+
key: ConfigKey.property_example,
|
|
11576
|
+
exportJsonKey: [
|
|
11577
|
+
{ "@keyonly": { isExample: true } },
|
|
11578
|
+
{ "@absent": { isExample: true } },
|
|
11579
|
+
{ isExample: true, example: "$" }
|
|
11580
|
+
],
|
|
11581
|
+
description: "The example(s) for the bit",
|
|
11582
|
+
format: TagFormat.bitmarkText,
|
|
11583
|
+
nullable: true
|
|
11584
|
+
}
|
|
9751
11585
|
],
|
|
9752
11586
|
cardSet: CardSetConfigKey.quiz
|
|
9753
11587
|
},
|
|
@@ -9767,8 +11601,20 @@ var BITS = {
|
|
|
9767
11601
|
description: "Common quiz tags for multiple choice text questions"
|
|
9768
11602
|
},
|
|
9769
11603
|
{
|
|
9770
|
-
key: ConfigKey.
|
|
11604
|
+
key: ConfigKey.group_trueFalseInlineSelect,
|
|
9771
11605
|
description: "True/False tags for multiple choice text questions"
|
|
11606
|
+
},
|
|
11607
|
+
{
|
|
11608
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
11609
|
+
key: ConfigKey.property_example,
|
|
11610
|
+
exportJsonKey: [
|
|
11611
|
+
{ "@keyonly": { isExample: true } },
|
|
11612
|
+
{ "@absent": { isExample: true } },
|
|
11613
|
+
{ isExample: true, example: "$" }
|
|
11614
|
+
],
|
|
11615
|
+
description: "The example(s) for the bit",
|
|
11616
|
+
format: TagFormat.bitmarkText,
|
|
11617
|
+
nullable: true
|
|
9772
11618
|
}
|
|
9773
11619
|
]
|
|
9774
11620
|
},
|
|
@@ -9793,8 +11639,20 @@ var BITS = {
|
|
|
9793
11639
|
description: "Common quiz tags for multiple response 1 questions"
|
|
9794
11640
|
},
|
|
9795
11641
|
{
|
|
9796
|
-
key: ConfigKey.
|
|
11642
|
+
key: ConfigKey.group_trueFalseResponses,
|
|
9797
11643
|
description: "True/False tags for multiple response 1 questions"
|
|
11644
|
+
},
|
|
11645
|
+
{
|
|
11646
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
11647
|
+
key: ConfigKey.property_example,
|
|
11648
|
+
exportJsonKey: [
|
|
11649
|
+
{ "@keyonly": { isExample: true } },
|
|
11650
|
+
{ "@absent": { isExample: true } },
|
|
11651
|
+
{ isExample: true, example: "$" }
|
|
11652
|
+
],
|
|
11653
|
+
description: "The example(s) for the bit",
|
|
11654
|
+
format: TagFormat.bitmarkText,
|
|
11655
|
+
nullable: true
|
|
9798
11656
|
}
|
|
9799
11657
|
]
|
|
9800
11658
|
},
|
|
@@ -9814,12 +11672,24 @@ var BITS = {
|
|
|
9814
11672
|
description: "Common quiz tags for multiple response questions"
|
|
9815
11673
|
},
|
|
9816
11674
|
{
|
|
9817
|
-
key: ConfigKey.
|
|
11675
|
+
key: ConfigKey.group_trueFalseResponses,
|
|
9818
11676
|
description: "True/False tags for multiple response questions"
|
|
9819
|
-
}
|
|
11677
|
+
},
|
|
9820
11678
|
// This is actually for multiple-response-1, but we support it here as well (as many bits are wrong)
|
|
11679
|
+
{
|
|
11680
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
11681
|
+
key: ConfigKey.property_example,
|
|
11682
|
+
exportJsonKey: [
|
|
11683
|
+
{ "@keyonly": { isExample: true } },
|
|
11684
|
+
{ "@absent": { isExample: true } },
|
|
11685
|
+
{ isExample: true, example: "$" }
|
|
11686
|
+
],
|
|
11687
|
+
description: "The example(s) for the bit",
|
|
11688
|
+
format: TagFormat.bitmarkText,
|
|
11689
|
+
nullable: true
|
|
11690
|
+
}
|
|
9821
11691
|
],
|
|
9822
|
-
cardSet: CardSetConfigKey.
|
|
11692
|
+
cardSet: CardSetConfigKey.quizResponses
|
|
9823
11693
|
},
|
|
9824
11694
|
[BitType.coachSelfReflectionMultipleResponse]: {
|
|
9825
11695
|
since: "1.3.0",
|
|
@@ -10069,6 +11939,8 @@ var BITS = {
|
|
|
10069
11939
|
},
|
|
10070
11940
|
{
|
|
10071
11941
|
key: ConfigKey.resource_platformIcon,
|
|
11942
|
+
jsonKey: "platformIcon|resource(type=image, key=image)",
|
|
11943
|
+
exportJsonKey: { platformIcon: { type: "image", image: { src: "$" } } },
|
|
10072
11944
|
description: "The platform icon",
|
|
10073
11945
|
chain: [
|
|
10074
11946
|
{
|
|
@@ -10104,6 +11976,8 @@ var BITS = {
|
|
|
10104
11976
|
tags: [
|
|
10105
11977
|
{
|
|
10106
11978
|
key: ConfigKey.resource_platformLogo,
|
|
11979
|
+
jsonKey: "platformLogo|resource(type=image, key=image)",
|
|
11980
|
+
exportJsonKey: { platformLogo: { type: "image", image: { src: "$" } } },
|
|
10107
11981
|
description: "The platform logo",
|
|
10108
11982
|
chain: [
|
|
10109
11983
|
{
|
|
@@ -10315,7 +12189,8 @@ var BITS = {
|
|
|
10315
12189
|
{ isExample: true, example: "$" }
|
|
10316
12190
|
],
|
|
10317
12191
|
description: "The example text for the review note bit",
|
|
10318
|
-
format: TagFormat.plainText
|
|
12192
|
+
format: TagFormat.plainText,
|
|
12193
|
+
nullable: true
|
|
10319
12194
|
}
|
|
10320
12195
|
],
|
|
10321
12196
|
rootExampleType: ExampleType.string
|
|
@@ -10402,7 +12277,8 @@ var BITS = {
|
|
|
10402
12277
|
{ isExample: true, example: "$" }
|
|
10403
12278
|
],
|
|
10404
12279
|
description: "The example flag for the bit (boolean)",
|
|
10405
|
-
format: TagFormat.
|
|
12280
|
+
format: TagFormat.boolean,
|
|
12281
|
+
nullable: true
|
|
10406
12282
|
}
|
|
10407
12283
|
],
|
|
10408
12284
|
cardSet: CardSetConfigKey.elements,
|
|
@@ -10676,15 +12552,8 @@ var BITS = {
|
|
|
10676
12552
|
format: TagFormat.bitmarkText
|
|
10677
12553
|
},
|
|
10678
12554
|
{
|
|
10679
|
-
key: ConfigKey.
|
|
10680
|
-
|
|
10681
|
-
description: "Background wallpaper for the image, used to set a background for the image",
|
|
10682
|
-
chain: [
|
|
10683
|
-
{
|
|
10684
|
-
key: ConfigKey.group_resourceImageCommon,
|
|
10685
|
-
description: "Common resource image tags for images"
|
|
10686
|
-
}
|
|
10687
|
-
]
|
|
12555
|
+
key: ConfigKey.group_backgroundWallpaper,
|
|
12556
|
+
description: "Background wallpaper tags for images, used to define background properties for images"
|
|
10688
12557
|
},
|
|
10689
12558
|
{
|
|
10690
12559
|
key: ConfigKey.group_resourceBitTags,
|
|
@@ -11061,16 +12930,20 @@ var BITS = {
|
|
|
11061
12930
|
format: TagFormat.plainText
|
|
11062
12931
|
},
|
|
11063
12932
|
{
|
|
11064
|
-
|
|
12933
|
+
// PLAN-067 Edit #1: true-false-1's `+` / `-` emit a flat
|
|
12934
|
+
// `{ statement, isCorrect }` at bit root (single-statement bit),
|
|
12935
|
+
// not a `choices[]` array. Format is bitmarkText so the statement
|
|
12936
|
+
// body renders as a ProseMirror tree, matching reference fixtures.
|
|
12937
|
+
exportJsonKey: { statement: "$", isCorrect: true },
|
|
11065
12938
|
key: ConfigKey.tag_true,
|
|
11066
12939
|
description: "Tag for the true option in the true/false question",
|
|
11067
|
-
format: TagFormat.
|
|
12940
|
+
format: TagFormat.bitmarkText
|
|
11068
12941
|
},
|
|
11069
12942
|
{
|
|
11070
|
-
exportJsonKey: {
|
|
12943
|
+
exportJsonKey: { statement: "$", isCorrect: false },
|
|
11071
12944
|
key: ConfigKey.tag_false,
|
|
11072
12945
|
description: "Tag for the false option in the true/false question",
|
|
11073
|
-
format: TagFormat.
|
|
12946
|
+
format: TagFormat.bitmarkText
|
|
11074
12947
|
},
|
|
11075
12948
|
{
|
|
11076
12949
|
// Override inherited @example from group_standardTags: boolean root
|
|
@@ -11081,14 +12954,24 @@ var BITS = {
|
|
|
11081
12954
|
// sufficient for `[+...]` (correct) statements which dominate the
|
|
11082
12955
|
// fixtures; `[-...]` (incorrect) bits with bare `[@example]` would
|
|
11083
12956
|
// need either a runtime extension or an explicit `[@example:false]`.
|
|
12957
|
+
//
|
|
12958
|
+
// Format is `boolean` so `[@example:false]` coerces to JSON `false`
|
|
12959
|
+
// (a natural default that gets stripped in optimised mode, matching
|
|
12960
|
+
// BPG's `rootExampleType: boolean`). PlainText would emit the
|
|
12961
|
+
// STRING `"false"` which doesn't strip.
|
|
11084
12962
|
key: ConfigKey.property_example,
|
|
11085
12963
|
exportJsonKey: [
|
|
11086
12964
|
{ "@keyonly": { isExample: true, example: true } },
|
|
11087
12965
|
{ "@absent": { isExample: true, example: true } },
|
|
12966
|
+
// `[@example:false]`: emit `isExample: true` only — `example: false`
|
|
12967
|
+
// is the natural default for boolean, stripped in optimised mode.
|
|
12968
|
+
// Mirrors BPG `removeUnwantedProperties` `ignoreAllFalse: true`.
|
|
12969
|
+
{ "@value=false": { isExample: true } },
|
|
11088
12970
|
{ isExample: true, example: "$" }
|
|
11089
12971
|
],
|
|
11090
12972
|
description: "The example flag for the bit (boolean)",
|
|
11091
|
-
format: TagFormat.
|
|
12973
|
+
format: TagFormat.boolean,
|
|
12974
|
+
nullable: true
|
|
11092
12975
|
}
|
|
11093
12976
|
],
|
|
11094
12977
|
rootExampleType: ExampleType.boolean
|
|
@@ -11112,6 +12995,18 @@ var BITS = {
|
|
|
11112
12995
|
key: ConfigKey.property_labelFalse,
|
|
11113
12996
|
description: "Label for the false option in the true/false question",
|
|
11114
12997
|
format: TagFormat.plainText
|
|
12998
|
+
},
|
|
12999
|
+
{
|
|
13000
|
+
// Override inherited @example: in `isTopLevelExample` allow-list.
|
|
13001
|
+
key: ConfigKey.property_example,
|
|
13002
|
+
exportJsonKey: [
|
|
13003
|
+
{ "@keyonly": { isExample: true } },
|
|
13004
|
+
{ "@absent": { isExample: true } },
|
|
13005
|
+
{ isExample: true, example: "$" }
|
|
13006
|
+
],
|
|
13007
|
+
description: "The example(s) for the bit",
|
|
13008
|
+
format: TagFormat.bitmarkText,
|
|
13009
|
+
nullable: true
|
|
11115
13010
|
}
|
|
11116
13011
|
],
|
|
11117
13012
|
cardSet: CardSetConfigKey.statements
|
|
@@ -11301,6 +13196,11 @@ var BITS = {
|
|
|
11301
13196
|
baseBitType: BitType.vendorStripePricingTable,
|
|
11302
13197
|
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",
|
|
11303
13198
|
tags: [
|
|
13199
|
+
{
|
|
13200
|
+
key: ConfigKey.property_stripePricingTableDescriptiveName,
|
|
13201
|
+
description: "Descriptive name for the Stripe pricing table",
|
|
13202
|
+
format: TagFormat.plainText
|
|
13203
|
+
},
|
|
11304
13204
|
{
|
|
11305
13205
|
key: ConfigKey.property_printOnRequestCustomerId,
|
|
11306
13206
|
description: "Print-on-request customer ID, used to identify the customer for the print-on-request flow",
|
|
@@ -11777,7 +13677,7 @@ var instance2 = new Config();
|
|
|
11777
13677
|
// src/generated/package_info.ts
|
|
11778
13678
|
var PACKAGE_INFO = {
|
|
11779
13679
|
"name": "@gmb/bitmark-parser-generator",
|
|
11780
|
-
"version": "5.
|
|
13680
|
+
"version": "5.25.0",
|
|
11781
13681
|
"author": "Get More Brain Ltd",
|
|
11782
13682
|
"license": "ISC",
|
|
11783
13683
|
"description": "A bitmark parser and generator using Peggy.js"
|
|
@@ -12423,6 +14323,8 @@ var NodeType = {
|
|
|
12423
14323
|
internalPrintPdf: "internalPrintPdf",
|
|
12424
14324
|
internalPrintPdfValue: "internalPrintPdfValue",
|
|
12425
14325
|
isCaseSensitive: "isCaseSensitive",
|
|
14326
|
+
isCollapsible: "isCollapsible",
|
|
14327
|
+
isCollapsibleValue: "isCollapsibleValue",
|
|
12426
14328
|
isCommented: "isCommented",
|
|
12427
14329
|
isCorrect: "isCorrect",
|
|
12428
14330
|
isEditable: "isEditable",
|
|
@@ -12742,6 +14644,8 @@ var NodeType = {
|
|
|
12742
14644
|
stillImageFilmLink: "stillImageFilmLink",
|
|
12743
14645
|
stripePricingTableId: "stripePricingTableId",
|
|
12744
14646
|
stripePricingTableIdValue: "stripePricingTableIdValue",
|
|
14647
|
+
stripePricingTableDescriptiveName: "stripePricingTableDescriptiveName",
|
|
14648
|
+
stripePricingTableDescriptiveNameValue: "stripePricingTableDescriptiveNameValue",
|
|
12745
14649
|
stripePublishableKey: "stripePublishableKey",
|
|
12746
14650
|
stripePublishableKeyValue: "stripePublishableKeyValue",
|
|
12747
14651
|
subject: "subject",
|
|
@@ -26167,6 +28071,12 @@ var Builder = class extends BaseBuilder {
|
|
|
26167
28071
|
data.stripePricingTableId,
|
|
26168
28072
|
options
|
|
26169
28073
|
),
|
|
28074
|
+
stripePricingTableDescriptiveName: this.toAstProperty(
|
|
28075
|
+
bitType,
|
|
28076
|
+
ConfigKey.property_stripePricingTableDescriptiveName,
|
|
28077
|
+
data.stripePricingTableDescriptiveName,
|
|
28078
|
+
options
|
|
28079
|
+
),
|
|
26170
28080
|
stripePublishableKey: this.toAstProperty(
|
|
26171
28081
|
bitType,
|
|
26172
28082
|
ConfigKey.property_stripePublishableKey,
|
|
@@ -26653,6 +28563,12 @@ var Builder = class extends BaseBuilder {
|
|
|
26653
28563
|
level: instance6.asNumber(data.level),
|
|
26654
28564
|
toc: this.toAstProperty(bitType, ConfigKey.property_toc, data.toc, options),
|
|
26655
28565
|
progress: this.toAstProperty(bitType, ConfigKey.property_progress, data.progress, options),
|
|
28566
|
+
isCollapsible: this.toAstProperty(
|
|
28567
|
+
bitType,
|
|
28568
|
+
ConfigKey.property_isCollapsible,
|
|
28569
|
+
data.isCollapsible,
|
|
28570
|
+
options
|
|
28571
|
+
),
|
|
26656
28572
|
anchor: data.anchor,
|
|
26657
28573
|
reference: data.reference,
|
|
26658
28574
|
referenceEnd: data.referenceEnd,
|
|
@@ -32199,6 +34115,7 @@ var JsonGenerator = class extends AstWalkerGenerator {
|
|
|
32199
34115
|
if (instance2.isOfBitType(bitType, BitType.chapter)) {
|
|
32200
34116
|
if (bitJson.toc == null) bitJson.toc = true;
|
|
32201
34117
|
if (bitJson.progress == null) bitJson.progress = true;
|
|
34118
|
+
if (bitJson.isCollapsible == null) bitJson.isCollapsible = false;
|
|
32202
34119
|
if (bitJson.level == null) bitJson.level = 1;
|
|
32203
34120
|
if (bitJson.body == null) bitJson.body = this.bodyDefault;
|
|
32204
34121
|
}
|
|
@@ -34412,7 +36329,9 @@ function buildCards(context, bitType, textFormat, parsedCardSet, statementV1, st
|
|
|
34412
36329
|
const cardSetType = bitConfig.cardSet?.configKey;
|
|
34413
36330
|
switch (cardSetType) {
|
|
34414
36331
|
case CardSetConfigKey.flashcard:
|
|
36332
|
+
case CardSetConfigKey.flashcard1:
|
|
34415
36333
|
case CardSetConfigKey.definitionList:
|
|
36334
|
+
case CardSetConfigKey.definitionListPlain:
|
|
34416
36335
|
result = parseFlashcardOrDefinitionList(context, bitType, processedCardSet);
|
|
34417
36336
|
break;
|
|
34418
36337
|
case CardSetConfigKey.elements:
|
|
@@ -34425,6 +36344,7 @@ function buildCards(context, bitType, textFormat, parsedCardSet, statementV1, st
|
|
|
34425
36344
|
result = parseFeedback(context, bitType, processedCardSet);
|
|
34426
36345
|
break;
|
|
34427
36346
|
case CardSetConfigKey.quiz:
|
|
36347
|
+
case CardSetConfigKey.quizResponses:
|
|
34428
36348
|
result = parseQuiz(context, bitType, processedCardSet, choicesV1, responsesV1);
|
|
34429
36349
|
break;
|
|
34430
36350
|
case CardSetConfigKey.questions:
|
|
@@ -34451,6 +36371,7 @@ function buildCards(context, bitType, textFormat, parsedCardSet, statementV1, st
|
|
|
34451
36371
|
break;
|
|
34452
36372
|
case CardSetConfigKey.clozeList:
|
|
34453
36373
|
case CardSetConfigKey.exampleBitList:
|
|
36374
|
+
case CardSetConfigKey.pageFooterSections:
|
|
34454
36375
|
case CardSetConfigKey.bookReferenceList:
|
|
34455
36376
|
result = parseCardBits(context, bitType, textFormat, processedCardSet);
|
|
34456
36377
|
break;
|
|
@@ -40747,6 +42668,235 @@ var BitmarkFileGenerator = class {
|
|
|
40747
42668
|
}
|
|
40748
42669
|
};
|
|
40749
42670
|
|
|
42671
|
+
// src/generator/html/HtmlTableGenerator.ts
|
|
42672
|
+
var MARK_TO_TAG = {
|
|
42673
|
+
bold: "b",
|
|
42674
|
+
italic: "i",
|
|
42675
|
+
superscript: "sup",
|
|
42676
|
+
subscript: "sub",
|
|
42677
|
+
strike: "s",
|
|
42678
|
+
del: "del",
|
|
42679
|
+
ins: "ins",
|
|
42680
|
+
underline: "u",
|
|
42681
|
+
highlight: "mark",
|
|
42682
|
+
code: "code"
|
|
42683
|
+
};
|
|
42684
|
+
var HtmlTableGenerator = class {
|
|
42685
|
+
constructor() {
|
|
42686
|
+
__publicField(this, "indentUnit", " ");
|
|
42687
|
+
}
|
|
42688
|
+
/**
|
|
42689
|
+
* Generate HTML table fragments from a string or JSON value.
|
|
42690
|
+
* @returns the concatenated `<table>` fragments, blank-line separated (empty string if none).
|
|
42691
|
+
*/
|
|
42692
|
+
generate(input) {
|
|
42693
|
+
let data = input;
|
|
42694
|
+
if (typeof data === "string") {
|
|
42695
|
+
try {
|
|
42696
|
+
data = JSON.parse(data);
|
|
42697
|
+
} catch {
|
|
42698
|
+
return "";
|
|
42699
|
+
}
|
|
42700
|
+
}
|
|
42701
|
+
const bits = this.collectBits(data);
|
|
42702
|
+
const fragments = [];
|
|
42703
|
+
for (const bit of bits) {
|
|
42704
|
+
const html = this.renderTableBit(bit);
|
|
42705
|
+
if (html) fragments.push(html);
|
|
42706
|
+
}
|
|
42707
|
+
return fragments.join("\n\n");
|
|
42708
|
+
}
|
|
42709
|
+
// --- bit collection -------------------------------------------------------
|
|
42710
|
+
collectBits(data) {
|
|
42711
|
+
const out = [];
|
|
42712
|
+
const visit = (value) => {
|
|
42713
|
+
if (value == null || typeof value !== "object") return;
|
|
42714
|
+
if (Array.isArray(value)) {
|
|
42715
|
+
value.forEach(visit);
|
|
42716
|
+
return;
|
|
42717
|
+
}
|
|
42718
|
+
const obj = value;
|
|
42719
|
+
if ("bit" in obj && obj.bit && typeof obj.bit === "object") {
|
|
42720
|
+
out.push(obj.bit);
|
|
42721
|
+
} else if ("type" in obj) {
|
|
42722
|
+
out.push(obj);
|
|
42723
|
+
}
|
|
42724
|
+
};
|
|
42725
|
+
visit(data);
|
|
42726
|
+
return out;
|
|
42727
|
+
}
|
|
42728
|
+
// --- table rendering ------------------------------------------------------
|
|
42729
|
+
renderTableBit(bit) {
|
|
42730
|
+
const table = bit.table;
|
|
42731
|
+
if (!table) return "";
|
|
42732
|
+
if (bit.type !== "table" && bit.type !== "table-extended") return "";
|
|
42733
|
+
const extended = this.toExtended(table);
|
|
42734
|
+
const lines = [];
|
|
42735
|
+
lines.push("<table>");
|
|
42736
|
+
if (bit.caption != null) {
|
|
42737
|
+
const caption = this.cellContentToHtml(this.asTextAst(bit.caption));
|
|
42738
|
+
if (caption) lines.push(`${this.indentUnit}<caption>${caption}</caption>`);
|
|
42739
|
+
}
|
|
42740
|
+
this.renderSection(lines, "thead", extended.header, "header");
|
|
42741
|
+
this.renderSection(lines, "tbody", extended.body, "body");
|
|
42742
|
+
this.renderSection(lines, "tfoot", extended.footer, "footer");
|
|
42743
|
+
lines.push("</table>");
|
|
42744
|
+
return lines.join("\n");
|
|
42745
|
+
}
|
|
42746
|
+
toExtended(table) {
|
|
42747
|
+
const t = table;
|
|
42748
|
+
if (t.header || t.body || t.footer) return table;
|
|
42749
|
+
return convertBasicToExtendedTableFormat(table);
|
|
42750
|
+
}
|
|
42751
|
+
renderSection(lines, tag, section, key) {
|
|
42752
|
+
if (!section || !section.rows || section.rows.length === 0) return;
|
|
42753
|
+
lines.push(`${this.indentUnit}<${tag}>`);
|
|
42754
|
+
for (const row of section.rows) {
|
|
42755
|
+
lines.push(`${this.indentUnit.repeat(2)}<tr>`);
|
|
42756
|
+
for (const cell of row.cells ?? []) {
|
|
42757
|
+
lines.push(`${this.indentUnit.repeat(3)}${this.renderCell(cell, key)}`);
|
|
42758
|
+
}
|
|
42759
|
+
lines.push(`${this.indentUnit.repeat(2)}</tr>`);
|
|
42760
|
+
}
|
|
42761
|
+
lines.push(`${this.indentUnit}</${tag}>`);
|
|
42762
|
+
}
|
|
42763
|
+
renderCell(cell, _key) {
|
|
42764
|
+
const tag = cell.title === true ? "th" : "td";
|
|
42765
|
+
const attrs = [];
|
|
42766
|
+
if (cell.rowspan && cell.rowspan > 1) attrs.push(`rowspan="${cell.rowspan}"`);
|
|
42767
|
+
if (cell.colspan && cell.colspan > 1) attrs.push(`colspan="${cell.colspan}"`);
|
|
42768
|
+
if (cell.scope) attrs.push(`scope="${cell.scope}"`);
|
|
42769
|
+
if (cell.colwidth && cell.colwidth > 0) attrs.push(`width="${cell.colwidth}"`);
|
|
42770
|
+
const open = attrs.length > 0 ? `<${tag} ${attrs.join(" ")}>` : `<${tag}>`;
|
|
42771
|
+
return `${open}${this.cellContentToHtml(this.asTextAst(cell.content))}</${tag}>`;
|
|
42772
|
+
}
|
|
42773
|
+
// --- cell content (TextAst -> HTML) ---------------------------------------
|
|
42774
|
+
asTextAst(content) {
|
|
42775
|
+
if (content == null) return [];
|
|
42776
|
+
if (typeof content === "string") return [{ type: "text", text: content }];
|
|
42777
|
+
return content;
|
|
42778
|
+
}
|
|
42779
|
+
cellContentToHtml(blocks) {
|
|
42780
|
+
if (blocks.length === 0) return "";
|
|
42781
|
+
if (blocks.length === 1 && blocks[0].type === "paragraph") {
|
|
42782
|
+
return this.inlineToHtml(blocks[0].content ?? []);
|
|
42783
|
+
}
|
|
42784
|
+
return blocks.map((b) => this.blockToHtml(b)).join("");
|
|
42785
|
+
}
|
|
42786
|
+
blockToHtml(node) {
|
|
42787
|
+
switch (node.type) {
|
|
42788
|
+
case "paragraph":
|
|
42789
|
+
return `<p>${this.inlineToHtml(node.content ?? [])}</p>`;
|
|
42790
|
+
case "bulletList":
|
|
42791
|
+
case "noBulletList":
|
|
42792
|
+
case "orderedList":
|
|
42793
|
+
case "orderedListRoman":
|
|
42794
|
+
case "orderedListRomanLower":
|
|
42795
|
+
case "letteredList":
|
|
42796
|
+
case "letteredListLower":
|
|
42797
|
+
case "taskList":
|
|
42798
|
+
return this.listToHtml(node);
|
|
42799
|
+
case "image":
|
|
42800
|
+
case "imageInline":
|
|
42801
|
+
return this.imageToHtml(node);
|
|
42802
|
+
default:
|
|
42803
|
+
return this.inlineToHtml([node]);
|
|
42804
|
+
}
|
|
42805
|
+
}
|
|
42806
|
+
inlineToHtml(nodes) {
|
|
42807
|
+
return nodes.map((n) => this.inlineNodeToHtml(n)).join("");
|
|
42808
|
+
}
|
|
42809
|
+
inlineNodeToHtml(node) {
|
|
42810
|
+
if (node.type === "hardBreak") return "<br>";
|
|
42811
|
+
if (node.type === "image" || node.type === "imageInline") return this.imageToHtml(node);
|
|
42812
|
+
if (node.type === "text") {
|
|
42813
|
+
return this.wrapMarks(this.escapeText(node.text ?? ""), node.marks);
|
|
42814
|
+
}
|
|
42815
|
+
if (node.content) return this.inlineToHtml(node.content);
|
|
42816
|
+
return this.escapeText(node.text ?? "");
|
|
42817
|
+
}
|
|
42818
|
+
wrapMarks(text, marks) {
|
|
42819
|
+
if (!marks || marks.length === 0) return text;
|
|
42820
|
+
let open = "";
|
|
42821
|
+
let close = "";
|
|
42822
|
+
for (const mark of marks) {
|
|
42823
|
+
if (mark.type === "link") {
|
|
42824
|
+
const href = mark.attrs?.href ?? "";
|
|
42825
|
+
open += `<a href="${this.escapeAttr(href)}">`;
|
|
42826
|
+
close = `</a>${close}`;
|
|
42827
|
+
} else {
|
|
42828
|
+
const tag = MARK_TO_TAG[mark.type];
|
|
42829
|
+
if (tag) {
|
|
42830
|
+
open += `<${tag}>`;
|
|
42831
|
+
close = `</${tag}>${close}`;
|
|
42832
|
+
}
|
|
42833
|
+
}
|
|
42834
|
+
}
|
|
42835
|
+
return `${open}${text}${close}`;
|
|
42836
|
+
}
|
|
42837
|
+
// --- lists ----------------------------------------------------------------
|
|
42838
|
+
listToHtml(node) {
|
|
42839
|
+
const { tag, attrs } = this.listTag(node);
|
|
42840
|
+
const items = (node.content ?? []).map((item) => this.listItemToHtml(item, node.type === "taskList")).join("");
|
|
42841
|
+
return `<${tag}${attrs}>${items}</${tag}>`;
|
|
42842
|
+
}
|
|
42843
|
+
listTag(node) {
|
|
42844
|
+
const start = node.attrs?.start;
|
|
42845
|
+
const startAttr = typeof start === "number" && start !== 1 ? ` start="${start}"` : "";
|
|
42846
|
+
switch (node.type) {
|
|
42847
|
+
case "orderedList":
|
|
42848
|
+
return { tag: "ol", attrs: startAttr };
|
|
42849
|
+
case "orderedListRoman":
|
|
42850
|
+
return { tag: "ol", attrs: ` type="I"${startAttr}` };
|
|
42851
|
+
case "orderedListRomanLower":
|
|
42852
|
+
return { tag: "ol", attrs: ` type="i"${startAttr}` };
|
|
42853
|
+
case "letteredList":
|
|
42854
|
+
return { tag: "ol", attrs: ` type="A"${startAttr}` };
|
|
42855
|
+
case "letteredListLower":
|
|
42856
|
+
return { tag: "ol", attrs: ` type="a"${startAttr}` };
|
|
42857
|
+
case "noBulletList":
|
|
42858
|
+
return { tag: "ul", attrs: ' style="list-style:none"' };
|
|
42859
|
+
case "taskList":
|
|
42860
|
+
case "bulletList":
|
|
42861
|
+
default:
|
|
42862
|
+
return { tag: "ul", attrs: "" };
|
|
42863
|
+
}
|
|
42864
|
+
}
|
|
42865
|
+
listItemToHtml(item, task) {
|
|
42866
|
+
const inner = (item.content ?? []).map((b) => {
|
|
42867
|
+
if (b.type === "paragraph") return this.inlineToHtml(b.content ?? []);
|
|
42868
|
+
return this.blockToHtml(b);
|
|
42869
|
+
}).join("");
|
|
42870
|
+
if (task) {
|
|
42871
|
+
const checked = item.attrs?.checked ? " checked" : "";
|
|
42872
|
+
return `<li><input type="checkbox"${checked}>${inner}</li>`;
|
|
42873
|
+
}
|
|
42874
|
+
return `<li>${inner}</li>`;
|
|
42875
|
+
}
|
|
42876
|
+
// --- images ---------------------------------------------------------------
|
|
42877
|
+
imageToHtml(node) {
|
|
42878
|
+
const attrs = node.attrs ?? {};
|
|
42879
|
+
const parts = [];
|
|
42880
|
+
const src = attrs.src;
|
|
42881
|
+
parts.push(`src="${this.escapeAttr(typeof src === "string" ? src : "")}"`);
|
|
42882
|
+
if (typeof attrs.alt === "string") parts.push(`alt="${this.escapeAttr(attrs.alt)}"`);
|
|
42883
|
+
if (typeof attrs.title === "string") parts.push(`title="${this.escapeAttr(attrs.title)}"`);
|
|
42884
|
+
if (attrs.width != null) parts.push(`width="${attrs.width}"`);
|
|
42885
|
+
if (attrs.height != null) parts.push(`height="${attrs.height}"`);
|
|
42886
|
+
if (typeof attrs.class === "string" && attrs.class) {
|
|
42887
|
+
parts.push(`class="${this.escapeAttr(attrs.class)}"`);
|
|
42888
|
+
}
|
|
42889
|
+
return `<img ${parts.join(" ")}>`;
|
|
42890
|
+
}
|
|
42891
|
+
// --- escaping -------------------------------------------------------------
|
|
42892
|
+
escapeText(text) {
|
|
42893
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
42894
|
+
}
|
|
42895
|
+
escapeAttr(text) {
|
|
42896
|
+
return this.escapeText(text).replace(/"/g, """);
|
|
42897
|
+
}
|
|
42898
|
+
};
|
|
42899
|
+
|
|
40750
42900
|
// src/generator/json/JsonFileGenerator.ts
|
|
40751
42901
|
var import_node_fs4 = require("fs");
|
|
40752
42902
|
var JsonFileGenerator = class {
|
|
@@ -41245,6 +43395,13 @@ var ConfigBuilder = class {
|
|
|
41245
43395
|
const cardSetConfig = CARDSETS[cardSetKey];
|
|
41246
43396
|
if (!cardSetConfig) return void 0;
|
|
41247
43397
|
const normalizedKey = normalizeCardKey(cardSetKey);
|
|
43398
|
+
const variantTagEntriesTypeOrder = [
|
|
43399
|
+
BitTagConfigKeyType.group,
|
|
43400
|
+
BitTagConfigKeyType.tag,
|
|
43401
|
+
BitTagConfigKeyType.property,
|
|
43402
|
+
BitTagConfigKeyType.resource,
|
|
43403
|
+
BitTagConfigKeyType.unknown
|
|
43404
|
+
];
|
|
41248
43405
|
const sides = cardSetConfig.sides.map((side, sideIdx) => {
|
|
41249
43406
|
const sidePath = `cardSets.${normalizedKey} / sides.${side.name ?? `[${sideIdx}]`}`;
|
|
41250
43407
|
const variants = side.variants.map((variant, variantIdx) => {
|
|
@@ -41255,7 +43412,7 @@ var ConfigBuilder = class {
|
|
|
41255
43412
|
const tagB = b[1];
|
|
41256
43413
|
const typeA = typeFromConfigKey(tagA.key);
|
|
41257
43414
|
const typeB = typeFromConfigKey(tagB.key);
|
|
41258
|
-
const typeOrder =
|
|
43415
|
+
const typeOrder = variantTagEntriesTypeOrder.indexOf(typeA) - variantTagEntriesTypeOrder.indexOf(typeB);
|
|
41259
43416
|
return typeOrder;
|
|
41260
43417
|
});
|
|
41261
43418
|
for (const [, variantTag] of variantTagEntries) {
|
|
@@ -41295,6 +43452,10 @@ var ConfigBuilder = class {
|
|
|
41295
43452
|
name: sectionName,
|
|
41296
43453
|
...section.isDefault ? { isDefault: true } : {},
|
|
41297
43454
|
...cardJsonKeyField(section, "jsonKey", "exportJsonKey", sectionPath),
|
|
43455
|
+
// PLAN-085: per-section cardinality. Emit only when non-default
|
|
43456
|
+
// (treat `0` / undefined as "unbounded — omit").
|
|
43457
|
+
...section.minCount != null && section.minCount !== 0 ? { min: section.minCount } : {},
|
|
43458
|
+
...section.maxCount != null && section.maxCount !== 0 ? { max: section.maxCount } : {},
|
|
41298
43459
|
sides: cardSides
|
|
41299
43460
|
};
|
|
41300
43461
|
});
|
|
@@ -41364,10 +43525,20 @@ var ConfigBuilder = class {
|
|
|
41364
43525
|
return typeOrder;
|
|
41365
43526
|
});
|
|
41366
43527
|
const isInheritedFrom = bitGroupConfigKeys.has(b.bitType);
|
|
43528
|
+
const indexByKey = /* @__PURE__ */ new Map();
|
|
43529
|
+
const pushOrReplace = (entry) => {
|
|
43530
|
+
const existing = indexByKey.get(entry.key);
|
|
43531
|
+
if (existing !== void 0) {
|
|
43532
|
+
tags2[existing] = entry;
|
|
43533
|
+
} else {
|
|
43534
|
+
indexByKey.set(entry.key, tags2.length);
|
|
43535
|
+
tags2.push(entry);
|
|
43536
|
+
}
|
|
43537
|
+
};
|
|
41367
43538
|
if (isInheritedFrom) {
|
|
41368
43539
|
const resolvedGroups = resolveGroupReferences(`group-${b.bitType}`);
|
|
41369
43540
|
for (const ref of resolvedGroups) {
|
|
41370
|
-
|
|
43541
|
+
pushOrReplace({
|
|
41371
43542
|
type: "group",
|
|
41372
43543
|
key: ref.key,
|
|
41373
43544
|
...ref.hasExportJsonKey ? { jsonKey: ref.exportJsonKey } : {},
|
|
@@ -41380,7 +43551,7 @@ var ConfigBuilder = class {
|
|
|
41380
43551
|
if (b.baseBitType) {
|
|
41381
43552
|
const resolvedGroups = resolveGroupReferences(`group-${b.baseBitType}`);
|
|
41382
43553
|
for (const ref of resolvedGroups) {
|
|
41383
|
-
|
|
43554
|
+
pushOrReplace({
|
|
41384
43555
|
type: "group",
|
|
41385
43556
|
key: ref.key,
|
|
41386
43557
|
...ref.hasExportJsonKey ? { jsonKey: ref.exportJsonKey } : {},
|
|
@@ -41391,7 +43562,9 @@ var ConfigBuilder = class {
|
|
|
41391
43562
|
}
|
|
41392
43563
|
}
|
|
41393
43564
|
for (const [_tagKey, tag] of tagEntries) {
|
|
41394
|
-
|
|
43565
|
+
for (const entry of processTagEntries(tag, [])) {
|
|
43566
|
+
pushOrReplace(entry);
|
|
43567
|
+
}
|
|
41395
43568
|
}
|
|
41396
43569
|
}
|
|
41397
43570
|
let cardRef;
|
|
@@ -41700,6 +43873,499 @@ var ConfigBuilder = class {
|
|
|
41700
43873
|
}
|
|
41701
43874
|
};
|
|
41702
43875
|
|
|
43876
|
+
// src/parser/html/HtmlDom.ts
|
|
43877
|
+
var VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
43878
|
+
"area",
|
|
43879
|
+
"base",
|
|
43880
|
+
"br",
|
|
43881
|
+
"col",
|
|
43882
|
+
"embed",
|
|
43883
|
+
"hr",
|
|
43884
|
+
"img",
|
|
43885
|
+
"input",
|
|
43886
|
+
"link",
|
|
43887
|
+
"meta",
|
|
43888
|
+
"param",
|
|
43889
|
+
"source",
|
|
43890
|
+
"track",
|
|
43891
|
+
"wbr"
|
|
43892
|
+
]);
|
|
43893
|
+
var IMPLIED_CLOSE = {
|
|
43894
|
+
li: /* @__PURE__ */ new Set(["li"]),
|
|
43895
|
+
td: /* @__PURE__ */ new Set(["td", "th"]),
|
|
43896
|
+
th: /* @__PURE__ */ new Set(["td", "th"]),
|
|
43897
|
+
tr: /* @__PURE__ */ new Set(["td", "th", "tr"]),
|
|
43898
|
+
thead: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
|
|
43899
|
+
tbody: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
|
|
43900
|
+
tfoot: /* @__PURE__ */ new Set(["td", "th", "tr", "thead", "tbody", "tfoot"]),
|
|
43901
|
+
p: /* @__PURE__ */ new Set(["p"]),
|
|
43902
|
+
option: /* @__PURE__ */ new Set(["option"])
|
|
43903
|
+
};
|
|
43904
|
+
var TAG_RE = /<(\/)?([a-zA-Z][a-zA-Z0-9-]*)((?:\s+(?:"[^"]*"|'[^']*'|[^<>"'])*)?)(\/)?>/g;
|
|
43905
|
+
var ATTR_RE = /([a-zA-Z_:][a-zA-Z0-9:._-]*)(?:\s*=\s*("[^"]*"|'[^']*'|[^\s"'>]+))?/g;
|
|
43906
|
+
function parseAttrs(raw) {
|
|
43907
|
+
const attrs = {};
|
|
43908
|
+
if (!raw) return attrs;
|
|
43909
|
+
ATTR_RE.lastIndex = 0;
|
|
43910
|
+
let m;
|
|
43911
|
+
while ((m = ATTR_RE.exec(raw)) !== null) {
|
|
43912
|
+
const name = m[1].toLowerCase();
|
|
43913
|
+
let value = m[2] ?? "";
|
|
43914
|
+
if (value.length >= 2 && (value[0] === '"' || value[0] === "'")) {
|
|
43915
|
+
value = value.slice(1, -1);
|
|
43916
|
+
}
|
|
43917
|
+
attrs[name] = value;
|
|
43918
|
+
}
|
|
43919
|
+
return attrs;
|
|
43920
|
+
}
|
|
43921
|
+
function stripNonContent(html) {
|
|
43922
|
+
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, "");
|
|
43923
|
+
}
|
|
43924
|
+
function tokenize(html) {
|
|
43925
|
+
const tokens = [];
|
|
43926
|
+
const clean = stripNonContent(html);
|
|
43927
|
+
TAG_RE.lastIndex = 0;
|
|
43928
|
+
let lastIndex = 0;
|
|
43929
|
+
let m;
|
|
43930
|
+
while ((m = TAG_RE.exec(clean)) !== null) {
|
|
43931
|
+
if (m.index > lastIndex) {
|
|
43932
|
+
tokens.push({ kind: "text", text: clean.slice(lastIndex, m.index) });
|
|
43933
|
+
}
|
|
43934
|
+
lastIndex = TAG_RE.lastIndex;
|
|
43935
|
+
const isClose = m[1] === "/";
|
|
43936
|
+
const name = m[2].toLowerCase();
|
|
43937
|
+
if (isClose) {
|
|
43938
|
+
tokens.push({ kind: "close", name });
|
|
43939
|
+
} else {
|
|
43940
|
+
tokens.push({
|
|
43941
|
+
kind: "open",
|
|
43942
|
+
name,
|
|
43943
|
+
attrs: parseAttrs(m[3]),
|
|
43944
|
+
selfClose: m[4] === "/"
|
|
43945
|
+
});
|
|
43946
|
+
}
|
|
43947
|
+
}
|
|
43948
|
+
if (lastIndex < clean.length) {
|
|
43949
|
+
tokens.push({ kind: "text", text: clean.slice(lastIndex) });
|
|
43950
|
+
}
|
|
43951
|
+
return tokens;
|
|
43952
|
+
}
|
|
43953
|
+
function parseHtml(html) {
|
|
43954
|
+
const root = { type: "element", name: "#root", attrs: {}, children: [] };
|
|
43955
|
+
const stack = [root];
|
|
43956
|
+
const top = () => stack[stack.length - 1];
|
|
43957
|
+
for (const token of tokenize(html)) {
|
|
43958
|
+
if (token.kind === "text") {
|
|
43959
|
+
if (token.text) top().children.push({ type: "text", value: token.text });
|
|
43960
|
+
continue;
|
|
43961
|
+
}
|
|
43962
|
+
if (token.kind === "open") {
|
|
43963
|
+
const name = token.name;
|
|
43964
|
+
const closes = IMPLIED_CLOSE[name];
|
|
43965
|
+
if (closes) {
|
|
43966
|
+
while (stack.length > 1 && closes.has(top().name)) stack.pop();
|
|
43967
|
+
}
|
|
43968
|
+
const el = {
|
|
43969
|
+
type: "element",
|
|
43970
|
+
name,
|
|
43971
|
+
attrs: token.attrs ?? {},
|
|
43972
|
+
children: []
|
|
43973
|
+
};
|
|
43974
|
+
top().children.push(el);
|
|
43975
|
+
if (!token.selfClose && !VOID_ELEMENTS.has(name)) {
|
|
43976
|
+
stack.push(el);
|
|
43977
|
+
}
|
|
43978
|
+
continue;
|
|
43979
|
+
}
|
|
43980
|
+
for (let i = stack.length - 1; i > 0; i--) {
|
|
43981
|
+
if (stack[i].name === token.name) {
|
|
43982
|
+
stack.length = i;
|
|
43983
|
+
break;
|
|
43984
|
+
}
|
|
43985
|
+
}
|
|
43986
|
+
}
|
|
43987
|
+
return root.children;
|
|
43988
|
+
}
|
|
43989
|
+
function childElements(el, names) {
|
|
43990
|
+
return el.children.filter((c) => c.type === "element" && names.has(c.name));
|
|
43991
|
+
}
|
|
43992
|
+
|
|
43993
|
+
// src/parser/html/HtmlTableParser.ts
|
|
43994
|
+
var MARK_TAGS = {
|
|
43995
|
+
b: "bold",
|
|
43996
|
+
strong: "bold",
|
|
43997
|
+
i: "italic",
|
|
43998
|
+
em: "italic",
|
|
43999
|
+
sup: "superscript",
|
|
44000
|
+
sub: "subscript",
|
|
44001
|
+
s: "strike",
|
|
44002
|
+
strike: "strike",
|
|
44003
|
+
del: "del",
|
|
44004
|
+
ins: "ins",
|
|
44005
|
+
u: "underline",
|
|
44006
|
+
mark: "highlight",
|
|
44007
|
+
code: "code"
|
|
44008
|
+
};
|
|
44009
|
+
var SECTION_TAGS = /* @__PURE__ */ new Set(["thead", "tbody", "tfoot"]);
|
|
44010
|
+
var ROW_TAGS = /* @__PURE__ */ new Set(["tr"]);
|
|
44011
|
+
var CELL_TAGS = /* @__PURE__ */ new Set(["td", "th"]);
|
|
44012
|
+
var LI_TAGS = /* @__PURE__ */ new Set(["li"]);
|
|
44013
|
+
var VALID_SCOPES = /* @__PURE__ */ new Set(["row", "col", "rowgroup", "colgroup"]);
|
|
44014
|
+
var NAMED_ENTITIES = {
|
|
44015
|
+
amp: "&",
|
|
44016
|
+
lt: "<",
|
|
44017
|
+
gt: ">",
|
|
44018
|
+
quot: '"',
|
|
44019
|
+
apos: "'",
|
|
44020
|
+
nbsp: "\xA0"
|
|
44021
|
+
};
|
|
44022
|
+
function decodeEntities(text) {
|
|
44023
|
+
return text.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z][a-zA-Z0-9]*);/g, (whole, body) => {
|
|
44024
|
+
if (body[0] === "#") {
|
|
44025
|
+
const code = body[1] === "x" || body[1] === "X" ? Number.parseInt(body.slice(2), 16) : Number.parseInt(body.slice(1), 10);
|
|
44026
|
+
if (Number.isFinite(code) && code >= 0 && code <= 1114111) {
|
|
44027
|
+
try {
|
|
44028
|
+
return String.fromCodePoint(code);
|
|
44029
|
+
} catch {
|
|
44030
|
+
return whole;
|
|
44031
|
+
}
|
|
44032
|
+
}
|
|
44033
|
+
return whole;
|
|
44034
|
+
}
|
|
44035
|
+
return NAMED_ENTITIES[body] ?? whole;
|
|
44036
|
+
});
|
|
44037
|
+
}
|
|
44038
|
+
function intAttr(value) {
|
|
44039
|
+
if (value == null) return void 0;
|
|
44040
|
+
const n = Number.parseInt(value, 10);
|
|
44041
|
+
return Number.isInteger(n) ? n : void 0;
|
|
44042
|
+
}
|
|
44043
|
+
function isElement(node) {
|
|
44044
|
+
return node.type === "element";
|
|
44045
|
+
}
|
|
44046
|
+
var HtmlTableParser = class {
|
|
44047
|
+
constructor() {
|
|
44048
|
+
__publicField(this, "keepUnknownTags", false);
|
|
44049
|
+
}
|
|
44050
|
+
/**
|
|
44051
|
+
* Parse an HTML string and return one table bit per top-level `<table>`.
|
|
44052
|
+
*/
|
|
44053
|
+
parse(html, options) {
|
|
44054
|
+
this.keepUnknownTags = options?.keepUnknownTags ?? false;
|
|
44055
|
+
const tableFormat = options?.tableFormat ?? "table-extended";
|
|
44056
|
+
const onWarning = options?.onWarning;
|
|
44057
|
+
const tables = this.collectTopLevelTables(parseHtml(html));
|
|
44058
|
+
const bits = [];
|
|
44059
|
+
for (const table of tables) {
|
|
44060
|
+
const bit = this.tableToBit(table, tableFormat, onWarning);
|
|
44061
|
+
if (bit) bits.push(bit);
|
|
44062
|
+
}
|
|
44063
|
+
return bits;
|
|
44064
|
+
}
|
|
44065
|
+
// --- table extraction -----------------------------------------------------
|
|
44066
|
+
/** Collect every `<table>` element, NOT descending into nested tables. */
|
|
44067
|
+
collectTopLevelTables(nodes) {
|
|
44068
|
+
const tables = [];
|
|
44069
|
+
const visit = (node) => {
|
|
44070
|
+
if (!isElement(node)) return;
|
|
44071
|
+
if (node.name === "table") {
|
|
44072
|
+
tables.push(node);
|
|
44073
|
+
return;
|
|
44074
|
+
}
|
|
44075
|
+
node.children.forEach(visit);
|
|
44076
|
+
};
|
|
44077
|
+
nodes.forEach(visit);
|
|
44078
|
+
return tables;
|
|
44079
|
+
}
|
|
44080
|
+
tableToBit(table, tableFormat, onWarning) {
|
|
44081
|
+
const header = [];
|
|
44082
|
+
const body = [];
|
|
44083
|
+
const footer = [];
|
|
44084
|
+
let caption;
|
|
44085
|
+
for (const child of table.children) {
|
|
44086
|
+
if (!isElement(child)) continue;
|
|
44087
|
+
if (child.name === "caption") {
|
|
44088
|
+
const capContent = this.inlineNodes(child.children, []);
|
|
44089
|
+
const trimmed = this.trimInline(capContent);
|
|
44090
|
+
if (trimmed.length > 0) caption = trimmed;
|
|
44091
|
+
} else if (SECTION_TAGS.has(child.name)) {
|
|
44092
|
+
const target = child.name === "thead" ? header : child.name === "tfoot" ? footer : body;
|
|
44093
|
+
for (const tr of childElements(child, ROW_TAGS)) {
|
|
44094
|
+
target.push(this.rowToCells(tr));
|
|
44095
|
+
}
|
|
44096
|
+
} else if (ROW_TAGS.has(child.name)) {
|
|
44097
|
+
body.push(this.rowToCells(child));
|
|
44098
|
+
}
|
|
44099
|
+
}
|
|
44100
|
+
if (header.length === 0 && body.length === 0 && footer.length === 0) {
|
|
44101
|
+
return void 0;
|
|
44102
|
+
}
|
|
44103
|
+
const tableExtended = {};
|
|
44104
|
+
if (header.length > 0) tableExtended.header = { rows: header };
|
|
44105
|
+
if (body.length > 0) tableExtended.body = { rows: body };
|
|
44106
|
+
if (footer.length > 0) tableExtended.footer = { rows: footer };
|
|
44107
|
+
let tableData = tableExtended;
|
|
44108
|
+
if (tableFormat === "table") {
|
|
44109
|
+
if (this.isLossyAsBasic(tableExtended)) {
|
|
44110
|
+
onWarning?.(
|
|
44111
|
+
"Converting an HTML table to [.table] loses rowspan/colspan/scope and multi-section structure; use --tableFormat table-extended to preserve them."
|
|
44112
|
+
);
|
|
44113
|
+
}
|
|
44114
|
+
tableData = convertExtendedToBasicTableFormat(tableExtended);
|
|
44115
|
+
}
|
|
44116
|
+
const bit = {
|
|
44117
|
+
type: tableFormat,
|
|
44118
|
+
format: "bitmark++",
|
|
44119
|
+
table: tableData
|
|
44120
|
+
};
|
|
44121
|
+
if (caption) bit.caption = caption;
|
|
44122
|
+
return bit;
|
|
44123
|
+
}
|
|
44124
|
+
rowToCells(tr) {
|
|
44125
|
+
const cells = [];
|
|
44126
|
+
for (const cell of childElements(tr, CELL_TAGS)) {
|
|
44127
|
+
cells.push(this.cellToJson(cell));
|
|
44128
|
+
}
|
|
44129
|
+
return { cells };
|
|
44130
|
+
}
|
|
44131
|
+
cellToJson(cell) {
|
|
44132
|
+
const json = {
|
|
44133
|
+
content: this.blockNodes(cell.children)
|
|
44134
|
+
};
|
|
44135
|
+
if (cell.name === "th") json.title = true;
|
|
44136
|
+
const rowspan = intAttr(cell.attrs.rowspan);
|
|
44137
|
+
if (rowspan != null && rowspan >= 2) json.rowspan = rowspan;
|
|
44138
|
+
const colspan = intAttr(cell.attrs.colspan);
|
|
44139
|
+
if (colspan != null && colspan >= 2) json.colspan = colspan;
|
|
44140
|
+
const scope = cell.attrs.scope?.toLowerCase();
|
|
44141
|
+
if (scope && VALID_SCOPES.has(scope)) {
|
|
44142
|
+
json.scope = scope;
|
|
44143
|
+
}
|
|
44144
|
+
const width = this.widthFromCell(cell);
|
|
44145
|
+
if (width != null) json.colwidth = width;
|
|
44146
|
+
return json;
|
|
44147
|
+
}
|
|
44148
|
+
widthFromCell(cell) {
|
|
44149
|
+
const attrWidth = intAttr(cell.attrs.width);
|
|
44150
|
+
if (attrWidth != null && attrWidth > 0) return attrWidth;
|
|
44151
|
+
const style = cell.attrs.style;
|
|
44152
|
+
if (style) {
|
|
44153
|
+
const m = style.match(/width\s*:\s*(\d+)\s*px/i);
|
|
44154
|
+
if (m) {
|
|
44155
|
+
const n = Number.parseInt(m[1], 10);
|
|
44156
|
+
if (Number.isInteger(n) && n > 0) return n;
|
|
44157
|
+
}
|
|
44158
|
+
}
|
|
44159
|
+
return void 0;
|
|
44160
|
+
}
|
|
44161
|
+
isLossyAsBasic(table) {
|
|
44162
|
+
if (table.footer) return true;
|
|
44163
|
+
if (table.header && table.header.rows.length > 1) return true;
|
|
44164
|
+
const sections = [table.header, table.body, table.footer];
|
|
44165
|
+
for (const section of sections) {
|
|
44166
|
+
if (!section) continue;
|
|
44167
|
+
for (const row of section.rows) {
|
|
44168
|
+
for (const cell of row.cells) {
|
|
44169
|
+
if (cell.rowspan || cell.colspan || cell.scope) return true;
|
|
44170
|
+
}
|
|
44171
|
+
}
|
|
44172
|
+
}
|
|
44173
|
+
return false;
|
|
44174
|
+
}
|
|
44175
|
+
// --- content conversion (HTML -> TextAst) ---------------------------------
|
|
44176
|
+
/**
|
|
44177
|
+
* Convert a cell/li's child nodes into block-level TextAst nodes (paragraphs, lists, images).
|
|
44178
|
+
*/
|
|
44179
|
+
blockNodes(nodes) {
|
|
44180
|
+
const blocks = [];
|
|
44181
|
+
let inline = [];
|
|
44182
|
+
const flush = () => {
|
|
44183
|
+
const trimmed = this.trimInline(inline);
|
|
44184
|
+
inline = [];
|
|
44185
|
+
if (trimmed.length === 0) return;
|
|
44186
|
+
if (trimmed.length === 1 && trimmed[0].type === "imageInline") {
|
|
44187
|
+
blocks.push({ ...trimmed[0], type: "image" });
|
|
44188
|
+
} else {
|
|
44189
|
+
blocks.push({ type: "paragraph", content: trimmed, attrs: {} });
|
|
44190
|
+
}
|
|
44191
|
+
};
|
|
44192
|
+
for (const node of nodes) {
|
|
44193
|
+
if (!isElement(node)) {
|
|
44194
|
+
inline.push(...this.inlineText(node.value, []));
|
|
44195
|
+
continue;
|
|
44196
|
+
}
|
|
44197
|
+
const name = node.name;
|
|
44198
|
+
if (name === "p") {
|
|
44199
|
+
flush();
|
|
44200
|
+
const content = this.trimInline(this.inlineNodes(node.children, []));
|
|
44201
|
+
if (content.length > 0) {
|
|
44202
|
+
blocks.push({ type: "paragraph", content, attrs: {} });
|
|
44203
|
+
}
|
|
44204
|
+
} else if (this.isListTag(name)) {
|
|
44205
|
+
flush();
|
|
44206
|
+
const list = this.listNode(node);
|
|
44207
|
+
if (list) blocks.push(list);
|
|
44208
|
+
} else {
|
|
44209
|
+
inline.push(...this.inlineFromElement(node, []));
|
|
44210
|
+
}
|
|
44211
|
+
}
|
|
44212
|
+
flush();
|
|
44213
|
+
return blocks;
|
|
44214
|
+
}
|
|
44215
|
+
/** Convert child nodes to a flat list of inline TextNodes, applying the given marks. */
|
|
44216
|
+
inlineNodes(nodes, marks) {
|
|
44217
|
+
const out = [];
|
|
44218
|
+
for (const node of nodes) {
|
|
44219
|
+
if (!isElement(node)) {
|
|
44220
|
+
out.push(...this.inlineText(node.value, marks));
|
|
44221
|
+
} else {
|
|
44222
|
+
out.push(...this.inlineFromElement(node, marks));
|
|
44223
|
+
}
|
|
44224
|
+
}
|
|
44225
|
+
return out;
|
|
44226
|
+
}
|
|
44227
|
+
inlineFromElement(el, marks) {
|
|
44228
|
+
const name = el.name;
|
|
44229
|
+
if (name === "br") {
|
|
44230
|
+
return [{ type: "hardBreak" }];
|
|
44231
|
+
}
|
|
44232
|
+
if (name === "img") {
|
|
44233
|
+
return [this.imageNode(el, true)];
|
|
44234
|
+
}
|
|
44235
|
+
const markType = MARK_TAGS[name];
|
|
44236
|
+
if (markType) {
|
|
44237
|
+
return this.inlineNodes(el.children, [...marks, { type: markType }]);
|
|
44238
|
+
}
|
|
44239
|
+
if (name === "a") {
|
|
44240
|
+
const linkMark = {
|
|
44241
|
+
type: "link",
|
|
44242
|
+
attrs: {
|
|
44243
|
+
href: el.attrs.href ?? "",
|
|
44244
|
+
...el.attrs.target ? { target: el.attrs.target } : {}
|
|
44245
|
+
}
|
|
44246
|
+
};
|
|
44247
|
+
return this.inlineNodes(el.children, [...marks, linkMark]);
|
|
44248
|
+
}
|
|
44249
|
+
if (this.keepUnknownTags) {
|
|
44250
|
+
const open = this.serializeOpenTag(el);
|
|
44251
|
+
const close = `</${name}>`;
|
|
44252
|
+
return [
|
|
44253
|
+
...this.inlineText(open, marks),
|
|
44254
|
+
...this.inlineNodes(el.children, marks),
|
|
44255
|
+
...this.inlineText(close, marks)
|
|
44256
|
+
];
|
|
44257
|
+
}
|
|
44258
|
+
return this.inlineNodes(el.children, marks);
|
|
44259
|
+
}
|
|
44260
|
+
inlineText(raw, marks) {
|
|
44261
|
+
const text = decodeEntities(raw).replace(/\s+/g, " ");
|
|
44262
|
+
if (text === "") return [];
|
|
44263
|
+
const node = { text, type: "text" };
|
|
44264
|
+
if (marks.length > 0) node.marks = marks.map((m) => ({ ...m }));
|
|
44265
|
+
return [node];
|
|
44266
|
+
}
|
|
44267
|
+
serializeOpenTag(el) {
|
|
44268
|
+
const attrs = Object.entries(el.attrs).map(([k, v]) => v === "" ? ` ${k}` : ` ${k}="${v}"`).join("");
|
|
44269
|
+
return `<${el.name}${attrs}>`;
|
|
44270
|
+
}
|
|
44271
|
+
// --- lists ----------------------------------------------------------------
|
|
44272
|
+
isListTag(name) {
|
|
44273
|
+
return name === "ul" || name === "ol";
|
|
44274
|
+
}
|
|
44275
|
+
listNode(el) {
|
|
44276
|
+
const items = childElements(el, LI_TAGS);
|
|
44277
|
+
if (items.length === 0) return void 0;
|
|
44278
|
+
const isTask = el.name === "ul" && items.some((li) => {
|
|
44279
|
+
const firstEl = li.children.find(isElement);
|
|
44280
|
+
return firstEl?.name === "input" && firstEl.attrs.type?.toLowerCase() === "checkbox";
|
|
44281
|
+
});
|
|
44282
|
+
if (isTask) {
|
|
44283
|
+
return {
|
|
44284
|
+
type: "taskList",
|
|
44285
|
+
content: items.map((li) => this.taskItemNode(li))
|
|
44286
|
+
};
|
|
44287
|
+
}
|
|
44288
|
+
const type = this.listType(el);
|
|
44289
|
+
const node = {
|
|
44290
|
+
type,
|
|
44291
|
+
content: items.map((li) => this.listItemNode(li))
|
|
44292
|
+
};
|
|
44293
|
+
if (el.name === "ol") {
|
|
44294
|
+
const start = intAttr(el.attrs.start);
|
|
44295
|
+
if (start != null) {
|
|
44296
|
+
node.attrs = { start };
|
|
44297
|
+
}
|
|
44298
|
+
}
|
|
44299
|
+
return node;
|
|
44300
|
+
}
|
|
44301
|
+
listType(el) {
|
|
44302
|
+
if (el.name === "ol") {
|
|
44303
|
+
switch (el.attrs.type) {
|
|
44304
|
+
case "i":
|
|
44305
|
+
return "orderedListRomanLower";
|
|
44306
|
+
case "I":
|
|
44307
|
+
return "orderedListRoman";
|
|
44308
|
+
case "a":
|
|
44309
|
+
return "letteredListLower";
|
|
44310
|
+
case "A":
|
|
44311
|
+
return "letteredList";
|
|
44312
|
+
default:
|
|
44313
|
+
return "orderedList";
|
|
44314
|
+
}
|
|
44315
|
+
}
|
|
44316
|
+
const style = el.attrs.style ?? "";
|
|
44317
|
+
if (/list-style(-type)?\s*:\s*none/i.test(style)) return "noBulletList";
|
|
44318
|
+
return "bulletList";
|
|
44319
|
+
}
|
|
44320
|
+
listItemNode(li) {
|
|
44321
|
+
let content = this.blockNodes(li.children);
|
|
44322
|
+
if (content.length === 0) {
|
|
44323
|
+
content = [{ type: "paragraph", content: [], attrs: {} }];
|
|
44324
|
+
}
|
|
44325
|
+
return { type: "listItem", content };
|
|
44326
|
+
}
|
|
44327
|
+
taskItemNode(li) {
|
|
44328
|
+
const checkbox = li.children.find(
|
|
44329
|
+
(c) => isElement(c) && c.name === "input" && c.attrs.type?.toLowerCase() === "checkbox"
|
|
44330
|
+
);
|
|
44331
|
+
const checked = checkbox ? "checked" in checkbox.attrs : false;
|
|
44332
|
+
const rest = li.children.filter((c) => c !== checkbox);
|
|
44333
|
+
let content = this.blockNodes(rest);
|
|
44334
|
+
if (content.length === 0) {
|
|
44335
|
+
content = [{ type: "paragraph", content: [], attrs: {} }];
|
|
44336
|
+
}
|
|
44337
|
+
return { type: "taskItem", attrs: { checked }, content };
|
|
44338
|
+
}
|
|
44339
|
+
// --- images ---------------------------------------------------------------
|
|
44340
|
+
imageNode(el, inline) {
|
|
44341
|
+
const attrs = { src: el.attrs.src ?? "" };
|
|
44342
|
+
attrs.alt = el.attrs.alt ?? null;
|
|
44343
|
+
attrs.title = el.attrs.title ?? null;
|
|
44344
|
+
const width = intAttr(el.attrs.width);
|
|
44345
|
+
if (width != null) attrs.width = width;
|
|
44346
|
+
const height = intAttr(el.attrs.height);
|
|
44347
|
+
if (height != null) attrs.height = height;
|
|
44348
|
+
if (el.attrs.class) attrs.class = el.attrs.class;
|
|
44349
|
+
return { type: inline ? "imageInline" : "image", attrs };
|
|
44350
|
+
}
|
|
44351
|
+
// --- whitespace -----------------------------------------------------------
|
|
44352
|
+
/** Trim leading/trailing whitespace from inline content (edges only). */
|
|
44353
|
+
trimInline(nodes) {
|
|
44354
|
+
const out = nodes.slice();
|
|
44355
|
+
const isWs = (n) => n.type === "text" && /^\s*$/.test(n.text ?? "");
|
|
44356
|
+
while (out.length > 0 && isWs(out[0])) out.shift();
|
|
44357
|
+
if (out.length > 0 && out[0].type === "text") {
|
|
44358
|
+
out[0] = { ...out[0], text: (out[0].text ?? "").replace(/^\s+/, "") };
|
|
44359
|
+
}
|
|
44360
|
+
while (out.length > 0 && isWs(out[out.length - 1])) out.pop();
|
|
44361
|
+
if (out.length > 0 && out[out.length - 1].type === "text") {
|
|
44362
|
+
const last = out.length - 1;
|
|
44363
|
+
out[last] = { ...out[last], text: (out[last].text ?? "").replace(/\s+$/, "") };
|
|
44364
|
+
}
|
|
44365
|
+
return out.filter((n) => !(n.type === "text" && n.text === ""));
|
|
44366
|
+
}
|
|
44367
|
+
};
|
|
44368
|
+
|
|
41703
44369
|
// src/BitmarkParserGenerator.ts
|
|
41704
44370
|
var Input = {
|
|
41705
44371
|
/**
|
|
@@ -41741,7 +44407,11 @@ var InputFormat = {
|
|
|
41741
44407
|
/**
|
|
41742
44408
|
* Input is plain text
|
|
41743
44409
|
*/
|
|
41744
|
-
plainText: "plainText"
|
|
44410
|
+
plainText: "plainText",
|
|
44411
|
+
/**
|
|
44412
|
+
* Input is HTML (used by convertHtmlTable)
|
|
44413
|
+
*/
|
|
44414
|
+
html: "html"
|
|
41745
44415
|
};
|
|
41746
44416
|
var BitmarkParserGenerator = class {
|
|
41747
44417
|
constructor() {
|
|
@@ -42276,6 +44946,87 @@ var BitmarkParserGenerator = class {
|
|
|
42276
44946
|
const res = generator.generate(data);
|
|
42277
44947
|
return res;
|
|
42278
44948
|
}
|
|
44949
|
+
/**
|
|
44950
|
+
* Convert between HTML tables and bitmark tables.
|
|
44951
|
+
*
|
|
44952
|
+
* Direction is auto-detected (override with `options.inputFormat`):
|
|
44953
|
+
* - HTML input: every `<table>` is extracted and emitted as a `[.table-extended]` (or `[.table]`)
|
|
44954
|
+
* bit; output as bitmark (default), JSON, AST, or HTML (re-emit).
|
|
44955
|
+
* - bitmark / JSON / AST input: each `table` / `table-extended` bit is rendered as an HTML
|
|
44956
|
+
* `<table>` fragment.
|
|
44957
|
+
*
|
|
44958
|
+
* Cell content is reproduced verbatim except inline conversion to bitmark marks (bold, italic,
|
|
44959
|
+
* superscript, etc.), with lists and images mapped to their bitmark equivalents.
|
|
44960
|
+
*
|
|
44961
|
+
* @param input - HTML, bitmark, JSON or AST string; or a parsed JSON/AST value; or a file path.
|
|
44962
|
+
* @param options - the conversion options
|
|
44963
|
+
* @returns the converted output as a string (or object for JSON/AST), or void when writing a file
|
|
44964
|
+
* @throws Error if any error occurs
|
|
44965
|
+
*/
|
|
44966
|
+
convertHtmlTable(input, options) {
|
|
44967
|
+
const opts = Object.assign({}, options);
|
|
44968
|
+
if (env.isBrowser && opts.outputFile) {
|
|
44969
|
+
throw new Error("Cannot write to file in browser environment");
|
|
44970
|
+
}
|
|
44971
|
+
let inStr = input;
|
|
44972
|
+
const inputIsString = typeof input === "string";
|
|
44973
|
+
if (env.isNode && inputIsString && import_fs_extra4.default.existsSync(inStr)) {
|
|
44974
|
+
inStr = import_fs_extra4.default.readFileSync(inStr, { encoding: "utf8" });
|
|
44975
|
+
}
|
|
44976
|
+
const isHtmlString = (s) => {
|
|
44977
|
+
const t = s.replace(/^\s+/, "");
|
|
44978
|
+
return t.startsWith("<") && /<table/i.test(t);
|
|
44979
|
+
};
|
|
44980
|
+
let htmlInput;
|
|
44981
|
+
if (opts.inputFormat === InputFormat.html) {
|
|
44982
|
+
htmlInput = true;
|
|
44983
|
+
} else if (opts.inputFormat != null) {
|
|
44984
|
+
htmlInput = false;
|
|
44985
|
+
} else {
|
|
44986
|
+
htmlInput = inputIsString && isHtmlString(inStr);
|
|
44987
|
+
}
|
|
44988
|
+
if (htmlInput) {
|
|
44989
|
+
const parser2 = new HtmlTableParser();
|
|
44990
|
+
const bits = parser2.parse(inStr, {
|
|
44991
|
+
tableFormat: opts.tableFormat ?? "table-extended",
|
|
44992
|
+
keepUnknownTags: opts.keepUnknownTags,
|
|
44993
|
+
onWarning: (m) => console.warn(m)
|
|
44994
|
+
});
|
|
44995
|
+
const outputFormat2 = opts.outputFormat ?? Output.bitmark;
|
|
44996
|
+
if (outputFormat2 === "html") {
|
|
44997
|
+
const json2 = this.convert(bits, { outputFormat: Output.json });
|
|
44998
|
+
return this.outputHtml(new HtmlTableGenerator().generate(json2), opts);
|
|
44999
|
+
}
|
|
45000
|
+
return this.convert(bits, {
|
|
45001
|
+
outputFormat: (0, import_superenum51.Enum)(Output).fromValue(outputFormat2),
|
|
45002
|
+
outputFile: opts.outputFile,
|
|
45003
|
+
fileOptions: opts.fileOptions,
|
|
45004
|
+
jsonOptions: opts.jsonOptions,
|
|
45005
|
+
bitmarkOptions: opts.bitmarkOptions
|
|
45006
|
+
});
|
|
45007
|
+
}
|
|
45008
|
+
const outputFormat = opts.outputFormat ?? "html";
|
|
45009
|
+
if (outputFormat !== "html") {
|
|
45010
|
+
throw new Error(
|
|
45011
|
+
"convertHtmlTable: non-HTML input can only be converted to HTML. Use convert() for bitmark/JSON/AST conversions."
|
|
45012
|
+
);
|
|
45013
|
+
}
|
|
45014
|
+
const json = this.convert(inputIsString ? inStr : input, { outputFormat: Output.json });
|
|
45015
|
+
return this.outputHtml(new HtmlTableGenerator().generate(json), opts);
|
|
45016
|
+
}
|
|
45017
|
+
/**
|
|
45018
|
+
* Write an HTML string to the configured output file, or return it.
|
|
45019
|
+
*/
|
|
45020
|
+
outputHtml(html, opts) {
|
|
45021
|
+
if (opts.outputFile) {
|
|
45022
|
+
const output = opts.outputFile.toString();
|
|
45023
|
+
const flag = opts.fileOptions?.append ? "a" : "w";
|
|
45024
|
+
import_fs_extra4.default.ensureDirSync(import_node_path4.default.dirname(output));
|
|
45025
|
+
import_fs_extra4.default.writeFileSync(output, html, { flag });
|
|
45026
|
+
return;
|
|
45027
|
+
}
|
|
45028
|
+
return html;
|
|
45029
|
+
}
|
|
42279
45030
|
/**
|
|
42280
45031
|
* Breakscape bitmark text.
|
|
42281
45032
|
*
|
|
@@ -42572,6 +45323,8 @@ init();
|
|
|
42572
45323
|
Builder,
|
|
42573
45324
|
CardSetVersion,
|
|
42574
45325
|
FileWriter,
|
|
45326
|
+
HtmlTableGenerator,
|
|
45327
|
+
HtmlTableParser,
|
|
42575
45328
|
InfoFormat,
|
|
42576
45329
|
InfoType,
|
|
42577
45330
|
Input,
|