koishi-plugin-chat-patch 1.3.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/index.scss +35 -0
- package/client/index.ts +2 -0
- package/client/vue/chat-logic.ts +148 -3715
- package/client/vue/composables/useChatActions.ts +61 -0
- package/client/vue/composables/useChatData.ts +184 -0
- package/client/vue/composables/useImageCache.ts +140 -0
- package/client/vue/index.vue +399 -327
- package/client/vue/types.ts +71 -0
- package/dist/index.js +5 -27
- package/dist/style.css +1 -1
- package/lib/index.js +68 -36
- package/package.json +14 -5
- package/readme.md +1 -1
- package/src/api-handlers.ts +17 -10
- package/src/config.ts +2 -4
- package/src/index.ts +1 -1
- package/src/message-handler.ts +5 -4
- package/src/utils.ts +48 -4
- package/client/vue/style.css +0 -1997
package/dist/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.message-quote[data-v-d6c77cba]{margin:8px 0;padding:8px 12px;background-color:#bebebe1a;border-left:4px solid rgba(151,152,152,.45);border-radius:6px;font-size:.9em;position:relative}.quote-indicator[data-v-d6c77cba]{font-size:.75em;color:#007bff;font-weight:600;margin-bottom:4px;text-transform:uppercase}.quote-header[data-v-d6c77cba]{display:flex;align-items:center;margin-bottom:4px;gap:6px}.quote-avatar[data-v-d6c77cba]{width:20px;height:20px;border-radius:50%;overflow:hidden;flex-shrink:0}.quote-avatar img[data-v-d6c77cba]{width:100%;height:100%;object-fit:cover}.quote-avatar .avatar-placeholder[data-v-d6c77cba]{width:100%;height:100%;display:flex;align-items:center;justify-content:center;background-color:#6c757d;color:#fff;font-size:10px;font-weight:700}.quote-username[data-v-d6c77cba]{font-weight:600;color:#007bff;font-size:.85em}.quote-time[data-v-d6c77cba]{color:#6c757d;font-size:.75em;margin-left:auto}.quote-content[data-v-d6c77cba]{color:#7276b7;line-height:1.3;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere;-webkit-hyphens:auto;hyphens:auto}.quote-content .message-image[data-v-d6c77cba]{max-width:min(200px,50vw);max-height:100px}.quote-content .message-image-container[data-v-d6c77cba]{margin:2px 0}@media (prefers-color-scheme: dark){.message-quote[data-v-d6c77cba]{background-color:#ffffff14;border-left-color:#0d6efd}.quote-content[data-v-d6c77cba]{color:#adb5bd}.quote-username[data-v-d6c77cba]{color:#0d6efd}}.chat-container[data-v-d6c77cba]{display:flex;height:100vh;background:0 0;margin-left:0;width:100%;overflow:hidden;color:inherit;position:relative}.bot-list[data-v-d6c77cba],.channel-list[data-v-d6c77cba],.message-area[data-v-d6c77cba]{display:flex;flex-direction:column;background:0 0;border-right:1px solid rgba(128,128,128,.2)}.bot-list[data-v-d6c77cba]{width:250px;min-width:250px;max-width:250px;flex-shrink:0}.channel-list[data-v-d6c77cba]{width:300px;min-width:300px;max-width:300px;flex-shrink:0}.message-area[data-v-d6c77cba]{flex:1;border-right:none}.panel-header[data-v-d6c77cba]{padding:16px;border-bottom:1px solid rgba(128,128,128,.2);background:0 0;display:flex;align-items:center;justify-content:space-between}.panel-header h3[data-v-d6c77cba]{margin:0;font-size:16px;font-weight:600;color:currentColor}.back-button[data-v-d6c77cba]{display:none;background:0 0;border:none;color:currentColor;font-size:18px;cursor:pointer;padding:4px 8px;border-radius:4px;transition:background-color .2s}.back-button[data-v-d6c77cba]:hover{background:#8080801a}.bot-items[data-v-d6c77cba],.channel-items[data-v-d6c77cba]{flex:1;overflow-y:auto;overflow-x:hidden;max-height:calc(100vh - 140px);-webkit-overflow-scrolling:touch;scrollbar-width:thin;scrollbar-color:rgba(144,147,153,.4) rgba(0,0,0,.05);scroll-behavior:smooth;overscroll-behavior:contain;padding-bottom:8px}.bot-item[data-v-d6c77cba],.channel-item[data-v-d6c77cba]{display:flex;align-items:center;padding:12px 16px;cursor:pointer;border-bottom:1px solid rgba(128,128,128,.1);transition:background-color .2s}.bot-item[data-v-d6c77cba]:hover,.channel-item[data-v-d6c77cba]:hover{background:#8080801a}.bot-item.active[data-v-d6c77cba],.channel-item.active[data-v-d6c77cba]{background:#2196f31a;border-right:3px solid #2196f3}.bot-avatar[data-v-d6c77cba]{width:40px;height:40px;margin-right:12px;border-radius:50%;overflow:hidden;display:flex;align-items:center;justify-content:center;background:#e0e0e0}.bot-avatar img[data-v-d6c77cba]{width:100%;height:100%;object-fit:cover}.avatar-placeholder[data-v-d6c77cba]{font-size:16px;font-weight:700;color:currentColor;opacity:.7}.bot-info[data-v-d6c77cba]{flex:1}.bot-name[data-v-d6c77cba]{font-weight:500;color:currentColor;margin-bottom:2px}.bot-platform[data-v-d6c77cba]{font-size:12px;color:currentColor;opacity:.7}.bot-status[data-v-d6c77cba]{width:8px;height:8px;border-radius:50%;margin-left:8px}.bot-status.online[data-v-d6c77cba]{background:#4caf50}.bot-status.offline[data-v-d6c77cba]{background:#f44336}.channel-info[data-v-d6c77cba]{flex:1}.channel-name[data-v-d6c77cba]{font-weight:500;color:currentColor;margin-bottom:2px}.channel-type[data-v-d6c77cba]{font-size:12px;color:currentColor;opacity:.7}.channel-message-count[data-v-d6c77cba]{background:#2196f3;color:#fff;font-size:12px;padding:2px 6px;border-radius:10px;min-width:18px;text-align:center;position:relative;transition:all .2s ease}.channel-message-count.draggable-bubble[data-v-d6c77cba]{cursor:grab;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.channel-message-count.draggable-bubble[data-v-d6c77cba]:hover{background:#1976d2;transform:scale(1.1);box-shadow:0 2px 8px #2196f34d}.channel-message-count.draggable-bubble.dragging[data-v-d6c77cba],.channel-message-count.draggable-bubble[data-v-d6c77cba]:active{cursor:grabbing;background:#1565c0;box-shadow:0 4px 12px #2196f366}.dragging-clone[data-v-d6c77cba]{position:fixed;z-index:1000;pointer-events:none;transition:none;box-sizing:border-box;display:flex;align-items:center;justify-content:center;background:#2196f3;color:#fff;font-size:12px;padding:2px 6px;border-radius:10px;min-width:18px;text-align:center;box-shadow:0 4px 12px #2196f366}.dragging-clone.will-delete[data-v-d6c77cba]{background:#f44336!important;animation:shake-d6c77cba .3s ease-in-out infinite}@keyframes pulseThreshold-d6c77cba{0%,to{opacity:.4;transform:scale(.9);border-color:#f4433699;box-shadow:0 0 15px #f4433633}50%{opacity:.8;transform:scale(1.1);border-color:#f44336;box-shadow:0 0 25px #f4433666}}@keyframes menuSlideIn-d6c77cba{0%{transform:translateY(8px);opacity:0}to{transform:translateY(0);opacity:1}}.message-content[data-v-d6c77cba]{display:flex;flex-direction:column;height:calc(100vh - 60px);overflow:hidden;position:relative}.message-history[data-v-d6c77cba]{flex:1;overflow-y:auto;padding:16px 16px 8px;max-height:calc(100vh - 200px)}.message-item[data-v-d6c77cba]{display:flex;margin-bottom:16px;padding:8px;border-radius:8px;transition:background-color .3s ease}.message-item[data-v-d6c77cba]:hover{background-color:#8080801a}.message-item.new-message[data-v-d6c77cba]{background-color:#4caf5033;animation:highlight-d6c77cba 2s ease-out}.message-item.bot-message[data-v-d6c77cba]{flex-direction:row-reverse;justify-content:flex-start}.message-item.bot-message .message-avatar[data-v-d6c77cba]{margin-right:0;margin-left:12px}.message-item.bot-message .message-content-wrapper[data-v-d6c77cba]{display:flex;flex-direction:column;align-items:flex-end}.message-item.bot-message .message-header[data-v-d6c77cba]{display:flex;align-items:center;justify-content:flex-end;margin-bottom:4px}.message-item.bot-message .message-username[data-v-d6c77cba]{margin-right:0;margin-left:8px;order:2}.message-item.bot-message .message-time[data-v-d6c77cba]{order:1}.message-item.bot-message .message-text[data-v-d6c77cba]{background-color:#2196f31a;padding:8px 12px;border-radius:12px 12px 4px;display:inline-block;max-width:80%;text-align:left;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere}@keyframes highlight-d6c77cba{0%{background-color:#4caf504d}to{background-color:transparent}}.message-avatar[data-v-d6c77cba]{width:32px;height:32px;margin-right:12px;border-radius:50%;overflow:hidden;display:flex;align-items:center;justify-content:center;background:#e0e0e0;flex-shrink:0}.message-avatar img[data-v-d6c77cba]{width:100%;height:100%;object-fit:cover}.message-avatar .avatar-placeholder[data-v-d6c77cba]{font-size:12px;font-weight:700;color:currentColor;opacity:.7}.message-content-wrapper[data-v-d6c77cba]{flex:1}.message-header[data-v-d6c77cba]{display:flex;align-items:center;margin-bottom:4px}.message-username[data-v-d6c77cba]{font-weight:500;color:currentColor;margin-right:8px}.message-time[data-v-d6c77cba]{font-size:12px;color:currentColor;opacity:.5}.message-text[data-v-d6c77cba]{color:currentColor;line-height:1.4;word-wrap:break-word;word-break:break-all;white-space:pre-wrap;max-width:100%;overflow-wrap:anywhere;-webkit-hyphens:auto;hyphens:auto}.message-item:not(.bot-message) .message-text[data-v-d6c77cba]{background-color:#8080801a;padding:8px 12px;border-radius:12px 12px 12px 4px;display:inline-block;max-width:80%;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere}.message-input[data-v-d6c77cba]{display:flex;flex-direction:column;padding:12px 16px 16px;border-top:1px solid rgba(128,128,128,.2);background:0 0;flex-shrink:0;position:sticky;bottom:0;margin-top:-10px}.input-row[data-v-d6c77cba]{display:flex;gap:8px;align-items:center}.floating-scroll-button[data-v-d6c77cba]{position:absolute;right:24px;bottom:18%;width:48px;height:48px;background:#2196f3;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 12px #2196f34d;color:#fff;transition:all .3s ease;z-index:100;border:2px solid rgba(255,255,255,.2)}.floating-scroll-button[data-v-d6c77cba]:hover{background:#1976d2;transform:scale(1.1);box-shadow:0 6px 16px #2196f366}.floating-scroll-button[data-v-d6c77cba]:active{transform:scale(.95)}.input-row button[data-v-d6c77cba]{padding:8px 16px;background:#2196f3;color:#fff;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s}.message-image-container[data-v-d6c77cba]{margin:8px 0;max-width:100%;display:block}.message-image[data-v-d6c77cba]{border-radius:8px;object-fit:contain;display:block;cursor:pointer;transition:transform .2s ease;max-width:min(400px,66.67vw);max-height:200px;width:auto;height:auto;image-rendering:auto;-webkit-image-rendering:auto;-moz-image-rendering:auto;-o-image-rendering:auto}.message-image[src*=".gif"][data-v-d6c77cba],.message-image[src*="image/gif"][data-v-d6c77cba]{image-rendering:auto!important;-webkit-image-rendering:auto!important;animation-play-state:running!important}.message-image-loading[data-v-d6c77cba]{display:inline-block;padding:8px 12px;background:#8080801a;border-radius:4px;color:currentColor;opacity:.7;font-size:12px}.message-image-error[data-v-d6c77cba]{display:inline-block;padding:8px 12px;background:#f443361a;border:1px solid rgba(244,67,54,.3);border-radius:4px;color:#f44336;font-size:12px;max-width:300px;word-break:break-word}.message-paragraph[data-v-d6c77cba]{display:block;margin-bottom:4px}.message-paragraph[data-v-d6c77cba]:last-child{margin-bottom:0}.forward-message-container[data-v-d6c77cba]{border:1px solid #fff;border-radius:8px;margin:4px 0;overflow:hidden;background:#f9f9f900}.forward-message-preview[data-v-d6c77cba]{padding:12px;cursor:pointer;transition:background-color .2s}.forward-message-preview[data-v-d6c77cba]:hover{background:#f0f0f0}.forward-message-title[data-v-d6c77cba]{font-weight:600;color:#333;margin-bottom:8px;font-size:14px}.forward-message-preview-item[data-v-d6c77cba]{color:#666;font-size:13px;margin:2px 0;line-height:1.4;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere}.forward-message-footer[data-v-d6c77cba]{display:flex;justify-content:space-between;align-items:center;margin-top:8px;padding-top:8px;border-top:1px solid #e8e8e8}.forward-message-count[data-v-d6c77cba]{color:#2196f3;font-size:12px}.forward-message-toggle[data-v-d6c77cba]{color:#666;font-size:12px}.forward-message-expanded[data-v-d6c77cba]{border-top:1px solid #e0e0e0;background:#fff;max-height:400px;overflow-y:auto}.forwarded-message-item[data-v-d6c77cba]{padding:12px;border-bottom:1px solid #f0f0f0}.forwarded-message-item[data-v-d6c77cba]:last-child{border-bottom:none}.forwarded-message-header[data-v-d6c77cba]{display:flex;align-items:center;margin-bottom:6px}.forwarded-message-nickname[data-v-d6c77cba]{font-weight:600;color:#333;font-size:13px}.forwarded-message-userid[data-v-d6c77cba]{color:#999;font-size:11px;margin-left:6px}.forwarded-message-content[data-v-d6c77cba]{color:#555;font-size:13px;line-height:1.4;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere}.message-at[data-v-d6c77cba]{background:#2196f31a;color:#2196f3;padding:2px 4px;border-radius:3px;font-weight:500;cursor:pointer}.message-unknown[data-v-d6c77cba]{background:#ff98001a;color:#ff9800;padding:2px 4px;border-radius:3px;font-style:italic;cursor:help}.input-row input[data-v-d6c77cba]{flex:1;min-width:0;padding:8px 12px;border:1px solid rgba(128,128,128,.3);border-radius:4px;outline:0;background:0 0;color:currentColor}.input-row input[data-v-d6c77cba]:focus{border-color:#2196f3;box-shadow:0 0 0 2px #2196f333}.input-row input[data-v-d6c77cba]::placeholder{color:currentColor;opacity:.5}.input-row button[data-v-d6c77cba]{padding:8px 16px;background:#2196f3;color:#fff;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s;flex-shrink:0;white-space:nowrap}.input-row button[data-v-d6c77cba]:hover:not(:disabled){background:#1976d2}.input-row button[data-v-d6c77cba]:disabled{background:#8080804d;cursor:not-allowed}.input-row button.is-sending[data-v-d6c77cba]{background:#ffc107!important;color:#333!important;cursor:default!important}.message-input .input-row button.is-sending[data-v-d6c77cba]{background:#ffc107!important;color:#333!important;cursor:default!important;border-color:#ffc107!important}.empty-state[data-v-d6c77cba]{display:flex;align-items:center;justify-content:center;height:100%;color:currentColor;opacity:.5;font-size:14px}.message-text-content[data-v-d6c77cba]{display:inline;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere;-webkit-hyphens:auto;hyphens:auto}.message-image-container[data-v-d6c77cba]{display:block;margin:4px 0}.message-image[data-v-d6c77cba]{border-radius:8px;margin:4px 0;display:block;cursor:pointer;transition:transform .2s ease;object-fit:contain;background:#8080801a}.message-image[data-v-d6c77cba]:hover{transform:scale(1.02)}.message-image-loading[data-v-d6c77cba]{height:60px;width:200px;border-radius:8px;margin:4px 0;display:flex;align-items:center;justify-content:center;background:#8080801a;color:currentColor;opacity:.7;font-size:14px;border:1px dashed rgba(128,128,128,.3)}.message-image-error[data-v-d6c77cba]{min-height:60px;width:200px;border-radius:8px;margin:4px 0;display:flex;flex-direction:column;align-items:center;justify-content:center;background:#8080801a;color:currentColor;opacity:.7;font-size:14px;border:1px dashed rgba(128,128,128,.3);padding:8px;text-align:center}.message-at[data-v-d6c77cba]{background:#2196f31a;color:#2196f3;padding:2px 6px;border-radius:4px;font-weight:500;cursor:pointer;display:inline-block;margin:0 2px}.message-at[data-v-d6c77cba]:hover{background:#2196f333}.message-unknown[data-v-d6c77cba]{background:#ff98001a;color:#ff9800;padding:2px 6px;border-radius:4px;font-size:12px;display:inline-block;margin:0 2px;cursor:help}@keyframes pulse-d6c77cba{0%,to{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-50%,-50%) scale(1.1)}}.channel-message-count.will-delete[data-v-d6c77cba]{background:#f44336!important;animation:shake-d6c77cba .3s ease-in-out infinite}@keyframes shake-d6c77cba{0%,to{transform:translate(0)}25%{transform:translate(-2px)}75%{transform:translate(2px)}}body.dragging-bubble-global[data-v-d6c77cba]{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;cursor:grabbing}.drag-threshold-circle[data-v-d6c77cba]{position:fixed;pointer-events:none;border:3px dashed rgba(244,67,54,.8);border-radius:50%;z-index:999;background:#f443361a;box-shadow:0 0 20px #f443364d;animation:pulseThreshold-d6c77cba 1.5s ease-in-out infinite}.context-menu[data-v-d6c77cba]{position:fixed;background:#fffffffa;border:none;border-radius:16px;box-shadow:0 8px 32px #0000001f,0 2px 8px #00000014;backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);overflow:hidden;z-index:10000;min-width:180px}.context-menu-item[data-v-d6c77cba]{display:flex;align-items:center;gap:12px;padding:14px 18px;background:0 0;border:none;color:#333;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);width:100%;text-align:left;position:relative;border-bottom:1px solid rgba(0,0,0,.08);min-width:140px}.context-menu-item[data-v-d6c77cba]:last-child{border-bottom:none}.context-menu-item[data-v-d6c77cba]:before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:linear-gradient(135deg,#667eea 0,#764ba2);transform:scaleY(0);transition:transform .2s cubic-bezier(.4,0,.2,1)}.context-menu-item[data-v-d6c77cba]:hover{background:linear-gradient(90deg,#667eea14 0,#764ba214);color:#667eea;transform:translate(4px)}.context-menu-item[data-v-d6c77cba]:hover:before{transform:scaleY(1)}.context-menu-item.danger[data-v-d6c77cba]{color:#d32f2f}.context-menu-item.danger[data-v-d6c77cba]:hover{background:linear-gradient(90deg,#f4433614 0,#d32f2f14);color:#d32f2f}.bot-item.pinned[data-v-d6c77cba],.channel-item.pinned[data-v-d6c77cba]{background:#ffc1071a;border-left:3px solid #ffc107}.bot-item.pinned[data-v-d6c77cba]:before,.channel-item.pinned[data-v-d6c77cba]:before{content:"📌";position:absolute;left:4px;top:50%;transform:translateY(-50%);font-size:12px;opacity:.7}.bot-item.pinned[data-v-d6c77cba],.channel-item.pinned[data-v-d6c77cba]{position:relative;padding-left:24px}@media (prefers-color-scheme: dark){.context-menu[data-v-d6c77cba]{background:#1e1e1efa;box-shadow:0 8px 32px #0000004d,0 2px 8px #0003}.context-menu-item[data-v-d6c77cba]{color:#fff;border-bottom-color:#ffffff1a}.context-menu-item[data-v-d6c77cba]:before{background:linear-gradient(135deg,#4facfe 0,#00f2fe)}.context-menu-item[data-v-d6c77cba]:hover{background:linear-gradient(90deg,#4facfe1f 0,#00f2fe1f);color:#4facfe}.context-menu-item.danger[data-v-d6c77cba]:hover{background:linear-gradient(90deg,#f443361f 0,#ff8a801f);color:#ff8a80}.bot-item.pinned[data-v-d6c77cba],.channel-item.pinned[data-v-d6c77cba]{background:#ffc10726}}.message-share-card[data-v-d6c77cba]{display:inline-block;width:75px;margin:8px 0;border:1px solid rgba(128,128,128,.2);border-radius:12px;background:#ffffff0d;transition:all .3s ease;cursor:pointer;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.message-share-card[data-v-d6c77cba]:hover{transform:translateY(-2px);box-shadow:0 8px 24px #00000026;border-color:#2196f34d}.share-card-preview[data-v-d6c77cba]{width:100%;position:relative;display:flex;align-items:center;justify-content:center;padding:4px;border-radius:12px 12px 0 0}.share-card-image[data-v-d6c77cba]{max-width:calc(100% - 8px);width:auto;height:auto;object-fit:contain;transition:transform .3s ease;display:block;border-radius:4px}.message-share-card:hover .share-card-image[data-v-d6c77cba]{transform:scale(1.05)}.share-card-content[data-v-d6c77cba]{padding:8px}.share-card-header[data-v-d6c77cba]{display:flex;align-items:center;margin-bottom:12px;gap:12px}.share-card-icon[data-v-d6c77cba]{width:32px;height:32px;border-radius:8px;object-fit:cover;flex-shrink:0}.share-card-app-info[data-v-d6c77cba]{flex:1;min-width:0}.share-card-app-name[data-v-d6c77cba]{font-size:12px;color:currentColor;opacity:.7;margin-bottom:2px;font-weight:500}.share-card-title[data-v-d6c77cba]{font-size:10px;font-weight:600;color:currentColor;line-height:1.2;word-wrap:break-word;display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden}.share-card-desc[data-v-d6c77cba]{font-size:14px;color:currentColor;opacity:.8;line-height:1.4;margin-bottom:12px;display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;word-wrap:break-word}.share-card-link-hint[data-v-d6c77cba]{font-size:12px;color:#2196f3;font-weight:500;display:flex;align-items:center;gap:4px}.share-card-link-hint[data-v-d6c77cba]:after{content:"→";transition:transform .2s ease}.message-share-card:hover .share-card-link-hint[data-v-d6c77cba]:after{transform:translate(2px)}.message-json-error[data-v-d6c77cba],.message-json-raw[data-v-d6c77cba]{display:inline-block;margin:8px 0;max-width:400px}.json-error-text[data-v-d6c77cba]{color:#f44336;font-size:14px;font-weight:500}.json-label[data-v-d6c77cba]{color:currentColor;opacity:.7;font-size:12px;font-weight:500;margin-bottom:8px}.json-raw-data[data-v-d6c77cba]{margin-top:8px}.json-raw-data summary[data-v-d6c77cba]{cursor:pointer;font-size:12px;color:#2196f3;font-weight:500;padding:4px 8px;border-radius:4px;background:#2196f31a;display:inline-block;transition:background-color .2s ease}.json-raw-data summary[data-v-d6c77cba]:hover{background:#2196f333}.json-raw-content[data-v-d6c77cba]{margin-top:8px;padding:12px;background:#8080801a;border-radius:8px;font-family:Courier New,monospace;font-size:12px;color:currentColor;opacity:.8;white-space:pre-wrap;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere;max-height:200px;overflow-y:auto;border:1px solid rgba(128,128,128,.2)}@media (prefers-color-scheme: dark){.message-share-card[data-v-d6c77cba]{background:#ffffff14;border-color:#ffffff26}.message-share-card[data-v-d6c77cba]:hover{border-color:#2196f380;box-shadow:0 8px 24px #0000004d}.json-raw-content[data-v-d6c77cba]{background:#ffffff0d;border-color:#ffffff1a}}@media (max-width: 768px){.chat-container[data-v-d6c77cba]{position:relative;height:100vh;height:100dvh}.bot-list[data-v-d6c77cba],.channel-list[data-v-d6c77cba],.message-area[data-v-d6c77cba]{position:absolute;top:0;left:0;width:100%;height:100%;transition:transform .3s ease;z-index:1}.bot-list[data-v-d6c77cba]{transform:translate(0);z-index:3}.channel-list[data-v-d6c77cba]{transform:translate(100%);z-index:2}.message-area[data-v-d6c77cba]{transform:translate(200%);z-index:1}.chat-container.show-channels .bot-list[data-v-d6c77cba]{transform:translate(-100%)}.chat-container.show-channels .channel-list[data-v-d6c77cba]{transform:translate(0);z-index:3}.chat-container.show-channels .message-area[data-v-d6c77cba]{transform:translate(100%)}.chat-container.show-messages .bot-list[data-v-d6c77cba]{transform:translate(-200%)}.chat-container.show-messages .channel-list[data-v-d6c77cba]{transform:translate(-100%)}.chat-container.show-messages .message-area[data-v-d6c77cba]{transform:translate(0);z-index:3}.back-button[data-v-d6c77cba]{display:none!important}.panel-header[data-v-d6c77cba]{flex-direction:row-reverse;position:sticky;top:0;z-index:5;background:inherit}.panel-header h3[data-v-d6c77cba]{text-align:right}.floating-scroll-button[data-v-d6c77cba]{right:16px;bottom:calc(90px + env(safe-area-inset-bottom,0px));width:40px;height:40px;transition:bottom .3s ease}.message-input[data-v-d6c77cba]{padding:8px 12px calc(12px + env(safe-area-inset-bottom,0px)) 12px;position:fixed;bottom:20px;left:0;right:0;background:inherit;border-top:1px solid rgba(128,128,128,.2);z-index:10;min-height:calc(60px + env(safe-area-inset-bottom,0px));transition:transform .3s ease;transform:translateZ(0)}.input-row input[data-v-d6c77cba]{font-size:16px;padding:10px 12px}.input-row button[data-v-d6c77cba]{padding:10px 16px;font-size:16px}.message-history[data-v-d6c77cba]{padding:12px;max-height:calc(100vh - 140px - env(safe-area-inset-bottom,0px));max-height:calc(100dvh - 140px - env(safe-area-inset-bottom,0px));padding-bottom:calc(80px + env(safe-area-inset-bottom,0px));overflow-y:auto;-webkit-overflow-scrolling:touch;transition:max-height .3s ease}.message-item[data-v-d6c77cba]{margin-bottom:12px;padding:6px}.message-text[data-v-d6c77cba]{max-width:90%!important}.message-avatar[data-v-d6c77cba]{width:28px;height:28px;margin-right:8px}.message-item.bot-message .message-avatar[data-v-d6c77cba]{margin-left:8px}.message-image[data-v-d6c77cba]{max-width:min(280px,66.67vw);max-height:160px}.quote-content .message-image[data-v-d6c77cba]{max-width:min(150px,50vw);max-height:80px}.context-menu[data-v-d6c77cba]{min-width:160px;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.context-menu-item[data-v-d6c77cba]{padding:10px 12px;font-size:13px;min-width:100px}.bot-item[data-v-d6c77cba],.channel-item[data-v-d6c77cba]{padding:14px 16px}.bot-avatar[data-v-d6c77cba]{width:36px;height:36px;margin-right:10px}.bot-name[data-v-d6c77cba],.channel-name[data-v-d6c77cba]{font-size:15px}.bot-platform[data-v-d6c77cba],.channel-type[data-v-d6c77cba]{font-size:13px}.bot-items[data-v-d6c77cba],.channel-items[data-v-d6c77cba]{flex:1;overflow-y:auto;overflow-x:hidden;max-height:calc(100vh - 140px);max-height:calc(100dvh - 140px);-webkit-overflow-scrolling:touch;scrollbar-width:thin;scrollbar-color:rgba(144,147,153,.4) rgba(0,0,0,.05);scroll-behavior:smooth;overscroll-behavior:contain;padding-bottom:8px}}@media (min-width: 769px) and (max-width: 1024px){.bot-list[data-v-d6c77cba]{width:220px;min-width:220px;max-width:220px}.channel-list[data-v-d6c77cba]{width:280px;min-width:280px;max-width:280px}.message-image[data-v-d6c77cba]{max-width:min(350px,66.67vw);max-height:180px}}.chat-container.swiping[data-v-d6c77cba]{overflow:hidden}.bot-list.swiping[data-v-d6c77cba],.channel-list.swiping[data-v-d6c77cba],.message-area.swiping[data-v-d6c77cba]{transition:none!important}.swipe-indicator[data-v-d6c77cba]{position:fixed;top:50%;left:20px;transform:translateY(-50%);background:#2196f3cc;color:#fff;padding:8px 12px;border-radius:20px;font-size:14px;z-index:1000;opacity:0;transition:opacity .2s ease;pointer-events:none;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.swipe-indicator.show[data-v-d6c77cba]{opacity:1}.message-area .message-content[data-v-d6c77cba]{padding-bottom:80px}.image-preview-container[data-v-d6c77cba]{display:flex;flex-wrap:wrap;gap:8px;padding:8px 0;margin-bottom:8px;border-bottom:1px solid rgba(128,128,128,.1)}.image-preview-item[data-v-d6c77cba]{position:relative;width:60px;height:60px;border-radius:8px;overflow:hidden;border:2px solid rgba(33,150,243,.3);background:#8080801a}.preview-image[data-v-d6c77cba]{width:100%;height:100%;object-fit:cover;display:block}.remove-image-btn[data-v-d6c77cba]{position:absolute;top:-6px;right:-6px;width:20px;height:20px;border-radius:50%;background:#f44336;color:#fff;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:12px;transition:all .2s ease;box-shadow:0 2px 4px #0003}.remove-image-btn[data-v-d6c77cba]:hover{background:#d32f2f;transform:scale(1.1)}.input-actions[data-v-d6c77cba]{position:relative;margin-right:8px}.add-button[data-v-d6c77cba]{width:36px;height:36px;border-radius:12px;background:linear-gradient(135deg,#667eea 0,#764ba2);border:none;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .3s cubic-bezier(.4,0,.2,1);flex-shrink:0;box-shadow:0 2px 8px #667eea4d;position:relative;overflow:hidden}.add-button[data-v-d6c77cba]:before{content:"";position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(135deg,#fff3 0,#ffffff1a);border-radius:12px;opacity:0;transition:opacity .3s ease}.add-button[data-v-d6c77cba]:hover{transform:translateY(-2px) scale(1.05);box-shadow:0 4px 16px #667eea66}.add-button[data-v-d6c77cba]:hover:before{opacity:1}.add-button.active[data-v-d6c77cba]{background:linear-gradient(135deg,#f093fb 0,#f5576c);transform:translateY(-2px) scale(1.05) rotate(45deg);box-shadow:0 4px 16px #f5576c66}.add-button[data-v-d6c77cba]:active{transform:translateY(0) scale(.98);transition:all .1s ease}.action-menu[data-v-d6c77cba]{position:absolute;bottom:100%;left:0;margin-bottom:12px;background:#fffffffa;border:none;border-radius:16px;box-shadow:0 8px 32px #0000001f,0 2px 8px #00000014;backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);overflow:hidden;z-index:1000;min-width:140px;transform:translateY(8px);opacity:0;animation:menuSlideIn-d6c77cba .3s cubic-bezier(.4,0,.2,1) forwards}.action-menu-item[data-v-d6c77cba]{display:flex;align-items:center;gap:12px;padding:14px 18px;background:0 0;border:none;color:#333;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);width:100%;text-align:left;position:relative}.action-menu-item[data-v-d6c77cba]:before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:linear-gradient(135deg,#667eea 0,#764ba2);transform:scaleY(0);transition:transform .2s cubic-bezier(.4,0,.2,1)}.action-menu-item[data-v-d6c77cba]:hover{background:linear-gradient(90deg,#667eea14 0,#764ba214);color:#667eea;transform:translate(4px)}.action-menu-item[data-v-d6c77cba]:hover:before{transform:scaleY(1)}.action-menu-item svg[data-v-d6c77cba]{flex-shrink:0}@media (prefers-color-scheme: dark){.action-menu[data-v-d6c77cba]{background:#1e1e1efa;box-shadow:0 8px 32px #0000004d,0 2px 8px #0003}.action-menu-item[data-v-d6c77cba]{color:#fff}.action-menu-item[data-v-d6c77cba]:before{background:linear-gradient(135deg,#4facfe 0,#00f2fe)}.action-menu-item[data-v-d6c77cba]:hover{background:linear-gradient(90deg,#4facfe1f 0,#00f2fe1f);color:#4facfe}.add-button[data-v-d6c77cba]{background:linear-gradient(135deg,#4facfe 0,#00f2fe);box-shadow:0 2px 8px #4facfe4d}.add-button[data-v-d6c77cba]:hover{box-shadow:0 4px 16px #4facfe66}.add-button.active[data-v-d6c77cba]{background:linear-gradient(135deg,#fa709a 0,#fee140);box-shadow:0 4px 16px #fa709a66}.image-preview-item[data-v-d6c77cba]{border-color:#64b5f680;background:#ffffff0d}}@media (max-width: 768px){.image-preview-container[data-v-d6c77cba]{padding:6px 0;margin-bottom:6px}.image-preview-item[data-v-d6c77cba]{width:50px;height:50px}.add-button[data-v-d6c77cba]{width:32px;height:32px;border-radius:10px}.action-menu[data-v-d6c77cba]{min-width:100px}.action-menu-item[data-v-d6c77cba]{padding:10px 12px;font-size:13px}.remove-image-btn[data-v-d6c77cba]{width:18px;height:18px;top:-5px;right:-5px}}.message-history[data-v-d6c77cba]::-webkit-scrollbar{width:6px}.message-history[data-v-d6c77cba]::-webkit-scrollbar-track{background:0 0}.message-history[data-v-d6c77cba]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.message-history[data-v-d6c77cba]::-webkit-scrollbar-thumb:hover{background-color:#90939980}.bot-items[data-v-d6c77cba]::-webkit-scrollbar,.channel-items[data-v-d6c77cba]::-webkit-scrollbar{width:8px}.bot-items[data-v-d6c77cba]::-webkit-scrollbar-track,.channel-items[data-v-d6c77cba]::-webkit-scrollbar-track{background:#0000000d;border-radius:4px}.bot-items[data-v-d6c77cba]::-webkit-scrollbar-thumb,.channel-items[data-v-d6c77cba]::-webkit-scrollbar-thumb{background-color:#90939966;border-radius:4px;transition:all .3s ease;border:1px solid rgba(255,255,255,.1);background-clip:padding-box}.bot-items[data-v-d6c77cba]::-webkit-scrollbar-thumb:hover,.channel-items[data-v-d6c77cba]::-webkit-scrollbar-thumb:hover{background-color:#90939999;transform:scaleX(1.2)}.bot-items[data-v-d6c77cba]::-webkit-scrollbar-thumb:active,.channel-items[data-v-d6c77cba]::-webkit-scrollbar-thumb:active{background-color:#909399cc}.forward-message-expanded[data-v-d6c77cba]::-webkit-scrollbar{width:6px}.forward-message-expanded[data-v-d6c77cba]::-webkit-scrollbar-track{background:0 0}.forward-message-expanded[data-v-d6c77cba]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.forward-message-expanded[data-v-d6c77cba]::-webkit-scrollbar-thumb:hover{background-color:#90939980}.json-raw-content[data-v-d6c77cba]::-webkit-scrollbar{width:6px}.json-raw-content[data-v-d6c77cba]::-webkit-scrollbar-track{background:0 0}.json-raw-content[data-v-d6c77cba]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.json-raw-content[data-v-d6c77cba]::-webkit-scrollbar-thumb:hover{background-color:#90939980}@media (prefers-color-scheme: dark){.bot-items[data-v-d6c77cba]::-webkit-scrollbar-track,.channel-items[data-v-d6c77cba]::-webkit-scrollbar-track{background:#ffffff0d}.bot-items[data-v-d6c77cba]::-webkit-scrollbar-thumb,.channel-items[data-v-d6c77cba]::-webkit-scrollbar-thumb,.forward-message-expanded[data-v-d6c77cba]::-webkit-scrollbar-thumb,.json-raw-content[data-v-d6c77cba]::-webkit-scrollbar-thumb,.message-history[data-v-d6c77cba]::-webkit-scrollbar-thumb{background-color:#ffffff4d;border-color:#0000001a}.bot-items[data-v-d6c77cba]::-webkit-scrollbar-thumb:hover,.channel-items[data-v-d6c77cba]::-webkit-scrollbar-thumb:hover,.forward-message-expanded[data-v-d6c77cba]::-webkit-scrollbar-thumb:hover,.json-raw-content[data-v-d6c77cba]::-webkit-scrollbar-thumb:hover,.message-history[data-v-d6c77cba]::-webkit-scrollbar-thumb:hover{background-color:#ffffff80}.bot-items[data-v-d6c77cba]::-webkit-scrollbar-thumb:active,.channel-items[data-v-d6c77cba]::-webkit-scrollbar-thumb:active{background-color:#ffffffb3}}.loading-more-indicator[data-v-d6c77cba]{display:flex;align-items:center;justify-content:center;padding:12px;color:#6c757d;font-size:14px}.loading-spinner[data-v-d6c77cba]{width:20px;height:20px;border:2px solid #f3f3f3;border-top:2px solid #2196f3;border-radius:50%;animation:spin-d6c77cba 1s linear infinite;margin-right:8px}@keyframes spin-d6c77cba{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.layout-iframe{width:100%;height:100%;border:none}
|
|
1
|
+
.scrollbar-hide::-webkit-scrollbar{display:none}.chat-patch-wrapper .el-textarea__inner{border-radius:12px;padding:10px 12px;resize:none}.chat-patch-wrapper .el-button--primary{border-radius:10px}.forward-dialog .el-dialog__body{padding:0}.image-viewer-dialog .el-dialog__header{padding-bottom:10px}.fixed{position:fixed!important}.absolute{position:absolute!important}.relative{position:relative!important}.inset-0{top:0!important;right:0!important;bottom:0!important;left:0!important}.-right-2{right:-.5rem!important}.-top-2{top:-.5rem!important}.z-10{z-index:10!important}.z-50{z-index:50!important}.z-\[2000\]{z-index:2000!important}.z-\[60\]{z-index:60!important}.mx-0\.5{margin-left:.125rem!important;margin-right:.125rem!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-1\.5{margin-top:.375rem!important;margin-bottom:.375rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.mb-1{margin-bottom:.25rem!important}.mb-1\.5{margin-bottom:.375rem!important}.mb-2{margin-bottom:.5rem!important}.mb-2\.5{margin-bottom:.625rem!important}.mb-3{margin-bottom:.75rem!important}.mb-6{margin-bottom:1.5rem!important}.ml-2{margin-left:.5rem!important}.ml-3{margin-left:.75rem!important}.mr-1{margin-right:.25rem!important}.mr-3{margin-right:.75rem!important}.mt-2{margin-top:.5rem!important}.block{display:block!important}.flex{display:flex!important}.\!h-10,.h-10{height:2.5rem!important}.h-14{height:3.5rem!important}.h-2\.5{height:.625rem!important}.h-20{height:5rem!important}.h-full{height:100%!important}.max-h-\[100px\]{max-height:100px!important}.max-h-\[400px\]{max-height:400px!important}.max-h-\[70vh\]{max-height:70vh!important}.max-h-full{max-height:100%!important}.min-h-\[1em\]{min-height:1em!important}.min-h-full{min-height:100%!important}.\!w-10{width:2.5rem!important}.w-2\.5{width:.625rem!important}.w-20{width:5rem!important}.w-full{width:100%!important}.min-w-\[140px\]{min-width:140px!important}.min-w-\[20px\]{min-width:20px!important}.max-w-\[100px\]{max-width:100px!important}.max-w-\[300px\]{max-width:300px!important}.max-w-\[85\%\]{max-width:85%!important}.max-w-full{max-width:100%!important}.flex-1{flex:1 1 0%!important}.flex-shrink-0{flex-shrink:0!important}.cursor-default{cursor:default!important}.cursor-pointer{cursor:pointer!important}.resize{resize:both!important}.flex-row{flex-direction:row!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-col{flex-direction:column!important}.items-start{align-items:flex-start!important}.items-end{align-items:flex-end!important}.items-center{align-items:center!important}.justify-center{justify-content:center!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:.75rem!important}.gap-4{gap:1rem!important}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0 !important;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)))!important;margin-bottom:calc(.375rem * var(--tw-space-y-reverse))!important}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0 !important;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)))!important;margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))!important}.overflow-hidden{overflow:hidden!important}.overflow-x-auto{overflow-x:auto!important}.truncate{overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important}.break-all{word-break:break-all!important}.rounded{border-radius:.25rem!important}.rounded-2xl{border-radius:1rem!important}.rounded-full{border-radius:9999px!important}.rounded-lg{border-radius:.5rem!important}.rounded-xl{border-radius:.75rem!important}.rounded-tl-none{border-top-left-radius:0!important}.rounded-tr-none{border-top-right-radius:0!important}.border{border-width:1px!important}.border-2{border-width:2px!important}.border-b{border-bottom-width:1px!important}.border-l-4{border-left-width:4px!important}.border-r{border-right-width:1px!important}.border-t{border-top-width:1px!important}.\!border-none{border-style:none!important}.\!border-\[var\(--k-color-primary\)\]{border-color:var(--k-color-primary)!important}.border-\[var\(--k-border-color\)\]{border-color:var(--k-border-color)!important}.border-black\/10{border-color:#0000001a!important}.border-black\/5{border-color:#0000000d!important}.border-gray-400\/50{border-color:#9ca3af80!important}.border-transparent{border-color:transparent!important}.border-white\/10{border-color:#ffffff1a!important}.\!bg-white\/10{background-color:#ffffff1a!important}.bg-\[\#95ec69\]{--tw-bg-opacity: 1 !important;background-color:rgb(149 236 105 / var(--tw-bg-opacity, 1))!important}.bg-\[var\(--k-button-active-bg\)\]{background-color:var(--k-button-active-bg)!important}.bg-\[var\(--k-card-bg\)\]{background-color:var(--k-card-bg)!important}.bg-\[var\(--k-page-bg\)\]{background-color:var(--k-page-bg)!important}.bg-black{--tw-bg-opacity: 1 !important;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))!important}.bg-black\/5{background-color:#0000000d!important}.bg-gray-100{--tw-bg-opacity: 1 !important;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))!important}.bg-gray-400{--tw-bg-opacity: 1 !important;background-color:rgb(156 163 175 / var(--tw-bg-opacity, 1))!important}.bg-gray-50{--tw-bg-opacity: 1 !important;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))!important}.bg-green-500{--tw-bg-opacity: 1 !important;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))!important}.bg-white{--tw-bg-opacity: 1 !important;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))!important}.bg-opacity-50{--tw-bg-opacity: .5 !important}.bg-opacity-90{--tw-bg-opacity: .9 !important}.object-contain{-o-object-fit:contain!important;object-fit:contain!important}.p-0{padding:0!important}.p-2\.5{padding:.625rem!important}.p-3{padding:.75rem!important}.p-3\.5{padding:.875rem!important}.p-4{padding:1rem!important}.p-6{padding:1.5rem!important}.px-4{padding-left:1rem!important;padding-right:1rem!important}.px-6{padding-left:1.5rem!important;padding-right:1.5rem!important}.py-1\.5{padding-top:.375rem!important;padding-bottom:.375rem!important}.py-2\.5{padding-top:.625rem!important;padding-bottom:.625rem!important}.py-4{padding-top:1rem!important;padding-bottom:1rem!important}.pb-2{padding-bottom:.5rem!important}.pt-2{padding-top:.5rem!important}.text-left{text-align:left!important}.font-sans{font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"!important}.\!text-base{font-size:1rem!important;line-height:1.5rem!important}.\!text-xl{font-size:1.25rem!important;line-height:1.75rem!important}.text-\[10px\]{font-size:10px!important}.text-\[15px\]{font-size:15px!important}.text-lg{font-size:1.125rem!important;line-height:1.75rem!important}.text-sm{font-size:.875rem!important;line-height:1.25rem!important}.text-xs{font-size:.75rem!important;line-height:1rem!important}.font-bold{font-weight:700!important}.font-medium{font-weight:500!important}.italic{font-style:italic!important}.leading-relaxed{line-height:1.625!important}.\!text-white{--tw-text-opacity: 1 !important;color:rgb(255 255 255 / var(--tw-text-opacity, 1))!important}.text-\[var\(--k-color-primary\)\]{color:var(--k-color-primary)!important}.text-\[var\(--k-text-color\)\]{color:var(--k-text-color)!important}.text-\[var\(--k-text-color-secondary\)\]{color:var(--k-text-color-secondary)!important}.text-black{--tw-text-opacity: 1 !important;color:rgb(0 0 0 / var(--tw-text-opacity, 1))!important}.text-blue-500{--tw-text-opacity: 1 !important;color:rgb(59 130 246 / var(--tw-text-opacity, 1))!important}.text-gray-400{--tw-text-opacity: 1 !important;color:rgb(156 163 175 / var(--tw-text-opacity, 1))!important}.text-orange-400{--tw-text-opacity: 1 !important;color:rgb(251 146 60 / var(--tw-text-opacity, 1))!important}.text-red-500{--tw-text-opacity: 1 !important;color:rgb(239 68 68 / var(--tw-text-opacity, 1))!important}.text-white{--tw-text-opacity: 1 !important;color:rgb(255 255 255 / var(--tw-text-opacity, 1))!important}.opacity-0{opacity:0!important}.opacity-50{opacity:.5!important}.opacity-60{opacity:.6!important}.opacity-70{opacity:.7!important}.opacity-80{opacity:.8!important}.opacity-90{opacity:.9!important}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25) !important;--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color) !important;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)!important}.shadow-\[0_-2px_10px_rgba\(0\,0\,0\,0\.05\)\]{--tw-shadow: 0 -2px 10px rgba(0,0,0,.05) !important;--tw-shadow-colored: 0 -2px 10px var(--tw-shadow-color) !important;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)!important}.shadow-inner{--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / .05) !important;--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color) !important;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)!important}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1) !important;--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color) !important;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)!important}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1) !important;--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color) !important;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)!important}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05) !important;--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color) !important;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)!important}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1) !important;--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color) !important;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)!important}.brightness-100{--tw-brightness: brightness(1) !important;filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.brightness-105{--tw-brightness: brightness(1.05) !important;filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.brightness-95{--tw-brightness: brightness(.95) !important;filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px) !important;-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)!important;backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)!important}.transition-all{transition-property:all!important;transition-timing-function:cubic-bezier(.4,0,.2,1)!important;transition-duration:.15s!important}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke!important;transition-timing-function:cubic-bezier(.4,0,.2,1)!important;transition-duration:.15s!important}.transition-opacity{transition-property:opacity!important;transition-timing-function:cubic-bezier(.4,0,.2,1)!important;transition-duration:.15s!important}.transition-transform{transition-property:transform!important;transition-timing-function:cubic-bezier(.4,0,.2,1)!important;transition-duration:.15s!important}:root{--chat-bg: var(--k-page-bg);--chat-panel-bg: var(--k-card-bg);--chat-border: var(--k-border-color);--chat-text: var(--k-text-color);--chat-text-sub: var(--k-text-color-secondary)}.chat-patch-wrapper{color:var(--chat-text);background-color:var(--chat-bg)}.chat-patch-wrapper .el-aside,.chat-patch-wrapper .el-main,.chat-patch-wrapper .el-header,.chat-patch-wrapper .el-footer{background-color:transparent!important;color:inherit!important}.chat-patch-wrapper .el-scrollbar{background-color:transparent!important}.chat-patch-wrapper .el-input__wrapper,.chat-patch-wrapper .el-textarea__inner{background-color:var(--k-input-bg)!important;color:var(--chat-text)!important;box-shadow:0 0 0 1px var(--chat-border) inset!important}.chat-patch-wrapper .el-empty{background-color:transparent!important}.layout-iframe{width:100%;height:100%;border:none}.hover\:scale-105:hover{--tw-scale-x: 1.05 !important;--tw-scale-y: 1.05 !important;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.hover\:bg-\[var\(--k-button-hover-bg\)\]:hover{background-color:var(--k-button-hover-bg)!important}.hover\:bg-black\/10:hover{background-color:#0000001a!important}.hover\:underline:hover{text-decoration-line:underline!important}.hover\:opacity-90:hover{opacity:.9!important}.group:hover .group-hover\:opacity-100{opacity:1!important}@media (min-width: 768px){.md\:max-w-\[75\%\]{max-width:75%!important}}@media (prefers-color-scheme: dark){.dark\:border-white\/10{border-color:#ffffff1a!important}.dark\:border-white\/5{border-color:#ffffff0d!important}.dark\:bg-black\/10{background-color:#0000001a!important}.dark\:bg-black\/20{background-color:#0003!important}.dark\:bg-white\/5{background-color:#ffffff0d!important}.dark\:brightness-100{--tw-brightness: brightness(1) !important;filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.dark\:brightness-90{--tw-brightness: brightness(.9) !important;filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.dark\:brightness-95{--tw-brightness: brightness(.95) !important;filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.dark\:hover\:bg-white\/10:hover{background-color:#ffffff1a!important}}
|
package/lib/index.js
CHANGED
|
@@ -39,12 +39,17 @@ __export(src_exports, {
|
|
|
39
39
|
usage: () => usage
|
|
40
40
|
});
|
|
41
41
|
module.exports = __toCommonJS(src_exports);
|
|
42
|
-
var
|
|
42
|
+
var import_node_path3 = __toESM(require("node:path"));
|
|
43
43
|
|
|
44
44
|
// src/utils.ts
|
|
45
|
+
var import_node_fs = require("node:fs");
|
|
46
|
+
var import_node_path = require("node:path");
|
|
47
|
+
var import_node_crypto = require("node:crypto");
|
|
48
|
+
var import_node_url = require("node:url");
|
|
45
49
|
var Utils = class {
|
|
46
|
-
constructor(config) {
|
|
50
|
+
constructor(config, ctx) {
|
|
47
51
|
this.config = config;
|
|
52
|
+
this.ctx = ctx;
|
|
48
53
|
}
|
|
49
54
|
static {
|
|
50
55
|
__name(this, "Utils");
|
|
@@ -95,14 +100,41 @@ var Utils = class {
|
|
|
95
100
|
}
|
|
96
101
|
return false;
|
|
97
102
|
}
|
|
98
|
-
//
|
|
103
|
+
// 持久化 base64 图片并返回本地文件 URL
|
|
104
|
+
persistBase64Image(base64Data) {
|
|
105
|
+
if (!this.ctx || !base64Data.startsWith("data:image/")) return base64Data;
|
|
106
|
+
try {
|
|
107
|
+
const dir = (0, import_node_path.join)(this.ctx.baseDir, "data", "chat-patch", "persist-images");
|
|
108
|
+
if (!(0, import_node_fs.existsSync)(dir)) (0, import_node_fs.mkdirSync)(dir, { recursive: true });
|
|
109
|
+
const hash = (0, import_node_crypto.createHash)("md5").update(base64Data).digest("hex");
|
|
110
|
+
const ext = base64Data.split(";")[0].split("/")[1] || "png";
|
|
111
|
+
const filename = `${Date.now()}_${hash}.${ext}`;
|
|
112
|
+
const filePath = (0, import_node_path.join)(dir, filename);
|
|
113
|
+
const base64Content = base64Data.split(",")[1];
|
|
114
|
+
(0, import_node_fs.writeFileSync)(filePath, Buffer.from(base64Content, "base64"));
|
|
115
|
+
this.cleanupPersistImages(dir);
|
|
116
|
+
return (0, import_node_url.pathToFileURL)(filePath).href;
|
|
117
|
+
} catch (e) {
|
|
118
|
+
return base64Data;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
cleanupPersistImages(dir) {
|
|
122
|
+
try {
|
|
123
|
+
const files = (0, import_node_fs.readdirSync)(dir).map((name2) => ({ name: name2, path: (0, import_node_path.join)(dir, name2), mtime: (0, import_node_fs.statSync)((0, import_node_path.join)(dir, name2)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
124
|
+
if (files.length > this.config.maxPersistImages) {
|
|
125
|
+
files.slice(this.config.maxPersistImages).forEach((f) => (0, import_node_fs.unlinkSync)(f.path));
|
|
126
|
+
}
|
|
127
|
+
} catch (e) {
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// 清理对象中的base64内容,改为持久化存储
|
|
99
131
|
cleanBase64Content(obj) {
|
|
100
132
|
if (obj === null || obj === void 0) {
|
|
101
133
|
return obj;
|
|
102
134
|
}
|
|
103
135
|
if (typeof obj === "string") {
|
|
104
136
|
if (this.isBase64(obj)) {
|
|
105
|
-
return
|
|
137
|
+
return this.persistBase64Image(obj);
|
|
106
138
|
}
|
|
107
139
|
return obj;
|
|
108
140
|
}
|
|
@@ -113,7 +145,7 @@ var Utils = class {
|
|
|
113
145
|
const cleaned = {};
|
|
114
146
|
for (const [key, value] of Object.entries(obj)) {
|
|
115
147
|
if (typeof value === "string" && (key === "src" || key === "url" || key === "file" || key === "data" || key === "content") && this.isBase64(value)) {
|
|
116
|
-
cleaned[key] =
|
|
148
|
+
cleaned[key] = this.persistBase64Image(value);
|
|
117
149
|
} else {
|
|
118
150
|
cleaned[key] = this.cleanBase64Content(value);
|
|
119
151
|
}
|
|
@@ -173,7 +205,7 @@ var MessageHandler = class {
|
|
|
173
205
|
const guild = await session.bot.getGuild(session.guildId);
|
|
174
206
|
guildName = guild?.name || session.channelId;
|
|
175
207
|
}
|
|
176
|
-
if (session.isDirect && session.bot.getUser && typeof session.bot.getUser === "function") {
|
|
208
|
+
if (session.userId && session.isDirect && session.bot.getUser && typeof session.bot.getUser === "function") {
|
|
177
209
|
try {
|
|
178
210
|
const user = await session.bot.getUser(session.userId);
|
|
179
211
|
directUserName = user?.name || session.username || session.userId || "未知用户";
|
|
@@ -198,7 +230,7 @@ var MessageHandler = class {
|
|
|
198
230
|
}
|
|
199
231
|
const channelInfo = {
|
|
200
232
|
id: session.channelId,
|
|
201
|
-
name: session.isDirect ? `私聊(${directUserName})` : `${guildName} (${session.channelId})`,
|
|
233
|
+
name: session.isDirect ? `私聊(${session.username || session.userId || directUserName})` : `${guildName} (${session.channelId})`,
|
|
202
234
|
type: session.type || 0,
|
|
203
235
|
channelId: session.channelId,
|
|
204
236
|
guildName,
|
|
@@ -303,8 +335,8 @@ var MessageHandler = class {
|
|
|
303
335
|
await this.updateBotInfoToFile(session);
|
|
304
336
|
const guildName = await this.updateChannelInfoToFile(session);
|
|
305
337
|
const timestamp = Date.now();
|
|
306
|
-
let content = "";
|
|
307
|
-
if (session.event?.message?.elements) {
|
|
338
|
+
let content = session.content || "";
|
|
339
|
+
if (!content && session.event?.message?.elements) {
|
|
308
340
|
content = this.utils.extractTextContent(session.event.message.elements).trim();
|
|
309
341
|
}
|
|
310
342
|
const messageInfo = {
|
|
@@ -370,13 +402,13 @@ var MessageHandler = class {
|
|
|
370
402
|
};
|
|
371
403
|
|
|
372
404
|
// src/file-manager.ts
|
|
373
|
-
var
|
|
374
|
-
var
|
|
405
|
+
var import_node_path2 = __toESM(require("node:path"));
|
|
406
|
+
var import_node_fs2 = __toESM(require("node:fs"));
|
|
375
407
|
var FileManager = class {
|
|
376
408
|
constructor(ctx, config) {
|
|
377
409
|
this.ctx = ctx;
|
|
378
410
|
this.config = config;
|
|
379
|
-
this.dataFilePath =
|
|
411
|
+
this.dataFilePath = import_node_path2.default.resolve(ctx.baseDir, "data", "chat-patch", "chat-data.json");
|
|
380
412
|
this.logger = ctx.logger("chat-patch");
|
|
381
413
|
this.utils = new Utils(config);
|
|
382
414
|
}
|
|
@@ -389,16 +421,16 @@ var FileManager = class {
|
|
|
389
421
|
utils;
|
|
390
422
|
// 确保目录存在
|
|
391
423
|
ensureDataDir() {
|
|
392
|
-
const dir =
|
|
393
|
-
if (!
|
|
394
|
-
|
|
424
|
+
const dir = import_node_path2.default.dirname(this.dataFilePath);
|
|
425
|
+
if (!import_node_fs2.default.existsSync(dir)) {
|
|
426
|
+
import_node_fs2.default.mkdirSync(dir, { recursive: true });
|
|
395
427
|
}
|
|
396
428
|
}
|
|
397
429
|
// 从JSON文件读取数据
|
|
398
430
|
readChatDataFromFile() {
|
|
399
431
|
try {
|
|
400
|
-
if (
|
|
401
|
-
const jsonData =
|
|
432
|
+
if (import_node_fs2.default.existsSync(this.dataFilePath)) {
|
|
433
|
+
const jsonData = import_node_fs2.default.readFileSync(this.dataFilePath, "utf8");
|
|
402
434
|
const data = JSON.parse(jsonData);
|
|
403
435
|
return {
|
|
404
436
|
bots: data.bots || {},
|
|
@@ -426,7 +458,7 @@ var FileManager = class {
|
|
|
426
458
|
this.ensureDataDir();
|
|
427
459
|
data.lastSaveTime = Date.now();
|
|
428
460
|
const jsonData = JSON.stringify(data, null, 2);
|
|
429
|
-
|
|
461
|
+
import_node_fs2.default.writeFileSync(this.dataFilePath, jsonData, "utf8");
|
|
430
462
|
} catch (error) {
|
|
431
463
|
this.logger.error("写入聊天数据失败:", error);
|
|
432
464
|
}
|
|
@@ -514,7 +546,9 @@ var FileManager = class {
|
|
|
514
546
|
|
|
515
547
|
// src/api-handlers.ts
|
|
516
548
|
var import_koishi = require("koishi");
|
|
517
|
-
var
|
|
549
|
+
var import_node_url2 = require("node:url");
|
|
550
|
+
var import_node_fs3 = require("node:fs");
|
|
551
|
+
var mime = __toESM(require("mime-types"));
|
|
518
552
|
var ApiHandlers = class {
|
|
519
553
|
constructor(ctx, config, fileManager, messageHandler) {
|
|
520
554
|
this.ctx = ctx;
|
|
@@ -706,8 +740,8 @@ var ApiHandlers = class {
|
|
|
706
740
|
messageId,
|
|
707
741
|
content: messageContent,
|
|
708
742
|
userId: data.selfId,
|
|
709
|
-
|
|
710
|
-
|
|
743
|
+
username: bot2?.user?.name || `Bot-${data.selfId}`,
|
|
744
|
+
avatar: bot2?.user?.avatar,
|
|
711
745
|
timestamp: Date.now(),
|
|
712
746
|
guildName: "",
|
|
713
747
|
// 这个信息在前端会补充
|
|
@@ -881,7 +915,6 @@ var ApiHandlers = class {
|
|
|
881
915
|
keepTempImages: this.config.keepTempImages,
|
|
882
916
|
loggerinfo: this.config.loggerinfo,
|
|
883
917
|
blockedPlatforms: this.config.blockedPlatforms || [],
|
|
884
|
-
chatContainerHeight: this.config.chatContainerHeight,
|
|
885
918
|
clearIndexedDBOnStart: this.config.clearIndexedDBOnStart
|
|
886
919
|
}
|
|
887
920
|
};
|
|
@@ -906,7 +939,7 @@ var ApiHandlers = class {
|
|
|
906
939
|
// 检查是否为文件 URL
|
|
907
940
|
isFileUrl(url) {
|
|
908
941
|
try {
|
|
909
|
-
const parsedUrl = new
|
|
942
|
+
const parsedUrl = new import_node_url2.URL(url);
|
|
910
943
|
return parsedUrl.protocol === "file:";
|
|
911
944
|
} catch {
|
|
912
945
|
return false;
|
|
@@ -915,7 +948,7 @@ var ApiHandlers = class {
|
|
|
915
948
|
// 创建文件 URL
|
|
916
949
|
createFileUrl(filePath) {
|
|
917
950
|
try {
|
|
918
|
-
return (0,
|
|
951
|
+
return (0, import_node_url2.pathToFileURL)(filePath).href;
|
|
919
952
|
} catch (error) {
|
|
920
953
|
this.logger.error("创建文件URL失败:", { filePath, error });
|
|
921
954
|
return `file://${filePath}`;
|
|
@@ -924,15 +957,16 @@ var ApiHandlers = class {
|
|
|
924
957
|
// 处理本地文件请求
|
|
925
958
|
async handleLocalFileRequest(fileUrl) {
|
|
926
959
|
try {
|
|
927
|
-
const
|
|
928
|
-
const
|
|
929
|
-
|
|
930
|
-
|
|
960
|
+
const filePath = (0, import_node_url2.fileURLToPath)(fileUrl);
|
|
961
|
+
const buffer = (0, import_node_fs3.readFileSync)(filePath);
|
|
962
|
+
const base64 = buffer.toString("base64");
|
|
963
|
+
const contentType = mime.lookup(filePath) || "application/octet-stream";
|
|
964
|
+
this.logInfo("成功读取本地文件:", { fileUrl, filePath, contentType });
|
|
931
965
|
return {
|
|
932
966
|
success: true,
|
|
933
|
-
base64
|
|
967
|
+
base64,
|
|
934
968
|
contentType,
|
|
935
|
-
dataUrl: `data:${contentType};base64,${
|
|
969
|
+
dataUrl: `data:${contentType};base64,${base64}`
|
|
936
970
|
};
|
|
937
971
|
} catch (error) {
|
|
938
972
|
this.logger.error("读取本地文件失败:", { fileUrl, error: error.message });
|
|
@@ -1015,6 +1049,7 @@ var Config = import_koishi2.Schema.intersect([
|
|
|
1015
1049
|
maxMessagesPerChannel: import_koishi2.Schema.number().default(500).description("每个群组最大保存消息数量").min(50).max(1500),
|
|
1016
1050
|
keepMessagesOnClear: import_koishi2.Schema.number().default(50).description("手动清理历史记录时保留的消息数量").min(0).max(1e3),
|
|
1017
1051
|
keepTempImages: import_koishi2.Schema.number().default(50).description("发送消息保留的临时图片数量(最新的N张)").min(10).max(200),
|
|
1052
|
+
maxPersistImages: import_koishi2.Schema.number().default(100).description("持久化存储的机器人发送图片数量").min(10).max(500),
|
|
1018
1053
|
blockedPlatforms: import_koishi2.Schema.array(import_koishi2.Schema.object({
|
|
1019
1054
|
platformName: import_koishi2.Schema.string().description("平台名称或关键词"),
|
|
1020
1055
|
exactMatch: import_koishi2.Schema.boolean().default(false).description("完全匹配?如果关闭,包含关键词即屏蔽").default(true)
|
|
@@ -1035,9 +1070,6 @@ var Config = import_koishi2.Schema.intersect([
|
|
|
1035
1070
|
]
|
|
1036
1071
|
)
|
|
1037
1072
|
}).description("基础设置"),
|
|
1038
|
-
import_koishi2.Schema.object({
|
|
1039
|
-
chatContainerHeight: import_koishi2.Schema.number().default(80).description("手机端使用的视口高度(防止文本输入框被挡住)").min(50).max(100)
|
|
1040
|
-
}).description("进阶设置"),
|
|
1041
1073
|
import_koishi2.Schema.object({
|
|
1042
1074
|
clearIndexedDBOnStart: import_koishi2.Schema.boolean().default(true).description("启动时强制清空IndexedDB缓存(适用于紧急情况,防止浏览器卡死)"),
|
|
1043
1075
|
loggerinfo: import_koishi2.Schema.boolean().default(false).description("日志调试模式").experimental()
|
|
@@ -1066,7 +1098,7 @@ async function apply(ctx, config) {
|
|
|
1066
1098
|
const fileManager = new FileManager(ctx, config);
|
|
1067
1099
|
const messageHandler = new MessageHandler(ctx, config, fileManager);
|
|
1068
1100
|
const apiHandlers = new ApiHandlers(ctx, config, fileManager, messageHandler);
|
|
1069
|
-
const utils = new Utils(config);
|
|
1101
|
+
const utils = new Utils(config, ctx);
|
|
1070
1102
|
const initialData = fileManager.readChatDataFromFile();
|
|
1071
1103
|
const cleanedData = fileManager.cleanExcessMessages(initialData);
|
|
1072
1104
|
const originalCount = Object.values(initialData.messages).reduce((total, msgs) => total + msgs.length, 0);
|
|
@@ -1115,8 +1147,8 @@ async function apply(ctx, config) {
|
|
|
1115
1147
|
});
|
|
1116
1148
|
apiHandlers.registerApiHandlers();
|
|
1117
1149
|
ctx.console.addEntry({
|
|
1118
|
-
dev:
|
|
1119
|
-
prod:
|
|
1150
|
+
dev: import_node_path3.default.resolve(__dirname, "../client/index.ts"),
|
|
1151
|
+
prod: import_node_path3.default.resolve(__dirname, "../dist")
|
|
1120
1152
|
});
|
|
1121
1153
|
}
|
|
1122
1154
|
__name(apply, "apply");
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-chat-patch",
|
|
3
3
|
"description": "[<ruby>chat-patch<rp>(</rp><rt>点我预览效果</rt><rp>)</rp></ruby>](https://i0.hdslb.com/bfs/openplatform/71074dfc9e5256fc3333d8bd8478bec1af874046.png) 视奸小插件((bushi( (低性能警告)。手机端适配。灵感来自 chat 插件。",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "2.0.1",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
"src"
|
|
12
12
|
],
|
|
13
13
|
"license": "MIT",
|
|
14
|
-
"homepage": "https://github.com/
|
|
14
|
+
"homepage": "https://github.com/koishi-shangxue-plugins/koishi-shangxue-apps/",
|
|
15
15
|
"bugs": {
|
|
16
|
-
"url": "https://github.com/
|
|
16
|
+
"url": "https://github.com/koishi-shangxue-plugins/koishi-shangxue-apps/issues"
|
|
17
17
|
},
|
|
18
18
|
"keywords": [
|
|
19
19
|
"koishi",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"聊天室",
|
|
23
23
|
"console"
|
|
24
24
|
],
|
|
25
|
-
"
|
|
25
|
+
"koishi": {
|
|
26
26
|
"service": {
|
|
27
27
|
"required": [
|
|
28
28
|
"console"
|
|
@@ -35,6 +35,15 @@
|
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@koishijs/client": "^5.11.0",
|
|
38
|
-
"
|
|
38
|
+
"@types/mime-types": "^3.0.1",
|
|
39
|
+
"autoprefixer": "^10.4.23",
|
|
40
|
+
"koishi": "^4.16.0",
|
|
41
|
+
"postcss": "^8.5.6",
|
|
42
|
+
"tailwindcss": "^3.4.17"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@element-plus/icons-vue": "^2.3.2",
|
|
46
|
+
"element-plus": "^2.13.0",
|
|
47
|
+
"mime-types": "^3.0.2"
|
|
39
48
|
}
|
|
40
49
|
}
|
package/readme.md
CHANGED
|
@@ -18,7 +18,7 @@ Koishi 控制台聊天插件,允许用户直接在控制台中管理机器人
|
|
|
18
18
|
|
|
19
19
|
欢迎 PR ~
|
|
20
20
|
|
|
21
|
-
PR 方式请参考 -> https://github.com/
|
|
21
|
+
PR 方式请参考 -> <https://github.com/koishi-shangxue-plugins/koishi-shangxue-apps/tree/main?tab=readme-ov-file#%E5%A6%82%E4%BD%95%E5%9C%A8%E9%A1%B9%E7%9B%AE%E6%A8%A1%E6%9D%BF%E4%B8%AD%E5%BC%80%E5%8F%91%E6%AD%A4%E4%BB%93%E5%BA%93>
|
|
22
22
|
|
|
23
23
|
## 许可证
|
|
24
24
|
|
package/src/api-handlers.ts
CHANGED
|
@@ -3,7 +3,9 @@ import { MessageHandler } from './message-handler'
|
|
|
3
3
|
import { Context, h, Logger } from 'koishi'
|
|
4
4
|
import { Config } from './config'
|
|
5
5
|
import { } from '@koishijs/plugin-console'
|
|
6
|
-
import { URL, pathToFileURL } from 'node:url'
|
|
6
|
+
import { URL, pathToFileURL, fileURLToPath } from 'node:url'
|
|
7
|
+
import { readFileSync } from 'node:fs'
|
|
8
|
+
import * as mime from 'mime-types'
|
|
7
9
|
|
|
8
10
|
export class ApiHandlers {
|
|
9
11
|
private logger: Logger
|
|
@@ -267,8 +269,8 @@ export class ApiHandlers {
|
|
|
267
269
|
messageId: messageId,
|
|
268
270
|
content: messageContent,
|
|
269
271
|
userId: data.selfId,
|
|
270
|
-
|
|
271
|
-
|
|
272
|
+
username: bot?.user?.name || `Bot-${data.selfId}`,
|
|
273
|
+
avatar: bot?.user?.avatar,
|
|
272
274
|
timestamp: Date.now(),
|
|
273
275
|
guildName: '', // 这个信息在前端会补充
|
|
274
276
|
channelType: 0, // 这个信息在前端会补充
|
|
@@ -504,7 +506,6 @@ export class ApiHandlers {
|
|
|
504
506
|
keepTempImages: this.config.keepTempImages,
|
|
505
507
|
loggerinfo: this.config.loggerinfo,
|
|
506
508
|
blockedPlatforms: this.config.blockedPlatforms || [],
|
|
507
|
-
chatContainerHeight: this.config.chatContainerHeight,
|
|
508
509
|
clearIndexedDBOnStart: this.config.clearIndexedDBOnStart
|
|
509
510
|
}
|
|
510
511
|
}
|
|
@@ -553,17 +554,23 @@ export class ApiHandlers {
|
|
|
553
554
|
// 处理本地文件请求
|
|
554
555
|
private async handleLocalFileRequest(fileUrl: string) {
|
|
555
556
|
try {
|
|
556
|
-
|
|
557
|
-
const
|
|
558
|
-
let contentType = fileresponse.type
|
|
557
|
+
// 使用 fileURLToPath 转换 file:// URL 为系统路径
|
|
558
|
+
const filePath = fileURLToPath(fileUrl)
|
|
559
559
|
|
|
560
|
-
|
|
560
|
+
// 改用 node:fs 直接读取文件,避免 ctx.http.file 可能存在的编码或协议处理问题
|
|
561
|
+
const buffer = readFileSync(filePath)
|
|
562
|
+
const base64 = buffer.toString('base64')
|
|
563
|
+
|
|
564
|
+
// 使用 mime-types 库精确推断 MIME 类型
|
|
565
|
+
const contentType = mime.lookup(filePath) || 'application/octet-stream'
|
|
566
|
+
|
|
567
|
+
this.logInfo('成功读取本地文件:', { fileUrl, filePath, contentType })
|
|
561
568
|
|
|
562
569
|
return {
|
|
563
570
|
success: true,
|
|
564
|
-
base64:
|
|
571
|
+
base64: base64,
|
|
565
572
|
contentType: contentType,
|
|
566
|
-
dataUrl: `data:${contentType};base64,${
|
|
573
|
+
dataUrl: `data:${contentType};base64,${base64}`
|
|
567
574
|
}
|
|
568
575
|
} catch (error: any) {
|
|
569
576
|
this.logger.error('读取本地文件失败:', { fileUrl, error: error.message })
|
package/src/config.ts
CHANGED
|
@@ -6,11 +6,11 @@ export interface Config {
|
|
|
6
6
|
maxMessagesPerChannel: number
|
|
7
7
|
keepMessagesOnClear: number
|
|
8
8
|
keepTempImages: number
|
|
9
|
+
maxPersistImages: number
|
|
9
10
|
blockedPlatforms: Array<{
|
|
10
11
|
platformName: string
|
|
11
12
|
exactMatch: boolean
|
|
12
13
|
}>
|
|
13
|
-
chatContainerHeight: number
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export const Config: Schema<Config> = Schema.intersect([
|
|
@@ -18,6 +18,7 @@ export const Config: Schema<Config> = Schema.intersect([
|
|
|
18
18
|
maxMessagesPerChannel: Schema.number().default(500).description('每个群组最大保存消息数量').min(50).max(1500),
|
|
19
19
|
keepMessagesOnClear: Schema.number().default(50).description('手动清理历史记录时保留的消息数量').min(0).max(1000),
|
|
20
20
|
keepTempImages: Schema.number().default(50).description('发送消息保留的临时图片数量(最新的N张)').min(10).max(200),
|
|
21
|
+
maxPersistImages: Schema.number().default(100).description('持久化存储的机器人发送图片数量').min(10).max(500),
|
|
21
22
|
blockedPlatforms: Schema.array(Schema.object({
|
|
22
23
|
platformName: Schema.string().description('平台名称或关键词'),
|
|
23
24
|
exactMatch: Schema.boolean().default(false).description('完全匹配?如果关闭,包含关键词即屏蔽').default(true)
|
|
@@ -39,9 +40,6 @@ export const Config: Schema<Config> = Schema.intersect([
|
|
|
39
40
|
),
|
|
40
41
|
}).description('基础设置'),
|
|
41
42
|
|
|
42
|
-
Schema.object({
|
|
43
|
-
chatContainerHeight: Schema.number().default(80).description('手机端使用的视口高度(防止文本输入框被挡住)').min(50).max(100),
|
|
44
|
-
}).description('进阶设置'),
|
|
45
43
|
|
|
46
44
|
Schema.object({
|
|
47
45
|
clearIndexedDBOnStart: Schema.boolean().default(true).description('启动时强制清空IndexedDB缓存(适用于紧急情况,防止浏览器卡死)'),
|
package/src/index.ts
CHANGED
|
@@ -42,7 +42,7 @@ export async function apply(ctx: Context, config: Config) {
|
|
|
42
42
|
const fileManager = new FileManager(ctx, config)
|
|
43
43
|
const messageHandler = new MessageHandler(ctx, config, fileManager)
|
|
44
44
|
const apiHandlers = new ApiHandlers(ctx, config, fileManager, messageHandler)
|
|
45
|
-
const utils = new Utils(config)
|
|
45
|
+
const utils = new Utils(config, ctx)
|
|
46
46
|
|
|
47
47
|
// 初始化数据
|
|
48
48
|
const initialData = fileManager.readChatDataFromFile()
|
package/src/message-handler.ts
CHANGED
|
@@ -63,7 +63,7 @@ export class MessageHandler {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// 获取私聊用户昵称
|
|
66
|
-
if (session.isDirect && session.bot.getUser && typeof session.bot.getUser === 'function') {
|
|
66
|
+
if (session.userId && session.isDirect && session.bot.getUser && typeof session.bot.getUser === 'function') {
|
|
67
67
|
try {
|
|
68
68
|
const user = await session.bot.getUser(session.userId)
|
|
69
69
|
directUserName = user?.name || session.username || session.userId || '未知用户'
|
|
@@ -93,7 +93,7 @@ export class MessageHandler {
|
|
|
93
93
|
const channelInfo: ChannelInfo = {
|
|
94
94
|
id: session.channelId,
|
|
95
95
|
name: session.isDirect
|
|
96
|
-
? `私聊(${directUserName})`
|
|
96
|
+
? `私聊(${session.username || session.userId || directUserName})`
|
|
97
97
|
: `${guildName} (${session.channelId})`,
|
|
98
98
|
type: session.type || 0,
|
|
99
99
|
channelId: session.channelId,
|
|
@@ -226,9 +226,10 @@ export class MessageHandler {
|
|
|
226
226
|
|
|
227
227
|
const timestamp = Date.now()
|
|
228
228
|
|
|
229
|
-
|
|
229
|
+
// 优先使用 session.content,它包含了完整的消息内容(含标签)
|
|
230
|
+
let content = session.content || ''
|
|
230
231
|
|
|
231
|
-
if (session.event?.message?.elements) {
|
|
232
|
+
if (!content && session.event?.message?.elements) {
|
|
232
233
|
content = this.utils.extractTextContent(session.event.message.elements).trim()
|
|
233
234
|
}
|
|
234
235
|
|
package/src/utils.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { Config } from './config'
|
|
2
|
+
import { Context } from 'koishi'
|
|
3
|
+
import { writeFileSync, existsSync, mkdirSync, readdirSync, statSync, unlinkSync } from 'node:fs'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
import { createHash } from 'node:crypto'
|
|
6
|
+
import { pathToFileURL } from 'node:url'
|
|
2
7
|
|
|
3
8
|
export class Utils {
|
|
4
|
-
constructor(private config: Config) { }
|
|
9
|
+
constructor(private config: Config, private ctx?: Context) { }
|
|
5
10
|
|
|
6
11
|
// 检查平台是否被屏蔽
|
|
7
12
|
isPlatformBlocked(platform: string): boolean {
|
|
@@ -59,7 +64,46 @@ export class Utils {
|
|
|
59
64
|
return false
|
|
60
65
|
}
|
|
61
66
|
|
|
62
|
-
//
|
|
67
|
+
// 持久化 base64 图片并返回本地文件 URL
|
|
68
|
+
persistBase64Image(base64Data: string): string {
|
|
69
|
+
if (!this.ctx || !base64Data.startsWith('data:image/')) return base64Data
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const dir = join(this.ctx.baseDir, 'data', 'chat-patch', 'persist-images')
|
|
73
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
|
|
74
|
+
|
|
75
|
+
// 生成文件名:时间戳 + 内容哈希
|
|
76
|
+
const hash = createHash('md5').update(base64Data).digest('hex')
|
|
77
|
+
const ext = base64Data.split(';')[0].split('/')[1] || 'png'
|
|
78
|
+
const filename = `${Date.now()}_${hash}.${ext}`
|
|
79
|
+
const filePath = join(dir, filename)
|
|
80
|
+
|
|
81
|
+
// 写入文件
|
|
82
|
+
const base64Content = base64Data.split(',')[1]
|
|
83
|
+
writeFileSync(filePath, Buffer.from(base64Content, 'base64'))
|
|
84
|
+
|
|
85
|
+
// 清理旧图片
|
|
86
|
+
this.cleanupPersistImages(dir)
|
|
87
|
+
|
|
88
|
+
return pathToFileURL(filePath).href
|
|
89
|
+
} catch (e) {
|
|
90
|
+
return base64Data
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private cleanupPersistImages(dir: string) {
|
|
95
|
+
try {
|
|
96
|
+
const files = readdirSync(dir)
|
|
97
|
+
.map(name => ({ name, path: join(dir, name), mtime: statSync(join(dir, name)).mtimeMs }))
|
|
98
|
+
.sort((a, b) => b.mtime - a.mtime)
|
|
99
|
+
|
|
100
|
+
if (files.length > this.config.maxPersistImages) {
|
|
101
|
+
files.slice(this.config.maxPersistImages).forEach(f => unlinkSync(f.path))
|
|
102
|
+
}
|
|
103
|
+
} catch (e) { }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 清理对象中的base64内容,改为持久化存储
|
|
63
107
|
cleanBase64Content(obj: any): any {
|
|
64
108
|
if (obj === null || obj === undefined) {
|
|
65
109
|
return obj
|
|
@@ -67,7 +111,7 @@ export class Utils {
|
|
|
67
111
|
|
|
68
112
|
if (typeof obj === 'string') {
|
|
69
113
|
if (this.isBase64(obj)) {
|
|
70
|
-
return
|
|
114
|
+
return this.persistBase64Image(obj)
|
|
71
115
|
}
|
|
72
116
|
return obj
|
|
73
117
|
}
|
|
@@ -87,7 +131,7 @@ export class Utils {
|
|
|
87
131
|
key === 'data' ||
|
|
88
132
|
key === 'content'
|
|
89
133
|
) && this.isBase64(value)) {
|
|
90
|
-
cleaned[key] =
|
|
134
|
+
cleaned[key] = this.persistBase64Image(value)
|
|
91
135
|
} else {
|
|
92
136
|
cleaned[key] = this.cleanBase64Content(value)
|
|
93
137
|
}
|