shared ui code
Skrip ini tidak untuk dipasang secara langsung. Ini adalah pustaka skrip lain untuk disertakan dengan direktif meta // @require https://update.greasyfork.org/scripts/582598/1850862/ccswitch-shared-uilocal.js
// 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,
};
})();