@bendyline/squisq 2.11.11 → 2.11.13

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
@@ -20,7 +20,7 @@ npm install @bendyline/squisq
20
20
  | **schemas** | Type definitions — `Doc`, `BlockTemplate`, `Viewport`, `Theme`, `LayoutStrategy` |
21
21
  | **doc** | Template registry, 26 block templates, animation/theme utilities |
22
22
  | **markdown** | Markdown parsing, stringifying, AST types (`MarkdownDocument`), tree utilities |
23
- | **spatial** | Haversine distance, Geohash encode/decode |
23
+ | **spatial** | Haversine distance and bearing, great-circle paths, Geohash encode/decode |
24
24
  | **storage** | `StorageAdapter` interface, Memory + LocalStorage + LocalForage adapters |
25
25
 
26
26
  ## New in this release
@@ -92,10 +92,19 @@ the original fence byte-for-byte for markdown round-trips.
92
92
  ### Spatial Utilities
93
93
 
94
94
  ```ts
95
- import { encodeGeohash, haversineDistance } from '@bendyline/squisq/spatial';
95
+ import {
96
+ destinationPoint,
97
+ encodeGeohash,
98
+ haversineDistance,
99
+ sampleGreatCircle,
100
+ } from '@bendyline/squisq/spatial';
96
101
 
97
102
  const distanceKm = haversineDistance({ lat: 47.6, lng: -122.3 }, { lat: 37.7, lng: -122.4 });
98
103
  const hash = encodeGeohash(47.6, -122.3, 7);
104
+
105
+ // Points along the shortest route, and a position projected 5 km due east.
106
+ const route = sampleGreatCircle({ lat: 47.6, lng: -122.3 }, { lat: 51.5, lng: -0.1 }, 20);
107
+ const ahead = destinationPoint({ lat: 47.6, lng: -122.3 }, 90, 5_000);
99
108
  ```
100
109
 
101
110
  ### Materialize a Block
@@ -329,20 +329,43 @@ function scopeContainer(parent, prefix) {
329
329
  // src/storage/MediaProviderFromContainer.ts
330
330
  function createMediaProviderFromContainer(container) {
331
331
  const blobUrlCache = /* @__PURE__ */ new Map();
332
- return {
333
- async resolveUrl(relativePath) {
334
- const cached = blobUrlCache.get(relativePath);
335
- if (cached) return cached;
332
+ const pending = /* @__PURE__ */ new Map();
333
+ const generations = /* @__PURE__ */ new Map();
334
+ let disposed = false;
335
+ function invalidate(path) {
336
+ generations.set(path, (generations.get(path) ?? 0) + 1);
337
+ pending.delete(path);
338
+ const cached = blobUrlCache.get(path);
339
+ if (cached) URL.revokeObjectURL(cached);
340
+ blobUrlCache.delete(path);
341
+ }
342
+ function resolveUrl(relativePath) {
343
+ if (disposed) return Promise.resolve(relativePath);
344
+ const cached = blobUrlCache.get(relativePath);
345
+ if (cached) return Promise.resolve(cached);
346
+ const existing = pending.get(relativePath);
347
+ if (existing) return existing;
348
+ const generation = generations.get(relativePath) ?? 0;
349
+ const work = (async () => {
336
350
  const data = await container.readFile(relativePath);
351
+ const entries = data ? await container.listFiles() : [];
352
+ if (disposed) return relativePath;
353
+ if ((generations.get(relativePath) ?? 0) !== generation) return resolveUrl(relativePath);
337
354
  if (!data) return relativePath;
338
- const entries = await container.listFiles();
339
- const entry = entries.find((e) => e.path === relativePath);
340
- const mimeType = entry?.mimeType ?? "application/octet-stream";
341
- const blob = new Blob([data], { type: mimeType });
342
- const url = URL.createObjectURL(blob);
355
+ const mimeType = entries.find((e) => e.path === relativePath)?.mimeType;
356
+ const url = URL.createObjectURL(
357
+ new Blob([data], { type: mimeType ?? "application/octet-stream" })
358
+ );
343
359
  blobUrlCache.set(relativePath, url);
344
360
  return url;
345
- },
361
+ })().finally(() => {
362
+ if (pending.get(relativePath) === work) pending.delete(relativePath);
363
+ });
364
+ pending.set(relativePath, work);
365
+ return work;
366
+ }
367
+ return {
368
+ resolveUrl,
346
369
  async listMedia() {
347
370
  const entries = await container.listFiles();
348
371
  return entries.filter((e) => !e.path.toLowerCase().endsWith(".md")).map((e) => ({
@@ -352,11 +375,7 @@ function createMediaProviderFromContainer(container) {
352
375
  }));
353
376
  },
354
377
  async addMedia(name, data, mimeType) {
355
- const cached = blobUrlCache.get(name);
356
- if (cached) {
357
- URL.revokeObjectURL(cached);
358
- blobUrlCache.delete(name);
359
- }
378
+ invalidate(name);
360
379
  let buffer;
361
380
  if (data instanceof Blob) {
362
381
  buffer = await data.arrayBuffer();
@@ -364,17 +383,17 @@ function createMediaProviderFromContainer(container) {
364
383
  buffer = data;
365
384
  }
366
385
  await container.writeFile(name, buffer, mimeType);
386
+ invalidate(name);
367
387
  return name;
368
388
  },
369
389
  async removeMedia(relativePath) {
370
- const cached = blobUrlCache.get(relativePath);
371
- if (cached) {
372
- URL.revokeObjectURL(cached);
373
- blobUrlCache.delete(relativePath);
374
- }
390
+ invalidate(relativePath);
375
391
  await container.removeFile(relativePath);
392
+ invalidate(relativePath);
376
393
  },
377
394
  dispose() {
395
+ disposed = true;
396
+ pending.clear();
378
397
  for (const url of blobUrlCache.values()) {
379
398
  URL.revokeObjectURL(url);
380
399
  }
@@ -109,6 +109,53 @@ function geohashOverlapsBounds(hash, bounds) {
109
109
  return !(hashNorth < bounds.south || hashSouth > bounds.north || hashEast < bounds.west || hashWest > bounds.east);
110
110
  }
111
111
 
112
+ // src/spatial/GreatCircle.ts
113
+ var DEG2RAD = Math.PI / 180;
114
+ var RAD2DEG = 180 / Math.PI;
115
+ var EARTH_RADIUS_METERS = 6371e3;
116
+ function interpolateGreatCircle(from, to, fraction) {
117
+ const lat1 = from.lat * DEG2RAD;
118
+ const lon1 = from.lng * DEG2RAD;
119
+ const lat2 = to.lat * DEG2RAD;
120
+ const lon2 = to.lng * DEG2RAD;
121
+ const dLat = lat2 - lat1;
122
+ const dLon = lon2 - lon1;
123
+ const a = Math.sin(dLat / 2) ** 2 + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2) ** 2;
124
+ const delta = 2 * Math.asin(Math.min(1, Math.sqrt(a)));
125
+ if (delta === 0) return { lat: from.lat, lng: from.lng };
126
+ const A = Math.sin((1 - fraction) * delta) / Math.sin(delta);
127
+ const B = Math.sin(fraction * delta) / Math.sin(delta);
128
+ const x = A * Math.cos(lat1) * Math.cos(lon1) + B * Math.cos(lat2) * Math.cos(lon2);
129
+ const y = A * Math.cos(lat1) * Math.sin(lon1) + B * Math.cos(lat2) * Math.sin(lon2);
130
+ const z = A * Math.sin(lat1) + B * Math.sin(lat2);
131
+ const lat = Math.atan2(z, Math.sqrt(x * x + y * y));
132
+ const lon = Math.atan2(y, x);
133
+ return { lat: lat * RAD2DEG, lng: lon * RAD2DEG };
134
+ }
135
+ function sampleGreatCircle(from, to, samples) {
136
+ const n = Math.max(2, Math.floor(samples));
137
+ const points = [];
138
+ for (let i = 0; i < n; i++) {
139
+ points.push(interpolateGreatCircle(from, to, i / (n - 1)));
140
+ }
141
+ return points;
142
+ }
143
+ function destinationPoint(from, bearingDeg, distanceMeters) {
144
+ const delta = distanceMeters / EARTH_RADIUS_METERS;
145
+ const theta = bearingDeg * DEG2RAD;
146
+ const lat1 = from.lat * DEG2RAD;
147
+ const lon1 = from.lng * DEG2RAD;
148
+ const lat2 = Math.asin(
149
+ Math.sin(lat1) * Math.cos(delta) + Math.cos(lat1) * Math.sin(delta) * Math.cos(theta)
150
+ );
151
+ const lon2 = lon1 + Math.atan2(
152
+ Math.sin(theta) * Math.sin(delta) * Math.cos(lat1),
153
+ Math.cos(delta) - Math.sin(lat1) * Math.sin(lat2)
154
+ );
155
+ const lng = ((lon2 * RAD2DEG + 180) % 360 + 360) % 360 - 180;
156
+ return { lat: lat2 * RAD2DEG, lng };
157
+ }
158
+
112
159
  export {
113
160
  haversineDistance,
114
161
  calculateBearing,
@@ -119,5 +166,8 @@ export {
119
166
  getGeohashPrefix,
120
167
  geohashToHierarchicalPath,
121
168
  getGeohashPath,
122
- geohashOverlapsBounds
169
+ geohashOverlapsBounds,
170
+ interpolateGreatCircle,
171
+ sampleGreatCircle,
172
+ destinationPoint
123
173
  };
@@ -6,7 +6,9 @@ import {
6
6
  import {
7
7
  formatFrontmatterYaml,
8
8
  getChildren,
9
- splitFrontmatterBlock
9
+ splitFrontmatterBlock,
10
+ stringifyHtmlNodes,
11
+ walkMarkdownTree
10
12
  } from "./chunk-X7BNDUNI.js";
11
13
 
12
14
  // src/markdown/stringify.ts
@@ -964,6 +966,175 @@ function detectMarkdownWrapState(source) {
964
966
  };
965
967
  }
966
968
 
969
+ // src/markdown/contentReferences.ts
970
+ function rewriteMarkdownReferences(source, options = {}) {
971
+ const { frontmatter, body } = splitFrontmatterBlock(source);
972
+ const document = parseMarkdown(body, { frontmatter: false });
973
+ const html = (nodes, insidePicture = false) => {
974
+ let changed = false;
975
+ const output2 = nodes.flatMap((node) => {
976
+ if (node.type !== "htmlElement") return [node];
977
+ if (options.images === "omit" && node.tagName === "picture") {
978
+ changed = true;
979
+ return html(node.children, true).nodes;
980
+ }
981
+ if (options.images === "omit" && (node.tagName === "img" || node.tagName === "source" && insidePicture)) {
982
+ changed = true;
983
+ const alt = node.tagName === "img" ? node.attributes.alt : void 0;
984
+ return alt ? [
985
+ {
986
+ type: "htmlText",
987
+ value: alt.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
988
+ }
989
+ ] : [];
990
+ }
991
+ const child = html(node.children);
992
+ changed || (changed = child.changed);
993
+ const copy = { ...node, attributes: { ...node.attributes }, children: child.nodes };
994
+ const key = node.tagName === "a" ? "href" : node.tagName === "img" ? "src" : void 0;
995
+ if (key && node.attributes[key]) {
996
+ const value = options.rewriteUrl?.(node.attributes[key], key === "href" ? "link" : "image");
997
+ if (value !== void 0 && value !== node.attributes[key]) {
998
+ copy.attributes[key] = value;
999
+ changed = true;
1000
+ }
1001
+ }
1002
+ return [copy];
1003
+ });
1004
+ return { nodes: output2, changed };
1005
+ };
1006
+ const blocks = /* @__PURE__ */ new Set([
1007
+ "heading",
1008
+ "paragraph",
1009
+ "blockquote",
1010
+ "code",
1011
+ "thematicBreak",
1012
+ "list",
1013
+ "table",
1014
+ "htmlBlock",
1015
+ "mathBlock",
1016
+ "definition",
1017
+ "footnoteDefinition",
1018
+ "containerDirective",
1019
+ "leafDirective",
1020
+ "definitionList"
1021
+ ]);
1022
+ const structural = /* @__PURE__ */ new Set([
1023
+ "listItem",
1024
+ "tableRow",
1025
+ "tableCell",
1026
+ "definitionTerm",
1027
+ "definitionDescription"
1028
+ ]);
1029
+ const render = (node) => {
1030
+ if (node.type === "document") return stringifyMarkdown(node).trimEnd();
1031
+ return stringifyMarkdown({
1032
+ type: "document",
1033
+ children: blocks.has(node.type) ? [node] : [{ type: "paragraph", children: [node] }]
1034
+ }).trimEnd();
1035
+ };
1036
+ const visit = (node) => {
1037
+ const children = getChildren(node).map(visit);
1038
+ const childChanged = children.some((child) => child.changed);
1039
+ let clone = childChanged && "children" in node ? { ...node, children: children.map((child) => child.node) } : node;
1040
+ let ownChange = false;
1041
+ if ((node.type === "image" || node.type === "imageReference") && options.images === "omit") {
1042
+ clone = { type: "text", value: node.alt ?? "" };
1043
+ ownChange = true;
1044
+ } else if (node.type === "htmlBlock" || node.type === "htmlInline") {
1045
+ const transformed = html(node.htmlChildren);
1046
+ if (transformed.changed) {
1047
+ clone = {
1048
+ ...node,
1049
+ htmlChildren: transformed.nodes,
1050
+ rawHtml: stringifyHtmlNodes(transformed.nodes)
1051
+ };
1052
+ ownChange = true;
1053
+ }
1054
+ } else if (clone.type === "link" || clone.type === "image" || clone.type === "definition") {
1055
+ const url = options.rewriteUrl?.(clone.url, clone.type === "image" ? "image" : "link");
1056
+ if (url !== void 0 && url !== clone.url) {
1057
+ clone = { ...clone, url };
1058
+ ownChange = true;
1059
+ }
1060
+ }
1061
+ const changed = ownChange || childChanged;
1062
+ const pending = ownChange || children.some((child) => child.pending);
1063
+ const start = node.position?.start.offset;
1064
+ const end = node.position?.end.offset;
1065
+ if (pending && start !== void 0 && end !== void 0 && !structural.has(node.type)) {
1066
+ return { node: clone, changed, pending: false, edits: [{ start, end, text: render(clone) }] };
1067
+ }
1068
+ return { node: clone, changed, pending, edits: children.flatMap((child) => child.edits) };
1069
+ };
1070
+ const result = visit(document);
1071
+ if (result.pending) throw new Error("Reference edit has no positioned ancestor");
1072
+ const edits = result.edits;
1073
+ let output = body;
1074
+ let boundary = body.length;
1075
+ for (const e of edits.sort((a, b) => b.start - a.start)) {
1076
+ if (e.end > boundary) throw new Error("Overlapping reference edits");
1077
+ output = output.slice(0, e.start) + e.text + output.slice(e.end);
1078
+ boundary = e.start;
1079
+ }
1080
+ return (frontmatter ?? "") + output;
1081
+ }
1082
+
1083
+ // src/markdown/docfxMarkdown.ts
1084
+ function normalizeDocfxMarkdown(source, options = {}) {
1085
+ const { frontmatter, body } = splitFrontmatterBlock(source);
1086
+ const protectedSpans = [];
1087
+ for (const comment of body.matchAll(/<!--[\s\S]*?-->/g))
1088
+ protectedSpans.push([comment.index, comment.index + comment[0].length]);
1089
+ walkMarkdownTree(parseMarkdown(body, { frontmatter: false }), (node) => {
1090
+ if ((node.type === "code" || node.type === "inlineCode") && node.position?.start.offset !== void 0 && node.position.end.offset !== void 0)
1091
+ protectedSpans.push([node.position.start.offset, node.position.end.offset]);
1092
+ });
1093
+ const escape = (text) => stringifyMarkdown({
1094
+ type: "document",
1095
+ children: [{ type: "paragraph", children: [{ type: "text", value: text }] }]
1096
+ }).trimEnd();
1097
+ const output = body.replace(
1098
+ /(^[^\S\n]*(?:>\s*)?):::\s*([\w-]+)([^\n]*?)(?=\r?$)|(^[^\S\n]*>\s*)\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION|div)\b([^\]]*)\]|:::\s*(image)\b([^\n]*?):::/gim,
1099
+ (match, prefix, name, raw, quote, admonition, _extra, inlineName, inlineRaw, offset) => {
1100
+ if (protectedSpans.some(([start, end]) => offset >= start && offset < end)) return match;
1101
+ if (admonition)
1102
+ return admonition.toLowerCase() === "div" ? quote ?? "" : `${quote}**${admonition[0] + admonition.slice(1).toLowerCase()}:**`;
1103
+ name ?? (name = inlineName);
1104
+ raw ?? (raw = inlineRaw);
1105
+ const attributes = /* @__PURE__ */ new Map();
1106
+ for (const attr of (raw ?? "").matchAll(/([\w-]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g))
1107
+ attributes.set(attr[1], attr[2] ?? attr[3]);
1108
+ switch (name?.toLowerCase()) {
1109
+ case "image": {
1110
+ const alt = attributes.get("alt-text") ?? "";
1111
+ const url = attributes.get("source");
1112
+ if (options.images === "omit") return `${prefix ?? ""}${escape(alt)}`;
1113
+ if (!url) throw new Error("DocFX image has no source");
1114
+ const image = stringifyMarkdown({
1115
+ type: "document",
1116
+ children: [{ type: "paragraph", children: [{ type: "image", url, alt }] }]
1117
+ }).trimEnd();
1118
+ return `${prefix ?? ""}${image}`;
1119
+ }
1120
+ case "zone":
1121
+ return attributes.has("pivot") ? `${prefix ?? ""}**Applies to: ${escape(attributes.get("pivot"))}**
1122
+ ` : "";
1123
+ case "zone-end":
1124
+ case "image-end":
1125
+ case "row":
1126
+ case "row-end":
1127
+ case "column":
1128
+ case "column-end":
1129
+ return "";
1130
+ default:
1131
+ throw new Error(`Unsupported DocFX directive: ${name}`);
1132
+ }
1133
+ }
1134
+ );
1135
+ return (frontmatter ?? "") + output;
1136
+ }
1137
+
967
1138
  export {
968
1139
  stringifyMarkdown,
969
1140
  DEFAULT_RESOURCE_MAX_BYTES,
@@ -979,5 +1150,7 @@ export {
979
1150
  unwrapMarkdownSource,
980
1151
  wrapMarkdownSource,
981
1152
  cleanupMarkdownSource,
982
- detectMarkdownWrapState
1153
+ detectMarkdownWrapState,
1154
+ rewriteMarkdownReferences,
1155
+ normalizeDocfxMarkdown
983
1156
  };
package/dist/index.d.ts CHANGED
@@ -7,10 +7,10 @@ export { M as MediaEntry, a as MediaProvider } from './MediaProvider-wpSe21B3.js
7
7
  export { E as EditorLayerMeta, I as ImageEditCanvas, a as ImageEditDoc, b as ImageEditLayer, c as ImageEditLayerKind, d as ImageEditMeta } from './ImageEditDoc-CYRQK8so.js';
8
8
  export { ASCII_CHAR_H, ASCII_CHAR_W, ASCII_DIAGRAM_FENCE_LANGS, ASCII_TIMELINE_FENCE_LANGS, AccentLayout, AsciiDiagram, AsciiDiagramDetection, AsciiDiagramEdge, AsciiDiagramNode, AsciiTimeline, AsciiTimelineDetection, AsciiTimelineEvent, AsciiTimelineLink, AsciiTimelineMarker, AsciiTimelineSide, AsciiTimelineStats, AsciiTimelineStyle, AsciiTimelineTrack, AudioSegmentTiming, BASE_INPUT_DESCRIPTORS, BLOCK_MEDIA_LAYOUT_POLICIES, BUILTIN_DASHBOARD_LAYOUTS, BlockLayerMaterialization, BlockMediaLayoutPolicy, BuildPreviewDocOptions, BuiltInTemplateName, CONTAINER_TEMPLATES, COVER_SLIDE_FRONTMATTER_KEYS, COVER_SLIDE_TEMPLATE_OPTIONS, ClipBox, ConnectorAnchor, ConnectorPort, ConnectorRouting, ConnectorSnapPoint, CoverBlockInput, CoverSlidePlayback, CoverSlideSettings, CoverSlideTemplate, CoverSlideTemplateOption, DASHBOARD_AUTO_LAYOUT_ID, DASHBOARD_FRONTMATTER_KEYS, DASHBOARD_STYLES, DASHBOARD_STYLE_IDS, DASHBOARD_ZOOM_LEVELS, DATA_FILE_EXTENSIONS, DEFAULT_COVER_SLIDE_SETTINGS, DEFAULT_DASHBOARD_SETTINGS, DEFAULT_DASHBOARD_STYLE, DEFAULT_LAYOUT, DEFAULT_WORD_STAGGER, DIAGRAM_LABEL_HORIZONTAL_PADDING, DIAGRAM_LABEL_LINE_HEIGHT, DIAGRAM_LABEL_MIN_FONT_SIZE, DIAGRAM_LABEL_VERTICAL_PADDING, DashboardCell, DashboardCellChrome, DashboardCellDefinition, DashboardCellFrame, DashboardDiagnostic, DashboardLayoutDefinition, DashboardLayoutSummary, DashboardLayoutValidationError, DashboardLayoutValidationResult, DashboardMaterialization, DashboardRectPct, DashboardSettings, DashboardSettingsOverrides, DashboardStyleId, DashboardStyleSummary, DashboardTitle, DashboardTitleSlotDefinition, DashboardZoomCandidate, DashboardZoomLevel, DashboardZoomMode, DataFenceParseResult, DataSourceReadOptions, DataSourceReader, DataSourceStats, DataSourceTable, DeriveTemplateInputsOptions, DetectAsciiTimelineOptions, DiagramEdge, DiagramLabelFit, DiagramLayout, DiagramLayoutOptions, DiagramNodePosition, DrawingConnector, DrawingLayout, DrawingLayoutOptions, DrawingShape, DrawingShapeKind, EmbeddedVideo, ExpandDocBlocksOptions, ExtractedTableData, FRONTMATTER_DASHBOARD_LAYOUTS_KEY, FirstImage, Flashcard, FlashcardChoice, FlashcardDeck, FlashcardDiagnostic, FlashcardDiagnosticCode, FlashcardFace, FlashcardKind, FlashcardSourceMode, GEOMETRY_BOUND_TEMPLATES, InputCoercion, LayerMaterializationDiagnostic, LayerMaterializationFailureMode, LayerMaterializationSource, LayoutLayerDefaults, LayoutLayersResult, MAX_COVER_SLIDE_DURATION_SECONDS, MarkdownToDocOptions, MarkdownValidationResult, MaterializeBlockLayersOptions, MaterializeDashboardOptions, MaterializeFlashcardsOptions, NarrationResolution, NativeMediaLayout, NoMediaLayout, PAGE_BASE_CSS, PATH_SHAPE_KINDS, PageSectionContext, PageSectionDraft, ParsedStat, RenderAsciiDiagramOptions, RenderAsciiTimelineOptions, RenderTreeOptions, RepairResult, ResolveDataReferencesOptions, ResolvedDashboardCell, ResolvedDataReferences, ResolvedPageBlock, RichListItem, RuntimeTemplateRegistry, SHAPE_NAMES, SectionExtractor, SupplementalMediaLayoutVariant, SupplementalMediaShape, SupplementalMediaVariantMatrix, TABLE_FED_TEMPLATES, TEMPLATE_AUTHORING_METADATA, TEMPLATE_INPUT_DESCRIPTORS, TEMPLATE_METADATA, TREE_FENCE_LANGS, TemplateAuthoringMetadata, TemplateAuthoringRole, TemplateBodyPolicy, TemplateInputDescriptor, TemplateMediaOwnership, TemplateMetadata, TemplateParamFinding, Tree, TreeDetection, TreeItem, TreeNode, UnconsumedMediaBehavior, ValidateOptions, adjustY, anchorPoint, applyNarrationTiming, applyRenderStyleToLayers, asciiCellToCanvas, asciiDiagramFromBlocks, asciiDiagramFromTemplateData, asciiDiagramToTemplateData, asciiTimelineToTemplateData, autoTemplatePreservesContent, bigText, buildDashboardCellChrome, buildPageCss, buildPageCssVars, buildPreviewDoc, buildRegistry, canvasToAsciiCell, chooseDashboardLayout, clipEndpoints, clipPoint, coerceTemplateParams, comparisonBar, composeDashboardLayers, computeDiagramLayout, computeDrawingLayout, computeLayoutLayers, connectorPath, contentBlock, countBlocks, countUpSpecFor, coverBlock, createAccentLayers, cssFilterForTreatment, dashboardCanvasFill, dashboardCellAccent, dataSidecarPrefix, dataTable, dateEvent, definitionCard, deriveTemplateInputs, desiredCellZoom, detectAsciiDiagram, detectAsciiTimeline, detectTree, diagramBlock, docToMarkdown, documentTitleFromFileName, drawingBlock, expandCoverBlock, expandDocBlocks, expandPersistentLayers, extractBlockquoteText, extractBodyPlainText, extractEmbeddedVideos, extractFirstEmbeddedVideo, extractFirstImage, extractImages, extractListItems, extractRichListItems, extractTableData, extractTableFromContents, factCard, fallbackBlockLayers, findFirstList, findFirstTable, fitBigTextSize, fitDiagramLabel, flattenBlocks, flattenRenderableBlocks, formatCountUp, formatCountValue, fullBleedQuote, getAccentLayout, getAnimationProgress, getAnimationStyle, getAvailableTemplates, getBlockBodyText, getBlockDepth, getBlockMediaLayoutPolicy, getDashboardLayoutSummaries, getDefaultAnimation, getDefaultAnimationDuration, getMotionProfile, getOverlayOpacity, getPersistentLayersFromTheme, getPinnedBlockMeta, getTemplateHint, getThemeFont, getTransitionClass, hasTemplate, imageWithCaption, isAsciiDiagramFence, isAsciiTimelineFence, isContainerTemplate, isDataFence, isDataFilePath, isEligibleAsciiFenceLang, isEligibleAsciiTimelineFenceLang, isEligibleTreeFenceLang, isExplicitDiagramLang, isExplicitTimelineLang, isExplicitTreeLang, isGeometryBoundTemplate, isRepairableDiagram, isShapeName, isTemplatedPageBlock, isTreeFence, isWrappedFlowTimelineSource, layoutBlock, layoutCapacity, leftFeature, lineStyleDasharray, lintTemplateParams, listBlock, listDashboardLayouts, mapBlock, markdownBlockSeparatorLines, markdownToDoc, markerPath, materializeBlockLayers, materializeDashboard, materializeFlashcards, motionDelay, motionEntrance, motionStateTransform, nearestSnapPoint, normalizeDashboardZoom, normalizeShapeKind, pageStyleDataAttributes, parseAsciiDiagram, parseAsciiTimeline, parseAsciiTimelineWithStats, parseDataFence, parseStatNumber, parseTree, parseWrappedFlowTimeline, parseYamlSubset, photoGrid, pullQuote, quoteBlock, readCustomTemplatesFromFrontmatter, readCustomThemesFromFrontmatter, readDashboardLayoutsFromFrontmatter, renderAsciiDiagram, renderAsciiTimeline, renderMarkdownBlocksHtml, renderTree, repairAsciiDiagram, replaceDataFence, resolveAudioMapping, resolveColorScheme, resolveCoverSlideSettings, resolveDashboardLayoutDefinition, resolveDashboardSettings, resolveDashboardStyleId, resolveDashboardZooms, resolveDataReferences, resolveFlashcardMarker, resolveLayoutCells, resolvePageBlock, resolvePersistentLayers, resolveTemplateName, resolveThemeForDoc, rightFeature, scaleAnimationDuration, scoreTextSimilarity, sectionExtractors, sectionHeader, shapePath, shouldUseShadow, snapEndpoints, snapPoints, statHighlight, stripBlockBackdropLayer, stripsBlockBackdrop, templateRegistry, themeWantsAmbientMotion, themedEntrance, themedFontSize, themedImageTreatment, themedScrim, themedSurfaceGradient, timelineBlock, titleBlock, transformOriginFor, transposeCells, treeBlock, treeFromMarkdownList, treeFromTemplateData, treeToTemplateData, twoColumn, validateDashboardLayoutDefinition, validateMarkdownDoc, validateMarkdownSource, videoPullQuote, videoWithCaption, wordRevealDelay, wrapWithPersistentLayers, writeCustomTemplatesToFrontmatter, writeCustomThemesToFrontmatter, writeDashboardLayoutsToFrontmatter } from './doc/index.js';
9
9
  export { M as MaterializePageSectionOptions, a as MaterializePageSectionsOptions, P as PageMedia, b as PageRichText, c as PageSection, d as PageSectionDiagnostic, e as PageSectionItem, f as PageSectionMaterialization, g as PageSectionSource, h as PageSpatialKind, i as PageTransformHints, m as materializePageSection, j as materializePageSections, r as resolvePageStyle } from './materializePageSection-Db7pABI0.js';
10
- export { calculateBearing, decodeGeohash, encodeGeohash, geohashOverlapsBounds, geohashToHierarchicalPath, getGeohash4Neighbors, getGeohashPath, getGeohashPrefix, getNeighbors, haversineDistance } from './spatial/index.js';
10
+ export { calculateBearing, decodeGeohash, destinationPoint, encodeGeohash, geohashOverlapsBounds, geohashToHierarchicalPath, getGeohash4Neighbors, getGeohashPath, getGeohashPrefix, getNeighbors, haversineDistance, interpolateGreatCircle, sampleGreatCircle } from './spatial/index.js';
11
11
  export { LocalForageAdapter, LocalForageAdapterOptions, LocalStorageAdapter, MemoryStorageAdapter, ScopedContentContainer, StorageAdapter, createMediaProviderFromContainer, scopeContainer } from './storage/index.js';
12
12
  export { C as ContentContainer, a as ContentEntry, M as MemoryContentContainer, f as findDocumentPath } from './ContentContainer-B2w9sUoL.js';
13
- export { DEFAULT_INTERACTIVE_RESOURCE_POLICY, DEFAULT_RESOURCE_MAX_BYTES, DEFAULT_RESOURCE_TIMEOUT_MS, DEFAULT_WRAP_WIDTH, FetchResourceBytesOptions, FetchedResource, HtmlPolicy, LOCAL_ONLY_RESOURCE_POLICY, MARKDOWN_SOURCE_TRANSFORMS, MarkdownSourceEdit, MarkdownSourceTransform, MarkdownSourceTransformId, MarkdownSourceTransformOptions, MarkdownSourceTransformResult, MarkdownWrapState, ResourcePolicy, ResourcePolicyError, SanitizeUrlOptions, TrailingAnnotationMatch, UrlKind, applyMarkdownSourceTransform, cleanupMarkdownSource, countNodes, createDocument, detectMarkdownWrapState, extractPlainText, fetchResourceBytes, findNodesByType, formatBlockScalar, formatFrontmatterValue, formatFrontmatterYaml, fromMdast, getChildren, inferDocumentTitle, isResourceUrlAllowed, matchTrailingPandocAttr, matchTrailingTemplateAnnotation, needsQuoting, parseFrontmatter, parseHtmlToNodes, parseMarkdown, parsePandocAttrTokens, plainTextFromInlineHtml, quoteAttrValue, readFrontmatterThemeId, sanitizeHtmlNodes, sanitizeUrl, serializePandocAttributes, setFrontmatterValues, splitFrontmatterBlock, splitKeyValueToken, stringifyHtmlNodes, stringifyMarkdown, toMdast, tokenizeAttrTokens, unquoteAttrValue, unwrapMarkdownSource, walkMarkdownTree, wrapMarkdownSource } from './markdown/index.js';
13
+ export { DEFAULT_INTERACTIVE_RESOURCE_POLICY, DEFAULT_RESOURCE_MAX_BYTES, DEFAULT_RESOURCE_TIMEOUT_MS, DEFAULT_WRAP_WIDTH, DocfxMarkdownOptions, FetchResourceBytesOptions, FetchedResource, HtmlPolicy, LOCAL_ONLY_RESOURCE_POLICY, MARKDOWN_SOURCE_TRANSFORMS, MarkdownReferenceOptions, MarkdownSourceEdit, MarkdownSourceTransform, MarkdownSourceTransformId, MarkdownSourceTransformOptions, MarkdownSourceTransformResult, MarkdownWrapState, ResourcePolicy, ResourcePolicyError, SanitizeUrlOptions, TrailingAnnotationMatch, UrlKind, applyMarkdownSourceTransform, cleanupMarkdownSource, countNodes, createDocument, detectMarkdownWrapState, extractPlainText, fetchResourceBytes, findNodesByType, formatBlockScalar, formatFrontmatterValue, formatFrontmatterYaml, fromMdast, getChildren, inferDocumentTitle, isResourceUrlAllowed, matchTrailingPandocAttr, matchTrailingTemplateAnnotation, needsQuoting, normalizeDocfxMarkdown, parseFrontmatter, parseHtmlToNodes, parseMarkdown, parsePandocAttrTokens, plainTextFromInlineHtml, quoteAttrValue, readFrontmatterThemeId, rewriteMarkdownReferences, sanitizeHtmlNodes, sanitizeUrl, serializePandocAttributes, setFrontmatterValues, splitFrontmatterBlock, splitKeyValueToken, stringifyHtmlNodes, stringifyMarkdown, toMdast, tokenizeAttrTokens, unquoteAttrValue, unwrapMarkdownSource, walkMarkdownTree, wrapMarkdownSource } from './markdown/index.js';
14
14
  export { B as BLOCK_META_KEY_DESCRIPTORS, a as BlockMetaKeyDescriptor, K as KNOWN_BLOCK_META_KEYS, b as KnownBlockMetaKey, p as parseTimeSeconds } from './annotationCoercion-Ckqpu33y.js';
15
15
  export { BuildCaptionTrackOptions, CaptionSegmentSource, DEFAULT_WORDS_PER_SECOND, NarrationTimeEstimate, NarrationTimeOptions, ReadingTimeEstimate, ReadingTimeOptions, buildCaptionTrack, calculatePrefixDuration, countSpokenWords, estimateNarrationDuration, estimateNarrationTime, estimateReadingTime, estimateSpokenWordCount, estimateTimeFromText } from './timing/index.js';
16
16
  export { SeededRandom, hashString } from './random/index.js';
package/dist/index.js CHANGED
@@ -158,6 +158,7 @@ import {
158
158
  import {
159
159
  calculateBearing,
160
160
  decodeGeohash,
161
+ destinationPoint,
161
162
  encodeGeohash,
162
163
  geohashOverlapsBounds,
163
164
  geohashToHierarchicalPath,
@@ -165,8 +166,10 @@ import {
165
166
  getGeohashPath,
166
167
  getGeohashPrefix,
167
168
  getNeighbors,
168
- haversineDistance
169
- } from "./chunk-R3KTTOMS.js";
169
+ haversineDistance,
170
+ interpolateGreatCircle,
171
+ sampleGreatCircle
172
+ } from "./chunk-JQLUGVII.js";
170
173
  import {
171
174
  BUILTIN_DASHBOARD_LAYOUTS,
172
175
  COVER_SLIDE_FRONTMATTER_KEYS,
@@ -525,7 +528,7 @@ import {
525
528
  createMediaProviderFromContainer,
526
529
  findDocumentPath,
527
530
  scopeContainer
528
- } from "./chunk-UNVHAK3A.js";
531
+ } from "./chunk-GIYWXYCK.js";
529
532
  import {
530
533
  DEFAULT_INTERACTIVE_RESOURCE_POLICY,
531
534
  DEFAULT_RESOURCE_MAX_BYTES,
@@ -539,10 +542,12 @@ import {
539
542
  detectMarkdownWrapState,
540
543
  fetchResourceBytes,
541
544
  isResourceUrlAllowed,
545
+ normalizeDocfxMarkdown,
546
+ rewriteMarkdownReferences,
542
547
  stringifyMarkdown,
543
548
  unwrapMarkdownSource,
544
549
  wrapMarkdownSource
545
- } from "./chunk-XZVBR5BP.js";
550
+ } from "./chunk-RLBHQSUG.js";
546
551
  import {
547
552
  DEFAULT_MARKDOWN_SAFETY_LIMITS,
548
553
  MarkdownLimitError,
@@ -844,6 +849,7 @@ export {
844
849
  deriveScale,
845
850
  deriveTemplateInputs,
846
851
  desiredCellZoom,
852
+ destinationPoint,
847
853
  detectAsciiDiagram,
848
854
  detectAsciiTimeline,
849
855
  detectMarkdownWrapState,
@@ -961,6 +967,7 @@ export {
961
967
  inferDocumentTitle,
962
968
  inferSchema,
963
969
  inspectAnimatedImage,
970
+ interpolateGreatCircle,
964
971
  isAsciiDiagramFence,
965
972
  isAsciiTimelineFence,
966
973
  isContainerTemplate,
@@ -1020,6 +1027,7 @@ export {
1020
1027
  nearestSnapPoint,
1021
1028
  needsQuoting,
1022
1029
  normalizeDashboardZoom,
1030
+ normalizeDocfxMarkdown,
1023
1031
  normalizeShapeKind,
1024
1032
  normalizeTransitionDirection,
1025
1033
  normalizeTransitionType,
@@ -1114,7 +1122,9 @@ export {
1114
1122
  resolveTransitionDuration,
1115
1123
  revertToImageEditVersion,
1116
1124
  revertToVersion,
1125
+ rewriteMarkdownReferences,
1117
1126
  rightFeature,
1127
+ sampleGreatCircle,
1118
1128
  sanitizeHtmlNodes,
1119
1129
  sanitizeUrl,
1120
1130
  saveImageEditVersion,
@@ -616,4 +616,29 @@ declare function cleanupMarkdownSource(source: string, options?: MarkdownSourceT
616
616
  */
617
617
  declare function detectMarkdownWrapState(source: string): MarkdownWrapState;
618
618
 
619
- export { DEFAULT_INTERACTIVE_RESOURCE_POLICY, DEFAULT_RESOURCE_MAX_BYTES, DEFAULT_RESOURCE_TIMEOUT_MS, DEFAULT_WRAP_WIDTH, type FetchResourceBytesOptions, type FetchedResource, HeadingAttributes, HtmlNode, type HtmlPolicy, LOCAL_ONLY_RESOURCE_POLICY, MARKDOWN_SOURCE_TRANSFORMS, MarkdownDocument, MarkdownNode, type MarkdownSourceEdit, type MarkdownSourceTransform, type MarkdownSourceTransformId, type MarkdownSourceTransformOptions, type MarkdownSourceTransformResult, type MarkdownWrapState, ParseOptions, type ResourcePolicy, ResourcePolicyError, type SanitizeUrlOptions, StringifyOptions, type TrailingAnnotationMatch, type UrlKind, applyMarkdownSourceTransform, cleanupMarkdownSource, countNodes, createDocument, detectMarkdownWrapState, extractPlainText, fetchResourceBytes, findNodesByType, formatBlockScalar, formatFrontmatterValue, formatFrontmatterYaml, fromMdast, getChildren, inferDocumentTitle, isResourceUrlAllowed, matchTrailingPandocAttr, matchTrailingTemplateAnnotation, needsQuoting, parseFrontmatter, parseHtmlToNodes, parseMarkdown, parsePandocAttrTokens, plainTextFromInlineHtml, quoteAttrValue, readFrontmatterThemeId, sanitizeHtmlNodes, sanitizeUrl, serializePandocAttributes, setFrontmatterValues, splitFrontmatterBlock, splitKeyValueToken, stringifyHtmlNodes, stringifyMarkdown, toMdast, tokenizeAttrTokens, unquoteAttrValue, unwrapMarkdownSource, walkMarkdownTree, wrapMarkdownSource };
619
+ interface MarkdownReferenceOptions {
620
+ /** Return undefined to retain a URL. The caller owns source-specific resolution. */
621
+ rewriteUrl?: (url: string, kind: 'link' | 'image') => string | undefined;
622
+ /** Omit image nodes, retaining their alternative text; default preserve. */
623
+ images?: 'preserve' | 'omit';
624
+ }
625
+ /**
626
+ * Rewrite references using the Markdown and HTML trees. Only changed node
627
+ * spans are serialized: frontmatter, prose, and code outside those spans stay
628
+ * byte-identical. Handles inline and reference links, images, and raw HTML.
629
+ * Does not fetch resources and is not an HTML sanitizer.
630
+ */
631
+ declare function rewriteMarkdownReferences(source: string, options?: MarkdownReferenceOptions): string;
632
+
633
+ interface DocfxMarkdownOptions {
634
+ /** Image omission retains descriptive alt text. */
635
+ images?: 'preserve' | 'omit';
636
+ }
637
+ /** Convert presentational DocFX directives to portable Markdown without fetching.
638
+ * Includes and code transclusions remain the source adapter's responsibility.
639
+ * All zone variants are retained with their pivot labels. Code examples and
640
+ * frontmatter remain untouched. Unsupported directives throw instead of losing text.
641
+ */
642
+ declare function normalizeDocfxMarkdown(source: string, options?: DocfxMarkdownOptions): string;
643
+
644
+ export { DEFAULT_INTERACTIVE_RESOURCE_POLICY, DEFAULT_RESOURCE_MAX_BYTES, DEFAULT_RESOURCE_TIMEOUT_MS, DEFAULT_WRAP_WIDTH, type DocfxMarkdownOptions, type FetchResourceBytesOptions, type FetchedResource, HeadingAttributes, HtmlNode, type HtmlPolicy, LOCAL_ONLY_RESOURCE_POLICY, MARKDOWN_SOURCE_TRANSFORMS, MarkdownDocument, MarkdownNode, type MarkdownReferenceOptions, type MarkdownSourceEdit, type MarkdownSourceTransform, type MarkdownSourceTransformId, type MarkdownSourceTransformOptions, type MarkdownSourceTransformResult, type MarkdownWrapState, ParseOptions, type ResourcePolicy, ResourcePolicyError, type SanitizeUrlOptions, StringifyOptions, type TrailingAnnotationMatch, type UrlKind, applyMarkdownSourceTransform, cleanupMarkdownSource, countNodes, createDocument, detectMarkdownWrapState, extractPlainText, fetchResourceBytes, findNodesByType, formatBlockScalar, formatFrontmatterValue, formatFrontmatterYaml, fromMdast, getChildren, inferDocumentTitle, isResourceUrlAllowed, matchTrailingPandocAttr, matchTrailingTemplateAnnotation, needsQuoting, normalizeDocfxMarkdown, parseFrontmatter, parseHtmlToNodes, parseMarkdown, parsePandocAttrTokens, plainTextFromInlineHtml, quoteAttrValue, readFrontmatterThemeId, rewriteMarkdownReferences, sanitizeHtmlNodes, sanitizeUrl, serializePandocAttributes, setFrontmatterValues, splitFrontmatterBlock, splitKeyValueToken, stringifyHtmlNodes, stringifyMarkdown, toMdast, tokenizeAttrTokens, unquoteAttrValue, unwrapMarkdownSource, walkMarkdownTree, wrapMarkdownSource };
@@ -11,10 +11,12 @@ import {
11
11
  detectMarkdownWrapState,
12
12
  fetchResourceBytes,
13
13
  isResourceUrlAllowed,
14
+ normalizeDocfxMarkdown,
15
+ rewriteMarkdownReferences,
14
16
  stringifyMarkdown,
15
17
  unwrapMarkdownSource,
16
18
  wrapMarkdownSource
17
- } from "../chunk-XZVBR5BP.js";
19
+ } from "../chunk-RLBHQSUG.js";
18
20
  import {
19
21
  DEFAULT_MARKDOWN_SAFETY_LIMITS,
20
22
  MarkdownLimitError,
@@ -94,6 +96,7 @@ export {
94
96
  matchTrailingPandocAttr,
95
97
  matchTrailingTemplateAnnotation,
96
98
  needsQuoting,
99
+ normalizeDocfxMarkdown,
97
100
  parseFrontmatter,
98
101
  parseHtmlToNodes,
99
102
  parseMarkdown,
@@ -103,6 +106,7 @@ export {
103
106
  quoteAttrValue,
104
107
  readFrontmatterThemeId,
105
108
  resolveMarkdownSafetyLimits,
109
+ rewriteMarkdownReferences,
106
110
  sanitizeHtmlNodes,
107
111
  sanitizeUrl,
108
112
  serializePandocAttributes,
@@ -113,4 +113,32 @@ declare function geohashOverlapsBounds(hash: string, bounds: {
113
113
  west: number;
114
114
  }): boolean;
115
115
 
116
- export { calculateBearing, decodeGeohash, encodeGeohash, geohashOverlapsBounds, geohashToHierarchicalPath, getGeohash4Neighbors, getGeohashPath, getGeohashPrefix, getNeighbors, haversineDistance };
116
+ /**
117
+ * Great-circle paths on a spherical Earth.
118
+ *
119
+ * `interpolateGreatCircle` and `sampleGreatCircle` walk the shortest path between two
120
+ * coordinates (spherical linear interpolation), which stays true over long and high-latitude
121
+ * routes where a straight lat/lng line distorts badly: use them to sample points along a route,
122
+ * for example to find places near it. `destinationPoint` goes the other way: from a coordinate
123
+ * along a compass bearing for a distance, for example to project a moving position ahead of
124
+ * itself. The sphere is the same one `haversineDistance` uses (mean radius 6,371 km), so the
125
+ * two agree.
126
+ */
127
+
128
+ /**
129
+ * Interpolate a point a `fraction` (0..1) of the way along the great-circle path from `from`
130
+ * to `to`. At fraction 0 returns `from`; at 1 returns `to`.
131
+ */
132
+ declare function interpolateGreatCircle(from: Coordinates, to: Coordinates, fraction: number): Coordinates;
133
+ /**
134
+ * Sample `samples` evenly spaced points (both endpoints included) along the great-circle path.
135
+ * `samples` is clamped to at least 2.
136
+ */
137
+ declare function sampleGreatCircle(from: Coordinates, to: Coordinates, samples: number): Coordinates[];
138
+ /**
139
+ * The point `distanceMeters` from `from` along the great circle that starts at compass bearing
140
+ * `bearingDeg` (0 north, 90 east). Longitude is normalized to [-180, 180).
141
+ */
142
+ declare function destinationPoint(from: Coordinates, bearingDeg: number, distanceMeters: number): Coordinates;
143
+
144
+ export { calculateBearing, decodeGeohash, destinationPoint, encodeGeohash, geohashOverlapsBounds, geohashToHierarchicalPath, getGeohash4Neighbors, getGeohashPath, getGeohashPrefix, getNeighbors, haversineDistance, interpolateGreatCircle, sampleGreatCircle };
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  calculateBearing,
3
3
  decodeGeohash,
4
+ destinationPoint,
4
5
  encodeGeohash,
5
6
  geohashOverlapsBounds,
6
7
  geohashToHierarchicalPath,
@@ -8,11 +9,14 @@ import {
8
9
  getGeohashPath,
9
10
  getGeohashPrefix,
10
11
  getNeighbors,
11
- haversineDistance
12
- } from "../chunk-R3KTTOMS.js";
12
+ haversineDistance,
13
+ interpolateGreatCircle,
14
+ sampleGreatCircle
15
+ } from "../chunk-JQLUGVII.js";
13
16
  export {
14
17
  calculateBearing,
15
18
  decodeGeohash,
19
+ destinationPoint,
16
20
  encodeGeohash,
17
21
  geohashOverlapsBounds,
18
22
  geohashToHierarchicalPath,
@@ -20,5 +24,7 @@ export {
20
24
  getGeohashPath,
21
25
  getGeohashPrefix,
22
26
  getNeighbors,
23
- haversineDistance
27
+ haversineDistance,
28
+ interpolateGreatCircle,
29
+ sampleGreatCircle
24
30
  };
@@ -7,7 +7,7 @@ import {
7
7
  createMediaProviderFromContainer,
8
8
  findDocumentPath,
9
9
  scopeContainer
10
- } from "../chunk-UNVHAK3A.js";
10
+ } from "../chunk-GIYWXYCK.js";
11
11
  export {
12
12
  LocalForageAdapter,
13
13
  LocalStorageAdapter,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/squisq",
3
- "version": "2.11.11",
3
+ "version": "2.11.13",
4
4
  "description": "Headless utilities for doc/block rendering, spatial math, Markdown, and storage",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",