@morscherlab/mint-sdk 1.2.0 → 1.2.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 (99) hide show
  1. package/dist/{ExperimentSelectorModal-DCXmwKNS.js → ExperimentSelectorModal-DHE9k8TP.js} +3 -3
  2. package/dist/{ExperimentSelectorModal-DCXmwKNS.js.map → ExperimentSelectorModal-DHE9k8TP.js.map} +1 -1
  3. package/dist/{SettingsModal-BYR20I4c.js → SettingsModal-DOcCf0Vo.js} +39 -16
  4. package/dist/SettingsModal-DOcCf0Vo.js.map +1 -0
  5. package/dist/{Skeleton-BAF3CKY7.js → Skeleton-jhF9wUkU.js} +17 -7
  6. package/dist/Skeleton-jhF9wUkU.js.map +1 -0
  7. package/dist/__tests__/components/FormField.test.d.ts +1 -0
  8. package/dist/__tests__/components/NumberInput.test.d.ts +1 -0
  9. package/dist/__tests__/composables/localFiles.test.d.ts +1 -0
  10. package/dist/__tests__/composables/usePickerSelection.test.d.ts +1 -0
  11. package/dist/__tests__/styles/tokenOnlyColors.test.d.ts +1 -0
  12. package/dist/components/AppSidebar.vue.d.ts +7 -7
  13. package/dist/components/BasePill.vue.d.ts +3 -0
  14. package/dist/components/BaseTabs.vue.d.ts +1 -4
  15. package/dist/components/FileUploader.vue.d.ts +3 -0
  16. package/dist/components/FormField.vue.d.ts +8 -1
  17. package/dist/components/PluginWorkspaceView.props.d.ts +3 -3
  18. package/dist/components/index.js +5 -5
  19. package/dist/{components-DIum5hkx.js → components-Cp8DcNrU.js} +472 -259
  20. package/dist/components-Cp8DcNrU.js.map +1 -0
  21. package/dist/composables/index.js +7 -7
  22. package/dist/composables/pluginWorkspaceModel.d.ts +0 -3
  23. package/dist/composables/useToast.d.ts +22 -1
  24. package/dist/{composables-mFXsTA4g.js → composables-D_4qqOlp.js} +6 -6
  25. package/dist/{composables-mFXsTA4g.js.map → composables-D_4qqOlp.js.map} +1 -1
  26. package/dist/index.js +10 -10
  27. package/dist/install.js +2 -2
  28. package/dist/styles.css +560 -303
  29. package/dist/templates/index.js +2 -2
  30. package/dist/{templates-CmGfmBRA.js → templates-Dd9bBSs6.js} +2 -2
  31. package/dist/{templates-CmGfmBRA.js.map → templates-Dd9bBSs6.js.map} +1 -1
  32. package/dist/types/components.d.ts +21 -0
  33. package/dist/types/form-builder.d.ts +2 -0
  34. package/dist/types/generated-ui.d.ts +2 -0
  35. package/dist/types/index.d.ts +1 -1
  36. package/dist/{useControlSchema-DldpxNOP.js → useControlSchema-BB2GRPhW.js} +151 -71
  37. package/dist/useControlSchema-BB2GRPhW.js.map +1 -0
  38. package/dist/{useExperimentSelector-CEcIaueJ.js → useExperimentSelector-G_CAOFTX.js} +2 -2
  39. package/dist/{useExperimentSelector-CEcIaueJ.js.map → useExperimentSelector-G_CAOFTX.js.map} +1 -1
  40. package/dist/{useFormBuilder-z5L_dUIy.js → useFormBuilder-BnGSdSRG.js} +2 -2
  41. package/dist/{useFormBuilder-z5L_dUIy.js.map → useFormBuilder-BnGSdSRG.js.map} +1 -1
  42. package/dist/{validation-uwoJGEb6.js → validation-CTxuzLIX.js} +21 -9
  43. package/dist/validation-CTxuzLIX.js.map +1 -0
  44. package/package.json +1 -1
  45. package/src/__tests__/components/AppSidebar.test.ts +84 -94
  46. package/src/__tests__/components/AppToastContainer.test.ts +55 -1
  47. package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +0 -21
  48. package/src/__tests__/components/ControlWorkspaceView.test.ts +0 -2
  49. package/src/__tests__/components/DoseDesignWorkspaceView.test.ts +0 -2
  50. package/src/__tests__/components/FormBuilder.test.ts +26 -0
  51. package/src/__tests__/components/FormField.test.ts +26 -0
  52. package/src/__tests__/components/GeneratedPathInput.test.ts +18 -0
  53. package/src/__tests__/components/NumberInput.test.ts +105 -0
  54. package/src/__tests__/components/PluginWorkspaceView.internal.test.ts +1 -2
  55. package/src/__tests__/components/PluginWorkspaceView.test.ts +1 -2
  56. package/src/__tests__/composables/localFiles.test.ts +57 -0
  57. package/src/__tests__/composables/usePickerSelection.test.ts +98 -0
  58. package/src/__tests__/generatedUi.test.ts +111 -0
  59. package/src/__tests__/styles/tokenOnlyColors.test.ts +16 -0
  60. package/src/components/AppLayout.story.vue +1 -1
  61. package/src/components/AppSidebar.story.vue +7 -10
  62. package/src/components/AppSidebar.vue +178 -58
  63. package/src/components/AppToastContainer.vue +65 -23
  64. package/src/components/BasePill.story.vue +2 -0
  65. package/src/components/BasePill.vue +4 -0
  66. package/src/components/BaseTabs.story.vue +0 -42
  67. package/src/components/BaseTabs.vue +3 -6
  68. package/src/components/FileUploader.vue +10 -1
  69. package/src/components/FormField.vue +10 -3
  70. package/src/components/GeneratedPathInput.vue +11 -7
  71. package/src/components/NumberInput.story.vue +32 -13
  72. package/src/components/NumberInput.vue +116 -55
  73. package/src/components/PluginWorkspaceView.props.ts +3 -3
  74. package/src/components/internal/FormFieldRendererInternal.vue +17 -0
  75. package/src/components/internal/WorkspaceSidebarInternal.vue +1 -2
  76. package/src/composables/filePicker/localFiles.ts +21 -14
  77. package/src/composables/filePicker/usePickerSelection.ts +15 -4
  78. package/src/composables/pluginWorkspaceModel.ts +0 -3
  79. package/src/composables/usePluginWorkspace.ts +1 -2
  80. package/src/composables/useToast.ts +21 -5
  81. package/src/generated/schema.ts +58 -18
  82. package/src/styles/components/app-sidebar.css +282 -121
  83. package/src/styles/components/form-builder.css +1 -1
  84. package/src/styles/components/form-field.css +28 -0
  85. package/src/styles/components/number-input.css +73 -91
  86. package/src/styles/components/pill.css +49 -51
  87. package/src/styles/components/segmented-control.css +16 -11
  88. package/src/styles/components/tabs.css +18 -34
  89. package/src/styles/components/toast.css +136 -27
  90. package/src/styles/variables.css +25 -5
  91. package/src/types/components.ts +23 -0
  92. package/src/types/form-builder.ts +2 -0
  93. package/src/types/generated-ui.ts +2 -0
  94. package/src/types/index.ts +2 -0
  95. package/dist/SettingsModal-BYR20I4c.js.map +0 -1
  96. package/dist/Skeleton-BAF3CKY7.js.map +0 -1
  97. package/dist/components-DIum5hkx.js.map +0 -1
  98. package/dist/useControlSchema-DldpxNOP.js.map +0 -1
  99. package/dist/validation-uwoJGEb6.js.map +0 -1
@@ -38,8 +38,9 @@ export function generatedJobFormSchema(definition: GeneratedJobDefinition): Form
38
38
  export function generatedJobDefaults(definition: GeneratedJobDefinition): Record<string, unknown> {
39
39
  return Object.fromEntries(
40
40
  Object.entries(definition.input_schema.properties ?? {})
41
- .filter(([, schema]) => hasOwnDefault(schema))
42
- .map(([name, schema]) => [name, structuredClone(schema.default)]),
41
+ .map(([name, schema]) => [name, resolveGeneratedField(definition.input_schema, schema)] as const)
42
+ .filter(([, field]) => field.hasDefault)
43
+ .map(([name, field]) => [name, structuredClone(field.defaultValue)]),
43
44
  )
44
45
  }
45
46
 
@@ -51,7 +52,17 @@ export function normalizeGeneratedJobInput(
51
52
  for (const [name, value] of Object.entries(values)) {
52
53
  const field = definition.input_schema.properties?.[name]
53
54
  if (!field) continue
54
- normalized[name] = value
55
+ const { schema, reference } = resolveGeneratedField(definition.input_schema, field)
56
+ if (reference !== 'path' && Array.isArray(value) && ['integer', 'number'].includes(String(schema.items?.type))) {
57
+ normalized[name] = value.map(item => {
58
+ const numeric = typeof item === 'string' && item.trim() !== '' ? Number(item) : item
59
+ return typeof numeric === 'number' && Number.isFinite(numeric) ? numeric : item
60
+ })
61
+ } else if (reference !== 'path' && Array.isArray(value) && schema.items?.type === 'boolean') {
62
+ normalized[name] = value.map(item => item === 'true' ? true : item === 'false' ? false : item)
63
+ } else {
64
+ normalized[name] = value === undefined && allowsNull(schema) ? null : value
65
+ }
55
66
  }
56
67
  return normalized
57
68
  }
@@ -63,7 +74,12 @@ function toFormField(
63
74
  ): FormFieldSchema {
64
75
  const { schema, reference } = resolved
65
76
  const validation: NonNullable<FormFieldSchema['validation']> = {}
66
- if (required) validation.required = true
77
+ const allowsEmpty = schema.type === 'array' || (
78
+ schema.type === 'string' && reference !== 'path'
79
+ && !(schema.minLength && schema.minLength > 0)
80
+ && (!schema.enum || schema.enum.includes(''))
81
+ )
82
+ if (required && !allowsNull(schema) && !allowsEmpty) validation.required = true
67
83
  if (schema.minimum !== undefined) validation.min = schema.minimum
68
84
  if (schema.maximum !== undefined) validation.max = schema.maximum
69
85
  if (schema.minLength !== undefined) validation.minLength = schema.minLength
@@ -71,15 +87,37 @@ function toFormField(
71
87
 
72
88
  const props: Record<string, unknown> = {}
73
89
  if (schema.enum) props.options = [...schema.enum]
90
+ if (schema.type === 'integer') {
91
+ if (schema.exclusiveMinimum !== undefined) validation.min = Math.max(Number(validation.min ?? -Infinity), Math.floor(schema.exclusiveMinimum) + 1)
92
+ if (schema.exclusiveMaximum !== undefined) validation.max = Math.min(Number(validation.max ?? Infinity), Math.ceil(schema.exclusiveMaximum) - 1)
93
+ if (validation.min !== undefined) validation.min = Math.ceil(Number(validation.min))
94
+ if (validation.max !== undefined) validation.max = Math.floor(Number(validation.max))
95
+ if (schema.multipleOf && Number.isInteger(schema.multipleOf)) {
96
+ if (validation.min !== undefined) validation.min = Math.ceil(Number(validation.min) / schema.multipleOf) * schema.multipleOf
97
+ if (validation.max !== undefined) validation.max = Math.floor(Number(validation.max) / schema.multipleOf) * schema.multipleOf
98
+ }
99
+ props.step = 1
100
+ }
101
+ if (schema.type === 'integer' || schema.type === 'number') {
102
+ if (validation.min !== undefined) props.min = validation.min
103
+ if (validation.max !== undefined) props.max = validation.max
104
+ if (schema.multipleOf !== undefined) props.step = schema.multipleOf
105
+ }
106
+ if (schema.type === 'array' && reference !== 'path') {
107
+ if (schema.items?.enum) props.options = [...schema.items.enum]
108
+ else props.allowDuplicates = true
109
+ }
74
110
  if (reference === 'path' && schema.type === 'array') props.multiple = true
75
111
  return {
76
112
  name,
77
113
  label: schema.title || humanize(name),
78
114
  type: fieldType(schema, reference),
79
115
  defaultValue: resolved.hasDefault ? structuredClone(resolved.defaultValue) : undefined,
116
+ disabled: Object.prototype.hasOwnProperty.call(schema, 'const') || undefined,
80
117
  hint: schema.description,
81
118
  validation,
82
119
  props,
120
+ itemType: schema.items?.type === 'number' || schema.items?.type === 'integer' ? 'number' : undefined,
83
121
  }
84
122
  }
85
123
 
@@ -87,27 +125,24 @@ function resolveGeneratedField(
87
125
  root: GeneratedJsonSchema,
88
126
  field: GeneratedJsonSchema,
89
127
  ): ResolvedGeneratedField {
90
- const hasDefault = hasOwnDefault(field)
91
128
  const candidate = field.anyOf?.find(item => item.type !== 'null')
92
129
  ?? field.allOf?.[0]
93
130
  ?? field
131
+ let schema = { ...candidate, ...field }
94
132
  if (candidate.$ref) {
95
133
  const referenceParts = candidate.$ref.split('/')
96
134
  const key = referenceParts[referenceParts.length - 1]
97
- const schema = key ? root.$defs?.[key] : undefined
98
- if (!schema) throw new Error(`Generated field reference is missing: ${candidate.$ref}`)
99
- return {
100
- schema: { ...schema, title: field.title ?? schema.title },
101
- reference: field['x-mint-component'] ?? schema['x-mint-component'],
102
- hasDefault,
103
- defaultValue: field.default,
104
- }
135
+ const referenced = key ? root.$defs?.[key] : undefined
136
+ if (!referenced) throw new Error(`Generated field reference is missing: ${candidate.$ref}`)
137
+ schema = { ...referenced, ...candidate, ...field }
105
138
  }
139
+ if (schema.items) schema.items = resolveGeneratedField(root, schema.items).schema
140
+ const constant = Object.prototype.hasOwnProperty.call(schema, 'const')
106
141
  return {
107
- schema: candidate,
108
- reference: field['x-mint-component'] ?? candidate['x-mint-component'],
109
- hasDefault,
110
- defaultValue: field.default,
142
+ schema,
143
+ reference: schema['x-mint-component'],
144
+ hasDefault: constant || hasOwnDefault(field) || allowsNull(schema),
145
+ defaultValue: constant ? schema.const : hasOwnDefault(field) ? field.default : null,
111
146
  }
112
147
  }
113
148
 
@@ -117,7 +152,7 @@ function fieldType(schema: GeneratedJsonSchema, reference?: GeneratedReference):
117
152
  const type = Array.isArray(schema.type) ? schema.type.find(value => value !== 'null') : schema.type
118
153
  if (type === 'boolean') return 'toggle'
119
154
  if (type === 'number' || type === 'integer') return 'number'
120
- if (type === 'array') return 'tags'
155
+ if (type === 'array') return schema.items?.enum ? 'multiselect' : 'tags'
121
156
  return 'text'
122
157
  }
123
158
 
@@ -125,6 +160,11 @@ function hasOwnDefault(schema: GeneratedJsonSchema): boolean {
125
160
  return Object.prototype.hasOwnProperty.call(schema, 'default')
126
161
  }
127
162
 
163
+ function allowsNull(schema: GeneratedJsonSchema): boolean {
164
+ return schema.type === 'null' || (Array.isArray(schema.type) && schema.type.includes('null'))
165
+ || !!schema.anyOf?.some(item => item.type === 'null')
166
+ }
167
+
128
168
  function humanize(value: string): string {
129
169
  return value.replace(/[_-]+/g, ' ').replace(/^\w/, character => character.toUpperCase())
130
170
  }
@@ -18,6 +18,8 @@
18
18
  border-left: 1px solid var(--border-color);
19
19
  }
20
20
 
21
+ /* Floating shell — the platform's layout language. Kept as-is; only the
22
+ nested per-section card inside it was removed. */
21
23
  .mint-sidebar--floating {
22
24
  position: absolute;
23
25
  top: 0;
@@ -41,27 +43,9 @@
41
43
  border-right: 1px solid var(--border-color);
42
44
  }
43
45
 
44
- .mint-sidebar--analysis {
45
- background-color: var(--bg-secondary);
46
- }
47
-
48
- .mint-sidebar--analysis .mint-sidebar__header {
49
- padding: 1rem;
50
- }
51
-
52
- .mint-sidebar--analysis .mint-sidebar__sections {
53
- padding: 0.75rem;
54
- gap: 0.75rem;
55
- }
56
-
57
- .mint-sidebar--analysis.mint-sidebar--dense .mint-sidebar__sections {
58
- padding: 0.75rem;
59
- gap: 0.75rem;
60
- }
61
-
62
46
  /* Header / Footer slots */
63
47
  .mint-sidebar__header {
64
- padding: 0.75rem;
48
+ padding: 0.625rem 0.875rem;
65
49
  flex-shrink: 0;
66
50
  display: flex;
67
51
  align-items: center;
@@ -70,6 +54,10 @@
70
54
  border-bottom: 1px solid var(--border-color);
71
55
  }
72
56
 
57
+ .mint-sidebar--comfortable .mint-sidebar__header {
58
+ padding: 0.875rem 1rem;
59
+ }
60
+
73
61
  .mint-sidebar__heading {
74
62
  min-width: 0;
75
63
  display: flex;
@@ -161,7 +149,7 @@
161
149
  }
162
150
 
163
151
  .mint-sidebar__footer {
164
- padding: 0.75rem;
152
+ padding: 0.625rem 0.875rem;
165
153
  flex-shrink: 0;
166
154
  border-top: 1px solid var(--border-color);
167
155
  }
@@ -171,12 +159,249 @@
171
159
  flex: 1 1 0;
172
160
  min-height: 0;
173
161
  overflow-y: auto;
174
- padding: 0.75rem;
175
162
  display: flex;
176
163
  flex-direction: column;
164
+ }
165
+
166
+ /* ---------- Flat section groups: header rows separated by one hairline ---------- */
167
+
168
+ .mint-sidebar__group {
169
+ flex-shrink: 0;
170
+ }
171
+
172
+ .mint-sidebar__group + .mint-sidebar__group {
173
+ border-top: 1px solid var(--border-light);
174
+ }
175
+
176
+ .mint-sidebar__group-head {
177
+ display: flex;
178
+ align-items: center;
179
+ transition: background-color 120ms ease;
180
+ }
181
+
182
+ .mint-sidebar__group-head:hover {
183
+ background: var(--bg-hover);
184
+ }
185
+
186
+ .mint-sidebar__group-toggle {
187
+ flex: 1;
188
+ min-width: 0;
189
+ display: flex;
190
+ align-items: center;
177
191
  gap: 0.5rem;
192
+ min-height: 2.25rem;
193
+ padding: 0.4375rem 0.5rem 0.4375rem 0.875rem;
194
+ border: 0;
195
+ background: transparent;
196
+ cursor: pointer;
197
+ text-align: left;
198
+ font-family: inherit;
199
+ }
200
+
201
+ .mint-sidebar__group-toggle:focus-visible {
202
+ outline: none;
203
+ box-shadow: inset var(--focus-ring-soft);
204
+ }
205
+
206
+ .mint-sidebar__group-icon {
207
+ display: flex;
208
+ align-items: center;
209
+ justify-content: center;
210
+ flex-shrink: 0;
211
+ width: 1.25rem;
212
+ height: 1.25rem;
213
+ border-radius: 0.3125rem;
214
+ }
215
+
216
+ .mint-sidebar__group-svg {
217
+ width: 0.75rem;
218
+ height: 0.75rem;
219
+ }
220
+
221
+ .mint-sidebar__group-text-icon {
222
+ font-size: 0.75rem;
223
+ line-height: 1;
224
+ }
225
+
226
+ .mint-sidebar__group-copy {
227
+ flex: 1;
228
+ min-width: 0;
229
+ display: flex;
230
+ align-items: baseline;
231
+ gap: 0.4375rem;
232
+ }
233
+
234
+ .mint-sidebar__group-label {
235
+ flex: 0 1 auto;
236
+ min-width: max-content;
237
+ overflow: hidden;
238
+ color: var(--text-primary);
239
+ font-size: 0.8125rem;
240
+ font-weight: 600;
241
+ line-height: 1.3;
242
+ white-space: nowrap;
243
+ text-overflow: ellipsis;
244
+ transition: color 120ms ease;
245
+ }
246
+
247
+ .mint-sidebar__group--closed .mint-sidebar__group-label {
248
+ color: var(--text-secondary);
249
+ }
250
+
251
+ /* Subtitle carries file names and parameter values, not prose: mono, and the
252
+ disposable half when the row truncates. */
253
+ .mint-sidebar__group-subtitle {
254
+ flex-shrink: 1;
255
+ min-width: 0;
256
+ overflow: hidden;
257
+ color: var(--text-muted);
258
+ font-family: var(--font-mono);
259
+ font-size: 0.6875rem;
260
+ line-height: 1.3;
261
+ white-space: nowrap;
262
+ text-overflow: ellipsis;
263
+ }
264
+
265
+ .mint-sidebar__group-tail {
266
+ display: flex;
267
+ align-items: center;
268
+ flex-shrink: 0;
269
+ gap: 0.375rem;
270
+ padding-right: 0.625rem;
271
+ }
272
+
273
+ .mint-sidebar__tag {
274
+ display: inline-flex;
275
+ align-items: center;
276
+ justify-content: center;
277
+ min-width: 1.125rem;
278
+ height: 1rem;
279
+ padding: 0 0.25rem;
280
+ border-radius: 0.1875rem;
281
+ background: color-mix(in srgb, var(--mint-sidebar-tone) 14%, transparent);
282
+ color: var(--mint-sidebar-tone);
283
+ font-family: var(--font-mono);
284
+ font-size: 0.625rem;
285
+ font-weight: 600;
286
+ line-height: 1;
287
+ font-variant-numeric: tabular-nums;
288
+ }
289
+
290
+ .mint-sidebar__group-action,
291
+ .mint-sidebar__chevron-button {
292
+ display: inline-flex;
293
+ align-items: center;
294
+ justify-content: center;
295
+ width: 1.25rem;
296
+ height: 1.25rem;
297
+ padding: 0;
298
+ border: 0;
299
+ border-radius: var(--radius-sm);
300
+ background: transparent;
301
+ color: var(--text-muted);
302
+ cursor: pointer;
303
+ transition: color 120ms ease, background-color 120ms ease;
304
+ }
305
+
306
+ .mint-sidebar__group-action {
307
+ color: var(--mint-sidebar-tone, var(--text-secondary));
308
+ }
309
+
310
+ .mint-sidebar__group-action:hover:not(:disabled),
311
+ .mint-sidebar__chevron-button:hover:not(:disabled) {
312
+ color: var(--text-primary);
313
+ background: var(--bg-tertiary);
314
+ }
315
+
316
+ .mint-sidebar__group-action:focus-visible,
317
+ .mint-sidebar__chevron-button:focus-visible {
318
+ outline: none;
319
+ box-shadow: var(--focus-ring);
320
+ }
321
+
322
+ .mint-sidebar__group-action:disabled {
323
+ opacity: var(--mint-disabled-opacity);
324
+ cursor: not-allowed;
325
+ }
326
+
327
+ .mint-sidebar__group-action-icon,
328
+ .mint-sidebar__chevron {
329
+ width: 0.75rem;
330
+ height: 0.75rem;
331
+ }
332
+
333
+ .mint-sidebar__chevron {
334
+ transition: transform 180ms var(--mint-ease-out-quart);
335
+ }
336
+
337
+ .mint-sidebar__chevron--open {
338
+ transform: rotate(180deg);
339
+ }
340
+
341
+ /* Inline enable switch: 26×15 track, 11px knob. */
342
+ .mint-sidebar__switch {
343
+ position: relative;
344
+ display: inline-block;
345
+ flex-shrink: 0;
346
+ width: 26px;
347
+ height: 15px;
348
+ padding: 0;
349
+ border: 0;
350
+ border-radius: 9999px;
351
+ background: var(--bg-tertiary);
352
+ cursor: pointer;
353
+ transition: background-color 150ms ease;
354
+ }
355
+
356
+ .mint-sidebar__switch--on {
357
+ background: var(--color-primary);
358
+ }
359
+
360
+ .mint-sidebar__switch:focus-visible {
361
+ outline: none;
362
+ box-shadow: var(--focus-ring-offset);
363
+ }
364
+
365
+ .mint-sidebar__switch-knob {
366
+ position: absolute;
367
+ top: 2px;
368
+ left: 2px;
369
+ width: 11px;
370
+ height: 11px;
371
+ border-radius: 9999px;
372
+ background: white;
373
+ box-shadow: var(--shadow-sm);
374
+ transition: left 180ms var(--mint-ease-out-quart);
375
+ }
376
+
377
+ .mint-sidebar__switch--on .mint-sidebar__switch-knob {
378
+ left: 13px;
379
+ }
380
+
381
+ .mint-sidebar__group-body {
382
+ display: flex;
383
+ flex-direction: column;
384
+ gap: 0.625rem;
385
+ padding: 0.125rem 0.875rem 0.75rem;
386
+ }
387
+
388
+ /* Comfortable density: the old roomier spacing as an opt-in. */
389
+ .mint-sidebar--comfortable .mint-sidebar__group-toggle {
390
+ min-height: 2.625rem;
391
+ padding: 0.625rem 0.5rem 0.625rem 1rem;
392
+ }
393
+
394
+ .mint-sidebar--comfortable .mint-sidebar__group-tail {
395
+ padding-right: 0.75rem;
396
+ }
397
+
398
+ .mint-sidebar--comfortable .mint-sidebar__group-body {
399
+ gap: 0.875rem;
400
+ padding: 0.25rem 1rem 1rem;
178
401
  }
179
402
 
403
+ /* ---------- Collapsed rail ---------- */
404
+
180
405
  .mint-sidebar__collapsed {
181
406
  flex: 1 1 0;
182
407
  min-height: 0;
@@ -251,7 +476,7 @@
251
476
  height: 1rem;
252
477
  padding: 0 0.25rem;
253
478
  border-radius: 9999px;
254
- background: var(--mint-sidebar-rail-badge-color);
479
+ background: var(--mint-sidebar-tone);
255
480
  color: white;
256
481
  font-size: 0.625rem;
257
482
  font-weight: 700;
@@ -260,118 +485,51 @@
260
485
  font-variant-numeric: tabular-nums;
261
486
  }
262
487
 
263
- .mint-sidebar__rail-badge--primary {
264
- --mint-sidebar-rail-badge-color: var(--color-primary);
265
- }
266
-
267
- .mint-sidebar__rail-badge--cta {
268
- --mint-sidebar-rail-badge-color: var(--color-cta);
269
- }
270
-
271
- .mint-sidebar__rail-badge--success {
272
- --mint-sidebar-rail-badge-color: var(--mint-success);
273
- }
274
-
275
- .mint-sidebar__rail-badge--warning {
276
- --mint-sidebar-rail-badge-color: var(--mint-warning);
277
- }
278
-
279
- .mint-sidebar__rail-badge--error {
280
- --mint-sidebar-rail-badge-color: var(--mint-error);
281
- }
282
-
283
- .mint-sidebar__rail-badge--info {
284
- --mint-sidebar-rail-badge-color: var(--mint-info);
285
- }
286
-
287
- .mint-sidebar__rail-badge--neutral {
288
- --mint-sidebar-rail-badge-color: var(--text-secondary);
289
- }
290
-
291
- /* Dense sidebar variant — compact sections */
292
- .mint-sidebar--dense .mint-sidebar__sections {
293
- padding: 0.5rem;
294
- gap: 0.375rem;
295
- }
296
-
297
- .mint-sidebar--dense .mint-sidebar__sections > .mint-collapsible-card {
298
- box-shadow: none;
299
- border-color: var(--border-light);
300
- }
301
-
302
- /* CollapsibleCard appearance inside sidebar */
303
- .mint-sidebar__sections > .mint-collapsible-card {
304
- flex-shrink: 0;
305
- border: 1px solid var(--border-light);
306
- border-radius: var(--radius-md);
307
- background-color: var(--bg-card);
308
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
309
- }
310
-
311
- .mint-sidebar--analysis .mint-sidebar__sections > .mint-collapsible-card {
312
- border-color: var(--border-color);
313
- border-radius: var(--radius-lg);
488
+ /* Semantic tones shared by the rail badge, the inline tag and section actions. */
489
+ .mint-sidebar__rail-badge--primary,
490
+ .mint-sidebar__tag--primary,
491
+ .mint-sidebar__group-action--primary {
492
+ --mint-sidebar-tone: var(--color-primary);
314
493
  }
315
494
 
316
- .mint-sidebar__sections > .mint-collapsible-card .mint-collapsible-card__header {
317
- padding: 0.625rem 0.75rem;
318
- min-height: auto;
495
+ .mint-sidebar__rail-badge--cta,
496
+ .mint-sidebar__tag--cta,
497
+ .mint-sidebar__group-action--cta {
498
+ --mint-sidebar-tone: var(--color-cta);
319
499
  }
320
500
 
321
- .mint-sidebar__sections > .mint-collapsible-card .mint-collapsible-card__icon-badge {
322
- width: 1.75rem;
323
- height: 1.75rem;
324
- border-radius: var(--radius-md);
501
+ .mint-sidebar__rail-badge--success,
502
+ .mint-sidebar__tag--success,
503
+ .mint-sidebar__group-action--success {
504
+ --mint-sidebar-tone: var(--mint-success);
325
505
  }
326
506
 
327
- .mint-sidebar__sections > .mint-collapsible-card .mint-collapsible-card__icon {
328
- width: 0.9375rem;
329
- height: 0.9375rem;
507
+ .mint-sidebar__rail-badge--warning,
508
+ .mint-sidebar__tag--warning,
509
+ .mint-sidebar__group-action--warning {
510
+ --mint-sidebar-tone: var(--mint-warning);
330
511
  }
331
512
 
332
- .mint-sidebar__sections > .mint-collapsible-card .mint-collapsible-card__icon-text {
333
- font-size: 0.9375rem;
513
+ .mint-sidebar__tag--warning {
514
+ color: var(--mint-warning-text);
334
515
  }
335
516
 
336
- .mint-sidebar__sections > .mint-collapsible-card .mint-collapsible-card__title {
337
- font-size: 0.875rem;
338
- font-weight: 600;
339
- color: var(--text-primary);
340
- }
341
-
342
- .mint-sidebar__sections > .mint-collapsible-card .mint-collapsible-card__subtitle {
343
- font-size: 0.75rem;
344
- color: var(--text-muted);
517
+ .mint-sidebar__rail-badge--error,
518
+ .mint-sidebar__tag--error,
519
+ .mint-sidebar__group-action--error {
520
+ --mint-sidebar-tone: var(--mint-error);
345
521
  }
346
522
 
347
- .mint-sidebar__sections > .mint-collapsible-card .mint-collapsible-card__chevron {
348
- width: 0.875rem;
349
- height: 0.875rem;
523
+ .mint-sidebar__rail-badge--info,
524
+ .mint-sidebar__tag--info,
525
+ .mint-sidebar__group-action--info {
526
+ --mint-sidebar-tone: var(--mint-info);
350
527
  }
351
528
 
352
- .mint-sidebar__sections > .mint-collapsible-card .mint-collapsible-card__toggle-track {
353
- width: 1.75rem;
354
- height: 1rem;
355
- }
356
-
357
- /* Scaled-down knob: 12px circle inside a 28px track. Must also override the
358
- base `--on` left position (1.125rem = 18px, calibrated for the 36px track)
359
- because otherwise the knob overflows the narrower track. */
360
- .mint-sidebar__sections > .mint-collapsible-card .mint-collapsible-card__toggle-knob {
361
- width: 0.75rem;
362
- height: 0.75rem;
363
- margin-top: -0.375rem;
364
- }
365
-
366
- .mint-sidebar__sections > .mint-collapsible-card .mint-collapsible-card__toggle-knob--on {
367
- left: 0.875rem;
368
- }
369
-
370
- .mint-sidebar__sections > .mint-collapsible-card .mint-collapsible-card__content {
371
- padding: 0.75rem;
372
- display: flex;
373
- flex-direction: column;
374
- gap: 0.75rem;
529
+ .mint-sidebar__rail-badge--neutral,
530
+ .mint-sidebar__tag--neutral,
531
+ .mint-sidebar__group-action--neutral {
532
+ --mint-sidebar-tone: var(--text-secondary);
375
533
  }
376
534
 
377
535
  .mint-sidebar__footer--collapsed {
@@ -381,7 +539,10 @@
381
539
  @media (prefers-reduced-motion: reduce) {
382
540
  .mint-sidebar,
383
541
  .mint-sidebar__rail-item,
384
- .mint-sidebar__rail-icon {
542
+ .mint-sidebar__rail-icon,
543
+ .mint-sidebar__chevron,
544
+ .mint-sidebar__switch,
545
+ .mint-sidebar__switch-knob {
385
546
  transition: none;
386
547
  }
387
548
 
@@ -6,7 +6,7 @@
6
6
  }
7
7
 
8
8
  .mint-form-builder--sm {
9
- gap: 1rem;
9
+ gap: 0.625rem;
10
10
  }
11
11
 
12
12
  .mint-form-builder--lg {
@@ -6,6 +6,34 @@
6
6
  gap: 0.375rem;
7
7
  }
8
8
 
9
+ /* Row layout: label left, control in a fixed 120px column. For numbers, toggles
10
+ and short enums; anything that would truncate in 120px stays stacked. */
11
+ .mint-form-field--row {
12
+ flex-direction: row;
13
+ align-items: center;
14
+ justify-content: space-between;
15
+ gap: 0.75rem;
16
+ }
17
+
18
+ .mint-form-field--row .mint-form-field__label-row {
19
+ flex: 1;
20
+ min-width: 0;
21
+ }
22
+
23
+ .mint-form-field--row .mint-form-field__label {
24
+ letter-spacing: 0.04em;
25
+ }
26
+
27
+ .mint-form-field--row .mint-form-field__control {
28
+ flex: 0 0 7.5rem;
29
+ min-width: 0;
30
+ }
31
+
32
+ .mint-form-field--row .mint-form-field__error,
33
+ .mint-form-field--row .mint-form-field__hint {
34
+ flex-basis: 100%;
35
+ }
36
+
9
37
  .mint-form-field__label-row {
10
38
  display: flex;
11
39
  align-items: baseline;