吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1442|回复: 7
收起左侧

[其他转载] 金山文档 WPS云文档- AirScript添加复制运行日志按钮

  [复制链接]
Main91 发表于 2026-3-23 14:52
本帖最后由 Main91 于 2026-3-23 14:54 编辑

AirScript脚本编写调试的时候日志不好复制,所以用AI写了个篡改猴的JS脚本,自动添加复制按钮,一键复制日志
方便将日志发给AI
PixPin_2026-03-23_14-51-11.png

[JavaScript] 纯文本查看 复制代码
// ==UserScript==
// @name         金山文档 WPS云文档- AirScript添加复制运行日志按钮
// @namespace    https://github.com/your-name/kdocs-copy-log
// @version      3.3.0
// @description  将复制按钮完美嵌入运行日志面板,利用原生 Flex 布局完美贴合并垂直居中,还原原生按钮间距,自动单行排版
// @author       You
// @match        https://www.kdocs.cn/*
// @match        https://kdocs.cn/*
// @grant        GM_setClipboard
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';

  // ===================== 原生风格图标 (SVG) =====================
  const COPY_SVG = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
    <rect x="5.25" y="5.25" width="9" height="9" rx="1.5" stroke="#333" stroke-width="1.2" fill="none"/>
    <path d="M10.75 5.25V3.25C10.75 2.42157 10.0784 1.75 9.25 1.75H3.25
             C2.42157 1.75 1.75 2.42157 1.75 3.25V9.25
             C1.75 10.0784 2.42157 10.75 3.25 10.75H5.25"
          stroke="#333" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
  </svg>`;

  const CHECK_SVG = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path d="M3 8.5L6.5 12L13 4" stroke="#52c41a" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
  </svg>`;

  // ===================== 注入样式到 iframe =====================
  function injectIframeStyle(doc) {
    if (doc.getElementById('db-copy-log-style')) return;
    const style = doc.createElement('style');
    style.id = 'db-copy-log-style';
    style.textContent = `
      #db-copy-log-btn {
        position: relative;
        display: flex;
        align-items: center;
        justify-content: center;
      }
      #db-copy-log-btn .copy-tooltip {
        display: none;
        position: absolute;
        bottom: -30px;
        left: 50%;
        transform: translateX(-50%);
        background: rgba(0, 0, 0, 0.75);
        color: #fff;
        font-size: 12px;
        padding: 4px 8px;
        border-radius: 4px;
        white-space: nowrap;
        pointer-events: none;
        z-index: 99999;
        font-weight: normal;
        line-height: 1;
      }
      #db-copy-log-btn .copy-tooltip.show {
        display: block;
      }
    `;
    doc.head.appendChild(style);
  }

  // ============ 格式化日志内容 ============
  function formatLogText(rawText) {
    if (!rawText) return '';

    const lines = rawText.split('\n').map(l => l.trim()).filter(l => l !== '');
    const timeRegex = /^\d{2}:\d{2}:\d{2}(?:\.\d{3})?$/;

    const logs = [];
    let currentLog = [];

    for (const line of lines) {
      if (timeRegex.test(line)) {
        if (currentLog.length > 0) logs.push(currentLog.join(' '));
        currentLog = [line];
      } else {
        if (currentLog.length > 0) {
          currentLog.push(line);
        }
      }
    }
    if (currentLog.length > 0) logs.push(currentLog.join(' '));

    const uniqueLogs = [...new Set(logs)];
    return uniqueLogs.join('\n');
  }

  // ============ 提取日志文本 ============
  function extractLogText() {
    try {
      const iframe = document.getElementById('db_script_ide');
      if (!iframe || !iframe.contentDocument) return '';

      const doc = iframe.contentDocument;

      const knownContainer = doc.querySelector('.logArea');
      if (knownContainer && knownContainer.innerText.trim()) {
        return formatLogText(knownContainer.innerText);
      }

      const candidates = doc.querySelectorAll('div, pre, code, ul, section, main');
      let bestElement = null;
      let maxTimeCount = 0;
      const timeRegexGlobal = /^\d{2}:\d{2}:\d{2}(?:\.\d{3})?$/gm;

      for (const el of candidates) {
        const text = el.innerText || '';
        const matches = text.match(timeRegexGlobal);
        const timeCount = matches ? matches.length : 0;

        if (timeCount > 0) {
          if (timeCount > maxTimeCount) {
            maxTimeCount = timeCount;
            bestElement = el;
          } else if (timeCount === maxTimeCount && bestElement) {
            if (text.length < bestElement.innerText.length) {
              bestElement = el;
            }
          }
        }
      }

      if (bestElement) {
        return formatLogText(bestElement.innerText);
      }

    } catch (e) {
      console.warn('[复制日志] 无法访问 iframe:', e);
    }
    return '';
  }

  // ============ 复制到剪贴板 ============
  function copyToClipboard(text) {
    if (typeof GM_setClipboard === 'function') {
      GM_setClipboard(text, 'text');
      return;
    }
    if (navigator.clipboard && navigator.clipboard.writeText) {
      navigator.clipboard.writeText(text).catch(() => fallbackCopy(text));
      return;
    }
    fallbackCopy(text);
  }

  function fallbackCopy(text) {
    const ta = document.createElement('textarea');
    ta.value = text;
    ta.style.cssText = 'position:fixed;left:-9999px;top:-9999px';
    document.body.appendChild(ta);
    ta.select();
    document.execCommand('copy');
    document.body.removeChild(ta);
  }

  // ============ 注入按钮 ============
  function tryInject() {
    const iframe = document.getElementById('db_script_ide');
    if (!iframe || !iframe.contentDocument) return;

    const doc = iframe.contentDocument;

    if (doc.getElementById('db-copy-log-btn')) return;

    const headers = doc.querySelectorAll('.header');
    let logHeader = null;
    for (const h of headers) {
      if (h.innerText.includes('运行日志')) {
        logHeader = h;
        break;
      }
    }

    if (!logHeader) return;

    const closeBtn = logHeader.querySelector('button.close');
    if (!closeBtn) return;

    injectIframeStyle(doc);

    const btn = doc.createElement('button');
    btn.id = 'db-copy-log-btn';
    btn.type = 'button';
    btn.className = 'kd-button kd-button-light kd-button-lg kd-button-icon';
    btn.title = '复制运行日志';

    // ── 【还原官方紧凑间距】 ──
    btn.style.width = '32px';
    btn.style.height = '32px';
    btn.style.marginLeft = 'auto'; // 依然负责把关闭按钮顶到最右侧
    btn.style.marginRight = '0px'; // &#10071; 设为0,完全依赖原生按钮自带的内边距,达到无缝贴合效果

    btn.innerHTML = `
      <i class="kd-icon kd-icon-pro" style="line-height: 0;">
        ${COPY_SVG}
      </i>
      <span class="copy-tooltip">已复制!</span>
    `;

    closeBtn.parentElement.insertBefore(btn, closeBtn);

    btn.addEventListener('click', () => {
      let logText = extractLogText();

      if (!logText) {
        logText = '(未获取到运行日志内容,请确保面板已输出日志)';
      }

      copyToClipboard(logText);

      const iconContainer = btn.querySelector('i');
      const tooltip = btn.querySelector('.copy-tooltip');

      iconContainer.innerHTML = CHECK_SVG;
      tooltip.classList.add('show');

      setTimeout(() => {
        iconContainer.innerHTML = COPY_SVG;
        tooltip.classList.remove('show');
      }, 1500);
    });
  }

  // ============ 轮询监听 ============
  setInterval(() => {
    tryInject();
  }, 1000);

})();
image.png

免费评分

参与人数 2吾爱币 +2 热心值 +2 收起 理由
qsj521521 + 1 + 1 谢谢@Thanks!
王金彪 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Galaxyou 发表于 2026-3-23 15:14
我拿金山文档当数据库用
 楼主| Main91 发表于 2026-3-23 15:34
jxlncf 发表于 2026-3-23 16:27
川振 发表于 2026-3-23 16:28
不明觉厉
Galaxyou 发表于 2026-3-23 17:49
Main91 发表于 2026-3-23 15:34
AirScript的介绍里有说可以当数据库用

我当数据库用两三年了,感觉是真方便,能随时管理
HoganWu 发表于 2026-3-23 22:25
谢,有用
tiedong 发表于 2026-3-24 15:21
感觉是真方便,能随时管理的。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-9-30 20:28

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表