@diagrammo/dgmo 0.49.0 → 0.50.1

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 (198) hide show
  1. package/.cursorrules +6 -2
  2. package/.github/copilot-instructions.md +6 -2
  3. package/.windsurfrules +6 -2
  4. package/SKILL.md +6 -2
  5. package/dist/advanced.js +557 -195
  6. package/dist/auto.js +129 -119
  7. package/dist/auto.mjs +19496 -12761
  8. package/dist/block.js +145 -48
  9. package/dist/{charts-d3-EW3YQTEA.js → charts-d3-LR6D3D7A.js} +5 -5
  10. package/dist/{chunk-5FGDJIJX.js → chunk-2C56LEML.js} +3 -3
  11. package/dist/{chunk-XJ7J6WOO.js → chunk-2KGXHFYK.js} +4 -4
  12. package/dist/{chunk-T7IKPCMI.js → chunk-3LH5CLP4.js} +3 -3
  13. package/dist/{chunk-ZDUQGL3N.js → chunk-3LKM6FMW.js} +2 -2
  14. package/dist/{chunk-4FIV4K4Z.js → chunk-3NODSS6A.js} +7 -7
  15. package/dist/{chunk-4BD7EVPK.js → chunk-3P2DKJCG.js} +2 -2
  16. package/dist/{chunk-TC3YWBFO.js → chunk-3UIKSPFB.js} +35 -9
  17. package/dist/{chunk-SJEGO2QL.js → chunk-3XGZF2QJ.js} +4 -4
  18. package/dist/chunk-3XPUDB7B.js +420 -0
  19. package/dist/{chunk-O5MQPK7E.js → chunk-3Y2DRD7S.js} +3 -3
  20. package/dist/chunk-3YPY3OXD.js +354 -0
  21. package/dist/{chunk-N4J5KGWG.js → chunk-4MJ7M6YL.js} +8 -8
  22. package/dist/{chunk-JFPY3UJ7.js → chunk-4S52SK6G.js} +8 -8
  23. package/dist/{chunk-ALRHIR6I.js → chunk-5GJYGP5Z.js} +4 -4
  24. package/dist/{chunk-SQDFK5ZQ.js → chunk-5OWVC2A6.js} +3 -3
  25. package/dist/{chunk-7VLUGEMJ.js → chunk-66NVRO4K.js} +4 -4
  26. package/dist/{chunk-ZREZOIGS.js → chunk-66RUL3XF.js} +6 -6
  27. package/dist/{chunk-MJCG5K2O.js → chunk-6BFZKBGD.js} +2 -2
  28. package/dist/{chunk-2WMBWZY2.js → chunk-6HJBFVLO.js} +3 -3
  29. package/dist/{chunk-XCC2RENT.js → chunk-6IAA2QQ2.js} +2 -2
  30. package/dist/{chunk-B57V3DWN.js → chunk-7L3R6I3U.js} +3 -3
  31. package/dist/chunk-7PQYTZMA.js +681 -0
  32. package/dist/{chunk-6SA7FJE4.js → chunk-7QKLIMRZ.js} +10 -10
  33. package/dist/{chunk-YTN572Q2.js → chunk-7TV5Y53Y.js} +2 -2
  34. package/dist/{chunk-6CLKNXVP.js → chunk-BBGDM4NQ.js} +457 -78
  35. package/dist/{chunk-3NFJWFNP.js → chunk-BDXC7MDK.js} +1045 -124
  36. package/dist/{chunk-D5AVQCDN.js → chunk-BH3UYK36.js} +3 -3
  37. package/dist/chunk-BTUTZMPT.js +482 -0
  38. package/dist/{chunk-LVHPTBO3.js → chunk-C2CVAM3U.js} +4 -4
  39. package/dist/{chunk-BCT5G2VN.js → chunk-DFTFPYUD.js} +5 -5
  40. package/dist/{chunk-KV6Y2HE6.js → chunk-DSWVNQVS.js} +4 -4
  41. package/dist/{chunk-4YNP6O47.js → chunk-DZXYF2F5.js} +13 -3
  42. package/dist/{chunk-FUCAB3KQ.js → chunk-EAMEADTV.js} +3 -3
  43. package/dist/chunk-FA6K3FWM.js +940 -0
  44. package/dist/{chunk-HBVD67XG.js → chunk-FJUNPCYI.js} +2 -2
  45. package/dist/{chunk-6VE3VLWM.js → chunk-GBE54KY6.js} +6 -5
  46. package/dist/{chunk-YRVLP7UU.js → chunk-GKC3HILQ.js} +4 -4
  47. package/dist/{chunk-ZXL6MB4R.js → chunk-GL5UZ6NY.js} +3 -3
  48. package/dist/{chunk-GB3YZTAM.js → chunk-H73FP42Q.js} +5 -5
  49. package/dist/{chunk-4UDKKGHH.js → chunk-HEVYAHLW.js} +2 -2
  50. package/dist/{chunk-DDTJ5N7Q.js → chunk-HLJVEAOF.js} +6 -6
  51. package/dist/{chunk-CLR4A2N2.js → chunk-HTMN2Z6U.js} +4 -4
  52. package/dist/{chunk-TLKMTQYL.js → chunk-I2WKW7C7.js} +3 -3
  53. package/dist/{chunk-IUK7AGD4.js → chunk-I7F2A35O.js} +2 -2
  54. package/dist/{chunk-IBC35BTK.js → chunk-IUMPEA64.js} +2 -2
  55. package/dist/{chunk-5XCM7UUW.js → chunk-J6K7RP6P.js} +100 -36
  56. package/dist/{chunk-AJ2PEY7J.js → chunk-JJUOHGX2.js} +2 -2
  57. package/dist/{chunk-CEHZOPGM.js → chunk-KFZYNWWO.js} +2 -2
  58. package/dist/{chunk-4NQ3IKUM.js → chunk-KHEYDLMT.js} +5 -5
  59. package/dist/{chunk-YM2BSPYW.js → chunk-LE4MYMLI.js} +2 -2
  60. package/dist/{chunk-PU3AUQPA.js → chunk-M5RXSRO2.js} +2 -2
  61. package/dist/{chunk-XKTN7CZF.js → chunk-MCZ5LBZD.js} +4 -4
  62. package/dist/{chunk-35BBZDFE.js → chunk-NU3XLTGP.js} +4 -4
  63. package/dist/{chunk-BPIJLRYE.js → chunk-NWED7CYO.js} +3 -3
  64. package/dist/{chunk-YYX3SU6H.js → chunk-OMNGQPEZ.js} +257 -101
  65. package/dist/{chunk-BGJV5ATD.js → chunk-ONA5KPV3.js} +2 -2
  66. package/dist/{chunk-ZNQX6QWH.js → chunk-OTAL43EH.js} +6 -2
  67. package/dist/{chunk-F743CM2N.js → chunk-OTQKUHRS.js} +3 -3
  68. package/dist/{chunk-KUBBJ2CD.js → chunk-P7CE7LXL.js} +6 -6
  69. package/dist/{chunk-UVCXZEGU.js → chunk-PFHVU7UA.js} +2 -2
  70. package/dist/{chunk-5ZZLLBFK.js → chunk-PGEGPSCZ.js} +2 -2
  71. package/dist/chunk-Q4V4XQWV.js +158 -0
  72. package/dist/{chunk-IHLSHCPZ.js → chunk-QJP6PG7F.js} +5 -5
  73. package/dist/{chunk-EYN7VFKL.js → chunk-QROYX7TT.js} +3 -3
  74. package/dist/chunk-QXKWYZNR.js +379 -0
  75. package/dist/{chunk-VC3TR4NL.js → chunk-RZ6YKEUJ.js} +3 -3
  76. package/dist/{chunk-K6HNAY5V.js → chunk-SCHXJTZL.js} +2 -2
  77. package/dist/{chunk-7OVSJI2V.js → chunk-SQLNBA64.js} +3 -3
  78. package/dist/{chunk-65RTDUZH.js → chunk-SXRTRMEE.js} +3 -3
  79. package/dist/{chunk-3BIAFYD2.js → chunk-T4VOVZT5.js} +2 -2
  80. package/dist/{chunk-UWDO3O52.js → chunk-TN35C5KM.js} +2 -2
  81. package/dist/{chunk-IFK3GIUW.js → chunk-TSA237IM.js} +2 -2
  82. package/dist/{chunk-DKEPS5LH.js → chunk-U2HGPLZS.js} +36 -2
  83. package/dist/{chunk-4HVNZ7WF.js → chunk-VBYSIOOY.js} +3 -3
  84. package/dist/{chunk-ZTLWVRNS.js → chunk-VDOOQZUD.js} +2 -2
  85. package/dist/{chunk-RTERS6S5.js → chunk-VUJGMJE6.js} +4 -4
  86. package/dist/{chunk-X2CH42BN.js → chunk-VVW3F3MD.js} +7 -7
  87. package/dist/{chunk-RI4NIMFP.js → chunk-VVZRV54S.js} +75 -35
  88. package/dist/{chunk-LG4TWW4U.js → chunk-WW32K72E.js} +8 -8
  89. package/dist/{chunk-NVT6C2VE.js → chunk-X6EZPSW6.js} +4 -4
  90. package/dist/{chunk-KQAEKMIX.js → chunk-XDXNDSKO.js} +2 -2
  91. package/dist/{chunk-FMKTYMP4.js → chunk-XJLKLU4D.js} +3 -3
  92. package/dist/chunk-Y4RNXEAO.js +952 -0
  93. package/dist/{chunk-26RVGM44.js → chunk-YIA4LEX4.js} +15 -7
  94. package/dist/chunk-YNRDWYZP.js +295 -0
  95. package/dist/{chunk-SGHSY3LU.js → chunk-Z6XJQ6KO.js} +81 -6
  96. package/dist/{chunk-SVDWQGX2.js → chunk-ZAD4HV2G.js} +139 -78
  97. package/dist/cli.cjs +153 -144
  98. package/dist/clock.d.ts +10 -0
  99. package/dist/clock.js +235 -0
  100. package/dist/countdown.d.ts +12 -0
  101. package/dist/countdown.js +450 -0
  102. package/dist/dgmo-router-YTU3ENAX.js +84 -0
  103. package/dist/{dimensions-WSIJXT5Q.js → dimensions-OTBKC5M6.js} +5 -5
  104. package/dist/editor.js +39 -1
  105. package/dist/element.js +131 -121
  106. package/dist/element.mjs +19496 -12761
  107. package/dist/{flowchart-parser-IVZN7CQN.js → flowchart-parser-OE4WHLTF.js} +4 -4
  108. package/dist/{flowchart-renderer-GMI2U3DD.js → flowchart-renderer-GPCSMTOR.js} +6 -6
  109. package/dist/highlight.d.ts +17 -1
  110. package/dist/highlight.js +156 -5
  111. package/dist/index.js +50 -45
  112. package/dist/{layout-HQMBPJVS.js → layout-46366KBT.js} +2 -2
  113. package/dist/{layout-D42XE4RE.js → layout-5BG6DFCU.js} +3 -3
  114. package/dist/{layout-K2ZN3Q42.js → layout-7MZ5NF2N.js} +4 -4
  115. package/dist/{layout-4Z5JM7PT.js → layout-FWODLIMP.js} +4 -4
  116. package/dist/{layout-GZ2CWONU.js → layout-J36454XU.js} +4 -4
  117. package/dist/{layout-S3EK4IYO.js → layout-LD6XTLD6.js} +4 -4
  118. package/dist/{parser-CLMFZWCC.js → parser-36HVKU5U.js} +5 -5
  119. package/dist/{parser-MCJWF5G3.js → parser-4AI5ZN5F.js} +3 -3
  120. package/dist/{parser-DERPY5KC.js → parser-4NO6VZ6S.js} +4 -4
  121. package/dist/parser-4WQQQXCG.js +13 -0
  122. package/dist/{parser-4ROBKVUR.js → parser-5OXGPNBF.js} +5 -5
  123. package/dist/{parser-XWUDFJ7P.js → parser-AGVYTQXV.js} +4 -4
  124. package/dist/{parser-TITK4YW6.js → parser-AXKBDGNO.js} +5 -5
  125. package/dist/parser-B5FVS7O6.js +11 -0
  126. package/dist/parser-CSFUN3CT.js +14 -0
  127. package/dist/parser-CVMEDULU.js +15 -0
  128. package/dist/{parser-KPMN5KKE.js → parser-D5VILGN2.js} +4 -4
  129. package/dist/{parser-IKZFTLOJ.js → parser-DFFEDKHH.js} +3 -3
  130. package/dist/{parser-JD7OQ2E6.js → parser-H3XEPFPV.js} +4 -4
  131. package/dist/{parser-UBGPTIZD.js → parser-HYRTF3DO.js} +3 -3
  132. package/dist/{parser-K647FRI4.js → parser-IBWNAPY4.js} +3 -3
  133. package/dist/{parser-4BKIFG2C.js → parser-IJBMRXMC.js} +5 -5
  134. package/dist/{parser-MBJSBTL5.js → parser-JJR52VAT.js} +5 -5
  135. package/dist/{parser-SMBJO3XN.js → parser-K3XXLMUW.js} +4 -4
  136. package/dist/{parser-OBQG655V.js → parser-KCR2WS3V.js} +4 -4
  137. package/dist/{parser-HVJFTOXL.js → parser-LWKKBNSA.js} +3 -3
  138. package/dist/{parser-ZYC3XGNS.js → parser-MPCE5JBG.js} +4 -4
  139. package/dist/{parser-QJYS7KQQ.js → parser-MTOMTWTD.js} +4 -4
  140. package/dist/{parser-GFSOPDB7.js → parser-O4HIUN6G.js} +4 -4
  141. package/dist/{parser-BL7Z72EY.js → parser-PSA4UBSP.js} +4 -4
  142. package/dist/{parser-TNYX5S7E.js → parser-RV4M6AS2.js} +4 -4
  143. package/dist/parser-RZPVAQVX.js +11 -0
  144. package/dist/{parser-UGA23PYK.js → parser-TGQZJ5OT.js} +4 -4
  145. package/dist/parser-TXND652E.js +13 -0
  146. package/dist/{parser-WISPH7LW.js → parser-VKOJM7KS.js} +4 -4
  147. package/dist/{parser-ZKF5K2H2.js → parser-W4PQUGX3.js} +4 -4
  148. package/dist/{parser-MRBPXXIV.js → parser-WAV5JRPT.js} +4 -4
  149. package/dist/{parser-D6ONJQFR.js → parser-YS7TOS2K.js} +3 -3
  150. package/dist/pert.d.ts +8 -0
  151. package/dist/{renderer-VFUVRNLK.js → renderer-2AZ5HJFK.js} +4 -4
  152. package/dist/{renderer-V3JEMWMM.js → renderer-2QPMJ2DA.js} +6 -6
  153. package/dist/renderer-45PSOL3E.js +19 -0
  154. package/dist/{renderer-BB5PREMY.js → renderer-4ITCHBT7.js} +4 -4
  155. package/dist/{renderer-XEKIRPI6.js → renderer-5DSEWF74.js} +5 -5
  156. package/dist/{renderer-QNHUXCRL.js → renderer-6OKF44KS.js} +9 -9
  157. package/dist/renderer-72LGR2YF.js +14 -0
  158. package/dist/{renderer-YHXPN3GF.js → renderer-7SD6MSBS.js} +7 -7
  159. package/dist/renderer-A5VE5BCI.js +24 -0
  160. package/dist/{renderer-A5VFZQQK.js → renderer-AUW2BE6G.js} +6 -6
  161. package/dist/{renderer-LWGJFT3D.js → renderer-B5TBNZJE.js} +5 -5
  162. package/dist/{renderer-4IUPINAE.js → renderer-BSR2O2H6.js} +7 -7
  163. package/dist/{renderer-DKESNMU5.js → renderer-CYB4LK3G.js} +6 -6
  164. package/dist/{renderer-MFJ7HY2H.js → renderer-FF24KZCI.js} +5 -5
  165. package/dist/{renderer-CGW7QPNA.js → renderer-FPE3J5VZ.js} +7 -7
  166. package/dist/{renderer-6T3KK4S3.js → renderer-FYSVXL7M.js} +7 -7
  167. package/dist/{renderer-THD6V5TL.js → renderer-HDCCZURL.js} +9 -7
  168. package/dist/{renderer-TPIBXNOS.js → renderer-JCCHNECZ.js} +6 -6
  169. package/dist/{renderer-QL5KO6SK.js → renderer-KS7YZJE2.js} +7 -7
  170. package/dist/{renderer-SQ3JUAI7.js → renderer-NGXZTRK6.js} +6 -6
  171. package/dist/{renderer-MJDDU27W.js → renderer-ODUUOPVT.js} +10 -10
  172. package/dist/renderer-PJM35LJN.js +17 -0
  173. package/dist/{renderer-TYXRHMRI.js → renderer-PPIHO5FI.js} +9 -9
  174. package/dist/{renderer-W5RMOURB.js → renderer-PSMMZCKH.js} +6 -6
  175. package/dist/{renderer-UZF3JROD.js → renderer-QAOBLRXV.js} +8 -8
  176. package/dist/{renderer-ROV756WT.js → renderer-R2WNIC2C.js} +5 -5
  177. package/dist/{renderer-VICQ4WNY.js → renderer-SBS3YWU5.js} +5 -5
  178. package/dist/{renderer-6L3NBEY5.js → renderer-UYMQQCJK.js} +6 -6
  179. package/dist/{renderer-radial-MHO2VDXN.js → renderer-radial-F7X5F2VU.js} +6 -6
  180. package/dist/{state-parser-RX3GXOIB.js → state-parser-3REXRK3R.js} +4 -4
  181. package/dist/{state-renderer-AJUKGNWU.js → state-renderer-XYLNRWXR.js} +6 -6
  182. package/docs/ai-integration.md +6 -2
  183. package/docs/language-reference.md +253 -22
  184. package/gallery/fixtures/body.dgmo +17 -0
  185. package/gallery/fixtures/bracket.dgmo +30 -0
  186. package/gallery/fixtures/clock.dgmo +8 -0
  187. package/gallery/fixtures/countdown.dgmo +3 -0
  188. package/gallery/fixtures/goal.dgmo +4 -0
  189. package/package.json +12 -4
  190. package/dist/advanced.d.ts +0 -6162
  191. package/dist/auto.d.ts +0 -54
  192. package/dist/block.d.ts +0 -123
  193. package/dist/dgmo-router-ODKPEJ3P.js +0 -79
  194. package/dist/element.d.ts +0 -62
  195. package/dist/index.d.ts +0 -166
  196. package/dist/parser-667RGSUW.js +0 -13
  197. package/dist/parser-J3P2D6IP.js +0 -13
  198. package/dist/themes-1-CuKpeH.d.ts +0 -908
@@ -0,0 +1,952 @@
1
+ import {
2
+ parseInlineMarkdown,
3
+ renderInlineText
4
+ } from "./chunk-3HDBAXF5.js";
5
+ import {
6
+ wrapDescriptionLines
7
+ } from "./chunk-BMR4CIEO.js";
8
+ import {
9
+ DAY_MS,
10
+ applyOrdinalTemplate,
11
+ formatCompound,
12
+ formatCount,
13
+ formatDateShort,
14
+ formatFooter,
15
+ formatHuman,
16
+ formatWordsDetail,
17
+ ordinalFor
18
+ } from "./chunk-YNRDWYZP.js";
19
+ import {
20
+ FONT_FAMILY
21
+ } from "./chunk-6KFWC4CO.js";
22
+ import {
23
+ getSeriesColors,
24
+ mix,
25
+ themeBaseBg
26
+ } from "./chunk-P6T66CCZ.js";
27
+ import {
28
+ resolveColor
29
+ } from "./chunk-TLVIHFRO.js";
30
+
31
+ // src/countdown/renderer.ts
32
+ import * as d3Selection from "d3-selection";
33
+ function bakedHero(parsed, now) {
34
+ const resolved = parsed.resolvedMs;
35
+ if (resolved === null) return "\u2014";
36
+ const remaining = resolved - now;
37
+ if (!parsed.rule && remaining <= 0) {
38
+ if (parsed.expired !== null) return parsed.expired;
39
+ const elapsed = -remaining;
40
+ if (elapsed <= 0) return "Now!";
41
+ if (parsed.units === "compound" || parsed.units === "human")
42
+ return `${formatCompound(resolved, now)} ago`;
43
+ const bu = parsed.units === "full" || parsed.units === "clock" ? "days" : parsed.units;
44
+ return `${formatCount(elapsed, { units: bu, round: parsed.round, fields: parsed.fields })} ago`;
45
+ }
46
+ if (parsed.rule && sameLocalDay(resolved, now))
47
+ return parsed.onDay ?? "Today!";
48
+ if (parsed.units === "human") {
49
+ const [hn, ht] = parsed.hasTime ? [now, resolved] : [DAY_START(now), DAY_START(resolved)];
50
+ return formatHuman(hn, ht).big;
51
+ }
52
+ if (parsed.units === "compound") return formatCompound(now, resolved);
53
+ const bakeUnits = parsed.units === "full" || parsed.units === "clock" ? "days" : parsed.units;
54
+ return formatCount(Math.max(0, remaining), {
55
+ units: bakeUnits,
56
+ round: parsed.round,
57
+ fields: parsed.fields
58
+ });
59
+ }
60
+ function sameLocalDay(a, b) {
61
+ const x = new Date(a);
62
+ const y = new Date(b);
63
+ return x.getFullYear() === y.getFullYear() && x.getMonth() === y.getMonth() && x.getDate() === y.getDate();
64
+ }
65
+ function stampRecur(sel, parsed) {
66
+ const r = parsed.rule;
67
+ if (!r) return;
68
+ sel.attr("data-dgmo-recur-kind", r.kind);
69
+ if (r.month !== void 0) sel.attr("data-dgmo-recur-month", r.month);
70
+ if (r.day !== void 0) sel.attr("data-dgmo-recur-day", r.day);
71
+ if (r.nth !== void 0) sel.attr("data-dgmo-recur-nth", r.nth);
72
+ if (r.weekday !== void 0) sel.attr("data-dgmo-recur-weekday", r.weekday);
73
+ sel.attr("data-dgmo-recur-hour", r.hour);
74
+ sel.attr("data-dgmo-recur-minute", r.minute);
75
+ sel.attr("data-dgmo-recur-allday", r.allDay ? "1" : "0");
76
+ if (r.intervalN !== void 0)
77
+ sel.attr("data-dgmo-recur-interval-n", r.intervalN);
78
+ if (r.intervalUnit !== void 0)
79
+ sel.attr("data-dgmo-recur-interval-unit", r.intervalUnit);
80
+ if (r.anchorMs !== void 0) sel.attr("data-dgmo-recur-anchor", r.anchorMs);
81
+ }
82
+ var MON_ABBR = [
83
+ "Jan",
84
+ "Feb",
85
+ "Mar",
86
+ "Apr",
87
+ "May",
88
+ "Jun",
89
+ "Jul",
90
+ "Aug",
91
+ "Sep",
92
+ "Oct",
93
+ "Nov",
94
+ "Dec"
95
+ ];
96
+ var WD_ABBR = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
97
+ var DAY_START = (ms) => {
98
+ const d = new Date(ms);
99
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
100
+ };
101
+ var dayDelta = (a, b) => Math.round((DAY_START(b) - DAY_START(a)) / DAY_MS);
102
+ var keyOf = (ms) => {
103
+ const d = new Date(ms);
104
+ return d.getFullYear() * 1e4 + d.getMonth() * 100 + d.getDate();
105
+ };
106
+ function bandColors(palette, accent, muted, faint) {
107
+ const blue = resolveColor("blue", palette) ?? mix(palette.text, palette.bg, 45);
108
+ const collides = accent.toLowerCase() === blue.toLowerCase();
109
+ const now = collides ? resolveColor("teal", palette) ?? resolveColor("cyan", palette) ?? mix(palette.text, palette.bg, 55) : blue;
110
+ const mid = mix(accent, now, 50);
111
+ return {
112
+ bg: palette.bg,
113
+ text: palette.text,
114
+ muted,
115
+ faint,
116
+ rule: mix(palette.text, palette.bg, 14),
117
+ now,
118
+ event: accent,
119
+ midSoft: mix(mid, palette.bg, 24),
120
+ inert: mix(palette.text, palette.bg, 7),
121
+ inertBorder: mix(palette.text, palette.bg, 22)
122
+ };
123
+ }
124
+ function roleStyle(C, role) {
125
+ switch (role) {
126
+ case "past":
127
+ return { fill: C.inert, stroke: C.inertBorder, text: C.faint };
128
+ case "future":
129
+ return { fill: C.inert, stroke: C.inertBorder, text: C.muted };
130
+ case "today":
131
+ return { fill: C.now, stroke: C.now, text: C.bg };
132
+ case "event":
133
+ return { fill: C.event, stroke: C.event, text: C.bg };
134
+ }
135
+ }
136
+ function gradStyle(C, frac) {
137
+ const t = Math.max(0, Math.min(1, frac));
138
+ const f = mix(C.event, C.now, Math.round(t * 100));
139
+ return { fill: f, stroke: f, text: C.bg };
140
+ }
141
+ function aText(svg, x, y, size, fill, weight, anchor, txt, extra) {
142
+ const t = svg.append("text").attr("x", x).attr("y", y).attr("text-anchor", anchor).attr("font-size", size).attr("font-weight", weight).attr("fill", fill).attr("font-family", FONT_FAMILY).attr("font-variant-numeric", "tabular-nums").text(txt);
143
+ if (extra) for (const k in extra) t.attr(k, extra[k]);
144
+ }
145
+ function aRect(svg, x, y, w, h, rx, fill, stroke, sw) {
146
+ const r = svg.append("rect").attr("x", x).attr("y", y).attr("width", w).attr("height", h).attr("rx", rx).attr("fill", fill);
147
+ if (stroke) r.attr("stroke", stroke).attr("stroke-width", sw ?? 1);
148
+ }
149
+ function aLine(svg, x1, y1, x2, y2, stroke, sw, opacity) {
150
+ const l = svg.append("line").attr("x1", x1).attr("y1", y1).attr("x2", x2).attr("y2", y2).attr("stroke", stroke).attr("stroke-width", sw);
151
+ if (opacity != null) l.attr("opacity", opacity);
152
+ }
153
+ var REPEAT_ICON_PATH = "m17 2 4 4-4 4 M3 11v-1a4 4 0 0 1 4-4h14 m7 22-4-4 4-4 M21 13v1a4 4 0 0 1-4 4H3";
154
+ function recurGlyph(svg, cx, cy, r, color) {
155
+ const side = r * 2.4;
156
+ const scale = side / 24;
157
+ const swPx = Math.max(1.5, r * 0.34);
158
+ svg.append("path").attr("d", REPEAT_ICON_PATH).attr(
159
+ "transform",
160
+ `translate(${cx - side / 2} ${cy - side / 2}) scale(${scale})`
161
+ ).attr("data-embed-ignore-bbox", "").attr("fill", "none").attr("stroke", color).attr("stroke-width", swPx / scale).attr("stroke-linecap", "round").attr("stroke-linejoin", "round");
162
+ }
163
+ function haloRect(svg, x, y, w, h, rx, C) {
164
+ aRect(svg, x - 1, y - 1, w + 2, h + 2, rx + 1, "none", C.bg, 2);
165
+ aRect(svg, x - 3, y - 3, w + 6, h + 6, rx + 3, "none", C.event, 2);
166
+ }
167
+ function pickBand(nowMs, resolvedMs, hasTime) {
168
+ const days = dayDelta(nowMs, resolvedMs);
169
+ if (hasTime && days <= 0) return "clock";
170
+ if (days > 365) return "year";
171
+ if (days > 92) return "months";
172
+ if (days > 18) return "monthgrid";
173
+ if (days >= 1) return "weekstrip";
174
+ if (days === 0) return "today";
175
+ return "after";
176
+ }
177
+ function monthSpan(nowMs, resolvedMs) {
178
+ const n = new Date(nowMs);
179
+ const t = new Date(resolvedMs);
180
+ return (t.getFullYear() - n.getFullYear()) * 12 + (t.getMonth() - n.getMonth()) + 1;
181
+ }
182
+ var MONTH_TARGET_PX = 200;
183
+ function monthGridLayout(nowMs, resolvedMs, contentW) {
184
+ const now = new Date(nowMs);
185
+ const ev = new Date(resolvedMs);
186
+ const realSpan = monthSpan(nowMs, resolvedMs);
187
+ const shown = Math.max(realSpan, Math.round(contentW / MONTH_TARGET_PX));
188
+ const padBefore = Math.floor((shown - realSpan) / 2);
189
+ const nowMk = now.getFullYear() * 12 + now.getMonth();
190
+ const evMk = ev.getFullYear() * 12 + ev.getMonth();
191
+ const months = [];
192
+ const dim = [];
193
+ for (let i = 0; i < shown; i++) {
194
+ const d = new Date(now.getFullYear(), now.getMonth() - padBefore + i, 1);
195
+ const mk = d.getFullYear() * 12 + d.getMonth();
196
+ months.push(d);
197
+ dim.push(mk < nowMk || mk > evMk);
198
+ }
199
+ return { months, dim };
200
+ }
201
+ function bandHeightFor(kind, nowMs, resolvedMs, contentW) {
202
+ const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
203
+ switch (kind) {
204
+ case "year": {
205
+ const R = new Date(resolvedMs).getFullYear() - new Date(nowMs).getFullYear() + 1;
206
+ const yearW = 66;
207
+ const gap = 8;
208
+ const cellW = (contentW - yearW - 11 * gap) / 12;
209
+ const cellH = clamp(cellW * 0.6, 28, 44);
210
+ const rowGap = 12;
211
+ return R * cellH + (R - 1) * rowGap;
212
+ }
213
+ case "months": {
214
+ const realSpan = monthSpan(nowMs, resolvedMs);
215
+ const perRow = 6;
216
+ const shown = Math.max(perRow, Math.ceil(realSpan / perRow) * perRow);
217
+ const rows = shown / perRow;
218
+ const cellW = (contentW - 16 * (perRow - 1)) / perRow;
219
+ const cellH = clamp(cellW * 0.5, 58, 84);
220
+ return rows * cellH + (rows - 1) * 16;
221
+ }
222
+ case "monthgrid": {
223
+ const { months } = monthGridLayout(nowMs, resolvedMs, contentW);
224
+ const nM = months.length;
225
+ const mw = (contentW - 20 * (nM - 1)) / nM;
226
+ const cellW = (mw - 20) / 7;
227
+ const cellH = clamp(cellW * 0.8, 15, 22);
228
+ return 26 + 6 * cellH + 8;
229
+ }
230
+ case "weekstrip": {
231
+ const n = Math.max(7, dayDelta(nowMs, resolvedMs) + 1);
232
+ const cw = (contentW - 10 * (n - 1)) / n;
233
+ return clamp(cw * 1.5, 116, 170);
234
+ }
235
+ case "clock":
236
+ return clamp(contentW * 0.26, 150, 205);
237
+ case "today":
238
+ case "after":
239
+ return clamp(contentW * 0.22, 125, 170);
240
+ }
241
+ }
242
+ function vizYear(svg, g, nowMs, resolvedMs, C) {
243
+ const now = new Date(nowMs);
244
+ const ev = new Date(resolvedMs);
245
+ const y0 = now.getFullYear();
246
+ const y1 = ev.getFullYear();
247
+ const years = [];
248
+ for (let y = y0; y <= y1; y++) years.push(y);
249
+ const R = years.length;
250
+ const yearW = 66;
251
+ const gap = 8;
252
+ const rowGap = 12;
253
+ const rowH = (g.height - rowGap * (R - 1)) / R;
254
+ const cellH = rowH;
255
+ const cellW = (g.contentW - yearW - 11 * gap) / 12;
256
+ const nowY = now.getFullYear();
257
+ const nowM = now.getMonth();
258
+ const evY = ev.getFullYear();
259
+ const evM = ev.getMonth();
260
+ const nowOrd = nowY * 12 + nowM;
261
+ const evOrd = evY * 12 + evM;
262
+ years.forEach((yr, r) => {
263
+ const ry = g.top + r * (rowH + rowGap);
264
+ aText(
265
+ svg,
266
+ g.left + yearW - 12,
267
+ ry + rowH / 2,
268
+ Math.min(22, rowH * 0.6),
269
+ C.muted,
270
+ 800,
271
+ "end",
272
+ String(yr),
273
+ { "dominant-baseline": "central" }
274
+ );
275
+ for (let m = 0; m < 12; m++) {
276
+ const before = yr < nowY || yr === nowY && m < nowM;
277
+ const isNow = yr === nowY && m === nowM;
278
+ const isEv = yr === evY && m === evM;
279
+ const between = (yr > nowY || yr === nowY && m > nowM) && (yr < evY || yr === evY && m < evM);
280
+ const anchor = isEv || isNow;
281
+ const st = isEv ? roleStyle(C, "event") : isNow ? roleStyle(C, "today") : between ? gradStyle(C, (yr * 12 + m - nowOrd) / (evOrd - nowOrd)) : roleStyle(C, before ? "past" : "future");
282
+ const x = g.left + yearW + m * (cellW + gap);
283
+ const rx = Math.min(5, Math.min(cellW, cellH) * 0.22);
284
+ aRect(svg, x, ry, cellW, cellH, rx, st.fill, st.stroke, 1);
285
+ if (isEv) haloRect(svg, x, ry, cellW, cellH, rx, C);
286
+ const cx = x + cellW / 2;
287
+ const mFont = Math.min(11, cellW * 0.32);
288
+ if (anchor) {
289
+ aText(
290
+ svg,
291
+ cx,
292
+ ry + cellH * 0.36,
293
+ mFont,
294
+ st.text,
295
+ 700,
296
+ "middle",
297
+ MON_ABBR[m],
298
+ { "dominant-baseline": "central" }
299
+ );
300
+ const dnum = isEv ? ev.getDate() : now.getDate();
301
+ aText(
302
+ svg,
303
+ cx,
304
+ ry + cellH * 0.72,
305
+ Math.min(14, cellW * 0.42),
306
+ st.text,
307
+ 800,
308
+ "middle",
309
+ String(dnum),
310
+ { "dominant-baseline": "central" }
311
+ );
312
+ } else {
313
+ aText(
314
+ svg,
315
+ cx,
316
+ ry + cellH / 2,
317
+ mFont,
318
+ st.text,
319
+ 700,
320
+ "middle",
321
+ MON_ABBR[m],
322
+ { "dominant-baseline": "central" }
323
+ );
324
+ }
325
+ }
326
+ });
327
+ }
328
+ function vizMonths(svg, g, nowMs, resolvedMs, C) {
329
+ const now = new Date(nowMs);
330
+ const ev = new Date(resolvedMs);
331
+ const realSpan = monthSpan(nowMs, resolvedMs);
332
+ const perRow = 6;
333
+ const shown = Math.max(perRow, Math.ceil(realSpan / perRow) * perRow);
334
+ const padBefore = Math.floor((shown - realSpan) / 2);
335
+ const rows = shown / perRow;
336
+ const gap = 16;
337
+ const rowGap = 16;
338
+ const labelH = 27;
339
+ const cellW = (g.contentW - gap * (perRow - 1)) / perRow;
340
+ const cellH = (g.height - rowGap * (rows - 1)) / rows;
341
+ const nowMk = now.getFullYear() * 12 + now.getMonth();
342
+ const evMk = ev.getFullYear() * 12 + ev.getMonth();
343
+ for (let i = 0; i < shown; i++) {
344
+ const d = new Date(now.getFullYear(), now.getMonth() - padBefore + i, 1);
345
+ const mk = d.getFullYear() * 12 + d.getMonth();
346
+ const isNow = mk === nowMk;
347
+ const isEv = mk === evMk;
348
+ const isContext = mk < nowMk || mk > evMk;
349
+ const anchor = isNow || isEv;
350
+ const col = i % perRow;
351
+ const row = Math.floor(i / perRow);
352
+ const x = g.left + col * (cellW + gap);
353
+ const y = g.top + row * (cellH + rowGap);
354
+ const mt = isContext ? svg.append("g").attr("opacity", 0.42) : svg;
355
+ const st = isEv ? roleStyle(C, "event") : isNow ? roleStyle(C, "today") : mk > nowMk && mk < evMk ? gradStyle(C, (mk - nowMk) / (evMk - nowMk)) : roleStyle(C, mk < nowMk ? "past" : "future");
356
+ const rxM = Math.min(12, cellH * 0.14);
357
+ aRect(mt, x, y, cellW, cellH, rxM, st.fill, st.stroke, 1.25);
358
+ if (isEv) haloRect(mt, x, y, cellW, cellH, rxM, C);
359
+ aText(
360
+ mt,
361
+ x + cellW / 2,
362
+ y + 19,
363
+ 16,
364
+ st.text,
365
+ 700,
366
+ "middle",
367
+ MON_ABBR[d.getMonth()]
368
+ );
369
+ aLine(
370
+ mt,
371
+ x,
372
+ y + labelH,
373
+ x + cellW,
374
+ y + labelH,
375
+ st.text,
376
+ 1,
377
+ anchor ? 0.5 : 0.3
378
+ );
379
+ if (anchor) {
380
+ const dnum = isEv ? ev.getDate() : now.getDate();
381
+ const dFont = Math.min(cellH - labelH - 6, cellW * 0.42, 44);
382
+ aText(
383
+ mt,
384
+ x + cellW / 2,
385
+ y + labelH + (cellH - labelH) / 2 + 1,
386
+ dFont,
387
+ st.text,
388
+ 800,
389
+ "middle",
390
+ String(dnum),
391
+ { "dominant-baseline": "central" }
392
+ );
393
+ }
394
+ }
395
+ }
396
+ function vizMonthGrid(svg, g, nowMs, resolvedMs, C) {
397
+ const { months, dim: isContext } = monthGridLayout(
398
+ nowMs,
399
+ resolvedMs,
400
+ g.contentW
401
+ );
402
+ const nM = months.length;
403
+ const gap = 20;
404
+ const mw = (g.contentW - gap * (nM - 1)) / nM;
405
+ const padX = 10;
406
+ const gridLeft = padX;
407
+ const gridW = mw - 2 * padX;
408
+ const cols = 7;
409
+ const cellW = gridW / cols;
410
+ const top = g.top;
411
+ const headH = 26;
412
+ const cellH = (g.height - headH - 8) / 6;
413
+ const gridH = 6 * cellH;
414
+ const weekendShade = mix(C.text, C.bg, 8);
415
+ const todayKey = keyOf(nowMs);
416
+ const evKey = keyOf(resolvedMs);
417
+ months.forEach((m, mi) => {
418
+ const mt = isContext[mi] ? svg.append("g").attr("opacity", 0.42) : svg;
419
+ const mx = g.left + mi * (mw + gap);
420
+ aRect(mt, mx, top, mw, g.height, 14, C.inert, C.inertBorder, 1.25);
421
+ const gy = top + headH;
422
+ for (const wcol of [0, 6])
423
+ aRect(
424
+ mt,
425
+ mx + gridLeft + wcol * cellW,
426
+ gy,
427
+ cellW,
428
+ gridH,
429
+ 6,
430
+ weekendShade
431
+ );
432
+ aText(
433
+ mt,
434
+ mx + mw / 2,
435
+ top + 16,
436
+ 14,
437
+ C.text,
438
+ 700,
439
+ "middle",
440
+ `${MON_ABBR[m.getMonth()]} ${m.getFullYear()}`
441
+ );
442
+ aLine(mt, mx, top + headH - 6, mx + mw, top + headH - 6, C.rule, 1.25);
443
+ const first = new Date(m.getFullYear(), m.getMonth(), 1).getDay();
444
+ const dim = new Date(m.getFullYear(), m.getMonth() + 1, 0).getDate();
445
+ for (let day = 1; day <= dim; day++) {
446
+ const pos = first + day - 1;
447
+ const wk = Math.floor(pos / 7);
448
+ const dk = keyOf(new Date(m.getFullYear(), m.getMonth(), day).getTime());
449
+ const wd = pos % 7;
450
+ const cx = mx + gridLeft + wd * cellW;
451
+ const cy = gy + wk * cellH;
452
+ const isEvent = dk === evKey;
453
+ const isToday = dk === todayKey;
454
+ const between = dk > todayKey && dk < evKey;
455
+ const anchor = isEvent || isToday;
456
+ const dtMs = new Date(m.getFullYear(), m.getMonth(), day).getTime();
457
+ const st = isEvent ? roleStyle(C, "event") : isToday ? roleStyle(C, "today") : between ? gradStyle(C, dayDelta(nowMs, dtMs) / dayDelta(nowMs, resolvedMs)) : roleStyle(C, dk < todayKey ? "past" : "future");
458
+ if (anchor) {
459
+ const s = Math.min(cellW, cellH) * 1.02;
460
+ const sx = cx + (cellW - s) / 2;
461
+ const sy = cy + (cellH - s) / 2;
462
+ aRect(mt, sx, sy, s, s, 7, st.fill, st.stroke, 2);
463
+ if (isEvent) haloRect(mt, sx, sy, s, s, 7, C);
464
+ aText(
465
+ mt,
466
+ cx + cellW / 2,
467
+ cy + cellH / 2,
468
+ Math.min(16, cellH * 0.62),
469
+ st.text,
470
+ 750,
471
+ "middle",
472
+ String(day),
473
+ { "dominant-baseline": "central" }
474
+ );
475
+ } else {
476
+ const s = Math.min(cellW, cellH) * 0.5;
477
+ aRect(
478
+ mt,
479
+ cx + (cellW - s) / 2,
480
+ cy + (cellH - s) / 2,
481
+ s,
482
+ s,
483
+ Math.min(3, s * 0.3),
484
+ st.fill,
485
+ st.stroke,
486
+ 1
487
+ );
488
+ }
489
+ }
490
+ });
491
+ }
492
+ function vizWeekStrip(svg, g, nowMs, resolvedMs, C) {
493
+ const now = new Date(nowMs);
494
+ const nReal = dayDelta(nowMs, resolvedMs) + 1;
495
+ const n = Math.max(7, nReal);
496
+ const padBefore = Math.floor((n - nReal) / 2);
497
+ const gap = n <= 7 ? 14 : n <= 12 ? 10 : 7;
498
+ const cw = (g.contentW - gap * (n - 1)) / n;
499
+ const cellTop = g.top;
500
+ const ch = g.height;
501
+ const showWd = cw >= 30;
502
+ const showTag = cw >= 44;
503
+ const nowOrd = DAY_START(nowMs);
504
+ const evOrd = DAY_START(resolvedMs);
505
+ for (let i = 0; i < n; i++) {
506
+ const dt = new Date(
507
+ now.getFullYear(),
508
+ now.getMonth(),
509
+ now.getDate() + i - padBefore
510
+ );
511
+ const dtOrd = DAY_START(dt.getTime());
512
+ const x = g.left + i * (cw + gap);
513
+ const isToday = dtOrd === nowOrd;
514
+ const isEvent = dtOrd === evOrd;
515
+ const isContext = dtOrd < nowOrd || dtOrd > evOrd;
516
+ const st = isEvent ? roleStyle(C, "event") : isToday ? roleStyle(C, "today") : isContext ? roleStyle(C, dtOrd < nowOrd ? "past" : "future") : gradStyle(C, (dtOrd - nowOrd) / (evOrd - nowOrd));
517
+ const ct = isContext ? svg.append("g").attr("opacity", 0.5) : svg;
518
+ const sw = isToday || isEvent ? 2 : 1.25;
519
+ aRect(ct, x, cellTop, cw, ch, 14, st.fill, st.stroke, sw);
520
+ if (isEvent) haloRect(ct, x, cellTop, cw, ch, 14, C);
521
+ if (showWd) {
522
+ aText(
523
+ ct,
524
+ x + cw / 2,
525
+ cellTop + 18,
526
+ Math.min(12, cw * 0.3),
527
+ st.text,
528
+ 700,
529
+ "middle",
530
+ WD_ABBR[dt.getDay()],
531
+ { "letter-spacing": "0.04em" }
532
+ );
533
+ aLine(
534
+ ct,
535
+ x,
536
+ cellTop + 27,
537
+ x + cw,
538
+ cellTop + 27,
539
+ st.text,
540
+ 1,
541
+ isToday || isEvent ? 0.5 : 0.3
542
+ );
543
+ }
544
+ aText(
545
+ ct,
546
+ x + cw / 2,
547
+ cellTop + (showWd ? ch * 0.6 : ch * 0.54),
548
+ Math.min(44, cw * 0.62),
549
+ st.text,
550
+ 750,
551
+ "middle",
552
+ String(dt.getDate())
553
+ );
554
+ if (showTag && isToday)
555
+ aText(
556
+ ct,
557
+ x + cw / 2,
558
+ cellTop + ch - 13,
559
+ 10,
560
+ st.text,
561
+ 700,
562
+ "middle",
563
+ "TODAY",
564
+ { "letter-spacing": "0.04em" }
565
+ );
566
+ }
567
+ }
568
+ function vizToday(svg, g, resolvedMs, C) {
569
+ const ev = new Date(resolvedMs);
570
+ const cx = g.left + g.contentW / 2;
571
+ const cy = g.top + g.height / 2;
572
+ aLine(svg, g.left, cy, g.left + g.contentW, cy, C.rule, 2);
573
+ aLine(svg, cx, cy, g.left + g.contentW, cy, C.midSoft, 2);
574
+ const rays = 16;
575
+ const r0 = g.height * 0.3;
576
+ const r1 = g.height * 0.44;
577
+ for (let i = 0; i < rays; i++) {
578
+ const a = i / rays * Math.PI * 2;
579
+ svg.append("line").attr("x1", cx + Math.cos(a) * r0).attr("y1", cy + Math.sin(a) * r0).attr("x2", cx + Math.cos(a) * r1).attr("y2", cy + Math.sin(a) * r1).attr("stroke", C.event).attr("stroke-width", 2.5).attr("stroke-linecap", "round").attr("opacity", 0.45);
580
+ }
581
+ const w = Math.min(120, g.contentW * 0.16);
582
+ const h = g.height * 0.52;
583
+ aRect(svg, cx - w / 2, cy - h / 2, w, h, 18, C.event, C.event, 2);
584
+ aText(
585
+ svg,
586
+ cx,
587
+ cy - h * 0.14,
588
+ 14,
589
+ C.bg,
590
+ 700,
591
+ "middle",
592
+ MON_ABBR[ev.getMonth()].toUpperCase(),
593
+ { "letter-spacing": "0.08em" }
594
+ );
595
+ aText(
596
+ svg,
597
+ cx,
598
+ cy + h * 0.22,
599
+ Math.min(48, h * 0.4),
600
+ C.bg,
601
+ 800,
602
+ "middle",
603
+ String(ev.getDate())
604
+ );
605
+ aText(
606
+ svg,
607
+ cx,
608
+ cy + h / 2 + 26,
609
+ 13,
610
+ C.event,
611
+ 700,
612
+ "middle",
613
+ "THE DAY IS HERE",
614
+ { "letter-spacing": "0.12em" }
615
+ );
616
+ }
617
+ function vizAfter(svg, g, nowMs, resolvedMs, C) {
618
+ const ev = new Date(resolvedMs);
619
+ const elapsed = dayDelta(resolvedMs, nowMs);
620
+ const n = elapsed + 1;
621
+ const gap = 10;
622
+ const cw = (g.contentW - gap * (n - 1)) / n;
623
+ const ch = Math.min(cw * 1.1, g.height * 0.62);
624
+ const top = g.top + (g.height - ch) / 2 - 8;
625
+ for (let i = 0; i < n; i++) {
626
+ const dt = new Date(ev);
627
+ dt.setDate(ev.getDate() + i);
628
+ const x = g.left + i * (cw + gap);
629
+ const isEvent = i === 0;
630
+ const isToday = i === n - 1;
631
+ const st = roleStyle(C, isEvent ? "event" : "past");
632
+ const fill = st.fill;
633
+ const stroke = st.stroke;
634
+ const tcol = st.text;
635
+ const sw = isEvent ? 2.5 : 1;
636
+ aRect(svg, x, top, cw, ch, 12, fill, stroke, sw);
637
+ if (isEvent) haloRect(svg, x, top, cw, ch, 12, C);
638
+ aText(
639
+ svg,
640
+ x + cw / 2,
641
+ top + ch * 0.46,
642
+ Math.min(34, cw * 0.34),
643
+ tcol,
644
+ isEvent ? 750 : 500,
645
+ "middle",
646
+ String(dt.getDate())
647
+ );
648
+ aText(
649
+ svg,
650
+ x + cw / 2,
651
+ top + ch * 0.72,
652
+ 11,
653
+ tcol,
654
+ 600,
655
+ "middle",
656
+ MON_ABBR[dt.getMonth()]
657
+ );
658
+ if (isEvent)
659
+ aText(
660
+ svg,
661
+ x + cw / 2,
662
+ top - 8,
663
+ 11,
664
+ C.event,
665
+ 700,
666
+ "middle",
667
+ "IT HAPPENED",
668
+ { "letter-spacing": "0.05em" }
669
+ );
670
+ if (isToday)
671
+ aText(svg, x + cw / 2, top - 8, 11, C.now, 700, "middle", "TODAY", {
672
+ "letter-spacing": "0.05em"
673
+ });
674
+ }
675
+ aText(
676
+ svg,
677
+ g.left,
678
+ top + ch + 28,
679
+ 14,
680
+ C.muted,
681
+ 400,
682
+ "start",
683
+ `${elapsed} day${elapsed === 1 ? "" : "s"} ago \u2014 the day has passed`
684
+ );
685
+ }
686
+ function polar(cx, cy, r, deg) {
687
+ const a = (deg - 90) * Math.PI / 180;
688
+ return [cx + r * Math.cos(a), cy + r * Math.sin(a)];
689
+ }
690
+ function arcPath(cx, cy, r, frac) {
691
+ frac = Math.max(0, Math.min(0.9999, frac));
692
+ const [x0, y0] = polar(cx, cy, r, 0);
693
+ const [x1, y1] = polar(cx, cy, r, frac * 360);
694
+ return `M ${x0} ${y0} A ${r} ${r} 0 ${frac > 0.5 ? 1 : 0} 1 ${x1} ${y1}`;
695
+ }
696
+ function vizClock(svg, g, nowMs, targetMs, C) {
697
+ const ahead = targetMs >= nowMs;
698
+ const rem = Math.abs(targetMs - nowMs);
699
+ const h = Math.floor(rem / 36e5);
700
+ const m = Math.floor(rem % 36e5 / 6e4);
701
+ const s = Math.floor(rem % 6e4 / 1e3);
702
+ const R = Math.min(g.contentW / 3 * 0.3, g.height * 0.34);
703
+ const cy = g.top + g.height * 0.42;
704
+ const sw = Math.max(8, R * 0.17);
705
+ const gauges = [
706
+ { frac: h % 24 / 24, val: h, label: "HOURS", col: C.event, key: "h" },
707
+ { frac: m / 60, val: m, label: "MINUTES", col: C.event, key: "m" },
708
+ { frac: s / 60, val: s, label: "SECONDS", col: C.event, key: "s" }
709
+ ];
710
+ gauges.forEach((gg, i) => {
711
+ const cx = g.left + g.contentW * (i + 0.5) / 3;
712
+ svg.append("circle").attr("cx", cx).attr("cy", cy).attr("r", R).attr("fill", "none").attr("stroke", C.inert).attr("stroke-width", sw);
713
+ const arc = svg.append("path").attr("d", gg.frac > 1e-3 ? arcPath(cx, cy, R, gg.frac) : "").attr("fill", "none").attr("stroke", gg.col).attr("stroke-width", sw).attr("stroke-linecap", "round").attr("data-dgmo-gauge-arc", gg.key).attr("data-cx", cx).attr("data-cy", cy).attr("data-r", R);
714
+ void arc;
715
+ aText(
716
+ svg,
717
+ cx,
718
+ cy + R * 0.22,
719
+ R * 0.62,
720
+ C.text,
721
+ 800,
722
+ "middle",
723
+ pad2(gg.val),
724
+ {
725
+ "data-dgmo-gauge-val": gg.key
726
+ }
727
+ );
728
+ aText(svg, cx, cy + R + 22, 12, C.muted, 700, "middle", gg.label, {
729
+ "letter-spacing": "0.1em"
730
+ });
731
+ });
732
+ const cxMid = g.left + g.contentW * 0.5;
733
+ aText(
734
+ svg,
735
+ cxMid,
736
+ cy - R - 14,
737
+ 13,
738
+ ahead ? C.event : C.muted,
739
+ 700,
740
+ "middle",
741
+ ahead ? "TO GO" : "AGO",
742
+ {
743
+ "letter-spacing": "0.12em",
744
+ "data-dgmo-gauge-caption": ""
745
+ }
746
+ );
747
+ }
748
+ function pad2(n) {
749
+ return n < 10 ? "0" + n : String(n);
750
+ }
751
+ function drawBand(svg, kind, g, nowMs, resolvedMs, C) {
752
+ switch (kind) {
753
+ case "year":
754
+ return vizYear(svg, g, nowMs, resolvedMs, C);
755
+ case "months":
756
+ return vizMonths(svg, g, nowMs, resolvedMs, C);
757
+ case "monthgrid":
758
+ return vizMonthGrid(svg, g, nowMs, resolvedMs, C);
759
+ case "weekstrip":
760
+ return vizWeekStrip(svg, g, nowMs, resolvedMs, C);
761
+ case "today":
762
+ return vizToday(svg, g, resolvedMs, C);
763
+ case "after":
764
+ return vizAfter(svg, g, nowMs, resolvedMs, C);
765
+ case "clock":
766
+ return vizClock(svg, g, nowMs, resolvedMs, C);
767
+ }
768
+ }
769
+ function renderCountdown(container, parsed, palette, isDark, exportDims) {
770
+ d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
771
+ const width = exportDims?.width ?? container.clientWidth;
772
+ const height = exportDims?.height ?? container.clientHeight;
773
+ if (width <= 0 || height <= 0) return;
774
+ const now = Date.now();
775
+ const resolved = parsed.resolvedMs;
776
+ const label = parsed.sinceLabel ?? parsed.title ?? "";
777
+ const ordinal = parsed.since !== null && resolved !== null ? ordinalFor(resolved, parsed.since) : null;
778
+ const expiredFrozen = !parsed.rule && parsed.expired !== null && resolved !== null && resolved - now <= 0;
779
+ const bandKind = resolved === null || parsed.noVisual || expiredFrozen ? null : pickBand(now, resolved, parsed.hasTime);
780
+ let hero = bakedHero(parsed, now);
781
+ if (bandKind === "clock" && resolved !== null) {
782
+ const clock = formatCount(Math.abs(resolved - now), {
783
+ units: "clock",
784
+ round: parsed.round,
785
+ fields: parsed.fields
786
+ });
787
+ hero = resolved - now < 0 ? `${clock} ago` : clock;
788
+ }
789
+ const seriesAccent = getSeriesColors(palette)[0];
790
+ const defaultAccent = resolveColor("red", palette) ?? seriesAccent;
791
+ const manualColor = parsed.color && resolveColor(parsed.color, palette) || parsed.color || null;
792
+ const accent = manualColor ?? defaultAccent;
793
+ const muted = mix(palette.text, themeBaseBg(palette, isDark), 55);
794
+ const banked = resolveColor("green", palette) ?? muted;
795
+ const faint = mix(palette.text, themeBaseBg(palette, isDark), 72);
796
+ const svg = d3Selection.select(container).append("svg").attr("width", width).attr("preserveAspectRatio", "xMidYMid meet").attr("xmlns", "http://www.w3.org/2000/svg").attr("role", "img").attr("aria-label", `${parsed.title ?? "Countdown"}: ${hero}`).style("font-family", FONT_FAMILY);
797
+ const bgRect = svg.append("rect").attr("width", width).attr("fill", palette.bg);
798
+ const estWidth = (t, fs) => t.length * fs * 0.58;
799
+ const fitFont = (t, base, maxW, min) => {
800
+ const w = estWidth(t, base);
801
+ return w <= maxW ? base : Math.max(min, Math.floor(base * maxW / w));
802
+ };
803
+ const dispLen = (s) => parseInlineMarkdown(s).reduce((n, sp) => n + sp.text.length, 0);
804
+ const padX = Math.max(30, Math.round(width * 0.045));
805
+ const padY = Math.max(26, Math.round(width * 0.03));
806
+ const contentW = width - 2 * padX;
807
+ const leftX = padX;
808
+ const remainingNow = resolved === null ? 0 : Math.max(0, resolved - now);
809
+ const dayCount = Math.ceil(remainingNow / DAY_MS);
810
+ const heroSizeStr = parsed.units === "full" ? `${dayCount > 0 ? `${dayCount}d ` : ""}00:00:00` : parsed.units === "clock" ? formatCount(remainingNow, {
811
+ units: "clock",
812
+ round: parsed.round,
813
+ fields: parsed.fields
814
+ }) : hero;
815
+ const heroMaxW = contentW * 0.44;
816
+ const heroFont = fitFont(heroSizeStr, 96, heroMaxW, 26);
817
+ const heroW = Math.min(heroMaxW, estWidth(heroSizeStr, heroFont));
818
+ const gapMid = Math.max(28, Math.round(width * 0.03));
819
+ const leftW = Math.max(contentW * 0.42, contentW - heroW - gapMid);
820
+ const titleBase = 40;
821
+ let titleFont = 0;
822
+ let titleLines = [];
823
+ if (parsed.title) {
824
+ const longestWord = parsed.title.split(/\s+/).reduce((a, b) => b.length > a.length ? b : a, "");
825
+ titleFont = fitFont(longestWord, titleBase, leftW, 22);
826
+ const cpl = Math.max(6, Math.floor(leftW / (titleFont * 0.58)));
827
+ titleLines = wrapDescriptionLines([parsed.title], cpl).map((l) => l.text);
828
+ }
829
+ const eyebrowText = ordinal !== null ? applyOrdinalTemplate(parsed.sinceLabel ?? `Nth ${label}`, ordinal) : null;
830
+ const eyebrowFont = 16;
831
+ const footerText = resolved !== null ? formatFooter(resolved, parsed.hasTime) : null;
832
+ const footerFont = 17;
833
+ const noteFont = 19;
834
+ const asofFont = 13;
835
+ const noteCharsPerLine = Math.max(8, Math.floor(leftW / (noteFont * 0.5)));
836
+ const noteSource = parsed.note ? parsed.note.split("\n").map((l) => l.replace(/^[-*]\s+/, "\u2022 ")) : [];
837
+ const noteLines = noteSource.length ? wrapDescriptionLines(noteSource, noteCharsPerLine, dispLen) : [];
838
+ const bulletPx = noteFont * 0.95;
839
+ let y = padY;
840
+ const drawText = (text, yTop, fs, fill, weight) => svg.append("text").attr("x", leftX).attr("y", yTop + fs).attr("text-anchor", "start").attr("dominant-baseline", "alphabetic").attr("fill", fill).attr("font-family", FONT_FAMILY).attr("font-size", fs).attr("font-weight", weight).text(text);
841
+ if (parsed.title) {
842
+ titleLines.forEach((tl, i) => {
843
+ const t = drawText(tl, y, titleFont, palette.text, 700);
844
+ if (i === 0) t.attr("data-line-number", parsed.titleLineNumber);
845
+ y += i < titleLines.length - 1 ? Math.round(titleFont * 1.12) : titleFont;
846
+ });
847
+ y += Math.round(titleFont * 0.45);
848
+ const heroLeft = width - padX - heroW;
849
+ const ruleRight = Math.max(leftX + 40, heroLeft - 20);
850
+ svg.append("line").attr("x1", leftX).attr("x2", ruleRight).attr("y1", y).attr("y2", y).attr("stroke", mix(palette.text, palette.bg, 14)).attr("stroke-width", 1.25);
851
+ y += Math.round(titleFont * 0.5);
852
+ }
853
+ if (eyebrowText) {
854
+ drawText(eyebrowText, y, eyebrowFont, banked, 700).attr(
855
+ "data-dgmo-countdown-eyebrow",
856
+ ""
857
+ );
858
+ y += eyebrowFont + 8;
859
+ }
860
+ if (footerText) {
861
+ const fx = parsed.rule ? leftX + 22 : leftX;
862
+ if (parsed.rule)
863
+ recurGlyph(svg, leftX + 8, y + footerFont * 0.52, 6.5, muted);
864
+ svg.append("text").attr("x", fx).attr("y", y + footerFont).attr("text-anchor", "start").attr("dominant-baseline", "alphabetic").attr("fill", muted).attr("font-family", FONT_FAMILY).attr("font-size", footerFont).attr("font-weight", 500).attr("data-dgmo-countdown-footer", "").text(footerText);
865
+ y += footerFont + 8;
866
+ }
867
+ if (noteLines.length) {
868
+ y += 4;
869
+ for (const nl of noteLines) {
870
+ const bx = nl.kind === "plain" ? leftX : leftX + bulletPx;
871
+ if (nl.kind === "bullet-first") {
872
+ svg.append("text").attr("x", leftX).attr("y", y + noteFont).attr("fill", muted).attr("font-family", FONT_FAMILY).attr("font-size", noteFont).text("\u2022");
873
+ }
874
+ const t = svg.append("text").attr("x", bx).attr("y", y + noteFont).attr("fill", palette.text).attr("font-family", FONT_FAMILY).attr("font-size", noteFont).attr("font-weight", 400);
875
+ renderInlineText(
876
+ t,
877
+ nl.text,
878
+ palette,
879
+ noteFont
880
+ );
881
+ y += Math.round(noteFont * 1.35);
882
+ }
883
+ y += 2;
884
+ }
885
+ drawText(`as of ${formatDateShort(now)}`, y, asofFont, faint, 400).attr(
886
+ "data-dgmo-countdown-asof",
887
+ ""
888
+ );
889
+ y += asofFont;
890
+ const leftBottom = y;
891
+ const detailFont = 18;
892
+ const remainingSub = resolved !== null ? resolved - now : 0;
893
+ const subText = resolved === null || remainingSub <= 0 ? null : parsed.units === "words" ? formatWordsDetail(remainingSub) : parsed.units === "human" && bandKind !== "clock" ? parsed.hasTime ? formatCount(remainingSub, {
894
+ units: "clock",
895
+ round: parsed.round,
896
+ fields: parsed.fields
897
+ }) : formatHuman(DAY_START(now), DAY_START(resolved)).sub || null : null;
898
+ const heroCapTop = padY + 0.28 * titleFont;
899
+ const heroBaseline = heroCapTop + 0.72 * heroFont;
900
+ const heroBlockBottom = heroBaseline + (subText ? heroFont * 0.28 + detailFont : 0);
901
+ const contentBottom = Math.max(leftBottom, heroBlockBottom);
902
+ const hasBand = bandKind !== null;
903
+ const topSection = contentBottom;
904
+ const bandTop = topSection + 26;
905
+ const BAND_H = hasBand && resolved !== null ? Math.round(bandHeightFor(bandKind, now, resolved, width - 2 * padX)) : 0;
906
+ const bannerH = hasBand ? bandTop + BAND_H + padY : Math.max(
907
+ contentBottom + padY,
908
+ heroFont * 1.25 + 2 * padY,
909
+ Math.round(width * 0.28)
910
+ );
911
+ svg.attr("height", bannerH).attr("viewBox", `0 0 ${width} ${bannerH}`);
912
+ bgRect.attr("height", bannerH);
913
+ if (hasBand && resolved !== null) {
914
+ drawBand(
915
+ svg,
916
+ bandKind,
917
+ { left: leftX, top: bandTop, contentW: width - 2 * padX, height: BAND_H },
918
+ now,
919
+ resolved,
920
+ bandColors(palette, accent, muted, faint)
921
+ );
922
+ }
923
+ const value = svg.append("text").attr("class", "countdown-value").attr("x", width - padX).attr("y", heroBaseline).attr("text-anchor", "end").attr("dominant-baseline", "alphabetic").attr("fill", accent).attr("font-family", FONT_FAMILY).attr("font-size", heroFont).attr("font-weight", 800).attr("data-dgmo-countdown-units", parsed.units).attr("data-dgmo-countdown-round", parsed.round).attr("data-dgmo-countdown-fields", parsed.fields.join(",")).attr("data-dgmo-countdown-expired", parsed.expired).attr("aria-label", `${parsed.title ?? "Countdown"}: ${hero}`).text(hero);
924
+ if (parsed.rule && resolved !== null) {
925
+ value.attr("data-dgmo-countdown", new Date(resolved).toISOString());
926
+ } else if (parsed.target) {
927
+ value.attr("data-dgmo-countdown", parsed.target);
928
+ }
929
+ if (parsed.title) value.attr("data-dgmo-countdown-title", parsed.title);
930
+ if (parsed.onDay) value.attr("data-dgmo-countdown-onday", parsed.onDay);
931
+ if (parsed.hasTime) value.attr("data-dgmo-countdown-hastime", "1");
932
+ if (parsed.since !== null) {
933
+ value.attr("data-dgmo-countdown-since", parsed.since);
934
+ value.attr(
935
+ "data-dgmo-countdown-since-label",
936
+ parsed.sinceLabel ?? `Nth ${parsed.title ?? ""}`
937
+ );
938
+ }
939
+ stampRecur(value, parsed);
940
+ if (subText) {
941
+ svg.append("text").attr("x", width - padX).attr("y", heroBaseline + heroFont * 0.28 + detailFont).attr("text-anchor", "end").attr("dominant-baseline", "alphabetic").attr("fill", muted).attr("font-family", FONT_FAMILY).attr("font-size", detailFont).attr("font-weight", 500).attr("data-dgmo-countdown-detail", "").text(subText);
942
+ }
943
+ }
944
+ function renderCountdownForExport(container, parsed, palette, isDark, exportDims) {
945
+ renderCountdown(container, parsed, palette, isDark, exportDims);
946
+ }
947
+
948
+ export {
949
+ renderCountdown,
950
+ renderCountdownForExport
951
+ };
952
+ //# sourceMappingURL=chunk-Y4RNXEAO.js.map