platform
platform copied to clipboard
Feat/5269
I added an implementation of a download feature for the text editor.
I tried several options before arriving at what seems to be a temporarily optimal solution.
Initially, I implemented this feature without any libraries by creating a popup window
element, adding a deep clone of the content to be printed, and then calling window.print()
from that DOM element. This method worked technically, but formatting and colors were very inconsistent. I attempted to recursively apply all styles pertaining to each DOM node within this content as inline styles so that the printer would respect them, but this approach also proved quite inconsistent. Many CSS styles from parent elements not in the cloned DOM node rendered incorrectly, making the output look awkward. CSS variables also broke.
Next, I tried a pure CSS approach using the @media print
media query, which preserved the initial styles much better. However, due to the numerous divs within the WYSIWYG editor and extensive scrolling content, this method often failed to print the entire document, cutting off some content. I also think some of the styles within the inline text editor may not appear as good in a PDF, with titles, for example, looking too large.
I also considered extracting HTML content directly from the TipTap/Prose instance but encountered a Svelte error stating Uncaught Error: <DocumentEditor>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'
. I presumed this setting was omitted for performance reasons, so I disregarded this approach. Alternatively, I would have used the getHTML()
method from TipTap to render this content into a new window
and reapply relevant styles, though cross-browser support would likely necessitate generic serif
or sans-serif
font-family
choices.
Ultimately, I adopted the current approach using the printJS
library. Technically, it employs the same method as my initial attempt but handles many edge cases. This approach has worked the best so far, although I am not fully satisfied with its cleanliness. I still needed to apply several new styles, replicating existing CSS logic that was stripped from the DOM node, such as for inline code, removing elements from the editable tables, and hiding input bars for the editable title. While this solution may be close to ideal for physical printing, it lacks the document’s included fonts since it opens in a new window, and it cannot append relevant CSS files from the document because the styles are managed in JS/SCSS by Svelte. Considering that more customers likely prefer PDFs for email or attachment purposes rather than physical prints, this solution is adequate but not ideal.
Long term, I recommend generating PDFs on the server and allowing users to download them, using a library like PDFKit to convert raw HTML saved from TipTap. This method would be much more consistent, avoid browser quirks, and allow for greater customization of fonts and user backgrounds, similar to how Notion handles PDF downloads. It may also be advisable to style the documents differently for this context, such as using serif fonts for better readability and adjusting headings and line heights.
Additional Notes:
- I added a quick download icon that was the first result from GitHub Co-Pilot. With better editing tools, I would add a bit more margin between the arrow and the bottom line.
- I accidentally committed some changes to the pnpm lock files.
- I ran the linter before committing and it changed a few lines stylistically. Either someone did not lint before, or the versions of the linter should be pinned as to be consistent