@jokerized/decksmith 0.1.2 → 0.1.4
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/cli.js +51 -16
- package/dist/index.js +42 -11
- package/dist/mcp.js +50 -15
- package/dist/types/plan/duration.d.ts +65 -6
- package/dist/types/version.d.ts +1 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { cp, mkdir as mkdir7, readdir as readdir3, readFile as readFile13, stat, writeFile as writeFile8 } from "node:fs/promises";
|
|
5
|
-
import { createRequire as
|
|
5
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
6
6
|
import { dirname as dirname4, join as join12, relative as relative2, resolve as resolve3 } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { Command } from "commander";
|
|
@@ -1053,24 +1053,55 @@ var ADVANCE = {
|
|
|
1053
1053
|
var KERN_SLACK = 1.007;
|
|
1054
1054
|
var TABULAR_FIGURE = 0.649;
|
|
1055
1055
|
var TABULAR_SEPARATOR = 0.269;
|
|
1056
|
+
var BLOCK_ADVANCE = [
|
|
1057
|
+
[12288, 12351, 1],
|
|
1058
|
+
// CJK symbols and punctuation
|
|
1059
|
+
[12352, 12447, 1],
|
|
1060
|
+
// hiragana
|
|
1061
|
+
[12448, 12543, 1],
|
|
1062
|
+
// katakana
|
|
1063
|
+
[12592, 12687, 0.92],
|
|
1064
|
+
// Hangul compatibility jamo
|
|
1065
|
+
[13312, 19903, 1],
|
|
1066
|
+
// CJK unified ideographs, extension A
|
|
1067
|
+
[19968, 40959, 1],
|
|
1068
|
+
// CJK unified ideographs
|
|
1069
|
+
[44032, 55203, 0.92],
|
|
1070
|
+
// Hangul syllables
|
|
1071
|
+
[63744, 64255, 1],
|
|
1072
|
+
// CJK compatibility ideographs
|
|
1073
|
+
[65281, 65376, 1]
|
|
1074
|
+
// full-width forms
|
|
1075
|
+
];
|
|
1076
|
+
var UNMEASURED = 1.02;
|
|
1056
1077
|
function charUnits(c, tabular) {
|
|
1057
1078
|
if (tabular) {
|
|
1058
|
-
if (c >= "0" && c <= "9") return TABULAR_FIGURE;
|
|
1059
|
-
if (c === "." || c === ",") return TABULAR_SEPARATOR;
|
|
1079
|
+
if (c >= "0" && c <= "9") return [TABULAR_FIGURE, true];
|
|
1080
|
+
if (c === "." || c === ",") return [TABULAR_SEPARATOR, true];
|
|
1060
1081
|
}
|
|
1061
|
-
|
|
1082
|
+
const measured = ADVANCE[c];
|
|
1083
|
+
if (measured !== void 0) return [measured, true];
|
|
1084
|
+
const cp2 = c.codePointAt(0) ?? 0;
|
|
1085
|
+
for (const [lo, hi, units] of BLOCK_ADVANCE) if (cp2 >= lo && cp2 <= hi) return [units, false];
|
|
1086
|
+
return [UNMEASURED, true];
|
|
1062
1087
|
}
|
|
1063
1088
|
function weightFactor(weight) {
|
|
1064
1089
|
return weight >= 700 ? 1.045 : weight >= 600 ? 1.03 : weight >= 500 ? 1.015 : 1;
|
|
1065
1090
|
}
|
|
1066
1091
|
function textWidth(text2, fontSize, weight = 400, tracking = 0, tabular = false) {
|
|
1067
|
-
let
|
|
1092
|
+
let weighted = 0;
|
|
1093
|
+
let emGrid = 0;
|
|
1068
1094
|
let chars = 0;
|
|
1069
1095
|
for (const c of text2) {
|
|
1070
|
-
units
|
|
1096
|
+
const [units, scalesWithWeight] = charUnits(c, tabular);
|
|
1097
|
+
if (scalesWithWeight) weighted += units;
|
|
1098
|
+
else emGrid += units;
|
|
1071
1099
|
chars++;
|
|
1072
1100
|
}
|
|
1073
|
-
|
|
1101
|
+
if (emGrid === 0) {
|
|
1102
|
+
return weighted * KERN_SLACK * fontSize * weightFactor(weight) + tracking * fontSize * chars;
|
|
1103
|
+
}
|
|
1104
|
+
return (weighted * weightFactor(weight) + emGrid) * KERN_SLACK * fontSize + tracking * fontSize * chars;
|
|
1074
1105
|
}
|
|
1075
1106
|
function wrap(text2, fontSize, maxWidth, weight = 400, tracking = 0) {
|
|
1076
1107
|
if (maxWidth <= 0) return [text2];
|
|
@@ -4620,7 +4651,7 @@ var SENTENCES_PER_BEAT = 2;
|
|
|
4620
4651
|
var FF_BEAT_SECONDS = 8;
|
|
4621
4652
|
var MIN_BEAT_SECONDS = 4;
|
|
4622
4653
|
var SHORT_FORM_CPS = 22;
|
|
4623
|
-
var CUE_OVERHEAD = 1.
|
|
4654
|
+
var CUE_OVERHEAD = 1.3;
|
|
4624
4655
|
var COMFORTABLE_CPS = 17;
|
|
4625
4656
|
var MAX_PLAYBACK = 1.25;
|
|
4626
4657
|
var SPEAKING_STOPS = {
|
|
@@ -4630,10 +4661,10 @@ var SPEAKING_STOPS = {
|
|
|
4630
4661
|
};
|
|
4631
4662
|
var RATE_STEPS = [
|
|
4632
4663
|
["+0%", 1],
|
|
4633
|
-
["+10%", 1.
|
|
4634
|
-
["+20%", 1.
|
|
4635
|
-
["+30%", 1.
|
|
4636
|
-
["+40%", 1.
|
|
4664
|
+
["+10%", 1.086],
|
|
4665
|
+
["+20%", 1.182],
|
|
4666
|
+
["+30%", 1.278],
|
|
4667
|
+
["+40%", 1.393]
|
|
4637
4668
|
];
|
|
4638
4669
|
function durationPlan(prefs) {
|
|
4639
4670
|
const speakingStops = SPEAKING_STOPS[prefs.narration.density];
|
|
@@ -8340,6 +8371,10 @@ async function readCompositions(dir) {
|
|
|
8340
8371
|
);
|
|
8341
8372
|
}
|
|
8342
8373
|
|
|
8374
|
+
// src/version.ts
|
|
8375
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
8376
|
+
var VERSION = createRequire2(import.meta.url)("../package.json").version;
|
|
8377
|
+
|
|
8343
8378
|
// src/cli.ts
|
|
8344
8379
|
var AUDIO_DIR = "audio";
|
|
8345
8380
|
var NARRATION_FILE = "narration.json";
|
|
@@ -8353,7 +8388,7 @@ async function deckRuntime() {
|
|
|
8353
8388
|
}
|
|
8354
8389
|
}
|
|
8355
8390
|
function playerBundle() {
|
|
8356
|
-
const require2 =
|
|
8391
|
+
const require2 = createRequire3(import.meta.url);
|
|
8357
8392
|
try {
|
|
8358
8393
|
return join12(dirname4(require2.resolve("hyperframes/package.json")), "dist", PLAYER_FILE);
|
|
8359
8394
|
} catch {
|
|
@@ -8361,7 +8396,7 @@ function playerBundle() {
|
|
|
8361
8396
|
}
|
|
8362
8397
|
}
|
|
8363
8398
|
async function vendorKatex(out) {
|
|
8364
|
-
const require2 =
|
|
8399
|
+
const require2 = createRequire3(import.meta.url);
|
|
8365
8400
|
const dist = join12(dirname4(require2.resolve("katex/package.json")), "dist");
|
|
8366
8401
|
const css = await readFile13(join12(dist, "katex.min.css"), "utf8");
|
|
8367
8402
|
await mkdir7(join12(out, "katex/fonts"), { recursive: true });
|
|
@@ -8376,7 +8411,7 @@ async function vendorKatex(out) {
|
|
|
8376
8411
|
await writeFile8(join12(out, "katex/katex.min.css"), woff2Only);
|
|
8377
8412
|
}
|
|
8378
8413
|
async function vendorScripts(out) {
|
|
8379
|
-
const require2 =
|
|
8414
|
+
const require2 = createRequire3(import.meta.url);
|
|
8380
8415
|
await mkdir7(join12(out, "vendor"), { recursive: true });
|
|
8381
8416
|
for (const [pkg, rel, name] of [
|
|
8382
8417
|
["gsap/package.json", "dist/gsap.min.js", "gsap.min.js"],
|
|
@@ -8435,7 +8470,7 @@ function stated(prefs, key) {
|
|
|
8435
8470
|
return prefs[key] === DEFAULTS[key] ? void 0 : prefs[key];
|
|
8436
8471
|
}
|
|
8437
8472
|
var program = new Command();
|
|
8438
|
-
program.name("decksmith").description("Turn a source document into an animated explanation deck.").version(
|
|
8473
|
+
program.name("decksmith").description("Turn a source document into an animated explanation deck.").version(VERSION);
|
|
8439
8474
|
program.command("ingest").description("Parse a document into source.json, localising its figures and font.").argument("<input>", "source document (markdown)").requiredOption("-o, --out <file>", "where to write source.json").option("--lang <bcp47>", "override the sniffed language").action(async (input, o) => {
|
|
8440
8475
|
const md = await readFile13(resolve3(input), "utf8").catch(() => {
|
|
8441
8476
|
throw new Error(`Cannot read ${input}.`);
|
package/dist/index.js
CHANGED
|
@@ -1053,24 +1053,55 @@ var ADVANCE = {
|
|
|
1053
1053
|
var KERN_SLACK = 1.007;
|
|
1054
1054
|
var TABULAR_FIGURE = 0.649;
|
|
1055
1055
|
var TABULAR_SEPARATOR = 0.269;
|
|
1056
|
+
var BLOCK_ADVANCE = [
|
|
1057
|
+
[12288, 12351, 1],
|
|
1058
|
+
// CJK symbols and punctuation
|
|
1059
|
+
[12352, 12447, 1],
|
|
1060
|
+
// hiragana
|
|
1061
|
+
[12448, 12543, 1],
|
|
1062
|
+
// katakana
|
|
1063
|
+
[12592, 12687, 0.92],
|
|
1064
|
+
// Hangul compatibility jamo
|
|
1065
|
+
[13312, 19903, 1],
|
|
1066
|
+
// CJK unified ideographs, extension A
|
|
1067
|
+
[19968, 40959, 1],
|
|
1068
|
+
// CJK unified ideographs
|
|
1069
|
+
[44032, 55203, 0.92],
|
|
1070
|
+
// Hangul syllables
|
|
1071
|
+
[63744, 64255, 1],
|
|
1072
|
+
// CJK compatibility ideographs
|
|
1073
|
+
[65281, 65376, 1]
|
|
1074
|
+
// full-width forms
|
|
1075
|
+
];
|
|
1076
|
+
var UNMEASURED = 1.02;
|
|
1056
1077
|
function charUnits(c, tabular) {
|
|
1057
1078
|
if (tabular) {
|
|
1058
|
-
if (c >= "0" && c <= "9") return TABULAR_FIGURE;
|
|
1059
|
-
if (c === "." || c === ",") return TABULAR_SEPARATOR;
|
|
1079
|
+
if (c >= "0" && c <= "9") return [TABULAR_FIGURE, true];
|
|
1080
|
+
if (c === "." || c === ",") return [TABULAR_SEPARATOR, true];
|
|
1060
1081
|
}
|
|
1061
|
-
|
|
1082
|
+
const measured = ADVANCE[c];
|
|
1083
|
+
if (measured !== void 0) return [measured, true];
|
|
1084
|
+
const cp2 = c.codePointAt(0) ?? 0;
|
|
1085
|
+
for (const [lo, hi, units] of BLOCK_ADVANCE) if (cp2 >= lo && cp2 <= hi) return [units, false];
|
|
1086
|
+
return [UNMEASURED, true];
|
|
1062
1087
|
}
|
|
1063
1088
|
function weightFactor(weight) {
|
|
1064
1089
|
return weight >= 700 ? 1.045 : weight >= 600 ? 1.03 : weight >= 500 ? 1.015 : 1;
|
|
1065
1090
|
}
|
|
1066
1091
|
function textWidth(text2, fontSize, weight = 400, tracking = 0, tabular = false) {
|
|
1067
|
-
let
|
|
1092
|
+
let weighted = 0;
|
|
1093
|
+
let emGrid = 0;
|
|
1068
1094
|
let chars = 0;
|
|
1069
1095
|
for (const c of text2) {
|
|
1070
|
-
units
|
|
1096
|
+
const [units, scalesWithWeight] = charUnits(c, tabular);
|
|
1097
|
+
if (scalesWithWeight) weighted += units;
|
|
1098
|
+
else emGrid += units;
|
|
1071
1099
|
chars++;
|
|
1072
1100
|
}
|
|
1073
|
-
|
|
1101
|
+
if (emGrid === 0) {
|
|
1102
|
+
return weighted * KERN_SLACK * fontSize * weightFactor(weight) + tracking * fontSize * chars;
|
|
1103
|
+
}
|
|
1104
|
+
return (weighted * weightFactor(weight) + emGrid) * KERN_SLACK * fontSize + tracking * fontSize * chars;
|
|
1074
1105
|
}
|
|
1075
1106
|
function wrap(text2, fontSize, maxWidth, weight = 400, tracking = 0) {
|
|
1076
1107
|
if (maxWidth <= 0) return [text2];
|
|
@@ -5131,7 +5162,7 @@ var SENTENCES_PER_BEAT = 2;
|
|
|
5131
5162
|
var FF_BEAT_SECONDS = 8;
|
|
5132
5163
|
var MIN_BEAT_SECONDS = 4;
|
|
5133
5164
|
var SHORT_FORM_CPS = 22;
|
|
5134
|
-
var CUE_OVERHEAD = 1.
|
|
5165
|
+
var CUE_OVERHEAD = 1.3;
|
|
5135
5166
|
var COMFORTABLE_CPS = 17;
|
|
5136
5167
|
var MAX_PLAYBACK = 1.25;
|
|
5137
5168
|
var SPEAKING_STOPS = {
|
|
@@ -5141,10 +5172,10 @@ var SPEAKING_STOPS = {
|
|
|
5141
5172
|
};
|
|
5142
5173
|
var RATE_STEPS = [
|
|
5143
5174
|
["+0%", 1],
|
|
5144
|
-
["+10%", 1.
|
|
5145
|
-
["+20%", 1.
|
|
5146
|
-
["+30%", 1.
|
|
5147
|
-
["+40%", 1.
|
|
5175
|
+
["+10%", 1.086],
|
|
5176
|
+
["+20%", 1.182],
|
|
5177
|
+
["+30%", 1.278],
|
|
5178
|
+
["+40%", 1.393]
|
|
5148
5179
|
];
|
|
5149
5180
|
function durationPlan(prefs) {
|
|
5150
5181
|
const speakingStops = SPEAKING_STOPS[prefs.narration.density];
|
package/dist/mcp.js
CHANGED
|
@@ -7,6 +7,10 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
7
7
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
8
8
|
import { z as z4 } from "zod";
|
|
9
9
|
|
|
10
|
+
// src/version.ts
|
|
11
|
+
import { createRequire } from "node:module";
|
|
12
|
+
var VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
13
|
+
|
|
10
14
|
// src/mcp/tools.ts
|
|
11
15
|
import { randomBytes } from "node:crypto";
|
|
12
16
|
import { mkdir as mkdir9, readFile as readFile10 } from "node:fs/promises";
|
|
@@ -16,7 +20,7 @@ import { z as z3 } from "zod";
|
|
|
16
20
|
|
|
17
21
|
// src/index.ts
|
|
18
22
|
import { cp, mkdir as mkdir7, readdir, readFile as readFile8, stat, writeFile as writeFile8 } from "node:fs/promises";
|
|
19
|
-
import { createRequire } from "node:module";
|
|
23
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
20
24
|
import { dirname as dirname3, join as join7, resolve as resolve2 } from "node:path";
|
|
21
25
|
import { fileURLToPath } from "node:url";
|
|
22
26
|
|
|
@@ -1053,24 +1057,55 @@ var ADVANCE = {
|
|
|
1053
1057
|
var KERN_SLACK = 1.007;
|
|
1054
1058
|
var TABULAR_FIGURE = 0.649;
|
|
1055
1059
|
var TABULAR_SEPARATOR = 0.269;
|
|
1060
|
+
var BLOCK_ADVANCE = [
|
|
1061
|
+
[12288, 12351, 1],
|
|
1062
|
+
// CJK symbols and punctuation
|
|
1063
|
+
[12352, 12447, 1],
|
|
1064
|
+
// hiragana
|
|
1065
|
+
[12448, 12543, 1],
|
|
1066
|
+
// katakana
|
|
1067
|
+
[12592, 12687, 0.92],
|
|
1068
|
+
// Hangul compatibility jamo
|
|
1069
|
+
[13312, 19903, 1],
|
|
1070
|
+
// CJK unified ideographs, extension A
|
|
1071
|
+
[19968, 40959, 1],
|
|
1072
|
+
// CJK unified ideographs
|
|
1073
|
+
[44032, 55203, 0.92],
|
|
1074
|
+
// Hangul syllables
|
|
1075
|
+
[63744, 64255, 1],
|
|
1076
|
+
// CJK compatibility ideographs
|
|
1077
|
+
[65281, 65376, 1]
|
|
1078
|
+
// full-width forms
|
|
1079
|
+
];
|
|
1080
|
+
var UNMEASURED = 1.02;
|
|
1056
1081
|
function charUnits(c, tabular) {
|
|
1057
1082
|
if (tabular) {
|
|
1058
|
-
if (c >= "0" && c <= "9") return TABULAR_FIGURE;
|
|
1059
|
-
if (c === "." || c === ",") return TABULAR_SEPARATOR;
|
|
1083
|
+
if (c >= "0" && c <= "9") return [TABULAR_FIGURE, true];
|
|
1084
|
+
if (c === "." || c === ",") return [TABULAR_SEPARATOR, true];
|
|
1060
1085
|
}
|
|
1061
|
-
|
|
1086
|
+
const measured = ADVANCE[c];
|
|
1087
|
+
if (measured !== void 0) return [measured, true];
|
|
1088
|
+
const cp2 = c.codePointAt(0) ?? 0;
|
|
1089
|
+
for (const [lo, hi, units] of BLOCK_ADVANCE) if (cp2 >= lo && cp2 <= hi) return [units, false];
|
|
1090
|
+
return [UNMEASURED, true];
|
|
1062
1091
|
}
|
|
1063
1092
|
function weightFactor(weight) {
|
|
1064
1093
|
return weight >= 700 ? 1.045 : weight >= 600 ? 1.03 : weight >= 500 ? 1.015 : 1;
|
|
1065
1094
|
}
|
|
1066
1095
|
function textWidth(text2, fontSize, weight = 400, tracking = 0, tabular = false) {
|
|
1067
|
-
let
|
|
1096
|
+
let weighted = 0;
|
|
1097
|
+
let emGrid = 0;
|
|
1068
1098
|
let chars = 0;
|
|
1069
1099
|
for (const c of text2) {
|
|
1070
|
-
units
|
|
1100
|
+
const [units, scalesWithWeight] = charUnits(c, tabular);
|
|
1101
|
+
if (scalesWithWeight) weighted += units;
|
|
1102
|
+
else emGrid += units;
|
|
1071
1103
|
chars++;
|
|
1072
1104
|
}
|
|
1073
|
-
|
|
1105
|
+
if (emGrid === 0) {
|
|
1106
|
+
return weighted * KERN_SLACK * fontSize * weightFactor(weight) + tracking * fontSize * chars;
|
|
1107
|
+
}
|
|
1108
|
+
return (weighted * weightFactor(weight) + emGrid) * KERN_SLACK * fontSize + tracking * fontSize * chars;
|
|
1074
1109
|
}
|
|
1075
1110
|
function wrap(text2, fontSize, maxWidth, weight = 400, tracking = 0) {
|
|
1076
1111
|
if (maxWidth <= 0) return [text2];
|
|
@@ -5128,7 +5163,7 @@ var SENTENCES_PER_BEAT = 2;
|
|
|
5128
5163
|
var FF_BEAT_SECONDS = 8;
|
|
5129
5164
|
var MIN_BEAT_SECONDS = 4;
|
|
5130
5165
|
var SHORT_FORM_CPS = 22;
|
|
5131
|
-
var CUE_OVERHEAD = 1.
|
|
5166
|
+
var CUE_OVERHEAD = 1.3;
|
|
5132
5167
|
var COMFORTABLE_CPS = 17;
|
|
5133
5168
|
var MAX_PLAYBACK = 1.25;
|
|
5134
5169
|
var SPEAKING_STOPS = {
|
|
@@ -5138,10 +5173,10 @@ var SPEAKING_STOPS = {
|
|
|
5138
5173
|
};
|
|
5139
5174
|
var RATE_STEPS = [
|
|
5140
5175
|
["+0%", 1],
|
|
5141
|
-
["+10%", 1.
|
|
5142
|
-
["+20%", 1.
|
|
5143
|
-
["+30%", 1.
|
|
5144
|
-
["+40%", 1.
|
|
5176
|
+
["+10%", 1.086],
|
|
5177
|
+
["+20%", 1.182],
|
|
5178
|
+
["+30%", 1.278],
|
|
5179
|
+
["+40%", 1.393]
|
|
5145
5180
|
];
|
|
5146
5181
|
function durationPlan(prefs) {
|
|
5147
5182
|
const speakingStops = SPEAKING_STOPS[prefs.narration.density];
|
|
@@ -6906,7 +6941,7 @@ async function deckRuntime() {
|
|
|
6906
6941
|
}
|
|
6907
6942
|
}
|
|
6908
6943
|
function playerBundle() {
|
|
6909
|
-
const require2 =
|
|
6944
|
+
const require2 = createRequire2(import.meta.url);
|
|
6910
6945
|
try {
|
|
6911
6946
|
return join7(dirname3(require2.resolve("hyperframes/package.json")), "dist", PLAYER_FILE);
|
|
6912
6947
|
} catch {
|
|
@@ -6914,7 +6949,7 @@ function playerBundle() {
|
|
|
6914
6949
|
}
|
|
6915
6950
|
}
|
|
6916
6951
|
async function vendorKatex(out) {
|
|
6917
|
-
const require2 =
|
|
6952
|
+
const require2 = createRequire2(import.meta.url);
|
|
6918
6953
|
const dist = join7(dirname3(require2.resolve("katex/package.json")), "dist");
|
|
6919
6954
|
const css = await readFile8(join7(dist, "katex.min.css"), "utf8");
|
|
6920
6955
|
const written = [];
|
|
@@ -8168,7 +8203,7 @@ var TOOLS = [
|
|
|
8168
8203
|
inputSchema: schema(statusSchema)
|
|
8169
8204
|
}
|
|
8170
8205
|
];
|
|
8171
|
-
var server = new Server({ name: "decksmith", version:
|
|
8206
|
+
var server = new Server({ name: "decksmith", version: VERSION }, { capabilities: { tools: {} } });
|
|
8172
8207
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
8173
8208
|
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
8174
8209
|
const args = req.params.arguments ?? {};
|
|
@@ -212,13 +212,49 @@ export declare const SHORT_FORM_CPS = 22;
|
|
|
212
212
|
* takes `+10%` and lands at 21.9. Wrong in the OUTPUT, not in a gate — no gate
|
|
213
213
|
* looks at this, which is why it survived.
|
|
214
214
|
*
|
|
215
|
-
* SCALE-INVARIANCE IS
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
215
|
+
* SCALE-INVARIANCE IS MEASURED, and it holds. The ratio is taken at `+0%` and
|
|
216
|
+
* applied at every step, on the reasoning that speeding the voice up shrinks the
|
|
217
|
+
* cue windows and the breaths between them together. `scripts/measure-cue-rate.mjs`
|
|
218
|
+
* re-synthesised all 37 segments at all five steps — `+0%` included, in one
|
|
219
|
+
* session, because edge-tts does not repeat itself and a fresh rate judged
|
|
220
|
+
* against a stored baseline measures the drift instead:
|
|
221
|
+
*
|
|
222
|
+
* ```
|
|
223
|
+
* rate meanCps p95 cue p95/mean median/mean
|
|
224
|
+
* +0% 14.509 18.089 1.2467 1.0601
|
|
225
|
+
* +10% 15.752 20.392 1.2945 1.0653
|
|
226
|
+
* +20% 17.148 21.321 1.2434 1.0567
|
|
227
|
+
* +30% 18.540 23.263 1.2548 1.0572
|
|
228
|
+
* +40% 20.205 25.515 1.2628 1.0663
|
|
229
|
+
* ```
|
|
230
|
+
*
|
|
231
|
+
* No trend in rate — so this stays a constant rather than becoming a function of
|
|
232
|
+
* the step. The p95 column wobbles because p95 over 39 cues IS THE SECOND-HIGHEST
|
|
233
|
+
* CUE, one window wide; `median/mean` is flat to within a percent, and it is the
|
|
234
|
+
* honest read on whether the distribution's shape moves. It does not.
|
|
235
|
+
*
|
|
236
|
+
* IT AND `RATE_STEPS` ARE ONE UNIT, and they moved together — 2026-08-02, from
|
|
237
|
+
* the run above. Read that table's `speedup` note before changing either.
|
|
238
|
+
*
|
|
239
|
+
* WHY 1.30 AND NOT 1.2945. It has to sit above EVERY ratio the run observed,
|
|
240
|
+
* because the number it feeds is a ceiling on caption readability and being
|
|
241
|
+
* under it means shipping captions faster than `SHORT_FORM_CPS`. The old 1.28
|
|
242
|
+
* was below the `+10%` reading and got away with it only because the speedups
|
|
243
|
+
* beside it were inflated by more — two errors cancelling, which is not a margin
|
|
244
|
+
* anyone can reason about. Correcting the speedups removed that cover, so this
|
|
245
|
+
* had to rise with them.
|
|
246
|
+
*
|
|
247
|
+
* IT COSTS A STEP, KNOWINGLY. The demo's real p95 cue at `+20%` is 21.321,
|
|
248
|
+
* inside the ceiling of 22 — but this predicts 22.13 for that step and refuses
|
|
249
|
+
* it, so the deck takes `+10%` and speaks slower than the artifact proves it
|
|
250
|
+
* could. A central estimate of ~1.26 would admit `+20%` and track the artifact
|
|
251
|
+
* to within 0.6%; it would also under-predict on about half of future runs. The
|
|
252
|
+
* trade was made deliberately in favour of never being under, on a statistic
|
|
253
|
+
* measured once over 39 cues.
|
|
254
|
+
*
|
|
255
|
+
* `test/duration.test.ts` pins the `+0%` end against the artifact.
|
|
220
256
|
*/
|
|
221
|
-
export declare const CUE_OVERHEAD = 1.
|
|
257
|
+
export declare const CUE_OVERHEAD = 1.3;
|
|
222
258
|
/** Broadcast subtitle practice. Past this the captions stop being readable. */
|
|
223
259
|
export declare const COMFORTABLE_CPS = 17;
|
|
224
260
|
/** How far playback may be sped up before the captions are the problem. */
|
|
@@ -261,6 +297,29 @@ export declare const SPEAKING_STOPS: Record<Prefs["narration"]["density"], numbe
|
|
|
261
297
|
* `+60%` it is 28.5 and the caption is gone before it is read. `+50%` is
|
|
262
298
|
* excluded for measuring slower than the step below it.
|
|
263
299
|
*
|
|
300
|
+
* THE SPEEDUPS BELOW ARE NOT FROM THAT TABLE. They were, and they were too high
|
|
301
|
+
* at every step. The table above times ONE 72-character sentence, and a short
|
|
302
|
+
* sentence carries proportionally more silence at its ends, so dividing its
|
|
303
|
+
* total duration overstates what the prosody rate does to speech itself.
|
|
304
|
+
* Re-measured over the anchor deck's 37 segments and 196 seconds:
|
|
305
|
+
*
|
|
306
|
+
* ```
|
|
307
|
+
* step was is from
|
|
308
|
+
* +10% 1.187 1.086 meanCps 15.752 / 14.509
|
|
309
|
+
* +20% 1.252 1.182 meanCps 17.148 / 14.509
|
|
310
|
+
* +30% 1.394 1.278 meanCps 18.540 / 14.509
|
|
311
|
+
* +40% 1.533 1.393 meanCps 20.205 / 14.509
|
|
312
|
+
* ```
|
|
313
|
+
*
|
|
314
|
+
* `CUE_OVERHEAD` rose from 1.28 to 1.30 in the same commit and for this reason:
|
|
315
|
+
* it was below the cue ratio measured at `+10%` and only safe because these
|
|
316
|
+
* numbers were inflated by more. Neither is a safe edit alone — see the
|
|
317
|
+
* paragraph there. One run of `scripts/measure-cue-rate.mjs` produces both.
|
|
318
|
+
*
|
|
319
|
+
* THE TABLE ABOVE STILL EARNS ITS PLACE, because it is the only measurement of
|
|
320
|
+
* `+50%` and `+60%` anyone has taken, and what it says about them is why this
|
|
321
|
+
* list stops at `+40%`. The re-measurement covered these five steps only.
|
|
322
|
+
*
|
|
264
323
|
* Latin-measured. A CJK deck gets the same steps, which is a guess of the same
|
|
265
324
|
* kind `SPEECH_CPS.cjk` already is — replace it the first time one is narrated.
|
|
266
325
|
*/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VERSION: string;
|