@aparte/core 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"aparte-chat-host.d.ts","sourceRoot":"","sources":["../../src/host/aparte-chat-host.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"aparte-chat-host.d.ts","sourceRoot":"","sources":["../../src/host/aparte-chat-host.ts"],"names":[],"mappings":"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"}
@@ -237,11 +237,11 @@ class AparteStreamParser {
237
237
  if (seg.type === "text") content = stripTrailingFence(content);
238
238
  seg.content = content;
239
239
  }
240
- finalSegments.push(this._state.activeSegment);
240
+ finalSegments.push(this._closed(this._state.activeSegment));
241
241
  this._state.activeSegment = null;
242
242
  this._state.buffer = "";
243
243
  } else if (this._state.buffer.trim()) {
244
- finalSegments.push(this._createTextSegment(stripTrailingFence(this._state.buffer)));
244
+ finalSegments.push(this._closed(this._createTextSegment(stripTrailingFence(this._state.buffer))));
245
245
  this._state.buffer = "";
246
246
  }
247
247
  return finalSegments;
@@ -311,7 +311,7 @@ class AparteStreamParser {
311
311
  }
312
312
  let segmentToEmit = null;
313
313
  if (this._state.activeSegment && this._state.activeSegment.type === "text") {
314
- segmentToEmit = this._state.activeSegment;
314
+ segmentToEmit = this._closed(this._state.activeSegment);
315
315
  this._state.activeSegment = null;
316
316
  }
317
317
  if (firstPattern.type === "code") {
@@ -360,7 +360,7 @@ class AparteStreamParser {
360
360
  if (this._state.activeSegment && "content" in this._state.activeSegment) {
361
361
  this._state.activeSegment.content += codeContent2;
362
362
  }
363
- const segment2 = this._state.activeSegment;
363
+ const segment2 = this._closed(this._state.activeSegment);
364
364
  this._state.mode = "text";
365
365
  this._state.activeSegment = null;
366
366
  return { segment: segment2, remaining: "" };
@@ -378,7 +378,7 @@ class AparteStreamParser {
378
378
  if (this._state.activeSegment && "content" in this._state.activeSegment) {
379
379
  this._state.activeSegment.content += codeContent;
380
380
  }
381
- const segment = this._state.activeSegment;
381
+ const segment = this._closed(this._state.activeSegment);
382
382
  this._state.mode = "text";
383
383
  this._state.activeSegment = null;
384
384
  const afterClose = buffer.slice(closeIndex + 4);
@@ -420,7 +420,7 @@ class AparteStreamParser {
420
420
  if (this._state.activeSegment && "content" in this._state.activeSegment) {
421
421
  this._state.activeSegment.content += thinkingContent;
422
422
  }
423
- const segment = this._state.activeSegment;
423
+ const segment = this._closed(this._state.activeSegment);
424
424
  this._state.mode = "text";
425
425
  this._state.activeSegment = null;
426
426
  this._state.thinkingEnd = void 0;
@@ -482,7 +482,7 @@ class AparteStreamParser {
482
482
  if (this._state.activeSegment && "content" in this._state.activeSegment) {
483
483
  this._state.activeSegment.content += tail;
484
484
  }
485
- const segment = this._state.activeSegment;
485
+ const segment = this._closed(this._state.activeSegment);
486
486
  this._state.mode = "text";
487
487
  this._state.activeSegment = null;
488
488
  return { segment, remaining: buffer.slice(closeIndex + endDelim.length) };
@@ -503,6 +503,34 @@ class AparteStreamParser {
503
503
  ++this._state.segmentCounter;
504
504
  return `${this._options.idPrefix}-${uuid()}`;
505
505
  }
506
+ /**
507
+ * Mark a segment the parser has just CLOSED.
508
+ *
509
+ * The end of a delimited segment is not a guess: the closing token IS the end.
510
+ * `</think>`, a closing fence, `</artifact>` — and, for a text run, the opening
511
+ * of whatever comes next. The parser computes that moment at six sites and used
512
+ * to drop it, emitting the finished segment with no flag saying so.
513
+ *
514
+ * What that cost: nothing downstream could tell a finished reasoning block from
515
+ * one still streaming, so the only remaining signal was the END OF THE TURN. A
516
+ * reader watched "Thinking" for as long as the answer took to stream — twenty
517
+ * seconds after the model had stopped thinking — and the Markdown flush and the
518
+ * highlight-on-settle waited exactly as long. Two workarounds were written
519
+ * before this one: close at the end of the message (wrong value), and close when
520
+ * the next segment opens (right value, wrong moment, and no help for a reply
521
+ * that is reasoning and nothing else).
522
+ *
523
+ * Both agent loops share this parser, so saying it here says it once.
524
+ *
525
+ * Not every end is in band, and the exceptions are honest: a text run that ends
526
+ * only because the STREAM ended has no delimiter — `finalize()` is its truth —
527
+ * and a `tool_call` never passes through here at all, since its end is its
528
+ * `status`.
529
+ */
530
+ _closed(segment) {
531
+ if (segment) segment.isStreaming = false;
532
+ return segment;
533
+ }
506
534
  _createTextSegment(content) {
507
535
  return {
508
536
  id: this._options.autoGenerateIds ? this._generateId() : "",
@@ -2074,7 +2102,8 @@ const codeRenderer = {
2074
2102
  const copyBtn = element.querySelector(".code-copy");
2075
2103
  if (copyBtn) {
2076
2104
  copyBtn.addEventListener("click", () => {
2077
- void navigator.clipboard.writeText(segment.content || "").catch(() => {
2105
+ const source = element.querySelector(".code-content-wrapper")?.textContent ?? "";
2106
+ void navigator.clipboard.writeText(source).catch(() => {
2078
2107
  });
2079
2108
  copyBtn.innerHTML = contextConfig(copyBtn).getIcon("check");
2080
2109
  copyBtn.setAttribute("title", contextConfig(copyBtn).t("copied"));
@@ -2126,7 +2155,7 @@ const terminalRenderer = {
2126
2155
  ${segment.exitCode !== void 0 && segment.exitCode !== 0 ? `<div class="terminal-error">Command failed with exit code ${segment.exitCode}</div>` : ""}
2127
2156
  </div>
2128
2157
  `,
2129
- setup: (element) => {
2158
+ setup: (element, segment) => {
2130
2159
  const copyBtn = element.querySelector(".terminal-copy-btn");
2131
2160
  const command = element.querySelector(".terminal-command");
2132
2161
  if (copyBtn && command) {
@@ -2144,12 +2173,12 @@ const terminalRenderer = {
2144
2173
  const runBtn = element.querySelector(".terminal-run-btn");
2145
2174
  if (runBtn) {
2146
2175
  runBtn.addEventListener("click", () => {
2147
- const segmentId = element.getAttribute("data-segment-id");
2148
2176
  element.dispatchEvent(new CustomEvent("aparte-terminal-run", {
2149
2177
  bubbles: true,
2150
2178
  composed: true,
2151
2179
  detail: {
2152
- segmentId,
2180
+ segmentId: segment.id,
2181
+ messageId: segment.messageId,
2153
2182
  command: command?.textContent || ""
2154
2183
  }
2155
2184
  }));
@@ -3344,8 +3373,12 @@ function injectRendererStyles(config = contextConfig()) {
3344
3373
  if (added) styleElement.textContent = Array.from(injectedRendererStyles).join("\n");
3345
3374
  }
3346
3375
  function registerDefaultRenderers(config = contextConfig()) {
3347
- registryFor(config).defaultsInstalled = true;
3348
- for (const renderer of DEFAULT_RENDERERS) registerSegmentRenderer(renderer, config);
3376
+ const reg = registryFor(config);
3377
+ reg.defaultsInstalled = true;
3378
+ for (const renderer of DEFAULT_RENDERERS) {
3379
+ if (!reg.renderers.has(renderer.type)) registerSegmentRenderer(renderer, config);
3380
+ }
3381
+ injectRendererStyles(config);
3349
3382
  installArtifactReadyHook();
3350
3383
  }
3351
3384
  const DEFAULT_RENDERERS = [
@@ -3360,11 +3393,64 @@ const DEFAULT_RENDERERS = [
3360
3393
  artifactRenderer,
3361
3394
  pipelineWaitingRenderer
3362
3395
  ];
3396
+ const OPEN_TOOL_STATUSES = /* @__PURE__ */ new Set(["pending", "awaiting-approval"]);
3397
+ const ACTIVITY_KEYS = ["content", "output", "result"];
3398
+ function isActivity(updates) {
3399
+ return ACTIVITY_KEYS.some((key) => key in updates);
3400
+ }
3401
+ function stampSegmentOnInsert(segments, segment, messageId) {
3402
+ return {
3403
+ ...segment,
3404
+ messageId: segment.messageId ?? messageId,
3405
+ index: segment.index ?? segments.length,
3406
+ startedAt: segment.startedAt ?? Date.now()
3407
+ };
3408
+ }
3409
+ function isSegmentSettled(segment) {
3410
+ if (segment.type === "tool_call") return !OPEN_TOOL_STATUSES.has(segment.status);
3411
+ return segment.isStreaming === false;
3412
+ }
3413
+ function stampSegmentOnUpdate(segment, updates) {
3414
+ if (isSegmentSettled(segment)) return updates;
3415
+ const merged = { ...segment, ...updates };
3416
+ if (isSegmentSettled(merged)) {
3417
+ return { ...updates, endedAt: segment.endedAt ?? Date.now() };
3418
+ }
3419
+ return isActivity(updates) ? { ...updates, endedAt: Date.now() } : updates;
3420
+ }
3421
+ function segmentDuration(segment) {
3422
+ const { startedAt, endedAt } = segment;
3423
+ if (startedAt == null || endedAt == null) return void 0;
3424
+ return endedAt - startedAt;
3425
+ }
3426
+ function stampSegmentActivity(segment) {
3427
+ return isSegmentSettled(segment) ? {} : { endedAt: Date.now() };
3428
+ }
3429
+ function segmentContentUpdate(segment) {
3430
+ const content = segment.content;
3431
+ return {
3432
+ content,
3433
+ ...segment.isStreaming === false ? { isStreaming: false } : {}
3434
+ };
3435
+ }
3436
+ function isTerminalStatus(status) {
3437
+ return status != null && status !== "streaming" && status !== "pending";
3438
+ }
3439
+ function openSegmentIds(segments) {
3440
+ return segments.filter((segment) => !isSegmentSettled(segment)).map((segment) => segment.id);
3441
+ }
3442
+ function renumberSegments(segments) {
3443
+ for (let i = 0; i < segments.length; i++) {
3444
+ const segment = segments[i];
3445
+ if (segment.index === i) continue;
3446
+ segments[i] = { ...segment, index: i };
3447
+ }
3448
+ }
3363
3449
  function populateBubbleFromMessage(bubble, message, siblingInfo) {
3364
3450
  if (message.segments && message.segments.length > 0) {
3365
3451
  const existing = bubble.getSegments?.() ?? [];
3366
3452
  if (existing.length === message.segments.length) {
3367
- const settled = message.status != null && message.status !== "streaming" && message.status !== "pending";
3453
+ const settled = isTerminalStatus(message.status);
3368
3454
  for (const seg of message.segments) {
3369
3455
  if ("content" in seg) {
3370
3456
  const patch = { content: seg.content };
@@ -4120,12 +4206,18 @@ class AparteChatHost {
4120
4206
  const index = msgs.findIndex((m) => m.id === messageId);
4121
4207
  if (index === -1) return;
4122
4208
  const next = [...msgs];
4123
- next[index] = { ...next[index], ...updates };
4209
+ const merged = { ...next[index], ...updates };
4210
+ next[index] = merged;
4124
4211
  this.binding.setMessages(next);
4125
4212
  this.binding.onMessagesChange?.(next);
4126
4213
  if (updates.status) {
4127
4214
  this._bubbleById(messageId)?.updateMessage?.({ status: updates.status });
4128
4215
  }
4216
+ if (isTerminalStatus(merged.status) && merged.segments) {
4217
+ for (const id of openSegmentIds(merged.segments)) {
4218
+ this.updateSegment(id, { isStreaming: false });
4219
+ }
4220
+ }
4129
4221
  }
4130
4222
  /** Append/replace the last message content (streaming text). */
4131
4223
  updateLastMessage(content, options) {
@@ -4142,13 +4234,16 @@ class AparteChatHost {
4142
4234
  /** Add a segment to the last message (immediate bubble + state sync). */
4143
4235
  addSegment(segment) {
4144
4236
  this._flushStreamState();
4145
- const msgs = this.binding.getMessages();
4146
- const last = msgs[msgs.length - 1];
4147
- if (last && this._isOrphan(last.id)) return;
4237
+ const before = this.binding.getMessages();
4238
+ const target = before[before.length - 1];
4239
+ if (target && this._isOrphan(target.id)) return;
4148
4240
  this.binding.onTypingChange?.(false);
4149
- this._lastBubble()?.addSegment?.(segment);
4241
+ const msgs = before;
4242
+ const last = target;
4243
+ const stamped = last ? stampSegmentOnInsert(last.segments || [], segment, last.id) : segment;
4244
+ this._lastBubble()?.addSegment?.(stamped);
4150
4245
  if (!last) return;
4151
- const segments = [...last.segments || [], segment];
4246
+ const segments = [...last.segments || [], stamped];
4152
4247
  const next = [...msgs.slice(0, -1), { ...last, segments }];
4153
4248
  this.binding.setMessages(next);
4154
4249
  this.binding.onMessagesChange?.(next);
@@ -4159,10 +4254,12 @@ class AparteChatHost {
4159
4254
  const msgs = this.binding.getMessages();
4160
4255
  const last = msgs[msgs.length - 1];
4161
4256
  if (last && this._isOrphan(last.id)) return;
4162
- this._lastBubble()?.updateSegment?.(segmentId, updates);
4257
+ const current = last?.segments?.find((s) => s.id === segmentId);
4258
+ const stamped = current ? stampSegmentOnUpdate(current, updates) : updates;
4259
+ this._lastBubble()?.updateSegment?.(segmentId, stamped);
4163
4260
  if (!last || !last.segments) return;
4164
4261
  const segments = last.segments.map(
4165
- (s) => s.id === segmentId ? { ...s, ...updates } : s
4262
+ (s) => s.id === segmentId ? { ...s, ...stamped } : s
4166
4263
  );
4167
4264
  const next = [...msgs.slice(0, -1), { ...last, segments }];
4168
4265
  this.binding.setMessages(next);
@@ -4177,6 +4274,7 @@ class AparteChatHost {
4177
4274
  if (!last?.segments) return;
4178
4275
  const segments = last.segments.filter((s) => s.id !== segmentId);
4179
4276
  if (segments.length === last.segments.length) return;
4277
+ renumberSegments(segments);
4180
4278
  const next = [...msgs.slice(0, -1), { ...last, segments }];
4181
4279
  this.binding.setMessages(next);
4182
4280
  this.binding.onMessagesChange?.(next);
@@ -4258,7 +4356,12 @@ class AparteChatHost {
4258
4356
  if (last?.segments) {
4259
4357
  const segments = last.segments.map((s) => {
4260
4358
  const buffered = pending.get(s.id);
4261
- return buffered !== void 0 && "content" in s ? { ...s, content: buffered.base + buffered.text } : s;
4359
+ if (buffered === void 0 || !("content" in s)) return s;
4360
+ return {
4361
+ ...s,
4362
+ content: buffered.base + buffered.text,
4363
+ ...stampSegmentActivity(s)
4364
+ };
4262
4365
  });
4263
4366
  msgs = [...msgs.slice(0, -1), { ...last, segments }];
4264
4367
  changed = true;
@@ -4587,7 +4690,7 @@ function feedXmlArtifactDelta(delta, xml, ctx) {
4587
4690
  targetElement.addSegment?.(seg);
4588
4691
  streamingSegmentIds.add(seg.id);
4589
4692
  } else if ("content" in seg) {
4590
- targetElement.updateSegment?.(seg.id, { content: seg.content });
4693
+ targetElement.updateSegment?.(seg.id, segmentContentUpdate(seg));
4591
4694
  }
4592
4695
  }
4593
4696
  const active = textParser.getState().activeSegment;
@@ -4596,7 +4699,7 @@ function feedXmlArtifactDelta(delta, xml, ctx) {
4596
4699
  targetElement.addSegment?.(active);
4597
4700
  streamingSegmentIds.add(active.id);
4598
4701
  } else {
4599
- targetElement.updateSegment?.(active.id, { content: active.content });
4702
+ targetElement.updateSegment?.(active.id, segmentContentUpdate(active));
4600
4703
  }
4601
4704
  } else if (!r.segments.length && !active) ;
4602
4705
  };
@@ -4707,7 +4810,7 @@ function finalizeXmlArtifact(xml, ctx) {
4707
4810
  targetElement.addSegment?.(seg);
4708
4811
  ctx.streamingSegmentIds.add(seg.id);
4709
4812
  } else if ("content" in seg) {
4710
- targetElement.updateSegment?.(seg.id, { content: seg.content });
4813
+ targetElement.updateSegment?.(seg.id, segmentContentUpdate(seg));
4711
4814
  }
4712
4815
  }
4713
4816
  const active = ctx.textParser.getState().activeSegment;
@@ -4716,7 +4819,7 @@ function finalizeXmlArtifact(xml, ctx) {
4716
4819
  targetElement.addSegment?.(active);
4717
4820
  ctx.streamingSegmentIds.add(active.id);
4718
4821
  } else {
4719
- targetElement.updateSegment?.(active.id, { content: active.content });
4822
+ targetElement.updateSegment?.(active.id, segmentContentUpdate(active));
4720
4823
  }
4721
4824
  }
4722
4825
  }
@@ -4782,7 +4885,7 @@ function createStreamAdapter(opts) {
4782
4885
  }
4783
4886
  case "text-delta": {
4784
4887
  if (thinkingId && !thinkingCollapsed) {
4785
- target.updateSegment?.(thinkingId, { collapsed: true });
4888
+ target.updateSegment?.(thinkingId, { collapsed: true, isStreaming: false });
4786
4889
  thinkingCollapsed = true;
4787
4890
  }
4788
4891
  if (e.reduced) {
@@ -4792,7 +4895,7 @@ function createStreamAdapter(opts) {
4792
4895
  target.addSegment?.(segment);
4793
4896
  streaming.add(segment.id);
4794
4897
  } else if ("content" in segment) {
4795
- target.updateSegment?.(segment.id, { content: segment.content });
4898
+ target.updateSegment?.(segment.id, segmentContentUpdate(segment));
4796
4899
  }
4797
4900
  }
4798
4901
  break;
@@ -4816,7 +4919,7 @@ function createStreamAdapter(opts) {
4816
4919
  target.addSegment?.(segment);
4817
4920
  streaming.add(segment.id);
4818
4921
  } else if ("content" in segment) {
4819
- target.updateSegment?.(segment.id, { content: segment.content });
4922
+ target.updateSegment?.(segment.id, segmentContentUpdate(segment));
4820
4923
  }
4821
4924
  if (segment.type === "artifact") {
4822
4925
  dispatchArtifactLifecycle(target, messageId, segment, artifactProgress, true);
@@ -4831,7 +4934,7 @@ function createStreamAdapter(opts) {
4831
4934
  dispatchArtifactLifecycle(target, messageId, active, artifactProgress, false);
4832
4935
  }
4833
4936
  } else {
4834
- target.updateSegment?.(active.id, { content: active.content });
4937
+ target.updateSegment?.(active.id, segmentContentUpdate(active));
4835
4938
  if (active.type === "artifact") {
4836
4939
  dispatchArtifactLifecycle(target, messageId, active, artifactProgress, false);
4837
4940
  }
@@ -4862,7 +4965,7 @@ function createStreamAdapter(opts) {
4862
4965
  }
4863
4966
  for (const s of finals) {
4864
4967
  if (!streaming.has(s.id)) target.addSegment?.(s);
4865
- else if ("content" in s) target.updateSegment?.(s.id, { content: s.content });
4968
+ else if ("content" in s) target.updateSegment?.(s.id, segmentContentUpdate(s));
4866
4969
  if (s.type === "artifact") {
4867
4970
  dispatchArtifactLifecycle(target, messageId, s, artifactProgress, true);
4868
4971
  }
@@ -6203,7 +6306,7 @@ ${content}
6203
6306
  }
6204
6307
  case "text": {
6205
6308
  if (thinkingSegmentId && !thinkingCollapsed) {
6206
- targetElement.updateSegment?.(thinkingSegmentId, { collapsed: true });
6309
+ targetElement.updateSegment?.(thinkingSegmentId, { collapsed: true, isStreaming: false });
6207
6310
  thinkingCollapsed = true;
6208
6311
  }
6209
6312
  precedingText += event.delta;
@@ -6250,7 +6353,7 @@ ${content}
6250
6353
  targetElement.addSegment?.(segment);
6251
6354
  streamingSegmentIds.add(segment.id);
6252
6355
  } else if ("content" in segment) {
6253
- targetElement.updateSegment?.(segment.id, { content: segment.content });
6356
+ targetElement.updateSegment?.(segment.id, segmentContentUpdate(segment));
6254
6357
  }
6255
6358
  if (segment.type === "artifact") {
6256
6359
  dispatchArtifactLifecycle(targetElement, messageId, segment, artifactProgress, true);
@@ -6265,7 +6368,7 @@ ${content}
6265
6368
  dispatchArtifactLifecycle(targetElement, messageId, active, artifactProgress, false);
6266
6369
  }
6267
6370
  } else {
6268
- targetElement.updateSegment?.(active.id, { content: active.content });
6371
+ targetElement.updateSegment?.(active.id, segmentContentUpdate(active));
6269
6372
  if (active.type === "artifact") {
6270
6373
  dispatchArtifactLifecycle(targetElement, messageId, active, artifactProgress, false);
6271
6374
  }
@@ -6335,7 +6438,7 @@ ${content}
6335
6438
  if (!streamingSegmentIds.has(s.id)) {
6336
6439
  targetElement.addSegment?.(s);
6337
6440
  } else if ("content" in s) {
6338
- targetElement.updateSegment?.(s.id, { content: s.content });
6441
+ targetElement.updateSegment?.(s.id, segmentContentUpdate(s));
6339
6442
  }
6340
6443
  if (s.type === "artifact") {
6341
6444
  dispatchArtifactLifecycle(targetElement, messageId, s, artifactProgress, true);
@@ -7021,7 +7124,7 @@ function requestUserInput(request) {
7021
7124
  return resolveConfig(request.target ?? null).requestUserInput(request);
7022
7125
  }
7023
7126
  export {
7024
- uuid as $,
7127
+ segmentDuration as $,
7025
7128
  APARTE_CONVERSATION_SCHEMA_VERSION as A,
7026
7129
  createStreamAdapter as B,
7027
7130
  cssEscape as C,
@@ -7038,18 +7141,26 @@ export {
7038
7141
  isAwaitingReply as N,
7039
7142
  isFormatAdapter as O,
7040
7143
  isSafeUrl as P,
7041
- parseAparteEventStream as Q,
7042
- parseMarkdownToSegments as R,
7043
- populateBubbleFromMessage as S,
7044
- readableToAsyncIterable as T,
7045
- registerDefaultRenderers as U,
7046
- registerSegmentRenderer as V,
7047
- requestUserInput as W,
7048
- resolveConfig as X,
7049
- revokeAttachmentUrls as Y,
7050
- runWithConfig as Z,
7051
- unregisterSegmentRenderer as _,
7144
+ isSegmentSettled as Q,
7145
+ parseAparteEventStream as R,
7146
+ parseMarkdownToSegments as S,
7147
+ populateBubbleFromMessage as T,
7148
+ readableToAsyncIterable as U,
7149
+ registerDefaultRenderers as V,
7150
+ registerSegmentRenderer as W,
7151
+ requestUserInput as X,
7152
+ resolveConfig as Y,
7153
+ revokeAttachmentUrls as Z,
7154
+ runWithConfig as _,
7052
7155
  APARTE_DEFAULT_BUBBLE_ACTIONS as a,
7156
+ unregisterSegmentRenderer as a0,
7157
+ uuid as a1,
7158
+ stampSegmentActivity as a2,
7159
+ stampSegmentOnInsert as a3,
7160
+ stampSegmentOnUpdate as a4,
7161
+ renumberSegments as a5,
7162
+ openSegmentIds as a6,
7163
+ isTerminalStatus as a7,
7053
7164
  APARTE_DEFAULT_HOST_HANDLERS as b,
7054
7165
  APARTE_DEFAULT_ICON_FALLBACKS as c,
7055
7166
  APARTE_DEFAULT_LOCALE as d,
@@ -7076,4 +7187,4 @@ export {
7076
7187
  contextConfig as y,
7077
7188
  createAparteChatHandler as z
7078
7189
  };
7079
- //# sourceMappingURL=index-BieFbKeW.js.map
7190
+ //# sourceMappingURL=index-hQQljwfX.js.map