@linloir/limr 0.1.0 → 1.0.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.
- package/README.en.md +151 -0
- package/README.md +80 -127
- package/benchmarks/results/layout.json +2593 -0
- package/benchmarks/results/markstream-code.json +1823 -0
- package/benchmarks/results/markstream-reading-repeat.json +1152 -0
- package/benchmarks/results/markstream.json +12486 -0
- package/benchmarks/results/streaming-browser.json +77 -0
- package/benchmarks/results/streaming.json +357 -0
- package/dist/base.css +93 -10
- package/dist/{chunk-7JF5HHVF.js → chunk-BG5QUQFR.js} +340 -72
- package/dist/chunk-BG5QUQFR.js.map +1 -0
- package/dist/chunk-BVHHOVMT.js +179 -0
- package/dist/chunk-BVHHOVMT.js.map +1 -0
- package/dist/chunk-FJZCQY2S.js +155 -0
- package/dist/chunk-FJZCQY2S.js.map +1 -0
- package/dist/chunk-FSI5OTBW.js +1 -0
- package/dist/{chunk-ETHLPBKQ.js → chunk-GU2RPESF.js} +238 -131
- package/dist/chunk-GU2RPESF.js.map +1 -0
- package/dist/{chunk-65V5OZ4G.js → chunk-SUAAUGAX.js} +1350 -511
- package/dist/chunk-SUAAUGAX.js.map +1 -0
- package/dist/core/index.d.ts +6 -3
- package/dist/core/index.js +15 -5
- package/dist/extensions/index.d.ts +30 -5
- package/dist/extensions/index.js +15 -3
- package/dist/index.d.ts +4 -4
- package/dist/index.js +31 -8
- package/dist/{engine-43u8B4yt.d.ts → knuth-plass-C8mgFm7W.d.ts} +67 -1
- package/dist/knuth-plass-layout-CHQCWKB5.js +449 -0
- package/dist/knuth-plass-layout-CHQCWKB5.js.map +1 -0
- package/dist/react/index.d.ts +7 -5
- package/dist/react/index.js +7 -3
- package/dist/styles.css +93 -10
- package/dist/{translations-BliabrPd.d.ts → translations-BXzSwLBJ.d.ts} +49 -4
- package/docs/README.md +13 -0
- package/docs/api.md +5 -158
- package/docs/assets/rendered.svg +9 -0
- package/docs/assets/source.svg +9 -0
- package/docs/en/README.md +28 -0
- package/docs/en/ai-skills.md +57 -0
- package/docs/en/annotations.md +86 -0
- package/docs/en/api.md +115 -0
- package/docs/en/architecture.md +41 -0
- package/docs/en/extensions.md +208 -0
- package/docs/en/getting-started.md +69 -0
- package/docs/en/integration.md +154 -0
- package/docs/en/modules.md +84 -0
- package/docs/en/performance.md +93 -0
- package/docs/en/plugin-packages.md +68 -0
- package/docs/en/styles.md +99 -0
- package/docs/en/syntax.md +455 -0
- package/docs/extensions.md +5 -306
- package/docs/implementation.md +5 -76
- package/docs/performance.md +5 -81
- package/docs/screenshots/blocks.png +0 -0
- package/docs/screenshots/dark.png +0 -0
- package/docs/screenshots/desktop.png +0 -0
- package/docs/screenshots/plugin-mobile.png +0 -0
- package/docs/styles.md +5 -100
- package/docs/visual-upgrade.md +5 -145
- package/docs/zh-CN/README.md +28 -0
- package/docs/zh-CN/ai-skills.md +57 -0
- package/docs/zh-CN/annotations.md +86 -0
- package/docs/zh-CN/api.md +115 -0
- package/docs/zh-CN/architecture.md +41 -0
- package/docs/zh-CN/extensions.md +210 -0
- package/docs/zh-CN/getting-started.md +69 -0
- package/docs/zh-CN/integration.md +154 -0
- package/docs/zh-CN/modules.md +84 -0
- package/docs/zh-CN/performance.md +93 -0
- package/docs/zh-CN/plugin-packages.md +68 -0
- package/docs/zh-CN/styles.md +99 -0
- package/docs/zh-CN/syntax.md +457 -0
- package/examples/notes.tsx +18 -11
- package/examples/packages/paper-theme/index.css +25 -0
- package/examples/packages/paper-theme/package.json +7 -0
- package/examples/packages/reading-tools/index.d.ts +2 -0
- package/examples/packages/reading-tools/index.js +25 -0
- package/examples/packages/reading-tools/package.json +16 -0
- package/package.json +17 -5
- package/skills/limr-plugin-development/SKILL.md +22 -0
- package/skills/limr-plugin-development/agents/openai.yaml +4 -0
- package/skills/limr-plugin-development/assets/plugin-template/README.md +30 -0
- package/skills/limr-plugin-development/assets/plugin-template/package.json +36 -0
- package/skills/limr-plugin-development/assets/plugin-template/src/index.tsx +50 -0
- package/skills/limr-plugin-development/assets/plugin-template/src/styles.css +9 -0
- package/skills/limr-plugin-development/assets/plugin-template/tsconfig.json +20 -0
- package/skills/limr-plugin-development/assets/plugin-template/tsup.config.ts +8 -0
- package/skills/limr-plugin-development/references/contract.md +51 -0
- package/skills/limr-use/SKILL.md +23 -0
- package/skills/limr-use/agents/openai.yaml +4 -0
- package/skills/limr-use/references/integration.md +49 -0
- package/skills/limr-use/references/syntax.md +20 -0
- package/dist/chunk-65V5OZ4G.js.map +0 -1
- package/dist/chunk-7JF5HHVF.js.map +0 -1
- package/dist/chunk-ETHLPBKQ.js.map +0 -1
- package/dist/chunk-V2OG4Y3F.js +0 -1
- /package/dist/{chunk-V2OG4Y3F.js.map → chunk-FSI5OTBW.js.map} +0 -0
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
highlightPalette,
|
|
10
10
|
highlightSelection,
|
|
11
11
|
scrollWithMotion,
|
|
12
|
-
sourceRangeStartRect,
|
|
13
12
|
useAppearance,
|
|
14
13
|
useDocument,
|
|
15
14
|
useDocumentSelector,
|
|
@@ -19,20 +18,26 @@ import {
|
|
|
19
18
|
useSelection,
|
|
20
19
|
useStoreSelector,
|
|
21
20
|
useTheme
|
|
22
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-SUAAUGAX.js";
|
|
23
22
|
import {
|
|
23
|
+
annotationReferenceRange,
|
|
24
24
|
defaultHighlight,
|
|
25
|
-
intersects,
|
|
26
25
|
mapText,
|
|
27
|
-
rangeOf,
|
|
28
26
|
resolveAnnotationAnchors,
|
|
29
27
|
sameRange,
|
|
30
|
-
selectionFromRange
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
selectionFromRange
|
|
29
|
+
} from "./chunk-BG5QUQFR.js";
|
|
30
|
+
import {
|
|
31
|
+
atomicSelectionText,
|
|
32
|
+
sourceRangeStartRect
|
|
33
|
+
} from "./chunk-BVHHOVMT.js";
|
|
34
|
+
import {
|
|
35
|
+
intersects,
|
|
36
|
+
rangeOf
|
|
37
|
+
} from "./chunk-FJZCQY2S.js";
|
|
33
38
|
|
|
34
39
|
// src/components/Reader.tsx
|
|
35
|
-
import { useEffect as useEffect7, useId as useId2, useRef as
|
|
40
|
+
import { useEffect as useEffect7, useId as useId2, useRef as useRef6, useState as useState6 } from "react";
|
|
36
41
|
|
|
37
42
|
// src/components/TableOfContents.tsx
|
|
38
43
|
import { useEffect as useEffect2, useRef } from "react";
|
|
@@ -43,7 +48,8 @@ function useOverflowFade(ref, runtime, contentSelector, beforeMeasure) {
|
|
|
43
48
|
useEffect(() => {
|
|
44
49
|
const host = ref.current;
|
|
45
50
|
if (!host) return;
|
|
46
|
-
let frame = 0
|
|
51
|
+
let frame = 0;
|
|
52
|
+
const transitions = /* @__PURE__ */ new Map();
|
|
47
53
|
const measure = () => {
|
|
48
54
|
frame = 0;
|
|
49
55
|
beforeMeasure?.();
|
|
@@ -68,8 +74,12 @@ function useOverflowFade(ref, runtime, contentSelector, beforeMeasure) {
|
|
|
68
74
|
};
|
|
69
75
|
set("--limr-fade-start", top - box.top);
|
|
70
76
|
set("--limr-fade-end", bottom - box.top);
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
const now = performance.now();
|
|
78
|
+
for (const [target, properties] of transitions) {
|
|
79
|
+
for (const [property, end2] of properties) if (end2 <= now || !host.contains(target)) properties.delete(property);
|
|
80
|
+
if (!properties.size) transitions.delete(target);
|
|
81
|
+
}
|
|
82
|
+
if (transitions.size) schedule();
|
|
73
83
|
};
|
|
74
84
|
const schedule = () => {
|
|
75
85
|
if (!frame) frame = requestAnimationFrame(measure);
|
|
@@ -78,12 +88,23 @@ function useOverflowFade(ref, runtime, contentSelector, beforeMeasure) {
|
|
|
78
88
|
cancelAnimationFrame(frame);
|
|
79
89
|
measure();
|
|
80
90
|
};
|
|
81
|
-
const start = () => {
|
|
82
|
-
|
|
91
|
+
const start = (event) => {
|
|
92
|
+
const target = event.target;
|
|
93
|
+
if (!(target instanceof Element) || !/^(top|bottom|left|right|height|max-height|min-height|width|max-width|transform|translate|grid-template-rows|padding.*|margin.*)$/.test(event.propertyName)) return;
|
|
94
|
+
const css = getComputedStyle(target), properties = css.transitionProperty.split(",").map((value) => value.trim());
|
|
95
|
+
const index = Math.max(0, properties.indexOf(event.propertyName));
|
|
96
|
+
const milliseconds = (value) => {
|
|
97
|
+
const list = value.split(",");
|
|
98
|
+
const item = list[index % list.length].trim();
|
|
99
|
+
return parseFloat(item) * (item.endsWith("ms") ? 1 : 1e3);
|
|
100
|
+
};
|
|
101
|
+
const pending = transitions.get(target) ?? /* @__PURE__ */ new Map();
|
|
102
|
+
pending.set(event.propertyName, performance.now() + milliseconds(css.transitionDuration) + milliseconds(css.transitionDelay) + 34);
|
|
103
|
+
transitions.set(target, pending);
|
|
83
104
|
schedule();
|
|
84
105
|
};
|
|
85
|
-
const end = () => {
|
|
86
|
-
|
|
106
|
+
const end = (event) => {
|
|
107
|
+
if (event.target instanceof Element) transitions.get(event.target)?.delete(event.propertyName);
|
|
87
108
|
schedule();
|
|
88
109
|
};
|
|
89
110
|
host.addEventListener("transitionrun", start);
|
|
@@ -95,7 +116,10 @@ function useOverflowFade(ref, runtime, contentSelector, beforeMeasure) {
|
|
|
95
116
|
if (records.some((record) => {
|
|
96
117
|
const element = record.target instanceof Element ? record.target : record.target.parentElement;
|
|
97
118
|
return element && element !== host && !element.closest(".limr-highlight-layer,.limr-paint-layer");
|
|
98
|
-
}))
|
|
119
|
+
})) {
|
|
120
|
+
if (beforeMeasure) refresh();
|
|
121
|
+
else schedule();
|
|
122
|
+
}
|
|
99
123
|
});
|
|
100
124
|
mutation.observe(host, { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: ["style", "class"] });
|
|
101
125
|
const unsubscribe = [runtime.layout.subscribe(schedule), runtime.viewport.subscribe(refresh), runtime.documentOffset.subscribe(schedule), runtime.configuration.subscribe(schedule)];
|
|
@@ -187,7 +211,7 @@ function TableOfContents({ title, maxDepth = 6, followActive = true, topInset =
|
|
|
187
211
|
}
|
|
188
212
|
|
|
189
213
|
// src/components/AnnotationSidebar.tsx
|
|
190
|
-
import { useCallback, useEffect as useEffect4, useMemo, useRef as useRef2, useState as useState2, useSyncExternalStore } from "react";
|
|
214
|
+
import { memo, useCallback, useEffect as useEffect4, useMemo, useRef as useRef2, useState as useState2, useSyncExternalStore } from "react";
|
|
191
215
|
import { flushSync } from "react-dom";
|
|
192
216
|
|
|
193
217
|
// src/components/AnnotationContent.tsx
|
|
@@ -236,39 +260,29 @@ function AnnotationGroup({ occurrence, presentation, expanded = true, enabled =
|
|
|
236
260
|
|
|
237
261
|
// src/components/annotation-reference.ts
|
|
238
262
|
function annotationReferenceText(document2, occurrence) {
|
|
239
|
-
const
|
|
263
|
+
const referenceRange = annotationReferenceRange(document2, occurrence);
|
|
264
|
+
const blocks = document2.blocks.filter((block) => intersects(block.range, referenceRange));
|
|
240
265
|
let reference = "";
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
node.
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const mapping = mapText(document2.source, node.value, range);
|
|
256
|
-
reference += node.value.split("").filter((_, index) => mapping.ends[index] > occurrence.range.start && mapping.starts[index] < occurrence.range.end).join("");
|
|
257
|
-
} else if (node.type === "image") reference += String(node.alt ?? "");
|
|
258
|
-
else node.children?.forEach(visit);
|
|
259
|
-
};
|
|
260
|
-
blocks.forEach((block, index) => {
|
|
261
|
-
if (index) reference += " ";
|
|
262
|
-
visit(block.node);
|
|
263
|
-
});
|
|
264
|
-
reference ||= occurrence.quote ?? document2.source.slice(occurrence.range.start, occurrence.range.end);
|
|
265
|
-
}
|
|
266
|
+
const visit = (node) => {
|
|
267
|
+
const range = rangeOf(node);
|
|
268
|
+
if (!intersects(range, referenceRange) || ["annotationReference", "footnoteReference", "annotationDefinition", "definition"].includes(node.type)) return;
|
|
269
|
+
if (node.value !== void 0) {
|
|
270
|
+
const mapping = mapText(document2.source, node.value, range);
|
|
271
|
+
reference += node.value.split("").filter((_, index) => mapping.ends[index] > referenceRange.start && mapping.starts[index] < referenceRange.end).join("");
|
|
272
|
+
} else if (node.type === "image") reference += String(node.alt ?? "");
|
|
273
|
+
else node.children?.forEach(visit);
|
|
274
|
+
};
|
|
275
|
+
blocks.forEach((block, index) => {
|
|
276
|
+
if (index) reference += " ";
|
|
277
|
+
visit(block.node);
|
|
278
|
+
});
|
|
279
|
+
if (!occurrence.embedded) reference ||= occurrence.quote ?? document2.source.slice(referenceRange.start, referenceRange.end);
|
|
266
280
|
return reference.replace(/\s+/g, " ").trim();
|
|
267
281
|
}
|
|
268
282
|
|
|
269
283
|
// src/components/AnnotationSidebar.tsx
|
|
270
284
|
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
271
|
-
var alignSidebar = (items, { activeId, gap, height }) => {
|
|
285
|
+
var alignSidebar = (items, { activeId, gap, height, prioritizeActive }) => {
|
|
272
286
|
const positions = items.map((item) => item.anchorTop);
|
|
273
287
|
const activeIndex = items.findIndex((item) => item.id === activeId);
|
|
274
288
|
const pivot = activeIndex < 0 ? 0 : activeIndex;
|
|
@@ -277,11 +291,25 @@ var alignSidebar = (items, { activeId, gap, height }) => {
|
|
|
277
291
|
for (let i = pivot - 1; i >= 0; i--) positions[i] = Math.min(items[i].anchorTop, positions[i + 1] - items[i].height - gap);
|
|
278
292
|
for (let i = pivot + 1; i < items.length; i++) positions[i] = Math.max(items[i].anchorTop, positions[i - 1] + items[i - 1].height + gap);
|
|
279
293
|
if (height !== void 0 && items.reduce((sum, item) => sum + item.height, gap * (items.length - 1)) <= height) {
|
|
280
|
-
|
|
281
|
-
for (let i =
|
|
294
|
+
const start = prioritizeActive && activeIndex >= 0 ? pivot + 1 : 0;
|
|
295
|
+
for (let i = items.length - 1; i >= start; i--) positions[i] = Math.min(positions[i], (i === items.length - 1 ? height : positions[i + 1] - gap) - items[i].height);
|
|
296
|
+
if (start || activeIndex < 0 || positions[0] >= 0) for (let i = start; i < items.length; i++) positions[i] = Math.max(positions[i], i === 0 ? 0 : positions[i - 1] + items[i - 1].height + gap);
|
|
282
297
|
}
|
|
283
298
|
return new Map(items.map((item, index) => [item.id, positions[index]]));
|
|
284
299
|
};
|
|
300
|
+
var SidebarCardBody = memo(function SidebarCardBody2({ occurrence, annotation, annotations, reference, active, title, toggleLabel, Preview, activate }) {
|
|
301
|
+
return /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
302
|
+
reference && /* @__PURE__ */ jsx3("div", { className: "limr-sidebar-reference", title: reference, children: /* @__PURE__ */ jsx3("p", { className: "limr-sidebar-excerpt", children: reference }) }),
|
|
303
|
+
/* @__PURE__ */ jsxs3("div", { className: "limr-sidebar-card-body", children: [
|
|
304
|
+
/* @__PURE__ */ jsxs3("button", { type: "button", className: "limr-sidebar-card-heading", onClick: () => activate(occurrence.id), "aria-expanded": active, children: [
|
|
305
|
+
title,
|
|
306
|
+
/* @__PURE__ */ jsx3("span", { className: "limr-sidebar-toggle", children: /* @__PURE__ */ jsx3("span", { className: "limr-button-label limr-underline", children: toggleLabel }, String(active)) })
|
|
307
|
+
] }),
|
|
308
|
+
Preview && annotation && /* @__PURE__ */ jsx3(Collapse, { open: !active, keepMounted: true, className: "limr-sidebar-preview-panel", children: /* @__PURE__ */ jsx3("div", { className: "limr-sidebar-preview", children: /* @__PURE__ */ jsx3(Preview, { annotation, data: annotation.data, occurrence, annotations, referenceText: reference }) }) }),
|
|
309
|
+
/* @__PURE__ */ jsx3(Collapse, { open: active, className: "limr-sidebar-expansion", children: /* @__PURE__ */ jsx3("div", { className: "limr-sidebar-card-content", children: /* @__PURE__ */ jsx3(AnnotationGroup, { occurrence, presentation: "sidebar", enabled: active }) }) })
|
|
310
|
+
] })
|
|
311
|
+
] });
|
|
312
|
+
});
|
|
285
313
|
function AnnotationSidebar({ sync = "document", gap = 14, positioner = alignSidebar, renderCard, className, style }) {
|
|
286
314
|
const runtime = useRuntime();
|
|
287
315
|
const document2 = useDocument();
|
|
@@ -298,12 +326,16 @@ function AnnotationSidebar({ sync = "document", gap = 14, positioner = alignSide
|
|
|
298
326
|
const rail = useRef2(null);
|
|
299
327
|
const cards = useRef2(/* @__PURE__ */ new Map());
|
|
300
328
|
const [sizes, setSizes] = useState2(/* @__PURE__ */ new Map());
|
|
329
|
+
const [minimumSizes, setMinimumSizes] = useState2(/* @__PURE__ */ new Map());
|
|
301
330
|
const [anchorTops, setAnchorTops] = useState2(/* @__PURE__ */ new Map());
|
|
302
331
|
const [railHeight, setRailHeight] = useState2(0);
|
|
303
|
-
const measured = useRef2({ sizes: /* @__PURE__ */ new Map(), height: 0 });
|
|
332
|
+
const measured = useRef2({ sizes: /* @__PURE__ */ new Map(), minimum: /* @__PURE__ */ new Map(), height: 0 });
|
|
304
333
|
const resizeFrame = useRef2(0);
|
|
305
334
|
const [viewportTop, setViewportTop] = useState2(0);
|
|
335
|
+
const [viewportHeight, setViewportHeight] = useState2(0);
|
|
306
336
|
const [cardLock, setCardLock] = useState2(null);
|
|
337
|
+
const lastFocus = useRef2(null);
|
|
338
|
+
const [release, setRelease] = useState2(null);
|
|
307
339
|
const occurrences = useMemo(() => {
|
|
308
340
|
const values = [...decorations.occurrences].sort((a, b) => a.range.start - b.range.start || a.range.end - b.range.end || a.id.localeCompare(b.id));
|
|
309
341
|
if (sync === "none") {
|
|
@@ -318,13 +350,20 @@ function AnnotationSidebar({ sync = "document", gap = 14, positioner = alignSide
|
|
|
318
350
|
})])), [decorations, occurrences]);
|
|
319
351
|
const measure = useCallback((beforePaint = false) => {
|
|
320
352
|
const next = /* @__PURE__ */ new Map();
|
|
321
|
-
|
|
353
|
+
const minimum = /* @__PURE__ */ new Map();
|
|
354
|
+
cards.current.forEach((element2, id) => {
|
|
355
|
+
const height2 = element2.getBoundingClientRect().height;
|
|
356
|
+
next.set(id, height2);
|
|
357
|
+
const panels = [...element2.querySelectorAll(":scope > .limr-sidebar-card-body > .limr-collapse")];
|
|
358
|
+
minimum.set(id, height2 - panels.reduce((sum, panel) => sum + panel.getBoundingClientRect().height, 0));
|
|
359
|
+
});
|
|
322
360
|
const height = rail.current?.getBoundingClientRect().height ?? 0;
|
|
323
361
|
const old = measured.current;
|
|
324
|
-
if (old.height === height && old.sizes.size === next.size && [...next].every(([id, size2]) => size2 === old.sizes.get(id))) return;
|
|
325
|
-
measured.current = { sizes: next, height };
|
|
362
|
+
if (old.height === height && old.sizes.size === next.size && [...next].every(([id, size2]) => size2 === old.sizes.get(id) && Math.abs(minimum.get(id) - (old.minimum.get(id) ?? 0)) < 0.5)) return;
|
|
363
|
+
measured.current = { sizes: next, minimum, height };
|
|
326
364
|
const update = () => {
|
|
327
365
|
setSizes(next);
|
|
366
|
+
setMinimumSizes(minimum);
|
|
328
367
|
setRailHeight(height);
|
|
329
368
|
};
|
|
330
369
|
if (!beforePaint) {
|
|
@@ -357,13 +396,19 @@ function AnnotationSidebar({ sync = "document", gap = 14, positioner = alignSide
|
|
|
357
396
|
for (const occurrence of occurrences) {
|
|
358
397
|
const marker = runtime.anchors.get(occurrence.id);
|
|
359
398
|
if (!marker || marker.closest("[inert]")) continue;
|
|
360
|
-
const rect =
|
|
399
|
+
const rect = sourceRangeStartRect(runtime, decorations.referenceRanges.get(occurrence.id) ?? occurrence.range);
|
|
361
400
|
values.set(occurrence.id, (rect || marker.getBoundingClientRect()).top - top - displacement2);
|
|
362
401
|
}
|
|
363
402
|
setAnchorTops((old) => old.size === values.size && [...values].every(([id, value]) => Math.abs(value - (old.get(id) ?? -Infinity)) < 0.5) ? old : values);
|
|
364
|
-
}, [runtime, occurrences, layout, sync]);
|
|
403
|
+
}, [runtime, decorations.referenceRanges, occurrences, layout, sync]);
|
|
365
404
|
useIsomorphicLayoutEffect(() => {
|
|
366
|
-
if (sync
|
|
405
|
+
if (sync !== "document" || !rail.current) return;
|
|
406
|
+
const root = runtime.configuration.get().scrollRoot;
|
|
407
|
+
const bounds = rail.current.getBoundingClientRect();
|
|
408
|
+
const top = Math.max(bounds.top, runtime.getViewportTop() + (root?.clientTop ?? 0), window.visualViewport?.offsetTop ?? 0);
|
|
409
|
+
const bottom = Math.min(bounds.bottom, root ? runtime.getViewportTop() + root.clientTop + root.clientHeight : Infinity, (window.visualViewport?.offsetTop ?? 0) + (window.visualViewport?.height ?? innerHeight));
|
|
410
|
+
setViewportTop(Math.max(0, top - bounds.top + (runtime.configuration.get().scrollOffset ?? 24)));
|
|
411
|
+
setViewportHeight(Math.max(0, bottom - top));
|
|
367
412
|
}, [runtime, layout, viewport, sync]);
|
|
368
413
|
useEffect4(() => {
|
|
369
414
|
const dismiss = (event) => {
|
|
@@ -395,35 +440,79 @@ function AnnotationSidebar({ sync = "document", gap = 14, positioner = alignSide
|
|
|
395
440
|
if (sync !== "none") items.sort((a, b) => (anchorTops.get(a.id) ?? 0) - (anchorTops.get(b.id) ?? 0));
|
|
396
441
|
const readingId = items.find((item) => (anchorTops.get(item.id) ?? 0) >= viewportTop)?.id ?? items.at(-1)?.id ?? null;
|
|
397
442
|
const alignmentId = hovered ?? active ?? readingId;
|
|
443
|
+
const focusedId = hovered ?? active;
|
|
398
444
|
const source = hovered ? hoverSource : active ? activeSource : null;
|
|
399
445
|
const locked = source === "sidebar" && cardLock?.id === alignmentId ? cardLock : null;
|
|
446
|
+
const lockedTop = locked?.top;
|
|
447
|
+
useIsomorphicLayoutEffect(() => {
|
|
448
|
+
const previous = lastFocus.current;
|
|
449
|
+
lastFocus.current = focusedId ? { id: focusedId, top: lockedTop } : null;
|
|
450
|
+
if (focusedId || sync !== "document" || !rail.current || matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
451
|
+
setRelease(null);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
if (!previous) return;
|
|
455
|
+
const style2 = getComputedStyle(rail.current), value = style2.getPropertyValue("--limr-motion-slow").trim();
|
|
456
|
+
const milliseconds = parseFloat(value) * (value.endsWith("ms") ? 1 : 1e3);
|
|
457
|
+
const duration = Number.isFinite(milliseconds) ? Math.max(0, milliseconds) : 460;
|
|
458
|
+
if (!duration) {
|
|
459
|
+
setRelease(null);
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
const motion = rail.current.animate([{}, {}], { duration, easing: style2.getPropertyValue("--limr-ease").trim() || "ease-out" });
|
|
463
|
+
setRelease({ ...previous, progress: 0 });
|
|
464
|
+
let frame = 0;
|
|
465
|
+
const step = () => {
|
|
466
|
+
if (motion.playState === "finished") {
|
|
467
|
+
setRelease(null);
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
setRelease({ ...previous, progress: motion.effect?.getComputedTiming().progress ?? 0 });
|
|
471
|
+
frame = requestAnimationFrame(step);
|
|
472
|
+
};
|
|
473
|
+
frame = requestAnimationFrame(step);
|
|
474
|
+
return () => {
|
|
475
|
+
cancelAnimationFrame(frame);
|
|
476
|
+
motion.cancel();
|
|
477
|
+
};
|
|
478
|
+
}, [focusedId, lockedTop, sync]);
|
|
400
479
|
const sidebarItems = items.map((item) => ({ id: item.id, anchorTop: anchorTops.get(item.id) ?? 0, height: sizes.get(item.id) ?? 80 }));
|
|
401
|
-
const positionContext = { activeId: alignmentId, gap, height: sync === "document" && railHeight > 0 ? railHeight : void 0 };
|
|
402
|
-
const positions = positioner(locked ? sidebarItems.map((item) => item.id === locked.id ? { ...item, anchorTop: locked.top } : item) : sidebarItems, positionContext);
|
|
480
|
+
const positionContext = { activeId: alignmentId, gap, height: sync === "document" && railHeight > 0 ? railHeight : void 0, prioritizeActive: !!(hovered || active) };
|
|
481
|
+
const positions = new Map(positioner(locked ? sidebarItems.map((item) => item.id === locked.id ? { ...item, anchorTop: locked.top } : item) : sidebarItems, positionContext));
|
|
482
|
+
const releasing = !focusedId && release;
|
|
483
|
+
if (releasing) {
|
|
484
|
+
const origin = positioner(sidebarItems.map((item) => item.id === releasing.id && releasing.top !== void 0 ? { ...item, anchorTop: releasing.top } : item), { ...positionContext, activeId: releasing.id, prioritizeActive: true });
|
|
485
|
+
for (const [id, top] of positions) positions.set(id, top + ((origin.get(id) ?? top) - top) * (1 - releasing.progress));
|
|
486
|
+
}
|
|
403
487
|
const lockedAnchor = locked ? anchorTops.get(locked.id) ?? locked.top : 0;
|
|
404
|
-
const
|
|
405
|
-
const lockFits = locked && (positions.get(locked.id) ?? locked.top) >= locked.top - 0.5;
|
|
406
|
-
const displacement = sync === "document" && locked && anchorFits && lockFits ? (positions.get(locked.id) ?? locked.top) - lockedAnchor : 0;
|
|
488
|
+
const displacement = sync === "document" && locked ? (positions.get(locked.id) ?? locked.top) - lockedAnchor : 0;
|
|
407
489
|
useIsomorphicLayoutEffect(() => {
|
|
408
490
|
if (sync === "document") runtime.documentOffset.set(displacement);
|
|
409
491
|
}, [runtime, displacement, sync]);
|
|
410
|
-
const lockCard = (id) => {
|
|
492
|
+
const lockCard = useCallback((id) => {
|
|
411
493
|
if (locked?.id === id) return;
|
|
412
494
|
const card = cards.current.get(id);
|
|
413
495
|
if (card && rail.current) {
|
|
414
496
|
const origin = rail.current.getBoundingClientRect().top;
|
|
415
497
|
const top = card.getBoundingClientRect().top - origin;
|
|
416
|
-
|
|
417
|
-
const boundedTop = positioner(sidebarItems, { ...positionContext, activeId: id }).get(id) ?? anchorTop;
|
|
418
|
-
setCardLock({ id, top: boundedTop < anchorTop - 0.5 ? anchorTop : top });
|
|
498
|
+
setCardLock({ id, top });
|
|
419
499
|
}
|
|
420
|
-
};
|
|
421
|
-
|
|
500
|
+
}, [locked?.id]);
|
|
501
|
+
const activateCard = useCallback((id) => {
|
|
502
|
+
const state = runtime.interaction.get();
|
|
503
|
+
const hovering = state.hovered === id || cards.current.get(id)?.matches(":hover");
|
|
504
|
+
lockCard(id);
|
|
505
|
+
runtime.activate(state.active === id ? null : id, "sidebar");
|
|
506
|
+
if (hovering) runtime.hover(id, "sidebar");
|
|
507
|
+
}, [runtime, lockCard]);
|
|
508
|
+
return /* @__PURE__ */ jsx3("aside", { ref: rail, "aria-label": locale.annotations, className: `limr-sidebar ${className ?? ""}`, "data-sync": sync, "data-releasing": releasing ? "" : void 0, "data-limr-theme": theme, "data-limr-appearance": appearance, style, children: items.map((occurrence) => {
|
|
422
509
|
const annotation = decorations.annotations.get(occurrence.annotationIds[0]);
|
|
423
510
|
const definition = runtime.annotationTypes.get(annotation?.type ?? "citation");
|
|
424
511
|
const isActive = active === occurrence.id;
|
|
425
512
|
const palette = definition?.sidebar;
|
|
426
513
|
const Preview = palette?.Preview;
|
|
514
|
+
const available = source === "marker" && alignmentId === occurrence.id && !isActive ? Math.max(minimumSizes.get(occurrence.id) ?? 80, railHeight - (anchorTops.get(occurrence.id) ?? 0)) : railHeight;
|
|
515
|
+
const cardHeight = isActive ? Math.min(available, Math.max(minimumSizes.get(occurrence.id) ?? 80, viewportHeight || railHeight)) : available;
|
|
427
516
|
const cardStyle = {
|
|
428
517
|
"--limr-annotation-color": palette?.color ?? definition?.color,
|
|
429
518
|
"--limr-annotation-border": palette?.borderColor,
|
|
@@ -436,16 +525,11 @@ function AnnotationSidebar({ sync = "document", gap = 14, positioner = alignSide
|
|
|
436
525
|
position: "absolute",
|
|
437
526
|
top: positions.get(occurrence.id) ?? 0,
|
|
438
527
|
insetInline: 0,
|
|
439
|
-
"--limr-sidebar-card-height": `${
|
|
528
|
+
"--limr-sidebar-card-height": `${cardHeight}px`
|
|
440
529
|
} : { marginBlockEnd: gap }
|
|
441
530
|
};
|
|
442
531
|
const collapseOnContentClick = occurrence.annotationIds.every((id) => runtime.annotationTypes.get(decorations.annotations.get(id)?.type ?? "")?.sidebar?.collapseOnContentClick);
|
|
443
|
-
const activate = () =>
|
|
444
|
-
const hovering = runtime.interaction.get().hovered === occurrence.id || cards.current.get(occurrence.id)?.matches(":hover");
|
|
445
|
-
lockCard(occurrence.id);
|
|
446
|
-
runtime.activate(isActive ? null : occurrence.id, "sidebar");
|
|
447
|
-
if (hovering) runtime.hover(occurrence.id, "sidebar");
|
|
448
|
-
};
|
|
532
|
+
const activate = () => activateCard(occurrence.id);
|
|
449
533
|
return /* @__PURE__ */ jsx3(
|
|
450
534
|
"div",
|
|
451
535
|
{
|
|
@@ -475,17 +559,7 @@ function AnnotationSidebar({ sync = "document", gap = 14, positioner = alignSide
|
|
|
475
559
|
onClick: (event) => {
|
|
476
560
|
if ((!isActive || collapseOnContentClick) && !renderCard && !event.defaultPrevented && !event.target.closest('button,a,input,select,textarea,summary,[role="button"],[role="link"],[contenteditable="true"],[data-limr-card-interactive]') && window.getSelection()?.isCollapsed !== false) activate();
|
|
477
561
|
},
|
|
478
|
-
children: renderCard ? renderCard({ occurrence, active: isActive, activate }) : /* @__PURE__ */
|
|
479
|
-
references.get(occurrence.id) && /* @__PURE__ */ jsx3("div", { className: "limr-sidebar-reference", title: references.get(occurrence.id), children: /* @__PURE__ */ jsx3("p", { className: "limr-sidebar-excerpt", children: references.get(occurrence.id) }) }),
|
|
480
|
-
/* @__PURE__ */ jsxs3("div", { className: "limr-sidebar-card-body", children: [
|
|
481
|
-
/* @__PURE__ */ jsxs3("button", { type: "button", className: "limr-sidebar-card-heading", onClick: activate, "aria-expanded": isActive, children: [
|
|
482
|
-
definition?.label ?? annotation?.type ?? "Reference",
|
|
483
|
-
/* @__PURE__ */ jsx3("span", { className: "limr-sidebar-toggle", children: /* @__PURE__ */ jsx3("span", { className: "limr-button-label limr-underline", children: isActive ? locale.collapse : locale.expand }, String(isActive)) })
|
|
484
|
-
] }),
|
|
485
|
-
Preview && annotation && /* @__PURE__ */ jsx3(Collapse, { open: !isActive, keepMounted: true, className: "limr-sidebar-preview-panel", children: /* @__PURE__ */ jsx3("div", { className: "limr-sidebar-preview", children: /* @__PURE__ */ jsx3(Preview, { annotation, data: annotation.data, occurrence, annotations: annotationGroups.get(occurrence.id), referenceText: references.get(occurrence.id) ?? "" }) }) }),
|
|
486
|
-
/* @__PURE__ */ jsx3(Collapse, { open: isActive, className: "limr-sidebar-expansion", children: /* @__PURE__ */ jsx3("div", { className: "limr-sidebar-card-content", children: /* @__PURE__ */ jsx3(AnnotationGroup, { occurrence, presentation: "sidebar", enabled: isActive }) }) })
|
|
487
|
-
] })
|
|
488
|
-
] })
|
|
562
|
+
children: renderCard ? renderCard({ occurrence, active: isActive, activate }) : /* @__PURE__ */ jsx3(SidebarCardBody, { occurrence, annotation, annotations: annotationGroups.get(occurrence.id), reference: references.get(occurrence.id) ?? "", active: isActive, title: definition?.label ?? annotation?.type ?? "Reference", toggleLabel: isActive ? locale.collapse : locale.expand, Preview, activate: activateCard })
|
|
489
563
|
},
|
|
490
564
|
occurrence.id
|
|
491
565
|
);
|
|
@@ -804,9 +878,33 @@ function AnnotationOverlay({ disabled = false, portalRoot, previewWidth = 340, m
|
|
|
804
878
|
}
|
|
805
879
|
|
|
806
880
|
// src/components/SelectionToolbar.tsx
|
|
807
|
-
import { useEffect as useEffect6, useId, useMemo as useMemo2, useRef as
|
|
881
|
+
import { useEffect as useEffect6, useId, useMemo as useMemo2, useRef as useRef5, useState as useState5 } from "react";
|
|
808
882
|
import { autoUpdate as autoUpdate2, FloatingPortal as FloatingPortal2, offset as offset2, shift as shift2, useFloating as useFloating2 } from "@floating-ui/react";
|
|
809
|
-
|
|
883
|
+
|
|
884
|
+
// src/components/ToolFaces.tsx
|
|
885
|
+
import { useRef as useRef4, useState as useState4 } from "react";
|
|
886
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
887
|
+
function ToolFaces({ state, children }) {
|
|
888
|
+
const ref = useRef4(null);
|
|
889
|
+
const [width, setWidth] = useState4();
|
|
890
|
+
useIsomorphicLayoutEffect(() => {
|
|
891
|
+
const element = ref.current;
|
|
892
|
+
if (!element) return;
|
|
893
|
+
const faces = [...element.querySelectorAll(".limr-tool-face")];
|
|
894
|
+
const measure = () => {
|
|
895
|
+
const face = faces.find((face2) => face2.hasAttribute("data-visible"));
|
|
896
|
+
if (face) setWidth(face.offsetWidth);
|
|
897
|
+
};
|
|
898
|
+
measure();
|
|
899
|
+
const observer = new ResizeObserver(measure);
|
|
900
|
+
faces.forEach((face) => observer.observe(face));
|
|
901
|
+
return () => observer.disconnect();
|
|
902
|
+
}, [state]);
|
|
903
|
+
return /* @__PURE__ */ jsx5("span", { className: "limr-tool-faces", ref, style: { width }, children });
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// src/components/SelectionToolbar.tsx
|
|
907
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
810
908
|
var defaults = [highlightPalette(), highlightSelection(), copyText(), deleteAnnotations()];
|
|
811
909
|
function SelectionToolbar({ actions, portalRoot, maxPanelHeight = 260, className = "", style, render }) {
|
|
812
910
|
const runtime = useRuntime();
|
|
@@ -825,15 +923,15 @@ function SelectionToolbar({ actions, portalRoot, maxPanelHeight = 260, className
|
|
|
825
923
|
const order = (action) => action.placement === "start" ? -1 : action.placement === "end" ? 1 : 0;
|
|
826
924
|
return [...map.values()].sort((a, b) => order(a) - order(b));
|
|
827
925
|
}, [config.extensions, actions]);
|
|
828
|
-
const [busy, setBusy] =
|
|
829
|
-
const busyRef =
|
|
830
|
-
const [error, setError] =
|
|
831
|
-
const [expanded, setExpanded] =
|
|
832
|
-
const [success, setSuccess] =
|
|
833
|
-
const [deleteHintOpen, setDeleteHintOpen] =
|
|
834
|
-
const [preferredHighlight, setPreferredHighlight] =
|
|
835
|
-
const closeTimer =
|
|
836
|
-
const successTimer =
|
|
926
|
+
const [busy, setBusy] = useState5(null);
|
|
927
|
+
const busyRef = useRef5(false);
|
|
928
|
+
const [error, setError] = useState5(null);
|
|
929
|
+
const [expanded, setExpanded] = useState5(null);
|
|
930
|
+
const [success, setSuccess] = useState5(null);
|
|
931
|
+
const [deleteHintOpen, setDeleteHintOpen] = useState5(false);
|
|
932
|
+
const [preferredHighlight, setPreferredHighlight] = useState5(defaultHighlight);
|
|
933
|
+
const closeTimer = useRef5(void 0);
|
|
934
|
+
const successTimer = useRef5(void 0);
|
|
837
935
|
const panelId = useId();
|
|
838
936
|
const documentSnapshot = runtime.document.getSnapshot();
|
|
839
937
|
const effectiveSelection = useMemo2(() => {
|
|
@@ -841,9 +939,15 @@ function SelectionToolbar({ actions, portalRoot, maxPanelHeight = 260, className
|
|
|
841
939
|
if (interaction.editing && active?.annotationIds.includes(interaction.editing)) return null;
|
|
842
940
|
const range = paint?.range ?? (hiddenActive ? active?.range : void 0);
|
|
843
941
|
return range ? selectionFromRange(documentSnapshot, range, {
|
|
844
|
-
text: paint ? runtime.highlightReference?.text ?? annotationReferenceText(documentSnapshot, { id: paint.id, annotationIds: [], range }) : annotationReferenceText(documentSnapshot, active)
|
|
942
|
+
text: atomicSelectionText(runtime, [range]) ?? (paint ? runtime.highlightReference?.text ?? annotationReferenceText(documentSnapshot, { id: paint.id, annotationIds: [], range }) : annotationReferenceText(documentSnapshot, active))
|
|
845
943
|
}) : null;
|
|
846
944
|
}, [documentSnapshot, paint, active, selection, interaction.editing, hiddenActive]);
|
|
945
|
+
const blockReference = useMemo2(() => {
|
|
946
|
+
const ranges = effectiveSelection?.raw.ranges;
|
|
947
|
+
if (ranges?.length !== 1) return null;
|
|
948
|
+
const { start, end } = ranges[0];
|
|
949
|
+
return runtime.content?.querySelector(`[data-limr-atomic="block"][data-source-start="${start}"][data-source-end="${end}"]`) ?? null;
|
|
950
|
+
}, [runtime, effectiveSelection]);
|
|
847
951
|
const annotations = useMemo2(() => effectiveSelection ? [...decorations.annotations.values()].filter((annotation) => resolveAnnotationAnchors(annotation, documentSnapshot).some((anchor) => [...effectiveSelection.raw.ranges, ...effectiveSelection.syntax.ranges].some((range) => sameRange(anchor.range, range)))) : [], [decorations, effectiveSelection, documentSnapshot]);
|
|
848
952
|
const highlight = paint ? decorations.annotations.get(paint.annotationId) : annotations.find((annotation) => annotation.type === "highlight");
|
|
849
953
|
const highlightAppearance = highlight?.data ?? preferredHighlight;
|
|
@@ -858,7 +962,8 @@ function SelectionToolbar({ actions, portalRoot, maxPanelHeight = 260, className
|
|
|
858
962
|
signal: runtime.signal,
|
|
859
963
|
clearSelection: () => runtime.clearSelection()
|
|
860
964
|
} : null;
|
|
861
|
-
const
|
|
965
|
+
const readOnly = highlight?.data?.readOnly === true;
|
|
966
|
+
const available = context ? readOnly ? [defaults[2]] : combined.filter((action) => action.visible?.(context) ?? true) : [];
|
|
862
967
|
const expandedAction = available.find((action) => action.id === expanded && action.renderExpanded);
|
|
863
968
|
const hasPanels = available.some((action) => action.renderExpanded);
|
|
864
969
|
const open = !!context && effectiveSelection.settled && available.length > 0;
|
|
@@ -866,16 +971,16 @@ function SelectionToolbar({ actions, portalRoot, maxPanelHeight = 260, className
|
|
|
866
971
|
open,
|
|
867
972
|
strategy: "fixed",
|
|
868
973
|
placement: "bottom",
|
|
869
|
-
middleware: [offset2(10), { name: "toolbarSpace", options: { hasPanels, maxPanelHeight }, fn({ rects, placement: placement2, elements }) {
|
|
974
|
+
middleware: [offset2(10), { name: "toolbarSpace", options: { hasPanels, maxPanelHeight, blockReference }, fn({ rects, placement: placement2, elements }) {
|
|
870
975
|
const viewport = window.visualViewport;
|
|
871
976
|
const root = runtime.configuration.get().scrollRoot?.getBoundingClientRect();
|
|
872
977
|
const top = Math.max(viewport?.offsetTop ?? 0, root?.top ?? 0);
|
|
873
978
|
const bottom = Math.min((viewport?.offsetTop ?? 0) + (viewport?.height ?? window.innerHeight), root?.bottom ?? Infinity);
|
|
874
979
|
const above = Math.max(0, rects.reference.y - top - 20), below = Math.max(0, bottom - rects.reference.y - rects.reference.height - 20);
|
|
875
|
-
const row = (elements.floating.querySelector(".limr-toolbar-actions")?.getBoundingClientRect().height ?? 34) + (elements.floating.querySelector(".limr-toolbar-delete-hint")?.getBoundingClientRect().height ?? 0);
|
|
980
|
+
const row = (elements.floating.querySelector(".limr-toolbar-actions")?.getBoundingClientRect().height ?? 34) + (elements.floating.querySelector(".limr-toolbar-delete-hint,.limr-toolbar-readonly-hint")?.getBoundingClientRect().height ?? 0);
|
|
876
981
|
const limit = Math.max(0, parseFloat(getComputedStyle(elements.floating).getPropertyValue("--limr-toolbar-panel-max-height")) || maxPanelHeight);
|
|
877
982
|
const needed = row + (hasPanels ? limit : 0) + 24;
|
|
878
|
-
const next = below >= needed || below >= above ? "bottom" : "top";
|
|
983
|
+
const next = blockReference || below >= needed || below >= above ? "bottom" : "top";
|
|
879
984
|
const room = next === "bottom" ? below : above;
|
|
880
985
|
elements.floating.style.setProperty("--limr-toolbar-panel-height", `${Math.max(0, Math.min(limit, room - row - 24))}px`);
|
|
881
986
|
elements.floating.style.setProperty("--limr-toolbar-available-height", `${room}px`);
|
|
@@ -884,9 +989,9 @@ function SelectionToolbar({ actions, portalRoot, maxPanelHeight = 260, className
|
|
|
884
989
|
whileElementsMounted: autoUpdate2
|
|
885
990
|
});
|
|
886
991
|
useEffect6(() => {
|
|
887
|
-
const reference = selection ? runtime.selectionRange : paint ? runtime.highlightReference : active ? runtime.anchors.get(active.id) : null;
|
|
888
|
-
if (reference) refs.setPositionReference({ getBoundingClientRect: () => reference.getBoundingClientRect(), getClientRects: () => reference.getClientRects(), contextElement: runtime.content ?? void 0 });
|
|
889
|
-
}, [runtime, refs, selection, paint, active]);
|
|
992
|
+
const reference = blockReference ?? (selection ? runtime.selectionRange : paint ? runtime.highlightReference : active ? runtime.anchors.get(active.id) : null);
|
|
993
|
+
if (reference) refs.setPositionReference({ getBoundingClientRect: () => reference.getBoundingClientRect(), getClientRects: () => reference.getClientRects(), contextElement: blockReference ?? runtime.content ?? void 0 });
|
|
994
|
+
}, [runtime, refs, blockReference, selection, paint, active]);
|
|
890
995
|
const selectionKey = effectiveSelection?.raw.ranges.map((range) => `${range.start}-${range.end}`).join(",") ?? "";
|
|
891
996
|
useEffect6(() => {
|
|
892
997
|
setError(null);
|
|
@@ -969,7 +1074,7 @@ function SelectionToolbar({ actions, portalRoot, maxPanelHeight = 260, className
|
|
|
969
1074
|
};
|
|
970
1075
|
const annotationCount = annotations.filter((annotation) => annotation.type !== "highlight").length;
|
|
971
1076
|
const hint = available.some((action) => action.id === "delete-annotations") && annotationCount > 1 ? context.locale.deleteAnnotationsHint.replace("{count}", String(annotationCount)) : null;
|
|
972
|
-
return /* @__PURE__ */
|
|
1077
|
+
return /* @__PURE__ */ jsx6(FloatingPortal2, { root: portalRoot, children: /* @__PURE__ */ jsx6(
|
|
973
1078
|
"div",
|
|
974
1079
|
{
|
|
975
1080
|
ref: refs.setFloating,
|
|
@@ -982,6 +1087,7 @@ function SelectionToolbar({ actions, portalRoot, maxPanelHeight = 260, className
|
|
|
982
1087
|
"data-limr-appearance": appearance,
|
|
983
1088
|
"data-placement": placement,
|
|
984
1089
|
"data-highlight": highlight?.id,
|
|
1090
|
+
"data-readonly": readOnly || void 0,
|
|
985
1091
|
"data-expanded": expandedAction?.id,
|
|
986
1092
|
onMouseDown: (event) => {
|
|
987
1093
|
if (!(event.target instanceof Element) || !event.target.closest("input,textarea,select,[contenteditable]")) event.preventDefault();
|
|
@@ -1013,7 +1119,7 @@ function SelectionToolbar({ actions, portalRoot, maxPanelHeight = 260, className
|
|
|
1013
1119
|
event.preventDefault();
|
|
1014
1120
|
},
|
|
1015
1121
|
children: /* @__PURE__ */ jsxs5("div", { className: "limr-toolbar-surface", "data-ready": isPositioned || void 0, children: [
|
|
1016
|
-
/* @__PURE__ */
|
|
1122
|
+
/* @__PURE__ */ jsx6("div", { className: "limr-toolbar-actions", children: render && !readOnly ? render({ context, actions: available, run }) : available.map((action, index) => /* @__PURE__ */ jsx6(
|
|
1017
1123
|
"div",
|
|
1018
1124
|
{
|
|
1019
1125
|
className: "limr-toolbar-item",
|
|
@@ -1034,7 +1140,7 @@ function SelectionToolbar({ actions, portalRoot, maxPanelHeight = 260, className
|
|
|
1034
1140
|
setError(cause instanceof Error ? cause.message : String(cause));
|
|
1035
1141
|
}
|
|
1036
1142
|
},
|
|
1037
|
-
children: action.render ? action.render(actionContext(action)) : /* @__PURE__ */
|
|
1143
|
+
children: action.render ? action.render(actionContext(action)) : /* @__PURE__ */ jsx6(
|
|
1038
1144
|
"button",
|
|
1039
1145
|
{
|
|
1040
1146
|
className: "limr-toolbar-action",
|
|
@@ -1058,33 +1164,34 @@ function SelectionToolbar({ actions, portalRoot, maxPanelHeight = 260, className
|
|
|
1058
1164
|
onBlur: (event) => {
|
|
1059
1165
|
if (action.id === "delete-annotations" && !event.currentTarget.matches(":hover")) setDeleteHintOpen(false);
|
|
1060
1166
|
},
|
|
1061
|
-
children: [
|
|
1167
|
+
children: /* @__PURE__ */ jsxs5(ToolFaces, { state: label(action), children: [
|
|
1062
1168
|
/* @__PURE__ */ jsxs5("span", { className: "limr-tool-face", "data-visible": success !== action.id || void 0, "aria-hidden": "true", children: [
|
|
1063
|
-
/* @__PURE__ */
|
|
1064
|
-
/* @__PURE__ */
|
|
1169
|
+
/* @__PURE__ */ jsx6("span", { className: "limr-tool-icon", children: typeof action.icon === "function" ? action.icon(context) : action.icon }, typeof action.label === "function" ? action.label(context) : action.label),
|
|
1170
|
+
/* @__PURE__ */ jsx6("span", { className: "limr-tool-label limr-underline", children: typeof action.label === "function" ? action.label(context) : action.label })
|
|
1065
1171
|
] }),
|
|
1066
1172
|
action.successLabel && /* @__PURE__ */ jsxs5("span", { className: "limr-tool-face", "data-visible": success === action.id || void 0, "aria-hidden": "true", children: [
|
|
1067
|
-
/* @__PURE__ */
|
|
1068
|
-
/* @__PURE__ */
|
|
1173
|
+
/* @__PURE__ */ jsx6("span", { className: "limr-tool-icon", children: action.successIcon ?? (typeof action.icon === "function" ? action.icon(context) : action.icon) }),
|
|
1174
|
+
/* @__PURE__ */ jsx6("span", { className: "limr-tool-label limr-underline", children: typeof action.successLabel === "function" ? action.successLabel(context) : action.successLabel })
|
|
1069
1175
|
] })
|
|
1070
|
-
]
|
|
1176
|
+
] })
|
|
1071
1177
|
}
|
|
1072
1178
|
)
|
|
1073
1179
|
},
|
|
1074
1180
|
action.id
|
|
1075
1181
|
)) }),
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1182
|
+
readOnly && /* @__PURE__ */ jsx6("p", { className: "limr-toolbar-readonly-hint", children: context.locale.inlineHighlightHint ?? "Markdown inline highlight, editing unavailable" }),
|
|
1183
|
+
expandedAction && /* @__PURE__ */ jsx6("div", { id: panelId, className: "limr-toolbar-panel", role: "group", "aria-label": label(expandedAction), children: /* @__PURE__ */ jsx6(OverlayScrollArea, { active: true, label: label(expandedAction), children: expandedAction.renderExpanded(actionContext(expandedAction)) }) }, expandedAction.id),
|
|
1184
|
+
hint && /* @__PURE__ */ jsx6(Collapse, { open: deleteHintOpen, keepMounted: true, className: "limr-toolbar-delete-warning", children: /* @__PURE__ */ jsx6("p", { id: `${panelId}-delete`, className: "limr-toolbar-delete-hint", children: hint }) }),
|
|
1185
|
+
error && /* @__PURE__ */ jsx6("span", { role: "alert", className: "limr-action-error", children: error })
|
|
1079
1186
|
] }, selectionKey)
|
|
1080
1187
|
}
|
|
1081
1188
|
) });
|
|
1082
1189
|
}
|
|
1083
1190
|
|
|
1084
1191
|
// src/components/Reader.tsx
|
|
1085
|
-
import { Fragment as Fragment3, jsx as
|
|
1192
|
+
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1086
1193
|
function Reader({ actions, toc = true, sidebar = true, followOutput = false, sidebarBreakpoint = 1120, fillHeight = true, className, style, ...provider }) {
|
|
1087
|
-
return /* @__PURE__ */
|
|
1194
|
+
return /* @__PURE__ */ jsx7(DocumentProvider, { ...provider, children: /* @__PURE__ */ jsx7(ReaderLayout, { actions, toc, sidebar, followOutput, sidebarBreakpoint, fillHeight, className, style }) });
|
|
1088
1195
|
}
|
|
1089
1196
|
function ReaderLayout({ actions, toc, sidebar, followOutput, sidebarBreakpoint, fillHeight, className, style }) {
|
|
1090
1197
|
const runtime = useRuntime();
|
|
@@ -1092,12 +1199,12 @@ function ReaderLayout({ actions, toc, sidebar, followOutput, sidebarBreakpoint,
|
|
|
1092
1199
|
const layout = useStoreSelector(runtime.layout, (value) => value);
|
|
1093
1200
|
const appearance = useAppearance();
|
|
1094
1201
|
const theme = useTheme();
|
|
1095
|
-
const ref =
|
|
1096
|
-
const tocRail =
|
|
1097
|
-
const content =
|
|
1202
|
+
const ref = useRef6(null);
|
|
1203
|
+
const tocRail = useRef6(null);
|
|
1204
|
+
const content = useRef6(null);
|
|
1098
1205
|
useOverflowFade(content, runtime, ":scope > .limr-prose");
|
|
1099
|
-
const [width, setWidth] =
|
|
1100
|
-
const [availableHeight, setAvailableHeight] =
|
|
1206
|
+
const [width, setWidth] = useState6(0);
|
|
1207
|
+
const [availableHeight, setAvailableHeight] = useState6(0);
|
|
1101
1208
|
useIsomorphicLayoutEffect(() => {
|
|
1102
1209
|
if (!fillHeight || !ref.current) return;
|
|
1103
1210
|
const origin = ref.current.getBoundingClientRect().top - runtime.getViewportTop() - (scrollRoot?.clientTop ?? 0) + runtime.getScrollTop();
|
|
@@ -1158,25 +1265,25 @@ function ReaderLayout({ actions, toc, sidebar, followOutput, sidebarBreakpoint,
|
|
|
1158
1265
|
const sidebarVisible = !!sidebar && width >= sidebarBreakpoint;
|
|
1159
1266
|
return /* @__PURE__ */ jsxs6("div", { ref, className: `limr-reader ${className ?? ""}`, style: { "--limr-reader-available-height": fillHeight ? `${availableHeight}px` : void 0, ...style }, "data-fill-height": fillHeight || void 0, "data-sidebar": sidebarVisible || void 0, "data-toc": toc && width >= 800 || void 0, "data-limr-theme": theme, "data-limr-appearance": appearance, children: [
|
|
1160
1267
|
toc && /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
1161
|
-
/* @__PURE__ */
|
|
1162
|
-
/* @__PURE__ */
|
|
1268
|
+
/* @__PURE__ */ jsx7("div", { ref: tocRail, className: "limr-reader-toc", children: /* @__PURE__ */ jsx7(TableOfContents, {}) }),
|
|
1269
|
+
/* @__PURE__ */ jsx7(MobileContents, {})
|
|
1163
1270
|
] }),
|
|
1164
|
-
/* @__PURE__ */
|
|
1165
|
-
sidebarVisible && /* @__PURE__ */
|
|
1166
|
-
/* @__PURE__ */
|
|
1167
|
-
/* @__PURE__ */
|
|
1271
|
+
/* @__PURE__ */ jsx7("main", { ref: content, className: "limr-reader-content", children: /* @__PURE__ */ jsx7(Markdown, { followOutput }) }),
|
|
1272
|
+
sidebarVisible && /* @__PURE__ */ jsx7(AnnotationSidebar, {}),
|
|
1273
|
+
/* @__PURE__ */ jsx7(AnnotationOverlay, { disabled: sidebarVisible }),
|
|
1274
|
+
/* @__PURE__ */ jsx7(SelectionToolbar, { actions })
|
|
1168
1275
|
] });
|
|
1169
1276
|
}
|
|
1170
1277
|
function MobileContents() {
|
|
1171
1278
|
const locale = useLocale();
|
|
1172
|
-
const [open, setOpen] =
|
|
1279
|
+
const [open, setOpen] = useState6(false);
|
|
1173
1280
|
const id = useId2();
|
|
1174
1281
|
return /* @__PURE__ */ jsxs6("div", { className: "limr-reader-mobile-toc", children: [
|
|
1175
1282
|
/* @__PURE__ */ jsxs6("button", { type: "button", className: "limr-mobile-toc-toggle", "aria-expanded": open, "aria-controls": id, onClick: () => setOpen((value) => !value), children: [
|
|
1176
|
-
/* @__PURE__ */
|
|
1177
|
-
/* @__PURE__ */
|
|
1283
|
+
/* @__PURE__ */ jsx7("span", { children: locale.contents }),
|
|
1284
|
+
/* @__PURE__ */ jsx7("svg", { viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("path", { d: "m6 8 4 4 4-4" }) })
|
|
1178
1285
|
] }),
|
|
1179
|
-
/* @__PURE__ */
|
|
1286
|
+
/* @__PURE__ */ jsx7("div", { id, children: /* @__PURE__ */ jsx7(Collapse, { open, keepMounted: true, children: /* @__PURE__ */ jsx7(TableOfContents, {}) }) })
|
|
1180
1287
|
] });
|
|
1181
1288
|
}
|
|
1182
1289
|
|
|
@@ -1190,4 +1297,4 @@ export {
|
|
|
1190
1297
|
SelectionToolbar,
|
|
1191
1298
|
Reader
|
|
1192
1299
|
};
|
|
1193
|
-
//# sourceMappingURL=chunk-
|
|
1300
|
+
//# sourceMappingURL=chunk-GU2RPESF.js.map
|