-
Notifications
You must be signed in to change notification settings - Fork 0
Developer Console
gxxk-dev edited this page Mar 1, 2026
·
4 revisions
本文档详细介绍 StudyWithMiku 的开发者控制台
swm_dev,包括使用方法、内部实现原理和完整的 API 参考。
swm_dev 是 StudyWithMiku 的开发者调试工具,通过浏览器控制台暴露内部 API,方便开发者和高级用户进行调试、测试和数据管理。
- 调试便利 - 无需修改代码即可测试功能
- 数据管理 - 直接操作歌单、记录等数据
- 状态检查 - 查看应用内部状态
- 功能验证 - 验证新功能的正确性
src/dev/
├── index.js # 主入口,创建 swm_dev 对象
└── help/
├── index.js # 帮助系统核心
├── introspection.js # 运行时反射工具
└── formatter.js # 控制台格式化输出
在浏览器开发者工具(DevTools)或 Eruda 控制台中直接访问全局对象 swm_dev:
// 查看帮助
swm_dev.help()
// 查看特定模块
swm_dev.help('player')
// 或使用模块内置帮助
swm_dev.player.help()// 播放控制
await swm_dev.player.play()
await swm_dev.player.pause()
// 查看番茄钟状态
swm_dev.focus.state.value // 'idle' | 'running' | 'paused'
swm_dev.focus.remaining.value // 剩余秒数
// 显示通知
swm_dev.toast.show('success', '操作成功', '这是一条测试通知')
// 查看歌单列表
swm_dev.playlist.playlists.value
// 修改运行时配置
swm_dev.config.set('UI_CONFIG', 'TOAST_DEFAULT_DURATION', 5000)帮助系统由三个核心模块组成:
flowchart TB
subgraph HelpIndexJS["help/index.js<br/>(帮助系统核心)"]
createHelpSystem["createHelpSystem(): 创建帮助系统"]
preloadMetadata["preloadMetadata(): 预加载 JSDoc 元数据"]
injectModuleHelp["injectModuleHelp(): 为每个模块注入 help() 方法"]
end
subgraph IntrospectionJS["introspection.js<br/>(运行时反射)"]
introspect["introspect(obj)"]
extractSignature["extractSignature(fn)"]
isVueRef["isVueRef(value)"]
summarizeValue["summarizeValue(value)"]
end
subgraph FormatterJS["formatter.js<br/>(格式化输出)"]
printOverview["printOverview()"]
printModuleDetail["printModuleDetail()"]
ConsoleBuilder["ConsoleBuilder"]
STYLES["STYLES (颜色定义)"]
end
HelpIndexJS --> IntrospectionJS
HelpIndexJS --> FormatterJS
运行时分析对象结构,提取方法、属性和 Vue Ref 信息。
// src/dev/help/introspection.js
/**
* 检测值是否为 Vue Ref
*/
const isVueRef = (value) =>
value && typeof value === 'object' && '__v_isRef' in value
/**
* 从函数中提取参数签名
*/
const extractSignature = (fn) => {
const fnStr = fn.toString()
// 匹配函数参数列表
const match = fnStr.match(/^(?:async\s+)?(?:function\s*)?\w*\s*\(([^)]*)\)/)
if (match) {
return match[1].trim() || ''
}
// 箭头函数简写形式
const arrowMatch = fnStr.match(/^(?:async\s+)?(\w+)\s*=>/)
if (arrowMatch) {
return arrowMatch[1]
}
return '...'
}
/**
* 反射分析对象结构
* @returns {{ methods: [], properties: [], refs: [] }}
*/
export const introspect = (obj) => {
const info = { methods: [], properties: [], refs: [] }
for (const key of Object.keys(obj)) {
if (key.startsWith('_') || key === 'help') continue
const value = obj[key]
if (typeof value === 'function') {
info.methods.push({
name: key,
signature: extractSignature(value),
isAsync: value.constructor.name === 'AsyncFunction'
})
} else if (isVueRef(value)) {
info.refs.push({
name: key,
refType: typeof value.value,
currentValue: summarizeValue(value.value)
})
} else {
info.properties.push({
name: key,
type: typeof value,
currentValue: summarizeValue(value)
})
}
}
return info
}提供带颜色的控制台输出:
// src/dev/help/formatter.js
const STYLES = {
title: 'color: #39c5bb; font-weight: bold; font-size: 14px',
module: 'color: #4fc3f7; font-weight: bold',
method: 'color: #81c784',
param: 'color: #90a4ae',
type: 'color: #78909c; font-style: italic',
ref: 'color: #ba68c8',
// ...
}
// 颜色模式开关
let colorMode = false
export const setColorMode = (enabled) => {
colorMode = !!enabled
}
// ConsoleBuilder 类用于构建带样式的输出
class ConsoleBuilder {
constructor(useColor = colorMode) {
this.useColor = useColor
this.parts = []
this.styles = []
}
add(text, style = '') {
if (this.useColor && style) {
this.parts.push(`%c${text}`)
this.styles.push(style)
} else {
this.parts.push(text)
}
return this
}
print() {
if (this.useColor) {
console.log(this.parts.join(''), ...this.styles)
} else {
console.log(this.parts.join(''))
}
}
}构建时从源代码提取的 JSDoc 注释会生成 /help-metadata.json,帮助系统会预加载这些元数据:
// src/dev/help/index.js
let helpMetadata = null
let metadataLoading = null
function preloadMetadata() {
if (helpMetadata) return Promise.resolve(helpMetadata)
if (metadataLoading) return metadataLoading
metadataLoading = fetch('/help-metadata.json')
.then(response => response.json())
.then(data => {
helpMetadata = data
return data
})
.catch(e => {
console.warn('[help] 无法加载帮助元数据:', e.message)
helpMetadata = {}
return {}
})
return metadataLoading
}// 启用彩色输出
swm_dev.setColorMode(true)
// 关闭彩色输出
swm_dev.setColorMode(false)
// 查看当前模式
swm_dev.getColorMode()| 模块 | 对象路径 | 说明 |
|---|---|---|
player |
swm_dev.player |
播放器控制(play/pause/seek/volume) |
focus |
swm_dev.focus |
番茄钟状态和控制 |
playlist |
swm_dev.playlist |
歌单 CRUD 和管理 |
music |
swm_dev.music |
音乐源管理和缓存 |
toast |
swm_dev.toast |
通知系统 |
config |
swm_dev.config |
运行时配置修改 |
auth |
swm_dev.auth |
认证状态和操作 |
sync |
swm_dev.sync |
云端数据同步 |
authStorage |
swm_dev.authStorage |
Token/认证信息存储工具 |
hooks |
swm_dev.hooks |
钩子系统(Hook CRUD、Provider 管理) |
swm_dev.auth 暴露 useAuth() composable 的完整实例,用于调试认证流程。
swm_dev.auth.user // Ref<Object|null> 当前用户信息
swm_dev.auth.isAuthenticated // Ref<boolean> 是否已认证
swm_dev.auth.isLoading // Ref<boolean> 操作加载中
swm_dev.auth.error // Ref<Object|null> 错误信息 {code, message, type}
swm_dev.auth.devices // Ref<Array> 已注册的 WebAuthn 设备
swm_dev.auth.authMethods // Ref<Array> 所有认证方法 (WebAuthn + OAuth)
swm_dev.auth.availableProviders // Ref<Object> 可用的认证提供商// 登录与注册
await swm_dev.auth.login('username')
await swm_dev.auth.register('username', 'deviceName')
swm_dev.auth.loginWithOAuth('github')
// 注销
await swm_dev.auth.logout()
// 设备管理
await swm_dev.auth.getDevices()
await swm_dev.auth.addDevice('新设备')
await swm_dev.auth.removeDevice('credentialId')
// 认证方法管理
await swm_dev.auth.getAuthMethods()
await swm_dev.auth.linkOAuthProvider('google')
await swm_dev.auth.unlinkOAuthAccount('accountId')
// 用户资料
await swm_dev.auth.updateProfile({ displayName: '新名称', email: 'new@email.com' })
// Token 管理
await swm_dev.auth.refreshTokenIfNeeded()
// 配置
await swm_dev.auth.fetchConfig()
// 工具
swm_dev.auth.clearError()swm_dev.auth.isWebAuthnSupported // Computed<boolean> 浏览器是否支持 WebAuthn
swm_dev.auth.hasDevices // Computed<boolean> 是否有已注册设备// 检查当前认证状态
console.log('已认证:', swm_dev.auth.isAuthenticated.value)
console.log('用户:', swm_dev.auth.user.value)
// 查看所有认证方法
await swm_dev.auth.getAuthMethods()
console.log(swm_dev.auth.authMethods.value)
// 查看设备列表
await swm_dev.auth.getDevices()
swm_dev.auth.devices.value.forEach(d => console.log(d.deviceName, d.lastUsedAt))swm_dev.sync 暴露 useDataSync() composable 的完整实例,用于调试数据同步。
swm_dev.sync.syncStatus // Ref<Object> 各数据类型的同步状态
swm_dev.sync.isSyncing // Ref<boolean> 是否正在同步
swm_dev.sync.lastSyncTime // Ref<number> 最后同步时间戳
swm_dev.sync.pendingChanges // Ref<Array> 离线队列中的待处理变更
swm_dev.sync.error // Ref<Object|null> 同步错误// 全量同步
await swm_dev.sync.triggerSync()
// 单类型操作
await swm_dev.sync.uploadData('focus_records', recordsArray)
await swm_dev.sync.downloadData('focus_records')
// 队列管理
swm_dev.sync.queueChange('focus_settings', settingsObj)
await swm_dev.sync.processQueue()
swm_dev.sync.cancelPendingSync()
// 工具
swm_dev.sync.clearError()swm_dev.sync.hasPendingChanges // Computed<boolean> 是否有待处理变更
swm_dev.sync.isOnline // Computed<boolean> 网络是否在线
swm_dev.sync.canSync // Computed<boolean> 是否可以同步// 检查同步状态
console.log('正在同步:', swm_dev.sync.isSyncing.value)
console.log('待处理变更:', swm_dev.sync.pendingChanges.value.length)
console.log('可以同步:', swm_dev.sync.canSync.value)
// 手动触发全量同步
await swm_dev.sync.triggerSync()
// 查看各类型同步状态
Object.entries(swm_dev.sync.syncStatus.value).forEach(([type, status]) => {
console.log(type, '版本:', status.version, '已同步:', status.synced)
})swm_dev.authStorage 直接暴露 authStorage 工具模块,用于检查和操作 Token 存储。
// Token 操作
swm_dev.authStorage.getAccessToken() // 获取内存中的 Access Token
swm_dev.authStorage.getTokenType() // 获取 Token 类型 (默认 'Bearer')
swm_dev.authStorage.getTokenExpiresAt() // 获取过期时间戳
swm_dev.authStorage.isTokenExpiringSoon(60) // 是否即将过期 (阈值秒数)
swm_dev.authStorage.isTokenExpired() // 是否已过期
swm_dev.authStorage.getTokens() // 获取完整 Token 对象
swm_dev.authStorage.clearTokens() // 清除所有 Token
// 用户信息
swm_dev.authStorage.getUser() // 获取存储的用户信息
swm_dev.authStorage.saveUser(userObj) // 保存用户信息
swm_dev.authStorage.clearUser() // 清除用户信息
// 设备 ID
swm_dev.authStorage.getDeviceId() // 获取设备 ID
swm_dev.authStorage.saveDeviceId('id') // 保存设备 ID
swm_dev.authStorage.clearDeviceId() // 清除设备 ID
// 综合操作
swm_dev.authStorage.hasValidAuth() // 快速检查认证状态
swm_dev.authStorage.clearAllAuthData() // 清除所有认证数据
swm_dev.authStorage.cleanupLegacyKeys() // 清理旧版键名// 检查认证是否有效
console.log('认证有效:', swm_dev.authStorage.hasValidAuth())
console.log('Token 过期:', swm_dev.authStorage.isTokenExpired())
// 查看用户信息
console.log(swm_dev.authStorage.getUser())
// 调试 Token 状态
const expiresAt = swm_dev.authStorage.getTokenExpiresAt()
if (expiresAt) {
const remaining = (expiresAt - Date.now()) / 1000
console.log('Token 剩余时间:', remaining, '秒')
}
// 紧急清除所有认证数据
swm_dev.authStorage.clearAllAuthData()swm_dev.hooks 暴露 useHooks() composable 的完整实例,用于调试钩子系统。
swm_dev.hooks.hooks // Ref<Array> 所有钩子列表// CRUD 操作
swm_dev.hooks.addHook({
name: '测试钩子',
provider: 'notification',
trigger: 'focus_completed',
action: { title: '测试', body: '测试通知' }
}) // → 返回新钩子 ID
swm_dev.hooks.updateHook('hookId', { enabled: false })
swm_dev.hooks.removeHook('hookId') // built-in 不可删除
swm_dev.hooks.clearCustomHooks() // 清除所有非 built-in 钩子
// 预设
swm_dev.hooks.applyPreset(preset)
// 查询
swm_dev.hooks.getHooksByProvider('notification')
swm_dev.hooks.getHooksByProvider('estim')
// 云同步
swm_dev.hooks.getHooksData() // 获取用于云同步的数据
swm_dev.hooks.loadFromCloud(data) // 从云端加载电刺激 Provider 默认不可用,需要解锁:
// 解锁 estim provider
localStorage.setItem('swm_coyote_unlocked', 'true')
// 然后刷新页面,estim provider 会自动注册// 查看已注册的 providers
swm_dev.hooks.providerRegistry.getAll()
// 检查某个 provider 是否已注册
swm_dev.hooks.providerRegistry.has('estim') // false(默认未解锁)
swm_dev.hooks.providerRegistry.has('notification') // true// 查看所有钩子
swm_dev.hooks.hooks.value.forEach(h =>
console.log(h.name, h.provider, h.trigger, h.enabled ? '✓' : '✗')
)
// 添加一个测试通知钩子
const id = swm_dev.hooks.addHook({
name: '专注开始通知',
provider: 'notification',
trigger: 'focus_start',
action: { title: '开始专注', body: '加油!' }
})
// 临时禁用某个钩子
swm_dev.hooks.updateHook(id, { enabled: false })
// 查看通知类型的钩子
swm_dev.hooks.getHooksByProvider('notification')
// 查看已注册的 provider 列表
swm_dev.hooks.providerRegistry.getAll().map(p => p.id)
// → ['notification', 'sound', 'push'](未解锁 estim)