Compare commits

...

94 Commits
v46.0 ... main

Author SHA1 Message Date
Sonny Piers
730729b2f1 f 2025-07-06 18:08:09 +02:00
Sonny Piers
b5d2e7806f Prepare v48 2025-04-18 14:02:31 +02:00
Sonny Piers
a48d234c56 Use GNOME 48 2025-04-18 14:02:31 +02:00
Angelo Verlain Shema
34fbe9f018
fix: HoverProvider get_iter crashing (#1003) 2025-03-24 20:54:55 +01:00
Sonny Piers
dc0c931917 Update troll 2025-03-13 14:50:26 +01:00
Sonny Piers
af32c09966 cli: Add Interrupt to simplify control flow 2025-03-13 14:50:26 +01:00
Sonny Piers
584b9e440e cli: Deduplicate code with a diagnose fn 2025-03-13 14:50:26 +01:00
Sonny Piers
2a65be9e33 cli: Split by lang 2025-03-11 17:53:51 +01:00
Sonny Piers
630dbb486f Minor changes 2025-03-11 17:53:51 +01:00
Sonny
8ec88e697b
Reformat files for Blueprint 0.16 (#1001) 2025-03-08 21:26:19 +01:00
Sonny
987a9d4587
Update dependencies (#1000) 2025-03-08 20:05:40 +01:00
Sonny Piers
42c5e6c1d7 v47.1 2025-01-24 15:45:34 +01:00
UrtsiSantsi
39f6eb9b51
Use CSS variables instead of GTK-specific syntax (#997) 2025-01-24 13:47:41 +01:00
Sonny Piers
156186b1c9 Fix permissions dialog
Closes https://github.com/workbenchdev/Workbench/issues/996
2025-01-24 13:40:55 +01:00
Sonny Piers
e97114eee2 Update libportal to 0.9.0 2025-01-24 13:40:23 +01:00
Sonny Piers
0719075808 Update Blueprint to 0.16.0 2025-01-24 13:39:31 +01:00
Sonny
aa93296968
Support Flatpak < 1.15.6 for permissions (#992) 2024-12-11 16:19:09 +01:00
Sonny
5506bf85f9
Release 47 (#991) 2024-12-11 15:52:11 +01:00
Sonny Piers
1b75996fab Use -- to pass custom command to run.js 2024-10-18 12:50:06 +02:00
Sonny
2057f39390
Include freedesktop sdk TypeScript extension (#988)
Closes https://github.com/workbenchdev/Workbench/issues/975

TypeScript is still disabled by default for now but it's just a
gsetting.
2024-10-18 12:08:31 +02:00
Sonny Piers
d9badbe19c Stop using deprecated gtk_show_uri 2024-10-17 22:54:25 +02:00
Sonny Piers
6c173507a5 Fix js override for registerClass signature with a single argument 2024-10-17 22:54:25 +02:00
Sonny Piers
541572809a Update release notes 2024-10-17 22:54:25 +02:00
Nokse22
eafa99f771
Added Nokse in contributors section (#987) 2024-10-10 14:41:19 +02:00
Sonny Piers
48897f71d2 Update to libspelling 0.4.2 2024-10-07 15:52:28 +02:00
Sonny
812895129f
Disable TypeScript by default (#986)
Temporarily until
https://github.com/flathub/org.freedesktop.Sdk.Extension.typescript/issues/29
2024-10-04 14:26:04 +02:00
Sonny
e5c077bd78
Remove unsafe permissions and add dialog (#985)
Fixes https://github.com/workbenchdev/Workbench/issues/915
2024-10-03 23:15:00 +02:00
Sonny Piers
d79bc8c84a Rename extension property enabled to available 2024-10-03 17:39:15 +02:00
Sonny Piers
efb14d9961 Include GOM dependency 2024-09-30 15:27:38 +02:00
Sonny Piers
2e4e318e17 Various improvements to Library 2024-09-26 13:44:16 +02:00
Sonny Piers
26e63a11a5 Fix Library category filtering 2024-09-25 12:37:45 +02:00
Sonny
dfb44ab923
Remove gst-plugin-gtk4 (#984)
Fixes https://github.com/workbenchdev/Workbench/issues/983
2024-09-23 23:03:05 +02:00
Atbrat
7aa97b6cc7
library: Add Language and Category filters (#967) 2024-09-23 14:28:05 +02:00
Sonny Piers
d02b1d1eef Add wip build script 2024-09-23 13:01:14 +02:00
Sonny
020cfec21b
GNOME 47 (#982)
Technically depends on
https://github.com/flathub/org.freedesktop.Sdk.Extension.typescript/issues/29

Without, TypeScript support is broken.
But I'll merge anyway as keeping separate sdk branches is a mess, and we
can move forward and prepare the release.

For now I have re-enabled Biome for JavaScript. 

Hopefully `org.freedesktop.Sdk.Extension.typescript//24.08` will get
released soon.
2024-09-23 11:25:28 +02:00
Atbrat
154f5eaa82
Add dependency for GOM demo (#981)
- Parent PR: https://github.com/workbenchdev/demos/pull/200
2024-09-04 22:42:43 -07:00
Roland Lötscher
2c1dad91de
Add option to create new project (#970)
Fixes #951
2024-07-23 21:56:51 +02:00
Sonny
b66871f6bd
Various improvements (#974) 2024-07-16 15:51:01 +02:00
Sonny
6a1dd637ce
Remove Biome (#973)
We switched to TypeScript Language Server so we don't need it anymore.
2024-07-14 23:13:59 +02:00
Angelo Verlain Shema
3a8e52ceb0
Typechecking with JavaScript (#972)
Using the world-famous `jsconfig.json`
2024-07-14 20:52:44 +02:00
Angelo Verlain Shema
bf5e999a00
Use the node18 SDK Extension instead of 20 (#971)
That's what typescript requires
2024-07-14 14:31:08 +02:00
Diego Iván M.E
2c0a46abf2
extensions: Fix language detection (#968) 2024-07-12 12:06:28 +02:00
Sonny
669d808c89
Switch back devel to 46 again (#966)
Nightly is too broken at this stage
2024-07-11 16:59:20 +02:00
UrtsiSantsi
fa3f77ef51
Fix code search close button behavior (#960)
Fixes #959 

Clear the highlights in the code and remember the last searched word
2024-07-10 21:52:00 +02:00
Brage Fuglseth
05d6c3cf1a
readme: Update Flathub badge (#963)
See https://fosstodon.org/@bragefuglseth/112753049810610017
2024-07-09 16:39:39 +02:00
Sonny
2d9b184bd5
Use org.flatpak.Builder (#962) 2024-07-07 19:40:36 +02:00
Sonny
60cfdb38fb
Use GNOME nightly again (#961) 2024-07-07 16:23:59 +02:00
Atbrat
f4eb0e604b
library: Remove Usage of PreferencesPage and Group (#958) 2024-07-07 01:48:56 +02:00
Angelo Verlain Shema
2b13cf3ce2
Add typechecking for TypeScript (#946)
Co-authored-by: Sonny <sonny@fastmail.net>
2024-07-07 01:23:09 +02:00
Sonny Piers
afea0a5339 Use GNOME 46 for Workbench devel
GNOME nightly is temporarily broken
https://discourse.gnome.org/t/nightly-runtime-updated-to-freedesktop-sdk-24-08/21962
2024-07-05 12:58:33 +02:00
Sonny Piers
be55d5a675 Update Blueprint 2024-07-05 00:31:28 +02:00
Sonny Piers
bdf7bed717 Add make stable and make devel commands 2024-07-03 15:01:32 +02:00
Atbrat
78dbbfbc75
library: Remove usage of Adw.PreferencesRow (#955) 2024-07-03 14:47:31 +02:00
Sonny Piers
dd9a3e87a3 ci: Allow triggering workflows manually 2024-07-02 14:04:27 +02:00
Atbrat
d4066fc32a
library: Add search bar inside library view (#950) 2024-06-30 00:47:45 +02:00
Atbrat
5a29338cce
library: Remove usage of Adw.PreferencesWindow (#949) 2024-06-25 00:58:27 +02:00
Sonny Piers
ea18e429c9 Update language support 2024-06-18 22:42:22 +02:00
Sonny Piers
b16fc2ddec Improve Blueprint completion experience 2024-06-16 23:49:27 +02:00
Sonny Piers
d8f06f1b46 cli: Filter out erronous Vala CSS Gradients deprecations 2024-06-15 00:23:15 +02:00
Angelo Verlain Shema
84f1bfa1cc Add TypeScript support
Squashed commit of the following:

commit 705f0256b155d523f0759081b995b902851f1010
Merge: 915fe997 17e53775
Author: Sonny Piers <sonny@fastmail.net>
Date:   Thu May 30 17:35:46 2024 +0300

    Merge branch 'main' into typescript

commit 915fe997c8c0fa30db061d17cc7849678aa1c681
Author: Angelo Verlain Shema <37999241+vixalien@users.noreply.github.com>
Date:   Tue May 28 11:19:04 2024 +0200

    Compile TypeScript to JavaScript before executing (#941)

    This basically compiles from TypeScript to JavaScript at runtime when
    the "Run" button is clicked.

    However, there are currently 2 issues worth mentioning:

    ### 1. Speed

    Notice that this is noticeably slow because it's using `tsc`. It could
    possibly be improved by using `esbuild`, `swc`, `babel` or something
    similar but then there will be no typechecking when the "Run" button is
    clicked.

    However, I think the above typechecking caveat will not make much sense
    when we have real-time Intellisense in the editor for TypeScript.

    ### 2. Sourcemaps

    Another consideration is the lack of sourcemap support in GJS. While tsc
    can generate sourcemaps, this feature is disabled because GJS won't use
    them. This means that some errors will have the wrong line:column
    information.

    For example:

    ![image](https://github.com/workbenchdev/Workbench/assets/37999241/c6487292-18a9-4e50-85a0-5c8771f107fc)

    Workbench/GJS reports the error is on line number 17 even if it's
    actually on line number 22 because that's where it ends up after it's
    compiled to JavaScript (many compilers will eat up unnecessary line
    breaks even though the minify option is turned off).

    I left some TODOs in here where some decisions need to be made and hope
    to get some feedback

commit 773669f02adb90d59f82a11ba6d16e35bd114a28
Author: Angelo Verlain Shema <37999241+vixalien@users.noreply.github.com>
Date:   Tue May 7 15:53:06 2024 +0200

    Add base TypeScript view to Workbench (#938)
2024-06-06 15:05:24 +03:00
Sonny Piers
17a8e4a6af Various improvements to completion 2024-06-03 22:36:19 +03:00
Sonny Piers
e9748dd79f Fix CSS scoping for CSS variables
```
:root {
  --color: red;
}

label {
  color: var(--color);
}
```
2024-06-03 22:34:06 +03:00
Sonny Piers
a4e1163e13 Fix confusing warning when typing a property
"Argument type may not be null"
2024-06-02 07:55:16 +03:00
Sonny
3a9c3338e0
Completion provider (#155)
Fixes https://github.com/sonnyp/Workbench/issues/257

Works well with CSS and Blueprint for now
We'll need to tweak language servers and fix some issues for
Vala/Python/Rust/JavaScript

---
EDIT: the following is outdated thanks to
https://github.com/sonnyp/Workbench/pull/371

This is blocked by https://gitlab.gnome.org/GNOME/gjs/-/issues/72

https://gitlab.gnome.org/GNOME/gjs/-/issues/255 is a better explanation
of the problem
2024-06-02 06:56:55 +03:00
Sonny Piers
b26fd1d381 Update vte 0.76.2 2024-05-31 11:28:07 +03:00
Sonny Piers
d2c8150675 Update libshumate 1.2.2 2024-05-31 11:27:45 +03:00
Sonny Piers
17e5377587 Fix an async function signature
Argument ready_callback_closure may not be null
2024-05-30 17:11:12 +03:00
Sonny
df4b1d4461
Use org.gnome master for devel (#944)
We are now on gnome nightly
2024-05-28 17:36:22 +03:00
Sonny Piers
89a9f1b348 ci: Checkout submodules for deploy 2024-05-28 15:44:47 +03:00
Sonny
26d48f4fe1
ci: Deploy to gnome-nightly (#942) 2024-05-28 15:24:12 +03:00
Sonny Piers
0b0a0a283c Fix python-previewer formatting 2024-05-14 00:51:47 +02:00
Sonny Piers
b2dc1df6f3 Add Adwaita icon theme to search path 2024-04-30 01:19:52 +02:00
Sonny Piers
563a316695 Do not read icons from host 2024-04-28 18:12:17 +02:00
Sonny Piers
c65bdb49e2 Support icons in projects 2024-04-28 17:14:42 +02:00
Sonny Piers
3ae1c8c611 cli: Skip incompatible demos 2024-04-28 15:09:19 +02:00
Sonny Piers
be124d22d8 Fix Vala and Rust extensions detection on "Run" 2024-04-28 11:53:19 +02:00
Sonny Piers
d8584653ce Update metadata 2024-04-28 11:02:23 +02:00
Sonny Piers
16f4f12810 Ignore incompatible demos 2024-04-27 23:25:01 +02:00
Sonny Piers
fae77d8ea1 Revert "Use Flatter to generate nightly builds (#936)"
This reverts commit d8c79925127a32fe04109684ae8e006dfabb96d6.
2024-04-27 10:44:05 +02:00
Sonny
d8c7992512
Use Flatter to generate nightly builds (#936) 2024-04-27 00:24:06 +02:00
Sonny Piers
2b7a0aa6da Use Adw.Dialog instead of custom Modal widget 2024-04-21 17:09:41 +02:00
Sonny
1ebbe1e391
Remove icon library (#932) 2024-04-21 16:12:29 +02:00
Sonny
f511bc15ec
Load project icons using resources (#934)
Follow up to https://github.com/workbenchdev/Workbench/pull/887

The current solution using file search paths is not ideal.

It requires the following structure in session / demo dirs:
`icons/scalable/actions`
Exposing users to unnecessary jargon which can usually be ignored during
app development since the icon theme is not something we are interested
in.

With the file search paths it's possible to place `library-symbolic.svg`
in `icons` but it won't be considered symbolic no matter what and
therefor won't recolor.

Also by unloading icons, we can avoid conflicts between demos for the
internal previewer.
2024-04-21 15:00:37 +02:00
Sonny Piers
16cda14d9a Save and restore session windows 2024-04-21 12:37:53 +02:00
Sonny
6ee084e1a0
Allow custom icons in projects (#887) 2024-04-16 01:51:39 +02:00
Sonny Piers
4126d00920 Update demos 2024-04-15 22:08:07 +02:00
Jamie Gravendeel
ac5ec74787
Update Bart Gravendeel in contributors (#927)
Change name and update the domain.
2024-04-03 15:27:41 +02:00
Parker Cook
185dfe415a
Removing the install button from Vala in Workbench Extensions (#926)
I have been asked to delete the Install button for Vala in Workbenches extensions due to it having people install a different version of Vala than is needed.

https://github.com/workbenchdev/Workbench/issues/925
2024-04-03 02:33:30 +02:00
Sonny
4c2944e89c
Enable Code Search (#917) 2024-04-03 02:18:39 +02:00
Angelo Verlain Shema
9f6af155b8
Use Reverse-DNS name for developer ID (#921)
Fixes #906
2024-04-01 00:32:12 +02:00
Angelo Verlain Shema
5d442dbe33
Don't use deprecated methods (#924)
This PR utilises the following methods instead of their deprecated
counterparts:

- `Gtk.Widget.get_height` instead of `Gtk.Widget.get_allocated_height` 
- `Gtk.Widget.get_width` instead of `Gtk.Widget.get_allocated_width` 
- `GLib.resource_register` instead of `GLib.Resource._register`

Thanks!
2024-03-30 16:42:12 +01:00
Sonny
011d9d837f
Do not copy build files to the library (#918)
The way we copied `demos` to `pkgdatadir` was
`install_subdir('../demos/src', install_dir : join_paths(pkgdatadir,
'demos'), strip_directory: true)`

But this method would copy all files, including build files so when a
user creates a session from a demo, the build files would be carried
over as well.

Build files are present in `demos` when opening of the demo with
Workbench directly as a folder project.

The presence of `Cargo.lock` in the session caused problems
which this solves.
2024-03-25 00:20:30 +01:00
Sonny
75ce9e32a6
Make it easier to debug lsp (#920) 2024-03-24 18:04:37 +01:00
Sonny Piers
13dccf5b30 Update Blueprint to 0.12.0 2024-03-24 13:53:50 +01:00
155 changed files with 6467 additions and 5515 deletions

View File

@ -4,11 +4,10 @@ env:
parser: "@babel/eslint-parser" parser: "@babel/eslint-parser"
parserOptions: parserOptions:
sourceType: module sourceType: module
ecmaVersion: 2023
requireConfigFile: false requireConfigFile: false
babelOptions: babelOptions:
plugins: plugins:
- "@babel/plugin-syntax-import-assertions" - "@babel/plugin-syntax-import-attributes"
extends: extends:
- eslint:recommended - eslint:recommended
- plugin:import/errors - plugin:import/errors

4
.gitconfig Normal file
View File

@ -0,0 +1,4 @@
[re.sonny.Commit]
title-length-hint=72
body-length-wrap=50
auto-capitalize-title=true

View File

@ -5,10 +5,11 @@ name: CI
on: on:
pull_request: pull_request:
branches: [main] branches: [main]
workflow_dispatch:
jobs: jobs:
CI: CI:
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
@ -16,13 +17,12 @@ jobs:
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 20
cache: "npm" cache: "npm"
- name: Install host dependencies - name: Install host dependencies
run: | run: |
sudo apt-get update sudo apt-get install flatpak mutter flatpak-builder
sudo apt-get install flatpak flatpak-builder mutter
# Restore caches # Restore caches
- name: Restore Flatpak dependencies - name: Restore Flatpak dependencies

32
.github/workflows/deploy.yaml vendored Normal file
View File

@ -0,0 +1,32 @@
name: Deploy
on:
push:
branches: [main]
workflow_dispatch:
jobs:
flatpak:
name: "Flatpak"
runs-on: ubuntu-latest
container:
image: bilelmoussaoui/flatpak-github-actions:gnome-nightly
options: --privileged
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: flatpak/flatpak-github-actions/flatpak-builder@v6
name: "Build"
with:
bundle: re.sonny.Workbench.Devel.flatpak
manifest-path: build-aux/re.sonny.Workbench.Devel.json
cache-key: flatpak-builder-${{ github.sha }}
- uses: flatpak/flatpak-github-actions/flat-manager@v6
name: "Deploy"
with:
repository: nightly
flat-manager-url: https://flat-manager.gnome.org/
token: ${{ secrets.GNOME_NIGHTLY_TOKEN }}

1
.gitignore vendored
View File

@ -15,6 +15,7 @@ install
__pycache__ __pycache__
*.pyc *.pyc
*.gresource *.gresource
.frun
# IDEs / editors # IDEs / editors
.idea .idea

7
.gitmodules vendored
View File

@ -4,3 +4,10 @@
[submodule "demos"] [submodule "demos"]
path = demos path = demos
url = https://github.com/workbenchdev/demos.git url = https://github.com/workbenchdev/demos.git
[submodule "blueprint-compiler"]
path = blueprint-compiler
url = https://gitlab.gnome.org/jwestman/blueprint-compiler.git
[submodule "src/langs/typescript/template/gi-types"]
path = gi-types
url = https://gitlab.gnome.org/BrainBlasted/gi-typescript-definitions.git
branch = nightly

17
.vscode/settings.json vendored
View File

@ -5,8 +5,8 @@
"files.watcherExclude": { "files.watcherExclude": {
"**/.git/objects/**": true, "**/.git/objects/**": true,
"**/.git/subtree-cache/**": true, "**/.git/subtree-cache/**": true,
"**/node_modules/*/**": true,
"**/.hg/store/**": true, "**/.hg/store/**": true,
"**/node_modules/*/**": true,
"**/.flatpak": true, "**/.flatpak": true,
"**/src/lib": true, "**/src/lib": true,
".flatpak": true, ".flatpak": true,
@ -14,6 +14,12 @@
"_build/**": true "_build/**": true
}, },
"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.defaultFormatter": "esbenp.prettier-vscode",
"[yaml]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"rust-analyzer.server.path": "${workspaceFolder}/.flatpak/rust-analyzer.sh", "rust-analyzer.server.path": "${workspaceFolder}/.flatpak/rust-analyzer.sh",
"rust-analyzer.runnables.command": "${workspaceFolder}/.flatpak/cargo.sh", "rust-analyzer.runnables.command": "${workspaceFolder}/.flatpak/cargo.sh",
"rust-analyzer.files.excludeDirs": [ "rust-analyzer.files.excludeDirs": [
@ -23,13 +29,10 @@
"build", "build",
"builddir" "builddir"
], ],
"[yaml]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"vala.languageServerPath": "${workspaceFolder}/.flatpak/vala-language-server.sh", "vala.languageServerPath": "${workspaceFolder}/.flatpak/vala-language-server.sh",
"[meson]": {
"editor.defaultFormatter": "mesonbuild.mesonbuild"
},
"mesonbuild.configureOnOpen": false, "mesonbuild.configureOnOpen": false,
"mesonbuild.buildFolder": "_build", "mesonbuild.buildFolder": "_build",
"mesonbuild.mesonPath": "${workspaceFolder}/.flatpak/meson.sh" "mesonbuild.mesonPath": "${workspaceFolder}/.flatpak/meson.sh"

View File

@ -32,7 +32,7 @@ We provide a couple of tools to make the development process pleasant.
# sudo dnf install flatpak flatpak-builder nodejs make gcc gcc-c++ # sudo dnf install flatpak flatpak-builder nodejs make gcc gcc-c++
cd Workbench cd Workbench
make setup make
``` ```
Before submitting a PR, we recommend running tests locally with Before submitting a PR, we recommend running tests locally with

View File

@ -1,19 +1,30 @@
SHELL:=/bin/bash -O globstar SHELL:=/bin/bash -O globstar
.PHONY: setup build lint unit test ci sandbox flatpak .PHONY: setup build lint unit test ci sandbox flatpak
.DEFAULT_GOAL := test .DEFAULT_GOAL := setup
setup: setup:
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak install --or-update --user --noninteractive flathub org.gnome.Sdk//48 org.flatpak.Builder org.freedesktop.Sdk.Extension.rust-stable//24.08 org.freedesktop.Sdk.Extension.vala//24.08 org.freedesktop.Sdk.Extension.llvm18//24.08 org.freedesktop.Sdk.Extension.node20//24.08 org.freedesktop.Sdk.Extension.typescript//24.08
# flatpak remote-add --user --if-not-exists flathub-beta https://flathub.org/beta-repo/flathub-beta.flatpakrepo # flatpak remote-add --user --if-not-exists flathub-beta https://flathub.org/beta-repo/flathub-beta.flatpakrepo
flatpak install --or-update --user --noninteractive flathub org.gnome.Sdk//46 org.flatpak.Builder org.freedesktop.Sdk.Extension.rust-stable//23.08 org.freedesktop.Sdk.Extension.vala//23.08 org.freedesktop.Sdk.Extension.llvm16//23.08 # flatpak remote-add --user --if-not-exists gnome-nightly https://nightly.gnome.org/gnome-nightly.flatpakrepo
npm install # flatpak install --or-update --user --noninteractive gnome-nightly org.gnome.Sdk//master
make build git submodule update --init
npm install --no-fund
@echo "✅ You can use "make build" to build Workbench"
build: stable:
# flatpak --user run org.flatpak.Builder --delete-build-dirs --disable-updates --build-only --ccache --force-clean flatpak build-aux/re.sonny.Workbench.json
flatpak-builder --delete-build-dirs --disable-updates --build-only --ccache --force-clean flatpak build-aux/re.sonny.Workbench.json
devel:
# flatpak --user run org.flatpak.Builder --delete-build-dirs --disable-updates --build-only --ccache --force-clean flatpak build-aux/re.sonny.Workbench.Devel.json
flatpak-builder --delete-build-dirs --disable-updates --build-only --ccache --force-clean flatpak build-aux/re.sonny.Workbench.Devel.json flatpak-builder --delete-build-dirs --disable-updates --build-only --ccache --force-clean flatpak build-aux/re.sonny.Workbench.Devel.json
build: devel
cli: cli:
./troll/gjspack/bin/gjspack src/cli/main.js --appid=re.sonny.Workbench.cli --prefix=/re/sonny/Workbench --resource-root=src/ --no-executable flatpak/files/share/re.sonny.Workbench.cli/ ./troll/gjspack/bin/gjspack src/cli/main.js --appid=re.sonny.Workbench.cli --prefix=/re/sonny/Workbench --resource-root=src/ --no-executable flatpak/files/share/re.sonny.Workbench.cli/
cp src/cli/bin.js flatpak/files/bin/workbench-cli cp src/cli/bin.js flatpak/files/bin/workbench-cli
lint: lint:
@ -32,11 +43,12 @@ lint:
# CSS # CSS
./build-aux/fun workbench-cli check css src/**/*.css ./build-aux/fun workbench-cli check css src/**/*.css
# Flatpak manifests # Flatpak manifests
flatpak run --user --command=flatpak-builder-lint org.flatpak.Builder manifest --exceptions build-aux/re.sonny.Workbench.json flatpak run --user --command=flatpak-builder-lint org.flatpak.Builder manifest --exceptions --user-exceptions ./build-aux/exceptions.json build-aux/re.sonny.Workbench.json
flatpak run --user --command=flatpak-builder-lint org.flatpak.Builder manifest --exceptions build-aux/re.sonny.Workbench.Devel.json flatpak run --user --command=flatpak-builder-lint org.flatpak.Builder manifest --exceptions --user-exceptions ./build-aux/exceptions.json build-aux/re.sonny.Workbench.Devel.json
unit: unit:
./build-aux/fun gjs -m ./troll/tst/bin.js test/*.test.js ./build-aux/fun gjs -m ./troll/tst/bin.js test/*.test.js
#./build-aux/wip/run.js build-aux/re.sonny.Workbench.Devel.json -- gjs -m ./troll/tst/bin.js test/*.test.js
# https://github.com/ximion/appstream/issues/398#issuecomment-1129454985 # https://github.com/ximion/appstream/issues/398#issuecomment-1129454985
# flatpak run org.freedesktop.appstream.cli validate --override=release-time-missing=info --no-net data/app.metainfo.xml # flatpak run org.freedesktop.appstream.cli validate --override=release-time-missing=info --no-net data/app.metainfo.xml
@ -50,24 +62,27 @@ unit:
test: unit lint test: unit lint
./build-aux/fun workbench-cli ci demos/src/Welcome ./build-aux/fun workbench-cli ci demos/src/Welcome
# ./build-aux/wip/run.js build-aux/re.sonny.Workbench.Devel.json -- workbench-cli ci demos/src/Welcome/
ci: setup test ci: setup build test
# See Permissions.js
# flatpak override --user --share=network --socket=pulseaudio --device=input re.sonny.Workbench.Devel
./build-aux/fun workbench-cli ci demos/src/* ./build-aux/fun workbench-cli ci demos/src/*
# Note that if you have Sdk extensions installed they will be used # Note that if you have Sdk extensions installed they will be used
# make sure to test without the sdk extensions installed # make sure to test without the sdk extensions installed
sandbox: setup sandbox: setup
flatpak-builder --ccache --user --install --force-clean flatpak build-aux/re.sonny.Workbench.Devel.json flatpak run org.flatpak.Builder --ccache --user --install --force-clean flatpak build-aux/re.sonny.Workbench.Devel.json
# flatpak remove --noninteractive org.freedesktop.Sdk.Extension.rust-stable//23.08 org.freedesktop.Sdk.Extension.vala//23.08 org.freedesktop.Sdk.Extension.llvm16//23.08 # flatpak remove --noninteractive org.freedesktop.Sdk.Extension.rust-stable//24.08 org.freedesktop.Sdk.Extension.vala//24.08 org.freedesktop.Sdk.Extension.llvm18//24.08
flatpak run --command="bash" re.sonny.Workbench.Devel flatpak run --command="bash" re.sonny.Workbench.Devel
flatpak: flatpak:
flatpak-builder --ccache --force-clean flatpak build-aux/re.sonny.Workbench.Devel.json flatpak run org.flatpak.Builder --ccache --force-clean flatpak build-aux/re.sonny.Workbench.Devel.json
# This is what Flathub does - consider moving to lint # This is what Flathub does - consider moving to lint
flatpak run --env=G_DEBUG=fatal-criticals --command=appstream-util org.flatpak.Builder validate flatpak/files/share/appdata/re.sonny.Workbench.Devel.appdata.xml flatpak run --env=G_DEBUG=fatal-criticals --command=appstream-util org.flatpak.Builder validate flatpak/files/share/appdata/re.sonny.Workbench.Devel.appdata.xml
flatpak run --command="desktop-file-validate" --filesystem=host:ro org.freedesktop.Sdk//23.08 flatpak/files/share/applications/re.sonny.Workbench.Devel.desktop flatpak run --command="desktop-file-validate" --filesystem=host:ro org.freedesktop.Sdk//24.08 flatpak/files/share/applications/re.sonny.Workbench.Devel.desktop
# appstreamcli validate --override=release-time-missing=info /path/to/your/app.metainfo.xml # appstreamcli validate --override=release-time-missing=info /path/to/your/app.metainfo.xml
flatpak-builder --run flatpak build-aux/re.sonny.Workbench.Devel.json bash flatpak run org.flatpak.Builder --run flatpak build-aux/re.sonny.Workbench.Devel.json bash
# Sync with .gitignore # Sync with .gitignore
clean: clean:

View File

@ -6,7 +6,7 @@ Learn and prototype with GNOME technologies
![](data/workbench.gif) ![](data/workbench.gif)
<a href='https://flathub.org/apps/re.sonny.Workbench'><img width='240' height='80' alt='Download on Flathub' src='https://dl.flathub.org/assets/badges/flathub-badge-en.svg'/></a> <a href='https://flathub.org/apps/re.sonny.Workbench'><img width='240' alt='Download on Flathub' src='https://flathub.org/api/badge?svg&locale=en'/></a>
Workbench lets you experiment with GNOME technologies, no matter if tinkering for the first time or building and testing a GTK user interface. Workbench lets you experiment with GNOME technologies, no matter if tinkering for the first time or building and testing a GTK user interface.
@ -14,12 +14,12 @@ Among other things, Workbench comes with
- Live GTK/CSS preview - Live GTK/CSS preview
- Library of 100+ examples - Library of 100+ examples
<!-- - JavaScript, TypeScript, Rust, Python and Vala support -->
- JavaScript, Rust, Python and Vala support - JavaScript, Rust, Python and Vala support
- Declarative user interface syntax - Declarative user interface syntax
- Autosave, sessions and projects - Autosave, sessions and projects
- Code linter and formatter - Code diagnostics, completion and formatter
- Terminal output - Terminal output
- 1000+ icons
Workbench is made possible by Flatpak. Only Flathub Workbench is supported. Workbench is made possible by Flatpak. Only Flathub Workbench is supported.
@ -31,21 +31,21 @@ Among other things, Workbench comes with
| | Formatter | Linter | Library demos[1] | | | Formatter | Linter | Library demos[1] |
| ---------- | --------- | ------ | ---------------- | | ---------- | --------- | ------ | ---------------- |
| JavaScript | ✅ | ✅ | 95 | | JavaScript | ✅ | ✅ | 103 |
| Python | ✅ | ✅ | 89 | | Python | ✅ | ✅ | 98 |
| Vala | ✅ | ✅ | 59 | | Vala | ✅ | ✅ | 92 |
| Rust | ✅ | ✅ | 45 | | Rust | ✅ | ✅ | 52 |
| Blueprint | ✅ | ✅ | | | Blueprint | ✅ | ✅ | |
| CSS | ✅ | ✅ | | | CSS | ✅ | ✅ | |
[1] As of 2024-01-14 <!--counted with `~/go/bin/scc demos/src`--> [1] As of 2024-06-16 <!--counted with `~/go/bin/scc demos/src`-->
## Tips and tricks ## Tips and tricks
<details> <details>
<summary>Disable code formatting</summary> <summary>Disable code formatting</summary>
[JavaScript](https://docs.rome.tools/formatter/#ignoring-code) [JavaScript](https://biomejs.dev/formatter/#ignore-code)
[CSS](https://prettier.io/docs/en/ignore.html#css) [CSS](https://prettier.io/docs/en/ignore.html#css)

View File

@ -9,8 +9,8 @@
<shortdesc>Learn and prototype with GNOME technologies</shortdesc> <shortdesc>Learn and prototype with GNOME technologies</shortdesc>
<description>Workbench goal is to let you experiment with GNOME technologies, no matter if tinkering for the first time or building and testing a custom GTK widget.</description> <description>Workbench goal is to let you experiment with GNOME technologies, no matter if tinkering for the first time or building and testing a custom GTK widget.</description>
<homepage rdf:resource="https://apps.gnome.org/Workbench" /> <homepage rdf:resource="https://workbench.sonny.re" />
<bug-database rdf:resource="https://github.com/workbenchdev/Workbench/issues" /> <bug-database rdf:resource="https://workbench.sonny.re/feedback" />
<programming-language>JavaScript</programming-language> <programming-language>JavaScript</programming-language>
<programming-language>Vala</programming-language> <programming-language>Vala</programming-language>
<programming-language>C</programming-language> <programming-language>C</programming-language>

1
blueprint-compiler Submodule

@ -0,0 +1 @@
Subproject commit 04ef0944db56ab01307a29aaa7303df6067cb3c0

View File

@ -4,16 +4,6 @@ This directory contains the Flatpak Manifest and other build utilities.
## Workbench Flatpak Manifests ## Workbench Flatpak Manifests
### gst-plugin-gtk4
```sh
wget https://crates.io/api/v1/crates/gst-plugin-gtk4/0.11.1/download
tar-xf download
cd gst-plugin-gtk4-0.11.1/
~/Projects/flathub/flatpak-builder-tools/cargo/flatpak-cargo-generator.py Cargo.lock
# cp generated-sources.json to gst-plugin-gtk4-sources.json
```
### Python Modules ### Python Modules
The `modules/python-*.json` files contain Flatpak modules to install Python dependencies. The `modules/python-*.json` files contain Flatpak modules to install Python dependencies.

View File

@ -0,0 +1,4 @@
{
"re.sonny.Workbench": ["external-gitmodule-url-found"],
"re.sonny.Workbench.Devel": ["external-gitmodule-url-found"]
}

View File

@ -1,77 +0,0 @@
#!/usr/bin/env -S gjs -m
import Gio from "gi://Gio";
import GLib from "gi://GLib";
Gio._promisify(
Gio.File.prototype,
"enumerate_children_async",
"enumerate_children_finish",
);
Gio._promisify(
Gio.File.prototype,
"replace_contents_async",
"replace_contents_finish",
);
const demos_dir = Gio.File.new_for_path(
GLib.getenv("MESON_SOURCE_ROOT"),
).get_child("demos/src");
const demos = [];
const enumerator = await demos_dir.enumerate_children_async(
`${Gio.FILE_ATTRIBUTE_STANDARD_NAME},${Gio.FILE_ATTRIBUTE_STANDARD_IS_HIDDEN}`,
Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
GLib.PRIORITY_DEFAULT,
null,
);
for (const file_info of enumerator) {
if (file_info.get_is_hidden()) continue;
if (file_info.get_file_type() !== Gio.FileType.DIRECTORY) continue;
const demo_dir = enumerator.get_child(file_info);
let str;
try {
str = new TextDecoder().decode(
demo_dir.get_child("main.json").load_contents(null)[1],
);
} catch (err) {
console.warn(err);
continue;
}
const demo = JSON.parse(str);
demo.name = file_info.get_name();
const languages = [];
if (demo_dir.get_child("main.js").query_exists(null)) {
languages.push("javascript");
}
if (demo_dir.get_child("main.vala").query_exists(null)) {
languages.push("vala");
}
if (demo_dir.get_child("code.rs").query_exists(null)) {
languages.push("rust");
}
if (demo_dir.get_child("main.py").query_exists(null)) {
languages.push("python");
}
demo.languages = languages;
demos.push(demo);
}
const [pkgdatadir] = ARGV;
GLib.mkdir_with_parents(pkgdatadir, 0o755);
await Gio.File.new_for_path(pkgdatadir)
.get_child("demos/index.json")
.replace_contents_async(
new TextEncoder().encode(JSON.stringify(demos)),
null,
false,
Gio.FileCreateFlags.NONE,
null,
);

250
build-aux/library.js Executable file
View File

@ -0,0 +1,250 @@
#!/usr/bin/env -S gjs -m
import Gio from "gi://Gio";
import GLib from "gi://GLib";
import { programArgs, exit } from "system";
Gio._promisify(
Gio.File.prototype,
"enumerate_children_async",
"enumerate_children_finish",
);
Gio._promisify(
Gio.File.prototype,
"replace_contents_async",
"replace_contents_finish",
);
Gio._promisify(
Gio.File.prototype,
"make_directory_async",
"make_directory_finish",
);
Gio._promisify(Gio.File.prototype, "copy_async", "copy_finish");
const loop = new GLib.MainLoop(null, false);
const [pkgdatadir] = programArgs;
GLib.mkdir_with_parents(
Gio.File.new_for_path(pkgdatadir).get_child("demos").get_path(),
0o755,
);
const demos_dir = Gio.File.new_for_path(
GLib.getenv("MESON_SOURCE_ROOT"),
).get_child("demos/src");
const demos = [];
(async () => {
const enumerator = await demos_dir.enumerate_children_async(
`${Gio.FILE_ATTRIBUTE_STANDARD_NAME},${Gio.FILE_ATTRIBUTE_STANDARD_IS_HIDDEN}`,
Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
GLib.PRIORITY_DEFAULT,
null,
);
for (const file_info of enumerator) {
if (file_info.get_is_hidden()) continue;
if (file_info.get_file_type() !== Gio.FileType.DIRECTORY) continue;
const demo_dir = enumerator.get_child(file_info);
let str;
try {
str = new TextDecoder().decode(
demo_dir.get_child("main.json").load_contents(null)[1],
);
} catch (err) {
console.warn(err);
continue;
}
const demo = JSON.parse(str);
demo.name = file_info.get_name();
if (!isDemoCompatible(demo)) {
continue;
}
const languages = [];
if (demo_dir.get_child("main.js").query_exists(null)) {
languages.push("javascript");
}
if (demo_dir.get_child("main.ts").query_exists(null)) {
languages.push("typescript");
}
if (demo_dir.get_child("main.vala").query_exists(null)) {
languages.push("vala");
}
if (demo_dir.get_child("code.rs").query_exists(null)) {
languages.push("rust");
}
if (demo_dir.get_child("main.py").query_exists(null)) {
languages.push("python");
}
demo.languages = languages;
await copyDemo(
demo_dir,
Gio.File.new_for_path(pkgdatadir)
.get_child("demos")
.get_child(demo_dir.get_basename()),
);
demos.push(demo);
}
demos.sort((a, b) => {
if (a.name === "Welcome") return -1;
if (b.name === "Welcome") return 1;
if (a.name === "Platform Tools") return -1;
if (b.name === "Platform Tools") return 1;
else return a.name.localeCompare(b.name);
});
await Gio.File.new_for_path(pkgdatadir)
.get_child("demos/index.json")
.replace_contents_async(
new TextEncoder().encode(JSON.stringify(demos)),
null,
false,
Gio.FileCreateFlags.NONE,
null,
);
})()
.catch((err) => {
loop.quit();
console.error(err);
exit(1);
})
.then(() => {
loop.quit();
});
async function copyDemo(source, destination) {
try {
destination.make_directory_with_parents(null);
} catch (err) {
if (!err.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS)) {
throw err;
}
}
const enumerator = await source.enumerate_children_async(
`${Gio.FILE_ATTRIBUTE_STANDARD_NAME},${Gio.FILE_ATTRIBUTE_STANDARD_IS_HIDDEN}`,
Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
GLib.PRIORITY_DEFAULT,
null,
);
for await (const file_info of enumerator) {
if (file_info.get_is_hidden()) continue;
const child = enumerator.get_child(file_info);
if (
// Sync with demos "make clean" and .gitignore
[
"Cargo.lock",
"Cargo.toml",
"lib.rs",
"workbench.rs",
"workbench.vala",
"libworkbenchcode.so",
"settings",
"main.ui",
"icons.gresource",
"icons.gresource.xml",
].includes(child.get_basename())
) {
continue;
}
const child_dest = destination.get_child(child.get_basename());
if (file_info.get_file_type() === Gio.FileType.DIRECTORY) {
await copyDirectory(child, child_dest);
continue;
}
try {
await child.copy_async(
child_dest, // destination
Gio.FileCopyFlags.NONE, // flags
GLib.PRIORITY_DEFAULT, // priority
null, // cancellable
null, // progress_callback
);
} catch (err) {
if (!err.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS)) {
throw err;
}
}
}
}
async function copyDirectory(source, destination) {
try {
await destination.make_directory_async(GLib.PRIORITY_DEFAULT, null);
} catch (err) {
if (!err.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS)) {
throw err;
}
}
const enumerator = await source.enumerate_children_async(
`${Gio.FILE_ATTRIBUTE_STANDARD_NAME},${Gio.FILE_ATTRIBUTE_STANDARD_IS_HIDDEN}`,
Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
GLib.PRIORITY_DEFAULT,
null,
);
for await (const file_info of enumerator) {
if (file_info.get_is_hidden()) continue;
const child = enumerator.get_child(file_info);
const child_dest = destination.get_child(child.get_basename());
if (file_info.get_file_type() === Gio.FileType.DIRECTORY) {
await copyDirectory(child, child_dest);
continue;
}
try {
await child.copy_async(
child_dest, // destination
Gio.FileCopyFlags.NONE, // flags
GLib.PRIORITY_DEFAULT, // priority
null, // cancellable
null, // progress_callback
);
} catch (err) {
if (!err.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS)) {
throw err;
}
}
}
}
const key_file = new GLib.KeyFile();
key_file.load_from_file("/.flatpak-info", GLib.KeyFileFlags.NONE);
// runtime/org.gnome.Sdk/x86_64/master
const [, , , runtime_version] = key_file
.get_string("Application", "runtime")
.split("/");
function isDemoCompatible(demo) {
const demo_runtime_version = demo["runtime-version"];
if (demo_runtime_version === "master") {
return runtime_version === "master";
} else if (runtime_version === "master") {
return true;
} else if (!demo_runtime_version) {
return true;
}
return +runtime_version >= +demo_runtime_version;
}
loop.run();

View File

@ -12,13 +12,17 @@
"modules": [ "modules": [
{ {
"name": "jsonrpc-glib", "name": "jsonrpc-glib",
"config-opts": ["--buildtype=release", "-Denable_tests=false"], "config-opts": [
"--libdir=/app/lib",
"--buildtype=release",
"-Denable_tests=false"
],
"buildsystem": "meson", "buildsystem": "meson",
"sources": [ "sources": [
{ {
"type": "archive", "type": "archive",
"url": "https://download.gnome.org/sources/jsonrpc-glib/3.44/jsonrpc-glib-3.44.0.tar.xz", "url": "https://download.gnome.org/sources/jsonrpc-glib/3.44/jsonrpc-glib-3.44.1.tar.xz",
"sha256": "69406a0250d0cc5175408cae7eca80c0c6bfaefc4ae1830b354c0433bcd5ce06" "sha256": "1361d17e9c805646afe5102e59baf8ca450238600fcabd01586c654b78bb30df"
} }
] ]
} }

View File

@ -7,15 +7,15 @@
"type": "file", "type": "file",
"dest-filename": "biome", "dest-filename": "biome",
"only-arches": ["aarch64"], "only-arches": ["aarch64"],
"url": "https://github.com/biomejs/biome/releases/download/cli%2Fv1.6.0/biome-linux-arm64", "url": "https://github.com/biomejs/biome/releases/download/cli%2Fv1.9.4/biome-linux-arm64",
"sha256": "e25968d3cc7df0e8e36ecf99d87296c0dca394d18ada7e75cbe22cec149e4549" "sha256": "f0f0f3e7cdec78420a600b05bfc364aa9b804811bd3bbae04e7bf090828ae970"
}, },
{ {
"type": "file", "type": "file",
"dest-filename": "biome", "dest-filename": "biome",
"only-arches": ["x86_64"], "only-arches": ["x86_64"],
"url": "https://github.com/biomejs/biome/releases/download/cli%2Fv1.6.0/biome-linux-x64", "url": "https://github.com/biomejs/biome/releases/download/cli%2Fv1.9.4/biome-linux-x64",
"sha256": "207ae06f84e4d4f0958837fa8e8c97a347af2217a4c55f5c6b53fba521095696" "sha256": "ce247fb644999ef52e5111dd6fd6e471019669fc9c4a44b5699721e39b7032c3"
} }
] ]
} }

View File

@ -3,9 +3,8 @@
"buildsystem": "meson", "buildsystem": "meson",
"sources": [ "sources": [
{ {
"type": "git", "type": "dir",
"url": "https://gitlab.gnome.org/jwestman/blueprint-compiler.git", "path": "../../blueprint-compiler"
"commit": "d47955c5a20b2f7cf85ff25a00b02160883aa0b1"
} }
] ]
} }

View File

@ -0,0 +1,12 @@
{
"name": "gom",
"buildsystem": "meson",
"config-opts": ["--libdir=/app/lib", "-Denable-gtk-doc=false"],
"sources": [
{
"type": "archive",
"url": "https://download.gnome.org/sources/gom/0.5/gom-0.5.3.tar.xz",
"sha256": "069d0909fbdc6b4d27edf7a879366194e3ab508b03548bf5b89ff63546d20177"
}
]
}

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +0,0 @@
{
"name": "gst-plugin-gtk4",
"buildsystem": "simple",
"sources": [
{
"type": "archive",
"url": "https://crates.io/api/v1/crates/gst-plugin-gtk4/0.12.1/download",
"dest-filename": "gst-plugin-gtk4-0.12.1.tar.gz",
"sha256": "162762eb8df14f696707ff54382b3c02f58706255ab3a37a61d59bc1d0c0115a"
},
"gst-plugin-gtk4-sources.json"
],
"build-commands": [
"cargo cinstall --offline --release --features=wayland,x11glx,x11egl --library-type=cdylib --prefix=/app"
],
"build-options": {
"env": {
"CARGO_HOME": "/run/build/gst-plugin-gtk4/cargo"
}
}
}

View File

@ -1,40 +0,0 @@
{
"name": "icon-development-kit",
"buildsystem": "simple",
"build-commands": [
"mkdir -p /app/share/icon-development-kit",
"mkdir -p /app/share/icons/hicolor/scalable/actions/",
"yq -o=json _data/icons.yaml > /app/share/icon-development-kit/icons.json",
"cp -r img/symbolic/**/*.svg /app/share/icons/hicolor/scalable/actions/ || true"
],
"sources": [
{
"type": "git",
"url": "https://gitlab.gnome.org/Teams/Design/icon-development-kit-www.git",
"commit": "c5fecee437afd64fa0aac5755af9259aaa24b667"
}
],
"modules": [
{
"name": "yq",
"buildsystem": "simple",
"build-commands": ["chmod +x yq", "cp yq /app/bin/yq"],
"sources": [
{
"type": "file",
"only-arches": ["x86_64"],
"url": "https://github.com/mikefarah/yq/releases/download/v4.33.3/yq_linux_amd64",
"sha256": "4ee662847c588c3ef2fec8bfb304e8739e3dbaba87ccb9a608d691c88f5b64dc",
"dest-filename": "yq"
},
{
"type": "file",
"only-arches": ["aarch64"],
"url": "https://github.com/mikefarah/yq/releases/download/v4.33.3/yq_linux_arm64",
"sha256": "15925a972d268bcb0a7aa2236c7e5925b7a3ba4b5569bb57e943db7e8c6f834f",
"dest-filename": "yq"
}
]
}
]
}

View File

@ -2,6 +2,7 @@
"name": "libportal", "name": "libportal",
"buildsystem": "meson", "buildsystem": "meson",
"config-opts": [ "config-opts": [
"--libdir=/app/lib",
"-Dtests=false", "-Dtests=false",
"-Dbackend-gtk3=disabled", "-Dbackend-gtk3=disabled",
"-Dbackend-gtk4=enabled", "-Dbackend-gtk4=enabled",
@ -11,8 +12,8 @@
"sources": [ "sources": [
{ {
"type": "archive", "type": "archive",
"url": "https://github.com/flatpak/libportal/releases/download/0.7.1/libportal-0.7.1.tar.xz", "url": "https://github.com/flatpak/libportal/releases/download/0.9.1/libportal-0.9.1.tar.xz",
"sha256": "297b90b263fad22190a26b8c7e8ea938fe6b18fb936265e588927179920d3805" "sha256": "de801ee349ed3c255a9af3c01b1a401fab5b3fc1c35eb2fd7dfb35d4b8194d7f"
} }
] ]
} }

View File

@ -1,12 +1,12 @@
{ {
"name": "libshumate", "name": "libshumate",
"buildsystem": "meson", "buildsystem": "meson",
"config-opts": ["-Dgtk_doc=false"], "config-opts": ["--libdir=/app/lib", "-Ddemos=false", "-Dgtk_doc=false"],
"sources": [ "sources": [
{ {
"type": "archive", "type": "archive",
"url": "https://download.gnome.org/sources/libshumate/1.2/libshumate-1.2.0.tar.xz", "url": "https://download.gnome.org/sources/libshumate/1.4/libshumate-1.4.rc.tar.xz",
"sha256": "4f8413a707cd00f84cee39ca49f58c48fc436f008ea80d6532ac37dafd0ba96b" "sha256": "a57b97dbbda55bcb07c6f0197ff006128518c2b2c88fbee8bea5168acbc9baee"
} }
], ],
"modules": [ "modules": [
@ -29,8 +29,8 @@
"sources": [ "sources": [
{ {
"type": "archive", "type": "archive",
"url": "https://github.com/protobuf-c/protobuf-c/releases/download/v1.5.0/protobuf-c-1.5.0.tar.gz", "url": "https://github.com/protobuf-c/protobuf-c/releases/download/v1.4.0/protobuf-c-1.4.0.tar.gz",
"sha256": "7b404c63361ed35b3667aec75cc37b54298d56dd2bcf369de3373212cc06fd98" "sha256": "26d98ee9bf18a6eba0d3f855ddec31dbe857667d269bc0b6017335572f85bbcb"
} }
] ]
} }

View File

@ -1,12 +1,12 @@
{ {
"name": "libspelling", "name": "libspelling",
"buildsystem": "meson", "buildsystem": "meson",
"config-opts": ["-Ddocs=false"], "config-opts": ["--libdir=/app/lib", "-Ddocs=false"],
"sources": [ "sources": [
{ {
"type": "archive", "type": "archive",
"url": "https://download.gnome.org/sources/libspelling/0.2/libspelling-0.2.1.tar.xz", "url": "https://download.gnome.org/sources/libspelling/0.4/libspelling-0.4.6.tar.xz",
"sha256": "7a787b467bd493f6baffb44138dbc4bef78aaab60efb76a7db88b243bf0f6343" "sha256": "3248a9b5336ea2f727d2db912d2f0083accc0505ce707679b3d9b8266c0101f5"
} }
] ]
} }

View File

@ -2,6 +2,7 @@
"name": "vte", "name": "vte",
"buildsystem": "meson", "buildsystem": "meson",
"config-opts": [ "config-opts": [
"--libdir=/app/lib",
"-Ddocs=false", "-Ddocs=false",
"-Dgtk3=false", "-Dgtk3=false",
"-Dgtk4=true", "-Dgtk4=true",
@ -11,8 +12,21 @@
"sources": [ "sources": [
{ {
"type": "archive", "type": "archive",
"url": "https://download.gnome.org/sources/vte/0.76/vte-0.76.0.tar.xz", "url": "https://download.gnome.org/sources/vte/0.79/vte-0.79.91.tar.xz",
"sha256": "bbce30b8f504370b12d6439c07a82993e97d7e9afe2dd367817cd58ff029ffda" "sha256": "adca667d40ae1839ba258b63701cd05fba249303d72258711cc69294b2cb8646"
}
],
"modules": [
{
"name": "fast_float",
"buildsystem": "cmake-ninja",
"sources": [
{
"type": "archive",
"url": "https://github.com/fastfloat/fast_float/archive/refs/tags/v6.1.6.tar.gz",
"sha256": "4458aae4b0eb55717968edda42987cabf5f7fc737aee8fede87a70035dba9ab0"
}
]
} }
] ]
} }

View File

@ -1,15 +1,18 @@
{ {
"$schema": "https://raw.githubusercontent.com/flatpak/flatpak-builder/main/data/flatpak-manifest.schema.json",
"id": "re.sonny.Workbench.Devel", "id": "re.sonny.Workbench.Devel",
"runtime": "org.gnome.Sdk", "runtime": "org.gnome.Sdk",
"runtime-version": "46", "runtime-version": "48",
"sdk": "org.gnome.Sdk", "sdk": "org.gnome.Sdk",
"sdk-extensions": [ "sdk-extensions": [
"org.freedesktop.Sdk.Extension.vala", "org.freedesktop.Sdk.Extension.vala",
"org.freedesktop.Sdk.Extension.rust-stable", "org.freedesktop.Sdk.Extension.rust-stable",
"org.freedesktop.Sdk.Extension.llvm16" "org.freedesktop.Sdk.Extension.llvm18",
"org.freedesktop.Sdk.Extension.node20",
"org.freedesktop.Sdk.Extension.typescript"
], ],
"build-options": { "build-options": {
"append-path": "/usr/lib/sdk/vala/bin:/usr/lib/sdk/rust-stable/bin", "append-path": "/usr/lib/sdk/vala/bin:/usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/node20/bin:/usr/lib/sdk/typescript/bin",
"append-ld-library-path": "/usr/lib/sdk/vala/lib" "append-ld-library-path": "/usr/lib/sdk/vala/lib"
}, },
"command": "workbench", "command": "workbench",
@ -18,9 +21,7 @@
"--share=ipc", "--share=ipc",
"--socket=fallback-x11", "--socket=fallback-x11",
"--socket=wayland", "--socket=wayland",
"--device=dri", "--device=dri"
"--share=network",
"--socket=pulseaudio"
], ],
"cleanup": [ "cleanup": [
"#/include", "#/include",
@ -37,13 +38,12 @@
"modules": [ "modules": [
"modules/blueprint-compiler.json", "modules/blueprint-compiler.json",
"modules/biome.json", "modules/biome.json",
"modules/gst-plugin-gtk4.json",
"modules/vte.json", "modules/vte.json",
"modules/libshumate.json", "modules/libshumate.json",
"modules/libportal.json", "modules/libportal.json",
"modules/libspelling.json", "modules/libspelling.json",
"modules/gom.json",
"modules/GTKCssLanguageServer.json", "modules/GTKCssLanguageServer.json",
"modules/icon-development-kit.json",
"modules/python-python-lsp-server.json", "modules/python-python-lsp-server.json",
"modules/python-ruff.json", "modules/python-ruff.json",
"modules/python-python-lsp-ruff.json", "modules/python-python-lsp-ruff.json",

View File

@ -1,15 +1,18 @@
{ {
"$schema": "https://raw.githubusercontent.com/flatpak/flatpak-builder/main/data/flatpak-manifest.schema.json",
"id": "re.sonny.Workbench", "id": "re.sonny.Workbench",
"runtime": "org.gnome.Sdk", "runtime": "org.gnome.Sdk",
"runtime-version": "46", "runtime-version": "48",
"sdk": "org.gnome.Sdk", "sdk": "org.gnome.Sdk",
"sdk-extensions": [ "sdk-extensions": [
"org.freedesktop.Sdk.Extension.vala", "org.freedesktop.Sdk.Extension.vala",
"org.freedesktop.Sdk.Extension.rust-stable", "org.freedesktop.Sdk.Extension.rust-stable",
"org.freedesktop.Sdk.Extension.llvm16" "org.freedesktop.Sdk.Extension.llvm18",
"org.freedesktop.Sdk.Extension.node20",
"org.freedesktop.Sdk.Extension.typescript"
], ],
"build-options": { "build-options": {
"append-path": "/usr/lib/sdk/vala/bin:/usr/lib/sdk/rust-stable/bin", "append-path": "/usr/lib/sdk/vala/bin:/usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/node20/bin:/usr/lib/sdk/typescript/bin",
"append-ld-library-path": "/usr/lib/sdk/vala/lib" "append-ld-library-path": "/usr/lib/sdk/vala/lib"
}, },
"command": "workbench", "command": "workbench",
@ -18,9 +21,7 @@
"--share=ipc", "--share=ipc",
"--socket=fallback-x11", "--socket=fallback-x11",
"--socket=wayland", "--socket=wayland",
"--device=dri", "--device=dri"
"--share=network",
"--socket=pulseaudio"
], ],
"cleanup": [ "cleanup": [
"#/include", "#/include",
@ -37,13 +38,12 @@
"modules": [ "modules": [
"modules/blueprint-compiler.json", "modules/blueprint-compiler.json",
"modules/biome.json", "modules/biome.json",
"modules/gst-plugin-gtk4.json",
"modules/vte.json", "modules/vte.json",
"modules/libshumate.json", "modules/libshumate.json",
"modules/libportal.json", "modules/libportal.json",
"modules/libspelling.json", "modules/libspelling.json",
"modules/gom.json",
"modules/GTKCssLanguageServer.json", "modules/GTKCssLanguageServer.json",
"modules/icon-development-kit.json",
"modules/python-python-lsp-server.json", "modules/python-python-lsp-server.json",
"modules/python-ruff.json", "modules/python-ruff.json",
"modules/python-python-lsp-ruff.json", "modules/python-python-lsp-ruff.json",

308
build-aux/wip/minimist.js Normal file
View File

@ -0,0 +1,308 @@
/* eslint-disable */
// https://github.com/minimistjs/minimist/blob/bedaa8b9ab5a901fa342aad4494cbbf676b11a21/index.js
function hasKey(obj, keys) {
var o = obj;
keys.slice(0, -1).forEach(function (key) {
o = o[key] || {};
});
var key = keys[keys.length - 1];
return key in o;
}
function isNumber(x) {
if (typeof x === "number") {
return true;
}
if (/^0x[0-9a-f]+$/i.test(x)) {
return true;
}
return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
}
function isConstructorOrProto(obj, key) {
return (
(key === "constructor" && typeof obj[key] === "function") ||
key === "__proto__"
);
}
export default function minimist(args, opts) {
if (!opts) {
opts = {};
}
var flags = {
bools: {},
strings: {},
unknownFn: null,
};
if (typeof opts.unknown === "function") {
flags.unknownFn = opts.unknown;
}
if (typeof opts.boolean === "boolean" && opts.boolean) {
flags.allBools = true;
} else {
[]
.concat(opts.boolean)
.filter(Boolean)
.forEach(function (key) {
flags.bools[key] = true;
});
}
var aliases = {};
function isBooleanKey(key) {
if (flags.bools[key]) {
return true;
}
if (!aliases[key]) {
return false;
}
return aliases[key].some(function (x) {
return flags.bools[x];
});
}
Object.keys(opts.alias || {}).forEach(function (key) {
aliases[key] = [].concat(opts.alias[key]);
aliases[key].forEach(function (x) {
aliases[x] = [key].concat(
aliases[key].filter(function (y) {
return x !== y;
}),
);
});
});
[]
.concat(opts.string)
.filter(Boolean)
.forEach(function (key) {
flags.strings[key] = true;
if (aliases[key]) {
[].concat(aliases[key]).forEach(function (k) {
flags.strings[k] = true;
});
}
});
var defaults = opts.default || {};
var argv = { _: [] };
function argDefined(key, arg) {
return (
(flags.allBools && /^--[^=]+$/.test(arg)) ||
flags.strings[key] ||
flags.bools[key] ||
aliases[key]
);
}
function setKey(obj, keys, value) {
var o = obj;
for (var i = 0; i < keys.length - 1; i++) {
var key = keys[i];
if (isConstructorOrProto(o, key)) {
return;
}
if (o[key] === undefined) {
o[key] = {};
}
if (
o[key] === Object.prototype ||
o[key] === Number.prototype ||
o[key] === String.prototype
) {
o[key] = {};
}
if (o[key] === Array.prototype) {
o[key] = [];
}
o = o[key];
}
var lastKey = keys[keys.length - 1];
if (isConstructorOrProto(o, lastKey)) {
return;
}
if (
o === Object.prototype ||
o === Number.prototype ||
o === String.prototype
) {
o = {};
}
if (o === Array.prototype) {
o = [];
}
if (
o[lastKey] === undefined ||
isBooleanKey(lastKey) ||
typeof o[lastKey] === "boolean"
) {
o[lastKey] = value;
} else if (Array.isArray(o[lastKey])) {
o[lastKey].push(value);
} else {
o[lastKey] = [o[lastKey], value];
}
}
function setArg(key, val, arg) {
if (arg && flags.unknownFn && !argDefined(key, arg)) {
if (flags.unknownFn(arg) === false) {
return;
}
}
var value = !flags.strings[key] && isNumber(val) ? Number(val) : val;
setKey(argv, key.split("."), value);
(aliases[key] || []).forEach(function (x) {
setKey(argv, x.split("."), value);
});
}
// Set booleans to false by default.
Object.keys(flags.bools).forEach(function (key) {
setArg(key, false);
});
// Set booleans to user defined default if supplied.
Object.keys(defaults)
.filter(isBooleanKey)
.forEach(function (key) {
setArg(key, defaults[key]);
});
var notFlags = [];
if (args.indexOf("--") !== -1) {
notFlags = args.slice(args.indexOf("--") + 1);
args = args.slice(0, args.indexOf("--"));
}
for (var i = 0; i < args.length; i++) {
var arg = args[i];
var key;
var next;
if (/^--.+=/.test(arg)) {
// Using [\s\S] instead of . because js doesn't support the
// 'dotall' regex modifier. See:
// http://stackoverflow.com/a/1068308/13216
var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
key = m[1];
var value = m[2];
if (isBooleanKey(key)) {
value = value !== "false";
}
setArg(key, value, arg);
} else if (/^--no-.+/.test(arg)) {
key = arg.match(/^--no-(.+)/)[1];
setArg(key, false, arg);
} else if (/^--.+/.test(arg)) {
key = arg.match(/^--(.+)/)[1];
next = args[i + 1];
if (
next !== undefined &&
!/^(-|--)[^-]/.test(next) &&
!isBooleanKey(key) &&
!flags.allBools
) {
setArg(key, next, arg);
i += 1;
} else if (/^(true|false)$/.test(next)) {
setArg(key, next === "true", arg);
i += 1;
} else {
setArg(key, flags.strings[key] ? "" : true, arg);
}
} else if (/^-[^-]+/.test(arg)) {
var letters = arg.slice(1, -1).split("");
var broken = false;
for (var j = 0; j < letters.length; j++) {
next = arg.slice(j + 2);
if (next === "-") {
setArg(letters[j], next, arg);
continue;
}
if (/[A-Za-z]/.test(letters[j]) && next[0] === "=") {
setArg(letters[j], next.slice(1), arg);
broken = true;
break;
}
if (
/[A-Za-z]/.test(letters[j]) &&
/-?\d+(\.\d*)?(e-?\d+)?$/.test(next)
) {
setArg(letters[j], next, arg);
broken = true;
break;
}
if (letters[j + 1] && letters[j + 1].match(/\W/)) {
setArg(letters[j], arg.slice(j + 2), arg);
broken = true;
break;
} else {
setArg(letters[j], flags.strings[letters[j]] ? "" : true, arg);
}
}
key = arg.slice(-1)[0];
if (!broken && key !== "-") {
if (
args[i + 1] &&
!/^(-|--)[^-]/.test(args[i + 1]) &&
!isBooleanKey(key)
) {
setArg(key, args[i + 1], arg);
i += 1;
} else if (args[i + 1] && /^(true|false)$/.test(args[i + 1])) {
setArg(key, args[i + 1] === "true", arg);
i += 1;
} else {
setArg(key, flags.strings[key] ? "" : true, arg);
}
}
} else {
if (!flags.unknownFn || flags.unknownFn(arg) !== false) {
argv._.push(flags.strings._ || !isNumber(arg) ? arg : Number(arg));
}
if (opts.stopEarly) {
argv._.push.apply(argv._, args.slice(i + 1));
break;
}
}
}
Object.keys(defaults).forEach(function (k) {
if (!hasKey(argv, k.split("."))) {
setKey(argv, k.split("."), defaults[k]);
(aliases[k] || []).forEach(function (x) {
setKey(argv, x.split("."), defaults[k]);
});
}
});
if (opts["--"]) {
argv["--"] = notFlags.slice();
} else {
notFlags.forEach(function (k) {
argv._.push(k);
});
}
return argv;
}

264
build-aux/wip/run.js Executable file
View File

@ -0,0 +1,264 @@
#!/usr/bin/env -S gjs -m
import Gio from "gi://Gio";
import { exit, programArgs, programInvocationName } from "system";
import GLib from "gi://GLib";
import minimist from "./minimist.js";
// export G_MESSAGES_DEBUG=Gjs-Console && ./run.js
Gio._promisify(
Gio.File.prototype,
"load_contents_async",
"load_contents_finish",
);
Gio._promisify(
Gio.Subprocess.prototype,
"wait_check_async",
"wait_check_finish",
);
const path = GLib.get_current_dir();
console.debug(programInvocationName, programArgs);
const argv = minimist(programArgs, { boolean: true, "--": true });
console.debug(argv);
const [manifest_path] = argv._;
if (!manifest_path) {
// eslint-disable-next-line no-restricted-globals
print(`${programInvocationName} [--verbose] [--debug] MANIFEST [-- command]`);
exit(0);
}
const home = GLib.get_home_dir();
const manifest_file = Gio.File.new_for_path(manifest_path);
const [contents] = await manifest_file.load_contents_async(null);
const manifest = JSON.parse(new TextDecoder().decode(contents));
// console.log(JSON.parse(manifest));
const flatpak_id = manifest.id;
// We assume the last module is the app itself
const app_module = manifest.modules.at(-1);
function exists(path) {
const result = Gio.File.new_for_path(path).query_exists(null);
console.debug(`${path} ${result ? "exists" : "non existant"}`);
return result;
}
if (!exists(`${path}/.flatpak/repo`)) {
const { runtime, sdk } = manifest;
const runtime_version = manifest["runtime-version"];
// initializes repo
await run([
"flatpak",
"build-init",
`${path}/.flatpak/repo`,
flatpak_id,
sdk,
runtime,
runtime_version,
]);
}
const prefix = [
"flatpak-builder",
"--ccache",
"--force-clean",
"--disable-updates",
];
const suffix = [
`--state-dir=${path}/.flatpak/flatpak-builder`,
`--stop-at=${app_module.name}`,
`${path}/.flatpak/repo`,
Gio.File.new_for_path(path).get_relative_path(manifest_file),
];
// de-initializes
async function downloadSources() {
await run([...prefix, "--download-only", ...suffix]);
}
// de-initializes
async function buildModules() {
await run([
...prefix,
"--disable-download",
"--build-only",
"--keep-build-dirs",
...suffix,
]);
}
if (!exists(`${path}/.flatpak/flatpak-builder`)) {
await downloadSources();
await buildModules();
}
// builds workbench
if (!exists(`${path}/_build`)) {
await buildModules();
await buildCommand([
"meson",
"--prefix",
"/app",
"_build",
"-Dprofile=development",
]);
}
await buildCommand(["meson", "install", "-C", "_build"]);
// await buildCommand([
// `troll/gjspack/bin/gjspack`,
// `--appid=${flatpak_id}`,
// "--prefix=/re/sonny/Workbench",
// `--project-root=.`,
// `--resource-root=./src`,
// "--blueprint-compiler=/app/bin/blueprint-compiler",
// "--no-executable",
// `${path}/src/main.js`,
// `/app/share/${flatpak_id}.src.gresource`,
// ]);
// starts workbench
const command = argv["--"].length ? argv["--"] : [manifest.command];
await runCommand(command);
function buildCommand(argv) {
let PATH =
"/app/bin:/app/bin:/app/bin:/usr/bin:${home}/.var/app/com.visualstudio.code/data/node_modules/bin:/app/bin:/usr/bin";
const append_path = manifest["build-options"]?.["append-path"];
if (append_path) PATH += `:${append_path}`;
let LD_LIBRARY_PATH = "/app/lib:/app/lib";
const append_ld_library_path =
manifest["build-options"]?.["append-ls-library-path"];
if (append_ld_library_path) LD_LIBRARY_PATH += `:${append_ld_library_path}`;
const PKG_CONFIG_PATH =
"/usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig:/usr/local/share/pkgconfig:/usr/lib64/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig:/app/lib/pkgconfig:/app/share/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig";
return run([
"flatpak",
"build",
"--share=network",
`--filesystem=${path}`,
`--filesystem=${path}/.flatpak/repo`,
`--env=PATH=${PATH}`,
`--env=LD_LIBRARY_PATH=${LD_LIBRARY_PATH}`,
`--env=PKG_CONFIG_PATH=${PKG_CONFIG_PATH}`,
`--filesystem=${path}/_build`,
`${path}/.flatpak/repo`,
...argv,
]);
}
async function runCommand(argv) {
await exec(
[
"flatpak",
"build",
"--with-appdir",
"--allow=devel",
`--bind-mount=/run/user/1000/doc=/run/user/1000/doc/by-app/${flatpak_id}`,
...manifest["finish-args"],
// Non default permissions, see Permissions.js
// consider getting installed overrides instead with
// flatpak override --user --show re.sonny.Workbench.Devel
"--share=network",
"--socket=pulseaudio",
"--device=input",
"--talk-name=org.freedesktop.portal.*",
"--talk-name=org.a11y.Bus",
"--bind-mount=/run/flatpak/at-spi-bus=/run/user/1000/at-spi/bus",
"--env=AT_SPI_BUS_ADDRESS=unix:path=/run/flatpak/at-spi-bus",
...getHostEnv(),
"--bind-mount=/run/host/fonts=/usr/share/fonts",
"--bind-mount=/run/host/fonts-cache=/usr/lib/fontconfig/cache",
`--filesystem=${home}/.local/share/fonts:ro`,
`--filesystem=${home}/.cache/fontconfig:ro`,
`--bind-mount=/run/host/user-fonts-cache=${home}/.cache/fontconfig`,
`--bind-mount=/run/host/font-dirs.xml=${home}/.cache/font-dirs.xml`,
`${path}/.flatpak/repo`,
...argv,
],
{ verbose: true },
);
}
function getHostEnv() {
// https://github.com/bilelmoussaoui/flatpak-vscode/blob/6424e7d8f53924faa33c9043153e08b0aedf6225/src/utils.ts#L88
const forwarded_env_keys = [
"COLORTERM",
"DESKTOP_SESSION",
"LANG",
"WAYLAND_DISPLAY",
"XDG_CURRENT_DESKTOP",
"XDG_SEAT",
"XDG_SESSION_DESKTOP",
"XDG_SESSION_ID",
"XDG_SESSION_TYPE",
"XDG_VTNR",
"AT_SPI_BUS_ADDRESS",
];
const env_vars = [];
forwarded_env_keys.forEach((key) => {
const value = GLib.getenv(key);
if (value === undefined) env_vars.push(key + "=" + value);
});
return env_vars;
}
async function run(_) {
return exec(_, { verbose: argv.verbose });
}
async function exec(argv, { cancellable = null /*, verbose = false*/ }) {
argv = argv.map((arg) => {
return arg.toString();
});
console.debug(`$ ${argv.join(" ")}`);
let cancelId = 0;
// meson uses stdout for logs
// const flags = verbose
// ? Gio.SubprocessFlags.NONE
// : Gio.SubprocessFlags.STDOUT_SILENCE;
const flags = Gio.SubprocessFlags.NONE;
const proc = new Gio.Subprocess({
argv,
flags,
});
proc.init(cancellable);
if (cancellable instanceof Gio.Cancellable)
cancelId = cancellable.connect(() => proc.force_exit());
try {
const success = await proc.wait_check_async(null);
if (!success) {
const status = proc.get_exit_status();
throw new Gio.IOErrorEnum({
code: Gio.IOErrorEnum.FAILED,
message: `Command '${argv}' failed with exit code ${status}`,
});
}
} catch (err) {
console.debug(err);
exit(1);
} finally {
if (cancelId > 0) cancellable.disconnect(cancelId);
}
}

34
build-aux/wip/run.sh Executable file
View File

@ -0,0 +1,34 @@
set -eux
flatpak_id=re.sonny.Workbench.Devel
path=/home/sonny/Projects/Workbench
manifest=$path/build-aux/$flatpak_id.json
module_name=Workbench
command=workbench
function run_build_command {
flatpak build --share=network --filesystem=$path --filesystem=$path/.flatpak/repo --env=PATH=/app/bin:/app/bin:/app/bin:/usr/bin:/home/sonny/.var/app/com.visualstudio.code/data/node_modules/bin:/app/bin:/usr/bin:/usr/lib/sdk/vala/bin:/usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/node20/bin:/usr/lib/sdk/typescript/bin --env=LD_LIBRARY_PATH=/app/lib:/app/lib:/usr/lib/sdk/vala/lib --env=PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig:/usr/local/share/pkgconfig:/usr/lib64/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig:/app/lib/pkgconfig:/app/share/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig --filesystem=$path/_build $path/.flatpak/repo $1
}
if [ ! -d "$path/.flatpak/repo" ]; then
# initializes repo
flatpak build-init $path/.flatpak/repo $flatpak_id org.gnome.Sdk org.gnome.Sdk 47
fi
if [ ! -d "$path/.flatpak/flatpak-builder" ]; then
# downloads sources (de-initializes)
flatpak-builder --ccache --force-clean --disable-updates --download-only --state-dir=$path/.flatpak/flatpak-builder --stop-at=$module_name $path/.flatpak/repo $manifest
# builds modules (de-initializes)
flatpak-builder --ccache --force-clean --disable-updates --disable-download --build-only --keep-build-dirs --state-dir=$path/.flatpak/flatpak-builder --stop-at=$module_name $path/.flatpak/repo $manifest
fi
# builds Workbench module
if [ ! -d "$path/_build" ]; then
run_build_command "meson --prefix /app _build -Dprofile=development"
else
time run_build_command "meson install -C _build"
# time run_build_command "$path/troll/gjspack/bin/gjspack --appid=$flatpak_id --prefix=/re.sonny.Workbench --project-root=$path --resource-root=$path/src --blueprint-compiler=/app/bin/blueprint-compiler --no-executable $path/src/main.js /app/share/$flatpak_id.src.gresource"
fi
# starts workbench
flatpak build --with-appdir --allow=devel --bind-mount=/run/user/1000/doc=/run/user/1000/doc/by-app/$flatpak_id --share=ipc --socket=fallback-x11 --socket=wayland --device=dri --share=network --socket=pulseaudio --talk-name='org.freedesktop.portal.*' --talk-name=org.a11y.Bus --bind-mount=/run/flatpak/at-spi-bus=/run/user/1000/at-spi/bus --env=AT_SPI_BUS_ADDRESS=unix:path=/run/flatpak/at-spi-bus --env=AT_SPI_BUS_ADDRESS=unix:path=/run/flatpak/at-spi-bus --env=COLORTERM=truecolor --env=DESKTOP_SESSION=gnome --env=LANG=en_US.UTF-8 --env=WAYLAND_DISPLAY=wayland-0 --env=XDG_CURRENT_DESKTOP=GNOME --env=XDG_SESSION_DESKTOP=gnome --env=XDG_SESSION_TYPE=wayland --bind-mount=/run/host/fonts=/usr/share/fonts --bind-mount=/run/host/fonts-cache=/usr/lib/fontconfig/cache --filesystem=/home/sonny/.local/share/fonts:ro --filesystem=/home/sonny/.cache/fontconfig:ro --bind-mount=/run/host/user-fonts-cache=/home/sonny/.cache/fontconfig --bind-mount=/run/host/font-dirs.xml=/home/sonny/.cache/font-dirs.xml $path/.flatpak/repo $command

View File

@ -8,6 +8,6 @@ Type=Application
Categories=WebDevelopment;Development;IDE;GNOME;GTK; Categories=WebDevelopment;Development;IDE;GNOME;GTK;
Icon=@app_id@ Icon=@app_id@
# TRANSLATORS: Don't translate # TRANSLATORS: Don't translate
Keywords=CSS;JavaScript;GJS;Blueprint;builder;Vala;GTK;libadwaita;Python;PyGObject;Rust;doc;playground;code; Keywords=CSS;JavaScript;GJS;Blueprint;builder;Vala;GTK;libadwaita;Python;PyGObject;Rust;doc;playground;code;TypeScript;
DBusActivatable=true DBusActivatable=true
StartupNotify=true StartupNotify=true

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<schemalist gettext-domain="@app_id@"> <schemalist>
<enum id="re.sonny.Workbench.UserInterfaceLanguage"> <enum id="re.sonny.Workbench.UserInterfaceLanguage">
<value nick="blueprint" value="0"/> <value nick="blueprint" value="0"/>
<value nick="xml" value="1"/> <value nick="xml" value="1"/>
@ -18,6 +18,9 @@
<key name="first-run" type="b"> <key name="first-run" type="b">
<default>true</default> <default>true</default>
</key> </key>
<key name="typescript" type="b">
<default>true</default>
</key>
</schema> </schema>
<schema id="@app_id@.Session" path="/re/sonny/Workbench/"> <schema id="@app_id@.Session" path="/re/sonny/Workbench/">
<key name="show-code" type="b"> <key name="show-code" type="b">
@ -56,5 +59,17 @@
<key name="edited" type="b"> <key name="edited" type="b">
<default>false</default> <default>false</default>
</key> </key>
<key name="window-width" type="i">
<default>0</default>
</key>
<key name="window-height" type="i">
<default>0</default>
</key>
<key name="window-maximized" type="b">
<default>false</default>
</key>
<key name="window-fullscreened" type="b">
<default>false</default>
</key>
</schema> </schema>
</schemalist> </schemalist>

View File

@ -3,7 +3,7 @@
<id>@app_id@</id> <id>@app_id@</id>
<launchable type="desktop-id">@app_id@.desktop</launchable> <launchable type="desktop-id">@app_id@.desktop</launchable>
<name translatable="no">Workbench</name> <name translatable="no">Workbench</name>
<developer id="sonny.re"> <developer id="re.sonny">
<name translatable="no">Sonny Piers</name> <name translatable="no">Sonny Piers</name>
</developer> </developer>
<update_contact>sonnyp@gnome.org</update_contact> <update_contact>sonnyp@gnome.org</update_contact>
@ -19,35 +19,157 @@
<li>JavaScript, Rust, Python and Vala support</li> <li>JavaScript, Rust, Python and Vala support</li>
<li>Declarative user interface syntax</li> <li>Declarative user interface syntax</li>
<li>Autosave, sessions and projects</li> <li>Autosave, sessions and projects</li>
<li>Code linter and formatter</li> <li>Code diagnostics, completion and formatter</li>
<li>Terminal output</li> <li>Terminal output</li>
<li>1000+ icons</li>
</ul> </ul>
</description> </description>
<url type="homepage">https://apps.gnome.org/Workbench</url> <url type="homepage">https://workbench.sonny.re</url>
<url type="bugtracker">https://github.com/workbenchdev/Workbench/issues</url> <url type="bugtracker">https://workbench.sonny.re/feedback</url>
<url type="donation">https://ko-fi.com/sonnyp</url> <url type="donation">https://workbench.sonny.re/donate</url>
<url type="vcs-browser">https://github.com/workbenchdev/Workbench</url> <url type="vcs-browser">https://workbench.sonny.re/source</url>
<screenshots> <screenshots>
<screenshot type="default"> <screenshot type="default">
<image>https://raw.githubusercontent.com/workbenchdev/Workbench/main/data/screenshot.png</image> <image>https://raw.githubusercontent.com/workbenchdev/Workbench/main/data/screenshots/welcome.png</image>
<caption>Workbench welcome screen</caption> <caption>Welcome screen</caption>
</screenshot> </screenshot>
<screenshot> <screenshot>
<image>https://raw.githubusercontent.com/workbenchdev/Workbench/main/data/screenshot-code.png</image> <image>https://raw.githubusercontent.com/workbenchdev/Workbench/main/data/screenshots/code.png</image>
<caption>Workbench CSS Gradients demo</caption> <caption>Drawing Area demo</caption>
</screenshot> </screenshot>
<screenshot> <screenshot>
<image>https://raw.githubusercontent.com/workbenchdev/Workbench/main/data/screenshot-library.png</image> <image>https://raw.githubusercontent.com/workbenchdev/Workbench/main/data/screenshots/library.png</image>
<caption>Workbench Library window</caption> <caption>Library window</caption>
</screenshot>
<screenshot>
<image>https://raw.githubusercontent.com/workbenchdev/Workbench/main/data/screenshot-extensions.png</image>
<caption>Workbench Extensions Window</caption>
</screenshot> </screenshot>
</screenshots> </screenshots>
<content_rating type="oars-1.1" /> <content_rating type="oars-1.1" />
<releases> <releases>
<release version="48.0" date="2025-04-xx">
<description translatable="no">
<ul>
<li>Use GNOME 48</li>
</ul>
<p>Library:</p>
<ul>
<li>Add "Shortcuts Window" demo</li>
</ul>
<p>Dependencies:</p>
<ul>
<!-- <li>Update Jsonrpc-GLib to 3.44.1</li> -->
<li>Update libportal to 0.9.1</li>
<li>Update libshumate to 1.4</li>
<li>Update libspelling to 0.4.6</li>
<li>Update vte to 0.80.0</li>
</ul>
</description>
</release>
<release version="47.1" date="2025-01-24">
<description translatable="no">
<ul>
<li>Fix permissions dialog</li>
<li>Update Blueprint to 0.16.0</li>
<li>Update libportal to 0.9.0</li>
</ul>
<p>Library:</p>
<ul>
<li>Replace GTK syntax with CSS variables</li>
<li>Fix year sorting in "Column View" Python</li>
</ul>
</description>
</release>
<release version="47.0" date="2024-12-11">
<description translatable="no">
<ul>
<li>Use GNOME 47</li>
<li>Add code suggestions/completions support</li>
<li>Add Language and Category filters to the Library</li>
<li>Add action to create new blank project</li>
<li>Add experimental TypeScript support</li>
</ul>
<p>Library:</p>
<ul>
<li>Add "Button Row" demo</li>
<li>Add "Gamepad" demo</li>
<li>Add "List View with Sections" demo</li>
<li>Add "List View with a Tree" demo</li>
<li>Add "Bottom Sheet" demo</li>
<li>Add "Database" demo</li>
<li>Update "Spinner" to use AdwSpinner</li>
<li>Port "Dialog" to Python</li>
<li>Port "List View" to Python</li>
<li>Port "Grid View" to Python</li>
<li>Port "Level Bars" to Rust</li>
<li>Port "Text Fields" to Rust</li>
<li>Port "Column View" to Rust</li>
<li>Port "Clamp" to Rust</li>
<li>Port "List View" to Rust</li>
<li>Port "Frame" to Rust</li>
<li>Port "Audio" to Rust</li>
<li>Port "File Monitor" to Vala</li>
<li>Port "Scrolled Window" to Vala</li>
<li>Port "Menu" to Vala</li>
<li>Port "HTTP Server" to Vala</li>
<li>Port "Map" to Vala</li>
<li>Port "Session Monitor and Inhibit" to Vala</li>
<li>Port "Save File" to Vala</li>
<li>Port "Spin Button" to Vala</li>
<li>Port "Progress Bar" to Vala</li>
<li>Port "Label" to Vala</li>
<li>Port "Power Profile Monitor" to Vala</li>
<li>Port "Drawing Area" to Vala</li>
<li>Port "Font Dialog" to Vala</li>
<li>Port "Select Folder" to Vala</li>
<li>Port "Network Monitor" to Vala</li>
<li>Port "Toggle Button" to Vala</li>
<li>Port "Tooltip" to Vala</li>
<li>Port "Spell Checker" to Vala</li>
<li>Port "Popovers" to Vala</li>
<li>Port "Menu Button" to Vala</li>
<li>Port "Frame" to Vala</li>
<li>Port "Actions" to Vala</li>
<li>Port "Dialog" to Vala</li>
<li>Port "CSS Gradients" to Vala</li>
<li>Port "Snapshot" to Vala</li>
</ul>
<p>Dependencies:</p>
<ul>
<!-- <li>Replace Biome with TypeScript Language Server</li> -->
<li>Add GOM dependency 0.5.3</li>
<li>Update Biome to 1.9.4</li>
<li>Update libshumate to 1.3</li>
<li>Update Blueprint to 0.14.0</li>
<li>Update vte to 0.78.2</li>
<li>Update libspelling to 0.4.5</li>
<li>Update libportal to 0.8.1</li>
</ul>
</description>
</release>
<release version="46.1" date="2024-04-30">
<description translatable="no">
<ul>
<li>Add "Find" to search for code in the current editor</li>
<li>Save and restore the session window dimensions</li>
<li>Add "Reveal in Folder" to browse project files</li>
<li>Remove Icon Library, please refer to the "Using Icons" entry</li>
<li>List editor shortcuts in Shortcuts</li>
<li>Fix Vala and Rust extensions detection on "Run"</li>
<li>Library: Add "Using Icons"</li>
<li>Library: Add "SVG"</li>
<li>Library: Split "List View Widget" into "List View" and "Grid View"</li>
<li>Library: Port "Animation" to Python</li>
<li>Library: Port "Radio Buttons" to Vala</li>
<li>Library: Port "Switch" to Vala</li>
<li>Library: Port "Revealer" to Vala</li>
<li>Library: Port "Styling with CSS" to Vala</li>
<li>Library: Port "Separator" to Vala</li>
<li>Library: Port "Level Bars" to Vala</li>
<li>Library: Port "Link Button" to Vala</li>
</ul>
</description>
</release>
<release version="46" date="2024-03-20"> <release version="46" date="2024-03-20">
<description translatable="no"> <description translatable="no">
<ul> <ul>
@ -66,7 +188,7 @@
<li>Update gst-plugin-gtk4 to 0.12.1</li> <li>Update gst-plugin-gtk4 to 0.12.1</li>
<li>Update libshumate to 1.2</li> <li>Update libshumate to 1.2</li>
<li>Update VTE to 0.76</li> <li>Update VTE to 0.76</li>
<li>Update Blueprint</li> <li>Update Blueprint to 0.12.0</li>
<li>Update GTKCssLanguageServer</li> <li>Update GTKCssLanguageServer</li>
<li>Update icon-development-kit</li> <li>Update icon-development-kit</li>
<li>Library: Adapt demos to use GNOME 46 features</li> <li>Library: Adapt demos to use GNOME 46 features</li>
@ -113,7 +235,6 @@
<li>Open the Library on start if there are no sessions to restore</li> <li>Open the Library on start if there are no sessions to restore</li>
<li>Restore scroll and cusor positions on format and Run</li> <li>Restore scroll and cusor positions on format and Run</li>
<li>Add "Copy" and "Select All" to Console</li> <li>Add "Copy" and "Select All" to Console</li>
<!-- <li>Add find text support</li> -->
<li>Add Vala formatter support</li> <li>Add Vala formatter support</li>
<li>Add WebP image format support</li> <li>Add WebP image format support</li>
<li>Use Biome instead of prettier as JavaScript formatter</li> <li>Use Biome instead of prettier as JavaScript formatter</li>
@ -571,11 +692,9 @@
<kudo>ModernToolkit</kudo> <kudo>ModernToolkit</kudo>
</kudos> </kudos>
<branding> <branding>
<color type="primary">#33d17a</color> <color type="primary" scheme_preference="light">#57e389</color>
<color type="primary" scheme_preference="dark">#26a269</color>
</branding> </branding>
<custom>
<value key="GnomeSoftware::key-colors">[(51, 209, 122)]</value>
</custom>
<requires> <requires>
<display_length compare="ge">1024</display_length> <display_length compare="ge">1024</display_length>
</requires> </requires>

View File

@ -2,7 +2,7 @@ desktop_file = configure_file(
input: 'app.desktop', input: 'app.desktop',
output: '@0@.desktop'.format(app_id), output: '@0@.desktop'.format(app_id),
configuration: { 'app_id': app_id }, configuration: { 'app_id': app_id },
install_dir: join_paths(get_option('datadir'), 'applications') install_dir: get_option('datadir') / 'applications'
) )
desktop_utils = find_program('desktop-file-validate', required: true) desktop_utils = find_program('desktop-file-validate', required: true)
@ -14,26 +14,28 @@ configure_file(
input: 'app.service', input: 'app.service',
output: '@0@.service'.format(app_id), output: '@0@.service'.format(app_id),
configuration: { 'app_id': app_id, 'bindir': bindir }, configuration: { 'app_id': app_id, 'bindir': bindir },
install_dir: join_paths(get_option('datadir'), 'dbus-1/services') install_dir: get_option('datadir') / 'dbus-1/services'
) )
appstream_file = configure_file( appstream_file = configure_file(
input: 'app.metainfo.xml', input: 'app.metainfo.xml',
output: '@0@.metainfo.xml'.format(app_id), output: '@0@.metainfo.xml'.format(app_id),
configuration: { 'app_id': app_id }, configuration: { 'app_id': app_id },
install_dir: join_paths(get_option('datadir'), 'metainfo') install_dir: get_option('datadir') / 'metainfo'
) )
appstream_cli = find_program('appstreamcli', required: true) appstreamcli = find_program('appstreamcli', required: false)
test('Validate metainfo file', appstream_cli, test(
args: ['validate', '--override=release-time-missing=info', '--no-net', '--explain', appstream_file] 'Validate appstream file',
appstreamcli,
args: ['validate', '--no-net', '--explain', appstream_file],
) )
configure_file( configure_file(
input: 'app.gschema.xml', input: 'app.gschema.xml',
output: '@0@.gschema.xml'.format(app_id), output: '@0@.gschema.xml'.format(app_id),
configuration: { 'app_id': app_id }, configuration: { 'app_id': app_id },
install_dir: join_paths(get_option('datadir'), 'glib-2.0/schemas') install_dir: get_option('datadir') / 'glib-2.0/schemas'
) )
compile_schemas = find_program('glib-compile-schemas', required: true) compile_schemas = find_program('glib-compile-schemas', required: true)
@ -41,4 +43,4 @@ test('Validate schema file', compile_schemas,
args: ['--strict', '--dry-run', meson.current_source_dir()] args: ['--strict', '--dry-run', meson.current_source_dir()]
) )
install_subdir('icons/hicolor', install_dir : join_paths(get_option('datadir'), 'icons')) install_subdir('icons/hicolor', install_dir : get_option('datadir') / 'icons')

Binary file not shown.

Before

Width:  |  Height:  |  Size: 560 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 503 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

BIN
data/screenshots/code.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

2
demos

@ -1 +1 @@
Subproject commit 20cfcef70038c2aba1df485c3dd7d14d58537139 Subproject commit cd8b209df99294c72a5846ead00797db0bafa862

1
gi-types Submodule

@ -0,0 +1 @@
Subproject commit 396fe147142e28a921f0745eff1a562c7a551843

View File

@ -1,7 +1,14 @@
project('Workbench', ['vala', 'c', 'rust'], project(
version: '46', 'Workbench',
meson_version: '>= 0.59.0', ['vala', 'c', 'rust'],
license: 'GPL-3.0-only' version: '48.0',
meson_version: '>= 1.0.0',
license: 'GPL-3.0-only',
default_options: [
'libdir=lib',
'warning_level=2',
'werror=false',
],
) )
gnome = import('gnome') gnome = import('gnome')
@ -20,9 +27,9 @@ else
endif endif
prefix = get_option('prefix') prefix = get_option('prefix')
bindir = join_paths(prefix, 'bin') bindir = prefix / 'bin'
datadir = join_paths(prefix, get_option('datadir')) datadir = prefix / get_option('datadir')
pkgdatadir = join_paths(datadir, app_id) pkgdatadir = datadir / app_id
subdir('data') subdir('data')
subdir('src') subdir('src')
@ -30,5 +37,5 @@ subdir('src')
gnome.post_install( gnome.post_install(
glib_compile_schemas: true, glib_compile_schemas: true,
gtk_update_icon_cache: true, gtk_update_icon_cache: true,
update_desktop_database: true update_desktop_database: true,
) )

1026
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,9 @@
{ {
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "^7.22.11", "@babel/core": "^7.25.7",
"@babel/plugin-syntax-import-assertions": "^7.22.5", "@babel/eslint-parser": "^7.25.7",
"@babel/plugin-syntax-import-attributes": "^7.25.7",
"@rollup/plugin-commonjs": "^22.0.1", "@rollup/plugin-commonjs": "^22.0.1",
"@rollup/plugin-node-resolve": "^13.3.0", "@rollup/plugin-node-resolve": "^13.3.0",
"eslint": "^8.48.0", "eslint": "^8.48.0",
@ -12,7 +13,7 @@
"events": "^3.3.0", "events": "^3.3.0",
"husky": "^8.0.3", "husky": "^8.0.3",
"lint-staged": "^14.0.1", "lint-staged": "^14.0.1",
"ltx": "git://github.com/xmppjs/ltx.git#d351ffa26ef1c5f2fbee7888d0bcd5047eb8a988", "ltx": "git://github.com/xmppjs/ltx.git#072690a43a51254ddd17b082131a8b9115586e8a",
"postcss": "^8.4.14", "postcss": "^8.4.14",
"prettier": "3.0.3", "prettier": "3.0.3",
"rollup": "^2.76.0", "rollup": "^2.76.0",

View File

@ -17,11 +17,11 @@ template $Extension: ListBoxRow {
Label label_title {} Label label_title {}
[end] [end]
Image image_enabled { Image image_available {
icon-name: "test-pass-symbolic"; icon-name: "re.sonny.Workbench-test-pass-symbolic";
styles [ styles [
"success" "success",
] ]
} }
} }
@ -33,19 +33,13 @@ template $Extension: ListBoxRow {
margin-bottom: 14; margin-bottom: 14;
orientation: vertical; orientation: vertical;
Button button { Label {
label: _("Install");
visible: false;
halign: start;
margin-bottom: 12;
}
Label label_hint {
wrap: true; wrap: true;
xalign: 0; xalign: 0;
label: _("Run the following command");
styles [ styles [
"dim-label" "dim-label",
] ]
} }
@ -58,7 +52,7 @@ template $Extension: ListBoxRow {
xalign: 0; xalign: 0;
styles [ styles [
"command_snippet" "command_snippet",
] ]
} }
} }

View File

@ -9,11 +9,9 @@ export default GObject.registerClass(
Template, Template,
InternalChildren: [ InternalChildren: [
"label_title", "label_title",
"image_enabled", "image_available",
"installation_guide", "installation_guide",
"label_hint",
"label_command", "label_command",
"button",
], ],
Properties: { Properties: {
title: GObject.ParamSpec.string( title: GObject.ParamSpec.string(
@ -23,27 +21,13 @@ export default GObject.registerClass(
GObject.ParamFlags.READWRITE, GObject.ParamFlags.READWRITE,
"", "",
), ),
enabled: GObject.ParamSpec.boolean( available: GObject.ParamSpec.boolean(
"enabled", "available",
"", "",
"", "",
GObject.ParamFlags.READWRITE, GObject.ParamFlags.READWRITE,
false, false,
), ),
uri: GObject.ParamSpec.string(
"uri",
"",
"",
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT,
"",
),
hint: GObject.ParamSpec.string(
"hint",
"",
"",
GObject.ParamFlags.READWRITE,
"",
),
command: GObject.ParamSpec.string( command: GObject.ParamSpec.string(
"command", "command",
"", "",
@ -57,15 +41,6 @@ export default GObject.registerClass(
constructor(properties = {}) { constructor(properties = {}) {
super(properties); super(properties);
if (properties.uri) {
this._button.visible = true;
this._button.connect("clicked", () => {
new Gtk.UriLauncher({ uri: properties.uri })
.launch(this.get_root(), null)
.catch(console.error);
});
}
this.bind_property( this.bind_property(
"title", "title",
this._label_title, this._label_title,
@ -74,26 +49,19 @@ export default GObject.registerClass(
); );
this.bind_property( this.bind_property(
"enabled", "available",
this._image_enabled, this._image_available,
"visible", "visible",
GObject.BindingFlags.SYNC_CREATE, GObject.BindingFlags.SYNC_CREATE,
); );
this.bind_property( this.bind_property(
"enabled", "available",
this._installation_guide, this._installation_guide,
"visible", "visible",
GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN, GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN,
); );
this.bind_property(
"hint",
this._label_hint,
"label",
GObject.BindingFlags.SYNC_CREATE,
);
this.bind_property( this.bind_property(
"command", "command",
this._label_command, this._label_command,

View File

@ -38,30 +38,29 @@ Adw.Dialog dialog {
selection-mode: none; selection-mode: none;
styles [ styles [
"boxed-list" "boxed-list",
] ]
$Extension { $Extension {
title: _("JavaScript"); title: _("JavaScript");
enabled: true; available: true;
} }
$Extension { $Extension {
title: _("Python"); title: _("Python");
enabled: true; available: true;
} }
$Extension extension_rust { $Extension extension_rust {
title: _("Rust"); title: _("Rust");
hint: _("Run the following command");
command: "flatpak install flathub org.freedesktop.Sdk.Extension.rust-stable//23.08 org.freedesktop.Sdk.Extension.llvm16//23.08";
} }
$Extension extension_vala { $Extension extension_vala {
title: _("Vala"); title: _("Vala");
hint: _("or run the following command"); }
command: "flatpak install flathub org.freedesktop.Sdk.Extension.vala//23.08";
uri: "appstream://org.freedesktop.Sdk.Extension.vala"; $Extension extension_typescript {
title: _("TypeScript");
} }
} }
@ -73,7 +72,7 @@ Adw.Dialog dialog {
wrap: true; wrap: true;
styles [ styles [
"dim-label" "dim-label",
] ]
} }
@ -84,7 +83,7 @@ Adw.Dialog dialog {
wrap: true; wrap: true;
styles [ styles [
"dim-label" "dim-label",
] ]
} }
} }

View File

@ -6,6 +6,8 @@ import Interface from "./Extensions.blp" with { type: "uri" };
import illustration from "./extensions.svg"; import illustration from "./extensions.svg";
import "./Extension.js"; import "./Extension.js";
import { settings } from "../util.js";
import { getFlatpakInfo } from "../flatpak.js";
export const action_extensions = new Gio.SimpleAction({ export const action_extensions = new Gio.SimpleAction({
name: "extensions", name: "extensions",
@ -18,17 +20,29 @@ export function Extensions({ window }) {
picture_illustration, picture_illustration,
extension_rust, extension_rust,
extension_vala, extension_vala,
extension_typescript,
restart_hint, restart_hint,
all_set_hint, all_set_hint,
} = build(Interface); } = build(Interface);
picture_illustration.set_resource(illustration); picture_illustration.set_resource(illustration);
extension_rust.enabled = isRustEnabled(); extension_rust.available = isRustAvailable();
extension_vala.enabled = isValaEnabled(); extension_rust.command = `flatpak install flathub org.freedesktop.Sdk.Extension.rust-stable//${freedesktop_version} org.freedesktop.Sdk.Extension.${llvm}//${freedesktop_version}`;
for (const extension of [extension_rust, extension_vala]) { extension_vala.available = isValaAvailable();
if (!extension.enabled) { extension_vala.command = `flatpak install flathub org.freedesktop.Sdk.Extension.vala//${freedesktop_version}`;
extension_typescript.available = isTypeScriptAvailable();
extension_typescript.command = `flatpak install flathub org.freedesktop.Sdk.Extension.${node}//${freedesktop_version} org.freedesktop.Sdk.Extension.typescript//${freedesktop_version}`;
extension_typescript.visible = isTypeScriptEnabled();
for (const extension of [
extension_rust,
extension_vala,
extension_typescript,
]) {
if (!extension.available) {
all_set_hint.set_visible(false); all_set_hint.set_visible(false);
restart_hint.set_visible(true); restart_hint.set_visible(true);
} }
@ -41,17 +55,35 @@ export function Extensions({ window }) {
window.add_action(action_extensions); window.add_action(action_extensions);
} }
let rust_enabled; let rust_available = null;
export function isRustEnabled() { export function isRustAvailable() {
rust_enabled ??= rust_available ??=
Gio.File.new_for_path("/usr/lib/sdk/rust-stable").query_exists(null) && Gio.File.new_for_path("/usr/lib/sdk/rust-stable").query_exists(null) &&
Gio.File.new_for_path("/usr/lib/sdk/llvm16").query_exists(null); Gio.File.new_for_path(`/usr/lib/sdk/${llvm}`).query_exists(null);
return rust_enabled; return rust_available;
} }
let vala_enabled; let vala_available = null;
export function isValaEnabled() { export function isValaAvailable() {
vala_enabled ??= vala_available ??=
Gio.File.new_for_path("/usr/lib/sdk/vala").query_exists(null); Gio.File.new_for_path("/usr/lib/sdk/vala").query_exists(null);
return vala_enabled; return vala_available;
}
let typescript_available = null;
export function isTypeScriptAvailable() {
typescript_available ??=
isTypeScriptEnabled() &&
Gio.File.new_for_path("/usr/lib/sdk/typescript").query_exists(null) &&
Gio.File.new_for_path(`/usr/lib/sdk/${node}`).query_exists(null);
return typescript_available;
}
const llvm = "llvm18";
const node = "node20";
const runtime = getFlatpakInfo().get_string("Application", "runtime");
const freedesktop_version = runtime.endsWith("master") ? "24.08" : "24.08";
export function isTypeScriptEnabled() {
return settings.get_boolean("typescript");
} }

View File

@ -1,24 +0,0 @@
using Gtk 4.0;
template $IconWidget: FlowBoxChild {
focusable: false;
halign: start;
Button button {
clicked => $onClicked();
width-request: 48;
height-request: 48;
Image image {
icon-name: "applications-science-symbolic";
// preview only
pixel-size: 32;
halign: center;
valign: center;
}
styles [
"flat"
]
}
}

View File

@ -1,47 +0,0 @@
import Gtk from "gi://Gtk";
import GObject from "gi://GObject";
import Template from "./IconWidget.blp" with { type: "uri" };
import { registerClass } from "../overrides.js";
class IconWidget extends Gtk.FlowBoxChild {
constructor(params = {}) {
super(params);
this.bind_property(
"icon_name",
this._image,
"icon-name",
GObject.BindingFlags.SYNC_CREATE,
);
this.bind_property(
"icon_name",
this,
"tooltip_text",
GObject.BindingFlags.SYNC_CREATE,
);
}
onClicked() {
this.emit("clicked");
}
}
export default registerClass(
{
GTypeName: "IconWidget",
Template,
InternalChildren: ["image"],
Properties: {
icon_name: GObject.ParamSpec.string(
"icon_name", // Name
"icon_name", // Nick
"", // Blurb
GObject.ParamFlags.READWRITE, // Flags
null, // Default value
),
},
Signals: {
clicked: {},
},
},
IconWidget,
);

View File

@ -1,81 +0,0 @@
using Gtk 4.0;
using Adw 1;
Adw.Window window {
width-request: 800;
height-request: 600;
hide-on-close: true;
title: _("Workbench — Icon Library");
Adw.ToastOverlay overlay {
Adw.ToolbarView {
[top]
Adw.HeaderBar {
[title]
Adw.Clamp {
maximum-size: 800;
SearchEntry search_entry {
hexpand: true;
placeholder-text: _("Search for icons by name, category or tag");
}
}
}
ScrolledWindow {
hexpand: true;
vexpand: true;
Adw.Clamp {
maximum-size: 800;
Box {
orientation: vertical;
Label {
styles [
"title-4"
]
label: "Icon Development Kit";
margin-bottom: 6;
margin-top: 12;
halign: start;
}
FlowBox flow_box_devkit {
visible: false;
valign: start;
min-children-per-line: 5;
max-children-per-line: 18;
activate-on-single-click: true;
selection-mode: none;
margin-bottom: 12;
}
Label {
styles [
"title-4"
]
label: "Platform icons";
margin-bottom: 6;
margin-top: 12;
halign: start;
}
FlowBox flow_box_platform {
visible: false;
valign: start;
min-children-per-line: 5;
max-children-per-line: 18;
activate-on-single-click: true;
selection-mode: none;
margin-bottom: 12;
}
}
}
}
}
}
}

View File

@ -1,131 +0,0 @@
import Gtk from "gi://Gtk";
import Gdk from "gi://Gdk";
import Adw from "gi://Adw";
import Gio from "gi://Gio";
import { build } from "../../troll/src/builder.js";
import IconWidget from "./IconWidget.js";
import resource from "./main.blp" with { type: "uri" };
const toasts = new Set();
export default function IconLibrary() {
const display = Gdk.Display.get_default();
const clipboard = display.get_clipboard();
const dev_kit_icons = getDevKitIcons();
const platform_icons = getPlatformIcons(dev_kit_icons);
const icons = Object.assign(
Object.create(null),
platform_icons,
dev_kit_icons,
);
const { window, overlay, search_entry, flow_box_devkit, flow_box_platform } =
build(resource);
function selectIcon(icon_name) {
clipboard.set(icon_name);
for (const toast of toasts) {
toast.dismiss();
}
const toast = new Adw.Toast({
title: `${icon_name}” copied to clipboard`,
priority: Adw.ToastPriority.HIGH,
});
toast.connect("dismissed", onToastDismissed);
toasts.add(toast);
overlay.add_toast(toast);
}
function filter_func({ icon_name }) {
return icons[icon_name]?.some((tag) => tag.includes(search_entry.text));
}
flow_box_devkit.set_filter_func(filter_func);
flow_box_platform.set_filter_func(filter_func);
search_entry.connect("search-changed", () => {
flow_box_devkit.invalidate_filter();
flow_box_platform.invalidate_filter();
});
function populateIconDevKit() {
for (const icon_name of Object.keys(dev_kit_icons).sort((a, b) =>
a.localeCompare(b),
)) {
const icon = new IconWidget({
icon_name,
});
icon.connect("clicked", () => selectIcon(icon_name));
flow_box_devkit.append(icon);
}
flow_box_devkit.visible = true;
}
function populatePlatformIcons() {
for (const icon_name of Object.keys(platform_icons).sort((a, b) =>
a.localeCompare(b),
)) {
const icon = new IconWidget({
icon_name,
});
icon.connect("clicked", () => selectIcon(icon_name));
flow_box_platform.append(icon);
}
flow_box_platform.visible = true;
}
populateIconDevKit();
populatePlatformIcons();
return window;
}
function onToastDismissed(toast) {
toasts.delete(toast);
}
function getDevKitIcons() {
const icons = Object.create(null);
const [bytes] = Gio.File.new_for_path(
"/app/share/icon-development-kit/icons.json",
).load_bytes(null);
const icons_dev_kit = JSON.parse(new TextDecoder().decode(bytes.get_data()));
for (const icon of icons_dev_kit) {
// https://gitlab.gnome.org/Teams/Design/icon-development-kit/-/issues/62
if (icon.context === "noexport-bits") continue;
icons[`${icon.filename}-symbolic`] = [
icon.filename,
icon.context,
...icon.tags,
];
}
return icons;
}
function getPlatformIcons(dev_kit_icons) {
const display = Gdk.Display.get_default();
const icons = Object.create(null);
const icon_theme = Gtk.IconTheme.get_for_display(display);
const icons_theme = icon_theme.get_icon_names();
for (const icon of icons_theme) {
if (
icon.startsWith("re.sonny.Workbench") ||
!icon.endsWith("-symbolic") ||
icon in dev_kit_icons
)
continue;
if (!(icon in icons)) icons[icon] = [icon.split("-symbolic")[0]];
}
return icons;
}

View File

@ -1,20 +1,20 @@
using Gtk 4.0; using Gtk 4.0;
using Adw 1; using Adw 1;
template $EntryRow: Adw.PreferencesRow { template $EntryRow: Adw.ActionRow {
activatable: true;
accessibility { accessibility {
labelled-by: title_label; labelled-by: title_label;
described-by: description_label; described-by: description_label;
} }
title: bind title_label.label; [prefix]
Box contents { Box contents {
orientation: horizontal; orientation: horizontal;
Box labels_box { Box labels_box {
margin-top: 6; margin-top: 6;
margin-start: 12;
margin-bottom: 6; margin-bottom: 6;
spacing: 3; spacing: 3;
orientation: vertical; orientation: vertical;
@ -28,7 +28,7 @@ template $EntryRow: Adw.PreferencesRow {
Label description_label { Label description_label {
styles [ styles [
"dim-label", "dim-label",
"caption" "caption",
] ]
xalign: 0; xalign: 0;
@ -43,13 +43,13 @@ template $EntryRow: Adw.PreferencesRow {
margin-top: 3; margin-top: 3;
} }
} }
}
Image { [suffix]
icon-name: "go-next-symbolic"; Image {
margin-end: 12; icon-name: "go-next-symbolic";
margin-start: 6; margin-start: 6;
hexpand: true; hexpand: true;
halign: end; halign: end;
}
} }
} }

View File

@ -6,7 +6,7 @@ import GObject from "gi://GObject";
import { getLanguage } from "../util.js"; import { getLanguage } from "../util.js";
import Template from "./EntryRow.blp" with { type: "uri" }; import Template from "./EntryRow.blp" with { type: "uri" };
class EntryRow extends Adw.PreferencesRow { class EntryRow extends Adw.ActionRow {
constructor({ demo, ...params } = {}) { constructor({ demo, ...params } = {}) {
super(params); super(params);
@ -22,7 +22,7 @@ class EntryRow extends Adw.PreferencesRow {
}); });
activate_action.connect("activate", () => { activate_action.connect("activate", () => {
this.emit("activated", null); this.emit("triggered", null);
}); });
action_group.add_action(activate_action); action_group.add_action(activate_action);
@ -47,7 +47,7 @@ class EntryRow extends Adw.PreferencesRow {
}); });
button.connect("clicked", () => { button.connect("clicked", () => {
this.emit("activated", language); this.emit("triggered", language);
}); });
return button; return button;
@ -68,7 +68,7 @@ export default GObject.registerClass(
), ),
}, },
Signals: { Signals: {
activated: { triggered: {
param_types: [GObject.TYPE_JSOBJECT], param_types: [GObject.TYPE_JSOBJECT],
}, },
}, },

View File

@ -1,75 +1,130 @@
using Gtk 4.0; using Gtk 4.0;
using Adw 1; using Adw 1;
Adw.PreferencesWindow window { Adw.Window window {
hide-on-close: true; hide-on-close: true;
modal: false; modal: false;
title: _("Workbench — Library"); title: _("Workbench — Library");
default-height: 700; default-height: 700;
default-width: 700; default-width: 700;
Adw.PreferencesPage { Adw.ToolbarView toolbar_view {
Adw.PreferencesGroup { [top]
Picture picture_illustration { Adw.HeaderBar header_bar {
can-shrink: false; title-widget: Adw.WindowTitle {
margin-bottom: 32; title: _("Workbench — Library");
} };
Label {
label: _("Learn, Test, Remix");
styles [
"title-1"
]
}
} }
Adw.PreferencesGroup library_uncategorized {} content: ScrolledWindow scrolled_window {
hscrollbar-policy: never;
Adw.PreferencesGroup library_tools { child: Adw.Clamp {
title: _("Tools"); maximum-size: 576;
} margin-end: 12;
margin-start: 12;
Adw.PreferencesGroup library_network { child: Box {
title: _("Network"); orientation: vertical;
} spacing: 12;
Adw.PreferencesGroup library_controls { Box {
title: _("Controls"); orientation: vertical;
}
Adw.PreferencesGroup library_layout { Box {
title: _("Layout"); halign: center;
} vexpand: false;
Adw.PreferencesGroup library_feedback { Picture picture_illustration {
title: _("Feedback"); can-shrink: false;
} margin-bottom: 32;
margin-top: 24;
}
}
Adw.PreferencesGroup library_navigation { Label {
title: _("Navigation"); label: _("Learn, Test, Remix");
}
Adw.PreferencesGroup library_user_interface { styles [
title: _("User Interface"); "title-1",
} ]
}
Adw.PreferencesGroup library_platform { Box {
title: _("Platform APIs"); spacing: 6;
}
Adw.PreferencesGroup { SearchEntry search_entry {
vexpand: true; search-delay: 100;
valign: end; placeholder-text: _("Search demos");
activates-default: true;
hexpand: true;
margin-top: 32;
}
Label { DropDown dropdown_language {
label: _('All examples are dedicated to the public domain\nand <b>can be used freely</b> under the terms of <a href="https://creativecommons.org/publicdomain/zero/1.0/">CC0 1.0</a>'); valign: end;
use-markup: true;
styles [ model: Gtk.StringList {};
"caption" }
]
} DropDown dropdown_category {
} valign: end;
model: Gtk.StringList {};
}
}
}
ListBox listbox {
selection-mode: none;
styles [
"boxed-list",
]
}
Box {
halign: center;
margin-bottom: 24;
margin-top: 12;
orientation: vertical;
Box results_empty {
orientation: vertical;
visible: false;
margin-top: 46;
margin-bottom: 70;
spacing: 6;
Label {
label: _("No results");
styles [
"title-3",
]
}
Button button_reset {
label: _("Reset filters");
halign: center;
styles [
"pill",
]
}
}
Label {
label: _("All examples are dedicated to the public domain\nand <b>can be used freely</b> under the terms of <a href=\"https://creativecommons.org/publicdomain/zero/1.0/\">CC0 1.0</a>");
use-markup: true;
styles [
"caption",
]
}
}
};
};
};
} }
} }

View File

@ -14,12 +14,28 @@ import { createSessionFromDemo } from "../sessions.js";
import EntryRow from "./EntryRow.js"; import EntryRow from "./EntryRow.js";
import illustration from "./library.svg"; import illustration from "./library.svg";
import { gettext as _ } from "gettext";
import { build } from "../../troll/src/builder.js"; import { build } from "../../troll/src/builder.js";
import {
needsAdditionalPermissions,
showPermissionsDialog,
} from "../Permissions/Permissions.js";
import { isTypeScriptEnabled } from "../Extensions/Extensions.js";
export default function Library({ application }) { export default function Library({ application }) {
const objects = build(resource); const objects = build(resource);
const { window, picture_illustration } = objects; const {
window,
picture_illustration,
search_entry,
dropdown_category,
dropdown_language,
results_empty,
button_reset,
listbox,
scrolled_window,
} = objects;
window.application = application; window.application = application;
picture_illustration.set_resource(illustration); picture_illustration.set_resource(illustration);
@ -27,17 +43,60 @@ export default function Library({ application }) {
window.add_css_class("devel"); window.add_css_class("devel");
} }
let last_selected; let last_triggered;
window.connect("close-request", quitOnLastWindowClose); window.connect("close-request", quitOnLastWindowClose);
const demos = getDemos(); const categories = [
demos.forEach((demo) => { { id: "all", name: _("Any Category"), index: 0 },
const widget = new EntryRow({ demo: demo }); { id: "tools", name: _("Tools"), index: 1 },
if (demo.name === "Welcome") last_selected = widget; { id: "network", name: _("Network"), index: 2 },
{ id: "controls", name: _("Controls"), index: 3 },
{ id: "layout", name: _("Layout"), index: 4 },
{ id: "feedback", name: _("Feedback"), index: 5 },
{ id: "navigation", name: _("Navigation"), index: 6 },
{ id: "user_interface", name: _("User Interface"), index: 7 },
{ id: "platform", name: _("Platform APIs"), index: 8 },
];
const categories_by_id = Object.fromEntries(
categories.map((category) => [category.id, category]),
);
const categories_by_index = Object.fromEntries(
categories.map((category) => [category.index, category]),
);
const category_all = categories_by_id["all"];
widget.connect("activated", (_self, language) => { categories.forEach((category) => {
last_selected = widget; dropdown_category.model.append(category.name);
});
const languages = [
{ id: "all", name: _("Any Language"), index: 0 },
{ id: "javascript", name: _("JavaScript"), index: 1 },
{ id: "python", name: _("Python"), index: 2 },
{ id: "rust", name: _("Rust"), index: 3 },
{ id: "vala", name: _("Vala"), index: 4 },
];
if (isTypeScriptEnabled()) {
languages.push({ id: "typescript", name: _("TypeScript"), index: 5 });
}
const languages_by_id = Object.fromEntries(
languages.map((language) => [language.id, language]),
);
const languages_by_index = Object.fromEntries(
languages.map((language) => [language.index, language]),
);
languages.forEach((language) => {
dropdown_language.model.append(language.name);
});
const demos = getDemos();
const entries = demos.map((demo) => {
const entry_row = new EntryRow({ demo });
if (demo.name === "Welcome") last_triggered = entry_row;
entry_row.connect("triggered", (_self, language) => {
last_triggered = entry_row;
openDemo({ openDemo({
application, application,
@ -45,17 +104,66 @@ export default function Library({ application }) {
language, language,
}).catch(console.error); }).catch(console.error);
}); });
listbox.append(entry_row);
objects[`library_${demo.category}`].add(widget); const category = categories_by_id[demo.category];
const widget = entry_row;
const languages = demo.languages.map((lang) => languages_by_id[lang]);
return { ...demo, category, widget, languages };
}); });
const language_all = languages_by_id["all"];
function updateList() {
const current_category =
categories_by_index[dropdown_category.get_selected()];
const current_language =
languages_by_index[dropdown_language.get_selected()];
const search_term = search_entry.get_text().toLowerCase();
let results_found = false;
entries.forEach(({ name, description, category, languages, widget }) => {
const category_match =
current_category === category_all || category === current_category;
const language_match =
current_language === language_all ||
languages.includes(current_language);
const search_match =
search_term === "" ||
name.toLowerCase().includes(search_term) ||
description.toLowerCase().includes(search_term) ||
category.name.toLowerCase().includes(search_term);
const is_match = category_match && language_match && search_match;
widget.visible = is_match;
if (is_match) {
results_found = true;
}
});
results_empty.visible = !results_found;
}
search_entry.connect("search-changed", updateList);
dropdown_category.connect("notify::selected", updateList);
dropdown_language.connect("notify::selected", updateList);
function reset() {
scrolled_window.vadjustment = null;
search_entry.text = "";
dropdown_category.selected = category_all.index;
dropdown_language.selected = language_all.index;
}
button_reset.connect("clicked", reset);
window.connect("show", reset);
const action_library = new Gio.SimpleAction({ const action_library = new Gio.SimpleAction({
name: "library", name: "library",
parameter_type: null, parameter_type: null,
}); });
action_library.connect("activate", () => { action_library.connect("activate", () => {
window.present(); window.present();
last_selected?.grab_focus(); last_triggered?.grab_focus();
}); });
application.add_action(action_library); application.add_action(action_library);
application.set_accels_for_action("app.library", ["<Control><Shift>O"]); application.set_accels_for_action("app.library", ["<Control><Shift>O"]);
@ -86,9 +194,9 @@ async function openDemo({ application, demo_name, language }) {
session.settings.set_int("code-language", language.index); session.settings.set_int("code-language", language.index);
global_settings.set_int("recent-code-language", language.index); global_settings.set_int("recent-code-language", language.index);
// If the user explictely requested to open the demo // If the user explicitly requested to open the demo
// in a specific language then that's probably what they are interested in // in a specific language then that's probably what they are interested in
// therefor override the demo default and force show the code panel // therefore override the demo default and force show the code panel
session.settings.set_boolean("show-code", true); session.settings.set_boolean("show-code", true);
} }
@ -103,9 +211,17 @@ async function openDemo({ application, demo_name, language }) {
); );
} }
const { load, runCode } = Window({ application, session }); const { load, runCode, window } = Window({
application,
session,
});
await load(); await load();
if (needsAdditionalPermissions({ demo })) {
showPermissionsDialog({ window });
return;
}
const code_language = session.getCodeLanguage(); const code_language = session.getCodeLanguage();
const run = autorun && code_language.id === "javascript"; const run = autorun && code_language.id === "javascript";
if (run) { if (run) {

View File

@ -1,13 +1,16 @@
import Gio from "gi://Gio"; import Gio from "gi://Gio";
import GObject from "gi://GObject"; import GObject from "gi://GObject";
import { settings as global_settings, makeDropdownFlat } from "./util.js"; import { makeDropdownFlat, settings as global_settings } from "./util.js";
import { setupRustProject } from "./langs/rust/rust.js"; import { setupRustProject } from "./langs/rust/rust.js";
import { setupTypeScriptProject } from "./langs/typescript/typescript.js";
import { setupJavascriptProject } from "./langs/javascript/javascript.js";
export default function PanelCode({ export default function PanelCode({
builder, builder,
previewer, previewer,
session: { settings, file }, session: { settings, file },
langs,
}) { }) {
const panel_code = builder.get_object("panel_code"); const panel_code = builder.get_object("panel_code");
const button_code = builder.get_object("button_code"); const button_code = builder.get_object("button_code");
@ -53,9 +56,21 @@ export default function PanelCode({
stack_code.visible_child_name = panel.language; stack_code.visible_child_name = panel.language;
previewer.useInternal().catch(console.error); previewer.useInternal().catch(console.error);
if (panel.language.toLowerCase() === "javascript") {
setupJavascriptProject(file, langs.javascript.document).catch(
console.error,
);
}
if (panel.language.toLowerCase() === "rust") { if (panel.language.toLowerCase() === "rust") {
setupRustProject(file).catch(console.error); setupRustProject(file).catch(console.error);
} }
if (panel.language.toLowerCase() === "typescript") {
setupTypeScriptProject(file, langs.typescript.document).catch(
console.error,
);
}
} }
switchLanguage(); switchLanguage();

View File

@ -76,19 +76,18 @@ export default function PanelUI({
"button_ui_experimental_blueprint", "button_ui_experimental_blueprint",
); );
button_ui_experimental_blueprint.connect("clicked", () => { button_ui_experimental_blueprint.connect("clicked", () => {
const modal = builder.get_object("modal_blueprint_experimental"); const dialog = builder.get_object("dialog_blueprint_experimental");
modal.set_transient_for(application.get_active_window()); dialog.present(application.get_active_window());
modal.present();
}); });
const button_blueprint_documentation = builder.get_object( const button_blueprint_documentation = builder.get_object(
"button_blueprint_documentation", "button_blueprint_documentation",
); );
button_blueprint_documentation.connect("clicked", () => { button_blueprint_documentation.connect("clicked", () => {
Gtk.show_uri( new Gtk.UriLauncher({
null, uri: "https://jwestman.pages.gitlab.gnome.org/blueprint-compiler/",
"https://jwestman.pages.gitlab.gnome.org/blueprint-compiler/", })
null, .launch(application.get_active_window(), null)
); .catch(console.error);
}); });
let handler_id_xml = null; let handler_id_xml = null;

View File

@ -0,0 +1,155 @@
using Gtk 4.0;
using Adw 1;
Adw.Dialog dialog {
content-height: 750;
content-width: 600;
Adw.ToolbarView {
[top]
Adw.HeaderBar {}
content: ScrolledWindow {
hscrollbar-policy: never;
Adw.Clamp {
maximum-size: 520;
tightening-threshold: 400;
margin-start: 12;
margin-end: 12;
margin-bottom: 24;
Box {
orientation: vertical;
halign: fill;
spacing: 24;
// Gtk.Picture needs to be wrapped in a box to behave properly
Box {
halign: center;
Picture picture_illustration {
can-shrink: false;
margin-bottom: 24;
}
}
Label {
label: _("Permissions Needed");
styles [
"title-1",
]
}
Label {
label: _("Workbench needs additional permissions. Please run the following command in a terminal and restart Workbench.");
wrap: true;
justify: center;
}
Label label_command {
use-markup: true;
wrap: true;
wrap-mode: word_char;
selectable: true;
xalign: 0;
styles [
"command_snippet",
]
}
Box {
orientation: vertical;
Box {
margin-bottom: 6;
Label {
label: _("What it does");
halign: start;
hexpand: true;
styles [
"heading",
]
}
Button button_info {
icon-name: "re.sonny.Workbench-external-link-symbolic";
styles [
"flat",
]
}
}
ListBox {
selection-mode: none;
styles [
"boxed-list",
]
Adw.ActionRow {
[prefix]
Image {
icon-name: "re.sonny.Workbench-person-symbolic";
}
title: _("--user");
subtitle: _("Grant for your account only");
styles [
"property",
]
}
Adw.ActionRow {
[prefix]
Image {
icon-name: "re.sonny.Workbench-network-wireless-symbolic";
}
title: _("--share-network");
subtitle: _("Network access");
styles [
"property",
]
}
Adw.ActionRow {
[prefix]
Image {
icon-name: "re.sonny.Workbench-speakers-symbolic";
}
title: _("--socket=pulseaudio");
subtitle: _("Record and play audio");
styles [
"property",
]
}
Adw.ActionRow action_row_device {
[prefix]
Image {
icon-name: "re.sonny.Workbench-gamepad-symbolic";
}
// title: _("--device=input");
subtitle: _("Access to input device such as gamepads");
styles [
"property",
]
}
}
}
}
}
};
}
}

View File

@ -0,0 +1,72 @@
import Gio from "gi://Gio";
import Gtk from "gi://Gtk";
import { build } from "../../troll/src/main.js";
import Interface from "./Permissions.blp" with { type: "uri" };
import illustration from "./permissions.svg";
import {
getFlatpakId,
getFlatpakInfo,
isDeviceInputOverrideAvailable,
} from "../flatpak.js";
const device = isDeviceInputOverrideAvailable() ? "input" : "all";
const action_permissions = new Gio.SimpleAction({
name: "permissions",
parameter_type: null,
});
export function Permissions({ window }) {
const {
dialog,
picture_illustration,
label_command,
button_info,
action_row_device,
} = build(Interface);
picture_illustration.set_resource(illustration);
label_command.label = `flatpak override --user --share=network --socket=pulseaudio --device=${device} ${getFlatpakId()}`;
action_row_device.title = `--input=${device}`;
button_info.connect("clicked", () => {
new Gtk.UriLauncher({
uri: "https://docs.flatpak.org/en/latest/sandbox-permissions.html",
})
.launch(window, null)
.catch(console.error);
});
action_permissions.connect("activate", () => {
dialog.present(window);
});
window.add_action(action_permissions);
}
const missing_permissions = (() => {
const flatpak_info = getFlatpakInfo();
const shared = flatpak_info.get_string_list("Context", "shared");
const sockets = flatpak_info.get_string_list("Context", "sockets");
const devices = flatpak_info.get_string_list("Context", "devices");
return (
!shared.includes("network") ||
!sockets.includes("pulseaudio") ||
!devices.includes(device)
);
})();
export function needsAdditionalPermissions({ demo }) {
if (!demo["flatpak-finish-args"]) return false;
return missing_permissions;
}
export function showPermissionsDialog({ window }) {
window.activate_action("permissions", null);
}

View File

@ -0,0 +1,568 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="332"
height="200"
viewBox="0 0 332 200"
version="1.1"
id="svg5"
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
sodipodi:docname="permissions.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="false"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="false"
inkscape:deskcolor="#ffffff"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="0.099784977"
inkscape:cx="-661.42221"
inkscape:cy="-65.140066"
inkscape:current-layer="layer1"
showguides="false"
inkscape:window-width="1920"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1">
<inkscape:grid
id="grid1"
units="px"
originx="0"
originy="0"
spacingx="1"
spacingy="1"
empcolor="#0099e5"
empopacity="0.30196078"
color="#0099e5"
opacity="0.14901961"
empspacing="4"
dotted="false"
gridanglex="30"
gridanglez="30"
visible="false" />
</sodipodi:namedview>
<defs
id="defs2">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect65"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,21.13606,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,9.1490264,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<linearGradient
id="linearGradient63"
inkscape:collect="always">
<stop
style="stop-color:#fbe47c;stop-opacity:0.6635828;"
offset="0"
id="stop63" />
<stop
style="stop-color:#87e8b5;stop-opacity:0.7638889;"
offset="1"
id="stop64" />
</linearGradient>
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect62"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,1,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect60"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,2,0,1 @ F,0,1,1,0,2,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect58"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,6,0,1 @ F,0,1,1,0,6,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect56"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,1,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<linearGradient
id="linearGradient26"
inkscape:collect="always">
<stop
style="stop-color:#2177df;stop-opacity:1;"
offset="0"
id="stop26" />
<stop
style="stop-color:#1a5fb4;stop-opacity:1;"
offset="0.09090688"
id="stop28" />
<stop
style="stop-color:#1a5fb4;stop-opacity:1;"
offset="0.9090907"
id="stop29" />
<stop
style="stop-color:#15498a;stop-opacity:1;"
offset="1"
id="stop27" />
</linearGradient>
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect23"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,1,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<linearGradient
id="linearGradient5"
inkscape:collect="always">
<stop
style="stop-color:#5e5c64;stop-opacity:1;"
offset="0"
id="stop5" />
<stop
style="stop-color:#4d4c50;stop-opacity:1;"
offset="1"
id="stop6" />
</linearGradient>
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect5"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,2,0,1 @ F,0,1,1,0,2,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,6,0,1 @ F,0,1,1,0,6,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,8,0,1 @ F,0,0,1,0,8.2101528,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect211"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect157"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect102"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1 @ F,0,1,1,0,12.476317,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect39"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,3.414062,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath31">
<path
style="opacity:1;fill:#865e3c;fill-opacity:1;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:3.57621, 3.57621"
d="m 104.57142,32 c -2.53257,0 -4.571424,1.784002 -4.571424,4 V 82 H 227.99999 V 36 c 0,-2.215998 -2.03886,-4 -4.57143,-4 z"
id="path32" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath33">
<path
style="opacity:1;fill:#865e3c;fill-opacity:1;stroke-width:1.99999;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:3.57621, 3.57621"
d="m 99.999997,90 v 46 c 0,2.216 2.102573,4 4.714283,4 h 122.57143 c 2.61171,0 4.71428,-1.784 4.71428,-4 V 90 Z"
id="path34" />
</clipPath>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5"
id="linearGradient6"
x1="176.92285"
y1="156"
x2="176.92285"
y2="164"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.4984166,0,0,1.4984166,-49.616298,-72.451818)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient26"
id="linearGradient27"
x1="100"
y1="120.00011"
x2="100"
y2="164"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.4984166,0,0,1.4984166,-49.616298,-72.451818)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient63"
id="linearGradient65"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-35.382733,-4.2152026)"
x1="248.07971"
y1="113.07767"
x2="85.016251"
y2="113.07767" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
id="circle65"
style="fill:url(#linearGradient65);stroke:none;stroke-width:1.46117;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 131.16602,27.330078 c -45.029881,-8.12e-4 -81.534017,36.503321 -81.533208,81.533202 2.7e-4,45.02912 36.504089,81.53206 81.533208,81.53125 8.38262,-0.006 16.67054,-1.30441 24.58233,-3.81292 11.09609,-3.51812 26.47255,-2.63586 37.20566,1.85535 9.07487,3.79734 18.86926,5.80785 28.84287,5.84038 41.61705,-7.5e-4 75.35384,-33.73841 75.35351,-75.35546 -7.4e-4,-41.616289 -33.73722,-75.352772 -75.35351,-75.353521 -8.35948,0.02724 -16.6344,1.44503 -24.49097,4.175374 -4.76994,1.657664 -11.30579,0.572701 -15.25124,-2.579772 -14.3779,-11.488134 -32.30568,-17.832988 -50.88865,-17.833883 z"
transform="matrix(-1.0569476,0,0,1.0569476,354.87042,-13.797304)" />
<path
style="fill:none;fill-opacity:1;stroke:#1a5fb4;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 85.947825,152 H 64 a 4,4 135 0 0 -4,4 4,4 45 0 0 4,4 h 4 a 4,4 45 0 1 4,4 4,4 135 0 1 -4,4 H 56 a 4,4 135 0 0 -4,4 4,4 45 0 0 4,4 l 28,0 a 4,4 135 0 0 4,-4 4,4 135 0 1 4,-4 h 20"
id="path23"
sodipodi:nodetypes="cccccccccc"
transform="matrix(1.4984165,0,0,1.4984165,-49.616294,-72.451812)" />
<g
id="g21"
transform="matrix(1.4984165,0,0,1.4984165,-291.25069,-41.061959)"
style="fill:#5e5c64">
<path
id="path16"
style="color:#000000;fill:#5e5c64;stroke-linecap:square;-inkscape-stroke:none"
d="m 296.00007,60.838426 -15.15429,45.460934 a 1.0001,1.0001 0 0 0 -0.002,0.008 l -10.51757,31.55664 -0.72461,2.16992 -0.31641,0.94726 1.89844,0.63282 0.3164,-0.94922 0.61719,-1.85156 23.88281,-19.40625 23.88282,19.40625 0.18554,0.55468 0.5,-0.16601 a 1,1 0 0 0 0.86328,-0.28906 l 0.53321,-0.17774 -0.31446,-0.94726 -10.49414,-31.48047 v -0.004 a 1.0001,1.0001 0 0 0 -0.004,-0.008 l -0.006,-0.0195 a 1,1 0 0 0 -0.004,-0.008 z m 0,6.326172 6.58204,19.748047 h -13.16407 z m -5.23046,21.748047 h 10.46093 l -5.23047,4.40625 z m -2.25586,0.712891 5.93359,5 -10.56836,8.902344 z m 14.97461,0 4.63281,13.904294 -10.56836,-8.904294 z m -7.48829,6.308593 11.45704,9.650391 h -22.91407 z m -11.3789,11.650391 h 22.75781 l -11.37891,9.24609 z m -2.32422,0.6875 12.11719,9.84766 -21.11914,17.16015 z m 27.40625,0 9.00195,27.00781 -21.11914,-17.16015 z" />
</g>
<circle
style="fill:#deddda;fill-opacity:1;stroke:none;stroke-width:1.49841;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="circle82"
cx="152.28058"
cy="56.836697"
r="15.564472" />
<path
id="path1"
style="fill:#3d3846;stroke-width:2;stroke-dasharray:2, 2"
d="m 278,104 h 55.8457 l 8.80509,-38.153666 A 4.7730278,4.7730278 51.497589 0 0 338,60 l -44,0 a 7.5166441,7.5166441 141.40213 0 0 -7.32973,5.850797 z"
transform="matrix(1.4984165,0,0,1.4984165,-217.43895,5.4658483)" />
<path
id="path2"
style="fill:url(#linearGradient6);stroke-width:2.99683;stroke-dasharray:2.99683, 2.99683"
d="m 148.1747,161.30117 c 0,6.62041 5.3669,11.98733 11.98733,11.98733 h 107.57109 c 7.20105,0 13.44901,-4.9706 15.06784,-11.98733 z"
sodipodi:nodetypes="ccccc" />
<path
id="path3"
style="fill:#77767b;stroke-width:2.99683;stroke-dasharray:2.99683, 2.99683"
d="m 232.08602,161.30117 c 0,6.62041 5.3669,11.98733 11.98733,11.98733 h 23.65977 c 7.20105,0 13.44901,-4.9706 15.06784,-11.98733 z"
sodipodi:nodetypes="ccccc" />
<path
id="path4"
style="fill:#99c1f1;stroke-width:2;stroke-dasharray:2, 2"
d="m 282,104 h 47.8457 l 8.70812,-38.050404 A 1.5939923,1.5939923 51.445289 0 0 337,64 h -44 a 2.5,2.5 141.34019 0 0 -2.43902,1.95122 z"
sodipodi:nodetypes="ccccc"
transform="matrix(1.4984165,0,0,1.4984165,-217.43895,5.4658483)" />
<rect
style="fill:url(#linearGradient27);stroke:none;stroke-width:2.99683;stroke-linecap:round;stroke-dasharray:none"
id="rect7"
width="59.936661"
height="65.930313"
x="70.257019"
y="107.35817"
ry="6.3252244"
rx="6.3252244" />
<rect
style="fill:#62a0ea;fill-opacity:1;stroke:none;stroke-width:2.99683;stroke-linecap:round;stroke-dasharray:none"
id="rect6"
width="47.949329"
height="65.930313"
x="88.238022"
y="107.35818"
ry="6.3252249"
rx="6.3252244" />
<circle
style="fill:#1a5fb4;fill-opacity:1;stroke:none;stroke-width:2.99683;stroke-linecap:round;stroke-dasharray:none"
id="circle7"
cx="112.21269"
cy="155.30748"
r="11.987332" />
<circle
style="fill:#1a5fb4;fill-opacity:1;stroke:none;stroke-width:2.99683;stroke-linecap:round;stroke-dasharray:none"
id="circle8"
cx="112.21269"
cy="125.33916"
r="11.987332" />
<circle
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.99683;stroke-linecap:round;stroke-dasharray:none"
id="path8"
cx="112.21269"
cy="125.33916"
r="4.4952497" />
<circle
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.99683;stroke-linecap:round;stroke-dasharray:none"
id="circle9"
cx="112.21269"
cy="155.30748"
r="4.4952497" />
<circle
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.49841;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="path22"
cx="152.28058"
cy="54.836697"
r="15.564472" />
<circle
style="fill:#77767b;fill-opacity:1;stroke:none;stroke-width:2.99683;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="circle22"
cx="152.28058"
cy="54.836697"
r="3.1248076" />
<rect
style="fill:#f6f5f4;fill-opacity:1;stroke:none;stroke-width:1.49841;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
id="rect24"
width="14.984165"
height="2.9968331"
x="115.20953"
y="177.78374"
ry="1.4984165"
rx="1.4984165" />
<rect
style="fill:#1a5fb4;fill-opacity:1;stroke:none;stroke-width:1.49841;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
id="rect23"
width="13.485737"
height="5.9936943"
x="106.21901"
y="176.28529"
ry="2.9968472" />
<rect
style="fill:#c0bfbc;fill-opacity:1;stroke:none;stroke-width:1.49841;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
id="rect25"
width="1.4984146"
height="2.9968281"
x="122.70161"
y="177.78374" />
<rect
style="fill:#c0bfbc;fill-opacity:1;stroke:none;stroke-width:1.49841;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
id="rect26"
width="1.4984146"
height="2.9968281"
x="125.69843"
y="177.78374" />
<circle
style="fill:#bf61ca;fill-opacity:0.48627451;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:0.443622"
id="path71"
cx="150.07011"
cy="33.371632"
r="1.7915844" />
<circle
style="fill:#bf61ca;fill-opacity:0.48627451;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:0.443622"
id="circle71"
cx="150.07011"
cy="26.488079"
r="1.7915844" />
<circle
style="fill:#bf61ca;fill-opacity:0.48627451;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:0.443622"
id="circle72"
cx="150.07011"
cy="19.604527"
r="1.7915844" />
<circle
style="fill:#bf61ca;fill-opacity:0.48627451;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:0.443622"
id="circle77"
cx="150.07011"
cy="12.720974"
r="1.7915844" />
<circle
style="fill:#c061cb;fill-opacity:0.484565;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:0.443622"
id="circle78"
cx="154.56282"
cy="30.071247"
r="1.7915844" />
<circle
style="fill:#c061cb;fill-opacity:0.484565;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:0.443622"
id="circle79"
cx="154.56282"
cy="23.187695"
r="1.7915844" />
<circle
style="fill:#c061cb;fill-opacity:0.484565;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:0.443622"
id="circle80"
cx="154.56282"
cy="16.304142"
r="1.7915844" />
<circle
style="fill:#c061cb;fill-opacity:0.484565;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:0.443622"
id="circle81"
cx="154.56282"
cy="9.4205904"
r="1.7915844" />
<g
fill="#222222"
id="g2"
transform="matrix(1.7236266,0,-0.3679857,1.7236266,234.34039,117.54652)"
style="fill:#62a0ea;stroke-width:0.580172">
<path
d="M 9.492188,0.140625 C 8.640625,-0.0234375 7.769531,-0.0429688 6.917969,0.0742188 5.21875,0.304688 3.589844,1.082031 2.316406,2.367188 -0.230469,4.933594 -0.734375,8.90625 1.085938,12.03125 c 1.824218,3.121094 5.53125,4.636719 9.019531,3.683594 3.488281,-0.949219 5.90625,-4.132813 5.890625,-7.75 0,-0.550782 -0.453125,-1 -1.003906,-0.996094 -0.550782,0.00391 -0.996094,0.453125 -0.996094,1.003906 0.01563,2.722656 -1.792969,5.097656 -4.417969,5.816406 C 6.953125,14.503906 4.1875,13.371094 2.816406,11.023438 1.445312,8.671875 1.820312,5.707031 3.738281,3.773438 5.652344,1.84375 8.613281,1.4375 10.976562,2.789062 11.453125,3.0625 12.066406,2.898438 12.339844,2.417969 12.613281,1.9375 12.449219,1.328125 11.96875,1.050781 11.183594,0.605469 10.347656,0.300781 9.492188,0.140625 Z m 0,0"
id="path1-6"
style="fill:#62a0ea;stroke-width:0.580172" />
<path
d="M 15.753906,3.65625 C 15.929688,3.457031 16.015625,3.195312 16,2.933594 15.980469,2.667969 15.859375,2.421875 15.660156,2.246094 15.460938,2.070312 15.199219,1.980469 14.933594,2 14.667969,2.015625 14.421875,2.140625 14.246094,2.339844 L 7.949219,9.535156 5.707031,7.289062 C 5.316406,6.898438 4.683594,6.898438 4.292969,7.289062 4.105469,7.476562 4,7.734375 4,8 4,8.265625 4.105469,8.519531 4.292969,8.707031 l 3,3 C 7.488281,11.902344 7.757812,12.007812 8.035156,12 8.3125,11.988281 8.570312,11.867188 8.753906,11.65625 Z m 0,0"
id="path2-7"
style="fill:#62a0ea;stroke-width:0.580172" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -30,10 +30,7 @@ export default function External({ output, builder, onWindowChange }) {
updateColorScheme(); updateColorScheme();
stack.set_visible_child_name("close_window"); stack.set_visible_child_name("close_window");
try { try {
await dbus_proxy.OpenWindowAsync( await dbus_proxy.OpenWindowAsync(output.get_width(), output.get_height());
output.get_allocated_width(),
output.get_allocated_height(),
);
} catch (err) { } catch (err) {
console.debug(err); console.debug(err);
} }

View File

@ -132,7 +132,7 @@ export default function Internal({
const prop_name = prop.get_name(); const prop_name = prop.get_name();
// AdwWindow and AdwApplicationWindow have child and titlebar properties but do not support setting them // AdwWindow and AdwApplicationWindow have child and titlebar properties but do not support setting them
// "Using gtk_window_get_titlebar() and gtk_window_set_titlebar() is not supported and will result in a crash." // "Using gtk_window_get_titlebar() and gtk_window_set_titlebar() is not supported and will result in a crash."
// https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1.5/class.Window.html // https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1.6/class.Window.html
// https://github.com/workbenchdev/Workbench/issues/130 // https://github.com/workbenchdev/Workbench/issues/130
if ( if (
(object_preview instanceof Adw.Window || (object_preview instanceof Adw.Window ||
@ -257,7 +257,7 @@ function scopeStylesheet(style, id) {
for (const node of ast.nodes) { for (const node of ast.nodes) {
if (node.selector === "window") { if (node.selector === "window") {
node.selector = `#${id}`; node.selector = `#${id}`;
} else if (node.selector) { } else if (!node.selector.startsWith(":")) {
node.selector = `#${id} ${node.selector}`; node.selector = `#${id} ${node.selector}`;
} }
} }
@ -272,8 +272,8 @@ function scopeStylesheet(style, id) {
function screenshot({ widget, path }) { function screenshot({ widget, path }) {
const paintable = new Gtk.WidgetPaintable({ widget }); const paintable = new Gtk.WidgetPaintable({ widget });
const width = widget.get_allocated_width(); const width = widget.get_width();
const height = widget.get_allocated_height(); const height = widget.get_height();
const snapshot = Gtk.Snapshot.new(); const snapshot = Gtk.Snapshot.new();
paintable.snapshot(snapshot, width, height); paintable.snapshot(snapshot, width, height);

View File

@ -6,6 +6,13 @@ namespace Workbench {
this.notify["ColorScheme"].connect (() => { this.notify["ColorScheme"].connect (() => {
this.style_manager.color_scheme = this.ColorScheme; this.style_manager.color_scheme = this.ColorScheme;
}); });
// See application.js
var icon_theme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default());
string[] resource_path = {"/org/gtk/libgtk/icons/", "/org/gnome/Adwaita/icons/", "/re/sonny/Workbench/icons/"};
icon_theme.resource_path = resource_path;
string [] search_path = {"/usr/share/icons", "/app/share/icons"};
icon_theme.search_path = search_path;
} }
private void ensure_window () { private void ensure_window () {
@ -31,8 +38,8 @@ namespace Workbench {
public bool screenshot (string path) { public bool screenshot (string path) {
Gtk.Widget widget = this.target; Gtk.Widget widget = this.target;
var paintable = new Gtk.WidgetPaintable (widget); var paintable = new Gtk.WidgetPaintable (widget);
int width = widget.get_allocated_width (); int width = widget.get_width ();
int height = widget.get_allocated_height (); int height = widget.get_height ();
var snapshot = new Gtk.Snapshot (); var snapshot = new Gtk.Snapshot ();
paintable.snapshot (snapshot, width, height); paintable.snapshot (snapshot, width, height);
Gsk.RenderNode? node = snapshot.to_node (); Gsk.RenderNode? node = snapshot.to_node ();
@ -136,6 +143,8 @@ namespace Workbench {
return; return;
} }
this.reload_icons (uri);
void* function; void* function;
this.module.symbol ("set_base_uri", out function); this.module.symbol ("set_base_uri", out function);
@ -163,6 +172,17 @@ namespace Workbench {
main_function (); main_function ();
} }
public async void reload_icons (string uri) {
if (this.resource_icons != null) {
this.resource_icons._unregister ();
this.resource_icons = null;
}
try {
this.resource_icons = Resource.load (File.new_for_uri(uri).get_child("icons.gresource").get_path());
this.resource_icons._register ();
} catch {}
}
public void close_window () { public void close_window () {
if (this.window == null) { if (this.window == null) {
return; return;
@ -205,6 +225,7 @@ namespace Workbench {
private Module module; private Module module;
private Gtk.Builder? builder = null; private Gtk.Builder? builder = null;
private Adw.StyleManager style_manager = Adw.StyleManager.get_default (); private Adw.StyleManager style_manager = Adw.StyleManager.get_default ();
private GLib.Resource? resource_icons = null;
} }
void main (string[] args) { void main (string[] args) {
@ -214,7 +235,8 @@ namespace Workbench {
} }
var app_id = GLib.Environment.get_variable("FLATPAK_ID"); var app_id = GLib.Environment.get_variable("FLATPAK_ID");
Resource.load (@"/app/share/$app_id/re.sonny.Workbench.libworkbench.gresource")._register (); var resource = Resource.load (@"/app/share/$app_id/re.sonny.Workbench.libworkbench.gresource");
GLib.resources_register (resource);
var loop = new MainLoop (); var loop = new MainLoop ();

View File

@ -110,14 +110,14 @@ function assertObjectBuildable(el_object, is_root) {
`${klass.$gtype.name} does not support the child property.`, `${klass.$gtype.name} does not support the child property.`,
); );
} }
}
// Gtk-ERROR **: 23:19:54.204: GtkStackPage '<unnamed>' [0x55b094802eb0] is missing a child widget // Gtk-ERROR **: 23:19:54.204: GtkStackPage '<unnamed>' [0x55b094802eb0] is missing a child widget
if (GObject.type_is_a(klass, Gtk.StackPage)) { if (GObject.type_is_a(klass, Gtk.StackPage)) {
const child = getChildProperty(el_object); const child = getChildProperty(el_object);
// log(child); // log(child);
if (!child) { if (!child) {
throw new Error(`${klass.$gtype.name} is missing a child widget.`); throw new Error(`${klass.$gtype.name} is missing a child widget.`);
}
} }
} }

View File

@ -12,8 +12,7 @@ class WorkbenchHoverProvider extends GObject.Object {
} }
findDiagnostics(context) { findDiagnostics(context) {
const iter = new Gtk.TextIter(); const [, iter] = context.get_iter();
context.get_iter(iter);
const line = iter.get_line(); const line = iter.get_line();
// Looks like line_offset starts at 0 // Looks like line_offset starts at 0

View File

@ -8,7 +8,7 @@ import {
getGjsVersion, getGjsVersion,
getGLibVersion, getGLibVersion,
} from "../troll/src/util.js"; } from "../troll/src/util.js";
import { getFlatpakInfo } from "./util.js"; import { getFlatpakInfo } from "./flatpak.js";
export default function About({ application }) { export default function About({ application }) {
const flatpak_info = getFlatpakInfo(); const flatpak_info = getFlatpakInfo();
@ -32,9 +32,9 @@ ${getBlueprintVersion()}
copyright: "© 2022 Sonny Piers", copyright: "© 2022 Sonny Piers",
license_type: Gtk.License.GPL_3_0_ONLY, license_type: Gtk.License.GPL_3_0_ONLY,
version: pkg.version, version: pkg.version,
website: "https://apps.gnome.org/Workbench", website: "https://workbench.sonny.re",
application_icon: pkg.name, application_icon: pkg.name,
issue_url: "https://github.com/workbenchdev/Workbench/issues", issue_url: "https://workbench.sonny.re/feedback",
debug_info, debug_info,
developers: [ developers: [
"Sonny Piers https://sonny.re", "Sonny Piers https://sonny.re",
@ -81,10 +81,11 @@ ${getBlueprintVersion()}
"Urtsi Santsi <urtsi.santsi@proton.me>", "Urtsi Santsi <urtsi.santsi@proton.me>",
"Roland Lötscher https://github.com/rolandlo", "Roland Lötscher https://github.com/rolandlo",
"Gregor Niehl https://fosstodon.org/@gregorni", "Gregor Niehl https://fosstodon.org/@gregorni",
"Bart Gravendeel https://monster.codeberg.page", "Jamie Gravendeel https://jamie.garden",
"Bharat Tyagi https://github.com/BharatAtbrat", "Bharat Tyagi https://github.com/BharatAtbrat",
"Jan Fooken https://git.janvhs.com", "Jan Fooken https://git.janvhs.com",
"Vladimir Vaskov https://github.com/Rirusha", "Vladimir Vaskov https://github.com/Rirusha",
"Nokse https://github.com/Nokse22",
// Add yourself as // Add yourself as
// "John Doe", // "John Doe",
// or // or
@ -103,5 +104,5 @@ function getValaVersion() {
} }
function getBlueprintVersion() { function getBlueprintVersion() {
return "Blueprint d47955c5"; return "Blueprint 0.16.0";
} }

View File

@ -1,14 +1,13 @@
import Gtk from "gi://Gtk"; import Gtk from "gi://Gtk";
import Gio from "gi://Gio"; import Gio from "gi://Gio";
import GLib from "gi://GLib"; import GLib from "gi://GLib";
import Gdk from "gi://Gdk";
import Xdp from "gi://Xdp"; import Xdp from "gi://Xdp";
import XdpGtk from "gi://XdpGtk4"; import XdpGtk from "gi://XdpGtk4";
import About from "./about.js"; import About from "./about.js";
import Window from "./window.js";
import { portal, settings } from "./util.js"; import { portal, settings } from "./util.js";
import { createSession } from "./sessions.js";
import IconLibrary from "./IconLibrary/main.js";
export default function Actions({ application }) { export default function Actions({ application }) {
const quit = new Gio.SimpleAction({ const quit = new Gio.SimpleAction({
@ -30,38 +29,16 @@ export default function Actions({ application }) {
}); });
application.add_action(showAboutDialog); application.add_action(showAboutDialog);
const action_icon_library = new Gio.SimpleAction({
name: "icon_library",
});
let window_icon_browser;
action_icon_library.connect("activate", (_self, _target) => {
window_icon_browser ??= IconLibrary();
window_icon_browser.present();
});
application.add_action(action_icon_library);
const action_open_uri = new Gio.SimpleAction({ const action_open_uri = new Gio.SimpleAction({
name: "open_uri", name: "open_uri",
parameter_type: new GLib.VariantType("s"), parameter_type: new GLib.VariantType("s"),
}); });
action_open_uri.connect("activate", (_self, target) => { action_open_uri.connect("activate", (_self, target) => {
// This is not using the portal but we silence the GVFS warnings new Gtk.UriLauncher({
// in `log_handler.js` uri: target.unpack(),
Gtk.show_uri( })
application.get_active_window(), .launch(application.get_active_window(), null)
target.unpack(), .catch(console.error);
Gdk.CURRENT_TIME,
);
// an other option is to use libportal:
// const parent = XdpGtk.parent_new_gtk(application.get_active_window());
// portal
// .open_uri(
// parent,
// target.unpack(),
// Xdp.OpenUriFlags.NONE,
// null, // cancellable
// )
// .catch(console.error);
}); });
application.add_action(action_open_uri); application.add_action(action_open_uri);
@ -90,6 +67,15 @@ export default function Actions({ application }) {
// application.add_action(settings.create_action("safe-mode")); // application.add_action(settings.create_action("safe-mode"));
// application.add_action(settings.create_action("auto-preview")); // application.add_action(settings.create_action("auto-preview"));
const action_new_project = new Gio.SimpleAction({
name: "new",
});
action_new_project.connect("activate", (_self, _target) => {
newProject({ application }).catch(console.error);
});
application.add_action(action_new_project);
application.set_accels_for_action("app.new", ["<Control>N"]);
const action_open_file = new Gio.SimpleAction({ const action_open_file = new Gio.SimpleAction({
name: "open", name: "open",
parameter_type: new GLib.VariantType("s"), parameter_type: new GLib.VariantType("s"),
@ -122,6 +108,12 @@ async function showScreenshot({ application, uri }) {
); );
} }
async function newProject({ application }) {
const session = createSession();
const { load } = Window({ application, session });
await load();
}
async function open({ application, hint }) { async function open({ application, hint }) {
const file_dialog = new Gtk.FileDialog(); const file_dialog = new Gtk.FileDialog();

View File

@ -1,5 +1,7 @@
import Adw from "gi://Adw"; import Adw from "gi://Adw";
import Gio from "gi://Gio"; import Gio from "gi://Gio";
import Gtk from "gi://Gtk";
import Gdk from "gi://Gdk";
import Window from "./window.js"; import Window from "./window.js";
import Actions from "./actions.js"; import Actions from "./actions.js";
@ -14,6 +16,35 @@ import {
} from "./sessions.js"; } from "./sessions.js";
import ShortcutsWindow from "./shortcutsWindow.js"; import ShortcutsWindow from "./shortcutsWindow.js";
import "./icons/re.sonny.Workbench-beaker.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-code-symbolic.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-placeholder-symbolic.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-preview-symbolic.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-ui-symbolic.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-screenshot-symbolic.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-multitasking-windows-symbolic.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-down-symbolic.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-eraser4-symbolic.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-larger-brush-symbolic.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-terminal-symbolic.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-test-pass-symbolic.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-up-symbolic.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-library-symbolic.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-gamepad-symbolic.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-network-wireless-symbolic.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-person-symbolic.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-speakers-symbolic.svg" with { type: "icon" };
import "./icons/re.sonny.Workbench-external-link-symbolic.svg" with { type: "icon" };
const icon_theme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default());
icon_theme.resource_path = [
"/org/gtk/libgtk/icons/",
"/org/gnome/Adwaita/icons/",
"/re/sonny/Workbench/icons/",
];
// Remove hosts and useless search paths
icon_theme.search_path = ["/usr/share/icons", "/app/share/icons"];
ensureDir(data_dir); ensureDir(data_dir);
const application = new Adw.Application({ const application = new Adw.Application({

View File

@ -1,7 +1,6 @@
#!@GJS@ -m #!@GJS@ -m
import { exit, programArgs } from "system"; import { exit, programArgs } from "system";
import GLib from "gi://GLib";
import { setConsoleLogDomain } from "console"; import { setConsoleLogDomain } from "console";
import Xdp from "gi://Xdp"; import Xdp from "gi://Xdp";
@ -14,7 +13,6 @@ imports.package.init({
datadir: "@datadir@", datadir: "@datadir@",
}); });
setConsoleLogDomain(pkg.name); setConsoleLogDomain(pkg.name);
GLib.set_application_name("Workbench");
if (!Xdp.Portal.running_under_flatpak()) { if (!Xdp.Portal.running_under_flatpak()) {
console.error( console.error(

97
src/cli/blueprint.js Normal file
View File

@ -0,0 +1,97 @@
/* eslint-disable no-restricted-globals */
import Gtk from "gi://Gtk";
import { getLanguage } from "../common.js";
import { parse } from "../langs/xml/xml.js";
import { LSPError } from "../lsp/LSP.js";
import { checkFile, diagnose } from "./util.js";
const languageId = "blueprint";
export default async function blueprint({ file, lspc }) {
print(` ${file.get_path()}`);
await diagnose({
file,
lspc,
languageId,
filter(diagnostic) {
// No replacements yet
return ![
"Gtk.ShortcutsShortcut is deprecated\nhint: This widget will be removed in GTK 5",
"Gtk.ShortcutLabel is deprecated\nhint: This widget will be removed in GTK 5",
"Gtk.ShortcutsWindow is deprecated\nhint: This widget will be removed in GTK 5",
"Gtk.ShortcutsGroup is deprecated\nhint: This widget will be removed in GTK 5",
"Gtk.ShortcutsSection is deprecated\nhint: This widget will be removed in GTK 5",
].includes(diagnostic.message);
},
});
const { xml } = await lspc._request("textDocument/x-blueprint-compile", {
textDocument: {
uri: file.get_uri(),
},
});
print(` ✅ compiles`);
try {
await lspc._request("x-blueprint/decompile", {
text: xml,
});
print(" ✅ decompiles");
} catch (err) {
if (!(err instanceof LSPError)) throw err;
if (
![
// https://gitlab.gnome.org/jwestman/blueprint-compiler/-/issues/128
"unsupported XML tag: <condition>",
// https://gitlab.gnome.org/jwestman/blueprint-compiler/-/issues/139
"unsupported XML tag: <items>",
].includes(err.message)
) {
throw err;
}
}
await checkFile({
lspc,
file,
lang: getLanguage(languageId),
uri: file.get_uri(),
});
await lspc._notify("textDocument/didClose", {
textDocument: {
uri: file.get_uri(),
},
});
const tree = parse(xml);
const template_el = tree.getChild("template");
let template;
const builder = new Gtk.Builder();
const blueprint_object_ids = [];
if (template_el) {
template = tree.toString();
} else {
builder.add_from_string(xml, -1);
print(` ✅ instantiates`);
getXMLObjectIds(tree, blueprint_object_ids);
}
return { template, builder, blueprint_object_ids };
}
function getXMLObjectIds(tree, object_ids) {
for (const object of tree.getChildren("object")) {
if (object.attrs.id) object_ids.push(object.attrs.id);
// <child> or <property name="child">
for (const child of object.getChildElements()) {
getXMLObjectIds(child, object_ids);
}
}
}

25
src/cli/css.js Normal file
View File

@ -0,0 +1,25 @@
/* eslint-disable no-restricted-globals */
import { getLanguage } from "../common.js";
import { checkFile, diagnose } from "./util.js";
const languageId = "css";
export default async function css({ file, lspc }) {
print(` ${file.get_path()}`);
await diagnose({ file, lspc, languageId });
await checkFile({
lspc,
file,
lang: getLanguage(languageId),
uri: file.get_uri(),
});
await lspc._notify("textDocument/didClose", {
textDocument: {
uri: file.get_uri(),
},
});
}

56
src/cli/javascript.js Normal file
View File

@ -0,0 +1,56 @@
/* eslint-disable no-restricted-globals */
import { getLanguage } from "../common.js";
import { checkFile, getCodeObjectIds, diagnose, Interrupt } from "./util.js";
const languageId = "javascript";
export default async function javascript({
file,
lspc,
blueprint_object_ids,
demo_dir,
application,
builder,
template,
window,
}) {
print(` ${file.get_path()}`);
const text = await diagnose({ file, lspc, languageId });
await checkFile({
lspc: lspc,
file: file,
lang: getLanguage("javascript"),
uri: file.get_uri(),
});
const js_object_ids = getCodeObjectIds(text);
for (const object_id of js_object_ids) {
if (!blueprint_object_ids.includes(object_id)) {
print(` ❌ Reference to inexistant object id "${object_id}"`);
throw new Interrupt();
}
}
globalThis.workbench = {
window,
application,
builder,
template,
resolve(path) {
return demo_dir.resolve_relative_path(path).get_uri();
},
preview() {},
};
await import(`file://${file.get_path()}`);
print(" ✅ runs");
await lspc._notify("textDocument/didClose", {
textDocument: {
uri: file.get_uri(),
},
});
}

View File

@ -4,6 +4,7 @@ import Gio from "gi://Gio";
import { formatting } from "./format.js"; import { formatting } from "./format.js";
import { diagnostic_severities } from "../lsp/LSP.js"; import { diagnostic_severities } from "../lsp/LSP.js";
import { waitForDiagnostics } from "./util.js";
export default async function lint({ filenames, lang, lspc, ci }) { export default async function lint({ filenames, lang, lspc, ci }) {
let success = true; let success = true;
@ -47,19 +48,6 @@ export default async function lint({ filenames, lang, lspc, ci }) {
return success; return success;
} }
export function waitForDiagnostics({ uri, lspc }) {
return new Promise((resolve) => {
const handler_id = lspc.connect(
"notification::textDocument/publishDiagnostics",
(_self, params) => {
if (uri !== params.uri) return;
lspc.disconnect(handler_id);
resolve(params.diagnostics);
},
);
});
}
function serializeDiagnostics({ file, diagnostics }) { function serializeDiagnostics({ file, diagnostics }) {
return ( return (
`\n${file.get_path()}\n` + `\n${file.get_path()}\n` +

View File

@ -5,23 +5,23 @@ import "../init.js";
import GLib from "gi://GLib"; import GLib from "gi://GLib";
import Gio from "gi://Gio"; import Gio from "gi://Gio";
import Gtk from "gi://Gtk";
import Adw from "gi://Adw"; import Adw from "gi://Adw";
import GObject from "gi://GObject"; import GObject from "gi://GObject";
import Shumate from "gi://Shumate"; import Shumate from "gi://Shumate";
import WebKit from "gi://WebKit"; import WebKit from "gi://WebKit";
import { parse } from "../langs/xml/xml.js"; import { createLSPClient, languages, PYTHON_LSP_CONFIG } from "../common.js";
import { LSPError, diagnostic_severities } from "../lsp/LSP.js"; import lint from "./lint.js";
import format from "./format.js";
import { import blueprint from "./blueprint.js";
createLSPClient, import css from "./css.js";
languages, import javascript from "./javascript.js";
getLanguage, import typescript from "./typescript.js";
PYTHON_LSP_CONFIG, import vala from "./vala.js";
} from "../common.js"; import python from "./python.js";
import lint, { waitForDiagnostics } from "./lint.js"; import rust from "./rust.js";
import format, { formatting } from "./format.js"; import { Interrupt } from "./util.js";
GObject.type_ensure(Shumate.SimpleMap); GObject.type_ensure(Shumate.SimpleMap);
GObject.type_ensure(WebKit.WebView); GObject.type_ensure(WebKit.WebView);
@ -31,8 +31,16 @@ export async function main([action, ...args]) {
if (action === "ci") { if (action === "ci") {
const filenames = args; const filenames = args;
const success = await ci({ filenames, current_dir }); try {
return success ? 0 : 1; await ci({ filenames });
return 0;
} catch (err) {
if (err instanceof Interrupt) {
return 1;
} else {
throw err;
}
}
} }
const [language_id, ...filenames] = args; const [language_id, ...filenames] = args;
@ -43,12 +51,10 @@ export async function main([action, ...args]) {
} }
if (lang.id === "vala") { if (lang.id === "vala") {
const api_file = ( const file_api = Gio.File.new_for_path(pkg.pkgdatadir).get_child(
GLib.getenv("FLATPAK_ID") "workbench.vala",
? Gio.File.new_for_path(pkg.pkgdatadir) );
: current_dir.resolve_relative_path("src/langs/vala") file_api.copy(
).get_child("workbench.vala");
api_file.copy(
current_dir.get_child("workbench.vala"), current_dir.get_child("workbench.vala"),
Gio.FileCopyFlags.OVERWRITE, Gio.FileCopyFlags.OVERWRITE,
null, null,
@ -69,19 +75,27 @@ export async function main([action, ...args]) {
}); });
} }
let success = false; try {
if (action === "lint") {
if (action === "lint") { await lint({ filenames, lang, lspc, ci: false });
success = await lint({ filenames, lang, lspc, ci: false }); return 0;
} else if (action === "check") { } else if (action === "check") {
success = await lint({ filenames, lang, lspc, ci: true }); await lint({ filenames, lang, lspc, ci: true });
} else if (action === "format") { return 0;
success = await format({ filenames, lang, lspc }); } else if (action === "format") {
} else { await format({ filenames, lang, lspc });
printerr(`Unknown action "${action}"}`); return 0;
} else {
printerr(`Unknown action "${action}"}`);
return 1;
}
} catch (err) {
if (err instanceof Interrupt) {
return 1;
} else {
throw err;
}
} }
return success ? 0 : 1;
} }
const application = new Adw.Application(); const application = new Adw.Application();
@ -89,7 +103,15 @@ const window = new Adw.ApplicationWindow();
function createLSPClients({ root_uri }) { function createLSPClients({ root_uri }) {
return Object.fromEntries( return Object.fromEntries(
["javascript", "blueprint", "css", "vala", "rust", "python"].map((id) => { [
"javascript",
"blueprint",
"css",
"vala",
"rust",
"python",
"typescript",
].map((id) => {
const lang = languages.find((language) => language.id === id); const lang = languages.find((language) => language.id === id);
const lspc = createLSPClient({ const lspc = createLSPClient({
lang, lang,
@ -101,31 +123,20 @@ function createLSPClients({ root_uri }) {
); );
} }
async function checkFile({ lspc, file, lang, uri }) { async function ci({ filenames }) {
const [contents] = await file.load_contents_async(null);
const text = new TextDecoder().decode(contents);
const buffer = new Gtk.TextBuffer({ text });
const buffer_tmp = new Gtk.TextBuffer({ text: buffer.text });
await formatting({ buffer: buffer_tmp, uri, lang, lspc });
if (buffer_tmp.text === buffer.text) {
print(` ✅ checks`);
return true;
} else {
printerr(
` ❌ formatting differs - open and run ${file
.get_parent()
.get_basename()} with Workbench to fix`,
);
return false;
}
}
async function ci({ filenames, current_dir }) {
for (const filename of filenames) { for (const filename of filenames) {
const demo_dir = Gio.File.new_for_path(filename); const demo_dir = Gio.File.new_for_path(filename);
print(`\n📂${demo_dir.get_path()}`);
const [compatible, required_runtime_version] = isDemoCompatible(demo_dir);
if (!compatible) {
print(
` ⚠️ skipped - requires runtime version ${required_runtime_version}`,
);
continue;
}
const lsp_clients = createLSPClients({ root_uri: demo_dir.get_uri() }); const lsp_clients = createLSPClients({ root_uri: demo_dir.get_uri() });
await Promise.all( await Promise.all(
Object.entries(lsp_clients).map(([, lspc]) => { Object.entries(lsp_clients).map(([, lspc]) => {
@ -133,377 +144,64 @@ async function ci({ filenames, current_dir }) {
}), }),
); );
print(`\n📂${demo_dir.get_path()}`);
let template = null; let template = null;
const builder = new Gtk.Builder(); let builder = null;
const blueprint_object_ids = []; let blueprint_object_ids = null;
let xml = null;
const file_blueprint = demo_dir.get_child("main.blp"); const file_blueprint = demo_dir.get_child("main.blp");
if (file_blueprint.query_exists(null)) { if (file_blueprint.query_exists(null)) {
print(` ${file_blueprint.get_path()}`); ({ template, builder, blueprint_object_ids } = await blueprint({
const uri = file_blueprint.get_uri();
const languageId = "blueprint";
let version = 0;
const [contents] = await file_blueprint.load_contents_async(null);
const text = new TextDecoder().decode(contents);
await lsp_clients.blueprint._notify("textDocument/didOpen", {
textDocument: {
uri,
languageId,
version: version++,
text,
},
});
const diagnostics = await waitForDiagnostics({
uri,
lspc: lsp_clients.blueprint,
});
if (diagnostics.length > 0) {
printerr(serializeDiagnostics({ diagnostics }));
return false;
}
print(` ✅ lints`);
({ xml } = await lsp_clients.blueprint._request(
"textDocument/x-blueprint-compile",
{
textDocument: {
uri,
},
},
));
print(` ✅ compiles`);
try {
await lsp_clients.blueprint._request("x-blueprint/decompile", {
text: xml,
});
print(" ✅ decompiles");
} catch (err) {
if (!(err instanceof LSPError)) throw err;
if (
![
// https://gitlab.gnome.org/jwestman/blueprint-compiler/-/issues/128
"unsupported XML tag: <condition>",
// https://gitlab.gnome.org/jwestman/blueprint-compiler/-/issues/139
"unsupported XML tag: <items>",
].includes(err.message)
) {
throw err;
}
}
const checks = await checkFile({
lspc: lsp_clients.blueprint,
file: file_blueprint, file: file_blueprint,
lang: getLanguage("blueprint"), lspc: lsp_clients.blueprint,
uri, }));
});
if (!checks) return false;
await lsp_clients.blueprint._notify("textDocument/didClose", {
textDocument: {
uri,
},
});
const tree = parse(xml);
const template_el = tree.getChild("template");
if (template_el) {
template = tree.toString();
} else {
builder.add_from_string(xml, -1);
print(` ✅ instantiates`);
getXMLObjectIds(tree, blueprint_object_ids);
}
} }
const file_css = demo_dir.get_child("main.css"); const file_css = demo_dir.get_child("main.css");
if (file_css.query_exists(null)) { if (file_css.query_exists(null)) {
print(` ${file_css.get_path()}`); await css({ file: file_css, lspc: lsp_clients.css });
const uri = file_css.get_uri();
const languageId = "css";
let version = 0;
const [contents] = await file_css.load_contents_async(null);
const text = new TextDecoder().decode(contents);
await lsp_clients.css._notify("textDocument/didOpen", {
textDocument: {
uri,
languageId,
version: version++,
text,
},
});
const diagnostics = await waitForDiagnostics({
uri,
lspc: lsp_clients.css,
});
if (diagnostics.length > 0) {
printerr(serializeDiagnostics({ diagnostics }));
return false;
}
print(` ✅ lints`);
const checks = await checkFile({
lspc: lsp_clients.css,
file: file_css,
lang: getLanguage("css"),
uri,
});
if (!checks) return false;
await lsp_clients.css._notify("textDocument/didClose", {
textDocument: {
uri,
},
});
} }
const file_javascript = demo_dir.get_child("main.js"); const file_javascript = demo_dir.get_child("main.js");
if (file_javascript.query_exists(null)) { if (file_javascript.query_exists(null)) {
print(` ${file_javascript.get_path()}`); await javascript({
const uri = file_javascript.get_uri();
const languageId = "javascript";
let version = 0;
const [contents] = await file_javascript.load_contents_async(null);
const text = new TextDecoder().decode(contents);
await lsp_clients.javascript._notify("textDocument/didOpen", {
textDocument: {
uri,
languageId,
version: version++,
text,
},
});
const diagnostics = await waitForDiagnostics({
uri,
lspc: lsp_clients.javascript,
});
if (diagnostics.length > 0) {
printerr(serializeDiagnostics({ diagnostics }));
return false;
}
print(` ✅ lints`);
const checks = await checkFile({
lspc: lsp_clients.javascript,
file: file_javascript, file: file_javascript,
lang: getLanguage("javascript"), lspc: lsp_clients.javascript,
uri, blueprint_object_ids,
}); demo_dir,
if (!checks) return false;
const js_object_ids = getCodeObjectIds(text);
for (const object_id of js_object_ids) {
if (!blueprint_object_ids.includes(object_id)) {
print(` ❌ Reference to inexistant object id "${object_id}"`);
return false;
}
}
globalThis.workbench = {
window,
application, application,
builder, builder,
template, template,
resolve(path) { window,
return demo_dir.resolve_relative_path(path).get_uri(); });
}, }
preview() {},
};
await import(`file://${file_javascript.get_path()}`); const file_typescript = demo_dir.get_child("main.ts");
print(" ✅ runs"); if (file_typescript.query_exists(null)) {
await typescript({
await lsp_clients.javascript._notify("textDocument/didClose", { file: file_typescript,
textDocument: { lspc: lsp_clients.typescript,
uri, blueprint_object_ids,
}, demo_dir,
application,
builder,
template,
window,
}); });
} }
const file_vala = demo_dir.get_child("main.vala"); const file_vala = demo_dir.get_child("main.vala");
if (file_vala.query_exists(null)) { if (file_vala.query_exists(null)) {
print(` ${file_vala.get_path()}`); await vala({ file: file_vala, lspc: lsp_clients.vala, demo_dir });
const uri = file_vala.get_uri();
const languageId = "vala";
let version = 0;
const api_file = (
GLib.getenv("FLATPAK_ID")
? Gio.File.new_for_path(`/app/share/${GLib.getenv("FLATPAK_ID")}`)
: current_dir.resolve_relative_path("src/langs/vala")
).get_child("workbench.vala");
api_file.copy(
demo_dir.get_child("workbench.vala"),
Gio.FileCopyFlags.OVERWRITE,
null,
null,
);
const [contents] = await file_vala.load_contents_async(null);
const text = new TextDecoder().decode(contents);
await lsp_clients.vala._notify("textDocument/didOpen", {
textDocument: {
uri,
languageId,
version: version++,
text,
},
});
let diagnostics = await waitForDiagnostics({
uri,
lspc: lsp_clients.vala,
});
// FIXME: deprecated features, no replacement?
if (demo_dir.get_basename() === "Text Fields") {
const ignore_for_text_fields = [
"`Gtk.EntryCompletion' has been deprecated since 4.10",
"`Gtk.Entry.completion' has been deprecated since 4.10",
"`Gtk.ListStore' has been deprecated since 4.10",
"`Gtk.TreeIter' has been deprecated since 4.10",
];
diagnostics = diagnostics.filter((diagnostic) => {
return !ignore_for_text_fields.includes(diagnostic.message);
});
}
if (diagnostics.length > 0) {
printerr(serializeDiagnostics({ diagnostics }));
return false;
}
print(` ✅ lints`);
const checks = await checkFile({
lspc: lsp_clients.vala,
file: file_vala,
lang: getLanguage("vala"),
uri,
});
if (!checks) return false;
await lsp_clients.vala._notify("textDocument/didClose", {
textDocument: {
uri,
},
});
} }
const file_python = demo_dir.get_child("main.py"); const file_python = demo_dir.get_child("main.py");
if (file_python.query_exists(null)) { if (file_python.query_exists(null)) {
print(` ${file_python.get_path()}`); await python({ file: file_python, lspc: lsp_clients.python });
const uri = file_python.get_uri();
const languageId = "python";
let version = 0;
const [contents] = await file_python.load_contents_async(null);
const text = new TextDecoder().decode(contents);
await lsp_clients.python._request("workspace/didChangeConfiguration", {
settings: PYTHON_LSP_CONFIG,
});
await lsp_clients.python._notify("textDocument/didOpen", {
textDocument: {
uri,
languageId,
version: version++,
text,
},
});
const diagnostics = await waitForDiagnostics({
uri,
lspc: lsp_clients.python,
});
if (diagnostics.length > 0) {
printerr(serializeDiagnostics({ diagnostics }));
return false;
}
print(` ✅ lints`);
const checks = await checkFile({
lspc: lsp_clients.python,
file: file_python,
lang: getLanguage("python"),
uri,
});
if (!checks) return false;
await lsp_clients.python._notify("textDocument/didClose", {
textDocument: {
uri,
},
});
} }
const file_rust = demo_dir.get_child("code.rs"); const file_rust = demo_dir.get_child("code.rs");
if (file_rust.query_exists(null)) { if (file_rust.query_exists(null)) {
print(` ${file_rust.get_path()}`); await rust({ file: file_rust, lspc: lsp_clients.rust });
const uri = file_rust.get_uri();
const languageId = "rust";
let version = 0;
const [contents] = await file_rust.load_contents_async(null);
const text = new TextDecoder().decode(contents);
await lsp_clients.rust._notify("textDocument/didOpen", {
textDocument: {
uri,
languageId,
version: version++,
text,
},
});
// FIXME: rust analyzer doesn't publish diagnostics if there are none
// probably we should switch to pulling diagnostics but unknown if supported
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_pullDiagnostics
// const diagnostics = await waitForDiagnostics({
// uri,
// lspc: lsp_clients.rust,
// });
// if (diagnostics.length > 0) {
// printerr(serializeDiagnostics({ diagnostics }));
// return false;
// }
// print(` ✅ lints`);
const checks = await checkFile({
lspc: lsp_clients.rust,
file: file_rust,
lang: getLanguage("rust"),
uri,
});
if (!checks) return false;
await lsp_clients.rust._notify("textDocument/didClose", {
textDocument: {
uri,
},
});
} }
await Promise.all( await Promise.all(
@ -512,43 +210,38 @@ async function ci({ filenames, current_dir }) {
}), }),
); );
} }
return true;
} }
function getXMLObjectIds(tree, object_ids) { const key_file = new GLib.KeyFile();
for (const object of tree.getChildren("object")) { key_file.load_from_file("/.flatpak-info", GLib.KeyFileFlags.NONE);
if (object.attrs.id) object_ids.push(object.attrs.id); // runtime/org.gnome.Sdk/x86_64/master
// <child> or <property name="child"> const [, , , runtime_version] = key_file
for (const child of object.getChildElements()) { .get_string("Application", "runtime")
getXMLObjectIds(child, object_ids); .split("/");
}
function isDemoCompatible(file) {
let str;
try {
str = new TextDecoder().decode(
file.get_child("main.json").load_contents(null)[1],
);
} catch (err) {
console.warn(err);
return true;
} }
}
function getCodeObjectIds(text) { const demo = JSON.parse(str);
const object_ids = []; demo.name = file.get_basename();
for (const match of text.matchAll(/get_object\("(.+)"\)/g)) {
object_ids.push(match[1]); const demo_runtime_version = demo["runtime-version"];
if (demo_runtime_version === "master") {
return [runtime_version === "master", demo_runtime_version];
} else if (runtime_version === "master") {
return [true, demo_runtime_version];
} else if (!demo_runtime_version) {
return [true, demo_runtime_version];
} }
return object_ids;
}
function serializeDiagnostics({ diagnostics }) { return [+runtime_version >= +demo_runtime_version, demo_runtime_version];
return (
diagnostics
.map(({ severity, range, message }) => {
return (
" ❌ " +
diagnostic_severities[severity] +
" " +
range.start.line +
":" +
range.start.character +
" " +
message.split("\n")[0]
);
})
.join("\n") + "\n"
);
} }

View File

@ -3,7 +3,7 @@ bin_conf.set('GJS', find_program('gjs').full_path())
bin_conf.set('version', meson.project_version() + version_suffix) bin_conf.set('version', meson.project_version() + version_suffix)
bin_conf.set('app_id', app_id) bin_conf.set('app_id', app_id)
bin_conf.set('prefix', prefix) bin_conf.set('prefix', prefix)
bin_conf.set('libdir', join_paths(get_option('prefix'), get_option('libdir'))) bin_conf.set('libdir', get_option('prefix') / get_option('libdir'))
bin_conf.set('datadir', datadir) bin_conf.set('datadir', datadir)
bin_conf.set('pkgdatadir', pkgdatadir) bin_conf.set('pkgdatadir', pkgdatadir)
bin_conf.set('sourcedir', meson.project_source_root()) bin_conf.set('sourcedir', meson.project_source_root())

30
src/cli/python.js Normal file
View File

@ -0,0 +1,30 @@
/* eslint-disable no-restricted-globals */
import { PYTHON_LSP_CONFIG, getLanguage } from "../common.js";
import { checkFile, diagnose } from "./util.js";
const languageId = "python";
export default async function python({ file, lspc }) {
print(` ${file.get_path()}`);
await lspc._request("workspace/didChangeConfiguration", {
settings: PYTHON_LSP_CONFIG,
});
await diagnose({ file, lspc, languageId });
await checkFile({
lspc,
file,
lang: getLanguage(languageId),
uri: file.get_uri(),
});
await lspc._notify("textDocument/didClose", {
textDocument: {
uri: file.get_uri(),
},
});
}

48
src/cli/rust.js Normal file
View File

@ -0,0 +1,48 @@
/* eslint-disable no-restricted-globals */
import { getLanguage } from "../common.js";
import { checkFile } from "./util.js";
const languageId = "rust";
export default async function rust({ file, lspc }) {
print(` ${file.get_path()}`);
const uri = file.get_uri();
let version = 0;
const [contents] = await file.load_contents_async(null);
const text = new TextDecoder().decode(contents);
await lspc._notify("textDocument/didOpen", {
textDocument: {
uri,
languageId,
version: version++,
text,
},
});
// FIXME: rust analyzer doesn't publish diagnostics if there are none
// probably we should switch to pulling diagnostics but unknown if supported
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_pullDiagnostics
// await diagnose({
// file,
// lspc,
// languageId,
// });
await checkFile({
lspc,
file,
lang: getLanguage(languageId),
uri,
});
await lspc._notify("textDocument/didClose", {
textDocument: {
uri,
},
});
}

56
src/cli/typescript.js Normal file
View File

@ -0,0 +1,56 @@
/* eslint-disable no-restricted-globals */
import { getLanguage } from "../common.js";
import { checkFile, getCodeObjectIds, diagnose, Interrupt } from "./util.js";
const languageId = "typescript";
export default async function typescript({
file,
lspc,
blueprint_object_ids,
demo_dir,
application,
builder,
template,
window,
}) {
print(` ${file.get_path()}`);
const text = await diagnose({ file, lspc, languageId });
await checkFile({
lspc,
file,
lang: getLanguage(languageId),
uri: file.get_uri(),
});
const js_object_ids = getCodeObjectIds(text);
for (const object_id of js_object_ids) {
if (!blueprint_object_ids.includes(object_id)) {
print(` ❌ Reference to inexistant object id "${object_id}"`);
throw new Interrupt();
}
}
globalThis.workbench = {
window,
application,
builder,
template,
resolve(path) {
return demo_dir.resolve_relative_path(path).get_uri();
},
preview() {},
};
await import(`file://${file.get_path()}`);
print(" ✅ runs");
await lspc._notify("textDocument/didClose", {
textDocument: {
uri: file.get_uri(),
},
});
}

111
src/cli/util.js Normal file
View File

@ -0,0 +1,111 @@
/* eslint-disable no-restricted-globals */
import Gtk from "gi://Gtk";
import { diagnostic_severities } from "../lsp/LSP.js";
import { formatting } from "./format.js";
export class Interrupt extends Error {
constructor(...args) {
super(...args);
Error.captureStackTrace?.(this, Interrupt);
}
}
export async function diagnose({
file,
lspc,
languageId,
filter = (_diagnostic) => {
return true;
},
}) {
const [contents] = await file.load_contents_async(null);
const text = new TextDecoder().decode(contents);
const uri = file.get_uri();
let version = 0;
await lspc._notify("textDocument/didOpen", {
textDocument: {
uri,
languageId,
version: version++,
text,
},
});
let diagnostics = await waitForDiagnostics({
uri,
lspc,
});
diagnostics = diagnostics.filter(filter);
if (diagnostics.length > 0) {
printerr(serializeDiagnostics({ diagnostics }));
throw new Interrupt();
}
print(` ✅ lints`);
return text;
}
export function serializeDiagnostics({ diagnostics }) {
return (
diagnostics
.map(({ severity, range, message }) => {
return (
" ❌ " +
diagnostic_severities[severity] +
" " +
range.start.line +
":" +
range.start.character +
" " +
message.split("\n")[0]
);
})
.join("\n") + "\n"
);
}
export async function checkFile({ lspc, file, lang, uri }) {
const [contents] = await file.load_contents_async(null);
const text = new TextDecoder().decode(contents);
const buffer = new Gtk.TextBuffer({ text });
const buffer_tmp = new Gtk.TextBuffer({ text: buffer.text });
await formatting({ buffer: buffer_tmp, uri, lang, lspc });
if (buffer_tmp.text === buffer.text) {
print(` ✅ checks`);
} else {
printerr(
` ❌ formatting differs - open and run ${file
.get_parent()
.get_basename()} with Workbench to fix`,
);
throw new Interrupt();
}
}
export function getCodeObjectIds(text) {
const object_ids = [];
for (const match of text.matchAll(/get_object\("(.+)"\)/g)) {
object_ids.push(match[1]);
}
return object_ids;
}
export function waitForDiagnostics({ uri, lspc }) {
return new Promise((resolve) => {
const handler_id = lspc.connect(
"notification::textDocument/publishDiagnostics",
(_self, params) => {
if (uri !== params.uri) return;
lspc.disconnect(handler_id);
resolve(params.diagnostics);
},
);
});
}

62
src/cli/vala.js Normal file
View File

@ -0,0 +1,62 @@
/* eslint-disable no-restricted-globals */
import Gio from "gi://Gio";
import { getLanguage } from "../common.js";
import { checkFile, diagnose } from "./util.js";
const languageId = "vala";
export default async function vala({ file, lspc, demo_dir }) {
print(` ${file.get_path()}`);
const file_api = Gio.File.new_for_path(pkg.pkgdatadir).get_child(
"workbench.vala",
);
file_api.copy(
demo_dir.get_child("workbench.vala"),
Gio.FileCopyFlags.OVERWRITE,
null,
null,
);
await diagnose({
file,
lspc,
languageId,
filter(diagnostic) {
// FIXME: deprecated features, no replacement?
if (demo_dir.get_basename() === "Text Fields") {
const ignore_for_text_fields = [
"`Gtk.EntryCompletion' has been deprecated since 4.10",
"`Gtk.Entry.completion' has been deprecated since 4.10",
"`Gtk.ListStore' has been deprecated since 4.10",
"`Gtk.TreeIter' has been deprecated since 4.10",
];
return !ignore_for_text_fields.includes(diagnostic.message);
// Gtk.StyleContext class is deprecated but not the following methods
// gtk_style_context_add_provider_for_display
// gtk_style_context_remove_provider_for_display
} else if (demo_dir.get_basename() === "CSS Gradients") {
return (
diagnostic.message !==
"`Gtk.StyleContext' has been deprecated since 4.10"
);
}
return true;
},
});
await checkFile({
lspc,
file,
lang: getLanguage("vala"),
uri: file.get_uri(),
});
await lspc._notify("textDocument/didClose", {
textDocument: {
uri: file.get_uri(),
},
});
}

View File

@ -1,5 +1,3 @@
import GLib from "gi://GLib";
import LSPClient from "./lsp/LSPClient.js"; import LSPClient from "./lsp/LSPClient.js";
const formatting_options = { const formatting_options = {
@ -19,6 +17,10 @@ export const languages = [
types: [], types: [],
document: null, document: null,
default_file: "main.blp", default_file: "main.blp",
// language_server: [
// "/home/sonny/Projects/GNOME/blueprint-compiler/blueprint-compiler.py",
// "lsp",
// ],
language_server: ["blueprint-compiler", "lsp"], language_server: ["blueprint-compiler", "lsp"],
formatting_options: { formatting_options: {
...formatting_options, ...formatting_options,
@ -43,13 +45,12 @@ export const languages = [
document: null, document: null,
default_file: "main.js", default_file: "main.js",
index: 0, index: 0,
// language_server: ["typescript-language-server", "--stdio"],
language_server: [ language_server: [
"biome", "biome",
"lsp-proxy", "lsp-proxy",
// src/meson.build installs biome.json there // src/meson.build installs biome.json there
GLib.getenv("FLATPAK_ID") `--config-path=${pkg.pkgdatadir}`,
? `--config-path=${pkg.pkgdatadir}`
: `--config-path=src/langs/javascript`,
], ],
formatting_options: { formatting_options: {
...formatting_options, ...formatting_options,
@ -115,6 +116,21 @@ export const languages = [
tabSize: 4, tabSize: 4,
}, },
}, },
{
id: "typescript",
name: "TypeScript",
panel: "code",
extensions: [".ts", ".mts"],
types: [],
document: null,
default_file: "main.ts",
index: 4,
language_server: ["typescript-language-server", "--stdio"],
formatting_options: {
...formatting_options,
tabSize: 2,
},
},
]; ];
export function getLanguage(id) { export function getLanguage(id) {
@ -131,19 +147,30 @@ export function createLSPClient({ lang, root_uri, quiet = true }) {
languageId: language_id, languageId: language_id,
quiet, quiet,
}); });
lspc.connect("exit", () => {
console.debug(`${language_id} language server exit`); if (quiet === false) {
}); lspc.connect("exit", () => {
lspc.connect("output", (_self, message) => { console.log(`${language_id} language server exit`);
console.debug( });
`${language_id} language server OUT:\n${JSON.stringify(message)}`, lspc.connect("output", (_self, message) => {
); console.log(
}); `${language_id} language server OUT:\n${JSON.stringify(
lspc.connect("input", (_self, message) => { message,
console.debug( null,
`${language_id} language server IN:\n${JSON.stringify(message)}`, 2,
); )}`,
}); );
});
lspc.connect("input", (_self, message) => {
console.log(
`${language_id} language server IN:\n${JSON.stringify(
message,
null,
2,
)}`,
);
});
}
return lspc; return lspc;
} }

36
src/flatpak.js Normal file
View File

@ -0,0 +1,36 @@
import GLib from "gi://GLib";
let flatpak_info;
export function getFlatpakInfo() {
if (flatpak_info) return flatpak_info;
flatpak_info = new GLib.KeyFile();
try {
flatpak_info.load_from_file("/.flatpak-info", GLib.KeyFileFlags.NONE);
} catch (err) {
if (!err.matches(GLib.FileError, GLib.FileError.NOENT)) {
console.error(err);
}
return null;
}
return flatpak_info;
}
export function getFlatpakId() {
return getFlatpakInfo().get_string("Application", "name");
}
// https://repology.org/project/flatpak/versions
export function isDeviceInputOverrideAvailable(flatpak_version) {
flatpak_version ??= getFlatpakInfo().get_string(
"Instance",
"flatpak-version",
);
// https://github.com/flatpak/flatpak/releases/tag/1.15.6
return (
flatpak_version.localeCompare("1.15.6", undefined, {
numeric: true,
sensitivity: "base",
}) > -1
);
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 2.292969 6.707031 l 5 5 c 0.390625 0.390625 1.023437 0.390625 1.414062 0 l 5 -5 c 0.390625 -0.390625 0.390625 -1.023437 0 -1.414062 s -1.023437 -0.390625 -1.414062 0 l -4.292969 4.292969 l -4.292969 -4.292969 c -0.390625 -0.390625 -1.023437 -0.390625 -1.414062 0 s -0.390625 1.023437 0 1.414062 z m 0 0" fill="#222222" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 484 B

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222"><path d="m 13.949219 8.359375 s 0 0.019531 0.003906 0.011719 l -5.003906 4.914062 l 0.699219 -0.285156 h -4.621094 l 0.707031 0.292969 l -3.683594 -3.683594 v -0.019531 l 6.554688 -6.554688 h 0.019531 l -0.292969 -0.707031 c 0 0.265625 0.105469 0.519531 0.292969 0.707031 z m -3.617188 -6.03125 c 0 -0.265625 -0.105469 -0.519531 -0.292969 -0.707031 c -0.789062 -0.789063 -2.058593 -0.789063 -2.847656 0 l -6.554687 6.554687 c -0.789063 0.789063 -0.789063 2.058594 0 2.847657 l 3.683593 3.683593 c 0.1875 0.1875 0.441407 0.292969 0.707032 0.292969 h 4.621094 c 0.261718 0 0.511718 -0.101562 0.699218 -0.285156 l 5.015625 -4.921875 c 0.789063 -0.789063 0.789063 -2.058594 0 -2.847657 l -5.324219 -5.324218 z m 0 0"/><path d="m 4.277344 7.007812 l 6.015625 5.992188 c 0.195312 0.195312 0.511719 0.195312 0.707031 0 s 0.195312 -0.511719 0 -0.707031 l -6.015625 -5.996094 c -0.195313 -0.195313 -0.511719 -0.195313 -0.707031 0.003906 c -0.195313 0.195313 -0.195313 0.511719 0 0.707031 z m 0 0"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 3 2 c -1.660156 0 -3 1.339844 -3 3 v 8 c 0 1.660156 1.339844 3 3 3 h 8 c 1.660156 0 3 -1.339844 3 -3 v -4 c 0 -0.550781 -0.449219 -1 -1 -1 s -1 0.449219 -1 1 v 4 c 0 0.554688 -0.445312 1 -1 1 h -8 c -0.554688 0 -1 -0.445312 -1 -1 v -8 c 0 -0.554688 0.445312 -1 1 -1 h 4 c 0.550781 0 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 z m 7 -2 c -0.550781 0 -1 0.449219 -1 1 s 0.449219 1 1 1 h 2.585938 l -5.292969 5.289062 c -0.390625 0.394532 -0.390625 1.027344 0 1.417969 s 1.023437 0.390625 1.414062 0 l 5.292969 -5.292969 v 2.585938 c 0 0.550781 0.449219 1 1 1 s 1 -0.449219 1 -1 v -5 c 0 -0.085938 -0.011719 -0.171875 -0.035156 -0.257812 c -0.023438 -0.085938 -0.054688 -0.167969 -0.101563 -0.242188 c -0.042969 -0.074219 -0.09375 -0.144531 -0.15625 -0.207031 c -0.015625 -0.011719 -0.03125 -0.023438 -0.046875 -0.035157 c -0.054687 -0.050781 -0.117187 -0.09375 -0.183594 -0.128906 c -0.035156 -0.019531 -0.074218 -0.035156 -0.113281 -0.0468748 c -0.050781 -0.0234374 -0.101562 -0.0390624 -0.15625 -0.0507812 c -0.039062 -0.0117188 -0.082031 -0.015625 -0.121093 -0.0195312 c -0.03125 -0.00781255 -0.058594 -0.00781255 -0.085938 -0.0117188 z m 0 0" fill="#222222"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 4 2 c -1.597656 0 -2.644531 0.664062 -3 2 l -0.738281 8.324219 c -0.261719 1.59375 1.738281 2.175781 2.738281 1.175781 l 2.75 -4.5 h 4.5 l 2.75 4.5 c 1 1 3 0.527344 2.738281 -1.175781 l -0.738281 -8.324219 c -0.214844 -1.203125 -1.402344 -2.03125 -2.972656 -2.027344 z m 0.25 2 h 0.5 c 0.136719 0 0.25 0.113281 0.25 0.25 v 0.75 h 0.75 c 0.136719 0 0.25 0.113281 0.25 0.25 v 0.5 c 0 0.136719 -0.113281 0.25 -0.25 0.25 h -0.75 v 0.75 c 0 0.136719 -0.113281 0.25 -0.25 0.25 h -0.5 c -0.136719 0 -0.25 -0.113281 -0.25 -0.25 v -0.75 h -0.75 c -0.136719 0 -0.25 -0.113281 -0.25 -0.25 v -0.5 c 0 -0.136719 0.113281 -0.25 0.25 -0.25 h 0.75 v -0.75 c 0 -0.136719 0.113281 -0.25 0.25 -0.25 z m 5.765625 -0.113281 c 0.59375 0 1.078125 0.484375 1.078125 1.082031 s -0.484375 1.082031 -1.078125 1.082031 c -0.597656 0 -1.082031 -0.484375 -1.082031 -1.082031 s 0.484375 -1.082031 1.082031 -1.082031 z m 2.96875 2.019531 c 0.597656 0 1.082031 0.484375 1.082031 1.078125 c 0 0.597656 -0.484375 1.082031 -1.082031 1.082031 c -0.59375 0 -1.078125 -0.484375 -1.078125 -1.082031 c 0 -0.59375 0.484375 -1.078125 1.078125 -1.078125 z m 0 0" fill="#222222"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222"><path d="m 7.996094 0 c -1.109375 0 -2 0.890625 -2 2 v 3.003906 c 0 0.632813 -0.480469 0.996094 -1 0.996094 h -1 c -1.109375 0 -2 0.890625 -2 2 v 1 h 12 v -1 c 0 -1.109375 -0.894532 -2 -2 -2 h -0.976563 c -0.53125 0 -1.023437 -0.359375 -1.023437 -1 v -3 c 0 -1.109375 -0.894532 -2 -2 -2 z m 0 1.40625 c 0.324218 0 0.59375 0.265625 0.59375 0.59375 s -0.269532 0.59375 -0.59375 0.59375 c -0.328125 0 -0.59375 -0.265625 -0.59375 -0.59375 s 0.265625 -0.59375 0.59375 -0.59375 z m 0 0"/><path d="m 2 10 h 12 l -0.800781 5.003906 c -0.199219 0.820313 -0.699219 0.996094 -1.246094 0.996094 h -1.953125 v -3 l -1.125 3 h -1.847656 l -0.027344 -3 l -1.035156 3 h -0.964844 v -1.5 l -1 1.5 h -3 l 0.210938 -1.046875 z m 0 0"/></g></svg>

After

Width:  |  Height:  |  Size: 871 B

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222"><path d="m 1.5 2 h 2 c 0.277344 0 0.5 0.222656 0.5 0.5 v 12 c 0 0.277344 -0.222656 0.5 -0.5 0.5 h -2 c -0.277344 0 -0.5 -0.222656 -0.5 -0.5 v -12 c 0 -0.277344 0.222656 -0.5 0.5 -0.5 z m 0 0"/><path d="m 5.5 4 h 1 c 0.277344 0 0.5 0.222656 0.5 0.5 v 10 c 0 0.277344 -0.222656 0.5 -0.5 0.5 h -1 c -0.277344 0 -0.5 -0.222656 -0.5 -0.5 v -10 c 0 -0.277344 0.222656 -0.5 0.5 -0.5 z m 0 0"/><path d="m 8.5 3 h 1 c 0.277344 0 0.5 0.222656 0.5 0.5 v 11 c 0 0.277344 -0.222656 0.5 -0.5 0.5 h -1 c -0.277344 0 -0.5 -0.222656 -0.5 -0.5 v -11 c 0 -0.277344 0.222656 -0.5 0.5 -0.5 z m 0 0"/><path d="m 10.707031 1.460938 l 0.964844 -0.261719 c 0.265625 -0.070313 0.539063 0.089843 0.613281 0.355469 l 3.363282 12.558593 c 0.070312 0.265625 -0.085938 0.539063 -0.351563 0.609375 l -0.96875 0.261719 c -0.265625 0.070313 -0.539063 -0.089844 -0.613281 -0.355469 l -3.363282 -12.554687 c -0.070312 -0.269531 0.085938 -0.542969 0.355469 -0.613281 z m 0 0"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222"><path d="m 2.035156 7.007812 c -0.53125 0 -1.03125 0.421876 -1.03125 1 v 7.011719 h 8.011719 v -3.984375 h -2 v 1.984375 h -4.011719 v -2.996093 h 2.015625 v -3.015626 z m 0 0"/><path d="m 8.019531 1.007812 c -0.53125 0 -1.03125 0.421876 -1.03125 1 v 7.011719 h 8.011719 v -7.042969 c 0 -0.578124 -0.519531 -0.96875 -1.019531 -0.96875 z m 0.96875 3.015626 h 4.011719 v 2.996093 h -4.011719 z m 0 0"/></g></svg>

After

Width:  |  Height:  |  Size: 555 B

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
<path d="m 8 1.992188 c -2.617188 0 -5.238281 0.933593 -7.195312 2.808593 l -0.496094 0.480469 c -0.3984378 0.378906 -0.410156 1.011719 -0.03125 1.410156 c 0.382812 0.398438 1.015625 0.410156 1.414062 0.03125 l 0.5 -0.476562 c 3.085938 -2.957032 8.53125 -2.957032 11.617188 0 l 0.5 0.476562 c 0.398437 0.378906 1.03125 0.367188 1.414062 -0.03125 c 0.378906 -0.398437 0.367188 -1.03125 -0.03125 -1.410156 l -0.496094 -0.484375 c -1.957031 -1.871094 -4.578124 -2.804687 -7.195312 -2.804687 z m -0.03125 4.007812 c -1.570312 0.011719 -3.128906 0.628906 -4.207031 1.8125 l -0.5 0.550781 c -0.179688 0.195313 -0.277344 0.453125 -0.261719 0.71875 c 0.011719 0.265625 0.128906 0.515625 0.328125 0.695313 c 0.195313 0.179687 0.453125 0.273437 0.71875 0.257812 c 0.265625 -0.011718 0.515625 -0.128906 0.695313 -0.328125 l 0.496093 -0.546875 c 1.277344 -1.402344 4.160157 -1.496094 5.523438 0.003906 l 0.5 0.542969 c 0.175781 0.199219 0.425781 0.316407 0.691406 0.328125 c 0.265625 0.015625 0.523437 -0.078125 0.722656 -0.257812 c 0.195313 -0.179688 0.3125 -0.429688 0.324219 -0.695313 c 0.011719 -0.261719 -0.082031 -0.523437 -0.261719 -0.71875 l -0.5 -0.546875 c -1.121093 -1.234375 -2.703125 -1.828125 -4.269531 -1.816406 z m 0.03125 4 c -0.511719 0 -1.023438 0.195312 -1.414062 0.585938 c -0.78125 0.78125 -0.78125 2.046874 0 2.828124 s 2.046874 0.78125 2.828124 0 s 0.78125 -2.046874 0 -2.828124 c -0.390624 -0.390626 -0.902343 -0.585938 -1.414062 -0.585938 z m 0 0" fill="#2e3436"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 8 1 c -1.65625 0 -3 1.34375 -3 3 s 1.34375 3 3 3 s 3 -1.34375 3 -3 s -1.34375 -3 -3 -3 z m -1.5 7 c -2.492188 0 -4.5 2.007812 -4.5 4.5 v 1.5 c 0 1 1 1 1 1 h 10 s 1 0 1 -1 v -1.5 c 0 -2.492188 -2.007812 -4.5 -4.5 -4.5 z m 0 0" fill="#222222"/></svg>

After

Width:  |  Height:  |  Size: 386 B

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 12.039062 0.00390625 c -0.257812 -0.01171875 -0.523437 0.07421875 -0.726562 0.28124975 l -3.3125 3.292969 v 1.421875 h 1.390625 l 3.304687 -3.296875 c 0.40625 -0.40625 0.363282 -1.042969 0.03125 -1.394531 c -0.175781 -0.183594 -0.429687 -0.292969 -0.6875 -0.30468775 z m -5.039062 1.00390575 c -0.296875 -0.003906 -0.578125 0.125 -0.765625 0.351563 l -3.234375 3.640625 h -1 c -1.09375 0 -2 0.84375 -2 2 v 2 c 0 1.089844 0.910156 2 2 2 h 1 l 3.234375 3.640625 c 0.207031 0.253906 0.488281 0.363281 0.765625 0.359375 z m 1 5.992188 v 2 h 6 c 0.75 0 1 -0.5 1 -1 s -0.25 -1 -1 -1 z m 0 4 v 1.421875 l 3.324219 3.292969 c 0.402343 0.410156 1.0625 0.347656 1.414062 -0.023438 c 0.332031 -0.351562 0.371094 -0.988281 -0.03125 -1.390625 l -3.316406 -3.300781 z m 0 0" fill="#222222"/></svg>

After

Width:  |  Height:  |  Size: 921 B

Some files were not shown because too many files have changed in this diff Show More