Move files based on size in Pentaho

Within Pentaho, how do I move files from a particular local directory that are 1 KB in size to another folder? Ideally, I’d like to move all 1 KB files at once and not go one by one (we’re talking hundreds of files that will need to be moved daily).

Thanks for any help!

Unable to get TypeScript to recognize file path aliases

I am trying to get file path aliases setup in a project. I’ve setup everything according to this article. Vite is able to resolve the path aliases, so I don’t get any runtime errors. However, TypeScript keeps giving the following error whenever I try to import using a path alias: Cannot find module '@components/AppLayout' or its corresponding type declarations. ts(2307)

I’ve tried several different options for the paths, and have made sure to restart VSCode after each change. Can anyone tell what I am doing wrong?

  • TypeScript v5.7.2
  • Vite v6.2.0
  • Node v22.9.0

tsconfig.json

{
  "files": [],
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@assets/*": ["src/assets/*"],
      "@components/*": ["src/components/*"],
      "@hooks/*": ["src/hooks/*"],
      "@pages/*": ["src/pages/*"],
      "@redux/*": ["src/redux/*"],
      "@types/*": ["src/types/*"],
      "@utils/*": ["src/utils/*"],
    },
  },
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.node.json" }
  ]
}

vite.config.js

/* eslint-disable import/no-extraneous-dependencies */
import path from 'path'

import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'

// https://vite.dev/config/
export default defineConfig({
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
      '@assets': path.resolve(__dirname, './src/assets'),
      '@components': path.resolve(__dirname, './src/components'),
      '@hooks': path.resolve(__dirname, './src/hooks'),
      '@pages': path.resolve(__dirname, './src/pages'),
      '@redux': path.resolve(__dirname, './src/redux'),
      '@types': path.resolve(__dirname, './src/types'),
      '@utils': path.resolve(__dirname, './src/utils'),
    },
  },
  plugins: [react()],
})

Example import with TS errors

import AppLayout from '@components/AppLayout'
import HomePage from '@pages/HomePage'
import LoginPage from '@pages/LoginPage'

Google Maps Directions API doesn’t find certain addresses that seem valid

I’m building a web app that uses the Google Maps Directions API to calculate routes between multiple addresses. The app works fine for most addresses, but for some inputs like:

Dolores 33, Buenos Aires, Argentina
or
Portela 61, C1406FDA Cdad. Autónoma de Buenos Aires, Argentina

…the API does not return an error, but it returns a generic or imprecise result, such as just: Buenos Aires, Argentina

This causes issues when trying to map precise routes — the origin or destination ends up being too vague or completely incorrect.

What I’m doing

  • Users can enter addresses manually or import them from Excel.
  • I use the Geocoder API to validate addresses before routing.
  • Then I pass them to DirectionsService.route().

Here’s how I validate the address first (simplified):

const geocoder = new google.maps.Geocoder();

geocoder.geocode(
  {
    address: "Dolores 33, Buenos Aires, Argentina",
    componentRestrictions: { country: "ar" }
  },
  (results, status) => {
    if (status === google.maps.GeocoderStatus.OK && results.length > 0) {
      console.log("VALID:", results[0].formatted_address);
    } else {
      console.warn("INVALID:", status);
    }
  }
);

Then I use Directions API like this (simplified):

const directionsService = new google.maps.DirectionsService();

directionsService.route(
  {
    origin: "Dolores 33, Buenos Aires, Argentina",
    destination: "Some other address...",
    travelMode: google.maps.TravelMode.DRIVING
  },
  (response, status) => {
    if (status === google.maps.DirectionsStatus.OK) {
      // Success
    } else {
      console.error("Directions error:", status);
    }
  }
);

What I expected

I expected the Directions API to either:

use the full, specific address if it exists, or

return a clear error if it can’t find a match.

What happens instead

It returns an imprecise or vague result, often just “Buenos Aires, Argentina”.

This leads to incorrect routes or routes starting/ending in the wrong place.

My questions

Is this behavior expected?

Why would the Directions API ignore a more specific address and resolve to something vague?

Is there any reliable workaround (e.g., forcing coordinates, refining geocoding before routing, etc.)?

Any help is appreciated — especially from others working with addresses in Argentina or Latin America in general. Thanks!

Multi Level Dropdown Menu Popup [closed]

I want to learn a small thing.

I want to make a menu using wordpress and php with js tweaks. It should be the same style as it is in the video.

The menu should be dynamic, so that admin can be able to add menu options from wordpress backend dashboard and only single menu should be used.

Can anyone help me out finding a solution?

I have tried everything from my knowledge, even using chatgpt but nothing worked.

Can I reference an object in a function using a variable passed into another function as a parameter? [duplicate]

I am currently trying to build a game in html/js/css. During a portion of the game I want to dynamically grab a value, assign it to a variable and then send that variable as a parameter to another function to grab information. I am trying this so I don’t need 19000 “if/else” statements, that way the second function can dynamically call an object based on the value passed in through arguments. I.E. the argument will be “boots” and I can then get/modify values of the “boots” object detailed in another js file (that is also being loaded when the page loads). My hope is the second function can essentially run the same code by grabbing the object with the same name as the variable’s value.

My code looks as the following

const bones = {
    itemName: "Bones",
    itemValue: "2",
    itemStats: {
        itemAtk: null,
        itemDef: null
    }
}
function dropLoot()
{
    var goldGivenMax = parseInt(playerLoc.Enemies[currentEnemyIndex].enemyLvl)
    var goldGiven = getRandomIntInclusive(0, goldGivenMax)
    var totalGold = parseInt(localStorage.getItem("playerCurrency")) + parseInt(goldGiven)
    var maxItemIndex = parseInt(playerLoc.Enemies[currentEnemyIndex].lootTable.length) - 1
    var itemIndex = getRandomIntInclusive(0,maxItemIndex)
    var itemReceived = playerLoc.Enemies[currentEnemyIndex].lootTable[itemIndex].itemName

    
    alert("You have received" + goldGiven + " gold! You now have " + totalGold + " total gold.")
    alert("You have received item: " + playerLoc.Enemies[currentEnemyIndex].lootTable[itemIndex].itemName)
    addToInventory(itemReceived)
    localStorage.setItem("playerCurrency",totalGold)
    //alert(playerLoc.Enemies[currentEnemyIndex].lootTable[0].itemName)
}
function addToInventory(item)
{
    objName = item
    alert(objName)
    //Doesn't work - also tried this[objName].itemValue
    alert("Your latest item is worth " + window[objName].itemValue)
}

However, nothing I do enables me to actually grab the “goldValue” property of the “bones” item. Everything says “Cannot get property of ‘undefined'” I saw a few articles on here recommending using the “this[myItem].goldValue” syntax and the window[myItem] syntax, and both of those are failing to work. Essentially, I need the object with the same name as the variable’s value, not the variable if that makes sense.

Can anyone point me in the right direction here?

JavaScript + IEEE754 – what a strange behavior? The same number sometimes looks good and sometimes not [duplicate]

A simple JS code which reproduce a problem:

var startX = 4.721
var delta = 0.001
var count = 10
console.log(`startX: ${startX}, delta: ${delta}`)
for (var i = 0; i<count; i++) {
  console.log(`${i}:  ${x}`)
  x += delta
}

On 2-3 step of the loop in output it shows numbers with lot of extra digits in fraction part:

StartX: 4.721, delta: 0.001
0: 4.721
1: 4.722
2: 4.723000000000001
3: 4.724000000000001
4: 4.725000000000001
5: 4.726000000000002
[...]

And according to a theory:

“…Numbers like 0.5, 0.25, 0.75 (decimal) will be represented precisely in binary floating-point format, since these numbers are either powers of 2 (2^-1, 2^-2) or sums thereof.
Meanwhile, such number as decimal 0.1 cannot be expressed by a finite sum of powers of 2. The representation of decimal 0.1 in floating-point binary has infinite length. This immediately means that 0.1 cannot be ever represented precisely in finite binary floating-point format. Note that 0.1 has only one decimal digit. However, this number is still not representable…” [1]

Ok. No problem. I can understand that. Then I assume that 4.725 cannot be ‘represented precisely in binary fp-format’.

But(!) let me change one line the code:

var startX = 4.725

And run it again:

StartX: 4.725, delta: 0.001
0: 4.725
1: 4.726
2: 4.727
3: 4.728000000000001
4: 4.729000000000001
5: 4.730000000000001
[...]

Waw! Now 4.725 (and 3 numbers after it) displayed without any extra extra digits in fraction part!

So, in some cases the same number can be ‘represented precisely in binary floating-point format’ and in some cases – not?!

Why? Why when loop starts with 4.721 then 4.725 cannot be represented precisely in binary fp-format.
But when loop starts with 4.725 then 4.725 can be represented precisely in binary fp-format?

Why such a strange behavior?

Also it brings me to the question – why not to make all fp-numbers always represented precisely in a way loop started? So, without that extra digits in fraction part.

Also strange thing – when I apply to a 4.725000000000001 a simple function:

const fixFp = (x: number, scale: number) => {
    return parseFloat(x.toFixed(scale))
}

Then it is again – can be represented precisely in binary fp-format. WHY?!
Why not to make all numbers always represented correctly in such case?

PS.
Yes, I know about call .toFixed(N).
But I’m just trying to figure out how to deal with situation when we have 1000s of UI forms in app and most of them use fp-numbers with different scales(>0).
Thus, adding to each place “.toFixed(N)” call with correct N – that could be quite a problem.

Why aren’t styles loading in PrimeVue?

I don’t understand what the problem is.
I installed primevue 4.3.3 and @primeuix
In main.ts I connect it like this

import PrimeVue from "primevue/config";
import Aura from "@primeuix/themes/aura";
import "primeicons/primeicons.css";
import "@primeuix/styles";

app.use(PrimeVue, {
  theme: {
    preset: Aura,
    options: {
      prefix: "talktune",
      darkModeSelector: "system",
      cssLayer: false,
    },
  },
  locale: PrimeVueLocaleRU,
})

But when I import the components, there are no styles.
My vite.config looks like this

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";

export default defineConfig({
  plugins: [vue()],
  server: {
    hmr: {
      protocol: "ws",
      host: "localhost",
    },
    port: 3000,
  },
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "src/assets"),
      ui: path.resolve(__dirname, "src/components/ui"),
      components: path.resolve(__dirname, "src/components"),
      pages: path.resolve(__dirname, "src/pages"),
      styles: path.resolve(__dirname, "src/styles"),
      src: path.resolve(__dirname, "src"),
    },
  },
});

What is the most efficient way to render endless looping animations using D3.js and GSAP in frontend development?

I’m working on a React project where I need to create smooth, endless looping animations, particularly for line drawing using D3.js and GSAP. However, I’m encountering an issue where the line drawing isn’t as smooth as I would like it to be.

Here’s what I’ve done so far:

I’m using D3.js to generate dynamic SVG paths for the line drawing.

GSAP is used to animate the line drawing with stroke-dasharray and stroke-dashoffset for a “drawing” effect.

The animation is supposed to loop endlessly and run smoothly, but it seems like there are performance issues that affect the smoothness.

Specifics:

React rendering: I’m using React to manage the DOM and render SVG elements dynamically. The lines are drawn in an SVG element, and the path elements are being updated with GSAP.

Performance issues: The animation works but is not smooth; it seems jittery or laggy, especially with complex paths or when there are many elements to animate.

Smoothness issue: I’ve tried adjusting the duration and easing of the GSAP animations, but the line still doesn’t draw as smoothly as expected.

import gsap from 'gsap';
import * as d3 from 'd3';

// Graph setup
const xAxisLength = 600;
const yAxisHeight = (xAxisLength / 16) * 9;
const paddingRatio = 0.1;
const svgWidth = xAxisLength * (1 + paddingRatio);
const svgHeight = yAxisHeight * (1 + paddingRatio);
const xAxisStartPos = xAxisLength * paddingRatio;
const yAxisStartPos = yAxisHeight * paddingRatio;

// Line data setup
const numPoints = 100;
const xSpacing = (xAxisLength - xAxisStartPos) / (numPoints - 1);

// Generate a new point with random sharp movement
function generateNewPoint(prevY = yAxisHeight / 2) {
  const delta = (Math.random() - 0.5) * 20; // Random movement
  return Math.max(0, Math.min(yAxisHeight, prevY + delta)); // Clamping within yAxisHeight
}

const HomeDataViz = () => {
  const xAxisLineRef = useRef();
  const yAxisLineRef = useRef();
  const pathRef = useRef();
  const groupRef = useRef();
  const [axesAnimated, setAxesAnimated] = useState(false);
  const [line1Points, setLine1Points] = useState([
    {
      x: xAxisStartPos,
      y: yAxisHeight / 2 + (Math.random() - 0.5) * 20,
    },
  ]);
  const [shift, setShift] = useState(0);

  useLayoutEffect(() => {
    const xAxisLineLength = xAxisLineRef.current.getTotalLength();
    const yAxisLineLength = yAxisLineRef.current.getTotalLength();

    gsap.set([xAxisLineRef.current, yAxisLineRef.current], {
      strokeDasharray: (i) => (i === 0 ? xAxisLineLength : yAxisLineLength),
      strokeDashoffset: (i) => (i === 0 ? xAxisLineLength : yAxisLineLength),
    });

    const tl = gsap.timeline();

    tl.to([xAxisLineRef.current, yAxisLineRef.current], {
      strokeDashoffset: 0,
      duration: 0.75,
      ease: 'power1.inOut',
      stagger: 0.1,
    });

    tl.addLabel("axesDone");
    tl.call(() => setAxesAnimated(true), null, "axesDone+=0.1");
  }, []);

  useEffect(() => {
    const interval = setInterval(() => {
      setLine1Points((prev) => {
        const lastX = prev[prev.length - 1].x;
        const lastY = prev[prev.length - 1].y;
        const newX = lastX + xSpacing;
        const newY = generateNewPoint(lastY);

        const newPoints = [...prev, { x: newX, y: newY }];
        return newPoints.length > numPoints ? newPoints.slice(1) : newPoints;
      });

      setShift((prevShift) => {
        return line1Points.length >= numPoints ? prevShift - xSpacing : prevShift;
      });
    }, 100);

    return () => clearInterval(interval);
  }, [line1Points]);

  useEffect(() => {
    if (line1Points.length > 0) {
      if (!axesAnimated) return;

      const line = d3
        .line()
        .x((d) => d.x)
        .y((d) => d.y)
        .curve(d3.curveCatmullRom.alpha(0.5));

      const pathData = line(line1Points);
      if (pathRef.current) {
        pathRef.current.setAttribute('d', pathData);
        const pathLength = pathRef.current.getTotalLength();
        pathRef.current.style.strokeDasharray = pathLength;
        pathRef.current.style.strokeDashoffset = pathLength;

        gsap.to(pathRef.current, {
          strokeDashoffset: 0,
          duration: 2,
          ease: 'power2.out',
        });
      }
    }

    gsap.to(groupRef.current, {
      x: shift,
      duration: 0.1,
      ease: 'linear',
    });
  }, [line1Points, shift]);

  return (
    <svg width={svgWidth} height={svgHeight} style={{ border: '1px solid white' }}>
      <g ref={groupRef}>
        <path ref={pathRef} fill="none" stroke="white" strokeWidth={2} />
      </g>
      <line
        ref={xAxisLineRef}
        x1={xAxisStartPos}
        y1={yAxisHeight}
        x2={xAxisLength}
        y2={yAxisHeight}
        stroke="white"
        strokeWidth="2"
      />
      <line
        ref={yAxisLineRef}
        x1={xAxisStartPos}
        y1={yAxisHeight}
        x2={xAxisStartPos}
        y2={yAxisStartPos}
        stroke="white"
        strokeWidth="2"
      />
    </svg>
  );
};

export default HomeDataViz;

How to show custom value in TextField type of select?

Here is my component.
So when I select e.g. “Option 1” in dropdown I want to show in TextField this selected item like e.g. “Option 1 selected”. Please advise

import React from 'react';
import { TextField, MenuItem } from '@mui/material';

const MyComponent = () => {
  const [selectedOption, setSelectedOption] = React.useState('');

  const handleChange = (event) => {
    setSelectedOption(event.target.value);
  };

  return (
    <TextField id="select-option" select label="Select an option" value={selectedOption} onChange={handleChange}>
      <MenuItem value="option1">Option 1</MenuItem>
      <MenuItem value="option2">Option 2</MenuItem>
      <MenuItem value="option3">Option 3</MenuItem>
    </TextField>
  );
};

export default MyComponent;

Primefaces dialog minimize property

I am trying to load a primefaces dialog with an iframe as its content. I want the dialog to be minimizable so that user can get back to their progress within the dialog box. But the issue I’m encountering is dialog box is rerendering its content on restore i.e. for example if user navigates to a different view within the iframe, when the dialog is restored they are taken back to the src the iframe was initially provided. Is this a limitation on the primefaces dialog? Does not make sense to have a minimize button if the contents are refreshed if the user wants the dialog restored. I’m open to any suggestions or workaround.

<h:body>

    <h:form>
    <p:commandButton value="Show" type="button" icon="pi pi-external-link" onclick="PF('dlg1').show()"/>
    <p:dialog header="Header" widgetVar="dlg1" minHeight="40" width="80vw" height="80vh" closeOnEscape="true" minimizable="true" maximizable="true" dynamic="true">
    <iframe id="moduleAppFrame" title="Test Dialog" src="https://www.wikipedia.org/" class="moduleFrame" width="100%" height="100%"/>
    </p:dialog>
    </h:form>
</h:body>

How to Build a Simple To-Do List App with HTML, CSS, and JavaScript for Beginners [closed]

I’m a beginner in web development and trying to build a simple to-do list app using HTML, CSS, and JavaScript. My goal is to create an app where users can:

Add a task to the list.
Mark a task as completed (e.g., by striking through the text).
Delete a task from the list.
I’ve started with the following code, but I’m facing issues with adding and deleting tasks dynamically. Here’s what I have so far:

Leaflet.js Tooltip Rendering Issue When Binding Dynamic Data to Station Markers in Loop

I’m working on a route visualization tool using Leaflet.js to map Amtrak train routes. I’m loading station coordinates from a JSON array and creating markers with custom tooltips showing station names, cities, and codes.

Here’s the core of my Javascript code:

var stationData = [
  { name: 'Chicago Union Station', code: 'CHI', lat: 41.8789, lng: -87.6400 },
  { name: 'Denver Union Station', code: 'DEN', lat: 39.7527, lng: -104.9994 },
  { name: 'Salt Lake City', code: 'SLC', lat: 40.7608, lng: -111.8910 }
  // ... more
];

stationData.forEach(function(station) {
  var marker = L.marker([station.lat, station.lng]).addTo(map);
  var popupContent = `<strong>${station.name}</strong><br>Code: ${station.code}`;

  marker.bindTooltip(popupContent, {
    permanent: false,
    direction: 'top',
    offset: [0, -10],
    className: 'station-tooltip'
  });
});

The tooltips sometimes:

  • Fail to render on specific zoom levels (especially when clustered)
  • Don’t reappear after being closed
  • Cause undefined values in popupContent even though the JSON data looks fine

Things I’ve tried:

  • Wrapping the content generation in setTimeout (no improvement)
  • Manually calling marker.openTooltip() after bindTooltip() (breaks hover logic)
  • Debugging station.code values — confirmed they are not undefined

Here’s a working map where I’m trying to replicate this behavior:
Amtrak Routes Map

My question:
Is there a common reason Leaflet tooltips silently fail when used in a data loop?

  • Could DOM rendering timing affect dynamic tooltips?
  • Should I be pre-processing the content strings before binding to avoid hidden async issues?

If it helps, I’m also using:

  • Leaflet 1.9.4
  • jQuery 3.6 (for other DOM tasks, not here)
  • Vanilla JS (no React/Vue)

I made a Tapermonkey Userscrpt but for some reason it doesnt show up on the website [closed]

I changed somthing for the HTML layout what has nothing to do if taper monkey recognize the website. I think its a Tapermonkey problem but I’m not sure. The link wich should match (@match…) is correct even tried a more accurate link.
I did not tried to load an older version of the code because as i know it would likely dont change anything

// ==UserScript==
// @name         R6 Marketplace Purchases from Google Sheets
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Anzeigen von R6-Items aus Google Sheets
// @author       github.com/realifebro
// @match        *www.ubisoft.com/de-de/game/rainbow-six/siege/marketplace?*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    // Your Google Sheets-ID und API-Key (if used)
    const sheetId = "Your_Google-Sheet-ID"; // replace with your Google Sheet ID
    const apiKey = "Your_API-KEY"; // replace with your API-Key

    // Google Sheets API URL (tested for public sheets)
    const sheetURL = `https://sheets.googleapis.com/v4/spreadsheets/${sheetId}/values/Sheet1?key=${apiKey}`;

    // HTML for the layout of the UI
    const displayContainer = document.createElement('div');
    displayContainer.style.position = 'fixed';
    displayContainer.style.top = '10px';
    displayContainer.style.right = '10px';
    displayContainer.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
    displayContainer.style.color = 'white';
    displayContainer.style.padding = '10px';
    displayContainer.style.maxHeight = '80vh';
    displayContainer.style.overflowY = 'auto';
    displayContainer.style.zIndex = 9999; // Ensure it's on top
    displayContainer.style.fontFamily = 'Arial, sans-serif';
    displayContainer.style.fontSize = '12px';
    displayContainer.style.borderRadius = '5px';
    displayContainer.style.pointerEvents = 'auto'; // Allows clicking on the UI
    document.body.appendChild(displayContainer);

    // Function to retrieve data from Google Sheets
    GM_xmlhttpRequest({
        method: "GET",
        url: sheetURL,
        onload: function(response) {
            const jsonData = JSON.parse(response.responseText);

            // Check if data is returned
            if (!jsonData || !jsonData.values || jsonData.values.length === 0) {
                displayContainer.innerHTML = 'Keine Daten in Google Sheets gefunden.';
                return;
            }

            const rows = jsonData.values;
            displayItems(rows);
        },
        onerror: function() {
            displayContainer.innerHTML = 'Fehler beim Laden der Daten aus Google Sheets!';
        }
    });

    // Show items
    function displayItems(rows) {
        let htmlContent = '<h3>R6 Marketplace Purchases</h3><ul>';

        // skip the first line (Header)
        for (let i = 1; i < rows.length; i++) {
            const row = rows[i];
            const itemName = row[0] || "Unbekannter Artikel";
            const sellDate = row[2] || "Unbekannt";
            const credits = row[3] || "Unbekannt";

            // show item name bigger
            htmlContent += `
                <li>
                    <strong style="font-size: 16px;">Item:</strong> <span style="font-size: 18px; font-weight: bold;">${itemName}</span><br>
                    <strong>Verkaufsdatum:</strong> ${sellDate}<br>
                    <strong>Credits:</strong> ${credits}
                </li>
                <hr>
            `;
        }

        htmlContent += '</ul>';
        displayContainer.innerHTML = htmlContent;
    }

})();


Update:
The script is running now but it doesent show anything so the First problem is solved now its creating the Field were infos should display but it doesnt happen.
How do I fix that the Infos that it gets from the google sheet displays?

PDF Auto-Upload Not Working After Editing in React Component

PDF Auto-Upload Not Working After Editing in React Component

Problem Description

I’m implementing a PDF editor with auto-upload functionality in a React component. The PDF is generated and opened in a new window for editing, but changes made to the PDF are not being properly uploaded back to the server.

Current Implementation

Here’s the relevant code from my ChatMessage.jsx component:

const handleGenerateParPdf = async () => {
  try {
    // Generate initial PDF
    const response = await dispatch(generateParPdf(formData)).unwrap();
    
    // Convert base64 to blob and create URL
    const byteCharacters = atob(response.data);
    const byteArray = new Uint8Array(byteCharacters.length);
    for (let i = 0; i < byteCharacters.length; i++) {
      byteArray[i] = byteCharacters.charCodeAt(i);
    }
    const blob = new Blob([byteArray], { type: "application/pdf" });
    const pdfUrl = URL.createObjectURL(blob);

    // Open PDF in new window with auto-save functionality
    const newWindow = window.open("", "_blank");
    newWindow.document.write(`
      <!DOCTYPE html>
      <html>
        <head>
          <title>PAR PDF Editor</title>
          <style>
            /* ... styles ... */
          </style>
        </head>
        <body>
          <div class="toolbar">
            <div class="status">Changes will be saved automatically</div>
            <div class="button-group">
              <button class="upload-btn" onclick="handleManualUpload()">Upload</button>
              <button class="close-btn" onclick="window.close()">Close</button>
            </div>
          </div>
          <iframe 
            id="pdf-container" 
            src="${pdfUrl}#toolbar=1" 
            type="application/pdf" 
            width="100%" 
            height="calc(100vh - 50px)"
          ></iframe>
          <script>
            // Auto-save functionality
            let saveTimeout;
            const statusEl = document.querySelector('.status');
            
            async function handlePdfChange() {
              try {
                statusEl.textContent = 'Saving changes...';
                statusEl.className = 'status saving';
                
                const pdfFrame = document.getElementById('pdf-container');
                const response = await fetch(pdfFrame.src);
                const pdfBlob = await response.blob();
                
                window.opener.postMessage({
                  type: 'autosavePdf',
                  pdfData: await pdfBlob.arrayBuffer(),
                  timestamp: Date.now()
                }, '*');

                statusEl.textContent = 'Changes saved';
                statusEl.className = 'status saved';
              } catch (error) {
                console.error('Error saving PDF:', error);
                statusEl.textContent = 'Error saving changes';
                statusEl.className = 'status error';
              }
            }

            // Watch for PDF changes
            const observer = new MutationObserver(() => {
              clearTimeout(saveTimeout);
              saveTimeout = setTimeout(handlePdfChange, 2000);
            });

            observer.observe(document.getElementById('pdf-container'), {
              attributes: true,
              childList: true,
              subtree: true
            });
          </script>
        </body>
      </html>
    `);
  } catch (error) {
    console.error("Error generating PAR PDF:", error);
  }
};

Expected Behavior

  1. PDF should open in a new window with editing capabilities
  2. Changes made to the PDF should be automatically detected
  3. Modified PDF should be automatically uploaded to the server
  4. Status should update to show successful save

Actual Behavior

  1. PDF opens correctly in new window
  2. Changes can be made to the PDF
  3. Auto-save triggers but changes are not being properly captured
  4. Status shows “Changes saved” but server doesn’t receive updates

What I’ve Tried

  1. Using MutationObserver to detect changes in the iframe
  2. Implementing manual upload button as fallback
  3. Using postMessage to communicate between windows
  4. Converting PDF to blob before sending

Questions

  1. How can I properly detect changes made to the PDF in the iframe?
  2. Is there a better way to capture the modified PDF content?
  3. How can I ensure the changes are properly uploaded to the server?
  4. Are there any security considerations I should be aware of?

Environment

  • React 18
  • Next.js
  • PDF.js (if relevant)
  • Browser: Chrome/Firefox

Any help or guidance would be greatly appreciated!