@sushichan044/kg-core 0.9.1 → 0.9.2
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/README.md
CHANGED
|
@@ -95,9 +95,14 @@ stage, or by hanging a comma or full stop; a short line is answered by opening u
|
|
|
95
95
|
where JLReq 3.8.4 and appendix 表6
|
|
96
96
|
allow it — the word space first, up to a half, then the Japanese-to-Western quarter em,
|
|
97
97
|
also up to a half, then a solid pair between kana, kanji, and the marks that go with
|
|
98
|
-
them, up to a quarter.
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
them, up to a quarter. If those stages cannot fill the line, the remaining width is
|
|
99
|
+
added equally to the admitted pairs in 表6 that are solid at rest, including pairs used by
|
|
100
|
+
the third stage. Intrinsic spaces keep their finite limits: a word space and a mixed
|
|
101
|
+
Japanese-to-Western, numeral, or unit-symbol gap stop at a half em, while punctuation and
|
|
102
|
+
explicit full-em spaces retain their own width. This keeps the unbounded stage from producing
|
|
103
|
+
conspicuous holes around short upright Western words or already-spaced marks.
|
|
104
|
+
Line-head and line-end space, inseparable pairs, and source-authored full-em gaps after
|
|
105
|
+
question or exclamation marks stay fixed. A line with no admitted gap is forced. The choice is made for the
|
|
101
106
|
paragraph rather than line by line,
|
|
102
107
|
minimizing in order the number of forced lines, then stretched, then hanging, then
|
|
103
108
|
shrunk lines that spent visible space. Which space may be reduced, and in which
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as ManuscriptResult, d as NamespacedId, f as ManuscriptRange, h as readonlyObject, l as ManuscriptDiagnostic, o as graphemeSegmenter, s as ValidationIssue } from "./question-or-exclamation-spacing-B5o23ZLf.mjs";
|
|
2
|
-
import { c as ManuscriptOffsets, d as FontSizePt, f as FontPresetId, i as PaperSize, l as ManuscriptAppearanceSettings, n as novelComposer, o as NovelCompositionSettings, p as ParsedManuscript, r as ManuscriptGeometry, s as NovelFlowSettings, u as PaperSizeId } from "./novel-composer-
|
|
2
|
+
import { c as ManuscriptOffsets, d as FontSizePt, f as FontPresetId, i as PaperSize, l as ManuscriptAppearanceSettings, n as novelComposer, o as NovelCompositionSettings, p as ParsedManuscript, r as ManuscriptGeometry, s as NovelFlowSettings, u as PaperSizeId } from "./novel-composer-CNBT4TDN.mjs";
|
|
3
3
|
import { n as assertNever, t as describeCause } from "./describe-cause-BV8mZ3Um.mjs";
|
|
4
4
|
import * as v from "valibot";
|
|
5
5
|
const Rejection = { schema: readonlyObject({ reason: v.string() }) };
|
|
@@ -377,10 +377,6 @@ const REDUCTION_STAGE = {
|
|
|
377
377
|
* {@link REDUCTION_STAGE}: the lower the number, the earlier the paragraph optimizer takes it. A
|
|
378
378
|
* line is either reduced or expanded, never both, so the two scales never meet.
|
|
379
379
|
*
|
|
380
|
-
* Stage 4 spreads whatever a、b and c could not absorb across every character gap that is not
|
|
381
|
-
* unbreakable, and is deliberately not implemented: it would justify every line that currently
|
|
382
|
-
* falls short, which reads as prose combed out to the margin rather than a novel.
|
|
383
|
-
*
|
|
384
380
|
* Nothing here takes priority 0. Expanding is always visible, and priority 0 means an adjustment
|
|
385
381
|
* the optimizer is charged nothing for.
|
|
386
382
|
*/
|
|
@@ -396,7 +392,11 @@ const EXPANSION_STAGE = {
|
|
|
396
392
|
/**
|
|
397
393
|
* Stage 3: everywhere else 表6 admits, from solid up to a quarter em.
|
|
398
394
|
*/
|
|
399
|
-
solid: 3
|
|
395
|
+
solid: 3,
|
|
396
|
+
/**
|
|
397
|
+
* Stage 4: the unbounded remainder, spread equally across every colored or gray cell of 表6.
|
|
398
|
+
*/
|
|
399
|
+
final: 4
|
|
400
400
|
};
|
|
401
401
|
/**
|
|
402
402
|
* 無印 in 表1 with no colour in 表6: the pair is set solid and line adjustment leaves it alone.
|
|
@@ -635,6 +635,125 @@ const EXPANDABLE_AFTER = [
|
|
|
635
635
|
"cl-30"
|
|
636
636
|
];
|
|
637
637
|
/**
|
|
638
|
+
* The gray, blue and pink cells of 表6: all pairs that join the fourth and final expansion stage.
|
|
639
|
+
* The finite capacities above cover the blue and pink cells at stages three and two; once those
|
|
640
|
+
* stages are exhausted, JLReq 3.8.4 d adds the remainder equally across every cell named here.
|
|
641
|
+
*
|
|
642
|
+
* A cl-08/cl-08 cell is blue only when the two characters are different kinds of mark (注4). This
|
|
643
|
+
* profile cannot distinguish the two halves of one 2倍ダッシュ from different marks, so it keeps the
|
|
644
|
+
* pair joined, as it already does for the finite third stage.
|
|
645
|
+
*/
|
|
646
|
+
const FINAL_EXPANSION_BASIC_AFTER = /* @__PURE__ */ new Set([
|
|
647
|
+
"cl-01",
|
|
648
|
+
"cl-08",
|
|
649
|
+
"cl-12",
|
|
650
|
+
"cl-13",
|
|
651
|
+
"cl-14",
|
|
652
|
+
"cl-15",
|
|
653
|
+
"cl-16",
|
|
654
|
+
"cl-19",
|
|
655
|
+
"cl-24",
|
|
656
|
+
"cl-25",
|
|
657
|
+
"cl-26",
|
|
658
|
+
"cl-27",
|
|
659
|
+
"cl-30"
|
|
660
|
+
]);
|
|
661
|
+
const FINAL_EXPANSION_WIDE_AFTER = /* @__PURE__ */ new Set([
|
|
662
|
+
...FINAL_EXPANSION_BASIC_AFTER,
|
|
663
|
+
"cl-09",
|
|
664
|
+
"cl-10",
|
|
665
|
+
"cl-11"
|
|
666
|
+
]);
|
|
667
|
+
const FINAL_EXPANSION_AFTER = new Map([
|
|
668
|
+
...[
|
|
669
|
+
"cl-02",
|
|
670
|
+
"cl-03",
|
|
671
|
+
"cl-04",
|
|
672
|
+
"cl-05",
|
|
673
|
+
"cl-06",
|
|
674
|
+
"cl-07",
|
|
675
|
+
"cl-14"
|
|
676
|
+
].map((left) => [left, FINAL_EXPANSION_BASIC_AFTER]),
|
|
677
|
+
...[
|
|
678
|
+
"cl-09",
|
|
679
|
+
"cl-10",
|
|
680
|
+
"cl-11",
|
|
681
|
+
"cl-13",
|
|
682
|
+
"cl-15",
|
|
683
|
+
"cl-16",
|
|
684
|
+
"cl-19",
|
|
685
|
+
"cl-30"
|
|
686
|
+
].map((left) => [left, FINAL_EXPANSION_WIDE_AFTER]),
|
|
687
|
+
["cl-08", new Set([...FINAL_EXPANSION_WIDE_AFTER].filter((characterClass) => characterClass !== "cl-08"))],
|
|
688
|
+
["cl-12", /* @__PURE__ */ new Set([
|
|
689
|
+
"cl-08",
|
|
690
|
+
"cl-09",
|
|
691
|
+
"cl-10",
|
|
692
|
+
"cl-11",
|
|
693
|
+
"cl-12",
|
|
694
|
+
"cl-13",
|
|
695
|
+
"cl-15",
|
|
696
|
+
"cl-16",
|
|
697
|
+
"cl-19",
|
|
698
|
+
"cl-25",
|
|
699
|
+
"cl-26",
|
|
700
|
+
"cl-27",
|
|
701
|
+
"cl-30"
|
|
702
|
+
])],
|
|
703
|
+
["cl-24", /* @__PURE__ */ new Set([
|
|
704
|
+
"cl-01",
|
|
705
|
+
"cl-08",
|
|
706
|
+
"cl-09",
|
|
707
|
+
"cl-10",
|
|
708
|
+
"cl-11",
|
|
709
|
+
"cl-12",
|
|
710
|
+
"cl-14",
|
|
711
|
+
"cl-15",
|
|
712
|
+
"cl-16",
|
|
713
|
+
"cl-19",
|
|
714
|
+
"cl-25",
|
|
715
|
+
"cl-26",
|
|
716
|
+
"cl-30"
|
|
717
|
+
])],
|
|
718
|
+
["cl-25", /* @__PURE__ */ new Set([
|
|
719
|
+
"cl-01",
|
|
720
|
+
"cl-08",
|
|
721
|
+
"cl-09",
|
|
722
|
+
"cl-10",
|
|
723
|
+
"cl-11",
|
|
724
|
+
"cl-12",
|
|
725
|
+
"cl-13",
|
|
726
|
+
"cl-14",
|
|
727
|
+
"cl-15",
|
|
728
|
+
"cl-16",
|
|
729
|
+
"cl-19",
|
|
730
|
+
"cl-24",
|
|
731
|
+
"cl-26",
|
|
732
|
+
"cl-30"
|
|
733
|
+
])],
|
|
734
|
+
["cl-26", new Set(japaneseCharacterClasses.filter((characterClass) => characterClass !== "cl-02"))],
|
|
735
|
+
["cl-27", /* @__PURE__ */ new Set([
|
|
736
|
+
"cl-01",
|
|
737
|
+
"cl-08",
|
|
738
|
+
"cl-09",
|
|
739
|
+
"cl-10",
|
|
740
|
+
"cl-11",
|
|
741
|
+
"cl-12",
|
|
742
|
+
"cl-13",
|
|
743
|
+
"cl-14",
|
|
744
|
+
"cl-15",
|
|
745
|
+
"cl-16",
|
|
746
|
+
"cl-19",
|
|
747
|
+
"cl-26",
|
|
748
|
+
"cl-30"
|
|
749
|
+
])]
|
|
750
|
+
]);
|
|
751
|
+
function canExpandAtFinalStage(left, right) {
|
|
752
|
+
if (left === "cl-14" || left === "cl-26" || left === "cl-27" || right === "cl-14" || right === "cl-26" || right === "cl-27") return false;
|
|
753
|
+
if (FINAL_EXPANSION_AFTER.get(left)?.has(right) !== true) return false;
|
|
754
|
+
return pairSpacing(left, right).naturalWidthEm === 0;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
638
757
|
* The classes JLReq 3.1.2 sets on a half em rather than a full one.
|
|
639
758
|
*/
|
|
640
759
|
const HALF_EM_CLASSES = /* @__PURE__ */ new Set([
|
|
@@ -922,6 +1041,8 @@ const defaultJapaneseTypesettingProfile = {
|
|
|
922
1041
|
},
|
|
923
1042
|
absorbsPrecedingEm: .25
|
|
924
1043
|
} : null,
|
|
1044
|
+
finalStretchPriority: EXPANSION_STAGE.final,
|
|
1045
|
+
canExpandAtFinalStage,
|
|
925
1046
|
breakPenalty,
|
|
926
1047
|
/**
|
|
927
1048
|
* Hanging punctuation (ぶら下げ組, JLReq 3.8.2) lets a full stop or comma sit outside the text area
|
|
@@ -951,6 +1072,15 @@ function skipSourceGaps(atoms, start) {
|
|
|
951
1072
|
function previousVisible(atoms, end) {
|
|
952
1073
|
for (let index = end - 1; index >= 0; index -= 1) if (atoms[index]?.sourceGap === false) return index;
|
|
953
1074
|
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Whether a shorter line ending somewhere inside `(contentStart, end)` was ever an option — checked
|
|
1077
|
+
* as a plain short-circuiting scan rather than building the boundary list first, since this runs
|
|
1078
|
+
* once per over-long candidate the optimizer considers.
|
|
1079
|
+
*/
|
|
1080
|
+
function hasEarlierBoundary(boundaryAllowed, contentStart, end) {
|
|
1081
|
+
for (let boundary = contentStart + 1; boundary < end; boundary += 1) if (boundaryAllowed(boundary - 1, boundary)) return true;
|
|
1082
|
+
return false;
|
|
1083
|
+
}
|
|
954
1084
|
function opportunities(atoms, classes, start, end, profile, lineHead) {
|
|
955
1085
|
const result = [];
|
|
956
1086
|
const firstClass = classes[start];
|
|
@@ -961,6 +1091,7 @@ function opportunities(atoms, classes, start, end, profile, lineHead) {
|
|
|
961
1091
|
boundary: start
|
|
962
1092
|
},
|
|
963
1093
|
spacing: startSpacing,
|
|
1094
|
+
finalStretch: false,
|
|
964
1095
|
absorbsPrecedingEm: 0
|
|
965
1096
|
});
|
|
966
1097
|
for (let index = start; index < end; index += 1) {
|
|
@@ -973,6 +1104,7 @@ function opportunities(atoms, classes, start, end, profile, lineHead) {
|
|
|
973
1104
|
index
|
|
974
1105
|
},
|
|
975
1106
|
spacing,
|
|
1107
|
+
finalStretch: false,
|
|
976
1108
|
absorbsPrecedingEm: 0
|
|
977
1109
|
});
|
|
978
1110
|
}
|
|
@@ -988,6 +1120,7 @@ function opportunities(atoms, classes, start, end, profile, lineHead) {
|
|
|
988
1120
|
boundary: right
|
|
989
1121
|
},
|
|
990
1122
|
spacing: profile.pairSpacing(leftClass, rightClass),
|
|
1123
|
+
finalStretch: profile.canExpandAtFinalStage(leftClass, rightClass),
|
|
991
1124
|
absorbsPrecedingEm: 0
|
|
992
1125
|
});
|
|
993
1126
|
}
|
|
@@ -999,6 +1132,7 @@ function opportunities(atoms, classes, start, end, profile, lineHead) {
|
|
|
999
1132
|
boundary: end
|
|
1000
1133
|
},
|
|
1001
1134
|
spacing: endSpacing.spacing,
|
|
1135
|
+
finalStretch: false,
|
|
1002
1136
|
absorbsPrecedingEm: endSpacing.absorbsPrecedingEm
|
|
1003
1137
|
});
|
|
1004
1138
|
return result;
|
|
@@ -1072,17 +1206,20 @@ function adjustmentUnits(values, adjustment) {
|
|
|
1072
1206
|
* Within a stage the amount is therefore split in proportion to what each space can give, rather
|
|
1073
1207
|
* than taken out of the earliest space until it runs dry. Across stages the order stays a
|
|
1074
1208
|
* waterfall: a later stage is reached only once every stage before it is spent out. The composer
|
|
1075
|
-
* sets one character size, so a share of the stage's capacity is a share by character size.
|
|
1209
|
+
* sets one character size, so a share of the stage's capacity is a share by character size. JLReq
|
|
1210
|
+
* 3.8.4's final stage has no upper bound: after the finite stages are full, the unresolved
|
|
1211
|
+
* remainder is added equally to every pair the profile admits, including the earlier-stage pairs.
|
|
1076
1212
|
*
|
|
1077
1213
|
* An all-or-nothing unit larger than what is still needed is skipped rather than partly spent, and
|
|
1078
1214
|
* never revisited: `remaining` only falls, so a unit that did not fit at its own stage cannot fit
|
|
1079
1215
|
* at a later one. One forward pass is therefore exact and no search over subsets is needed.
|
|
1080
1216
|
*/
|
|
1081
|
-
function resolveSpacings(values, units, adjustment, amountEm) {
|
|
1217
|
+
function resolveSpacings(values, units, adjustment, amountEm, finalStretchPriority) {
|
|
1082
1218
|
const usedBySlot = /* @__PURE__ */ new Map();
|
|
1083
1219
|
let remaining = amountEm;
|
|
1084
1220
|
let priorityCost = 0;
|
|
1085
1221
|
let freeEm = 0;
|
|
1222
|
+
let finalStretchPerSlotEm = 0;
|
|
1086
1223
|
const spend = (unit, fraction) => {
|
|
1087
1224
|
for (const { slot, amountEm: partEm } of unit.parts) {
|
|
1088
1225
|
const key = slotKey(slot);
|
|
@@ -1109,6 +1246,20 @@ function resolveSpacings(values, units, adjustment, amountEm) {
|
|
|
1109
1246
|
const fraction = Math.min(remaining, stageCapacityEm) / stageCapacityEm;
|
|
1110
1247
|
for (const unit of stage) if (unit.granularity === "continuous") spend(unit, fraction);
|
|
1111
1248
|
}
|
|
1249
|
+
if (adjustment === "stretch" && remaining > EPSILON) {
|
|
1250
|
+
let finalSlotCount = 0;
|
|
1251
|
+
for (const { finalStretch } of values) if (finalStretch) finalSlotCount += 1;
|
|
1252
|
+
if (finalSlotCount > 0) {
|
|
1253
|
+
finalStretchPerSlotEm = remaining / finalSlotCount;
|
|
1254
|
+
for (const { slot, finalStretch } of values) {
|
|
1255
|
+
if (!finalStretch) continue;
|
|
1256
|
+
const key = slotKey(slot);
|
|
1257
|
+
usedBySlot.set(key, (usedBySlot.get(key) ?? 0) + finalStretchPerSlotEm);
|
|
1258
|
+
}
|
|
1259
|
+
priorityCost += remaining * finalStretchPriority;
|
|
1260
|
+
remaining = 0;
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1112
1263
|
return {
|
|
1113
1264
|
spacings: splitBySlot(values, (slot, spacing) => {
|
|
1114
1265
|
const used = usedBySlot.get(slotKey(slot)) ?? 0;
|
|
@@ -1116,6 +1267,7 @@ function resolveSpacings(values, units, adjustment, amountEm) {
|
|
|
1116
1267
|
}),
|
|
1117
1268
|
priorityCost,
|
|
1118
1269
|
freeEm,
|
|
1270
|
+
finalStretchPerSlotEm,
|
|
1119
1271
|
unabsorbedEm: Math.max(0, remaining)
|
|
1120
1272
|
};
|
|
1121
1273
|
}
|
|
@@ -1159,11 +1311,11 @@ function hangingSpacings(values, end) {
|
|
|
1159
1311
|
characterSpacings
|
|
1160
1312
|
};
|
|
1161
1313
|
}
|
|
1162
|
-
function candidate(atoms, classes, start, end, lineLengthEm, profile) {
|
|
1314
|
+
function candidate(atoms, classes, boxPrefixEm, start, end, lineLengthEm, profile) {
|
|
1163
1315
|
const contentStart = skipSourceGaps(atoms, start);
|
|
1164
1316
|
const suppressedIndexes = Array.from({ length: contentStart - start }, (_, index) => start + index);
|
|
1165
1317
|
const pairValues = opportunities(atoms, classes, contentStart, end, profile, start === 0 ? "paragraph-start" : "turned-over");
|
|
1166
|
-
const naturalSizeEm =
|
|
1318
|
+
const naturalSizeEm = (boxPrefixEm[end] ?? 0) - (boxPrefixEm[contentStart] ?? 0) + pairValues.reduce((total, value) => total + value.spacing.naturalWidthEm, 0);
|
|
1167
1319
|
const terminal = skipSourceGaps(atoms, end) === atoms.length;
|
|
1168
1320
|
const overflow = naturalSizeEm - lineLengthEm;
|
|
1169
1321
|
const underflow = lineLengthEm - naturalSizeEm;
|
|
@@ -1174,6 +1326,7 @@ function candidate(atoms, classes, start, end, lineLengthEm, profile) {
|
|
|
1174
1326
|
const lastClass = lastVisible === void 0 ? void 0 : classes[lastVisible];
|
|
1175
1327
|
const lastAdvance = lastVisible === void 0 ? 0 : atoms[lastVisible]?.boxAdvanceEm ?? 0;
|
|
1176
1328
|
const trailingSpacing = pairValues.find(({ slot }) => slot.kind === "gap" && slot.boundary === end)?.spacing.naturalWidthEm ?? 0;
|
|
1329
|
+
const hasFinalStretch = pairValues.some(({ finalStretch }) => finalStretch);
|
|
1177
1330
|
if (terminal && overflow <= EPSILON) return {
|
|
1178
1331
|
start,
|
|
1179
1332
|
contentStart,
|
|
@@ -1198,7 +1351,7 @@ function candidate(atoms, classes, start, end, lineLengthEm, profile) {
|
|
|
1198
1351
|
deformationRatio: 0,
|
|
1199
1352
|
priorityCost: 0
|
|
1200
1353
|
};
|
|
1201
|
-
const shrunk = overflow > 0 && overflow <= capacity + EPSILON ? resolveSpacings(pairValues, units, "shrink", overflow) : null;
|
|
1354
|
+
const shrunk = overflow > 0 && overflow <= capacity + EPSILON ? resolveSpacings(pairValues, units, "shrink", overflow, profile.finalStretchPriority) : null;
|
|
1202
1355
|
if (shrunk !== null && shrunk.unabsorbedEm <= EPSILON) {
|
|
1203
1356
|
const chargeableCapacity = capacity - freeShrinkCapacity;
|
|
1204
1357
|
const chargeableShrink = Math.max(0, overflow - shrunk.freeEm);
|
|
@@ -1227,7 +1380,7 @@ function candidate(atoms, classes, start, end, lineLengthEm, profile) {
|
|
|
1227
1380
|
deformationRatio: overflow / Math.max(lastAdvance, EPSILON),
|
|
1228
1381
|
priorityCost: 0
|
|
1229
1382
|
};
|
|
1230
|
-
const stretched = !terminal && underflow > EPSILON && underflow <= capacity + EPSILON ? resolveSpacings(pairValues, units, "stretch", underflow) : null;
|
|
1383
|
+
const stretched = !terminal && underflow > EPSILON && (underflow <= capacity + EPSILON || hasFinalStretch) ? resolveSpacings(pairValues, units, "stretch", underflow, profile.finalStretchPriority) : null;
|
|
1231
1384
|
if (stretched !== null && stretched.unabsorbedEm <= EPSILON) return {
|
|
1232
1385
|
start,
|
|
1233
1386
|
contentStart,
|
|
@@ -1237,7 +1390,7 @@ function candidate(atoms, classes, start, end, lineLengthEm, profile) {
|
|
|
1237
1390
|
inlineSizeEm: lineLengthEm,
|
|
1238
1391
|
break: { kind: "stretched" },
|
|
1239
1392
|
hangingIndex: null,
|
|
1240
|
-
deformationRatio: capacity === 0 ? 0 : underflow / capacity,
|
|
1393
|
+
deformationRatio: stretched.finalStretchPerSlotEm > 0 ? 1 + stretched.finalStretchPerSlotEm : capacity === 0 ? 0 : underflow / capacity,
|
|
1241
1394
|
priorityCost: stretched.priorityCost
|
|
1242
1395
|
};
|
|
1243
1396
|
return {
|
|
@@ -1287,6 +1440,22 @@ function addScore(left, right) {
|
|
|
1287
1440
|
left[6] + right[6]
|
|
1288
1441
|
];
|
|
1289
1442
|
}
|
|
1443
|
+
/**
|
|
1444
|
+
* The line-end offsets a state's path took, earliest first. Rebuilt on demand by walking `previous`
|
|
1445
|
+
* rather than carried on every `State`: every candidate line attempted would otherwise pay for a
|
|
1446
|
+
* copy of its whole path, when `isBetter` only ever reads it on the exact-score tie this comparison
|
|
1447
|
+
* breaks — for `boundaryAllowed` predicates permissive enough to fully saturate the optimizer's
|
|
1448
|
+
* search window, ties are rare enough that this deferred cost is negligible.
|
|
1449
|
+
*/
|
|
1450
|
+
function breaksOf(state, states) {
|
|
1451
|
+
const result = [];
|
|
1452
|
+
let cursor = state;
|
|
1453
|
+
while (cursor?.line !== null && cursor?.line !== void 0) {
|
|
1454
|
+
result.push(cursor.line.end);
|
|
1455
|
+
cursor = cursor.previous === null ? void 0 : states.get(cursor.previous.index)?.get(cursor.previous.fitness);
|
|
1456
|
+
}
|
|
1457
|
+
return result.reverse();
|
|
1458
|
+
}
|
|
1290
1459
|
function compareBreaks(left, right) {
|
|
1291
1460
|
const length = Math.min(left.length, right.length);
|
|
1292
1461
|
for (let index = 0; index < length; index += 1) {
|
|
@@ -1295,17 +1464,19 @@ function compareBreaks(left, right) {
|
|
|
1295
1464
|
}
|
|
1296
1465
|
return left.length - right.length;
|
|
1297
1466
|
}
|
|
1298
|
-
function isBetter(candidateState, current) {
|
|
1467
|
+
function isBetter(candidateState, current, states) {
|
|
1299
1468
|
if (current === void 0) return true;
|
|
1300
1469
|
for (let index = 0; index < candidateState.score.length; index += 1) {
|
|
1301
1470
|
const difference = (candidateState.score[index] ?? 0) - (current.score[index] ?? 0);
|
|
1302
1471
|
if (Math.abs(difference) > EPSILON) return difference < 0;
|
|
1303
1472
|
}
|
|
1304
|
-
return compareBreaks(candidateState
|
|
1473
|
+
return compareBreaks(breaksOf(candidateState, states), breaksOf(current, states)) < 0;
|
|
1305
1474
|
}
|
|
1306
1475
|
function layoutParagraph(atoms, lineLengthEm, profile, boundaryAllowed) {
|
|
1307
1476
|
if (atoms.length === 0) return [];
|
|
1308
1477
|
const classes = atoms.map(({ characterClass }) => characterClass);
|
|
1478
|
+
const boxPrefixEm = Array.from({ length: atoms.length + 1 }).fill(0);
|
|
1479
|
+
for (let index = 0; index < atoms.length; index += 1) boxPrefixEm[index + 1] = (boxPrefixEm[index] ?? 0) + (atoms[index]?.boxAdvanceEm ?? 0);
|
|
1309
1480
|
const states = /* @__PURE__ */ new Map([[0, /* @__PURE__ */ new Map([[0, {
|
|
1310
1481
|
score: [
|
|
1311
1482
|
0,
|
|
@@ -1318,8 +1489,7 @@ function layoutParagraph(atoms, lineLengthEm, profile, boundaryAllowed) {
|
|
|
1318
1489
|
],
|
|
1319
1490
|
fitness: 0,
|
|
1320
1491
|
previous: null,
|
|
1321
|
-
line: null
|
|
1322
|
-
breaks: []
|
|
1492
|
+
line: null
|
|
1323
1493
|
}]])]]);
|
|
1324
1494
|
for (let start = 0; start < atoms.length; start += 1) {
|
|
1325
1495
|
const activeStates = states.get(start);
|
|
@@ -1327,7 +1497,7 @@ function layoutParagraph(atoms, lineLengthEm, profile, boundaryAllowed) {
|
|
|
1327
1497
|
const contentStart = skipSourceGaps(atoms, start);
|
|
1328
1498
|
if (contentStart === atoms.length) {
|
|
1329
1499
|
const terminalStates = states.get(atoms.length) ?? /* @__PURE__ */ new Map();
|
|
1330
|
-
for (const state of activeStates.values()) if (isBetter(state, terminalStates.get(state.fitness))) terminalStates.set(state.fitness, state);
|
|
1500
|
+
for (const state of activeStates.values()) if (isBetter(state, terminalStates.get(state.fitness), states)) terminalStates.set(state.fitness, state);
|
|
1331
1501
|
states.set(atoms.length, terminalStates);
|
|
1332
1502
|
continue;
|
|
1333
1503
|
}
|
|
@@ -1336,8 +1506,8 @@ function layoutParagraph(atoms, lineLengthEm, profile, boundaryAllowed) {
|
|
|
1336
1506
|
const left = previousVisible(atoms, end);
|
|
1337
1507
|
if (atoms[end - 1]?.sourceGap === true) continue;
|
|
1338
1508
|
if (end < atoms.length && right < atoms.length && (left === void 0 || !boundaryAllowed(left, right))) continue;
|
|
1339
|
-
const line = candidate(atoms, classes, start, end, lineLengthEm, profile);
|
|
1340
|
-
if (line.break.kind === "forced" && line.inlineSizeEm > lineLengthEm + EPSILON &&
|
|
1509
|
+
const line = candidate(atoms, classes, boxPrefixEm, start, end, lineLengthEm, profile);
|
|
1510
|
+
if (line.break.kind === "forced" && line.inlineSizeEm > lineLengthEm + EPSILON && hasEarlierBoundary(boundaryAllowed, contentStart, end)) {
|
|
1341
1511
|
if (line.inlineSizeEm > lineLengthEm * 2 && end > contentStart + 1) break;
|
|
1342
1512
|
continue;
|
|
1343
1513
|
}
|
|
@@ -1349,18 +1519,17 @@ function layoutParagraph(atoms, lineLengthEm, profile, boundaryAllowed) {
|
|
|
1349
1519
|
index: start,
|
|
1350
1520
|
fitness: state.fitness
|
|
1351
1521
|
},
|
|
1352
|
-
line
|
|
1353
|
-
breaks: [...state.breaks, end]
|
|
1522
|
+
line
|
|
1354
1523
|
};
|
|
1355
1524
|
const statesAtEnd = states.get(end) ?? /* @__PURE__ */ new Map();
|
|
1356
|
-
if (isBetter(nextState, statesAtEnd.get(nextState.fitness))) {
|
|
1525
|
+
if (isBetter(nextState, statesAtEnd.get(nextState.fitness), states)) {
|
|
1357
1526
|
statesAtEnd.set(nextState.fitness, nextState);
|
|
1358
1527
|
states.set(end, statesAtEnd);
|
|
1359
1528
|
}
|
|
1360
1529
|
if (line.inlineSizeEm > lineLengthEm * 2 && line.break.kind === "forced" && end > contentStart + 1) break;
|
|
1361
1530
|
}
|
|
1362
1531
|
}
|
|
1363
|
-
const terminal = [...states.get(atoms.length)?.values() ?? []].reduce((best, state) => isBetter(state, best) ? state : best, void 0);
|
|
1532
|
+
const terminal = [...states.get(atoms.length)?.values() ?? []].reduce((best, state) => isBetter(state, best, states) ? state : best, void 0);
|
|
1364
1533
|
if (terminal === void 0) return [];
|
|
1365
1534
|
const lines = [];
|
|
1366
1535
|
let cursor = terminal;
|
package/dist/plugin.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { c as ManuscriptResult, d as NamespacedId, s as ValidationIssue } from "./question-or-exclamation-spacing-B5o23ZLf.mjs";
|
|
2
|
-
import { a as logicalInlineMeasurer, p as ParsedManuscript, t as createNovelComposer } from "./novel-composer-
|
|
2
|
+
import { a as logicalInlineMeasurer, p as ParsedManuscript, t as createNovelComposer } from "./novel-composer-CNBT4TDN.mjs";
|
|
3
3
|
export { ManuscriptResult, NamespacedId, ParsedManuscript, ValidationIssue, createNovelComposer, logicalInlineMeasurer };
|