niuma-ui 1.0.2 → 1.1.1

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 (88) hide show
  1. package/CHANGELOG.md +37 -1
  2. package/package.json +1 -1
  3. package/src/__tests__/RsDatePicker.spec.ts +21 -0
  4. package/src/__tests__/RsDialog.spec.ts +61 -0
  5. package/src/__tests__/RsForm.spec.ts +5 -5
  6. package/src/__tests__/RsFormItem.spec.ts +478 -0
  7. package/src/__tests__/RsFormList.spec.ts +109 -0
  8. package/src/__tests__/RsInput.spec.ts +93 -0
  9. package/src/__tests__/RsInputNumber.spec.ts +33 -0
  10. package/src/__tests__/RsSelect.spec.ts +423 -1
  11. package/src/__tests__/RsSwitch.spec.ts +30 -0
  12. package/src/__tests__/RsTimePicker.spec.ts +9 -0
  13. package/src/__tests__/RsTooltip.spec.ts +43 -0
  14. package/src/__tests__/css-token.spec.ts +51 -0
  15. package/src/__tests__/date-picker-utils.spec.ts +49 -0
  16. package/src/__tests__/dialog-utils.spec.ts +43 -0
  17. package/src/__tests__/form-path.spec.ts +46 -0
  18. package/src/__tests__/form-rules.spec.ts +63 -0
  19. package/src/__tests__/theme.spec.ts +2 -0
  20. package/src/components/RsAlert.vue +1 -1
  21. package/src/components/RsAvatar.vue +1 -1
  22. package/src/components/RsBadge.vue +1 -1
  23. package/src/components/RsBreadcrumb.vue +1 -1
  24. package/src/components/RsButton.vue +4 -4
  25. package/src/components/RsCalendarGrid.vue +3 -3
  26. package/src/components/RsCard.vue +4 -4
  27. package/src/components/RsCodeBlock.vue +4 -4
  28. package/src/components/RsCodeEditor.vue +8 -8
  29. package/src/components/RsConfirmDialog.vue +2 -2
  30. package/src/components/RsContextMenu.vue +6 -4
  31. package/src/components/RsDatePicker.vue +75 -31
  32. package/src/components/RsDateTimePicker.vue +9 -1
  33. package/src/components/RsDescriptions.vue +2 -2
  34. package/src/components/RsDialog.vue +26 -16
  35. package/src/components/RsDrawer.vue +1 -1
  36. package/src/components/RsDropdown.vue +1 -1
  37. package/src/components/RsDynamicTags.vue +18 -4
  38. package/src/components/RsEmpty.vue +1 -1
  39. package/src/components/RsForm.vue +68 -1
  40. package/src/components/RsFormItem.vue +423 -0
  41. package/src/components/RsFormItemControl.vue +21 -0
  42. package/src/components/RsFormList.vue +117 -0
  43. package/src/components/RsInput.vue +411 -18
  44. package/src/components/RsInputNumber.vue +13 -4
  45. package/src/components/RsLink.vue +1 -1
  46. package/src/components/RsMarkdown.vue +5 -5
  47. package/src/components/RsMenu.vue +2 -2
  48. package/src/components/RsMonacoEditor.vue +9 -3
  49. package/src/components/RsPagination.vue +1 -1
  50. package/src/components/RsProseEditor.vue +3 -3
  51. package/src/components/RsSelect.vue +332 -611
  52. package/src/components/RsSidebarGroup.vue +1 -1
  53. package/src/components/RsSplitPane.vue +22 -1
  54. package/src/components/RsStatCard.vue +2 -2
  55. package/src/components/RsSteps.vue +2 -2
  56. package/src/components/RsSwitch.vue +20 -7
  57. package/src/components/RsTabs.vue +9 -9
  58. package/src/components/RsTag.vue +1 -1
  59. package/src/components/RsTerminal.vue +58 -19
  60. package/src/components/RsTimePicker.vue +28 -4
  61. package/src/components/RsTimePickerColumns.vue +4 -4
  62. package/src/components/RsToaster.vue +7 -7
  63. package/src/components/RsTooltip.vue +10 -1
  64. package/src/components/RsTooltipProvider.vue +11 -1
  65. package/src/components/RsTree.vue +6 -6
  66. package/src/components/RsUpload.vue +2 -2
  67. package/src/components/date-picker-utils.ts +113 -2
  68. package/src/components/dialog-utils.ts +37 -2
  69. package/src/components/form-item-control.ts +55 -0
  70. package/src/components/form-path.ts +92 -0
  71. package/src/components/form-rules.ts +64 -10
  72. package/src/components/form-utils.ts +94 -2
  73. package/src/components/select-utils.ts +272 -12
  74. package/src/components/table/RsTableCellEditor.vue +13 -5
  75. package/src/components/table/RsTableHeaderFilter.vue +1 -1
  76. package/src/components/table/rs-table.css +1 -1
  77. package/src/components/use-rs-select.ts +436 -0
  78. package/src/composables/useRsConfig.ts +10 -3
  79. package/src/composables/useRsI18n.ts +16 -5
  80. package/src/composables/useRsTableCore.ts +3 -1
  81. package/src/composables/useRsTableHeadless.ts +10 -2
  82. package/src/index.ts +69 -1
  83. package/src/locale/interpolate.ts +32 -0
  84. package/src/locale/messages.ts +10 -0
  85. package/src/styles.css +68 -12
  86. package/src/theme/brand.example.css +4 -1
  87. package/src/theme/css-token.ts +107 -0
  88. package/src/theme/types.ts +1 -0
@@ -4,8 +4,10 @@ import {
4
4
  formatDateTimeDisplay,
5
5
  formatDateTimeValue,
6
6
  formatDateValue,
7
+ fromInternalPickerValue,
7
8
  parseDateTimeValue,
8
9
  parseDateValue,
10
+ toInternalPickerValue,
9
11
  } from '../components/date-picker-utils'
10
12
  import { RS_DATE_FORMAT, RS_DATETIME_FORMAT } from '../lib/rs-dayjs'
11
13
 
@@ -68,3 +70,50 @@ describe('date-picker-utils (dayjs)', () => {
68
70
  expect(formatDateTimeValue('not-a-date')).toBe('not-a-date')
69
71
  })
70
72
  })
73
+
74
+ describe('date-picker valueFormat conversion', () => {
75
+ const datetimeOpts = { valueFormat: 'iso', withTime: true }
76
+ const dateOpts = { valueFormat: 'iso', withTime: false }
77
+
78
+ it('converts internal datetime to local-offset RFC3339', () => {
79
+ const iso = fromInternalPickerValue('2025-06-16 14:30:00', datetimeOpts)
80
+ expect(iso).toMatch(/^2025-06-16T14:30:00[+-]\d{2}:\d{2}$/)
81
+ })
82
+
83
+ it('converts internal date to RFC3339 at midnight local offset', () => {
84
+ const iso = fromInternalPickerValue('2025-06-16', dateOpts)
85
+ expect(iso).toMatch(/^2025-06-16T00:00:00[+-]\d{2}:\d{2}$/)
86
+ })
87
+
88
+ it('round-trips iso datetime as wall clock', () => {
89
+ const iso = fromInternalPickerValue('2025-06-16 14:30:00', datetimeOpts)
90
+ expect(toInternalPickerValue(iso, datetimeOpts)).toBe('2025-06-16 14:30:00')
91
+ expect(toInternalPickerValue('2026-07-01T14:49:43Z', datetimeOpts)).toBe('2026-07-01 14:49:43')
92
+ expect(toInternalPickerValue('2026-08-14T14:38:42+08:00', datetimeOpts)).toBe('2026-08-14 14:38:42')
93
+ })
94
+
95
+ it('keeps string format as internal wall clock', () => {
96
+ expect(fromInternalPickerValue('2025-06-16 14:30:00', { valueFormat: 'string', withTime: true }))
97
+ .toBe('2025-06-16 14:30:00')
98
+ expect(toInternalPickerValue('2025-06-16 14:30:00', { valueFormat: 'string', withTime: true }))
99
+ .toBe('2025-06-16 14:30:00')
100
+ })
101
+
102
+ it('converts timestamp format', () => {
103
+ const ms = fromInternalPickerValue('2025-06-16 14:30:00', { valueFormat: 'timestamp', withTime: true })
104
+ expect(typeof ms).toBe('number')
105
+ expect(toInternalPickerValue(ms, { valueFormat: 'timestamp', withTime: true })).toBe('2025-06-16 14:30:00')
106
+ })
107
+
108
+ it('supports custom dayjs value-format templates', () => {
109
+ const custom = { valueFormat: 'YYYY-MM-DDTHH:mm:ss', withTime: true }
110
+ expect(fromInternalPickerValue('2025-06-16 14:30:00', custom)).toBe('2025-06-16T14:30:00')
111
+ expect(toInternalPickerValue('2025-06-16T14:30:00', custom)).toBe('2025-06-16 14:30:00')
112
+ })
113
+
114
+ it('emits null for empty iso and timestamp', () => {
115
+ expect(fromInternalPickerValue('', datetimeOpts)).toBeNull()
116
+ expect(fromInternalPickerValue('', { valueFormat: 'timestamp', withTime: true })).toBeNull()
117
+ expect(fromInternalPickerValue('', { valueFormat: 'string', withTime: true })).toBe('')
118
+ })
119
+ })
@@ -0,0 +1,43 @@
1
+ import { afterEach, describe, expect, it } from 'vitest'
2
+ import { resolveRsDialogCssWidth, resolveRsDialogWidthPx } from '../components/dialog-utils'
3
+
4
+ describe('resolveRsDialogWidthPx', () => {
5
+ afterEach(() => {
6
+ document.documentElement.style.removeProperty('font-size')
7
+ })
8
+
9
+ it('returns undefined for presets', () => {
10
+ expect(resolveRsDialogWidthPx('sm')).toBeUndefined()
11
+ expect(resolveRsDialogWidthPx('md')).toBeUndefined()
12
+ expect(resolveRsDialogWidthPx('lg')).toBeUndefined()
13
+ })
14
+
15
+ it('keeps numeric and px widths', () => {
16
+ expect(resolveRsDialogWidthPx(1200)).toBe(1200)
17
+ expect(resolveRsDialogWidthPx('960px')).toBe(960)
18
+ })
19
+
20
+ it('converts rem using root font size', () => {
21
+ document.documentElement.style.fontSize = '16px'
22
+ expect(resolveRsDialogWidthPx('40rem')).toBe(640)
23
+ })
24
+
25
+ it('converts percent against the given viewport width', () => {
26
+ expect(resolveRsDialogWidthPx('90%', 1000)).toBe(900)
27
+ expect(resolveRsDialogWidthPx('80%', 800)).toBe(640)
28
+ })
29
+
30
+ it('returns undefined for empty or unknown units', () => {
31
+ expect(resolveRsDialogWidthPx('')).toBeUndefined()
32
+ expect(resolveRsDialogWidthPx('min(90vw, 1200px)')).toBeUndefined()
33
+ expect(resolveRsDialogWidthPx(0)).toBeUndefined()
34
+ })
35
+ })
36
+
37
+ describe('resolveRsDialogCssWidth', () => {
38
+ it('maps number to px and keeps custom CSS strings', () => {
39
+ expect(resolveRsDialogCssWidth('md')).toBeUndefined()
40
+ expect(resolveRsDialogCssWidth(520)).toBe('520px')
41
+ expect(resolveRsDialogCssWidth('90%')).toBe('90%')
42
+ })
43
+ })
@@ -0,0 +1,46 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import {
3
+ concatNamePath,
4
+ getByNamePath,
5
+ hasByNamePath,
6
+ namePathKey,
7
+ normalizeNamePath,
8
+ setByNamePath,
9
+ } from '../components/form-path'
10
+
11
+ describe('form-path', () => {
12
+ it('normalizes string and array name paths', () => {
13
+ expect(normalizeNamePath('user.email')).toEqual(['user', 'email'])
14
+ expect(normalizeNamePath(['user', 'email'])).toEqual(['user', 'email'])
15
+ expect(namePathKey(['a', 0, 'b'])).toBe('a.0.b')
16
+ })
17
+
18
+ it('gets and sets nested values', () => {
19
+ const model: Record<string, unknown> = {}
20
+ setByNamePath(model, 'user.email', 'a@b.com')
21
+ expect(model).toEqual({ user: { email: 'a@b.com' } })
22
+ expect(getByNamePath(model, ['user', 'email'])).toBe('a@b.com')
23
+ expect(hasByNamePath(model, 'user.email')).toBe(true)
24
+ })
25
+
26
+ it('walks multi-level JSON paths', () => {
27
+ const model: Record<string, unknown> = {}
28
+ setByNamePath(model, 'config.headerConfig.isRequestHeader', true)
29
+ expect(model).toEqual({
30
+ config: { headerConfig: { isRequestHeader: true } },
31
+ })
32
+ expect(getByNamePath(model, 'config.headerConfig.isRequestHeader')).toBe(true)
33
+ expect(hasByNamePath(model, 'config.headerConfig.headerName')).toBe(false)
34
+ })
35
+
36
+ it('preserves arrays when setting nested list fields', () => {
37
+ const model: Record<string, unknown> = { users: [{ name: 'A' }] }
38
+ setByNamePath(model, ['users', 0, 'name'], 'B')
39
+ expect(model.users).toEqual([{ name: 'B' }])
40
+ expect(Array.isArray(model.users)).toBe(true)
41
+ })
42
+
43
+ it('concatenates list prefix and item name', () => {
44
+ expect(concatNamePath('users', [0, 'name'])).toEqual(['users', 0, 'name'])
45
+ })
46
+ })
@@ -63,6 +63,11 @@ describe('form-rules', () => {
63
63
  expect(rules[1]?.type).toBe('email')
64
64
  })
65
65
 
66
+ it('interpolates required message with label', async () => {
67
+ const labeled = await runFormFieldRules('', [{ required: true }], { label: '邮箱' })
68
+ expect(labeled.message).toBe('请填写邮箱')
69
+ })
70
+
66
71
  it('supports message as function', async () => {
67
72
  const result = await runFormFieldRules('x', [
68
73
  {
@@ -73,4 +78,62 @@ describe('form-rules', () => {
73
78
  expect(result.valid).toBe(false)
74
79
  expect(result.message).toBe('当前「x」太短')
75
80
  })
81
+
82
+ it('validator can read other fields via context', async () => {
83
+ const result = await runFormFieldRules('two', [
84
+ {
85
+ validator(value, ctx) {
86
+ if (String(value) !== String(ctx.getFieldValue('a'))) return '不一致'
87
+ return true
88
+ },
89
+ },
90
+ ], {
91
+ getFieldValue: (name) => (String(name) === 'a' ? 'one' : undefined),
92
+ })
93
+ expect(result).toEqual({ valid: false, message: '不一致' })
94
+
95
+ const ok = await runFormFieldRules('one', [
96
+ {
97
+ validator(value, ctx) {
98
+ if (String(value) !== String(ctx.getFieldValue('a'))) return '不一致'
99
+ return true
100
+ },
101
+ },
102
+ ], {
103
+ getFieldValue: (name) => (String(name) === 'a' ? 'one' : undefined),
104
+ })
105
+ expect(ok.valid).toBe(true)
106
+ })
107
+
108
+ it('runs custom validator on empty value for cross-field required', async () => {
109
+ const result = await runFormFieldRules(
110
+ '',
111
+ [
112
+ {
113
+ validator(_value, ctx) {
114
+ if (ctx.getFieldValue('kind') === 'email') return '邮箱必填'
115
+ return true
116
+ },
117
+ },
118
+ ],
119
+ { getFieldValue: (name) => (String(name) === 'kind' ? 'email' : undefined) },
120
+ )
121
+ expect(result).toEqual({ valid: false, message: '邮箱必填' })
122
+ })
123
+
124
+ it('treats Error return and thrown errors as failure', async () => {
125
+ const returned = await runFormFieldRules('x', [
126
+ { validator: () => new Error('返回错误') as unknown as string },
127
+ ])
128
+ expect(returned).toEqual({ valid: false, message: '返回错误' })
129
+
130
+ const thrown = await runFormFieldRules('x', [
131
+ {
132
+ validator() {
133
+ throw new Error('抛出错误')
134
+ },
135
+ },
136
+ ])
137
+ expect(thrown).toEqual({ valid: false, message: '抛出错误' })
138
+ })
76
139
  })
@@ -17,6 +17,8 @@ describe('useRsConfig / theme', () => {
17
17
  expect(en.t('select.placeholder')).toBe('Select')
18
18
  expect(zh.t('input.required')).toBe('此项为必填')
19
19
  expect(en.t('input.required')).toBe('This field is required')
20
+ expect(zh.t('form.validate.required', { label: '邮箱' })).toBe('请填写邮箱')
21
+ expect(en.t('form.validate.required', { label: 'Email' })).toBe('Please enter Email')
20
22
  expect(en.t('breadcrumb.label')).toBe('Breadcrumb')
21
23
  })
22
24
 
@@ -108,7 +108,7 @@ function onClose(event: MouseEvent): void {
108
108
 
109
109
  .rs-alert__title {
110
110
  font-size: var(--rs-font-size-sm);
111
- font-weight: 600;
111
+ font-weight: var(--rs-font-weight-semibold);
112
112
  line-height: var(--rs-line-height-tight);
113
113
  color: inherit;
114
114
  }
@@ -90,7 +90,7 @@ const ariaLabel = computed(() => props.label ?? props.alt ?? props.name ?? undef
90
90
  width: 100%;
91
91
  height: 100%;
92
92
  font-size: var(--rs-font-size-xs);
93
- font-weight: 600;
93
+ font-weight: var(--rs-font-weight-semibold);
94
94
  }
95
95
  .rs-avatar__fallback--default {
96
96
  background: var(--rs-surface-hover);
@@ -22,7 +22,7 @@ withDefaults(
22
22
  padding: 0.125rem 0.625rem;
23
23
  border-radius: var(--rs-radius-full);
24
24
  font-size: var(--rs-font-size-xs);
25
- font-weight: 500;
25
+ font-weight: var(--rs-font-weight-medium);
26
26
  line-height: var(--rs-line-height-tight);
27
27
  letter-spacing: 0.02em;
28
28
  }
@@ -56,7 +56,7 @@ const renderItems = computed(() => buildBreadcrumbRenderItems(props.items))
56
56
  }
57
57
  .rs-breadcrumb__link--current {
58
58
  color: var(--rs-text);
59
- font-weight: 500;
59
+ font-weight: var(--rs-font-weight-medium);
60
60
  pointer-events: none;
61
61
  }
62
62
  .rs-breadcrumb__sep {
@@ -196,7 +196,7 @@ watch(
196
196
  border-radius: var(--rs-btn-radius, var(--rs-radius-full));
197
197
  border: 1px solid transparent;
198
198
  font-size: var(--rs-font-size-sm);
199
- font-weight: 500;
199
+ font-weight: var(--rs-font-weight-medium);
200
200
  letter-spacing: 0.01em;
201
201
  line-height: 1;
202
202
  white-space: nowrap;
@@ -329,7 +329,7 @@ watch(
329
329
  background: transparent;
330
330
  color: var(--rs-btn-tone, var(--rs-primary));
331
331
  box-shadow: none;
332
- font-weight: 500;
332
+ font-weight: var(--rs-font-weight-medium);
333
333
  }
334
334
  .rs-btn--link:hover:not(:disabled) {
335
335
  color: var(--rs-btn-tone-hover, var(--rs-primary-hover));
@@ -346,7 +346,7 @@ watch(
346
346
  border-color: transparent;
347
347
  background: transparent;
348
348
  box-shadow: none;
349
- font-weight: 400;
349
+ font-weight: var(--rs-font-weight-regular);
350
350
  color: var(--rs-btn-tone, var(--rs-text));
351
351
  }
352
352
  .rs-btn--text.rs-btn--ssm {
@@ -474,7 +474,7 @@ watch(
474
474
  background: var(--rs-surface-elevated);
475
475
  color: var(--rs-text);
476
476
  font-size: var(--rs-font-size-xs);
477
- font-weight: 500;
477
+ font-weight: var(--rs-font-weight-medium);
478
478
  line-height: 1.25rem;
479
479
  white-space: nowrap;
480
480
  box-shadow: var(--rs-shadow-sm);
@@ -204,7 +204,7 @@ function selectDate(cell: RsCalendarCell): void {
204
204
  flex: 1;
205
205
  text-align: center;
206
206
  font-size: var(--rs-font-size-sm);
207
- font-weight: 600;
207
+ font-weight: var(--rs-font-weight-semibold);
208
208
  color: var(--rs-text);
209
209
  }
210
210
  .rs-calendar-grid__nav {
@@ -233,8 +233,8 @@ function selectDate(cell: RsCalendarCell): void {
233
233
  .rs-calendar-grid__weekday {
234
234
  padding: 0.125rem 0;
235
235
  text-align: center;
236
- font-size: 0.6875rem;
237
- font-weight: 500;
236
+ font-size: var(--rs-font-size-xs);
237
+ font-weight: var(--rs-font-weight-medium);
238
238
  color: var(--rs-muted);
239
239
  }
240
240
  .rs-calendar-grid__day {
@@ -232,7 +232,7 @@ const rootStyle = computed(() => ({
232
232
  }
233
233
  .rs-card--plain .rs-card__title {
234
234
  font-size: var(--rs-font-size-xs);
235
- font-weight: 500;
235
+ font-weight: var(--rs-font-weight-medium);
236
236
  color: var(--rs-card-description-fg);
237
237
  letter-spacing: 0;
238
238
  }
@@ -255,7 +255,7 @@ const rootStyle = computed(() => ({
255
255
  box-shadow: none;
256
256
  }
257
257
  .rs-card--outlined .rs-card__title {
258
- font-weight: 500;
258
+ font-weight: var(--rs-font-weight-medium);
259
259
  letter-spacing: 0;
260
260
  }
261
261
  .rs-card--outlined .rs-card__body {
@@ -277,7 +277,7 @@ const rootStyle = computed(() => ({
277
277
  box-shadow: none;
278
278
  }
279
279
  .rs-card--filled .rs-card__title {
280
- font-weight: 500;
280
+ font-weight: var(--rs-font-weight-medium);
281
281
  letter-spacing: 0;
282
282
  }
283
283
  .rs-card--filled .rs-card__body {
@@ -385,7 +385,7 @@ const rootStyle = computed(() => ({
385
385
  .rs-card__title {
386
386
  margin: 0;
387
387
  font-size: var(--rs-card-title-size);
388
- font-weight: 600;
388
+ font-weight: var(--rs-font-weight-semibold);
389
389
  letter-spacing: -0.01em;
390
390
  color: var(--rs-card-title-fg);
391
391
  }
@@ -192,7 +192,7 @@ function download() {
192
192
  }
193
193
 
194
194
  .rs-code-block__lang {
195
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
195
+ font-family: var(--rs-code-font-family, var(--rs-font-mono));
196
196
  font-size: var(--rs-font-size-xs);
197
197
  color: var(--rs-muted);
198
198
  text-transform: lowercase;
@@ -251,9 +251,9 @@ function download() {
251
251
  }
252
252
 
253
253
  .rs-code-block__editor :deep(.cm-scroller) {
254
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
255
- font-size: var(--rs-font-size-sm);
256
- line-height: 1.6;
254
+ font-family: var(--rs-code-font-family, var(--rs-font-mono));
255
+ font-size: var(--rs-code-font-size, var(--rs-font-size-sm));
256
+ line-height: var(--rs-code-line-height, var(--rs-line-height-relaxed));
257
257
  }
258
258
 
259
259
  /* 只读时隐藏光标,保持纯展示外观 */
@@ -445,14 +445,14 @@ defineExpose({
445
445
  justify-content: space-between;
446
446
  margin-bottom: 0.5rem;
447
447
  font-size: var(--rs-font-size-xs);
448
- font-weight: 600;
448
+ font-weight: var(--rs-font-weight-semibold);
449
449
  color: var(--rs-foreground);
450
450
  }
451
451
  .rs-code-editor__inline-edit-head kbd {
452
452
  padding: 0.1rem 0.35rem;
453
453
  border-radius: 4px;
454
454
  border: 1px solid var(--rs-border-subtle);
455
- font-size: 0.7rem;
455
+ font-size: var(--rs-font-size-xs);
456
456
  color: var(--rs-muted);
457
457
  }
458
458
  .rs-code-editor__inline-preview {
@@ -461,8 +461,8 @@ defineExpose({
461
461
  border-radius: 4px;
462
462
  background: color-mix(in srgb, var(--rs-muted) 8%, transparent);
463
463
  color: var(--rs-muted);
464
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
465
- font-size: 0.72rem;
464
+ font-family: var(--rs-code-font-family, var(--rs-font-mono));
465
+ font-size: var(--rs-font-size-xs);
466
466
  line-height: 1.4;
467
467
  white-space: pre-wrap;
468
468
  max-height: 4.5rem;
@@ -520,9 +520,9 @@ defineExpose({
520
520
  background: var(--rs-surface-elevated) !important;
521
521
  }
522
522
  .rs-code-editor__surface :deep(.cm-scroller) {
523
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
524
- font-size: var(--rs-font-size-sm);
525
- line-height: 1.6;
523
+ font-family: var(--rs-code-font-family, var(--rs-font-mono));
524
+ font-size: var(--rs-code-font-size, var(--rs-font-size-sm));
525
+ line-height: var(--rs-code-line-height, var(--rs-line-height-relaxed));
526
526
  }
527
527
  .rs-code-editor__surface :deep(.cm-gutters) {
528
528
  background: color-mix(in srgb, var(--rs-surface-elevated) 85%, var(--rs-border) 15%) !important;
@@ -571,7 +571,7 @@ defineExpose({
571
571
  border: 1px solid var(--rs-border);
572
572
  background: var(--rs-surface-elevated);
573
573
  color: var(--rs-foreground);
574
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
574
+ font-family: var(--rs-code-font-family, var(--rs-font-mono));
575
575
  font-size: var(--rs-font-size-xs);
576
576
  line-height: 1.45;
577
577
  white-space: pre-wrap;
@@ -297,7 +297,7 @@ defineExpose({
297
297
  height: 2.5rem;
298
298
  flex: 0 0 auto;
299
299
  border-radius: var(--rs-radius);
300
- font-weight: 700;
300
+ font-weight: var(--rs-font-weight-bold);
301
301
  }
302
302
  .rs-confirm-dialog__icon-glyph {
303
303
  width: 1.25rem;
@@ -328,7 +328,7 @@ defineExpose({
328
328
  .rs-confirm-dialog__title {
329
329
  margin: 0;
330
330
  font-size: var(--rs-font-size-base);
331
- font-weight: 600;
331
+ font-weight: var(--rs-font-weight-semibold);
332
332
  letter-spacing: -0.01em;
333
333
  color: var(--rs-dialog-title-fg);
334
334
  }
@@ -152,9 +152,10 @@ function onSelect(item: RsContextMenuItem) {
152
152
  align-items: center;
153
153
  gap: 0;
154
154
  padding: 0 8px 0 0;
155
- height: 26px;
155
+ height: 24px;
156
156
  border-radius: 6px;
157
- font-size: 13px;
157
+ font-size: var(--rs-font-size-xs);
158
+ font-weight: var(--rs-font-weight-regular);
158
159
  line-height: 1;
159
160
  color: var(--rs-text);
160
161
  cursor: default;
@@ -185,10 +186,11 @@ function onSelect(item: RsContextMenuItem) {
185
186
  .rs-context-menu__shortcut {
186
187
  flex-shrink: 0;
187
188
  margin-left: 16px;
188
- font-size: 12px;
189
+ font-size: var(--rs-font-size-xs);
190
+ font-weight: var(--rs-font-weight-regular);
189
191
  color: var(--rs-ctx-shortcut);
190
192
  letter-spacing: 0.02em;
191
- font-family: -apple-system, 'SF Pro Text', system-ui, sans-serif;
193
+ font-family: var(--rs-font-sans);
192
194
  }
193
195
 
194
196
  /* 子菜单展开箭头 */