Files
HomeDash/web/extension/install.html
JianFeeeee ff0e6418f8 feat(nav): auto-detect browser extension and offer one-click install
导航页现在会自动检测浏览历史插件是否已安装:

- 扩展在用户配置的服务地址上动态注册 content script
  (Chrome: chrome.scripting.registerContentScripts,
   Firefox: browser.contentScripts.register),
  于 document_start 给 <html> 打上 data-navi-ext 标记,
  不在其他站点运行。
- history-server 始终把「安装浏览历史插件」放在「历史访问」第一条
  (入口 url=/extension/install.html),启动时也会重建一次,
  不再需要等下一次同步。
- web/navi.js 读到标记后隐藏该入口(连 .item-wrapper 一起隐藏,
  避免留下空网格位)。
- 新增 /extension/install.html:自动识别浏览器,给出对应安装包与步骤;
  支持在 data 卷放 extension-store.json 配置商店链接,
  有商店链接时显示真正的「一键安装」。
- 段落名匹配同时兼容中英文(常用服务/Favorites、历史访问/History),
  修掉了公开示例 conf.yml 用英文名时重建逻辑完全失效的问题。
- 扩展升到 1.1.0(新增 scripting 权限与 detect.js)。

Note: Chrome/Edge 不允许从网站直接安装非商店扩展,
因此自托管场景下只能下载后手动加载;发布到商店后才会出现一键安装。
2026-09-11 10:41:10 +08:00

151 lines
7.0 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<title>安装浏览历史插件</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",sans-serif;
background:#0f172a;color:#e2e8f0;line-height:1.6;padding:32px 20px}
.wrap{max-width:720px;margin:0 auto}
h1{font-size:24px;font-weight:700;margin-bottom:8px}
.sub{color:#94a3b8;font-size:14px;margin-bottom:24px}
.card{background:#1e293b;border:1px solid #334155;border-radius:12px;padding:20px 22px;margin-bottom:16px}
.card h2{font-size:17px;font-weight:600;margin-bottom:12px;display:flex;align-items:center;gap:8px}
.badge{font-size:12px;font-weight:600;padding:2px 8px;border-radius:999px;
background:#1e3a5f;color:#93c5fd;border:1px solid #2563eb}
.card.current{border-color:#2563eb;box-shadow:0 0 0 1px #2563eb inset}
ol{margin:0 0 0 20px;font-size:14px;color:#cbd5e1}
ol li{margin-bottom:6px}
a{color:#60a5fa}
code{background:#0f172a;border:1px solid #334155;border-radius:4px;padding:1px 6px;font-size:13px}
.note{font-size:13px;color:#94a3b8;margin-top:12px;border-top:1px solid #334155;padding-top:12px}
.actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:14px}
.btn{display:inline-block;padding:9px 18px;border-radius:8px;font-size:14px;font-weight:600;
text-decoration:none;border:1px solid #334155;background:#0f172a;color:#e2e8f0;cursor:pointer}
.btn.primary{background:#2563eb;border-color:#2563eb;color:#fff}
.btn.primary:hover{background:#1d4ed8}
.btn:hover{border-color:#475569}
.site{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:10px}
.site input{flex:1;min-width:260px;background:#0f172a;border:1px solid #334155;border-radius:8px;
padding:9px 12px;color:#e2e8f0;font-size:14px}
.hint{font-size:13px;color:#94a3b8;margin-top:10px}
.steps-done{font-size:13px;color:#86efac;margin-top:10px}
</style>
</head>
<body>
<div class="wrap">
<h1>安装浏览历史插件</h1>
<p class="sub">安装后浏览记录会自动同步到本站,并据此把最常访问的服务排到「常用服务」最前面。</p>
<div class="card">
<h2>第一步:填写本站地址</h2>
<p class="hint">在插件设置里粘贴下面这个地址(点「复制」)。同步令牌从部署方的 <code>NAVI_SYNC_TOKEN</code> 环境变量获取。</p>
<div class="site">
<input id="site-url" readonly>
<button class="btn" id="copy-btn" type="button">复制</button>
</div>
<div class="steps-done" id="copy-done" hidden>已复制</div>
</div>
<div class="card" id="card-chromium" hidden>
<h2>Chrome / Edge <span class="badge">已识别你的浏览器</span></h2>
<div class="actions">
<a class="btn primary" id="store-chromium" hidden>从商店一键安装</a>
<a class="btn" href="/extension/navi-chrome.zip" download>下载扩展包(zip)</a>
</div>
<ol>
<li>解压下载的 zip 到一个固定目录(不要删除)</li>
<li>地址栏打开 <code>chrome://extensions</code>,Edge 用 <code>edge://extensions</code></li>
<li>打开右上角「开发者模式」</li>
<li>点「加载已解压的扩展程序」,选择解压出来的目录</li>
<li>点扩展图标,填入上面的本站地址与同步令牌,保存</li>
</ol>
<p class="note">Chrome 不允许从网站直接安装非商店扩展,所以这里只能下载后手动加载。若已发布到商店,上面的「一键安装」按钮会自动出现。</p>
</div>
<div class="card" id="card-firefox" hidden>
<h2>Firefox <span class="badge">已识别你的浏览器</span></h2>
<div class="actions">
<a class="btn primary" id="store-firefox" hidden>从商店一键安装</a>
<a class="btn" href="/extension/navi-firefox.xpi" download>下载扩展包(xpi)</a>
</div>
<ol>
<li>地址栏打开 <code>about:debugging#/runtime/this-firefox</code></li>
<li>点「临时加载附加组件」,选择下载的 xpi</li>
<li>点扩展图标,填入上面的本站地址与同步令牌,保存</li>
</ol>
<p class="note">Firefox 正式版只接受签名附加组件;未签名的 xpi 只能在开发者版 / ESR 上加载,或在 <code>about:config</code> 关闭 <code>xpinstall.signatures.required</code>。发布到 AMO 后可用上面的「一键安装」。</p>
</div>
<div class="card" id="card-other" hidden>
<h2>其他浏览器</h2>
<p class="hint">未能识别具体浏览器。Chromium 内核(Brave、Vivaldi、Opera、Kiwi 等)请按上面 Chrome 的步骤操作;Firefox 请按 Firefox 的步骤操作。</p>
<div class="actions">
<a class="btn" href="/extension/navi-chrome.zip" download>下载 Chromium 版(zip)</a>
<a class="btn" href="/extension/navi-firefox.xpi" download>下载 Firefox 版(xpi)</a>
</div>
</div>
<div class="card">
<h2>装好后</h2>
<ol>
<li>回到导航页并刷新,本次会话浏览几条页面</li>
<li>约 30 秒后历史会自动上报,「历史访问」开始按访问频次排序</li>
<li>检测到插件已安装后,本入口会自动隐藏</li>
</ol>
</div>
</div>
<script>
(function () {
'use strict';
document.getElementById('site-url').value = location.origin;
var copyBtn = document.getElementById('copy-btn');
copyBtn.addEventListener('click', function () {
var input = document.getElementById('site-url');
input.select();
input.setSelectionRange(0, 999);
var done = document.getElementById('copy-done');
function ok() { done.hidden = false; setTimeout(function () { done.hidden = true; }, 2000); }
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(input.value).then(ok, function () { document.execCommand('copy'); ok(); });
} else {
document.execCommand('copy'); ok();
}
});
// 浏览器识别:Firefox 用 UA 判断,其余按 Chromium 处理。
var ua = navigator.userAgent;
var isFirefox = /firefox\/|fxios\//i.test(ua);
var isChromium = /chrome\/|chromium\/|edg\//i.test(ua);
var card = document.getElementById(isFirefox ? 'card-firefox' : (isChromium ? 'card-chromium' : 'card-other'));
card.hidden = false;
card.classList.add('current');
// 可选:部署方在 /app/data/extension-store.json 放商店链接后,
// 对应平台的「一键安装」按钮会自动出现(无需重建镜像)。
fetch('/extension/store.json', { cache: 'no-store' })
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (s) {
if (!s) return;
if (s.chrome) showStore('store-chromium', s.chrome);
if (s.firefox) showStore('store-firefox', s.firefox);
})
.catch(function () { /* 未配置商店链接,保持手动安装步骤 */ });
function showStore(id, url) {
var el = document.getElementById(id);
if (!el) return;
el.href = url;
el.hidden = false;
}
})();
</script>
</body>
</html>