Files
Quick-Paste/renderer/index.html
2026-09-16 21:19:32 +08:00

513 lines
24 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 http-equiv="Content-Security-Policy"
content="default-src 'self' file:; style-src 'self' 'unsafe-inline'; script-src 'self'; img-src 'self' data: file:;" />
<title>快速粘贴</title>
<!-- 浏览器标签页 / 收藏夹 / 桌面快捷方式都会用到的 favicon
用项目根的 icon.icoChromium 内核会自动挑合适尺寸。 -->
<link rel="icon" type="image/x-icon" href="../icon.ico" />
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<!-- ====== SVG 图标精灵(统一引用) ====== -->
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
<defs>
<symbol id="i-pin" viewBox="0 0 24 24" fill="none">
<path d="M14 4l5 5-2 2-1 4-4 1-1 4-3-3-5 5 5-5-3-3 4-1 1-4 4-1 2-2z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round"/>
</symbol>
<symbol id="i-search" viewBox="0 0 24 24" fill="none">
<circle cx="11" cy="11" r="7" stroke="currentColor" stroke-width="1.6"/>
<path d="m20 20-3.5-3.5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
</symbol>
<symbol id="i-plus" viewBox="0 0 24 24" fill="none">
<path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
</symbol>
<symbol id="i-x" viewBox="0 0 24 24" fill="none">
<path d="M6 6l12 12M18 6 6 18" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
</symbol>
<symbol id="i-tray" viewBox="0 0 24 24" fill="none">
<path d="M4 13v5a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-5M4 13h4l1 2h6l1-2h4M4 13l1-6a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1l1 6" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round"/>
</symbol>
<symbol id="i-edit" viewBox="0 0 24 24" fill="none">
<path d="m4 20 4-1 10-10-3-3L5 16l-1 4z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round"/>
<path d="m14 6 3 3" stroke="currentColor" stroke-width="1.6"/>
</symbol>
<symbol id="i-trash" viewBox="0 0 24 24" fill="none">
<path d="M5 7h14M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2M7 7l1 12a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1l1-12" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round"/>
</symbol>
<symbol id="i-copy" viewBox="0 0 24 24" fill="none">
<rect x="8" y="8" width="12" height="12" rx="2" stroke="currentColor" stroke-width="1.6"/>
<path d="M16 8V6a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h2" stroke="currentColor" stroke-width="1.6"/>
</symbol>
<symbol id="i-arrow-up" viewBox="0 0 24 24" fill="none">
<path d="M12 19V5M6 11l6-6 6 6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<symbol id="i-arrow-down" viewBox="0 0 24 24" fill="none">
<path d="M12 5v14M6 13l6 6 6-6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<symbol id="i-folder" viewBox="0 0 24 24" fill="none">
<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/>
</symbol>
<symbol id="i-recent" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="1.6"/>
<path d="M12 7v5l3 2" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<symbol id="i-settings" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="1.6"/>
<path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/>
</symbol>
<symbol id="i-star" viewBox="0 0 24 24" fill="none">
<path d="m12 3 2.7 5.7 6.3.9-4.6 4.4 1.1 6.2L12 17.5l-5.5 2.7 1.1-6.2L3 9.6l6.3-.9L12 3z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>
</symbol>
<symbol id="i-star-fill" viewBox="0 0 24 24">
<path d="m12 3 2.7 5.7 6.3.9-4.6 4.4 1.1 6.2L12 17.5l-5.5 2.7 1.1-6.2L3 9.6l6.3-.9L12 3z" fill="currentColor"/>
</symbol>
<symbol id="i-check" viewBox="0 0 24 24" fill="none">
<path d="m5 12 5 5 9-11" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<symbol id="i-alert" viewBox="0 0 24 24" fill="none">
<path d="M12 3 2 21h20L12 3z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/>
<path d="M12 10v5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
<circle cx="12" cy="18" r="0.9" fill="currentColor"/>
</symbol>
<symbol id="i-info" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="1.6"/>
<path d="M12 11v6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
<circle cx="12" cy="7.5" r="0.9" fill="currentColor"/>
</symbol>
<symbol id="i-sun" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="4" stroke="currentColor" stroke-width="1.6"/>
<path d="M12 3v2M12 19v2M3 12h2M19 12h2M5.5 5.5l1.4 1.4M17.1 17.1l1.4 1.4M5.5 18.5l1.4-1.4M17.1 6.9l1.4-1.4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
</symbol>
<symbol id="i-moon" viewBox="0 0 24 24" fill="none">
<path d="M20 14A8 8 0 1 1 10 4a6 6 0 0 0 10 10z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/>
</symbol>
<symbol id="i-paste-icon" viewBox="0 0 24 24" fill="none">
<rect x="6" y="4" width="12" height="16" rx="2" stroke="currentColor" stroke-width="1.6"/>
<path d="M9 4h6v3H9z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/>
<path d="M9 12h6M9 16h4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
</symbol>
<!-- 折叠:左侧栏往左收 -->
<symbol id="i-sidebar-collapse" viewBox="0 0 24 24" fill="none">
<rect x="3" y="4" width="18" height="16" rx="2" stroke="currentColor" stroke-width="1.6"/>
<path d="M9 4v16" stroke="currentColor" stroke-width="1.6"/>
<path d="m13 10-3 2 3 2" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<!-- 展开:左侧栏从左侧展开 -->
<symbol id="i-sidebar-expand" viewBox="0 0 24 24" fill="none">
<rect x="3" y="4" width="18" height="16" rx="2" stroke="currentColor" stroke-width="1.6"/>
<path d="M9 4v16" stroke="currentColor" stroke-width="1.6"/>
<path d="m11 10 3 2-3 2" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<!-- 置顶:标准图钉(圆角矩形头 + 针尖)。开关靠 .toggle-on 高亮态区分 -->
<symbol id="i-pin-on" viewBox="0 0 24 24" fill="none">
<path d="M9 3a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v3l2 3H7l2-3V3z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/>
<path d="M8 9l-2 3h12l-2-3" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M12 12v9" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
</symbol>
<!-- 外部链接:方框 + 右上方箭头,经典「在新窗口打开」语义 -->
<symbol id="i-external" viewBox="0 0 24 24" fill="none">
<path d="M14 4h6v6" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M20 4 10 14" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
<path d="M19 13v6a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h6" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
</defs>
</svg>
<!-- ====== 标题栏 ====== -->
<header id="titlebar">
<div class="title">
<span class="logo">
<!-- 任务栏/Alt-Tab 已经在用 icon.ico标题栏左上角的徽标也用同一份图标保持一致。
ICO 内置多尺寸Chromium 会按容器尺寸自动挑最接近的那一档渲染。 -->
<img class="logo-img" src="../icon.ico" alt="快速粘贴" />
</span>
<span class="title-name">快速粘贴</span>
</div>
<div class="title-actions">
<button id="btn-pin-top" class="icon-btn toggle-on" title="始终置顶(点击切换)" aria-label="切换始终置顶" aria-pressed="true">
<svg class="icon"><use href="#i-pin-on"/></svg>
</button>
<button id="btn-settings" class="icon-btn" title="设置">
<svg class="icon"><use href="#i-settings"/></svg>
</button>
<button id="btn-hide" class="icon-btn" title="关闭" aria-label="关闭(最小化到托盘)">
<svg class="icon"><use href="#i-x"/></svg>
</button>
</div>
</header>
<!-- ====== 主体 ====== -->
<main id="main">
<!-- 左侧分组 -->
<aside id="sidebar" aria-label="分组与快捷视图">
<!-- 侧栏顶部:收藏 / 最近(跨分组的快捷入口,独立区域) -->
<div class="sidebar-quick">
<button class="quick-item" data-filter="fav" title="收藏F">
<svg class="icon"><use href="#i-star"/></svg>
<span class="name">收藏</span>
<span class="count" id="quick-count-fav">0</span>
</button>
<button class="quick-item" data-filter="recent" title="最近使用R">
<svg class="icon"><use href="#i-recent"/></svg>
<span class="name">最近</span>
<span class="count" id="quick-count-recent">0</span>
</button>
</div>
<div class="sidebar-section">
<div class="sidebar-header">
<span>分组</span>
<button id="btn-add-group" class="mini-btn" title="新建分组">
<svg class="icon"><use href="#i-plus"/></svg>
</button>
</div>
<ul id="group-list"></ul>
</div>
<div class="sidebar-footer">
<button id="btn-toggle-sidebar" class="icon-btn sidebar-toggle-btn" title="折叠分组栏" aria-label="折叠分组栏" aria-pressed="true">
<svg class="icon"><use href="#i-sidebar-collapse"/></svg>
</button>
</div>
</aside>
<!-- 右侧内容 -->
<section id="content">
<div class="content-header">
<div class="content-header-left">
<h2 id="current-group-name">常用</h2>
<span id="phrase-count" class="phrase-count hidden"></span>
</div>
</div>
<div class="search-bar">
<svg class="icon search-icon"><use href="#i-search"/></svg>
<input id="search-input" type="text" placeholder="搜索短语…" autocomplete="off" spellcheck="false" />
<button id="search-clear" class="search-clear hidden" title="清空">
<svg class="icon"><use href="#i-x"/></svg>
</button>
</div>
<!-- 过滤器已移至左侧栏顶部(收藏 / 最近);这里不再保留 -->
<ul id="phrase-list"></ul>
<div id="phrase-empty" class="empty-hint hidden">
<div class="empty-illustration" id="empty-illustration">
<svg viewBox="0 0 64 64" fill="none">
<rect x="14" y="10" width="32" height="44" rx="4" stroke="currentColor" stroke-width="1.6" opacity="0.4"/>
<path d="M22 22h16M22 28h16M22 34h10" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" opacity="0.4"/>
<circle cx="46" cy="46" r="10" stroke="currentColor" stroke-width="1.6"/>
<path d="M46 41v10M41 46h10" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
</svg>
</div>
<div class="empty-title">这个分组还是空的</div>
<div class="empty-desc">点下方按钮添加你的第一条常用语</div>
<button id="empty-add-btn" class="empty-action">
<svg class="icon"><use href="#i-plus"/></svg>
添加第一条短语
</button>
</div>
<div class="add-bar">
<!-- 仅在侧栏折叠时显示:把按钮重新放回「+ 添加短语」左边,
这样折叠态下用户不用回到标题栏 / 托盘去找展开按钮 -->
<button id="btn-expand-sidebar" class="icon-btn sidebar-expand-btn" title="展开分组栏" aria-label="展开分组栏" aria-pressed="false">
<svg class="icon"><use href="#i-sidebar-expand"/></svg>
</button>
<button id="btn-add-phrase" class="primary-btn">
<svg class="icon"><use href="#i-plus"/></svg>
<span>添加短语</span>
</button>
</div>
</section>
</main>
<!-- ====== 短语编辑弹窗 ====== -->
<div id="modal" class="modal hidden">
<div class="modal-card phrase-modal">
<div class="modal-header">
<span class="modal-title-wrap">
<svg class="icon modal-title-icon"><use href="#i-paste-icon"/></svg>
<span id="modal-title">添加短语</span>
</span>
<button id="modal-close" class="icon-btn" title="关闭">
<svg class="icon"><use href="#i-x"/></svg>
</button>
</div>
<div class="modal-body">
<label class="field-label">
<span>标题</span>
<span class="field-counter" id="title-counter">0 / 40</span>
</label>
<input id="phrase-title" type="text" placeholder="比如:客服开场白" maxlength="40" />
<label class="field-label">
<span>内容</span>
<span class="field-counter" id="content-counter">0 / 2000</span>
</label>
<textarea id="phrase-content" rows="6" maxlength="2000"
placeholder="点击后会粘贴这段文字到当前焦点窗口"></textarea>
<!-- 占位符提示:插入变量后会在这里实时显示展开结果 -->
<div id="placeholder-preview" class="placeholder-preview hidden">
<div class="placeholder-preview-label">展开预览</div>
<div id="placeholder-preview-text" class="placeholder-preview-text"></div>
</div>
<details class="placeholder-hint">
<summary>支持的变量</summary>
<div class="placeholder-list">
<span><code>{date}</code> 当前日期2026-09-11</span>
<span><code>{time}</code> 当前时间14:30</span>
<span><code>{datetime}</code> 日期 + 时间</span>
<span><code>{weekday}</code> 周几(周一)</span>
<span><code>{clipboard}</code> 当前剪贴板内容</span>
</div>
</details>
<div class="field-row">
<label class="field-label-inline">
<input id="phrase-fav" type="checkbox" />
<svg class="icon"><use href="#i-star"/></svg>
<span>加入收藏</span>
</label>
<span class="field-tip">收藏后会显示在顶部分类「收藏」中</span>
</div>
<div class="modal-actions">
<button id="modal-cancel" class="text-btn">取消</button>
<button id="modal-save" class="primary-btn" disabled>
<svg class="icon"><use href="#i-check"/></svg>
<span>保存</span>
</button>
</div>
</div>
</div>
</div>
<!-- ====== 通用提示/输入弹窗 ====== -->
<div id="prompt-modal" class="modal hidden">
<div class="modal-card prompt-card">
<div class="modal-header">
<span class="prompt-icon" id="prompt-icon">
<svg class="icon"><use href="#i-info"/></svg>
</span>
<span id="prompt-title">提示</span>
</div>
<div class="modal-body">
<p id="prompt-message" class="prompt-message"></p>
<input id="prompt-input" type="text" class="prompt-input" maxlength="60" />
<div class="modal-actions">
<button id="prompt-cancel" class="text-btn">取消</button>
<button id="prompt-ok" class="primary-btn">确定</button>
</div>
</div>
</div>
</div>
<!-- ====== 上下文菜单 ====== -->
<div id="context-menu" class="context-menu hidden"></div>
<!-- ====== 设置面板 ====== -->
<div id="settings-panel" class="panel hidden">
<div class="panel-backdrop"></div>
<div class="panel-card settings-card">
<div class="panel-header">
<span class="panel-title">
<svg class="icon"><use href="#i-settings"/></svg>
设置
</span>
<button class="icon-btn" data-close>
<svg class="icon"><use href="#i-x"/></svg>
</button>
</div>
<div class="panel-body">
<div class="setting-group">
<div class="setting-group-title">通用</div>
<div class="setting-row">
<div class="setting-row-text">
<div class="setting-label">开机自启动</div>
<div class="setting-desc">登录 Windows 时自动启动(窗口默认隐藏在托盘)</div>
</div>
<label class="switch">
<input type="checkbox" id="auto-launch-toggle" />
<span class="switch-slider"></span>
</label>
</div>
</div>
<div class="setting-group">
<div class="setting-group-title">快捷键</div>
<div class="setting-row">
<div class="setting-row-text">
<div class="setting-label">全局唤出 / 收起</div>
<div class="setting-desc">在系统任意位置唤出或收起悬浮窗;与其它软件冲突时可改</div>
</div>
<div class="shortcut-display">
<kbd id="shortcut-display">Ctrl+Shift+V</kbd>
<button class="text-btn" id="btn-record-shortcut" title="录制新快捷键">录制</button>
<button class="text-btn" id="btn-reset-shortcut" title="恢复为 Ctrl+Shift+V">恢复默认</button>
</div>
</div>
</div>
<div class="setting-group">
<div class="setting-group-title">外观</div>
<div class="setting-row">
<div class="setting-row-text">
<div class="setting-label">主题</div>
<div class="setting-desc">切换明暗主题</div>
</div>
<div class="segmented" id="theme-segmented">
<button data-theme="dark" class="seg-btn active">
<svg class="icon"><use href="#i-moon"/></svg>
</button>
<button data-theme="light" class="seg-btn">
<svg class="icon"><use href="#i-sun"/></svg>
</button>
</div>
</div>
<div class="setting-row">
<div class="setting-row-text">
<div class="setting-label">窗口透明度</div>
<div class="setting-desc"><span id="opacity-value">100</span>%</div>
</div>
<input type="range" id="opacity-slider" min="60" max="100" step="5" value="100" class="slider" />
</div>
</div>
<div class="setting-group">
<div class="setting-group-title">粘贴</div>
<div class="setting-row">
<div class="setting-row-text">
<div class="setting-label">点击短语时</div>
<div class="setting-desc">单击短语的默认行为</div>
</div>
<div class="segmented" id="click-action-segmented">
<button data-action="paste" class="seg-btn active">直接粘贴</button>
<button data-action="copy" class="seg-btn">仅复制</button>
</div>
</div>
<div class="setting-row">
<div class="setting-row-text">
<div class="setting-label">粘贴时隐藏悬浮窗</div>
<div class="setting-desc">关闭后悬浮窗会立即重新显示</div>
</div>
<label class="switch">
<input type="checkbox" id="hide-on-paste-toggle" />
<span class="switch-slider"></span>
</label>
</div>
</div>
<div class="setting-group">
<div class="setting-group-title">最近使用</div>
<div class="setting-row">
<div class="setting-row-text">
<div class="setting-label">记录最近使用</div>
<div class="setting-desc">在「最近」分类中显示粘贴历史</div>
</div>
<label class="switch">
<input type="checkbox" id="recent-toggle" checked />
<span class="switch-slider"></span>
</label>
</div>
<div class="setting-row" id="clear-recent-row">
<div class="setting-row-text">
<div class="setting-label">清空最近记录</div>
<div class="setting-desc" id="recent-count-desc">还没有记录</div>
</div>
<button class="text-btn danger" id="btn-clear-recent">
<svg class="icon"><use href="#i-trash"/></svg>
清空
</button>
</div>
<div class="setting-row" id="clear-usecount-row">
<div class="setting-row-text">
<div class="setting-label">清空使用统计</div>
<div class="setting-desc" id="usecount-count-desc">还没有使用记录</div>
</div>
<button class="text-btn danger" id="btn-clear-usecount">
<svg class="icon"><use href="#i-trash"/></svg>
清空
</button>
</div>
</div>
<div class="setting-group">
<div class="setting-group-title">数据</div>
<div class="setting-row">
<div class="setting-row-text">
<div class="setting-label">数据文件路径</div>
<div class="setting-desc mono" id="data-path-value"></div>
</div>
<button class="text-btn" id="btn-reveal-file-2">
<svg class="icon"><use href="#i-folder"/></svg>
打开
</button>
</div>
<div class="setting-row">
<div class="setting-row-text">
<div class="setting-label">导入 / 导出</div>
<div class="setting-desc">备份或恢复你的短语库</div>
</div>
<div class="setting-actions">
<button class="text-btn" id="btn-export">
<svg class="icon"><use href="#i-arrow-down"/></svg>
导出
</button>
<button class="text-btn" id="btn-import">
<svg class="icon"><use href="#i-arrow-up"/></svg>
导入
</button>
</div>
</div>
</div>
<div class="setting-group">
<div class="setting-group-title">关于</div>
<div class="setting-row">
<div class="setting-row-text">
<div class="setting-label">开发者</div>
<div class="setting-desc">
关济寰 ·
<a href="https://www.guanjihuan.com/about" id="link-developer" class="setting-link">https://www.guanjihuan.com/about</a>
</div>
</div>
<button class="text-btn" id="btn-open-developer" title="在浏览器中打开">
<svg class="icon"><use href="#i-external"/></svg>
打开
</button>
</div>
</div>
</div>
</div>
</div>
<!-- ====== 隐藏的文件选择器(用于导入) ====== -->
<input type="file" id="import-file-input" accept="application/json" hidden />
<!-- ====== 提示 Toast 栈 ====== -->
<div id="toast-stack" class="toast-stack"></div>
<script src="renderer.js"></script>
</body>
</html>