@editora/print 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,254 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=()=>`
2
+ * {
3
+ margin: 0;
4
+ padding: 0;
5
+ box-sizing: border-box;
6
+ }
7
+
8
+ html, body {
9
+ background: white;
10
+ color: black;
11
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
12
+ line-height: 1.5;
13
+ }
14
+
15
+ .rte-print {
16
+ background: white;
17
+ color: black;
18
+ }
19
+
20
+ /* Page break handling */
21
+ .rte-page-break {
22
+ page-break-after: always;
23
+ display: block;
24
+ height: 0;
25
+ margin: 0;
26
+ border: none;
27
+ background: none;
28
+ }
29
+
30
+ .rte-page-break::before {
31
+ display: none;
32
+ }
33
+
34
+ /* Code block formatting */
35
+ .rte-code-block,
36
+ pre {
37
+ background: #f5f5f5;
38
+ border: 1px solid #ddd;
39
+ border-radius: 4px;
40
+ padding: 12px;
41
+ margin: 12px 0;
42
+ overflow-x: auto;
43
+ page-break-inside: avoid;
44
+ }
45
+
46
+ .rte-code-block code,
47
+ pre code {
48
+ font-family: 'Courier New', Courier, monospace;
49
+ font-size: 12px;
50
+ line-height: 1.4;
51
+ white-space: pre-wrap;
52
+ word-break: break-word;
53
+ }
54
+
55
+ /* Footnotes */
56
+ .rte-footnotes {
57
+ border-top: 1px solid #ccc;
58
+ margin-top: 40px;
59
+ padding-top: 12px;
60
+ page-break-inside: avoid;
61
+ }
62
+
63
+ .rte-footnotes ol {
64
+ margin-left: 20px;
65
+ }
66
+
67
+ .rte-footnotes li {
68
+ margin: 8px 0;
69
+ font-size: 0.9em;
70
+ }
71
+
72
+ .rte-footnote-ref {
73
+ vertical-align: super;
74
+ font-size: 0.8em;
75
+ }
76
+
77
+ .rte-footnote-backref {
78
+ margin-left: 4px;
79
+ text-decoration: none;
80
+ color: #666;
81
+ }
82
+
83
+ /* Anchors - preserve IDs but hide visual markers */
84
+ .rte-anchor {
85
+ display: none;
86
+ }
87
+
88
+ /* Lists and tables */
89
+ ul, ol {
90
+ margin: 12px 0;
91
+ padding-left: 40px;
92
+ }
93
+
94
+ li {
95
+ margin: 4px 0;
96
+ }
97
+
98
+ table {
99
+ border-collapse: collapse;
100
+ width: 100%;
101
+ margin: 12px 0;
102
+ page-break-inside: avoid;
103
+ }
104
+
105
+ th, td {
106
+ border: 1px solid #ddd;
107
+ padding: 8px;
108
+ text-align: left;
109
+ }
110
+
111
+ th {
112
+ background: #f5f5f5;
113
+ font-weight: bold;
114
+ }
115
+
116
+ /* Heading hierarchy */
117
+ h1 {
118
+ font-size: 2em;
119
+ margin: 20px 0 12px;
120
+ page-break-after: avoid;
121
+ }
122
+
123
+ h2 {
124
+ font-size: 1.5em;
125
+ margin: 16px 0 10px;
126
+ page-break-after: avoid;
127
+ }
128
+
129
+ h3 {
130
+ font-size: 1.25em;
131
+ margin: 14px 0 8px;
132
+ page-break-after: avoid;
133
+ }
134
+
135
+ h4 {
136
+ font-size: 1.1em;
137
+ margin: 12px 0 6px;
138
+ page-break-after: avoid;
139
+ }
140
+
141
+ h5 {
142
+ font-size: 1em;
143
+ margin: 12px 0 6px;
144
+ page-break-after: avoid;
145
+ }
146
+
147
+ h6 {
148
+ font-size: 0.9em;
149
+ margin: 12px 0 6px;
150
+ page-break-after: avoid;
151
+ }
152
+
153
+ p {
154
+ margin: 8px 0;
155
+ }
156
+
157
+ /* Emphasis and strong */
158
+ strong, b {
159
+ font-weight: bold;
160
+ }
161
+
162
+ em, i {
163
+ font-style: italic;
164
+ }
165
+
166
+ u {
167
+ text-decoration: underline;
168
+ }
169
+
170
+ /* Block elements */
171
+ blockquote {
172
+ border-left: 4px solid #ddd;
173
+ margin: 12px 0;
174
+ padding-left: 16px;
175
+ color: #666;
176
+ }
177
+
178
+ hr {
179
+ border: none;
180
+ border-top: 1px solid #ddd;
181
+ margin: 16px 0;
182
+ page-break-after: avoid;
183
+ }
184
+
185
+ /* Images */
186
+ img {
187
+ max-width: 100%;
188
+ height: auto;
189
+ page-break-inside: avoid;
190
+ }
191
+
192
+ /* Links */
193
+ a {
194
+ color: #0066cc;
195
+ text-decoration: underline;
196
+ }
197
+
198
+ /* Merge tags */
199
+ .rte-merge-tag {
200
+ background-color: #e3f2fd;
201
+ border: 1px solid #bbdefb;
202
+ border-radius: 3px;
203
+ padding: 2px 6px;
204
+ margin: 0 2px;
205
+ display: inline-block;
206
+ white-space: nowrap;
207
+ font-weight: 500;
208
+ color: #1976d2;
209
+ font-size: 0.9em;
210
+ }
211
+
212
+ /* Hide selection */
213
+ ::selection {
214
+ background: transparent;
215
+ }
216
+
217
+ /* Print-specific rules */
218
+ @media print {
219
+ body {
220
+ margin: 0;
221
+ padding: 0;
222
+ }
223
+
224
+ .rte-page-break {
225
+ page-break-after: always;
226
+ }
227
+
228
+ h1, h2, h3, h4, h5, h6 {
229
+ page-break-after: avoid;
230
+ page-break-inside: avoid;
231
+ }
232
+
233
+ table, figure, img, pre {
234
+ page-break-inside: avoid;
235
+ }
236
+
237
+ ul, ol, blockquote {
238
+ page-break-inside: avoid;
239
+ }
240
+ }
241
+ `,l=()=>{if(typeof window>"u")return!1;const a=(()=>{const i=window.getSelection();if(i&&i.rangeCount>0){let t=i.getRangeAt(0).startContainer;for(;t&&t!==document.body;){if(t.nodeType===Node.ELEMENT_NODE){const d=t;if(d.getAttribute("contenteditable")==="true")return d}t=t.parentNode}}const n=document.activeElement;if(n){if(n.getAttribute("contenteditable")==="true")return n;const t=n.closest('[contenteditable="true"]');if(t)return t}return document.querySelector('[contenteditable="true"]')})();if(!a)return console.warn("Editor content not found"),!1;const c=a.cloneNode(!0),r=document.createElement("article");r.className="rte-document rte-print",r.appendChild(c);const p=`
242
+ <!DOCTYPE html>
243
+ <html>
244
+ <head>
245
+ <meta charset="UTF-8">
246
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
247
+ <title>Print Document</title>
248
+ <style>${s()}</style>
249
+ </head>
250
+ <body>
251
+ ${r.outerHTML}
252
+ </body>
253
+ </html>
254
+ `,e=document.createElement("iframe");e.style.position="absolute",e.style.left="-9999px",e.style.top="-9999px",e.style.width="0",e.style.height="0",document.body.appendChild(e);const o=e.contentDocument||e.contentWindow?.document;return o?(o.open(),o.write(p),o.close(),setTimeout(()=>{e.contentWindow&&(e.contentWindow.print(),setTimeout(()=>{document.body.removeChild(e)},100))},250),!0):(console.error("Could not access print frame document"),document.body.removeChild(e),!1)},g=()=>({name:"print",toolbar:[{label:"Print",command:"print",icon:'<svg width="24" height="24" viewBox="0 0 24 24" fill="none" focusable="false" aria-hidden="true"><path d="M7 9V4h10v5M6 18h12v-4H6v4Zm0 0v2h12v-2M6 9H5a2 2 0 0 0-2 2v3h3m12-5h1a2 2 0 0 1 2 2v3h-3" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>',shortcut:"Mod-p"}],commands:{print:l},keymap:{"Mod-p":()=>(l(),!0)}});exports.PrintPlugin=g;
@@ -0,0 +1,309 @@
1
+ const s = () => `
2
+ * {
3
+ margin: 0;
4
+ padding: 0;
5
+ box-sizing: border-box;
6
+ }
7
+
8
+ html, body {
9
+ background: white;
10
+ color: black;
11
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
12
+ line-height: 1.5;
13
+ }
14
+
15
+ .rte-print {
16
+ background: white;
17
+ color: black;
18
+ }
19
+
20
+ /* Page break handling */
21
+ .rte-page-break {
22
+ page-break-after: always;
23
+ display: block;
24
+ height: 0;
25
+ margin: 0;
26
+ border: none;
27
+ background: none;
28
+ }
29
+
30
+ .rte-page-break::before {
31
+ display: none;
32
+ }
33
+
34
+ /* Code block formatting */
35
+ .rte-code-block,
36
+ pre {
37
+ background: #f5f5f5;
38
+ border: 1px solid #ddd;
39
+ border-radius: 4px;
40
+ padding: 12px;
41
+ margin: 12px 0;
42
+ overflow-x: auto;
43
+ page-break-inside: avoid;
44
+ }
45
+
46
+ .rte-code-block code,
47
+ pre code {
48
+ font-family: 'Courier New', Courier, monospace;
49
+ font-size: 12px;
50
+ line-height: 1.4;
51
+ white-space: pre-wrap;
52
+ word-break: break-word;
53
+ }
54
+
55
+ /* Footnotes */
56
+ .rte-footnotes {
57
+ border-top: 1px solid #ccc;
58
+ margin-top: 40px;
59
+ padding-top: 12px;
60
+ page-break-inside: avoid;
61
+ }
62
+
63
+ .rte-footnotes ol {
64
+ margin-left: 20px;
65
+ }
66
+
67
+ .rte-footnotes li {
68
+ margin: 8px 0;
69
+ font-size: 0.9em;
70
+ }
71
+
72
+ .rte-footnote-ref {
73
+ vertical-align: super;
74
+ font-size: 0.8em;
75
+ }
76
+
77
+ .rte-footnote-backref {
78
+ margin-left: 4px;
79
+ text-decoration: none;
80
+ color: #666;
81
+ }
82
+
83
+ /* Anchors - preserve IDs but hide visual markers */
84
+ .rte-anchor {
85
+ display: none;
86
+ }
87
+
88
+ /* Lists and tables */
89
+ ul, ol {
90
+ margin: 12px 0;
91
+ padding-left: 40px;
92
+ }
93
+
94
+ li {
95
+ margin: 4px 0;
96
+ }
97
+
98
+ table {
99
+ border-collapse: collapse;
100
+ width: 100%;
101
+ margin: 12px 0;
102
+ page-break-inside: avoid;
103
+ }
104
+
105
+ th, td {
106
+ border: 1px solid #ddd;
107
+ padding: 8px;
108
+ text-align: left;
109
+ }
110
+
111
+ th {
112
+ background: #f5f5f5;
113
+ font-weight: bold;
114
+ }
115
+
116
+ /* Heading hierarchy */
117
+ h1 {
118
+ font-size: 2em;
119
+ margin: 20px 0 12px;
120
+ page-break-after: avoid;
121
+ }
122
+
123
+ h2 {
124
+ font-size: 1.5em;
125
+ margin: 16px 0 10px;
126
+ page-break-after: avoid;
127
+ }
128
+
129
+ h3 {
130
+ font-size: 1.25em;
131
+ margin: 14px 0 8px;
132
+ page-break-after: avoid;
133
+ }
134
+
135
+ h4 {
136
+ font-size: 1.1em;
137
+ margin: 12px 0 6px;
138
+ page-break-after: avoid;
139
+ }
140
+
141
+ h5 {
142
+ font-size: 1em;
143
+ margin: 12px 0 6px;
144
+ page-break-after: avoid;
145
+ }
146
+
147
+ h6 {
148
+ font-size: 0.9em;
149
+ margin: 12px 0 6px;
150
+ page-break-after: avoid;
151
+ }
152
+
153
+ p {
154
+ margin: 8px 0;
155
+ }
156
+
157
+ /* Emphasis and strong */
158
+ strong, b {
159
+ font-weight: bold;
160
+ }
161
+
162
+ em, i {
163
+ font-style: italic;
164
+ }
165
+
166
+ u {
167
+ text-decoration: underline;
168
+ }
169
+
170
+ /* Block elements */
171
+ blockquote {
172
+ border-left: 4px solid #ddd;
173
+ margin: 12px 0;
174
+ padding-left: 16px;
175
+ color: #666;
176
+ }
177
+
178
+ hr {
179
+ border: none;
180
+ border-top: 1px solid #ddd;
181
+ margin: 16px 0;
182
+ page-break-after: avoid;
183
+ }
184
+
185
+ /* Images */
186
+ img {
187
+ max-width: 100%;
188
+ height: auto;
189
+ page-break-inside: avoid;
190
+ }
191
+
192
+ /* Links */
193
+ a {
194
+ color: #0066cc;
195
+ text-decoration: underline;
196
+ }
197
+
198
+ /* Merge tags */
199
+ .rte-merge-tag {
200
+ background-color: #e3f2fd;
201
+ border: 1px solid #bbdefb;
202
+ border-radius: 3px;
203
+ padding: 2px 6px;
204
+ margin: 0 2px;
205
+ display: inline-block;
206
+ white-space: nowrap;
207
+ font-weight: 500;
208
+ color: #1976d2;
209
+ font-size: 0.9em;
210
+ }
211
+
212
+ /* Hide selection */
213
+ ::selection {
214
+ background: transparent;
215
+ }
216
+
217
+ /* Print-specific rules */
218
+ @media print {
219
+ body {
220
+ margin: 0;
221
+ padding: 0;
222
+ }
223
+
224
+ .rte-page-break {
225
+ page-break-after: always;
226
+ }
227
+
228
+ h1, h2, h3, h4, h5, h6 {
229
+ page-break-after: avoid;
230
+ page-break-inside: avoid;
231
+ }
232
+
233
+ table, figure, img, pre {
234
+ page-break-inside: avoid;
235
+ }
236
+
237
+ ul, ol, blockquote {
238
+ page-break-inside: avoid;
239
+ }
240
+ }
241
+ `, l = () => {
242
+ if (typeof window > "u") return !1;
243
+ const a = (() => {
244
+ const i = window.getSelection();
245
+ if (i && i.rangeCount > 0) {
246
+ let t = i.getRangeAt(0).startContainer;
247
+ for (; t && t !== document.body; ) {
248
+ if (t.nodeType === Node.ELEMENT_NODE) {
249
+ const d = t;
250
+ if (d.getAttribute("contenteditable") === "true")
251
+ return d;
252
+ }
253
+ t = t.parentNode;
254
+ }
255
+ }
256
+ const n = document.activeElement;
257
+ if (n) {
258
+ if (n.getAttribute("contenteditable") === "true")
259
+ return n;
260
+ const t = n.closest('[contenteditable="true"]');
261
+ if (t) return t;
262
+ }
263
+ return document.querySelector('[contenteditable="true"]');
264
+ })();
265
+ if (!a)
266
+ return console.warn("Editor content not found"), !1;
267
+ const p = a.cloneNode(!0), r = document.createElement("article");
268
+ r.className = "rte-document rte-print", r.appendChild(p);
269
+ const c = `
270
+ <!DOCTYPE html>
271
+ <html>
272
+ <head>
273
+ <meta charset="UTF-8">
274
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
275
+ <title>Print Document</title>
276
+ <style>${s()}</style>
277
+ </head>
278
+ <body>
279
+ ${r.outerHTML}
280
+ </body>
281
+ </html>
282
+ `, e = document.createElement("iframe");
283
+ e.style.position = "absolute", e.style.left = "-9999px", e.style.top = "-9999px", e.style.width = "0", e.style.height = "0", document.body.appendChild(e);
284
+ const o = e.contentDocument || e.contentWindow?.document;
285
+ return o ? (o.open(), o.write(c), o.close(), setTimeout(() => {
286
+ e.contentWindow && (e.contentWindow.print(), setTimeout(() => {
287
+ document.body.removeChild(e);
288
+ }, 100));
289
+ }, 250), !0) : (console.error("Could not access print frame document"), document.body.removeChild(e), !1);
290
+ }, m = () => ({
291
+ name: "print",
292
+ toolbar: [
293
+ {
294
+ label: "Print",
295
+ command: "print",
296
+ icon: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" focusable="false" aria-hidden="true"><path d="M7 9V4h10v5M6 18h12v-4H6v4Zm0 0v2h12v-2M6 9H5a2 2 0 0 0-2 2v3h3m12-5h1a2 2 0 0 1 2 2v3h-3" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>',
297
+ shortcut: "Mod-p"
298
+ }
299
+ ],
300
+ commands: {
301
+ print: l
302
+ },
303
+ keymap: {
304
+ "Mod-p": () => (l(), !0)
305
+ }
306
+ });
307
+ export {
308
+ m as PrintPlugin
309
+ };
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@editora/print",
3
+ "version": "1.0.1",
4
+ "description": "Print plugin for Rich Text Editor - Clone content to print-safe DOM",
5
+ "main": "dist/index.cjs.js",
6
+ "module": "dist/index.esm.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "src"
11
+ ],
12
+ "scripts": {
13
+ "build": "vite build"
14
+ },
15
+ "keywords": [
16
+ "editor",
17
+ "print",
18
+ "page-break",
19
+ "document"
20
+ ],
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/ajaykr089/Editora.git"
24
+ },
25
+ "author": "",
26
+ "license": "MIT",
27
+ "dependencies": {
28
+ "@editora/core": "^1.0.5"
29
+ },
30
+ "devDependencies": {
31
+ "react": "^18.2.0",
32
+ "react-dom": "^18.2.0"
33
+ }
34
+ }
@@ -0,0 +1,377 @@
1
+ import { Plugin } from '@editora/core';
2
+
3
+ /**
4
+ * Print Plugin - Native Implementation
5
+ *
6
+ * Features:
7
+ * - Print-safe DOM cloning (removes editor UI)
8
+ * - Theme normalization (light theme for printing)
9
+ * - CSS integration with page breaks, footnotes, code blocks
10
+ * - iOS Safari print iframe support
11
+ * - Comprehensive print styles for all elements
12
+ */
13
+
14
+ const getPrintStyles = (): string => {
15
+ return `
16
+ * {
17
+ margin: 0;
18
+ padding: 0;
19
+ box-sizing: border-box;
20
+ }
21
+
22
+ html, body {
23
+ background: white;
24
+ color: black;
25
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
26
+ line-height: 1.5;
27
+ }
28
+
29
+ .rte-print {
30
+ background: white;
31
+ color: black;
32
+ }
33
+
34
+ /* Page break handling */
35
+ .rte-page-break {
36
+ page-break-after: always;
37
+ display: block;
38
+ height: 0;
39
+ margin: 0;
40
+ border: none;
41
+ background: none;
42
+ }
43
+
44
+ .rte-page-break::before {
45
+ display: none;
46
+ }
47
+
48
+ /* Code block formatting */
49
+ .rte-code-block,
50
+ pre {
51
+ background: #f5f5f5;
52
+ border: 1px solid #ddd;
53
+ border-radius: 4px;
54
+ padding: 12px;
55
+ margin: 12px 0;
56
+ overflow-x: auto;
57
+ page-break-inside: avoid;
58
+ }
59
+
60
+ .rte-code-block code,
61
+ pre code {
62
+ font-family: 'Courier New', Courier, monospace;
63
+ font-size: 12px;
64
+ line-height: 1.4;
65
+ white-space: pre-wrap;
66
+ word-break: break-word;
67
+ }
68
+
69
+ /* Footnotes */
70
+ .rte-footnotes {
71
+ border-top: 1px solid #ccc;
72
+ margin-top: 40px;
73
+ padding-top: 12px;
74
+ page-break-inside: avoid;
75
+ }
76
+
77
+ .rte-footnotes ol {
78
+ margin-left: 20px;
79
+ }
80
+
81
+ .rte-footnotes li {
82
+ margin: 8px 0;
83
+ font-size: 0.9em;
84
+ }
85
+
86
+ .rte-footnote-ref {
87
+ vertical-align: super;
88
+ font-size: 0.8em;
89
+ }
90
+
91
+ .rte-footnote-backref {
92
+ margin-left: 4px;
93
+ text-decoration: none;
94
+ color: #666;
95
+ }
96
+
97
+ /* Anchors - preserve IDs but hide visual markers */
98
+ .rte-anchor {
99
+ display: none;
100
+ }
101
+
102
+ /* Lists and tables */
103
+ ul, ol {
104
+ margin: 12px 0;
105
+ padding-left: 40px;
106
+ }
107
+
108
+ li {
109
+ margin: 4px 0;
110
+ }
111
+
112
+ table {
113
+ border-collapse: collapse;
114
+ width: 100%;
115
+ margin: 12px 0;
116
+ page-break-inside: avoid;
117
+ }
118
+
119
+ th, td {
120
+ border: 1px solid #ddd;
121
+ padding: 8px;
122
+ text-align: left;
123
+ }
124
+
125
+ th {
126
+ background: #f5f5f5;
127
+ font-weight: bold;
128
+ }
129
+
130
+ /* Heading hierarchy */
131
+ h1 {
132
+ font-size: 2em;
133
+ margin: 20px 0 12px;
134
+ page-break-after: avoid;
135
+ }
136
+
137
+ h2 {
138
+ font-size: 1.5em;
139
+ margin: 16px 0 10px;
140
+ page-break-after: avoid;
141
+ }
142
+
143
+ h3 {
144
+ font-size: 1.25em;
145
+ margin: 14px 0 8px;
146
+ page-break-after: avoid;
147
+ }
148
+
149
+ h4 {
150
+ font-size: 1.1em;
151
+ margin: 12px 0 6px;
152
+ page-break-after: avoid;
153
+ }
154
+
155
+ h5 {
156
+ font-size: 1em;
157
+ margin: 12px 0 6px;
158
+ page-break-after: avoid;
159
+ }
160
+
161
+ h6 {
162
+ font-size: 0.9em;
163
+ margin: 12px 0 6px;
164
+ page-break-after: avoid;
165
+ }
166
+
167
+ p {
168
+ margin: 8px 0;
169
+ }
170
+
171
+ /* Emphasis and strong */
172
+ strong, b {
173
+ font-weight: bold;
174
+ }
175
+
176
+ em, i {
177
+ font-style: italic;
178
+ }
179
+
180
+ u {
181
+ text-decoration: underline;
182
+ }
183
+
184
+ /* Block elements */
185
+ blockquote {
186
+ border-left: 4px solid #ddd;
187
+ margin: 12px 0;
188
+ padding-left: 16px;
189
+ color: #666;
190
+ }
191
+
192
+ hr {
193
+ border: none;
194
+ border-top: 1px solid #ddd;
195
+ margin: 16px 0;
196
+ page-break-after: avoid;
197
+ }
198
+
199
+ /* Images */
200
+ img {
201
+ max-width: 100%;
202
+ height: auto;
203
+ page-break-inside: avoid;
204
+ }
205
+
206
+ /* Links */
207
+ a {
208
+ color: #0066cc;
209
+ text-decoration: underline;
210
+ }
211
+
212
+ /* Merge tags */
213
+ .rte-merge-tag {
214
+ background-color: #e3f2fd;
215
+ border: 1px solid #bbdefb;
216
+ border-radius: 3px;
217
+ padding: 2px 6px;
218
+ margin: 0 2px;
219
+ display: inline-block;
220
+ white-space: nowrap;
221
+ font-weight: 500;
222
+ color: #1976d2;
223
+ font-size: 0.9em;
224
+ }
225
+
226
+ /* Hide selection */
227
+ ::selection {
228
+ background: transparent;
229
+ }
230
+
231
+ /* Print-specific rules */
232
+ @media print {
233
+ body {
234
+ margin: 0;
235
+ padding: 0;
236
+ }
237
+
238
+ .rte-page-break {
239
+ page-break-after: always;
240
+ }
241
+
242
+ h1, h2, h3, h4, h5, h6 {
243
+ page-break-after: avoid;
244
+ page-break-inside: avoid;
245
+ }
246
+
247
+ table, figure, img, pre {
248
+ page-break-inside: avoid;
249
+ }
250
+
251
+ ul, ol, blockquote {
252
+ page-break-inside: avoid;
253
+ }
254
+ }
255
+ `;
256
+ };
257
+
258
+ const printDocument = () => {
259
+ if (typeof window === 'undefined') return false;
260
+
261
+ // Find the active editor based on current selection or focus
262
+ const findActiveEditor = (): HTMLElement | null => {
263
+ const selection = window.getSelection();
264
+ if (selection && selection.rangeCount > 0) {
265
+ let node: Node | null = selection.getRangeAt(0).startContainer;
266
+ while (node && node !== document.body) {
267
+ if (node.nodeType === Node.ELEMENT_NODE) {
268
+ const element = node as HTMLElement;
269
+ if (element.getAttribute('contenteditable') === 'true') {
270
+ return element;
271
+ }
272
+ }
273
+ node = node.parentNode;
274
+ }
275
+ }
276
+
277
+ const activeElement = document.activeElement;
278
+ if (activeElement) {
279
+ if (activeElement.getAttribute('contenteditable') === 'true') {
280
+ return activeElement as HTMLElement;
281
+ }
282
+ const editor = activeElement.closest('[contenteditable="true"]');
283
+ if (editor) return editor as HTMLElement;
284
+ }
285
+
286
+ return document.querySelector('[contenteditable="true"]');
287
+ };
288
+
289
+ const contentElement = findActiveEditor();
290
+ if (!contentElement) {
291
+ console.warn('Editor content not found');
292
+ return false;
293
+ }
294
+
295
+ // Clone only the content
296
+ const contentClone = contentElement.cloneNode(true) as HTMLElement;
297
+
298
+ // Create print document structure
299
+ const article = document.createElement('article');
300
+ article.className = 'rte-document rte-print';
301
+ article.appendChild(contentClone);
302
+
303
+ // Build complete HTML for print
304
+ const printHTML = `
305
+ <!DOCTYPE html>
306
+ <html>
307
+ <head>
308
+ <meta charset="UTF-8">
309
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
310
+ <title>Print Document</title>
311
+ <style>${getPrintStyles()}</style>
312
+ </head>
313
+ <body>
314
+ ${article.outerHTML}
315
+ </body>
316
+ </html>
317
+ `;
318
+
319
+ // Create an iframe for printing
320
+ const printFrame = document.createElement('iframe');
321
+ printFrame.style.position = 'absolute';
322
+ printFrame.style.left = '-9999px';
323
+ printFrame.style.top = '-9999px';
324
+ printFrame.style.width = '0';
325
+ printFrame.style.height = '0';
326
+ document.body.appendChild(printFrame);
327
+
328
+ // Write content to iframe
329
+ const frameDoc = printFrame.contentDocument || printFrame.contentWindow?.document;
330
+ if (!frameDoc) {
331
+ console.error('Could not access print frame document');
332
+ document.body.removeChild(printFrame);
333
+ return false;
334
+ }
335
+
336
+ frameDoc.open();
337
+ frameDoc.write(printHTML);
338
+ frameDoc.close();
339
+
340
+ // Wait for content to load, then print
341
+ setTimeout(() => {
342
+ if (printFrame.contentWindow) {
343
+ printFrame.contentWindow.print();
344
+
345
+ // Clean up after print
346
+ setTimeout(() => {
347
+ document.body.removeChild(printFrame);
348
+ }, 100);
349
+ }
350
+ }, 250);
351
+
352
+ return true;
353
+ };
354
+
355
+ export const PrintPlugin = (): Plugin => ({
356
+ name: "print",
357
+
358
+ toolbar: [
359
+ {
360
+ label: "Print",
361
+ command: "print",
362
+ icon: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" focusable="false" aria-hidden="true"><path d="M7 9V4h10v5M6 18h12v-4H6v4Zm0 0v2h12v-2M6 9H5a2 2 0 0 0-2 2v3h3m12-5h1a2 2 0 0 1 2 2v3h-3" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>',
363
+ shortcut: "Mod-p",
364
+ },
365
+ ],
366
+
367
+ commands: {
368
+ print: printDocument,
369
+ },
370
+
371
+ keymap: {
372
+ "Mod-p": () => {
373
+ printDocument();
374
+ return true;
375
+ },
376
+ },
377
+ });
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { PrintPlugin } from './PrintPlugin.native';