ccswitch-shared-ui.local

shared ui code

Questo script non dovrebbe essere installato direttamente. È una libreria per altri script da includere con la chiave // @require https://update.greasyfork.org/scripts/582598/1850862/ccswitch-shared-uilocal.js

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// Shared local development helpers for CC Switch import userscripts.
// Load this file before any local business entry via Tampermonkey @require.
// @name         ccswitch-shared-ui.local
// @version      0.0.1
// @author       irisWirisW (https://github.com/irisWirisW)
// @license      AGPL-3.0-or-later
(() => {
  "use strict";

  if (window.CCSwitchSharedUI) return;

  const DEFAULT_APP = "codex";
  const SHARED_DIALOG_STYLE_ID = "ccs-shared-dialog-style";
  const SHARED_DIALOG_CSS = `
    .ccs-shared-modal {
      --ccs-overlay: rgba(2, 6, 23, 0.6);
      --ccs-panel: rgba(15, 23, 42, 0.86);
      --ccs-panel-strong: rgba(30, 41, 59, 0.78);
      --ccs-panel-border: rgba(148, 163, 184, 0.22);
      --ccs-text: #e5eefb;
      --ccs-muted: #94a3b8;
      --ccs-input-bg: rgba(15, 23, 42, 0.72);
      --ccs-input-border: rgba(148, 163, 184, 0.26);
      --ccs-primary: #38bdf8;
      --ccs-primary-strong: #0284c7;
      --ccs-focus: rgba(56, 189, 248, 0.18);
      position: fixed;
      inset: 0;
      background: var(--ccs-overlay);
      display: none;
      align-items: center;
      justify-content: center;
      z-index: 99999;
      padding: 18px;
      backdrop-filter: blur(10px);
      -webkit-backdrop-filter: blur(10px);
      color-scheme: dark;
    }
    .ccs-shared-modal.open {
      display: flex;
    }
    .ccs-shared-panel {
      width: min(600px, 100%);
      max-height: calc(100vh - 36px);
      overflow: auto;
      border-radius: 18px;
      background:
        radial-gradient(circle at 16% 8%, rgba(56, 189, 248, 0.18), transparent 30%),
        radial-gradient(circle at 82% 0%, rgba(45, 212, 191, 0.12), transparent 26%),
        linear-gradient(135deg, var(--ccs-panel-strong), var(--ccs-panel));
      border: 1px solid var(--ccs-panel-border);
      box-shadow:
        0 24px 70px rgba(0, 0, 0, 0.42),
        0 1px 0 rgba(255, 255, 255, 0.12) inset;
      color: var(--ccs-text);
      padding: 18px;
      font-size: 13px;
      backdrop-filter: blur(22px) saturate(150%);
      -webkit-backdrop-filter: blur(22px) saturate(150%);
    }
    .ccs-shared-head {
      display: flex;
      align-items: flex-start;
      justify-content: space-between;
      gap: 16px;
      margin-bottom: 14px;
    }
    .ccs-shared-kicker {
      width: fit-content;
      border-radius: 999px;
      border: 1px solid rgba(56, 189, 248, 0.24);
      background: rgba(8, 47, 73, 0.46);
      color: #7dd3fc;
      font-size: 11px;
      font-weight: 700;
      line-height: 1;
      padding: 5px 8px;
      margin-bottom: 8px;
    }
    .ccs-shared-close {
      width: 32px;
      height: 32px;
      flex: 0 0 32px;
      border-radius: 999px;
      border: 1px solid rgba(148, 163, 184, 0.24);
      background: rgba(15, 23, 42, 0.48);
      color: #cbd5e1;
      font-size: 20px;
      line-height: 1;
      cursor: pointer;
      box-shadow: 0 1px 0 rgba(255, 255, 255, 0.08) inset;
    }
    .ccs-shared-title {
      font-size: 20px;
      font-weight: 700;
      line-height: 1.25;
      color: var(--ccs-text);
    }
    .ccs-shared-message {
      color: #cbd5e1;
      font-size: 12px;
      line-height: 1.5;
      margin: 0;
      padding: 10px 12px;
      border: 1px solid rgba(148, 163, 184, 0.16);
      border-radius: 12px;
      background: rgba(15, 23, 42, 0.46);
    }
    .ccs-shared-hint {
      margin-top: 7px;
      font-size: 12px;
      color: #fde68a;
      line-height: 1.4;
      display: none;
      padding: 8px 10px;
      border-radius: 10px;
      background: rgba(120, 53, 15, 0.24);
      border: 1px solid rgba(245, 158, 11, 0.22);
    }
    .ccs-shared-panel label {
      display: block;
      color: #cbd5e1;
      font-size: 12px;
      font-weight: 700;
      margin: 0 0 6px;
    }
    .ccs-shared-overview {
      display: grid;
      grid-template-columns: minmax(0, 1fr) minmax(220px, 1fr);
      gap: 12px 18px;
      align-items: stretch;
      margin-bottom: 14px;
    }
    .ccs-shared-primary-fields,
    .ccs-shared-notes {
      min-width: 0;
      display: grid;
      gap: 12px;
      align-content: start;
    }
    .ccs-shared-grid {
      display: grid;
      grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
      gap: 12px;
    }
    .ccs-shared-field {
      min-width: 0;
    }
    .ccs-shared-field-wide {
      grid-column: 1 / -1;
    }
    .ccs-shared-panel input,
    .ccs-shared-panel select {
      width: 100%;
      display: block;
      box-sizing: border-box;
      min-height: 38px;
      border-radius: 11px;
      border: 1px solid var(--ccs-input-border);
      background-color: var(--ccs-input-bg);
      color: var(--ccs-text);
      padding: 8px 11px;
      font-size: 13px;
      outline: none;
      box-shadow: 0 1px 0 rgba(255, 255, 255, 0.08) inset;
      transition:
        border-color 140ms ease,
        box-shadow 140ms ease,
        background 140ms ease;
    }
    .ccs-shared-panel input:focus,
    .ccs-shared-panel select:focus {
      border-color: rgba(56, 189, 248, 0.72);
      background-color: rgba(15, 23, 42, 0.9);
      box-shadow:
        0 0 0 4px var(--ccs-focus),
        0 1px 0 rgba(255, 255, 255, 0.1) inset;
    }
    .ccs-shared-panel select {
      appearance: none;
      -webkit-appearance: none;
      padding-right: 34px;
      background-image:
        linear-gradient(45deg, transparent 50%, rgba(203, 213, 225, 0.86) 50%),
        linear-gradient(135deg, rgba(203, 213, 225, 0.86) 50%, transparent 50%);
      background-position:
        calc(100% - 18px) calc(50% - 1px),
        calc(100% - 13px) calc(50% - 1px);
      background-size: 6px 6px, 6px 6px;
      background-repeat: no-repeat;
    }
    .ccs-shared-panel select:focus {
      background-image:
        linear-gradient(45deg, transparent 50%, rgba(224, 242, 254, 0.96) 50%),
        linear-gradient(135deg, rgba(224, 242, 254, 0.96) 50%, transparent 50%);
    }
    .ccs-shared-panel option {
      background: #0f172a;
      color: var(--ccs-text);
    }
    .ccs-shared-actions {
      margin-top: 16px;
      display: flex;
      justify-content: flex-end;
      gap: 8px;
      flex-wrap: wrap;
    }
    .ccs-shared-actions button {
      min-height: 34px;
      border-radius: 999px;
      border: 1px solid rgba(148, 163, 184, 0.24);
      background: rgba(15, 23, 42, 0.52);
      color: #cbd5e1;
      font-size: 12px;
      font-weight: 700;
      padding: 7px 13px;
      cursor: pointer;
      box-shadow: 0 1px 0 rgba(255, 255, 255, 0.08) inset;
    }
    .ccs-shared-actions .ccs-shared-primary {
      background: linear-gradient(135deg, var(--ccs-primary), var(--ccs-primary-strong));
      border-color: rgba(56, 189, 248, 0.3);
      color: #f8fafc;
      box-shadow:
        0 12px 26px rgba(14, 165, 233, 0.22),
        0 1px 0 rgba(255, 255, 255, 0.18) inset;
    }
    .ccs-shared-actions .ccs-shared-primary:disabled {
      opacity: 0.6;
      cursor: not-allowed;
    }
    .ccs-shared-close:hover,
    .ccs-shared-actions button:hover {
      filter: brightness(1.08);
    }
    @media (max-width: 560px) {
      .ccs-shared-overview,
      .ccs-shared-grid {
        grid-template-columns: 1fr;
      }
      .ccs-shared-panel {
        border-radius: 16px;
        padding: 15px;
      }
      .ccs-shared-actions button {
        flex: 1 1 auto;
      }
    }
  `;

  function injectStyleOnce(styleId, cssText) {
    const existing = document.getElementById(styleId);
    if (existing) return existing;

    const style = document.createElement("style");
    style.id = styleId;
    style.textContent = cssText;
    document.head.appendChild(style);
    return style;
  }

  function injectSharedDialogStyles() {
    return injectStyleOnce(SHARED_DIALOG_STYLE_ID, SHARED_DIALOG_CSS);
  }

  function inferName(endpoint) {
    try {
      return new URL(endpoint).host;
    } catch {
      return "custom";
    }
  }

  function getDefaultNameByApp(app, endpoint, defaultApp = DEFAULT_APP) {
    if ((app || defaultApp) === "codex") return "custom";
    return inferName(endpoint);
  }

  function updateCodexNameHint(target, options) {
    if (!target) return;

    const {
      app,
      name,
      defaultApp = DEFAULT_APP,
      prefixText = "",
      okText = "当前已设置为 custom。",
      warnText = "当前不是 custom,可能导致 thread 出错。",
    } = options || {};

    if ((app || defaultApp) !== "codex") {
      target.style.display = "none";
      target.textContent = "";
      return;
    }

    const normalizedName = (name || "").trim();
    const statusText = normalizedName === "custom" ? okText : warnText;
    target.style.display = "block";
    target.textContent = `${prefixText}${statusText}`;
  }

  function bindAutoSelect(input) {
    if (!input) return;

    const selectAll = () => {
      input.focus();
      input.select();
    };

    input.addEventListener("focus", () => setTimeout(selectAll, 0));
    input.addEventListener("click", selectAll);
    input.addEventListener("mouseup", (event) => event.preventDefault());
  }

  function buildDialogShell(options) {
    const {
      panelClass = "",
      titleId,
      kicker,
      title,
      primaryHtml = "",
      notesHtml = "",
      gridHtml = "",
      actionsHtml = "",
    } = options || {};

    return `
      <div class="${panelClass} ccs-shared-panel" role="dialog" aria-modal="true" aria-labelledby="${titleId}">
        <div class="ccs-shared-head">
          <div>
            <div class="ccs-shared-kicker">${kicker}</div>
            <div class="ccs-shared-title" id="${titleId}">${title}</div>
          </div>
          <button type="button" class="ccs-shared-close" aria-label="关闭">×</button>
        </div>
        <div class="ccs-shared-overview">
          <div class="ccs-shared-primary-fields">${primaryHtml}</div>
          <div class="ccs-shared-notes">${notesHtml}</div>
        </div>
        <div class="ccs-shared-grid">${gridHtml}</div>
        <div class="ccs-shared-actions">${actionsHtml}</div>
      </div>
    `;
  }

  window.CCSwitchSharedUI = {
    DEFAULT_APP,
    injectStyleOnce,
    injectSharedDialogStyles,
    inferName,
    getDefaultNameByApp,
    updateCodexNameHint,
    bindAutoSelect,
    buildDialogShell,
  };
})();