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