@k2b/ui 0.35.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/actions/Dropdown.js +132 -116
- package/dist/browser/actions/Dropdown.js.map +1 -1
- package/dist/browser/actions/Tabs.js +18 -15
- package/dist/browser/actions/Tabs.js.map +1 -1
- package/dist/browser/chat/ChatComposer.js +58 -25
- package/dist/browser/chat/ChatComposer.js.map +1 -1
- package/dist/browser/content/MapView.js +128 -13
- package/dist/browser/content/MapView.js.map +1 -1
- package/dist/browser/content/MapViewFrame.js +1 -1
- package/dist/browser/content/MapViewFrame.js.map +1 -1
- package/dist/browser/content/MessageRow.js +164 -152
- package/dist/browser/content/MessageRow.js.map +1 -1
- package/dist/browser/content/index.js +1 -0
- package/dist/browser/content/index.js.map +1 -1
- package/dist/browser/content/map-view-glyphs.js +23 -0
- package/dist/browser/content/map-view-glyphs.js.map +1 -0
- package/dist/browser/content/map-view-live.js +45 -22
- package/dist/browser/content/map-view-live.js.map +1 -1
- package/dist/browser/content/map-view-snapshot.js +191 -0
- package/dist/browser/content/map-view-snapshot.js.map +1 -0
- package/dist/browser/content/map-view-types.js.map +1 -1
- package/dist/browser/feedback/prompts.js +23 -9
- package/dist/browser/feedback/prompts.js.map +1 -1
- package/dist/browser/inputs/Combobox.js +164 -82
- package/dist/browser/inputs/Combobox.js.map +1 -1
- package/dist/browser/inputs/RichSelect.js +86 -67
- package/dist/browser/inputs/RichSelect.js.map +1 -1
- package/dist/browser/inputs/TextInput.js +1 -1
- package/dist/browser/inputs/TextInput.js.map +1 -1
- package/dist/browser/internal/keyed-for.js +47 -0
- package/dist/browser/internal/keyed-for.js.map +1 -0
- package/dist/browser/layout/AppWorkspace.js +73 -70
- package/dist/browser/layout/AppWorkspace.js.map +1 -1
- package/dist/browser/layout/ScrollArea.js +4 -1
- package/dist/browser/layout/ScrollArea.js.map +1 -1
- package/dist/browser/text-editor/engine.js +13 -10
- package/dist/browser/text-editor/engine.js.map +1 -1
- package/dist/ssr/actions/Dropdown.js +101 -85
- package/dist/ssr/actions/Dropdown.js.map +1 -1
- package/dist/ssr/actions/Tabs.js +12 -9
- package/dist/ssr/actions/Tabs.js.map +1 -1
- package/dist/ssr/chat/ChatComposer.js +41 -15
- package/dist/ssr/chat/ChatComposer.js.map +1 -1
- package/dist/ssr/content/MapView.js +128 -13
- package/dist/ssr/content/MapView.js.map +1 -1
- package/dist/ssr/content/MapViewFrame.js +1 -1
- package/dist/ssr/content/MapViewFrame.js.map +1 -1
- package/dist/ssr/content/MessageRow.js +9 -2
- package/dist/ssr/content/MessageRow.js.map +1 -1
- package/dist/ssr/content/index.js +1 -0
- package/dist/ssr/content/index.js.map +1 -1
- package/dist/ssr/content/map-view-glyphs.js +23 -0
- package/dist/ssr/content/map-view-glyphs.js.map +1 -0
- package/dist/ssr/content/map-view-snapshot.js +191 -0
- package/dist/ssr/content/map-view-snapshot.js.map +1 -0
- package/dist/ssr/content/map-view-types.js.map +1 -1
- package/dist/ssr/feedback/prompts.js +22 -8
- package/dist/ssr/feedback/prompts.js.map +1 -1
- package/dist/ssr/inputs/Combobox.js +79 -28
- package/dist/ssr/inputs/Combobox.js.map +1 -1
- package/dist/ssr/inputs/RichSelect.js +47 -28
- package/dist/ssr/inputs/RichSelect.js.map +1 -1
- package/dist/ssr/inputs/TextInput.js.map +1 -1
- package/dist/ssr/internal/keyed-for.js +47 -0
- package/dist/ssr/internal/keyed-for.js.map +1 -0
- package/dist/ssr/layout/AppWorkspace.js +1 -1
- package/dist/ssr/layout/AppWorkspace.js.map +1 -1
- package/dist/ssr/layout/ScrollArea.js +4 -1
- package/dist/ssr/layout/ScrollArea.js.map +1 -1
- package/dist/ssr/text-editor/engine.js +13 -10
- package/dist/ssr/text-editor/engine.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/content/MapViewFrame.d.ts +1 -1
- package/dist/types/content/MessageRow.d.ts +1 -1
- package/dist/types/content/index.d.ts +2 -1
- package/dist/types/content/map-view-glyphs.d.ts +5 -0
- package/dist/types/content/map-view-live.d.ts +1 -1
- package/dist/types/content/map-view-snapshot.d.ts +13 -0
- package/dist/types/content/map-view-types.d.ts +15 -0
- package/dist/types/feedback/prompts.d.ts +5 -0
- package/dist/types/inputs/Combobox.d.ts +12 -0
- package/dist/types/inputs/TextInput.d.ts +2 -1
- package/dist/types/internal/keyed-for.d.ts +13 -0
- package/dist/types/layout/AppWorkspace.d.ts +4 -1
- package/dist/types/layout/ScrollArea.d.ts +2 -0
- package/dist/types/text-editor/engine.d.ts +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/** Same decoded RGBA budget as Cloud's isolated print-image worker (64 MiB). */
|
|
2
|
+
export const MAP_SNAPSHOT_MAX_PIXELS = 16 * 1024 * 1024;
|
|
3
|
+
export class MapSnapshotError extends Error {
|
|
4
|
+
constructor(code) {
|
|
5
|
+
super(`Map snapshot: ${code}`);
|
|
6
|
+
this.code = code;
|
|
7
|
+
this.name = "MapSnapshotError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function snapshotAborted(signal) {
|
|
11
|
+
return signal.reason instanceof MapSnapshotError ? signal.reason : new MapSnapshotError("aborted");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Also observes late rejection; a cancelled image/font/encoding operation never becomes unhandled. */
|
|
15
|
+
export function snapshotWait(promise, signal) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const abort = () => reject(snapshotAborted(signal));
|
|
18
|
+
if (signal.aborted) return abort();
|
|
19
|
+
signal.addEventListener("abort", abort, {
|
|
20
|
+
once: true
|
|
21
|
+
});
|
|
22
|
+
promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const svgNamespace = "http://www.w3.org/2000/svg";
|
|
26
|
+
// Only renderer-owned shapes and paint cross into the rasterizer; no foreignObject, links, images or CSS URLs.
|
|
27
|
+
const paints = ["fill", "fill-opacity", "stroke", "stroke-width", "stroke-opacity", "stroke-dasharray", "stroke-linecap", "stroke-linejoin"];
|
|
28
|
+
function geometryImage(root, bounds) {
|
|
29
|
+
const source = root.querySelector(".k2b-map-view__geometry");
|
|
30
|
+
if (!source) return;
|
|
31
|
+
const svg = document.createElementNS(svgNamespace, "svg");
|
|
32
|
+
svg.setAttribute("width", String(bounds.width));
|
|
33
|
+
svg.setAttribute("height", String(bounds.height));
|
|
34
|
+
const geometry = document.createElementNS(svgNamespace, "svg");
|
|
35
|
+
const box = source.getBoundingClientRect();
|
|
36
|
+
for (const [name, value] of Object.entries({
|
|
37
|
+
x: box.left - bounds.left,
|
|
38
|
+
y: box.top - bounds.top,
|
|
39
|
+
width: box.width,
|
|
40
|
+
height: box.height,
|
|
41
|
+
viewBox: source.getAttribute("viewBox") ?? "",
|
|
42
|
+
preserveAspectRatio: "none",
|
|
43
|
+
overflow: "visible"
|
|
44
|
+
})) geometry.setAttribute(name, String(value));
|
|
45
|
+
for (const shape of source.querySelectorAll("path")) {
|
|
46
|
+
const copy = document.createElementNS(svgNamespace, shape.localName);
|
|
47
|
+
for (const name of ["d", "cx", "cy", "r", "vector-effect"]) {
|
|
48
|
+
const value = shape.getAttribute(name);
|
|
49
|
+
if (value !== null) copy.setAttribute(name, value);
|
|
50
|
+
}
|
|
51
|
+
const style = getComputedStyle(shape);
|
|
52
|
+
for (const name of paints) {
|
|
53
|
+
const value = style.getPropertyValue(name);
|
|
54
|
+
if (value.includes("url(")) throw new MapSnapshotError("capture_failed");
|
|
55
|
+
copy.setAttribute(name, value);
|
|
56
|
+
}
|
|
57
|
+
geometry.append(copy);
|
|
58
|
+
}
|
|
59
|
+
svg.append(geometry);
|
|
60
|
+
const image = new Image();
|
|
61
|
+
image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(new XMLSerializer().serializeToString(svg))}`;
|
|
62
|
+
return image;
|
|
63
|
+
}
|
|
64
|
+
function drawPins(ctx, root, bounds) {
|
|
65
|
+
const pins = Array.from(root.querySelectorAll(".k2b-map-view__pin"));
|
|
66
|
+
// The selected pin paints above the others, as in the DOM.
|
|
67
|
+
pins.sort((a, b) => Number(a.getAttribute("aria-pressed") === "true") - Number(b.getAttribute("aria-pressed") === "true"));
|
|
68
|
+
for (const pin of pins) {
|
|
69
|
+
const box = pin.getBoundingClientRect();
|
|
70
|
+
const style = getComputedStyle(pin);
|
|
71
|
+
const x = box.left - bounds.left,
|
|
72
|
+
y = box.top - bounds.top;
|
|
73
|
+
// Resolve CSS variables through a paint property, since custom properties can still contain var().
|
|
74
|
+
const selected = pin.getAttribute("aria-pressed") === "true" || pin.hasAttribute("data-selected");
|
|
75
|
+
const stop = pin.hasAttribute("data-stop");
|
|
76
|
+
const surface = getComputedStyle(root.querySelector(".k2b-map-view__attribution") ?? root).backgroundColor;
|
|
77
|
+
ctx.save();
|
|
78
|
+
if (pin.hasAttribute("data-drop")) {
|
|
79
|
+
const span = pin.querySelector("span");
|
|
80
|
+
ctx.fillStyle = getComputedStyle(span).backgroundColor;
|
|
81
|
+
ctx.beginPath();
|
|
82
|
+
ctx.ellipse(x + box.width / 2, y + box.height / 2, box.width / 2, box.height / 2, 0, Math.PI, Math.PI * 2);
|
|
83
|
+
ctx.lineTo(x + box.width, y + box.height * 0.68);
|
|
84
|
+
ctx.lineTo(x + box.width / 2, y + box.height);
|
|
85
|
+
ctx.lineTo(x, y + box.height * 0.68);
|
|
86
|
+
ctx.closePath();
|
|
87
|
+
ctx.fill();
|
|
88
|
+
if (selected) {
|
|
89
|
+
ctx.fillStyle = surface;
|
|
90
|
+
ctx.beginPath();
|
|
91
|
+
ctx.arc(x + box.width / 2, y + box.height * 0.34, 4, 0, Math.PI * 2);
|
|
92
|
+
ctx.fill();
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
ctx.beginPath();
|
|
96
|
+
ctx.arc(x + box.width / 2, y + box.height / 2, box.width / 2 + 2.5, 0, Math.PI * 2);
|
|
97
|
+
ctx.fillStyle = surface;
|
|
98
|
+
ctx.fill();
|
|
99
|
+
ctx.beginPath();
|
|
100
|
+
ctx.arc(x + box.width / 2, y + box.height / 2, box.width / 2, 0, Math.PI * 2);
|
|
101
|
+
ctx.fillStyle = style.backgroundColor;
|
|
102
|
+
ctx.fill();
|
|
103
|
+
// The first box-shadow is the resolved inset tone ring. Text ink can differ in dark mode.
|
|
104
|
+
const ring = style.boxShadow.match(/(?:rgba?|color|oklch|oklab)\([^)]*\)/)?.[0];
|
|
105
|
+
if (!ring && !stop && !selected) throw new MapSnapshotError("capture_failed");
|
|
106
|
+
ctx.strokeStyle = stop || selected ? style.backgroundColor : ring;
|
|
107
|
+
ctx.lineWidth = 2;
|
|
108
|
+
ctx.beginPath();
|
|
109
|
+
ctx.arc(x + box.width / 2, y + box.height / 2, box.width / 2 - 1, 0, Math.PI * 2);
|
|
110
|
+
ctx.stroke();
|
|
111
|
+
ctx.fillStyle = style.color;
|
|
112
|
+
ctx.font = `${style.fontWeight} ${style.fontSize} ${style.fontFamily}`;
|
|
113
|
+
ctx.textAlign = "center";
|
|
114
|
+
ctx.textBaseline = "middle";
|
|
115
|
+
ctx.fillText(pin.textContent ?? "", x + box.width / 2, y + box.height / 2);
|
|
116
|
+
}
|
|
117
|
+
ctx.restore();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function drawAttribution(ctx, root, bounds) {
|
|
121
|
+
const attribution = root.querySelector(".k2b-map-view__attribution");
|
|
122
|
+
if (!attribution?.textContent?.trim()) throw new MapSnapshotError("capture_failed");
|
|
123
|
+
const box = attribution.getBoundingClientRect();
|
|
124
|
+
const style = getComputedStyle(attribution);
|
|
125
|
+
ctx.fillStyle = style.backgroundColor;
|
|
126
|
+
ctx.fillRect(box.left - bounds.left, box.top - bounds.top, box.width, box.height);
|
|
127
|
+
const walker = document.createTreeWalker(attribution, NodeFilter.SHOW_TEXT);
|
|
128
|
+
while (walker.nextNode()) {
|
|
129
|
+
const node = walker.currentNode;
|
|
130
|
+
const parent = node.parentElement;
|
|
131
|
+
const ink = getComputedStyle(parent);
|
|
132
|
+
ctx.fillStyle = ink.color;
|
|
133
|
+
ctx.font = `${ink.fontWeight} ${ink.fontSize} ${ink.fontFamily}`;
|
|
134
|
+
ctx.textBaseline = "top";
|
|
135
|
+
ctx.textAlign = "left";
|
|
136
|
+
// Per-character ranges preserve line breaks, wrapping, and bidi positions without capturing arbitrary HTML.
|
|
137
|
+
const range = document.createRange();
|
|
138
|
+
for (let i = 0; i < (node.textContent?.length ?? 0); i++) {
|
|
139
|
+
range.setStart(node, i);
|
|
140
|
+
range.setEnd(node, i + 1);
|
|
141
|
+
const r = range.getBoundingClientRect();
|
|
142
|
+
ctx.fillText(node.textContent[i], r.left - bounds.left, r.top - bounds.top);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Called synchronously in MapLibre's render event, before WebGL clears its drawing buffer. */
|
|
148
|
+
export async function rasterizeMapView(root, base, signal) {
|
|
149
|
+
const bounds = root.getBoundingClientRect();
|
|
150
|
+
const width = Math.round(bounds.width),
|
|
151
|
+
height = Math.round(bounds.height);
|
|
152
|
+
if (width < 1 || height < 1 || width > 16383 || height > 16383 || width * height > MAP_SNAPSHOT_MAX_PIXELS) throw new MapSnapshotError("too_large");
|
|
153
|
+
const canvas = document.createElement("canvas");
|
|
154
|
+
canvas.width = width;
|
|
155
|
+
canvas.height = height;
|
|
156
|
+
const ctx = canvas.getContext("2d");
|
|
157
|
+
if (!ctx) throw new MapSnapshotError("capture_failed");
|
|
158
|
+
try {
|
|
159
|
+
// Copy the GPU frame immediately. No preserveDrawingBuffer overhead for ordinary interactive maps.
|
|
160
|
+
ctx.drawImage(base, 0, 0, width, height);
|
|
161
|
+
const geometry = geometryImage(root, bounds);
|
|
162
|
+
// Save all overlay pixels before yielding, so subsequent DOM changes cannot mix revisions.
|
|
163
|
+
const overlay = document.createElement("canvas");
|
|
164
|
+
overlay.width = width;
|
|
165
|
+
overlay.height = height;
|
|
166
|
+
try {
|
|
167
|
+
const ink = overlay.getContext("2d");
|
|
168
|
+
drawPins(ink, root, bounds);
|
|
169
|
+
drawAttribution(ink, root, bounds);
|
|
170
|
+
if (geometry) {
|
|
171
|
+
await snapshotWait(geometry.decode(), signal);
|
|
172
|
+
ctx.drawImage(geometry, 0, 0, width, height);
|
|
173
|
+
}
|
|
174
|
+
ctx.drawImage(overlay, 0, 0);
|
|
175
|
+
} finally {
|
|
176
|
+
overlay.width = overlay.height = 0;
|
|
177
|
+
}
|
|
178
|
+
const data = await snapshotWait(new Promise((resolve, reject) => canvas.toBlob(blob => blob ? resolve(blob) : reject(new MapSnapshotError("capture_failed")), "image/png")), signal);
|
|
179
|
+
return {
|
|
180
|
+
data,
|
|
181
|
+
width,
|
|
182
|
+
height
|
|
183
|
+
};
|
|
184
|
+
} catch (error) {
|
|
185
|
+
if (error instanceof MapSnapshotError) throw error;
|
|
186
|
+
throw new MapSnapshotError("capture_failed");
|
|
187
|
+
} finally {
|
|
188
|
+
canvas.width = canvas.height = 0;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=map-view-snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["content/map-view-snapshot.ts"],"sourcesContent":["import type { MapViewSnapshot } from \"./map-view-types\";\n\n/** Same decoded RGBA budget as Cloud's isolated print-image worker (64 MiB). */\nexport const MAP_SNAPSHOT_MAX_PIXELS = 16 * 1024 * 1024;\nexport type MapSnapshotErrorCode =\n | \"not_ready\"\n | \"resource_failed\"\n | \"changed\"\n | \"disposed\"\n | \"busy\"\n | \"too_large\"\n | \"timed_out\"\n | \"aborted\"\n | \"capture_failed\";\nexport class MapSnapshotError extends Error {\n constructor(readonly code: MapSnapshotErrorCode) {\n super(`Map snapshot: ${code}`);\n this.name = \"MapSnapshotError\";\n }\n}\n\nexport function snapshotAborted(signal: AbortSignal): MapSnapshotError {\n return signal.reason instanceof MapSnapshotError ? signal.reason : new MapSnapshotError(\"aborted\");\n}\n\n/** Also observes late rejection; a cancelled image/font/encoding operation never becomes unhandled. */\nexport function snapshotWait<T>(promise: Promise<T>, signal: AbortSignal): Promise<T> {\n return new Promise((resolve, reject) => {\n const abort = () => reject(snapshotAborted(signal));\n if (signal.aborted) return abort();\n signal.addEventListener(\"abort\", abort, { once: true });\n promise.then(resolve, reject).finally(() => signal.removeEventListener(\"abort\", abort));\n });\n}\n\nconst svgNamespace = \"http://www.w3.org/2000/svg\";\n// Only renderer-owned shapes and paint cross into the rasterizer; no foreignObject, links, images or CSS URLs.\nconst paints = [\n \"fill\",\n \"fill-opacity\",\n \"stroke\",\n \"stroke-width\",\n \"stroke-opacity\",\n \"stroke-dasharray\",\n \"stroke-linecap\",\n \"stroke-linejoin\",\n] as const;\nfunction geometryImage(root: HTMLElement, bounds: DOMRect): HTMLImageElement | undefined {\n const source = root.querySelector<SVGSVGElement>(\".k2b-map-view__geometry\");\n if (!source) return;\n const svg = document.createElementNS(svgNamespace, \"svg\");\n svg.setAttribute(\"width\", String(bounds.width));\n svg.setAttribute(\"height\", String(bounds.height));\n const geometry = document.createElementNS(svgNamespace, \"svg\");\n const box = source.getBoundingClientRect();\n for (const [name, value] of Object.entries({\n x: box.left - bounds.left,\n y: box.top - bounds.top,\n width: box.width,\n height: box.height,\n viewBox: source.getAttribute(\"viewBox\") ?? \"\",\n preserveAspectRatio: \"none\",\n overflow: \"visible\",\n }))\n geometry.setAttribute(name, String(value));\n for (const shape of source.querySelectorAll(\"path\")) {\n const copy = document.createElementNS(svgNamespace, shape.localName);\n for (const name of [\"d\", \"cx\", \"cy\", \"r\", \"vector-effect\"]) {\n const value = shape.getAttribute(name);\n if (value !== null) copy.setAttribute(name, value);\n }\n const style = getComputedStyle(shape);\n for (const name of paints) {\n const value = style.getPropertyValue(name);\n if (value.includes(\"url(\")) throw new MapSnapshotError(\"capture_failed\");\n copy.setAttribute(name, value);\n }\n geometry.append(copy);\n }\n svg.append(geometry);\n const image = new Image();\n image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(new XMLSerializer().serializeToString(svg))}`;\n return image;\n}\n\nfunction drawPins(ctx: CanvasRenderingContext2D, root: HTMLElement, bounds: DOMRect) {\n const pins = Array.from(root.querySelectorAll<HTMLElement>(\".k2b-map-view__pin\"));\n // The selected pin paints above the others, as in the DOM.\n pins.sort((a, b) => Number(a.getAttribute(\"aria-pressed\") === \"true\") - Number(b.getAttribute(\"aria-pressed\") === \"true\"));\n for (const pin of pins) {\n const box = pin.getBoundingClientRect();\n const style = getComputedStyle(pin);\n const x = box.left - bounds.left,\n y = box.top - bounds.top;\n // Resolve CSS variables through a paint property, since custom properties can still contain var().\n const selected = pin.getAttribute(\"aria-pressed\") === \"true\" || pin.hasAttribute(\"data-selected\");\n const stop = pin.hasAttribute(\"data-stop\");\n const surface = getComputedStyle(root.querySelector(\".k2b-map-view__attribution\") ?? root).backgroundColor;\n ctx.save();\n if (pin.hasAttribute(\"data-drop\")) {\n const span = pin.querySelector(\"span\")!;\n ctx.fillStyle = getComputedStyle(span).backgroundColor;\n ctx.beginPath();\n ctx.ellipse(x + box.width / 2, y + box.height / 2, box.width / 2, box.height / 2, 0, Math.PI, Math.PI * 2);\n ctx.lineTo(x + box.width, y + box.height * 0.68);\n ctx.lineTo(x + box.width / 2, y + box.height);\n ctx.lineTo(x, y + box.height * 0.68);\n ctx.closePath();\n ctx.fill();\n if (selected) {\n ctx.fillStyle = surface;\n ctx.beginPath();\n ctx.arc(x + box.width / 2, y + box.height * 0.34, 4, 0, Math.PI * 2);\n ctx.fill();\n }\n } else {\n ctx.beginPath();\n ctx.arc(x + box.width / 2, y + box.height / 2, box.width / 2 + 2.5, 0, Math.PI * 2);\n ctx.fillStyle = surface;\n ctx.fill();\n ctx.beginPath();\n ctx.arc(x + box.width / 2, y + box.height / 2, box.width / 2, 0, Math.PI * 2);\n ctx.fillStyle = style.backgroundColor;\n ctx.fill();\n // The first box-shadow is the resolved inset tone ring. Text ink can differ in dark mode.\n const ring = style.boxShadow.match(/(?:rgba?|color|oklch|oklab)\\([^)]*\\)/)?.[0];\n if (!ring && !stop && !selected) throw new MapSnapshotError(\"capture_failed\");\n ctx.strokeStyle = stop || selected ? style.backgroundColor : ring!;\n ctx.lineWidth = 2;\n ctx.beginPath();\n ctx.arc(x + box.width / 2, y + box.height / 2, box.width / 2 - 1, 0, Math.PI * 2);\n ctx.stroke();\n ctx.fillStyle = style.color;\n ctx.font = `${style.fontWeight} ${style.fontSize} ${style.fontFamily}`;\n ctx.textAlign = \"center\";\n ctx.textBaseline = \"middle\";\n ctx.fillText(pin.textContent ?? \"\", x + box.width / 2, y + box.height / 2);\n }\n ctx.restore();\n }\n}\n\nfunction drawAttribution(ctx: CanvasRenderingContext2D, root: HTMLElement, bounds: DOMRect) {\n const attribution = root.querySelector<HTMLElement>(\".k2b-map-view__attribution\");\n if (!attribution?.textContent?.trim()) throw new MapSnapshotError(\"capture_failed\");\n const box = attribution.getBoundingClientRect();\n const style = getComputedStyle(attribution);\n ctx.fillStyle = style.backgroundColor;\n ctx.fillRect(box.left - bounds.left, box.top - bounds.top, box.width, box.height);\n const walker = document.createTreeWalker(attribution, NodeFilter.SHOW_TEXT);\n while (walker.nextNode()) {\n const node = walker.currentNode;\n const parent = node.parentElement!;\n const ink = getComputedStyle(parent);\n ctx.fillStyle = ink.color;\n ctx.font = `${ink.fontWeight} ${ink.fontSize} ${ink.fontFamily}`;\n ctx.textBaseline = \"top\";\n ctx.textAlign = \"left\";\n // Per-character ranges preserve line breaks, wrapping, and bidi positions without capturing arbitrary HTML.\n const range = document.createRange();\n for (let i = 0; i < (node.textContent?.length ?? 0); i++) {\n range.setStart(node, i);\n range.setEnd(node, i + 1);\n const r = range.getBoundingClientRect();\n ctx.fillText(node.textContent![i]!, r.left - bounds.left, r.top - bounds.top);\n }\n }\n}\n\n/** Called synchronously in MapLibre's render event, before WebGL clears its drawing buffer. */\nexport async function rasterizeMapView(root: HTMLElement, base: HTMLCanvasElement, signal: AbortSignal): Promise<MapViewSnapshot> {\n const bounds = root.getBoundingClientRect();\n const width = Math.round(bounds.width),\n height = Math.round(bounds.height);\n if (width < 1 || height < 1 || width > 16383 || height > 16383 || width * height > MAP_SNAPSHOT_MAX_PIXELS)\n throw new MapSnapshotError(\"too_large\");\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) throw new MapSnapshotError(\"capture_failed\");\n try {\n // Copy the GPU frame immediately. No preserveDrawingBuffer overhead for ordinary interactive maps.\n ctx.drawImage(base, 0, 0, width, height);\n const geometry = geometryImage(root, bounds);\n // Save all overlay pixels before yielding, so subsequent DOM changes cannot mix revisions.\n const overlay = document.createElement(\"canvas\");\n overlay.width = width;\n overlay.height = height;\n try {\n const ink = overlay.getContext(\"2d\")!;\n drawPins(ink, root, bounds);\n drawAttribution(ink, root, bounds);\n if (geometry) {\n await snapshotWait(geometry.decode(), signal);\n ctx.drawImage(geometry, 0, 0, width, height);\n }\n ctx.drawImage(overlay, 0, 0);\n } finally {\n overlay.width = overlay.height = 0;\n }\n const data = await snapshotWait(\n new Promise<Blob>((resolve, reject) =>\n canvas.toBlob((blob) => (blob ? resolve(blob) : reject(new MapSnapshotError(\"capture_failed\"))), \"image/png\"),\n ),\n signal,\n );\n return { data, width, height };\n } catch (error) {\n if (error instanceof MapSnapshotError) throw error;\n throw new MapSnapshotError(\"capture_failed\");\n } finally {\n canvas.width = canvas.height = 0;\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["content/map-view-types.ts"],"sourcesContent":["import type { StyleSpecification } from \"maplibre-gl\";\nimport type { JSX } from \"solid-js\";\n\nexport type MapViewPoint = { lat: number; lon: number };\nexport type MapViewTone = \"accent\" | \"second\" | \"neutral\" | \"warning\";\nexport type MapViewMarker = {\n id: string;\n point: MapViewPoint;\n /** A number, a single letter, or \"+\". Omit for a drop pin. */\n mark?: number | string;\n label: string;\n tone?: MapViewTone;\n selected?: boolean;\n};\nexport type MapViewLineTone = \"accent\" | \"muted\" | \"preview\" | \"previous\";\nexport type MapViewLine = { id: string; tone?: MapViewLineTone } & ({ polyline6: string } | { points: MapViewPoint[] });\nexport type MapViewArea = { id: string; polygon: MapViewPoint[]; tone?: MapViewTone };\nexport type MapViewOwnLocation = { point: MapViewPoint; accuracyMeters?: number };\nexport type MapViewStyle = string | StyleSpecification;\nexport type MapViewStyleSource = MapViewStyle | { light: MapViewStyle; dark: MapViewStyle };\nexport type MapViewFit = \"all\" | \"markers\";\nexport type MapViewFrameProps = {\n markers?: MapViewMarker[];\n lines?: MapViewLine[];\n areas?: MapViewArea[];\n ownLocation?: MapViewOwnLocation;\n height: number;\n padding?: number;\n fit?: MapViewFit;\n selectedId?: string;\n attribution?: JSX.Element;\n class?: string;\n};\nexport type MapViewProps = Omit<MapViewFrameProps, \"attribution\"> & {\n style: MapViewStyleSource;\n attribution: JSX.Element;\n onSelect?: (id: string) => void;\n infoTile?: (marker: MapViewMarker) => JSX.Element;\n notice?: JSX.Element;\n empty?: JSX.Element;\n onLocate?: () => void;\n /** Observe rendering and resource failures without receiving provider responses or URLs. */\n onStateChange?: (state: \"static\" | \"loading\" | \"ready\" | \"error\" | \"no-webgl\" | \"empty\") => void;\n};\n"],"mappings":"","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":[],"sources":["content/map-view-types.ts"],"sourcesContent":["import type { StyleSpecification } from \"maplibre-gl\";\nimport type { JSX } from \"solid-js\";\n\nexport type MapViewPoint = { lat: number; lon: number };\nexport type MapViewTone = \"accent\" | \"second\" | \"neutral\" | \"warning\";\nexport type MapViewMarker = {\n id: string;\n point: MapViewPoint;\n /** A number, a single letter, or \"+\". Omit for a drop pin. */\n mark?: number | string;\n label: string;\n tone?: MapViewTone;\n selected?: boolean;\n};\nexport type MapViewLineTone = \"accent\" | \"muted\" | \"preview\" | \"previous\";\nexport type MapViewLine = { id: string; tone?: MapViewLineTone } & ({ polyline6: string } | { points: MapViewPoint[] });\nexport type MapViewArea = { id: string; polygon: MapViewPoint[]; tone?: MapViewTone };\nexport type MapViewOwnLocation = { point: MapViewPoint; accuracyMeters?: number };\nexport type MapViewStyle = string | StyleSpecification;\nexport type MapViewStyleSource = MapViewStyle | { light: MapViewStyle; dark: MapViewStyle };\nexport type MapViewFit = \"all\" | \"markers\";\nexport type MapViewFrameProps = {\n markers?: MapViewMarker[];\n lines?: MapViewLine[];\n areas?: MapViewArea[];\n ownLocation?: MapViewOwnLocation;\n height: number;\n padding?: number;\n fit?: MapViewFit;\n selectedId?: string;\n attribution?: JSX.Element;\n class?: string;\n};\nexport type MapViewSnapshot = { data: Blob; width: number; height: number };\nexport type MapViewSnapshotOptions = { signal?: AbortSignal; timeoutMs?: number };\nexport type MapViewHandle = {\n /** Capture the current complete view; never loads, pans, refits, or retries provider resources. */\n snapshot: (options?: MapViewSnapshotOptions) => Promise<MapViewSnapshot>;\n};\nexport type MapViewProps = Omit<MapViewFrameProps, \"attribution\"> & {\n style: MapViewStyleSource;\n /** Browser-only handle; remains safely callable after disposal (rejects disposed). */\n ref?: (handle: MapViewHandle) => void;\n attribution: JSX.Element;\n onSelect?: (id: string) => void;\n infoTile?: (marker: MapViewMarker) => JSX.Element;\n notice?: JSX.Element;\n empty?: JSX.Element;\n onLocate?: () => void;\n /** Observe rendering and resource failures without receiving provider responses or URLs. */\n onStateChange?: (state: \"static\" | \"loading\" | \"ready\" | \"error\" | \"no-webgl\" | \"empty\") => void;\n};\n"],"mappings":"","ignoreList":[]}
|
|
@@ -50,6 +50,7 @@ import { Select } from "../inputs/Select";
|
|
|
50
50
|
import { resolveSelectMessages } from "../inputs/select-messages";
|
|
51
51
|
import { TagsInput } from "../inputs/TagsInput";
|
|
52
52
|
import { TextInput } from "../inputs/TextInput";
|
|
53
|
+
import { KeyedFor } from "../internal/keyed-for";
|
|
53
54
|
import { resolveUiMessages } from "../intl/messages";
|
|
54
55
|
import { dialogCore } from "./dialog-core";
|
|
55
56
|
import { resolvePromptMessages } from "./prompt-messages";
|
|
@@ -460,6 +461,18 @@ const PromptFormDialog = props => {
|
|
|
460
461
|
return _el$1;
|
|
461
462
|
})();
|
|
462
463
|
};
|
|
464
|
+
|
|
465
|
+
// Results without an ID keep today's behavior: each object is its own identity, so a new answer replaces the row.
|
|
466
|
+
const anonymousRows = new WeakMap();
|
|
467
|
+
let anonymousRow = 0;
|
|
468
|
+
const searchRowIdentity = item => {
|
|
469
|
+
if (item.id !== undefined) return `id:${item.id}`;
|
|
470
|
+
const known = anonymousRows.get(item);
|
|
471
|
+
if (known !== undefined) return known;
|
|
472
|
+
const key = `object:${anonymousRow++}`;
|
|
473
|
+
anonymousRows.set(item, key);
|
|
474
|
+
return key;
|
|
475
|
+
};
|
|
463
476
|
const openSearchPrompt = (resolver, options) => dialogCore.open(close => {
|
|
464
477
|
const [query, setQuery] = createSignal(options?.initialQuery ?? "");
|
|
465
478
|
const [items, setItems] = createSignal([]);
|
|
@@ -635,10 +648,11 @@ const openSearchPrompt = (resolver, options) => dialogCore.open(close => {
|
|
|
635
648
|
get children() {
|
|
636
649
|
var _el$29 = _$getNextElement(_tmpl$0);
|
|
637
650
|
_$setAttribute(_el$29, "id", listboxId);
|
|
638
|
-
_$insert(_el$29, _$createComponent(
|
|
651
|
+
_$insert(_el$29, _$createComponent(KeyedFor, {
|
|
639
652
|
get each() {
|
|
640
653
|
return items();
|
|
641
654
|
},
|
|
655
|
+
by: searchRowIdentity,
|
|
642
656
|
children: (item, index) => (() => {
|
|
643
657
|
var _el$39 = _$getNextElement(_tmpl$15),
|
|
644
658
|
_el$46 = _el$39.firstChild,
|
|
@@ -647,42 +661,42 @@ const openSearchPrompt = (resolver, options) => dialogCore.open(close => {
|
|
|
647
661
|
_el$43 = _el$42.firstChild,
|
|
648
662
|
_el$44 = _el$43.nextSibling,
|
|
649
663
|
[_el$45, _co$9] = _$getNextMarker(_el$44.nextSibling);
|
|
650
|
-
_el$39.$$click = () => execute(item);
|
|
664
|
+
_el$39.$$click = () => execute(item());
|
|
651
665
|
_el$39.addEventListener("mouseenter", () => setActiveIndex(index()));
|
|
652
666
|
_$use(element => {
|
|
653
667
|
if (!element) rowRefs.delete(index());else rowRefs.set(index(), element);
|
|
654
668
|
}, _el$39);
|
|
655
669
|
_$insert(_el$39, _$createComponent(Show, {
|
|
656
670
|
get when() {
|
|
657
|
-
return item.previewUrl?.startsWith("/") || item.icon;
|
|
671
|
+
return item().previewUrl?.startsWith("/") || item().icon;
|
|
658
672
|
},
|
|
659
673
|
get children() {
|
|
660
674
|
var _el$40 = _$getNextElement(_tmpl$14);
|
|
661
675
|
_$insert(_el$40, _$createComponent(Show, {
|
|
662
676
|
get when() {
|
|
663
|
-
return _$memo(() => !!item.previewUrl?.startsWith("/"))() && !failedPreviews[index()];
|
|
677
|
+
return _$memo(() => !!item().previewUrl?.startsWith("/"))() && !failedPreviews[index()];
|
|
664
678
|
},
|
|
665
679
|
get fallback() {
|
|
666
680
|
return (() => {
|
|
667
681
|
var _el$48 = _$getNextElement(_tmpl$2);
|
|
668
|
-
_$effect(() => _$className(_el$48, item.icon ?? "ti ti-file"));
|
|
682
|
+
_$effect(() => _$className(_el$48, item().icon ?? "ti ti-file"));
|
|
669
683
|
return _el$48;
|
|
670
684
|
})();
|
|
671
685
|
},
|
|
672
686
|
get children() {
|
|
673
687
|
var _el$41 = _$getNextElement(_tmpl$13);
|
|
674
688
|
_el$41.addEventListener("error", () => setFailedPreviews(index(), true));
|
|
675
|
-
_$effect(() => _$setAttribute(_el$41, "src", item.previewUrl));
|
|
689
|
+
_$effect(() => _$setAttribute(_el$41, "src", item().previewUrl));
|
|
676
690
|
return _el$41;
|
|
677
691
|
}
|
|
678
692
|
}));
|
|
679
693
|
return _el$40;
|
|
680
694
|
}
|
|
681
695
|
}), _el$47, _co$0);
|
|
682
|
-
_$insert(_el$43, () => item.label);
|
|
696
|
+
_$insert(_el$43, () => item().label);
|
|
683
697
|
_$insert(_el$42, _$createComponent(Show, {
|
|
684
698
|
get when() {
|
|
685
|
-
return item.desc;
|
|
699
|
+
return item().desc;
|
|
686
700
|
},
|
|
687
701
|
children: description => (() => {
|
|
688
702
|
var _el$49 = _$getNextElement(_tmpl$16);
|
|
@@ -694,7 +708,7 @@ const openSearchPrompt = (resolver, options) => dialogCore.open(close => {
|
|
|
694
708
|
var _v$9 = optionId(index()),
|
|
695
709
|
_v$0 = activeIndex() === index(),
|
|
696
710
|
_v$1 = activeIndex() === index() ? "true" : undefined,
|
|
697
|
-
_v$10 = item.desc ? "true" : undefined;
|
|
711
|
+
_v$10 = item().desc ? "true" : undefined;
|
|
698
712
|
_v$9 !== _p$.e && _$setAttribute(_el$39, "id", _p$.e = _v$9);
|
|
699
713
|
_v$0 !== _p$.t && _$setAttribute(_el$39, "aria-selected", _p$.t = _v$0);
|
|
700
714
|
_v$1 !== _p$.a && _$setAttribute(_el$39, "data-active", _p$.a = _v$1);
|