marp | style |
---|---|
true |
/*@import url('https://fonts.googleapis.com/css2?family=Cantarell:ital,wght@0,400;0,700;1,400;1,700&display=swap');
@import url('https://cdn.jsdelivr.net/npm/hack-font@3/build/web/hack-subset.css');*/
@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css');
section {
font-family: Cantarell;
}
code {
background-color: #d8d8d8);
border-radius: 5px;
color: black;
font-family: Hack
}
.hljs-params { color: #8f5902; }
.hljs-built_in { color: #d73a49; }
|
Philip Chimento • ptomato • @therealptomato Linux Application Summit, May 13, 2021
- I maintain GJS (GNOME JavaScript)
- This talk is a bit of an experiment for me
- Can web JS programmers ramp up quickly on writing a desktop app?
- For JavaScript developers and enthusiasts
- who are curious about writing a desktop app
- A walk through creating and publishing a desktop app in JS
- Technologies: GJS, GTK, Flatpak, Flathub
- A slide deck that you can read later
- A step-by-step tutorial on how to write an app
- There's already a good one on gjs.guide
- Presented by an experienced web developer
- Can also use gtk-js-app
- a Meson build system
- a placeholder icon
- resource bundles
- a
.desktop
file - a settings schema
- an AppStream meta info file
- infrastructure for i18n
- skeleton code
- a Flatpak manifest
- Meson is probably a good one to stick with
- You will need it if your app ever includes any C code
- Coming from JS development you still might want something more familiar
$ yarn init
"scripts": {
"prebuild": "test -d _build || meson _build",
"build": "ninja -C _build",
"start": "meson compile -C _build devel",
"test": "meson test -C _build"
}
$ yarn build
$ yarn start
$ yarn add --dev prettier eslint eslint-config-prettier
"lint": "eslint . --fix && prettier --write ."
- You can write in TypeScript, it mostly works
- Or write JS with type annotations in comments and use TypeScript to typecheck
- Thanks to the hard work of Evan Welsh
- Bundlers are probably not needed
- Tree shaking can be useful
- use e.g. find-unused-exports
- Minifiers are probably not needed
- Babel probably works
- XML-CSS-JS is like the trinity of HTML-CSS-JS
- Alternative is to build your UI in code
<object class="GtkListView" id="notesList">
<property name="show-separators">True</property>
<signal name="activate" handler="_onNotesListActivate"/>
</object>
vs.
this._notesList = new Gtk.ListView({ showSeparators: true });
this._notesList.connect("activate", this._onNotesListActivate.bind(this));
- Tedious to write by hand
- Glade UI Designer
- GTK 3 only
- GTK 4 alternative underway
.large-icon {
color: #888a85;
-gtk-icon-shadow: #d3d7cf 1px 1px;
padding-right: 8px;
}
- Every UI element is based on
Gtk.Widget
- Roughly equivalent to a HTML DOM element
- Methods
- Properties
- Signals (events)
- CSS element name and classes
- Things that are not UI elements are based on
GObject.Object
import Gdk from "gi:https://Gtk";
import Gio from "gi:https://Gio";
import GObject from "gi:https://GObject";
import Gtk from "gi:https://Gtk";
import { NotesListItem } from "./item.js";
- GNOME platform has asynchronous, cancellable I/O
- Experimental opt-in support for JS
await
Gio._promisify(Gio.OutputStream.prototype, 'write_bytes_async', 'write_bytes_finish');
// ...
let bytesWritten = 0;
while (bytesWritten < bytes.length) {
bytesWritten = await stream.write_bytes_async(bytes, priority, cancellable);
bytes = bytes.slice(bytesWritten);
}
- These may or may not work
- Check if you actually need the dependency
- Use ES module directly if it doesn't have other deps
- Some modules ship a browser bundle, this might work
- Else, build a UMD bundle with Browserify and vendor it
yarn add my-library
mkdir -p src/vendor
npx browserify -r my-library -s myLibrary -o src/vendor/my-library.js
import './vendor/my-library.js';
// myLibrary is now a global object
Top 5 most used NPM libraries
- lodash
- chalk
- request
- commander
- react
- In some cases not necessary
- Use
lodash-es
if you need lodash
import _ from './vendor/lodash-es/lodash.js';
_.defaults({ 'a': 1 }, { 'a': 3, 'b': 2 });
- No bundle, so make a Browserified one
- Color support detection code is Node-only
- Edit bundle, change
stdout: false
andstderr: false
totrue
- Edit bundle, change
import './vendor/chalk.js';
print(chalk.blue('Hello') + ' World' + chalk.red('!'));
- Deprecated
- Use
Soup
instead
const request = require('request');
request('https://ptomato.name', function (error, response, body) {
console.error('error:', error);
console.log('statusCode:', response && response.statusCode);
console.log('body:', body);
});
import Soup from 'gi:https://Soup';
const session = new Soup.Session();
const msg = new Soup.Message({ method: 'GET', uri: new Soup.URI('https://ptomato.name') });
session.queue_message(msg, (_, {statusCode, responseBody}) => {
log(`statusCode: ${statusCode}`);
log(`body: ${responseBody.data}`);
});
- No bundle, so make a Browserified one
import System from 'system';
import './vendor/commander.js';
const { Command } = commander;
const options = new Command()
.option('-p, --pizza-type <type>', 'flavour of pizza')
.parse(System.programArgs, { from: 'user' })
.opts(); // ^^^^^^^^^^^^
if (options.pizzaType) print(`pizza flavour: ${options.pizzaType}`);
- Not applicable
P.S. Although it would be cool if React Native worked with GTK
Fast-forward to the written code
(Live demo, but in case that doesn't work out, screenshots follow)
- Flathub
- Requirements
- Luckily, the generated project skeleton meets all of these
- Only need to fill in a few things
- This file is used to provide the description that users see on Flathub
- And in their software updater appplication
- Description of file format
- Generator to get you started
- Asks you a few questions
- Asks for URLs of screenshots
- Flathub guidelines
- OARS rating
- OARS Generator
- Tells how to display your app in the desktop
- Description of file format
- List of categories
[Desktop Entry]
Name=Bloatpad
Comment=Unnecessary note-taking application
Exec=name.ptomato.Bloatpad
Icon=name.ptomato.Bloatpad
Terminal=false
Type=Application
Categories=Utility;GTK;
StartupNotify=true
- Tobias Bernard on Designing an Icon for your App
- Instructions here
- Gettext is built-in to the platform
- Venerable framework for UI translations
- Use a website like Transifex
- Recruit volunteer translators
- Or translate the UI yourself in whatever languages you speak
- Some things might seem familiar to JS developers, others might not
- We should reduce the friction for these developers
- But not everything from the web or Node.js applies well to the desktop
- Andy Holmes, Evan Welsh, Sri Ramkrishna for discussions and their work on improving the GJS developer experience
- Presentation licensed under Creative Commons BY-NC-ND 4.0
- Bloatpad code, permissive MIT license