# Add 'Share' button to your Pickaxe chat that exports current thread to a PDF (advanced)

**URL:** <https://community.pickaxe.co/t/add-share-button-to-your-pickaxe-chat-that-exports-current-thread-to-a-pdf-advanced/7444>\
**Category:** Community Scripts\
**Tags:** embed\
**Created:** [October 31, 2025, 6:28pm UTC](https://community.pickaxe.co/t/add-share-button-to-your-pickaxe-chat-that-exports-current-thread-to-a-pdf-advanced/7444 "2025-10-31T18:28:17Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![carsondev](https://yyz2.discourse-cdn.com/flex004/user_avatar/community.pickaxe.co/carsondev/32/4588_2.png) [@carsondev](https://community.pickaxe.co/u/carsondev)\
**Post date:** [October 31, 2025, 6:28pm UTC](https://community.pickaxe.co/t/add-share-button-to-your-pickaxe-chat-that-exports-current-thread-to-a-pdf-advanced/7444/1 "2025-10-31T18:28:17Z")

</div>

Hi everyone!! 🎃

Here’s how you can add a Share to PDF button to your Pickaxe. This exports the visible chat thread to a PDF using custom javascript. Inspired by our dynamic-loader so it won’t clash with your page!!

To do this, you’ll:

1. Use a **script embed** (not an iframe)

2. Add one more `<script>` that loads `html2pdf.js`, mounts a floating button, and captures the chat DOM to PDF

3. Everything is **namespaced** and guarded to avoid duplicate injections

Example

Your Pickaxe script embed →

`<script src="https://studio.pickaxe.co/api/embed/bundle.js" defer></script>`

Script begins here! →

```auto
<script>
(() => {
  // prevent dupe loads if snippet appears more than once
  if (window. __PICKAXE_PDF_LOADER__ ) return;
  window. __PICKAXE_PDF_LOADER__ = true;
  // ---- config (edit these!!) ----
  const NS = 'pa-pdf';
  const CONFIG = {
    buttonText: 'Share',
    fileName: 'chat.pdf',
    // if you customized your chat DOM, update this selector
    chatSelector: '[data-pickaxe-embed] section, [data-pickaxe-embed] .chat, [data-pickaxe-embed]',
    position: { bottom: '24px', right: '24px' }
  };
  // load a script exactly once by URL
  const loadOnce = (src) => new Promise((res, rej) => {
    const id = `${NS}-script-${btoa(src)}`;
    if (document.getElementById(id)) return res();
    const s = document.createElement('script');
    s.id = id; s.src = src; s.defer = true;
    s.onload = res; s.onerror = rej;
    document.head.appendChild(s);
  });
  // wait for the Pickaxe embed to exist in the DOM
  const waitForEmbed = () => new Promise((resolve) => {
    if (document.querySelector('[data-pickaxe-embed]')) return resolve();
    const obs = new MutationObserver(() => {
      if (document.querySelector('[data-pickaxe-embed]')) { obs.disconnect(); resolve(); }
    });
    obs.observe(document.documentElement || document.body, { childList: true, subtree: true });
  });
  // find the chat container
  const getChatRoot = () => {
    const root = document.querySelector('[data-pickaxe-embed]');
    if (root) {
      const inner = root.querySelector('section, .chat, .messages, [role="log"]');
      return inner || root;
    }
    return document.querySelector(CONFIG.chatSelector) || document.body;
  };
  // build a copy w/ stripped inputs/toolbars
  const buildPrintable = (chatEl) => {
    const clone = chatEl.cloneNode(true);
    clone.querySelectorAll('textarea, input, button, [contenteditable], .composer, .toolbar, [data-action]')
      .forEach(n => n.remove());
    // optional: hide reasoning / system blocks (uncomment if you want them omitted!!)
    // clone.querySelectorAll('.reasoning, think, [data-reasoning]').forEach(n => n.remove());
    const wrap = document.createElement('div');
    wrap.style.padding = '16px';
    wrap.style.background = 'white';
    wrap.style.color = '#111';
    wrap.style.maxWidth = '920px';
    wrap.style.margin = '0 auto';
    wrap.appendChild(clone);
    return wrap;
  };
  // create the floating Share button
  const mountButton = () => {
    if (document.querySelector(`.${NS}-btn`)) return null;
    const btn = document.createElement('button');
    btn.type = 'button';
    btn.className = `${NS}-btn`;
    btn.textContent = CONFIG.buttonText;
    Object.assign(btn.style, {
      position: 'fixed',
      zIndex: '2147483646',
      bottom: CONFIG.position.bottom,
      right: CONFIG.position.right,
      padding: '10px 14px',
      borderRadius: '10px',
      border: '1px solid rgba(0,0,0,.12)',
      background: 'white',
      boxShadow: '0 8px 24px rgba(0,0,0,.15)',
      font: '500 14px/1.2 system-ui, -apple-system, Segoe UI, Roboto, sans-serif',
      cursor: 'pointer'
    });
    btn.setAttribute('aria-label', 'Share chat as PDF');
    document.body.appendChild(btn);
    return btn;
  };
  // wire everything up
  const init = async () => {
    await waitForEmbed();
    await loadOnce('https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js');
    const btn = mountButton();
    if (!btn) return;
    btn.addEventListener('click', () => {
      try {
        const chatEl = getChatRoot();
        const node = buildPrintable(chatEl);
        window.html2pdf().set({
          margin: [10,10,10,10],
          filename: CONFIG.fileName,
          image: { type: 'jpeg', quality: 0.98 },
          html2canvas: { scale: 2, useCORS: true },
          jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
        }).from(node).save();
      } catch (e) {
        console.error('[Pickaxe PDF] export failed:', e);
        alert('Sorry, there was a problem exporting your PDF.');
      }
    }, { passive: true });
  };
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init, { once: true });
  } else {
    init();
  }
})();
</script>

```

**CUSTOMIZATION**

You can tweak the **filename** , **button position** , or which elements are included/excluded in the PDF (see the comments in the code above)

**Notes**

- Use **script embed** for this (iframes isolate content and can’t be captured from the parent page).

- The snippet is **namespaced** (`pa-pdf`) and guarded with `window. __PICKAXE_PDF_LOADER__ ` so you won’t get duplicates if it’s included in both a layout and a page

- If your chat wrapper is customized, update `chatSelector` to match your DOM

If any of this is super duper confusing but you still want to try: we’ll reply below with a short glossary defining some of the logic talked about above

Happy Building and special shoutout to one of our users @leoreche for this inspiration on this one! 👷

---

<div class="post-metadata">

**Author:** ![lindsay\_support](https://yyz2.discourse-cdn.com/flex004/user_avatar/community.pickaxe.co/lindsay_support/32/4508_2.png) [@lindsay\_support](https://community.pickaxe.co/u/lindsay_support)\
**Post date:** [October 31, 2025, 6:35pm UTC](https://community.pickaxe.co/t/add-share-button-to-your-pickaxe-chat-that-exports-current-thread-to-a-pdf-advanced/7444/2 "2025-10-31T18:35:45Z")

</div>

This is so cool, thanks for sharing Carson! 🎉

---

<div class="post-metadata">

**Author:** ![carsondev](https://yyz2.discourse-cdn.com/flex004/user_avatar/community.pickaxe.co/carsondev/32/4588_2.png) [@carsondev](https://community.pickaxe.co/u/carsondev)\
**Post date:** [October 31, 2025, 6:48pm UTC](https://community.pickaxe.co/t/add-share-button-to-your-pickaxe-chat-that-exports-current-thread-to-a-pdf-advanced/7444/3 "2025-10-31T18:48:26Z")

</div>

# Quick Reference (2-min read)

## DOM (Document Object Model)

**In plain English:** Your webpage as a tree of elements that JavaScript can grab and manipulate.

**Why you care:** We find your chat container, make a copy, and feed it to the PDF library.

## CSS Selector

**In plain English:** A search pattern for finding stuff in the DOM (`#id`, `.class`, `[data-something]`).

**Why you care:** If the PDF is blank, you probably need to tweak `chatSelector` to match your theme’s chat wrapper.

## Script Embed vs. Iframe

**In plain English:** Script embeds inject directly into your page. Iframes are locked boxes your page can’t peek into.

**Why you care:**  **PDFs only work with script embeds**. We can’t see inside iframes!!

## Namespacing

**In plain English:** Adding prefixes like `pa-pdf-` to avoid stepping on other code’s toes.

**Why you care:** Keeps our button and styles from breaking your site (or vice versa).

## Duplicate-Load Guard

**In plain English:** A “has this run already?” check using something like `window. __PICKAXE_PDF_LOADER__ `.

**Why you care:** Page builders love running the same code twice. This prevents double buttons and double downloads.

## MutationObserver

**In plain English:** A browser API that yells “Hey, something changed!” when the DOM updates.

**Why you care:** Your chatbot loads async, we wait for it to appear before adding the button.

## CDN (Content Delivery Network)

**In plain English:** Fast servers around the world hosting libraries.

**Why you care:** We lazy-load `html2pdf.js` from a CDN only when you click Share, it keeps your page light.

## html2pdf.js

**In plain English:** Takes HTML, turns it into pixels, wraps it in a PDF.

**Why you care:** This is what actually generates your `chat.pdf`.

## Rasterization / html2canvas

**In plain English:** Converting your rendered page to pixels before PDFing it.

**Why you care:** Custom fonts and wild CSS might look slightly off. Try not to get too crazy and keep your chat area simple for best results!

## CSP (Content Security Policy)

**In plain English:** Your site’s bouncer that blocks sketchy scripts.

**Why you care:** If inline `<script>` tags don’t work, host the code in a `.js` file or tweak your CSP settings.

## Idempotent

**In plain English:** Run it once, run it ten times and get the same result. Like the old school definition for insanity.

**Why you care:** Our code is safe to run multiple times (SPA navigation, accidental double-pastes, etc.).

## Event Listener

**In plain English:** “When user does X, run Y.”

**Why you care:** The Share button’s click listener kicks off the PDF export.

## Deep Clone

**In plain English:** Copying a DOM chunk and all its children.

**Why you care:** We clone the chat, strip out textboxes and toolbars, then hand the clean copy to the PDF generator.

* * *

## The Mental Model

**Find the chat → clone it → clean it → PDFify it.**

Everything else (guards, observers, namespaces) just makes this stronger across different platforms.

* * *

## Quick 30-Second Test to Run Through

✅ Using **script embed** (not iframe)?

✅ See **one** Share button (not duplicates)?

✅ Click → downloads `chat.pdf` with visible messages?

❌ **Blank PDF?** → Fix your `chatSelector` to target the actual chat wrapper

🔒 **Hide internal stuff?** → Uncomment the line that strips reasoning blocks

---

<div class="post-metadata">

**Author:** ![riskacademy](https://yyz2.discourse-cdn.com/flex004/user_avatar/community.pickaxe.co/riskacademy/32/228_2.png) [@riskacademy](https://community.pickaxe.co/u/riskacademy)\
**Post date:** [November 5, 2025, 8:20pm UTC](https://community.pickaxe.co/t/add-share-button-to-your-pickaxe-chat-that-exports-current-thread-to-a-pdf-advanced/7444/4 "2025-11-05T20:20:12Z")

</div>

Will try, sounds amazing

---

<div class="post-metadata">

**Author:** ![carsondev](https://yyz2.discourse-cdn.com/flex004/user_avatar/community.pickaxe.co/carsondev/32/4588_2.png) [@carsondev](https://community.pickaxe.co/u/carsondev)\
**Post date:** [November 6, 2025, 3:07am UTC](https://community.pickaxe.co/t/add-share-button-to-your-pickaxe-chat-that-exports-current-thread-to-a-pdf-advanced/7444/5 "2025-11-06T03:07:36Z")

</div>

whenever you do get a chance to try we’re here to help if ya run into any hiccups!

---

<div class="post-metadata">

**Author:** ![Ned.Malki](https://yyz2.discourse-cdn.com/flex004/user_avatar/community.pickaxe.co/ned.malki/32/5918_2.png) [@Ned.Malki](https://community.pickaxe.co/u/Ned.Malki)\
**Post date:** [November 6, 2025, 8:10am UTC](https://community.pickaxe.co/t/add-share-button-to-your-pickaxe-chat-that-exports-current-thread-to-a-pdf-advanced/7444/6 "2025-11-06T08:10:08Z")

</div>

Nice one @carsondev 🙌 This script is a handy UX win!

Quick clarity for everyone on where this shines and where a webhook-to-PDF pipeline is a better fit.

“DOM” (“Document Object Model”) = the live page your browser renders. This script can only export what exists in that live page.

| Topic | Share button script (client-side) | Webhook to Make. com (server-side) |
| --- | --- | --- |
| What it exports | Only what is currently in the “DOM” | Full transcript you send or fetch (not limited to what’s visible) |
| PDF method | html2pdf.js captures the chat area; output is rasterized | Server renderer from an HTML template; higher layout fidelity |
| Branding | Light CSS tweaks and optional element hiding; no standard header/footer template | Fully branded templates with headers, footers, page numbers, watermarks, logos |
| Delivery | Instant download to the user’s device | Automatic email to user and team, plus storage to Drive/S3 and CRM logging |
| Range control | Exports the on-page thread; no built-in range inference | Rules like “last N messages,” between message IDs, or redaction are easy to implement |
| Configuration | Selector for chat container, filename pattern, button position | Page size/orientation, naming conventions, merge rules, redaction, analytics |
| Reliability | Works when the script can access the chat container; breaks inside strict iframes | Runs regardless of embed; pipeline executes even if the user closes the page |
| Setup effort | Simple script embed | Webhook + Make scenario + PDF renderer + email/storage modules |
| Best for | Quick share of what you’re seeing right now | Production-grade branded summaries and records at scale |

### Guidance

- If you need fast, user-initiated exports of the current view, this script is great. Remember it’s limited to the live “DOM,” uses rasterized PDFs, and has no built-in email or storage.
- If you need consistent brand output, automated delivery, archival, and range/redaction rules, use a Pickaxe Action that posts to a Make. com webhook, render a branded HTML template to PDF, email it, and store it.

### Who should use which

- Builders who want a quick “save what I see” for a client or thread ➝ Use the Share button script.
- Builders/Teams that need consistent branding, audit trails, and automated distribution ➝ Use the webhook-to-PDF flow.

For branded PDFs and team workflows, the webhook route remains the right tool.
