dsh-math-calculator 2.1.1 → 2.2.0

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.
Files changed (44) hide show
  1. package/CHANGELOG.md +921 -0
  2. package/README.md +64 -50
  3. package/dsh.plugin.json +2 -2
  4. package/lib/__wtest.txt +1 -0
  5. package/lib/awareness.js +69 -20
  6. package/lib/format.js +522 -13
  7. package/lib/formula-library-em.js +428 -0
  8. package/lib/formulas.js +28 -1
  9. package/lib/guards.js +653 -0
  10. package/lib/help.js +509 -0
  11. package/lib/index.js +1357 -212
  12. package/lib/ops/shared.js +547 -6
  13. package/lib/ops/units-op.js +116 -10
  14. package/lib/ops/workspace-op.js +10 -0
  15. package/lib/playbook.js +32 -7
  16. package/lib/types/format.d.ts +31 -1
  17. package/lib/types/formula-library-em.d.ts +28 -0
  18. package/lib/types/formulas.d.ts +2 -0
  19. package/lib/types/guards.d.ts +255 -0
  20. package/lib/types/help.d.ts +70 -0
  21. package/lib/types/index.d.ts +34 -1
  22. package/lib/types/ops/shared.d.ts +87 -3
  23. package/lib/types/playbook.d.ts +28 -0
  24. package/lib/types/units-data.d.ts +253 -0
  25. package/lib/types/units.d.ts +54 -4
  26. package/lib/types/workspace.d.ts +50 -0
  27. package/lib/units-data.js +1037 -0
  28. package/lib/units.js +82 -117
  29. package/lib/workspace.js +54 -0
  30. package/package.json +1 -1
  31. package/src/awareness.ts +69 -20
  32. package/src/format.ts +520 -12
  33. package/src/formula-library-em.ts +435 -0
  34. package/src/formulas.ts +35 -4
  35. package/src/guards.ts +673 -0
  36. package/src/help.ts +543 -0
  37. package/src/index.ts +1492 -213
  38. package/src/ops/shared.ts +589 -9
  39. package/src/ops/units-op.ts +125 -9
  40. package/src/ops/workspace-op.ts +10 -0
  41. package/src/playbook.ts +32 -7
  42. package/src/units-data.ts +1128 -0
  43. package/src/units.ts +149 -130
  44. package/src/workspace.ts +65 -0
package/src/units.ts CHANGED
@@ -35,6 +35,34 @@
35
35
  * @module dsh-math-calculator/units
36
36
  */
37
37
 
38
+ import { ALL_UNITS } from './units-data.js'
39
+
40
+ /** 一个单位。 */
41
+ /**
42
+ * 单位的**用途分类**。
43
+ *
44
+ * ## 为什么要分
45
+ *
46
+ * 这两类的使用场景完全不同,混在一张表里会让列表变得没法读:
47
+ *
48
+ * | 分类 | 例子 | 谁会用 |
49
+ * |---|---|---|
50
+ * | `measure`(度量衡) | 里、斤、斗、俄尺、日尺、加仑、英亩 | 写小说、算账、历史考据 |
51
+ * | `science`(科学) | eV、里德伯、靶恩、玻尔半径、amu、erg、slug | 物理/化学/天文计算 |
52
+ *
53
+ * 一个作者问「三丈是多少米」,不该在 422 条里跟「玻尔半径」挤在一起找;
54
+ * 反过来,算原子结合能的人也不关心「日尺」。分开列,两边都清爽。
55
+ *
56
+ * ## 判据
57
+ *
58
+ * · **度量衡**:日常生活、贸易、历史、工程里**人真的会说出口**的量
59
+ * · **科学**:只在专业计算里出现,日常语境说了没人懂(「这块地 3 靶恩」是笑话)
60
+ *
61
+ * 边界情况按「作者会不会在正文里写它」判:`psi` 会(工程题材),
62
+ * 所以算度量衡;`hartree` 不会(原子物理单位),算科学。
63
+ */
64
+ export type UnitCategory = 'measure' | 'science'
65
+
38
66
  /** 一个单位。 */
39
67
  export interface UnitDef {
40
68
  /** 单位名(作者会写的写法)。 */
@@ -45,12 +73,26 @@ export interface UnitDef {
45
73
  dimension: Dimension
46
74
  /** 换算到该量纲基准单位的系数(基准单位见 BASE_UNIT)。 */
47
75
  factor: number
76
+ /**
77
+ * 用途分类,默认 `measure`(度量衡)。
78
+ *
79
+ * 省略时按度量衡处理 —— 表里绝大多数是这一类,
80
+ * 只给科学单位显式标 `science`,比逐条标更不容易漏。
81
+ */
82
+ category?: UnitCategory
48
83
  /** 说明(什么时候用、有什么坑)。 */
49
84
  note?: string
50
85
  }
51
86
 
52
- /** 支持的量纲。 */
87
+ /**
88
+ * 支持的量纲。
89
+ *
90
+ * 分两层:**物理量纲**(长/质/时/面/体/能/功/温/速/压)与**扩展量纲**
91
+ * (数据量/角度/力/频率)—— 后者是写小说时同样会撞上的(「这段代码多大」
92
+ * 「转三十度」「拉弓要多少牛」),它们各自的公制基准也定义在 units-data。
93
+ */
53
94
  export type Dimension =
95
+ // ── 力学与几何(14 个原有)──
54
96
  | 'length'
55
97
  | 'mass'
56
98
  | 'time'
@@ -60,32 +102,45 @@ export type Dimension =
60
102
  | 'power'
61
103
  | 'temperature'
62
104
  | 'speed'
105
+ | 'pressure'
106
+ | 'data'
107
+ | 'angle'
108
+ | 'force'
109
+ | 'frequency'
110
+ // ── 电学(8 个)──
111
+ | 'current'
112
+ | 'charge'
113
+ | 'voltage'
114
+ | 'capacitance'
115
+ | 'resistance'
116
+ | 'conductance'
117
+ | 'magnetic_flux'
118
+ | 'magnetic_flux_density'
119
+ | 'inductance'
120
+ // ── 光学(3 个)──
121
+ | 'solid_angle'
122
+ | 'luminous_intensity'
123
+ | 'luminous_flux'
124
+ | 'illuminance'
125
+ // ── 化学与核物理(5 个)──
126
+ | 'amount_of_substance'
127
+ | 'activity'
128
+ | 'absorbed_dose'
129
+ | 'dose_equivalent'
130
+ | 'catalytic_activity'
63
131
 
64
- /** 每个量纲的基准单位(factor = 1 的那个)。 */
65
- export const BASE_UNIT: Record<Dimension, string> = {
66
- length: 'm',
67
- mass: 'kg',
68
- time: 's',
69
- area: 'm²',
70
- volume: 'm³',
71
- energy: 'J',
72
- power: 'W',
73
- temperature: 'K',
74
- speed: 'm/s',
75
- }
132
+ /**
133
+ * 每个量纲的基准单位(factor = 1 的那个)—— 即「公制中转点」。
134
+ *
135
+ * 定义在 `units-data.ts`,这里再导出,保持旧的导入路径可用。
136
+ */
137
+ import { DIMENSION_BASE, DIMENSION_LABEL, DIMENSION_ORDER } from './units-data.js'
138
+
139
+ /** 每个量纲的基准单位 —— 即「公制中转点」。 */
140
+ export const BASE_UNIT = DIMENSION_BASE
76
141
 
77
142
  /** 量纲的中文名。 */
78
- export const DIMENSION_LABEL: Record<Dimension, string> = {
79
- length: '长度',
80
- mass: '质量',
81
- time: '时间',
82
- area: '面积',
83
- volume: '体积',
84
- energy: '能量',
85
- power: '功率',
86
- temperature: '温度',
87
- speed: '速度',
88
- }
143
+ export { DIMENSION_LABEL, DIMENSION_ORDER }
89
144
 
90
145
  /**
91
146
  * 内置单位表。
@@ -93,104 +148,13 @@ export const DIMENSION_LABEL: Record<Dimension, string> = {
93
148
  * ⚠️ 中文古制的换算**历朝不同**,这里用最常见的近似值并在 note 里标注。
94
149
  * 作者若需要精确到某个朝代,用 `customUnits` 覆盖。
95
150
  */
96
- export const UNITS: UnitDef[] = [
97
- // ── 长度 ──
98
- { name: 'm', dimension: 'length', factor: 1, note: '基准单位' },
99
- { name: 'km', dimension: 'length', factor: 1000 },
100
- { name: 'cm', dimension: 'length', factor: 0.01 },
101
- { name: 'mm', dimension: 'length', factor: 0.001 },
102
- {
103
- name: '里',
104
- dimension: 'length',
105
- factor: 500,
106
- note: '市里 = 500 m。周制约 415 m、汉制约 415 m、清制 576 m——写古代按朝代覆盖',
107
- },
108
- { name: '丈', dimension: 'length', factor: 3.2, note: '市丈 = 3.2 m(10 尺)' },
109
- { name: '尺', dimension: 'length', factor: 0.32, note: '市尺 ≈ 32 cm。汉尺约 23 cm、明清营造尺约 32 cm' },
110
- { name: '寸', dimension: 'length', factor: 0.032, note: '市寸 = 1/10 尺' },
111
- { name: '步', dimension: 'length', factor: 1.6, note: '古代一步 ≈ 5 尺 ≈ 1.6 m(「百步穿杨」约 160 m)' },
112
- { name: 'mile', aliases: ['英里', 'mi'], dimension: 'length', factor: 1609.344 },
113
- { name: 'ft', aliases: ['英尺'], dimension: 'length', factor: 0.3048 },
114
- { name: 'in', aliases: ['英寸'], dimension: 'length', factor: 0.0254 },
115
- { name: 'nmi', aliases: ['海里'], dimension: 'length', factor: 1852 },
116
- { name: 'AU', aliases: ['天文单位'], dimension: 'length', factor: 1.495978707e11, note: '日地平均距离' },
117
- { name: 'ly', aliases: ['光年'], dimension: 'length', factor: 9.4607304725808e15 },
118
- { name: 'pc', aliases: ['秒差距'], dimension: 'length', factor: 3.0856775814913673e16 },
119
- { name: 'Rearth', aliases: ['地球半径'], dimension: 'length', factor: 6.371e6 },
120
- { name: 'Rsun', aliases: ['太阳半径'], dimension: 'length', factor: 6.957e8 },
121
-
122
- // ── 质量 ──
123
- { name: 'kg', dimension: 'mass', factor: 1, note: '基准单位' },
124
- { name: 'g', dimension: 'mass', factor: 0.001 },
125
- { name: 't', aliases: ['吨'], dimension: 'mass', factor: 1000 },
126
- { name: '斤', dimension: 'mass', factor: 0.5, note: '市斤 = 500 g(10 两)' },
127
- { name: '两', dimension: 'mass', factor: 0.05, note: '市两 = 50 g' },
128
- { name: '石', dimension: 'mass', factor: 60, note: '容量/重量混用单位,此处按「一石米约 60 kg」估' },
129
- { name: 'lb', aliases: ['磅'], dimension: 'mass', factor: 0.45359237 },
130
- { name: 'oz', aliases: ['盎司'], dimension: 'mass', factor: 0.028349523125 },
131
- { name: 'Mearth', aliases: ['地球质量'], dimension: 'mass', factor: 5.972e24 },
132
- { name: 'Msun', aliases: ['太阳质量'], dimension: 'mass', factor: 1.989e30 },
133
-
134
- // ── 时间 ──
135
- { name: 's', dimension: 'time', factor: 1, note: '基准单位' },
136
- { name: 'min', aliases: ['分钟'], dimension: 'time', factor: 60 },
137
- { name: 'h', aliases: ['小时'], dimension: 'time', factor: 3600, note: '与普朗克常数 h 同名,单位换算里按小时理解' },
138
- { name: 'day', aliases: ['天', '日'], dimension: 'time', factor: 86400 },
139
- { name: 'year', aliases: ['年'], dimension: 'time', factor: 3.15576e7, note: '儒略年 = 365.25 天' },
140
- { name: 'Gyr', aliases: ['十亿年'], dimension: 'time', factor: 3.15576e16 },
141
- { name: '刻', dimension: 'time', factor: 900, note: '古代一刻 ≈ 15 分钟(百刻制一天)' },
142
- { name: '时辰', dimension: 'time', factor: 7200, note: '一个时辰 = 2 小时(十二时辰制)' },
143
- { name: '更', dimension: 'time', factor: 7200, note: '一夜五更,每更约 2 小时' },
144
-
145
- // ── 面积 ──
146
- { name: 'm²', dimension: 'area', factor: 1, note: '基准单位' },
147
- { name: 'km²', dimension: 'area', factor: 1e6 },
148
- { name: 'ha', aliases: ['公顷'], dimension: 'area', factor: 1e4 },
149
- { name: '亩', dimension: 'area', factor: 666.6666666666666, note: '市亩 ≈ 666.7 m²(15 亩 = 1 公顷)' },
150
- { name: '顷', dimension: 'area', factor: 66666.66666666667, note: '一顷 = 100 亩' },
151
- { name: 'acre', aliases: ['英亩'], dimension: 'area', factor: 4046.8564224 },
152
-
153
- // ── 体积 ──
154
- { name: 'm³', dimension: 'volume', factor: 1, note: '基准单位' },
155
- { name: 'L', aliases: ['升'], dimension: 'volume', factor: 0.001 },
156
- { name: '斗', dimension: 'volume', factor: 0.01, note: '市斗 = 10 L;古代一斗约 2—20 L,差异极大' },
157
- { name: 'gal', aliases: ['加仑'], dimension: 'volume', factor: 0.003785411784, note: '美制加仑' },
158
-
159
- // ── 能量 ──
160
- { name: 'J', dimension: 'energy', factor: 1, note: '基准单位' },
161
- { name: 'kJ', dimension: 'energy', factor: 1000 },
162
- { name: 'MJ', dimension: 'energy', factor: 1e6 },
163
- { name: 'GJ', dimension: 'energy', factor: 1e9 },
164
- { name: 'kWh', aliases: ['度'], dimension: 'energy', factor: 3.6e6, note: '一度电 = 3.6 MJ' },
165
- { name: 'cal', aliases: ['卡'], dimension: 'energy', factor: 4.184 },
166
- { name: 'kcal', aliases: ['大卡', '千卡'], dimension: 'energy', factor: 4184, note: '营养学的「卡路里」其实是大卡' },
167
- { name: 'TNT', dimension: 'energy', factor: 4.184e9, note: '1 吨 TNT 当量' },
168
- { name: 'eV', dimension: 'energy', factor: 1.602176634e-19 },
169
- { name: 'eVperBond', aliases: [], dimension: 'energy', factor: 1.602176634e-19 },
170
-
171
- // ── 功率 ──
172
- { name: 'W', dimension: 'power', factor: 1, note: '基准单位' },
173
- { name: 'kW', dimension: 'power', factor: 1000 },
174
- { name: 'MW', dimension: 'power', factor: 1e6 },
175
- { name: 'GW', dimension: 'power', factor: 1e9 },
176
- { name: 'TW', dimension: 'power', factor: 1e12 },
177
- { name: 'hp', aliases: ['马力'], dimension: 'power', factor: 745.6998715822702 },
178
- { name: '人', aliases: ['人力'], dimension: 'power', factor: 75, note: '一个成年人持续体力输出约 75—100 W(长时间)' },
179
- { name: '畜', aliases: ['畜力'], dimension: 'power', factor: 500, note: '一头上等役畜持续输出约 500 W' },
180
-
181
- // ── 温度 ──
182
- // 温度是仿射换算(有偏移),不能只用系数;见 convert() 里的专门分支
183
- { name: 'K', dimension: 'temperature', factor: 1, note: '基准单位,无偏移' },
184
- { name: '°C', aliases: ['℃', '摄氏度'], dimension: 'temperature', factor: 1, note: 'K = °C + 273.15' },
185
- { name: '°F', aliases: ['℉', '华氏度'], dimension: 'temperature', factor: 5 / 9, note: 'K = (°F + 459.67) × 5/9' },
186
-
187
- // ── 速度 ──
188
- { name: 'm/s', dimension: 'speed', factor: 1, note: '基准单位' },
189
- { name: 'km/h', dimension: 'speed', factor: 1 / 3.6 },
190
- { name: 'mph', aliases: ['英里每小时'], dimension: 'speed', factor: 0.44704 },
191
- { name: 'mach', aliases: ['马赫'], dimension: 'speed', factor: 340.29, note: '按海平面 15°C 声速折算;高空声速更低' },
192
- { name: 'c', aliases: ['光速'], dimension: 'speed', factor: 299792458 },
193
- ]
151
+ /**
152
+ * 内置单位表 —— 从 `units-data.ts` 导入(那里按体系分节,便于查阅与扩充)。
153
+ *
154
+ * 现在是 **288 条**:公制(含 SI 词头)、中国市制与古制、英制与美制、
155
+ * 航海、天文、日本、俄国、印度、古希腊罗马,外加数据量/角度/力/频率。
156
+ */
157
+ export const UNITS: UnitDef[] = ALL_UNITS
194
158
 
195
159
  /**
196
160
  * 按名字查单位(含别名)。
@@ -325,14 +289,69 @@ export function convert(
325
289
  } else {
326
290
  const inBase = value * from.factor
327
291
  result = inBase / to.factor
328
- steps.push('1 ' + from.name + ' = ' + from.factor + ' ' + BASE_UNIT[from.dimension])
329
- steps.push(
330
- show(value) + ' ' + from.name + ' = ' + show(inBase) + ' ' + BASE_UNIT[from.dimension],
331
- )
332
- steps.push(
333
- '1 ' + to.name + ' = ' + to.factor + ' ' + BASE_UNIT[to.dimension] +
334
- ',故结果 = ' + show(result) + ' ' + to.name,
335
- )
292
+ /**
293
+ * 代入过程只写「有信息量」的步骤。
294
+ *
295
+ * ## 修的是什么
296
+ *
297
+ * 实测反馈(2026-11-06)原话:换算 kg → t 时「代入过程」里出现
298
+ * 「1 kg = 1 kg」「9.0000e+15 kg = 9.0000e+15 kg」这类**同义重复**,
299
+ * 属无效信息 —— 而且它让输出**看起来有内容,其实没有**。
300
+ *
301
+ * 根因:当 from 或 to 恰好是基准单位(factor = 1)时,
302
+ * 那一步就是「X = X」,什么都没说明。
303
+ *
304
+ * 现在的规则:
305
+ * · from.factor ≠ 1 时才写「1 from = N base」(否则 1 = 1,无信息)
306
+ * · from.factor ≠ 1 时才写「value from = inBase base」(上面那行是它的依据)
307
+ * · to.factor ≠ 1 时才写「1 to = M base」;无论哪种都写「故结果 = ...」
308
+ *
309
+ * 换算到基准单位时,过程就只剩一行结论 —— 那本来就是它的全部信息。
310
+ */
311
+ const fromIsBase = from.factor === 1
312
+ const toIsBase = to.factor === 1
313
+ const baseName = BASE_UNIT[from.dimension]
314
+
315
+ /**
316
+ * ## 合并成一行,避免同一个换算关系说两遍
317
+ *
318
+ * 踩过:`t → kg` 时先写「1 t = 1000 kg」,再写
319
+ * 「9.0000e+15 t = 9.0000e+15 kg」—— 两行说的是**同一件事**
320
+ * (因为 inBase 恰好等于 inBase,且 to 就是基准单位)。
321
+ *
322
+ * 现在按信息量分三种:
323
+ * · 源非基准、目标非基准 → 「1 from = N base,1 to = M base,故结果」
324
+ * · 源非基准、目标是基准 → 「value from = X base,故结果 = X to」
325
+ * · 源是基准、目标非基准 → 「1 to = M base,故结果」
326
+ * · 两者都是基准 → 只剩结论(其实同名,前面已处理)
327
+ */
328
+ if (!fromIsBase && !toIsBase) {
329
+ /**
330
+ * 两个都非基准 → 中间值**必须给**。
331
+ *
332
+ * 它是「30 里 = 15000 m = 15 km」这条链的中段:
333
+ * 少了它,调用方无法核对「500 × 30」这一步是否算对。
334
+ * 这与「去掉同义重复」不冲突 —— 重复指的是 `1 X = 1 X` 那种零信息行,
335
+ * 而中间值是真信息。
336
+ */
337
+ steps.push('1 ' + from.name + ' = ' + from.factor + ' ' + baseName)
338
+ steps.push(show(value) + ' ' + from.name + ' = ' + show(inBase) + ' ' + baseName)
339
+ steps.push('1 ' + to.name + ' = ' + to.factor + ' ' + baseName)
340
+ steps.push('故结果 = ' + show(result) + ' ' + to.name)
341
+ } else if (!fromIsBase && toIsBase) {
342
+ steps.push(show(value) + ' ' + from.name + ' = ' + show(inBase) + ' ' + baseName)
343
+ steps.push('故结果 = ' + show(result) + ' ' + to.name)
344
+ } else if (fromIsBase && !toIsBase) {
345
+ steps.push('1 ' + to.name + ' = ' + to.factor + ' ' + baseName)
346
+ steps.push('故结果 = ' + show(result) + ' ' + to.name)
347
+ } else {
348
+ steps.push('故结果 = ' + show(result) + ' ' + to.name)
349
+ }
350
+ }
351
+ if (from.name === to.name) {
352
+ /** 源与目标同名:换算本身无操作,别编出一段过程。 */
353
+ steps.length = 0
354
+ steps.push('源单位与目标单位同名,数值不变。')
336
355
  }
337
356
 
338
357
  if (from.note) warnings.push(from.name + ':' + from.note)
package/src/workspace.ts CHANGED
@@ -81,6 +81,19 @@ export interface WorkspaceData {
81
81
  * 未设置时沿用计算精度(即完整输出,旧行为)。
82
82
  */
83
83
  displayPrecision?: number
84
+ /**
85
+ * 是否让 `variables` 里的命名变量**参与计算**。
86
+ *
87
+ * ## 为什么默认关闭
88
+ *
89
+ * 打开后,同一个调用在不同工作区状态下会给出不同结果 ——
90
+ * 那会削弱「同输入同输出」,而后者是本项目做 hash 复现与回归对比的基础。
91
+ * 所以这是一个**显式的、逐工作区的**选择,不默认生效。
92
+ *
93
+ * 关闭时 `variables` 仍然照常存着(能 show、能 remove),
94
+ * 只是不参与计算 —— 与未引入这个功能时的行为完全一致。
95
+ */
96
+ useWorkspaceVariables?: boolean
84
97
  /** 创建时间。 */
85
98
  createdAt: string
86
99
  /** 最后修改时间。 */
@@ -899,6 +912,58 @@ export function bindHostWorkspace(
899
912
  * @param name - 工作区名;省略用当前活动工作区
900
913
  * @returns 显示有效位数;未设置返回 undefined
901
914
  */
915
+ /**
916
+ * 读出当前工作区里登记的命名变量(`variables` 字段)。
917
+ *
918
+ * ## 这个函数为什么存在
919
+ *
920
+ * 工作区的 `variables` 字段、写入路径(`action2:"set"` + `workspaceVariables`)、
921
+ * 删除路径(`remove-variable`)、显示(`show`)**都已经有了**,
922
+ * 唯独**读取路径缺**——存进去的变量在计算里根本用不到。
923
+ *
924
+ * 实测:
925
+ *
926
+ * ```
927
+ * {action2:"set", workspaceVariables:{BTU_LB:1260000}} → 变量 2 个 ✓ 存住了
928
+ * {expression:"BTU_LB/1000"} → Undefined symbol ✗ 用不了
929
+ * ```
930
+ *
931
+ * 也就是说这是「能删不能加、加了用不了」的半成品,不是需要从头设计的新能力。
932
+ *
933
+ * ## 失败时返回空对象,不抛异常
934
+ *
935
+ * 读工作区失败(文件损坏、目录不可写)不该让一次正常计算失败。
936
+ * 但**空对象与「真的没有变量」无法区分**,所以缓存指纹那边
937
+ * 不能只靠这个函数的返回值 —— 见 `index.ts` 里的 `workspaceVariablesFingerprint`。
938
+ *
939
+ * @param name - 工作区名;省略则用当前工作区
940
+ * @returns 变量字典(数字形态);读不到时为空对象
941
+ */
942
+ export function workspaceVariablesOf(name?: string): Record<string, number> {
943
+ try {
944
+ const data = loadWorkspace(name ?? activeWorkspaceName)
945
+ const source = data.variables
946
+ if (!source || typeof source !== 'object') return {}
947
+ const result: Record<string, number> = {}
948
+ for (const [key, value] of Object.entries(source)) {
949
+ const numeric = typeof value === 'number' ? value : Number(value)
950
+ if (Number.isFinite(numeric)) result[key] = numeric
951
+ }
952
+ return result
953
+ } catch {
954
+ return {}
955
+ }
956
+ }
957
+
958
+ /**
959
+ * 读当前工作区固化的**显示**精度(小数点后几位)。
960
+ *
961
+ * 未设或读到 0 时返回 undefined,调用方据此回落默认值。
962
+ * 语义与「本次调用传入的 displayPrecision」一致:都是小数点后位数。
963
+ *
964
+ * @param name - 工作区名;省略则用当前工作区
965
+ * @returns 显示位数;未设或读不到返回 undefined
966
+ */
902
967
  export function displayPrecisionOf(name?: string): number | undefined {
903
968
  try {
904
969
  const data = loadWorkspace(name ?? activeWorkspaceName)