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

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morscherlab/mint-sdk",
3
- "version": "1.4.0-beta.10",
3
+ "version": "1.4.0-beta.11",
4
4
  "description": "MINT Platform SDK — Vue 3 components, composables, and types for plugin development. MINT = Mass-spec INtegrated Toolkit.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -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;
@@ -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
 
@@ -5,6 +5,10 @@
5
5
 
6
6
  @import url('https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500;600&display=swap');
7
7
 
8
+ /* Tailwind v4's layer order, declared first so the SDK's base and utilities
9
+ layers sort the same way with or without Tailwind in the host. */
10
+ @layer theme, base, components, utilities;
11
+
8
12
  /* ==========================================================================
9
13
  CSS Variables - Light Mode
10
14
  ========================================================================== */
@@ -135,6 +139,8 @@
135
139
  --form-height-md: 2rem;
136
140
  --form-height-lg: 2.5rem;
137
141
  --mint-gutter: 0.5rem;
142
+ /* Distance from the outermost cards to the window edge, in every density. */
143
+ --mint-page-inset: 1rem;
138
144
 
139
145
  /* Semantic colors */
140
146
  --mint-success: #10B981;
@@ -344,6 +350,9 @@ button, input, select, textarea {
344
350
  /* ==========================================================================
345
351
  MINT Utility Classes (for Tailwind v4 compatibility)
346
352
  ========================================================================== */
353
+ /* In Tailwind's utilities layer: unlayered, these beat every layered rule, so a
354
+ host's responsive variant (`lg:flex-row`) lost to the SDK's `.flex-col`. */
355
+ @layer utilities {
347
356
  .bg-bg-hover {
348
357
  background-color: var(--bg-tertiary);
349
358
  }
@@ -719,6 +728,7 @@ button, input, select, textarea {
719
728
  .max-w-sm {
720
729
  max-width: 24rem;
721
730
  }
731
+ } /* @layer utilities */
722
732
 
723
733
  /* ==========================================================================
724
734
  Reduced Motion