@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
package/dist/styles.css
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/* LiMR classic skin. Local assets only; unlayered application CSS wins. */
|
|
2
2
|
@import "./fonts/maple.css";
|
|
3
|
+
/* Viewport coordinates change on every scroll. They belong to the mask host,
|
|
4
|
+
never its text descendants; inheriting them invalidates the entire article. */
|
|
5
|
+
@property --limr-fade-start { syntax: '<length>'; inherits: false; initial-value: 0px; }
|
|
6
|
+
@property --limr-fade-end { syntax: '<length>'; inherits: false; initial-value: 0px; }
|
|
7
|
+
|
|
3
8
|
@layer limr {
|
|
4
9
|
:where([data-limr-appearance]) {
|
|
5
10
|
--limr-ease: cubic-bezier(.16, 1, .3, 1);
|
|
@@ -62,8 +67,21 @@
|
|
|
62
67
|
--limr-link: #8bb8e3; --limr-selection: #6e88c955; --limr-shadow: #0006;
|
|
63
68
|
}
|
|
64
69
|
}
|
|
65
|
-
.limr-prose { width: 100%; min-width: 0; font-size: var(--limr-font-size, 1rem); line-height: var(--limr-line-height, 1.8); font-feature-settings: var(--limr-font-features,
|
|
70
|
+
.limr-prose { width: 100%; min-width: 0; font-size: var(--limr-font-size, 1rem); line-height: var(--limr-line-height, 1.8); font-feature-settings: var(--limr-font-features, normal); text-spacing-trim: var(--limr-punctuation-spacing, space-all); overflow-wrap: break-word; --limr-paragraph-align: start; }
|
|
66
71
|
.limr-prose[data-limr-justify='true'] { --limr-paragraph-align: justify; }
|
|
72
|
+
.limr-prose[data-limr-typography='pretty'] :where(p,.limr-paragraph,li,figcaption) { text-wrap: pretty; }
|
|
73
|
+
.limr-prose[data-limr-typography='pretty'] :where(h1,h2,h3,h4,h5,h6) { text-wrap: balance; }
|
|
74
|
+
/* Stable native wrapping prevents earlier lines moving for every token. */
|
|
75
|
+
.limr-prose[data-limr-typography='pretty'] :where([data-pending], [data-pending] p, [data-pending] li, [data-pending] .limr-paragraph) { text-wrap: wrap; text-wrap: stable; }
|
|
76
|
+
.limr-block-loading { display: flex; align-items: center; min-height: 3rem; padding-block: .5rem; }
|
|
77
|
+
.limr-loading { display: inline-flex; flex: none; width: 22px; height: 22px; line-height: 0; color: var(--limr-secondary, #7b7975); }
|
|
78
|
+
.limr-loading svg { display: block; width: 100%; height: 100%; overflow: visible; }
|
|
79
|
+
.limr-loading circle { fill: none; stroke-width: 4; }
|
|
80
|
+
.limr-loading-track { stroke: currentColor; opacity: .17; }
|
|
81
|
+
.limr-loading-orbit { transform-box: view-box; transform-origin: 16px 16px; animation: limr-loading-orbit 1.55s cubic-bezier(.65,0,.35,1) infinite; }
|
|
82
|
+
.limr-loading-arc { stroke: currentColor; stroke-linecap: round; stroke-dasharray: 3 97; transform: rotate(-90deg); transform-box: view-box; transform-origin: 16px 16px; animation: limr-loading-arc 1.55s cubic-bezier(.65,0,.35,1) infinite; }
|
|
83
|
+
@keyframes limr-loading-orbit { 0% { transform: rotate(0deg); } 50% { transform: rotate(300deg); } 100% { transform: rotate(720deg); } }
|
|
84
|
+
@keyframes limr-loading-arc { 0% { stroke-dasharray: 3 97; stroke-dashoffset: 0; } 46%,57% { stroke-dasharray: 92 8; stroke-dashoffset: -4; } 100% { stroke-dasharray: 3 97; stroke-dashoffset: -100; } }
|
|
67
85
|
.limr-prose :where(p,.limr-paragraph,li) { text-align: var(--limr-paragraph-align); text-align-last: start; text-justify: auto; }
|
|
68
86
|
.limr-prose ::selection { background: var(--limr-selection, #667eea4d); }
|
|
69
87
|
.limr-prose { position: relative; isolation: isolate; transition: translate var(--limr-motion) var(--limr-ease); }
|
|
@@ -82,6 +100,7 @@
|
|
|
82
100
|
.limr-highlight[data-hidden] { background: transparent; text-decoration: none; }
|
|
83
101
|
.limr-paint-background { background: color-mix(in srgb, var(--limr-highlight) 30%, transparent); }
|
|
84
102
|
.limr-paint-background:is([data-hovered],[data-active]) { background: color-mix(in srgb, var(--limr-highlight) 48%, transparent); }
|
|
103
|
+
.limr-paint-background[data-reference] { background: color-mix(in srgb, var(--limr-highlight) 18%, transparent); }
|
|
85
104
|
.limr-highlight-layer .limr-paint-rect:is([data-hovered],[data-active]):not(.limr-paint-background) { background-color: color-mix(in srgb, var(--limr-highlight) 14%, transparent); }
|
|
86
105
|
.limr-highlight { cursor: pointer; }
|
|
87
106
|
.limr-paint-selection { background: var(--limr-selection, #667eea4d); z-index: 1; }
|
|
@@ -318,9 +337,9 @@
|
|
|
318
337
|
.limr-sidebar-card { color: var(--limr-annotation-text, var(--limr-text, #1a1a1a)); min-width: 0; transition: top var(--limr-motion-slow) var(--limr-ease), border-color var(--limr-motion) var(--limr-ease), background-color var(--limr-motion) var(--limr-ease); }
|
|
319
338
|
.limr-sidebar-card:not([data-default-card]),.limr-sidebar-card-body { border-inline-start: 2px solid var(--limr-annotation-border, var(--limr-annotation-color, var(--limr-border-strong, #d5d0c8))); background: var(--limr-background, #fffefa); padding: var(--limr-sidebar-padding-block, .65rem) var(--limr-sidebar-padding-inline, 10px) calc(var(--limr-sidebar-padding-block, .65rem) + 2px); }
|
|
320
339
|
.limr-sidebar-card[data-locked] { z-index: 1; transition-property: border-color, background-color; }
|
|
321
|
-
.limr-sidebar[data-resizing] > .limr-sidebar-card { transition-property: border-color, background-color; }
|
|
340
|
+
.limr-sidebar:is([data-resizing],[data-releasing]) > .limr-sidebar-card { transition-property: border-color, background-color; }
|
|
322
341
|
.limr-sidebar-card[data-default-card] { display: flex; flex-direction: column; }
|
|
323
|
-
.limr-sidebar-card[data-default-card]
|
|
342
|
+
.limr-sidebar-card[data-default-card] { max-height: var(--limr-sidebar-card-height, none); }
|
|
324
343
|
.limr-sidebar-card-body { display: flex; flex-direction: column; min-height: 0; transition: border-color var(--limr-motion) var(--limr-ease), background-color var(--limr-motion) var(--limr-ease); }
|
|
325
344
|
.limr-sidebar-card-body > .limr-collapse { min-height: 0; }
|
|
326
345
|
/* Preview and details trade height; fading both can leave a blank card when
|
|
@@ -398,8 +417,9 @@
|
|
|
398
417
|
.limr-toolbar-item { display: flex; align-items: center; border-radius: 3px; transition: color 240ms var(--limr-ease); }
|
|
399
418
|
[data-ready] .limr-toolbar-item { animation: limr-tool-float 330ms cubic-bezier(.16,1,.3,1) calc(30ms + var(--limr-tool-index) * 22ms) both, limr-tool-fade 210ms cubic-bezier(.22,.7,.3,1) calc(65ms + var(--limr-tool-index) * 22ms) both; }
|
|
400
419
|
.limr-toolbar-action { display: inline-grid; place-items: center; flex: none; min-height: 34px; padding: 5px 7px; border: 0; border-radius: 3px; background: transparent; white-space: nowrap; line-height: 1.5; }
|
|
401
|
-
.limr-tool-
|
|
402
|
-
.limr-tool-face
|
|
420
|
+
.limr-tool-faces { display: inline-grid; min-width: 0; transition: width 320ms var(--limr-ease); }
|
|
421
|
+
.limr-tool-face { display: flex; align-items: center; justify-self: start; grid-area: 1 / 1; gap: 5px; width: max-content; min-width: 0; line-height: 1.5; opacity: 0; transform: translateY(2px) scale(.94); transform-origin: left center; pointer-events: none; transition: opacity 220ms var(--limr-ease), transform 320ms var(--limr-ease); }
|
|
422
|
+
.limr-tool-face[data-visible] { opacity: 1; transform: none; }
|
|
403
423
|
.limr-tool-label { display: block; flex: none; padding-block-end: 1px; }
|
|
404
424
|
.limr-tool-face > .limr-tool-icon { animation: limr-tool-fade 240ms var(--limr-ease) both; }
|
|
405
425
|
.limr-toolbar-item[data-status='success'] { color: var(--limr-success, #52885e); }
|
|
@@ -408,11 +428,12 @@
|
|
|
408
428
|
.limr-tool-icon :where(*) { max-inline-size: 100% !important; max-block-size: 100% !important; min-inline-size: 0 !important; min-block-size: 0 !important; }
|
|
409
429
|
.limr-tool-icon :where(svg,img) { display: block; width: 100% !important; height: 100% !important; object-fit: contain; flex: none; }
|
|
410
430
|
.limr-tool-icon :where(div,span,i,b,strong,em) { display: inline-flex; align-items: center; justify-content: center; font-size: inherit !important; line-height: 1 !important; }
|
|
411
|
-
|
|
431
|
+
/* Only the actions determine toolbar width. Panel contents cannot enlarge it. */
|
|
432
|
+
.limr-toolbar-panel { display: flex; contain: inline-size; min-width: 0; min-height: 0; align-self: stretch; max-height: var(--limr-toolbar-panel-height, 260px); overflow: hidden; text-align: start; margin-top: 4px; border-top: 1px solid var(--limr-toolbar-border, var(--limr-border, #e5e2dc)); animation: limr-tool-float 300ms cubic-bezier(.16,1,.3,1) both, limr-tool-fade 220ms ease 35ms both; }
|
|
412
433
|
.limr-toolbar-panel .limr-overlay-content { padding: 10px 9px 4px; scroll-padding-block: 8px; }
|
|
413
|
-
.limr-toolbar-panel .limr-overlay-scroll
|
|
434
|
+
.limr-toolbar-panel .limr-overlay-scroll { width: 100%; min-width: 0; }
|
|
435
|
+
.limr-toolbar-panel .limr-overlay-scroll-inner { width: 100%; min-width: 0; overflow-wrap: anywhere; }
|
|
414
436
|
.limr-toolbar-panel :where(input,textarea,select,img) { max-width: 100%; }
|
|
415
|
-
.limr-selection-toolbar[data-expanded] .limr-toolbar-surface { min-width: min(calc(var(--limr-toolbar-panel-width, 246px) + 30px), calc(100vw - 20px)); }
|
|
416
437
|
[data-placement^='top'] .limr-toolbar-panel { order: -1; border-top: 0; border-bottom: 1px solid var(--limr-toolbar-border, var(--limr-border, #e5e2dc)); margin-top: 0; margin-bottom: 4px; }
|
|
417
438
|
.limr-toolbar-swatch { position: relative; width: 24px; height: 24px; margin: 3px 12px 3px 5px; padding: 0; border: 1px solid color-mix(in srgb, var(--swatch) 60%, transparent); border-radius: var(--limr-toolbar-swatch-radius, 4px); background: color-mix(in srgb, var(--swatch) 30%, transparent); transition: scale 240ms var(--limr-ease); --limr-highlight: var(--swatch); }
|
|
418
439
|
.limr-toolbar-swatch:hover { scale: 1.07; }
|
|
@@ -421,6 +442,8 @@
|
|
|
421
442
|
.limr-toolbar-delete-warning[aria-hidden='true'] { visibility: hidden; }
|
|
422
443
|
[data-placement^='top'] .limr-toolbar-delete-warning { order: -1; }
|
|
423
444
|
.limr-toolbar-delete-hint { margin: 4px 8px 5px; font-size: 11px; color: var(--limr-error, #b45858); }
|
|
445
|
+
.limr-selection-toolbar[data-readonly] { width: max-content; }
|
|
446
|
+
.limr-toolbar-readonly-hint { margin: 4px 8px 5px; font-size: 11px; line-height: 1.6; color: var(--limr-secondary, #7b756e); white-space: nowrap; text-align: start; }
|
|
424
447
|
.limr-highlight-settings fieldset { border: 0; padding: 0; margin: 0 0 12px; min-width: 0; }
|
|
425
448
|
.limr-highlight-settings legend { margin-bottom: 7px; padding: 0; color: var(--limr-secondary, #7b756e); font-size: 11px; }
|
|
426
449
|
.limr-highlight-colors { display: flex; flex-wrap: wrap; justify-content: flex-start; gap: 9px; }
|
|
@@ -429,8 +452,9 @@
|
|
|
429
452
|
.limr-color-swatch[aria-pressed='true'] { outline-color: var(--swatch); }
|
|
430
453
|
.limr-color-swatch svg { width: 15px; height: 15px; color: var(--limr-text, #3d3a36); opacity: 0; }
|
|
431
454
|
.limr-color-swatch[aria-pressed='true'] svg { opacity: 1; }
|
|
432
|
-
.limr-highlight-styles { display:
|
|
433
|
-
.limr-highlight-styles button { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 6px
|
|
455
|
+
.limr-highlight-styles { display: flex; flex-wrap: wrap; justify-content: flex-start; align-items: flex-start; gap: 5px; }
|
|
456
|
+
.limr-highlight-styles button { display: flex; flex: 0 0 auto; min-width: 48px; flex-direction: column; align-items: center; gap: 4px; padding: 6px 8px; border: 1px solid transparent; border-radius: 4px; background: transparent; font-size: 11px; white-space: nowrap; }
|
|
457
|
+
.limr-highlight-style-label { text-align: center; }
|
|
434
458
|
.limr-highlight-styles button svg { width: 21px; height: 21px; }
|
|
435
459
|
.limr-highlight-styles [aria-pressed='true'] { color: var(--limr-toolbar-accent, var(--limr-accent, #8a7865)); border-color: currentColor; }
|
|
436
460
|
.limr-action-error { display: block; max-width: 300px; padding: .5rem; color: #b45858; }
|
|
@@ -449,3 +473,62 @@
|
|
|
449
473
|
.limr-prose pre { white-space: pre-wrap; }
|
|
450
474
|
}
|
|
451
475
|
}
|
|
476
|
+
|
|
477
|
+
@layer limr { .limr-stream-block { display: contents; } }
|
|
478
|
+
|
|
479
|
+
@layer limr {
|
|
480
|
+
.limr-mermaid-display { padding: 0; overflow: hidden; }
|
|
481
|
+
.limr-mermaid-loading { display: grid; place-items: center; min-height: 80px; }
|
|
482
|
+
.limr-mermaid-viewport { overflow: auto; max-width: 100%; max-height: var(--limr-mermaid-max-height, min(70vh, 36rem)); padding: .75rem 1rem; box-sizing: border-box; scrollbar-width: thin; scrollbar-color: var(--limr-mermaid-scrollbar, #87837866) transparent; cursor: grab; user-select: none; -webkit-user-select: none; }
|
|
483
|
+
.limr-mermaid-viewport * { user-select: none; -webkit-user-select: none; }
|
|
484
|
+
.limr-mermaid-viewport[data-dragging] { cursor: grabbing; }
|
|
485
|
+
.limr-mermaid-viewport:focus-visible { outline-offset: -2px; }
|
|
486
|
+
.limr-mermaid-viewport::-webkit-scrollbar { width: 5px; height: 5px; }
|
|
487
|
+
.limr-mermaid-viewport::-webkit-scrollbar-track,.limr-mermaid-viewport::-webkit-scrollbar-corner { background: transparent; }
|
|
488
|
+
.limr-mermaid-viewport::-webkit-scrollbar-thumb { background: var(--limr-mermaid-scrollbar, #87837866); border-radius: 8px; }
|
|
489
|
+
.limr-mermaid-viewport::-webkit-scrollbar-button { display: none; }
|
|
490
|
+
.limr-mermaid-viewport[hidden],.limr-prose .limr-mermaid-source[hidden] { display: none; }
|
|
491
|
+
.limr-mermaid-canvas { margin-inline: auto; min-width: 0; transition: width var(--limr-motion) var(--limr-ease); }
|
|
492
|
+
.limr-mermaid-canvas > svg { display: block; width: 100%; height: auto; max-width: none !important; }
|
|
493
|
+
.limr-prose .limr-mermaid-source { background: transparent; padding: .75rem 1rem; }
|
|
494
|
+
.limr-mermaid-error { color: var(--limr-secondary); font-size: .8em; margin-bottom: .5rem; }
|
|
495
|
+
.limr-mermaid-error button { border: 0; background: transparent; text-decoration: underline; }
|
|
496
|
+
.limr-block-tools { position: absolute; inset-block-start: .45rem; inset-inline-end: .5rem; display: flex; align-items: center; padding: 2px; gap: 1px; border-radius: 9px; background: #ffffff99; backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); box-shadow: 0 1px 5px #29231d0a, inset 0 0 0 1px #ffffff80; color: #6b6760; z-index: 1; opacity: 0; transition: opacity var(--limr-motion-fast) var(--limr-ease); }
|
|
497
|
+
.limr-mermaid-block:hover .limr-block-tools,.limr-mermaid-block:focus-within .limr-block-tools { opacity: 1; }
|
|
498
|
+
@media (hover: none) { .limr-block-tools { opacity: 1; } }
|
|
499
|
+
.limr-block-tools > button { display: inline-grid; place-items: center; position: relative; width: 28px; height: 28px; padding: 0; border: 0; border-radius: 7px; background: transparent; color: inherit; transition: color var(--limr-motion-fast) var(--limr-ease), background-color var(--limr-motion-fast) var(--limr-ease), opacity var(--limr-motion-fast) var(--limr-ease); }
|
|
500
|
+
.limr-block-tools > button > svg { width: 19px; height: 19px; }
|
|
501
|
+
.limr-block-tools > button:enabled:hover { background: #87837822; color: #2c2925; }
|
|
502
|
+
.limr-block-tools > button:enabled:active { background: #87837838; }
|
|
503
|
+
.limr-block-tools > button[aria-pressed='true'] { color: var(--limr-heading); }
|
|
504
|
+
[data-limr-theme='dark'] .limr-block-tools { background: #37332dcc; box-shadow: 0 1px 5px #0002, inset 0 0 0 1px #ffffff10; color: #c7c0b6; }
|
|
505
|
+
[data-limr-theme='dark'] .limr-block-tools > button:enabled:hover { color: #f0ebe4; }
|
|
506
|
+
@media (prefers-color-scheme: dark) {
|
|
507
|
+
[data-limr-theme='auto'] .limr-block-tools { background: #37332dcc; box-shadow: 0 1px 5px #0002, inset 0 0 0 1px #ffffff10; color: #c7c0b6; }
|
|
508
|
+
[data-limr-theme='auto'] .limr-block-tools > button:enabled:hover { color: #f0ebe4; }
|
|
509
|
+
}
|
|
510
|
+
.limr-callout { border-color: var(--limr-callout-color); background: color-mix(in srgb, var(--limr-callout-color) 8%, transparent); }
|
|
511
|
+
.limr-callout-title { display: flex; align-items: center; gap: .5em; color: var(--limr-callout-color); text-transform: none; letter-spacing: .02em; font-size: .9em; }
|
|
512
|
+
.limr-callout-title > svg { width: 18px; height: 18px; flex: none; }
|
|
513
|
+
.limr-callout-title > span { flex: 1; }
|
|
514
|
+
.limr-callout-toggle { border: 0; border-radius: 5px; background: transparent; padding: 2px; display: inline-flex; }
|
|
515
|
+
.limr-callout-toggle:hover { background: color-mix(in srgb, var(--limr-callout-color) 12%, transparent); }
|
|
516
|
+
.limr-callout-toggle > svg { width: 18px; height: 18px; transition: transform var(--limr-motion) var(--limr-ease); }
|
|
517
|
+
.limr-callout-toggle[aria-expanded='false'] > svg { transform: rotate(-90deg); }
|
|
518
|
+
.limr-callout > .limr-collapse > .limr-collapse-inner > .limr-block-content { min-height: 0; }
|
|
519
|
+
@media (prefers-reduced-motion: reduce) { .limr-mermaid-canvas,.limr-block-tools,.limr-block-tools > button,.limr-callout-toggle > svg { transition: none; } }
|
|
520
|
+
@media print {
|
|
521
|
+
.limr-mermaid-viewport { max-height: none; overflow: visible; }
|
|
522
|
+
.limr-mermaid-canvas { width: 100% !important; }
|
|
523
|
+
.limr-block-tools { display: none; }
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
@layer limr {
|
|
528
|
+
[data-limr-kp] { white-space: nowrap; text-align: start; text-wrap: nowrap !important; }
|
|
529
|
+
[data-limr-kp][data-limr-kp-stale='clip'] { overflow-x: clip; }
|
|
530
|
+
[data-limr-kp][data-limr-kp-stale='wrap'] { white-space: normal; text-wrap: wrap !important; }
|
|
531
|
+
@media print { [data-limr-kp][data-limr-kp-stale] { overflow: visible; white-space: normal; text-wrap: wrap !important; } }
|
|
532
|
+
.limr-kp-glue { display: inline-block; font-size: 0; line-height: 0; white-space: pre; }
|
|
533
|
+
.limr-kp-hyphen::before { content: '-'; }
|
|
534
|
+
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ComponentType, ReactNode, CSSProperties } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { n as SyntaxExtension, M as MarkdownNode, A as Annotation, b as AnnotationOccurrence, g as HighlightData, m as SourceRange, j as SelectionSnapshot, f as DocumentSnapshot, K as KnuthPlassOptions, c as DocumentEngine } from './knuth-plass-C8mgFm7W.js';
|
|
4
4
|
|
|
5
5
|
interface NodeRendererProps {
|
|
6
6
|
node: MarkdownNode;
|
|
7
7
|
children: ReactNode;
|
|
8
8
|
pending: boolean;
|
|
9
|
+
/** True only for this unclosed construct, not every child of a growing block. */
|
|
10
|
+
incomplete?: boolean;
|
|
9
11
|
source: string;
|
|
10
12
|
/** Render an existing subtree without parsing strings again. */
|
|
11
13
|
renderChildren: (nodes: readonly MarkdownNode[], options?: {
|
|
@@ -23,6 +25,18 @@ interface TextToken {
|
|
|
23
25
|
style?: CSSProperties;
|
|
24
26
|
}
|
|
25
27
|
type NodeRenderer = ComponentType<NodeRendererProps>;
|
|
28
|
+
interface RendererOverride {
|
|
29
|
+
/** AST type, or the registered name of a component. */
|
|
30
|
+
type: string;
|
|
31
|
+
component: NodeRenderer;
|
|
32
|
+
/** Limits the replacement; false falls back to the next match or built-in. */
|
|
33
|
+
when?: (node: MarkdownNode, context: {
|
|
34
|
+
pending: boolean;
|
|
35
|
+
incomplete: boolean;
|
|
36
|
+
ancestors: readonly MarkdownNode[];
|
|
37
|
+
}) => boolean;
|
|
38
|
+
}
|
|
39
|
+
type TypographyMode = 'knuth-plass' | 'standard' | 'justify';
|
|
26
40
|
type AnnotationPresentation = 'preview' | 'expanded' | 'sidebar';
|
|
27
41
|
interface AnnotationExpansion {
|
|
28
42
|
/** Grow beside the marker, or center within the page's visual viewport. */
|
|
@@ -69,9 +83,12 @@ interface AnnotationOverlayCardProps<T = unknown> {
|
|
|
69
83
|
}
|
|
70
84
|
interface AnnotationType<T = unknown> {
|
|
71
85
|
type: string;
|
|
72
|
-
|
|
86
|
+
/** Resolve labels from the reader locale without recreating the plugin. */
|
|
87
|
+
label: string | ((locale: LiMRLocale) => string);
|
|
73
88
|
icon?: ReactNode | ComponentType;
|
|
74
89
|
color?: string;
|
|
90
|
+
/** Highlight referenced text in the marker color while hovered or active. */
|
|
91
|
+
highlight?: 'hover' | false;
|
|
75
92
|
Content?: ComponentType<AnnotationContentProps<T>>;
|
|
76
93
|
Marker?: ComponentType<{
|
|
77
94
|
annotation: Annotation<T>;
|
|
@@ -108,18 +125,34 @@ interface AnnotationType<T = unknown> {
|
|
|
108
125
|
}) => Promise<T>;
|
|
109
126
|
}
|
|
110
127
|
interface RendererExtension extends SyntaxExtension {
|
|
128
|
+
streaming?: {
|
|
129
|
+
/** Optional module-owned intermediate state. Omit to render progressively. */
|
|
130
|
+
Loading?: NodeRenderer;
|
|
131
|
+
/** Last configured effect wins. The reader may override it. */
|
|
132
|
+
effect?: StreamEffect | false;
|
|
133
|
+
};
|
|
111
134
|
/** Register document parameter defaults. Plugins extend RendererParameters
|
|
112
135
|
* through TypeScript declaration merging and read them with useRendererParameter. */
|
|
113
136
|
parameters?: Readonly<RendererParameters>;
|
|
114
137
|
components?: Readonly<Record<string, NodeRenderer>>;
|
|
138
|
+
/** Ordered, reader-local overrides. Last matching entry wins. */
|
|
139
|
+
rendererOverrides?: readonly RendererOverride[];
|
|
115
140
|
annotationTypes?: readonly AnnotationType<any>[];
|
|
116
141
|
actions?: readonly SelectionAction[];
|
|
117
142
|
}
|
|
143
|
+
/** Animation applies to newly appended text and newly appearing blocks. CSS can style the supplied
|
|
144
|
+
* class; keyframes/timing use the Web Animations API. No source rewriting. */
|
|
145
|
+
interface StreamEffect {
|
|
146
|
+
className?: string;
|
|
147
|
+
keyframes?: Keyframe[];
|
|
148
|
+
timing?: KeyframeAnimationOptions;
|
|
149
|
+
}
|
|
118
150
|
/** Open to module augmentation; parameter names and semantics belong to plugins. */
|
|
119
151
|
interface RendererParameters {
|
|
120
152
|
}
|
|
121
153
|
interface DocumentCommands {
|
|
122
|
-
/** Content annotations get a shared highlight unless
|
|
154
|
+
/** Content annotations get a shared highlight unless disabled or their type
|
|
155
|
+
* uses hover highlighting. An explicit appearance still creates a saved highlight. */
|
|
123
156
|
addAnnotation: (annotation: Annotation, options?: {
|
|
124
157
|
highlight?: HighlightData | false;
|
|
125
158
|
edit?: boolean;
|
|
@@ -172,6 +205,9 @@ interface SelectionAction {
|
|
|
172
205
|
render?: (context: ToolbarActionContext) => ReactNode;
|
|
173
206
|
}
|
|
174
207
|
interface LiMRLocale {
|
|
208
|
+
/** Optional language tag for plugin-owned translations. */
|
|
209
|
+
language?: string;
|
|
210
|
+
citation?: string;
|
|
175
211
|
contents: string;
|
|
176
212
|
annotations: string;
|
|
177
213
|
expand: string;
|
|
@@ -207,6 +243,7 @@ interface LiMRLocale {
|
|
|
207
243
|
highlightWavy: string;
|
|
208
244
|
hideHighlight: string;
|
|
209
245
|
showHighlight: string;
|
|
246
|
+
inlineHighlightHint?: string;
|
|
210
247
|
deleteAnnotations: string;
|
|
211
248
|
deleteAnnotationsHint: string;
|
|
212
249
|
}
|
|
@@ -219,11 +256,18 @@ interface ViewConfiguration {
|
|
|
219
256
|
/** CSS skin name, applied to every view including portalled controls. */
|
|
220
257
|
appearance?: string;
|
|
221
258
|
components?: Readonly<Record<string, NodeRenderer>>;
|
|
259
|
+
/** Reader rules take precedence over extension rules and component mappings. */
|
|
260
|
+
rendererOverrides?: readonly RendererOverride[];
|
|
222
261
|
annotationTypes?: readonly AnnotationType<any>[];
|
|
223
262
|
annotations?: readonly Annotation[];
|
|
224
263
|
theme?: 'light' | 'dark' | 'auto';
|
|
225
264
|
/** Justify prose paragraphs and list items, leaving the last line start-aligned. */
|
|
226
265
|
justify?: boolean;
|
|
266
|
+
/** TeX Knuth–Plass, standard (default), or browser justification.
|
|
267
|
+
* pretty/normal remain compatible native-CSS aliases. */
|
|
268
|
+
typography?: TypographyMode | 'pretty' | 'normal';
|
|
269
|
+
knuthPlass?: KnuthPlassOptions;
|
|
270
|
+
streamEffect?: StreamEffect | false;
|
|
227
271
|
locale?: Partial<LiMRLocale>;
|
|
228
272
|
scrollRoot?: HTMLElement | null;
|
|
229
273
|
scrollOffset?: number;
|
|
@@ -252,6 +296,7 @@ declare function useRendererParameter<K extends keyof RendererParameters>(name:
|
|
|
252
296
|
declare function useDocument(): DocumentSnapshot;
|
|
253
297
|
declare function useDocumentSelector<R>(select: (snapshot: DocumentSnapshot) => R): R;
|
|
254
298
|
declare function useSelection(): SelectionSnapshot | null;
|
|
299
|
+
declare function useLocale(): LiMRLocale;
|
|
255
300
|
declare function useAnnotation(id: string): Annotation | undefined;
|
|
256
301
|
declare function useAnnotations(): {
|
|
257
302
|
annotations: ReadonlyMap<string, Annotation<unknown>>;
|
|
@@ -307,4 +352,4 @@ declare module '@linloir/limr' {
|
|
|
307
352
|
}
|
|
308
353
|
declare function translations(options?: TranslationsOptions): RendererExtension;
|
|
309
354
|
|
|
310
|
-
export { type ActionContext as A, type DocumentCommands as D, type LiMRLocale as L, Markdown as M, type NodeRenderer as N, type RendererExtension as R, type SelectionAction as S, type TextToken as T, type ViewConfiguration as V, type AnnotationContentProps as a, type AnnotationExpansion as b, type AnnotationOverlayCardProps as c, type AnnotationPresentation as d, type AnnotationSidebarPreviewProps as e, type AnnotationType as f, DocumentProvider as g, type DocumentProviderProps as h, type MarkdownProps as i, type NodeRendererProps as j, type
|
|
355
|
+
export { type ActionContext as A, useLocale as B, useRendererParameter as C, type DocumentCommands as D, useSelection as E, type LiMRLocale as L, Markdown as M, type NodeRenderer as N, type RendererExtension as R, type SelectionAction as S, type TextToken as T, type ViewConfiguration as V, type AnnotationContentProps as a, type AnnotationExpansion as b, type AnnotationOverlayCardProps as c, type AnnotationPresentation as d, type AnnotationSidebarPreviewProps as e, type AnnotationType as f, DocumentProvider as g, type DocumentProviderProps as h, type MarkdownProps as i, type NodeRendererProps as j, type RendererOverride as k, type RendererParameters as l, type StreamEffect as m, type StyledProps as n, type ToolbarActionContext as o, type TranslationMode as p, type TranslationsOptions as q, type TypographyMode as r, useAnnotation as s, translations as t, useAnchor as u, useAnnotationState as v, useAnnotations as w, useDocument as x, useDocumentSelector as y, useLiMR as z };
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# LiMR 文档 / Documentation
|
|
2
|
+
|
|
3
|
+
[简体中文](zh-CN/README.md) · [English](en/README.md)
|
|
4
|
+
|
|
5
|
+
文档按阅读、接入与扩展开发场景组织;两种语言使用相同的文件结构,每页顶部可以切换语言。
|
|
6
|
+
|
|
7
|
+
Documentation is organized around writing content, integrating LiMR, and developing extensions. Both languages use the same file structure, with a language switch on each page.
|
|
8
|
+
|
|
9
|
+
- [中文入门](zh-CN/getting-started.md) / [Getting started](en/getting-started.md)
|
|
10
|
+
- [默认语法](zh-CN/syntax.md) / [Default syntax](en/syntax.md)
|
|
11
|
+
- [AI Skills 下载与导入](zh-CN/ai-skills.md) / [Download and install AI Skills](en/ai-skills.md)
|
|
12
|
+
|
|
13
|
+
[项目首页](../README.md) · [Project overview](../README.en.md)
|
package/docs/api.md
CHANGED
|
@@ -1,161 +1,8 @@
|
|
|
1
|
-
# API
|
|
1
|
+
# API 参考 / API reference
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
文档已整理到语言目录。This guide now lives in the language directories.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- [简体中文](zh-CN/api.md)
|
|
6
|
+
- [English](en/api.md)
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
| --- | --- |
|
|
9
|
-
| `source` | `''`,未经预处理的 Markdown 字符串 |
|
|
10
|
-
| `streaming` | `false`,追加模式下允许复用稳定区块 |
|
|
11
|
-
| `documentId` | 自动生成且与 SSR hydration 一致;显式传入时应在页面内唯一 |
|
|
12
|
-
| `extensions` | `standard()`,完整扩展列表 |
|
|
13
|
-
| `plugins` | 追加到默认模块或显式 extensions 的第三方扩展 |
|
|
14
|
-
| `parameters` | 模块注册的文档级参数;例如 `{ translationMode: 'both' }`,覆盖各段译文模式 |
|
|
15
|
-
| `appearance` | `classic`;CSS 样式包作用域,包含 portal 浮层 |
|
|
16
|
-
| `components` | 覆盖 AST 节点类型或特殊标签名对应的组件 |
|
|
17
|
-
| `annotationTypes` | 追加或覆盖同 type 的角注显示定义 |
|
|
18
|
-
| `annotations` | 外部控制的 annotation 数组,包括角注与 `type: 'highlight'` 的高亮;不传时由命令维护内部状态 |
|
|
19
|
-
| `onAnnotationsChange` | 受控模式下接收下一份数组,应用负责回传 |
|
|
20
|
-
| `theme` | `light`;也可以是 `dark` / `auto` |
|
|
21
|
-
| `justify` | `false`;开启后段落和列表两端对齐,末行保持起始侧对齐 |
|
|
22
|
-
| `locale` | `english`;可以传 `chinese` 或部分文字覆盖 |
|
|
23
|
-
| `scrollRoot` | 窗口;传入内部滚动容器 DOM 节点可切换 |
|
|
24
|
-
| `scrollOffset` | `24`,标题、页内链接及脚注定位时留出的顶部距离 |
|
|
25
|
-
| `onSelectionChange` | 接收三种选区结果或 `null` |
|
|
26
|
-
| `onAnnotationClick` / `onAnnotationHover` | 接收 occurrence;hover 离开时为 `null` |
|
|
27
|
-
| `onDocumentChange` | 输入或扩展变更后接收新快照 |
|
|
28
|
-
|
|
29
|
-
数组、扩展和数据对象按不可变数据使用。发生变更时传新对象,静态配置保持引用稳定。更换整篇独立文档时可以给 Provider/Reader 换一个 React `key`,从而清除上篇文章的局部交互和任务。
|
|
30
|
-
|
|
31
|
-
`useRendererParameter(name)` 读取模块通过 `RendererExtension.parameters` 注册的默认值及宿主覆盖,类型由可扩展的 `RendererParameters` 接口提供。参数更新不会重新解析 Markdown。模块可用 `InlineMarker` 注入仅执行操作的角标,再通过 `renderChildren(nodes, { trailing })` 将其放在内容末尾;它与 `Annotation` 的卡片生命周期相互独立。详见[扩展指南](extensions.md#分段译文与全局参数)。
|
|
32
|
-
|
|
33
|
-
`<Reader source={source} justify />`、独立 `<Markdown source={source} justify />` 或 `<DocumentProvider justify>` 均可开启两端对齐;Provider 中的 `<Markdown justify={false} />` 可显式覆盖。开关可动态更新,选区、高亮和侧栏位置随重新排版更新。标题、代码块和表格保留原有对齐方式。
|
|
34
|
-
|
|
35
|
-
`Reader` 额外接受 `toc`、`sidebar`、`fillHeight`(均默认 true)、`sidebarBreakpoint`(默认 1120,按容器宽度判断)、`actions`、`followOutput`、`className`、`style`。`fillHeight` 让短正文补足当前位置到滚动视口底部的可用高度,默认预留 24px;可通过 `--limr-reader-bottom-space` 为页脚留位,或用 `--limr-reader-min-height` 覆盖最小高度。内嵌小卡片可设 `fillHeight={false}`。计算使用滚动坐标中的固定起点,普通滚动不会改变最小高度。`Markdown` 额外接受 `interactive`(默认 true,控制选区监听)、`followOutput` 和样式属性。
|
|
36
|
-
|
|
37
|
-
## 布局和定位
|
|
38
|
-
|
|
39
|
-
正文内同页 `#fragment` 链接(包括标准脚注)与目录使用同一个非线性滚动实现,遵守 `scrollRoot`、`scrollOffset` 和减少动态效果设置。滚轮、触摸、指针按下或滚动按键会中断动画;带修饰键、下载及外部链接保持原有行为。目标在当前正文内查找,多个阅读器不会相互跳转。
|
|
40
|
-
|
|
41
|
-
默认组合约定:目录、正文和侧栏在同一个滚动容器内。正文与阅读区域最小高度共同决定布局,同步侧栏拉伸至所在网格行的高度,卡片在内部绝对定位并裁切溢出,不参与父组件高度计算。保持侧栏和正文处于同一个未缩放的 CSS 坐标系,不额外添加独立滚动或 transform。自定义组合应像下面一样使用同一行的 grid,并给正文提供裁切容器;非 grid 布局需要由宿主给侧栏明确高度。ResizeObserver、图片加载、字体加载及容器变化会重新测量几何;普通滚动使用缓存的标题位置。
|
|
42
|
-
|
|
43
|
-
```tsx
|
|
44
|
-
const [scrollRoot, setScrollRoot] = useState<HTMLDivElement | null>(null);
|
|
45
|
-
|
|
46
|
-
<div ref={setScrollRoot} style={{ height: '80vh', overflowY: 'auto' }}>
|
|
47
|
-
<DocumentProvider source={source} scrollRoot={scrollRoot}>
|
|
48
|
-
<div style={{ display: 'grid', gridTemplateColumns: '180px minmax(0,1fr) 260px', gap: 24, alignItems: 'start' }}>
|
|
49
|
-
<TableOfContents style={{ position: 'sticky', top: 0, maxHeight: '75vh' }} />
|
|
50
|
-
<main className="limr-reader-content" style={{ alignSelf: 'stretch', overflow: 'clip' }}><Markdown /></main>
|
|
51
|
-
<AnnotationSidebar />
|
|
52
|
-
</div>
|
|
53
|
-
<SelectionToolbar actions={actions} />
|
|
54
|
-
</DocumentProvider>
|
|
55
|
-
</div>
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
同步侧栏没有独立滚动条,也不接受滚轮、触摸或聚焦引起的列表滚动。悬浮正文角标时移动侧栏;悬浮侧栏卡片时锁住当前卡片的命中位置,在边界允许时让正文平滑位移至关联位置。其他条目根据实际高度重新排列,收起时无需移开鼠标就会归位。正文位移不改变布局高度,测量锚点时扣除动画中的实时位移,避免位置反馈循环。展开/收起保留交互来源与 hover;点击空白处清除卡片与角标的激活状态并恢复正文位置,Esc 收起并把焦点还给角标。长回答的正文仍可在卡片内滚动,隐藏原生滚动条,卡片高度受侧栏整体可用高度限制。
|
|
59
|
-
|
|
60
|
-
在独立抽屉等场景使用 `<AnnotationSidebar sync="none" />`。它保留正常列表流,不读取正文的对齐位置,也展示没有锚点的角注,宿主自行约束高度与滚动。`positioner(items, {activeId, gap, height})` 可以替换同步侧栏的对齐算法;items 含 occurrence ID、相对侧栏的关联文本起始行顶部(`anchorTop`)和卡片高度,`height` 是可用侧栏高度(尚未测量时为 undefined),返回 ID 到顶部距离的 Map。绑定文本范围的脚注按片段第一个可见字符所在行对齐,末尾角标位置不变;内嵌引用等仅有角标的脚注按角标对齐。默认优先对齐 hover/激活条目;无激活条目时跟随正文当前阅读位置。空间足够时,末尾卡片向上借用留白,所有卡片保持顺序和间隔并完整显示;容纳不下时优先保留当前卡片,其他条目允许越过边界并渐隐。边界处优先保证卡片可见,不为精确对齐强行平移正文。
|
|
61
|
-
|
|
62
|
-
`renderCard({occurrence, active, activate})` 替换侧栏卡片外壳;单个角注的内容通常通过 `AnnotationType.Content` 替换。需要完全自主的布局时,用 `useAnchor(occurrenceId)` 获得实时角标元素、视口矩形和正文矩形,再自行放置组件。
|
|
63
|
-
|
|
64
|
-
`AnnotationOverlay` 接受 `disabled`、`portalRoot`、`previewWidth`(340)和 `maxHeight`(`80dvh`,模块未指定展开上限时使用)。浮层默认通过 portal 放入 body。模块通过 `overlay.Card` 渲染完整卡片,通过 `overlay.previewWidth` 和 `overlay.expand` 声明预览宽度、展开能力及布局;LiMR 管理挂载、位置、焦点和动画。预览贴近角标,展开可以原地增高或在整个页面的可视区域居中。默认 Citation 不展开,点击会固定预览。模块可配置尺寸或按 annotation 动态返回策略,详见[扩展指南](extensions.md)。所有模式均支持触摸、Esc、外部点击关闭和焦点恢复,居中模式另有遮罩与焦点约束。打开时焦点落到对话框容器,Tab 再进入模块提供的控件。高 z-index 宿主可覆盖 `--limr-overlay-z` / `--limr-toolbar-z`。
|
|
65
|
-
|
|
66
|
-
`AnnotationCard` 是模块可选用的默认卡片布局,接收 `AnnotationOverlayCardProps` 以及必填的 `title: ReactNode`、可选的 `bodyLabel`。它提供标题栏、展开/收起/关闭按钮和滚动区域;模块也可以完全自行渲染这些部分。旧模块只提供 `Content` 时仍可使用兼容布局,其标题来自类型的 `label`。
|
|
67
|
-
|
|
68
|
-
`TableOfContents` 接受 `title`、`maxDepth`、`followActive`、`topInset`(24)、`updateHash`(false)和样式属性。独立使用时给它或父容器设定可用高度。标题位于滚动区之外;列表上下仅在实际溢出时渐隐,默认跟随留白让当前章节位于渐隐区之外。手动滚动目录会短暂暂停自动跟随;页面底部将最后一个标题视为 active。
|
|
69
|
-
|
|
70
|
-
`Reader` 的宽屏目录自动适配窗口或 `scrollRoot` 的可用高度。正文滚到末尾及页脚出现时,目录外框保持吸顶,只有内部列表滚动;目录不会撑高正文或增加页面滚动范围。整个 Reader 离开可视区域后,目录隐藏且不可聚焦,避免覆盖后续页面内容。playground 使用同一默认布局。
|
|
71
|
-
|
|
72
|
-
`Reader` 的窄屏目录使用整行按钮,右侧箭头随展开状态旋转。目录内容持续挂载,展开/收起从当前高度连续过渡,快速点击可随时反转;隐藏内容不可获得焦点。
|
|
73
|
-
|
|
74
|
-
## 角注数据与命令
|
|
75
|
-
|
|
76
|
-
```tsx
|
|
77
|
-
const [annotations, setAnnotations] = useState<Annotation[]>([
|
|
78
|
-
{
|
|
79
|
-
id: 'paper',
|
|
80
|
-
type: 'citation',
|
|
81
|
-
data: { title: '论文', url: 'https://example.com', description: '**摘要**' },
|
|
82
|
-
anchors: [{ range: { start: 10, end: 18 }, quote: original.slice(10, 18) }],
|
|
83
|
-
},
|
|
84
|
-
]);
|
|
85
|
-
|
|
86
|
-
<Reader source={original} annotations={annotations} onAnnotationsChange={setAnnotations} />
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
一个 annotation 可包含多个 anchors;一个嵌入式 `<ref ids="a,b" />` occurrence 可显示多个 annotation。单个锚点可以用 `range` 简写。范围采用 UTF-16 偏移、右端不包含。可加 `documentId` / `revision` 验证所属文档和版本。持久化时推荐保存 `quote`:原范围失配时只接受唯一的同文匹配,歧义或找不到时不猜测位置。LiMR 不负责协同编辑中的锚点变换;复杂编辑场景由应用更新范围。
|
|
90
|
-
|
|
91
|
-
`useLiMR()` 提供:
|
|
92
|
-
|
|
93
|
-
- `addAnnotation(annotation, { highlight?, edit? })`、`updateAnnotation(id, patch)`、`removeAnnotation(id)`。
|
|
94
|
-
- `removeAnnotationsAt(ranges)`:删除与这些原文范围完全匹配的绑定,保留 annotation 的其他 anchors。
|
|
95
|
-
- `editAnnotation(id | null)`:进入指定 annotation 的编辑态并激活;传 null 结束编辑。
|
|
96
|
-
- `activateAnnotation(annotationIdOrOccurrenceId | null)`、`scrollToAnnotation(occurrenceId)`、`clearSelection()`。
|
|
97
|
-
- `scrollToHeading(headingId)`、`invalidateAnnotation(id)`。
|
|
98
|
-
- `document`:底层 `DocumentEngine`,可读取/订阅快照;`signal` 随阅读器卸载而取消。
|
|
99
|
-
|
|
100
|
-
脚注、旁注、笔记、提问与高亮都使用 `Annotation`,通过同一组命令绑定原文。添加有锚点的内容 annotation 时,默认同时创建或复用该片段的高亮;传 `highlight: false` 可跳过,传 `HighlightData` 可指定外观。`edit: true` 同时激活并进入编辑态,模块的 `Content` 通过 `editing` 决定显示表单。编辑草稿由模块管理,建议存入 annotation data,以便跨侧栏/浮层切换时保留。
|
|
101
|
-
|
|
102
|
-
高亮格式为 `{ id, type: 'highlight', anchors, data: { color?, style?, hidden? } }`,也支持 `range` 简写。`HighlightData.style` 支持 `background`、`underline`、`dotted`、`wavy`,默认背景色。高亮不产生角标和侧栏条目;`hidden` 只隐藏装饰,保留绑定。激活一个 annotation 时,其他片段的高亮暂时隐藏,结束激活后恢复。
|
|
103
|
-
|
|
104
|
-
语义范围保留在 React 节点中;装饰绘制层独立维护,不拆分或替换正文的文字 DOM。高亮绘制在文字后方,重叠时后添加的高亮优先命中。`updateAnnotation` 保留 ID 和叠放顺序,`addAnnotation` 替换同 ID 时会将它移到顶层。`onHighlightHover(annotation | null)` 和 `onHighlightClick(annotation)` 接收 `Annotation<HighlightData>`。点击高亮会打开工具栏,`context.highlight` 指向该 annotation,`context.annotations` 包含该片段的全部 annotation,`context.selection` 表示它的原文范围。
|
|
105
|
-
|
|
106
|
-
## 选区和动作
|
|
107
|
-
|
|
108
|
-
`useSelection()` 与 `onSelectionChange` 返回同一结构:
|
|
109
|
-
|
|
110
|
-
| 字段 | 意义 |
|
|
111
|
-
| --- | --- |
|
|
112
|
-
| `text` | 浏览器选中的可见文本 |
|
|
113
|
-
| `raw` | 映射到原始输入的范围与原文 |
|
|
114
|
-
| `syntax` | 两端分别吸附紧邻的标准 Markdown 标记,忽略间隙空白;不增加未选中的普通文字 |
|
|
115
|
-
| `blocks` | 所在的完整段落、标题、列表项等区块,可含多个范围 |
|
|
116
|
-
| `quality` | `exact`、`atomic` 或 `unmapped` |
|
|
117
|
-
| `settled` | 拖动调整选区时为 false |
|
|
118
|
-
| `documentId` / `revision` | 选区对应的文档版本 |
|
|
119
|
-
|
|
120
|
-
`syntax` 不扩展普通文本。开头只吸附该侧紧邻的起始标记,结尾只吸附结束标记,嵌套标记逐层吸附。例如从 `***first word***` 选择 `first` 得到 `***first`,选择 `word` 得到 `word***`,选择中间的 `st wo` 则不变。列表、标题、强调、链接、代码和公式使用同一规则;自定义语法块的内部 Markdown 正常处理,外层标签或指令围栏不参与吸附。
|
|
121
|
-
|
|
122
|
-
公式、图片属于原子节点,浏览器选区映射得到的 `raw` 已覆盖整个节点,并标为 `atomic`。行内代码及围栏代码支持部分选择,只有选到对应内容边界时 `syntax` 才吸附该侧定界符。默认高亮、笔记、提问和助手插件使用 `syntax.ranges` 创建 annotation;原始 Annotation API 仍精确保留调用者传入的范围。完整覆盖行内代码的高亮包含两端留白;吸附了结束标记的脚注位于格式节点外,代码中间的脚注仍跟随精确字符位置。
|
|
123
|
-
|
|
124
|
-
自定义组件应渲染提供的 `children` / `renderChildren` / `renderValue` 来保留正文映射,或声明原子选择。生成的 UI 文字不应冒充原文;可加 `data-limr-ignore` 排除按钮和标签。
|
|
125
|
-
|
|
126
|
-
`Reader` 和 `SelectionToolbar` 默认提供 `highlightPalette()`、`highlightSelection()`、`copyText()`、`deleteAnnotations()`,并追加插件动作。显式 `actions` 替换默认列表,按 ID 覆盖插件同名动作;`actions={[]}` 只去掉内置动作,插件动作仍保留。`Markdown` 不挂载菜单,需要应用自行提供 `SelectionToolbar`。
|
|
127
|
-
|
|
128
|
-
`SelectionAction` 必须提供 `id`、`label`、`icon`。`label` 和 `icon` 可通过 `(context) => …` 动态生成。可选字段为 `visible`、`disabled`、`run`、`onHover`、`renderExpanded` 和 `render`。`placement: 'start' | 'end'` 控制两端顺序,`tone: 'danger'` 表示删除类操作。`successLabel` / `successIcon` 在动作成功后显示 2.4 秒,默认原位淡出淡入。建议使用圆润、居中、方形 viewBox 的 SVG;骨架通过固定图标槽统一大小,内置 `ToolIcon` 可用于插件。
|
|
129
|
-
|
|
130
|
-
`run(context)` 支持 async,错误在菜单中显示。`ToolbarActionContext` 包含选区、文档快照、`annotations`、`highlight`、`locale`、文档命令和 `signal`,以及 `busy`、`expanded`、`expand()`、`collapse()`、`run(operation)`。`highlightAppearance` / `setHighlightAppearance()` 管理下一次创建高亮的外观,工具栏中的 `addAnnotation` 会沿用它。这个 signal 的生命期是阅读器,关闭菜单不会中止已开始的请求,卸载会取消。展开面板里的异步操作应使用 `context.run(...)`,复用忙碌状态和错误处理。
|
|
131
|
-
|
|
132
|
-
`renderExpanded(context)` 返回插件自己的面板;悬浮动作自动展开,没有独立的展开指示器。未配置 `run` 时点击动作切换展开;有 `run` 时可自行调用 `expand()`,触屏也使用同一个按钮。聚焦动作后按上下方向键展开并进入面板。`onHover(context)` 可补充悬浮行为。面板可放输入框、颜色或样式选择器,输入时保留选区快照。Escape 先收起面板并归还焦点,再次按下关闭工具栏;左右方向键在工具之间移动。`render(context)` 保留完整自定义单项槽位,`SelectionToolbar render={...}` 可接管整个工具条,这两种完全自绘入口的控件、图标与可访问性由调用方负责。
|
|
133
|
-
|
|
134
|
-
骨架优先在选区下方显示,并预留统一的展开预算;下方不足时选择空间较多的一侧。`SelectionToolbar.maxPanelHeight` 默认 260px,可由 `--limr-toolbar-panel-max-height` 覆盖。实际可用高度通过 `--limr-toolbar-panel-height` 传给面板,正文宽度通过 `--limr-toolbar-panel-width` 控制。骨架负责按内容溢出显示不占布局宽度的 3px 滚动条,插件按正常文档流响应式排版即可,无需再包一层滚动容器。面板不足最大高度时随内容收缩。
|
|
135
|
-
|
|
136
|
-
`highlightPalette({ colors? })` 的色块面板提供七种默认颜色和四种高亮样式,不显示预览文本。`highlightColors` 导出默认色板;`colors` 为 `{ color, label, labelZh? }[]`。`highlightSelection()` 直接添加高亮;只有高亮时隐藏该按钮,有其他注释时变为隐藏/显示高亮。颜色和样式对已有高亮即时应用。`deleteAnnotations()` 删除当前片段的全部绑定,超过一个非高亮 annotation 时,仅悬浮或键盘聚焦删除按钮才展开红色说明,计数不包含高亮。受控 `annotations` 要提供 `onAnnotationsChange` 才能接受修改。
|
|
137
|
-
|
|
138
|
-
## 状态 hooks 和 core
|
|
139
|
-
|
|
140
|
-
`useDocument()` 获取源文档快照;`useAnnotation(id)` 和 `useAnnotations()` 获取合并了源文档、外部数据和插件新增内容的角注数据,后者返回 annotations Map 与 occurrences 数组;`useDocumentSelector(select)` 只订阅选择出的值;`useAnnotationState()` 获取 active/hovered occurrence 和修改方法;`useAnchor(id)` 获取定位信息。频繁变化的布局、滚动、选区和文档使用不同订阅通道。
|
|
141
|
-
|
|
142
|
-
```ts
|
|
143
|
-
import { createDocument, selectionFromRange } from '@linloir/limr/core';
|
|
144
|
-
|
|
145
|
-
const engine = createDocument('', { streaming: true });
|
|
146
|
-
const unsubscribe = engine.subscribe(() => consume(engine.getSnapshot()));
|
|
147
|
-
engine.append('# Heading\n\n');
|
|
148
|
-
engine.append('Content');
|
|
149
|
-
engine.finish();
|
|
150
|
-
unsubscribe();
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
core 默认仅 CommonMark,不依赖 React、DOM 或浏览器全局。需要 GFM 等能力时传入对应的语法扩展。`setExtensions()` 会重新解析全文。`metrics` 提供本次重新解析的字符数、复用区块数和原因;它不是总 CPU 开销的计量器。
|
|
154
|
-
|
|
155
|
-
## 样式与服务端
|
|
156
|
-
|
|
157
|
-
常用 CSS 变量:`--limr-font-text`、`--limr-font-code`、`--limr-font-size`、`--limr-line-height`、`--limr-text`、`--limr-heading`、`--limr-background`、`--limr-accent`、`--limr-content-width`、`--limr-reader-width`、`--limr-sticky-top`。
|
|
158
|
-
|
|
159
|
-
样式放在 `@layer limr`,应用的普通 CSS 可以覆盖它。`appearance` 传播样式包的作用域到各组件和 portal。默认 `styles.css` 附带本地 Maple Mono NF CN 子集;`styles/base.css` 不导入字体。Playground 的 Noto Serif SC 是演示依赖。缓存、主题包和交互样式见[样式指南](styles.md)。
|
|
160
|
-
|
|
161
|
-
服务端和客户端使用相同 source、extensions、documentId 和首次 streaming 值。不要把随后客户端已有的数据提前只传给其中一端。同步 SSR 的公式可用 `await preloadMath()` 预加载;代码始终有可读的源码回退。自动 documentId 使用 React useId,多根 hydration 可使用 React identifierPrefix 或显式唯一 ID。
|
|
8
|
+
[文档目录 / Documentation](README.md)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="480" height="240" viewBox="0 0 480 240" role="img" aria-label="Rendered document">
|
|
2
|
+
<rect width="480" height="240" rx="12" fill="#eef2ec"/>
|
|
3
|
+
<g fill="#364633" font-family="Georgia,serif">
|
|
4
|
+
<text x="32" y="60" font-size="32" font-weight="bold">Markdown</text>
|
|
5
|
+
<text x="32" y="108" font-size="24" font-weight="bold">Readable source</text>
|
|
6
|
+
<text x="32" y="156" font-size="24">• Write</text>
|
|
7
|
+
<text x="32" y="196" font-size="24">• Read</text>
|
|
8
|
+
</g>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="480" height="240" viewBox="0 0 480 240" role="img" aria-label="Markdown source">
|
|
2
|
+
<rect width="480" height="240" rx="12" fill="#f3f1ee"/>
|
|
3
|
+
<g fill="#5a5650" font-family="monospace" font-size="24">
|
|
4
|
+
<text x="32" y="60"># Markdown</text>
|
|
5
|
+
<text x="32" y="108">**Readable source**</text>
|
|
6
|
+
<text x="32" y="156">- Write</text>
|
|
7
|
+
<text x="32" y="196">- Read</text>
|
|
8
|
+
</g>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# English documentation
|
|
2
|
+
|
|
3
|
+
> Reading note: this page uses LiMR syntax and interactive examples. Some content cannot be fully displayed by ordinary Markdown viewers such as GitHub; visit the [GitHub Pages documentation](https://linloir.github.io/LiMR/#/docs/en/README) for the best reading experience.
|
|
4
|
+
|
|
5
|
+
[Documentation](README.md) · [简体中文](../zh-CN/README.md)
|
|
6
|
+
|
|
7
|
+
These pages describe the current repository implementation, including the new streaming, typography, and module capabilities. Check installed exports and types when consuming an npm release. Chinese is the default documentation language; every English page has a matching Chinese page.
|
|
8
|
+
|
|
9
|
+
| What you want to do | Read |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| Install and render a first document | [Getting started](getting-started.md) |
|
|
12
|
+
| Understand modules, included capabilities, and configuration | [Built-in modules](modules.md) |
|
|
13
|
+
| Write Markdown, Mermaid, callouts, highlights, footnotes, and two-colon components | [Default syntax](syntax.md) |
|
|
14
|
+
| Integrate React, typography modes, streaming, custom layouts, or SSR | [Application integration](integration.md) |
|
|
15
|
+
| Persist selections, highlights, citations, and annotations; understand atomic copying | [Annotations and selection](annotations.md) |
|
|
16
|
+
| Add syntax, cards, actions, streaming effects, or conditional renderers | [Extension development](extensions.md) |
|
|
17
|
+
| Build a standalone npm plugin and import it into an app | [Plugin packages](plugin-packages.md) |
|
|
18
|
+
| Customize themes, fonts, KP layout, animation, and diagrams | [Styles and fonts](styles.md) |
|
|
19
|
+
| Look up component props, hooks, and core APIs | [API reference](api.md) |
|
|
20
|
+
| Understand parsing, state, and resource boundaries | [Architecture and behavior](architecture.md) |
|
|
21
|
+
| Understand measurements and validation boundaries | [Performance and limits](performance.md) |
|
|
22
|
+
| Have an AI use LiMR or develop plugins | [AI Skills](ai-skills.md) |
|
|
23
|
+
|
|
24
|
+
Start with `Reader`, split components when a custom layout is needed, and add plugins for application features. Local persistence, import/export, and simulated assistant responses in the Playground belong to the demo application, not to Reader itself.
|
|
25
|
+
|
|
26
|
+
Playground uses standard typography by default; its menu also offers Knuth–Plass and justification, and the streaming demo runs at a slower pace. Refresh opens the default document without restoring the previous article or edits. Explicitly saved drafts can be loaded manually; locale preference persists separately. Docs links open the selected page directly, and switching between Docs and Playground preserves edits during the current session.
|
|
27
|
+
|
|
28
|
+
[Back to README](../../README.en.md) · [Live Playground](https://linloir.github.io/LiMR/)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# AI Skills: download, install, and use
|
|
2
|
+
|
|
3
|
+
> Reading note: this page uses LiMR syntax and interactive examples. Some content cannot be fully displayed by ordinary Markdown viewers such as GitHub; visit the [GitHub Pages documentation](https://linloir.github.io/LiMR/#/docs/en/ai-skills) for the best reading experience.
|
|
4
|
+
|
|
5
|
+
[Documentation](README.md) · [简体中文](../zh-CN/ai-skills.md)
|
|
6
|
+
|
|
7
|
+
Skills are instruction bundles for AI agents, not RendererExtension modules or model services. Installing a Skill does not automatically change a project, install plugins, or connect an AI API.
|
|
8
|
+
|
|
9
|
+
| Skill | Tasks |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| [limr-use](../../skills/limr-use/SKILL.md) | Integrate LiMR, choose components, author syntax, configure annotations/streaming/themes, import existing plugins |
|
|
12
|
+
| [limr-plugin-development](../../skills/limr-plugin-development/SKILL.md) | Build new plugin packages with cards/syntax/actions/parameters and validate consumers |
|
|
13
|
+
|
|
14
|
+
Instructions are in English and accept tasks written in Chinese. Human-facing documentation is bilingual. The development Skill includes a standalone starter; both include local references, so the entire repository is not required in the agent's context.
|
|
15
|
+
|
|
16
|
+
## Import from the npm package
|
|
17
|
+
|
|
18
|
+
Install a LiMR version containing Skills, then run in **your application project**:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
npm install @linloir/limr
|
|
22
|
+
mkdir -p .agents/skills
|
|
23
|
+
cp -R node_modules/@linloir/limr/skills/limr-use .agents/skills/
|
|
24
|
+
cp -R node_modules/@linloir/limr/skills/limr-plugin-development .agents/skills/
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
These commands illustrate a fresh installation. Review local customizations before replacing an existing directory, and update the whole folder to avoid stale templates. Copies are snapshots; npm upgrades do not update them automatically. Review compatibility when upgrading LiMR.
|
|
28
|
+
|
|
29
|
+
## Import from the repository or ZIP
|
|
30
|
+
|
|
31
|
+
Copy from the repository's skills directory, or generate ZIPs locally (requires Python 3):
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
pnpm pack:skills
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Archives are written to artifacts/skills and mirrored to Playground public/downloads for Docs downloads. Each ZIP contains a complete top-level skill directory, not just SKILL.md. A versioned combined archive is also produced. Building the Docs site generates download assets automatically.
|
|
38
|
+
|
|
39
|
+
After the corresponding site version is deployed, download [limr-use.zip](https://linloir.github.io/LiMR/downloads/limr-use.zip) or [limr-plugin-development.zip](https://linloir.github.io/LiMR/downloads/limr-plugin-development.zip). The local AI Skills page also has download buttons. ZIPs are transport archives, not a promise that every AI client supports one-click import; extract and copy the full folders where needed.
|
|
40
|
+
|
|
41
|
+
## Codex locations and invocation
|
|
42
|
+
|
|
43
|
+
Codex supports project skills in .agents/skills and user skills in ~/.agents/skills. Choose one scope to avoid duplicate names. Older clients may use a different supported directory. Changes are normally discovered automatically; restart if missing. Invoke through /skills or $skill-name. Source: [official OpenAI Skills documentation](https://developers.openai.com/codex/skills/).
|
|
44
|
+
|
|
45
|
+
```text
|
|
46
|
+
$limr-use Integrate LiMR into this React application with English labels, streaming answers, and controlled annotations.
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
$limr-plugin-development Create a glossary plugin package with a term block, selection action, expandable card, and sidebar preview, then import it into this application.
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
For other SKILL.md-compatible agents, use their supported project/user locations. Without automatic discovery, ask the AI to read SKILL.md and follow its reference/asset links. Do not assume one client's commands work everywhere.
|
|
54
|
+
|
|
55
|
+
## Expected deliverables
|
|
56
|
+
|
|
57
|
+
The use Skill should deliver working integration, stable configuration, and validation. The development Skill should deliver module source, exports/types/CSS, a host example, and real package-consumer verification. Both prioritize the installed API, avoid invented syntax and loss of persisted unknown annotations, and do not publish packages or modify external services unless instructed.
|