SlideShare a Scribd company logo
The Newest JavaScript
Technologies in GNOME
Evan Welsh
 @ewlsh:gnome.org
 ewlsh
Philip Chimento
 @ptomato:gnome.org
 ptomato
GUADEC, July 19, 2024
1
Introduction: What this talk is
about
Using TypeScript to write GNOME apps
Community-led documentation updates
What's new and what's next in GJS?
Thanks to everyone who contributed!
2
Introduction
Presentation is full of links if you want to click through later
If you want to follow along: ptomato.name/talks/guadec2024
㊻, ㊼,
3
Community
Community
Community
4
4
4
State of TypeScript for GNOME
The first beta of unified TypeScript definitions has been published
Since then Pascal has been working on filling in feature gaps and Evan
has been bug hunting.
Thanks to STF funding
5
How to use TypeScript today
A massive shoutout to Chris Davis for working continuously on a
TypeScript template for GJS
Most apps should start there!
6
How to use TypeScript today
(experimental)
If you're developing Shell extensions or are adventurous, the beta
bindings are currently published as NPM packages
Distro packaging with NPM packages is not simple, so proceed
with caution for apps.
We're working to support an ideal format for the TypeScript template
and distro packaging, reach out if you want to help!
7
More Community initiatives
We have a GNOME-home for JavaScript projects now!
gjs.guide
gjs.guide has a new (more official) home at
https://gitlab.gnome.org/World/javascript/gjs-guide
the TypeScript template
The TypeScript template is at
https://gitlab.gnome.org/World/javascript/gnome-typescript-template 8
gjs.guide
Updated and upgraded with better search and easier navigation
Docs written this year...
Extensions, the Shell, and GNOME 46 ( Andy, Javad)
D-Bus ( Andy)
St widget examples ( Nitin Dhembare)
to Sebastian Wiesner, Daniel Steele, Adrien Delessert, Evangelos
Paterakis, "Bytez", Jeffery To, Marko Kocic, Pedro Sader Azevedo and
many more for adding examples, fixing typos, and making the
community better
9
Workbench
10
Workbench improvements
TypeScript support!
Write JS/TS code in Workbench
with autocomplete support
using the language server!
Angelo Verlain
11
What's new in GJS
What's new in GJS
What's new in GJS
for GNOME ㊻and ㊼?
for GNOME ㊻and ㊼?
for GNOME ㊻and ㊼?
12
12
12
Console output ㊻㊼
Improvements in logging repository namespaces, e.g.
imports.gi.GLib ㊻ Sriyansh Shivam
Recent improvements in logging objects with circular references,
ParamSpecs, Uint8Arrays ㊼ Gary Li
Improvements to pretty printing in console.log /REPL/debugger is
a great place to get started if you want to contribute to GJS
13
Performance improvements ㊻㊼
Performance of setting/getting GObject properties improved
More improvements to come
Marco Trevisan, as usual!
14
Gtk.AccessibleList ㊻
Gtk.AccessibleList can be used to represent list relationships in
GTK4 for better accessibility
widget.update_relation(
Gtk.AccessibleRelation.LABELLED_BY,
new Gtk.AccessibleList([label1, label2]
);
Thanks to STF funding
15
An update on async support
We've been working on getting rid of Gio._promisify ...
GObject Introspection now has async annotations ㊻
GLib + GObject Introspection will soon support native async
operations
GJS has a draft MR to support the work ... ㊼?
Thanks to STF funding
16
JS engine upgrades ㊼
GJS's underlying JavaScript engine is the one from Firefox, but
embedded.
Tracks Firefox's long-term support version (currently Firefox ESR
115)
Brings in newer editions of the JS language, and security updates
GNOME ㊼will upgrade to Firefox ESR 128
17
Intl.Segmenter ㊼
const segmenter = new Intl.Segmenter('en', { granularity: 'word' });
const segments = segmenter.segment('The mile-high city');
for (const { segment, isWordLike } of segments) {
if (isWordLike) {
console.log(segment);
}
}
// The
// mile
// high
// city
18
groupBy ㊼
array = ['the', 'array', 'has', 'words'];
Object.groupBy(array, word => word.length);
// => {
// '3': ['the', 'has'],
// '5': ['array', 'words'],
// }
compare Lodash's _.groupBy()
also Map.groupBy() to create a Map instead of plain object
19
Promise.withResolvers ㊼
Useful for integrating promises with signal-based APIs
function promisifyWebKitDownload(dl) {
let {promise, resolve, reject} = Promise.withResolvers();
const finishID = dl.connect('finished', resolve);
const failID = dl.connect('failed', (_, error) => reject(error));
return promise.finally(() => {
dl.disconnect(finishID);
dl.disconnect(failID);
});
}
20
ArrayBuffer transfer ㊼
Zero-copy ownership takeover of data buffers
async function validateAndWrite(arrayBuffer, stream, prio, cancellable) {
const owned = arrayBuffer.transfer();
// transfer() "detaches" the old object and creates a new one without copying. If we
// didn't transfer, other code might modify arrayBuffer while we await validate()
await validate(owned, cancellable);
const bytes = new Uint8Array(owned);
await stream.write_bytes_async(bytes, prio, cancellable);
}
const bytes = new TextEncoder().encode("a string");
validateAndWrite(bytes.buffer, stream, ...asyncArgs);
setTimeout(() => (bytes[0] = 0x00), 5); //
21
Update your code
GLibUnix / GioUnix / GLibWin32 / GioWin32 ㊻
new Gio.FileIcon(myFile) vs Gio.FileIcon.new(myFile) ㊼
get_data() / set_data() and ref() / unref() are blocked ㊼
22
What's coming down the line in JS
What's coming down the line in JS
What's coming down the line in JS
23
23
23
Already implemented in nightly Firefox
new Set methods (union, intersection, etc)
Resizable ArrayBuffer
We'll get these in next year's SpiderMonkey update
24
WinterCG
Common Minimum API Proposal
Any interest in joining this group on GNOME's behalf?
25
Other exciting proposals coming 2025
or 2026
Temporal (I gave a talk on it if you want to know more)
Decorators
Iterator methods
using statements
26
Intl.MessageFormat
Stage 1 proposal, coming farther in the future
Gettext is central to GNOME's workflow, but ...
xgettext has always been a bit hacky to integrate with JS and is
just flat-out broken for backtick strings
We could drop Gettext integration in JS and move to
Intl.MessageFormat for the developer API, while still consuming
the Gettext files from GNOME's translation workflow
Is anyone interested in a proof of concept?
27
And on that note...
And on that note...
And on that note...
28
28
28
29
29
29
GNOME and Intl
Intl is a global object
Does internationalization stuff for you
Some things awkward to do with Gettext. Intl makes them easy
Some things Gettext does well. Intl is gaining those capabilities...
30
Intl
Intl.Collator - locale-sensitive sort
Intl.DateTimeFormat - print dates and times ( "Fri Jul 19" )
Intl.DisplayNames - translate names of regions, languages, ...
Intl.ListFormat - print lists ( "A, B, and C" )
Intl.Locale - information about user's locale conventions
Intl.NumberFormat - print numbers ( "€ 0,00" )
Intl.PluralRules - select appropriate plural form for language
Intl.RelativeTimeFormat - print relative times ( "3 days ago" )
Intl.Segmenter ㊼- split strings into graphemes/words/sentences
31
Why you should use Intl
/* Translators: this is the month name and day number
followed by a time string in 24h format.
i.e. "May 25, 14:30" */
format = N_("%B %-d, %Hu2236%M");
How does the translator know what to put there for their locale?
32
“Well why don't they just read the
manual for strftime?”
33
Why you should use Intl
#. Translators: this is the month name and day number
#. followed by a time string in 24h format.
#. i.e. "May 25, 14:30"
#: js/misc/util.js:255
#, no-c-format
msgid "%B %-d, %H∶%M"
msgstr "%j, %R %p"
34
Why you should use Intl
More readable, even if longer
Less burden on GNOME
translators
format = new Intl.DateTimeFormat(myLocale, {
dateStyle: 'medium',
timeStyle: 'short',
hourCycle: 'h24',
});
format.format(Date.now());
// ⇒ "Jul 17, 2024, 15:36" in my locale
35
Why you should use Intl
Who does translate those, anyway?
ICU project
Unicode Common Locale Data Repository (CLDR)
They have the power of major browser vendors behind them
36
Why you should use Intl
What if I don't like the ICU/CLDR's translations in my UI?
You can still customize them using formatToParts()
37
Why you should use Intl
What if I don't like the ICU/CLDR's translations in my UI?
You can still customize them using formatToParts()
12:00 12∶00
38
Thanks
GJS contributors from 46 and 47
License
Presentation licensed under Creative Commons BY-NC-ND 4.0
39
Questions?
Questions?
Questions?
‍
‍
‍
Image:
Image:
Image: IRCat
IRCat
IRCat from
from
from Pixabay
Pixabay
Pixabay 40
40
40

More Related Content

PDF
What's new with JavaScript in GNOME: The 2020 edition (GUADEC 2020)
PDF
What is new with JavaScript in Gnome: The 2021 edition
PDF
Javascript, the GNOME way (JSConf EU 2011)
PDF
Writing native Linux desktop apps with JavaScript
PDF
State of the GNOME - 2022 - Ubuntu Summit
PDF
Why Go Lang?
PDF
Clojurescript slides
PDF
Developing GNOME Apps in Javascript
What's new with JavaScript in GNOME: The 2020 edition (GUADEC 2020)
What is new with JavaScript in Gnome: The 2021 edition
Javascript, the GNOME way (JSConf EU 2011)
Writing native Linux desktop apps with JavaScript
State of the GNOME - 2022 - Ubuntu Summit
Why Go Lang?
Clojurescript slides
Developing GNOME Apps in Javascript

Similar to The Newest JavaScript Technologies in GNOME (20)

PDF
JavaScript for impatient programmers.pdf
PDF
The GO programming language
PDF
Javascript in linux desktop (ICOS ver.)
PDF
Go Course Day1
PPTX
Go & multi platform GUI Trials and Errors
PDF
Building a Desktop Streaming console with Electron and ReactJS
PDF
JavaScript: Past, Present, Future
PDF
Introduction to Go
PPTX
PUG Challenge 2016 - The nativescript pug app challenge
PDF
JSLab.Руслан Шевченко."JavaScript как платформа компиляции"
PDF
Jslab rssh: JS as language platform
PPT
A First Look at Google's Go Programming Language
PPT
Google's Go Programming Language - Introduction
PDF
openSUSE Asia 2024: Developing an application for GNOME in Rust
PPTX
ES6 - JavaCro 2016
PDF
An introduction to Gnome An introdu.pptx
PDF
Comparing JSON Libraries - July 19 2011
PDF
How can your applications benefit from Java 9?
PDF
Go 1.8 Release Party
PDF
How can your applications benefit from Java 9?
JavaScript for impatient programmers.pdf
The GO programming language
Javascript in linux desktop (ICOS ver.)
Go Course Day1
Go & multi platform GUI Trials and Errors
Building a Desktop Streaming console with Electron and ReactJS
JavaScript: Past, Present, Future
Introduction to Go
PUG Challenge 2016 - The nativescript pug app challenge
JSLab.Руслан Шевченко."JavaScript как платформа компиляции"
Jslab rssh: JS as language platform
A First Look at Google's Go Programming Language
Google's Go Programming Language - Introduction
openSUSE Asia 2024: Developing an application for GNOME in Rust
ES6 - JavaCro 2016
An introduction to Gnome An introdu.pptx
Comparing JSON Libraries - July 19 2011
How can your applications benefit from Java 9?
Go 1.8 Release Party
How can your applications benefit from Java 9?
Ad

More from Igalia (20)

PDF
Life of a Kernel Bug Fix
PDF
Unlocking the Full Potential of WPE to Build a Successful Embedded Product
PDF
Advancing WebDriver BiDi support in WebKit
PDF
Jumping Over the Garden Wall - WPE WebKit on Android
PDF
Collective Funding, Governance and Prioritiation of Browser Engine Projects
PDF
Don't let your motivation go, save time with kworkflow
PDF
Solving the world’s (localization) problems
PDF
The Whippet Embeddable Garbage Collection Library
PDF
Nobody asks "How is JavaScript?"
PDF
Getting more juice out from your Raspberry Pi GPU
PDF
WebRTC support in WebKitGTK and WPEWebKit with GStreamer: Status update
PDF
Demystifying Temporal: A Deep Dive into JavaScript New Temporal API
PDF
CSS :has() Unlimited Power
PDF
Device-Generated Commands in Vulkan
PDF
Current state of Lavapipe: Mesa's software renderer for Vulkan
PDF
Vulkan Video is Open: Application showcase
PDF
Scheme on WebAssembly: It is happening!
PDF
EBC - A new backend compiler for etnaviv
PDF
RISC-V LLVM State of the Union
PDF
Device-Generated Commands in Vulkan
Life of a Kernel Bug Fix
Unlocking the Full Potential of WPE to Build a Successful Embedded Product
Advancing WebDriver BiDi support in WebKit
Jumping Over the Garden Wall - WPE WebKit on Android
Collective Funding, Governance and Prioritiation of Browser Engine Projects
Don't let your motivation go, save time with kworkflow
Solving the world’s (localization) problems
The Whippet Embeddable Garbage Collection Library
Nobody asks "How is JavaScript?"
Getting more juice out from your Raspberry Pi GPU
WebRTC support in WebKitGTK and WPEWebKit with GStreamer: Status update
Demystifying Temporal: A Deep Dive into JavaScript New Temporal API
CSS :has() Unlimited Power
Device-Generated Commands in Vulkan
Current state of Lavapipe: Mesa's software renderer for Vulkan
Vulkan Video is Open: Application showcase
Scheme on WebAssembly: It is happening!
EBC - A new backend compiler for etnaviv
RISC-V LLVM State of the Union
Device-Generated Commands in Vulkan
Ad

Recently uploaded (20)

PDF
A comparative analysis of optical character recognition models for extracting...
DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
MIND Revenue Release Quarter 2 2025 Press Release
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
cuic standard and advanced reporting.pdf
PDF
Electronic commerce courselecture one. Pdf
PDF
Machine learning based COVID-19 study performance prediction
PPTX
Machine Learning_overview_presentation.pptx
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PDF
Assigned Numbers - 2025 - Bluetooth® Document
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
Encapsulation_ Review paper, used for researhc scholars
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PPTX
Spectroscopy.pptx food analysis technology
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PPTX
sap open course for s4hana steps from ECC to s4
PDF
gpt5_lecture_notes_comprehensive_20250812015547.pdf
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
A comparative analysis of optical character recognition models for extracting...
The AUB Centre for AI in Media Proposal.docx
MIND Revenue Release Quarter 2 2025 Press Release
Mobile App Security Testing_ A Comprehensive Guide.pdf
cuic standard and advanced reporting.pdf
Electronic commerce courselecture one. Pdf
Machine learning based COVID-19 study performance prediction
Machine Learning_overview_presentation.pptx
Agricultural_Statistics_at_a_Glance_2022_0.pdf
“AI and Expert System Decision Support & Business Intelligence Systems”
Assigned Numbers - 2025 - Bluetooth® Document
Per capita expenditure prediction using model stacking based on satellite ima...
Encapsulation_ Review paper, used for researhc scholars
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Spectroscopy.pptx food analysis technology
Digital-Transformation-Roadmap-for-Companies.pptx
sap open course for s4hana steps from ECC to s4
gpt5_lecture_notes_comprehensive_20250812015547.pdf
The Rise and Fall of 3GPP – Time for a Sabbatical?
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx

The Newest JavaScript Technologies in GNOME

  • 1. The Newest JavaScript Technologies in GNOME Evan Welsh  @ewlsh:gnome.org  ewlsh Philip Chimento  @ptomato:gnome.org  ptomato GUADEC, July 19, 2024 1
  • 2. Introduction: What this talk is about Using TypeScript to write GNOME apps Community-led documentation updates What's new and what's next in GJS? Thanks to everyone who contributed! 2
  • 3. Introduction Presentation is full of links if you want to click through later If you want to follow along: ptomato.name/talks/guadec2024 ㊻, ㊼, 3
  • 5. State of TypeScript for GNOME The first beta of unified TypeScript definitions has been published Since then Pascal has been working on filling in feature gaps and Evan has been bug hunting. Thanks to STF funding 5
  • 6. How to use TypeScript today A massive shoutout to Chris Davis for working continuously on a TypeScript template for GJS Most apps should start there! 6
  • 7. How to use TypeScript today (experimental) If you're developing Shell extensions or are adventurous, the beta bindings are currently published as NPM packages Distro packaging with NPM packages is not simple, so proceed with caution for apps. We're working to support an ideal format for the TypeScript template and distro packaging, reach out if you want to help! 7
  • 8. More Community initiatives We have a GNOME-home for JavaScript projects now! gjs.guide gjs.guide has a new (more official) home at https://gitlab.gnome.org/World/javascript/gjs-guide the TypeScript template The TypeScript template is at https://gitlab.gnome.org/World/javascript/gnome-typescript-template 8
  • 9. gjs.guide Updated and upgraded with better search and easier navigation Docs written this year... Extensions, the Shell, and GNOME 46 ( Andy, Javad) D-Bus ( Andy) St widget examples ( Nitin Dhembare) to Sebastian Wiesner, Daniel Steele, Adrien Delessert, Evangelos Paterakis, "Bytez", Jeffery To, Marko Kocic, Pedro Sader Azevedo and many more for adding examples, fixing typos, and making the community better 9
  • 11. Workbench improvements TypeScript support! Write JS/TS code in Workbench with autocomplete support using the language server! Angelo Verlain 11
  • 12. What's new in GJS What's new in GJS What's new in GJS for GNOME ㊻and ㊼? for GNOME ㊻and ㊼? for GNOME ㊻and ㊼? 12 12 12
  • 13. Console output ㊻㊼ Improvements in logging repository namespaces, e.g. imports.gi.GLib ㊻ Sriyansh Shivam Recent improvements in logging objects with circular references, ParamSpecs, Uint8Arrays ㊼ Gary Li Improvements to pretty printing in console.log /REPL/debugger is a great place to get started if you want to contribute to GJS 13
  • 14. Performance improvements ㊻㊼ Performance of setting/getting GObject properties improved More improvements to come Marco Trevisan, as usual! 14
  • 15. Gtk.AccessibleList ㊻ Gtk.AccessibleList can be used to represent list relationships in GTK4 for better accessibility widget.update_relation( Gtk.AccessibleRelation.LABELLED_BY, new Gtk.AccessibleList([label1, label2] ); Thanks to STF funding 15
  • 16. An update on async support We've been working on getting rid of Gio._promisify ... GObject Introspection now has async annotations ㊻ GLib + GObject Introspection will soon support native async operations GJS has a draft MR to support the work ... ㊼? Thanks to STF funding 16
  • 17. JS engine upgrades ㊼ GJS's underlying JavaScript engine is the one from Firefox, but embedded. Tracks Firefox's long-term support version (currently Firefox ESR 115) Brings in newer editions of the JS language, and security updates GNOME ㊼will upgrade to Firefox ESR 128 17
  • 18. Intl.Segmenter ㊼ const segmenter = new Intl.Segmenter('en', { granularity: 'word' }); const segments = segmenter.segment('The mile-high city'); for (const { segment, isWordLike } of segments) { if (isWordLike) { console.log(segment); } } // The // mile // high // city 18
  • 19. groupBy ㊼ array = ['the', 'array', 'has', 'words']; Object.groupBy(array, word => word.length); // => { // '3': ['the', 'has'], // '5': ['array', 'words'], // } compare Lodash's _.groupBy() also Map.groupBy() to create a Map instead of plain object 19
  • 20. Promise.withResolvers ㊼ Useful for integrating promises with signal-based APIs function promisifyWebKitDownload(dl) { let {promise, resolve, reject} = Promise.withResolvers(); const finishID = dl.connect('finished', resolve); const failID = dl.connect('failed', (_, error) => reject(error)); return promise.finally(() => { dl.disconnect(finishID); dl.disconnect(failID); }); } 20
  • 21. ArrayBuffer transfer ㊼ Zero-copy ownership takeover of data buffers async function validateAndWrite(arrayBuffer, stream, prio, cancellable) { const owned = arrayBuffer.transfer(); // transfer() "detaches" the old object and creates a new one without copying. If we // didn't transfer, other code might modify arrayBuffer while we await validate() await validate(owned, cancellable); const bytes = new Uint8Array(owned); await stream.write_bytes_async(bytes, prio, cancellable); } const bytes = new TextEncoder().encode("a string"); validateAndWrite(bytes.buffer, stream, ...asyncArgs); setTimeout(() => (bytes[0] = 0x00), 5); // 21
  • 22. Update your code GLibUnix / GioUnix / GLibWin32 / GioWin32 ㊻ new Gio.FileIcon(myFile) vs Gio.FileIcon.new(myFile) ㊼ get_data() / set_data() and ref() / unref() are blocked ㊼ 22
  • 23. What's coming down the line in JS What's coming down the line in JS What's coming down the line in JS 23 23 23
  • 24. Already implemented in nightly Firefox new Set methods (union, intersection, etc) Resizable ArrayBuffer We'll get these in next year's SpiderMonkey update 24
  • 25. WinterCG Common Minimum API Proposal Any interest in joining this group on GNOME's behalf? 25
  • 26. Other exciting proposals coming 2025 or 2026 Temporal (I gave a talk on it if you want to know more) Decorators Iterator methods using statements 26
  • 27. Intl.MessageFormat Stage 1 proposal, coming farther in the future Gettext is central to GNOME's workflow, but ... xgettext has always been a bit hacky to integrate with JS and is just flat-out broken for backtick strings We could drop Gettext integration in JS and move to Intl.MessageFormat for the developer API, while still consuming the Gettext files from GNOME's translation workflow Is anyone interested in a proof of concept? 27
  • 28. And on that note... And on that note... And on that note... 28 28 28
  • 30. GNOME and Intl Intl is a global object Does internationalization stuff for you Some things awkward to do with Gettext. Intl makes them easy Some things Gettext does well. Intl is gaining those capabilities... 30
  • 31. Intl Intl.Collator - locale-sensitive sort Intl.DateTimeFormat - print dates and times ( "Fri Jul 19" ) Intl.DisplayNames - translate names of regions, languages, ... Intl.ListFormat - print lists ( "A, B, and C" ) Intl.Locale - information about user's locale conventions Intl.NumberFormat - print numbers ( "€ 0,00" ) Intl.PluralRules - select appropriate plural form for language Intl.RelativeTimeFormat - print relative times ( "3 days ago" ) Intl.Segmenter ㊼- split strings into graphemes/words/sentences 31
  • 32. Why you should use Intl /* Translators: this is the month name and day number followed by a time string in 24h format. i.e. "May 25, 14:30" */ format = N_("%B %-d, %Hu2236%M"); How does the translator know what to put there for their locale? 32
  • 33. “Well why don't they just read the manual for strftime?” 33
  • 34. Why you should use Intl #. Translators: this is the month name and day number #. followed by a time string in 24h format. #. i.e. "May 25, 14:30" #: js/misc/util.js:255 #, no-c-format msgid "%B %-d, %H∶%M" msgstr "%j, %R %p" 34
  • 35. Why you should use Intl More readable, even if longer Less burden on GNOME translators format = new Intl.DateTimeFormat(myLocale, { dateStyle: 'medium', timeStyle: 'short', hourCycle: 'h24', }); format.format(Date.now()); // ⇒ "Jul 17, 2024, 15:36" in my locale 35
  • 36. Why you should use Intl Who does translate those, anyway? ICU project Unicode Common Locale Data Repository (CLDR) They have the power of major browser vendors behind them 36
  • 37. Why you should use Intl What if I don't like the ICU/CLDR's translations in my UI? You can still customize them using formatToParts() 37
  • 38. Why you should use Intl What if I don't like the ICU/CLDR's translations in my UI? You can still customize them using formatToParts() 12:00 12∶00 38
  • 39. Thanks GJS contributors from 46 and 47 License Presentation licensed under Creative Commons BY-NC-ND 4.0 39