diff --git a/package-lock.json b/package-lock.json index e773d0b8..198b5d4d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "flowbite-svelte": "^0.46.15", "got": "^14.4.2", "html2canvas": "^1.4.1", + "html2canvas-pro": "^1.5.8", "jsonwebtoken": "^9.0.2", "katex": "^0.16.11", "lightweight-charts": "^4.1.3", @@ -5591,6 +5592,20 @@ "node": ">=8.0.0" } }, + "node_modules/html2canvas-pro": { + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/html2canvas-pro/-/html2canvas-pro-1.5.8.tgz", + "integrity": "sha512-bVGAU7IvhBwBlRAmX6QhekX8lsaxmYoF6zIwf/HNlHscjx+KN8jw/U4PQRYqeEVm9+m13hcS1l5ChJB9/e29Lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/http_ece": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.2.0.tgz", diff --git a/package.json b/package.json index 85b6db94..904191ae 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "flowbite-svelte": "^0.46.15", "got": "^14.4.2", "html2canvas": "^1.4.1", + "html2canvas-pro": "^1.5.8", "jsonwebtoken": "^9.0.2", "katex": "^0.16.11", "lightweight-charts": "^4.1.3", diff --git a/src/routes/potus-tracker/+page.svelte b/src/routes/potus-tracker/+page.svelte index 50920a19..9db34eff 100644 --- a/src/routes/potus-tracker/+page.svelte +++ b/src/routes/potus-tracker/+page.svelte @@ -8,9 +8,15 @@ import avatar from "$lib/images/trump-avatar.jpeg"; import { mode } from "mode-watcher"; import { goto } from "$app/navigation"; + import html2canvas from "html2canvas-pro"; export let data; + let postContent = "n/a"; + let postDate = "n/a"; + let postUrl = "#"; + let postTitle = "n/a"; + const updatedSectorList = ["S&P500", ...sectorList]; let rawData = data?.getData?.history || []; @@ -198,6 +204,123 @@ let config = null; + async function captureScreenshot(index) { + const postElement = document.querySelector(`#post-${index}`); + + // Clone the element to avoid modifying the original + const clonedElement = postElement.cloneNode(true); + + // Create a temporary container for the clone with fixed dimensions and styling + const tempContainer = document.createElement("div"); + tempContainer.style.position = "absolute"; + tempContainer.style.left = "-9999px"; + tempContainer.style.top = "0"; + tempContainer.appendChild(clonedElement); + document.body.appendChild(tempContainer); + + // Force light mode styling explicitly + clonedElement.style.cssText = + "background-color: white !important; color: black !important;"; + + // Process all elements recursively to ensure text visibility + function forceVisibleText(element) { + // Apply styles directly + element.style.cssText += + "; color: black !important; background-color: white !important; border-color: #ccc !important;"; + + // For SVG elements, ensure they're visible + if ( + element.tagName.toLowerCase() === "svg" || + element.tagName.toLowerCase() === "path" + ) { + element.style.cssText += + "; fill: #333 !important; stroke: #333 !important;"; + } + + // Remove problematic classes completely instead of just removing dark: prefix + if (element.classList) { + const classesToRemove = []; + element.classList.forEach((cls) => { + if ( + cls.includes("dark:") || + cls.includes("text-gray") || + cls.includes("text-white") + ) { + classesToRemove.push(cls); + } + }); + classesToRemove.forEach((cls) => element.classList.remove(cls)); + + // Add explicit light mode classes + element.classList.add("text-black"); + } + + // Process all child elements + if (element.children && element.children.length > 0) { + Array.from(element.children).forEach((child) => + forceVisibleText(child), + ); + } + } + + // Apply the text visibility fix to all elements + forceVisibleText(clonedElement); + + // Additional specific fixes for elements that might still have issues + const allTextElements = clonedElement.querySelectorAll( + "p, h1, h2, h3, h4, h5, span, a, div, label", + ); + allTextElements.forEach((el) => { + el.style.color = "black"; + el.setAttribute( + "style", + el.getAttribute("style") + "; color: black !important;", + ); + }); + + // Convert any CSS variables that might affect color + const computed = window.getComputedStyle(postElement); + const cssText = + ":root { --text-color: black !important; --background-color: white !important; }"; + const style = document.createElement("style"); + style.textContent = cssText; + clonedElement.appendChild(style); + + // Wait a bit to ensure styles are applied + await new Promise((resolve) => setTimeout(resolve, 50)); + + // Capture screenshot + try { + const canvas = await html2canvas(clonedElement, { + backgroundColor: "white", + logging: true, // Enable logging to help debug + scale: 2, // Higher quality + useCORS: true, + allowTaint: true, + removeContainer: false, // Handle cleanup ourselves + }); + + // Convert to image + const image = canvas.toDataURL("image/png"); + + // Create a download link + const link = document.createElement("a"); + link.href = image; + link.download = `post-${index}.png`; + document.body.appendChild(link); + link.click(); + + // Clean up + document.body.removeChild(link); + document.body.removeChild(tempContainer); + + return image; + } catch (error) { + console.error("Screenshot capture failed:", error); + document.body.removeChild(tempContainer); + throw error; + } + } $: { if (selectedSector || $mode) { config = plotData() || null; @@ -271,7 +394,7 @@ class="w-56 h-fit max-h-72 overflow-y-auto scroller" > Select Sector @@ -411,7 +534,9 @@ {/if} - + {item.time_formatted} {item.location !== null ? `- ${item?.location}` @@ -419,7 +544,7 @@ - + {item.details} @@ -454,140 +579,112 @@
{#each items as item, indexB} +
+
- Avatar + > + Trump Image + -
-

- {item?.title} -

- -
+

- {item?.sentiment} -

+ Donald J. Trump + +

+
+ {item?.title} + +
+ {item?.sentiment} +
+
+

- - {#if item.description.length > 150} - {expandedDescriptions[item.title] - ? item.description - : truncateText(item.description)} - - {:else} - {item.description} - {/if} - - - - Source - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ - + {item?.description?.length > 300 + ? item?.description?.slice(0, 300) + "..." + : item?.description} + +
+ +
+ + +
+ + + + + + +
{/each} @@ -618,137 +715,148 @@
-
- {#each posts as item} -
-
-
- - Avatar - -
-

- Donald J. Trump -

-

- @realDonaldTrump - · {item?.date} -

-
-
- - - {item?.content} - - +
+ {#each posts as item, index} +
+ + +

+ {item?.content?.length > 400 + ? item?.content?.slice(0, 400) + "..." + : item?.content} +

+ +
+ {item?.date} +
+
+ +
{/each} @@ -818,20 +926,146 @@
- + + + + + + + + + +