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.
Files changed (95) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.rubocop.yml +8 -0
  4. data/.yardopts +11 -0
  5. data/CLAUDE.md +141 -0
  6. data/README.md +409 -0
  7. data/Rakefile +19 -0
  8. data/app/assets/javascripts/inkpen/controllers/editor_controller.js +2050 -0
  9. data/app/assets/javascripts/inkpen/controllers/sticky_toolbar_controller.js +667 -0
  10. data/app/assets/javascripts/inkpen/controllers/toolbar_controller.js +693 -0
  11. data/app/assets/javascripts/inkpen/export/html.js +637 -0
  12. data/app/assets/javascripts/inkpen/export/index.js +30 -0
  13. data/app/assets/javascripts/inkpen/export/markdown.js +697 -0
  14. data/app/assets/javascripts/inkpen/export/pdf.js +372 -0
  15. data/app/assets/javascripts/inkpen/extensions/advanced_table.js +640 -0
  16. data/app/assets/javascripts/inkpen/extensions/block_commands.js +300 -0
  17. data/app/assets/javascripts/inkpen/extensions/block_gutter.js +338 -0
  18. data/app/assets/javascripts/inkpen/extensions/callout.js +303 -0
  19. data/app/assets/javascripts/inkpen/extensions/columns.js +403 -0
  20. data/app/assets/javascripts/inkpen/extensions/database.js +990 -0
  21. data/app/assets/javascripts/inkpen/extensions/document_section.js +352 -0
  22. data/app/assets/javascripts/inkpen/extensions/drag_handle.js +407 -0
  23. data/app/assets/javascripts/inkpen/extensions/embed.js +629 -0
  24. data/app/assets/javascripts/inkpen/extensions/enhanced_image.js +566 -0
  25. data/app/assets/javascripts/inkpen/extensions/export_commands.js +271 -0
  26. data/app/assets/javascripts/inkpen/extensions/file_attachment.js +593 -0
  27. data/app/assets/javascripts/inkpen/extensions/inkpen_table/index.js +58 -0
  28. data/app/assets/javascripts/inkpen/extensions/inkpen_table/inkpen_table.js +638 -0
  29. data/app/assets/javascripts/inkpen/extensions/inkpen_table/inkpen_table_cell.js +100 -0
  30. data/app/assets/javascripts/inkpen/extensions/inkpen_table/inkpen_table_header.js +100 -0
  31. data/app/assets/javascripts/inkpen/extensions/inkpen_table/table_constants.js +152 -0
  32. data/app/assets/javascripts/inkpen/extensions/inkpen_table/table_helpers.js +254 -0
  33. data/app/assets/javascripts/inkpen/extensions/inkpen_table/table_menu.js +282 -0
  34. data/app/assets/javascripts/inkpen/extensions/preformatted.js +239 -0
  35. data/app/assets/javascripts/inkpen/extensions/section.js +281 -0
  36. data/app/assets/javascripts/inkpen/extensions/section_title.js +126 -0
  37. data/app/assets/javascripts/inkpen/extensions/slash_commands.js +439 -0
  38. data/app/assets/javascripts/inkpen/extensions/table_of_contents.js +474 -0
  39. data/app/assets/javascripts/inkpen/extensions/toggle_block.js +332 -0
  40. data/app/assets/javascripts/inkpen/index.js +87 -0
  41. data/app/assets/stylesheets/inkpen/advanced_table.css +514 -0
  42. data/app/assets/stylesheets/inkpen/animations.css +626 -0
  43. data/app/assets/stylesheets/inkpen/block_gutter.css +265 -0
  44. data/app/assets/stylesheets/inkpen/callout.css +359 -0
  45. data/app/assets/stylesheets/inkpen/columns.css +314 -0
  46. data/app/assets/stylesheets/inkpen/database.css +658 -0
  47. data/app/assets/stylesheets/inkpen/document_section.css +305 -0
  48. data/app/assets/stylesheets/inkpen/drag_drop.css +220 -0
  49. data/app/assets/stylesheets/inkpen/editor.css +652 -0
  50. data/app/assets/stylesheets/inkpen/embed.css +468 -0
  51. data/app/assets/stylesheets/inkpen/enhanced_image.css +453 -0
  52. data/app/assets/stylesheets/inkpen/export.css +499 -0
  53. data/app/assets/stylesheets/inkpen/file_attachment.css +347 -0
  54. data/app/assets/stylesheets/inkpen/footnotes.css +136 -0
  55. data/app/assets/stylesheets/inkpen/inkpen_table.css +608 -0
  56. data/app/assets/stylesheets/inkpen/preformatted.css +215 -0
  57. data/app/assets/stylesheets/inkpen/search_replace.css +58 -0
  58. data/app/assets/stylesheets/inkpen/section.css +236 -0
  59. data/app/assets/stylesheets/inkpen/slash_menu.css +252 -0
  60. data/app/assets/stylesheets/inkpen/sticky_toolbar.css +314 -0
  61. data/app/assets/stylesheets/inkpen/toc.css +386 -0
  62. data/app/assets/stylesheets/inkpen/toggle.css +260 -0
  63. data/app/helpers/inkpen/editor_helper.rb +114 -0
  64. data/app/views/inkpen/_editor.html.erb +139 -0
  65. data/config/importmap.rb +170 -0
  66. data/docs/.DS_Store +0 -0
  67. data/docs/CHANGELOG.md +571 -0
  68. data/docs/FEATURES.md +436 -0
  69. data/docs/ROADMAP.md +3029 -0
  70. data/docs/VISION.md +235 -0
  71. data/docs/extensions/INKPEN_TABLE.md +482 -0
  72. data/docs/thinking/CORRECTED_NO_VUE.md +756 -0
  73. data/docs/thinking/EXECUTIVE_SUMMARY.md +403 -0
  74. data/docs/thinking/INKPEN_CODE_SAMPLES.md +1479 -0
  75. data/docs/thinking/INKPEN_MASTER_GUIDE.md +891 -0
  76. data/docs/thinking/README_START_HERE.md +341 -0
  77. data/lib/inkpen/configuration.rb +175 -0
  78. data/lib/inkpen/editor.rb +204 -0
  79. data/lib/inkpen/engine.rb +32 -0
  80. data/lib/inkpen/extensions/base.rb +109 -0
  81. data/lib/inkpen/extensions/code_block_syntax.rb +177 -0
  82. data/lib/inkpen/extensions/document_section.rb +111 -0
  83. data/lib/inkpen/extensions/forced_document.rb +183 -0
  84. data/lib/inkpen/extensions/mention.rb +155 -0
  85. data/lib/inkpen/extensions/preformatted.rb +111 -0
  86. data/lib/inkpen/extensions/section.rb +139 -0
  87. data/lib/inkpen/extensions/slash_commands.rb +100 -0
  88. data/lib/inkpen/extensions/table.rb +182 -0
  89. data/lib/inkpen/extensions/task_list.rb +145 -0
  90. data/lib/inkpen/sticky_toolbar.rb +157 -0
  91. data/lib/inkpen/toolbar.rb +145 -0
  92. data/lib/inkpen/version.rb +5 -0
  93. data/lib/inkpen.rb +101 -0
  94. data/sig/inkpen.rbs +4 -0
  95. 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
+ "&": "&amp;",
303
+ "<": "&lt;",
304
+ ">": "&gt;",
305
+ '"': "&quot;",
306
+ "'": "&#039;"
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
+ }