Open matthewruzzi opened 3 years ago
@mattruzzi awesome! Following up soon with my thoughts.
@mattruzzi @TheCleric thanks! Here are my thoughts. Before you read, I want to stress out that the size of this brain dump shouldn't make it overwhelming/intimidating. I just happen to have seen this idea bounced a lot, so naturally I have a few ideas. I have zero expectations about this, and me suggesting something doesn't mean I'd expect it to be here yesterday on a first release. Alright, here it goes:
Your interactive prompt with a list & autocomplete looks rad, and would help a lot making Nativefier easier to use for people not familiar with the command-line.
I don't think your new interactive UI obviates the need for current Nativefier; some users will want control over specific flags, add custom --inject
s, etc. Use cases I can picture:
Will build Google Docs. Are there extra flags you would like to add?
catalog
domain, and offer to use its config: You requested to build an app on mail.google.com. Nativefier-catalog has a config for this site, named "Gmail", with an icon, options [counter, bounce], and injectable CSS+JS. Do you want to use it?
. This "do you want to use it" could then be selective: a user could opt in to just the icon, or icon + one JS but no custom CSS, etc.This I think lets us answer the question "Where should this client live? Should it keep being a wrapper (like your current project is, @mattruzzi), or should the client & logic live inside Nativefier proper?". My thinking about this is twofold:
I agree that the json file should live in its own repo, to let it be its own thing, with maybe its own maintainers helping digest quickly community contributions if the volume increases, and yes it opens the way to other clients.
CSS & JS injects ("userscripts") must live alongside in the repo, yes. No gist URLs, especially since we'd be giving these userscripts more visibility and accessibility, they should live in the repo, for security.
This repo should absolutely replace the severely-uncared-for nativefier-icons repo and its gitcloud client, yes. These things are two of the weirdest things in Nativefier, leftovers of Jia (Nativefier creator) doing dubious things as novice dev, generating HTML using Jekyll, that is later on parsed in gitcloud (using html-parsing npm dep cheerio
) to grab icons, rube-goldberging the act of fetching data, that would be a simple JSON.parse if it were JSON to begin with. It's nonsensical and Jia himself jokes about it 😄. Yes, let's merrily burn this and replace it all with a simple JSON. While doing this, we should merge the mountain of icons accumulating in open PRs and that I'm ignoring 😬.
The new repo of configs could be entirely JS-less / only a JSON (plus CSS+JS injects). Contributors would submit PRs modifying the JSON (it's reasonable, JSON is readable) and potential css/js injects. We'd have GitHub Actions-based CI that does sanity checks, for maximally automatic checks and minimal PR-reviewing overhead:
In your RFC, you indexed the JSON by an "app key/token". My initial thinking was to index by domain, but I may be wrong. Domain/subdomain stuff might be tricky, change with time, so maybe it's not a good idea and you're right with an arbitrary key. Maybe this key should be the app name shown to the user, though? It's totally fine to have any character and spaces in a JSON key.
Then, I was imagining something like what you're suggesting:
url
options
(with nested options like the counter
and bounce
you already mention, but any other options should be usable here)Options should probably not be raw key: value
couples, we'll definitely want an object per option, letting us add extra metadata about each option. I imagine we could want each option to have:
default
/ recommended
flag, conditioning whether this is used by default. This would let users contribute flags/injects that we know are not desirable for everybody, but are still nice to offer, ready for cherry-picking in interactive mode.description
field, helping explain to users what some options do (e.g. if WhatsApp has 6 JS injects offered, we want to let users know what each of them does)As experimentation happens in your interactive client, please keep an eye on npm dependencies, and align them with Nativefier's. If picking a dep, review its activity, popularity, and sub-dependencies weight; I want to keep Nativefier dependencies weight minimal. I'm not putting a hard no on extra deps, and for example the interactive prompt deserves a dep and enquirer
looks like a sane choice, but for example I'd ask you replace meow
with commander
we already use.
I'm working on a adding a way of specifying css/js injects in nactivefier-catalog but I am having problems related to #458 when passing multiple files to inject where it seems to overwrite it each time I pass it a --inject
so only the last one is actually included.
Follow-up of https://github.com/nativefier/nativefier/issues/1147 : udemy.com
is a case where the catalog should suggest using our flag --widevine
@ronjouch did you mean to close this?
@ronjouch did you mean to close this?
Whoopsie 😄. No! Just meant to add the link to #1147. Thanks.
I was thinking for every site that is in nativefier-catalog we also could put it in a markdown file with explanations for all of the flags used so that the json schema for nativefier-catalog can remain clean and not have the explanations for the flags in it.
Edit: actually I changed my mind I think we just need a whole new schema that includes proper support for explanations See https://github.com/nativefier/nativefier/issues/1130#issuecomment-830675601
Eventually when this gets combined with the main nativefier codebase maybe we could make it so that if you ran nativefier without passing a site or any flags it could enter an interactive mode asking you to choose from an existing site or build an app for your own with an interactive wizard asking you questions eg.
$ nativefier
(we could have some way of asking if you want to create your own custom config for the app or if you want to use a premade one here, or we could default to the custom app maker if the user types an app that is not in the catalog})
(custom app maker:)
? What is the url to that website you want to nativefy … https://mail.google.com/
? What name should the app be saved as … Gmail
? Do you want a counter (insert explanation of counter?) (y/n) … y
? Do you want to use disk cache (insert other explanation) (y/n) … n
I was thinking for every site that is in nativefier-catalog we also could put it in a markdown file with explanations for all of the flags used so that the json schema for nativefier-catalog can remain clean and not have the explanations for the flags in it.
@mattruzzi I disagree: I don't see anything dirty about having a comment
key for each catalog entry. I'd prefer it to an explosion of small markdown files. Everything remains closely at the same place, obvious to understand & maintain without having to go fish for a separate file.
To which someone may argue "But in a md file we'll have space, access to formatting, and multiline". I'm not sure we really need these, and being "cramped" in a JSON file will push us to remain concise, and that's good.
In the current version the json is directly passed to nativefier. I am working on adding injects to it and have added the following to the schema:
"example": {
"name": "Example",
"targetUrl": "https://example.com/",
"injects": {
"does something ": {
"path": "./injects/example/do-thing1.js",
"desc": "does something "
},
"does something else": {
"path": "./injects/example/do-thing2.js",
"desc": "does something else"
},
"important fix": {
"path": "./injects/example/fix-thing3.js",
"desc": "fix something else"
}
}
}
I am using the following code prompt for the userscripts and remove the unused ones in memory at runtime:
if (apps[name].hasOwnProperty("injects")) {
var injectsList = Object.keys(apps[name].injects);
console.log(injectsList)
const prompt = new MultiSelect({
name: 'injects',
message: 'Available userscripts/stylesheets',
hint: '(Use <space> to select, <return> to submit)',
choices: injectsList
});
prompt.run()
.then(answer => runNativefier( getOptions(name, answer)))
.catch(console.error);
}}
function getOptions(name = {}, injects = {}) {
var toInject = [];
injects.forEach(element => {
toInject.push(apps[name].injects[element].path)
});
var options = apps[name];
delete options.injects; // delete the list of available injects (see json above)
options.inject = toInject; // add the only ones that were selected in the prompt to the options passed to nativefier
console.dir(options);
return options;
}
function runNativefier(config = {}) {
nativefier(config, function (error, appPath) {
if (error) {
console.error(error);
return;
}
console.log("App has been nativefied to", appPath);
});
}
The current way I am adding extra features to the schema is by adding code that will translate them to standard nativefier configs at runtime (see above where I translate "injects" (my schema addon for injects) to "inject" (a nativefier flag).
I don't know how scalable this is for adding more options/descriptions for options to the schema.
What do you think of the following schema? Do you have any different ideas?
"example": {
"App name": {"name": "Example"},
"App Url": {"targetUrl": "https://example.com/"},
"Fix something with inject": {"inject": "injects/example/fix-thing3.js"},
"Do something else with inject": {"inject": "injects/example/do-thing2.js"},
"Use a Widevine-enabled version of Electron to allow video playback":{"widevine":true},
"Explanation": {"flag":"value"}
}
We could provide some kind of interactive prompt that would allow users to toggle/edit each option.
What do you think of the following schema? Do you have any different ideas?
@mattruzzi I like the new schema you're proposing:
"comment"
or "catalogComment"
key, that is used by the interactive UI to explain the solution in detail and e.g. link to discussion in an issue, if users ask for details. This key would be of course stripped when passing flags to Nativefier."solutions"
(or "options"
) root key. If we don't do such nesting, we'll have to allow arbitrary keys at the root, and I don't like that as I'd like to be able to validate required keys like "name"
and "url"
(see next comment)."App name"
and "App Url"
spelled like the rest of the "bag of cherry-pickable options". These two (and maybe more) aren't like the others, in that they're mandatory. Feels like the keys should be standard to "name"
and "url"
, to offer a consistent experience and to do a good validation job. But I like that they use the same "it's just a define/override" approach."example"
), name (
"Example"), and
url(
"https://example.com/"`). Makes sense?We could provide some kind of interactive prompt that would allow users to toggle/edit each option.
Yeah, that fits nicely with the workflow you're suggesting.
"example": {
"name": "Example",
"targetUrl": "https://example.com/",
"additionalOptions": {
"Allow video playback": {
"flags": {"inject": "injects/example/fix-video.js", "widevine":true},
"default": true,
"comment":"Longer explanation"},
"linktomoreinfo?" : "https://explanation.example/something"
}
}
Should the link be part of the comment or a different field? Maybe we could standardize it and have a GitHub issue for each one?
@ronjouch I just committed some Proof of concept code for the new schema.
https://github.com/mattruzzi/nativefier-catalog
I'm starting to see a lot of duplication in it as a lot of apps use the same fix (all Google apps need user agent fix). Should we add a section for common fixes that can be referenced from multiple apps?
I'm starting to see a lot of duplication in it as a lot of apps use the same fix (all Google apps need user agent fix). Should we add a section for common fixes that can be referenced from multiple apps?
@mattruzzi yeah indeed, a section for common fixes (that are reference-able-by-name by apps) seems legit. So:
"__commonOptions"
key containing an object of the common options, named e.g. "useragent-firefox"
in your case.additionalOptions
we'd have something like importedOptions
(or referencedOptions
? or libraryOptions
? , and in your case it would be [{ "libraryOptionName": "useragent-firefox", "default": true }]
. Something like this?@ronjouch @TheCleric I noticed there is another very similar project to both nativefier-catalog and nativefier-gui. https://github.com/vinifmor/bauh#native-web-applications https://github.com/vinifmor/bauh-files/blob/master/web/suggestions.yml https://github.com/vinifmor/bauh-files/tree/master/web
I don't know if this changes anything.
yaml
instead of json
?Could we collaborate with them and share the catalog of apps and fixes?
@mattruzzi two thoughts:
nativefier-icons
repo) and use icons from the weboptions
they set are debatable. As discussed above, it feels preferable to only set the minimal amount of options to make a site work at all (e.g. widevine, inject, useragent, etc) but let cosmetic (maximized, start in tray, etc) things up to everybody's taste, maybe offering it but not defaulting to it.Sooo, if I were in your shoes I'd err on the side of doing my thing, keeping in mind the features they support, and then later inviting them to contribute.
Is there any reason switch to
yaml
instead ofjson
?
I was suggesting json
initially for absolute maximal platform (node/js) platform friendliness at the detriment of maintainer friendliness (see HACKING.md / point 4. about deps). That being said, for such a database that is bound to be frequently modified by humans, it's probably reasonable to use YAML, as it's much more readable. Also, I see that yaml and js-yaml both seem well-maintained and are very light on dependencies (0 for one, 1 for the other). So, no objection to YAML.
If we use yaml, should we require keys with spaces to be quoted? It isn't required by yaml, but I don't know if there is any reason we would want to do it. For things like Change user agent to prevent Google from blocking access
, instead of having them in this file as keys, we might want to move them to a different file and give them simpler names to reference by like google-ua-fix
or something, and then add a key like referencedOptions: [ google-ua-fix ]
I used an online json to yaml converter and got this:
gmail:
name: Gmail
targetUrl: https://mail.google.com/
additionalOptions:
Show counter on app icon in dock:
flags:
counter: true
bounce: true
default: true
comment: (macOS only) Use a counter that persists even with window focus for
the application badge for sites that use an "(X)" format counter in the page
title (i.e. Gmail).
Change user agent to prevent Google from blocking access:
flags:
user-agent: firefox
default: true
comment: 'Note: lying about the User Agent is required, else Google will notice
your "Chrome" isn''t a real Chrome, and will refuse access.'
google-meet:
name: Google Meet
targetUrl: https://meet.google.com/
additionalOptions:
Change user agent to prevent Google from blocking access:
flags:
user-agent: firefox
default: true
comment: 'Note: lying about the User Agent is required, else Google will notice
your "Chrome" isn''t a real Chrome, and will refuse access.'
google-docs:
name: Google Docs
targetUrl: https://docs.google.com/
additionalOptions:
Change user agent to prevent Google from blocking access:
flags:
user-agent: firefox
default: true
comment: 'Note: lying about the User Agent is required, else Google will notice
your "Chrome" isn''t a real Chrome, and will refuse access.'
google-sheets:
name: Google Sheets
targetUrl: https://sheets.google.com/
additionalOptions:
Change user agent to prevent Google from blocking access:
flags:
user-agent: firefox
default: true
comment: 'Note: lying about the User Agent is required, else Google will notice
your "Chrome" isn''t a real Chrome, and will refuse access.'
google-slides:
name: Google Slides
targetUrl: https://slides.google.com/
additionalOptions:
Change user agent to prevent Google from blocking access:
flags:
user-agent: firefox
default: true
comment: 'Note: lying about the User Agent is required, else Google will notice
your "Chrome" isn''t a real Chrome, and will refuse access.'
example2:
name: Example2
targetUrl: https://example.net/
additionalOptions:
Allow video playback:
flags:
inject: injects/example/fix-video.js
widevine: true
default: true
comment: Longer explanation
@ronjouch Should I use the .yml
or .yaml
extension?
@mattruzzi
If we use yaml, should we require keys with spaces to be quoted? It isn't required by yaml, but I don't know if there is any reason we would want to do it.
Quoting not necessary indeed, and given the main goal of YAML is to be human-friendly, I'd err on the side of omitting them.
For things like
Change user agent to prevent Google from blocking access
, instead of having them in this file as keys, we might want to move them to a different file and give them simpler names to reference by likegoogle-ua-fix
or something, and then add a key likereferencedOptions: [ google-ua-fix ]
👍, I like the idea of a "named options referrable to" section, containing common fixes that we can refer to by name.
Should I use the
.yml
or.yaml
extension?
Dunno; I don't see any objection to one or the other.
@ronjouch I noticed another similar project made by @Nickersoft. I don't know if that changes anything about our plans. https://github.com/Nickersoft/hop
@ronjouch Wow it's been a year! I just pushed code that switches the apps file from json to yaml. We could use yaml anchors to fix the duplication. I switched the Google user agent fix to use them. https://www.educative.io/blog/advanced-yaml-syntax-cheatsheet#anchors
gmail:
name: Gmail
targetUrl: https://mail.google.com/
additionalOptions:
Show counter on app icon in dock:
flags:
counter: true
bounce: true
default: true
comment: (macOS only) Use a counter that persists even with window focus for
the application badge for sites that use an "(X)" format counter in the page
title (i.e. Gmail).
Change user agent to prevent Google from blocking access: &google-ua-fix
flags:
user-agent: firefox
default: true
comment: 'Note: lying about the User Agent is required, else Google will notice
your "Chrome" isn''t a real Chrome, and will refuse access.'
google-meet:
name: Google Meet
targetUrl: https://meet.google.com/
additionalOptions:
Change user agent to prevent Google from blocking access:
flags:
user-agent: firefox
default: true
comment: 'Note: lying about the User Agent is required, else Google will notice
your "Chrome" isn''t a real Chrome, and will refuse access.'
google-docs:
name: Google Docs
targetUrl: https://docs.google.com/
additionalOptions:
*google-ua-fix
google-sheets:
name: Google Sheets
targetUrl: https://sheets.google.com/
additionalOptions:
*google-ua-fix
google-slides:
name: Google Slides
targetUrl: https://slides.google.com/
additionalOptions:
*google-ua-fix
example2:
name: Example2
targetUrl: https://example.net/
additionalOptions:
Allow video playback:
flags:
inject: injects/example/fix-video.js
widevine: true
default: true
comment: Longer explanation
Nativefier catalog is a catalog of configs for nativefier apps. It currently stores app configs as a json file with the following structure:
The options are passed directly to the nativefier Programmatic API. The UI is built using enquirer autocomplete prompts and shows a searchable list of available app configs.
I also could make a graphical way of viewing it to go along with my nativefier-gui project.
The json file is curently stored in the same repo as the code but should probably be moved to a different repo to seperate app adition prs from code pr like how Homebrew/brew (package manager code) is separate from Homebrew/homebrew-core (package repo). Having a repo of just configs could allow multiple different clients to all access it and share the same configs. (the default nativefier cli (see question 3), nativefier-catalog cli, nativefier-gui, something else?... )
Questions
Should this integrate with the existing nativefier-icons repo somehow?
Should there be a folder for userscripts to go along with apps.json or should they be linked as gist urls?
Should there be some kind of url (regex?) matching so the configs are used by default on the standard nativefier cli? (eg. use counter by default on gmail because the gmail entry in apps.json has counter enabled.
Any other ideas? Comments? Questions? Different directions?
@ronjouch @TheCleric