ai-zero-token 2.0.9 → 2.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.10 - 2026-05-26
4
+
5
+ - Added a manual OAuth fallback in the desktop UI so login can continue by pasting the callback URL or authorization code when the local callback is not received.
6
+ - Started the OAuth callback listener on both IPv4 and IPv6 loopback addresses for better compatibility with browser redirects.
7
+ - Refreshed accounts before applying them to local Codex so `auth.json` receives a current `id_token`.
8
+ - Tightened `id_token` validation by checking expiry and matching embedded account identity when available.
9
+
3
10
  ## 2.0.9 - 2026-05-26
4
11
 
5
12
  - Clarified auto-switch settings so manually excluded accounts are separated from accounts that are runtime-ineligible because login is unavailable or quota is exhausted.
@@ -1,4 +1,4 @@
1
- import{a as e,r as t,t as n}from"./jsx-runtime-DqpGtLhh.js";import{t as r}from"./earth-DFdZaQIi.js";import{t as i}from"./refresh-cw-CAAH2rqe.js";import{t as a}from"./search-B2hz41D3.js";import{T as o,_ as s,a as c,b as l,c as u,d,f,g as p,h as m,i as h,m as g,o as _,p as v,r as y,s as b,t as x,v as S,w as C,x as w,y as T}from"./profiles-iNTmJFRe.js";import{_ as E,d as D,p as O,r as k,x as A}from"./index-BM5N4YUY.js";import{t as j}from"./InfoRow-0ULI9iI3.js";var M=e(t(),1),N=n();function P(e){let t=e.config?.codex?.accountId,n=e.profiles.length<=0?``:e.profiles.length===1?`profile-count-1`:e.profiles.length===2?`profile-count-2`:e.profiles.length===3?`profile-count-3`:`profile-count-many`;return(0,N.jsxs)(`section`,{className:`card`,id:`accounts`,children:[(0,N.jsxs)(`div`,{className:`section-head`,children:[(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`h2`,{children:`账号额度预览`}),(0,N.jsx)(`p`,{children:`账号信息采用卡片式布局展示,支持搜索、状态筛选和额度排序。`})]}),(0,N.jsxs)(`div`,{className:`section-actions`,children:[(0,N.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onLocate,children:`定位当前账号`}),(0,N.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onExportSelected,children:`导出所选`}),(0,N.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onSelectVisible,disabled:e.visibleCount===0,children:`全选筛选结果`}),(0,N.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onClearSelected,disabled:e.selectedCount===0,children:`取消选择`}),(0,N.jsx)(`button`,{className:`btn-danger`,type:`button`,onClick:e.onRemoveSelected,disabled:e.selectedCount===0||e.busy===`bulk-remove`,children:`删除所选`}),(0,N.jsx)(`button`,{className:`btn-primary`,type:`button`,onClick:e.onAddAccount,children:`新增账号`}),(0,N.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onRefreshStatus,children:`刷新状态`}),(0,N.jsx)(`button`,{className:`btn-danger`,type:`button`,onClick:e.onClearAccounts,children:`清空账号`})]})]}),(0,N.jsx)(`div`,{className:`account-stat-strip`,"aria-label":`账号池统计`,children:e.accountStats.map(t=>(0,N.jsxs)(`button`,{className:`account-stat-pill tone-${t.tone} ${e.filter.status===t.key?`is-active`:``}`,type:`button`,onClick:()=>e.onFilter({...e.filter,status:t.key}),children:[(0,N.jsx)(`span`,{children:t.label}),(0,N.jsx)(`strong`,{children:t.value})]},t.key))}),(0,N.jsxs)(`div`,{className:`filter-row`,children:[(0,N.jsxs)(`label`,{className:`search-box`,children:[(0,N.jsx)(a,{size:16}),(0,N.jsx)(`input`,{value:e.filter.search,onChange:t=>e.onFilter({...e.filter,search:t.target.value}),placeholder:`搜索邮箱、账号 ID 或 Profile ID`})]}),(0,N.jsxs)(`select`,{className:`control`,value:e.filter.status,onChange:t=>e.onFilter({...e.filter,status:t.target.value}),children:[(0,N.jsx)(`option`,{value:`all`,children:`全部状态`}),(0,N.jsx)(`option`,{value:`available`,children:`可用`}),(0,N.jsx)(`option`,{value:`unavailable`,children:`不可用`}),(0,N.jsx)(`option`,{value:`active`,children:`使用中`}),(0,N.jsx)(`option`,{value:`api-active`,children:`API 使用中`}),(0,N.jsx)(`option`,{value:`codex-active`,children:`Codex 使用中`}),(0,N.jsx)(`option`,{value:`healthy`,children:`健康`}),(0,N.jsx)(`option`,{value:`warning`,children:`即将耗尽`}),(0,N.jsx)(`option`,{value:`unknown`,children:`待请求验证`}),(0,N.jsx)(`option`,{value:`exhausted`,children:`额度耗尽`}),(0,N.jsx)(`option`,{value:`invalid`,children:`登录/认证异常`}),(0,N.jsx)(`option`,{value:`login-invalid`,children:`登录失效`}),(0,N.jsx)(`option`,{value:`auth-error`,children:`认证异常`}),(0,N.jsx)(`option`,{value:`expired`,children:`已过期`}),(0,N.jsx)(`option`,{value:`free`,children:`Free`}),(0,N.jsx)(`option`,{value:`plus`,children:`Plus`}),(0,N.jsx)(`option`,{value:`pro-team`,children:`Pro/Team`}),(0,N.jsx)(`option`,{value:`auto-included`,children:`配置参与`}),(0,N.jsx)(`option`,{value:`auto-excluded`,children:`手动排除`})]}),(0,N.jsxs)(`select`,{className:`control`,value:e.filter.sort,onChange:t=>e.onFilter({...e.filter,sort:t.target.value}),children:[(0,N.jsx)(`option`,{value:`quota-desc`,children:`默认排序`}),(0,N.jsx)(`option`,{value:`latency-asc`,children:`按额度更新时间`}),(0,N.jsx)(`option`,{value:`expiry-asc`,children:`按过期时间`}),(0,N.jsx)(`option`,{value:`name-asc`,children:`按名称排序`}),(0,N.jsx)(`option`,{value:`quota-asc`,children:`按剩余额度升序`}),(0,N.jsx)(`option`,{value:`plan-desc`,children:`按套餐排序`}),(0,N.jsx)(`option`,{value:`email-asc`,children:`按邮箱排序`})]}),(0,N.jsxs)(`span`,{className:`account-selected-count`,children:[`已选择 `,e.selectedCount,` 个`]})]}),(0,N.jsx)(`div`,{className:`account-grid ${n}`,children:e.profiles.length===0?(0,N.jsx)(`div`,{className:`empty-state`,children:`还没有匹配的账号。可以新增账号或调整筛选条件。`}):e.profiles.map(n=>{let a=v(n),o=f(n),u=l(n),d=!!e.expandedProfiles[n.profileId],p=!!(t&&n.accountId===t),h=w(n,p),C=b(n),D=_(n),O=n.exportAudit,k=O?.exported?`已导出 ${O.count} 次`:`未导出`,M=typeof e.busy==`string`&&e.busy.startsWith(`profile:`)&&e.busy.endsWith(n.profileId),P=e.busy===`profile:sync-quota:${n.profileId}`;return(0,N.jsxs)(`article`,{className:`account-card plan-${y(n)} ${C?`is-auth-invalid`:``}`,"data-profile-card":n.profileId,title:C?x(n):void 0,children:[h&&(0,N.jsx)(`span`,{className:`usage-corner ${h.className}`,children:(0,N.jsx)(`span`,{children:h.label})}),(0,N.jsxs)(`div`,{className:`account-head`,children:[(0,N.jsxs)(`div`,{className:`account-title`,children:[(0,N.jsxs)(`div`,{className:`account-name`,children:[(0,N.jsx)(`span`,{className:`avatar`,children:g(n)}),(0,N.jsx)(`strong`,{children:m(n,e.showEmails)}),(0,N.jsx)(`button`,{"aria-label":`刷新额度`,className:`account-icon-btn`,disabled:M,onClick:()=>e.onAction(`sync-quota`,n),title:`刷新额度`,type:`button`,children:P?(0,N.jsx)(E,{className:`spin`,size:14}):(0,N.jsx)(i,{size:14})})]}),(0,N.jsxs)(`div`,{className:`badge-row`,children:[(0,N.jsx)(`span`,{className:`badge brand`,children:c(n)}),(0,N.jsx)(`span`,{className:`badge ${a.tone}`,children:a.label}),(0,N.jsx)(`span`,{className:`badge ${D.ok?`green`:`orange`}`,children:`gpt-image-2`}),(0,N.jsx)(`span`,{className:`badge ${O?.exported?`orange`:`muted`}`,children:k})]})]}),(0,N.jsxs)(`label`,{className:`account-select`,children:[(0,N.jsx)(`input`,{type:`checkbox`,checked:!!e.selectedProfiles[n.profileId],onChange:t=>e.onSelect(n.profileId,t.target.checked)}),(0,N.jsx)(`span`,{children:`选择`})]})]}),(0,N.jsxs)(`div`,{className:`account-metrics`,children:[(0,N.jsx)(L,{label:S(n,`primary`),value:o,tone:s(o)}),(0,N.jsx)(L,{label:S(n,`secondary`),value:u,tone:s(u)})]}),(0,N.jsxs)(`div`,{className:`usage-status-row`,children:[(0,N.jsxs)(`span`,{className:`usage-status ${n.isActive?`is-active`:``}`,children:[(0,N.jsx)(r,{size:14}),(0,N.jsx)(`span`,{children:`API`}),(0,N.jsx)(`span`,{className:`usage-dot ${n.isActive?`active`:``}`}),(0,N.jsx)(`span`,{className:`usage-state-text`,children:n.isActive?`使用中`:`未使用`})]}),(0,N.jsxs)(`span`,{className:`usage-status ${p?`is-active`:``}`,children:[(0,N.jsx)(A,{size:14}),(0,N.jsx)(`span`,{children:`Codex`}),(0,N.jsx)(`span`,{className:`usage-dot ${p?`active`:``}`}),(0,N.jsx)(`span`,{className:`usage-state-text`,children:p?`使用中`:`未使用`})]})]}),(0,N.jsxs)(`div`,{className:`compact-meta-row`,children:[(0,N.jsxs)(`div`,{className:`compact-reset-list`,children:[(0,N.jsxs)(`div`,{className:`compact-meta-item`,children:[(0,N.jsx)(`label`,{children:S(n,`primary`)}),(0,N.jsx)(`strong`,{children:T(n,`primary`)})]}),(0,N.jsxs)(`div`,{className:`compact-meta-item`,children:[(0,N.jsx)(`label`,{children:S(n,`secondary`)}),(0,N.jsx)(`strong`,{children:T(n,`secondary`)})]})]}),(0,N.jsx)(`div`,{className:`compact-meta-actions`,children:(0,N.jsxs)(`button`,{className:`details-toggle ${d?`is-expanded`:``}`,type:`button`,onClick:()=>e.onToggle(n.profileId),children:[(0,N.jsx)(`span`,{children:d?`收起详情`:`查看详情`}),(0,N.jsx)(I,{})]})})]}),d&&(0,N.jsxs)(`div`,{className:`meta-grid`,children:[(0,N.jsx)(j,{label:`套餐`,value:c(n)}),(0,N.jsx)(j,{label:`Account ID`,value:(e.showEmails,n.accountId),code:!0}),(0,N.jsx)(j,{label:`Profile ID`,value:(e.showEmails,n.profileId),code:!0}),(0,N.jsx)(j,{label:`认证状态`,value:x(n)}),(0,N.jsx)(j,{label:`生图能力`,value:D.ok?`gpt-image-2 可用`:D.detail}),(0,N.jsx)(j,{label:`导出记录`,value:F(O)}),(0,N.jsx)(j,{label:`过期时间`,value:n.expiresAt?new Date(n.expiresAt).toLocaleString(`zh-CN`):`-`}),(0,N.jsx)(j,{label:`额度快照`,value:n.quota?.capturedAt?new Date(n.quota.capturedAt).toLocaleString(`zh-CN`):`-`})]}),(0,N.jsxs)(`div`,{className:`account-actions`,children:[(0,N.jsx)(`button`,{className:`btn-secondary ${n.isActive?`is-current`:``}`,type:`button`,onClick:()=>e.onAction(`activate`,n),disabled:n.isActive||M||C,children:C?`网关不可用`:n.isActive?`网关使用中`:`应用网关`}),(0,N.jsx)(`button`,{className:`btn-secondary ${p?`is-current codex`:``}`,type:`button`,onClick:()=>e.onAction(`apply-codex`,n),disabled:p||M||C,children:C?`Codex 不可用`:p?`Codex 使用中`:`应用 Codex`}),(0,N.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:()=>e.onAction(`export`,n),disabled:M,children:`导出`}),(0,N.jsx)(`button`,{className:`btn-danger`,type:`button`,onClick:()=>e.onAction(`remove`,n),disabled:M,children:`删除`})]})]},n.profileId)})})]})}function F(e){if(!e?.exported)return`未导出`;let t=e.lastExportKind===`single`?`单账号导出`:e.lastExportKind===`batch`?`批量导出`:`全部导出`;return`${e.count} 次,最近 ${C(e.lastExportedAt)},方式 ${t}`}function I(){return(0,N.jsx)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,"aria-hidden":`true`,children:(0,N.jsx)(`path`,{d:`m6 9 6 6 6-6`})})}function L(e){return(0,N.jsxs)(`div`,{className:`quota-row`,children:[(0,N.jsxs)(`div`,{className:`quota-line`,children:[(0,N.jsxs)(`span`,{children:[e.label,` · 已用 `,e.value,`% / 剩余 `,100-e.value,`%`]}),(0,N.jsxs)(`strong`,{children:[`剩余 `,100-e.value,`%`]})]}),(0,N.jsx)(`div`,{className:`progress-track`,children:(0,N.jsx)(`div`,{className:`progress-bar ${e.tone}`,style:{width:`${e.value}%`}})})]})}function R(e){let[t,n]=(0,M.useState)({}),[r,i]=(0,M.useState)({}),[a,s]=(0,M.useState)({search:``,status:`all`,sort:`quota-desc`}),c=(0,M.useMemo)(()=>{let t=e.config?.profiles?[...e.config.profiles]:[],n=new Set(e.config?.settings.autoSwitch.excludedProfileIds||[]),r=a.search.trim().toLowerCase(),i=t.filter(t=>{let i=[m(t,!0).toLowerCase(),t.accountId,t.profileId,t.email||``].join(` `).toLowerCase(),o=v(t),s=!!(e.codexAccountId&&t.accountId===e.codexAccountId),c=y(t);return r&&!i.includes(r)?!1:a.status===`active`?t.isActive||s:a.status===`healthy`?o.key===`healthy`:a.status===`warning`?o.key===`warning`:a.status===`unknown`?o.key===`unknown`:a.status===`exhausted`?o.key===`exhausted`:a.status===`expired`?o.key===`expired`:a.status===`invalid`?o.key===`invalid`:a.status===`login-invalid`?t.authStatus?.state===`token_invalidated`:a.status===`auth-error`?t.authStatus?.state===`auth_error`:a.status===`available`?o.key===`healthy`||o.key===`warning`||o.key===`unknown`:a.status===`unavailable`?o.key===`invalid`||o.key===`expired`||o.key===`exhausted`:a.status===`free`?c===`free`:a.status===`plus`?c===`plus`:a.status===`pro-team`?c===`pro`||c===`team`||c===`enterprise`||c===`premium`:a.status===`api-active`?t.isActive:a.status===`codex-active`?s:a.status===`auto-included`?!n.has(t.profileId):a.status===`auto-excluded`?n.has(t.profileId):!0});return i.sort((t,n)=>{let r=p(t,e.codexAccountId)-p(n,e.codexAccountId);if(r!==0)return r;let i=h(n)-h(t);if(i!==0)return i;let o=d(n)-d(t);return o===0?a.sort===`latency-asc`?(n.quota?.capturedAt||0)-(t.quota?.capturedAt||0):a.sort===`expiry-asc`?(t.expiresAt||2**53-1)-(n.expiresAt||2**53-1):a.sort===`name-asc`?m(t,!0).localeCompare(m(n,!0),`zh-CN`):a.sort===`quota-asc`?100-f(n)-(100-f(t)):a.sort===`plan-desc`?h(n)-h(t):a.sort===`email-asc`?m(t,!0).localeCompare(m(n,!0)):f(n)-f(t):o}),i},[a,e.codexAccountId,e.config?.profiles,e.config?.settings.autoSwitch.excludedProfileIds]),l=(0,M.useMemo)(()=>{let t=e.config?.profiles||[],n=new Set(e.config?.settings.autoSwitch.excludedProfileIds||[]),r=e=>t.filter(e).length,i=r(t=>!!(e.codexAccountId&&t.accountId===e.codexAccountId));return[{key:`all`,label:`总账号`,value:t.length,tone:`blue`},{key:`available`,label:`可用`,value:r(e=>[`healthy`,`warning`,`unknown`].includes(v(e).key)),tone:`green`},{key:`unavailable`,label:`不可用`,value:r(e=>[`invalid`,`expired`,`exhausted`].includes(v(e).key)),tone:`red`},{key:`unknown`,label:`待请求验证`,value:r(e=>v(e).key===`unknown`),tone:`blue`},{key:`login-invalid`,label:`登录失效`,value:r(e=>e.authStatus?.state===`token_invalidated`),tone:`red`},{key:`auth-error`,label:`认证异常`,value:r(e=>e.authStatus?.state===`auth_error`),tone:`red`},{key:`exhausted`,label:`额度耗尽`,value:r(e=>v(e).key===`exhausted`),tone:`orange`},{key:`free`,label:`Free`,value:r(e=>y(e)===`free`),tone:`muted`},{key:`plus`,label:`Plus`,value:r(e=>y(e)===`plus`),tone:`brand`},{key:`pro-team`,label:`Pro/Team`,value:r(e=>[`pro`,`team`,`enterprise`,`premium`].includes(y(e))),tone:`blue`},{key:`api-active`,label:`API 使用中`,value:r(e=>e.isActive),tone:`green`},{key:`codex-active`,label:`Codex 使用中`,value:i,tone:`green`},{key:`auto-included`,label:`配置参与`,value:r(e=>!n.has(e.profileId)),tone:`blue`},{key:`auto-excluded`,label:`手动排除`,value:r(e=>n.has(e.profileId)),tone:`orange`}]},[e.codexAccountId,e.config?.profiles,e.config?.settings.autoSwitch.excludedProfileIds]),g=Object.values(t).filter(Boolean).length,_=Object.keys(t).filter(e=>t[e]),x=(0,M.useMemo)(()=>c.map(e=>e.profileId),[c]);async function S(t,n){let r=await O(`/_gateway/admin/profiles/export`,{method:`POST`,headers:{"Content-Type":`application/json`},body:o(n?{profileIds:n}:{profileId:t})});D(`ai-zero-token-${n?`profiles-${n.length}`:t||`active`}.json`,r.profile),r.config?e.setConfig(r.config):await e.refreshConfig({silent:!0}),e.setStatus(n?`已导出 ${n.length} 个账号。`:`账号配置已导出。`)}async function C(t,n){if(!(t===`remove`&&!window.confirm(`确认删除 ${m(n,e.showEmails)}?`))){if((t===`activate`||t===`apply-codex`)&&b(n)){e.setStatus(`${m(n,e.showEmails)} 登录已失效,不能应用到${t===`activate`?`网关`:`Codex`}。`);return}if((t===`activate`||t===`apply-codex`)&&u(n)){let r=t===`activate`?`网关`:`Codex`;if(!window.confirm(`${m(n,e.showEmails)} 的额度看起来已耗尽,仍要应用到${r}吗?`))return}if(t===`export`){await S(n.profileId);return}e.setBusy(`profile:${t}:${n.profileId}`);try{let r=await O({activate:`/_gateway/admin/profiles/activate`,"apply-codex":`/_gateway/admin/codex/apply`,"sync-quota":`/_gateway/admin/profiles/sync-quota`,remove:`/_gateway/admin/profiles/remove`}[t],{method:`POST`,headers:{"Content-Type":`application/json`},body:o({profileId:n.profileId})}),i=`config`in r?r.config:r;if(e.setConfig(i),e.setStatus(t===`activate`?`已应用到网关。`:t===`apply-codex`?`已应用到本机 Codex。`:t===`sync-quota`?`额度信息已同步。`:`账号已删除。`),t===`apply-codex`)if(i.codexRestartSupported&&window.confirm(`Codex 账号已切换,是否现在重启 Codex 客户端?
1
+ import{a as e,r as t,t as n}from"./jsx-runtime-DqpGtLhh.js";import{t as r}from"./earth-DFdZaQIi.js";import{t as i}from"./refresh-cw-CAAH2rqe.js";import{t as a}from"./search-B2hz41D3.js";import{T as o,_ as s,a as c,b as l,c as u,d,f,g as p,h as m,i as h,m as g,o as _,p as v,r as y,s as b,t as x,v as S,w as C,x as w,y as T}from"./profiles-iNTmJFRe.js";import{_ as E,d as D,p as O,r as k,x as A}from"./index-D3zM8GaW.js";import{t as j}from"./InfoRow-0ULI9iI3.js";var M=e(t(),1),N=n();function P(e){let t=e.config?.codex?.accountId,n=e.profiles.length<=0?``:e.profiles.length===1?`profile-count-1`:e.profiles.length===2?`profile-count-2`:e.profiles.length===3?`profile-count-3`:`profile-count-many`;return(0,N.jsxs)(`section`,{className:`card`,id:`accounts`,children:[(0,N.jsxs)(`div`,{className:`section-head`,children:[(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`h2`,{children:`账号额度预览`}),(0,N.jsx)(`p`,{children:`账号信息采用卡片式布局展示,支持搜索、状态筛选和额度排序。`})]}),(0,N.jsxs)(`div`,{className:`section-actions`,children:[(0,N.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onLocate,children:`定位当前账号`}),(0,N.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onExportSelected,children:`导出所选`}),(0,N.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onSelectVisible,disabled:e.visibleCount===0,children:`全选筛选结果`}),(0,N.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onClearSelected,disabled:e.selectedCount===0,children:`取消选择`}),(0,N.jsx)(`button`,{className:`btn-danger`,type:`button`,onClick:e.onRemoveSelected,disabled:e.selectedCount===0||e.busy===`bulk-remove`,children:`删除所选`}),(0,N.jsx)(`button`,{className:`btn-primary`,type:`button`,onClick:e.onAddAccount,children:`新增账号`}),(0,N.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onRefreshStatus,children:`刷新状态`}),(0,N.jsx)(`button`,{className:`btn-danger`,type:`button`,onClick:e.onClearAccounts,children:`清空账号`})]})]}),(0,N.jsx)(`div`,{className:`account-stat-strip`,"aria-label":`账号池统计`,children:e.accountStats.map(t=>(0,N.jsxs)(`button`,{className:`account-stat-pill tone-${t.tone} ${e.filter.status===t.key?`is-active`:``}`,type:`button`,onClick:()=>e.onFilter({...e.filter,status:t.key}),children:[(0,N.jsx)(`span`,{children:t.label}),(0,N.jsx)(`strong`,{children:t.value})]},t.key))}),(0,N.jsxs)(`div`,{className:`filter-row`,children:[(0,N.jsxs)(`label`,{className:`search-box`,children:[(0,N.jsx)(a,{size:16}),(0,N.jsx)(`input`,{value:e.filter.search,onChange:t=>e.onFilter({...e.filter,search:t.target.value}),placeholder:`搜索邮箱、账号 ID 或 Profile ID`})]}),(0,N.jsxs)(`select`,{className:`control`,value:e.filter.status,onChange:t=>e.onFilter({...e.filter,status:t.target.value}),children:[(0,N.jsx)(`option`,{value:`all`,children:`全部状态`}),(0,N.jsx)(`option`,{value:`available`,children:`可用`}),(0,N.jsx)(`option`,{value:`unavailable`,children:`不可用`}),(0,N.jsx)(`option`,{value:`active`,children:`使用中`}),(0,N.jsx)(`option`,{value:`api-active`,children:`API 使用中`}),(0,N.jsx)(`option`,{value:`codex-active`,children:`Codex 使用中`}),(0,N.jsx)(`option`,{value:`healthy`,children:`健康`}),(0,N.jsx)(`option`,{value:`warning`,children:`即将耗尽`}),(0,N.jsx)(`option`,{value:`unknown`,children:`待请求验证`}),(0,N.jsx)(`option`,{value:`exhausted`,children:`额度耗尽`}),(0,N.jsx)(`option`,{value:`invalid`,children:`登录/认证异常`}),(0,N.jsx)(`option`,{value:`login-invalid`,children:`登录失效`}),(0,N.jsx)(`option`,{value:`auth-error`,children:`认证异常`}),(0,N.jsx)(`option`,{value:`expired`,children:`已过期`}),(0,N.jsx)(`option`,{value:`free`,children:`Free`}),(0,N.jsx)(`option`,{value:`plus`,children:`Plus`}),(0,N.jsx)(`option`,{value:`pro-team`,children:`Pro/Team`}),(0,N.jsx)(`option`,{value:`auto-included`,children:`配置参与`}),(0,N.jsx)(`option`,{value:`auto-excluded`,children:`手动排除`})]}),(0,N.jsxs)(`select`,{className:`control`,value:e.filter.sort,onChange:t=>e.onFilter({...e.filter,sort:t.target.value}),children:[(0,N.jsx)(`option`,{value:`quota-desc`,children:`默认排序`}),(0,N.jsx)(`option`,{value:`latency-asc`,children:`按额度更新时间`}),(0,N.jsx)(`option`,{value:`expiry-asc`,children:`按过期时间`}),(0,N.jsx)(`option`,{value:`name-asc`,children:`按名称排序`}),(0,N.jsx)(`option`,{value:`quota-asc`,children:`按剩余额度升序`}),(0,N.jsx)(`option`,{value:`plan-desc`,children:`按套餐排序`}),(0,N.jsx)(`option`,{value:`email-asc`,children:`按邮箱排序`})]}),(0,N.jsxs)(`span`,{className:`account-selected-count`,children:[`已选择 `,e.selectedCount,` 个`]})]}),(0,N.jsx)(`div`,{className:`account-grid ${n}`,children:e.profiles.length===0?(0,N.jsx)(`div`,{className:`empty-state`,children:`还没有匹配的账号。可以新增账号或调整筛选条件。`}):e.profiles.map(n=>{let a=v(n),o=f(n),u=l(n),d=!!e.expandedProfiles[n.profileId],p=!!(t&&n.accountId===t),h=w(n,p),C=b(n),D=_(n),O=n.exportAudit,k=O?.exported?`已导出 ${O.count} 次`:`未导出`,M=typeof e.busy==`string`&&e.busy.startsWith(`profile:`)&&e.busy.endsWith(n.profileId),P=e.busy===`profile:sync-quota:${n.profileId}`;return(0,N.jsxs)(`article`,{className:`account-card plan-${y(n)} ${C?`is-auth-invalid`:``}`,"data-profile-card":n.profileId,title:C?x(n):void 0,children:[h&&(0,N.jsx)(`span`,{className:`usage-corner ${h.className}`,children:(0,N.jsx)(`span`,{children:h.label})}),(0,N.jsxs)(`div`,{className:`account-head`,children:[(0,N.jsxs)(`div`,{className:`account-title`,children:[(0,N.jsxs)(`div`,{className:`account-name`,children:[(0,N.jsx)(`span`,{className:`avatar`,children:g(n)}),(0,N.jsx)(`strong`,{children:m(n,e.showEmails)}),(0,N.jsx)(`button`,{"aria-label":`刷新额度`,className:`account-icon-btn`,disabled:M,onClick:()=>e.onAction(`sync-quota`,n),title:`刷新额度`,type:`button`,children:P?(0,N.jsx)(E,{className:`spin`,size:14}):(0,N.jsx)(i,{size:14})})]}),(0,N.jsxs)(`div`,{className:`badge-row`,children:[(0,N.jsx)(`span`,{className:`badge brand`,children:c(n)}),(0,N.jsx)(`span`,{className:`badge ${a.tone}`,children:a.label}),(0,N.jsx)(`span`,{className:`badge ${D.ok?`green`:`orange`}`,children:`gpt-image-2`}),(0,N.jsx)(`span`,{className:`badge ${O?.exported?`orange`:`muted`}`,children:k})]})]}),(0,N.jsxs)(`label`,{className:`account-select`,children:[(0,N.jsx)(`input`,{type:`checkbox`,checked:!!e.selectedProfiles[n.profileId],onChange:t=>e.onSelect(n.profileId,t.target.checked)}),(0,N.jsx)(`span`,{children:`选择`})]})]}),(0,N.jsxs)(`div`,{className:`account-metrics`,children:[(0,N.jsx)(L,{label:S(n,`primary`),value:o,tone:s(o)}),(0,N.jsx)(L,{label:S(n,`secondary`),value:u,tone:s(u)})]}),(0,N.jsxs)(`div`,{className:`usage-status-row`,children:[(0,N.jsxs)(`span`,{className:`usage-status ${n.isActive?`is-active`:``}`,children:[(0,N.jsx)(r,{size:14}),(0,N.jsx)(`span`,{children:`API`}),(0,N.jsx)(`span`,{className:`usage-dot ${n.isActive?`active`:``}`}),(0,N.jsx)(`span`,{className:`usage-state-text`,children:n.isActive?`使用中`:`未使用`})]}),(0,N.jsxs)(`span`,{className:`usage-status ${p?`is-active`:``}`,children:[(0,N.jsx)(A,{size:14}),(0,N.jsx)(`span`,{children:`Codex`}),(0,N.jsx)(`span`,{className:`usage-dot ${p?`active`:``}`}),(0,N.jsx)(`span`,{className:`usage-state-text`,children:p?`使用中`:`未使用`})]})]}),(0,N.jsxs)(`div`,{className:`compact-meta-row`,children:[(0,N.jsxs)(`div`,{className:`compact-reset-list`,children:[(0,N.jsxs)(`div`,{className:`compact-meta-item`,children:[(0,N.jsx)(`label`,{children:S(n,`primary`)}),(0,N.jsx)(`strong`,{children:T(n,`primary`)})]}),(0,N.jsxs)(`div`,{className:`compact-meta-item`,children:[(0,N.jsx)(`label`,{children:S(n,`secondary`)}),(0,N.jsx)(`strong`,{children:T(n,`secondary`)})]})]}),(0,N.jsx)(`div`,{className:`compact-meta-actions`,children:(0,N.jsxs)(`button`,{className:`details-toggle ${d?`is-expanded`:``}`,type:`button`,onClick:()=>e.onToggle(n.profileId),children:[(0,N.jsx)(`span`,{children:d?`收起详情`:`查看详情`}),(0,N.jsx)(I,{})]})})]}),d&&(0,N.jsxs)(`div`,{className:`meta-grid`,children:[(0,N.jsx)(j,{label:`套餐`,value:c(n)}),(0,N.jsx)(j,{label:`Account ID`,value:(e.showEmails,n.accountId),code:!0}),(0,N.jsx)(j,{label:`Profile ID`,value:(e.showEmails,n.profileId),code:!0}),(0,N.jsx)(j,{label:`认证状态`,value:x(n)}),(0,N.jsx)(j,{label:`生图能力`,value:D.ok?`gpt-image-2 可用`:D.detail}),(0,N.jsx)(j,{label:`导出记录`,value:F(O)}),(0,N.jsx)(j,{label:`过期时间`,value:n.expiresAt?new Date(n.expiresAt).toLocaleString(`zh-CN`):`-`}),(0,N.jsx)(j,{label:`额度快照`,value:n.quota?.capturedAt?new Date(n.quota.capturedAt).toLocaleString(`zh-CN`):`-`})]}),(0,N.jsxs)(`div`,{className:`account-actions`,children:[(0,N.jsx)(`button`,{className:`btn-secondary ${n.isActive?`is-current`:``}`,type:`button`,onClick:()=>e.onAction(`activate`,n),disabled:n.isActive||M||C,children:C?`网关不可用`:n.isActive?`网关使用中`:`应用网关`}),(0,N.jsx)(`button`,{className:`btn-secondary ${p?`is-current codex`:``}`,type:`button`,onClick:()=>e.onAction(`apply-codex`,n),disabled:p||M||C,children:C?`Codex 不可用`:p?`Codex 使用中`:`应用 Codex`}),(0,N.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:()=>e.onAction(`export`,n),disabled:M,children:`导出`}),(0,N.jsx)(`button`,{className:`btn-danger`,type:`button`,onClick:()=>e.onAction(`remove`,n),disabled:M,children:`删除`})]})]},n.profileId)})})]})}function F(e){if(!e?.exported)return`未导出`;let t=e.lastExportKind===`single`?`单账号导出`:e.lastExportKind===`batch`?`批量导出`:`全部导出`;return`${e.count} 次,最近 ${C(e.lastExportedAt)},方式 ${t}`}function I(){return(0,N.jsx)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,"aria-hidden":`true`,children:(0,N.jsx)(`path`,{d:`m6 9 6 6 6-6`})})}function L(e){return(0,N.jsxs)(`div`,{className:`quota-row`,children:[(0,N.jsxs)(`div`,{className:`quota-line`,children:[(0,N.jsxs)(`span`,{children:[e.label,` · 已用 `,e.value,`% / 剩余 `,100-e.value,`%`]}),(0,N.jsxs)(`strong`,{children:[`剩余 `,100-e.value,`%`]})]}),(0,N.jsx)(`div`,{className:`progress-track`,children:(0,N.jsx)(`div`,{className:`progress-bar ${e.tone}`,style:{width:`${e.value}%`}})})]})}function R(e){let[t,n]=(0,M.useState)({}),[r,i]=(0,M.useState)({}),[a,s]=(0,M.useState)({search:``,status:`all`,sort:`quota-desc`}),c=(0,M.useMemo)(()=>{let t=e.config?.profiles?[...e.config.profiles]:[],n=new Set(e.config?.settings.autoSwitch.excludedProfileIds||[]),r=a.search.trim().toLowerCase(),i=t.filter(t=>{let i=[m(t,!0).toLowerCase(),t.accountId,t.profileId,t.email||``].join(` `).toLowerCase(),o=v(t),s=!!(e.codexAccountId&&t.accountId===e.codexAccountId),c=y(t);return r&&!i.includes(r)?!1:a.status===`active`?t.isActive||s:a.status===`healthy`?o.key===`healthy`:a.status===`warning`?o.key===`warning`:a.status===`unknown`?o.key===`unknown`:a.status===`exhausted`?o.key===`exhausted`:a.status===`expired`?o.key===`expired`:a.status===`invalid`?o.key===`invalid`:a.status===`login-invalid`?t.authStatus?.state===`token_invalidated`:a.status===`auth-error`?t.authStatus?.state===`auth_error`:a.status===`available`?o.key===`healthy`||o.key===`warning`||o.key===`unknown`:a.status===`unavailable`?o.key===`invalid`||o.key===`expired`||o.key===`exhausted`:a.status===`free`?c===`free`:a.status===`plus`?c===`plus`:a.status===`pro-team`?c===`pro`||c===`team`||c===`enterprise`||c===`premium`:a.status===`api-active`?t.isActive:a.status===`codex-active`?s:a.status===`auto-included`?!n.has(t.profileId):a.status===`auto-excluded`?n.has(t.profileId):!0});return i.sort((t,n)=>{let r=p(t,e.codexAccountId)-p(n,e.codexAccountId);if(r!==0)return r;let i=h(n)-h(t);if(i!==0)return i;let o=d(n)-d(t);return o===0?a.sort===`latency-asc`?(n.quota?.capturedAt||0)-(t.quota?.capturedAt||0):a.sort===`expiry-asc`?(t.expiresAt||2**53-1)-(n.expiresAt||2**53-1):a.sort===`name-asc`?m(t,!0).localeCompare(m(n,!0),`zh-CN`):a.sort===`quota-asc`?100-f(n)-(100-f(t)):a.sort===`plan-desc`?h(n)-h(t):a.sort===`email-asc`?m(t,!0).localeCompare(m(n,!0)):f(n)-f(t):o}),i},[a,e.codexAccountId,e.config?.profiles,e.config?.settings.autoSwitch.excludedProfileIds]),l=(0,M.useMemo)(()=>{let t=e.config?.profiles||[],n=new Set(e.config?.settings.autoSwitch.excludedProfileIds||[]),r=e=>t.filter(e).length,i=r(t=>!!(e.codexAccountId&&t.accountId===e.codexAccountId));return[{key:`all`,label:`总账号`,value:t.length,tone:`blue`},{key:`available`,label:`可用`,value:r(e=>[`healthy`,`warning`,`unknown`].includes(v(e).key)),tone:`green`},{key:`unavailable`,label:`不可用`,value:r(e=>[`invalid`,`expired`,`exhausted`].includes(v(e).key)),tone:`red`},{key:`unknown`,label:`待请求验证`,value:r(e=>v(e).key===`unknown`),tone:`blue`},{key:`login-invalid`,label:`登录失效`,value:r(e=>e.authStatus?.state===`token_invalidated`),tone:`red`},{key:`auth-error`,label:`认证异常`,value:r(e=>e.authStatus?.state===`auth_error`),tone:`red`},{key:`exhausted`,label:`额度耗尽`,value:r(e=>v(e).key===`exhausted`),tone:`orange`},{key:`free`,label:`Free`,value:r(e=>y(e)===`free`),tone:`muted`},{key:`plus`,label:`Plus`,value:r(e=>y(e)===`plus`),tone:`brand`},{key:`pro-team`,label:`Pro/Team`,value:r(e=>[`pro`,`team`,`enterprise`,`premium`].includes(y(e))),tone:`blue`},{key:`api-active`,label:`API 使用中`,value:r(e=>e.isActive),tone:`green`},{key:`codex-active`,label:`Codex 使用中`,value:i,tone:`green`},{key:`auto-included`,label:`配置参与`,value:r(e=>!n.has(e.profileId)),tone:`blue`},{key:`auto-excluded`,label:`手动排除`,value:r(e=>n.has(e.profileId)),tone:`orange`}]},[e.codexAccountId,e.config?.profiles,e.config?.settings.autoSwitch.excludedProfileIds]),g=Object.values(t).filter(Boolean).length,_=Object.keys(t).filter(e=>t[e]),x=(0,M.useMemo)(()=>c.map(e=>e.profileId),[c]);async function S(t,n){let r=await O(`/_gateway/admin/profiles/export`,{method:`POST`,headers:{"Content-Type":`application/json`},body:o(n?{profileIds:n}:{profileId:t})});D(`ai-zero-token-${n?`profiles-${n.length}`:t||`active`}.json`,r.profile),r.config?e.setConfig(r.config):await e.refreshConfig({silent:!0}),e.setStatus(n?`已导出 ${n.length} 个账号。`:`账号配置已导出。`)}async function C(t,n){if(!(t===`remove`&&!window.confirm(`确认删除 ${m(n,e.showEmails)}?`))){if((t===`activate`||t===`apply-codex`)&&b(n)){e.setStatus(`${m(n,e.showEmails)} 登录已失效,不能应用到${t===`activate`?`网关`:`Codex`}。`);return}if((t===`activate`||t===`apply-codex`)&&u(n)){let r=t===`activate`?`网关`:`Codex`;if(!window.confirm(`${m(n,e.showEmails)} 的额度看起来已耗尽,仍要应用到${r}吗?`))return}if(t===`export`){await S(n.profileId);return}e.setBusy(`profile:${t}:${n.profileId}`);try{let r=await O({activate:`/_gateway/admin/profiles/activate`,"apply-codex":`/_gateway/admin/codex/apply`,"sync-quota":`/_gateway/admin/profiles/sync-quota`,remove:`/_gateway/admin/profiles/remove`}[t],{method:`POST`,headers:{"Content-Type":`application/json`},body:o({profileId:n.profileId})}),i=`config`in r?r.config:r;if(e.setConfig(i),e.setStatus(t===`activate`?`已应用到网关。`:t===`apply-codex`?`已应用到本机 Codex。`:t===`sync-quota`?`额度信息已同步。`:`账号已删除。`),t===`apply-codex`)if(i.codexRestartSupported&&window.confirm(`Codex 账号已切换,是否现在重启 Codex 客户端?
2
2
 
3
3
  Codex 通常在启动时读取本机 auth.json,重启后新账号会立即生效。`))try{await O(`/_gateway/admin/desktop/restart-codex`,{method:`POST`}),e.setStatus(`已应用到本机 Codex,并已重启 Codex 客户端。`)}catch(t){e.setStatus(`已应用到本机 Codex,但重启 Codex 失败: ${k(t)}`)}else e.setStatus(`已应用到本机 Codex,重启 Codex 客户端后生效。`)}catch(t){e.setStatus(k(t))}finally{e.setBusy(null)}}}async function w(){let t=_;if(t.length===0){e.setStatus(`请先勾选要删除的账号。`);return}let r=e.config?.profiles.filter(e=>t.includes(e.profileId)).slice(0,3).map(t=>m(t,e.showEmails)),i=r?.length?`\n\n${r.join(`
4
4
  `)}${t.length>r.length?`\n等 ${t.length} 个账号`:``}`:``;if(window.confirm(`确认删除所选 ${t.length} 个账号?此操作不可撤销。${i}`)){e.setBusy(`bulk-remove`),e.setStatus(`正在删除 ${t.length} 个账号...`);try{let r=await O(`/_gateway/admin/profiles/remove-batch`,{method:`POST`,headers:{"Content-Type":`application/json`},body:o({profileIds:t})});e.setConfig(r),n({}),e.setStatus(`已删除 ${r.removedProfileCount??t.length} 个账号。`)}catch(t){e.setStatus(`删除所选失败: ${k(t)}`)}finally{e.setBusy(null)}}}function T(t,r){if(t.length===0){e.setStatus(`没有可选择的账号。`);return}n(e=>{let n={...e};for(let e of t)n[e]=!0;return n}),e.setStatus(r)}return(0,N.jsx)(P,{config:e.config,profiles:c,accountStats:l,showEmails:e.showEmails,filter:a,selectedProfiles:t,expandedProfiles:r,selectedCount:g,visibleCount:x.length,busy:e.busy,onFilter:s,onSelect:(e,t)=>n(n=>({...n,[e]:t})),onSelectVisible:()=>T(x,`已选择当前筛选结果 ${x.length} 个账号。`),onClearSelected:()=>{n({}),e.setStatus(`已取消选择。`)},onToggle:e=>i(t=>({...t,[e]:!t[e]})),onAction:C,onLocate:()=>e.activeProfile&&document.querySelector(`[data-profile-card="${e.activeProfile.profileId}"]`)?.scrollIntoView({behavior:`smooth`,block:`center`}),onExportSelected:()=>{let t=_;if(t.length===0){e.setStatus(`请先勾选要导出的账号。`);return}S(void 0,t).catch(t=>e.setStatus(t instanceof Error?t.message:String(t)))},onRemoveSelected:()=>void w(),onAddAccount:()=>e.setAccountModalOpen(!0),onRefreshStatus:()=>e.refreshConfig({runtime:!0}),onClearAccounts:()=>e.logout()})}export{R as AccountsPage};
@@ -1,4 +1,4 @@
1
- import{a as e,n as t,r as n,t as r}from"./jsx-runtime-DqpGtLhh.js";import{t as i}from"./server-BrjJPb9D.js";import{b as a,f as o,g as s,n as c,v as l,y as u}from"./index-BM5N4YUY.js";var d=t(`arrow-right`,[[`path`,{d:`M5 12h14`,key:`1ays0h`}],[`path`,{d:`m12 5 7 7-7 7`,key:`xquz4c`}]]),f=`# AI-Zero-Token Local Gateway Skill
1
+ import{a as e,n as t,r as n,t as r}from"./jsx-runtime-DqpGtLhh.js";import{t as i}from"./server-BrjJPb9D.js";import{b as a,f as o,g as s,n as c,v as l,y as u}from"./index-D3zM8GaW.js";var d=t(`arrow-right`,[[`path`,{d:`M5 12h14`,key:`1ays0h`}],[`path`,{d:`m12 5 7 7-7 7`,key:`xquz4c`}]]),f=`# AI-Zero-Token Local Gateway Skill
2
2
 
3
3
  ## Purpose
4
4
 
@@ -1 +1 @@
1
- import{a as e,n as t,r as n,t as r}from"./jsx-runtime-DqpGtLhh.js";import{t as i}from"./circle-check-ZYtn9GqY.js";import{t as a}from"./upload-CwXb7Q1b.js";import{C as o,w as s}from"./profiles-iNTmJFRe.js";import{_ as c,b as l,n as u,o as d,p as f,r as p}from"./index-BM5N4YUY.js";var m=t(`chevron-down`,[[`path`,{d:`m6 9 6 6 6-6`,key:`qrunsl`}]]),h=t(`link-2`,[[`path`,{d:`M9 17H7A5 5 0 0 1 7 7h2`,key:`8i5ue5`}],[`path`,{d:`M15 7h2a5 5 0 1 1 0 10h-2`,key:`1b9ql8`}],[`line`,{x1:`8`,x2:`16`,y1:`12`,y2:`12`,key:`1jonct`}]]),g=t(`trash-2`,[[`path`,{d:`M10 11v6`,key:`nco0om`}],[`path`,{d:`M14 11v6`,key:`outv1u`}],[`path`,{d:`M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6`,key:`miytrc`}],[`path`,{d:`M3 6h18`,key:`d0wm0j`}],[`path`,{d:`M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2`,key:`e791ji`}]]),_=e(n(),1),v=r();function y(e,t){return e?`${e.owner}/${e.repository} · ${e.branch}`:t?.hasToken?`已保存,待验证`:`尚未保存 token`}function b(e,t){if(!e)return t;try{let t=JSON.parse(e);if(typeof t.error?.message==`string`&&t.error.message.trim())return t.error.message}catch{}return e||t}async function x(e,t){let n=await d(e);return new Promise((r,i)=>{let a=new XMLHttpRequest;a.open(`POST`,`/_gateway/image-bed/upload`),a.setRequestHeader(`Content-Type`,`application/json`),a.responseType=`text`,a.upload.onprogress=e=>{e.lengthComputable&&e.total>0&&t(e.loaded/e.total*100)},a.onerror=()=>i(Error(`上传请求失败。`)),a.onload=()=>{let e=a.responseText||``;if(a.status<200||a.status>=300){i(Error(b(e,`HTTP ${a.status}`)));return}try{r(JSON.parse(e))}catch{i(Error(`上传响应解析失败。`))}},a.send(JSON.stringify({filename:e.name,dataUrl:n}))})}function S(e){let[t,n]=(0,_.useState)(null),[r,d]=(0,_.useState)(``),[b,S]=(0,_.useState)(!0),[C,w]=(0,_.useState)(null),[T,E]=(0,_.useState)([]),[D,O]=(0,_.useState)(12),[k,A]=(0,_.useState)(`正在读取图床配置...`),[j,M]=(0,_.useState)(!1),[N,P]=(0,_.useState)(null),[F,I]=(0,_.useState)(null),L=(0,_.useRef)(null),R=(0,_.useMemo)(()=>y(C,t),[C,t]),z=(0,_.useMemo)(()=>T.slice(0,D),[T,D]);(0,_.useEffect)(()=>{let e=!0;async function t(){try{let t=await f(`/_gateway/image-bed/config`);if(!e)return;n(t),S(!t.hasToken),A(t.hasToken?`GitHub token 已保存,正在验证连接...`:`请先保存一个 GitHub token。`),t.hasToken&&await V(!1)}catch(t){if(!e)return;A(p(t))}try{let t=await f(`/_gateway/image-bed/history?limit=100`);if(!e)return;E(t.items)}catch(t){if(!e)return;A(e=>`${e} ${p(t)}`)}}return t(),()=>{e=!1}},[]);async function B(){E((await f(`/_gateway/image-bed/history?limit=100`)).items)}async function V(t=!0){t&&e.setBusy(`image-bed-save`);try{let t=await f(`/_gateway/image-bed/validate`,{method:`POST`});return w(t),A(`已连接到 ${t.owner}/${t.repository},默认分支 ${t.branch}。`),e.setStatus(`图床连接正常:${t.owner}/${t.repository}`),t}catch(t){let n=p(t);throw w(null),A(`连接失败: ${n}`),e.setStatus(n),t}finally{t&&e.setBusy(null)}}async function H(){let t=r.trim();if(!t){A(`请先填写 GitHub token。`);return}e.setBusy(`image-bed-save`);try{n(await f(`/_gateway/image-bed/config`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({token:t})})),d(``),S(!1),A(`GitHub token 已保存,正在验证...`),e.setStatus(`GitHub token 已保存。`);try{await V(!1),A(`GitHub token 已保存并验证。`)}catch(e){S(!0),A(`已保存,但验证失败: ${p(e)}`)}}catch(t){let n=p(t);A(`保存失败: ${n}`),e.setStatus(n)}finally{e.setBusy(null)}}async function U(){e.setBusy(`image-bed-save`);try{n(await f(`/_gateway/image-bed/config`,{method:`DELETE`})),w(null),d(``),S(!0),A(`GitHub token 已清除。`),e.setStatus(`GitHub token 已清除。`)}catch(t){let n=p(t);A(`清除失败: ${n}`),e.setStatus(n)}finally{e.setBusy(null)}}async function W(){S(!0),d(``)}async function G(e,t){if(!e.type.startsWith(`image/`))throw Error(`文件 ${e.name} 不是图片。`);return x(e,t)}async function K(n){let r=Array.from(n).filter(e=>e.type.startsWith(`image/`));if(r.length===0){A(`请选择图片文件。`);return}if(!t?.hasToken){A(`请先保存 GitHub token,再上传图片。`);return}e.setBusy(`image-bed-upload`);try{let t=[];for(let n=0;n<r.length;n+=1){let i=r[n];P({phase:`reading`,fileName:i.name,fileIndex:n+1,totalFiles:r.length,percent:n/r.length*100}),A(`正在读取 ${n+1}/${r.length}: ${i.name}`),e.setStatus(`正在读取 ${n+1}/${r.length}: ${i.name}`);let a=await G(i,e=>{P({phase:`uploading`,fileName:i.name,fileIndex:n+1,totalFiles:r.length,percent:(n+e/100)/r.length*100})}),o={id:a.path,createdAt:Date.now(),filename:a.filename,path:a.path,url:a.url,htmlUrl:a.htmlUrl,downloadUrl:a.downloadUrl,owner:a.owner,repository:a.repository,branch:a.branch,size:a.size,mimeType:a.mimeType,previewUrl:a.url,sha:a.sha};t.unshift(o),E(e=>[o,...e.filter(e=>e.id!==o.id)].slice(0,100))}O(e=>Math.max(e,Math.min(12,T.length+t.length))),A(`已上传 ${r.length} 张图片。`),e.setStatus(`已上传 ${r.length} 张图片。`),await B()}catch(t){let n=p(t);A(`上传失败: ${n}`),e.setStatus(n)}finally{P(null),e.setBusy(null)}}async function q(t){let n=await u(t)?`链接已复制。`:`链接复制失败。`;A(n),e.setStatus(n)}function J(e){let t=Array.from(e.currentTarget.files||[]);e.currentTarget.value=``,t.length!==0&&K(t)}async function Y(){await f(`/_gateway/image-bed/history`,{method:`DELETE`}),E([]),O(12),A(`历史记录已清空。`),e.setStatus(`历史记录已清空。`)}async function X(t){if(window.confirm(`确认从 GitHub 仓库删除 ${t.filename} 吗?删除后原链接会失效。`)){I(t.id),e.setBusy(`image-bed-delete`);try{E((await f(`/_gateway/image-bed/history/${encodeURIComponent(t.id)}`,{method:`DELETE`})).items),A(`已删除 ${t.filename}。`),e.setStatus(`已从图床删除 ${t.filename}。`)}catch(t){let n=p(t);A(`删除失败: ${n}`),e.setStatus(n)}finally{I(null),e.setBusy(null)}}}let Z=e.busy===`image-bed-save`||e.busy===`image-bed-upload`||e.busy===`image-bed-delete`,Q=T[0];return(0,v.jsxs)(`section`,{className:`image-bed-page`,children:[(0,v.jsxs)(`div`,{className:`image-bed-workbench`,children:[(0,v.jsxs)(`section`,{className:`image-bed-upload-panel ${j?`is-dragging`:``}`,children:[(0,v.jsxs)(`div`,{className:`image-bed-upload-copy`,children:[(0,v.jsx)(`span`,{children:t?.hasToken?`GitHub 图床已准备`:`先配置 GitHub token`}),(0,v.jsx)(`h2`,{children:`拖入图片,直接拿公网链接`}),(0,v.jsxs)(`p`,{children:[`文件会写入公开仓库 `,(0,v.jsx)(`strong`,{children:t?.repository||`azt-img-bed`}),` 的 `,(0,v.jsx)(`strong`,{children:t?.pathPrefix||`images`}),` 目录,上传结果会保存在本机历史里。`]})]}),(0,v.jsxs)(`div`,{className:`upload-dropzone ${j?`is-dragging`:``} ${t?.hasToken?``:`is-disabled`}`,role:`button`,tabIndex:0,onClick:()=>{if(!t?.hasToken){A(`请先保存 GitHub token,再上传图片。`);return}L.current&&(L.current.value=``,L.current.click())},onKeyDown:e=>{if(e.key===`Enter`||e.key===` `){if(e.preventDefault(),!t?.hasToken){A(`请先保存 GitHub token,再上传图片。`);return}L.current&&(L.current.value=``,L.current.click())}},onDragOver:e=>{e.preventDefault(),M(!0)},onDragLeave:()=>M(!1),onDrop:e=>{e.preventDefault(),M(!1),K(e.dataTransfer.files)},children:[(0,v.jsx)(`input`,{ref:L,className:`upload-dropzone-input`,type:`file`,accept:`image/*`,multiple:!0,onChange:J}),(0,v.jsx)(`div`,{className:`upload-dropzone-icon`,children:(0,v.jsx)(a,{size:22})}),(0,v.jsx)(`strong`,{children:`选择图片或拖拽到这里`}),(0,v.jsx)(`span`,{children:t?.hasToken?`支持批量上传,完成后自动生成可访问链接。`:`保存并验证 token 后即可上传。`})]}),N?(0,v.jsxs)(`div`,{className:`upload-progress-block`,"aria-live":`polite`,children:[(0,v.jsxs)(`div`,{className:`upload-progress-head`,children:[(0,v.jsx)(`strong`,{children:N.phase===`reading`?`正在读取`:`正在上传`}),(0,v.jsxs)(`span`,{children:[N.fileIndex,`/`,N.totalFiles,` · `,N.fileName]})]}),(0,v.jsx)(`div`,{className:`upload-progress-track`,role:`progressbar`,"aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":Math.round(N.percent),children:(0,v.jsx)(`div`,{className:`upload-progress-fill`,style:{width:`${Math.max(4,Math.min(100,N.percent))}%`}})})]}):Q?(0,v.jsxs)(`div`,{className:`image-bed-latest`,children:[(0,v.jsx)(`button`,{type:`button`,className:`image-bed-latest-preview`,onClick:()=>void q(Q.url),title:`点击复制链接`,children:(0,v.jsx)(`img`,{loading:`lazy`,decoding:`async`,src:Q.previewUrl,alt:Q.filename})}),(0,v.jsxs)(`div`,{className:`image-bed-latest-info`,children:[(0,v.jsx)(`span`,{children:`最近上传`}),(0,v.jsx)(`strong`,{children:Q.filename}),(0,v.jsx)(`code`,{children:Q.url})]}),(0,v.jsxs)(`button`,{className:`btn-primary`,type:`button`,onClick:()=>void q(Q.url),children:[(0,v.jsx)(l,{size:16}),`复制链接`]})]}):(0,v.jsx)(`div`,{className:`image-bed-upload-note`,children:k})]}),(0,v.jsxs)(`aside`,{className:`image-bed-side-stack`,children:[(0,v.jsxs)(`section`,{className:`image-bed-side-card`,children:[(0,v.jsxs)(`div`,{className:`image-bed-section-head`,children:[(0,v.jsx)(`h4`,{children:`连接`}),(0,v.jsx)(`span`,{className:`image-bed-status-dot ${C?`is-ok`:t?.hasToken?`is-warn`:``}`})]}),(0,v.jsx)(`strong`,{className:`image-bed-connection-label`,children:R}),(0,v.jsx)(`p`,{children:k})]}),(0,v.jsxs)(`section`,{className:`image-bed-side-card`,children:[(0,v.jsxs)(`div`,{className:`image-bed-section-head`,children:[(0,v.jsx)(`h4`,{children:`Token`}),!b&&t?.hasToken&&(0,v.jsx)(`button`,{className:`image-bed-link-button`,type:`button`,onClick:()=>void W(),children:`修改`})]}),b||!t?.hasToken?(0,v.jsxs)(v.Fragment,{children:[(0,v.jsxs)(`label`,{className:`field`,children:[(0,v.jsx)(`span`,{children:`GitHub token`}),(0,v.jsx)(`input`,{className:`input`,value:r,onChange:e=>d(e.target.value),placeholder:`github_pat_...`,spellCheck:!1,autoComplete:`off`}),(0,v.jsxs)(`p`,{className:`image-bed-token-hint`,children:[`推荐使用 fine-grained token,只给公开仓库 `,(0,v.jsx)(`code`,{children:`azt-img-bed`}),` 的 Contents 读写权限。`]})]}),(0,v.jsxs)(`div`,{className:`image-bed-token-actions`,children:[(0,v.jsxs)(`button`,{className:`btn-primary`,type:`button`,onClick:()=>void H(),disabled:Z||!r.trim(),children:[e.busy===`image-bed-save`?(0,v.jsx)(c,{className:`spin`,size:16}):(0,v.jsx)(i,{size:16}),`保存并验证`]}),(0,v.jsxs)(`button`,{className:`btn-secondary`,type:`button`,onClick:U,disabled:!t?.hasToken,children:[(0,v.jsx)(g,{size:16}),`清除`]})]})]}):(0,v.jsxs)(`div`,{className:`image-bed-token-summary`,children:[(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`span`,{children:`已保存到本机`}),(0,v.jsx)(`strong`,{children:`GitHub token`})]}),(0,v.jsx)(`button`,{className:`btn-secondary icon-only`,type:`button`,onClick:U,title:`清除 Token`,children:(0,v.jsx)(g,{size:16})})]})]}),(0,v.jsxs)(`section`,{className:`image-bed-side-card image-bed-target-card`,children:[(0,v.jsx)(`h4`,{children:`目标`}),(0,v.jsxs)(`div`,{className:`image-bed-target-list`,children:[(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`span`,{children:`仓库`}),(0,v.jsx)(`strong`,{children:t?.repository||`azt-img-bed`})]}),(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`span`,{children:`分支`}),(0,v.jsx)(`strong`,{children:t?.defaultBranch||`auto`})]}),(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`span`,{children:`目录`}),(0,v.jsx)(`strong`,{children:t?.pathPrefix||`images`})]})]})]})]})]}),(0,v.jsxs)(`section`,{className:`image-bed-gallery-section`,children:[(0,v.jsxs)(`div`,{className:`image-bed-section-head`,children:[(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`h4`,{children:`上传历史`}),(0,v.jsx)(`p`,{children:`本机保存最近 100 条,只加载当前可见缩略图。`})]}),(0,v.jsx)(`button`,{className:`btn-secondary icon-only`,type:`button`,onClick:()=>void Y(),disabled:T.length===0,title:`清空历史`,children:(0,v.jsx)(g,{size:16})})]}),T.length===0?(0,v.jsx)(`div`,{className:`image-bed-empty`,children:`还没有上传记录。上传完成后,这里会以图库形式展示预览和链接。`}):(0,v.jsx)(`div`,{className:`image-bed-results-grid`,children:z.map(e=>(0,v.jsxs)(`figure`,{className:`image-bed-result-card`,children:[(0,v.jsx)(`button`,{type:`button`,className:`image-bed-preview-button`,onClick:()=>void q(e.url),title:`点击复制链接`,children:(0,v.jsx)(`img`,{loading:`lazy`,decoding:`async`,src:e.previewUrl,alt:e.filename})}),(0,v.jsxs)(`figcaption`,{children:[(0,v.jsx)(`strong`,{children:e.filename}),(0,v.jsxs)(`span`,{children:[o(e.size),` · `,e.mimeType]}),(0,v.jsx)(`code`,{children:s(e.createdAt)})]}),(0,v.jsxs)(`div`,{className:`image-bed-result-actions`,children:[(0,v.jsx)(`button`,{className:`image-bed-card-action`,type:`button`,onClick:()=>void q(e.url),title:`复制链接`,"aria-label":`复制链接`,children:(0,v.jsx)(l,{size:15})}),(0,v.jsx)(`a`,{className:`image-bed-card-action`,href:e.url,target:`_blank`,rel:`noreferrer`,title:`打开原图`,"aria-label":`打开原图`,children:(0,v.jsx)(h,{size:15})}),(0,v.jsx)(`button`,{className:`image-bed-card-action is-danger`,type:`button`,onClick:()=>void X(e),title:`删除图床文件`,"aria-label":`删除图床文件`,disabled:F===e.id,children:F===e.id?(0,v.jsx)(c,{className:`spin`,size:15}):(0,v.jsx)(g,{size:15})})]})]},e.path))}),T.length>D&&(0,v.jsxs)(`button`,{className:`btn-secondary image-bed-load-more`,type:`button`,onClick:()=>O(e=>Math.min(e+12,T.length)),children:[(0,v.jsx)(m,{size:16}),`加载更多`]})]}),(0,v.jsxs)(`details`,{className:`image-bed-help-section`,children:[(0,v.jsx)(`summary`,{children:`GitHub token 创建说明`}),(0,v.jsxs)(`p`,{className:`image-bed-help-intro`,children:[`这个图床固定使用当前 GitHub 账号下的公开仓库 `,(0,v.jsx)(`code`,{children:`azt-img-bed`}),`,图片会写入 `,(0,v.jsx)(`code`,{children:`images`}),` 目录并返回 raw.githubusercontent.com 原图链接。`]}),(0,v.jsxs)(`ol`,{className:`image-bed-steps`,children:[(0,v.jsxs)(`li`,{children:[(0,v.jsx)(`strong`,{children:`先建公开仓库。`}),` 在 GitHub 新建仓库 `,(0,v.jsx)(`code`,{children:`azt-img-bed`}),`,Visibility 选 `,(0,v.jsx)(`strong`,{children:`Public`}),`。如果仓库已经存在,确认它属于这个 token 对应的个人账号,并且不是 Private。`]}),(0,v.jsxs)(`li`,{children:[(0,v.jsx)(`strong`,{children:`创建 fine-grained token。`}),` 打开 GitHub 的 `,(0,v.jsx)(`a`,{href:`https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens`,target:`_blank`,rel:`noreferrer`,children:`Personal access tokens`}),` 页面,选择 `,(0,v.jsx)(`strong`,{children:`Fine-grained tokens`}),`,点 `,(0,v.jsx)(`strong`,{children:`Generate new token`}),`。`]}),(0,v.jsxs)(`li`,{children:[(0,v.jsx)(`strong`,{children:`限制仓库范围。`}),` Token name 可填 `,(0,v.jsx)(`code`,{children:`AI Zero Token image bed`}),`,Expiration 按需设置,Resource owner 选自己的账号;Repository access 选择 `,(0,v.jsx)(`strong`,{children:`Only select repositories`}),`,只勾选 `,(0,v.jsx)(`code`,{children:`azt-img-bed`}),`。`]}),(0,v.jsxs)(`li`,{children:[(0,v.jsx)(`strong`,{children:`给最小权限。`}),` 在 Repository permissions 里把 `,(0,v.jsx)(`strong`,{children:`Contents`}),` 设置为 `,(0,v.jsx)(`strong`,{children:`Read and write`}),`;`,(0,v.jsx)(`strong`,{children:`Metadata`}),` 保持默认 Read-only 即可,其它权限不需要打开。`]}),(0,v.jsxs)(`li`,{children:[(0,v.jsx)(`strong`,{children:`复制并验证。`}),` GitHub 只会展示一次生成后的 token,复制后回到这里粘贴,点击 `,(0,v.jsx)(`strong`,{children:`保存并验证`}),`。验证通过后就可以拖图上传。`]})]}),(0,v.jsxs)(`dl`,{className:`image-bed-help-facts`,children:[(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`dt`,{children:`Token 格式`}),(0,v.jsxs)(`dd`,{children:[`fine-grained token 通常以 `,(0,v.jsx)(`code`,{children:`github_pat_`}),` 开头。`]})]}),(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`dt`,{children:`验证失败`}),(0,v.jsxs)(`dd`,{children:[`如果提示未找到仓库,优先检查仓库名是否为 `,(0,v.jsx)(`code`,{children:`azt-img-bed`}),`、仓库是否 Public、Repository access 是否选中了这个仓库。`]})]})]}),(0,v.jsx)(`p`,{className:`hint`,children:`Token 要像密码一样保管,不要发给别人。这个页面只会把它保存到你本机状态目录。`})]})]})}export{S as ImageBedPage};
1
+ import{a as e,n as t,r as n,t as r}from"./jsx-runtime-DqpGtLhh.js";import{t as i}from"./circle-check-ZYtn9GqY.js";import{t as a}from"./upload-CwXb7Q1b.js";import{C as o,w as s}from"./profiles-iNTmJFRe.js";import{_ as c,b as l,n as u,o as d,p as f,r as p}from"./index-D3zM8GaW.js";var m=t(`chevron-down`,[[`path`,{d:`m6 9 6 6 6-6`,key:`qrunsl`}]]),h=t(`link-2`,[[`path`,{d:`M9 17H7A5 5 0 0 1 7 7h2`,key:`8i5ue5`}],[`path`,{d:`M15 7h2a5 5 0 1 1 0 10h-2`,key:`1b9ql8`}],[`line`,{x1:`8`,x2:`16`,y1:`12`,y2:`12`,key:`1jonct`}]]),g=t(`trash-2`,[[`path`,{d:`M10 11v6`,key:`nco0om`}],[`path`,{d:`M14 11v6`,key:`outv1u`}],[`path`,{d:`M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6`,key:`miytrc`}],[`path`,{d:`M3 6h18`,key:`d0wm0j`}],[`path`,{d:`M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2`,key:`e791ji`}]]),_=e(n(),1),v=r();function y(e,t){return e?`${e.owner}/${e.repository} · ${e.branch}`:t?.hasToken?`已保存,待验证`:`尚未保存 token`}function b(e,t){if(!e)return t;try{let t=JSON.parse(e);if(typeof t.error?.message==`string`&&t.error.message.trim())return t.error.message}catch{}return e||t}async function x(e,t){let n=await d(e);return new Promise((r,i)=>{let a=new XMLHttpRequest;a.open(`POST`,`/_gateway/image-bed/upload`),a.setRequestHeader(`Content-Type`,`application/json`),a.responseType=`text`,a.upload.onprogress=e=>{e.lengthComputable&&e.total>0&&t(e.loaded/e.total*100)},a.onerror=()=>i(Error(`上传请求失败。`)),a.onload=()=>{let e=a.responseText||``;if(a.status<200||a.status>=300){i(Error(b(e,`HTTP ${a.status}`)));return}try{r(JSON.parse(e))}catch{i(Error(`上传响应解析失败。`))}},a.send(JSON.stringify({filename:e.name,dataUrl:n}))})}function S(e){let[t,n]=(0,_.useState)(null),[r,d]=(0,_.useState)(``),[b,S]=(0,_.useState)(!0),[C,w]=(0,_.useState)(null),[T,E]=(0,_.useState)([]),[D,O]=(0,_.useState)(12),[k,A]=(0,_.useState)(`正在读取图床配置...`),[j,M]=(0,_.useState)(!1),[N,P]=(0,_.useState)(null),[F,I]=(0,_.useState)(null),L=(0,_.useRef)(null),R=(0,_.useMemo)(()=>y(C,t),[C,t]),z=(0,_.useMemo)(()=>T.slice(0,D),[T,D]);(0,_.useEffect)(()=>{let e=!0;async function t(){try{let t=await f(`/_gateway/image-bed/config`);if(!e)return;n(t),S(!t.hasToken),A(t.hasToken?`GitHub token 已保存,正在验证连接...`:`请先保存一个 GitHub token。`),t.hasToken&&await V(!1)}catch(t){if(!e)return;A(p(t))}try{let t=await f(`/_gateway/image-bed/history?limit=100`);if(!e)return;E(t.items)}catch(t){if(!e)return;A(e=>`${e} ${p(t)}`)}}return t(),()=>{e=!1}},[]);async function B(){E((await f(`/_gateway/image-bed/history?limit=100`)).items)}async function V(t=!0){t&&e.setBusy(`image-bed-save`);try{let t=await f(`/_gateway/image-bed/validate`,{method:`POST`});return w(t),A(`已连接到 ${t.owner}/${t.repository},默认分支 ${t.branch}。`),e.setStatus(`图床连接正常:${t.owner}/${t.repository}`),t}catch(t){let n=p(t);throw w(null),A(`连接失败: ${n}`),e.setStatus(n),t}finally{t&&e.setBusy(null)}}async function H(){let t=r.trim();if(!t){A(`请先填写 GitHub token。`);return}e.setBusy(`image-bed-save`);try{n(await f(`/_gateway/image-bed/config`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({token:t})})),d(``),S(!1),A(`GitHub token 已保存,正在验证...`),e.setStatus(`GitHub token 已保存。`);try{await V(!1),A(`GitHub token 已保存并验证。`)}catch(e){S(!0),A(`已保存,但验证失败: ${p(e)}`)}}catch(t){let n=p(t);A(`保存失败: ${n}`),e.setStatus(n)}finally{e.setBusy(null)}}async function U(){e.setBusy(`image-bed-save`);try{n(await f(`/_gateway/image-bed/config`,{method:`DELETE`})),w(null),d(``),S(!0),A(`GitHub token 已清除。`),e.setStatus(`GitHub token 已清除。`)}catch(t){let n=p(t);A(`清除失败: ${n}`),e.setStatus(n)}finally{e.setBusy(null)}}async function W(){S(!0),d(``)}async function G(e,t){if(!e.type.startsWith(`image/`))throw Error(`文件 ${e.name} 不是图片。`);return x(e,t)}async function K(n){let r=Array.from(n).filter(e=>e.type.startsWith(`image/`));if(r.length===0){A(`请选择图片文件。`);return}if(!t?.hasToken){A(`请先保存 GitHub token,再上传图片。`);return}e.setBusy(`image-bed-upload`);try{let t=[];for(let n=0;n<r.length;n+=1){let i=r[n];P({phase:`reading`,fileName:i.name,fileIndex:n+1,totalFiles:r.length,percent:n/r.length*100}),A(`正在读取 ${n+1}/${r.length}: ${i.name}`),e.setStatus(`正在读取 ${n+1}/${r.length}: ${i.name}`);let a=await G(i,e=>{P({phase:`uploading`,fileName:i.name,fileIndex:n+1,totalFiles:r.length,percent:(n+e/100)/r.length*100})}),o={id:a.path,createdAt:Date.now(),filename:a.filename,path:a.path,url:a.url,htmlUrl:a.htmlUrl,downloadUrl:a.downloadUrl,owner:a.owner,repository:a.repository,branch:a.branch,size:a.size,mimeType:a.mimeType,previewUrl:a.url,sha:a.sha};t.unshift(o),E(e=>[o,...e.filter(e=>e.id!==o.id)].slice(0,100))}O(e=>Math.max(e,Math.min(12,T.length+t.length))),A(`已上传 ${r.length} 张图片。`),e.setStatus(`已上传 ${r.length} 张图片。`),await B()}catch(t){let n=p(t);A(`上传失败: ${n}`),e.setStatus(n)}finally{P(null),e.setBusy(null)}}async function q(t){let n=await u(t)?`链接已复制。`:`链接复制失败。`;A(n),e.setStatus(n)}function J(e){let t=Array.from(e.currentTarget.files||[]);e.currentTarget.value=``,t.length!==0&&K(t)}async function Y(){await f(`/_gateway/image-bed/history`,{method:`DELETE`}),E([]),O(12),A(`历史记录已清空。`),e.setStatus(`历史记录已清空。`)}async function X(t){if(window.confirm(`确认从 GitHub 仓库删除 ${t.filename} 吗?删除后原链接会失效。`)){I(t.id),e.setBusy(`image-bed-delete`);try{E((await f(`/_gateway/image-bed/history/${encodeURIComponent(t.id)}`,{method:`DELETE`})).items),A(`已删除 ${t.filename}。`),e.setStatus(`已从图床删除 ${t.filename}。`)}catch(t){let n=p(t);A(`删除失败: ${n}`),e.setStatus(n)}finally{I(null),e.setBusy(null)}}}let Z=e.busy===`image-bed-save`||e.busy===`image-bed-upload`||e.busy===`image-bed-delete`,Q=T[0];return(0,v.jsxs)(`section`,{className:`image-bed-page`,children:[(0,v.jsxs)(`div`,{className:`image-bed-workbench`,children:[(0,v.jsxs)(`section`,{className:`image-bed-upload-panel ${j?`is-dragging`:``}`,children:[(0,v.jsxs)(`div`,{className:`image-bed-upload-copy`,children:[(0,v.jsx)(`span`,{children:t?.hasToken?`GitHub 图床已准备`:`先配置 GitHub token`}),(0,v.jsx)(`h2`,{children:`拖入图片,直接拿公网链接`}),(0,v.jsxs)(`p`,{children:[`文件会写入公开仓库 `,(0,v.jsx)(`strong`,{children:t?.repository||`azt-img-bed`}),` 的 `,(0,v.jsx)(`strong`,{children:t?.pathPrefix||`images`}),` 目录,上传结果会保存在本机历史里。`]})]}),(0,v.jsxs)(`div`,{className:`upload-dropzone ${j?`is-dragging`:``} ${t?.hasToken?``:`is-disabled`}`,role:`button`,tabIndex:0,onClick:()=>{if(!t?.hasToken){A(`请先保存 GitHub token,再上传图片。`);return}L.current&&(L.current.value=``,L.current.click())},onKeyDown:e=>{if(e.key===`Enter`||e.key===` `){if(e.preventDefault(),!t?.hasToken){A(`请先保存 GitHub token,再上传图片。`);return}L.current&&(L.current.value=``,L.current.click())}},onDragOver:e=>{e.preventDefault(),M(!0)},onDragLeave:()=>M(!1),onDrop:e=>{e.preventDefault(),M(!1),K(e.dataTransfer.files)},children:[(0,v.jsx)(`input`,{ref:L,className:`upload-dropzone-input`,type:`file`,accept:`image/*`,multiple:!0,onChange:J}),(0,v.jsx)(`div`,{className:`upload-dropzone-icon`,children:(0,v.jsx)(a,{size:22})}),(0,v.jsx)(`strong`,{children:`选择图片或拖拽到这里`}),(0,v.jsx)(`span`,{children:t?.hasToken?`支持批量上传,完成后自动生成可访问链接。`:`保存并验证 token 后即可上传。`})]}),N?(0,v.jsxs)(`div`,{className:`upload-progress-block`,"aria-live":`polite`,children:[(0,v.jsxs)(`div`,{className:`upload-progress-head`,children:[(0,v.jsx)(`strong`,{children:N.phase===`reading`?`正在读取`:`正在上传`}),(0,v.jsxs)(`span`,{children:[N.fileIndex,`/`,N.totalFiles,` · `,N.fileName]})]}),(0,v.jsx)(`div`,{className:`upload-progress-track`,role:`progressbar`,"aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":Math.round(N.percent),children:(0,v.jsx)(`div`,{className:`upload-progress-fill`,style:{width:`${Math.max(4,Math.min(100,N.percent))}%`}})})]}):Q?(0,v.jsxs)(`div`,{className:`image-bed-latest`,children:[(0,v.jsx)(`button`,{type:`button`,className:`image-bed-latest-preview`,onClick:()=>void q(Q.url),title:`点击复制链接`,children:(0,v.jsx)(`img`,{loading:`lazy`,decoding:`async`,src:Q.previewUrl,alt:Q.filename})}),(0,v.jsxs)(`div`,{className:`image-bed-latest-info`,children:[(0,v.jsx)(`span`,{children:`最近上传`}),(0,v.jsx)(`strong`,{children:Q.filename}),(0,v.jsx)(`code`,{children:Q.url})]}),(0,v.jsxs)(`button`,{className:`btn-primary`,type:`button`,onClick:()=>void q(Q.url),children:[(0,v.jsx)(l,{size:16}),`复制链接`]})]}):(0,v.jsx)(`div`,{className:`image-bed-upload-note`,children:k})]}),(0,v.jsxs)(`aside`,{className:`image-bed-side-stack`,children:[(0,v.jsxs)(`section`,{className:`image-bed-side-card`,children:[(0,v.jsxs)(`div`,{className:`image-bed-section-head`,children:[(0,v.jsx)(`h4`,{children:`连接`}),(0,v.jsx)(`span`,{className:`image-bed-status-dot ${C?`is-ok`:t?.hasToken?`is-warn`:``}`})]}),(0,v.jsx)(`strong`,{className:`image-bed-connection-label`,children:R}),(0,v.jsx)(`p`,{children:k})]}),(0,v.jsxs)(`section`,{className:`image-bed-side-card`,children:[(0,v.jsxs)(`div`,{className:`image-bed-section-head`,children:[(0,v.jsx)(`h4`,{children:`Token`}),!b&&t?.hasToken&&(0,v.jsx)(`button`,{className:`image-bed-link-button`,type:`button`,onClick:()=>void W(),children:`修改`})]}),b||!t?.hasToken?(0,v.jsxs)(v.Fragment,{children:[(0,v.jsxs)(`label`,{className:`field`,children:[(0,v.jsx)(`span`,{children:`GitHub token`}),(0,v.jsx)(`input`,{className:`input`,value:r,onChange:e=>d(e.target.value),placeholder:`github_pat_...`,spellCheck:!1,autoComplete:`off`}),(0,v.jsxs)(`p`,{className:`image-bed-token-hint`,children:[`推荐使用 fine-grained token,只给公开仓库 `,(0,v.jsx)(`code`,{children:`azt-img-bed`}),` 的 Contents 读写权限。`]})]}),(0,v.jsxs)(`div`,{className:`image-bed-token-actions`,children:[(0,v.jsxs)(`button`,{className:`btn-primary`,type:`button`,onClick:()=>void H(),disabled:Z||!r.trim(),children:[e.busy===`image-bed-save`?(0,v.jsx)(c,{className:`spin`,size:16}):(0,v.jsx)(i,{size:16}),`保存并验证`]}),(0,v.jsxs)(`button`,{className:`btn-secondary`,type:`button`,onClick:U,disabled:!t?.hasToken,children:[(0,v.jsx)(g,{size:16}),`清除`]})]})]}):(0,v.jsxs)(`div`,{className:`image-bed-token-summary`,children:[(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`span`,{children:`已保存到本机`}),(0,v.jsx)(`strong`,{children:`GitHub token`})]}),(0,v.jsx)(`button`,{className:`btn-secondary icon-only`,type:`button`,onClick:U,title:`清除 Token`,children:(0,v.jsx)(g,{size:16})})]})]}),(0,v.jsxs)(`section`,{className:`image-bed-side-card image-bed-target-card`,children:[(0,v.jsx)(`h4`,{children:`目标`}),(0,v.jsxs)(`div`,{className:`image-bed-target-list`,children:[(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`span`,{children:`仓库`}),(0,v.jsx)(`strong`,{children:t?.repository||`azt-img-bed`})]}),(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`span`,{children:`分支`}),(0,v.jsx)(`strong`,{children:t?.defaultBranch||`auto`})]}),(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`span`,{children:`目录`}),(0,v.jsx)(`strong`,{children:t?.pathPrefix||`images`})]})]})]})]})]}),(0,v.jsxs)(`section`,{className:`image-bed-gallery-section`,children:[(0,v.jsxs)(`div`,{className:`image-bed-section-head`,children:[(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`h4`,{children:`上传历史`}),(0,v.jsx)(`p`,{children:`本机保存最近 100 条,只加载当前可见缩略图。`})]}),(0,v.jsx)(`button`,{className:`btn-secondary icon-only`,type:`button`,onClick:()=>void Y(),disabled:T.length===0,title:`清空历史`,children:(0,v.jsx)(g,{size:16})})]}),T.length===0?(0,v.jsx)(`div`,{className:`image-bed-empty`,children:`还没有上传记录。上传完成后,这里会以图库形式展示预览和链接。`}):(0,v.jsx)(`div`,{className:`image-bed-results-grid`,children:z.map(e=>(0,v.jsxs)(`figure`,{className:`image-bed-result-card`,children:[(0,v.jsx)(`button`,{type:`button`,className:`image-bed-preview-button`,onClick:()=>void q(e.url),title:`点击复制链接`,children:(0,v.jsx)(`img`,{loading:`lazy`,decoding:`async`,src:e.previewUrl,alt:e.filename})}),(0,v.jsxs)(`figcaption`,{children:[(0,v.jsx)(`strong`,{children:e.filename}),(0,v.jsxs)(`span`,{children:[o(e.size),` · `,e.mimeType]}),(0,v.jsx)(`code`,{children:s(e.createdAt)})]}),(0,v.jsxs)(`div`,{className:`image-bed-result-actions`,children:[(0,v.jsx)(`button`,{className:`image-bed-card-action`,type:`button`,onClick:()=>void q(e.url),title:`复制链接`,"aria-label":`复制链接`,children:(0,v.jsx)(l,{size:15})}),(0,v.jsx)(`a`,{className:`image-bed-card-action`,href:e.url,target:`_blank`,rel:`noreferrer`,title:`打开原图`,"aria-label":`打开原图`,children:(0,v.jsx)(h,{size:15})}),(0,v.jsx)(`button`,{className:`image-bed-card-action is-danger`,type:`button`,onClick:()=>void X(e),title:`删除图床文件`,"aria-label":`删除图床文件`,disabled:F===e.id,children:F===e.id?(0,v.jsx)(c,{className:`spin`,size:15}):(0,v.jsx)(g,{size:15})})]})]},e.path))}),T.length>D&&(0,v.jsxs)(`button`,{className:`btn-secondary image-bed-load-more`,type:`button`,onClick:()=>O(e=>Math.min(e+12,T.length)),children:[(0,v.jsx)(m,{size:16}),`加载更多`]})]}),(0,v.jsxs)(`details`,{className:`image-bed-help-section`,children:[(0,v.jsx)(`summary`,{children:`GitHub token 创建说明`}),(0,v.jsxs)(`p`,{className:`image-bed-help-intro`,children:[`这个图床固定使用当前 GitHub 账号下的公开仓库 `,(0,v.jsx)(`code`,{children:`azt-img-bed`}),`,图片会写入 `,(0,v.jsx)(`code`,{children:`images`}),` 目录并返回 raw.githubusercontent.com 原图链接。`]}),(0,v.jsxs)(`ol`,{className:`image-bed-steps`,children:[(0,v.jsxs)(`li`,{children:[(0,v.jsx)(`strong`,{children:`先建公开仓库。`}),` 在 GitHub 新建仓库 `,(0,v.jsx)(`code`,{children:`azt-img-bed`}),`,Visibility 选 `,(0,v.jsx)(`strong`,{children:`Public`}),`。如果仓库已经存在,确认它属于这个 token 对应的个人账号,并且不是 Private。`]}),(0,v.jsxs)(`li`,{children:[(0,v.jsx)(`strong`,{children:`创建 fine-grained token。`}),` 打开 GitHub 的 `,(0,v.jsx)(`a`,{href:`https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens`,target:`_blank`,rel:`noreferrer`,children:`Personal access tokens`}),` 页面,选择 `,(0,v.jsx)(`strong`,{children:`Fine-grained tokens`}),`,点 `,(0,v.jsx)(`strong`,{children:`Generate new token`}),`。`]}),(0,v.jsxs)(`li`,{children:[(0,v.jsx)(`strong`,{children:`限制仓库范围。`}),` Token name 可填 `,(0,v.jsx)(`code`,{children:`AI Zero Token image bed`}),`,Expiration 按需设置,Resource owner 选自己的账号;Repository access 选择 `,(0,v.jsx)(`strong`,{children:`Only select repositories`}),`,只勾选 `,(0,v.jsx)(`code`,{children:`azt-img-bed`}),`。`]}),(0,v.jsxs)(`li`,{children:[(0,v.jsx)(`strong`,{children:`给最小权限。`}),` 在 Repository permissions 里把 `,(0,v.jsx)(`strong`,{children:`Contents`}),` 设置为 `,(0,v.jsx)(`strong`,{children:`Read and write`}),`;`,(0,v.jsx)(`strong`,{children:`Metadata`}),` 保持默认 Read-only 即可,其它权限不需要打开。`]}),(0,v.jsxs)(`li`,{children:[(0,v.jsx)(`strong`,{children:`复制并验证。`}),` GitHub 只会展示一次生成后的 token,复制后回到这里粘贴,点击 `,(0,v.jsx)(`strong`,{children:`保存并验证`}),`。验证通过后就可以拖图上传。`]})]}),(0,v.jsxs)(`dl`,{className:`image-bed-help-facts`,children:[(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`dt`,{children:`Token 格式`}),(0,v.jsxs)(`dd`,{children:[`fine-grained token 通常以 `,(0,v.jsx)(`code`,{children:`github_pat_`}),` 开头。`]})]}),(0,v.jsxs)(`div`,{children:[(0,v.jsx)(`dt`,{children:`验证失败`}),(0,v.jsxs)(`dd`,{children:[`如果提示未找到仓库,优先检查仓库名是否为 `,(0,v.jsx)(`code`,{children:`azt-img-bed`}),`、仓库是否 Public、Repository access 是否选中了这个仓库。`]})]})]}),(0,v.jsx)(`p`,{className:`hint`,children:`Token 要像密码一样保管,不要发给别人。这个页面只会把它保存到你本机状态目录。`})]})]})}export{S as ImageBedPage};