ccswitch-shared-ui.local

shared ui code

ეს სკრიპტი არ უნდა იყოს პირდაპირ დაინსტალირებული. ეს ბიბლიოთეკაა, სხვა სკრიპტებისთვის უნდა ჩართეთ მეტა-დირექტივაში // @require https://update.greasyfork.org/scripts/582598/1850862/ccswitch-shared-uilocal.js.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

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