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/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 import_node_path2 = __toESM(require("node:path"));
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
- // 清理对象中的base64内容
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 "暂不支持记录base64内容";
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] = "暂不支持记录base64内容";
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 import_node_path = __toESM(require("node:path"));
374
- var import_node_fs = __toESM(require("node:fs"));
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 = import_node_path.default.resolve(ctx.baseDir, "data", "chat-patch", "chat-data.json");
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 = import_node_path.default.dirname(this.dataFilePath);
393
- if (!import_node_fs.default.existsSync(dir)) {
394
- import_node_fs.default.mkdirSync(dir, { recursive: true });
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 (import_node_fs.default.existsSync(this.dataFilePath)) {
401
- const jsonData = import_node_fs.default.readFileSync(this.dataFilePath, "utf8");
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
- import_node_fs.default.writeFileSync(this.dataFilePath, jsonData, "utf8");
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 import_node_url = require("node:url");
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
- botUsername: bot2?.user?.name || `Bot-${data.selfId}`,
710
- botAvatar: bot2?.user?.avatar,
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 import_node_url.URL(url);
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, import_node_url.pathToFileURL)(filePath).href;
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 fileresponse = await this.ctx.http.file(fileUrl);
928
- const fileresponsebase64 = Buffer.from(fileresponse.data).toString("base64");
929
- let contentType = fileresponse.type;
930
- this.logInfo("成功读取本地文件:", { fileUrl, contentType });
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: fileresponsebase64,
967
+ base64,
934
968
  contentType,
935
- dataUrl: `data:${contentType};base64,${fileresponsebase64}`
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: import_node_path2.default.resolve(__dirname, "../client/index.ts"),
1119
- prod: import_node_path2.default.resolve(__dirname, "../dist")
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": "1.3.0",
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/shangxueink/koishi-shangxue-apps/",
14
+ "homepage": "https://github.com/koishi-shangxue-plugins/koishi-shangxue-apps/",
15
15
  "bugs": {
16
- "url": "https://github.com/shangxueink/koishi-shangxue-apps/issues"
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
- "cordis": {
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
- "koishi": "^4.16.0"
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/shangxueink/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
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
 
@@ -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
- botUsername: bot?.user?.name || `Bot-${data.selfId}`,
271
- botAvatar: bot?.user?.avatar,
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
- const fileresponse = await this.ctx.http.file(fileUrl)
557
- const fileresponsebase64 = Buffer.from(fileresponse.data).toString('base64')
558
- let contentType = fileresponse.type
557
+ // 使用 fileURLToPath 转换 file:// URL 为系统路径
558
+ const filePath = fileURLToPath(fileUrl)
559
559
 
560
- this.logInfo('成功读取本地文件:', { fileUrl, contentType })
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: fileresponsebase64,
571
+ base64: base64,
565
572
  contentType: contentType,
566
- dataUrl: `data:${contentType};base64,${fileresponsebase64}`
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()
@@ -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
- let content = ''
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
- // 清理对象中的base64内容
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 '暂不支持记录base64内容'
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] = '暂不支持记录base64内容'
134
+ cleaned[key] = this.persistBase64Image(value)
91
135
  } else {
92
136
  cleaned[key] = this.cleanBase64Content(value)
93
137
  }