fxdyz
This commit is contained in:
@@ -7,7 +7,15 @@ const state = {
|
||||
servers: [],
|
||||
events: {},
|
||||
session_control: { mode: "all", entries: [] },
|
||||
legacy_bilei: [],
|
||||
token_stats: null,
|
||||
cache: {
|
||||
defaults: { api: 300, image: 300 },
|
||||
limits: { api_memory_entries: 256, image_max_mb: 512 },
|
||||
api: [],
|
||||
images: [],
|
||||
stats: {},
|
||||
},
|
||||
};
|
||||
const editing = { bindingSession: null, controlSession: null, aliasServer: null, kungfuPzid: null };
|
||||
const restoreConfirmationTimers = new WeakMap();
|
||||
@@ -21,6 +29,14 @@ function formatUsageCount(value) {
|
||||
: "—";
|
||||
}
|
||||
|
||||
function formatBytes(value) {
|
||||
const bytes = Number(value) || 0;
|
||||
if (bytes < 1024) return `${bytes} B`;
|
||||
if (bytes < 1024 ** 2) return `${(bytes / 1024).toFixed(1)} KB`;
|
||||
if (bytes < 1024 ** 3) return `${(bytes / 1024 ** 2).toFixed(1)} MB`;
|
||||
return `${(bytes / 1024 ** 3).toFixed(1)} GB`;
|
||||
}
|
||||
|
||||
function renderTokenStats() {
|
||||
const stats = state.token_stats;
|
||||
byId("token-level").textContent = Number.isSafeInteger(stats?.level)
|
||||
@@ -392,6 +408,76 @@ function renderBindings() {
|
||||
}));
|
||||
}
|
||||
|
||||
function renderLegacyBilei() {
|
||||
const records = state.legacy_bilei || [];
|
||||
const body = byId("legacy-bilei-body");
|
||||
byId("legacy-bilei-count").textContent = String(records.length);
|
||||
if (!records.length) {
|
||||
body.replaceChildren(emptyRow(7, "没有待迁移的旧避雷数据"));
|
||||
return;
|
||||
}
|
||||
|
||||
body.replaceChildren(...records.map((item) => {
|
||||
const row = document.createElement("tr");
|
||||
const id = document.createElement("td");
|
||||
const name = document.createElement("td");
|
||||
const note = document.createElement("td");
|
||||
const time = document.createElement("td");
|
||||
const user = document.createElement("td");
|
||||
const target = document.createElement("td");
|
||||
const actions = document.createElement("td");
|
||||
id.dataset.label = "ID";
|
||||
name.dataset.label = "避雷名称";
|
||||
note.dataset.label = "避雷备注";
|
||||
time.dataset.label = "时间";
|
||||
user.dataset.label = "记录人";
|
||||
target.dataset.label = "目标会话";
|
||||
actions.dataset.label = "操作";
|
||||
id.textContent = String(item.id);
|
||||
name.textContent = item.name || "—";
|
||||
note.textContent = item.text || "—";
|
||||
note.className = "legacy-note-cell";
|
||||
time.textContent = item.time || "—";
|
||||
user.textContent = item.user || "—";
|
||||
target.className = "legacy-session-cell";
|
||||
actions.className = "actions";
|
||||
|
||||
const sessionInput = document.createElement("input");
|
||||
sessionInput.className = "inline-editor";
|
||||
sessionInput.maxLength = 512;
|
||||
sessionInput.required = true;
|
||||
sessionInput.setAttribute("list", "session-options");
|
||||
sessionInput.setAttribute("aria-label", `避雷记录 ${item.id} 的目标会话`);
|
||||
sessionInput.placeholder = "选择已有会话或直接输入";
|
||||
target.append(sessionInput);
|
||||
|
||||
const migrateButton = button("迁移", "", async () => {
|
||||
if (!sessionInput.reportValidity()) return;
|
||||
sessionInput.disabled = true;
|
||||
migrateButton.disabled = true;
|
||||
const migrated = await mutate(
|
||||
"bilei/legacy/migrate",
|
||||
{ id: item.id, session_id: sessionInput.value },
|
||||
`避雷记录 ${item.id} 已迁移`,
|
||||
);
|
||||
if (!migrated) {
|
||||
sessionInput.disabled = false;
|
||||
migrateButton.disabled = false;
|
||||
sessionInput.focus();
|
||||
}
|
||||
});
|
||||
sessionInput.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
migrateButton.click();
|
||||
}
|
||||
});
|
||||
actions.append(migrateButton);
|
||||
row.append(id, name, note, time, user, target, actions);
|
||||
return row;
|
||||
}));
|
||||
}
|
||||
|
||||
function renderSubscriptions() {
|
||||
const body = byId("subscriptions-body");
|
||||
const bindings = bindingMap();
|
||||
@@ -552,15 +638,134 @@ function renderKungfu() {
|
||||
}));
|
||||
}
|
||||
|
||||
function cacheSettingRow(cacheType, item) {
|
||||
const row = document.createElement("tr");
|
||||
const name = document.createElement("td");
|
||||
const ttl = document.createElement("td");
|
||||
const status = document.createElement("td");
|
||||
const actions = document.createElement("td");
|
||||
name.dataset.label = cacheType === "api" ? "接口路径" : "图片指令";
|
||||
ttl.dataset.label = "缓存时间(秒)";
|
||||
status.dataset.label = "配置状态";
|
||||
actions.dataset.label = "操作";
|
||||
name.textContent = item.name;
|
||||
name.className = "cache-name-cell";
|
||||
actions.className = "actions";
|
||||
|
||||
const input = document.createElement("input");
|
||||
input.className = "inline-editor inline-editor--ttl";
|
||||
input.type = "number";
|
||||
input.min = "0";
|
||||
input.max = "2592000";
|
||||
input.step = "1";
|
||||
input.required = true;
|
||||
input.value = String(item.ttl_seconds);
|
||||
input.setAttribute("aria-label", `${item.name}缓存时间(秒)`);
|
||||
ttl.append(input);
|
||||
|
||||
const badge = document.createElement("span");
|
||||
badge.className = `cache-badge ${item.overridden ? "cache-badge--custom" : ""}`.trim();
|
||||
badge.textContent = item.overridden
|
||||
? "独立设置"
|
||||
: item.safe_default
|
||||
? "安全默认"
|
||||
: "继承默认";
|
||||
status.append(badge);
|
||||
|
||||
const saveButton = button("保存", "", async () => {
|
||||
if (!input.reportValidity()) return;
|
||||
saveButton.disabled = true;
|
||||
restoreButton.disabled = true;
|
||||
const saved = await mutate(
|
||||
"cache/settings/save",
|
||||
{ cache_type: cacheType, cache_name: item.name, ttl_seconds: Number(input.value) },
|
||||
`${item.name}缓存时间已保存`,
|
||||
);
|
||||
if (!saved) {
|
||||
saveButton.disabled = false;
|
||||
restoreButton.disabled = false;
|
||||
}
|
||||
});
|
||||
const restoreButton = button("恢复默认", "", async () => {
|
||||
restoreButton.disabled = true;
|
||||
saveButton.disabled = true;
|
||||
const saved = await mutate(
|
||||
"cache/settings/save",
|
||||
{ cache_type: cacheType, cache_name: item.name, inherit: true },
|
||||
`${item.name}已恢复默认时间`,
|
||||
);
|
||||
if (!saved) {
|
||||
restoreButton.disabled = false;
|
||||
saveButton.disabled = false;
|
||||
}
|
||||
});
|
||||
const clearButton = button("清除此项", "link-button--danger", async () => {
|
||||
clearButton.disabled = true;
|
||||
const cleared = await mutate(
|
||||
"cache/item/clear",
|
||||
{ cache_type: cacheType, cache_name: item.name },
|
||||
`${item.name}缓存已清除,下次调用将重新生成`,
|
||||
);
|
||||
if (!cleared) clearButton.disabled = false;
|
||||
});
|
||||
restoreButton.disabled = !item.overridden;
|
||||
input.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
saveButton.click();
|
||||
}
|
||||
});
|
||||
actions.append(saveButton, restoreButton, clearButton);
|
||||
row.append(name, ttl, status, actions);
|
||||
return row;
|
||||
}
|
||||
|
||||
function renderCacheTable(cacheType) {
|
||||
const isApi = cacheType === "api";
|
||||
const items = isApi ? state.cache.api : state.cache.images;
|
||||
const filter = byId(isApi ? "api-cache-filter" : "image-cache-filter")
|
||||
.value.trim().toLocaleLowerCase("zh-CN");
|
||||
const visible = items.filter((item) => item.name.toLocaleLowerCase("zh-CN").includes(filter));
|
||||
const body = byId(isApi ? "api-cache-settings-body" : "image-cache-settings-body");
|
||||
body.replaceChildren(...(
|
||||
visible.length
|
||||
? visible.map((item) => cacheSettingRow(cacheType, item))
|
||||
: [emptyRow(4, filter ? "没有匹配的缓存项目" : "暂无缓存项目")]
|
||||
));
|
||||
}
|
||||
|
||||
function renderCache() {
|
||||
const cache = state.cache || { defaults: {}, limits: {}, api: [], images: [], stats: {} };
|
||||
const stats = cache.stats || {};
|
||||
const apiDefault = cache.defaults?.api ?? 300;
|
||||
const imageDefault = cache.defaults?.image ?? 600;
|
||||
const memoryLimit = cache.limits?.api_memory_entries ?? stats.api_memory_limit ?? 256;
|
||||
const imageLimitMb = cache.limits?.image_max_mb ?? 512;
|
||||
byId("api-cache-count").textContent = `${stats.api_count || 0} 条`;
|
||||
byId("api-cache-size").textContent = formatBytes(stats.api_size_bytes);
|
||||
byId("image-cache-count").textContent = `${stats.image_count || 0} 张`;
|
||||
byId("image-cache-size").textContent = `${formatBytes(stats.image_size_bytes)} / ${formatBytes(stats.image_limit_bytes)}`;
|
||||
byId("api-default-ttl").value = String(apiDefault);
|
||||
byId("image-default-ttl").value = String(imageDefault);
|
||||
byId("api-memory-limit").value = String(memoryLimit);
|
||||
byId("image-size-limit").value = String(imageLimitMb);
|
||||
byId("api-memory-summary").textContent = `${stats.api_memory_count || 0} / ${memoryLimit} 条`;
|
||||
byId("cache-default-summary").textContent = `${apiDefault} / ${imageDefault} 秒`;
|
||||
renderCacheTable("api");
|
||||
renderCacheTable("image");
|
||||
}
|
||||
|
||||
function render() {
|
||||
renderTokenStats();
|
||||
renderServerOptions();
|
||||
renderSessionOptions();
|
||||
renderSessionControl();
|
||||
renderLegacyBilei();
|
||||
renderBindings();
|
||||
renderSubscriptions();
|
||||
renderAliases();
|
||||
renderKungfu();
|
||||
renderCache();
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
@@ -691,6 +896,72 @@ byId("restore-kungfu").addEventListener("click", async (event) => {
|
||||
);
|
||||
});
|
||||
|
||||
byId("api-cache-filter").addEventListener("input", () => renderCacheTable("api"));
|
||||
byId("image-cache-filter").addEventListener("input", () => renderCacheTable("image"));
|
||||
|
||||
byId("cache-default-form").addEventListener("submit", async (event) => {
|
||||
event.preventDefault();
|
||||
const submit = event.currentTarget.querySelector('button[type="submit"]');
|
||||
submit.disabled = true;
|
||||
try {
|
||||
await bridge.apiPost("cache/settings/save", {
|
||||
cache_type: "api",
|
||||
cache_name: "*",
|
||||
ttl_seconds: Number(byId("api-default-ttl").value),
|
||||
});
|
||||
await bridge.apiPost("cache/settings/save", {
|
||||
cache_type: "image",
|
||||
cache_name: "*",
|
||||
ttl_seconds: Number(byId("image-default-ttl").value),
|
||||
});
|
||||
await loadData();
|
||||
showToast("默认缓存时间已保存");
|
||||
} catch (error) {
|
||||
showToast(error?.message || "默认缓存时间保存失败", true);
|
||||
} finally {
|
||||
submit.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
byId("cache-limit-form").addEventListener("submit", async (event) => {
|
||||
event.preventDefault();
|
||||
const submit = event.currentTarget.querySelector('button[type="submit"]');
|
||||
submit.disabled = true;
|
||||
try {
|
||||
await bridge.apiPost("cache/limits/save", {
|
||||
api_memory_entries: Number(byId("api-memory-limit").value),
|
||||
image_max_mb: Number(byId("image-size-limit").value),
|
||||
});
|
||||
await loadData();
|
||||
showToast("缓存容量限制已保存并立即生效");
|
||||
} catch (error) {
|
||||
showToast(error?.message || "缓存容量限制保存失败", true);
|
||||
} finally {
|
||||
submit.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
async function clearCache(cacheType, control) {
|
||||
control.disabled = true;
|
||||
try {
|
||||
const result = await bridge.apiPost("cache/clear", { cache_type: cacheType });
|
||||
await loadData();
|
||||
const removed = result?.removed?.[cacheType] ?? 0;
|
||||
showToast(`${cacheType === "api" ? "接口" : "图片"}缓存已清空,共清理 ${removed} 项`);
|
||||
} catch (error) {
|
||||
showToast(error?.message || "缓存清理失败", true);
|
||||
} finally {
|
||||
control.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
byId("clear-api-cache").addEventListener("click", (event) => {
|
||||
clearCache("api", event.currentTarget);
|
||||
});
|
||||
byId("clear-image-cache").addEventListener("click", (event) => {
|
||||
clearCache("image", event.currentTarget);
|
||||
});
|
||||
|
||||
byId("refresh").addEventListener("click", async (event) => {
|
||||
const control = event.currentTarget;
|
||||
control.disabled = true;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<header class="page-header">
|
||||
<div>
|
||||
<h1>剑网三插件管理</h1>
|
||||
<p>管理事件推送、会话访问范围、区服绑定及别名。</p>
|
||||
<p>管理会话访问范围、查询缓存、避雷迁移、事件推送、区服绑定及别名。</p>
|
||||
</div>
|
||||
<button class="button button--secondary" id="refresh" type="button">刷新数据</button>
|
||||
</header>
|
||||
@@ -42,6 +42,8 @@
|
||||
<button class="tab" type="button" role="tab" aria-selected="false" data-tab="bindings">区服绑定</button>
|
||||
<button class="tab" type="button" role="tab" aria-selected="false" data-tab="aliases">区服别名</button>
|
||||
<button class="tab" type="button" role="tab" aria-selected="false" data-tab="kungfu">心法别名</button>
|
||||
<button class="tab" type="button" role="tab" aria-selected="false" data-tab="cache">缓存管理</button>
|
||||
<button class="tab" type="button" role="tab" aria-selected="false" data-tab="bilei-migration">避雷迁移</button>
|
||||
</nav>
|
||||
|
||||
<section class="panel is-active" id="session-control-panel" role="tabpanel">
|
||||
@@ -100,6 +102,93 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel" id="cache-panel" role="tabpanel" hidden>
|
||||
<div class="section-intro section-intro--actions">
|
||||
<div>
|
||||
<h2>查询与图片缓存</h2>
|
||||
<p>接口数据以 JSON 保存到 SQLite,渲染图片保存到插件数据目录。图片命中时会直接发送,不再请求其上游接口;缓存时间使用秒,填写 0 可关闭对应缓存。</p>
|
||||
</div>
|
||||
<div class="cache-clear-actions">
|
||||
<button class="button button--secondary" id="clear-api-cache" type="button">清空接口缓存</button>
|
||||
<button class="button button--secondary" id="clear-image-cache" type="button">清空图片缓存</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cache-summary" aria-label="缓存统计">
|
||||
<div class="cache-stat"><span>接口缓存(SQLite)</span><strong id="api-cache-count">0 条</strong><small id="api-cache-size">0 B</small></div>
|
||||
<div class="cache-stat"><span>图片缓存</span><strong id="image-cache-count">0 张</strong><small id="image-cache-size">0 B</small></div>
|
||||
<div class="cache-stat"><span>接口内存缓存</span><strong id="api-memory-summary">0 / 256 条</strong><small>最久未使用优先淘汰</small></div>
|
||||
<div class="cache-stat"><span>默认缓存时间</span><strong id="cache-default-summary">300 / 600 秒</strong><small>接口 / 图片</small></div>
|
||||
</div>
|
||||
|
||||
<form class="cache-defaults" id="cache-default-form">
|
||||
<label>
|
||||
<span>接口数据默认缓存时间(秒)</span>
|
||||
<input id="api-default-ttl" type="number" min="0" max="2592000" step="1" required />
|
||||
</label>
|
||||
<label>
|
||||
<span>图片默认缓存时间(秒)</span>
|
||||
<input id="image-default-ttl" type="number" min="0" max="2592000" step="1" required />
|
||||
</label>
|
||||
<button class="button button--primary" type="submit">保存默认时间</button>
|
||||
</form>
|
||||
|
||||
<form class="cache-defaults cache-limits" id="cache-limit-form">
|
||||
<label>
|
||||
<span>接口内存缓存最大条数</span>
|
||||
<input id="api-memory-limit" type="number" min="1" max="100000" step="1" required />
|
||||
</label>
|
||||
<label>
|
||||
<span>图片缓存最大容量(MB)</span>
|
||||
<input id="image-size-limit" type="number" min="1" max="10240" step="1" required />
|
||||
</label>
|
||||
<button class="button button--primary" type="submit">保存容量限制</button>
|
||||
</form>
|
||||
|
||||
<details class="cache-group" open>
|
||||
<summary>JX3API 接口数据缓存</summary>
|
||||
<div class="cache-toolbar">
|
||||
<input id="api-cache-filter" type="search" placeholder="筛选接口路径" aria-label="筛选接口路径" />
|
||||
<span>单项设置优先;恢复后通常继承全局时间,“安全默认”项目恢复为 0 秒。</span>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>接口路径</th><th>缓存时间(秒)</th><th>配置状态</th><th class="actions">操作</th></tr></thead>
|
||||
<tbody id="api-cache-settings-body"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="cache-group" open>
|
||||
<summary>渲染图片缓存</summary>
|
||||
<div class="cache-toolbar">
|
||||
<input id="image-cache-filter" type="search" placeholder="筛选图片指令" aria-label="筛选图片指令" />
|
||||
<span>图片缓存时间决定最终结果的新鲜度;过期后才重新请求数据并渲染。</span>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>图片指令</th><th>缓存时间(秒)</th><th>配置状态</th><th class="actions">操作</th></tr></thead>
|
||||
<tbody id="image-cache-settings-body"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section class="panel" id="bilei-migration-panel" role="tabpanel" hidden>
|
||||
<div class="section-intro">
|
||||
<div>
|
||||
<h2>迁移旧避雷数据</h2>
|
||||
<p>旧版记录暂存在历史公共数据区。请为每条记录选择或输入目标会话,迁移后该记录仅能在目标会话中访问。剩余 <strong id="legacy-bilei-count">0</strong> 条。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>ID</th><th>避雷名称</th><th>避雷备注</th><th>时间</th><th>记录人</th><th>目标会话</th><th class="actions">操作</th></tr></thead>
|
||||
<tbody id="legacy-bilei-body"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel" id="bindings-panel" role="tabpanel" hidden>
|
||||
<form class="editor" id="binding-form">
|
||||
<label>
|
||||
|
||||
@@ -86,6 +86,23 @@ input:focus, select:focus { border-color: var(--focus); box-shadow: 0 0 0 3px co
|
||||
.section-intro { padding: 22px; border-bottom: 1px solid var(--border); }
|
||||
.section-intro--actions { display: flex; align-items: center; justify-content: space-between; gap: 20px; }
|
||||
.section-intro h2 { margin: 0; font-size: 18px; }
|
||||
.cache-clear-actions { display: flex; flex: 0 0 auto; justify-content: flex-end; gap: 10px; }
|
||||
.cache-summary { display: grid; grid-template-columns: repeat(4, 1fr); border-bottom: 1px solid var(--border); background: var(--surface-subtle); }
|
||||
.cache-stat { display: flex; min-height: 100px; flex-direction: column; justify-content: center; padding: 18px 22px; }
|
||||
.cache-stat + .cache-stat { border-left: 1px solid var(--border); }
|
||||
.cache-stat span, .cache-stat small { color: var(--muted); font-size: 12px; font-weight: 650; }
|
||||
.cache-stat strong { margin: 3px 0; font-size: 21px; font-variant-numeric: tabular-nums; }
|
||||
.cache-defaults { display: grid; grid-template-columns: minmax(240px, 1fr) minmax(240px, 1fr) auto; align-items: end; gap: 16px; padding: 22px; border-bottom: 1px solid var(--border); }
|
||||
.cache-group { border-bottom: 1px solid var(--border); }
|
||||
.cache-group:last-child { border-bottom: 0; }
|
||||
.cache-group summary { padding: 18px 22px; cursor: pointer; font-size: 16px; font-weight: 750; }
|
||||
.cache-group summary:hover { background: var(--surface-subtle); }
|
||||
.cache-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 18px; padding: 0 22px 16px; color: var(--muted); font-size: 13px; }
|
||||
.cache-toolbar input { max-width: 360px; }
|
||||
.cache-name-cell { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; text-align: left; }
|
||||
.inline-editor--ttl { min-width: 130px; max-width: 180px; }
|
||||
.cache-badge { display: inline-flex; padding: 3px 9px; border-radius: 999px; background: color-mix(in srgb, var(--muted) 10%, transparent); color: var(--muted); font-size: 12px; font-weight: 700; }
|
||||
.cache-badge--custom { background: var(--accent-soft); color: var(--accent); }
|
||||
.table-wrap { overflow-x: auto; }
|
||||
table { width: 100%; table-layout: auto; border-collapse: collapse; }
|
||||
th, td { padding: 15px 20px; text-align: center; vertical-align: middle; border-bottom: 1px solid var(--border); }
|
||||
@@ -94,6 +111,8 @@ tbody tr:last-child td { border-bottom: 0; }
|
||||
tbody tr:hover { background: color-mix(in srgb, var(--accent-soft) 42%, transparent); }
|
||||
tbody tr.is-editing { background: color-mix(in srgb, var(--accent-soft) 62%, transparent); }
|
||||
.alias-cell { min-width: 320px; }
|
||||
.legacy-note-cell { min-width: 260px; max-width: 420px; text-align: left; white-space: pre-wrap; overflow-wrap: anywhere; }
|
||||
.legacy-session-cell { min-width: 300px; }
|
||||
.inline-editor { min-width: 260px; }
|
||||
.inline-editor--compact { min-width: 130px; }
|
||||
.actions { white-space: nowrap; }
|
||||
@@ -120,6 +139,13 @@ tbody tr.is-editing { background: color-mix(in srgb, var(--accent-soft) 62%, tra
|
||||
.shell { width: min(100% - 24px, 1260px); padding-top: 20px; }
|
||||
.page-header { align-items: stretch; flex-direction: column; }
|
||||
.section-intro--actions { align-items: stretch; flex-direction: column; }
|
||||
.cache-clear-actions { display: flex; justify-content: flex-end; }
|
||||
.cache-summary { grid-template-columns: repeat(2, 1fr); }
|
||||
.cache-stat:nth-child(3) { border-top: 1px solid var(--border); border-left: 0; }
|
||||
.cache-stat:nth-child(4) { border-top: 1px solid var(--border); }
|
||||
.cache-defaults { grid-template-columns: 1fr; }
|
||||
.cache-toolbar { align-items: stretch; flex-direction: column; }
|
||||
.cache-toolbar input { max-width: none; }
|
||||
.token-summary { grid-template-columns: repeat(2, 1fr); }
|
||||
.token-stat { min-height: 88px; padding: 15px 18px; }
|
||||
.token-stat:nth-child(3) { border-top: 1px solid var(--border); border-left: 0; }
|
||||
@@ -139,8 +165,9 @@ tbody tr.is-editing { background: color-mix(in srgb, var(--accent-soft) 62%, tra
|
||||
tbody tr:last-child { border-bottom: 0; }
|
||||
td { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 12px; padding: 8px 16px; border: 0; overflow-wrap: anywhere; }
|
||||
td::before { color: var(--muted); content: attr(data-label); font-size: 12px; font-weight: 700; }
|
||||
td.actions { grid-template-columns: max-content auto auto; align-items: center; width: 100%; white-space: normal; }
|
||||
.alias-cell, .inline-editor { min-width: 0; }
|
||||
td.actions { grid-template-columns: max-content repeat(3, auto); align-items: center; width: 100%; white-space: normal; }
|
||||
.alias-cell, .legacy-note-cell, .legacy-session-cell, .inline-editor { min-width: 0; max-width: none; }
|
||||
.cache-name-cell { text-align: right; }
|
||||
td.empty { display: block; padding: 36px 16px; }
|
||||
td.empty::before { content: none; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user