@spotpatch/runtime 1.15.4 → 1.15.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.
@@ -345,13 +345,19 @@ function contextualAskApiErrorCode(error) {
345
345
  }
346
346
  function createContextualAskApi(options) {
347
347
  const pending = /* @__PURE__ */ new Set();
348
+ const session = /* @__PURE__ */ new Set();
348
349
  function cancelPending() {
349
350
  for (const controller of pending) controller.abort();
350
351
  pending.clear();
351
352
  }
352
- async function request(endpoint, method, body) {
353
+ function dispose() {
354
+ cancelPending();
355
+ for (const controller of session) controller.abort();
356
+ session.clear();
357
+ }
358
+ async function request(endpoint, method, body, scope = pending) {
353
359
  const controller = new AbortController();
354
- pending.add(controller);
360
+ scope.add(controller);
355
361
  try {
356
362
  const response = await options.fetch(endpoint, {
357
363
  method,
@@ -364,7 +370,7 @@ function createContextualAskApi(options) {
364
370
  });
365
371
  return await readEnvelope(response);
366
372
  } finally {
367
- pending.delete(controller);
373
+ scope.delete(controller);
368
374
  }
369
375
  }
370
376
  async function events(jobId, afterSequence, onEvent) {
@@ -436,7 +442,12 @@ function createContextualAskApi(options) {
436
442
  }
437
443
  return Object.freeze({
438
444
  async capability() {
439
- const data = await request(import_contextual_ask_browser2.SPOTPATCH_ENDPOINTS.askCapability, "GET");
445
+ const data = await request(
446
+ import_contextual_ask_browser2.SPOTPATCH_ENDPOINTS.askCapability,
447
+ "GET",
448
+ void 0,
449
+ session
450
+ );
440
451
  if (!isContextualAskCapability(data)) throw new ContextualAskApiError();
441
452
  return deepFreeze(data);
442
453
  },
@@ -455,7 +466,7 @@ function createContextualAskApi(options) {
455
466
  }
456
467
  return deepFreeze(data);
457
468
  },
458
- dispose: cancelPending,
469
+ dispose,
459
470
  events,
460
471
  async result(jobId) {
461
472
  const data = await request((0, import_contextual_ask_browser2.getAskJobEndpoint)(jobId, "result"), "GET");
@@ -479,6 +490,7 @@ function createContextualAskWorkflow(input) {
479
490
  let currentResult;
480
491
  let currentQuestion = "";
481
492
  let capabilityLoaded = false;
493
+ let capabilityLoading = false;
482
494
  let composing = false;
483
495
  const consentByExecutor = /* @__PURE__ */ new Map();
484
496
  const api = input.api ?? createContextualAskApi({
@@ -526,23 +538,24 @@ function createContextualAskWorkflow(input) {
526
538
  createdAt
527
539
  };
528
540
  }
529
- async function loadCapability() {
530
- if (capabilityLoaded) return;
531
- const requestRevision = revision;
541
+ function loadCapability() {
542
+ if (capabilityLoaded || capabilityLoading) return;
543
+ capabilityLoading = true;
532
544
  input.panel.renderCapability(void 0);
533
- try {
534
- const capability = await api.capability();
535
- if (!isCurrent(requestRevision)) return;
545
+ void api.capability().then((capability) => {
546
+ if (!mounted) return;
536
547
  capabilityLoaded = true;
537
548
  input.panel.renderCapability(capability);
538
549
  const executorId = input.panel.readExecutorId();
539
550
  input.panel.setConsent(
540
551
  executorId !== void 0 && consentByExecutor.get(executorId) === true
541
552
  );
542
- } catch (error) {
543
- if (!isCurrent(requestRevision) || isAbortError(error)) return;
553
+ }).catch((error) => {
554
+ if (!mounted || isAbortError(error)) return;
544
555
  input.panel.renderError(contextualAskApiErrorCode(error));
545
- }
556
+ }).finally(() => {
557
+ capabilityLoading = false;
558
+ });
546
559
  }
547
560
  function handleEvent(event, requestRevision) {
548
561
  if (!isCurrent(requestRevision)) return;
@@ -726,7 +739,7 @@ function createContextualAskWorkflow(input) {
726
739
  input.panel.questionInput.addEventListener("compositionstart", compositionStart);
727
740
  input.panel.questionInput.addEventListener("compositionend", compositionEnd);
728
741
  input.panel.root.addEventListener("click", openSource);
729
- void loadCapability();
742
+ loadCapability();
730
743
  }
731
744
  function dispose() {
732
745
  if (!mounted) return;
@@ -754,7 +767,7 @@ function createContextualAskWorkflow(input) {
754
767
  currentResult = void 0;
755
768
  currentQuestion = "";
756
769
  input.panel.clear();
757
- void loadCapability();
770
+ loadCapability();
758
771
  },
759
772
  cancelPending,
760
773
  dispose,
@@ -808,18 +821,18 @@ function createButton(document, label, className = "") {
808
821
  // src/ui/ask-picker.ts
809
822
  var SELECT_PICKER_STYLES = `
810
823
  .spotpatch-select-picker { position: relative; display: grid; gap: 5px; min-width: 0; }
811
- .spotpatch-select-trigger { box-sizing: border-box; display: flex; width: 100%; min-height: 38px; align-items: center; justify-content: space-between; gap: 10px; border: 1px solid var(--spotpatch-border); border-radius: 9px; padding: 0 11px; overflow: hidden; color: var(--spotpatch-text); background: var(--spotpatch-bg-input); cursor: pointer; font: inherit; outline: none; text-align: left; }
824
+ .spotpatch-select-trigger { box-sizing: border-box; display: flex; width: 100%; min-height: 38px; align-items: center; justify-content: space-between; gap: 10px; border: 1px solid var(--spotpatch-border); border-radius: var(--spotpatch-radius-md); padding: 0 12px; overflow: hidden; color: var(--spotpatch-text); background: var(--spotpatch-bg-input); cursor: pointer; font: inherit; outline: none; text-align: left; transition: border-color var(--spotpatch-duration-fast) ease, box-shadow var(--spotpatch-duration-fast) ease; }
825
+ .spotpatch-select-trigger:hover:not(:disabled) { border-color: var(--spotpatch-border-strong); }
812
826
  .spotpatch-select-trigger > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
813
- .spotpatch-select-trigger[data-expandable="true"]::after { width: 7px; height: 7px; flex: 0 0 auto; border-right: 1.5px solid #8f96a3; border-bottom: 1.5px solid #8f96a3; content: ""; transform: translateY(-2px) rotate(45deg); }
827
+ .spotpatch-select-trigger[data-expandable="true"]::after { width: 7px; height: 7px; flex: 0 0 auto; border-right: 1.5px solid var(--spotpatch-text-muted); border-bottom: 1.5px solid var(--spotpatch-text-muted); content: ""; transform: translateY(-2px) rotate(45deg); transition: transform var(--spotpatch-duration-base) var(--spotpatch-ease-out); }
814
828
  .spotpatch-select-trigger[aria-expanded="true"]::after { transform: translateY(2px) rotate(225deg); }
815
- .spotpatch-select-trigger:focus-visible { border-color: rgb(139 123 255 / 68%); box-shadow: 0 0 0 3px rgb(139 123 255 / 12%); }
816
- .spotpatch-select-trigger:disabled { color: var(--spotpatch-text); background: rgb(255 255 255 / 3%); cursor: default; opacity: 1; }
829
+ .spotpatch-select-trigger:focus-visible { border-color: var(--spotpatch-accent-line); box-shadow: 0 0 0 3px var(--spotpatch-accent-tint); }
830
+ .spotpatch-select-trigger:disabled { color: var(--spotpatch-text); background: var(--spotpatch-hover); cursor: default; opacity: 1; }
817
831
  .spotpatch-select-trigger[data-empty="true"]:disabled { color: var(--spotpatch-text-muted); }
818
- .spotpatch-select-menu { position: relative; z-index: 1; display: grid; gap: 3px; max-height: 224px; overflow-y: auto; overscroll-behavior: contain; border: 1px solid var(--spotpatch-border); border-radius: 9px; padding: 4px; background: var(--spotpatch-bg-input); box-shadow: 0 10px 28px rgb(0 0 0 / 28%); scrollbar-width: thin; }
819
- .spotpatch-select-menu[hidden] { display: none; }
820
- .spotpatch-select-option { box-sizing: border-box; min-height: 34px; border: 0; border-radius: 6px; padding: 7px 9px; overflow-wrap: anywhere; color: var(--spotpatch-text-secondary); background: transparent; cursor: pointer; font: inherit; text-align: left; }
821
- .spotpatch-select-option[data-active="true"], .spotpatch-select-option:hover { color: #fff; background: rgb(139 123 255 / 13%); outline: none; }
822
- .spotpatch-select-option[aria-selected="true"] { color: #f4f1ff; background: rgb(139 123 255 / 20%); }
832
+ .spotpatch-select-menu { position: relative; z-index: 1; display: grid; gap: 2px; max-height: 224px; overflow-y: auto; overscroll-behavior: contain; border: 1px solid var(--spotpatch-border); border-radius: var(--spotpatch-radius-md); padding: 4px; background: var(--spotpatch-bg-raised); box-shadow: var(--spotpatch-shadow-panel); animation: spotpatch-enter var(--spotpatch-duration-base) var(--spotpatch-ease-out) both; }
833
+ .spotpatch-select-option { box-sizing: border-box; min-height: 34px; border: 0; border-radius: var(--spotpatch-radius-xs); padding: 7px 10px; overflow-wrap: anywhere; color: var(--spotpatch-text-secondary); background: transparent; cursor: pointer; font: inherit; text-align: left; }
834
+ .spotpatch-select-option[data-active="true"], .spotpatch-select-option:hover { color: var(--spotpatch-text); background: var(--spotpatch-hover); outline: none; }
835
+ .spotpatch-select-option[aria-selected="true"] { color: var(--spotpatch-text); background: var(--spotpatch-accent-tint); }
823
836
  .spotpatch-select-native { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; border: 0; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
824
837
  `;
825
838
  function createSelectPicker(document, onViewChange) {
@@ -1124,53 +1137,80 @@ function contextualAskMessages(locale) {
1124
1137
  }
1125
1138
 
1126
1139
  // src/ui/contextual-ask-panel.ts
1140
+ var MAXIMUM_STAGGERED_ANSWER_ITEMS = 8;
1127
1141
  function createStyles(document) {
1128
1142
  const style = document.createElement("style");
1129
1143
  style.textContent = `
1130
1144
  ${SELECT_PICKER_STYLES}
1131
- .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; }
1132
- .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; }
1133
- .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%); }
1145
+ .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); }
1146
+ .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); }
1147
+ .spotpatch-ask-mode[data-mode="change"]::before { transform: translateX(calc(100% + 3px)); }
1148
+ .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; }
1149
+ .spotpatch-ask-mode button:hover, .spotpatch-ask-mode button[aria-selected="true"] { color: var(--spotpatch-text); }
1134
1150
  .spotpatch-ask-panel { display: grid; gap: 14px; }
1151
+ .spotpatch-ask-panel[data-phase="answered"] > :is(.spotpatch-ask-field, .spotpatch-ask-safety) { display: none; }
1135
1152
  .spotpatch-ask-field { display: grid; gap: 7px; min-width: 0; }
1136
- .spotpatch-ask-field[hidden] { display: none; }
1137
- .spotpatch-ask-field > label, .spotpatch-ask-label { color: #c9cad2; font-size: 11px; font-weight: 680; }
1138
- .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; }
1139
- .spotpatch-ask-question:focus { border-color: rgb(139 123 255 / 68%); box-shadow: 0 0 0 3px rgb(139 123 255 / 12%); }
1153
+ .spotpatch-ask-field > label, .spotpatch-ask-label { color: var(--spotpatch-text-secondary); font-size: 11.5px; font-weight: 600; }
1154
+ .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; }
1155
+ .spotpatch-ask-question::placeholder { color: var(--spotpatch-text-muted); }
1156
+ .spotpatch-ask-question:focus { border-color: var(--spotpatch-accent-line); box-shadow: 0 0 0 3px var(--spotpatch-accent-tint); }
1140
1157
  .spotpatch-ask-suggestions { display: flex; flex-wrap: wrap; gap: 6px; }
1141
- .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; }
1142
- .spotpatch-ask-suggestions button:hover { border-color: rgb(139 123 255 / 42%); color: #fff; }
1143
- .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; }
1144
- .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%); }
1145
- .spotpatch-ask-data { display: flex; justify-content: space-between; gap: 10px; color: #aab3c2; font-size: 10.5px; }
1146
- .spotpatch-ask-data strong { color: #9fe3c4; font-weight: 650; white-space: nowrap; }
1147
- .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; }
1158
+ .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; }
1159
+ .spotpatch-ask-suggestions button:hover { background: var(--spotpatch-accent-tint); }
1160
+ .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; }
1161
+ .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); }
1162
+ .spotpatch-ask-data { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 10px; color: var(--spotpatch-text-secondary); font-size: 11.5px; }
1163
+ .spotpatch-ask-data strong { color: var(--spotpatch-success-text); font-weight: 600; white-space: nowrap; }
1164
+ .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; }
1148
1165
  .spotpatch-ask-consent input { margin: 2px 0 0; accent-color: var(--spotpatch-accent); }
1149
- .spotpatch-ask-actions, .spotpatch-ask-answer-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 7px; }
1150
- .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; }
1151
- .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); }
1152
- .spotpatch-ask-actions button:disabled, .spotpatch-ask-answer-actions button:disabled { opacity: .45; cursor: not-allowed; }
1153
- .spotpatch-ask-status { display: grid; gap: 7px; border-left: 2px solid var(--spotpatch-accent); padding: 2px 0 2px 10px; color: #c8c9d2; font-size: 11px; }
1154
- .spotpatch-ask-activity { display: grid; gap: 4px; color: var(--spotpatch-text-muted); font: 500 10px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace; }
1155
- .spotpatch-ask-error, .spotpatch-ask-stale, .spotpatch-ask-warning { border-radius: 8px; padding: 9px 10px; font-size: 10.5px; }
1156
- .spotpatch-ask-error { color: #fecaca; background: rgb(251 113 133 / 9%); }
1157
- .spotpatch-ask-stale, .spotpatch-ask-warning { color: #f9d68a; background: rgb(245 158 11 / 8%); }
1158
- .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%); }
1159
- .spotpatch-ask-answer h3, .spotpatch-ask-sources h4 { margin: 0; color: var(--spotpatch-text); font-size: 13px; }
1160
- .spotpatch-ask-blocks { display: grid; gap: 10px; color: #d9dae2; font-size: 12px; line-height: 1.65; overflow-wrap: anywhere; }
1166
+ .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); }
1167
+ .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; }
1168
+ .spotpatch-ask-actions button:hover:not(:disabled) { border-color: var(--spotpatch-border-strong); background: var(--spotpatch-bg-active); }
1169
+ .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); }
1170
+ .spotpatch-ask-actions .spotpatch-primary:hover:not(:disabled) { border-color: transparent; background: var(--spotpatch-primary-fill); filter: brightness(1.1); }
1171
+ .spotpatch-ask-actions button:disabled { box-shadow: none; cursor: not-allowed; opacity: .4; }
1172
+ .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; }
1173
+ .spotpatch-ask-activity { display: grid; gap: 4px; color: var(--spotpatch-text-muted); font: 500 11px/1.45 var(--spotpatch-font-mono); }
1174
+ .spotpatch-ask-error, .spotpatch-ask-stale, .spotpatch-ask-warning { border-radius: var(--spotpatch-radius-sm); padding: 9px 10px; font-size: 11.5px; }
1175
+ .spotpatch-ask-error { color: var(--spotpatch-danger-text); background: var(--spotpatch-danger-tint); }
1176
+ .spotpatch-ask-stale, .spotpatch-ask-warning { color: var(--spotpatch-warning-text); background: var(--spotpatch-warning-tint); }
1177
+ .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); }
1178
+ .spotpatch-ask-answer[data-entering="true"] { animation: spotpatch-enter var(--spotpatch-duration-slow) var(--spotpatch-ease-out) both; }
1179
+ .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); }
1180
+ .spotpatch-ask-recap { display: grid; gap: 3px; border-left: 2px solid var(--spotpatch-accent-line); padding: 1px 0 1px 10px; }
1181
+ .spotpatch-ask-recap span { color: var(--spotpatch-text-muted); font-size: 10.5px; font-weight: 600; letter-spacing: .02em; }
1182
+ .spotpatch-ask-recap p { margin: 0; color: var(--spotpatch-text-secondary); font-size: 12.5px; line-height: 1.5; overflow-wrap: anywhere; }
1183
+ .spotpatch-ask-answer-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; min-width: 0; }
1184
+ .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; }
1185
+ .spotpatch-ask-answer h3, .spotpatch-ask-sources h4 { margin: 0; font-size: 13px; font-weight: 620; }
1186
+ .spotpatch-ask-blocks { display: grid; gap: 10px; font-size: 13px; line-height: 1.65; overflow-wrap: anywhere; }
1161
1187
  .spotpatch-ask-blocks p, .spotpatch-ask-blocks ul { margin: 0; }
1162
1188
  .spotpatch-ask-blocks ul { padding-left: 18px; }
1163
- .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; }
1164
- .spotpatch-ask-citations, .spotpatch-ask-source-list { display: flex; flex-wrap: wrap; gap: 5px; }
1165
- .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; }
1189
+ .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); }
1190
+ .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); }
1191
+ .spotpatch-ask-citations, .spotpatch-ask-source-list { display: flex; flex-wrap: wrap; gap: 6px; }
1192
+ .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; }
1193
+ .spotpatch-ask-source:hover { border-color: var(--spotpatch-accent-cyan); }
1166
1194
  .spotpatch-ask-sources { display: grid; gap: 7px; }
1167
- .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; }
1168
- .spotpatch-ask-origin strong { display: block; margin-bottom: 2px; color: #d9d4ff; font-size: 11px; }
1169
- @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; } }
1170
- @media (prefers-reduced-motion: reduce) { .spotpatch-ask-panel *, .spotpatch-ask-mode * { scroll-behavior: auto !important; transition: none !important; } }
1195
+ .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; }
1196
+ .spotpatch-ask-origin strong { display: block; margin-bottom: 2px; color: var(--spotpatch-accent-soft); font-size: 12px; }
1197
+ @media (max-width: 420px) { .spotpatch-ask-actions { justify-content: stretch; } .spotpatch-ask-actions button { flex: 1 1 auto; } }
1171
1198
  `;
1172
1199
  return style;
1173
1200
  }
1201
+ var INLINE_CODE = /`([^`\n]+)`/gu;
1202
+ function appendInlineText(document, root, text) {
1203
+ let offset = 0;
1204
+ for (const match of text.matchAll(INLINE_CODE)) {
1205
+ root.append(text.slice(offset, match.index));
1206
+ const code = createMarkedElement(document, "code");
1207
+ code.className = "spotpatch-ask-inline-code";
1208
+ code.textContent = match[1] ?? "";
1209
+ root.append(code);
1210
+ offset = match.index + match[0].length;
1211
+ }
1212
+ root.append(text.slice(offset));
1213
+ }
1174
1214
  function appendSourceChips(document, root, sourceIds, sources, messages) {
1175
1215
  if (sourceIds.length === 0) return;
1176
1216
  const chips = createMarkedElement(document, "div");
@@ -1205,6 +1245,7 @@ function createContextualAskPanel(input) {
1205
1245
  sourceCount: 0
1206
1246
  });
1207
1247
  let busy = false;
1248
+ let renderedJobId;
1208
1249
  const root = createMarkedElement(document, "section");
1209
1250
  const modeSwitch = createMarkedElement(document, "div");
1210
1251
  modeSwitch.className = "spotpatch-ask-mode";
@@ -1284,16 +1325,34 @@ function createContextualAskPanel(input) {
1284
1325
  actions.className = "spotpatch-ask-actions";
1285
1326
  const newQuestionButton = createButton(document, "");
1286
1327
  const cancelButton = createButton(document, "");
1328
+ const copyButton = createButton(document, "");
1329
+ const convertButton = createButton(document, "", "spotpatch-primary");
1287
1330
  const submitButton = createButton(document, "", "spotpatch-primary");
1288
1331
  cancelButton.hidden = true;
1289
- actions.append(newQuestionButton, cancelButton, submitButton);
1332
+ actions.append(
1333
+ newQuestionButton,
1334
+ cancelButton,
1335
+ copyButton,
1336
+ convertButton,
1337
+ submitButton
1338
+ );
1290
1339
  const answer = createMarkedElement(document, "article");
1291
1340
  answer.className = "spotpatch-ask-answer";
1292
1341
  answer.hidden = true;
1293
1342
  const stale = createMarkedElement(document, "div");
1294
1343
  stale.className = "spotpatch-ask-stale";
1295
1344
  stale.hidden = true;
1345
+ const recap = createMarkedElement(document, "div");
1346
+ recap.className = "spotpatch-ask-recap";
1347
+ const recapLabel = createMarkedElement(document, "span");
1348
+ const recapText = createMarkedElement(document, "p");
1349
+ recap.append(recapLabel, recapText);
1350
+ const answerHead = createMarkedElement(document, "div");
1351
+ answerHead.className = "spotpatch-ask-answer-head";
1296
1352
  const answerTitle = createMarkedElement(document, "h3");
1353
+ const answerMeta = createMarkedElement(document, "span");
1354
+ answerMeta.className = "spotpatch-ask-answer-meta";
1355
+ answerHead.append(answerTitle, answerMeta);
1297
1356
  const warnings = createMarkedElement(document, "div");
1298
1357
  const blocks = createMarkedElement(document, "div");
1299
1358
  blocks.className = "spotpatch-ask-blocks";
@@ -1303,21 +1362,16 @@ function createContextualAskPanel(input) {
1303
1362
  const sourceList = createMarkedElement(document, "div");
1304
1363
  sourceList.className = "spotpatch-ask-source-list";
1305
1364
  sourcesSection.append(sourcesTitle, sourceList);
1306
- const answerActions = createMarkedElement(document, "div");
1307
- answerActions.className = "spotpatch-ask-answer-actions";
1308
- const copyButton = createButton(document, "");
1309
- const convertButton = createButton(document, "", "spotpatch-primary");
1310
- answerActions.append(copyButton, convertButton);
1311
- answer.append(stale, answerTitle, warnings, blocks, sourcesSection, answerActions);
1365
+ answer.append(stale, recap, answerHead, warnings, blocks, sourcesSection);
1312
1366
  askPanel.append(
1367
+ error,
1368
+ answer,
1313
1369
  questionField,
1314
1370
  executorField,
1315
1371
  modelField,
1316
1372
  safety,
1317
1373
  status,
1318
- error,
1319
- actions,
1320
- answer
1374
+ actions
1321
1375
  );
1322
1376
  root.append(modeSwitch, origin, askPanel);
1323
1377
  function hasReadyExecutor() {
@@ -1357,11 +1411,17 @@ function createContextualAskPanel(input) {
1357
1411
  executorPicker.setDisabled(executorUnavailable);
1358
1412
  modelPicker.setDisabled(busy || !hasReadyExecutor());
1359
1413
  consentCheckbox.disabled = busy || !hasReadyExecutor();
1360
- newQuestionButton.hidden = busy || currentResult === void 0;
1414
+ const answered = !busy && currentResult !== void 0;
1415
+ askPanel.dataset.phase = busy ? "running" : answered ? "answered" : "compose";
1416
+ newQuestionButton.hidden = !answered;
1417
+ copyButton.hidden = !answered;
1418
+ convertButton.hidden = !answered;
1419
+ submitButton.hidden = answered;
1361
1420
  cancelButton.hidden = !busy;
1362
1421
  }
1363
1422
  function applyMode() {
1364
1423
  const asking = currentMode === "ask";
1424
+ modeSwitch.dataset.mode = currentMode;
1365
1425
  askTab.setAttribute("aria-selected", String(asking));
1366
1426
  changeTab.setAttribute("aria-selected", String(!asking));
1367
1427
  askPanel.hidden = !asking;
@@ -1395,6 +1455,7 @@ function createContextualAskPanel(input) {
1395
1455
  askTab.textContent = messages.mode.ask;
1396
1456
  changeTab.textContent = messages.mode.change;
1397
1457
  questionLabel.textContent = messages.questionLabel;
1458
+ recapLabel.textContent = messages.questionLabel;
1398
1459
  questionInput.placeholder = messages.questionPlaceholder;
1399
1460
  suggestions.setAttribute("aria-label", messages.suggestionsLabel);
1400
1461
  executorLabel.textContent = messages.executorLabel;
@@ -1545,7 +1606,7 @@ function createContextualAskPanel(input) {
1545
1606
  for (const block of result.blocks) {
1546
1607
  if (block.kind === "paragraph") {
1547
1608
  const paragraph = createMarkedElement(document, "p");
1548
- paragraph.textContent = block.text;
1609
+ appendInlineText(document, paragraph, block.text);
1549
1610
  blocks.append(paragraph);
1550
1611
  appendSourceChips(document, blocks, block.sourceIds, sourceMap, messages);
1551
1612
  } else if (block.kind === "code") {
@@ -1560,7 +1621,7 @@ function createContextualAskPanel(input) {
1560
1621
  const list = createMarkedElement(document, "ul");
1561
1622
  for (const item of block.items) {
1562
1623
  const listItem = createMarkedElement(document, "li");
1563
- listItem.textContent = item.text;
1624
+ appendInlineText(document, listItem, item.text);
1564
1625
  appendSourceChips(document, listItem, item.sourceIds, sourceMap, messages);
1565
1626
  list.append(listItem);
1566
1627
  }
@@ -1578,6 +1639,27 @@ function createContextualAskPanel(input) {
1578
1639
  sourceList.append(button);
1579
1640
  }
1580
1641
  sourcesSection.hidden = result.sources.length === 0;
1642
+ warnings.hidden = result.warnings.length === 0;
1643
+ const question = questionInput.value.trim();
1644
+ recapText.textContent = question;
1645
+ recap.hidden = question.length === 0;
1646
+ answerMeta.textContent = `${result.executor.label} · ${result.executor.modelLabel}`;
1647
+ answer.dataset.entering = String(result.jobId !== renderedJobId);
1648
+ renderedJobId = result.jobId;
1649
+ const sequence = [
1650
+ recap,
1651
+ answerHead,
1652
+ ...warnings.querySelectorAll(":scope > *"),
1653
+ ...blocks.querySelectorAll(":scope > *"),
1654
+ sourcesSection
1655
+ ];
1656
+ for (const [order, element] of sequence.entries()) {
1657
+ element.dataset.askOrder = "";
1658
+ element.style.setProperty(
1659
+ "--spotpatch-ask-order",
1660
+ String(Math.min(order, MAXIMUM_STAGGERED_ANSWER_ITEMS))
1661
+ );
1662
+ }
1581
1663
  answer.hidden = false;
1582
1664
  status.hidden = true;
1583
1665
  busy = false;