配置每次落盘都打顶层 patchbay: 标记(盐加版本的哈希)。内核忽略未知 顶层键(yaml.Unmarshal 没开严格模式,config/config.go:612)。刻意不对 内容做哈希:本项目只接管部分段落,手改其余段落是正常用法,内容哈希 会把合法手改判成无效。上传配置时靠标记分流:有标记的确认后替换, 没有的用更重的措辞确认导入;两者都走 config-store 完整保护链路 (新端点 POST /api/config/import)。自动备份列表从界面撤下,改成 下载当前配置 + 拖放上传。 内核参数一组编辑五个老式顶层端口和 keep-alive-interval(config.go:444, 整数秒),走带基准指纹的保存链路。资源一组接 POST /configs/geo (同步接口,透传给了 300 秒超时)。测速地址成为本地偏好,延迟测试 读它;prefs 模块顶层的浏览器全局加了存在性守卫——api.ts 开始引用 prefs,而 bun test 环境没有 localStorage。 退出登录挪到侧栏底部;密度设置从设置页撤下(侧栏按钮还在); 免责声明和关于做成子页(左上角返回),登录页输入框下加同一份 免责声明的弹层入口。
67 lines
2.3 KiB
TypeScript
67 lines
2.3 KiB
TypeScript
/**
|
|
* 界面偏好,存在 localStorage 里。
|
|
*
|
|
* 密度:工具栏和导航是只显示图标,还是图标加文字。
|
|
* 默认是「完整」——这个工具是隔一阵子才动一次的,纯图标对偶尔来一次的人不友好;
|
|
* 想收拢的人用侧栏底部的按钮切。
|
|
*
|
|
* 主题:跟随系统、浅色、深色。默认跟随系统。
|
|
*
|
|
* localStorage 和 document 都加了存在性守卫:api.ts 引用这里(测速地址),
|
|
* 而 api.ts 被 bun test 环境里跑的模块间接引用——那里没有浏览器全局,
|
|
* 模块顶层直接碰它们会把整个测试文件炸掉。
|
|
*/
|
|
import { ref, watch } from 'vue'
|
|
|
|
const storage = typeof localStorage === 'undefined' ? null : localStorage
|
|
|
|
export type Density = 'full' | 'compact'
|
|
|
|
const KEY = 'patchbay.density'
|
|
|
|
const stored = storage?.getItem(KEY)
|
|
export const density = ref<Density>(stored === 'compact' ? 'compact' : 'full')
|
|
|
|
watch(density, (value) => {
|
|
storage?.setItem(KEY, value)
|
|
})
|
|
|
|
export function toggleDensity(): void {
|
|
density.value = density.value === 'full' ? 'compact' : 'full'
|
|
}
|
|
|
|
/** 测延迟用的目标地址。204 空响应的端点最合适,默认值全球都有部署。 */
|
|
export const DEFAULT_TEST_URL = 'https://www.gstatic.com/generate_204'
|
|
|
|
const TEST_URL_KEY = 'patchbay.test-url'
|
|
|
|
export const testUrl = ref(storage?.getItem(TEST_URL_KEY) || DEFAULT_TEST_URL)
|
|
|
|
watch(testUrl, (value) => {
|
|
if (value.trim() === '' || value === DEFAULT_TEST_URL) storage?.removeItem(TEST_URL_KEY)
|
|
else storage?.setItem(TEST_URL_KEY, value.trim())
|
|
})
|
|
|
|
export type Theme = 'system' | 'light' | 'dark'
|
|
|
|
const THEME_KEY = 'patchbay.theme'
|
|
|
|
const storedTheme = storage?.getItem(THEME_KEY)
|
|
export const theme = ref<Theme>(
|
|
storedTheme === 'light' || storedTheme === 'dark' ? storedTheme : 'system',
|
|
)
|
|
|
|
// 显式选了浅色或深色就在 <html> 上标出来:tokens.css 里 :root[data-theme=…] 的权重
|
|
// 高于 :root 和媒体查询里的 :root,压过系统偏好。跟随系统则不加标记,交回给媒体查询,
|
|
// 这样系统主题切换时界面仍然跟着变。
|
|
watch(
|
|
theme,
|
|
(value) => {
|
|
storage?.setItem(THEME_KEY, value)
|
|
if (typeof document === 'undefined') return
|
|
if (value === 'system') document.documentElement.removeAttribute('data-theme')
|
|
else document.documentElement.setAttribute('data-theme', value)
|
|
},
|
|
{ immediate: true },
|
|
)
|