@morscherlab/mint-sdk 1.1.0-beta.1 → 1.1.0-beta.3

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 (38) hide show
  1. package/dist/__tests__/components/SectionCard.test.d.ts +1 -0
  2. package/dist/components/SectionCard.vue.d.ts +47 -0
  3. package/dist/components/index.d.ts +1 -0
  4. package/dist/components/index.js +2 -2
  5. package/dist/{components-UfkPN1bK.js → components-DFH5whuh.js} +699 -620
  6. package/dist/components-DFH5whuh.js.map +1 -0
  7. package/dist/composables/index.js +1 -1
  8. package/dist/composables/usePlatformContext.d.ts +3 -0
  9. package/dist/{composables-F4JFK9K1.js → composables-B512bWJP.js} +133 -26
  10. package/dist/composables-B512bWJP.js.map +1 -0
  11. package/dist/index.js +3 -3
  12. package/dist/install.js +1 -1
  13. package/dist/styles.css +301 -0
  14. package/dist/tailwind.preset.d.ts +5 -0
  15. package/dist/tailwind.preset.js +7 -2
  16. package/dist/tailwind.preset.js.map +1 -1
  17. package/dist/types/components.d.ts +2 -0
  18. package/dist/types/index.d.ts +1 -1
  19. package/dist/types/platform.d.ts +2 -0
  20. package/package.json +1 -1
  21. package/src/__tests__/components/SectionCard.test.ts +181 -0
  22. package/src/__tests__/composables/usePlatformContext.test.ts +28 -0
  23. package/src/__tests__/composables/usePluginClient.test.ts +329 -20
  24. package/src/__tests__/composables/usePluginConfig.test.ts +323 -7
  25. package/src/components/SectionCard.story.vue +163 -0
  26. package/src/components/SectionCard.vue +98 -0
  27. package/src/components/index.ts +1 -0
  28. package/src/composables/usePluginClient.ts +118 -15
  29. package/src/composables/usePluginConfig.ts +156 -20
  30. package/src/styles/components/section-card.css +161 -0
  31. package/src/styles/index.css +1 -0
  32. package/src/styles/variables.css +25 -0
  33. package/src/tailwind.preset.ts +5 -0
  34. package/src/types/components.ts +12 -0
  35. package/src/types/index.ts +2 -0
  36. package/src/types/platform.ts +2 -0
  37. package/dist/components-UfkPN1bK.js.map +0 -1
  38. package/dist/composables-F4JFK9K1.js.map +0 -1
package/dist/styles.css CHANGED
@@ -19708,6 +19708,145 @@ to { transform: rotate(360deg);
19708
19708
  animation: none;
19709
19709
  }
19710
19710
  }
19711
+ /* SectionCard — the MINT 1.1 one-card system.
19712
+ *
19713
+ * One card per page region: 12px radius, one shadow token (--card-shadow),
19714
+ * a single header strip, and hairline-separated regions inside — never a
19715
+ * card nested in a card.
19716
+ */
19717
+ .mint-section-card {
19718
+ display: flex;
19719
+ flex-direction: column;
19720
+ border-radius: 12px;
19721
+ border: 1px solid var(--border-color);
19722
+ background: var(--bg-secondary);
19723
+ box-shadow: var(--card-shadow);
19724
+ overflow: hidden;
19725
+ transition: background-color var(--mint-transition), border-color var(--mint-transition);
19726
+ }
19727
+
19728
+ /* Fill the height the parent offers: the body takes the slack and scrolls,
19729
+ * so the header and footer stay pinned to the card's edges. */
19730
+ .mint-section-card--fill {
19731
+ flex: 1;
19732
+ min-height: 0;
19733
+ }
19734
+ .mint-section-card--fill .mint-section-card__body {
19735
+ flex: 1;
19736
+ min-height: 0;
19737
+ overflow: auto;
19738
+ }
19739
+ .mint-section-card__header {
19740
+ display: flex;
19741
+ align-items: center;
19742
+ gap: 0.625rem;
19743
+ padding: 0.6875rem 1rem;
19744
+ border-bottom: 1px solid var(--border-color);
19745
+ flex-shrink: 0;
19746
+ }
19747
+ .mint-section-card__header--divider-light {
19748
+ border-bottom-color: var(--border-light);
19749
+ }
19750
+ .mint-section-card__icon-chip {
19751
+ display: inline-flex;
19752
+ align-items: center;
19753
+ justify-content: center;
19754
+ flex-shrink: 0;
19755
+ width: 1.375rem;
19756
+ height: 1.375rem;
19757
+ border-radius: 6px;
19758
+ }
19759
+ .mint-section-card__icon {
19760
+ width: 0.8125rem;
19761
+ height: 0.8125rem;
19762
+ }
19763
+ .mint-section-card__icon-chip--primary {
19764
+ background: var(--color-primary-soft);
19765
+ color: var(--color-primary);
19766
+ }
19767
+ .mint-section-card__icon-chip--experiment {
19768
+ background: var(--color-experiment-soft);
19769
+ color: var(--color-experiment);
19770
+ }
19771
+ .mint-section-card__icon-chip--project {
19772
+ background: var(--color-project-soft);
19773
+ color: var(--color-project);
19774
+ }
19775
+ .mint-section-card__icon-chip--success {
19776
+ background: var(--mint-success-bg);
19777
+ color: var(--mint-success);
19778
+ }
19779
+ .mint-section-card__icon-chip--error {
19780
+ background: var(--mint-error-bg);
19781
+ color: var(--mint-error);
19782
+ }
19783
+ .mint-section-card__icon-chip--warning {
19784
+ background: var(--mint-warning-bg);
19785
+ color: var(--mint-warning);
19786
+ }
19787
+ .mint-section-card__icon-chip--info {
19788
+ background: var(--mint-info-bg);
19789
+ color: var(--mint-info);
19790
+ }
19791
+ .mint-section-card__title {
19792
+ margin: 0;
19793
+ font-size: 0.8125rem;
19794
+ font-weight: 600;
19795
+ color: var(--text-primary);
19796
+ }
19797
+ .mint-section-card__title--uppercase {
19798
+ font-size: 0.75rem;
19799
+ letter-spacing: 0.08em;
19800
+ text-transform: uppercase;
19801
+ color: var(--text-secondary);
19802
+ }
19803
+ .mint-section-card__count {
19804
+ font-family: 'Fira Code', monospace;
19805
+ font-size: 0.6875rem;
19806
+ color: var(--text-muted);
19807
+ }
19808
+ .mint-section-card__spacer {
19809
+ flex: 1;
19810
+ }
19811
+ .mint-section-card__toolbar {
19812
+ flex-shrink: 0;
19813
+ }
19814
+ .mint-section-card__footer {
19815
+ display: flex;
19816
+ align-items: center;
19817
+ justify-content: space-between;
19818
+ padding: 0.5rem 1rem;
19819
+ border-top: 1px solid var(--border-color);
19820
+ background: var(--bg-primary);
19821
+ flex-shrink: 0;
19822
+ }
19823
+
19824
+ /* Hairline row helpers — regions inside a card separate with 1px lines,
19825
+ * rendered by a 1px gap over a hairline-colored track. */
19826
+ .mint-section-card__rows {
19827
+ display: flex;
19828
+ flex-direction: column;
19829
+ gap: 1px;
19830
+ background: var(--border-light);
19831
+ }
19832
+ .mint-section-card__row {
19833
+ display: flex;
19834
+ align-items: center;
19835
+ gap: 0.625rem;
19836
+ padding: 0.5625rem 1rem;
19837
+ background: var(--bg-secondary);
19838
+ transition: background-color var(--mint-transition);
19839
+ }
19840
+ a.mint-section-card__row,
19841
+ button.mint-section-card__row {
19842
+ cursor: pointer;
19843
+ color: inherit;
19844
+ text-decoration: none;
19845
+ }
19846
+ a.mint-section-card__row:hover,
19847
+ button.mint-section-card__row:hover {
19848
+ background: var(--bg-primary);
19849
+ }
19711
19850
  .mint-artifact-save-dialog {
19712
19851
  display: flex;
19713
19852
  flex-direction: column;
@@ -19824,6 +19963,18 @@ to { transform: rotate(360deg);
19824
19963
  --color-cta-hover: #EA580C;
19825
19964
  --color-purple: #8B5CF6;
19826
19965
 
19966
+ /* Entity accent colors — experiments (cyan) and projects (sky), from the
19967
+ MINT 1.1 card system. -soft backs icon chips and pills, -border outlines
19968
+ pills that sit on a card surface. */
19969
+ --color-experiment: #06B6D4;
19970
+ --color-experiment-hover: #0891B2;
19971
+ --color-experiment-soft: rgba(6, 182, 212, 0.12);
19972
+ --color-experiment-border: rgba(6, 182, 212, 0.3);
19973
+ --color-project: #0EA5E9;
19974
+ --color-project-hover: #0284C7;
19975
+ --color-project-soft: rgba(14, 165, 233, 0.12);
19976
+ --color-project-border: rgba(14, 165, 233, 0.32);
19977
+
19827
19978
  /* MINT brand mark — used for browser theme-color, marketing chrome, app icon background */
19828
19979
  --mint-brand: #7BD0B5;
19829
19980
  --mint-brand-hover: #5FB89A;
@@ -19839,6 +19990,8 @@ to { transform: rotate(360deg);
19839
19990
  --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
19840
19991
  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
19841
19992
  --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
19993
+ /* The MINT 1.1 card system uses exactly one shadow for every card. */
19994
+ --card-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.04), 0 1px 3px 0 rgb(0 0 0 / 0.06);
19842
19995
 
19843
19996
  /* Border radius */
19844
19997
  --radius-sm: 0.25rem;
@@ -19935,6 +20088,17 @@ html.dark {
19935
20088
  --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.3), 0 1px 2px -1px rgb(0 0 0 / 0.2);
19936
20089
  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.3), 0 2px 4px -2px rgb(0 0 0 / 0.2);
19937
20090
  --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.3), 0 4px 6px -4px rgb(0 0 0 / 0.2);
20091
+ --card-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.24), 0 1px 3px 0 rgb(0 0 0 / 0.32);
20092
+
20093
+ /* Entity accent colors - Dark mode */
20094
+ --color-experiment: #22D3EE;
20095
+ --color-experiment-hover: #67E8F9;
20096
+ --color-experiment-soft: rgba(34, 211, 238, 0.16);
20097
+ --color-experiment-border: rgba(34, 211, 238, 0.36);
20098
+ --color-project: #38BDF8;
20099
+ --color-project-hover: #7DD3FC;
20100
+ --color-project-soft: rgba(56, 189, 248, 0.16);
20101
+ --color-project-border: rgba(56, 189, 248, 0.38);
19938
20102
 
19939
20103
  /* Semantic colors - Dark mode */
19940
20104
  --mint-success-bg: rgba(16, 185, 129, 0.15);
@@ -25587,6 +25751,143 @@ code, pre {
25587
25751
  .mint-sample-legend__add-text--lg {
25588
25752
  font-size: 1rem;
25589
25753
  }
25754
+ /* SectionCard — the MINT 1.1 one-card system.
25755
+ *
25756
+ * One card per page region: 12px radius, one shadow token (--card-shadow),
25757
+ * a single header strip, and hairline-separated regions inside — never a
25758
+ * card nested in a card.
25759
+ */
25760
+ .mint-section-card {
25761
+ display: flex;
25762
+ flex-direction: column;
25763
+ border-radius: 12px;
25764
+ border: 1px solid var(--border-color);
25765
+ background: var(--bg-secondary);
25766
+ box-shadow: var(--card-shadow);
25767
+ overflow: hidden;
25768
+ transition: background-color var(--mint-transition), border-color var(--mint-transition);
25769
+ }
25770
+ /* Fill the height the parent offers: the body takes the slack and scrolls,
25771
+ * so the header and footer stay pinned to the card's edges. */
25772
+ .mint-section-card--fill {
25773
+ flex: 1;
25774
+ min-height: 0;
25775
+ }
25776
+ .mint-section-card--fill .mint-section-card__body {
25777
+ flex: 1;
25778
+ min-height: 0;
25779
+ overflow: auto;
25780
+ }
25781
+ .mint-section-card__header {
25782
+ display: flex;
25783
+ align-items: center;
25784
+ gap: 0.625rem;
25785
+ padding: 0.6875rem 1rem;
25786
+ border-bottom: 1px solid var(--border-color);
25787
+ flex-shrink: 0;
25788
+ }
25789
+ .mint-section-card__header--divider-light {
25790
+ border-bottom-color: var(--border-light);
25791
+ }
25792
+ .mint-section-card__icon-chip {
25793
+ display: inline-flex;
25794
+ align-items: center;
25795
+ justify-content: center;
25796
+ flex-shrink: 0;
25797
+ width: 1.375rem;
25798
+ height: 1.375rem;
25799
+ border-radius: 6px;
25800
+ }
25801
+ .mint-section-card__icon {
25802
+ width: 0.8125rem;
25803
+ height: 0.8125rem;
25804
+ }
25805
+ .mint-section-card__icon-chip--primary {
25806
+ background: var(--color-primary-soft);
25807
+ color: var(--color-primary);
25808
+ }
25809
+ .mint-section-card__icon-chip--experiment {
25810
+ background: var(--color-experiment-soft);
25811
+ color: var(--color-experiment);
25812
+ }
25813
+ .mint-section-card__icon-chip--project {
25814
+ background: var(--color-project-soft);
25815
+ color: var(--color-project);
25816
+ }
25817
+ .mint-section-card__icon-chip--success {
25818
+ background: var(--mint-success-bg);
25819
+ color: var(--mint-success);
25820
+ }
25821
+ .mint-section-card__icon-chip--error {
25822
+ background: var(--mint-error-bg);
25823
+ color: var(--mint-error);
25824
+ }
25825
+ .mint-section-card__icon-chip--warning {
25826
+ background: var(--mint-warning-bg);
25827
+ color: var(--mint-warning);
25828
+ }
25829
+ .mint-section-card__icon-chip--info {
25830
+ background: var(--mint-info-bg);
25831
+ color: var(--mint-info);
25832
+ }
25833
+ .mint-section-card__title {
25834
+ margin: 0;
25835
+ font-size: 0.8125rem;
25836
+ font-weight: 600;
25837
+ color: var(--text-primary);
25838
+ }
25839
+ .mint-section-card__title--uppercase {
25840
+ font-size: 0.75rem;
25841
+ letter-spacing: 0.08em;
25842
+ text-transform: uppercase;
25843
+ color: var(--text-secondary);
25844
+ }
25845
+ .mint-section-card__count {
25846
+ font-family: 'Fira Code', monospace;
25847
+ font-size: 0.6875rem;
25848
+ color: var(--text-muted);
25849
+ }
25850
+ .mint-section-card__spacer {
25851
+ flex: 1;
25852
+ }
25853
+ .mint-section-card__toolbar {
25854
+ flex-shrink: 0;
25855
+ }
25856
+ .mint-section-card__footer {
25857
+ display: flex;
25858
+ align-items: center;
25859
+ justify-content: space-between;
25860
+ padding: 0.5rem 1rem;
25861
+ border-top: 1px solid var(--border-color);
25862
+ background: var(--bg-primary);
25863
+ flex-shrink: 0;
25864
+ }
25865
+ /* Hairline row helpers — regions inside a card separate with 1px lines,
25866
+ * rendered by a 1px gap over a hairline-colored track. */
25867
+ .mint-section-card__rows {
25868
+ display: flex;
25869
+ flex-direction: column;
25870
+ gap: 1px;
25871
+ background: var(--border-light);
25872
+ }
25873
+ .mint-section-card__row {
25874
+ display: flex;
25875
+ align-items: center;
25876
+ gap: 0.625rem;
25877
+ padding: 0.5625rem 1rem;
25878
+ background: var(--bg-secondary);
25879
+ transition: background-color var(--mint-transition);
25880
+ }
25881
+ a.mint-section-card__row,
25882
+ button.mint-section-card__row {
25883
+ cursor: pointer;
25884
+ color: inherit;
25885
+ text-decoration: none;
25886
+ }
25887
+ a.mint-section-card__row:hover,
25888
+ button.mint-section-card__row:hover {
25889
+ background: var(--bg-primary);
25890
+ }
25590
25891
  /* BaseSelect Component Styles */
25591
25892
  .mint-select {
25592
25893
  position: relative;
@@ -16,6 +16,10 @@ declare const mintPreset: {
16
16
  'warning-hover': string;
17
17
  brand: string;
18
18
  'brand-hover': string;
19
+ experiment: string;
20
+ 'experiment-hover': string;
21
+ project: string;
22
+ 'project-hover': string;
19
23
  };
20
24
  bg: {
21
25
  primary: string;
@@ -48,6 +52,7 @@ declare const mintPreset: {
48
52
  mint: string;
49
53
  'mint-md': string;
50
54
  'mint-lg': string;
55
+ 'mint-card': string;
51
56
  };
52
57
  transitionDuration: {
53
58
  mint: string;
@@ -14,7 +14,11 @@ var mintPreset = { theme: { extend: {
14
14
  warning: "var(--mint-warning)",
15
15
  "warning-hover": "var(--mint-warning-hover)",
16
16
  brand: "var(--mint-brand)",
17
- "brand-hover": "var(--mint-brand-hover)"
17
+ "brand-hover": "var(--mint-brand-hover)",
18
+ experiment: "var(--color-experiment)",
19
+ "experiment-hover": "var(--color-experiment-hover)",
20
+ project: "var(--color-project)",
21
+ "project-hover": "var(--color-project-hover)"
18
22
  },
19
23
  bg: {
20
24
  primary: "var(--mint-bg-primary)",
@@ -56,7 +60,8 @@ var mintPreset = { theme: { extend: {
56
60
  boxShadow: {
57
61
  mint: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
58
62
  "mint-md": "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
59
- "mint-lg": "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)"
63
+ "mint-lg": "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)",
64
+ "mint-card": "var(--card-shadow)"
60
65
  },
61
66
  transitionDuration: { mint: "150ms" }
62
67
  } } };
@@ -1 +1 @@
1
- {"version":3,"file":"tailwind.preset.js","names":[],"sources":["../src/tailwind.preset.ts"],"sourcesContent":["import type { Config } from 'tailwindcss'\n\nconst mintPreset = {\n theme: {\n extend: {\n colors: {\n mint: {\n primary: 'var(--color-primary)',\n 'primary-hover': 'var(--color-primary-hover)',\n 'primary-light': 'var(--color-primary-light)',\n cta: 'var(--color-cta)',\n 'cta-hover': 'var(--color-cta-hover)',\n danger: 'var(--mint-error)',\n 'danger-hover': 'var(--mint-error-hover)',\n success: 'var(--mint-success)',\n 'success-hover': 'var(--mint-success-hover)',\n warning: 'var(--mint-warning)',\n 'warning-hover': 'var(--mint-warning-hover)',\n brand: 'var(--mint-brand)',\n 'brand-hover': 'var(--mint-brand-hover)',\n },\n bg: {\n primary: 'var(--mint-bg-primary)',\n secondary: 'var(--mint-bg-secondary)',\n card: 'var(--mint-bg-card)',\n hover: 'var(--mint-bg-hover)',\n input: 'var(--mint-bg-input)',\n },\n text: {\n primary: 'var(--mint-text-primary)',\n secondary: 'var(--mint-text-secondary)',\n muted: 'var(--mint-text-muted)',\n inverse: 'var(--mint-text-inverse)',\n },\n border: {\n DEFAULT: 'var(--mint-border)',\n focus: 'var(--mint-border-focus)',\n },\n },\n fontFamily: {\n sans: ['Fira Sans', 'system-ui', '-apple-system', 'sans-serif'],\n mono: ['Fira Code', 'ui-monospace', 'SFMono-Regular', 'monospace'],\n },\n borderRadius: {\n mint: '0.5rem',\n 'mint-sm': '0.375rem',\n 'mint-lg': '0.75rem',\n },\n boxShadow: {\n mint: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',\n 'mint-md': '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',\n 'mint-lg': '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',\n },\n transitionDuration: {\n mint: '150ms',\n },\n },\n },\n} satisfies Partial<Config>\n\nexport default mintPreset\n"],"mappings":";AAEA,IAAM,aAAa,EACjB,OAAO,EACL,QAAQ;CACN,QAAQ;EACN,MAAM;GACJ,SAAS;GACT,iBAAiB;GACjB,iBAAiB;GACjB,KAAK;GACL,aAAa;GACb,QAAQ;GACR,gBAAgB;GAChB,SAAS;GACT,iBAAiB;GACjB,SAAS;GACT,iBAAiB;GACjB,OAAO;GACP,eAAe;GAChB;EACD,IAAI;GACF,SAAS;GACT,WAAW;GACX,MAAM;GACN,OAAO;GACP,OAAO;GACR;EACD,MAAM;GACJ,SAAS;GACT,WAAW;GACX,OAAO;GACP,SAAS;GACV;EACD,QAAQ;GACN,SAAS;GACT,OAAO;GACR;EACF;CACD,YAAY;EACV,MAAM;GAAC;GAAa;GAAa;GAAiB;GAAa;EAC/D,MAAM;GAAC;GAAa;GAAgB;GAAkB;GAAY;EACnE;CACD,cAAc;EACZ,MAAM;EACN,WAAW;EACX,WAAW;EACZ;CACD,WAAW;EACT,MAAM;EACN,WAAW;EACX,WAAW;EACZ;CACD,oBAAoB,EAClB,MAAM,SACP;CACF,EACF,EACF"}
1
+ {"version":3,"file":"tailwind.preset.js","names":[],"sources":["../src/tailwind.preset.ts"],"sourcesContent":["import type { Config } from 'tailwindcss'\n\nconst mintPreset = {\n theme: {\n extend: {\n colors: {\n mint: {\n primary: 'var(--color-primary)',\n 'primary-hover': 'var(--color-primary-hover)',\n 'primary-light': 'var(--color-primary-light)',\n cta: 'var(--color-cta)',\n 'cta-hover': 'var(--color-cta-hover)',\n danger: 'var(--mint-error)',\n 'danger-hover': 'var(--mint-error-hover)',\n success: 'var(--mint-success)',\n 'success-hover': 'var(--mint-success-hover)',\n warning: 'var(--mint-warning)',\n 'warning-hover': 'var(--mint-warning-hover)',\n brand: 'var(--mint-brand)',\n 'brand-hover': 'var(--mint-brand-hover)',\n experiment: 'var(--color-experiment)',\n 'experiment-hover': 'var(--color-experiment-hover)',\n project: 'var(--color-project)',\n 'project-hover': 'var(--color-project-hover)',\n },\n bg: {\n primary: 'var(--mint-bg-primary)',\n secondary: 'var(--mint-bg-secondary)',\n card: 'var(--mint-bg-card)',\n hover: 'var(--mint-bg-hover)',\n input: 'var(--mint-bg-input)',\n },\n text: {\n primary: 'var(--mint-text-primary)',\n secondary: 'var(--mint-text-secondary)',\n muted: 'var(--mint-text-muted)',\n inverse: 'var(--mint-text-inverse)',\n },\n border: {\n DEFAULT: 'var(--mint-border)',\n focus: 'var(--mint-border-focus)',\n },\n },\n fontFamily: {\n sans: ['Fira Sans', 'system-ui', '-apple-system', 'sans-serif'],\n mono: ['Fira Code', 'ui-monospace', 'SFMono-Regular', 'monospace'],\n },\n borderRadius: {\n mint: '0.5rem',\n 'mint-sm': '0.375rem',\n 'mint-lg': '0.75rem',\n },\n boxShadow: {\n mint: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',\n 'mint-md': '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',\n 'mint-lg': '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',\n 'mint-card': 'var(--card-shadow)',\n },\n transitionDuration: {\n mint: '150ms',\n },\n },\n },\n} satisfies Partial<Config>\n\nexport default mintPreset\n"],"mappings":";AAEA,IAAM,aAAa,EACjB,OAAO,EACL,QAAQ;CACN,QAAQ;EACN,MAAM;GACJ,SAAS;GACT,iBAAiB;GACjB,iBAAiB;GACjB,KAAK;GACL,aAAa;GACb,QAAQ;GACR,gBAAgB;GAChB,SAAS;GACT,iBAAiB;GACjB,SAAS;GACT,iBAAiB;GACjB,OAAO;GACP,eAAe;GACf,YAAY;GACZ,oBAAoB;GACpB,SAAS;GACT,iBAAiB;GAClB;EACD,IAAI;GACF,SAAS;GACT,WAAW;GACX,MAAM;GACN,OAAO;GACP,OAAO;GACR;EACD,MAAM;GACJ,SAAS;GACT,WAAW;GACX,OAAO;GACP,SAAS;GACV;EACD,QAAQ;GACN,SAAS;GACT,OAAO;GACR;EACF;CACD,YAAY;EACV,MAAM;GAAC;GAAa;GAAa;GAAiB;GAAa;EAC/D,MAAM;GAAC;GAAa;GAAgB;GAAkB;GAAY;EACnE;CACD,cAAc;EACZ,MAAM;EACN,WAAW;EACX,WAAW;EACZ;CACD,WAAW;EACT,MAAM;EACN,WAAW;EACX,WAAW;EACX,aAAa;EACd;CACD,oBAAoB,EAClB,MAAM,SACP;CACF,EACF,EACF"}
@@ -41,6 +41,8 @@ export interface FormFieldProps {
41
41
  hint?: string;
42
42
  required?: boolean;
43
43
  }
44
+ export type SectionCardAccent = 'primary' | 'experiment' | 'project' | 'success' | 'error' | 'warning' | 'info';
45
+ export type SectionCardDivider = 'strong' | 'light';
44
46
  export type SidebarBadgeTone = 'primary' | 'cta' | 'success' | 'warning' | 'error' | 'info' | 'neutral';
45
47
  export interface SidebarToolSectionAction {
46
48
  id: string;
@@ -1,4 +1,4 @@
1
- export type { ContainerDirection, ButtonVariant, ButtonSize, InputType, ModalSize, ModalVariant, AlertType, Toast, TabItem, TabItemInput, OptionPrimitive, SelectOption, SelectOptionInput, RadioOption, RadioOptionInput, FormFieldProps, SidebarBadgeTone, SidebarToolSection, SidebarToolSectionAction, CollapsibleState, TopBarVariant, TopBarSettingsConfig, PillNavOption, PillNavOptionInput, PillNavItem, PillNavItemInput, PageSelectorItem, PageSelectorItemInput, PluginSwitcherPlugin, PluginSwitcherInfo, AccountMenuItem, WellPlateFormat, WellState, WellPlateSelectionMode, WellPlateSize, WellShape, Well, HeatmapColorScale, HeatmapConfig, SlotPosition, WellExtendedData, WellEditData, WellEditField, WellSampleDropData, WellSampleDropContext, WellSampleDropParser, WellLegendItem, PlateCondition, ColumnCondition, RowCondition, Rack, RackSampleDropContext, RackSampleDropMapper, SampleType, PlateMap, PlateMapEditorState, ProtocolStepType, ProtocolStepStatus, ProtocolStep, SampleGroup, GroupItem, FileUploaderMode, SegmentedOption, SegmentedOptionInput, SegmentedControlVariant, SegmentedControlSize, MultiSelectOption, MultiSelectOptionInput, MultiSelectSize, PillVariant, PillColor, PillSize, CalendarSelectionMode, CalendarMarker, CalendarDayContext, SortDirection, SortState, DataFrameColumn, PaginationState, SpinnerSize, SpinnerVariant, DividerSpacing, StatusType, ProgressVariant, ProgressSize, AvatarSize, EmptyStateColor, EmptyStateSize, BreadcrumbItem, BreadcrumbItemInput, TooltipPosition, ConfirmVariant, SettingsTab, SettingsTabInput, SettingsModalLayout, SettingsGroup, SettingsModalSchema, SettingsUserType, SettingsAudience, SettingsAudienceInput, NumberNotation, UnitOption, WizardStep, WizardStepState, AuditEntryType, AuditEntry, BatchItemStatus, BatchItem, BatchSummary, TimePickerFormat, TimeRange, ScheduleView, ScheduleEventStatus, ScheduleEvent, ScheduleBlockedSlot, ScheduleSlotContext, ScheduleEventCreateContext, ScheduleEventUpdateContext, SummarySectionItem, SummarySection, SummaryData, ResourceStatus, ResourceSpec, MoleculeData, ExperimentStatus, DatePreset, ExperimentSortField, ExperimentTypeOption, ExperimentSummary, ExperimentListResponse, ExperimentFilters, FitState, FitResultSummary, StorageCondition, ReagentColumn, Reagent, TreeNodeType, BadgeVariant, TreeNode, } from './components';
1
+ export type { ContainerDirection, ButtonVariant, ButtonSize, InputType, ModalSize, ModalVariant, AlertType, Toast, TabItem, TabItemInput, OptionPrimitive, SelectOption, SelectOptionInput, RadioOption, RadioOptionInput, FormFieldProps, SectionCardAccent, SectionCardDivider, SidebarBadgeTone, SidebarToolSection, SidebarToolSectionAction, CollapsibleState, TopBarVariant, TopBarSettingsConfig, PillNavOption, PillNavOptionInput, PillNavItem, PillNavItemInput, PageSelectorItem, PageSelectorItemInput, PluginSwitcherPlugin, PluginSwitcherInfo, AccountMenuItem, WellPlateFormat, WellState, WellPlateSelectionMode, WellPlateSize, WellShape, Well, HeatmapColorScale, HeatmapConfig, SlotPosition, WellExtendedData, WellEditData, WellEditField, WellSampleDropData, WellSampleDropContext, WellSampleDropParser, WellLegendItem, PlateCondition, ColumnCondition, RowCondition, Rack, RackSampleDropContext, RackSampleDropMapper, SampleType, PlateMap, PlateMapEditorState, ProtocolStepType, ProtocolStepStatus, ProtocolStep, SampleGroup, GroupItem, FileUploaderMode, SegmentedOption, SegmentedOptionInput, SegmentedControlVariant, SegmentedControlSize, MultiSelectOption, MultiSelectOptionInput, MultiSelectSize, PillVariant, PillColor, PillSize, CalendarSelectionMode, CalendarMarker, CalendarDayContext, SortDirection, SortState, DataFrameColumn, PaginationState, SpinnerSize, SpinnerVariant, DividerSpacing, StatusType, ProgressVariant, ProgressSize, AvatarSize, EmptyStateColor, EmptyStateSize, BreadcrumbItem, BreadcrumbItemInput, TooltipPosition, ConfirmVariant, SettingsTab, SettingsTabInput, SettingsModalLayout, SettingsGroup, SettingsModalSchema, SettingsUserType, SettingsAudience, SettingsAudienceInput, NumberNotation, UnitOption, WizardStep, WizardStepState, AuditEntryType, AuditEntry, BatchItemStatus, BatchItem, BatchSummary, TimePickerFormat, TimeRange, ScheduleView, ScheduleEventStatus, ScheduleEvent, ScheduleBlockedSlot, ScheduleSlotContext, ScheduleEventCreateContext, ScheduleEventUpdateContext, SummarySectionItem, SummarySection, SummaryData, ResourceStatus, ResourceSpec, MoleculeData, ExperimentStatus, DatePreset, ExperimentSortField, ExperimentTypeOption, ExperimentSummary, ExperimentListResponse, ExperimentFilters, FitState, FitResultSummary, StorageCondition, ReagentColumn, Reagent, TreeNodeType, BadgeVariant, TreeNode, } from './components';
2
2
  export type * from './generated-ui';
3
3
  export type { AnalysisArtifactStatus, AnalysisArtifactStatusFilter, AnalysisArtifactSummary, AnalysisArtifactDetail, AnalysisArtifactListResponse, AnalysisArtifactMetadataUpdate, ArtifactSaveMode, ArtifactFileSource, JsonArtifactSavePayload, FileArtifactCreatePayload, FileArtifactUpdatePayload, HandlerFileArtifactCreatePayload, HandlerFileArtifactUpdatePayload, ArtifactSavePayload, HandlerArtifactSavePayload, ArtifactSaveDialogPayload, ArtifactSaveResult, ArtifactSaveHandler, HandlerArtifactSaveHandler, ArtifactSaveDialogHandler, } from './analysisArtifactTypes';
4
4
  export { ANALYSIS_FILE_ARTIFACT_SCHEMA } from './analysisArtifactTypes';
@@ -8,6 +8,8 @@ export interface PluginInfo {
8
8
  color?: string;
9
9
  route_prefix: string;
10
10
  api_prefix: string;
11
+ /** Settings route selected by the platform for this plugin runtime. */
12
+ settings_api?: 'plugin' | 'platform';
11
13
  nav_items?: PluginNavItem[];
12
14
  settings?: PluginSettings;
13
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morscherlab/mint-sdk",
3
- "version": "1.1.0-beta.1",
3
+ "version": "1.1.0-beta.3",
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,181 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import SectionCard from '../../components/SectionCard.vue'
4
+
5
+ describe('SectionCard', () => {
6
+ describe('basic rendering', () => {
7
+ it('should render container, header, and title when given the required title prop', () => {
8
+ const wrapper = mount(SectionCard, {
9
+ props: { title: 'Experiments' },
10
+ })
11
+ expect(wrapper.find('.mint-section-card').exists()).toBe(true)
12
+ expect(wrapper.find('.mint-section-card__header').exists()).toBe(true)
13
+ expect(wrapper.find('.mint-section-card__title').text()).toBe('Experiments')
14
+ })
15
+
16
+ it('should render default slot content in the body', () => {
17
+ const wrapper = mount(SectionCard, {
18
+ props: { title: 'Experiments' },
19
+ slots: { default: '<p class="body-probe">rows</p>' },
20
+ })
21
+ expect(wrapper.find('.mint-section-card__body .body-probe').exists()).toBe(true)
22
+ })
23
+ })
24
+
25
+ describe('count meta', () => {
26
+ it('should render the count in the header when provided', () => {
27
+ const wrapper = mount(SectionCard, {
28
+ props: { title: 'Plugins', count: '10 enabled' },
29
+ })
30
+ expect(wrapper.find('.mint-section-card__count').text()).toBe('10 enabled')
31
+ })
32
+
33
+ it('should not render a count element when count is omitted', () => {
34
+ const wrapper = mount(SectionCard, {
35
+ props: { title: 'Plugins' },
36
+ })
37
+ expect(wrapper.find('.mint-section-card__count').exists()).toBe(false)
38
+ })
39
+
40
+ it('should accept a numeric count', () => {
41
+ const wrapper = mount(SectionCard, {
42
+ props: { title: 'Artifacts', count: 4 },
43
+ })
44
+ expect(wrapper.find('.mint-section-card__count').text()).toBe('4')
45
+ })
46
+ })
47
+
48
+ describe('title style variants', () => {
49
+ it('should apply the uppercase modifier when uppercase is true', () => {
50
+ const wrapper = mount(SectionCard, {
51
+ props: { title: 'Experiments', uppercase: true },
52
+ })
53
+ expect(wrapper.find('.mint-section-card__title--uppercase').exists()).toBe(true)
54
+ })
55
+
56
+ it('should not apply the uppercase modifier by default', () => {
57
+ const wrapper = mount(SectionCard, {
58
+ props: { title: 'Design data' },
59
+ })
60
+ expect(wrapper.find('.mint-section-card__title--uppercase').exists()).toBe(false)
61
+ })
62
+ })
63
+
64
+ describe('icon chip', () => {
65
+ it('should render an icon chip with an SVG path when icon is provided', () => {
66
+ const wrapper = mount(SectionCard, {
67
+ props: { title: 'Experiments', icon: 'M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7' },
68
+ })
69
+ expect(wrapper.find('.mint-section-card__icon-chip').exists()).toBe(true)
70
+ expect(wrapper.find('.mint-section-card__icon-chip path').exists()).toBe(true)
71
+ })
72
+
73
+ it('should render one path per entry when icon is an array of paths', () => {
74
+ const wrapper = mount(SectionCard, {
75
+ props: { title: 'Experiments', icon: ['M4 7v10', 'M4 7c0 2.21'] },
76
+ })
77
+ expect(wrapper.findAll('.mint-section-card__icon-chip path')).toHaveLength(2)
78
+ })
79
+
80
+ it('should not render an icon chip when icon is omitted', () => {
81
+ const wrapper = mount(SectionCard, {
82
+ props: { title: 'Experiments' },
83
+ })
84
+ expect(wrapper.find('.mint-section-card__icon-chip').exists()).toBe(false)
85
+ })
86
+
87
+ it('should apply the accent modifier class matching the accent prop', () => {
88
+ const wrapper = mount(SectionCard, {
89
+ props: { title: 'Projects', icon: 'M3 7v10', accent: 'project' },
90
+ })
91
+ expect(wrapper.find('.mint-section-card__icon-chip--project').exists()).toBe(true)
92
+ })
93
+
94
+ it('should default to the primary accent when accent is omitted', () => {
95
+ const wrapper = mount(SectionCard, {
96
+ props: { title: 'Projects', icon: 'M3 7v10' },
97
+ })
98
+ expect(wrapper.find('.mint-section-card__icon-chip--primary').exists()).toBe(true)
99
+ })
100
+ })
101
+
102
+ describe('header divider', () => {
103
+ it('should use the strong divider by default', () => {
104
+ const wrapper = mount(SectionCard, {
105
+ props: { title: 'Experiments' },
106
+ })
107
+ expect(wrapper.find('.mint-section-card__header--divider-light').exists()).toBe(false)
108
+ })
109
+
110
+ it('should apply the light divider modifier when divider is light', () => {
111
+ const wrapper = mount(SectionCard, {
112
+ props: { title: 'Design data', divider: 'light' },
113
+ })
114
+ expect(wrapper.find('.mint-section-card__header--divider-light').exists()).toBe(true)
115
+ })
116
+ })
117
+
118
+ describe('fill mode', () => {
119
+ it('should not stretch by default', () => {
120
+ const wrapper = mount(SectionCard, {
121
+ props: { title: 'Experiments' },
122
+ })
123
+ expect(wrapper.find('.mint-section-card--fill').exists()).toBe(false)
124
+ })
125
+
126
+ it('should apply the fill modifier when fill is true', () => {
127
+ const wrapper = mount(SectionCard, {
128
+ props: { title: 'Experiments', fill: true },
129
+ })
130
+ expect(wrapper.find('.mint-section-card--fill').exists()).toBe(true)
131
+ })
132
+
133
+ it('should render the toolbar slot outside the scrolling body', () => {
134
+ const wrapper = mount(SectionCard, {
135
+ props: { title: 'Experiments', fill: true },
136
+ slots: { toolbar: '<div class="toolbar-probe">chips</div>', default: '<p>rows</p>' },
137
+ })
138
+ expect(wrapper.find('.mint-section-card__toolbar .toolbar-probe').exists()).toBe(true)
139
+ expect(wrapper.find('.mint-section-card__body .toolbar-probe').exists()).toBe(false)
140
+ })
141
+
142
+ it('should not render a toolbar region when the slot is unused', () => {
143
+ const wrapper = mount(SectionCard, {
144
+ props: { title: 'Experiments' },
145
+ })
146
+ expect(wrapper.find('.mint-section-card__toolbar').exists()).toBe(false)
147
+ })
148
+
149
+ it('should keep the header, body and footer structure when filling', () => {
150
+ const wrapper = mount(SectionCard, {
151
+ props: { title: 'Experiments', fill: true },
152
+ slots: { default: '<p>rows</p>', footer: '<span>1 / 3</span>' },
153
+ })
154
+ expect(wrapper.find('.mint-section-card__header').exists()).toBe(true)
155
+ expect(wrapper.find('.mint-section-card__body').exists()).toBe(true)
156
+ expect(wrapper.find('.mint-section-card__footer').exists()).toBe(true)
157
+ })
158
+ })
159
+
160
+ describe('slots', () => {
161
+ it('should render the actions slot at the end of the header', () => {
162
+ const wrapper = mount(SectionCard, {
163
+ props: { title: 'Experiments' },
164
+ slots: { actions: '<a class="actions-probe" href="#">All →</a>' },
165
+ })
166
+ expect(wrapper.find('.mint-section-card__header .actions-probe').exists()).toBe(true)
167
+ })
168
+
169
+ it('should render the footer strip only when the footer slot is provided', () => {
170
+ const withFooter = mount(SectionCard, {
171
+ props: { title: 'Experiments' },
172
+ slots: { footer: '<span class="footer-probe">Showing 1–18 of 42</span>' },
173
+ })
174
+ const withoutFooter = mount(SectionCard, {
175
+ props: { title: 'Experiments' },
176
+ })
177
+ expect(withFooter.find('.mint-section-card__footer .footer-probe').exists()).toBe(true)
178
+ expect(withoutFooter.find('.mint-section-card__footer').exists()).toBe(false)
179
+ })
180
+ })
181
+ })
@@ -17,6 +17,11 @@ vi.mock('vue', async () => {
17
17
  })
18
18
 
19
19
  import { usePlatformContext } from '../../composables/usePlatformContext'
20
+ import type { PlatformContext } from '../../types'
21
+
22
+ type InjectedPlatformWindow = Window & {
23
+ __MINT_PLATFORM__?: PlatformContext
24
+ }
20
25
 
21
26
  function sendThemeMessage(origin: string, theme: 'light' | 'dark' | 'system'): void {
22
27
  if (!messageHandler) {
@@ -37,6 +42,7 @@ describe('usePlatformContext', () => {
37
42
  beforeEach(() => {
38
43
  unmountedCallbacks.length = 0
39
44
  messageHandler = null
45
+ delete (window as InjectedPlatformWindow).__MINT_PLATFORM__
40
46
 
41
47
  vi.spyOn(window, 'addEventListener').mockImplementation((type, listener) => {
42
48
  if (type === 'message') {
@@ -51,9 +57,31 @@ describe('usePlatformContext', () => {
51
57
  const callback = unmountedCallbacks.pop()
52
58
  callback?.()
53
59
  }
60
+ delete (window as InjectedPlatformWindow).__MINT_PLATFORM__
54
61
  vi.restoreAllMocks()
55
62
  })
56
63
 
64
+ it('preserves the injected plugin settings API capability', () => {
65
+ const injectedContext: PlatformContext = {
66
+ isIntegrated: true,
67
+ theme: 'light',
68
+ platformApiUrl: '/api',
69
+ plugin: {
70
+ id: 'test-plugin',
71
+ name: 'test-plugin',
72
+ version: '1.0.0',
73
+ route_prefix: '/test-plugin',
74
+ api_prefix: '/api/test-plugin',
75
+ settings_api: 'plugin',
76
+ },
77
+ }
78
+ ;(window as InjectedPlatformWindow).__MINT_PLATFORM__ = injectedContext
79
+
80
+ const context = usePlatformContext()
81
+
82
+ expect(context.plugin.value?.settings_api).toBe('plugin')
83
+ })
84
+
57
85
  it('clears allowAnyOrigin policy after last consumer unmounts', () => {
58
86
  const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
59
87