paperlint 14.4.0 → 14.6.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/skills/build-label-page/scripts/label-page-core.d.ts +435 -1
- package/package.json +1 -1
- package/skills/build-label-page/SKILL.md +21 -1
- package/skills/build-label-page/SKILL.md.spec.ts +20 -0
- package/skills/build-label-page/references/closed-choice-generator.md +104 -0
- package/skills/build-label-page/scripts/fixtures/v7-cards-ru.json +23 -0
- package/skills/build-label-page/scripts/label-page-browser.test.ts +344 -1
- package/skills/build-label-page/scripts/label-page-core.js +217 -17
- package/skills/build-label-page/scripts/label-page-core.test.ts +68 -0
- package/skills/build-label-page/scripts/label-page-core.ts +312 -17
- package/skills/build-label-page/scripts/label-page-sources.test.ts +231 -0
- package/skills/build-label-page/scripts/label-page-translations.test.ts +421 -0
- package/skills/build-label-page/scripts/page-template.html +91 -29
|
@@ -105,6 +105,7 @@ interface Blocks {
|
|
|
105
105
|
readonly situation?: {
|
|
106
106
|
readonly summary?: string;
|
|
107
107
|
readonly question: string;
|
|
108
|
+
readonly from?: string;
|
|
108
109
|
};
|
|
109
110
|
readonly excerpt?: {
|
|
110
111
|
readonly file?: string;
|
|
@@ -115,10 +116,13 @@ interface Blocks {
|
|
|
115
116
|
readonly annotation?: string;
|
|
116
117
|
readonly caption?: string;
|
|
117
118
|
readonly mark_label?: string;
|
|
119
|
+
readonly from?: string;
|
|
120
|
+
readonly caption_from?: string;
|
|
118
121
|
};
|
|
119
122
|
readonly quote?: {
|
|
120
123
|
readonly text: string;
|
|
121
124
|
readonly source?: string;
|
|
125
|
+
readonly from?: string;
|
|
122
126
|
};
|
|
123
127
|
readonly terms?: readonly Term[];
|
|
124
128
|
readonly answer_notes?: Readonly<Record<string, Readonly<Record<string, string>>>>;
|
|
@@ -193,8 +197,50 @@ interface Config {
|
|
|
193
197
|
readonly practice: readonly Practice[];
|
|
194
198
|
readonly practice_round?: number;
|
|
195
199
|
readonly labels?: Readonly<Record<string, string>>;
|
|
200
|
+
readonly sources?: Readonly<Record<string, string>>;
|
|
201
|
+
readonly lang?: string;
|
|
202
|
+
readonly translations?: Readonly<Record<string, Translation>>;
|
|
196
203
|
readonly items: readonly Item[];
|
|
197
204
|
}
|
|
205
|
+
interface QuestionWords {
|
|
206
|
+
readonly text: string;
|
|
207
|
+
readonly short?: string;
|
|
208
|
+
readonly options: Readonly<Record<string, {
|
|
209
|
+
readonly label: string;
|
|
210
|
+
readonly example: string;
|
|
211
|
+
readonly ask_why?: string;
|
|
212
|
+
}>>;
|
|
213
|
+
}
|
|
214
|
+
/** The page's own words in one more language (#394). */
|
|
215
|
+
interface Translation {
|
|
216
|
+
readonly title?: string;
|
|
217
|
+
readonly intro?: string;
|
|
218
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
219
|
+
readonly situation_question?: string;
|
|
220
|
+
readonly sources?: Readonly<Record<string, string>>;
|
|
221
|
+
readonly questions: Readonly<Record<string, QuestionWords>>;
|
|
222
|
+
}
|
|
223
|
+
/** The colours the page has for tags (`.tag.s0` to `.tag.s4` in page-template.html), in `sources` order. */
|
|
224
|
+
export declare const SOURCE_COLOURS = 5;
|
|
225
|
+
/**
|
|
226
|
+
* Every language of the page with its words whole, the translations checked: each covers every
|
|
227
|
+
* question and answer of the page and names none it does not have, and gives an intro, a reason
|
|
228
|
+
* prompt and a situation question exactly where the page shows one. A label it leaves out reads as
|
|
229
|
+
* in the page's own language.
|
|
230
|
+
*/
|
|
231
|
+
declare function translationsOf(cfg: Config): {
|
|
232
|
+
[x: string]: {
|
|
233
|
+
questions: Question[];
|
|
234
|
+
situation_question?: string;
|
|
235
|
+
sources?: Readonly<Record<string, string>>;
|
|
236
|
+
title: string;
|
|
237
|
+
intro: string;
|
|
238
|
+
labels: {
|
|
239
|
+
[x: string]: string;
|
|
240
|
+
};
|
|
241
|
+
};
|
|
242
|
+
};
|
|
243
|
+
type Translated = ReturnType<typeof translationsOf>;
|
|
198
244
|
interface KeyEntry {
|
|
199
245
|
readonly source_id: string;
|
|
200
246
|
readonly group: string;
|
|
@@ -212,7 +258,61 @@ declare function itemsOnPage(cfg: Config, keyPath: string): {
|
|
|
212
258
|
idOf: (i: Item) => string;
|
|
213
259
|
};
|
|
214
260
|
/** What a page shows: the questions, the practice and the items, blind, with their neutral ids. */
|
|
215
|
-
declare function payloadOf(cfg: Config, page: ReturnType<typeof itemsOnPage>, described: Described): {
|
|
261
|
+
declare function payloadOf(cfg: Config, page: ReturnType<typeof itemsOnPage>, described: Described, translated: Translated): {
|
|
262
|
+
questions: readonly Question[];
|
|
263
|
+
practice: {
|
|
264
|
+
round: number;
|
|
265
|
+
items: ({
|
|
266
|
+
rules?: never;
|
|
267
|
+
id: string;
|
|
268
|
+
expected: Readonly<Record<string, string>>;
|
|
269
|
+
why: string;
|
|
270
|
+
} | {
|
|
271
|
+
rules: {
|
|
272
|
+
description?: string;
|
|
273
|
+
family?: string;
|
|
274
|
+
linter: "eslint" | "ruff";
|
|
275
|
+
id: string;
|
|
276
|
+
}[];
|
|
277
|
+
id: string;
|
|
278
|
+
expected: Readonly<Record<string, string>>;
|
|
279
|
+
why: string;
|
|
280
|
+
})[];
|
|
281
|
+
};
|
|
282
|
+
items: ({
|
|
283
|
+
answerers?: readonly string[];
|
|
284
|
+
rules?: never;
|
|
285
|
+
id: string;
|
|
286
|
+
} | {
|
|
287
|
+
answerers?: readonly string[];
|
|
288
|
+
rules: {
|
|
289
|
+
description?: string;
|
|
290
|
+
family?: string;
|
|
291
|
+
linter: "eslint" | "ruff";
|
|
292
|
+
id: string;
|
|
293
|
+
}[];
|
|
294
|
+
id: string;
|
|
295
|
+
})[];
|
|
296
|
+
lang?: string;
|
|
297
|
+
answerers?: readonly string[];
|
|
298
|
+
min_seconds?: number;
|
|
299
|
+
groups: {
|
|
300
|
+
id: string;
|
|
301
|
+
label: string;
|
|
302
|
+
}[];
|
|
303
|
+
familiarity_question: string;
|
|
304
|
+
familiarity: {
|
|
305
|
+
value: string;
|
|
306
|
+
label: string;
|
|
307
|
+
}[];
|
|
308
|
+
allowed: JsonObject;
|
|
309
|
+
sources?: never;
|
|
310
|
+
title: string;
|
|
311
|
+
intro: string;
|
|
312
|
+
labels: {
|
|
313
|
+
[x: string]: string;
|
|
314
|
+
};
|
|
315
|
+
} | {
|
|
216
316
|
questions: readonly Question[];
|
|
217
317
|
practice: {
|
|
218
318
|
round: number;
|
|
@@ -247,13 +347,144 @@ declare function payloadOf(cfg: Config, page: ReturnType<typeof itemsOnPage>, de
|
|
|
247
347
|
}[];
|
|
248
348
|
id: string;
|
|
249
349
|
})[];
|
|
350
|
+
lang: string;
|
|
351
|
+
translations: {
|
|
352
|
+
[x: string]: {
|
|
353
|
+
questions: Question[];
|
|
354
|
+
situation_question?: string;
|
|
355
|
+
sources?: Readonly<Record<string, string>>;
|
|
356
|
+
title: string;
|
|
357
|
+
intro: string;
|
|
358
|
+
labels: {
|
|
359
|
+
[x: string]: string;
|
|
360
|
+
};
|
|
361
|
+
};
|
|
362
|
+
};
|
|
250
363
|
answerers?: readonly string[];
|
|
251
364
|
min_seconds?: number;
|
|
365
|
+
groups: {
|
|
366
|
+
id: string;
|
|
367
|
+
label: string;
|
|
368
|
+
}[];
|
|
369
|
+
familiarity_question: string;
|
|
370
|
+
familiarity: {
|
|
371
|
+
value: string;
|
|
372
|
+
label: string;
|
|
373
|
+
}[];
|
|
374
|
+
allowed: JsonObject;
|
|
375
|
+
sources?: never;
|
|
252
376
|
title: string;
|
|
253
377
|
intro: string;
|
|
254
378
|
labels: {
|
|
255
379
|
[x: string]: string;
|
|
256
380
|
};
|
|
381
|
+
} | {
|
|
382
|
+
questions: readonly Question[];
|
|
383
|
+
practice: {
|
|
384
|
+
round: number;
|
|
385
|
+
items: ({
|
|
386
|
+
rules?: never;
|
|
387
|
+
id: string;
|
|
388
|
+
expected: Readonly<Record<string, string>>;
|
|
389
|
+
why: string;
|
|
390
|
+
} | {
|
|
391
|
+
rules: {
|
|
392
|
+
description?: string;
|
|
393
|
+
family?: string;
|
|
394
|
+
linter: "eslint" | "ruff";
|
|
395
|
+
id: string;
|
|
396
|
+
}[];
|
|
397
|
+
id: string;
|
|
398
|
+
expected: Readonly<Record<string, string>>;
|
|
399
|
+
why: string;
|
|
400
|
+
})[];
|
|
401
|
+
};
|
|
402
|
+
items: ({
|
|
403
|
+
answerers?: readonly string[];
|
|
404
|
+
rules?: never;
|
|
405
|
+
id: string;
|
|
406
|
+
} | {
|
|
407
|
+
answerers?: readonly string[];
|
|
408
|
+
rules: {
|
|
409
|
+
description?: string;
|
|
410
|
+
family?: string;
|
|
411
|
+
linter: "eslint" | "ruff";
|
|
412
|
+
id: string;
|
|
413
|
+
}[];
|
|
414
|
+
id: string;
|
|
415
|
+
})[];
|
|
416
|
+
lang?: string;
|
|
417
|
+
answerers?: readonly string[];
|
|
418
|
+
min_seconds?: number;
|
|
419
|
+
groups: {
|
|
420
|
+
id: string;
|
|
421
|
+
label: string;
|
|
422
|
+
}[];
|
|
423
|
+
familiarity_question: string;
|
|
424
|
+
familiarity: {
|
|
425
|
+
value: string;
|
|
426
|
+
label: string;
|
|
427
|
+
}[];
|
|
428
|
+
allowed: JsonObject;
|
|
429
|
+
sources: {
|
|
430
|
+
id: string;
|
|
431
|
+
label: string;
|
|
432
|
+
}[];
|
|
433
|
+
title: string;
|
|
434
|
+
intro: string;
|
|
435
|
+
labels: {
|
|
436
|
+
[x: string]: string;
|
|
437
|
+
};
|
|
438
|
+
} | {
|
|
439
|
+
questions: readonly Question[];
|
|
440
|
+
practice: {
|
|
441
|
+
round: number;
|
|
442
|
+
items: ({
|
|
443
|
+
rules?: never;
|
|
444
|
+
id: string;
|
|
445
|
+
expected: Readonly<Record<string, string>>;
|
|
446
|
+
why: string;
|
|
447
|
+
} | {
|
|
448
|
+
rules: {
|
|
449
|
+
description?: string;
|
|
450
|
+
family?: string;
|
|
451
|
+
linter: "eslint" | "ruff";
|
|
452
|
+
id: string;
|
|
453
|
+
}[];
|
|
454
|
+
id: string;
|
|
455
|
+
expected: Readonly<Record<string, string>>;
|
|
456
|
+
why: string;
|
|
457
|
+
})[];
|
|
458
|
+
};
|
|
459
|
+
items: ({
|
|
460
|
+
answerers?: readonly string[];
|
|
461
|
+
rules?: never;
|
|
462
|
+
id: string;
|
|
463
|
+
} | {
|
|
464
|
+
answerers?: readonly string[];
|
|
465
|
+
rules: {
|
|
466
|
+
description?: string;
|
|
467
|
+
family?: string;
|
|
468
|
+
linter: "eslint" | "ruff";
|
|
469
|
+
id: string;
|
|
470
|
+
}[];
|
|
471
|
+
id: string;
|
|
472
|
+
})[];
|
|
473
|
+
lang: string;
|
|
474
|
+
translations: {
|
|
475
|
+
[x: string]: {
|
|
476
|
+
questions: Question[];
|
|
477
|
+
situation_question?: string;
|
|
478
|
+
sources?: Readonly<Record<string, string>>;
|
|
479
|
+
title: string;
|
|
480
|
+
intro: string;
|
|
481
|
+
labels: {
|
|
482
|
+
[x: string]: string;
|
|
483
|
+
};
|
|
484
|
+
};
|
|
485
|
+
};
|
|
486
|
+
answerers?: readonly string[];
|
|
487
|
+
min_seconds?: number;
|
|
257
488
|
groups: {
|
|
258
489
|
id: string;
|
|
259
490
|
label: string;
|
|
@@ -264,6 +495,15 @@ declare function payloadOf(cfg: Config, page: ReturnType<typeof itemsOnPage>, de
|
|
|
264
495
|
label: string;
|
|
265
496
|
}[];
|
|
266
497
|
allowed: JsonObject;
|
|
498
|
+
sources: {
|
|
499
|
+
id: string;
|
|
500
|
+
label: string;
|
|
501
|
+
}[];
|
|
502
|
+
title: string;
|
|
503
|
+
intro: string;
|
|
504
|
+
labels: {
|
|
505
|
+
[x: string]: string;
|
|
506
|
+
};
|
|
267
507
|
};
|
|
268
508
|
export type Payload = ReturnType<typeof payloadOf>;
|
|
269
509
|
/**
|
|
@@ -307,13 +547,75 @@ export declare function prepareBuild(configPath: string, outDir: string): {
|
|
|
307
547
|
}[];
|
|
308
548
|
id: string;
|
|
309
549
|
})[];
|
|
550
|
+
lang?: string;
|
|
310
551
|
answerers?: readonly string[];
|
|
311
552
|
min_seconds?: number;
|
|
553
|
+
groups: {
|
|
554
|
+
id: string;
|
|
555
|
+
label: string;
|
|
556
|
+
}[];
|
|
557
|
+
familiarity_question: string;
|
|
558
|
+
familiarity: {
|
|
559
|
+
value: string;
|
|
560
|
+
label: string;
|
|
561
|
+
}[];
|
|
562
|
+
allowed: JsonObject;
|
|
563
|
+
sources?: never;
|
|
312
564
|
title: string;
|
|
313
565
|
intro: string;
|
|
314
566
|
labels: {
|
|
315
567
|
[x: string]: string;
|
|
316
568
|
};
|
|
569
|
+
} | {
|
|
570
|
+
questions: readonly Question[];
|
|
571
|
+
practice: {
|
|
572
|
+
round: number;
|
|
573
|
+
items: ({
|
|
574
|
+
rules?: never;
|
|
575
|
+
id: string;
|
|
576
|
+
expected: Readonly<Record<string, string>>;
|
|
577
|
+
why: string;
|
|
578
|
+
} | {
|
|
579
|
+
rules: {
|
|
580
|
+
description?: string;
|
|
581
|
+
family?: string;
|
|
582
|
+
linter: "eslint" | "ruff";
|
|
583
|
+
id: string;
|
|
584
|
+
}[];
|
|
585
|
+
id: string;
|
|
586
|
+
expected: Readonly<Record<string, string>>;
|
|
587
|
+
why: string;
|
|
588
|
+
})[];
|
|
589
|
+
};
|
|
590
|
+
items: ({
|
|
591
|
+
answerers?: readonly string[];
|
|
592
|
+
rules?: never;
|
|
593
|
+
id: string;
|
|
594
|
+
} | {
|
|
595
|
+
answerers?: readonly string[];
|
|
596
|
+
rules: {
|
|
597
|
+
description?: string;
|
|
598
|
+
family?: string;
|
|
599
|
+
linter: "eslint" | "ruff";
|
|
600
|
+
id: string;
|
|
601
|
+
}[];
|
|
602
|
+
id: string;
|
|
603
|
+
})[];
|
|
604
|
+
lang: string;
|
|
605
|
+
translations: {
|
|
606
|
+
[x: string]: {
|
|
607
|
+
questions: Question[];
|
|
608
|
+
situation_question?: string;
|
|
609
|
+
sources?: Readonly<Record<string, string>>;
|
|
610
|
+
title: string;
|
|
611
|
+
intro: string;
|
|
612
|
+
labels: {
|
|
613
|
+
[x: string]: string;
|
|
614
|
+
};
|
|
615
|
+
};
|
|
616
|
+
};
|
|
617
|
+
answerers?: readonly string[];
|
|
618
|
+
min_seconds?: number;
|
|
317
619
|
groups: {
|
|
318
620
|
id: string;
|
|
319
621
|
label: string;
|
|
@@ -324,6 +626,138 @@ export declare function prepareBuild(configPath: string, outDir: string): {
|
|
|
324
626
|
label: string;
|
|
325
627
|
}[];
|
|
326
628
|
allowed: JsonObject;
|
|
629
|
+
sources?: never;
|
|
630
|
+
title: string;
|
|
631
|
+
intro: string;
|
|
632
|
+
labels: {
|
|
633
|
+
[x: string]: string;
|
|
634
|
+
};
|
|
635
|
+
} | {
|
|
636
|
+
questions: readonly Question[];
|
|
637
|
+
practice: {
|
|
638
|
+
round: number;
|
|
639
|
+
items: ({
|
|
640
|
+
rules?: never;
|
|
641
|
+
id: string;
|
|
642
|
+
expected: Readonly<Record<string, string>>;
|
|
643
|
+
why: string;
|
|
644
|
+
} | {
|
|
645
|
+
rules: {
|
|
646
|
+
description?: string;
|
|
647
|
+
family?: string;
|
|
648
|
+
linter: "eslint" | "ruff";
|
|
649
|
+
id: string;
|
|
650
|
+
}[];
|
|
651
|
+
id: string;
|
|
652
|
+
expected: Readonly<Record<string, string>>;
|
|
653
|
+
why: string;
|
|
654
|
+
})[];
|
|
655
|
+
};
|
|
656
|
+
items: ({
|
|
657
|
+
answerers?: readonly string[];
|
|
658
|
+
rules?: never;
|
|
659
|
+
id: string;
|
|
660
|
+
} | {
|
|
661
|
+
answerers?: readonly string[];
|
|
662
|
+
rules: {
|
|
663
|
+
description?: string;
|
|
664
|
+
family?: string;
|
|
665
|
+
linter: "eslint" | "ruff";
|
|
666
|
+
id: string;
|
|
667
|
+
}[];
|
|
668
|
+
id: string;
|
|
669
|
+
})[];
|
|
670
|
+
lang?: string;
|
|
671
|
+
answerers?: readonly string[];
|
|
672
|
+
min_seconds?: number;
|
|
673
|
+
groups: {
|
|
674
|
+
id: string;
|
|
675
|
+
label: string;
|
|
676
|
+
}[];
|
|
677
|
+
familiarity_question: string;
|
|
678
|
+
familiarity: {
|
|
679
|
+
value: string;
|
|
680
|
+
label: string;
|
|
681
|
+
}[];
|
|
682
|
+
allowed: JsonObject;
|
|
683
|
+
sources: {
|
|
684
|
+
id: string;
|
|
685
|
+
label: string;
|
|
686
|
+
}[];
|
|
687
|
+
title: string;
|
|
688
|
+
intro: string;
|
|
689
|
+
labels: {
|
|
690
|
+
[x: string]: string;
|
|
691
|
+
};
|
|
692
|
+
} | {
|
|
693
|
+
questions: readonly Question[];
|
|
694
|
+
practice: {
|
|
695
|
+
round: number;
|
|
696
|
+
items: ({
|
|
697
|
+
rules?: never;
|
|
698
|
+
id: string;
|
|
699
|
+
expected: Readonly<Record<string, string>>;
|
|
700
|
+
why: string;
|
|
701
|
+
} | {
|
|
702
|
+
rules: {
|
|
703
|
+
description?: string;
|
|
704
|
+
family?: string;
|
|
705
|
+
linter: "eslint" | "ruff";
|
|
706
|
+
id: string;
|
|
707
|
+
}[];
|
|
708
|
+
id: string;
|
|
709
|
+
expected: Readonly<Record<string, string>>;
|
|
710
|
+
why: string;
|
|
711
|
+
})[];
|
|
712
|
+
};
|
|
713
|
+
items: ({
|
|
714
|
+
answerers?: readonly string[];
|
|
715
|
+
rules?: never;
|
|
716
|
+
id: string;
|
|
717
|
+
} | {
|
|
718
|
+
answerers?: readonly string[];
|
|
719
|
+
rules: {
|
|
720
|
+
description?: string;
|
|
721
|
+
family?: string;
|
|
722
|
+
linter: "eslint" | "ruff";
|
|
723
|
+
id: string;
|
|
724
|
+
}[];
|
|
725
|
+
id: string;
|
|
726
|
+
})[];
|
|
727
|
+
lang: string;
|
|
728
|
+
translations: {
|
|
729
|
+
[x: string]: {
|
|
730
|
+
questions: Question[];
|
|
731
|
+
situation_question?: string;
|
|
732
|
+
sources?: Readonly<Record<string, string>>;
|
|
733
|
+
title: string;
|
|
734
|
+
intro: string;
|
|
735
|
+
labels: {
|
|
736
|
+
[x: string]: string;
|
|
737
|
+
};
|
|
738
|
+
};
|
|
739
|
+
};
|
|
740
|
+
answerers?: readonly string[];
|
|
741
|
+
min_seconds?: number;
|
|
742
|
+
groups: {
|
|
743
|
+
id: string;
|
|
744
|
+
label: string;
|
|
745
|
+
}[];
|
|
746
|
+
familiarity_question: string;
|
|
747
|
+
familiarity: {
|
|
748
|
+
value: string;
|
|
749
|
+
label: string;
|
|
750
|
+
}[];
|
|
751
|
+
allowed: JsonObject;
|
|
752
|
+
sources: {
|
|
753
|
+
id: string;
|
|
754
|
+
label: string;
|
|
755
|
+
}[];
|
|
756
|
+
title: string;
|
|
757
|
+
intro: string;
|
|
758
|
+
labels: {
|
|
759
|
+
[x: string]: string;
|
|
760
|
+
};
|
|
327
761
|
};
|
|
328
762
|
dropped: number;
|
|
329
763
|
};
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ description: Build a phone labelling page published as a claude.ai Artifact with
|
|
|
4
4
|
allowed-tools: [Read, Write, Edit, Grep, Glob, Bash, Artifact, ArtifactData]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
<!-- vigiles:sha256:
|
|
7
|
+
<!-- vigiles:sha256:c5863cfb5f123f19 compiled from skills/build-label-page/SKILL.md.spec.ts -->
|
|
8
8
|
|
|
9
9
|
# build-label-page — a page the person labels on a phone, and the labels back in the repo
|
|
10
10
|
|
|
@@ -158,6 +158,26 @@ Two optional top-level keys of `items.json`, both left out by default:
|
|
|
158
158
|
Details and the export's shape: [`references/closed-choice-generator.md`](references/closed-choice-generator.md),
|
|
159
159
|
"Two answerers, and a minimum time on screen".
|
|
160
160
|
|
|
161
|
+
## A second language
|
|
162
|
+
|
|
163
|
+
A labeller who reads another language better gets the page's own words in it: `"translations": {"ru":
|
|
164
|
+
{…}}` (and `"lang"`, the page's own, `en` by default) adds a switch under the title. The questions,
|
|
165
|
+
answers, examples, reason prompts, intro and labels change; the quote, the code and every note never
|
|
166
|
+
do, because those words are what is judged. The build refuses a translation that leaves out or
|
|
167
|
+
invents a question or an answer; the ids and `key.json` are the same with or without it.
|
|
168
|
+
[`references/closed-choice-generator.md`](references/closed-choice-generator.md), "A second
|
|
169
|
+
language for the page's own words".
|
|
170
|
+
|
|
171
|
+
## A source tag above each block
|
|
172
|
+
|
|
173
|
+
When the labeller must tell where a block's text came from (copied from a repository, written by the
|
|
174
|
+
model under test, restated by an earlier labelling model, counted by the script), the page's
|
|
175
|
+
`"sources": {"repo": "From the repository", …}` names the tags, in colour order (at most five), and
|
|
176
|
+
`from` on a `quote`, a `situation` (its summary) or an `excerpt` (`caption_from` for its caption)
|
|
177
|
+
puts one above that block. The build refuses an unknown id, a dead entry and a tag over a text the card
|
|
178
|
+
lacks. [`references/closed-choice-generator.md`](references/closed-choice-generator.md), "A source tag
|
|
179
|
+
above each block".
|
|
180
|
+
|
|
161
181
|
## When the page misbehaves
|
|
162
182
|
|
|
163
183
|
Look at what the store holds (`ArtifactData` `list`) before changing anything: the page may be fine
|
|
@@ -169,6 +169,26 @@ Two optional top-level keys of \`items.json\`, both left out by default:
|
|
|
169
169
|
Details and the export's shape: [\`references/closed-choice-generator.md\`](references/closed-choice-generator.md),
|
|
170
170
|
"Two answerers, and a minimum time on screen".
|
|
171
171
|
|
|
172
|
+
## A second language
|
|
173
|
+
|
|
174
|
+
A labeller who reads another language better gets the page's own words in it: \`"translations": {"ru":
|
|
175
|
+
{…}}\` (and \`"lang"\`, the page's own, \`en\` by default) adds a switch under the title. The questions,
|
|
176
|
+
answers, examples, reason prompts, intro and labels change; the quote, the code and every note never
|
|
177
|
+
do, because those words are what is judged. The build refuses a translation that leaves out or
|
|
178
|
+
invents a question or an answer; the ids and \`key.json\` are the same with or without it.
|
|
179
|
+
[\`references/closed-choice-generator.md\`](references/closed-choice-generator.md), "A second
|
|
180
|
+
language for the page's own words".
|
|
181
|
+
|
|
182
|
+
## A source tag above each block
|
|
183
|
+
|
|
184
|
+
When the labeller must tell where a block's text came from (copied from a repository, written by the
|
|
185
|
+
model under test, restated by an earlier labelling model, counted by the script), the page's
|
|
186
|
+
\`"sources": {"repo": "From the repository", …}\` names the tags, in colour order (at most five), and
|
|
187
|
+
\`from\` on a \`quote\`, a \`situation\` (its summary) or an \`excerpt\` (\`caption_from\` for its caption)
|
|
188
|
+
puts one above that block. The build refuses an unknown id, a dead entry and a tag over a text the card
|
|
189
|
+
lacks. [\`references/closed-choice-generator.md\`](references/closed-choice-generator.md), "A source tag
|
|
190
|
+
above each block".
|
|
191
|
+
|
|
172
192
|
## When the page misbehaves
|
|
173
193
|
|
|
174
194
|
Look at what the store holds (\`ArtifactData\` \`list\`) before changing anything: the page may be fine
|
|
@@ -229,6 +229,8 @@ Any card may carry these blocks, on any `kind`, and each one is optional. The pa
|
|
|
229
229
|
| `hidden` | a `⋮` row for lines left out | `lines {from}–{to}` |
|
|
230
230
|
| `hidden_one` | a `⋮` row for one line left out | `line {from}` |
|
|
231
231
|
| `wait` | the bar under the answers while `min_seconds` runs | `⏳ You can answer in {s} s` |
|
|
232
|
+
| `how_to` | the heading of the first screen's questions and examples | `📖 How to answer` |
|
|
233
|
+
| `for_example` | text before each example on the first screen | `For example: ` |
|
|
232
234
|
|
|
233
235
|
- The leak guard reads every string these blocks render, one string at a time.
|
|
234
236
|
|
|
@@ -286,6 +288,108 @@ author cannot read it is an exclusion made after the draw and goes unrecorded. T
|
|
|
286
288
|
units whose fate is obvious. Each dropped unit carries its reason in the labels file. The author's words, with the date, then go into
|
|
287
289
|
`author_check` of the plan; a dropped unit is replaced by the next one in the draw.
|
|
288
290
|
|
|
291
|
+
## A second language for the page's own words
|
|
292
|
+
|
|
293
|
+
For a labeller who reads another language better (#394). Both keys are optional and top-level; left
|
|
294
|
+
out, the page is exactly as above.
|
|
295
|
+
|
|
296
|
+
```jsonc
|
|
297
|
+
{
|
|
298
|
+
"lang": "en", // the language of the page's own words; "en" when left out
|
|
299
|
+
"translations": {
|
|
300
|
+
"ru": {
|
|
301
|
+
"title": "…", // optional: the page's title when left out
|
|
302
|
+
"intro": "…", // given exactly when the page has an `intro`
|
|
303
|
+
"labels": {
|
|
304
|
+
"place": "Карточка {n} из {total}",
|
|
305
|
+
"how_to": "📖 Как отвечать",
|
|
306
|
+
"for_example": "Например: ",
|
|
307
|
+
},
|
|
308
|
+
"situation_question": "Эти слова запрещают строку {n}?", // given exactly when a card has a `situation`
|
|
309
|
+
"questions": {
|
|
310
|
+
"forbid": {
|
|
311
|
+
"text": "…",
|
|
312
|
+
"short": "…", // optional: the translated `text` stands in for it
|
|
313
|
+
"options": {
|
|
314
|
+
"forbid": { "label": "Запрещают", "example": "…" },
|
|
315
|
+
"cannot": { "label": "…", "example": "…", "ask_why": "…" },
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
- **What changes with the language:** the title, the intro, the `labels`, every question's text and
|
|
325
|
+
short form, every answer's label and example, every reason prompt (`ask_why`), and the first question
|
|
326
|
+
as the cards with a `situation` ask it. The first screen's rows ("How to answer") change too.
|
|
327
|
+
- **What never does:** the quote and its source, the code, the annotation, the term notes, the
|
|
328
|
+
answer sub-lines (`answer_notes`), a situation's `summary`, and every other card field. Those words
|
|
329
|
+
are what the labeller judges; a translated quote would be a different item.
|
|
330
|
+
- **`situation_question`** replaces each card's own `situation.question` in that language. `{n}` is
|
|
331
|
+
the number of the card's first marked line; `{line}` works as in `situation`. Either one on a card
|
|
332
|
+
without an `excerpt` is refused by path.
|
|
333
|
+
- **Refused, by path:** a translation into the page's own `lang`; a question or an answer it leaves
|
|
334
|
+
out, or one the page does not have; two answers whose labels read the same; an `intro`, an
|
|
335
|
+
`ask_why` or a `situation_question` given where the page has none, or missing where it has one.
|
|
336
|
+
- **Left out on purpose:** a label the translation does not give reads as in the page's language.
|
|
337
|
+
The page's fixed words ("Practice", "Change", "Saved", "Next unanswered", "Who is answering?")
|
|
338
|
+
and the word «line» that `{line}` writes stay in English.
|
|
339
|
+
- **The switch** sits under the title (`data-test="lang-switch"`), one button per language with its
|
|
340
|
+
tag ("EN", "RU"). The choice is kept in the browser's localStorage under `label-lang` when it has
|
|
341
|
+
one.
|
|
342
|
+
- **Ids and key:** the neutral ids, the store's documents, `key.json` and the export are the same,
|
|
343
|
+
byte for byte, with or without translations.
|
|
344
|
+
|
|
345
|
+
## A source tag above each block
|
|
346
|
+
|
|
347
|
+
For a labeller who cannot tell where a block's text came from: some of it copied from a repository,
|
|
348
|
+
some written by a model under test, some by an earlier labelling model, some counted by the study's
|
|
349
|
+
script (#400). A small coloured tag above the block says which. All fields are optional; a page
|
|
350
|
+
without `sources` shows no tag, and its config, store documents and key are unchanged.
|
|
351
|
+
|
|
352
|
+
```jsonc
|
|
353
|
+
{
|
|
354
|
+
// id → the tag's label, in colour order (at most 5 ids: the page has five colours)
|
|
355
|
+
"sources": {
|
|
356
|
+
"repo": "From the repository",
|
|
357
|
+
"ours": "Our labelling model's restatement",
|
|
358
|
+
"model": "Written by the model under test",
|
|
359
|
+
"count": "Counted by our script",
|
|
360
|
+
},
|
|
361
|
+
"items": [
|
|
362
|
+
{
|
|
363
|
+
"quote": { "text": "…", "source": "…", "from": "repo" }, // tags the quote and its source line
|
|
364
|
+
"situation": { "summary": "…", "question": "…", "from": "ours" }, // tags the summary, not the question
|
|
365
|
+
"excerpt": {
|
|
366
|
+
"lines": [],
|
|
367
|
+
"caption": "…",
|
|
368
|
+
"from": "repo",
|
|
369
|
+
"caption_from": "count",
|
|
370
|
+
}, // the box, the caption under it
|
|
371
|
+
},
|
|
372
|
+
],
|
|
373
|
+
"translations": { "ru": { "sources": { "repo": "Из репозитория" } } },
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
- **`from`** names an id of `sources`, on `quote`, `situation` and `excerpt`; **`caption_from`**
|
|
378
|
+
on `excerpt` tags the caption. Practice items take the same fields. The question is the page's own
|
|
379
|
+
words and gets no tag.
|
|
380
|
+
- **On the page:** a small uppercase pill directly above the block. Its colour is the id's place in
|
|
381
|
+
`sources` (accent, green, amber, red, grey-blue: tokens the page already has). Each carries
|
|
382
|
+
`data-test="source-tag"` and `data-test-meta` `{"source": "<id>", "block": "quote" | "situation" |
|
|
383
|
+
"excerpt" | "caption"}`.
|
|
384
|
+
- **Refused, by path:** a `from` or `caption_from` with no `sources` on the page; one naming an id not
|
|
385
|
+
in `sources`; one over a text the card does not have (a `caption_from` without a `caption`, a
|
|
386
|
+
situation `from` without a `summary`); a `sources` id no item or practice item names; an empty
|
|
387
|
+
label; more than 5 ids; a `translations.<lang>.sources` id the page's `sources` lack.
|
|
388
|
+
- **Translations:** `translations.<lang>.sources` gives the tag labels in that language. A label it
|
|
389
|
+
leaves out reads as in the page's own language.
|
|
390
|
+
- **A tag is not a blind-page field:** it shows the labeller where text came from. Use it only where
|
|
391
|
+
the study allows the labeller to know; a study that blinds the source must not name one.
|
|
392
|
+
|
|
289
393
|
## Two answerers, and a minimum time on screen
|
|
290
394
|
|
|
291
395
|
Both keys are optional and top-level. Left out, the page, its store documents and the export are
|