5 Commits
Author SHA1 Message Date
jinghui 40f335331b v1.10.5: 侧边栏标题旁显示版本号(读 manifest,不写死) 2026-10-21 00:03:47 +08:00
jinghui 9c52bc4178 v1.10.4: 侧边栏授权状态不截断 + 购买授权改独占一行醒目按钮 2026-10-20 23:45:03 +08:00
jinghui 226e4fbbf9 v1.10.3: 修「买完仍显示试用倒计时」与「到期时间显示永久」
根因:CardAuth 自动发卡只写 ca_cards(status=used + bind_info.machine_id),
从不写 ca_authorizations,而插件只查 /public/authorizations/query → 已购用户恒为空。

- checkLicense 增加卡密直验兜底:用本地记住的卡密调 /public/verify,
  返回 valid/expire_time/is_permanent,直接作为授权状态与到期时间
- activateCard 成功即持久化 activeCardKey + activeCardInfo(换机/过期自动清除)
- buy.js 不再用「永久」兜底到期时间(订单接口不返回 expire_time),
  改为只在服务端明确给出时显示,否则显示套餐名
- 侧边栏已授权态显示套餐名 + 真实到期时间
2026-10-20 23:32:43 +08:00
jinghui 99d1d1fe92 v1.10.2: 侧边栏「没有模型?注册即送额度」移到 LLM 配置正上方 2026-10-20 22:25:55 +08:00
jinghui 32f48613c4 v1.10.1: 支付成功即显示卡密 + 已支付未发卡的明确状态
- 订单状态区新增「你的卡密」展示区,拿到 card_key 立刻显示(不等激活结果)
- 自动激活失败时给出手动激活路径(卡密+侧边栏步骤+订单号)
- paid 但无卡密时显示发卡进度,90s 后提示权益不丢失可凭订单号补发
- 卡密一键复制(clipboard API → execCommand 降级)
2026-10-20 22:01:47 +08:00
8 changed files with 275 additions and 18 deletions

No files matched your search

+47
View File
@@ -4,6 +4,53 @@ B站评论助手 版本历史。下载:https://dl.88531.cn/bili.html · 
--- ---
## v1.10.5 · 2026-10-20
### ✨ 新功能
- 侧边栏顶部标题旁显示**当前版本号**(半透明白色胶囊,如 `B站评论助手 v1.10.5`),直接读 manifest,升级后自动跟着变、不用改代码
---
## v1.10.4 · 2026-10-20
### 🔧 调整
- 侧边栏授权状态**不再被截断**:去掉 `nowrap + ellipsis`,像「免费试用中 · 剩余 71:59:27 · 还可评论 12 条」这种长状态现在完整换行显示,不再变成 `…还可评...`
- **「购买授权」改成独占一行的醒目按钮**(渐变粉红底 + 白字 + 阴影,通栏 40px 高),不再挤在状态行右侧当一个小链接
---
## v1.10.3 · 2026-10-20
### 🐛 修复
- **买完仍显示试用倒计时**:授权系统的自动发卡只把卡密标成「已使用」并绑定机器码,**从不写入授权表**,而插件原来只查授权表 → 已购用户永远查不到自己的授权、倒计时照旧。现在插件改用**本地记住的卡密直接向卡密系统校验**(`/public/verify`,它读的是卡表),拿到真实授权状态与到期时间
- **到期时间显示成「永久」**:购买页原先是 `到期时间 || '永久'` 兜底,而订单接口根本不返回到期时间 → **买 1 天也显示永久**。现在到期时间只在服务端明确给出时才显示,「永久」只在卡类型确为永久时出现
### ✨ 新功能
- 激活成功后插件**永久记住这张卡**(换机绑定或过期时自动清除),不再依赖服务端授权表
- 侧边栏已授权状态显示**套餐名 + 真实到期时间**:`已授权 · 1天试用 · 到期 2026-10-21 23:06:36`
---
## v1.10.2 · 2026-10-20
### 🔧 调整
- 侧边栏「🤖 没有模型?注册即送额度」**移到 LLM 配置正上方**——原先它卡在授权区和评论记录之间,和 LLM 之间隔着一大块,现在配模型时一眼就能看到注册送额度的入口
---
## v1.10.1 · 2026-10-20
### ✨ 新功能
- **支付成功后立刻显示卡密**:只要服务端发出卡,订单状态区马上出现独立的「你的卡密」展示区(大号等宽字体 + 一键复制),**不等自动激活结果**——自动激活失败时用户手里也有码,回侧边栏粘贴即可激活
- **「已支付但还没发卡」的明确状态**:订单已是 `paid` 却还没拿到卡密时显示「已支付,正在为你发卡」,超过 90 秒改为「**权益不会丢失**,可凭订单号联系客服补发」,不再无限转在「等待支付中」
- 卡密一键复制,剪贴板 API 无权限时自动降级 `execCommand`
### 🔧 调整
- 激活失败时给出可执行路径(卡密展示 + 侧边栏操作步骤 + 订单号),不再只报一句错误
- 卡密展示区用 B站蓝虚线框独立成块,`user-select: all` 便于长按全选
---
## v1.10.0 · 2026-10-20 ## v1.10.0 · 2026-10-20
### ✨ 新功能 ### ✨ 新功能
+56 -1
View File
@@ -125,6 +125,36 @@ async function checkLicense() {
offline = true; // 网络/超时 → 放行 offline = true; // 网络/超时 → 放行
} }
// 兜底:卡密直验。自动发卡不写 ca_authorizations,上面的查询对已购用户恒为空;
// 用本地记住的卡密直接调 /public/verify(它读 ca_cards,返回真实到期时间)。
let cardInfo = null;
if (!authorized) {
try {
const saved = await chrome.storage.local.get(["activeCardKey"]);
const key = String(saved.activeCardKey || "").trim();
if (key) {
const v = await authFetch("/public/verify", {
method: "POST",
headers: { "Content-Type": "application/json", "X-Api-Key": CARD_API_KEY },
body: JSON.stringify({
card_key: key,
machine_id: machine,
client_version: chrome.runtime.getManifest().version
})
}, 12000);
const vd = (v && v.data) || {};
if (v && v.code === 200 && vd.valid) {
authorized = true;
expire = vd.expire_time || "";
cardInfo = { type: vd.type || "", expireTime: expire, isPermanent: !!vd.is_permanent, remainingDays: vd.remaining_days };
} else if (v && v.code === 200 && vd.valid === false) {
// 换机绑定或已过期:清掉本地卡密,别每次都拿废卡打接口
await chrome.storage.local.remove(["activeCardKey", "activeCardInfo"]);
}
}
} catch (e) { /* 直验失败不影响试用判定 */ }
}
const st = await chrome.storage.local.get(["trialStart", "trialComments"]); const st = await chrome.storage.local.get(["trialStart", "trialComments"]);
let trialStart = Number(st.trialStart) || 0; let trialStart = Number(st.trialStart) || 0;
if (!trialStart) { if (!trialStart) {
@@ -146,6 +176,7 @@ async function checkLicense() {
offline: offline, offline: offline,
realAuthorized: authorized, realAuthorized: authorized,
expire: expire, expire: expire,
cardInfo: cardInfo,
machine: machine, machine: machine,
trialStart: trialStart, trialStart: trialStart,
trialEnd: trialEnd, trialEnd: trialEnd,
@@ -172,7 +203,27 @@ async function activateCard(cardKey) {
}, 12000); }, 12000);
const d = (r && r.data) || {}; const d = (r && r.data) || {};
if (r && r.code === 200 && d.valid) { if (r && r.code === 200 && d.valid) {
return { ok: true, message: d.message || "激活成功", days: d.duration_days || 0 }; // 记住这张卡:CardAuth 的自动发卡只把 ca_cards 标成 used 并绑定机器码,
// 从不写 ca_authorizations,所以 /public/authorizations/query 对已购用户恒为空。
// 插件自己留着卡密,才能持续向 /public/verify 校验真实授权状态。
const key = String(cardKey || "").trim();
await chrome.storage.local.set({
activeCardKey: key,
activeCardInfo: {
type: d.type || "",
durationDays: Number(d.duration_days) || 0,
expireTime: d.expire_time || "",
isPermanent: !!d.is_permanent,
activatedAt: Date.now()
}
});
return {
ok: true, message: d.message || "激活成功",
days: d.duration_days || 0,
cardType: d.type || "",
expireTime: d.expire_time || "",
isPermanent: !!d.is_permanent
};
} }
return { ok: false, message: (d && d.message) || (r && r.message) || "激活失败" }; return { ok: false, message: (d && d.message) || (r && r.message) || "激活失败" };
} }
@@ -623,6 +674,10 @@ async function authQueryOrder(orderNo) {
const act = await activateCard(out.cardKey); const act = await activateCard(out.cardKey);
out.activated = !!(act && act.ok); out.activated = !!(act && act.ok);
out.message = (act && act.message) || ""; out.message = (act && act.message) || "";
// /public/orders/query 不返回到期时间,verify 返回的才是真值——别用「永久」兜底
out.expireTime = (act && act.expireTime) || "";
out.isPermanent = !!(act && act.isPermanent);
out.planName = d.card_type_name || "";
if (out.activated) { if (out.activated) {
await chrome.storage.local.remove(["pendingOrder"]); await chrome.storage.local.remove(["pendingOrder"]);
await clearOrderAlarm(); await clearOrderAlarm();
+70
View File
@@ -126,6 +126,76 @@ h2 {
.order-status.error { color: #c62828; background: #fdecea; } .order-status.error { color: #c62828; background: #fdecea; }
.order-status.success { color: #1b5e20; background: #eef7ee; } .order-status.success { color: #1b5e20; background: #eef7ee; }
/* 卡密展示区:支付成功后必须让用户亲眼看到卡密 */
.card-box {
margin-top: 14px;
border: 2px dashed #00a1d6;
border-radius: 10px;
background: #f2fbff;
padding: 16px 14px;
word-break: break-all;
}
.cb-title {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
font-weight: 700;
color: #0089ba;
margin-bottom: 10px;
}
.cb-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #00a1d6;
flex: none;
box-shadow: 0 0 0 4px rgba(0, 161, 214, 0.18);
}
.cb-key {
background: #fff;
border: 1px solid #cdeaf7;
border-radius: 8px;
padding: 14px 12px;
text-align: center;
margin-bottom: 12px;
}
.cb-key code {
font-family: "SF Mono", Consolas, "Courier New", monospace;
font-size: 20px;
font-weight: 700;
letter-spacing: 2px;
color: #111;
user-select: all;
}
.cb-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.cb-copy {
padding: 9px 22px;
border: none;
border-radius: 6px;
background: #00a1d6;
color: #fff;
font-size: 14px;
cursor: pointer;
transition: background 0.18s ease;
}
.cb-copy:hover { background: #0089ba; }
.cb-tip { font-size: 12px; color: #0089ba; }
.cb-hint {
margin-top: 10px;
font-size: 12px;
line-height: 1.7;
color: #7a7f86;
}
.ok-title { font-size: 16px; font-weight: 700; margin-bottom: 8px; } .ok-title { font-size: 16px; font-weight: 700; margin-bottom: 8px; }
.ok-line { font-size: 13px; margin-bottom: 4px; } .ok-line { font-size: 13px; margin-bottom: 4px; }
.ok-line b { color: #1b5e20; } .ok-line b { color: #1b5e20; }
+9
View File
@@ -34,6 +34,15 @@
<section id="order-panel" hidden> <section id="order-panel" hidden>
<h2><span class="num">3</span> 订单状态</h2> <h2><span class="num">3</span> 订单状态</h2>
<div id="order-status" class="order-status">等待创建订单…</div> <div id="order-status" class="order-status">等待创建订单…</div>
<div id="card-box" class="card-box" hidden>
<div class="cb-title"><span class="cb-dot"></span>你的卡密</div>
<div class="cb-key"><code id="cb-key-text"></code></div>
<div class="cb-row">
<button type="button" id="cb-copy" class="cb-copy">复制卡密</button>
<span id="cb-copy-tip" class="cb-tip"></span>
</div>
<p class="cb-hint" id="cb-hint">万一自动激活没成功:回到插件侧边栏,在卡密输入框粘贴并点「粘贴卡密并激活」即可,不影响你的权益。</p>
</div>
</section> </section>
</main> </main>
+66 -4
View File
@@ -99,20 +99,36 @@ function startPolling() {
try { try {
const res = await chrome.runtime.sendMessage({ action: 'authQueryOrder', orderNo }); const res = await chrome.runtime.sendMessage({ action: 'authQueryOrder', orderNo });
if (!res || !res.ok) return; if (!res || !res.ok) return;
// 只要拿到卡密就先展示出来,不等激活结果——自动激活失败时用户手里必须有码
if (res.cardKey) showCardBox(res.cardKey, !!res.activated);
if (res.activated) { if (res.activated) {
clearInterval(pollTimer); clearInterval(pollTimer);
pollTimer = null; pollTimer = null;
showActivated(res); showActivated(res);
} else if (res.cardKey && !res.activated) { } else if (res.cardKey) {
clearInterval(pollTimer); clearInterval(pollTimer);
pollTimer = null; pollTimer = null;
const status = $('order-status'); const status = $('order-status');
status.className = 'order-status error'; status.className = 'order-status error';
status.textContent = '✗ 已支付但激活失败:' + (res.message || '请联系客服,提供订单号补发'); status.innerHTML = '✗ <b>已支付并已发卡</b>,但自动激活未成功(' +
esc(res.message || '本机绑定可能被占用') + ')。请用上方卡密手动激活:回到插件侧边栏 → 卡密输入框粘贴 → 点「粘贴卡密并激活」。<br>' +
'订单号:<b>' + esc(orderNo || '') + '</b>';
} else if (res.paid) {
// 已付款但服务端还没把卡发出来(最常见原因是卡池库存不足)
const waited = attempts * 5;
const status = $('order-status');
status.className = 'order-status pending';
status.textContent = '⏳ 已支付,正在为你发卡…(订单 ' + orderNo + ',已等待 ' + waited + ' 秒)';
if (waited >= 90) {
status.className = 'order-status error';
status.innerHTML = '⏳ <b>已支付</b>,但发卡耗时较长(已 ' + waited + ' 秒)。订单 <b>' + esc(orderNo) +
'</b> 付款状态为已支付,<b>权益不会丢失</b>:继续等会自动发放,或凭此订单号联系客服补发。';
}
} else { } else {
const status = $('order-status'); const status = $('order-status');
status.className = 'order-status pending'; status.className = 'order-status pending';
status.textContent = `⏳ 等待支付中…(订单 ${orderNo},已等待 ${attempts * 5} 秒)`; status.textContent = '⏳ 等待支付中…(订单 ' + orderNo + ',已等待 ' + attempts * 5 + ' 秒)';
} }
} catch (e) { } catch (e) {
/* 单次查询失败不中断轮询 */ /* 单次查询失败不中断轮询 */
@@ -120,13 +136,33 @@ function startPolling() {
}, 5000); }, 5000);
} }
/** 支付成功后立刻把卡密展示给用户(含自动激活失败的场景) */
function showCardBox(key, activated) {
const box = $('card-box');
if (!box || !key) return;
if (!box.hidden && $('cb-key-text').textContent === key) return;
$('cb-key-text').textContent = key;
box.hidden = false;
$('cb-copy-tip').textContent = activated ? '已自动激活本机,这是你的卡密,建议留存' : '';
box.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
function showActivated(res) { function showActivated(res) {
const status = $('order-status'); const status = $('order-status');
const permanent = !!res.isPermanent;
const exp = String(res.expireTime || '').trim();
const plan = String(res.planName || '').trim();
// 绝不默认「永久」:到期时间只在服务端明确给出时才显示
let expireLine;
if (permanent) expireLine = '到期时间:<b>永久</b>';
else if (exp) expireLine = '到期时间:<b>' + esc(exp) + '</b>';
else if (plan) expireLine = '套餐:<b>' + esc(plan) + '</b>(实际到期时间以插件侧边栏「已授权 · 到期 …」为准)';
else expireLine = '到期时间:以插件侧边栏显示为准';
status.className = 'order-status success'; status.className = 'order-status success';
status.innerHTML = ` status.innerHTML = `
<div class="ok-title">✓ 支付成功,已自动激活</div> <div class="ok-title">✓ 支付成功,已自动激活</div>
<div class="ok-line">卡密:<b>${esc(res.cardKey || '')}</b></div> <div class="ok-line">卡密:<b>${esc(res.cardKey || '')}</b></div>
<div class="ok-line">到期时间:${esc(res.expireTime || '永久')}</div> <div class="ok-line">${expireLine}</div>
<div class="ok-line muted">回到插件侧边栏即可使用全部功能(本机已授权)。</div>`; <div class="ok-line muted">回到插件侧边栏即可使用全部功能(本机已授权)。</div>`;
} }
@@ -134,4 +170,30 @@ document.querySelectorAll('.pay-btn').forEach((btn) => {
btn.addEventListener('click', () => createOrder(btn.dataset.pay)); btn.addEventListener('click', () => createOrder(btn.dataset.pay));
}); });
// 复制卡密:优先 clipboard API,无权限时降级 execCommand
$('cb-copy') && $('cb-copy').addEventListener('click', async () => {
const key = ($('cb-key-text').textContent || '').trim();
if (!key) return;
let ok = false;
try {
await navigator.clipboard.writeText(key);
ok = true;
} catch (e) { /* 降级 */ }
if (!ok) {
try {
const ta = document.createElement('textarea');
ta.value = key;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
ok = document.execCommand('copy');
document.body.removeChild(ta);
} catch (e2) {
ok = false;
}
}
$('cb-copy-tip').textContent = ok ? '已复制,可直接粘贴到侧边栏激活' : '复制失败,请手动长按选择卡密';
});
loadPackages(); loadPackages();
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "B站评论助手", "name": "B站评论助手",
"version": "1.10.0", "version": "1.10.5",
"description": "B站网页版关键词评论助手:按搜索关键词找视频,默认模拟真人(打开视频页、滚到评论区、逐字输入、点发布),也可快速API直发;支持下载当前页视频;带防重复记录、可中断执行与页面实时进度浮层。", "description": "B站网页版关键词评论助手:按搜索关键词找视频,默认模拟真人(打开视频页、滚到评论区、逐字输入、点发布),也可快速API直发;支持下载当前页视频;带防重复记录、可中断执行与页面实时进度浮层。",
"permissions": ["storage", "tabs", "scripting", "sidePanel", "downloads", "declarativeNetRequest", "alarms"], "permissions": ["storage", "tabs", "scripting", "sidePanel", "downloads", "declarativeNetRequest", "alarms"],
"host_permissions": [ "host_permissions": [
+20 -11
View File
@@ -6,9 +6,18 @@
* { box-sizing: border-box; margin: 0; padding: 0; } * { box-sizing: border-box; margin: 0; padding: 0; }
body { width: 100%; min-height: 100vh; font-family: "PingFang SC","Microsoft YaHei","Noto Sans CJK SC",sans-serif; font-size: 13px; color: #222; } body { width: 100%; min-height: 100vh; font-family: "PingFang SC","Microsoft YaHei","Noto Sans CJK SC",sans-serif; font-size: 13px; color: #222; }
header { background: #00A1D6; color: #fff; padding: 10px 12px; font-weight: bold; font-size: 14px; } header { background: #00A1D6; color: #fff; padding: 10px 12px; font-weight: bold; font-size: 14px; }
/* 标题旁的版本号:读 manifest,不写死,升级后自动跟着变 */
.hdr-ver { display:inline-block; margin-left:6px; padding:1px 7px; border-radius:10px;
background:rgba(255,255,255,.22); font-size:11px; font-weight:normal; vertical-align:middle; }
.promo { display:flex; align-items:center; justify-content:space-between; gap:8px; .promo { display:flex; align-items:center; justify-content:space-between; gap:8px;
background: linear-gradient(90deg,#00A1D6,#FB7299); color:#fff; padding: 8px 12px; font-size: 13px; } background: linear-gradient(90deg,#00A1D6,#FB7299); color:#fff; padding: 8px 12px; font-size: 13px; }
.promo a { color:#fff; font-weight:bold; text-decoration:underline; cursor:pointer; } .promo a { color:#fff; font-weight:bold; text-decoration:underline; cursor:pointer; }
/* 购买授权:独占一行的醒目按钮(授权文字不再和它抢同一行,避免状态被截断) */
.buy-btn { display:block; margin-top:8px; padding:10px 12px; border-radius:7px;
background:linear-gradient(90deg,#e91e63,#FB7299); color:#fff; font-size:14px;
font-weight:bold; text-align:center; text-decoration:none; cursor:pointer;
box-shadow:0 2px 6px rgba(251,114,153,.35); }
.buy-btn:active { opacity:.85; }
.sec { padding: 10px 12px; border-bottom: 1px solid #eee; } .sec { padding: 10px 12px; border-bottom: 1px solid #eee; }
.lbl { font-size: 11px; color: #888; margin-bottom: 5px; } .lbl { font-size: 11px; color: #888; margin-bottom: 5px; }
input[type=text], input[type=password], input[type=number], select { input[type=text], input[type=password], input[type=number], select {
@@ -45,15 +54,15 @@
</style> </style>
</head> </head>
<body> <body>
<header>B站评论助手</header> <header>B站评论助手<span id="hdrVer" class="hdr-ver"></span></header>
<div id="licSec" style="padding:8px 12px;border-bottom:1px solid #eee;background:#fafbfc"> <div id="licSec" style="padding:8px 12px;border-bottom:1px solid #eee;background:#fafbfc">
<div style="display:flex;align-items:center;gap:6px"> <div style="display:flex;align-items:flex-start;gap:6px">
<span id="licIcon" style="font-size:14px">⏳</span> <span id="licIcon" style="font-size:14px;line-height:1.45">⏳</span>
<span id="licText" style="font-size:13px;font-weight:bold;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">授权检查中…</span> <span id="licText" style="font-size:13px;font-weight:bold;flex:1;line-height:1.45;word-break:break-word">授权检查中…</span>
<a id="licBuy" style="color:#e91e63;font-weight:bold;text-decoration:underline;cursor:pointer;white-space:nowrap">购买授权 →</a>
</div> </div>
<div id="licSub" style="font-size:11px;color:#888;margin-top:3px"></div> <div id="licSub" style="font-size:11px;color:#888;margin-top:3px;line-height:1.5"></div>
<a id="licBuy" class="buy-btn">🛒 购买授权 · 解锁不限时不限量</a>
<div id="updMsg" style="display:none;font-size:12px;color:#005f8b;background:#e6f6fd;border:1px solid #9ed8f0;border-radius:4px;padding:4px 8px;margin-top:6px"></div> <div id="updMsg" style="display:none;font-size:12px;color:#005f8b;background:#e6f6fd;border:1px solid #9ed8f0;border-radius:4px;padding:4px 8px;margin-top:6px"></div>
<div id="licNotice" style="display:none;font-size:12px;color:#7a5c00;background:#fff8e1;border:1px solid #ffe082;border-radius:4px;padding:4px 8px;margin-top:6px;line-height:1.5"></div> <div id="licNotice" style="display:none;font-size:12px;color:#7a5c00;background:#fff8e1;border:1px solid #ffe082;border-radius:4px;padding:4px 8px;margin-top:6px;line-height:1.5"></div>
<div id="licActRow" style="display:none;margin-top:6px;align-items:center;gap:6px"> <div id="licActRow" style="display:none;margin-top:6px;align-items:center;gap:6px">
@@ -64,11 +73,6 @@
<div id="licMachine" style="font-size:10px;color:#bbb;margin-top:4px;cursor:pointer" title="点击复制机器码">机器码:<span id="machineIdText">…</span>(点击复制)</div> <div id="licMachine" style="font-size:10px;color:#bbb;margin-top:4px;cursor:pointer" title="点击复制机器码">机器码:<span id="machineIdText">…</span>(点击复制)</div>
</div> </div>
<div class="promo">
<span>🤖 没有模型?注册即送额度</span>
<a id="promoLink">ai.88531.cn 注册 →</a>
</div>
<div class="sec"> <div class="sec">
<div class="lbl">📊 评论记录</div> <div class="lbl">📊 评论记录</div>
<div class="statRow"> <div class="statRow">
@@ -79,6 +83,11 @@
</div> </div>
</div> </div>
<div class="promo">
<span>🤖 没有模型?注册即送额度</span>
<a id="promoLink">ai.88531.cn 注册 →</a>
</div>
<div class="sec"> <div class="sec">
<div class="lbl">LLM 配置(自动保存,仅 AI 模式需要)</div> <div class="lbl">LLM 配置(自动保存,仅 AI 模式需要)</div>
<input type="text" id="baseUrl" placeholder="https://ai.88531.cn/v1"> <input type="text" id="baseUrl" placeholder="https://ai.88531.cn/v1">
+6 -1
View File
@@ -152,7 +152,8 @@ function refreshCustomVisibility() {
actRow.style.display = "none"; actRow.style.display = "none";
} else if (lic.authorized) { } else if (lic.authorized) {
icon.textContent = "✅"; icon.textContent = "✅";
text.textContent = "已授权" + (lic.expire ? " · 到期 " + lic.expire : "(长期)"); const planTag = (lic.cardInfo && lic.cardInfo.type) ? " · " + lic.cardInfo.type : "";
text.textContent = "已授权" + planTag + (lic.expire ? " · 到期 " + lic.expire : "(长期)");
text.style.color = "#0a7"; text.style.color = "#0a7";
sub.textContent = "感谢支持!全部功能可用"; sub.textContent = "感谢支持!全部功能可用";
actRow.style.display = "none"; actRow.style.display = "none";
@@ -617,6 +618,10 @@ function refreshCustomVisibility() {
// ============ 初始化 ============ // ============ 初始化 ============
async function init() { async function init() {
const hv = $("hdrVer");
if (hv) {
try { hv.textContent = "v" + chrome.runtime.getManifest().version; } catch (e) { hv.textContent = ""; }
}
let saved = {}; let saved = {};
try { saved = (await chrome.storage.local.get("settings")).settings || {}; } catch (e) {} try { saved = (await chrome.storage.local.get("settings")).settings || {}; } catch (e) {}
settings = Object.assign({}, DEFAULTS, saved); settings = Object.assign({}, DEFAULTS, saved);