gluon-framework / gluon Goto Github PK
View Code? Open in Web Editor NEWA new framework for creating desktop apps from websites, using system installed browsers and NodeJS
Home Page: https://gluonjs.org
License: MIT License
A new framework for creating desktop apps from websites, using system installed browsers and NodeJS
Home Page: https://gluonjs.org
License: MIT License
Chrome's window controls overlay defines 4 CSS env variables which are insanely useful for detecting the titlebar size, those are:
left: env(titlebar-area-x, 0);
/* Use the environment variable for the top anchoring with a fallback. */
top: env(titlebar-area-y, 0);
/* Use the environment variable for setting the width with a fallback. */
width: env(titlebar-area-width, 100%);
/* Use the environment variable for setting the height with a fallback. */
height: env(titlebar-area-height, 33px);
currently gluton doesn't define these, would be cool if it did
Is this possible to support the minimize on close
functionality? Not necessarily moving to the menu bar, but at least minimizing the dock when clicking the close button? I miss this functionality, because I want the app to display content immediately when activated, and I don't wait for the page to load. I am looking for something similar to this functionality in Electron
mainWindow.on('close', function (event) {
if(!application.isQuiting){
event.preventDefault();
mainWindow.hide();
}
return false;
});
510 git clone https://github.com/gluon-framework/examples.git
511 ll
512 cd examples
513 ll
514 cd gluworld
515 npm install
516 node .
517 h
dean@pop-os ~/.../examples/gluworld (main) $ node .
[Gluon] starting browser...
file:///home/dean/projects/gatsby/LIBRARY/gluon/examples/gluworld/node_modules/@gluon-framework/gluon/src/index.js:198
const [ browserPath, browserName ] = await findBrowserPath(forceBrowser, forceEngine);
^
TypeError: (intermediate value) is not iterable
at startBrowser (file:///home/dean/projects/gatsby/LIBRARY/gluon/examples/gluworld/node_modules/@gluon-framework/gluon/src/index.js:198:40)
at async Module.open (file:///home/dean/projects/gatsby/LIBRARY/gluon/examples/gluworld/node_modules/@gluon-framework/gluon/src/index.js:255:19)
at async file:///home/dean/projects/gatsby/LIBRARY/gluon/examples/gluworld/index.js:17:3
Node.js v18.12.1
dean@pop-os ~/.../examples/gluworld (main) $
Happy to help any way I can.
Sample app works great on my desktop computer but crashes instantly on my laptop. Same version of Nodejs (16) and both Windows 11 machines. Any ideas would be much appreciated!
[Gluon] starting browser...
[Gluon] found browser chrome (chromium based) at path: C:\Program Files\Google\Chrome\Application\chrome.exe
[Gluon] data path: C:\Users\zanec\OneDrive\Documents\GitHub\gluworld\gluon_data\chrome
[Gluon] connecting to CDP over stdio pipe...
[Gluon] browser: Chrome/110.0.5481.178
[Gluon] acquiring target...
.[Gluon] pipe read closed
file:///C:/Users/zanec/OneDrive/Documents/GitHub/gluworld/node_modules/@gluon-framework/gluon/src/launcher/inject.js:77
Window.close();
^
ReferenceError: Cannot access 'Window' before initialization
Apps using Local (giving a path to Gluon.open()
) should have a way of having CSP easily by default, without being annoying or intrusive for development.
My current thoughts is to have a csp
option to Gluon.open()
for Local, with options like:
none
: No (Gluon set) CSPlocal
: Gluon Local-only CSP<custom>
: Sets CSP header to other custom value givenIs there an example somewhere of how to package a Gluon app as a native application?
Window/top-level navigations should be restricted by default. This will be handled by the allowRedirects
Window option, with the values:
false
: No redirects are allowed.same-origin
: Redirects are allowed if the redirect URL is the same origin (as the URL given to open()
).true
: All redirects are allowed. Not recommended.Add a way to override the useragent
For example:
import * as Gluon from '@gluon-framework/gluon';
const Window = await Gluon.open('https://gluonjs.org', {
userAgent: 'Mozilla/5.0 (Android 13; Mobile; LG-M255; rv:109.0) Gecko/109.0 Firefox/109.0'
});
Env: Local
Need Update Node.js to version 16+
https://github.com/gluon-framework/gluon/blob/main/src/api/v8Cache.js#L25
Or update code to
urls ??= await getScriptUrls(includePreload);
// urls = urls ?? await getScriptUrls(includePreload);
Currently gluton hardcodes a few browsers, however is someone is using a different browser like for example a custom chromium build or browsers like opera or brave this fails.
A decent solution would be:
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\Shell\Associations\URLAssociations\https\UserChoice
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\${ProgID}\shell\open\command
for windows the best module for navigating regedit seems to be winreg, not sure how to do this on linux/macos
Hey there,
Just wondering if Gluon has any affiliation with Gluon for Micro.blog? It's been around for some time.
If not, perhaps you can make it clear it's not affiliated? Was a little confused at first.
Open to suggestions.
– Vincent
Perhaps it could give them an alert telling them they need to install XYZ, and try to open the app in an available browser if its just opening a url like in glucord?
Use require.main.filename
as basis, with a central gluon_data
(?) directory with subdirs for Chromium/Firefox (not per browser, just per type?),
I'm getting this error message:
[Gluon] warn: CDP reply error. method: Page.stopLoading error: {
message: 'Page.stopLoading',
data: 'RemoteAgentError@chrome://remote/content/cdp/Error.sys.mjs:20:5\n' +
'UnknownMethodError@chrome://remote/content/cdp/Error.sys.mjs:103:7\n' +
'execute@chrome://remote/content/cdp/domains/DomainCache.sys.mjs:92:13\n' +
'receiveMessage@chrome://remote/content/cdp/sessions/ContentProcessSession.sys.mjs:79:45\n'
}
My code is:
import * as Gluon from '@gluon-framework/gluon';
const Window = await Gluon.open('https://www.fimfiction.net', {});
Is this a problem with the site or something I'm doing wrong?
use code like
// backend
const win = await Gluon.open('index.html', {
windowSize: [800, 500]
});
win.ipc.test= async (data) => {
//....
})
// frontend
await Gluon.ipc.test('Message!');
invoke success on first run but after refresh page invoke will got error Uncaught (in promise) TypeError: Gluon.ipc.test is not a function
Supporting webmanifest could be cool, this would allow for stuff like custom titlebar colors, icons, shortcuts etc, I can see 2 ways of implementing this:
chrome_proxy.exe --profile-directory=Default --app-id=cinhimbnkkaeohfgghhklpknlkffjgod
, this would automatically support all webmanifest features, even ones enabled experimentally with flags and fix stuff like #12Is it possible to bundle a web app (for instance, a Vue SPA) rather than bringing in a web app/page via an HTTPS URL via Gluon.open()
?
Very long term thing. Move from CDP to WebDriver BiDi for browser control/communication.
Depends on many various (niche) upstream BiDi features for a full switch for most compat/wants, in order of importance:
Wishlist (probably never going to be added to BiDi):
Is there any way to print silently bypassing the OS prompt? Electron and NWJS have it. It would be a great addition and surpass Tauri and Neutralinojs.
:-)
Looks great BTW.
When following the browser priority order and a browser fails in some way (eg. Thorium on Windows), automatically fallback to any other browser(s) present on the system.
I installed my browser (firefox and chrome) via homebrew, when i run the gluworld example. i got error:
❯ node .
[Gluon] starting browser...
file:///Users/d/Data/labs/glutonis/node_modules/@gluon-framework/gluon/src/index.js:86
const [ browserPath, browserName ] = await findBrowserPath(forceBrowser);
^
TypeError: (intermediate value) is not iterable
at startBrowser (file:///Users/d/Data/labs/glutonis/node_modules/@gluon-framework/gluon/src/index.js:86:40)
at async Module.open (file:///Users/d/Data/labs/glutonis/node_modules/@gluon-framework/gluon/src/index.js:114:19)
at async file:///Users/d/Data/labs/glutonis/index.js:38:19
How to build or compile to .exe ?
When running the compiled exe of the deno branch, it creates 'gluon_data/chrome' in the physical path of the developer's project. This behavior is inconvenient when distributing the exe to third parties.
After reviewing the implementation in 'gluon/src/index.js', it was found that the browser's temporary directory is created using fileURLToPath(import.meta.url) and files are placed there. import.meta.url contains the physical path of the project at compile time in the compiled exe. Therefore, the behavior is determined by where the developer clones the gluon project.
I would like to request that 'gluon_data/chrome' be created in the current directory instead.
Here are relevant lines from 'gluon/src/index.js' :
(Line 17)
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
(Line 147)
const ranJsDir = !Deno.args[0] ? __dirname : (Deno.args[0].endsWith('.js') ? dirname(Deno.args[0]) : Deno.args[0]);
const getDataPath = browser => join(ranJsDir, 'gluon_data', browser);
Thank you for your attention to this issue.
Full output:
borkdude@m1 /tmp $ git clone https://github.com/gluon-framework/examples.git
Cloning into 'examples'...
remote: Enumerating objects: 48, done.
remote: Counting objects: 100% (48/48), done.
remote: Compressing objects: 100% (34/34), done.
remote: Total 48 (delta 12), reused 44 (delta 11), pack-reused 0
Receiving objects: 100% (48/48), 260.47 KiB | 1.65 MiB/s, done.
Resolving deltas: 100% (12/12), done.
borkdude@m1 /tmp $ cd examples
borkdude@m1 /tmp/examples (main) $ cd gluworld
borkdude@m1 /tmp/examples/gluworld (main) $ npm install
added 2 packages, and audited 3 packages in 4s
found 0 vulnerabilities
borkdude@m1 /tmp/examples/gluworld (main) $ cat package.json
{"type":"module","dependencies":{"@gluon-framework/gluon":"^0.9.0"}}%
borkdude@m1 /tmp/examples/gluworld (main) $ node .
[Gluon] starting browser...
[Gluon] checking if chrome exists: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome true
[Gluon] browser path: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[Gluon] data path: /private/tmp/examples/gluworld/node_modules/@gluon-framework/gluon/chrome_data
[Gluon] connecting to CDP over stdio pipe...
[Gluon] browser: Chrome/108.0.5359.124
file:///private/tmp/examples/gluworld/node_modules/@gluon-framework/gluon/src/launcher/inject.js:22
targetId: target.targetId,
^
TypeError: Cannot read properties of undefined (reading 'targetId')
at default (file:///private/tmp/examples/gluworld/node_modules/@gluon-framework/gluon/src/launcher/inject.js:22:24)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async default (file:///private/tmp/examples/gluworld/node_modules/@gluon-framework/gluon/src/launcher/start.js:36:10)
at async default (file:///private/tmp/examples/gluworld/node_modules/@gluon-framework/gluon/src/browser/chromium.js:11:10)
at async startBrowser (file:///private/tmp/examples/gluworld/node_modules/@gluon-framework/gluon/src/index.js:113:18)
at async Module.open (file:///private/tmp/examples/gluworld/node_modules/@gluon-framework/gluon/src/index.js:131:19)
at async file:///private/tmp/examples/gluworld/index.js:38:19
Node.js v17.8.0
Second (major) iteration of IPC API. I dislike having to use an event-based system, I think there should be a wrapper for most developers to use which allows seemingly exposing functions into the web context, either via a function or using a setter in the IPC API object.
Window.ipc.expose('key', (arg1, arg2) => {});
Window.ipc.expose({
key: (arg1, arg2) => {}
});
Window.ipc.key = (arg1, arg2) => {};
// node
Window.ipc.myFunction = () => {}; // or
Window.ipc.expose('myFunction', () => {}); // or
Window.ipc.expose({
myFunction: () => {}
});
// web
Gluon.ipc.myFunction();
Window.ipc.unexpose('key');
delete.Window.ipc.key:
delete Window.ipc.key; // or
Window.ipc.unexpose('key');
I'm leaning towards using a setter, but it might seem strange/wrong to some people (for the first time). Please comment with which you prefer and/or opinions on all!
Just another fork of Firefox
https://pulsebrowser.app/
located at "C:\Program Files\Mozilla Developer Preview\pulse-browser.exe" on windows
Gluon should provide window events that will trigger when window is resized/maximized/closed etc.
For example here's a list of all available BrowserWindow events in Electron. This could help changing the default behaviour for window management as seen in #33
Meta tracking issue for security in general. Whilst Gluon is already probably one of the most secure frameworks of it's kind, we should still be constantly focused on improving it as best as possible by default.
Edit: This got long, sorry for the novel!
Happy to break up this issue and close it in favor of multiple issues if any of the ideas seem valuable, or move it to Discord for a more live discussion, but it seemed helpful to present as a single concept first!
We take a very similar approach to Gluon's IPC methods / stores in our Electron app with a homebrew state management library, and all of the concepts below relate to the challenges we ran into as our code base began to scale and design decisions we made to make IPC methods / stores work better together in large codebases. I thought some of the ideas could be helpful as the Gluon API continues to evolve!
First: I love the direction of this new framework – thank you so much for creating it! Very excited to watch (and hopefully help with?) how it develops.
We build a fairly complex Electron app, and I'd love to evaluate migrating to Gluon in the near future as a lighter weight option once it hits 1.0.0.
Specifically, your built-in exposed IPC methods and state objects (forgive me, I'm going to use "state" and "store" interchangeably here), resonate perfectly with how we've ended up architecting data flow in our app. The ergonomics of IPC methods / state over Electron's more 'events only' model is something we've focused closely on too.
We actually went as far as to develop a framework that jams something similar on top of Electron's IPC events API: https://github.com/universe/electron-state
I wanted to drop a flew thoughts here and see if some of the use cases (and growing pains) we encountered using IPC methods / state at-scale might be helpful with API design.
The three biggest problems we ran into when building out our own methods / state model as our app scaled up included:
If we adopted Gluon right now, I would see us running into similar issues since all shared state and methods exist on the singe IPC object!
We built around these issues in electron-state by leaning on class syntax to couple and namespace relevant methods and state, in a way that automatically emits events from the class singleton whenever data changes so we can easily wire up any frontend framework to respond to state updates.
There are three independent ideas from our stab at IPC methods/state that may be interesting to take into account for Gluon, and conveniently can all be considered independently!
ipc.store
. Perhaps only allow writing user-defined methods to something like ipc.methods
(to mirror the store API) instead. If you encourage users to write directly to the main IPC object, once you hit 1.0.0 you can not add any more framework methods without bumping a major version: you might accidentally be overwriting a user created function! This will give Gluon more API flexibility in the long run, and allow new IPC helper methods on minor version bumps instead of major version bumps. This will also make the inevitable Typescript types for the library (written by you, or a 3rd party) much more managable.ipc.store
is modified! I see you trigger an un-document 'web store change'
IPC event to write data back to the main process from the renderer, but more granular event triggers would allow front-end frameworks to create hooks into state changes so the UI can be more easily reflected. It may be helpful to create a separate event channel than the current user-created event Gluon.ipc.on
API for these automated events, in service of isolating the event namespaces as well. (Humble recommendation for all internal data sync events too – currently, if I for some reason wanted to create both a myFun
and exposed myFunc
function, I think things would not run as expected!)electron-state
module does this by co-opting JS class syntax for for very dev-friendly namespaces, and the base class that these IPC state objects extend from is responsible for wiring up all the IPC sync event channels, ensuring it remains a singleton, etc. Notably though, this couples the concept of state and methods. I think this is a feature (it mirrors the "data down actions up" principle we find in frontend component design!) but a lower level, less opinionated, version of this for Gluon may simply allow for named IPC method collections or state objects that users can create and retrieve through the IPC API. E.g. Gluon.ipc.getState('my-state-object-name')
or Gluon.ipc.getMethods('my-functionality-collection-name')
Let me know if any of these are worth exploring – I'm always happy to chat about them more if they align with how you imagine Gluon evolving!
Gluon doesn't provide a way to implement single instance lock in apps that use it. Consequently, clicking on the app shortcut starts another instance of the app. This could be unwanted behavior by many developers
Could use similar approach as Electron
Hi, I was trying to use Gluon on my existing node.js cli application I tried to run gluon like this but when I try that. I get this error. What should I do if I don't wanna gluon as started point of node.js.
https://app.gluon/
HTTP ERROR 404
index.js
import { gluon } from "./glu.js";
const gluon1 = gluon();
glu.js
import * as Gluon from "@gluon-framework/gluon";
export const gluon = async () => {
const browsers = process.argv.slice(2).filter((x) => !x.startsWith("-"));
if (browsers.length > 0) {
// use argv as browsers to use
for (const forceBrowser of browsers) {
await Gluon.open("index.html", {
windowSize: [800, 500],
forceBrowser,
});
}
return;
}
await Gluon.open("index.html", {
windowSize: [800, 500],
});
};
Dropped a demo code to try proof of problem. Little edited version of gluworld.
Electron allows developers to spawn the window at specific X/Y on the screen, an api to gather current position of the window would be also useful. An implementation respecting multi-monitor setups should be considered.
Example:
const Window = await Gluon.open('https://gluonjs.org', {
x: 360,
y: 200
});
Window.ipc.getWindowX = () => {
return Window.Page.x;
};
Window.ipc.getWindowY = () => {
return Window.Page.y;
};
Some of us gluon enjoyers want less RAM but we can't live in a perfect tauri world, gluon is the least worst alternative from avoiding JavaScript completely.
Some of us want performance, so why not recommend using Thorium since it's a fast browser (not as secure as hardened firefox but, it's not like you're gonna browse the web with a gluon app, are you 🤣?)
Gluon checks for chrome binaries called chrome
and chrome-canary
.
If google chrome bin packages have been installed from the AUR, these are called google-chrome-stable
and google-chrome-unstable
.
Currently gluton doesn't inherit the source browsers flags, this is a major issue for browsers like brave, which inherently don't support some API's like FileSystemAccess or LocalFontAccess, which need to be enabled via flags, gluton should either inherit those flags, or allow flags to be enabled via launch options
Proposal for adding a "Store" to IPC, accessible to both Node and Web, for easily sharing common data between them. Not persistent. Example usage:
Web would fetch config key from IPC when gotten, hence being async.
// Node backend
Window.ipc.store.config = {
env: 'prod'
};
// Web frontend
const runningEnv = (await Gluon.ipc.store.config).env;
Web would be sent updated Store key and values whenever they are set, hence being sync.
// Node backend
Window.ipc.store.config = {
env: 'prod'
};
// Web frontend
const runningEnv = Gluon.ipc.store.config.env;
// Node backend
Window.ipc.store.set('config', {
env: 'prod'
});
// Web frontend
const runningEnv = (await Gluon.ipc.store.get('config')).env;
// Node backend
Window.ipc.store.set('config', {
env: 'prod'
});
// Web frontend
const runningEnv = Gluon.ipc.store.get('config').env;
Please comment with which proposal(s), you prefer! Open to implementing 1/2 and 3/4.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.