pcm-agents 0.3.0 → 0.3.2
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 +195 -0
- package/dist/cjs/index-BFPEnLbS.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-jlpx-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 +6560 -0
- package/dist/cjs/pcm-app-chat-modal_7.cjs.entry.js.map +1 -0
- package/dist/cjs/pcm-chat-modal.cjs.entry.js +17 -32
- package/dist/cjs/pcm-chat-modal.cjs.entry.js.map +1 -1
- package/dist/cjs/pcm-chat-modal.entry.cjs.js.map +1 -1
- package/dist/collection/collection-manifest.json +2 -0
- 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.css +2 -1
- package/dist/collection/components/pcm-app-chat-modal/pcm-app-chat-modal.js +137 -159
- package/dist/collection/components/pcm-app-chat-modal/pcm-app-chat-modal.js.map +1 -1
- package/dist/collection/components/pcm-chat-message/pcm-chat-message.css +66 -12
- package/dist/collection/components/pcm-chat-message/pcm-chat-message.js +106 -13
- package/dist/collection/components/pcm-chat-message/pcm-chat-message.js.map +1 -1
- package/dist/collection/components/pcm-chat-modal/pcm-chat-modal.js +39 -34
- package/dist/collection/components/pcm-chat-modal/pcm-chat-modal.js.map +1 -1
- package/dist/collection/components/pcm-hr-chat-modal/pcm-hr-chat-modal.js +76 -105
- 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.css +162 -0
- package/dist/collection/components/pcm-jlpx-modal/pcm-jlpx-modal.js +616 -0
- package/dist/collection/components/pcm-jlpx-modal/pcm-jlpx-modal.js.map +1 -0
- package/dist/collection/components/pcm-mnms-modal/pcm-mnms-modal.css +0 -79
- package/dist/collection/components/pcm-mnms-modal/pcm-mnms-modal.js +133 -81
- 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 +77 -101
- 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.css +273 -0
- package/dist/collection/components/pcm-zygh-modal/pcm-zygh-modal.js +613 -0
- package/dist/collection/components/pcm-zygh-modal/pcm-zygh-modal.js.map +1 -0
- package/dist/collection/global/global.css +324 -0
- package/dist/collection/index.js.map +1 -1
- package/dist/collection/interfaces/chat.js.map +1 -1
- package/dist/collection/utils/utils.js +54 -113
- package/dist/collection/utils/utils.js.map +1 -1
- package/dist/components/index.js +1298 -11280
- package/dist/components/index.js.map +1 -1
- package/dist/components/my-component.js +2 -3
- package/dist/components/my-component.js.map +1 -1
- package/dist/components/{p-C4l_DOnx.js → p-BctfuDvG.js} +106 -147
- package/dist/components/p-BctfuDvG.js.map +1 -0
- package/dist/components/{p-D0s1Q-3O.js → p-LkDC0SN2.js} +343 -16
- package/dist/components/p-LkDC0SN2.js.map +1 -0
- package/dist/components/pcm-app-chat-modal.js +1 -1
- package/dist/components/pcm-chat-message.js +1 -1
- package/dist/components/pcm-chat-modal.js +19 -34
- package/dist/components/pcm-chat-modal.js.map +1 -1
- package/dist/components/pcm-hr-chat-modal.js +70 -100
- package/dist/components/pcm-hr-chat-modal.js.map +1 -1
- package/dist/components/pcm-jlpx-modal.d.ts +11 -0
- package/dist/components/pcm-jlpx-modal.js +339 -0
- package/dist/components/pcm-jlpx-modal.js.map +1 -0
- package/dist/components/pcm-mnms-modal.js +109 -57
- package/dist/components/pcm-mnms-modal.js.map +1 -1
- package/dist/components/pcm-video-chat-modal.js +74 -99
- package/dist/components/pcm-video-chat-modal.js.map +1 -1
- package/dist/components/pcm-zygh-modal.d.ts +11 -0
- package/dist/components/pcm-zygh-modal.js +330 -0
- package/dist/components/pcm-zygh-modal.js.map +1 -0
- package/dist/esm/index-nVjZGfA8.js +189 -0
- package/dist/esm/index-nVjZGfA8.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-jlpx-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 +6552 -0
- package/dist/esm/pcm-app-chat-modal_7.entry.js.map +1 -0
- package/dist/esm/pcm-chat-modal.entry.js +17 -32
- package/dist/esm/pcm-chat-modal.entry.js.map +1 -1
- package/dist/pcm-agents/index.esm.js +1 -1
- package/dist/pcm-agents/my-component.entry.esm.js.map +1 -1
- package/dist/pcm-agents/p-55417392.entry.js +2 -0
- package/dist/pcm-agents/p-55417392.entry.js.map +1 -0
- package/dist/pcm-agents/p-a698b59f.entry.js +2 -0
- package/dist/pcm-agents/p-a698b59f.entry.js.map +1 -0
- package/dist/pcm-agents/p-f3ca99b4.entry.js +2 -0
- package/dist/pcm-agents/p-f3ca99b4.entry.js.map +1 -0
- package/dist/pcm-agents/p-nVjZGfA8.js +2 -0
- package/dist/pcm-agents/p-nVjZGfA8.js.map +1 -0
- package/dist/pcm-agents/pcm-agents.esm.js +1 -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 +1 -0
- package/dist/pcm-agents/pcm-chat-modal.entry.esm.js.map +1 -1
- package/dist/types/components/pcm-app-chat-modal/pcm-app-chat-modal.d.ts +13 -8
- package/dist/types/components/pcm-chat-message/pcm-chat-message.d.ts +5 -0
- package/dist/types/components/pcm-chat-modal/pcm-chat-modal.d.ts +8 -8
- package/dist/types/components/pcm-hr-chat-modal/pcm-hr-chat-modal.d.ts +6 -12
- package/dist/types/components/pcm-jlpx-modal/pcm-jlpx-modal.d.ts +113 -0
- package/dist/types/components/pcm-mnms-modal/pcm-mnms-modal.d.ts +19 -20
- package/dist/types/components/pcm-video-chat-modal/pcm-video-chat-modal.d.ts +4 -4
- package/dist/types/components/pcm-zygh-modal/pcm-zygh-modal.d.ts +117 -0
- package/dist/types/components.d.ts +429 -80
- package/dist/types/interfaces/chat.d.ts +0 -4
- package/dist/types/utils/utils.d.ts +29 -83
- package/package.json +61 -60
- package/readme.md +307 -307
- package/dist/cjs/index-DfIUl99H.js +0 -11413
- package/dist/cjs/index-DfIUl99H.js.map +0 -1
- package/dist/cjs/pcm-app-chat-modal.pcm-chat-message.pcm-mnms-modal.entry.cjs.js.map +0 -1
- package/dist/cjs/pcm-app-chat-modal_3.cjs.entry.js +0 -3734
- package/dist/cjs/pcm-app-chat-modal_3.cjs.entry.js.map +0 -1
- package/dist/cjs/pcm-hr-chat-modal.cjs.entry.js +0 -1078
- package/dist/cjs/pcm-hr-chat-modal.cjs.entry.js.map +0 -1
- package/dist/cjs/pcm-hr-chat-modal.entry.cjs.js.map +0 -1
- package/dist/cjs/pcm-video-chat-modal.cjs.entry.js +0 -927
- package/dist/cjs/pcm-video-chat-modal.cjs.entry.js.map +0 -1
- package/dist/cjs/pcm-video-chat-modal.entry.cjs.js.map +0 -1
- package/dist/components/p-C4l_DOnx.js.map +0 -1
- package/dist/components/p-CgDy4pJp.js +0 -1244
- package/dist/components/p-CgDy4pJp.js.map +0 -1
- package/dist/components/p-D0s1Q-3O.js.map +0 -1
- package/dist/esm/index-B2EtEi7v.js +0 -11409
- package/dist/esm/index-B2EtEi7v.js.map +0 -1
- package/dist/esm/pcm-app-chat-modal.pcm-chat-message.pcm-mnms-modal.entry.js.map +0 -1
- package/dist/esm/pcm-app-chat-modal_3.entry.js +0 -3730
- package/dist/esm/pcm-app-chat-modal_3.entry.js.map +0 -1
- package/dist/esm/pcm-hr-chat-modal.entry.js +0 -1076
- package/dist/esm/pcm-hr-chat-modal.entry.js.map +0 -1
- package/dist/esm/pcm-video-chat-modal.entry.js +0 -925
- package/dist/esm/pcm-video-chat-modal.entry.js.map +0 -1
- package/dist/pcm-agents/p-0ddd5c47.entry.js +0 -2
- package/dist/pcm-agents/p-0ddd5c47.entry.js.map +0 -1
- package/dist/pcm-agents/p-5f624943.entry.js +0 -2
- package/dist/pcm-agents/p-5f624943.entry.js.map +0 -1
- package/dist/pcm-agents/p-6c07f155.entry.js +0 -2
- package/dist/pcm-agents/p-6c07f155.entry.js.map +0 -1
- package/dist/pcm-agents/p-9a1fb6ca.entry.js +0 -2
- package/dist/pcm-agents/p-9a1fb6ca.entry.js.map +0 -1
- package/dist/pcm-agents/p-B2EtEi7v.js +0 -146
- package/dist/pcm-agents/p-B2EtEi7v.js.map +0 -1
- package/dist/pcm-agents/p-e21bc169.entry.js +0 -2
- package/dist/pcm-agents/p-e21bc169.entry.js.map +0 -1
- package/dist/pcm-agents/pcm-app-chat-modal.pcm-chat-message.pcm-mnms-modal.entry.esm.js.map +0 -1
- package/dist/pcm-agents/pcm-hr-chat-modal.entry.esm.js.map +0 -1
- package/dist/pcm-agents/pcm-video-chat-modal.entry.esm.js.map +0 -1
|
@@ -1,3730 +0,0 @@
|
|
|
1
|
-
import { r as registerInstance, c as createEvent, g as getElement, h } from './index-DaMwkrv5.js';
|
|
2
|
-
import { a as sendSSERequest, s as sendHttpRequest } from './index-B2EtEi7v.js';
|
|
3
|
-
|
|
4
|
-
const pcmAppChatModalCss = ":host{display:block}.modal-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background-color:rgba(0, 0, 0, 0.5);display:flex;justify-content:center;align-items:center;overflow-y:auto;padding:20px;z-index:1000}.fullscreen-overlay{padding:0}.modal-container{background:white;border-radius:8px;width:100%;max-width:900px;display:flex;flex-direction:column;position:relative;margin:auto}.modal-container.fullscreen{width:100vw;max-width:none;height:100%;border-radius:0;margin:0;display:flex;flex-direction:column;height:100vh;max-height:100vh}.modal-container.fullscreen>div:not(.modal-header):not(.initial-upload){display:flex;flex-direction:column;flex:1;overflow:hidden;height:100%}.pc-layout{width:80%;max-width:800px;min-width:320px;min-height:400px}.mobile-layout{width:100%;height:100%;border-radius:0}.video-preview.placeholder{display:flex;justify-content:center;align-items:center;background:#EAEAEA}.placeholder-status{color:#00000066}.waiting-message p{margin:0;font-size:16px;color:white;font-weight:500}.recording-container{width:100%;display:flex;flex-direction:column;align-items:center}.video-area{width:100%;display:flex;flex-direction:column;align-items:center}.stop-recording-button{width:100%;height:100%;font-size:16px;background:#f44336;border-radius:6px;color:white;border:none;cursor:pointer}.stop-recording-button:hover{background:#d32f2f}.play-audio-container{width:100%;height:100%;display:flex;justify-content:center;align-items:center}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:4px 16px;height:50px;border-bottom:1px solid #e8e8e8;flex-shrink:0;}.header-left{display:flex;align-items:center;gap:8px}.header-icon{width:24px;height:24px}.close-button{background:transparent;border:none;cursor:pointer;padding:8px;display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:4px}.close-button:hover{background-color:rgba(0, 0, 0, 0.04)}.close-button span{font-size:24px;line-height:1;color:#999}.close-button:hover span{color:#666}.chat-history{position:relative;flex:1;overflow-y:auto;padding:20px;scroll-behavior:smooth;min-height:400px;background:url(https://pcm-resource-1312611446.cos.ap-guangzhou.myqcloud.com/web/sdk/chat_bg.png);background-size:100%}.fullscreen .chat-history{height:auto;flex:1 1 auto}.message-input{padding:16px;border-top:1px solid #eee;display:flex;gap:8px;align-items:center}.message-input input{flex:1;padding:8px 12px;border:1px solid #ddd;border-radius:4px;outline:none;transition:border-color 0.2s ease}.message-input input:focus{border-color:#bbb}.message{margin-bottom:16px;opacity:1;transition:opacity 0.3s ease}.message-content{max-width:70%;padding:8px 12px;border-radius:8px;word-break:break-word}.message-content p{margin:0;word-break:break-word}.user-message{display:flex;justify-content:flex-end}.agent-message{display:flex;justify-content:flex-start}.user-message .message-content{background-color:#007bff;color:white}.agent-message .message-content{background-color:#f1f1f1}.message-time{font-size:12px;color:#999;margin-top:4px;display:block}.send-button{background-color:#1890ff;color:white;border:none;border-radius:4px;padding:8px 16px;cursor:pointer;font-weight:500}.send-button:disabled{background-color:#ccc;cursor:not-allowed}.empty-state{display:flex;justify-content:center;align-items:center;height:100%;color:#999;text-align:center}.loading-container{position:absolute;top:0;left:0;right:0;bottom:0;display:flex;flex-direction:column;justify-content:center;align-items:center;background-color:rgba(255, 255, 255, 0.98);z-index:1;opacity:1;transition:opacity 0.3s ease}.loading-container p{margin-top:16px;color:#666;font-size:14px}.loading-spinner{width:40px;height:40px;border:3px solid #f3f3f3;border-top:3px solid #1890ff;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.messages-wrapper{width:100%;min-height:100%;display:flex;flex-direction:column;justify-content:flex-end}.messages-wrapper.has-overflow{justify-content:flex-start}.suggested-questions{display:flex;flex-direction:column;gap:8px;padding:16px}.suggested-question{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background-color:#f3f4f6;border-radius:4px;cursor:pointer;font-size:14px;color:#374151;transition:background-color 0.2s}.suggested-question:hover{background-color:#e5e7eb}.arrow-right{margin-left:8px}.loading-suggestions{display:flex;justify-content:center;padding:16px}.loading-spinner-small{width:20px;height:20px;border:2px solid #e5e7eb;border-top-color:#6b7280;border-radius:50%;animation:spin 1s linear infinite}.upload-button{background:transparent;border:none;cursor:pointer;padding:8px;display:flex;align-items:center;justify-content:center;color:#666;border-radius:4px;transition:background-color 0.2s}.upload-button:hover{background-color:rgba(0, 0, 0, 0.04)}.upload-button svg{width:20px;height:20px}.file-input{display:none}.selected-file{font-size:12px;color:#666;margin-left:8px;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.input-wrapper{flex:1;display:flex;align-items:center;border:1px solid #ddd;border-radius:4px;padding:0 4px;background:white}.input-wrapper input{border:none;flex:1;padding:8px;outline:none}.input-wrapper:focus-within{border-color:#bbb}.file-preview{padding:8px 16px;border-top:1px solid #eee;background-color:#f9f9f9}.file-info{display:flex;align-items:center;justify-content:space-between;padding:8px;background:white;border:1px solid #e8e8e8;border-radius:4px}.recording-section{border-top:1px solid #eee;display:flex;flex-direction:column;align-items:center;padding:20px;border-radius:14px 14px 0 0;flex:0 0 auto}.recording-section .video-preview{width:100%;height:200px;max-width:400px;position:relative;margin-bottom:10px;border:1px solid #ddd;border-radius:12px;overflow:hidden}.recording-section video{width:100%;height:100%;object-fit:cover}.recording-status{position:absolute;top:10px;left:10px;background-color:rgba(0, 0, 0, 0.6);color:white;padding:4px 8px;border-radius:4px;display:flex;align-items:center;gap:5px;font-size:0.8rem;z-index:2}.recording-status .recording-dot{display:inline-block;width:10px;height:10px;background-color:red;border-radius:50%;margin-right:5px;animation:blink 1s infinite}.recording-status.warning{color:#ff4d4f;animation:blink 1s infinite}@keyframes blink{0%{opacity:1}50%{opacity:0.5}100%{opacity:1}}.recording-section .stop-recording-button{background-color:#f44336;color:white;border:none;cursor:pointer;font-weight:bold}.recording-section .stop-recording-button:hover{background-color:#d32f2f}.fullscreen{width:100vw;border-radius:0;height:100vh;display:flex;flex-direction:column;overflow-y:auto}.recording-controls{margin-top:10px;height:53px;width:100%;max-width:400px;display:flex;justify-content:center}.recording-controls .waiting-message{text-align:center;color:white;font-size:1rem;background-image:linear-gradient(100deg, #4A9FFF 0%, #1058FF 100%);border-radius:6px;box-shadow:0 2px 8px rgba(0, 0, 0, 0.15);width:95%;display:flex;justify-content:center;align-items:center;cursor:pointer}.recording-controls .waiting-message.loading{background:#faad14}.recording-controls .waiting-message p{margin:0;font-size:16px;color:white;font-weight:500}.recording-controls .stop-recording-button{background-color:#dc3545;color:white;border:none;cursor:pointer;font-size:1rem}.recording-controls .stop-recording-button:hover{background-color:#c82333}.recording-controls .stop-recording-button.disabled{background:#ccc;cursor:not-allowed}.recording-controls .stop-recording-button.disabled:hover{background:#ccc}.progress-container{display:flex;justify-content:space-between;align-items:center;width:100%;max-width:400px;margin-top:10px;padding:0 5px}.progress-bar-container{height:4px;background-color:#E5E5E5;border-radius:2px;overflow:hidden;margin-right:10px;width:75px}.progress-bar{height:100%;background-image:linear-gradient(111deg, #4A9FFF 0%, #1058FF 100%);border-radius:2px;transition:width 0.3s ease}.progress-text{font-size:14px;color:#666;white-space:nowrap}.text-input-area{display:flex;flex-direction:column;width:100%;height:100%;padding:16px;border-radius:8px;border:none;}.text-answer-input{flex:1;min-height:80px;padding:12px 12px 0px 12px;border:1px solid #ddd;border-radius:8px 8px 0 0;resize:none;font-size:16px;background-color:#fff;border-bottom:none;outline:none;}.input-toolbar{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;background-color:#fff;border:1px solid #ddd;border-top:none;border-radius:0 0 8px 8px}.text-answer-input:focus{border-color:rgb(74, 144, 226);border-bottom:none}.text-answer-input:focus+.input-toolbar{border-color:rgb(74, 144, 226);border-top:none}.toolbar-actions{display:flex;gap:8px}.toolbar-button{background:transparent;border:none;color:#666;padding:4px;cursor:pointer;border-radius:4px}.toolbar-button:hover{background-color:#f0f0f0}.submit-text-button{padding:6px 16px;background-color:#4a90e2;color:white;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:background-color 0.2s}.submit-text-button:hover:not(.disabled){background-color:#3a7bc8}.submit-text-button.disabled{background-color:#b3b3b3;cursor:not-allowed}@media screen and (max-width: 768px){.pc-layout{width:95%;}.modal-overlay{padding:0}.modal-container.fullscreen{height:-webkit-fill-available;max-height:-webkit-fill-available;padding:env(safe-area-inset-top) 0 env(safe-area-inset-bottom)}.modal-container.mobile-layout{width:100%;height:100vh;max-height:100vh;min-height:100vh;border-radius:0;margin:0;display:flex;flex-direction:column}.chat-history{min-height:200px}}";
|
|
5
|
-
|
|
6
|
-
const ChatAPPModal = class {
|
|
7
|
-
constructor(hostRef) {
|
|
8
|
-
registerInstance(this, hostRef);
|
|
9
|
-
this.modalClosed = createEvent(this, "modalClosed");
|
|
10
|
-
this.streamComplete = createEvent(this, "streamComplete");
|
|
11
|
-
this.conversationStart = createEvent(this, "conversationStart");
|
|
12
|
-
this.interviewComplete = createEvent(this, "interviewComplete");
|
|
13
|
-
this.recordingError = createEvent(this, "recordingError");
|
|
14
|
-
this.recordingStatusChange = createEvent(this, "recordingStatusChange");
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* 模态框标题
|
|
18
|
-
*/
|
|
19
|
-
modalTitle = '在线客服';
|
|
20
|
-
/**
|
|
21
|
-
* API鉴权密钥
|
|
22
|
-
*/
|
|
23
|
-
apiKey = '';
|
|
24
|
-
/**
|
|
25
|
-
* 是否显示聊天模态框
|
|
26
|
-
*/
|
|
27
|
-
isOpen = false;
|
|
28
|
-
/**
|
|
29
|
-
* 聊天消息历史
|
|
30
|
-
*/
|
|
31
|
-
messages = [];
|
|
32
|
-
/**
|
|
33
|
-
* 当点击模态框关闭时触发
|
|
34
|
-
*/
|
|
35
|
-
modalClosed;
|
|
36
|
-
/**
|
|
37
|
-
* 应用图标URL
|
|
38
|
-
*/
|
|
39
|
-
icon;
|
|
40
|
-
/**
|
|
41
|
-
* 聊天框的页面层级
|
|
42
|
-
*/
|
|
43
|
-
zIndex = 1000;
|
|
44
|
-
/**
|
|
45
|
-
* 是否展示顶部标题栏
|
|
46
|
-
*/
|
|
47
|
-
isShowHeader = true;
|
|
48
|
-
/**
|
|
49
|
-
* 是否展示右上角的关闭按钮
|
|
50
|
-
*/
|
|
51
|
-
isNeedClose = true;
|
|
52
|
-
/**
|
|
53
|
-
* 会话ID
|
|
54
|
-
*/
|
|
55
|
-
conversationId;
|
|
56
|
-
/**
|
|
57
|
-
* 当前助手回复的消息
|
|
58
|
-
*/
|
|
59
|
-
currentAssistantMessage = '';
|
|
60
|
-
/**
|
|
61
|
-
* 是否正在加载回复
|
|
62
|
-
*/
|
|
63
|
-
isLoading = false;
|
|
64
|
-
/**
|
|
65
|
-
* 当前正在流式输出的消息
|
|
66
|
-
*/
|
|
67
|
-
currentStreamingMessage = null;
|
|
68
|
-
// 添加新的状态控制
|
|
69
|
-
shouldAutoScroll = true;
|
|
70
|
-
isLoadingHistory = false;
|
|
71
|
-
get hostElement() { return getElement(this); }
|
|
72
|
-
/**
|
|
73
|
-
* 一轮对话结束时的回调
|
|
74
|
-
*/
|
|
75
|
-
streamComplete;
|
|
76
|
-
/**
|
|
77
|
-
* 新会话开始的回调,只会在一轮对话开始时触发一次
|
|
78
|
-
*/
|
|
79
|
-
conversationStart;
|
|
80
|
-
selectedFile = null;
|
|
81
|
-
isUploading = false;
|
|
82
|
-
uploadedFileInfo = [];
|
|
83
|
-
/**
|
|
84
|
-
* 默认查询文本
|
|
85
|
-
*/
|
|
86
|
-
defaultQuery = '';
|
|
87
|
-
// 添加新的状态
|
|
88
|
-
showInitialUpload = false;
|
|
89
|
-
// 添加视频录制相关状态
|
|
90
|
-
isRecording = false;
|
|
91
|
-
recordingStream = null;
|
|
92
|
-
recordedBlob = null;
|
|
93
|
-
mediaRecorder = null;
|
|
94
|
-
recordingTimeLeft = 0;
|
|
95
|
-
showRecordingUI = false;
|
|
96
|
-
recordingTimer = null;
|
|
97
|
-
recordingStartTime = 0;
|
|
98
|
-
recordingMaxTime = 120; // 最大录制时间(秒)
|
|
99
|
-
waitingToRecord = false;
|
|
100
|
-
waitingTimer = null;
|
|
101
|
-
waitingTimeLeft = 10; // 等待时间(秒)
|
|
102
|
-
// 添加一个新的私有属性来存储视频元素的引用
|
|
103
|
-
videoRef = null;
|
|
104
|
-
/**
|
|
105
|
-
* 控制对话轮数
|
|
106
|
-
*/
|
|
107
|
-
totalQuestions = 2;
|
|
108
|
-
/**
|
|
109
|
-
* 当前轮数
|
|
110
|
-
*/
|
|
111
|
-
currentQuestionNumber = 0;
|
|
112
|
-
/**
|
|
113
|
-
* 当聊天完成时触发
|
|
114
|
-
*/
|
|
115
|
-
interviewComplete;
|
|
116
|
-
SCROLL_THRESHOLD = 30;
|
|
117
|
-
/**
|
|
118
|
-
* 视频录制最大时长(秒)
|
|
119
|
-
*/
|
|
120
|
-
maxRecordingTime = 120;
|
|
121
|
-
/**
|
|
122
|
-
* 录制倒计时提醒时间(秒)
|
|
123
|
-
* 当剩余时间小于此值时,显示倒计时警告
|
|
124
|
-
*/
|
|
125
|
-
countdownWarningTime = 30;
|
|
126
|
-
showCountdownWarning = false;
|
|
127
|
-
/**
|
|
128
|
-
* 是否以全屏模式打开
|
|
129
|
-
*/
|
|
130
|
-
fullscreen = false;
|
|
131
|
-
// 添加新的状态来跟踪视频上传
|
|
132
|
-
isUploadingVideo = false;
|
|
133
|
-
// 添加新的状态和属性
|
|
134
|
-
isPlayingAudio = false;
|
|
135
|
-
audioUrl = null;
|
|
136
|
-
audioElement = null;
|
|
137
|
-
/**
|
|
138
|
-
* 录制错误事件
|
|
139
|
-
*/
|
|
140
|
-
recordingError;
|
|
141
|
-
/**
|
|
142
|
-
* 录制状态变化事件
|
|
143
|
-
*/
|
|
144
|
-
recordingStatusChange;
|
|
145
|
-
/**
|
|
146
|
-
* 是否自动播放语音问题
|
|
147
|
-
*/
|
|
148
|
-
enableVoice = true;
|
|
149
|
-
/**
|
|
150
|
-
* 是否显示题干内容
|
|
151
|
-
* 1: 显示题干内容
|
|
152
|
-
* 0: 不显示题干内容
|
|
153
|
-
*/
|
|
154
|
-
displayContentStatus = "1";
|
|
155
|
-
/**
|
|
156
|
-
* 用户ID
|
|
157
|
-
*/
|
|
158
|
-
userId = '';
|
|
159
|
-
/**
|
|
160
|
-
* 面试模式
|
|
161
|
-
* video: 视频面试模式
|
|
162
|
-
* text: 文字面试模式
|
|
163
|
-
*/
|
|
164
|
-
interviewMode = 'video';
|
|
165
|
-
// 添加文字输入相关状态
|
|
166
|
-
textAnswer = '';
|
|
167
|
-
isSubmittingText = false;
|
|
168
|
-
/**
|
|
169
|
-
* 自定义智能体inputs输入参数
|
|
170
|
-
*/
|
|
171
|
-
customInputs = {};
|
|
172
|
-
handleClose = () => {
|
|
173
|
-
this.stopRecording();
|
|
174
|
-
this.modalClosed.emit();
|
|
175
|
-
};
|
|
176
|
-
async sendMessageToAPI(message, videoUrl) {
|
|
177
|
-
this.isLoading = true;
|
|
178
|
-
let answer = '';
|
|
179
|
-
let llmText = ''; // 添加变量存储 LLMText
|
|
180
|
-
const now = new Date();
|
|
181
|
-
const time = `${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`;
|
|
182
|
-
// 修改消息处理逻辑,移除文件上传相关代码
|
|
183
|
-
const queryText = message.trim() || '请开始';
|
|
184
|
-
// 检查是否是最后一题
|
|
185
|
-
const isLastQuestion = this.currentQuestionNumber >= this.totalQuestions;
|
|
186
|
-
// 创建新的消息对象
|
|
187
|
-
const newMessage = {
|
|
188
|
-
id: `temp-${Date.now()}`, // 消息唯一标识
|
|
189
|
-
time: time, // 消息时间
|
|
190
|
-
query: queryText, // 用户输入的消息内容
|
|
191
|
-
answer: '',
|
|
192
|
-
isStreaming: true, // 是否正在流式输出
|
|
193
|
-
conversation_id: this.conversationId, // 会话ID
|
|
194
|
-
inputs: {}, // 输入参数
|
|
195
|
-
status: "normal", // 消息状态
|
|
196
|
-
error: null // 错误信息
|
|
197
|
-
};
|
|
198
|
-
// 设置当前流式消息
|
|
199
|
-
this.currentStreamingMessage = newMessage;
|
|
200
|
-
this.shouldAutoScroll = true;
|
|
201
|
-
// 滚动到底部
|
|
202
|
-
this.scrollToBottom();
|
|
203
|
-
// 如果是最后一题,直接显示结束消息并完成面试
|
|
204
|
-
if (isLastQuestion) {
|
|
205
|
-
this.messages = [...this.messages, newMessage];
|
|
206
|
-
this.currentStreamingMessage = null;
|
|
207
|
-
this.isLoading = false;
|
|
208
|
-
await this.completeInterview();
|
|
209
|
-
this.interviewComplete.emit({
|
|
210
|
-
conversation_id: this.conversationId,
|
|
211
|
-
total_questions: this.totalQuestions
|
|
212
|
-
});
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
// 准备请求数据
|
|
216
|
-
const requestData = {
|
|
217
|
-
response_mode: 'streaming',
|
|
218
|
-
conversation_id: this.conversationId,
|
|
219
|
-
query: queryText,
|
|
220
|
-
user: this.userId // 使用传入的 userId
|
|
221
|
-
};
|
|
222
|
-
// 合并基本输入参数和自定义输入参数
|
|
223
|
-
requestData.inputs = {
|
|
224
|
-
// 合并自定义输入参数
|
|
225
|
-
...this.customInputs
|
|
226
|
-
};
|
|
227
|
-
// 如果有视频URL,添加到inputs中
|
|
228
|
-
if (videoUrl) {
|
|
229
|
-
requestData.inputs.video_url = videoUrl;
|
|
230
|
-
}
|
|
231
|
-
await sendSSERequest({
|
|
232
|
-
url: `https://pcm_api.ylzhaopin.com/external/v1/chat/chat-messages`,
|
|
233
|
-
method: 'POST',
|
|
234
|
-
headers: {
|
|
235
|
-
'authorization': 'Bearer ' + this.apiKey
|
|
236
|
-
},
|
|
237
|
-
data: requestData,
|
|
238
|
-
onMessage: (data) => {
|
|
239
|
-
console.log('收到Stream数据:', data);
|
|
240
|
-
if (data.conversation_id && !this.conversationId) {
|
|
241
|
-
this.conversationId = data.conversation_id;
|
|
242
|
-
this.conversationStart.emit({
|
|
243
|
-
conversation_id: data.conversation_id,
|
|
244
|
-
event: data.event,
|
|
245
|
-
message_id: data.message_id,
|
|
246
|
-
id: data.id,
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
// 检查是否有 node_finished 事件和 LLMText
|
|
250
|
-
if (data.event === 'node_finished' && data.data.inputs && data.data.inputs.LLMText) {
|
|
251
|
-
llmText = data.data.inputs.LLMText;
|
|
252
|
-
console.log('获取到 LLMText:', llmText);
|
|
253
|
-
}
|
|
254
|
-
if (data.event === 'message') {
|
|
255
|
-
if (data.event === 'agent_message' || data.event === 'message') {
|
|
256
|
-
if (data.answer) {
|
|
257
|
-
answer += data.answer;
|
|
258
|
-
const updatedMessage = {
|
|
259
|
-
...this.currentStreamingMessage,
|
|
260
|
-
answer,
|
|
261
|
-
isStreaming: true
|
|
262
|
-
};
|
|
263
|
-
this.currentStreamingMessage = updatedMessage;
|
|
264
|
-
this.scrollToBottom();
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
if (data.event === "message_end") {
|
|
269
|
-
this.streamComplete.emit({
|
|
270
|
-
conversation_id: data.conversation_id || '',
|
|
271
|
-
event: data.event,
|
|
272
|
-
message_id: data.message_id,
|
|
273
|
-
id: data.id,
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
},
|
|
277
|
-
onError: (error) => {
|
|
278
|
-
console.error('发生错误:', error);
|
|
279
|
-
alert(error instanceof Error ? error.message : '消息发送失败,请稍后再试');
|
|
280
|
-
this.messages = [...this.messages, {
|
|
281
|
-
...newMessage,
|
|
282
|
-
answer: '抱歉,发生了错误,请稍后再试。',
|
|
283
|
-
error: error,
|
|
284
|
-
isStreaming: false
|
|
285
|
-
}];
|
|
286
|
-
this.currentStreamingMessage = null;
|
|
287
|
-
this.isLoading = false;
|
|
288
|
-
},
|
|
289
|
-
onComplete: async () => {
|
|
290
|
-
this.isLoading = false;
|
|
291
|
-
// 获取最新的AI回复内容
|
|
292
|
-
const latestAIMessage = this.currentStreamingMessage;
|
|
293
|
-
// 更新消息列表
|
|
294
|
-
this.messages = [...this.messages, this.currentStreamingMessage];
|
|
295
|
-
this.currentStreamingMessage = null;
|
|
296
|
-
// 如果是初始消息或"下一题"消息,增加题目计数
|
|
297
|
-
if (message === "下一题" || this.currentQuestionNumber === 0) {
|
|
298
|
-
this.currentQuestionNumber++;
|
|
299
|
-
}
|
|
300
|
-
if (latestAIMessage && latestAIMessage.answer) {
|
|
301
|
-
// 优先使用 LLMText,如果没有则使用 answer
|
|
302
|
-
const textForSynthesis = llmText || latestAIMessage.answer;
|
|
303
|
-
if (textForSynthesis) {
|
|
304
|
-
// 合成语音
|
|
305
|
-
const audioUrl = await this.synthesizeAudio(textForSynthesis);
|
|
306
|
-
if (this.enableVoice) {
|
|
307
|
-
// 自动播放语音
|
|
308
|
-
await this.playAudio(audioUrl);
|
|
309
|
-
// 自动播放模式下,播放完成后立即开始等待录制
|
|
310
|
-
this.startWaitingToRecord();
|
|
311
|
-
}
|
|
312
|
-
else {
|
|
313
|
-
// 只保存音频URL,不自动播放
|
|
314
|
-
this.audioUrl = audioUrl;
|
|
315
|
-
// 非自动播放模式下,不立即开始等待录制
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
// 监听滚动事件,用于控制聊天历史记录的自动滚动行为。
|
|
323
|
-
handleScroll = () => {
|
|
324
|
-
const chatHistory = this.hostElement.shadowRoot?.querySelector('.chat-history');
|
|
325
|
-
if (!chatHistory)
|
|
326
|
-
return;
|
|
327
|
-
const { scrollTop, scrollHeight, clientHeight } = chatHistory;
|
|
328
|
-
const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
|
|
329
|
-
// 更新是否应该自动滚动的状态
|
|
330
|
-
this.shouldAutoScroll = distanceFromBottom <= this.SCROLL_THRESHOLD;
|
|
331
|
-
};
|
|
332
|
-
scrollToBottom() {
|
|
333
|
-
if (!this.shouldAutoScroll)
|
|
334
|
-
return;
|
|
335
|
-
const chatHistory = this.hostElement.shadowRoot?.querySelector('.chat-history');
|
|
336
|
-
if (chatHistory && this.isOpen) {
|
|
337
|
-
// 强制浏览器重新计算布局
|
|
338
|
-
chatHistory.scrollTop = chatHistory.scrollHeight;
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
// 添加 componentDidRender 生命周期方法,用于在组件渲染后滚动到底部
|
|
342
|
-
componentDidRender() {
|
|
343
|
-
if (this.isLoadingHistory || (this.shouldAutoScroll && this.isOpen)) {
|
|
344
|
-
const chatHistory = this.hostElement.shadowRoot?.querySelector('.chat-history');
|
|
345
|
-
if (chatHistory) {
|
|
346
|
-
chatHistory.scrollTop = chatHistory.scrollHeight;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
// 修改 loadHistoryMessages 方法
|
|
351
|
-
async loadHistoryMessages() {
|
|
352
|
-
if (!this.conversationId)
|
|
353
|
-
return;
|
|
354
|
-
this.isLoadingHistory = true;
|
|
355
|
-
console.log('加载历史消息...');
|
|
356
|
-
try {
|
|
357
|
-
await sendHttpRequest({
|
|
358
|
-
url: `https://pcm_api.ylzhaopin.com/external/v1/chat/messages`,
|
|
359
|
-
method: 'GET',
|
|
360
|
-
headers: {
|
|
361
|
-
'authorization': 'Bearer ' + this.apiKey
|
|
362
|
-
},
|
|
363
|
-
data: {
|
|
364
|
-
conversation_id: this.conversationId,
|
|
365
|
-
limit: 20
|
|
366
|
-
},
|
|
367
|
-
onMessage: (data) => {
|
|
368
|
-
if (data.data) {
|
|
369
|
-
const historyData = data.data || [];
|
|
370
|
-
const formattedMessages = historyData.map(msg => {
|
|
371
|
-
const time = new Date(msg.created_at * 1000);
|
|
372
|
-
const hours = time.getHours().toString().padStart(2, '0');
|
|
373
|
-
const minutes = time.getMinutes().toString().padStart(2, '0');
|
|
374
|
-
const timeStr = `${hours}:${minutes}`;
|
|
375
|
-
// 创建新的消息对象,不包含 inputs 字段
|
|
376
|
-
const { inputs, ...msgWithoutInputs } = msg;
|
|
377
|
-
return {
|
|
378
|
-
...msgWithoutInputs,
|
|
379
|
-
time: timeStr,
|
|
380
|
-
isStreaming: false,
|
|
381
|
-
status: msg.status === 'error' ? 'error' : 'normal'
|
|
382
|
-
};
|
|
383
|
-
});
|
|
384
|
-
this.messages = formattedMessages;
|
|
385
|
-
this.isLoadingHistory = false;
|
|
386
|
-
requestAnimationFrame(() => {
|
|
387
|
-
this.shouldAutoScroll = true;
|
|
388
|
-
this.scrollToBottom();
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
else {
|
|
392
|
-
this.isLoadingHistory = false;
|
|
393
|
-
}
|
|
394
|
-
},
|
|
395
|
-
onError: (error) => {
|
|
396
|
-
console.error('加载历史消息失败:', error);
|
|
397
|
-
alert(error instanceof Error ? error.message : '加载历史消息失败,请刷新重试');
|
|
398
|
-
this.isLoadingHistory = false;
|
|
399
|
-
},
|
|
400
|
-
onComplete: () => {
|
|
401
|
-
this.isLoadingHistory = false;
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
catch (error) {
|
|
406
|
-
console.error('加载历史消息失败:', error);
|
|
407
|
-
alert(error instanceof Error ? error.message : '加载历史消息失败,请刷新重试');
|
|
408
|
-
this.isLoadingHistory = false;
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
// 修改 componentDidLoad 生命周期方法
|
|
412
|
-
componentDidLoad() {
|
|
413
|
-
// 添加滚动事件监听器
|
|
414
|
-
const chatHistory = this.hostElement.shadowRoot?.querySelector('.chat-history');
|
|
415
|
-
if (chatHistory) {
|
|
416
|
-
chatHistory.addEventListener('scroll', this.handleScroll);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
// 添加 componentWillLoad 生命周期方法
|
|
420
|
-
componentWillLoad() {
|
|
421
|
-
// 如果组件加载时已经是打开状态,则直接开始对话
|
|
422
|
-
if (this.isOpen) {
|
|
423
|
-
if (this.conversationId) {
|
|
424
|
-
// 在下一个事件循环中加载历史消息,避免在componentWillLoad中进行异步操作
|
|
425
|
-
setTimeout(() => this.loadHistoryMessages(), 0);
|
|
426
|
-
}
|
|
427
|
-
else {
|
|
428
|
-
// 在下一个事件循环中发送初始消息,避免在componentWillLoad中进行异步操作
|
|
429
|
-
setTimeout(() => this.sendMessageToAPI(this.defaultQuery), 0);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
// 开始等待录制
|
|
434
|
-
startWaitingToRecord() {
|
|
435
|
-
// 清除可能存在的计时器
|
|
436
|
-
if (this.waitingTimer) {
|
|
437
|
-
clearInterval(this.waitingTimer);
|
|
438
|
-
}
|
|
439
|
-
if (this.recordingTimer) {
|
|
440
|
-
clearInterval(this.recordingTimer);
|
|
441
|
-
}
|
|
442
|
-
this.waitingToRecord = true;
|
|
443
|
-
this.waitingTimeLeft = 10;
|
|
444
|
-
this.waitingTimer = setInterval(() => {
|
|
445
|
-
this.waitingTimeLeft--;
|
|
446
|
-
if (this.waitingTimeLeft <= 0) {
|
|
447
|
-
clearInterval(this.waitingTimer);
|
|
448
|
-
this.waitingTimer = null;
|
|
449
|
-
this.waitingToRecord = false;
|
|
450
|
-
this.startRecording();
|
|
451
|
-
}
|
|
452
|
-
}, 1000);
|
|
453
|
-
}
|
|
454
|
-
// 开始录制视频
|
|
455
|
-
async startRecording() {
|
|
456
|
-
try {
|
|
457
|
-
const stream = await navigator.mediaDevices.getUserMedia({
|
|
458
|
-
audio: true,
|
|
459
|
-
video: {
|
|
460
|
-
width: { ideal: 1280 },
|
|
461
|
-
height: { ideal: 720 },
|
|
462
|
-
frameRate: { ideal: 30 }
|
|
463
|
-
}
|
|
464
|
-
});
|
|
465
|
-
this.recordingStream = stream;
|
|
466
|
-
this.showRecordingUI = true;
|
|
467
|
-
this.showCountdownWarning = false;
|
|
468
|
-
// 重置视频引用
|
|
469
|
-
this.videoRef = null;
|
|
470
|
-
// 确保视频元素获取到流
|
|
471
|
-
this.setupVideoPreview(stream);
|
|
472
|
-
// 检测浏览器支持的MIME类型
|
|
473
|
-
const mimeType = this.getSupportedMimeType();
|
|
474
|
-
// 创建MediaRecorder实例
|
|
475
|
-
let mediaRecorder;
|
|
476
|
-
try {
|
|
477
|
-
mediaRecorder = new MediaRecorder(stream, {
|
|
478
|
-
mimeType: mimeType
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
catch (e) {
|
|
482
|
-
// 如果指定MIME类型失败,尝试使用默认设置
|
|
483
|
-
console.warn('指定的MIME类型不受支持,使用默认设置:', e);
|
|
484
|
-
try {
|
|
485
|
-
mediaRecorder = new MediaRecorder(stream);
|
|
486
|
-
}
|
|
487
|
-
catch (recorderError) {
|
|
488
|
-
// 通知父组件录制器创建失败
|
|
489
|
-
this.recordingError.emit({
|
|
490
|
-
type: 'recorder_creation_failed',
|
|
491
|
-
message: '无法创建媒体录制器,您的浏览器可能不支持此功能',
|
|
492
|
-
details: recorderError
|
|
493
|
-
});
|
|
494
|
-
this.showRecordingUI = false;
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
this.mediaRecorder = mediaRecorder;
|
|
499
|
-
const chunks = [];
|
|
500
|
-
mediaRecorder.ondataavailable = (event) => {
|
|
501
|
-
if (event.data.size > 0) {
|
|
502
|
-
chunks.push(event.data);
|
|
503
|
-
}
|
|
504
|
-
};
|
|
505
|
-
mediaRecorder.onerror = (event) => {
|
|
506
|
-
// 通知父组件录制过程中发生错误
|
|
507
|
-
this.recordingError.emit({
|
|
508
|
-
type: 'recording_error',
|
|
509
|
-
message: '录制过程中发生错误',
|
|
510
|
-
details: event
|
|
511
|
-
});
|
|
512
|
-
this.stopRecording();
|
|
513
|
-
};
|
|
514
|
-
mediaRecorder.onstop = () => {
|
|
515
|
-
try {
|
|
516
|
-
// 根据实际使用的MIME类型创建Blob
|
|
517
|
-
const blobType = mimeType || 'video/mp4';
|
|
518
|
-
const blob = new Blob(chunks, { type: blobType });
|
|
519
|
-
if (blob.size === 0) {
|
|
520
|
-
// 通知父组件录制的视频为空
|
|
521
|
-
this.recordingError.emit({
|
|
522
|
-
type: 'empty_recording',
|
|
523
|
-
message: '录制的视频为空'
|
|
524
|
-
});
|
|
525
|
-
this.showRecordingUI = false;
|
|
526
|
-
return;
|
|
527
|
-
}
|
|
528
|
-
this.recordedBlob = blob;
|
|
529
|
-
// 通知父组件录制已完成
|
|
530
|
-
this.recordingStatusChange.emit({
|
|
531
|
-
status: 'stopped',
|
|
532
|
-
details: {
|
|
533
|
-
duration: Math.floor((Date.now() - this.recordingStartTime) / 1000),
|
|
534
|
-
size: blob.size,
|
|
535
|
-
type: blob.type
|
|
536
|
-
}
|
|
537
|
-
});
|
|
538
|
-
this.uploadRecordedVideo();
|
|
539
|
-
}
|
|
540
|
-
catch (error) {
|
|
541
|
-
// 通知父组件处理录制视频时出错
|
|
542
|
-
this.recordingError.emit({
|
|
543
|
-
type: 'processing_error',
|
|
544
|
-
message: '处理录制视频时出错',
|
|
545
|
-
details: error
|
|
546
|
-
});
|
|
547
|
-
this.showRecordingUI = false;
|
|
548
|
-
}
|
|
549
|
-
};
|
|
550
|
-
// 开始录制
|
|
551
|
-
try {
|
|
552
|
-
mediaRecorder.start();
|
|
553
|
-
this.isRecording = true;
|
|
554
|
-
this.recordingStartTime = Date.now();
|
|
555
|
-
this.recordingTimeLeft = this.maxRecordingTime;
|
|
556
|
-
// 通知父组件录制已开始
|
|
557
|
-
this.recordingStatusChange.emit({
|
|
558
|
-
status: 'started',
|
|
559
|
-
details: {
|
|
560
|
-
maxDuration: this.maxRecordingTime,
|
|
561
|
-
mimeType: mediaRecorder.mimeType
|
|
562
|
-
}
|
|
563
|
-
});
|
|
564
|
-
}
|
|
565
|
-
catch (startError) {
|
|
566
|
-
// 通知父组件开始录制失败
|
|
567
|
-
this.recordingError.emit({
|
|
568
|
-
type: 'start_failed',
|
|
569
|
-
message: '开始录制失败,请检查您的设备权限',
|
|
570
|
-
details: startError
|
|
571
|
-
});
|
|
572
|
-
this.showRecordingUI = false;
|
|
573
|
-
return;
|
|
574
|
-
}
|
|
575
|
-
// 设置录制计时器
|
|
576
|
-
this.recordingTimer = setInterval(() => {
|
|
577
|
-
const elapsedTime = Math.floor((Date.now() - this.recordingStartTime) / 1000);
|
|
578
|
-
this.recordingTimeLeft = Math.max(0, this.maxRecordingTime - elapsedTime);
|
|
579
|
-
// 检查是否需要显示倒计时警告
|
|
580
|
-
if (this.recordingTimeLeft <= this.countdownWarningTime && !this.showCountdownWarning) {
|
|
581
|
-
this.showCountdownWarning = true;
|
|
582
|
-
}
|
|
583
|
-
// 时间到自动停止录制
|
|
584
|
-
if (this.recordingTimeLeft <= 0) {
|
|
585
|
-
this.stopRecording();
|
|
586
|
-
}
|
|
587
|
-
}, 1000);
|
|
588
|
-
}
|
|
589
|
-
catch (error) {
|
|
590
|
-
console.error('无法访问摄像头或麦克风:', error);
|
|
591
|
-
// 通知父组件无法访问媒体设备
|
|
592
|
-
this.recordingError.emit({
|
|
593
|
-
type: 'media_access_failed',
|
|
594
|
-
message: '无法访问摄像头或麦克风,请确保已授予权限',
|
|
595
|
-
details: error
|
|
596
|
-
});
|
|
597
|
-
this.showRecordingUI = false;
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
// 添加新方法来设置视频预览
|
|
601
|
-
setupVideoPreview(stream) {
|
|
602
|
-
// 延迟执行以确保DOM已更新
|
|
603
|
-
setTimeout(() => {
|
|
604
|
-
const videoElement = this.hostElement.shadowRoot?.querySelector('video');
|
|
605
|
-
if (videoElement && stream) {
|
|
606
|
-
// 先尝试使用标准方法
|
|
607
|
-
try {
|
|
608
|
-
videoElement.srcObject = stream;
|
|
609
|
-
videoElement.play().catch(err => {
|
|
610
|
-
console.error('视频播放失败:', err);
|
|
611
|
-
});
|
|
612
|
-
}
|
|
613
|
-
catch (e) {
|
|
614
|
-
console.warn('设置srcObject失败,尝试替代方法:', e);
|
|
615
|
-
// 对于不支持srcObject的旧浏览器,使用URL.createObjectURL
|
|
616
|
-
try {
|
|
617
|
-
// 使用类型断言解决TypeScript错误
|
|
618
|
-
const objectUrl = URL.createObjectURL(stream);
|
|
619
|
-
videoElement.src = objectUrl;
|
|
620
|
-
// 确保在视频元素不再使用时释放URL
|
|
621
|
-
videoElement.onended = () => {
|
|
622
|
-
URL.revokeObjectURL(objectUrl);
|
|
623
|
-
};
|
|
624
|
-
}
|
|
625
|
-
catch (urlError) {
|
|
626
|
-
console.error('创建对象URL失败:', urlError);
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
else {
|
|
631
|
-
console.warn('未找到视频元素或媒体流无效');
|
|
632
|
-
}
|
|
633
|
-
}, 100);
|
|
634
|
-
}
|
|
635
|
-
// 添加一个新方法来检测浏览器支持的MIME类型
|
|
636
|
-
getSupportedMimeType() {
|
|
637
|
-
// 按优先级排列的MIME类型列表
|
|
638
|
-
const mimeTypes = [
|
|
639
|
-
'video/webm;codecs=vp8,opus',
|
|
640
|
-
'video/webm;codecs=vp9,opus',
|
|
641
|
-
'video/webm',
|
|
642
|
-
'video/mp4',
|
|
643
|
-
'video/mp4;codecs=h264,aac',
|
|
644
|
-
'' // 空字符串表示使用浏览器默认值
|
|
645
|
-
];
|
|
646
|
-
// 检查MediaRecorder是否可用
|
|
647
|
-
if (!window.MediaRecorder) {
|
|
648
|
-
console.warn('MediaRecorder API不可用');
|
|
649
|
-
return '';
|
|
650
|
-
}
|
|
651
|
-
// 检查每种MIME类型是否受支持
|
|
652
|
-
for (const type of mimeTypes) {
|
|
653
|
-
if (!type)
|
|
654
|
-
return ''; // 如果是空字符串,直接返回
|
|
655
|
-
try {
|
|
656
|
-
if (MediaRecorder.isTypeSupported(type)) {
|
|
657
|
-
return type;
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
catch (e) {
|
|
661
|
-
console.warn(`检查MIME类型支持时出错 ${type}:`, e);
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
// 如果没有找到支持的类型,返回空字符串
|
|
665
|
-
console.warn('没有找到支持的MIME类型,将使用浏览器默认值');
|
|
666
|
-
return '';
|
|
667
|
-
}
|
|
668
|
-
// 停止录制
|
|
669
|
-
stopRecording() {
|
|
670
|
-
if (this.mediaRecorder && this.isRecording) {
|
|
671
|
-
this.mediaRecorder.stop();
|
|
672
|
-
this.isRecording = false;
|
|
673
|
-
// 清理计时器
|
|
674
|
-
if (this.recordingTimer) {
|
|
675
|
-
clearInterval(this.recordingTimer);
|
|
676
|
-
this.recordingTimer = null;
|
|
677
|
-
}
|
|
678
|
-
// 停止并释放媒体流
|
|
679
|
-
if (this.recordingStream) {
|
|
680
|
-
this.recordingStream.getTracks().forEach(track => track.stop());
|
|
681
|
-
this.recordingStream = null;
|
|
682
|
-
}
|
|
683
|
-
// 清理视频引用
|
|
684
|
-
this.videoRef = null;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
// 修改音频转文字方法
|
|
688
|
-
async convertAudioToText(cosKey) {
|
|
689
|
-
try {
|
|
690
|
-
// 创建一个Promise来处理响应
|
|
691
|
-
return new Promise((resolve, reject) => {
|
|
692
|
-
sendHttpRequest({
|
|
693
|
-
url: `https://pcm_api.ylzhaopin.com/external/v1/tts/audio_to_text`,
|
|
694
|
-
method: 'POST',
|
|
695
|
-
headers: {
|
|
696
|
-
'Content-Type': 'application/json',
|
|
697
|
-
'authorization': 'Bearer ' + this.apiKey
|
|
698
|
-
},
|
|
699
|
-
data: {
|
|
700
|
-
cos_key: cosKey
|
|
701
|
-
},
|
|
702
|
-
onMessage: (data) => {
|
|
703
|
-
// 检查返回结果中是否包含转换后的文本
|
|
704
|
-
if (data && data.text) {
|
|
705
|
-
resolve(data.text);
|
|
706
|
-
}
|
|
707
|
-
else {
|
|
708
|
-
console.warn('音频转文字返回结果格式不正确');
|
|
709
|
-
resolve(null);
|
|
710
|
-
}
|
|
711
|
-
},
|
|
712
|
-
onError: (error) => {
|
|
713
|
-
console.error('音频转文字请求失败:', error);
|
|
714
|
-
reject(error);
|
|
715
|
-
}
|
|
716
|
-
});
|
|
717
|
-
});
|
|
718
|
-
}
|
|
719
|
-
catch (error) {
|
|
720
|
-
console.error('音频转文字错误:', error);
|
|
721
|
-
return null;
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
// 上传录制的视频
|
|
725
|
-
async uploadRecordedVideo() {
|
|
726
|
-
if (!this.recordedBlob)
|
|
727
|
-
return;
|
|
728
|
-
try {
|
|
729
|
-
this.isUploadingVideo = true; // 开始上传时设置状态
|
|
730
|
-
this.showRecordingUI = false; // 隐藏视频预览
|
|
731
|
-
// 根据Blob类型确定文件扩展名
|
|
732
|
-
const fileExtension = this.recordedBlob.type.includes('webm') ? 'webm' : 'mp4';
|
|
733
|
-
const fileName = `answer.${fileExtension}`;
|
|
734
|
-
const formData = new FormData();
|
|
735
|
-
formData.append('file', this.recordedBlob, fileName);
|
|
736
|
-
const response = await fetch('https://pcm_api.ylzhaopin.com/external/v1/files/upload', {
|
|
737
|
-
method: 'POST',
|
|
738
|
-
headers: {
|
|
739
|
-
'authorization': 'Bearer ' + this.apiKey
|
|
740
|
-
},
|
|
741
|
-
body: formData
|
|
742
|
-
});
|
|
743
|
-
const result = await response.json();
|
|
744
|
-
if (result && result.cos_key) {
|
|
745
|
-
// 调用音频转文字API
|
|
746
|
-
const transcriptionText = await this.convertAudioToText(result.cos_key);
|
|
747
|
-
// 保存视频答案
|
|
748
|
-
await this.saveVideoAnswer(result.cos_key);
|
|
749
|
-
// 发送"下一题"请求,可以附带转录文本
|
|
750
|
-
this.sendMessageToAPI(transcriptionText || "下一题");
|
|
751
|
-
}
|
|
752
|
-
else {
|
|
753
|
-
throw new Error('视频上传失败');
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
catch (error) {
|
|
757
|
-
console.error('视频上传或处理错误:', error);
|
|
758
|
-
// 通知父组件视频上传失败
|
|
759
|
-
this.recordingError.emit({
|
|
760
|
-
type: 'upload_failed',
|
|
761
|
-
message: '视频上传或处理失败',
|
|
762
|
-
details: error
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
finally {
|
|
766
|
-
this.isUploadingVideo = false; // 上传完成后重置状态
|
|
767
|
-
this.showRecordingUI = false;
|
|
768
|
-
this.recordedBlob = null;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
// 保存视频答案
|
|
772
|
-
async saveVideoAnswer(cosKey) {
|
|
773
|
-
if (!this.conversationId)
|
|
774
|
-
return;
|
|
775
|
-
try {
|
|
776
|
-
const lastAIMessage = this.messages.length > 0 ? this.messages[this.messages.length - 1] : null;
|
|
777
|
-
if (!lastAIMessage)
|
|
778
|
-
return;
|
|
779
|
-
await sendHttpRequest({
|
|
780
|
-
url: 'https://pcm_api.ylzhaopin.com/agents/hr_competition/answer',
|
|
781
|
-
method: 'POST',
|
|
782
|
-
headers: {
|
|
783
|
-
'authorization': 'Bearer ' + this.apiKey
|
|
784
|
-
},
|
|
785
|
-
data: {
|
|
786
|
-
conversation_id: this.conversationId,
|
|
787
|
-
user: this.userId, // 使用传入的 userId
|
|
788
|
-
question: lastAIMessage.answer,
|
|
789
|
-
file_url: cosKey
|
|
790
|
-
},
|
|
791
|
-
});
|
|
792
|
-
}
|
|
793
|
-
catch (error) {
|
|
794
|
-
console.error('保存视频答案失败:', error);
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
/**
|
|
798
|
-
* 发送面试完成请求
|
|
799
|
-
*/
|
|
800
|
-
async completeInterview() {
|
|
801
|
-
if (!this.conversationId)
|
|
802
|
-
return;
|
|
803
|
-
try {
|
|
804
|
-
const requestData = {
|
|
805
|
-
response_mode: 'streaming',
|
|
806
|
-
conversation_id: this.conversationId,
|
|
807
|
-
query: "面试完成",
|
|
808
|
-
user: this.userId,
|
|
809
|
-
inputs: {
|
|
810
|
-
// 合并自定义输入参数
|
|
811
|
-
...this.customInputs
|
|
812
|
-
}
|
|
813
|
-
};
|
|
814
|
-
// 不使用 await,直接发送请求
|
|
815
|
-
sendSSERequest({
|
|
816
|
-
url: `https://pcm_api.ylzhaopin.com/external/v1/chat/chat-messages`,
|
|
817
|
-
method: 'POST',
|
|
818
|
-
headers: {
|
|
819
|
-
'authorization': 'Bearer ' + this.apiKey
|
|
820
|
-
},
|
|
821
|
-
data: requestData,
|
|
822
|
-
}).catch(error => {
|
|
823
|
-
console.error('发送面试完成请求失败:', error);
|
|
824
|
-
});
|
|
825
|
-
}
|
|
826
|
-
catch (error) {
|
|
827
|
-
console.error('发送面试完成请求失败:', error);
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
// 添加TTS合成音频的方法
|
|
831
|
-
async synthesizeAudio(text) {
|
|
832
|
-
try {
|
|
833
|
-
const response = await fetch('https://pcm_api.ylzhaopin.com/external/v1/tts/synthesize_audio', {
|
|
834
|
-
method: 'POST',
|
|
835
|
-
headers: {
|
|
836
|
-
'Content-Type': 'application/json',
|
|
837
|
-
'authorization': 'Bearer ' + this.apiKey
|
|
838
|
-
},
|
|
839
|
-
body: JSON.stringify({ text })
|
|
840
|
-
});
|
|
841
|
-
if (!response.ok) {
|
|
842
|
-
throw new Error('语音合成失败');
|
|
843
|
-
}
|
|
844
|
-
// 获取音频数据并创建Blob URL
|
|
845
|
-
const audioBlob = await response.blob();
|
|
846
|
-
return URL.createObjectURL(audioBlob);
|
|
847
|
-
}
|
|
848
|
-
catch (error) {
|
|
849
|
-
console.error('语音合成错误:', error);
|
|
850
|
-
throw error;
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
// 播放音频的方法
|
|
854
|
-
playAudio(audioUrl) {
|
|
855
|
-
return new Promise((resolve) => {
|
|
856
|
-
this.isPlayingAudio = true;
|
|
857
|
-
this.audioUrl = audioUrl;
|
|
858
|
-
// 创建音频元素
|
|
859
|
-
if (!this.audioElement) {
|
|
860
|
-
this.audioElement = new Audio();
|
|
861
|
-
}
|
|
862
|
-
this.audioElement.src = audioUrl;
|
|
863
|
-
this.audioElement.onended = () => {
|
|
864
|
-
this.isPlayingAudio = false;
|
|
865
|
-
this.audioUrl = null;
|
|
866
|
-
resolve();
|
|
867
|
-
};
|
|
868
|
-
this.audioElement.onerror = () => {
|
|
869
|
-
console.error('音频播放错误');
|
|
870
|
-
this.isPlayingAudio = false;
|
|
871
|
-
this.audioUrl = null;
|
|
872
|
-
resolve();
|
|
873
|
-
};
|
|
874
|
-
this.audioElement.play().catch(error => {
|
|
875
|
-
console.error('音频播放失败:', error);
|
|
876
|
-
this.isPlayingAudio = false;
|
|
877
|
-
this.audioUrl = null;
|
|
878
|
-
resolve();
|
|
879
|
-
});
|
|
880
|
-
});
|
|
881
|
-
}
|
|
882
|
-
// 修改 componentDidLoad 生命周期方法,确保组件卸载时释放资源
|
|
883
|
-
disconnectedCallback() {
|
|
884
|
-
// 移除滚动事件监听器
|
|
885
|
-
const chatHistory = this.hostElement.shadowRoot?.querySelector('.chat-history');
|
|
886
|
-
if (chatHistory) {
|
|
887
|
-
chatHistory.removeEventListener('scroll', this.handleScroll);
|
|
888
|
-
}
|
|
889
|
-
// 释放音频资源
|
|
890
|
-
if (this.audioElement) {
|
|
891
|
-
this.audioElement.pause();
|
|
892
|
-
this.audioElement.src = '';
|
|
893
|
-
this.audioElement = null;
|
|
894
|
-
}
|
|
895
|
-
// 释放 Blob URL
|
|
896
|
-
if (this.audioUrl) {
|
|
897
|
-
URL.revokeObjectURL(this.audioUrl);
|
|
898
|
-
this.audioUrl = null;
|
|
899
|
-
}
|
|
900
|
-
// 清理其他计时器
|
|
901
|
-
if (this.waitingTimer) {
|
|
902
|
-
clearInterval(this.waitingTimer);
|
|
903
|
-
this.waitingTimer = null;
|
|
904
|
-
}
|
|
905
|
-
if (this.recordingTimer) {
|
|
906
|
-
clearInterval(this.recordingTimer);
|
|
907
|
-
this.recordingTimer = null;
|
|
908
|
-
}
|
|
909
|
-
// 停止录制
|
|
910
|
-
this.stopRecording();
|
|
911
|
-
}
|
|
912
|
-
// 修改手动播放音频的方法
|
|
913
|
-
handlePlayAudio = async () => {
|
|
914
|
-
if (this.audioUrl) {
|
|
915
|
-
await this.playAudio(this.audioUrl);
|
|
916
|
-
// 手动播放完成后开始等待录制
|
|
917
|
-
this.startWaitingToRecord();
|
|
918
|
-
}
|
|
919
|
-
};
|
|
920
|
-
// 处理文本输入变化
|
|
921
|
-
handleTextInputChange = (event) => {
|
|
922
|
-
const input = event.target;
|
|
923
|
-
this.textAnswer = input.value;
|
|
924
|
-
};
|
|
925
|
-
// 添加处理键盘事件的方法
|
|
926
|
-
handleKeyDown = (event) => {
|
|
927
|
-
// 如果按下的是回车键
|
|
928
|
-
if (event.key === 'Enter') {
|
|
929
|
-
// 如果同时按下了Ctrl键,允许换行
|
|
930
|
-
if (event.ctrlKey) {
|
|
931
|
-
return; // 不阻止默认行为,允许插入换行符
|
|
932
|
-
}
|
|
933
|
-
else {
|
|
934
|
-
// 阻止默认的换行行为
|
|
935
|
-
event.preventDefault();
|
|
936
|
-
// 如果文本框不为空且不处于禁用状态,则发送消息
|
|
937
|
-
if (this.textAnswer.trim() && !this.isSubmittingText && !this.isLoading &&
|
|
938
|
-
!this.currentStreamingMessage && !this.waitingToRecord && !this.isPlayingAudio) {
|
|
939
|
-
this.submitTextAnswer();
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
};
|
|
944
|
-
// 修改提交文本回答的方法
|
|
945
|
-
submitTextAnswer = async () => {
|
|
946
|
-
if (!this.textAnswer.trim() || this.isSubmittingText) {
|
|
947
|
-
return;
|
|
948
|
-
}
|
|
949
|
-
this.isSubmittingText = true;
|
|
950
|
-
try {
|
|
951
|
-
// 保存当前输入内容
|
|
952
|
-
const textToSend = this.textAnswer;
|
|
953
|
-
// 立即清空文本输入
|
|
954
|
-
this.textAnswer = '';
|
|
955
|
-
// 发送用户输入的文本作为查询
|
|
956
|
-
await this.sendMessageToAPI(textToSend);
|
|
957
|
-
}
|
|
958
|
-
catch (error) {
|
|
959
|
-
console.error('提交文本回答失败:', error);
|
|
960
|
-
alert('提交回答失败,请重试');
|
|
961
|
-
}
|
|
962
|
-
finally {
|
|
963
|
-
this.isSubmittingText = false;
|
|
964
|
-
}
|
|
965
|
-
};
|
|
966
|
-
render() {
|
|
967
|
-
if (!this.isOpen)
|
|
968
|
-
return null;
|
|
969
|
-
const modalStyle = {
|
|
970
|
-
zIndex: String(this.zIndex)
|
|
971
|
-
};
|
|
972
|
-
const containerClass = {
|
|
973
|
-
'modal-container': true,
|
|
974
|
-
'fullscreen': this.fullscreen
|
|
975
|
-
};
|
|
976
|
-
const overlayClass = {
|
|
977
|
-
'modal-overlay': true,
|
|
978
|
-
'fullscreen-overlay': this.fullscreen
|
|
979
|
-
};
|
|
980
|
-
const renderVideoPreview = () => (h("div", { class: "video-preview" }, h("video", { autoPlay: true, playsInline: true, muted: true, style: { transform: 'scaleX(-1)' }, ref: (el) => {
|
|
981
|
-
if (el && this.recordingStream && !this.videoRef) {
|
|
982
|
-
this.videoRef = el;
|
|
983
|
-
// 不在这里设置srcObject,而是使用setupVideoPreview方法
|
|
984
|
-
}
|
|
985
|
-
} }), h("div", { class: {
|
|
986
|
-
'recording-status': true,
|
|
987
|
-
'warning': this.showCountdownWarning
|
|
988
|
-
} }, h("span", { class: "recording-dot" }), h("span", null, "\u5F55\u5236\u4E2D ", Math.floor(this.recordingTimeLeft / 60), ":", (this.recordingTimeLeft % 60).toString().padStart(2, '0'), this.showCountdownWarning && ` (即将自动完成)`))));
|
|
989
|
-
// 渲染占位符状态信息
|
|
990
|
-
const renderPlaceholderStatus = () => {
|
|
991
|
-
// 正在播放音频
|
|
992
|
-
if (this.isPlayingAudio) {
|
|
993
|
-
return (h("div", { class: "placeholder-status" }, h("p", null, "\u6B63\u5728\u64AD\u653E\u95EE\u9898\uFF0C\u8BF7\u542C\u5B8C\u540E\u51C6\u5907\u56DE\u7B54...")));
|
|
994
|
-
}
|
|
995
|
-
// 正在上传视频
|
|
996
|
-
if (this.isUploadingVideo) {
|
|
997
|
-
return (h("div", { class: "placeholder-status" }, h("p", null, "\u6B63\u5728\u4E0A\u4F20\u89C6\u9891\uFF0C\u8BF7\u7A0D\u5019...")));
|
|
998
|
-
}
|
|
999
|
-
// 正在加载或等待AI回复
|
|
1000
|
-
if (this.isLoading || this.currentStreamingMessage) {
|
|
1001
|
-
return (h("div", { class: "placeholder-status" }, h("p", null, "\u8BF7\u7B49\u5F85\u9898\u76EE...")));
|
|
1002
|
-
}
|
|
1003
|
-
// 等待开始录制
|
|
1004
|
-
if (this.waitingToRecord) {
|
|
1005
|
-
return (h("div", { class: "placeholder-status" }, h("p", null, "\u8BF7\u51C6\u5907\u597D\uFF0C", this.waitingTimeLeft, "\u79D2\u540E\u5C06\u5F00\u59CB\u5F55\u5236\u60A8\u7684\u56DE\u7B54...")));
|
|
1006
|
-
}
|
|
1007
|
-
// 添加默认状态
|
|
1008
|
-
return (h("div", { class: "placeholder-status default-status" }, h("p", null, "\u51C6\u5907\u4E2D...")));
|
|
1009
|
-
};
|
|
1010
|
-
// 修改文本输入区域渲染函数
|
|
1011
|
-
const renderTextInputArea = () => (h("div", { class: "text-input-area" }, h("textarea", { class: "text-answer-input", placeholder: "\u8BF7\u8F93\u5165\u60A8\u7684\u56DE\u7B54...(\u6309\u56DE\u8F66\u53D1\u9001\uFF0CCtrl+\u56DE\u8F66\u6362\u884C)", value: this.textAnswer, onInput: this.handleTextInputChange, onKeyDown: this.handleKeyDown, disabled: this.isSubmittingText || this.isLoading || !!this.currentStreamingMessage || this.waitingToRecord || this.isPlayingAudio }), h("div", { class: "input-toolbar" }, h("div", { class: "toolbar-actions" }), h("button", { class: {
|
|
1012
|
-
'submit-text-button': true,
|
|
1013
|
-
'disabled': !this.textAnswer.trim() || this.isSubmittingText || this.isLoading || !!this.currentStreamingMessage || this.waitingToRecord || this.isPlayingAudio
|
|
1014
|
-
}, disabled: !this.textAnswer.trim() || this.isSubmittingText || this.isLoading || !!this.currentStreamingMessage || this.waitingToRecord || this.isPlayingAudio, onClick: this.submitTextAnswer }, this.isSubmittingText ? '发送中...' : '发送'))));
|
|
1015
|
-
return (h("div", { class: overlayClass, style: modalStyle }, h("div", { class: containerClass }, this.isShowHeader && (h("div", { class: "modal-header" }, h("div", { class: "header-left" }, this.icon && h("img", { src: this.icon, class: "header-icon", alt: "\u5E94\u7528\u56FE\u6807" }), h("div", null, this.modalTitle)), this.isNeedClose && (h("button", { class: "close-button", onClick: this.handleClose }, h("span", null, "\u00D7"))))), h("div", { style: { height: '100%' } }, h("div", { class: "chat-history", onScroll: this.handleScroll }, this.isLoadingHistory ? (h("div", { class: "loading-container" }, h("div", { class: "loading-spinner" }), h("p", null, "\u52A0\u8F7D\u5386\u53F2\u6D88\u606F\u4E2D..."))) : (h("div", null, this.messages.map((message) => (h("div", { id: `message_${message.id}`, key: message.id }, h("pcm-chat-message", { message: message, onMessageChange: (event) => {
|
|
1016
|
-
const updatedMessages = this.messages.map(msg => msg.id === message.id ? { ...msg, ...event.detail } : msg);
|
|
1017
|
-
this.messages = updatedMessages;
|
|
1018
|
-
} })))), this.currentStreamingMessage && (h("div", { id: `message_${this.currentStreamingMessage.id}` }, h("pcm-chat-message", { message: this.currentStreamingMessage }))), this.messages.length === 0 && !this.currentStreamingMessage && (h("div", { class: "empty-state" }, h("p", null, "\u6B63\u5728\u51C6\u5907\u9762\u8BD5...")))))), h("div", { class: "recording-section" }, h("div", { class: "recording-container" }, this.interviewMode === 'text' && (renderTextInputArea()), this.interviewMode === 'video' && (h("div", { style: { width: '100%', display: 'flex', flexWrap: 'wrap', justifyContent: 'center' } }, h("div", { class: "video-area" }, this.showRecordingUI ? (renderVideoPreview()) : (h("div", { class: "video-preview placeholder" }, renderPlaceholderStatus())), h("div", { class: "progress-container" }, h("div", { class: "progress-bar-container" }, h("div", { class: "progress-bar", style: {
|
|
1019
|
-
width: `${Math.max(0, this.currentQuestionNumber - 1) / this.totalQuestions * 100}%`
|
|
1020
|
-
} })), h("div", { class: "progress-text" }, "\u5DF2\u5B8C\u6210", Math.max(0, this.currentQuestionNumber - 1), "/", this.totalQuestions))), h("div", { class: "recording-controls" }, this.showRecordingUI ? (h("button", { class: "stop-recording-button", onClick: () => this.stopRecording() }, "\u5B8C\u6210\u672C\u9898\u56DE\u7B54")) : (h("div", { class: "waiting-message" }, (() => {
|
|
1021
|
-
// 显示播放按钮(当不自动播放且有音频URL时)
|
|
1022
|
-
if (!this.enableVoice && this.audioUrl && !this.isPlayingAudio) {
|
|
1023
|
-
return (h("div", { class: "play-audio-container", onClick: this.handlePlayAudio }, h("p", null, h("svg", { viewBox: "0 0 24 24", width: "24", height: "24", fill: "currentColor", style: { verticalAlign: 'middle', marginRight: '8px' } }, h("path", { d: "M8 5v14l11-7z" })), h("span", { style: { verticalAlign: 'middle' } }, "\u64AD\u653E\u9898\u76EE"))));
|
|
1024
|
-
}
|
|
1025
|
-
// 其他状态下显示禁用的"完成回答"按钮
|
|
1026
|
-
return (h("button", { class: "stop-recording-button disabled", disabled: true }, "\u5B8C\u6210\u56DE\u7B54"));
|
|
1027
|
-
})())))))))))));
|
|
1028
|
-
}
|
|
1029
|
-
};
|
|
1030
|
-
ChatAPPModal.style = pcmAppChatModalCss;
|
|
1031
|
-
|
|
1032
|
-
/**
|
|
1033
|
-
* marked v9.1.6 - a markdown parser
|
|
1034
|
-
* Copyright (c) 2011-2023, Christopher Jeffrey. (MIT Licensed)
|
|
1035
|
-
* https://github.com/markedjs/marked
|
|
1036
|
-
*/
|
|
1037
|
-
|
|
1038
|
-
/**
|
|
1039
|
-
* DO NOT EDIT THIS FILE
|
|
1040
|
-
* The code in this file is generated from files in ./src/
|
|
1041
|
-
*/
|
|
1042
|
-
|
|
1043
|
-
/**
|
|
1044
|
-
* Gets the original marked default options.
|
|
1045
|
-
*/
|
|
1046
|
-
function _getDefaults() {
|
|
1047
|
-
return {
|
|
1048
|
-
async: false,
|
|
1049
|
-
breaks: false,
|
|
1050
|
-
extensions: null,
|
|
1051
|
-
gfm: true,
|
|
1052
|
-
hooks: null,
|
|
1053
|
-
pedantic: false,
|
|
1054
|
-
renderer: null,
|
|
1055
|
-
silent: false,
|
|
1056
|
-
tokenizer: null,
|
|
1057
|
-
walkTokens: null
|
|
1058
|
-
};
|
|
1059
|
-
}
|
|
1060
|
-
let _defaults = _getDefaults();
|
|
1061
|
-
function changeDefaults(newDefaults) {
|
|
1062
|
-
_defaults = newDefaults;
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
/**
|
|
1066
|
-
* Helpers
|
|
1067
|
-
*/
|
|
1068
|
-
const escapeTest = /[&<>"']/;
|
|
1069
|
-
const escapeReplace = new RegExp(escapeTest.source, 'g');
|
|
1070
|
-
const escapeTestNoEncode = /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/;
|
|
1071
|
-
const escapeReplaceNoEncode = new RegExp(escapeTestNoEncode.source, 'g');
|
|
1072
|
-
const escapeReplacements = {
|
|
1073
|
-
'&': '&',
|
|
1074
|
-
'<': '<',
|
|
1075
|
-
'>': '>',
|
|
1076
|
-
'"': '"',
|
|
1077
|
-
"'": '''
|
|
1078
|
-
};
|
|
1079
|
-
const getEscapeReplacement = (ch) => escapeReplacements[ch];
|
|
1080
|
-
function escape(html, encode) {
|
|
1081
|
-
if (encode) {
|
|
1082
|
-
if (escapeTest.test(html)) {
|
|
1083
|
-
return html.replace(escapeReplace, getEscapeReplacement);
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
else {
|
|
1087
|
-
if (escapeTestNoEncode.test(html)) {
|
|
1088
|
-
return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
return html;
|
|
1092
|
-
}
|
|
1093
|
-
const unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;
|
|
1094
|
-
function unescape(html) {
|
|
1095
|
-
// explicitly match decimal, hex, and named HTML entities
|
|
1096
|
-
return html.replace(unescapeTest, (_, n) => {
|
|
1097
|
-
n = n.toLowerCase();
|
|
1098
|
-
if (n === 'colon')
|
|
1099
|
-
return ':';
|
|
1100
|
-
if (n.charAt(0) === '#') {
|
|
1101
|
-
return n.charAt(1) === 'x'
|
|
1102
|
-
? String.fromCharCode(parseInt(n.substring(2), 16))
|
|
1103
|
-
: String.fromCharCode(+n.substring(1));
|
|
1104
|
-
}
|
|
1105
|
-
return '';
|
|
1106
|
-
});
|
|
1107
|
-
}
|
|
1108
|
-
const caret = /(^|[^\[])\^/g;
|
|
1109
|
-
function edit(regex, opt) {
|
|
1110
|
-
regex = typeof regex === 'string' ? regex : regex.source;
|
|
1111
|
-
opt = opt || '';
|
|
1112
|
-
const obj = {
|
|
1113
|
-
replace: (name, val) => {
|
|
1114
|
-
val = typeof val === 'object' && 'source' in val ? val.source : val;
|
|
1115
|
-
val = val.replace(caret, '$1');
|
|
1116
|
-
regex = regex.replace(name, val);
|
|
1117
|
-
return obj;
|
|
1118
|
-
},
|
|
1119
|
-
getRegex: () => {
|
|
1120
|
-
return new RegExp(regex, opt);
|
|
1121
|
-
}
|
|
1122
|
-
};
|
|
1123
|
-
return obj;
|
|
1124
|
-
}
|
|
1125
|
-
function cleanUrl(href) {
|
|
1126
|
-
try {
|
|
1127
|
-
href = encodeURI(href).replace(/%25/g, '%');
|
|
1128
|
-
}
|
|
1129
|
-
catch (e) {
|
|
1130
|
-
return null;
|
|
1131
|
-
}
|
|
1132
|
-
return href;
|
|
1133
|
-
}
|
|
1134
|
-
const noopTest = { exec: () => null };
|
|
1135
|
-
function splitCells(tableRow, count) {
|
|
1136
|
-
// ensure that every cell-delimiting pipe has a space
|
|
1137
|
-
// before it to distinguish it from an escaped pipe
|
|
1138
|
-
const row = tableRow.replace(/\|/g, (match, offset, str) => {
|
|
1139
|
-
let escaped = false;
|
|
1140
|
-
let curr = offset;
|
|
1141
|
-
while (--curr >= 0 && str[curr] === '\\')
|
|
1142
|
-
escaped = !escaped;
|
|
1143
|
-
if (escaped) {
|
|
1144
|
-
// odd number of slashes means | is escaped
|
|
1145
|
-
// so we leave it alone
|
|
1146
|
-
return '|';
|
|
1147
|
-
}
|
|
1148
|
-
else {
|
|
1149
|
-
// add space before unescaped |
|
|
1150
|
-
return ' |';
|
|
1151
|
-
}
|
|
1152
|
-
}), cells = row.split(/ \|/);
|
|
1153
|
-
let i = 0;
|
|
1154
|
-
// First/last cell in a row cannot be empty if it has no leading/trailing pipe
|
|
1155
|
-
if (!cells[0].trim()) {
|
|
1156
|
-
cells.shift();
|
|
1157
|
-
}
|
|
1158
|
-
if (cells.length > 0 && !cells[cells.length - 1].trim()) {
|
|
1159
|
-
cells.pop();
|
|
1160
|
-
}
|
|
1161
|
-
if (count) {
|
|
1162
|
-
if (cells.length > count) {
|
|
1163
|
-
cells.splice(count);
|
|
1164
|
-
}
|
|
1165
|
-
else {
|
|
1166
|
-
while (cells.length < count)
|
|
1167
|
-
cells.push('');
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
for (; i < cells.length; i++) {
|
|
1171
|
-
// leading or trailing whitespace is ignored per the gfm spec
|
|
1172
|
-
cells[i] = cells[i].trim().replace(/\\\|/g, '|');
|
|
1173
|
-
}
|
|
1174
|
-
return cells;
|
|
1175
|
-
}
|
|
1176
|
-
/**
|
|
1177
|
-
* Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
|
|
1178
|
-
* /c*$/ is vulnerable to REDOS.
|
|
1179
|
-
*
|
|
1180
|
-
* @param str
|
|
1181
|
-
* @param c
|
|
1182
|
-
* @param invert Remove suffix of non-c chars instead. Default falsey.
|
|
1183
|
-
*/
|
|
1184
|
-
function rtrim(str, c, invert) {
|
|
1185
|
-
const l = str.length;
|
|
1186
|
-
if (l === 0) {
|
|
1187
|
-
return '';
|
|
1188
|
-
}
|
|
1189
|
-
// Length of suffix matching the invert condition.
|
|
1190
|
-
let suffLen = 0;
|
|
1191
|
-
// Step left until we fail to match the invert condition.
|
|
1192
|
-
while (suffLen < l) {
|
|
1193
|
-
const currChar = str.charAt(l - suffLen - 1);
|
|
1194
|
-
if (currChar === c && true) {
|
|
1195
|
-
suffLen++;
|
|
1196
|
-
}
|
|
1197
|
-
else {
|
|
1198
|
-
break;
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
return str.slice(0, l - suffLen);
|
|
1202
|
-
}
|
|
1203
|
-
function findClosingBracket(str, b) {
|
|
1204
|
-
if (str.indexOf(b[1]) === -1) {
|
|
1205
|
-
return -1;
|
|
1206
|
-
}
|
|
1207
|
-
let level = 0;
|
|
1208
|
-
for (let i = 0; i < str.length; i++) {
|
|
1209
|
-
if (str[i] === '\\') {
|
|
1210
|
-
i++;
|
|
1211
|
-
}
|
|
1212
|
-
else if (str[i] === b[0]) {
|
|
1213
|
-
level++;
|
|
1214
|
-
}
|
|
1215
|
-
else if (str[i] === b[1]) {
|
|
1216
|
-
level--;
|
|
1217
|
-
if (level < 0) {
|
|
1218
|
-
return i;
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
return -1;
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
function outputLink(cap, link, raw, lexer) {
|
|
1226
|
-
const href = link.href;
|
|
1227
|
-
const title = link.title ? escape(link.title) : null;
|
|
1228
|
-
const text = cap[1].replace(/\\([\[\]])/g, '$1');
|
|
1229
|
-
if (cap[0].charAt(0) !== '!') {
|
|
1230
|
-
lexer.state.inLink = true;
|
|
1231
|
-
const token = {
|
|
1232
|
-
type: 'link',
|
|
1233
|
-
raw,
|
|
1234
|
-
href,
|
|
1235
|
-
title,
|
|
1236
|
-
text,
|
|
1237
|
-
tokens: lexer.inlineTokens(text)
|
|
1238
|
-
};
|
|
1239
|
-
lexer.state.inLink = false;
|
|
1240
|
-
return token;
|
|
1241
|
-
}
|
|
1242
|
-
return {
|
|
1243
|
-
type: 'image',
|
|
1244
|
-
raw,
|
|
1245
|
-
href,
|
|
1246
|
-
title,
|
|
1247
|
-
text: escape(text)
|
|
1248
|
-
};
|
|
1249
|
-
}
|
|
1250
|
-
function indentCodeCompensation(raw, text) {
|
|
1251
|
-
const matchIndentToCode = raw.match(/^(\s+)(?:```)/);
|
|
1252
|
-
if (matchIndentToCode === null) {
|
|
1253
|
-
return text;
|
|
1254
|
-
}
|
|
1255
|
-
const indentToCode = matchIndentToCode[1];
|
|
1256
|
-
return text
|
|
1257
|
-
.split('\n')
|
|
1258
|
-
.map(node => {
|
|
1259
|
-
const matchIndentInNode = node.match(/^\s+/);
|
|
1260
|
-
if (matchIndentInNode === null) {
|
|
1261
|
-
return node;
|
|
1262
|
-
}
|
|
1263
|
-
const [indentInNode] = matchIndentInNode;
|
|
1264
|
-
if (indentInNode.length >= indentToCode.length) {
|
|
1265
|
-
return node.slice(indentToCode.length);
|
|
1266
|
-
}
|
|
1267
|
-
return node;
|
|
1268
|
-
})
|
|
1269
|
-
.join('\n');
|
|
1270
|
-
}
|
|
1271
|
-
/**
|
|
1272
|
-
* Tokenizer
|
|
1273
|
-
*/
|
|
1274
|
-
class _Tokenizer {
|
|
1275
|
-
options;
|
|
1276
|
-
// TODO: Fix this rules type
|
|
1277
|
-
rules;
|
|
1278
|
-
lexer;
|
|
1279
|
-
constructor(options) {
|
|
1280
|
-
this.options = options || _defaults;
|
|
1281
|
-
}
|
|
1282
|
-
space(src) {
|
|
1283
|
-
const cap = this.rules.block.newline.exec(src);
|
|
1284
|
-
if (cap && cap[0].length > 0) {
|
|
1285
|
-
return {
|
|
1286
|
-
type: 'space',
|
|
1287
|
-
raw: cap[0]
|
|
1288
|
-
};
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
code(src) {
|
|
1292
|
-
const cap = this.rules.block.code.exec(src);
|
|
1293
|
-
if (cap) {
|
|
1294
|
-
const text = cap[0].replace(/^ {1,4}/gm, '');
|
|
1295
|
-
return {
|
|
1296
|
-
type: 'code',
|
|
1297
|
-
raw: cap[0],
|
|
1298
|
-
codeBlockStyle: 'indented',
|
|
1299
|
-
text: !this.options.pedantic
|
|
1300
|
-
? rtrim(text, '\n')
|
|
1301
|
-
: text
|
|
1302
|
-
};
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
fences(src) {
|
|
1306
|
-
const cap = this.rules.block.fences.exec(src);
|
|
1307
|
-
if (cap) {
|
|
1308
|
-
const raw = cap[0];
|
|
1309
|
-
const text = indentCodeCompensation(raw, cap[3] || '');
|
|
1310
|
-
return {
|
|
1311
|
-
type: 'code',
|
|
1312
|
-
raw,
|
|
1313
|
-
lang: cap[2] ? cap[2].trim().replace(this.rules.inline._escapes, '$1') : cap[2],
|
|
1314
|
-
text
|
|
1315
|
-
};
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
heading(src) {
|
|
1319
|
-
const cap = this.rules.block.heading.exec(src);
|
|
1320
|
-
if (cap) {
|
|
1321
|
-
let text = cap[2].trim();
|
|
1322
|
-
// remove trailing #s
|
|
1323
|
-
if (/#$/.test(text)) {
|
|
1324
|
-
const trimmed = rtrim(text, '#');
|
|
1325
|
-
if (this.options.pedantic) {
|
|
1326
|
-
text = trimmed.trim();
|
|
1327
|
-
}
|
|
1328
|
-
else if (!trimmed || / $/.test(trimmed)) {
|
|
1329
|
-
// CommonMark requires space before trailing #s
|
|
1330
|
-
text = trimmed.trim();
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
return {
|
|
1334
|
-
type: 'heading',
|
|
1335
|
-
raw: cap[0],
|
|
1336
|
-
depth: cap[1].length,
|
|
1337
|
-
text,
|
|
1338
|
-
tokens: this.lexer.inline(text)
|
|
1339
|
-
};
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
hr(src) {
|
|
1343
|
-
const cap = this.rules.block.hr.exec(src);
|
|
1344
|
-
if (cap) {
|
|
1345
|
-
return {
|
|
1346
|
-
type: 'hr',
|
|
1347
|
-
raw: cap[0]
|
|
1348
|
-
};
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
blockquote(src) {
|
|
1352
|
-
const cap = this.rules.block.blockquote.exec(src);
|
|
1353
|
-
if (cap) {
|
|
1354
|
-
const text = rtrim(cap[0].replace(/^ *>[ \t]?/gm, ''), '\n');
|
|
1355
|
-
const top = this.lexer.state.top;
|
|
1356
|
-
this.lexer.state.top = true;
|
|
1357
|
-
const tokens = this.lexer.blockTokens(text);
|
|
1358
|
-
this.lexer.state.top = top;
|
|
1359
|
-
return {
|
|
1360
|
-
type: 'blockquote',
|
|
1361
|
-
raw: cap[0],
|
|
1362
|
-
tokens,
|
|
1363
|
-
text
|
|
1364
|
-
};
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
list(src) {
|
|
1368
|
-
let cap = this.rules.block.list.exec(src);
|
|
1369
|
-
if (cap) {
|
|
1370
|
-
let bull = cap[1].trim();
|
|
1371
|
-
const isordered = bull.length > 1;
|
|
1372
|
-
const list = {
|
|
1373
|
-
type: 'list',
|
|
1374
|
-
raw: '',
|
|
1375
|
-
ordered: isordered,
|
|
1376
|
-
start: isordered ? +bull.slice(0, -1) : '',
|
|
1377
|
-
loose: false,
|
|
1378
|
-
items: []
|
|
1379
|
-
};
|
|
1380
|
-
bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`;
|
|
1381
|
-
if (this.options.pedantic) {
|
|
1382
|
-
bull = isordered ? bull : '[*+-]';
|
|
1383
|
-
}
|
|
1384
|
-
// Get next list item
|
|
1385
|
-
const itemRegex = new RegExp(`^( {0,3}${bull})((?:[\t ][^\\n]*)?(?:\\n|$))`);
|
|
1386
|
-
let raw = '';
|
|
1387
|
-
let itemContents = '';
|
|
1388
|
-
let endsWithBlankLine = false;
|
|
1389
|
-
// Check if current bullet point can start a new List Item
|
|
1390
|
-
while (src) {
|
|
1391
|
-
let endEarly = false;
|
|
1392
|
-
if (!(cap = itemRegex.exec(src))) {
|
|
1393
|
-
break;
|
|
1394
|
-
}
|
|
1395
|
-
if (this.rules.block.hr.test(src)) { // End list if bullet was actually HR (possibly move into itemRegex?)
|
|
1396
|
-
break;
|
|
1397
|
-
}
|
|
1398
|
-
raw = cap[0];
|
|
1399
|
-
src = src.substring(raw.length);
|
|
1400
|
-
let line = cap[2].split('\n', 1)[0].replace(/^\t+/, (t) => ' '.repeat(3 * t.length));
|
|
1401
|
-
let nextLine = src.split('\n', 1)[0];
|
|
1402
|
-
let indent = 0;
|
|
1403
|
-
if (this.options.pedantic) {
|
|
1404
|
-
indent = 2;
|
|
1405
|
-
itemContents = line.trimStart();
|
|
1406
|
-
}
|
|
1407
|
-
else {
|
|
1408
|
-
indent = cap[2].search(/[^ ]/); // Find first non-space char
|
|
1409
|
-
indent = indent > 4 ? 1 : indent; // Treat indented code blocks (> 4 spaces) as having only 1 indent
|
|
1410
|
-
itemContents = line.slice(indent);
|
|
1411
|
-
indent += cap[1].length;
|
|
1412
|
-
}
|
|
1413
|
-
let blankLine = false;
|
|
1414
|
-
if (!line && /^ *$/.test(nextLine)) { // Items begin with at most one blank line
|
|
1415
|
-
raw += nextLine + '\n';
|
|
1416
|
-
src = src.substring(nextLine.length + 1);
|
|
1417
|
-
endEarly = true;
|
|
1418
|
-
}
|
|
1419
|
-
if (!endEarly) {
|
|
1420
|
-
const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`);
|
|
1421
|
-
const hrRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`);
|
|
1422
|
-
const fencesBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:\`\`\`|~~~)`);
|
|
1423
|
-
const headingBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}#`);
|
|
1424
|
-
// Check if following lines should be included in List Item
|
|
1425
|
-
while (src) {
|
|
1426
|
-
const rawLine = src.split('\n', 1)[0];
|
|
1427
|
-
nextLine = rawLine;
|
|
1428
|
-
// Re-align to follow commonmark nesting rules
|
|
1429
|
-
if (this.options.pedantic) {
|
|
1430
|
-
nextLine = nextLine.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' ');
|
|
1431
|
-
}
|
|
1432
|
-
// End list item if found code fences
|
|
1433
|
-
if (fencesBeginRegex.test(nextLine)) {
|
|
1434
|
-
break;
|
|
1435
|
-
}
|
|
1436
|
-
// End list item if found start of new heading
|
|
1437
|
-
if (headingBeginRegex.test(nextLine)) {
|
|
1438
|
-
break;
|
|
1439
|
-
}
|
|
1440
|
-
// End list item if found start of new bullet
|
|
1441
|
-
if (nextBulletRegex.test(nextLine)) {
|
|
1442
|
-
break;
|
|
1443
|
-
}
|
|
1444
|
-
// Horizontal rule found
|
|
1445
|
-
if (hrRegex.test(src)) {
|
|
1446
|
-
break;
|
|
1447
|
-
}
|
|
1448
|
-
if (nextLine.search(/[^ ]/) >= indent || !nextLine.trim()) { // Dedent if possible
|
|
1449
|
-
itemContents += '\n' + nextLine.slice(indent);
|
|
1450
|
-
}
|
|
1451
|
-
else {
|
|
1452
|
-
// not enough indentation
|
|
1453
|
-
if (blankLine) {
|
|
1454
|
-
break;
|
|
1455
|
-
}
|
|
1456
|
-
// paragraph continuation unless last line was a different block level element
|
|
1457
|
-
if (line.search(/[^ ]/) >= 4) { // indented code block
|
|
1458
|
-
break;
|
|
1459
|
-
}
|
|
1460
|
-
if (fencesBeginRegex.test(line)) {
|
|
1461
|
-
break;
|
|
1462
|
-
}
|
|
1463
|
-
if (headingBeginRegex.test(line)) {
|
|
1464
|
-
break;
|
|
1465
|
-
}
|
|
1466
|
-
if (hrRegex.test(line)) {
|
|
1467
|
-
break;
|
|
1468
|
-
}
|
|
1469
|
-
itemContents += '\n' + nextLine;
|
|
1470
|
-
}
|
|
1471
|
-
if (!blankLine && !nextLine.trim()) { // Check if current line is blank
|
|
1472
|
-
blankLine = true;
|
|
1473
|
-
}
|
|
1474
|
-
raw += rawLine + '\n';
|
|
1475
|
-
src = src.substring(rawLine.length + 1);
|
|
1476
|
-
line = nextLine.slice(indent);
|
|
1477
|
-
}
|
|
1478
|
-
}
|
|
1479
|
-
if (!list.loose) {
|
|
1480
|
-
// If the previous item ended with a blank line, the list is loose
|
|
1481
|
-
if (endsWithBlankLine) {
|
|
1482
|
-
list.loose = true;
|
|
1483
|
-
}
|
|
1484
|
-
else if (/\n *\n *$/.test(raw)) {
|
|
1485
|
-
endsWithBlankLine = true;
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
let istask = null;
|
|
1489
|
-
let ischecked;
|
|
1490
|
-
// Check for task list items
|
|
1491
|
-
if (this.options.gfm) {
|
|
1492
|
-
istask = /^\[[ xX]\] /.exec(itemContents);
|
|
1493
|
-
if (istask) {
|
|
1494
|
-
ischecked = istask[0] !== '[ ] ';
|
|
1495
|
-
itemContents = itemContents.replace(/^\[[ xX]\] +/, '');
|
|
1496
|
-
}
|
|
1497
|
-
}
|
|
1498
|
-
list.items.push({
|
|
1499
|
-
type: 'list_item',
|
|
1500
|
-
raw,
|
|
1501
|
-
task: !!istask,
|
|
1502
|
-
checked: ischecked,
|
|
1503
|
-
loose: false,
|
|
1504
|
-
text: itemContents,
|
|
1505
|
-
tokens: []
|
|
1506
|
-
});
|
|
1507
|
-
list.raw += raw;
|
|
1508
|
-
}
|
|
1509
|
-
// Do not consume newlines at end of final item. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic
|
|
1510
|
-
list.items[list.items.length - 1].raw = raw.trimEnd();
|
|
1511
|
-
list.items[list.items.length - 1].text = itemContents.trimEnd();
|
|
1512
|
-
list.raw = list.raw.trimEnd();
|
|
1513
|
-
// Item child tokens handled here at end because we needed to have the final item to trim it first
|
|
1514
|
-
for (let i = 0; i < list.items.length; i++) {
|
|
1515
|
-
this.lexer.state.top = false;
|
|
1516
|
-
list.items[i].tokens = this.lexer.blockTokens(list.items[i].text, []);
|
|
1517
|
-
if (!list.loose) {
|
|
1518
|
-
// Check if list should be loose
|
|
1519
|
-
const spacers = list.items[i].tokens.filter(t => t.type === 'space');
|
|
1520
|
-
const hasMultipleLineBreaks = spacers.length > 0 && spacers.some(t => /\n.*\n/.test(t.raw));
|
|
1521
|
-
list.loose = hasMultipleLineBreaks;
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
// Set all items to loose if list is loose
|
|
1525
|
-
if (list.loose) {
|
|
1526
|
-
for (let i = 0; i < list.items.length; i++) {
|
|
1527
|
-
list.items[i].loose = true;
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
return list;
|
|
1531
|
-
}
|
|
1532
|
-
}
|
|
1533
|
-
html(src) {
|
|
1534
|
-
const cap = this.rules.block.html.exec(src);
|
|
1535
|
-
if (cap) {
|
|
1536
|
-
const token = {
|
|
1537
|
-
type: 'html',
|
|
1538
|
-
block: true,
|
|
1539
|
-
raw: cap[0],
|
|
1540
|
-
pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
|
|
1541
|
-
text: cap[0]
|
|
1542
|
-
};
|
|
1543
|
-
return token;
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
def(src) {
|
|
1547
|
-
const cap = this.rules.block.def.exec(src);
|
|
1548
|
-
if (cap) {
|
|
1549
|
-
const tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
|
|
1550
|
-
const href = cap[2] ? cap[2].replace(/^<(.*)>$/, '$1').replace(this.rules.inline._escapes, '$1') : '';
|
|
1551
|
-
const title = cap[3] ? cap[3].substring(1, cap[3].length - 1).replace(this.rules.inline._escapes, '$1') : cap[3];
|
|
1552
|
-
return {
|
|
1553
|
-
type: 'def',
|
|
1554
|
-
tag,
|
|
1555
|
-
raw: cap[0],
|
|
1556
|
-
href,
|
|
1557
|
-
title
|
|
1558
|
-
};
|
|
1559
|
-
}
|
|
1560
|
-
}
|
|
1561
|
-
table(src) {
|
|
1562
|
-
const cap = this.rules.block.table.exec(src);
|
|
1563
|
-
if (cap) {
|
|
1564
|
-
if (!/[:|]/.test(cap[2])) {
|
|
1565
|
-
// delimiter row must have a pipe (|) or colon (:) otherwise it is a setext heading
|
|
1566
|
-
return;
|
|
1567
|
-
}
|
|
1568
|
-
const item = {
|
|
1569
|
-
type: 'table',
|
|
1570
|
-
raw: cap[0],
|
|
1571
|
-
header: splitCells(cap[1]).map(c => {
|
|
1572
|
-
return { text: c, tokens: [] };
|
|
1573
|
-
}),
|
|
1574
|
-
align: cap[2].replace(/^\||\| *$/g, '').split('|'),
|
|
1575
|
-
rows: cap[3] && cap[3].trim() ? cap[3].replace(/\n[ \t]*$/, '').split('\n') : []
|
|
1576
|
-
};
|
|
1577
|
-
if (item.header.length === item.align.length) {
|
|
1578
|
-
let l = item.align.length;
|
|
1579
|
-
let i, j, k, row;
|
|
1580
|
-
for (i = 0; i < l; i++) {
|
|
1581
|
-
const align = item.align[i];
|
|
1582
|
-
if (align) {
|
|
1583
|
-
if (/^ *-+: *$/.test(align)) {
|
|
1584
|
-
item.align[i] = 'right';
|
|
1585
|
-
}
|
|
1586
|
-
else if (/^ *:-+: *$/.test(align)) {
|
|
1587
|
-
item.align[i] = 'center';
|
|
1588
|
-
}
|
|
1589
|
-
else if (/^ *:-+ *$/.test(align)) {
|
|
1590
|
-
item.align[i] = 'left';
|
|
1591
|
-
}
|
|
1592
|
-
else {
|
|
1593
|
-
item.align[i] = null;
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
}
|
|
1597
|
-
l = item.rows.length;
|
|
1598
|
-
for (i = 0; i < l; i++) {
|
|
1599
|
-
item.rows[i] = splitCells(item.rows[i], item.header.length).map(c => {
|
|
1600
|
-
return { text: c, tokens: [] };
|
|
1601
|
-
});
|
|
1602
|
-
}
|
|
1603
|
-
// parse child tokens inside headers and cells
|
|
1604
|
-
// header child tokens
|
|
1605
|
-
l = item.header.length;
|
|
1606
|
-
for (j = 0; j < l; j++) {
|
|
1607
|
-
item.header[j].tokens = this.lexer.inline(item.header[j].text);
|
|
1608
|
-
}
|
|
1609
|
-
// cell child tokens
|
|
1610
|
-
l = item.rows.length;
|
|
1611
|
-
for (j = 0; j < l; j++) {
|
|
1612
|
-
row = item.rows[j];
|
|
1613
|
-
for (k = 0; k < row.length; k++) {
|
|
1614
|
-
row[k].tokens = this.lexer.inline(row[k].text);
|
|
1615
|
-
}
|
|
1616
|
-
}
|
|
1617
|
-
return item;
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
1621
|
-
lheading(src) {
|
|
1622
|
-
const cap = this.rules.block.lheading.exec(src);
|
|
1623
|
-
if (cap) {
|
|
1624
|
-
return {
|
|
1625
|
-
type: 'heading',
|
|
1626
|
-
raw: cap[0],
|
|
1627
|
-
depth: cap[2].charAt(0) === '=' ? 1 : 2,
|
|
1628
|
-
text: cap[1],
|
|
1629
|
-
tokens: this.lexer.inline(cap[1])
|
|
1630
|
-
};
|
|
1631
|
-
}
|
|
1632
|
-
}
|
|
1633
|
-
paragraph(src) {
|
|
1634
|
-
const cap = this.rules.block.paragraph.exec(src);
|
|
1635
|
-
if (cap) {
|
|
1636
|
-
const text = cap[1].charAt(cap[1].length - 1) === '\n'
|
|
1637
|
-
? cap[1].slice(0, -1)
|
|
1638
|
-
: cap[1];
|
|
1639
|
-
return {
|
|
1640
|
-
type: 'paragraph',
|
|
1641
|
-
raw: cap[0],
|
|
1642
|
-
text,
|
|
1643
|
-
tokens: this.lexer.inline(text)
|
|
1644
|
-
};
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
text(src) {
|
|
1648
|
-
const cap = this.rules.block.text.exec(src);
|
|
1649
|
-
if (cap) {
|
|
1650
|
-
return {
|
|
1651
|
-
type: 'text',
|
|
1652
|
-
raw: cap[0],
|
|
1653
|
-
text: cap[0],
|
|
1654
|
-
tokens: this.lexer.inline(cap[0])
|
|
1655
|
-
};
|
|
1656
|
-
}
|
|
1657
|
-
}
|
|
1658
|
-
escape(src) {
|
|
1659
|
-
const cap = this.rules.inline.escape.exec(src);
|
|
1660
|
-
if (cap) {
|
|
1661
|
-
return {
|
|
1662
|
-
type: 'escape',
|
|
1663
|
-
raw: cap[0],
|
|
1664
|
-
text: escape(cap[1])
|
|
1665
|
-
};
|
|
1666
|
-
}
|
|
1667
|
-
}
|
|
1668
|
-
tag(src) {
|
|
1669
|
-
const cap = this.rules.inline.tag.exec(src);
|
|
1670
|
-
if (cap) {
|
|
1671
|
-
if (!this.lexer.state.inLink && /^<a /i.test(cap[0])) {
|
|
1672
|
-
this.lexer.state.inLink = true;
|
|
1673
|
-
}
|
|
1674
|
-
else if (this.lexer.state.inLink && /^<\/a>/i.test(cap[0])) {
|
|
1675
|
-
this.lexer.state.inLink = false;
|
|
1676
|
-
}
|
|
1677
|
-
if (!this.lexer.state.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
|
|
1678
|
-
this.lexer.state.inRawBlock = true;
|
|
1679
|
-
}
|
|
1680
|
-
else if (this.lexer.state.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
|
|
1681
|
-
this.lexer.state.inRawBlock = false;
|
|
1682
|
-
}
|
|
1683
|
-
return {
|
|
1684
|
-
type: 'html',
|
|
1685
|
-
raw: cap[0],
|
|
1686
|
-
inLink: this.lexer.state.inLink,
|
|
1687
|
-
inRawBlock: this.lexer.state.inRawBlock,
|
|
1688
|
-
block: false,
|
|
1689
|
-
text: cap[0]
|
|
1690
|
-
};
|
|
1691
|
-
}
|
|
1692
|
-
}
|
|
1693
|
-
link(src) {
|
|
1694
|
-
const cap = this.rules.inline.link.exec(src);
|
|
1695
|
-
if (cap) {
|
|
1696
|
-
const trimmedUrl = cap[2].trim();
|
|
1697
|
-
if (!this.options.pedantic && /^</.test(trimmedUrl)) {
|
|
1698
|
-
// commonmark requires matching angle brackets
|
|
1699
|
-
if (!(/>$/.test(trimmedUrl))) {
|
|
1700
|
-
return;
|
|
1701
|
-
}
|
|
1702
|
-
// ending angle bracket cannot be escaped
|
|
1703
|
-
const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\');
|
|
1704
|
-
if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {
|
|
1705
|
-
return;
|
|
1706
|
-
}
|
|
1707
|
-
}
|
|
1708
|
-
else {
|
|
1709
|
-
// find closing parenthesis
|
|
1710
|
-
const lastParenIndex = findClosingBracket(cap[2], '()');
|
|
1711
|
-
if (lastParenIndex > -1) {
|
|
1712
|
-
const start = cap[0].indexOf('!') === 0 ? 5 : 4;
|
|
1713
|
-
const linkLen = start + cap[1].length + lastParenIndex;
|
|
1714
|
-
cap[2] = cap[2].substring(0, lastParenIndex);
|
|
1715
|
-
cap[0] = cap[0].substring(0, linkLen).trim();
|
|
1716
|
-
cap[3] = '';
|
|
1717
|
-
}
|
|
1718
|
-
}
|
|
1719
|
-
let href = cap[2];
|
|
1720
|
-
let title = '';
|
|
1721
|
-
if (this.options.pedantic) {
|
|
1722
|
-
// split pedantic href and title
|
|
1723
|
-
const link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
|
|
1724
|
-
if (link) {
|
|
1725
|
-
href = link[1];
|
|
1726
|
-
title = link[3];
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
else {
|
|
1730
|
-
title = cap[3] ? cap[3].slice(1, -1) : '';
|
|
1731
|
-
}
|
|
1732
|
-
href = href.trim();
|
|
1733
|
-
if (/^</.test(href)) {
|
|
1734
|
-
if (this.options.pedantic && !(/>$/.test(trimmedUrl))) {
|
|
1735
|
-
// pedantic allows starting angle bracket without ending angle bracket
|
|
1736
|
-
href = href.slice(1);
|
|
1737
|
-
}
|
|
1738
|
-
else {
|
|
1739
|
-
href = href.slice(1, -1);
|
|
1740
|
-
}
|
|
1741
|
-
}
|
|
1742
|
-
return outputLink(cap, {
|
|
1743
|
-
href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
|
|
1744
|
-
title: title ? title.replace(this.rules.inline._escapes, '$1') : title
|
|
1745
|
-
}, cap[0], this.lexer);
|
|
1746
|
-
}
|
|
1747
|
-
}
|
|
1748
|
-
reflink(src, links) {
|
|
1749
|
-
let cap;
|
|
1750
|
-
if ((cap = this.rules.inline.reflink.exec(src))
|
|
1751
|
-
|| (cap = this.rules.inline.nolink.exec(src))) {
|
|
1752
|
-
let link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
|
|
1753
|
-
link = links[link.toLowerCase()];
|
|
1754
|
-
if (!link) {
|
|
1755
|
-
const text = cap[0].charAt(0);
|
|
1756
|
-
return {
|
|
1757
|
-
type: 'text',
|
|
1758
|
-
raw: text,
|
|
1759
|
-
text
|
|
1760
|
-
};
|
|
1761
|
-
}
|
|
1762
|
-
return outputLink(cap, link, cap[0], this.lexer);
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1765
|
-
emStrong(src, maskedSrc, prevChar = '') {
|
|
1766
|
-
let match = this.rules.inline.emStrong.lDelim.exec(src);
|
|
1767
|
-
if (!match)
|
|
1768
|
-
return;
|
|
1769
|
-
// _ can't be between two alphanumerics. \p{L}\p{N} includes non-english alphabet/numbers as well
|
|
1770
|
-
if (match[3] && prevChar.match(/[\p{L}\p{N}]/u))
|
|
1771
|
-
return;
|
|
1772
|
-
const nextChar = match[1] || match[2] || '';
|
|
1773
|
-
if (!nextChar || !prevChar || this.rules.inline.punctuation.exec(prevChar)) {
|
|
1774
|
-
// unicode Regex counts emoji as 1 char; spread into array for proper count (used multiple times below)
|
|
1775
|
-
const lLength = [...match[0]].length - 1;
|
|
1776
|
-
let rDelim, rLength, delimTotal = lLength, midDelimTotal = 0;
|
|
1777
|
-
const endReg = match[0][0] === '*' ? this.rules.inline.emStrong.rDelimAst : this.rules.inline.emStrong.rDelimUnd;
|
|
1778
|
-
endReg.lastIndex = 0;
|
|
1779
|
-
// Clip maskedSrc to same section of string as src (move to lexer?)
|
|
1780
|
-
maskedSrc = maskedSrc.slice(-1 * src.length + lLength);
|
|
1781
|
-
while ((match = endReg.exec(maskedSrc)) != null) {
|
|
1782
|
-
rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];
|
|
1783
|
-
if (!rDelim)
|
|
1784
|
-
continue; // skip single * in __abc*abc__
|
|
1785
|
-
rLength = [...rDelim].length;
|
|
1786
|
-
if (match[3] || match[4]) { // found another Left Delim
|
|
1787
|
-
delimTotal += rLength;
|
|
1788
|
-
continue;
|
|
1789
|
-
}
|
|
1790
|
-
else if (match[5] || match[6]) { // either Left or Right Delim
|
|
1791
|
-
if (lLength % 3 && !((lLength + rLength) % 3)) {
|
|
1792
|
-
midDelimTotal += rLength;
|
|
1793
|
-
continue; // CommonMark Emphasis Rules 9-10
|
|
1794
|
-
}
|
|
1795
|
-
}
|
|
1796
|
-
delimTotal -= rLength;
|
|
1797
|
-
if (delimTotal > 0)
|
|
1798
|
-
continue; // Haven't found enough closing delimiters
|
|
1799
|
-
// Remove extra characters. *a*** -> *a*
|
|
1800
|
-
rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);
|
|
1801
|
-
// char length can be >1 for unicode characters;
|
|
1802
|
-
const lastCharLength = [...match[0]][0].length;
|
|
1803
|
-
const raw = src.slice(0, lLength + match.index + lastCharLength + rLength);
|
|
1804
|
-
// Create `em` if smallest delimiter has odd char count. *a***
|
|
1805
|
-
if (Math.min(lLength, rLength) % 2) {
|
|
1806
|
-
const text = raw.slice(1, -1);
|
|
1807
|
-
return {
|
|
1808
|
-
type: 'em',
|
|
1809
|
-
raw,
|
|
1810
|
-
text,
|
|
1811
|
-
tokens: this.lexer.inlineTokens(text)
|
|
1812
|
-
};
|
|
1813
|
-
}
|
|
1814
|
-
// Create 'strong' if smallest delimiter has even char count. **a***
|
|
1815
|
-
const text = raw.slice(2, -2);
|
|
1816
|
-
return {
|
|
1817
|
-
type: 'strong',
|
|
1818
|
-
raw,
|
|
1819
|
-
text,
|
|
1820
|
-
tokens: this.lexer.inlineTokens(text)
|
|
1821
|
-
};
|
|
1822
|
-
}
|
|
1823
|
-
}
|
|
1824
|
-
}
|
|
1825
|
-
codespan(src) {
|
|
1826
|
-
const cap = this.rules.inline.code.exec(src);
|
|
1827
|
-
if (cap) {
|
|
1828
|
-
let text = cap[2].replace(/\n/g, ' ');
|
|
1829
|
-
const hasNonSpaceChars = /[^ ]/.test(text);
|
|
1830
|
-
const hasSpaceCharsOnBothEnds = /^ /.test(text) && / $/.test(text);
|
|
1831
|
-
if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
|
|
1832
|
-
text = text.substring(1, text.length - 1);
|
|
1833
|
-
}
|
|
1834
|
-
text = escape(text, true);
|
|
1835
|
-
return {
|
|
1836
|
-
type: 'codespan',
|
|
1837
|
-
raw: cap[0],
|
|
1838
|
-
text
|
|
1839
|
-
};
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
1842
|
-
br(src) {
|
|
1843
|
-
const cap = this.rules.inline.br.exec(src);
|
|
1844
|
-
if (cap) {
|
|
1845
|
-
return {
|
|
1846
|
-
type: 'br',
|
|
1847
|
-
raw: cap[0]
|
|
1848
|
-
};
|
|
1849
|
-
}
|
|
1850
|
-
}
|
|
1851
|
-
del(src) {
|
|
1852
|
-
const cap = this.rules.inline.del.exec(src);
|
|
1853
|
-
if (cap) {
|
|
1854
|
-
return {
|
|
1855
|
-
type: 'del',
|
|
1856
|
-
raw: cap[0],
|
|
1857
|
-
text: cap[2],
|
|
1858
|
-
tokens: this.lexer.inlineTokens(cap[2])
|
|
1859
|
-
};
|
|
1860
|
-
}
|
|
1861
|
-
}
|
|
1862
|
-
autolink(src) {
|
|
1863
|
-
const cap = this.rules.inline.autolink.exec(src);
|
|
1864
|
-
if (cap) {
|
|
1865
|
-
let text, href;
|
|
1866
|
-
if (cap[2] === '@') {
|
|
1867
|
-
text = escape(cap[1]);
|
|
1868
|
-
href = 'mailto:' + text;
|
|
1869
|
-
}
|
|
1870
|
-
else {
|
|
1871
|
-
text = escape(cap[1]);
|
|
1872
|
-
href = text;
|
|
1873
|
-
}
|
|
1874
|
-
return {
|
|
1875
|
-
type: 'link',
|
|
1876
|
-
raw: cap[0],
|
|
1877
|
-
text,
|
|
1878
|
-
href,
|
|
1879
|
-
tokens: [
|
|
1880
|
-
{
|
|
1881
|
-
type: 'text',
|
|
1882
|
-
raw: text,
|
|
1883
|
-
text
|
|
1884
|
-
}
|
|
1885
|
-
]
|
|
1886
|
-
};
|
|
1887
|
-
}
|
|
1888
|
-
}
|
|
1889
|
-
url(src) {
|
|
1890
|
-
let cap;
|
|
1891
|
-
if (cap = this.rules.inline.url.exec(src)) {
|
|
1892
|
-
let text, href;
|
|
1893
|
-
if (cap[2] === '@') {
|
|
1894
|
-
text = escape(cap[0]);
|
|
1895
|
-
href = 'mailto:' + text;
|
|
1896
|
-
}
|
|
1897
|
-
else {
|
|
1898
|
-
// do extended autolink path validation
|
|
1899
|
-
let prevCapZero;
|
|
1900
|
-
do {
|
|
1901
|
-
prevCapZero = cap[0];
|
|
1902
|
-
cap[0] = this.rules.inline._backpedal.exec(cap[0])[0];
|
|
1903
|
-
} while (prevCapZero !== cap[0]);
|
|
1904
|
-
text = escape(cap[0]);
|
|
1905
|
-
if (cap[1] === 'www.') {
|
|
1906
|
-
href = 'http://' + cap[0];
|
|
1907
|
-
}
|
|
1908
|
-
else {
|
|
1909
|
-
href = cap[0];
|
|
1910
|
-
}
|
|
1911
|
-
}
|
|
1912
|
-
return {
|
|
1913
|
-
type: 'link',
|
|
1914
|
-
raw: cap[0],
|
|
1915
|
-
text,
|
|
1916
|
-
href,
|
|
1917
|
-
tokens: [
|
|
1918
|
-
{
|
|
1919
|
-
type: 'text',
|
|
1920
|
-
raw: text,
|
|
1921
|
-
text
|
|
1922
|
-
}
|
|
1923
|
-
]
|
|
1924
|
-
};
|
|
1925
|
-
}
|
|
1926
|
-
}
|
|
1927
|
-
inlineText(src) {
|
|
1928
|
-
const cap = this.rules.inline.text.exec(src);
|
|
1929
|
-
if (cap) {
|
|
1930
|
-
let text;
|
|
1931
|
-
if (this.lexer.state.inRawBlock) {
|
|
1932
|
-
text = cap[0];
|
|
1933
|
-
}
|
|
1934
|
-
else {
|
|
1935
|
-
text = escape(cap[0]);
|
|
1936
|
-
}
|
|
1937
|
-
return {
|
|
1938
|
-
type: 'text',
|
|
1939
|
-
raw: cap[0],
|
|
1940
|
-
text
|
|
1941
|
-
};
|
|
1942
|
-
}
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1945
|
-
|
|
1946
|
-
/**
|
|
1947
|
-
* Block-Level Grammar
|
|
1948
|
-
*/
|
|
1949
|
-
// Not all rules are defined in the object literal
|
|
1950
|
-
// @ts-expect-error
|
|
1951
|
-
const block = {
|
|
1952
|
-
newline: /^(?: *(?:\n|$))+/,
|
|
1953
|
-
code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,
|
|
1954
|
-
fences: /^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,
|
|
1955
|
-
hr: /^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,
|
|
1956
|
-
heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,
|
|
1957
|
-
blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
|
|
1958
|
-
list: /^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/,
|
|
1959
|
-
html: '^ {0,3}(?:' // optional indentation
|
|
1960
|
-
+ '<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
|
|
1961
|
-
+ '|comment[^\\n]*(\\n+|$)' // (2)
|
|
1962
|
-
+ '|<\\?[\\s\\S]*?(?:\\?>\\n*|$)' // (3)
|
|
1963
|
-
+ '|<![A-Z][\\s\\S]*?(?:>\\n*|$)' // (4)
|
|
1964
|
-
+ '|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)' // (5)
|
|
1965
|
-
+ '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (6)
|
|
1966
|
-
+ '|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) open tag
|
|
1967
|
-
+ '|</(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) closing tag
|
|
1968
|
-
+ ')',
|
|
1969
|
-
def: /^ {0,3}\[(label)\]: *(?:\n *)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/,
|
|
1970
|
-
table: noopTest,
|
|
1971
|
-
lheading: /^(?!bull )((?:.|\n(?!\s*?\n|bull ))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,
|
|
1972
|
-
// regex template, placeholders will be replaced according to different paragraph
|
|
1973
|
-
// interruption rules of commonmark and the original markdown spec:
|
|
1974
|
-
_paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,
|
|
1975
|
-
text: /^[^\n]+/
|
|
1976
|
-
};
|
|
1977
|
-
block._label = /(?!\s*\])(?:\\.|[^\[\]\\])+/;
|
|
1978
|
-
block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
|
|
1979
|
-
block.def = edit(block.def)
|
|
1980
|
-
.replace('label', block._label)
|
|
1981
|
-
.replace('title', block._title)
|
|
1982
|
-
.getRegex();
|
|
1983
|
-
block.bullet = /(?:[*+-]|\d{1,9}[.)])/;
|
|
1984
|
-
block.listItemStart = edit(/^( *)(bull) */)
|
|
1985
|
-
.replace('bull', block.bullet)
|
|
1986
|
-
.getRegex();
|
|
1987
|
-
block.list = edit(block.list)
|
|
1988
|
-
.replace(/bull/g, block.bullet)
|
|
1989
|
-
.replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))')
|
|
1990
|
-
.replace('def', '\\n+(?=' + block.def.source + ')')
|
|
1991
|
-
.getRegex();
|
|
1992
|
-
block._tag = 'address|article|aside|base|basefont|blockquote|body|caption'
|
|
1993
|
-
+ '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'
|
|
1994
|
-
+ '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'
|
|
1995
|
-
+ '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'
|
|
1996
|
-
+ '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr'
|
|
1997
|
-
+ '|track|ul';
|
|
1998
|
-
block._comment = /<!--(?!-?>)[\s\S]*?(?:-->|$)/;
|
|
1999
|
-
block.html = edit(block.html, 'i')
|
|
2000
|
-
.replace('comment', block._comment)
|
|
2001
|
-
.replace('tag', block._tag)
|
|
2002
|
-
.replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/)
|
|
2003
|
-
.getRegex();
|
|
2004
|
-
block.lheading = edit(block.lheading)
|
|
2005
|
-
.replace(/bull/g, block.bullet) // lists can interrupt
|
|
2006
|
-
.getRegex();
|
|
2007
|
-
block.paragraph = edit(block._paragraph)
|
|
2008
|
-
.replace('hr', block.hr)
|
|
2009
|
-
.replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
|
|
2010
|
-
.replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs
|
|
2011
|
-
.replace('|table', '')
|
|
2012
|
-
.replace('blockquote', ' {0,3}>')
|
|
2013
|
-
.replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
|
|
2014
|
-
.replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
|
|
2015
|
-
.replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
|
|
2016
|
-
.replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
|
|
2017
|
-
.getRegex();
|
|
2018
|
-
block.blockquote = edit(block.blockquote)
|
|
2019
|
-
.replace('paragraph', block.paragraph)
|
|
2020
|
-
.getRegex();
|
|
2021
|
-
/**
|
|
2022
|
-
* Normal Block Grammar
|
|
2023
|
-
*/
|
|
2024
|
-
block.normal = { ...block };
|
|
2025
|
-
/**
|
|
2026
|
-
* GFM Block Grammar
|
|
2027
|
-
*/
|
|
2028
|
-
block.gfm = {
|
|
2029
|
-
...block.normal,
|
|
2030
|
-
table: '^ *([^\\n ].*)\\n' // Header
|
|
2031
|
-
+ ' {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)' // Align
|
|
2032
|
-
+ '(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells
|
|
2033
|
-
};
|
|
2034
|
-
block.gfm.table = edit(block.gfm.table)
|
|
2035
|
-
.replace('hr', block.hr)
|
|
2036
|
-
.replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
|
|
2037
|
-
.replace('blockquote', ' {0,3}>')
|
|
2038
|
-
.replace('code', ' {4}[^\\n]')
|
|
2039
|
-
.replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
|
|
2040
|
-
.replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
|
|
2041
|
-
.replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
|
|
2042
|
-
.replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
|
|
2043
|
-
.getRegex();
|
|
2044
|
-
block.gfm.paragraph = edit(block._paragraph)
|
|
2045
|
-
.replace('hr', block.hr)
|
|
2046
|
-
.replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
|
|
2047
|
-
.replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs
|
|
2048
|
-
.replace('table', block.gfm.table) // interrupt paragraphs with table
|
|
2049
|
-
.replace('blockquote', ' {0,3}>')
|
|
2050
|
-
.replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
|
|
2051
|
-
.replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
|
|
2052
|
-
.replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
|
|
2053
|
-
.replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
|
|
2054
|
-
.getRegex();
|
|
2055
|
-
/**
|
|
2056
|
-
* Pedantic grammar (original John Gruber's loose markdown specification)
|
|
2057
|
-
*/
|
|
2058
|
-
block.pedantic = {
|
|
2059
|
-
...block.normal,
|
|
2060
|
-
html: edit('^ *(?:comment *(?:\\n|\\s*$)'
|
|
2061
|
-
+ '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
|
|
2062
|
-
+ '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))')
|
|
2063
|
-
.replace('comment', block._comment)
|
|
2064
|
-
.replace(/tag/g, '(?!(?:'
|
|
2065
|
-
+ 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'
|
|
2066
|
-
+ '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'
|
|
2067
|
-
+ '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b')
|
|
2068
|
-
.getRegex(),
|
|
2069
|
-
def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
|
|
2070
|
-
heading: /^(#{1,6})(.*)(?:\n+|$)/,
|
|
2071
|
-
fences: noopTest,
|
|
2072
|
-
lheading: /^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,
|
|
2073
|
-
paragraph: edit(block.normal._paragraph)
|
|
2074
|
-
.replace('hr', block.hr)
|
|
2075
|
-
.replace('heading', ' *#{1,6} *[^\n]')
|
|
2076
|
-
.replace('lheading', block.lheading)
|
|
2077
|
-
.replace('blockquote', ' {0,3}>')
|
|
2078
|
-
.replace('|fences', '')
|
|
2079
|
-
.replace('|list', '')
|
|
2080
|
-
.replace('|html', '')
|
|
2081
|
-
.getRegex()
|
|
2082
|
-
};
|
|
2083
|
-
/**
|
|
2084
|
-
* Inline-Level Grammar
|
|
2085
|
-
*/
|
|
2086
|
-
// Not all rules are defined in the object literal
|
|
2087
|
-
// @ts-expect-error
|
|
2088
|
-
const inline = {
|
|
2089
|
-
escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
|
|
2090
|
-
autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
|
|
2091
|
-
url: noopTest,
|
|
2092
|
-
tag: '^comment'
|
|
2093
|
-
+ '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
|
|
2094
|
-
+ '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
|
|
2095
|
-
+ '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
|
|
2096
|
-
+ '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
|
|
2097
|
-
+ '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>',
|
|
2098
|
-
link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,
|
|
2099
|
-
reflink: /^!?\[(label)\]\[(ref)\]/,
|
|
2100
|
-
nolink: /^!?\[(ref)\](?:\[\])?/,
|
|
2101
|
-
reflinkSearch: 'reflink|nolink(?!\\()',
|
|
2102
|
-
emStrong: {
|
|
2103
|
-
lDelim: /^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/,
|
|
2104
|
-
// (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.
|
|
2105
|
-
// | Skip orphan inside strong | Consume to delim | (1) #*** | (2) a***#, a*** | (3) #***a, ***a | (4) ***# | (5) #***# | (6) a***a
|
|
2106
|
-
rDelimAst: /^[^_*]*?__[^_*]*?\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\*)[punct](\*+)(?=[\s]|$)|[^punct\s](\*+)(?!\*)(?=[punct\s]|$)|(?!\*)[punct\s](\*+)(?=[^punct\s])|[\s](\*+)(?!\*)(?=[punct])|(?!\*)[punct](\*+)(?!\*)(?=[punct])|[^punct\s](\*+)(?=[^punct\s])/,
|
|
2107
|
-
rDelimUnd: /^[^_*]*?\*\*[^_*]*?_[^_*]*?(?=\*\*)|[^_]+(?=[^_])|(?!_)[punct](_+)(?=[\s]|$)|[^punct\s](_+)(?!_)(?=[punct\s]|$)|(?!_)[punct\s](_+)(?=[^punct\s])|[\s](_+)(?!_)(?=[punct])|(?!_)[punct](_+)(?!_)(?=[punct])/ // ^- Not allowed for _
|
|
2108
|
-
},
|
|
2109
|
-
code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
|
|
2110
|
-
br: /^( {2,}|\\)\n(?!\s*$)/,
|
|
2111
|
-
del: noopTest,
|
|
2112
|
-
text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/,
|
|
2113
|
-
punctuation: /^((?![*_])[\spunctuation])/
|
|
2114
|
-
};
|
|
2115
|
-
// list of unicode punctuation marks, plus any missing characters from CommonMark spec
|
|
2116
|
-
inline._punctuation = '\\p{P}$+<=>`^|~';
|
|
2117
|
-
inline.punctuation = edit(inline.punctuation, 'u').replace(/punctuation/g, inline._punctuation).getRegex();
|
|
2118
|
-
// sequences em should skip over [title](link), `code`, <html>
|
|
2119
|
-
inline.blockSkip = /\[[^[\]]*?\]\([^\(\)]*?\)|`[^`]*?`|<[^<>]*?>/g;
|
|
2120
|
-
inline.anyPunctuation = /\\[punct]/g;
|
|
2121
|
-
inline._escapes = /\\([punct])/g;
|
|
2122
|
-
inline._comment = edit(block._comment).replace('(?:-->|$)', '-->').getRegex();
|
|
2123
|
-
inline.emStrong.lDelim = edit(inline.emStrong.lDelim, 'u')
|
|
2124
|
-
.replace(/punct/g, inline._punctuation)
|
|
2125
|
-
.getRegex();
|
|
2126
|
-
inline.emStrong.rDelimAst = edit(inline.emStrong.rDelimAst, 'gu')
|
|
2127
|
-
.replace(/punct/g, inline._punctuation)
|
|
2128
|
-
.getRegex();
|
|
2129
|
-
inline.emStrong.rDelimUnd = edit(inline.emStrong.rDelimUnd, 'gu')
|
|
2130
|
-
.replace(/punct/g, inline._punctuation)
|
|
2131
|
-
.getRegex();
|
|
2132
|
-
inline.anyPunctuation = edit(inline.anyPunctuation, 'gu')
|
|
2133
|
-
.replace(/punct/g, inline._punctuation)
|
|
2134
|
-
.getRegex();
|
|
2135
|
-
inline._escapes = edit(inline._escapes, 'gu')
|
|
2136
|
-
.replace(/punct/g, inline._punctuation)
|
|
2137
|
-
.getRegex();
|
|
2138
|
-
inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
|
|
2139
|
-
inline._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])?)+(?![-_])/;
|
|
2140
|
-
inline.autolink = edit(inline.autolink)
|
|
2141
|
-
.replace('scheme', inline._scheme)
|
|
2142
|
-
.replace('email', inline._email)
|
|
2143
|
-
.getRegex();
|
|
2144
|
-
inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
|
|
2145
|
-
inline.tag = edit(inline.tag)
|
|
2146
|
-
.replace('comment', inline._comment)
|
|
2147
|
-
.replace('attribute', inline._attribute)
|
|
2148
|
-
.getRegex();
|
|
2149
|
-
inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
|
|
2150
|
-
inline._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/;
|
|
2151
|
-
inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
|
|
2152
|
-
inline.link = edit(inline.link)
|
|
2153
|
-
.replace('label', inline._label)
|
|
2154
|
-
.replace('href', inline._href)
|
|
2155
|
-
.replace('title', inline._title)
|
|
2156
|
-
.getRegex();
|
|
2157
|
-
inline.reflink = edit(inline.reflink)
|
|
2158
|
-
.replace('label', inline._label)
|
|
2159
|
-
.replace('ref', block._label)
|
|
2160
|
-
.getRegex();
|
|
2161
|
-
inline.nolink = edit(inline.nolink)
|
|
2162
|
-
.replace('ref', block._label)
|
|
2163
|
-
.getRegex();
|
|
2164
|
-
inline.reflinkSearch = edit(inline.reflinkSearch, 'g')
|
|
2165
|
-
.replace('reflink', inline.reflink)
|
|
2166
|
-
.replace('nolink', inline.nolink)
|
|
2167
|
-
.getRegex();
|
|
2168
|
-
/**
|
|
2169
|
-
* Normal Inline Grammar
|
|
2170
|
-
*/
|
|
2171
|
-
inline.normal = { ...inline };
|
|
2172
|
-
/**
|
|
2173
|
-
* Pedantic Inline Grammar
|
|
2174
|
-
*/
|
|
2175
|
-
inline.pedantic = {
|
|
2176
|
-
...inline.normal,
|
|
2177
|
-
strong: {
|
|
2178
|
-
start: /^__|\*\*/,
|
|
2179
|
-
middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
|
|
2180
|
-
endAst: /\*\*(?!\*)/g,
|
|
2181
|
-
endUnd: /__(?!_)/g
|
|
2182
|
-
},
|
|
2183
|
-
em: {
|
|
2184
|
-
start: /^_|\*/,
|
|
2185
|
-
middle: /^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,
|
|
2186
|
-
endAst: /\*(?!\*)/g,
|
|
2187
|
-
endUnd: /_(?!_)/g
|
|
2188
|
-
},
|
|
2189
|
-
link: edit(/^!?\[(label)\]\((.*?)\)/)
|
|
2190
|
-
.replace('label', inline._label)
|
|
2191
|
-
.getRegex(),
|
|
2192
|
-
reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/)
|
|
2193
|
-
.replace('label', inline._label)
|
|
2194
|
-
.getRegex()
|
|
2195
|
-
};
|
|
2196
|
-
/**
|
|
2197
|
-
* GFM Inline Grammar
|
|
2198
|
-
*/
|
|
2199
|
-
inline.gfm = {
|
|
2200
|
-
...inline.normal,
|
|
2201
|
-
escape: edit(inline.escape).replace('])', '~|])').getRegex(),
|
|
2202
|
-
_extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
|
|
2203
|
-
url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
|
|
2204
|
-
_backpedal: /(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,
|
|
2205
|
-
del: /^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,
|
|
2206
|
-
text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/
|
|
2207
|
-
};
|
|
2208
|
-
inline.gfm.url = edit(inline.gfm.url, 'i')
|
|
2209
|
-
.replace('email', inline.gfm._extended_email)
|
|
2210
|
-
.getRegex();
|
|
2211
|
-
/**
|
|
2212
|
-
* GFM + Line Breaks Inline Grammar
|
|
2213
|
-
*/
|
|
2214
|
-
inline.breaks = {
|
|
2215
|
-
...inline.gfm,
|
|
2216
|
-
br: edit(inline.br).replace('{2,}', '*').getRegex(),
|
|
2217
|
-
text: edit(inline.gfm.text)
|
|
2218
|
-
.replace('\\b_', '\\b_| {2,}\\n')
|
|
2219
|
-
.replace(/\{2,\}/g, '*')
|
|
2220
|
-
.getRegex()
|
|
2221
|
-
};
|
|
2222
|
-
|
|
2223
|
-
/**
|
|
2224
|
-
* Block Lexer
|
|
2225
|
-
*/
|
|
2226
|
-
class _Lexer {
|
|
2227
|
-
tokens;
|
|
2228
|
-
options;
|
|
2229
|
-
state;
|
|
2230
|
-
tokenizer;
|
|
2231
|
-
inlineQueue;
|
|
2232
|
-
constructor(options) {
|
|
2233
|
-
// TokenList cannot be created in one go
|
|
2234
|
-
// @ts-expect-error
|
|
2235
|
-
this.tokens = [];
|
|
2236
|
-
this.tokens.links = Object.create(null);
|
|
2237
|
-
this.options = options || _defaults;
|
|
2238
|
-
this.options.tokenizer = this.options.tokenizer || new _Tokenizer();
|
|
2239
|
-
this.tokenizer = this.options.tokenizer;
|
|
2240
|
-
this.tokenizer.options = this.options;
|
|
2241
|
-
this.tokenizer.lexer = this;
|
|
2242
|
-
this.inlineQueue = [];
|
|
2243
|
-
this.state = {
|
|
2244
|
-
inLink: false,
|
|
2245
|
-
inRawBlock: false,
|
|
2246
|
-
top: true
|
|
2247
|
-
};
|
|
2248
|
-
const rules = {
|
|
2249
|
-
block: block.normal,
|
|
2250
|
-
inline: inline.normal
|
|
2251
|
-
};
|
|
2252
|
-
if (this.options.pedantic) {
|
|
2253
|
-
rules.block = block.pedantic;
|
|
2254
|
-
rules.inline = inline.pedantic;
|
|
2255
|
-
}
|
|
2256
|
-
else if (this.options.gfm) {
|
|
2257
|
-
rules.block = block.gfm;
|
|
2258
|
-
if (this.options.breaks) {
|
|
2259
|
-
rules.inline = inline.breaks;
|
|
2260
|
-
}
|
|
2261
|
-
else {
|
|
2262
|
-
rules.inline = inline.gfm;
|
|
2263
|
-
}
|
|
2264
|
-
}
|
|
2265
|
-
this.tokenizer.rules = rules;
|
|
2266
|
-
}
|
|
2267
|
-
/**
|
|
2268
|
-
* Expose Rules
|
|
2269
|
-
*/
|
|
2270
|
-
static get rules() {
|
|
2271
|
-
return {
|
|
2272
|
-
block,
|
|
2273
|
-
inline
|
|
2274
|
-
};
|
|
2275
|
-
}
|
|
2276
|
-
/**
|
|
2277
|
-
* Static Lex Method
|
|
2278
|
-
*/
|
|
2279
|
-
static lex(src, options) {
|
|
2280
|
-
const lexer = new _Lexer(options);
|
|
2281
|
-
return lexer.lex(src);
|
|
2282
|
-
}
|
|
2283
|
-
/**
|
|
2284
|
-
* Static Lex Inline Method
|
|
2285
|
-
*/
|
|
2286
|
-
static lexInline(src, options) {
|
|
2287
|
-
const lexer = new _Lexer(options);
|
|
2288
|
-
return lexer.inlineTokens(src);
|
|
2289
|
-
}
|
|
2290
|
-
/**
|
|
2291
|
-
* Preprocessing
|
|
2292
|
-
*/
|
|
2293
|
-
lex(src) {
|
|
2294
|
-
src = src
|
|
2295
|
-
.replace(/\r\n|\r/g, '\n');
|
|
2296
|
-
this.blockTokens(src, this.tokens);
|
|
2297
|
-
let next;
|
|
2298
|
-
while (next = this.inlineQueue.shift()) {
|
|
2299
|
-
this.inlineTokens(next.src, next.tokens);
|
|
2300
|
-
}
|
|
2301
|
-
return this.tokens;
|
|
2302
|
-
}
|
|
2303
|
-
blockTokens(src, tokens = []) {
|
|
2304
|
-
if (this.options.pedantic) {
|
|
2305
|
-
src = src.replace(/\t/g, ' ').replace(/^ +$/gm, '');
|
|
2306
|
-
}
|
|
2307
|
-
else {
|
|
2308
|
-
src = src.replace(/^( *)(\t+)/gm, (_, leading, tabs) => {
|
|
2309
|
-
return leading + ' '.repeat(tabs.length);
|
|
2310
|
-
});
|
|
2311
|
-
}
|
|
2312
|
-
let token;
|
|
2313
|
-
let lastToken;
|
|
2314
|
-
let cutSrc;
|
|
2315
|
-
let lastParagraphClipped;
|
|
2316
|
-
while (src) {
|
|
2317
|
-
if (this.options.extensions
|
|
2318
|
-
&& this.options.extensions.block
|
|
2319
|
-
&& this.options.extensions.block.some((extTokenizer) => {
|
|
2320
|
-
if (token = extTokenizer.call({ lexer: this }, src, tokens)) {
|
|
2321
|
-
src = src.substring(token.raw.length);
|
|
2322
|
-
tokens.push(token);
|
|
2323
|
-
return true;
|
|
2324
|
-
}
|
|
2325
|
-
return false;
|
|
2326
|
-
})) {
|
|
2327
|
-
continue;
|
|
2328
|
-
}
|
|
2329
|
-
// newline
|
|
2330
|
-
if (token = this.tokenizer.space(src)) {
|
|
2331
|
-
src = src.substring(token.raw.length);
|
|
2332
|
-
if (token.raw.length === 1 && tokens.length > 0) {
|
|
2333
|
-
// if there's a single \n as a spacer, it's terminating the last line,
|
|
2334
|
-
// so move it there so that we don't get unnecessary paragraph tags
|
|
2335
|
-
tokens[tokens.length - 1].raw += '\n';
|
|
2336
|
-
}
|
|
2337
|
-
else {
|
|
2338
|
-
tokens.push(token);
|
|
2339
|
-
}
|
|
2340
|
-
continue;
|
|
2341
|
-
}
|
|
2342
|
-
// code
|
|
2343
|
-
if (token = this.tokenizer.code(src)) {
|
|
2344
|
-
src = src.substring(token.raw.length);
|
|
2345
|
-
lastToken = tokens[tokens.length - 1];
|
|
2346
|
-
// An indented code block cannot interrupt a paragraph.
|
|
2347
|
-
if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) {
|
|
2348
|
-
lastToken.raw += '\n' + token.raw;
|
|
2349
|
-
lastToken.text += '\n' + token.text;
|
|
2350
|
-
this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
|
|
2351
|
-
}
|
|
2352
|
-
else {
|
|
2353
|
-
tokens.push(token);
|
|
2354
|
-
}
|
|
2355
|
-
continue;
|
|
2356
|
-
}
|
|
2357
|
-
// fences
|
|
2358
|
-
if (token = this.tokenizer.fences(src)) {
|
|
2359
|
-
src = src.substring(token.raw.length);
|
|
2360
|
-
tokens.push(token);
|
|
2361
|
-
continue;
|
|
2362
|
-
}
|
|
2363
|
-
// heading
|
|
2364
|
-
if (token = this.tokenizer.heading(src)) {
|
|
2365
|
-
src = src.substring(token.raw.length);
|
|
2366
|
-
tokens.push(token);
|
|
2367
|
-
continue;
|
|
2368
|
-
}
|
|
2369
|
-
// hr
|
|
2370
|
-
if (token = this.tokenizer.hr(src)) {
|
|
2371
|
-
src = src.substring(token.raw.length);
|
|
2372
|
-
tokens.push(token);
|
|
2373
|
-
continue;
|
|
2374
|
-
}
|
|
2375
|
-
// blockquote
|
|
2376
|
-
if (token = this.tokenizer.blockquote(src)) {
|
|
2377
|
-
src = src.substring(token.raw.length);
|
|
2378
|
-
tokens.push(token);
|
|
2379
|
-
continue;
|
|
2380
|
-
}
|
|
2381
|
-
// list
|
|
2382
|
-
if (token = this.tokenizer.list(src)) {
|
|
2383
|
-
src = src.substring(token.raw.length);
|
|
2384
|
-
tokens.push(token);
|
|
2385
|
-
continue;
|
|
2386
|
-
}
|
|
2387
|
-
// html
|
|
2388
|
-
if (token = this.tokenizer.html(src)) {
|
|
2389
|
-
src = src.substring(token.raw.length);
|
|
2390
|
-
tokens.push(token);
|
|
2391
|
-
continue;
|
|
2392
|
-
}
|
|
2393
|
-
// def
|
|
2394
|
-
if (token = this.tokenizer.def(src)) {
|
|
2395
|
-
src = src.substring(token.raw.length);
|
|
2396
|
-
lastToken = tokens[tokens.length - 1];
|
|
2397
|
-
if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) {
|
|
2398
|
-
lastToken.raw += '\n' + token.raw;
|
|
2399
|
-
lastToken.text += '\n' + token.raw;
|
|
2400
|
-
this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
|
|
2401
|
-
}
|
|
2402
|
-
else if (!this.tokens.links[token.tag]) {
|
|
2403
|
-
this.tokens.links[token.tag] = {
|
|
2404
|
-
href: token.href,
|
|
2405
|
-
title: token.title
|
|
2406
|
-
};
|
|
2407
|
-
}
|
|
2408
|
-
continue;
|
|
2409
|
-
}
|
|
2410
|
-
// table (gfm)
|
|
2411
|
-
if (token = this.tokenizer.table(src)) {
|
|
2412
|
-
src = src.substring(token.raw.length);
|
|
2413
|
-
tokens.push(token);
|
|
2414
|
-
continue;
|
|
2415
|
-
}
|
|
2416
|
-
// lheading
|
|
2417
|
-
if (token = this.tokenizer.lheading(src)) {
|
|
2418
|
-
src = src.substring(token.raw.length);
|
|
2419
|
-
tokens.push(token);
|
|
2420
|
-
continue;
|
|
2421
|
-
}
|
|
2422
|
-
// top-level paragraph
|
|
2423
|
-
// prevent paragraph consuming extensions by clipping 'src' to extension start
|
|
2424
|
-
cutSrc = src;
|
|
2425
|
-
if (this.options.extensions && this.options.extensions.startBlock) {
|
|
2426
|
-
let startIndex = Infinity;
|
|
2427
|
-
const tempSrc = src.slice(1);
|
|
2428
|
-
let tempStart;
|
|
2429
|
-
this.options.extensions.startBlock.forEach((getStartIndex) => {
|
|
2430
|
-
tempStart = getStartIndex.call({ lexer: this }, tempSrc);
|
|
2431
|
-
if (typeof tempStart === 'number' && tempStart >= 0) {
|
|
2432
|
-
startIndex = Math.min(startIndex, tempStart);
|
|
2433
|
-
}
|
|
2434
|
-
});
|
|
2435
|
-
if (startIndex < Infinity && startIndex >= 0) {
|
|
2436
|
-
cutSrc = src.substring(0, startIndex + 1);
|
|
2437
|
-
}
|
|
2438
|
-
}
|
|
2439
|
-
if (this.state.top && (token = this.tokenizer.paragraph(cutSrc))) {
|
|
2440
|
-
lastToken = tokens[tokens.length - 1];
|
|
2441
|
-
if (lastParagraphClipped && lastToken.type === 'paragraph') {
|
|
2442
|
-
lastToken.raw += '\n' + token.raw;
|
|
2443
|
-
lastToken.text += '\n' + token.text;
|
|
2444
|
-
this.inlineQueue.pop();
|
|
2445
|
-
this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
|
|
2446
|
-
}
|
|
2447
|
-
else {
|
|
2448
|
-
tokens.push(token);
|
|
2449
|
-
}
|
|
2450
|
-
lastParagraphClipped = (cutSrc.length !== src.length);
|
|
2451
|
-
src = src.substring(token.raw.length);
|
|
2452
|
-
continue;
|
|
2453
|
-
}
|
|
2454
|
-
// text
|
|
2455
|
-
if (token = this.tokenizer.text(src)) {
|
|
2456
|
-
src = src.substring(token.raw.length);
|
|
2457
|
-
lastToken = tokens[tokens.length - 1];
|
|
2458
|
-
if (lastToken && lastToken.type === 'text') {
|
|
2459
|
-
lastToken.raw += '\n' + token.raw;
|
|
2460
|
-
lastToken.text += '\n' + token.text;
|
|
2461
|
-
this.inlineQueue.pop();
|
|
2462
|
-
this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;
|
|
2463
|
-
}
|
|
2464
|
-
else {
|
|
2465
|
-
tokens.push(token);
|
|
2466
|
-
}
|
|
2467
|
-
continue;
|
|
2468
|
-
}
|
|
2469
|
-
if (src) {
|
|
2470
|
-
const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
|
|
2471
|
-
if (this.options.silent) {
|
|
2472
|
-
console.error(errMsg);
|
|
2473
|
-
break;
|
|
2474
|
-
}
|
|
2475
|
-
else {
|
|
2476
|
-
throw new Error(errMsg);
|
|
2477
|
-
}
|
|
2478
|
-
}
|
|
2479
|
-
}
|
|
2480
|
-
this.state.top = true;
|
|
2481
|
-
return tokens;
|
|
2482
|
-
}
|
|
2483
|
-
inline(src, tokens = []) {
|
|
2484
|
-
this.inlineQueue.push({ src, tokens });
|
|
2485
|
-
return tokens;
|
|
2486
|
-
}
|
|
2487
|
-
/**
|
|
2488
|
-
* Lexing/Compiling
|
|
2489
|
-
*/
|
|
2490
|
-
inlineTokens(src, tokens = []) {
|
|
2491
|
-
let token, lastToken, cutSrc;
|
|
2492
|
-
// String with links masked to avoid interference with em and strong
|
|
2493
|
-
let maskedSrc = src;
|
|
2494
|
-
let match;
|
|
2495
|
-
let keepPrevChar, prevChar;
|
|
2496
|
-
// Mask out reflinks
|
|
2497
|
-
if (this.tokens.links) {
|
|
2498
|
-
const links = Object.keys(this.tokens.links);
|
|
2499
|
-
if (links.length > 0) {
|
|
2500
|
-
while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {
|
|
2501
|
-
if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) {
|
|
2502
|
-
maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);
|
|
2503
|
-
}
|
|
2504
|
-
}
|
|
2505
|
-
}
|
|
2506
|
-
}
|
|
2507
|
-
// Mask out other blocks
|
|
2508
|
-
while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) {
|
|
2509
|
-
maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);
|
|
2510
|
-
}
|
|
2511
|
-
// Mask out escaped characters
|
|
2512
|
-
while ((match = this.tokenizer.rules.inline.anyPunctuation.exec(maskedSrc)) != null) {
|
|
2513
|
-
maskedSrc = maskedSrc.slice(0, match.index) + '++' + maskedSrc.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);
|
|
2514
|
-
}
|
|
2515
|
-
while (src) {
|
|
2516
|
-
if (!keepPrevChar) {
|
|
2517
|
-
prevChar = '';
|
|
2518
|
-
}
|
|
2519
|
-
keepPrevChar = false;
|
|
2520
|
-
// extensions
|
|
2521
|
-
if (this.options.extensions
|
|
2522
|
-
&& this.options.extensions.inline
|
|
2523
|
-
&& this.options.extensions.inline.some((extTokenizer) => {
|
|
2524
|
-
if (token = extTokenizer.call({ lexer: this }, src, tokens)) {
|
|
2525
|
-
src = src.substring(token.raw.length);
|
|
2526
|
-
tokens.push(token);
|
|
2527
|
-
return true;
|
|
2528
|
-
}
|
|
2529
|
-
return false;
|
|
2530
|
-
})) {
|
|
2531
|
-
continue;
|
|
2532
|
-
}
|
|
2533
|
-
// escape
|
|
2534
|
-
if (token = this.tokenizer.escape(src)) {
|
|
2535
|
-
src = src.substring(token.raw.length);
|
|
2536
|
-
tokens.push(token);
|
|
2537
|
-
continue;
|
|
2538
|
-
}
|
|
2539
|
-
// tag
|
|
2540
|
-
if (token = this.tokenizer.tag(src)) {
|
|
2541
|
-
src = src.substring(token.raw.length);
|
|
2542
|
-
lastToken = tokens[tokens.length - 1];
|
|
2543
|
-
if (lastToken && token.type === 'text' && lastToken.type === 'text') {
|
|
2544
|
-
lastToken.raw += token.raw;
|
|
2545
|
-
lastToken.text += token.text;
|
|
2546
|
-
}
|
|
2547
|
-
else {
|
|
2548
|
-
tokens.push(token);
|
|
2549
|
-
}
|
|
2550
|
-
continue;
|
|
2551
|
-
}
|
|
2552
|
-
// link
|
|
2553
|
-
if (token = this.tokenizer.link(src)) {
|
|
2554
|
-
src = src.substring(token.raw.length);
|
|
2555
|
-
tokens.push(token);
|
|
2556
|
-
continue;
|
|
2557
|
-
}
|
|
2558
|
-
// reflink, nolink
|
|
2559
|
-
if (token = this.tokenizer.reflink(src, this.tokens.links)) {
|
|
2560
|
-
src = src.substring(token.raw.length);
|
|
2561
|
-
lastToken = tokens[tokens.length - 1];
|
|
2562
|
-
if (lastToken && token.type === 'text' && lastToken.type === 'text') {
|
|
2563
|
-
lastToken.raw += token.raw;
|
|
2564
|
-
lastToken.text += token.text;
|
|
2565
|
-
}
|
|
2566
|
-
else {
|
|
2567
|
-
tokens.push(token);
|
|
2568
|
-
}
|
|
2569
|
-
continue;
|
|
2570
|
-
}
|
|
2571
|
-
// em & strong
|
|
2572
|
-
if (token = this.tokenizer.emStrong(src, maskedSrc, prevChar)) {
|
|
2573
|
-
src = src.substring(token.raw.length);
|
|
2574
|
-
tokens.push(token);
|
|
2575
|
-
continue;
|
|
2576
|
-
}
|
|
2577
|
-
// code
|
|
2578
|
-
if (token = this.tokenizer.codespan(src)) {
|
|
2579
|
-
src = src.substring(token.raw.length);
|
|
2580
|
-
tokens.push(token);
|
|
2581
|
-
continue;
|
|
2582
|
-
}
|
|
2583
|
-
// br
|
|
2584
|
-
if (token = this.tokenizer.br(src)) {
|
|
2585
|
-
src = src.substring(token.raw.length);
|
|
2586
|
-
tokens.push(token);
|
|
2587
|
-
continue;
|
|
2588
|
-
}
|
|
2589
|
-
// del (gfm)
|
|
2590
|
-
if (token = this.tokenizer.del(src)) {
|
|
2591
|
-
src = src.substring(token.raw.length);
|
|
2592
|
-
tokens.push(token);
|
|
2593
|
-
continue;
|
|
2594
|
-
}
|
|
2595
|
-
// autolink
|
|
2596
|
-
if (token = this.tokenizer.autolink(src)) {
|
|
2597
|
-
src = src.substring(token.raw.length);
|
|
2598
|
-
tokens.push(token);
|
|
2599
|
-
continue;
|
|
2600
|
-
}
|
|
2601
|
-
// url (gfm)
|
|
2602
|
-
if (!this.state.inLink && (token = this.tokenizer.url(src))) {
|
|
2603
|
-
src = src.substring(token.raw.length);
|
|
2604
|
-
tokens.push(token);
|
|
2605
|
-
continue;
|
|
2606
|
-
}
|
|
2607
|
-
// text
|
|
2608
|
-
// prevent inlineText consuming extensions by clipping 'src' to extension start
|
|
2609
|
-
cutSrc = src;
|
|
2610
|
-
if (this.options.extensions && this.options.extensions.startInline) {
|
|
2611
|
-
let startIndex = Infinity;
|
|
2612
|
-
const tempSrc = src.slice(1);
|
|
2613
|
-
let tempStart;
|
|
2614
|
-
this.options.extensions.startInline.forEach((getStartIndex) => {
|
|
2615
|
-
tempStart = getStartIndex.call({ lexer: this }, tempSrc);
|
|
2616
|
-
if (typeof tempStart === 'number' && tempStart >= 0) {
|
|
2617
|
-
startIndex = Math.min(startIndex, tempStart);
|
|
2618
|
-
}
|
|
2619
|
-
});
|
|
2620
|
-
if (startIndex < Infinity && startIndex >= 0) {
|
|
2621
|
-
cutSrc = src.substring(0, startIndex + 1);
|
|
2622
|
-
}
|
|
2623
|
-
}
|
|
2624
|
-
if (token = this.tokenizer.inlineText(cutSrc)) {
|
|
2625
|
-
src = src.substring(token.raw.length);
|
|
2626
|
-
if (token.raw.slice(-1) !== '_') { // Track prevChar before string of ____ started
|
|
2627
|
-
prevChar = token.raw.slice(-1);
|
|
2628
|
-
}
|
|
2629
|
-
keepPrevChar = true;
|
|
2630
|
-
lastToken = tokens[tokens.length - 1];
|
|
2631
|
-
if (lastToken && lastToken.type === 'text') {
|
|
2632
|
-
lastToken.raw += token.raw;
|
|
2633
|
-
lastToken.text += token.text;
|
|
2634
|
-
}
|
|
2635
|
-
else {
|
|
2636
|
-
tokens.push(token);
|
|
2637
|
-
}
|
|
2638
|
-
continue;
|
|
2639
|
-
}
|
|
2640
|
-
if (src) {
|
|
2641
|
-
const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
|
|
2642
|
-
if (this.options.silent) {
|
|
2643
|
-
console.error(errMsg);
|
|
2644
|
-
break;
|
|
2645
|
-
}
|
|
2646
|
-
else {
|
|
2647
|
-
throw new Error(errMsg);
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
return tokens;
|
|
2652
|
-
}
|
|
2653
|
-
}
|
|
2654
|
-
|
|
2655
|
-
/**
|
|
2656
|
-
* Renderer
|
|
2657
|
-
*/
|
|
2658
|
-
class _Renderer {
|
|
2659
|
-
options;
|
|
2660
|
-
constructor(options) {
|
|
2661
|
-
this.options = options || _defaults;
|
|
2662
|
-
}
|
|
2663
|
-
code(code, infostring, escaped) {
|
|
2664
|
-
const lang = (infostring || '').match(/^\S*/)?.[0];
|
|
2665
|
-
code = code.replace(/\n$/, '') + '\n';
|
|
2666
|
-
if (!lang) {
|
|
2667
|
-
return '<pre><code>'
|
|
2668
|
-
+ (escaped ? code : escape(code, true))
|
|
2669
|
-
+ '</code></pre>\n';
|
|
2670
|
-
}
|
|
2671
|
-
return '<pre><code class="language-'
|
|
2672
|
-
+ escape(lang)
|
|
2673
|
-
+ '">'
|
|
2674
|
-
+ (escaped ? code : escape(code, true))
|
|
2675
|
-
+ '</code></pre>\n';
|
|
2676
|
-
}
|
|
2677
|
-
blockquote(quote) {
|
|
2678
|
-
return `<blockquote>\n${quote}</blockquote>\n`;
|
|
2679
|
-
}
|
|
2680
|
-
html(html, block) {
|
|
2681
|
-
return html;
|
|
2682
|
-
}
|
|
2683
|
-
heading(text, level, raw) {
|
|
2684
|
-
// ignore IDs
|
|
2685
|
-
return `<h${level}>${text}</h${level}>\n`;
|
|
2686
|
-
}
|
|
2687
|
-
hr() {
|
|
2688
|
-
return '<hr>\n';
|
|
2689
|
-
}
|
|
2690
|
-
list(body, ordered, start) {
|
|
2691
|
-
const type = ordered ? 'ol' : 'ul';
|
|
2692
|
-
const startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
|
|
2693
|
-
return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
|
|
2694
|
-
}
|
|
2695
|
-
listitem(text, task, checked) {
|
|
2696
|
-
return `<li>${text}</li>\n`;
|
|
2697
|
-
}
|
|
2698
|
-
checkbox(checked) {
|
|
2699
|
-
return '<input '
|
|
2700
|
-
+ (checked ? 'checked="" ' : '')
|
|
2701
|
-
+ 'disabled="" type="checkbox">';
|
|
2702
|
-
}
|
|
2703
|
-
paragraph(text) {
|
|
2704
|
-
return `<p>${text}</p>\n`;
|
|
2705
|
-
}
|
|
2706
|
-
table(header, body) {
|
|
2707
|
-
if (body)
|
|
2708
|
-
body = `<tbody>${body}</tbody>`;
|
|
2709
|
-
return '<table>\n'
|
|
2710
|
-
+ '<thead>\n'
|
|
2711
|
-
+ header
|
|
2712
|
-
+ '</thead>\n'
|
|
2713
|
-
+ body
|
|
2714
|
-
+ '</table>\n';
|
|
2715
|
-
}
|
|
2716
|
-
tablerow(content) {
|
|
2717
|
-
return `<tr>\n${content}</tr>\n`;
|
|
2718
|
-
}
|
|
2719
|
-
tablecell(content, flags) {
|
|
2720
|
-
const type = flags.header ? 'th' : 'td';
|
|
2721
|
-
const tag = flags.align
|
|
2722
|
-
? `<${type} align="${flags.align}">`
|
|
2723
|
-
: `<${type}>`;
|
|
2724
|
-
return tag + content + `</${type}>\n`;
|
|
2725
|
-
}
|
|
2726
|
-
/**
|
|
2727
|
-
* span level renderer
|
|
2728
|
-
*/
|
|
2729
|
-
strong(text) {
|
|
2730
|
-
return `<strong>${text}</strong>`;
|
|
2731
|
-
}
|
|
2732
|
-
em(text) {
|
|
2733
|
-
return `<em>${text}</em>`;
|
|
2734
|
-
}
|
|
2735
|
-
codespan(text) {
|
|
2736
|
-
return `<code>${text}</code>`;
|
|
2737
|
-
}
|
|
2738
|
-
br() {
|
|
2739
|
-
return '<br>';
|
|
2740
|
-
}
|
|
2741
|
-
del(text) {
|
|
2742
|
-
return `<del>${text}</del>`;
|
|
2743
|
-
}
|
|
2744
|
-
link(href, title, text) {
|
|
2745
|
-
const cleanHref = cleanUrl(href);
|
|
2746
|
-
if (cleanHref === null) {
|
|
2747
|
-
return text;
|
|
2748
|
-
}
|
|
2749
|
-
href = cleanHref;
|
|
2750
|
-
let out = '<a href="' + href + '"';
|
|
2751
|
-
if (title) {
|
|
2752
|
-
out += ' title="' + title + '"';
|
|
2753
|
-
}
|
|
2754
|
-
out += '>' + text + '</a>';
|
|
2755
|
-
return out;
|
|
2756
|
-
}
|
|
2757
|
-
image(href, title, text) {
|
|
2758
|
-
const cleanHref = cleanUrl(href);
|
|
2759
|
-
if (cleanHref === null) {
|
|
2760
|
-
return text;
|
|
2761
|
-
}
|
|
2762
|
-
href = cleanHref;
|
|
2763
|
-
let out = `<img src="${href}" alt="${text}"`;
|
|
2764
|
-
if (title) {
|
|
2765
|
-
out += ` title="${title}"`;
|
|
2766
|
-
}
|
|
2767
|
-
out += '>';
|
|
2768
|
-
return out;
|
|
2769
|
-
}
|
|
2770
|
-
text(text) {
|
|
2771
|
-
return text;
|
|
2772
|
-
}
|
|
2773
|
-
}
|
|
2774
|
-
|
|
2775
|
-
/**
|
|
2776
|
-
* TextRenderer
|
|
2777
|
-
* returns only the textual part of the token
|
|
2778
|
-
*/
|
|
2779
|
-
class _TextRenderer {
|
|
2780
|
-
// no need for block level renderers
|
|
2781
|
-
strong(text) {
|
|
2782
|
-
return text;
|
|
2783
|
-
}
|
|
2784
|
-
em(text) {
|
|
2785
|
-
return text;
|
|
2786
|
-
}
|
|
2787
|
-
codespan(text) {
|
|
2788
|
-
return text;
|
|
2789
|
-
}
|
|
2790
|
-
del(text) {
|
|
2791
|
-
return text;
|
|
2792
|
-
}
|
|
2793
|
-
html(text) {
|
|
2794
|
-
return text;
|
|
2795
|
-
}
|
|
2796
|
-
text(text) {
|
|
2797
|
-
return text;
|
|
2798
|
-
}
|
|
2799
|
-
link(href, title, text) {
|
|
2800
|
-
return '' + text;
|
|
2801
|
-
}
|
|
2802
|
-
image(href, title, text) {
|
|
2803
|
-
return '' + text;
|
|
2804
|
-
}
|
|
2805
|
-
br() {
|
|
2806
|
-
return '';
|
|
2807
|
-
}
|
|
2808
|
-
}
|
|
2809
|
-
|
|
2810
|
-
/**
|
|
2811
|
-
* Parsing & Compiling
|
|
2812
|
-
*/
|
|
2813
|
-
class _Parser {
|
|
2814
|
-
options;
|
|
2815
|
-
renderer;
|
|
2816
|
-
textRenderer;
|
|
2817
|
-
constructor(options) {
|
|
2818
|
-
this.options = options || _defaults;
|
|
2819
|
-
this.options.renderer = this.options.renderer || new _Renderer();
|
|
2820
|
-
this.renderer = this.options.renderer;
|
|
2821
|
-
this.renderer.options = this.options;
|
|
2822
|
-
this.textRenderer = new _TextRenderer();
|
|
2823
|
-
}
|
|
2824
|
-
/**
|
|
2825
|
-
* Static Parse Method
|
|
2826
|
-
*/
|
|
2827
|
-
static parse(tokens, options) {
|
|
2828
|
-
const parser = new _Parser(options);
|
|
2829
|
-
return parser.parse(tokens);
|
|
2830
|
-
}
|
|
2831
|
-
/**
|
|
2832
|
-
* Static Parse Inline Method
|
|
2833
|
-
*/
|
|
2834
|
-
static parseInline(tokens, options) {
|
|
2835
|
-
const parser = new _Parser(options);
|
|
2836
|
-
return parser.parseInline(tokens);
|
|
2837
|
-
}
|
|
2838
|
-
/**
|
|
2839
|
-
* Parse Loop
|
|
2840
|
-
*/
|
|
2841
|
-
parse(tokens, top = true) {
|
|
2842
|
-
let out = '';
|
|
2843
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
2844
|
-
const token = tokens[i];
|
|
2845
|
-
// Run any renderer extensions
|
|
2846
|
-
if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) {
|
|
2847
|
-
const genericToken = token;
|
|
2848
|
-
const ret = this.options.extensions.renderers[genericToken.type].call({ parser: this }, genericToken);
|
|
2849
|
-
if (ret !== false || !['space', 'hr', 'heading', 'code', 'table', 'blockquote', 'list', 'html', 'paragraph', 'text'].includes(genericToken.type)) {
|
|
2850
|
-
out += ret || '';
|
|
2851
|
-
continue;
|
|
2852
|
-
}
|
|
2853
|
-
}
|
|
2854
|
-
switch (token.type) {
|
|
2855
|
-
case 'space': {
|
|
2856
|
-
continue;
|
|
2857
|
-
}
|
|
2858
|
-
case 'hr': {
|
|
2859
|
-
out += this.renderer.hr();
|
|
2860
|
-
continue;
|
|
2861
|
-
}
|
|
2862
|
-
case 'heading': {
|
|
2863
|
-
const headingToken = token;
|
|
2864
|
-
out += this.renderer.heading(this.parseInline(headingToken.tokens), headingToken.depth, unescape(this.parseInline(headingToken.tokens, this.textRenderer)));
|
|
2865
|
-
continue;
|
|
2866
|
-
}
|
|
2867
|
-
case 'code': {
|
|
2868
|
-
const codeToken = token;
|
|
2869
|
-
out += this.renderer.code(codeToken.text, codeToken.lang, !!codeToken.escaped);
|
|
2870
|
-
continue;
|
|
2871
|
-
}
|
|
2872
|
-
case 'table': {
|
|
2873
|
-
const tableToken = token;
|
|
2874
|
-
let header = '';
|
|
2875
|
-
// header
|
|
2876
|
-
let cell = '';
|
|
2877
|
-
for (let j = 0; j < tableToken.header.length; j++) {
|
|
2878
|
-
cell += this.renderer.tablecell(this.parseInline(tableToken.header[j].tokens), { header: true, align: tableToken.align[j] });
|
|
2879
|
-
}
|
|
2880
|
-
header += this.renderer.tablerow(cell);
|
|
2881
|
-
let body = '';
|
|
2882
|
-
for (let j = 0; j < tableToken.rows.length; j++) {
|
|
2883
|
-
const row = tableToken.rows[j];
|
|
2884
|
-
cell = '';
|
|
2885
|
-
for (let k = 0; k < row.length; k++) {
|
|
2886
|
-
cell += this.renderer.tablecell(this.parseInline(row[k].tokens), { header: false, align: tableToken.align[k] });
|
|
2887
|
-
}
|
|
2888
|
-
body += this.renderer.tablerow(cell);
|
|
2889
|
-
}
|
|
2890
|
-
out += this.renderer.table(header, body);
|
|
2891
|
-
continue;
|
|
2892
|
-
}
|
|
2893
|
-
case 'blockquote': {
|
|
2894
|
-
const blockquoteToken = token;
|
|
2895
|
-
const body = this.parse(blockquoteToken.tokens);
|
|
2896
|
-
out += this.renderer.blockquote(body);
|
|
2897
|
-
continue;
|
|
2898
|
-
}
|
|
2899
|
-
case 'list': {
|
|
2900
|
-
const listToken = token;
|
|
2901
|
-
const ordered = listToken.ordered;
|
|
2902
|
-
const start = listToken.start;
|
|
2903
|
-
const loose = listToken.loose;
|
|
2904
|
-
let body = '';
|
|
2905
|
-
for (let j = 0; j < listToken.items.length; j++) {
|
|
2906
|
-
const item = listToken.items[j];
|
|
2907
|
-
const checked = item.checked;
|
|
2908
|
-
const task = item.task;
|
|
2909
|
-
let itemBody = '';
|
|
2910
|
-
if (item.task) {
|
|
2911
|
-
const checkbox = this.renderer.checkbox(!!checked);
|
|
2912
|
-
if (loose) {
|
|
2913
|
-
if (item.tokens.length > 0 && item.tokens[0].type === 'paragraph') {
|
|
2914
|
-
item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
|
|
2915
|
-
if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
|
|
2916
|
-
item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
|
|
2917
|
-
}
|
|
2918
|
-
}
|
|
2919
|
-
else {
|
|
2920
|
-
item.tokens.unshift({
|
|
2921
|
-
type: 'text',
|
|
2922
|
-
text: checkbox + ' '
|
|
2923
|
-
});
|
|
2924
|
-
}
|
|
2925
|
-
}
|
|
2926
|
-
else {
|
|
2927
|
-
itemBody += checkbox + ' ';
|
|
2928
|
-
}
|
|
2929
|
-
}
|
|
2930
|
-
itemBody += this.parse(item.tokens, loose);
|
|
2931
|
-
body += this.renderer.listitem(itemBody, task, !!checked);
|
|
2932
|
-
}
|
|
2933
|
-
out += this.renderer.list(body, ordered, start);
|
|
2934
|
-
continue;
|
|
2935
|
-
}
|
|
2936
|
-
case 'html': {
|
|
2937
|
-
const htmlToken = token;
|
|
2938
|
-
out += this.renderer.html(htmlToken.text, htmlToken.block);
|
|
2939
|
-
continue;
|
|
2940
|
-
}
|
|
2941
|
-
case 'paragraph': {
|
|
2942
|
-
const paragraphToken = token;
|
|
2943
|
-
out += this.renderer.paragraph(this.parseInline(paragraphToken.tokens));
|
|
2944
|
-
continue;
|
|
2945
|
-
}
|
|
2946
|
-
case 'text': {
|
|
2947
|
-
let textToken = token;
|
|
2948
|
-
let body = textToken.tokens ? this.parseInline(textToken.tokens) : textToken.text;
|
|
2949
|
-
while (i + 1 < tokens.length && tokens[i + 1].type === 'text') {
|
|
2950
|
-
textToken = tokens[++i];
|
|
2951
|
-
body += '\n' + (textToken.tokens ? this.parseInline(textToken.tokens) : textToken.text);
|
|
2952
|
-
}
|
|
2953
|
-
out += top ? this.renderer.paragraph(body) : body;
|
|
2954
|
-
continue;
|
|
2955
|
-
}
|
|
2956
|
-
default: {
|
|
2957
|
-
const errMsg = 'Token with "' + token.type + '" type was not found.';
|
|
2958
|
-
if (this.options.silent) {
|
|
2959
|
-
console.error(errMsg);
|
|
2960
|
-
return '';
|
|
2961
|
-
}
|
|
2962
|
-
else {
|
|
2963
|
-
throw new Error(errMsg);
|
|
2964
|
-
}
|
|
2965
|
-
}
|
|
2966
|
-
}
|
|
2967
|
-
}
|
|
2968
|
-
return out;
|
|
2969
|
-
}
|
|
2970
|
-
/**
|
|
2971
|
-
* Parse Inline Tokens
|
|
2972
|
-
*/
|
|
2973
|
-
parseInline(tokens, renderer) {
|
|
2974
|
-
renderer = renderer || this.renderer;
|
|
2975
|
-
let out = '';
|
|
2976
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
2977
|
-
const token = tokens[i];
|
|
2978
|
-
// Run any renderer extensions
|
|
2979
|
-
if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) {
|
|
2980
|
-
const ret = this.options.extensions.renderers[token.type].call({ parser: this }, token);
|
|
2981
|
-
if (ret !== false || !['escape', 'html', 'link', 'image', 'strong', 'em', 'codespan', 'br', 'del', 'text'].includes(token.type)) {
|
|
2982
|
-
out += ret || '';
|
|
2983
|
-
continue;
|
|
2984
|
-
}
|
|
2985
|
-
}
|
|
2986
|
-
switch (token.type) {
|
|
2987
|
-
case 'escape': {
|
|
2988
|
-
const escapeToken = token;
|
|
2989
|
-
out += renderer.text(escapeToken.text);
|
|
2990
|
-
break;
|
|
2991
|
-
}
|
|
2992
|
-
case 'html': {
|
|
2993
|
-
const tagToken = token;
|
|
2994
|
-
out += renderer.html(tagToken.text);
|
|
2995
|
-
break;
|
|
2996
|
-
}
|
|
2997
|
-
case 'link': {
|
|
2998
|
-
const linkToken = token;
|
|
2999
|
-
out += renderer.link(linkToken.href, linkToken.title, this.parseInline(linkToken.tokens, renderer));
|
|
3000
|
-
break;
|
|
3001
|
-
}
|
|
3002
|
-
case 'image': {
|
|
3003
|
-
const imageToken = token;
|
|
3004
|
-
out += renderer.image(imageToken.href, imageToken.title, imageToken.text);
|
|
3005
|
-
break;
|
|
3006
|
-
}
|
|
3007
|
-
case 'strong': {
|
|
3008
|
-
const strongToken = token;
|
|
3009
|
-
out += renderer.strong(this.parseInline(strongToken.tokens, renderer));
|
|
3010
|
-
break;
|
|
3011
|
-
}
|
|
3012
|
-
case 'em': {
|
|
3013
|
-
const emToken = token;
|
|
3014
|
-
out += renderer.em(this.parseInline(emToken.tokens, renderer));
|
|
3015
|
-
break;
|
|
3016
|
-
}
|
|
3017
|
-
case 'codespan': {
|
|
3018
|
-
const codespanToken = token;
|
|
3019
|
-
out += renderer.codespan(codespanToken.text);
|
|
3020
|
-
break;
|
|
3021
|
-
}
|
|
3022
|
-
case 'br': {
|
|
3023
|
-
out += renderer.br();
|
|
3024
|
-
break;
|
|
3025
|
-
}
|
|
3026
|
-
case 'del': {
|
|
3027
|
-
const delToken = token;
|
|
3028
|
-
out += renderer.del(this.parseInline(delToken.tokens, renderer));
|
|
3029
|
-
break;
|
|
3030
|
-
}
|
|
3031
|
-
case 'text': {
|
|
3032
|
-
const textToken = token;
|
|
3033
|
-
out += renderer.text(textToken.text);
|
|
3034
|
-
break;
|
|
3035
|
-
}
|
|
3036
|
-
default: {
|
|
3037
|
-
const errMsg = 'Token with "' + token.type + '" type was not found.';
|
|
3038
|
-
if (this.options.silent) {
|
|
3039
|
-
console.error(errMsg);
|
|
3040
|
-
return '';
|
|
3041
|
-
}
|
|
3042
|
-
else {
|
|
3043
|
-
throw new Error(errMsg);
|
|
3044
|
-
}
|
|
3045
|
-
}
|
|
3046
|
-
}
|
|
3047
|
-
}
|
|
3048
|
-
return out;
|
|
3049
|
-
}
|
|
3050
|
-
}
|
|
3051
|
-
|
|
3052
|
-
class _Hooks {
|
|
3053
|
-
options;
|
|
3054
|
-
constructor(options) {
|
|
3055
|
-
this.options = options || _defaults;
|
|
3056
|
-
}
|
|
3057
|
-
static passThroughHooks = new Set([
|
|
3058
|
-
'preprocess',
|
|
3059
|
-
'postprocess'
|
|
3060
|
-
]);
|
|
3061
|
-
/**
|
|
3062
|
-
* Process markdown before marked
|
|
3063
|
-
*/
|
|
3064
|
-
preprocess(markdown) {
|
|
3065
|
-
return markdown;
|
|
3066
|
-
}
|
|
3067
|
-
/**
|
|
3068
|
-
* Process HTML after marked is finished
|
|
3069
|
-
*/
|
|
3070
|
-
postprocess(html) {
|
|
3071
|
-
return html;
|
|
3072
|
-
}
|
|
3073
|
-
}
|
|
3074
|
-
|
|
3075
|
-
class Marked {
|
|
3076
|
-
defaults = _getDefaults();
|
|
3077
|
-
options = this.setOptions;
|
|
3078
|
-
parse = this.#parseMarkdown(_Lexer.lex, _Parser.parse);
|
|
3079
|
-
parseInline = this.#parseMarkdown(_Lexer.lexInline, _Parser.parseInline);
|
|
3080
|
-
Parser = _Parser;
|
|
3081
|
-
Renderer = _Renderer;
|
|
3082
|
-
TextRenderer = _TextRenderer;
|
|
3083
|
-
Lexer = _Lexer;
|
|
3084
|
-
Tokenizer = _Tokenizer;
|
|
3085
|
-
Hooks = _Hooks;
|
|
3086
|
-
constructor(...args) {
|
|
3087
|
-
this.use(...args);
|
|
3088
|
-
}
|
|
3089
|
-
/**
|
|
3090
|
-
* Run callback for every token
|
|
3091
|
-
*/
|
|
3092
|
-
walkTokens(tokens, callback) {
|
|
3093
|
-
let values = [];
|
|
3094
|
-
for (const token of tokens) {
|
|
3095
|
-
values = values.concat(callback.call(this, token));
|
|
3096
|
-
switch (token.type) {
|
|
3097
|
-
case 'table': {
|
|
3098
|
-
const tableToken = token;
|
|
3099
|
-
for (const cell of tableToken.header) {
|
|
3100
|
-
values = values.concat(this.walkTokens(cell.tokens, callback));
|
|
3101
|
-
}
|
|
3102
|
-
for (const row of tableToken.rows) {
|
|
3103
|
-
for (const cell of row) {
|
|
3104
|
-
values = values.concat(this.walkTokens(cell.tokens, callback));
|
|
3105
|
-
}
|
|
3106
|
-
}
|
|
3107
|
-
break;
|
|
3108
|
-
}
|
|
3109
|
-
case 'list': {
|
|
3110
|
-
const listToken = token;
|
|
3111
|
-
values = values.concat(this.walkTokens(listToken.items, callback));
|
|
3112
|
-
break;
|
|
3113
|
-
}
|
|
3114
|
-
default: {
|
|
3115
|
-
const genericToken = token;
|
|
3116
|
-
if (this.defaults.extensions?.childTokens?.[genericToken.type]) {
|
|
3117
|
-
this.defaults.extensions.childTokens[genericToken.type].forEach((childTokens) => {
|
|
3118
|
-
values = values.concat(this.walkTokens(genericToken[childTokens], callback));
|
|
3119
|
-
});
|
|
3120
|
-
}
|
|
3121
|
-
else if (genericToken.tokens) {
|
|
3122
|
-
values = values.concat(this.walkTokens(genericToken.tokens, callback));
|
|
3123
|
-
}
|
|
3124
|
-
}
|
|
3125
|
-
}
|
|
3126
|
-
}
|
|
3127
|
-
return values;
|
|
3128
|
-
}
|
|
3129
|
-
use(...args) {
|
|
3130
|
-
const extensions = this.defaults.extensions || { renderers: {}, childTokens: {} };
|
|
3131
|
-
args.forEach((pack) => {
|
|
3132
|
-
// copy options to new object
|
|
3133
|
-
const opts = { ...pack };
|
|
3134
|
-
// set async to true if it was set to true before
|
|
3135
|
-
opts.async = this.defaults.async || opts.async || false;
|
|
3136
|
-
// ==-- Parse "addon" extensions --== //
|
|
3137
|
-
if (pack.extensions) {
|
|
3138
|
-
pack.extensions.forEach((ext) => {
|
|
3139
|
-
if (!ext.name) {
|
|
3140
|
-
throw new Error('extension name required');
|
|
3141
|
-
}
|
|
3142
|
-
if ('renderer' in ext) { // Renderer extensions
|
|
3143
|
-
const prevRenderer = extensions.renderers[ext.name];
|
|
3144
|
-
if (prevRenderer) {
|
|
3145
|
-
// Replace extension with func to run new extension but fall back if false
|
|
3146
|
-
extensions.renderers[ext.name] = function (...args) {
|
|
3147
|
-
let ret = ext.renderer.apply(this, args);
|
|
3148
|
-
if (ret === false) {
|
|
3149
|
-
ret = prevRenderer.apply(this, args);
|
|
3150
|
-
}
|
|
3151
|
-
return ret;
|
|
3152
|
-
};
|
|
3153
|
-
}
|
|
3154
|
-
else {
|
|
3155
|
-
extensions.renderers[ext.name] = ext.renderer;
|
|
3156
|
-
}
|
|
3157
|
-
}
|
|
3158
|
-
if ('tokenizer' in ext) { // Tokenizer Extensions
|
|
3159
|
-
if (!ext.level || (ext.level !== 'block' && ext.level !== 'inline')) {
|
|
3160
|
-
throw new Error("extension level must be 'block' or 'inline'");
|
|
3161
|
-
}
|
|
3162
|
-
const extLevel = extensions[ext.level];
|
|
3163
|
-
if (extLevel) {
|
|
3164
|
-
extLevel.unshift(ext.tokenizer);
|
|
3165
|
-
}
|
|
3166
|
-
else {
|
|
3167
|
-
extensions[ext.level] = [ext.tokenizer];
|
|
3168
|
-
}
|
|
3169
|
-
if (ext.start) { // Function to check for start of token
|
|
3170
|
-
if (ext.level === 'block') {
|
|
3171
|
-
if (extensions.startBlock) {
|
|
3172
|
-
extensions.startBlock.push(ext.start);
|
|
3173
|
-
}
|
|
3174
|
-
else {
|
|
3175
|
-
extensions.startBlock = [ext.start];
|
|
3176
|
-
}
|
|
3177
|
-
}
|
|
3178
|
-
else if (ext.level === 'inline') {
|
|
3179
|
-
if (extensions.startInline) {
|
|
3180
|
-
extensions.startInline.push(ext.start);
|
|
3181
|
-
}
|
|
3182
|
-
else {
|
|
3183
|
-
extensions.startInline = [ext.start];
|
|
3184
|
-
}
|
|
3185
|
-
}
|
|
3186
|
-
}
|
|
3187
|
-
}
|
|
3188
|
-
if ('childTokens' in ext && ext.childTokens) { // Child tokens to be visited by walkTokens
|
|
3189
|
-
extensions.childTokens[ext.name] = ext.childTokens;
|
|
3190
|
-
}
|
|
3191
|
-
});
|
|
3192
|
-
opts.extensions = extensions;
|
|
3193
|
-
}
|
|
3194
|
-
// ==-- Parse "overwrite" extensions --== //
|
|
3195
|
-
if (pack.renderer) {
|
|
3196
|
-
const renderer = this.defaults.renderer || new _Renderer(this.defaults);
|
|
3197
|
-
for (const prop in pack.renderer) {
|
|
3198
|
-
const rendererFunc = pack.renderer[prop];
|
|
3199
|
-
const rendererKey = prop;
|
|
3200
|
-
const prevRenderer = renderer[rendererKey];
|
|
3201
|
-
// Replace renderer with func to run extension, but fall back if false
|
|
3202
|
-
renderer[rendererKey] = (...args) => {
|
|
3203
|
-
let ret = rendererFunc.apply(renderer, args);
|
|
3204
|
-
if (ret === false) {
|
|
3205
|
-
ret = prevRenderer.apply(renderer, args);
|
|
3206
|
-
}
|
|
3207
|
-
return ret || '';
|
|
3208
|
-
};
|
|
3209
|
-
}
|
|
3210
|
-
opts.renderer = renderer;
|
|
3211
|
-
}
|
|
3212
|
-
if (pack.tokenizer) {
|
|
3213
|
-
const tokenizer = this.defaults.tokenizer || new _Tokenizer(this.defaults);
|
|
3214
|
-
for (const prop in pack.tokenizer) {
|
|
3215
|
-
const tokenizerFunc = pack.tokenizer[prop];
|
|
3216
|
-
const tokenizerKey = prop;
|
|
3217
|
-
const prevTokenizer = tokenizer[tokenizerKey];
|
|
3218
|
-
// Replace tokenizer with func to run extension, but fall back if false
|
|
3219
|
-
tokenizer[tokenizerKey] = (...args) => {
|
|
3220
|
-
let ret = tokenizerFunc.apply(tokenizer, args);
|
|
3221
|
-
if (ret === false) {
|
|
3222
|
-
ret = prevTokenizer.apply(tokenizer, args);
|
|
3223
|
-
}
|
|
3224
|
-
return ret;
|
|
3225
|
-
};
|
|
3226
|
-
}
|
|
3227
|
-
opts.tokenizer = tokenizer;
|
|
3228
|
-
}
|
|
3229
|
-
// ==-- Parse Hooks extensions --== //
|
|
3230
|
-
if (pack.hooks) {
|
|
3231
|
-
const hooks = this.defaults.hooks || new _Hooks();
|
|
3232
|
-
for (const prop in pack.hooks) {
|
|
3233
|
-
const hooksFunc = pack.hooks[prop];
|
|
3234
|
-
const hooksKey = prop;
|
|
3235
|
-
const prevHook = hooks[hooksKey];
|
|
3236
|
-
if (_Hooks.passThroughHooks.has(prop)) {
|
|
3237
|
-
hooks[hooksKey] = (arg) => {
|
|
3238
|
-
if (this.defaults.async) {
|
|
3239
|
-
return Promise.resolve(hooksFunc.call(hooks, arg)).then(ret => {
|
|
3240
|
-
return prevHook.call(hooks, ret);
|
|
3241
|
-
});
|
|
3242
|
-
}
|
|
3243
|
-
const ret = hooksFunc.call(hooks, arg);
|
|
3244
|
-
return prevHook.call(hooks, ret);
|
|
3245
|
-
};
|
|
3246
|
-
}
|
|
3247
|
-
else {
|
|
3248
|
-
hooks[hooksKey] = (...args) => {
|
|
3249
|
-
let ret = hooksFunc.apply(hooks, args);
|
|
3250
|
-
if (ret === false) {
|
|
3251
|
-
ret = prevHook.apply(hooks, args);
|
|
3252
|
-
}
|
|
3253
|
-
return ret;
|
|
3254
|
-
};
|
|
3255
|
-
}
|
|
3256
|
-
}
|
|
3257
|
-
opts.hooks = hooks;
|
|
3258
|
-
}
|
|
3259
|
-
// ==-- Parse WalkTokens extensions --== //
|
|
3260
|
-
if (pack.walkTokens) {
|
|
3261
|
-
const walkTokens = this.defaults.walkTokens;
|
|
3262
|
-
const packWalktokens = pack.walkTokens;
|
|
3263
|
-
opts.walkTokens = function (token) {
|
|
3264
|
-
let values = [];
|
|
3265
|
-
values.push(packWalktokens.call(this, token));
|
|
3266
|
-
if (walkTokens) {
|
|
3267
|
-
values = values.concat(walkTokens.call(this, token));
|
|
3268
|
-
}
|
|
3269
|
-
return values;
|
|
3270
|
-
};
|
|
3271
|
-
}
|
|
3272
|
-
this.defaults = { ...this.defaults, ...opts };
|
|
3273
|
-
});
|
|
3274
|
-
return this;
|
|
3275
|
-
}
|
|
3276
|
-
setOptions(opt) {
|
|
3277
|
-
this.defaults = { ...this.defaults, ...opt };
|
|
3278
|
-
return this;
|
|
3279
|
-
}
|
|
3280
|
-
lexer(src, options) {
|
|
3281
|
-
return _Lexer.lex(src, options ?? this.defaults);
|
|
3282
|
-
}
|
|
3283
|
-
parser(tokens, options) {
|
|
3284
|
-
return _Parser.parse(tokens, options ?? this.defaults);
|
|
3285
|
-
}
|
|
3286
|
-
#parseMarkdown(lexer, parser) {
|
|
3287
|
-
return (src, options) => {
|
|
3288
|
-
const origOpt = { ...options };
|
|
3289
|
-
const opt = { ...this.defaults, ...origOpt };
|
|
3290
|
-
// Show warning if an extension set async to true but the parse was called with async: false
|
|
3291
|
-
if (this.defaults.async === true && origOpt.async === false) {
|
|
3292
|
-
if (!opt.silent) {
|
|
3293
|
-
console.warn('marked(): The async option was set to true by an extension. The async: false option sent to parse will be ignored.');
|
|
3294
|
-
}
|
|
3295
|
-
opt.async = true;
|
|
3296
|
-
}
|
|
3297
|
-
const throwError = this.#onError(!!opt.silent, !!opt.async);
|
|
3298
|
-
// throw error in case of non string input
|
|
3299
|
-
if (typeof src === 'undefined' || src === null) {
|
|
3300
|
-
return throwError(new Error('marked(): input parameter is undefined or null'));
|
|
3301
|
-
}
|
|
3302
|
-
if (typeof src !== 'string') {
|
|
3303
|
-
return throwError(new Error('marked(): input parameter is of type '
|
|
3304
|
-
+ Object.prototype.toString.call(src) + ', string expected'));
|
|
3305
|
-
}
|
|
3306
|
-
if (opt.hooks) {
|
|
3307
|
-
opt.hooks.options = opt;
|
|
3308
|
-
}
|
|
3309
|
-
if (opt.async) {
|
|
3310
|
-
return Promise.resolve(opt.hooks ? opt.hooks.preprocess(src) : src)
|
|
3311
|
-
.then(src => lexer(src, opt))
|
|
3312
|
-
.then(tokens => opt.walkTokens ? Promise.all(this.walkTokens(tokens, opt.walkTokens)).then(() => tokens) : tokens)
|
|
3313
|
-
.then(tokens => parser(tokens, opt))
|
|
3314
|
-
.then(html => opt.hooks ? opt.hooks.postprocess(html) : html)
|
|
3315
|
-
.catch(throwError);
|
|
3316
|
-
}
|
|
3317
|
-
try {
|
|
3318
|
-
if (opt.hooks) {
|
|
3319
|
-
src = opt.hooks.preprocess(src);
|
|
3320
|
-
}
|
|
3321
|
-
const tokens = lexer(src, opt);
|
|
3322
|
-
if (opt.walkTokens) {
|
|
3323
|
-
this.walkTokens(tokens, opt.walkTokens);
|
|
3324
|
-
}
|
|
3325
|
-
let html = parser(tokens, opt);
|
|
3326
|
-
if (opt.hooks) {
|
|
3327
|
-
html = opt.hooks.postprocess(html);
|
|
3328
|
-
}
|
|
3329
|
-
return html;
|
|
3330
|
-
}
|
|
3331
|
-
catch (e) {
|
|
3332
|
-
return throwError(e);
|
|
3333
|
-
}
|
|
3334
|
-
};
|
|
3335
|
-
}
|
|
3336
|
-
#onError(silent, async) {
|
|
3337
|
-
return (e) => {
|
|
3338
|
-
e.message += '\nPlease report this to https://github.com/markedjs/marked.';
|
|
3339
|
-
if (silent) {
|
|
3340
|
-
const msg = '<p>An error occurred:</p><pre>'
|
|
3341
|
-
+ escape(e.message + '', true)
|
|
3342
|
-
+ '</pre>';
|
|
3343
|
-
if (async) {
|
|
3344
|
-
return Promise.resolve(msg);
|
|
3345
|
-
}
|
|
3346
|
-
return msg;
|
|
3347
|
-
}
|
|
3348
|
-
if (async) {
|
|
3349
|
-
return Promise.reject(e);
|
|
3350
|
-
}
|
|
3351
|
-
throw e;
|
|
3352
|
-
};
|
|
3353
|
-
}
|
|
3354
|
-
}
|
|
3355
|
-
|
|
3356
|
-
const markedInstance = new Marked();
|
|
3357
|
-
function marked(src, opt) {
|
|
3358
|
-
return markedInstance.parse(src, opt);
|
|
3359
|
-
}
|
|
3360
|
-
/**
|
|
3361
|
-
* Sets the default options.
|
|
3362
|
-
*
|
|
3363
|
-
* @param options Hash of options
|
|
3364
|
-
*/
|
|
3365
|
-
marked.options =
|
|
3366
|
-
marked.setOptions = function (options) {
|
|
3367
|
-
markedInstance.setOptions(options);
|
|
3368
|
-
marked.defaults = markedInstance.defaults;
|
|
3369
|
-
changeDefaults(marked.defaults);
|
|
3370
|
-
return marked;
|
|
3371
|
-
};
|
|
3372
|
-
/**
|
|
3373
|
-
* Gets the original marked default options.
|
|
3374
|
-
*/
|
|
3375
|
-
marked.getDefaults = _getDefaults;
|
|
3376
|
-
marked.defaults = _defaults;
|
|
3377
|
-
/**
|
|
3378
|
-
* Use Extension
|
|
3379
|
-
*/
|
|
3380
|
-
marked.use = function (...args) {
|
|
3381
|
-
markedInstance.use(...args);
|
|
3382
|
-
marked.defaults = markedInstance.defaults;
|
|
3383
|
-
changeDefaults(marked.defaults);
|
|
3384
|
-
return marked;
|
|
3385
|
-
};
|
|
3386
|
-
/**
|
|
3387
|
-
* Run callback for every token
|
|
3388
|
-
*/
|
|
3389
|
-
marked.walkTokens = function (tokens, callback) {
|
|
3390
|
-
return markedInstance.walkTokens(tokens, callback);
|
|
3391
|
-
};
|
|
3392
|
-
/**
|
|
3393
|
-
* Compiles markdown to HTML without enclosing `p` tag.
|
|
3394
|
-
*
|
|
3395
|
-
* @param src String of markdown source to be compiled
|
|
3396
|
-
* @param options Hash of options
|
|
3397
|
-
* @return String of compiled HTML
|
|
3398
|
-
*/
|
|
3399
|
-
marked.parseInline = markedInstance.parseInline;
|
|
3400
|
-
/**
|
|
3401
|
-
* Expose
|
|
3402
|
-
*/
|
|
3403
|
-
marked.Parser = _Parser;
|
|
3404
|
-
marked.parser = _Parser.parse;
|
|
3405
|
-
marked.Renderer = _Renderer;
|
|
3406
|
-
marked.TextRenderer = _TextRenderer;
|
|
3407
|
-
marked.Lexer = _Lexer;
|
|
3408
|
-
marked.lexer = _Lexer.lex;
|
|
3409
|
-
marked.Tokenizer = _Tokenizer;
|
|
3410
|
-
marked.Hooks = _Hooks;
|
|
3411
|
-
marked.parse = marked;
|
|
3412
|
-
|
|
3413
|
-
const pcmChatMessageCss = ":host{display:block;width:100%}.message-round{margin-bottom:16px;width:100%}.user-message-container{display:flex;flex-direction:column;align-items:flex-end;margin-bottom:8px}.assistant-message-container{display:flex;flex-direction:column;align-items:flex-start;margin-bottom:8px}.message-bubble{padding:7px 18px;max-width:80%;position:relative}.user-message{background-image:linear-gradient(100deg, #4A9FFF 0%, #1058FF 100%);font-weight:600;font-size:14px;color:#FFFFFF;border-radius:14px 0 14px 14px}.assistant-message{background-color:#0000000f;border-radius:0 14px 14px 14px;font-weight:600;font-size:14px;color:#00000099}.message-time{font-size:12px;color:#999;margin-top:4px}.user-message-container .message-time{color:rgba(255, 255, 255, 0.7)}.loading-dots{display:flex;align-items:center;padding:12px 16px;background-color:#f0f0f0;border-radius:18px;border-top-left-radius:4px}.loading-dots span{width:8px;height:8px;margin:0 3px;background-color:#999;border-radius:50%;display:inline-block;animation:dot-pulse 1.5s infinite ease-in-out}.loading-dots span:nth-child(2){animation-delay:0.2s}.loading-dots span:nth-child(3){animation-delay:0.4s}@keyframes dot-pulse{0%,80%,100%{transform:scale(0.8);opacity:0.5}40%{transform:scale(1.2);opacity:1}}.file-view{margin-top:8px;padding:8px;background-color:#f5f5f5;border-radius:4px;font-size:14px;word-break:break-all}.input-view{margin-top:8px;border:1px solid #eee;border-radius:4px;overflow:hidden}.input-label{padding:4px 8px;background-color:#f5f5f5;font-size:12px;font-weight:500}.input-value{padding:8px;font-size:14px}.input-metadata{font-size:0.875rem;margin-top:4px}.markdown-body{--base-size-4:0.25rem;--base-size-8:0.5rem;--base-size-16:1rem;--base-size-24:1.5rem;--base-size-40:2.5rem;--base-text-weight-normal:400;--base-text-weight-medium:500;--base-text-weight-semibold:600;--fontStack-monospace:ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;--fgColor-accent:Highlight}@media (prefers-color-scheme: dark){.markdown-body,[data-theme=\"dark\"]{color-scheme:dark;--focus-outlineColor:#1f6feb;--fgColor-default:#f0f6fc;--fgColor-muted:#9198a1;--fgColor-accent:#4493f8;--fgColor-success:#3fb950;--fgColor-attention:#d29922;--fgColor-danger:#f85149;--fgColor-done:#ab7df8;--bgColor-default:#0d1117;--bgColor-muted:#151b23;--bgColor-neutral-muted:#656c7633;--bgColor-attention-muted:#bb800926;--borderColor-default:#3d444d;--borderColor-muted:#3d444db3;--borderColor-neutral-muted:#3d444db3;--borderColor-accent-emphasis:#1f6feb;--borderColor-success-emphasis:#238636;--borderColor-attention-emphasis:#9e6a03;--borderColor-danger-emphasis:#da3633;--borderColor-done-emphasis:#8957e5;--color-prettylights-syntax-comment:#9198a1;--color-prettylights-syntax-constant:#79c0ff;--color-prettylights-syntax-constant-other-reference-link:#a5d6ff;--color-prettylights-syntax-entity:#d2a8ff;--color-prettylights-syntax-storage-modifier-import:#f0f6fc;--color-prettylights-syntax-entity-tag:#7ee787;--color-prettylights-syntax-keyword:#ff7b72;--color-prettylights-syntax-string:#a5d6ff;--color-prettylights-syntax-variable:#ffa657;--color-prettylights-syntax-brackethighlighter-unmatched:#f85149;--color-prettylights-syntax-brackethighlighter-angle:#9198a1;--color-prettylights-syntax-invalid-illegal-text:#f0f6fc;--color-prettylights-syntax-invalid-illegal-bg:#8e1519;--color-prettylights-syntax-carriage-return-text:#f0f6fc;--color-prettylights-syntax-carriage-return-bg:#b62324;--color-prettylights-syntax-string-regexp:#7ee787;--color-prettylights-syntax-markup-list:#f2cc60;--color-prettylights-syntax-markup-heading:#1f6feb;--color-prettylights-syntax-markup-italic:#f0f6fc;--color-prettylights-syntax-markup-bold:#f0f6fc;--color-prettylights-syntax-markup-deleted-text:#ffdcd7;--color-prettylights-syntax-markup-deleted-bg:#67060c;--color-prettylights-syntax-markup-inserted-text:#aff5b4;--color-prettylights-syntax-markup-inserted-bg:#033a16;--color-prettylights-syntax-markup-changed-text:#ffdfb6;--color-prettylights-syntax-markup-changed-bg:#5a1e02;--color-prettylights-syntax-markup-ignored-text:#f0f6fc;--color-prettylights-syntax-markup-ignored-bg:#1158c7;--color-prettylights-syntax-meta-diff-range:#d2a8ff;--color-prettylights-syntax-sublimelinter-gutter-mark:#3d444d}}@media (prefers-color-scheme: light){.markdown-body,[data-theme=\"light\"]{color-scheme:light;--focus-outlineColor:#0969da;--fgColor-default:#1f2328;--fgColor-muted:#59636e;--fgColor-accent:#0969da;--fgColor-success:#1a7f37;--fgColor-attention:#9a6700;--fgColor-danger:#d1242f;--fgColor-done:#8250df;--bgColor-default:#ffffff;--bgColor-muted:#f6f8fa;--bgColor-neutral-muted:#818b981f;--bgColor-attention-muted:#fff8c5;--borderColor-default:#d1d9e0;--borderColor-muted:#d1d9e0b3;--borderColor-neutral-muted:#d1d9e0b3;--borderColor-accent-emphasis:#0969da;--borderColor-success-emphasis:#1a7f37;--borderColor-attention-emphasis:#9a6700;--borderColor-danger-emphasis:#cf222e;--borderColor-done-emphasis:#8250df;--color-prettylights-syntax-comment:#59636e;--color-prettylights-syntax-constant:#0550ae;--color-prettylights-syntax-constant-other-reference-link:#0a3069;--color-prettylights-syntax-entity:#6639ba;--color-prettylights-syntax-storage-modifier-import:#1f2328;--color-prettylights-syntax-entity-tag:#0550ae;--color-prettylights-syntax-keyword:#cf222e;--color-prettylights-syntax-string:#0a3069;--color-prettylights-syntax-variable:#953800;--color-prettylights-syntax-brackethighlighter-unmatched:#82071e;--color-prettylights-syntax-brackethighlighter-angle:#59636e;--color-prettylights-syntax-invalid-illegal-text:#f6f8fa;--color-prettylights-syntax-invalid-illegal-bg:#82071e;--color-prettylights-syntax-carriage-return-text:#f6f8fa;--color-prettylights-syntax-carriage-return-bg:#cf222e;--color-prettylights-syntax-string-regexp:#116329;--color-prettylights-syntax-markup-list:#3b2300;--color-prettylights-syntax-markup-heading:#0550ae;--color-prettylights-syntax-markup-italic:#1f2328;--color-prettylights-syntax-markup-bold:#1f2328;--color-prettylights-syntax-markup-deleted-text:#82071e;--color-prettylights-syntax-markup-deleted-bg:#ffebe9;--color-prettylights-syntax-markup-inserted-text:#116329;--color-prettylights-syntax-markup-inserted-bg:#dafbe1;--color-prettylights-syntax-markup-changed-text:#953800;--color-prettylights-syntax-markup-changed-bg:#ffd8b5;--color-prettylights-syntax-markup-ignored-text:#d1d9e0;--color-prettylights-syntax-markup-ignored-bg:#0550ae;--color-prettylights-syntax-meta-diff-range:#8250df;--color-prettylights-syntax-sublimelinter-gutter-mark:#818b98}}.markdown-body{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;margin:0;font-family:-apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Noto Sans\", Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\";font-size:16px;line-height:1.5;word-wrap:break-word}.markdown-body .octicon{display:inline-block;fill:currentColor;vertical-align:text-bottom}.markdown-body h1:hover .anchor .octicon-link:before,.markdown-body h2:hover .anchor .octicon-link:before,.markdown-body h3:hover .anchor .octicon-link:before,.markdown-body h4:hover .anchor .octicon-link:before,.markdown-body h5:hover .anchor .octicon-link:before,.markdown-body h6:hover .anchor .octicon-link:before{width:16px;height:16px;content:' ';display:inline-block;background-color:currentColor;-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>\");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>\")}.markdown-body details,.markdown-body figcaption,.markdown-body figure{display:block}.markdown-body summary{display:list-item}.markdown-body [hidden]{display:none !important}.markdown-body a{background-color:transparent;color:var(--fgColor-accent);text-decoration:none}.markdown-body abbr[title]{border-bottom:none;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.markdown-body b,.markdown-body strong{font-weight:var(--base-text-weight-semibold, 600)}.markdown-body dfn{font-style:italic}.markdown-body h1{margin:.67em 0;font-weight:var(--base-text-weight-semibold, 600);padding-bottom:.3em;font-size:1.5em;border-bottom:1px solid var(--borderColor-muted)}.markdown-body mark{background-color:var(--bgColor-attention-muted);color:var(--fgColor-default)}.markdown-body small{font-size:90%}.markdown-body sub,.markdown-body sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}.markdown-body sub{bottom:-0.25em}.markdown-body sup{top:-0.5em}.markdown-body img{border-style:none;max-width:100%;box-sizing:content-box}.markdown-body code,.markdown-body kbd,.markdown-body pre,.markdown-body samp{font-family:monospace;font-size:1em}.markdown-body figure{margin:1em var(--base-size-40)}.markdown-body hr{box-sizing:content-box;overflow:hidden;background:transparent;border-bottom:1px solid var(--borderColor-muted);height:.25em;padding:0;margin:var(--base-size-24) 0;background-color:var(--borderColor-default);border:0}.markdown-body input{font:inherit;margin:0;overflow:visible;font-family:inherit;font-size:inherit;line-height:inherit}.markdown-body [type=button],.markdown-body [type=reset],.markdown-body [type=submit]{-webkit-appearance:button;appearance:button}.markdown-body [type=checkbox],.markdown-body [type=radio]{box-sizing:border-box;padding:0}.markdown-body [type=number]::-webkit-inner-spin-button,.markdown-body [type=number]::-webkit-outer-spin-button{height:auto}.markdown-body [type=search]::-webkit-search-cancel-button,.markdown-body [type=search]::-webkit-search-decoration{-webkit-appearance:none;appearance:none}.markdown-body ::-webkit-input-placeholder{color:inherit;opacity:.54}.markdown-body ::-webkit-file-upload-button{-webkit-appearance:button;appearance:button;font:inherit}.markdown-body a:hover{text-decoration:underline}.markdown-body ::placeholder{color:var(--fgColor-muted);opacity:1}.markdown-body hr::before{display:table;content:\"\"}.markdown-body hr::after{display:table;clear:both;content:\"\"}.markdown-body table{border-spacing:0;border-collapse:collapse;display:block;width:max-content;max-width:100%;overflow:auto;font-variant:tabular-nums}.markdown-body td,.markdown-body th{padding:0}.markdown-body details summary{cursor:pointer}.markdown-body a:focus,.markdown-body [role=button]:focus,.markdown-body input[type=radio]:focus,.markdown-body input[type=checkbox]:focus{outline:2px solid var(--focus-outlineColor);outline-offset:-2px;box-shadow:none}.markdown-body a:focus:not(:focus-visible),.markdown-body [role=button]:focus:not(:focus-visible),.markdown-body input[type=radio]:focus:not(:focus-visible),.markdown-body input[type=checkbox]:focus:not(:focus-visible){outline:solid 1px transparent}.markdown-body a:focus-visible,.markdown-body [role=button]:focus-visible,.markdown-body input[type=radio]:focus-visible,.markdown-body input[type=checkbox]:focus-visible{outline:2px solid var(--focus-outlineColor);outline-offset:-2px;box-shadow:none}.markdown-body a:not([class]):focus,.markdown-body a:not([class]):focus-visible,.markdown-body input[type=radio]:focus,.markdown-body input[type=radio]:focus-visible,.markdown-body input[type=checkbox]:focus,.markdown-body input[type=checkbox]:focus-visible{outline-offset:0}.markdown-body kbd{display:inline-block;padding:var(--base-size-4);font:11px var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace);line-height:10px;color:var(--fgColor-default);vertical-align:middle;background-color:var(--bgColor-muted);border:solid 1px var(--borderColor-neutral-muted);border-bottom-color:var(--borderColor-neutral-muted);border-radius:6px;box-shadow:inset 0 -1px 0 var(--borderColor-neutral-muted)}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{margin-top:var(--base-size-24);margin-bottom:var(--base-size-16);font-weight:var(--base-text-weight-semibold, 600);line-height:1.25}.markdown-body h2{font-weight:var(--base-text-weight-semibold, 600);padding-bottom:.3em;font-size:1.2em;border-bottom:1px solid var(--borderColor-muted)}.markdown-body h3{font-weight:var(--base-text-weight-semibold, 600);font-size:1.05em}.markdown-body h4{font-weight:var(--base-text-weight-semibold, 600);font-size:0.875em}.markdown-body h5{font-weight:var(--base-text-weight-semibold, 600);font-size:.85em}.markdown-body h6{font-weight:var(--base-text-weight-semibold, 600);font-size:.80em;color:var(--fgColor-muted)}.markdown-body p{margin-top:0;margin-bottom:10px}.markdown-body blockquote{margin:0;padding:0 1em;color:var(--fgColor-muted);border-left:.25em solid var(--borderColor-default)}.markdown-body ul,.markdown-body ol{margin-top:0;margin-bottom:0;padding-left:2em}.markdown-body ol ol,.markdown-body ul ol{list-style-type:lower-roman}.markdown-body ul ul ol,.markdown-body ul ol ol,.markdown-body ol ul ol,.markdown-body ol ol ol{list-style-type:lower-alpha}.markdown-body dd{margin-left:0}.markdown-body tt,.markdown-body code,.markdown-body samp{font-family:var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace);font-size:12px}.markdown-body pre{margin-top:0;margin-bottom:0;font-family:var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace);font-size:12px;word-wrap:normal}.markdown-body .octicon{display:inline-block;overflow:visible !important;vertical-align:text-bottom;fill:currentColor}.markdown-body input::-webkit-outer-spin-button,.markdown-body input::-webkit-inner-spin-button{margin:0;appearance:none}.markdown-body .mr-2{margin-right:var(--base-size-8, 8px) !important}.markdown-body::before{display:table;content:\"\"}.markdown-body::after{display:table;clear:both;content:\"\"}.markdown-body>*:first-child{margin-top:0 !important}.markdown-body>*:last-child{margin-bottom:0 !important}.markdown-body a:not([href]){color:inherit;text-decoration:none}.markdown-body .absent{color:var(--fgColor-danger)}.markdown-body .anchor{float:left;padding-right:var(--base-size-4);margin-left:-20px;line-height:1}.markdown-body .anchor:focus{outline:none}.markdown-body p,.markdown-body blockquote,.markdown-body ul,.markdown-body ol,.markdown-body dl,.markdown-body table,.markdown-body pre,.markdown-body details{margin-top:0;margin-bottom:var(--base-size-16);font-size:16px;font-weight:400}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body h1 .octicon-link,.markdown-body h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body h5 .octicon-link,.markdown-body h6 .octicon-link{color:var(--fgColor-default);vertical-align:middle;visibility:hidden}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none}.markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor .octicon-link,.markdown-body h6:hover .anchor .octicon-link{visibility:visible}.markdown-body h1 tt,.markdown-body h1 code,.markdown-body h2 tt,.markdown-body h2 code,.markdown-body h3 tt,.markdown-body h3 code,.markdown-body h4 tt,.markdown-body h4 code,.markdown-body h5 tt,.markdown-body h5 code,.markdown-body h6 tt,.markdown-body h6 code{padding:0 .2em;font-size:inherit}.markdown-body summary h1,.markdown-body summary h2,.markdown-body summary h3,.markdown-body summary h4,.markdown-body summary h5,.markdown-body summary h6{display:inline-block}.markdown-body summary h1 .anchor,.markdown-body summary h2 .anchor,.markdown-body summary h3 .anchor,.markdown-body summary h4 .anchor,.markdown-body summary h5 .anchor,.markdown-body summary h6 .anchor{margin-left:-40px}.markdown-body summary h1,.markdown-body summary h2{padding-bottom:0;border-bottom:0}.markdown-body ul.no-list,.markdown-body ol.no-list{padding:0;list-style-type:none}.markdown-body ol[type=\"a s\"]{list-style-type:lower-alpha}.markdown-body ol[type=\"A s\"]{list-style-type:upper-alpha}.markdown-body ol[type=\"i s\"]{list-style-type:lower-roman}.markdown-body ol[type=\"I s\"]{list-style-type:upper-roman}.markdown-body ol[type=\"1\"]{list-style-type:decimal}.markdown-body div>ol:not([type]){list-style-type:decimal}.markdown-body ul ul,.markdown-body ul ol,.markdown-body ol ol,.markdown-body ol ul{margin-top:0;margin-bottom:0}.markdown-body li>p{margin-top:var(--base-size-16)}.markdown-body li+li{margin-top:.25em}.markdown-body dl{padding:0}.markdown-body dl dt{padding:0;margin-top:var(--base-size-16);font-size:1em;font-style:italic;font-weight:var(--base-text-weight-semibold, 600)}.markdown-body dl dd{padding:0 var(--base-size-16);margin-bottom:var(--base-size-16)}.markdown-body table th{font-weight:var(--base-text-weight-semibold, 600)}.markdown-body table th,.markdown-body table td{padding:6px 13px;border:1px solid var(--borderColor-default)}.markdown-body table td>:last-child{margin-bottom:0}.markdown-body table tr{background-color:var(--bgColor-default);border-top:1px solid var(--borderColor-muted)}.markdown-body table tr:nth-child(2n){background-color:var(--bgColor-muted)}.markdown-body table img{background-color:transparent}.markdown-body img[align=right]{padding-left:20px}.markdown-body img[align=left]{padding-right:20px}.markdown-body .emoji{max-width:none;vertical-align:text-top;background-color:transparent}.markdown-body span.frame{display:block;overflow:hidden}.markdown-body span.frame>span{display:block;float:left;width:auto;padding:7px;margin:13px 0 0;overflow:hidden;border:1px solid var(--borderColor-default)}.markdown-body span.frame span img{display:block;float:left}.markdown-body span.frame span span{display:block;padding:5px 0 0;clear:both;color:var(--fgColor-default)}.markdown-body span.align-center{display:block;overflow:hidden;clear:both}.markdown-body span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown-body span.align-center span img{margin:0 auto;text-align:center}.markdown-body span.align-right{display:block;overflow:hidden;clear:both}.markdown-body span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown-body span.align-right span img{margin:0;text-align:right}.markdown-body span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown-body span.float-left span{margin:13px 0 0}.markdown-body span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown-body span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown-body code,.markdown-body tt{padding:.2em .4em;margin:0;font-size:85%;white-space:break-spaces;background-color:var(--bgColor-neutral-muted);border-radius:6px}.markdown-body code br,.markdown-body tt br{display:none}.markdown-body del code{text-decoration:inherit}.markdown-body samp{font-size:85%}.markdown-body pre code{font-size:100%}.markdown-body pre>code{padding:0;margin:0;word-break:normal;white-space:pre;background:transparent;border:0}.markdown-body .highlight{margin-bottom:var(--base-size-16)}.markdown-body .highlight pre{margin-bottom:0;word-break:normal}.markdown-body .highlight pre,.markdown-body pre{padding:var(--base-size-16);overflow:auto;font-size:85%;line-height:1.45;color:var(--fgColor-default);background-color:var(--bgColor-muted);border-radius:6px}.markdown-body pre code,.markdown-body pre tt{display:inline;max-width:auto;padding:0;margin:0;overflow:visible;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown-body .csv-data td,.markdown-body .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}.markdown-body .csv-data .blob-num{padding:10px var(--base-size-8) 9px;text-align:right;background:var(--bgColor-default);border:0}.markdown-body .csv-data tr{border-top:0}.markdown-body .csv-data th{font-weight:var(--base-text-weight-semibold, 600);background:var(--bgColor-muted);border-top:0}.markdown-body [data-footnote-ref]::before{content:\"[\"}.markdown-body [data-footnote-ref]::after{content:\"]\"}.markdown-body .footnotes{font-size:12px;color:var(--fgColor-muted);border-top:1px solid var(--borderColor-default)}.markdown-body .footnotes ol{padding-left:var(--base-size-16)}.markdown-body .footnotes ol ul{display:inline-block;padding-left:var(--base-size-16);margin-top:var(--base-size-16)}.markdown-body .footnotes li{position:relative}.markdown-body .footnotes li:target::before{position:absolute;top:calc(var(--base-size-8)*-1);right:calc(var(--base-size-8)*-1);bottom:calc(var(--base-size-8)*-1);left:calc(var(--base-size-24)*-1);pointer-events:none;content:\"\";border:2px solid var(--borderColor-accent-emphasis);border-radius:6px}.markdown-body .footnotes li:target{color:var(--fgColor-default)}.markdown-body .footnotes .data-footnote-backref g-emoji{font-family:monospace}.markdown-body body:has(:modal){padding-right:var(--dialog-scrollgutter) !important}.markdown-body .pl-c{color:var(--color-prettylights-syntax-comment)}.markdown-body .pl-c1,.markdown-body .pl-s .pl-v{color:var(--color-prettylights-syntax-constant)}.markdown-body .pl-e,.markdown-body .pl-en{color:var(--color-prettylights-syntax-entity)}.markdown-body .pl-smi,.markdown-body .pl-s .pl-s1{color:var(--color-prettylights-syntax-storage-modifier-import)}.markdown-body .pl-ent{color:var(--color-prettylights-syntax-entity-tag)}.markdown-body .pl-k{color:var(--color-prettylights-syntax-keyword)}.markdown-body .pl-s,.markdown-body .pl-pds,.markdown-body .pl-s .pl-pse .pl-s1,.markdown-body .pl-sr,.markdown-body .pl-sr .pl-cce,.markdown-body .pl-sr .pl-sre,.markdown-body .pl-sr .pl-sra{color:var(--color-prettylights-syntax-string)}.markdown-body .pl-v,.markdown-body .pl-smw{color:var(--color-prettylights-syntax-variable)}.markdown-body .pl-bu{color:var(--color-prettylights-syntax-brackethighlighter-unmatched)}.markdown-body .pl-ii{color:var(--color-prettylights-syntax-invalid-illegal-text);background-color:var(--color-prettylights-syntax-invalid-illegal-bg)}.markdown-body .pl-c2{color:var(--color-prettylights-syntax-carriage-return-text);background-color:var(--color-prettylights-syntax-carriage-return-bg)}.markdown-body .pl-sr .pl-cce{font-weight:bold;color:var(--color-prettylights-syntax-string-regexp)}.markdown-body .pl-ml{color:var(--color-prettylights-syntax-markup-list)}.markdown-body .pl-mh,.markdown-body .pl-mh .pl-en,.markdown-body .pl-ms{font-weight:bold;color:var(--color-prettylights-syntax-markup-heading)}.markdown-body .pl-mi{font-style:italic;color:var(--color-prettylights-syntax-markup-italic)}.markdown-body .pl-mb{font-weight:bold;color:var(--color-prettylights-syntax-markup-bold)}.markdown-body .pl-md{color:var(--color-prettylights-syntax-markup-deleted-text);background-color:var(--color-prettylights-syntax-markup-deleted-bg)}.markdown-body .pl-mi1{color:var(--color-prettylights-syntax-markup-inserted-text);background-color:var(--color-prettylights-syntax-markup-inserted-bg)}.markdown-body .pl-mc{color:var(--color-prettylights-syntax-markup-changed-text);background-color:var(--color-prettylights-syntax-markup-changed-bg)}.markdown-body .pl-mi2{color:var(--color-prettylights-syntax-markup-ignored-text);background-color:var(--color-prettylights-syntax-markup-ignored-bg)}.markdown-body .pl-mdr{font-weight:bold;color:var(--color-prettylights-syntax-meta-diff-range)}.markdown-body .pl-ba{color:var(--color-prettylights-syntax-brackethighlighter-angle)}.markdown-body .pl-sg{color:var(--color-prettylights-syntax-sublimelinter-gutter-mark)}.markdown-body .pl-corl{text-decoration:underline;color:var(--color-prettylights-syntax-constant-other-reference-link)}.markdown-body [role=button]:focus:not(:focus-visible),.markdown-body [role=tabpanel][tabindex=\"0\"]:focus:not(:focus-visible),.markdown-body button:focus:not(:focus-visible),.markdown-body summary:focus:not(:focus-visible),.markdown-body a:focus:not(:focus-visible){outline:none;box-shadow:none}.markdown-body [tabindex=\"0\"]:focus:not(:focus-visible),.markdown-body details-dialog:focus:not(:focus-visible){outline:none}.markdown-body g-emoji{display:inline-block;min-width:1ch;font-family:\"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";font-size:1em;font-style:normal !important;font-weight:var(--base-text-weight-normal, 400);line-height:1;vertical-align:-0.075em}.markdown-body g-emoji img{width:1em;height:1em}.markdown-body .task-list-item{list-style-type:none}.markdown-body .task-list-item label{font-weight:var(--base-text-weight-normal, 400)}.markdown-body .task-list-item.enabled label{cursor:pointer}.markdown-body .task-list-item+.task-list-item{margin-top:var(--base-size-4)}.markdown-body .task-list-item .handle{display:none}.markdown-body .task-list-item-checkbox{margin:0 .2em .25em -1.4em;vertical-align:middle}.markdown-body ul:dir(rtl) .task-list-item-checkbox{margin:0 -1.6em .25em .2em}.markdown-body ol:dir(rtl) .task-list-item-checkbox{margin:0 -1.6em .25em .2em}.markdown-body .contains-task-list:hover .task-list-item-convert-container,.markdown-body .contains-task-list:focus-within .task-list-item-convert-container{display:block;width:auto;height:24px;overflow:visible;clip:auto}.markdown-body ::-webkit-calendar-picker-indicator{filter:invert(50%)}.markdown-body .markdown-alert{padding:var(--base-size-8) var(--base-size-16);margin-bottom:var(--base-size-16);color:inherit;border-left:.25em solid var(--borderColor-default)}.markdown-body .markdown-alert>:first-child{margin-top:0}.markdown-body .markdown-alert>:last-child{margin-bottom:0}.markdown-body .markdown-alert .markdown-alert-title{display:flex;font-weight:var(--base-text-weight-medium, 500);align-items:center;line-height:1}.markdown-body .markdown-alert.markdown-alert-note{border-left-color:var(--borderColor-accent-emphasis)}.markdown-body .markdown-alert.markdown-alert-note .markdown-alert-title{color:var(--fgColor-accent)}.markdown-body .markdown-alert.markdown-alert-important{border-left-color:var(--borderColor-done-emphasis)}.markdown-body .markdown-alert.markdown-alert-important .markdown-alert-title{color:var(--fgColor-done)}.markdown-body .markdown-alert.markdown-alert-warning{border-left-color:var(--borderColor-attention-emphasis)}.markdown-body .markdown-alert.markdown-alert-warning .markdown-alert-title{color:var(--fgColor-attention)}.markdown-body .markdown-alert.markdown-alert-tip{border-left-color:var(--borderColor-success-emphasis)}.markdown-body .markdown-alert.markdown-alert-tip .markdown-alert-title{color:var(--fgColor-success)}.markdown-body .markdown-alert.markdown-alert-caution{border-left-color:var(--borderColor-danger-emphasis)}.markdown-body .markdown-alert.markdown-alert-caution .markdown-alert-title{color:var(--fgColor-danger)}.markdown-body>*:first-child>.heading-element:first-child{margin-top:0 !important}.markdown-body .highlight pre:has(+.zeroclipboard-container){min-height:52px}.message-actions{position:absolute;top:8px;right:8px;opacity:0;transition:opacity 0.2s ease}.assistant-message:hover .message-actions{opacity:1}.copy-button{background:none;border:none;cursor:pointer;padding:4px;border-radius:4px;color:#6e6e80;display:flex;align-items:center;justify-content:center}.copy-button:hover{background-color:rgba(0, 0, 0, 0.05);color:#000}.copy-button svg{width:16px;height:16px}";
|
|
3414
|
-
|
|
3415
|
-
const ChatMessageComponent = class {
|
|
3416
|
-
/**
|
|
3417
|
-
* 消息数据
|
|
3418
|
-
*/
|
|
3419
|
-
message;
|
|
3420
|
-
/**
|
|
3421
|
-
* 消息变更事件
|
|
3422
|
-
*/
|
|
3423
|
-
messageChange;
|
|
3424
|
-
get hostElement() { return getElement(this); }
|
|
3425
|
-
constructor(hostRef) {
|
|
3426
|
-
registerInstance(this, hostRef);
|
|
3427
|
-
this.messageChange = createEvent(this, "messageChange");
|
|
3428
|
-
// 配置 marked 选项
|
|
3429
|
-
marked.setOptions({
|
|
3430
|
-
breaks: true,
|
|
3431
|
-
gfm: true
|
|
3432
|
-
});
|
|
3433
|
-
}
|
|
3434
|
-
// 复制消息内容到剪贴板
|
|
3435
|
-
copyMessageContent() {
|
|
3436
|
-
if (this.message.answer) {
|
|
3437
|
-
navigator.clipboard.writeText(this.message.answer)
|
|
3438
|
-
.then(() => {
|
|
3439
|
-
// 可以添加复制成功的提示
|
|
3440
|
-
console.log('内容已复制到剪贴板');
|
|
3441
|
-
})
|
|
3442
|
-
.catch(err => {
|
|
3443
|
-
console.error('复制失败:', err);
|
|
3444
|
-
});
|
|
3445
|
-
}
|
|
3446
|
-
}
|
|
3447
|
-
// 渲染用户消息部分
|
|
3448
|
-
renderUserMessage() {
|
|
3449
|
-
if (!this.message?.query?.trim())
|
|
3450
|
-
return null;
|
|
3451
|
-
return (h("div", { class: "user-message-container" }, h("div", { class: "message-bubble user-message" }, h("p", null, this.message.query), this.renderInputs())));
|
|
3452
|
-
}
|
|
3453
|
-
// 渲染助手消息部分
|
|
3454
|
-
renderAssistantMessage() {
|
|
3455
|
-
if (!this.message.answer && !this.message.isStreaming)
|
|
3456
|
-
return null;
|
|
3457
|
-
// 只有在开始流式输出且还没有内容时才显示loading
|
|
3458
|
-
const showLoading = this.message.isStreaming && !this.message.answer;
|
|
3459
|
-
const htmlContent = this.message.answer ? marked(this.message.answer) : '';
|
|
3460
|
-
return (h("div", { class: "assistant-message-container" }, h("div", { class: "message-bubble assistant-message" }, h("div", { class: "markdown-content markdown-body", innerHTML: showLoading ?
|
|
3461
|
-
`请稍等...` :
|
|
3462
|
-
htmlContent })), !showLoading && this.message.answer && (h("div", { class: "message-actions" }, h("button", { class: "copy-button", onClick: () => this.copyMessageContent(), title: "\u590D\u5236\u5185\u5BB9" }, h("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" }, h("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }), h("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })))))));
|
|
3463
|
-
}
|
|
3464
|
-
// 渲染输入数据
|
|
3465
|
-
renderInputs() {
|
|
3466
|
-
if (!this.message.inputs)
|
|
3467
|
-
return null;
|
|
3468
|
-
return (h("div", null, Object.keys(this.message.inputs).map((key, index) => {
|
|
3469
|
-
const value = this.message.inputs[key];
|
|
3470
|
-
if (value && !key.startsWith('hide_') && key !== 'answer') {
|
|
3471
|
-
if (key === 'file_url') {
|
|
3472
|
-
return h("div", { key: index, class: "file-view" }, value);
|
|
3473
|
-
}
|
|
3474
|
-
else if (key === 'file_urls' || key === 'fileUrls') {
|
|
3475
|
-
const fileList = value.split(',');
|
|
3476
|
-
return (h("div", { key: index, class: "flex flex-wrap" }, fileList.map((fileUrl, fileIndex) => (h("div", { key: fileIndex, class: "file-view" }, fileUrl)))));
|
|
3477
|
-
}
|
|
3478
|
-
else if (key === 'job_info') {
|
|
3479
|
-
return (h("div", { key: index, class: "input-view" }, h("div", { class: "input-label" }, "\u804C\u4F4D\u4FE1\u606F"), h("div", { class: "input-value" }, value)));
|
|
3480
|
-
}
|
|
3481
|
-
else if (key === 'rule') {
|
|
3482
|
-
return (h("div", { key: index, class: "input-view" }, h("div", { class: "input-label" }, "\u8BC4\u4F30\u89C4\u5219"), h("div", { class: "input-value" }, value)));
|
|
3483
|
-
}
|
|
3484
|
-
else {
|
|
3485
|
-
return h("div", { key: index, class: "input-metadata" }, key, ": ", `${value}`);
|
|
3486
|
-
}
|
|
3487
|
-
}
|
|
3488
|
-
return null;
|
|
3489
|
-
})));
|
|
3490
|
-
}
|
|
3491
|
-
render() {
|
|
3492
|
-
return (h("div", { key: '5748f17414b9442b9b791955de38437184fa2f7e', class: "message-round" }, this.renderUserMessage(), this.renderAssistantMessage()));
|
|
3493
|
-
}
|
|
3494
|
-
};
|
|
3495
|
-
ChatMessageComponent.style = pcmChatMessageCss;
|
|
3496
|
-
|
|
3497
|
-
const pcmMnmsModalCss = ":host{display:block}.modal-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background-color:rgba(0, 0, 0, 0.5);display:flex;justify-content:center;align-items:center;overflow-y:auto;padding:20px;z-index:1000}.fullscreen-overlay{padding:0}.modal-container{background:white;border-radius:8px;width:100%;max-width:500px;display:flex;flex-direction:column;position:relative;margin:auto;transition:all 0.3s ease-out;overflow:hidden}.modal-container.fullscreen{width:100vw;max-width:none;height:100%;border-radius:0;margin:0;display:flex;flex-direction:column;height:100vh;max-height:100vh}.pc-layout{width:80%;max-width:500px;min-width:320px}.mobile-layout{width:100%;height:100%;border-radius:0}@media screen and (max-width: 768px){.pc-layout{width:95%}.modal-overlay{padding:0}.modal-container.fullscreen{height:-webkit-fill-available;max-height:-webkit-fill-available;padding:env(safe-area-inset-top) 0 env(safe-area-inset-bottom);margin-top:40px;height:calc(100% - 40px);max-height:calc(100% - 40px);border-radius:16px 16px 0 0}.modal-container.mobile-layout{width:100%;height:100vh;max-height:100vh;min-height:100vh;border-radius:0;margin:0;display:flex;flex-direction:column}}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:4px 16px;height:50px;border-bottom:1px solid #e8e8e8;flex-shrink:0;}.header-left{display:flex;align-items:center;gap:8px}.header-icon{width:24px;height:24px}.close-button{background:transparent;border:none;cursor:pointer;padding:8px;display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:4px}.close-button:hover{background-color:rgba(0, 0, 0, 0.04)}.close-button span{font-size:24px;line-height:1;color:#999}.close-button:hover span{color:#666}.upload-container{padding:30px 20px;display:flex;flex-direction:column;align-items:center;text-align:center;height:calc(100% - 60px);transition:opacity 0.5s ease-out}.upload-container h3{margin-top:0;margin-bottom:20px;font-size:18px;color:#333}.upload-area{border:2px dashed #ddd;border-radius:8px;width:100%;max-width:400px;cursor:pointer;transition:all 0.3s ease;margin-bottom:20px}.upload-area:hover{border-color:#1890ff;background-color:rgba(24, 144, 255, 0.05)}.upload-placeholder{display:flex;flex-direction:column;align-items:center;color:#666}.upload-hint{font-size:0.8rem;color:#999;margin-top:0.5rem}.file-info{display:flex;align-items:center;justify-content:space-between;padding:8px;background:#f9f9f9;border:1px solid #e8e8e8;border-radius:4px}.file-info span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:calc(100% - 30px)}.remove-file{background:transparent;border:none;color:#999;cursor:pointer;padding:4px 8px;font-size:16px;line-height:1;border-radius:4px;transition:all 0.2s}.remove-file:hover{background-color:#f0f0f0;color:#666}.submit-button{margin-top:10px;padding:10px 30px;background:#1890ff;color:white;border:none;border-radius:4px;font-size:16px;cursor:pointer;transition:all 0.3s ease;width:100%;max-width:400px}.submit-button:disabled{background:#ccc;cursor:not-allowed}.submit-button:hover:not(:disabled){background:#40a9ff}.file-input{display:none}.transitioning{position:relative;overflow:hidden}.fade-out{opacity:0.7;transition:opacity 0.5s ease-out}.transition-container{margin-top:20px;width:100%}.progress-bar{width:100%;height:6px;background-color:#f0f0f0;border-radius:3px;overflow:hidden;margin-bottom:10px}.progress-fill{height:100%;background-color:#4285f4;transition:width 0.3s ease-out}.upload-container.fade-out{opacity:0;transform:translateY(-20px);transition:opacity 0.3s ease, transform 0.3s ease}.chat-modal-container{position:absolute;top:0;left:0;right:0;bottom:0;display:flex;flex-direction:column}.modal-container{position:relative;overflow:hidden}";
|
|
3498
|
-
|
|
3499
|
-
const MnmsModal = class {
|
|
3500
|
-
constructor(hostRef) {
|
|
3501
|
-
registerInstance(this, hostRef);
|
|
3502
|
-
this.modalClosed = createEvent(this, "modalClosed");
|
|
3503
|
-
this.uploadSuccess = createEvent(this, "uploadSuccess");
|
|
3504
|
-
this.streamComplete = createEvent(this, "streamComplete");
|
|
3505
|
-
this.conversationStart = createEvent(this, "conversationStart");
|
|
3506
|
-
this.interviewComplete = createEvent(this, "interviewComplete");
|
|
3507
|
-
}
|
|
3508
|
-
/**
|
|
3509
|
-
* 模态框标题
|
|
3510
|
-
*/
|
|
3511
|
-
modalTitle = '在线客服';
|
|
3512
|
-
/**
|
|
3513
|
-
* API鉴权密钥
|
|
3514
|
-
*/
|
|
3515
|
-
apiKey = '';
|
|
3516
|
-
/**
|
|
3517
|
-
* 是否显示聊天模态框
|
|
3518
|
-
*/
|
|
3519
|
-
isOpen = false;
|
|
3520
|
-
/**
|
|
3521
|
-
* 当点击模态框关闭时触发
|
|
3522
|
-
*/
|
|
3523
|
-
modalClosed;
|
|
3524
|
-
/**
|
|
3525
|
-
* 应用图标URL
|
|
3526
|
-
*/
|
|
3527
|
-
icon;
|
|
3528
|
-
/**
|
|
3529
|
-
* 聊天框的页面层级
|
|
3530
|
-
*/
|
|
3531
|
-
zIndex = 1000;
|
|
3532
|
-
/**
|
|
3533
|
-
* 是否展示顶部标题栏
|
|
3534
|
-
*/
|
|
3535
|
-
isShowHeader = true;
|
|
3536
|
-
/**
|
|
3537
|
-
* 是否展示右上角的关闭按钮
|
|
3538
|
-
*/
|
|
3539
|
-
isNeedClose = true;
|
|
3540
|
-
/**
|
|
3541
|
-
* 会话ID
|
|
3542
|
-
*/
|
|
3543
|
-
conversationId;
|
|
3544
|
-
/**
|
|
3545
|
-
* 默认查询文本
|
|
3546
|
-
*/
|
|
3547
|
-
defaultQuery = '';
|
|
3548
|
-
/**
|
|
3549
|
-
* 是否以全屏模式打开
|
|
3550
|
-
*/
|
|
3551
|
-
fullscreen = false;
|
|
3552
|
-
/**
|
|
3553
|
-
* 是否为移动端布局
|
|
3554
|
-
*/
|
|
3555
|
-
isMobile = false;
|
|
3556
|
-
/**
|
|
3557
|
-
* 自定义输入参数
|
|
3558
|
-
*/
|
|
3559
|
-
customInputs = {};
|
|
3560
|
-
/**
|
|
3561
|
-
* 上传成功事件
|
|
3562
|
-
*/
|
|
3563
|
-
uploadSuccess;
|
|
3564
|
-
/**
|
|
3565
|
-
* 流式输出完成事件
|
|
3566
|
-
*/
|
|
3567
|
-
streamComplete;
|
|
3568
|
-
/**
|
|
3569
|
-
* 新会话开始的回调,只会在一轮对话开始时触发一次
|
|
3570
|
-
*/
|
|
3571
|
-
conversationStart;
|
|
3572
|
-
/**
|
|
3573
|
-
* 当聊天完成时触发
|
|
3574
|
-
*/
|
|
3575
|
-
interviewComplete;
|
|
3576
|
-
selectedFile = null;
|
|
3577
|
-
isUploading = false;
|
|
3578
|
-
uploadedFileInfo = null;
|
|
3579
|
-
showChatModal = false;
|
|
3580
|
-
// 添加新的状态来控制过渡动画
|
|
3581
|
-
isTransitioning = false;
|
|
3582
|
-
transitionTimer = null;
|
|
3583
|
-
get hostElement() { return getElement(this); }
|
|
3584
|
-
handleClose = () => {
|
|
3585
|
-
this.isOpen = false;
|
|
3586
|
-
this.modalClosed.emit();
|
|
3587
|
-
};
|
|
3588
|
-
handleFileChange = (event) => {
|
|
3589
|
-
const input = event.target;
|
|
3590
|
-
if (input.files && input.files.length > 0) {
|
|
3591
|
-
this.selectedFile = input.files[0];
|
|
3592
|
-
}
|
|
3593
|
-
};
|
|
3594
|
-
handleUploadClick = () => {
|
|
3595
|
-
const fileInput = this.hostElement.shadowRoot?.querySelector('.file-input');
|
|
3596
|
-
fileInput?.click();
|
|
3597
|
-
};
|
|
3598
|
-
clearSelectedFile = () => {
|
|
3599
|
-
this.selectedFile = null;
|
|
3600
|
-
this.uploadedFileInfo = null;
|
|
3601
|
-
const fileInput = this.hostElement.shadowRoot?.querySelector('.file-input');
|
|
3602
|
-
if (fileInput) {
|
|
3603
|
-
fileInput.value = '';
|
|
3604
|
-
}
|
|
3605
|
-
};
|
|
3606
|
-
async uploadFile() {
|
|
3607
|
-
if (!this.selectedFile)
|
|
3608
|
-
return;
|
|
3609
|
-
this.isUploading = true;
|
|
3610
|
-
try {
|
|
3611
|
-
const formData = new FormData();
|
|
3612
|
-
formData.append('file', this.selectedFile);
|
|
3613
|
-
const response = await fetch('https://pcm_api.ylzhaopin.com/external/v1/files/upload', {
|
|
3614
|
-
method: 'POST',
|
|
3615
|
-
headers: {
|
|
3616
|
-
'authorization': 'Bearer ' + this.apiKey
|
|
3617
|
-
},
|
|
3618
|
-
body: formData
|
|
3619
|
-
});
|
|
3620
|
-
const result = await response.json();
|
|
3621
|
-
if (!response.ok) {
|
|
3622
|
-
throw new Error(result.message || '文件上传失败');
|
|
3623
|
-
}
|
|
3624
|
-
if (result) {
|
|
3625
|
-
this.uploadedFileInfo = {
|
|
3626
|
-
cos_key: result.cos_key,
|
|
3627
|
-
filename: result.filename,
|
|
3628
|
-
ext: result.ext,
|
|
3629
|
-
presigned_url: result.presigned_url
|
|
3630
|
-
};
|
|
3631
|
-
// 触发上传成功事件
|
|
3632
|
-
this.uploadSuccess.emit(this.uploadedFileInfo);
|
|
3633
|
-
}
|
|
3634
|
-
}
|
|
3635
|
-
catch (error) {
|
|
3636
|
-
console.error('文件上传错误:', error);
|
|
3637
|
-
this.clearSelectedFile();
|
|
3638
|
-
alert(error instanceof Error ? error.message : '文件上传失败,请重试');
|
|
3639
|
-
}
|
|
3640
|
-
finally {
|
|
3641
|
-
this.isUploading = false;
|
|
3642
|
-
}
|
|
3643
|
-
}
|
|
3644
|
-
handleStartInterview = async () => {
|
|
3645
|
-
if (!this.selectedFile) {
|
|
3646
|
-
alert('请上传简历');
|
|
3647
|
-
return;
|
|
3648
|
-
}
|
|
3649
|
-
// 如果还没上传,先上传文件
|
|
3650
|
-
if (!this.uploadedFileInfo) {
|
|
3651
|
-
await this.uploadFile();
|
|
3652
|
-
if (!this.uploadedFileInfo) {
|
|
3653
|
-
return; // 上传失败
|
|
3654
|
-
}
|
|
3655
|
-
}
|
|
3656
|
-
console.log('传递的customInputs:', {
|
|
3657
|
-
...this.customInputs,
|
|
3658
|
-
file_url: this.uploadedFileInfo.cos_key
|
|
3659
|
-
});
|
|
3660
|
-
// 直接显示聊天模态框,不使用过渡动画
|
|
3661
|
-
this.showChatModal = true;
|
|
3662
|
-
};
|
|
3663
|
-
handleIsOpenChange(newValue) {
|
|
3664
|
-
if (!newValue) {
|
|
3665
|
-
// 重置状态
|
|
3666
|
-
this.clearSelectedFile();
|
|
3667
|
-
this.showChatModal = false;
|
|
3668
|
-
// 清除可能存在的计时器
|
|
3669
|
-
if (this.transitionTimer) {
|
|
3670
|
-
clearTimeout(this.transitionTimer);
|
|
3671
|
-
this.transitionTimer = null;
|
|
3672
|
-
}
|
|
3673
|
-
}
|
|
3674
|
-
else if (this.conversationId) {
|
|
3675
|
-
// 如果有会话ID,直接显示聊天模态框
|
|
3676
|
-
this.showChatModal = true;
|
|
3677
|
-
}
|
|
3678
|
-
}
|
|
3679
|
-
// 处理流式输出完成事件
|
|
3680
|
-
handleStreamComplete = (event) => {
|
|
3681
|
-
// 将事件转发出去
|
|
3682
|
-
this.streamComplete.emit(event.detail);
|
|
3683
|
-
};
|
|
3684
|
-
// 处理会话开始事件
|
|
3685
|
-
handleConversationStart = (event) => {
|
|
3686
|
-
this.conversationStart.emit(event.detail);
|
|
3687
|
-
};
|
|
3688
|
-
// 处理面试完成事件
|
|
3689
|
-
handleInterviewComplete = (event) => {
|
|
3690
|
-
this.interviewComplete.emit(event.detail);
|
|
3691
|
-
};
|
|
3692
|
-
render() {
|
|
3693
|
-
if (!this.isOpen)
|
|
3694
|
-
return null;
|
|
3695
|
-
const modalStyle = {
|
|
3696
|
-
zIndex: String(this.zIndex)
|
|
3697
|
-
};
|
|
3698
|
-
console.log('showChatModal:', this.showChatModal);
|
|
3699
|
-
const containerClass = {
|
|
3700
|
-
'modal-container': true,
|
|
3701
|
-
'fullscreen': this.fullscreen,
|
|
3702
|
-
'pc-layout': !this.isMobile,
|
|
3703
|
-
'mobile-layout': this.isMobile
|
|
3704
|
-
};
|
|
3705
|
-
const overlayClass = {
|
|
3706
|
-
'modal-overlay': true,
|
|
3707
|
-
'fullscreen-overlay': this.fullscreen
|
|
3708
|
-
};
|
|
3709
|
-
// 检查是否有会话ID,如果有则直接显示聊天模态框
|
|
3710
|
-
if (this.conversationId && !this.showChatModal) {
|
|
3711
|
-
this.showChatModal = true;
|
|
3712
|
-
}
|
|
3713
|
-
return (h("div", { class: overlayClass, style: modalStyle }, h("div", { class: containerClass }, this.isShowHeader && (h("div", { class: "modal-header" }, h("div", { class: "header-left" }, this.icon && h("img", { src: this.icon, class: "header-icon", alt: "\u5E94\u7528\u56FE\u6807" }), h("div", null, this.modalTitle)), this.isNeedClose && (h("button", { class: "close-button", onClick: this.handleClose }, h("span", null, "\u00D7"))))), !this.showChatModal && !this.conversationId && (h("div", { class: "upload-container" }, h("h3", null, "\u5F00\u59CB\u524D\uFF0C\u8BF7\u4E0A\u4F20\u60A8\u7684\u7B80\u5386"), h("div", { class: "upload-area", onClick: this.handleUploadClick }, this.selectedFile ? (h("div", { class: "file-info" }, h("span", null, this.selectedFile.name), h("button", { class: "remove-file", onClick: (e) => {
|
|
3714
|
-
e.stopPropagation();
|
|
3715
|
-
this.clearSelectedFile();
|
|
3716
|
-
} }, "\u00D7"))) : (h("div", { class: "upload-placeholder" }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", width: "48", height: "48" }, h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "2", d: "M12 4v16m0-16l-4 4m4-4l4 4" })), h("p", null, "\u70B9\u51FB\u4E0A\u4F20\u7B80\u5386"), h("p", { class: "upload-hint" }, "\u652F\u6301 txt\u3001 markdown\u3001 pdf\u3001 docx\u3001 md \u683C\u5F0F")))), h("button", { class: "submit-button", disabled: !this.selectedFile || this.isUploading, onClick: this.handleStartInterview }, this.isUploading ? '上传中...' : '开始面试'), h("input", { type: "file", class: "file-input", onChange: this.handleFileChange, accept: ".pdf,.doc,.docx,.txt,.md" }))), this.showChatModal && (h("div", { class: "chat-modal-container" }, h("pcm-app-chat-modal", { isOpen: true, modalTitle: this.modalTitle, icon: this.icon, apiKey: this.apiKey, isShowHeader: this.isShowHeader, isNeedClose: this.isShowHeader, zIndex: this.zIndex, fullscreen: this.fullscreen, conversationId: this.conversationId, defaultQuery: this.defaultQuery, enableVoice: false, customInputs: this.conversationId ? undefined : {
|
|
3717
|
-
...this.customInputs,
|
|
3718
|
-
file_url: this.uploadedFileInfo?.cos_key
|
|
3719
|
-
}, interviewMode: "text", onModalClosed: this.handleClose, onStreamComplete: this.handleStreamComplete, onConversationStart: this.handleConversationStart, onInterviewComplete: this.handleInterviewComplete }))))));
|
|
3720
|
-
}
|
|
3721
|
-
static get watchers() { return {
|
|
3722
|
-
"isOpen": ["handleIsOpenChange"]
|
|
3723
|
-
}; }
|
|
3724
|
-
};
|
|
3725
|
-
MnmsModal.style = pcmMnmsModalCss;
|
|
3726
|
-
|
|
3727
|
-
export { ChatAPPModal as pcm_app_chat_modal, ChatMessageComponent as pcm_chat_message, MnmsModal as pcm_mnms_modal };
|
|
3728
|
-
//# sourceMappingURL=pcm-app-chat-modal.pcm-chat-message.pcm-mnms-modal.entry.js.map
|
|
3729
|
-
|
|
3730
|
-
//# sourceMappingURL=pcm-app-chat-modal_3.entry.js.map
|