Files
cdp_hub/docs/ui/browser-manager.html
T

467 lines
65 KiB
HTML
Raw Normal View History

2026-07-22 14:51:11 +08:00
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Chub 繚 瘚讛��典�靘讠恣�����</title>
<style>
:root {
color-scheme: light;
--bg: #f5f7fb;
--surface: #ffffff;
--surface-2: #f8f9fc;
--surface-3: #eef2f8;
--text: #18202b;
--muted: #617083;
--line: #dce2eb;
--line-strong: #c7d0dd;
--accent: #2563eb;
--accent-strong: #1d4ed8;
--accent-soft: #e7efff;
--success: #087443;
--success-soft: #e4f6ed;
--warning: #9a5b00;
--warning-soft: #fff3d8;
--danger: #b42318;
--danger-soft: #ffebe8;
--shadow: 0 18px 48px rgba(26, 43, 73, .14), 0 2px 7px rgba(26, 43, 73, .08);
--radius-control: 8px;
--radius-card: 14px;
--radius-dialog: 16px;
--focus: 0 0 0 3px rgba(37, 99, 235, .25);
}
[data-theme="dark"] {
color-scheme: dark;
--bg: #11151c;
--surface: #1b222d;
--surface-2: #202936;
--surface-3: #293544;
--text: #f1f5fb;
--muted: #aab7c8;
--line: #364354;
--line-strong: #4a5a6c;
--accent: #83aaff;
--accent-strong: #a9c2ff;
--accent-soft: #263b66;
--success: #65d49a;
--success-soft: #173c2b;
--warning: #f6c15c;
--warning-soft: #4a3612;
--danger: #ff9c91;
--danger-soft: #4b2421;
--shadow: 0 18px 48px rgba(0, 0, 0, .35);
}
* { box-sizing: border-box; }
html, body { margin: 0; min-height: 100%; font-family: "Segoe UI Variable", "Segoe UI", system-ui, sans-serif; color: var(--text); background: var(--bg); }
body { font-size: 14px; }
button, input, select { font: inherit; }
button { border: 0; cursor: pointer; }
button:focus-visible, input:focus-visible, select:focus-visible { outline: none; box-shadow: var(--focus); }
.prototype-ribbon { position: fixed; z-index: 20; top: 0; left: 50%; transform: translateX(-50%); background: #173b72; color: #fff; padding: 5px 14px; border-radius: 0 0 8px 8px; font-size: 11px; letter-spacing: .04em; }
.app { display: grid; grid-template-columns: 232px minmax(0, 1fr); min-height: 100vh; }
.sidebar { background: var(--surface); border-right: 1px solid var(--line); padding: 34px 14px 18px; display: flex; flex-direction: column; gap: 24px; }
.brand { display: flex; align-items: center; gap: 10px; padding: 0 10px; }
.brand-mark { width: 32px; height: 32px; border-radius: 10px; background: linear-gradient(135deg, #2563eb, #66a1ff); color: #fff; display: grid; place-items: center; font-weight: 800; letter-spacing: -.04em; }
.brand strong { display: block; font-size: 17px; letter-spacing: -.02em; }
.brand span { color: var(--muted); font-size: 11px; }
.nav { display: grid; gap: 4px; }
.nav button { min-height: 40px; padding: 0 12px; text-align: left; color: var(--muted); background: transparent; border-radius: var(--radius-control); display: flex; align-items: center; gap: 11px; }
.nav button:hover { background: var(--surface-2); color: var(--text); }
.nav button[aria-current="page"] { background: var(--accent-soft); color: var(--accent-strong); font-weight: 650; }
.nav-icon { width: 18px; text-align: center; font-size: 16px; }
.sidebar-footer { margin-top: auto; border-top: 1px solid var(--line); padding: 16px 10px 0; color: var(--muted); font-size: 12px; line-height: 1.5; }
.main { min-width: 0; padding: 34px clamp(18px, 3vw, 42px) 38px; }
.topbar { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; max-width: 1420px; margin: 0 auto 24px; }
.eyebrow { color: var(--accent-strong); font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
h1 { margin: 5px 0 6px; font-size: clamp(24px, 3vw, 32px); letter-spacing: -.035em; }
.subtitle { margin: 0; color: var(--muted); }
2026-07-25 16:21:38 +08:00
.top-actions { display: flex; gap: 8px; align-items: center; width: min(468px, 50vw); }
.instance-commands { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; flex: 1; }
.instance-commands button { width: 100%; }
2026-07-22 14:51:11 +08:00
.icon-button, .secondary, .primary, .danger-button { min-height: 36px; padding: 0 14px; border-radius: var(--radius-control); font-weight: 650; white-space: nowrap; }
.icon-button, .secondary { color: var(--text); background: var(--surface); border: 1px solid var(--line-strong); }
.icon-button { width: 38px; padding: 0; }
.secondary:hover, .icon-button:hover { background: var(--surface-2); }
.primary { color: #fff; background: var(--accent); }
.primary:hover { background: var(--accent-strong); }
.danger-button { color: #fff; background: var(--danger); }
.danger-button:hover { filter: brightness(.94); }
.content { max-width: 1420px; margin: auto; display: grid; gap: 18px; }
.banner { display: flex; gap: 12px; align-items: flex-start; background: var(--warning-soft); border: 1px solid color-mix(in srgb, var(--warning) 30%, transparent); border-radius: var(--radius-card); padding: 13px 16px; color: var(--text); }
.banner b { color: var(--warning); }
.banner p { margin: 3px 0 0; color: var(--muted); font-size: 13px; }
.summary-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
.summary { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-card); padding: 16px; }
.summary-label { color: var(--muted); font-size: 12px; }
.summary-value { margin-top: 8px; display: flex; align-items: baseline; gap: 8px; font-size: 25px; font-weight: 750; letter-spacing: -.03em; }
.summary-note { color: var(--muted); font-size: 12px; }
.workspace { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-card); overflow: hidden; }
.toolbar { padding: 14px 16px; border-bottom: 1px solid var(--line); display: flex; flex-wrap: wrap; align-items: center; gap: 9px; }
.search { min-width: 220px; flex: 1; position: relative; }
.search input { width: 100%; height: 36px; border: 1px solid var(--line-strong); border-radius: var(--radius-control); background: var(--surface-2); padding: 0 12px 0 35px; color: var(--text); }
.search span { position: absolute; left: 12px; top: 8px; color: var(--muted); }
select { height: 36px; color: var(--text); background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius-control); padding: 0 30px 0 11px; }
.toolbar-spacer { flex: 1; }
.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; min-width: 760px; }
th { height: 42px; padding: 0 16px; text-align: left; color: var(--muted); font-size: 12px; font-weight: 650; background: var(--surface-2); border-bottom: 1px solid var(--line); }
td { padding: 14px 16px; border-bottom: 1px solid var(--line); vertical-align: middle; }
tbody tr { cursor: pointer; }
tbody tr:hover { background: var(--surface-2); }
tbody tr.selected { background: var(--accent-soft); }
.instance-name { display: flex; align-items: center; gap: 10px; min-width: 190px; }
.browser-logo { width: 28px; height: 28px; display: grid; place-items: center; border-radius: 8px; color: #fff; font-size: 12px; font-weight: 800; }
.chrome { background: conic-gradient(#e84c3d 0 33%, #f5c746 0 66%, #36a269 0); }
.edge { background: linear-gradient(140deg, #0c9b9e, #1768d2); }
.instance-name strong { display: block; font-size: 14px; }
.instance-name small, .mono { color: var(--muted); font-size: 12px; font-family: "Cascadia Mono", Consolas, monospace; }
.status { display: inline-flex; align-items: center; gap: 7px; border-radius: 999px; padding: 5px 9px; font-size: 12px; font-weight: 700; white-space: nowrap; }
.status::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.status.running { color: var(--success); background: var(--success-soft); }
.status.starting { color: var(--accent-strong); background: var(--accent-soft); }
2026-07-25 15:36:23 +08:00
.status.associated { color: var(--warning); background: var(--warning-soft); }
2026-07-22 14:51:11 +08:00
.status.occupied { color: var(--warning); background: var(--warning-soft); }
.status.exited { color: var(--muted); background: var(--surface-3); }
2026-07-25 17:47:02 +08:00
.row-actions { display: flex; justify-content: flex-end; gap: 10px; opacity: .7; }
2026-07-22 14:51:11 +08:00
tr:hover .row-actions, tr.selected .row-actions { opacity: 1; }
2026-07-25 17:47:02 +08:00
.row-actions button { background: transparent; color: var(--muted); padding: 8px 10px; border-radius: 6px; }
2026-07-22 14:51:11 +08:00
.row-actions button:hover { color: var(--accent-strong); background: var(--accent-soft); }
.table-footer { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; color: var(--muted); font-size: 12px; }
.drawer-backdrop { position: fixed; inset: 0; background: rgba(16, 24, 40, .28); z-index: 10; opacity: 0; pointer-events: none; transition: opacity .16s ease; }
.drawer-backdrop.open { opacity: 1; pointer-events: auto; }
.drawer { position: fixed; z-index: 11; top: 0; right: 0; bottom: 0; width: min(480px, 100vw); background: var(--surface); box-shadow: var(--shadow); transform: translateX(100%); transition: transform .2s ease; overflow: auto; }
.drawer.open { transform: translateX(0); }
.drawer-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 28px 26px 20px; border-bottom: 1px solid var(--line); }
.drawer-head h2 { margin: 0 0 5px; font-size: 21px; letter-spacing: -.02em; }
.drawer-head p { margin: 0; color: var(--muted); font-size: 13px; }
.drawer-body { padding: 22px 26px; display: grid; gap: 22px; }
2026-07-27 09:40:37 +08:00
.drawer-body.instance-form-workspace { margin: 14px; padding: 14px; gap: 14px; border: 1px solid var(--line-strong); border-radius: calc(var(--radius-card) + 2px); background: var(--surface-2); }
.instance-form-workspace .form-section { padding: 13px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface); }
.instance-form-workspace .field { padding: 10px; border: 1px solid var(--line); border-radius: 9px; background: var(--surface-2); }
.instance-form-workspace .field:focus-within { border-color: var(--accent); box-shadow: var(--focus); }
.instance-form-workspace .drawer-actions { padding: 12px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface); }
2026-07-22 14:51:11 +08:00
.form-section { display: grid; gap: 13px; }
.form-section h3 { margin: 0; font-size: 13px; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; }
.field { display: grid; gap: 6px; }
.field label { font-weight: 650; }
.field label span { color: var(--danger); }
.field input, .field select { width: 100%; height: 38px; border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--text); border-radius: var(--radius-control); padding: 0 11px; }
.field small, .help { color: var(--muted); font-size: 12px; line-height: 1.45; }
.field.error input { border-color: var(--danger); box-shadow: 0 0 0 2px color-mix(in srgb, var(--danger) 18%, transparent); }
.field-error { color: var(--danger); font-size: 12px; }
.check-line { display: flex; gap: 9px; align-items: flex-start; color: var(--text); }
.check-line input { width: 16px; height: 16px; accent-color: var(--accent); margin-top: 2px; }
.drawer-actions { display: flex; justify-content: flex-end; gap: 8px; border-top: 1px solid var(--line); padding-top: 18px; }
.detail-card { padding: 16px; background: var(--surface-2); border: 1px solid var(--line); border-radius: 12px; display: grid; gap: 12px; }
.detail-line { display: flex; justify-content: space-between; gap: 18px; align-items: baseline; }
.detail-line span:first-child { color: var(--muted); }
.detail-line span:last-child { text-align: right; max-width: 68%; overflow-wrap: anywhere; }
.activity { display: grid; gap: 9px; }
.activity-item { display: grid; grid-template-columns: 9px 1fr; gap: 9px; font-size: 12px; color: var(--muted); }
.activity-item i { width: 8px; height: 8px; background: var(--accent); border-radius: 50%; margin-top: 4px; }
dialog { border: 0; border-radius: var(--radius-dialog); padding: 0; width: min(430px, calc(100vw - 32px)); color: var(--text); background: var(--surface); box-shadow: var(--shadow); }
2026-07-27 10:01:07 +08:00
dialog.edit-dialog, dialog.tabs-dialog { width: min(620px, calc(100vw - 32px)); padding: 10px; border: 1px solid var(--line-strong); background: var(--surface-2); }
dialog.tabs-dialog { width: min(720px, calc(100vw - 32px)); }
2026-07-22 14:51:11 +08:00
dialog::backdrop { background: rgba(16, 24, 40, .42); }
.dialog-inner { padding: 24px; display: grid; gap: 17px; }
.dialog-inner h2 { margin: 0; font-size: 19px; }
.dialog-inner p { margin: 0; color: var(--muted); line-height: 1.55; }
.dialog-warning { padding: 11px 12px; border-radius: 9px; background: var(--danger-soft); color: var(--danger); font-size: 12px; line-height: 1.45; }
.dialog-list { margin: -3px 0 0; padding-left: 20px; color: var(--text); display: grid; gap: 5px; }
.dialog-list li { line-height: 1.45; }
2026-07-22 14:51:11 +08:00
.dialog-actions { display: flex; justify-content: flex-end; gap: 8px; }
2026-07-27 09:40:37 +08:00
.edit-form { display: grid; gap: 12px; border: 1px solid var(--line); border-radius: var(--radius-card); background: var(--surface); }
.edit-form > .field { padding: 10px; border: 1px solid var(--line); border-radius: 9px; background: var(--surface-2); }
.edit-form > .field:focus-within { border-color: var(--accent); box-shadow: var(--focus); }
.edit-form .form-note { border: 1px solid color-mix(in srgb, var(--warning) 35%, var(--line)); }
.edit-form .dialog-actions { padding: 12px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface-2); }
2026-07-25 16:21:38 +08:00
.edit-form .path-field { grid-template-columns: minmax(0, 1fr) auto; }
2026-07-27 10:01:07 +08:00
.tabs-meta { padding: 10px 12px; border: 1px solid var(--line); border-radius: 9px; background: var(--surface-2); color: var(--muted); font-size: 12px; line-height: 1.5; }
.tabs-state { min-height: 42px; padding: 11px 12px; border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--line)); border-radius: 9px; background: var(--accent-soft); color: var(--accent-strong); font-size: 12px; line-height: 1.45; }
.tabs-target-list { max-height: min(300px, 42vh); overflow: auto; display: grid; gap: 7px; }
.tabs-target { padding: 10px 12px; border: 1px solid var(--line); border-radius: 9px; background: var(--surface-2); display: grid; gap: 3px; }
.tabs-target small { color: var(--muted); }
.tabs-target strong, .tabs-target code { overflow-wrap: anywhere; }
2026-07-25 16:21:38 +08:00
.readonly-value { background: var(--surface-3) !important; color: var(--muted) !important; cursor: default; }
.form-note { padding: 10px 12px; border-radius: 9px; background: var(--warning-soft); color: var(--warning); font-size: 12px; line-height: 1.5; }
2026-07-22 14:51:11 +08:00
.empty-state { display: none; text-align: center; padding: 48px 20px; color: var(--muted); }
.empty-state strong { display: block; color: var(--text); font-size: 16px; margin-bottom: 7px; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
.toast { position: fixed; right: 22px; bottom: 22px; z-index: 30; max-width: min(420px, calc(100vw - 44px)); padding: 12px 15px; border: 1px solid var(--line); border-left: 4px solid var(--accent); border-radius: 10px; background: var(--surface); box-shadow: var(--shadow); color: var(--text); opacity: 0; transform: translateY(8px); pointer-events: none; transition: opacity .16s ease, transform .16s ease; }
.toast.show { opacity: 1; transform: translateY(0); }
.page-hidden { display: none !important; }
2026-07-26 17:42:40 +08:00
.settings-workspace { padding: 16px; border: 1px solid var(--line); border-radius: calc(var(--radius-card) + 2px); background: var(--surface-2); }
.settings-grid { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(260px, .8fr); gap: 16px; }
.settings-card { background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius-card); padding: 20px; display: grid; gap: 16px; }
2026-07-22 14:51:11 +08:00
.settings-card h2 { margin: 0; font-size: 18px; letter-spacing: -.02em; }
2026-07-26 17:42:40 +08:00
.settings-card > p { margin: -10px 0 0; color: var(--muted); line-height: 1.5; }
.settings-card .form-section, .settings-card .setting-list { padding: 14px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface-2); }
.settings-card .form-section { gap: 12px; }
.settings-card .field { padding: 12px; border: 1px solid var(--line); border-radius: 9px; background: var(--surface); }
.settings-card .field:focus-within { border-color: var(--accent); box-shadow: var(--focus); }
.settings-card .check-line { padding: 12px; border: 1px solid var(--line); border-radius: 9px; background: var(--surface); }
2026-07-22 15:57:02 +08:00
.path-field { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: 8px; }
.radio-row { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; min-height: 38px; }
.radio-row label { font-weight: 500; display: inline-flex; gap: 7px; align-items: center; }
2026-07-26 17:42:40 +08:00
.settings-status { min-height: 42px; padding: 11px 12px; border: 1px solid color-mix(in srgb, var(--success) 35%, var(--line)); border-radius: 9px; background: var(--success-soft); color: var(--success); font-size: 12px; line-height: 1.45; }
2026-07-22 14:51:11 +08:00
.settings-status.warning { background: var(--warning-soft); color: var(--warning); }
2026-07-26 17:42:40 +08:00
.settings-actions { display: flex; justify-content: flex-end; gap: 8px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface-2); padding: 12px; }
2026-07-22 14:51:11 +08:00
.setting-list { display: grid; gap: 12px; }
.setting-list .detail-line { padding-bottom: 12px; border-bottom: 1px solid var(--line); }
.setting-list .detail-line:last-child { border-bottom: 0; padding-bottom: 0; }
@media (max-width: 800px) { .settings-grid { grid-template-columns: 1fr; } }
@media (max-width: 1007px) {
.app { grid-template-columns: 70px minmax(0, 1fr); }
.sidebar { padding-inline: 10px; }
.brand { justify-content: center; padding: 0; }
.brand > div:last-child, .nav button span:not(.nav-icon), .sidebar-footer { display: none; }
.nav button { justify-content: center; padding: 0; }
.summary-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
.app { display: block; }
.sidebar { height: 58px; padding: 10px 14px; border-right: 0; border-bottom: 1px solid var(--line); flex-direction: row; align-items: center; }
.brand { margin-right: auto; }
.brand > div:last-child { display: block; }
.nav { display: flex; }
.nav button { width: 38px; }
.nav button span:not(.nav-icon) { display: none; }
.main { padding: 28px 12px; }
.topbar { display: block; }
2026-07-25 16:21:38 +08:00
.top-actions { margin-top: 16px; width: 100%; }
.instance-commands { grid-template-columns: 1fr; }
2026-07-22 14:51:11 +08:00
.summary-grid { gap: 8px; }
.summary { padding: 12px; }
.summary-value { font-size: 21px; }
.toolbar { align-items: stretch; }
.search { min-width: 100%; }
.toolbar-spacer { display: none; }
.toolbar select { flex: 1; }
}
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition-duration: .01ms !important; animation-duration: .01ms !important; } }
</style>
</head>
<body>
<div class="prototype-ribbon">PROTOTYPE 繚 隞��鈭支�蝖株恕嚗屸��煺漣摰䂿緵 繚 Ctrl+Shift+E 璅⊥��㛖恣瘚讛��券����</div>
2026-07-22 14:51:11 +08:00
<div class="app" id="app">
<aside class="sidebar" aria-label="銝餃紡��">
<div class="brand"><div class="brand-mark" aria-hidden="true">C</div><div><strong>Chub</strong><span>Browser Hub</span></div></div>
<nav class="nav">
<button aria-current="page" data-nav="instances"><span class="nav-icon" aria-hidden="true">��</span><span>摰硺�</span></button>
<button data-nav="settings"><span class="nav-icon" aria-hidden="true">��</span><span>霈曄蔭</span></button>
</nav>
<div class="sidebar-footer">�砍𧑐餈鞱�<br>銝滢�摮䀹�閫�膥�剛�</div>
</aside>
<main class="main">
<header class="topbar">
<div><div class="eyebrow" id="pageEyebrow">Browser instances</div><h1 id="pageTitle">瘚讛��典�靘�</h1><p class="subtitle" id="pageSubtitle">�臬𢆡���撖笔僎摰匧�蝞∠� Chrome 銝� Edge �臬�</p></div>
2026-07-25 16:21:38 +08:00
<div class="top-actions"><button class="icon-button" id="themeToggle" aria-label="��揢瘛梯𠧧銝駁�" title="��揢瘛梯𠧧銝駁�">��</button><div class="instance-commands" id="instanceCommands"><button class="primary" id="newInstance">嚗� �啣遣摰硺�</button><button class="secondary" id="refresh" title="�瑟鰵摰硺��嗆�� (F5)">�� �瑟鰵�嗆��</button></div></div>
2026-07-22 14:51:11 +08:00
</header>
<section class="content" id="instancesPage" aria-label="摰硺�撌乩���">
<div class="banner" role="status"><div aria-hidden="true">��</div><div><b>�砍𧑐摰匧�璅∪�</b><p>Chub �芰恣��歇瘜典�銝磰澈隞賣嵗撉屸�朞����靘卝����冽�閫�膥銝滢�鋡怨䌊�典��准��</p></div></div>
<section class="summary-grid" aria-label="摰硺�璁��">
<article class="summary"><div class="summary-label">�券�摰硺�</div><div class="summary-value">4 <span class="summary-note">撌脤�蝵�</span></div></article>
<article class="summary"><div class="summary-label">餈鞱�銝�</div><div class="summary-value" style="color:var(--success)">1 <span class="summary-note">蝔喳�</span></div></article>
<article class="summary"><div class="summary-label">�臬𢆡銝�</div><div class="summary-value" style="color:var(--accent-strong)">1 <span class="summary-note">霂瑞���</span></div></article>
<article class="summary"><div class="summary-label">��憭��</div><div class="summary-value" style="color:var(--warning)">1 <span class="summary-note">profile �删鍂</span></div></article>
</section>
<section class="workspace" aria-labelledby="listTitle">
2026-07-25 16:21:38 +08:00
<div class="toolbar"><div class="search"><span aria-hidden="true">��</span><input id="search" type="search" placeholder="�𦦵揣摰硺���楝敺���ID �𣇉垢��" aria-label="�𦦵揣摰硺���楝敺���ID �𣇉垢��"></div><select id="browserFilter" aria-label="蝑偦�㗇�閫�膥"><option value="all">�券�瘚讛���</option><option value="chrome">Chrome</option><option value="edge">Edge</option></select><select id="statusFilter" aria-label="蝑偦�厩𠶖��"><option value="all">�券��嗆��</option><option value="running">餈鞱�銝�</option><option value="starting">�臬𢆡銝�</option><option value="associated">憭㚚�撌脣���</option><option value="occupied">鋡怠���</option><option value="exited">撌脤����</option></select></div>
2026-07-25 15:36:23 +08:00
<div class="table-wrap"><table><caption id="listTitle" class="sr-only">瘚讛��典�靘见�銵�</caption><thead><tr><th scope="col">摰硺��滨妍</th><th scope="col">瘚讛��函掩��</th><th scope="col">�冽��唳旿�桀�</th><th scope="col">靚��蝡臬藁</th><th scope="col">�嗆��</th><th scope="col">�滢�</th></tr></thead><tbody id="instanceRows"></tbody></table><div class="empty-state" id="emptyState"><strong>瘝⊥��寥����靘�</strong><span>撠肽�皜�膄蝑偦�㚁��𡝗鰵撱箔�銝芣�閫�膥摰硺���</span></div></div>
2026-07-22 14:51:11 +08:00
<div class="table-footer"><span id="resultCount">�曄內 4 銝芸�靘�</span><span>���擧��伐��𡁜�</span></div>
</section>
</section>
<section class="content page-hidden" id="settingsPage" aria-label="摨𠉛鍂霈曄蔭">
2026-07-26 17:42:40 +08:00
<div class="settings-workspace"><div class="settings-grid">
2026-07-22 14:51:11 +08:00
<form class="settings-card" id="settingsForm">
<div><h2>瘚讛��其��桀�</h2><p>餈嗘�霈曄蔭�其��啣遣摰硺����霈文�潘�撌脫�摰硺����蝵桐�隡朞◤�䠷�靽格㺿��</p></div>
<div class="form-section"><h3>瘚讛��刻楝敺�</h3>
2026-07-22 15:57:02 +08:00
<div class="field"><label for="chromePath">Chrome �舀�銵峕�隞�</label><div class="path-field"><input id="chromePath" value="�芸𢆡�𤑳緵嚗䬙hrome.exe"><button type="button" class="secondary browse-setting" data-target="chromePath">�㗇𥋘��辣</button><button type="button" class="secondary search-setting" data-target="chromePath">�𦦵揣</button></div><small>�嗵征�㚚�㗇𥋘��辣�𠬍�Chub �臬𢆡�嗡��⊿�頝臬��航挪�柴��</small></div>
<div class="field"><label for="edgePath">Edge �舀�銵峕�隞�</label><div class="path-field"><input id="edgePath" value="�芸𢆡�𤑳緵嚗饢sedge.exe"><button type="button" class="secondary browse-setting" data-target="edgePath">�㗇𥋘��辣</button><button type="button" class="secondary search-setting" data-target="edgePath">�𦦵揣</button></div><small>Chrome 銝� Edge �臬��怨挽蝵殷�銋笔虾隞亦誧蝏凋蝙�刻䌊�典��啜��</small></div>
2026-07-22 14:51:11 +08:00
</div>
<div class="form-section"><h3>暺䁅恕�桀�</h3>
2026-07-27 23:50:24 +08:00
<div class="field"><label for="defaultDataDir">暺䁅恕 User Data Dir �寧𤌍敶�</label><div class="path-field"><input id="defaultDataDir" value="D:\\Tools\\chub\\user_data_dirs"><button type="button" class="secondary browse-setting" data-target="defaultDataDir">�㗇𥋘�桀�</button><button type="button" class="secondary restore-portable-setting" data-target="defaultDataDir">�W�蝔见��桀�暺䁅恕</button><button type="button" class="secondary search-setting" data-target="defaultDataDir">�𦦵揣</button></div><small>暺䁅恕頝罸�蝔见��桀�嚗Ếhub.exe ���函𤌍敶𤏪��� <span class="mono">user_data_dirs</span>嚗𥕦蘨敶勗�銋见��啣遣���靘页�銝滨宏�典歇�� profile��</small></div>
<div class="field"><label for="logDir">�亙��桀�</label><div class="path-field"><input id="logDir" value="D:\\Tools\\chub\\logs"><button type="button" class="secondary browse-setting" data-target="logDir">�㗇𥋘�桀�</button><button type="button" class="secondary restore-portable-setting" data-target="logDir">�W�蝔见��桀�暺䁅恕</button><button type="button" class="secondary search-setting" data-target="logDir">�𦦵揣</button></div><small>暺䁅恕頝罸�蝔见��桀��� <span class="mono">logs</span>嚗𥟇𠯫敹𦯀�靽嘥�敹��霂𦠜鱏靽⊥�嚗�僎餈�誘�𤩺���㺭��</small></div>
2026-07-22 14:51:11 +08:00
</div>
2026-07-25 17:47:02 +08:00
<div class="form-section"><h3>隞��</h3><p class="help">隞��摮䀹�霈方�隞������㗇����靘见�撱箏�蝻𤥁����蝷箏��游𧑐����</p><div class="field"><label for="proxySelect">撌脖�摮䀝誨��</label><select id="proxySelect"><option value="">�㗇𥋘隞��嚗�鰵撱綽�</option></select><small>�㗇𥋘�𦒘��𧼮‵�啣�嚗偦�㗇𥋘�𨀣鰵撱算�苷�皜�征颲枏�獢���</small></div><div class="field"><label for="proxyServer">隞���啣�</label><div class="path-field"><input id="proxyServer" placeholder="靘见�嚗冴ttp://127.0.0.1:8080"><button type="button" class="primary" id="proxySave">靽嘥�隞��</button><button type="button" class="danger-button" id="proxyDelete" disabled>�𣳇膄隞��</button></div><small>�舀� http��ttps��ocks4��ocks5 �� <span class="mono">scheme://host:port</span>嚗𥕢���捂�冽��溻������頝臬���</small></div><div class="settings-status" id="proxyStatus">�啣遣隞��嚗𡁜‵�坔𧑐���𦒘�摮塩��</div></div>
2026-07-22 14:51:11 +08:00
<div class="settings-actions"><button type="button" class="secondary" id="discardSettings">�𡝗�</button><button type="submit" class="primary">靽嘥�霈曄蔭</button></div>
</form>
2026-07-25 15:36:23 +08:00
<aside class="settings-card"><div><h2>餈鞱�銵䔶蛹</h2><p>敶勗��𡒊賒�臬𢆡����典��剜𧒄���霈方�銝箝��</p></div><div class="setting-list"><label class="check-line"><input type="checkbox" id="closeOnExit"><span><b>���� Chub �嗅��剜𧋦撌亙��臬𢆡���靘�</b><br><small class="help">暺䁅恕�喲𡡒���隡𡁜蔣�滚��冽�閫�膥��</small></span></label><div class="field"><label for="remoteDebugPort">餈𦦵�靚��韏瑕�蝡臬藁</label><input id="remoteDebugPort" type="number" min="1024" max="65535" value="9666" inputmode="numeric"><small>�嗵征�嗡蝙�� 9666��鍳�典�靘衤�蝏穃� 127.0.0.1嚗𤤿垢��◤�删鍂�園◇摨誩�霂蓥�銝�銝芸虾�函垢����</small></div><div class="settings-status" id="settingsStatus">霈曄蔭撌脖�摮� 繚 ���擧凒�堆��𡁜�</div></div></aside>
2026-07-26 17:42:40 +08:00
</div></div>
2026-07-22 14:51:11 +08:00
</section>
</main>
</div>
<div class="drawer-backdrop" id="backdrop"></div>
<aside class="drawer" id="drawer" aria-label="摰硺�霂行��屸�蝵�" aria-hidden="true"></aside>
<dialog id="confirmDialog" aria-labelledby="dialogTitle"><div class="dialog-inner"><h2 id="dialogTitle">�喲𡡒瘚讛��典�靘页�</h2><p id="dialogText">撠�窈瘙��閫�膥甇�虜���箝��𧊋靽嘥����憿萄�摰寧眏瘚讛��刻䌊銵������</p><div class="dialog-warning" id="dialogWarning" hidden>撘箏�蝏�迫�航��䭾� profile �唳旿�笔�����冽�閫�膥�惩�摨娍𧒄雿輻鍂��</div><div class="dialog-actions"><button class="secondary" id="dialogCancel">�𡝗�</button><button class="primary" id="dialogConfirm">隡㗛��喲𡡒</button></div></div></dialog>
2026-07-27 22:59:10 +08:00
<dialog id="instanceDeleteDialog" aria-labelledby="instanceDeleteTitle" aria-describedby="instanceDeleteText"><div class="dialog-inner"><h2 id="instanceDeleteTitle">�𣳇膄摰硺�嚗�</h2><p id="instanceDeleteText"></p><label class="check-line"><input type="checkbox" id="deleteInstanceData"><span><b>�峕𧒄�𣳇膄 User Data Dir 銝剔��唳旿</b><br><small class="help">暺䁅恕銝滚��扎��𤌍敶蓥葉���閫�膥�唳旿�𣳇膄�擧�瘜閙�憭溻��</small></span></label><div class="form-note" id="instanceDeleteUnavailable" hidden></div><div class="dialog-warning" id="instanceDeleteWarning" hidden></div><div class="dialog-actions"><button class="secondary" id="instanceDeleteCancel">�𡝗�</button><button class="danger-button" id="instanceDeleteConfirm">�𣳇膄摰硺�</button></div></div></dialog>
2026-07-27 10:01:07 +08:00
<dialog class="edit-dialog" id="editDialog" aria-labelledby="editTitle"><form class="dialog-inner edit-form" id="editForm"><div><h2 id="editTitle">蝻𤥁�摰硺�</h2><p id="editSubtitle">靽格㺿銝𧢲活�臬𢆡雿輻鍂���摮㗛�蝵柴��</p></div><div class="field"><label for="editName">摰硺��滨妍 <span>*</span></label><input id="editName" required><small>�其��典�靘见�銵其葉霂��甇斗�閫�膥�臬���</small></div><div class="field"><span>瘚讛��函掩��</span><div class="radio-row" role="radiogroup" aria-label="瘚讛��函掩��"><label><input type="radio" name="editKind" value="chrome" id="editChrome"> Chrome</label><label><input type="radio" name="editKind" value="edge" id="editEdge"> Edge</label></div></div><div class="field"><label for="editDataDir">User Data Dir <span>*</span></label><div class="path-field"><input id="editDataDir" required><button type="button" class="secondary" id="chooseEditDir">�㗇𥋘�桀�</button></div><small>敹�◆�舐�撖寡楝敺���䔶��桀�銝滩�鋡怠�銝芸�靘见��嗡蝙�具��</small></div><div class="field"><label for="editURL">�臬𢆡 URL嚗�虾�㚁�</label><input id="editURL" type="url" placeholder="https://example.com"><small>隞�𣈲�� http/https嚗𤤿�蝛箸𧒄�臬𢆡瘚讛��券�霈日△��</small></div><div class="field"><label for="editPreferredPort">擐㚚�㕑�霂閧垢��</label><input id="editPreferredPort" type="number" min="1024" max="65535" inputmode="numeric"><small>銝𧢲活�臬𢆡隡睃�雿輻鍂嚗𥕢��嗡� Chub 摰硺��滚��嗡��鞟內靽格㺿��</small></div><div class="field"><label for="editProxy">隞��</label><select id="editProxy"><option value="">�牐誨��</option></select><small>�㗇𥋘撌脖�摮条��㰘恕霂�誨���靽格㺿�𦒘�甈∪鍳�函�����</small></div><div class="field"><label for="editPort">摰鮋�靚��蝡臬藁</label><input class="readonly-value" id="editPort" readonly><small>隞�銁撌脤�霂��餈鞱��硋��典��𠉛𠶖��遬蝷綽�甇文��芾粉��</small></div><div class="form-note" id="editReadOnlyNote" hidden>霂亙�靘𧢲迤�刻�銵峕�銝箏��典��𢛵��蛹�踹��滨蔭銝舘�銵諹澈隞賭�銝��湛�瘚讛��函掩�卝��ser Data Dir �屸��厩垢��歇�����</div><div class="dialog-actions"><button type="button" class="secondary" id="editTabs">�亦���倌憿�</button><button type="button" class="secondary" id="editCancel">�𡝗�</button><button type="submit" class="primary" id="editSave">靽嘥��湔㺿</button></div></form></dialog>
<dialog class="tabs-dialog" id="tabsDialog" aria-labelledby="tabsTitle" aria-describedby="tabsMeta"><div class="dialog-inner"><div><h2 id="tabsTitle">��倌憿蛛�CDP 憿菟𢒰�格�嚗�</h2><p id="tabsSubtitle">�芾粉�亦�蝏誯�霂���祆㦤 CDP 憿菟𢒰�格���</p></div><div class="tabs-meta" id="tabsMeta">銝滢��曄內 WebSocket �啣�嚗䔶�銝滢�撖潸⏛����啜����剝△�X��扯��𡁏𧋦��</div><div class="tabs-state" id="tabsState" role="status" aria-live="polite">甇�銁霂餃�撌脤�霂���砍𧑐 CDP 憿菟𢒰�格���</div><div class="tabs-target-list" id="tabsTargetList" hidden></div><div class="dialog-actions"><button class="primary" id="tabsClose">�喲𡡒</button></div></div></dialog>
2026-07-25 16:21:38 +08:00
<dialog id="discardDialog" aria-labelledby="discardTitle"><div class="dialog-inner"><h2 id="discardTitle">�曉��芯�摮条��湔㺿嚗�</h2><p>�臭誑���摮㗛�蝵殷��𡝗𦆮撘�𧋦甈∠�颲穃僎�喲𡡒撖寡�獢���</p><div class="dialog-actions"><button class="secondary" id="keepEditing">蝏抒賒蝻𤥁�</button><button class="secondary" id="discardChanges">銝滢�摮�</button><button class="primary" id="saveChanges">靽嘥�</button></div></div></dialog>
2026-07-25 17:47:02 +08:00
<dialog id="proxyDeleteDialog" aria-labelledby="proxyDeleteTitle"><div class="dialog-inner"><h2 id="proxyDeleteTitle">�𣳇膄隞��嚗�</h2><p id="proxyDeleteText"></p><div class="dialog-actions"><button class="secondary" id="proxyDeleteCancel">�𡝗�</button><button class="danger-button" id="proxyDeleteConfirm">�𣳇膄隞��</button></div></div></dialog>
<dialog id="unexpectedExitDialog" aria-labelledby="unexpectedExitTitle" aria-describedby="unexpectedExitText"><div class="dialog-inner"><h2 id="unexpectedExitTitle">瘚讛��典歇����</h2><p id="unexpectedExitText"></p><ul class="dialog-list" id="unexpectedExitList"></ul><div class="form-note">摰硺��滨蔭�� User Data Dir �芾◤�𣳇膄����剜迨�鞟內�𠬍��臭誑隞𤾸�靘见�銵券��啣鍳�冽�閫�膥��</div><div class="dialog-actions"><button class="primary" id="unexpectedExitAcknowledge">�仿�鈭�</button></div></div></dialog>
2026-07-22 14:51:11 +08:00
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script>
2026-07-25 16:53:44 +08:00
const proxies = [
{ id: 'proxy-sg', name: '�啣��∪枂��', server: 'http://127.0.0.1:8080' },
{ id: 'proxy-test', name: '瘚贝� SOCKS5', server: 'socks5://127.0.0.1:1080' }
];
2026-07-22 14:51:11 +08:00
const instances = [
2026-07-27 09:29:10 +08:00
{ id: 'inst-chrome-shop', name: 'Shopee 繚 SG 銝餃�', kind: 'chrome', browser: 'Chrome', status: 'running', statusText: '餈鞱�銝哨��条恣嚗�', pid: '18420', port: 9666, preferredPort: 9666, dir: 'D:\\Chub\\profiles\\shopee-sg', started: '隞𠰴予 09:42', url: 'https://seller.shopee.sg/', proxyId: 'proxy-sg', executable: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe' },
{ id: 'inst-edge-review', name: '蝝䭾�摰⊥瓲 繚 Edge', kind: 'edge', browser: 'Edge', status: 'starting', statusText: '�臬𢆡銝�', pid: '��', port: 0, preferredPort: 9667, dir: 'D:\\Chub\\profiles\\asset-review', started: '甇�銁�臬𢆡', url: 'https://example.com/review', executable: 'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe' },
{ id: 'inst-chrome-ph', name: 'Shopee 繚 PH 憭�鍂', kind: 'chrome', browser: 'Chrome', status: 'associated', statusText: '憭㚚�撌脣���', pid: '16108', port: 9668, preferredPort: 9668, dir: 'D:\\Chub\\profiles\\shopee-ph', started: '�典予 18:16', url: 'https://seller.shopee.ph/', executable: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe' },
{ id: 'inst-edge-test', name: '瘚贝��臬� 繚 Edge', kind: 'edge', browser: 'Edge', status: 'exited', statusText: '撌脤����', pid: '��', port: 0, preferredPort: 9669, dir: 'D:\\Chub\\profiles\\edge-test', started: '�典予 16:04', url: 'https://localhost:3000', executable: 'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe', exit: '���箇� 0' }
2026-07-22 14:51:11 +08:00
];
let selectedId = 'inst-chrome-shop';
let modalAction = 'stop';
2026-07-25 16:21:38 +08:00
let editingID = '';
let editSnapshot = null;
2026-07-25 17:47:02 +08:00
let selectedProxyID = '';
let unexpectedExitIDs = [];
let unexpectedExitFocusID = '';
2026-07-27 10:01:07 +08:00
let tabsRequest = 0;
2026-07-27 22:59:10 +08:00
let pendingInstanceDeleteID = '';
let instanceDeleteRunning = false;
2026-07-22 14:51:11 +08:00
const $ = (id) => document.getElementById(id);
const statusClass = (s) => s;
2026-07-25 17:47:02 +08:00
const proxyLabel = (id) => proxies.find(p => p.id === id)?.server || '�牐誨��';
2026-07-27 09:29:10 +08:00
const portLabel = (x) => x.status === 'associated' ? (x.port ? `憭㚚� ${x.port}` : '��') : x.status === 'starting' ? (x.preferredPort ? `��� ${x.preferredPort}` : '��') : x.port ? `摰鮋� ${x.port}` : x.status === 'exited' ? (x.preferredPort ? `撱箄悅 ${x.preferredPort}` : '��') : '��';
2026-07-25 17:47:02 +08:00
function renderProxyOptions(select, selected = '') { select.innerHTML = '<option value="">�牐誨��</option>' + proxies.map(p => `<option value="${p.id}">${p.server}</option>`).join(''); select.value = selected; }
function renderProxies() { const select = $('proxySelect'), input = $('proxyServer'), status = $('proxyStatus'), remove = $('proxyDelete'); select.innerHTML = '<option value="">�㗇𥋘隞��嚗�鰵撱綽�</option>' + proxies.map(p => `<option value="${p.id}">${p.server}</option>`).join(''); if (!proxies.some(p => p.id === selectedProxyID)) selectedProxyID = ''; select.value = selectedProxyID; const selected = proxies.find(p => p.id === selectedProxyID); input.value = selected?.server || ''; remove.disabled = !selected; status.textContent = selected ? `甇�銁蝻𤥁�撌脖�摮䀝誨���撌脰◤ ${instances.filter(i => i.proxyId === selected.id).length} 銝芸�靘衤蝙�具�� : '�啣遣隞��嚗𡁜‵�坔𧑐���𦒘�摮塩��'; }
2026-07-22 14:51:11 +08:00
function filtered() {
const q = $('search').value.trim().toLowerCase(), browser = $('browserFilter').value, status = $('statusFilter').value;
2026-07-27 09:29:10 +08:00
return instances.filter(x => (!q || [x.name, x.browser, x.pid, portLabel(x), x.dir].join(' ').toLowerCase().includes(q)) && (browser === 'all' || x.kind === browser) && (status === 'all' || x.status === status));
2026-07-22 14:51:11 +08:00
}
2026-07-25 16:21:38 +08:00
function selectRow(id, body) {
selectedId = id;
body.querySelectorAll('tr').forEach(row => row.classList.toggle('selected', row.dataset.id === id));
}
2026-07-22 14:51:11 +08:00
function renderRows() {
const rows = filtered(), body = $('instanceRows'); body.innerHTML = '';
2026-07-25 16:21:38 +08:00
rows.forEach(x => { const tr = document.createElement('tr'); tr.className = x.id === selectedId ? 'selected' : ''; tr.tabIndex = 0; tr.dataset.id = x.id; tr.title = '��稬�𡝗� Enter 蝻𤥁�摰硺�'; tr.innerHTML = `
<td><div class="instance-name"><span class="browser-logo ${x.kind}" aria-hidden="true">${x.kind === 'chrome' ? 'C' : 'E'}</span><div><strong>${x.name}</strong><small>${x.id}</small></div></div></td>
2026-07-27 09:29:10 +08:00
<td>${x.browser}</td><td class="mono">${x.dir}</td><td class="mono">${portLabel(x)}</td><td><span class="status ${statusClass(x.status)}">${x.statusText}</span></td>
2026-07-25 17:47:02 +08:00
<td><div class="row-actions"><button data-action="start" aria-label="${x.status === 'associated' ? '�齿鰵璉�瘚�' : x.status === 'running' ? '�𨀣迫' : '�臬𢆡'} ${x.name}">${x.status === 'associated' ? '�齿鰵璉�瘚�' : x.status === 'running' ? '�� �𨀣迫' : '�� �臬𢆡'}</button><button data-action="edit" aria-label="蝻𤥁� ${x.name}">蝻𤥁�</button><button data-action="delete" aria-label="�𣳇膄 ${x.name}">�𣳇膄</button></div></td>`;
2026-07-25 16:21:38 +08:00
tr.addEventListener('click', e => { if (!e.target.closest('button')) selectRow(x.id, body); });
tr.addEventListener('dblclick', e => { if (!e.target.closest('button')) openEdit(x.id); });
tr.addEventListener('keydown', e => { if (e.key === 'Enter') { e.preventDefault(); openEdit(x.id); } else if (e.key === ' ') { e.preventDefault(); selectRow(x.id, body); } });
2026-07-27 22:59:10 +08:00
tr.querySelectorAll('button').forEach(b => b.addEventListener('click', e => { e.stopPropagation(); selectRow(x.id, body); if (b.dataset.action === 'edit') openEdit(x.id); else if (b.dataset.action === 'delete') openInstanceDelete(x.id); else if (x.status === 'running') { x.status = 'stopping'; x.statusText = '�𨀣迫銝�'; renderRows(); setTimeout(() => { x.status = 'exited'; x.statusText = '撌脤����'; x.pid = '��'; x.port = 0; renderRows(); showToast(`��${x.name}�嘥歇隡㗛����箝��); }, 700); } else openConfirm(x.id, 'start'); }));
2026-07-22 14:51:11 +08:00
body.appendChild(tr);
});
$('emptyState').style.display = rows.length ? 'none' : 'block'; $('resultCount').textContent = `�曄內 ${rows.length} 銝芸�靘㞗;
}
2026-07-27 09:29:10 +08:00
function openDetails(id) { selectedId = id; const x = instances.find(i => i.id === id), externallyAssociated = x.status === 'associated'; $('drawer').innerHTML = `<div class="drawer-head"><div><h2>${x.name}</h2><p>${x.browser} 繚 ${x.id}</p></div><button class="icon-button" id="closeDrawer" aria-label="�喲𡡒霂行�">�</button></div><div class="drawer-body"><div><span class="status ${x.status}">${x.statusText}</span></div><div class="detail-card"><div class="detail-line"><span>餈𤤿� PID</span><span class="mono">${x.pid}</span></div><div class="detail-line"><span>靚��蝡臬藁</span><span class="mono">${portLabel(x)}</span></div><div class="detail-line"><span>瘚讛���</span><span>${x.browser}</span></div><div class="detail-line"><span>user data dir</span><span class="mono">${x.dir}</span></div><div class="detail-line"><span>�臬𢆡�啣�</span><span class="mono">${x.url}</span></div><div class="detail-line"><span>�舀�銵峕�隞�</span><span class="mono">${x.executable}</span></div>${x.exit ? `<div class="detail-line"><span>���箇���</span><span>${x.exit}</span></div>` : ''}</div><div class="form-section"><h3>摰硺��滢�</h3><div class="drawer-actions" style="border-top:0;padding-top:0;justify-content:flex-start"><button class="secondary" id="restartBtn" ${x.status === 'starting' || externallyAssociated ? 'disabled' : ''}>�� �滚鍳</button><button class="danger-button" id="stopBtn" ${x.status === 'exited' || externallyAssociated ? 'disabled' : ''}>�喲𡡒摰硺�</button></div>${externallyAssociated ? '<small class="help">憭㚚��唾�摰硺�隞��閫��嚗龦hub 銝滢��亦恣�嗥��賢𪂹�麄��</small>' : ''}</div><div class="form-section"><h3>��餈烐暑��</h3><div class="activity"><div class="activity-item"><i></i><span>�嗆��歇�峕郊 繚 ${x.started}</span></div><div class="activity-item"><i></i><span>${externallyAssociated ? '撌脤�霂�𧋦�� CDP 蝡舐�嚗𥟇𧊋�亦恣憭㚚�瘚讛��具��' : x.status === 'occupied' ? '璉�瘚见�憭㚚�餈𤤿��删鍂霂� user data dir' : '摰硺��滨蔭撌脤�朞�頨思遢�⊿�'}</span></div></div></div></div>`; showDrawer(); $('closeDrawer').onclick = hideDrawer; $('stopBtn').onclick = () => openConfirm(x.id, false); $('restartBtn').onclick = () => openConfirm(x.id, 'restart'); }
2026-07-25 16:21:38 +08:00
function editIsLocked(x) { return ['running', 'starting', 'associated'].includes(x.status); }
function editIsDirty() {
if (!editingID || !editSnapshot) return false;
2026-07-27 09:29:10 +08:00
return $('editName').value !== editSnapshot.name || $('editDataDir').value !== editSnapshot.dir || $('editURL').value !== editSnapshot.url || $('editPreferredPort').value !== String(editSnapshot.preferredPort) || $('editProxy').value !== editSnapshot.proxyId || document.querySelector('input[name="editKind"]:checked')?.value !== editSnapshot.kind;
2026-07-25 16:21:38 +08:00
}
function focusEditedRow(id) { requestAnimationFrame(() => document.querySelector(`tr[data-id="${id}"]`)?.focus()); }
2026-07-27 10:01:07 +08:00
function finishEdit() { if ($('tabsDialog').open) $('tabsDialog').close(); const id = editingID; $('editDialog').close(); editingID = ''; editSnapshot = null; focusEditedRow(id); }
2026-07-25 16:21:38 +08:00
function openEdit(id) {
const x = instances.find(i => i.id === id); if (!x) return;
2026-07-27 09:29:10 +08:00
selectedId = id; editingID = id; editSnapshot = { name: x.name, dir: x.dir, url: x.url || '', kind: x.kind, preferredPort: x.preferredPort || '', proxyId: x.proxyId || '' };
const locked = editIsLocked(x); $('editName').value = x.name; $('editDataDir').value = x.dir; $('editURL').value = x.url || ''; $('editPreferredPort').value = x.preferredPort || ''; renderProxyOptions($('editProxy'), x.proxyId || ''); $('editPort').value = x.port || '��'; $('editChrome').checked = x.kind === 'chrome'; $('editEdge').checked = x.kind === 'edge';
2026-07-25 16:21:38 +08:00
$('editSubtitle').textContent = locked ? '敶枏�餈鞱��嗆����舀䰻�钅�蝵殷�雿�澈隞賜漲�笔�畾萎��臭耨�嫘��' : '靽格㺿銝𧢲活�臬𢆡雿輻鍂���摮㗛�蝵柴��';
2026-07-27 10:01:07 +08:00
const canInspectTabs = ['running', 'associated'].includes(x.status) && Number(x.port) > 0;
$('editChrome').disabled = locked; $('editEdge').disabled = locked; $('editDataDir').disabled = locked; $('chooseEditDir').disabled = locked; $('editPreferredPort').disabled = locked; $('editTabs').disabled = !canInspectTabs; $('editTabs').title = canInspectTabs ? '�芾粉�亦�撌脤�霂�� CDP 憿菟𢒰�格�' : '�亦���倌憿菟�閬�歇撉諹����銵峕�憭㚚��唾��嗆���摰鮋�靚��蝡臬藁'; $('editReadOnlyNote').hidden = !locked;
2026-07-25 16:21:38 +08:00
$('editDialog').showModal(); setTimeout(() => $('editName').focus(), 0);
}
function saveEdit() {
const x = instances.find(i => i.id === editingID); if (!x) return finishEdit();
2026-07-27 09:29:10 +08:00
const name = $('editName').value.trim(), dir = $('editDataDir').value.trim(), url = $('editURL').value.trim(), preferredPort = Number($('editPreferredPort').value);
2026-07-25 16:21:38 +08:00
if (!name || !dir) { showToast('霂瑕‵�坔�靘见�蝘啣� User Data Dir��'); (!name ? $('editName') : $('editDataDir')).focus(); return; }
2026-07-27 23:50:24 +08:00
if (instances.some(item => item.id !== x.id && item.name.trim().toLocaleLowerCase() === name.toLocaleLowerCase())) { showToast('摰硺��滨妍撌脣��剁�霂瑟凒�U��'); $('editName').focus(); return; }
2026-07-25 16:53:44 +08:00
const locked = editIsLocked(x); x.name = name; x.url = url; x.proxyId = $('editProxy').value;
2026-07-27 09:29:10 +08:00
if (!locked) { if (!Number.isInteger(preferredPort) || preferredPort < 1024 || preferredPort > 65535) { showToast('霂瑕‵�蹱����擐㚚�㕑�霂閧垢����'); $('editPreferredPort').focus(); return; } x.dir = dir; x.kind = document.querySelector('input[name="editKind"]:checked')?.value || x.kind; x.browser = x.kind === 'edge' ? 'Edge' : 'Chrome'; x.preferredPort = preferredPort; }
2026-07-25 16:21:38 +08:00
if ($('discardDialog').open) $('discardDialog').close(); renderRows(); const savedName = x.name; finishEdit(); showToast(`撌脖�摮覀��${savedName}�萘�摰硺��滨蔭��);
}
function requestEditClose() { if (editIsDirty()) { $('editDialog').close(); $('discardDialog').showModal(); return; } finishEdit(); }
2026-07-27 10:01:07 +08:00
function closeTabs() { tabsRequest++; if ($('tabsDialog').open) $('tabsDialog').close(); if ($('editDialog').open) setTimeout(() => $('editTabs').focus(), 0); }
function openTabs() {
const x = instances.find(i => i.id === editingID);
if (!x || !['running', 'associated'].includes(x.status) || !Number(x.port)) { showToast('�亦���倌憿菟�閬��鈭𤾸歇撉諹����銵峕�憭㚚��唾��嗆���撟嗅��匧����霂閧垢����'); return; }
const request = ++tabsRequest;
$('tabsTitle').textContent = '��倌憿蛛�CDP 憿菟𢒰�格�嚗�';
$('tabsSubtitle').textContent = `${x.name} 繚 摰鮋�蝡臬藁 ${x.port} 繚 �芾粉�亦�`;
$('tabsState').hidden = false; $('tabsState').textContent = '甇�銁霂餃�撌脤�霂���砍𧑐 CDP 憿菟𢒰�格���'; $('tabsTargetList').hidden = true; $('tabsTargetList').innerHTML = '';
$('tabsDialog').showModal(); setTimeout(() => $('tabsClose').focus(), 0);
setTimeout(() => {
if (request !== tabsRequest || !$('tabsDialog').open || editingID !== x.id) return;
const targets = x.kind === 'edge'
? [{ type: 'page', title: '蝝䭾�摰⊥瓲', url: 'https://example.com/review' }, { type: 'service_worker', title: '嚗�����嚗�', url: 'chrome-extension://example/worker.js' }]
: [{ type: 'page', title: '餈鞱𨯫�𤾸蝱', url: 'https://seller.shopee.sg/' }, { type: 'page', title: '霈W�銝剖�', url: 'https://seller.shopee.sg/orders' }];
$('tabsState').textContent = targets.length ? '撌脰粉�硋蘨霂� CDP 憿菟𢒰�格���RL 撌脩宏�斤鍂�瑚縑�胯��䰻霂W��啣���挾��' : '�芸��啣虾�曄內�� CDP 憿菟𢒰�格���';
$('tabsTargetList').innerHTML = targets.map(target => `<article class="tabs-target"><small>${target.type}</small><strong>${target.title}</strong><code>${target.url}</code></article>`).join('');
$('tabsTargetList').hidden = !targets.length;
}, 380);
}
2026-07-27 23:50:24 +08:00
function openLaunch() { $('drawer').innerHTML = `<div class="drawer-head"><div><h2>�啣遣瘚讛��典�靘�</h2><p>�滨蔭摰峕��𠬍�Chub 隡𡁜銁�祆㦤�臬𢆡銝�銝芷�蝳餌㴓憓���</p></div><button class="icon-button" id="closeDrawer" aria-label="�喲𡡒�啣遣摰硺�">�</button></div><form class="drawer-body instance-form-workspace" id="launchForm"><div class="form-section"><h3>瘚讛��券�蝵�</h3><div class="field"><label for="instanceName">摰硺��滨妍 <span>*</span></label><input id="instanceName" placeholder="靘见�嚗𡁜恣�� Edge" required><small>�滨妍敹�◆�臭�嚗𤤿𤌍敶蓥�雿輻鍂摰匧��硋�蝘啣�蝔喳� ID嚗䔶��擧㺿�滢�隡𡁶宏�典歇�㗇㺭�柴��</small></div><div class="field"><span>瘚讛��函掩�� <span>*</span></span><div class="radio-row" role="radiogroup" aria-label="瘚讛��函掩��"><label><input type="radio" name="kind" value="chrome" checked> Chrome</label><label><input type="radio" name="kind" value="edge"> Edge</label></div></div><div class="field"><label for="exe">�舀�銵峕�隞�</label><input id="exe" value="�芸𢆡�𤑳緵" aria-describedby="exeHelp"><small id="exeHelp">�嗵征�嗆�瘚讛��函掩�𧢲�蝝X����鋆�楝敺���</small></div></div><div class="form-section"><h3>餈鞱��臬�</h3><div class="field"><label for="dataDir">User Data Dir <span>*</span></label><div class="path-field"><input id="dataDir" value="D:\\Tools\\chub\\user_data_dirs\\instance-摰⊥瓲-7f3a21" required><button type="button" class="secondary" id="chooseInstanceDir">�㗇𥋘�桀�</button></div><small>暺䁅恕頝罸�蝔见��桀�嚗𥕦鍳�典�隡𡁻�霂�𤌍敶訫虾�辷��䔶��桀�銝滩�鋡怠�銝芸�靘见��嗡蝙�具��</small></div><div class="field"><label for="profile">profile-directory</label><input id="profile" value="Default"></div><div class="field"><label for="url">�臬𢆡 URL嚗�虾�㚁�</label><input id="url" type="url" placeholder="https://example.com"><small>隞�𣈲�� http/https嚗𤤿�蝛箸𧒄�臬𢆡瘚讛��券�霈日△��</small></div><div class="field"><label for="launchPreferredPort">擐㚚�㕑�霂閧垢��</label><input id="launchPreferredPort" type="number" min="1024" max="65535" value="9670" inputmode="numeric"><small>蝟餌�隞擧迨�澆�憪见遣霈格𧊋鋡怠�隞� Chub 摰硺�憸����垢���摰鮋�蝡臬藁隡𡁜銁�臬𢆡�園�霂���</small></div><div class="field"><label for="launchProxy">隞��</label><select id="launchProxy"><option value="">�牐誨��</option></select><small>�㗇𥋘霈曄蔭銝凋�摮条��㰘恕霂�誨���蝡舐�隡𡁜銁�臬𢆡�嗅��其��鉝��</small></div></div><div class="form-section"><h3>�臬𢆡�厰★</h3><label class="check-line"><input type="checkbox" id="headless"> <span><b>�惩仍璅∪�</b><br><small class="help">�臬𢆡�𤾸蝱瘚讛��剁�銝齿遬蝷箇�����</small></span></label><label class="check-line"><input type="checkbox" id="remember" checked> <span><b>靽嘥�甇日�蝵�</b><br><small class="help">�芯�摮䁅楝敺���墧��笔��啜��</small></span></label></div><div class="drawer-actions"><button type="button" class="secondary" id="cancelLaunch">�𡝗�</button><button type="submit" class="primary">�臬𢆡摰硺�</button></div></form>`; showDrawer(); renderProxyOptions($('launchProxy')); $('closeDrawer').onclick = hideDrawer; $('cancelLaunch').onclick = hideDrawer; $('chooseInstanceDir').onclick = () => showToast('�笔�瞍𠉛內嚗𡁜��枏��桀��㗇𥋘�具��'); $('launchForm').onsubmit = e => { e.preventDefault(); const name = $('instanceName'), data = $('dataDir'), port = Number($('launchPreferredPort').value); if (!name.value.trim()) { name.focus(); showToast('霂瑁��亙�靘见�蝘啜��'); return; } if (instances.some(item => item.name.trim().toLocaleLowerCase() === name.value.trim().toLocaleLowerCase())) { name.focus(); showToast('摰硺��滨妍撌脣��剁�霂瑟凒�U��'); return; } if (!data.value.trim()) { data.focus(); data.parentElement.classList.add('error'); const msg = document.createElement('div'); msg.className = 'field-error'; msg.textContent = '霂瑕‵�� User Data Dir��'; data.parentElement.appendChild(msg); return; } if (!Number.isInteger(port) || port < 1024 || port > 65535) { $('launchPreferredPort').focus(); showToast('霂瑕
2026-07-22 14:51:11 +08:00
function showDrawer() { $('drawer').classList.add('open'); $('backdrop').classList.add('open'); $('drawer').setAttribute('aria-hidden', 'false'); setTimeout(() => $('drawer').querySelector('button, input, select')?.focus(), 60); }
function hideDrawer() { $('drawer').classList.remove('open'); $('backdrop').classList.remove('open'); $('drawer').setAttribute('aria-hidden', 'true'); }
function openConfirm(id, action) { const x = instances.find(i => i.id === id); modalAction = action; $('dialogTitle').textContent = action === 'restart' ? '�滚鍳瘚讛��典�靘页�' : action === 'start' ? '�臬𢆡瘚讛��典�靘页�' : '�喲𡡒瘚讛��典�靘页�'; $('dialogText').textContent = action === 'restart' ? `撠��霂瑟���${x.name}�脲迤撣賊���綽��滢蝙�函㮾�屸�蝵桅��啣鍳�具�� : action === 'start' ? `撠�蝙�典歇靽嘥��滨蔭�臬𢆡��${x.name}�腈�� : `撠�窈瘙���${x.name}�脲迤撣賊���箝��𧊋靽嘥����憿萄�摰寧眏瘚讛��刻䌊銵������; $('dialogWarning').hidden = action !== false; $('dialogConfirm').textContent = action === false ? '隡㗛��喲𡡒' : action === 'restart' ? '蝖株恕�滚鍳' : '�臬𢆡摰硺�'; $('dialogConfirm').className = action === false ? 'danger-button' : 'primary'; $('confirmDialog').showModal(); }
2026-07-27 22:59:10 +08:00
function canDeleteInstanceData(x) { return ['exited', 'failed'].includes(x.status); }
function updateInstanceDeleteChoice() { const x = instances.find(i => i.id === pendingInstanceDeleteID), checked = $('deleteInstanceData').checked && !$('deleteInstanceData').disabled; $('instanceDeleteWarning').hidden = !checked; $('instanceDeleteWarning').textContent = checked && x ? `撠�偶銋���歹�${x.dir}��迨�滢��䭾��日�嚗𡱰hub 隞滢��冽�銵���齿鰵璉��交�閫�膥�删鍂�� : ''; $('instanceDeleteConfirm').textContent = checked ? '�𣳇膄摰硺��𦠜㺭��' : '�𣳇膄摰硺�'; }
function openInstanceDelete(id) { const x = instances.find(i => i.id === id); if (!x) return; pendingInstanceDeleteID = id; instanceDeleteRunning = false; const allowed = canDeleteInstanceData(x); $('instanceDeleteText').textContent = `蝖桀��𣳇膄��${x.name}�萘� Chub 摰硺��滨蔭�梹�暺䁅恕隡帋��� User Data Dir��; $('deleteInstanceData').checked = false; $('deleteInstanceData').disabled = !allowed; $('instanceDeleteUnavailable').hidden = allowed; $('instanceDeleteUnavailable').textContent = allowed ? '' : '璉�瘚见�瘚讛��冽迤�刻�銵䎚��鍳��/�𨀣迫�硋��剁�霂瑕��𨀣迫撟嗅��啁𠶖����滚��斗㺭�柴����滢��臬蘨�𣳇膄摰硺��滨蔭��'; $('instanceDeleteCancel').disabled = false; $('instanceDeleteConfirm').disabled = false; updateInstanceDeleteChoice(); $('instanceDeleteDialog').showModal(); setTimeout(() => $('instanceDeleteCancel').focus(), 0); }
function closeInstanceDelete() { if (instanceDeleteRunning) return; $('instanceDeleteDialog').close(); pendingInstanceDeleteID = ''; }
$('deleteInstanceData').onchange = updateInstanceDeleteChoice;
$('instanceDeleteCancel').onclick = closeInstanceDelete;
$('instanceDeleteDialog').addEventListener('cancel', e => { e.preventDefault(); closeInstanceDelete(); });
$('instanceDeleteConfirm').onclick = () => { const x = instances.find(i => i.id === pendingInstanceDeleteID), withData = $('deleteInstanceData').checked && !$('deleteInstanceData').disabled; if (!x || instanceDeleteRunning) return; if (!withData) { instances.splice(instances.indexOf(x), 1); $('instanceDeleteDialog').close(); pendingInstanceDeleteID = ''; renderRows(); showToast(`撌脣��手��${x.name}�萘�摰硺��滨蔭嚗𢥧ser Data Dir �芾◤�𣳇膄��); return; } instanceDeleteRunning = true; $('instanceDeleteCancel').disabled = true; $('instanceDeleteConfirm').disabled = true; $('instanceDeleteConfirm').textContent = '甇�銁�𣳇膄�唳旿��'; setTimeout(() => { const current = instances.find(i => i.id === pendingInstanceDeleteID); if (current) instances.splice(instances.indexOf(current), 1); $('instanceDeleteDialog').close(); pendingInstanceDeleteID = ''; instanceDeleteRunning = false; renderRows(); showToast(`�笔�瞍𠉛內嚗𡁜歇�𣳇膄��${x.name}�萘�摰硺��滨蔭�� User Data Dir �唳旿��); }, 700); };
2026-07-22 14:51:11 +08:00
function showToast(message) { const toast = $('toast'); toast.textContent = message; toast.classList.add('show'); clearTimeout(showToast.timer); showToast.timer = setTimeout(() => toast.classList.remove('show'), 3600); }
function hasOtherModal() { return [...document.querySelectorAll('dialog[open]')].some(dialog => dialog.id !== 'unexpectedExitDialog'); }
function queueUnexpectedExit(id) {
const instance = instances.find(x => x.id === id);
if (!instance || instance.status !== 'running') return;
2026-07-27 09:29:10 +08:00
instance.status = 'exited'; instance.statusText = '撌脤����'; instance.pid = '��'; instance.port = 0; instance.exit = '撌脫�瘚见�餈𤤿�����';
if (!unexpectedExitIDs.includes(id)) unexpectedExitIDs.push(id);
selectedId = id;
renderRows();
presentUnexpectedExitNotice();
}
function presentUnexpectedExitNotice() {
const dialog = $('unexpectedExitDialog');
const exited = unexpectedExitIDs.map(id => instances.find(x => x.id === id)).filter(Boolean);
if (!exited.length || hasOtherModal()) return;
$('unexpectedExitTitle').textContent = exited.length === 1 ? '瘚讛��典歇����' : `${exited.length} 銝芣�閫�膥撌脤���槁;
$('unexpectedExitText').textContent = exited.length === 1 ? `璉�瘚见���${exited[0].name}�萘� ${exited[0].browser} 撌脤���箝�� : '璉�瘚见�隞乩��� Chub �臬𢆡���閫�膥撌脤���箝��';
$('unexpectedExitList').innerHTML = exited.map(x => `<li><strong>${x.name}</strong> 繚 ${x.browser}</li>`).join('');
unexpectedExitFocusID = exited[0].id;
if (!dialog.open) { dialog.showModal(); setTimeout(() => $('unexpectedExitAcknowledge').focus(), 0); }
}
function dismissUnexpectedExitNotice() {
const focusID = unexpectedExitFocusID;
unexpectedExitIDs = [];
$('unexpectedExitDialog').close();
requestAnimationFrame(() => {
const start = document.querySelector(`tr[data-id="${focusID}"] button[data-action="start"]`);
(start || document.querySelector(`tr[data-id="${focusID}"]`))?.focus();
});
}
2026-07-22 14:51:11 +08:00
$('dialogCancel').onclick = () => $('confirmDialog').close(); $('confirmDialog').addEventListener('cancel', e => { e.preventDefault(); $('confirmDialog').close(); }); $('dialogConfirm').onclick = () => { $('confirmDialog').close(); const message = modalAction === false ? '�喲𡡒霂瑟�撌脫�鈭歹�摰硺�撠���� STOPPING��' : modalAction === 'restart' ? '�滚鍳霂瑟�撌脫�鈭歹�甇�銁蝑匧��批�靘钅���箝��' : '�臬𢆡霂瑟�撌脫�鈭歹�摰硺�撠���� STARTING��'; setTimeout(() => showToast(message), 80); };
$('unexpectedExitAcknowledge').onclick = dismissUnexpectedExitNotice; $('unexpectedExitDialog').addEventListener('cancel', e => { e.preventDefault(); dismissUnexpectedExitNotice(); }); document.querySelectorAll('dialog').forEach(dialog => dialog.addEventListener('close', () => setTimeout(presentUnexpectedExitNotice, 0)));
2026-07-25 16:21:38 +08:00
function refreshInstances() { const button = $('refresh'); if (button.disabled) return; button.disabled = true; button.textContent = '�� �瑟鰵銝凌��'; setTimeout(() => { const starting = instances.find(x => x.status === 'starting'); if (starting) { starting.status = 'running'; starting.statusText = '餈鞱�銝哨��条恣嚗�'; starting.pid = '18468'; starting.port = '9667'; } renderRows(); button.disabled = false; button.textContent = '�� �瑟鰵�嗆��'; document.querySelector('.table-footer span:last-child').textContent = '���擧��伐��𡁜�'; showToast('撌脫��乩�摮条�摰硺��嗆����芣醌�誩�隞� Chrome �� Edge��'); }, 900); }
function setPage(page) { const settings = page === 'settings'; $('instancesPage').classList.toggle('page-hidden', settings); $('settingsPage').classList.toggle('page-hidden', !settings); $('pageEyebrow').textContent = settings ? 'Application settings' : 'Browser instances'; $('pageTitle').textContent = settings ? '霈曄蔭' : '瘚讛��典�靘�'; $('pageSubtitle').textContent = settings ? '蝞∠�瘚讛��刻楝敺����霈斤𤌍敶訫��砍𧑐餈鞱�銵䔶蛹' : '�臬𢆡���撖笔僎摰匧�蝞∠� Chrome 銝� Edge �臬�'; $('instanceCommands').classList.toggle('page-hidden', settings); if (settings) $('chromePath').focus(); }
$('newInstance').onclick = openLaunch; $('backdrop').onclick = hideDrawer; $('search').oninput = renderRows; $('browserFilter').onchange = renderRows; $('statusFilter').onchange = renderRows; $('refresh').onclick = refreshInstances; $('themeToggle').onclick = () => { const dark = document.documentElement.dataset.theme === 'dark'; document.documentElement.dataset.theme = dark ? '' : 'dark'; $('themeToggle').textContent = dark ? '��' : '��'; $('themeToggle').setAttribute('aria-label', dark ? '��揢瘛梯𠧧銝駁�' : '��揢瘚�𠧧銝駁�'); };
2026-07-27 10:01:07 +08:00
$('editCancel').onclick = requestEditClose; $('editTabs').onclick = openTabs; $('tabsClose').onclick = closeTabs; $('tabsDialog').addEventListener('cancel', e => { e.preventDefault(); closeTabs(); }); $('tabsDialog').addEventListener('close', () => { if ($('editDialog').open) setTimeout(() => $('editTabs').focus(), 0); }); $('chooseEditDir').onclick = () => showToast('�笔�瞍𠉛內嚗𡁜��枏��桀��㗇𥋘�典僎�𧼮‵ User Data Dir��'); $('editForm').addEventListener('submit', e => { e.preventDefault(); saveEdit(); }); $('editDialog').addEventListener('cancel', e => { e.preventDefault(); requestEditClose(); }); $('keepEditing').onclick = () => { $('discardDialog').close(); $('editDialog').showModal(); setTimeout(() => $('editName').focus(), 0); }; $('discardChanges').onclick = () => { $('discardDialog').close(); finishEdit(); }; $('saveChanges').onclick = saveEdit;
document.addEventListener('keydown', e => { if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'e') { e.preventDefault(); queueUnexpectedExit('inst-chrome-shop'); return; } if (e.key === 'F5' && !$('editDialog').open && !$('tabsDialog').open && !$('discardDialog').open) { e.preventDefault(); refreshInstances(); } });
2026-07-22 14:51:11 +08:00
document.querySelectorAll('[data-nav]').forEach(b => b.onclick = () => { document.querySelectorAll('[data-nav]').forEach(n => n.removeAttribute('aria-current')); b.setAttribute('aria-current', 'page'); setPage(b.dataset.nav); });
document.querySelectorAll('.browse-setting').forEach(b => b.onclick = () => showToast(`�笔�瞍𠉛內嚗𡁜��枏�${b.dataset.target.includes('Path') ? '��辣' : '�桀�'}�㗇𥋘�具��));
2026-07-22 15:57:02 +08:00
document.querySelectorAll('.search-setting').forEach(b => b.onclick = () => { if (b.dataset.timer) { clearTimeout(Number(b.dataset.timer)); delete b.dataset.timer; b.textContent = '�𦦵揣'; showToast('撌脣�瘨���齿�蝝U��'); return; } b.textContent = '�𡝗�'; showToast('甇�銁�𦦵揣�舐鍂頝臬���'); b.dataset.timer = String(setTimeout(() => { delete b.dataset.timer; b.textContent = '�𦦵揣'; showToast('�𦦵揣摰峕�嚗𡁜歇�湔鰵敶枏�摮埈挾��'); }, 1600)); });
2026-07-25 17:47:02 +08:00
$('proxySelect').onchange = () => { selectedProxyID = $('proxySelect').value; renderProxies(); $('proxyServer').focus(); };
$('proxySave').onclick = () => { const server = $('proxyServer').value.trim().toLowerCase(); if (!/^(https?|socks[45]):\/\/[^\/@?#:]+(?::\d+)$/.test(server)) { $('proxyStatus').textContent = '霂瑕‵�蹱�霈方��� scheme://host:port 隞���啣���'; return; } if (proxies.some(p => p.server === server && p.id !== selectedProxyID)) { $('proxyStatus').textContent = '霂乩誨��𧑐��撌脣��剁�霂瑚�銝𧢲�獢��㗇𥋘摰���'; return; } if (selectedProxyID) { const p = proxies.find(item => item.id === selectedProxyID); p.server = server; p.name = server; } else { selectedProxyID = `proxy-${Date.now()}`; proxies.push({ id: selectedProxyID, name: server, server }); } renderProxies(); showToast('隞��撌脖�摮矋��𡒊賒�臬𢆡撠�蝙�冽��啁垢�嫘��'); };
2026-07-27 23:50:24 +08:00
document.querySelectorAll('.restore-portable-setting').forEach(button => button.onclick = () => { const target = $(button.dataset.target); target.value = button.dataset.target === 'logDir' ? 'D:\\Tools\\chub\\logs' : 'D:\\Tools\\chub\\user_data_dirs'; showToast('撌脫�憭滢蛹蝔见��桀�暺䁅恕�潦��'); });
2026-07-25 17:47:02 +08:00
$('proxyDelete').onclick = () => { const proxy = proxies.find(p => p.id === selectedProxyID), uses = instances.filter(i => i.proxyId === selectedProxyID).length; if (!proxy) return; if (uses) { $('proxyStatus').textContent = `霂乩誨���鋡� ${uses} 銝芸�靘衤蝙�剁�銝滩��𣳇膄��; return; } $('proxyDeleteText').textContent = `蝖桀��𣳇膄隞����${proxy.server}�嘅�甇斗�雿靝�隡帋耨�嫣遙雿訫�靘卝��; $('proxyDeleteDialog').showModal(); setTimeout(() => $('proxyDeleteCancel').focus(), 0); };
$('proxyDeleteCancel').onclick = () => $('proxyDeleteDialog').close(); $('proxyDeleteDialog').addEventListener('cancel', e => { e.preventDefault(); $('proxyDeleteDialog').close(); }); $('proxyDeleteConfirm').onclick = () => { const proxy = proxies.find(p => p.id === selectedProxyID); if (proxy) proxies.splice(proxies.indexOf(proxy), 1); selectedProxyID = ''; $('proxyDeleteDialog').close(); renderProxies(); $('proxySelect').focus(); showToast('隞��撌脣��扎��'); };
2026-07-22 14:51:11 +08:00
$('settingsForm').addEventListener('submit', e => { e.preventDefault(); $('settingsStatus').textContent = '霈曄蔭撌脖�摮� 繚 ���擧凒�堆��𡁜�'; $('settingsStatus').classList.remove('warning'); showToast('霈曄蔭撌脖�摮矋��𡒊賒�啣遣摰硺�撠�蝙�冽鰵���霈文�潦��'); });
$('discardSettings').onclick = () => { $('settingsForm').reset(); $('settingsStatus').textContent = '撌脣�瘨�𧋦甈∩耨�對��W��唬�甈∩�摮睃�潦��'; $('settingsStatus').classList.add('warning'); showToast('撌脣�瘨�挽蝵桐耨�嫘��'); };
2026-07-25 16:53:44 +08:00
renderRows(); renderProxies();
2026-07-22 14:51:11 +08:00
</script>
</body>
</html>