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.
Files changed (146) hide show
  1. package/LICENSE +21 -21
  2. package/dist/cjs/index-BFPEnLbS.js +195 -0
  3. package/dist/cjs/index-BFPEnLbS.js.map +1 -0
  4. package/dist/cjs/index.cjs.js +1 -1
  5. package/dist/cjs/loader.cjs.js +1 -1
  6. package/dist/cjs/my-component.cjs.entry.js +2 -2
  7. package/dist/cjs/my-component.cjs.entry.js.map +1 -1
  8. package/dist/cjs/my-component.entry.cjs.js.map +1 -1
  9. package/dist/cjs/pcm-agents.cjs.js +1 -1
  10. 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
  11. package/dist/cjs/pcm-app-chat-modal_7.cjs.entry.js +6560 -0
  12. package/dist/cjs/pcm-app-chat-modal_7.cjs.entry.js.map +1 -0
  13. package/dist/cjs/pcm-chat-modal.cjs.entry.js +17 -32
  14. package/dist/cjs/pcm-chat-modal.cjs.entry.js.map +1 -1
  15. package/dist/cjs/pcm-chat-modal.entry.cjs.js.map +1 -1
  16. package/dist/collection/collection-manifest.json +2 -0
  17. package/dist/collection/components/my-component/my-component.css +3 -3
  18. package/dist/collection/components/my-component/my-component.js +1 -1
  19. package/dist/collection/components/my-component/my-component.js.map +1 -1
  20. package/dist/collection/components/pcm-app-chat-modal/pcm-app-chat-modal.css +2 -1
  21. package/dist/collection/components/pcm-app-chat-modal/pcm-app-chat-modal.js +137 -159
  22. package/dist/collection/components/pcm-app-chat-modal/pcm-app-chat-modal.js.map +1 -1
  23. package/dist/collection/components/pcm-chat-message/pcm-chat-message.css +66 -12
  24. package/dist/collection/components/pcm-chat-message/pcm-chat-message.js +106 -13
  25. package/dist/collection/components/pcm-chat-message/pcm-chat-message.js.map +1 -1
  26. package/dist/collection/components/pcm-chat-modal/pcm-chat-modal.js +39 -34
  27. package/dist/collection/components/pcm-chat-modal/pcm-chat-modal.js.map +1 -1
  28. package/dist/collection/components/pcm-hr-chat-modal/pcm-hr-chat-modal.js +76 -105
  29. package/dist/collection/components/pcm-hr-chat-modal/pcm-hr-chat-modal.js.map +1 -1
  30. package/dist/collection/components/pcm-jlpx-modal/pcm-jlpx-modal.css +162 -0
  31. package/dist/collection/components/pcm-jlpx-modal/pcm-jlpx-modal.js +616 -0
  32. package/dist/collection/components/pcm-jlpx-modal/pcm-jlpx-modal.js.map +1 -0
  33. package/dist/collection/components/pcm-mnms-modal/pcm-mnms-modal.css +0 -79
  34. package/dist/collection/components/pcm-mnms-modal/pcm-mnms-modal.js +133 -81
  35. package/dist/collection/components/pcm-mnms-modal/pcm-mnms-modal.js.map +1 -1
  36. package/dist/collection/components/pcm-video-chat-modal/pcm-video-chat-modal.js +77 -101
  37. package/dist/collection/components/pcm-video-chat-modal/pcm-video-chat-modal.js.map +1 -1
  38. package/dist/collection/components/pcm-zygh-modal/pcm-zygh-modal.css +273 -0
  39. package/dist/collection/components/pcm-zygh-modal/pcm-zygh-modal.js +613 -0
  40. package/dist/collection/components/pcm-zygh-modal/pcm-zygh-modal.js.map +1 -0
  41. package/dist/collection/global/global.css +324 -0
  42. package/dist/collection/index.js.map +1 -1
  43. package/dist/collection/interfaces/chat.js.map +1 -1
  44. package/dist/collection/utils/utils.js +54 -113
  45. package/dist/collection/utils/utils.js.map +1 -1
  46. package/dist/components/index.js +1298 -11280
  47. package/dist/components/index.js.map +1 -1
  48. package/dist/components/my-component.js +2 -3
  49. package/dist/components/my-component.js.map +1 -1
  50. package/dist/components/{p-C4l_DOnx.js → p-BctfuDvG.js} +106 -147
  51. package/dist/components/p-BctfuDvG.js.map +1 -0
  52. package/dist/components/{p-D0s1Q-3O.js → p-LkDC0SN2.js} +343 -16
  53. package/dist/components/p-LkDC0SN2.js.map +1 -0
  54. package/dist/components/pcm-app-chat-modal.js +1 -1
  55. package/dist/components/pcm-chat-message.js +1 -1
  56. package/dist/components/pcm-chat-modal.js +19 -34
  57. package/dist/components/pcm-chat-modal.js.map +1 -1
  58. package/dist/components/pcm-hr-chat-modal.js +70 -100
  59. package/dist/components/pcm-hr-chat-modal.js.map +1 -1
  60. package/dist/components/pcm-jlpx-modal.d.ts +11 -0
  61. package/dist/components/pcm-jlpx-modal.js +339 -0
  62. package/dist/components/pcm-jlpx-modal.js.map +1 -0
  63. package/dist/components/pcm-mnms-modal.js +109 -57
  64. package/dist/components/pcm-mnms-modal.js.map +1 -1
  65. package/dist/components/pcm-video-chat-modal.js +74 -99
  66. package/dist/components/pcm-video-chat-modal.js.map +1 -1
  67. package/dist/components/pcm-zygh-modal.d.ts +11 -0
  68. package/dist/components/pcm-zygh-modal.js +330 -0
  69. package/dist/components/pcm-zygh-modal.js.map +1 -0
  70. package/dist/esm/index-nVjZGfA8.js +189 -0
  71. package/dist/esm/index-nVjZGfA8.js.map +1 -0
  72. package/dist/esm/index.js +1 -1
  73. package/dist/esm/loader.js +1 -1
  74. package/dist/esm/my-component.entry.js +2 -2
  75. package/dist/esm/my-component.entry.js.map +1 -1
  76. package/dist/esm/pcm-agents.js +1 -1
  77. 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
  78. package/dist/esm/pcm-app-chat-modal_7.entry.js +6552 -0
  79. package/dist/esm/pcm-app-chat-modal_7.entry.js.map +1 -0
  80. package/dist/esm/pcm-chat-modal.entry.js +17 -32
  81. package/dist/esm/pcm-chat-modal.entry.js.map +1 -1
  82. package/dist/pcm-agents/index.esm.js +1 -1
  83. package/dist/pcm-agents/my-component.entry.esm.js.map +1 -1
  84. package/dist/pcm-agents/p-55417392.entry.js +2 -0
  85. package/dist/pcm-agents/p-55417392.entry.js.map +1 -0
  86. package/dist/pcm-agents/p-a698b59f.entry.js +2 -0
  87. package/dist/pcm-agents/p-a698b59f.entry.js.map +1 -0
  88. package/dist/pcm-agents/p-f3ca99b4.entry.js +2 -0
  89. package/dist/pcm-agents/p-f3ca99b4.entry.js.map +1 -0
  90. package/dist/pcm-agents/p-nVjZGfA8.js +2 -0
  91. package/dist/pcm-agents/p-nVjZGfA8.js.map +1 -0
  92. package/dist/pcm-agents/pcm-agents.esm.js +1 -1
  93. 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
  94. package/dist/pcm-agents/pcm-chat-modal.entry.esm.js.map +1 -1
  95. package/dist/types/components/pcm-app-chat-modal/pcm-app-chat-modal.d.ts +13 -8
  96. package/dist/types/components/pcm-chat-message/pcm-chat-message.d.ts +5 -0
  97. package/dist/types/components/pcm-chat-modal/pcm-chat-modal.d.ts +8 -8
  98. package/dist/types/components/pcm-hr-chat-modal/pcm-hr-chat-modal.d.ts +6 -12
  99. package/dist/types/components/pcm-jlpx-modal/pcm-jlpx-modal.d.ts +113 -0
  100. package/dist/types/components/pcm-mnms-modal/pcm-mnms-modal.d.ts +19 -20
  101. package/dist/types/components/pcm-video-chat-modal/pcm-video-chat-modal.d.ts +4 -4
  102. package/dist/types/components/pcm-zygh-modal/pcm-zygh-modal.d.ts +117 -0
  103. package/dist/types/components.d.ts +429 -80
  104. package/dist/types/interfaces/chat.d.ts +0 -4
  105. package/dist/types/utils/utils.d.ts +29 -83
  106. package/package.json +61 -60
  107. package/readme.md +307 -307
  108. package/dist/cjs/index-DfIUl99H.js +0 -11413
  109. package/dist/cjs/index-DfIUl99H.js.map +0 -1
  110. package/dist/cjs/pcm-app-chat-modal.pcm-chat-message.pcm-mnms-modal.entry.cjs.js.map +0 -1
  111. package/dist/cjs/pcm-app-chat-modal_3.cjs.entry.js +0 -3734
  112. package/dist/cjs/pcm-app-chat-modal_3.cjs.entry.js.map +0 -1
  113. package/dist/cjs/pcm-hr-chat-modal.cjs.entry.js +0 -1078
  114. package/dist/cjs/pcm-hr-chat-modal.cjs.entry.js.map +0 -1
  115. package/dist/cjs/pcm-hr-chat-modal.entry.cjs.js.map +0 -1
  116. package/dist/cjs/pcm-video-chat-modal.cjs.entry.js +0 -927
  117. package/dist/cjs/pcm-video-chat-modal.cjs.entry.js.map +0 -1
  118. package/dist/cjs/pcm-video-chat-modal.entry.cjs.js.map +0 -1
  119. package/dist/components/p-C4l_DOnx.js.map +0 -1
  120. package/dist/components/p-CgDy4pJp.js +0 -1244
  121. package/dist/components/p-CgDy4pJp.js.map +0 -1
  122. package/dist/components/p-D0s1Q-3O.js.map +0 -1
  123. package/dist/esm/index-B2EtEi7v.js +0 -11409
  124. package/dist/esm/index-B2EtEi7v.js.map +0 -1
  125. package/dist/esm/pcm-app-chat-modal.pcm-chat-message.pcm-mnms-modal.entry.js.map +0 -1
  126. package/dist/esm/pcm-app-chat-modal_3.entry.js +0 -3730
  127. package/dist/esm/pcm-app-chat-modal_3.entry.js.map +0 -1
  128. package/dist/esm/pcm-hr-chat-modal.entry.js +0 -1076
  129. package/dist/esm/pcm-hr-chat-modal.entry.js.map +0 -1
  130. package/dist/esm/pcm-video-chat-modal.entry.js +0 -925
  131. package/dist/esm/pcm-video-chat-modal.entry.js.map +0 -1
  132. package/dist/pcm-agents/p-0ddd5c47.entry.js +0 -2
  133. package/dist/pcm-agents/p-0ddd5c47.entry.js.map +0 -1
  134. package/dist/pcm-agents/p-5f624943.entry.js +0 -2
  135. package/dist/pcm-agents/p-5f624943.entry.js.map +0 -1
  136. package/dist/pcm-agents/p-6c07f155.entry.js +0 -2
  137. package/dist/pcm-agents/p-6c07f155.entry.js.map +0 -1
  138. package/dist/pcm-agents/p-9a1fb6ca.entry.js +0 -2
  139. package/dist/pcm-agents/p-9a1fb6ca.entry.js.map +0 -1
  140. package/dist/pcm-agents/p-B2EtEi7v.js +0 -146
  141. package/dist/pcm-agents/p-B2EtEi7v.js.map +0 -1
  142. package/dist/pcm-agents/p-e21bc169.entry.js +0 -2
  143. package/dist/pcm-agents/p-e21bc169.entry.js.map +0 -1
  144. package/dist/pcm-agents/pcm-app-chat-modal.pcm-chat-message.pcm-mnms-modal.entry.esm.js.map +0 -1
  145. package/dist/pcm-agents/pcm-hr-chat-modal.entry.esm.js.map +0 -1
  146. 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
- '&': '&amp;',
1074
- '<': '&lt;',
1075
- '>': '&gt;',
1076
- '"': '&quot;',
1077
- "'": '&#39;'
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