@spotpatch/runtime 1.15.4 → 1.15.6

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.
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  SELECT_PICKER_STYLES,
7
7
  createSelectPicker
8
- } from "./chunk-ELCIEQGT.js";
8
+ } from "./chunk-HVI76KFV.js";
9
9
  import {
10
10
  createButton,
11
11
  createMarkedElement
@@ -346,13 +346,19 @@ function contextualAskApiErrorCode(error) {
346
346
  }
347
347
  function createContextualAskApi(options) {
348
348
  const pending = /* @__PURE__ */ new Set();
349
+ const session = /* @__PURE__ */ new Set();
349
350
  function cancelPending() {
350
351
  for (const controller of pending) controller.abort();
351
352
  pending.clear();
352
353
  }
353
- async function request(endpoint, method, body) {
354
+ function dispose() {
355
+ cancelPending();
356
+ for (const controller of session) controller.abort();
357
+ session.clear();
358
+ }
359
+ async function request(endpoint, method, body, scope = pending) {
354
360
  const controller = new AbortController();
355
- pending.add(controller);
361
+ scope.add(controller);
356
362
  try {
357
363
  const response = await options.fetch(endpoint, {
358
364
  method,
@@ -365,7 +371,7 @@ function createContextualAskApi(options) {
365
371
  });
366
372
  return await readEnvelope(response);
367
373
  } finally {
368
- pending.delete(controller);
374
+ scope.delete(controller);
369
375
  }
370
376
  }
371
377
  async function events(jobId, afterSequence, onEvent) {
@@ -437,7 +443,12 @@ function createContextualAskApi(options) {
437
443
  }
438
444
  return Object.freeze({
439
445
  async capability() {
440
- const data = await request(SPOTPATCH_ENDPOINTS.askCapability, "GET");
446
+ const data = await request(
447
+ SPOTPATCH_ENDPOINTS.askCapability,
448
+ "GET",
449
+ void 0,
450
+ session
451
+ );
441
452
  if (!isContextualAskCapability(data)) throw new ContextualAskApiError();
442
453
  return deepFreeze(data);
443
454
  },
@@ -456,7 +467,7 @@ function createContextualAskApi(options) {
456
467
  }
457
468
  return deepFreeze(data);
458
469
  },
459
- dispose: cancelPending,
470
+ dispose,
460
471
  events,
461
472
  async result(jobId) {
462
473
  const data = await request(getAskJobEndpoint(jobId, "result"), "GET");
@@ -480,6 +491,7 @@ function createContextualAskWorkflow(input) {
480
491
  let currentResult;
481
492
  let currentQuestion = "";
482
493
  let capabilityLoaded = false;
494
+ let capabilityLoading = false;
483
495
  let composing = false;
484
496
  const consentByExecutor = /* @__PURE__ */ new Map();
485
497
  const api = input.api ?? createContextualAskApi({
@@ -527,23 +539,24 @@ function createContextualAskWorkflow(input) {
527
539
  createdAt
528
540
  };
529
541
  }
530
- async function loadCapability() {
531
- if (capabilityLoaded) return;
532
- const requestRevision = revision;
542
+ function loadCapability() {
543
+ if (capabilityLoaded || capabilityLoading) return;
544
+ capabilityLoading = true;
533
545
  input.panel.renderCapability(void 0);
534
- try {
535
- const capability = await api.capability();
536
- if (!isCurrent(requestRevision)) return;
546
+ void api.capability().then((capability) => {
547
+ if (!mounted) return;
537
548
  capabilityLoaded = true;
538
549
  input.panel.renderCapability(capability);
539
550
  const executorId = input.panel.readExecutorId();
540
551
  input.panel.setConsent(
541
552
  executorId !== void 0 && consentByExecutor.get(executorId) === true
542
553
  );
543
- } catch (error) {
544
- if (!isCurrent(requestRevision) || isAbortError(error)) return;
554
+ }).catch((error) => {
555
+ if (!mounted || isAbortError(error)) return;
545
556
  input.panel.renderError(contextualAskApiErrorCode(error));
546
- }
557
+ }).finally(() => {
558
+ capabilityLoading = false;
559
+ });
547
560
  }
548
561
  function handleEvent(event, requestRevision) {
549
562
  if (!isCurrent(requestRevision)) return;
@@ -727,7 +740,7 @@ function createContextualAskWorkflow(input) {
727
740
  input.panel.questionInput.addEventListener("compositionstart", compositionStart);
728
741
  input.panel.questionInput.addEventListener("compositionend", compositionEnd);
729
742
  input.panel.root.addEventListener("click", openSource);
730
- void loadCapability();
743
+ loadCapability();
731
744
  }
732
745
  function dispose() {
733
746
  if (!mounted) return;
@@ -755,7 +768,7 @@ function createContextualAskWorkflow(input) {
755
768
  currentResult = void 0;
756
769
  currentQuestion = "";
757
770
  input.panel.clear();
758
- void loadCapability();
771
+ loadCapability();
759
772
  },
760
773
  cancelPending,
761
774
  dispose,
@@ -913,53 +926,80 @@ function contextualAskMessages(locale) {
913
926
  }
914
927
 
915
928
  // src/ui/contextual-ask-panel.ts
929
+ var MAXIMUM_STAGGERED_ANSWER_ITEMS = 8;
916
930
  function createStyles(document) {
917
931
  const style = document.createElement("style");
918
932
  style.textContent = `
919
933
  ${SELECT_PICKER_STYLES}
920
- .spotpatch-ask-mode { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; margin-bottom: 14px; padding: 4px; border: 1px solid var(--spotpatch-border-subtle); border-radius: 10px; background: #0a0a0e; }
921
- .spotpatch-ask-mode button { min-height: 34px; border: 0; border-radius: 7px; color: var(--spotpatch-text-secondary); background: transparent; cursor: pointer; font-size: 12px; font-weight: 700; }
922
- .spotpatch-ask-mode button[aria-selected="true"] { color: #f7f7ff; background: linear-gradient(135deg, rgb(139 123 255 / 25%), rgb(82 168 255 / 14%)); box-shadow: inset 0 0 0 1px rgb(139 123 255 / 24%); }
934
+ .spotpatch-ask-mode { position: relative; isolation: isolate; display: grid; grid-template-columns: 1fr 1fr; gap: 3px; margin-bottom: 14px; padding: 3px; border: 1px solid var(--spotpatch-border-subtle); border-radius: var(--spotpatch-radius-md); background: var(--spotpatch-bg-input); }
935
+ .spotpatch-ask-mode::before { position: absolute; z-index: -1; top: 3px; bottom: 3px; left: 3px; width: calc(50% - 4.5px); border-radius: var(--spotpatch-radius-sm); background: var(--spotpatch-bg-active); box-shadow: var(--spotpatch-shadow-inset), 0 0 0 1px var(--spotpatch-border); content: ""; transition: transform var(--spotpatch-duration-slow) var(--spotpatch-ease-out); }
936
+ .spotpatch-ask-mode[data-mode="change"]::before { transform: translateX(calc(100% + 3px)); }
937
+ .spotpatch-ask-mode button { min-height: 32px; border: 0; border-radius: var(--spotpatch-radius-sm); color: var(--spotpatch-text-secondary); background: transparent; cursor: pointer; font-size: 12.5px; font-weight: 600; transition: color var(--spotpatch-duration-base) ease; }
938
+ .spotpatch-ask-mode button:hover, .spotpatch-ask-mode button[aria-selected="true"] { color: var(--spotpatch-text); }
923
939
  .spotpatch-ask-panel { display: grid; gap: 14px; }
940
+ .spotpatch-ask-panel[data-phase="answered"] > :is(.spotpatch-ask-field, .spotpatch-ask-safety) { display: none; }
924
941
  .spotpatch-ask-field { display: grid; gap: 7px; min-width: 0; }
925
- .spotpatch-ask-field[hidden] { display: none; }
926
- .spotpatch-ask-field > label, .spotpatch-ask-label { color: #c9cad2; font-size: 11px; font-weight: 680; }
927
- .spotpatch-ask-question { box-sizing: border-box; width: 100%; min-height: 92px; resize: vertical; border: 1px solid var(--spotpatch-border); border-radius: 10px; padding: 11px 12px; outline: none; color: var(--spotpatch-text); background: var(--spotpatch-bg-input); line-height: 1.55; }
928
- .spotpatch-ask-question:focus { border-color: rgb(139 123 255 / 68%); box-shadow: 0 0 0 3px rgb(139 123 255 / 12%); }
942
+ .spotpatch-ask-field > label, .spotpatch-ask-label { color: var(--spotpatch-text-secondary); font-size: 11.5px; font-weight: 600; }
943
+ .spotpatch-ask-question { box-sizing: border-box; width: 100%; min-height: 92px; resize: vertical; border: 1px solid var(--spotpatch-border); border-radius: var(--spotpatch-radius-md); padding: 10px 12px; outline: none; color: var(--spotpatch-text); background: var(--spotpatch-bg-input); font-size: 13px; line-height: 1.55; transition: border-color var(--spotpatch-duration-fast) ease, box-shadow var(--spotpatch-duration-fast) ease; }
944
+ .spotpatch-ask-question::placeholder { color: var(--spotpatch-text-muted); }
945
+ .spotpatch-ask-question:focus { border-color: var(--spotpatch-accent-line); box-shadow: 0 0 0 3px var(--spotpatch-accent-tint); }
929
946
  .spotpatch-ask-suggestions { display: flex; flex-wrap: wrap; gap: 6px; }
930
- .spotpatch-ask-suggestions button { border: 1px solid var(--spotpatch-border-subtle); border-radius: 999px; padding: 5px 9px; color: var(--spotpatch-text-secondary); background: rgb(255 255 255 / 2%); cursor: pointer; font-size: 10.5px; }
931
- .spotpatch-ask-suggestions button:hover { border-color: rgb(139 123 255 / 42%); color: #fff; }
932
- .spotpatch-ask-executor-status { margin: 0; border-left: 2px solid var(--spotpatch-warning); padding-left: 8px; color: #d8b66c; font-size: 10.5px; line-height: 1.45; }
933
- .spotpatch-ask-safety { display: grid; gap: 8px; border: 1px solid rgb(82 168 255 / 16%); border-radius: 10px; padding: 10px 11px; background: rgb(82 168 255 / 4%); }
934
- .spotpatch-ask-data { display: flex; justify-content: space-between; gap: 10px; color: #aab3c2; font-size: 10.5px; }
935
- .spotpatch-ask-data strong { color: #9fe3c4; font-weight: 650; white-space: nowrap; }
936
- .spotpatch-ask-consent { display: grid; grid-template-columns: 16px 1fr; gap: 8px; align-items: start; color: var(--spotpatch-text-secondary); cursor: pointer; font-size: 10.5px; line-height: 1.45; }
947
+ .spotpatch-ask-suggestions button { border: 1px dashed var(--spotpatch-accent-line); border-radius: var(--spotpatch-radius-pill); padding: 4px 10px; color: var(--spotpatch-accent-soft); background: transparent; cursor: pointer; font-size: 11.5px; transition: background var(--spotpatch-duration-fast) ease; }
948
+ .spotpatch-ask-suggestions button:hover { background: var(--spotpatch-accent-tint); }
949
+ .spotpatch-ask-executor-status { margin: 0; border-left: 2px solid var(--spotpatch-warning); padding-left: 8px; color: var(--spotpatch-warning-text); font-size: 11.5px; line-height: 1.45; }
950
+ .spotpatch-ask-safety { display: grid; gap: 8px; border: 1px solid var(--spotpatch-cyan-line); border-radius: var(--spotpatch-radius-md); padding: 10px 12px; background: var(--spotpatch-cyan-tint); }
951
+ .spotpatch-ask-data { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 10px; color: var(--spotpatch-text-secondary); font-size: 11.5px; }
952
+ .spotpatch-ask-data strong { color: var(--spotpatch-success-text); font-weight: 600; white-space: nowrap; }
953
+ .spotpatch-ask-consent { display: grid; grid-template-columns: 16px 1fr; gap: 8px; align-items: start; color: var(--spotpatch-text-secondary); cursor: pointer; font-size: 11.5px; line-height: 1.45; }
937
954
  .spotpatch-ask-consent input { margin: 2px 0 0; accent-color: var(--spotpatch-accent); }
938
- .spotpatch-ask-actions, .spotpatch-ask-answer-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 7px; }
939
- .spotpatch-ask-actions button, .spotpatch-ask-answer-actions button { min-height: 34px; border: 1px solid var(--spotpatch-border); border-radius: 8px; padding: 0 12px; color: #d6d6de; background: var(--spotpatch-bg-raised); cursor: pointer; font-size: 11px; font-weight: 680; }
940
- .spotpatch-ask-actions .spotpatch-primary, .spotpatch-ask-answer-actions .spotpatch-primary { border-color: transparent; color: var(--spotpatch-text-on-accent); background: linear-gradient(135deg, #a99cff, #65b7ff); }
941
- .spotpatch-ask-actions button:disabled, .spotpatch-ask-answer-actions button:disabled { opacity: .45; cursor: not-allowed; }
942
- .spotpatch-ask-status { display: grid; gap: 7px; border-left: 2px solid var(--spotpatch-accent); padding: 2px 0 2px 10px; color: #c8c9d2; font-size: 11px; }
943
- .spotpatch-ask-activity { display: grid; gap: 4px; color: var(--spotpatch-text-muted); font: 500 10px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace; }
944
- .spotpatch-ask-error, .spotpatch-ask-stale, .spotpatch-ask-warning { border-radius: 8px; padding: 9px 10px; font-size: 10.5px; }
945
- .spotpatch-ask-error { color: #fecaca; background: rgb(251 113 133 / 9%); }
946
- .spotpatch-ask-stale, .spotpatch-ask-warning { color: #f9d68a; background: rgb(245 158 11 / 8%); }
947
- .spotpatch-ask-answer { display: grid; gap: 12px; border: 1px solid var(--spotpatch-border-subtle); border-radius: 11px; padding: 12px; background: var(--spotpatch-bg); box-shadow: 0 12px 30px rgb(0 0 0 / 18%); }
948
- .spotpatch-ask-answer h3, .spotpatch-ask-sources h4 { margin: 0; color: var(--spotpatch-text); font-size: 13px; }
949
- .spotpatch-ask-blocks { display: grid; gap: 10px; color: #d9dae2; font-size: 12px; line-height: 1.65; overflow-wrap: anywhere; }
955
+ .spotpatch-ask-actions { position: sticky; bottom: 0; z-index: 1; display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; padding: 10px 0 2px; background: var(--spotpatch-bg); box-shadow: 0 -14px 14px -6px var(--spotpatch-bg); }
956
+ .spotpatch-ask-actions button { min-height: 36px; border: 1px solid var(--spotpatch-border); border-radius: var(--spotpatch-radius-md); padding: 0 12px; color: var(--spotpatch-text); background: var(--spotpatch-bg-raised); cursor: pointer; font-size: 12.5px; font-weight: 600; transition: border-color var(--spotpatch-duration-fast) ease, background var(--spotpatch-duration-fast) ease; }
957
+ .spotpatch-ask-actions button:hover:not(:disabled) { border-color: var(--spotpatch-border-strong); background: var(--spotpatch-bg-active); }
958
+ .spotpatch-ask-actions .spotpatch-primary { border-color: transparent; color: var(--spotpatch-text-on-accent); background: var(--spotpatch-primary-fill); box-shadow: var(--spotpatch-shadow-accent), var(--spotpatch-shadow-inset); }
959
+ .spotpatch-ask-actions .spotpatch-primary:hover:not(:disabled) { border-color: transparent; background: var(--spotpatch-primary-fill); filter: brightness(1.1); }
960
+ .spotpatch-ask-actions button:disabled { box-shadow: none; cursor: not-allowed; opacity: .4; }
961
+ .spotpatch-ask-status { display: grid; gap: 7px; border-left: 2px solid var(--spotpatch-accent); padding: 2px 0 2px 10px; color: var(--spotpatch-text-secondary); font-size: 12px; animation: spotpatch-enter var(--spotpatch-duration-slow) var(--spotpatch-ease-out) both; }
962
+ .spotpatch-ask-activity { display: grid; gap: 4px; color: var(--spotpatch-text-muted); font: 500 11px/1.45 var(--spotpatch-font-mono); }
963
+ .spotpatch-ask-error, .spotpatch-ask-stale, .spotpatch-ask-warning { border-radius: var(--spotpatch-radius-sm); padding: 9px 10px; font-size: 11.5px; }
964
+ .spotpatch-ask-error { color: var(--spotpatch-danger-text); background: var(--spotpatch-danger-tint); }
965
+ .spotpatch-ask-stale, .spotpatch-ask-warning { color: var(--spotpatch-warning-text); background: var(--spotpatch-warning-tint); }
966
+ .spotpatch-ask-answer { display: grid; gap: 12px; border: 1px solid var(--spotpatch-border-subtle); border-radius: var(--spotpatch-radius-card); padding: 14px; background: var(--spotpatch-bg-raised); box-shadow: var(--spotpatch-shadow-inset); }
967
+ .spotpatch-ask-answer[data-entering="true"] { animation: spotpatch-enter var(--spotpatch-duration-slow) var(--spotpatch-ease-out) both; }
968
+ .spotpatch-ask-answer[data-entering="true"] [data-ask-order] { --spotpatch-ask-order: 0; animation: spotpatch-enter var(--spotpatch-duration-slow) var(--spotpatch-ease-out) both; animation-delay: calc(140ms + var(--spotpatch-ask-order) * 55ms); }
969
+ .spotpatch-ask-recap { display: grid; gap: 3px; border-left: 2px solid var(--spotpatch-accent-line); padding: 1px 0 1px 10px; }
970
+ .spotpatch-ask-recap span { color: var(--spotpatch-text-muted); font-size: 10.5px; font-weight: 600; letter-spacing: .02em; }
971
+ .spotpatch-ask-recap p { margin: 0; color: var(--spotpatch-text-secondary); font-size: 12.5px; line-height: 1.5; overflow-wrap: anywhere; }
972
+ .spotpatch-ask-answer-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; min-width: 0; }
973
+ .spotpatch-ask-answer-meta { overflow: hidden; color: var(--spotpatch-text-muted); font: 500 10.5px/1.3 var(--spotpatch-font-mono); text-overflow: ellipsis; white-space: nowrap; }
974
+ .spotpatch-ask-answer h3, .spotpatch-ask-sources h4 { margin: 0; font-size: 13px; font-weight: 620; }
975
+ .spotpatch-ask-blocks { display: grid; gap: 10px; font-size: 13px; line-height: 1.65; overflow-wrap: anywhere; }
950
976
  .spotpatch-ask-blocks p, .spotpatch-ask-blocks ul { margin: 0; }
951
977
  .spotpatch-ask-blocks ul { padding-left: 18px; }
952
- .spotpatch-ask-blocks pre { max-width: 100%; overflow: auto; margin: 0; border: 1px solid var(--spotpatch-border-subtle); border-radius: 9px; padding: 10px; color: #dbeafe; background: #08090d; font: 500 10.5px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace; }
953
- .spotpatch-ask-citations, .spotpatch-ask-source-list { display: flex; flex-wrap: wrap; gap: 5px; }
954
- .spotpatch-ask-source { max-width: 100%; overflow: hidden; border: 1px solid rgb(82 168 255 / 22%); border-radius: 999px; padding: 4px 8px; color: #b8dcff; background: rgb(82 168 255 / 7%); cursor: pointer; font: 550 9.5px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace; text-overflow: ellipsis; white-space: nowrap; }
978
+ .spotpatch-ask-inline-code { border: 1px solid var(--spotpatch-border-subtle); border-radius: var(--spotpatch-radius-xs); padding: 1px 5px; color: var(--spotpatch-accent-soft); background: var(--spotpatch-bg-input); font: 500 .88em/1.4 var(--spotpatch-font-mono); }
979
+ .spotpatch-ask-blocks pre { max-width: 100%; overflow: auto; margin: 0; border: 1px solid var(--spotpatch-border-subtle); border-radius: var(--spotpatch-radius-sm); padding: 10px; background: var(--spotpatch-bg-input); font: 500 11px/1.55 var(--spotpatch-font-mono); }
980
+ .spotpatch-ask-citations, .spotpatch-ask-source-list { display: flex; flex-wrap: wrap; gap: 6px; }
981
+ .spotpatch-ask-source { max-width: 100%; overflow: hidden; border: 1px solid var(--spotpatch-cyan-line); border-radius: var(--spotpatch-radius-pill); padding: 4px 9px; color: var(--spotpatch-cyan-text); background: var(--spotpatch-cyan-tint); cursor: pointer; font: 500 10.5px/1.3 var(--spotpatch-font-mono); text-overflow: ellipsis; white-space: nowrap; transition: border-color var(--spotpatch-duration-fast) ease; }
982
+ .spotpatch-ask-source:hover { border-color: var(--spotpatch-accent-cyan); }
955
983
  .spotpatch-ask-sources { display: grid; gap: 7px; }
956
- .spotpatch-ask-origin { margin-bottom: 12px; border: 1px solid rgb(139 123 255 / 22%); border-radius: 10px; padding: 10px 11px; color: var(--spotpatch-text-secondary); background: rgb(139 123 255 / 6%); font-size: 10.5px; }
957
- .spotpatch-ask-origin strong { display: block; margin-bottom: 2px; color: #d9d4ff; font-size: 11px; }
958
- @media (max-width: 420px) { .spotpatch-ask-data { display: grid; } .spotpatch-ask-actions, .spotpatch-ask-answer-actions { justify-content: stretch; } .spotpatch-ask-actions button, .spotpatch-ask-answer-actions button { flex: 1 1 auto; } }
959
- @media (prefers-reduced-motion: reduce) { .spotpatch-ask-panel *, .spotpatch-ask-mode * { scroll-behavior: auto !important; transition: none !important; } }
984
+ .spotpatch-ask-origin { margin-bottom: 12px; border: 1px solid var(--spotpatch-accent-line); border-radius: var(--spotpatch-radius-md); padding: 10px 12px; color: var(--spotpatch-text-secondary); background: var(--spotpatch-accent-tint); font-size: 11.5px; }
985
+ .spotpatch-ask-origin strong { display: block; margin-bottom: 2px; color: var(--spotpatch-accent-soft); font-size: 12px; }
986
+ @media (max-width: 420px) { .spotpatch-ask-actions { justify-content: stretch; } .spotpatch-ask-actions button { flex: 1 1 auto; } }
960
987
  `;
961
988
  return style;
962
989
  }
990
+ var INLINE_CODE = /`([^`\n]+)`/gu;
991
+ function appendInlineText(document, root, text) {
992
+ let offset = 0;
993
+ for (const match of text.matchAll(INLINE_CODE)) {
994
+ root.append(text.slice(offset, match.index));
995
+ const code = createMarkedElement(document, "code");
996
+ code.className = "spotpatch-ask-inline-code";
997
+ code.textContent = match[1] ?? "";
998
+ root.append(code);
999
+ offset = match.index + match[0].length;
1000
+ }
1001
+ root.append(text.slice(offset));
1002
+ }
963
1003
  function appendSourceChips(document, root, sourceIds, sources, messages) {
964
1004
  if (sourceIds.length === 0) return;
965
1005
  const chips = createMarkedElement(document, "div");
@@ -994,6 +1034,7 @@ function createContextualAskPanel(input) {
994
1034
  sourceCount: 0
995
1035
  });
996
1036
  let busy = false;
1037
+ let renderedJobId;
997
1038
  const root = createMarkedElement(document, "section");
998
1039
  const modeSwitch = createMarkedElement(document, "div");
999
1040
  modeSwitch.className = "spotpatch-ask-mode";
@@ -1073,16 +1114,34 @@ function createContextualAskPanel(input) {
1073
1114
  actions.className = "spotpatch-ask-actions";
1074
1115
  const newQuestionButton = createButton(document, "");
1075
1116
  const cancelButton = createButton(document, "");
1117
+ const copyButton = createButton(document, "");
1118
+ const convertButton = createButton(document, "", "spotpatch-primary");
1076
1119
  const submitButton = createButton(document, "", "spotpatch-primary");
1077
1120
  cancelButton.hidden = true;
1078
- actions.append(newQuestionButton, cancelButton, submitButton);
1121
+ actions.append(
1122
+ newQuestionButton,
1123
+ cancelButton,
1124
+ copyButton,
1125
+ convertButton,
1126
+ submitButton
1127
+ );
1079
1128
  const answer = createMarkedElement(document, "article");
1080
1129
  answer.className = "spotpatch-ask-answer";
1081
1130
  answer.hidden = true;
1082
1131
  const stale = createMarkedElement(document, "div");
1083
1132
  stale.className = "spotpatch-ask-stale";
1084
1133
  stale.hidden = true;
1134
+ const recap = createMarkedElement(document, "div");
1135
+ recap.className = "spotpatch-ask-recap";
1136
+ const recapLabel = createMarkedElement(document, "span");
1137
+ const recapText = createMarkedElement(document, "p");
1138
+ recap.append(recapLabel, recapText);
1139
+ const answerHead = createMarkedElement(document, "div");
1140
+ answerHead.className = "spotpatch-ask-answer-head";
1085
1141
  const answerTitle = createMarkedElement(document, "h3");
1142
+ const answerMeta = createMarkedElement(document, "span");
1143
+ answerMeta.className = "spotpatch-ask-answer-meta";
1144
+ answerHead.append(answerTitle, answerMeta);
1086
1145
  const warnings = createMarkedElement(document, "div");
1087
1146
  const blocks = createMarkedElement(document, "div");
1088
1147
  blocks.className = "spotpatch-ask-blocks";
@@ -1092,21 +1151,16 @@ function createContextualAskPanel(input) {
1092
1151
  const sourceList = createMarkedElement(document, "div");
1093
1152
  sourceList.className = "spotpatch-ask-source-list";
1094
1153
  sourcesSection.append(sourcesTitle, sourceList);
1095
- const answerActions = createMarkedElement(document, "div");
1096
- answerActions.className = "spotpatch-ask-answer-actions";
1097
- const copyButton = createButton(document, "");
1098
- const convertButton = createButton(document, "", "spotpatch-primary");
1099
- answerActions.append(copyButton, convertButton);
1100
- answer.append(stale, answerTitle, warnings, blocks, sourcesSection, answerActions);
1154
+ answer.append(stale, recap, answerHead, warnings, blocks, sourcesSection);
1101
1155
  askPanel.append(
1156
+ error,
1157
+ answer,
1102
1158
  questionField,
1103
1159
  executorField,
1104
1160
  modelField,
1105
1161
  safety,
1106
1162
  status,
1107
- error,
1108
- actions,
1109
- answer
1163
+ actions
1110
1164
  );
1111
1165
  root.append(modeSwitch, origin, askPanel);
1112
1166
  function hasReadyExecutor() {
@@ -1146,11 +1200,17 @@ function createContextualAskPanel(input) {
1146
1200
  executorPicker.setDisabled(executorUnavailable);
1147
1201
  modelPicker.setDisabled(busy || !hasReadyExecutor());
1148
1202
  consentCheckbox.disabled = busy || !hasReadyExecutor();
1149
- newQuestionButton.hidden = busy || currentResult === void 0;
1203
+ const answered = !busy && currentResult !== void 0;
1204
+ askPanel.dataset.phase = busy ? "running" : answered ? "answered" : "compose";
1205
+ newQuestionButton.hidden = !answered;
1206
+ copyButton.hidden = !answered;
1207
+ convertButton.hidden = !answered;
1208
+ submitButton.hidden = answered;
1150
1209
  cancelButton.hidden = !busy;
1151
1210
  }
1152
1211
  function applyMode() {
1153
1212
  const asking = currentMode === "ask";
1213
+ modeSwitch.dataset.mode = currentMode;
1154
1214
  askTab.setAttribute("aria-selected", String(asking));
1155
1215
  changeTab.setAttribute("aria-selected", String(!asking));
1156
1216
  askPanel.hidden = !asking;
@@ -1184,6 +1244,7 @@ function createContextualAskPanel(input) {
1184
1244
  askTab.textContent = messages.mode.ask;
1185
1245
  changeTab.textContent = messages.mode.change;
1186
1246
  questionLabel.textContent = messages.questionLabel;
1247
+ recapLabel.textContent = messages.questionLabel;
1187
1248
  questionInput.placeholder = messages.questionPlaceholder;
1188
1249
  suggestions.setAttribute("aria-label", messages.suggestionsLabel);
1189
1250
  executorLabel.textContent = messages.executorLabel;
@@ -1334,7 +1395,7 @@ function createContextualAskPanel(input) {
1334
1395
  for (const block of result.blocks) {
1335
1396
  if (block.kind === "paragraph") {
1336
1397
  const paragraph = createMarkedElement(document, "p");
1337
- paragraph.textContent = block.text;
1398
+ appendInlineText(document, paragraph, block.text);
1338
1399
  blocks.append(paragraph);
1339
1400
  appendSourceChips(document, blocks, block.sourceIds, sourceMap, messages);
1340
1401
  } else if (block.kind === "code") {
@@ -1349,7 +1410,7 @@ function createContextualAskPanel(input) {
1349
1410
  const list = createMarkedElement(document, "ul");
1350
1411
  for (const item of block.items) {
1351
1412
  const listItem = createMarkedElement(document, "li");
1352
- listItem.textContent = item.text;
1413
+ appendInlineText(document, listItem, item.text);
1353
1414
  appendSourceChips(document, listItem, item.sourceIds, sourceMap, messages);
1354
1415
  list.append(listItem);
1355
1416
  }
@@ -1367,6 +1428,27 @@ function createContextualAskPanel(input) {
1367
1428
  sourceList.append(button);
1368
1429
  }
1369
1430
  sourcesSection.hidden = result.sources.length === 0;
1431
+ warnings.hidden = result.warnings.length === 0;
1432
+ const question = questionInput.value.trim();
1433
+ recapText.textContent = question;
1434
+ recap.hidden = question.length === 0;
1435
+ answerMeta.textContent = `${result.executor.label} · ${result.executor.modelLabel}`;
1436
+ answer.dataset.entering = String(result.jobId !== renderedJobId);
1437
+ renderedJobId = result.jobId;
1438
+ const sequence = [
1439
+ recap,
1440
+ answerHead,
1441
+ ...warnings.querySelectorAll(":scope > *"),
1442
+ ...blocks.querySelectorAll(":scope > *"),
1443
+ sourcesSection
1444
+ ];
1445
+ for (const [order, element] of sequence.entries()) {
1446
+ element.dataset.askOrder = "";
1447
+ element.style.setProperty(
1448
+ "--spotpatch-ask-order",
1449
+ String(Math.min(order, MAXIMUM_STAGGERED_ANSWER_ITEMS))
1450
+ );
1451
+ }
1370
1452
  answer.hidden = false;
1371
1453
  status.hidden = true;
1372
1454
  busy = false;