import { RefreshIcon } from "~/components/ui/refresh-icon"; import { questionAttachmentDraftId, useQuestionAttachmentPreparation, changeQuestionAttachmentPreparation, } from "../../questionAttachments"; import type { ApprovalRequestId, AssistantCitation, ChatFileAttachment, EnvironmentId, ModelSelection, PreviewAnnotationPayload, ProviderApprovalDecision, ProviderInteractionMode, ResolvedKeybindingsConfig, RuntimeMode, ScopedThreadRef, ServerProvider, ThreadId, SnapShotSource, } from "@t3tools/contracts"; import { ProviderDriverKind, ProviderInstanceId, PROVIDER_SEND_TURN_MAX_ATTACHMENTS, PROVIDER_SEND_TURN_MAX_IMAGE_BYTES, } from "@t3tools/contracts"; import type { EnvironmentConnectionPresentation } from "@t3tools/client-runtime/connection"; import { serializeComposerFileLink } from "@t3tools/shared/composerTrigger"; import { createModelSelection, normalizeModelSlug } from "@t3tools/shared/model"; import { USAGE_LIMITS_COMMAND } from "@t3tools/shared/usageLimits"; import { Fragment, memo, type ComponentProps, type ReactNode, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore, } from "react"; import { createPortal } from "react-dom"; import { clampCollapsedComposerCursor, type ComposerSubmissionIntent, type ComposerTrigger, collapseExpandedComposerCursor, composerSubmissionIntentForEnter, detectComposerTrigger, expandCollapsedComposerCursor, formatAssistantCitationForComposer, replaceTextRange, } from "../../composer-logic"; import { DISCONNECTED_COMPOSER_PLACEHOLDER } from "../../composerPlaceholder"; import { deriveComposerSendState, getAntigravitySendBlockReason, readFileAsDataUrl, resolveComposerInteractionMode, resolveComposerProviderSelection, threadShellHasStarted, } from "../ChatView.logic"; import { dataTransferHasComposerMention, makeComposerMentionDragHandlers, } from "./composerMentionDrag"; import { composerFloatingLayerProps, isInsideCollapsedComposerControls, isInsideComposerFloatingLayer, isInsideRestingComposerControlScope, } from "./composerEventScope"; import { type ComposerFileAttachment, type ComposerImageAttachment, type DraftId, type PersistedComposerFileAttachment, type PersistedComposerImageAttachment, composerFileDedupKey, composerFileMatchesReattachMarker, composerFileNeedsReattach, composerTargetKey, hydrateImagesFromPersisted, useComposerDraftStore, useComposerThreadDraft, useEffectiveComposerModelState, } from "../../composerDraftStore"; import { MAX_STASH_ENTRIES, partitionStashAttachments, usePromptStashStore, type PromptStashEntry, } from "../../promptStashStore"; import { ComposerStashBadge } from "./ComposerStashBadge"; import { ComposerStashMenu } from "./ComposerStashMenu"; import { useComposerMenuState } from "./useComposerMenuState"; import { useComposerFocusState } from "./useComposerFocusState"; import { useComposerMultilinePrompt } from "./useComposerMultilinePrompt"; import { ComposerTasksBadge, ComposerTasksContent, ComposerTasksDrawer, type ComposerTaskStep, type ComposerTasksProgress, } from "./ComposerTasksBadge"; import { ComposerActivityRow } from "./ComposerActivityStatus"; import type { ThreadSyncPhase } from "../../threadSync"; import { ComposerBanner } from "./ComposerBanner"; import { ComposerSurface } from "./ComposerSurface"; import { ComposerBannerStack, type ComposerBannerStackContent, type ComposerBannerStackItem, } from "./ComposerBannerStack"; import { compressImageForStash, prepareImageForAttachment } from "../../lib/imageCompression"; import { fileAttachmentTooLargeMessage, formatAttachmentSize, } from "@t3tools/client-runtime/state/attachments"; import { attachmentsToReleaseOnUploadCapabilityLoss, classifyComposerAttachmentFile, fileAttachmentCapabilityBlockReason, fileAttachmentStagingLimit, isPreviewableComposerVideo, normalizeComposerImageFileMimeType, shouldHandleComposerAttachmentPaste, } from "./composerAttachmentFiles"; import { readAttachmentUpload, releaseAttachmentUpload, releaseDraftAttachment, releasePersistedAttachmentUpload, retryAttachmentUpload, startAttachmentUpload, useAttachmentUploadStore, verifyStashedAttachmentUpload, } from "../../lib/attachmentUploadQueue"; import { attachmentUploadBlockReason, formatAttachmentUploadProgress, } from "../../lib/attachmentUploadState"; import { isCommandPaletteOpen } from "../../commandPaletteBus"; import { getTerminalFocusOwner } from "../../lib/terminalFocus"; import type { AssistantCitationSourceAnchor } from "~/lib/assistantTextSelection"; import { resolveShortcutCommand, shortcutLabelForCommand } from "../../keybindings"; import { type TerminalContextDraft, type TerminalContextSelection, INLINE_TERMINAL_CONTEXT_PLACEHOLDER, insertInlineTerminalContextPlaceholder, removeInlineTerminalContextPlaceholder, } from "../../lib/terminalContext"; import { useComposerPathSearch } from "../../lib/composerPathSearchState"; import { type ElementContextDraft } from "../../lib/elementContext"; import { ComposerPendingElementContexts } from "./ComposerPendingElementContexts"; import { ComposerPendingReviewComments } from "./ComposerPendingReviewComments"; import { ComposerPreviewAnnotationCards } from "./ComposerPreviewAnnotationCards"; import { COMPOSER_FOOTER_COMPACT_BREAKPOINT_PX, COMPOSER_FOOTER_WIDE_ACTIONS_COMPACT_BREAKPOINT_PX, getRestingComposerImagePreviewCounts, resolveRestingComposerControlsLayout, shouldAnimateComposerRestingTransition, shouldUseCompactComposerPrimaryActions, shouldUseCompactComposerFooter, shouldUseRestingComposerLayout, } from "../composerFooterLayout"; import { measureRestingComposerControls } from "./restingComposerControlsMeasurement"; import { observeResponsiveBreakpointFade, usePanelAnimationSettings } from "../../panelAnimations"; import { type ComposerPromptEditorHandle, ComposerPromptEditor } from "../ComposerPromptEditor"; import { ProviderModelPicker } from "./ProviderModelPicker"; import { type ComposerCommandItem, ComposerCommandMenu } from "./ComposerCommandMenu"; import { ComposerPendingApprovalActions } from "./ComposerPendingApprovalActions"; import { CompactComposerControlsMenu } from "./CompactComposerControlsMenu"; import { ComposerPrimaryActions } from "./ComposerPrimaryActions"; import { ComposerPendingApprovalPanel } from "./ComposerPendingApprovalPanel"; import { ComposerPendingUserInputPanel } from "./ComposerPendingUserInputPanel"; import { ComposerPlanFollowUpBanner } from "./ComposerPlanFollowUpBanner"; import { ComposerControl, ComposerControlIcon, ComposerControlSeparator, ComposerSelectControl, } from "./ComposerControl"; import { resolveComposerMenuActiveItemId } from "./composerMenuHighlight"; import { searchSlashCommandItems, slashCommandItemsForPromptPosition, } from "./composerSlashCommandSearch"; import { getComposerPromptInjectionState, getComposerProviderState, renderProviderTraitsMenuContent, renderProviderTraitsPicker, } from "./composerProviderState"; import { ContextWindowMeter, ContextWindowMeterPlaceholder } from "./ContextWindowMeter"; import { providerSupportsManualCompaction, resolveContextWindowModelDisplayName, shouldReserveContextWindowMeter, } from "./ContextWindowMeter.logic"; import { attachVideoThumbnail, buildExpandedImagePreview, type ExpandedImagePreview, } from "./ExpandedImagePreview"; import { SNAP_SHOT_ATTACHMENT_FRAME_CLASS, SnapShotAttachmentDetails, } from "./SnapShotAttachmentDetails"; import { getPendingSnapShotAnimations, pendingSnapShotAnimationIdsForTarget, scheduleSnapShotAnimationDestination, setSnapShotAnimationDestination, shouldAnimateSnapShotArrival, subscribeToPendingSnapShotAnimations, } from "../../lib/snapShotAnimation"; import { resizeSnapShotSource } from "../../lib/snapShotSource"; import { basenameOfPath } from "../../pierre-icons"; import { cn, randomUUID } from "~/lib/utils"; import { getComposerPromptLengthValidationMessage, getComposerSubmissionValidationMessage, submitComposerDraft, } from "./composerSubmission"; import { ComposerPromptLengthValidation } from "./ComposerPromptLengthValidation"; import { PierreEntryIcon } from "./PierreEntryIcon"; import { createComposerScrollGestureState, recordComposerScrollGestureEvent, shouldCollapseComposerForScrollKey, resetComposerScrollGesture, suppressActiveComposerScrollGesture, } from "./composerScrollGesture"; import { prepareVideoFirstFrame } from "../../lib/videoFirstFrame"; function ComposerVideoThumbnail({ file }: { file: File }) { const setVideo = useCallback( (video: HTMLVideoElement | null) => { if (!video) return; return attachVideoThumbnail(video, file); }, [file], ); return (