@mulmocast/deck 0.1.1 → 0.2.0

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/lib/render.js CHANGED
@@ -1,4 +1,4 @@
1
- import { escapeHtml, buildTailwindConfig, sanitizeHex, detectBlockTypes } from "./utils.js";
1
+ import { escapeHtml, buildTailwindConfig, sanitizeHex, detectBlockTypes, isSafeCssBackground } from "./utils.js";
2
2
  import { renderSlideContent } from "./layouts/index.js";
3
3
  /** Determine if a hex color is dark (luminance < 128) */
4
4
  const isDarkBg = (hex) => {
@@ -64,9 +64,30 @@ export const generateSlideHTML = (theme, slide, reference, branding) => {
64
64
  const cdnScripts = buildCdnScripts(theme, slide);
65
65
  const slideStyle = slide.style;
66
66
  const hasBgOpacity = branding?.backgroundImage?.bgOpacity !== undefined;
67
- const bgCls = hasBgOpacity || slideStyle?.bgColor ? "" : "bg-d-bg";
68
- const bgColorStyle = slideStyle?.bgColor ? ` style="background-color:#${sanitizeHex(slideStyle.bgColor)}"` : "";
69
- const inlineStyle = hasBgOpacity ? "" : bgColorStyle;
67
+ // Background resolution priority (any new field is optional; when absent the output is byte-identical to pre-extension behavior):
68
+ // slide.style.bgGradient > theme.bgGradient > slide.style.bgColor > theme.colors.bg (via `bg-d-bg` class).
69
+ // hasBgOpacity (branding bg image with custom opacity) still suppresses background styling, as before.
70
+ let bgCls = "";
71
+ let inlineStyle = "";
72
+ if (!hasBgOpacity) {
73
+ const slideBgGradient = slideStyle?.bgGradient && isSafeCssBackground(slideStyle.bgGradient) ? slideStyle.bgGradient : undefined;
74
+ const themeBgGradient = !slideBgGradient && theme.bgGradient && isSafeCssBackground(theme.bgGradient) ? theme.bgGradient : undefined;
75
+ const bgGradient = slideBgGradient ?? themeBgGradient;
76
+ if (bgGradient) {
77
+ inlineStyle = ` style="background:${bgGradient}"`;
78
+ }
79
+ else if (slideStyle?.bgColor) {
80
+ inlineStyle = ` style="background-color:#${sanitizeHex(slideStyle.bgColor)}"`;
81
+ }
82
+ else {
83
+ bgCls = "bg-d-bg";
84
+ }
85
+ }
86
+ // Optional `<style>` block: when theme.titleGradient is set (and safe), paint h1 titles with a background-clipped gradient.
87
+ // Prepend a newline so that when set the block appears on its own line; when empty, no extra blank line is emitted.
88
+ const titleGradientCss = theme.titleGradient && isSafeCssBackground(theme.titleGradient)
89
+ ? `\n<style>h1.font-title.font-bold{background:${theme.titleGradient};-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:transparent;}</style>`
90
+ : "";
70
91
  const footer = slideStyle?.footer ? `<p class="absolute bottom-2 right-4 text-xs text-d-dim font-body">${escapeHtml(slideStyle.footer)}</p>` : "";
71
92
  const referenceHtml = reference
72
93
  ? `<div class="mt-auto px-4 pb-2"><p class="text-sm text-d-muted font-body opacity-80">${escapeHtml(reference)}</p></div>`
@@ -84,7 +105,7 @@ export const generateSlideHTML = (theme, slide, reference, branding) => {
84
105
  ${cdnScripts}
85
106
  <style>
86
107
  html, body { height: 100%; margin: 0; padding: 0; overflow: hidden; }
87
- </style>
108
+ </style>${titleGradientCss}
88
109
  </head>
89
110
  <body class="h-full">
90
111
  <div class="relative overflow-hidden ${bgCls} w-full h-full flex flex-col"${inlineStyle}>
package/lib/schema.d.ts CHANGED
@@ -28,6 +28,7 @@ export declare const slideThemeFontsSchema: z.ZodObject<{
28
28
  title: z.ZodString;
29
29
  body: z.ZodString;
30
30
  mono: z.ZodString;
31
+ accent: z.ZodOptional<z.ZodString>;
31
32
  }, z.core.$strip>;
32
33
  export declare const slideThemeSchema: z.ZodObject<{
33
34
  colors: z.ZodObject<{
@@ -49,7 +50,10 @@ export declare const slideThemeSchema: z.ZodObject<{
49
50
  title: z.ZodString;
50
51
  body: z.ZodString;
51
52
  mono: z.ZodString;
53
+ accent: z.ZodOptional<z.ZodString>;
52
54
  }, z.core.$strip>;
55
+ bgGradient: z.ZodOptional<z.ZodString>;
56
+ titleGradient: z.ZodOptional<z.ZodString>;
53
57
  }, z.core.$strip>;
54
58
  export declare const textBlockSchema: z.ZodObject<{
55
59
  type: z.ZodLiteral<"text">;
@@ -895,6 +899,7 @@ export declare const cardSchema: z.ZodObject<{
895
899
  }, z.core.$strip>;
896
900
  export declare const slideStyleSchema: z.ZodObject<{
897
901
  bgColor: z.ZodOptional<z.ZodString>;
902
+ bgGradient: z.ZodOptional<z.ZodString>;
898
903
  decorations: z.ZodOptional<z.ZodBoolean>;
899
904
  bgOpacity: z.ZodOptional<z.ZodNumber>;
900
905
  footer: z.ZodOptional<z.ZodString>;
@@ -915,6 +920,7 @@ export declare const titleSlideSchema: z.ZodObject<{
915
920
  }>>;
916
921
  style: z.ZodOptional<z.ZodObject<{
917
922
  bgColor: z.ZodOptional<z.ZodString>;
923
+ bgGradient: z.ZodOptional<z.ZodString>;
918
924
  decorations: z.ZodOptional<z.ZodBoolean>;
919
925
  bgOpacity: z.ZodOptional<z.ZodNumber>;
920
926
  footer: z.ZodOptional<z.ZodString>;
@@ -1230,6 +1236,7 @@ export declare const columnsSlideSchema: z.ZodObject<{
1230
1236
  }>>;
1231
1237
  style: z.ZodOptional<z.ZodObject<{
1232
1238
  bgColor: z.ZodOptional<z.ZodString>;
1239
+ bgGradient: z.ZodOptional<z.ZodString>;
1233
1240
  decorations: z.ZodOptional<z.ZodBoolean>;
1234
1241
  bgOpacity: z.ZodOptional<z.ZodNumber>;
1235
1242
  footer: z.ZodOptional<z.ZodString>;
@@ -2082,6 +2089,7 @@ export declare const comparisonSlideSchema: z.ZodObject<{
2082
2089
  }>>;
2083
2090
  style: z.ZodOptional<z.ZodObject<{
2084
2091
  bgColor: z.ZodOptional<z.ZodString>;
2092
+ bgGradient: z.ZodOptional<z.ZodString>;
2085
2093
  decorations: z.ZodOptional<z.ZodBoolean>;
2086
2094
  bgOpacity: z.ZodOptional<z.ZodNumber>;
2087
2095
  footer: z.ZodOptional<z.ZodString>;
@@ -2650,6 +2658,7 @@ export declare const gridSlideSchema: z.ZodObject<{
2650
2658
  }>>;
2651
2659
  style: z.ZodOptional<z.ZodObject<{
2652
2660
  bgColor: z.ZodOptional<z.ZodString>;
2661
+ bgGradient: z.ZodOptional<z.ZodString>;
2653
2662
  decorations: z.ZodOptional<z.ZodBoolean>;
2654
2663
  bgOpacity: z.ZodOptional<z.ZodNumber>;
2655
2664
  footer: z.ZodOptional<z.ZodString>;
@@ -2671,6 +2680,7 @@ export declare const bigQuoteSlideSchema: z.ZodObject<{
2671
2680
  }>>;
2672
2681
  style: z.ZodOptional<z.ZodObject<{
2673
2682
  bgColor: z.ZodOptional<z.ZodString>;
2683
+ bgGradient: z.ZodOptional<z.ZodString>;
2674
2684
  decorations: z.ZodOptional<z.ZodBoolean>;
2675
2685
  bgOpacity: z.ZodOptional<z.ZodNumber>;
2676
2686
  footer: z.ZodOptional<z.ZodString>;
@@ -2738,6 +2748,7 @@ export declare const statsSlideSchema: z.ZodObject<{
2738
2748
  }>>;
2739
2749
  style: z.ZodOptional<z.ZodObject<{
2740
2750
  bgColor: z.ZodOptional<z.ZodString>;
2751
+ bgGradient: z.ZodOptional<z.ZodString>;
2741
2752
  decorations: z.ZodOptional<z.ZodBoolean>;
2742
2753
  bgOpacity: z.ZodOptional<z.ZodNumber>;
2743
2754
  footer: z.ZodOptional<z.ZodString>;
@@ -2789,6 +2800,7 @@ export declare const timelineSlideSchema: z.ZodObject<{
2789
2800
  }>>;
2790
2801
  style: z.ZodOptional<z.ZodObject<{
2791
2802
  bgColor: z.ZodOptional<z.ZodString>;
2803
+ bgGradient: z.ZodOptional<z.ZodString>;
2792
2804
  decorations: z.ZodOptional<z.ZodBoolean>;
2793
2805
  bgOpacity: z.ZodOptional<z.ZodNumber>;
2794
2806
  footer: z.ZodOptional<z.ZodString>;
@@ -3647,6 +3659,7 @@ export declare const splitSlideSchema: z.ZodObject<{
3647
3659
  }>>;
3648
3660
  style: z.ZodOptional<z.ZodObject<{
3649
3661
  bgColor: z.ZodOptional<z.ZodString>;
3662
+ bgGradient: z.ZodOptional<z.ZodString>;
3650
3663
  decorations: z.ZodOptional<z.ZodBoolean>;
3651
3664
  bgOpacity: z.ZodOptional<z.ZodNumber>;
3652
3665
  footer: z.ZodOptional<z.ZodString>;
@@ -4222,6 +4235,7 @@ export declare const matrixSlideSchema: z.ZodObject<{
4222
4235
  }>>;
4223
4236
  style: z.ZodOptional<z.ZodObject<{
4224
4237
  bgColor: z.ZodOptional<z.ZodString>;
4238
+ bgGradient: z.ZodOptional<z.ZodString>;
4225
4239
  decorations: z.ZodOptional<z.ZodBoolean>;
4226
4240
  bgOpacity: z.ZodOptional<z.ZodNumber>;
4227
4241
  footer: z.ZodOptional<z.ZodString>;
@@ -4278,6 +4292,7 @@ export declare const tableSlideSchema: z.ZodObject<{
4278
4292
  }>>;
4279
4293
  style: z.ZodOptional<z.ZodObject<{
4280
4294
  bgColor: z.ZodOptional<z.ZodString>;
4295
+ bgGradient: z.ZodOptional<z.ZodString>;
4281
4296
  decorations: z.ZodOptional<z.ZodBoolean>;
4282
4297
  bgOpacity: z.ZodOptional<z.ZodNumber>;
4283
4298
  footer: z.ZodOptional<z.ZodString>;
@@ -4346,6 +4361,7 @@ export declare const waterfallSlideSchema: z.ZodObject<{
4346
4361
  }>>;
4347
4362
  style: z.ZodOptional<z.ZodObject<{
4348
4363
  bgColor: z.ZodOptional<z.ZodString>;
4364
+ bgGradient: z.ZodOptional<z.ZodString>;
4349
4365
  decorations: z.ZodOptional<z.ZodBoolean>;
4350
4366
  bgOpacity: z.ZodOptional<z.ZodNumber>;
4351
4367
  footer: z.ZodOptional<z.ZodString>;
@@ -4413,6 +4429,7 @@ export declare const funnelSlideSchema: z.ZodObject<{
4413
4429
  }>>;
4414
4430
  style: z.ZodOptional<z.ZodObject<{
4415
4431
  bgColor: z.ZodOptional<z.ZodString>;
4432
+ bgGradient: z.ZodOptional<z.ZodString>;
4416
4433
  decorations: z.ZodOptional<z.ZodBoolean>;
4417
4434
  bgOpacity: z.ZodOptional<z.ZodNumber>;
4418
4435
  footer: z.ZodOptional<z.ZodString>;
@@ -4495,6 +4512,7 @@ export declare const slideLayoutSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
4495
4512
  }>>;
4496
4513
  style: z.ZodOptional<z.ZodObject<{
4497
4514
  bgColor: z.ZodOptional<z.ZodString>;
4515
+ bgGradient: z.ZodOptional<z.ZodString>;
4498
4516
  decorations: z.ZodOptional<z.ZodBoolean>;
4499
4517
  bgOpacity: z.ZodOptional<z.ZodNumber>;
4500
4518
  footer: z.ZodOptional<z.ZodString>;
@@ -4809,6 +4827,7 @@ export declare const slideLayoutSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
4809
4827
  }>>;
4810
4828
  style: z.ZodOptional<z.ZodObject<{
4811
4829
  bgColor: z.ZodOptional<z.ZodString>;
4830
+ bgGradient: z.ZodOptional<z.ZodString>;
4812
4831
  decorations: z.ZodOptional<z.ZodBoolean>;
4813
4832
  bgOpacity: z.ZodOptional<z.ZodNumber>;
4814
4833
  footer: z.ZodOptional<z.ZodString>;
@@ -5389,6 +5408,7 @@ export declare const slideLayoutSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
5389
5408
  }>>;
5390
5409
  style: z.ZodOptional<z.ZodObject<{
5391
5410
  bgColor: z.ZodOptional<z.ZodString>;
5411
+ bgGradient: z.ZodOptional<z.ZodString>;
5392
5412
  decorations: z.ZodOptional<z.ZodBoolean>;
5393
5413
  bgOpacity: z.ZodOptional<z.ZodNumber>;
5394
5414
  footer: z.ZodOptional<z.ZodString>;
@@ -5683,6 +5703,7 @@ export declare const slideLayoutSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
5683
5703
  }>>;
5684
5704
  style: z.ZodOptional<z.ZodObject<{
5685
5705
  bgColor: z.ZodOptional<z.ZodString>;
5706
+ bgGradient: z.ZodOptional<z.ZodString>;
5686
5707
  decorations: z.ZodOptional<z.ZodBoolean>;
5687
5708
  bgOpacity: z.ZodOptional<z.ZodNumber>;
5688
5709
  footer: z.ZodOptional<z.ZodString>;
@@ -5703,6 +5724,7 @@ export declare const slideLayoutSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
5703
5724
  }>>;
5704
5725
  style: z.ZodOptional<z.ZodObject<{
5705
5726
  bgColor: z.ZodOptional<z.ZodString>;
5727
+ bgGradient: z.ZodOptional<z.ZodString>;
5706
5728
  decorations: z.ZodOptional<z.ZodBoolean>;
5707
5729
  bgOpacity: z.ZodOptional<z.ZodNumber>;
5708
5730
  footer: z.ZodOptional<z.ZodString>;
@@ -5755,6 +5777,7 @@ export declare const slideLayoutSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
5755
5777
  }>>;
5756
5778
  style: z.ZodOptional<z.ZodObject<{
5757
5779
  bgColor: z.ZodOptional<z.ZodString>;
5780
+ bgGradient: z.ZodOptional<z.ZodString>;
5758
5781
  decorations: z.ZodOptional<z.ZodBoolean>;
5759
5782
  bgOpacity: z.ZodOptional<z.ZodNumber>;
5760
5783
  footer: z.ZodOptional<z.ZodString>;
@@ -5790,6 +5813,7 @@ export declare const slideLayoutSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
5790
5813
  }>>;
5791
5814
  style: z.ZodOptional<z.ZodObject<{
5792
5815
  bgColor: z.ZodOptional<z.ZodString>;
5816
+ bgGradient: z.ZodOptional<z.ZodString>;
5793
5817
  decorations: z.ZodOptional<z.ZodBoolean>;
5794
5818
  bgOpacity: z.ZodOptional<z.ZodNumber>;
5795
5819
  footer: z.ZodOptional<z.ZodString>;
@@ -6367,6 +6391,7 @@ export declare const slideLayoutSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
6367
6391
  }>>;
6368
6392
  style: z.ZodOptional<z.ZodObject<{
6369
6393
  bgColor: z.ZodOptional<z.ZodString>;
6394
+ bgGradient: z.ZodOptional<z.ZodString>;
6370
6395
  decorations: z.ZodOptional<z.ZodBoolean>;
6371
6396
  bgOpacity: z.ZodOptional<z.ZodNumber>;
6372
6397
  footer: z.ZodOptional<z.ZodString>;
@@ -6670,6 +6695,7 @@ export declare const slideLayoutSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
6670
6695
  }>>;
6671
6696
  style: z.ZodOptional<z.ZodObject<{
6672
6697
  bgColor: z.ZodOptional<z.ZodString>;
6698
+ bgGradient: z.ZodOptional<z.ZodString>;
6673
6699
  decorations: z.ZodOptional<z.ZodBoolean>;
6674
6700
  bgOpacity: z.ZodOptional<z.ZodNumber>;
6675
6701
  footer: z.ZodOptional<z.ZodString>;
@@ -6725,6 +6751,7 @@ export declare const slideLayoutSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
6725
6751
  }>>;
6726
6752
  style: z.ZodOptional<z.ZodObject<{
6727
6753
  bgColor: z.ZodOptional<z.ZodString>;
6754
+ bgGradient: z.ZodOptional<z.ZodString>;
6728
6755
  decorations: z.ZodOptional<z.ZodBoolean>;
6729
6756
  bgOpacity: z.ZodOptional<z.ZodNumber>;
6730
6757
  footer: z.ZodOptional<z.ZodString>;
@@ -6777,6 +6804,7 @@ export declare const slideLayoutSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
6777
6804
  }>>;
6778
6805
  style: z.ZodOptional<z.ZodObject<{
6779
6806
  bgColor: z.ZodOptional<z.ZodString>;
6807
+ bgGradient: z.ZodOptional<z.ZodString>;
6780
6808
  decorations: z.ZodOptional<z.ZodBoolean>;
6781
6809
  bgOpacity: z.ZodOptional<z.ZodNumber>;
6782
6810
  footer: z.ZodOptional<z.ZodString>;
@@ -6830,6 +6858,7 @@ export declare const slideLayoutSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
6830
6858
  }>>;
6831
6859
  style: z.ZodOptional<z.ZodObject<{
6832
6860
  bgColor: z.ZodOptional<z.ZodString>;
6861
+ bgGradient: z.ZodOptional<z.ZodString>;
6833
6862
  decorations: z.ZodOptional<z.ZodBoolean>;
6834
6863
  bgOpacity: z.ZodOptional<z.ZodNumber>;
6835
6864
  footer: z.ZodOptional<z.ZodString>;
@@ -6859,7 +6888,10 @@ export declare const mulmoSlideMediaSchema: z.ZodObject<{
6859
6888
  title: z.ZodString;
6860
6889
  body: z.ZodString;
6861
6890
  mono: z.ZodString;
6891
+ accent: z.ZodOptional<z.ZodString>;
6862
6892
  }, z.core.$strip>;
6893
+ bgGradient: z.ZodOptional<z.ZodString>;
6894
+ titleGradient: z.ZodOptional<z.ZodString>;
6863
6895
  }, z.core.$strip>>;
6864
6896
  slide: z.ZodDiscriminatedUnion<[z.ZodObject<{
6865
6897
  title: z.ZodString;
@@ -6877,6 +6909,7 @@ export declare const mulmoSlideMediaSchema: z.ZodObject<{
6877
6909
  }>>;
6878
6910
  style: z.ZodOptional<z.ZodObject<{
6879
6911
  bgColor: z.ZodOptional<z.ZodString>;
6912
+ bgGradient: z.ZodOptional<z.ZodString>;
6880
6913
  decorations: z.ZodOptional<z.ZodBoolean>;
6881
6914
  bgOpacity: z.ZodOptional<z.ZodNumber>;
6882
6915
  footer: z.ZodOptional<z.ZodString>;
@@ -7191,6 +7224,7 @@ export declare const mulmoSlideMediaSchema: z.ZodObject<{
7191
7224
  }>>;
7192
7225
  style: z.ZodOptional<z.ZodObject<{
7193
7226
  bgColor: z.ZodOptional<z.ZodString>;
7227
+ bgGradient: z.ZodOptional<z.ZodString>;
7194
7228
  decorations: z.ZodOptional<z.ZodBoolean>;
7195
7229
  bgOpacity: z.ZodOptional<z.ZodNumber>;
7196
7230
  footer: z.ZodOptional<z.ZodString>;
@@ -7771,6 +7805,7 @@ export declare const mulmoSlideMediaSchema: z.ZodObject<{
7771
7805
  }>>;
7772
7806
  style: z.ZodOptional<z.ZodObject<{
7773
7807
  bgColor: z.ZodOptional<z.ZodString>;
7808
+ bgGradient: z.ZodOptional<z.ZodString>;
7774
7809
  decorations: z.ZodOptional<z.ZodBoolean>;
7775
7810
  bgOpacity: z.ZodOptional<z.ZodNumber>;
7776
7811
  footer: z.ZodOptional<z.ZodString>;
@@ -8065,6 +8100,7 @@ export declare const mulmoSlideMediaSchema: z.ZodObject<{
8065
8100
  }>>;
8066
8101
  style: z.ZodOptional<z.ZodObject<{
8067
8102
  bgColor: z.ZodOptional<z.ZodString>;
8103
+ bgGradient: z.ZodOptional<z.ZodString>;
8068
8104
  decorations: z.ZodOptional<z.ZodBoolean>;
8069
8105
  bgOpacity: z.ZodOptional<z.ZodNumber>;
8070
8106
  footer: z.ZodOptional<z.ZodString>;
@@ -8085,6 +8121,7 @@ export declare const mulmoSlideMediaSchema: z.ZodObject<{
8085
8121
  }>>;
8086
8122
  style: z.ZodOptional<z.ZodObject<{
8087
8123
  bgColor: z.ZodOptional<z.ZodString>;
8124
+ bgGradient: z.ZodOptional<z.ZodString>;
8088
8125
  decorations: z.ZodOptional<z.ZodBoolean>;
8089
8126
  bgOpacity: z.ZodOptional<z.ZodNumber>;
8090
8127
  footer: z.ZodOptional<z.ZodString>;
@@ -8137,6 +8174,7 @@ export declare const mulmoSlideMediaSchema: z.ZodObject<{
8137
8174
  }>>;
8138
8175
  style: z.ZodOptional<z.ZodObject<{
8139
8176
  bgColor: z.ZodOptional<z.ZodString>;
8177
+ bgGradient: z.ZodOptional<z.ZodString>;
8140
8178
  decorations: z.ZodOptional<z.ZodBoolean>;
8141
8179
  bgOpacity: z.ZodOptional<z.ZodNumber>;
8142
8180
  footer: z.ZodOptional<z.ZodString>;
@@ -8172,6 +8210,7 @@ export declare const mulmoSlideMediaSchema: z.ZodObject<{
8172
8210
  }>>;
8173
8211
  style: z.ZodOptional<z.ZodObject<{
8174
8212
  bgColor: z.ZodOptional<z.ZodString>;
8213
+ bgGradient: z.ZodOptional<z.ZodString>;
8175
8214
  decorations: z.ZodOptional<z.ZodBoolean>;
8176
8215
  bgOpacity: z.ZodOptional<z.ZodNumber>;
8177
8216
  footer: z.ZodOptional<z.ZodString>;
@@ -8749,6 +8788,7 @@ export declare const mulmoSlideMediaSchema: z.ZodObject<{
8749
8788
  }>>;
8750
8789
  style: z.ZodOptional<z.ZodObject<{
8751
8790
  bgColor: z.ZodOptional<z.ZodString>;
8791
+ bgGradient: z.ZodOptional<z.ZodString>;
8752
8792
  decorations: z.ZodOptional<z.ZodBoolean>;
8753
8793
  bgOpacity: z.ZodOptional<z.ZodNumber>;
8754
8794
  footer: z.ZodOptional<z.ZodString>;
@@ -9052,6 +9092,7 @@ export declare const mulmoSlideMediaSchema: z.ZodObject<{
9052
9092
  }>>;
9053
9093
  style: z.ZodOptional<z.ZodObject<{
9054
9094
  bgColor: z.ZodOptional<z.ZodString>;
9095
+ bgGradient: z.ZodOptional<z.ZodString>;
9055
9096
  decorations: z.ZodOptional<z.ZodBoolean>;
9056
9097
  bgOpacity: z.ZodOptional<z.ZodNumber>;
9057
9098
  footer: z.ZodOptional<z.ZodString>;
@@ -9107,6 +9148,7 @@ export declare const mulmoSlideMediaSchema: z.ZodObject<{
9107
9148
  }>>;
9108
9149
  style: z.ZodOptional<z.ZodObject<{
9109
9150
  bgColor: z.ZodOptional<z.ZodString>;
9151
+ bgGradient: z.ZodOptional<z.ZodString>;
9110
9152
  decorations: z.ZodOptional<z.ZodBoolean>;
9111
9153
  bgOpacity: z.ZodOptional<z.ZodNumber>;
9112
9154
  footer: z.ZodOptional<z.ZodString>;
@@ -9159,6 +9201,7 @@ export declare const mulmoSlideMediaSchema: z.ZodObject<{
9159
9201
  }>>;
9160
9202
  style: z.ZodOptional<z.ZodObject<{
9161
9203
  bgColor: z.ZodOptional<z.ZodString>;
9204
+ bgGradient: z.ZodOptional<z.ZodString>;
9162
9205
  decorations: z.ZodOptional<z.ZodBoolean>;
9163
9206
  bgOpacity: z.ZodOptional<z.ZodNumber>;
9164
9207
  footer: z.ZodOptional<z.ZodString>;
@@ -9212,6 +9255,7 @@ export declare const mulmoSlideMediaSchema: z.ZodObject<{
9212
9255
  }>>;
9213
9256
  style: z.ZodOptional<z.ZodObject<{
9214
9257
  bgColor: z.ZodOptional<z.ZodString>;
9258
+ bgGradient: z.ZodOptional<z.ZodString>;
9215
9259
  decorations: z.ZodOptional<z.ZodBoolean>;
9216
9260
  bgOpacity: z.ZodOptional<z.ZodNumber>;
9217
9261
  footer: z.ZodOptional<z.ZodString>;
package/lib/schema.js CHANGED
@@ -25,10 +25,16 @@ export const slideThemeFontsSchema = z.object({
25
25
  title: z.string(),
26
26
  body: z.string(),
27
27
  mono: z.string(),
28
+ /** Optional secondary font for numbers / English labels (e.g. "Outfit"). When set, registers a `font-accent` Tailwind utility. */
29
+ accent: z.string().optional(),
28
30
  });
29
31
  export const slideThemeSchema = z.object({
30
32
  colors: slideThemeColorsSchema,
31
33
  fonts: slideThemeFontsSchema,
34
+ /** Optional CSS background value applied to every slide (e.g. `linear-gradient(...)`). Per-slide `style.bgGradient` wins over this. */
35
+ bgGradient: z.string().optional(),
36
+ /** Optional CSS gradient injected as `<style>` to paint `h1.font-title.font-bold` with `background-clip: text`. */
37
+ titleGradient: z.string().optional(),
32
38
  });
33
39
  // ═══════════════════════════════════════════════════════════
34
40
  // Content Blocks — the atoms of slide content
@@ -171,6 +177,8 @@ export const cardSchema = z.object({
171
177
  // ═══════════════════════════════════════════════════════════
172
178
  export const slideStyleSchema = z.object({
173
179
  bgColor: z.string().optional(),
180
+ /** Optional per-slide CSS background value (e.g. `linear-gradient(...)`); wins over `theme.bgGradient` and `bgColor`. */
181
+ bgGradient: z.string().optional(),
174
182
  decorations: z.boolean().optional(),
175
183
  bgOpacity: z.number().optional(),
176
184
  footer: z.string().optional(),
package/lib/utils.d.ts CHANGED
@@ -27,6 +27,12 @@ export declare const resolveChangeColor: (change: string) => string;
27
27
  export declare const renderOptionalCallout: (callout: CalloutBar | undefined) => string;
28
28
  /** Build the Tailwind config JSON string for theme colors and fonts */
29
29
  export declare const buildTailwindConfig: (theme: SlideTheme) => string;
30
+ /**
31
+ * Validate a user-supplied CSS background value before injecting it into an HTML attribute or `<style>` block.
32
+ * Accepts gradient / color syntax; rejects anything that could break out of the attribute context, inject script,
33
+ * or pull external resources. Returns true only when the string is safe to embed verbatim.
34
+ */
35
+ export declare const isSafeCssBackground: (s: string) => boolean;
30
36
  /** Render a numbered circle badge */
31
37
  export declare const numBadge: (num: number, colorKey: string) => string;
32
38
  /** Render an icon in a square container */
package/lib/utils.js CHANGED
@@ -90,19 +90,37 @@ export const buildTailwindConfig = (theme) => {
90
90
  colorMap[mapped] = `#${v}`;
91
91
  }
92
92
  });
93
+ const fontFamily = {
94
+ title: [theme.fonts.title, "serif"],
95
+ body: [theme.fonts.body, "Arial", "sans-serif"],
96
+ mono: [theme.fonts.mono, "monospace"],
97
+ };
98
+ if (theme.fonts.accent) {
99
+ fontFamily.accent = [theme.fonts.accent, "ui-sans-serif", "system-ui", "sans-serif"];
100
+ }
93
101
  return JSON.stringify({
94
102
  theme: {
95
103
  extend: {
96
104
  colors: { d: colorMap },
97
- fontFamily: {
98
- title: [theme.fonts.title, "serif"],
99
- body: [theme.fonts.body, "Arial", "sans-serif"],
100
- mono: [theme.fonts.mono, "monospace"],
101
- },
105
+ fontFamily,
102
106
  },
103
107
  },
104
108
  });
105
109
  };
110
+ /**
111
+ * Validate a user-supplied CSS background value before injecting it into an HTML attribute or `<style>` block.
112
+ * Accepts gradient / color syntax; rejects anything that could break out of the attribute context, inject script,
113
+ * or pull external resources. Returns true only when the string is safe to embed verbatim.
114
+ */
115
+ export const isSafeCssBackground = (s) => {
116
+ if (!s || typeof s !== "string" || s.length > 2000)
117
+ return false;
118
+ if (/[<>"'`{};:\\\r\n]/.test(s))
119
+ return false;
120
+ if (/(javascript|vbscript|data\s+|expression\s*\(|behavior|@import|url\s*\(|attr\s*\(|content\s*\()/i.test(s))
121
+ return false;
122
+ return true;
123
+ };
106
124
  /** Render a numbered circle badge */
107
125
  export const numBadge = (num, colorKey) => {
108
126
  return `<div class="w-10 h-10 rounded-full bg-${c(colorKey)} flex items-center justify-center shrink-0">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mulmocast/deck",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "MulmoCast deck DSL: JSON-described semantic slide layouts (stats, comparison, timeline, ...) rendered to Tailwind-based HTML",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -16,7 +16,7 @@
16
16
  "lib"
17
17
  ],
18
18
  "dependencies": {
19
- "zod": "^4.3.5"
19
+ "zod": "^4.4.3"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@eslint/js": "^10.0.1",