@jokerized/decksmith 0.1.3 → 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 +42 -11
- package/dist/index.js +42 -11
- package/dist/mcp.js +42 -11
- package/dist/types/plan/duration.d.ts +65 -6
- package/package.json +1 -1
package/dist/cli.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];
|
|
@@ -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];
|
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
|
@@ -1057,24 +1057,55 @@ var ADVANCE = {
|
|
|
1057
1057
|
var KERN_SLACK = 1.007;
|
|
1058
1058
|
var TABULAR_FIGURE = 0.649;
|
|
1059
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;
|
|
1060
1081
|
function charUnits(c, tabular) {
|
|
1061
1082
|
if (tabular) {
|
|
1062
|
-
if (c >= "0" && c <= "9") return TABULAR_FIGURE;
|
|
1063
|
-
if (c === "." || c === ",") return TABULAR_SEPARATOR;
|
|
1083
|
+
if (c >= "0" && c <= "9") return [TABULAR_FIGURE, true];
|
|
1084
|
+
if (c === "." || c === ",") return [TABULAR_SEPARATOR, true];
|
|
1064
1085
|
}
|
|
1065
|
-
|
|
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];
|
|
1066
1091
|
}
|
|
1067
1092
|
function weightFactor(weight) {
|
|
1068
1093
|
return weight >= 700 ? 1.045 : weight >= 600 ? 1.03 : weight >= 500 ? 1.015 : 1;
|
|
1069
1094
|
}
|
|
1070
1095
|
function textWidth(text2, fontSize, weight = 400, tracking = 0, tabular = false) {
|
|
1071
|
-
let
|
|
1096
|
+
let weighted = 0;
|
|
1097
|
+
let emGrid = 0;
|
|
1072
1098
|
let chars = 0;
|
|
1073
1099
|
for (const c of text2) {
|
|
1074
|
-
units
|
|
1100
|
+
const [units, scalesWithWeight] = charUnits(c, tabular);
|
|
1101
|
+
if (scalesWithWeight) weighted += units;
|
|
1102
|
+
else emGrid += units;
|
|
1075
1103
|
chars++;
|
|
1076
1104
|
}
|
|
1077
|
-
|
|
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;
|
|
1078
1109
|
}
|
|
1079
1110
|
function wrap(text2, fontSize, maxWidth, weight = 400, tracking = 0) {
|
|
1080
1111
|
if (maxWidth <= 0) return [text2];
|
|
@@ -5132,7 +5163,7 @@ var SENTENCES_PER_BEAT = 2;
|
|
|
5132
5163
|
var FF_BEAT_SECONDS = 8;
|
|
5133
5164
|
var MIN_BEAT_SECONDS = 4;
|
|
5134
5165
|
var SHORT_FORM_CPS = 22;
|
|
5135
|
-
var CUE_OVERHEAD = 1.
|
|
5166
|
+
var CUE_OVERHEAD = 1.3;
|
|
5136
5167
|
var COMFORTABLE_CPS = 17;
|
|
5137
5168
|
var MAX_PLAYBACK = 1.25;
|
|
5138
5169
|
var SPEAKING_STOPS = {
|
|
@@ -5142,10 +5173,10 @@ var SPEAKING_STOPS = {
|
|
|
5142
5173
|
};
|
|
5143
5174
|
var RATE_STEPS = [
|
|
5144
5175
|
["+0%", 1],
|
|
5145
|
-
["+10%", 1.
|
|
5146
|
-
["+20%", 1.
|
|
5147
|
-
["+30%", 1.
|
|
5148
|
-
["+40%", 1.
|
|
5176
|
+
["+10%", 1.086],
|
|
5177
|
+
["+20%", 1.182],
|
|
5178
|
+
["+30%", 1.278],
|
|
5179
|
+
["+40%", 1.393]
|
|
5149
5180
|
];
|
|
5150
5181
|
function durationPlan(prefs) {
|
|
5151
5182
|
const speakingStops = SPEAKING_STOPS[prefs.narration.density];
|
|
@@ -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
|
*/
|