@aparte/core 0.16.3 → 0.16.5

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.
@@ -200,6 +200,8 @@ export interface AparteSplitAttributes {
200
200
  disabled?: boolean;
201
201
  /** The seam's accessible name. Defaults to the locale's `splitHandleLabel`. */
202
202
  label?: string;
203
+ /** Show one pane — the one `pane` names — whatever the width: the seam and the other pane are gone, as under the breakpoint. For a host that decides itself when a second pane exists (a preview with nothing to preview yet). `collapsed` is not this: it folds the primary pane to `--aparte-split-min` and keeps the seam. The CSS route, `.aparte-split--only-start` / `--only-end`, is the same state for a host that owns its breakpoints. */
204
+ single?: boolean;
203
205
  /** Written BY the element while one pane is shown. Read-only; style against it. */
204
206
  'data-stacked'?: boolean;
205
207
  }
@@ -1 +1 @@
1
- {"version":3,"file":"element-attributes.d.ts","sourceRoot":"","sources":["../../src/generated/element-attributes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"element-attributes.d.ts","sourceRoot":"","sources":["../../src/generated/element-attributes.ts"],"names":[],"mappings":"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"}
package/dist/index.css CHANGED
@@ -596,7 +596,11 @@ aparte-chat {
596
596
  The eight text sizes were px and are rem now, like the rest of the typography:
597
597
  identical at a 16px root, and following the reader's setting elsewhere. */
598
598
  --aparte-message-gap: var(--aparte-space-6);
599
- --aparte-message-padding: var(--aparte-space-8) var(--aparte-space-6);
599
+ /* Split, because the action bar's offset below a message is computed from the
600
+ vertical half (#56) — a shorthand cannot be read by parts. */
601
+ --aparte-message-padding-block: var(--aparte-space-8);
602
+ --aparte-message-padding-inline: var(--aparte-space-6);
603
+ --aparte-message-padding: var(--aparte-message-padding-block) var(--aparte-message-padding-inline);
600
604
  --aparte-message-content-padding: var(--aparte-space-5) var(--aparte-space-7);
601
605
  --aparte-action-bar-gap: var(--aparte-space-2);
602
606
  --aparte-branch-picker-gap: var(--aparte-space-2);
@@ -1030,6 +1034,10 @@ aparte-conversation-list {
1030
1034
  * <button class="aparte-btn aparte-btn--surface">1 / 2</button>
1031
1035
  * <button class="aparte-btn aparte-btn--surface">›</button>
1032
1036
  * </div>
1037
+ * <div class="aparte-btn-group" role="group" aria-label="View">
1038
+ * <button class="aparte-btn aparte-btn--primary aparte-btn--outline" aria-pressed="true">Chat</button>
1039
+ * <button class="aparte-btn aparte-btn--primary aparte-btn--outline" aria-pressed="false">Preview</button>
1040
+ * </div>
1033
1041
  *
1034
1042
  * An intent on its own is a ghost — the quietest form, and the one a chat uses most.
1035
1043
  * Add a fill when the button has to carry more weight.
@@ -1037,7 +1045,9 @@ aparte-conversation-list {
1037
1045
  * `.aparte-btn-group` fuses adjacent buttons into ONE control: the inner corners
1038
1046
  * drop, the shared borders overlap, and only the group's ends keep the radius. Give
1039
1047
  * the wrapper `role="group"` and a name — visually one control, it should read as
1040
- * one too.
1048
+ * one too. A SEGMENTED control is the same group with one segment selected: mark it
1049
+ * `aria-pressed="true"` (or `aria-selected="true"` under `role="tablist"`) and it is
1050
+ * painted solid in the group's intent, at rest — not only under the pointer.
1041
1051
  *
1042
1052
  * This layer is NEUTRAL: it is what a UI library offers, not a summary of what this
1043
1053
  * repo happens to use. A variant nothing wears still ships, because the plugin that
@@ -1242,12 +1252,23 @@ aparte-conversation-list {
1242
1252
  outline-offset: var(--aparte-btn-focus-offset);
1243
1253
  }
1244
1254
 
1245
- /* Toggled — a disclosure that is open, a toggle that is on. */
1255
+ /* Toggled — a disclosure that is open, a toggle that is on, a tab that is current. */
1246
1256
  .aparte-btn[aria-expanded='true'],
1247
- .aparte-btn[aria-pressed='true'] {
1257
+ .aparte-btn[aria-pressed='true'],
1258
+ .aparte-btn[aria-selected='true'] {
1248
1259
  background: var(--aparte-btn-bg-toggled);
1249
1260
  color: var(--aparte-btn-color-toggled);
1250
1261
  }
1262
+ /* On a coloured fill the toggled wash must be DEEPER than the hover wash (22 %), or the
1263
+ pointer reads as the state and the state reads as nothing — measured on a dark
1264
+ `Chat | Preview` group, where `--aparte-btn-bg-toggled` (surface-2) sits a hair from
1265
+ surface-1 and the selected segment showed as an empty outline until hovered (#53). */
1266
+ .aparte-btn--outline[aria-pressed='true'],
1267
+ .aparte-btn--soft[aria-pressed='true'],
1268
+ .aparte-btn--outline[aria-selected='true'],
1269
+ .aparte-btn--soft[aria-selected='true'] {
1270
+ background: color-mix(in srgb, var(--aparte-btn-intent) 30%, transparent);
1271
+ }
1251
1272
 
1252
1273
  /* Busy is NOT disabled: it stays focusable, and the app is what swaps the icon for a
1253
1274
  spinner. Only the cursor changes here. */
@@ -1294,6 +1315,24 @@ aparte-conversation-list {
1294
1315
  .aparte-btn-group > .aparte-btn:not(:first-child) { margin-inline-start: calc(var(--aparte-border-width) * -1); }
1295
1316
  .aparte-btn-group > .aparte-btn:hover,
1296
1317
  .aparte-btn-group > .aparte-btn:focus-visible { z-index: var(--aparte-z-raised); }
1318
+ /* The selected segment. Solid in the group's intent, whatever fill the segments wear:
1319
+ a segmented control has exactly one thing to say at rest, and a wash could not say
1320
+ it on dark (#53). `--surface` ignores the intent, so its groups select in neutral.
1321
+ Hover leaves it alone — the pointer must never be what makes selection legible. */
1322
+ .aparte-btn-group > .aparte-btn[aria-pressed='true'],
1323
+ .aparte-btn-group > .aparte-btn[aria-selected='true'],
1324
+ .aparte-btn-group > .aparte-btn[aria-current] {
1325
+ background: var(--aparte-btn-intent);
1326
+ border-color: var(--aparte-btn-intent);
1327
+ color: var(--aparte-btn-on-intent);
1328
+ z-index: var(--aparte-z-raised);
1329
+ }
1330
+ .aparte-btn-group > .aparte-btn[aria-pressed='true']:hover:not(:disabled),
1331
+ .aparte-btn-group > .aparte-btn[aria-selected='true']:hover:not(:disabled),
1332
+ .aparte-btn-group > .aparte-btn[aria-current]:hover:not(:disabled) {
1333
+ background: var(--aparte-btn-intent);
1334
+ filter: none;
1335
+ }
1297
1336
 
1298
1337
  /* In forced-colors the UA repaints everything and drops backgrounds, so a solid button
1299
1338
  loses the only thing marking it as the primary action. A border survives. */
@@ -4549,7 +4588,10 @@ aparte-chat-viewport[framework-managed] {
4549
4588
  overscroll-behavior: contain;
4550
4589
  display: flex;
4551
4590
  flex-direction: column;
4552
- gap: var(--aparte-message-gap);
4591
+ /* No flex gap — each bubble carries the distance to the next as its own padding,
4592
+ inside its contained box, where an older message's action bar floats (#56;
4593
+ the rationale is on `.aparte-messages-wrapper` in bubble.css). */
4594
+ gap: 0;
4553
4595
  padding: var(--aparte-viewport-padding);
4554
4596
  /* No bottom padding: a sticky child is clamped to its parent's CONTENT box, so
4555
4597
  clearance carried as padding held the scroll button `padding + spacer` above
@@ -4583,11 +4625,10 @@ aparte-chat-viewport.aparte-viewport--framework::after,
4583
4625
  aparte-chat-viewport[framework-managed]::after {
4584
4626
  content: '';
4585
4627
  flex: 0 0 calc(var(--aparte-viewport-padding) + var(--aparte-fw-spacer, 0px) + var(--aparte-bottom-inset, 0px));
4586
- /* A flex item pays the column's `gap`; the padding this replaced did not. Without
4587
- the cancel, an EMPTY transcript was 8px taller than its box and grew a scrollbar
4588
- beside the welcome state with children the spacer recalc absorbed it, which is
4589
- why the geometry specs stayed green while the smoke case regressed. */
4590
- margin-block-start: calc(var(--aparte-message-gap) * -1);
4628
+ /* The column has no `gap` any more (each bubble carries its own), so this item pays
4629
+ none — the negative margin that used to cancel it would now eat the last bubble's
4630
+ padding. (History: with a gap, an EMPTY transcript was 8px taller than its box and
4631
+ grew a scrollbar beside the welcome state.) */
4591
4632
  }
4592
4633
 
4593
4634
  aparte-chat-viewport.aparte-viewport--framework::-webkit-scrollbar {
@@ -4730,7 +4771,11 @@ aparte-chat[overlay-composer][center-empty][data-empty] > aparte-chat-viewport,
4730
4771
  .aparte-messages-wrapper {
4731
4772
  display: flex;
4732
4773
  flex-direction: column;
4733
- gap: var(--aparte-message-gap);
4774
+ /* No flex gap: the space between two turns is each bubble's own padding-block-end
4775
+ (below), so that it lies INSIDE the bubble's contained box — where the action
4776
+ bar of an older message floats (#56). A flex gap is outside every box, and the
4777
+ bubble's paint containment clipped anything placed there. */
4778
+ gap: 0;
4734
4779
  padding: var(--aparte-viewport-padding);
4735
4780
  }
4736
4781
 
@@ -4753,6 +4798,10 @@ aparte-chat-bubble {
4753
4798
  contain: layout style paint;
4754
4799
  content-visibility: auto;
4755
4800
  contain-intrinsic-size: auto 320px;
4801
+ /* The distance to the next turn, carried by the bubble rather than by the list's
4802
+ `gap`, so it is part of this box: an older message's action bar floats over it
4803
+ (#56). Same token, same distance — nothing moved. */
4804
+ padding-block-end: var(--aparte-message-gap);
4756
4805
  }
4757
4806
 
4758
4807
  .aparte-message {
@@ -4799,6 +4848,9 @@ aparte-chat-bubble {
4799
4848
  .aparte-body {
4800
4849
  flex: 1;
4801
4850
  min-width: 0;
4851
+ /* The containing block of a floating footer: the bar starts where the text starts
4852
+ and sits under it, whichever way the avatar column is laid out. */
4853
+ position: relative;
4802
4854
  }
4803
4855
 
4804
4856
  /* The message bubble — the surface fills ONLY the content (segments + text).
@@ -4866,11 +4918,22 @@ aparte-chat-bubble:last-of-type .aparte-message[data-role="assistant"]:not(.apar
4866
4918
  * Measured (UI audit 2026-08-28, §8.3 #7): 103px between the text of one turn and the
4867
4919
  * text of the next, and 34 of them were this footer — 28px of buttons nobody could
4868
4920
  * see plus its margin, under EVERY message, so that the bar could appear on hover
4869
- * without moving anything. The market floats it instead. It cannot float below the
4870
- * message here: the bubble is a paint-containment boundary (`content-visibility`),
4871
- * so anything outside its box is clipped. It floats INSIDE the box, over the header
4872
- * row's empty end — the one place it overlaps no text — as the small bordered
4873
- * toolbar a Slack message shows on hover.
4921
+ * without moving anything. The market floats it instead, BELOW the message, in the
4922
+ * space that already separates two turns: that is where ChatGPT and Claude put it,
4923
+ * and it takes no height because the space is already there.
4924
+ *
4925
+ * It floated top-right first (#56), over the header row's empty end, for one reason:
4926
+ * the bubble is a paint-containment boundary (`content-visibility`), and the space
4927
+ * between two bubbles was the list's flex `gap` — outside every box — so a bar
4928
+ * placed under the text was clipped. The gap is the bubble's own padding-block-end
4929
+ * now (see `aparte-chat-bubble` above), inside its box. The bar hangs from the
4930
+ * content column's bottom edge by `message padding-block + gap`: 16 + 12 = 28px at
4931
+ * the default density, 24 + 18 = 42px at the comfortable one, and the bar is 24px on
4932
+ * these messages — it overlaps padding, never text. The distance between turns is
4933
+ * what it was. A narrow container (responsive.css) shrinks the padding to 12 + 8 = 20:
4934
+ * 4px of the bar reach the last line's leading, under glyphs that do not descend that
4935
+ * far; and a narrow container is mostly a coarse pointer, where this block does not
4936
+ * apply.
4874
4937
  *
4875
4938
  * Three footers stay in the flow. The last assistant message: its bar is always on
4876
4939
  * show, under the reply, where every chat puts it. A message whose branch picker is
@@ -4878,38 +4941,27 @@ aparte-chat-bubble:last-of-type .aparte-message[data-role="assistant"]:not(.apar
4878
4941
  * picker must stay in view. And every message under a pointer that cannot hover, or
4879
4942
  * that is coarse — `(hover: hover) and (pointer: fine)`, because the two features are
4880
4943
  * independent and a TV remote reports both hover and coarse; responsive.css gives a
4881
- * coarse pointer a 44px bar in the flow, and floated over the header row a 44px bar
4882
- * would sit on the first line of the reply.
4883
- *
4884
- * The bar's place depends on the header row: 8px from the top, it fills the empty end
4885
- * of the "Assistant · 12:34" line and clears the text by the header's height. A host
4886
- * that hides `.aparte-header` moves that line away and should move the bar too
4887
- * (`.aparte-footer { top: … }`), or keep it in the flow.
4944
+ * coarse pointer a 44px bar in the flow, and floated under the text a 44px bar would
4945
+ * cover the next turn.
4888
4946
  *
4889
4947
  * Invisible but NOT inert, on purpose. `opacity: 0` keeps the bar hit-testable, so a
4890
- * drag-select started on the empty end of the header row begins on a button nobody
4891
- * sees — a review flagged it, and `pointer-events: none` until `:hover` was tried. It
4892
- * made the bar unreachable to anything that clicks without hovering first: Playwright
4893
- * (which hit-tests the point before it moves the mouse — the header "intercepted" the
4894
- * edit button on four projects), and with it voice control and switch access, which
4948
+ * drag-select started under a message begins on a button nobody sees a review
4949
+ * flagged it, and `pointer-events: none` until `:hover` was tried. It made the bar
4950
+ * unreachable to anything that clicks without hovering first: Playwright (which
4951
+ * hit-tests the point before it moves the mouse — the header "intercepted" the edit
4952
+ * button on four projects), and with it voice control and switch access, which
4895
4953
  * synthesise a click the same way. A control only a hover can enable is a control
4896
- * some users cannot reach; a phantom button on an empty header end is not. */
4954
+ * some users cannot reach; a phantom button in the space between two turns is not. */
4897
4955
  @media (hover: hover) and (pointer: fine) {
4898
4956
  aparte-chat-bubble:not(:last-of-type) .aparte-message:not([data-branches]) .aparte-footer,
4899
4957
  aparte-chat-bubble:last-of-type .aparte-message[data-role="user"]:not([data-branches]) .aparte-footer {
4900
4958
  position: absolute;
4901
- top: var(--aparte-space-4);
4902
- inset-inline-end: var(--aparte-space-6);
4959
+ inset-inline-start: 0;
4960
+ inset-block-end: calc(-1 * (var(--aparte-message-padding-block) + var(--aparte-message-gap)));
4903
4961
  margin: 0;
4904
4962
  z-index: 1;
4905
- }
4906
-
4907
- aparte-chat-bubble:not(:last-of-type) .aparte-message:not([data-branches]) .aparte-action-bar,
4908
- aparte-chat-bubble:last-of-type .aparte-message[data-role="user"]:not([data-branches]) .aparte-action-bar {
4909
- background: var(--aparte-surface-1);
4910
- border: var(--aparte-border-width) solid var(--aparte-border);
4911
- border-radius: var(--aparte-radius-md);
4912
- box-shadow: var(--aparte-popover-shadow);
4963
+ /* Smaller than the last reply's 28px: this one lives in the padding. */
4964
+ --aparte-action-bar-btn-size: 24px;
4913
4965
  }
4914
4966
  }
4915
4967
 
@@ -5449,11 +5501,18 @@ aparte-composer-input .aparte-ci-editor {
5449
5501
  caret-color: var(--aparte-text);
5450
5502
  }
5451
5503
 
5452
- /* Placeholder via CSS */
5504
+ /* Placeholder via CSS. It never wraps: a `::before` counts in `scrollHeight`, and the
5505
+ auto-grow measured it once in a box that had no width yet — "Type a message…" folded
5506
+ over a dozen lines and the editor came up ~300 px tall while empty (#55). A
5507
+ placeholder longer than the box is clipped, which is what every native input does. */
5453
5508
  aparte-composer-input .aparte-ci-editor:empty::before {
5454
5509
  content: attr(data-placeholder);
5455
5510
  color: var(--aparte-input-placeholder);
5456
5511
  pointer-events: none;
5512
+ white-space: nowrap;
5513
+ overflow: hidden;
5514
+ text-overflow: ellipsis;
5515
+ display: block;
5457
5516
  }
5458
5517
 
5459
5518
  aparte-composer-input .aparte-ci-editor[aria-disabled="true"] {
@@ -7262,6 +7321,7 @@ aparte-split[orientation='vertical'] .aparte-split__scrim,
7262
7321
  Not "position: 100% and a zero-size handle": that leaves a focusable separator of
7263
7322
  no width sitting in the tab order. */
7264
7323
  aparte-split[data-stacked],
7324
+ aparte-split[single],
7265
7325
  .aparte-split--only-start,
7266
7326
  .aparte-split--only-end {
7267
7327
  grid-template-columns: minmax(0, 1fr);
@@ -7269,17 +7329,23 @@ aparte-split[data-stacked],
7269
7329
  }
7270
7330
 
7271
7331
  aparte-split[data-stacked] > .aparte-split__handle,
7332
+ aparte-split[single] > .aparte-split__handle,
7272
7333
  .aparte-split--only-start > .aparte-split__handle,
7273
7334
  .aparte-split--only-end > .aparte-split__handle {
7274
7335
  display: none;
7275
7336
  }
7276
7337
 
7338
+ /* `single` is the host's word for the same state (#54): one pane, whatever the width,
7339
+ the one `pane` names. Same rules as the breakpoint's, so the element reads both the
7340
+ same way. */
7277
7341
  aparte-split[data-stacked]:not([pane='end']) > :nth-child(3),
7342
+ aparte-split[single]:not([pane='end']) > :nth-child(3),
7278
7343
  .aparte-split--only-start > :nth-child(3) {
7279
7344
  display: none;
7280
7345
  }
7281
7346
 
7282
7347
  aparte-split[data-stacked][pane='end'] > :nth-child(1),
7348
+ aparte-split[single][pane='end'] > :nth-child(1),
7283
7349
  .aparte-split--only-end > :nth-child(1) {
7284
7350
  display: none;
7285
7351
  }
@@ -7578,7 +7644,8 @@ aparte-split[disabled] > .aparte-split__handle:hover {
7578
7644
  without container-query support keep the wide defaults. */
7579
7645
  @container (max-width: 520px) {
7580
7646
  .aparte-message {
7581
- --aparte-message-padding: var(--aparte-space-6) var(--aparte-space-4);
7647
+ --aparte-message-padding-block: var(--aparte-space-6);
7648
+ --aparte-message-padding-inline: var(--aparte-space-4);
7582
7649
  --aparte-message-gap: var(--aparte-space-4);
7583
7650
  }
7584
7651
 
package/dist/index.js CHANGED
@@ -2311,6 +2311,24 @@ class AparteChatViewport extends HTMLElement {
2311
2311
  /** How long after a scroll of ours we keep re-anchoring while the layout churns.
2312
2312
  * Not `readonly`: a test shortens it to run past the window's end in a few frames. */
2313
2313
  _settleWindowMs = 400;
2314
+ /**
2315
+ * Until when a smooth scroll of ours is in flight (#57). A user send glides to the
2316
+ * bottom with `scrollTo({ behavior: 'smooth' })`; the assistant placeholder and the
2317
+ * first tokens used to land INSIDE that glide through the streaming path — an instant
2318
+ * `scrollTop = scrollHeight` plus the settle chain — and an instant write cancels a
2319
+ * running smooth scroll in every engine. Measured before the fix: five instant writes
2320
+ * in the send's own frame (the spacer recalculation scrolls synchronously, the
2321
+ * mutation observer queues another), so there never was a glide to cut — the view
2322
+ * teleported. While this deadline is ahead, every bottom-pin RE-TARGETS the glide
2323
+ * (a second smooth `scrollTo` is continuous) instead of writing `scrollTop`; the
2324
+ * browser's `scrollend` closes it early where supported. Streaming is instant again
2325
+ * after that, as it must be to keep up with token bursts.
2326
+ */
2327
+ _glideUntil = 0;
2328
+ /** The glide's budget when `scrollend` is not there to close it: Chromium's smooth scroll over a viewport takes ~300–500 ms. */
2329
+ _glideWindowMs = 450;
2330
+ /** Armed with each glide: when the window closes, a view still short of the bottom is pinned. */
2331
+ _glideSettleTimer = null;
2314
2332
  /** Deadline of the settle in flight (0 = none). Pushed forward, never stacked. */
2315
2333
  _settleUntil = 0;
2316
2334
  _settleRafId = null;
@@ -2347,6 +2365,7 @@ class AparteChatViewport extends HTMLElement {
2347
2365
  constructor() {
2348
2366
  super();
2349
2367
  this._handleScroll = this._handleScroll.bind(this);
2368
+ this._handleScrollEnd = this._handleScrollEnd.bind(this);
2350
2369
  }
2351
2370
  connectedCallback() {
2352
2371
  if (this.hasAttribute("framework-managed")) this._frameworkManagedDOM = true;
@@ -2727,9 +2746,12 @@ class AparteChatViewport extends HTMLElement {
2727
2746
  }
2728
2747
  this._pruneRenderedBubbles();
2729
2748
  this._syncBusy();
2730
- this._recalculateSpacer();
2731
2749
  if (message.role === "user") {
2732
2750
  this._isAutoScrollEnabled = true;
2751
+ this._smoothScrollToBottom();
2752
+ }
2753
+ this._recalculateSpacer();
2754
+ if (message.role === "user") {
2733
2755
  requestAnimationFrame(() => {
2734
2756
  if (this._isAutoScrollEnabled) this._smoothScrollToBottom();
2735
2757
  });
@@ -3081,16 +3103,9 @@ class AparteChatViewport extends HTMLElement {
3081
3103
  }
3082
3104
  _autoScroll() {
3083
3105
  if (this._isAutoScrollEnabled) {
3084
- if (this._smoothScrollOnce) {
3085
- this._smoothScrollOnce = false;
3086
- requestAnimationFrame(() => {
3087
- if (this._isAutoScrollEnabled) this._smoothScrollToBottom();
3088
- });
3089
- } else {
3090
- requestAnimationFrame(() => {
3091
- if (this._isAutoScrollEnabled) this._scrollToBottom();
3092
- });
3093
- }
3106
+ requestAnimationFrame(() => {
3107
+ if (this._isAutoScrollEnabled) this._scrollToBottom();
3108
+ });
3094
3109
  }
3095
3110
  this._pruneRenderedBubbles();
3096
3111
  }
@@ -3234,6 +3249,14 @@ class AparteChatViewport extends HTMLElement {
3234
3249
  if (!inGutter) return;
3235
3250
  }
3236
3251
  this._readerInputAt = performance.now();
3252
+ if (this._gliding() && this._container) {
3253
+ this._glideUntil = 0;
3254
+ if (e.type === "wheel" || e.type === "touchmove") {
3255
+ this._ownScrollAt = performance.now();
3256
+ const here = this._container.scrollTop;
3257
+ this._container.scrollTop = here;
3258
+ }
3259
+ }
3237
3260
  };
3238
3261
  _onBranchNavigate = (e) => {
3239
3262
  const evt = e;
@@ -3242,6 +3265,7 @@ class AparteChatViewport extends HTMLElement {
3242
3265
  };
3243
3266
  _setupEventListeners() {
3244
3267
  this._container?.addEventListener("scroll", this._handleScroll, { passive: true });
3268
+ this._container?.addEventListener("scrollend", this._handleScrollEnd, { passive: true });
3245
3269
  for (const type of ["wheel", "touchmove", "pointerdown", "keydown"]) {
3246
3270
  this._container?.addEventListener(type, this._noteReaderInput, { passive: true });
3247
3271
  }
@@ -3275,8 +3299,16 @@ class AparteChatViewport extends HTMLElement {
3275
3299
  this._overlayObserver.observe(this._overlayRoot, { childList: true });
3276
3300
  this._updateOverlayInset();
3277
3301
  }
3278
- this._mutationObserver = new MutationObserver(() => {
3302
+ this._mutationObserver = new MutationObserver((mutations) => {
3279
3303
  if (this._frameworkManagedDOM) this._keepScrollButtonLast();
3304
+ if (this._isAutoScrollEnabled && !this._gliding()) {
3305
+ const added = mutations.flatMap((m2) => Array.from(m2.addedNodes)).filter((n2) => n2 instanceof Element && n2.tagName === "APARTE-CHAT-BUBBLE");
3306
+ const only = added.length === 1 ? added[0] : void 0;
3307
+ if (only && only.getAttribute("data-role") === "user" && only.parentElement) {
3308
+ const bubbles = only.parentElement.querySelectorAll("aparte-chat-bubble");
3309
+ if (bubbles[bubbles.length - 1] === only) this._smoothScrollOnce = true;
3310
+ }
3311
+ }
3280
3312
  if (this._isAutoScrollEnabled) {
3281
3313
  requestAnimationFrame(() => {
3282
3314
  if (this._isAutoScrollEnabled) this._scrollToBottom();
@@ -3352,10 +3384,41 @@ class AparteChatViewport extends HTMLElement {
3352
3384
  }
3353
3385
  _scrollToBottom() {
3354
3386
  if (!this._container) return;
3387
+ if (this._smoothScrollOnce) {
3388
+ this._smoothScrollOnce = false;
3389
+ this._smoothScrollToBottom();
3390
+ return;
3391
+ }
3392
+ if (this._gliding()) {
3393
+ this._retargetGlide();
3394
+ return;
3395
+ }
3355
3396
  this._ownScrollAt = performance.now();
3356
3397
  this._container.scrollTop = this._container.scrollHeight;
3357
3398
  this._settleAtBottom();
3358
3399
  }
3400
+ _gliding() {
3401
+ return performance.now() < this._glideUntil;
3402
+ }
3403
+ /** A second smooth `scrollTo` is continuous where an instant write is a cut. */
3404
+ _retargetGlide() {
3405
+ if (!this._container) return;
3406
+ this._ownScrollAt = performance.now();
3407
+ this._container.scrollTo({ top: this._container.scrollHeight, behavior: "smooth" });
3408
+ }
3409
+ /**
3410
+ * The browser says a scroll came to rest — the glide is over ONLY if it rested at the
3411
+ * bottom. WebKit fires `scrollend` when a smooth scroll is REPLACED by another (our own
3412
+ * re-target), 45–60 ms into the glide and hundreds of pixels short; taken at its word,
3413
+ * that closed the window and the next pin was the cut this guard exists to prevent
3414
+ * (react-webkit and vanilla-webkit, 2 of 2). Short of the bottom, the budget keeps
3415
+ * bounding the window.
3416
+ */
3417
+ _handleScrollEnd() {
3418
+ if (!this._container) return;
3419
+ const max = this._container.scrollHeight - this._container.clientHeight;
3420
+ if (max - this._container.scrollTop <= 1) this._glideUntil = 0;
3421
+ }
3359
3422
  /**
3360
3423
  * Confirm over the next few frames that we actually reached the bottom.
3361
3424
  *
@@ -3401,7 +3464,7 @@ class AparteChatViewport extends HTMLElement {
3401
3464
  return;
3402
3465
  }
3403
3466
  const max = this._container.scrollHeight - this._container.clientHeight;
3404
- if (max - this._container.scrollTop > 1) {
3467
+ if (max - this._container.scrollTop > 1 && !this._gliding()) {
3405
3468
  this._ownScrollAt = performance.now();
3406
3469
  this._container.scrollTop = max;
3407
3470
  }
@@ -3414,7 +3477,15 @@ class AparteChatViewport extends HTMLElement {
3414
3477
  if (!this._container) return;
3415
3478
  this._ownScrollAt = performance.now();
3416
3479
  if (typeof this._container.scrollTo === "function" && !this._prefersReducedMotion()) {
3480
+ this._glideUntil = performance.now() + this._glideWindowMs;
3417
3481
  this._container.scrollTo({ top: this._container.scrollHeight, behavior: "smooth" });
3482
+ if (this._glideSettleTimer !== null) clearTimeout(this._glideSettleTimer);
3483
+ this._glideSettleTimer = setTimeout(() => {
3484
+ this._glideSettleTimer = null;
3485
+ if (!this._container || !this._isAutoScrollEnabled || this._gliding()) return;
3486
+ const max = this._container.scrollHeight - this._container.clientHeight;
3487
+ if (max - this._container.scrollTop > 1) this._scrollToBottom();
3488
+ }, this._glideWindowMs + 16);
3418
3489
  } else {
3419
3490
  this._container.scrollTop = this._container.scrollHeight;
3420
3491
  }
@@ -3530,6 +3601,11 @@ class AparteChatViewport extends HTMLElement {
3530
3601
  }
3531
3602
  _cleanup() {
3532
3603
  this._container?.removeEventListener("scroll", this._handleScroll);
3604
+ this._container?.removeEventListener("scrollend", this._handleScrollEnd);
3605
+ if (this._glideSettleTimer !== null) {
3606
+ clearTimeout(this._glideSettleTimer);
3607
+ this._glideSettleTimer = null;
3608
+ }
3533
3609
  for (const type of ["wheel", "touchmove", "pointerdown", "keydown"]) {
3534
3610
  this._container?.removeEventListener(type, this._noteReaderInput);
3535
3611
  }
@@ -4041,6 +4117,14 @@ class AparteComposerInput extends HTMLElement {
4041
4117
  _editor = null;
4042
4118
  _maxHeight = 200;
4043
4119
  _minHeight = 44;
4120
+ /**
4121
+ * Re-measures the editor when its WIDTH changes (#55). The three fixed instants
4122
+ * (connect, next frame, fonts.ready) all miss the same case: a box not yet laid
4123
+ * out — the placeholder wraps in a 0 px column, `scrollHeight` reads ~300 px, and
4124
+ * that became the inline height until the first keystroke. Width is the variable
4125
+ * none of them watched.
4126
+ */
4127
+ _resizeObserver = null;
4044
4128
  _unsubscribes = [];
4045
4129
  // Bound handlers
4046
4130
  _onInput = this._handleInput.bind(this);
@@ -4061,6 +4145,10 @@ class AparteComposerInput extends HTMLElement {
4061
4145
  this._editor?.addEventListener("paste", this._onPaste);
4062
4146
  this._connectToRoot();
4063
4147
  this._scheduleInitialReflow();
4148
+ if (this._editor && typeof ResizeObserver === "function") {
4149
+ this._resizeObserver = new ResizeObserver(() => this._adjustHeight());
4150
+ this._resizeObserver.observe(this._editor);
4151
+ }
4064
4152
  this._unsubscribes.push(subscribeConfigChange(this, () => this._updatePlaceholder()));
4065
4153
  }
4066
4154
  disconnectedCallback() {
@@ -4069,6 +4157,8 @@ class AparteComposerInput extends HTMLElement {
4069
4157
  this._editor?.removeEventListener("focus", this._onFocus);
4070
4158
  this._editor?.removeEventListener("blur", this._onBlur);
4071
4159
  this._editor?.removeEventListener("paste", this._onPaste);
4160
+ this._resizeObserver?.disconnect();
4161
+ this._resizeObserver = null;
4072
4162
  this._unsubscribes.forEach((fn) => fn());
4073
4163
  this._unsubscribes = [];
4074
4164
  }
@@ -6133,7 +6223,7 @@ function toPercent(raw, fallback) {
6133
6223
  }
6134
6224
  class AparteSplit extends HTMLElement {
6135
6225
  static get observedAttributes() {
6136
- return ["position", "orientation", "primary", "collapsed", "breakpoint", "pane", "disabled", "label"];
6226
+ return ["position", "orientation", "primary", "collapsed", "breakpoint", "pane", "disabled", "label", "single"];
6137
6227
  }
6138
6228
  _handle = null;
6139
6229
  _scrim = null;
@@ -6244,7 +6334,14 @@ class AparteSplit extends HTMLElement {
6244
6334
  * alone would measure a one-track grid and commit `position="100"`.
6245
6335
  */
6246
6336
  get stacked() {
6247
- return this.hasAttribute("data-stacked") || this.classList.contains("aparte-split--only-start") || this.classList.contains("aparte-split--only-end");
6337
+ return this.hasAttribute("data-stacked") || this.hasAttribute("single") || this.classList.contains("aparte-split--only-start") || this.classList.contains("aparte-split--only-end");
6338
+ }
6339
+ /** One pane on demand, whatever the width (#54). Reflected. */
6340
+ get single() {
6341
+ return this.hasAttribute("single");
6342
+ }
6343
+ set single(value) {
6344
+ this.toggleAttribute("single", value);
6248
6345
  }
6249
6346
  // ─── Public API ───────────────────────────────────────────────────────
6250
6347
  /** Fold the primary pane to its minimum, remembering the size it had. */
@@ -6375,6 +6472,10 @@ class AparteSplit extends HTMLElement {
6375
6472
  if (this.disabled && this._dragging) this._endDrag("pointer", this._dragStartPercent);
6376
6473
  this._stampHandle();
6377
6474
  return;
6475
+ case "single":
6476
+ if (this.single && this._dragging) this._endDrag("pointer", this._dragStartPercent);
6477
+ this._stampHandle();
6478
+ return;
6378
6479
  case "breakpoint":
6379
6480
  this._watchBreakpoint();
6380
6481
  return;