@aparte/core 0.16.0 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/components/chat/aparte-chat.d.ts +8 -1
  2. package/dist/components/chat/aparte-chat.d.ts.map +1 -1
  3. package/dist/components/composer/aparte-composer-action.d.ts +1 -1
  4. package/dist/components/composer/aparte-composer-action.d.ts.map +1 -1
  5. package/dist/components/composer/aparte-composer-add-attachment.d.ts +1 -1
  6. package/dist/components/composer/aparte-composer-add-attachment.d.ts.map +1 -1
  7. package/dist/components/composer/aparte-composer-attachments.d.ts +1 -1
  8. package/dist/components/composer/aparte-composer-cancel.d.ts +1 -1
  9. package/dist/components/composer/aparte-composer-cancel.d.ts.map +1 -1
  10. package/dist/components/composer/aparte-composer-input.d.ts +1 -1
  11. package/dist/components/composer/aparte-composer-input.d.ts.map +1 -1
  12. package/dist/components/composer/aparte-composer-send.d.ts +1 -1
  13. package/dist/components/composer/aparte-composer-send.d.ts.map +1 -1
  14. package/dist/components/composer/aparte-composer.d.ts +9 -1
  15. package/dist/components/composer/aparte-composer.d.ts.map +1 -1
  16. package/dist/components/elicitation/aparte-elicitation.d.ts +1 -1
  17. package/dist/components/split/aparte-split.d.ts +1 -1
  18. package/dist/components/suggestions/aparte-suggestions.d.ts +1 -1
  19. package/dist/components/viewport/aparte-chat-viewport.d.ts +25 -0
  20. package/dist/components/viewport/aparte-chat-viewport.d.ts.map +1 -1
  21. package/dist/custom-elements.json +12104 -12035
  22. package/dist/generated/element-attributes.d.ts +2 -0
  23. package/dist/generated/element-attributes.d.ts.map +1 -1
  24. package/dist/index.css +194 -9
  25. package/dist/index.js +68 -14
  26. package/dist/index.js.map +1 -1
  27. package/package.json +2 -2
@@ -8,6 +8,8 @@ export interface AparteChatAttributes {
8
8
  'center-empty'?: boolean;
9
9
  /** Add the file picker + chips strip to the default composition (opt-in: the host must consume the files — an `AparteClient` does, a hand-rolled loop must read `event.detail.files`) */
10
10
  attachments?: boolean;
11
+ /** The ChatGPT anatomy, opt-in: the transcript's scroll surface spans the whole column and the composer (with the rest of the bottom stack) floats over it, so the scrollbar runs edge to edge instead of stopping at the composer's top. The viewport measures the stack and publishes `--aparte-bottom-inset`; content, spacer and the scroll button clear it. Read when the viewport wires its observers — set it in the initial markup. Also honoured on a wrapper's `[data-aparte-chat]` root. */
12
+ 'overlay-composer'?: boolean;
11
13
  /** The wrapper's explicit hands-off signal: set it and this element composes none of its own children, because the framework owns them. Read once at connect (it is not observed), so it has to be in the initial markup. Angular's wrapper sets it on this element — its component selector IS `aparte-chat`; React/Vue/Svelte render a `[data-aparte-chat]` div and never create this element at all. */
12
14
  'framework-managed'?: boolean;
13
15
  }
@@ -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
@@ -1025,10 +1025,20 @@ aparte-conversation-list {
1025
1025
  * <button class="aparte-btn aparte-btn--icon" aria-label="Copy">
1026
1026
  * <aparte-icon name="copy"></aparte-icon>
1027
1027
  * </button>
1028
+ * <div class="aparte-btn-group" role="group" aria-label="Versions">
1029
+ * <button class="aparte-btn aparte-btn--surface">‹</button>
1030
+ * <button class="aparte-btn aparte-btn--surface">1 / 2</button>
1031
+ * <button class="aparte-btn aparte-btn--surface">›</button>
1032
+ * </div>
1028
1033
  *
1029
1034
  * An intent on its own is a ghost — the quietest form, and the one a chat uses most.
1030
1035
  * Add a fill when the button has to carry more weight.
1031
1036
  *
1037
+ * `.aparte-btn-group` fuses adjacent buttons into ONE control: the inner corners
1038
+ * drop, the shared borders overlap, and only the group's ends keep the radius. Give
1039
+ * the wrapper `role="group"` and a name — visually one control, it should read as
1040
+ * one too.
1041
+ *
1032
1042
  * This layer is NEUTRAL: it is what a UI library offers, not a summary of what this
1033
1043
  * repo happens to use. A variant nothing wears still ships, because the plugin that
1034
1044
  * needs it is not written yet, and its absence is what makes an author invent a
@@ -4337,6 +4347,59 @@ aparte-chat > aparte-chat-viewport,
4337
4347
  min-height: 0;
4338
4348
  }
4339
4349
 
4350
+ /* ─── Overlay composer (`overlay-composer`) ───────────────────────────────────
4351
+ The ChatGPT anatomy, opt-in: the transcript's scroll surface spans the WHOLE
4352
+ column and the composer floats over it, so the scrollbar runs edge to edge
4353
+ instead of stopping at the composer's top. Opt-in, never the default — a chat
4354
+ embedded in a small box should not have its composer eating the transcript.
4355
+
4356
+ The viewport leaves the flow (absolute over the shell) and everything else —
4357
+ elicitation, an above-composer row, the composer — keeps flowing, pushed to the
4358
+ bottom and painted over it. The transcript clears that stack through
4359
+ `--aparte-bottom-inset`, which the viewport measures from the stack's top edge
4360
+ (see `_updateOverlayInset`): the `::after` spacer grows by it in
4361
+ framework-managed mode, the container's own padding in core mode, and the
4362
+ scroll button's `bottom` rides it in both. The inset var and its readers are
4363
+ unconditional (0px when unset), so a host that overlays a composer of its own
4364
+ can write the variable itself without this attribute.
4365
+
4366
+ Three shell shapes, because the wrappers differ: the vanilla <aparte-chat>
4367
+ element, the [data-aparte-chat] div React/Vue/Svelte render, and Angular's
4368
+ inner .aparte-chat-container (its host IS aparte-chat, but the viewport is a
4369
+ child of the inner div, so that div is the shell the recipe lays out — the
4370
+ Angular wrapper binds the attribute there). The inner-div shape declares its
4371
+ own flex column: unlike the other two it inherits none. */
4372
+ aparte-chat[overlay-composer],
4373
+ [data-aparte-chat][overlay-composer],
4374
+ .aparte-chat-container[overlay-composer] {
4375
+ position: relative;
4376
+ display: flex;
4377
+ flex-direction: column;
4378
+ justify-content: flex-end;
4379
+ }
4380
+
4381
+ aparte-chat[overlay-composer] > aparte-chat-viewport,
4382
+ [data-aparte-chat][overlay-composer] > aparte-chat-viewport,
4383
+ .aparte-chat-container[overlay-composer] > aparte-chat-viewport {
4384
+ position: absolute;
4385
+ /* The padding box, so the scroll surface reaches the shell's true bottom edge —
4386
+ the `--aparte-chat-bottom-gap` padding stays under the floating stack and the
4387
+ measured inset covers it. */
4388
+ inset: 0;
4389
+ height: auto;
4390
+ z-index: 0;
4391
+ }
4392
+
4393
+ /* The bottom stack paints over the absolutely positioned viewport. `:not()` is
4394
+ exact here: the only child that is not the viewport IS the stack (the scroll
4395
+ button lives inside the viewport). */
4396
+ aparte-chat[overlay-composer] > :not(aparte-chat-viewport),
4397
+ [data-aparte-chat][overlay-composer] > :not(aparte-chat-viewport),
4398
+ .aparte-chat-container[overlay-composer] > :not(aparte-chat-viewport) {
4399
+ position: relative;
4400
+ z-index: var(--aparte-z-raised);
4401
+ }
4402
+
4340
4403
  /* center-empty: the shell centers the composer as a welcome state until the first
4341
4404
  message, then slides to the normal layout. The component toggles [data-empty]
4342
4405
  on itself by watching its viewport — only `flex-grow` animates, so the composer
@@ -4382,6 +4445,11 @@ aparte-chat-viewport {
4382
4445
  width: 100%;
4383
4446
  overflow-y: auto;
4384
4447
  overflow-x: hidden;
4448
+ /* The overlay clearance (0 outside overlay mode). Padding is SAFE on this box —
4449
+ the scroll button is absolute on the host, which does not scroll — where the
4450
+ framework-managed host must carry the same clearance in its ::after instead,
4451
+ because there the button is a sticky CHILD and padding would clamp it. */
4452
+ padding-bottom: var(--aparte-bottom-inset, 0px);
4385
4453
  overscroll-behavior: contain;
4386
4454
  scrollbar-width: thin;
4387
4455
  scrollbar-color: var(--aparte-scrollbar-thumb) var(--aparte-scrollbar-track);
@@ -4392,6 +4460,11 @@ aparte-chat-viewport {
4392
4460
  scrollbar-gutter: stable both-edges;
4393
4461
  }
4394
4462
 
4463
+ /* NOT dead code: on modern Chromium/WebKit these ::-webkit-scrollbar-* blocks are
4464
+ ignored the moment `scrollbar-width`/`scrollbar-color` above are non-initial
4465
+ (CSSWG resolution, Chrome 121+). They exist for Safari BEFORE 18.2, which has no
4466
+ `scrollbar-width` at all - remove them and old Safari falls back to the fat
4467
+ native bar. Re-measured 2026-08-30 against MDN before a cleanup deletes them. */
4395
4468
  .aparte-viewport-container::-webkit-scrollbar {
4396
4469
  width: var(--aparte-scrollbar-width);
4397
4470
  }
@@ -4424,7 +4497,9 @@ aparte-chat-viewport.aparte-viewport--framework::-webkit-scrollbar-thumb {
4424
4497
  .aparte-scroll-btn {
4425
4498
  --aparte-btn-size: var(--aparte-scroll-btn-size);
4426
4499
  position: absolute;
4427
- bottom: var(--aparte-space-8);
4500
+ /* Rides the overlay clearance (0 outside overlay mode), so it floats above a
4501
+ composer laid over the transcript rather than behind it. */
4502
+ bottom: calc(var(--aparte-space-8) + var(--aparte-bottom-inset, 0px));
4428
4503
  left: 50%;
4429
4504
  transform: translateX(-50%);
4430
4505
  z-index: var(--aparte-z-floating);
@@ -4450,10 +4525,19 @@ aparte-chat-viewport.aparte-viewport--framework::-webkit-scrollbar-thumb {
4450
4525
  the viewport does NOT relocate them into an internal wrapper (that would break
4451
4526
  the framework's DOM reconciliation). Instead the HOST itself is the scroll
4452
4527
  surface — these rules mirror .aparte-viewport-container + .aparte-messages-wrapper
4453
- onto the host, and the bottom spacer becomes an additive padding (no element,
4454
- so it can't collide with the framework's children). */
4528
+ onto the host, and the bottom spacer becomes an `::after` flex item — still
4529
+ nothing in the DOM, so it can't collide with the framework's children. */
4455
4530
  aparte-chat-viewport.aparte-viewport--framework,
4456
4531
  aparte-chat-viewport[framework-managed] {
4532
+ /* The base rule gives this element `width: 100%`, and core ships no global
4533
+ border-box reset — every `box-sizing` in these sheets is per-element, on purpose.
4534
+ So the padding below was ADDED to 100% and the host overflowed its own chat by
4535
+ 32px, which `aparte-chat`'s `overflow: hidden` then clipped: measured on a 1500px
4536
+ chat, the host was 1532 wide and hung 32px past the end edge. An app with its own
4537
+ `* { box-sizing: border-box }` never saw it, which is exactly why it survived —
4538
+ every example in this repo has one. The composer cannot share a column with a
4539
+ transcript that is off-centre, so it is fixed here rather than compensated there. */
4540
+ box-sizing: border-box;
4457
4541
  overflow-y: auto;
4458
4542
  overflow-x: hidden;
4459
4543
  overscroll-behavior: contain;
@@ -4461,7 +4545,10 @@ aparte-chat-viewport[framework-managed] {
4461
4545
  flex-direction: column;
4462
4546
  gap: var(--aparte-message-gap);
4463
4547
  padding: var(--aparte-viewport-padding);
4464
- padding-bottom: calc(var(--aparte-viewport-padding) + var(--aparte-fw-spacer, 0px));
4548
+ /* No bottom padding: a sticky child is clamped to its parent's CONTENT box, so
4549
+ clearance carried as padding held the scroll button `padding + spacer` above
4550
+ the bottom edge — the whole clearance lives in the `::after` below instead. */
4551
+ padding-bottom: 0;
4465
4552
  scrollbar-width: thin;
4466
4553
  scrollbar-color: var(--aparte-scrollbar-thumb) var(--aparte-scrollbar-track);
4467
4554
  /* Same gutter rule as `.aparte-viewport-container` above: the host is the scroll
@@ -4479,6 +4566,24 @@ aparte-chat-viewport[framework-managed] > * {
4479
4566
  flex-shrink: 0;
4480
4567
  }
4481
4568
 
4569
+ /* The bottom spacer: the base padding plus whatever `_setSpacerHeight` writes to
4570
+ `--aparte-fw-spacer`. A flex item rather than host padding because the scroll
4571
+ button's `position: sticky` is clamped to the CONTENT box — clearance carried as
4572
+ padding is territory the button can never enter, and it floated the full
4573
+ `padding + spacer` above the bottom edge (measured: 48-278px for 32 asked).
4574
+ `flex: 0 0` spells out `flex-shrink: 0`: the `> *` pin above matches elements
4575
+ only, never a pseudo. */
4576
+ aparte-chat-viewport.aparte-viewport--framework::after,
4577
+ aparte-chat-viewport[framework-managed]::after {
4578
+ content: '';
4579
+ flex: 0 0 calc(var(--aparte-viewport-padding) + var(--aparte-fw-spacer, 0px) + var(--aparte-bottom-inset, 0px));
4580
+ /* A flex item pays the column's `gap`; the padding this replaced did not. Without
4581
+ the cancel, an EMPTY transcript was 8px taller than its box and grew a scrollbar
4582
+ beside the welcome state — with children the spacer recalc absorbed it, which is
4583
+ why the geometry specs stayed green while the smoke case regressed. */
4584
+ margin-block-start: calc(var(--aparte-message-gap) * -1);
4585
+ }
4586
+
4482
4587
  aparte-chat-viewport.aparte-viewport--framework::-webkit-scrollbar {
4483
4588
  width: var(--aparte-scrollbar-width);
4484
4589
  }
@@ -4489,18 +4594,29 @@ aparte-chat-viewport.aparte-viewport--framework::-webkit-scrollbar-track {
4489
4594
 
4490
4595
  /* The scroll button lives INSIDE the scrolling host here (last child), so it
4491
4596
  floats with `position: sticky` instead of the `absolute` used in core mode
4492
- (where it sits outside the scroll container). */
4597
+ (where it sits outside the scroll container). `order: 1` puts its FLOW position
4598
+ after the `::after` spacer (source order cannot — a pseudo always renders last):
4599
+ a bottom-sticky element sits at its flow position whenever that is above the
4600
+ sticky line, so a button flowing before a 230px spacer drifted up to 262px from
4601
+ the edge as the reader neared the bottom. Flowing last, its flow position is the
4602
+ very end and the sticky line always wins. */
4493
4603
  aparte-chat-viewport.aparte-viewport--framework > .aparte-scroll-btn {
4494
4604
  position: sticky;
4495
- bottom: var(--aparte-space-8);
4605
+ bottom: calc(var(--aparte-space-8) + var(--aparte-bottom-inset, 0px));
4496
4606
  left: auto;
4497
4607
  align-self: center;
4498
4608
  transform: none;
4499
4609
  flex-shrink: 0;
4610
+ order: 1;
4500
4611
  }
4501
4612
 
4613
+ /* Fade only, no slide: since `order: 1` the button is the LAST flow content, so a
4614
+ hidden-state translate overhangs the content end by its full distance — 8px of
4615
+ scrollable overflow, which on an EMPTY transcript is the whole scrollbar (the old
4616
+ padding used to absorb the overhang; the ::after spacer flows BEFORE the button
4617
+ and cannot). Core mode keeps the slide: its button is absolute, out of flow. */
4502
4618
  aparte-chat-viewport.aparte-viewport--framework > .aparte-scroll-btn.aparte-scroll-btn--hidden {
4503
- transform: translateY(var(--aparte-slide-distance));
4619
+ transform: none;
4504
4620
  }
4505
4621
 
4506
4622
  /* =========================================
@@ -4586,6 +4702,18 @@ aparte-chat-status:not([visible]) {
4586
4702
  aparte-chat-viewport.aparte-viewport--framework {
4587
4703
  flex-grow: 0;
4588
4704
  }
4705
+
4706
+ /* overlay-composer × center-empty: while the welcome state centres the composer,
4707
+ the overlay stands down — an empty transcript has nothing to scroll, so the
4708
+ surface has nothing to span, and an absolute viewport would pin the welcome to
4709
+ the top of the page while the composer floated alone at the bottom. One
4710
+ property: back in the flow, the centring rules above govern the rest. The first
4711
+ message drops the empty flag and the overlay takes over. */
4712
+ aparte-chat[overlay-composer][center-empty][data-empty] > aparte-chat-viewport,
4713
+ [data-aparte-chat][overlay-composer][data-aparte-empty] > aparte-chat-viewport,
4714
+ .aparte-chat-container[overlay-composer][data-aparte-empty] > aparte-chat-viewport {
4715
+ position: static;
4716
+ }
4589
4717
  /*
4590
4718
  * aparté — A message: avatar, name, timestamp, body, action bar, branch picker.
4591
4719
  *
@@ -5259,6 +5387,34 @@ aparte-composer {
5259
5387
  display: block;
5260
5388
  /* Container-query root — see aparte-chat-viewport. */
5261
5389
  container-type: inline-size;
5390
+ /* The transcript's own inline gutter, read from the token that produces it.
5391
+ `.aparte-composer-shell` and `.aparte-message` both cap at
5392
+ `--aparte-message-max-width` and centre with `margin: 0 auto`, so on a container
5393
+ WIDER than the cap they lined up by construction. Below it the cap stops applying,
5394
+ each becomes 100% of its own parent — and this element had no padding, so the
5395
+ composer went flush to the chat's edges while the transcript kept its inset.
5396
+ Measured at a 512px chat: message column 26/26, composer 0/0. Every chat narrower
5397
+ than 800px was hit — a phone, an embedded widget, either pane of `<aparte-split>`,
5398
+ an app shell whose docked sidebar leaves the chat narrow on a wide window.
5399
+
5400
+ INLINE ONLY, and that is not shorthand-avoidance: the block axis is already spaced
5401
+ deliberately, 16px above from the viewport and 16px below from
5402
+ `--aparte-chat-bottom-gap` (shell.css), which were tuned to match. The `padding`
5403
+ shorthand would add a second 16px to each.
5404
+
5405
+ No 520px step to follow the transcript's, and it is not an oversight: a container
5406
+ query cannot match its own container, and this element IS one (the line above).
5407
+ Nothing above it is a container either, and `aparte-chat` may never become one —
5408
+ it would then be the containing block of the `position: fixed` select dropdown
5409
+ that exists to escape `overflow: hidden` (select.css, and the same warning in
5410
+ split.css). So below 520px the transcript tightens to 8px and this stays at 16.
5411
+ A flat 16 was measured against a step: the step wins for vanilla at phone width
5412
+ and LOSES for framework-managed, which is the majority path, because the
5413
+ transcript's own step targets a class framework mode never builds. Flat is a
5414
+ uniform 10px worst case in both modes — which is the scrollbar gutter the
5415
+ transcript reserves and the composer cannot, and which is 0 on the overlay
5416
+ scrollbars every phone has. */
5417
+ padding-inline: var(--aparte-viewport-padding);
5262
5418
  }
5263
5419
 
5264
5420
  aparte-composer-input {
@@ -5593,7 +5749,16 @@ aparte-composer-cancel .aparte-cc-button:hover {
5593
5749
  reply text beside it for attention it did not deserve. */
5594
5750
  .aparte-tool-summary {
5595
5751
  display: flex; align-items: center; gap: var(--aparte-space-4);
5596
- padding: var(--aparte-space-2) var(--aparte-space-3); margin-inline-start: calc(var(--aparte-space-3) * -1);
5752
+ /* The negative margin is what puts the row's CONTENT on the message column rather
5753
+ than one padding in from it — the padding here is the hover surface, not the
5754
+ column. So it has to be given back on BOTH sides. It used to be
5755
+ `margin-inline-start` alone: the name, the icon and the chevron landed on the
5756
+ column, and the trailing state stopped `--aparte-space-3` short of the right edge
5757
+ every line of prose reaches. Measured at a 512px chat: text L26/R26, the state's
5758
+ right edge at R32 — six pixels, on the one line of a turn whose whole job is to
5759
+ read as a quiet aside beside the text. `margin-inline` puts it at R26 and makes
5760
+ the hover surface symmetric with it. */
5761
+ padding: var(--aparte-space-2) var(--aparte-space-3); margin-inline: calc(var(--aparte-space-3) * -1);
5597
5762
  border-radius: var(--aparte-tool-row-radius);
5598
5763
  cursor: pointer; list-style: none;
5599
5764
  }
@@ -6807,7 +6972,7 @@ aparte-sidebar,
6807
6972
  * <aparte-composer>
6808
6973
  * <div class="aparte-composer-shell">
6809
6974
  * <div class="aparte-composer-row">
6810
- * <aparte-composer-input style="flex: 1"></aparte-composer-input>
6975
+ * <aparte-composer-input></aparte-composer-input>
6811
6976
  * <aparte-composer-send></aparte-composer-send>
6812
6977
  * </div>
6813
6978
  * </div>
@@ -7295,6 +7460,26 @@ aparte-split[disabled] > .aparte-split__handle:hover {
7295
7460
  overflow-y: auto;
7296
7461
  }
7297
7462
 
7463
+ /* A fenced block that came through MARKDOWN, not through the `code` segment.
7464
+ `@aparte/plugin-marked` renders ``` as a bare <pre><code>, and the recipe below is
7465
+ scoped to `.aparte-code-content-wrapper` — a class only the code SEGMENT renderer
7466
+ emits, which marked cannot produce. So a markdown block matched no `pre` rule at all
7467
+ and kept the UA's `white-space: pre`: it never wrapped, laid out at its own intrinsic
7468
+ width, and `aparte-chat-bubble`'s `overflow: hidden` amputated the tail. No scrollbar,
7469
+ no ellipsis — the code was simply gone. Measured on the vanilla example: one block's
7470
+ `scrollWidth` was a CONSTANT 963px at chat widths 1500, 800, 600, 512 and 380, against
7471
+ client widths 776 / 724 / 524 / 460 / 328. It overflowed even at 1500.
7472
+
7473
+ Only the two wrapping declarations are shared, not the surface: the padding and the
7474
+ background below are the code CARD's, and a markdown block has no card. Giving it one
7475
+ is a look decision, not this fix. */
7476
+ .aparte-content pre,
7477
+ .aparte-segment-content pre,
7478
+ .aparte-thinking-content pre {
7479
+ white-space: pre-wrap;
7480
+ overflow-wrap: anywhere;
7481
+ }
7482
+
7298
7483
  .aparte-code-content-wrapper pre {
7299
7484
  margin: 0;
7300
7485
  padding: var(--aparte-space-6);
package/dist/index.js CHANGED
@@ -2328,6 +2328,12 @@ class AparteChatViewport extends HTMLElement {
2328
2328
  _maxRenderedBubbles = 1e3;
2329
2329
  _resizeObserver = null;
2330
2330
  _mutationObserver = null;
2331
+ /** The `[overlay-composer]` shell this viewport floats in, if any. Read at observer setup, like `framework-managed`. */
2332
+ _overlayRoot = null;
2333
+ /** Watches the overlay shell's childList, so a stack child that mounts later (elicitation) gets observed too. */
2334
+ _overlayObserver = null;
2335
+ /** Last `--aparte-bottom-inset` written, in px — style writes only when it changes. */
2336
+ _overlayInset = -1;
2331
2337
  _boundResetHandler = null;
2332
2338
  /**
2333
2339
  * When true, _reRenderActivePath() only dispatches aparte-path-changed without
@@ -3244,6 +3250,7 @@ class AparteChatViewport extends HTMLElement {
3244
3250
  }
3245
3251
  _setupObservers() {
3246
3252
  this._resizeObserver = new ResizeObserver(() => {
3253
+ this._updateOverlayInset();
3247
3254
  if (this._isAutoScrollEnabled) {
3248
3255
  this._scrollToBottom();
3249
3256
  }
@@ -3258,6 +3265,16 @@ class AparteChatViewport extends HTMLElement {
3258
3265
  this._resizeObserver.observe(this._container);
3259
3266
  }
3260
3267
  }
3268
+ this._overlayRoot = this.closest("[overlay-composer]");
3269
+ if (this._overlayRoot && this._resizeObserver) {
3270
+ for (const child of this._overlayStack()) this._resizeObserver.observe(child);
3271
+ this._overlayObserver = new MutationObserver(() => {
3272
+ for (const child of this._overlayStack()) this._resizeObserver?.observe(child);
3273
+ this._updateOverlayInset();
3274
+ });
3275
+ this._overlayObserver.observe(this._overlayRoot, { childList: true });
3276
+ this._updateOverlayInset();
3277
+ }
3261
3278
  this._mutationObserver = new MutationObserver(() => {
3262
3279
  if (this._frameworkManagedDOM) this._keepScrollButtonLast();
3263
3280
  if (this._isAutoScrollEnabled) {
@@ -3275,6 +3292,37 @@ class AparteChatViewport extends HTMLElement {
3275
3292
  });
3276
3293
  }
3277
3294
  }
3295
+ /** The overlay shell's element children that are not this viewport — the floating bottom stack. */
3296
+ _overlayStack() {
3297
+ if (!this._overlayRoot) return [];
3298
+ return Array.from(this._overlayRoot.children).filter(
3299
+ (el2) => el2 instanceof HTMLElement && el2 !== this
3300
+ );
3301
+ }
3302
+ /**
3303
+ * Measure how much of this viewport the overlay stack covers, and publish it as
3304
+ * `--aparte-bottom-inset` — the `::after` spacer (framework mode), the
3305
+ * container's own padding (core mode) and the scroll button's `bottom` all read
3306
+ * it (shell.css). Measured from the stack's highest visible top edge rather
3307
+ * than by summing heights, so margins and anything the host renders between
3308
+ * the rows count themselves. No-op outside overlay mode, and when the value
3309
+ * has not changed — a var write invalidates layout, and this runs from the
3310
+ * ResizeObserver its own write can re-trigger (core mode observes the
3311
+ * container's content box, which the padding is part of).
3312
+ */
3313
+ _updateOverlayInset() {
3314
+ if (!this._overlayRoot) return;
3315
+ const bottom = this.getBoundingClientRect().bottom;
3316
+ let top = Infinity;
3317
+ for (const el2 of this._overlayStack()) {
3318
+ const rect = el2.getBoundingClientRect();
3319
+ if (rect.height > 0) top = Math.min(top, rect.top);
3320
+ }
3321
+ const inset = top === Infinity ? 0 : Math.max(0, Math.round(bottom - top));
3322
+ if (inset === this._overlayInset) return;
3323
+ this._overlayInset = inset;
3324
+ this.style.setProperty("--aparte-bottom-inset", `${inset}px`);
3325
+ }
3278
3326
  /** Is the scroll surface within `_scrollThreshold` of its bottom, right now? */
3279
3327
  _isAtBottom() {
3280
3328
  if (!this._container) return true;
@@ -3290,9 +3338,10 @@ class AparteChatViewport extends HTMLElement {
3290
3338
  const now = performance.now();
3291
3339
  const settlingOurs = drop <= churn + 2 && now - this._ownScrollAt < this._ownScrollWindowMs && now - this._readerInputAt >= this._ownScrollWindowMs;
3292
3340
  const readerWentUp = drop > 1 && !settlingOurs;
3341
+ const readerHandOnIt = drop > 1 && now - this._readerInputAt < this._ownScrollWindowMs;
3293
3342
  this._lastScrollTop = top;
3294
3343
  this._lastScrollHeight = height;
3295
- if (this._isAtBottom()) {
3344
+ if (this._isAtBottom() && !readerHandOnIt) {
3296
3345
  this._isAutoScrollEnabled = true;
3297
3346
  } else if (readerWentUp) {
3298
3347
  this._isAutoScrollEnabled = false;
@@ -3488,6 +3537,10 @@ class AparteChatViewport extends HTMLElement {
3488
3537
  this.removeEventListener("aparte-branch-navigate", this._onBranchNavigate);
3489
3538
  this._resizeObserver?.disconnect();
3490
3539
  this._mutationObserver?.disconnect();
3540
+ this._overlayObserver?.disconnect();
3541
+ this._overlayObserver = null;
3542
+ this._overlayRoot = null;
3543
+ this._overlayInset = -1;
3491
3544
  this._resizeObserver = null;
3492
3545
  this._mutationObserver = null;
3493
3546
  if (this._spacerRafId !== null) {
@@ -4000,6 +4053,12 @@ class AparteComposerInput extends HTMLElement {
4000
4053
  }
4001
4054
  connectedCallback() {
4002
4055
  this._render();
4056
+ this._editor = this.querySelector(".aparte-ci-editor");
4057
+ this._editor?.addEventListener("input", this._onInput);
4058
+ this._editor?.addEventListener("keydown", this._onKeydown);
4059
+ this._editor?.addEventListener("focus", this._onFocus);
4060
+ this._editor?.addEventListener("blur", this._onBlur);
4061
+ this._editor?.addEventListener("paste", this._onPaste);
4003
4062
  this._connectToRoot();
4004
4063
  this._scheduleInitialReflow();
4005
4064
  this._unsubscribes.push(subscribeConfigChange(this, () => this._updatePlaceholder()));
@@ -4103,11 +4162,6 @@ class AparteComposerInput extends HTMLElement {
4103
4162
  data-placeholder="${placeholder}"
4104
4163
  ></div>`;
4105
4164
  this._editor = this.querySelector(".aparte-ci-editor");
4106
- this._editor?.addEventListener("input", this._onInput);
4107
- this._editor?.addEventListener("keydown", this._onKeydown);
4108
- this._editor?.addEventListener("focus", this._onFocus);
4109
- this._editor?.addEventListener("blur", this._onBlur);
4110
- this._editor?.addEventListener("paste", this._onPaste);
4111
4165
  this._adjustHeight();
4112
4166
  }
4113
4167
  _connectToRoot() {
@@ -4236,6 +4290,8 @@ class AparteComposerSend extends HTMLElement {
4236
4290
  _onClick = this._handleClick.bind(this);
4237
4291
  connectedCallback() {
4238
4292
  this._render();
4293
+ this._button = this.querySelector(".aparte-cs-button");
4294
+ this._button?.addEventListener("click", this._onClick);
4239
4295
  this._connectToRoot();
4240
4296
  }
4241
4297
  disconnectedCallback() {
@@ -4259,8 +4315,6 @@ class AparteComposerSend extends HTMLElement {
4259
4315
  title="${escapeAttr(label)}"
4260
4316
  ${disabled ? "disabled" : ""}
4261
4317
  >${icon}</button>`;
4262
- this._button = this.querySelector(".aparte-cs-button");
4263
- this._button?.addEventListener("click", this._onClick);
4264
4318
  }
4265
4319
  _connectToRoot() {
4266
4320
  const root = this._getRoot();
@@ -4388,6 +4442,8 @@ class AparteComposerCancel extends HTMLElement {
4388
4442
  _onClick = this._handleClick.bind(this);
4389
4443
  connectedCallback() {
4390
4444
  this._render();
4445
+ this._button = this.querySelector(".aparte-cc-button");
4446
+ this._button?.addEventListener("click", this._onClick);
4391
4447
  this._connectToRoot();
4392
4448
  this._unsubscribes.push(subscribeConfigChange(this, () => this._refreshChrome()));
4393
4449
  }
@@ -4410,8 +4466,6 @@ class AparteComposerCancel extends HTMLElement {
4410
4466
  title="${escapeAttr(label)}"
4411
4467
  hidden
4412
4468
  >${icon}</button>`;
4413
- this._button = this.querySelector(".aparte-cc-button");
4414
- this._button?.addEventListener("click", this._onClick);
4415
4469
  }
4416
4470
  _connectToRoot() {
4417
4471
  const root = this._getRoot();
@@ -4542,6 +4596,8 @@ class AparteComposerAddAttachment extends HTMLElement {
4542
4596
  }
4543
4597
  connectedCallback() {
4544
4598
  this._render();
4599
+ this._button = this.querySelector(".aparte-caa-button");
4600
+ this._button?.addEventListener("click", this._onClick);
4545
4601
  this._connectToRoot();
4546
4602
  this._setupDragDrop();
4547
4603
  this._unsubscribes.push(subscribeConfigChange(this, () => this._refreshChrome()));
@@ -4590,8 +4646,6 @@ class AparteComposerAddAttachment extends HTMLElement {
4590
4646
  type="button"
4591
4647
  ${disabled ? "disabled" : ""}
4592
4648
  >${icon}</button>`;
4593
- this._button = this.querySelector(".aparte-caa-button");
4594
- this._button?.addEventListener("click", this._onClick);
4595
4649
  }
4596
4650
  _connectToRoot() {
4597
4651
  const root = this._getRoot();
@@ -4667,6 +4721,8 @@ class AparteComposerAction extends HTMLElement {
4667
4721
  }
4668
4722
  connectedCallback() {
4669
4723
  this._render();
4724
+ this._button = this.querySelector(".aparte-cact-button");
4725
+ this._button?.addEventListener("click", this._onClick);
4670
4726
  this._connectToRoot();
4671
4727
  this._unsubscribes.push(subscribeConfigChange(this, () => {
4672
4728
  if (this._button) this._button.innerHTML = this._resolveIcon(this.getAttribute("icon") ?? "");
@@ -4706,8 +4762,6 @@ class AparteComposerAction extends HTMLElement {
4706
4762
  type="button"
4707
4763
  ${disabled ? "disabled" : ""}
4708
4764
  >${icon}</button>`;
4709
- this._button = this.querySelector(".aparte-cact-button");
4710
- this._button?.addEventListener("click", this._onClick);
4711
4765
  }
4712
4766
  _connectToRoot() {
4713
4767
  const root = this._getRoot();