pcm-agents 0.3.2 → 0.3.3
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/LICENSE +21 -21
- package/dist/cjs/{index-BFPEnLbS.js → index-RmBfqEV7.js} +4 -3
- package/dist/cjs/index-RmBfqEV7.js.map +1 -0
- package/dist/cjs/index.cjs.js +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/my-component.cjs.entry.js +2 -2
- package/dist/cjs/my-component.cjs.entry.js.map +1 -1
- package/dist/cjs/my-component.entry.cjs.js.map +1 -1
- package/dist/cjs/pcm-agents.cjs.js +1 -1
- package/dist/cjs/pcm-app-chat-modal.pcm-chat-message.pcm-hr-chat-modal.pcm-jlpp-modal.pcm-mnms-modal.pcm-video-chat-modal.pcm-zygh-modal.entry.cjs.js.map +1 -0
- package/dist/cjs/pcm-app-chat-modal_7.cjs.entry.js +56 -53
- package/dist/cjs/pcm-app-chat-modal_7.cjs.entry.js.map +1 -1
- package/dist/cjs/pcm-chat-modal.cjs.entry.js +1 -1
- package/dist/collection/collection-manifest.json +1 -1
- package/dist/collection/components/my-component/my-component.css +3 -3
- package/dist/collection/components/my-component/my-component.js +1 -1
- package/dist/collection/components/my-component/my-component.js.map +1 -1
- package/dist/collection/components/pcm-app-chat-modal/pcm-app-chat-modal.js +3 -4
- package/dist/collection/components/pcm-app-chat-modal/pcm-app-chat-modal.js.map +1 -1
- package/dist/collection/components/pcm-hr-chat-modal/pcm-hr-chat-modal.js +2 -2
- package/dist/collection/components/pcm-hr-chat-modal/pcm-hr-chat-modal.js.map +1 -1
- package/dist/collection/components/{pcm-jlpx-modal/pcm-jlpx-modal.js → pcm-jlpp-modal/pcm-jlpp-modal.js} +21 -21
- package/dist/collection/components/pcm-jlpp-modal/pcm-jlpp-modal.js.map +1 -0
- package/dist/collection/components/pcm-mnms-modal/pcm-mnms-modal.js +16 -16
- package/dist/collection/components/pcm-mnms-modal/pcm-mnms-modal.js.map +1 -1
- package/dist/collection/components/pcm-video-chat-modal/pcm-video-chat-modal.js +2 -2
- package/dist/collection/components/pcm-video-chat-modal/pcm-video-chat-modal.js.map +1 -1
- package/dist/collection/components/pcm-zygh-modal/pcm-zygh-modal.js +22 -18
- package/dist/collection/components/pcm-zygh-modal/pcm-zygh-modal.js.map +1 -1
- package/dist/collection/global/global.css +31 -0
- package/dist/collection/index.js.map +1 -1
- package/dist/collection/utils/utils.js +2 -1
- package/dist/collection/utils/utils.js.map +1 -1
- package/dist/components/index.js +2 -1
- package/dist/components/index.js.map +1 -1
- package/dist/components/my-component.js +1 -1
- package/dist/components/my-component.js.map +1 -1
- package/dist/components/{p-BctfuDvG.js → p-BG-UIl44.js} +4 -5
- package/dist/components/p-BG-UIl44.js.map +1 -0
- package/dist/components/pcm-app-chat-modal.js +1 -1
- package/dist/components/pcm-hr-chat-modal.js +1 -1
- package/dist/components/pcm-hr-chat-modal.js.map +1 -1
- package/dist/components/{pcm-jlpx-modal.d.ts → pcm-jlpp-modal.d.ts} +4 -4
- package/dist/components/{pcm-jlpx-modal.js → pcm-jlpp-modal.js} +26 -26
- package/dist/components/pcm-jlpp-modal.js.map +1 -0
- package/dist/components/pcm-mnms-modal.js +15 -15
- package/dist/components/pcm-mnms-modal.js.map +1 -1
- package/dist/components/pcm-video-chat-modal.js +1 -1
- package/dist/components/pcm-video-chat-modal.js.map +1 -1
- package/dist/components/pcm-zygh-modal.js +19 -15
- package/dist/components/pcm-zygh-modal.js.map +1 -1
- package/dist/esm/{index-nVjZGfA8.js → index-CDtyzi83.js} +4 -3
- package/dist/esm/index-CDtyzi83.js.map +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/my-component.entry.js +2 -2
- package/dist/esm/my-component.entry.js.map +1 -1
- package/dist/esm/pcm-agents.js +1 -1
- package/dist/esm/pcm-app-chat-modal.pcm-chat-message.pcm-hr-chat-modal.pcm-jlpp-modal.pcm-mnms-modal.pcm-video-chat-modal.pcm-zygh-modal.entry.js.map +1 -0
- package/dist/esm/pcm-app-chat-modal_7.entry.js +56 -53
- package/dist/esm/pcm-app-chat-modal_7.entry.js.map +1 -1
- package/dist/esm/pcm-chat-modal.entry.js +1 -1
- package/dist/pcm-agents/app-globals-DQuL1Twl.js.map +1 -0
- package/dist/pcm-agents/index-Csq0E9fM.js.map +1 -0
- package/dist/pcm-agents/index.esm.js +1 -1
- package/dist/pcm-agents/index.esm.js.map +1 -1
- package/dist/pcm-agents/my-component.entry.esm.js.map +1 -1
- package/dist/pcm-agents/p-11c829b3.entry.js +2 -0
- package/dist/pcm-agents/p-11c829b3.entry.js.map +1 -0
- package/dist/pcm-agents/{p-f3ca99b4.entry.js → p-430f9a9f.entry.js} +2 -2
- package/dist/pcm-agents/p-CDtyzi83.js +2 -0
- package/dist/pcm-agents/p-CDtyzi83.js.map +1 -0
- package/dist/pcm-agents/p-e7221181.entry.js +2 -0
- package/dist/pcm-agents/p-e7221181.entry.js.map +1 -0
- package/dist/pcm-agents/pcm-agents.esm.js +1 -1
- package/dist/pcm-agents/pcm-agents.esm.js.map +1 -1
- package/dist/pcm-agents/pcm-app-chat-modal.pcm-chat-message.pcm-hr-chat-modal.pcm-jlpp-modal.pcm-mnms-modal.pcm-video-chat-modal.pcm-zygh-modal.entry.esm.js.map +1 -0
- package/dist/pcm-agents/utils-CfkrvVlp.js.map +1 -0
- package/dist/types/components/pcm-app-chat-modal/pcm-app-chat-modal.d.ts +1 -1
- package/dist/types/components/pcm-hr-chat-modal/pcm-hr-chat-modal.d.ts +1 -1
- package/dist/types/components/{pcm-jlpx-modal/pcm-jlpx-modal.d.ts → pcm-jlpp-modal/pcm-jlpp-modal.d.ts} +4 -4
- package/dist/types/components/pcm-mnms-modal/pcm-mnms-modal.d.ts +3 -3
- package/dist/types/components/pcm-video-chat-modal/pcm-video-chat-modal.d.ts +1 -1
- package/dist/types/components/pcm-zygh-modal/pcm-zygh-modal.d.ts +8 -4
- package/dist/types/components.d.ts +48 -48
- package/dist/types/utils/utils.d.ts +1 -1
- package/package.json +1 -1
- package/readme.md +307 -307
- package/dist/cjs/index-BFPEnLbS.js.map +0 -1
- package/dist/cjs/pcm-app-chat-modal.pcm-chat-message.pcm-hr-chat-modal.pcm-jlpx-modal.pcm-mnms-modal.pcm-video-chat-modal.pcm-zygh-modal.entry.cjs.js.map +0 -1
- package/dist/collection/components/pcm-jlpx-modal/pcm-jlpx-modal.js.map +0 -1
- package/dist/components/p-BctfuDvG.js.map +0 -1
- package/dist/components/pcm-jlpx-modal.js.map +0 -1
- package/dist/esm/index-nVjZGfA8.js.map +0 -1
- package/dist/esm/pcm-app-chat-modal.pcm-chat-message.pcm-hr-chat-modal.pcm-jlpx-modal.pcm-mnms-modal.pcm-video-chat-modal.pcm-zygh-modal.entry.js.map +0 -1
- package/dist/pcm-agents/p-55417392.entry.js +0 -2
- package/dist/pcm-agents/p-55417392.entry.js.map +0 -1
- package/dist/pcm-agents/p-a698b59f.entry.js +0 -2
- package/dist/pcm-agents/p-a698b59f.entry.js.map +0 -1
- package/dist/pcm-agents/p-nVjZGfA8.js +0 -2
- package/dist/pcm-agents/p-nVjZGfA8.js.map +0 -1
- package/dist/pcm-agents/pcm-app-chat-modal.pcm-chat-message.pcm-hr-chat-modal.pcm-jlpx-modal.pcm-mnms-modal.pcm-video-chat-modal.pcm-zygh-modal.entry.esm.js.map +0 -1
- /package/dist/collection/components/{pcm-jlpx-modal/pcm-jlpx-modal.css → pcm-jlpp-modal/pcm-jlpp-modal.css} +0 -0
- /package/dist/pcm-agents/{p-f3ca99b4.entry.js.map → p-430f9a9f.entry.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["pcmAppChatModalCss","ChatAPPModal","modalTitle","token","isOpen","messages","modalClosed","icon","zIndex","isShowHeader","isNeedClose","conversationId","currentAssistantMessage","isLoading","currentStreamingMessage","shouldAutoScroll","isLoadingHistory","streamComplete","conversationStart","selectedFile","isUploading","uploadedFileInfo","defaultQuery","showInitialUpload","isRecording","recordingStream","recordedBlob","mediaRecorder","recordingTimeLeft","showRecordingUI","recordingTimer","recordingStartTime","recordingMaxTime","waitingToRecord","waitingTimer","waitingTimeLeft","videoRef","totalQuestions","currentQuestionNumber","interviewComplete","SCROLL_THRESHOLD","maxRecordingTime","countdownWarningTime","showCountdownWarning","fullscreen","isUploadingVideo","isPlayingAudio","audioUrl","audioElement","recordingError","recordingStatusChange","enableTTS","enableVoice","displayContentStatus","interviewMode","textAnswer","isSubmittingText","customInputs","botId","handleClose","this","stopRecording","emit","sendMessageToAPI","message","videoUrl","answer","llmText","now","Date","time","getHours","getMinutes","toString","padStart","queryText","trim","isLastQuestion","newMessage","id","query","isStreaming","conversation_id","inputs","status","error","scrollToBottom","completeInterview","total_questions","requestData","response_mode","bot_id","video_url","sendSSERequest","url","method","headers","authorization","data","onMessage","event","message_id","LLMText","updatedMessage","onError","console","alert","Error","onComplete","async","latestAIMessage","textForSynthesis","synthesizeAudio","playAudio","startWaitingToRecord","handleScroll","chatHistory","hostElement","shadowRoot","querySelector","scrollTop","scrollHeight","clientHeight","distanceFromBottom","componentDidRender","loadHistoryMessages","log","result","sendHttpRequest","limit","success","historyData","formattedMessages","map","msg","created_at","hours","minutes","timeStr","msgWithoutInputs","requestAnimationFrame","componentDidLoad","addEventListener","componentWillLoad","setTimeout","clearInterval","setInterval","startRecording","stream","navigator","mediaDevices","getUserMedia","audio","video","width","ideal","height","frameRate","setupVideoPreview","mimeType","getSupportedMimeType","MediaRecorder","e","warn","recorderError","type","details","chunks","ondataavailable","size","push","onerror","onstop","blobType","blob","Blob","duration","Math","floor","uploadRecordedVideo","start","maxDuration","startError","elapsedTime","max","videoElement","srcObject","play","catch","err","objectUrl","URL","createObjectURL","src","onended","revokeObjectURL","urlError","mimeTypes","window","isTypeSupported","stop","getTracks","forEach","track","convertAudioToText","cosKey","cos_key","text","fileExtension","includes","fileName","file","File","fileInfo","uploadFileToBackend","transcriptionText","response","fetch","API_DOMAIN","body","JSON","stringify","ok","audioBlob","Promise","resolve","Audio","disconnectedCallback","removeEventListener","pause","handlePlayAudio","handleTextInputChange","input","target","value","handleKeyDown","key","ctrlKey","preventDefault","submitTextAnswer","textToSend","render","modalStyle","String","containerClass","overlayClass","renderVideoPreview","h","class","autoPlay","playsInline","muted","style","transform","ref","el","warning","renderPlaceholderStatus","renderTextInputArea","placeholder","onInput","onKeyDown","disabled","onClick","alt","onScroll","onMessageChange","updatedMessages","detail","length","display","flexWrap","justifyContent","viewBox","fill","verticalAlign","marginRight","d","_getDefaults","breaks","extensions","gfm","hooks","pedantic","renderer","silent","tokenizer","walkTokens","_defaults","changeDefaults","newDefaults","escapeTest","escapeReplace","RegExp","source","escapeTestNoEncode","escapeReplaceNoEncode","escapeReplacements","getEscapeReplacement","ch","escape","html","encode","test","replace","unescapeTest","unescape","_","n","toLowerCase","charAt","fromCharCode","parseInt","substring","caret","edit","regex","opt","obj","name","val","getRegex","cleanUrl","href","encodeURI","noopTest","exec","splitCells","tableRow","count","row","match","offset","str","escaped","curr","cells","split","i","shift","pop","splice","rtrim","c","invert","l","suffLen","currChar","slice","findClosingBracket","b","indexOf","level","outputLink","cap","link","raw","lexer","title","state","inLink","tokens","inlineTokens","indentCodeCompensation","matchIndentToCode","indentToCode","node","matchIndentInNode","indentInNode","join","_Tokenizer","options","rules","constructor","space","block","newline","code","codeBlockStyle","fences","lang","inline","_escapes","heading","trimmed","depth","hr","blockquote","top","blockTokens","list","bull","isordered","ordered","loose","items","itemRegex","itemContents","endsWithBlankLine","endEarly","line","t","repeat","nextLine","indent","trimStart","search","blankLine","nextBulletRegex","min","hrRegex","fencesBeginRegex","headingBeginRegex","rawLine","istask","ischecked","task","checked","trimEnd","spacers","filter","hasMultipleLineBreaks","some","pre","def","tag","table","item","header","align","rows","j","k","lheading","paragraph","inRawBlock","trimmedUrl","rtrimSlash","lastParenIndex","linkLen","reflink","links","nolink","emStrong","maskedSrc","prevChar","lDelim","nextChar","punctuation","lLength","rDelim","rLength","delimTotal","midDelimTotal","endReg","rDelimAst","rDelimUnd","lastIndex","lastCharLength","index","codespan","hasNonSpaceChars","hasSpaceCharsOnBothEnds","br","del","autolink","prevCapZero","_backpedal","inlineText","_paragraph","_label","_title","bullet","listItemStart","_tag","_comment","normal","reflinkSearch","_punctuation","blockSkip","anyPunctuation","_scheme","_email","_attribute","_href","strong","middle","endAst","endUnd","em","_extended_email","_Lexer","inlineQueue","Object","create","lex","lexInline","next","leading","tabs","lastToken","cutSrc","lastParagraphClipped","extTokenizer","call","startBlock","startIndex","Infinity","tempSrc","tempStart","getStartIndex","errMsg","charCodeAt","keepPrevChar","keys","lastIndexOf","startInline","_Renderer","infostring","quote","startatt","listitem","checkbox","tablerow","content","tablecell","flags","cleanHref","out","image","_TextRenderer","_Parser","textRenderer","parse","parser","parseInline","renderers","genericToken","ret","headingToken","codeToken","tableToken","cell","blockquoteToken","listToken","itemBody","unshift","htmlToken","paragraphToken","textToken","escapeToken","tagToken","linkToken","imageToken","strongToken","emToken","codespanToken","delToken","_Hooks","static","Set","preprocess","markdown","postprocess","Marked","defaults","setOptions","parseMarkdown","Parser","Renderer","TextRenderer","Lexer","Tokenizer","Hooks","args","use","callback","values","concat","childTokens","pack","opts","ext","prevRenderer","apply","extLevel","prop","rendererFunc","rendererKey","tokenizerFunc","tokenizerKey","prevTokenizer","hooksFunc","hooksKey","prevHook","passThroughHooks","has","arg","then","packWalktokens","origOpt","throwError","prototype","all","reject","markedInstance","marked","getDefaults","interruptPatterns","skipEmptyRows","regexString","widthRegex","colCount","reduce","colspan","col","output","getTableCell","emptyRow","rowspan","prevRow","matchAll","x","numCols","trimmedCell","prevCell","prevCols","rowSpanTarget","lib","pcmChatMessageCss","ChatMessageComponent","messageChange","hostRef","extendedTables","copyMessageContent","clipboard","writeText","renderUserMessage","renderInputs","renderAssistantMessage","showLoading","htmlContent","innerHTML","xmlns","stroke","y","rx","ry","getFileIcon","extension","color","getFileName","fileUrl","parts","getCosPreviewUrl","params","file_url","baseUrl","handleFileClick","previewUrl","open","renderFileItem","startsWith","fileList","Array","isArray","fileIndex","pcmHrChatModalCss","ChatHRModal","selectedJobCategory","jobCategories","dimensions","selectedDimensions","isInterviewComplete","toEmail","requireResume","userId","handleFileChange","files","uploadFile","file_name","file_size","presigned_url","clearSelectedFile","handleUploadClick","fileInput","click","lastAIMessage","saveAnswer","user","job_info","dimensional_info","email","display_content_status","fileUrls","file_urls","updateUrlWithConversationId","question","urlParams","URLSearchParams","location","get","newUrl","searchParams","set","history","replaceState","handleIsOpenChange","newValue","handleJobCategorySelect","category","handleDimensionSelect","dimension","handleInitialSubmit","confirmed","confirm","videoFile","saveVideoAnswer","sendNextQuestion","Fragment","stopPropagation","selected","onChange","accept","pcmJlppModalCss","globalCss","JlppModal","uploadSuccess","tokenInvalid","showChatModal","jobDescription","isSubmitting","isTransitioning","transitionTimer","handleJobDescriptionChange","textarea","handleStartAnalysis","clearTimeout","verifyApiKey","Authorization","handleStreamComplete","handleConversationStart","handleInterviewComplete","hideJdInput","Boolean","htmlFor","rel","undefined","onModalClosed","onStreamComplete","onConversationStart","onInterviewComplete","pcmMnmsModalCss","MnmsModal","handleStartInterview","pcmVideoChatModalCss","VideoChatModal","resumeId","uploadResult","pcmZyghModalCss","ZyghModal","planningComplete","selectedPlanType","handlePlanTypeChange","handleStartPlanning","plan_type","handlePlanningComplete"],"sources":["src/components/pcm-app-chat-modal/pcm-app-chat-modal.css?tag=pcm-app-chat-modal&encapsulation=shadow","src/components/pcm-app-chat-modal/pcm-app-chat-modal.tsx","node_modules/.pnpm/marked@9.1.6/node_modules/marked/lib/marked.esm.js","node_modules/.pnpm/marked-extended-tables@2.0.1_marked@9.1.6/node_modules/marked-extended-tables/lib/index.cjs","src/components/pcm-chat-message/pcm-chat-message.css?tag=pcm-chat-message&encapsulation=shadow","src/components/pcm-chat-message/pcm-chat-message.tsx","src/components/pcm-hr-chat-modal/pcm-hr-chat-modal.css?tag=pcm-hr-chat-modal&encapsulation=shadow","src/components/pcm-hr-chat-modal/pcm-hr-chat-modal.tsx","src/components/pcm-jlpp-modal/pcm-jlpp-modal.css?tag=pcm-jlpp-modal&encapsulation=shadow","src/global/global.css?tag=pcm-jlpp-modal&encapsulation=shadow","src/components/pcm-jlpp-modal/pcm-jlpp-modal.tsx","src/components/pcm-mnms-modal/pcm-mnms-modal.css?tag=pcm-mnms-modal&encapsulation=shadow","src/global/global.css?tag=pcm-mnms-modal&encapsulation=shadow","src/components/pcm-mnms-modal/pcm-mnms-modal.tsx","src/components/pcm-video-chat-modal/pcm-video-chat-modal.css?tag=pcm-video-chat-modal&encapsulation=shadow","src/components/pcm-video-chat-modal/pcm-video-chat-modal.tsx","src/components/pcm-zygh-modal/pcm-zygh-modal.css?tag=pcm-zygh-modal&encapsulation=shadow","src/global/global.css?tag=pcm-zygh-modal&encapsulation=shadow","src/components/pcm-zygh-modal/pcm-zygh-modal.tsx"],"sourcesContent":[":host {\r\n display: block;\r\n}\r\n\r\n.modal-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background-color: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n overflow-y: auto;\r\n padding: 20px;\r\n z-index: 1000;\r\n}\r\n\r\n/* 全屏模式下取消 padding */\r\n.fullscreen-overlay {\r\n padding: 0;\r\n}\r\n\r\n.modal-container {\r\n background: white;\r\n border-radius: 8px;\r\n width: 100%;\r\n max-width: 900px;\r\n display: flex;\r\n flex-direction: column;\r\n position: relative;\r\n margin: auto;\r\n}\r\n\r\n/* 全屏模式样式 */\r\n.modal-container.fullscreen {\r\n width: 100vw;\r\n max-width: none;\r\n height: 100%;\r\n border-radius: 0;\r\n margin: 0;\r\n display: flex;\r\n flex-direction: column;\r\n height: 100vh;\r\n max-height: 100vh;\r\n}\r\n\r\n/* 确保内容区域也使用 flex 布局并占满剩余空间 */\r\n.modal-container.fullscreen > div:not(.modal-header):not(.initial-upload) {\r\n display: flex;\r\n flex-direction: column;\r\n flex: 1;\r\n overflow: hidden; /* 防止内容溢出 */\r\n height: 100%;\r\n}\r\n\r\n/* PC端布局 */\r\n.pc-layout {\r\n width: 80%;\r\n max-width: 800px;\r\n /* height: 80vh; */\r\n /* max-height: 700px; */\r\n min-width: 320px;\r\n min-height: 400px;\r\n}\r\n\r\n/* 移动端布局 */\r\n.mobile-layout {\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 0;\r\n}\r\n\r\n\r\n.video-preview.placeholder {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n background: #EAEAEA;\r\n}\r\n\r\n.placeholder-status {\r\n color: #00000066;\r\n}\r\n\r\n.waiting-message p {\r\n margin: 0;\r\n font-size: 16px;\r\n color: white;\r\n font-weight: 500;\r\n}\r\n\r\n.recording-container {\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n}\r\n\r\n\r\n\r\n.video-area {\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n}\r\n\r\n.stop-recording-button {\r\n width: 100%;\r\n height: 100%;\r\n font-size: 16px;\r\n background: #f44336;\r\n border-radius: 6px;\r\n color: white;\r\n border: none;\r\n cursor: pointer;\r\n}\r\n\r\n.stop-recording-button:hover {\r\n background: #d32f2f;\r\n}\r\n\r\n.play-audio-container {\r\n width: 100%;\r\n height: 100%;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n}\r\n\r\n.modal-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 4px 16px;\r\n height: 50px;\r\n border-bottom: 1px solid #e8e8e8;\r\n flex-shrink: 0; /* 防止头部被压缩 */\r\n}\r\n\r\n.header-left {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n}\r\n\r\n.header-icon {\r\n width: 24px;\r\n height: 24px;\r\n}\r\n\r\n.close-button {\r\n background: transparent;\r\n border: none;\r\n cursor: pointer;\r\n padding: 8px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 4px;\r\n}\r\n\r\n.close-button:hover {\r\n background-color: rgba(0, 0, 0, 0.04);\r\n}\r\n\r\n.close-button span {\r\n font-size: 24px;\r\n line-height: 1;\r\n color: #999;\r\n}\r\n\r\n.close-button:hover span {\r\n color: #666;\r\n}\r\n\r\n.chat-history {\r\n position: relative;\r\n flex: 1;\r\n overflow-y: auto;\r\n padding: 20px;\r\n scroll-behavior: smooth;\r\n height: 400px;\r\n background: url(https://pcm-resource-1312611446.cos.ap-guangzhou.myqcloud.com/web/sdk/chat_bg.png);\r\n background-size: 100%;\r\n}\r\n\r\n/* 添加全屏模式下的样式 */\r\n.fullscreen .chat-history {\r\n height: auto;\r\n flex: 1 1 auto;\r\n}\r\n\r\n\r\n.message-input {\r\n padding: 16px;\r\n border-top: 1px solid #eee;\r\n display: flex;\r\n gap: 8px;\r\n align-items: center;\r\n}\r\n\r\n.message-input input {\r\n flex: 1;\r\n padding: 8px 12px;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n outline: none;\r\n transition: border-color 0.2s ease;\r\n}\r\n\r\n.message-input input:focus {\r\n border-color: #bbb;\r\n}\r\n\r\n/* 消息样式 */\r\n.message {\r\n margin-bottom: 16px;\r\n opacity: 1;\r\n transition: opacity 0.3s ease;\r\n}\r\n\r\n.message-content {\r\n max-width: 70%;\r\n padding: 8px 12px;\r\n border-radius: 8px;\r\n word-break: break-word;\r\n}\r\n\r\n.message-content p {\r\n margin: 0;\r\n word-break: break-word;\r\n}\r\n\r\n.user-message {\r\n display: flex;\r\n justify-content: flex-end;\r\n}\r\n\r\n.agent-message {\r\n display: flex;\r\n justify-content: flex-start;\r\n}\r\n\r\n.user-message .message-content {\r\n background-color: #007bff;\r\n color: white;\r\n}\r\n\r\n.agent-message .message-content {\r\n background-color: #f1f1f1;\r\n}\r\n\r\n.message-time {\r\n font-size: 12px;\r\n color: #999;\r\n margin-top: 4px;\r\n display: block;\r\n}\r\n\r\n.send-button {\r\n background-color: #1890ff;\r\n color: white;\r\n border: none;\r\n border-radius: 4px;\r\n padding: 8px 16px;\r\n cursor: pointer;\r\n font-weight: 500;\r\n}\r\n\r\n.send-button:disabled {\r\n background-color: #ccc;\r\n cursor: not-allowed;\r\n}\r\n\r\n.empty-state {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n height: 100%;\r\n color: #999;\r\n text-align: center;\r\n}\r\n\r\n.loading-container {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n display: flex;\r\n flex-direction: column;\r\n justify-content: center;\r\n align-items: center;\r\n background-color: rgba(255, 255, 255, 0.98);\r\n z-index: 1;\r\n opacity: 1;\r\n transition: opacity 0.3s ease;\r\n}\r\n\r\n.loading-container p {\r\n margin-top: 16px;\r\n color: #666;\r\n font-size: 14px;\r\n}\r\n\r\n.loading-spinner {\r\n width: 40px;\r\n height: 40px;\r\n border: 3px solid #f3f3f3;\r\n border-top: 3px solid #1890ff;\r\n border-radius: 50%;\r\n animation: spin 1s linear infinite;\r\n}\r\n\r\n@keyframes spin {\r\n 0% {\r\n transform: rotate(0deg);\r\n }\r\n\r\n 100% {\r\n transform: rotate(360deg);\r\n }\r\n}\r\n\r\n/* 修改 messages-wrapper 的样式 */\r\n.messages-wrapper {\r\n width: 100%;\r\n min-height: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n /* 当内容少时,将内容放在底部 */\r\n justify-content: flex-end;\r\n}\r\n\r\n/* 当有很多消息时,取消固定在底部 */\r\n.messages-wrapper.has-overflow {\r\n justify-content: flex-start;\r\n}\r\n\r\n.suggested-questions {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n padding: 16px;\r\n}\r\n\r\n.suggested-question {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 8px 12px;\r\n background-color: #f3f4f6;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n font-size: 14px;\r\n color: #374151;\r\n transition: background-color 0.2s;\r\n}\r\n\r\n.suggested-question:hover {\r\n background-color: #e5e7eb;\r\n}\r\n\r\n.arrow-right {\r\n margin-left: 8px;\r\n}\r\n\r\n.loading-suggestions {\r\n display: flex;\r\n justify-content: center;\r\n padding: 16px;\r\n}\r\n\r\n.loading-spinner-small {\r\n width: 20px;\r\n height: 20px;\r\n border: 2px solid #e5e7eb;\r\n border-top-color: #6b7280;\r\n border-radius: 50%;\r\n animation: spin 1s linear infinite;\r\n}\r\n\r\n/* 添加上传按钮样式 */\r\n.upload-button {\r\n background: transparent;\r\n border: none;\r\n cursor: pointer;\r\n padding: 8px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n color: #666;\r\n border-radius: 4px;\r\n transition: background-color 0.2s;\r\n}\r\n\r\n.upload-button:hover {\r\n background-color: rgba(0, 0, 0, 0.04);\r\n}\r\n\r\n.upload-button svg {\r\n width: 20px;\r\n height: 20px;\r\n}\r\n\r\n/* 隐藏原生文件输入框 */\r\n.file-input {\r\n display: none;\r\n}\r\n\r\n/* 添加文件名显示区域样式 */\r\n.selected-file {\r\n font-size: 12px;\r\n color: #666;\r\n margin-left: 8px;\r\n max-width: 150px;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.input-wrapper {\r\n flex: 1;\r\n display: flex;\r\n align-items: center;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n padding: 0 4px;\r\n background: white;\r\n}\r\n\r\n.input-wrapper input {\r\n border: none;\r\n flex: 1;\r\n padding: 8px;\r\n outline: none;\r\n}\r\n\r\n.input-wrapper:focus-within {\r\n border-color: #bbb;\r\n}\r\n\r\n/* 文件预览区域样式 */\r\n.file-preview {\r\n padding: 8px 16px;\r\n border-top: 1px solid #eee;\r\n background-color: #f9f9f9;\r\n}\r\n\r\n.file-info {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 8px;\r\n background: white;\r\n border: 1px solid #e8e8e8;\r\n border-radius: 4px;\r\n}\r\n\r\n\r\n.recording-section {\r\n border-top: 1px solid #eee;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n padding: 20px;\r\n border-radius: 14px 14px 0 0;\r\n flex: 0 0 auto;\r\n}\r\n\r\n.recording-section .video-preview {\r\n width: 100%;\r\n height: 200px;\r\n max-width: 400px;\r\n position: relative;\r\n margin-bottom: 10px;\r\n border: 1px solid #ddd;\r\n border-radius: 12px;\r\n overflow: hidden;\r\n}\r\n\r\n.recording-section video {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n}\r\n\r\n/* 修改 recording-status 样式 */\r\n.recording-status {\r\n position: absolute;\r\n top: 10px;\r\n left: 10px;\r\n background-color: rgba(0, 0, 0, 0.6);\r\n color: white;\r\n padding: 4px 8px;\r\n border-radius: 4px;\r\n display: flex;\r\n align-items: center;\r\n gap: 5px;\r\n font-size: 0.8rem;\r\n z-index: 2;\r\n}\r\n\r\n.recording-status .recording-dot {\r\n display: inline-block;\r\n width: 10px;\r\n height: 10px;\r\n background-color: red;\r\n border-radius: 50%;\r\n margin-right: 5px;\r\n animation: blink 1s infinite;\r\n}\r\n\r\n.recording-status.warning {\r\n color: #ff4d4f;\r\n animation: blink 1s infinite;\r\n}\r\n\r\n@keyframes blink {\r\n 0% {\r\n opacity: 1;\r\n }\r\n\r\n 50% {\r\n opacity: 0.5;\r\n }\r\n\r\n 100% {\r\n opacity: 1;\r\n }\r\n}\r\n\r\n.recording-section .stop-recording-button {\r\n background-color: #f44336;\r\n color: white;\r\n border: none;\r\n cursor: pointer;\r\n font-weight: bold;\r\n}\r\n\r\n.recording-section .stop-recording-button:hover {\r\n background-color: #d32f2f;\r\n}\r\n\r\n.fullscreen {\r\n width: 100vw;\r\n border-radius: 0;\r\n height: 100vh;\r\n display: flex;\r\n flex-direction: column;\r\n overflow-y: auto;\r\n}\r\n\r\n.recording-controls {\r\n margin-top: 10px;\r\n height: 53px;\r\n width: 100%;\r\n max-width: 400px;\r\n display: flex;\r\n justify-content: center;\r\n}\r\n\r\n.recording-controls .waiting-message {\r\n text-align: center;\r\n color: white;\r\n font-size: 1rem;\r\n background-image: linear-gradient(100deg, #4A9FFF 0%, #1058FF 100%);\r\n border-radius: 6px;\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\r\n width: 95%;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n cursor: pointer;\r\n}\r\n\r\n.recording-controls .waiting-message.loading {\r\n background: #faad14;\r\n}\r\n\r\n.recording-controls .waiting-message p {\r\n margin: 0;\r\n font-size: 16px;\r\n color: white;\r\n font-weight: 500;\r\n}\r\n\r\n.recording-controls .stop-recording-button {\r\n background-color: #dc3545;\r\n color: white;\r\n border: none;\r\n cursor: pointer;\r\n font-size: 1rem;\r\n}\r\n\r\n.recording-controls .stop-recording-button:hover {\r\n background-color: #c82333;\r\n}\r\n\r\n/* 添加禁用状态的样式 */\r\n.recording-controls .stop-recording-button.disabled {\r\n background: #ccc;\r\n cursor: not-allowed;\r\n}\r\n\r\n.recording-controls .stop-recording-button.disabled:hover {\r\n background: #ccc;\r\n}\r\n\r\n/* 添加进度条和数字进度的样式 */\r\n.progress-container {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n width: 100%;\r\n max-width: 400px;\r\n margin-top: 10px;\r\n padding: 0 5px;\r\n}\r\n\r\n.progress-bar-container {\r\n height: 4px;\r\n background-color: #E5E5E5;\r\n border-radius: 2px;\r\n overflow: hidden;\r\n margin-right: 10px;\r\n width: 75px;\r\n}\r\n\r\n.progress-bar {\r\n height: 100%;\r\n background-image: linear-gradient(111deg, #4A9FFF 0%, #1058FF 100%);\r\n border-radius: 2px;\r\n transition: width 0.3s ease;\r\n}\r\n\r\n.progress-text {\r\n font-size: 14px;\r\n color: #666;\r\n white-space: nowrap;\r\n}\r\n\r\n/* 重新设计文本输入区域样式 */\r\n.text-input-area {\r\n display: flex;\r\n flex-direction: column;\r\n width: 100%;\r\n height: 100%;\r\n padding: 16px;\r\n border-radius: 8px;\r\n border: none; /* 确保容器本身没有边框 */\r\n}\r\n\r\n/* 修改文本输入框样式 */\r\n.text-answer-input {\r\n flex: 1;\r\n min-height: 80px;\r\n padding: 12px 12px 0px 12px;\r\n border: 1px solid #ddd;\r\n border-radius: 8px 8px 0 0;\r\n resize: none;\r\n font-size: 16px;\r\n background-color: #fff;\r\n border-bottom: none;\r\n outline: none; /* 移除默认的焦点轮廓 */\r\n}\r\n\r\n/* 修改工具栏样式 */\r\n.input-toolbar {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 8px 12px;\r\n background-color: #fff;\r\n border: 1px solid #ddd;\r\n border-top: none;\r\n border-radius: 0 0 8px 8px;\r\n}\r\n\r\n/* 当输入框获得焦点时,修改边框颜色 */\r\n.text-answer-input:focus {\r\n border-color: rgb(74, 144, 226);\r\n border-bottom: none;\r\n}\r\n\r\n.text-answer-input:focus + .input-toolbar {\r\n border-color: rgb(74, 144, 226);\r\n border-top: none;\r\n}\r\n\r\n/* 左侧工具按钮区域 */\r\n.toolbar-actions {\r\n display: flex;\r\n gap: 8px;\r\n}\r\n\r\n.toolbar-button {\r\n background: transparent;\r\n border: none;\r\n color: #666;\r\n padding: 4px;\r\n cursor: pointer;\r\n border-radius: 4px;\r\n}\r\n\r\n.toolbar-button:hover {\r\n background-color: #f0f0f0;\r\n}\r\n\r\n/* 发送按钮样式 */\r\n.submit-text-button {\r\n padding: 6px 16px;\r\n background-color: #4a90e2;\r\n color: white;\r\n border: none;\r\n border-radius: 4px;\r\n font-size: 14px;\r\n font-weight: 500;\r\n cursor: pointer;\r\n transition: background-color 0.2s;\r\n}\r\n\r\n.submit-text-button:hover:not(.disabled) {\r\n background-color: #3a7bc8;\r\n}\r\n\r\n.submit-text-button.disabled {\r\n background-color: #b3b3b3;\r\n cursor: not-allowed;\r\n}\r\n\r\n\r\n/* 响应式布局 */\r\n@media screen and (max-width: 768px) {\r\n .pc-layout {\r\n width: 95%;\r\n /* height: 90vh; */\r\n }\r\n\r\n .modal-overlay {\r\n padding: 0;\r\n }\r\n\r\n .modal-container.fullscreen {\r\n /* 支持 iOS Safari */\r\n height: -webkit-fill-available;\r\n max-height: -webkit-fill-available;\r\n /* 确保内容不会被顶部状态栏和底部工具栏遮挡 */\r\n padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom);\r\n }\r\n\r\n\r\n .modal-container.mobile-layout {\r\n width: 100%;\r\n height: 100vh;\r\n max-height: 100vh;\r\n min-height: 100vh;\r\n border-radius: 0;\r\n margin: 0;\r\n display: flex;\r\n flex-direction: column;\r\n }\r\n\r\n .chat-history {\r\n min-height: 200px;\r\n max-height: 350px;\r\n }\r\n}\r\n\r\n","import { Component, Prop, h, State, Event, EventEmitter, Element } from '@stencil/core';\r\nimport { convertWorkflowStreamNodeToMessageRound, UserInputMessageType, sendSSERequest, sendHttpRequest, uploadFileToBackend, API_DOMAIN } from '../../utils/utils';\r\nimport { ChatMessage } from '../../interfaces/chat';\r\n\r\n@Component({\r\n tag: 'pcm-app-chat-modal',\r\n styleUrl: 'pcm-app-chat-modal.css',\r\n shadow: true,\r\n})\r\nexport class ChatAPPModal {\r\n /**\r\n * 模态框标题\r\n */\r\n @Prop() modalTitle: string = '在线客服';\r\n\r\n /**\r\n * SDK鉴权密钥\r\n */\r\n @Prop({ attribute: 'token' }) token: string = '';\r\n\r\n /**\r\n * 是否显示聊天模态框\r\n */\r\n @Prop({ mutable: true }) isOpen: boolean = false;\r\n\r\n /**\r\n * 聊天消息历史\r\n */\r\n @State() messages: ChatMessage[] = [];\r\n\r\n\r\n /**\r\n * 当点击模态框关闭时触发\r\n */\r\n @Event() modalClosed: EventEmitter<void>;\r\n\r\n /**\r\n * 应用图标URL\r\n */\r\n @Prop() icon?: string;\r\n\r\n /**\r\n * 聊天框的页面层级\r\n */\r\n @Prop() zIndex?: number = 1000;\r\n\r\n /**\r\n * 是否展示顶部标题栏\r\n */\r\n @Prop() isShowHeader: boolean = true;\r\n\r\n /**\r\n * 是否展示右上角的关闭按钮\r\n */\r\n @Prop() isNeedClose: boolean = true;\r\n\r\n\r\n /**\r\n * 会话ID,传入继续对话,否则创建新会话\r\n */\r\n @Prop({ mutable: true }) conversationId?: string;\r\n\r\n /**\r\n * 当前助手回复的消息\r\n */\r\n @State() currentAssistantMessage: string = '';\r\n\r\n /**\r\n * 是否正在加载回复\r\n */\r\n @State() isLoading: boolean = false;\r\n\r\n /**\r\n * 当前正在流式输出的消息\r\n */\r\n @State() currentStreamingMessage: ChatMessage | null = null;\r\n\r\n // 添加新的状态控制\r\n @State() shouldAutoScroll: boolean = true;\r\n\r\n @State() isLoadingHistory: boolean = false;\r\n\r\n // 使用 @Element 装饰器获取组件的 host 元素\r\n @Element() hostElement: HTMLElement;\r\n\r\n /**\r\n * 一轮对话结束时的回调\r\n */\r\n @Event() streamComplete: EventEmitter<{\r\n conversation_id: string;\r\n event: string;\r\n message_id: string;\r\n id: string;\r\n }>;\r\n\r\n /**\r\n * 新会话开始的回调,只会在一轮对话开始时触发一次\r\n */\r\n @Event() conversationStart: EventEmitter<{\r\n conversation_id: string;\r\n event: string;\r\n message_id: string;\r\n id: string;\r\n }>;\r\n\r\n @State() selectedFile: File | null = null;\r\n @State() isUploading: boolean = false;\r\n @State() uploadedFileInfo: { cos_key: string, filename: string, ext: string, presigned_url: string }[] = [];\r\n\r\n /**\r\n * 默认查询文本\r\n */\r\n @Prop() defaultQuery: string = '';\r\n\r\n // 添加新的状态\r\n @State() showInitialUpload: boolean = false;\r\n\r\n\r\n // 添加视频录制相关状态\r\n @State() isRecording: boolean = false;\r\n @State() recordingStream: MediaStream | null = null;\r\n @State() recordedBlob: Blob | null = null;\r\n @State() mediaRecorder: MediaRecorder | null = null;\r\n @State() recordingTimeLeft: number = 0;\r\n @State() showRecordingUI: boolean = false;\r\n @State() recordingTimer: any = null;\r\n @State() recordingStartTime: number = 0;\r\n @State() recordingMaxTime: number = 120; // 最大录制时间(秒)\r\n @State() waitingToRecord: boolean = false;\r\n @State() waitingTimer: any = null;\r\n @State() waitingTimeLeft: number = 10; // 等待时间(秒)\r\n\r\n // 添加一个新的私有属性来存储视频元素的引用\r\n private videoRef: HTMLVideoElement | null = null;\r\n\r\n /**\r\n * 控制对话轮数\r\n */\r\n @Prop() totalQuestions: number = 2;\r\n\r\n /**\r\n * 当前轮数\r\n */\r\n @State() currentQuestionNumber: number = 0;\r\n\r\n\r\n /**\r\n * 当聊天完成时触发\r\n */\r\n @Event() interviewComplete: EventEmitter<{\r\n conversation_id: string;\r\n total_questions: number;\r\n }>;\r\n\r\n private readonly SCROLL_THRESHOLD = 30;\r\n\r\n /**\r\n * 视频录制最大时长(秒)\r\n */\r\n @Prop() maxRecordingTime: number = 120;\r\n\r\n /**\r\n * 录制倒计时提醒时间(秒)\r\n * 当剩余时间小于此值时,显示倒计时警告\r\n */\r\n @Prop() countdownWarningTime: number = 30;\r\n\r\n @State() showCountdownWarning: boolean = false;\r\n\r\n /**\r\n * 是否以全屏模式打开,移动端建议设置为true\r\n */\r\n @Prop() fullscreen: boolean = false;\r\n\r\n // 添加新的状态来跟踪视频上传\r\n @State() isUploadingVideo: boolean = false;\r\n\r\n // 添加新的状态和属性\r\n @State() isPlayingAudio: boolean = false;\r\n @State() audioUrl: string | null = null;\r\n private audioElement: HTMLAudioElement | null = null;\r\n\r\n /**\r\n * 录制错误事件\r\n */\r\n @Event() recordingError: EventEmitter<{\r\n type: string;\r\n message: string;\r\n details?: any;\r\n }>;\r\n\r\n /**\r\n * 录制状态变化事件\r\n */\r\n @Event() recordingStatusChange: EventEmitter<{\r\n status: 'started' | 'stopped' | 'paused' | 'resumed' | 'failed';\r\n details?: any;\r\n }>;\r\n\r\n /**\r\n * 是否启用语音播报功能\r\n * true: 启用语音合成\r\n * false: 禁用语音合成\r\n */\r\n @Prop() enableTTS: boolean = false;\r\n\r\n /**\r\n * 是否自动播放语音问题\r\n */\r\n @Prop() enableVoice: boolean = false;\r\n\r\n /**\r\n * 是否显示题干内容\r\n * 1: 显示题干内容\r\n * 0: 不显示题干内容\r\n */\r\n @Prop() displayContentStatus: string = \"1\";\r\n\r\n\r\n /**\r\n * 面试模式\r\n * video: 视频面试模式\r\n * text: 文字面试模式\r\n */\r\n @Prop() interviewMode: 'video' | 'text' = 'video';\r\n\r\n // 添加文字输入相关状态\r\n @State() textAnswer: string = '';\r\n @State() isSubmittingText: boolean = false;\r\n\r\n /**\r\n * 自定义智能体inputs输入参数\r\n */\r\n @Prop() customInputs: Record<string, any> = {};\r\n\r\n /**\r\n * 机器人ID\r\n */\r\n @Prop() botId?: string;\r\n\r\n\r\n\r\n private handleClose = () => {\r\n this.stopRecording();\r\n this.modalClosed.emit();\r\n };\r\n\r\n\r\n private async sendMessageToAPI(message: string, videoUrl?: string) {\r\n this.isLoading = true;\r\n let answer = '';\r\n let llmText = ''; // 添加变量存储 LLMText\r\n\r\n const now = new Date();\r\n const time = `${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`;\r\n\r\n // 修改消息处理逻辑,移除文件上传相关代码\r\n const queryText = message.trim() || '请开始';\r\n\r\n // 检查是否是最后一题\r\n const isLastQuestion = this.currentQuestionNumber >= this.totalQuestions;\r\n\r\n // 创建新的消息对象\r\n const newMessage: ChatMessage = {\r\n id: `temp-${Date.now()}`, // 消息唯一标识\r\n time: time, // 消息时间\r\n query: queryText, // 用户输入的消息内容\r\n answer: '',\r\n isStreaming: true, // 是否正在流式输出\r\n conversation_id: this.conversationId, // 会话ID\r\n inputs: this.customInputs, // 输入参数\r\n status: \"normal\", // 消息状态\r\n error: null // 错误信息\r\n };\r\n\r\n // 设置当前流式消息\r\n this.currentStreamingMessage = newMessage;\r\n\r\n this.shouldAutoScroll = true;\r\n // 滚动到底部\r\n this.scrollToBottom();\r\n\r\n // 如果是最后一题,直接显示结束消息并完成面试\r\n if (isLastQuestion) {\r\n this.messages = [...this.messages, newMessage];\r\n this.currentStreamingMessage = null;\r\n this.isLoading = false;\r\n await this.completeInterview();\r\n this.interviewComplete.emit({\r\n conversation_id: this.conversationId,\r\n total_questions: this.totalQuestions\r\n });\r\n return;\r\n }\r\n\r\n // 准备请求数据\r\n const requestData: any = {\r\n response_mode: 'streaming',\r\n conversation_id: this.conversationId,\r\n query: queryText,\r\n bot_id: this.botId // 添加 botId 到请求数据中\r\n };\r\n\r\n // 合并基本输入参数和自定义输入参数\r\n requestData.inputs = {\r\n // 合并自定义输入参数\r\n ...this.customInputs\r\n };\r\n\r\n // 如果有视频URL,添加到inputs中\r\n if (videoUrl) {\r\n requestData.inputs.video_url = videoUrl;\r\n }\r\n\r\n await sendSSERequest({\r\n url: `/sdk/v1/chat/chat-messages`,\r\n method: 'POST',\r\n headers: {\r\n 'authorization': 'Bearer ' + this.token\r\n },\r\n data: requestData,\r\n onMessage: (data) => {\r\n // console.log('收到Stream数据:', data);\r\n\r\n if (data.conversation_id && !this.conversationId) {\r\n this.conversationId = data.conversation_id;\r\n this.conversationStart.emit({\r\n conversation_id: data.conversation_id,\r\n event: data.event,\r\n message_id: data.message_id,\r\n id: data.id,\r\n });\r\n }\r\n\r\n // 检查是否有 node_finished 事件和 LLMText\r\n if (data.event === 'node_finished' && data.data.inputs && data.data.inputs.LLMText) {\r\n llmText = data.data.inputs.LLMText;\r\n }\r\n\r\n if (data.event === 'message') {\r\n const inputMessage: UserInputMessageType = { message: message };\r\n convertWorkflowStreamNodeToMessageRound('message', inputMessage, data);\r\n\r\n if (data.event === 'agent_message' || data.event === 'message') {\r\n if (data.answer) {\r\n answer += data.answer;\r\n const updatedMessage: ChatMessage = {\r\n ...this.currentStreamingMessage,\r\n answer,\r\n isStreaming: true\r\n };\r\n this.currentStreamingMessage = updatedMessage;\r\n this.scrollToBottom();\r\n }\r\n }\r\n }\r\n if (data.event === \"message_end\") {\r\n this.streamComplete.emit({\r\n conversation_id: data.conversation_id || '',\r\n event: data.event,\r\n message_id: data.message_id,\r\n id: data.id,\r\n });\r\n }\r\n },\r\n onError: (error) => {\r\n console.error('发生错误:', error);\r\n alert(error instanceof Error ? error.message : '消息发送失败,请稍后再试');\r\n this.messages = [...this.messages, {\r\n ...newMessage,\r\n answer: '抱歉,发生了错误,请稍后再试。',\r\n error: error,\r\n isStreaming: false\r\n }];\r\n this.currentStreamingMessage = null;\r\n this.isLoading = false;\r\n },\r\n onComplete: async () => {\r\n this.isLoading = false;\r\n\r\n // 获取最新的AI回复内容\r\n const latestAIMessage = this.currentStreamingMessage;\r\n\r\n // 更新消息列表\r\n this.messages = [...this.messages, this.currentStreamingMessage];\r\n this.currentStreamingMessage = null;\r\n\r\n // 如果是初始消息或\"下一题\"消息,增加题目计数\r\n if (message === \"下一题\" || this.currentQuestionNumber === 0) {\r\n this.currentQuestionNumber++;\r\n }\r\n\r\n if (latestAIMessage && latestAIMessage.answer) {\r\n // 优先使用 LLMText,如果没有则使用 answer\r\n const textForSynthesis = llmText || latestAIMessage.answer;\r\n\r\n if (textForSynthesis && this.enableTTS) {\r\n // 合成语音\r\n const audioUrl = await this.synthesizeAudio(textForSynthesis);\r\n\r\n if (this.enableVoice) {\r\n // 自动播放语音\r\n await this.playAudio(audioUrl);\r\n // 自动播放模式下,只在视频模式时开始等待录制\r\n if (this.interviewMode === 'video') {\r\n this.startWaitingToRecord();\r\n }\r\n } else {\r\n // 只保存音频URL,不自动播放\r\n this.audioUrl = audioUrl;\r\n }\r\n } else {\r\n // 如果禁用了语音合成,只在视频模式时开始等待录制\r\n if (this.interviewMode === 'video') {\r\n this.startWaitingToRecord();\r\n }\r\n }\r\n }\r\n }\r\n });\r\n }\r\n\r\n\r\n // 监听滚动事件,用于控制聊天历史记录的自动滚动行为。\r\n private handleScroll = () => {\r\n const chatHistory = this.hostElement.shadowRoot?.querySelector('.chat-history');\r\n if (!chatHistory) return;\r\n\r\n const { scrollTop, scrollHeight, clientHeight } = chatHistory;\r\n const distanceFromBottom = scrollHeight - scrollTop - clientHeight;\r\n\r\n // 更新是否应该自动滚动的状态\r\n this.shouldAutoScroll = distanceFromBottom <= this.SCROLL_THRESHOLD;\r\n };\r\n\r\n private scrollToBottom() {\r\n if (!this.shouldAutoScroll) return;\r\n const chatHistory = this.hostElement.shadowRoot?.querySelector('.chat-history');\r\n if (chatHistory && this.isOpen) {\r\n // 强制浏览器重新计算布局\r\n chatHistory.scrollTop = chatHistory.scrollHeight;\r\n }\r\n }\r\n\r\n // 添加 componentDidRender 生命周期方法,用于在组件渲染后滚动到底部\r\n componentDidRender() {\r\n if (this.isLoadingHistory || (this.shouldAutoScroll && this.isOpen)) {\r\n const chatHistory = this.hostElement.shadowRoot?.querySelector('.chat-history');\r\n if (chatHistory) {\r\n chatHistory.scrollTop = chatHistory.scrollHeight;\r\n }\r\n }\r\n }\r\n\r\n\r\n // 修改 loadHistoryMessages 方法\r\n private async loadHistoryMessages() {\r\n if (!this.conversationId) return;\r\n\r\n this.isLoadingHistory = true;\r\n console.log('加载历史消息...');\r\n\r\n try {\r\n const result = await sendHttpRequest({\r\n url: '/sdk/v1/chat/messages',\r\n method: 'GET',\r\n headers: {\r\n 'authorization': 'Bearer ' + this.token\r\n },\r\n data: {\r\n conversation_id: this.conversationId,\r\n bot_id: this.botId,\r\n limit: 20\r\n }\r\n });\r\n\r\n if (result.success && result.data) {\r\n const historyData = result.data.data || [];\r\n const formattedMessages: ChatMessage[] = historyData.map(msg => {\r\n const time = new Date(msg.created_at * 1000);\r\n const hours = time.getHours().toString().padStart(2, '0');\r\n const minutes = time.getMinutes().toString().padStart(2, '0');\r\n const timeStr = `${hours}:${minutes}`;\r\n\r\n // 创建新的消息对象,不包含 inputs 字段\r\n const { inputs, ...msgWithoutInputs } = msg;\r\n\r\n return {\r\n ...msgWithoutInputs,\r\n time: timeStr,\r\n isStreaming: false,\r\n status: msg.status === 'error' ? 'error' : 'normal' as const\r\n };\r\n });\r\n\r\n this.messages = formattedMessages;\r\n \r\n requestAnimationFrame(() => {\r\n this.shouldAutoScroll = true;\r\n this.scrollToBottom();\r\n });\r\n }\r\n } catch (error) {\r\n console.error('加载历史消息失败:', error);\r\n alert(error instanceof Error ? error.message : '加载历史消息失败,请刷新重试');\r\n } finally {\r\n this.isLoadingHistory = false;\r\n }\r\n }\r\n\r\n // 修改 componentDidLoad 生命周期方法\r\n componentDidLoad() {\r\n\r\n // 添加滚动事件监听器\r\n const chatHistory = this.hostElement.shadowRoot?.querySelector('.chat-history');\r\n if (chatHistory) {\r\n chatHistory.addEventListener('scroll', this.handleScroll);\r\n }\r\n }\r\n\r\n // 添加 componentWillLoad 生命周期方法\r\n componentWillLoad() {\r\n\r\n // 如果组件加载时已经是打开状态,则直接开始对话\r\n if (this.isOpen) {\r\n if (this.conversationId) {\r\n // 在下一个事件循环中加载历史消息,避免在componentWillLoad中进行异步操作\r\n setTimeout(() => this.loadHistoryMessages(), 0);\r\n } else {\r\n // 在下一个事件循环中发送初始消息,避免在componentWillLoad中进行异步操作\r\n setTimeout(() => this.sendMessageToAPI(this.defaultQuery), 0);\r\n }\r\n }\r\n }\r\n\r\n // 开始等待录制\r\n private startWaitingToRecord() {\r\n // 如果不是视频模式,直接返回\r\n if (this.interviewMode !== 'video') {\r\n return;\r\n }\r\n\r\n // 清除可能存在的计时器\r\n if (this.waitingTimer) {\r\n clearInterval(this.waitingTimer);\r\n }\r\n if (this.recordingTimer) {\r\n clearInterval(this.recordingTimer);\r\n }\r\n\r\n this.waitingToRecord = true;\r\n this.waitingTimeLeft = 10;\r\n\r\n this.waitingTimer = setInterval(() => {\r\n this.waitingTimeLeft--;\r\n if (this.waitingTimeLeft <= 0) {\r\n clearInterval(this.waitingTimer);\r\n this.waitingTimer = null;\r\n this.waitingToRecord = false;\r\n this.startRecording();\r\n }\r\n }, 1000);\r\n }\r\n\r\n // 开始录制视频\r\n private async startRecording() {\r\n try {\r\n const stream = await navigator.mediaDevices.getUserMedia({\r\n audio: true,\r\n video: {\r\n width: { ideal: 1280 },\r\n height: { ideal: 720 },\r\n frameRate: { ideal: 30 }\r\n }\r\n });\r\n\r\n this.recordingStream = stream;\r\n this.showRecordingUI = true;\r\n this.showCountdownWarning = false;\r\n\r\n // 重置视频引用\r\n this.videoRef = null;\r\n\r\n // 确保视频元素获取到流\r\n this.setupVideoPreview(stream);\r\n\r\n // 检测浏览器支持的MIME类型\r\n const mimeType = this.getSupportedMimeType();\r\n\r\n // 创建MediaRecorder实例\r\n let mediaRecorder;\r\n try {\r\n mediaRecorder = new MediaRecorder(stream, {\r\n mimeType: mimeType\r\n });\r\n } catch (e) {\r\n // 如果指定MIME类型失败,尝试使用默认设置\r\n console.warn('指定的MIME类型不受支持,使用默认设置:', e);\r\n try {\r\n mediaRecorder = new MediaRecorder(stream);\r\n } catch (recorderError) {\r\n // 通知父组件录制器创建失败\r\n this.recordingError.emit({\r\n type: 'recorder_creation_failed',\r\n message: '无法创建媒体录制器,您的浏览器可能不支持此功能',\r\n details: recorderError\r\n });\r\n this.showRecordingUI = false;\r\n return;\r\n }\r\n }\r\n\r\n this.mediaRecorder = mediaRecorder;\r\n\r\n const chunks: BlobPart[] = [];\r\n\r\n mediaRecorder.ondataavailable = (event) => {\r\n if (event.data.size > 0) {\r\n chunks.push(event.data);\r\n }\r\n };\r\n\r\n mediaRecorder.onerror = (event) => {\r\n // 通知父组件录制过程中发生错误\r\n this.recordingError.emit({\r\n type: 'recording_error',\r\n message: '录制过程中发生错误',\r\n details: event\r\n });\r\n this.stopRecording();\r\n };\r\n\r\n mediaRecorder.onstop = () => {\r\n try {\r\n // 根据实际使用的MIME类型创建Blob\r\n const blobType = mimeType || 'video/mp4';\r\n const blob = new Blob(chunks, { type: blobType });\r\n\r\n if (blob.size === 0) {\r\n // 通知父组件录制的视频为空\r\n this.recordingError.emit({\r\n type: 'empty_recording',\r\n message: '录制的视频为空'\r\n });\r\n this.showRecordingUI = false;\r\n return;\r\n }\r\n\r\n this.recordedBlob = blob;\r\n\r\n // 通知父组件录制已完成\r\n this.recordingStatusChange.emit({\r\n status: 'stopped',\r\n details: {\r\n duration: Math.floor((Date.now() - this.recordingStartTime) / 1000),\r\n size: blob.size,\r\n type: blob.type\r\n }\r\n });\r\n\r\n this.uploadRecordedVideo();\r\n } catch (error) {\r\n // 通知父组件处理录制视频时出错\r\n this.recordingError.emit({\r\n type: 'processing_error',\r\n message: '处理录制视频时出错',\r\n details: error\r\n });\r\n this.showRecordingUI = false;\r\n }\r\n };\r\n\r\n // 开始录制\r\n try {\r\n mediaRecorder.start();\r\n this.isRecording = true;\r\n this.recordingStartTime = Date.now();\r\n this.recordingTimeLeft = this.maxRecordingTime;\r\n\r\n // 通知父组件录制已开始\r\n this.recordingStatusChange.emit({\r\n status: 'started',\r\n details: {\r\n maxDuration: this.maxRecordingTime,\r\n mimeType: mediaRecorder.mimeType\r\n }\r\n });\r\n } catch (startError) {\r\n // 通知父组件开始录制失败\r\n this.recordingError.emit({\r\n type: 'start_failed',\r\n message: '开始录制失败,请检查您的设备权限',\r\n details: startError\r\n });\r\n this.showRecordingUI = false;\r\n return;\r\n }\r\n\r\n // 设置录制计时器\r\n this.recordingTimer = setInterval(() => {\r\n const elapsedTime = Math.floor((Date.now() - this.recordingStartTime) / 1000);\r\n this.recordingTimeLeft = Math.max(0, this.maxRecordingTime - elapsedTime);\r\n\r\n // 检查是否需要显示倒计时警告\r\n if (this.recordingTimeLeft <= this.countdownWarningTime && !this.showCountdownWarning) {\r\n this.showCountdownWarning = true;\r\n }\r\n\r\n // 时间到自动停止录制\r\n if (this.recordingTimeLeft <= 0) {\r\n this.stopRecording();\r\n }\r\n }, 1000);\r\n\r\n } catch (error) {\r\n console.error('无法访问摄像头或麦克风:', error);\r\n // 通知父组件无法访问媒体设备\r\n this.recordingError.emit({\r\n type: 'media_access_failed',\r\n message: '无法访问摄像头或麦克风,请确保已授予权限',\r\n details: error\r\n });\r\n this.showRecordingUI = false;\r\n }\r\n }\r\n\r\n // 添加新方法来设置视频预览\r\n private setupVideoPreview(stream: MediaStream) {\r\n // 延迟执行以确保DOM已更新\r\n setTimeout(() => {\r\n const videoElement = this.hostElement.shadowRoot?.querySelector('video') as HTMLVideoElement;\r\n if (videoElement && stream) {\r\n // 先尝试使用标准方法\r\n try {\r\n videoElement.srcObject = stream;\r\n videoElement.play().catch(err => {\r\n console.error('视频播放失败:', err);\r\n });\r\n } catch (e) {\r\n console.warn('设置srcObject失败,尝试替代方法:', e);\r\n\r\n // 对于不支持srcObject的旧浏览器,使用URL.createObjectURL\r\n try {\r\n // 使用类型断言解决TypeScript错误\r\n const objectUrl = URL.createObjectURL(stream as unknown as MediaSource);\r\n videoElement.src = objectUrl;\r\n\r\n // 确保在视频元素不再使用时释放URL\r\n videoElement.onended = () => {\r\n URL.revokeObjectURL(objectUrl);\r\n };\r\n } catch (urlError) {\r\n console.error('创建对象URL失败:', urlError);\r\n }\r\n }\r\n } else {\r\n console.warn('未找到视频元素或媒体流无效');\r\n }\r\n }, 100);\r\n }\r\n\r\n // 添加一个新方法来检测浏览器支持的MIME类型\r\n private getSupportedMimeType(): string {\r\n // 按优先级排列的MIME类型列表\r\n const mimeTypes = [\r\n 'video/webm;codecs=vp8,opus',\r\n 'video/webm;codecs=vp9,opus',\r\n 'video/webm',\r\n 'video/mp4',\r\n 'video/mp4;codecs=h264,aac',\r\n '' // 空字符串表示使用浏览器默认值\r\n ];\r\n\r\n // 检查MediaRecorder是否可用\r\n if (!window.MediaRecorder) {\r\n console.warn('MediaRecorder API不可用');\r\n return '';\r\n }\r\n\r\n // 检查每种MIME类型是否受支持\r\n for (const type of mimeTypes) {\r\n if (!type) return ''; // 如果是空字符串,直接返回\r\n\r\n try {\r\n if (MediaRecorder.isTypeSupported(type)) {\r\n return type;\r\n }\r\n } catch (e) {\r\n console.warn(`检查MIME类型支持时出错 ${type}:`, e);\r\n }\r\n }\r\n\r\n // 如果没有找到支持的类型,返回空字符串\r\n console.warn('没有找到支持的MIME类型,将使用浏览器默认值');\r\n return '';\r\n }\r\n\r\n // 停止录制\r\n private stopRecording() {\r\n if (this.mediaRecorder && this.isRecording) {\r\n this.mediaRecorder.stop();\r\n this.isRecording = false;\r\n\r\n // 清理计时器\r\n if (this.recordingTimer) {\r\n clearInterval(this.recordingTimer);\r\n this.recordingTimer = null;\r\n }\r\n\r\n // 停止并释放媒体流\r\n if (this.recordingStream) {\r\n this.recordingStream.getTracks().forEach(track => track.stop());\r\n this.recordingStream = null;\r\n }\r\n\r\n // 清理视频引用\r\n this.videoRef = null;\r\n }\r\n }\r\n\r\n // 修改音频转文字方法\r\n private async convertAudioToText(cosKey: string): Promise<string | null> {\r\n try {\r\n const result = await sendHttpRequest<{ text: string }>({\r\n url: '/sdk/v1/tts/audio_to_text',\r\n method: 'POST',\r\n headers: {\r\n 'authorization': 'Bearer ' + this.token\r\n },\r\n data: {\r\n cos_key: cosKey\r\n }\r\n });\r\n \r\n if (result.success && result.data && result.data.text) {\r\n return result.data.text;\r\n } else {\r\n console.warn('音频转文字返回结果格式不正确');\r\n return null;\r\n }\r\n } catch (error) {\r\n console.error('音频转文字错误:', error);\r\n return null;\r\n }\r\n }\r\n\r\n // 上传录制的视频\r\n private async uploadRecordedVideo() {\r\n if (!this.recordedBlob) return;\r\n\r\n try {\r\n this.isUploadingVideo = true; // 开始上传时设置状态\r\n this.showRecordingUI = false; // 隐藏视频预览\r\n\r\n // 根据Blob类型确定文件扩展名\r\n const fileExtension = this.recordedBlob.type.includes('webm') ? 'webm' : 'mp4';\r\n const fileName = `answer.${fileExtension}`;\r\n\r\n // 创建File对象\r\n const file = new File([this.recordedBlob], fileName, { type: this.recordedBlob.type });\r\n \r\n // 使用uploadFileToBackend上传文件\r\n const fileInfo = await uploadFileToBackend(file, {\r\n 'authorization': 'Bearer ' + this.token\r\n });\r\n \r\n // 调用音频转文字API\r\n const transcriptionText = await this.convertAudioToText(fileInfo.cos_key);\r\n\r\n // 发送\"下一题\"请求,可以附带转录文本\r\n this.sendMessageToAPI(transcriptionText || \"下一题\");\r\n } catch (error) {\r\n console.error('视频上传或处理错误:', error);\r\n // 通知父组件视频上传失败\r\n this.recordingError.emit({\r\n type: 'upload_failed',\r\n message: '视频上传或处理失败',\r\n details: error\r\n });\r\n } finally {\r\n this.isUploadingVideo = false; // 上传完成后重置状态\r\n this.showRecordingUI = false;\r\n this.recordedBlob = null;\r\n }\r\n }\r\n\r\n\r\n /**\r\n * 发送面试完成请求\r\n */\r\n private async completeInterview() {\r\n if (!this.conversationId) return;\r\n\r\n try {\r\n const requestData: any = {\r\n response_mode: 'streaming',\r\n conversation_id: this.conversationId,\r\n query: \"面试完成\",\r\n inputs: {\r\n // 合并自定义输入参数\r\n ...this.customInputs\r\n }\r\n };\r\n\r\n // 使用sendHttpRequest发送请求\r\n sendHttpRequest({\r\n url: '/sdk/v1/chat/chat-messages',\r\n method: 'POST',\r\n headers: {\r\n 'authorization': 'Bearer ' + this.token\r\n },\r\n data: requestData\r\n }).catch(error => {\r\n console.error('发送面试完成请求失败:', error);\r\n });\r\n\r\n } catch (error) {\r\n console.error('发送面试完成请求失败:', error);\r\n }\r\n }\r\n\r\n // 添加TTS合成音频的方法\r\n private async synthesizeAudio(text: string): Promise<string> {\r\n try {\r\n const response = await fetch(`${API_DOMAIN}/sdk/v1/tts/synthesize_audio`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'authorization': 'Bearer ' + this.token\r\n },\r\n body: JSON.stringify({ text })\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error('语音合成失败');\r\n }\r\n\r\n // 获取音频数据并创建Blob URL\r\n const audioBlob = await response.blob();\r\n return URL.createObjectURL(audioBlob);\r\n } catch (error) {\r\n console.error('语音合成错误:', error);\r\n throw error;\r\n }\r\n }\r\n\r\n // 播放音频的方法\r\n private playAudio(audioUrl: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n this.isPlayingAudio = true;\r\n this.audioUrl = audioUrl;\r\n\r\n // 创建音频元素\r\n if (!this.audioElement) {\r\n this.audioElement = new Audio();\r\n }\r\n\r\n this.audioElement.src = audioUrl;\r\n this.audioElement.onended = () => {\r\n this.isPlayingAudio = false;\r\n this.audioUrl = null;\r\n resolve();\r\n };\r\n\r\n this.audioElement.onerror = () => {\r\n console.error('音频播放错误');\r\n this.isPlayingAudio = false;\r\n this.audioUrl = null;\r\n resolve();\r\n };\r\n\r\n this.audioElement.play().catch(error => {\r\n console.error('音频播放失败:', error);\r\n this.isPlayingAudio = false;\r\n this.audioUrl = null;\r\n resolve();\r\n });\r\n });\r\n }\r\n\r\n // 修改 componentDidLoad 生命周期方法,确保组件卸载时释放资源\r\n disconnectedCallback() {\r\n // 移除滚动事件监听器\r\n const chatHistory = this.hostElement.shadowRoot?.querySelector('.chat-history');\r\n if (chatHistory) {\r\n chatHistory.removeEventListener('scroll', this.handleScroll);\r\n }\r\n\r\n // 释放音频资源\r\n if (this.audioElement) {\r\n this.audioElement.pause();\r\n this.audioElement.src = '';\r\n this.audioElement = null;\r\n }\r\n\r\n // 释放 Blob URL\r\n if (this.audioUrl) {\r\n URL.revokeObjectURL(this.audioUrl);\r\n this.audioUrl = null;\r\n }\r\n\r\n // 清理其他计时器\r\n if (this.waitingTimer) {\r\n clearInterval(this.waitingTimer);\r\n this.waitingTimer = null;\r\n }\r\n\r\n if (this.recordingTimer) {\r\n clearInterval(this.recordingTimer);\r\n this.recordingTimer = null;\r\n }\r\n\r\n // 停止录制\r\n this.stopRecording();\r\n }\r\n\r\n // 修改手动播放音频的方法\r\n private handlePlayAudio = async () => {\r\n if (this.audioUrl) {\r\n await this.playAudio(this.audioUrl);\r\n // 手动播放完成后只在视频模式时开始等待录制\r\n if (this.interviewMode === 'video') {\r\n this.startWaitingToRecord();\r\n }\r\n }\r\n };\r\n\r\n // 处理文本输入变化\r\n private handleTextInputChange = (event: Event) => {\r\n const input = event.target as HTMLTextAreaElement;\r\n this.textAnswer = input.value;\r\n };\r\n\r\n // 添加处理键盘事件的方法\r\n private handleKeyDown = (event: KeyboardEvent) => {\r\n // 如果按下的是回车键\r\n if (event.key === 'Enter') {\r\n // 如果同时按下了Ctrl键,允许换行\r\n if (event.ctrlKey) {\r\n return; // 不阻止默认行为,允许插入换行符\r\n } else {\r\n // 阻止默认的换行行为\r\n event.preventDefault();\r\n // 如果文本框不为空且不处于禁用状态,则发送消息\r\n if (this.textAnswer.trim() && !this.isSubmittingText && !this.isLoading &&\r\n !this.currentStreamingMessage && !this.waitingToRecord && !this.isPlayingAudio) {\r\n this.submitTextAnswer();\r\n }\r\n }\r\n }\r\n };\r\n\r\n // 修改提交文本回答的方法\r\n private submitTextAnswer = async () => {\r\n if (!this.textAnswer.trim() || this.isSubmittingText) {\r\n return;\r\n }\r\n\r\n this.isSubmittingText = true;\r\n\r\n try {\r\n // 保存当前输入内容\r\n const textToSend = this.textAnswer;\r\n \r\n // 立即清空文本输入\r\n this.textAnswer = '';\r\n \r\n // 发送用户输入的文本作为查询\r\n await this.sendMessageToAPI(textToSend);\r\n } catch (error) {\r\n console.error('提交文本回答失败:', error);\r\n alert('提交回答失败,请重试');\r\n } finally {\r\n this.isSubmittingText = false;\r\n }\r\n };\r\n\r\n render() {\r\n if (!this.isOpen) return null;\r\n\r\n const modalStyle = {\r\n zIndex: String(this.zIndex)\r\n };\r\n\r\n const containerClass = {\r\n 'modal-container': true,\r\n 'fullscreen': this.fullscreen\r\n };\r\n\r\n const overlayClass = {\r\n 'modal-overlay': true,\r\n 'fullscreen-overlay': this.fullscreen\r\n };\r\n\r\n const renderVideoPreview = () => (\r\n <div class=\"video-preview\">\r\n <video\r\n autoPlay\r\n playsInline\r\n muted\r\n style={{ transform: 'scaleX(-1)' }}\r\n ref={(el) => {\r\n if (el && this.recordingStream && !this.videoRef) {\r\n this.videoRef = el;\r\n // 不在这里设置srcObject,而是使用setupVideoPreview方法\r\n }\r\n }}\r\n ></video>\r\n <div class={{\r\n 'recording-status': true,\r\n 'warning': this.showCountdownWarning\r\n }}>\r\n <span class=\"recording-dot\"></span>\r\n <span>\r\n 录制中 {Math.floor(this.recordingTimeLeft / 60)}:{(this.recordingTimeLeft % 60).toString().padStart(2, '0')}\r\n {this.showCountdownWarning && ` (即将自动完成)`}\r\n </span>\r\n </div>\r\n </div>\r\n );\r\n\r\n // 渲染占位符状态信息\r\n const renderPlaceholderStatus = () => {\r\n // 正在播放音频\r\n if (this.isPlayingAudio) {\r\n return (\r\n <div class=\"placeholder-status\">\r\n <p>正在播放问题,请听完后准备回答...</p>\r\n </div>\r\n );\r\n }\r\n\r\n // 正在上传视频\r\n if (this.isUploadingVideo) {\r\n return (\r\n <div class=\"placeholder-status\">\r\n <p>正在上传视频,请稍候...</p>\r\n </div>\r\n );\r\n }\r\n\r\n // 正在加载或等待AI回复\r\n if (this.isLoading || this.currentStreamingMessage) {\r\n return (\r\n <div class=\"placeholder-status\">\r\n <p>请等待题目...</p>\r\n </div>\r\n );\r\n }\r\n\r\n // 等待开始录制\r\n if (this.waitingToRecord) {\r\n return (\r\n <div class=\"placeholder-status\">\r\n <p>请准备好,{this.waitingTimeLeft}秒后将开始录制您的回答...</p>\r\n </div>\r\n );\r\n }\r\n\r\n // 添加默认状态\r\n return (\r\n <div class=\"placeholder-status default-status\">\r\n <p>准备中...</p>\r\n </div>\r\n );\r\n };\r\n\r\n // 修改文本输入区域渲染函数\r\n const renderTextInputArea = () => (\r\n <div class=\"text-input-area\">\r\n <textarea\r\n class=\"text-answer-input\"\r\n placeholder=\"请输入...(按回车发送,Ctrl+回车换行)\"\r\n value={this.textAnswer}\r\n onInput={this.handleTextInputChange}\r\n onKeyDown={this.handleKeyDown}\r\n disabled={this.isSubmittingText || this.isLoading || !!this.currentStreamingMessage || this.waitingToRecord || this.isPlayingAudio}\r\n ></textarea>\r\n <div class=\"input-toolbar\">\r\n <div class=\"toolbar-actions\">\r\n {/* <button class=\"toolbar-button\" title=\"表情\" disabled>\r\n <svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"currentColor\">\r\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-5-6c.78 2.34 2.72 4 5 4s4.22-1.66 5-4H7zm1.5-5a1.5 1.5 0 100 3 1.5 1.5 0 000-3zm7 0a1.5 1.5 0 100 3 1.5 1.5 0 000-3z\" />\r\n </svg>\r\n </button>\r\n <button class=\"toolbar-button\" title=\"图片\" disabled>\r\n <svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"currentColor\">\r\n <path d=\"M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4.86 8.86l-3 3.87L9 13.14 6 17h12l-3.86-5.14z\" />\r\n </svg>\r\n </button> */}\r\n </div>\r\n <button\r\n class={{\r\n 'submit-text-button': true,\r\n 'disabled': !this.textAnswer.trim() || this.isSubmittingText || this.isLoading || !!this.currentStreamingMessage || this.waitingToRecord || this.isPlayingAudio\r\n }}\r\n disabled={!this.textAnswer.trim() || this.isSubmittingText || this.isLoading || !!this.currentStreamingMessage || this.waitingToRecord || this.isPlayingAudio}\r\n onClick={this.submitTextAnswer}\r\n >\r\n {this.isSubmittingText ? '发送中...' : '发送'}\r\n </button>\r\n </div>\r\n </div>\r\n );\r\n\r\n return (\r\n <div class={overlayClass} style={modalStyle}>\r\n <div class={containerClass}>\r\n {this.isShowHeader && (\r\n <div class=\"modal-header\">\r\n <div class=\"header-left\">\r\n {this.icon && <img src={this.icon} class=\"header-icon\" alt=\"应用图标\" />}\r\n <div>{this.modalTitle}</div>\r\n </div>\r\n {this.isNeedClose && (\r\n <button class=\"close-button\" onClick={this.handleClose}>\r\n <span>×</span>\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n <div style={{ height: '100%' }}>\r\n <div class=\"chat-history\" onScroll={this.handleScroll}>\r\n {this.isLoadingHistory ? (\r\n <div class=\"loading-container\">\r\n <div class=\"loading-spinner\"></div>\r\n <p>加载历史消息中...</p>\r\n </div>\r\n ) : (\r\n <div>\r\n {this.messages.map((message) => (\r\n <div id={`message_${message.id}`} key={message.id}>\r\n <pcm-chat-message\r\n message={message}\r\n onMessageChange={(event) => {\r\n const updatedMessages = this.messages.map(msg =>\r\n msg.id === message.id ? { ...msg, ...event.detail } : msg\r\n );\r\n this.messages = updatedMessages;\r\n }}\r\n ></pcm-chat-message>\r\n </div>\r\n ))}\r\n {this.currentStreamingMessage && (\r\n <div id={`message_${this.currentStreamingMessage.id}`}>\r\n <pcm-chat-message\r\n message={this.currentStreamingMessage}\r\n ></pcm-chat-message>\r\n </div>\r\n )}\r\n {this.messages.length === 0 && !this.currentStreamingMessage && (\r\n <div class=\"empty-state\">\r\n <p>正在准备面试...</p>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n <div class=\"recording-section\">\r\n <div class=\"recording-container\">\r\n {\r\n this.interviewMode === 'text' && (\r\n renderTextInputArea()\r\n )\r\n }\r\n {this.interviewMode === 'video' && (\r\n <div style={{ width: '100%', display: 'flex', flexWrap: 'wrap', justifyContent: 'center' }}>\r\n <div class=\"video-area\">\r\n {this.showRecordingUI ? (\r\n renderVideoPreview()\r\n ) : (\r\n <div class=\"video-preview placeholder\">\r\n {renderPlaceholderStatus()}\r\n </div>\r\n )}\r\n <div class=\"progress-container\">\r\n <div class=\"progress-bar-container\">\r\n <div\r\n class=\"progress-bar\"\r\n style={{\r\n width: `${Math.max(0, this.currentQuestionNumber - 1) / this.totalQuestions * 100}%`\r\n }}\r\n ></div>\r\n </div>\r\n <div class=\"progress-text\">\r\n 已完成{Math.max(0, this.currentQuestionNumber - 1)}/{this.totalQuestions}\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"recording-controls\">\r\n {this.showRecordingUI ? (\r\n <button\r\n class=\"stop-recording-button\"\r\n onClick={() => this.stopRecording()}\r\n >\r\n 完成本题回答\r\n </button>\r\n ) : (\r\n <div class=\"waiting-message\">\r\n {(() => {\r\n // 显示播放按钮(当不自动播放且有音频URL时)\r\n if (!this.enableVoice && this.audioUrl && !this.isPlayingAudio) {\r\n return (\r\n <div class=\"play-audio-container\" onClick={this.handlePlayAudio}>\r\n <p>\r\n <svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"currentColor\" style={{ verticalAlign: 'middle', marginRight: '8px' }}>\r\n <path d=\"M8 5v14l11-7z\" />\r\n </svg>\r\n <span style={{ verticalAlign: 'middle' }}>播放题目</span>\r\n </p>\r\n </div>\r\n );\r\n }\r\n\r\n // 其他状态下显示禁用的\"完成回答\"按钮\r\n return (\r\n <button class=\"stop-recording-button disabled\" disabled>\r\n 完成回答\r\n </button>\r\n );\r\n })()}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n }\r\n}","/**\n * marked v9.1.6 - a markdown parser\n * Copyright (c) 2011-2023, Christopher Jeffrey. (MIT Licensed)\n * https://github.com/markedjs/marked\n */\n\n/**\n * DO NOT EDIT THIS FILE\n * The code in this file is generated from files in ./src/\n */\n\n/**\n * Gets the original marked default options.\n */\nfunction _getDefaults() {\n return {\n async: false,\n breaks: false,\n extensions: null,\n gfm: true,\n hooks: null,\n pedantic: false,\n renderer: null,\n silent: false,\n tokenizer: null,\n walkTokens: null\n };\n}\nlet _defaults = _getDefaults();\nfunction changeDefaults(newDefaults) {\n _defaults = newDefaults;\n}\n\n/**\n * Helpers\n */\nconst escapeTest = /[&<>\"']/;\nconst escapeReplace = new RegExp(escapeTest.source, 'g');\nconst escapeTestNoEncode = /[<>\"']|&(?!(#\\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\\w+);)/;\nconst escapeReplaceNoEncode = new RegExp(escapeTestNoEncode.source, 'g');\nconst escapeReplacements = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": '''\n};\nconst getEscapeReplacement = (ch) => escapeReplacements[ch];\nfunction escape(html, encode) {\n if (encode) {\n if (escapeTest.test(html)) {\n return html.replace(escapeReplace, getEscapeReplacement);\n }\n }\n else {\n if (escapeTestNoEncode.test(html)) {\n return html.replace(escapeReplaceNoEncode, getEscapeReplacement);\n }\n }\n return html;\n}\nconst unescapeTest = /&(#(?:\\d+)|(?:#x[0-9A-Fa-f]+)|(?:\\w+));?/ig;\nfunction unescape(html) {\n // explicitly match decimal, hex, and named HTML entities\n return html.replace(unescapeTest, (_, n) => {\n n = n.toLowerCase();\n if (n === 'colon')\n return ':';\n if (n.charAt(0) === '#') {\n return n.charAt(1) === 'x'\n ? String.fromCharCode(parseInt(n.substring(2), 16))\n : String.fromCharCode(+n.substring(1));\n }\n return '';\n });\n}\nconst caret = /(^|[^\\[])\\^/g;\nfunction edit(regex, opt) {\n regex = typeof regex === 'string' ? regex : regex.source;\n opt = opt || '';\n const obj = {\n replace: (name, val) => {\n val = typeof val === 'object' && 'source' in val ? val.source : val;\n val = val.replace(caret, '$1');\n regex = regex.replace(name, val);\n return obj;\n },\n getRegex: () => {\n return new RegExp(regex, opt);\n }\n };\n return obj;\n}\nfunction cleanUrl(href) {\n try {\n href = encodeURI(href).replace(/%25/g, '%');\n }\n catch (e) {\n return null;\n }\n return href;\n}\nconst noopTest = { exec: () => null };\nfunction splitCells(tableRow, count) {\n // ensure that every cell-delimiting pipe has a space\n // before it to distinguish it from an escaped pipe\n const row = tableRow.replace(/\\|/g, (match, offset, str) => {\n let escaped = false;\n let curr = offset;\n while (--curr >= 0 && str[curr] === '\\\\')\n escaped = !escaped;\n if (escaped) {\n // odd number of slashes means | is escaped\n // so we leave it alone\n return '|';\n }\n else {\n // add space before unescaped |\n return ' |';\n }\n }), cells = row.split(/ \\|/);\n let i = 0;\n // First/last cell in a row cannot be empty if it has no leading/trailing pipe\n if (!cells[0].trim()) {\n cells.shift();\n }\n if (cells.length > 0 && !cells[cells.length - 1].trim()) {\n cells.pop();\n }\n if (count) {\n if (cells.length > count) {\n cells.splice(count);\n }\n else {\n while (cells.length < count)\n cells.push('');\n }\n }\n for (; i < cells.length; i++) {\n // leading or trailing whitespace is ignored per the gfm spec\n cells[i] = cells[i].trim().replace(/\\\\\\|/g, '|');\n }\n return cells;\n}\n/**\n * Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').\n * /c*$/ is vulnerable to REDOS.\n *\n * @param str\n * @param c\n * @param invert Remove suffix of non-c chars instead. Default falsey.\n */\nfunction rtrim(str, c, invert) {\n const l = str.length;\n if (l === 0) {\n return '';\n }\n // Length of suffix matching the invert condition.\n let suffLen = 0;\n // Step left until we fail to match the invert condition.\n while (suffLen < l) {\n const currChar = str.charAt(l - suffLen - 1);\n if (currChar === c && !invert) {\n suffLen++;\n }\n else if (currChar !== c && invert) {\n suffLen++;\n }\n else {\n break;\n }\n }\n return str.slice(0, l - suffLen);\n}\nfunction findClosingBracket(str, b) {\n if (str.indexOf(b[1]) === -1) {\n return -1;\n }\n let level = 0;\n for (let i = 0; i < str.length; i++) {\n if (str[i] === '\\\\') {\n i++;\n }\n else if (str[i] === b[0]) {\n level++;\n }\n else if (str[i] === b[1]) {\n level--;\n if (level < 0) {\n return i;\n }\n }\n }\n return -1;\n}\n\nfunction outputLink(cap, link, raw, lexer) {\n const href = link.href;\n const title = link.title ? escape(link.title) : null;\n const text = cap[1].replace(/\\\\([\\[\\]])/g, '$1');\n if (cap[0].charAt(0) !== '!') {\n lexer.state.inLink = true;\n const token = {\n type: 'link',\n raw,\n href,\n title,\n text,\n tokens: lexer.inlineTokens(text)\n };\n lexer.state.inLink = false;\n return token;\n }\n return {\n type: 'image',\n raw,\n href,\n title,\n text: escape(text)\n };\n}\nfunction indentCodeCompensation(raw, text) {\n const matchIndentToCode = raw.match(/^(\\s+)(?:```)/);\n if (matchIndentToCode === null) {\n return text;\n }\n const indentToCode = matchIndentToCode[1];\n return text\n .split('\\n')\n .map(node => {\n const matchIndentInNode = node.match(/^\\s+/);\n if (matchIndentInNode === null) {\n return node;\n }\n const [indentInNode] = matchIndentInNode;\n if (indentInNode.length >= indentToCode.length) {\n return node.slice(indentToCode.length);\n }\n return node;\n })\n .join('\\n');\n}\n/**\n * Tokenizer\n */\nclass _Tokenizer {\n options;\n // TODO: Fix this rules type\n rules;\n lexer;\n constructor(options) {\n this.options = options || _defaults;\n }\n space(src) {\n const cap = this.rules.block.newline.exec(src);\n if (cap && cap[0].length > 0) {\n return {\n type: 'space',\n raw: cap[0]\n };\n }\n }\n code(src) {\n const cap = this.rules.block.code.exec(src);\n if (cap) {\n const text = cap[0].replace(/^ {1,4}/gm, '');\n return {\n type: 'code',\n raw: cap[0],\n codeBlockStyle: 'indented',\n text: !this.options.pedantic\n ? rtrim(text, '\\n')\n : text\n };\n }\n }\n fences(src) {\n const cap = this.rules.block.fences.exec(src);\n if (cap) {\n const raw = cap[0];\n const text = indentCodeCompensation(raw, cap[3] || '');\n return {\n type: 'code',\n raw,\n lang: cap[2] ? cap[2].trim().replace(this.rules.inline._escapes, '$1') : cap[2],\n text\n };\n }\n }\n heading(src) {\n const cap = this.rules.block.heading.exec(src);\n if (cap) {\n let text = cap[2].trim();\n // remove trailing #s\n if (/#$/.test(text)) {\n const trimmed = rtrim(text, '#');\n if (this.options.pedantic) {\n text = trimmed.trim();\n }\n else if (!trimmed || / $/.test(trimmed)) {\n // CommonMark requires space before trailing #s\n text = trimmed.trim();\n }\n }\n return {\n type: 'heading',\n raw: cap[0],\n depth: cap[1].length,\n text,\n tokens: this.lexer.inline(text)\n };\n }\n }\n hr(src) {\n const cap = this.rules.block.hr.exec(src);\n if (cap) {\n return {\n type: 'hr',\n raw: cap[0]\n };\n }\n }\n blockquote(src) {\n const cap = this.rules.block.blockquote.exec(src);\n if (cap) {\n const text = rtrim(cap[0].replace(/^ *>[ \\t]?/gm, ''), '\\n');\n const top = this.lexer.state.top;\n this.lexer.state.top = true;\n const tokens = this.lexer.blockTokens(text);\n this.lexer.state.top = top;\n return {\n type: 'blockquote',\n raw: cap[0],\n tokens,\n text\n };\n }\n }\n list(src) {\n let cap = this.rules.block.list.exec(src);\n if (cap) {\n let bull = cap[1].trim();\n const isordered = bull.length > 1;\n const list = {\n type: 'list',\n raw: '',\n ordered: isordered,\n start: isordered ? +bull.slice(0, -1) : '',\n loose: false,\n items: []\n };\n bull = isordered ? `\\\\d{1,9}\\\\${bull.slice(-1)}` : `\\\\${bull}`;\n if (this.options.pedantic) {\n bull = isordered ? bull : '[*+-]';\n }\n // Get next list item\n const itemRegex = new RegExp(`^( {0,3}${bull})((?:[\\t ][^\\\\n]*)?(?:\\\\n|$))`);\n let raw = '';\n let itemContents = '';\n let endsWithBlankLine = false;\n // Check if current bullet point can start a new List Item\n while (src) {\n let endEarly = false;\n if (!(cap = itemRegex.exec(src))) {\n break;\n }\n if (this.rules.block.hr.test(src)) { // End list if bullet was actually HR (possibly move into itemRegex?)\n break;\n }\n raw = cap[0];\n src = src.substring(raw.length);\n let line = cap[2].split('\\n', 1)[0].replace(/^\\t+/, (t) => ' '.repeat(3 * t.length));\n let nextLine = src.split('\\n', 1)[0];\n let indent = 0;\n if (this.options.pedantic) {\n indent = 2;\n itemContents = line.trimStart();\n }\n else {\n indent = cap[2].search(/[^ ]/); // Find first non-space char\n indent = indent > 4 ? 1 : indent; // Treat indented code blocks (> 4 spaces) as having only 1 indent\n itemContents = line.slice(indent);\n indent += cap[1].length;\n }\n let blankLine = false;\n if (!line && /^ *$/.test(nextLine)) { // Items begin with at most one blank line\n raw += nextLine + '\\n';\n src = src.substring(nextLine.length + 1);\n endEarly = true;\n }\n if (!endEarly) {\n const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\\\d{1,9}[.)])((?:[ \\t][^\\\\n]*)?(?:\\\\n|$))`);\n const hrRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\\\* *){3,})(?:\\\\n+|$)`);\n const fencesBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:\\`\\`\\`|~~~)`);\n const headingBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}#`);\n // Check if following lines should be included in List Item\n while (src) {\n const rawLine = src.split('\\n', 1)[0];\n nextLine = rawLine;\n // Re-align to follow commonmark nesting rules\n if (this.options.pedantic) {\n nextLine = nextLine.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' ');\n }\n // End list item if found code fences\n if (fencesBeginRegex.test(nextLine)) {\n break;\n }\n // End list item if found start of new heading\n if (headingBeginRegex.test(nextLine)) {\n break;\n }\n // End list item if found start of new bullet\n if (nextBulletRegex.test(nextLine)) {\n break;\n }\n // Horizontal rule found\n if (hrRegex.test(src)) {\n break;\n }\n if (nextLine.search(/[^ ]/) >= indent || !nextLine.trim()) { // Dedent if possible\n itemContents += '\\n' + nextLine.slice(indent);\n }\n else {\n // not enough indentation\n if (blankLine) {\n break;\n }\n // paragraph continuation unless last line was a different block level element\n if (line.search(/[^ ]/) >= 4) { // indented code block\n break;\n }\n if (fencesBeginRegex.test(line)) {\n break;\n }\n if (headingBeginRegex.test(line)) {\n break;\n }\n if (hrRegex.test(line)) {\n break;\n }\n itemContents += '\\n' + nextLine;\n }\n if (!blankLine && !nextLine.trim()) { // Check if current line is blank\n blankLine = true;\n }\n raw += rawLine + '\\n';\n src = src.substring(rawLine.length + 1);\n line = nextLine.slice(indent);\n }\n }\n if (!list.loose) {\n // If the previous item ended with a blank line, the list is loose\n if (endsWithBlankLine) {\n list.loose = true;\n }\n else if (/\\n *\\n *$/.test(raw)) {\n endsWithBlankLine = true;\n }\n }\n let istask = null;\n let ischecked;\n // Check for task list items\n if (this.options.gfm) {\n istask = /^\\[[ xX]\\] /.exec(itemContents);\n if (istask) {\n ischecked = istask[0] !== '[ ] ';\n itemContents = itemContents.replace(/^\\[[ xX]\\] +/, '');\n }\n }\n list.items.push({\n type: 'list_item',\n raw,\n task: !!istask,\n checked: ischecked,\n loose: false,\n text: itemContents,\n tokens: []\n });\n list.raw += raw;\n }\n // Do not consume newlines at end of final item. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic\n list.items[list.items.length - 1].raw = raw.trimEnd();\n list.items[list.items.length - 1].text = itemContents.trimEnd();\n list.raw = list.raw.trimEnd();\n // Item child tokens handled here at end because we needed to have the final item to trim it first\n for (let i = 0; i < list.items.length; i++) {\n this.lexer.state.top = false;\n list.items[i].tokens = this.lexer.blockTokens(list.items[i].text, []);\n if (!list.loose) {\n // Check if list should be loose\n const spacers = list.items[i].tokens.filter(t => t.type === 'space');\n const hasMultipleLineBreaks = spacers.length > 0 && spacers.some(t => /\\n.*\\n/.test(t.raw));\n list.loose = hasMultipleLineBreaks;\n }\n }\n // Set all items to loose if list is loose\n if (list.loose) {\n for (let i = 0; i < list.items.length; i++) {\n list.items[i].loose = true;\n }\n }\n return list;\n }\n }\n html(src) {\n const cap = this.rules.block.html.exec(src);\n if (cap) {\n const token = {\n type: 'html',\n block: true,\n raw: cap[0],\n pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',\n text: cap[0]\n };\n return token;\n }\n }\n def(src) {\n const cap = this.rules.block.def.exec(src);\n if (cap) {\n const tag = cap[1].toLowerCase().replace(/\\s+/g, ' ');\n const href = cap[2] ? cap[2].replace(/^<(.*)>$/, '$1').replace(this.rules.inline._escapes, '$1') : '';\n const title = cap[3] ? cap[3].substring(1, cap[3].length - 1).replace(this.rules.inline._escapes, '$1') : cap[3];\n return {\n type: 'def',\n tag,\n raw: cap[0],\n href,\n title\n };\n }\n }\n table(src) {\n const cap = this.rules.block.table.exec(src);\n if (cap) {\n if (!/[:|]/.test(cap[2])) {\n // delimiter row must have a pipe (|) or colon (:) otherwise it is a setext heading\n return;\n }\n const item = {\n type: 'table',\n raw: cap[0],\n header: splitCells(cap[1]).map(c => {\n return { text: c, tokens: [] };\n }),\n align: cap[2].replace(/^\\||\\| *$/g, '').split('|'),\n rows: cap[3] && cap[3].trim() ? cap[3].replace(/\\n[ \\t]*$/, '').split('\\n') : []\n };\n if (item.header.length === item.align.length) {\n let l = item.align.length;\n let i, j, k, row;\n for (i = 0; i < l; i++) {\n const align = item.align[i];\n if (align) {\n if (/^ *-+: *$/.test(align)) {\n item.align[i] = 'right';\n }\n else if (/^ *:-+: *$/.test(align)) {\n item.align[i] = 'center';\n }\n else if (/^ *:-+ *$/.test(align)) {\n item.align[i] = 'left';\n }\n else {\n item.align[i] = null;\n }\n }\n }\n l = item.rows.length;\n for (i = 0; i < l; i++) {\n item.rows[i] = splitCells(item.rows[i], item.header.length).map(c => {\n return { text: c, tokens: [] };\n });\n }\n // parse child tokens inside headers and cells\n // header child tokens\n l = item.header.length;\n for (j = 0; j < l; j++) {\n item.header[j].tokens = this.lexer.inline(item.header[j].text);\n }\n // cell child tokens\n l = item.rows.length;\n for (j = 0; j < l; j++) {\n row = item.rows[j];\n for (k = 0; k < row.length; k++) {\n row[k].tokens = this.lexer.inline(row[k].text);\n }\n }\n return item;\n }\n }\n }\n lheading(src) {\n const cap = this.rules.block.lheading.exec(src);\n if (cap) {\n return {\n type: 'heading',\n raw: cap[0],\n depth: cap[2].charAt(0) === '=' ? 1 : 2,\n text: cap[1],\n tokens: this.lexer.inline(cap[1])\n };\n }\n }\n paragraph(src) {\n const cap = this.rules.block.paragraph.exec(src);\n if (cap) {\n const text = cap[1].charAt(cap[1].length - 1) === '\\n'\n ? cap[1].slice(0, -1)\n : cap[1];\n return {\n type: 'paragraph',\n raw: cap[0],\n text,\n tokens: this.lexer.inline(text)\n };\n }\n }\n text(src) {\n const cap = this.rules.block.text.exec(src);\n if (cap) {\n return {\n type: 'text',\n raw: cap[0],\n text: cap[0],\n tokens: this.lexer.inline(cap[0])\n };\n }\n }\n escape(src) {\n const cap = this.rules.inline.escape.exec(src);\n if (cap) {\n return {\n type: 'escape',\n raw: cap[0],\n text: escape(cap[1])\n };\n }\n }\n tag(src) {\n const cap = this.rules.inline.tag.exec(src);\n if (cap) {\n if (!this.lexer.state.inLink && /^<a /i.test(cap[0])) {\n this.lexer.state.inLink = true;\n }\n else if (this.lexer.state.inLink && /^<\\/a>/i.test(cap[0])) {\n this.lexer.state.inLink = false;\n }\n if (!this.lexer.state.inRawBlock && /^<(pre|code|kbd|script)(\\s|>)/i.test(cap[0])) {\n this.lexer.state.inRawBlock = true;\n }\n else if (this.lexer.state.inRawBlock && /^<\\/(pre|code|kbd|script)(\\s|>)/i.test(cap[0])) {\n this.lexer.state.inRawBlock = false;\n }\n return {\n type: 'html',\n raw: cap[0],\n inLink: this.lexer.state.inLink,\n inRawBlock: this.lexer.state.inRawBlock,\n block: false,\n text: cap[0]\n };\n }\n }\n link(src) {\n const cap = this.rules.inline.link.exec(src);\n if (cap) {\n const trimmedUrl = cap[2].trim();\n if (!this.options.pedantic && /^</.test(trimmedUrl)) {\n // commonmark requires matching angle brackets\n if (!(/>$/.test(trimmedUrl))) {\n return;\n }\n // ending angle bracket cannot be escaped\n const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\\\');\n if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {\n return;\n }\n }\n else {\n // find closing parenthesis\n const lastParenIndex = findClosingBracket(cap[2], '()');\n if (lastParenIndex > -1) {\n const start = cap[0].indexOf('!') === 0 ? 5 : 4;\n const linkLen = start + cap[1].length + lastParenIndex;\n cap[2] = cap[2].substring(0, lastParenIndex);\n cap[0] = cap[0].substring(0, linkLen).trim();\n cap[3] = '';\n }\n }\n let href = cap[2];\n let title = '';\n if (this.options.pedantic) {\n // split pedantic href and title\n const link = /^([^'\"]*[^\\s])\\s+(['\"])(.*)\\2/.exec(href);\n if (link) {\n href = link[1];\n title = link[3];\n }\n }\n else {\n title = cap[3] ? cap[3].slice(1, -1) : '';\n }\n href = href.trim();\n if (/^</.test(href)) {\n if (this.options.pedantic && !(/>$/.test(trimmedUrl))) {\n // pedantic allows starting angle bracket without ending angle bracket\n href = href.slice(1);\n }\n else {\n href = href.slice(1, -1);\n }\n }\n return outputLink(cap, {\n href: href ? href.replace(this.rules.inline._escapes, '$1') : href,\n title: title ? title.replace(this.rules.inline._escapes, '$1') : title\n }, cap[0], this.lexer);\n }\n }\n reflink(src, links) {\n let cap;\n if ((cap = this.rules.inline.reflink.exec(src))\n || (cap = this.rules.inline.nolink.exec(src))) {\n let link = (cap[2] || cap[1]).replace(/\\s+/g, ' ');\n link = links[link.toLowerCase()];\n if (!link) {\n const text = cap[0].charAt(0);\n return {\n type: 'text',\n raw: text,\n text\n };\n }\n return outputLink(cap, link, cap[0], this.lexer);\n }\n }\n emStrong(src, maskedSrc, prevChar = '') {\n let match = this.rules.inline.emStrong.lDelim.exec(src);\n if (!match)\n return;\n // _ can't be between two alphanumerics. \\p{L}\\p{N} includes non-english alphabet/numbers as well\n if (match[3] && prevChar.match(/[\\p{L}\\p{N}]/u))\n return;\n const nextChar = match[1] || match[2] || '';\n if (!nextChar || !prevChar || this.rules.inline.punctuation.exec(prevChar)) {\n // unicode Regex counts emoji as 1 char; spread into array for proper count (used multiple times below)\n const lLength = [...match[0]].length - 1;\n let rDelim, rLength, delimTotal = lLength, midDelimTotal = 0;\n const endReg = match[0][0] === '*' ? this.rules.inline.emStrong.rDelimAst : this.rules.inline.emStrong.rDelimUnd;\n endReg.lastIndex = 0;\n // Clip maskedSrc to same section of string as src (move to lexer?)\n maskedSrc = maskedSrc.slice(-1 * src.length + lLength);\n while ((match = endReg.exec(maskedSrc)) != null) {\n rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];\n if (!rDelim)\n continue; // skip single * in __abc*abc__\n rLength = [...rDelim].length;\n if (match[3] || match[4]) { // found another Left Delim\n delimTotal += rLength;\n continue;\n }\n else if (match[5] || match[6]) { // either Left or Right Delim\n if (lLength % 3 && !((lLength + rLength) % 3)) {\n midDelimTotal += rLength;\n continue; // CommonMark Emphasis Rules 9-10\n }\n }\n delimTotal -= rLength;\n if (delimTotal > 0)\n continue; // Haven't found enough closing delimiters\n // Remove extra characters. *a*** -> *a*\n rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);\n // char length can be >1 for unicode characters;\n const lastCharLength = [...match[0]][0].length;\n const raw = src.slice(0, lLength + match.index + lastCharLength + rLength);\n // Create `em` if smallest delimiter has odd char count. *a***\n if (Math.min(lLength, rLength) % 2) {\n const text = raw.slice(1, -1);\n return {\n type: 'em',\n raw,\n text,\n tokens: this.lexer.inlineTokens(text)\n };\n }\n // Create 'strong' if smallest delimiter has even char count. **a***\n const text = raw.slice(2, -2);\n return {\n type: 'strong',\n raw,\n text,\n tokens: this.lexer.inlineTokens(text)\n };\n }\n }\n }\n codespan(src) {\n const cap = this.rules.inline.code.exec(src);\n if (cap) {\n let text = cap[2].replace(/\\n/g, ' ');\n const hasNonSpaceChars = /[^ ]/.test(text);\n const hasSpaceCharsOnBothEnds = /^ /.test(text) && / $/.test(text);\n if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {\n text = text.substring(1, text.length - 1);\n }\n text = escape(text, true);\n return {\n type: 'codespan',\n raw: cap[0],\n text\n };\n }\n }\n br(src) {\n const cap = this.rules.inline.br.exec(src);\n if (cap) {\n return {\n type: 'br',\n raw: cap[0]\n };\n }\n }\n del(src) {\n const cap = this.rules.inline.del.exec(src);\n if (cap) {\n return {\n type: 'del',\n raw: cap[0],\n text: cap[2],\n tokens: this.lexer.inlineTokens(cap[2])\n };\n }\n }\n autolink(src) {\n const cap = this.rules.inline.autolink.exec(src);\n if (cap) {\n let text, href;\n if (cap[2] === '@') {\n text = escape(cap[1]);\n href = 'mailto:' + text;\n }\n else {\n text = escape(cap[1]);\n href = text;\n }\n return {\n type: 'link',\n raw: cap[0],\n text,\n href,\n tokens: [\n {\n type: 'text',\n raw: text,\n text\n }\n ]\n };\n }\n }\n url(src) {\n let cap;\n if (cap = this.rules.inline.url.exec(src)) {\n let text, href;\n if (cap[2] === '@') {\n text = escape(cap[0]);\n href = 'mailto:' + text;\n }\n else {\n // do extended autolink path validation\n let prevCapZero;\n do {\n prevCapZero = cap[0];\n cap[0] = this.rules.inline._backpedal.exec(cap[0])[0];\n } while (prevCapZero !== cap[0]);\n text = escape(cap[0]);\n if (cap[1] === 'www.') {\n href = 'http://' + cap[0];\n }\n else {\n href = cap[0];\n }\n }\n return {\n type: 'link',\n raw: cap[0],\n text,\n href,\n tokens: [\n {\n type: 'text',\n raw: text,\n text\n }\n ]\n };\n }\n }\n inlineText(src) {\n const cap = this.rules.inline.text.exec(src);\n if (cap) {\n let text;\n if (this.lexer.state.inRawBlock) {\n text = cap[0];\n }\n else {\n text = escape(cap[0]);\n }\n return {\n type: 'text',\n raw: cap[0],\n text\n };\n }\n }\n}\n\n/**\n * Block-Level Grammar\n */\n// Not all rules are defined in the object literal\n// @ts-expect-error\nconst block = {\n newline: /^(?: *(?:\\n|$))+/,\n code: /^( {4}[^\\n]+(?:\\n(?: *(?:\\n|$))*)?)+/,\n fences: /^ {0,3}(`{3,}(?=[^`\\n]*(?:\\n|$))|~{3,})([^\\n]*)(?:\\n|$)(?:|([\\s\\S]*?)(?:\\n|$))(?: {0,3}\\1[~`]* *(?=\\n|$)|$)/,\n hr: /^ {0,3}((?:-[\\t ]*){3,}|(?:_[ \\t]*){3,}|(?:\\*[ \\t]*){3,})(?:\\n+|$)/,\n heading: /^ {0,3}(#{1,6})(?=\\s|$)(.*)(?:\\n+|$)/,\n blockquote: /^( {0,3}> ?(paragraph|[^\\n]*)(?:\\n|$))+/,\n list: /^( {0,3}bull)([ \\t][^\\n]+?)?(?:\\n|$)/,\n html: '^ {0,3}(?:' // optional indentation\n + '<(script|pre|style|textarea)[\\\\s>][\\\\s\\\\S]*?(?:</\\\\1>[^\\\\n]*\\\\n+|$)' // (1)\n + '|comment[^\\\\n]*(\\\\n+|$)' // (2)\n + '|<\\\\?[\\\\s\\\\S]*?(?:\\\\?>\\\\n*|$)' // (3)\n + '|<![A-Z][\\\\s\\\\S]*?(?:>\\\\n*|$)' // (4)\n + '|<!\\\\[CDATA\\\\[[\\\\s\\\\S]*?(?:\\\\]\\\\]>\\\\n*|$)' // (5)\n + '|</?(tag)(?: +|\\\\n|/?>)[\\\\s\\\\S]*?(?:(?:\\\\n *)+\\\\n|$)' // (6)\n + '|<(?!script|pre|style|textarea)([a-z][\\\\w-]*)(?:attribute)*? */?>(?=[ \\\\t]*(?:\\\\n|$))[\\\\s\\\\S]*?(?:(?:\\\\n *)+\\\\n|$)' // (7) open tag\n + '|</(?!script|pre|style|textarea)[a-z][\\\\w-]*\\\\s*>(?=[ \\\\t]*(?:\\\\n|$))[\\\\s\\\\S]*?(?:(?:\\\\n *)+\\\\n|$)' // (7) closing tag\n + ')',\n def: /^ {0,3}\\[(label)\\]: *(?:\\n *)?([^<\\s][^\\s]*|<.*?>)(?:(?: +(?:\\n *)?| *\\n *)(title))? *(?:\\n+|$)/,\n table: noopTest,\n lheading: /^(?!bull )((?:.|\\n(?!\\s*?\\n|bull ))+?)\\n {0,3}(=+|-+) *(?:\\n+|$)/,\n // regex template, placeholders will be replaced according to different paragraph\n // interruption rules of commonmark and the original markdown spec:\n _paragraph: /^([^\\n]+(?:\\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\\n)[^\\n]+)*)/,\n text: /^[^\\n]+/\n};\nblock._label = /(?!\\s*\\])(?:\\\\.|[^\\[\\]\\\\])+/;\nblock._title = /(?:\"(?:\\\\\"?|[^\"\\\\])*\"|'[^'\\n]*(?:\\n[^'\\n]+)*\\n?'|\\([^()]*\\))/;\nblock.def = edit(block.def)\n .replace('label', block._label)\n .replace('title', block._title)\n .getRegex();\nblock.bullet = /(?:[*+-]|\\d{1,9}[.)])/;\nblock.listItemStart = edit(/^( *)(bull) */)\n .replace('bull', block.bullet)\n .getRegex();\nblock.list = edit(block.list)\n .replace(/bull/g, block.bullet)\n .replace('hr', '\\\\n+(?=\\\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\\\* *){3,})(?:\\\\n+|$))')\n .replace('def', '\\\\n+(?=' + block.def.source + ')')\n .getRegex();\nblock._tag = 'address|article|aside|base|basefont|blockquote|body|caption'\n + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'\n + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'\n + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'\n + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr'\n + '|track|ul';\nblock._comment = /<!--(?!-?>)[\\s\\S]*?(?:-->|$)/;\nblock.html = edit(block.html, 'i')\n .replace('comment', block._comment)\n .replace('tag', block._tag)\n .replace('attribute', / +[a-zA-Z:_][\\w.:-]*(?: *= *\"[^\"\\n]*\"| *= *'[^'\\n]*'| *= *[^\\s\"'=<>`]+)?/)\n .getRegex();\nblock.lheading = edit(block.lheading)\n .replace(/bull/g, block.bullet) // lists can interrupt\n .getRegex();\nblock.paragraph = edit(block._paragraph)\n .replace('hr', block.hr)\n .replace('heading', ' {0,3}#{1,6}(?:\\\\s|$)')\n .replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs\n .replace('|table', '')\n .replace('blockquote', ' {0,3}>')\n .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n')\n .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt\n .replace('html', '</?(?:tag)(?: +|\\\\n|/?>)|<(?:script|pre|style|textarea|!--)')\n .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks\n .getRegex();\nblock.blockquote = edit(block.blockquote)\n .replace('paragraph', block.paragraph)\n .getRegex();\n/**\n * Normal Block Grammar\n */\nblock.normal = { ...block };\n/**\n * GFM Block Grammar\n */\nblock.gfm = {\n ...block.normal,\n table: '^ *([^\\\\n ].*)\\\\n' // Header\n + ' {0,3}((?:\\\\| *)?:?-+:? *(?:\\\\| *:?-+:? *)*(?:\\\\| *)?)' // Align\n + '(?:\\\\n((?:(?! *\\\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\\\n|$))*)\\\\n*|$)' // Cells\n};\nblock.gfm.table = edit(block.gfm.table)\n .replace('hr', block.hr)\n .replace('heading', ' {0,3}#{1,6}(?:\\\\s|$)')\n .replace('blockquote', ' {0,3}>')\n .replace('code', ' {4}[^\\\\n]')\n .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n')\n .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt\n .replace('html', '</?(?:tag)(?: +|\\\\n|/?>)|<(?:script|pre|style|textarea|!--)')\n .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks\n .getRegex();\nblock.gfm.paragraph = edit(block._paragraph)\n .replace('hr', block.hr)\n .replace('heading', ' {0,3}#{1,6}(?:\\\\s|$)')\n .replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs\n .replace('table', block.gfm.table) // interrupt paragraphs with table\n .replace('blockquote', ' {0,3}>')\n .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n')\n .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt\n .replace('html', '</?(?:tag)(?: +|\\\\n|/?>)|<(?:script|pre|style|textarea|!--)')\n .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks\n .getRegex();\n/**\n * Pedantic grammar (original John Gruber's loose markdown specification)\n */\nblock.pedantic = {\n ...block.normal,\n html: edit('^ *(?:comment *(?:\\\\n|\\\\s*$)'\n + '|<(tag)[\\\\s\\\\S]+?</\\\\1> *(?:\\\\n{2,}|\\\\s*$)' // closed tag\n + '|<tag(?:\"[^\"]*\"|\\'[^\\']*\\'|\\\\s[^\\'\"/>\\\\s]*)*?/?> *(?:\\\\n{2,}|\\\\s*$))')\n .replace('comment', block._comment)\n .replace(/tag/g, '(?!(?:'\n + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'\n + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'\n + '\\\\b)\\\\w+(?!:|[^\\\\w\\\\s@]*@)\\\\b')\n .getRegex(),\n def: /^ *\\[([^\\]]+)\\]: *<?([^\\s>]+)>?(?: +([\"(][^\\n]+[\")]))? *(?:\\n+|$)/,\n heading: /^(#{1,6})(.*)(?:\\n+|$)/,\n fences: noopTest,\n lheading: /^(.+?)\\n {0,3}(=+|-+) *(?:\\n+|$)/,\n paragraph: edit(block.normal._paragraph)\n .replace('hr', block.hr)\n .replace('heading', ' *#{1,6} *[^\\n]')\n .replace('lheading', block.lheading)\n .replace('blockquote', ' {0,3}>')\n .replace('|fences', '')\n .replace('|list', '')\n .replace('|html', '')\n .getRegex()\n};\n/**\n * Inline-Level Grammar\n */\n// Not all rules are defined in the object literal\n// @ts-expect-error\nconst inline = {\n escape: /^\\\\([!\"#$%&'()*+,\\-./:;<=>?@\\[\\]\\\\^_`{|}~])/,\n autolink: /^<(scheme:[^\\s\\x00-\\x1f<>]*|email)>/,\n url: noopTest,\n tag: '^comment'\n + '|^</[a-zA-Z][\\\\w:-]*\\\\s*>' // self-closing tag\n + '|^<[a-zA-Z][\\\\w-]*(?:attribute)*?\\\\s*/?>' // open tag\n + '|^<\\\\?[\\\\s\\\\S]*?\\\\?>' // processing instruction, e.g. <?php ?>\n + '|^<![a-zA-Z]+\\\\s[\\\\s\\\\S]*?>' // declaration, e.g. <!DOCTYPE html>\n + '|^<!\\\\[CDATA\\\\[[\\\\s\\\\S]*?\\\\]\\\\]>',\n link: /^!?\\[(label)\\]\\(\\s*(href)(?:\\s+(title))?\\s*\\)/,\n reflink: /^!?\\[(label)\\]\\[(ref)\\]/,\n nolink: /^!?\\[(ref)\\](?:\\[\\])?/,\n reflinkSearch: 'reflink|nolink(?!\\\\()',\n emStrong: {\n lDelim: /^(?:\\*+(?:((?!\\*)[punct])|[^\\s*]))|^_+(?:((?!_)[punct])|([^\\s_]))/,\n // (1) and (2) can only be a Right Delimiter. (3) and (4) can only be Left. (5) and (6) can be either Left or Right.\n // | Skip orphan inside strong | Consume to delim | (1) #*** | (2) a***#, a*** | (3) #***a, ***a | (4) ***# | (5) #***# | (6) a***a\n rDelimAst: /^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)[punct](\\*+)(?=[\\s]|$)|[^punct\\s](\\*+)(?!\\*)(?=[punct\\s]|$)|(?!\\*)[punct\\s](\\*+)(?=[^punct\\s])|[\\s](\\*+)(?!\\*)(?=[punct])|(?!\\*)[punct](\\*+)(?!\\*)(?=[punct])|[^punct\\s](\\*+)(?=[^punct\\s])/,\n rDelimUnd: /^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)[punct](_+)(?=[\\s]|$)|[^punct\\s](_+)(?!_)(?=[punct\\s]|$)|(?!_)[punct\\s](_+)(?=[^punct\\s])|[\\s](_+)(?!_)(?=[punct])|(?!_)[punct](_+)(?!_)(?=[punct])/ // ^- Not allowed for _\n },\n code: /^(`+)([^`]|[^`][\\s\\S]*?[^`])\\1(?!`)/,\n br: /^( {2,}|\\\\)\\n(?!\\s*$)/,\n del: noopTest,\n text: /^(`+|[^`])(?:(?= {2,}\\n)|[\\s\\S]*?(?:(?=[\\\\<!\\[`*_]|\\b_|$)|[^ ](?= {2,}\\n)))/,\n punctuation: /^((?![*_])[\\spunctuation])/\n};\n// list of unicode punctuation marks, plus any missing characters from CommonMark spec\ninline._punctuation = '\\\\p{P}$+<=>`^|~';\ninline.punctuation = edit(inline.punctuation, 'u').replace(/punctuation/g, inline._punctuation).getRegex();\n// sequences em should skip over [title](link), `code`, <html>\ninline.blockSkip = /\\[[^[\\]]*?\\]\\([^\\(\\)]*?\\)|`[^`]*?`|<[^<>]*?>/g;\ninline.anyPunctuation = /\\\\[punct]/g;\ninline._escapes = /\\\\([punct])/g;\ninline._comment = edit(block._comment).replace('(?:-->|$)', '-->').getRegex();\ninline.emStrong.lDelim = edit(inline.emStrong.lDelim, 'u')\n .replace(/punct/g, inline._punctuation)\n .getRegex();\ninline.emStrong.rDelimAst = edit(inline.emStrong.rDelimAst, 'gu')\n .replace(/punct/g, inline._punctuation)\n .getRegex();\ninline.emStrong.rDelimUnd = edit(inline.emStrong.rDelimUnd, 'gu')\n .replace(/punct/g, inline._punctuation)\n .getRegex();\ninline.anyPunctuation = edit(inline.anyPunctuation, 'gu')\n .replace(/punct/g, inline._punctuation)\n .getRegex();\ninline._escapes = edit(inline._escapes, 'gu')\n .replace(/punct/g, inline._punctuation)\n .getRegex();\ninline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;\ninline._email = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/;\ninline.autolink = edit(inline.autolink)\n .replace('scheme', inline._scheme)\n .replace('email', inline._email)\n .getRegex();\ninline._attribute = /\\s+[a-zA-Z:_][\\w.:-]*(?:\\s*=\\s*\"[^\"]*\"|\\s*=\\s*'[^']*'|\\s*=\\s*[^\\s\"'=<>`]+)?/;\ninline.tag = edit(inline.tag)\n .replace('comment', inline._comment)\n .replace('attribute', inline._attribute)\n .getRegex();\ninline._label = /(?:\\[(?:\\\\.|[^\\[\\]\\\\])*\\]|\\\\.|`[^`]*`|[^\\[\\]\\\\`])*?/;\ninline._href = /<(?:\\\\.|[^\\n<>\\\\])+>|[^\\s\\x00-\\x1f]*/;\ninline._title = /\"(?:\\\\\"?|[^\"\\\\])*\"|'(?:\\\\'?|[^'\\\\])*'|\\((?:\\\\\\)?|[^)\\\\])*\\)/;\ninline.link = edit(inline.link)\n .replace('label', inline._label)\n .replace('href', inline._href)\n .replace('title', inline._title)\n .getRegex();\ninline.reflink = edit(inline.reflink)\n .replace('label', inline._label)\n .replace('ref', block._label)\n .getRegex();\ninline.nolink = edit(inline.nolink)\n .replace('ref', block._label)\n .getRegex();\ninline.reflinkSearch = edit(inline.reflinkSearch, 'g')\n .replace('reflink', inline.reflink)\n .replace('nolink', inline.nolink)\n .getRegex();\n/**\n * Normal Inline Grammar\n */\ninline.normal = { ...inline };\n/**\n * Pedantic Inline Grammar\n */\ninline.pedantic = {\n ...inline.normal,\n strong: {\n start: /^__|\\*\\*/,\n middle: /^__(?=\\S)([\\s\\S]*?\\S)__(?!_)|^\\*\\*(?=\\S)([\\s\\S]*?\\S)\\*\\*(?!\\*)/,\n endAst: /\\*\\*(?!\\*)/g,\n endUnd: /__(?!_)/g\n },\n em: {\n start: /^_|\\*/,\n middle: /^()\\*(?=\\S)([\\s\\S]*?\\S)\\*(?!\\*)|^_(?=\\S)([\\s\\S]*?\\S)_(?!_)/,\n endAst: /\\*(?!\\*)/g,\n endUnd: /_(?!_)/g\n },\n link: edit(/^!?\\[(label)\\]\\((.*?)\\)/)\n .replace('label', inline._label)\n .getRegex(),\n reflink: edit(/^!?\\[(label)\\]\\s*\\[([^\\]]*)\\]/)\n .replace('label', inline._label)\n .getRegex()\n};\n/**\n * GFM Inline Grammar\n */\ninline.gfm = {\n ...inline.normal,\n escape: edit(inline.escape).replace('])', '~|])').getRegex(),\n _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,\n url: /^((?:ftp|https?):\\/\\/|www\\.)(?:[a-zA-Z0-9\\-]+\\.?)+[^\\s<]*|^email/,\n _backpedal: /(?:[^?!.,:;*_'\"~()&]+|\\([^)]*\\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'\"~)]+(?!$))+/,\n del: /^(~~?)(?=[^\\s~])([\\s\\S]*?[^\\s~])\\1(?=[^~]|$)/,\n text: /^([`~]+|[^`~])(?:(?= {2,}\\n)|(?=[a-zA-Z0-9.!#$%&'*+\\/=?_`{\\|}~-]+@)|[\\s\\S]*?(?:(?=[\\\\<!\\[`*~_]|\\b_|https?:\\/\\/|ftp:\\/\\/|www\\.|$)|[^ ](?= {2,}\\n)|[^a-zA-Z0-9.!#$%&'*+\\/=?_`{\\|}~-](?=[a-zA-Z0-9.!#$%&'*+\\/=?_`{\\|}~-]+@)))/\n};\ninline.gfm.url = edit(inline.gfm.url, 'i')\n .replace('email', inline.gfm._extended_email)\n .getRegex();\n/**\n * GFM + Line Breaks Inline Grammar\n */\ninline.breaks = {\n ...inline.gfm,\n br: edit(inline.br).replace('{2,}', '*').getRegex(),\n text: edit(inline.gfm.text)\n .replace('\\\\b_', '\\\\b_| {2,}\\\\n')\n .replace(/\\{2,\\}/g, '*')\n .getRegex()\n};\n\n/**\n * Block Lexer\n */\nclass _Lexer {\n tokens;\n options;\n state;\n tokenizer;\n inlineQueue;\n constructor(options) {\n // TokenList cannot be created in one go\n // @ts-expect-error\n this.tokens = [];\n this.tokens.links = Object.create(null);\n this.options = options || _defaults;\n this.options.tokenizer = this.options.tokenizer || new _Tokenizer();\n this.tokenizer = this.options.tokenizer;\n this.tokenizer.options = this.options;\n this.tokenizer.lexer = this;\n this.inlineQueue = [];\n this.state = {\n inLink: false,\n inRawBlock: false,\n top: true\n };\n const rules = {\n block: block.normal,\n inline: inline.normal\n };\n if (this.options.pedantic) {\n rules.block = block.pedantic;\n rules.inline = inline.pedantic;\n }\n else if (this.options.gfm) {\n rules.block = block.gfm;\n if (this.options.breaks) {\n rules.inline = inline.breaks;\n }\n else {\n rules.inline = inline.gfm;\n }\n }\n this.tokenizer.rules = rules;\n }\n /**\n * Expose Rules\n */\n static get rules() {\n return {\n block,\n inline\n };\n }\n /**\n * Static Lex Method\n */\n static lex(src, options) {\n const lexer = new _Lexer(options);\n return lexer.lex(src);\n }\n /**\n * Static Lex Inline Method\n */\n static lexInline(src, options) {\n const lexer = new _Lexer(options);\n return lexer.inlineTokens(src);\n }\n /**\n * Preprocessing\n */\n lex(src) {\n src = src\n .replace(/\\r\\n|\\r/g, '\\n');\n this.blockTokens(src, this.tokens);\n let next;\n while (next = this.inlineQueue.shift()) {\n this.inlineTokens(next.src, next.tokens);\n }\n return this.tokens;\n }\n blockTokens(src, tokens = []) {\n if (this.options.pedantic) {\n src = src.replace(/\\t/g, ' ').replace(/^ +$/gm, '');\n }\n else {\n src = src.replace(/^( *)(\\t+)/gm, (_, leading, tabs) => {\n return leading + ' '.repeat(tabs.length);\n });\n }\n let token;\n let lastToken;\n let cutSrc;\n let lastParagraphClipped;\n while (src) {\n if (this.options.extensions\n && this.options.extensions.block\n && this.options.extensions.block.some((extTokenizer) => {\n if (token = extTokenizer.call({ lexer: this }, src, tokens)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n return true;\n }\n return false;\n })) {\n continue;\n }\n // newline\n if (token = this.tokenizer.space(src)) {\n src = src.substring(token.raw.length);\n if (token.raw.length === 1 && tokens.length > 0) {\n // if there's a single \\n as a spacer, it's terminating the last line,\n // so move it there so that we don't get unnecessary paragraph tags\n tokens[tokens.length - 1].raw += '\\n';\n }\n else {\n tokens.push(token);\n }\n continue;\n }\n // code\n if (token = this.tokenizer.code(src)) {\n src = src.substring(token.raw.length);\n lastToken = tokens[tokens.length - 1];\n // An indented code block cannot interrupt a paragraph.\n if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) {\n lastToken.raw += '\\n' + token.raw;\n lastToken.text += '\\n' + token.text;\n this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;\n }\n else {\n tokens.push(token);\n }\n continue;\n }\n // fences\n if (token = this.tokenizer.fences(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // heading\n if (token = this.tokenizer.heading(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // hr\n if (token = this.tokenizer.hr(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // blockquote\n if (token = this.tokenizer.blockquote(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // list\n if (token = this.tokenizer.list(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // html\n if (token = this.tokenizer.html(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // def\n if (token = this.tokenizer.def(src)) {\n src = src.substring(token.raw.length);\n lastToken = tokens[tokens.length - 1];\n if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) {\n lastToken.raw += '\\n' + token.raw;\n lastToken.text += '\\n' + token.raw;\n this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;\n }\n else if (!this.tokens.links[token.tag]) {\n this.tokens.links[token.tag] = {\n href: token.href,\n title: token.title\n };\n }\n continue;\n }\n // table (gfm)\n if (token = this.tokenizer.table(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // lheading\n if (token = this.tokenizer.lheading(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // top-level paragraph\n // prevent paragraph consuming extensions by clipping 'src' to extension start\n cutSrc = src;\n if (this.options.extensions && this.options.extensions.startBlock) {\n let startIndex = Infinity;\n const tempSrc = src.slice(1);\n let tempStart;\n this.options.extensions.startBlock.forEach((getStartIndex) => {\n tempStart = getStartIndex.call({ lexer: this }, tempSrc);\n if (typeof tempStart === 'number' && tempStart >= 0) {\n startIndex = Math.min(startIndex, tempStart);\n }\n });\n if (startIndex < Infinity && startIndex >= 0) {\n cutSrc = src.substring(0, startIndex + 1);\n }\n }\n if (this.state.top && (token = this.tokenizer.paragraph(cutSrc))) {\n lastToken = tokens[tokens.length - 1];\n if (lastParagraphClipped && lastToken.type === 'paragraph') {\n lastToken.raw += '\\n' + token.raw;\n lastToken.text += '\\n' + token.text;\n this.inlineQueue.pop();\n this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;\n }\n else {\n tokens.push(token);\n }\n lastParagraphClipped = (cutSrc.length !== src.length);\n src = src.substring(token.raw.length);\n continue;\n }\n // text\n if (token = this.tokenizer.text(src)) {\n src = src.substring(token.raw.length);\n lastToken = tokens[tokens.length - 1];\n if (lastToken && lastToken.type === 'text') {\n lastToken.raw += '\\n' + token.raw;\n lastToken.text += '\\n' + token.text;\n this.inlineQueue.pop();\n this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;\n }\n else {\n tokens.push(token);\n }\n continue;\n }\n if (src) {\n const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);\n if (this.options.silent) {\n console.error(errMsg);\n break;\n }\n else {\n throw new Error(errMsg);\n }\n }\n }\n this.state.top = true;\n return tokens;\n }\n inline(src, tokens = []) {\n this.inlineQueue.push({ src, tokens });\n return tokens;\n }\n /**\n * Lexing/Compiling\n */\n inlineTokens(src, tokens = []) {\n let token, lastToken, cutSrc;\n // String with links masked to avoid interference with em and strong\n let maskedSrc = src;\n let match;\n let keepPrevChar, prevChar;\n // Mask out reflinks\n if (this.tokens.links) {\n const links = Object.keys(this.tokens.links);\n if (links.length > 0) {\n while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {\n if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) {\n maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);\n }\n }\n }\n }\n // Mask out other blocks\n while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) {\n maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);\n }\n // Mask out escaped characters\n while ((match = this.tokenizer.rules.inline.anyPunctuation.exec(maskedSrc)) != null) {\n maskedSrc = maskedSrc.slice(0, match.index) + '++' + maskedSrc.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);\n }\n while (src) {\n if (!keepPrevChar) {\n prevChar = '';\n }\n keepPrevChar = false;\n // extensions\n if (this.options.extensions\n && this.options.extensions.inline\n && this.options.extensions.inline.some((extTokenizer) => {\n if (token = extTokenizer.call({ lexer: this }, src, tokens)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n return true;\n }\n return false;\n })) {\n continue;\n }\n // escape\n if (token = this.tokenizer.escape(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // tag\n if (token = this.tokenizer.tag(src)) {\n src = src.substring(token.raw.length);\n lastToken = tokens[tokens.length - 1];\n if (lastToken && token.type === 'text' && lastToken.type === 'text') {\n lastToken.raw += token.raw;\n lastToken.text += token.text;\n }\n else {\n tokens.push(token);\n }\n continue;\n }\n // link\n if (token = this.tokenizer.link(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // reflink, nolink\n if (token = this.tokenizer.reflink(src, this.tokens.links)) {\n src = src.substring(token.raw.length);\n lastToken = tokens[tokens.length - 1];\n if (lastToken && token.type === 'text' && lastToken.type === 'text') {\n lastToken.raw += token.raw;\n lastToken.text += token.text;\n }\n else {\n tokens.push(token);\n }\n continue;\n }\n // em & strong\n if (token = this.tokenizer.emStrong(src, maskedSrc, prevChar)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // code\n if (token = this.tokenizer.codespan(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // br\n if (token = this.tokenizer.br(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // del (gfm)\n if (token = this.tokenizer.del(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // autolink\n if (token = this.tokenizer.autolink(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // url (gfm)\n if (!this.state.inLink && (token = this.tokenizer.url(src))) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // text\n // prevent inlineText consuming extensions by clipping 'src' to extension start\n cutSrc = src;\n if (this.options.extensions && this.options.extensions.startInline) {\n let startIndex = Infinity;\n const tempSrc = src.slice(1);\n let tempStart;\n this.options.extensions.startInline.forEach((getStartIndex) => {\n tempStart = getStartIndex.call({ lexer: this }, tempSrc);\n if (typeof tempStart === 'number' && tempStart >= 0) {\n startIndex = Math.min(startIndex, tempStart);\n }\n });\n if (startIndex < Infinity && startIndex >= 0) {\n cutSrc = src.substring(0, startIndex + 1);\n }\n }\n if (token = this.tokenizer.inlineText(cutSrc)) {\n src = src.substring(token.raw.length);\n if (token.raw.slice(-1) !== '_') { // Track prevChar before string of ____ started\n prevChar = token.raw.slice(-1);\n }\n keepPrevChar = true;\n lastToken = tokens[tokens.length - 1];\n if (lastToken && lastToken.type === 'text') {\n lastToken.raw += token.raw;\n lastToken.text += token.text;\n }\n else {\n tokens.push(token);\n }\n continue;\n }\n if (src) {\n const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);\n if (this.options.silent) {\n console.error(errMsg);\n break;\n }\n else {\n throw new Error(errMsg);\n }\n }\n }\n return tokens;\n }\n}\n\n/**\n * Renderer\n */\nclass _Renderer {\n options;\n constructor(options) {\n this.options = options || _defaults;\n }\n code(code, infostring, escaped) {\n const lang = (infostring || '').match(/^\\S*/)?.[0];\n code = code.replace(/\\n$/, '') + '\\n';\n if (!lang) {\n return '<pre><code>'\n + (escaped ? code : escape(code, true))\n + '</code></pre>\\n';\n }\n return '<pre><code class=\"language-'\n + escape(lang)\n + '\">'\n + (escaped ? code : escape(code, true))\n + '</code></pre>\\n';\n }\n blockquote(quote) {\n return `<blockquote>\\n${quote}</blockquote>\\n`;\n }\n html(html, block) {\n return html;\n }\n heading(text, level, raw) {\n // ignore IDs\n return `<h${level}>${text}</h${level}>\\n`;\n }\n hr() {\n return '<hr>\\n';\n }\n list(body, ordered, start) {\n const type = ordered ? 'ol' : 'ul';\n const startatt = (ordered && start !== 1) ? (' start=\"' + start + '\"') : '';\n return '<' + type + startatt + '>\\n' + body + '</' + type + '>\\n';\n }\n listitem(text, task, checked) {\n return `<li>${text}</li>\\n`;\n }\n checkbox(checked) {\n return '<input '\n + (checked ? 'checked=\"\" ' : '')\n + 'disabled=\"\" type=\"checkbox\">';\n }\n paragraph(text) {\n return `<p>${text}</p>\\n`;\n }\n table(header, body) {\n if (body)\n body = `<tbody>${body}</tbody>`;\n return '<table>\\n'\n + '<thead>\\n'\n + header\n + '</thead>\\n'\n + body\n + '</table>\\n';\n }\n tablerow(content) {\n return `<tr>\\n${content}</tr>\\n`;\n }\n tablecell(content, flags) {\n const type = flags.header ? 'th' : 'td';\n const tag = flags.align\n ? `<${type} align=\"${flags.align}\">`\n : `<${type}>`;\n return tag + content + `</${type}>\\n`;\n }\n /**\n * span level renderer\n */\n strong(text) {\n return `<strong>${text}</strong>`;\n }\n em(text) {\n return `<em>${text}</em>`;\n }\n codespan(text) {\n return `<code>${text}</code>`;\n }\n br() {\n return '<br>';\n }\n del(text) {\n return `<del>${text}</del>`;\n }\n link(href, title, text) {\n const cleanHref = cleanUrl(href);\n if (cleanHref === null) {\n return text;\n }\n href = cleanHref;\n let out = '<a href=\"' + href + '\"';\n if (title) {\n out += ' title=\"' + title + '\"';\n }\n out += '>' + text + '</a>';\n return out;\n }\n image(href, title, text) {\n const cleanHref = cleanUrl(href);\n if (cleanHref === null) {\n return text;\n }\n href = cleanHref;\n let out = `<img src=\"${href}\" alt=\"${text}\"`;\n if (title) {\n out += ` title=\"${title}\"`;\n }\n out += '>';\n return out;\n }\n text(text) {\n return text;\n }\n}\n\n/**\n * TextRenderer\n * returns only the textual part of the token\n */\nclass _TextRenderer {\n // no need for block level renderers\n strong(text) {\n return text;\n }\n em(text) {\n return text;\n }\n codespan(text) {\n return text;\n }\n del(text) {\n return text;\n }\n html(text) {\n return text;\n }\n text(text) {\n return text;\n }\n link(href, title, text) {\n return '' + text;\n }\n image(href, title, text) {\n return '' + text;\n }\n br() {\n return '';\n }\n}\n\n/**\n * Parsing & Compiling\n */\nclass _Parser {\n options;\n renderer;\n textRenderer;\n constructor(options) {\n this.options = options || _defaults;\n this.options.renderer = this.options.renderer || new _Renderer();\n this.renderer = this.options.renderer;\n this.renderer.options = this.options;\n this.textRenderer = new _TextRenderer();\n }\n /**\n * Static Parse Method\n */\n static parse(tokens, options) {\n const parser = new _Parser(options);\n return parser.parse(tokens);\n }\n /**\n * Static Parse Inline Method\n */\n static parseInline(tokens, options) {\n const parser = new _Parser(options);\n return parser.parseInline(tokens);\n }\n /**\n * Parse Loop\n */\n parse(tokens, top = true) {\n let out = '';\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i];\n // Run any renderer extensions\n if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) {\n const genericToken = token;\n const ret = this.options.extensions.renderers[genericToken.type].call({ parser: this }, genericToken);\n if (ret !== false || !['space', 'hr', 'heading', 'code', 'table', 'blockquote', 'list', 'html', 'paragraph', 'text'].includes(genericToken.type)) {\n out += ret || '';\n continue;\n }\n }\n switch (token.type) {\n case 'space': {\n continue;\n }\n case 'hr': {\n out += this.renderer.hr();\n continue;\n }\n case 'heading': {\n const headingToken = token;\n out += this.renderer.heading(this.parseInline(headingToken.tokens), headingToken.depth, unescape(this.parseInline(headingToken.tokens, this.textRenderer)));\n continue;\n }\n case 'code': {\n const codeToken = token;\n out += this.renderer.code(codeToken.text, codeToken.lang, !!codeToken.escaped);\n continue;\n }\n case 'table': {\n const tableToken = token;\n let header = '';\n // header\n let cell = '';\n for (let j = 0; j < tableToken.header.length; j++) {\n cell += this.renderer.tablecell(this.parseInline(tableToken.header[j].tokens), { header: true, align: tableToken.align[j] });\n }\n header += this.renderer.tablerow(cell);\n let body = '';\n for (let j = 0; j < tableToken.rows.length; j++) {\n const row = tableToken.rows[j];\n cell = '';\n for (let k = 0; k < row.length; k++) {\n cell += this.renderer.tablecell(this.parseInline(row[k].tokens), { header: false, align: tableToken.align[k] });\n }\n body += this.renderer.tablerow(cell);\n }\n out += this.renderer.table(header, body);\n continue;\n }\n case 'blockquote': {\n const blockquoteToken = token;\n const body = this.parse(blockquoteToken.tokens);\n out += this.renderer.blockquote(body);\n continue;\n }\n case 'list': {\n const listToken = token;\n const ordered = listToken.ordered;\n const start = listToken.start;\n const loose = listToken.loose;\n let body = '';\n for (let j = 0; j < listToken.items.length; j++) {\n const item = listToken.items[j];\n const checked = item.checked;\n const task = item.task;\n let itemBody = '';\n if (item.task) {\n const checkbox = this.renderer.checkbox(!!checked);\n if (loose) {\n if (item.tokens.length > 0 && item.tokens[0].type === 'paragraph') {\n item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;\n if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {\n item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;\n }\n }\n else {\n item.tokens.unshift({\n type: 'text',\n text: checkbox + ' '\n });\n }\n }\n else {\n itemBody += checkbox + ' ';\n }\n }\n itemBody += this.parse(item.tokens, loose);\n body += this.renderer.listitem(itemBody, task, !!checked);\n }\n out += this.renderer.list(body, ordered, start);\n continue;\n }\n case 'html': {\n const htmlToken = token;\n out += this.renderer.html(htmlToken.text, htmlToken.block);\n continue;\n }\n case 'paragraph': {\n const paragraphToken = token;\n out += this.renderer.paragraph(this.parseInline(paragraphToken.tokens));\n continue;\n }\n case 'text': {\n let textToken = token;\n let body = textToken.tokens ? this.parseInline(textToken.tokens) : textToken.text;\n while (i + 1 < tokens.length && tokens[i + 1].type === 'text') {\n textToken = tokens[++i];\n body += '\\n' + (textToken.tokens ? this.parseInline(textToken.tokens) : textToken.text);\n }\n out += top ? this.renderer.paragraph(body) : body;\n continue;\n }\n default: {\n const errMsg = 'Token with \"' + token.type + '\" type was not found.';\n if (this.options.silent) {\n console.error(errMsg);\n return '';\n }\n else {\n throw new Error(errMsg);\n }\n }\n }\n }\n return out;\n }\n /**\n * Parse Inline Tokens\n */\n parseInline(tokens, renderer) {\n renderer = renderer || this.renderer;\n let out = '';\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i];\n // Run any renderer extensions\n if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) {\n const ret = this.options.extensions.renderers[token.type].call({ parser: this }, token);\n if (ret !== false || !['escape', 'html', 'link', 'image', 'strong', 'em', 'codespan', 'br', 'del', 'text'].includes(token.type)) {\n out += ret || '';\n continue;\n }\n }\n switch (token.type) {\n case 'escape': {\n const escapeToken = token;\n out += renderer.text(escapeToken.text);\n break;\n }\n case 'html': {\n const tagToken = token;\n out += renderer.html(tagToken.text);\n break;\n }\n case 'link': {\n const linkToken = token;\n out += renderer.link(linkToken.href, linkToken.title, this.parseInline(linkToken.tokens, renderer));\n break;\n }\n case 'image': {\n const imageToken = token;\n out += renderer.image(imageToken.href, imageToken.title, imageToken.text);\n break;\n }\n case 'strong': {\n const strongToken = token;\n out += renderer.strong(this.parseInline(strongToken.tokens, renderer));\n break;\n }\n case 'em': {\n const emToken = token;\n out += renderer.em(this.parseInline(emToken.tokens, renderer));\n break;\n }\n case 'codespan': {\n const codespanToken = token;\n out += renderer.codespan(codespanToken.text);\n break;\n }\n case 'br': {\n out += renderer.br();\n break;\n }\n case 'del': {\n const delToken = token;\n out += renderer.del(this.parseInline(delToken.tokens, renderer));\n break;\n }\n case 'text': {\n const textToken = token;\n out += renderer.text(textToken.text);\n break;\n }\n default: {\n const errMsg = 'Token with \"' + token.type + '\" type was not found.';\n if (this.options.silent) {\n console.error(errMsg);\n return '';\n }\n else {\n throw new Error(errMsg);\n }\n }\n }\n }\n return out;\n }\n}\n\nclass _Hooks {\n options;\n constructor(options) {\n this.options = options || _defaults;\n }\n static passThroughHooks = new Set([\n 'preprocess',\n 'postprocess'\n ]);\n /**\n * Process markdown before marked\n */\n preprocess(markdown) {\n return markdown;\n }\n /**\n * Process HTML after marked is finished\n */\n postprocess(html) {\n return html;\n }\n}\n\nclass Marked {\n defaults = _getDefaults();\n options = this.setOptions;\n parse = this.#parseMarkdown(_Lexer.lex, _Parser.parse);\n parseInline = this.#parseMarkdown(_Lexer.lexInline, _Parser.parseInline);\n Parser = _Parser;\n Renderer = _Renderer;\n TextRenderer = _TextRenderer;\n Lexer = _Lexer;\n Tokenizer = _Tokenizer;\n Hooks = _Hooks;\n constructor(...args) {\n this.use(...args);\n }\n /**\n * Run callback for every token\n */\n walkTokens(tokens, callback) {\n let values = [];\n for (const token of tokens) {\n values = values.concat(callback.call(this, token));\n switch (token.type) {\n case 'table': {\n const tableToken = token;\n for (const cell of tableToken.header) {\n values = values.concat(this.walkTokens(cell.tokens, callback));\n }\n for (const row of tableToken.rows) {\n for (const cell of row) {\n values = values.concat(this.walkTokens(cell.tokens, callback));\n }\n }\n break;\n }\n case 'list': {\n const listToken = token;\n values = values.concat(this.walkTokens(listToken.items, callback));\n break;\n }\n default: {\n const genericToken = token;\n if (this.defaults.extensions?.childTokens?.[genericToken.type]) {\n this.defaults.extensions.childTokens[genericToken.type].forEach((childTokens) => {\n values = values.concat(this.walkTokens(genericToken[childTokens], callback));\n });\n }\n else if (genericToken.tokens) {\n values = values.concat(this.walkTokens(genericToken.tokens, callback));\n }\n }\n }\n }\n return values;\n }\n use(...args) {\n const extensions = this.defaults.extensions || { renderers: {}, childTokens: {} };\n args.forEach((pack) => {\n // copy options to new object\n const opts = { ...pack };\n // set async to true if it was set to true before\n opts.async = this.defaults.async || opts.async || false;\n // ==-- Parse \"addon\" extensions --== //\n if (pack.extensions) {\n pack.extensions.forEach((ext) => {\n if (!ext.name) {\n throw new Error('extension name required');\n }\n if ('renderer' in ext) { // Renderer extensions\n const prevRenderer = extensions.renderers[ext.name];\n if (prevRenderer) {\n // Replace extension with func to run new extension but fall back if false\n extensions.renderers[ext.name] = function (...args) {\n let ret = ext.renderer.apply(this, args);\n if (ret === false) {\n ret = prevRenderer.apply(this, args);\n }\n return ret;\n };\n }\n else {\n extensions.renderers[ext.name] = ext.renderer;\n }\n }\n if ('tokenizer' in ext) { // Tokenizer Extensions\n if (!ext.level || (ext.level !== 'block' && ext.level !== 'inline')) {\n throw new Error(\"extension level must be 'block' or 'inline'\");\n }\n const extLevel = extensions[ext.level];\n if (extLevel) {\n extLevel.unshift(ext.tokenizer);\n }\n else {\n extensions[ext.level] = [ext.tokenizer];\n }\n if (ext.start) { // Function to check for start of token\n if (ext.level === 'block') {\n if (extensions.startBlock) {\n extensions.startBlock.push(ext.start);\n }\n else {\n extensions.startBlock = [ext.start];\n }\n }\n else if (ext.level === 'inline') {\n if (extensions.startInline) {\n extensions.startInline.push(ext.start);\n }\n else {\n extensions.startInline = [ext.start];\n }\n }\n }\n }\n if ('childTokens' in ext && ext.childTokens) { // Child tokens to be visited by walkTokens\n extensions.childTokens[ext.name] = ext.childTokens;\n }\n });\n opts.extensions = extensions;\n }\n // ==-- Parse \"overwrite\" extensions --== //\n if (pack.renderer) {\n const renderer = this.defaults.renderer || new _Renderer(this.defaults);\n for (const prop in pack.renderer) {\n const rendererFunc = pack.renderer[prop];\n const rendererKey = prop;\n const prevRenderer = renderer[rendererKey];\n // Replace renderer with func to run extension, but fall back if false\n renderer[rendererKey] = (...args) => {\n let ret = rendererFunc.apply(renderer, args);\n if (ret === false) {\n ret = prevRenderer.apply(renderer, args);\n }\n return ret || '';\n };\n }\n opts.renderer = renderer;\n }\n if (pack.tokenizer) {\n const tokenizer = this.defaults.tokenizer || new _Tokenizer(this.defaults);\n for (const prop in pack.tokenizer) {\n const tokenizerFunc = pack.tokenizer[prop];\n const tokenizerKey = prop;\n const prevTokenizer = tokenizer[tokenizerKey];\n // Replace tokenizer with func to run extension, but fall back if false\n tokenizer[tokenizerKey] = (...args) => {\n let ret = tokenizerFunc.apply(tokenizer, args);\n if (ret === false) {\n ret = prevTokenizer.apply(tokenizer, args);\n }\n return ret;\n };\n }\n opts.tokenizer = tokenizer;\n }\n // ==-- Parse Hooks extensions --== //\n if (pack.hooks) {\n const hooks = this.defaults.hooks || new _Hooks();\n for (const prop in pack.hooks) {\n const hooksFunc = pack.hooks[prop];\n const hooksKey = prop;\n const prevHook = hooks[hooksKey];\n if (_Hooks.passThroughHooks.has(prop)) {\n hooks[hooksKey] = (arg) => {\n if (this.defaults.async) {\n return Promise.resolve(hooksFunc.call(hooks, arg)).then(ret => {\n return prevHook.call(hooks, ret);\n });\n }\n const ret = hooksFunc.call(hooks, arg);\n return prevHook.call(hooks, ret);\n };\n }\n else {\n hooks[hooksKey] = (...args) => {\n let ret = hooksFunc.apply(hooks, args);\n if (ret === false) {\n ret = prevHook.apply(hooks, args);\n }\n return ret;\n };\n }\n }\n opts.hooks = hooks;\n }\n // ==-- Parse WalkTokens extensions --== //\n if (pack.walkTokens) {\n const walkTokens = this.defaults.walkTokens;\n const packWalktokens = pack.walkTokens;\n opts.walkTokens = function (token) {\n let values = [];\n values.push(packWalktokens.call(this, token));\n if (walkTokens) {\n values = values.concat(walkTokens.call(this, token));\n }\n return values;\n };\n }\n this.defaults = { ...this.defaults, ...opts };\n });\n return this;\n }\n setOptions(opt) {\n this.defaults = { ...this.defaults, ...opt };\n return this;\n }\n lexer(src, options) {\n return _Lexer.lex(src, options ?? this.defaults);\n }\n parser(tokens, options) {\n return _Parser.parse(tokens, options ?? this.defaults);\n }\n #parseMarkdown(lexer, parser) {\n return (src, options) => {\n const origOpt = { ...options };\n const opt = { ...this.defaults, ...origOpt };\n // Show warning if an extension set async to true but the parse was called with async: false\n if (this.defaults.async === true && origOpt.async === false) {\n if (!opt.silent) {\n console.warn('marked(): The async option was set to true by an extension. The async: false option sent to parse will be ignored.');\n }\n opt.async = true;\n }\n const throwError = this.#onError(!!opt.silent, !!opt.async);\n // throw error in case of non string input\n if (typeof src === 'undefined' || src === null) {\n return throwError(new Error('marked(): input parameter is undefined or null'));\n }\n if (typeof src !== 'string') {\n return throwError(new Error('marked(): input parameter is of type '\n + Object.prototype.toString.call(src) + ', string expected'));\n }\n if (opt.hooks) {\n opt.hooks.options = opt;\n }\n if (opt.async) {\n return Promise.resolve(opt.hooks ? opt.hooks.preprocess(src) : src)\n .then(src => lexer(src, opt))\n .then(tokens => opt.walkTokens ? Promise.all(this.walkTokens(tokens, opt.walkTokens)).then(() => tokens) : tokens)\n .then(tokens => parser(tokens, opt))\n .then(html => opt.hooks ? opt.hooks.postprocess(html) : html)\n .catch(throwError);\n }\n try {\n if (opt.hooks) {\n src = opt.hooks.preprocess(src);\n }\n const tokens = lexer(src, opt);\n if (opt.walkTokens) {\n this.walkTokens(tokens, opt.walkTokens);\n }\n let html = parser(tokens, opt);\n if (opt.hooks) {\n html = opt.hooks.postprocess(html);\n }\n return html;\n }\n catch (e) {\n return throwError(e);\n }\n };\n }\n #onError(silent, async) {\n return (e) => {\n e.message += '\\nPlease report this to https://github.com/markedjs/marked.';\n if (silent) {\n const msg = '<p>An error occurred:</p><pre>'\n + escape(e.message + '', true)\n + '</pre>';\n if (async) {\n return Promise.resolve(msg);\n }\n return msg;\n }\n if (async) {\n return Promise.reject(e);\n }\n throw e;\n };\n }\n}\n\nconst markedInstance = new Marked();\nfunction marked(src, opt) {\n return markedInstance.parse(src, opt);\n}\n/**\n * Sets the default options.\n *\n * @param options Hash of options\n */\nmarked.options =\n marked.setOptions = function (options) {\n markedInstance.setOptions(options);\n marked.defaults = markedInstance.defaults;\n changeDefaults(marked.defaults);\n return marked;\n };\n/**\n * Gets the original marked default options.\n */\nmarked.getDefaults = _getDefaults;\nmarked.defaults = _defaults;\n/**\n * Use Extension\n */\nmarked.use = function (...args) {\n markedInstance.use(...args);\n marked.defaults = markedInstance.defaults;\n changeDefaults(marked.defaults);\n return marked;\n};\n/**\n * Run callback for every token\n */\nmarked.walkTokens = function (tokens, callback) {\n return markedInstance.walkTokens(tokens, callback);\n};\n/**\n * Compiles markdown to HTML without enclosing `p` tag.\n *\n * @param src String of markdown source to be compiled\n * @param options Hash of options\n * @return String of compiled HTML\n */\nmarked.parseInline = markedInstance.parseInline;\n/**\n * Expose\n */\nmarked.Parser = _Parser;\nmarked.parser = _Parser.parse;\nmarked.Renderer = _Renderer;\nmarked.TextRenderer = _TextRenderer;\nmarked.Lexer = _Lexer;\nmarked.lexer = _Lexer.lex;\nmarked.Tokenizer = _Tokenizer;\nmarked.Hooks = _Hooks;\nmarked.parse = marked;\nconst options = marked.options;\nconst setOptions = marked.setOptions;\nconst use = marked.use;\nconst walkTokens = marked.walkTokens;\nconst parseInline = marked.parseInline;\nconst parse = marked;\nconst parser = _Parser.parse;\nconst lexer = _Lexer.lex;\n\nexport { _Hooks as Hooks, _Lexer as Lexer, Marked, _Parser as Parser, _Renderer as Renderer, _TextRenderer as TextRenderer, _Tokenizer as Tokenizer, _defaults as defaults, _getDefaults as getDefaults, lexer, marked, options, parse, parseInline, parser, setOptions, use, walkTokens };\n//# sourceMappingURL=marked.esm.js.map\n","'use strict';\n\nfunction index({ interruptPatterns = [], skipEmptyRows = true } = {}) {\n return {\n extensions: [\n {\n name: 'spanTable',\n level: 'block', // Is this a block-level or inline-level tokenizer?\n start(src) { return src.match(/\\n *([^\\n ].*\\|.*)\\n/m)?.index; }, // Hint to Marked.js to stop and check for a match\n tokenizer(src, tokens) {\n // const regex = this.tokenizer.rules.block.table;\n let regexString = '^ *([^\\\\n ].*\\\\|.*\\\\n(?: *[^\\\\s].*\\\\n)*?)' // Header\n + ' {0,3}(?:\\\\| *)?(:?-+(?: *(?:100|[1-9][0-9]?%) *-+)?:? *(?:\\\\| *:?-+(?: *(?:100|[1-9][0-9]?%) *-+)?:? *)*)(?:\\\\| *)?' // Align\n + '(?:\\\\n((?:(?! *\\\\n| {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\\\\* *){3,})' // Cells\n + '(?:\\\\n+|$)| {0,3}#{1,6}(?:\\\\s|$)| {0,3}>| {4}[^\\\\n]| {0,3}(?:`{3,}'\n + '(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n| {0,3}(?:[*+-]|1[.)]) |'\n + '<\\\\/?(?:address|article|aside|base|basefont|blockquote|body'\n + '|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt'\n + '|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]'\n + '|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem'\n + '|meta|nav|noframes|ol|optgroup|option|p|param|section|source'\n + '|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)'\n + '(?: +|\\\\n|\\\\/?>)|<(?:script|pre|style|textarea|!--)endRegex).*(?:\\\\n|$))*)\\\\n*|$)'; // Cells\n\n regexString = regexString.replace('endRegex', interruptPatterns.map(str => `|(?:${str})`).join(''));\n const widthRegex = / *(?:100|[1-9][0-9]?%) */g;\n const regex = new RegExp(regexString);\n const cap = regex.exec(src);\n\n if (cap) {\n const item = {\n type: 'spanTable',\n header: cap[1].replace(/\\n$/, '').split('\\n'),\n align: cap[2].replace(widthRegex, '').replace(/^ *|\\| *$/g, '').split(/ *\\| */),\n rows: cap[3]?.trim() ? cap[3].replace(/\\n[ \\t]*$/, '').split('\\n') : [],\n width: cap[2].replace(/:/g, '').replace(/-+| /g, '').split('|')\n };\n\n // Get first header row to determine how many columns\n item.header[0] = splitCells(item.header[0]);\n\n const colCount = item.header[0].reduce((length, header) => {\n return length + header.colspan;\n }, 0);\n\n if (colCount === item.align.length) {\n item.raw = cap[0];\n\n let i, j, k, row;\n\n // Get alignment row (:---:)\n let l = item.align.length;\n\n for (i = 0; i < l; i++) {\n if (/^ *-+: *$/.test(item.align[i])) {\n item.align[i] = 'right';\n } else if (/^ *:-+: *$/.test(item.align[i])) {\n item.align[i] = 'center';\n } else if (/^ *:-+ *$/.test(item.align[i])) {\n item.align[i] = 'left';\n } else {\n item.align[i] = null;\n }\n }\n\n // Get any remaining header rows\n l = item.header.length;\n for (i = 1; i < l; i++) {\n item.header[i] = splitCells(item.header[i], colCount, item.header[i - 1], skipEmptyRows);\n }\n\n // Get main table cells\n l = item.rows.length;\n for (i = 0; i < l; i++) {\n item.rows[i] = splitCells(item.rows[i], colCount, item.rows[i - 1], skipEmptyRows);\n }\n\n // header child tokens\n l = item.header.length;\n for (j = 0; j < l; j++) {\n row = item.header[j];\n for (k = 0; k < row.length; k++) {\n row[k].tokens = [];\n this.lexer.inline(row[k].text, row[k].tokens);\n }\n }\n\n // cell child tokens\n l = item.rows.length;\n for (j = 0; j < l; j++) {\n row = item.rows[j];\n for (k = 0; k < row.length; k++) {\n row[k].tokens = [];\n this.lexer.inline(row[k].text, row[k].tokens);\n }\n }\n return item;\n }\n }\n },\n renderer(token) {\n let i, j, row, cell, col, text;\n let output = '<table>';\n output += '<thead>';\n for (i = 0; i < token.header.length; i++) {\n row = token.header[i];\n let col = 0;\n output += '<tr>';\n for (j = 0; j < row.length; j++) {\n cell = row[j];\n text = this.parser.parseInline(cell.tokens);\n output += getTableCell(text, cell, 'th', token.align[col], token.width[col]);\n col += cell.colspan;\n }\n output += '</tr>';\n }\n output += '</thead>';\n if (token.rows.length) {\n output += '<tbody>';\n for (i = 0; i < token.rows.length; i++) {\n row = token.rows[i];\n col = 0;\n if (!row[0].emptyRow) {\n output += '<tr>';\n for (j = 0; j < row.length; j++) {\n cell = row[j];\n text = this.parser.parseInline(cell.tokens);\n output += getTableCell(text, cell, 'td', token.align[col], token.width[col]);\n col += cell.colspan;\n }\n output += '</tr>';\n }\n }\n output += '</tbody>';\n }\n output += '</table>';\n return output;\n }\n }\n ]\n };\n}\n\nconst getTableCell = (text, cell, type, align, width) => {\n if (!cell.rowspan) {\n return '';\n }\n const tag = `<${type}`\n + `${cell.colspan > 1 ? ` colspan=${cell.colspan}` : ''}`\n + `${cell.rowspan > 1 ? ` rowspan=${cell.rowspan}` : ''}`\n + `${align ? ` align=${align}` : ''}`\n + `${width ? ` width=${width}` : ''}>`;\n return `${tag + text}</${type}>\\n`;\n};\n\nconst splitCells = (tableRow, count, prevRow = [], skipEmptyRows) => {\n const cells = [...tableRow.trim().matchAll(/(?:[^|\\\\]|\\\\.?)+(?:\\|+|$)/g)].map((x) => x[0]);\n\n // Remove first/last cell in a row if whitespace only and no leading/trailing pipe\n if (!cells[0]?.trim()) { cells.shift(); }\n if (!cells[cells.length - 1]?.trim()) { cells.pop(); }\n\n let numCols = 0;\n let i, j, trimmedCell, prevCell, prevCols;\n\n for (i = 0; i < cells.length; i++) {\n trimmedCell = cells[i].split(/\\|+$/)[0];\n cells[i] = {\n rowspan: 1,\n colspan: Math.max(cells[i].length - trimmedCell.length, 1),\n text: trimmedCell.trim().replace(/\\\\\\|/g, '|')\n // display escaped pipes as normal character\n };\n\n // Handle Rowspan\n if (trimmedCell.slice(-1) === '^' && prevRow.length) {\n // Find matching cell in previous row\n prevCols = 0;\n for (j = 0; j < prevRow.length; j++) {\n prevCell = prevRow[j];\n if ((prevCols === numCols) && (prevCell.colspan === cells[i].colspan)) {\n // merge into matching cell in previous row (the \"target\")\n cells[i].rowSpanTarget = prevCell.rowSpanTarget ?? prevCell;\n cells[i].rowSpanTarget.text += ` ${cells[i].text.slice(0, -1)}`;\n cells[i].rowSpanTarget.rowspan += 1;\n cells[i].rowspan = 0;\n break;\n }\n prevCols += prevCell.colspan;\n if (prevCols > numCols) { break; }\n }\n }\n\n numCols += cells[i].colspan;\n }\n\n // If all cells have been merged, flag as an empty row\n if (cells.length > 0 && skipEmptyRows && cells.length === cells.filter((cell) => { return cell.rowspan === 0; }).length) {\n cells[0].emptyRow = true;\n for (i = 0; i < cells.length; i++) {\n cells[i].rowSpanTarget.rowspan -= 1;\n }\n }\n\n // Force main cell rows to match header column count\n if (numCols > count) {\n cells.splice(count);\n } else {\n while (numCols < count) {\n cells.push({\n rowspan: 1,\n colspan: 1,\n text: ''\n });\n numCols += 1;\n }\n }\n return cells;\n};\n\nmodule.exports = index;\n",":host {\r\n display: block;\r\n width: 100%;\r\n}\r\n\r\n.message-round {\r\n margin-bottom: 16px;\r\n width: 100%;\r\n}\r\n\r\n.user-message-container {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: flex-end;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.assistant-message-container {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: flex-start;\r\n margin-bottom: 8px;\r\n}\r\n\r\n/* 添加消息气泡样式 */\r\n.message-bubble {\r\n padding: 7px 18px;\r\n max-width: 80%;\r\n position: relative;\r\n}\r\n\r\n/* 用户消息气泡样式 */\r\n.user-message {\r\n background-image: linear-gradient(100deg, #4A9FFF 0%, #1058FF 100%);\r\n font-weight: 600;\r\nfont-size: 14px;\r\ncolor: #FFFFFF;\r\n border-radius: 14px 0 14px 14px;\r\n}\r\n\r\n/* 助手消息气泡样式 */\r\n.assistant-message {\r\n background-color: #0000000f;\r\n border-radius: 0 14px 14px 14px;\r\n font-weight: 600;\r\n font-size: 14px;\r\n color: #00000099;\r\n}\r\n\r\n.message-time {\r\n font-size: 12px;\r\n color: #999;\r\n margin-top: 4px;\r\n}\r\n\r\n/* 用户消息时间样式 */\r\n.user-message-container .message-time {\r\n color: rgba(255, 255, 255, 0.7);\r\n}\r\n\r\n.loading-dots {\r\n display: flex;\r\n align-items: center;\r\n padding: 12px 16px;\r\n background-color: #f0f0f0;\r\n border-radius: 18px;\r\n border-top-left-radius: 4px;\r\n}\r\n\r\n.loading-dots span {\r\n width: 8px;\r\n height: 8px;\r\n margin: 0 3px;\r\n background-color: #999;\r\n border-radius: 50%;\r\n display: inline-block;\r\n animation: dot-pulse 1.5s infinite ease-in-out;\r\n}\r\n\r\n.loading-dots span:nth-child(2) {\r\n animation-delay: 0.2s;\r\n}\r\n\r\n.loading-dots span:nth-child(3) {\r\n animation-delay: 0.4s;\r\n}\r\n\r\n@keyframes dot-pulse {\r\n\r\n 0%,\r\n 80%,\r\n 100% {\r\n transform: scale(0.8);\r\n opacity: 0.5;\r\n }\r\n\r\n 40% {\r\n transform: scale(1.2);\r\n opacity: 1;\r\n }\r\n}\r\n\r\n.file-view {\r\n margin-top: 8px;\r\n padding: 12px;\r\n background-color: #f8f9fa;\r\n border: 1px solid #e9ecef;\r\n border-radius: 6px;\r\n font-size: 14px;\r\n word-break: break-all;\r\n color: #2c3e50;\r\n line-height: 1.5;\r\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\r\n}\r\n\r\n.input-view {\r\n margin-top: 8px;\r\n padding-bottom: 10px;\r\n border: 1px solid #e9ecef;\r\n border-radius: 6px;\r\n overflow: hidden;\r\n background-color: #ffffff;\r\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\r\n}\r\n\r\n.input-label {\r\n padding: 8px 12px;\r\n background-color: #f8f9fa;\r\n font-size: 13px;\r\n font-weight: 600;\r\n color: #495057;\r\n border-bottom: 1px solid #e9ecef;\r\n}\r\n\r\n.input-value {\r\n padding: 12px 12px 0px;\r\n font-size: 14px;\r\n line-height: 1.5;\r\n color: #2c3e50;\r\n background-color: #ffffff;\r\n display: -webkit-box;\r\n -webkit-line-clamp: 2;\r\n -webkit-box-orient: vertical;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n}\r\n\r\n.input-metadata {\r\n font-size: 13px;\r\n margin-top: 8px;\r\n}\r\n\r\n.markdown-body {\r\n --base-size-4: 0.25rem;\r\n --base-size-8: 0.5rem;\r\n --base-size-16: 1rem;\r\n --base-size-24: 1.5rem;\r\n --base-size-40: 2.5rem;\r\n --base-text-weight-normal: 400;\r\n --base-text-weight-medium: 500;\r\n --base-text-weight-semibold: 600;\r\n --fontStack-monospace: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;\r\n --fgColor-accent: Highlight;\r\n}\r\n\r\n@media (prefers-color-scheme: dark) {\r\n\r\n .markdown-body,\r\n [data-theme=\"dark\"] {\r\n /* dark */\r\n color-scheme: dark;\r\n --focus-outlineColor: #1f6feb;\r\n --fgColor-default: #f0f6fc;\r\n --fgColor-muted: #9198a1;\r\n --fgColor-accent: #4493f8;\r\n --fgColor-success: #3fb950;\r\n --fgColor-attention: #d29922;\r\n --fgColor-danger: #f85149;\r\n --fgColor-done: #ab7df8;\r\n --bgColor-default: #0d1117;\r\n --bgColor-muted: #151b23;\r\n --bgColor-neutral-muted: #656c7633;\r\n --bgColor-attention-muted: #bb800926;\r\n --borderColor-default: #3d444d;\r\n --borderColor-muted: #3d444db3;\r\n --borderColor-neutral-muted: #3d444db3;\r\n --borderColor-accent-emphasis: #1f6feb;\r\n --borderColor-success-emphasis: #238636;\r\n --borderColor-attention-emphasis: #9e6a03;\r\n --borderColor-danger-emphasis: #da3633;\r\n --borderColor-done-emphasis: #8957e5;\r\n --color-prettylights-syntax-comment: #9198a1;\r\n --color-prettylights-syntax-constant: #79c0ff;\r\n --color-prettylights-syntax-constant-other-reference-link: #a5d6ff;\r\n --color-prettylights-syntax-entity: #d2a8ff;\r\n --color-prettylights-syntax-storage-modifier-import: #f0f6fc;\r\n --color-prettylights-syntax-entity-tag: #7ee787;\r\n --color-prettylights-syntax-keyword: #ff7b72;\r\n --color-prettylights-syntax-string: #a5d6ff;\r\n --color-prettylights-syntax-variable: #ffa657;\r\n --color-prettylights-syntax-brackethighlighter-unmatched: #f85149;\r\n --color-prettylights-syntax-brackethighlighter-angle: #9198a1;\r\n --color-prettylights-syntax-invalid-illegal-text: #f0f6fc;\r\n --color-prettylights-syntax-invalid-illegal-bg: #8e1519;\r\n --color-prettylights-syntax-carriage-return-text: #f0f6fc;\r\n --color-prettylights-syntax-carriage-return-bg: #b62324;\r\n --color-prettylights-syntax-string-regexp: #7ee787;\r\n --color-prettylights-syntax-markup-list: #f2cc60;\r\n --color-prettylights-syntax-markup-heading: #1f6feb;\r\n --color-prettylights-syntax-markup-italic: #f0f6fc;\r\n --color-prettylights-syntax-markup-bold: #f0f6fc;\r\n --color-prettylights-syntax-markup-deleted-text: #ffdcd7;\r\n --color-prettylights-syntax-markup-deleted-bg: #67060c;\r\n --color-prettylights-syntax-markup-inserted-text: #aff5b4;\r\n --color-prettylights-syntax-markup-inserted-bg: #033a16;\r\n --color-prettylights-syntax-markup-changed-text: #ffdfb6;\r\n --color-prettylights-syntax-markup-changed-bg: #5a1e02;\r\n --color-prettylights-syntax-markup-ignored-text: #f0f6fc;\r\n --color-prettylights-syntax-markup-ignored-bg: #1158c7;\r\n --color-prettylights-syntax-meta-diff-range: #d2a8ff;\r\n --color-prettylights-syntax-sublimelinter-gutter-mark: #3d444d;\r\n }\r\n}\r\n\r\n@media (prefers-color-scheme: light) {\r\n\r\n .markdown-body,\r\n [data-theme=\"light\"] {\r\n /* light */\r\n color-scheme: light;\r\n --focus-outlineColor: #0969da;\r\n --fgColor-default: #1f2328;\r\n --fgColor-muted: #59636e;\r\n --fgColor-accent: #0969da;\r\n --fgColor-success: #1a7f37;\r\n --fgColor-attention: #9a6700;\r\n --fgColor-danger: #d1242f;\r\n --fgColor-done: #8250df;\r\n --bgColor-default: #ffffff;\r\n --bgColor-muted: #f6f8fa;\r\n --bgColor-neutral-muted: #818b981f;\r\n --bgColor-attention-muted: #fff8c5;\r\n --borderColor-default: #d1d9e0;\r\n --borderColor-muted: #d1d9e0b3;\r\n --borderColor-neutral-muted: #d1d9e0b3;\r\n --borderColor-accent-emphasis: #0969da;\r\n --borderColor-success-emphasis: #1a7f37;\r\n --borderColor-attention-emphasis: #9a6700;\r\n --borderColor-danger-emphasis: #cf222e;\r\n --borderColor-done-emphasis: #8250df;\r\n --color-prettylights-syntax-comment: #59636e;\r\n --color-prettylights-syntax-constant: #0550ae;\r\n --color-prettylights-syntax-constant-other-reference-link: #0a3069;\r\n --color-prettylights-syntax-entity: #6639ba;\r\n --color-prettylights-syntax-storage-modifier-import: #1f2328;\r\n --color-prettylights-syntax-entity-tag: #0550ae;\r\n --color-prettylights-syntax-keyword: #cf222e;\r\n --color-prettylights-syntax-string: #0a3069;\r\n --color-prettylights-syntax-variable: #953800;\r\n --color-prettylights-syntax-brackethighlighter-unmatched: #82071e;\r\n --color-prettylights-syntax-brackethighlighter-angle: #59636e;\r\n --color-prettylights-syntax-invalid-illegal-text: #f6f8fa;\r\n --color-prettylights-syntax-invalid-illegal-bg: #82071e;\r\n --color-prettylights-syntax-carriage-return-text: #f6f8fa;\r\n --color-prettylights-syntax-carriage-return-bg: #cf222e;\r\n --color-prettylights-syntax-string-regexp: #116329;\r\n --color-prettylights-syntax-markup-list: #3b2300;\r\n --color-prettylights-syntax-markup-heading: #0550ae;\r\n --color-prettylights-syntax-markup-italic: #1f2328;\r\n --color-prettylights-syntax-markup-bold: #1f2328;\r\n --color-prettylights-syntax-markup-deleted-text: #82071e;\r\n --color-prettylights-syntax-markup-deleted-bg: #ffebe9;\r\n --color-prettylights-syntax-markup-inserted-text: #116329;\r\n --color-prettylights-syntax-markup-inserted-bg: #dafbe1;\r\n --color-prettylights-syntax-markup-changed-text: #953800;\r\n --color-prettylights-syntax-markup-changed-bg: #ffd8b5;\r\n --color-prettylights-syntax-markup-ignored-text: #d1d9e0;\r\n --color-prettylights-syntax-markup-ignored-bg: #0550ae;\r\n --color-prettylights-syntax-meta-diff-range: #8250df;\r\n --color-prettylights-syntax-sublimelinter-gutter-mark: #818b98;\r\n }\r\n}\r\n\r\n.markdown-body {\r\n -ms-text-size-adjust: 100%;\r\n -webkit-text-size-adjust: 100%;\r\n margin: 0;\r\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Noto Sans\", Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\";\r\n font-size: 16px;\r\n line-height: 1.5;\r\n word-wrap: break-word;\r\n}\r\n\r\n.markdown-body .octicon {\r\n display: inline-block;\r\n fill: currentColor;\r\n vertical-align: text-bottom;\r\n}\r\n\r\n.markdown-body h1:hover .anchor .octicon-link:before,\r\n.markdown-body h2:hover .anchor .octicon-link:before,\r\n.markdown-body h3:hover .anchor .octicon-link:before,\r\n.markdown-body h4:hover .anchor .octicon-link:before,\r\n.markdown-body h5:hover .anchor .octicon-link:before,\r\n.markdown-body h6:hover .anchor .octicon-link:before {\r\n width: 16px;\r\n height: 16px;\r\n content: ' ';\r\n display: inline-block;\r\n background-color: currentColor;\r\n -webkit-mask-image: url(\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>\");\r\n mask-image: url(\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>\");\r\n}\r\n\r\n.markdown-body details,\r\n.markdown-body figcaption,\r\n.markdown-body figure {\r\n display: block;\r\n}\r\n\r\n.markdown-body summary {\r\n display: list-item;\r\n}\r\n\r\n.markdown-body [hidden] {\r\n display: none !important;\r\n}\r\n\r\n.markdown-body a {\r\n background-color: transparent;\r\n color: var(--fgColor-accent);\r\n text-decoration: none;\r\n}\r\n\r\n.markdown-body abbr[title] {\r\n border-bottom: none;\r\n -webkit-text-decoration: underline dotted;\r\n text-decoration: underline dotted;\r\n}\r\n\r\n.markdown-body b,\r\n.markdown-body strong {\r\n font-weight: var(--base-text-weight-semibold, 600);\r\n}\r\n\r\n.markdown-body dfn {\r\n font-style: italic;\r\n}\r\n\r\n.markdown-body h1 {\r\n margin: .67em 0;\r\n font-weight: var(--base-text-weight-semibold, 600);\r\n padding-bottom: .3em;\r\n font-size: 1.5em;\r\n border-bottom: 1px solid var(--borderColor-muted);\r\n}\r\n\r\n.markdown-body mark {\r\n background-color: var(--bgColor-attention-muted);\r\n color: var(--fgColor-default);\r\n}\r\n\r\n.markdown-body small {\r\n font-size: 90%;\r\n}\r\n\r\n.markdown-body sub,\r\n.markdown-body sup {\r\n font-size: 75%;\r\n line-height: 0;\r\n position: relative;\r\n vertical-align: baseline;\r\n}\r\n\r\n.markdown-body sub {\r\n bottom: -0.25em;\r\n}\r\n\r\n.markdown-body sup {\r\n top: -0.5em;\r\n}\r\n\r\n.markdown-body img {\r\n border-style: none;\r\n max-width: 100%;\r\n box-sizing: content-box;\r\n}\r\n\r\n.markdown-body code,\r\n.markdown-body kbd,\r\n.markdown-body pre,\r\n.markdown-body samp {\r\n font-family: monospace;\r\n font-size: 1em;\r\n}\r\n\r\n.markdown-body figure {\r\n margin: 1em var(--base-size-40);\r\n}\r\n\r\n.markdown-body hr {\r\n box-sizing: content-box;\r\n overflow: hidden;\r\n background: transparent;\r\n border-bottom: 1px solid var(--borderColor-muted);\r\n height: .25em;\r\n padding: 0;\r\n margin: var(--base-size-24) 0;\r\n background-color: var(--borderColor-default);\r\n border: 0;\r\n}\r\n\r\n.markdown-body input {\r\n font: inherit;\r\n margin: 0;\r\n overflow: visible;\r\n font-family: inherit;\r\n font-size: inherit;\r\n line-height: inherit;\r\n}\r\n\r\n.markdown-body [type=button],\r\n.markdown-body [type=reset],\r\n.markdown-body [type=submit] {\r\n -webkit-appearance: button;\r\n appearance: button;\r\n}\r\n\r\n.markdown-body [type=checkbox],\r\n.markdown-body [type=radio] {\r\n box-sizing: border-box;\r\n padding: 0;\r\n}\r\n\r\n.markdown-body [type=number]::-webkit-inner-spin-button,\r\n.markdown-body [type=number]::-webkit-outer-spin-button {\r\n height: auto;\r\n}\r\n\r\n.markdown-body [type=search]::-webkit-search-cancel-button,\r\n.markdown-body [type=search]::-webkit-search-decoration {\r\n -webkit-appearance: none;\r\n appearance: none;\r\n}\r\n\r\n.markdown-body ::-webkit-input-placeholder {\r\n color: inherit;\r\n opacity: .54;\r\n}\r\n\r\n.markdown-body ::-webkit-file-upload-button {\r\n -webkit-appearance: button;\r\n appearance: button;\r\n font: inherit;\r\n}\r\n\r\n.markdown-body a:hover {\r\n text-decoration: underline;\r\n}\r\n\r\n.markdown-body ::placeholder {\r\n color: var(--fgColor-muted);\r\n opacity: 1;\r\n}\r\n\r\n.markdown-body hr::before {\r\n display: table;\r\n content: \"\";\r\n}\r\n\r\n.markdown-body hr::after {\r\n display: table;\r\n clear: both;\r\n content: \"\";\r\n}\r\n\r\n.markdown-body table {\r\n border-spacing: 0;\r\n border-collapse: collapse;\r\n display: block;\r\n width: max-content;\r\n max-width: 100%;\r\n overflow: auto;\r\n font-variant: tabular-nums;\r\n}\r\n\r\n.markdown-body td,\r\n.markdown-body th {\r\n padding: 0;\r\n}\r\n\r\n.markdown-body details summary {\r\n cursor: pointer;\r\n}\r\n\r\n.markdown-body a:focus,\r\n.markdown-body [role=button]:focus,\r\n.markdown-body input[type=radio]:focus,\r\n.markdown-body input[type=checkbox]:focus {\r\n outline: 2px solid var(--focus-outlineColor);\r\n outline-offset: -2px;\r\n box-shadow: none;\r\n}\r\n\r\n.markdown-body a:focus:not(:focus-visible),\r\n.markdown-body [role=button]:focus:not(:focus-visible),\r\n.markdown-body input[type=radio]:focus:not(:focus-visible),\r\n.markdown-body input[type=checkbox]:focus:not(:focus-visible) {\r\n outline: solid 1px transparent;\r\n}\r\n\r\n.markdown-body a:focus-visible,\r\n.markdown-body [role=button]:focus-visible,\r\n.markdown-body input[type=radio]:focus-visible,\r\n.markdown-body input[type=checkbox]:focus-visible {\r\n outline: 2px solid var(--focus-outlineColor);\r\n outline-offset: -2px;\r\n box-shadow: none;\r\n}\r\n\r\n.markdown-body a:not([class]):focus,\r\n.markdown-body a:not([class]):focus-visible,\r\n.markdown-body input[type=radio]:focus,\r\n.markdown-body input[type=radio]:focus-visible,\r\n.markdown-body input[type=checkbox]:focus,\r\n.markdown-body input[type=checkbox]:focus-visible {\r\n outline-offset: 0;\r\n}\r\n\r\n.markdown-body kbd {\r\n display: inline-block;\r\n padding: var(--base-size-4);\r\n font: 11px var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace);\r\n line-height: 10px;\r\n color: var(--fgColor-default);\r\n vertical-align: middle;\r\n background-color: var(--bgColor-muted);\r\n border: solid 1px var(--borderColor-neutral-muted);\r\n border-bottom-color: var(--borderColor-neutral-muted);\r\n border-radius: 6px;\r\n box-shadow: inset 0 -1px 0 var(--borderColor-neutral-muted);\r\n}\r\n\r\n.markdown-body h1,\r\n.markdown-body h2,\r\n.markdown-body h3,\r\n.markdown-body h4,\r\n.markdown-body h5,\r\n.markdown-body h6 {\r\n margin-top: var(--base-size-24);\r\n margin-bottom: var(--base-size-16);\r\n font-weight: var(--base-text-weight-semibold, 600);\r\n line-height: 1.25;\r\n}\r\n\r\n.markdown-body h2 {\r\n font-weight: var(--base-text-weight-semibold, 600);\r\n padding-bottom: .3em;\r\n font-size: 1.2em;\r\n border-bottom: 1px solid var(--borderColor-muted);\r\n}\r\n\r\n.markdown-body h3 {\r\n font-weight: var(--base-text-weight-semibold, 600);\r\n font-size: 1.05em;\r\n}\r\n\r\n.markdown-body h4 {\r\n font-weight: var(--base-text-weight-semibold, 600);\r\n font-size: 0.875em;\r\n}\r\n\r\n.markdown-body h5 {\r\n font-weight: var(--base-text-weight-semibold, 600);\r\n font-size: .85em;\r\n}\r\n\r\n.markdown-body h6 {\r\n font-weight: var(--base-text-weight-semibold, 600);\r\n font-size: .80em;\r\n color: var(--fgColor-muted);\r\n}\r\n\r\n.markdown-body p {\r\n margin-top: 0;\r\n margin-bottom: 10px;\r\n}\r\n\r\n.markdown-body blockquote {\r\n margin: 0;\r\n padding: 0 1em;\r\n color: var(--fgColor-muted);\r\n border-left: .25em solid var(--borderColor-default);\r\n}\r\n\r\n.markdown-body ul,\r\n.markdown-body ol {\r\n margin-top: 0;\r\n margin-bottom: 0;\r\n padding-left: 2em;\r\n}\r\n\r\n.markdown-body ol ol,\r\n.markdown-body ul ol {\r\n list-style-type: lower-roman;\r\n}\r\n\r\n.markdown-body ul ul ol,\r\n.markdown-body ul ol ol,\r\n.markdown-body ol ul ol,\r\n.markdown-body ol ol ol {\r\n list-style-type: lower-alpha;\r\n}\r\n\r\n.markdown-body dd {\r\n margin-left: 0;\r\n}\r\n\r\n.markdown-body tt,\r\n.markdown-body code,\r\n.markdown-body samp {\r\n font-family: var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace);\r\n font-size: 12px;\r\n}\r\n\r\n.markdown-body pre {\r\n margin-top: 0;\r\n margin-bottom: 0;\r\n font-family: var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace);\r\n font-size: 12px;\r\n word-wrap: normal;\r\n}\r\n\r\n.markdown-body .octicon {\r\n display: inline-block;\r\n overflow: visible !important;\r\n vertical-align: text-bottom;\r\n fill: currentColor;\r\n}\r\n\r\n.markdown-body input::-webkit-outer-spin-button,\r\n.markdown-body input::-webkit-inner-spin-button {\r\n margin: 0;\r\n appearance: none;\r\n}\r\n\r\n.markdown-body .mr-2 {\r\n margin-right: var(--base-size-8, 8px) !important;\r\n}\r\n\r\n.markdown-body::before {\r\n display: table;\r\n content: \"\";\r\n}\r\n\r\n.markdown-body::after {\r\n display: table;\r\n clear: both;\r\n content: \"\";\r\n}\r\n\r\n.markdown-body>*:first-child {\r\n margin-top: 0 !important;\r\n}\r\n\r\n.markdown-body>*:last-child {\r\n margin-bottom: 0 !important;\r\n}\r\n\r\n.markdown-body a:not([href]) {\r\n color: inherit;\r\n text-decoration: none;\r\n}\r\n\r\n.markdown-body .absent {\r\n color: var(--fgColor-danger);\r\n}\r\n\r\n.markdown-body .anchor {\r\n float: left;\r\n padding-right: var(--base-size-4);\r\n margin-left: -20px;\r\n line-height: 1;\r\n}\r\n\r\n.markdown-body .anchor:focus {\r\n outline: none;\r\n}\r\n\r\n.markdown-body p,\r\n.markdown-body blockquote,\r\n.markdown-body ul,\r\n.markdown-body ol,\r\n.markdown-body dl,\r\n.markdown-body table,\r\n.markdown-body pre,\r\n.markdown-body details {\r\n margin-top: 0;\r\n margin-bottom: var(--base-size-16);\r\n font-size: 16px;\r\n font-weight: 400;\r\n}\r\n\r\n.markdown-body blockquote>:first-child {\r\n margin-top: 0;\r\n}\r\n\r\n.markdown-body blockquote>:last-child {\r\n margin-bottom: 0;\r\n}\r\n\r\n.markdown-body h1 .octicon-link,\r\n.markdown-body h2 .octicon-link,\r\n.markdown-body h3 .octicon-link,\r\n.markdown-body h4 .octicon-link,\r\n.markdown-body h5 .octicon-link,\r\n.markdown-body h6 .octicon-link {\r\n color: var(--fgColor-default);\r\n vertical-align: middle;\r\n visibility: hidden;\r\n}\r\n\r\n.markdown-body h1:hover .anchor,\r\n.markdown-body h2:hover .anchor,\r\n.markdown-body h3:hover .anchor,\r\n.markdown-body h4:hover .anchor,\r\n.markdown-body h5:hover .anchor,\r\n.markdown-body h6:hover .anchor {\r\n text-decoration: none;\r\n}\r\n\r\n.markdown-body h1:hover .anchor .octicon-link,\r\n.markdown-body h2:hover .anchor .octicon-link,\r\n.markdown-body h3:hover .anchor .octicon-link,\r\n.markdown-body h4:hover .anchor .octicon-link,\r\n.markdown-body h5:hover .anchor .octicon-link,\r\n.markdown-body h6:hover .anchor .octicon-link {\r\n visibility: visible;\r\n}\r\n\r\n.markdown-body h1 tt,\r\n.markdown-body h1 code,\r\n.markdown-body h2 tt,\r\n.markdown-body h2 code,\r\n.markdown-body h3 tt,\r\n.markdown-body h3 code,\r\n.markdown-body h4 tt,\r\n.markdown-body h4 code,\r\n.markdown-body h5 tt,\r\n.markdown-body h5 code,\r\n.markdown-body h6 tt,\r\n.markdown-body h6 code {\r\n padding: 0 .2em;\r\n font-size: inherit;\r\n}\r\n\r\n.markdown-body summary h1,\r\n.markdown-body summary h2,\r\n.markdown-body summary h3,\r\n.markdown-body summary h4,\r\n.markdown-body summary h5,\r\n.markdown-body summary h6 {\r\n display: inline-block;\r\n}\r\n\r\n.markdown-body summary h1 .anchor,\r\n.markdown-body summary h2 .anchor,\r\n.markdown-body summary h3 .anchor,\r\n.markdown-body summary h4 .anchor,\r\n.markdown-body summary h5 .anchor,\r\n.markdown-body summary h6 .anchor {\r\n margin-left: -40px;\r\n}\r\n\r\n.markdown-body summary h1,\r\n.markdown-body summary h2 {\r\n padding-bottom: 0;\r\n border-bottom: 0;\r\n}\r\n\r\n.markdown-body ul.no-list,\r\n.markdown-body ol.no-list {\r\n padding: 0;\r\n list-style-type: none;\r\n}\r\n\r\n.markdown-body ol[type=\"a s\"] {\r\n list-style-type: lower-alpha;\r\n}\r\n\r\n.markdown-body ol[type=\"A s\"] {\r\n list-style-type: upper-alpha;\r\n}\r\n\r\n.markdown-body ol[type=\"i s\"] {\r\n list-style-type: lower-roman;\r\n}\r\n\r\n.markdown-body ol[type=\"I s\"] {\r\n list-style-type: upper-roman;\r\n}\r\n\r\n.markdown-body ol[type=\"1\"] {\r\n list-style-type: decimal;\r\n}\r\n\r\n.markdown-body div>ol:not([type]) {\r\n list-style-type: decimal;\r\n}\r\n\r\n.markdown-body ul ul,\r\n.markdown-body ul ol,\r\n.markdown-body ol ol,\r\n.markdown-body ol ul {\r\n margin-top: 0;\r\n margin-bottom: 0;\r\n}\r\n\r\n.markdown-body li>p {\r\n margin-top: var(--base-size-16);\r\n}\r\n\r\n.markdown-body li+li {\r\n margin-top: .25em;\r\n}\r\n\r\n.markdown-body dl {\r\n padding: 0;\r\n}\r\n\r\n.markdown-body dl dt {\r\n padding: 0;\r\n margin-top: var(--base-size-16);\r\n font-size: 1em;\r\n font-style: italic;\r\n font-weight: var(--base-text-weight-semibold, 600);\r\n}\r\n\r\n.markdown-body dl dd {\r\n padding: 0 var(--base-size-16);\r\n margin-bottom: var(--base-size-16);\r\n}\r\n\r\n.markdown-body table th {\r\n font-weight: var(--base-text-weight-semibold, 600);\r\n}\r\n\r\n.markdown-body table th,\r\n.markdown-body table td {\r\n padding: 6px 13px;\r\n border: 1px solid var(--borderColor-default);\r\n}\r\n\r\n.markdown-body table td>:last-child {\r\n margin-bottom: 0;\r\n}\r\n\r\n.markdown-body table tr {\r\n background-color: var(--bgColor-default);\r\n border-top: 1px solid var(--borderColor-muted);\r\n}\r\n\r\n.markdown-body table tr:nth-child(2n) {\r\n background-color: var(--bgColor-muted);\r\n}\r\n\r\n.markdown-body table img {\r\n background-color: transparent;\r\n}\r\n\r\n.markdown-body img[align=right] {\r\n padding-left: 20px;\r\n}\r\n\r\n.markdown-body img[align=left] {\r\n padding-right: 20px;\r\n}\r\n\r\n.markdown-body .emoji {\r\n max-width: none;\r\n vertical-align: text-top;\r\n background-color: transparent;\r\n}\r\n\r\n.markdown-body span.frame {\r\n display: block;\r\n overflow: hidden;\r\n}\r\n\r\n.markdown-body span.frame>span {\r\n display: block;\r\n float: left;\r\n width: auto;\r\n padding: 7px;\r\n margin: 13px 0 0;\r\n overflow: hidden;\r\n border: 1px solid var(--borderColor-default);\r\n}\r\n\r\n.markdown-body span.frame span img {\r\n display: block;\r\n float: left;\r\n}\r\n\r\n.markdown-body span.frame span span {\r\n display: block;\r\n padding: 5px 0 0;\r\n clear: both;\r\n color: var(--fgColor-default);\r\n}\r\n\r\n.markdown-body span.align-center {\r\n display: block;\r\n overflow: hidden;\r\n clear: both;\r\n}\r\n\r\n.markdown-body span.align-center>span {\r\n display: block;\r\n margin: 13px auto 0;\r\n overflow: hidden;\r\n text-align: center;\r\n}\r\n\r\n.markdown-body span.align-center span img {\r\n margin: 0 auto;\r\n text-align: center;\r\n}\r\n\r\n.markdown-body span.align-right {\r\n display: block;\r\n overflow: hidden;\r\n clear: both;\r\n}\r\n\r\n.markdown-body span.align-right>span {\r\n display: block;\r\n margin: 13px 0 0;\r\n overflow: hidden;\r\n text-align: right;\r\n}\r\n\r\n.markdown-body span.align-right span img {\r\n margin: 0;\r\n text-align: right;\r\n}\r\n\r\n.markdown-body span.float-left {\r\n display: block;\r\n float: left;\r\n margin-right: 13px;\r\n overflow: hidden;\r\n}\r\n\r\n.markdown-body span.float-left span {\r\n margin: 13px 0 0;\r\n}\r\n\r\n.markdown-body span.float-right {\r\n display: block;\r\n float: right;\r\n margin-left: 13px;\r\n overflow: hidden;\r\n}\r\n\r\n.markdown-body span.float-right>span {\r\n display: block;\r\n margin: 13px auto 0;\r\n overflow: hidden;\r\n text-align: right;\r\n}\r\n\r\n.markdown-body code,\r\n.markdown-body tt {\r\n padding: .2em .4em;\r\n margin: 0;\r\n font-size: 85%;\r\n white-space: break-spaces;\r\n background-color: var(--bgColor-neutral-muted);\r\n border-radius: 6px;\r\n}\r\n\r\n.markdown-body code br,\r\n.markdown-body tt br {\r\n display: none;\r\n}\r\n\r\n.markdown-body del code {\r\n text-decoration: inherit;\r\n}\r\n\r\n.markdown-body samp {\r\n font-size: 85%;\r\n}\r\n\r\n.markdown-body pre code {\r\n font-size: 100%;\r\n}\r\n\r\n.markdown-body pre>code {\r\n padding: 0;\r\n margin: 0;\r\n word-break: normal;\r\n white-space: pre;\r\n background: transparent;\r\n border: 0;\r\n}\r\n\r\n.markdown-body .highlight {\r\n margin-bottom: var(--base-size-16);\r\n}\r\n\r\n.markdown-body .highlight pre {\r\n margin-bottom: 0;\r\n word-break: normal;\r\n}\r\n\r\n.markdown-body .highlight pre,\r\n.markdown-body pre {\r\n padding: var(--base-size-16);\r\n overflow: auto;\r\n font-size: 85%;\r\n line-height: 1.45;\r\n color: var(--fgColor-default);\r\n background-color: var(--bgColor-muted);\r\n border-radius: 6px;\r\n}\r\n\r\n.markdown-body pre code,\r\n.markdown-body pre tt {\r\n display: inline;\r\n max-width: auto;\r\n padding: 0;\r\n margin: 0;\r\n overflow: visible;\r\n line-height: inherit;\r\n word-wrap: normal;\r\n background-color: transparent;\r\n border: 0;\r\n}\r\n\r\n.markdown-body .csv-data td,\r\n.markdown-body .csv-data th {\r\n padding: 5px;\r\n overflow: hidden;\r\n font-size: 12px;\r\n line-height: 1;\r\n text-align: left;\r\n white-space: nowrap;\r\n}\r\n\r\n.markdown-body .csv-data .blob-num {\r\n padding: 10px var(--base-size-8) 9px;\r\n text-align: right;\r\n background: var(--bgColor-default);\r\n border: 0;\r\n}\r\n\r\n.markdown-body .csv-data tr {\r\n border-top: 0;\r\n}\r\n\r\n.markdown-body .csv-data th {\r\n font-weight: var(--base-text-weight-semibold, 600);\r\n background: var(--bgColor-muted);\r\n border-top: 0;\r\n}\r\n\r\n.markdown-body [data-footnote-ref]::before {\r\n content: \"[\";\r\n}\r\n\r\n.markdown-body [data-footnote-ref]::after {\r\n content: \"]\";\r\n}\r\n\r\n.markdown-body .footnotes {\r\n font-size: 12px;\r\n color: var(--fgColor-muted);\r\n border-top: 1px solid var(--borderColor-default);\r\n}\r\n\r\n.markdown-body .footnotes ol {\r\n padding-left: var(--base-size-16);\r\n}\r\n\r\n.markdown-body .footnotes ol ul {\r\n display: inline-block;\r\n padding-left: var(--base-size-16);\r\n margin-top: var(--base-size-16);\r\n}\r\n\r\n.markdown-body .footnotes li {\r\n position: relative;\r\n}\r\n\r\n.markdown-body .footnotes li:target::before {\r\n position: absolute;\r\n top: calc(var(--base-size-8)*-1);\r\n right: calc(var(--base-size-8)*-1);\r\n bottom: calc(var(--base-size-8)*-1);\r\n left: calc(var(--base-size-24)*-1);\r\n pointer-events: none;\r\n content: \"\";\r\n border: 2px solid var(--borderColor-accent-emphasis);\r\n border-radius: 6px;\r\n}\r\n\r\n.markdown-body .footnotes li:target {\r\n color: var(--fgColor-default);\r\n}\r\n\r\n.markdown-body .footnotes .data-footnote-backref g-emoji {\r\n font-family: monospace;\r\n}\r\n\r\n.markdown-body body:has(:modal) {\r\n padding-right: var(--dialog-scrollgutter) !important;\r\n}\r\n\r\n.markdown-body .pl-c {\r\n color: var(--color-prettylights-syntax-comment);\r\n}\r\n\r\n.markdown-body .pl-c1,\r\n.markdown-body .pl-s .pl-v {\r\n color: var(--color-prettylights-syntax-constant);\r\n}\r\n\r\n.markdown-body .pl-e,\r\n.markdown-body .pl-en {\r\n color: var(--color-prettylights-syntax-entity);\r\n}\r\n\r\n.markdown-body .pl-smi,\r\n.markdown-body .pl-s .pl-s1 {\r\n color: var(--color-prettylights-syntax-storage-modifier-import);\r\n}\r\n\r\n.markdown-body .pl-ent {\r\n color: var(--color-prettylights-syntax-entity-tag);\r\n}\r\n\r\n.markdown-body .pl-k {\r\n color: var(--color-prettylights-syntax-keyword);\r\n}\r\n\r\n.markdown-body .pl-s,\r\n.markdown-body .pl-pds,\r\n.markdown-body .pl-s .pl-pse .pl-s1,\r\n.markdown-body .pl-sr,\r\n.markdown-body .pl-sr .pl-cce,\r\n.markdown-body .pl-sr .pl-sre,\r\n.markdown-body .pl-sr .pl-sra {\r\n color: var(--color-prettylights-syntax-string);\r\n}\r\n\r\n.markdown-body .pl-v,\r\n.markdown-body .pl-smw {\r\n color: var(--color-prettylights-syntax-variable);\r\n}\r\n\r\n.markdown-body .pl-bu {\r\n color: var(--color-prettylights-syntax-brackethighlighter-unmatched);\r\n}\r\n\r\n.markdown-body .pl-ii {\r\n color: var(--color-prettylights-syntax-invalid-illegal-text);\r\n background-color: var(--color-prettylights-syntax-invalid-illegal-bg);\r\n}\r\n\r\n.markdown-body .pl-c2 {\r\n color: var(--color-prettylights-syntax-carriage-return-text);\r\n background-color: var(--color-prettylights-syntax-carriage-return-bg);\r\n}\r\n\r\n.markdown-body .pl-sr .pl-cce {\r\n font-weight: bold;\r\n color: var(--color-prettylights-syntax-string-regexp);\r\n}\r\n\r\n.markdown-body .pl-ml {\r\n color: var(--color-prettylights-syntax-markup-list);\r\n}\r\n\r\n.markdown-body .pl-mh,\r\n.markdown-body .pl-mh .pl-en,\r\n.markdown-body .pl-ms {\r\n font-weight: bold;\r\n color: var(--color-prettylights-syntax-markup-heading);\r\n}\r\n\r\n.markdown-body .pl-mi {\r\n font-style: italic;\r\n color: var(--color-prettylights-syntax-markup-italic);\r\n}\r\n\r\n.markdown-body .pl-mb {\r\n font-weight: bold;\r\n color: var(--color-prettylights-syntax-markup-bold);\r\n}\r\n\r\n.markdown-body .pl-md {\r\n color: var(--color-prettylights-syntax-markup-deleted-text);\r\n background-color: var(--color-prettylights-syntax-markup-deleted-bg);\r\n}\r\n\r\n.markdown-body .pl-mi1 {\r\n color: var(--color-prettylights-syntax-markup-inserted-text);\r\n background-color: var(--color-prettylights-syntax-markup-inserted-bg);\r\n}\r\n\r\n.markdown-body .pl-mc {\r\n color: var(--color-prettylights-syntax-markup-changed-text);\r\n background-color: var(--color-prettylights-syntax-markup-changed-bg);\r\n}\r\n\r\n.markdown-body .pl-mi2 {\r\n color: var(--color-prettylights-syntax-markup-ignored-text);\r\n background-color: var(--color-prettylights-syntax-markup-ignored-bg);\r\n}\r\n\r\n.markdown-body .pl-mdr {\r\n font-weight: bold;\r\n color: var(--color-prettylights-syntax-meta-diff-range);\r\n}\r\n\r\n.markdown-body .pl-ba {\r\n color: var(--color-prettylights-syntax-brackethighlighter-angle);\r\n}\r\n\r\n.markdown-body .pl-sg {\r\n color: var(--color-prettylights-syntax-sublimelinter-gutter-mark);\r\n}\r\n\r\n.markdown-body .pl-corl {\r\n text-decoration: underline;\r\n color: var(--color-prettylights-syntax-constant-other-reference-link);\r\n}\r\n\r\n.markdown-body [role=button]:focus:not(:focus-visible),\r\n.markdown-body [role=tabpanel][tabindex=\"0\"]:focus:not(:focus-visible),\r\n.markdown-body button:focus:not(:focus-visible),\r\n.markdown-body summary:focus:not(:focus-visible),\r\n.markdown-body a:focus:not(:focus-visible) {\r\n outline: none;\r\n box-shadow: none;\r\n}\r\n\r\n.markdown-body [tabindex=\"0\"]:focus:not(:focus-visible),\r\n.markdown-body details-dialog:focus:not(:focus-visible) {\r\n outline: none;\r\n}\r\n\r\n.markdown-body g-emoji {\r\n display: inline-block;\r\n min-width: 1ch;\r\n font-family: \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\r\n font-size: 1em;\r\n font-style: normal !important;\r\n font-weight: var(--base-text-weight-normal, 400);\r\n line-height: 1;\r\n vertical-align: -0.075em;\r\n}\r\n\r\n.markdown-body g-emoji img {\r\n width: 1em;\r\n height: 1em;\r\n}\r\n\r\n.markdown-body .task-list-item {\r\n list-style-type: none;\r\n}\r\n\r\n.markdown-body .task-list-item label {\r\n font-weight: var(--base-text-weight-normal, 400);\r\n}\r\n\r\n.markdown-body .task-list-item.enabled label {\r\n cursor: pointer;\r\n}\r\n\r\n.markdown-body .task-list-item+.task-list-item {\r\n margin-top: var(--base-size-4);\r\n}\r\n\r\n.markdown-body .task-list-item .handle {\r\n display: none;\r\n}\r\n\r\n.markdown-body .task-list-item-checkbox {\r\n margin: 0 .2em .25em -1.4em;\r\n vertical-align: middle;\r\n}\r\n\r\n.markdown-body ul:dir(rtl) .task-list-item-checkbox {\r\n margin: 0 -1.6em .25em .2em;\r\n}\r\n\r\n.markdown-body ol:dir(rtl) .task-list-item-checkbox {\r\n margin: 0 -1.6em .25em .2em;\r\n}\r\n\r\n.markdown-body .contains-task-list:hover .task-list-item-convert-container,\r\n.markdown-body .contains-task-list:focus-within .task-list-item-convert-container {\r\n display: block;\r\n width: auto;\r\n height: 24px;\r\n overflow: visible;\r\n clip: auto;\r\n}\r\n\r\n.markdown-body ::-webkit-calendar-picker-indicator {\r\n filter: invert(50%);\r\n}\r\n\r\n.markdown-body .markdown-alert {\r\n padding: var(--base-size-8) var(--base-size-16);\r\n margin-bottom: var(--base-size-16);\r\n color: inherit;\r\n border-left: .25em solid var(--borderColor-default);\r\n}\r\n\r\n.markdown-body .markdown-alert>:first-child {\r\n margin-top: 0;\r\n}\r\n\r\n.markdown-body .markdown-alert>:last-child {\r\n margin-bottom: 0;\r\n}\r\n\r\n.markdown-body .markdown-alert .markdown-alert-title {\r\n display: flex;\r\n font-weight: var(--base-text-weight-medium, 500);\r\n align-items: center;\r\n line-height: 1;\r\n}\r\n\r\n.markdown-body .markdown-alert.markdown-alert-note {\r\n border-left-color: var(--borderColor-accent-emphasis);\r\n}\r\n\r\n.markdown-body .markdown-alert.markdown-alert-note .markdown-alert-title {\r\n color: var(--fgColor-accent);\r\n}\r\n\r\n.markdown-body .markdown-alert.markdown-alert-important {\r\n border-left-color: var(--borderColor-done-emphasis);\r\n}\r\n\r\n.markdown-body .markdown-alert.markdown-alert-important .markdown-alert-title {\r\n color: var(--fgColor-done);\r\n}\r\n\r\n.markdown-body .markdown-alert.markdown-alert-warning {\r\n border-left-color: var(--borderColor-attention-emphasis);\r\n}\r\n\r\n.markdown-body .markdown-alert.markdown-alert-warning .markdown-alert-title {\r\n color: var(--fgColor-attention);\r\n}\r\n\r\n.markdown-body .markdown-alert.markdown-alert-tip {\r\n border-left-color: var(--borderColor-success-emphasis);\r\n}\r\n\r\n.markdown-body .markdown-alert.markdown-alert-tip .markdown-alert-title {\r\n color: var(--fgColor-success);\r\n}\r\n\r\n.markdown-body .markdown-alert.markdown-alert-caution {\r\n border-left-color: var(--borderColor-danger-emphasis);\r\n}\r\n\r\n.markdown-body .markdown-alert.markdown-alert-caution .markdown-alert-title {\r\n color: var(--fgColor-danger);\r\n}\r\n\r\n.markdown-body>*:first-child>.heading-element:first-child {\r\n margin-top: 0 !important;\r\n}\r\n\r\n.markdown-body .highlight pre:has(+.zeroclipboard-container) {\r\n min-height: 52px;\r\n}\r\n\r\n.message-actions {\r\n position: absolute;\r\n top: 8px;\r\n right: 8px;\r\n opacity: 0;\r\n transition: opacity 0.2s ease;\r\n}\r\n\r\n.assistant-message:hover .message-actions {\r\n opacity: 1;\r\n}\r\n\r\n.copy-button {\r\n background: none;\r\n border: none;\r\n cursor: pointer;\r\n padding: 4px;\r\n border-radius: 4px;\r\n color: #6e6e80;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n.copy-button:hover {\r\n background-color: rgba(0, 0, 0, 0.05);\r\n color: #000;\r\n}\r\n\r\n.copy-button svg {\r\n width: 16px;\r\n height: 16px;\r\n}\r\n\r\n.file-list {\r\n display: flex;\r\n flex-wrap: wrap;\r\n gap: 12px;\r\n margin-top: 8px;\r\n}\r\n\r\n.file-item {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n width: 80px;\r\n cursor: pointer;\r\n padding: 8px;\r\n border-radius: 4px;\r\n transition: background-color 0.3s;\r\n margin: 5px 10px;\r\n}\r\n\r\n.file-item:hover {\r\n background-color: rgba(0, 0, 0, 0.04);\r\n}\r\n\r\n.file-icon {\r\n margin-bottom: 4px;\r\n}\r\n\r\n.file-name {\r\n font-size: 12px;\r\n text-align: center;\r\n width: 100%;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n color: #8B4513;\r\n}","import { Component, Prop, h, Element, Event, EventEmitter } from '@stencil/core';\r\nimport { marked } from 'marked';\r\nimport extendedTables from 'marked-extended-tables';\r\nimport { ChatMessage } from '../../interfaces/chat';\r\nimport { sendHttpRequest } from '../../utils/utils';\r\n\r\n@Component({\r\n tag: 'pcm-chat-message',\r\n styleUrl: 'pcm-chat-message.css',\r\n shadow: true,\r\n})\r\nexport class ChatMessageComponent {\r\n /**\r\n * 消息数据\r\n */\r\n @Prop() message: ChatMessage;\r\n\r\n /**\r\n * 消息变更事件\r\n */\r\n @Event() messageChange: EventEmitter<Partial<ChatMessage>>;\r\n\r\n // 使用 @Element 装饰器获取组件的 host 元素\r\n @Element() hostElement: HTMLElement;\r\n\r\n constructor() {\r\n // 配置 marked 选项\r\n marked.use(extendedTables);\r\n marked.setOptions({\r\n breaks: true,\r\n gfm: true\r\n });\r\n }\r\n\r\n // 复制消息内容到剪贴板\r\n private copyMessageContent() {\r\n if (this.message.answer) {\r\n navigator.clipboard.writeText(this.message.answer)\r\n .then(() => {\r\n // 可以添加复制成功的提示\r\n console.log('内容已复制到剪贴板');\r\n })\r\n .catch(err => {\r\n console.error('复制失败:', err);\r\n });\r\n }\r\n }\r\n\r\n // 渲染用户消息部分\r\n private renderUserMessage() {\r\n if (!this.message?.query?.trim()) return null;\r\n\r\n return (\r\n <div class=\"user-message-container\">\r\n <div class=\"message-bubble user-message\">\r\n {this.renderInputs()}\r\n <p>{this.message.query}</p>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n // 渲染助手消息部分\r\n private renderAssistantMessage() {\r\n if (!this.message.answer && !this.message.isStreaming) return null;\r\n\r\n // 只有在开始流式输出且还没有内容时才显示loading\r\n const showLoading = this.message.isStreaming && !this.message.answer;\r\n const htmlContent = this.message.answer ? marked(this.message.answer) : '';\r\n\r\n return (\r\n <div class=\"assistant-message-container\">\r\n <div class=\"message-bubble assistant-message\">\r\n <div\r\n class=\"markdown-content markdown-body\"\r\n innerHTML={showLoading ?\r\n `请稍等...` :\r\n htmlContent\r\n }\r\n ></div>\r\n </div>\r\n {!showLoading && this.message.answer && (\r\n <div class=\"message-actions\">\r\n <button class=\"copy-button\" onClick={() => this.copyMessageContent()} title=\"复制内容\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\r\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"></rect>\r\n <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"></path>\r\n </svg>\r\n </button>\r\n </div>\r\n )}\r\n </div>\r\n );\r\n }\r\n\r\n private getFileIcon(fileName: string): { icon: string, color: string } {\r\n const extension = fileName.split('.').pop()?.toLowerCase();\r\n\r\n switch (extension) {\r\n case 'pdf':\r\n return {\r\n icon: `<svg width=\"32\" height=\"32\" viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M19 3H9C7.89543 3 7 3.89543 7 5V27C7 28.1046 7.89543 29 9 29H23C24.1046 29 25 28.1046 25 27V9L19 3Z\" stroke=\"#1677FF\" stroke-width=\"2\"/>\r\n <path d=\"M19 3V9H25\" stroke=\"#1677FF\" stroke-width=\"2\"/>\r\n <text x=\"11\" y=\"20\" fill=\"#1677FF\" font-size=\"8\">PDF</text>\r\n </svg>`,\r\n color: '#1677FF'\r\n };\r\n case 'doc':\r\n case 'docx':\r\n return {\r\n icon: `<svg width=\"32\" height=\"32\" viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M19 3H9C7.89543 3 7 3.89543 7 5V27C7 28.1046 7.89543 29 9 29H23C24.1046 29 25 28.1046 25 27V9L19 3Z\" stroke=\"#1677FF\" stroke-width=\"2\"/>\r\n <path d=\"M19 3V9H25\" stroke=\"#1677FF\" stroke-width=\"2\"/>\r\n <text x=\"11\" y=\"20\" fill=\"#1677FF\" font-size=\"6\">DOC</text>\r\n </svg>`,\r\n color: '#1677FF'\r\n };\r\n case 'jpg':\r\n case 'jpeg':\r\n case 'png':\r\n case 'gif':\r\n return {\r\n icon: `<svg width=\"32\" height=\"32\" viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M19 3H9C7.89543 3 7 3.89543 7 5V27C7 28.1046 7.89543 29 9 29H23C24.1046 29 25 28.1046 25 27V9L19 3Z\" stroke=\"#52C41A\" stroke-width=\"2\"/>\r\n <path d=\"M19 3V9H25\" stroke=\"#52C41A\" stroke-width=\"2\"/>\r\n <path d=\"M12 14C13.1046 14 14 13.1046 14 12C14 10.8954 13.1046 10 12 10C10.8954 10 10 10.8954 10 12C10 13.1046 10.8954 14 12 14Z\" fill=\"#52C41A\"/>\r\n <path d=\"M10 21L13 18L15 20L19 16L22 21H10Z\" fill=\"#52C41A\"/>\r\n </svg>`,\r\n color: '#52C41A'\r\n };\r\n default:\r\n return {\r\n icon: `<svg width=\"32\" height=\"32\" viewBox=\"0 0 32 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M19 3H9C7.89543 3 7 3.89543 7 5V27C7 28.1046 7.89543 29 9 29H23C24.1046 29 25 28.1046 25 27V9L19 3Z\" stroke=\"#666666\" stroke-width=\"2\"/>\r\n <path d=\"M19 3V9H25\" stroke=\"#666666\" stroke-width=\"2\"/>\r\n </svg>`,\r\n color: '#666666'\r\n };\r\n }\r\n }\r\n\r\n private getFileName(fileUrl: string): string {\r\n const parts = fileUrl.split('/');\r\n return parts[parts.length - 1];\r\n }\r\n\r\n // 获取预览URL\r\n private async getCosPreviewUrl(cosKey: string): Promise<string | null> {\r\n try {\r\n const result = await sendHttpRequest<{ file_url: string }>({\r\n url: '/sdk/v1/files/presigned-url',\r\n method: 'GET',\r\n headers: {\r\n 'authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuaWNrbmFtZSI6Ilx1NTMzYlx1ODA1OFx1NzMyYjgxMDMiLCJ1aWQiOjcsImNoYXRfdXNlciI6bnVsbCwiYm90X2lkIjoiMSIsInNlY3JldF9pZCI6Mzg5OTk1ODAyNTUxOTEwNDAsImV4cCI6MTc0NTA0OTAxM30.56OoTrp16avgl48YfWBKOHywAKHJ5qPGypqRCGCyVt0'\r\n },\r\n params: {\r\n cos_key: cosKey\r\n }\r\n });\r\n \r\n console.log(result);\r\n \r\n if (result.success && result.data?.file_url) {\r\n const baseUrl = result.data.file_url;\r\n return `${baseUrl}${baseUrl.includes('?') ? '&' : '?'}ci-process=doc-preview©able=1&dstType=html`;\r\n }\r\n return null;\r\n } catch (error) {\r\n console.error('获取预览URL失败:', error);\r\n return null;\r\n }\r\n }\r\n\r\n // 处理文件点击\r\n private async handleFileClick(fileUrl: string) {\r\n const previewUrl = await this.getCosPreviewUrl(fileUrl);\r\n if (previewUrl) {\r\n window.open(previewUrl, '_blank');\r\n } else {\r\n console.error('无法获取预览URL');\r\n }\r\n }\r\n\r\n // 修改渲染文件项的部分\r\n private renderFileItem(fileName: string, fileUrl: string, index: number) {\r\n const { icon } = this.getFileIcon(fileName);\r\n return (\r\n <div key={index} class=\"input-view\">\r\n <div class=\"input-label\">附件:</div>\r\n <div class=\"file-item\" onClick={() => this.handleFileClick(fileUrl)}>\r\n <div class=\"file-icon\" innerHTML={icon}></div>\r\n <div class=\"file-name\">{fileName}</div>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n // 修改渲染输入数据的方法\r\n private renderInputs() {\r\n if (!this.message.inputs) return null;\r\n\r\n return (\r\n <div>\r\n {Object.keys(this.message.inputs).map((key, index) => {\r\n const value = this.message.inputs[key];\r\n if (value && !key.startsWith('hide_') && key !== 'answer') {\r\n if (key === 'file_url') {\r\n const fileName = this.getFileName(value);\r\n return this.renderFileItem(fileName, value, index);\r\n } else if (key === 'file_urls') {\r\n const fileList = Array.isArray(value) ? value : value.split(',');\r\n return (\r\n <div key={index} class=\"file-list\">\r\n {fileList.map((fileUrl, fileIndex) => {\r\n const fileName = this.getFileName(fileUrl);\r\n return this.renderFileItem(fileName, fileUrl, fileIndex);\r\n })}\r\n </div>\r\n );\r\n } else if (key === 'job_info' || key === 'rule') {\r\n return (\r\n <div key={index} class=\"input-view\">\r\n <div class=\"input-label\">{key === 'job_info' ? '职位信息' : '评估规则'}</div>\r\n <div class=\"input-value\">{value}</div>\r\n </div>\r\n );\r\n } else {\r\n return <div key={index} class=\"input-metadata\">{key}: {`${value}`}</div>;\r\n }\r\n }\r\n return null;\r\n })}\r\n </div>\r\n );\r\n }\r\n\r\n render() {\r\n return (\r\n <div class=\"message-round\">\r\n {this.renderUserMessage()}\r\n {this.renderAssistantMessage()}\r\n </div>\r\n );\r\n }\r\n} ",":host {\r\n display: block;\r\n}\r\n\r\n.modal-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background-color: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n overflow-y: auto;\r\n padding: 20px;\r\n z-index: 1000;\r\n}\r\n\r\n/* 全屏模式下取消 padding */\r\n.fullscreen-overlay {\r\n padding: 0;\r\n}\r\n\r\n.modal-container {\r\n background: white;\r\n border-radius: 8px;\r\n width: 100%;\r\n max-width: 800px;\r\n display: flex;\r\n flex-direction: column;\r\n position: relative;\r\n margin: auto;\r\n}\r\n\r\n/* 全屏模式样式 */\r\n.modal-container.fullscreen {\r\n width: 100vw;\r\n max-width: none;\r\n height: 100%;\r\n border-radius: 0;\r\n margin: 0;\r\n display: flex;\r\n flex-direction: column;\r\n height: 100vh;\r\n max-height: 100vh;\r\n}\r\n\r\n/* 确保内容区域也使用 flex 布局并占满剩余空间 */\r\n.modal-container.fullscreen > div:not(.modal-header):not(.initial-upload) {\r\n display: flex;\r\n flex-direction: column;\r\n flex: 1;\r\n overflow: hidden; /* 防止内容溢出 */\r\n height: 100%;\r\n}\r\n\r\n/* PC端布局 */\r\n.pc-layout {\r\n width: 80%;\r\n max-width: 800px;\r\n /* height: 80vh; */\r\n /* max-height: 700px; */\r\n min-width: 320px;\r\n min-height: 400px;\r\n}\r\n\r\n/* 移动端布局 */\r\n.mobile-layout {\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 0;\r\n}\r\n\r\n/* 响应式布局 */\r\n@media screen and (max-width: 768px) {\r\n .pc-layout {\r\n width: 95%;\r\n /* height: 90vh; */\r\n }\r\n\r\n .modal-overlay {\r\n padding: 0;\r\n }\r\n\r\n .modal-container.fullscreen {\r\n /* 支持 iOS Safari */\r\n height: -webkit-fill-available;\r\n max-height: -webkit-fill-available;\r\n /* 确保内容不会被顶部状态栏和底部工具栏遮挡 */\r\n padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom);\r\n }\r\n\r\n\r\n .modal-container.mobile-layout {\r\n width: 100%;\r\n height: 100vh;\r\n max-height: 100vh;\r\n min-height: 100vh;\r\n border-radius: 0;\r\n margin: 0;\r\n display: flex;\r\n flex-direction: column;\r\n }\r\n\r\n}\r\n.video-preview.placeholder {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n background: #EAEAEA;\r\n}\r\n\r\n.placeholder-status {\r\n color: #00000066;\r\n}\r\n\r\n.waiting-message p {\r\n margin: 0;\r\n font-size: 16px;\r\n color: white;\r\n font-weight: 500;\r\n}\r\n\r\n.recording-container {\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n}\r\n\r\n\r\n\r\n.video-area {\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n}\r\n\r\n.stop-recording-button {\r\n width: 100%;\r\n height: 100%;\r\n font-size: 16px;\r\n background: #f44336;\r\n border-radius: 6px;\r\n color: white;\r\n border: none;\r\n cursor: pointer;\r\n}\r\n\r\n.stop-recording-button:hover {\r\n background: #d32f2f;\r\n}\r\n\r\n.play-audio-container {\r\n width: 100%;\r\n height: 100%;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n}\r\n\r\n.modal-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 4px 16px;\r\n height: 50px;\r\n border-bottom: 1px solid #e8e8e8;\r\n flex-shrink: 0; /* 防止头部被压缩 */\r\n}\r\n\r\n.header-left {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n}\r\n\r\n.header-icon {\r\n width: 24px;\r\n height: 24px;\r\n}\r\n\r\n.close-button {\r\n background: transparent;\r\n border: none;\r\n cursor: pointer;\r\n padding: 8px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 4px;\r\n}\r\n\r\n.close-button:hover {\r\n background-color: rgba(0, 0, 0, 0.04);\r\n}\r\n\r\n.close-button span {\r\n font-size: 24px;\r\n line-height: 1;\r\n color: #999;\r\n}\r\n\r\n.close-button:hover span {\r\n color: #666;\r\n}\r\n\r\n.chat-history {\r\n position: relative;\r\n flex: 1;\r\n overflow-y: auto;\r\n padding: 20px;\r\n scroll-behavior: smooth;\r\n min-height: 200px;\r\n background: url(https://pcm-resource-1312611446.cos.ap-guangzhou.myqcloud.com/web/sdk/chat_bg.png);\r\n background-size: 100%;\r\n}\r\n\r\n/* 添加全屏模式下的样式 */\r\n.fullscreen .chat-history {\r\n height: auto;\r\n flex: 1 1 auto;\r\n}\r\n\r\n\r\n.message-input {\r\n padding: 16px;\r\n border-top: 1px solid #eee;\r\n display: flex;\r\n gap: 8px;\r\n align-items: center;\r\n}\r\n\r\n.message-input input {\r\n flex: 1;\r\n padding: 8px 12px;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n outline: none;\r\n transition: border-color 0.2s ease;\r\n}\r\n\r\n.message-input input:focus {\r\n border-color: #bbb;\r\n}\r\n\r\n/* 消息样式 */\r\n.message {\r\n margin-bottom: 16px;\r\n opacity: 1;\r\n transition: opacity 0.3s ease;\r\n}\r\n\r\n.message-content {\r\n max-width: 70%;\r\n padding: 8px 12px;\r\n border-radius: 8px;\r\n word-break: break-word;\r\n}\r\n\r\n.message-content p {\r\n margin: 0;\r\n word-break: break-word;\r\n}\r\n\r\n.user-message {\r\n display: flex;\r\n justify-content: flex-end;\r\n}\r\n\r\n.agent-message {\r\n display: flex;\r\n justify-content: flex-start;\r\n}\r\n\r\n.user-message .message-content {\r\n background-color: #007bff;\r\n color: white;\r\n}\r\n\r\n.agent-message .message-content {\r\n background-color: #f1f1f1;\r\n}\r\n\r\n.message-time {\r\n font-size: 12px;\r\n color: #999;\r\n margin-top: 4px;\r\n display: block;\r\n}\r\n\r\n.send-button {\r\n background-color: #1890ff;\r\n color: white;\r\n border: none;\r\n border-radius: 4px;\r\n padding: 8px 16px;\r\n cursor: pointer;\r\n font-weight: 500;\r\n}\r\n\r\n.send-button:disabled {\r\n background-color: #ccc;\r\n cursor: not-allowed;\r\n}\r\n\r\n.empty-state {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n height: 100%;\r\n color: #999;\r\n text-align: center;\r\n}\r\n\r\n.loading-container {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n display: flex;\r\n flex-direction: column;\r\n justify-content: center;\r\n align-items: center;\r\n background-color: rgba(255, 255, 255, 0.98);\r\n z-index: 1;\r\n opacity: 1;\r\n transition: opacity 0.3s ease;\r\n}\r\n\r\n.loading-container p {\r\n margin-top: 16px;\r\n color: #666;\r\n font-size: 14px;\r\n}\r\n\r\n.loading-spinner {\r\n width: 40px;\r\n height: 40px;\r\n border: 3px solid #f3f3f3;\r\n border-top: 3px solid #1890ff;\r\n border-radius: 50%;\r\n animation: spin 1s linear infinite;\r\n}\r\n\r\n@keyframes spin {\r\n 0% {\r\n transform: rotate(0deg);\r\n }\r\n\r\n 100% {\r\n transform: rotate(360deg);\r\n }\r\n}\r\n\r\n/* 修改 messages-wrapper 的样式 */\r\n.messages-wrapper {\r\n width: 100%;\r\n min-height: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n /* 当内容少时,将内容放在底部 */\r\n justify-content: flex-end;\r\n}\r\n\r\n/* 当有很多消息时,取消固定在底部 */\r\n.messages-wrapper.has-overflow {\r\n justify-content: flex-start;\r\n}\r\n\r\n.suggested-questions {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n padding: 16px;\r\n}\r\n\r\n.suggested-question {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 8px 12px;\r\n background-color: #f3f4f6;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n font-size: 14px;\r\n color: #374151;\r\n transition: background-color 0.2s;\r\n}\r\n\r\n.suggested-question:hover {\r\n background-color: #e5e7eb;\r\n}\r\n\r\n.arrow-right {\r\n margin-left: 8px;\r\n}\r\n\r\n.loading-suggestions {\r\n display: flex;\r\n justify-content: center;\r\n padding: 16px;\r\n}\r\n\r\n.loading-spinner-small {\r\n width: 20px;\r\n height: 20px;\r\n border: 2px solid #e5e7eb;\r\n border-top-color: #6b7280;\r\n border-radius: 50%;\r\n animation: spin 1s linear infinite;\r\n}\r\n\r\n/* 添加上传按钮样式 */\r\n.upload-button {\r\n background: transparent;\r\n border: none;\r\n cursor: pointer;\r\n padding: 8px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n color: #666;\r\n border-radius: 4px;\r\n transition: background-color 0.2s;\r\n}\r\n\r\n.upload-button:hover {\r\n background-color: rgba(0, 0, 0, 0.04);\r\n}\r\n\r\n.upload-button svg {\r\n width: 20px;\r\n height: 20px;\r\n}\r\n\r\n/* 隐藏原生文件输入框 */\r\n.file-input {\r\n display: none;\r\n}\r\n\r\n/* 添加文件名显示区域样式 */\r\n.selected-file {\r\n font-size: 12px;\r\n color: #666;\r\n margin-left: 8px;\r\n max-width: 150px;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.input-wrapper {\r\n flex: 1;\r\n display: flex;\r\n align-items: center;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n padding: 0 4px;\r\n background: white;\r\n}\r\n\r\n.input-wrapper input {\r\n border: none;\r\n flex: 1;\r\n padding: 8px;\r\n outline: none;\r\n}\r\n\r\n.input-wrapper:focus-within {\r\n border-color: #bbb;\r\n}\r\n\r\n/* 文件预览区域样式 */\r\n.file-preview {\r\n padding: 8px 16px;\r\n border-top: 1px solid #eee;\r\n background-color: #f9f9f9;\r\n}\r\n\r\n.file-info {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 8px;\r\n background: white;\r\n border: 1px solid #e8e8e8;\r\n border-radius: 4px;\r\n}\r\n\r\n.file-name {\r\n font-size: 13px;\r\n color: #333;\r\n margin-right: 8px;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n max-width: calc(100% - 30px);\r\n}\r\n\r\n.remove-file {\r\n background: transparent;\r\n border: none;\r\n color: #999;\r\n cursor: pointer;\r\n padding: 4px 8px;\r\n font-size: 16px;\r\n line-height: 1;\r\n border-radius: 4px;\r\n transition: all 0.2s;\r\n}\r\n\r\n.remove-file:hover {\r\n background-color: #f0f0f0;\r\n color: #666;\r\n}\r\n\r\n.initial-upload {\r\n padding: 1rem 1rem;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n height: 100%;\r\n}\r\n\r\n.upload-section {\r\n max-width: 600px;\r\n width: 100%;\r\n text-align: center;\r\n}\r\n\r\n.upload-area {\r\n border: 2px dashed #ddd;\r\n border-radius: 8px;\r\n padding: 1rem;\r\n cursor: pointer;\r\n transition: all 0.3s ease;\r\n}\r\n\r\n.upload-area:hover {\r\n border-color: #1890ff;\r\n background-color: rgba(24, 144, 255, 0.05);\r\n}\r\n\r\n.upload-placeholder {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n color: #666;\r\n}\r\n\r\n.upload-hint {\r\n font-size: 0.8rem;\r\n color: #999;\r\n margin-top: 0.5rem;\r\n}\r\n\r\n.function-select {\r\n margin-top: 2rem;\r\n}\r\n\r\n.function-options {\r\n display: flex;\r\n flex-wrap: wrap;\r\n gap: 1rem;\r\n justify-content: center;\r\n margin-top: 1rem;\r\n}\r\n\r\n.function-button {\r\n padding: 0.5rem 1rem;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n background: white;\r\n cursor: pointer;\r\n transition: all 0.3s ease;\r\n}\r\n\r\n.function-button:hover {\r\n border-color: #1890ff;\r\n color: #1890ff;\r\n}\r\n\r\n.function-button.selected {\r\n background: #1890ff;\r\n color: white;\r\n border-color: #1890ff;\r\n}\r\n\r\n.submit-button {\r\n margin-top: 1rem;\r\n padding: 0.8rem 2rem;\r\n background: #1890ff;\r\n color: white;\r\n border: none;\r\n border-radius: 4px;\r\n font-size: 1rem;\r\n cursor: pointer;\r\n transition: all 0.3s ease;\r\n width: 95%;\r\n}\r\n\r\n.submit-button:disabled {\r\n background: #ccc;\r\n cursor: not-allowed;\r\n}\r\n\r\n.submit-button:hover:not(:disabled) {\r\n background: #40a9ff;\r\n}\r\n\r\n.category-select,\r\n.dimension-select {\r\n margin: 30px 0;\r\n}\r\n\r\n.category-options,\r\n.dimension-options {\r\n display: flex;\r\n flex-wrap: wrap;\r\n gap: 10px;\r\n margin-top: 10px;\r\n}\r\n\r\n.category-button,\r\n.dimension-button {\r\n padding: 12px 16px;\r\n border: 1px solid #E5E5E5;\r\n border-radius: 6px;\r\n background: white;\r\n cursor: pointer;\r\n transition: all 0.3s;\r\n}\r\n\r\n.category-button:hover,\r\n.dimension-button:hover {\r\n background: #f5f5f5;\r\n}\r\n\r\n.category-button.selected {\r\n background-image: linear-gradient(111deg, #4A9FFF 0%, #1058FF 100%);\r\n color: white;\r\n}\r\n\r\n.dimension-button.selected {\r\n background-image: linear-gradient(111deg, #4A9FFF 0%, #1058FF 100%);\r\n color: white;\r\n}\r\n\r\n.recording-section {\r\n border-top: 1px solid #eee;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n padding: 20px;\r\n border-radius: 14px 14px 0 0;\r\n flex: 0 0 auto;\r\n}\r\n\r\n.recording-section .video-preview {\r\n width: 100%;\r\n height: 200px;\r\n max-width: 400px;\r\n position: relative;\r\n margin-bottom: 10px;\r\n border: 1px solid #ddd;\r\n border-radius: 12px;\r\n overflow: hidden;\r\n}\r\n\r\n.recording-section video {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n}\r\n\r\n/* 修改 recording-status 样式 */\r\n.recording-status {\r\n position: absolute;\r\n top: 10px;\r\n left: 10px;\r\n background-color: rgba(0, 0, 0, 0.6);\r\n color: white;\r\n padding: 4px 8px;\r\n border-radius: 4px;\r\n display: flex;\r\n align-items: center;\r\n gap: 5px;\r\n font-size: 0.8rem;\r\n z-index: 2;\r\n}\r\n\r\n.recording-status .recording-dot {\r\n display: inline-block;\r\n width: 10px;\r\n height: 10px;\r\n background-color: red;\r\n border-radius: 50%;\r\n margin-right: 5px;\r\n animation: blink 1s infinite;\r\n}\r\n\r\n.recording-status.warning {\r\n color: #ff4d4f;\r\n animation: blink 1s infinite;\r\n}\r\n\r\n@keyframes blink {\r\n 0% {\r\n opacity: 1;\r\n }\r\n\r\n 50% {\r\n opacity: 0.5;\r\n }\r\n\r\n 100% {\r\n opacity: 1;\r\n }\r\n}\r\n\r\n.recording-section .stop-recording-button {\r\n background-color: #f44336;\r\n color: white;\r\n border: none;\r\n cursor: pointer;\r\n font-weight: bold;\r\n}\r\n\r\n.recording-section .stop-recording-button:hover {\r\n background-color: #d32f2f;\r\n}\r\n\r\n.fullscreen {\r\n width: 100vw;\r\n border-radius: 0;\r\n height: 100vh;\r\n display: flex;\r\n flex-direction: column;\r\n overflow-y: auto;\r\n}\r\n\r\n.recording-controls {\r\n margin-top: 10px;\r\n height: 53px;\r\n width: 100%;\r\n max-width: 400px;\r\n display: flex;\r\n justify-content: center;\r\n}\r\n\r\n.recording-controls .waiting-message {\r\n text-align: center;\r\n color: white;\r\n font-size: 1rem;\r\n background-image: linear-gradient(100deg, #4A9FFF 0%, #1058FF 100%);\r\n border-radius: 6px;\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\r\n width: 95%;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n cursor: pointer;\r\n}\r\n\r\n.recording-controls .waiting-message.loading {\r\n background: #faad14;\r\n}\r\n\r\n.recording-controls .waiting-message p {\r\n margin: 0;\r\n font-size: 16px;\r\n color: white;\r\n font-weight: 500;\r\n}\r\n\r\n.recording-controls .stop-recording-button {\r\n background-color: #dc3545;\r\n color: white;\r\n border: none;\r\n cursor: pointer;\r\n font-size: 1rem;\r\n}\r\n\r\n.recording-controls .stop-recording-button:hover {\r\n background-color: #c82333;\r\n}\r\n\r\n/* 添加禁用状态的样式 */\r\n.recording-controls .stop-recording-button.disabled {\r\n background: #ccc;\r\n cursor: not-allowed;\r\n}\r\n\r\n.recording-controls .stop-recording-button.disabled:hover {\r\n background: #ccc;\r\n}\r\n\r\n/* 添加进度条和数字进度的样式 */\r\n.progress-container {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n width: 100%;\r\n max-width: 400px;\r\n margin-top: 10px;\r\n padding: 0 5px;\r\n}\r\n\r\n.progress-bar-container {\r\n height: 4px;\r\n background-color: #E5E5E5;\r\n border-radius: 2px;\r\n overflow: hidden;\r\n margin-right: 10px;\r\n width: 75px;\r\n}\r\n\r\n.progress-bar {\r\n height: 100%;\r\n background-image: linear-gradient(111deg, #4A9FFF 0%, #1058FF 100%);\r\n border-radius: 2px;\r\n transition: width 0.3s ease;\r\n}\r\n\r\n.progress-text {\r\n font-size: 14px;\r\n color: #666;\r\n white-space: nowrap;\r\n}","import { Component, Prop, h, State, Event, EventEmitter, Element, Watch } from '@stencil/core';\r\nimport { convertWorkflowStreamNodeToMessageRound, UserInputMessageType, sendSSERequest, sendHttpRequest, uploadFileToBackend, FileUploadResponse, API_DOMAIN } from '../../utils/utils';\r\nimport { ChatMessage } from '../../interfaces/chat';\r\n\r\n@Component({\r\n tag: 'pcm-hr-chat-modal',\r\n styleUrl: 'pcm-hr-chat-modal.css',\r\n shadow: true,\r\n})\r\nexport class ChatHRModal {\r\n /**\r\n * 模态框标题\r\n */\r\n @Prop() modalTitle: string = '在线客服';\r\n\r\n /**\r\n * SDK鉴权密钥\r\n */\r\n @Prop({ attribute: 'token' }) token: string = '';\r\n\r\n /**\r\n * 是否显示聊天模态框\r\n */\r\n @Prop({ mutable: true }) isOpen: boolean = false;\r\n\r\n /**\r\n * 聊天消息历史\r\n */\r\n @State() messages: ChatMessage[] = [];\r\n\r\n\r\n /**\r\n * 当点击模态框关闭时触发\r\n */\r\n @Event() modalClosed: EventEmitter<void>;\r\n\r\n /**\r\n * 应用图标URL\r\n */\r\n @Prop() icon?: string;\r\n\r\n /**\r\n * 聊天框的页面层级\r\n */\r\n @Prop() zIndex?: number = 1000;\r\n\r\n /**\r\n * 是否展示顶部标题栏\r\n */\r\n @Prop() isShowHeader: boolean = true;\r\n\r\n /**\r\n * 是否展示右上角的关闭按钮\r\n */\r\n @Prop() isNeedClose: boolean = true;\r\n\r\n\r\n /**\r\n * 会话ID,传入继续对话,否则创建新会话\r\n */\r\n @Prop({ mutable: true }) conversationId?: string;\r\n\r\n /**\r\n * 当前助手回复的消息\r\n */\r\n @State() currentAssistantMessage: string = '';\r\n\r\n /**\r\n * 是否正在加载回复\r\n */\r\n @State() isLoading: boolean = false;\r\n\r\n /**\r\n * 当前正在流式输出的消息\r\n */\r\n @State() currentStreamingMessage: ChatMessage | null = null;\r\n\r\n // 添加新的状态控制\r\n @State() shouldAutoScroll: boolean = true;\r\n\r\n @State() isLoadingHistory: boolean = false;\r\n\r\n // 使用 @Element 装饰器获取组件的 host 元素\r\n @Element() hostElement: HTMLElement;\r\n\r\n // 添加新的 Event\r\n @Event() streamComplete: EventEmitter<{\r\n conversation_id: string;\r\n event: string;\r\n message_id: string;\r\n id: string;\r\n }>;\r\n\r\n @State() selectedFile: File | null = null;\r\n @State() isUploading: boolean = false;\r\n @State() uploadedFileInfo: FileUploadResponse[] = [];\r\n\r\n /**\r\n * 默认查询文本\r\n */\r\n @Prop() defaultQuery: string = '';\r\n\r\n // 添加新的状态\r\n @State() showInitialUpload: boolean = true;\r\n @State() selectedJobCategory: string = '';\r\n @State() jobCategories: string[] = [\r\n '人力资源学生(实习)',\r\n '人力资源专员',\r\n '人力资源主管',\r\n '人力资源经理',\r\n '人力资源总监'\r\n ];\r\n\r\n @State() dimensions: string[] = [\r\n '人力资源规划',\r\n '招聘与配置',\r\n '员工关系',\r\n '培训与开发',\r\n '薪酬与绩效',\r\n '组织与人才发展'\r\n ];\r\n\r\n @State() selectedDimensions: string[] = [];\r\n\r\n // 添加视频录制相关状态\r\n @State() isRecording: boolean = false;\r\n @State() recordingStream: MediaStream | null = null;\r\n @State() recordedBlob: Blob | null = null;\r\n @State() mediaRecorder: MediaRecorder | null = null;\r\n @State() recordingTimeLeft: number = 0;\r\n @State() showRecordingUI: boolean = false;\r\n @State() recordingTimer: any = null;\r\n @State() recordingStartTime: number = 0;\r\n @State() recordingMaxTime: number = 120; // 最大录制时间(秒)\r\n @State() waitingToRecord: boolean = false;\r\n @State() waitingTimer: any = null;\r\n @State() waitingTimeLeft: number = 10; // 等待时间(秒)\r\n\r\n // 添加一个新的私有属性来存储视频元素的引用\r\n private videoRef: HTMLVideoElement | null = null;\r\n\r\n /**\r\n * 总题目数量\r\n */\r\n @Prop() totalQuestions: number = 2;\r\n\r\n /**\r\n * 当前题目序号\r\n */\r\n @State() currentQuestionNumber: number = 0;\r\n\r\n /**\r\n * 面试是否已完成\r\n */\r\n @State() isInterviewComplete: boolean = false;\r\n\r\n /**\r\n * 当面试完成时触发\r\n */\r\n @Event() interviewComplete: EventEmitter<{\r\n conversation_id: string;\r\n total_questions: number;\r\n }>;\r\n\r\n private readonly SCROLL_THRESHOLD = 30;\r\n\r\n /**\r\n * 视频录制最大时长(秒)\r\n */\r\n @Prop() maxRecordingTime: number = 120;\r\n\r\n /**\r\n * 录制倒计时提醒时间(秒)\r\n * 当剩余时间小于此值时,显示倒计时警告\r\n */\r\n @Prop() countdownWarningTime: number = 30;\r\n\r\n @State() showCountdownWarning: boolean = false;\r\n\r\n /**\r\n * 接收报告的邮箱地址\r\n */\r\n @Prop() toEmail: string = '';\r\n\r\n /**\r\n * 是否以全屏模式打开,移动端建议设置为true\r\n */\r\n @Prop() fullscreen: boolean = false;\r\n\r\n // 添加新的状态来跟踪视频上传\r\n @State() isUploadingVideo: boolean = false;\r\n\r\n /**\r\n * 是否需要上传简历\r\n */\r\n @Prop() requireResume: boolean = false;\r\n\r\n // 添加新的状态和属性\r\n @State() isPlayingAudio: boolean = false;\r\n @State() audioUrl: string | null = null;\r\n private audioElement: HTMLAudioElement | null = null;\r\n\r\n /**\r\n * 录制错误事件\r\n */\r\n @Event() recordingError: EventEmitter<{\r\n type: string;\r\n message: string;\r\n details?: any;\r\n }>;\r\n\r\n /**\r\n * 录制状态变化事件\r\n */\r\n @Event() recordingStatusChange: EventEmitter<{\r\n status: 'started' | 'stopped' | 'paused' | 'resumed' | 'failed';\r\n details?: any;\r\n }>;\r\n\r\n /**\r\n * 是否播放语音问题\r\n */\r\n @Prop() enableVoice: boolean = true;\r\n\r\n /**\r\n * 是否显示题干内容\r\n */\r\n @Prop() displayContentStatus: boolean = true;\r\n\r\n /**\r\n * 用户ID\r\n */\r\n @Prop() userId: string = '';\r\n\r\n private handleClose = () => {\r\n this.stopRecording();\r\n this.modalClosed.emit();\r\n };\r\n\r\n private handleFileChange = async (event: Event) => {\r\n const input = event.target as HTMLInputElement;\r\n if (input.files && input.files.length > 0) {\r\n this.selectedFile = input.files[0];\r\n }\r\n };\r\n\r\n private async uploadFile() {\r\n if (!this.selectedFile || this.uploadedFileInfo.length > 0) return;\r\n \r\n this.isUploading = true;\r\n \r\n try {\r\n const result = await uploadFileToBackend(this.selectedFile, {\r\n 'authorization': 'Bearer ' + this.token\r\n });\r\n \r\n if (result) {\r\n this.uploadedFileInfo = [{\r\n cos_key: result.cos_key,\r\n file_name: result.file_name,\r\n file_size: result.file_size,\r\n presigned_url: result.presigned_url,\r\n ext: result.ext\r\n }];\r\n }\r\n } catch (error) {\r\n console.error('文件上传错误:', error);\r\n this.clearSelectedFile();\r\n alert(error instanceof Error ? error.message : '文件上传失败,请重试');\r\n } finally {\r\n this.isUploading = false;\r\n }\r\n }\r\n\r\n private handleUploadClick = () => {\r\n const fileInput = this.hostElement.shadowRoot?.querySelector('.file-input') as HTMLInputElement;\r\n fileInput?.click();\r\n };\r\n\r\n private clearSelectedFile = () => {\r\n this.selectedFile = null;\r\n this.uploadedFileInfo = [];\r\n const fileInput = this.hostElement.shadowRoot?.querySelector('.file-input') as HTMLInputElement;\r\n if (fileInput) {\r\n fileInput.value = '';\r\n }\r\n };\r\n\r\n private async sendMessageToAPI(message: string) {\r\n this.isLoading = true;\r\n let answer = '';\r\n let llmText = ''; // 添加变量存储 LLMText\r\n\r\n const now = new Date();\r\n const time = `${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`;\r\n\r\n // 如果消息为空但有文件,使用默认文本\r\n const queryText = message.trim() || (this.uploadedFileInfo.length > 0 ? '请分析这个文件' : '');\r\n\r\n // 获取上一条AI消息的回答内容\r\n const lastAIMessage = this.messages.length > 0 ? this.messages[this.messages.length - 1] : null;\r\n\r\n // 保存AI提问和用户回答\r\n if (lastAIMessage && this.conversationId && message !== \"下一题\") {\r\n this.saveAnswer(\r\n this.conversationId,\r\n lastAIMessage.answer, // AI的提问作为question\r\n queryText // 用户的输入作为answer\r\n );\r\n }\r\n\r\n // 检查是否是最后一题的\"下一题\"请求\r\n const isLastQuestion = (this.currentQuestionNumber >= this.totalQuestions) && message === \"下一题\";\r\n\r\n // 创建新的消息对象\r\n const newMessage: ChatMessage = {\r\n id: `temp-${Date.now()}`, // 消息唯一标识\r\n time: time, // 消息时间\r\n query: queryText, // 用户输入的消息内容\r\n answer: '',\r\n isStreaming: true, // 是否正在流式输出\r\n conversation_id: this.conversationId, // 会话ID\r\n inputs: {}, // 输入参数\r\n status: \"normal\", // 消息状态\r\n error: null // 错误信息\r\n };\r\n\r\n // 设置当前流式消息\r\n this.currentStreamingMessage = newMessage;\r\n\r\n this.shouldAutoScroll = true;\r\n // 滚动到底部\r\n this.scrollToBottom();\r\n\r\n // 如果是最后一题,直接显示结束消息并完成面试\r\n if (isLastQuestion) {\r\n this.messages = [...this.messages, newMessage];\r\n this.currentStreamingMessage = null;\r\n this.isLoading = false;\r\n this.isInterviewComplete = true;\r\n await this.completeInterview();\r\n this.interviewComplete.emit({\r\n conversation_id: this.conversationId,\r\n total_questions: this.totalQuestions\r\n });\r\n return;\r\n }\r\n\r\n // 准备请求数据\r\n const requestData: any = {\r\n response_mode: 'streaming',\r\n conversation_id: this.conversationId,\r\n query: queryText,\r\n user: this.userId, // 使用传入的 userId\r\n bot_id: \"3022316191018880\"\r\n };\r\n requestData.inputs = {\r\n job_info: this.selectedJobCategory,\r\n dimensional_info: this.selectedDimensions.join(','),\r\n email: this.toEmail,\r\n display_content_status: this.displayContentStatus ? \"1\" : \"0\"\r\n };\r\n // 如果有上传的文件,添加到inputs参数\r\n if (this.uploadedFileInfo.length > 0) {\r\n const fileUrls = this.uploadedFileInfo.map(fileInfo => fileInfo.cos_key).join(',');\r\n requestData.inputs.file_urls = fileUrls;\r\n }\r\n\r\n\r\n await sendSSERequest({\r\n url: '/sdk/v1/chat/chat-messages',\r\n method: 'POST',\r\n headers: {\r\n 'authorization': 'Bearer ' + this.token\r\n },\r\n data: requestData,\r\n onMessage: (data) => {\r\n console.log('收到Stream数据:', data);\r\n\r\n if (data.conversation_id && !this.conversationId) {\r\n this.conversationId = data.conversation_id;\r\n this.updateUrlWithConversationId(data.conversation_id);\r\n }\r\n\r\n // 检查是否有 node_finished 事件和 LLMText\r\n if (data.event === 'node_finished' && data.data.inputs && data.data.inputs.LLMText) {\r\n llmText = data.data.inputs.LLMText;\r\n console.log('获取到 LLMText:', llmText);\r\n }\r\n\r\n if (data.event === 'message') {\r\n const inputMessage: UserInputMessageType = { message: message };\r\n convertWorkflowStreamNodeToMessageRound('message', inputMessage, data);\r\n\r\n if (data.event === 'agent_message' || data.event === 'message') {\r\n if (data.answer) {\r\n answer += data.answer;\r\n const updatedMessage: ChatMessage = {\r\n ...this.currentStreamingMessage,\r\n answer,\r\n isStreaming: true\r\n };\r\n this.currentStreamingMessage = updatedMessage;\r\n this.scrollToBottom();\r\n }\r\n }\r\n }\r\n if (data.event === \"message_end\") {\r\n this.streamComplete.emit({\r\n conversation_id: data.conversation_id || '',\r\n event: data.event,\r\n message_id: data.message_id,\r\n id: data.id,\r\n });\r\n }\r\n },\r\n onError: (error) => {\r\n console.error('发生错误:', error);\r\n alert(error instanceof Error ? error.message : '消息发送失败,请稍后再试');\r\n this.messages = [...this.messages, {\r\n ...newMessage,\r\n answer: '抱歉,发生了错误,请稍后再试。',\r\n error: error,\r\n isStreaming: false\r\n }];\r\n this.currentStreamingMessage = null;\r\n this.isLoading = false;\r\n },\r\n onComplete: async () => {\r\n console.log('请求完成');\r\n this.isLoading = false;\r\n\r\n // 获取最新的AI回复内容\r\n const latestAIMessage = this.currentStreamingMessage;\r\n\r\n // 更新消息列表\r\n this.messages = [...this.messages, this.currentStreamingMessage];\r\n this.currentStreamingMessage = null;\r\n\r\n // 如果是初始消息或\"下一题\"消息,增加题目计数\r\n if (message === \"下一题\" || this.currentQuestionNumber === 0) {\r\n this.currentQuestionNumber++;\r\n }\r\n console.log(this.currentQuestionNumber);\r\n console.log(message);\r\n\r\n if (latestAIMessage && latestAIMessage.answer) {\r\n // 优先使用 LLMText,如果没有则使用 answer\r\n const textForSynthesis = llmText || latestAIMessage.answer;\r\n \r\n if (textForSynthesis) {\r\n // 合成语音\r\n const audioUrl = await this.synthesizeAudio(textForSynthesis);\r\n\r\n if (this.enableVoice) {\r\n // 自动播放语音\r\n await this.playAudio(audioUrl);\r\n // 自动播放模式下,播放完成后立即开始等待录制\r\n this.startWaitingToRecord();\r\n } else {\r\n // 只保存音频URL,不自动播放\r\n this.audioUrl = audioUrl;\r\n // 非自动播放模式下,不立即开始等待录制\r\n }\r\n }\r\n }\r\n }\r\n });\r\n }\r\n\r\n // 修改保存答案的方法\r\n private async saveAnswer(conversationId: string, question: string, answer: string) {\r\n try {\r\n await sendHttpRequest({\r\n url: '/sdk/v1/hr_competition/answer',\r\n method: 'POST',\r\n headers: {\r\n 'authorization': 'Bearer ' + this.token\r\n },\r\n data: {\r\n conversation_id: conversationId,\r\n user: this.userId,\r\n question: question,\r\n answer: answer\r\n },\r\n });\r\n } catch (error) {\r\n console.error('保存答案失败:', error);\r\n }\r\n }\r\n\r\n // 监听滚动事件,用于控制聊天历史记录的自动滚动行为。\r\n private handleScroll = () => {\r\n const chatHistory = this.hostElement.shadowRoot?.querySelector('.chat-history');\r\n if (!chatHistory) return;\r\n\r\n const { scrollTop, scrollHeight, clientHeight } = chatHistory;\r\n const distanceFromBottom = scrollHeight - scrollTop - clientHeight;\r\n\r\n // 更新是否应该自动滚动的状态\r\n this.shouldAutoScroll = distanceFromBottom <= this.SCROLL_THRESHOLD;\r\n };\r\n\r\n private scrollToBottom() {\r\n if (!this.shouldAutoScroll) return;\r\n const chatHistory = this.hostElement.shadowRoot?.querySelector('.chat-history');\r\n if (chatHistory && this.isOpen) {\r\n // 强制浏览器重新计算布局\r\n chatHistory.scrollTop = chatHistory.scrollHeight;\r\n }\r\n }\r\n\r\n // 添加 componentDidRender 生命周期方法,用于在组件渲染后滚动到底部\r\n componentDidRender() {\r\n if (this.isLoadingHistory || (this.shouldAutoScroll && this.isOpen)) {\r\n const chatHistory = this.hostElement.shadowRoot?.querySelector('.chat-history');\r\n if (chatHistory) {\r\n chatHistory.scrollTop = chatHistory.scrollHeight;\r\n }\r\n }\r\n }\r\n\r\n private updateUrlWithConversationId(conversationId: string) {\r\n const urlParams = new URLSearchParams(window.location.search);\r\n if (!urlParams.get('conversation_id')) {\r\n const newUrl = new URL(window.location.href);\r\n newUrl.searchParams.set('conversation_id', conversationId);\r\n window.history.replaceState({}, '', newUrl);\r\n }\r\n }\r\n\r\n // 修改加载历史消息的方法\r\n private async loadHistoryMessages() {\r\n if (!this.conversationId) return;\r\n\r\n this.isLoadingHistory = true;\r\n console.log('加载历史消息...');\r\n\r\n try {\r\n const response = await sendHttpRequest({\r\n url: '/sdk/v1/chat/messages',\r\n method: 'GET',\r\n headers: {\r\n 'authorization': 'Bearer ' + this.token\r\n },\r\n data: {\r\n conversation_id: this.conversationId,\r\n bot_id: \"3022316191018880\",\r\n limit: 20\r\n }\r\n });\r\n\r\n if (response.success && response.data) {\r\n const historyData = response.data.data || [];\r\n const formattedMessages: ChatMessage[] = historyData.map(msg => {\r\n const time = new Date(msg.created_at * 1000);\r\n const hours = time.getHours().toString().padStart(2, '0');\r\n const minutes = time.getMinutes().toString().padStart(2, '0');\r\n const timeStr = `${hours}:${minutes}`;\r\n\r\n const { inputs, ...msgWithoutInputs } = msg;\r\n\r\n return {\r\n ...msgWithoutInputs,\r\n time: timeStr,\r\n isStreaming: false,\r\n status: msg.status === 'error' ? 'error' : 'normal' as const\r\n };\r\n });\r\n\r\n this.messages = formattedMessages;\r\n }\r\n } catch (error) {\r\n console.error('加载历史消息失败:', error);\r\n alert(error instanceof Error ? error.message : '加载历史消息失败,请刷新重试');\r\n } finally {\r\n this.isLoadingHistory = false;\r\n requestAnimationFrame(() => {\r\n this.shouldAutoScroll = true;\r\n this.scrollToBottom();\r\n });\r\n }\r\n }\r\n\r\n // 修改 isOpen 的 watch 方法\r\n @Watch('isOpen')\r\n async handleIsOpenChange(newValue: boolean) {\r\n if (newValue) {\r\n if (this.conversationId) {\r\n await this.loadHistoryMessages();\r\n } \r\n }\r\n }\r\n\r\n\r\n private handleJobCategorySelect = (category: string) => {\r\n this.selectedJobCategory = category;\r\n };\r\n\r\n private handleDimensionSelect = (dimension: string) => {\r\n if (this.selectedDimensions.includes(dimension)) {\r\n this.selectedDimensions = this.selectedDimensions.filter(d => d !== dimension);\r\n } else {\r\n this.selectedDimensions = [...this.selectedDimensions, dimension];\r\n }\r\n };\r\n\r\n private handleInitialSubmit = async () => {\r\n // 修改验证逻辑\r\n if (this.requireResume && !this.selectedFile) {\r\n alert('请上传简历');\r\n return;\r\n }\r\n\r\n if (!this.selectedJobCategory) {\r\n alert('请选择职能类别');\r\n return;\r\n }\r\n\r\n if (this.selectedDimensions.length === 0) {\r\n alert('请至少选择一个关注模块');\r\n return;\r\n }\r\n\r\n // 不再显示欢迎确认对话框,因为已经在组件打开时显示了\r\n // 直接询问用户是否准备好开始面试\r\n const confirmed = confirm('如果您已做好准备请点击\"确定\"开始面试。');\r\n\r\n if (!confirmed) {\r\n return;\r\n }\r\n\r\n // 修改文件上传逻辑\r\n if (this.requireResume) {\r\n await this.uploadFile();\r\n if (this.uploadedFileInfo.length === 0) {\r\n return;\r\n }\r\n }\r\n\r\n this.showInitialUpload = false;\r\n const message = `我是一名${this.selectedJobCategory},请您开始提问`;\r\n this.sendMessageToAPI(message);\r\n };\r\n\r\n // 开始等待录制\r\n private startWaitingToRecord() {\r\n // 清除可能存在的计时器\r\n if (this.waitingTimer) {\r\n clearInterval(this.waitingTimer);\r\n }\r\n if (this.recordingTimer) {\r\n clearInterval(this.recordingTimer);\r\n }\r\n\r\n this.waitingToRecord = true;\r\n this.waitingTimeLeft = 10;\r\n\r\n this.waitingTimer = setInterval(() => {\r\n this.waitingTimeLeft--;\r\n if (this.waitingTimeLeft <= 0) {\r\n clearInterval(this.waitingTimer);\r\n this.waitingTimer = null;\r\n this.waitingToRecord = false;\r\n this.startRecording();\r\n }\r\n }, 1000);\r\n }\r\n\r\n // 开始录制视频\r\n private async startRecording() {\r\n try {\r\n const stream = await navigator.mediaDevices.getUserMedia({\r\n audio: true,\r\n video: {\r\n width: { ideal: 1280 },\r\n height: { ideal: 720 },\r\n frameRate: { ideal: 30 }\r\n }\r\n });\r\n\r\n this.recordingStream = stream;\r\n this.showRecordingUI = true;\r\n this.showCountdownWarning = false;\r\n\r\n // 重置视频引用\r\n this.videoRef = null;\r\n\r\n // 确保视频元素获取到流\r\n this.setupVideoPreview(stream);\r\n\r\n // 检测浏览器支持的MIME类型\r\n const mimeType = this.getSupportedMimeType();\r\n\r\n // 创建MediaRecorder实例\r\n let mediaRecorder;\r\n try {\r\n mediaRecorder = new MediaRecorder(stream, {\r\n mimeType: mimeType\r\n });\r\n } catch (e) {\r\n // 如果指定MIME类型失败,尝试使用默认设置\r\n console.warn('指定的MIME类型不受支持,使用默认设置:', e);\r\n try {\r\n mediaRecorder = new MediaRecorder(stream);\r\n } catch (recorderError) {\r\n // 通知父组件录制器创建失败\r\n this.recordingError.emit({\r\n type: 'recorder_creation_failed',\r\n message: '无法创建媒体录制器,您的浏览器可能不支持此功能',\r\n details: recorderError\r\n });\r\n this.showRecordingUI = false;\r\n return;\r\n }\r\n }\r\n\r\n this.mediaRecorder = mediaRecorder;\r\n\r\n const chunks: BlobPart[] = [];\r\n\r\n mediaRecorder.ondataavailable = (event) => {\r\n if (event.data.size > 0) {\r\n chunks.push(event.data);\r\n }\r\n };\r\n\r\n mediaRecorder.onerror = (event) => {\r\n // 通知父组件录制过程中发生错误\r\n this.recordingError.emit({\r\n type: 'recording_error',\r\n message: '录制过程中发生错误',\r\n details: event\r\n });\r\n this.stopRecording();\r\n };\r\n\r\n mediaRecorder.onstop = () => {\r\n try {\r\n // 根据实际使用的MIME类型创建Blob\r\n const blobType = mimeType || 'video/mp4';\r\n const blob = new Blob(chunks, { type: blobType });\r\n\r\n if (blob.size === 0) {\r\n // 通知父组件录制的视频为空\r\n this.recordingError.emit({\r\n type: 'empty_recording',\r\n message: '录制的视频为空'\r\n });\r\n this.showRecordingUI = false;\r\n return;\r\n }\r\n\r\n this.recordedBlob = blob;\r\n\r\n // 通知父组件录制已完成\r\n this.recordingStatusChange.emit({\r\n status: 'stopped',\r\n details: {\r\n duration: Math.floor((Date.now() - this.recordingStartTime) / 1000),\r\n size: blob.size,\r\n type: blob.type\r\n }\r\n });\r\n\r\n this.uploadRecordedVideo();\r\n } catch (error) {\r\n // 通知父组件处理录制视频时出错\r\n this.recordingError.emit({\r\n type: 'processing_error',\r\n message: '处理录制视频时出错',\r\n details: error\r\n });\r\n this.showRecordingUI = false;\r\n }\r\n };\r\n\r\n // 开始录制\r\n try {\r\n mediaRecorder.start();\r\n this.isRecording = true;\r\n this.recordingStartTime = Date.now();\r\n this.recordingTimeLeft = this.maxRecordingTime;\r\n\r\n // 通知父组件录制已开始\r\n this.recordingStatusChange.emit({\r\n status: 'started',\r\n details: {\r\n maxDuration: this.maxRecordingTime,\r\n mimeType: mediaRecorder.mimeType\r\n }\r\n });\r\n } catch (startError) {\r\n // 通知父组件开始录制失败\r\n this.recordingError.emit({\r\n type: 'start_failed',\r\n message: '开始录制失败,请检查您的设备权限',\r\n details: startError\r\n });\r\n this.showRecordingUI = false;\r\n return;\r\n }\r\n\r\n // 设置录制计时器\r\n this.recordingTimer = setInterval(() => {\r\n const elapsedTime = Math.floor((Date.now() - this.recordingStartTime) / 1000);\r\n this.recordingTimeLeft = Math.max(0, this.maxRecordingTime - elapsedTime);\r\n\r\n // 检查是否需要显示倒计时警告\r\n if (this.recordingTimeLeft <= this.countdownWarningTime && !this.showCountdownWarning) {\r\n this.showCountdownWarning = true;\r\n }\r\n\r\n // 时间到自动停止录制\r\n if (this.recordingTimeLeft <= 0) {\r\n this.stopRecording();\r\n }\r\n }, 1000);\r\n\r\n } catch (error) {\r\n console.error('无法访问摄像头或麦克风:', error);\r\n // 通知父组件无法访问媒体设备\r\n this.recordingError.emit({\r\n type: 'media_access_failed',\r\n message: '无法访问摄像头或麦克风,请确保已授予权限',\r\n details: error\r\n });\r\n this.showRecordingUI = false;\r\n }\r\n }\r\n\r\n // 添加新方法来设置视频预览\r\n private setupVideoPreview(stream: MediaStream) {\r\n // 延迟执行以确保DOM已更新\r\n setTimeout(() => {\r\n const videoElement = this.hostElement.shadowRoot?.querySelector('video') as HTMLVideoElement;\r\n if (videoElement && stream) {\r\n // 先尝试使用标准方法\r\n try {\r\n videoElement.srcObject = stream;\r\n videoElement.play().catch(err => {\r\n console.error('视频播放失败:', err);\r\n });\r\n } catch (e) {\r\n console.warn('设置srcObject失败,尝试替代方法:', e);\r\n\r\n // 对于不支持srcObject的旧浏览器,使用URL.createObjectURL\r\n try {\r\n // 使用类型断言解决TypeScript错误\r\n const objectUrl = URL.createObjectURL(stream as unknown as MediaSource);\r\n videoElement.src = objectUrl;\r\n\r\n // 确保在视频元素不再使用时释放URL\r\n videoElement.onended = () => {\r\n URL.revokeObjectURL(objectUrl);\r\n };\r\n } catch (urlError) {\r\n console.error('创建对象URL失败:', urlError);\r\n }\r\n }\r\n } else {\r\n console.warn('未找到视频元素或媒体流无效');\r\n }\r\n }, 100);\r\n }\r\n\r\n // 添加一个新方法来检测浏览器支持的MIME类型\r\n private getSupportedMimeType(): string {\r\n // 按优先级排列的MIME类型列表\r\n const mimeTypes = [\r\n 'video/webm;codecs=vp8,opus',\r\n 'video/webm;codecs=vp9,opus',\r\n 'video/webm',\r\n 'video/mp4',\r\n 'video/mp4;codecs=h264,aac',\r\n '' // 空字符串表示使用浏览器默认值\r\n ];\r\n\r\n // 检查MediaRecorder是否可用\r\n if (!window.MediaRecorder) {\r\n console.warn('MediaRecorder API不可用');\r\n return '';\r\n }\r\n\r\n // 检查每种MIME类型是否受支持\r\n for (const type of mimeTypes) {\r\n if (!type) return ''; // 如果是空字符串,直接返回\r\n\r\n try {\r\n if (MediaRecorder.isTypeSupported(type)) {\r\n console.log('使用支持的MIME类型:', type);\r\n return type;\r\n }\r\n } catch (e) {\r\n console.warn(`检查MIME类型支持时出错 ${type}:`, e);\r\n }\r\n }\r\n\r\n // 如果没有找到支持的类型,返回空字符串\r\n console.warn('没有找到支持的MIME类型,将使用浏览器默认值');\r\n return '';\r\n }\r\n\r\n // 停止录制\r\n private stopRecording() {\r\n if (this.mediaRecorder && this.isRecording) {\r\n this.mediaRecorder.stop();\r\n this.isRecording = false;\r\n\r\n // 清理计时器\r\n if (this.recordingTimer) {\r\n clearInterval(this.recordingTimer);\r\n this.recordingTimer = null;\r\n }\r\n\r\n // 停止并释放媒体流\r\n if (this.recordingStream) {\r\n this.recordingStream.getTracks().forEach(track => track.stop());\r\n this.recordingStream = null;\r\n }\r\n\r\n // 清理视频引用\r\n this.videoRef = null;\r\n }\r\n }\r\n\r\n // 修改上传录制的视频的方法\r\n private async uploadRecordedVideo() {\r\n if (!this.recordedBlob) return;\r\n\r\n try {\r\n this.isUploadingVideo = true;\r\n this.showRecordingUI = false;\r\n\r\n // 根据Blob类型确定文件扩展名\r\n const fileExtension = this.recordedBlob.type.includes('webm') ? 'webm' : 'mp4';\r\n const fileName = `answer.${fileExtension}`;\r\n \r\n // 创建File对象\r\n const videoFile = new File([this.recordedBlob], fileName, { type: this.recordedBlob.type });\r\n \r\n // 使用uploadFileToBackend上传视频\r\n const result = await uploadFileToBackend(videoFile, {\r\n 'authorization': 'Bearer ' + this.token\r\n });\r\n \r\n if (result) {\r\n // 使用 FileUploadResponse 类型的字段\r\n await this.saveVideoAnswer(result.cos_key);\r\n this.sendNextQuestion();\r\n } else {\r\n throw new Error('视频上传失败');\r\n }\r\n } catch (error) {\r\n console.error('视频上传错误:', error);\r\n this.recordingError.emit({\r\n type: 'upload_failed',\r\n message: '视频上传失败',\r\n details: error\r\n });\r\n } finally {\r\n this.isUploadingVideo = false;\r\n this.showRecordingUI = false;\r\n this.recordedBlob = null;\r\n }\r\n }\r\n\r\n // 修改保存视频答案的方法\r\n private async saveVideoAnswer(cosKey: string) {\r\n if (!this.conversationId) return;\r\n\r\n try {\r\n const lastAIMessage = this.messages.length > 0 ? this.messages[this.messages.length - 1] : null;\r\n\r\n if (!lastAIMessage) return;\r\n\r\n await sendHttpRequest({\r\n url: '/sdk/v1/hr_competition/answer',\r\n method: 'POST',\r\n headers: {\r\n 'authorization': 'Bearer ' + this.token\r\n },\r\n data: {\r\n conversation_id: this.conversationId,\r\n user: this.userId,\r\n question: lastAIMessage.answer,\r\n file_url: cosKey\r\n },\r\n });\r\n } catch (error) {\r\n console.error('保存视频答案失败:', error);\r\n }\r\n }\r\n\r\n // 发送\"下一题\"请求\r\n private sendNextQuestion() {\r\n this.sendMessageToAPI(\"下一题\");\r\n }\r\n\r\n /**\r\n * 发送面试完成请求\r\n */\r\n private async completeInterview() {\r\n if (!this.conversationId) return;\r\n\r\n try {\r\n await sendHttpRequest({\r\n url: `/sdk/v1/hr_competition/${this.conversationId}/end`,\r\n method: 'POST',\r\n headers: {\r\n 'authorization': 'Bearer ' + this.token\r\n },\r\n });\r\n } catch (error) {\r\n console.error('发送面试完成请求失败:', error);\r\n }\r\n }\r\n\r\n // 添加TTS合成音频的方法\r\n private async synthesizeAudio(text: string): Promise<string> {\r\n try {\r\n const response = await fetch(`${API_DOMAIN}/sdk/v1/tts/synthesize_audio`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'authorization': 'Bearer ' + this.token\r\n },\r\n body: JSON.stringify({ text })\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error('语音合成失败');\r\n }\r\n\r\n // 获取音频数据并创建Blob URL\r\n const audioBlob = await response.blob();\r\n return URL.createObjectURL(audioBlob);\r\n } catch (error) {\r\n console.error('语音合成错误:', error);\r\n throw error;\r\n }\r\n }\r\n\r\n // 播放音频的方法\r\n private playAudio(audioUrl: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n this.isPlayingAudio = true;\r\n this.audioUrl = audioUrl;\r\n\r\n // 创建音频元素\r\n if (!this.audioElement) {\r\n this.audioElement = new Audio();\r\n }\r\n\r\n this.audioElement.src = audioUrl;\r\n this.audioElement.onended = () => {\r\n this.isPlayingAudio = false;\r\n this.audioUrl = null;\r\n resolve();\r\n };\r\n\r\n this.audioElement.onerror = () => {\r\n console.error('音频播放错误');\r\n this.isPlayingAudio = false;\r\n this.audioUrl = null;\r\n resolve();\r\n };\r\n\r\n this.audioElement.play().catch(error => {\r\n console.error('音频播放失败:', error);\r\n this.isPlayingAudio = false;\r\n this.audioUrl = null;\r\n resolve();\r\n });\r\n });\r\n }\r\n\r\n // 修改 componentDidLoad 生命周期方法,确保组件卸载时释放资源\r\n disconnectedCallback() {\r\n // 释放音频资源\r\n if (this.audioElement) {\r\n this.audioElement.pause();\r\n this.audioElement.src = '';\r\n this.audioElement = null;\r\n }\r\n\r\n // 释放 Blob URL\r\n if (this.audioUrl) {\r\n URL.revokeObjectURL(this.audioUrl);\r\n this.audioUrl = null;\r\n }\r\n\r\n // 清理其他计时器\r\n if (this.waitingTimer) {\r\n clearInterval(this.waitingTimer);\r\n this.waitingTimer = null;\r\n }\r\n\r\n if (this.recordingTimer) {\r\n clearInterval(this.recordingTimer);\r\n this.recordingTimer = null;\r\n }\r\n\r\n // 停止录制\r\n this.stopRecording();\r\n }\r\n\r\n // 修改手动播放音频的方法\r\n private handlePlayAudio = async () => {\r\n if (this.audioUrl) {\r\n await this.playAudio(this.audioUrl);\r\n // 手动播放完成后开始等待录制\r\n this.startWaitingToRecord();\r\n }\r\n };\r\n\r\n render() {\r\n if (!this.isOpen) return null;\r\n\r\n const modalStyle = {\r\n zIndex: String(this.zIndex)\r\n };\r\n\r\n const containerClass = {\r\n 'modal-container': true,\r\n 'fullscreen': this.fullscreen\r\n };\r\n\r\n const overlayClass = {\r\n 'modal-overlay': true,\r\n 'fullscreen-overlay': this.fullscreen\r\n };\r\n\r\n const renderVideoPreview = () => (\r\n <div class=\"video-preview\">\r\n <video\r\n autoPlay\r\n playsInline\r\n muted\r\n style={{ transform: 'scaleX(-1)' }}\r\n ref={(el) => {\r\n if (el && this.recordingStream && !this.videoRef) {\r\n this.videoRef = el;\r\n // 不在这里设置srcObject,而是使用setupVideoPreview方法\r\n }\r\n }}\r\n ></video>\r\n <div class={{\r\n 'recording-status': true,\r\n 'warning': this.showCountdownWarning\r\n }}>\r\n <span class=\"recording-dot\"></span>\r\n <span>\r\n 录制中 {Math.floor(this.recordingTimeLeft / 60)}:{(this.recordingTimeLeft % 60).toString().padStart(2, '0')}\r\n {this.showCountdownWarning && ` (即将自动完成)`}\r\n </span>\r\n </div>\r\n </div>\r\n );\r\n\r\n // 渲染占位符状态信息\r\n const renderPlaceholderStatus = () => {\r\n // 正在播放音频\r\n if (this.isPlayingAudio) {\r\n return (\r\n <div class=\"placeholder-status\">\r\n <p>正在播放问题,请听完后准备回答...</p>\r\n </div>\r\n );\r\n }\r\n\r\n // 正在上传视频\r\n if (this.isUploadingVideo) {\r\n return (\r\n <div class=\"placeholder-status\">\r\n <p>正在上传视频,请稍候...</p>\r\n </div>\r\n );\r\n }\r\n\r\n // 正在加载或等待AI回复\r\n if (this.isLoading || this.currentStreamingMessage) {\r\n return (\r\n <div class=\"placeholder-status\">\r\n <p>请等待题目...</p>\r\n </div>\r\n );\r\n }\r\n\r\n // 等待开始录制\r\n if (this.waitingToRecord) {\r\n return (\r\n <div class=\"placeholder-status\">\r\n <p>请准备好,{this.waitingTimeLeft}秒后将开始录制您的回答...</p>\r\n </div>\r\n );\r\n }\r\n \r\n // 添加默认状态\r\n return (\r\n <div class=\"placeholder-status default-status\">\r\n <p>准备中...</p>\r\n </div>\r\n );\r\n };\r\n\r\n return (\r\n <div class={overlayClass} style={modalStyle}>\r\n <div class={containerClass}>\r\n {this.isShowHeader && (\r\n <div class=\"modal-header\">\r\n <div class=\"header-left\">\r\n {this.icon && <img src={this.icon} class=\"header-icon\" alt=\"应用图标\" />}\r\n <div>{this.modalTitle}</div>\r\n </div>\r\n {this.isNeedClose && (\r\n <button class=\"close-button\" onClick={this.handleClose}>\r\n <span>×</span>\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n {this.showInitialUpload ? (\r\n <div class=\"initial-upload\">\r\n <div class=\"upload-section\">\r\n {/* 根据 requireResume 条件渲染简历上传部分 */}\r\n {this.requireResume && (\r\n <>\r\n <h3>开始前,请上传您的简历</h3>\r\n <div class=\"upload-area\" onClick={this.handleUploadClick}>\r\n {this.selectedFile ? (\r\n <div class=\"file-info\">\r\n <span>{this.selectedFile.name}</span>\r\n <button class=\"remove-file\" onClick={(e) => {\r\n e.stopPropagation();\r\n this.clearSelectedFile();\r\n }}>×</button>\r\n </div>\r\n ) : (\r\n <div class=\"upload-placeholder\">\r\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" width=\"48\" height=\"48\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m0-16l-4 4m4-4l4 4\" />\r\n </svg>\r\n <p>点击上传简历</p>\r\n <p class=\"upload-hint\">支持 txt、 markdown、 pdf、 docx、 md 格式</p>\r\n </div>\r\n )}\r\n </div>\r\n </>\r\n )}\r\n\r\n <div class=\"category-select\">\r\n <h3>请选择您的职能类别(单选)</h3>\r\n <div class=\"category-options\">\r\n {this.jobCategories.map(category => (\r\n <button\r\n class={{\r\n 'category-button': true,\r\n 'selected': this.selectedJobCategory === category\r\n }}\r\n onClick={() => this.handleJobCategorySelect(category)}\r\n >\r\n {category}\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n\r\n <div class=\"dimension-select\">\r\n <h3>请选择关注的模块(可多选)</h3>\r\n <div class=\"dimension-options\">\r\n {this.dimensions.map(dimension => (\r\n <button\r\n class={{\r\n 'dimension-button': true,\r\n 'selected': this.selectedDimensions.includes(dimension)\r\n }}\r\n onClick={() => this.handleDimensionSelect(dimension)}\r\n >\r\n {dimension}\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n\r\n <button\r\n class=\"submit-button\"\r\n disabled={\r\n (this.requireResume && !this.selectedFile) ||\r\n !this.selectedJobCategory ||\r\n this.selectedDimensions.length === 0 ||\r\n (this.requireResume && this.isUploading)\r\n }\r\n onClick={this.handleInitialSubmit}\r\n >\r\n {this.requireResume && this.isUploading ? '上传中...' : '开始面试'}\r\n </button>\r\n </div>\r\n {this.requireResume && (\r\n <input\r\n type=\"file\"\r\n class=\"file-input\"\r\n onChange={this.handleFileChange}\r\n accept=\".pdf,.doc,.docx,.txt\"\r\n />\r\n )}\r\n </div>\r\n ) : (\r\n <div style={{ height: '100%' }}>\r\n <div class=\"chat-history\" onScroll={this.handleScroll}>\r\n {this.isLoadingHistory ? (\r\n <div class=\"loading-container\">\r\n <div class=\"loading-spinner\"></div>\r\n <p>加载历史消息中...</p>\r\n </div>\r\n ) : (\r\n <div>\r\n {this.messages.map((message) => (\r\n <div id={`message_${message.id}`} key={message.id}>\r\n <pcm-chat-message\r\n message={message}\r\n onMessageChange={(event) => {\r\n const updatedMessages = this.messages.map(msg =>\r\n msg.id === message.id ? { ...msg, ...event.detail } : msg\r\n );\r\n this.messages = updatedMessages;\r\n }}\r\n ></pcm-chat-message>\r\n </div>\r\n ))}\r\n {this.currentStreamingMessage && (\r\n <div id={`message_${this.currentStreamingMessage.id}`}>\r\n <pcm-chat-message\r\n message={this.currentStreamingMessage}\r\n ></pcm-chat-message>\r\n </div>\r\n )}\r\n {this.messages.length === 0 && !this.currentStreamingMessage && (\r\n <div class=\"empty-state\">\r\n <p>请上传简历开始面试</p>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n <div class=\"recording-section\">\r\n <div class=\"recording-container\">\r\n <div class=\"video-area\">\r\n {this.showRecordingUI ? (\r\n renderVideoPreview()\r\n ) : (\r\n <div class=\"video-preview placeholder\">\r\n {renderPlaceholderStatus()}\r\n </div>\r\n )}\r\n </div>\r\n {/* 添加进度条和数字进度 */}\r\n <div class=\"progress-container\">\r\n <div class=\"progress-bar-container\">\r\n <div \r\n class=\"progress-bar\" \r\n style={{ \r\n width: `${Math.max(0, this.currentQuestionNumber - 1) / this.totalQuestions * 100}%` \r\n }}\r\n ></div>\r\n </div>\r\n <div class=\"progress-text\">\r\n 已完成{Math.max(0, this.currentQuestionNumber - 1)}/{this.totalQuestions}\r\n </div>\r\n </div>\r\n <div class=\"recording-controls\">\r\n {this.showRecordingUI ? (\r\n <button\r\n class=\"stop-recording-button\"\r\n onClick={() => this.stopRecording()}\r\n >\r\n 完成本题回答\r\n </button>\r\n ) : (\r\n <div class=\"waiting-message\">\r\n {(() => {\r\n // 显示播放按钮(当不自动播放且有音频URL时)\r\n if (!this.enableVoice && this.audioUrl && !this.isPlayingAudio) {\r\n return (\r\n <div class=\"play-audio-container\" onClick={this.handlePlayAudio}>\r\n <p>\r\n <svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"currentColor\" style={{ verticalAlign: 'middle', marginRight: '8px' }}>\r\n <path d=\"M8 5v14l11-7z\" />\r\n </svg>\r\n <span style={{ verticalAlign: 'middle' }}>播放题目</span>\r\n </p>\r\n </div>\r\n );\r\n }\r\n \r\n // 其他状态下显示禁用的\"完成回答\"按钮\r\n return (\r\n <button class=\"stop-recording-button disabled\" disabled>\r\n 完成回答\r\n </button>\r\n );\r\n })()}\r\n </div>\r\n )}\r\n </div>\r\n \r\n \r\n </div>\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n }\r\n}",":host {\r\n display: block;\r\n}\r\n\r\n.modal-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background-color: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n overflow-y: auto;\r\n padding: 20px;\r\n z-index: 1000;\r\n}\r\n\r\n/* 全屏模式下取消 padding */\r\n.fullscreen-overlay {\r\n padding: 0;\r\n}\r\n\r\n.modal-container {\r\n background: white;\r\n border-radius: 8px;\r\n width: 100%;\r\n max-width: 500px;\r\n display: flex;\r\n flex-direction: column;\r\n position: relative;\r\n margin: auto;\r\n transition: all 0.3s ease-out;\r\n overflow: hidden;\r\n}\r\n\r\n/* 全屏模式样式 */\r\n.modal-container.fullscreen {\r\n width: 100vw;\r\n max-width: none;\r\n height: 100%;\r\n border-radius: 0;\r\n margin: 0;\r\n display: flex;\r\n flex-direction: column;\r\n height: 100vh;\r\n max-height: 100vh;\r\n}\r\n\r\n/* PC端布局 */\r\n.pc-layout {\r\n width: 80%;\r\n max-width: 500px;\r\n min-width: 320px;\r\n}\r\n\r\n/* 移动端布局 */\r\n.mobile-layout {\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 0;\r\n}\r\n\r\n/* 响应式布局 */\r\n@media screen and (max-width: 768px) {\r\n .pc-layout {\r\n width: 95%;\r\n }\r\n\r\n .modal-overlay {\r\n padding: 0;\r\n }\r\n\r\n .modal-container.fullscreen {\r\n /* 支持 iOS Safari */\r\n height: -webkit-fill-available;\r\n max-height: -webkit-fill-available;\r\n /* 确保内容不会被顶部状态栏和底部工具栏遮挡 */\r\n padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom);\r\n margin-top: 40px;\r\n height: calc(100% - 40px);\r\n max-height: calc(100% - 40px);\r\n border-radius: 16px 16px 0 0;\r\n }\r\n\r\n .modal-container.mobile-layout {\r\n width: 100%;\r\n height: 100vh;\r\n max-height: 100vh;\r\n min-height: 100vh;\r\n border-radius: 0;\r\n margin: 0;\r\n display: flex;\r\n flex-direction: column;\r\n }\r\n}\r\n\r\n.modal-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 4px 16px;\r\n height: 50px;\r\n border-bottom: 1px solid #e8e8e8;\r\n flex-shrink: 0; /* 防止头部被压缩 */\r\n}\r\n\r\n.header-left {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n}\r\n\r\n.header-icon {\r\n width: 24px;\r\n height: 24px;\r\n}\r\n\r\n.close-button {\r\n background: transparent;\r\n border: none;\r\n cursor: pointer;\r\n padding: 8px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 4px;\r\n}\r\n\r\n.close-button:hover {\r\n background-color: rgba(0, 0, 0, 0.04);\r\n}\r\n\r\n.close-button span {\r\n font-size: 24px;\r\n line-height: 1;\r\n color: #999;\r\n}\r\n\r\n.close-button:hover span {\r\n color: #666;\r\n}\r\n\r\n\r\n\r\n.file-input {\r\n display: none;\r\n}\r\n\r\n/* 聊天模态框容器 */\r\n.chat-modal-container {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n display: flex;\r\n flex-direction: column;\r\n} ","/* 模态框基础样式 */\r\n.modal-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background-color: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n z-index: 1000;\r\n overflow-y: auto;\r\n padding: 20px;\r\n}\r\n\r\n/* 全屏模式下取消 padding */\r\n.fullscreen-overlay {\r\n padding: 0;\r\n background-color: rgba(0, 0, 0, 0.7);\r\n}\r\n\r\n.modal-container {\r\n background-color: #fff;\r\n border-radius: 8px;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n position: relative;\r\n margin: auto;\r\n transition: all 0.3s ease-out;\r\n overflow: hidden;\r\n}\r\n\r\n/* 全屏模式样式 */\r\n.modal-container.fullscreen {\r\n width: 100vw;\r\n max-width: none;\r\n height: 100%;\r\n border-radius: 0;\r\n margin: 0;\r\n display: flex;\r\n flex-direction: column;\r\n height: 100vh;\r\n max-height: 100vh;\r\n}\r\n\r\n/* PC端布局 */\r\n.pc-layout {\r\n width: 80%;\r\n max-width: 500px;\r\n min-width: 320px;\r\n}\r\n\r\n/* 移动端布局 */\r\n.mobile-layout {\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 0;\r\n}\r\n\r\n/* 响应式布局 */\r\n@media screen and (max-width: 768px) {\r\n .pc-layout {\r\n width: 95%;\r\n }\r\n\r\n .modal-overlay {\r\n padding: 0;\r\n }\r\n\r\n .modal-container.fullscreen {\r\n /* 支持 iOS Safari */\r\n height: -webkit-fill-available;\r\n max-height: -webkit-fill-available;\r\n /* 确保内容不会被顶部状态栏和底部工具栏遮挡 */\r\n padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom);\r\n margin-top: 40px;\r\n height: calc(100% - 40px);\r\n max-height: calc(100% - 40px);\r\n border-radius: 16px 16px 0 0;\r\n }\r\n\r\n .modal-container.mobile-layout {\r\n width: 100%;\r\n height: 100vh;\r\n max-height: 100vh;\r\n min-height: 100vh;\r\n border-radius: 0;\r\n margin: 0;\r\n display: flex;\r\n flex-direction: column;\r\n }\r\n}\r\n\r\n/* 模态框头部样式 */\r\n.modal-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 4px 16px;\r\n height: 50px;\r\n border-bottom: 1px solid #e8e8e8;\r\n flex-shrink: 0;\r\n}\r\n\r\n.header-left {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n}\r\n\r\n.header-icon {\r\n width: 24px;\r\n height: 24px;\r\n}\r\n\r\n.close-button {\r\n background: transparent;\r\n border: none;\r\n cursor: pointer;\r\n padding: 8px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 4px;\r\n}\r\n\r\n.close-button:hover {\r\n background-color: rgba(0, 0, 0, 0.04);\r\n}\r\n\r\n.close-button span {\r\n font-size: 24px;\r\n line-height: 1;\r\n color: #999;\r\n}\r\n\r\n.close-button:hover span {\r\n color: #666;\r\n}\r\n\r\n\r\n/* 文件上传区域通用样式 */\r\n.upload-area {\r\n border: 2px dashed #ddd;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n transition: all 0.3s ease;\r\n margin-bottom: 20px;\r\n width: 100%;\r\n}\r\n\r\n.upload-area:hover {\r\n border-color: #1890ff;\r\n background-color: rgba(24, 144, 255, 0.05);\r\n}\r\n\r\n\r\n.upload-placeholder {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n color: #666;\r\n}\r\n\r\n.upload-placeholder svg {\r\n color: #1890ff;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.upload-placeholder p {\r\n margin: 4px 0;\r\n}\r\n\r\n.upload-hint {\r\n font-size: 0.8rem;\r\n color: #999;\r\n margin-top: 0.5rem;\r\n}\r\n\r\n.file-info {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 8px;\r\n background: #f9f9f9;\r\n border: 1px solid #e8e8e8;\r\n border-radius: 4px;\r\n}\r\n\r\n.file-info span {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n max-width: calc(100% - 30px);\r\n}\r\n\r\n.remove-file {\r\n background: transparent;\r\n border: none;\r\n color: #999;\r\n cursor: pointer;\r\n padding: 4px 8px;\r\n font-size: 16px;\r\n line-height: 1;\r\n border-radius: 4px;\r\n transition: all 0.2s;\r\n}\r\n\r\n.remove-file:hover {\r\n background-color: #f0f0f0;\r\n color: #666;\r\n}\r\n\r\n.file-input {\r\n display: none;\r\n}\r\n\r\n\r\n\r\n/* 输入容器样式 */\r\n.input-container {\r\n padding: 20px;\r\n display: flex;\r\n flex-direction: column;\r\n height: calc(100% - 50px);\r\n /* 减去header高度 */\r\n overflow-y: auto;\r\n}\r\n\r\n.input-container h3 {\r\n margin-top: 0;\r\n margin-bottom: 20px;\r\n font-size: 18px;\r\n color: #333;\r\n text-align: center;\r\n}\r\n\r\n/* JD输入区域样式 */\r\n.jd-input-section {\r\n margin-bottom: 20px;\r\n}\r\n\r\n.jd-input-section label {\r\n display: block;\r\n margin-bottom: 8px;\r\n font-weight: 500;\r\n color: #333;\r\n}\r\n\r\n.job-description-textarea {\r\n width: 100%;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n resize: vertical;\r\n font-family: inherit;\r\n font-size: 14px;\r\n line-height: 1.5;\r\n transition: border-color 0.3s;\r\n}\r\n\r\n.job-description-textarea:focus {\r\n outline: none;\r\n border-color: #1890ff;\r\n box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);\r\n}\r\n\r\n/* 简历上传区域样式 */\r\n.resume-upload-section {\r\n margin-bottom: 20px;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n}\r\n\r\n.resume-upload-section label {\r\n display: block;\r\n margin-bottom: 8px;\r\n font-weight: 500;\r\n color: #333;\r\n align-self: flex-start;\r\n}\r\n\r\n\r\n/* 提交按钮通用样式 */\r\n.submit-button {\r\n margin-top: 10px;\r\n padding: 10px 30px;\r\n background: #1890ff;\r\n color: white;\r\n border: none;\r\n border-radius: 4px;\r\n font-size: 16px;\r\n cursor: pointer;\r\n transition: all 0.3s ease;\r\n width: 100%;\r\n max-width: 400px;\r\n align-self: center;\r\n /* 确保按钮居中 */\r\n}\r\n\r\n.submit-button:disabled {\r\n background: #ccc;\r\n cursor: not-allowed;\r\n}\r\n\r\n.submit-button:hover:not(:disabled) {\r\n background: #40a9ff;\r\n}\r\n\r\n/* 聊天模态框容器 */\r\n.chat-modal-container {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n/* AI免责声明和备案信息样式 */\r\n.ai-disclaimer {\r\n margin-top: 16px;\r\n text-align: center;\r\n font-size: 12px;\r\n color: #999;\r\n line-height: 1.5;\r\n}\r\n\r\n.ai-disclaimer p {\r\n margin: 4px 0;\r\n}\r\n\r\n.beian-info {\r\n display: flex;\r\n justify-content: center;\r\n flex-wrap: wrap;\r\n gap: 4px;\r\n}\r\n\r\n.ai-disclaimer a {\r\n color: #666;\r\n text-decoration: none;\r\n transition: color 0.2s ease;\r\n}\r\n\r\n.ai-disclaimer a:hover {\r\n color: #1890ff;\r\n text-decoration: underline;\r\n}","import { Component, Prop, h, State, Element, Event, EventEmitter, Watch } from '@stencil/core';\r\nimport { uploadFileToBackend, FileUploadResponse, sendHttpRequest } from '../../utils/utils';\r\n\r\n@Component({\r\n tag: 'pcm-jlpp-modal',\r\n styleUrls: ['pcm-jlpp-modal.css','../../global/global.css'],\r\n shadow: true,\r\n})\r\nexport class JlppModal {\r\n /**\r\n * 模态框标题\r\n */\r\n @Prop() modalTitle: string = '简历剖析助手';\r\n\r\n /**\r\n * SDK鉴权密钥\r\n */\r\n @Prop({ attribute: 'token' }) token: string = '';\r\n\r\n /**\r\n * 是否显示聊天模态框\r\n */\r\n @Prop({ mutable: true }) isOpen: boolean = false;\r\n\r\n /**\r\n * 当点击模态框关闭时触发\r\n */\r\n @Event() modalClosed: EventEmitter<void>;\r\n\r\n /**\r\n * 应用图标URL\r\n */\r\n @Prop() icon?: string;\r\n\r\n /**\r\n * 聊天框的页面层级\r\n */\r\n @Prop() zIndex?: number = 1000;\r\n\r\n /**\r\n * 是否展示顶部标题栏\r\n */\r\n @Prop() isShowHeader: boolean = true;\r\n\r\n /**\r\n * 是否展示右上角的关闭按钮\r\n */\r\n @Prop() isNeedClose: boolean = true;\r\n\r\n /**\r\n * 会话ID,传入继续对话,否则创建新会话\r\n */\r\n @Prop({ mutable: true }) conversationId?: string;\r\n\r\n /**\r\n * 默认查询文本\r\n */\r\n @Prop() defaultQuery: string = '';\r\n\r\n /**\r\n * 是否以全屏模式打开,移动端建议设置为true\r\n */\r\n @Prop() fullscreen: boolean = false;\r\n\r\n\r\n /**\r\n * 自定义输入参数,传入job_info时,会隐藏JD输入区域\r\n */\r\n @Prop() customInputs: { [key: string]: any } = {};\r\n\r\n /**\r\n * 上传成功事件\r\n */\r\n @Event() uploadSuccess: EventEmitter<FileUploadResponse>;\r\n\r\n /**\r\n * 流式输出完成事件\r\n */\r\n @Event() streamComplete: EventEmitter<{\r\n conversation_id: string;\r\n event: string;\r\n message_id: string;\r\n id: string;\r\n }>;\r\n\r\n /**\r\n * 新会话开始的回调,只会在一轮对话开始时触发一次\r\n */\r\n @Event() conversationStart: EventEmitter<{\r\n conversation_id: string;\r\n event: string;\r\n message_id: string;\r\n id: string;\r\n }>;\r\n\r\n /**\r\n * 当聊天完成时触发\r\n */\r\n @Event() interviewComplete: EventEmitter<{\r\n conversation_id: string;\r\n total_questions: number;\r\n }>;\r\n\r\n /**\r\n * SDK密钥验证失败事件\r\n */\r\n @Event() tokenInvalid: EventEmitter<void>;\r\n\r\n @State() selectedFile: File | null = null;\r\n @State() isUploading: boolean = false;\r\n @State() uploadedFileInfo: FileUploadResponse | null = null;\r\n @State() showChatModal: boolean = false;\r\n @State() jobDescription: string = '';\r\n @State() isSubmitting: boolean = false;\r\n\r\n // 添加新的状态来控制过渡动画\r\n @State() isTransitioning: boolean = false;\r\n @State() transitionTimer: any = null;\r\n\r\n // 使用 @Element 装饰器获取组件的 host 元素\r\n @Element() hostElement: HTMLElement;\r\n\r\n private handleClose = () => {\r\n this.isOpen = false;\r\n this.modalClosed.emit();\r\n };\r\n\r\n private handleFileChange = (event: Event) => {\r\n const input = event.target as HTMLInputElement;\r\n if (input.files && input.files.length > 0) {\r\n this.selectedFile = input.files[0];\r\n }\r\n };\r\n\r\n private handleUploadClick = () => {\r\n const fileInput = this.hostElement.shadowRoot?.querySelector('.file-input') as HTMLInputElement;\r\n fileInput?.click();\r\n };\r\n\r\n private clearSelectedFile = () => {\r\n this.selectedFile = null;\r\n this.uploadedFileInfo = null;\r\n const fileInput = this.hostElement.shadowRoot?.querySelector('.file-input') as HTMLInputElement;\r\n if (fileInput) {\r\n fileInput.value = '';\r\n }\r\n };\r\n\r\n private handleJobDescriptionChange = (event: Event) => {\r\n const textarea = event.target as HTMLTextAreaElement;\r\n this.jobDescription = textarea.value;\r\n };\r\n\r\n private async uploadFile() {\r\n if (!this.selectedFile) return;\r\n\r\n this.isUploading = true;\r\n\r\n try {\r\n const result = await uploadFileToBackend(this.selectedFile, {\r\n 'authorization': 'Bearer ' + this.token\r\n });\r\n\r\n this.uploadedFileInfo = result;\r\n // 触发上传成功事件\r\n this.uploadSuccess.emit(result);\r\n } catch (error) {\r\n console.error('文件上传错误:', error);\r\n this.clearSelectedFile();\r\n alert(error instanceof Error ? error.message : '文件上传失败,请重试');\r\n } finally {\r\n this.isUploading = false;\r\n }\r\n }\r\n\r\n private handleStartAnalysis = async () => {\r\n if (!this.selectedFile) {\r\n alert('请上传简历');\r\n return;\r\n }\r\n\r\n // 如果没有预设的job_info,则需要检查用户输入\r\n if (!this.customInputs?.job_info && !this.jobDescription.trim()) {\r\n alert('请输入职位描述');\r\n return;\r\n }\r\n\r\n this.isSubmitting = true;\r\n\r\n try {\r\n // 如果还没上传,先上传文件\r\n if (!this.uploadedFileInfo) {\r\n await this.uploadFile();\r\n if (!this.uploadedFileInfo) {\r\n this.isSubmitting = false;\r\n return; // 上传失败\r\n }\r\n }\r\n\r\n // 使用预设的job_info或用户输入的jobDescription\r\n // const jobInfo = this.customInputs?.job_info || this.jobDescription;\r\n\r\n // console.log('传递的customInputs:', {\r\n // ...this.customInputs,\r\n // file_url: this.uploadedFileInfo.cos_key,\r\n // job_info: jobInfo\r\n // });\r\n\r\n // 直接显示聊天模态框\r\n this.showChatModal = true;\r\n } catch (error) {\r\n console.error('开始分析时出错:', error);\r\n alert('开始分析时出错,请重试');\r\n } finally {\r\n this.isSubmitting = false;\r\n }\r\n };\r\n\r\n @Watch('isOpen')\r\n handleIsOpenChange(newValue: boolean) {\r\n if (!newValue) {\r\n // 重置状态\r\n this.clearSelectedFile();\r\n this.showChatModal = false;\r\n this.jobDescription = '';\r\n \r\n // 清除可能存在的计时器\r\n if (this.transitionTimer) {\r\n clearTimeout(this.transitionTimer);\r\n this.transitionTimer = null;\r\n }\r\n } else {\r\n // 当模态框打开时,验证API密钥\r\n this.verifyApiKey();\r\n \r\n if (this.conversationId) {\r\n // 如果有会话ID,直接显示聊天模态框\r\n this.showChatModal = true;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 验证API密钥\r\n */\r\n private async verifyApiKey() {\r\n if (!this.token) {\r\n this.tokenInvalid.emit();\r\n return;\r\n }\r\n try {\r\n const response = await sendHttpRequest({\r\n url: '/sdk/v1/user',\r\n method: 'GET',\r\n headers: {\r\n 'Authorization': `Bearer ${this.token}`\r\n }\r\n });\r\n\r\n if (!response.success) {\r\n throw new Error(response.message || 'API密钥验证失败');\r\n }\r\n \r\n // 验证成功,继续正常流程\r\n } catch (error) {\r\n console.error('API密钥验证错误:', error);\r\n // 通知父组件API密钥无效\r\n this.tokenInvalid.emit();\r\n } \r\n }\r\n\r\n componentWillLoad() {\r\n // 检查 customInputs 中是否有 job_info\r\n if (this.customInputs && this.customInputs.job_info) {\r\n this.jobDescription = this.customInputs.job_info;\r\n }\r\n }\r\n\r\n // 处理流式输出完成事件\r\n private handleStreamComplete = (event: CustomEvent) => {\r\n // 将事件转发出去\r\n this.streamComplete.emit(event.detail);\r\n };\r\n\r\n // 处理会话开始事件\r\n private handleConversationStart = (event: CustomEvent) => {\r\n this.conversationStart.emit(event.detail);\r\n };\r\n\r\n // 处理面试完成事件\r\n private handleInterviewComplete = (event: CustomEvent) => {\r\n this.interviewComplete.emit(event.detail);\r\n };\r\n\r\n render() {\r\n if (!this.isOpen) return null;\r\n\r\n const modalStyle = {\r\n zIndex: String(this.zIndex)\r\n };\r\n\r\n const containerClass = {\r\n 'modal-container': true,\r\n 'fullscreen': this.fullscreen,\r\n 'pc-layout': true,\r\n };\r\n \r\n const overlayClass = {\r\n 'modal-overlay': true,\r\n 'fullscreen-overlay': this.fullscreen\r\n };\r\n\r\n // 检查是否有会话ID,如果有则直接显示聊天模态框\r\n if (this.conversationId && !this.showChatModal) {\r\n this.showChatModal = true;\r\n }\r\n\r\n // 修正这里的逻辑,确保当 customInputs.job_info 存在时,hideJdInput 为 true\r\n const hideJdInput = Boolean(this.customInputs && this.customInputs.job_info);\r\n\r\n return (\r\n <div class={overlayClass} style={modalStyle}>\r\n <div class={containerClass}>\r\n {this.isShowHeader && (\r\n <div class=\"modal-header\">\r\n <div class=\"header-left\">\r\n {this.icon && <img src={this.icon} class=\"header-icon\" alt=\"应用图标\" />}\r\n <div>{this.modalTitle}</div>\r\n </div>\r\n {this.isNeedClose && (\r\n <button class=\"close-button\" onClick={this.handleClose}>\r\n <span>×</span>\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* 输入界面 - 仅在不显示聊天模态框且没有会话ID时显示 */}\r\n {!this.showChatModal && !this.conversationId && (\r\n <div class=\"input-container\">\r\n {/* JD输入区域 - 仅在没有customInputs.job_info时显示 */}\r\n {!hideJdInput && (\r\n <div class=\"jd-input-section\">\r\n <label htmlFor=\"job-description\">请输入职位描述 (JD)</label>\r\n <textarea \r\n id=\"job-description\"\r\n class=\"job-description-textarea\"\r\n placeholder=\"请输入职位描述,包括职责、要求等信息...\"\r\n rows={6}\r\n value={this.jobDescription}\r\n onInput={this.handleJobDescriptionChange}\r\n ></textarea>\r\n </div>\r\n )}\r\n \r\n {/* 简历上传区域 */}\r\n <div class=\"resume-upload-section\">\r\n <label>上传简历</label>\r\n <div class=\"upload-area\" onClick={this.handleUploadClick}>\r\n {this.selectedFile ? (\r\n <div class=\"file-info\">\r\n <span>{this.selectedFile.name}</span>\r\n <button class=\"remove-file\" onClick={(e) => {\r\n e.stopPropagation();\r\n this.clearSelectedFile();\r\n }}>×</button>\r\n </div>\r\n ) : (\r\n <div class=\"upload-placeholder\">\r\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" width=\"48\" height=\"48\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m0-16l-4 4m4-4l4 4\" />\r\n </svg>\r\n <p>点击上传简历</p>\r\n <p class=\"upload-hint\">支持 txt、markdown、pdf、docx、doc、md 格式</p>\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n\r\n <button\r\n class=\"submit-button\"\r\n disabled={!this.selectedFile || (!hideJdInput && !this.jobDescription.trim()) || this.isUploading || this.isSubmitting}\r\n onClick={this.handleStartAnalysis}\r\n >\r\n {this.isUploading ? '上传中...' : this.isSubmitting ? '处理中...' : '开始分析'}\r\n </button>\r\n\r\n <div class=\"ai-disclaimer\">\r\n <p>所有内容均由AI生成仅供参考</p>\r\n <p class=\"beian-info\">\r\n <span>中央网信办生成式人工智能服务备案号</span>:\r\n <a href=\"https://www.pincaimao.com\" target=\"_blank\" rel=\"noopener noreferrer\">Hunan-PinCaiMao-202412310003</a>\r\n </p>\r\n </div>\r\n\r\n <input\r\n type=\"file\"\r\n class=\"file-input\"\r\n onChange={this.handleFileChange}\r\n />\r\n </div>\r\n )}\r\n\r\n {/* 聊天界面 - 在显示聊天模态框时显示 */}\r\n {this.showChatModal && (\r\n <div class=\"chat-modal-container\">\r\n <pcm-app-chat-modal\r\n isOpen={true}\r\n modalTitle={this.modalTitle}\r\n icon={this.icon}\r\n token={this.token}\r\n isShowHeader={this.isShowHeader} \r\n isNeedClose={this.isShowHeader} \r\n zIndex={this.zIndex}\r\n fullscreen={this.fullscreen}\r\n conversationId={this.conversationId}\r\n defaultQuery={this.defaultQuery}\r\n enableTTS={false}\r\n enableVoice={false}\r\n botId=\"3022316191018881\"\r\n customInputs={this.conversationId ? undefined : {\r\n ...this.customInputs,\r\n file_url: this.uploadedFileInfo?.cos_key,\r\n job_info: this.customInputs?.job_info || this.jobDescription\r\n }}\r\n interviewMode=\"text\"\r\n onModalClosed={this.handleClose}\r\n onStreamComplete={this.handleStreamComplete}\r\n onConversationStart={this.handleConversationStart}\r\n onInterviewComplete={this.handleInterviewComplete}\r\n ></pcm-app-chat-modal>\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n }\r\n} ",":host {\r\n display: block;\r\n}\r\n\r\n.modal-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background-color: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n overflow-y: auto;\r\n padding: 20px;\r\n z-index: 1000;\r\n}\r\n\r\n/* 全屏模式下取消 padding */\r\n.fullscreen-overlay {\r\n padding: 0;\r\n}\r\n\r\n.modal-container {\r\n background: white;\r\n border-radius: 8px;\r\n width: 100%;\r\n max-width: 500px;\r\n display: flex;\r\n flex-direction: column;\r\n position: relative;\r\n margin: auto;\r\n transition: all 0.3s ease-out;\r\n overflow: hidden;\r\n}\r\n\r\n/* 全屏模式样式 */\r\n.modal-container.fullscreen {\r\n width: 100vw;\r\n max-width: none;\r\n height: 100%;\r\n border-radius: 0;\r\n margin: 0;\r\n display: flex;\r\n flex-direction: column;\r\n height: 100vh;\r\n max-height: 100vh;\r\n}\r\n\r\n/* PC端布局 */\r\n.pc-layout {\r\n width: 80%;\r\n max-width: 500px;\r\n min-width: 320px;\r\n}\r\n\r\n/* 移动端布局 */\r\n.mobile-layout {\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 0;\r\n}\r\n\r\n/* 响应式布局 */\r\n@media screen and (max-width: 768px) {\r\n .pc-layout {\r\n width: 95%;\r\n }\r\n\r\n .modal-overlay {\r\n padding: 0;\r\n }\r\n\r\n .modal-container.fullscreen {\r\n /* 支持 iOS Safari */\r\n height: -webkit-fill-available;\r\n max-height: -webkit-fill-available;\r\n /* 确保内容不会被顶部状态栏和底部工具栏遮挡 */\r\n padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom);\r\n margin-top: 40px;\r\n height: calc(100% - 40px);\r\n max-height: calc(100% - 40px);\r\n border-radius: 16px 16px 0 0;\r\n }\r\n\r\n .modal-container.mobile-layout {\r\n width: 100%;\r\n height: 100vh;\r\n max-height: 100vh;\r\n min-height: 100vh;\r\n border-radius: 0;\r\n margin: 0;\r\n display: flex;\r\n flex-direction: column;\r\n }\r\n}\r\n\r\n.modal-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 4px 16px;\r\n height: 50px;\r\n border-bottom: 1px solid #e8e8e8;\r\n flex-shrink: 0; /* 防止头部被压缩 */\r\n}\r\n\r\n.header-left {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n}\r\n\r\n.header-icon {\r\n width: 24px;\r\n height: 24px;\r\n}\r\n\r\n.close-button {\r\n background: transparent;\r\n border: none;\r\n cursor: pointer;\r\n padding: 8px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 4px;\r\n}\r\n\r\n.close-button:hover {\r\n background-color: rgba(0, 0, 0, 0.04);\r\n}\r\n\r\n.close-button span {\r\n font-size: 24px;\r\n line-height: 1;\r\n color: #999;\r\n}\r\n\r\n.close-button:hover span {\r\n color: #666;\r\n}\r\n\r\n.upload-container {\r\n padding: 30px 20px;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n text-align: center;\r\n height: calc(100% - 60px); /* 减去header高度 */\r\n transition: opacity 0.5s ease-out;\r\n}\r\n\r\n.upload-container h3 {\r\n margin-top: 0;\r\n margin-bottom: 20px;\r\n font-size: 18px;\r\n color: #333;\r\n}\r\n\r\n\r\n.submit-button {\r\n max-width: 400px;\r\n}\r\n\r\n\r\n.file-input {\r\n display: none;\r\n}\r\n\r\n.transitioning {\r\n position: relative;\r\n overflow: hidden;\r\n}\r\n\r\n.fade-out {\r\n opacity: 0.7;\r\n transition: opacity 0.5s ease-out;\r\n}\r\n\r\n.transition-container {\r\n margin-top: 20px;\r\n width: 100%;\r\n}\r\n\r\n.progress-bar {\r\n width: 100%;\r\n height: 6px;\r\n background-color: #f0f0f0;\r\n border-radius: 3px;\r\n overflow: hidden;\r\n margin-bottom: 10px;\r\n}\r\n\r\n.progress-fill {\r\n height: 100%;\r\n background-color: #4285f4;\r\n transition: width 0.3s ease-out;\r\n}\r\n\r\n/* 淡出动画 */\r\n.upload-container.fade-out {\r\n opacity: 0;\r\n transform: translateY(-20px);\r\n transition: opacity 0.3s ease, transform 0.3s ease;\r\n}\r\n\r\n/* 淡入动画 */\r\n.chat-modal-container {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n/* 确保容器有相对定位,以便绝对定位的子元素能够正确定位 */\r\n.modal-container {\r\n position: relative;\r\n overflow: hidden;\r\n} ","/* 模态框基础样式 */\r\n.modal-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background-color: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n z-index: 1000;\r\n overflow-y: auto;\r\n padding: 20px;\r\n}\r\n\r\n/* 全屏模式下取消 padding */\r\n.fullscreen-overlay {\r\n padding: 0;\r\n background-color: rgba(0, 0, 0, 0.7);\r\n}\r\n\r\n.modal-container {\r\n background-color: #fff;\r\n border-radius: 8px;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n position: relative;\r\n margin: auto;\r\n transition: all 0.3s ease-out;\r\n overflow: hidden;\r\n}\r\n\r\n/* 全屏模式样式 */\r\n.modal-container.fullscreen {\r\n width: 100vw;\r\n max-width: none;\r\n height: 100%;\r\n border-radius: 0;\r\n margin: 0;\r\n display: flex;\r\n flex-direction: column;\r\n height: 100vh;\r\n max-height: 100vh;\r\n}\r\n\r\n/* PC端布局 */\r\n.pc-layout {\r\n width: 80%;\r\n max-width: 500px;\r\n min-width: 320px;\r\n}\r\n\r\n/* 移动端布局 */\r\n.mobile-layout {\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 0;\r\n}\r\n\r\n/* 响应式布局 */\r\n@media screen and (max-width: 768px) {\r\n .pc-layout {\r\n width: 95%;\r\n }\r\n\r\n .modal-overlay {\r\n padding: 0;\r\n }\r\n\r\n .modal-container.fullscreen {\r\n /* 支持 iOS Safari */\r\n height: -webkit-fill-available;\r\n max-height: -webkit-fill-available;\r\n /* 确保内容不会被顶部状态栏和底部工具栏遮挡 */\r\n padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom);\r\n margin-top: 40px;\r\n height: calc(100% - 40px);\r\n max-height: calc(100% - 40px);\r\n border-radius: 16px 16px 0 0;\r\n }\r\n\r\n .modal-container.mobile-layout {\r\n width: 100%;\r\n height: 100vh;\r\n max-height: 100vh;\r\n min-height: 100vh;\r\n border-radius: 0;\r\n margin: 0;\r\n display: flex;\r\n flex-direction: column;\r\n }\r\n}\r\n\r\n/* 模态框头部样式 */\r\n.modal-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 4px 16px;\r\n height: 50px;\r\n border-bottom: 1px solid #e8e8e8;\r\n flex-shrink: 0;\r\n}\r\n\r\n.header-left {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n}\r\n\r\n.header-icon {\r\n width: 24px;\r\n height: 24px;\r\n}\r\n\r\n.close-button {\r\n background: transparent;\r\n border: none;\r\n cursor: pointer;\r\n padding: 8px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 4px;\r\n}\r\n\r\n.close-button:hover {\r\n background-color: rgba(0, 0, 0, 0.04);\r\n}\r\n\r\n.close-button span {\r\n font-size: 24px;\r\n line-height: 1;\r\n color: #999;\r\n}\r\n\r\n.close-button:hover span {\r\n color: #666;\r\n}\r\n\r\n\r\n/* 文件上传区域通用样式 */\r\n.upload-area {\r\n border: 2px dashed #ddd;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n transition: all 0.3s ease;\r\n margin-bottom: 20px;\r\n width: 100%;\r\n}\r\n\r\n.upload-area:hover {\r\n border-color: #1890ff;\r\n background-color: rgba(24, 144, 255, 0.05);\r\n}\r\n\r\n\r\n.upload-placeholder {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n color: #666;\r\n}\r\n\r\n.upload-placeholder svg {\r\n color: #1890ff;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.upload-placeholder p {\r\n margin: 4px 0;\r\n}\r\n\r\n.upload-hint {\r\n font-size: 0.8rem;\r\n color: #999;\r\n margin-top: 0.5rem;\r\n}\r\n\r\n.file-info {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 8px;\r\n background: #f9f9f9;\r\n border: 1px solid #e8e8e8;\r\n border-radius: 4px;\r\n}\r\n\r\n.file-info span {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n max-width: calc(100% - 30px);\r\n}\r\n\r\n.remove-file {\r\n background: transparent;\r\n border: none;\r\n color: #999;\r\n cursor: pointer;\r\n padding: 4px 8px;\r\n font-size: 16px;\r\n line-height: 1;\r\n border-radius: 4px;\r\n transition: all 0.2s;\r\n}\r\n\r\n.remove-file:hover {\r\n background-color: #f0f0f0;\r\n color: #666;\r\n}\r\n\r\n.file-input {\r\n display: none;\r\n}\r\n\r\n\r\n\r\n/* 输入容器样式 */\r\n.input-container {\r\n padding: 20px;\r\n display: flex;\r\n flex-direction: column;\r\n height: calc(100% - 50px);\r\n /* 减去header高度 */\r\n overflow-y: auto;\r\n}\r\n\r\n.input-container h3 {\r\n margin-top: 0;\r\n margin-bottom: 20px;\r\n font-size: 18px;\r\n color: #333;\r\n text-align: center;\r\n}\r\n\r\n/* JD输入区域样式 */\r\n.jd-input-section {\r\n margin-bottom: 20px;\r\n}\r\n\r\n.jd-input-section label {\r\n display: block;\r\n margin-bottom: 8px;\r\n font-weight: 500;\r\n color: #333;\r\n}\r\n\r\n.job-description-textarea {\r\n width: 100%;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n resize: vertical;\r\n font-family: inherit;\r\n font-size: 14px;\r\n line-height: 1.5;\r\n transition: border-color 0.3s;\r\n}\r\n\r\n.job-description-textarea:focus {\r\n outline: none;\r\n border-color: #1890ff;\r\n box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);\r\n}\r\n\r\n/* 简历上传区域样式 */\r\n.resume-upload-section {\r\n margin-bottom: 20px;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n}\r\n\r\n.resume-upload-section label {\r\n display: block;\r\n margin-bottom: 8px;\r\n font-weight: 500;\r\n color: #333;\r\n align-self: flex-start;\r\n}\r\n\r\n\r\n/* 提交按钮通用样式 */\r\n.submit-button {\r\n margin-top: 10px;\r\n padding: 10px 30px;\r\n background: #1890ff;\r\n color: white;\r\n border: none;\r\n border-radius: 4px;\r\n font-size: 16px;\r\n cursor: pointer;\r\n transition: all 0.3s ease;\r\n width: 100%;\r\n max-width: 400px;\r\n align-self: center;\r\n /* 确保按钮居中 */\r\n}\r\n\r\n.submit-button:disabled {\r\n background: #ccc;\r\n cursor: not-allowed;\r\n}\r\n\r\n.submit-button:hover:not(:disabled) {\r\n background: #40a9ff;\r\n}\r\n\r\n/* 聊天模态框容器 */\r\n.chat-modal-container {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n/* AI免责声明和备案信息样式 */\r\n.ai-disclaimer {\r\n margin-top: 16px;\r\n text-align: center;\r\n font-size: 12px;\r\n color: #999;\r\n line-height: 1.5;\r\n}\r\n\r\n.ai-disclaimer p {\r\n margin: 4px 0;\r\n}\r\n\r\n.beian-info {\r\n display: flex;\r\n justify-content: center;\r\n flex-wrap: wrap;\r\n gap: 4px;\r\n}\r\n\r\n.ai-disclaimer a {\r\n color: #666;\r\n text-decoration: none;\r\n transition: color 0.2s ease;\r\n}\r\n\r\n.ai-disclaimer a:hover {\r\n color: #1890ff;\r\n text-decoration: underline;\r\n}","import { Component, Prop, h, State, Element, Event, EventEmitter, Watch } from '@stencil/core';\r\nimport { uploadFileToBackend, FileUploadResponse, sendHttpRequest } from '../../utils/utils';\r\n\r\n@Component({\r\n tag: 'pcm-mnms-modal',\r\n styleUrls: ['pcm-mnms-modal.css', '../../global/global.css'],\r\n shadow: true,\r\n})\r\nexport class MnmsModal {\r\n /**\r\n * 模态框标题\r\n */\r\n @Prop() modalTitle: string = '模拟面试';\r\n\r\n /**\r\n * SDK鉴权密钥\r\n */\r\n @Prop({ attribute: 'token' }) token: string = '';\r\n\r\n /**\r\n * 是否显示聊天模态框\r\n */\r\n @Prop({ mutable: true }) isOpen: boolean = false;\r\n\r\n /**\r\n * 当点击模态框关闭时触发\r\n */\r\n @Event() modalClosed: EventEmitter<void>;\r\n\r\n /**\r\n * 应用图标URL\r\n */\r\n @Prop() icon?: string;\r\n\r\n /**\r\n * 聊天框的页面层级\r\n */\r\n @Prop() zIndex?: number = 1000;\r\n\r\n /**\r\n * 是否展示顶部标题栏\r\n */\r\n @Prop() isShowHeader: boolean = true;\r\n\r\n /**\r\n * 是否展示右上角的关闭按钮\r\n */\r\n @Prop() isNeedClose: boolean = true;\r\n\r\n /**\r\n * 会话ID,传入继续对话,否则创建新会话\r\n */\r\n @Prop({ mutable: true }) conversationId?: string;\r\n\r\n /**\r\n * 默认查询文本\r\n */\r\n @Prop() defaultQuery: string = '';\r\n\r\n /**\r\n * 是否以全屏模式打开,移动端建议设置为true\r\n */\r\n @Prop() fullscreen: boolean = false;\r\n\r\n /**\r\n * 自定义输入参数,传入job_info时,会隐藏JD输入区域\r\n */\r\n @Prop() customInputs: { [key: string]: any } = {};\r\n\r\n /**\r\n * 上传成功事件\r\n */\r\n @Event() uploadSuccess: EventEmitter<FileUploadResponse>;\r\n\r\n /**\r\n * 流式输出完成事件\r\n */\r\n @Event() streamComplete: EventEmitter<{\r\n conversation_id: string;\r\n event: string;\r\n message_id: string;\r\n id: string;\r\n }>;\r\n\r\n /**\r\n * 新会话开始的回调,只会在一轮对话开始时触发一次\r\n */\r\n @Event() conversationStart: EventEmitter<{\r\n conversation_id: string;\r\n event: string;\r\n message_id: string;\r\n id: string;\r\n }>;\r\n\r\n /**\r\n * 当聊天完成时触发\r\n */\r\n @Event() interviewComplete: EventEmitter<{\r\n conversation_id: string;\r\n total_questions: number;\r\n }>;\r\n\r\n /**\r\n * SDK密钥验证失败事件\r\n */\r\n @Event() tokenInvalid: EventEmitter<void>;\r\n\r\n @State() selectedFile: File | null = null;\r\n @State() isUploading: boolean = false;\r\n @State() uploadedFileInfo: FileUploadResponse | null = null;\r\n @State() showChatModal: boolean = false;\r\n\r\n // 添加新的状态来控制过渡动画\r\n @State() isTransitioning: boolean = false;\r\n @State() transitionTimer: any = null;\r\n\r\n // 使用 @Element 装饰器获取组件的 host 元素\r\n @Element() hostElement: HTMLElement;\r\n\r\n @State() jobDescription: string = '';\r\n @State() isSubmitting: boolean = false;\r\n\r\n private handleClose = () => {\r\n this.isOpen = false;\r\n this.modalClosed.emit();\r\n };\r\n\r\n private handleFileChange = (event: Event) => {\r\n const input = event.target as HTMLInputElement;\r\n if (input.files && input.files.length > 0) {\r\n this.selectedFile = input.files[0];\r\n }\r\n };\r\n\r\n private handleUploadClick = () => {\r\n const fileInput = this.hostElement.shadowRoot?.querySelector('.file-input') as HTMLInputElement;\r\n fileInput?.click();\r\n };\r\n\r\n private clearSelectedFile = () => {\r\n this.selectedFile = null;\r\n this.uploadedFileInfo = null;\r\n const fileInput = this.hostElement.shadowRoot?.querySelector('.file-input') as HTMLInputElement;\r\n if (fileInput) {\r\n fileInput.value = '';\r\n }\r\n };\r\n\r\n private async uploadFile() {\r\n if (!this.selectedFile) return;\r\n\r\n this.isUploading = true;\r\n\r\n try {\r\n // 使用 uploadFileToBackend 工具函数上传文件\r\n const result = await uploadFileToBackend(this.selectedFile, {\r\n 'authorization': 'Bearer ' + this.token\r\n });\r\n\r\n this.uploadedFileInfo = result;\r\n this.uploadSuccess.emit(result);\r\n } catch (error) {\r\n console.error('文件上传错误:', error);\r\n this.clearSelectedFile();\r\n alert(error instanceof Error ? error.message : '文件上传失败,请重试');\r\n } finally {\r\n this.isUploading = false;\r\n }\r\n }\r\n\r\n private handleJobDescriptionChange = (event: Event) => {\r\n const textarea = event.target as HTMLTextAreaElement;\r\n this.jobDescription = textarea.value;\r\n };\r\n\r\n private handleStartInterview = async () => {\r\n if (!this.selectedFile) {\r\n alert('请上传简历');\r\n return;\r\n }\r\n\r\n // 如果没有预设的job_info,则需要检查用户输入\r\n if (!this.customInputs?.job_info && !this.jobDescription.trim()) {\r\n alert('请输入职位描述');\r\n return;\r\n }\r\n\r\n this.isSubmitting = true;\r\n\r\n try {\r\n // 如果还没上传,先上传文件\r\n if (!this.uploadedFileInfo) {\r\n await this.uploadFile();\r\n if (!this.uploadedFileInfo) {\r\n this.isSubmitting = false;\r\n return; // 上传失败\r\n }\r\n }\r\n\r\n // 使用预设的job_info或用户输入的jobDescription\r\n // const jobInfo = this.customInputs?.job_info || this.jobDescription;\r\n\r\n // console.log('传递的customInputs:', {\r\n // ...this.customInputs,\r\n // file_url: this.uploadedFileInfo.cos_key,\r\n // job_info: jobInfo\r\n // });\r\n\r\n // 直接显示聊天模态框\r\n this.showChatModal = true;\r\n } catch (error) {\r\n console.error('开始面试时出错:', error);\r\n alert('开始面试时出错,请重试');\r\n } finally {\r\n this.isSubmitting = false;\r\n }\r\n };\r\n\r\n @Watch('isOpen')\r\n handleIsOpenChange(newValue: boolean) {\r\n if (!newValue) {\r\n // 重置状态\r\n this.clearSelectedFile();\r\n this.showChatModal = false;\r\n this.jobDescription = '';\r\n\r\n // 清除可能存在的计时器\r\n if (this.transitionTimer) {\r\n clearTimeout(this.transitionTimer);\r\n this.transitionTimer = null;\r\n }\r\n } else {\r\n // 当模态框打开时,验证API密钥\r\n this.verifyApiKey();\r\n \r\n if (this.conversationId) {\r\n // 如果有会话ID,直接显示聊天模态框\r\n this.showChatModal = true;\r\n }\r\n }\r\n }\r\n\r\n componentWillLoad() {\r\n // 检查 customInputs 中是否有 job_info\r\n if (this.customInputs && this.customInputs.job_info) {\r\n this.jobDescription = this.customInputs.job_info;\r\n }\r\n }\r\n\r\n // 处理流式输出完成事件\r\n private handleStreamComplete = (event: CustomEvent) => {\r\n // 将事件转发出去\r\n this.streamComplete.emit(event.detail);\r\n };\r\n\r\n // 处理会话开始事件\r\n private handleConversationStart = (event: CustomEvent) => {\r\n this.conversationStart.emit(event.detail);\r\n };\r\n\r\n // 处理面试完成事件\r\n private handleInterviewComplete = (event: CustomEvent) => {\r\n this.interviewComplete.emit(event.detail);\r\n };\r\n\r\n /**\r\n * 验证API密钥\r\n */\r\n private async verifyApiKey() {\r\n if (!this.token) {\r\n this.tokenInvalid.emit();\r\n return;\r\n }\r\n \r\n try {\r\n const response = await sendHttpRequest({\r\n url: '/sdk/v1/user',\r\n method: 'GET',\r\n headers: {\r\n 'Authorization': `Bearer ${this.token}`\r\n }\r\n });\r\n\r\n if (!response.success) {\r\n throw new Error(response.message || 'API密钥验证失败');\r\n }\r\n \r\n // 验证成功,继续正常流程\r\n } catch (error) {\r\n console.error('API密钥验证错误:', error);\r\n // 通知父组件API密钥无效\r\n this.tokenInvalid.emit();\r\n }\r\n }\r\n\r\n render() {\r\n if (!this.isOpen) return null;\r\n\r\n const modalStyle = {\r\n zIndex: String(this.zIndex)\r\n };\r\n\r\n console.log('showChatModal:', this.showChatModal);\r\n\r\n const containerClass = {\r\n 'modal-container': true,\r\n 'fullscreen': this.fullscreen,\r\n 'pc-layout': true,\r\n };\r\n\r\n const overlayClass = {\r\n 'modal-overlay': true,\r\n 'fullscreen-overlay': this.fullscreen\r\n };\r\n\r\n // 检查是否有会话ID,如果有则直接显示聊天模态框\r\n if (this.conversationId && !this.showChatModal) {\r\n this.showChatModal = true;\r\n }\r\n\r\n // 修正这里的逻辑,确保当 customInputs.job_info 存在时,hideJdInput 为 true\r\n const hideJdInput = Boolean(this.customInputs && this.customInputs.job_info);\r\n\r\n return (\r\n <div class={overlayClass} style={modalStyle}>\r\n <div class={containerClass}>\r\n {this.isShowHeader && (\r\n <div class=\"modal-header\">\r\n <div class=\"header-left\">\r\n {this.icon && <img src={this.icon} class=\"header-icon\" alt=\"应用图标\" />}\r\n <div>{this.modalTitle}</div>\r\n </div>\r\n {this.isNeedClose && (\r\n <button class=\"close-button\" onClick={this.handleClose}>\r\n <span>×</span>\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* 上传界面 - 仅在不显示聊天模态框且没有会话ID时显示 */}\r\n {!this.showChatModal && !this.conversationId && (\r\n <div class=\"input-container\">\r\n {/* JD输入区域 - 仅在没有customInputs.job_info时显示 */}\r\n {!hideJdInput && (\r\n <div class=\"jd-input-section\">\r\n <label htmlFor=\"job-description\">请输入职位描述 (JD)</label>\r\n <textarea \r\n id=\"job-description\"\r\n class=\"job-description-textarea\"\r\n placeholder=\"请输入职位描述,包括职责、要求等信息...\"\r\n rows={6}\r\n value={this.jobDescription}\r\n onInput={this.handleJobDescriptionChange}\r\n ></textarea>\r\n </div>\r\n )}\r\n \r\n {/* 简历上传区域 */}\r\n <div class=\"resume-upload-section\">\r\n <label>上传简历</label>\r\n <div class=\"upload-area\" onClick={this.handleUploadClick}>\r\n {this.selectedFile ? (\r\n <div class=\"file-info\">\r\n <span>{this.selectedFile.name}</span>\r\n <button class=\"remove-file\" onClick={(e) => {\r\n e.stopPropagation();\r\n this.clearSelectedFile();\r\n }}>×</button>\r\n </div>\r\n ) : (\r\n <div class=\"upload-placeholder\">\r\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" width=\"48\" height=\"48\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m0-16l-4 4m4-4l4 4\" />\r\n </svg>\r\n <p>点击上传简历</p>\r\n <p class=\"upload-hint\">支持 txt、markdown、pdf、docx、doc、md 格式</p>\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n\r\n <button\r\n class=\"submit-button\"\r\n disabled={!this.selectedFile || (!hideJdInput && !this.jobDescription.trim()) || this.isUploading || this.isSubmitting}\r\n onClick={this.handleStartInterview}\r\n >\r\n {this.isUploading ? '上传中...' : this.isSubmitting ? '处理中...' : '开始分析'}\r\n </button>\r\n\r\n <div class=\"ai-disclaimer\">\r\n <p>所有内容均由AI生成仅供参考</p>\r\n <p class=\"beian-info\">\r\n <span>中央网信办生成式人工智能服务备案号</span>:\r\n <a href=\"https://www.pincaimao.com\" target=\"_blank\" rel=\"noopener noreferrer\">Hunan-PinCaiMao-202412310003</a>\r\n </p>\r\n </div>\r\n\r\n <input\r\n type=\"file\"\r\n class=\"file-input\"\r\n onChange={this.handleFileChange}\r\n />\r\n </div>\r\n )}\r\n\r\n {/* 聊天界面 - 在显示聊天模态框时显示 */}\r\n {this.showChatModal && (\r\n <div class=\"chat-modal-container\">\r\n <pcm-app-chat-modal\r\n isOpen={true}\r\n modalTitle={this.modalTitle}\r\n icon={this.icon}\r\n token={this.token}\r\n isShowHeader={this.isShowHeader} // 不显示内部的标题栏,因为外部已有\r\n isNeedClose={this.isShowHeader} // 不显示内部的关闭按钮,因为外部已有\r\n zIndex={this.zIndex}\r\n fullscreen={this.fullscreen}\r\n botId=\"3022316191018884\"\r\n conversationId={this.conversationId}\r\n defaultQuery={this.defaultQuery}\r\n enableVoice={false}\r\n customInputs={this.conversationId ? undefined : {\r\n ...this.customInputs,\r\n file_url: this.uploadedFileInfo?.cos_key,\r\n job_info: this.customInputs?.job_info || this.jobDescription\r\n }}\r\n interviewMode=\"text\"\r\n onModalClosed={this.handleClose}\r\n onStreamComplete={this.handleStreamComplete}\r\n onConversationStart={this.handleConversationStart}\r\n onInterviewComplete={this.handleInterviewComplete}\r\n ></pcm-app-chat-modal>\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n }\r\n} ",":host {\r\n display: block;\r\n }\r\n \r\n .modal-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background-color: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n overflow-y: auto;\r\n padding: 20px;\r\n z-index: 1000;\r\n }\r\n \r\n /* 全屏模式下取消 padding */\r\n .fullscreen-overlay {\r\n padding: 0;\r\n }\r\n \r\n .modal-container {\r\n background: white;\r\n border-radius: 8px;\r\n width: 100%;\r\n max-width: 800px;\r\n display: flex;\r\n flex-direction: column;\r\n position: relative;\r\n margin: auto;\r\n }\r\n \r\n /* 全屏模式样式 */\r\n .modal-container.fullscreen {\r\n width: 100vw;\r\n max-width: none;\r\n height: 100%;\r\n border-radius: 0;\r\n margin: 0;\r\n display: flex;\r\n flex-direction: column;\r\n height: 100vh;\r\n max-height: 100vh;\r\n }\r\n \r\n /* 确保内容区域也使用 flex 布局并占满剩余空间 */\r\n .modal-container.fullscreen > div:not(.modal-header):not(.initial-upload) {\r\n display: flex;\r\n flex-direction: column;\r\n flex: 1;\r\n overflow: hidden; /* 防止内容溢出 */\r\n height: 100%;\r\n }\r\n \r\n /* PC端布局 */\r\n .pc-layout {\r\n width: 80%;\r\n max-width: 800px;\r\n /* height: 80vh; */\r\n /* max-height: 700px; */\r\n min-width: 320px;\r\n min-height: 400px;\r\n }\r\n \r\n /* 移动端布局 */\r\n .mobile-layout {\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 0;\r\n }\r\n \r\n /* 响应式布局 */\r\n @media screen and (max-width: 768px) {\r\n .pc-layout {\r\n width: 95%;\r\n /* height: 90vh; */\r\n }\r\n \r\n .modal-overlay {\r\n padding: 0;\r\n }\r\n \r\n .modal-container.fullscreen {\r\n /* 支持 iOS Safari */\r\n height: -webkit-fill-available;\r\n max-height: -webkit-fill-available;\r\n /* 确保内容不会被顶部状态栏和底部工具栏遮挡 */\r\n padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom);\r\n }\r\n \r\n \r\n .modal-container.mobile-layout {\r\n width: 100%;\r\n height: 100vh;\r\n max-height: 100vh;\r\n min-height: 100vh;\r\n border-radius: 0;\r\n margin: 0;\r\n display: flex;\r\n flex-direction: column;\r\n }\r\n \r\n }\r\n .video-preview.placeholder {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n background: #EAEAEA;\r\n }\r\n \r\n .placeholder-status {\r\n color: #00000066;\r\n }\r\n \r\n .waiting-message p {\r\n margin: 0;\r\n font-size: 16px;\r\n color: white;\r\n font-weight: 500;\r\n }\r\n \r\n .recording-container {\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n }\r\n \r\n \r\n \r\n .video-area {\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n }\r\n \r\n .stop-recording-button {\r\n width: 100%;\r\n height: 100%;\r\n font-size: 16px;\r\n background: #f44336;\r\n border-radius: 6px;\r\n color: white;\r\n border: none;\r\n cursor: pointer;\r\n }\r\n \r\n .stop-recording-button:hover {\r\n background: #d32f2f;\r\n }\r\n \r\n .play-audio-container {\r\n width: 100%;\r\n height: 100%;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n }\r\n \r\n .modal-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 4px 16px;\r\n height: 50px;\r\n border-bottom: 1px solid #e8e8e8;\r\n flex-shrink: 0; /* 防止头部被压缩 */\r\n }\r\n \r\n .header-left {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n }\r\n \r\n .header-icon {\r\n width: 24px;\r\n height: 24px;\r\n }\r\n \r\n .close-button {\r\n background: transparent;\r\n border: none;\r\n cursor: pointer;\r\n padding: 8px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 4px;\r\n }\r\n \r\n .close-button:hover {\r\n background-color: rgba(0, 0, 0, 0.04);\r\n }\r\n \r\n .close-button span {\r\n font-size: 24px;\r\n line-height: 1;\r\n color: #999;\r\n }\r\n \r\n .close-button:hover span {\r\n color: #666;\r\n }\r\n \r\n .chat-history {\r\n position: relative;\r\n flex: 1;\r\n overflow-y: auto;\r\n padding: 20px;\r\n scroll-behavior: smooth;\r\n min-height: 200px;\r\n background: url(https://pcm-resource-1312611446.cos.ap-guangzhou.myqcloud.com/web/sdk/chat_bg.png);\r\n background-size: 100%;\r\n }\r\n \r\n /* 添加全屏模式下的样式 */\r\n .fullscreen .chat-history {\r\n height: auto;\r\n flex: 1 1 auto;\r\n }\r\n \r\n \r\n .message-input {\r\n padding: 16px;\r\n border-top: 1px solid #eee;\r\n display: flex;\r\n gap: 8px;\r\n align-items: center;\r\n }\r\n \r\n .message-input input {\r\n flex: 1;\r\n padding: 8px 12px;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n outline: none;\r\n transition: border-color 0.2s ease;\r\n }\r\n \r\n .message-input input:focus {\r\n border-color: #bbb;\r\n }\r\n \r\n /* 消息样式 */\r\n .message {\r\n margin-bottom: 16px;\r\n opacity: 1;\r\n transition: opacity 0.3s ease;\r\n }\r\n \r\n .message-content {\r\n max-width: 70%;\r\n padding: 8px 12px;\r\n border-radius: 8px;\r\n word-break: break-word;\r\n }\r\n \r\n .message-content p {\r\n margin: 0;\r\n word-break: break-word;\r\n }\r\n \r\n .user-message {\r\n display: flex;\r\n justify-content: flex-end;\r\n }\r\n \r\n .agent-message {\r\n display: flex;\r\n justify-content: flex-start;\r\n }\r\n \r\n .user-message .message-content {\r\n background-color: #007bff;\r\n color: white;\r\n }\r\n \r\n .agent-message .message-content {\r\n background-color: #f1f1f1;\r\n }\r\n \r\n .message-time {\r\n font-size: 12px;\r\n color: #999;\r\n margin-top: 4px;\r\n display: block;\r\n }\r\n \r\n .send-button {\r\n background-color: #1890ff;\r\n color: white;\r\n border: none;\r\n border-radius: 4px;\r\n padding: 8px 16px;\r\n cursor: pointer;\r\n font-weight: 500;\r\n }\r\n \r\n .send-button:disabled {\r\n background-color: #ccc;\r\n cursor: not-allowed;\r\n }\r\n \r\n .empty-state {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n height: 100%;\r\n color: #999;\r\n text-align: center;\r\n }\r\n \r\n .loading-container {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n display: flex;\r\n flex-direction: column;\r\n justify-content: center;\r\n align-items: center;\r\n background-color: rgba(255, 255, 255, 0.98);\r\n z-index: 1;\r\n opacity: 1;\r\n transition: opacity 0.3s ease;\r\n }\r\n \r\n .loading-container p {\r\n margin-top: 16px;\r\n color: #666;\r\n font-size: 14px;\r\n }\r\n \r\n .loading-spinner {\r\n width: 40px;\r\n height: 40px;\r\n border: 3px solid #f3f3f3;\r\n border-top: 3px solid #1890ff;\r\n border-radius: 50%;\r\n animation: spin 1s linear infinite;\r\n }\r\n \r\n @keyframes spin {\r\n 0% {\r\n transform: rotate(0deg);\r\n }\r\n \r\n 100% {\r\n transform: rotate(360deg);\r\n }\r\n }\r\n \r\n /* 修改 messages-wrapper 的样式 */\r\n .messages-wrapper {\r\n width: 100%;\r\n min-height: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n /* 当内容少时,将内容放在底部 */\r\n justify-content: flex-end;\r\n }\r\n \r\n /* 当有很多消息时,取消固定在底部 */\r\n .messages-wrapper.has-overflow {\r\n justify-content: flex-start;\r\n }\r\n \r\n .suggested-questions {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n padding: 16px;\r\n }\r\n \r\n .suggested-question {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 8px 12px;\r\n background-color: #f3f4f6;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n font-size: 14px;\r\n color: #374151;\r\n transition: background-color 0.2s;\r\n }\r\n \r\n .suggested-question:hover {\r\n background-color: #e5e7eb;\r\n }\r\n \r\n .arrow-right {\r\n margin-left: 8px;\r\n }\r\n \r\n .loading-suggestions {\r\n display: flex;\r\n justify-content: center;\r\n padding: 16px;\r\n }\r\n \r\n .loading-spinner-small {\r\n width: 20px;\r\n height: 20px;\r\n border: 2px solid #e5e7eb;\r\n border-top-color: #6b7280;\r\n border-radius: 50%;\r\n animation: spin 1s linear infinite;\r\n }\r\n \r\n /* 添加上传按钮样式 */\r\n .upload-button {\r\n background: transparent;\r\n border: none;\r\n cursor: pointer;\r\n padding: 8px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n color: #666;\r\n border-radius: 4px;\r\n transition: background-color 0.2s;\r\n }\r\n \r\n .upload-button:hover {\r\n background-color: rgba(0, 0, 0, 0.04);\r\n }\r\n \r\n .upload-button svg {\r\n width: 20px;\r\n height: 20px;\r\n }\r\n \r\n /* 添加文件名显示区域样式 */\r\n .selected-file {\r\n font-size: 12px;\r\n color: #666;\r\n margin-left: 8px;\r\n max-width: 150px;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n }\r\n \r\n .input-wrapper {\r\n flex: 1;\r\n display: flex;\r\n align-items: center;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n padding: 0 4px;\r\n background: white;\r\n }\r\n \r\n .input-wrapper input {\r\n border: none;\r\n flex: 1;\r\n padding: 8px;\r\n outline: none;\r\n }\r\n \r\n .input-wrapper:focus-within {\r\n border-color: #bbb;\r\n }\r\n \r\n /* 文件预览区域样式 */\r\n .file-preview {\r\n padding: 8px 16px;\r\n border-top: 1px solid #eee;\r\n background-color: #f9f9f9;\r\n }\r\n \r\n .function-select {\r\n margin-top: 2rem;\r\n }\r\n \r\n .function-options {\r\n display: flex;\r\n flex-wrap: wrap;\r\n gap: 1rem;\r\n justify-content: center;\r\n margin-top: 1rem;\r\n }\r\n \r\n .function-button {\r\n padding: 0.5rem 1rem;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n background: white;\r\n cursor: pointer;\r\n transition: all 0.3s ease;\r\n }\r\n \r\n .function-button:hover {\r\n border-color: #1890ff;\r\n color: #1890ff;\r\n }\r\n \r\n .function-button.selected {\r\n background: #1890ff;\r\n color: white;\r\n border-color: #1890ff;\r\n }\r\n \r\n .recording-section {\r\n border-top: 1px solid #eee;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n padding: 20px;\r\n border-radius: 14px 14px 0 0;\r\n flex: 0 0 auto;\r\n }\r\n \r\n .recording-section .video-preview {\r\n width: 100%;\r\n height: 200px;\r\n max-width: 400px;\r\n position: relative;\r\n margin-bottom: 10px;\r\n border: 1px solid #ddd;\r\n border-radius: 12px;\r\n overflow: hidden;\r\n }\r\n \r\n .recording-section video {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n }\r\n \r\n /* 修改 recording-status 样式 */\r\n .recording-status {\r\n position: absolute;\r\n top: 10px;\r\n left: 10px;\r\n background-color: rgba(0, 0, 0, 0.6);\r\n color: white;\r\n padding: 4px 8px;\r\n border-radius: 4px;\r\n display: flex;\r\n align-items: center;\r\n gap: 5px;\r\n font-size: 0.8rem;\r\n z-index: 2;\r\n }\r\n \r\n .recording-status .recording-dot {\r\n display: inline-block;\r\n width: 10px;\r\n height: 10px;\r\n background-color: red;\r\n border-radius: 50%;\r\n margin-right: 5px;\r\n animation: blink 1s infinite;\r\n }\r\n \r\n .recording-status.warning {\r\n color: #ff4d4f;\r\n animation: blink 1s infinite;\r\n }\r\n \r\n @keyframes blink {\r\n 0% {\r\n opacity: 1;\r\n }\r\n \r\n 50% {\r\n opacity: 0.5;\r\n }\r\n \r\n 100% {\r\n opacity: 1;\r\n }\r\n }\r\n \r\n .recording-section .stop-recording-button {\r\n background-color: #f44336;\r\n color: white;\r\n border: none;\r\n cursor: pointer;\r\n font-weight: bold;\r\n }\r\n \r\n .recording-section .stop-recording-button:hover {\r\n background-color: #d32f2f;\r\n }\r\n \r\n .fullscreen {\r\n width: 100vw;\r\n border-radius: 0;\r\n height: 100vh;\r\n display: flex;\r\n flex-direction: column;\r\n overflow-y: auto;\r\n }\r\n \r\n .recording-controls {\r\n margin-top: 10px;\r\n height: 53px;\r\n width: 100%;\r\n max-width: 400px;\r\n display: flex;\r\n justify-content: center;\r\n }\r\n \r\n .recording-controls .waiting-message {\r\n text-align: center;\r\n color: white;\r\n font-size: 1rem;\r\n background-image: linear-gradient(100deg, #4A9FFF 0%, #1058FF 100%);\r\n border-radius: 6px;\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\r\n width: 95%;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n cursor: pointer;\r\n }\r\n \r\n .recording-controls .waiting-message.loading {\r\n background: #faad14;\r\n }\r\n \r\n .recording-controls .waiting-message p {\r\n margin: 0;\r\n font-size: 16px;\r\n color: white;\r\n font-weight: 500;\r\n }\r\n \r\n .recording-controls .stop-recording-button {\r\n background-color: #dc3545;\r\n color: white;\r\n border: none;\r\n cursor: pointer;\r\n font-size: 1rem;\r\n }\r\n \r\n .recording-controls .stop-recording-button:hover {\r\n background-color: #c82333;\r\n }\r\n \r\n /* 添加禁用状态的样式 */\r\n .recording-controls .stop-recording-button.disabled {\r\n background: #ccc;\r\n cursor: not-allowed;\r\n }\r\n \r\n .recording-controls .stop-recording-button.disabled:hover {\r\n background: #ccc;\r\n }\r\n \r\n /* 添加进度条和数字进度的样式 */\r\n .progress-container {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n width: 100%;\r\n max-width: 400px;\r\n margin-top: 10px;\r\n padding: 0 5px;\r\n }\r\n \r\n .progress-bar-container {\r\n height: 4px;\r\n background-color: #E5E5E5;\r\n border-radius: 2px;\r\n overflow: hidden;\r\n margin-right: 10px;\r\n width: 75px;\r\n }\r\n \r\n .progress-bar {\r\n height: 100%;\r\n background-image: linear-gradient(111deg, #4A9FFF 0%, #1058FF 100%);\r\n border-radius: 2px;\r\n transition: width 0.3s ease;\r\n }\r\n \r\n .progress-text {\r\n font-size: 14px;\r\n color: #666;\r\n white-space: nowrap;\r\n }","import { Component, Prop, h, State, Event, EventEmitter, Element, Watch } from '@stencil/core';\r\nimport { convertWorkflowStreamNodeToMessageRound, UserInputMessageType, sendSSERequest, sendHttpRequest, uploadFileToBackend, API_DOMAIN } from '../../utils/utils';\r\nimport { ChatMessage } from '../../interfaces/chat';\r\n\r\n@Component({\r\n tag: 'pcm-video-chat-modal',\r\n styleUrl: 'pcm-video-chat-modal.css',\r\n shadow: true,\r\n})\r\nexport class VideoChatModal {\r\n /**\r\n * 模态框标题\r\n */\r\n @Prop() modalTitle: string = '在线客服';\r\n\r\n /**\r\n * SDK鉴权密钥\r\n */\r\n @Prop({ attribute: 'token' }) token: string = '';\r\n\r\n /**\r\n * 是否显示聊天模态框\r\n */\r\n @Prop({ mutable: true }) isOpen: boolean = false;\r\n\r\n /**\r\n * 聊天消息历史\r\n */\r\n @State() messages: ChatMessage[] = [];\r\n\r\n\r\n /**\r\n * 当点击模态框关闭时触发\r\n */\r\n @Event() modalClosed: EventEmitter<void>;\r\n\r\n /**\r\n * 应用图标URL\r\n */\r\n @Prop() icon?: string;\r\n\r\n /**\r\n * 聊天框的页面层级\r\n */\r\n @Prop() zIndex?: number = 1000;\r\n\r\n /**\r\n * 是否展示顶部标题栏\r\n */\r\n @Prop() isShowHeader: boolean = true;\r\n\r\n /**\r\n * 是否展示右上角的关闭按钮\r\n */\r\n @Prop() isNeedClose: boolean = true;\r\n\r\n\r\n /**\r\n * 会话ID,传入继续对话,否则创建新会话\r\n */\r\n @Prop({ mutable: true }) conversationId?: string;\r\n\r\n /**\r\n * 当前助手回复的消息\r\n */\r\n @State() currentAssistantMessage: string = '';\r\n\r\n /**\r\n * 是否正在加载回复\r\n */\r\n @State() isLoading: boolean = false;\r\n\r\n /**\r\n * 当前正在流式输出的消息\r\n */\r\n @State() currentStreamingMessage: ChatMessage | null = null;\r\n\r\n // 添加新的状态控制\r\n @State() shouldAutoScroll: boolean = true;\r\n\r\n @State() isLoadingHistory: boolean = false;\r\n\r\n // 使用 @Element 装饰器获取组件的 host 元素\r\n @Element() hostElement: HTMLElement;\r\n\r\n // 添加新的 Event\r\n @Event() streamComplete: EventEmitter<{\r\n conversation_id: string;\r\n event: string;\r\n message_id: string;\r\n id: string;\r\n }>;\r\n\r\n /**\r\n * 首次对话提问文本\r\n */\r\n @Prop() defaultQuery: string = '';\r\n\r\n // 添加新的状态\r\n @State() showInitialUpload: boolean = true;\r\n\r\n // 添加视频录制相关状态\r\n @State() isRecording: boolean = false;\r\n @State() recordingStream: MediaStream | null = null;\r\n @State() recordedBlob: Blob | null = null;\r\n @State() mediaRecorder: MediaRecorder | null = null;\r\n @State() recordingTimeLeft: number = 0;\r\n @State() showRecordingUI: boolean = false;\r\n @State() recordingTimer: any = null;\r\n @State() recordingStartTime: number = 0;\r\n @State() recordingMaxTime: number = 120; // 最大录制时间(秒)\r\n @State() waitingToRecord: boolean = false;\r\n @State() waitingTimer: any = null;\r\n @State() waitingTimeLeft: number = 10; // 等待时间(秒)\r\n\r\n // 添加一个新的私有属性来存储视频元素的引用\r\n private videoRef: HTMLVideoElement | null = null;\r\n\r\n /**\r\n * 总题目数量\r\n */\r\n @Prop() totalQuestions: number = 2;\r\n\r\n /**\r\n * 当前题目序号\r\n */\r\n @State() currentQuestionNumber: number = 0;\r\n\r\n /**\r\n * 面试是否已完成\r\n */\r\n @State() isInterviewComplete: boolean = false;\r\n\r\n /**\r\n * 当面试完成时触发\r\n */\r\n @Event() interviewComplete: EventEmitter<{\r\n conversation_id: string;\r\n total_questions: number;\r\n }>;\r\n\r\n private readonly SCROLL_THRESHOLD = 30;\r\n\r\n /**\r\n * 视频录制最大时长(秒)\r\n */\r\n @Prop() maxRecordingTime: number = 120;\r\n\r\n /**\r\n * 录制倒计时提醒时间(秒)\r\n * 当剩余时间小于此值时,显示倒计时警告\r\n */\r\n @Prop() countdownWarningTime: number = 30;\r\n\r\n @State() showCountdownWarning: boolean = false;\r\n\r\n\r\n /**\r\n * 是否以全屏模式打开,移动端建议设置为true\r\n */\r\n @Prop() fullscreen: boolean = false;\r\n\r\n // 添加新的状态来跟踪视频上传\r\n @State() isUploadingVideo: boolean = false;\r\n\r\n // 添加新的状态和属性\r\n @State() isPlayingAudio: boolean = false;\r\n @State() audioUrl: string | null = null;\r\n private audioElement: HTMLAudioElement | null = null;\r\n\r\n /**\r\n * 录制错误事件\r\n */\r\n @Event() recordingError: EventEmitter<{\r\n type: string;\r\n message: string;\r\n details?: any;\r\n }>;\r\n\r\n /**\r\n * 录制状态变化事件\r\n */\r\n @Event() recordingStatusChange: EventEmitter<{\r\n status: 'started' | 'stopped' | 'paused' | 'resumed' | 'failed';\r\n details?: any;\r\n }>;\r\n\r\n /**\r\n * 是否自动播放语音问题\r\n */\r\n @Prop() enableVoice: boolean = true;\r\n\r\n /**\r\n * 是否显示题干内容\r\n * 1: 显示题干内容\r\n * 0: 不显示题干内容\r\n */\r\n @Prop() displayContentStatus: string = \"1\";\r\n\r\n\r\n /**\r\n * 父组件传入的 简历id\r\n */\r\n @Prop() resumeId?: string;\r\n\r\n private handleClose = () => {\r\n this.stopRecording();\r\n this.modalClosed.emit();\r\n };\r\n\r\n private async sendMessageToAPI(message: string, videoUrl?: string) {\r\n this.isLoading = true;\r\n let answer = '';\r\n let llmText = '';\r\n\r\n const now = new Date();\r\n const time = `${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`;\r\n\r\n // 如果消息为空但有文件,使用默认文本\r\n const queryText = message.trim();\r\n\r\n // 检查是否是最后一题的\"下一题\"请求\r\n const isLastQuestion = this.currentQuestionNumber >= this.totalQuestions;\r\n\r\n // 创建新的消息对象\r\n const newMessage: ChatMessage = {\r\n id: `temp-${Date.now()}`,\r\n time: time,\r\n query: queryText,\r\n answer: '',\r\n isStreaming: true,\r\n conversation_id: this.conversationId,\r\n inputs: {},\r\n status: \"normal\",\r\n error: null\r\n };\r\n\r\n // 设置当前流式消息\r\n this.currentStreamingMessage = newMessage;\r\n\r\n this.shouldAutoScroll = true;\r\n // 滚动到底部\r\n this.scrollToBottom();\r\n\r\n // 如果是最后一题,直接显示结束消息并完成面试\r\n if (isLastQuestion) {\r\n this.messages = [...this.messages, newMessage];\r\n this.currentStreamingMessage = null;\r\n this.isLoading = false;\r\n this.isInterviewComplete = true;\r\n await this.completeInterview(queryText, videoUrl);\r\n this.interviewComplete.emit({\r\n conversation_id: this.conversationId,\r\n total_questions: this.totalQuestions\r\n });\r\n this.currentQuestionNumber++;\r\n return;\r\n }\r\n\r\n // 准备请求数据\r\n const requestData: any = {\r\n response_mode: 'streaming',\r\n conversation_id: this.conversationId,\r\n query: queryText,\r\n bot_id: \"3022316191018903\",\r\n inputs: {\r\n id: this.resumeId\r\n }\r\n };\r\n\r\n // 如果有视频URL,添加到inputs中\r\n if (videoUrl) {\r\n requestData.inputs.video_url = videoUrl;\r\n }\r\n\r\n await sendSSERequest({\r\n url: `/sdk/v1/chat/chat-messages`,\r\n method: 'POST',\r\n headers: {\r\n 'authorization': 'Bearer ' + this.token\r\n },\r\n data: requestData,\r\n onMessage: (data) => {\r\n console.log('收到Stream数据:', data);\r\n\r\n if (data.conversation_id && !this.conversationId) {\r\n this.conversationId = data.conversation_id;\r\n this.updateUrlWithConversationId(data.conversation_id);\r\n }\r\n\r\n // 检查是否有 node_finished 事件和 LLMText\r\n if (data.event === 'node_finished' && data.data.inputs && data.data.inputs.LLMText) {\r\n llmText = data.data.inputs.LLMText;\r\n console.log('获取到 LLMText:', llmText);\r\n }\r\n\r\n if (data.event === 'message') {\r\n const inputMessage: UserInputMessageType = { message: message };\r\n convertWorkflowStreamNodeToMessageRound('message', inputMessage, data);\r\n\r\n if (data.event === 'agent_message' || data.event === 'message') {\r\n if (data.answer) {\r\n answer += data.answer;\r\n const updatedMessage: ChatMessage = {\r\n ...this.currentStreamingMessage,\r\n answer,\r\n isStreaming: true\r\n };\r\n this.currentStreamingMessage = updatedMessage;\r\n this.scrollToBottom();\r\n }\r\n }\r\n }\r\n if (data.event === \"message_end\") {\r\n this.streamComplete.emit({\r\n conversation_id: data.conversation_id || '',\r\n event: data.event,\r\n message_id: data.message_id,\r\n id: data.id,\r\n });\r\n }\r\n },\r\n onError: (error) => {\r\n console.error('发生错误:', error);\r\n alert(error instanceof Error ? error.message : '消息发送失败,请稍后再试');\r\n this.messages = [...this.messages, {\r\n ...newMessage,\r\n answer: '抱歉,发生了错误,请稍后再试。',\r\n error: error,\r\n isStreaming: false\r\n }];\r\n this.currentStreamingMessage = null;\r\n this.isLoading = false;\r\n },\r\n onComplete: async () => {\r\n console.log('请求完成');\r\n this.isLoading = false;\r\n\r\n // 获取最新的AI回复内容\r\n const latestAIMessage = this.currentStreamingMessage;\r\n\r\n // 更新消息列表\r\n this.messages = [...this.messages, this.currentStreamingMessage];\r\n this.currentStreamingMessage = null;\r\n\r\n // 增加题目计数\r\n this.currentQuestionNumber++;\r\n\r\n if (latestAIMessage && latestAIMessage.answer) {\r\n // 优先使用 LLMText,如果没有则使用 answer\r\n const textForSynthesis = llmText || latestAIMessage.answer;\r\n\r\n if (textForSynthesis) {\r\n // 合成语音\r\n const audioUrl = await this.synthesizeAudio(textForSynthesis);\r\n\r\n if (this.enableVoice) {\r\n // 自动播放语音\r\n await this.playAudio(audioUrl);\r\n // 自动播放模式下,播放完成后立即开始等待录制\r\n this.startWaitingToRecord();\r\n } else {\r\n // 只保存音频URL,不自动播放\r\n this.audioUrl = audioUrl;\r\n // 非自动播放模式下,不立即开始等待录制\r\n }\r\n }\r\n }\r\n }\r\n });\r\n }\r\n\r\n // 监听滚动事件,用于控制聊天历史记录的自动滚动行为。\r\n private handleScroll = () => {\r\n const chatHistory = this.hostElement.shadowRoot?.querySelector('.chat-history');\r\n if (!chatHistory) return;\r\n\r\n const { scrollTop, scrollHeight, clientHeight } = chatHistory;\r\n const distanceFromBottom = scrollHeight - scrollTop - clientHeight;\r\n\r\n // 更新是否应该自动滚动的状态\r\n this.shouldAutoScroll = distanceFromBottom <= this.SCROLL_THRESHOLD;\r\n };\r\n\r\n private scrollToBottom() {\r\n if (!this.shouldAutoScroll) return;\r\n const chatHistory = this.hostElement.shadowRoot?.querySelector('.chat-history');\r\n if (chatHistory && this.isOpen) {\r\n // 强制浏览器重新计算布局\r\n chatHistory.scrollTop = chatHistory.scrollHeight;\r\n }\r\n }\r\n\r\n // 添加 componentDidRender 生命周期方法,用于在组件渲染后滚动到底部\r\n componentDidRender() {\r\n if (this.isLoadingHistory || (this.shouldAutoScroll && this.isOpen)) {\r\n const chatHistory = this.hostElement.shadowRoot?.querySelector('.chat-history');\r\n if (chatHistory) {\r\n chatHistory.scrollTop = chatHistory.scrollHeight;\r\n }\r\n }\r\n }\r\n\r\n private updateUrlWithConversationId(conversationId: string) {\r\n const urlParams = new URLSearchParams(window.location.search);\r\n if (!urlParams.get('conversation_id')) {\r\n const newUrl = new URL(window.location.href);\r\n newUrl.searchParams.set('conversation_id', conversationId);\r\n window.history.replaceState({}, '', newUrl);\r\n }\r\n }\r\n\r\n // 修改 loadHistoryMessages 方法\r\n private async loadHistoryMessages() {\r\n if (!this.conversationId) return;\r\n\r\n this.isLoadingHistory = true;\r\n console.log('加载历史消息...');\r\n\r\n try {\r\n const result = await sendHttpRequest({\r\n url: '/sdk/v1/chat/messages',\r\n method: 'GET',\r\n headers: {\r\n 'authorization': 'Bearer ' + this.token\r\n },\r\n data: {\r\n conversation_id: this.conversationId,\r\n bot_id: \"3022316191018903\",\r\n limit: 20\r\n }\r\n });\r\n\r\n if (result.success && result.data) {\r\n const historyData = result.data.data;\r\n const formattedMessages: ChatMessage[] = historyData.map(msg => {\r\n const time = new Date(msg.created_at * 1000);\r\n const hours = time.getHours().toString().padStart(2, '0');\r\n const minutes = time.getMinutes().toString().padStart(2, '0');\r\n const timeStr = `${hours}:${minutes}`;\r\n\r\n const { inputs, ...msgWithoutInputs } = msg;\r\n\r\n return {\r\n ...msgWithoutInputs,\r\n time: timeStr,\r\n isStreaming: false,\r\n status: msg.status === 'error' ? 'error' : 'normal' as const\r\n };\r\n });\r\n\r\n this.messages = formattedMessages;\r\n \r\n requestAnimationFrame(() => {\r\n this.shouldAutoScroll = true;\r\n this.scrollToBottom();\r\n });\r\n }\r\n } catch (error) {\r\n console.error('加载历史消息失败:', error);\r\n alert(error instanceof Error ? error.message : '加载历史消息失败,请刷新重试');\r\n } finally {\r\n this.isLoadingHistory = false;\r\n }\r\n }\r\n\r\n // 修改 isOpen 的 watch 方法\r\n @Watch('isOpen')\r\n async handleIsOpenChange(newValue: boolean) {\r\n if (newValue) {\r\n if (this.conversationId) {\r\n await this.loadHistoryMessages();\r\n } else {\r\n // 直接开始对话\r\n this.sendMessageToAPI('请您开始提问');\r\n }\r\n }\r\n }\r\n\r\n // 开始等待录制\r\n private startWaitingToRecord() {\r\n // 清除可能存在的计时器\r\n if (this.waitingTimer) {\r\n clearInterval(this.waitingTimer);\r\n }\r\n if (this.recordingTimer) {\r\n clearInterval(this.recordingTimer);\r\n }\r\n\r\n this.waitingToRecord = true;\r\n this.waitingTimeLeft = 10;\r\n\r\n this.waitingTimer = setInterval(() => {\r\n this.waitingTimeLeft--;\r\n if (this.waitingTimeLeft <= 0) {\r\n clearInterval(this.waitingTimer);\r\n this.waitingTimer = null;\r\n this.waitingToRecord = false;\r\n this.startRecording();\r\n }\r\n }, 1000);\r\n }\r\n\r\n // 开始录制视频\r\n private async startRecording() {\r\n try {\r\n const stream = await navigator.mediaDevices.getUserMedia({\r\n audio: true,\r\n video: {\r\n width: { ideal: 1280 },\r\n height: { ideal: 720 },\r\n frameRate: { ideal: 30 }\r\n }\r\n });\r\n\r\n this.recordingStream = stream;\r\n this.showRecordingUI = true;\r\n this.showCountdownWarning = false;\r\n\r\n // 重置视频引用\r\n this.videoRef = null;\r\n\r\n // 确保视频元素获取到流\r\n this.setupVideoPreview(stream);\r\n\r\n // 检测浏览器支持的MIME类型\r\n const mimeType = this.getSupportedMimeType();\r\n\r\n // 创建MediaRecorder实例\r\n let mediaRecorder;\r\n try {\r\n mediaRecorder = new MediaRecorder(stream, {\r\n mimeType: mimeType\r\n });\r\n } catch (e) {\r\n // 如果指定MIME类型失败,尝试使用默认设置\r\n console.warn('指定的MIME类型不受支持,使用默认设置:', e);\r\n try {\r\n mediaRecorder = new MediaRecorder(stream);\r\n } catch (recorderError) {\r\n // 通知父组件录制器创建失败\r\n this.recordingError.emit({\r\n type: 'recorder_creation_failed',\r\n message: '无法创建媒体录制器,您的浏览器可能不支持此功能',\r\n details: recorderError\r\n });\r\n this.showRecordingUI = false;\r\n return;\r\n }\r\n }\r\n\r\n this.mediaRecorder = mediaRecorder;\r\n\r\n const chunks: BlobPart[] = [];\r\n\r\n mediaRecorder.ondataavailable = (event) => {\r\n if (event.data.size > 0) {\r\n chunks.push(event.data);\r\n }\r\n };\r\n\r\n mediaRecorder.onerror = (event) => {\r\n // 通知父组件录制过程中发生错误\r\n this.recordingError.emit({\r\n type: 'recording_error',\r\n message: '录制过程中发生错误',\r\n details: event\r\n });\r\n this.stopRecording();\r\n };\r\n\r\n mediaRecorder.onstop = () => {\r\n try {\r\n // 根据实际使用的MIME类型创建Blob\r\n const blobType = mimeType || 'video/mp4';\r\n const blob = new Blob(chunks, { type: blobType });\r\n\r\n if (blob.size === 0) {\r\n // 通知父组件录制的视频为空\r\n this.recordingError.emit({\r\n type: 'empty_recording',\r\n message: '录制的视频为空'\r\n });\r\n this.showRecordingUI = false;\r\n return;\r\n }\r\n\r\n this.recordedBlob = blob;\r\n\r\n // 通知父组件录制已完成\r\n this.recordingStatusChange.emit({\r\n status: 'stopped',\r\n details: {\r\n duration: Math.floor((Date.now() - this.recordingStartTime) / 1000),\r\n size: blob.size,\r\n type: blob.type\r\n }\r\n });\r\n\r\n this.uploadRecordedVideo();\r\n } catch (error) {\r\n // 通知父组件处理录制视频时出错\r\n this.recordingError.emit({\r\n type: 'processing_error',\r\n message: '处理录制视频时出错',\r\n details: error\r\n });\r\n this.showRecordingUI = false;\r\n }\r\n };\r\n\r\n // 开始录制\r\n try {\r\n mediaRecorder.start();\r\n this.isRecording = true;\r\n this.recordingStartTime = Date.now();\r\n this.recordingTimeLeft = this.maxRecordingTime;\r\n\r\n // 通知父组件录制已开始\r\n this.recordingStatusChange.emit({\r\n status: 'started',\r\n details: {\r\n maxDuration: this.maxRecordingTime,\r\n mimeType: mediaRecorder.mimeType\r\n }\r\n });\r\n } catch (startError) {\r\n // 通知父组件开始录制失败\r\n this.recordingError.emit({\r\n type: 'start_failed',\r\n message: '开始录制失败,请检查您的设备权限',\r\n details: startError\r\n });\r\n this.showRecordingUI = false;\r\n return;\r\n }\r\n\r\n // 设置录制计时器\r\n this.recordingTimer = setInterval(() => {\r\n const elapsedTime = Math.floor((Date.now() - this.recordingStartTime) / 1000);\r\n this.recordingTimeLeft = Math.max(0, this.maxRecordingTime - elapsedTime);\r\n\r\n // 检查是否需要显示倒计时警告\r\n if (this.recordingTimeLeft <= this.countdownWarningTime && !this.showCountdownWarning) {\r\n this.showCountdownWarning = true;\r\n }\r\n\r\n // 时间到自动停止录制\r\n if (this.recordingTimeLeft <= 0) {\r\n this.stopRecording();\r\n }\r\n }, 1000);\r\n\r\n } catch (error) {\r\n console.error('无法访问摄像头或麦克风:', error);\r\n // 通知父组件无法访问媒体设备\r\n this.recordingError.emit({\r\n type: 'media_access_failed',\r\n message: '无法访问摄像头或麦克风,请确保已授予权限',\r\n details: error\r\n });\r\n this.showRecordingUI = false;\r\n }\r\n }\r\n\r\n // 添加新方法来设置视频预览\r\n private setupVideoPreview(stream: MediaStream) {\r\n // 延迟执行以确保DOM已更新\r\n setTimeout(() => {\r\n const videoElement = this.hostElement.shadowRoot?.querySelector('video') as HTMLVideoElement;\r\n if (videoElement && stream) {\r\n // 先尝试使用标准方法\r\n try {\r\n videoElement.srcObject = stream;\r\n videoElement.play().catch(err => {\r\n console.error('视频播放失败:', err);\r\n });\r\n } catch (e) {\r\n console.warn('设置srcObject失败,尝试替代方法:', e);\r\n\r\n // 对于不支持srcObject的旧浏览器,使用URL.createObjectURL\r\n try {\r\n // 使用类型断言解决TypeScript错误\r\n const objectUrl = URL.createObjectURL(stream as unknown as MediaSource);\r\n videoElement.src = objectUrl;\r\n\r\n // 确保在视频元素不再使用时释放URL\r\n videoElement.onended = () => {\r\n URL.revokeObjectURL(objectUrl);\r\n };\r\n } catch (urlError) {\r\n console.error('创建对象URL失败:', urlError);\r\n }\r\n }\r\n } else {\r\n console.warn('未找到视频元素或媒体流无效');\r\n }\r\n }, 100);\r\n }\r\n\r\n // 添加一个新方法来检测浏览器支持的MIME类型\r\n private getSupportedMimeType(): string {\r\n // 按优先级排列的MIME类型列表\r\n const mimeTypes = [\r\n 'video/webm;codecs=vp8,opus',\r\n 'video/webm;codecs=vp9,opus',\r\n 'video/webm',\r\n 'video/mp4',\r\n 'video/mp4;codecs=h264,aac',\r\n '' // 空字符串表示使用浏览器默认值\r\n ];\r\n\r\n // 检查MediaRecorder是否可用\r\n if (!window.MediaRecorder) {\r\n console.warn('MediaRecorder API不可用');\r\n return '';\r\n }\r\n\r\n // 检查每种MIME类型是否受支持\r\n for (const type of mimeTypes) {\r\n if (!type) return ''; // 如果是空字符串,直接返回\r\n\r\n try {\r\n if (MediaRecorder.isTypeSupported(type)) {\r\n console.log('使用支持的MIME类型:', type);\r\n return type;\r\n }\r\n } catch (e) {\r\n console.warn(`检查MIME类型支持时出错 ${type}:`, e);\r\n }\r\n }\r\n\r\n // 如果没有找到支持的类型,返回空字符串\r\n console.warn('没有找到支持的MIME类型,将使用浏览器默认值');\r\n return '';\r\n }\r\n\r\n // 停止录制\r\n private stopRecording() {\r\n if (this.mediaRecorder && this.isRecording) {\r\n this.mediaRecorder.stop();\r\n this.isRecording = false;\r\n\r\n // 清理计时器\r\n if (this.recordingTimer) {\r\n clearInterval(this.recordingTimer);\r\n this.recordingTimer = null;\r\n }\r\n\r\n // 停止并释放媒体流\r\n if (this.recordingStream) {\r\n this.recordingStream.getTracks().forEach(track => track.stop());\r\n this.recordingStream = null;\r\n }\r\n\r\n // 清理视频引用\r\n this.videoRef = null;\r\n }\r\n }\r\n\r\n // 修改上传录制的视频方法\r\n private async uploadRecordedVideo() {\r\n if (!this.recordedBlob) return;\r\n\r\n try {\r\n this.isUploadingVideo = true;\r\n this.showRecordingUI = false;\r\n\r\n // 根据Blob类型确定文件扩展名\r\n const fileExtension = this.recordedBlob.type.includes('webm') ? 'webm' : 'mp4';\r\n const fileName = `answer.${fileExtension}`;\r\n \r\n // 创建File对象\r\n const file = new File([this.recordedBlob], fileName, { type: this.recordedBlob.type });\r\n\r\n // 使用uploadFileToBackend上传文件\r\n const uploadResult = await uploadFileToBackend(file, {\r\n 'authorization': 'Bearer ' + this.token\r\n });\r\n console.log('视频上传结果:', uploadResult);\r\n\r\n // 使用 cos_key 作为视频标识符\r\n if (uploadResult.cos_key) {\r\n // 调用音频转文字API\r\n const transcriptionText = await this.convertAudioToText(uploadResult.cos_key);\r\n \r\n // 发送转换后的文本和视频URL到下一个请求\r\n await this.sendMessageToAPI(transcriptionText || \"下一题\", uploadResult.cos_key);\r\n } else {\r\n throw new Error('视频上传失败');\r\n }\r\n } catch (error) {\r\n console.error('视频上传或处理错误:', error);\r\n this.recordingError.emit({\r\n type: 'upload_failed',\r\n message: '视频上传或处理失败',\r\n details: error\r\n });\r\n } finally {\r\n this.isUploadingVideo = false;\r\n this.showRecordingUI = false;\r\n this.recordedBlob = null;\r\n }\r\n }\r\n\r\n // 修改音频转文字方法,使用 cos_key\r\n private async convertAudioToText(cosKey: string): Promise<string | null> {\r\n try {\r\n const result = await sendHttpRequest<{ text: string }>({\r\n url: '/sdk/v1/tts/audio_to_text',\r\n method: 'POST',\r\n headers: {\r\n 'authorization': 'Bearer ' + this.token\r\n },\r\n data: {\r\n cos_key: cosKey\r\n }\r\n });\r\n \r\n if (result.success && result.data?.text) {\r\n console.log('转换后的文本:', result.data.text);\r\n return result.data.text;\r\n } else {\r\n console.warn('音频转文字返回结果格式不正确');\r\n return null;\r\n }\r\n } catch (error) {\r\n console.error('音频转文字错误:', error);\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * 发送面试完成请求\r\n */\r\n private async completeInterview(queryText: string, videoUrl: string) {\r\n if (!this.conversationId) return;\r\n const requestData: any = {\r\n response_mode: 'streaming',\r\n conversation_id: this.conversationId,\r\n query: queryText,\r\n bot_id: \"3022316191018903\",\r\n inputs: {\r\n id: this.resumeId\r\n }\r\n };\r\n\r\n // 如果有视频URL,添加到inputs中\r\n if (videoUrl) {\r\n requestData.inputs.video_url = videoUrl;\r\n }\r\n\r\n // 不使用 await,直接发送请求\r\n sendSSERequest({\r\n url: `/sdk/v1/chat/chat-messages`,\r\n method: 'POST',\r\n headers: {\r\n 'authorization': 'Bearer ' + this.token\r\n },\r\n data: requestData,\r\n }).catch(error => {\r\n console.error('发送面试完成请求失败:', error);\r\n });\r\n }\r\n\r\n // 添加TTS合成音频的方法\r\n private async synthesizeAudio(text: string): Promise<string> {\r\n try {\r\n const response = await fetch(`${API_DOMAIN}/sdk/v1/tts/synthesize_audio`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'authorization': 'Bearer ' + this.token\r\n },\r\n body: JSON.stringify({ text })\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error('语音合成失败');\r\n }\r\n\r\n // 获取音频数据并创建Blob URL\r\n const audioBlob = await response.blob();\r\n return URL.createObjectURL(audioBlob);\r\n } catch (error) {\r\n console.error('语音合成错误:', error);\r\n throw error;\r\n }\r\n }\r\n\r\n // 播放音频的方法\r\n private playAudio(audioUrl: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n this.isPlayingAudio = true;\r\n this.audioUrl = audioUrl;\r\n\r\n // 创建音频元素\r\n if (!this.audioElement) {\r\n this.audioElement = new Audio();\r\n }\r\n\r\n this.audioElement.src = audioUrl;\r\n this.audioElement.onended = () => {\r\n this.isPlayingAudio = false;\r\n this.audioUrl = null;\r\n resolve();\r\n };\r\n\r\n this.audioElement.onerror = () => {\r\n console.error('音频播放错误');\r\n this.isPlayingAudio = false;\r\n this.audioUrl = null;\r\n resolve();\r\n };\r\n\r\n this.audioElement.play().catch(error => {\r\n console.error('音频播放失败:', error);\r\n this.isPlayingAudio = false;\r\n this.audioUrl = null;\r\n resolve();\r\n });\r\n });\r\n }\r\n\r\n // 修改 componentDidLoad 生命周期方法,确保组件卸载时释放资源\r\n disconnectedCallback() {\r\n // 释放音频资源\r\n if (this.audioElement) {\r\n this.audioElement.pause();\r\n this.audioElement.src = '';\r\n this.audioElement = null;\r\n }\r\n\r\n // 释放 Blob URL\r\n if (this.audioUrl) {\r\n URL.revokeObjectURL(this.audioUrl);\r\n this.audioUrl = null;\r\n }\r\n\r\n // 清理其他计时器\r\n if (this.waitingTimer) {\r\n clearInterval(this.waitingTimer);\r\n this.waitingTimer = null;\r\n }\r\n\r\n if (this.recordingTimer) {\r\n clearInterval(this.recordingTimer);\r\n this.recordingTimer = null;\r\n }\r\n\r\n // 停止录制\r\n this.stopRecording();\r\n }\r\n\r\n // 修改手动播放音频的方法\r\n private handlePlayAudio = async () => {\r\n if (this.audioUrl) {\r\n await this.playAudio(this.audioUrl);\r\n // 手动播放完成后开始等待录制\r\n this.startWaitingToRecord();\r\n }\r\n };\r\n\r\n render() {\r\n if (!this.isOpen) return null;\r\n\r\n const modalStyle = {\r\n zIndex: String(this.zIndex)\r\n };\r\n\r\n const containerClass = {\r\n 'modal-container': true,\r\n 'fullscreen': this.fullscreen\r\n };\r\n\r\n const overlayClass = {\r\n 'modal-overlay': true,\r\n 'fullscreen-overlay': this.fullscreen\r\n };\r\n\r\n const renderVideoPreview = () => (\r\n <div class=\"video-preview\">\r\n <video\r\n autoPlay\r\n playsInline\r\n muted\r\n style={{ transform: 'scaleX(-1)' }}\r\n ref={(el) => {\r\n if (el && this.recordingStream && !this.videoRef) {\r\n this.videoRef = el;\r\n // 不在这里设置srcObject,而是使用setupVideoPreview方法\r\n }\r\n }}\r\n ></video>\r\n <div class={{\r\n 'recording-status': true,\r\n 'warning': this.showCountdownWarning\r\n }}>\r\n <span class=\"recording-dot\"></span>\r\n <span>\r\n 录制中 {Math.floor(this.recordingTimeLeft / 60)}:{(this.recordingTimeLeft % 60).toString().padStart(2, '0')}\r\n {this.showCountdownWarning && ` (即将自动完成)`}\r\n </span>\r\n </div>\r\n </div>\r\n );\r\n\r\n // 渲染占位符状态信息\r\n const renderPlaceholderStatus = () => {\r\n // 正在播放音频\r\n if (this.isPlayingAudio) {\r\n return (\r\n <div class=\"placeholder-status\">\r\n <p>正在播放问题,请听完后准备回答...</p>\r\n </div>\r\n );\r\n }\r\n\r\n // 正在上传视频\r\n if (this.isUploadingVideo) {\r\n return (\r\n <div class=\"placeholder-status\">\r\n <p>正在上传视频,请稍候...</p>\r\n </div>\r\n );\r\n }\r\n\r\n // 正在加载或等待AI回复\r\n if (this.isLoading || this.currentStreamingMessage) {\r\n return (\r\n <div class=\"placeholder-status\">\r\n <p>请等待题目...</p>\r\n </div>\r\n );\r\n }\r\n\r\n // 等待开始录制\r\n if (this.waitingToRecord) {\r\n return (\r\n <div class=\"placeholder-status\">\r\n <p>请准备好,{this.waitingTimeLeft}秒后将开始录制您的回答...</p>\r\n </div>\r\n );\r\n }\r\n\r\n // 添加默认状态\r\n return (\r\n <div class=\"placeholder-status default-status\">\r\n <p>准备中...</p>\r\n </div>\r\n );\r\n };\r\n\r\n return (\r\n <div class={overlayClass} style={modalStyle}>\r\n <div class={containerClass}>\r\n {this.isShowHeader && (\r\n <div class=\"modal-header\">\r\n <div class=\"header-left\">\r\n {this.icon && <img src={this.icon} class=\"header-icon\" alt=\"应用图标\" />}\r\n <div>{this.modalTitle}</div>\r\n </div>\r\n {this.isNeedClose && (\r\n <button class=\"close-button\" onClick={this.handleClose}>\r\n <span>×</span>\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n <div style={{ height: '100%' }}>\r\n <div class=\"chat-history\" onScroll={this.handleScroll}>\r\n {this.isLoadingHistory ? (\r\n <div class=\"loading-container\">\r\n <div class=\"loading-spinner\"></div>\r\n <p>加载历史消息中...</p>\r\n </div>\r\n ) : (\r\n <div>\r\n {this.messages.map((message) => (\r\n <div id={`message_${message.id}`} key={message.id}>\r\n <pcm-chat-message\r\n message={message}\r\n onMessageChange={(event) => {\r\n const updatedMessages = this.messages.map(msg =>\r\n msg.id === message.id ? { ...msg, ...event.detail } : msg\r\n );\r\n this.messages = updatedMessages;\r\n }}\r\n ></pcm-chat-message>\r\n </div>\r\n ))}\r\n {this.currentStreamingMessage && (\r\n <div id={`message_${this.currentStreamingMessage.id}`}>\r\n <pcm-chat-message\r\n message={this.currentStreamingMessage}\r\n ></pcm-chat-message>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n <div class=\"recording-section\">\r\n <div class=\"recording-container\">\r\n <div class=\"video-area\">\r\n {this.showRecordingUI ? (\r\n renderVideoPreview()\r\n ) : (\r\n <div class=\"video-preview placeholder\">\r\n {renderPlaceholderStatus()}\r\n </div>\r\n )}\r\n </div>\r\n {/* 添加进度条和数字进度 */}\r\n <div class=\"progress-container\">\r\n <div class=\"progress-bar-container\">\r\n <div\r\n class=\"progress-bar\"\r\n style={{\r\n width: `${Math.max(0, this.currentQuestionNumber - 1) / this.totalQuestions * 100}%`\r\n }}\r\n ></div>\r\n </div>\r\n <div class=\"progress-text\">\r\n 已完成{Math.max(0, this.currentQuestionNumber - 1)}/{this.totalQuestions}\r\n </div>\r\n </div>\r\n <div class=\"recording-controls\">\r\n {this.showRecordingUI ? (\r\n <button\r\n class=\"stop-recording-button\"\r\n onClick={() => this.stopRecording()}\r\n >\r\n 完成本题回答\r\n </button>\r\n ) : (\r\n <div class=\"waiting-message\">\r\n {(() => {\r\n // 显示播放按钮(当不自动播放且有音频URL时)\r\n if (!this.enableVoice && this.audioUrl && !this.isPlayingAudio) {\r\n return (\r\n <div class=\"play-audio-container\" onClick={this.handlePlayAudio}>\r\n <p>\r\n <svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"currentColor\" style={{ verticalAlign: 'middle', marginRight: '8px' }}>\r\n <path d=\"M8 5v14l11-7z\" />\r\n </svg>\r\n <span style={{ verticalAlign: 'middle' }}>播放题目</span>\r\n </p>\r\n </div>\r\n );\r\n }\r\n\r\n // 其他状态下显示禁用的\"完成回答\"按钮\r\n return (\r\n <button class=\"stop-recording-button disabled\" disabled>\r\n 完成回答\r\n </button>\r\n );\r\n })()}\r\n </div>\r\n )}\r\n </div>\r\n\r\n\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n }\r\n}",".modal-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background-color: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n z-index: 1000;\r\n}\r\n\r\n.fullscreen-overlay {\r\n background-color: rgba(0, 0, 0, 0.7);\r\n}\r\n\r\n.modal-container {\r\n background-color: #fff;\r\n border-radius: 8px;\r\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\r\n width: 90%;\r\n max-width: 800px;\r\n max-height: 90vh;\r\n display: flex;\r\n flex-direction: column;\r\n overflow: hidden;\r\n}\r\n\r\n.fullscreen {\r\n width: 100%;\r\n height: 100%;\r\n max-width: 100%;\r\n max-height: 100%;\r\n border-radius: 0;\r\n}\r\n\r\n.pc-layout {\r\n width: 90%;\r\n max-width: 800px;\r\n max-height: 90vh;\r\n}\r\n\r\n.mobile-layout {\r\n width: 100%;\r\n height: 100%;\r\n max-width: 100%;\r\n max-height: 100%;\r\n border-radius: 0;\r\n}\r\n\r\n.modal-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 16px 20px;\r\n border-bottom: 1px solid #f0f0f0;\r\n background-color: #fff;\r\n}\r\n\r\n.header-left {\r\n display: flex;\r\n align-items: center;\r\n font-size: 16px;\r\n font-weight: 600;\r\n color: #333;\r\n}\r\n\r\n.header-icon {\r\n width: 24px;\r\n height: 24px;\r\n margin-right: 8px;\r\n border-radius: 4px;\r\n}\r\n\r\n.close-button {\r\n background: none;\r\n border: none;\r\n font-size: 20px;\r\n cursor: pointer;\r\n color: #999;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 50%;\r\n transition: background-color 0.3s;\r\n}\r\n\r\n.close-button:hover {\r\n background-color: #f5f5f5;\r\n color: #666;\r\n}\r\n\r\n.input-container {\r\n padding: 20px;\r\n overflow-y: auto;\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 20px;\r\n}\r\n\r\n.plan-type-section {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 10px;\r\n}\r\n\r\n.plan-type-section label {\r\n font-weight: 600;\r\n color: #333;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.plan-type-options {\r\n display: flex;\r\n gap: 15px;\r\n flex-wrap: wrap;\r\n}\r\n\r\n.plan-type-option {\r\n flex: 1;\r\n min-width: 120px;\r\n border: 1px solid #e8e8e8;\r\n border-radius: 8px;\r\n padding: 15px;\r\n cursor: pointer;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n transition: all 0.3s;\r\n}\r\n\r\n.plan-type-option:hover {\r\n border-color: #1890ff;\r\n background-color: #f0f7ff;\r\n}\r\n\r\n.plan-type-option.selected {\r\n border-color: #1890ff;\r\n background-color: #e6f7ff;\r\n box-shadow: 0 2px 8px rgba(24, 144, 255, 0.2);\r\n}\r\n\r\n.option-icon {\r\n font-size: 24px;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.option-label {\r\n font-size: 14px;\r\n font-weight: 500;\r\n color: #333;\r\n}\r\n\r\n.resume-upload-section {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n}\r\n\r\n.resume-upload-section label {\r\n font-weight: 600;\r\n color: #333;\r\n}\r\n\r\n.upload-area {\r\n border: 2px dashed #d9d9d9;\r\n border-radius: 4px;\r\n text-align: center;\r\n cursor: pointer;\r\n transition: border-color 0.3s, background-color 0.3s;\r\n \r\n}\r\n\r\n.upload-area:hover {\r\n border-color: #1890ff;\r\n background-color: #f0f7ff;\r\n}\r\n\r\n.upload-placeholder {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n color: #666;\r\n}\r\n\r\n.upload-placeholder svg {\r\n color: #1890ff;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.upload-placeholder p {\r\n margin: 4px 0;\r\n}\r\n\r\n.upload-hint {\r\n font-size: 12px;\r\n color: #999;\r\n}\r\n\r\n.file-info {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n background-color: #f5f5f5;\r\n padding: 8px 12px;\r\n border-radius: 4px;\r\n}\r\n\r\n.file-info span {\r\n flex: 1;\r\n display: -webkit-box;\r\n -webkit-line-clamp: 2;\r\n -webkit-box-orient: vertical;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n word-break: break-word;\r\n margin-right: 8px;\r\n max-width: calc(100% - 32px);\r\n line-height: 1.4;\r\n}\r\n\r\n.remove-file {\r\n flex-shrink: 0;\r\n background: none;\r\n border: none;\r\n color: #ff4d4f;\r\n cursor: pointer;\r\n font-size: 16px;\r\n padding: 4px 8px;\r\n border-radius: 50%;\r\n transition: background-color 0.3s;\r\n}\r\n\r\n.remove-file:hover {\r\n background-color: #fff1f0;\r\n}\r\n\r\n.file-input {\r\n display: none;\r\n}\r\n\r\n.submit-button {\r\n background-color: #1890ff;\r\n color: white;\r\n border: none;\r\n border-radius: 4px;\r\n padding: 12px 20px;\r\n font-size: 16px;\r\n font-weight: 500;\r\n cursor: pointer;\r\n transition: background-color 0.3s;\r\n margin-top: 10px;\r\n}\r\n\r\n.submit-button:hover {\r\n background-color: #40a9ff;\r\n}\r\n\r\n.submit-button:disabled {\r\n background-color: #d9d9d9;\r\n cursor: not-allowed;\r\n}\r\n\r\n.chat-modal-container {\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n overflow: hidden;\r\n} ","/* 模态框基础样式 */\r\n.modal-overlay {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background-color: rgba(0, 0, 0, 0.5);\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n z-index: 1000;\r\n overflow-y: auto;\r\n padding: 20px;\r\n}\r\n\r\n/* 全屏模式下取消 padding */\r\n.fullscreen-overlay {\r\n padding: 0;\r\n background-color: rgba(0, 0, 0, 0.7);\r\n}\r\n\r\n.modal-container {\r\n background-color: #fff;\r\n border-radius: 8px;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n position: relative;\r\n margin: auto;\r\n transition: all 0.3s ease-out;\r\n overflow: hidden;\r\n}\r\n\r\n/* 全屏模式样式 */\r\n.modal-container.fullscreen {\r\n width: 100vw;\r\n max-width: none;\r\n height: 100%;\r\n border-radius: 0;\r\n margin: 0;\r\n display: flex;\r\n flex-direction: column;\r\n height: 100vh;\r\n max-height: 100vh;\r\n}\r\n\r\n/* PC端布局 */\r\n.pc-layout {\r\n width: 80%;\r\n max-width: 500px;\r\n min-width: 320px;\r\n}\r\n\r\n/* 移动端布局 */\r\n.mobile-layout {\r\n width: 100%;\r\n height: 100%;\r\n border-radius: 0;\r\n}\r\n\r\n/* 响应式布局 */\r\n@media screen and (max-width: 768px) {\r\n .pc-layout {\r\n width: 95%;\r\n }\r\n\r\n .modal-overlay {\r\n padding: 0;\r\n }\r\n\r\n .modal-container.fullscreen {\r\n /* 支持 iOS Safari */\r\n height: -webkit-fill-available;\r\n max-height: -webkit-fill-available;\r\n /* 确保内容不会被顶部状态栏和底部工具栏遮挡 */\r\n padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom);\r\n margin-top: 40px;\r\n height: calc(100% - 40px);\r\n max-height: calc(100% - 40px);\r\n border-radius: 16px 16px 0 0;\r\n }\r\n\r\n .modal-container.mobile-layout {\r\n width: 100%;\r\n height: 100vh;\r\n max-height: 100vh;\r\n min-height: 100vh;\r\n border-radius: 0;\r\n margin: 0;\r\n display: flex;\r\n flex-direction: column;\r\n }\r\n}\r\n\r\n/* 模态框头部样式 */\r\n.modal-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n padding: 4px 16px;\r\n height: 50px;\r\n border-bottom: 1px solid #e8e8e8;\r\n flex-shrink: 0;\r\n}\r\n\r\n.header-left {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n}\r\n\r\n.header-icon {\r\n width: 24px;\r\n height: 24px;\r\n}\r\n\r\n.close-button {\r\n background: transparent;\r\n border: none;\r\n cursor: pointer;\r\n padding: 8px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 32px;\r\n height: 32px;\r\n border-radius: 4px;\r\n}\r\n\r\n.close-button:hover {\r\n background-color: rgba(0, 0, 0, 0.04);\r\n}\r\n\r\n.close-button span {\r\n font-size: 24px;\r\n line-height: 1;\r\n color: #999;\r\n}\r\n\r\n.close-button:hover span {\r\n color: #666;\r\n}\r\n\r\n\r\n/* 文件上传区域通用样式 */\r\n.upload-area {\r\n border: 2px dashed #ddd;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n transition: all 0.3s ease;\r\n margin-bottom: 20px;\r\n width: 100%;\r\n}\r\n\r\n.upload-area:hover {\r\n border-color: #1890ff;\r\n background-color: rgba(24, 144, 255, 0.05);\r\n}\r\n\r\n\r\n.upload-placeholder {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n color: #666;\r\n}\r\n\r\n.upload-placeholder svg {\r\n color: #1890ff;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.upload-placeholder p {\r\n margin: 4px 0;\r\n}\r\n\r\n.upload-hint {\r\n font-size: 0.8rem;\r\n color: #999;\r\n margin-top: 0.5rem;\r\n}\r\n\r\n.file-info {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 8px;\r\n background: #f9f9f9;\r\n border: 1px solid #e8e8e8;\r\n border-radius: 4px;\r\n}\r\n\r\n.file-info span {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n max-width: calc(100% - 30px);\r\n}\r\n\r\n.remove-file {\r\n background: transparent;\r\n border: none;\r\n color: #999;\r\n cursor: pointer;\r\n padding: 4px 8px;\r\n font-size: 16px;\r\n line-height: 1;\r\n border-radius: 4px;\r\n transition: all 0.2s;\r\n}\r\n\r\n.remove-file:hover {\r\n background-color: #f0f0f0;\r\n color: #666;\r\n}\r\n\r\n.file-input {\r\n display: none;\r\n}\r\n\r\n\r\n\r\n/* 输入容器样式 */\r\n.input-container {\r\n padding: 20px;\r\n display: flex;\r\n flex-direction: column;\r\n height: calc(100% - 50px);\r\n /* 减去header高度 */\r\n overflow-y: auto;\r\n}\r\n\r\n.input-container h3 {\r\n margin-top: 0;\r\n margin-bottom: 20px;\r\n font-size: 18px;\r\n color: #333;\r\n text-align: center;\r\n}\r\n\r\n/* JD输入区域样式 */\r\n.jd-input-section {\r\n margin-bottom: 20px;\r\n}\r\n\r\n.jd-input-section label {\r\n display: block;\r\n margin-bottom: 8px;\r\n font-weight: 500;\r\n color: #333;\r\n}\r\n\r\n.job-description-textarea {\r\n width: 100%;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n resize: vertical;\r\n font-family: inherit;\r\n font-size: 14px;\r\n line-height: 1.5;\r\n transition: border-color 0.3s;\r\n}\r\n\r\n.job-description-textarea:focus {\r\n outline: none;\r\n border-color: #1890ff;\r\n box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);\r\n}\r\n\r\n/* 简历上传区域样式 */\r\n.resume-upload-section {\r\n margin-bottom: 20px;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n}\r\n\r\n.resume-upload-section label {\r\n display: block;\r\n margin-bottom: 8px;\r\n font-weight: 500;\r\n color: #333;\r\n align-self: flex-start;\r\n}\r\n\r\n\r\n/* 提交按钮通用样式 */\r\n.submit-button {\r\n margin-top: 10px;\r\n padding: 10px 30px;\r\n background: #1890ff;\r\n color: white;\r\n border: none;\r\n border-radius: 4px;\r\n font-size: 16px;\r\n cursor: pointer;\r\n transition: all 0.3s ease;\r\n width: 100%;\r\n max-width: 400px;\r\n align-self: center;\r\n /* 确保按钮居中 */\r\n}\r\n\r\n.submit-button:disabled {\r\n background: #ccc;\r\n cursor: not-allowed;\r\n}\r\n\r\n.submit-button:hover:not(:disabled) {\r\n background: #40a9ff;\r\n}\r\n\r\n/* 聊天模态框容器 */\r\n.chat-modal-container {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n/* AI免责声明和备案信息样式 */\r\n.ai-disclaimer {\r\n margin-top: 16px;\r\n text-align: center;\r\n font-size: 12px;\r\n color: #999;\r\n line-height: 1.5;\r\n}\r\n\r\n.ai-disclaimer p {\r\n margin: 4px 0;\r\n}\r\n\r\n.beian-info {\r\n display: flex;\r\n justify-content: center;\r\n flex-wrap: wrap;\r\n gap: 4px;\r\n}\r\n\r\n.ai-disclaimer a {\r\n color: #666;\r\n text-decoration: none;\r\n transition: color 0.2s ease;\r\n}\r\n\r\n.ai-disclaimer a:hover {\r\n color: #1890ff;\r\n text-decoration: underline;\r\n}","import { Component, Prop, h, State, Element, Event, EventEmitter, Watch } from '@stencil/core';\r\nimport { uploadFileToBackend, FileUploadResponse, sendHttpRequest } from '../../utils/utils';\r\n\r\n/**\r\n * 职业规划类型枚举\r\n */\r\nexport type CareerPlanType = '长期规划' | '转行建议' | '晋升路径';\r\n\r\n@Component({\r\n tag: 'pcm-zygh-modal',\r\n styleUrls: ['pcm-zygh-modal.css','../../global/global.css'],\r\n shadow: true,\r\n})\r\nexport class ZyghModal {\r\n /**\r\n * 模态框标题\r\n */\r\n @Prop() modalTitle: string = '职业规划助手';\r\n\r\n /**\r\n * SDK鉴权密钥\r\n */\r\n @Prop({ attribute: 'token' }) token: string = '';\r\n\r\n /**\r\n * 是否显示聊天模态框\r\n */\r\n @Prop({ mutable: true }) isOpen: boolean = false;\r\n\r\n /**\r\n * 当点击模态框关闭时触发\r\n */\r\n @Event() modalClosed: EventEmitter<void>;\r\n\r\n /**\r\n * 应用图标URL\r\n */\r\n @Prop() icon?: string;\r\n\r\n /**\r\n * 聊天框的页面层级\r\n */\r\n @Prop() zIndex?: number = 1000;\r\n\r\n /**\r\n * 是否展示顶部标题栏\r\n */\r\n @Prop() isShowHeader: boolean = true;\r\n\r\n /**\r\n * 是否展示右上角的关闭按钮\r\n */\r\n @Prop() isNeedClose: boolean = true;\r\n\r\n /**\r\n * 会话ID,传入继续对话,否则创建新会话\r\n */\r\n @Prop({ mutable: true }) conversationId?: string;\r\n\r\n /**\r\n * 默认查询文本\r\n */\r\n @Prop() defaultQuery: string = '';\r\n\r\n /**\r\n * 是否以全屏模式打开,移动端建议设置为true\r\n */\r\n @Prop() fullscreen: boolean = false;\r\n\r\n\r\n /**\r\n * 自定义输入参数,传入plan_type则可以指定规划类型,可传入\"长期规划\"、\"转行建议\"、\"晋升路径\"\r\n * 例如: \r\n * zyghModal.customInputs = {\r\n * plan_type: \"转行建议\"\r\n * };\r\n */\r\n @Prop() customInputs: { [key: string]: any } = {};\r\n\r\n /**\r\n * 上传成功事件\r\n */\r\n @Event() uploadSuccess: EventEmitter<FileUploadResponse>;\r\n\r\n /**\r\n * 流式输出完成事件\r\n */\r\n @Event() streamComplete: EventEmitter<{\r\n conversation_id: string;\r\n event: string;\r\n message_id: string;\r\n id: string;\r\n }>;\r\n\r\n /**\r\n * 新会话开始的回调,只会在一轮对话开始时触发一次\r\n */\r\n @Event() conversationStart: EventEmitter<{\r\n conversation_id: string;\r\n event: string;\r\n message_id: string;\r\n id: string;\r\n }>;\r\n\r\n /**\r\n * 当聊天完成时触发\r\n */\r\n @Event() planningComplete: EventEmitter<{\r\n conversation_id: string;\r\n plan_type: CareerPlanType;\r\n }>;\r\n\r\n /**\r\n * SDK密钥验证失败事件\r\n */\r\n @Event() tokenInvalid: EventEmitter<void>;\r\n\r\n @State() selectedFile: File | null = null;\r\n @State() isUploading: boolean = false;\r\n @State() uploadedFileInfo: FileUploadResponse | null = null;\r\n @State() showChatModal: boolean = false;\r\n @State() isSubmitting: boolean = false;\r\n @State() selectedPlanType: CareerPlanType = '长期规划';\r\n\r\n // 添加新的状态来控制过渡动画\r\n @State() isTransitioning: boolean = false;\r\n @State() transitionTimer: any = null;\r\n\r\n // 使用 @Element 装饰器获取组件的 host 元素\r\n @Element() hostElement: HTMLElement;\r\n\r\n private handleClose = () => {\r\n this.isOpen = false;\r\n this.modalClosed.emit();\r\n };\r\n\r\n private handleFileChange = (event: Event) => {\r\n const input = event.target as HTMLInputElement;\r\n if (input.files && input.files.length > 0) {\r\n this.selectedFile = input.files[0];\r\n }\r\n };\r\n\r\n private handleUploadClick = () => {\r\n const fileInput = this.hostElement.shadowRoot?.querySelector('.file-input') as HTMLInputElement;\r\n fileInput?.click();\r\n };\r\n\r\n private clearSelectedFile = () => {\r\n this.selectedFile = null;\r\n this.uploadedFileInfo = null;\r\n const fileInput = this.hostElement.shadowRoot?.querySelector('.file-input') as HTMLInputElement;\r\n if (fileInput) {\r\n fileInput.value = '';\r\n }\r\n };\r\n\r\n private handlePlanTypeChange = (type: CareerPlanType) => {\r\n this.selectedPlanType = type;\r\n };\r\n\r\n private async uploadFile() {\r\n if (!this.selectedFile) return;\r\n\r\n this.isUploading = true;\r\n\r\n try {\r\n const result = await uploadFileToBackend(this.selectedFile, {\r\n 'authorization': 'Bearer ' + this.token\r\n });\r\n \r\n this.uploadedFileInfo = result;\r\n this.uploadSuccess.emit(result);\r\n } catch (error) {\r\n console.error('文件上传错误:', error);\r\n this.clearSelectedFile();\r\n alert(error instanceof Error ? error.message : '文件上传失败,请重试');\r\n } finally {\r\n this.isUploading = false;\r\n }\r\n }\r\n\r\n private handleStartPlanning = async () => {\r\n if (!this.selectedFile) {\r\n alert('请上传简历');\r\n return;\r\n }\r\n\r\n this.isSubmitting = true;\r\n\r\n try {\r\n // 如果还没上传,先上传文件\r\n if (!this.uploadedFileInfo) {\r\n await this.uploadFile();\r\n if (!this.uploadedFileInfo) {\r\n this.isSubmitting = false;\r\n return; // 上传失败\r\n }\r\n }\r\n\r\n // console.log('传递的customInputs:', {\r\n // ...this.customInputs,\r\n // file_url: this.uploadedFileInfo.cos_key,\r\n // plan_type: this.selectedPlanType\r\n // });\r\n\r\n // 直接显示聊天模态框\r\n this.showChatModal = true;\r\n } catch (error) {\r\n console.error('开始规划时出错:', error);\r\n alert('开始规划时出错,请重试');\r\n } finally {\r\n this.isSubmitting = false;\r\n }\r\n };\r\n\r\n @Watch('isOpen')\r\n handleIsOpenChange(newValue: boolean) {\r\n if (!newValue) {\r\n // 重置状态\r\n this.clearSelectedFile();\r\n this.showChatModal = false;\r\n \r\n // 清除可能存在的计时器\r\n if (this.transitionTimer) {\r\n clearTimeout(this.transitionTimer);\r\n this.transitionTimer = null;\r\n }\r\n } else {\r\n // 当模态框打开时,验证API密钥\r\n this.verifyApiKey();\r\n \r\n if (this.conversationId) {\r\n // 如果有会话ID,直接显示聊天模态框\r\n this.showChatModal = true;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 验证API密钥\r\n */\r\n private async verifyApiKey() {\r\n if (!this.token) {\r\n this.tokenInvalid.emit();\r\n return;\r\n }\r\n \r\n try {\r\n const response = await sendHttpRequest({\r\n url: '/sdk/v1/user',\r\n method: 'GET',\r\n headers: {\r\n 'Authorization': `Bearer ${this.token}`\r\n }\r\n });\r\n\r\n if (!response.success) {\r\n throw new Error(response.message || 'API密钥验证失败');\r\n }\r\n \r\n // 验证成功,继续正常流程\r\n } catch (error) {\r\n console.error('API密钥验证错误:', error);\r\n // 通知父组件API密钥无效\r\n this.tokenInvalid.emit();\r\n }\r\n }\r\n\r\n componentWillLoad() {\r\n // 检查 customInputs 中是否有 plan_type\r\n if (this.customInputs && this.customInputs.plan_type) {\r\n this.selectedPlanType = this.customInputs.plan_type;\r\n }\r\n }\r\n\r\n // 处理流式输出完成事件\r\n private handleStreamComplete = (event: CustomEvent) => {\r\n // 将事件转发出去\r\n this.streamComplete.emit(event.detail);\r\n };\r\n\r\n // 处理会话开始事件\r\n private handleConversationStart = (event: CustomEvent) => {\r\n this.conversationStart.emit(event.detail);\r\n };\r\n\r\n // 处理规划完成事件\r\n private handlePlanningComplete = (event: CustomEvent) => {\r\n this.planningComplete.emit({\r\n ...event.detail,\r\n plan_type: this.selectedPlanType\r\n });\r\n };\r\n\r\n render() {\r\n if (!this.isOpen) return null;\r\n\r\n const modalStyle = {\r\n zIndex: String(this.zIndex)\r\n };\r\n\r\n const containerClass = {\r\n 'modal-container': true,\r\n 'fullscreen': this.fullscreen,\r\n 'pc-layout': true,\r\n };\r\n \r\n const overlayClass = {\r\n 'modal-overlay': true,\r\n 'fullscreen-overlay': this.fullscreen\r\n };\r\n\r\n // 检查是否有会话ID,如果有则直接显示聊天模态框\r\n if (this.conversationId && !this.showChatModal) {\r\n this.showChatModal = true;\r\n }\r\n\r\n return (\r\n <div class={overlayClass} style={modalStyle}>\r\n <div class={containerClass}>\r\n {this.isShowHeader && (\r\n <div class=\"modal-header\">\r\n <div class=\"header-left\">\r\n {this.icon && <img src={this.icon} class=\"header-icon\" alt=\"应用图标\" />}\r\n <div>{this.modalTitle}</div>\r\n </div>\r\n {this.isNeedClose && (\r\n <button class=\"close-button\" onClick={this.handleClose}>\r\n <span>×</span>\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* 输入界面 - 仅在不显示聊天模态框且没有会话ID时显示 */}\r\n {!this.showChatModal && !this.conversationId && (\r\n <div class=\"input-container\">\r\n \r\n {/* 规划类型选择 */}\r\n <div class=\"plan-type-section\">\r\n <label>选择规划类型</label>\r\n <div class=\"plan-type-options\">\r\n <div \r\n class={`plan-type-option ${this.selectedPlanType === '长期规划' ? 'selected' : ''}`}\r\n onClick={() => this.handlePlanTypeChange('长期规划')}\r\n >\r\n <div class=\"option-icon\">📈</div>\r\n <div class=\"option-label\">长期规划</div>\r\n </div>\r\n <div \r\n class={`plan-type-option ${this.selectedPlanType === '转行建议' ? 'selected' : ''}`}\r\n onClick={() => this.handlePlanTypeChange('转行建议')}\r\n >\r\n <div class=\"option-icon\">🔄</div>\r\n <div class=\"option-label\">转行建议</div>\r\n </div>\r\n <div \r\n class={`plan-type-option ${this.selectedPlanType === '晋升路径' ? 'selected' : ''}`}\r\n onClick={() => this.handlePlanTypeChange('晋升路径')}\r\n >\r\n <div class=\"option-icon\">🚀</div>\r\n <div class=\"option-label\">晋升路径</div>\r\n </div>\r\n </div>\r\n </div>\r\n \r\n {/* 简历上传区域 */}\r\n <div class=\"resume-upload-section\">\r\n <label>上传简历</label>\r\n <div class=\"upload-area\" onClick={this.handleUploadClick}>\r\n {this.selectedFile ? (\r\n <div class=\"file-info\">\r\n <span>{this.selectedFile.name}</span>\r\n <button class=\"remove-file\" onClick={(e) => {\r\n e.stopPropagation();\r\n this.clearSelectedFile();\r\n }}>×</button>\r\n </div>\r\n ) : (\r\n <div class=\"upload-placeholder\">\r\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" width=\"48\" height=\"48\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m0-16l-4 4m4-4l4 4\" />\r\n </svg>\r\n <p>点击上传简历</p>\r\n <p class=\"upload-hint\">支持 txt、markdown、pdf、docx、doc、md 格式</p>\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n\r\n <button\r\n class=\"submit-button\"\r\n disabled={!this.selectedFile || this.isUploading || this.isSubmitting}\r\n onClick={this.handleStartPlanning}\r\n >\r\n {this.isUploading ? '上传中...' : this.isSubmitting ? '处理中...' : '开始规划'}\r\n </button>\r\n\r\n <div class=\"ai-disclaimer\">\r\n <p>所有内容均由AI生成仅供参考</p>\r\n <p class=\"beian-info\">\r\n <span>中央网信办生成式人工智能服务备案号</span>:\r\n <a href=\"https://www.pincaimao.com\" target=\"_blank\" rel=\"noopener noreferrer\">Hunan-PinCaiMao-202412310003</a>\r\n </p>\r\n </div>\r\n\r\n <input\r\n type=\"file\"\r\n class=\"file-input\"\r\n onChange={this.handleFileChange}\r\n />\r\n </div>\r\n )}\r\n\r\n {/* 聊天界面 - 在显示聊天模态框时显示 */}\r\n {this.showChatModal && (\r\n <div class=\"chat-modal-container\">\r\n <pcm-app-chat-modal\r\n isOpen={true}\r\n modalTitle={this.modalTitle}\r\n icon={this.icon}\r\n token={this.token}\r\n isShowHeader={this.isShowHeader} \r\n isNeedClose={this.isShowHeader} \r\n zIndex={this.zIndex}\r\n botId=\"3022316191018898\"\r\n fullscreen={this.fullscreen}\r\n conversationId={this.conversationId}\r\n defaultQuery={this.defaultQuery}\r\n enableVoice={false}\r\n customInputs={this.conversationId ? undefined : {\r\n ...this.customInputs,\r\n file_url: this.uploadedFileInfo?.cos_key,\r\n type: this.selectedPlanType\r\n }}\r\n interviewMode=\"text\"\r\n onModalClosed={this.handleClose}\r\n onStreamComplete={this.handleStreamComplete}\r\n onConversationStart={this.handleConversationStart}\r\n onInterviewComplete={this.handlePlanningComplete}\r\n ></pcm-app-chat-modal>\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n }\r\n} "],"mappings":"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","ignoreList":[]}
|