@linloir/limr 0.1.0 → 1.0.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.
Files changed (80) hide show
  1. package/README.en.md +123 -0
  2. package/README.md +63 -138
  3. package/dist/base.css +10 -7
  4. package/dist/{chunk-65V5OZ4G.js → chunk-3ILUZKDB.js} +77 -17
  5. package/dist/chunk-3ILUZKDB.js.map +1 -0
  6. package/dist/{chunk-ETHLPBKQ.js → chunk-N77VD2YR.js} +96 -51
  7. package/dist/chunk-N77VD2YR.js.map +1 -0
  8. package/dist/{chunk-7JF5HHVF.js → chunk-TTISGBST.js} +14 -1
  9. package/dist/{chunk-7JF5HHVF.js.map → chunk-TTISGBST.js.map} +1 -1
  10. package/dist/core/index.d.ts +6 -3
  11. package/dist/core/index.js +3 -1
  12. package/dist/extensions/index.d.ts +2 -2
  13. package/dist/extensions/index.js +2 -2
  14. package/dist/index.d.ts +2 -2
  15. package/dist/index.js +7 -3
  16. package/dist/react/index.d.ts +4 -3
  17. package/dist/react/index.js +5 -3
  18. package/dist/styles.css +10 -7
  19. package/dist/{translations-BliabrPd.d.ts → translations-DHnYMg4h.d.ts} +11 -3
  20. package/docs/README.md +13 -0
  21. package/docs/api.md +5 -158
  22. package/docs/assets/rendered.svg +9 -0
  23. package/docs/assets/source.svg +9 -0
  24. package/docs/en/README.md +26 -0
  25. package/docs/en/ai-skills.md +57 -0
  26. package/docs/en/annotations.md +84 -0
  27. package/docs/en/api.md +107 -0
  28. package/docs/en/architecture.md +33 -0
  29. package/docs/en/extensions.md +159 -0
  30. package/docs/en/getting-started.md +67 -0
  31. package/docs/en/integration.md +130 -0
  32. package/docs/en/modules.md +80 -0
  33. package/docs/en/performance.md +53 -0
  34. package/docs/en/plugin-packages.md +68 -0
  35. package/docs/en/styles.md +80 -0
  36. package/docs/en/syntax.md +352 -0
  37. package/docs/extensions.md +5 -306
  38. package/docs/implementation.md +5 -76
  39. package/docs/performance.md +5 -81
  40. package/docs/screenshots/blocks.png +0 -0
  41. package/docs/screenshots/dark.png +0 -0
  42. package/docs/screenshots/desktop.png +0 -0
  43. package/docs/screenshots/plugin-mobile.png +0 -0
  44. package/docs/styles.md +5 -100
  45. package/docs/visual-upgrade.md +5 -145
  46. package/docs/zh-CN/README.md +26 -0
  47. package/docs/zh-CN/ai-skills.md +57 -0
  48. package/docs/zh-CN/annotations.md +84 -0
  49. package/docs/zh-CN/api.md +107 -0
  50. package/docs/zh-CN/architecture.md +33 -0
  51. package/docs/zh-CN/extensions.md +159 -0
  52. package/docs/zh-CN/getting-started.md +67 -0
  53. package/docs/zh-CN/integration.md +130 -0
  54. package/docs/zh-CN/modules.md +80 -0
  55. package/docs/zh-CN/performance.md +53 -0
  56. package/docs/zh-CN/plugin-packages.md +68 -0
  57. package/docs/zh-CN/styles.md +80 -0
  58. package/docs/zh-CN/syntax.md +352 -0
  59. package/examples/notes.tsx +18 -11
  60. package/examples/packages/paper-theme/index.css +25 -0
  61. package/examples/packages/paper-theme/package.json +7 -0
  62. package/examples/packages/reading-tools/index.d.ts +2 -0
  63. package/examples/packages/reading-tools/index.js +25 -0
  64. package/examples/packages/reading-tools/package.json +16 -0
  65. package/package.json +9 -5
  66. package/skills/limr-plugin-development/SKILL.md +22 -0
  67. package/skills/limr-plugin-development/agents/openai.yaml +4 -0
  68. package/skills/limr-plugin-development/assets/plugin-template/README.md +30 -0
  69. package/skills/limr-plugin-development/assets/plugin-template/package.json +36 -0
  70. package/skills/limr-plugin-development/assets/plugin-template/src/index.tsx +50 -0
  71. package/skills/limr-plugin-development/assets/plugin-template/src/styles.css +9 -0
  72. package/skills/limr-plugin-development/assets/plugin-template/tsconfig.json +20 -0
  73. package/skills/limr-plugin-development/assets/plugin-template/tsup.config.ts +8 -0
  74. package/skills/limr-plugin-development/references/contract.md +45 -0
  75. package/skills/limr-use/SKILL.md +23 -0
  76. package/skills/limr-use/agents/openai.yaml +4 -0
  77. package/skills/limr-use/references/integration.md +41 -0
  78. package/skills/limr-use/references/syntax.md +17 -0
  79. package/dist/chunk-65V5OZ4G.js.map +0 -1
  80. package/dist/chunk-ETHLPBKQ.js.map +0 -1
@@ -1,4 +1,5 @@
1
1
  import {
2
+ annotationReferenceRange,
2
3
  createDocument,
3
4
  defaultHighlight,
4
5
  intersects,
@@ -10,7 +11,7 @@ import {
10
11
  selectionFromRange,
11
12
  textContent,
12
13
  walk
13
- } from "./chunk-7JF5HHVF.js";
14
+ } from "./chunk-TTISGBST.js";
14
15
  import {
15
16
  ResourceCache
16
17
  } from "./chunk-6N6TFHEN.js";
@@ -56,6 +57,8 @@ function scrollWithMotion(root, top) {
56
57
 
57
58
  // src/react/runtime.ts
58
59
  var english = {
60
+ language: "en",
61
+ citation: "Citation",
59
62
  contents: "Contents",
60
63
  annotations: "Annotations",
61
64
  expand: "Expand",
@@ -95,6 +98,8 @@ var english = {
95
98
  deleteAnnotationsHint: "Deletes {count} annotations on this text and any highlight."
96
99
  };
97
100
  var chinese = {
101
+ language: "zh-CN",
102
+ citation: "\u5F15\u7528",
98
103
  contents: "\u76EE\u5F55",
99
104
  annotations: "\u6CE8\u91CA",
100
105
  expand: "\u5C55\u5F00",
@@ -158,7 +163,7 @@ var ReaderRuntime = class {
158
163
  layout = new Store(0);
159
164
  viewport = new Store(0);
160
165
  documentOffset = new Store(0);
161
- decorations = new Store({ annotations: /* @__PURE__ */ new Map(), highlights: /* @__PURE__ */ new Map(), occurrences: [], byBlock: /* @__PURE__ */ new Map() });
166
+ decorations = new Store({ annotations: /* @__PURE__ */ new Map(), highlights: /* @__PURE__ */ new Map(), occurrences: [], referenceRanges: /* @__PURE__ */ new Map(), byBlock: /* @__PURE__ */ new Map() });
162
167
  configuration;
163
168
  resources = new ResourceCache(128);
164
169
  resourceVersions = new Store(/* @__PURE__ */ new Map());
@@ -171,6 +176,7 @@ var ReaderRuntime = class {
171
176
  textBinders = /* @__PURE__ */ new WeakMap();
172
177
  components = {};
173
178
  annotationTypes = /* @__PURE__ */ new Map();
179
+ resolvedTypes = /* @__PURE__ */ new WeakMap();
174
180
  locale = english;
175
181
  content = null;
176
182
  selectionRange = null;
@@ -190,7 +196,7 @@ var ReaderRuntime = class {
190
196
  addAnnotation: (annotation, options = {}) => {
191
197
  this.changeAnnotations((items) => {
192
198
  const next = [...items.filter((item) => item.id !== annotation.id), annotation];
193
- if (annotation.type !== "highlight" && options.highlight !== false) {
199
+ if (annotation.type !== "highlight" && options.highlight !== false && (options.highlight !== void 0 || this.annotationTypes.get(annotation.type)?.highlight !== "hover")) {
194
200
  const doc = this.document.getSnapshot();
195
201
  for (const anchor of resolveAnnotationAnchors(annotation, doc)) {
196
202
  if (anchor.range.start === anchor.range.end || next.some((item) => item.type === "highlight" && resolveAnnotationAnchors(item, doc).some((other) => sameRange(other.range, anchor.range)))) continue;
@@ -270,10 +276,17 @@ var ReaderRuntime = class {
270
276
  });
271
277
  }
272
278
  installConfiguration(config) {
279
+ this.locale = { ...english, ...config.locale };
273
280
  this.components = Object.assign({}, ...(config.extensions ?? []).map((ext) => ext.components), config.components);
274
281
  const annotationTypes = [...(config.extensions ?? []).flatMap((ext) => [...ext.annotationTypes ?? []]), ...config.annotationTypes ?? []];
275
- this.annotationTypes = new Map(annotationTypes.map((type) => [type.type, type]));
276
- this.locale = { ...english, ...config.locale };
282
+ this.annotationTypes = new Map(annotationTypes.map((type) => {
283
+ if (typeof type.label === "string") return [type.type, type];
284
+ const label = type.label(this.locale);
285
+ const previous = this.resolvedTypes.get(type);
286
+ const resolved = previous?.label === label ? previous : { ...type, label };
287
+ this.resolvedTypes.set(type, resolved);
288
+ return [type.type, resolved];
289
+ }));
277
290
  }
278
291
  configure(config) {
279
292
  const old = this.configuration.get();
@@ -281,7 +294,7 @@ var ReaderRuntime = class {
281
294
  this.installConfiguration(config);
282
295
  this.configuration.set(config);
283
296
  if (old.extensions !== config.extensions) this.document.setExtensions(config.extensions ?? []);
284
- if (old.annotations !== config.annotations) this.rebuildDecorations();
297
+ else if (old.annotations !== config.annotations || old.annotationTypes !== config.annotationTypes) this.rebuildDecorations();
285
298
  if (old.annotationTypes !== config.annotationTypes || old.extensions !== config.extensions) this.resources.invalidate();
286
299
  }
287
300
  updateInteraction(patch) {
@@ -345,11 +358,23 @@ var ReaderRuntime = class {
345
358
  const id = index ? `${annotation.id}:${index}` : annotation.id;
346
359
  const value = { ...annotation.data, id, annotationId: annotation.id, range: anchor.range };
347
360
  const old = previous.highlights.get(id);
348
- allHighlights.push(old && sameRange(old.range, value.range) && old.color === value.color && old.style === value.style && old.hidden === value.hidden ? old : value);
361
+ allHighlights.push(old && !old.occurrenceId && sameRange(old.range, value.range) && old.color === value.color && old.style === value.style && old.hidden === value.hidden ? old : value);
349
362
  } else occurrences.push({ ...anchor, id: anchor.id ?? `${doc.id}-${annotation.id}-${index}`, annotationIds: [annotation.id] });
350
363
  });
351
364
  }
352
365
  occurrences.sort((a, b) => a.range.end - b.range.end || a.id.localeCompare(b.id));
366
+ const referenceRanges = new Map(occurrences.map((occurrence) => [occurrence.id, annotationReferenceRange(doc, occurrence)]));
367
+ for (const occurrence of occurrences) {
368
+ const annotation = annotations.get(occurrence.annotationIds[0]);
369
+ const definition2 = annotation && this.annotationTypes.get(annotation.type);
370
+ if (!annotation || definition2?.highlight !== "hover") continue;
371
+ const range = referenceRanges.get(occurrence.id);
372
+ if (range.start === range.end) continue;
373
+ const id = `reference-${occurrence.id}`;
374
+ const value = { id, annotationId: annotation.id, occurrenceId: occurrence.id, range, color: definition2.color ?? defaultHighlight.color, style: "background" };
375
+ const old = previous.highlights.get(id);
376
+ allHighlights.push(old && old.occurrenceId === value.occurrenceId && old.annotationId === value.annotationId && sameRange(old.range, range) && old.color === value.color ? old : value);
377
+ }
353
378
  const byBlock = /* @__PURE__ */ new Map();
354
379
  for (const block of occurrences.length || allHighlights.length ? doc.blocks : []) {
355
380
  const marks = occurrences.filter((item) => !item.embedded && item.range.end > block.range.start && item.range.end <= block.range.end);
@@ -363,7 +388,7 @@ var ReaderRuntime = class {
363
388
  }
364
389
  const sameBlocks = byBlock.size === previous.byBlock.size && [...byBlock].every(([id, value]) => previous.byBlock.get(id) === value);
365
390
  const highlights = new Map(allHighlights.map((item) => [item.id, item]));
366
- this.decorations.set({ annotations, highlights, occurrences, byBlock: sameBlocks ? previous.byBlock : byBlock });
391
+ this.decorations.set({ annotations, highlights, occurrences, referenceRanges, byBlock: sameBlocks ? previous.byBlock : byBlock });
367
392
  const state = this.interaction.get();
368
393
  if (state.editing && !annotations.has(state.editing) && this.pendingOpen !== state.editing) this.updateInteraction({ editing: null });
369
394
  if (state.activeHighlight && !highlights.has(state.activeHighlight)) this.activateHighlight(null);
@@ -1112,7 +1137,8 @@ function renderNode(node, context, index = 0, trailing) {
1112
1137
  var BlockView = memo(function BlockView2({ block, runtime, components, extensions, decorations, footnoteId }) {
1113
1138
  const element = useRef2(null);
1114
1139
  const clearBindings = useRef2(void 0);
1115
- const { occurrences, highlights } = decorations;
1140
+ const { occurrences } = decorations;
1141
+ const highlights = useMemo(() => decorations.highlights.filter((highlight) => !highlight.occurrenceId), [decorations.highlights]);
1116
1142
  const root = useMemo(() => footnoteId ? { ...block.node, type: "listItem" } : block.node, [block, footnoteId]);
1117
1143
  const texts = useMemo(() => compactTextNodes(root, runtime.components), [root, runtime, components, extensions]);
1118
1144
  const compact = !!texts && !occurrences.length && !highlights.length;
@@ -1586,7 +1612,7 @@ function SelectionPaint({ runtime, interactive = true }) {
1586
1612
  useEffect3(() => {
1587
1613
  const container = runtime.content, paint = layer.current;
1588
1614
  if (!container || !paint) return;
1589
- let frame = 0;
1615
+ let frame = 0, translating = false;
1590
1616
  let leavesCache = /* @__PURE__ */ new WeakMap();
1591
1617
  let metrics = /* @__PURE__ */ new WeakMap();
1592
1618
  const fontMetrics = /* @__PURE__ */ new Map();
@@ -1603,7 +1629,14 @@ function SelectionPaint({ runtime, interactive = true }) {
1603
1629
  const highlightRects = (id) => [...highlightLayers.values()].flatMap((layer2) => Array.from(layer2.children).filter((item) => item.dataset.paint === id).map((item) => item.getBoundingClientRect()));
1604
1630
  const focusedRange = () => {
1605
1631
  const state = runtime.interaction.get(), decorations = runtime.decorations.get();
1606
- return decorations.occurrences.find((item) => item.id === state.hovered)?.range ?? (state.activeHighlight ? decorations.highlights.get(state.activeHighlight)?.range : void 0) ?? decorations.occurrences.find((item) => item.id === state.active)?.range;
1632
+ return decorations.referenceRanges.get(state.hovered ?? "") ?? (state.activeHighlight ? decorations.highlights.get(state.activeHighlight)?.range : void 0) ?? decorations.referenceRanges.get(state.active ?? "");
1633
+ };
1634
+ const referenceActive = (occurrenceId) => {
1635
+ const state = runtime.interaction.get(), decorations = runtime.decorations.get();
1636
+ if (occurrenceId === state.hovered) return true;
1637
+ if (occurrenceId !== state.active) return false;
1638
+ const hovered = decorations.highlights.get(`reference-${state.hovered}`);
1639
+ return !hovered?.occurrenceId || !sameRange(hovered.range, decorations.referenceRanges.get(occurrenceId));
1607
1640
  };
1608
1641
  const feedback = () => {
1609
1642
  const state = runtime.interaction.get();
@@ -1612,6 +1645,12 @@ function SelectionPaint({ runtime, interactive = true }) {
1612
1645
  for (const layer2 of highlightLayers.values()) for (const item of layer2.children) {
1613
1646
  item.toggleAttribute("data-hovered", item.dataset.paint === state.hoveredHighlight);
1614
1647
  const highlight = decorations.highlights.get(item.dataset.paint ?? "");
1648
+ if (highlight?.occurrenceId) {
1649
+ const active = referenceActive(highlight.occurrenceId);
1650
+ item.toggleAttribute("data-active", active);
1651
+ item.toggleAttribute("data-muted", !active);
1652
+ continue;
1653
+ }
1615
1654
  item.toggleAttribute("data-active", item.dataset.paint === state.activeHighlight || !!focused && !!highlight && sameRange(focused, highlight.range));
1616
1655
  item.toggleAttribute("data-muted", !!highlight?.hidden || !!focused && !!highlight && !sameRange(focused, highlight.range));
1617
1656
  }
@@ -1769,6 +1808,7 @@ function SelectionPaint({ runtime, interactive = true }) {
1769
1808
  const className = `limr-paint-rect limr-paint-${task.selection ? "selection" : task.style ?? "background"}`;
1770
1809
  if (item.className !== className) item.className = className;
1771
1810
  item.dataset.paint = task.id;
1811
+ item.toggleAttribute("data-reference", !!task.occurrenceId);
1772
1812
  if (rect.marker) item.dataset.marker = rect.marker;
1773
1813
  else delete item.dataset.marker;
1774
1814
  if (rect.atomic) item.dataset.atomic = "true";
@@ -1781,6 +1821,11 @@ function SelectionPaint({ runtime, interactive = true }) {
1781
1821
  item.dataset.geometry = css;
1782
1822
  }
1783
1823
  if (!task.selection) item.style.setProperty("--limr-highlight", task.color ?? "#F2C744");
1824
+ if (task.occurrenceId) {
1825
+ const active2 = referenceActive(task.occurrenceId);
1826
+ item.toggleAttribute("data-active", active2);
1827
+ item.toggleAttribute("data-muted", !active2);
1828
+ }
1784
1829
  if (!item.parentNode) target.append(item);
1785
1830
  });
1786
1831
  for (const layer2 of [paint, ...highlightLayers.values()]) while (layer2.children.length > (counts.get(layer2) ?? 0)) layer2.lastChild.remove();
@@ -1789,10 +1834,22 @@ function SelectionPaint({ runtime, interactive = true }) {
1789
1834
  const active = runtime.interaction.get().activeHighlight;
1790
1835
  if (active && !hits.some((hit) => hit.id === active)) runtime.clearSelection();
1791
1836
  feedback();
1837
+ if (translating) {
1838
+ translating = container.getAnimations().some((animation) => animation instanceof CSSTransition && animation.transitionProperty === "translate" && animation.playState === "running");
1839
+ if (translating) schedule();
1840
+ }
1792
1841
  };
1793
1842
  const schedule = () => {
1794
1843
  if (!frame) frame = requestAnimationFrame(render);
1795
1844
  };
1845
+ const translate = (event) => {
1846
+ if (event.target !== container || event.propertyName !== "translate") return;
1847
+ translating = true;
1848
+ schedule();
1849
+ };
1850
+ container.addEventListener("transitionrun", translate);
1851
+ container.addEventListener("transitionend", translate);
1852
+ container.addEventListener("transitioncancel", translate);
1796
1853
  const invalidate = () => {
1797
1854
  blocks = void 0;
1798
1855
  metrics = /* @__PURE__ */ new WeakMap();
@@ -1820,7 +1877,7 @@ function SelectionPaint({ runtime, interactive = true }) {
1820
1877
  lastDecorations = decorations;
1821
1878
  leavesCache = /* @__PURE__ */ new WeakMap();
1822
1879
  schedule();
1823
- }), runtime.layout.subscribe(invalidate), runtime.configuration.subscribe(invalidate)];
1880
+ }), runtime.layout.subscribe(invalidate), runtime.documentOffset.subscribe(schedule), runtime.configuration.subscribe(invalidate)];
1824
1881
  const observer = new MutationObserver((records) => {
1825
1882
  let changed = false;
1826
1883
  for (const record of records) {
@@ -1853,7 +1910,7 @@ function SelectionPaint({ runtime, interactive = true }) {
1853
1910
  for (let index = hits.length - 1; index >= 0; index--) {
1854
1911
  const { rect, id } = hits[index];
1855
1912
  const highlight = decorations.highlights.get(id);
1856
- if (highlight?.hidden || focused && highlight && !sameRange(focused, highlight.range)) continue;
1913
+ if (highlight?.occurrenceId || highlight?.hidden || focused && highlight && !sameRange(focused, highlight.range)) continue;
1857
1914
  if (event.clientX >= rect.left && event.clientX <= rect.left + rect.width && event.clientY >= rect.top && event.clientY <= rect.top + rect.height) return id;
1858
1915
  }
1859
1916
  return null;
@@ -1918,6 +1975,9 @@ function SelectionPaint({ runtime, interactive = true }) {
1918
1975
  window.visualViewport?.removeEventListener("resize", schedule);
1919
1976
  window.visualViewport?.removeEventListener("scroll", schedule);
1920
1977
  document.fonts?.removeEventListener("loadingdone", fontsChanged);
1978
+ container.removeEventListener("transitionrun", translate);
1979
+ container.removeEventListener("transitionend", translate);
1980
+ container.removeEventListener("transitioncancel", translate);
1921
1981
  container.removeEventListener("pointermove", move);
1922
1982
  container.removeEventListener("pointerleave", leave);
1923
1983
  container.removeEventListener("pointerdown", pointerDown);
@@ -2101,7 +2161,7 @@ function citation(options = {}) {
2101
2161
  }
2102
2162
  function Card(props) {
2103
2163
  const locale = useLocale();
2104
- return /* @__PURE__ */ jsx7(AnnotationCard, { ...props, title: locale.annotations });
2164
+ return /* @__PURE__ */ jsx7(AnnotationCard, { ...props, title: locale.citation ?? locale.annotations });
2105
2165
  }
2106
2166
  function Content({ data }) {
2107
2167
  const runtime = useRuntime();
@@ -2120,7 +2180,7 @@ function citation(options = {}) {
2120
2180
  name: "citation",
2121
2181
  tags: ["citation", "ref"],
2122
2182
  scope: "local",
2123
- annotationTypes: [{ type: "citation", label: "Citation", icon: CitationIcon, color: "#6366f1", Content, ...options, sidebar: { Preview, collapseOnContentClick: false, ...options.sidebar }, overlay: { Card, expand: false, ...options.overlay } }],
2183
+ annotationTypes: [{ type: "citation", label: (locale) => locale.citation ?? "Citation", icon: CitationIcon, color: "#6366f1", highlight: "hover", Content, ...options, sidebar: { Preview, collapseOnContentClick: false, ...options.sidebar }, overlay: { Card, expand: false, ...options.overlay } }],
2124
2184
  transform(root) {
2125
2185
  walk(root, (node) => {
2126
2186
  if (node.type !== "component") return;
@@ -2968,7 +3028,7 @@ function HighlightSettings({ context, options }) {
2968
3028
  /* @__PURE__ */ jsx20("legend", { children: locale.highlightStyle }),
2969
3029
  /* @__PURE__ */ jsx20("div", { className: "limr-highlight-styles", children: styles.map(([value, label]) => /* @__PURE__ */ jsxs16("button", { type: "button", "aria-pressed": style === value, onClick: () => change(color, value), children: [
2970
3030
  /* @__PURE__ */ jsx20(ToolIcon, { name: value }),
2971
- /* @__PURE__ */ jsx20("span", { className: "limr-underline", children: label })
3031
+ /* @__PURE__ */ jsx20("span", { className: "limr-highlight-style-label", children: /* @__PURE__ */ jsx20("span", { className: "limr-underline", children: label }) })
2972
3032
  ] }, value)) })
2973
3033
  ] }),
2974
3034
  /* @__PURE__ */ jsxs16("fieldset", { disabled: context.busy, children: [
@@ -3067,4 +3127,4 @@ export {
3067
3127
  highlightSelection,
3068
3128
  deleteAnnotations
3069
3129
  };
3070
- //# sourceMappingURL=chunk-65V5OZ4G.js.map
3130
+ //# sourceMappingURL=chunk-3ILUZKDB.js.map