inkpen 0.7.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.
- checksums.yaml +7 -0
- data/.DS_Store +0 -0
- data/.rubocop.yml +8 -0
- data/.yardopts +11 -0
- data/CLAUDE.md +141 -0
- data/README.md +409 -0
- data/Rakefile +19 -0
- data/app/assets/javascripts/inkpen/controllers/editor_controller.js +2050 -0
- data/app/assets/javascripts/inkpen/controllers/sticky_toolbar_controller.js +667 -0
- data/app/assets/javascripts/inkpen/controllers/toolbar_controller.js +693 -0
- data/app/assets/javascripts/inkpen/export/html.js +637 -0
- data/app/assets/javascripts/inkpen/export/index.js +30 -0
- data/app/assets/javascripts/inkpen/export/markdown.js +697 -0
- data/app/assets/javascripts/inkpen/export/pdf.js +372 -0
- data/app/assets/javascripts/inkpen/extensions/advanced_table.js +640 -0
- data/app/assets/javascripts/inkpen/extensions/block_commands.js +300 -0
- data/app/assets/javascripts/inkpen/extensions/block_gutter.js +338 -0
- data/app/assets/javascripts/inkpen/extensions/callout.js +303 -0
- data/app/assets/javascripts/inkpen/extensions/columns.js +403 -0
- data/app/assets/javascripts/inkpen/extensions/database.js +990 -0
- data/app/assets/javascripts/inkpen/extensions/document_section.js +352 -0
- data/app/assets/javascripts/inkpen/extensions/drag_handle.js +407 -0
- data/app/assets/javascripts/inkpen/extensions/embed.js +629 -0
- data/app/assets/javascripts/inkpen/extensions/enhanced_image.js +566 -0
- data/app/assets/javascripts/inkpen/extensions/export_commands.js +271 -0
- data/app/assets/javascripts/inkpen/extensions/file_attachment.js +593 -0
- data/app/assets/javascripts/inkpen/extensions/inkpen_table/index.js +58 -0
- data/app/assets/javascripts/inkpen/extensions/inkpen_table/inkpen_table.js +638 -0
- data/app/assets/javascripts/inkpen/extensions/inkpen_table/inkpen_table_cell.js +100 -0
- data/app/assets/javascripts/inkpen/extensions/inkpen_table/inkpen_table_header.js +100 -0
- data/app/assets/javascripts/inkpen/extensions/inkpen_table/table_constants.js +152 -0
- data/app/assets/javascripts/inkpen/extensions/inkpen_table/table_helpers.js +254 -0
- data/app/assets/javascripts/inkpen/extensions/inkpen_table/table_menu.js +282 -0
- data/app/assets/javascripts/inkpen/extensions/preformatted.js +239 -0
- data/app/assets/javascripts/inkpen/extensions/section.js +281 -0
- data/app/assets/javascripts/inkpen/extensions/section_title.js +126 -0
- data/app/assets/javascripts/inkpen/extensions/slash_commands.js +439 -0
- data/app/assets/javascripts/inkpen/extensions/table_of_contents.js +474 -0
- data/app/assets/javascripts/inkpen/extensions/toggle_block.js +332 -0
- data/app/assets/javascripts/inkpen/index.js +87 -0
- data/app/assets/stylesheets/inkpen/advanced_table.css +514 -0
- data/app/assets/stylesheets/inkpen/animations.css +626 -0
- data/app/assets/stylesheets/inkpen/block_gutter.css +265 -0
- data/app/assets/stylesheets/inkpen/callout.css +359 -0
- data/app/assets/stylesheets/inkpen/columns.css +314 -0
- data/app/assets/stylesheets/inkpen/database.css +658 -0
- data/app/assets/stylesheets/inkpen/document_section.css +305 -0
- data/app/assets/stylesheets/inkpen/drag_drop.css +220 -0
- data/app/assets/stylesheets/inkpen/editor.css +652 -0
- data/app/assets/stylesheets/inkpen/embed.css +468 -0
- data/app/assets/stylesheets/inkpen/enhanced_image.css +453 -0
- data/app/assets/stylesheets/inkpen/export.css +499 -0
- data/app/assets/stylesheets/inkpen/file_attachment.css +347 -0
- data/app/assets/stylesheets/inkpen/footnotes.css +136 -0
- data/app/assets/stylesheets/inkpen/inkpen_table.css +608 -0
- data/app/assets/stylesheets/inkpen/preformatted.css +215 -0
- data/app/assets/stylesheets/inkpen/search_replace.css +58 -0
- data/app/assets/stylesheets/inkpen/section.css +236 -0
- data/app/assets/stylesheets/inkpen/slash_menu.css +252 -0
- data/app/assets/stylesheets/inkpen/sticky_toolbar.css +314 -0
- data/app/assets/stylesheets/inkpen/toc.css +386 -0
- data/app/assets/stylesheets/inkpen/toggle.css +260 -0
- data/app/helpers/inkpen/editor_helper.rb +114 -0
- data/app/views/inkpen/_editor.html.erb +139 -0
- data/config/importmap.rb +170 -0
- data/docs/.DS_Store +0 -0
- data/docs/CHANGELOG.md +571 -0
- data/docs/FEATURES.md +436 -0
- data/docs/ROADMAP.md +3029 -0
- data/docs/VISION.md +235 -0
- data/docs/extensions/INKPEN_TABLE.md +482 -0
- data/docs/thinking/CORRECTED_NO_VUE.md +756 -0
- data/docs/thinking/EXECUTIVE_SUMMARY.md +403 -0
- data/docs/thinking/INKPEN_CODE_SAMPLES.md +1479 -0
- data/docs/thinking/INKPEN_MASTER_GUIDE.md +891 -0
- data/docs/thinking/README_START_HERE.md +341 -0
- data/lib/inkpen/configuration.rb +175 -0
- data/lib/inkpen/editor.rb +204 -0
- data/lib/inkpen/engine.rb +32 -0
- data/lib/inkpen/extensions/base.rb +109 -0
- data/lib/inkpen/extensions/code_block_syntax.rb +177 -0
- data/lib/inkpen/extensions/document_section.rb +111 -0
- data/lib/inkpen/extensions/forced_document.rb +183 -0
- data/lib/inkpen/extensions/mention.rb +155 -0
- data/lib/inkpen/extensions/preformatted.rb +111 -0
- data/lib/inkpen/extensions/section.rb +139 -0
- data/lib/inkpen/extensions/slash_commands.rb +100 -0
- data/lib/inkpen/extensions/table.rb +182 -0
- data/lib/inkpen/extensions/task_list.rb +145 -0
- data/lib/inkpen/sticky_toolbar.rb +157 -0
- data/lib/inkpen/toolbar.rb +145 -0
- data/lib/inkpen/version.rb +5 -0
- data/lib/inkpen.rb +101 -0
- data/sig/inkpen.rbs +4 -0
- metadata +165 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF Export
|
|
3
|
+
*
|
|
4
|
+
* Generates PDF documents from TipTap editor content.
|
|
5
|
+
* Uses html2pdf.js if available, otherwise falls back to print dialog.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { exportToHTML, getExportStylesheet } from "inkpen/export/html"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Check if html2pdf.js is available
|
|
12
|
+
*/
|
|
13
|
+
function hasHtml2Pdf() {
|
|
14
|
+
return typeof window !== "undefined" && typeof window.html2pdf !== "undefined"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Export editor content to PDF
|
|
19
|
+
*
|
|
20
|
+
* @param {Editor} editor - TipTap editor instance
|
|
21
|
+
* @param {Object} options - Export options
|
|
22
|
+
* @returns {Promise<void>}
|
|
23
|
+
*/
|
|
24
|
+
export async function exportToPDF(editor, options = {}) {
|
|
25
|
+
const {
|
|
26
|
+
filename = "document.pdf",
|
|
27
|
+
pageSize = "a4",
|
|
28
|
+
orientation = "portrait",
|
|
29
|
+
margins = { top: 20, right: 20, bottom: 20, left: 20 },
|
|
30
|
+
includeHeader = false,
|
|
31
|
+
includeFooter = true,
|
|
32
|
+
footerTemplate = null,
|
|
33
|
+
title = "Document",
|
|
34
|
+
author = null,
|
|
35
|
+
subject = null,
|
|
36
|
+
quality = 2
|
|
37
|
+
} = options
|
|
38
|
+
|
|
39
|
+
// Generate styled HTML content
|
|
40
|
+
const html = exportToHTML(editor, {
|
|
41
|
+
includeStyles: true,
|
|
42
|
+
inlineStyles: true,
|
|
43
|
+
includeWrapper: false,
|
|
44
|
+
title
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// Try html2pdf.js first
|
|
48
|
+
if (hasHtml2Pdf()) {
|
|
49
|
+
await generateWithHtml2Pdf(html, {
|
|
50
|
+
filename,
|
|
51
|
+
pageSize,
|
|
52
|
+
orientation,
|
|
53
|
+
margins,
|
|
54
|
+
includeFooter,
|
|
55
|
+
footerTemplate,
|
|
56
|
+
title,
|
|
57
|
+
author,
|
|
58
|
+
subject,
|
|
59
|
+
quality
|
|
60
|
+
})
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Fallback to print dialog
|
|
65
|
+
printToPDF(html, {
|
|
66
|
+
filename,
|
|
67
|
+
pageSize,
|
|
68
|
+
orientation,
|
|
69
|
+
margins,
|
|
70
|
+
includeFooter,
|
|
71
|
+
footerTemplate,
|
|
72
|
+
title
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Generate PDF using html2pdf.js library
|
|
78
|
+
*/
|
|
79
|
+
async function generateWithHtml2Pdf(html, options) {
|
|
80
|
+
const {
|
|
81
|
+
filename,
|
|
82
|
+
pageSize,
|
|
83
|
+
orientation,
|
|
84
|
+
margins,
|
|
85
|
+
includeFooter,
|
|
86
|
+
footerTemplate,
|
|
87
|
+
title,
|
|
88
|
+
author,
|
|
89
|
+
subject,
|
|
90
|
+
quality
|
|
91
|
+
} = options
|
|
92
|
+
|
|
93
|
+
// Create temporary container
|
|
94
|
+
const container = document.createElement("div")
|
|
95
|
+
container.innerHTML = `
|
|
96
|
+
<div class="inkpen-pdf-content">
|
|
97
|
+
${html}
|
|
98
|
+
</div>
|
|
99
|
+
`
|
|
100
|
+
container.style.cssText = `
|
|
101
|
+
position: absolute;
|
|
102
|
+
left: -9999px;
|
|
103
|
+
width: ${pageSize === "letter" ? "8.5in" : "210mm"};
|
|
104
|
+
padding: ${margins.top}mm ${margins.right}mm ${margins.bottom}mm ${margins.left}mm;
|
|
105
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
106
|
+
font-size: 12pt;
|
|
107
|
+
line-height: 1.6;
|
|
108
|
+
color: #000;
|
|
109
|
+
background: #fff;
|
|
110
|
+
`
|
|
111
|
+
|
|
112
|
+
// Add styles
|
|
113
|
+
const styleEl = document.createElement("style")
|
|
114
|
+
styleEl.textContent = getExportStylesheet("inkpen-")
|
|
115
|
+
container.prepend(styleEl)
|
|
116
|
+
|
|
117
|
+
document.body.appendChild(container)
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const opt = {
|
|
121
|
+
margin: [margins.top, margins.right, margins.bottom, margins.left],
|
|
122
|
+
filename,
|
|
123
|
+
image: { type: "jpeg", quality: 0.98 },
|
|
124
|
+
html2canvas: {
|
|
125
|
+
scale: quality,
|
|
126
|
+
useCORS: true,
|
|
127
|
+
logging: false
|
|
128
|
+
},
|
|
129
|
+
jsPDF: {
|
|
130
|
+
unit: "mm",
|
|
131
|
+
format: pageSize,
|
|
132
|
+
orientation,
|
|
133
|
+
compress: true
|
|
134
|
+
},
|
|
135
|
+
pagebreak: { mode: ["avoid-all", "css", "legacy"] }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Add metadata
|
|
139
|
+
if (title || author || subject) {
|
|
140
|
+
opt.jsPDF.properties = {}
|
|
141
|
+
if (title) opt.jsPDF.properties.title = title
|
|
142
|
+
if (author) opt.jsPDF.properties.author = author
|
|
143
|
+
if (subject) opt.jsPDF.properties.subject = subject
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
await window.html2pdf().set(opt).from(container).save()
|
|
147
|
+
} finally {
|
|
148
|
+
document.body.removeChild(container)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Fallback: Open print dialog for PDF generation
|
|
154
|
+
*/
|
|
155
|
+
function printToPDF(html, options) {
|
|
156
|
+
const {
|
|
157
|
+
filename,
|
|
158
|
+
pageSize,
|
|
159
|
+
orientation,
|
|
160
|
+
margins,
|
|
161
|
+
includeFooter,
|
|
162
|
+
title
|
|
163
|
+
} = options
|
|
164
|
+
|
|
165
|
+
const printWindow = window.open("", "_blank", "width=800,height=600")
|
|
166
|
+
|
|
167
|
+
if (!printWindow) {
|
|
168
|
+
console.error("Failed to open print window. Please allow popups.")
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const styles = getExportStylesheet("inkpen-")
|
|
173
|
+
|
|
174
|
+
printWindow.document.write(`<!DOCTYPE html>
|
|
175
|
+
<html lang="en">
|
|
176
|
+
<head>
|
|
177
|
+
<meta charset="UTF-8">
|
|
178
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
179
|
+
<title>${escapeHtml(title || filename)}</title>
|
|
180
|
+
<style>
|
|
181
|
+
@page {
|
|
182
|
+
size: ${pageSize} ${orientation};
|
|
183
|
+
margin: ${margins.top}mm ${margins.right}mm ${margins.bottom}mm ${margins.left}mm;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
* {
|
|
187
|
+
box-sizing: border-box;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
html, body {
|
|
191
|
+
margin: 0;
|
|
192
|
+
padding: 0;
|
|
193
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
194
|
+
font-size: 12pt;
|
|
195
|
+
line-height: 1.6;
|
|
196
|
+
color: #000;
|
|
197
|
+
background: #fff;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.inkpen-document {
|
|
201
|
+
max-width: none;
|
|
202
|
+
padding: 0;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
${styles}
|
|
206
|
+
|
|
207
|
+
/* Print-specific overrides */
|
|
208
|
+
@media print {
|
|
209
|
+
body {
|
|
210
|
+
-webkit-print-color-adjust: exact;
|
|
211
|
+
print-color-adjust: exact;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.inkpen-callout,
|
|
215
|
+
.inkpen-toc,
|
|
216
|
+
.inkpen-database,
|
|
217
|
+
blockquote {
|
|
218
|
+
-webkit-print-color-adjust: exact;
|
|
219
|
+
print-color-adjust: exact;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
pre {
|
|
223
|
+
white-space: pre-wrap;
|
|
224
|
+
word-wrap: break-word;
|
|
225
|
+
overflow: visible;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
a {
|
|
229
|
+
text-decoration: none;
|
|
230
|
+
color: inherit;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
a[href^="http"]::after {
|
|
234
|
+
content: " (" attr(href) ")";
|
|
235
|
+
font-size: 0.8em;
|
|
236
|
+
color: #666;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
img {
|
|
240
|
+
max-width: 100%;
|
|
241
|
+
page-break-inside: avoid;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
h1, h2, h3, h4, h5, h6 {
|
|
245
|
+
page-break-after: avoid;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
table, figure, pre {
|
|
249
|
+
page-break-inside: avoid;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.no-print {
|
|
253
|
+
display: none !important;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/* Footer with page numbers */
|
|
258
|
+
${includeFooter ? `
|
|
259
|
+
@page {
|
|
260
|
+
@bottom-center {
|
|
261
|
+
content: counter(page) " of " counter(pages);
|
|
262
|
+
font-size: 10pt;
|
|
263
|
+
color: #666;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
` : ""}
|
|
267
|
+
</style>
|
|
268
|
+
</head>
|
|
269
|
+
<body>
|
|
270
|
+
<article class="inkpen-document">
|
|
271
|
+
${html}
|
|
272
|
+
</article>
|
|
273
|
+
<script>
|
|
274
|
+
window.onload = function() {
|
|
275
|
+
// Short delay to ensure styles are applied
|
|
276
|
+
setTimeout(function() {
|
|
277
|
+
window.print();
|
|
278
|
+
// Close after print dialog closes
|
|
279
|
+
window.onafterprint = function() {
|
|
280
|
+
window.close();
|
|
281
|
+
};
|
|
282
|
+
// Fallback close after timeout
|
|
283
|
+
setTimeout(function() {
|
|
284
|
+
if (!window.closed) {
|
|
285
|
+
window.close();
|
|
286
|
+
}
|
|
287
|
+
}, 1000);
|
|
288
|
+
}, 250);
|
|
289
|
+
};
|
|
290
|
+
</script>
|
|
291
|
+
</body>
|
|
292
|
+
</html>`)
|
|
293
|
+
|
|
294
|
+
printWindow.document.close()
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Escape HTML entities
|
|
299
|
+
*/
|
|
300
|
+
function escapeHtml(text) {
|
|
301
|
+
const map = {
|
|
302
|
+
"&": "&",
|
|
303
|
+
"<": "<",
|
|
304
|
+
">": ">",
|
|
305
|
+
'"': """,
|
|
306
|
+
"'": "'"
|
|
307
|
+
}
|
|
308
|
+
return String(text).replace(/[&<>"']/g, char => map[char])
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Load html2pdf.js dynamically if needed
|
|
313
|
+
*
|
|
314
|
+
* @returns {Promise<boolean>} Whether the library was loaded successfully
|
|
315
|
+
*/
|
|
316
|
+
export async function loadHtml2Pdf() {
|
|
317
|
+
if (hasHtml2Pdf()) {
|
|
318
|
+
return true
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
const script = document.createElement("script")
|
|
323
|
+
script.src = "https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"
|
|
324
|
+
script.integrity = "sha512-GsLlZN/3F2ErC5ifS5QtgpiJtWd43JWSuIgh7mbzZ8zBps+dvLusV+eNQATqgA/HdeKFVgA5v3S/cIrLF7QnIg=="
|
|
325
|
+
script.crossOrigin = "anonymous"
|
|
326
|
+
script.referrerPolicy = "no-referrer"
|
|
327
|
+
|
|
328
|
+
await new Promise((resolve, reject) => {
|
|
329
|
+
script.onload = resolve
|
|
330
|
+
script.onerror = reject
|
|
331
|
+
document.head.appendChild(script)
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
return true
|
|
335
|
+
} catch (err) {
|
|
336
|
+
console.warn("Failed to load html2pdf.js, will use print fallback:", err)
|
|
337
|
+
return false
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Check if PDF export is available (with library support)
|
|
343
|
+
*/
|
|
344
|
+
export function isPDFExportAvailable() {
|
|
345
|
+
return hasHtml2Pdf()
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Get available page sizes
|
|
350
|
+
*/
|
|
351
|
+
export function getPageSizes() {
|
|
352
|
+
return [
|
|
353
|
+
{ id: "a4", name: "A4", width: "210mm", height: "297mm" },
|
|
354
|
+
{ id: "letter", name: "Letter", width: "8.5in", height: "11in" },
|
|
355
|
+
{ id: "legal", name: "Legal", width: "8.5in", height: "14in" },
|
|
356
|
+
{ id: "a3", name: "A3", width: "297mm", height: "420mm" },
|
|
357
|
+
{ id: "a5", name: "A5", width: "148mm", height: "210mm" }
|
|
358
|
+
]
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Get default PDF options
|
|
363
|
+
*/
|
|
364
|
+
export function getDefaultPDFOptions() {
|
|
365
|
+
return {
|
|
366
|
+
pageSize: "a4",
|
|
367
|
+
orientation: "portrait",
|
|
368
|
+
margins: { top: 20, right: 20, bottom: 20, left: 20 },
|
|
369
|
+
includeFooter: true,
|
|
370
|
+
quality: 2
|
|
371
|
+
}
|
|
372
|
+
}
|