@pacem/pacem 1.0.0-bessel → 1.0.0-dirac

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.
Files changed (66) hide show
  1. package/dist/browser/pacem-2d.js +149 -2
  2. package/dist/browser/pacem-2d.js.map +1 -1
  3. package/dist/browser/pacem-2d.min.js +1 -1
  4. package/dist/browser/pacem-3d.js +646 -2
  5. package/dist/browser/pacem-3d.js.map +1 -1
  6. package/dist/browser/pacem-3d.min.js +1 -1
  7. package/dist/browser/pacem-charts.js +21 -1
  8. package/dist/browser/pacem-charts.js.map +1 -1
  9. package/dist/browser/pacem-charts.min.js +1 -1
  10. package/dist/browser/pacem-cms.js +118 -3
  11. package/dist/browser/pacem-cms.js.map +1 -1
  12. package/dist/browser/pacem-cms.min.js +1 -1
  13. package/dist/browser/pacem-core.js +870 -9
  14. package/dist/browser/pacem-core.js.map +1 -1
  15. package/dist/browser/pacem-core.min.js +2 -2
  16. package/dist/browser/pacem-foundation.js +296 -1
  17. package/dist/browser/pacem-foundation.js.map +1 -1
  18. package/dist/browser/pacem-foundation.min.js +1 -1
  19. package/dist/browser/pacem-fx.js +18 -1
  20. package/dist/browser/pacem-fx.js.map +1 -1
  21. package/dist/browser/pacem-fx.min.js +1 -1
  22. package/dist/browser/pacem-logging.js +15 -1
  23. package/dist/browser/pacem-logging.js.map +1 -1
  24. package/dist/browser/pacem-logging.min.js +1 -1
  25. package/dist/browser/pacem-maps.js +97 -1
  26. package/dist/browser/pacem-maps.js.map +1 -1
  27. package/dist/browser/pacem-maps.min.js +1 -1
  28. package/dist/browser/pacem-media.js +14 -1
  29. package/dist/browser/pacem-media.js.map +1 -1
  30. package/dist/browser/pacem-media.min.js +1 -1
  31. package/dist/browser/pacem-networking.js +22 -1
  32. package/dist/browser/pacem-networking.js.map +1 -1
  33. package/dist/browser/pacem-networking.min.js +1 -1
  34. package/dist/browser/pacem-numerical.js +360 -1
  35. package/dist/browser/pacem-numerical.js.map +1 -1
  36. package/dist/browser/pacem-numerical.min.js +1 -1
  37. package/dist/browser/pacem-plus.js +195 -4
  38. package/dist/browser/pacem-plus.js.map +1 -1
  39. package/dist/browser/pacem-plus.min.js +1 -1
  40. package/dist/browser/pacem-scaffolding.js +420 -38
  41. package/dist/browser/pacem-scaffolding.js.map +1 -1
  42. package/dist/browser/pacem-scaffolding.min.js +2 -2
  43. package/dist/browser/pacem-ui.js +143 -19
  44. package/dist/browser/pacem-ui.js.map +1 -1
  45. package/dist/browser/pacem-ui.min.js +2 -2
  46. package/dist/bundle/pacem.min.mjs +147 -147
  47. package/dist/bundle/pacem.mjs +2905 -1417
  48. package/dist/bundle/pacem.mjs.map +3 -3
  49. package/dist/docs/pacem-2d.json +11549 -0
  50. package/dist/docs/pacem-3d.json +29096 -0
  51. package/dist/docs/pacem-charts.json +4244 -0
  52. package/dist/docs/pacem-cms.json +9325 -0
  53. package/dist/docs/pacem-core.json +40109 -0
  54. package/dist/docs/pacem-foundation.json +8941 -0
  55. package/dist/docs/pacem-fx.json +3121 -0
  56. package/dist/docs/pacem-logging.json +941 -0
  57. package/dist/docs/pacem-maps.json +17387 -0
  58. package/dist/docs/pacem-media.json +1203 -0
  59. package/dist/docs/pacem-networking.json +1798 -0
  60. package/dist/docs/pacem-numerical.json +13706 -0
  61. package/dist/docs/pacem-plus.json +8468 -0
  62. package/dist/docs/pacem-scaffolding.json +34788 -0
  63. package/dist/docs/pacem-ui.json +18628 -0
  64. package/dist/typings/index.d.ts +4109 -31
  65. package/dist/vscode.html-custom.json +1303 -741
  66. package/package.json +4 -2
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem v1.0.0-bessel (https://js.pacem.it)
2
+ * @pacem/pacem v1.0.0-dirac (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -41,7 +41,14 @@
41
41
 
42
42
  //namespace Pacem {
43
43
  const JSON_DATE_PATTERN = /^\/Date\([\d]+\)\/$/i;
44
+ /** Date parsing and arithmetic utilities. */
44
45
  class Dates {
46
+ /**
47
+ * Parses a value into a `Date`. Accepts ISO/parseable date strings, ASP.NET-style
48
+ * `/Date(ticks)/` JSON date strings, numeric timestamps, or a `Date` instance (returned as-is).
49
+ * @param input Value to parse.
50
+ * @returns The resulting `Date`.
51
+ */
45
52
  static parse(input) {
46
53
  let d;
47
54
  if (typeof input === 'string') {
@@ -57,9 +64,19 @@
57
64
  else
58
65
  return input;
59
66
  }
67
+ /**
68
+ * Gets whether the given year is a leap year.
69
+ * @param year Year to check.
70
+ */
60
71
  static isLeapYear(year) {
61
72
  return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
62
73
  }
74
+ /**
75
+ * Gets the number of days in a given month of a given year.
76
+ * @param year Year (used to account for leap Februaries).
77
+ * @param month Zero-based month index (0 = January, ..., 11 = December).
78
+ * @returns Number of days in the month.
79
+ */
63
80
  static daysInMonth(year, month) {
64
81
  return [31, (Dates.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
65
82
  }
@@ -77,6 +94,13 @@
77
94
  static dateOnly(datetime) {
78
95
  return new Date(datetime.getFullYear(), datetime.getMonth(), datetime.getDate());
79
96
  }
97
+ /**
98
+ * Returns a new date obtained by adding a (possibly negative) number of months to `input`,
99
+ * clamping the day-of-month to the target month's length (e.g. Jan 31 + 1 month => Feb 28/29).
100
+ * @param input Starting date.
101
+ * @param value Number of months to add.
102
+ * @returns The resulting date.
103
+ */
80
104
  static addMonths(input, value) {
81
105
  let n = input.getDate(), i = new Date(input), month = i.getMonth() + value, years = 0;
82
106
  while (month < 0) {
@@ -89,9 +113,21 @@
89
113
  i.setDate(Math.min(n, Dates.daysInMonth(i.getFullYear(), i.getMonth())));
90
114
  return i;
91
115
  }
116
+ /**
117
+ * Returns a new date obtained by adding a (possibly negative) number of days to `input`.
118
+ * @param input Starting date.
119
+ * @param value Number of days to add.
120
+ * @returns The resulting date.
121
+ */
92
122
  static addDays(input, value) {
93
123
  return new Date(input.valueOf() + value * 86400000);
94
124
  }
125
+ /**
126
+ * Computes the number of days (possibly fractional) between two dates.
127
+ * @param start Start date.
128
+ * @param end End date.
129
+ * @returns Number of days between `start` and `end` (negative if `end` precedes `start`).
130
+ */
95
131
  static daysBetween(start, end) {
96
132
  return (end.valueOf() - start.valueOf()) / 86400000;
97
133
  }
@@ -100,10 +136,21 @@
100
136
 
101
137
  /// <reference path="dates.ts" />
102
138
  // namespace Pacem {
139
+ /** Null/empty value-checking utilities. */
103
140
  class NullChecker {
141
+ /**
142
+ * Returns whether the given value is `null` or `undefined`.
143
+ * @param o Value to check.
144
+ */
104
145
  static isNull(o) {
105
146
  return o === null || o === void 0;
106
147
  }
148
+ /**
149
+ * Returns whether the given value is "empty": an empty array, an empty string, or a
150
+ * plain object with no own enumerable keys (dates, regexes and file-system handles are never
151
+ * considered empty).
152
+ * @param o Value to check.
153
+ */
107
154
  static isEmpty(o) {
108
155
  try {
109
156
  return (Array.isArray(o) && o.length === 0)
@@ -115,6 +162,10 @@
115
162
  return false;
116
163
  }
117
164
  }
165
+ /**
166
+ * Returns whether the given value is `null`, `undefined`, or "empty" (see {@link NullChecker.isEmpty}).
167
+ * @param o Value to check.
168
+ */
118
169
  static isNullOrEmpty(o) {
119
170
  return NullChecker.isNull(o) || NullChecker.isEmpty(o);
120
171
  }
@@ -209,9 +260,18 @@
209
260
  }
210
261
  return output;
211
262
  }
263
+ /** Deep-merging utility for plain objects. */
212
264
  class DeepMerger {
213
265
  constructor() {
214
266
  }
267
+ /**
268
+ * Recursively merges `source` into `target`, in place: plain-object properties are merged
269
+ * recursively (into a fresh object, never adopted by reference), arrays are shallow-copied,
270
+ * and any other value overwrites the target's.
271
+ * @param source Object whose properties are merged into `target`.
272
+ * @param target Object to merge into (defaults to, and lazily creates, the global `Pacem` namespace object).
273
+ * @returns The mutated `target`, typed as the union of both inputs.
274
+ */
215
275
  static merge(source, target) {
216
276
  target ??= (globalThis['Pacem'] ??= {});
217
277
  return deepMerge(target, source);
@@ -219,6 +279,7 @@
219
279
  }
220
280
 
221
281
  //namespace Pacem {
282
+ /** Array manipulation utilities. */
222
283
  class Arrays {
223
284
  static range(min, max, items = 2) {
224
285
  let x = min;
@@ -237,6 +298,12 @@
237
298
  }
238
299
  return output;
239
300
  }
301
+ /**
302
+ * Splits an array into chunks of a given size.
303
+ * @param source Array to split.
304
+ * @param size Maximum size of each chunk (must be a positive integer).
305
+ * @returns Array of chunks; an empty array if `source` is null or empty.
306
+ */
240
307
  static chunkify(source, size) {
241
308
  if (NullChecker.isNullOrEmpty(source)) {
242
309
  return [];
@@ -254,7 +321,13 @@
254
321
  //}
255
322
 
256
323
  //namespace Pacem {
324
+ /** Conversion utilities between `Blob`, base64/data URLs and plain text. */
257
325
  class Blobs {
326
+ /**
327
+ * Reads a `Blob` and resolves it as a base64-encoded data URL.
328
+ * @param blob Blob to read.
329
+ * @returns A promise resolving with the data URL string.
330
+ */
258
331
  static blobToDataURL(blob) {
259
332
  return new Promise((resolve, _) => {
260
333
  const a = new FileReader();
@@ -270,10 +343,21 @@
270
343
  }
271
344
  return u8arr;
272
345
  }
346
+ /**
347
+ * Converts a base64-encoded string into a `Blob`.
348
+ * @param bstr Base64-encoded string (without the `data:...;base64,` prefix).
349
+ * @param options Blob options (e.g. MIME `type`).
350
+ * @returns The resulting `Blob`.
351
+ */
273
352
  static base64ToBlob(bstr, options) {
274
353
  const u8arr = Blobs._b64ToUint8Array(atob(bstr));
275
354
  return new Blob([u8arr], options);
276
355
  }
356
+ /**
357
+ * Converts a data URL (`data:<mime>;base64,<data>`) into a `Blob`, inferring the MIME type when present.
358
+ * @param dataurl Data URL to convert.
359
+ * @returns The resulting `Blob`.
360
+ */
277
361
  static dataURLToBlob(dataurl) {
278
362
  const coma = dataurl.indexOf(',');
279
363
  if (coma === -1) {
@@ -285,6 +369,12 @@
285
369
  return Blobs.base64ToBlob(bstr, { type: mime });
286
370
  }
287
371
  }
372
+ /**
373
+ * Reads a `Blob` and decodes it as text.
374
+ * @param blob Blob to read.
375
+ * @param encoding Text encoding to use for decoding (defaults to the platform's default, typically UTF-8).
376
+ * @returns A promise resolving with the decoded text.
377
+ */
288
378
  static async blobToText(blob, encoding) {
289
379
  const buffer = await blob.arrayBuffer();
290
380
  const decoder = new TextDecoder(encoding, { fatal: false });
@@ -371,6 +461,11 @@
371
461
  return match ? match[2] : null;
372
462
  };
373
463
  // https://stackoverflow.com/77095874
464
+ /**
465
+ * Detects the browser name and version from a user-agent string.
466
+ * @param userAgent User-agent string to parse (defaults to `navigator.userAgent`).
467
+ * @returns An object with the detected browser `name` (e.g. `Chrome`, `Firefox`, `Safari`, `Edge`, `IE`, `unknown`, ...) and its `version`.
468
+ */
374
469
  function detect(userAgent = navigator.userAgent) {
375
470
  let browser = 'unknown';
376
471
  let version = null;
@@ -569,9 +664,13 @@
569
664
  return v.toString(16).padStart(2, '0');
570
665
  //return Pacem.Strings.leftPad(v.toString(16), 2, '0');
571
666
  }
667
+ /** Color parsing, conversion (RGB/HSL/CIE XYZ) and manipulation utilities. */
572
668
  class Colors {
669
+ /** Minimum WCAG contrast ratio recommended for large-scale text/graphical objects. */
573
670
  static { this.WCAG_MINIMUM_CONTRAST = 3.0; }
671
+ /** Minimum WCAG (AA) contrast ratio recommended for normal-sized text. */
574
672
  static { this.WCAG_NORMAL_TEXT_CONTRAST = 4.5; }
673
+ /** Minimum WCAG (AAA) contrast ratio recommended for enhanced accessibility. */
575
674
  static { this.WCAG_SAFE_CONTRAST = 7.0; }
576
675
  static _clampRGB(rgb) {
577
676
  const clmp = (n) => Math.min(1.0, Math.max(0, n));
@@ -583,6 +682,11 @@
583
682
  static _normalize(rgb) {
584
683
  return [rgb[0] / 255.0, rgb[1] / 255.0, rgb[2] / 255.0, rgb[3]];
585
684
  }
685
+ /**
686
+ * Parses a CSS color string (`rgb()`, `rgba()`, `#rrggbb`, `#rgb`, `hsl()` or `hsla()`) into an {@link Rgba} value.
687
+ * @param clr CSS color string.
688
+ * @returns The parsed color, or `undefined` if the string does not match any supported format.
689
+ */
586
690
  static parse(clr) {
587
691
  clr = (clr || '').toLowerCase();
588
692
  /* rgb(255,255,255) */
@@ -619,6 +723,11 @@
619
723
  return this.rgb(hsla);
620
724
  }
621
725
  }
726
+ /**
727
+ * Converts an {@link Rgba} color into its {@link Hsla} equivalent.
728
+ * @param rgb Color to convert.
729
+ * @returns The converted color.
730
+ */
622
731
  static hsl(rgb) {
623
732
  const l = luminance(rgb), s = saturation(rgb, l), h = hueCircle(rgb);
624
733
  return { h, s, l, a: rgb.a };
@@ -767,6 +876,11 @@
767
876
 
768
877
  // 1.7763568394002505e-15 -20.4551920341394 40 40
769
878
  const FLOAT_PATTERN = /[-+]?([\d]+(\.[\d]+(e-[\d]+)?)?|[\d]*(\.[\d]+(e-[\d]+)?))/g;
879
+ /**
880
+ * Extracts all the floating point numbers found in a string (e.g. `"10px 20px"` => `[10, 20]`).
881
+ * @param input String to parse.
882
+ * @returns Array of the numbers found, in order of appearance.
883
+ */
770
884
  function parseAsNumericalArray(input) {
771
885
  const arr = [];
772
886
  let reg = input.match(FLOAT_PATTERN);
@@ -775,7 +889,12 @@
775
889
  }
776
890
  return arr;
777
891
  }
892
+ /** Angle-related utilities. */
778
893
  class Angle {
894
+ /**
895
+ * Type-guards a value as a valid {@link Angle} object.
896
+ * @param obj Value to check.
897
+ */
779
898
  static isAngle(obj) {
780
899
  return typeof obj === 'object'
781
900
  && 'vertex' in obj && Point.isPoint(obj['vertex'])
@@ -809,7 +928,14 @@
809
928
  return -Math.atan2(cross, dot);
810
929
  }
811
930
  }
931
+ /** Size-related utilities. */
812
932
  class Size {
933
+ /**
934
+ * Parses a string containing exactly two numbers into a {@link Size}.
935
+ * @param sz String to parse (e.g. `"100 50"`).
936
+ * @returns The parsed size.
937
+ * @throws If `sz` does not contain exactly two numbers.
938
+ */
813
939
  static parse(sz) {
814
940
  let arr = parseAsNumericalArray(sz);
815
941
  if (arr && arr.length === 2) {
@@ -817,13 +943,24 @@
817
943
  }
818
944
  throw new Error(`Cannot parse "${sz}" as a valid Size.`);
819
945
  }
946
+ /**
947
+ * Type-guards a value as a valid {@link Size} object.
948
+ * @param obj Value to check.
949
+ */
820
950
  static isSize(obj) {
821
951
  return typeof obj === 'object'
822
952
  && 'width' in obj && typeof obj['width'] === 'number'
823
953
  && 'height' in obj && typeof obj['height'] === 'number';
824
954
  }
825
955
  }
956
+ /** Point-related utilities. */
826
957
  class Point {
958
+ /**
959
+ * Parses a string containing exactly two numbers into a {@link Point}.
960
+ * @param pt String to parse (e.g. `"10 20"`).
961
+ * @returns The parsed point.
962
+ * @throws If `pt` does not contain exactly two numbers.
963
+ */
827
964
  static parse(pt) {
828
965
  let arr = parseAsNumericalArray(pt);
829
966
  if (arr && arr.length === 2) {
@@ -831,6 +968,10 @@
831
968
  }
832
969
  throw new Error(`Cannot parse "${pt}" as a valid Point.`);
833
970
  }
971
+ /**
972
+ * Type-guards a value as a valid {@link Point} object.
973
+ * @param obj Value to check.
974
+ */
834
975
  static isPoint(obj) {
835
976
  return typeof obj === 'object'
836
977
  && 'x' in obj && typeof obj['x'] === 'number'
@@ -877,22 +1018,42 @@
877
1018
  //}
878
1019
 
879
1020
  /// <reference path="geom.ts" />
1021
+ /** 2D affine matrix ([a b; c d] linear part, [e f] translation) manipulation utilities. */
880
1022
  class Matrix2D {
1023
+ /**
1024
+ * Returns whether the given matrix is the identity matrix.
1025
+ * @param m Matrix to check.
1026
+ */
881
1027
  static isIdentity(m) {
882
1028
  return m.a === 1 && m.d === 1 && m.b === 0 && m.f === 0 && m.c === 0 && m.e === 0;
883
1029
  }
1030
+ /** Gets a fresh identity matrix. @readonly */
884
1031
  static get identity() {
885
1032
  return { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 };
886
1033
  }
1034
+ /**
1035
+ * Returns a shallow copy of the given matrix.
1036
+ * @param m Matrix to copy.
1037
+ */
887
1038
  static copy(m) {
888
1039
  return { a: m.a, b: m.b, c: m.c, d: m.d, e: m.e, f: m.f };
889
1040
  }
890
1041
  static scale(m, sx, sy = sx) {
891
1042
  return { a: m.a * sx, b: m.b, c: m.c, d: m.d * sy, e: m.e, f: m.f };
892
1043
  }
1044
+ /**
1045
+ * Returns a copy of `m` translated by the given vector.
1046
+ * @param m Matrix to translate.
1047
+ * @param t Translation vector.
1048
+ */
893
1049
  static translate(m, t) {
894
1050
  return { a: m.a, b: m.b, c: m.c, d: m.d, e: m.e + t.x, f: m.f + t.y };
895
1051
  }
1052
+ /**
1053
+ * Returns a copy of `m` rotated (about the origin) by the given angle.
1054
+ * @param m Matrix to rotate.
1055
+ * @param rad Rotation angle, in radians.
1056
+ */
896
1057
  static rotate(m, rad) {
897
1058
  const cos = Math.cos(rad), sin = Math.sin(rad);
898
1059
  return Matrix2D.multiply(m, { a: cos, b: sin, c: -sin, d: cos, e: 0, f: 0 });
@@ -920,9 +1081,18 @@
920
1081
  f: m1.e * m2.b + m1.f * m2.d + m2.f
921
1082
  };
922
1083
  }
1084
+ /**
1085
+ * Computes the determinant of the matrix's linear part.
1086
+ * @param m Matrix to compute the determinant of.
1087
+ */
923
1088
  static det(m) {
924
1089
  return m.a * m.d - m.c * m.b;
925
1090
  }
1091
+ /**
1092
+ * Computes the inverse of the given matrix.
1093
+ * @param m Matrix to invert.
1094
+ * @returns The inverse matrix, or `null` if `m` is singular (determinant is zero).
1095
+ */
926
1096
  static invert(m) {
927
1097
  const det = this.det(m);
928
1098
  if (det === 0) {
@@ -946,6 +1116,12 @@
946
1116
  f: m.e * m.b - m.a * m.f
947
1117
  }, invdet);
948
1118
  }
1119
+ /**
1120
+ * Decomposes a matrix into its scale, rotation and translation components.
1121
+ * @param m Matrix to decompose.
1122
+ * @param transformOrigin Origin about which the rotation/scale are considered to be applied (defaults to `{x:0, y:0}`).
1123
+ * @returns The decomposed `scaleX`, `scaleY`, `rotation` (radians) and `translation`.
1124
+ */
949
1125
  static toComponents(m, transformOrigin = { x: 0, y: 0 }) {
950
1126
  const scaleX = Math.sqrt(m.a * m.a + m.c * m.c);
951
1127
  const scaleY = Math.sqrt(m.b * m.b + m.d * m.d);
@@ -1009,7 +1185,14 @@
1009
1185
  }
1010
1186
  // }
1011
1187
 
1188
+ /** Rectangle-related utilities (parsing, transform computation, intersection, bounding box). */
1012
1189
  class Rect {
1190
+ /**
1191
+ * Parses a string containing exactly four numbers into a {@link Rect}.
1192
+ * @param rect String to parse (e.g. `"0 0 100 50"`, in `x y width height` order).
1193
+ * @returns The parsed rect.
1194
+ * @throws If `rect` does not contain exactly four numbers.
1195
+ */
1013
1196
  static parse(rect) {
1014
1197
  let arr = parseAsNumericalArray(rect);
1015
1198
  if (arr && arr.length === 4) {
@@ -1017,6 +1200,10 @@
1017
1200
  }
1018
1201
  throw new Error(`Cannot parse "${rect}" as a valid Rect.`);
1019
1202
  }
1203
+ /**
1204
+ * Type-guards a value as a valid {@link Rect} object.
1205
+ * @param obj Value to check.
1206
+ */
1020
1207
  static isRect(obj) {
1021
1208
  return Point.isPoint(obj) && Size.isSize(obj);
1022
1209
  }
@@ -1139,6 +1326,10 @@
1139
1326
  // namespace Pacem {
1140
1327
  /** Grants undo/redo functionalities for given state. */
1141
1328
  class HistoryService {
1329
+ /**
1330
+ * @param state Initial state.
1331
+ * @param maxlength Maximum number of stored states (undo queue + redo stack); unbounded when omitted.
1332
+ */
1142
1333
  constructor(state, maxlength) {
1143
1334
  this.#current = state;
1144
1335
  this.#maxlength = maxlength;
@@ -1156,9 +1347,11 @@
1156
1347
  get current() {
1157
1348
  return this.#current;
1158
1349
  }
1350
+ /** Gets whether there is a previous state to revert to. @readonly */
1159
1351
  get canUndo() {
1160
1352
  return this.#backwards.length > 0;
1161
1353
  }
1354
+ /** Gets whether there is a subsequent state to reapply. @readonly */
1162
1355
  get canRedo() {
1163
1356
  return this.#forwards.length > 0;
1164
1357
  }
@@ -1167,6 +1360,7 @@
1167
1360
  set maxlength(v) {
1168
1361
  this.#maxlength = v;
1169
1362
  }
1363
+ /** Reverts to the previous state, if any (see {@link canUndo}), moving the current state onto the redo stack. */
1170
1364
  undo() {
1171
1365
  if (this.canUndo) {
1172
1366
  const back = this.#backwards, fore = this.#forwards, current = this.#current;
@@ -1174,6 +1368,7 @@
1174
1368
  this.#current = back.pop();
1175
1369
  }
1176
1370
  }
1371
+ /** Reapplies the next state, if any (see {@link canRedo}), moving the current state onto the undo queue. */
1177
1372
  redo() {
1178
1373
  if (this.canRedo) {
1179
1374
  const back = this.#backwards, fore = this.#forwards, current = this.#current;
@@ -1181,6 +1376,11 @@
1181
1376
  this.#current = fore.shift();
1182
1377
  }
1183
1378
  }
1379
+ /**
1380
+ * Pushes a new current state, clearing the redo stack and enqueuing the previous current state for undo.
1381
+ * Trims the undo queue to `maxlength`, if set.
1382
+ * @param state New current state.
1383
+ */
1184
1384
  push(state) {
1185
1385
  this.#forwards.splice(0);
1186
1386
  this.#backwards.push(this.#current);
@@ -1190,6 +1390,7 @@
1190
1390
  queue.splice(0, queue.length - max);
1191
1391
  }
1192
1392
  }
1393
+ /** Clears both the undo queue and the redo stack, leaving the current state untouched. */
1193
1394
  reset() {
1194
1395
  this.#forwards.splice(0);
1195
1396
  this.#backwards.splice(0);
@@ -1205,7 +1406,13 @@
1205
1406
  quality: 1,
1206
1407
  type: 'image/jpeg'
1207
1408
  };
1409
+ /** Image loading and resizing utilities, based on `OffscreenCanvas`. */
1208
1410
  class Imaging {
1411
+ /**
1412
+ * Fetches an image from a URL as a `Blob`.
1413
+ * @param url URL of the image to load.
1414
+ * @returns A promise resolving with the loaded `Blob`, or `null` if the fetch response was not OK.
1415
+ */
1209
1416
  static async loadImage(url) {
1210
1417
  const response = await fetch(url);
1211
1418
  if (!response.ok) {
@@ -1248,16 +1455,30 @@
1248
1455
  // }
1249
1456
 
1250
1457
  // namespace Pacem {
1458
+ /** Unique key/id generation utilities. */
1251
1459
  class Keys {
1252
1460
  static #seed;
1461
+ /**
1462
+ * Generates a process-wide monotonically increasing numeric id, seeded from the current timestamp.
1463
+ * @returns A new unique numeric id.
1464
+ */
1253
1465
  static uniqueId() {
1254
1466
  let seed = Keys.#seed || Date.now();
1255
1467
  return Keys.#seed = ++seed;
1256
1468
  }
1469
+ /**
1470
+ * Generates a process-wide unique id, base-62 encoded.
1471
+ * @returns A new unique string code.
1472
+ */
1257
1473
  static uniqueCode() {
1258
1474
  const newid = Keys.uniqueId();
1259
1475
  return newid.toBase62();
1260
1476
  }
1477
+ /**
1478
+ * Generates a random base-62 string of at least the given length.
1479
+ * @param length Minimum length of the generated string.
1480
+ * @returns A random string.
1481
+ */
1261
1482
  static randomString(length) {
1262
1483
  let retval = '';
1263
1484
  do {
@@ -1269,13 +1490,32 @@
1269
1490
  // }
1270
1491
 
1271
1492
  // namespace Pacem {
1493
+ /** Numeric utilities. */
1272
1494
  class Numbers {
1495
+ /**
1496
+ * Rounds a number to the nearest multiple of `step`.
1497
+ * @param x Value to round.
1498
+ * @param step Rounding step/increment (defaults to 1).
1499
+ * @returns The rounded value.
1500
+ */
1273
1501
  static round(x, step = 1.0) {
1274
1502
  return Math.round(x / step) * step;
1275
1503
  }
1504
+ /**
1505
+ * Computes the logarithm of `x` in an arbitrary `base`.
1506
+ * @param x Value to compute the logarithm of.
1507
+ * @param base Logarithm base.
1508
+ */
1276
1509
  static log(x, base) {
1277
1510
  return Math.log10(x) / Math.log10(base);
1278
1511
  }
1512
+ /**
1513
+ * Converts a number (or numeric string) from one base to another.
1514
+ * @param v Value to convert, expressed in base `from`.
1515
+ * @param from Source base (2-36).
1516
+ * @param to Target base (2-36).
1517
+ * @returns The value re-expressed in base `to`, as a string; `"NaN"` if `v` is not a valid number in base `from`.
1518
+ */
1279
1519
  static rebase(v, from, to) {
1280
1520
  const vStr = v.toString();
1281
1521
  const firstPass = parseInt(vStr, from);
@@ -1428,6 +1668,7 @@
1428
1668
  return diffInternal(aTokens, bTokens, dp, '\n');
1429
1669
  }
1430
1670
  //#endregion
1671
+ /** String manipulation utilities. */
1431
1672
  class Strings {
1432
1673
  /** @deprecated Use String.prototype.padStart() instead. */
1433
1674
  static leftPad(v, targetLength, padChar) {
@@ -1437,6 +1678,14 @@
1437
1678
  // retval = padChar + retval;
1438
1679
  //return retval;
1439
1680
  }
1681
+ /**
1682
+ * Formats a template string, replacing `{0}`, `{1}`, ... placeholders with the corresponding arguments.
1683
+ * Doubled braces (`{{`/`}}`) escape a placeholder.
1684
+ * @param template Template string containing `{n}` placeholders.
1685
+ * @param args Values to substitute into the placeholders, by index.
1686
+ * @returns The formatted string.
1687
+ * @throws If the template references an argument index that was not supplied.
1688
+ */
1440
1689
  static format(template, ...args) {
1441
1690
  const array = Array.from(args);
1442
1691
  let retval = template;
@@ -1452,6 +1701,13 @@
1452
1701
  }
1453
1702
  return retval;
1454
1703
  }
1704
+ /**
1705
+ * Computes a diff between two strings, using the Longest Common Subsequence algorithm.
1706
+ * @param a First (original) string.
1707
+ * @param b Second (modified) string.
1708
+ * @param mode Diffing granularity: `'char'` (default), `'word'` or `'line'`.
1709
+ * @returns Array of {@link DiffToken}s describing unchanged, removed and added chunks.
1710
+ */
1455
1711
  static diff(a, b, mode = 'char') {
1456
1712
  switch (mode) {
1457
1713
  case 'line':
@@ -1462,6 +1718,12 @@
1462
1718
  return diffChars(a, b);
1463
1719
  }
1464
1720
  }
1721
+ /**
1722
+ * Computes the cryptographic hash of a string using the SubtleCrypto API.
1723
+ * @param input String to hash.
1724
+ * @param algo Hash algorithm to use (defaults to `SHA-256`).
1725
+ * @returns A promise resolving with the hash, hex-encoded.
1726
+ */
1465
1727
  static async hash(input, algo = 'SHA-256') {
1466
1728
  const hashBuffer = await crypto.subtle.digest(algo, new TextEncoder().encode(input));
1467
1729
  return Array.from(new Uint8Array(hashBuffer))
@@ -1494,10 +1756,21 @@
1494
1756
  return tokens;
1495
1757
  }
1496
1758
  }
1759
+ /** Generic tokenize-then-render text parser: lexes input against a grammar, then renders the resulting token tree. */
1497
1760
  let Parser$1 = class Parser {
1761
+ /**
1762
+ * @param _lexer Lexer implementation to use (defaults to a built-in greedy, rule-ordered lexer).
1763
+ */
1498
1764
  constructor(_lexer = new LexerClass()) {
1499
1765
  this._lexer = _lexer;
1500
1766
  }
1767
+ /**
1768
+ * Tokenizes `input` against `grammar` and renders the resulting token tree.
1769
+ * @param input String to parse.
1770
+ * @param grammar Ordered list of rules describing the grammar.
1771
+ * @param renderer Renderer used to turn the token tree into the output string.
1772
+ * @returns The rendered output.
1773
+ */
1501
1774
  parse(input, grammar, renderer) {
1502
1775
  const tokens = this._lexer.tokenize(input, grammar);
1503
1776
  return renderer.render(tokens);
@@ -1620,6 +1893,12 @@
1620
1893
  MARKDOWN_RULES[MARKDOWN_RULES["Text"] = 254] = "Text";
1621
1894
  MARKDOWN_RULES[MARKDOWN_RULES["Space"] = 255] = "Space";
1622
1895
  })(MARKDOWN_RULES || (MARKDOWN_RULES = {}));
1896
+ /**
1897
+ * Builds the full Markdown grammar (block + inline rules, comment/reference handling first),
1898
+ * optionally merging in caller-supplied {@link ExtraRule}s.
1899
+ * @param extra Additional block/inline/code rules to plug into the built-in grammar.
1900
+ * @returns Ordered list of {@link Compile.Rule}s ready to be fed to a {@link Compile.Lexer}.
1901
+ */
1623
1902
  function getGrammar(extra = []) {
1624
1903
  const inert = {
1625
1904
  // comment
@@ -1970,6 +2249,7 @@
1970
2249
  return input?.replace(TRIM_INSIDE, '\n').trim();
1971
2250
  }
1972
2251
  // #endregion
2252
+ /** Renders a Markdown token tree, as produced by the grammar returned by {@link getGrammar}, into HTML markup. */
1973
2253
  class HtmlRenderer {
1974
2254
  constructor(_extraItemRender = (token, __) => token?.text || '') {
1975
2255
  this._extraItemRender = _extraItemRender;
@@ -2061,6 +2341,7 @@
2061
2341
  return trim(this._render(tree));
2062
2342
  }
2063
2343
  }
2344
+ /** Renders a Markdown token tree as a JSON array of tokens, mainly useful for debugging/inspection. */
2064
2345
  class TokenRenderer {
2065
2346
  _render(token) {
2066
2347
  return NullChecker.isNullOrEmpty(token) ? '' : JSON.stringify(token);
@@ -2072,7 +2353,15 @@
2072
2353
  function isRenderer(obj) {
2073
2354
  return obj && typeof obj === 'object' && typeof obj.render === 'function';
2074
2355
  }
2356
+ /** Markdown parser: tokenizes Markdown source and renders it to HTML (or another format via a custom {@link Compile.Renderer}). */
2075
2357
  class Parser extends Parser$1 {
2358
+ /**
2359
+ * Parses Markdown `input` and renders it to an HTML string.
2360
+ * @param input Markdown source.
2361
+ * @param extraGrammar Additional grammar rules to extend the built-in Markdown syntax.
2362
+ * @param extraItemRenderer Fallback renderer invoked for token types not natively handled (e.g. tokens produced by `extraGrammar`); defaults to returning the token's `text`.
2363
+ * @returns The rendered HTML string.
2364
+ */
2076
2365
  toHtml(input, extraGrammar, extraItemRenderer) {
2077
2366
  const renderer = new HtmlRenderer(extraItemRenderer);
2078
2367
  if (NullChecker.isNullOrEmpty(extraGrammar)) {
@@ -2080,6 +2369,12 @@
2080
2369
  }
2081
2370
  return this.parse(input, getGrammar(extraGrammar), renderer);
2082
2371
  }
2372
+ /**
2373
+ * Parses Markdown `input` and returns its token tree as a plain (JSON-serializable) array, useful for inspection/debugging.
2374
+ * @param input Markdown source.
2375
+ * @param extraGrammar Additional grammar rules to extend the built-in Markdown syntax.
2376
+ * @returns The parsed token tree.
2377
+ */
2083
2378
  tokenize(input, extraGrammar) {
2084
2379
  const json = this.parse(input, getGrammar(extraGrammar), new TokenRenderer());
2085
2380
  return JSON.parse(json);