dsh-bottom-info-bar 1.14.2 → 1.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +752 -202
- package/lib/host-locale.js +29 -2
- package/lib/index.js +121 -5
- package/lib/locales.js +50 -26
- package/package.json +2 -2
package/lib/client.js
CHANGED
|
@@ -29,8 +29,37 @@ try {
|
|
|
29
29
|
// 必须定义在这里(FIELD_REGISTRY 锚点之前):D1 结构测试会切片求值锚点之后到 module.exports 之间的源码,
|
|
30
30
|
// 那段切片里引用不到 BIB_SET_PRIMITIVES,多一条依赖就会让求值抛错。
|
|
31
31
|
const CONTEXT_TOOLTIP = typeof BIB_SET_PRIMITIVES.Tooltip === 'function' ? BIB_SET_PRIMITIVES.Tooltip : null;
|
|
32
|
+
// 设置页卡片头部的折叠箭头同样取自 primitives,但它的导出名跟着宿主版本变过:
|
|
33
|
+
// DSH ≤0.1.6:IconChevronDownOutline14(尺寸写进名字)
|
|
34
|
+
// DSH ≥0.1.7:IconChevronDownOutlineRegular / IconChevronDownOutlineMedium(改成描边档位)
|
|
35
|
+
// 两边各只有自己那一套名字,所以运行时择优;都取不到时退回 CSS 画的箭头。
|
|
36
|
+
// 绝不能把 undefined 直接交给 React.createElement:那会让整个插件页配置区块抛
|
|
37
|
+
// React #130 而整块白屏——2026-09-22 升级到 0.1.7-alpha.1 后正是这么踩的(信息栏
|
|
38
|
+
// 本体照常显示,只有插件页的「信息栏设置」整块空白且控制台报 slot entry crashed)。
|
|
39
|
+
// 同样必须定义在 FIELD_REGISTRY 锚点之前:锚点之后的切片求值看不到 BIB_SET_PRIMITIVES。
|
|
40
|
+
const BIB_SET_CHEVRON_ICON = BIB_SET_PRIMITIVES.IconChevronDownOutlineRegular
|
|
41
|
+
|| BIB_SET_PRIMITIVES.IconChevronDownOutlineMedium
|
|
42
|
+
|| BIB_SET_PRIMITIVES.IconChevronDownOutline14
|
|
43
|
+
|| null;
|
|
44
|
+
// 设置面板一律优先渲染宿主原生组件(primitives),而不是自己画一套长得像的:
|
|
45
|
+
// 按钮/开关/标签/状态点/输入框/下拉菜单的形状、配色、动效都由宿主维护,宿主换皮时插件自动跟随。
|
|
46
|
+
// 每个成员都做存在性判断,取不到时退回插件内的等价实现——绝不能把 undefined 交给
|
|
47
|
+
// React.createElement(会产生 React #130 并让整个配置区块白屏,见上面折叠箭头的教训)。
|
|
48
|
+
function bibSetNative(name) {
|
|
49
|
+
const member = BIB_SET_PRIMITIVES[name];
|
|
50
|
+
return typeof member === 'function' || (member && typeof member === 'object') ? member : null;
|
|
51
|
+
}
|
|
52
|
+
const BIB_SET_NATIVE_BUTTON = bibSetNative('Button');
|
|
53
|
+
const BIB_SET_NATIVE_SWITCH = bibSetNative('Switch');
|
|
54
|
+
const BIB_SET_NATIVE_TAG = bibSetNative('Tag');
|
|
55
|
+
const BIB_SET_NATIVE_STATE_DOT = bibSetNative('StateDot');
|
|
56
|
+
const BIB_SET_NATIVE_INPUT = bibSetNative('Input');
|
|
57
|
+
const BIB_SET_NATIVE_MENU = bibSetNative('Menu');
|
|
58
|
+
// 决策 2/4:分组折叠用原生 DisclosureRow,重置二次确认用原生 RiskConfirmation。
|
|
59
|
+
// 与上面同一条铁律:取不到时退回插件内等价实现,绝不把 undefined 交给 React.createElement。
|
|
60
|
+
const BIB_SET_NATIVE_RISK_CONFIRM = bibSetNative('RiskConfirmation');
|
|
32
61
|
const LOCALE_NAMESPACE = 'dsh-bottom-info-bar';
|
|
33
|
-
const LOCALES = {"zh":{"ui.requestTimedOut":"请求超时","ui.requestCanceled":"请求已取消","ui.couldNotParseResponse":"响应解析失败","ui.rpcFailed":"RPC 失败","ui.refreshFailed":"刷新失败","color.red":"红","color.green":"绿","color.blue":"蓝","color.purple":"紫","color.orange":"橙","color.neutral":"中性","mode.balance":"余额制","mode.subscription":"订阅制","mode.billing":"账单制","mode.native":"原生统计行","mode.common":"通用","ui.nativeStatsField":"原生统计行字段","ui.shownIn":"出现于","ui.pleaseTryAgainLater":"请稍后再试","ui.restoreDefaultColor":"恢复默认颜色","ui.infoBarSettings":"信息栏","ui.settingsAreTemporarilyUnavailable":"暂时无法读取设置","ui.loadingInfoBarSettings":"正在加载设置…","ui.couldNotLoadInfoBar":"无法读取信息栏设置:","ui.changesAppliedButCouldNot":"已应用,但无法保存到本地:","ui.unknownReason":"未知原因","ui.couldNotSave":"{errorPrefix}保存失败:{value}","ui.color":"“{value}”颜色:","ui.hasAnInvalidColorEnter":"“{value}”的颜色格式不正确,请输入 #RRGGBB(例如 #0044CC)。","ui.defaultColorsRestored":"已恢复默认颜色","ui.defaultLabelsRestored":"已恢复默认显示设置","ui.couldNotReset":"重置失败:{value}","ui.identifiesTheProviderAndModel":"显示当前会话使用的服务商和模型。","ui.keepEnabledToSeeThese":"建议保留,方便及时看到异常。","ui.recommended":"建议保留","ui.show":"显示{label}","ui.clickToHide":"点击隐藏","ui.clickToShow":"点击显示","ui.presetColor":"{label}的预设颜色","ui.customColor":"{label}的自定义颜色","ui.customColorOpenColorPicker":"自定义颜色(打开系统取色器)","ui.hexColor":"{label}的十六进制颜色","ui.saving":"正在保存…","ui.processing":"正在处理…","ui.chooseWhichFieldsToShow":"选择信息栏中显示的内容。更改会自动保存。","ui.visibleFields":"显示内容","ui.hiddenFieldsTakeNoSpace":"关闭后不再显示,也不会占用空间。异常和账单提醒建议保留。","ui.resetLabels":"恢复默认显示","ui.resetColors":"恢复默认颜色","ui.couldNotDisplayInfoBar":"无法显示信息栏设置:{value}","ui.dataAndBilling":"账单数据","ui.dataAndBillingDesc":"导出或清除本插件保存的账单记录。不会影响设置或登录信息。","ui.exportBillingRecords":"导出账单","ui.exportBillingRecordsDesc":"保存为 CSV 或 JSON,包含时间、模型、用量和金额。","ui.exportBillingCsv":"导出 CSV","ui.exportBillingJson":"导出 JSON","ui.clearBillingRecords":"清除账单","ui.clearBillingRecordsDesc":"删除本插件保存的账单记录。建议先导出;清除后无法恢复。","ui.clearBillingRecordsConfirm":"确定清除本插件保存的全部账单记录吗?建议先导出。清除后无法恢复,但不会影响设置或登录信息。","ui.exportedBillingRecords":"已导出 {count} 条账单记录({format})","ui.noBillingRecordsToExport":"目前没有可导出的账单记录。","ui.exportFailed":"导出失败:{value}","ui.exportIncomplete":"历史账单读取不完整:{value}。本次未导出,请稍后再试。","ui.clearFailed":"清除失败:{value}","ui.clearFailedWithoutDetails":"账单记录未能全部清除","ui.clearedBillingRecords":"已清除 {count} 条账单记录","ui.clearCanceled":"已取消","ui.exportNotSupported":"当前环境不支持下载文件。","ui.infoBar":"信息栏","ui.bundleConfigSummary":"配置显示哪些信息、各自的配色,以及信息密度","ui.weekly":"周","ui.monthly":"月","ui.window":"窗口","ui.supportsImageInput":"支持图像输入。","ui.vision":"视觉","ui.unknown":"未知","ui.unknownModel":"未知模型","ui.modelSelectionPending":"正在读取当前模型","ui.modelCapabilityPending":"正在确认是否支持图像输入","ui.pluginVersion":"\n插件版本:{current}","ui.provider":"服务商:{provLabel} {modelLabel}\n","ui.pricingPeakOffPeakBeijing":"定价:峰谷价(工作日高峰 9-12、14-18 点;周末全天空闲)","ui.pricingFixed":"定价:固定价","ui.pricingNotListedUsingDefaults":"定价:暂未收录,不计算花费","ui.zhipu":"智谱","ui.xiaomiMiMo":"小米 MiMo","ui.commandCode":"Command Code","ui.subscription":"订阅","ui.cloudBilling":"云账单","ui.plan":"\n套餐:{plan}","ui.expiresLocalTime":"\n到期:{value}(本地时区)","ui.subscriptionServiceModel":"订阅服务:{serviceName}\n模型:{rawModelLabel}{planLine}{expiryLine}{versionLine}","ui.balanceLookupIsNotYet":"暂时无法读取该服务商的余额。","ui.notSupported":"未适配","ui.notConfigured":"未配置 ","ui.notConfiguredConfigureItIn":"未配置 {credName}。请在\"设置 → 模型\"中填写。","ui.notConfiguredSettingsModels":"未配置 {credName}。请在设置 → 模型中填写。","ui.estimatedBalance":"估算余额:{symbol}{value}","ui.balance":"余额:{symbol}{value}","ui.balance.pushBalanceGroups":"余额","ui.low":"低","ui.estimated":"(估算)","ui.balanceIsTemporarilyUnavailableShowing":"余额暂不可用;正在显示上次数据并自动重试。","ui.couldNotLoadBalanceCheck":"余额获取失败。请检查网络和 API Key。","ui.balanceUnavailable":"余额获取失败","ui.beijingTime":"北京时间 ","ui.peakPrice":"高峰价","ui.offPeakPrice":"空闲价","ui.input":":输入 ¥","ui.mCachedInput":"/M · 缓存 ¥","ui.mOutput":"/M · 输出 ¥","ui.beijingTimeSwitchesTo":"北京时间 {atLabel} 切换为","ui.until":"距","ui.offPeak":"空闲","ui.peak":"高峰","ui.today":"今天 {symbol}{value}","ui.lastDays":"近一月 {symbol}{value}","ui.allTime":"全部 {symbol}{value}","ui.sessionIncludingSubagents":"本会话 {costTxt}(含子代理){value}","ui.session":"本会话","ui.spendIsTemporarilyUnavailableChat":"花费暂不可用;不会影响对话。","ui.spendUnavailable":"花费获取失败","ui.noSignInCredentialsFound":"未找到 {serviceName} 登录凭证。请重新授权。","ui.credentialsHaveExpiredPleaseReauthorize":"{serviceName} 登录凭证已失效。请重新授权。","ui.deniedAccessReauthorizeOrTry":"{serviceName} 拒绝访问。请重新授权或稍后再试。","ui.rateLimitReachedPleaseTry":"{serviceName} 请求过于频繁。请稍后再试。","ui.timedOutCheckYourConnection":"{serviceName} 响应超时。请检查网络后再试。","ui.returnedAnUnrecognizedResponsePlease":"{serviceName} 返回的数据暂时无法识别。请稍后再试。","ui.isTemporarilyUnavailableCheckYour":"{serviceName} 暂不可用。请检查网络后再试。","ui.subscriptionExpiresLocalTime":"订阅到期:{value}(本地时区)","ui.expires":"到期","ui.subscriptionSource":"订阅源:{value}(","ui.prepaidBalance":"充值余额","ui.availableBalance":"可用余额:{balTxt}","ui.availableCredits":"剩余 credits:{value}","ui.credits":"credits","ui.subscriptionSource.titleLines":"订阅源:{value}","ui.windowRemainingUsed":"{label}窗口:剩余 {value}%(已用 {usedPercent}%)","ui.resetsResetsIn":" · 重置 {value} · 距重置 {value2}","ui.windowRemainingUsedResets":"{label}窗口 剩余 {value}%(已用 {usedPercent}%) · 重置 {value4}","ui.resetsIn":"距重置","ui.configureItInSettingsModels":"。请在\"设置 → 模型\"中填写。","ui.deniedAccessTheTokenMay":"{serviceName} 拒绝访问:Token 可能缺少账单读取权限。","ui.billingIsTemporarilyUnavailableCheck":"{serviceName} 账单暂不可用。请检查网络与权限后再试。","ui.billingSource":"账单源:{value}","ui.thisMonthSSpend":"本月花费:{symbol}{value}","ui.budgetUsed":"预算使用:{value}%","ui.dailyFreeQuotaRemaining":"每日免费额度剩余:{value}","ui.thisMonth":"本月","ui.thisMonthSUsage":"本月用量","ui.budget":"预算","ui.free":"免费","ui.resetsIn.pushBillingGroups":"{value} · 距重置 {value2}","ui.billingServiceModel":"账单服务:{serviceName}\n模型:{modelLabel}{versionLine}","ui.pricing":"定价","ui.spend":"花费","ui.mode":"模式","ui.subscriptionQuota":"订阅额度","ui.billing":"账单","ui.temporarilyUnavailableKeepingTheLast":"暂不可用;正在保留上次数据并自动重试。","ui.spendJournalSavedButThe":"账单流水已保存,但可直接查看的账单文件暂未更新:","ui.thisSpendRecordWasNot":"本次账单未保存,不会计入金额:","ui.ledgerUpdatePending":"账单待整理","ui.spendNotSaved":"账单未保存","ui.askYourAgentToUpdate":"请告知你的 Agent 将本插件更新到“{latest}”版本。\n或者点击本标签复制更新命令,到终端粘贴执行,重启 DSH 后生效。","ui.updateAvailable":"新版本提醒","ui.updateCommandCopied":"更新命令已复制","ui.tools":"工具调用","ui.avgTTFT":"首 token 平均","ui.cacheHit":"缓存命中","ui.input.BottomInfoBar":"输入","ui.output":"输出","ui.savingView":"正在保存切换…","ui.clickToSwitchFullCompact":"单击切换 完整/简洁","ui.pressEnterOrSpaceFor":"按 Enter 或空格切换为简洁模式。","ui.pressEnterOrSpaceFor.BottomInfoBar":"按 Enter 或空格切换为完整模式。","host.hour":"5 小时","host.unknownProvider":"未知服务商","host.unexpectedSubscriptionQuotaRequestFailure":"订阅额度请求未知异常","host.settingsFileCouldNotBe":"设置文件写入未完成","host.everydayQuestions":"日常问答","host.mediumCodingTask":"中等编码任务","host.longDocumentAnalysisCodeReview":"长文档分析/代码审查","host.largeProjectRefactorAcrossMultiple":"大工程重构(多轮)","host.subagentWorkflow":"子代理工作流","host.couldNotSaveSettingsJson":"settings.json 落盘失败:{value}","host.couldNotReadCredentials":"凭据读取失败","host.notConfigured":"未配置 {credential}","host.requestFailedHTTP":"请求失败(HTTP {status})","host.unexpectedResponseFormat":"响应格式异常","host.chatgptSubscriptionIsNotConnected":"未找到 ChatGPT 订阅登录凭证(~/.codex/auth.json),请安装 dsh-chatgpt-subscription 插件绑定","host.chatgptSubscriptionCredentialsAreMissing":"ChatGPT 订阅登录凭证缺少 id_token,请安装 dsh-chatgpt-subscription 插件重新绑定","host.opencodeGoIsNotConfigured":"未配置 OpenCode Go(OPENCODE_GO_API_KEY 或 opencode auth.json)","host.commandCodeIsNotConfigured":"未配置 Command Code(COMMAND_CODE_API_KEY、CMD_API_KEY 或 ~/.commandcode/auth.json)","host.commandCodeAuthenticationFailed":"Command Code 登录凭证已失效。请重新登录或更新 API Key","host.commandCodeQuotaUnrecognized":"Command Code 返回了无法识别的额度格式(接口可能已变更),已保留上次数据","host.zhipu":"智谱 {mapped}","host.zhipu.parseZaiQuota":"智谱 {value}{value2}","host.zhipuAPIKeyIsNot":"未配置智谱 API Key(ZAI_API_KEY 或 ZAI_CODING_CN_API_KEY)","host.zhipuAPIAuthenticationFailedThe":"智谱 API 认证失败(密钥过期或不正确)","host.zhipuQuotaWindowsUnrecognized":"智谱返回了无法识别的额度格式(接口可能已变更),已保留上次数据","host.requestFailed":"请求失败({value} {msg})","host.xiaomiMiMoTokenPlanCredentials":"未配置小米 MiMo Token Plan 凭据({credName} 或 XIAOMI_API_KEY)","host.requestFailedHTTP.fetchXiaomiTokenPlanUsage":"请求失败(HTTP {value})","host.notConfiguredTOGETHERAPIKEY":"未配置 TOGETHER_API_KEY","host.actualMonthlyBillFromThe":"本月真实账单(Together 官方 Usage API)","host.notConfiguredFIREWORKSAPIKEY":"未配置 FIREWORKS_API_KEY","host.couldNotReadAccountAccount":"账户解析失败(未取得 account_id)","host.actualBillForThisPeriod":"本周期真实账单(Fireworks Billing Summary)","host.actualUsageForThisPeriod":"本周期真实用量(billingUsage 回退端点,无金额)","host.notConfiguredAWSCredentialsAWS":"未配置 AWS 凭据(AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY)","host.requestFailedHTTPTheToken":"请求失败(HTTP {status})——可能缺少 ce:GetCostAndUsage 权限","host.actualMonthlyBillFromAWS":"AWS Cost Explorer 本月真实账单(账单延迟约 24 小时)","host.notConfiguredCLOUDFLAREAPIKEY":"未配置 CLOUDFLARE_API_KEY(需账号级 Token 并授予 Billing 读权限)","host.notConfiguredCLOUDFLAREACCOUNTID":"未配置 CLOUDFLARE_ACCOUNT_ID","host.requestFailedHTTPTheToken.fetchCloudflareBilling":"请求失败(HTTP {status})——Token 需 Billing 读权限","host.actualMonthlyUsageFromThe":"Cloudflare Billable Usage API(Alpha),本月真实用量","host.unexpectedBillingRequestFailure":"账单请求未知异常","host.spendSummaryFileMissingArchived":"账单汇总文件缺失:折叠期间的历史金额暂未计入显示(明细仍在冷归档 usage-archive/,可人工恢复)","host.spendLedgerCouldNotBe":"账单文件写入未完成","host.usageLedgerBusy":"当前仍有回答正在进行,完成后再清除账单记录","host.couldNotClearSpendRecords":"账单记录清理未完成:{value}","host.couldNotSaveArchivedSpend":"折叠汇总落盘失败:{value}","host.basedOnYourLastSessions":"基于你最近 {count} 次会话","host.estimatedFromSpendingOverThe":"基于过去 {SPEND_DAYS} 天消耗速度的估算","host.yourTypicalSession":"你的典型会话","host.patchMustIncludeFieldsOr":"patch 需包含 fields、colors、timeFormat、timeZones 或 customText 之一","host.timeFormatMustBeAnObject":"timeFormat 必须是对象","host.timeFieldMustBeABoolean":"时间格式字段必须是布尔值: {key}","host.timeZonesMustBeAnObject":"timeZones 必须是对象","host.timeZoneMustBeAValid":"时区必须是有效的 IANA 时区: {key}","host.customTextMustBeAString":"自定义文本必须是字符串","host.customTextTooLong":"自定义文本不能超过64字符","host.fieldsMustBeAnObject":"fields 必须是对象","host.unknownFieldId":"未知字段 id: {key}","host.fieldVisibilityMustBeA":"字段开关必须是布尔值: {key}","host.colorsMustBeAnObject":"colors 必须是对象","host.colorMustBeAPreset":"颜色只接受预设色名或 #RRGGBB: {key}","field.anchorGroup.label":"服务商与模型","field.anchorGroup.note":"显示当前会话使用的服务商和模型。","field.subServiceGroup.label":"订阅服务与模型","field.subServiceGroup.note":"显示订阅服务和当前模型或套餐。","field.billingServiceGroup.label":"账单服务与模型","field.billingServiceGroup.note":"显示账单服务和当前模型。","field.customText.label":"自定义文字","field.customText.note":"显示一段自定义文字,可用作备注或签名。","field.mainTime.label":"主时间","field.mainTime.note":"显示主时区的时间,可在下方调整时区和格式。","field.worldTime.label":"世界时间","field.worldTime.note":"显示另一时区的时间;格式与主时间相同。","field.sessionCost.label":"本次会话花费","field.sessionCost.note":"显示本次会话的实际花费,包含子代理。","field.balance.note":"显示账户余额;余额过低时会标红。","field.period.label":"当前时段","field.period.note":"显示当前是高峰还是空闲时段。","field.countdown.label":"下次价格切换","field.countdown.note":"显示距离下次价格切换还有多久。","field.expiry.label":"订阅到期日","field.expiry.note":"服务商提供到期时间时显示。","field.subWindow5h.label":"5 小时额度","field.subWindow5h.note":"显示滚动 5 小时额度的剩余比例。","field.subWindowWeek.label":"每周额度","field.subWindowWeek.note":"显示每周额度的剩余比例。","field.subWindowMonth.label":"每月额度","field.subWindowMonth.note":"显示每月额度的剩余比例。","field.resetCountdown.label":"距额度重置","field.resetCountdown.note":"显示当前额度何时重置。","field.subBalance.note":"充值账户显示余额;credits 型订阅在没有可显示窗口时显示剩余额度。","field.billingSpend.label":"本月用量","field.billingSpend.note":"显示本计费周期的实际用量或花费。","field.budget.label":"预算使用情况","field.budget.note":"服务商支持时显示已使用的预算比例。","field.freeQuota.label":"免费额度","field.freeQuota.note":"服务商明确提供时显示免费额度和重置时间。","field.turnsSteps.label":"轮次与步数","field.turnsSteps.note":"显示对话的轮次和步数。","field.llmTime.label":"模型耗时","field.llmTime.note":"显示模型推理耗时。","field.toolTime.label":"工具耗时","field.toolTime.note":"显示工具调用耗时。","field.cacheHit.note":"显示提示词缓存命中率。","field.tokensIO.label":"输入/输出 Token","field.tokensIO.note":"显示本次会话的输入和输出用量。","field.contextUsage.label":"上下文用量","field.contextUsage.note":"DeepSeek 原生的上下文占用圆环,始终显示在简洁模式这一行右端;点击查看构成明细。","ui.contextAria":"上下文已用 {percent}","ui.contextUsed":"上下文已用","ui.contextSystem":"系统提示词","ui.contextTools":"工具定义","ui.contextMessages":"对话消息","ui.contextFigures":"~{used} / {window}","field.unmapped.label":"暂不支持提示","field.unmapped.note":"当前服务商暂不支持余额查询时显示。","field.noKeyHint.label":"缺少密钥提示","field.noKeyHint.note":"缺少 API Key 时显示设置提示。","field.balanceError.label":"余额更新失败提示","field.balanceError.note":"余额更新失败时显示。","field.usageError.label":"用量更新失败提示","field.usageError.note":"花费暂时不可用时显示。","field.refreshFailure.label":"更新失败提示","field.refreshFailure.note":"任一数据更新失败时显示提醒。","field.persistWarning.label":"记录未保存提醒","field.persistWarning.note":"账单保存失败时显示提醒,建议保留。","field.updateNotice.note":"有新版本时显示提醒。","group.native":"原生信息","group.plugin":"信息栏内容","ui.sentenceSeparator":"","ui.listSeparator":"、","ui.sentenceEnd":"。","ui.fieldErrorPrefix":"「{label}」:","ui.turnCount":"{count} 轮","ui.turnCountPlural":"{count} 轮","ui.stepCount":"{count} 步","ui.stepCountPlural":"{count} 步","ui.mainTimeZone":"主时间时区","ui.worldTimeZone":"世界时间时区","ui.timeFormat":"时间格式","ui.year":"年","ui.month":"月","ui.day":"日","ui.hour":"时","ui.minute":"分","ui.second":"秒","ui.customTextTitle":"自定义文本","ui.customTextPlaceholder":"输入文字(最多 64 个字符)","ui.searchPlaceholder":"搜索内容…","ui.searchFieldsLabel":"搜索显示内容","ui.searchResultCount":"找到 {count} 项","ui.enabledFieldsCount":"已启用 {count} 项","ui.noSearchResults":"没有找到匹配内容","ui.collapsedDesc":"点击查看并调整显示内容。","ui.timePreview":"预览:","ui.mainTime":"主时间","ui.worldTime":"世界时间","ui.customText":"自定义文本"},"en":{"ui.requestTimedOut":"Request timed out","ui.requestCanceled":"Request canceled","ui.couldNotParseResponse":"Could not parse response","ui.rpcFailed":"RPC failed","ui.refreshFailed":"Refresh failed","color.red":"Red","color.green":"Green","color.blue":"Blue","color.purple":"Purple","color.orange":"Orange","color.neutral":"Neutral","mode.balance":"Balance","mode.subscription":"Subscription","mode.billing":"Billing","mode.native":"Native stats","mode.common":"General","ui.nativeStatsField":"Native stats field","ui.shownIn":"Shown in: ","ui.pleaseTryAgainLater":"Please try again later","ui.restoreDefaultColor":"Restore default color","ui.infoBarSettings":"Info Bar","ui.settingsAreTemporarilyUnavailable":"Settings are temporarily unavailable","ui.loadingInfoBarSettings":"Loading settings…","ui.couldNotLoadInfoBar":"Could not load Info Bar settings: ","ui.changesAppliedButCouldNot":"Applied, but could not save locally: ","ui.unknownReason":"Unknown reason","ui.couldNotSave":"{errorPrefix}Could not save: {value}","ui.color":"\"{value}\" color: ","ui.hasAnInvalidColorEnter":"\"{value}\" has an invalid color. Enter #RRGGBB (for example, #0044CC).","ui.defaultColorsRestored":"Default colors restored","ui.defaultLabelsRestored":"Default display restored","ui.couldNotReset":"Could not reset: {value}","ui.identifiesTheProviderAndModel":"Shows the provider and model used in this conversation.","ui.keepEnabledToSeeThese":"Recommended so you can spot issues quickly.","ui.recommended":"Recommended","ui.show":"Show {label}","ui.clickToHide":"Click to hide","ui.clickToShow":"Click to show","ui.presetColor":"{label} preset color","ui.customColor":"{label} custom color","ui.customColorOpenColorPicker":"Custom color (open color picker)","ui.hexColor":"{label} hex color","ui.saving":"Saving…","ui.processing":"Working…","ui.chooseWhichFieldsToShow":"Choose what appears in the Info Bar. Changes save automatically.","ui.visibleFields":"Visible content","ui.hiddenFieldsTakeNoSpace":"Hidden items disappear without leaving space. Keep issue and billing alerts enabled when possible.","ui.resetLabels":"Restore default display","ui.resetColors":"Restore default colors","ui.couldNotDisplayInfoBar":"Could not display Info Bar settings: {value}","ui.dataAndBilling":"Billing data","ui.dataAndBillingDesc":"Export or clear billing records saved by this plugin. Settings and sign-in information are not affected.","ui.exportBillingRecords":"Export billing","ui.exportBillingRecordsDesc":"Save a CSV or JSON file with timestamps, models, usage and amounts.","ui.exportBillingCsv":"Export CSV","ui.exportBillingJson":"Export JSON","ui.clearBillingRecords":"Clear billing","ui.clearBillingRecordsDesc":"Deletes billing records saved by this plugin. Export first; this cannot be undone.","ui.clearBillingRecordsConfirm":"Clear all billing records saved by this plugin? Export first. This cannot be undone, but settings and sign-in information will stay unchanged.","ui.exportedBillingRecords":"Exported {count} billing records ({format})","ui.noBillingRecordsToExport":"There are no billing records to export.","ui.exportFailed":"Export failed: {value}","ui.exportIncomplete":"Some historical billing records could not be read: {value}. Nothing was exported; try again later.","ui.clearFailed":"Could not clear billing records: {value}","ui.clearFailedWithoutDetails":"Billing records could not be fully cleared","ui.clearedBillingRecords":"Cleared {count} billing records","ui.clearCanceled":"Canceled","ui.exportNotSupported":"File downloads are not supported in this environment.","ui.infoBar":"Info Bar","ui.bundleConfigSummary":"Choose which fields to show, their colors, and the info density","ui.weekly":"Weekly","ui.monthly":"Monthly","ui.window":"Window","ui.supportsImageInput":"Supports image input.","ui.vision":"Vision","ui.unknown":"Unknown","ui.unknownModel":"Unknown model","ui.modelSelectionPending":"Reading current model","ui.modelCapabilityPending":"Checking image-input support","ui.pluginVersion":"\nPlugin version: {current}","ui.provider":"Provider: {provLabel} {modelLabel}\n","ui.pricingPeakOffPeakBeijing":"Pricing: peak/off-peak (Beijing time; weekday peaks 09:00–12:00 and 14:00–18:00; weekends off-peak)","ui.pricingFixed":"Pricing: fixed","ui.pricingNotListedUsingDefaults":"Pricing: not listed; spend is not calculated","ui.zhipu":"Zhipu","ui.xiaomiMiMo":"Xiaomi MiMo","ui.commandCode":"Command Code","ui.subscription":"Subscription","ui.cloudBilling":"Cloud billing","ui.plan":"\nPlan: {plan}","ui.expiresLocalTime":"\nExpires: {value} (local time)","ui.subscriptionServiceModel":"Subscription service: {serviceName}\nModel: {rawModelLabel}{planLine}{expiryLine}{versionLine}","ui.balanceLookupIsNotYet":"This provider's balance is not available yet.","ui.notSupported":"Not supported","ui.notConfigured":"Not configured: ","ui.notConfiguredConfigureItIn":"Not configured: {credName}. Configure it in Settings → Models.","ui.notConfiguredSettingsModels":"{credName} is not configured. Add it in Settings → Models.","ui.estimatedBalance":"Estimated balance: {symbol}{value}","ui.balance":"Balance: {symbol}{value}","ui.balance.pushBalanceGroups":"Balance","ui.low":"Low","ui.estimated":"(estimated)","ui.balanceIsTemporarilyUnavailableShowing":"Balance is temporarily unavailable. Showing the last data and retrying automatically.","ui.couldNotLoadBalanceCheck":"Could not load balance. Check your connection and API key.","ui.balanceUnavailable":"Balance unavailable","ui.beijingTime":"Beijing time: ","ui.peakPrice":"Peak price","ui.offPeakPrice":"Off-peak price","ui.input":": input ¥","ui.mCachedInput":"/M · cached input ¥","ui.mOutput":"/M · output ¥","ui.beijingTimeSwitchesTo":"Beijing time: {atLabel} switches to ","ui.until":"Until ","ui.offPeak":"off-peak","ui.peak":"peak","ui.today":"Today {symbol}{value}","ui.lastDays":"Last 30 days {symbol}{value}","ui.allTime":"All time {symbol}{value}","ui.sessionIncludingSubagents":"Session {costTxt} (including subagents){value}","ui.session":"Session","ui.spendIsTemporarilyUnavailableChat":"Spend is temporarily unavailable. Chat is unaffected.","ui.spendUnavailable":"Spend unavailable","ui.noSignInCredentialsFound":"No sign-in credentials found for {serviceName}. Please reauthorize.","ui.credentialsHaveExpiredPleaseReauthorize":"{serviceName} credentials have expired. Please reauthorize.","ui.deniedAccessReauthorizeOrTry":"{serviceName} denied access. Reauthorize or try again later.","ui.rateLimitReachedPleaseTry":"{serviceName} rate limit reached. Please try again later.","ui.timedOutCheckYourConnection":"{serviceName} timed out. Check your connection and try again.","ui.returnedAnUnrecognizedResponsePlease":"{serviceName} returned an unrecognized response. Please try again later.","ui.isTemporarilyUnavailableCheckYour":"{serviceName} is temporarily unavailable. Check your connection and try again.","ui.subscriptionExpiresLocalTime":"Subscription expires: {value} (local time)","ui.expires":"Expires","ui.subscriptionSource":"Subscription source: {value} (","ui.prepaidBalance":"Prepaid balance","ui.availableBalance":"Available balance: {balTxt}","ui.availableCredits":"Credits remaining: {value}","ui.credits":"credits","ui.subscriptionSource.titleLines":"Subscription source: {value}","ui.windowRemainingUsed":"{label} window: remaining {value}% (used {usedPercent}%)","ui.resetsResetsIn":" · Resets {value} · Resets in {value2}","ui.windowRemainingUsedResets":"{label} window: remaining {value}% (used {usedPercent}%) · Resets {value4}","ui.resetsIn":"Resets in","ui.configureItInSettingsModels":". Configure it in Settings → Models.","ui.deniedAccessTheTokenMay":"{serviceName} denied access: the token may lack billing read permissions.","ui.billingIsTemporarilyUnavailableCheck":"{serviceName} billing is temporarily unavailable. Check your connection and permissions, then try again.","ui.billingSource":"Billing source: {value}","ui.thisMonthSSpend":"This month’s spend: {symbol}{value}","ui.budgetUsed":"Budget used: {value}%","ui.dailyFreeQuotaRemaining":"Daily free quota remaining: {value}","ui.thisMonth":"This month","ui.thisMonthSUsage":"This month’s usage","ui.budget":"Budget","ui.free":"Free","ui.resetsIn.pushBillingGroups":"{value} · Resets in {value2}","ui.billingServiceModel":"Billing service: {serviceName}\nModel: {modelLabel}{versionLine}","ui.pricing":"Pricing","ui.spend":"Spend","ui.mode":"Mode","ui.subscriptionQuota":"Subscription quota","ui.billing":"Billing","ui.temporarilyUnavailableKeepingTheLast":": temporarily unavailable. Keeping the last data and retrying automatically.","ui.spendJournalSavedButThe":"Spend journal saved, but the readable ledger has not been updated: ","ui.thisSpendRecordWasNot":"This spend record was not saved and will not be added to totals: ","ui.ledgerUpdatePending":"Ledger update pending","ui.spendNotSaved":"Spend not saved","ui.askYourAgentToUpdate":"Ask your agent to update this plugin to version {latest}.\nOr click this label to copy the update command, paste it in a terminal, then restart DSH.","ui.updateAvailable":"Update available","ui.updateCommandCopied":"Update command copied","ui.tools":"Tools","ui.avgTTFT":"Avg. TTFT","ui.cacheHit":"Cache hit","ui.input.BottomInfoBar":"Input","ui.output":"Output","ui.savingView":"Saving view…","ui.clickToSwitchFullCompact":"Click to switch full/compact view","ui.pressEnterOrSpaceFor":"Press Enter or Space for compact view.","ui.pressEnterOrSpaceFor.BottomInfoBar":"Press Enter or Space for full view.","host.hour":"5-hour","host.unknownProvider":"Unknown provider","host.unexpectedSubscriptionQuotaRequestFailure":"Unexpected subscription quota request failure","host.settingsFileCouldNotBe":"Settings file could not be saved","host.everydayQuestions":"Everyday questions","host.mediumCodingTask":"Medium coding task","host.longDocumentAnalysisCodeReview":"Long document analysis / code review","host.largeProjectRefactorAcrossMultiple":"Large project refactor across multiple turns","host.subagentWorkflow":"Subagent workflow","host.couldNotSaveSettingsJson":"Could not save settings.json: {value}","host.couldNotReadCredentials":"Could not read credentials","host.notConfigured":"Not configured: {credential}","host.requestFailedHTTP":"Request failed: HTTP {status}.","host.unexpectedResponseFormat":"Unexpected response format","host.chatgptSubscriptionIsNotConnected":"ChatGPT subscription is not connected: credentials not found in ~/.codex/auth.json. Install dsh-chatgpt-subscription and sign in.","host.chatgptSubscriptionCredentialsAreMissing":"ChatGPT subscription credentials are missing id_token. Install dsh-chatgpt-subscription and reauthorize.","host.opencodeGoIsNotConfigured":"OpenCode Go is not configured. Set OPENCODE_GO_API_KEY or use opencode auth.json.","host.commandCodeIsNotConfigured":"Command Code is not configured. Set COMMAND_CODE_API_KEY, CMD_API_KEY, or sign in with the Command Code CLI.","host.commandCodeAuthenticationFailed":"Command Code credentials were rejected. Sign in again or update the API key.","host.commandCodeQuotaUnrecognized":"Command Code returned an unrecognized quota format; the previous snapshot was kept.","host.zhipu":"Zhipu {mapped}","host.zhipu.parseZaiQuota":"Zhipu {value}{value2}","host.zhipuAPIKeyIsNot":"Zhipu API key is not configured. Set ZAI_API_KEY or ZAI_CODING_CN_API_KEY.","host.zhipuAPIAuthenticationFailedThe":"Zhipu API authentication failed: the key is expired or invalid.","host.zhipuQuotaWindowsUnrecognized":"Zhipu returned an unrecognized quota format (the API may have changed); the last known data is kept.","host.requestFailed":"Request failed: {value} {msg}.","host.xiaomiMiMoTokenPlanCredentials":"Xiaomi MiMo Token Plan credentials are not configured: {credName} or XIAOMI_API_KEY.","host.requestFailedHTTP.fetchXiaomiTokenPlanUsage":"Request failed: HTTP {value}.","host.notConfiguredTOGETHERAPIKEY":"Not configured: TOGETHER_API_KEY","host.actualMonthlyBillFromThe":"Actual monthly bill from the Together Usage API","host.notConfiguredFIREWORKSAPIKEY":"Not configured: FIREWORKS_API_KEY","host.couldNotReadAccountAccount":"Could not read account (account_id missing)","host.actualBillForThisPeriod":"Actual bill for this period (Fireworks Billing Summary)","host.actualUsageForThisPeriod":"Actual usage for this period (billingUsage fallback; no spend amount)","host.notConfiguredAWSCredentialsAWS":"Not configured: AWS credentials — AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY","host.requestFailedHTTPTheToken":"Request failed: HTTP {status}. The token may lack ce:GetCostAndUsage permission","host.actualMonthlyBillFromAWS":"Actual monthly bill from AWS Cost Explorer — about 24 hours behind","host.notConfiguredCLOUDFLAREAPIKEY":"Not configured: CLOUDFLARE_API_KEY — requires an account-level token with Billing read permission","host.notConfiguredCLOUDFLAREACCOUNTID":"Not configured: CLOUDFLARE_ACCOUNT_ID","host.requestFailedHTTPTheToken.fetchCloudflareBilling":"Request failed: HTTP {status}. The token requires Billing read permission","host.actualMonthlyUsageFromThe":"Actual monthly usage from the Cloudflare Billable Usage API — Alpha","host.unexpectedBillingRequestFailure":"Unexpected billing request failure","host.spendSummaryFileMissingArchived":"Spend summary file missing: archived amounts are not included in displayed totals. Details remain in usage-archive/ for manual recovery.","host.spendLedgerCouldNotBe":"Spend ledger could not be saved","host.usageLedgerBusy":"A response is still in progress. Clear billing records after it finishes.","host.couldNotClearSpendRecords":"Billing records could not be fully cleared: {value}","host.couldNotSaveArchivedSpend":"Could not save archived spend summaries: {value}","host.basedOnYourLastSessions":"Based on your last {count} sessions","host.estimatedFromSpendingOverThe":"Estimated from spending over the last {SPEND_DAYS} days","host.yourTypicalSession":"Your typical session","host.patchMustIncludeFieldsOr":"patch must include fields, colors, timeFormat, timeZones or customText","host.timeFormatMustBeAnObject":"timeFormat must be an object","host.timeFieldMustBeABoolean":"Time format field must be a boolean: {key}","host.timeZonesMustBeAnObject":"timeZones must be an object","host.timeZoneMustBeAValid":"Time zone must be a valid IANA zone: {key}","host.customTextMustBeAString":"Custom text must be a string","host.customTextTooLong":"Custom text must not exceed 64 characters","host.fieldsMustBeAnObject":"fields must be an object","host.unknownFieldId":"Unknown field id: {key}","host.fieldVisibilityMustBeA":"Field visibility must be a boolean: {key}","host.colorsMustBeAnObject":"colors must be an object","host.colorMustBeAPreset":"Color must be a preset name or #RRGGBB: {key}","field.anchorGroup.label":"Provider and model","field.anchorGroup.note":"Shows the provider and model used in this conversation.","field.subServiceGroup.label":"Subscription service and model","field.subServiceGroup.note":"Shows the subscription service and current model or plan.","field.billingServiceGroup.label":"Billing service and model","field.billingServiceGroup.note":"Shows the billing service and current model.","field.customText.label":"Custom text","field.customText.note":"Shows custom text for a note or signature.","field.mainTime.label":"Main time","field.mainTime.note":"Shows the main time zone; adjust the zone and format below.","field.worldTime.label":"World time","field.worldTime.note":"Shows another time zone; it shares the main time format.","field.sessionCost.label":"Session spend","field.sessionCost.note":"Shows actual spend for this session, including subagents.","field.balance.note":"Shows the account balance; a low balance appears in red.","field.period.label":"Current period","field.period.note":"Shows whether the current period is peak or off-peak.","field.countdown.label":"Next price change","field.countdown.note":"Shows the time until the next price change.","field.expiry.label":"Subscription expiry","field.expiry.note":"Shown when the provider supplies an expiry date.","field.subWindow5h.label":"5-hour quota","field.subWindow5h.note":"Shows the remaining rolling 5-hour quota.","field.subWindowWeek.label":"Weekly quota","field.subWindowWeek.note":"Shows the remaining weekly quota.","field.subWindowMonth.label":"Monthly quota","field.subWindowMonth.note":"Shows the remaining monthly quota.","field.resetCountdown.label":"Quota reset","field.resetCountdown.note":"Shows when the current quota resets.","field.subBalance.note":"Shows prepaid balance, or remaining credits when a credits subscription has no visible quota window.","field.billingSpend.label":"Monthly usage","field.billingSpend.note":"Shows actual usage or spend for the current billing period.","field.budget.label":"Budget status","field.budget.note":"Shown when the provider reports a budget percentage.","field.freeQuota.label":"Free quota","field.freeQuota.note":"Shown when the provider reports a free allowance and reset time.","field.turnsSteps.label":"Turns and steps","field.turnsSteps.note":"Shows conversation turns and steps.","field.llmTime.label":"Model time","field.llmTime.note":"Shows model inference time.","field.toolTime.label":"Tool time","field.toolTime.note":"Shows tool execution time.","field.cacheHit.note":"Shows the prompt cache hit rate.","field.tokensIO.label":"Input / output tokens","field.tokensIO.note":"Shows this session’s input and output usage.","field.contextUsage.label":"Context usage","field.contextUsage.note":"DeepSeek’s native context ring, always shown at the end of the compact line; click for the breakdown.","ui.contextAria":"{percent} of context used","ui.contextUsed":"Context used","ui.contextSystem":"System prompt","ui.contextTools":"Tool definitions","ui.contextMessages":"Conversation messages","ui.contextFigures":"~{used} / {window}","field.unmapped.label":"Unsupported hint","field.unmapped.note":"Shown when balance lookup is not supported.","field.noKeyHint.label":"Missing key hint","field.noKeyHint.note":"Shown when an API key is missing.","field.balanceError.label":"Balance update error","field.balanceError.note":"Shown when balance update fails.","field.usageError.label":"Usage update error","field.usageError.note":"Shown when spend data is temporarily unavailable.","field.refreshFailure.label":"Update error","field.refreshFailure.note":"Shown when any data update fails.","field.persistWarning.label":"Unsaved record alert","field.persistWarning.note":"Shown when billing records cannot be saved; recommended.","field.updateNotice.note":"Shown when a new version is available.","group.native":"Native information","group.plugin":"Info Bar content","ui.sentenceSeparator":" ","ui.listSeparator":", ","ui.sentenceEnd":".","ui.fieldErrorPrefix":"\"{label}\": ","ui.turnCount":"{count} turn","ui.turnCountPlural":"{count} turns","ui.stepCount":"{count} step","ui.stepCountPlural":"{count} steps","ui.mainTimeZone":"Main time zone","ui.worldTimeZone":"World time zone","ui.timeFormat":"Time format","ui.year":"Year","ui.month":"Month","ui.day":"Day","ui.hour":"Hour","ui.minute":"Minute","ui.second":"Second","ui.customTextTitle":"Custom text","ui.customTextPlaceholder":"Enter text (up to 64 characters)","ui.searchPlaceholder":"Search content…","ui.searchFieldsLabel":"Search visible content","ui.searchResultCount":"{count} found","ui.enabledFieldsCount":"{count} enabled","ui.noSearchResults":"No matching content","ui.collapsedDesc":"Click to view and adjust what appears.","ui.timePreview":"Preview: ","ui.mainTime":"Main time","ui.worldTime":"World time","ui.customText":"Custom text"}};
|
|
62
|
+
const LOCALES = {"zh":{"ui.requestTimedOut":"请求超时","ui.requestCanceled":"请求已取消","ui.couldNotParseResponse":"响应解析失败","ui.rpcFailed":"RPC 失败","ui.refreshFailed":"刷新失败","color.red":"红","color.green":"绿","color.blue":"蓝","color.purple":"紫","color.orange":"橙","color.neutral":"中性","mode.balance":"余额制","mode.subscription":"订阅制","mode.billing":"账单制","mode.native":"原生统计行","mode.common":"通用","ui.nativeStatsField":"原生统计行字段","ui.shownIn":"出现于","ui.pleaseTryAgainLater":"请稍后再试","ui.restoreDefaultColor":"恢复默认颜色","ui.infoBarSettings":"信息栏","ui.settingsAreTemporarilyUnavailable":"暂时无法读取设置","ui.loadingInfoBarSettings":"正在加载设置…","ui.couldNotLoadInfoBar":"无法读取信息栏设置:","ui.changesAppliedButCouldNot":"已应用,但无法保存到本地:","ui.unknownReason":"未知原因","ui.couldNotSave":"{errorPrefix}保存失败:{value}","ui.color":"“{value}”颜色:","ui.hasAnInvalidColorEnter":"“{value}”的颜色格式不正确,请输入 #RRGGBB(例如 #0044CC)。","ui.defaultColorsRestored":"已恢复默认颜色","ui.defaultLabelsRestored":"已恢复默认显示设置","ui.couldNotReset":"重置失败:{value}","ui.identifiesTheProviderAndModel":"显示当前会话使用的服务商和模型。","ui.show":"显示{label}","ui.clickToHide":"点击隐藏","ui.clickToShow":"点击显示","ui.presetColor":"{label}的预设颜色","ui.customColor":"{label}的自定义颜色","ui.customColorOpenColorPicker":"自定义颜色(打开系统取色器)","ui.customColorItem":"自定义…","ui.colorSwatchLabel":"{label}的颜色","ui.hexColor":"{label}的十六进制颜色","ui.expand":"展开","ui.collapse":"收起","ui.saving":"正在保存…","ui.processing":"正在处理…","ui.chooseWhichFieldsToShow":"","ui.visibleFields":"显示内容","ui.timeDateTitle":"时间与日期","ui.timeDateDesc":"主时间和世界时间的时区与显示格式。","ui.customTextSectionDesc":"显示在信息栏中。","ui.resetConfirmTitle":"重置信息栏设置","ui.resetConfirmDescFields":"将把全部字段的显示开关恢复为默认状态(全部显示),自定义颜色不受影响。该操作会立即应用到信息栏。","ui.resetConfirmDescColors":"将清除全部自定义颜色并恢复默认配色,字段显示开关不受影响。该操作会立即应用到信息栏。","ui.resetConfirmAcknowledge":"我了解这次重置不可撤销","ui.resetConfirmCancel":"取消","ui.resetConfirmConfirm":"确认重置","ui.resetLabels":"恢复显示","ui.resetColors":"恢复颜色","ui.resetRowTitle":"恢复默认","ui.resetRowDesc":"恢复显示和颜色设置。","ui.customTextCount":"已输入 {value}/64 个字符","ui.couldNotDisplayInfoBar":"无法显示信息栏设置:{value}","ui.dataAndBilling":"账单数据","ui.dataAndBillingDesc":"导出或清除记录。","ui.exportBillingRecords":"导出账单","ui.exportBillingRecordsDesc":"导出为 CSV 或 JSON。","ui.exportBillingCsv":"导出 CSV","ui.exportBillingJson":"导出 JSON","ui.clearBillingRecords":"清除账单","ui.clearBillingRecordsDesc":"清除后无法恢复。","ui.clearBillingRecordsConfirm":"确定清除本插件保存的全部账单记录吗?建议先导出。清除后无法恢复,但不会影响设置或登录信息。","ui.exportedBillingRecords":"已导出 {count} 条账单记录({format})","ui.noBillingRecordsToExport":"目前没有可导出的账单记录。","ui.exportFailed":"导出失败:{value}","ui.exportIncomplete":"历史账单读取不完整:{value}。本次未导出,请稍后再试。","ui.clearFailed":"清除失败:{value}","ui.clearFailedWithoutDetails":"账单记录未能全部清除","ui.clearedBillingRecords":"已清除 {count} 条账单记录","ui.clearCanceled":"已取消","ui.exportNotSupported":"当前环境不支持下载文件。","ui.infoBar":"信息栏","ui.bundleConfigSummary":"配置显示哪些信息、各自的配色,以及信息密度","ui.weekly":"周","ui.monthly":"月","ui.window":"窗口","ui.supportsImageInput":"支持图像输入。","ui.vision":"视觉","ui.unknown":"未知","ui.unknownModel":"未知模型","ui.modelSelectionPending":"正在读取当前模型","ui.modelCapabilityPending":"正在确认是否支持图像输入","ui.pluginVersion":"\n插件版本:{current}","ui.provider":"服务商:{provLabel} {modelLabel}\n","ui.pricingPeakOffPeakBeijing":"定价:峰谷价(工作日高峰 9-12、14-18 点;周末全天空闲)","ui.pricingFixed":"定价:固定价","ui.pricingNotListedUsingDefaults":"定价:暂未收录,不计算花费","ui.zhipu":"智谱","ui.xiaomiMiMo":"小米 MiMo","ui.commandCode":"Command Code","ui.subscription":"订阅","ui.cloudBilling":"云账单","ui.plan":"\n套餐:{plan}","ui.expiresLocalTime":"\n到期:{value}(本地时区)","ui.subscriptionServiceModel":"订阅服务:{serviceName}\n模型:{rawModelLabel}{planLine}{expiryLine}{versionLine}","ui.balanceLookupIsNotYet":"暂时无法读取该服务商的余额。","ui.notSupported":"未适配","ui.notConfigured":"未配置 ","ui.notConfiguredConfigureItIn":"未配置 {credName}。请在\"设置 → 模型\"中填写。","ui.notConfiguredSettingsModels":"未配置 {credName}。请在设置 → 模型中填写。","ui.estimatedBalance":"估算余额:{symbol}{value}","ui.balance":"余额:{symbol}{value}","ui.balance.pushBalanceGroups":"余额","ui.low":"低","ui.estimated":"(估算)","ui.balanceIsTemporarilyUnavailableShowing":"余额暂不可用;正在显示上次数据并自动重试。","ui.couldNotLoadBalanceCheck":"余额获取失败。请检查网络和 API Key。","ui.balanceUnavailable":"余额获取失败","ui.beijingTime":"北京时间 ","ui.peakPrice":"高峰价","ui.offPeakPrice":"空闲价","ui.input":":输入 ¥","ui.mCachedInput":"/M · 缓存 ¥","ui.mOutput":"/M · 输出 ¥","ui.beijingTimeSwitchesTo":"北京时间 {atLabel} 切换为","ui.until":"距","ui.offPeak":"空闲","ui.peak":"高峰","ui.today":"今天 {symbol}{value}","ui.lastDays":"近一月 {symbol}{value}","ui.allTime":"全部 {symbol}{value}","ui.sessionIncludingSubagents":"本会话 {costTxt}(含子代理){value}","ui.session":"本会话","ui.spendIsTemporarilyUnavailableChat":"花费暂不可用;不会影响对话。","ui.spendUnavailable":"花费获取失败","ui.noSignInCredentialsFound":"未找到 {serviceName} 登录凭证。请重新授权。","ui.credentialsHaveExpiredPleaseReauthorize":"{serviceName} 登录凭证已失效。请重新授权。","ui.deniedAccessReauthorizeOrTry":"{serviceName} 拒绝访问。请重新授权或稍后再试。","ui.rateLimitReachedPleaseTry":"{serviceName} 请求过于频繁。请稍后再试。","ui.timedOutCheckYourConnection":"{serviceName} 响应超时。请检查网络后再试。","ui.returnedAnUnrecognizedResponsePlease":"{serviceName} 返回的数据暂时无法识别。请稍后再试。","ui.isTemporarilyUnavailableCheckYour":"{serviceName} 暂不可用。请检查网络后再试。","ui.subscriptionExpiresLocalTime":"订阅到期:{value}(本地时区)","ui.expires":"到期","ui.subscriptionSource":"订阅源:{value}(","ui.prepaidBalance":"充值余额","ui.availableBalance":"可用余额:{balTxt}","ui.availableCredits":"剩余 credits:{value}","ui.credits":"credits","ui.subscriptionSource.titleLines":"订阅源:{value}","ui.windowRemainingUsed":"{label}窗口:剩余 {value}%(已用 {usedPercent}%)","ui.resetsResetsIn":" · 重置 {value} · 距重置 {value2}","ui.windowRemainingUsedResets":"{label}窗口 剩余 {value}%(已用 {usedPercent}%) · 重置 {value4}","ui.resetsIn":"距重置","ui.configureItInSettingsModels":"。请在\"设置 → 模型\"中填写。","ui.deniedAccessTheTokenMay":"{serviceName} 拒绝访问:Token 可能缺少账单读取权限。","ui.billingIsTemporarilyUnavailableCheck":"{serviceName} 账单暂不可用。请检查网络与权限后再试。","ui.billingSource":"账单源:{value}","ui.thisMonthSSpend":"本月花费:{symbol}{value}","ui.budgetUsed":"预算使用:{value}%","ui.dailyFreeQuotaRemaining":"每日免费额度剩余:{value}","ui.thisMonth":"本月","ui.thisMonthSUsage":"本月用量","ui.budget":"预算","ui.free":"免费","ui.resetsIn.pushBillingGroups":"{value} · 距重置 {value2}","ui.billingServiceModel":"账单服务:{serviceName}\n模型:{modelLabel}{versionLine}","ui.pricing":"定价","ui.spend":"花费","ui.mode":"模式","ui.subscriptionQuota":"订阅额度","ui.billing":"账单","ui.temporarilyUnavailableKeepingTheLast":"暂不可用;正在保留上次数据并自动重试。","ui.spendJournalSavedButThe":"账单流水已保存,但可直接查看的账单文件暂未更新:","ui.thisSpendRecordWasNot":"本次账单未保存,不会计入金额:","ui.ledgerUpdatePending":"账单待整理","ui.spendNotSaved":"账单未保存","ui.askYourAgentToUpdate":"请告知你的 Agent 将本插件更新到“{latest}”版本。\n或者点击本标签复制更新命令,到终端粘贴执行,重启 DSH 后生效。","ui.updateAvailable":"新版本提醒","ui.updateCommandCopied":"更新命令已复制","ui.tools":"工具调用","ui.avgTTFT":"首 token 平均","ui.cacheHit":"缓存命中","ui.input.BottomInfoBar":"输入","ui.output":"输出","ui.savingView":"正在保存切换…","ui.clickToSwitchFullCompact":"单击切换 完整/简洁","ui.pressEnterOrSpaceFor":"按 Enter 或空格切换为简洁模式。","ui.pressEnterOrSpaceFor.BottomInfoBar":"按 Enter 或空格切换为完整模式。","host.hour":"5 小时","host.unknownProvider":"未知服务商","host.unexpectedSubscriptionQuotaRequestFailure":"订阅额度请求未知异常","host.settingsFileCouldNotBe":"设置文件写入未完成","host.everydayQuestions":"日常问答","host.mediumCodingTask":"中等编码任务","host.longDocumentAnalysisCodeReview":"长文档分析/代码审查","host.largeProjectRefactorAcrossMultiple":"大工程重构(多轮)","host.subagentWorkflow":"子代理工作流","host.couldNotSaveSettingsJson":"settings.json 落盘失败:{value}","host.couldNotReadCredentials":"凭据读取失败","host.notConfigured":"未配置 {credential}","host.requestFailedHTTP":"请求失败(HTTP {status})","host.unexpectedResponseFormat":"响应格式异常","host.chatgptSubscriptionIsNotConnected":"未找到 ChatGPT 订阅登录凭证(~/.codex/auth.json),请安装 dsh-chatgpt-subscription 插件绑定","host.chatgptSubscriptionCredentialsAreMissing":"ChatGPT 订阅登录凭证缺少 id_token,请安装 dsh-chatgpt-subscription 插件重新绑定","host.opencodeGoIsNotConfigured":"未配置 OpenCode Go(OPENCODE_GO_API_KEY 或 opencode auth.json)","host.commandCodeIsNotConfigured":"未配置 Command Code(COMMAND_CODE_API_KEY、CMD_API_KEY 或 ~/.commandcode/auth.json)","host.commandCodeAuthenticationFailed":"Command Code 登录凭证已失效。请重新登录或更新 API Key","host.commandCodeQuotaUnrecognized":"Command Code 返回了无法识别的额度格式(接口可能已变更),已保留上次数据","host.zhipu":"智谱 {mapped}","host.zhipu.parseZaiQuota":"智谱 {value}{value2}","host.zhipuAPIKeyIsNot":"未配置智谱 API Key(ZAI_API_KEY 或 ZAI_CODING_CN_API_KEY)","host.zhipuAPIAuthenticationFailedThe":"智谱 API 认证失败(密钥过期或不正确)","host.zhipuQuotaWindowsUnrecognized":"智谱返回了无法识别的额度格式(接口可能已变更),已保留上次数据","host.requestFailed":"请求失败({value} {msg})","host.xiaomiMiMoTokenPlanCredentials":"未配置小米 MiMo Token Plan 凭据({credName} 或 XIAOMI_API_KEY)","host.requestFailedHTTP.fetchXiaomiTokenPlanUsage":"请求失败(HTTP {value})","host.notConfiguredTOGETHERAPIKEY":"未配置 TOGETHER_API_KEY","host.actualMonthlyBillFromThe":"本月真实账单(Together 官方 Usage API)","host.notConfiguredFIREWORKSAPIKEY":"未配置 FIREWORKS_API_KEY","host.couldNotReadAccountAccount":"账户解析失败(未取得 account_id)","host.actualBillForThisPeriod":"本周期真实账单(Fireworks Billing Summary)","host.actualUsageForThisPeriod":"本周期真实用量(billingUsage 回退端点,无金额)","host.notConfiguredAWSCredentialsAWS":"未配置 AWS 凭据(AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY)","host.requestFailedHTTPTheToken":"请求失败(HTTP {status})——可能缺少 ce:GetCostAndUsage 权限","host.actualMonthlyBillFromAWS":"AWS Cost Explorer 本月真实账单(账单延迟约 24 小时)","host.notConfiguredCLOUDFLAREAPIKEY":"未配置 CLOUDFLARE_API_KEY(需账号级 Token 并授予 Billing 读权限)","host.notConfiguredCLOUDFLAREACCOUNTID":"未配置 CLOUDFLARE_ACCOUNT_ID","host.requestFailedHTTPTheToken.fetchCloudflareBilling":"请求失败(HTTP {status})——Token 需 Billing 读权限","host.actualMonthlyUsageFromThe":"Cloudflare Billable Usage API(Alpha),本月真实用量","host.unexpectedBillingRequestFailure":"账单请求未知异常","host.spendSummaryFileMissingArchived":"账单汇总文件缺失:折叠期间的历史金额暂未计入显示(明细仍在冷归档 usage-archive/,可人工恢复)","host.spendLedgerCouldNotBe":"账单文件写入未完成","host.usageLedgerBusy":"当前仍有回答正在进行,完成后再清除账单记录","host.couldNotClearSpendRecords":"账单记录清理未完成:{value}","host.couldNotSaveArchivedSpend":"折叠汇总落盘失败:{value}","host.basedOnYourLastSessions":"基于你最近 {count} 次会话","host.estimatedFromSpendingOverThe":"基于过去 {SPEND_DAYS} 天消耗速度的估算","host.yourTypicalSession":"你的典型会话","host.patchMustIncludeFieldsOr":"patch 需包含 fields、colors、timeFormat、timeZones 或 customText 之一","host.timeFormatMustBeAnObject":"timeFormat 必须是对象","host.timeFieldMustBeABoolean":"时间格式字段必须是布尔值: {key}","host.timeZonesMustBeAnObject":"timeZones 必须是对象","host.timeZoneMustBeAValid":"时区必须是有效的 IANA 时区: {key}","host.customTextMustBeAString":"自定义文本必须是字符串","host.customTextTooLong":"自定义文本不能超过64字符","host.fieldsMustBeAnObject":"fields 必须是对象","host.unknownFieldId":"未知字段 id: {key}","host.fieldVisibilityMustBeA":"字段开关必须是布尔值: {key}","host.colorsMustBeAnObject":"colors 必须是对象","host.colorMustBeAPreset":"颜色只接受预设色名或 #RRGGBB: {key}","field.anchorGroup.label":"服务商与模型","field.anchorGroup.note":"显示当前会话使用的服务商和模型。","field.subServiceGroup.label":"订阅服务与模型","field.subServiceGroup.note":"显示订阅服务和当前模型或套餐。","field.billingServiceGroup.label":"账单服务与模型","field.billingServiceGroup.note":"显示账单服务和当前模型。","field.customText.label":"自定义文字","field.customText.note":"显示一段自定义文字,可用作备注或签名。","field.mainTime.label":"主时间","field.mainTime.note":"显示主时区的时间;时区与格式在「时间与日期」中调整。","field.worldTime.label":"世界时间","field.worldTime.note":"显示另一时区的时间;格式与主时间相同。","field.sessionCost.label":"本次会话花费","field.sessionCost.note":"显示本次会话的实际花费,包含子代理。","field.balance.note":"显示账户余额;余额过低时会标红。","field.period.label":"当前时段","field.period.note":"显示当前是高峰还是空闲时段。","field.countdown.label":"下次价格切换","field.countdown.note":"显示距离下次价格切换还有多久。","field.expiry.label":"订阅到期日","field.expiry.note":"服务商提供到期时间时显示。","field.subWindow5h.label":"5 小时额度","field.subWindow5h.note":"显示滚动 5 小时额度的剩余比例。","field.subWindowWeek.label":"每周额度","field.subWindowWeek.note":"显示每周额度的剩余比例。","field.subWindowMonth.label":"每月额度","field.subWindowMonth.note":"显示每月额度的剩余比例。","field.resetCountdown.label":"距额度重置","field.resetCountdown.note":"显示当前额度何时重置。","field.subBalance.note":"充值账户显示余额;credits 型订阅在没有可显示窗口时显示剩余额度。","field.billingSpend.label":"本月用量","field.billingSpend.note":"显示本计费周期的实际用量或花费。","field.budget.label":"预算使用情况","field.budget.note":"服务商支持时显示已使用的预算比例。","field.freeQuota.label":"免费额度","field.freeQuota.note":"服务商明确提供时显示免费额度和重置时间。","field.turnsSteps.label":"轮次与步数","field.turnsSteps.note":"显示对话的轮次和步数。","field.llmTime.label":"模型耗时","field.llmTime.note":"显示模型推理耗时。","field.toolTime.label":"工具耗时","field.toolTime.note":"显示工具调用耗时。","field.cacheHit.note":"显示提示词缓存命中率。","field.tokensIO.label":"输入/输出 Token","field.tokensIO.note":"显示本次会话的输入和输出用量。","field.contextUsage.label":"上下文用量","field.contextUsage.note":"显示上下文占用情况。","ui.contextAria":"上下文已用 {percent}","ui.contextUsed":"上下文已用","ui.contextSystem":"系统提示词","ui.contextTools":"工具定义","ui.contextMessages":"对话消息","ui.contextFigures":"~{used} / {window}","field.unmapped.label":"暂不支持提示","field.unmapped.note":"当前服务商暂不支持余额查询时显示。","field.noKeyHint.label":"缺少密钥提示","field.noKeyHint.note":"缺少 API Key 时显示设置提示。","field.balanceError.label":"余额更新失败提示","field.balanceError.note":"余额更新失败时显示。","field.usageError.label":"用量更新失败提示","field.usageError.note":"花费暂时不可用时显示。","field.refreshFailure.label":"更新失败提示","field.refreshFailure.note":"任一数据更新失败时显示提醒。","field.persistWarning.label":"记录未保存提醒","field.persistWarning.note":"账单保存失败时显示提醒,建议保留。","field.updateNotice.note":"有新版本时显示提醒。","group.native":"原生信息","group.plugin":"插件信息","ui.sentenceSeparator":"","ui.listSeparator":"、","ui.sentenceEnd":"。","ui.fieldErrorPrefix":"「{label}」:","ui.turnCount":"{count} 轮","ui.turnCountPlural":"{count} 轮","ui.stepCount":"{count} 步","ui.stepCountPlural":"{count} 步","ui.mainTimeZone":"主时间时区","ui.worldTimeZone":"世界时间时区","ui.timeFormat":"时间格式","ui.year":"年","ui.month":"月","ui.day":"日","ui.hour":"时","ui.minute":"分","ui.second":"秒","ui.customTextTitle":"自定义文本","ui.customTextPlaceholder":"输入文字(最多 64 个字符)","ui.searchPlaceholder":"搜索内容…","ui.searchFieldsLabel":"搜索显示内容","ui.searchResultCount":"找到 {count} 项","ui.enabledFieldsCount":"已启用 {count} 项","ui.noSearchResults":"没有找到匹配内容","ui.timePreview":"预览:","ui.mainTime":"主时间","ui.worldTime":"世界时间","ui.customText":"自定义文本"},"en":{"ui.requestTimedOut":"Request timed out","ui.requestCanceled":"Request canceled","ui.couldNotParseResponse":"Could not parse response","ui.rpcFailed":"RPC failed","ui.refreshFailed":"Refresh failed","color.red":"Red","color.green":"Green","color.blue":"Blue","color.purple":"Purple","color.orange":"Orange","color.neutral":"Neutral","mode.balance":"Balance","mode.subscription":"Subscription","mode.billing":"Billing","mode.native":"Native stats","mode.common":"General","ui.nativeStatsField":"Native stats field","ui.shownIn":"Shown in: ","ui.pleaseTryAgainLater":"Please try again later","ui.restoreDefaultColor":"Restore default color","ui.infoBarSettings":"Info Bar","ui.settingsAreTemporarilyUnavailable":"Settings are temporarily unavailable","ui.loadingInfoBarSettings":"Loading settings…","ui.couldNotLoadInfoBar":"Could not load Info Bar settings: ","ui.changesAppliedButCouldNot":"Applied, but could not save locally: ","ui.unknownReason":"Unknown reason","ui.couldNotSave":"{errorPrefix}Could not save: {value}","ui.color":"\"{value}\" color: ","ui.hasAnInvalidColorEnter":"\"{value}\" has an invalid color. Enter #RRGGBB (for example, #0044CC).","ui.defaultColorsRestored":"Default colors restored","ui.defaultLabelsRestored":"Default display restored","ui.couldNotReset":"Could not reset: {value}","ui.identifiesTheProviderAndModel":"Shows the provider and model used in this conversation.","ui.show":"Show {label}","ui.clickToHide":"Click to hide","ui.clickToShow":"Click to show","ui.presetColor":"{label} preset color","ui.customColor":"{label} custom color","ui.customColorOpenColorPicker":"Custom color (open color picker)","ui.customColorItem":"Custom…","ui.colorSwatchLabel":"{label} color","ui.hexColor":"{label} hex color","ui.expand":"Expand","ui.collapse":"Collapse","ui.saving":"Saving…","ui.processing":"Working…","ui.chooseWhichFieldsToShow":"","ui.visibleFields":"Visible content","ui.timeDateTitle":"Time and date","ui.timeDateDesc":"Time zones and display format for main and world time.","ui.customTextSectionDesc":"Shown in the Info Bar.","ui.resetConfirmTitle":"Reset Info Bar settings","ui.resetConfirmDescFields":"All field switches will be restored to their defaults (all shown). Custom colors are not affected. This applies to the Info Bar immediately.","ui.resetConfirmDescColors":"All custom colors will be cleared and defaults restored. Field switches are not affected. This applies to the Info Bar immediately.","ui.resetConfirmAcknowledge":"I understand this reset cannot be undone","ui.resetConfirmCancel":"Cancel","ui.resetConfirmConfirm":"Reset","ui.resetLabels":"Restore display","ui.resetColors":"Restore colors","ui.resetRowTitle":"Reset to defaults","ui.resetRowDesc":"Restore display and color settings.","ui.customTextCount":"{value}/64 characters","ui.couldNotDisplayInfoBar":"Could not display Info Bar settings: {value}","ui.dataAndBilling":"Billing data","ui.dataAndBillingDesc":"Export or clear records.","ui.exportBillingRecords":"Export billing","ui.exportBillingRecordsDesc":"Export as CSV or JSON.","ui.exportBillingCsv":"Export CSV","ui.exportBillingJson":"Export JSON","ui.clearBillingRecords":"Clear billing","ui.clearBillingRecordsDesc":"This cannot be undone.","ui.clearBillingRecordsConfirm":"Clear all billing records saved by this plugin? Export first. This cannot be undone, but settings and sign-in information will stay unchanged.","ui.exportedBillingRecords":"Exported {count} billing records ({format})","ui.noBillingRecordsToExport":"There are no billing records to export.","ui.exportFailed":"Export failed: {value}","ui.exportIncomplete":"Some historical billing records could not be read: {value}. Nothing was exported; try again later.","ui.clearFailed":"Could not clear billing records: {value}","ui.clearFailedWithoutDetails":"Billing records could not be fully cleared","ui.clearedBillingRecords":"Cleared {count} billing records","ui.clearCanceled":"Canceled","ui.exportNotSupported":"File downloads are not supported in this environment.","ui.infoBar":"Info Bar","ui.bundleConfigSummary":"Choose which fields to show, their colors, and the info density","ui.weekly":"Weekly","ui.monthly":"Monthly","ui.window":"Window","ui.supportsImageInput":"Supports image input.","ui.vision":"Vision","ui.unknown":"Unknown","ui.unknownModel":"Unknown model","ui.modelSelectionPending":"Reading current model","ui.modelCapabilityPending":"Checking image-input support","ui.pluginVersion":"\nPlugin version: {current}","ui.provider":"Provider: {provLabel} {modelLabel}\n","ui.pricingPeakOffPeakBeijing":"Pricing: peak/off-peak (Beijing time; weekday peaks 09:00–12:00 and 14:00–18:00; weekends off-peak)","ui.pricingFixed":"Pricing: fixed","ui.pricingNotListedUsingDefaults":"Pricing: not listed; spend is not calculated","ui.zhipu":"Zhipu","ui.xiaomiMiMo":"Xiaomi MiMo","ui.commandCode":"Command Code","ui.subscription":"Subscription","ui.cloudBilling":"Cloud billing","ui.plan":"\nPlan: {plan}","ui.expiresLocalTime":"\nExpires: {value} (local time)","ui.subscriptionServiceModel":"Subscription service: {serviceName}\nModel: {rawModelLabel}{planLine}{expiryLine}{versionLine}","ui.balanceLookupIsNotYet":"This provider's balance is not available yet.","ui.notSupported":"Not supported","ui.notConfigured":"Not configured: ","ui.notConfiguredConfigureItIn":"Not configured: {credName}. Configure it in Settings → Models.","ui.notConfiguredSettingsModels":"{credName} is not configured. Add it in Settings → Models.","ui.estimatedBalance":"Estimated balance: {symbol}{value}","ui.balance":"Balance: {symbol}{value}","ui.balance.pushBalanceGroups":"Balance","ui.low":"Low","ui.estimated":"(estimated)","ui.balanceIsTemporarilyUnavailableShowing":"Balance is temporarily unavailable. Showing the last data and retrying automatically.","ui.couldNotLoadBalanceCheck":"Could not load balance. Check your connection and API key.","ui.balanceUnavailable":"Balance unavailable","ui.beijingTime":"Beijing time: ","ui.peakPrice":"Peak price","ui.offPeakPrice":"Off-peak price","ui.input":": input ¥","ui.mCachedInput":"/M · cached input ¥","ui.mOutput":"/M · output ¥","ui.beijingTimeSwitchesTo":"Beijing time: {atLabel} switches to ","ui.until":"Until ","ui.offPeak":"off-peak","ui.peak":"peak","ui.today":"Today {symbol}{value}","ui.lastDays":"Last 30 days {symbol}{value}","ui.allTime":"All time {symbol}{value}","ui.sessionIncludingSubagents":"Session {costTxt} (including subagents){value}","ui.session":"Session","ui.spendIsTemporarilyUnavailableChat":"Spend is temporarily unavailable. Chat is unaffected.","ui.spendUnavailable":"Spend unavailable","ui.noSignInCredentialsFound":"No sign-in credentials found for {serviceName}. Please reauthorize.","ui.credentialsHaveExpiredPleaseReauthorize":"{serviceName} credentials have expired. Please reauthorize.","ui.deniedAccessReauthorizeOrTry":"{serviceName} denied access. Reauthorize or try again later.","ui.rateLimitReachedPleaseTry":"{serviceName} rate limit reached. Please try again later.","ui.timedOutCheckYourConnection":"{serviceName} timed out. Check your connection and try again.","ui.returnedAnUnrecognizedResponsePlease":"{serviceName} returned an unrecognized response. Please try again later.","ui.isTemporarilyUnavailableCheckYour":"{serviceName} is temporarily unavailable. Check your connection and try again.","ui.subscriptionExpiresLocalTime":"Subscription expires: {value} (local time)","ui.expires":"Expires","ui.subscriptionSource":"Subscription source: {value} (","ui.prepaidBalance":"Prepaid balance","ui.availableBalance":"Available balance: {balTxt}","ui.availableCredits":"Credits remaining: {value}","ui.credits":"credits","ui.subscriptionSource.titleLines":"Subscription source: {value}","ui.windowRemainingUsed":"{label} window: remaining {value}% (used {usedPercent}%)","ui.resetsResetsIn":" · Resets {value} · Resets in {value2}","ui.windowRemainingUsedResets":"{label} window: remaining {value}% (used {usedPercent}%) · Resets {value4}","ui.resetsIn":"Resets in","ui.configureItInSettingsModels":". Configure it in Settings → Models.","ui.deniedAccessTheTokenMay":"{serviceName} denied access: the token may lack billing read permissions.","ui.billingIsTemporarilyUnavailableCheck":"{serviceName} billing is temporarily unavailable. Check your connection and permissions, then try again.","ui.billingSource":"Billing source: {value}","ui.thisMonthSSpend":"This month’s spend: {symbol}{value}","ui.budgetUsed":"Budget used: {value}%","ui.dailyFreeQuotaRemaining":"Daily free quota remaining: {value}","ui.thisMonth":"This month","ui.thisMonthSUsage":"This month’s usage","ui.budget":"Budget","ui.free":"Free","ui.resetsIn.pushBillingGroups":"{value} · Resets in {value2}","ui.billingServiceModel":"Billing service: {serviceName}\nModel: {modelLabel}{versionLine}","ui.pricing":"Pricing","ui.spend":"Spend","ui.mode":"Mode","ui.subscriptionQuota":"Subscription quota","ui.billing":"Billing","ui.temporarilyUnavailableKeepingTheLast":": temporarily unavailable. Keeping the last data and retrying automatically.","ui.spendJournalSavedButThe":"Spend journal saved, but the readable ledger has not been updated: ","ui.thisSpendRecordWasNot":"This spend record was not saved and will not be added to totals: ","ui.ledgerUpdatePending":"Ledger update pending","ui.spendNotSaved":"Spend not saved","ui.askYourAgentToUpdate":"Ask your agent to update this plugin to version {latest}.\nOr click this label to copy the update command, paste it in a terminal, then restart DSH.","ui.updateAvailable":"Update available","ui.updateCommandCopied":"Update command copied","ui.tools":"Tools","ui.avgTTFT":"Avg. TTFT","ui.cacheHit":"Cache hit","ui.input.BottomInfoBar":"Input","ui.output":"Output","ui.savingView":"Saving view…","ui.clickToSwitchFullCompact":"Click to switch full/compact view","ui.pressEnterOrSpaceFor":"Press Enter or Space for compact view.","ui.pressEnterOrSpaceFor.BottomInfoBar":"Press Enter or Space for full view.","host.hour":"5-hour","host.unknownProvider":"Unknown provider","host.unexpectedSubscriptionQuotaRequestFailure":"Unexpected subscription quota request failure","host.settingsFileCouldNotBe":"Settings file could not be saved","host.everydayQuestions":"Everyday questions","host.mediumCodingTask":"Medium coding task","host.longDocumentAnalysisCodeReview":"Long document analysis / code review","host.largeProjectRefactorAcrossMultiple":"Large project refactor across multiple turns","host.subagentWorkflow":"Subagent workflow","host.couldNotSaveSettingsJson":"Could not save settings.json: {value}","host.couldNotReadCredentials":"Could not read credentials","host.notConfigured":"Not configured: {credential}","host.requestFailedHTTP":"Request failed: HTTP {status}.","host.unexpectedResponseFormat":"Unexpected response format","host.chatgptSubscriptionIsNotConnected":"ChatGPT subscription is not connected: credentials not found in ~/.codex/auth.json. Install dsh-chatgpt-subscription and sign in.","host.chatgptSubscriptionCredentialsAreMissing":"ChatGPT subscription credentials are missing id_token. Install dsh-chatgpt-subscription and reauthorize.","host.opencodeGoIsNotConfigured":"OpenCode Go is not configured. Set OPENCODE_GO_API_KEY or use opencode auth.json.","host.commandCodeIsNotConfigured":"Command Code is not configured. Set COMMAND_CODE_API_KEY, CMD_API_KEY, or sign in with the Command Code CLI.","host.commandCodeAuthenticationFailed":"Command Code credentials were rejected. Sign in again or update the API key.","host.commandCodeQuotaUnrecognized":"Command Code returned an unrecognized quota format; the previous snapshot was kept.","host.zhipu":"Zhipu {mapped}","host.zhipu.parseZaiQuota":"Zhipu {value}{value2}","host.zhipuAPIKeyIsNot":"Zhipu API key is not configured. Set ZAI_API_KEY or ZAI_CODING_CN_API_KEY.","host.zhipuAPIAuthenticationFailedThe":"Zhipu API authentication failed: the key is expired or invalid.","host.zhipuQuotaWindowsUnrecognized":"Zhipu returned an unrecognized quota format (the API may have changed); the last known data is kept.","host.requestFailed":"Request failed: {value} {msg}.","host.xiaomiMiMoTokenPlanCredentials":"Xiaomi MiMo Token Plan credentials are not configured: {credName} or XIAOMI_API_KEY.","host.requestFailedHTTP.fetchXiaomiTokenPlanUsage":"Request failed: HTTP {value}.","host.notConfiguredTOGETHERAPIKEY":"Not configured: TOGETHER_API_KEY","host.actualMonthlyBillFromThe":"Actual monthly bill from the Together Usage API","host.notConfiguredFIREWORKSAPIKEY":"Not configured: FIREWORKS_API_KEY","host.couldNotReadAccountAccount":"Could not read account (account_id missing)","host.actualBillForThisPeriod":"Actual bill for this period (Fireworks Billing Summary)","host.actualUsageForThisPeriod":"Actual usage for this period (billingUsage fallback; no spend amount)","host.notConfiguredAWSCredentialsAWS":"Not configured: AWS credentials — AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY","host.requestFailedHTTPTheToken":"Request failed: HTTP {status}. The token may lack ce:GetCostAndUsage permission","host.actualMonthlyBillFromAWS":"Actual monthly bill from AWS Cost Explorer — about 24 hours behind","host.notConfiguredCLOUDFLAREAPIKEY":"Not configured: CLOUDFLARE_API_KEY — requires an account-level token with Billing read permission","host.notConfiguredCLOUDFLAREACCOUNTID":"Not configured: CLOUDFLARE_ACCOUNT_ID","host.requestFailedHTTPTheToken.fetchCloudflareBilling":"Request failed: HTTP {status}. The token requires Billing read permission","host.actualMonthlyUsageFromThe":"Actual monthly usage from the Cloudflare Billable Usage API — Alpha","host.unexpectedBillingRequestFailure":"Unexpected billing request failure","host.spendSummaryFileMissingArchived":"Spend summary file missing: archived amounts are not included in displayed totals. Details remain in usage-archive/ for manual recovery.","host.spendLedgerCouldNotBe":"Spend ledger could not be saved","host.usageLedgerBusy":"A response is still in progress. Clear billing records after it finishes.","host.couldNotClearSpendRecords":"Billing records could not be fully cleared: {value}","host.couldNotSaveArchivedSpend":"Could not save archived spend summaries: {value}","host.basedOnYourLastSessions":"Based on your last {count} sessions","host.estimatedFromSpendingOverThe":"Estimated from spending over the last {SPEND_DAYS} days","host.yourTypicalSession":"Your typical session","host.patchMustIncludeFieldsOr":"patch must include fields, colors, timeFormat, timeZones or customText","host.timeFormatMustBeAnObject":"timeFormat must be an object","host.timeFieldMustBeABoolean":"Time format field must be a boolean: {key}","host.timeZonesMustBeAnObject":"timeZones must be an object","host.timeZoneMustBeAValid":"Time zone must be a valid IANA zone: {key}","host.customTextMustBeAString":"Custom text must be a string","host.customTextTooLong":"Custom text must not exceed 64 characters","host.fieldsMustBeAnObject":"fields must be an object","host.unknownFieldId":"Unknown field id: {key}","host.fieldVisibilityMustBeA":"Field visibility must be a boolean: {key}","host.colorsMustBeAnObject":"colors must be an object","host.colorMustBeAPreset":"Color must be a preset name or #RRGGBB: {key}","field.anchorGroup.label":"Provider and model","field.anchorGroup.note":"Shows the provider and model used in this conversation.","field.subServiceGroup.label":"Subscription service and model","field.subServiceGroup.note":"Shows the subscription service and current model or plan.","field.billingServiceGroup.label":"Billing service and model","field.billingServiceGroup.note":"Shows the billing service and current model.","field.customText.label":"Custom text","field.customText.note":"Shows custom text for a note or signature.","field.mainTime.label":"Main time","field.mainTime.note":"Shows the main time zone; adjust zone and format in Time and date.","field.worldTime.label":"World time","field.worldTime.note":"Shows another time zone; it shares the main time format.","field.sessionCost.label":"Session spend","field.sessionCost.note":"Shows actual spend for this session, including subagents.","field.balance.note":"Shows the account balance; a low balance appears in red.","field.period.label":"Current period","field.period.note":"Shows whether the current period is peak or off-peak.","field.countdown.label":"Next price change","field.countdown.note":"Shows the time until the next price change.","field.expiry.label":"Subscription expiry","field.expiry.note":"Shown when the provider supplies an expiry date.","field.subWindow5h.label":"5-hour quota","field.subWindow5h.note":"Shows the remaining rolling 5-hour quota.","field.subWindowWeek.label":"Weekly quota","field.subWindowWeek.note":"Shows the remaining weekly quota.","field.subWindowMonth.label":"Monthly quota","field.subWindowMonth.note":"Shows the remaining monthly quota.","field.resetCountdown.label":"Quota reset","field.resetCountdown.note":"Shows when the current quota resets.","field.subBalance.note":"Shows prepaid balance, or remaining credits when a credits subscription has no visible quota window.","field.billingSpend.label":"Monthly usage","field.billingSpend.note":"Shows actual usage or spend for the current billing period.","field.budget.label":"Budget status","field.budget.note":"Shown when the provider reports a budget percentage.","field.freeQuota.label":"Free quota","field.freeQuota.note":"Shown when the provider reports a free allowance and reset time.","field.turnsSteps.label":"Turns and steps","field.turnsSteps.note":"Shows conversation turns and steps.","field.llmTime.label":"Model time","field.llmTime.note":"Shows model inference time.","field.toolTime.label":"Tool time","field.toolTime.note":"Shows tool execution time.","field.cacheHit.note":"Shows the prompt cache hit rate.","field.tokensIO.label":"Input / output tokens","field.tokensIO.note":"Shows this session’s input and output usage.","field.contextUsage.label":"Context usage","field.contextUsage.note":"Shows context usage.","ui.contextAria":"{percent} of context used","ui.contextUsed":"Context used","ui.contextSystem":"System prompt","ui.contextTools":"Tool definitions","ui.contextMessages":"Conversation messages","ui.contextFigures":"~{used} / {window}","field.unmapped.label":"Unsupported hint","field.unmapped.note":"Shown when balance lookup is not supported.","field.noKeyHint.label":"Missing key hint","field.noKeyHint.note":"Shown when an API key is missing.","field.balanceError.label":"Balance update error","field.balanceError.note":"Shown when balance update fails.","field.usageError.label":"Usage update error","field.usageError.note":"Shown when spend data is temporarily unavailable.","field.refreshFailure.label":"Update error","field.refreshFailure.note":"Shown when any data update fails.","field.persistWarning.label":"Unsaved record alert","field.persistWarning.note":"Shown when billing records cannot be saved; recommended.","field.updateNotice.note":"Shown when a new version is available.","group.native":"Native information","group.plugin":"Plugin information","ui.sentenceSeparator":" ","ui.listSeparator":", ","ui.sentenceEnd":".","ui.fieldErrorPrefix":"\"{label}\": ","ui.turnCount":"{count} turn","ui.turnCountPlural":"{count} turns","ui.stepCount":"{count} step","ui.stepCountPlural":"{count} steps","ui.mainTimeZone":"Main time zone","ui.worldTimeZone":"World time zone","ui.timeFormat":"Time format","ui.year":"Year","ui.month":"Month","ui.day":"Day","ui.hour":"Hour","ui.minute":"Minute","ui.second":"Second","ui.customTextTitle":"Custom text","ui.customTextPlaceholder":"Enter text (up to 64 characters)","ui.searchPlaceholder":"Search content…","ui.searchFieldsLabel":"Search visible content","ui.searchResultCount":"{count} found","ui.enabledFieldsCount":"{count} enabled","ui.noSearchResults":"No matching content","ui.timePreview":"Preview: ","ui.mainTime":"Main time","ui.worldTime":"World time","ui.customText":"Custom text"}};
|
|
34
63
|
let t;
|
|
35
64
|
let localeService;
|
|
36
65
|
// Compatibility with existing host snapshots, whose display fields are text.
|
|
@@ -807,20 +836,6 @@ const FIELD_GROUP_LABELS = {
|
|
|
807
836
|
native: "group.native",
|
|
808
837
|
plugin: "group.plugin",
|
|
809
838
|
};
|
|
810
|
-
// 每行出现条件文案:modes → 中文(余额制/订阅制/账单制/原生统计行/通用)
|
|
811
|
-
const BIB_SET_MODE_LABELS = { balance: "mode.balance", subscription: "mode.subscription", billing: "mode.billing", native: "mode.native", common: "mode.common" };
|
|
812
|
-
function bibSetModeText(field) {
|
|
813
|
-
if (!field || !Array.isArray(field.modes) || field.modes.length === 0) return '';
|
|
814
|
-
if (field.modes.length === 1 && field.modes[0] === 'native') return t('ui.nativeStatsField');
|
|
815
|
-
const labels = [];
|
|
816
|
-
for (let i = 0; i < field.modes.length; i++) {
|
|
817
|
-
const key = BIB_SET_MODE_LABELS[field.modes[i]];
|
|
818
|
-
const label = key && t(key);
|
|
819
|
-
if (label && labels.indexOf(label) === -1) labels.push(label);
|
|
820
|
-
}
|
|
821
|
-
return labels.length > 0 ? t('ui.shownIn') + labels.join(t('ui.listSeparator')) : '';
|
|
822
|
-
}
|
|
823
|
-
|
|
824
839
|
function bibSetDispatchChanged() {
|
|
825
840
|
// 设置页保存成功后广播:信息栏监听并立即重拉配置(宿主内存缓存,即回)
|
|
826
841
|
try { document.dispatchEvent(new CustomEvent(BIB_SET_EVENT)); } catch (err) { /* 事件总线不可用时静默:30s 周期校准兜底 */ }
|
|
@@ -876,126 +891,331 @@ function bibSetInstallStyles() {
|
|
|
876
891
|
style.dataset.plugin = 'dsh-bottom-info-bar';
|
|
877
892
|
style.dataset.pluginCss = id;
|
|
878
893
|
style.textContent = `
|
|
879
|
-
|
|
894
|
+
/* ============================================================================
|
|
895
|
+
度量层(唯一的数值来源)
|
|
896
|
+
----------------------------------------------------------------------------
|
|
897
|
+
本插件的配置区由宿主渲染进「插件详情页」的 .X_2TxG_detailSection 里,
|
|
898
|
+
所以视觉基线**只有一套**:宿主自己的 dsh-client-ui-plugin-manager 的 X_2TxG_*。
|
|
899
|
+
为避免数值散落在上百条规则里、以后又各改各的,这里把全部度量抽成变量;
|
|
900
|
+
下面所有规则只准引用变量,不准再写裸数字。
|
|
901
|
+
|
|
902
|
+
变量命名与取值来源(逐条实测 2026-09-22 / DSH 0.1.7-alpha.1,见 docs/DSH-PLUGIN-PAGE-BASELINE.md):
|
|
903
|
+
--bib-sec-gap .X_2TxG_detailSections { gap: 32px }
|
|
904
|
+
--bib-sec-inner .X_2TxG_detailSection { gap: 12px }
|
|
905
|
+
--bib-head-gap .X_2TxG_sectionHead { align-items: baseline; gap: 10px }
|
|
906
|
+
--bib-title-size/weight/line .X_2TxG_sectionTitle { 14 / 500 / 20 }
|
|
907
|
+
--bib-count-* .X_2TxG_sectionCount { 12 / 400 / 18, label-secondary }
|
|
908
|
+
--bib-intro-* .X_2TxG_pageIntro { 13 / 400 / 20, label-secondary }
|
|
909
|
+
--bib-desc-* .X_2TxG_cardDesc { 13 / 400 / 18, label-tertiary }
|
|
910
|
+
--bib-row-pad .X_2TxG_row { padding: 12px 2px }
|
|
911
|
+
--bib-row-rule .X_2TxG_row { border-bottom: .5px, last-child 无线 }
|
|
912
|
+
--bib-row-gap .X_2TxG_rowLine { gap: 16px }
|
|
913
|
+
--bib-row-label-* .X_2TxG_rowId { 13.5 / 500 / 20 }
|
|
914
|
+
--bib-row-hint-* .X_2TxG_rowModule { 11.5 / 400 / 16, label-tertiary }
|
|
915
|
+
--bib-btn-h/radius/pad/font primitives Button.module.css .sm { 28 / 14 / 0 10 / 12 18 }
|
|
916
|
+
--bib-input-h/radius/pad/font primitives settings-form fields.module.css .input { 34 / 8 / 0 12 / 13 }
|
|
917
|
+
|
|
918
|
+
【横向对齐铁律】宿主给我们的容器 .X_2TxG_detailSection 实测 padding:0、左沿 323.2,
|
|
919
|
+
宿主自己的区块标题文字左沿也是 323.2。所以我们的内容块一律 padding 左右为 0,
|
|
920
|
+
横向内缩只能来自行自己的 --bib-row-pad;任何内容块自带左右内边距都会整体错位。
|
|
921
|
+
|
|
922
|
+
【宽度铁律】宿主的 .X_2TxG_page > * 已经把宽度钉在 min(100%, 960px)。插件根节点
|
|
923
|
+
**不得再设 px 级 max-width**(曾经写 max-width:760px,在 ≥816px 的视口里把内容
|
|
924
|
+
整体压窄 200px,右侧控件够不到宿主右边界——这就是「被裁掉/对不齐」的真凶)。
|
|
925
|
+
只允许 max-inline-size: 100%。 */
|
|
926
|
+
.bib-set-root {
|
|
927
|
+
--bib-set-brand: #4d6bfe; /* 固定品牌蓝,保障與 #fff 的反色對比度,避免跟隨 --dsw-alias-brand-primary 在深色主題下變淺導致白字被吞 */
|
|
928
|
+
/* 页面区块 24px;同一区块中的独立操作组 16px;卡片内部 16px;相邻行 0px。 */
|
|
929
|
+
--bib-sec-gap: 24px;
|
|
930
|
+
--bib-sec-inner: 16px;
|
|
931
|
+
--bib-group-gap: 16px;
|
|
932
|
+
--bib-head-gap: 10px;
|
|
933
|
+
--bib-title-size: 14px; --bib-title-weight: 500; --bib-title-line: 20px;
|
|
934
|
+
--bib-count-size: 12px; --bib-count-line: 18px;
|
|
935
|
+
--bib-intro-size: 13px; --bib-intro-line: 20px;
|
|
936
|
+
--bib-desc-size: 13px; --bib-desc-line: 18px;
|
|
937
|
+
--bib-row-pad-block: 12px; --bib-row-pad-inline: 2px;
|
|
938
|
+
--bib-row-gap: 16px;
|
|
939
|
+
--bib-row-label-size: 13.5px; --bib-row-label-weight: 500; --bib-row-label-line: 20px;
|
|
940
|
+
--bib-row-hint-size: 11.5px; --bib-row-hint-line: 16px;
|
|
941
|
+
--bib-control-radius: 8px;
|
|
942
|
+
--bib-btn-height: 28px; --bib-btn-radius: var(--bib-control-radius); --bib-btn-pad-inline: 10px; --bib-btn-size: 12px; --bib-btn-line: 18px;
|
|
943
|
+
--bib-input-height: 34px; --bib-input-radius: var(--bib-control-radius); --bib-input-pad-inline: 12px; --bib-input-size: 13px;
|
|
944
|
+
--bib-rule: 0.5px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.16));
|
|
945
|
+
--bib-swatch-size: 20px; --bib-swatch-radius: var(--bib-control-radius);
|
|
946
|
+
--bib-menu-dot-size: 12px;
|
|
947
|
+
--bib-field-gap: 6px; --bib-field-pad-block: 12px;
|
|
948
|
+
--bib-field-label-size: 13px; --bib-field-label-weight: 500; --bib-field-label-line: 1.5;
|
|
949
|
+
--bib-field-hint-size: 12px; --bib-field-hint-line: 1.5;
|
|
950
|
+
box-sizing: border-box; max-inline-size: 100%; min-inline-size: 0;
|
|
951
|
+
}
|
|
880
952
|
.bib-settings, .bib-settings * { box-sizing: border-box; }
|
|
881
|
-
/*
|
|
882
|
-
|
|
953
|
+
/* 页头(标题 + 说明):标题与本页其它区块同级(原生 sectionTitle),不另起一套字号。
|
|
954
|
+
宿主已经在页面上方渲染了插件名(20/500)与简介,这里只是本配置块的区块头。 */
|
|
955
|
+
.bib-set-page-head { display: flex; flex-direction: column; gap: 2px; width: 100%; min-width: 0; padding: 0; }
|
|
956
|
+
.bib-set-page-title { width: 100%; margin: 0; font-size: var(--bib-title-size); font-weight: var(--bib-title-weight); line-height: var(--bib-title-line); color: var(--dsw-alias-label-primary); }
|
|
883
957
|
/* 只保留 DSH 设置面板这一层纵向滚动:根节点比宿主视口多 2px,确保收起时也会
|
|
884
|
-
进入同一个滚动状态;插件自身和字段清单不再创建第二、第三条滚动轨道。
|
|
885
|
-
|
|
958
|
+
进入同一个滚动状态;插件自身和字段清单不再创建第二、第三条滚动轨道。
|
|
959
|
+
注意:这里**不能**设 px 级 max-width(见上面「宽度铁律」)。 */
|
|
960
|
+
.bib-settings { display: flex; flex: 0 0 auto; align-self: stretch; width: 100%; inline-size: 100%; max-inline-size: 100%; min-width: 0; min-inline-size: 0; min-height: calc(100% + 2px); min-block-size: calc(100% + 2px); overflow: visible; contain: inline-size; flex-direction: column; gap: var(--bib-sec-gap); color: var(--dsw-alias-label-primary); }
|
|
886
961
|
/* 仅隐藏视觉轨道,不关闭滚动能力;宿主标记由组件生命周期维护。 */
|
|
887
962
|
.bib-settings, .bib-set-field-list, [data-dsh-bib-hide-scrollbars="true"] { scrollbar-width: none; -ms-overflow-style: none; }
|
|
888
963
|
.bib-settings::-webkit-scrollbar, .bib-set-field-list::-webkit-scrollbar, [data-dsh-bib-hide-scrollbars="true"]::-webkit-scrollbar { display: none !important; width: 0 !important; height: 0 !important; }
|
|
889
|
-
|
|
890
|
-
.bib-set-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
.bib-set-
|
|
964
|
+
/* 区块说明:原生 pageIntro 的 13/20 二级色(宿主 .X_2TxG_pageIntro)。 */
|
|
965
|
+
.bib-set-intro { width: 100%; margin: 0; color: var(--dsw-alias-label-secondary); font-size: var(--bib-intro-size); line-height: var(--bib-intro-line); }
|
|
966
|
+
.bib-set-status { margin: 0; color: var(--dsw-alias-label-tertiary); font-size: var(--bib-intro-size); line-height: var(--bib-intro-line); }
|
|
967
|
+
/* 搜索行:左侧输入框吃满剩余宽度,右侧计数用 auto 轨道。
|
|
968
|
+
计数框原来是写死的 104px + nowrap,文案一变长就会被切——改成 auto 轨道,永不裁切。 */
|
|
969
|
+
.bib-set-toolbar { width: 100%; max-width: 100%; min-width: 0; min-inline-size: 0; box-sizing: border-box; padding: 0; }
|
|
970
|
+
.bib-set-search-row { display: block; width: 100%; min-width: 0; min-height: 34px; }
|
|
895
971
|
.bib-set-search-shell { position: relative; width: 100%; min-width: 0; }
|
|
896
|
-
.bib-set-search { box-sizing: border-box; display: block; width: 100%; min-height:
|
|
972
|
+
.bib-set-search { box-sizing: border-box; display: block; width: 100%; height: var(--bib-input-height); min-height: var(--bib-input-height); padding: 0 30px 0 var(--bib-input-pad-inline); border: 0.5px solid var(--dsw-alias-border-l4); border-radius: var(--bib-input-radius); background: var(--dsw-alias-bg-layer-3); color: var(--dsw-alias-label-primary); font: inherit; font-size: var(--bib-input-size); line-height: 1.5; }
|
|
897
973
|
.bib-set-search::placeholder { color: var(--dsw-alias-label-tertiary); }
|
|
898
974
|
.bib-set-search:focus-visible { outline: 2px solid var(--bib-set-brand); outline-offset: 1px; }
|
|
899
|
-
.bib-set-count {
|
|
900
|
-
|
|
901
|
-
|
|
975
|
+
.bib-set-count { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; }
|
|
976
|
+
/* ===== 区块:完全照宿主插件详情页(dsh-client-ui-plugin-manager 的 X_2TxG_*)=====
|
|
977
|
+
.X_2TxG_detailSections { flex-direction:column; gap:32px } → .bib-settings 的 32px
|
|
978
|
+
.X_2TxG_detailSection { flex-direction:column; gap:12px } → .bib-set-card
|
|
979
|
+
.X_2TxG_sectionHead { align-items:baseline; gap:10px; padding:0 } → .bib-set-card-header
|
|
980
|
+
.X_2TxG_sectionTitle { 14/500/20 } → .bib-set-card-title
|
|
981
|
+
.X_2TxG_sectionCount { 12/18 二级色 } → .bib-set-card-desc / .bib-set-count
|
|
982
|
+
.X_2TxG_rows { flex-direction:column; gap:0; padding:0 } → .bib-set-field-list
|
|
983
|
+
.X_2TxG_row { padding:12px 2px; border-bottom:.5px solid border-l2 } → .bib-set-row
|
|
984
|
+
.X_2TxG_row:last-child { border-bottom:0 }
|
|
985
|
+
.X_2TxG_rowLine { align-items:center; gap:16px } → .bib-set-row-main 的列间距
|
|
986
|
+
.X_2TxG_rowId { 13.5/500/20 } → .bib-set-rowTitle
|
|
987
|
+
===== 实测数字(2026-09-22,宿主 0.1.7-alpha.1)=====
|
|
988
|
+
.X_2TxG_row{padding:12px 2px; border-bottom:.5px solid var(--dsw-alias-border-l2)}
|
|
989
|
+
.X_2TxG_row:last-child{border-bottom:0}
|
|
990
|
+
.X_2TxG_rowLine{align-items:center; gap:16px; min-width:0}
|
|
991
|
+
.X_2TxG_rowMain{flex-direction:column; flex:1; gap:2px; min-width:0}
|
|
992
|
+
.X_2TxG_rowId{font-size:13.5px; font-weight:500; line-height:20px}
|
|
993
|
+
注意:行**没有** margin:0 -8px,也**没有**圆角和 hover 填充——那是插件列表的
|
|
994
|
+
.X_2TxG_card 那一套,不是详情页的行。 */
|
|
995
|
+
.bib-set-card { --bib-set-surface: transparent; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--bib-sec-inner); flex: 0 0 auto; width: 100%; inline-size: 100%; max-width: 100%; max-inline-size: 100%; min-width: 0; min-inline-size: 0; overflow: visible; border: 0; background: transparent; border-radius: 0; }
|
|
996
|
+
.bib-set-card-header { appearance: none; box-sizing: border-box; display: flex; flex-direction: column; gap: 0; width: 100%; margin: 0; padding: 0; border: 0; background: transparent; color: inherit; font: inherit; text-align: left; }
|
|
902
997
|
.bib-set-card-header:not(.bib-set-card-header--static) { cursor: pointer; user-select: none; -webkit-user-select: none; }
|
|
903
|
-
.bib-set-card-header:not(.bib-set-card-header--static):hover { background:
|
|
904
|
-
.bib-set-card-header:not(.bib-set-card-header--static):focus-visible { outline: 2px solid var(--bib-set-brand); outline-offset:
|
|
998
|
+
.bib-set-card-header:not(.bib-set-card-header--static):hover { background: transparent; }
|
|
999
|
+
.bib-set-card-header:not(.bib-set-card-header--static):focus-visible { outline: 2px solid var(--bib-set-brand); outline-offset: 2px; }
|
|
905
1000
|
.bib-set-card-header--static { cursor: default; }
|
|
906
|
-
|
|
907
|
-
.bib-set-card-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
.bib-set-
|
|
911
|
-
.bib-set-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
.bib-set-
|
|
916
|
-
.bib-set-
|
|
917
|
-
.bib-set-
|
|
918
|
-
|
|
919
|
-
|
|
1001
|
+
/* 标题与折叠箭头同一基线、间距 10px(照原生 sectionHead 的 align-items:baseline + gap:10px)。 */
|
|
1002
|
+
.bib-set-card-header-main { display: flex; align-items: baseline; justify-content: flex-start; gap: var(--bib-head-gap); width: 100%; min-width: 0; }
|
|
1003
|
+
/* 实测基线(宿主插件详情页):区块标题 14/500/20;行标题 13.5/500/20(原生 .rowId);
|
|
1004
|
+
区块说明 13/20 二级色(原生 pageIntro);行说明 12/18 三级色(原生 .detailName)。 */
|
|
1005
|
+
.bib-set-card-title { min-width: 0; margin: 0; font-size: var(--bib-title-size); font-weight: var(--bib-title-weight); line-height: var(--bib-title-line); color: var(--dsw-alias-label-primary); }
|
|
1006
|
+
.bib-set-card-desc { width: 100%; min-width: 0; margin: 0; font-size: var(--bib-desc-size); line-height: var(--bib-desc-line); color: var(--dsw-alias-label-secondary); }
|
|
1007
|
+
/* 字段清单以 grid-template-rows 0fr→1fr 展开:高度本身参与过渡,不再用
|
|
1008
|
+
max-height 巨值把内容瞬间撑开、再让淡入/位移拖满 220ms(观感拖沓的来源)。
|
|
1009
|
+
仍然保持挂载(不用 display:none),避免宿主 WebView 重新计算固有宽度。 */
|
|
1010
|
+
.bib-set-collapse { display: grid; grid-template-rows: 0fr; width: 100%; inline-size: 100%; max-width: 100%; max-inline-size: 100%; min-width: 0; min-inline-size: 0; overflow: hidden; opacity: 0; visibility: hidden; contain: layout paint; transition: grid-template-rows 150ms cubic-bezier(0.4, 0, 0.2, 1), opacity 120ms ease, visibility 0s linear 150ms; }
|
|
1011
|
+
.bib-set-collapse--expanded { grid-template-rows: 1fr; opacity: 1; visibility: visible; transition: grid-template-rows 150ms cubic-bezier(0.4, 0, 0.2, 1), opacity 120ms ease 30ms, visibility 0s linear 0s; }
|
|
1012
|
+
.bib-set-collapse-inner { width: 100%; inline-size: 100%; max-width: 100%; max-inline-size: 100%; min-width: 0; min-inline-size: 0; min-height: 0; overflow: visible; }
|
|
1013
|
+
/* 字段清单跟随唯一的宿主滚动层,避免覆盖式滚动条彼此重叠。
|
|
1014
|
+
形态照原生 .X_2TxG_rows:纵向 flex、gap 0(行的分隔靠 .5px 下边线,不靠间距)。 */
|
|
1015
|
+
.bib-set-field-list { display: flex; flex-direction: column; gap: 0; width: 100%; inline-size: 100%; max-width: 100%; max-inline-size: 100%; min-width: 0; min-inline-size: 0; max-height: none; overflow: visible; }
|
|
1016
|
+
.bib-set-body { width: 100%; inline-size: 100%; max-width: 100%; max-inline-size: 100%; min-width: 0; min-inline-size: 0; box-sizing: border-box; margin: 0; padding: 0; background: transparent; }
|
|
1017
|
+
.bib-set-empty { margin: 0; padding: 16px 0; text-align: center; color: var(--dsw-alias-label-tertiary); font-size: 13px; line-height: 20px; }
|
|
1018
|
+
/* ===== 内容分组 =====
|
|
1019
|
+
这是“可进入的设置分组”,不是一行注释。采用受 macOS 设置启发的分组表单:
|
|
1020
|
+
一整块可点击的表面、标题/状态/动作文字三层信息,以及稳定可见的“展开/收起”。
|
|
1021
|
+
这样用户不会把一个小箭头旁的文字误认为说明文案。 */
|
|
1022
|
+
/* 两个可展开分组是并列的独立入口,留出 16px 呼吸空间,不能像同一列表行挤在一起。 */
|
|
1023
|
+
.bib-set-field-list { gap: var(--bib-group-gap); }
|
|
1024
|
+
.bib-set-group { display: flex; flex-direction: column; gap: 0; width: 100%; inline-size: 100%; max-width: 100%; max-inline-size: 100%; min-width: 0; min-inline-size: 0; overflow: hidden; border: 0.5px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.16)); border-radius: var(--bib-control-radius); background: var(--dsw-alias-bg-layer-3, rgba(128,128,128,0.06)); }
|
|
1025
|
+
.bib-set-group--expanded { background: var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.03)); }
|
|
1026
|
+
.bib-set-group-head { display: block; min-width: 0; }
|
|
1027
|
+
.bib-set-group-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--bib-title-size); font-weight: var(--bib-title-weight); line-height: var(--bib-title-line); color: var(--dsw-alias-label-primary); }
|
|
1028
|
+
.bib-set-group-fallback-head { appearance: none; display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 12px; width: 100%; min-width: 0; min-height: 48px; box-sizing: border-box; margin: 0; padding: 8px 12px; border: 0; border-radius: 0; background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; transition: background-color 120ms ease; }
|
|
1029
|
+
.bib-set-group-fallback-head:hover { background: var(--dsw-alias-fill-tsp-secondary, rgba(128,128,128,0.08)); }
|
|
1030
|
+
.bib-set-group-fallback-head:focus-visible { position: relative; z-index: 1; outline: 2px solid var(--bib-set-brand); outline-offset: -2px; }
|
|
1031
|
+
.bib-set-group--expanded .bib-set-group-fallback-head { border-bottom: var(--bib-rule); }
|
|
1032
|
+
.bib-set-group-action { display: inline-flex; align-items: center; gap: 4px; flex: none; color: var(--bib-set-brand); font-size: 13px; font-weight: 500; line-height: 20px; white-space: nowrap; }
|
|
1033
|
+
.bib-set-group-action .bib-set-chevron { color: currentColor; }
|
|
1034
|
+
.bib-set-group .bib-set-body { padding: 0 12px 2px; }
|
|
1035
|
+
/* 行 = 原生详情页的 .X_2TxG_row:padding 12px 2px、下边线 .5px、最后一行无线、
|
|
1036
|
+
无圆角、无 hover 填充、无负外边距。
|
|
1037
|
+
之前照插件列表的 .X_2TxG_card 写成「margin 0 -8px + padding 8px + r12」,又被
|
|
1038
|
+
overflow:hidden 的折叠容器裁掉右侧控件;后来改成「行不越界 + 内容左右各内缩 8px」,
|
|
1039
|
+
虽然不裁了,却把整行文字推离了宿主的 323.2 左边界。现在按详情页行的真实值来,
|
|
1040
|
+
既对齐又不越界。 */
|
|
1041
|
+
.bib-set-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--bib-row-gap); width: 100%; min-width: 0; box-sizing: border-box; margin: 0; padding: var(--bib-row-pad-block) var(--bib-row-pad-inline); border: 0; border-bottom: var(--bib-rule); border-radius: 0; }
|
|
1042
|
+
.bib-set-row:last-child { border-bottom: 0; }
|
|
1043
|
+
.bib-set-row--field { flex-direction: column; align-items: stretch; gap: 0; }
|
|
920
1044
|
.bib-set-row--language { justify-content: flex-end; }
|
|
921
|
-
/*
|
|
922
|
-
|
|
923
|
-
|
|
1045
|
+
/* 字段行 = 单行网格:「标签」col1、「开关」col2、「色块」col3。不再有第二行
|
|
1046
|
+
(决策 1:调色板收进色块弹层;决策 3:时区/格式/自定义文字拆去独立设置区)。
|
|
1047
|
+
fallback(宿主无 Menu)时控件块仍按 grid-column: 1 / -1 落第二行,见 .bib-set-controls--field。 */
|
|
1048
|
+
.bib-set-row-main { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; column-gap: var(--bib-row-gap); row-gap: 8px; align-items: start; width: 100%; min-width: 0; }
|
|
924
1049
|
.bib-set-rowText { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
925
1050
|
.bib-set-rowText--field { min-width: 0; }
|
|
926
|
-
|
|
927
|
-
.bib-set-
|
|
928
|
-
.bib-set-rowDesc { font-size:
|
|
1051
|
+
/* 行标题照原生 .X_2TxG_rowId:13.5/500/20。 */
|
|
1052
|
+
.bib-set-rowTitle { display: flex; align-items: center; gap: 6px; font-size: var(--bib-row-label-size); font-weight: var(--bib-row-label-weight); line-height: var(--bib-row-label-line); color: var(--dsw-alias-label-primary); }
|
|
1053
|
+
.bib-set-rowDesc { font-size: var(--bib-row-hint-size); line-height: var(--bib-row-hint-line); color: var(--dsw-alias-label-tertiary); }
|
|
1054
|
+
/* ===== 原生组件衔接 ===== */
|
|
1055
|
+
/* 开关:宿主原生 Switch 自带 track/thumb 几何,插件只允许给它布局类声明。
|
|
1056
|
+
宿主开关规则(._switch_*)与 .bib-set-switch-host 特异性同为 (0,1,0),而插件 <style>
|
|
1057
|
+
后插入 → 任何外观声明都会反过来压过宿主:OFF 态轨道 background 被清成透明(整列开关
|
|
1058
|
+
看不见,实测对比度 light 1.00:1 / dark 1.02:1)、轨道 padding 被清零(滑块 18/2 →
|
|
1059
|
+
16/4,2px 错位)。故原生分支只挂 .bib-set-switch-host;本规则体内禁止出现
|
|
1060
|
+
appearance/background/border/padding/margin/transform/opacity/width/height/
|
|
1061
|
+
border-radius 等任何外观或尺寸声明。 */
|
|
1062
|
+
.bib-set-switch-host { flex: none; }
|
|
1063
|
+
/* 原生 Switch 的行内定位:它目前靠 CSS 网格自动放置「恰好」落在 (1,2),但这是顺序依赖的隐式
|
|
1064
|
+
定位——同容器里 .bib-set-controls--field 带 grid-column: 1 / -1,一旦有人把 controls 挪到
|
|
1065
|
+
switch 之前,自动放置游标就会把开关挤到第二行。本规则只做定位、不含任何外观或尺寸声明
|
|
1066
|
+
(外观一律交还宿主,见上),把开关的行内位置显式钉死,不再取决于兄弟节点的书写顺序。
|
|
1067
|
+
旧版写死的正是 grid-column: 2; grid-row: 1; align-self: start;,本次按新类名恢复回来。 */
|
|
1068
|
+
.bib-set-row-main > .bib-set-switch-host { grid-column: 2; grid-row: 1; align-self: start; }
|
|
1069
|
+
/* 色块(决策 1)的行内定位:与开关同一套显式钉死思路,落在 col3。
|
|
1070
|
+
两个选择器成对写是因为原生 Menu 传了 portal:true——弹层去 body,但锚点根节点
|
|
1071
|
+
到底以 .bib-set-color-host 还是 .bib-set-swatch 出现在行内,取决于宿主 Menu
|
|
1072
|
+
是否再包一层;两个都钉死,哪个是网格直接子级就用哪个,不赌实现细节。
|
|
1073
|
+
本规则体同样只做定位、不含任何外观或尺寸声明——色块本体(.bib-set-swatch)
|
|
1074
|
+
是插件自绘控件,外观声明写在自己的规则里,不经过宿主原生组件。 */
|
|
1075
|
+
.bib-set-row-main > .bib-set-color-host, .bib-set-row-main > .bib-set-swatch { grid-column: 3; grid-row: 1; align-self: start; }
|
|
1076
|
+
/* 时区下拉:锚点是原生 Button,展开的是原生 Menu 卡片(替代系统自带的 <select>)。 */
|
|
1077
|
+
.bib-set-select { display: inline-flex; flex: none; min-width: 0; }
|
|
1078
|
+
.bib-set-select-trigger { justify-content: space-between; gap: 6px; min-width: 160px; max-width: 100%; }
|
|
1079
|
+
.bib-set-select-value { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1080
|
+
.bib-set-select-chevron { flex: none; color: var(--dsw-alias-label-tertiary); }
|
|
1081
|
+
.bib-set-select-trigger--open .bib-set-select-chevron { color: var(--dsw-alias-label-primary); }
|
|
1082
|
+
/* ===== 提示条:照宿主插件详情页的三种原生形态 ===== */
|
|
1083
|
+
/* 错误:.X_2TxG_failure(错误色 + 行内 + gap 10)+ .X_2TxG_reason(12/18、可任意换行) */
|
|
1084
|
+
.bib-set-alerts { display: flex; flex-direction: column; gap: 12px; width: 100%; min-width: 0; padding: 0; }
|
|
1085
|
+
.bib-set-alert { width: 100%; min-width: 0; box-sizing: border-box; margin: 0; font-size: 12px; line-height: 18px; }
|
|
1086
|
+
/* 错误:完全照原生 .X_2TxG_failure(行内 flex + 错误色 + gap 10)+ .X_2TxG_reason(12/18 可换行)。
|
|
1087
|
+
左边界与区块标题一致(都是容器左边缘),不再有自己的内缩。 */
|
|
1088
|
+
.bib-set-alert--error { display: flex; align-items: center; gap: 10px; color: var(--dsw-alias-state-error-primary, var(--dsw-alias-label-error, #d92d20)); overflow-wrap: anywhere; white-space: pre-wrap; }
|
|
1089
|
+
/* 警示:.X_2TxG_banner(12% 警示色底、r10、8px 12px 内边距) */
|
|
1090
|
+
.bib-set-alert--warning { background: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #f59e0b) 12%, transparent); color: var(--dsw-alias-label-primary); border-radius: var(--bib-control-radius); padding: 8px 12px; }
|
|
1091
|
+
/* 信息/加载中:安静的一行(原生 sectionCount 的字号与色阶) */
|
|
1092
|
+
.bib-set-alert--info { color: var(--dsw-alias-label-secondary); }
|
|
1093
|
+
.bib-set-statedot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: var(--dsw-alias-label-tertiary); }
|
|
1094
|
+
.bib-set-statedot--done { background: var(--dsw-alias-state-success-primary, #087f5b); }
|
|
1095
|
+
.bib-set-statedot--warning { background: var(--dsw-alias-state-warning-primary, #f59e0b); }
|
|
1096
|
+
.bib-set-statedot--error { background: var(--dsw-alias-state-error-primary, #d92d20); }
|
|
929
1097
|
/* 只使用 DSH 已验证的原生下箭头;展开态旋转 SVG 本身,确保跨宿主版本仍是上下方向。 */
|
|
930
1098
|
.bib-set-chevron { display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box; width: 14px; height: 14px; margin: 0; flex: none; color: var(--dsw-alias-label-tertiary); pointer-events: none; }
|
|
931
1099
|
.bib-set-card-header:not(.bib-set-card-header--static):hover .bib-set-chevron, .bib-set-card-header:not(.bib-set-card-header--static):focus-visible .bib-set-chevron { color: var(--dsw-alias-label-primary); }
|
|
932
|
-
.bib-set-chevron-icon { display: block; width: 14px; height: 14px; transform-box: fill-box; transform-origin: center; backface-visibility: hidden; will-change: transform; transition: transform
|
|
1100
|
+
.bib-set-chevron-icon { display: block; width: 14px; height: 14px; transform-box: fill-box; transform-origin: center; backface-visibility: hidden; will-change: transform; transition: transform 160ms cubic-bezier(0.32, 0.72, 0, 1), color 120ms ease; }
|
|
933
1101
|
.bib-set-chevron-icon--expanded { transform: rotate(180deg); }
|
|
934
|
-
/*
|
|
935
|
-
|
|
1102
|
+
/* 兜底箭头:宿主 primitives 两边都取不到图标时使用(见 BIB_SET_CHEVRON_ICON)。
|
|
1103
|
+
尺寸/描边对齐 14px 图标观感,方向由父级 data-expanded 驱动,不依赖任何宿主类名。 */
|
|
1104
|
+
.bib-set-chevron-glyph { display: block; width: 6px; height: 6px; margin-top: -3px; border-right: 1.6px solid currentColor; border-bottom: 1.6px solid currentColor; transform: rotate(45deg); backface-visibility: hidden; transition: transform 160ms cubic-bezier(0.32, 0.72, 0, 1), color 120ms ease; }
|
|
1105
|
+
.bib-set-chevron[data-expanded="true"] .bib-set-chevron-glyph { transform: rotate(225deg); }
|
|
1106
|
+
/* 开关:本规则只服务插件自己的兜底 <button>(宿主 primitives 取不到原生 Switch 时才走这条路径)。
|
|
1107
|
+
原生分支不得使用 .bib-set-switch 这个类名——它带外观声明,会以「同特异性 + 后插入」压过宿主,
|
|
1108
|
+
详见上方「原生组件衔接」处的说明。兜底几何对齐宿主原生 Switch
|
|
1109
|
+
(36×20 轨道 + 16px 圆钮 + 10px 圆角),语义 = role:switch + aria-checked */
|
|
1110
|
+
.bib-set-switch { appearance: none; background: 0 0; border: 0; padding: 0; margin: 0; cursor: pointer; display: inline-flex; flex: none; border-radius: 10px; }
|
|
936
1111
|
.bib-set-switch:disabled { cursor: default; opacity: 0.5; }
|
|
937
1112
|
.bib-set-switch:focus-visible { outline: 2px solid var(--bib-set-brand); outline-offset: 2px; }
|
|
938
|
-
.bib-set-switch-track { position: relative; display: inline-block; box-sizing: border-box; width:
|
|
939
|
-
.bib-set-switch-track[data-on="true"] { background: var(--bib-set-brand); }
|
|
940
|
-
.bib-set-switch-thumb { position: absolute; top: 2px; left: 2px; width:
|
|
1113
|
+
.bib-set-switch-track { position: relative; display: inline-block; box-sizing: border-box; width: 36px; height: 20px; border-radius: 10px; background: var(--dsw-alias-border-l3, rgba(128,128,128,0.4)); transition: background-color 120ms ease; }
|
|
1114
|
+
.bib-set-switch-track[data-on="true"] { background: var(--dsw-alias-brand-primary, var(--bib-set-brand)); }
|
|
1115
|
+
.bib-set-switch-thumb { position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: var(--dsw-alias-label-primary-foreground, #fff); box-shadow: 0 1px 2px rgba(0,0,0,0.2); transition: transform 120ms ease; }
|
|
941
1116
|
.bib-set-switch-track[data-on="true"] .bib-set-switch-thumb { transform: translateX(16px); }
|
|
942
1117
|
/* 色板圆点:role:radio + roving tabindex(方向键/Home/End 可达),选中态外圈描边 */
|
|
943
1118
|
.bib-set-row-main > .bib-set-switch { grid-column: 2; grid-row: 1; align-self: start; }
|
|
944
1119
|
.bib-set-controls { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 6px; min-width: 0; }
|
|
945
1120
|
.bib-set-controls--field { grid-column: 1 / -1; justify-content: flex-start; }
|
|
946
1121
|
.bib-set-controls--language { margin-left: auto; }
|
|
947
|
-
|
|
948
|
-
|
|
1122
|
+
/* ===== 独立参数设置区(决策 3)=====
|
|
1123
|
+
「时间与日期」「自定义文字」两个字段块照宿主 settings-form fields.module.css 基线:
|
|
1124
|
+
.field 列向 + gap 6 + padding 12px 0;相邻字段块之间 .5px 细线;label 13/500;
|
|
1125
|
+
输入框 34px / r8 / 0 12px;hint 12/1.5。全部度量走 --bib-* token。 */
|
|
1126
|
+
.bib-set-fieldblocks { display: flex; flex-direction: column; gap: 0; width: 100%; inline-size: 100%; max-width: 100%; max-inline-size: 100%; min-width: 0; min-inline-size: 0; box-sizing: border-box; }
|
|
1127
|
+
.bib-set-fieldblock { display: flex; flex-direction: column; gap: var(--bib-field-gap); min-width: 0; box-sizing: border-box; padding: var(--bib-field-pad-block) 0; }
|
|
1128
|
+
.bib-set-fieldblock + .bib-set-fieldblock { border-top: var(--bib-rule); }
|
|
1129
|
+
.bib-set-fieldblock-label { flex: none; min-width: 0; font-size: var(--bib-field-label-size); font-weight: var(--bib-field-label-weight); line-height: var(--bib-field-label-line); color: var(--dsw-alias-label-primary); }
|
|
1130
|
+
.bib-set-fieldblock-hint { margin: 0; font-size: var(--bib-field-hint-size); line-height: var(--bib-field-hint-line); color: var(--dsw-alias-label-tertiary); overflow-wrap: anywhere; }
|
|
949
1131
|
.bib-set-time-parts { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 8px; max-width: 100%; min-width: 0; }
|
|
950
1132
|
.bib-set-time-part { display: inline-flex; align-items: center; gap: 4px; }
|
|
951
|
-
.bib-set-time-part-label { font-size:
|
|
952
|
-
.bib-set-
|
|
953
|
-
.bib-set-custom-text-input { flex: 1 1 200px; box-sizing: border-box; padding: 6px 10px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 8px; background: var(--dsw-alias-bg-layer-2); color: var(--dsw-alias-label-primary); font: inherit; font-size: 12px; line-height: 18px; }
|
|
1133
|
+
.bib-set-time-part-label { font-size: var(--bib-row-hint-size); line-height: var(--bib-row-hint-line); color: var(--dsw-alias-label-secondary); }
|
|
1134
|
+
.bib-set-custom-text-input { box-sizing: border-box; width: 100%; height: var(--bib-input-height); padding: 0 var(--bib-input-pad-inline); border: 0.5px solid var(--dsw-alias-border-l4); border-radius: var(--bib-input-radius); background: var(--dsw-alias-bg-layer-3); color: var(--dsw-alias-label-primary); font: inherit; font-size: var(--bib-input-size); line-height: 1.5; }
|
|
954
1135
|
.bib-set-custom-text-input:focus-visible, .bib-set-time-zone:focus-visible { outline: 2px solid var(--bib-set-brand); outline-offset: 1px; }
|
|
955
|
-
.bib-set-custom-text-count { color: var(--dsw-alias-label-tertiary); font-size:
|
|
956
|
-
.bib-set-time-zone { box-sizing: border-box; max-width: 100%; min-width: 0; padding:
|
|
957
|
-
.bib-set-dots { display: inline-flex; align-items: center; gap:
|
|
958
|
-
|
|
1136
|
+
.bib-set-custom-text-count { color: var(--dsw-alias-label-tertiary); font-size: var(--bib-row-hint-size); line-height: var(--bib-row-hint-line); white-space: nowrap; }
|
|
1137
|
+
.bib-set-time-zone { box-sizing: border-box; height: var(--bib-input-height); max-width: 100%; min-width: 0; padding: 0 var(--bib-input-pad-inline); border: 0.5px solid var(--dsw-alias-border-l4); border-radius: var(--bib-input-radius); background: var(--dsw-alias-bg-layer-3); color: var(--dsw-alias-label-primary); font: inherit; font-size: var(--bib-input-size); line-height: 1.5; }
|
|
1138
|
+
.bib-set-dots { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; }
|
|
1139
|
+
/* 色板:预设色不再描边(描边 + 虚线圈是「手绘控件」的观感),选中用宿主主文字色的双环,
|
|
1140
|
+
hover 用一层浅环;「默认」用一个带斜杠的空圈,与原生「无/恢复默认」的语义一致。 */
|
|
1141
|
+
.bib-set-dot { appearance: none; width: 22px; height: 22px; padding: 0; margin: 0; border-radius: 50%; border: 0; background: transparent; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; transition: box-shadow 120ms var(--ds-ease-in-out, ease); }
|
|
1142
|
+
.bib-set-dot:hover { box-shadow: 0 0 0 2px var(--dsw-alias-fill-tsp-secondary, rgba(128,128,128,0.16)); }
|
|
959
1143
|
.bib-set-dot:focus-visible { outline: 2px solid var(--bib-set-brand); outline-offset: 2px; }
|
|
960
|
-
.bib-set-dot[aria-checked="true"] { box-shadow: 0 0 0 2px var(--dsw-alias-bg-layer-
|
|
961
|
-
.bib-set-dot-core { display: block; width:
|
|
962
|
-
|
|
1144
|
+
.bib-set-dot[aria-checked="true"] { box-shadow: 0 0 0 2px var(--dsw-alias-bg-layer-2, #fff), 0 0 0 4px var(--dsw-alias-label-primary); }
|
|
1145
|
+
.bib-set-dot-core { display: block; width: 16px; height: 16px; border-radius: 50%; }
|
|
1146
|
+
/* 「默认」色点描边改用 label-tertiary:border-l3 在浅色主题只有 12% 黑,16px 小圆上对比度约 1.3:1 等于看不见;label-tertiary 与中间那道斜线同令牌,浅色约 3.9:1、深色约 5:1,达标且随主题自动翻转。
|
|
1147
|
+
决策 1 后行内色块的未设置态与「默认」色点同语义:选择器成对扩展到 .bib-set-swatch--default。 */
|
|
1148
|
+
.bib-set-dot-default .bib-set-dot-core, .bib-set-swatch--default .bib-set-swatch-core { background: var(--dsw-alias-bg-layer-2, transparent); box-shadow: inset 0 0 0 1px var(--dsw-alias-label-tertiary, rgba(128,128,128,0.5)); position: relative; overflow: hidden; }
|
|
1149
|
+
.bib-set-dot-default .bib-set-dot-core::after, .bib-set-swatch--default .bib-set-swatch-core::after { content: ''; position: absolute; left: -2px; top: 50%; width: 20px; height: 1px; background: var(--dsw-alias-label-tertiary, rgba(128,128,128,0.5)); transform: rotate(-45deg); }
|
|
1150
|
+
/* ===== 行内色块(决策 1)=====
|
|
1151
|
+
字段行右侧唯一颜色入口:20px 圆角色块显示当前色(预设走 --bi-palette-*,
|
|
1152
|
+
自定义 hex 直填);未设置时显示与「默认」色点同语义的斜线态。
|
|
1153
|
+
点击打开原生 Menu 弹层(6 预设 + 自定义… + 恢复默认 + hex 输入)。 */
|
|
1154
|
+
.bib-set-color-host { display: inline-flex; flex: none; min-width: 0; }
|
|
1155
|
+
.bib-set-swatch { appearance: none; box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; width: var(--bib-swatch-size); height: var(--bib-swatch-size); padding: 0; margin: 0; border: 0.5px solid var(--dsw-alias-border-l4, rgba(128,128,128,0.4)); border-radius: var(--bib-swatch-radius); background: var(--dsw-alias-bg-layer-3, transparent); cursor: pointer; transition: box-shadow 120ms ease; }
|
|
1156
|
+
.bib-set-swatch:hover { box-shadow: 0 0 0 2px var(--dsw-alias-fill-tsp-secondary, rgba(128,128,128,0.16)); }
|
|
1157
|
+
.bib-set-swatch:focus-visible { outline: 2px solid var(--bib-set-brand); outline-offset: 2px; }
|
|
1158
|
+
.bib-set-swatch-core { display: block; width: calc(var(--bib-swatch-size) - 6px); height: calc(var(--bib-swatch-size) - 6px); border-radius: calc(var(--bib-swatch-radius) - 2px); }
|
|
1159
|
+
/* 色块弹层内的预设色点(Menu item icon) */
|
|
1160
|
+
.bib-set-menu-dot { display: inline-block; flex: none; width: var(--bib-menu-dot-size); height: var(--bib-menu-dot-size); border-radius: 50%; }
|
|
1161
|
+
/* 弹层底部 hex 输入行 + 隐藏的系统取色器(「自定义…」菜单项触发 .click()) */
|
|
1162
|
+
.bib-set-color-menu-extra { display: flex; align-items: center; gap: 6px; min-width: 0; box-sizing: border-box; padding: 6px 8px; }
|
|
1163
|
+
.bib-set-color-native-input { position: absolute; width: 1px; height: 1px; padding: 0; margin: 0; border: 0; opacity: 0; pointer-events: none; }
|
|
963
1164
|
/* 原生取色器色井:保留系统行为,仅样式化为圆角色井 */
|
|
964
1165
|
.bib-set-well { display: inline-flex; flex: none; }
|
|
965
|
-
.bib-set-well input[type="color"] { appearance: none; -webkit-appearance: none; box-sizing: border-box; width:
|
|
1166
|
+
.bib-set-well input[type="color"] { appearance: none; -webkit-appearance: none; box-sizing: border-box; width: 28px; height: 28px; padding: 3px; border: 0.5px solid var(--dsw-alias-border-l4, rgba(128,128,128,0.4)); border-radius: var(--bib-control-radius); background: var(--dsw-alias-bg-layer-3, transparent); cursor: pointer; }
|
|
966
1167
|
.bib-set-well input[type="color"]::-webkit-color-swatch-wrapper { padding: 2px; }
|
|
967
1168
|
.bib-set-well input[type="color"]::-webkit-color-swatch { border: none; border-radius: 5px; }
|
|
968
1169
|
.bib-set-well input[type="color"]::-moz-color-swatch { border: none; border-radius: 5px; }
|
|
969
1170
|
.bib-set-well input[type="color"]:focus-visible { outline: 2px solid var(--bib-set-brand); outline-offset: 2px; }
|
|
970
1171
|
/* hex 输入:等宽字体、即时校验(非法描红 + aria-invalid),Enter/失焦提交,非法回退 */
|
|
971
|
-
.bib-set-hex { box-sizing: border-box; width:
|
|
1172
|
+
.bib-set-hex { box-sizing: border-box; width: 96px; height: var(--bib-input-height); padding: 0 var(--bib-input-pad-inline); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: var(--bib-input-size); line-height: 1.5; color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-3, transparent); border: 0.5px solid var(--dsw-alias-border-l4, rgba(128,128,128,0.4)); border-radius: var(--bib-input-radius); }
|
|
972
1173
|
.bib-set-hex:focus-visible { outline: 2px solid var(--bib-set-brand); outline-offset: 1px; }
|
|
973
1174
|
.bib-set-hex[data-invalid="true"] { border-color: var(--dsw-alias-state-error-primary, var(--dsw-alias-label-error, #d92d20)); }
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
.bib-set-
|
|
1175
|
+
/* 页脚操作行已被「恢复默认」行取代:它现在是「显示内容」区块里的最后一个
|
|
1176
|
+
.bib-set-data-row(同「导出账单」的行几何),不再悬浮在页面右下角。 */
|
|
1177
|
+
.bib-set-reset-row { border-top: var(--bib-rule); border-bottom: 0; }
|
|
1178
|
+
.bib-set-reset-row .bib-set-data-copy { display: flex; flex-direction: column; gap: 2px; }
|
|
1179
|
+
/* 按钮优先渲染宿主原生 Button(.sm = h28 / r14 / 12px / 0 10px);
|
|
1180
|
+
下面这份只是宿主没有 Button 时的等价兜底。 */
|
|
1181
|
+
.bib-set-btn { appearance: none; font: inherit; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; height: var(--bib-btn-height); border: 0.5px solid var(--dsw-alias-border-l3); color: var(--dsw-alias-label-primary); background: transparent; border-radius: var(--bib-btn-radius); padding: 0 var(--bib-btn-pad-inline); font-size: var(--bib-btn-size); font-weight: 400; line-height: var(--bib-btn-line); transition: background-color 120ms ease, border-color 120ms ease; }
|
|
1182
|
+
.bib-set-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,0.08)); border-color: var(--dsw-alias-border-l3); }
|
|
977
1183
|
.bib-set-btn:focus-visible { outline: 2px solid var(--bib-set-brand); outline-offset: 2px; }
|
|
978
1184
|
.bib-set-btn:disabled { opacity: 0.5; cursor: default; }
|
|
979
|
-
.bib-set-alert { margin: 0; color: var(--dsw-alias-state-error-primary, var(--dsw-alias-label-error, #d92d20)); font-size: 12px; line-height: 18px; flex: 1 1 auto; min-width: 0; }
|
|
980
1185
|
.bib-set-notice { margin: 0; color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 18px; flex: 1 1 auto; min-width: 0; }
|
|
981
|
-
.bib-set-data-actions { width: 100%; min-width: 0; box-sizing: border-box;
|
|
982
|
-
|
|
1186
|
+
.bib-set-data-actions { display: flex; flex-direction: column; gap: 2px; width: 100%; min-width: 0; box-sizing: border-box; margin: 0; padding: 0; }
|
|
1187
|
+
/* 账单数据行同样是原生 .X_2TxG_row 的几何(12px 2px + .5px 下边线,末行无线)。 */
|
|
1188
|
+
.bib-set-data-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: var(--bib-row-gap); width: 100%; min-width: 0; padding: var(--bib-row-pad-block) var(--bib-row-pad-inline); border: 0; border-bottom: var(--bib-rule); border-radius: 0; }
|
|
1189
|
+
.bib-set-data-row:last-child { border-bottom: 0; }
|
|
983
1190
|
.bib-set-data-copy { min-width: 0; }
|
|
984
|
-
.bib-set-data-title { margin: 0 0 2px; color: var(--dsw-alias-label-primary); font-size:
|
|
985
|
-
.bib-set-data-desc { margin: 0; color: var(--dsw-alias-label-tertiary); font-size:
|
|
1191
|
+
.bib-set-data-title { margin: 0 0 2px; color: var(--dsw-alias-label-primary); font-size: var(--bib-row-label-size); font-weight: var(--bib-row-label-weight); line-height: var(--bib-row-label-line); }
|
|
1192
|
+
.bib-set-data-desc { margin: 0; color: var(--dsw-alias-label-tertiary); font-size: var(--bib-row-hint-size); line-height: var(--bib-row-hint-line); }
|
|
986
1193
|
.bib-set-data-button-group { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 6px; min-width: 0; }
|
|
987
1194
|
.bib-set-btn--destructive { border-color: var(--dsw-alias-state-error-primary, var(--dsw-alias-label-error, #d92d20)); color: var(--dsw-alias-state-error-primary, var(--dsw-alias-label-error, #d92d20)); }
|
|
988
1195
|
.bib-set-btn--destructive:hover { background: rgba(217,45,32,0.08); border-color: var(--dsw-alias-state-error-primary, var(--dsw-alias-label-error, #d92d20)); }
|
|
989
|
-
@media (max-width: 600px) { .bib-settings { gap:
|
|
990
|
-
@media (prefers-reduced-motion: reduce) { .bib-set-card-header, .bib-set-chevron-icon, .bib-set-collapse, .bib-set-btn, .bib-set-switch-track, .bib-set-switch-thumb { transition: none;
|
|
1196
|
+
@media (max-width: 600px) { .bib-settings { gap: 24px; } .bib-set-rowText { min-width: 0; flex-basis: 100%; } .bib-set-data-row { grid-template-columns: 1fr; gap: 10px; } .bib-set-data-button-group { justify-content: flex-start; } }
|
|
1197
|
+
@media (prefers-reduced-motion: reduce) { .bib-set-card-header, .bib-set-chevron-icon, .bib-set-chevron-glyph, .bib-set-collapse, .bib-set-btn, .bib-set-switch-track, .bib-set-switch-thumb { transition: none; } .bib-set-collapse--collapsed { grid-template-rows: 0fr; visibility: hidden; } .bib-set-collapse--expanded { grid-template-rows: 1fr; visibility: visible; } }
|
|
991
1198
|
`;
|
|
992
1199
|
document.head.appendChild(style);
|
|
993
1200
|
return function () { style.remove(); };
|
|
994
1201
|
}
|
|
995
1202
|
|
|
996
1203
|
// ---------- 控件 ----------
|
|
1204
|
+
// 开关:优先用宿主原生 Switch(几何/配色/动效由宿主维护);取不到时退回插件内实现,
|
|
1205
|
+
// 两者语义一致(role=switch + aria-checked)。
|
|
997
1206
|
function bibSetSwitch(props) {
|
|
998
1207
|
const checked = !!props.checked;
|
|
1208
|
+
if (BIB_SET_NATIVE_SWITCH) {
|
|
1209
|
+
return React.createElement(BIB_SET_NATIVE_SWITCH, {
|
|
1210
|
+
checked: checked,
|
|
1211
|
+
onChange: function (next) { if (props.onToggle) props.onToggle(next); },
|
|
1212
|
+
label: props.label,
|
|
1213
|
+
disabled: props.disabled === true,
|
|
1214
|
+
title: props.title,
|
|
1215
|
+
// 只挂布局类:绝不把插件的外观类名交给宿主原生 Switch(会反向覆盖宿主外观,见该规则处注释)
|
|
1216
|
+
className: 'bib-set-switch-host',
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
999
1219
|
return React.createElement('button', {
|
|
1000
1220
|
type: 'button',
|
|
1001
1221
|
className: 'bib-set-switch',
|
|
@@ -1010,6 +1230,54 @@ function bibSetSwitch(props) {
|
|
|
1010
1230
|
React.createElement('span', { className: 'bib-set-switch-thumb' })));
|
|
1011
1231
|
}
|
|
1012
1232
|
|
|
1233
|
+
// 按钮:优先用宿主原生 Button(size=sm 即 28px 胶囊,与插件详情页的原生按钮同一套几何)。
|
|
1234
|
+
// variant: 'outline' 普通、'primary' 主操作、'ghost' 无边框;danger 走宿主错误色令牌。
|
|
1235
|
+
function bibSetButton(props) {
|
|
1236
|
+
const variant = props.variant || (props.primary ? 'primary' : 'outline');
|
|
1237
|
+
const className = 'bib-set-btn'
|
|
1238
|
+
+ (props.destructive ? ' bib-set-btn--destructive' : '')
|
|
1239
|
+
+ (props.className ? ' ' + props.className : '');
|
|
1240
|
+
const children = props.children;
|
|
1241
|
+
if (BIB_SET_NATIVE_BUTTON) {
|
|
1242
|
+
return React.createElement(BIB_SET_NATIVE_BUTTON, {
|
|
1243
|
+
type: 'button',
|
|
1244
|
+
variant: variant,
|
|
1245
|
+
size: 'sm',
|
|
1246
|
+
className: className,
|
|
1247
|
+
disabled: props.disabled === true,
|
|
1248
|
+
title: props.title,
|
|
1249
|
+
onClick: props.onClick,
|
|
1250
|
+
}, children);
|
|
1251
|
+
}
|
|
1252
|
+
return React.createElement('button', {
|
|
1253
|
+
type: 'button',
|
|
1254
|
+
className: className,
|
|
1255
|
+
disabled: props.disabled === true,
|
|
1256
|
+
title: props.title,
|
|
1257
|
+
onClick: props.onClick,
|
|
1258
|
+
}, children);
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
// 状态点:优先用宿主原生 StateDot(done/ongoing/warning/error/idle)。
|
|
1262
|
+
function bibSetStateDot(props) {
|
|
1263
|
+
if (BIB_SET_NATIVE_STATE_DOT) {
|
|
1264
|
+
return React.createElement(BIB_SET_NATIVE_STATE_DOT, { state: props.state, className: props.className });
|
|
1265
|
+
}
|
|
1266
|
+
return React.createElement('span', { className: 'bib-set-statedot bib-set-statedot--' + props.state, 'aria-hidden': 'true' });
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
// 提示条:照宿主插件详情页的三种原生形态做——
|
|
1270
|
+
// error → .X_2TxG_failure(错误色、行内、gap 10)+ .X_2TxG_reason(12/18、可换行)
|
|
1271
|
+
// warning → .X_2TxG_banner(12% 警示色底、r10、8px 12px 内边距、12/18)
|
|
1272
|
+
// info → 中性 12/18 次要色
|
|
1273
|
+
// 原先是一段裸色文字贴在标题旁边,既没有形状也和页面其它部分对不齐。
|
|
1274
|
+
function bibSetAlert(props) {
|
|
1275
|
+
const tone = props.tone || 'info';
|
|
1276
|
+
const className = 'bib-set-alert bib-set-alert--' + tone + (props.className ? ' ' + props.className : '');
|
|
1277
|
+
const role = tone === 'error' ? 'alert' : 'status';
|
|
1278
|
+
return React.createElement('div', { className: className, role: role }, props.children);
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1013
1281
|
// 色板圆点组:默认 + 预设色名;role=radiogroup/radio + roving tabindex(方向键/Home/End)
|
|
1014
1282
|
function bibSetPalette(props) {
|
|
1015
1283
|
const options = ['default'].concat(PRESET_COLORS);
|
|
@@ -1055,6 +1323,46 @@ function bibSetPalette(props) {
|
|
|
1055
1323
|
return React.createElement('span', { className: 'bib-set-dots', role: 'radiogroup', 'aria-label': props.label }, children);
|
|
1056
1324
|
}
|
|
1057
1325
|
|
|
1326
|
+
// 时区选择:原先用浏览器原生 <select>,那是操作系统自带的古老控件(灰色直角框 + 系统菜单),
|
|
1327
|
+
// 与 DSH 的观感完全脱节。这里优先用宿主原生 Menu(锚点 = 原生 Button,列表 = 原生菜单卡片),
|
|
1328
|
+
// 宿主没有 Menu 时才退回 <select>,保证老版本仍可用。
|
|
1329
|
+
function bibSetTimeZonePicker(props) {
|
|
1330
|
+
const [open, setOpen] = React.useState(false);
|
|
1331
|
+
const value = props.value;
|
|
1332
|
+
if (!BIB_SET_NATIVE_MENU) {
|
|
1333
|
+
return React.createElement('select', {
|
|
1334
|
+
className: 'bib-set-time-zone',
|
|
1335
|
+
value: value,
|
|
1336
|
+
'aria-label': props.label,
|
|
1337
|
+
onChange: function (event) { props.onChange(event.target.value); },
|
|
1338
|
+
}, TIME_ZONE_OPTIONS.map(function (z) { return React.createElement('option', { key: z, value: z }, z); }));
|
|
1339
|
+
}
|
|
1340
|
+
const items = TIME_ZONE_OPTIONS.map(function (z) { return { id: z, label: z }; });
|
|
1341
|
+
const anchor = bibSetButton({
|
|
1342
|
+
variant: 'outline',
|
|
1343
|
+
className: 'bib-set-select-trigger' + (open ? ' bib-set-select-trigger--open' : ''),
|
|
1344
|
+
title: props.label,
|
|
1345
|
+
onClick: function () { setOpen(!open); },
|
|
1346
|
+
children: [
|
|
1347
|
+
React.createElement('span', { key: 'v', className: 'bib-set-select-value' }, value),
|
|
1348
|
+
BIB_SET_CHEVRON_ICON
|
|
1349
|
+
? React.createElement(BIB_SET_CHEVRON_ICON, { key: 'c', size: 14, className: 'bib-set-select-chevron' })
|
|
1350
|
+
: React.createElement('span', { key: 'c', className: 'bib-set-chevron-glyph bib-set-select-chevron' }),
|
|
1351
|
+
],
|
|
1352
|
+
});
|
|
1353
|
+
return React.createElement(BIB_SET_NATIVE_MENU, {
|
|
1354
|
+
open: open,
|
|
1355
|
+
anchor: anchor,
|
|
1356
|
+
items: items,
|
|
1357
|
+
selectedId: value,
|
|
1358
|
+
onSelect: function (id) { setOpen(false); props.onChange(id); },
|
|
1359
|
+
onClose: function () { setOpen(false); },
|
|
1360
|
+
align: 'start',
|
|
1361
|
+
portal: true,
|
|
1362
|
+
className: 'bib-set-select',
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1058
1366
|
// ---------- 页面组件 ----------
|
|
1059
1367
|
// M2 首渲骨架:页面标题行——任何状态下都先输出可见标题(数据未到、加载失败也一样有东西看)
|
|
1060
1368
|
function bibSetPageTitle() {
|
|
@@ -1063,15 +1371,22 @@ function bibSetPageTitle() {
|
|
|
1063
1371
|
|
|
1064
1372
|
function bibSetChevron(props) {
|
|
1065
1373
|
const iconClass = 'bib-set-chevron-icon' + (props.expanded ? ' bib-set-chevron-icon--expanded' : '');
|
|
1374
|
+
// 宿主图标缺失时退回同一位置上的 CSS 箭头(见 .bib-set-chevron-glyph):
|
|
1375
|
+
// 宁可少一个图形,也不让 React 收到 undefined 类型的元素。
|
|
1376
|
+
const glyph = BIB_SET_CHEVRON_ICON
|
|
1377
|
+
? React.createElement(BIB_SET_CHEVRON_ICON, { size: 14, className: iconClass })
|
|
1378
|
+
: React.createElement('span', { className: 'bib-set-chevron-glyph' });
|
|
1066
1379
|
return React.createElement('span', {
|
|
1067
1380
|
className: 'bib-set-chevron',
|
|
1068
1381
|
'data-expanded': props.expanded ? 'true' : 'false',
|
|
1069
1382
|
'aria-hidden': 'true',
|
|
1070
|
-
},
|
|
1383
|
+
}, glyph);
|
|
1071
1384
|
}
|
|
1072
1385
|
|
|
1073
1386
|
function bibSetCardHeader(props) {
|
|
1074
1387
|
const className = 'bib-set-card-header' + (props.static ? ' bib-set-card-header--static' : '');
|
|
1388
|
+
// 标题与折叠箭头同一行、左对齐(对齐原生 X_2TxG_sectionHead 的「标题 + 计数」排布);
|
|
1389
|
+
// 箭头紧贴标题,不再甩到整行最右端留出几百像素空白。
|
|
1075
1390
|
const title = React.createElement('span', {
|
|
1076
1391
|
id: props.titleId,
|
|
1077
1392
|
className: 'bib-set-card-title',
|
|
@@ -1092,14 +1407,101 @@ function bibSetCardHeader(props) {
|
|
|
1092
1407
|
}, main, description);
|
|
1093
1408
|
}
|
|
1094
1409
|
|
|
1410
|
+
// 行内色块 + Menu 弹层(决策 1):
|
|
1411
|
+
// 字段行右侧唯一颜色入口。弹层项依次为 6 个预设色(带色点)、「自定义…」(触发系统
|
|
1412
|
+
// <input type="color">)、「恢复默认」;弹层底部是 hex 输入框(Enter/失焦提交,非法回退)。
|
|
1413
|
+
// 仅在宿主有原生 Menu 时走本组件;没有 Menu 时 bibSetFieldRow 退回旧的行内控件。
|
|
1414
|
+
function bibSetColorPicker(props) {
|
|
1415
|
+
const [open, setOpen] = React.useState(false);
|
|
1416
|
+
const customInputRef = React.useRef(null);
|
|
1417
|
+
const value = props.value; // null = 默认 | 预设色名 | '#RRGGBB'
|
|
1418
|
+
const isDefault = value === null;
|
|
1419
|
+
const isPreset = !isDefault && PRESET_COLOR_SET.has(value);
|
|
1420
|
+
const swatchStyle = {};
|
|
1421
|
+
if (!isDefault) swatchStyle.background = isPreset ? 'var(--bi-palette-' + value + ')' : value;
|
|
1422
|
+
const swatch = React.createElement('button', {
|
|
1423
|
+
type: 'button',
|
|
1424
|
+
className: 'bib-set-swatch' + (isDefault ? ' bib-set-swatch--default' : ''),
|
|
1425
|
+
'aria-label': t('ui.colorSwatchLabel', { label: props.label }),
|
|
1426
|
+
'aria-haspopup': 'menu',
|
|
1427
|
+
'aria-expanded': open,
|
|
1428
|
+
title: t('ui.colorSwatchLabel', { label: props.label }),
|
|
1429
|
+
onClick: function () { setOpen(!open); },
|
|
1430
|
+
}, React.createElement('span', { className: 'bib-set-swatch-core', style: swatchStyle, 'aria-hidden': 'true' }));
|
|
1431
|
+
const items = PRESET_COLORS.map(function (name) {
|
|
1432
|
+
return {
|
|
1433
|
+
id: name,
|
|
1434
|
+
label: t(BIB_SET_PRESET_LABELS[name]) || name,
|
|
1435
|
+
icon: React.createElement('span', {
|
|
1436
|
+
className: 'bib-set-menu-dot',
|
|
1437
|
+
style: { background: 'var(--bi-palette-' + name + ')' },
|
|
1438
|
+
'aria-hidden': 'true',
|
|
1439
|
+
}),
|
|
1440
|
+
};
|
|
1441
|
+
});
|
|
1442
|
+
items.push({ id: 'bib-set-color-sep', type: 'separator' });
|
|
1443
|
+
items.push({ id: 'custom', label: t('ui.customColorItem') });
|
|
1444
|
+
items.push({ id: 'default', label: t('ui.restoreDefaultColor') });
|
|
1445
|
+
const onSelect = function (id) {
|
|
1446
|
+
if (id === 'custom') {
|
|
1447
|
+
// 系统取色器由隐藏的 <input type="color"> 承担;.click() 处于用户点击链路内。
|
|
1448
|
+
const input = customInputRef.current;
|
|
1449
|
+
if (input && typeof input.click === 'function') input.click();
|
|
1450
|
+
return;
|
|
1451
|
+
}
|
|
1452
|
+
if (id === 'default') { setOpen(false); props.onColorChange(null); return; }
|
|
1453
|
+
if (PRESET_COLOR_SET.has(id)) { setOpen(false); props.onColorChange(id); }
|
|
1454
|
+
};
|
|
1455
|
+
// 弹层底部:hex 输入 + 隐藏的系统取色器。children 渲染在 Menu 视口内,
|
|
1456
|
+
// pointerdown/Escape 的关闭逻辑都把列表内部当作菜单自身,输入不会被误关。
|
|
1457
|
+
const menuExtra = React.createElement('div', { className: 'bib-set-color-menu-extra' },
|
|
1458
|
+
React.createElement('input', {
|
|
1459
|
+
ref: customInputRef,
|
|
1460
|
+
type: 'color',
|
|
1461
|
+
className: 'bib-set-color-native-input',
|
|
1462
|
+
'aria-label': t('ui.customColor', { label: props.label }),
|
|
1463
|
+
title: t('ui.customColorOpenColorPicker'),
|
|
1464
|
+
value: isPreset ? (BIB_SET_PRESET_WELL_HEX[value] || '#333333') : (isDefault ? '#333333' : value),
|
|
1465
|
+
onChange: function (event) {
|
|
1466
|
+
const picked = event && event.target ? event.target.value : null;
|
|
1467
|
+
if (picked && BIB_SET_HEX_PATTERN.test(picked)) props.onColorChange(picked.toUpperCase());
|
|
1468
|
+
},
|
|
1469
|
+
}),
|
|
1470
|
+
(function () {
|
|
1471
|
+
const draft = props.hexDraftOf(props.fieldId);
|
|
1472
|
+
const hexValue = draft !== null ? draft : props.committedHexText(props.fieldId);
|
|
1473
|
+
const hexInvalid = draft !== null && draft.trim().length > 0 && !BIB_SET_HEX_PATTERN.test(draft.trim());
|
|
1474
|
+
return React.createElement('input', {
|
|
1475
|
+
type: 'text',
|
|
1476
|
+
className: 'bib-set-hex',
|
|
1477
|
+
'aria-label': t('ui.hexColor', { label: props.label }),
|
|
1478
|
+
'aria-invalid': hexInvalid ? 'true' : 'false',
|
|
1479
|
+
'data-invalid': hexInvalid ? 'true' : 'false',
|
|
1480
|
+
placeholder: '#RRGGBB',
|
|
1481
|
+
spellCheck: false,
|
|
1482
|
+
maxLength: 7,
|
|
1483
|
+
value: hexValue,
|
|
1484
|
+
onChange: function (event) { props.onHexChange(props.fieldId, event && event.target ? event.target.value : ''); },
|
|
1485
|
+
onBlur: function () { props.onHexCommit(props.fieldId); },
|
|
1486
|
+
onKeyDown: function (event) { if (event.key === 'Enter') { event.preventDefault(); props.onHexCommit(props.fieldId); } },
|
|
1487
|
+
});
|
|
1488
|
+
})());
|
|
1489
|
+
return React.createElement(BIB_SET_NATIVE_MENU, {
|
|
1490
|
+
open: open,
|
|
1491
|
+
anchor: swatch,
|
|
1492
|
+
items: items,
|
|
1493
|
+
selectedId: isPreset ? value : undefined,
|
|
1494
|
+
onSelect: onSelect,
|
|
1495
|
+
onClose: function () { setOpen(false); },
|
|
1496
|
+
align: 'end',
|
|
1497
|
+
portal: true,
|
|
1498
|
+
className: 'bib-set-color-host',
|
|
1499
|
+
}, menuExtra);
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1095
1502
|
function bibSetFieldRow(field, props) {
|
|
1096
|
-
const isAnchor = field.anchor === true;
|
|
1097
1503
|
const descParts = [];
|
|
1098
|
-
if (isAnchor) descParts.push(t('ui.identifiesTheProviderAndModel'));
|
|
1099
|
-
const modeText = bibSetModeText(field);
|
|
1100
|
-
if (modeText) descParts.push(modeText + t('ui.sentenceEnd'));
|
|
1101
1504
|
if (field.note) descParts.push(t(field.note));
|
|
1102
|
-
if (field.suggestKeep) descParts.push(t('ui.keepEnabledToSeeThese'));
|
|
1103
1505
|
|
|
1104
1506
|
const value = props.colorOf(field.id);
|
|
1105
1507
|
const isPreset = value !== null && PRESET_COLOR_SET.has(value);
|
|
@@ -1110,19 +1512,23 @@ function bibSetFieldRow(field, props) {
|
|
|
1110
1512
|
const wellValue = isHex ? value : (isPreset ? (BIB_SET_PRESET_WELL_HEX[value] || '#333333') : '#333333');
|
|
1111
1513
|
const fieldLabel = t(field.label);
|
|
1112
1514
|
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
label:
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1515
|
+
// 决策 1:行内右侧 = 开关 + 一个色块。宿主没有原生 Menu 时才退回旧的行内控件组
|
|
1516
|
+
// (色板圆点 + 系统取色器色井 + hex 输入)——这是兜底路径,不是死代码。
|
|
1517
|
+
let colorControl;
|
|
1518
|
+
if (BIB_SET_NATIVE_MENU) {
|
|
1519
|
+
colorControl = React.createElement(bibSetColorPicker, {
|
|
1520
|
+
key: 'color-picker',
|
|
1521
|
+
fieldId: field.id,
|
|
1522
|
+
label: fieldLabel,
|
|
1523
|
+
value: value,
|
|
1524
|
+
onColorChange: function (next) { props.onColorChange(field.id, next); },
|
|
1525
|
+
hexDraftOf: props.hexDraftOf,
|
|
1526
|
+
committedHexText: props.committedHexText,
|
|
1527
|
+
onHexChange: props.onHexChange,
|
|
1528
|
+
onHexCommit: props.onHexCommit,
|
|
1529
|
+
});
|
|
1530
|
+
} else {
|
|
1531
|
+
colorControl = React.createElement('div', { className: 'bib-set-controls bib-set-controls--field' },
|
|
1126
1532
|
React.createElement(bibSetPalette, {
|
|
1127
1533
|
label: t('ui.presetColor', { label: fieldLabel }),
|
|
1128
1534
|
value: value,
|
|
@@ -1152,65 +1558,153 @@ function bibSetFieldRow(field, props) {
|
|
|
1152
1558
|
onChange: function (event) { props.onHexChange(field.id, event && event.target ? event.target.value : ''); },
|
|
1153
1559
|
onBlur: function () { props.onHexCommit(field.id); },
|
|
1154
1560
|
onKeyDown: function (event) { if (event.key === 'Enter') { event.preventDefault(); props.onHexCommit(field.id); } },
|
|
1155
|
-
}))
|
|
1156
|
-
|
|
1157
|
-
const subcontrols = [];
|
|
1158
|
-
if (field.id === 'mainTime' || field.id === 'worldTime') {
|
|
1159
|
-
subcontrols.push(React.createElement('span', { key: 'zone-label', className: 'bib-set-subcontrol-label' }, field.id === 'mainTime' ? t('ui.mainTimeZone') : t('ui.worldTimeZone')));
|
|
1160
|
-
subcontrols.push(React.createElement('select', {
|
|
1161
|
-
key: 'zone',
|
|
1162
|
-
className: 'bib-set-time-zone',
|
|
1163
|
-
value: field.id === 'mainTime' ? props.timeZonesOf().main : props.timeZonesOf().world,
|
|
1164
|
-
onChange: function (event) { props.onTimeZoneChange(field.id === 'mainTime' ? 'main' : 'world', event.target.value); },
|
|
1165
|
-
}, TIME_ZONE_OPTIONS.map(function (z) { return React.createElement('option', { key: z, value: z }, z); })));
|
|
1166
|
-
if (field.id === 'mainTime') {
|
|
1167
|
-
subcontrols.push(React.createElement('span', { key: 'parts', className: 'bib-set-time-parts' },
|
|
1168
|
-
['year','month','day','hour','minute','second'].map(function (key) {
|
|
1169
|
-
const labelKey = { year: 'ui.year', month: 'ui.month', day: 'ui.day', hour: 'ui.hour', minute: 'ui.minute', second: 'ui.second' }[key];
|
|
1170
|
-
return React.createElement('span', { key: key, className: 'bib-set-time-part' },
|
|
1171
|
-
React.createElement('span', { className: 'bib-set-time-part-label' }, t(labelKey)),
|
|
1172
|
-
bibSetSwitch({ label: t(labelKey), checked: !!props.timeFormatOf()[key], onToggle: function (next) { props.onTimeFormatChange(key, next); } }));
|
|
1173
|
-
})));
|
|
1174
|
-
}
|
|
1175
|
-
subcontrols.push(React.createElement('span', { key: 'preview', className: 'bib-set-time-preview' },
|
|
1176
|
-
t('ui.timePreview') + formatClock(Date.now(), field.id === 'mainTime' ? props.timeZonesOf().main : props.timeZonesOf().world, props.timeFormatOf())));
|
|
1177
|
-
}
|
|
1178
|
-
if (field.id === 'customText') {
|
|
1179
|
-
subcontrols.push(React.createElement('input', {
|
|
1180
|
-
key: 'custom-text',
|
|
1181
|
-
type: 'text',
|
|
1182
|
-
className: 'bib-set-custom-text-input',
|
|
1183
|
-
placeholder: t('ui.customTextPlaceholder'),
|
|
1184
|
-
maxLength: 64,
|
|
1185
|
-
value: props.customTextDraft !== null ? props.customTextDraft : props.customTextOf(),
|
|
1186
|
-
onChange: function (event) { props.onCustomTextChange(event.target.value); },
|
|
1187
|
-
onBlur: props.onCustomTextCommit,
|
|
1188
|
-
onKeyDown: function (event) { if (event.key === 'Enter') { event.preventDefault(); props.onCustomTextCommit(); } },
|
|
1189
|
-
'aria-label': t('ui.customTextTitle'),
|
|
1190
|
-
}));
|
|
1191
|
-
subcontrols.push(React.createElement('span', { key: 'custom-text-count', className: 'bib-set-custom-text-count' }, props.customTextOf().length + '/64'));
|
|
1561
|
+
}));
|
|
1192
1562
|
}
|
|
1193
1563
|
|
|
1564
|
+
const rowMain = React.createElement('div', { className: 'bib-set-row-main' },
|
|
1565
|
+
React.createElement('div', { className: 'bib-set-rowText bib-set-rowText--field' },
|
|
1566
|
+
React.createElement('div', { className: 'bib-set-rowTitle' }, fieldLabel),
|
|
1567
|
+
React.createElement('div', { className: 'bib-set-rowDesc' }, descParts.join(t('ui.sentenceSeparator')))),
|
|
1568
|
+
bibSetSwitch({
|
|
1569
|
+
label: t('ui.show', { label: fieldLabel }),
|
|
1570
|
+
checked: props.fieldOn(field.id),
|
|
1571
|
+
title: props.fieldOn(field.id) ? t('ui.clickToHide') : t('ui.clickToShow'),
|
|
1572
|
+
onToggle: function (next) { props.onFieldToggle(field.id, next); },
|
|
1573
|
+
}),
|
|
1574
|
+
colorControl);
|
|
1575
|
+
|
|
1576
|
+
// 决策 3:带参数的字段(主/世界时间、自定义文字)已拆出为独立设置区,
|
|
1577
|
+
// 字段行从此单行,只承载「显隐开关 + 颜色」。
|
|
1194
1578
|
return React.createElement('div', { key: field.id, className: 'bib-set-row bib-set-row--field' },
|
|
1195
|
-
rowMain
|
|
1196
|
-
|
|
1579
|
+
rowMain);
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
// 分组折叠头:整块表面都是明确的操作入口,末端以“展开/收起”文字补足箭头的含义。
|
|
1583
|
+
// 这比把 DisclosureRow 当作标题文字更接近 macOS 设置里的可进入分组,也保留原生 button 语义。
|
|
1584
|
+
function bibSetDisclosure(props) {
|
|
1585
|
+
const open = props.open === true;
|
|
1586
|
+
const onToggle = function () { if (props.onToggle) props.onToggle(); };
|
|
1587
|
+
return React.createElement('section', { className: 'bib-set-group' + (open ? ' bib-set-group--expanded' : '') },
|
|
1588
|
+
React.createElement('button', {
|
|
1589
|
+
type: 'button',
|
|
1590
|
+
className: 'bib-set-group-fallback-head',
|
|
1591
|
+
'aria-expanded': open,
|
|
1592
|
+
'aria-controls': props.contentId,
|
|
1593
|
+
onClick: onToggle,
|
|
1594
|
+
},
|
|
1595
|
+
props.title,
|
|
1596
|
+
React.createElement('span', { className: 'bib-set-group-action', 'aria-hidden': 'true' },
|
|
1597
|
+
open ? t('ui.collapse') : t('ui.expand'),
|
|
1598
|
+
bibSetChevron({ expanded: open }))),
|
|
1599
|
+
React.createElement('div', {
|
|
1600
|
+
id: props.contentId,
|
|
1601
|
+
className: 'bib-set-collapse' + (open ? ' bib-set-collapse--expanded' : ' bib-set-collapse--collapsed'),
|
|
1602
|
+
'aria-hidden': open ? undefined : 'true',
|
|
1603
|
+
inert: open ? undefined : true,
|
|
1604
|
+
}, React.createElement('div', { className: 'bib-set-collapse-inner' }, props.children)));
|
|
1197
1605
|
}
|
|
1198
1606
|
|
|
1199
1607
|
function bibSetFieldGroups(props) {
|
|
1200
1608
|
const groups = [];
|
|
1201
1609
|
for (let g = 0; g < FIELD_GROUP_ORDER.length; g++) {
|
|
1202
1610
|
const group = FIELD_GROUP_ORDER[g];
|
|
1203
|
-
const groupFields = FIELD_REGISTRY.filter(function (field) {
|
|
1204
|
-
return field.group === group && props.matchesSearch(field, props.query);
|
|
1205
|
-
});
|
|
1611
|
+
const groupFields = FIELD_REGISTRY.filter(function (field) { return field.group === group; });
|
|
1206
1612
|
if (groupFields.length === 0) continue;
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1613
|
+
const visibleFields = groupFields.filter(function (field) { return props.matchesSearch(field, props.query); });
|
|
1614
|
+
// 搜索无命中的组整组不渲染(空状态由上层统一给出)
|
|
1615
|
+
if (props.searchActive && visibleFields.length === 0) continue;
|
|
1616
|
+
const groupLabel = FIELD_GROUP_LABELS[group] ? t(FIELD_GROUP_LABELS[group]) : group;
|
|
1617
|
+
// 组卡只保留名称与明确的动作;状态数字和分类说明会增加阅读负担。
|
|
1618
|
+
const title = React.createElement('span', { className: 'bib-set-group-head' },
|
|
1619
|
+
React.createElement('span', { className: 'bib-set-group-label' }, groupLabel));
|
|
1620
|
+
groups.push(React.createElement(bibSetDisclosure, {
|
|
1621
|
+
key: 'g-' + group,
|
|
1622
|
+
title: title,
|
|
1623
|
+
contentId: 'bib-set-group-' + group,
|
|
1624
|
+
open: props.groupOpenOf(group),
|
|
1625
|
+
onToggle: function () { props.onGroupToggle(group); },
|
|
1626
|
+
}, React.createElement('div', { className: 'bib-set-body' },
|
|
1627
|
+
visibleFields.map(function (field) { return bibSetFieldRow(field, props); }))));
|
|
1210
1628
|
}
|
|
1211
1629
|
return groups;
|
|
1212
1630
|
}
|
|
1213
1631
|
|
|
1632
|
+
// 「时间与日期」独立设置区(决策 3):主/世界时间的时区 + 主时间的年月日时分秒格式。
|
|
1633
|
+
// 对应字段开关全关时整区隐藏;区内每个字段块照宿主 settings-form 字段基线。
|
|
1634
|
+
function bibSetTimeDateSection(props) {
|
|
1635
|
+
const mainOn = props.fieldOn('mainTime');
|
|
1636
|
+
const worldOn = props.fieldOn('worldTime');
|
|
1637
|
+
if (!mainOn && !worldOn) return null;
|
|
1638
|
+
const blocks = [];
|
|
1639
|
+
if (mainOn) {
|
|
1640
|
+
blocks.push(React.createElement('div', { key: 'main-zone', className: 'bib-set-fieldblock' },
|
|
1641
|
+
React.createElement('span', { className: 'bib-set-fieldblock-label' }, t('ui.mainTimeZone')),
|
|
1642
|
+
React.createElement(bibSetTimeZonePicker, {
|
|
1643
|
+
label: t('ui.mainTimeZone'),
|
|
1644
|
+
value: props.timeZonesOf().main,
|
|
1645
|
+
onChange: function (value) { props.onTimeZoneChange('main', value); },
|
|
1646
|
+
})));
|
|
1647
|
+
}
|
|
1648
|
+
if (worldOn) {
|
|
1649
|
+
blocks.push(React.createElement('div', { key: 'world-zone', className: 'bib-set-fieldblock' },
|
|
1650
|
+
React.createElement('span', { className: 'bib-set-fieldblock-label' }, t('ui.worldTimeZone')),
|
|
1651
|
+
React.createElement(bibSetTimeZonePicker, {
|
|
1652
|
+
label: t('ui.worldTimeZone'),
|
|
1653
|
+
value: props.timeZonesOf().world,
|
|
1654
|
+
onChange: function (value) { props.onTimeZoneChange('world', value); },
|
|
1655
|
+
})));
|
|
1656
|
+
}
|
|
1657
|
+
if (mainOn) {
|
|
1658
|
+
blocks.push(React.createElement('div', { key: 'format', className: 'bib-set-fieldblock' },
|
|
1659
|
+
React.createElement('span', { className: 'bib-set-fieldblock-label' }, t('ui.timeFormat')),
|
|
1660
|
+
React.createElement('span', { className: 'bib-set-time-parts' },
|
|
1661
|
+
['year', 'month', 'day', 'hour', 'minute', 'second'].map(function (key) {
|
|
1662
|
+
const labelKey = { year: 'ui.year', month: 'ui.month', day: 'ui.day', hour: 'ui.hour', minute: 'ui.minute', second: 'ui.second' }[key];
|
|
1663
|
+
return React.createElement('span', { key: key, className: 'bib-set-time-part' },
|
|
1664
|
+
React.createElement('span', { className: 'bib-set-time-part-label' }, t(labelKey)),
|
|
1665
|
+
bibSetSwitch({ label: t(labelKey), checked: !!props.timeFormatOf()[key], onToggle: function (next) { props.onTimeFormatChange(key, next); } }));
|
|
1666
|
+
})),
|
|
1667
|
+
React.createElement('p', { className: 'bib-set-fieldblock-hint' },
|
|
1668
|
+
t('ui.timePreview') + formatClock(Date.now(), props.timeZonesOf().main, props.timeFormatOf()))));
|
|
1669
|
+
}
|
|
1670
|
+
return React.createElement('section', { className: 'bib-set-card', 'aria-labelledby': 'bib-set-time-date-title' },
|
|
1671
|
+
bibSetCardHeader({
|
|
1672
|
+
static: true,
|
|
1673
|
+
titleId: 'bib-set-time-date-title',
|
|
1674
|
+
title: t('ui.timeDateTitle'),
|
|
1675
|
+
description: t('ui.timeDateDesc'),
|
|
1676
|
+
}),
|
|
1677
|
+
React.createElement('div', { className: 'bib-set-fieldblocks' }, blocks));
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
// 「自定义文字」独立设置区(决策 3):输入框 + 字数提示;对应开关关闭时整区隐藏。
|
|
1681
|
+
function bibSetCustomTextSection(props) {
|
|
1682
|
+
if (!props.fieldOn('customText')) return null;
|
|
1683
|
+
return React.createElement('section', { className: 'bib-set-card', 'aria-labelledby': 'bib-set-custom-text-title' },
|
|
1684
|
+
bibSetCardHeader({
|
|
1685
|
+
static: true,
|
|
1686
|
+
titleId: 'bib-set-custom-text-title',
|
|
1687
|
+
title: t('field.customText.label'),
|
|
1688
|
+
description: t('ui.customTextSectionDesc'),
|
|
1689
|
+
}),
|
|
1690
|
+
React.createElement('div', { className: 'bib-set-fieldblocks' },
|
|
1691
|
+
React.createElement('div', { className: 'bib-set-fieldblock' },
|
|
1692
|
+
React.createElement('input', {
|
|
1693
|
+
id: 'bib-set-custom-text-input',
|
|
1694
|
+
type: 'text',
|
|
1695
|
+
className: 'bib-set-custom-text-input',
|
|
1696
|
+
placeholder: t('ui.customTextPlaceholder'),
|
|
1697
|
+
maxLength: 64,
|
|
1698
|
+
value: props.customTextDraft !== null ? props.customTextDraft : props.customTextOf(),
|
|
1699
|
+
onChange: function (event) { props.onCustomTextChange(event.target.value); },
|
|
1700
|
+
onBlur: props.onCustomTextCommit,
|
|
1701
|
+
onKeyDown: function (event) { if (event.key === 'Enter') { event.preventDefault(); props.onCustomTextCommit(); } },
|
|
1702
|
+
'aria-label': t('ui.customTextTitle'),
|
|
1703
|
+
}),
|
|
1704
|
+
React.createElement('p', { className: 'bib-set-fieldblock-hint' },
|
|
1705
|
+
t('ui.customTextCount', { value: props.customTextOf().length })))));
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1214
1708
|
const USAGE_EXPORT_COLUMNS = [
|
|
1215
1709
|
['timestamp', function (record) { return usageExportTimestamp(record.ts); }],
|
|
1216
1710
|
['provider', function (record) { return record.provider; }],
|
|
@@ -1286,14 +1780,14 @@ function bibSetDataCard(props) {
|
|
|
1286
1780
|
React.createElement('p', { className: 'bib-set-data-title' }, t('ui.exportBillingRecords')),
|
|
1287
1781
|
React.createElement('p', { className: 'bib-set-data-desc' }, t('ui.exportBillingRecordsDesc'))),
|
|
1288
1782
|
React.createElement('div', { className: 'bib-set-data-button-group' },
|
|
1289
|
-
|
|
1290
|
-
|
|
1783
|
+
bibSetButton({ disabled: disabled, onClick: function () { props.onExport('csv'); }, children: t('ui.exportBillingCsv') }),
|
|
1784
|
+
bibSetButton({ disabled: disabled, onClick: function () { props.onExport('json'); }, children: t('ui.exportBillingJson') }))),
|
|
1291
1785
|
React.createElement('div', { className: 'bib-set-data-row' },
|
|
1292
1786
|
React.createElement('div', { className: 'bib-set-data-copy' },
|
|
1293
1787
|
React.createElement('p', { className: 'bib-set-data-title' }, t('ui.clearBillingRecords')),
|
|
1294
1788
|
React.createElement('p', { className: 'bib-set-data-desc' }, t('ui.clearBillingRecordsDesc'))),
|
|
1295
1789
|
React.createElement('div', { className: 'bib-set-data-button-group' },
|
|
1296
|
-
|
|
1790
|
+
bibSetButton({ destructive: true, disabled: disabled, onClick: props.onClear, children: t('ui.clearBillingRecords') })))));
|
|
1297
1791
|
}
|
|
1298
1792
|
|
|
1299
1793
|
function InfoBarSettingsSection() {
|
|
@@ -1324,8 +1818,12 @@ function InfoBarSettingsSection() {
|
|
|
1324
1818
|
});
|
|
1325
1819
|
}, []);
|
|
1326
1820
|
const [searchQuery, setSearchQuery] = React.useState('');
|
|
1327
|
-
|
|
1328
|
-
|
|
1821
|
+
// 首屏先展示最常见的原生字段,插件字段按需展开;搜索时两组自动展开。
|
|
1822
|
+
// 用户随后仍可手动折叠,箭头与 aria-expanded 始终反映真实状态。
|
|
1823
|
+
const [groupOpen, setGroupOpen] = React.useState({ native: true, plugin: false });
|
|
1824
|
+
// 决策 4:重置的二次确认。null=未在确认;'fields'/'colors'=待确认的重置类别。
|
|
1825
|
+
const [resetConfirm, setResetConfirm] = React.useState(null);
|
|
1826
|
+
const [resetAcknowledged, setResetAcknowledged] = React.useState(false);
|
|
1329
1827
|
function matchesSearch(field, query) {
|
|
1330
1828
|
if (!query) return true;
|
|
1331
1829
|
const q = query.trim().toLowerCase();
|
|
@@ -1366,13 +1864,13 @@ function InfoBarSettingsSection() {
|
|
|
1366
1864
|
try {
|
|
1367
1865
|
if (status === 'loading') {
|
|
1368
1866
|
return React.createElement('div', { ref: settingsRootRef, className: 'bib-set-root bib-settings' },
|
|
1369
|
-
bibSetPageTitle(),
|
|
1370
|
-
|
|
1867
|
+
React.createElement('div', { className: 'bib-set-page-head' }, bibSetPageTitle()),
|
|
1868
|
+
bibSetAlert({ tone: 'info', children: t('ui.loadingInfoBarSettings') }));
|
|
1371
1869
|
}
|
|
1372
1870
|
if (status === 'error') {
|
|
1373
1871
|
return React.createElement('div', { ref: settingsRootRef, className: 'bib-set-root bib-settings' },
|
|
1374
|
-
bibSetPageTitle(),
|
|
1375
|
-
|
|
1872
|
+
React.createElement('div', { className: 'bib-set-page-head' }, bibSetPageTitle()),
|
|
1873
|
+
bibSetAlert({ tone: 'error', children: t('ui.couldNotLoadInfoBar') + (hostText(loadError) || t('ui.pleaseTryAgainLater')) }));
|
|
1376
1874
|
}
|
|
1377
1875
|
|
|
1378
1876
|
const fieldsById = {};
|
|
@@ -1384,6 +1882,9 @@ function InfoBarSettingsSection() {
|
|
|
1384
1882
|
}
|
|
1385
1883
|
function labelOf(id) { const f = fieldsById[id]; return f ? t(f.label) : id; }
|
|
1386
1884
|
function makePair(key, value) { const pair = {}; pair[key] = value; return pair; }
|
|
1885
|
+
function toggleGroup(group) {
|
|
1886
|
+
setGroupOpen(function (state) { return Object.assign({}, state, makePair(group, !(state && state[group] === true))); });
|
|
1887
|
+
}
|
|
1387
1888
|
|
|
1388
1889
|
// 服务端快照回写(configVersion 一并更新;persisted=false 时如实提示落盘失败)
|
|
1389
1890
|
function applyServerResult(res) {
|
|
@@ -1528,6 +2029,22 @@ function InfoBarSettingsSection() {
|
|
|
1528
2029
|
});
|
|
1529
2030
|
}
|
|
1530
2031
|
|
|
2032
|
+
// 决策 4:重置不再一键执行。宿主有原生 RiskConfirmation 时先弹确认(勾选后才能确认);
|
|
2033
|
+
// 取不到时退回 window.confirm——两条路径都保证「未确认不执行」。
|
|
2034
|
+
function requestReset(kind) {
|
|
2035
|
+
if (!BIB_SET_NATIVE_RISK_CONFIRM) {
|
|
2036
|
+
let confirmed = false;
|
|
2037
|
+
try {
|
|
2038
|
+
confirmed = typeof window !== 'undefined' && typeof window.confirm === 'function'
|
|
2039
|
+
? window.confirm(kind === 'colors' ? t('ui.resetConfirmDescColors') : t('ui.resetConfirmDescFields')) : false;
|
|
2040
|
+
} catch (err) { confirmed = false; }
|
|
2041
|
+
if (confirmed) runReset(kind);
|
|
2042
|
+
return;
|
|
2043
|
+
}
|
|
2044
|
+
setResetAcknowledged(false);
|
|
2045
|
+
setResetConfirm(kind === 'colors' ? 'colors' : 'fields');
|
|
2046
|
+
}
|
|
2047
|
+
|
|
1531
2048
|
function runExport(format) {
|
|
1532
2049
|
if (saving || dataBusy) return;
|
|
1533
2050
|
setOpError(null);
|
|
@@ -1573,15 +2090,17 @@ function InfoBarSettingsSection() {
|
|
|
1573
2090
|
}
|
|
1574
2091
|
|
|
1575
2092
|
// ---- 渲染 ----
|
|
1576
|
-
//
|
|
1577
|
-
// aria-expanded 始终反映真实 DOM
|
|
2093
|
+
// 搜索只负责筛选;输入时两分组自动展开(决策 2),但用户随后仍可明确折叠,
|
|
2094
|
+
// 箭头和 aria-expanded 始终反映真实 DOM 状态。
|
|
1578
2095
|
const searchActive = searchQuery.trim().length > 0;
|
|
1579
2096
|
const fieldsEnabledCount = FIELD_REGISTRY.filter(function (f) { return fieldOn(f.id); }).length;
|
|
1580
2097
|
const fieldsMatchCount = FIELD_REGISTRY.filter(function (f) { return matchesSearch(f, searchQuery); }).length;
|
|
1581
|
-
const fieldsExpanded = !fieldsCollapsed;
|
|
1582
2098
|
const groupsChildren = bibSetFieldGroups({
|
|
1583
2099
|
query: searchQuery,
|
|
2100
|
+
searchActive: searchActive,
|
|
1584
2101
|
matchesSearch: matchesSearch,
|
|
2102
|
+
groupOpenOf: function (group) { return groupOpen && groupOpen[group] === true; },
|
|
2103
|
+
onGroupToggle: toggleGroup,
|
|
1585
2104
|
fieldOn: fieldOn,
|
|
1586
2105
|
colorOf: colorOf,
|
|
1587
2106
|
hexDraftOf: hexDraftOf,
|
|
@@ -1600,41 +2119,48 @@ function InfoBarSettingsSection() {
|
|
|
1600
2119
|
onCustomTextCommit: commitCustomText,
|
|
1601
2120
|
});
|
|
1602
2121
|
|
|
2122
|
+
// 提示分两类:错误/警示是独立整块(照宿主 .X_2TxG_failure / .X_2TxG_banner 的形态),
|
|
2123
|
+
// 状态文字(已保存/保存中)是页脚左侧的安静一行(照宿主 .X_2TxG_sectionCount 的 12/18 次要色)。
|
|
2124
|
+
const alerts = [];
|
|
2125
|
+
if (opError) alerts.push(bibSetAlert({ tone: 'error', key: 'err', children: opError.text() }));
|
|
1603
2126
|
const feedback = [];
|
|
1604
|
-
if (
|
|
1605
|
-
else if (
|
|
1606
|
-
else if (
|
|
1607
|
-
else if (dataBusy) feedback.push(React.createElement('p', { key: 'processing', className: 'bib-set-notice', 'aria-live': 'polite' }, t('ui.processing')));
|
|
2127
|
+
if (notice) feedback.push(React.createElement('span', { key: 'notice', className: 'bib-set-notice', role: 'status' }, notice.text()));
|
|
2128
|
+
else if (saving) feedback.push(React.createElement('span', { key: 'saving', className: 'bib-set-notice', 'aria-live': 'polite' }, t('ui.saving')));
|
|
2129
|
+
else if (dataBusy) feedback.push(React.createElement('span', { key: 'processing', className: 'bib-set-notice', 'aria-live': 'polite' }, t('ui.processing')));
|
|
1608
2130
|
|
|
1609
2131
|
const fieldSummary = searchActive
|
|
1610
2132
|
? t('ui.searchResultCount', { count: fieldsMatchCount })
|
|
1611
2133
|
: t('ui.enabledFieldsCount', { count: fieldsEnabledCount });
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
2134
|
+
// 字段卡片 = 搜索工具栏 + 两个独立折叠的分组(决策 2)。搜索无命中时给明确空状态。
|
|
2135
|
+
// 分组必须作为 field-list 的直接子项,flex gap 才会真实落在两张卡之间;
|
|
2136
|
+
// 旧的 .bib-set-body 包装层让 16px 间距只存在于样式表中,画面上仍会挤在一起。
|
|
2137
|
+
const fieldsBody = React.createElement('div', { className: 'bib-set-field-list' },
|
|
2138
|
+
searchActive && fieldsMatchCount === 0
|
|
2139
|
+
? React.createElement('p', { className: 'bib-set-empty', role: 'status' }, t('ui.noSearchResults'))
|
|
2140
|
+
: groupsChildren);
|
|
2141
|
+
// 决策 4:确认弹窗(勾选前「确认」按钮由 RiskConfirmation 禁用)。
|
|
2142
|
+
const resetDialog = resetConfirm !== null && BIB_SET_NATIVE_RISK_CONFIRM
|
|
2143
|
+
? React.createElement(BIB_SET_NATIVE_RISK_CONFIRM, {
|
|
2144
|
+
open: true,
|
|
2145
|
+
title: t('ui.resetConfirmTitle'),
|
|
2146
|
+
description: resetConfirm === 'colors' ? t('ui.resetConfirmDescColors') : t('ui.resetConfirmDescFields'),
|
|
2147
|
+
acknowledgeLabel: t('ui.resetConfirmAcknowledge'),
|
|
2148
|
+
cancelLabel: t('ui.resetConfirmCancel'),
|
|
2149
|
+
closeLabel: t('ui.resetConfirmCancel'),
|
|
2150
|
+
confirmLabel: t('ui.resetConfirmConfirm'),
|
|
2151
|
+
acknowledged: resetAcknowledged,
|
|
2152
|
+
onAcknowledgedChange: function (next) { setResetAcknowledged(next === true); },
|
|
2153
|
+
onCancel: function () { setResetConfirm(null); },
|
|
2154
|
+
onConfirm: function () {
|
|
2155
|
+
const kind = resetConfirm;
|
|
2156
|
+
setResetConfirm(null);
|
|
2157
|
+
if (kind) runReset(kind);
|
|
2158
|
+
},
|
|
2159
|
+
})
|
|
2160
|
+
: null;
|
|
1625
2161
|
return React.createElement('div', { ref: settingsRootRef, className: 'bib-set-root bib-settings' },
|
|
1626
|
-
bibSetPageTitle(),
|
|
1627
|
-
React.createElement('
|
|
1628
|
-
t('ui.chooseWhichFieldsToShow')),
|
|
1629
|
-
React.createElement('section', { className: 'bib-set-card', 'aria-labelledby': 'bib-set-fields-title' },
|
|
1630
|
-
bibSetCardHeader({
|
|
1631
|
-
titleId: 'bib-set-fields-title',
|
|
1632
|
-
title: t('ui.visibleFields'),
|
|
1633
|
-
description: fieldsCollapsed ? t('ui.collapsedDesc') : t('ui.hiddenFieldsTakeNoSpace'),
|
|
1634
|
-
expanded: fieldsExpanded,
|
|
1635
|
-
contentId: 'bib-set-fields-body',
|
|
1636
|
-
onToggle: toggleFields,
|
|
1637
|
-
}),
|
|
2162
|
+
React.createElement('div', { className: 'bib-set-page-head' }, bibSetPageTitle()),
|
|
2163
|
+
React.createElement('section', { className: 'bib-set-card', 'aria-label': t('ui.visibleFields') },
|
|
1638
2164
|
React.createElement('div', { className: 'bib-set-toolbar' },
|
|
1639
2165
|
React.createElement('div', { className: 'bib-set-search-row' },
|
|
1640
2166
|
React.createElement('div', { className: 'bib-set-search-shell' },
|
|
@@ -1646,27 +2172,51 @@ function InfoBarSettingsSection() {
|
|
|
1646
2172
|
onChange: function (e) {
|
|
1647
2173
|
const value = e && e.target ? e.target.value : '';
|
|
1648
2174
|
setSearchQuery(value);
|
|
1649
|
-
if (value.trim().length > 0)
|
|
2175
|
+
if (value.trim().length > 0) setGroupOpen({ native: true, plugin: true });
|
|
1650
2176
|
},
|
|
1651
2177
|
'aria-label': t('ui.searchFieldsLabel') || 'Search visible content'
|
|
1652
2178
|
})),
|
|
1653
|
-
React.createElement('span', { className: 'bib-set-count', role: 'status', 'aria-live': 'polite'
|
|
1654
|
-
fieldsBody
|
|
2179
|
+
React.createElement('span', { className: 'bib-set-count', role: 'status', 'aria-live': 'polite' }, fieldSummary))),
|
|
2180
|
+
fieldsBody,
|
|
2181
|
+
// 「恢复默认」行(决策 4:带二次确认):照「导出账单」的原生行几何,
|
|
2182
|
+
// 挂在「显示内容」区块底部,不再悬浮在页面右下角。
|
|
2183
|
+
React.createElement('div', { className: 'bib-set-data-row bib-set-reset-row' },
|
|
2184
|
+
React.createElement('div', { className: 'bib-set-data-copy' },
|
|
2185
|
+
React.createElement('p', { className: 'bib-set-data-title' }, t('ui.resetRowTitle')),
|
|
2186
|
+
React.createElement('p', { className: 'bib-set-data-desc' }, t('ui.resetRowDesc')),
|
|
2187
|
+
feedback),
|
|
2188
|
+
React.createElement('div', { className: 'bib-set-data-button-group' },
|
|
2189
|
+
bibSetButton({
|
|
2190
|
+
disabled: saving || dataBusy,
|
|
2191
|
+
onClick: function () { requestReset('fields'); },
|
|
2192
|
+
children: t('ui.resetLabels'),
|
|
2193
|
+
}),
|
|
2194
|
+
bibSetButton({
|
|
2195
|
+
disabled: saving || dataBusy,
|
|
2196
|
+
onClick: function () { requestReset('colors'); },
|
|
2197
|
+
children: t('ui.resetColors'),
|
|
2198
|
+
})))),
|
|
2199
|
+
bibSetTimeDateSection({
|
|
2200
|
+
fieldOn: fieldOn,
|
|
2201
|
+
timeFormatOf: timeFormatOf,
|
|
2202
|
+
timeZonesOf: timeZonesOf,
|
|
2203
|
+
onTimeFormatChange: setTimeFormatPart,
|
|
2204
|
+
onTimeZoneChange: setTimeZone,
|
|
2205
|
+
}),
|
|
2206
|
+
bibSetCustomTextSection({
|
|
2207
|
+
fieldOn: fieldOn,
|
|
2208
|
+
customTextDraft: customTextDraft,
|
|
2209
|
+
customTextOf: customTextOf,
|
|
2210
|
+
onCustomTextChange: onCustomTextChange,
|
|
2211
|
+
onCustomTextCommit: commitCustomText,
|
|
2212
|
+
}),
|
|
1655
2213
|
bibSetDataCard({ busy: saving || dataBusy, onExport: runExport, onClear: runClearRecords }),
|
|
1656
|
-
React.createElement('div', { className: 'bib-set-
|
|
1657
|
-
|
|
1658
|
-
React.createElement('button', {
|
|
1659
|
-
type: 'button', className: 'bib-set-btn', disabled: saving || dataBusy,
|
|
1660
|
-
onClick: function () { runReset('fields'); },
|
|
1661
|
-
}, t('ui.resetLabels')),
|
|
1662
|
-
React.createElement('button', {
|
|
1663
|
-
type: 'button', className: 'bib-set-btn', disabled: saving || dataBusy,
|
|
1664
|
-
onClick: function () { runReset('colors'); },
|
|
1665
|
-
}, t('ui.resetColors'))));
|
|
2214
|
+
alerts.length > 0 ? React.createElement('div', { className: 'bib-set-alerts' }, alerts) : null,
|
|
2215
|
+
resetDialog);
|
|
1666
2216
|
} catch (err) {
|
|
1667
2217
|
return React.createElement('div', { ref: settingsRootRef, className: 'bib-set-root bib-settings' },
|
|
1668
|
-
bibSetPageTitle(),
|
|
1669
|
-
|
|
2218
|
+
React.createElement('div', { className: 'bib-set-page-head' }, bibSetPageTitle()),
|
|
2219
|
+
bibSetAlert({ tone: 'error', children: t('ui.couldNotDisplayInfoBar', { value: bibSetOperationMessage(err) }) }));
|
|
1670
2220
|
}
|
|
1671
2221
|
}
|
|
1672
2222
|
|