select a region of desktop screen with Electron - node.js

I'm trying to write an application that allow the user to select a region of the screen (like selecting to take a screen shot).
Is that even possible?

To specifically take a full screen shot, use the following code (example pulled from Electron Demo App). You can build off of this example, and use the screen, desktopCapturer and rectangle modules in the electron api to customize the code to get a specific screen/display, or select a specific bounding box (x/y coordinates and pixel area).
const electron = require('electron')
const desktopCapturer = electron.desktopCapturer
const electronScreen = electron.screen
const shell = electron.shell
const fs = require('fs')
const os = require('os')
const path = require('path')
const screenshot = document.getElementById('screen-shot')
const screenshotMsg = document.getElementById('screenshot-path')
screenshot.addEventListener('click', function (event) {
screenshotMsg.textContent = 'Gathering screens...'
const thumbSize = determineScreenShotSize()
let options = { types: ['screen'], thumbnailSize: thumbSize }
desktopCapturer.getSources(options, function (error, sources) {
if (error) return console.log(error)
sources.forEach(function (source) {
if (source.name === 'Entire screen' || source.name === 'Screen 1') {
const screenshotPath = path.join(os.tmpdir(), 'screenshot.png')
fs.writeFile(screenshotPath, source.thumbnail.toPng(), function (error) {
if (error) return console.log(error)
shell.openExternal('file://' + screenshotPath)
const message = `Saved screenshot to: ${screenshotPath}`
screenshotMsg.textContent = message
})
}
})
})
})
function determineScreenShotSize () {
const screenSize = electronScreen.getPrimaryDisplay().workAreaSize
const maxDimension = Math.max(screenSize.width, screenSize.height)
return {
width: maxDimension * window.devicePixelRatio,
height: maxDimension * window.devicePixelRatio
}
}
Other ways you could go about this are:
Use object.getClientRects() in the DOM to specify specific elements you want to capture, although this would require foreknowledge of what they are.
Add event listeners in your view to 'draw' the shape of what you want with mouseClick, mouseMove, etc. This stack overflow question has answers which could be adapted to fit what you want to do.

I doubt you are still looking for a solution to this, but after digging i have found a way to do it using a combination of shelljs and clipboard.
const userDataPath = (app).getPath(
'userData'
)
const useP = path.join(userDataPath, 'uploads')
let randomTmpfile = uniqueFilename(useP, 'prefix')
shelljs.exec(`screencapture -ic ${randomTmpfile}.png`, function (res) {
const image = clipboard.readImage('png').toDataURL()
})

Related

How to solve react-hydration-error in Next.js when using `useLocalStorage` and `useDebounce`

When I try to use https://usehooks-ts.com/react-hook/use-local-storage in Next.js in the following way, I get
Unhandled Runtime Error Error: Text content does not match
server-rendered HTML.
See more info here:
https://nextjs.org/docs/messages/react-hydration-error
const [toleranceH, setToleranceH] = useLocalStorage<number>('toleranceH', 3);
const [toleranceS, setToleranceS] = useLocalStorage<number>('toleranceS', 3);
const [toleranceL, setToleranceL] = useLocalStorage<number>('toleranceL', 3);
const [results, setResults] = useState<MegaColor[]>([]);
const debouncedToleranceH = useDebounce<number>(toleranceH, 200);
const debouncedToleranceS = useDebounce<number>(toleranceS, 200);
const debouncedToleranceL = useDebounce<number>(toleranceL, 200);
useEffect(() => {
const targetColorDetailsObject = getColorDetailsObject(targetColor);
const degreeTolerance = (360 / 100) * debouncedToleranceH;
const [hueMin, hueMax] = getHueTolerance(targetColorDetailsObject.hue(), degreeTolerance);
const filteredColors = getFilteredColors(targetColorDetailsObject, loadedMegaColors, hueMin, hueMax, debouncedToleranceS, debouncedToleranceL);
setResults(filteredColors);
return () => {
// console.log('cleanup');
};
}, [targetColor, loadedMegaColors, debouncedToleranceH, debouncedToleranceS, debouncedToleranceL]);
From that help page, I still can't figure out what to adjust so that I can use both useLocalStorage and useDebounce.
I found https://stackoverflow.com/a/73411103/470749 but don't want to forcefully set a localStorage value (it should only be set by the user).
I'd suggest checking out this excellent post on rehydration by Josh W Comeau.
Since Next.js pre-renders every page by default you need to ensure that the component in which you are calling window.localstorage is only rendered on the client.
A simple solution is to:
Keep a hasMounted state
const [hasMounted, setHasMounted] = useState(false);
Toggle it inside a useEffect
useEffect(() => {
// This will only be called once the component is mounted inside the browser
setHasMounted(true);
}, []);
Add a check so that Next.js won't complain about prerendering stuff on the server that won't match the stuff that gets rendered on the client
if (!hasMounted) {
return null;
}
Ensure that the client-side stuff comes after the check
To make it more reusable you could use one of these two methods which essentially do the same:
ClientOnly Component
function ClientOnly({ children, ...delegated }) {
const [hasMounted, setHasMounted] = React.useState(false);
React.useEffect(() => {
setHasMounted(true);
}, []);
if (!hasMounted) {
return null;
}
/**
* Could also replace the <div></div> with
* <></> and remove ...delegated if no need
*/
return (
<div {...delegated}>
{children}
</div>
);
}
...
<ClientOnly>
<MyComponent /> // <--- client only stuff, safe to use useLocalStorage in here
</ClientOnly>
or
Custom useHasMounted hook
function useHasMounted() {
const [hasMounted, setHasMounted] = React.useState(false);
React.useEffect(() => {
setHasMounted(true);
}, []);
return hasMounted;
}
...
function ParentComponent() {
const hasMounted = useHasMounted();
if (!hasMounted) {
return null;
}
return (
<MyComponent />
);
}
...
function MyComponent() {
const [toleranceH, setToleranceH] = useLocalStorage<number>('toleranceH', 3);
const [toleranceS, setToleranceS] = useLocalStorage<number>('toleranceS', 3);
const [toleranceL, setToleranceL] = useLocalStorage<number>('toleranceL', 3);
...
}
...
Note:
By overdoing this or using this method at the top level of your component tree, you are killing the Next.js prerendering capabilities and turning your app into more of a "client-side heavy" app (see performance implications). If you are using window.localstorage (outside of components, where you don't have useEffect available), you should always wrap with:
if (typeof window !== 'undefined') {
// client-side code
}

How to increase excel sheet column width while downloading in react js ? ,i am using a package called "export-from-json"

downloadPropertiesInXl = async () => {
let API_URL = "something....";
const property = await axios.get(API_URL);
const data = property.data;
const fileName = "download";
const exportType = "xls";
exportFromJSON({ data, fileName, exportType });
}
};
is there any other packages to change column width??
Use Excel.js, it have many options for customization
https://www.npmjs.com/package/exceljs#columnsex

How to pass a variable from one module to another?

I know there may be a duplicate question but I've been reading and really struggling to understand and figure out how to pass a variable from a command module into an event module shown below.
Command:
exports.run = async (client, message, args) => {
const embed = new Discord.RichEmbed()
.addField(':heart:', `${xb.toString()}`, true)
.addField(':black_heart:', `${ps.toString()}`, true)
.addField(':yellow_heart:', `${nin.toString()}`, true)
.addField(':purple_heart:', `${pcmr.toString()}`, true)
message.channel.send(embed).then(async msg => {
let embedid = msg.id;
module.exports.embedid = embedid;
await msg.react('❤');
await msg.react('🖤');
await msg.react('💛');
await msg.react('💜');
});
}
Event:
module.exports = async (client, messageReaction, user) => {
const message = messageReaction.message;
const channel = message.guild.channels.find(c => c.name === 'role-assignment');
const member = message.guild.members.get(user.id);
if(member.user.bot) return;
const xb = message.guild.roles.get('540281375106924555');
const ps = message.guild.roles.get('540296583632388115');
const nin = message.guild.roles.get('540296630260203520');
const pcmr = message.guild.roles.get('540296669733060618');
if(['❤', '🖤', '💛', '💜'].includes(messageReaction.emoji.name) && message.channel.id === channel.id && messageReaction.message.id === embedid) {};
I'm hoping to pass embedid, embed2id and so on to the event module so I can filter by the message ID that is generated when sending the RichEmbed()
Thanks in advance, I've been running in circles for days!
So I figured it out by looking into what exports actually do, which really, should have been the first thing I did, here :
What is the purpose of Node.js module.exports and how do you use it?
Using the info learned here, I made the following changes to my event:
const e1 = require('../commands/startroles'); // At the top of my messageReactionAdd.js file before module.exports[...]
messageReaction.message.id === e1.embedid // added the e1. to import the variable.

PDF to Text extractor in nodejs without OS dependencies

Is there a way to extract text from PDFs in nodejs without any OS dependencies (like pdf2text, or xpdf on windows)? I wasn't able to find any 'native' pdf packages in nodejs. They always are a wrapper/util on top of an existing OS command.
Thanks
Have you checked PDF2Json? It is built on top of PDF.js. Though it is not providing the text output as a single line but I believe you may just reconstruct the final text based on the generated Json output:
'Texts': an array of text blocks with position, actual text and styling informations:
'x' and 'y': relative coordinates for positioning
'clr': a color index in color dictionary, same 'clr' field as in 'Fill' object. If a color can be found in color dictionary, 'oc' field will be added to the field as 'original color" value.
'A': text alignment, including:
left
center
right
'R': an array of text run, each text run object has two main fields:
'T': actual text
'S': style index from style dictionary. More info about 'Style Dictionary' can be found at 'Dictionary Reference' section
After some work, I finally got a reliable function for reading text from PDF using https://github.com/mozilla/pdfjs-dist
To get this to work, first npm install on the command line:
npm i pdfjs-dist
Then create a file with this code (I named the file "pdfExport.js" in this example):
const pdfjsLib = require("pdfjs-dist");
async function GetTextFromPDF(path) {
let doc = await pdfjsLib.getDocument(path).promise;
let page1 = await doc.getPage(1);
let content = await page1.getTextContent();
let strings = content.items.map(function(item) {
return item.str;
});
return strings;
}
module.exports = { GetTextFromPDF }
Then it can simply be used in any other js file you have like so:
const pdfExport = require('./pdfExport');
pdfExport.GetTextFromPDF('./sample.pdf').then(data => console.log(data));
Thought I'd chime in here for anyone who came across this question in the future.
I had this problem and spent hours over literally all the PDF libraries on NPM. My requirements were that I needed to run it on AWS Lambda so could not depend on OS dependencies.
The code below is adapted from another stackoverflow answer (which I cannot currently find). The only difference being that we import the ES5 version which works with Node >= 12. If you just import pdfjs-dist there will be an error of "Readable Stream is not defined". Hope it helps!
import * as pdfjslib from 'pdfjs-dist/es5/build/pdf.js';
export default class Pdf {
public static async getPageText(pdf: any, pageNo: number) {
const page = await pdf.getPage(pageNo);
const tokenizedText = await page.getTextContent();
const pageText = tokenizedText.items.map((token: any) => token.str).join('');
return pageText;
}
public static async getPDFText(source: any): Promise<string> {
const pdf = await pdfjslib.getDocument(source).promise;
const maxPages = pdf.numPages;
const pageTextPromises = [];
for (let pageNo = 1; pageNo <= maxPages; pageNo += 1) {
pageTextPromises.push(Pdf.getPageText(pdf, pageNo));
}
const pageTexts = await Promise.all(pageTextPromises);
return pageTexts.join(' ');
}
}
Usage
const fileBuffer = fs.readFile('sample.pdf');
const pdfText = await Pdf.getPDFText(fileBuffer);
This solution worked for me using node 14.20.1 using "pdf-parse": "^1.1.1"
You can install it with:
yarn add pdf-parse
This is the main function which converts the PDF file to text.
const path = require('path');
const fs = require('fs');
const pdf = require('pdf-parse');
const assert = require('assert');
const extractText = async (pathStr) => {
assert (fs.existsSync(pathStr), `Path does not exist ${pathStr}`)
const pdfFile = path.resolve(pathStr)
const dataBuffer = fs.readFileSync(pdfFile);
const data = await pdf(dataBuffer)
return data.text
}
module.exports = {
extractText
}
Then you can use the function like this:
const { extractText } = require('../api/lighthouse/lib/pdfExtraction')
extractText('./data/CoreDeveloper-v5.1.4.pdf').then(t => console.log(t))
Instead of using the proposed PDF2Json you can also use PDF.js directly (https://github.com/mozilla/pdfjs-dist). This has the advantage that you are not depending on modesty who owns PDF2Json and that he updates the PDF.js base.

Cycling images in a live tile

I have a winJS app that is a working launcher for a steam game. I'd like to get it to cycle through 5 images even while not running.
It uses only the small tile — there are no wide tiles images for this app.
Here's the code:
(function () {
"use strict";
WinJS.Namespace.define("Steam", {
launch: function launch(url) {
var uri = new Windows.Foundation.Uri(url);
Windows.System.Launcher.launchUriAsync(uri).then(
function (success) {
if (success) {
// File launched
window.close();
} else {
// File launch failed
}
}
);
}
});
WinJS.Namespace.define("Tile", {
enqueue: function initialize() {
var updaterHandle = Windows.UI.Notifications.TileUpdateManager.createTileUpdaterForApplication();
updaterHandle.enableNotificationQueue(true);
return updaterHandle;
},
update: function update () {
var template = Windows.UI.Notifications.TileTemplateType.tileSquareImage;
var tileXml = Windows.UI.Notifications.TileUpdateManager.getTemplateContent(template);
var randIndx = Math.floor(Math.random() * 5);
var randUpdatetime = 1000 * 3 * (((randIndx == 0) ? 1 : 0) + 1); // let the base image stay longer
var tileImageAttributes = tileXml.getElementsByTagName("image");
tileImageAttributes[0].setAttribute("src", "ms-appx:///images/Borderlands2/borderlands_2_" + randIndx + "_sidyseven.png");
tileImageAttributes[0].setAttribute("alt", "Borderlands 2");
var tileNotification = new Windows.UI.Notifications.TileNotification(tileXml);
var currentTime = new Date();
tileNotification.expirationTime = new Date(currentTime.getTime() + randUpdatetime);
tileNotification.tag = "newTile";
var updater = Tile.enqueue();
updater.update(tileNotification);
setTimeout('Tile.update();', randUpdatetime);
}
});
WinJS.Binding.optimizeBindingReferences = true;
var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
app.onactivated = function (args) {
if (args.detail.kind === activation.ActivationKind.launch) {
setTimeout('Steam.launch("steam://rungameid/49520");', 800);
args.setPromise(WinJS.UI.processAll().then(function () {
return WinJS.Navigation.navigate("/default.html", args).then(function () {
Tile.update();
});
}));
}
};
app.start();
})();
Notes:
The code currently does not cycle the image, instead either
apparently never changing, or after launch replacing the application
name text with a tiny view of the default image. This reverts to the
text after a short time, and the cycle may repeat. It never shows a
different image (neither in the small image it erroneously shows, nor
in the main tile).
When I run in debug and set a breakpoint at the
TileUpdater.update(TileNotification) stage, I can verify in the
console that the image src attribute is set to a random image
just as I wanted:
>>>>tileNotification.content.getElementsByTagName("image")[0].getAttribute("src")
"ms-appx:///images/Borderlands2/borderlands_2_4_sidyseven.png"
But this never actually displays on the tile.
These image files are included in the solution, and they appear in the proper directory in the Solution Explorer.
If the image src attribute is set properly in debug then the image may not have the proper "Build Action".
In the 'Properties' of each image, set "Build Action" to "Resource".

Resources