Skip to content

Instantly share code, notes, and snippets.

@ilio
Last active August 15, 2018 21:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ilio/6f4239b60b84a918ab500e2e0528c094 to your computer and use it in GitHub Desktop.
Save ilio/6f4239b60b84a918ab500e2e0528c094 to your computer and use it in GitHub Desktop.
Standalone desktop app with JavaScript, Html, Css based on latest chromium with puppeteer

Create a standalone desktop app with puppeteer example.

  1. Run npm i
  2. Run node app

For run this example without installation, run in console:
npx https://gist.github.com/ilio/6f4239b60b84a918ab500e2e0528c094

#!/usr/bin/env node
const puppeteer = require('puppeteer');
const devtools = true;
const indexUrl = 'file:///' + __dirname.replace(/\\/g, '/') + '/index.html';
const args = [
// '--start-maximized',
'--disable-infobars',
'--disable-default-apps',
'--disable-popup-blocking',
'--enable-local-file-accesses',
'--allow-file-access-from-files',
'--app=' + indexUrl
];
puppeteer.launch({headless: false, devtools, args}).then(async browser => {
let page;
const pages = await browser.pages();
for (const p of pages) {
const url = await p.url();
console.log(url);
if (url === indexUrl) {
page = p;
}
}
if (!page) {
console.error('ui page not found', indexUrl);
process.exit(1);
}
await page._client.send('Emulation.clearDeviceMetricsOverride');
let headlessBrowser;
await page.exposeFunction('sendMessage', async message => {
console.log('message', message);
switch (message.action) {
case 'open-browser': {
headlessBrowser = await puppeteer.launch({headless: true});
return;
}
case 'new-page': {
const page = await headlessBrowser.newPage();
const pages = await headlessBrowser.pages();
return {pageId: pages.length - 1};
}
case 'pages': {
const pages = await headlessBrowser.pages();
return {pages: pages.length};
}
case 'screenshot': {
const pages = await headlessBrowser.pages();
return await pages[message.pageId].screenshot();
}
case 'goto': {
const pages = await headlessBrowser.pages();
await pages[message.pageId].goto(message.url);
return;
}
default: {
return {error: 'Invalid action: ' + message.action};
}
}
});
await page.evaluate(() => {
window.onLoaded();
});
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>PuppeApp</title>
<script src="index.js"></script>
</head>
<body>
PuppeApp loading
</body>
</html>
function screenShootToUrl(screenshot) {
const blob = new Blob([Uint8Array.from(screenshot.data)], {type: 'image/png'});
return URL.createObjectURL(blob);
}
function clear(node) {
while (node.firstChild) {
node.removeChild(node.firstChild);
}
}
const onLoaded = async () => {
clear(document.body);
const send = (message) => {
console.log('send', message);
return sendMessage(message);
};
console.log('on open');
await send({
action: 'open-browser'
});
const pages = await send({
action: 'pages'
});
let pageId;
if (pages < 1) {
({pageId} = await send({
action: 'new-page'
}));
} else {
pageId = 0;
}
let screenshot = await send({
action: 'screenshot',
pageId
});
const image = new Image();
image.src = screenShootToUrl(screenshot);
document.body.appendChild(image);
await send({
action: 'goto',
url: 'https://pptr.dev/',
pageId
});
screenshot = await send({
action: 'screenshot',
pageId
});
image.src = screenShootToUrl(screenshot);
await new Promise(r => setTimeout(r, 3000));
screenshot = await send({
action: 'screenshot',
pageId
});
image.src = screenShootToUrl(screenshot);
};
window.onLoaded = onLoaded;
/**
* @callback sendMessage
* @param {{action:String}} message
*/
{
"name": "puppe-app",
"version": "1.0.0",
"description": "",
"main": "app.js",
"bin": "./app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"puppeteer": "^1.6.2"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment