How do I get the data out of a block while preserving the formatting (as much as possible)?
This project aims to answer the question “how can I save the results of a logseq query?”
By leveraging the fact that your graph is rendered as HTML, you can have access to your formatted data. That includes query results with tables, advanced queries with custom views, markdown tables, lists, etc.
Functionality scope
Copy the formatted content of a single block to your clipboard via a button click.
Limitations
If your block has embedded images the formatting isn’t going to be pretty.
Disclaimer
I’d consider this project more a proof-of-concept than finished product. Do with it what you want, as it’s released under the AGPL v3 license.
Demonstration
Video showing table and list results rendered in formatted plain-text and as HTML elements.
Details
- This project uses kits
- It offers the ability to export your data by adding a kit button as a child block to the parent with content you want to export.
- The project script “exports” the block data to your clipboard.
- If you paste the data to a text editor, the data will be in formatted plain-text.
- If you paste the data to a rich-text editor, the data will render as HTML elements.
How to install
- Have the logseq kits framework installed, which this project uses.
- Create a new page named
exportBlockContent
, which will contain this project’s javascript code. - Add a new javascript markdown code fence to the page
exportBlockContent
- Include the following code within the new code fence
logseq.kits.exportBlockContent = exportBlockContent;
async function createHTMLContentWithImages(thisElement, keepOriginal = false) {
const element = thisElement;
if (!element || !(element instanceof Element)) {
return null;
}
const htmlContent = element.cloneNode(true);
if (keepOriginal) {
// Return the original HTML without processing images
return htmlContent.outerHTML;
}
// Process all images within the cloned content
const images = htmlContent.querySelectorAll('img');
for (const img of images) {
try {
// Create a temporary container for the image
const tempContainer = document.createElement('div');
tempContainer.appendChild(img.cloneNode());
document.body.appendChild(tempContainer);
// Use html2canvas to capture the image
const canvas = await html2canvas(tempContainer, {
logging: false,
useCORS: true,
backgroundColor: null
});
// Convert canvas to data URI
const dataUri = canvas.toDataURL('image/png');
// Replace original src with data URI
img.src = dataUri;
// Clean up temporary container
document.body.removeChild(tempContainer);
} catch (error) {
console.error('Error processing image:', error);
}
}
return htmlContent.outerHTML;
}
async function exportBlockContent(el, mode = 'process') {
const me = event.target.closest('.ls-block');
const parentBlock = me.parentElement.closest('.ls-block');
try {
let htmlContent;
let clipboardData;
switch (mode) {
case 'original':
htmlContent = await createHTMLContentWithImages(parentBlock, true);
clipboardData = {
'text/html': new Blob([htmlContent], { type: 'text/html' })
};
break;
case 'plaintext':
htmlContent = await createHTMLContentWithImages(parentBlock, true);
clipboardData = {
'text/plain': new Blob([htmlContent], { type: 'text/plain' })
};
break;
default: // 'process'
htmlContent = await createHTMLContentWithImages(parentBlock, false);
clipboardData = {
'text/html': new Blob([htmlContent], { type: 'text/html' }),
'text/plain': new Blob([parentBlock.innerText], { type: 'text/plain' })
};
}
// Use Electron's clipboard API if available
if (window.electron && window.electron.clipboard && window.electron.clipboard.writeHTML) {
window.electron.clipboard.writeHTML(htmlContent);
console.log('Content copied to clipboard successfully using Electron API');
} else {
// Fallback to web API if Electron API is not available
await navigator.clipboard.write([
new ClipboardItem(clipboardData)
]);
console.log('Content copied to clipboard successfully using Web API');
}
} catch (error) {
console.error('Failed to copy content to clipboard:', error);
}
}
exportBlockContent(null)
- Add a kit button
{{runpage exportBlockContent}}
as a child to the block with content that you want to export. For example:
- Example table
| item | content |
|---|---|
| 1 | stuff |
- {{runpage exportBlockContent}}