@jskit-ai/assistant-core 0.2.6 → 0.2.8
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.
- package/examples/conversation/package.json +5 -5
- package/package.json +18 -7
- package/src/client/browser.js +21 -0
- package/src/client/conversation/AssistantAttachmentQueue.vue +25 -108
- package/src/client/conversation/AssistantComposerActions.vue +1 -1
- package/src/client/conversation/AssistantConversationElement.vue +14 -52
- package/src/client/conversation/AssistantGoalControl.vue +18 -12
- package/src/client/conversation/AssistantPromptInput.vue +6 -91
- package/src/client/conversation/AssistantTranscript.css +505 -0
- package/src/client/conversation/AssistantTranscript.vue +24 -1197
- package/src/client/conversation/attachmentFiles.js +158 -0
- package/src/client/conversation/attachmentQueueItems.js +121 -0
- package/src/client/conversation/attachmentUploadRows.js +265 -0
- package/src/client/conversation/conversationDisplay.js +54 -0
- package/src/client/conversation/messageDelivery.js +13 -9
- package/src/client/conversation/retainedConversation.js +14 -3
- package/src/client/conversation/submitText.js +13 -2
- package/src/client/conversation/transcriptEntries.js +225 -0
- package/src/client/conversation/useAssistantAttachments.js +86 -388
- package/src/client/conversation/useAssistantQuestions.js +16 -3
- package/src/client/conversation/useAssistantSuggestions.js +16 -2
- package/src/client/conversation/useScrollToBottom.js +17 -19
- package/src/client/conversation/useTextareaAutoGrow.js +115 -0
- package/src/client/conversation/useTranscriptScrollFollow.js +525 -0
- package/src/client/lib/assistantApi.js +192 -161
- package/src/client/types.js +110 -0
- package/src/server/conversation/attachments.js +21 -6
- package/src/server/conversation/boundedToolLoop.js +1 -0
- package/src/server/conversation/claudeClient.js +71 -20
- package/src/server/conversation/claudeCommandRun.js +191 -0
- package/src/server/conversation/claudeHistory.js +7 -0
- package/src/server/conversation/claudeNativeTurn.js +708 -0
- package/src/server/conversation/claudeProcess.js +186 -112
- package/src/server/conversation/claudeProcessStart.js +214 -0
- package/src/server/conversation/claudePromptDelivery.js +183 -0
- package/src/server/conversation/claudeSessionCleanup.js +248 -0
- package/src/server/conversation/claudeTools.js +15 -3
- package/src/server/conversation/claudeTurn.js +212 -1388
- package/src/server/conversation/codexAccountReader.js +168 -0
- package/src/server/conversation/codexAuthPreflight.js +5 -4
- package/src/server/conversation/codexClient.js +18 -6
- package/src/server/conversation/codexConfiguration.js +8 -844
- package/src/server/conversation/codexConversationStops.js +377 -0
- package/src/server/conversation/codexConversationStore.js +119 -85
- package/src/server/conversation/codexConversationTurns.js +378 -0
- package/src/server/conversation/codexConversations.js +134 -866
- package/src/server/conversation/codexDetachedConversation.js +203 -0
- package/src/server/conversation/codexDetachedTurn.js +145 -104
- package/src/server/conversation/codexEvents.js +5 -200
- package/src/server/conversation/codexGoalCommands.js +14 -4
- package/src/server/conversation/codexHelperExecution.js +57 -57
- package/src/server/conversation/codexHelperThreadLedger.js +30 -288
- package/src/server/conversation/codexHelperThreadRecord.js +304 -0
- package/src/server/conversation/codexHelperThreadRecovery.js +54 -76
- package/src/server/conversation/codexHelperThreads.js +94 -93
- package/src/server/conversation/codexHistoryAdapter.js +13 -8
- package/src/server/conversation/codexIsolation.js +672 -0
- package/src/server/conversation/codexLiveItems.js +379 -0
- package/src/server/conversation/codexMessageCommands.js +68 -286
- package/src/server/conversation/codexMessageDelivery.js +46 -37
- package/src/server/conversation/codexModelCatalog.js +86 -7
- package/src/server/conversation/codexNotificationObserver.js +81 -88
- package/src/server/conversation/codexNotificationQueue.js +21 -10
- package/src/server/conversation/codexObservationControl.js +105 -89
- package/src/server/conversation/codexOutput.js +84 -419
- package/src/server/conversation/codexProcess.js +29 -1053
- package/src/server/conversation/codexProcessLeader.js +1 -0
- package/src/server/conversation/codexPromptDispatch.js +241 -0
- package/src/server/conversation/codexProvider.js +147 -1807
- package/src/server/conversation/codexProviderAuth.js +348 -0
- package/src/server/conversation/codexProviderInvalidation.js +371 -0
- package/src/server/conversation/codexProviderInventories.js +437 -0
- package/src/server/conversation/codexProviderOwner.js +149 -957
- package/src/server/conversation/codexProviderRelease.js +465 -0
- package/src/server/conversation/codexProviderSelection.js +26 -15
- package/src/server/conversation/codexProviderThreads.js +698 -0
- package/src/server/conversation/codexProviderTurns.js +453 -0
- package/src/server/conversation/codexReasoning.js +32 -23
- package/src/server/conversation/codexRenewalCommands.js +18 -6
- package/src/server/conversation/codexRenewalThreads.js +7 -22
- package/src/server/conversation/codexResultSettlement.js +70 -88
- package/src/server/conversation/codexRunJournal.js +91 -97
- package/src/server/conversation/codexRuntime.js +426 -0
- package/src/server/conversation/codexRuntimeInventory.js +113 -0
- package/src/server/conversation/codexRuntimeLifecycle.js +39 -13
- package/src/server/conversation/codexRuntimeStart.js +431 -0
- package/src/server/conversation/codexRuntimeStop.js +324 -0
- package/src/server/conversation/codexSessionCleanup.js +25 -10
- package/src/server/conversation/codexSessionReconciliation.js +21 -7
- package/src/server/conversation/codexThreadReadiness.js +49 -52
- package/src/server/conversation/codexThreadSnapshot.js +210 -0
- package/src/server/conversation/codexThreadStatus.js +48 -62
- package/src/server/conversation/codexTurn.js +171 -55
- package/src/server/conversation/codexTurnActivity.js +210 -0
- package/src/server/conversation/codexTurnControl.js +44 -48
- package/src/server/conversation/codexTurnEndings.js +610 -0
- package/src/server/conversation/codexTurnRecovery.js +97 -848
- package/src/server/conversation/codexTurnState.js +138 -111
- package/src/server/conversation/completedResponses.js +227 -0
- package/src/server/conversation/configuration.js +2 -2
- package/src/server/conversation/continuity.js +43 -6
- package/src/server/conversation/conversationDelivery.js +394 -0
- package/src/server/conversation/conversationEntries.js +365 -0
- package/src/server/conversation/conversationEvents.js +192 -0
- package/src/server/conversation/conversationGoals.js +187 -0
- package/src/server/conversation/conversationHandles.js +237 -0
- package/src/server/conversation/conversationReads.js +206 -0
- package/src/server/conversation/conversationRefusals.js +58 -0
- package/src/server/conversation/conversationReplacement.js +254 -0
- package/src/server/conversation/conversationSending.js +273 -0
- package/src/server/conversation/conversationStopping.js +121 -0
- package/src/server/conversation/conversationTurnDelivery.js +413 -0
- package/src/server/conversation/conversationTurns.js +511 -0
- package/src/server/conversation/deliveryTiming.js +25 -3
- package/src/server/conversation/fileStorage.js +11 -6
- package/src/server/conversation/hookBridge.js +14 -3
- package/src/server/conversation/index.js +1 -1
- package/src/server/conversation/localExecution.js +82 -625
- package/src/server/conversation/nativeCommandCallbacks.js +10 -0
- package/src/server/conversation/nativeConversationLifecycle.js +379 -0
- package/src/server/conversation/nativeHistoryExport.js +27 -18
- package/src/server/conversation/normalize.js +9 -0
- package/src/server/conversation/openCodeClient.js +295 -366
- package/src/server/conversation/openCodeNativeHistory.js +141 -0
- package/src/server/conversation/openCodeNativeSession.js +79 -0
- package/src/server/conversation/openCodeProcess.js +109 -53
- package/src/server/conversation/openCodeProcessOwner.js +565 -0
- package/src/server/conversation/openCodeRuntime.js +111 -1814
- package/src/server/conversation/openCodeSessionTurns.js +547 -0
- package/src/server/conversation/openCodeTemporaryConversations.js +394 -0
- package/src/server/conversation/openCodeTools.js +28 -14
- package/src/server/conversation/openCodeTurn.js +80 -74
- package/src/server/conversation/openCodeTurnMonitor.js +388 -0
- package/src/server/conversation/processIdentity.js +582 -0
- package/src/server/conversation/provider.js +21 -6
- package/src/server/conversation/providers/api.js +268 -107
- package/src/server/conversation/providers/claude.js +29 -6
- package/src/server/conversation/providers/claudeDriver.js +325 -250
- package/src/server/conversation/providers/codex.js +69 -33
- package/src/server/conversation/providers/codexDriver.js +503 -1081
- package/src/server/conversation/providers/codexDriverConnection.js +331 -0
- package/src/server/conversation/providers/codexDriverTurns.js +464 -0
- package/src/server/conversation/providers/opencode.js +30 -4
- package/src/server/conversation/providers/opencodeDriver.js +321 -659
- package/src/server/conversation/providers/opencodeStandalone.js +475 -0
- package/src/server/conversation/providers/scoped.js +64 -19
- package/src/server/conversation/runtime.js +171 -2863
- package/src/server/conversation/storage.js +91 -50
- package/src/server/conversation/streams.js +15 -2
- package/src/server/conversation/structuredOutput.js +7 -11
- package/src/server/conversation/systemPrompt.js +13 -1
- package/src/server/conversation/tools.js +47 -15
- package/src/server/conversation/transcript.js +49 -20
- package/src/server/hosts/claudeTurn.js +2 -4
- package/src/server/hosts/codexConfiguration.js +6 -8
- package/src/server/hosts/codexEvents.js +4 -2
- package/src/server/hosts/codexProcess.js +1 -1
- package/src/server/hosts/codexProvider.js +11 -9
- package/src/server/hosts/codexTurn.js +3 -3
- package/src/server/hosts/openCodeClient.js +4 -2
- package/src/server/index.js +0 -2
- package/src/server/lib/aiConnectionClient.js +35 -25
- package/src/server/lib/ndjson.js +1 -0
- package/src/server/lib/providers/anthropicClient.js +15 -6
- package/src/server/lib/providers/common.js +10 -6
- package/src/server/lib/providers/openAiCompatibleClient.js +40 -23
- package/src/server/lib/serviceToolCatalog.js +29 -283
- package/src/server/lib/serviceToolEntries.js +270 -0
- package/src/server/types.js +78 -0
- package/src/shared/assistantResource.js +0 -23
- package/src/shared/conversation/answerChoiceSugar.js +2 -14
- package/src/shared/conversation/messageInputSugar.js +18 -0
- package/src/shared/conversation/numberedQuestionSugar.js +6 -16
- package/src/shared/conversation/pagination.js +1 -1
- package/src/shared/conversation/questions.js +1 -0
- package/src/shared/conversation/streaming.js +1 -0
- package/src/shared/index.js +0 -1
- package/src/testing/conversationStorageContract.js +20 -0
- package/test/aiConnectionClient.test.js +42 -1
- package/test/apiConversationDriver.test.js +217 -38
- package/test/assistantBoundedToolLoop.test.js +1 -1
- package/test/claudeConversationSession.test.js +15 -11
- package/test/claudeInstructionResume.test.js +1 -1
- package/test/claudeProcess.test.js +9 -9
- package/test/claudeTurn.test.js +21 -8
- package/test/codexAccountChange.test.js +5 -3
- package/test/codexConversationSession.test.js +22 -21
- package/test/codexEvents.test.js +3 -1
- package/test/codexHelperDeletion.test.js +3 -7
- package/test/codexIsolation.test.js +6 -6
- package/test/codexModelCatalog.test.js +6 -7
- package/test/codexProcess.test.js +3 -3
- package/test/codexThreadSelection.test.js +4 -1
- package/test/codexTurn.test.js +29 -20
- package/test/conversationApiFacilities.test.js +11 -9
- package/test/conversationAttachments.test.js +3 -3
- package/test/conversationConfiguration.test.js +4 -4
- package/test/conversationNativeStorage.test.js +11 -10
- package/test/conversationReplacement.test.js +13 -13
- package/test/conversationRuntime.test.js +9 -5
- package/test/conversationSession.test.js +78 -22
- package/test/conversationTools.test.js +47 -26
- package/test/fileConversationStorage.test.js +10 -3
- package/test/fixtures/codexHelperThreadLedger.js +3 -3
- package/test/frameworkActions.test.js +33 -18
- package/test/localConversationExecution.test.js +4 -4
- package/test/localExecution.test.js +1 -1
- package/test/openCodeConnectionChange.test.js +3 -2
- package/test/openCodeRuntime.test.js +8 -7
- package/test/openCodeTools.test.js +1 -1
- package/test/opencodeConversationSession.test.js +4 -3
- package/test/serviceToolCatalog.test.js +3 -8
- package/src/server/lib/assistantCompletion.js +0 -190
- package/src/server/lib/assistantToolLoop.js +0 -259
|
@@ -318,30 +318,21 @@
|
|
|
318
318
|
<script setup>
|
|
319
319
|
import AssistantMessageAttachments from "./AssistantMessageAttachments.vue";
|
|
320
320
|
import AssistantConversationStatus from "./AssistantConversationStatus.vue";
|
|
321
|
-
import { computed,
|
|
321
|
+
import { computed, ref, watch } from "vue";
|
|
322
322
|
import {
|
|
323
323
|
mdiAccountOutline,
|
|
324
324
|
mdiInformationOutline,
|
|
325
325
|
mdiRefresh,
|
|
326
326
|
mdiRobotOutline
|
|
327
327
|
} from "@mdi/js";
|
|
328
|
-
import { useScrollToBottom } from "./useScrollToBottom.js";
|
|
329
328
|
import LongTextInlineParts from "./LongTextInlineParts.vue";
|
|
330
329
|
import LongTextPreviewBlocks from "./LongTextPreviewBlocks.vue";
|
|
331
330
|
import AssistantProgress from "./AssistantProgress.vue";
|
|
332
|
-
import {
|
|
333
|
-
import {
|
|
334
|
-
import {
|
|
335
|
-
scrollDistanceFromBottom,
|
|
336
|
-
scrollElementNearBottom
|
|
337
|
-
} from "../../shared/conversation/scrollFollowState.js";
|
|
338
|
-
import {
|
|
339
|
-
normalizeThinkingMessageText
|
|
340
|
-
} from "../../shared/conversation/thinkingText.js";
|
|
331
|
+
import { useTranscriptEntries, userMessagePreviewText } from "./transcriptEntries.js";
|
|
332
|
+
import { useTranscriptScrollFollow } from "./useTranscriptScrollFollow.js";
|
|
341
333
|
import { useJskitText } from "../i18n.js";
|
|
342
334
|
|
|
343
|
-
const
|
|
344
|
-
const { formatDateTime, serverMessage, t } = jskitText;
|
|
335
|
+
const { t } = useJskitText();
|
|
345
336
|
|
|
346
337
|
function previewForTurn(turn) {
|
|
347
338
|
const preview = turn.previewMessage || props.previewMessage;
|
|
@@ -427,234 +418,11 @@ const systemName = computed(() => props.systemLabel ?? t("jskit.assistant.transc
|
|
|
427
418
|
const assistantName = computed(() => props.assistantLabel ?? t("jskit.assistant.transcript.assistant"));
|
|
428
419
|
const reloadLabel = computed(() => props.reloading ? t("jskit.assistant.transcript.reloading") : t("jskit.assistant.transcript.reload"));
|
|
429
420
|
|
|
430
|
-
const USER_MESSAGE_COLLAPSE_MIN_CHARACTERS = 360;
|
|
431
|
-
const USER_MESSAGE_PREVIEW_MAX_CHARACTERS = 280;
|
|
432
|
-
const USER_MESSAGE_PREVIEW_MAX_LINES = 4;
|
|
433
|
-
const DISPLAY_MESSAGE_CACHE_LIMIT = 500;
|
|
434
421
|
const bodyElement = ref(null);
|
|
435
422
|
const bottomElement = ref(null);
|
|
436
423
|
const expandedUserMessages = ref(new Set());
|
|
437
424
|
const isWorking = computed(() => props.working ?? props.turns.some(turn => turn.pending));
|
|
438
|
-
const
|
|
439
|
-
const initialScrollSettled = ref(false);
|
|
440
|
-
const userScrollIntent = ref(false);
|
|
441
|
-
const displayMessageCache = new Map();
|
|
442
|
-
let liveScrollFrame = 0;
|
|
443
|
-
let userScrollIntentTimer = null;
|
|
444
|
-
let initialScrollVersion = 0;
|
|
445
|
-
let loadMoreScrollSnapshot = null;
|
|
446
|
-
let retainedReaderScrollSnapshot = null;
|
|
447
|
-
let loadMoreRequestVersion = 0;
|
|
448
|
-
let pendingTailFollow = false;
|
|
449
|
-
let previousScrollTop = 0;
|
|
450
|
-
let previousScrollHeight = 0;
|
|
451
|
-
let userScrollIntentUpward = false;
|
|
452
|
-
let lastTouchY = null;
|
|
453
|
-
const USER_SCROLL_INTENT_RESET_MS = 600;
|
|
454
|
-
const LOAD_MORE_THRESHOLD_PX = 160;
|
|
455
|
-
const KEYBOARD_SCROLL_KEYS = new Set([
|
|
456
|
-
" ",
|
|
457
|
-
"ArrowDown",
|
|
458
|
-
"ArrowUp",
|
|
459
|
-
"End",
|
|
460
|
-
"Home",
|
|
461
|
-
"PageDown",
|
|
462
|
-
"PageUp",
|
|
463
|
-
"Spacebar"
|
|
464
|
-
]);
|
|
465
|
-
const UPWARD_SCROLL_KEYS = new Set(["ArrowUp", "Home", "PageUp"]);
|
|
466
|
-
const TIME_FORMAT = Object.freeze({
|
|
467
|
-
year: "numeric",
|
|
468
|
-
month: "short",
|
|
469
|
-
day: "2-digit",
|
|
470
|
-
hour: "2-digit",
|
|
471
|
-
minute: "2-digit"
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
function displayTime(value = "") {
|
|
475
|
-
const date = new Date(String(value || ""));
|
|
476
|
-
if (Number.isNaN(date.getTime())) {
|
|
477
|
-
return "";
|
|
478
|
-
}
|
|
479
|
-
return formatDateTime(date, TIME_FORMAT);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
function userMessagePreviewText(value = "") {
|
|
483
|
-
const text = String(value || "");
|
|
484
|
-
const characters = [...text];
|
|
485
|
-
const lines = text.split(/\r\n?|\n/u);
|
|
486
|
-
if (
|
|
487
|
-
characters.length <= USER_MESSAGE_COLLAPSE_MIN_CHARACTERS &&
|
|
488
|
-
lines.length <= USER_MESSAGE_PREVIEW_MAX_LINES
|
|
489
|
-
) {
|
|
490
|
-
return "";
|
|
491
|
-
}
|
|
492
|
-
const firstLines = lines.slice(0, USER_MESSAGE_PREVIEW_MAX_LINES).join("\n");
|
|
493
|
-
const preview = [...firstLines]
|
|
494
|
-
.slice(0, USER_MESSAGE_PREVIEW_MAX_CHARACTERS)
|
|
495
|
-
.join("")
|
|
496
|
-
.trimEnd();
|
|
497
|
-
return preview ? `${preview}…` : "";
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
function displayMessage(message = null, {
|
|
501
|
-
allowNumberedQuestions = false,
|
|
502
|
-
preserveParagraphLineBreaks = false,
|
|
503
|
-
previewUserMessage = false
|
|
504
|
-
} = {}, cacheKey = "") {
|
|
505
|
-
if (!message) {
|
|
506
|
-
return null;
|
|
507
|
-
}
|
|
508
|
-
const normalizedCacheKey = String(cacheKey || "").trim();
|
|
509
|
-
const cached = normalizedCacheKey ? displayMessageCache.get(normalizedCacheKey) : null;
|
|
510
|
-
// Times and status texts follow the active locale, so a locale change re-renders them.
|
|
511
|
-
const activeLocale = jskitText.locale;
|
|
512
|
-
if (
|
|
513
|
-
cached &&
|
|
514
|
-
cached.locale === activeLocale &&
|
|
515
|
-
cached.allowNumberedQuestions === allowNumberedQuestions &&
|
|
516
|
-
cached.attachments === message.attachments &&
|
|
517
|
-
cached.at === message.at &&
|
|
518
|
-
cached.messageId === message.messageId &&
|
|
519
|
-
cached.preserveParagraphLineBreaks === preserveParagraphLineBreaks &&
|
|
520
|
-
cached.previewUserMessage === previewUserMessage &&
|
|
521
|
-
cached.role === message.role &&
|
|
522
|
-
cached.text === message.text
|
|
523
|
-
) {
|
|
524
|
-
return { ...message, ...cached.value };
|
|
525
|
-
}
|
|
526
|
-
const messageText = message.textCode ? serverMessage({ message: message.text, messageCode: message.textCode }) : message.text;
|
|
527
|
-
const questionInput = allowNumberedQuestions
|
|
528
|
-
? parseNumberedQuestionPrompt(messageText)
|
|
529
|
-
: {
|
|
530
|
-
intro: "",
|
|
531
|
-
outro: "",
|
|
532
|
-
questions: []
|
|
533
|
-
};
|
|
534
|
-
const hasQuestions = questionInput.questions.length > 0;
|
|
535
|
-
const previewText = previewUserMessage ? userMessagePreviewText(message.text) : "";
|
|
536
|
-
const value = {
|
|
537
|
-
blocks: parseLongTextReviewBlocks(hasQuestions ? questionInput.intro : messageText, {
|
|
538
|
-
preserveParagraphLineBreaks
|
|
539
|
-
}),
|
|
540
|
-
outroBlocks: parseLongTextReviewBlocks(hasQuestions ? questionInput.outro : "", {
|
|
541
|
-
preserveParagraphLineBreaks
|
|
542
|
-
}),
|
|
543
|
-
previewBlocks: previewText
|
|
544
|
-
? parseLongTextReviewBlocks(previewText, { preserveParagraphLineBreaks: true })
|
|
545
|
-
: null,
|
|
546
|
-
questions: hasQuestions ? questionInput.questions : [],
|
|
547
|
-
displayAt: displayTime(message.at)
|
|
548
|
-
};
|
|
549
|
-
if (normalizedCacheKey) {
|
|
550
|
-
if (
|
|
551
|
-
displayMessageCache.size >= DISPLAY_MESSAGE_CACHE_LIMIT &&
|
|
552
|
-
!displayMessageCache.has(normalizedCacheKey)
|
|
553
|
-
) {
|
|
554
|
-
displayMessageCache.delete(displayMessageCache.keys().next().value);
|
|
555
|
-
}
|
|
556
|
-
displayMessageCache.set(normalizedCacheKey, {
|
|
557
|
-
allowNumberedQuestions,
|
|
558
|
-
attachments: message.attachments,
|
|
559
|
-
at: message.at,
|
|
560
|
-
locale: activeLocale,
|
|
561
|
-
messageId: message.messageId,
|
|
562
|
-
preserveParagraphLineBreaks,
|
|
563
|
-
previewUserMessage,
|
|
564
|
-
role: message.role,
|
|
565
|
-
text: message.text,
|
|
566
|
-
value
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
return { ...message, ...value };
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
function displayThinkingMessage(message = null) {
|
|
573
|
-
if (!message) {
|
|
574
|
-
return null;
|
|
575
|
-
}
|
|
576
|
-
const text = normalizeThinkingMessageText(message.text);
|
|
577
|
-
if (!text) {
|
|
578
|
-
return null;
|
|
579
|
-
}
|
|
580
|
-
return {
|
|
581
|
-
...message,
|
|
582
|
-
text,
|
|
583
|
-
displayAt: displayTime(message.at)
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
function conversationAgentMessages(turn = {}) {
|
|
588
|
-
if (Array.isArray(turn.messages)) {
|
|
589
|
-
return turn.messages.filter((message) => (
|
|
590
|
-
["assistant", "commentary", "thinking"].includes(String(message?.role || "").trim())
|
|
591
|
-
));
|
|
592
|
-
}
|
|
593
|
-
return [
|
|
594
|
-
...(Array.isArray(turn.thinking) ? turn.thinking : []),
|
|
595
|
-
...(Array.isArray(turn.commentary) ? turn.commentary : []),
|
|
596
|
-
turn.assistant
|
|
597
|
-
].filter(Boolean);
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
function conversationMessageKey(message = {}, index = 0) {
|
|
601
|
-
return [
|
|
602
|
-
String(message.messageId || "").trim(),
|
|
603
|
-
String(message.role || "").trim(),
|
|
604
|
-
String(message.at || "").trim(),
|
|
605
|
-
index
|
|
606
|
-
].join(":");
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
// Storage rows are not visual boundaries. Only an intervening message ends a
|
|
610
|
-
// reasoning group, including when history is prepended or execution continues.
|
|
611
|
-
const displayEntries = computed(() => {
|
|
612
|
-
const candidates = [];
|
|
613
|
-
for (const [index, turn] of props.turns.entries()) {
|
|
614
|
-
const turnId = String(turn.turnId || index + 1);
|
|
615
|
-
// A delivery notice belongs to its exact user message, not the previous reply.
|
|
616
|
-
const userDelivery = turn.user && turn.system?.delivery && turn.system.delivery.messageId === turn.user.messageId
|
|
617
|
-
? turn.system : null;
|
|
618
|
-
const messages = [
|
|
619
|
-
turn.system && !userDelivery && { ...turn.system, role: "system" },
|
|
620
|
-
turn.user && { ...turn.user, role: "user" },
|
|
621
|
-
...conversationAgentMessages(turn)
|
|
622
|
-
].filter(Boolean);
|
|
623
|
-
for (const [messageIndex, message] of messages.entries()) {
|
|
624
|
-
const role = String(message.role || "").trim();
|
|
625
|
-
const key = `${turnId}:${conversationMessageKey(message, messageIndex)}`;
|
|
626
|
-
const displayed = role === "thinking" ? displayThinkingMessage(message) : displayMessage(message, {
|
|
627
|
-
allowNumberedQuestions: role !== "user" && role !== "system",
|
|
628
|
-
preserveParagraphLineBreaks: role === "user",
|
|
629
|
-
previewUserMessage: role === "user"
|
|
630
|
-
}, key);
|
|
631
|
-
if (!displayed) continue;
|
|
632
|
-
candidates.push({ key, role, turn: { ...turn, turnId }, message: displayed,
|
|
633
|
-
sortAt: turn.optimistic && turn.optimistic.status !== "accepted" && turn.optimistic.id === message.messageId
|
|
634
|
-
? NaN : Date.parse(message.at),
|
|
635
|
-
delivery: role === "user" ? userDelivery : null });
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
// Native streaming turns and saved authored turns can arrive independently.
|
|
639
|
-
// Local queued timestamps can precede an earlier message's native admission.
|
|
640
|
-
// Keep unconfirmed messages in their supplied positions, along with undated
|
|
641
|
-
// notices. Accepted local copies join chronology even before history arrives.
|
|
642
|
-
const dated = candidates.filter(entry => Number.isFinite(entry.sortAt))
|
|
643
|
-
.sort((a, b) => a.sortAt - b.sortAt)[Symbol.iterator]();
|
|
644
|
-
const entries = [];
|
|
645
|
-
for (const candidate of candidates) {
|
|
646
|
-
const entry = Number.isFinite(candidate.sortAt) ? dated.next().value : candidate;
|
|
647
|
-
const previous = entries.at(-1);
|
|
648
|
-
if (entry.role === "thinking" && previous?.role === "thinking") previous.messages.push({ ...entry.message, key: entry.key });
|
|
649
|
-
else entries.push({ ...entry, ...(entry.role === "thinking" ? { messages: [{ ...entry.message, key: entry.key }] } : {}) });
|
|
650
|
-
}
|
|
651
|
-
// Anchor a progress group to the following message, or to the transcript end.
|
|
652
|
-
// Appending progress or prepending history then preserves its expansion state.
|
|
653
|
-
for (const [index, entry] of entries.entries()) {
|
|
654
|
-
if (entry.role === "thinking") entry.key = `progress:${entries[index + 1]?.key || "tail"}`;
|
|
655
|
-
}
|
|
656
|
-
return entries;
|
|
657
|
-
});
|
|
425
|
+
const { clearDisplayCache, displayEntries } = useTranscriptEntries(props);
|
|
658
426
|
|
|
659
427
|
function handleLongTextLinkClick(payload) {
|
|
660
428
|
emit("link-click", payload);
|
|
@@ -692,971 +460,30 @@ function toggleUserMessage(turn = {}) {
|
|
|
692
460
|
toggleExpandedKey(expandedUserMessages, key);
|
|
693
461
|
}
|
|
694
462
|
|
|
695
|
-
|
|
696
|
-
props.loading &&
|
|
697
|
-
!displayEntries.value.length
|
|
698
|
-
));
|
|
699
|
-
const initialScrollPending = computed(() => Boolean(
|
|
700
|
-
props.visible &&
|
|
701
|
-
displayEntries.value.length &&
|
|
702
|
-
!initialScrollSettled.value
|
|
703
|
-
));
|
|
704
|
-
|
|
705
|
-
function messageScrollKey(message = null) {
|
|
706
|
-
if (!message) {
|
|
707
|
-
return "empty";
|
|
708
|
-
}
|
|
709
|
-
return [
|
|
710
|
-
message.messageId || "",
|
|
711
|
-
message.role || "",
|
|
712
|
-
message.at || "",
|
|
713
|
-
String(message.text || "")
|
|
714
|
-
].join("/");
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
const timelineScrollTrigger = computed(() => [
|
|
718
|
-
loadingIndicatorVisible.value ? "loading" : "ready",
|
|
719
|
-
displayEntries.value.length ? "has-messages" : "empty",
|
|
720
|
-
props.scrollKey
|
|
721
|
-
].join(":"));
|
|
722
|
-
const latestRenderedTailKey = computed(() => {
|
|
723
|
-
const entry = displayEntries.value.at(-1);
|
|
724
|
-
if (!entry) return "";
|
|
725
|
-
return [
|
|
726
|
-
entry.key,
|
|
727
|
-
isWorking.value,
|
|
728
|
-
entry.role === "thinking" ? entry.messages.map(messageScrollKey).join("+") : messageScrollKey(entry.message),
|
|
729
|
-
entry.turn.optimistic?.status || "",
|
|
730
|
-
entry.turn.optimistic?.error || ""
|
|
731
|
-
].join(":");
|
|
732
|
-
});
|
|
733
|
-
const autoScrollEnabled = computed(() => Boolean(
|
|
734
|
-
props.visible &&
|
|
735
|
-
followingLatest.value
|
|
736
|
-
));
|
|
737
|
-
|
|
738
|
-
const {
|
|
739
|
-
clearScheduledScrolls,
|
|
740
|
-
scrollAfterLayout: scrollToLatestMessage,
|
|
741
|
-
scrollNow: scrollToLatestMessageNow
|
|
742
|
-
} = useScrollToBottom({
|
|
743
|
-
anchor: bottomElement,
|
|
744
|
-
enabled: autoScrollEnabled,
|
|
745
|
-
scrollAnchorIntoView: false,
|
|
746
|
-
target: bodyElement
|
|
747
|
-
});
|
|
748
|
-
|
|
749
|
-
function clearUserScrollIntent() {
|
|
750
|
-
if (userScrollIntentTimer && typeof window !== "undefined" && typeof window.clearTimeout === "function") {
|
|
751
|
-
window.clearTimeout(userScrollIntentTimer);
|
|
752
|
-
}
|
|
753
|
-
userScrollIntentTimer = null;
|
|
754
|
-
userScrollIntent.value = false;
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
function resumePendingTailFollow() {
|
|
758
|
-
const shouldResume = pendingTailFollow && followingLatest.value;
|
|
759
|
-
pendingTailFollow = false;
|
|
760
|
-
if (shouldResume) {
|
|
761
|
-
queueLiveBottomScroll();
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
function markUserScrollIntent({ upward = false } = {}) {
|
|
766
|
-
retainedReaderScrollSnapshot = null;
|
|
767
|
-
userScrollIntent.value = true;
|
|
768
|
-
userScrollIntentUpward = upward;
|
|
769
|
-
clearLiveBottomScroll();
|
|
770
|
-
clearScheduledScrolls();
|
|
771
|
-
if (userScrollIntentTimer && typeof window !== "undefined" && typeof window.clearTimeout === "function") {
|
|
772
|
-
window.clearTimeout(userScrollIntentTimer);
|
|
773
|
-
}
|
|
774
|
-
if (typeof window === "undefined" || typeof window.setTimeout !== "function") {
|
|
775
|
-
return;
|
|
776
|
-
}
|
|
777
|
-
userScrollIntentTimer = window.setTimeout(() => {
|
|
778
|
-
userScrollIntentTimer = null;
|
|
779
|
-
userScrollIntent.value = false;
|
|
780
|
-
resumePendingTailFollow();
|
|
781
|
-
}, USER_SCROLL_INTENT_RESET_MS);
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
function markKeyboardScrollIntent(event = {}) {
|
|
785
|
-
if (!KEYBOARD_SCROLL_KEYS.has(String(event.key || "")) || event.defaultPrevented) {
|
|
786
|
-
return;
|
|
787
|
-
}
|
|
788
|
-
const target = event.target;
|
|
789
|
-
const tagName = String(target?.tagName || "").toLowerCase();
|
|
790
|
-
if (
|
|
791
|
-
target?.isContentEditable ||
|
|
792
|
-
["input", "select", "textarea"].includes(tagName) ||
|
|
793
|
-
([" ", "Spacebar"].includes(event.key) && ["a", "button"].includes(tagName))
|
|
794
|
-
) {
|
|
795
|
-
return;
|
|
796
|
-
}
|
|
797
|
-
markUserScrollIntent({
|
|
798
|
-
upward: UPWARD_SCROLL_KEYS.has(event.key) || (event.shiftKey && [" ", "Spacebar"].includes(event.key))
|
|
799
|
-
});
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
function rememberTouch(event = {}) {
|
|
803
|
-
lastTouchY = event.touches?.[0]?.clientY ?? null;
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
function markTouchScrollIntent(event = {}) {
|
|
807
|
-
const touchY = event.touches?.[0]?.clientY ?? null;
|
|
808
|
-
// A finger moving down the screen scrolls the transcript up.
|
|
809
|
-
markUserScrollIntent({ upward: touchY !== null && lastTouchY !== null && touchY > lastTouchY });
|
|
810
|
-
lastTouchY = touchY;
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
function scrollToLatestMessageAfterLayout({
|
|
814
|
-
behavior = "auto",
|
|
815
|
-
force = false
|
|
816
|
-
} = {}) {
|
|
817
|
-
if (force) {
|
|
818
|
-
retainedReaderScrollSnapshot = null;
|
|
819
|
-
followingLatest.value = true;
|
|
820
|
-
clearUserScrollIntent();
|
|
821
|
-
}
|
|
822
|
-
return scrollToLatestMessage({
|
|
823
|
-
behavior
|
|
824
|
-
});
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
function queueInitialBottomScroll() {
|
|
828
|
-
const version = initialScrollVersion + 1;
|
|
829
|
-
initialScrollVersion = version;
|
|
830
|
-
initialScrollSettled.value = false;
|
|
831
|
-
if (props.retainWhenHidden && !props.visible) {
|
|
832
|
-
return;
|
|
833
|
-
}
|
|
834
|
-
void scrollToLatestMessageAfterLayout({
|
|
835
|
-
behavior: "auto",
|
|
836
|
-
force: true
|
|
837
|
-
}).finally(() => {
|
|
838
|
-
if (initialScrollVersion === version) {
|
|
839
|
-
initialScrollSettled.value = true;
|
|
840
|
-
}
|
|
841
|
-
});
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
function queueLiveBottomScroll({
|
|
845
|
-
force = false
|
|
846
|
-
} = {}) {
|
|
847
|
-
if (userScrollIntent.value && !force) {
|
|
848
|
-
pendingTailFollow = true;
|
|
849
|
-
return;
|
|
850
|
-
}
|
|
851
|
-
if (force) {
|
|
852
|
-
retainedReaderScrollSnapshot = null;
|
|
853
|
-
pendingTailFollow = false;
|
|
854
|
-
followingLatest.value = true;
|
|
855
|
-
clearUserScrollIntent();
|
|
856
|
-
}
|
|
857
|
-
if (props.retainWhenHidden && !props.visible) {
|
|
858
|
-
return;
|
|
859
|
-
}
|
|
860
|
-
if (liveScrollFrame) {
|
|
861
|
-
return;
|
|
862
|
-
}
|
|
863
|
-
if (typeof window === "undefined" || typeof window.requestAnimationFrame !== "function") {
|
|
864
|
-
void scrollToLatestMessageAfterLayout({ behavior: "auto" });
|
|
865
|
-
return;
|
|
866
|
-
}
|
|
867
|
-
liveScrollFrame = window.requestAnimationFrame(() => {
|
|
868
|
-
liveScrollFrame = 0;
|
|
869
|
-
scrollToLatestMessageNow({ behavior: "auto" });
|
|
870
|
-
});
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
function clearLiveBottomScroll() {
|
|
874
|
-
if (
|
|
875
|
-
liveScrollFrame &&
|
|
876
|
-
typeof window !== "undefined" &&
|
|
877
|
-
typeof window.cancelAnimationFrame === "function"
|
|
878
|
-
) {
|
|
879
|
-
window.cancelAnimationFrame(liveScrollFrame);
|
|
880
|
-
}
|
|
881
|
-
liveScrollFrame = 0;
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
function updateLatestFollowFromScroll(event = {}) {
|
|
885
|
-
if (props.retainWhenHidden && !props.visible) {
|
|
886
|
-
return;
|
|
887
|
-
}
|
|
888
|
-
const target = event?.currentTarget || bodyElement.value;
|
|
889
|
-
if (retainedReaderScrollSnapshot?.restoring) {
|
|
890
|
-
previousScrollTop = target?.scrollTop || 0;
|
|
891
|
-
previousScrollHeight = target?.scrollHeight || 0;
|
|
892
|
-
return;
|
|
893
|
-
}
|
|
894
|
-
const scrollTop = target?.scrollTop || 0;
|
|
895
|
-
const scrollHeight = target?.scrollHeight || 0;
|
|
896
|
-
const scrolledUp = scrollTop < previousScrollTop;
|
|
897
|
-
// Layout changes such as scroll anchoring move scrollTop together with the
|
|
898
|
-
// content height; moving up over unchanged content is the reader's own.
|
|
899
|
-
const readerMovedUp = scrolledUp && scrollHeight === previousScrollHeight;
|
|
900
|
-
previousScrollTop = scrollTop;
|
|
901
|
-
previousScrollHeight = scrollHeight;
|
|
902
|
-
// Keep following the reader while the request is pending, before rows prepend.
|
|
903
|
-
if (loadMoreScrollSnapshot && !loadMoreScrollSnapshot.completed) {
|
|
904
|
-
loadMoreScrollSnapshot.scrollTop = scrollTop;
|
|
905
|
-
loadMoreScrollSnapshot.anchor = visibleHistoryAnchor(target);
|
|
906
|
-
loadMoreScrollSnapshot.scrollHeight = target.scrollHeight;
|
|
907
|
-
}
|
|
908
|
-
// The reader's own upward scrolling leaves the latest message from its first
|
|
909
|
-
// frame, even within the follow threshold.
|
|
910
|
-
const userScrollsUp = userScrollIntent.value && userScrollIntentUpward;
|
|
911
|
-
const shouldFollow = scrollElementNearBottom(target) &&
|
|
912
|
-
!(userScrollsUp && scrollDistanceFromBottom(target) > 1);
|
|
913
|
-
if (!shouldFollow && !userScrollIntent.value && !readerMovedUp && followingLatest.value) {
|
|
914
|
-
return;
|
|
915
|
-
}
|
|
916
|
-
followingLatest.value = shouldFollow;
|
|
917
|
-
if (shouldFollow) {
|
|
918
|
-
// A scroll event still at the end, queued before upward input, must not
|
|
919
|
-
// end that input's intent.
|
|
920
|
-
if (!userScrollsUp) {
|
|
921
|
-
clearUserScrollIntent();
|
|
922
|
-
}
|
|
923
|
-
resumePendingTailFollow();
|
|
924
|
-
return;
|
|
925
|
-
}
|
|
926
|
-
clearLiveBottomScroll();
|
|
927
|
-
clearScheduledScrolls();
|
|
928
|
-
if (
|
|
929
|
-
scrolledUp &&
|
|
930
|
-
initialScrollSettled.value &&
|
|
931
|
-
scrollTop <= LOAD_MORE_THRESHOLD_PX &&
|
|
932
|
-
!props.loadMoreError
|
|
933
|
-
) {
|
|
934
|
-
requestLoadMore();
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
function visibleHistoryAnchor(element) {
|
|
939
|
-
const top = element.getBoundingClientRect().top;
|
|
940
|
-
for (const turn of element.querySelectorAll(".assistant-transcript__turn")) {
|
|
941
|
-
const bounds = turn.getBoundingClientRect();
|
|
942
|
-
if (bounds.bottom > top) {
|
|
943
|
-
return { element: turn, offset: bounds.top - top };
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
return null;
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
// Offscreen turns use estimated heights. Preserve the visible anchor row instead
|
|
950
|
-
// of moving by the total height difference, which can change during layout.
|
|
951
|
-
function restoreAnchoredScroll(element, snapshot) {
|
|
952
|
-
const anchor = snapshot.anchor;
|
|
953
|
-
if (anchor && element.contains(anchor.element)) {
|
|
954
|
-
// Materialize the anchor row before reading the browser's adjusted scrollTop.
|
|
955
|
-
const offset = anchor.element.getBoundingClientRect().top - element.getBoundingClientRect().top;
|
|
956
|
-
previousScrollTop = element.scrollTop + offset - anchor.offset;
|
|
957
|
-
} else {
|
|
958
|
-
previousScrollTop = snapshot.scrollTop + (element.scrollHeight - snapshot.scrollHeight);
|
|
959
|
-
}
|
|
960
|
-
element.scrollTop = previousScrollTop;
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
// Reopening materializes previously hidden content-visibility estimates.
|
|
964
|
-
async function waitForTwoFrames() {
|
|
965
|
-
if (typeof window !== "undefined" && typeof window.requestAnimationFrame === "function") {
|
|
966
|
-
await new Promise(resolve => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve)));
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
async function restoreRetainedReaderScroll() {
|
|
971
|
-
const snapshot = retainedReaderScrollSnapshot;
|
|
972
|
-
snapshot.restoring = true;
|
|
973
|
-
await nextTick();
|
|
974
|
-
for (let pass = 0; pass < 2; pass += 1) {
|
|
975
|
-
const element = bodyElement.value;
|
|
976
|
-
if (
|
|
977
|
-
retainedReaderScrollSnapshot !== snapshot ||
|
|
978
|
-
element !== snapshot.body ||
|
|
979
|
-
snapshot.scrollKey !== props.scrollKey ||
|
|
980
|
-
!props.retainWhenHidden ||
|
|
981
|
-
!props.visible ||
|
|
982
|
-
followingLatest.value ||
|
|
983
|
-
loadMoreScrollSnapshot
|
|
984
|
-
) {
|
|
985
|
-
if (retainedReaderScrollSnapshot === snapshot) retainedReaderScrollSnapshot = null;
|
|
986
|
-
return;
|
|
987
|
-
}
|
|
988
|
-
restoreAnchoredScroll(element, snapshot);
|
|
989
|
-
if (pass === 0) await waitForTwoFrames();
|
|
990
|
-
}
|
|
991
|
-
if (retainedReaderScrollSnapshot === snapshot) retainedReaderScrollSnapshot = null;
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
function requestLoadMore() {
|
|
995
|
-
if ((props.retainWhenHidden && !props.visible) || !props.hasMoreBefore || props.loadingMore || loadMoreScrollSnapshot) {
|
|
996
|
-
return;
|
|
997
|
-
}
|
|
998
|
-
followingLatest.value = false;
|
|
999
|
-
pendingTailFollow = false;
|
|
1000
|
-
markUserScrollIntent();
|
|
1001
|
-
const element = bodyElement.value;
|
|
1002
|
-
const version = loadMoreRequestVersion + 1;
|
|
1003
|
-
loadMoreRequestVersion = version;
|
|
1004
|
-
loadMoreScrollSnapshot = element
|
|
1005
|
-
? {
|
|
1006
|
-
anchor: visibleHistoryAnchor(element),
|
|
1007
|
-
scrollHeight: element.scrollHeight,
|
|
1008
|
-
scrollKey: props.scrollKey,
|
|
1009
|
-
scrollTop: element.scrollTop,
|
|
1010
|
-
version
|
|
1011
|
-
}
|
|
1012
|
-
: null;
|
|
1013
|
-
emit("load-more", {
|
|
1014
|
-
complete: ({ changed = false } = {}) => {
|
|
1015
|
-
void completeLoadMoreRequest(version, changed);
|
|
1016
|
-
}
|
|
1017
|
-
});
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
function clearLoadMoreScrollSnapshot() {
|
|
1021
|
-
loadMoreRequestVersion += 1;
|
|
1022
|
-
loadMoreScrollSnapshot = null;
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
async function completeLoadMoreRequest(version, changed) {
|
|
1026
|
-
const snapshot = loadMoreScrollSnapshot;
|
|
1027
|
-
if (!snapshot || snapshot.version !== version) {
|
|
1028
|
-
return;
|
|
1029
|
-
}
|
|
1030
|
-
if (!changed || snapshot.scrollKey !== props.scrollKey) {
|
|
1031
|
-
clearLoadMoreScrollSnapshot();
|
|
1032
|
-
return;
|
|
1033
|
-
}
|
|
1034
|
-
snapshot.completed = true;
|
|
1035
|
-
await nextTick();
|
|
1036
|
-
if (loadMoreScrollSnapshot?.version !== version) {
|
|
1037
|
-
return;
|
|
1038
|
-
}
|
|
1039
|
-
const passes = props.retainWhenHidden ? 2 : 1;
|
|
1040
|
-
for (let pass = 0; pass < passes; pass += 1) {
|
|
1041
|
-
if (
|
|
1042
|
-
(props.retainWhenHidden && !props.visible) ||
|
|
1043
|
-
loadMoreScrollSnapshot?.version !== version ||
|
|
1044
|
-
snapshot.scrollKey !== props.scrollKey
|
|
1045
|
-
) {
|
|
1046
|
-
return;
|
|
1047
|
-
}
|
|
1048
|
-
const element = bodyElement.value;
|
|
1049
|
-
if (element) restoreAnchoredScroll(element, snapshot);
|
|
1050
|
-
if (pass + 1 < passes) await waitForTwoFrames();
|
|
1051
|
-
}
|
|
1052
|
-
clearLoadMoreScrollSnapshot();
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
onBeforeUnmount(() => {
|
|
1056
|
-
retainedReaderScrollSnapshot = null;
|
|
1057
|
-
clearLiveBottomScroll();
|
|
1058
|
-
clearUserScrollIntent();
|
|
1059
|
-
clearLoadMoreScrollSnapshot();
|
|
1060
|
-
});
|
|
1061
|
-
|
|
463
|
+
// Created before the scroll-following watchers, so a new conversation's expansion and cache reset run first.
|
|
1062
464
|
watch(() => props.scrollKey, () => {
|
|
1063
465
|
expandedUserMessages.value = new Set();
|
|
1064
|
-
|
|
466
|
+
clearDisplayCache();
|
|
1065
467
|
});
|
|
1066
468
|
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
watch(() => [
|
|
1085
|
-
props.scrollKey,
|
|
1086
|
-
props.followLatestKey
|
|
1087
|
-
], ([scrollKey, value], [previousScrollKey, previous] = []) => {
|
|
1088
|
-
if (scrollKey !== previousScrollKey || value === previous) {
|
|
1089
|
-
return;
|
|
1090
|
-
}
|
|
1091
|
-
queueLiveBottomScroll({
|
|
1092
|
-
force: true
|
|
1093
|
-
});
|
|
1094
|
-
}, {
|
|
1095
|
-
flush: "post"
|
|
1096
|
-
});
|
|
1097
|
-
|
|
1098
|
-
watch(timelineScrollTrigger, () => {
|
|
1099
|
-
retainedReaderScrollSnapshot = null;
|
|
1100
|
-
clearLoadMoreScrollSnapshot();
|
|
1101
|
-
queueInitialBottomScroll();
|
|
1102
|
-
}, {
|
|
1103
|
-
flush: "post",
|
|
1104
|
-
immediate: true
|
|
469
|
+
const {
|
|
470
|
+
initialScrollPending,
|
|
471
|
+
loadingIndicatorVisible,
|
|
472
|
+
markKeyboardScrollIntent,
|
|
473
|
+
markTouchScrollIntent,
|
|
474
|
+
markUserScrollIntent,
|
|
475
|
+
rememberTouch,
|
|
476
|
+
requestLoadMore,
|
|
477
|
+
updateLatestFollowFromScroll
|
|
478
|
+
} = useTranscriptScrollFollow({
|
|
479
|
+
props,
|
|
480
|
+
displayEntries,
|
|
481
|
+
isWorking,
|
|
482
|
+
bodyElement,
|
|
483
|
+
bottomElement,
|
|
484
|
+
loadMore: (request) => emit("load-more", request)
|
|
1105
485
|
});
|
|
1106
486
|
|
|
1107
|
-
watch(() => props.error, () => {
|
|
1108
|
-
if (followingLatest.value) queueLiveBottomScroll();
|
|
1109
|
-
}, { flush: "post" });
|
|
1110
|
-
|
|
1111
|
-
// Capture before this transcript or its parent's v-show removes its geometry.
|
|
1112
|
-
watch(() => props.visible, (visible) => {
|
|
1113
|
-
if (visible) return;
|
|
1114
|
-
retainedReaderScrollSnapshot = null;
|
|
1115
|
-
const element = bodyElement.value;
|
|
1116
|
-
if (
|
|
1117
|
-
props.retainWhenHidden &&
|
|
1118
|
-
initialScrollSettled.value &&
|
|
1119
|
-
!followingLatest.value &&
|
|
1120
|
-
!loadMoreScrollSnapshot &&
|
|
1121
|
-
element?.clientHeight > 0
|
|
1122
|
-
) {
|
|
1123
|
-
retainedReaderScrollSnapshot = {
|
|
1124
|
-
anchor: visibleHistoryAnchor(element),
|
|
1125
|
-
body: element,
|
|
1126
|
-
scrollHeight: element.scrollHeight,
|
|
1127
|
-
scrollKey: props.scrollKey,
|
|
1128
|
-
scrollTop: element.scrollTop
|
|
1129
|
-
};
|
|
1130
|
-
}
|
|
1131
|
-
}, { flush: "sync" });
|
|
1132
|
-
|
|
1133
|
-
watch(() => props.visible, (visible) => {
|
|
1134
|
-
if (props.retainWhenHidden) {
|
|
1135
|
-
if (!visible) {
|
|
1136
|
-
clearLiveBottomScroll();
|
|
1137
|
-
clearScheduledScrolls();
|
|
1138
|
-
clearUserScrollIntent();
|
|
1139
|
-
initialScrollVersion += 1;
|
|
1140
|
-
} else if (loadMoreScrollSnapshot?.completed) {
|
|
1141
|
-
void completeLoadMoreRequest(loadMoreScrollSnapshot.version, true);
|
|
1142
|
-
} else if (!initialScrollSettled.value) {
|
|
1143
|
-
queueInitialBottomScroll();
|
|
1144
|
-
} else if (retainedReaderScrollSnapshot) {
|
|
1145
|
-
void restoreRetainedReaderScroll();
|
|
1146
|
-
} else {
|
|
1147
|
-
queueLiveBottomScroll();
|
|
1148
|
-
}
|
|
1149
|
-
return;
|
|
1150
|
-
}
|
|
1151
|
-
clearLoadMoreScrollSnapshot();
|
|
1152
|
-
if (visible) queueLiveBottomScroll({ force: true });
|
|
1153
|
-
}, { flush: "post" });
|
|
1154
487
|
</script>
|
|
1155
488
|
|
|
1156
|
-
<style scoped>
|
|
1157
|
-
.assistant-transcript__plain-user { white-space: pre-wrap; margin: 0; }
|
|
1158
|
-
.assistant-transcript {
|
|
1159
|
-
border: 1px solid rgba(var(--v-theme-outline), 0.24);
|
|
1160
|
-
border-radius: 8px;
|
|
1161
|
-
display: grid;
|
|
1162
|
-
gap: 0.35rem;
|
|
1163
|
-
grid-template-rows: minmax(0, 1fr);
|
|
1164
|
-
min-height: 0;
|
|
1165
|
-
overflow: hidden;
|
|
1166
|
-
padding: 0.5rem;
|
|
1167
|
-
position: relative;
|
|
1168
|
-
text-align: left;
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
.assistant-transcript__loading-skeleton {
|
|
1172
|
-
inset: 0;
|
|
1173
|
-
overflow: hidden;
|
|
1174
|
-
position: absolute;
|
|
1175
|
-
z-index: 1;
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
.assistant-transcript--error {
|
|
1179
|
-
grid-template-rows: max-content minmax(0, 1fr);
|
|
1180
|
-
min-height: min-content;
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
.assistant-transcript--error .assistant-transcript__body {
|
|
1184
|
-
contain: size;
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
|
-
.assistant-transcript__error {
|
|
1188
|
-
position: relative;
|
|
1189
|
-
z-index: 4;
|
|
1190
|
-
background: rgb(var(--v-theme-surface));
|
|
1191
|
-
padding: 0.25rem 0.5rem;
|
|
1192
|
-
font-size: 0.85rem;
|
|
1193
|
-
min-width: 0;
|
|
1194
|
-
overflow-wrap: anywhere;
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
.assistant-transcript__error-content {
|
|
1198
|
-
align-items: center;
|
|
1199
|
-
display: flex;
|
|
1200
|
-
gap: 0.5rem;
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
.assistant-transcript__error-content > span {
|
|
1204
|
-
flex: 1 1 auto;
|
|
1205
|
-
min-width: 0;
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
.assistant-transcript__error-content > .v-btn {
|
|
1209
|
-
flex: 0 0 auto;
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
.assistant-transcript__settling {
|
|
1213
|
-
background: rgb(var(--v-theme-surface));
|
|
1214
|
-
inset: 0;
|
|
1215
|
-
overflow: hidden;
|
|
1216
|
-
padding: 0.5rem;
|
|
1217
|
-
position: absolute;
|
|
1218
|
-
z-index: 1;
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
.assistant-transcript__settling-skeleton {
|
|
1222
|
-
height: 100%;
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
.assistant-transcript__reload {
|
|
1226
|
-
color: rgba(var(--v-theme-on-surface), 0.66);
|
|
1227
|
-
position: absolute;
|
|
1228
|
-
right: 0.38rem;
|
|
1229
|
-
top: 0.38rem;
|
|
1230
|
-
z-index: 2;
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
|
-
.assistant-transcript__body {
|
|
1234
|
-
display: flex;
|
|
1235
|
-
flex-direction: column;
|
|
1236
|
-
gap: 0.65rem;
|
|
1237
|
-
min-height: 0;
|
|
1238
|
-
min-width: 0;
|
|
1239
|
-
overflow-x: hidden;
|
|
1240
|
-
overflow-y: auto;
|
|
1241
|
-
padding-right: 0.15rem;
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
.assistant-transcript__welcome {
|
|
1245
|
-
background: rgba(var(--v-theme-primary), 0.08);
|
|
1246
|
-
border: 1px solid rgba(var(--v-theme-primary), 0.18);
|
|
1247
|
-
border-radius: 16px;
|
|
1248
|
-
color: rgb(var(--v-theme-on-surface));
|
|
1249
|
-
display: grid;
|
|
1250
|
-
flex: 0 0 auto;
|
|
1251
|
-
gap: 0.55rem;
|
|
1252
|
-
margin: 0.1rem 0.15rem 0;
|
|
1253
|
-
padding: 0.8rem 0.9rem 0.9rem;
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
.assistant-transcript__welcome p {
|
|
1257
|
-
line-height: 1.48;
|
|
1258
|
-
margin: 0;
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
.assistant-transcript__body--settling {
|
|
1262
|
-
visibility: hidden;
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
.assistant-transcript__load-more {
|
|
1266
|
-
align-items: center;
|
|
1267
|
-
display: flex;
|
|
1268
|
-
flex: 0 0 auto;
|
|
1269
|
-
flex-direction: column;
|
|
1270
|
-
gap: 0.35rem;
|
|
1271
|
-
padding: 0.35rem 0 0.55rem;
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
.assistant-transcript__load-more-error {
|
|
1275
|
-
color: rgb(var(--v-theme-error));
|
|
1276
|
-
font-size: 0.76rem;
|
|
1277
|
-
line-height: 1.3;
|
|
1278
|
-
text-align: center;
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
.assistant-transcript__load-more + .assistant-transcript__turn,
|
|
1282
|
-
.assistant-transcript__body > .assistant-transcript__turn:first-child {
|
|
1283
|
-
margin-top: auto;
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
.assistant-transcript__turn {
|
|
1287
|
-
contain-intrinsic-block-size: auto 12rem;
|
|
1288
|
-
content-visibility: auto;
|
|
1289
|
-
display: flex;
|
|
1290
|
-
flex: 0 0 auto;
|
|
1291
|
-
flex-direction: column;
|
|
1292
|
-
gap: 0.65rem;
|
|
1293
|
-
min-height: 0;
|
|
1294
|
-
min-width: 0;
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
.assistant-transcript--retained .assistant-transcript__turn {
|
|
1298
|
-
/* Late offscreen height estimates cannot move a restored reader anchor. */
|
|
1299
|
-
content-visibility: visible;
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
.assistant-transcript__message-row {
|
|
1303
|
-
align-items: start;
|
|
1304
|
-
display: grid;
|
|
1305
|
-
gap: 0.65rem;
|
|
1306
|
-
max-width: 100%;
|
|
1307
|
-
min-width: 0;
|
|
1308
|
-
overflow-x: hidden;
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
.assistant-transcript__message-row--user {
|
|
1312
|
-
grid-template-columns: minmax(0, auto) auto;
|
|
1313
|
-
justify-self: end;
|
|
1314
|
-
max-width: min(28rem, 88%);
|
|
1315
|
-
margin-left: auto;
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
.assistant-transcript__message-row--assistant {
|
|
1319
|
-
display: flex;
|
|
1320
|
-
flex-direction: column;
|
|
1321
|
-
gap: 0.28rem;
|
|
1322
|
-
justify-self: start;
|
|
1323
|
-
max-width: min(52rem, 94%);
|
|
1324
|
-
margin-right: auto;
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
|
-
.assistant-transcript__assistant-header {
|
|
1328
|
-
align-items: center;
|
|
1329
|
-
display: grid;
|
|
1330
|
-
gap: 0.65rem;
|
|
1331
|
-
grid-template-columns: auto minmax(0, 1fr);
|
|
1332
|
-
min-width: 0;
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
.assistant-transcript__message {
|
|
1336
|
-
display: flex;
|
|
1337
|
-
flex-direction: column;
|
|
1338
|
-
gap: 0.24rem;
|
|
1339
|
-
max-width: 100%;
|
|
1340
|
-
min-width: 0;
|
|
1341
|
-
overflow-wrap: anywhere;
|
|
1342
|
-
word-break: break-word;
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
.assistant-transcript__avatar {
|
|
1346
|
-
align-items: center;
|
|
1347
|
-
border-radius: 999px;
|
|
1348
|
-
display: inline-flex;
|
|
1349
|
-
height: 1.5rem;
|
|
1350
|
-
justify-content: center;
|
|
1351
|
-
width: 1.5rem;
|
|
1352
|
-
}
|
|
1353
|
-
|
|
1354
|
-
.assistant-transcript__avatar--user {
|
|
1355
|
-
background: rgba(var(--v-theme-primary), 0.1);
|
|
1356
|
-
color: rgb(var(--v-theme-primary));
|
|
1357
|
-
margin-top: 0.2rem;
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
|
-
.assistant-transcript__avatar--assistant {
|
|
1361
|
-
background: rgb(var(--v-theme-primary));
|
|
1362
|
-
color: rgb(var(--v-theme-on-primary));
|
|
1363
|
-
margin-top: 0.05rem;
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1366
|
-
.assistant-transcript__message--user {
|
|
1367
|
-
background: rgba(var(--v-theme-primary), 0.1);
|
|
1368
|
-
border-radius: 16px;
|
|
1369
|
-
color: rgb(var(--v-theme-on-surface));
|
|
1370
|
-
gap: 0.75rem;
|
|
1371
|
-
justify-content: space-between;
|
|
1372
|
-
overflow-x: auto;
|
|
1373
|
-
padding: 0.75rem 1rem 0.72rem;
|
|
1374
|
-
width: fit-content;
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
.assistant-transcript__message--user.assistant-transcript__message--pending {
|
|
1378
|
-
background: rgba(var(--v-theme-on-surface), 0.04);
|
|
1379
|
-
outline: 1px dashed rgba(var(--v-theme-on-surface), 0.4);
|
|
1380
|
-
outline-offset: -1px;
|
|
1381
|
-
}
|
|
1382
|
-
|
|
1383
|
-
.assistant-transcript__message--pending .assistant-transcript__message-footer {
|
|
1384
|
-
color: rgba(var(--v-theme-on-surface), 0.7);
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
.assistant-transcript__message--user.assistant-transcript__message--failed {
|
|
1388
|
-
outline: 1px solid rgb(var(--v-theme-error));
|
|
1389
|
-
outline-offset: -1px;
|
|
1390
|
-
}
|
|
1391
|
-
|
|
1392
|
-
.assistant-transcript__message--assistant {
|
|
1393
|
-
background: rgb(var(--v-theme-surface));
|
|
1394
|
-
color: rgb(var(--v-theme-on-surface));
|
|
1395
|
-
padding: 0;
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
|
-
.assistant-transcript--task {
|
|
1399
|
-
border-color: rgba(126, 87, 194, 0.32);
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
|
-
.assistant-transcript--task .assistant-transcript__avatar--assistant {
|
|
1403
|
-
background: #7e57c2;
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
.assistant-transcript--task .assistant-transcript__message--user {
|
|
1407
|
-
background: #eee8f8;
|
|
1408
|
-
color: #2d2440;
|
|
1409
|
-
}
|
|
1410
|
-
|
|
1411
|
-
.assistant-transcript__thinking {
|
|
1412
|
-
justify-self: start;
|
|
1413
|
-
margin-left: 2.15rem;
|
|
1414
|
-
max-width: min(34rem, 86%);
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
|
-
.assistant-transcript__user-content-toggle {
|
|
1418
|
-
background: transparent;
|
|
1419
|
-
border: 0;
|
|
1420
|
-
color: rgb(var(--v-theme-primary));
|
|
1421
|
-
cursor: pointer;
|
|
1422
|
-
font: inherit;
|
|
1423
|
-
text-align: left;
|
|
1424
|
-
}
|
|
1425
|
-
|
|
1426
|
-
.assistant-transcript__system {
|
|
1427
|
-
align-items: start;
|
|
1428
|
-
background: transparent;
|
|
1429
|
-
color: rgba(var(--v-theme-on-surface), 0.84);
|
|
1430
|
-
display: grid;
|
|
1431
|
-
gap: 0.45rem;
|
|
1432
|
-
grid-template-columns: auto minmax(0, 1fr);
|
|
1433
|
-
justify-self: start;
|
|
1434
|
-
max-width: min(34rem, 96%);
|
|
1435
|
-
min-width: 0;
|
|
1436
|
-
padding: 0.1rem 0.15rem;
|
|
1437
|
-
}
|
|
1438
|
-
|
|
1439
|
-
.assistant-transcript__system-icon {
|
|
1440
|
-
color: rgba(var(--v-theme-primary), 0.82);
|
|
1441
|
-
margin-top: 0.15rem;
|
|
1442
|
-
}
|
|
1443
|
-
|
|
1444
|
-
.assistant-transcript__system-body {
|
|
1445
|
-
display: grid;
|
|
1446
|
-
gap: 0.1rem;
|
|
1447
|
-
min-width: 0;
|
|
1448
|
-
overflow-wrap: anywhere;
|
|
1449
|
-
}
|
|
1450
|
-
|
|
1451
|
-
.assistant-transcript__system-meta {
|
|
1452
|
-
align-items: center;
|
|
1453
|
-
color: rgba(var(--v-theme-on-surface), 0.48);
|
|
1454
|
-
display: flex;
|
|
1455
|
-
font-size: 0.72rem;
|
|
1456
|
-
gap: 0.5rem;
|
|
1457
|
-
line-height: 1.15;
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1460
|
-
.assistant-transcript__message-header {
|
|
1461
|
-
align-items: center;
|
|
1462
|
-
color: rgba(var(--v-theme-on-surface), 0.82);
|
|
1463
|
-
display: flex;
|
|
1464
|
-
font-size: 0.9rem;
|
|
1465
|
-
gap: 0.7rem;
|
|
1466
|
-
justify-content: space-between;
|
|
1467
|
-
line-height: 1.2;
|
|
1468
|
-
}
|
|
1469
|
-
|
|
1470
|
-
.assistant-transcript__message--assistant .assistant-transcript__message-header {
|
|
1471
|
-
color: rgba(var(--v-theme-on-surface), 0.78);
|
|
1472
|
-
font-weight: 560;
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
.assistant-transcript__message-header span {
|
|
1476
|
-
align-items: center;
|
|
1477
|
-
display: flex;
|
|
1478
|
-
gap: 0.25rem;
|
|
1479
|
-
min-width: 0;
|
|
1480
|
-
overflow-wrap: anywhere;
|
|
1481
|
-
}
|
|
1482
|
-
|
|
1483
|
-
.assistant-transcript__message-header time {
|
|
1484
|
-
color: #6d7888;
|
|
1485
|
-
font-weight: 650;
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
|
-
.assistant-transcript__message-footer {
|
|
1489
|
-
align-items: center;
|
|
1490
|
-
color: #9aa6b6;
|
|
1491
|
-
display: flex;
|
|
1492
|
-
font-size: 0.88rem;
|
|
1493
|
-
font-weight: 500;
|
|
1494
|
-
gap: 0.65rem;
|
|
1495
|
-
line-height: 1.2;
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
.assistant-transcript__message-footer--user {
|
|
1499
|
-
justify-content: flex-start;
|
|
1500
|
-
}
|
|
1501
|
-
|
|
1502
|
-
.assistant-transcript__message-footer--assistant {
|
|
1503
|
-
justify-content: flex-start;
|
|
1504
|
-
margin-top: -0.15rem;
|
|
1505
|
-
}
|
|
1506
|
-
|
|
1507
|
-
.assistant-transcript__optimistic-failure {
|
|
1508
|
-
align-items: center;
|
|
1509
|
-
color: rgba(var(--v-theme-error), 0.92);
|
|
1510
|
-
display: flex;
|
|
1511
|
-
flex-wrap: wrap;
|
|
1512
|
-
font-size: 0.76rem;
|
|
1513
|
-
gap: 0.35rem 0.55rem;
|
|
1514
|
-
line-height: 1.25;
|
|
1515
|
-
}
|
|
1516
|
-
|
|
1517
|
-
.assistant-transcript__optimistic-actions {
|
|
1518
|
-
align-items: center;
|
|
1519
|
-
display: inline-flex;
|
|
1520
|
-
gap: 0.25rem;
|
|
1521
|
-
}
|
|
1522
|
-
|
|
1523
|
-
@media (max-width: 600px), (pointer: coarse) {
|
|
1524
|
-
.assistant-transcript__delivery-action {
|
|
1525
|
-
min-height: 3rem;
|
|
1526
|
-
min-width: 3rem;
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
|
|
1530
|
-
.assistant-transcript__message--assistant :deep(.studio-long-text-review__blocks),
|
|
1531
|
-
.assistant-transcript__message--user :deep(.studio-long-text-review__blocks) {
|
|
1532
|
-
color: inherit;
|
|
1533
|
-
font-size: 0.94rem;
|
|
1534
|
-
line-height: 1.5;
|
|
1535
|
-
min-width: 0;
|
|
1536
|
-
overflow-wrap: anywhere;
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
|
-
.assistant-transcript__message--assistant :deep(.studio-long-text-review__paragraph),
|
|
1540
|
-
.assistant-transcript__message--user :deep(.studio-long-text-review__paragraph) {
|
|
1541
|
-
font-size: 0.94rem;
|
|
1542
|
-
margin-block: 0;
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1545
|
-
.assistant-transcript__message--user :deep(.studio-long-text-review__paragraph) {
|
|
1546
|
-
white-space: pre-wrap;
|
|
1547
|
-
}
|
|
1548
|
-
|
|
1549
|
-
.assistant-transcript__user-content {
|
|
1550
|
-
min-width: 0;
|
|
1551
|
-
}
|
|
1552
|
-
|
|
1553
|
-
.assistant-transcript__user-content-toggle {
|
|
1554
|
-
align-self: flex-start;
|
|
1555
|
-
font-size: 0.78rem;
|
|
1556
|
-
font-weight: 650;
|
|
1557
|
-
padding: 0;
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
.assistant-transcript__user-content-toggle:hover,
|
|
1561
|
-
.assistant-transcript__user-content-toggle:focus-visible {
|
|
1562
|
-
text-decoration: underline;
|
|
1563
|
-
}
|
|
1564
|
-
|
|
1565
|
-
.assistant-transcript__message--assistant :deep(.studio-long-text-review__paragraph code),
|
|
1566
|
-
.assistant-transcript__message--assistant :deep(.studio-long-text-review__list li > code),
|
|
1567
|
-
.assistant-transcript__message--assistant :deep(.studio-long-text-review__table th > code),
|
|
1568
|
-
.assistant-transcript__message--assistant :deep(.studio-long-text-review__table td > code),
|
|
1569
|
-
.assistant-transcript__message--assistant :deep(.studio-long-text-review__details-summary code),
|
|
1570
|
-
.assistant-transcript__message--user :deep(.studio-long-text-review__paragraph code),
|
|
1571
|
-
.assistant-transcript__message--user :deep(.studio-long-text-review__list li > code),
|
|
1572
|
-
.assistant-transcript__message--user :deep(.studio-long-text-review__table th > code),
|
|
1573
|
-
.assistant-transcript__message--user :deep(.studio-long-text-review__table td > code),
|
|
1574
|
-
.assistant-transcript__message--user :deep(.studio-long-text-review__details-summary code) {
|
|
1575
|
-
background: rgba(var(--v-theme-primary), 0.07);
|
|
1576
|
-
border: 1px solid rgba(var(--v-theme-primary), 0.14);
|
|
1577
|
-
border-radius: 3px;
|
|
1578
|
-
color: rgba(var(--v-theme-on-surface), 0.9);
|
|
1579
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
|
1580
|
-
font-size: 0.9em;
|
|
1581
|
-
padding: 0.02rem 0.2rem;
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
.assistant-transcript__questions {
|
|
1585
|
-
box-sizing: border-box;
|
|
1586
|
-
display: grid;
|
|
1587
|
-
gap: 0.2rem;
|
|
1588
|
-
list-style: none;
|
|
1589
|
-
margin: 0;
|
|
1590
|
-
max-width: 100%;
|
|
1591
|
-
min-width: 0;
|
|
1592
|
-
padding: 0;
|
|
1593
|
-
}
|
|
1594
|
-
|
|
1595
|
-
.assistant-transcript__question {
|
|
1596
|
-
align-items: start;
|
|
1597
|
-
background: rgba(var(--v-theme-surface), 0.62);
|
|
1598
|
-
border: 1px solid rgba(var(--v-theme-outline), 0.2);
|
|
1599
|
-
border-radius: 8px;
|
|
1600
|
-
box-sizing: border-box;
|
|
1601
|
-
display: grid;
|
|
1602
|
-
gap: 0.32rem;
|
|
1603
|
-
grid-template-columns: auto minmax(0, 1fr);
|
|
1604
|
-
max-width: 100%;
|
|
1605
|
-
min-width: 0;
|
|
1606
|
-
padding: 0.28rem 0.42rem;
|
|
1607
|
-
}
|
|
1608
|
-
|
|
1609
|
-
.assistant-transcript__question-number {
|
|
1610
|
-
align-items: center;
|
|
1611
|
-
background: rgba(var(--v-theme-primary), 0.1);
|
|
1612
|
-
border: 1px solid rgba(var(--v-theme-primary), 0.2);
|
|
1613
|
-
border-radius: 999px;
|
|
1614
|
-
color: rgb(var(--v-theme-primary));
|
|
1615
|
-
display: inline-flex;
|
|
1616
|
-
font-size: 0.72rem;
|
|
1617
|
-
font-weight: 760;
|
|
1618
|
-
height: 1.35rem;
|
|
1619
|
-
justify-content: center;
|
|
1620
|
-
line-height: 1;
|
|
1621
|
-
min-width: 1.35rem;
|
|
1622
|
-
}
|
|
1623
|
-
|
|
1624
|
-
.assistant-transcript__question-text {
|
|
1625
|
-
color: rgb(var(--v-theme-on-surface));
|
|
1626
|
-
font-size: 0.9rem;
|
|
1627
|
-
line-height: 1.35;
|
|
1628
|
-
max-width: 100%;
|
|
1629
|
-
min-width: 0;
|
|
1630
|
-
overflow-wrap: anywhere;
|
|
1631
|
-
}
|
|
1632
|
-
|
|
1633
|
-
.assistant-transcript__question-content {
|
|
1634
|
-
display: block;
|
|
1635
|
-
min-width: 0;
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
|
-
.assistant-transcript__question-choices {
|
|
1639
|
-
color: rgba(var(--v-theme-on-surface), 0.72);
|
|
1640
|
-
display: inline;
|
|
1641
|
-
font-size: 0.8rem;
|
|
1642
|
-
margin: 0;
|
|
1643
|
-
padding: 0;
|
|
1644
|
-
}
|
|
1645
|
-
|
|
1646
|
-
.assistant-transcript__question-choices::before {
|
|
1647
|
-
content: " · ";
|
|
1648
|
-
}
|
|
1649
|
-
|
|
1650
|
-
.assistant-transcript__question-choices li {
|
|
1651
|
-
display: inline;
|
|
1652
|
-
overflow-wrap: anywhere;
|
|
1653
|
-
}
|
|
1654
|
-
|
|
1655
|
-
.assistant-transcript__question-choices li + li::before {
|
|
1656
|
-
content: " · ";
|
|
1657
|
-
}
|
|
1658
|
-
|
|
1659
|
-
.assistant-transcript__bottom {
|
|
1660
|
-
height: 1px;
|
|
1661
|
-
}
|
|
1662
|
-
</style>
|
|
489
|
+
<style scoped src="./AssistantTranscript.css"></style>
|