@aparte/core 0.16.9 → 0.16.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/components/scroll-rail/aparte-scroll-rail.d.ts +89 -9
- package/dist/components/scroll-rail/aparte-scroll-rail.d.ts.map +1 -1
- package/dist/components/viewport/aparte-chat-viewport.d.ts +8 -1
- package/dist/components/viewport/aparte-chat-viewport.d.ts.map +1 -1
- package/dist/conversations/conversation-manager.d.ts +29 -0
- package/dist/conversations/conversation-manager.d.ts.map +1 -1
- package/dist/conversations/index.d.ts +1 -1
- package/dist/conversations/index.d.ts.map +1 -1
- package/dist/custom-elements.json +9012 -8633
- package/dist/{dialog-triggers-CApC7K1I.js → dialog-triggers-cYGTlfZf.js} +33 -5
- package/dist/dialog-triggers-cYGTlfZf.js.map +1 -0
- package/dist/index.css +56 -22
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +292 -49
- package/dist/index.js.map +1 -1
- package/dist/index.node.d.ts +1 -1
- package/dist/index.node.d.ts.map +1 -1
- package/dist/index.node.js +1 -1
- package/dist/index.node.js.map +1 -1
- package/package.json +2 -2
- package/dist/dialog-triggers-CApC7K1I.js.map +0 -1
package/dist/index.css
CHANGED
|
@@ -332,6 +332,9 @@
|
|
|
332
332
|
`display: none` under `(pointer: coarse)`, so 2.5.8's 24px is the bar here and not
|
|
333
333
|
2.5.5's 44px. */
|
|
334
334
|
--aparte-scroll-rail-hit-size: 24px;
|
|
335
|
+
/* The share of the transcript's height the rail may take: a list of ticks, not a
|
|
336
|
+
minimap, so it is the height of its list up to this, centred, and scrolls past it. */
|
|
337
|
+
--aparte-scroll-rail-share: .6;
|
|
335
338
|
|
|
336
339
|
/* Context gauge, ring variant — a diameter, and a stroke in the ring's own 36-unit
|
|
337
340
|
box. Literals like the rail's: a 22px ring is a toolbar's measure, not a spacing step. */
|
|
@@ -7265,10 +7268,10 @@ aparte-context[data-level="danger"] .aparte-context__value {
|
|
|
7265
7268
|
* One of the sheets src/index.ts imports in cascade order; check:derived-vars reads
|
|
7266
7269
|
* them concatenated in that same order, the way a browser does.
|
|
7267
7270
|
*
|
|
7268
|
-
* The rail floats on the transcript's end edge
|
|
7269
|
-
*
|
|
7270
|
-
*
|
|
7271
|
-
*
|
|
7271
|
+
* The rail floats on the transcript's end edge as a child of the chat host. `:has()`
|
|
7272
|
+
* gives the host its containing block only while a rail is inside it — a bare
|
|
7273
|
+
* `position: relative` on every chat would have moved any absolutely positioned child
|
|
7274
|
+
* a consumer already placed there.
|
|
7272
7275
|
*
|
|
7273
7276
|
* The three shell shapes shell.css already lays out, because an absolute element lands
|
|
7274
7277
|
* in the nearest POSITIONED ancestor and a shape missing from this list is a rail that
|
|
@@ -7284,27 +7287,57 @@ aparte-chat:has(> aparte-scroll-rail),
|
|
|
7284
7287
|
position: relative;
|
|
7285
7288
|
}
|
|
7286
7289
|
|
|
7287
|
-
/*
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7290
|
+
/* Where the rail sits is measured, not assumed. The element publishes three lengths on
|
|
7291
|
+
itself (aparte-scroll-rail.ts, `_layout`): `--aparte-scroll-rail-bar`, the width of
|
|
7292
|
+
the transcript's scrollbar when it is a classic one — 0 for an overlay bar — so the
|
|
7293
|
+
ticks never sit on the bar under Windows or Linux Chrome; and the two block insets,
|
|
7294
|
+
the transcript's distance from the host's top and bottom edges, so the rail centres
|
|
7295
|
+
on the messages and not on the composer under them. Without a measurement (jsdom, a
|
|
7296
|
+
rail with no transcript yet) the fallbacks centre it on the host, where it used to be.
|
|
7297
|
+
|
|
7298
|
+
It is a LIST of ticks, not a minimap: it takes the height of its list, capped at a
|
|
7299
|
+
share of the transcript (`--aparte-scroll-rail-share`, .6), and sits centred on the
|
|
7300
|
+
transcript's span — its top is the middle of that span, pulled back by half its own
|
|
7301
|
+
height. Measured on the market: every per-turn rail that ships as a list caps itself
|
|
7302
|
+
(LobeChat at 50vh), and every full-height strip is a proportional minimap with a
|
|
7303
|
+
viewport window, a different component; a list stretched to the full transcript read
|
|
7304
|
+
as the second while behaving as the first.
|
|
7305
|
+
|
|
7306
|
+
The block padding is the room the END zones need, and it is not decoration.
|
|
7307
|
+
The rail clips the list at its padding box, and the list has no padding of its own,
|
|
7308
|
+
so with no padding here the first tick's top edge sits exactly on the clip line. A
|
|
7309
|
+
zone grows symmetrically — half of `hit − thickness` above and below the line — so
|
|
7310
|
+
the first tick would lose its upper half and the last tick its lower half, to paint
|
|
7311
|
+
AND to hit-testing: 13px of the 24px, on the two ticks a reader aims at most ("jump
|
|
7312
|
+
to the first message", "jump to the latest"). Padding by exactly that half puts the
|
|
7313
|
+
clip line outside every zone instead of through the two end ones. `box-sizing` keeps
|
|
7314
|
+
`max-height` measuring the same box it measured before.
|
|
7315
|
+
|
|
7316
|
+
When more ticks exist than 24px targets fit in the cap, the element tightens the
|
|
7317
|
+
pitch by setting `--aparte-scroll-rail-hit-size` on itself (never under 6px), and
|
|
7318
|
+
gap, padding and zone follow it here as they would a consumer's value. Past that
|
|
7319
|
+
floor the list still overflows, and the rail scrolls — the element keeps the current
|
|
7320
|
+
tick in its window, the arrows walk every tick, a wheel over the rail slides it —
|
|
7321
|
+
with no bar of its own: a 24px column showing a scrollbar would be all bar. */
|
|
7296
7322
|
aparte-scroll-rail {
|
|
7297
7323
|
position: absolute;
|
|
7298
|
-
inset-inline-end: var(--aparte-space-2);
|
|
7299
|
-
top:
|
|
7324
|
+
inset-inline-end: calc(var(--aparte-space-2) + var(--aparte-scroll-rail-bar, 0px));
|
|
7325
|
+
top: calc(var(--aparte-scroll-rail-block-start, 0px) + (100% - var(--aparte-scroll-rail-block-start, 0px) - var(--aparte-scroll-rail-block-end, 0px)) / 2);
|
|
7300
7326
|
transform: translateY(-50%);
|
|
7301
7327
|
z-index: var(--aparte-z-floating);
|
|
7302
|
-
display:
|
|
7328
|
+
display: flex;
|
|
7329
|
+
flex-direction: column;
|
|
7303
7330
|
box-sizing: border-box;
|
|
7304
7331
|
width: var(--aparte-scroll-rail-width);
|
|
7305
7332
|
padding-block: calc((var(--aparte-scroll-rail-hit-size) - var(--aparte-scroll-rail-tick-thickness)) / 2);
|
|
7306
|
-
max-height: calc(100% -
|
|
7307
|
-
overflow: hidden;
|
|
7333
|
+
max-height: calc((100% - var(--aparte-scroll-rail-block-start, 0px) - var(--aparte-scroll-rail-block-end, 0px)) * var(--aparte-scroll-rail-share));
|
|
7334
|
+
overflow-x: hidden;
|
|
7335
|
+
overflow-y: auto;
|
|
7336
|
+
scrollbar-width: none;
|
|
7337
|
+
}
|
|
7338
|
+
|
|
7339
|
+
aparte-scroll-rail::-webkit-scrollbar {
|
|
7340
|
+
display: none;
|
|
7308
7341
|
}
|
|
7309
7342
|
|
|
7310
7343
|
aparte-scroll-rail[hidden],
|
|
@@ -7319,6 +7352,7 @@ aparte-scroll-rail[data-empty] {
|
|
|
7319
7352
|
display: flex;
|
|
7320
7353
|
flex-direction: column;
|
|
7321
7354
|
align-items: flex-end;
|
|
7355
|
+
flex-shrink: 0;
|
|
7322
7356
|
gap: var(--aparte-scroll-rail-gap);
|
|
7323
7357
|
}
|
|
7324
7358
|
|
|
@@ -7350,10 +7384,10 @@ aparte-scroll-rail[data-empty] {
|
|
|
7350
7384
|
Block: grow symmetrically to the hit size. The gap is `hit − thickness`, so the pitch
|
|
7351
7385
|
is exactly the hit size and two zones meet without overlapping — and the rail pads its
|
|
7352
7386
|
block axis by the same half-growth, so the clip does not eat the first and last zone.
|
|
7353
|
-
Inline: grow INWARD only. The rail clips
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7387
|
+
Inline: grow INWARD only. The rail clips and ticks are end-aligned, so a centred zone
|
|
7388
|
+
would hang past the end edge and come back cut to 19px; anchored to the tick's end
|
|
7389
|
+
edge it stays whole, and the rail's width carries the hit size as its floor so the
|
|
7390
|
+
column has room for it. */
|
|
7357
7391
|
.aparte-scroll-rail__tick::before {
|
|
7358
7392
|
content: "";
|
|
7359
7393
|
position: absolute;
|
package/dist/index.d.ts
CHANGED
|
@@ -84,7 +84,7 @@ export { AparteConversationList } from './components/index.js';
|
|
|
84
84
|
export type { AparteConversationListItem, AparteConversationSelectDetail, AparteConversationDeleteDetail, AparteConversationArchiveDetail, AparteConversationPinDetail, AparteConversationRenameDetail } from './components/index.js';
|
|
85
85
|
export type { AparteConversation, AparteConversationMeta, AparteStorageAdapter, AparteAttachmentRow, } from './conversations/index.js';
|
|
86
86
|
export { APARTE_CONVERSATION_SCHEMA_VERSION } from './conversations/index.js';
|
|
87
|
-
export { AparteConversationManager, type ConversationManagerOptions } from './conversations/index.js';
|
|
87
|
+
export { AparteConversationManager, type ConversationManagerOptions, type AparteConversationTitleProvider, } from './conversations/index.js';
|
|
88
88
|
export { AparteConversationController, type AparteChatBinding, type AparteConversationControllerOptions, } from './conversations/index.js';
|
|
89
89
|
export { AparteChatHost, type AparteChatHostBinding, type AparteChatHostOptions, } from './host/index.js';
|
|
90
90
|
export { AparteStreamParser, parseMarkdownToSegments } from './parsers/index.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as resolveConfig, a4 as subscribeConfigChange, a1 as runWithConfig, o as AparteElicitationAbortError, w as buildApprovalPanel, x as buildElicitationPanel, J as escapeAttr, F as cssEscape, a7 as mergeSegmentUpdate, K as escapeHtml, a8 as writeStreamedMarkdown, C as copyText, N as getSegmentRenderer, O as installDefaultRenderersOnce, r as AparteMessageRepository, a9 as stampSegmentActivity, aa as stampSegmentOnInsert, ab as stampSegmentOnUpdate, ac as renumberSegments, ad as openSegmentIds, ae as isTerminalStatus, af as adoptMessageSegments, Q as isAwaitingReply, W as populateBubbleFromMessage, a6 as uuid, a0 as revokeAttachmentUrls, e as APARTE_DEFAULT_LOCALE, P as installDialogTriggersOnce } from "./dialog-triggers-
|
|
2
|
-
import { A, a, b, c, d, f, g, h, i, j, k, l, m, n, p, q, s, t, u, v, y, z, B, D, E, G, H, I, L, M, R, S, T, U, V, X, Y, Z, _, a2, a3, a5 } from "./dialog-triggers-
|
|
1
|
+
import { $ as resolveConfig, a4 as subscribeConfigChange, a1 as runWithConfig, o as AparteElicitationAbortError, w as buildApprovalPanel, x as buildElicitationPanel, J as escapeAttr, F as cssEscape, a7 as mergeSegmentUpdate, K as escapeHtml, a8 as writeStreamedMarkdown, C as copyText, N as getSegmentRenderer, O as installDefaultRenderersOnce, r as AparteMessageRepository, a9 as stampSegmentActivity, aa as stampSegmentOnInsert, ab as stampSegmentOnUpdate, ac as renumberSegments, ad as openSegmentIds, ae as isTerminalStatus, af as adoptMessageSegments, Q as isAwaitingReply, W as populateBubbleFromMessage, a6 as uuid, a0 as revokeAttachmentUrls, e as APARTE_DEFAULT_LOCALE, P as installDialogTriggersOnce } from "./dialog-triggers-cYGTlfZf.js";
|
|
2
|
+
import { A, a, b, c, d, f, g, h, i, j, k, l, m, n, p, q, s, t, u, v, y, z, B, D, E, G, H, I, L, M, R, S, T, U, V, X, Y, Z, _, a2, a3, a5 } from "./dialog-triggers-cYGTlfZf.js";
|
|
3
3
|
function presenceOn(value) {
|
|
4
4
|
return value === "" ? true : Boolean(value);
|
|
5
5
|
}
|
|
@@ -3470,11 +3470,18 @@ class AparteChatViewport extends HTMLElement {
|
|
|
3470
3470
|
});
|
|
3471
3471
|
}
|
|
3472
3472
|
}
|
|
3473
|
-
/**
|
|
3473
|
+
/**
|
|
3474
|
+
* The overlay shell's element children that are the floating bottom stack: not this
|
|
3475
|
+
* viewport, and not the scroll rail, which floats over the transcript by design —
|
|
3476
|
+
* shell.css names it the same way (`:not(aparte-chat-viewport, aparte-scroll-rail)`).
|
|
3477
|
+
* Counted, a rail centred on the transcript set the inset to the distance from its
|
|
3478
|
+
* own top edge (398px on the vanilla example) and held it there while the composer
|
|
3479
|
+
* grew under a draft.
|
|
3480
|
+
*/
|
|
3474
3481
|
_overlayStack() {
|
|
3475
3482
|
if (!this._overlayRoot) return [];
|
|
3476
3483
|
return Array.from(this._overlayRoot.children).filter(
|
|
3477
|
-
(el2) => el2 instanceof HTMLElement && el2 !== this
|
|
3484
|
+
(el2) => el2 instanceof HTMLElement && el2 !== this && el2.tagName.toLowerCase() !== "aparte-scroll-rail"
|
|
3478
3485
|
);
|
|
3479
3486
|
}
|
|
3480
3487
|
/**
|
|
@@ -5882,18 +5889,40 @@ if (!customElements.get("aparte-context")) {
|
|
|
5882
5889
|
customElements.define("aparte-context", AparteContext);
|
|
5883
5890
|
}
|
|
5884
5891
|
const EXCERPT_LENGTH = 60;
|
|
5892
|
+
const MIN_PITCH = 6;
|
|
5893
|
+
const SETTLE_MS = 150;
|
|
5894
|
+
const MAX_ALIGN_PASSES = 4;
|
|
5895
|
+
const ALIGN_TOLERANCE = 1;
|
|
5885
5896
|
class AparteScrollRail extends HTMLElement {
|
|
5886
5897
|
static get observedAttributes() {
|
|
5887
5898
|
return ["target", "every"];
|
|
5888
5899
|
}
|
|
5889
5900
|
_host = null;
|
|
5890
5901
|
_mutations = null;
|
|
5902
|
+
_resizes = null;
|
|
5891
5903
|
_intersections = null;
|
|
5904
|
+
/** The bubbles the intersection observer watches — compared by identity, so a re-rendered bubble is re-observed. */
|
|
5905
|
+
_observedBubbles = [];
|
|
5906
|
+
/** The scroll surface the observer is rooted on, and whose bottom edge is read. */
|
|
5907
|
+
_observedSurface = null;
|
|
5892
5908
|
_rebuildQueued = false;
|
|
5909
|
+
_pickQueued = false;
|
|
5893
5910
|
_currentId = null;
|
|
5894
|
-
/** Visible ratio per message id, from the intersection observer. */
|
|
5911
|
+
/** Visible ratio per message id in the reading band, from the intersection observer. */
|
|
5895
5912
|
_visible = /* @__PURE__ */ new Map();
|
|
5913
|
+
/** The bubbles on screen at all, from the second observer; only the last one is read. */
|
|
5914
|
+
_onScreen = /* @__PURE__ */ new Set();
|
|
5915
|
+
_onScreenObserver = null;
|
|
5916
|
+
/** The mark a jump asked for, held until the transcript has stopped moving. */
|
|
5917
|
+
_held = null;
|
|
5918
|
+
_settleTimer = null;
|
|
5919
|
+
_settleSurface = null;
|
|
5920
|
+
/** Re-alignments done for the jump in flight. */
|
|
5921
|
+
_alignPasses = 0;
|
|
5922
|
+
/** The custom properties written on this element, so each is written only when it changes. */
|
|
5923
|
+
_written = /* @__PURE__ */ new Map();
|
|
5896
5924
|
_onConfigChange = () => this._rebuild();
|
|
5925
|
+
_onSurfaceScroll = () => this._bumpSettle();
|
|
5897
5926
|
/** The id of the message the rail currently marks, or `null`. */
|
|
5898
5927
|
get currentMessageId() {
|
|
5899
5928
|
return this._currentId;
|
|
@@ -5937,17 +5966,26 @@ class AparteScrollRail extends HTMLElement {
|
|
|
5937
5966
|
return;
|
|
5938
5967
|
}
|
|
5939
5968
|
if (typeof MutationObserver !== "undefined") {
|
|
5940
|
-
this._mutations = new MutationObserver(() =>
|
|
5969
|
+
this._mutations = new MutationObserver((records) => {
|
|
5970
|
+
if (records.some((record) => !this.contains(record.target))) this._queueRebuild();
|
|
5971
|
+
});
|
|
5941
5972
|
this._mutations.observe(this._host, { childList: true, subtree: true, characterData: true });
|
|
5942
5973
|
}
|
|
5974
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
5975
|
+
this._resizes = new ResizeObserver(() => this._layout());
|
|
5976
|
+
this._resizes.observe(this._host);
|
|
5977
|
+
const viewport = this._viewport();
|
|
5978
|
+
if (viewport) this._resizes.observe(viewport);
|
|
5979
|
+
}
|
|
5943
5980
|
this._rebuild();
|
|
5944
5981
|
}
|
|
5945
5982
|
_detach() {
|
|
5946
5983
|
this._mutations?.disconnect();
|
|
5947
5984
|
this._mutations = null;
|
|
5948
|
-
this.
|
|
5949
|
-
this.
|
|
5950
|
-
this.
|
|
5985
|
+
this._resizes?.disconnect();
|
|
5986
|
+
this._resizes = null;
|
|
5987
|
+
this._unobserve();
|
|
5988
|
+
this._release();
|
|
5951
5989
|
this._host = null;
|
|
5952
5990
|
}
|
|
5953
5991
|
/** Coalesce a burst of mutations (a streaming reply is one per token) into one rebuild. */
|
|
@@ -5988,6 +6026,15 @@ class AparteScrollRail extends HTMLElement {
|
|
|
5988
6026
|
const excerpt = this._excerpt(bubble);
|
|
5989
6027
|
return excerpt ? `${role}: ${excerpt}` : role;
|
|
5990
6028
|
}
|
|
6029
|
+
_list() {
|
|
6030
|
+
return this.querySelector(":scope > .aparte-scroll-rail__list");
|
|
6031
|
+
}
|
|
6032
|
+
/**
|
|
6033
|
+
* Reconcile the ticks with the bubbles, by message id. A tick that already exists is
|
|
6034
|
+
* kept — the same node, so the focus on it, its hover and its tooltip survive a turn
|
|
6035
|
+
* being appended or a reply streaming beside it — moved only when its order changed,
|
|
6036
|
+
* and dropped when its bubble left.
|
|
6037
|
+
*/
|
|
5991
6038
|
_rebuild() {
|
|
5992
6039
|
const bubbles = this._bubbles();
|
|
5993
6040
|
const locale = resolveConfig(this).getLocale();
|
|
@@ -5995,45 +6042,71 @@ class AparteScrollRail extends HTMLElement {
|
|
|
5995
6042
|
if (bubbles.length < 2) {
|
|
5996
6043
|
this.setAttribute("data-empty", "");
|
|
5997
6044
|
this.replaceChildren();
|
|
5998
|
-
this.
|
|
5999
|
-
this._intersections = null;
|
|
6000
|
-
this._visible.clear();
|
|
6045
|
+
this._unobserve();
|
|
6001
6046
|
this._currentId = null;
|
|
6047
|
+
this._write("--aparte-scroll-rail-hit-size", "");
|
|
6002
6048
|
return;
|
|
6003
6049
|
}
|
|
6004
6050
|
this.removeAttribute("data-empty");
|
|
6005
|
-
|
|
6006
|
-
list
|
|
6051
|
+
let list = this._list();
|
|
6052
|
+
if (!list) {
|
|
6053
|
+
list = document.createElement("ol");
|
|
6054
|
+
list.className = "aparte-scroll-rail__list";
|
|
6055
|
+
this.replaceChildren(list);
|
|
6056
|
+
}
|
|
6057
|
+
const existing = /* @__PURE__ */ new Map();
|
|
6058
|
+
for (const item of Array.from(list.children)) {
|
|
6059
|
+
const id = item.firstElementChild instanceof HTMLElement ? item.firstElementChild.dataset["messageId"] : void 0;
|
|
6060
|
+
if (id) existing.set(id, item);
|
|
6061
|
+
}
|
|
6007
6062
|
const known = /* @__PURE__ */ new Set();
|
|
6063
|
+
let index = 0;
|
|
6008
6064
|
for (const bubble of bubbles) {
|
|
6009
6065
|
const id = bubble.getAttribute("message-id");
|
|
6010
6066
|
known.add(id);
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6067
|
+
let item = existing.get(id);
|
|
6068
|
+
if (item) {
|
|
6069
|
+
existing.delete(id);
|
|
6070
|
+
} else {
|
|
6071
|
+
item = document.createElement("li");
|
|
6072
|
+
const tick2 = document.createElement("button");
|
|
6073
|
+
tick2.type = "button";
|
|
6074
|
+
tick2.className = "aparte-scroll-rail__tick";
|
|
6075
|
+
tick2.dataset["messageId"] = id;
|
|
6076
|
+
item.appendChild(tick2);
|
|
6077
|
+
}
|
|
6078
|
+
const tick = item.firstElementChild;
|
|
6016
6079
|
const label = this._tickLabel(bubble);
|
|
6017
|
-
tick.
|
|
6018
|
-
|
|
6080
|
+
if (tick.getAttribute("aria-label") !== label) {
|
|
6081
|
+
tick.setAttribute("aria-label", label);
|
|
6082
|
+
tick.title = label;
|
|
6083
|
+
}
|
|
6019
6084
|
if (id === this._currentId) tick.setAttribute("aria-current", "true");
|
|
6020
|
-
|
|
6021
|
-
list.
|
|
6085
|
+
else tick.removeAttribute("aria-current");
|
|
6086
|
+
const at = list.children[index] ?? null;
|
|
6087
|
+
if (at !== item) list.insertBefore(item, at);
|
|
6088
|
+
index++;
|
|
6022
6089
|
}
|
|
6023
|
-
|
|
6090
|
+
for (const stale of existing.values()) stale.remove();
|
|
6024
6091
|
if (this._currentId && !known.has(this._currentId)) this._currentId = null;
|
|
6025
|
-
this._observe(bubbles);
|
|
6092
|
+
if (!sameElements(this._observedBubbles, bubbles)) this._observe(bubbles);
|
|
6093
|
+
this._layout();
|
|
6026
6094
|
}
|
|
6027
6095
|
/**
|
|
6028
|
-
* Which tick is current is read, not computed: the scroll surface is the root
|
|
6029
|
-
*
|
|
6030
|
-
*
|
|
6031
|
-
*
|
|
6096
|
+
* Which tick is current is read, not computed: the scroll surface is the root and an
|
|
6097
|
+
* intersection observer reports which bubbles reach a reading band — the top 30% of
|
|
6098
|
+
* the surface, from its very top so that the bubble a jump aligned there counts. The
|
|
6099
|
+
* rule is a scrollspy's: the LAST question that has reached the band is the one the
|
|
6100
|
+
* reader is under. "The largest visible share" was the rule before, and at the bottom
|
|
6101
|
+
* of a thread of short turns, where three questions sit in the band, it named the
|
|
6102
|
+
* first of them. Scroll arithmetic was the alternative, and every scroll-position
|
|
6103
|
+
* rule in the viewport carries a regression note; the one read here is the bottom,
|
|
6104
|
+
* because at the bottom the reader is under the latest question whatever the band
|
|
6105
|
+
* holds, and the observer has nothing to report on the last few pixels.
|
|
6032
6106
|
*/
|
|
6033
6107
|
_observe(bubbles) {
|
|
6034
|
-
this.
|
|
6035
|
-
this.
|
|
6036
|
-
this._visible.clear();
|
|
6108
|
+
this._unobserve();
|
|
6109
|
+
this._observedBubbles = bubbles;
|
|
6037
6110
|
if (typeof IntersectionObserver === "undefined") return;
|
|
6038
6111
|
const root = this._scrollSurface();
|
|
6039
6112
|
if (!root) return;
|
|
@@ -6042,30 +6115,142 @@ class AparteScrollRail extends HTMLElement {
|
|
|
6042
6115
|
const id = entry.target.getAttribute("message-id");
|
|
6043
6116
|
if (id) this._visible.set(id, entry.isIntersecting ? entry.intersectionRatio : 0);
|
|
6044
6117
|
}
|
|
6045
|
-
this._pickCurrent(
|
|
6046
|
-
}, { root, rootMargin: "
|
|
6047
|
-
|
|
6118
|
+
this._pickCurrent();
|
|
6119
|
+
}, { root, rootMargin: "0px 0px -70% 0px", threshold: [0, 0.1, 0.25, 0.5, 0.75, 1] });
|
|
6120
|
+
this._onScreenObserver = new IntersectionObserver((entries) => {
|
|
6121
|
+
for (const entry of entries) {
|
|
6122
|
+
const id = entry.target.getAttribute("message-id");
|
|
6123
|
+
if (!id) continue;
|
|
6124
|
+
if (entry.isIntersecting) this._onScreen.add(id);
|
|
6125
|
+
else this._onScreen.delete(id);
|
|
6126
|
+
}
|
|
6127
|
+
this._pickCurrent();
|
|
6128
|
+
}, { root, threshold: [0] });
|
|
6129
|
+
for (const bubble of bubbles) {
|
|
6130
|
+
this._intersections.observe(bubble);
|
|
6131
|
+
this._onScreenObserver.observe(bubble);
|
|
6132
|
+
}
|
|
6133
|
+
this._observedSurface = root;
|
|
6134
|
+
root.addEventListener("scroll", this._onRootScroll, { passive: true });
|
|
6048
6135
|
}
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6136
|
+
_unobserve() {
|
|
6137
|
+
this._intersections?.disconnect();
|
|
6138
|
+
this._intersections = null;
|
|
6139
|
+
this._onScreenObserver?.disconnect();
|
|
6140
|
+
this._onScreenObserver = null;
|
|
6141
|
+
this._observedSurface?.removeEventListener("scroll", this._onRootScroll);
|
|
6142
|
+
this._observedSurface = null;
|
|
6143
|
+
this._observedBubbles = [];
|
|
6144
|
+
this._visible.clear();
|
|
6145
|
+
this._onScreen.clear();
|
|
6146
|
+
}
|
|
6147
|
+
/** A scroll only matters at the bottom edge, where the observer is silent: one pick per frame. */
|
|
6148
|
+
_onRootScroll = () => {
|
|
6149
|
+
if (this._pickQueued) return;
|
|
6150
|
+
this._pickQueued = true;
|
|
6151
|
+
requestAnimationFrame(() => {
|
|
6152
|
+
this._pickQueued = false;
|
|
6153
|
+
this._pickCurrent();
|
|
6154
|
+
});
|
|
6155
|
+
};
|
|
6156
|
+
/** At the bottom edge of a surface that scrolls; a surface with nothing to scroll has no bottom to be at. */
|
|
6157
|
+
_atBottom() {
|
|
6158
|
+
const surface = this._observedSurface;
|
|
6159
|
+
if (!surface) return false;
|
|
6160
|
+
const max = surface.scrollHeight - surface.clientHeight;
|
|
6161
|
+
return max > 0 && max - surface.scrollTop <= 2;
|
|
6162
|
+
}
|
|
6163
|
+
_pickCurrent() {
|
|
6164
|
+
if (this._held) return;
|
|
6165
|
+
const bubbles = this._observedBubbles;
|
|
6166
|
+
if (bubbles.length === 0) return;
|
|
6167
|
+
const lastId = bubbles[bubbles.length - 1].getAttribute("message-id");
|
|
6168
|
+
if (this._atBottom() || this._onScreen.has(lastId)) {
|
|
6169
|
+
this._setCurrent(lastId);
|
|
6170
|
+
return;
|
|
6171
|
+
}
|
|
6172
|
+
let last = null;
|
|
6052
6173
|
for (const bubble of bubbles) {
|
|
6053
6174
|
const id = bubble.getAttribute("message-id");
|
|
6054
|
-
|
|
6055
|
-
if (ratio > bestRatio) {
|
|
6056
|
-
bestRatio = ratio;
|
|
6057
|
-
best = id;
|
|
6058
|
-
}
|
|
6175
|
+
if ((this._visible.get(id) ?? 0) > 0) last = id;
|
|
6059
6176
|
}
|
|
6060
|
-
if (
|
|
6177
|
+
if (last !== null) this._setCurrent(last);
|
|
6061
6178
|
}
|
|
6062
6179
|
_setCurrent(id) {
|
|
6063
6180
|
if (this._currentId === id) return;
|
|
6064
6181
|
this._currentId = id;
|
|
6065
6182
|
for (const tick of this.querySelectorAll(".aparte-scroll-rail__tick")) {
|
|
6066
|
-
if (tick.dataset["messageId"] === id)
|
|
6067
|
-
|
|
6183
|
+
if (tick.dataset["messageId"] === id) {
|
|
6184
|
+
tick.setAttribute("aria-current", "true");
|
|
6185
|
+
this._reveal(tick);
|
|
6186
|
+
} else {
|
|
6187
|
+
tick.removeAttribute("aria-current");
|
|
6188
|
+
}
|
|
6189
|
+
}
|
|
6190
|
+
}
|
|
6191
|
+
/** Past the floor the rail scrolls itself: the current tick is kept inside its window. */
|
|
6192
|
+
_reveal(tick) {
|
|
6193
|
+
const room = this.clientHeight;
|
|
6194
|
+
if (!room) return;
|
|
6195
|
+
const top = tick.offsetTop;
|
|
6196
|
+
const bottom = top + tick.offsetHeight;
|
|
6197
|
+
if (top < this.scrollTop) this.scrollTop = top;
|
|
6198
|
+
else if (bottom > this.scrollTop + room) this.scrollTop = bottom - room;
|
|
6199
|
+
}
|
|
6200
|
+
// ─── Geometry ─────────────────────────────────────────────────────────
|
|
6201
|
+
/** Write a custom property on this element only when its value changed; '' removes it. */
|
|
6202
|
+
_write(name, value) {
|
|
6203
|
+
if (this._written.get(name) === value) return;
|
|
6204
|
+
this._written.set(name, value);
|
|
6205
|
+
if (value) this.style.setProperty(name, value);
|
|
6206
|
+
else this.style.removeProperty(name);
|
|
6207
|
+
}
|
|
6208
|
+
/**
|
|
6209
|
+
* The three measurements the stylesheet positions the rail by, and the pitch.
|
|
6210
|
+
*
|
|
6211
|
+
* The scrollbar: `offsetWidth − clientWidth` of the scroll surface is the width of a
|
|
6212
|
+
* classic bar and 0 for an overlay one, so the rail sits clear of the bar exactly
|
|
6213
|
+
* where there is one. The extent: the transcript's box within the host, so the rail
|
|
6214
|
+
* spans the messages and not the composer under them. The pitch: the stylesheet's
|
|
6215
|
+
* 24px targets whenever `count × 24` fits the rail; otherwise the pitch that does,
|
|
6216
|
+
* never under `MIN_PITCH` — the rail sets `--aparte-scroll-rail-hit-size` on itself
|
|
6217
|
+
* and the gap, the padding and the zone all follow it, as they do for a consumer's
|
|
6218
|
+
* value. Past the floor the rail scrolls (`_reveal`), and the arrows still walk it.
|
|
6219
|
+
*/
|
|
6220
|
+
_layout() {
|
|
6221
|
+
const host = this._host;
|
|
6222
|
+
const viewport = this._viewport();
|
|
6223
|
+
const surface = this._scrollSurface();
|
|
6224
|
+
if (host && surface) {
|
|
6225
|
+
this._write("--aparte-scroll-rail-bar", `${Math.max(0, surface.offsetWidth - surface.clientWidth)}px`);
|
|
6226
|
+
}
|
|
6227
|
+
if (host && viewport) {
|
|
6228
|
+
const v2 = viewport.getBoundingClientRect();
|
|
6229
|
+
const h2 = host.getBoundingClientRect();
|
|
6230
|
+
if (v2.height > 0 && h2.height > 0) {
|
|
6231
|
+
this._write("--aparte-scroll-rail-block-start", `${Math.max(0, Math.round(v2.top - h2.top))}px`);
|
|
6232
|
+
this._write("--aparte-scroll-rail-block-end", `${Math.max(0, Math.round(h2.bottom - v2.bottom))}px`);
|
|
6233
|
+
}
|
|
6234
|
+
}
|
|
6235
|
+
const count = this._list()?.children.length ?? 0;
|
|
6236
|
+
const height = this.clientHeight;
|
|
6237
|
+
if (!count || !height) {
|
|
6238
|
+
this._write("--aparte-scroll-rail-hit-size", "");
|
|
6239
|
+
this._write("--aparte-scroll-rail-gap", "");
|
|
6240
|
+
return;
|
|
6241
|
+
}
|
|
6242
|
+
const above = host ?? document.documentElement;
|
|
6243
|
+
const aboveStyle = getComputedStyle(above);
|
|
6244
|
+
const hit = parseFloat(aboveStyle.getPropertyValue("--aparte-scroll-rail-hit-size")) || 24;
|
|
6245
|
+
const thickness = parseFloat(aboveStyle.getPropertyValue("--aparte-scroll-rail-tick-thickness")) || 2;
|
|
6246
|
+
if (count * hit <= height) {
|
|
6247
|
+
this._write("--aparte-scroll-rail-hit-size", "");
|
|
6248
|
+
this._write("--aparte-scroll-rail-gap", "");
|
|
6249
|
+
return;
|
|
6068
6250
|
}
|
|
6251
|
+
const pitch = Math.max(MIN_PITCH, Math.floor(height / count));
|
|
6252
|
+
this._write("--aparte-scroll-rail-hit-size", `${pitch}px`);
|
|
6253
|
+
this._write("--aparte-scroll-rail-gap", `${Math.max(0, pitch - thickness)}px`);
|
|
6069
6254
|
}
|
|
6070
6255
|
// ─── Interaction ──────────────────────────────────────────────────────
|
|
6071
6256
|
_onClick = (e) => {
|
|
@@ -6088,6 +6273,16 @@ class AparteScrollRail extends HTMLElement {
|
|
|
6088
6273
|
/**
|
|
6089
6274
|
* Scroll the transcript to a message. Announced first, cancelable; then a
|
|
6090
6275
|
* `scrollIntoView` on the bubble, smooth unless the reader prefers reduced motion.
|
|
6276
|
+
* The clicked tick is the mark until the transcript has stopped moving: while a
|
|
6277
|
+
* smooth scroll crosses other questions the band would name each in turn, and it
|
|
6278
|
+
* ended on the wrong one two times in three before this hold.
|
|
6279
|
+
*
|
|
6280
|
+
* Where the scroll lands is checked once it has settled, and corrected. The bubbles
|
|
6281
|
+
* carry `content-visibility: auto`, so a bubble far from the reader has an estimated
|
|
6282
|
+
* height until it is rendered: the scroll aims at an estimate, the bubbles above it
|
|
6283
|
+
* take their real size on the way, and the message ends up above or below the top —
|
|
6284
|
+
* measured from 36px to 1,213px off, in all three engines, the further the worse.
|
|
6285
|
+
* A second `scrollIntoView` from a rendered neighbourhood lands; a few are allowed.
|
|
6091
6286
|
*/
|
|
6092
6287
|
jumpTo(messageId) {
|
|
6093
6288
|
const announced = this.dispatchEvent(new CustomEvent("aparte-scroll-rail-jump", {
|
|
@@ -6097,13 +6292,61 @@ class AparteScrollRail extends HTMLElement {
|
|
|
6097
6292
|
cancelable: true
|
|
6098
6293
|
}));
|
|
6099
6294
|
if (!announced) return;
|
|
6100
|
-
const
|
|
6101
|
-
const bubble = viewport?.querySelector(`aparte-chat-bubble[message-id="${cssEscape(messageId)}"]`);
|
|
6295
|
+
const bubble = this._bubbleOf(messageId);
|
|
6102
6296
|
if (!bubble) return;
|
|
6297
|
+
this._scrollTo(bubble);
|
|
6298
|
+
this._setCurrent(messageId);
|
|
6299
|
+
this._hold(messageId);
|
|
6300
|
+
}
|
|
6301
|
+
_bubbleOf(messageId) {
|
|
6302
|
+
return this._viewport()?.querySelector(`aparte-chat-bubble[message-id="${cssEscape(messageId)}"]`) ?? null;
|
|
6303
|
+
}
|
|
6304
|
+
_scrollTo(bubble) {
|
|
6103
6305
|
const reduced = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
6104
6306
|
bubble.scrollIntoView({ block: "start", behavior: reduced ? "auto" : "smooth" });
|
|
6105
|
-
this._setCurrent(messageId);
|
|
6106
6307
|
}
|
|
6308
|
+
/** Keep `id` as the mark until the scroll surface has been still for `SETTLE_MS`. */
|
|
6309
|
+
_hold(id) {
|
|
6310
|
+
this._release();
|
|
6311
|
+
this._held = id;
|
|
6312
|
+
this._alignPasses = 0;
|
|
6313
|
+
this._settleSurface = this._scrollSurface();
|
|
6314
|
+
this._settleSurface?.addEventListener("scroll", this._onSurfaceScroll);
|
|
6315
|
+
this._bumpSettle();
|
|
6316
|
+
}
|
|
6317
|
+
_bumpSettle() {
|
|
6318
|
+
if (this._settleTimer !== null) clearTimeout(this._settleTimer);
|
|
6319
|
+
this._settleTimer = setTimeout(() => this._settle(), SETTLE_MS);
|
|
6320
|
+
}
|
|
6321
|
+
/** The transcript is still: land on the message if the scroll did not, else release the hold. */
|
|
6322
|
+
_settle() {
|
|
6323
|
+
const id = this._held;
|
|
6324
|
+
const surface = this._settleSurface;
|
|
6325
|
+
const bubble = id ? this._bubbleOf(id) : null;
|
|
6326
|
+
if (id && bubble && surface && this._alignPasses < MAX_ALIGN_PASSES) {
|
|
6327
|
+
const off = bubble.getBoundingClientRect().top - surface.getBoundingClientRect().top;
|
|
6328
|
+
if (Math.abs(off) > ALIGN_TOLERANCE) {
|
|
6329
|
+
this._alignPasses++;
|
|
6330
|
+
this._scrollTo(bubble);
|
|
6331
|
+
this._bumpSettle();
|
|
6332
|
+
return;
|
|
6333
|
+
}
|
|
6334
|
+
}
|
|
6335
|
+
this._release();
|
|
6336
|
+
if (id) this._setCurrent(id);
|
|
6337
|
+
}
|
|
6338
|
+
_release() {
|
|
6339
|
+
if (this._settleTimer !== null) clearTimeout(this._settleTimer);
|
|
6340
|
+
this._settleTimer = null;
|
|
6341
|
+
this._settleSurface?.removeEventListener("scroll", this._onSurfaceScroll);
|
|
6342
|
+
this._settleSurface = null;
|
|
6343
|
+
this._held = null;
|
|
6344
|
+
}
|
|
6345
|
+
}
|
|
6346
|
+
function sameElements(a4, b2) {
|
|
6347
|
+
if (a4.length !== b2.length) return false;
|
|
6348
|
+
for (let i2 = 0; i2 < a4.length; i2++) if (a4[i2] !== b2[i2]) return false;
|
|
6349
|
+
return true;
|
|
6107
6350
|
}
|
|
6108
6351
|
if (!customElements.get("aparte-scroll-rail")) {
|
|
6109
6352
|
customElements.define("aparte-scroll-rail", AparteScrollRail);
|