@morscherlab/mint-sdk 1.4.0-beta.10 → 1.4.0-beta.12

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 (73) hide show
  1. package/dist/{AlertBox-B2Bgr3ei.js → AlertBox-BdoZqANv.js} +2 -2
  2. package/dist/{AlertBox-B2Bgr3ei.js.map → AlertBox-BdoZqANv.js.map} +1 -1
  3. package/dist/{AppSidebar-mvm--GxN.js → AppSidebar-DGK5i-hJ.js} +7 -7
  4. package/dist/{AppSidebar-mvm--GxN.js.map → AppSidebar-DGK5i-hJ.js.map} +1 -1
  5. package/dist/{ExperimentPopover-5T1fAq2L.js → ExperimentPopover-CbD7BR_s.js} +2 -2
  6. package/dist/{ExperimentPopover-5T1fAq2L.js.map → ExperimentPopover-CbD7BR_s.js.map} +1 -1
  7. package/dist/{ExperimentSelectorModal-65peG4Up.js → ExperimentSelectorModal-BoofZZxF.js} +4 -4
  8. package/dist/{ExperimentSelectorModal-65peG4Up.js.map → ExperimentSelectorModal-BoofZZxF.js.map} +1 -1
  9. package/dist/{SegmentedControl-BtnnjWzs.js → SegmentedControl-BkwYOKpG.js} +2 -2
  10. package/dist/{SegmentedControl-BtnnjWzs.js.map → SegmentedControl-BkwYOKpG.js.map} +1 -1
  11. package/dist/{SettingsModal-DO3IZ7qz.js → SettingsModal-BXsjIVnm.js} +25 -24
  12. package/dist/SettingsModal-BXsjIVnm.js.map +1 -0
  13. package/dist/__tests__/styles/paletteTokens.test.d.ts +1 -0
  14. package/dist/__tests__/styles/v2AuditRules.test.d.ts +1 -0
  15. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +1 -1
  16. package/dist/{controlSchemaModel-B8CTXagP.js → controlSchemaModel-BF5g4wRC.js} +2 -2
  17. package/dist/{controlSchemaModel-B8CTXagP.js.map → controlSchemaModel-BF5g4wRC.js.map} +1 -1
  18. package/dist/{data-CgILxDLW.js → data-ihsBd9bs.js} +10 -10
  19. package/dist/{data-CgILxDLW.js.map → data-ihsBd9bs.js.map} +1 -1
  20. package/dist/data.js +7 -7
  21. package/dist/{experiment-43OJtH7R.js → experiment-DyO54FQB.js} +25 -61
  22. package/dist/experiment-DyO54FQB.js.map +1 -0
  23. package/dist/index.js +18 -18
  24. package/dist/{install-CsPFJSx3.js → install-CFgfRO64.js} +13 -13
  25. package/dist/{install-CsPFJSx3.js.map → install-CFgfRO64.js.map} +1 -1
  26. package/dist/install.js +1 -1
  27. package/dist/{lab-MHzp-_J8.js → lab-B-pLwc5U.js} +10 -10
  28. package/dist/{lab-MHzp-_J8.js.map → lab-B-pLwc5U.js.map} +1 -1
  29. package/dist/lab.js +4 -4
  30. package/dist/{records-B6gpM_3e.js → records-DnK_1Q_S.js} +2 -2
  31. package/dist/{records-B6gpM_3e.js.map → records-DnK_1Q_S.js.map} +1 -1
  32. package/dist/{runtime-8bs8EogX.js → runtime-pKfF_iql.js} +9 -9
  33. package/dist/{runtime-8bs8EogX.js.map → runtime-pKfF_iql.js.map} +1 -1
  34. package/dist/runtime.js +7 -7
  35. package/dist/{shell-CywENwBT.js → shell-BIH8Rc21.js} +4 -4
  36. package/dist/{shell-CywENwBT.js.map → shell-BIH8Rc21.js.map} +1 -1
  37. package/dist/shell.js +4 -4
  38. package/dist/stores/index.d.ts +1 -1
  39. package/dist/stores/index.js +2 -2
  40. package/dist/stores/settings.d.ts +4 -12
  41. package/dist/styles.css +165 -43
  42. package/dist/types/platform.d.ts +1 -1
  43. package/dist/{useExperimentSamples-DHnxKFAF.js → useExperimentSamples-BGN1dmcR.js} +4 -4
  44. package/dist/{useExperimentSamples-DHnxKFAF.js.map → useExperimentSamples-BGN1dmcR.js.map} +1 -1
  45. package/dist/{useListReorder-BitdUhxn.js → useListReorder-DPynGrCj.js} +3 -3
  46. package/dist/{useListReorder-BitdUhxn.js.map → useListReorder-DPynGrCj.js.map} +1 -1
  47. package/dist/{useRackEditor-DiwCt77M.js → useRackEditor-B2FrQA0p.js} +8 -8
  48. package/dist/{useRackEditor-DiwCt77M.js.map → useRackEditor-B2FrQA0p.js.map} +1 -1
  49. package/dist/{useRealtime-C3iy9QkU.js → useRealtime-yWjTTnc9.js} +2 -2
  50. package/dist/{useRealtime-C3iy9QkU.js.map → useRealtime-yWjTTnc9.js.map} +1 -1
  51. package/dist/{useRequest-D506eHi8.js → useRequest-Dqw12k2s.js} +3 -3
  52. package/dist/{useRequest-D506eHi8.js.map → useRequest-Dqw12k2s.js.map} +1 -1
  53. package/package.json +1 -1
  54. package/src/__tests__/components/SettingsModal.test.ts +9 -2
  55. package/src/__tests__/stores/settings.test.ts +21 -21
  56. package/src/__tests__/styles/paletteTokens.test.ts +44 -0
  57. package/src/__tests__/styles/v2AuditRules.test.ts +96 -0
  58. package/src/components/GeneratedResultRenderer.vue +3 -4
  59. package/src/components/SettingsModal.vue +7 -12
  60. package/src/stores/index.ts +0 -2
  61. package/src/stores/settings.ts +20 -50
  62. package/src/styles/components/app-layout.css +3 -2
  63. package/src/styles/components/app-top-bar.css +3 -3
  64. package/src/styles/components/dataframe.css +1 -1
  65. package/src/styles/components/modal.css +2 -2
  66. package/src/styles/components/number-input.css +1 -1
  67. package/src/styles/components/progress-bar.css +3 -3
  68. package/src/styles/components/section-card.css +4 -2
  69. package/src/styles/components/settings-modal.css +7 -2
  70. package/src/styles/variables.css +131 -15
  71. package/src/types/platform.ts +1 -1
  72. package/dist/SettingsModal-DO3IZ7qz.js.map +0 -1
  73. package/dist/experiment-43OJtH7R.js.map +0 -1
@@ -0,0 +1,44 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { dirname, join, resolve } from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+ import { describe, expect, it } from 'vitest'
5
+ import { colorPalettes } from '../../stores/settings'
6
+
7
+ const here = dirname(fileURLToPath(import.meta.url))
8
+ const variablesCss = readFileSync(join(resolve(here, '../..'), 'styles/variables.css'), 'utf-8')
9
+
10
+ const ACCENT_TOKENS = [
11
+ '--color-primary',
12
+ '--color-primary-hover',
13
+ '--color-primary-pressed',
14
+ '--color-primary-soft',
15
+ '--color-primary-border',
16
+ ]
17
+ const STATUS_TOKENS = ['--mint-success', '--mint-error', '--mint-warning', '--mint-warning-text']
18
+
19
+ /** Declarations of every rule whose selector list has a selector that ends in `[data-palette="<key>"]`. */
20
+ function paletteDeclarations(key: string, dark: boolean): string {
21
+ const attr = `[data-palette="${key}"]`
22
+ return [...variablesCss.matchAll(/([^{}]+)\{([^}]*)\}/g)]
23
+ .filter(([, selector]) => selector.split(',').some((s) => {
24
+ const one = s.trim()
25
+ return one.endsWith(attr) && one.startsWith('html.dark') === dark
26
+ }))
27
+ .map(([, , body]) => body)
28
+ .join('\n')
29
+ }
30
+
31
+ describe('accent palettes', () => {
32
+ it.each(Object.keys(colorPalettes).flatMap((key) => [[key, 'light'], [key, 'dark']]))(
33
+ 'should define every accent token for the %s palette in %s mode',
34
+ (key, theme) => {
35
+ const body = paletteDeclarations(key, theme === 'dark')
36
+ for (const token of ACCENT_TOKENS) expect(body, `${key} ${theme} ${token}`).toMatch(new RegExp(`${token}\\s*:`))
37
+ },
38
+ )
39
+
40
+ it('should give the colour-blind palette Okabe-Ito status colours', () => {
41
+ const body = paletteDeclarations('colorblind', false)
42
+ for (const token of STATUS_TOKENS) expect(body, token).toMatch(new RegExp(`${token}\\s*:`))
43
+ })
44
+ })
@@ -0,0 +1,96 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { dirname, join, resolve } from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+ import { describe, expect, it } from 'vitest'
5
+
6
+ // v2 design audit (2026-10-28) fixes in the SDK stylesheets.
7
+
8
+ const srcRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../..')
9
+ const stylesDir = join(srcRoot, 'styles')
10
+
11
+ function css(path: string): string {
12
+ return readFileSync(join(stylesDir, path), 'utf-8').replace(/\/\*[\s\S]*?\*\//g, '')
13
+ }
14
+
15
+ function rule(text: string, selector: string): string {
16
+ const escaped = selector.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
17
+ return new RegExp(`(?:^|\\})\\s*${escaped}\\s*\\{([^}]*)\\}`).exec(text)?.[1] ?? ''
18
+ }
19
+
20
+ const rem = (value: string): number => Number(value.replace('rem', '')) * 16
21
+
22
+ describe('v2 audit style rules', () => {
23
+ it('should keep the outermost cards at least 16px from the window edge', () => {
24
+ const inset = /--mint-page-inset:\s*([\d.]+rem)/.exec(css('variables.css'))?.[1] ?? '0rem'
25
+ expect(rem(inset)).toBeGreaterThanOrEqual(16)
26
+ expect(rule(css('components/app-layout.css'), '.mint-layout--floating')).toMatch(/padding:\s*var\(--mint-page-inset\)/)
27
+ const topBarCard = rule(css('components/app-top-bar.css'), '.mint-topbar--card')
28
+ expect(topBarCard).toMatch(/width:\s*calc\(100% - 2 \* var\(--mint-page-inset\)\)/)
29
+ expect(topBarCard).toMatch(/margin:\s*var\(--mint-page-inset\) var\(--mint-page-inset\) 0/)
30
+ })
31
+
32
+ it('should put the SDK utility classes in the utilities layer so host variants beat them', () => {
33
+ const vars = css('variables.css')
34
+ const order = vars.indexOf('@layer theme, base, components, utilities;')
35
+ const layer = vars.indexOf('@layer utilities {')
36
+ expect(order).toBeGreaterThan(-1)
37
+ expect(order).toBeLessThan(vars.indexOf('@layer base {'))
38
+ const utilities = vars.slice(layer, vars.indexOf('@media (prefers-reduced-motion'))
39
+ expect(layer).toBeGreaterThan(-1)
40
+ for (const selector of ['.flex-col {', '.text-text-muted {', '.truncate {', '.max-w-sm {']) {
41
+ expect(utilities).toContain(selector)
42
+ }
43
+ })
44
+
45
+ it('should drop the last DataFrame row rule over the !important cell border', () => {
46
+ const body = rule(css('components/dataframe.css'), '.mint-dataframe__row:last-child .mint-dataframe__td')
47
+ expect(body).toMatch(/border-bottom:\s*none\s*!important/)
48
+ })
49
+
50
+ it('should round the modal close button with --radius-md', () => {
51
+ expect(rule(css('components/modal.css'), '.mint-modal__close')).toMatch(/border-radius:\s*var\(--radius-md\)/)
52
+ })
53
+
54
+ it('should draw no capsule on the progress rail or the sheet grab hint', () => {
55
+ expect(css('components/progress-bar.css')).not.toMatch(/9999px/)
56
+ expect(rule(css('components/modal.css'), '.mint-modal__grab-hint')).not.toMatch(/9999px/)
57
+ })
58
+
59
+ it('should keep a 28px header control inside the section card header strip', () => {
60
+ const text = css('components/section-card.css')
61
+ const cases: Array<[string, number]> = [
62
+ ['.mint-section-card__header', 28],
63
+ ['[data-density="comfortable"] .mint-section-card__header', 32],
64
+ ]
65
+ for (const [selector, control] of cases) {
66
+ const body = rule(text, selector)
67
+ const minHeight = rem(/min-height:\s*([\d.]+rem)/.exec(body)?.[1] ?? '0rem')
68
+ const padTop = rem(/padding:\s*([\d.]+rem)/.exec(body)?.[1] ?? '0rem')
69
+ expect(control + 2 * padTop + 1, selector).toBeLessThanOrEqual(minHeight)
70
+ }
71
+ })
72
+
73
+ it('should end the settings pane without a closing row hairline and draw no box around the density preview', () => {
74
+ const text = css('components/settings-modal.css')
75
+ const last = rule(
76
+ text,
77
+ '.mint-settings-modal__group:last-of-type > .mint-settings-modal__group-grid > .mint-settings-modal__row:last-child',
78
+ )
79
+ expect(last).toMatch(/border-bottom:\s*none/)
80
+ expect(rule(text, '.mint-settings-modal__density-preview')).not.toMatch(/(^|;)\s*border(-radius)?:/)
81
+ })
82
+
83
+ it('should set the md number input text no smaller than the sm one', () => {
84
+ const text = css('components/number-input.css')
85
+ const size = (variant: string) =>
86
+ rem(/font-size:\s*([\d.]+rem)/.exec(rule(text, `.mint-number-input__input--${variant}`))?.[1] ?? '0rem')
87
+ expect(size('md')).toBeGreaterThan(size('sm'))
88
+ })
89
+
90
+ it('should give the generated JSON block a token radius and no frame', () => {
91
+ const sfc = readFileSync(join(srcRoot, 'components/GeneratedResultRenderer.vue'), 'utf-8')
92
+ const body = /\.mint-generated-result__json\s*\{([^}]*overflow:[^}]*)\}/.exec(sfc)?.[1] ?? ''
93
+ expect(body).toMatch(/border-radius:\s*var\(--radius-md\)/)
94
+ expect(body).not.toMatch(/(^|;)\s*border:/)
95
+ })
96
+ })
@@ -130,10 +130,9 @@ function humanize(value: string): string {
130
130
 
131
131
  .mint-generated-result__json {
132
132
  overflow: auto;
133
- padding: 1rem;
134
- border: 1px solid var(--border-color);
135
- border-radius: 0.75rem;
136
- background: var(--bg-secondary);
133
+ padding: 0.75rem 1rem;
134
+ border-radius: var(--radius-md);
135
+ background: var(--bg-tertiary);
137
136
  font-size: 0.8125rem;
138
137
  line-height: 1.55;
139
138
  white-space: pre-wrap;
@@ -358,13 +358,8 @@ const themeOptions: { value: ThemeMode; label: string; halves: ('light' | 'dark'
358
358
  { value: 'system', label: 'System', halves: ['light', 'dark'] },
359
359
  ]
360
360
 
361
- /** Five stops along the palette's primary → accent hue arc (shortest way round), as in the board. */
362
- function paletteSwatches(key: ColorPalette): string[] {
363
- const [from, to] = colorPalettes[key].hues
364
- const span = ((to - from + 540) % 360) - 180
365
- const tone = key === 'pastel' ? '60% 72%' : '65% 48%'
366
- return Array.from({ length: 5 }, (_, i) => `hsl(${Math.round(from + (span * i) / 4)} ${tone})`)
367
- }
361
+ /** Accent tokens a palette swatch shows; `data-palette` on the strip resolves them per palette. */
362
+ const paletteSwatchTokens = ['--color-primary', '--color-primary-hover', '--color-primary-soft', '--color-primary-border']
368
363
 
369
364
  const densityPreviewRows = [
370
365
  ['S-0012', '2.41', '12.5'],
@@ -562,7 +557,7 @@ function canShowForCurrentUser(item: AccessControlled): boolean {
562
557
  <div class="mint-settings-modal__setting">
563
558
  <div class="mint-settings-modal__setting-text">
564
559
  <span class="mint-settings-modal__setting-label">Color palette</span>
565
- <span class="mint-settings-modal__setting-hint">Primary and accent hues across the interface</span>
560
+ <span class="mint-settings-modal__setting-hint">Accent colour across the interface</span>
566
561
  </div>
567
562
  <div class="mint-settings-modal__palettes">
568
563
  <button
@@ -573,12 +568,12 @@ function canShowForCurrentUser(item: AccessControlled): boolean {
573
568
  :class="['mint-settings-modal__palette', { 'mint-settings-modal__palette--active': settings.colorPalette === key }]"
574
569
  @click="settings.colorPalette = key as ColorPalette"
575
570
  >
576
- <span class="mint-settings-modal__swatches" aria-hidden="true">
571
+ <span class="mint-settings-modal__swatches" :data-palette="key" aria-hidden="true">
577
572
  <span
578
- v-for="color in paletteSwatches(key as ColorPalette)"
579
- :key="color"
573
+ v-for="token in paletteSwatchTokens"
574
+ :key="token"
580
575
  class="mint-settings-modal__swatch"
581
- :style="{ backgroundColor: color }"
576
+ :style="{ backgroundColor: `var(${token})` }"
582
577
  />
583
578
  </span>
584
579
  <span class="mint-settings-modal__palette-name">{{ palette.name }}</span>
@@ -3,9 +3,7 @@ export {
3
3
  useSettingsStore,
4
4
  tryUseSettingsStore,
5
5
  colorPalettes,
6
- paletteCssVariables,
7
6
  tableDensityToSize,
8
- PALETTE_CSS_VARIABLES,
9
7
  type SettingsState,
10
8
  } from './settings'
11
9
  export { useExperimentStore } from './experiment'
@@ -55,39 +55,24 @@ const defaultSettings: SettingsState = {
55
55
  tableDensity: 'normal',
56
56
  }
57
57
 
58
- export const colorPalettes: Record<ColorPalette, { name: string; hues: [number, number] }> = {
59
- default: { name: 'Default (Cyan-Pink)', hues: [180, 320] },
60
- colorblind: { name: 'Colorblind-friendly', hues: [45, 260] },
61
- viridis: { name: 'Viridis', hues: [280, 80] },
62
- pastel: { name: 'Pastel', hues: [200, 340] },
63
- }
64
-
65
58
  /**
66
- * Custom properties a non-default palette overrides on `<html>`.
67
- *
68
- * The saturation/lightness pairs are lifted from the original indigo/orange
69
- * tokens in `styles/variables.css` so only the hue moves; the `default`
70
- * palette writes nothing at all and the stylesheet values stand.
59
+ * Accent palettes. `styles/variables.css` holds each palette's light and dark
60
+ * tokens under `[data-palette]`; the store only writes the attribute.
71
61
  */
72
- export const PALETTE_CSS_VARIABLES = [
73
- '--color-primary',
74
- '--color-primary-hover',
75
- '--color-primary-soft',
76
- '--color-cta',
77
- '--color-cta-hover',
78
- ] as const
79
-
80
- /** Build the `<html>` custom-property overrides for a palette. */
81
- export function paletteCssVariables(palette: ColorPalette): Record<string, string> {
82
- if (palette === 'default') return {}
83
- const [primaryHue, accentHue] = colorPalettes[palette].hues
84
- return {
85
- '--color-primary': `hsl(${primaryHue} 84% 67%)`,
86
- '--color-primary-hover': `hsl(${primaryHue} 75% 59%)`,
87
- '--color-primary-soft': `hsl(${primaryHue} 84% 67% / 0.12)`,
88
- '--color-cta': `hsl(${accentHue} 95% 53%)`,
89
- '--color-cta-hover': `hsl(${accentHue} 90% 48%)`,
90
- }
62
+ export const colorPalettes: Record<ColorPalette, { name: string }> = {
63
+ default: { name: 'Teal' },
64
+ colorblind: { name: 'Colour-blind safe' },
65
+ purple: { name: 'Purple' },
66
+ ocean: { name: 'Ocean' },
67
+ forest: { name: 'Forest' },
68
+ }
69
+
70
+ /** Pre-v2 palette ids and the palette that replaces each one. */
71
+ const RETIRED_PALETTES: Record<string, ColorPalette> = { viridis: 'purple', pastel: 'default' }
72
+
73
+ function knownPalette(value: unknown): ColorPalette {
74
+ if (typeof value === 'string' && value in colorPalettes) return value as ColorPalette
75
+ return (typeof value === 'string' && RETIRED_PALETTES[value]) || 'default'
91
76
  }
92
77
 
93
78
  /** `DataFrame`-style size that each table density maps onto. */
@@ -107,13 +92,13 @@ function loadSettings(): SettingsState {
107
92
  const stored = localStorage.getItem(STORAGE_KEY)
108
93
  if (stored) {
109
94
  const parsed = JSON.parse(stored)
110
- return { ...defaultSettings, ...parsed }
95
+ return { ...defaultSettings, ...parsed, colorPalette: knownPalette(parsed.colorPalette) }
111
96
  }
112
97
 
113
98
  const legacyStored = localStorage.getItem(LEGACY_STORAGE_KEY)
114
99
  if (legacyStored) {
115
100
  const parsed = JSON.parse(legacyStored)
116
- const migrated = { ...defaultSettings, ...parsed }
101
+ const migrated = { ...defaultSettings, ...parsed, colorPalette: knownPalette(parsed.colorPalette) }
117
102
  localStorage.setItem(STORAGE_KEY, JSON.stringify(migrated))
118
103
  localStorage.removeItem(LEGACY_STORAGE_KEY)
119
104
  return migrated
@@ -202,20 +187,10 @@ export const useSettingsStore = defineStore('mint-settings', () => {
202
187
  document.documentElement.classList.toggle('dark', dark)
203
188
  }
204
189
 
205
- /**
206
- * Write the selected palette's hues onto `<html>` as custom-property
207
- * overrides. The `default` palette clears them so `styles/variables.css`
208
- * keeps ownership of the brand colors.
209
- */
190
+ /** Mirror the palette onto `<html data-palette>`; `styles/variables.css` maps it to tokens. */
210
191
  function applyPalette() {
211
192
  if (typeof document === 'undefined') return
212
- const style = document.documentElement.style
213
- const overrides = paletteCssVariables(colorPalette.value)
214
- for (const name of PALETTE_CSS_VARIABLES) {
215
- const value = overrides[name]
216
- if (value) style.setProperty(name, value)
217
- else style.removeProperty(name)
218
- }
193
+ document.documentElement.dataset.palette = colorPalette.value
219
194
  }
220
195
 
221
196
  /** Mirror the table density onto `<html data-density>` for CSS consumers. */
@@ -269,10 +244,6 @@ export const useSettingsStore = defineStore('mint-settings', () => {
269
244
  tableDensity.value = defaultSettings.tableDensity
270
245
  }
271
246
 
272
- function getPaletteHues(): [number, number] {
273
- return colorPalettes[colorPalette.value].hues
274
- }
275
-
276
247
  /** The `size` the SDK table components default to for the current density. */
277
248
  function getTableSize(): 'sm' | 'md' | 'lg' {
278
249
  return tableDensityToSize(tableDensity.value)
@@ -298,7 +269,6 @@ export const useSettingsStore = defineStore('mint-settings', () => {
298
269
  applyDensity,
299
270
  persistSettings,
300
271
  resetToDefaults,
301
- getPaletteHues,
302
272
  getTableSize,
303
273
  isDark,
304
274
  getApiBaseUrl,
@@ -60,10 +60,11 @@
60
60
  }
61
61
 
62
62
  /* Floating variant: all sections appear as separate cards on a background,
63
- spaced by the density gutter (8px, 12px comfortable). */
63
+ 16px from the window edge and spaced by the density gutter (8px, 12px
64
+ comfortable). */
64
65
  .mint-layout--floating {
65
66
  background-color: var(--bg-primary);
66
- padding: var(--mint-gutter);
67
+ padding: var(--mint-page-inset);
67
68
  gap: var(--mint-gutter);
68
69
  }
69
70
 
@@ -110,10 +110,10 @@
110
110
  padding: 0 0.375rem 0 0;
111
111
  }
112
112
 
113
- /* Variant: card - flat floating card, inset by the layout gutter */
113
+ /* Variant: card - flat floating card, inset 16px from the window edge */
114
114
  .mint-topbar--card {
115
- width: calc(100% - 2 * var(--mint-gutter));
116
- margin: var(--mint-gutter) var(--mint-gutter) 0;
115
+ width: calc(100% - 2 * var(--mint-page-inset));
116
+ margin: var(--mint-page-inset) var(--mint-page-inset) 0;
117
117
  padding: 0.375rem 0.5rem 0.375rem 0.625rem;
118
118
  border-radius: var(--radius-lg);
119
119
  border: 1px solid var(--border-color);
@@ -357,7 +357,7 @@
357
357
  }
358
358
 
359
359
  .mint-dataframe__row:last-child .mint-dataframe__td {
360
- border-bottom: none;
360
+ border-bottom: none !important;
361
361
  }
362
362
 
363
363
  /* Empty / error placeholder host (content comes from EmptyState) */
@@ -115,7 +115,7 @@
115
115
  width: 2.5rem;
116
116
  height: 3px;
117
117
  background-color: var(--border-color);
118
- border-radius: 9999px;
118
+ border-radius: var(--radius-sm);
119
119
  margin: 0.5rem auto 0;
120
120
  flex-shrink: 0;
121
121
  }
@@ -163,7 +163,7 @@
163
163
  width: 1.75rem;
164
164
  height: 1.75rem;
165
165
  padding: 0;
166
- border-radius: 0.4375rem;
166
+ border-radius: var(--radius-md);
167
167
  color: var(--text-secondary);
168
168
  background: none;
169
169
  border: none;
@@ -121,7 +121,7 @@
121
121
  /* Horizontal padding matches BaseInput canonical scale (10/12/16) so FormField's
122
122
  12px label indent aligns with the numeric text across all input types. */
123
123
  .mint-number-input__input--sm { padding: 0.25rem 0.625rem; font-size: 0.8125rem; }
124
- .mint-number-input__input--md { padding: 0.375rem 0.75rem; font-size: 0.75rem; }
124
+ .mint-number-input__input--md { padding: 0.375rem 0.75rem; font-size: 0.875rem; }
125
125
  .mint-number-input__input--lg { padding: 0.5rem 1rem; font-size: 1rem; }
126
126
 
127
127
  /* ---------- Unit suffix ---------- */
@@ -22,13 +22,13 @@
22
22
  }
23
23
 
24
24
  /* ========================================================================
25
- Rail variant (default) — single filled track, pill ends
25
+ Rail variant (default) — single filled track, square-ish ends (v2: no capsules)
26
26
  ======================================================================== */
27
27
 
28
28
  .mint-progress__track {
29
29
  width: 100%;
30
30
  background-color: var(--bg-tertiary);
31
- border-radius: 9999px; /* pill ends — spec */
31
+ border-radius: var(--radius-sm);
32
32
  overflow: hidden;
33
33
  }
34
34
 
@@ -38,7 +38,7 @@
38
38
 
39
39
  .mint-progress__bar {
40
40
  height: 100%;
41
- border-radius: 9999px;
41
+ border-radius: var(--radius-sm);
42
42
  transition: width 0.3s ease;
43
43
  }
44
44
 
@@ -34,7 +34,9 @@
34
34
  gap: 0.625rem;
35
35
  box-sizing: border-box;
36
36
  min-height: 2.5rem;
37
- padding: 0.375rem 0.625rem 0.375rem 0.75rem;
37
+ /* 4px block padding: a 28px header control plus padding plus the 1px rule
38
+ stays inside the 40px strip (6px made it 41px). */
39
+ padding: 0.25rem 0.625rem 0.25rem 0.75rem;
38
40
  border-bottom: 1px solid var(--border-color);
39
41
  flex-shrink: 0;
40
42
  }
@@ -131,7 +133,7 @@ button.mint-section-card__row:hover {
131
133
  /* Comfortable density gives the header and rows back their height. */
132
134
  [data-density="comfortable"] .mint-section-card__header {
133
135
  min-height: 2.75rem;
134
- padding: 0.5rem 0.75rem 0.5rem 0.875rem;
136
+ padding: 0.25rem 0.75rem 0.25rem 0.875rem;
135
137
  }
136
138
 
137
139
  [data-density="comfortable"] .mint-section-card__row {
@@ -37,6 +37,12 @@
37
37
  border-bottom-color: var(--border-color);
38
38
  }
39
39
 
40
+ /* The pane ends after the last group's last row: no closing hairline there,
41
+ it would sit on the footer rule with nothing between them. */
42
+ .mint-settings-modal__group:last-of-type > .mint-settings-modal__group-grid > .mint-settings-modal__row:last-child {
43
+ border-bottom: none;
44
+ }
45
+
40
46
  /* The field cell anchors the ↺ reset, which sits just left of the control (5d).
41
47
  --_settings-control-w is the control's width; stacked fields (text, custom slots)
42
48
  keep 0 and put the reset at the right end of their label line. */
@@ -655,10 +661,9 @@
655
661
  .mint-settings-modal__density-preview {
656
662
  --_density-row: 2.125rem;
657
663
 
664
+ /* A hairline table in the setting row, not a bordered box inside the modal. */
658
665
  grid-column: 1 / -1;
659
666
  overflow: hidden;
660
- border: 1px solid var(--border-color);
661
- border-radius: var(--radius-md);
662
667
  font-size: 0.75rem;
663
668
  }
664
669