@blockslides/extension-slide 0.1.0 → 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/dist/index.d.mts CHANGED
@@ -12,6 +12,23 @@ interface SlideOptions {
12
12
  * @default true
13
13
  */
14
14
  injectCSS: boolean;
15
+ /**
16
+ * Render mode for sizing
17
+ * - fixed: width/height set from size registry (mm/in/px)
18
+ * - dynamic: width:100% with preserved aspect ratio
19
+ * @default 'fixed'
20
+ */
21
+ renderMode: "fixed" | "dynamic";
22
+ /**
23
+ * Default size applied when attrs.size is absent
24
+ * @default '16x9'
25
+ */
26
+ defaultSize: "16x9" | "4x3" | "a4-portrait" | "a4-landscape" | "letter-portrait" | "letter-landscape" | "linkedin-banner";
27
+ /**
28
+ * Inject @media print/@page CSS for paper sizes
29
+ * @default true
30
+ */
31
+ injectPrintCSS: boolean;
15
32
  /**
16
33
  * Content Security Policy nonce
17
34
  */
@@ -19,4 +36,4 @@ interface SlideOptions {
19
36
  }
20
37
  declare const Slide: Node<SlideOptions, any>;
21
38
 
22
- export { Slide };
39
+ export { Slide, type SlideOptions };
package/dist/index.d.ts CHANGED
@@ -12,6 +12,23 @@ interface SlideOptions {
12
12
  * @default true
13
13
  */
14
14
  injectCSS: boolean;
15
+ /**
16
+ * Render mode for sizing
17
+ * - fixed: width/height set from size registry (mm/in/px)
18
+ * - dynamic: width:100% with preserved aspect ratio
19
+ * @default 'fixed'
20
+ */
21
+ renderMode: "fixed" | "dynamic";
22
+ /**
23
+ * Default size applied when attrs.size is absent
24
+ * @default '16x9'
25
+ */
26
+ defaultSize: "16x9" | "4x3" | "a4-portrait" | "a4-landscape" | "letter-portrait" | "letter-landscape" | "linkedin-banner";
27
+ /**
28
+ * Inject @media print/@page CSS for paper sizes
29
+ * @default true
30
+ */
31
+ injectPrintCSS: boolean;
15
32
  /**
16
33
  * Content Security Policy nonce
17
34
  */
@@ -19,4 +36,4 @@ interface SlideOptions {
19
36
  }
20
37
  declare const Slide: Node<SlideOptions, any>;
21
38
 
22
- export { Slide };
39
+ export { Slide, type SlideOptions };
package/dist/index.js CHANGED
@@ -40,6 +40,42 @@ var slideStyles = `
40
40
  margin-bottom: var(--slide-margin-bottom);
41
41
  }
42
42
  `;
43
+ var fixedSizeStyles = `
44
+ .slide[data-size="16x9"] { width: 1920px; height: 1080px; }
45
+ .slide[data-size="4x3"] { width: 1600px; height: 1200px; }
46
+ .slide[data-size="a4-portrait"] { width: 210mm; height: 297mm; }
47
+ .slide[data-size="a4-landscape"] { width: 297mm; height: 210mm; }
48
+ .slide[data-size="letter-portrait"] { width: 8.5in; height: 11in; }
49
+ .slide[data-size="letter-landscape"] { width: 11in; height: 8.5in; }
50
+ .slide[data-size="linkedin-banner"] { width: 1584px; height: 396px; }
51
+ `.trim();
52
+ var dynamicSizeStyles = `
53
+ .slide[data-size="16x9"] { width: 100%; height: auto; aspect-ratio: 16 / 9; }
54
+ .slide[data-size="4x3"] { width: 100%; height: auto; aspect-ratio: 4 / 3; }
55
+ .slide[data-size="a4-portrait"] { width: 100%; height: auto; aspect-ratio: 210 / 297; }
56
+ .slide[data-size="a4-landscape"] { width: 100%; height: auto; aspect-ratio: 297 / 210; }
57
+ .slide[data-size="letter-portrait"] { width: 100%; height: auto; aspect-ratio: 8.5 / 11; }
58
+ .slide[data-size="letter-landscape"] { width: 100%; height: auto; aspect-ratio: 11 / 8.5; }
59
+ .slide[data-size="linkedin-banner"] { width: 100%; height: auto; aspect-ratio: 1584 / 396; }
60
+ `.trim();
61
+ var printSizeStyles = `
62
+ @media print {
63
+ .slide[data-size="a4-portrait"] { width: 210mm; height: 297mm; }
64
+ @page { size: A4 portrait; margin: 0; }
65
+ }
66
+ @media print {
67
+ .slide[data-size="a4-landscape"] { width: 297mm; height: 210mm; }
68
+ @page { size: A4 landscape; margin: 0; }
69
+ }
70
+ @media print {
71
+ .slide[data-size="letter-portrait"] { width: 8.5in; height: 11in; }
72
+ @page { size: Letter portrait; margin: 0; }
73
+ }
74
+ @media print {
75
+ .slide[data-size="letter-landscape"] { width: 11in; height: 8.5in; }
76
+ @page { size: Letter landscape; margin: 0; }
77
+ }
78
+ `.trim();
43
79
  var SlidePluginKey = new import_state.PluginKey("slide");
44
80
  var Slide = import_core.Node.create({
45
81
  name: "slide",
@@ -51,9 +87,32 @@ var Slide = import_core.Node.create({
51
87
  return {
52
88
  HTMLAttributes: {},
53
89
  injectCSS: true,
90
+ renderMode: "fixed",
91
+ defaultSize: "16x9",
92
+ injectPrintCSS: true,
54
93
  injectNonce: void 0
55
94
  };
56
95
  },
96
+ addAttributes() {
97
+ return {
98
+ size: {
99
+ default: this.options.defaultSize,
100
+ parseHTML: (element) => element.getAttribute("data-size") || this.options.defaultSize,
101
+ renderHTML: (attributes) => {
102
+ if (!attributes.size) {
103
+ return { "data-size": this.options.defaultSize };
104
+ }
105
+ return { "data-size": attributes.size };
106
+ }
107
+ },
108
+ className: {
109
+ default: ""
110
+ },
111
+ id: {
112
+ default: null
113
+ }
114
+ };
115
+ },
57
116
  parseHTML() {
58
117
  return [{ tag: "div.slide" }];
59
118
  },
@@ -75,6 +134,16 @@ var Slide = import_core.Node.create({
75
134
  init: () => {
76
135
  if (this.options.injectCSS && typeof document !== "undefined") {
77
136
  (0, import_core.createStyleTag)(slideStyles, this.options.injectNonce, "slide");
137
+ const sizingCss = this.options.renderMode === "dynamic" ? dynamicSizeStyles : fixedSizeStyles;
138
+ if (sizingCss) {
139
+ (0, import_core.createStyleTag)(sizingCss, this.options.injectNonce, "slide-sizes");
140
+ }
141
+ if (this.options.injectPrintCSS) {
142
+ const printCss = printSizeStyles;
143
+ if (printCss) {
144
+ (0, import_core.createStyleTag)(printCss, this.options.injectNonce, "slide-print");
145
+ }
146
+ }
78
147
  }
79
148
  return {};
80
149
  },
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/slide.ts"],"sourcesContent":["export { Slide } from \"./slide\";\n","import { Node, mergeAttributes, createStyleTag } from \"@blockslides/core\";\nimport { Plugin, PluginKey } from \"@blockslides/pm/state\";\n\nconst slideStyles = `\n.slide {\n height: var(--slide-height, 100%);\n min-height: var(--slide-min-height, 250px);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n background-color: var(--slide-bg);\n border-radius: var(--slide-border-radius);\n box-shadow: var(--slide-shadow);\n margin-bottom: var(--slide-margin-bottom);\n}\n`;\n\nexport interface SlideOptions {\n /**\n * The HTML attributes for a slide node.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record<string, any>;\n /**\n * Whether to inject CSS styles\n * @default true\n */\n injectCSS: boolean;\n /**\n * Content Security Policy nonce\n */\n injectNonce?: string;\n}\n\nconst SlidePluginKey = new PluginKey(\"slide\");\n\nexport const Slide = Node.create<SlideOptions>({\n name: \"slide\",\n isolating: true,\n content: \"row+\",\n\n group: \"slide\",\n\n defining: true,\n\n addOptions() {\n return {\n HTMLAttributes: {},\n injectCSS: true,\n injectNonce: undefined,\n };\n },\n\n parseHTML() {\n return [{ tag: \"div.slide\" }];\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\n \"div\",\n mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {\n class: \"slide\",\n \"data-node-type\": \"slide\",\n }),\n 0,\n ];\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: SlidePluginKey,\n state: {\n init: () => {\n if (this.options.injectCSS && typeof document !== \"undefined\") {\n createStyleTag(slideStyles, this.options.injectNonce, \"slide\");\n }\n return {};\n },\n apply: (_tr, pluginState: Record<string, never>) => pluginState,\n },\n }),\n ];\n },\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAAsD;AACtD,mBAAkC;AAElC,IAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgCpB,IAAM,iBAAiB,IAAI,uBAAU,OAAO;AAErC,IAAM,QAAQ,iBAAK,OAAqB;AAAA,EAC7C,MAAM;AAAA,EACN,WAAW;AAAA,EACX,SAAS;AAAA,EAET,OAAO;AAAA,EAEP,UAAU;AAAA,EAEV,aAAa;AACX,WAAO;AAAA,MACL,gBAAgB,CAAC;AAAA,MACjB,WAAW;AAAA,MACX,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EAEA,YAAY;AACV,WAAO,CAAC,EAAE,KAAK,YAAY,CAAC;AAAA,EAC9B;AAAA,EAEA,WAAW,EAAE,eAAe,GAAG;AAC7B,WAAO;AAAA,MACL;AAAA,UACA,6BAAgB,KAAK,QAAQ,gBAAgB,gBAAgB;AAAA,QAC3D,OAAO;AAAA,QACP,kBAAkB;AAAA,MACpB,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,WAAO;AAAA,MACL,IAAI,oBAAO;AAAA,QACT,KAAK;AAAA,QACL,OAAO;AAAA,UACL,MAAM,MAAM;AACV,gBAAI,KAAK,QAAQ,aAAa,OAAO,aAAa,aAAa;AAC7D,8CAAe,aAAa,KAAK,QAAQ,aAAa,OAAO;AAAA,YAC/D;AACA,mBAAO,CAAC;AAAA,UACV;AAAA,UACA,OAAO,CAAC,KAAK,gBAAuC;AAAA,QACtD;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/slide.ts"],"sourcesContent":["export { Slide } from \"./slide\";\nexport type { SlideOptions } from \"./slide\";\n","import { Node, mergeAttributes, createStyleTag } from \"@blockslides/core\";\nimport { Plugin, PluginKey } from \"@blockslides/pm/state\";\n\nconst slideStyles = `\n.slide {\n height: var(--slide-height, 100%);\n min-height: var(--slide-min-height, 250px);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n background-color: var(--slide-bg);\n border-radius: var(--slide-border-radius);\n box-shadow: var(--slide-shadow);\n margin-bottom: var(--slide-margin-bottom);\n}\n`;\n\nconst fixedSizeStyles = `\n.slide[data-size=\"16x9\"] { width: 1920px; height: 1080px; }\n.slide[data-size=\"4x3\"] { width: 1600px; height: 1200px; }\n.slide[data-size=\"a4-portrait\"] { width: 210mm; height: 297mm; }\n.slide[data-size=\"a4-landscape\"] { width: 297mm; height: 210mm; }\n.slide[data-size=\"letter-portrait\"] { width: 8.5in; height: 11in; }\n.slide[data-size=\"letter-landscape\"] { width: 11in; height: 8.5in; }\n.slide[data-size=\"linkedin-banner\"] { width: 1584px; height: 396px; }\n`.trim();\n\nconst dynamicSizeStyles = `\n.slide[data-size=\"16x9\"] { width: 100%; height: auto; aspect-ratio: 16 / 9; }\n.slide[data-size=\"4x3\"] { width: 100%; height: auto; aspect-ratio: 4 / 3; }\n.slide[data-size=\"a4-portrait\"] { width: 100%; height: auto; aspect-ratio: 210 / 297; }\n.slide[data-size=\"a4-landscape\"] { width: 100%; height: auto; aspect-ratio: 297 / 210; }\n.slide[data-size=\"letter-portrait\"] { width: 100%; height: auto; aspect-ratio: 8.5 / 11; }\n.slide[data-size=\"letter-landscape\"] { width: 100%; height: auto; aspect-ratio: 11 / 8.5; }\n.slide[data-size=\"linkedin-banner\"] { width: 100%; height: auto; aspect-ratio: 1584 / 396; }\n`.trim();\n\nconst printSizeStyles = `\n@media print {\n .slide[data-size=\"a4-portrait\"] { width: 210mm; height: 297mm; }\n @page { size: A4 portrait; margin: 0; }\n}\n@media print {\n .slide[data-size=\"a4-landscape\"] { width: 297mm; height: 210mm; }\n @page { size: A4 landscape; margin: 0; }\n}\n@media print {\n .slide[data-size=\"letter-portrait\"] { width: 8.5in; height: 11in; }\n @page { size: Letter portrait; margin: 0; }\n}\n@media print {\n .slide[data-size=\"letter-landscape\"] { width: 11in; height: 8.5in; }\n @page { size: Letter landscape; margin: 0; }\n}\n`.trim();\n\nexport interface SlideOptions {\n /**\n * The HTML attributes for a slide node.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record<string, any>;\n /**\n * Whether to inject CSS styles\n * @default true\n */\n injectCSS: boolean;\n /**\n * Render mode for sizing\n * - fixed: width/height set from size registry (mm/in/px)\n * - dynamic: width:100% with preserved aspect ratio\n * @default 'fixed'\n */\n renderMode: \"fixed\" | \"dynamic\";\n /**\n * Default size applied when attrs.size is absent\n * @default '16x9'\n */\n defaultSize: \"16x9\" | \"4x3\" | \"a4-portrait\" | \"a4-landscape\" | \"letter-portrait\" | \"letter-landscape\" | \"linkedin-banner\";\n /**\n * Inject @media print/@page CSS for paper sizes\n * @default true\n */\n injectPrintCSS: boolean;\n /**\n * Content Security Policy nonce\n */\n injectNonce?: string;\n}\n\nconst SlidePluginKey = new PluginKey(\"slide\");\n\nexport const Slide = Node.create<SlideOptions>({\n name: \"slide\",\n isolating: true,\n content: \"row+\",\n\n group: \"slide\",\n\n defining: true,\n\n addOptions() {\n return {\n HTMLAttributes: {},\n injectCSS: true,\n renderMode: \"fixed\",\n defaultSize: \"16x9\",\n injectPrintCSS: true,\n injectNonce: undefined,\n };\n },\n\n addAttributes() {\n return {\n size: {\n default: this.options.defaultSize,\n parseHTML: (element) => element.getAttribute(\"data-size\") || this.options.defaultSize,\n renderHTML: (attributes) => {\n if (!attributes.size) {\n return { \"data-size\": this.options.defaultSize };\n }\n return { \"data-size\": attributes.size };\n },\n },\n className: {\n default: \"\",\n },\n id: {\n default: null,\n },\n };\n },\n\n parseHTML() {\n return [{ tag: \"div.slide\" }];\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\n \"div\",\n mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {\n class: \"slide\",\n \"data-node-type\": \"slide\",\n }),\n 0,\n ];\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: SlidePluginKey,\n state: {\n init: () => {\n if (this.options.injectCSS && typeof document !== \"undefined\") {\n createStyleTag(slideStyles, this.options.injectNonce, \"slide\");\n const sizingCss =\n this.options.renderMode === \"dynamic\" ? dynamicSizeStyles : fixedSizeStyles;\n if (sizingCss) {\n createStyleTag(sizingCss, this.options.injectNonce, \"slide-sizes\");\n }\n if (this.options.injectPrintCSS) {\n const printCss = printSizeStyles;\n if (printCss) {\n createStyleTag(printCss, this.options.injectNonce, \"slide-print\");\n }\n }\n }\n return {};\n },\n apply: (_tr, pluginState: Record<string, never>) => pluginState,\n },\n }),\n ];\n },\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAAsD;AACtD,mBAAkC;AAElC,IAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcpB,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQtB,KAAK;AAEP,IAAM,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQxB,KAAK;AAEP,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBtB,KAAK;AAqCP,IAAM,iBAAiB,IAAI,uBAAU,OAAO;AAErC,IAAM,QAAQ,iBAAK,OAAqB;AAAA,EAC7C,MAAM;AAAA,EACN,WAAW;AAAA,EACX,SAAS;AAAA,EAET,OAAO;AAAA,EAEP,UAAU;AAAA,EAEV,aAAa;AACX,WAAO;AAAA,MACL,gBAAgB,CAAC;AAAA,MACjB,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EAEA,gBAAgB;AACd,WAAO;AAAA,MACL,MAAM;AAAA,QACJ,SAAS,KAAK,QAAQ;AAAA,QACtB,WAAW,CAAC,YAAY,QAAQ,aAAa,WAAW,KAAK,KAAK,QAAQ;AAAA,QAC1E,YAAY,CAAC,eAAe;AAC1B,cAAI,CAAC,WAAW,MAAM;AACpB,mBAAO,EAAE,aAAa,KAAK,QAAQ,YAAY;AAAA,UACjD;AACA,iBAAO,EAAE,aAAa,WAAW,KAAK;AAAA,QACxC;AAAA,MACF;AAAA,MACA,WAAW;AAAA,QACT,SAAS;AAAA,MACX;AAAA,MACA,IAAI;AAAA,QACF,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EAEA,YAAY;AACV,WAAO,CAAC,EAAE,KAAK,YAAY,CAAC;AAAA,EAC9B;AAAA,EAEA,WAAW,EAAE,eAAe,GAAG;AAC7B,WAAO;AAAA,MACL;AAAA,UACA,6BAAgB,KAAK,QAAQ,gBAAgB,gBAAgB;AAAA,QAC3D,OAAO;AAAA,QACP,kBAAkB;AAAA,MACpB,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,WAAO;AAAA,MACL,IAAI,oBAAO;AAAA,QACT,KAAK;AAAA,QACL,OAAO;AAAA,UACL,MAAM,MAAM;AACV,gBAAI,KAAK,QAAQ,aAAa,OAAO,aAAa,aAAa;AAC7D,8CAAe,aAAa,KAAK,QAAQ,aAAa,OAAO;AAC7D,oBAAM,YACJ,KAAK,QAAQ,eAAe,YAAY,oBAAoB;AAC9D,kBAAI,WAAW;AACb,gDAAe,WAAW,KAAK,QAAQ,aAAa,aAAa;AAAA,cACnE;AACA,kBAAI,KAAK,QAAQ,gBAAgB;AAC/B,sBAAM,WAAW;AACjB,oBAAI,UAAU;AACZ,kDAAe,UAAU,KAAK,QAAQ,aAAa,aAAa;AAAA,gBAClE;AAAA,cACF;AAAA,YACF;AACA,mBAAO,CAAC;AAAA,UACV;AAAA,UACA,OAAO,CAAC,KAAK,gBAAuC;AAAA,QACtD;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
package/dist/index.mjs CHANGED
@@ -14,6 +14,42 @@ var slideStyles = `
14
14
  margin-bottom: var(--slide-margin-bottom);
15
15
  }
16
16
  `;
17
+ var fixedSizeStyles = `
18
+ .slide[data-size="16x9"] { width: 1920px; height: 1080px; }
19
+ .slide[data-size="4x3"] { width: 1600px; height: 1200px; }
20
+ .slide[data-size="a4-portrait"] { width: 210mm; height: 297mm; }
21
+ .slide[data-size="a4-landscape"] { width: 297mm; height: 210mm; }
22
+ .slide[data-size="letter-portrait"] { width: 8.5in; height: 11in; }
23
+ .slide[data-size="letter-landscape"] { width: 11in; height: 8.5in; }
24
+ .slide[data-size="linkedin-banner"] { width: 1584px; height: 396px; }
25
+ `.trim();
26
+ var dynamicSizeStyles = `
27
+ .slide[data-size="16x9"] { width: 100%; height: auto; aspect-ratio: 16 / 9; }
28
+ .slide[data-size="4x3"] { width: 100%; height: auto; aspect-ratio: 4 / 3; }
29
+ .slide[data-size="a4-portrait"] { width: 100%; height: auto; aspect-ratio: 210 / 297; }
30
+ .slide[data-size="a4-landscape"] { width: 100%; height: auto; aspect-ratio: 297 / 210; }
31
+ .slide[data-size="letter-portrait"] { width: 100%; height: auto; aspect-ratio: 8.5 / 11; }
32
+ .slide[data-size="letter-landscape"] { width: 100%; height: auto; aspect-ratio: 11 / 8.5; }
33
+ .slide[data-size="linkedin-banner"] { width: 100%; height: auto; aspect-ratio: 1584 / 396; }
34
+ `.trim();
35
+ var printSizeStyles = `
36
+ @media print {
37
+ .slide[data-size="a4-portrait"] { width: 210mm; height: 297mm; }
38
+ @page { size: A4 portrait; margin: 0; }
39
+ }
40
+ @media print {
41
+ .slide[data-size="a4-landscape"] { width: 297mm; height: 210mm; }
42
+ @page { size: A4 landscape; margin: 0; }
43
+ }
44
+ @media print {
45
+ .slide[data-size="letter-portrait"] { width: 8.5in; height: 11in; }
46
+ @page { size: Letter portrait; margin: 0; }
47
+ }
48
+ @media print {
49
+ .slide[data-size="letter-landscape"] { width: 11in; height: 8.5in; }
50
+ @page { size: Letter landscape; margin: 0; }
51
+ }
52
+ `.trim();
17
53
  var SlidePluginKey = new PluginKey("slide");
18
54
  var Slide = Node.create({
19
55
  name: "slide",
@@ -25,9 +61,32 @@ var Slide = Node.create({
25
61
  return {
26
62
  HTMLAttributes: {},
27
63
  injectCSS: true,
64
+ renderMode: "fixed",
65
+ defaultSize: "16x9",
66
+ injectPrintCSS: true,
28
67
  injectNonce: void 0
29
68
  };
30
69
  },
70
+ addAttributes() {
71
+ return {
72
+ size: {
73
+ default: this.options.defaultSize,
74
+ parseHTML: (element) => element.getAttribute("data-size") || this.options.defaultSize,
75
+ renderHTML: (attributes) => {
76
+ if (!attributes.size) {
77
+ return { "data-size": this.options.defaultSize };
78
+ }
79
+ return { "data-size": attributes.size };
80
+ }
81
+ },
82
+ className: {
83
+ default: ""
84
+ },
85
+ id: {
86
+ default: null
87
+ }
88
+ };
89
+ },
31
90
  parseHTML() {
32
91
  return [{ tag: "div.slide" }];
33
92
  },
@@ -49,6 +108,16 @@ var Slide = Node.create({
49
108
  init: () => {
50
109
  if (this.options.injectCSS && typeof document !== "undefined") {
51
110
  createStyleTag(slideStyles, this.options.injectNonce, "slide");
111
+ const sizingCss = this.options.renderMode === "dynamic" ? dynamicSizeStyles : fixedSizeStyles;
112
+ if (sizingCss) {
113
+ createStyleTag(sizingCss, this.options.injectNonce, "slide-sizes");
114
+ }
115
+ if (this.options.injectPrintCSS) {
116
+ const printCss = printSizeStyles;
117
+ if (printCss) {
118
+ createStyleTag(printCss, this.options.injectNonce, "slide-print");
119
+ }
120
+ }
52
121
  }
53
122
  return {};
54
123
  },
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/slide.ts"],"sourcesContent":["import { Node, mergeAttributes, createStyleTag } from \"@blockslides/core\";\nimport { Plugin, PluginKey } from \"@blockslides/pm/state\";\n\nconst slideStyles = `\n.slide {\n height: var(--slide-height, 100%);\n min-height: var(--slide-min-height, 250px);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n background-color: var(--slide-bg);\n border-radius: var(--slide-border-radius);\n box-shadow: var(--slide-shadow);\n margin-bottom: var(--slide-margin-bottom);\n}\n`;\n\nexport interface SlideOptions {\n /**\n * The HTML attributes for a slide node.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record<string, any>;\n /**\n * Whether to inject CSS styles\n * @default true\n */\n injectCSS: boolean;\n /**\n * Content Security Policy nonce\n */\n injectNonce?: string;\n}\n\nconst SlidePluginKey = new PluginKey(\"slide\");\n\nexport const Slide = Node.create<SlideOptions>({\n name: \"slide\",\n isolating: true,\n content: \"row+\",\n\n group: \"slide\",\n\n defining: true,\n\n addOptions() {\n return {\n HTMLAttributes: {},\n injectCSS: true,\n injectNonce: undefined,\n };\n },\n\n parseHTML() {\n return [{ tag: \"div.slide\" }];\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\n \"div\",\n mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {\n class: \"slide\",\n \"data-node-type\": \"slide\",\n }),\n 0,\n ];\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: SlidePluginKey,\n state: {\n init: () => {\n if (this.options.injectCSS && typeof document !== \"undefined\") {\n createStyleTag(slideStyles, this.options.injectNonce, \"slide\");\n }\n return {};\n },\n apply: (_tr, pluginState: Record<string, never>) => pluginState,\n },\n }),\n ];\n },\n});\n"],"mappings":";AAAA,SAAS,MAAM,iBAAiB,sBAAsB;AACtD,SAAS,QAAQ,iBAAiB;AAElC,IAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgCpB,IAAM,iBAAiB,IAAI,UAAU,OAAO;AAErC,IAAM,QAAQ,KAAK,OAAqB;AAAA,EAC7C,MAAM;AAAA,EACN,WAAW;AAAA,EACX,SAAS;AAAA,EAET,OAAO;AAAA,EAEP,UAAU;AAAA,EAEV,aAAa;AACX,WAAO;AAAA,MACL,gBAAgB,CAAC;AAAA,MACjB,WAAW;AAAA,MACX,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EAEA,YAAY;AACV,WAAO,CAAC,EAAE,KAAK,YAAY,CAAC;AAAA,EAC9B;AAAA,EAEA,WAAW,EAAE,eAAe,GAAG;AAC7B,WAAO;AAAA,MACL;AAAA,MACA,gBAAgB,KAAK,QAAQ,gBAAgB,gBAAgB;AAAA,QAC3D,OAAO;AAAA,QACP,kBAAkB;AAAA,MACpB,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,WAAO;AAAA,MACL,IAAI,OAAO;AAAA,QACT,KAAK;AAAA,QACL,OAAO;AAAA,UACL,MAAM,MAAM;AACV,gBAAI,KAAK,QAAQ,aAAa,OAAO,aAAa,aAAa;AAC7D,6BAAe,aAAa,KAAK,QAAQ,aAAa,OAAO;AAAA,YAC/D;AACA,mBAAO,CAAC;AAAA,UACV;AAAA,UACA,OAAO,CAAC,KAAK,gBAAuC;AAAA,QACtD;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/slide.ts"],"sourcesContent":["import { Node, mergeAttributes, createStyleTag } from \"@blockslides/core\";\nimport { Plugin, PluginKey } from \"@blockslides/pm/state\";\n\nconst slideStyles = `\n.slide {\n height: var(--slide-height, 100%);\n min-height: var(--slide-min-height, 250px);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n background-color: var(--slide-bg);\n border-radius: var(--slide-border-radius);\n box-shadow: var(--slide-shadow);\n margin-bottom: var(--slide-margin-bottom);\n}\n`;\n\nconst fixedSizeStyles = `\n.slide[data-size=\"16x9\"] { width: 1920px; height: 1080px; }\n.slide[data-size=\"4x3\"] { width: 1600px; height: 1200px; }\n.slide[data-size=\"a4-portrait\"] { width: 210mm; height: 297mm; }\n.slide[data-size=\"a4-landscape\"] { width: 297mm; height: 210mm; }\n.slide[data-size=\"letter-portrait\"] { width: 8.5in; height: 11in; }\n.slide[data-size=\"letter-landscape\"] { width: 11in; height: 8.5in; }\n.slide[data-size=\"linkedin-banner\"] { width: 1584px; height: 396px; }\n`.trim();\n\nconst dynamicSizeStyles = `\n.slide[data-size=\"16x9\"] { width: 100%; height: auto; aspect-ratio: 16 / 9; }\n.slide[data-size=\"4x3\"] { width: 100%; height: auto; aspect-ratio: 4 / 3; }\n.slide[data-size=\"a4-portrait\"] { width: 100%; height: auto; aspect-ratio: 210 / 297; }\n.slide[data-size=\"a4-landscape\"] { width: 100%; height: auto; aspect-ratio: 297 / 210; }\n.slide[data-size=\"letter-portrait\"] { width: 100%; height: auto; aspect-ratio: 8.5 / 11; }\n.slide[data-size=\"letter-landscape\"] { width: 100%; height: auto; aspect-ratio: 11 / 8.5; }\n.slide[data-size=\"linkedin-banner\"] { width: 100%; height: auto; aspect-ratio: 1584 / 396; }\n`.trim();\n\nconst printSizeStyles = `\n@media print {\n .slide[data-size=\"a4-portrait\"] { width: 210mm; height: 297mm; }\n @page { size: A4 portrait; margin: 0; }\n}\n@media print {\n .slide[data-size=\"a4-landscape\"] { width: 297mm; height: 210mm; }\n @page { size: A4 landscape; margin: 0; }\n}\n@media print {\n .slide[data-size=\"letter-portrait\"] { width: 8.5in; height: 11in; }\n @page { size: Letter portrait; margin: 0; }\n}\n@media print {\n .slide[data-size=\"letter-landscape\"] { width: 11in; height: 8.5in; }\n @page { size: Letter landscape; margin: 0; }\n}\n`.trim();\n\nexport interface SlideOptions {\n /**\n * The HTML attributes for a slide node.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record<string, any>;\n /**\n * Whether to inject CSS styles\n * @default true\n */\n injectCSS: boolean;\n /**\n * Render mode for sizing\n * - fixed: width/height set from size registry (mm/in/px)\n * - dynamic: width:100% with preserved aspect ratio\n * @default 'fixed'\n */\n renderMode: \"fixed\" | \"dynamic\";\n /**\n * Default size applied when attrs.size is absent\n * @default '16x9'\n */\n defaultSize: \"16x9\" | \"4x3\" | \"a4-portrait\" | \"a4-landscape\" | \"letter-portrait\" | \"letter-landscape\" | \"linkedin-banner\";\n /**\n * Inject @media print/@page CSS for paper sizes\n * @default true\n */\n injectPrintCSS: boolean;\n /**\n * Content Security Policy nonce\n */\n injectNonce?: string;\n}\n\nconst SlidePluginKey = new PluginKey(\"slide\");\n\nexport const Slide = Node.create<SlideOptions>({\n name: \"slide\",\n isolating: true,\n content: \"row+\",\n\n group: \"slide\",\n\n defining: true,\n\n addOptions() {\n return {\n HTMLAttributes: {},\n injectCSS: true,\n renderMode: \"fixed\",\n defaultSize: \"16x9\",\n injectPrintCSS: true,\n injectNonce: undefined,\n };\n },\n\n addAttributes() {\n return {\n size: {\n default: this.options.defaultSize,\n parseHTML: (element) => element.getAttribute(\"data-size\") || this.options.defaultSize,\n renderHTML: (attributes) => {\n if (!attributes.size) {\n return { \"data-size\": this.options.defaultSize };\n }\n return { \"data-size\": attributes.size };\n },\n },\n className: {\n default: \"\",\n },\n id: {\n default: null,\n },\n };\n },\n\n parseHTML() {\n return [{ tag: \"div.slide\" }];\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\n \"div\",\n mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {\n class: \"slide\",\n \"data-node-type\": \"slide\",\n }),\n 0,\n ];\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: SlidePluginKey,\n state: {\n init: () => {\n if (this.options.injectCSS && typeof document !== \"undefined\") {\n createStyleTag(slideStyles, this.options.injectNonce, \"slide\");\n const sizingCss =\n this.options.renderMode === \"dynamic\" ? dynamicSizeStyles : fixedSizeStyles;\n if (sizingCss) {\n createStyleTag(sizingCss, this.options.injectNonce, \"slide-sizes\");\n }\n if (this.options.injectPrintCSS) {\n const printCss = printSizeStyles;\n if (printCss) {\n createStyleTag(printCss, this.options.injectNonce, \"slide-print\");\n }\n }\n }\n return {};\n },\n apply: (_tr, pluginState: Record<string, never>) => pluginState,\n },\n }),\n ];\n },\n});\n"],"mappings":";AAAA,SAAS,MAAM,iBAAiB,sBAAsB;AACtD,SAAS,QAAQ,iBAAiB;AAElC,IAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcpB,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQtB,KAAK;AAEP,IAAM,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQxB,KAAK;AAEP,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBtB,KAAK;AAqCP,IAAM,iBAAiB,IAAI,UAAU,OAAO;AAErC,IAAM,QAAQ,KAAK,OAAqB;AAAA,EAC7C,MAAM;AAAA,EACN,WAAW;AAAA,EACX,SAAS;AAAA,EAET,OAAO;AAAA,EAEP,UAAU;AAAA,EAEV,aAAa;AACX,WAAO;AAAA,MACL,gBAAgB,CAAC;AAAA,MACjB,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EAEA,gBAAgB;AACd,WAAO;AAAA,MACL,MAAM;AAAA,QACJ,SAAS,KAAK,QAAQ;AAAA,QACtB,WAAW,CAAC,YAAY,QAAQ,aAAa,WAAW,KAAK,KAAK,QAAQ;AAAA,QAC1E,YAAY,CAAC,eAAe;AAC1B,cAAI,CAAC,WAAW,MAAM;AACpB,mBAAO,EAAE,aAAa,KAAK,QAAQ,YAAY;AAAA,UACjD;AACA,iBAAO,EAAE,aAAa,WAAW,KAAK;AAAA,QACxC;AAAA,MACF;AAAA,MACA,WAAW;AAAA,QACT,SAAS;AAAA,MACX;AAAA,MACA,IAAI;AAAA,QACF,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EAEA,YAAY;AACV,WAAO,CAAC,EAAE,KAAK,YAAY,CAAC;AAAA,EAC9B;AAAA,EAEA,WAAW,EAAE,eAAe,GAAG;AAC7B,WAAO;AAAA,MACL;AAAA,MACA,gBAAgB,KAAK,QAAQ,gBAAgB,gBAAgB;AAAA,QAC3D,OAAO;AAAA,QACP,kBAAkB;AAAA,MACpB,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,WAAO;AAAA,MACL,IAAI,OAAO;AAAA,QACT,KAAK;AAAA,QACL,OAAO;AAAA,UACL,MAAM,MAAM;AACV,gBAAI,KAAK,QAAQ,aAAa,OAAO,aAAa,aAAa;AAC7D,6BAAe,aAAa,KAAK,QAAQ,aAAa,OAAO;AAC7D,oBAAM,YACJ,KAAK,QAAQ,eAAe,YAAY,oBAAoB;AAC9D,kBAAI,WAAW;AACb,+BAAe,WAAW,KAAK,QAAQ,aAAa,aAAa;AAAA,cACnE;AACA,kBAAI,KAAK,QAAQ,gBAAgB;AAC/B,sBAAM,WAAW;AACjB,oBAAI,UAAU;AACZ,iCAAe,UAAU,KAAK,QAAQ,aAAa,aAAa;AAAA,gBAClE;AAAA,cACF;AAAA,YACF;AACA,mBAAO,CAAC;AAAA,UACV;AAAA,UACA,OAAO,CAAC,KAAK,gBAAuC;AAAA,QACtD;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockslides/extension-slide",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Slide node extension for BlockSlides slide editor",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
package/src/index.ts CHANGED
@@ -1 +1,2 @@
1
1
  export { Slide } from "./slide";
2
+ export type { SlideOptions } from "./slide";
package/src/slide.ts CHANGED
@@ -15,6 +15,45 @@ const slideStyles = `
15
15
  }
16
16
  `;
17
17
 
18
+ const fixedSizeStyles = `
19
+ .slide[data-size="16x9"] { width: 1920px; height: 1080px; }
20
+ .slide[data-size="4x3"] { width: 1600px; height: 1200px; }
21
+ .slide[data-size="a4-portrait"] { width: 210mm; height: 297mm; }
22
+ .slide[data-size="a4-landscape"] { width: 297mm; height: 210mm; }
23
+ .slide[data-size="letter-portrait"] { width: 8.5in; height: 11in; }
24
+ .slide[data-size="letter-landscape"] { width: 11in; height: 8.5in; }
25
+ .slide[data-size="linkedin-banner"] { width: 1584px; height: 396px; }
26
+ `.trim();
27
+
28
+ const dynamicSizeStyles = `
29
+ .slide[data-size="16x9"] { width: 100%; height: auto; aspect-ratio: 16 / 9; }
30
+ .slide[data-size="4x3"] { width: 100%; height: auto; aspect-ratio: 4 / 3; }
31
+ .slide[data-size="a4-portrait"] { width: 100%; height: auto; aspect-ratio: 210 / 297; }
32
+ .slide[data-size="a4-landscape"] { width: 100%; height: auto; aspect-ratio: 297 / 210; }
33
+ .slide[data-size="letter-portrait"] { width: 100%; height: auto; aspect-ratio: 8.5 / 11; }
34
+ .slide[data-size="letter-landscape"] { width: 100%; height: auto; aspect-ratio: 11 / 8.5; }
35
+ .slide[data-size="linkedin-banner"] { width: 100%; height: auto; aspect-ratio: 1584 / 396; }
36
+ `.trim();
37
+
38
+ const printSizeStyles = `
39
+ @media print {
40
+ .slide[data-size="a4-portrait"] { width: 210mm; height: 297mm; }
41
+ @page { size: A4 portrait; margin: 0; }
42
+ }
43
+ @media print {
44
+ .slide[data-size="a4-landscape"] { width: 297mm; height: 210mm; }
45
+ @page { size: A4 landscape; margin: 0; }
46
+ }
47
+ @media print {
48
+ .slide[data-size="letter-portrait"] { width: 8.5in; height: 11in; }
49
+ @page { size: Letter portrait; margin: 0; }
50
+ }
51
+ @media print {
52
+ .slide[data-size="letter-landscape"] { width: 11in; height: 8.5in; }
53
+ @page { size: Letter landscape; margin: 0; }
54
+ }
55
+ `.trim();
56
+
18
57
  export interface SlideOptions {
19
58
  /**
20
59
  * The HTML attributes for a slide node.
@@ -27,6 +66,23 @@ export interface SlideOptions {
27
66
  * @default true
28
67
  */
29
68
  injectCSS: boolean;
69
+ /**
70
+ * Render mode for sizing
71
+ * - fixed: width/height set from size registry (mm/in/px)
72
+ * - dynamic: width:100% with preserved aspect ratio
73
+ * @default 'fixed'
74
+ */
75
+ renderMode: "fixed" | "dynamic";
76
+ /**
77
+ * Default size applied when attrs.size is absent
78
+ * @default '16x9'
79
+ */
80
+ defaultSize: "16x9" | "4x3" | "a4-portrait" | "a4-landscape" | "letter-portrait" | "letter-landscape" | "linkedin-banner";
81
+ /**
82
+ * Inject @media print/@page CSS for paper sizes
83
+ * @default true
84
+ */
85
+ injectPrintCSS: boolean;
30
86
  /**
31
87
  * Content Security Policy nonce
32
88
  */
@@ -48,10 +104,34 @@ export const Slide = Node.create<SlideOptions>({
48
104
  return {
49
105
  HTMLAttributes: {},
50
106
  injectCSS: true,
107
+ renderMode: "fixed",
108
+ defaultSize: "16x9",
109
+ injectPrintCSS: true,
51
110
  injectNonce: undefined,
52
111
  };
53
112
  },
54
113
 
114
+ addAttributes() {
115
+ return {
116
+ size: {
117
+ default: this.options.defaultSize,
118
+ parseHTML: (element) => element.getAttribute("data-size") || this.options.defaultSize,
119
+ renderHTML: (attributes) => {
120
+ if (!attributes.size) {
121
+ return { "data-size": this.options.defaultSize };
122
+ }
123
+ return { "data-size": attributes.size };
124
+ },
125
+ },
126
+ className: {
127
+ default: "",
128
+ },
129
+ id: {
130
+ default: null,
131
+ },
132
+ };
133
+ },
134
+
55
135
  parseHTML() {
56
136
  return [{ tag: "div.slide" }];
57
137
  },
@@ -75,6 +155,17 @@ export const Slide = Node.create<SlideOptions>({
75
155
  init: () => {
76
156
  if (this.options.injectCSS && typeof document !== "undefined") {
77
157
  createStyleTag(slideStyles, this.options.injectNonce, "slide");
158
+ const sizingCss =
159
+ this.options.renderMode === "dynamic" ? dynamicSizeStyles : fixedSizeStyles;
160
+ if (sizingCss) {
161
+ createStyleTag(sizingCss, this.options.injectNonce, "slide-sizes");
162
+ }
163
+ if (this.options.injectPrintCSS) {
164
+ const printCss = printSizeStyles;
165
+ if (printCss) {
166
+ createStyleTag(printCss, this.options.injectNonce, "slide-print");
167
+ }
168
+ }
78
169
  }
79
170
  return {};
80
171
  },