When the phone goes to background, JS is paused and HTTP keepalive connections die. On return, the first poll's fetch can hit "Failed to fetch" because of a stale connection / DNS / TLS state. The original code showed an error immediately on a single failure. Now: same polling loop tolerates up to 5 consecutive errors with exponential backoff (1s, 2s, 4s, 8s) before surfacing showError. Any successful poll resets the error counter. This rides through the typical 1-3 second post-resume blip without any user-visible failure. Pairs with the earlier WS auto-reconnect + visibilitychange handler (both bridge channels — WS and HTTP polling — now recover cleanly). Bump app.js?v=87 -> 88.
575 lines
30 KiB
HTML
575 lines
30 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>GenericAgent</title>
|
||
<link rel="stylesheet" href="assets/fonts/fonts.css">
|
||
<link rel="stylesheet" href="styles.css?v=97">
|
||
<!-- KaTeX: LaTeX 公式渲染 -->
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" crossorigin="anonymous">
|
||
<!-- highlight.js: 代码高亮 -->
|
||
<link id="hljs-theme" rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/github.min.css" crossorigin="anonymous">
|
||
<!-- Flatpickr: 日期选择器 -->
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.css" crossorigin="anonymous">
|
||
<script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.js" crossorigin="anonymous"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/l10n/zh.js" crossorigin="anonymous"></script>
|
||
<script>
|
||
(function(){
|
||
var t=localStorage.getItem('ga_theme'); if(t) document.documentElement.dataset.theme=t;
|
||
var l=localStorage.getItem('ga_lang'); if(l==='en') document.documentElement.lang='en';
|
||
var legacy=localStorage.getItem('ga_style');
|
||
var app=localStorage.getItem('ga_appearance');
|
||
if(!app&&legacy){
|
||
if(legacy==='dark') app='dark';
|
||
else{ app='light'; if(legacy==='classic') localStorage.setItem('ga_plain','1'); }
|
||
localStorage.setItem('ga_appearance',app);
|
||
localStorage.removeItem('ga_style');
|
||
}
|
||
app=app||'light';
|
||
document.documentElement.dataset.appearance=app;
|
||
if(app==='light'&&localStorage.getItem('ga_plain')==='1') document.documentElement.dataset.plain='1';
|
||
var LEGACY_FONT={sm:12,md:14,lg:16};
|
||
function chatFontPx(v){
|
||
if(v&&LEGACY_FONT[v]) return LEGACY_FONT[v];
|
||
var n=parseInt(v,10); if(!isNaN(n)&&n>=10&&n<=20) return n;
|
||
return 14;
|
||
}
|
||
var fs=localStorage.getItem('ga_font_size');
|
||
var cfp=chatFontPx(fs);
|
||
document.documentElement.dataset.chatFont=String(cfp);
|
||
document.documentElement.style.setProperty('--chat-font',cfp+'px');
|
||
var hljs=document.getElementById('hljs-theme');
|
||
if(hljs&&app==='dark') hljs.href='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/github-dark.min.css';
|
||
try{ var sraw=localStorage.getItem('ga_sessions'); if(sraw && JSON.parse(sraw).length) document.documentElement.dataset.bootHasSessions='1'; }catch(_){}
|
||
})();
|
||
</script>
|
||
</head>
|
||
<body>
|
||
|
||
<div id="app">
|
||
|
||
<div class="body">
|
||
|
||
<!-- ① 左侧边栏 -->
|
||
<aside class="sidebar">
|
||
<div class="brand">
|
||
<div class="brand-text">
|
||
<div class="brand-name ga-wordmark">Generic Agent</div>
|
||
<div class="brand-sub" data-i18n="brand.sub"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<nav class="nav" id="nav">
|
||
<a class="nav-item active" data-page="chat">
|
||
<span class="ic" data-ga-icon="chatTeardropText"></span>
|
||
<span data-i18n="nav.chat"></span>
|
||
</a>
|
||
<a class="nav-item" data-page="collab">
|
||
<span class="ic" aria-hidden="true"></span>
|
||
<span data-i18n="nav.collab"></span>
|
||
<span id="collab-badge" class="collab-nav-badge" hidden></span>
|
||
</a>
|
||
<a class="nav-item" data-page="services">
|
||
<span class="ic" data-ga-icon="broadcast"></span>
|
||
<span data-i18n="nav.services"></span>
|
||
</a>
|
||
<a class="nav-item" data-page="token">
|
||
<span class="ic" data-ga-icon="coins"></span>
|
||
<span data-i18n="nav.token"></span>
|
||
</a>
|
||
</nav>
|
||
|
||
<div class="sidebar-foot">
|
||
<a class="foot-row" id="settings-btn">
|
||
<span class="fr-l">
|
||
<span class="ic" data-ga-icon="gear"></span>
|
||
<span data-i18n="foot.settings"></span>
|
||
</span>
|
||
<span class="chev" data-ga-icon="caretRight"></span>
|
||
</a>
|
||
<div class="foot-ver" data-i18n="foot.ver"></div>
|
||
</div>
|
||
</aside>
|
||
<div class="sb-resize" id="sb-resize"></div>
|
||
|
||
<!-- 中间主区 -->
|
||
<main class="main">
|
||
|
||
<div id="pages">
|
||
|
||
<!-- 聊天 -->
|
||
<section class="page active" data-page="chat"
|
||
>
|
||
<div class="page-top">
|
||
<button type="button" class="pt-icon pt-sb-toggle" aria-label="toggle sidebar">
|
||
<span data-ga-icon="caretLeft"></span>
|
||
</button>
|
||
<button class="run-state" id="run-toggle">
|
||
<i class="dot"></i> <span class="rs-label"></span>
|
||
</button>
|
||
<button type="button" class="pt-icon pt-rp-toggle" aria-label="toggle conversations">
|
||
<span data-ga-icon="caretRight"></span>
|
||
</button>
|
||
</div>
|
||
<div class="msg-area">
|
||
<div class="chat-start">
|
||
<div class="feature-head">
|
||
<div class="page-title" data-i18n="chat.startTitle"></div>
|
||
<div class="page-sub" data-i18n="chat.startSub"></div>
|
||
</div>
|
||
<div class="feature-grid"></div>
|
||
</div>
|
||
<div class="msg-loading" id="msg-loading" hidden aria-live="polite">
|
||
<span class="ga-spin" aria-hidden="true"></span>
|
||
<span data-i18n="chat.interrupting"></span>
|
||
</div>
|
||
</div>
|
||
<div class="composer">
|
||
<div class="composer-top">
|
||
<button class="ic-btn" id="upload-btn" data-i18n-title="upload.button" title="">
|
||
<span data-ga-icon="paperclip"></span>
|
||
</button>
|
||
<button class="ic-btn" id="preset-btn">
|
||
<span data-ga-icon="lightning"></span>
|
||
</button>
|
||
</div>
|
||
<div class="input" contenteditable="true" role="textbox" aria-multiline="true" data-i18n-ph="composer.placeholder"></div>
|
||
<input type="file" id="img-input" multiple hidden>
|
||
<div class="thumb-strip" id="thumb-strip" hidden></div>
|
||
<div class="composer-bot">
|
||
<button class="chip" id="model-chip" data-i18n-title="model.menuLabel" title=""><span class="model-name"></span> <span class="chip-ic" data-ga-icon="caretDown"></span></button>
|
||
<button class="send" id="send-btn" type="button">
|
||
<span data-ga-icon="paperPlaneTilt"></span>
|
||
</button>
|
||
</div>
|
||
<div class="ga-menu" id="model-menu" hidden></div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 后台服务:消息通道 + 状态面板 -->
|
||
<section class="page" data-page="services">
|
||
<div class="page-top">
|
||
<button type="button" class="pt-icon pt-sb-toggle" aria-label="toggle sidebar">
|
||
<span data-ga-icon="caretLeft"></span>
|
||
</button>
|
||
<span class="pt-spacer"></span>
|
||
<button type="button" class="pt-icon pt-rp-toggle" aria-label="toggle conversations">
|
||
<span data-ga-icon="caretRight"></span>
|
||
</button>
|
||
</div>
|
||
<div class="page-pad">
|
||
<div class="page-title" data-i18n="page.services.title"></div>
|
||
<div class="page-sub" data-i18n="page.services.sub"></div>
|
||
<div class="svc-tabs" id="svc-tabs">
|
||
<button type="button" class="svc-tab active" data-tab="channels" data-i18n="page.channels.title"></button>
|
||
<button type="button" class="svc-tab" data-tab="status" data-i18n="page.status.title"></button>
|
||
</div>
|
||
<div class="svc-panel active" data-svc-panel="channels">
|
||
<div class="list" id="chan-list"></div>
|
||
<div class="list-empty" id="chan-empty" hidden data-i18n="ch.empty"></div>
|
||
</div>
|
||
<div class="svc-panel" data-svc-panel="status">
|
||
<div class="list" id="status-list"></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 指挥家 Conductor -->
|
||
<section class="page" data-page="collab">
|
||
<div class="collab-offline" id="collab-offline" hidden>
|
||
<span data-i18n="collab.offline"></span>
|
||
<button type="button" class="chip sm" id="collab-retry" data-i18n="collab.retry"></button>
|
||
</div>
|
||
<div class="collab-reconnect" id="collab-reconnect" hidden data-i18n="collab.reconnect"></div>
|
||
<div class="page-top">
|
||
<button type="button" class="pt-icon pt-sb-toggle" aria-label="toggle sidebar">
|
||
<span data-ga-icon="caretLeft"></span>
|
||
</button>
|
||
<button class="run-state" id="collab-run-toggle" type="button">
|
||
<i class="dot"></i> <span class="rs-label"></span>
|
||
</button>
|
||
<button type="button" class="pt-icon pt-rp-toggle" aria-label="toggle conversations">
|
||
<span data-ga-icon="caretRight"></span>
|
||
</button>
|
||
</div>
|
||
<div class="msg-area collab-msgs" id="collab-msgs">
|
||
<div class="collab-scroll">
|
||
<div class="collab-guide" id="collab-welcome" hidden>
|
||
<div class="feature-head">
|
||
<div class="page-title" data-i18n="collab.guideTitle"></div>
|
||
<p class="page-sub" data-i18n="collab.guideWhen"></p>
|
||
</div>
|
||
<div class="collab-guide-steps">
|
||
<div class="collab-guide-step">
|
||
<span class="collab-guide-ic" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg></span>
|
||
<span class="collab-guide-txt"><span class="collab-guide-label" data-i18n="collab.guideStep1t"></span><span class="collab-guide-desc" data-i18n="collab.guideStep1d"></span></span>
|
||
</div>
|
||
<div class="collab-guide-step">
|
||
<span class="collab-guide-ic" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg></span>
|
||
<span class="collab-guide-txt"><span class="collab-guide-label" data-i18n="collab.guideStep2t"></span><span class="collab-guide-desc" data-i18n="collab.guideStep2d"></span></span>
|
||
</div>
|
||
<div class="collab-guide-step">
|
||
<span class="collab-guide-ic" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><path d="M9 15l2 2 4-4"/></svg></span>
|
||
<span class="collab-guide-txt"><span class="collab-guide-label" data-i18n="collab.guideStep3t"></span><span class="collab-guide-desc" data-i18n="collab.guideStep3d"></span></span>
|
||
</div>
|
||
<div class="collab-guide-step">
|
||
<span class="collab-guide-ic" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg></span>
|
||
<span class="collab-guide-txt"><span class="collab-guide-label" data-i18n="collab.guideStep4t"></span><span class="collab-guide-desc" data-i18n="collab.guideStep4d"></span></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="msgs" id="collab-msg-list" hidden></div>
|
||
</div>
|
||
<div class="collab-overlay">
|
||
<div class="collab-rail" id="collab-rail" hidden>
|
||
<button type="button" class="collab-rail-handle" id="collab-rail-toggle" data-i18n-title="collab.showProgressTitle" title="">
|
||
<span class="collab-rail-grip" aria-hidden="true"></span>
|
||
<svg class="collab-rail-chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="9 18 15 12 9 6"/></svg>
|
||
<span class="collab-rail-badge collab-rail-run" id="collab-rail-run" hidden>
|
||
<span class="collab-rail-spin" aria-hidden="true"></span>
|
||
<span class="n" id="collab-rail-run-n">0</span>
|
||
</span>
|
||
<span class="collab-rail-badge collab-rail-done" id="collab-rail-done" hidden>
|
||
<span class="collab-rail-dot" aria-hidden="true"></span>
|
||
<span class="n" id="collab-rail-done-n">0</span>
|
||
</span>
|
||
</button>
|
||
</div>
|
||
<aside class="collab-prog-panel" id="collab-prog-panel">
|
||
<div class="collab-prog-panel-head">
|
||
<span data-i18n="collab.progressTitle"></span>
|
||
<button type="button" class="ic-btn collab-prog-close" id="collab-prog-close" aria-label="close">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
||
</button>
|
||
</div>
|
||
<div class="collab-prog-stats" id="collab-progress-stats" hidden></div>
|
||
<div class="collab-progress-empty" id="collab-progress-empty" data-i18n="collab.progressEmpty"></div>
|
||
<div id="collab-workers"></div>
|
||
</aside>
|
||
</div>
|
||
</div>
|
||
<div class="composer cdb-composer" id="cdb-composer">
|
||
<div class="collab-quick" id="collab-quick">
|
||
<button type="button" class="pill" data-prompt-key="collab.chipProgress" data-i18n="collab.chipProgress"></button>
|
||
<button type="button" class="pill" data-prompt-key="collab.chipPause" data-i18n="collab.chipPause"></button>
|
||
<button type="button" class="pill" data-prompt-key="collab.chipSummary" data-i18n="collab.chipSummary"></button>
|
||
</div>
|
||
<div class="cdb-inset">
|
||
<div class="thumb-strip" id="cdb-thumb-strip" hidden></div>
|
||
<div class="input" id="cdb-input" contenteditable="true" role="textbox" aria-multiline="true" data-i18n-ph="collab.placeholder"></div>
|
||
<div class="composer-bot">
|
||
<div class="cdb-plus-wrap">
|
||
<button type="button" class="ic-btn cdb-plus" id="cdb-plus-btn" aria-haspopup="menu" aria-expanded="false" data-i18n-title="collab.plusMenu" title="">
|
||
<span data-ga-icon="plus"></span>
|
||
</button>
|
||
<div class="ga-menu cdb-menu" id="cdb-menu" hidden role="menu">
|
||
<button type="button" class="ga-menu-item" id="cdb-menu-upload" role="menuitem">
|
||
<span data-ga-icon="paperclip"></span>
|
||
<span data-i18n="upload.button"></span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<span class="spacer"></span>
|
||
<button type="button" class="chip" id="cdb-model-chip" data-i18n-title="model.menuLabel" title="">
|
||
<span class="model-name"></span>
|
||
<span class="chip-ic" data-ga-icon="caretDown"></span>
|
||
</button>
|
||
<button type="button" class="send" id="cdb-send" aria-label="send">
|
||
<span data-ga-icon="paperPlaneTilt"></span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="ga-menu" id="cdb-model-menu" hidden></div>
|
||
<input type="file" id="cdb-file-input" multiple hidden>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Token 统计 -->
|
||
<section class="page" data-page="token">
|
||
<div class="page-top">
|
||
<button type="button" class="pt-icon pt-sb-toggle" aria-label="toggle sidebar">
|
||
<span data-ga-icon="caretLeft"></span>
|
||
</button>
|
||
<span class="pt-spacer"></span>
|
||
<button type="button" class="pt-icon pt-rp-toggle" aria-label="toggle conversations">
|
||
<span data-ga-icon="caretRight"></span>
|
||
</button>
|
||
</div>
|
||
<div class="page-pad">
|
||
<div class="page-title" data-i18n="page.token.title"></div>
|
||
<div class="tok-tabs" id="tok-tabs">
|
||
<button class="tok-tab active" data-tab="chat" data-i18n="tok.tabAll"></button>
|
||
<button class="tok-tab" data-tab="conductor" data-i18n="tok.tabConductor"></button>
|
||
</div>
|
||
<div class="tok-filter">
|
||
<label data-i18n="tok.from"></label>
|
||
<input type="text" id="tok-since" class="tok-date" readonly>
|
||
<label data-i18n="tok.to"></label>
|
||
<input type="text" id="tok-until" class="tok-date" readonly>
|
||
<button class="tok-reset" id="tok-reset" data-i18n="tok.reset"></button>
|
||
</div>
|
||
<div class="stat-row">
|
||
<div class="stat"><div class="s-n" id="tok-total-n">0</div><div class="s-l" data-i18n="tok.total"></div></div>
|
||
<div class="stat"><div class="s-n" id="tok-cost-n">¥ 0</div><div class="s-l" data-i18n="tok.cost"></div></div>
|
||
<div class="stat"><div class="s-n" id="tok-today-n">0</div><div class="s-l" data-i18n="tok.today"></div></div>
|
||
</div>
|
||
<div class="tok-chart-wrap" id="tok-chart-wrap">
|
||
<button class="tok-chart-toggle" id="tok-chart-toggle" type="button" data-i18n="tok.chartToggle"></button>
|
||
<div class="tok-chart" id="tok-chart" hidden></div>
|
||
</div>
|
||
<table class="tok-table" id="tok-table">
|
||
<thead><tr>
|
||
<th data-i18n="tok.colSession"></th>
|
||
<th data-i18n="tok.colIn"></th>
|
||
<th data-i18n="tok.colOut"></th>
|
||
<th data-i18n="tok.colCacheW"></th>
|
||
<th data-i18n="tok.colCache"></th>
|
||
<th data-i18n="tok.cost"></th>
|
||
</tr></thead>
|
||
<tbody id="tok-tbody"></tbody>
|
||
</table>
|
||
<div class="tok-pager" id="tok-pager"></div>
|
||
<p style="color:var(--muted);font-size:12px;margin-top:12px;text-align:center" data-i18n="tok.disclaimer"></p>
|
||
</div>
|
||
</section>
|
||
|
||
</div>
|
||
</main>
|
||
|
||
<!-- ④ 右侧会话面板 -->
|
||
<div class="rp-resize" id="rp-resize"></div>
|
||
<aside class="rightpanel" id="rightpanel">
|
||
<div class="search">
|
||
<span class="search-ic" data-ga-icon="magnifyingGlass"></span>
|
||
<input data-i18n-ph="search.placeholder" placeholder="" maxlength="50">
|
||
</div>
|
||
<button class="new-conv">
|
||
<span data-ga-icon="plus"></span>
|
||
<span data-i18n="conv.new"></span>
|
||
</button>
|
||
<div class="conv-list"></div>
|
||
|
||
<!-- 会话操作菜单 -->
|
||
<div class="ctx-menu" id="conv-menu" hidden>
|
||
<div class="ctx-item" data-act="pin">
|
||
<span data-ga-icon="pushPinSimple"></span>
|
||
<span data-i18n="ctx.pin"></span>
|
||
</div>
|
||
<div class="ctx-item" data-act="rename">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg>
|
||
<span data-i18n="ctx.rename"></span>
|
||
</div>
|
||
<div class="ctx-item danger" data-act="del">
|
||
<span data-ga-icon="trash"></span>
|
||
<span data-i18n="ctx.del"></span>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
</div>
|
||
|
||
<!-- 预设功能 弹窗 -->
|
||
<div class="modal" id="preset-modal" hidden>
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-card" role="dialog">
|
||
<div class="modal-head">
|
||
<div class="modal-title" data-i18n="modal.preset"></div>
|
||
<button id="preset-restore-btn" class="preset-restore" type="button" data-i18n="builtinPreset.restoreBtn" hidden></button>
|
||
<button class="modal-x" data-close data-i18n-title="common.close" title="">
|
||
<span data-ga-icon="x"></span>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body preset-pop">
|
||
<div class="feature-grid"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 添加模型 弹窗(模板,未实现)-->
|
||
<div class="modal" id="add-model-modal" hidden>
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-card" role="dialog">
|
||
<div class="modal-head">
|
||
<div class="modal-title" id="model-form-title" data-i18n="modal.addModel"></div>
|
||
<button class="modal-x" data-close data-i18n-title="common.close" title="">
|
||
<span data-ga-icon="x"></span>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body model-form-body">
|
||
<form id="add-model-form" class="model-form" autocomplete="off">
|
||
<label><span class="field-label"><span data-i18n="model.model"></span><span class="field-req" aria-hidden="true">*</span></span><input name="model" required data-i18n-ph="model.modelPh" maxlength="50"><span class="field-hint" data-i18n="model.modelHint"></span></label>
|
||
<label id="model-apikey-label"><span class="field-label"><span data-i18n="model.apikey"></span><span class="field-req" aria-hidden="true">*</span></span><input name="apikey" type="password" data-i18n-ph="model.apikeyPh" id="model-apikey-input" maxlength="200"></label>
|
||
<label><span class="field-label"><span data-i18n="model.apibase"></span><span class="field-req" aria-hidden="true">*</span></span><input name="apibase" required data-i18n-ph="model.apibasePh" maxlength="200"></label>
|
||
<div class="model-form-field"><span class="field-label"><span data-i18n="model.protocol"></span><span class="field-req" aria-hidden="true">*</span></span>
|
||
<div class="seg-group" role="radiogroup">
|
||
<label class="seg-opt"><input type="radio" name="protocol" value="oai" required><span data-i18n="model.protocolOai"></span></label>
|
||
<label class="seg-opt"><input type="radio" name="protocol" value="claude" required><span data-i18n="model.protocolClaude"></span></label>
|
||
</div>
|
||
</div>
|
||
<label><span data-i18n="model.name"></span><input name="name" data-i18n-ph="model.namePh" data-optional-ph maxlength="50"></label>
|
||
<div class="model-form-row">
|
||
<label class="model-form-mini"><span data-i18n="model.retries"></span><input name="max_retries" type="text" inputmode="numeric" pattern="[0-9]*" min="1" max="20" value="5"></label>
|
||
<label class="model-form-mini"><span data-i18n="model.connTimeout"></span><input name="connect_timeout" type="text" inputmode="numeric" pattern="[0-9]*" min="1" max="300" value="15"></label>
|
||
<label class="model-form-mini"><span data-i18n="model.readTimeout"></span><input name="read_timeout" type="text" inputmode="numeric" pattern="[0-9]*" min="5" max="3600" value="300"></label>
|
||
</div>
|
||
<p class="model-form-err" id="add-model-err" hidden></p>
|
||
<div class="model-form-actions">
|
||
<button type="button" class="set-btn ghost" data-close data-i18n="common.cancel"></button>
|
||
<button type="submit" class="set-btn primary"><span data-i18n="model.save"></span></button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 自定义预设 弹窗 -->
|
||
<div class="modal" id="custom-preset-modal" hidden>
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-card" role="dialog">
|
||
<div class="modal-head">
|
||
<div class="modal-title" data-i18n="modal.customPreset"></div>
|
||
<button class="modal-x" data-close data-i18n-title="common.close" title="">
|
||
<span data-ga-icon="x"></span>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body cp-body">
|
||
<input class="cp-input" id="cp-title" type="text" data-i18n-ph="customPreset.titlePh" placeholder="" maxlength="50">
|
||
<textarea class="cp-textarea" id="cp-prompt" rows="6" data-i18n-ph="customPreset.promptPh" placeholder="" maxlength="10000"></textarea>
|
||
<div class="cp-error" id="cp-error" hidden></div>
|
||
<div class="cp-actions">
|
||
<button class="cp-btn" data-close data-i18n="common.cancel" type="button"></button>
|
||
<button class="cp-btn cp-btn-primary" id="cp-save" data-i18n="common.save" type="button"></button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 渠道日志 弹窗 -->
|
||
<div class="modal" id="chan-log-modal" hidden>
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-card modal-card-wide modal-card-inset" role="dialog">
|
||
<div class="modal-head">
|
||
<div class="modal-title" id="chan-log-title" data-i18n="modal.channelLogs"></div>
|
||
<button class="modal-x" data-close data-i18n-title="common.close" title="">
|
||
<span data-ga-icon="x"></span>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body modal-body-inset">
|
||
<pre class="chan-log-pre" id="chan-log-pre"></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- mykey 配置 弹窗 -->
|
||
<div class="modal" id="chan-config-modal" hidden>
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-card modal-card-wide modal-card-inset" role="dialog">
|
||
<div class="modal-head">
|
||
<div class="modal-title" id="chan-config-title" data-i18n="modal.mykeyConfig"></div>
|
||
<button class="modal-x" data-close data-i18n-title="common.close" title="">
|
||
<span data-ga-icon="x"></span>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body modal-body-inset chan-config-body">
|
||
<textarea id="chan-config-editor" class="mykey-editor" spellcheck="false" maxlength="20000"></textarea>
|
||
<div class="chan-config-foot">
|
||
<button type="button" class="chan-config-save" id="chan-config-save" data-i18n="common.save"></button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 配置 弹窗 -->
|
||
<div class="modal" id="settings-modal" hidden>
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-card" role="dialog">
|
||
<div class="modal-head">
|
||
<div class="modal-title" data-i18n="modal.settings"></div>
|
||
<button class="modal-x" data-close data-i18n-title="common.close" title="">
|
||
<span data-ga-icon="x"></span>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body set-body">
|
||
<div class="set-block">
|
||
<div class="set-sec-t" data-i18n="set.appearance"></div>
|
||
<div class="appear-cards" id="appearance-seg" role="radiogroup">
|
||
<button class="appear-card sel" type="button" data-appearance="light" role="radio" aria-checked="true">
|
||
<span class="appear-preview appear-preview-light" aria-hidden="true"></span>
|
||
<span class="appear-name" data-i18n="appearance.light"></span>
|
||
</button>
|
||
<button class="appear-card" type="button" data-appearance="dark" role="radio" aria-checked="false">
|
||
<span class="appear-preview appear-preview-dark" aria-hidden="true"></span>
|
||
<span class="appear-name" data-i18n="appearance.dark"></span>
|
||
</button>
|
||
</div>
|
||
<div class="set-toggle-row" id="plain-ui-row" hidden>
|
||
<span class="set-toggle-label" data-i18n="set.plainUi"></span>
|
||
<button class="set-switch" type="button" id="plain-ui-switch" role="switch" aria-checked="false">
|
||
<span class="set-switch-knob" aria-hidden="true"></span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="set-block">
|
||
<div class="set-sec-t" id="chat-font-label" data-i18n="set.fontSize"></div>
|
||
<div class="chat-font-panel">
|
||
<div class="chat-font-stepper" id="chat-font-stepper" role="slider"
|
||
aria-valuemin="10" aria-valuemax="20" aria-valuenow="14"
|
||
aria-valuetext="14px" aria-labelledby="chat-font-label" tabindex="0">
|
||
<div class="chat-font-segments" id="chat-font-segments"></div>
|
||
</div>
|
||
<span class="chat-font-value" id="chat-font-value" aria-hidden="true">14px</span>
|
||
<div class="chat-font-scale" aria-hidden="true">
|
||
<span class="chat-font-scale-min">10</span>
|
||
<span class="chat-font-scale-max">20</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="set-block">
|
||
<div class="set-sec-t" data-i18n="set.theme"></div>
|
||
<div class="color-grid" id="theme-swatches">
|
||
<span class="swatch sel" data-theme="1" role="button" tabindex="0" aria-label="1"></span>
|
||
<span class="swatch" data-theme="2" role="button" tabindex="0" aria-label="2"></span>
|
||
<span class="swatch" data-theme="3" role="button" tabindex="0" aria-label="3"></span>
|
||
<span class="swatch" data-theme="4" role="button" tabindex="0" aria-label="4"></span>
|
||
<span class="swatch" data-theme="5" role="button" tabindex="0" aria-label="5"></span>
|
||
<span class="swatch" data-theme="6" role="button" tabindex="0" aria-label="6"></span>
|
||
<span class="swatch" data-theme="7" role="button" tabindex="0" aria-label="7"></span>
|
||
<span class="swatch" data-theme="8" role="button" tabindex="0" aria-label="8"></span>
|
||
</div>
|
||
</div>
|
||
<div class="set-block">
|
||
<div class="set-sec-t" data-i18n="set.lang"></div>
|
||
<div class="model-list lang-list" id="lang-list"></div>
|
||
</div>
|
||
<div>
|
||
<div class="set-sec-t" data-i18n="set.model"></div>
|
||
<div class="model-list" id="model-list"></div>
|
||
<button class="set-btn" id="add-model-btn" type="button">
|
||
<span data-ga-icon="plus"></span>
|
||
<span data-i18n="set.addModel"></span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 图片预览 lightbox -->
|
||
<div class="lightbox" id="lightbox" hidden>
|
||
<div class="lightbox-backdrop" data-close></div>
|
||
<button class="lightbox-x" data-close data-i18n-title="lightbox.closeTitle" title="">×</button>
|
||
<img class="lightbox-img" id="lightbox-img" alt="">
|
||
</div>
|
||
|
||
<div id="toast-root" class="toast-root" aria-live="polite"></div>
|
||
|
||
<script src="vendor/marked.min.js"></script>
|
||
<!-- KaTeX JS -->
|
||
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" crossorigin="anonymous"></script>
|
||
<!-- highlight.js -->
|
||
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js" crossorigin="anonymous"></script>
|
||
<script src="phosphor-icons.js"></script>
|
||
<script src="app.js?v=88"></script>
|
||
</body>
|
||
</html>
|