ccswitch-shared-ui.local

shared ui code

Dit script moet niet direct worden geïnstalleerd - het is een bibliotheek voor andere scripts om op te nemen met de meta-richtlijn // @require https://update.greasyfork.org/scripts/582598/1850862/ccswitch-shared-uilocal.js

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// 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,
  };
})();