Compare commits

...

494 commits
v8 ... main

Author SHA1 Message Date
Exidex
ca066da5c9
Update README.md
Some checks failed
format / rust (push) Failing after 1s
format / nix (push) Failing after 1s
nix build / all (push) Failing after 3s
build / build-linux (push) Has been cancelled
build / build-macos (push) Has been cancelled
build / build-windows (push) Has been cancelled
2025-10-02 18:55:18 +02:00
Exidex
ce76f8211c
GitHub Actions workflow to bump Gauntlet version in WinGet
Some checks failed
format / rust (push) Failing after 3s
format / nix (push) Failing after 1s
nix build / all (push) Failing after 3s
build / build-linux (push) Has been cancelled
build / build-macos (push) Has been cancelled
build / build-windows (push) Has been cancelled
2025-08-24 18:21:19 +02:00
Exidex
e62a89f45e Prepare for v21 release
Some checks failed
format / rust (push) Failing after 3s
format / nix (push) Failing after 1s
nix build / all (push) Failing after 4s
build / build-linux (push) Has been cancelled
build / build-macos (push) Has been cancelled
build / build-windows (push) Has been cancelled
2025-08-16 15:39:58 +00:00
Exidex
1e8bd2eca9
Update CHANGELOG.md 2025-08-16 17:37:07 +02:00
Exidex
de5166ccae
Remove padding from content paragraph text
Some checks failed
build / build-linux (push) Waiting to run
build / build-macos (push) Waiting to run
build / build-windows (push) Waiting to run
format / rust (push) Failing after 3s
format / nix (push) Failing after 1s
nix build / all (push) Failing after 3s
2025-08-15 18:23:24 +02:00
Exidex
1116190f7d
Use lucide icons for shortcuts widget 2025-08-15 18:13:53 +02:00
Exidex
3516d35a35
Fix windows build 2025-08-15 17:24:49 +02:00
Exidex
a577202ec9
Implement native hud notifications on Linux, enable by default 2025-08-15 17:00:24 +02:00
Exidex
b008fcd907
Focus second item in Opened windows view as an alternative window 2025-08-15 16:04:47 +02:00
Exidex
1a2368ffde
Fix Opened Windows view often not showing any windows 2025-08-15 16:00:06 +02:00
Exidex
24405c92bc
Extend focusedItemId description
Some checks failed
format / rust (push) Failing after 2s
format / nix (push) Failing after 1s
nix build / all (push) Failing after 4s
build / build-linux (push) Has been cancelled
build / build-macos (push) Has been cancelled
build / build-windows (push) Has been cancelled
2025-08-10 21:30:20 +02:00
Exidex
204de4d88c
Add ability to programmatically control focus on list and grid 2025-08-10 21:24:08 +02:00
Exidex
4721a70111
Restrict js heap memory size to 50 mb per plugin
Some checks failed
build / build-linux (push) Waiting to run
build / build-macos (push) Waiting to run
build / build-windows (push) Waiting to run
format / rust (push) Failing after 3s
format / nix (push) Failing after 4s
nix build / all (push) Failing after 5s
2025-08-10 13:13:08 +02:00
Exidex
204e4182c9
Make text smaller in a main view search results, plugin view, action panel and bottom panel right side 2025-08-10 11:39:55 +02:00
Exidex
62a16d04e0
Fix hud view being shown in a main window sometimes
Some checks failed
build / build-linux (push) Waiting to run
build / build-macos (push) Waiting to run
build / build-windows (push) Waiting to run
format / rust (push) Failing after 2s
format / nix (push) Failing after 2s
nix build / all (push) Failing after 4s
2025-08-10 10:50:29 +02:00
Exidex
41f0fb460a
Fix action execution from inline view not being executed after some refactor
Some checks failed
build / build-linux (push) Waiting to run
build / build-macos (push) Waiting to run
build / build-windows (push) Waiting to run
format / rust (push) Failing after 3s
format / nix (push) Failing after 2s
nix build / all (push) Failing after 3s
2025-08-09 19:23:29 +02:00
Exidex
e7c1f0343e
Rework viewport scrolling when using keyboard navigation
Some checks failed
build / build-linux (push) Waiting to run
build / build-macos (push) Waiting to run
build / build-windows (push) Waiting to run
format / rust (push) Failing after 4s
format / nix (push) Failing after 2s
nix build / all (push) Failing after 4s
2025-08-08 22:02:41 +02:00
Exidex
32bf43438c
Pin windows version to 2022 in github actions
Some checks failed
format / rust (push) Failing after 2s
format / nix (push) Failing after 1s
nix build / all (push) Failing after 3s
build / build-linux (push) Has been cancelled
build / build-macos (push) Has been cancelled
build / build-windows (push) Has been cancelled
2025-08-02 15:40:39 +02:00
Exidex
0528b96fae
Fix back navigation being treated as whole separate view open session
Some checks failed
format / rust (push) Failing after 3s
format / nix (push) Failing after 2s
nix build / all (push) Failing after 4s
build / build-linux (push) Has been cancelled
build / build-macos (push) Has been cancelled
build / build-windows (push) Has been cancelled
2025-07-31 21:25:57 +02:00
Exidex
39bf811b11
Fix main window disappearing and reappearing when opening a plugin view 2025-07-31 20:18:56 +02:00
Exidex
f82841aad4
Refactor react view container
Some checks failed
build / build-linux (push) Waiting to run
build / build-macos (push) Waiting to run
build / build-windows (push) Waiting to run
format / rust (push) Failing after 3s
format / nix (push) Failing after 1s
nix build / all (push) Failing after 3s
2025-07-30 21:11:23 +02:00
Exidex
35afc3b0cd
Update winit fork, viewport handling fix to layer shell
Some checks failed
build / build-linux (push) Has been cancelled
build / build-macos (push) Has been cancelled
build / build-windows (push) Has been cancelled
format / rust (push) Failing after 3s
format / nix (push) Failing after 3s
nix build / all (push) Failing after 3s
2025-07-27 18:23:07 +02:00
Exidex
dea6aee03a
Fix formatting 2025-07-27 17:34:08 +02:00
Exidex
3245dcee96
Focus setting window after it as created 2025-07-27 17:16:00 +02:00
Exidex
f41ab9233b
Merge settings app into the main app 2025-07-27 16:50:07 +02:00
Exidex
136b2c2ab6
Fix more bundled plugins and examples after breaking change
Some checks failed
build / build-linux (push) Has been cancelled
build / build-macos (push) Has been cancelled
build / build-windows (push) Has been cancelled
format / rust (push) Failing after 2s
format / nix (push) Failing after 2s
nix build / all (push) Failing after 3s
2025-07-25 22:11:28 +02:00
Exidex
b4340f4c10
Fix bundled plugins and examples after breaking change 2025-07-25 22:06:17 +02:00
Exidex
72123db351
Refine nullability of event arguments. Distinguish between null and undefined properties when parsing react component tree 2025-07-25 20:11:32 +02:00
Exidex
e1f2ca85ec
Update winit fork, fix fractional scaling for layershell
Some checks failed
format / rust (push) Failing after 2s
format / nix (push) Failing after 1s
nix build / all (push) Failing after 3s
build / build-linux (push) Has been cancelled
build / build-macos (push) Has been cancelled
build / build-windows (push) Has been cancelled
2025-07-21 21:32:43 +02:00
Exidex
242f5a2eea
Rewrite react component parser into procmacros, better error reporting 2025-07-21 21:16:53 +02:00
Exidex
729b4f695f
Clean up couple of things
Some checks failed
format / rust (push) Failing after 3s
format / nix (push) Failing after 2s
nix build / all (push) Failing after 3s
build / build-linux (push) Has been cancelled
build / build-macos (push) Has been cancelled
build / build-windows (push) Has been cancelled
2025-07-18 17:44:51 +02:00
Exidex
3cdd16cfb3
Fix panic if state of the widget under specific id changed type
Some checks failed
format / rust (push) Failing after 2s
format / nix (push) Failing after 1s
nix build / all (push) Failing after 3s
build / build-linux (push) Has been cancelled
build / build-macos (push) Has been cancelled
build / build-windows (push) Has been cancelled
2025-07-17 08:10:05 +02:00
Exidex
21f8ab6d09
Fix some screenshots from screenshot generator being blank
Some checks failed
format / rust (push) Failing after 2s
format / nix (push) Failing after 1s
nix build / all (push) Failing after 2s
build / build-linux (push) Has been cancelled
build / build-macos (push) Has been cancelled
build / build-windows (push) Has been cancelled
2025-07-15 19:48:02 +02:00
Exidex
b7b8413146
Fix inline view being recreated each time key is pressed in search bar, causing useRef not preserving value
Some checks failed
build / build-linux (push) Waiting to run
build / build-macos (push) Waiting to run
build / build-windows (push) Waiting to run
format / rust (push) Failing after 2s
format / nix (push) Failing after 2s
nix build / all (push) Failing after 3s
2025-07-14 22:07:09 +02:00
dennkaii
97fc6360ea
Fix nix build (#85) 2025-07-11 18:00:46 +02:00
Exidex
e643dcc4f7
Update README.md 2025-07-07 22:04:14 +02:00
Exidex
771e2f95d7 Prepare for v20 release 2025-07-07 19:01:00 +00:00
Exidex
5c2db4d048
Update CHANGELOG.md 2025-07-07 20:57:54 +02:00
Exidex
6bb5f40065
Change action panel shortcut to Ctrl/Cmd + K, to match similar pattern in other apps 2025-07-07 19:37:45 +02:00
Exidex
94c8984649
Disable global shortcuts by default on wayland, add config option to enable if legacy x11 api is supported 2025-07-07 19:26:00 +02:00
Exidex
1c23514aac
Fix formatting 2025-07-06 13:15:14 +02:00
Exidex
08c5bb2c49
Fix sys tray open main and settings windows actions causing deadlock 2025-07-06 13:13:42 +02:00
Exidex
a8287ede60
Update iced and winit fork 2025-07-06 12:02:33 +02:00
Exidex
3a457450cb
Relax layer_shell requirement on linux wayland. Add config file setting to choose between normal and layer shell window 2025-07-04 19:50:55 +02:00
Exidex
bced4daedd
Cli gauntlet open now hides window when pressed while window is open, matching behavior of global shortcut 2025-07-03 18:59:24 +02:00
Exidex
283285c317
Replace iced_layer shell with layer_shell implementation from winit fork 2025-07-01 23:24:22 +02:00
Exidex
70e815b795
Fix formatting 2025-06-24 21:00:23 +02:00
Exidex
9111dd074b
Fix build on non-linux systems 2025-06-24 20:57:43 +02:00
Exidex
d1e61d4d19
Massively simplify scenario runner 2025-06-22 20:35:31 +02:00
Exidex
f529d061ff
Fix layershell taking exclusive keyboard focus, preventing from clicking anything else 2025-06-19 20:07:49 +02:00
Exidex
6645ce21ad
Refactor server and window state of ui into separate modules 2025-06-19 19:39:33 +02:00
Exidex
a0bbad9fea
Slightly refactor window management behaviour into separate module 2025-06-19 16:24:55 +02:00
Exidex
f20537181e
Bump npm dependencies 2025-06-19 15:03:41 +02:00
Exidex
71e8adf44d
Pin ubuntu version in github actions to lower glibc requirement from 2.38 to 2.35 2025-06-19 14:32:52 +02:00
Exidex
f51a123411
Change layershell namespace from Gauntlet to gauntlet, set hud to gauntlet-hud 2025-06-19 14:28:19 +02:00
Exidex
0d7a9db4d9
Fix icons font not being loaded in main window 2025-06-19 14:21:15 +02:00
Exidex
2afecf3b80
Fix padding on grid component after dependency updates 2025-06-19 12:05:10 +02:00
Exidex
29b1a4ab36
Fix useStorage and useCache hooks crashing plugin runtime when closing the view 2025-06-19 11:48:53 +02:00
Exidex
75fa1f3af5
Bump a lot of rust dependencies 2025-06-19 11:18:51 +02:00
Exidex
e7f621018c
Move GlobalHotkeyManager out of ApplicationManger to make it Send. Fixes build on Windows 2025-06-19 11:10:02 +02:00
Gabriel Berto
3d1beaca15 fix: show full cause when printing error 2025-06-18 21:38:37 +02:00
Exidex
8fd14b4dab
Replace usages of BackendForFrontendApi with direct calls to ApplicationManager 2025-06-16 21:28:35 +02:00
Exidex
e1787e7dc8
Fix macOS build 2025-06-15 19:54:25 +02:00
Exidex
9021f0580e
Bump iced_fonts version with fix for release build 2025-06-15 19:10:13 +02:00
Exidex
a91bf3066f
Explicitly add some iced features 2025-06-15 16:21:49 +02:00
Exidex
f2fadf8b70
Update iced to latest master. Remove iced_aw, iced_table dependencies. Remove DatePicker component 2025-06-15 14:29:19 +02:00
Philippe Loctaux
924fe69069 github actions: formatting: nix with alejandra, remove verbose for rustfmt 2025-06-14 20:39:50 +02:00
Philippe Loctaux
9fe9ccdb6b added editorconfig, with yaml 2025-06-14 20:39:50 +02:00
Philippe Loctaux
bce1969610 plugins/gauntlet: applications/macos: follow symlinks inside /Applications
Symlinks are not followed by default in function `walk` in the package deno @std/fs
2025-06-12 20:33:26 +02:00
Exidex
f7d7022000
Fix build 2025-06-04 22:14:46 +02:00
Exidex
e13ff1d688
Fix scenario runner build 2025-06-04 21:53:51 +02:00
Exidex
04e0dfd44a
Update settings ui screenshot in README.md 2025-06-02 20:13:39 +02:00
Exidex
547f502030
Fix deadlock during database call while typing in main search bar 2025-06-02 20:01:36 +02:00
Exidex
d5b4c71a61
Add missing focus screenshot scenarios for documentation 2025-06-01 13:24:47 +02:00
Exidex
41107d2043
Update README.md 2025-06-01 13:10:43 +02:00
Exidex
fee4e9a18b
Fix macos build 2025-05-31 21:35:42 +02:00
Exidex
747cb9435d
Fix windows build 2025-05-31 20:31:05 +02:00
Exidex
901f8fdfa3
Explicitly add bundled rusqlite feature 2025-05-31 19:34:22 +02:00
Exidex
30963ffd73
Fix nix hash mismatch. Add nix build to github actions 2025-05-31 19:23:23 +02:00
Exidex
1706840b8b
Migrate sqlite db migrations from sqlx migrations to rusqlite migrations 2025-05-30 23:50:23 +02:00
Exidex
7e46395152
Fix deno runtime not starting 2025-05-29 19:22:13 +02:00
Exidex
5181a366e7
Update lots of dependencies in Cargo.lock 2025-05-28 20:06:16 +02:00
Exidex
c0f61f444d
WIP Migration from sqlx to rusqlite. No sql migrations yet. Doesn't compile 2025-05-27 22:44:24 +02:00
Exidex
b0fdaa6b9e
Switch edition 2024 again after revert 2025-05-25 15:00:29 +02:00
Exidex
a0275cce28
Revert plugin runtime --extern rustc flag 2025-05-25 14:36:07 +02:00
Exidex
80bb33690b
Run cargo fmt 2025-05-24 14:15:12 +02:00
Exidex
a8182f4b2e
Switch to edition 2024, resolve all cargo warnings 2025-05-24 11:01:03 +02:00
Exidex
3599837033
Split plugin runtime (specifically deno) into separate workspace, and include it via --extern rustc flag 2025-05-23 20:59:04 +02:00
Exidex
2a80b51ff5
Update package-lock.json 2025-05-22 20:34:53 +02:00
Exidex
7a47cdb941
Change screenshot generator theme to macos dark 2025-05-22 15:22:59 +02:00
Exidex
c9019ca772
Svg component plugin examples 2025-05-22 15:22:02 +02:00
Exidex
3992897fcf Prepare for v19 release 2025-05-11 13:51:01 +00:00
Exidex
624f37aaeb
Read gauntlet version from main file in nix flake 2025-05-11 15:37:21 +02:00
Exidex
f0912c8e21
Run nix flake update 2025-05-11 15:36:27 +02:00
Exidex
40fb6f2617
Implement entrypoint search aliases 2025-05-11 13:25:13 +02:00
Exidex
3064f7f297
Make text selection color distinct from the background in form view text inputs in Settings UI 2025-05-09 19:41:56 +02:00
Exidex
5a52e1a9e5
Fixed crash when closing inline view due to Action being run ... again 2025-05-09 19:37:11 +02:00
Exidex
2ddd9c8bf0
Fix error toolip of global shortcuts for entrypoint not being shown on hover 2025-05-08 21:59:59 +02:00
Exidex
4ab582ddad
Fix panic when trying to assign global shortcut that is already assigned by another application 2025-05-08 21:41:57 +02:00
Exidex
4aec25397a
Fix formatting 2025-05-06 21:19:33 +02:00
Exidex
80396abbad
Migrate communication over Grpc to new system to reduce boilerplate 2025-05-05 19:52:07 +02:00
Exidex
44d594c395
Migrate FrontendApi and BackendForFrontendApi to new system to reduce boilerplate 2025-05-04 17:33:59 +02:00
Exidex
ba64162222
Proc-macro for api boundary trait boilerplate codegen. BackendForPluginRuntimeApi migrated 2025-05-04 13:54:55 +02:00
Oscar Muhr
5b4f94cfc6
update npmDepsHash in nix overlay (#69) 2025-05-04 12:45:17 +02:00
Exidex
b6c0b780db
Fix plugin runtime panic when using assetDataSync() 2025-05-04 10:52:59 +02:00
Exidex
f204969e63
Add support for svg images 2025-05-03 19:52:05 +02:00
Exidex
59307623ca
Make text selection color distinct from the background in form view text inputs 2025-05-03 10:24:57 +02:00
Exidex
ebcee6a470
Make entrypoint.*.actions.*.shortcut optional 2025-05-03 10:09:57 +02:00
Exidex
9abbc1909d
Sort windows in opened windows view according to focus order 2025-05-02 20:49:51 +02:00
Exidex
c403a5a386
Grid and List focus plugin examples 2025-04-27 20:56:28 +02:00
Exidex
417ed8a13a
More plugin examples. Clipboard, promise helpers, environment 2025-04-27 20:12:17 +02:00
Exidex
affeccceb1
Storage and assets plugin examples 2025-04-25 20:05:26 +02:00
Exidex
ace5a874e3
Fix build 2025-04-24 21:16:05 +02:00
Exidex
1a1c862973
More plugin examples 2025-04-24 21:13:43 +02:00
Exidex
87d6b1abb8
Fix typo in plugin manifest json schema 2025-04-20 17:27:21 +02:00
Exidex
a72329aeb7
Add a bunch of plugin examples for entrypoint generators 2025-04-20 17:16:00 +02:00
Exidex
b8f07d5028
Unify Vec<u8> usage to ArrayBuffer in js. Fixes icon in EntrypointGenerator requiring number[] 2025-04-20 16:15:43 +02:00
Exidex
61eca05f42
Add contributions to CHANGELOG.md 2025-04-13 20:01:02 +02:00
Exidex
7143ed2471 Prepare for v18 release 2025-04-13 17:41:09 +00:00
Exidex
25a10d573c
Bump nix version 2025-04-13 19:36:16 +02:00
Exidex
bdda05f579
Update CHANGELOG.md 2025-04-13 19:32:35 +02:00
Exidex
4a99160427
Fix plugin view being empty if the window is closed by toggling global shortcut 2025-04-13 18:59:48 +02:00
Benno
8439f96430
fix: ensure window state resets correctly (#65) 2025-04-13 18:29:29 +02:00
Exidex
6fafe23658
Fix window sometimes not being hidden on x11 2025-04-11 18:02:10 +02:00
Exidex
02f9d366fe
Pin wix toolkit version in github actions 2025-04-10 20:32:41 +02:00
Exidex
d8b6010ad1
Global shortcuts for entrypoints 2025-04-08 21:45:49 +02:00
Exidex
2bb69f8add
Fix formatting 2025-04-03 20:38:03 +02:00
Exidex
a258bdf674
Do not panic when global shortcut unregistration fails 2025-04-03 19:54:01 +02:00
Exidex
9f057c09dc
Show generated entrypoints in settings view 2025-04-03 19:37:57 +02:00
Exidex
ae0f833f15
Rework shortcut selector ui in settings 2025-03-30 19:25:09 +02:00
DaRacci
aacf78a377 fix(flake): update npmDepsHash 2025-03-20 18:17:30 +01:00
Exidex
b47906fbad
Fix author field schema description 2025-03-16 12:08:16 +01:00
Exidex
3d962f3405
Add authors field to plugin manifest 2025-03-16 11:56:28 +01:00
Exidex
6d37684fc8
Tweak descriptions of fields in plugin manifest schema 2025-03-16 11:05:37 +01:00
Exidex
c39ac08178
Update package-lock.json 2025-03-15 20:22:34 +01:00
Exidex
99f7ecec11
Fix formatting 2025-03-15 20:16:19 +01:00
Exidex
709f8337d4
Fix scenario runner 2025-03-15 20:14:10 +01:00
Exidex
d721bda736
Inline view examples 2025-03-15 20:14:10 +01:00
Exidex
90957f214a
Use pull_request instead of pull_request_target 2025-03-15 20:14:10 +01:00
Exidex
71284cbca8
Add search-bar-set-search-text list example 2025-03-15 20:14:10 +01:00
Exidex
6e04702903
Add 3 more grid examples. Tweak others 2025-03-15 20:14:10 +01:00
Exidex
8fc21934de
Improve ui form plugin examples 2025-03-15 20:14:10 +01:00
Exidex
2e2864e88a
Replace ` with " in docs markdown files 2025-03-15 20:14:09 +01:00
Exidex
67c7234b41
Replace _ with - in plugin examples file names 2025-03-15 20:14:09 +01:00
Exidex
345ab75143
Add script to generate component model from root 2025-03-15 20:14:09 +01:00
Exidex
c08e33c48c
Remove links from docs markdown for now 2025-03-15 20:14:09 +01:00
Exidex
77d20bcd6c
More ui_list plugin examples 2025-03-15 20:14:09 +01:00
Exidex
fc4df0efa1
Rename out-screenshot to out_screenshot 2025-03-15 20:14:09 +01:00
Exidex
d7d87d0829
Rename example plugins from docs_* to ui_* 2025-03-15 20:14:09 +01:00
Exidex
6ccd091c8f
Rename scenarios directory to example_plugins 2025-03-15 20:14:09 +01:00
Exidex
74f6433bea
Fix screenshot generator 2025-03-15 20:14:09 +01:00
Exidex
053b0a1008
Tweak window border color of macos dark theme on non-macos platforms 2025-03-15 20:14:08 +01:00
Exidex
338540cdec
Fix plugin examples after breaking changes 2025-03-15 20:14:08 +01:00
Benno
4f715674c0
Plugin Manifest schema (#62)
Co-authored-by: Exidex <16986685+exidex@users.noreply.github.com>
2025-03-15 19:32:13 +01:00
Exidex
81c21e9ab6
Bump version in nix overlay 2025-03-15 08:52:53 +01:00
Exidex
704b466b20 Prepare for v17 release 2025-03-15 07:19:55 +00:00
Exidex
93dbba1def
Fix typo 2025-03-14 21:46:42 +01:00
Exidex
9dfab9d537
Better process cleanup on linux id server process is killed 2025-03-14 21:45:11 +01:00
Exidex
fd481027e3
Fix zombie processes being left over after closing Settings UI 2025-03-14 20:43:29 +01:00
DaRacci
d4e88b97a5 fix(flake): use strings instead of deprecated url literals. 2025-03-11 20:15:53 +01:00
Exidex
705b42821c
Actually fix the event loss, finally 2025-03-11 20:08:10 +01:00
Exidex
76609064df
Revert previous attempt to fix event loss 2025-03-11 20:06:43 +01:00
Exidex
f96941d436
Add thread names to logs 2025-03-07 21:54:53 +01:00
Exidex
7e5d4aa8ea
Improve logging of panic, abort process on panic 2025-03-07 20:43:35 +01:00
Exidex
1aca32ae9c
Fix panic when closing inline view due to action being run 2025-03-05 21:54:15 +01:00
Exidex
b5575725f0
Clean up debugging leftovers 2025-03-04 22:18:17 +01:00
Exidex
b0f03daba7
Fix event loss due to race condition by allowing only one concurrent view event 2025-03-04 22:03:15 +01:00
Exidex
e1216598a9
Cleanup formatting a little 2025-03-04 19:45:47 +01:00
Exidex
ecd21cf518
Drop events if state doesn't exist due to race condition instead of panicking 2025-03-04 19:32:55 +01:00
Exidex
e2428ff759
Update README.md 2025-03-02 15:07:09 +01:00
Exidex
900180dfcb Prepare for v16 release 2025-02-23 13:15:12 +00:00
Exidex
1512e68237
Update CHANGELOG.md 2025-02-23 13:41:14 +01:00
Exidex
f4d63a08ee
Fix broken application plugin on non-macos platforms 2025-02-23 13:36:09 +01:00
Exidex
8784598cd8
Fix macos build artifact path 2025-02-22 20:10:13 +01:00
Exidex
fe707d8419
Try to fix macos release x2 2025-02-22 19:35:07 +01:00
Exidex
b9f4d98408
Try to fix macos release 2025-02-22 18:58:13 +01:00
Exidex
a0b4dbe8e9 Prepare for v15 release 2025-02-22 13:52:58 +00:00
Exidex
b7f178666f
Remove Chocolatey install option 2025-02-22 14:51:49 +01:00
Exidex
4d49952ea7
Update CHANGELOG.md 2025-02-22 14:42:42 +01:00
Exidex
0e26ac0ba4
Update CHANGELOG.md 2025-02-16 12:33:59 +01:00
Exidex
435f836eb2
Add support for x86_64 on macOS 2025-02-16 12:33:01 +01:00
Exidex
e49e5a3911
Fix database becoming corrupted after changing theme setting 2025-02-16 12:00:51 +01:00
Exidex
dc8f8d96eb
Fix build on linux 2025-02-16 11:12:59 +01:00
Exidex
3b886b0998
Add screenshot to README.md 2025-02-15 20:47:32 +01:00
Exidex
c18a4050d0
Fix typo in CHANGELOG.md 2025-02-15 20:43:44 +01:00
Michał Kalinowski
f50a9ae51e Don't clear search bar if window is closed using global shortcut 2025-02-15 20:42:45 +01:00
Exidex
047e0c34ca
Update CHANGELOG.md 2025-02-15 20:11:08 +01:00
Benno
652af98544
Localized application names on macOS (#50)
Co-authored-by: Exidex <16986685+exidex@users.noreply.github.com>
2025-02-15 20:09:05 +01:00
Exidex
0c6b0282c2
Refactor 3.5k line file into multiple files 2025-02-15 18:59:12 +01:00
Exidex
2d7af69372
Add --version flag 2025-02-15 09:51:29 +01:00
Exidex
ebc18be7c9
Fix missing cfg causing build to fail on non-linux platforms 2025-02-15 09:41:05 +01:00
Exidex
9f816c52ef
Fix windows build 2025-02-15 09:33:21 +01:00
Exidex
b542633cb6
On X11 listen on _NET_ACTIVE_WINDOW change instead of unfocus event for close-on-unfocus behaviour 2025-02-15 09:29:54 +01:00
Exidex
89c28bae6c
Change global shortcut to be executed on key down instead of key up 2025-02-15 08:21:02 +01:00
Benno
7ce6844ad0 feat: auto scroll to top in window hiding process 2025-02-13 22:07:45 +01:00
Exidex
6be4c2aeae
When using active screen setting for window positioning, position calculated is now relative to size of the screen 2025-02-11 20:51:51 +01:00
Benno
fcbeb20699
Add cmd/ctrl+comma to open settings ui 2025-02-11 20:22:39 +01:00
Benno
c8b3cfe09c
Global shortcut now hides the main window when the window is already open 2025-02-11 20:20:35 +01:00
Exidex
8c017baedd
Try fix github workflow 2025-02-10 21:00:54 +01:00
Exidex
5dade37cb4
Upload artifacts to github actions workflow after building 2025-02-10 20:58:35 +01:00
Exidex
ff1c1b8754
Fix formatting 2025-02-09 14:03:31 +01:00
Exidex
1b8ddf67b1
Implement ability to run entrypoints from cli 2025-02-09 14:00:59 +01:00
Exidex
e36ff58a94
Fix errors now showing up in console when developing 2025-02-08 22:21:01 +01:00
Exidex
9a1ee39485
Fix formatting 2025-02-03 18:18:05 +01:00
Exidex
b949886314
Make subtext in main window search items a little smaller 2025-02-03 18:16:43 +01:00
Exidex
7f185f2972
In search results, move name of plugin to entrypoint name, add entrypoint type in that place use. Use generator name as a type 2025-02-03 18:15:52 +01:00
Exidex
2d083074b1
Run cargo fmt. Add github workflow to validate 2025-02-02 17:10:54 +01:00
Exidex
478c72f69c
Use macOS theme on all platforms by default, instead of legacy theme 2025-02-02 15:48:01 +01:00
Exidex
d06d691a1a
Update CHANGELOG.md 2025-02-01 21:35:15 +01:00
Exidex
f9eeeb6ffd
Update winit fork 2025-02-01 21:08:08 +01:00
Exidex
c3c0ac3cfe
Fix build 2025-02-01 19:56:48 +01:00
Exidex
373909cc68
Add panic hook to log backtrace when plugin runtime panics for easier debugging 2025-02-01 19:10:38 +01:00
Exidex
5e42d0d6bc
Use null files as stderr/stdout/stdin instead of inheriting from parent. Fixes plugin runtime failing to start on windows when using subsystem windows 2025-02-01 14:55:48 +01:00
Exidex
792baa7e6a
Fix global shortcut unregistration failing if previous shortcut registration failed 2025-01-29 23:27:03 +01:00
Exidex
2c3a9f9dd3
Fix error when registering shortcut erroring whole settings window instead of adding an icon 2025-01-29 23:25:30 +01:00
Exidex
c6761e80b2
Use dpi crate from crates.io to avoid multiple instances of same version for cargo vendor 2025-01-28 21:51:15 +01:00
Exidex
19ff11a501
Remove warning from nix README.md because the issue has been fixed 2025-01-20 18:40:24 +01:00
Exidex
39046aabf9 Prepare for v14 release 2025-01-19 19:03:19 +00:00
Exidex
1d9da7ec5b
Bump version in nix overlay 2025-01-19 20:02:25 +01:00
Exidex
a285efc47c
Use thin LTO instead of fat LTO which breaks mouse events on macOS 2025-01-19 19:52:29 +01:00
Exidex
c0e68afc21
Fix build scripts when running locally 2025-01-19 18:46:33 +01:00
Exidex
5f42835c1b
Fix typo in CHANGELOG.md 2025-01-19 15:51:39 +01:00
Exidex
c4e2b763c2
Fix broken build pipeline after previous commit 2025-01-19 13:29:13 +01:00
Exidex
2a7e21c470
Fix broken publish pipeline 2025-01-19 13:17:13 +01:00
Exidex
7bf36f9d8d
Update README.md 2025-01-19 12:36:57 +01:00
Exidex
60a9dccb1d
Fix git pull in wrong place in publish pipeline 2025-01-19 12:36:57 +01:00
Exidex
b0d030dccf Prepare for v13 release 2025-01-19 11:32:44 +00:00
Exidex
c64dfbfe65
Bump version in nix overlay 2025-01-19 12:10:16 +01:00
Exidex
d861b730ef
Update CHANGELOG.md, README.md and THEME.md 2025-01-19 12:09:58 +01:00
Exidex
6f7732ce38
Pass CommandContext object to commands which contains plugin and entrypoint preferences 2025-01-19 12:06:14 +01:00
Exidex
4478f4a99e
Fix npm run dev failing because of missing log files when run for the first time 2025-01-19 11:53:26 +01:00
Exidex
085b5ce7e3
Revert "Fix npm run dev failing because of missing log files when run for the first time"
This reverts commit 8d66cac69f.
2025-01-19 11:45:15 +01:00
Exidex
8d66cac69f
Fix npm run dev failing because of missing log files when run for the first time 2025-01-19 11:21:15 +01:00
Exidex
66bf1c861c
Move image download example in dev_plugin to separate entrypoint to not slow down detail-view 2025-01-18 22:42:32 +01:00
Exidex
7c440c5dcd
Improve config and theme config parsing error logs 2025-01-18 22:34:38 +01:00
Exidex
bd268cbb01
Update CHANGELOG.md 2025-01-18 21:46:30 +01:00
Exidex
d237d2b38a
Enable experimental window tracking by default 2025-01-18 19:06:23 +01:00
Exidex
3201c721a5
Cleanup not needed usages of Arc<Mutex>> 2025-01-16 22:41:20 +01:00
Exidex
e4df96c890
Save position of the main window to be used after restart 2025-01-16 22:02:43 +01:00
Exidex
b4e255a92e
Fix window jumping to another screen if pressing global shortcut with window open when using active monitor position mode 2025-01-16 21:21:34 +01:00
Exidex
47bf9f28e0
Rename windowTracking preference id into experimentalWindowTracking 2025-01-16 21:00:24 +01:00
Exidex
c492d26711
Add config setting to chose on which monitor window appears: always on the same or current active 2025-01-16 20:52:43 +01:00
Exidex
4cf3d3210a
Add ability to open new instance of application there are already windows open and window tracking is enabled 2025-01-13 22:09:26 +01:00
Exidex
b6fe543df7
Fix action not being run if list or grid view has focused search bar 2025-01-13 21:47:16 +01:00
Exidex
2ee1e645c9
Add config option to disable close on unfocus functionality 2025-01-13 20:53:08 +01:00
Exidex
565519fa30
Add ability to control whether window is closed based on return value of action 2025-01-12 21:47:11 +01:00
Exidex
bb1562a362
Fix invalid cargo profile 2025-01-12 20:41:58 +01:00
Exidex
8ffe84c440
Use size optimized cargo profile only on releases, exclude nix as well 2025-01-12 20:38:41 +01:00
Exidex
ea527a7ece
Rename Window Tracking preference into Experimental Window Tracking 2025-01-12 20:25:51 +01:00
Exidex
b59762bd5b
Fix icons on macOS sometimes not loading 2025-01-12 20:18:17 +01:00
davfsa
e7c8575322 Reduce release binary size
`strip = true` alone reduces the size by 40MB!
2025-01-12 12:48:30 +01:00
Exidex
a905795c36
Add preference to enable window tracking. Currently disabled by default 2025-01-11 23:24:33 +01:00
Exidex
4304d35d6a
Reload whole plugin when changing entrypoint state or preference value instead of just reloading search index 2025-01-11 23:23:08 +01:00
Exidex
61b71c8858
Re-run entrypoint generators after changing preference value 2025-01-11 23:01:22 +01:00
Exidex
d57d10d1a8
Remove preferences helper functions. Add preferences react hooks. Rename GeneratorProps to GeneratorContext. Add preferences to GeneratorContext 2025-01-11 22:50:54 +01:00
Exidex
026bdf9b59
Wire up the Cosmic support for window tracking 2025-01-11 21:02:06 +01:00
Exidex
4611d06a8b
Do not sign binaries in build workflow 2025-01-11 20:10:13 +01:00
Exidex
a8666b1f57
Use pull_request_target instead of pull_request 2025-01-11 20:09:44 +01:00
Exidex
d3ac37996a
Do not call git pull in build workflow to fix build on pr branches 2025-01-11 19:48:58 +01:00
Exidex
033f2c8d82
Fix build on windows 2025-01-10 22:02:24 +01:00
Exidex
72ba3f03cb
Move npm version bump to final state of publishing, so nix hash is not mismatched on release tag 2025-01-10 21:46:04 +01:00
Exidex
4d13725692
Make windows_app_from_path async 2025-01-10 21:32:14 +01:00
Exidex
f9d6642900
Remove onClick events, instead primary action is called on click with id of clicked item. Add onItemFocusChange events 2025-01-10 21:26:54 +01:00
Tristan Schrader
8cf1fedf20 fix: libxkbcommon + pkg-config for linux devshell
Because libxkbcommon is needed during package build now on Linux, we
have to included it as a package and configure it for access by
smithay-client-toolkit with pkg-config
2025-01-10 20:55:24 +01:00
Exidex
fd01fa2036
Add tiny-skia back explicitly 2025-01-08 20:27:07 +01:00
Exidex
95b0d0d326
Fix windows on x11 sometimes not matching real state, if plugin was launched when windows already existed 2025-01-08 20:23:44 +01:00
Exidex
907e743607
Tweak styling of detail metadata when inside a list 2025-01-07 21:13:46 +01:00
Exidex
796008ad39
Make metadata section in detail in list slightly bigger 2025-01-07 20:51:39 +01:00
Exidex
e5117c6c45
Fix grid empty view not displaying the image 2025-01-07 20:45:02 +01:00
Exidex
d9b60d5e63
Fix list empty view image being too big 2025-01-07 20:44:21 +01:00
Exidex
b2094be545
Finish renaming Generated Command to Generated Entrypoint 2025-01-07 20:21:56 +01:00
Exidex
20fa211d05
Tweak styling of action panel 2025-01-07 20:07:49 +01:00
Exidex
63e1642823
Prevent from changing theme settings if theme file exist 2025-01-07 19:20:22 +01:00
Exidex
6b0e3fa39b
Add generator entrypoint name in addition to plugin name to main view search results 2025-01-06 22:10:40 +01:00
Exidex
a5d69e8d94
Add "show all opened windows" view endpoint to bundled plugin 2025-01-06 21:39:54 +01:00
Exidex
27e60d4bc5
Fix build 2025-01-06 19:06:30 +01:00
Exidex
f0a18f0e22
Rework themes. Add theme setting. Remove complex themes. Remove sample generation commands. Use toml for theme config 2025-01-06 18:52:25 +01:00
Tristan Schrader
f9e4f6660e fix: libxkbcommon needed during linux build on nix
the new smithay-client-toolkit dependency requires access to these
libraries during build, not just runtime as before
2025-01-06 10:15:15 +01:00
Exidex
532cd3edab
Use single window and hide/unhide it, instead of recreating window each time
This is a workaround for https://github.com/iced-rs/iced/issues/2719
2025-01-05 15:35:46 +01:00
Exidex
34a5c5e168
MacOS light and dark themes. Rework simplified theme 2025-01-04 22:00:39 +01:00
Exidex
2e062705c7
Remove not used dependency 2025-01-01 12:05:20 +01:00
Exidex
b406da1598
Implement applications plugin on windows 2025-01-01 11:52:41 +01:00
Exidex
4f2add81cc
Add cosmic support for window tracking 2024-12-31 21:04:05 +01:00
Exidex
434dd7a252
Fix build on non-linux platforms 2024-12-31 19:29:04 +01:00
Exidex
40e8d578ba
Fix focus on action panel for search result going outside of list 2024-12-31 19:25:12 +01:00
Exidex
a3532e9602
Implement window tracking and focusing for WMs that support wlr-foreign-toplevel-management-unstable-v1 protocol 2024-12-31 19:15:57 +01:00
Exidex
a84d3ee3da
Implement focusing for window tracking on x11 2024-12-29 21:45:33 +01:00
Exidex
9767290ecb
Match x11 window to app id also based on StartupWmClass and WM_CLASS 2024-12-29 17:36:32 +01:00
Exidex
d4548c0cda
Implement window tracking per application for x11 2024-12-29 13:22:52 +01:00
Exidex
873220ef89
Revert "Slightly throttle search index reload when calling add/remove in entrypoint generator"
This reverts commit f584f28a74.
2024-12-28 23:03:06 +01:00
Exidex
f584f28a74
Slightly throttle search index reload when calling add/remove in entrypoint generator 2024-12-28 21:00:20 +01:00
Exidex
11af8eaff2
Update nix hash 2024-12-26 11:10:41 +01:00
Exidex
1c1deca67e
Rename command-generator entrypoint type into entrypoint-generator 2024-12-25 23:59:45 +01:00
Exidex
9434c88f83
Add ability to open view with generated commands. Rename fn property into run, add view property 2024-12-25 23:45:11 +01:00
Exidex
0b48acdf77
Move command generator primary action into actions field which allows to specify custom default label 2024-12-25 13:37:46 +01:00
Exidex
ba4fd06f37
Merge updateAccessories into add, introduce get by id and getAll functions for command generator 2024-12-25 11:35:12 +01:00
Exidex
7fb6ca4074
Implement generated command accessories 2024-12-24 21:56:38 +01:00
Exidex
63a54d1d18
Fix build after renaming Image into ImageLike 2024-12-24 19:14:50 +01:00
Exidex
d01ab6bedc
Rename Image type to ImageLike to avoid conflict with Image component 2024-12-24 19:01:45 +01:00
Exidex
9604cb85f1
Improve rejected promise error log 2024-12-24 17:32:51 +01:00
Exidex
dd12028e15
Fix values in permissions.exec.command not being resolved properly 2024-12-24 14:49:15 +01:00
Exidex
45af858f13
Fix npm run dev sometimes not working 2024-12-24 14:25:37 +01:00
Exidex
c555579f2d
Fix zombie processes being left over after plugin runtime was stopped 2024-12-23 16:26:10 +01:00
Exidex
dbc22aa81b
Update mismatched nix hash 2024-12-22 16:56:25 +01:00
Exidex
ff05d7c673 Prepare for v12 release 2024-12-22 13:51:26 +00:00
Exidex
00163031e0
Remove publishing of deprecated @project-gauntlet/deno 2024-12-22 14:36:45 +01:00
Exidex
95b0243d5b
Bump version in nix overlay 2024-12-22 13:18:10 +01:00
Exidex
a00b78e719
Update iced-layershell to fix click transparency not working on hud window 2024-12-22 13:17:09 +01:00
Exidex
8fb6b5becb
Fix frontmost application not getting a focus on macOS after main window is closed 2024-12-22 13:12:46 +01:00
Exidex
f2caf8a60b
Update CHANGELOG.md and README.md 2024-12-21 21:47:35 +01:00
Exidex
68be9a1a2f
Rework plugin shutdown to be more reliable 2024-12-21 20:57:15 +01:00
Exidex
ba497bf14d
Fix non-closable ghost window after pressing shortcut if main window is already open 2024-12-21 19:34:27 +01:00
Tristan Schrader
f15e7c61ce
feat: Nix package + devShell & NixOS + Home-Manager modules (#27)
We use crane to provide incremental caching to avoid unnecessary rebuilds.

Because @project-gauntlet/tools is a git:// dependency in NPM, we must use
fetchNpmDeps with a hash unique to package-lock.json.

librusty_v8 builds quite slowly, so we fetch the binary bindings from the
project's GitHub release and provide a .#fetch-rusty-v8-hashes package to
get the correct hashes when v8 updates.

The modules are quite basic for now, but once declarative configuration is
supported in Gauntlet, these can be built out more.

Also, we had to include a patch in Cargo.toml for libffi-sys because its
crates.io release is outdated and breaks the nix sandbox.
2024-12-20 23:47:14 +01:00
Exidex
42f3bb4d7d
Use ArrayBuffer in Clipboard Api instead of Blob 2024-12-20 21:18:16 +01:00
Exidex
5bb92eea8a
Fix hud window not appearing on wayland 2024-12-20 19:55:41 +01:00
Exidex
c233b3ca00
Fix separator in inline view not being horizontally centered 2024-12-20 19:22:42 +01:00
Exidex
b685361033
Hide window when plugin action is executed 2024-12-20 18:49:19 +01:00
Exidex
6209ff6272
Update all js dependencies, react is still on v18 2024-12-20 18:33:23 +01:00
Exidex
bbfb17e4b1
Fix scenarios not working 2024-12-20 17:34:03 +01:00
Exidex
a54a760cd4
Update all rust dependencies 2024-12-20 15:36:54 +01:00
Exidex
536661e7d0
Update iced-layershell to fix choppy loading bar and missing window focused event 2024-12-20 11:24:05 +01:00
Exidex
e32a024f1a
Remove @project-gauntlet/deno, use @types/deno instead 2024-12-19 22:13:49 +01:00
Exidex
c41adc3c41
Replace tools git submodule with github dependency 2024-12-19 21:24:49 +01:00
Exidex
c5faf7f812
Actually fix occasional deadlock when spamming keys in UI 2024-12-19 18:26:09 +01:00
Exidex
84a598ddab
Move hud window on all non-wayland platforms slightly lower 2024-12-19 16:03:53 +01:00
Exidex
a4451acae4
Fix message loop in plugin runtime sometimes getting deadlocked 2024-12-19 16:03:22 +01:00
Exidex
b751759189
Fix main window closing on macOS because hud window took focus 2024-12-19 15:56:17 +01:00
Exidex
0b67e67d0b
Each JS runtime is in separate process 2024-12-18 22:11:15 +01:00
Exidex
337a8eaea4
Move Numbat context to gauntlet_internal_all extension 2024-12-18 22:08:13 +01:00
Exidex
49b52488f0
Move ComponentModel to separate file 2024-12-18 22:08:13 +01:00
Exidex
f45c797cdc
Move out dirs usages out of plugin runtime 2024-12-18 22:08:13 +01:00
Exidex
62a2ffc2b7
Abstract away usage of frontend api in plugin runtime behind backend api 2024-12-18 22:08:13 +01:00
Exidex
37d8a218f7
Abstract clipboard inside plugin runtime behind backend api 2024-12-18 22:08:12 +01:00
Exidex
364beb647b
Abstract away repository, icon_cache and search index from js runtime ops 2024-12-18 22:08:12 +01:00
Exidex
5b769c8e7d
Deno update to 2.1.1 2024-12-18 22:08:12 +01:00
Exidex
06ecad922d
Fix hud window not disappearing properly when multiple hud windows were present at the same time 2024-12-13 17:40:00 +01:00
Exidex
1f2fe341d6
Fix window flashing and not appearing after hud window is shown 2024-12-13 17:38:42 +01:00
Exidex
17648299ef
Fix window transparency not working on macOS 2024-12-12 19:49:03 +01:00
Exidex
8150bfd03f
Slightly refactor plugin action messages to better group result messages 2024-12-10 20:37:23 +01:00
Exidex
82ef93a626
Fix occasional deadlock when spamming keys in UI 2024-12-10 18:56:22 +01:00
Exidex
771d837eb0
Use NSPanel with Accessory activation_policy on MacOS for better window behavior 2024-12-08 22:01:49 +01:00
Exidex
27eccbebea
Migrate back iced_layershell because of performance problems and window transparency on macos not working 2024-12-08 15:59:16 +01:00
Exidex
5cd1c2759e
Recreate winit window each time instead of hiding and showing 2024-12-07 14:39:52 +01:00
Exidex
ca0264fe1e
Bring back layer shell support using PopOS iced fork 2024-12-07 10:10:36 +01:00
Exidex
7b41f1ac9b
Update iced to 0.13. No wayland support. No macOS window settings 2024-11-25 20:40:27 +01:00
Exidex
c8637733a7
Fix some code examples missing scenario data 2024-11-23 21:29:50 +01:00
Exidex
2f24d73ef5
Fix entrypoint id in code examples 2024-11-23 21:17:30 +01:00
Exidex
f742358b58
Add and improve some code examples 2024-11-23 21:16:08 +01:00
Exidex
2cb6be718a
Split inline scenario into 3 2024-11-21 22:02:42 +01:00
Exidex
c492783f0b
Remove docs-code-segment from most scenarios 2024-11-21 21:29:54 +01:00
Exidex
950f8ebfa3
Add missed changelog item 2024-11-16 21:24:15 +01:00
Exidex
1285724719 Prepare for v11 release 2024-11-16 19:34:08 +00:00
Exidex
5c8f13184b
Await on reloadSearchIndex after running cleanup functions and before running generators 2024-11-16 20:23:05 +01:00
Exidex
3bb5be6caf
Update CHANGELOG.md and THEME.md 2024-11-16 20:20:18 +01:00
Exidex
ef38ad4a5e
Fix generated command entrypoint not being removed after disabling entrypoint in Settings UI 2024-11-16 20:16:52 +01:00
Exidex
5855990952
Rename Theme into Simplified theme to avoid confusion 2024-11-16 19:33:11 +01:00
Exidex
c340a69b5b
Restrict plugin id schema to http(s), ssh and git, exclude file and unknown 2024-11-16 09:53:51 +01:00
Exidex
72bc21716b
Implement plugin environment 2024-11-15 15:14:04 +01:00
Exidex
6fe97d7afe
Split assetData helper function into sync and async variants 2024-11-15 14:00:39 +01:00
Exidex
3a095cfda6
Allow to specify border styling in simplified theme config 2024-11-15 12:49:27 +01:00
Exidex
84ccf5980a
Rename themes from "theme" and "color theme" into "complex theme" and "theme" respectively 2024-11-15 10:57:41 +01:00
Exidex
318895d241
Fix build broken by accidental import removal 2024-11-14 21:43:32 +01:00
Exidex
220fb27eca
Enable experimental optional types in grpc schema files 2024-11-14 21:31:38 +01:00
Exidex
bd76aa5099
Ability to unset global shortcut, better error reporting for setting global shortcut 2024-11-14 21:08:01 +01:00
Exidex
8dae5cb16c
Add "Indexing..." text on main view while load bar for currently running command generators is shown 2024-11-14 18:01:30 +01:00
Exidex
ca609d58e3
Fix inability to move left-right on the grid view because search bar text input is always focused 2024-11-14 17:42:20 +01:00
Exidex
1ca022ebd0
Fix button clicking events not working after react renderer parser refactor 2024-11-14 17:20:08 +01:00
Exidex
6a56604e89
Fix padding of grid and list section being too far if it is first in the view 2024-11-14 14:13:59 +01:00
Exidex
201fea1446
Autofocus searchbar in grid and list when opening the plugin view 2024-11-14 13:37:34 +01:00
Exidex
058bcde9d2
Distinct styling for pressed state of buttons 2024-11-13 22:09:32 +01:00
Exidex
57ea67d13e
Crappy heuristic for scrolling distance of grid items, dynamic grid item height 2024-11-13 21:55:00 +01:00
Exidex
1bd4491429
Rework grid keyboard navigation again, this time with tests 2024-11-13 20:04:50 +01:00
Exidex
504adac487
Fix keyboard navigation in grid not working properly in situation when going from one section to another with non-full rows 2024-11-12 23:43:18 +01:00
Exidex
c9dd9173bb
Implement visual part of keyboard navigation for list and grid views 2024-11-11 21:34:48 +01:00
Exidex
8c6f6b93e3
Fix scenarios after react renderer parsing rework 2024-11-11 20:01:16 +01:00
Exidex
589c3748f2
Add loading bar when opening plugin view takes more than 300 milliseconds 2024-11-10 22:51:31 +01:00
Exidex
e132fe10a5
Bring back image support after react renderer parsing rework 2024-11-10 22:03:48 +01:00
Exidex
579e29664d
Show loading bar in main search view when there is still some command generator running 2024-11-10 19:43:59 +01:00
Exidex
479594b385
Fix command generator example in dev plugin 2024-11-10 17:48:15 +01:00
Exidex
db0a02f97e
Ignore events that do not have respective widget state instead of crashing. 2024-11-10 17:37:43 +01:00
Exidex
d3cc26cfd3
Fix build on macOS 2024-11-10 17:36:53 +01:00
Exidex
26695673a9
Fix scrollable resetting when clicking action panel button in bottom panel 2024-11-10 17:28:51 +01:00
Exidex
f68fcf1b6e
Fix view render taking a long time of while app gen command generator runs 2024-11-10 17:09:06 +01:00
Exidex
6a70c4ecba
Rework react renderer parsing to use non-recursive data structures 2024-11-10 16:53:53 +01:00
Exidex
47547083d4
Update applications plugin for macOS after command generator rework 2024-11-03 18:38:35 +01:00
Exidex
8e0ec1d7b8
Rework command generators to allow updating list of generated entrypoints while plugin is running 2024-11-03 14:15:48 +01:00
Exidex
b415c90519
Add clarification about binary location on macOS to theme docs 2024-11-03 11:19:47 +01:00
Exidex
ec8511a5a4
Work in progress keyboard navigation for List and Grid 2024-10-30 21:05:03 +01:00
Exidex
750278dc48
Implement List and Grid search bar 2024-10-27 19:33:39 +01:00
Exidex
508909cf7d
Fix global shortcut not working on windows 2024-10-27 17:40:49 +01:00
Exidex
90a1493256
Rename default scenario input files 2024-10-27 10:28:35 +01:00
Exidex
8605293c81
Add docs code segment markers to scenarios 2024-10-27 10:28:11 +01:00
Exidex
f594b6310e
Fix broken scenarios 2024-10-25 21:01:26 +02:00
Exidex
f1191057d2
Fix emojis not working in multiple places across the application 2024-10-25 17:22:01 +02:00
Exidex
689461c456
Refactor ui action execution handling. Change primary action label on action bar into a button 2024-10-20 19:49:07 +02:00
Exidex
7bd8a5f583
Rework clipboard, fixes writeText() not working on KDE 2024-10-20 14:43:46 +02:00
Exidex
b9eea0d5ef
Fix primary action of first search result being called if primary action of inline view is called using enter key, causing duplicate action 2024-10-18 21:42:15 +02:00
Exidex
97ff3e53b6
Fix clipboard operations not working on wayland 2024-10-18 20:46:57 +02:00
Exidex
c3c58d3128
Reduce scope of try catch in calculator bundled plugin 2024-10-18 19:10:30 +02:00
Exidex
ed29af6583
Fix hud window not disappearing on wayland 2024-10-18 18:35:29 +02:00
Exidex
cbcbcf8b94 Prepare for v10 release 2024-10-13 15:42:37 +00:00
Exidex
f6e3748e78
Update CHANGELOG.md, README.md and THEME.md 2024-10-13 17:37:55 +02:00
Exidex
6ea9748027
Fix prompt in main search bar resetting after going back from plugin view 2024-10-13 17:37:34 +02:00
Exidex
06cc7ad8c8
Update demo video 2024-10-13 17:34:17 +02:00
Exidex
8d8ddd8f28
Update numbat to 1.14 2024-10-13 16:33:47 +02:00
Exidex
f1c7619fed
Fix compilation on macOS 2024-10-13 11:54:09 +02:00
Exidex
d6d619b4ec
Show hud when copying calculator result 2024-10-13 11:44:05 +02:00
Exidex
8397b108e0
Implement hud feedback window 2024-10-13 11:41:37 +02:00
Exidex
8bfd93ab57
Replace all upper-case key names with just first letter upper-case 2024-10-12 21:34:57 +02:00
Exidex
a8391712cf
On macOS show return arrow icons instead of "ENTER" text for shortcuts 2024-10-12 21:33:21 +02:00
Exidex
f9aa94f13b
Fix calculator plugin not working after bundled plugin merge 2024-10-12 21:19:37 +02:00
Exidex
978fafefcd
Fix entrypoint icons not working 2024-10-12 20:50:31 +02:00
Exidex
1fb03fdcfd
Rename action "Copy content" to "Copy result" for calculator plugin 2024-10-11 18:28:15 +02:00
Exidex
b9f5b32037
Add flatpak application support for Application plugin on Linux 2024-10-11 18:26:24 +02:00
Exidex
bb0ceead12
Highlight preference value fields which are required but not yet filled 2024-10-10 20:10:19 +02:00
Exidex
cf2aa7030d
Fix download info panel being transparent and sometimes partially obstructed 2024-10-10 18:41:29 +02:00
Exidex
792568816e
Add "Check for updates" button in settings ui 2024-10-10 18:33:59 +02:00
Exidex
d94563512d
Move "remove plugin" button in settings ui to the bottom of the view 2024-10-10 18:25:04 +02:00
Exidex
5051312323
Fix some macOS applications not having icons, also speedup icon cache generation 2024-10-08 22:48:18 +02:00
Exidex
1f2fa6e3b2
Update package-lock.json 2024-10-08 20:01:35 +02:00
Exidex
0c01048d32
Merge all bundled plugins into one 2024-10-08 19:56:06 +02:00
Exidex
6781a65e18
Show plugin id in settings 2024-10-08 19:49:46 +02:00
Exidex
07aeea6ebb
Make entrypoint icons a little bit bigger 2024-10-07 20:52:16 +02:00
Exidex
f970c9b26c
Make on hover search result background color in main view distinct from focused background color 2024-10-07 20:21:57 +02:00
Exidex
b0112a4e68
Add copy functionality for Calculator plugin output 2024-10-07 20:02:57 +02:00
Exidex
0a92457bd4
Implement inline view shortcut display in action panel 2024-10-07 19:55:23 +02:00
Exidex
2cac027a26
Implement primary and secondary shortcuts for inline view actions 2024-10-07 18:04:46 +02:00
Exidex
763d7b0ac4
Implement inline view action panel. Shortcuts still todo 2024-10-06 22:32:42 +02:00
Exidex
068cc0ddf0
Unfocus main search list if inline view is rendered 2024-10-06 14:59:26 +02:00
Exidex
e02e993949
Implement ability for list to be unfocused. Action panel list is unfocused by default if panel is opened using mouse click 2024-10-06 14:00:46 +02:00
Exidex
39451890a8
Fix previous inline view still being shown after main window is closed and opened again 2024-10-06 12:27:15 +02:00
Exidex
68f4aadc6a
Add log about macOS version used for indexing settings 2024-10-06 11:22:35 +02:00
Exidex
f26baeaecb
Add build information shown on startup 2024-10-06 11:19:22 +02:00
Exidex
ff23ea4d76
Slightly improve logging 2024-10-06 09:53:50 +02:00
Exidex
43e7cf919d
Fix log printing wrong thing 2024-10-06 09:29:55 +02:00
Exidex
628523302e
Move settings to the bottom of macOS Applications search list 2024-10-05 21:48:08 +02:00
Exidex
c021fbab7e
Gracefully handle parse errors of system extension plist in macOS Applications plugin 2024-10-05 21:47:16 +02:00
Exidex
a334ba2052
Fix action shortcut not being shown in main search view action panel 2024-10-05 21:31:51 +02:00
Exidex
12634f3d7d
Implement action shortcuts on main view 2024-10-05 21:14:51 +02:00
Exidex
72db547a99
Make patch part optional in macOS version parsing. Fixes Applications plugin not working on some systems 2024-10-05 18:59:03 +02:00
Exidex
f07795b76f
Implement primary and secondary shortcuts 2024-10-04 21:10:41 +02:00
Exidex
947502e628
Remove not needed macOS entitlement 2024-10-04 20:28:33 +02:00
Exidex
08931e2c80
Bundle openssl. Fixes crash when it is not present 2024-10-04 19:31:10 +02:00
Exidex
71b110aa9e
Implement visual part of primary and secondary actions 2024-10-01 22:27:20 +02:00
Exidex
904a34a83d
Implement running action via enter in plugin view 2024-10-01 19:57:05 +02:00
Exidex
1e2877b057
Fix alt + k not working in main search view 2024-09-30 21:26:21 +02:00
Exidex
e320145abe
Implement keyboard navigation for plugin view action panel 2024-09-29 20:43:30 +02:00
Exidex
6f88f3f56a
Fix flicker on main view after returning from plugin view after refactor 2024-09-29 16:32:28 +02:00
Exidex
17ccf5b57c
Slightly refactor usage of backend_api 2024-09-29 15:59:32 +02:00
Exidex
8793d678f6
Refactor focus management 2024-09-29 15:43:30 +02:00
Exidex
f831cd4eb3
Implement keyboard navigation for main view action panel 2024-09-28 21:32:17 +02:00
Exidex
9346c6f394
Implement secondary command actions for main search view 2024-09-28 20:18:55 +02:00
Exidex
1e181ea1a8
Rename action title to label 2024-09-28 16:42:51 +02:00
Exidex
4b33f2af51
Make action panel slightly bigger 2024-09-27 20:09:38 +02:00
Exidex
66cd004d32
Add default action label with shortcut to the action bar 2024-09-27 19:57:07 +02:00
Exidex
f39b303471
Implement Alt + K shortcut for main search view action panel 2024-09-27 19:02:00 +02:00
Exidex
00a2888b08
Add action bar to main search view 2024-09-27 18:30:34 +02:00
Exidex
cdf46205d3
Use same colors for settings ui 2024-09-26 20:26:43 +02:00
Exidex
2bca56a1d1
Add plugin name and entrypoint name over inline view. Refine inline view styling. 2024-09-24 20:26:12 +02:00
Exidex
2ce02919a0
Fix spacing on section subtitle. Tweak text colors 2024-09-23 20:59:04 +02:00
Exidex
34c3cf0da6
Add Contribution section to README.md 2024-09-23 19:24:37 +02:00
Exidex
2bb6355f0c
Fix panic when stopping disabled plugin 2024-09-23 17:55:10 +02:00
Exidex
468013397d
Grid and List accessories. Refine grid styling 2024-09-22 21:02:19 +02:00
Exidex
5420aafadf
Make title of grid item optional 2024-09-22 14:18:07 +02:00
Exidex
f697a40c4e
Create plugin data and cache directories when requested by plugin 2024-09-22 09:42:03 +02:00
Exidex
b582d7ba7f
Variables in paths of permissions 2024-09-21 20:36:31 +02:00
Exidex
1bb08e0f35
Update nodejs version to 22 to fix deadlock when running npm tasks in github actions on macos 2024-09-21 20:17:06 +02:00
Exidex
faa1c9e7b9
Update nodejs version in Actions to 20 2024-09-21 17:41:08 +02:00
Exidex
45015f8f8d
Update package-lock.json 2024-09-21 17:36:40 +02:00
Exidex
079386cd80
Rework permissions. Better validation. Remove ffi and high_resolution_time 2024-09-21 17:30:56 +02:00
Exidex
007a6aa4d8
Clarify macOS system settings item in CHANGELOG.md 2024-09-19 18:32:13 +02:00
Exidex
18dea0d56d
Add Numbat mention to README.md 2024-09-15 20:47:05 +02:00
Exidex
2cf4816c69 Prepare for v9 release 2024-09-15 17:47:40 +00:00
Exidex
ba86afac6d
Update CHANGELOG.md, README.md and THEME.md 2024-09-15 19:38:18 +02:00
Exidex
0caea4f456
Support macOS 13+ system extension settings 2024-09-15 17:56:05 +02:00
Exidex
ffd91f0b85
Extend macOS Application plugin with System settings items like Sound, Display, etc 2024-09-15 14:39:38 +02:00
Exidex
dd8e754a26
Include more directories in macOS application search 2024-09-15 12:45:22 +02:00
Exidex
8c8ade8a89
Replace onSelectionChange and id on List or Grid itself with onClick on item 2024-09-15 11:44:27 +02:00
Exidex
29649aae51
Split preference name into id and name 2024-09-15 10:50:16 +02:00
Exidex
ea9c795973
Require main_search_bar permission when entrypoint type 'inline-view' is used 2024-09-14 20:03:31 +02:00
Exidex
1fe349db47
Add permissions for clipboard 2024-09-14 17:26:29 +02:00
Exidex
a6e056c060
Updated numbat. Enable currency module 2024-09-14 15:45:05 +02:00
Exidex
d350a3f05e
Show Alt+K on bottom panel action button. Refine styling 2024-09-14 14:49:48 +02:00
Exidex
60578e6391
Add useFetch hook. Refine types in other hooks 2024-09-13 19:27:10 +02:00
Exidex
9f38e3aa2d
Display data in all hooks examples 2024-09-13 16:39:23 +02:00
Exidex
f9f373916f
Add useCachedPromise hook 2024-09-12 20:25:28 +02:00
Exidex
2154131b5c
Add useStorage and useCache hooks 2024-09-10 21:48:32 +02:00
Exidex
0eb1ba4c9b
Do not send abort event in usePromise when promise has already finished 2024-09-10 19:11:15 +02:00
Exidex
bff3bd8778
Reduce job timeout to 60 min 2024-09-09 21:32:19 +02:00
Exidex
b86ae6c0d9
Rename useAsync to usePromise 2024-09-09 21:27:48 +02:00
Exidex
2f232aae02
useAsync hook 2024-09-09 20:38:46 +02:00
Exidex
adb6a48347
Prevent js runtime from shutting down when uncaught exception is thrown inside a promise 2024-09-08 21:01:41 +02:00
Exidex
3c72104ea7
Add isLoading property 2024-09-08 19:51:50 +02:00
Exidex
cdcda9fb75
Fix race condition when restarting plugins that leads to crash 2024-09-08 16:07:04 +02:00
731 changed files with 51884 additions and 24357 deletions

14
.editorconfig Normal file
View file

@ -0,0 +1,14 @@
# EditorConfig is awesome: https://editorconfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
[*.yaml]
indent_style = space
indent_size = 2

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

View file

@ -11,14 +11,19 @@ jobs:
uses: ./.github/workflows/setup-linux.yaml
with:
command: npm run build-linux-project --workspace @project-gauntlet/build
upload-artifact: true
secrets: inherit
build-macos:
uses: ./.github/workflows/setup-macos.yaml
with:
command: npm run build-macos-project --workspace @project-gauntlet/build
upload-artifact: true
secrets: inherit
build-windows:
uses: ./.github/workflows/setup-windows.yaml
with:
command: npm run build-windows-project --workspace @project-gauntlet/build
upload-artifact: true
secrets: inherit

19
.github/workflows/format.yaml vendored Normal file
View file

@ -0,0 +1,19 @@
name: format
on: [push, pull_request]
jobs:
rust:
runs-on: ubuntu-22.04
steps:
- uses: dtolnay/rust-toolchain@nightly
with:
components: rustfmt
- uses: actions/checkout@v4
- name: rustfmt
run: cargo +nightly fmt --all -- --check
nix:
runs-on: ubuntu-22.04
steps:
- uses: cachix/install-nix-action@v31
- uses: actions/checkout@v4
- name: alejandra
run: nix shell nixpkgs#alejandra -c alejandra -c .

11
.github/workflows/nix.yaml vendored Normal file
View file

@ -0,0 +1,11 @@
name: nix build
on: [push, pull_request]
jobs:
all:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v25
with:
nix_path: nixpkgs=channel:nixos-unstable
- run: nix-build

View file

@ -25,16 +25,14 @@ on:
jobs:
publish-init:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
outputs:
github-release-id: ${{ steps.init-step.outputs.github-release-id }}
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 22
- run: npm ci

View file

@ -6,12 +6,16 @@ on:
command:
required: true
type: string
upload-artifact:
default: false
type: boolean
github-release-id:
type: string
jobs:
run-on-linux:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
timeout-minutes: 60
steps:
- run: sudo apt-get update
- run: sudo apt-get install -y protobuf-compiler
@ -20,12 +24,10 @@ jobs:
- run: sudo apt-get install -y libxkbcommon-dev
- uses: actions/checkout@v4
with:
submodules: true
- run: git pull
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 22
registry-url: "https://registry.npmjs.org"
scope: '@project-gauntlet'
- uses: dtolnay/rust-toolchain@stable
@ -41,3 +43,11 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
GITHUB_RELEASE_ID: ${{ inputs.github-release-id }}
- uses: actions/upload-artifact@v4
if: ${{ inputs.upload-artifact }}
with:
name: 'gauntlet-x86_64-linux.tar.gz'
path: 'target/x86_64-unknown-linux-gnu/release/archive/gauntlet-x86_64-linux.tar.gz'
if-no-files-found: 'error'
retention-days: 7

View file

@ -6,12 +6,16 @@ on:
command:
required: true
type: string
upload-artifact:
default: false
type: boolean
github-release-id:
type: string
jobs:
run-on-macos:
runs-on: macos-latest
timeout-minutes: 60
steps:
# https://github.com/actions/runner-images/issues/7522#issuecomment-1556766641
- name: Kill XProtectBehaviorService
@ -19,12 +23,10 @@ jobs:
echo Killing XProtect.; sudo pkill -9 XProtect >/dev/null || true;
- uses: actions/checkout@v4
with:
submodules: true
- run: git pull
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 22
registry-url: "https://registry.npmjs.org"
scope: '@project-gauntlet'
@ -33,6 +35,8 @@ jobs:
uses: Homebrew/actions/setup-homebrew@master
- uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-apple-darwin,x86_64-apple-darwin
- run: brew install protobuf
- run: brew install create-dmg
@ -53,3 +57,11 @@ jobs:
APPLE_SIGNING_KEY_PEM: ${{ secrets.APPLE_SIGNING_KEY_PEM }}
APPLE_SIGNING_CERT_PEM: ${{ secrets.APPLE_SIGNING_CERT_PEM }}
APP_STORE_CONNECT_KEY: ${{ secrets.APP_STORE_CONNECT_KEY }}
- uses: actions/upload-artifact@v4
if: ${{ inputs.upload-artifact }}
with:
name: 'gauntlet-universal-macos.dmg'
path: 'target/gauntlet-universal-macos.dmg'
if-no-files-found: 'error'
retention-days: 7

View file

@ -6,27 +6,29 @@ on:
command:
required: true
type: string
upload-artifact:
default: false
type: boolean
github-release-id:
type: string
jobs:
run-on-windows:
runs-on: windows-latest
runs-on: windows-2022
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
with:
submodules: true
- run: git pull
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 22
registry-url: "https://registry.npmjs.org"
scope: '@project-gauntlet'
- uses: dtolnay/rust-toolchain@stable
- run: choco install protoc
- run: dotnet tool install --global wix
- run: wix extension add -g WixToolset.Util.wixext
- run: dotnet tool install --global wix --version 5.0.2
- run: wix extension add -g WixToolset.Util.wixext/5.0.2
- uses: Swatinem/rust-cache@v2
with:
@ -39,3 +41,12 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
GITHUB_RELEASE_ID: ${{ inputs.github-release-id }}
- uses: actions/upload-artifact@v4
if: ${{ inputs.upload-artifact }}
with:
name: 'gauntlet-x86_64-windows.msi'
path: 'target/x86_64-pc-windows-msvc/release/gauntlet-x86_64-windows.msi'
if-no-files-found: 'error'
retention-days: 7

27
.github/workflows/winget.yaml vendored Normal file
View file

@ -0,0 +1,27 @@
name: Publish to WinGet
on:
release:
types: [published]
env:
VERSION_REGEX: '^v(\d+)$'
# winget-create will read the following environment variable to access the GitHub token needed for submitting a PR
# See https://aka.ms/winget-create-token
WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_TOKEN }}
jobs:
publish:
runs-on: windows-latest # Action can only run on Windows
steps:
- name: Publish To WinGet
run: |
$release = '${{ toJSON(github.event.release) }}' | ConvertFrom-Json
$wingetRelevantAsset = $release | Select-Object -Property assets | Where-Object { $_.name -like '*.msi' } | Select-Object -First 1
$regex = [Regex]::New($env:VERSION_REGEX)
$version = $regex.Match($release.tag_name).Groups[1].Value
$wingetPackage = "Exidex.Gauntlet"
& curl.exe -JLO https://aka.ms/wingetcreate/latest
& .\wingetcreate.exe update $wingetPackage -s -v $version -u $wingetRelevantAsset.browser_download_url

3
.gitignore vendored
View file

@ -1,3 +1,6 @@
.idea
/target
/node_modules
/.direnv
result
.DS_Store

3
.gitmodules vendored
View file

@ -1,3 +0,0 @@
[submodule "tools"]
path = tools
url = https://github.com/project-gauntlet/tools

1
.npmrc Normal file
View file

@ -0,0 +1 @@
@jsr:registry=https://npm.jsr.io

View file

@ -3,12 +3,534 @@
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project doesn't adhere to Semantic Versioning, see [Versioning](./README.md#versioning)
and this project doesn't adhere to Semantic Versioning, see [Versioning](https://gauntlet.sh/docs/information/versioning)
For changes in `@project-gauntlet/tools` see [separate CHANGELOG.md](https://github.com/project-gauntlet/tools/blob/main/CHANGELOG.md)
## [Unreleased]
## [21] - 2025-08-16
### General
- When opening `Opened windows` view second item is now focused by default
- Because the window ordering is "most recently focused on the top", second can be considered as an "alternative" application that was focused before the last one
- Implemented native hud notifications on Linux
- Enabled by default
- `linux.native_hud` boolean configuration option is available to disable this
- Restricted JavaScript runtime heap size to 50 MB per plugin
### Plugins
- It is now possible to programmatically control which item in grid/list is focused
- `<List/>` and `<Grid/>` now have new property `focusedItemId`
- If `focusedItemId` property is `undefined` the focus is uncontrolled
- if `focusedItemId` property is `null` the focus is controlled and unset
- if `focusedItemId` property is `string` the focus is controlled and set to item with specified `id`
- Refine nullability of event function arguments on React components
- **BREAKING CHANGE**: Following function properties now return `null` as an argument instead of `undefined`
- `<Action/>`'s `onAction`
- `<List/>`'s `onItemFocusChange`
- `<Gird/>`'s `onItemFocusChange`
- For following function property arguments `undefined` was removed from type signature
- `<SearchBar/>`'s `onChange`
- `<TextField/>`'s `onChange`
- `<PasswordField/>`'s `onChange`
- `<Select/>`'s `onChange`
### UI/UX improvements
- Text in main view search results, plugin view, action panel and bottom panel right side is now smaller
- Removed padding from content paragraph text
- Keyboard shortcuts in UI now use Lucide icons
### Fixes
- Settings are now part of the main application instead of the separate process
- Only single settings window can now exist at the same time.
- Fixed inline view being recreated each time key is pressed in search bar, causing useRef not preserve the value
- Fixed back navigation being treated as whole separate view session
- This also fixed state leaking between views when changing views very quickly
- Fixed panic if state of the widget under specific id changed type
- Fixed blurry window on Linux Wayland LayerShell due to missing fractional scaling support
- Reworked viewport scrolling when using keyboard navigation
- Fixed incorrect scrolling distance for long lists/grids
- Fixed scrolling position sometimes not being reset correctly
- Fixed `Opened Windows` view often not showing any windows
## [20] - 2025-07-07
### General
- Linux Gnome Wayland support
- `zwlr_layer_shell_v1` Wayland protocol is no longer required. It is still preferred, but if not supported application falls back to regular `xdg_shell` window
- Added `wayland.main_window_surface` config option to allow customization of this behavior
- Linux Wayland LayerShell improvements
- **BREAKING CHANGE**: Changed LayerShell surface namespace from `Gauntlet` to `gauntlet` for main window, and set namespace to `gauntlet-hud` for hud window
- Migrated to yet another LayerShell implementation
- Fixes event/keystroke duplication after suspend
- Disabled global shortcuts by default on Linux Wayland
- Added `wayland.global_shortcuts_api` config option to allow usage of legacy x11 api if supported by given environment
- Global Shortcuts XDG Portal is not and will not be supported until there will be major changes to it
- Input Method Editor (IME) support for input fields
- Changed action panel shortcut from <kbd>ALT</kbd> + <kbd>K</kbd> to <kbd>CTRL</kbd> + <kbd>K</kbd> (Windows/Linux) and <kbd>CMD</kbd> + <kbd>K</kbd> (macOS), to match similar pattern in other apps
- `gauntlet open` CLI command now hides window when executed while window is open, matching behavior of global shortcut
- Lots of internal dependency updates
### Plugins
- Updated Deno to 2.3.3
- **BREAKING CHANGE**: Remove `<DatePicker/>` component
- It caused difficulties when updating dependencies and needs a complete rework
### Fixes
- Reduced glibc requirement from 2.38 to 2.35
- Fixed systray open main and settings windows actions causing deadlock
- Fixed LayerShell window taking exclusive keyboard focus preventing any desktop interactions while window was open
- Fixed `useStorage` and `useCache` hooks crashing plugin runtime when closing the view
- Fixed `npm run dev` not showing full error cause (contributed by @Gabrielbdd)
- Fixed nix applications not being detected on macOS by following links inside `/Applications` (contributed by @deadbaed)
## [19] - 2025-05-11
### General
- It is now possible to assign custom alias to entrypoints which is used for search
- Windows in "Opened windows" view entrypoint are now sorted following "most recently focused on the top" order
### Plugins
- Plugin manifest property `entrypoint.*.actions.*.shortcut` is now optional
- Added `<Content.Svg/>` component to display SVG images
- **BREAKING CHANGE**: Renamed TypeScript types: `ImageSource` to `DataSource`, `ImageSourceUrl` to `DataSourceUrl`, `ImageSourceAsset` to `DataSourceAsset`
### UI/UX improvements
- Made font size in the Settings UI a little smaller
### Fixes
- Fixed crash when closing inline view due to Action being run, again...
- Fixed shortcut assignment error not being shown for global entrypoint shortcuts
- Fixed crash on X11 when trying to assign shortcut that is already used by another application
- Fixed `icon` in `EntrypointGenerator` requiring `number[]` instead of declared `ArrayBuffer`
- Fixed text selection not being visible when selecting text in form view text fields
- Fixed plugin runtime crash when using `assetDataSync()` function
## [18] - 2025-04-13
### General
- Entrypoints can now be run or opened using global shortcut set Settings UI
- On Linux X11 there is a known bug which will cause a crash and prevent server startup if attempted to register global shortcut that is already registered by another application
- Generated Entrypoints are now listed in the Settings UI
### Plugins
- Added authors field to Plugin Manifest
- `gauntlet.authors.*.name` - String
- `gauntlet.authors.*.uris` - List of strings. URIs that identify the author. Can be a link to social media page or an email (if email it should begin with `mailto:` schema)
- Added `$schema` field to Plugin Manifest which takes URL to the JSON Schema file
- Some editors use it to validate the content of the file
- Currently, the schema file is located inside the repository at path `https://raw.githubusercontent.com/project-gauntlet/gauntlet/refs/heads/main/docs/schema/plugin_manifest.schema.json` but at some point this will change
### Themes
- Tweaked window border color of macOS Dark theme on non-macos platforms to be not as bright
### UI/UX improvements
- Reworked shortcut selector widget in Setting UI
### Fixes
- Fixed window sometimes not being hidden on X11
- Main view search list is now refreshed when window being hidden instead of when it is shown, to avoid the list being changed after window was opened (contributed by @BennoCrafter)
- Fixed plugin view being empty if the window is closed by toggling global shortcut
- Fixes plugin view not being opened properly if it is created using global shortcut or cli command (contributed by @BennoCrafter)
## [17] - 2025-03-15
- Fixed crash when typing/clicking fast in React-created plugin ui
- Fixed events sometimes overwriting other parallel events when typing/clicking fast in React-created plugin ui
- Fixes mouse clicks sometimes being ignored in high refresh rate views
- Fixes keystrokes being rewritten/ignored when holding down keys or typing fast in input fields
- Fixed crash when closing inline view due to Action being run
- Fixed zombie processes being left over after closing Settings UI
## [16] - 2025-02-23
- Fixed application plugin being broken on non-macos platforms
## [15] - 2025-02-22
### General
- Published macOS `.dmg` file is now universal and can run on `x86_64` CPU architecture
- **BREAKING CHANGE**: Name of published macOS `.dmg` file was changed from `gauntlet-aarch64-macos.dmg` to `gauntlet-universal-macos.dmg`
- Pressing global shortcut while window is open now preserves search bar value when window is opened next time (contributed by @Kalin8900)
- Global shortcut now hides the main window if it is already open (contributed by @BennoCrafter)
- Global shortcut is now executed on key press, instead of key release
- It is now possible to run commands and open views using CLI command
- Format: `gauntlet run <plugin-id> <entrypoint-id> <action-id>`
- Action ID can be found in Plugin Manifest
- Action ID option also accepts special values
- `:primary` - to run primary action of the entrypoint
- `:secondary` - to run secondary action of the entrypoint
- Slightly improved `--help` documentation of CLI command
- Added `--version` flag to CLI to display Gauntlet version
- Added localization support for macOS application names (contributed by @BennoCrafter)
- Added plugin preference `Bundle Name Lang` of enum type
- `localized` option - use localized name of bundle if available - this is the default
- `default` option - use default name of bundle (usually english)
- Added shortcut to open Settings UI (contributed by @BennoCrafter)
- <kbd>Ctrl</kbd> + <kbd>,</kbd> on Windows and Linux
- <kbd>Cmd</kbd> + <kbd>,</kbd> on macOS
### UI/UX Improvements
- In main window search result, moved plugin name next to entrypoint name
- In main window search result, displayed type of entrypoint in place of plugin name, use generator entrypoint name if generated
### Fixes
- Fixed database becoming corrupted after changing theme setting, preventing application server startup
- If you encounter this issue you have to remove application database, file which can be found at
- Linux: `/home/alice/.local/share/gauntlet/data.db`
- macOS: `/Users/Alice/Library/Application Support/dev.project-gauntlet.Gauntlet/data.db`
- Windows: `C:\Users\Alice\AppData\Roaming\project-gauntlet\Gauntlet\data\data.db`
- Slightly improved close-on-unfocus behaviour of main window on X11
- On macOS use app stem name as a fallback if the bundle name is empty (contributed by @BennoCrafter)
- Fixes empty names of some apps, like "Creality Print" which have an empty bundle name
- When using active screen setting for window positioning, position calculated is now relative to size of the screen
- Fixes unexpected position when using monitors of different size (contributed by @BennoCrafter)
- Fixed no plugins starting on Windows in release mode
- Fixed all global shortcut registrations failing if one shortcut registration failed
- Fixed error when registering shortcut erroring whole settings window instead of adding an icon
## [14] - 2025-01-19
- Fixed mouse actions like scrolling or clicking not working on macOS
## [13] - 2025-01-19
### General
- Window Tracking
- Gauntlet now tracks opened windows and assigns them to specific application entry in results
- If application has window open, primary action now instead focuses the window
- If there are multiple windows open, primary action opens view which contains list of windows that can be focused
- If application has window open, it is still possible to open new application instance by using separate new action
- It is experimental, and it is possible to disable window tracking by unchecking checkbox in Application entrypoint preferences in Settings UI
- Currently supported on
- Linux X11
- wlroots-based window managers
- Hyprland
- Cosmic
- Added "Show all opened windows" entrypoint to bundled plugin
- Application plugin is now implemented on Windows
- macOS native-like dark and light mode themes are now available
- On macOS theme is now auto-selected based on system theme
- On macOS window can now be dragged to change its position
- Window position is saved and will be used after restart
- Binary size has been reduced by around 40% (contributed by @davfsa)
- Added `main_window.close_on_unfocus` boolean option to config file to disable "close on unfocus" functionality of main window
- Intended to be used when using "window focus follows mouse" functionality of OS, Desktop Environment or Window Manager
- Added option in Settings UI to choose were main window appears when opening it
- Current options
- `Static`
- Window always opens in the same location (on macOS location can be changed by dragging the window)
- `Active Monitor`
- Windows opens on monitor which has currently focused window
- Currently supported only on macOS
- Improve config and theme config error parsing logs
### Theming
- Themes have been reworked
- Removed complex themes
- Removed theme versioning
- Removed sample generation commands
- Themes are now defined in TOML format
- Theme file is located in config directory (varies based on OS) with name `theme.toml`
- Format of theme file has been reworked, see bundled themes for examples
- 3 bundled themes are now available: [Bundled themes](./bundled_themes)
- Legacy (previous default theme)
- macOS Light
- macOS Dark
- It is possible to change theme in Settings UI
- By default, theme is auto-detected to use one of the bundled ones
- Setting is locked if theme config file exists
### Plugins
- Entrypoint Generator improvements
- **BREAKING CHANGE**: Renamed `"command-generator"` entrypoint type into `"entrypoint-generator"`, as well as all types related to it
- **BREAKING CHANGE**: Removed `GeneratedEntrypoint`'s `fn: () => void`
- `actions: GeneratedEntrypointAction[]` field now is required to have at least one element
- It is now possible to specify label displayed on bottom row panel for primary action
- **BREAKING CHANGE**: Renamed `GeneratedEntrypointAction`'s `fn` field into `run`
- It is now possible to have `GeneratedEntrypointAction` which opens view instead of running command by specifying `view` field with value of React `FC` type instead of `run`
- Renamed `GeneratorProps` to `GeneratorContext`
- Added `pluginPreferences` and `entrypointPreferences` properties to `GeneratorContext` to access preferences from Entrypoint Generator
- Added `get: (id: string) => GeneratedEntrypoint | undefined` function to `GeneratorContext` to get added entrypoint
- Added `getAll: () => GeneratedEntrypoint[]` function to `GeneratorContext` to get all added entrypoints
- Generated Entrypoints can now have accessories similar to `<List/>` component
- Removed `pluginPreferences` and `entrypointPreferences` helper functions
- Added `usePluginPreferences` and `useEntrypointPreferences` React Hooks
- Command function now receives `CommandContext` as first argument
- Object contains `pluginPreferences` and `entrypointPreferences` properties to access preferences from Command
- Unified primary and secondary action execution in `<List.Item/>` and `<Grid.Item/>`
- **BREAKING CHANGE**: Removed `onClick` property on `<List.Item/>` and `<Grid.Item/>` components
- **BREAKING CHANGE**: `<List.Item/>` and `<Grid.Item/>` now has how `id: string` required property
- If primary or secondary action is executed when `<List.Item/>` and `<Grid.Item/>` is focused, `onAction` handler first parameter will be value of `id` prop of focused item
- Added `onItemFocusChange?: (itemId: string | undefined) => void` property on `<List.Item/>` and `<Grid.Item/>`. Function is called when focused item changes
- **BREAKING CHANGE**: Renamed `Image` type to `ImageLike` to avoid conflict with `<Image/>` component
- When entrypoint is enabled/disabled or preference value is changed whole plugin runtime is restarted instead of just reloading the search index
- It is now possible to control whether the action closes main window by returning `{ close: true }` object from `onAction` property function
- For `<Inline/>` view and commands (including generated commands) action always closes window without possibility to keep it open
- Improved rejected promise error log
### UI/UX Improvements
- On macOS main window now uses native window decorations
- Show name of generator entrypoint near plugin name of entrypoints generated by it in main view search results
- Improved styling of action panel popup
- Tweaked padding between sections
- Added shadow around it
- Tweak height of `<List.Detail.Metadata/>` to be slightly taller
- Values of fields in `<List.Detail.Metadata/>` are now positioned on the same row as labels
### Fixes
- Fixed one thread having close to 100% CPU usage while main window is hidden
- Fixed icons in main search view sometimes not loading when window is opened or disappearing after scrolling
- Fixed commands in `permissions.exec.command` in Plugin Manifest not being resolved properly
- Fixed zombie processes being left over after plugin runtime is stopped
- Fixed `npm run dev` failing to reload in some cases
- Fixed `<Grid.EmptyView/>` not displaying the image
- Fixed image in `<List.EmptyView/>` being too big so labels are not shown
- Fixed action not being run if `<List/>` or `<Grid/>` view has focused `<SearchBar/>`
- Fixed `npm run dev` failing because of missing log files when run for the first time
## [12] - 2024-12-22
### General
- Each plugin Deno runtime now runs in separate OS process
- Nix package, dev shell and home-manager modules are now available (contributed by @schradert)
- Replaced wayland layer-shell implementation, fixing several long-standing issues
- Fixed icons not being rendered properly
- Fixed <kbd>Ctrl</kbd> + <kbd>A</kbd> shortcut not working in text inputs
- Fixed <kbd>Backspace</kbd> only removing single character at a time
- Improved window handling on macOS
- Main window is now non-activating, so it doesn't take away focus from front-most application
- Main window no longer shows up in macOS Dock panel
- Fixed application not receiving keyboard input if front-most application is using Secure Event Input (e.g. Terminal)
- Main window is now closed automatically when plugin action is executed
- A lot of internal dependency updates
### Plugin API
- Internal JS functions are no longer accessible from plugins
- **BREAKING CHANGE**: Deno updated from `v1.37.0` to `v2.1.1`
- **BREAKING CHANGE**: Clipboard api now uses `ArrayBuffer` instead of `Blob`
- `@project-gauntlet/deno` is deprecated in favor of `@types/deno`
### UI/UX Improvements
- Hud window was moved lower (below main window) on non-wayland platforms
### Fixes
- Fixed possible freeze when spamming keys or buttons in plugin view
- Fixed separator in `<Inline/>` view not being horizontally centered
## [11] - 2024-11-16
### General
- Primary action label on bottom bar is now a clickable button
- It is now possible to unset global shortcut in settings
- Implemented keyboard navigation support for `<List/>` and `<Grid/>`
- Note: `<Grid/>` scrolling while using keyboard navigation is still quite buggy and is work in progress
### Bundled plugin
#### `Applications`
- Applications commands are now automatically added or removed when application is installed or uninstalled respectively
- When loading the list of applications from OS, loading bar and "Indexing..." text in bottom panel is shown in main window
### Plugin API
- Added `<SearchBar/>` component in `<List/>` and `<Grid/>` which is text input field above content of the respective view
- `"command-generator"` entrypoints have been reworked
- Now it is possible to update list of generated entrypoints (add or remove) after the main command generator function has finished running
- **BREAKING CHANGE**: Command Generator entrypoint function now accepts an object with `add: (id: string, data: GeneratedCommand) => void` and `remove: (id: string) => void` functions
- **BREAKING CHANGE**: Command Generator entrypoint function now should return nothing or a cleanup function e.g. close file watcher. Currently, it is called when disabling/enabling any of entrypoints in plugin, but it is not called when whole plugin is stopped
- While generator function itself is running (given that the function is async) the loading bar and "Indexing..." text in bottom panel will be shown in main window
- **BREAKING CHANGE**: Validation of React Component children is now a lot more strict with respect to what amount of children of specific type is allowed
- **BREAKING CHANGE**: `assetData` helper function renamed to `assetDataSync`
- Added async variant of `assetDataSync` helper function named `assetData` which returns `Promise<ArrayBuffer>`
- Added Plugin Environment API
- `Environment.gauntletVersion` - `number`, current Gauntlet version
- `Environment.isDevelopment` - `boolean`, `true` if plugin was added with `npm run dev` as opposed to Settings UI
- `Environment.pluginCacheDir` - `string`, path to plugin cache directory, corresponds to `{common:plugin-cache}` variable in permissions
- `Environment.pluginDataDir` - `string`, path to plugin data directory, corresponds to `{common:plugin-data}` variable in permissions
### Theming API
- Themes slightly reworked
- "Color Theme" is renamed into "Simple Theme"
- **BREAKING CHANGE**: Sample generation CLI command was changed to `gauntlet generate-sample-simple-theme`
- **BREAKING CHANGE**: Theme file is renamed from `color_theme.json` to `simple-theme.json`
- "Everything Theme" or just "Theme" is renamed into "Complex Theme"
- **BREAKING CHANGE**: Sample generation CLI command was changed to `gauntlet generate-sample-complex-theme`
- **BREAKING CHANGE**: Theme file is renamed from `theme.json` to `complex-theme.json`
- It is now possible to customize color, width and radius of borders in Simple Theme
- **BREAKING CHANGE**: Current Simple Theme version increased to `4`
- **BREAKING CHANGE**: Current Complex Theme version increased to `4`
### UI/UX Improvements
- Loading bar is now shown if opening plugin view takes more than 300 milliseconds
- Pressed button state now has distinct styling, providing more clear indication that button was pressed
- When registering global shortcut in settings fails, instead of showing error on whole settings screen now icon with on hover text is shown to the right of the setting field
- If registering global shortcut on application startup fails, error is now also shown in settings
- Fixed padding of grid and list section being too far down if it is first in the view
- Fixed incorrect supported schemas label in Settings UI, http(s), ssh and git are the only supported schemas for plugin IDs
- Better error for not supported plugin ID schemas
- `<Grid.Item/>` height is now dynamic and is based on `<Grid/>` or `<Grid.Section/>` `columns` property
### Fixes
- Fixed global shortcut not working on Windows
- Fixed emojis not working in a lot of places across the application
- Fixed hud window not disappearing on Wayland
- Fixed clipboard operations not working on KDE
- Note: `Clipboard.clear()` or `Clipboard.writeText("")` is still not working on KDE due to upstream bug
- Fixed clipboard operations not working on Wayland
- Fix primary action of first search result being called if primary action of inline view is called using enter key
- Fix scrollable resetting when clicking action panel button in bottom panel
## [10] - 2024-10-13
### General
- Main view now has action bar and action panel
- Action bar displays current primary action depending on focused result item
- <kbd>ALT</kbd> + <kbd>K</kbd> (<kbd>OPT</kbd> + <kbd>K</kbd> on macOS) is available to open action panel
- Content of action panel can be defined by plugins
- `"inline-view"` and `"command-generator"` entrypoint types can now specify custom actions on main view
- Plugin can also provide shortcut that will be available depending on focused result item without opening the action panel
- Primary and secondary actions
- First action in action panel is now considered primary and can be run using <kbd>ENTER</kbd> without opening action panel
- Second action in action panel is now considered secondary and can be run using <kbd>SHIFT</kbd> + <kbd>ENTER</kbd> without opening action panel
- Works for all places that can define actions: `"inline-view"`, `"command-generator"` and `"view"` entrypoint types
- Action panel now supports keyboard navigation
- All bundled plugins are merged into one
- It is now possible to update plugin using "Check for updates" button in settings
### Bundled plugin
#### `Applications`
- Add Flatpak application support on Linux
- Fixed no applications being shown on macOS Sequoia (15)
- Fixed crash on macOS if macOS version only contains two segments, e.g. `15.0` vs `15.0.1`
- Fixed some applications not having icons on macOS
#### `Calculator`
- It is now possible to copy result of calculation using primary action and its shortcut
- After copying, popup is shown to indicate that the result was copied
- Updated `numbat` dependency to [1.14.0](https://github.com/sharkdp/numbat/releases/tag/v1.14.0)
- Notable change: "Add lowercase aliases for currency units"
### Plugin API
- Plugin permissions reworked
- **BREAKING CHANGE**: Plugin manifest property `permissions.ffi` removed
- FFI in Deno is an unstable feature
- May be brought back in future
- **BREAKING CHANGE**: Plugin manifest property `permissions.high_resolution_time` removed
- This is done in preparation for Deno update, newer versions of which removed this permission
- **BREAKING CHANGE**: Plugin manifest property `permissions.fs_read_access` renamed to `permissions.filesystem.read`
- **BREAKING CHANGE**: Plugin manifest property `permissions.fs_write_access` renamed to `permissions.filesystem.write`
- **BREAKING CHANGE**: Plugin manifest property `permissions.run_subprocess` has been split into 2 properties: `permissions.exec.command` and `permissions.exec.executable`
- `command` is for commands on `PATH`, e.g. `"ls"`
- `executable` is for absolute paths to binary, e.g. `"/usr/bin/ls"`
- **BREAKING CHANGE**: Windows-style paths are not allowed in plugins that do not support Windows
- **BREAKING CHANGE**: Unix-style paths are not allowed in plugins that do not support Linux or macOS
- **BREAKING CHANGE**: Plugin manifest property `permissions.network` now can only contain domain and optionally port of URL
- **BREAKING CHANGE**: Path permissions (`permissions.filesystem.read`, `permissions.filesystem.write` and `permissions.exec.executable`) now can only contain absolute paths
- Path permissions (`permissions.filesystem.read`, `permissions.filesystem.write` and `permissions.exec.executable`) can now contain variables which will be replaced at plugin load time
- Examples: `{linux:user-home}/.local/share`, `{common:plugin-cache}/my-plugin-cache`
- Variables can only be used at the beginning of the path
- List of currently available variables
- `{macos:user-home}`
- Resolves to `$HOME`, i.e. `/Users/<username>`
- Only available if plugin supports macOS
- `{linux:user-home}`
- Resolves to `$HOME`, i.e. `/home/<username>`
- Only available if plugin supports Linux
- `{windows:user-home}`
- Resolves to `{FOLDERID_Profile}`, i.e. `C:\Users\<username>`
- Only available if plugin supports Windows
- `{common:plugin-data}`
- On Windows: `{FOLDERID_RoamingAppData}\Gauntlet\data\plugins\<plugin-uuid>`
- On Linux: `$XDG_DATA_HOME/gauntlet/plugins/<plugin-uuid>`
- On macOS: `$HOME/Library/Application Support/dev.project-gauntlet.gauntlet/plugins/<plugin-uuid>`
- `{common:plugin-cache}`
- On Windows: `{FOLDERID_LocalAppData}\Gauntlet\cache\plugins\<plugin-uuid>`
- On Linux: `$XDG_CACHE_HOME/gauntlet/plugins/<plugin-uuid>`
- On macOS: `$HOME/Library/Application Support/dev.project-gauntlet.gauntlet/plugins/<plugin-uuid>`
- `<Grid.Item/>`'s `title` property is now optional
- `<Grid.Item/>` have a new `accessory` property, which provides an ability to specify text and/or icon under the grid cell
- `<List.Item/>` have a new `accessories` property, which provides an ability to specify one or multiple text and/or icon items on the right side of list item
- **BREAKING CHANGE**: `<Action>`'s `title` property renamed to `label`
- Added `entrypoint.icon` plugin manifest property that accepts path to image inside plugin's `assets` directory
- Added `showHud` function that will create a simple popup window with text provided to that function
### Theming API
- **BREAKING CHANGE**: Current color theme version increased to `3`
- **BREAKING CHANGE**: Current everything theme version increased to `3`
### UI/UX Improvements
- Grid styling refined
- Inline view styling refined
- Plugin and entrypoint names of rendered inline view are now shown above that inline view
- Made color of text slightly more bright
- Focused (by keyboard navigation) and hovered (by hovering with mouse) search items now have distinct styling
- Slightly increased size of icons in main search view
- Plugin ID is now shown in sidebar in settings when plugin is selected
- "Remove plugin" button has been moved to the bottom of the sidebar in settings
- In settings required preferences that do not have value provided or do not have default value are now highlighted
- Names of keys of shortcuts were changed from all upper-case to first letter only upper-case
### Fixes
- Fixed panic when trying to stop already stopped plugin
- Fixed crash on macOS if `openssl@v3` library is not installed
- Fixed inline view still being shown after main view was closed and reopened
- Fixed download info panel in settings sometimes going outside of window size and being cut off
## [9] - 2024-09-15
### Plugin API
- New React Hooks
- `usePromise`
- Helper to run promises in a context of React view
- Returns `AsyncState` object which contains `isLoading`, `error` and `data` properties
- `useStorage`
- Helper to store data between entrypoint, plugin and application runs
- Follows API similar to `useState` built-in React Hook
- Uses `localStorage` internally
- `useCache`
- Helper to store data between entrypoint runs but will be reset when plugin or application is restarted
- Follows API similar to `useState` built-in React Hook
- Uses `sessionStorage` internally
- `useCachedPromise`
- Helper to run promises with caching done automatically
- Follows `stale-while-revalidate` caching strategy
- Uses `usePromise` and `useCache` Hooks internally
- `useFetch`
- Helper to run `fetch()` with caching done automatically
- Follows `stale-while-revalidate` caching strategy
- Uses `useCachedPromise` Hook internally
- Add `isLoading` property on `<Detail/>`, `<Form/>`, `<Grid/>` and `<List/>`
- If passed `true` the loading indicator will be shown above view content
- **BREAKING CHANGE**: To use `Clipboard` api, new permission `permissions.clipboard` is required to be specified in plugin manifest
- `permissions.clipboard` manifest property accepts a list that can include one or multiple of `"read"`, `"write"` or `"clear"` values
- **BREAKING CHANGE**: To use plugin entrypoint of type `inline-view`, new permission `permissions.main_search_bar` is required to be specified in plugin manifest
- `permissions.main_search_bar` manifest property accepts a list that can include `"read"` value
- **BREAKING CHANGE**: Plugin and Entrypoint Preference `name` properties in plugin manifest was split into 2 properties
- `preferences.name` is split into `preferences.name` and `preferences.id`
- `entrypoint.preferences.name` is split into `entrypoint.preferences.name` and `entrypoint.preferences.id`
- To preserve value set by user in settings please set the previous value of `name` to `id`
- **BREAKING CHANGE**: Replaced `onSelectionChange` and `id` properties on `<Grid/>` and `<List/>` with `onClick` on `<Grid.Item/>` and `<List.Item/>`
### UI/UX Improvements
- Added <kbd>ALT</kbd> + <kbd>K</kbd> (<kbd>OPT</kbd> + <kbd>K</kbd> on macOS) label to Action Panel button in bottom panel in plugin views
- Refined styling to accommodate this change
- **BREAKING CHANGE**: Current color theme version increased to `2`
- **BREAKING CHANGE**: Current everything theme version increased to `2`
### `Applications` plugin
- Add macOS System settings items like Sound, Network, etc
- Both pre- and post-Ventura macOS settings are supported
- Fixed macOS applications, that are nested more than one directory level deep in `Applications` directory, not being added
### `Calculator` plugin
- Updated `numbat` dependency to [1.13.0](https://github.com/sharkdp/numbat/releases/tag/v1.13.0)
- Enabled currency exchange rate module
### Fixes
- Fix application crash when refreshing plugin via `npm run dev` from tools
- Fix plugin runtime shutting down when exception is thrown inside a promise handler
## [8] - 2024-09-07
### Plugin API
@ -242,4 +764,4 @@ Themes are versioned and only one version is supported at the same time by appli
### Added
- Initial release.
- Initial release.

10437
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,34 +1,99 @@
[package]
name = "gauntlet"
edition = "2021"
edition.workspace = true
repository = "https://github.com/project-gauntlet/gauntlet"
[workspace]
members = [
"rust/management_client",
"rust/client",
"rust/server",
"rust/common",
"rust/common_ui",
"rust/common_plugin_runtime",
"rust/utils",
"rust/utils_macros",
"rust/cli",
"rust/component_model",
"rust/scenario_runner",
"rust/manifest_schema",
"rust/plugin_runtime",
]
[workspace.package]
edition = "2024"
[workspace.dependencies]
#iced = { version = "0.12.4", features = ["tokio", "lazy", "advanced", "image", "multi-window"] }
iced = { git = "https://github.com/project-gauntlet/iced.git", branch = "gauntlet", features = ["tokio", "lazy", "advanced", "image", "multi-window"] }
#iced_aw = { version = "0.9.0", features = ["icons", "date_picker", "floating_element", "wrap", "number_input", "grid", "spinner"] }
iced_aw = { git = "https://github.com/project-gauntlet/iced_aw.git", branch = "gauntlet", default-features = false, features = ["icons", "date_picker", "floating_element", "wrap", "number_input", "grid", "spinner"] }
#iced_table = "0.13.0"
iced_table = { git = "https://github.com/project-gauntlet/iced_table.git", branch = "gauntlet" }
# iced
#iced = { version = "0.13.99", features = ["wgpu", "tiny-skia", "web-colors", "tokio", "lazy", "advanced", "image", "svg"] }
iced = { git = "https://github.com/project-gauntlet/iced.git", branch = "gauntlet-0.13.1", features = ["wgpu", "tiny-skia", "web-colors", "tokio", "lazy", "advanced", "image", "svg"] }
#iced_fonts = { version = "0.2.99", features = ["bootstrap", "lucide"] }
iced_fonts = { git = "https://github.com/project-gauntlet/iced_fonts.git", branch = "gauntlet-0.13.1", features = ["bootstrap", "lucide"] }
# workspaces
gauntlet-common = { path = "./rust/common" }
gauntlet-common-ui = { path = "./rust/common_ui" }
gauntlet-common-plugin-runtime = { path = "./rust/common_plugin_runtime" }
gauntlet-plugin-runtime = { path = "./rust/plugin_runtime" }
gauntlet-client = { path = "./rust/client" }
gauntlet-server = { path = "./rust/server" }
gauntlet-utils = { path = "./rust/utils" }
gauntlet-utils-macros = { path = "./rust/utils_macros" }
gauntlet-component-model = { path = "./rust/component_model" }
gauntlet-scenario-runner = { path = "./rust/scenario_runner" }
# shared
anyhow = { version = "1", features = ["backtrace"] }
tracing = { version = "0.1" }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tokio = { version = "1.42" }
tokio-util = { version = "0.7" }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }
bincode = { version = "2.0.0-rc.3" }
thiserror = { version = "2" }
indexmap = { version = "2.1", features = ["serde"] }
itertools = { version = "0.13" }
regex = { version = "1.9.3" }
futures = { version = "0.3.31" }
image = { version = "0.25" }
once_cell = { version = "1.19" }
tonic = { version = "0.12.3" }
tonic-build = { version = "0.12.3" }
prost = { version = "0.13.4" }
bytes = { version = "1.6.0" }
walkdir = { version = "2.4.0" }
typed-path = { version = "0.10.0" }
interprocess = { version = "2.2.2", features = ["tokio"] }
toml = "0.8"
x11rb = { version = "0.13", features = ["extra-traits"] }
x11rb-async = { version = "0.13", features = ["extra-traits"] }
x11rb-protocol = { version = "0.13" }
smithay-client-toolkit = { version = "0.19.2" }
[dependencies]
cli = { path = "rust/cli" }
gauntlet-cli = { path = "rust/cli" }
[features]
release = ["cli/release"]
scenario_runner = ["cli/scenario_runner"]
release = ["gauntlet-cli/release"]
[profile.release]
opt-level = 1 # something at opt-level 2 breaks deno
[profile.release-size]
inherits = "release"
opt-level = "s"
lto = "thin"
strip = true
#[patch.crates-io]
#iced_fonts = { path = "../iced_fonts" }
#iced = { path = "../iced" }
#iced_debug = { path = "../iced/debug" }
#iced_program = { path = "../iced/program" }
#iced_core = { path = "../iced/core" }
#iced_futures = { path = "../iced/futures" }
#iced_graphics = { path = "../iced/graphics" }
#iced_renderer = { path = "../iced/renderer" }
#iced_runtime = { path = "../iced/runtime" }
#iced_tiny_skia = { path = "../iced/tiny_skia" }
#iced_wgpu = { path = "../iced/wgpu" }
#iced_widget = { path = "../iced/widget" }
#iced_winit = { path = "../iced/winit" }
#winit = { path = "../winit" }

458
README.md
View file

@ -4,420 +4,152 @@
<img align="right" width="100" height="100" src="assets/linux/icon_256.png">
Web-first cross-platform application launcher with React-based plugins.
Web-first cross-platform application launcher with React-based plugins
> [!NOTE]
> Launcher is in active development, expect bugs, missing features, incomplete ux, etc.
>
> There will probably be breaking changes which will be documented in [changelog](CHANGELOG.md).
> [!WARNING]
> The project is no longer being developed.
https://github.com/user-attachments/assets/1aa790dc-fce9-4ac5-97d8-3b81b83acf2e
![image](https://github.com/user-attachments/assets/81339462-9cc3-469e-8cdc-ca74918bceab)
## Demo
Slightly outdated demo
https://github.com/user-attachments/assets/19964ed6-9cd9-48d4-9835-6be04de14b66
## Features
- Plugin-first
- Plugins are written in TypeScript
- Plugins can have the following functionality
- Create UI
- One-shot commands
- Dynamically provide list of one-shot commands
- Render quick "inline" content directly under main search bar based on value in it
- Get content from and add to Clipboard
- Currently, 3 bundled plugins are provided
- Applications: provides list of applications
- Calculator: shows result of mathematical operations directly under main search bar
- Settings: open Gauntlet Settings from Gauntlet itself
- Plugins are distributed as separate branch in Git repository, meaning plugin distribution doesn't need any central
server
- Plugins IDs are just Git Repository URLs
- [React](https://github.com/facebook/react)-based UI for plugins
- Plugins are written in TypeScript
- Extensive plugin API
- Create UI views
- One-shot commands
- Dynamically provide list of one-shot commands
- Render quick "inline" content directly under main search bar based on value in it
- Get content from and add to Clipboard
- Plugins are distributed as separate branch in Git repository, meaning plugin distribution doesn't need any central
server
- Plugins IDs are just Git Repository URLs
- [React](https://github.com/facebook/react)-based UI for plugins
- Implemented using custom React Reconciler (no Electron)
- [iced-rs](https://github.com/iced-rs/iced) is used for UI
- [Deno JavaScript Runtime](https://github.com/denoland/deno)
- Deno allows us to sandbox JavaScript code for better security
- [Deno JavaScript Runtime](https://github.com/denoland/deno)
- Deno allows to sandbox JavaScript plugin code for better security
- Plugins are required to explicitly specify what permissions they need to work
- NodeJS is used to run plugin tooling, but as a plugin developer you will always write code that runs on Deno
- Node.js is used to run plugin tooling, but as a plugin developer you will always write code that runs on Deno
- Designed with cross-platform in mind from the beginning
- Commands and Views can be run/opened using custom global shortcuts
- Custom search alias can be assigned to Commands or Views
- Custom theme support
- Built-in functionality is provided by bundled plugins
- Applications: shows applications installed on the system in search results
- Plugin also tracks windows and which application they belong to, so opening already opened application will by default bring up previously created window
- Not all systems are supported at the moment. See [feature support](https://gauntlet.sh/docs/feature-support)
- Calculator: shows result of mathematical operations directly under main search bar
- Includes converting currency using exchange rates
- Powered by [Numbat](https://github.com/sharkdp/numbat)
- Frecency-based search result ordering
- Frecency is a combination of frequency and recency
- More often the item is used the higher in the result list it will be, but items used a lot in the past will be ranked lower than items used the same amount of times recently
- Designed with cross-platform in mind
- Permissions
- By default, plugins do not have access to host system
- If plugin asked for access to filesystem, env variables, FFI or running commands, it is required to specify
which operating systems it supports.
- If plugin doesn't use filesystem, env variables, ffi or running commands and just uses network and/or UI, it
is cross-platform
- Shortcuts
- Plugins are allowed to use only limited set of keys for shortcuts to support widest possible range of keyboards
- Only upper and lower-case letters, symbols and numbers
- Shortcut can have either `"main"` or `"alternative"` kind so plugins do not need to specify shortcut separately for each OS
- `"main"` shortcut requires following modifiers
- Windows and Linux: <kbd>CTRL</kbd>
- macOS: <kbd>CMD</kbd>
- `"alternative"` shortcut requires following modifiers
- Windows and Linux: <kbd>ALT</kbd>
- macOS: <kbd>OPT</kbd>
- Whether <kbd>SHIFT</kbd> is also required depends on character specified for shortcut, e.g `$` will
require <kbd>SHIFT</kbd> to be pressed, while `4` will not
- Results are matched per word by substring
##### OS Support
###### Implemented
##### Official
- <img src="https://cdn.jsdelivr.net/gh/simple-icons/simple-icons@develop/icons/linux.svg" width="18" height="18" /> Linux X11
- <img src="https://cdn.jsdelivr.net/gh/simple-icons/simple-icons@develop/icons/apple.svg" width="18" height="18" /> macOS M1
- <img src="https://cdn.jsdelivr.net/gh/simple-icons/simple-icons@develop/icons/linux.svg" width="18" height="18" /> Linux
- Both X11 and Wayland (via LayerShell protocol) are supported
- <img src="https://cdn.jsdelivr.net/gh/simple-icons/simple-icons@develop/icons/apple.svg" width="18" height="18" /> macOS
##### Best-effort
- <img src="https://cdn.jsdelivr.net/gh/simple-icons/simple-icons@develop/icons/linux.svg" width="18" height="18" /> Linux Wayland
- <img src="https://img.icons8.com/windows/32/windows-11.png" width="18" height="18" /> Windows
- built-in "Applications" plugin is not yet implemented. See [#9](https://github.com/project-gauntlet/gauntlet/issues/9)
##### UI
###### Implemented
- Detail
- Form
- Action Panel
- List
- Grid
- Separate settings window
- Stack-based Navigation
- Action Shortcuts
- Theming
###### Planned
See [#13](https://github.com/project-gauntlet/gauntlet/issues/13)
- Keyboard only navigation in plugin-views
- Vim motions
##### APIs
###### Implemented
- Preferences
- Inline views under main search bar
- Clipboard
###### Planned
See [#13](https://github.com/project-gauntlet/gauntlet/issues/13)
- <img src="https://cdn.jsdelivr.net/gh/simple-icons/simple-icons@develop/icons/apple.svg" width="18" height="18" /> macOS Intel
## Getting Started
### Create your own plugin
### Install Gauntlet
- Go to [plugin-template](https://github.com/project-gauntlet/plugin-template) and create your own GitHub repo from it.
- Run `npm run dev` to start dev server (requires running application server)
- Dev server will automatically refresh the plugin on any file change
- Do the changes you need
- You can configure plugin using [Plugin manifest](#plugin-manifest)
- Documentation is, at the moment, basically non-existent but TypeScript declarations in `@project-gauntlet/api`
and `@project-gauntlet/deno` should help
- For examples see [Dev Plugin](dev_plugin). It is very busy because it is used for Gauntlet development, but it has examples of pretty much every available API
- Push changes to GitHub
- Run `publish` GitHub Actions workflow to publish plugin to `gauntlet/release` branch
- Profit!
See [Installation](https://gauntlet.sh/docs/installation)
### Global Shortcut
Main window can be opened using global shortcut or CLI command:
- Global Shortcut (can be changed in Settings)
- Windows: <kbd>ALT</kbd> + <kbd>Space</kbd>
- Linux X11: <kbd>Super</kbd> + <kbd>Space</kbd>
- Linux Wayland
- Global shortcut may not be supported, see [feature support](https://gauntlet.sh/docs/feature-support)
- Please use CLI command instead, and invoke it using window manager specific approach
- macOS: <kbd>CMD</kbd> + <kbd>Space</kbd>
- CLI command
- `gauntlet open`
### Install plugin
Plugins are installed in Settings UI. Use Git repository url of the plugin to install it.
Plugins are installed in Settings UI. Use Git repository url of the plugin to install it, e.g. `https://github.com/project-gauntlet/readme-demo-plugin.git`
![](docs/settings_ui.png)
### Install application
### Create your own plugin
#### macOS
Although it is possible to install Gauntlet by using `.dmg` directly, application doesn't have auto-update functionality so it is recommended to install using `brew` package manager.
Brew package: [link](https://formulae.brew.sh/cask/gauntlet)
To install run:
```
brew install --cask gauntlet
```
To start, manually open application.
#### Windows
Although it is possible to install Gauntlet by using `.msi` directly, application doesn't have auto-update functionality so it is recommended to install using `chocolatey` package manager.
Chocolatey package: [link](https://community.chocolatey.org/packages/gauntlet)
To install run:
```
choco install gauntlet
```
To start, manually open application.
#### Arch Linux
AUR package: [link](https://aur.archlinux.org/packages/gauntlet-bin)
To install run:
```
yay -S gauntlet-bin
```
To start `systemd` service run:
```
systemctl --user enable --now gauntlet.service
```
#### Other Linux Distributions
At the moment application is only available for Arch Linux. If you want to create a package for other distributions see [Application packaging for Linux](#application-packaging-for-Linux)
### Global Shortcut
Main window can be opened using global shortcut or CLI command:
- Shortcut:
- Windows: <kbd>ALT</kbd> + <kbd>Space</kbd>
- Linux X11: <kbd>Super</kbd> + <kbd>Space</kbd>
- Linux Wayland: No global shortcut. Please use CLI command
- macOS: <kbd>CMD</kbd> + <kbd>Space</kbd>
- Can be changed in Settings
- CLI command:
- `gauntlet open`
## Configuration
### Plugin manifest
```toml
[gauntlet]
name = 'Plugin Name'
description = """
Plugin description
""" # required
[[preferences]] # plugin preference
name = 'testBool'
type = 'enum' # available values: 'number', 'string,' 'bool', 'enum', 'list_of_strings', 'list_of_numbers', 'list_of_enums'
default = 'item' # type of default depends on type field. Currently, list types have no default
description = "Some preference description"
enum_values = [{ label = 'Item', value = 'item'}] # defines list of available enum values, required for types "enum" and "list_of_enums"
[[entrypoint]]
id = 'ui-view' # id for entrypoint
name = 'UI view' # name of entrypoint
path = 'src/ui-view.tsx' # path to file, default export is expected to be function React Function Component
type = 'view'
description = 'Some entrypoint description' # required
[[entrypoint.preferences]] # entrypoint preference
name = 'boolPreference'
type = 'bool'
default = true
description = "bool preference description"
[[entrypoint.actions]]
id = 'someAction' # id of action, needs to align with value in <Action> "id" property
description = "demo action description"
shortcut = { key = ':', kind = 'main'} # key string only accepts lower and upper-case letters, numbers and symbols. kind can be "main" or "alternative"
[[entrypoint]]
id = 'command-a'
name = 'Command A'
path = 'src/command-a.ts' # path to file, the whole file is a js script
type = 'command'
description = 'Some entrypoint description' # required
[[entrypoint]]
id = 'command-generator'
name = 'Command generator'
path = 'src/command-generator.ts'
type = 'command-generator'
description = 'Some entrypoint description' # required
[[entrypoint]]
id = 'inline-view'
name = 'Inline view'
path = 'src/inline-view.tsx'
type = 'inline-view'
description = 'Some entrypoint description' # required
[permissions] # For allowed values see: https://docs.deno.com/runtime/manual/basics/permissions
environment = ["ENV_VAR_NAME"] # array of strings, if specified requires supported_system to be specified as well
high_resolution_time = false # boolean
network = ["github.com"] # array of strings
ffi = ["path/to/dynamic/lib"] # array of strings, if specified requires supported_system to be specified as well
fs_read_access = ["path/to/something"] # array of strings, if specified requires supported_system to be specified as well
fs_write_access = ["path/to/something"] # array of strings, if specified requires supported_system to be specified as well
run_subprocess = ["program"] # array of strings, if specified requires supported_system to be specified as well
system = ["apiName"] # array of strings, if specified requires supported_system to be specified as well
[[supported_system]]
os = 'linux' # 'linux', 'windows' or 'macos'
```
### Application config
Located at `$XDG_CONFIG_HOME/gauntlet/config.toml` for Linux. Not used at the moment.
## CLI
### Application
The Application has a simple command line interface
- `gauntlet` - starts server
- `gauntlet --minimized` - starts server without opening main window
- `gauntlet open` - opens application window, can be used instead of global shortcut
- `gauntlet settings` - settings, plugin installation and removal, preferences, etc
- `gauntlet generate-sample-color-theme` - generate sample color theme. See: [THEME.md](./docs/THEME.md)
- `gauntlet generate-sample-theme` - generate sample theme. See: [THEME.md](./docs/THEME.md)
### Dev Tools
[`@project-gauntlet/tools`](https://www.npmjs.com/package/@project-gauntlet/tools) contains separate CLI tool for plugin
development purposes. It has following commands:
- `gauntlet dev`
- Starts development server which will automatically refreshed plugin on any file change.
- `gauntlet build`
- Builds plugin
- `gauntlet publish`
- Publishes plugin to separate git branch. Includes `build`
- `publish` assumes some things about git repository, so it is recommended to publish plugin from GitHub Actions
workflow
[Plugin template](https://github.com/project-gauntlet/plugin-template) has nice `npm run` wrappers for them.
See [Getting started with plugin development](https://gauntlet.sh/docs/plugin-development/getting-started)
## Theming
See [THEME.md](./docs/THEME.md)
## Architecture
The Application consists of three parts: server, frontend and settings.
Server is an application that exposes gRPC server.
All plugins run on server.
Each plugin in its own sandboxed Deno Worker.
In plugin manifest it is possible to configure permissions which will allow plugin to have access to filesystem,
network, environment variables, ffi or subprocess execution.
Server saves plugins themselves and state of plugins into SQLite database.
Frontend is GUI application that uses [iced-rs](https://github.com/iced-rs/iced) as a GUI framework.
It is also exposes gRPC server that is used by server to render views
Plugins can create UI using [React](https://github.com/facebook/react).
Server implements custom React Reconciler (similar to React Native) which renders GUI components to frontend.
Server listens on signals from frontend, so when user opens view defined by plugin, frontend sends an open-view request.
Server then receives it, runs React render and React Reconciler
makes requests to the frontend containing information what actually should be rendered.
When a user interacts with the UI by clicking button or entering text into form,
frontend sends events to server to see whether any re-renders are needed.
Settings is a GUI application runs in separate process that communicates with server via gRPC using a simple request-response approach.
Simplified gRPC communication:
![](docs/architecture.png)
Components:
![](docs/architecture-blocks.png)
Each component runs in a separate thread. Main thread is the thread that renders GUI. Each component has its own tokio runtime instance.
Plugins (or rather its compiled state: manifest, js code and assets) are distributed via Git repository in `gauntlet/release` branch (similar to GitHub Pages).
Which means there is no one central place required for plugin distribution.
And to install plugin all you need is Git repository url.
Application defines set of React components to use for plugins.
Creating and validating components involves some boilerplate.
Component model was created for help manage is.
It is essentially a json file which defines what components exist, what properties and event handler they have.
This file is then used
to generate TypeScript typings for `@project-gauntlet/api` and Rust validation code for server and frontend.
## Application packaging for Linux
This section contains a list of things
that could be useful for someone who wants to package application for Linux distribution.
If something is missing, please [create an issue](https://github.com/project-gauntlet/gauntlet/issues).
Application is already packaged for Arch Linux so you can use it as example, see [Arch Linux](#arch-linux)
Relevant CLI commands:
- `$ gauntlet --minimized`
- Server needs to be started when user logs in, e.g. using `systemd` service
- `$ gauntlet open`
- Main windows is usually opened using [global shortcut](#global-shortcut), this CLI command can be used in cases where global shortcut functionality is not available
- `$ gauntlet settings`
- Settings are usually started on demand from Gauntlet itself
`.desktop` sample file can be found [here](assets/linux/gauntlet.desktop)
`systemd` service sample file can be found [here](assets/linux/gauntlet.service)
###### Directories used
- data dir - `$XDG_DATA_HOME/gauntlet` or `$HOME/.local/share/gauntlet`
- contains application state `data.db`
- cache dir - `$XDG_CACHE_HOME/gauntlet` or `$HOME/.cache/gauntlet`
- contains icon cache
- config dir - `$XDG_CONFIG_HOME/gauntlet` or `$HOME/.config/gauntlet`
- contains application config `config.toml`
- application will never do changes to config file
- state dir - `$XDG_STATE_HOME/gauntlet` or `$HOME/.local/state/gauntlet`
- contains log files created by plugin development
- `.desktop` files at locations defined by [Desktop Entry Specification](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html)
Application and Dev Tools use temporary directories:
- Rust: [tempfile crate](https://crates.io/crates/tempfile)
- JS: [NodeJS mkdtemp](https://nodejs.org/api/fs.html#fspromisesmkdtempprefix-options)
X11 API is used to add global shortcut
Client and Setting applications have GUI and therefore use all the usual graphics-related stuff from X11.
Wayland support requires LayerShell protocol `zwlr_layer_shell_v1`.
See [Theming](https://gauntlet.sh/docs/theming)
## Building Gauntlet
You will need:
- NodeJS v18
- NodeJS
- Rust
- Protobuf Compiler
- CMake (not used by the project itself, but is required by a dependency)
- On Linux: `libxkbcommon-dev` (note: name may differ depending on used distribution)
### Dev
To build dev run:
```bash
git submodule update --init
npm ci
npm run build
npm run build-dev-plugin
cargo build
```
In dev (without "release" feature) application will use only directories inside project directory to store state or cache.
In dev (without "release" feature) application will use directories ONLY inside project directory to store state or cache, to avoid messing up global installation
To build release run:
### Not-yet-packaged
To build not-yet-packaged release binary, run:
```bash
git submodule update --init
npm ci
npm run build
cargo build --release --features release
```
### Packaged
To build os-specific package, run one of the following:
macOS:
```bash
npm run build-macos-project --workspace @project-gauntlet/build
```
Windows:
```bash
npm run build-windows-project --workspace @project-gauntlet/build
```
Linux:
```bash
npm run build-linux-project --workspace @project-gauntlet/build
```
But the new version release needs to be done via GitHub Actions
## Versioning
## Contributing
### Application
If you'd like to help build Gauntlet you can do it in more ways than just contributing code:
- Reporting a bug or UI/UX problem
- Creating a plugin
Application uses simple incremental integers starting from `1`.
It doesn't follow the SemVer versioning.
Given application's reliance on plugins, once it is stable,
introducing breaking changes will be done carefully (if at all) and will be given a reasonable grace period to migrate.
SemVer is about a hard cutoff between major versions with breaking changes, which doesn't fit this kind of application.
Before application is declared stable, breaking changes could be done without a grace period.
### Tools
[`@project-gauntlet/tools`](https://www.npmjs.com/package/@project-gauntlet/tools) uses SemVer.
### Plugins
Plugins only have the latest published "version".
For simple problems feel free to open an issue or PR and tackle it yourself.
For more significant changes please contact creators on Discord (invite link on top of README) and discuss first.

View file

@ -1 +1 @@
8
21

View file

@ -2,10 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- openssl, maybe more -->
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<!-- v8 -->
<key>com.apple.security.cs.allow-jit</key>
<true/>

View file

@ -1,19 +0,0 @@
[gauntlet]
name = 'Applications'
description = 'Run applications from your system as commands'
[[entrypoint]]
id = 'default'
name = 'Default'
path = 'src/default.ts'
type = 'command-generator'
description = ''
[[supported_system]]
os = 'linux'
[[supported_system]]
os = 'macos'
[[supported_system]]
os = 'windows'

View file

@ -1,17 +0,0 @@
{
"name": "@project-gauntlet/bundled-plugin-application",
"private": true,
"scripts": {
"build": "gauntlet build",
"dev": "gauntlet dev"
},
"dependencies": {
"@project-gauntlet/api": "file:../../js/api"
},
"devDependencies": {
"@types/react": "^18.2.14",
"@project-gauntlet/deno": "file:../../js/deno",
"@project-gauntlet/tools": "file:../../tools",
"typescript": "^5.3.3"
}
}

View file

@ -1,28 +0,0 @@
import {GeneratedCommand} from "@project-gauntlet/api/helpers";
interface DesktopEntry {
name: string,
icon: ArrayBuffer | undefined,
command: string[],
}
// @ts-expect-error
const denoCore: DenoCore = Deno[Deno.internal].core;
const InternalApi: InternalApi = denoCore.ops;
interface InternalApi {
list_applications(): Promise<DesktopEntry[]>
open_application(command: string[]): void
}
export default async function Default(): Promise<GeneratedCommand[]> {
return (await InternalApi.list_applications())
.map(value => ({
id: `${value.name}-${value.command.join("-")}`,
name: value.name,
icon: value.icon,
fn: () => {
InternalApi.open_application(value.command)
}
}));
}

View file

@ -1,11 +0,0 @@
[gauntlet]
name = 'Calculator'
description = ''
[[entrypoint]]
id = 'default'
name = 'Default'
path = 'src/default.tsx'
type = 'inline-view'
description = ''

View file

@ -1,17 +0,0 @@
{
"name": "@project-gauntlet/bundled-plugin-calculator",
"private": true,
"scripts": {
"build": "gauntlet build",
"dev": "gauntlet dev"
},
"dependencies": {
"@project-gauntlet/api": "file:../../js/api"
},
"devDependencies": {
"@types/react": "^18.2.14",
"@project-gauntlet/deno": "file:../../js/deno",
"@project-gauntlet/tools": "file:../../tools",
"typescript": "^5.3.3"
}
}

View file

@ -1,44 +0,0 @@
import { Content, Icons, Inline } from "@project-gauntlet/api/components";
import { ReactNode } from "react";
// @ts-expect-error
const denoCore: DenoCore = Deno[Deno.internal].core;
const InternalApi: InternalApi = denoCore.ops;
interface InternalApi {
run_numbat(input: string): { left: string, right: string }
}
export default function Default(props: { text: string }): ReactNode | undefined {
const text = props.text;
try {
if (text.length < 3) {
return undefined
}
const { left, right } = InternalApi.run_numbat(text);
if (left == right) {
return undefined
}
return (
<Inline>
<Inline.Left>
<Content.Paragraph>
{left}
</Content.Paragraph>
</Inline.Left>
<Inline.Separator icon={Icons.ArrowRight}/>
<Inline.Right>
<Content.Paragraph>
{right}
</Content.Paragraph>
</Inline.Right>
</Inline>
)
} catch (e) {
return undefined
}
}

View file

@ -0,0 +1,70 @@
[gauntlet]
name = 'Gauntlet'
description = 'Default Gauntlet functionality as a bundled plugin'
[[entrypoint]]
id = 'applications'
name = 'Applications'
path = 'src/applications.tsx'
type = 'entrypoint-generator'
description = 'Run installed applications from your system'
[[entrypoint.preferences]]
id = 'experimentalWindowTracking'
name = 'Experimental Window Tracking'
type = 'bool'
default = true
description = "Enables experimental window tracking"
[[entrypoint.preferences]]
id = "bundleNameLang"
name = "Bundle Name Lang"
type = "enum"
default = "localized"
description = "Language of the bundle name"
enum_values = [
{ label = 'Default', value = 'default' },
{ label = 'Localized', value = 'localized' },
]
[[entrypoint]]
id = 'windows'
name = 'Opened Windows'
path = 'src/windows.tsx'
type = 'view'
description = 'Show all opened windows'
[[entrypoint]]
id = 'settings'
name = 'Gauntlet Settings'
path = 'src/settings.tsx'
type = 'command'
description = 'Open Gauntlet Settings'
[[entrypoint]]
id = 'calculator'
name = 'Calculator'
path = 'src/calculator.tsx'
type = 'inline-view'
description = 'Calculator right under search bar'
[permissions]
main_search_bar = ["read"]
clipboard = ["write"]
[permissions.filesystem]
read = [
# technically only uses locations defined by XDG Desktop Entry Specification, but
# the spec allows for customization via XDG_DATA_DIRS and XDG_DATA_HOME env vars so it can be any path
"/",
"C:\\",
]
[[supported_system]]
os = 'linux'
[[supported_system]]
os = 'macos'
[[supported_system]]
os = 'windows'

View file

@ -0,0 +1,19 @@
{
"name": "@project-gauntlet/bundled-plugin",
"private": true,
"scripts": {
"build": "gauntlet build",
"dev": "gauntlet dev"
},
"dependencies": {
"@project-gauntlet/api": "file:../../js/api",
"@std/async": "npm:@jsr/std__async@^1.0.9",
"@std/fs": "npm:@jsr/std__fs@^1.0.8"
},
"devDependencies": {
"@types/deno": "^2.0.0",
"@project-gauntlet/tools": "git://github.com/project-gauntlet/tools.git#6b77be418d6eb48c4139979ff1b8d0350c5b5268",
"@types/react": "^18.3.18",
"typescript": "^5.7.2"
}
}

View file

@ -0,0 +1,298 @@
import { GeneratedEntrypoint, GeneratorContext } from "@project-gauntlet/api/helpers";
import { walk, WalkOptions } from "@std/fs/walk";
import { debounce } from "@std/async/debounce";
import { current_os, wayland } from "gauntlet:bridge/internal-all";
import { linux_app_from_path, linux_application_dirs, linux_open_application, } from "gauntlet:bridge/internal-linux";
import {
macos_app_from_arbitrary_path,
macos_app_from_path,
macos_application_dirs,
macos_get_localized_language,
macos_major_version,
macos_open_application,
macos_open_setting_13_and_post,
macos_open_setting_pre_13,
macos_settings_13_and_post,
macos_settings_pre_13,
macos_system_applications
} from "gauntlet:bridge/internal-macos";
import { applicationAccessories, applicationActions } from "./window/shared";
import { applicationEventLoopX11, focusX11Window } from "./window/x11";
import { applicationEventLoopWayland, focusWaylandWindow } from "./window/wayland";
import { windows_app_from_path, windows_application_dirs, windows_open_application } from "gauntlet:bridge/internal-windows";
type EntrypointPreferences = { experimentalWindowTracking: boolean, bundleNameLang: "default" | "localized" };
export default async function Applications(context: GeneratorContext<object, EntrypointPreferences>): Promise<void | (() => void)> {
const { add, remove, get, getAll, entrypointPreferences: { experimentalWindowTracking, bundleNameLang } } = context;
switch (current_os()) {
case "linux": {
const cleanup = await genericGenerator<LinuxDesktopApplicationData>(
linux_application_dirs(),
path => linux_app_from_path(path),
(id, data) => {
if (wayland()) {
return {
name: data.name,
actions: applicationActions(
id,
experimentalWindowTracking,
() => {
linux_open_application(id)
},
focusWaylandWindow,
),
accessories: applicationAccessories(id, experimentalWindowTracking),
icon: data.icon, // TODO lazy icons
"__linux__": {
startupWmClass: data.startup_wm_class,
desktopFilePath: data.desktop_file_path
}
}
} else {
return {
name: data.name,
actions: applicationActions(
id,
experimentalWindowTracking,
() => {
linux_open_application(id)
},
focusX11Window,
),
accessories: applicationAccessories(id, experimentalWindowTracking),
icon: data.icon, // TODO lazy icons
"__linux__": {
startupWmClass: data.startup_wm_class,
desktopFilePath: data.desktop_file_path
}
}
}
},
add,
remove,
);
if (experimentalWindowTracking) {
if (wayland()) {
try {
applicationEventLoopWayland(
focusWaylandWindow,
add,
get,
getAll
);
} catch (e) {
console.log("error when setting up wayland application event loop", e)
}
} else {
try {
applicationEventLoopX11(
focusX11Window,
add,
get,
getAll
);
} catch (e) {
console.log("error when setting up x11 application event loop", e)
}
}
}
return cleanup;
}
case "macos": {
let lang: string | undefined;
switch (bundleNameLang) {
case "default": {
lang = undefined;
break;
}
case "localized": {
lang = macos_get_localized_language();
break;
}
default: {
throw new Error("Unknown bundle name type")
}
}
const majorVersion = macos_major_version();
if (majorVersion >= 13) {
for (const setting of macos_settings_13_and_post(lang)) {
add(`settings:${setting.preferences_id}`, {
name: setting.name,
actions: [
{
label: "Open settings",
run: () => {
macos_open_setting_13_and_post(setting.preferences_id)
},
}
],
icon: setting.icon,
})
}
} else {
for (const setting of macos_settings_pre_13()) {
add(`settings:${setting.path}`, {
name: setting.name,
actions: [
{
label: "Open settings",
run: () => {
macos_open_setting_pre_13(setting.path)
},
}
],
icon: setting.icon,
})
}
}
for (const path of macos_system_applications()) {
const app = await macos_app_from_path(path, lang)
if (app) {
switch (app.type) {
case "add": {
let data = app.data;
add(data.path, {
name: data.name,
actions: [
{
label: "Open application",
run: () => {
macos_open_application(data.path)
},
}
],
icon: data.icon,
})
break;
}
}
} else {
console.error(`System application '${path}' was not loaded`)
}
}
return await genericGenerator<MacOSDesktopApplicationData>(
macos_application_dirs(),
path => macos_app_from_arbitrary_path(path, lang),
(_id, data) => ({
name: data.name,
actions: [
{
label: "Open application",
run: () => {
macos_open_application(data.path)
},
}
],
icon: data.icon,
}),
add,
remove,
{ exts: ["app"], maxDepth: 2, followSymlinks: true, }
);
}
case "windows": {
return await genericGenerator<WindowsDesktopApplicationData>(
windows_application_dirs(),
path => windows_app_from_path(path),
(_id, data) => ({
name: data.name,
actions: [
{
label: "Open application",
run: () => {
windows_open_application(data.path)
},
}
],
icon: data.icon,
}),
add,
remove,
{ exts: ["lnk", "exe"], maxDepth: 2 }
);
}
}
}
async function genericGenerator<DATA>(
directoriesToWatch: string[],
appFromPath: (path: string) => Promise<undefined | DesktopPathAction<DATA>>,
commandFromApp: (id: string, data: DATA) => GeneratedEntrypoint,
add: (id: string, data: GeneratedEntrypoint) => void,
remove: (id: string) => void,
walkOpts?: WalkOptions
): Promise<() => void> {
const paths = directoriesToWatch
.filter(path => {
try {
Deno.lstatSync(path)
return true
} catch (err) {
// most frequent error here is NotFound
return false
}
});
for (const path of paths) {
for await (const dirEntry of walk(path, walkOpts)) {
const app = await appFromPath(dirEntry.path);
if (app) {
switch (app.type) {
case "add": {
add(app.id, commandFromApp(app.id, app.data))
break;
}
}
}
}
}
const watcher = Deno.watchFs(paths);
const handle = debounce(
async (event: Deno.FsEvent) => {
switch (event.kind) {
case "create":
case "modify":
case "remove": {
for (const path of event.paths) {
const app = await appFromPath(path);
if (app) {
switch (app.type) {
case "remove": {
remove(app.id)
break;
}
case "add": {
add(app.id, commandFromApp(app.id, app.data))
break;
}
}
}
}
}
}
},
1000
);
// noinspection ES6MissingAwait
(async () => {
for await (const event of watcher) {
handle(event)
}
})();
return () => {
watcher.close()
}
}

View file

@ -0,0 +1,56 @@
import { Action, ActionPanel, Content, Icons, Inline } from "@project-gauntlet/api/components";
import { ReactNode } from "react";
import { Clipboard, showHud } from "@project-gauntlet/api/helpers";
import { run_numbat } from "gauntlet:bridge/internal-all";
export default function Calculator(props: { text: string }): ReactNode | undefined {
const text = props.text;
if (text.length < 3) {
return undefined
}
let result;
try {
result = run_numbat(text);
} catch (e) {
// this view is executed on every key press in main search bar
// when numbat run fails it means expression is not valid so we return here and do not show inline view
return undefined
}
const { left, right } = result;
if (left == right) {
return undefined
}
return (
<Inline
actions={
<ActionPanel>
<Action
label={"Copy result"}
onAction={async () => {
await Clipboard.writeText(right)
showHud("Result copied")
}}
/>
</ActionPanel>
}
>
<Inline.Left>
<Content.H3>
{left}
</Content.H3>
</Inline.Left>
<Inline.Separator icon={Icons.ArrowRight}/>
<Inline.Right>
<Content.H3>
{right}
</Content.H3>
</Inline.Right>
</Inline>
)
}

View file

@ -0,0 +1,5 @@
import { open_settings } from "gauntlet:bridge/internal-all";
export default function Settings(): void {
open_settings()
}

View file

@ -0,0 +1,243 @@
import {
GeneratedEntrypoint,
GeneratedEntrypointAccessory,
GeneratedEntrypointAction,
} from "@project-gauntlet/api/helpers";
import { linux_open_application } from "gauntlet:bridge/internal-linux";
import { useState } from "react";
import { Action, ActionPanel, List } from "@project-gauntlet/api/components";
export function ListOfWindows({ windows, focusWindow, focusSecond }: {
windows: Record<string, OpenWindowData>,
focusWindow: (windowId: string) => void,
focusSecond: boolean
}) {
const knownWindows = readWindowOrder();
const sortedWindows = Object.keys(windows) // sort windows based on array stored on storage
.sort((a, b) => knownWindows.indexOf(a) - knownWindows.indexOf(b));
const [id, setId] = useState<string | null>(
focusSecond ? sortedWindows.at(1) || null : null
);
return (
<List
actions={
<ActionPanel>
<Action
label="Focus window"
onAction={id => {
if (id) {
focusAndSort(id, focusWindow)
return { close: true }
}
}}
/>
</ActionPanel>
}
onItemFocusChange={setId}
focusedItemId={id}
>
{
sortedWindows.map(window => <List.Item key={window} id={window} title={windows[window]!!.title}/>)
}
</List>
)
}
export type OpenWindowData = {
id: string,
title: string,
appId: string
}
export function openWindows(): Record<string, OpenWindowData> {
if ((globalThis as any).__openWindows == undefined) {
(globalThis as any).__openWindows = {}
}
return (globalThis as any).__openWindows
}
export function applicationActions(
id: string,
experimentalWindowTracking: boolean,
openApplication: () => void,
focusWindow: (windowId: string) => void,
): GeneratedEntrypointAction[] {
if (!experimentalWindowTracking) {
return [
{
label: "Open application",
run: () => {
openApplication()
},
}
]
}
const appWindows = Object.fromEntries(
Object.entries(openWindows())
.filter(([_, windowData]) => windowData.appId == id)
)
// TODO ability to close window
const windowCount = Object.keys(appWindows).length;
if (windowCount == 0) {
return [
{
label: "Open application",
run: () => {
openApplication()
},
}
]
} else if (windowCount == 1) {
return [
{
label: "Focus window",
run: () => {
let [windowId] = Object.keys(appWindows);
focusAndSort(windowId!!, focusWindow)
},
},
{
label: "Open new instance",
run: () => {
openApplication()
},
}
]
} else if (windowCount > 1) {
return [
{
label: "Show windows",
view: () => {
return (
<ListOfWindows
windows={appWindows}
focusWindow={windowId => focusWindow(windowId)}
focusSecond={false}
/>
)
}
},
{
label: "Open new instance",
run: () => {
openApplication()
},
}
]
} else {
return []
}
}
export function applicationAccessories(id: string, experimentalWindowTracking: boolean): GeneratedEntrypointAccessory[] {
if (!experimentalWindowTracking) {
return []
}
const appWindows = Object.entries(openWindows())
.filter(([_, windowData]) => windowData.appId == id)
if (appWindows.length == 0) {
return []
} else if (appWindows.length == 1) {
return [{ text: "1 window" }]
} else if (appWindows.length > 1) {
return [{ text: `${appWindows.length} windows` }]
} else {
return []
}
}
export function addOpenWindow(
appId: string,
generatedEntrypoint: GeneratedEntrypoint,
windowId: string,
windowTitle: string,
openApplication: () => void,
focusWindow: (windowId: string) => void,
add: (id: string, data: GeneratedEntrypoint) => void,
) {
if (generatedEntrypoint) {
openWindows()[windowId] = {
id: windowId,
appId: appId,
title: windowTitle
}
const knownWindows = readWindowOrder();
knownWindows.push(windowId);
writeWindowOrder(knownWindows)
add(appId, {
...generatedEntrypoint,
actions: applicationActions(appId, true, openApplication, focusWindow),
accessories: applicationAccessories(appId, true)
})
}
}
export function deleteOpenWindow(
windowId: string,
openApplication: (appId: string) => (() => void),
focusWindow: (windowId: string) => void,
get: (id: string) => GeneratedEntrypoint | undefined,
add: (id: string, data: GeneratedEntrypoint) => void,
) {
const openWindow = openWindows()[windowId];
if (openWindow) {
const generatedEntrypoint = get(openWindow.appId);
delete openWindows()[windowId];
const knownWindows = readWindowOrder();
const newKnownWindows = knownWindows.filter(id => id != windowId)
writeWindowOrder(newKnownWindows)
if (generatedEntrypoint) {
add(openWindow.appId, {
...generatedEntrypoint,
actions: applicationActions(openWindow.appId, true, openApplication(openWindow.appId), focusWindow),
accessories: applicationAccessories(openWindow.appId, true)
})
}
}
}
export function openLinuxApplication(appId: string) {
return () => {
linux_open_application(appId)
}
}
function focusAndSort(windowId: string, focus: (windowId: string) => void): void {
focus(windowId)
// TODO would probably be better if this is based on os focus events
const knownWindows = readWindowOrder();
const newKnownWindows = knownWindows.filter(id => id != windowId)
newKnownWindows.unshift(windowId);
writeWindowOrder(newKnownWindows)
}
const WINDOW_LIST_STORE_KEY = "opened-window-list";
function readWindowOrder(): string[] {
const item = sessionStorage.getItem(WINDOW_LIST_STORE_KEY);
if (item == null || item === "") {
return []
} else {
return item.split(",")
}
}
function writeWindowOrder(windowIds: string[]): void {
return sessionStorage.setItem(WINDOW_LIST_STORE_KEY, windowIds.join(","));
}

View file

@ -0,0 +1,134 @@
import { addOpenWindow, deleteOpenWindow, openLinuxApplication } from "./shared";
import { GeneratedEntrypoint } from "@project-gauntlet/api/helpers";
import { linux_wayland_focus_window, application_wayland_pending_event } from "gauntlet:bridge/internal-linux";
export function focusWaylandWindow(windowId: string) {
linux_wayland_focus_window(windowId)
}
export function applicationEventLoopWayland(
focusWindow: (windowId: string) => void,
add: (id: string, data: GeneratedEntrypoint) => void,
get: (id: string) => GeneratedEntrypoint | undefined,
getAll: () => { [id: string]: GeneratedEntrypoint },
) {
const knownWindows: Record<string, { title: string | undefined, appId: string | undefined }> = { };
// noinspection ES6MissingAwait
(async () => {
// noinspection InfiniteLoopJS
while (true) {
const applicationEvent = await application_wayland_pending_event();
switch (applicationEvent.type) {
case "WindowOpened": {
knownWindows[applicationEvent.window_id] = {
appId: undefined,
title: undefined
}
break;
}
case "WindowClosed": {
delete knownWindows[applicationEvent.window_id]
deleteOpenWindow(applicationEvent.window_id, openLinuxApplication, focusWindow, get, add)
break;
}
case "WindowTitleChanged": {
const windowId = applicationEvent.window_id;
const knownWindow = knownWindows[windowId];
if (knownWindow) {
knownWindow.title = applicationEvent.title;
const windowTitle = knownWindow.title;
const windowAppId = knownWindow.appId;
if (typeof windowAppId == "string") {
addOpenWindowWayland(
windowId,
windowAppId,
windowTitle,
focusWindow,
add,
get,
getAll
)
}
}
break;
}
case "WindowAppIdChanged": {
const windowId = applicationEvent.window_id;
const knownWindow = knownWindows[windowId];
if (knownWindow) {
knownWindow.appId = applicationEvent.app_id;
const windowTitle = knownWindow.title;
const windowAppId = knownWindow.appId;
if (typeof windowTitle == "string") {
addOpenWindowWayland(
windowId,
windowAppId,
windowTitle,
focusWindow,
add,
get,
getAll
)
}
}
break;
}
}
}
})()
}
function addOpenWindowWayland(
windowId: string,
windowAppId: string,
windowTitle: string,
focusWindow: (windowId: string) => void,
add: (id: string, data: GeneratedEntrypoint) => void,
get: (id: string) => GeneratedEntrypoint | undefined,
getAll: () => { [id: string]: GeneratedEntrypoint },
) {
let appId = windowAppId;
let generatedEntrypoint = get(windowAppId);
if (generatedEntrypoint == undefined) {
const startupWmClassToAppId = Object.fromEntries(
Object.entries(getAll())
.map(([appId, generated]): [string | undefined, string] => [((generated as any)["__linux__"]).startupWmClass, appId])
.filter((val): val is [string, string] => {
const [wmClass, _appId] = val
return wmClass != undefined
})
);
const appIdFromWmClass = startupWmClassToAppId[windowAppId];
if (appIdFromWmClass) {
appId = appIdFromWmClass;
generatedEntrypoint = get(appId);
}
}
if (generatedEntrypoint) {
addOpenWindow(
appId,
generatedEntrypoint,
windowId,
windowTitle,
openLinuxApplication(appId),
focusWindow,
add,
)
}
}

View file

@ -0,0 +1,296 @@
import { GeneratedEntrypoint } from "@project-gauntlet/api/helpers";
import { addOpenWindow, deleteOpenWindow, openLinuxApplication } from "./shared";
import { application_x11_pending_event, linux_x11_focus_window } from "gauntlet:bridge/internal-linux";
export type X11WindowData = {
// x11 window id
id: X11WindowId,
// x11 parent window id
parentId: X11WindowId,
// do not show override_redirect windows in list of windows
overrideRedirect: boolean,
// window is visibly only if it and all of its parents are mapped
mapped: boolean,
// _NET_WM_NAME, or WM_NAME if that is not present
title: string,
// WM_CLASS
class: string,
// WM_CLASS
instance: string,
// WM_PROTOCOLS
protocols: X11WindowProtocol[],
// WM_HINTS
windowGroup: X11WindowId | undefined,
// todo icon
// WM_TRANSIENT_FOR
transientFor: string | undefined,
// _NET_WM_WINDOW_TYPE
windowTypes: X11WindowType[],
// _KDE_NET_WM_DESKTOP_FILE or _GTK_APPLICATION_ID
desktopFileName: string | undefined,
}
export function focusX11Window(windowId: string) {
linux_x11_focus_window(windowId)
}
export function applicationEventLoopX11(
focusWindow: (windowId: string) => void,
add: (id: string, data: GeneratedEntrypoint) => void,
get: (id: string) => GeneratedEntrypoint | undefined,
getAll: () => { [id: string]: GeneratedEntrypoint },
) {
const windows: Record<string, X11WindowData> = {};
// noinspection ES6MissingAwait
(async () => {
// noinspection InfiniteLoopJS
while (true) {
const applicationEvent = await application_x11_pending_event();
switch (applicationEvent.type) {
case "Init": {
windows[applicationEvent.id] = {
id: applicationEvent.id,
parentId: applicationEvent.parent_id,
overrideRedirect: applicationEvent.override_redirect,
mapped: applicationEvent.mapped,
class: "",
instance: "",
protocols: [],
title: "",
transientFor: undefined,
windowGroup: undefined,
windowTypes: [],
desktopFileName: undefined,
}
break;
}
case "CreateNotify": {
windows[applicationEvent.id] = {
id: applicationEvent.id,
parentId: applicationEvent.parent_id,
overrideRedirect: applicationEvent.override_redirect,
mapped: false,
class: "",
instance: "",
protocols: [],
title: "",
transientFor: undefined,
windowGroup: undefined,
windowTypes: [],
desktopFileName: undefined,
}
break;
}
case "DestroyNotify": {
delete windows[applicationEvent.id]
deleteOpenWindow(applicationEvent.id, openLinuxApplication, focusWindow, get, add)
break;
}
case "MapNotify": {
const window = windows[applicationEvent.id];
if (window) {
window.mapped = true;
validateAndAddOpenWindow(window, windows, openLinuxApplication, focusWindow, add, getAll)
}
break;
}
case "UnmapNotify": {
const window = windows[applicationEvent.id];
if (window) {
window.mapped = false;
deleteOpenWindow(applicationEvent.id, openLinuxApplication, focusWindow, get, add)
}
break;
}
case "ReparentNotify": {
// for Dolphin FileManger map event doesn't seem to be fired, does reparent imply map?
const window = windows[applicationEvent.id];
if (window) {
window.mapped = true;
validateAndAddOpenWindow(window, windows, openLinuxApplication, focusWindow, add, getAll)
}
break;
}
case "TitlePropertyNotify": {
const window = windows[applicationEvent.id];
if (window) {
window.title = applicationEvent.title;
validateAndAddOpenWindow(window, windows, openLinuxApplication, focusWindow, add, getAll)
}
break;
}
case "ClassPropertyNotify": {
const window = windows[applicationEvent.id];
if (window) {
window.class = applicationEvent.class;
window.instance = applicationEvent.instance;
validateAndAddOpenWindow(window, windows, openLinuxApplication, focusWindow, add, getAll)
}
break;
}
case "HintsPropertyNotify": {
const window = windows[applicationEvent.id];
if (window) {
window.windowGroup = applicationEvent.window_group;
validateAndAddOpenWindow(window, windows, openLinuxApplication, focusWindow, add, getAll)
}
break;
}
case "ProtocolsPropertyNotify": {
const window = windows[applicationEvent.id];
if (window) {
window.protocols = applicationEvent.protocols;
validateAndAddOpenWindow(window, windows, openLinuxApplication, focusWindow, add, getAll)
}
break;
}
case "TransientForPropertyNotify": {
const window = windows[applicationEvent.id];
if (window) {
window.transientFor = applicationEvent.transient_for;
validateAndAddOpenWindow(window, windows, openLinuxApplication, focusWindow, add, getAll)
}
break;
}
case "WindowTypePropertyNotify": {
const window = windows[applicationEvent.id];
if (window) {
window.windowTypes = applicationEvent.window_types;
validateAndAddOpenWindow(window, windows, openLinuxApplication, focusWindow, add, getAll)
}
break;
}
case "DesktopFileNamePropertyNotify": {
const window = windows[applicationEvent.id];
if (window) {
window.desktopFileName = applicationEvent.desktop_file_name;
validateAndAddOpenWindow(window, windows, openLinuxApplication, focusWindow, add, getAll)
}
break;
}
}
}
})()
}
function validateAndAddOpenWindow(
window: X11WindowData,
windows: Record<string, X11WindowData>,
openApplication: (appId: string) => (() => void),
focusWindow: (windowId: string) => void,
add: (id: string, data: GeneratedEntrypoint) => void,
getAll: () => { [id: string]: GeneratedEntrypoint },
) {
if (window.overrideRedirect) {
return;
}
if (window.transientFor != undefined) {
return;
}
if (!window.mapped) {
return;
}
if (!window.windowTypes.includes("Normal")) {
return;
}
let parentWindow = windows[window.parentId]
while (parentWindow != undefined) {
if (!parentWindow.mapped) {
return;
}
parentWindow = windows[parentWindow.parentId]
}
function process(appId: string) {
const generatedEntrypoint = generated[appId];
if (generatedEntrypoint) {
addOpenWindow(
appId,
generatedEntrypoint,
window.id,
window.title,
openApplication(appId),
focusWindow,
add,
)
}
}
const generated = getAll();
let appId = window.desktopFileName;
if (appId) {
process(appId)
return;
}
const startupWmClassToAppId = Object.fromEntries(
Object.entries(generated)
.map(([appId, generated]): [string | undefined, string] => [((generated as any)["__linux__"]).startupWmClass, appId])
.filter((val): val is [string, string] => {
const [wmClass, _appId] = val
return wmClass != undefined
})
);
const appIdFromWmClassInstance = startupWmClassToAppId[window.instance];
if (appIdFromWmClassInstance) {
process(appIdFromWmClassInstance)
return;
}
const appIdFromWmClass = startupWmClassToAppId[window.class];
if (appIdFromWmClass) {
process(appIdFromWmClass)
return;
}
const wmClassInstanceAsAppId = window.instance;
if (wmClassInstanceAsAppId) {
process(wmClassInstanceAsAppId)
return;
}
const wmClassAsAppId = window.class;
if (wmClassAsAppId) {
process(wmClassAsAppId)
return;
}
// https://nicolasfella.de/posts/importance-of-desktop-file-mapping/
// TODO do the rest of heuristics
// OR just tell users to use wayland?
// https://github.com/KDE/plasma-workspace/blob/e2cf987971088640a149d871bcdfe63fa2aae855/libtaskmanager/xwindowtasksmodel.cpp#L519
// https://github.com/GNOME/gnome-shell/blob/8fbaa5e55a8d65454c4d2a6f53ceb8bcaa687af5/src/shell-window-tracker.c#L390
}

View file

@ -0,0 +1,37 @@
import React, { ReactElement } from "react";
import { List } from "@project-gauntlet/api/components";
import { ListOfWindows, openWindows } from "./window/shared";
import { current_os, wayland } from "gauntlet:bridge/internal-all";
import { focusWaylandWindow } from "./window/wayland";
import { focusX11Window } from "./window/x11";
export default function Windows(): ReactElement {
switch (current_os()) {
case "linux": {
if (wayland()) {
return (
<ListOfWindows
windows={openWindows()}
focusWindow={(windowId) => focusWaylandWindow(windowId)}
focusSecond={true}
/>
)
} else {
return (
<ListOfWindows
windows={openWindows()}
focusWindow={(windowId) => focusX11Window(windowId)}
focusSecond={true}
/>
)
}
}
default: {
return (
<List>
<List.Item id="not-supported" title="Not supported on current system"/>
</List>
)
}
}
}

View file

@ -0,0 +1,13 @@
{
"compilerOptions": {
"strict": true,
"module": "ES2022",
"esModuleInterop": true,
"target": "ES2022",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"types": ["@project-gauntlet/typings", "@types/deno"],
"noUncheckedIndexedAccess": true
},
"lib": ["ES2020"]
}

View file

@ -1,11 +0,0 @@
[gauntlet]
name = 'Settings'
description = ''
[[entrypoint]]
id = 'default'
name = 'Open Gauntlet Settings'
path = 'src/default.tsx'
type = 'command'
description = ''

View file

@ -1,17 +0,0 @@
{
"name": "@project-gauntlet/bundled-plugin-settings",
"private": true,
"scripts": {
"build": "gauntlet build",
"dev": "gauntlet dev"
},
"dependencies": {
"@project-gauntlet/api": "file:../../js/api"
},
"devDependencies": {
"@types/react": "^18.2.14",
"@project-gauntlet/deno": "file:../../js/deno",
"@project-gauntlet/tools": "file:../../tools",
"typescript": "^5.3.3"
}
}

View file

@ -1,11 +0,0 @@
// @ts-expect-error
const denoCore: DenoCore = Deno[Deno.internal].core;
const InternalApi: InternalApi = denoCore.ops;
interface InternalApi {
open_settings(): void
}
export default function Default(): void {
InternalApi.open_settings()
}

View file

@ -0,0 +1,23 @@
mode = "dark"
background = [
{ color = "#626974", alpha = 0.3 },
{ color = "#48505B", alpha = 0.5 },
"#333A42",
"#2C323A"
]
text = [
"#DDDFE1",
"#9AA0A6",
"#6B7785",
"#1D242C"
]
[window.border]
radius = 10
width = 1
color = { color = "#48505B", alpha = 0.5 }
[content.border]
radius = 4.0

View file

@ -0,0 +1,23 @@
mode = "dark"
background = [
{ color = "#646464", alpha = 0.5 },
"#373737",
"#2D2D2D",
"#242424",
]
text = [
"#E5E5E5",
"#C8C8C8",
"#969696",
"#323232"
]
[window.border]
radius = 8
width = 1
color = { color = "#383838", alpha = 1 }
[content.border]
radius = 4.0

View file

@ -0,0 +1,23 @@
mode = "light"
background = [
{ color = "#000000", alpha = 0.2 },
"#C8C8C8",
"#D2D2D2",
"#EAEAEA",
]
text = [
{ color = "#000000", alpha = 1.0 },
{ color = "#000000", alpha = 0.847 },
{ color = "#000000", alpha = 0.498 },
{ color = "#000000", alpha = 0.259 },
]
[window.border]
radius = 8
width = 1
color = "#B9B9B9"
[content.border]
radius = 4.0

13
default.nix Normal file
View file

@ -0,0 +1,13 @@
(import (
let
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
nodeName = lock.nodes.root.inputs.flake-compat;
in
fetchTarball {
url =
lock.nodes.${nodeName}.locked.url
or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.${nodeName}.locked.rev}.tar.gz";
sha256 = lock.nodes.${nodeName}.locked.narHash;
}
) {src = ./.;})
.defaultNix

1
dev_data/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
scenarios

View file

@ -1,3 +1,11 @@
[[plugins]]
id = "some-plugin-id"
#[main_window]
#close_on_unfocus = false
[wayland]
#main_window_surface = "xdg_shell"
global_shortcuts_api = "legacy_x11_api"
#global_shortcuts_api = "none"
[linux]
native_hud = true

View file

@ -1 +1,3 @@
logs
logs
local_storage
window_position

View file

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg id="svg2" width="620" height="472" xmlns="http://www.w3.org/2000/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">
<defs id="defs4">
<path id="box1" d="m0 0h77v210h-77z" stroke="#000" stroke-width="2"/>
<path id="box2" d="m0 0h77v60h-77z" stroke="#000" stroke-width="2"/>
</defs>
<path id="bg" d="m0 0h620v472h-620z" fill="#fff"/>
<g font-family="DejaVu Sans, Arial, Helvetica" stroke-width="1" xml:space="preserve">
<g id="g9" transform="translate(2 1)">
<use id="use11" fill="#fff" xlink:href="#box1"/>
<use id="use13" x="77" fill="#ff0" xlink:href="#box1"/>
<use id="use15" x="154" fill="#0ff" xlink:href="#box1"/>
<use id="use17" x="231" fill="#0f0" xlink:href="#box1"/>
<use id="use19" x="308" fill="#f0f" xlink:href="#box1"/>
<use id="use21" x="385" fill="red" xlink:href="#box1"/>
<use id="use23" x="462" fill="#00f" xlink:href="#box1"/>
<use id="use25" x="539" xlink:href="#box1"/>
</g>
<g id="g45" transform="translate(2 220)">
<use id="use47" fill="#0f0" xlink:href="#box2"/>
<use id="use49" x="77" fill="#0f0" xlink:href="#box2"/>
<use id="use51" x="154" fill="#0f0" xlink:href="#box2"/>
<use id="use53" x="231" fill="#0f0" xlink:href="#box2"/>
<use id="use55" x="308" fill="#fff" xlink:href="#box2"/>
<use id="use57" x="385" fill="#fff" xlink:href="#box2"/>
<use id="use59" x="462" fill="#fff" xlink:href="#box2"/>
<use id="use61" x="539" fill="#fff" xlink:href="#box2"/>
<text id="green100" x="30" y="35" fill="#fff">0.59</text>
</g>
<g id="g27" transform="translate(2 280)">
<use id="use29" fill="red" xlink:href="#box2"/>
<use id="use31" x="77" fill="red" xlink:href="#box2"/>
<use id="use33" x="154" fill="#fff" xlink:href="#box2"/>
<use id="use35" x="231" fill="#fff" xlink:href="#box2"/>
<use id="use37" x="308" fill="red" xlink:href="#box2"/>
<use id="use39" x="385" fill="red" xlink:href="#box2"/>
<use id="use41" x="462" fill="#fff" xlink:href="#box2"/>
<use id="use43" x="539" fill="#fff" xlink:href="#box2"/>
<text id="red100" x="20" y="35" fill="#fff">+0.30</text>
</g>
<g id="g63" transform="translate(2 340)">
<use id="use65" fill="#00f" xlink:href="#box2"/>
<use id="use67" x="77" fill="#fff" xlink:href="#box2"/>
<use id="use69" x="154" fill="#00f" xlink:href="#box2"/>
<use id="use71" x="231" fill="#fff" xlink:href="#box2"/>
<use id="use73" x="308" fill="#00f" xlink:href="#box2"/>
<use id="use75" x="385" fill="#fff" xlink:href="#box2"/>
<use id="use77" x="462" fill="#00f" xlink:href="#box2"/>
<use id="use79" x="539" fill="#fff" xlink:href="#box2"/>
<text id="blue100" x="20" y="35" fill="#fff">+0.11</text>
</g>
<g id="g63" transform="translate(2 410)">
<use id="grey100" fill="#fff" xlink:href="#box2"/>
<use id="grey89" x="77" fill="#e3e3e3" xlink:href="#box2"/>
<use id="grey70" x="154" fill="#b2b2b2" xlink:href="#box2"/>
<use id="grey59" x="231" fill="#969696" xlink:href="#box2"/>
<use id="grey41" x="308" fill="#696969" xlink:href="#box2"/>
<use id="grey30" x="385" fill="#4d4d4d" xlink:href="#box2"/>
<use id="grey11" x="462" fill="#1c1c1c" xlink:href="#box2"/>
<use id="grey0" x="539" fill="#000" xlink:href="#box2"/>
<text id="txgrey100" x="20" y="35">100%</text>
<text id="txgrey89" x="102" y="35">89%</text>
<text id="txgrey70" x="179" y="35">70%</text>
<text id="txgrey59" x="256" y="35">59%</text>
<text id="txgrey41" x="333" y="35" fill="#fff">41%</text>
<text id="txgrey30" x="408" y="35" fill="#fff">30%</text>
<text id="txgrey11" x="487" y="35" fill="#fff">11%</text>
<text id="txgrey0" x="569" y="35" fill="#fff">0%</text>
</g>
<text id="text3446-0" x="90" y="184" fill="#fff" font-size="180" stroke-width="4">TEST</text>
<text id="text3446" x="80" y="174" stroke-width="4"><tspan id="tspan3448" x="80" y="174" font-size="180">TEST</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4 KiB

View file

@ -5,11 +5,39 @@ A reasonably long plugin description that doesn't contain any usefull informatio
"""
[[preferences]]
name = 'testBool'
id = 'testBool'
name = 'Test Boolean'
type = 'bool'
default = true
description = "test bool description, not super long description to test things"
[[preferences]]
id = 'testBool2'
name = 'Test Boolean 2'
type = 'bool'
default = true
description = "test bool description"
[[preferences]]
id = 'testBool3'
name = 'Test Boolean 3'
type = 'bool'
default = true
description = "test bool description"
[[preferences]]
id = 'testBool4'
name = 'Test Boolean 4'
type = 'bool'
default = true
description = "test bool description"
[[entrypoint]]
id = 'detail-img-download-view'
name = 'Detail Img Download view'
path = 'src/detail-img-download-view.tsx'
type = 'view'
description = """"""
[[entrypoint]]
id = 'detail-view'
@ -31,42 +59,49 @@ description = "test action description 2"
shortcut = { key = 'B', kind = 'main'}
[[entrypoint.preferences]]
name = 'testBool'
id = 'testBool'
name = 'Test Boolean'
type = 'bool'
default = true
description = "test bool description"
[[entrypoint.preferences]]
name = 'testEnum'
id = 'testEnum'
name = 'Test Enum'
type = 'enum'
default = 'item'
enum_values = [{ label = 'Item', value = 'item'}, { label = 'Item 2', value = 'item_2'}]
description = "test enum description"
[[entrypoint.preferences]]
name = 'testListOfStrings'
id = 'testListOfStrings'
name = 'Test List of Strings'
type = 'list_of_strings'
description = "test list of strings description"
[[entrypoint.preferences]]
name = 'testListOfNumbers'
id = 'testListOfNumbers'
name = 'Test List of Numbers'
type = 'list_of_numbers'
description = "test list of numbers description"
[[entrypoint.preferences]]
name = 'testListOfEnums'
id = 'testListOfEnums'
name = 'Test List of Enums'
type = 'list_of_enums'
description = "test list of enums description"
enum_values = [{ label = 'Item', value = 'item'}, { label = 'Item 2', value = 'item_2'}]
[[entrypoint.preferences]]
name = 'testNum'
id = 'testNum'
name = 'Test Num'
type = 'number'
default = 2
description = "test number description"
[[entrypoint.preferences]]
name = 'testStr'
id = 'testStr'
name = 'Test Str'
type = 'string'
default = 'test_value'
description = "test string description"
@ -93,6 +128,11 @@ path = 'src/inline-view.tsx'
type = 'inline-view'
description = ''
[[entrypoint.actions]]
id = 'testInlineAction'
description = "test action description 1"
shortcut = { key = 'b', kind = 'main'}
[[entrypoint]]
id = 'grid-view'
name = 'Grid view'
@ -107,6 +147,13 @@ path = 'src/list-view.tsx'
type = 'view'
description = ''
[[entrypoint]]
id = 'hooks-view'
name = 'Hooks view'
path = 'src/hooks-view.tsx'
type = 'view'
description = ''
[[entrypoint]]
id = 'command-a'
name = 'Command A'
@ -115,12 +162,39 @@ type = 'command'
description = ''
[[entrypoint]]
id = 'command-generator'
name = 'Command generator'
path = 'src/command-generator.ts'
type = 'command-generator'
id = 'empty-entrypoint'
name = 'Empty Entrypoint'
path = 'src/empty.tsx'
type = 'view'
description = ''
[[entrypoint]]
id = 'empty-list-entrypoint'
name = 'Empty List Entrypoint'
path = 'src/empty-list.tsx'
type = 'view'
description = ''
[[entrypoint]]
id = 'empty-grid-entrypoint'
name = 'Empty Grid Entrypoint'
path = 'src/empty-grid.tsx'
type = 'view'
description = ''
[[entrypoint]]
id = 'entrypoint-generator'
name = 'Entrypoint generator'
path = 'src/entrypoint-generator.tsx'
type = 'entrypoint-generator'
description = ''
[[entrypoint.actions]]
id = 'testGeneratedAction1'
description = "test action description 1"
shortcut = { key = 'b', kind = 'main'}
[[entrypoint]]
id = 'test-list-detail'
name = 'Test List Detail'
@ -128,6 +202,20 @@ path = 'src/test-list-detail.tsx'
type = 'view'
description = ''
[[entrypoint]]
id = 'test-list-focus'
name = 'Test List Focus'
path = 'src/test-list-focus.tsx'
type = 'view'
description = ''
[[entrypoint]]
id = 'test-grid-focus'
name = 'Test Grid Focus'
path = 'src/test-grid-focus.tsx'
type = 'view'
description = ''
[[supported_system]]
os = 'linux'
@ -138,6 +226,23 @@ os = 'macos'
os = 'windows'
[permissions]
environment = ["RUST_LOG"]
environment = ["RUST_LOG", "LD_LIBRARY_PATH"]
system = ["systemMemoryInfo"]
network = ["upload.wikimedia.org"]
network = ["upload.wikimedia.org", "api.github.com"]
clipboard = ["read", "write", "clear"]
main_search_bar = ["read"]
[permissions.filesystem]
read = [
"C:\\ProgramFiles\\test",
"C:/ProgramFiles/test",
"{windows:user-home}\\test",
"{windows:user-home}/test",
"{linux:user-home}/test",
"/etc/test"
]
write = ["/home/exidex/.test"]
[permissions.exec]
command = ["echo"]
executable = ["/usr/bin/ls"]

View file

@ -7,13 +7,13 @@
},
"dependencies": {
"@project-gauntlet/api": "file:../js/api",
"@types/lodash": "^4.14.196",
"@types/lodash": "^4.17.13",
"lodash": "^4.17.21"
},
"devDependencies": {
"@types/react": "^18.2.14",
"@project-gauntlet/deno": "file:../js/deno",
"@project-gauntlet/tools": "file:../tools",
"typescript": "^5.3.3"
"@types/react": "^18.3.18",
"@types/deno": "^2.0.0",
"@project-gauntlet/tools": "git://github.com/project-gauntlet/tools.git#6b77be418d6eb48c4139979ff1b8d0350c5b5268",
"typescript": "^5.7.2"
}
}

View file

@ -1,5 +1,27 @@
import { CommandContext } from "@project-gauntlet/api/helpers";
export default function Command({ pluginPreferences, entrypointPreferences }: CommandContext) {
const env = Deno.env.get("LD_LIBRARY_PATH");
console.log("LD_LIBRARY_PATH:", env);
console.log("pluginPreferences:");
console.dir(pluginPreferences);
console.log("entrypointPreferences:");
console.dir(entrypointPreferences);
const command = new Deno.Command("echo", {
args: ["test"],
env: {
LD_LIBRARY_PATH: ""
}
});
const child = command.outputSync();
const stdout = new TextDecoder().decode(child.stdout);
console.dir(stdout)
export default function Command() {
const systemMemoryInfo = Deno.systemMemoryInfo();
console.dir(systemMemoryInfo)

View file

@ -1,34 +0,0 @@
import { GeneratedCommand } from "@project-gauntlet/api/helpers";
export default function CommandGenerator(): GeneratedCommand[] {
return [
{
id: 'generated-test-1',
name: 'Generated Item 1',
fn: () => {
console.log('generated-test-1')
}
},
{
id: 'generated-test-2',
name: 'Generated Item 2',
fn: () => {
console.log('generated-test-2')
}
},
{
id: 'generated-test-3',
name: 'Generated Item 3',
fn: () => {
console.log('generated-test-3')
}
},
{
id: 'generated-test-4',
name: 'Generated Item 4',
fn: () => {
console.log('generated-test-4')
}
}
]
}

View file

@ -0,0 +1,13 @@
import { ReactElement } from 'react';
import { Detail } from "@project-gauntlet/api/components";
export default function DetailView(): ReactElement {
return (
<Detail>
<Detail.Content>
<Detail.Content.Image source={{ url: "https://github.com/project-gauntlet/gauntlet/blob/main/docs/logo.png?raw=true" }}/>
</Detail.Content>
</Detail>
);
};

View file

@ -1,12 +1,13 @@
import { ReactElement, useEffect, useState } from 'react';
import upperCase from "lodash/upperCase";
import { Action, ActionPanel, Detail, Icons } from "@project-gauntlet/api/components";
import { useNavigation } from "@project-gauntlet/api/hooks";
import { Clipboard, entrypointPreferences, pluginPreferences } from "@project-gauntlet/api/helpers";
import { useEntrypointPreferences, useNavigation, usePluginPreferences } from "@project-gauntlet/api/hooks";
import { Clipboard } from "@project-gauntlet/api/helpers";
async function readFile(url: string): Promise<Blob> {
async function readFile(url: string): Promise<ArrayBuffer> {
const res = await fetch(url);
return await res.blob();
const blob = await res.blob();
return await blob.arrayBuffer();
}
interface DetailViewEntrypointConfig {
@ -54,14 +55,18 @@ export default function DetailView(): ReactElement {
const [count, setCount] = useState(0);
const { pushView } = useNavigation();
const { testBool } = pluginPreferences<{ testBool: boolean }>();
const preferences = entrypointPreferences<DetailViewEntrypointConfig>();
const { testBool } = usePluginPreferences<{ testBool: boolean }>();
const preferences = useEntrypointPreferences<DetailViewEntrypointConfig>();
const env = Deno.env.get("RUST_LOG");
console.log("RUST_LOG:", env);
console.error("DetailView error")
const buf = new Uint8Array(100);
Deno.stdin.read(buf)
.then(value => console.log(`read from stdin: ${value}`));
useEffect(() => {
return () => {
console.log("DetailView useEffect destructor called")
@ -82,23 +87,23 @@ export default function DetailView(): ReactElement {
return (
<Detail
actions={
<ActionPanel title={"Action panel"}>
<ActionPanel title={"Panel title"}>
<Action
title={"Action 1"}
label={"Action 1"}
onAction={() => {
console.log("ActionTest 1")
}}
/>
<ActionPanel.Section title={"Action panel section"}>
<Action
title={"Action 2.1"}
label={"Action 2.1"}
onAction={() => {
console.log("ActionTest 2.1")
}}
/>
<Action
id="testAction1"
title={"Action 2.2"}
label={"Action 2.2"}
onAction={() => {
console.log("ActionTest 2.2")
}}
@ -107,7 +112,7 @@ export default function DetailView(): ReactElement {
<ActionPanel.Section>
<Action
id="testAction2"
title={"Action 3"}
label={"Action 3"}
onAction={() => {
console.log("ActionTest 3")
}}
@ -123,7 +128,6 @@ export default function DetailView(): ReactElement {
<Detail.Content.H5>H5 Title</Detail.Content.H5>
<Detail.Content.H6>H6 Title</Detail.Content.H6>
<Detail.Content.Image source={{ asset: "logo.png" }}/>
<Detail.Content.Image source={{ url: "https://github.com/project-gauntlet/gauntlet/blob/main/docs/logo.png?raw=true" }}/>
<Detail.Content.CodeBlock>Code block Test</Detail.Content.CodeBlock>
<Detail.Content.HorizontalBreak/>
<Detail.Content.Paragraph>
@ -143,6 +147,7 @@ export default function DetailView(): ReactElement {
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.
</Detail.Content.Paragraph>
<Detail.Content.Svg source={{ asset: "image.svg" }}/>
</Detail.Content>
<Detail.Metadata>
<Detail.Metadata.TagList label="Tags 1">
@ -179,7 +184,7 @@ export default function DetailView(): ReactElement {
</Detail.Metadata.TagList.Item>
<Detail.Metadata.TagList.Item
onClick={() => {
readFile("https://upload.wikimedia.org/wikipedia/commons/thumb/6/6a/PNG_Test.png/477px-PNG_Test.png?20240527104658")
readFile("https://github.com/project-gauntlet/gauntlet/blob/main/docs/logo.png?raw=true")
.then(image => Clipboard.write({ "text/plain": "Gauntlet Test 1", "image/png": image }));
}}
>

View file

@ -0,0 +1,12 @@
import { ReactElement } from "react";
import { Grid } from "@project-gauntlet/api/components";
const alderaanImage = "https://static.wikia.nocookie.net/starwars/images/4/4a/Alderaan.jpg/revision/latest?cb=20061211013805"
export default function EmptyListView(): ReactElement {
return (
<Grid>
<Grid.EmptyView title="Nothing here" description="But there was something" image={{ url: alderaanImage }}/>
</Grid>
)
}

View file

@ -3,10 +3,10 @@ import { List } from "@project-gauntlet/api/components";
const alderaanImage = "https://static.wikia.nocookie.net/starwars/images/4/4a/Alderaan.jpg/revision/latest?cb=20061211013805"
export default function Main(): ReactElement {
export default function EmptyListView(): ReactElement {
return (
<List>
<List.EmptyView title={"Nothing here"} description={"But there was something"} image={{ url: alderaanImage }}/>
<List.EmptyView title="Nothing here" description="But there was something" image={{ url: alderaanImage }}/>
</List>
)
}

5
dev_plugin/src/empty.tsx Normal file
View file

@ -0,0 +1,5 @@
import { ReactElement } from "react";
export default function DetailView(): ReactElement {
return <></>
}

View file

@ -0,0 +1,89 @@
import { GeneratorContext, showHud } from "@project-gauntlet/api/helpers";
import { ReactElement } from "react";
import { List } from "@project-gauntlet/api/components";
function ListView(): ReactElement {
return (
<List>
<List.Item id="test-item" title={"Test Item"}/>
</List>
)
}
export default function EntrypointGenerator({ add }: GeneratorContext): void {
add('generated-test-1', {
name: 'Generated Item 1',
actions: [
{
label: "Run Generated Item 1",
run: () => {
new Promise(() => {
throw new Error("gen")
})
console.log('generated-test-1')
}
}
]
})
add('generated-test-2', {
name: 'Generated Item 2',
actions: [
{
label: "Run Generated Item 2",
run: () => {
console.log('generated-test-2')
sessionStorage.setItem("test", "test")
console.dir(sessionStorage.getItem("test"))
localStorage.setItem("test", "test")
console.dir(localStorage.getItem("test"))
},
},
{
label: "Test 1",
run: () => {
console.log('generated-action-1')
}
},
{
ref: "testGeneratedAction1",
label: "Test 2",
run: () => {
console.log('generated-action-2')
}
}
]
})
add('generated-test-3', {
name: 'Generated Item 3',
actions: [
{
label: "Run Generated Item 3",
run: () => {
showHud("HUD test display")
console.log('generated-test-3')
},
}
]
})
add('generated-test-4', {
name: 'Generated Item 4',
actions: [
{
label: "Run Generated Item 4",
view: () => <ListView/>
}
],
accessories: [
{
text: "Accessory"
}
],
})
}

View file

@ -13,20 +13,20 @@ export default function FormView(): ReactElement {
actions={
<ActionPanel title={"Action panel"}>
<Action
title={"Action 1"}
label={"Action 1"}
onAction={() => {
console.log("ActionTest Form 1")
}}
/>
<Action
title={"Action 2"}
label={"Action 2"}
onAction={() => {
console.log("ActionTest Form 2")
}}
/>
<Action
id="testAction"
title={"Action 3"}
label={"Action 3"}
onAction={() => {
console.log("ActionTest Form 3")
}}
@ -64,12 +64,6 @@ export default function FormView(): ReactElement {
<Form.Select.Item value={"select_item_3"}>Select Item 3</Form.Select.Item>
<Form.Select.Item value={"select_item_4"}>Select Item 4</Form.Select.Item>
</Form.Select>
<Form.DatePicker
label={"What is your birthday?"}
onChange={value => {
console.log(`uncontrolled value: ${value}`)
}}
/>
<Form.Separator/>
{/* controlled */}
<Form.TextField
@ -105,12 +99,6 @@ export default function FormView(): ReactElement {
<Form.Select.Item value={"default_selected_item"}>Default Select Item</Form.Select.Item>
<Form.Select.Item value={"select_item_4"}>Select Item 4</Form.Select.Item>
</Form.Select>
<Form.DatePicker
value={"2024-03-22"}
onChange={value => {
console.log(`controlled value: ${value}`)
}}
/>
</Form>
);
};

View file

@ -1,24 +1,26 @@
import { Grid } from "@project-gauntlet/api/components";
import { ReactElement } from "react";
import { Grid, IconAccessory, Icons, List, TextAccessory } from "@project-gauntlet/api/components";
import { ReactElement, useState } from "react";
import { useStorage } from "@project-gauntlet/api/hooks";
export default function GridView(): ReactElement {
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
const [val1, setValue1] = useStorage("grid-view-test-1", undefined);
const [val2, setValue2] = useStorage("grid-view-test-2", { " test": "test" });
const [val3, setValue3] = useStorage("grid-view-test-3", "");
const [val4, setValue4] = useStorage<string>("grid-view-test-4", "");
const [searchText, setSearchText] = useState<string | undefined>("");
return (
<Grid>
{
numbers.map(value => (
<Grid.Item id={"id" + value} title={"Title " + value}>
<Grid.Item.Content>
<Grid.Item.Content.Paragraph>
Test Paragraph {value}
</Grid.Item.Content.Paragraph>
</Grid.Item.Content>
</Grid.Item>
))
}
<Grid.Section title="Section 1">
<Grid.Item id="id section 1 1" title="Title Section 1 1">
<Grid.SearchBar
placeholder={"Search something..."}
value={searchText}
onChange={setSearchText}
/>
<Grid.Section title="Section Before">
<Grid.Item id="section-title" title="Section Title">
<Grid.Item.Content>
<Grid.Item.Content.Paragraph>
Test Paragraph Section 1 1
@ -26,21 +28,134 @@ export default function GridView(): ReactElement {
</Grid.Item.Content>
</Grid.Item>
</Grid.Section>
{
numbers.map(value => {
const title = "Title " + value;
if (title.toLowerCase().includes(searchText?.toLowerCase() ?? "")) {
return (
<Grid.Item id={"title-" + value} title={"Title " + value}>
<Grid.Item.Content>
<Grid.Item.Content.Paragraph>
Test Paragraph {value}
</Grid.Item.Content.Paragraph>
</Grid.Item.Content>
</Grid.Item>
)
} else {
return undefined
}
})
}
<Grid.Section title="Section 1">
<Grid.Item id="title-section-1-1" title="Title Section 1 1">
<Grid.Item.Content>
<Grid.Item.Content.Paragraph>
Test Paragraph Section 1 1
</Grid.Item.Content.Paragraph>
</Grid.Item.Content>
</Grid.Item>
<Grid.Item id="title-section-1-2" title="Title Section 1 2">
<Grid.Item.Content>
<Grid.Item.Content.Paragraph>
Test Paragraph Section 1 2
</Grid.Item.Content.Paragraph>
</Grid.Item.Content>
</Grid.Item>
<Grid.Item id="title-section-1-3" title="Title Section 1 3">
<Grid.Item.Content>
<Grid.Item.Content.Paragraph>
Test Paragraph Section 1 3
</Grid.Item.Content.Paragraph>
</Grid.Item.Content>
</Grid.Item>
</Grid.Section>
<Grid.Section title="Section 2">
<Grid.Item id="id section 2 1" title="Title Section 2 1">
<Grid.Item id="title-section-2-1" title="Title Section 2 1">
<Grid.Item.Content>
<Grid.Item.Content.Paragraph>
Test Paragraph Section 2 1
</Grid.Item.Content.Paragraph>
</Grid.Item.Content>
</Grid.Item>
<Grid.Item id="id section 2 2" title="Title Section 2 2">
<Grid.Item
id="title-section-2-2"
title="Title Section"
subtitle="Test subtitle"
accessory={<IconAccessory icon={Icons.Wallet} tooltip="Tooltip"/>}
>
<Grid.Item.Content>
<Grid.Item.Content.Paragraph>
Test Paragraph Section 2 2
</Grid.Item.Content.Paragraph>
</Grid.Item.Content>
</Grid.Item>
<Grid.Item id="title-section-2-3" accessory={<IconAccessory icon={Icons.Sun} tooltip="Tooltip"/>}>
<Grid.Item.Content>
<Grid.Item.Content.Paragraph>
Test Paragraph Section 2 3
</Grid.Item.Content.Paragraph>
</Grid.Item.Content>
</Grid.Item>
<Grid.Item id="title-section-2-4" title="Title Section 2 4">
<Grid.Item.Content>
<Grid.Item.Content.Paragraph>
Test Paragraph Section 2 3
</Grid.Item.Content.Paragraph>
</Grid.Item.Content>
</Grid.Item>
</Grid.Section>
<Grid.Section title="Section 3">
<Grid.Item id="title-section-3-1" title="Title Section 3 1">
<Grid.Item.Content>
<Grid.Item.Content.Paragraph>
Test Paragraph Section 2 1
</Grid.Item.Content.Paragraph>
</Grid.Item.Content>
</Grid.Item>
<Grid.Item id="title-section-3-2" title="Title Section" subtitle="Test subtitle">
<Grid.Item.Content>
<Grid.Item.Content.Paragraph>
Test Paragraph Section 2 2
</Grid.Item.Content.Paragraph>
</Grid.Item.Content>
</Grid.Item>
<Grid.Item id="title-section-3-3">
<Grid.Item.Content>
<Grid.Item.Content.Paragraph>
Test Paragraph Section 2 2
</Grid.Item.Content.Paragraph>
</Grid.Item.Content>
</Grid.Item>
<Grid.Item id="title-section-3-4" title="Title Section 3">
<Grid.Item.Content>
<Grid.Item.Content.Paragraph>
Test Paragraph Section 2 3
</Grid.Item.Content.Paragraph>
</Grid.Item.Content>
</Grid.Item>
<Grid.Item id="title-section-3-5" title="Title Section 3">
<Grid.Item.Content>
<Grid.Item.Content.Paragraph>
Test Paragraph Section 2 4
</Grid.Item.Content.Paragraph>
</Grid.Item.Content>
</Grid.Item>
</Grid.Section>
<Grid.Section title="Section Other" columns={8}>
{
Array.from({ length: 50 }, (_, k) => k + 1)
.map(value => (
<Grid.Item id={"title-section-4-" + value}>
<Grid.Item.Content>
<Grid.Item.Content.Paragraph>
Test {value}
</Grid.Item.Content.Paragraph>
</Grid.Item.Content>
</Grid.Item>
)
)
}
</Grid.Section>
</Grid>
)

View file

@ -0,0 +1,421 @@
import { Action, ActionPanel, Element, Icons, List } from "@project-gauntlet/api/components";
import React, { ReactElement, ReactNode, useRef } from "react";
import { useCachedPromise, useFetch, useNavigation, usePromise } from "@project-gauntlet/api/hooks";
export default function ListView(): ReactElement {
const { pushView } = useNavigation();
return (
<List
actions={
<ActionPanel>
<Action label="Run hook" onAction={(id) => pushPrimaryAction(id, pushView)}/>
</ActionPanel>
}
>
<List.Item id="UsePromiseTestBasic" title="UsePromiseTestBasic"/>
<List.Item id="UsePromiseTestExecuteFalse" title="UsePromiseTestExecuteFalse"/>
<List.Item id="UsePromiseTestRevalidate" title="UsePromiseTestRevalidate"/>
<List.Item id="UsePromiseTestAbortableRevalidate" title="UsePromiseTestAbortableRevalidate"/>
<List.Item id="UsePromiseTestMutate" title="UsePromiseTestMutate"/>
<List.Item id="UsePromiseTestMutateOptimistic" title="UsePromiseTestMutateOptimistic"/>
<List.Item id="UsePromiseTestMutateOptimisticRollback" title="UsePromiseTestMutateOptimisticRollback"/>
<List.Item id="UsePromiseTestMutateNoRevalidate" title="UsePromiseTestMutateNoRevalidate"/>
<List.Item id="UsePromiseTestThrow" title="UsePromiseTestThrow"/>
<List.Item id="UseCachedPromiseBasic" title="UseCachedPromiseBasic"/>
<List.Item id="UseCachedPromiseInitialState" title="UseCachedPromiseInitialState"/>
<List.Item id="UseFetchBasic" title="UseFetchBasic"/>
<List.Item id="UseFetchMap" title="UseFetchMap"/>
</List>
)
}
function pushPrimaryAction(id: string | null, pushView: (component: ReactNode) => void) {
switch (id) {
case "UsePromiseTestBasic": {
pushView(<UsePromiseTestBasic/>)
break
}
case "UsePromiseTestExecuteFalse": {
pushView(<UsePromiseTestExecuteFalse/>)
break
}
case "UsePromiseTestRevalidate": {
pushView(<UsePromiseTestRevalidate/>)
break
}
case "UsePromiseTestAbortableRevalidate": {
pushView(<UsePromiseTestAbortableRevalidate/>)
break
}
case "UsePromiseTestMutate": {
pushView(<UsePromiseTestMutate/>)
break
}
case "UsePromiseTestMutateOptimistic": {
pushView(<UsePromiseTestMutateOptimistic/>)
break
}
case "UsePromiseTestMutateOptimisticRollback": {
pushView(<UsePromiseTestMutateOptimisticRollback/>)
break
}
case "UsePromiseTestMutateNoRevalidate": {
pushView(<UsePromiseTestMutateNoRevalidate/>)
break
}
case "UsePromiseTestThrow": {
pushView(<UsePromiseTestThrow/>)
break
}
case "UseCachedPromiseBasic": {
pushView(<UseCachedPromiseBasic/>)
break
}
case "UseCachedPromiseInitialState": {
pushView(<UseCachedPromiseInitialState/>)
break
}
case "UseFetchBasic": {
pushView(<UseFetchBasic/>)
break
}
case "UseFetchMap": {
pushView(<UseFetchMap/>)
break
}
}
}
function actionPanel(runAction?: () => void): Element<typeof ActionPanel> {
const { popView } = useNavigation();
return (
<ActionPanel>
<Action
label="Run hook"
onAction={(itemId) => {
switch (itemId) {
case "go-back": {
popView()
break;
}
case "run": {
runAction?.()
break;
}
}
}}
/>
</ActionPanel>
)
}
function UsePromiseTestBasic(): ReactElement {
const { data, error, isLoading } = usePromise(
async (_one, _two, _three) => await inNSec(5),
[1, 2, 3]
);
printState(data, error, isLoading)
return (
<List actions={actionPanel()} isLoading={isLoading}>
<List.Section title={"Data " + data}>
<List.Item id="go-back" title="Go Back" icon={Icons.Clipboard}/>
</List.Section>
</List>
)
}
function UseCachedPromiseBasic(): ReactElement {
const { data, error, isLoading } = useCachedPromise(
async (_one, _two, _three) => await inNSec(5),
[1, 2, 3]
);
printState(data, error, isLoading)
return (
<List actions={actionPanel()} isLoading={isLoading}>
<List.Section title={"Data " + data}>
<List.Item id="go-back" title="Go Back" icon={Icons.Clipboard}/>
</List.Section>
</List>
)
}
function UseCachedPromiseInitialState(): ReactElement {
const { data, error, isLoading } = useCachedPromise(
async (_one, _two, _three) => await inNSec(5),
[1, 2, 3],
{
initialState: () => "initial"
}
);
printState(data, error, isLoading)
return (
<List actions={actionPanel()} isLoading={isLoading}>
<List.Section title={"Data " + data}>
<List.Item id="go-back" title="Go Back" icon={Icons.Clipboard}/>
</List.Section>
</List>
)
}
function UsePromiseTestExecuteFalse(): ReactElement {
const { data, error, isLoading } = usePromise(
async (_one, _two, _three) => await inNSec(5),
[1, 2, 3],
{
execute: false
}
);
printState(data, error, isLoading)
return (
<List actions={actionPanel()} isLoading={isLoading}>
<List.Section title={"Data " + data}>
<List.Item id="go-back" title="Go Back" icon={Icons.Clipboard}/>
</List.Section>
</List>
)
}
function UsePromiseTestRevalidate(): ReactElement {
const { data, error, isLoading, revalidate } = usePromise(
async (_one, _two, _three) => await inNSec(5),
[1, 2, 3],
);
printState(data, error, isLoading)
return (
<List actions={actionPanel(() => revalidate())} isLoading={isLoading}>
<List.Section title={"Data " + data}>
<List.Item id="run" title="Run" icon={Icons.Sun}/>
<List.Item id="go-back" title="Go Back" icon={Icons.Clipboard}/>
</List.Section>
</List>
)
}
function UsePromiseTestAbortableRevalidate(): ReactElement {
const abortable = useRef<AbortController>();
const { data, error, isLoading, revalidate } = usePromise(
async (_one, _two, _three) => {
await inNSec(5)
},
[1, 2, 3],
{
abortable,
}
);
printState(data, error, isLoading)
return (
<List actions={actionPanel(() => revalidate())} isLoading={isLoading}>
<List.Section title={"Data " + data}>
<List.Item id="run" title="Run" icon={Icons.Sun}/>
<List.Item id="go-back" title="Go Back" icon={Icons.Clipboard}/>
</List.Section>
</List>
)
}
function UsePromiseTestMutate(): ReactElement {
const { data, error, isLoading, mutate } = usePromise(
async (_one, _two, _three) => await inNSec(5),
[1, 2, 3],
);
printState(data, error, isLoading)
const onAction = async () => {
await mutate(inNSec(5))
};
return (
<List actions={actionPanel(onAction)} isLoading={isLoading}>
<List.Section title={"Data " + data}>
<List.Item id="run" title="Run" icon={Icons.Sun}/>
<List.Item id="go-back" title="Go Back" icon={Icons.Clipboard}/>
</List.Section>
</List>
)
}
function UsePromiseTestMutateOptimistic(): ReactElement {
const { data, error, isLoading, mutate } = usePromise(
async (_one, _two, _three) => await inNSec(5),
[1, 2, 3],
);
printState(data, error, isLoading)
const onAction = async () => {
await mutate(
inNSec(5),
{
optimisticUpdate: data1 => data1 + " optimistic",
}
)
};
return (
<List actions={actionPanel(onAction)} isLoading={isLoading}>
<List.Section title={"Data " + data}>
<List.Item id="run" title="Run" icon={Icons.Sun}/>
<List.Item id="go-back" title="Go Back" icon={Icons.Clipboard}/>
</List.Section>
</List>
)
}
function UsePromiseTestMutateOptimisticRollback(): ReactElement {
const { data, error, isLoading, mutate } = usePromise(
async (_one, _two, _three) => await inNSec(5),
[1, 2, 3],
);
printState(data, error, isLoading)
const onAction = async () => {
await mutate(
new Promise<string>((_resolve, reject) => {
setTimeout(
() => {
reject("fail")
},
5 * 1000
);
}),
{
optimisticUpdate: data1 => data1 + " optimistic",
rollbackOnError: data1 => data1 + " failed",
}
);
};
return (
<List actions={actionPanel(onAction)} isLoading={isLoading}>
<List.Section title={"Data " + data}>
<List.Item id="run" title="Run" icon={Icons.Sun}/>
<List.Item id="go-back" title="Go Back" icon={Icons.Clipboard}/>
</List.Section>
</List>
)
}
function UsePromiseTestMutateNoRevalidate(): ReactElement {
const { data, error, isLoading, mutate } = usePromise(
async (_one, _two, _three) => await inNSec(5),
[1, 2, 3],
);
printState(data, error, isLoading)
const onAction = async () => {
await mutate(
inNSec(5),
{
shouldRevalidateAfter: false,
}
)
}
return (
<List actions={actionPanel(onAction)} isLoading={isLoading}>
<List.Section title={"Data " + data}>
<List.Item id="run" title="Run" icon={Icons.Sun}/>
<List.Item id="go-back" title="Go Back" icon={Icons.Clipboard}/>
</List.Section>
</List>
)
}
function UsePromiseTestThrow(): ReactElement {
const { data, error, isLoading } = usePromise(
async (_one, _two, _three) => {
throw new Error("test")
},
[1, 2, 3],
);
printState(data, error, isLoading)
return (
<List actions={actionPanel()} isLoading={isLoading}>
<List.Section title={"Data " + data}>
<List.Item id="go-back" title="Go Back" icon={Icons.Clipboard}/>
</List.Section>
</List>
)
}
function UseFetchBasic(): ReactElement {
interface GithubLatestRelease {
}
const { data, error, isLoading } = useFetch<GithubLatestRelease>(
"https://api.github.com/repos/project-gauntlet/gauntlet/releases/latest"
);
printState(data, error, isLoading)
return (
<List actions={actionPanel()} isLoading={isLoading}>
<List.Section title={"Data " + data}>
<List.Item id="go-back" title="Go Back" icon={Icons.Clipboard}/>
</List.Section>
</List>
)
}
function UseFetchMap(): ReactElement {
interface GithubLatestRelease {
url: string
}
const { data, error, isLoading } = useFetch<GithubLatestRelease, string>(
"https://api.github.com/repos/project-gauntlet/gauntlet/releases/latest",
{
map: result => result.url
}
);
printState(data, error, isLoading)
return (
<List actions={actionPanel()} isLoading={isLoading}>
<List.Section title={"Data " + data}>
<List.Item id="go-back" title="Go Back" icon={Icons.Clipboard}/>
</List.Section>
</List>
)
}
async function inNSec(n: number): Promise<string> {
return new Promise<string>(resolve => {
setTimeout(
() => {
resolve(`Value: ${Math.random()}`)
},
n * 1000
);
})
}
function printState(data: any, error: unknown, isLoading: boolean) {
console.log("")
console.log("=====")
console.dir(data)
console.dir(error)
console.dir(isLoading)
}

View file

@ -1,5 +1,6 @@
import { Content, Icons, Inline } from "@project-gauntlet/api/components";
import { Action, ActionPanel, Content, Icons, Inline } from "@project-gauntlet/api/components";
import { ReactNode } from "react";
import { Clipboard } from "@project-gauntlet/api/helpers";
export default function InlineView(props: { text: string }): ReactNode | undefined {
const text = props.text;
@ -8,7 +9,32 @@ export default function InlineView(props: { text: string }): ReactNode | undefin
}
return (
<Inline>
<Inline
actions={
<ActionPanel>
<Action
label={"Copy content"}
onAction={async () => {
console.log("action test 1")
await Clipboard.writeText("Test Content")
}}
/>
<Action
label={"Test 2"}
onAction={() => {
console.log("action test 2")
}}
/>
<Action
id="testInlineAction"
label={"Test 3"}
onAction={() => {
console.log("action test 3")
}}
/>
</ActionPanel>
}
>
<Inline.Left>
<Content.Paragraph>
Testing inline view left {text}

View file

@ -1,30 +1,66 @@
import { Icons, List } from "@project-gauntlet/api/components";
import { Action, ActionPanel, IconAccessory, Icons, List, TextAccessory } from "@project-gauntlet/api/components";
import { ReactElement, useState } from "react";
import { Environment } from "@project-gauntlet/api/helpers";
export default function ListView(): ReactElement {
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
const [id, setId] = useState("default");
const onClick = (id: string | null) => {
if (id == "print-env") {
console.log(Environment.gauntletVersion);
console.log(Environment.isDevelopment);
console.log(Environment.pluginCacheDir);
console.log(Environment.pluginDataDir);
} else {
console.log("onClick " + id)
}
};
const [searchText, setSearchText] = useState<string | undefined>("");
return (
<List
onSelectionChange={id => {
console.log("onSelectionChange " + id)
setId(id);
}}
>
actions={
<ActionPanel>
<Action label="Run action" onAction={onClick}/>
</ActionPanel>
}>
<List.SearchBar
placeholder={"Search something..."}
value={searchText}
onChange={setSearchText}
/>
<List.Item id="title" title={"Title"} subtitle={"Subtitle"}/>
{
numbers.map(value => (
<List.Item id={"id" + value} title={"Title " + value}/>
))
numbers.map(value => {
const title = "Title " + value;
if (title.toLowerCase().includes(searchText?.toLowerCase() ?? "")) {
return (
<List.Item id={"title-" + value} key={"title-" + value} title={title}/>
)
} else {
return undefined
}
})
}
<List.Section title={"Selected id: " + id}>
<List.Section.Item id="id section 1" title="Title Section 1" icon={Icons.Clipboard}/>
<List.Section title={"Section"} subtitle="Test subtitle">
<List.Section.Item id="title-section-1" title="Title Section 1" icon={Icons.Clipboard}/>
</List.Section>
<List.Section.Item id="print-env" title="Print environment" icon={Icons.Book}/>
<List.Section title="Section 2">
<List.Section.Item id="id section 2 1" title="Title Section 2 1" subtitle="Subtitle 2 1"/>
<List.Section.Item id="id section 2 2" title="Title Section 2 2"/>
<List.Section.Item id="id section 2 3" title="Title Section 2 3" subtitle="Subtitle 2 3"/>
<List.Section.Item id="title-section-2-1" title="Title Section 2 1" subtitle="Subtitle 2 1"/>
<List.Section.Item id="title-section-2-2" title="Title Section 2 2"/>
<List.Section.Item
id="title-section-2-3"
title="Title Section 2 3"
subtitle="Subtitle 2 3"
accessories={[
<TextAccessory text="Accessory" icon={Icons.Alarm} tooltip={"Tooltip"}/>,
<IconAccessory icon={Icons.CloudSnow} tooltip={"Tooltip"}/>
]}
/>
</List.Section>
</List>
)
}
}

View file

@ -0,0 +1,43 @@
import { ReactElement, useState } from "react";
import { Action, ActionPanel, Grid } from "@project-gauntlet/api/components";
export default function Main(): ReactElement {
const [id, setId] = useState<string | null>(null);
const content = (id: string) => (
<Grid.Item.Content>
<Grid.Item.Content.Paragraph>
{id}
</Grid.Item.Content.Paragraph>
</Grid.Item.Content>
);
return (
<Grid
onItemFocusChange={setId}
focusedItemId={id}
actions={
<ActionPanel>
<Action
label={`Focused: ${id}`}
onAction={(id) => {
console.log(id)
setId("condluran")
}}
/>
</ActionPanel>
}
>
<Grid.Item id="adarian" title="Adarian">{content("adarian")}</Grid.Item>
<Grid.Item id="aruzan" title="Aruzan">{content("aruzan")}</Grid.Item>
<Grid.Item id="blutopian" title="Blutopian">{content("blutopian")}</Grid.Item>
<Grid.Item id="caphex" title="Caphex">{content("caphex")}</Grid.Item>
<Grid.Item id="condluran" title="Condluran">{content("condluran")}</Grid.Item>
<Grid.Item id="frozian" title="Frozian">{content("frozian")}</Grid.Item>
<Grid.Item id="evereni" title="Evereni">{content("evereni")}</Grid.Item>
<Grid.Item id="ezaraa" title="Ezaraa">{content("ezaraa")}</Grid.Item>
<Grid.Item id="houk" title="Houk">{content("houk")}</Grid.Item>
<Grid.Item id="inleshat" title="Inleshat">{content("inleshat")}</Grid.Item>
</Grid>
)
}

View file

@ -4,16 +4,16 @@ import { List } from "@project-gauntlet/api/components";
export default function Main(): ReactElement {
return (
<List>
<List.Item id="adarian" title="Adarian"/>
<List.Item id="aruzan" title="Aruzan"/>
<List.Item id="blutopian" title="Blutopian"/>
<List.Item id="caphex" title="Caphex"/>
<List.Item id="condluran" title="Condluran"/>
<List.Item id="frozian" title="Frozian"/>
<List.Item id="evereni" title="Evereni"/>
<List.Item id="ezaraa" title="Ezaraa"/>
<List.Item id="houk" title="Houk"/>
<List.Item id="inleshat" title="Inleshat"/>
<List.Item id="Adarian" title="Adarian"/>
<List.Item id="Aruzan" title="Aruzan"/>
<List.Item id="Blutopian" title="Blutopian"/>
<List.Item id="Caphex" title="Caphex"/>
<List.Item id="Condluran" title="Condluran"/>
<List.Item id="Frozian" title="Frozian"/>
<List.Item id="Evereni" title="Evereni"/>
<List.Item id="Ezaraa" title="Ezaraa"/>
<List.Item id="Houk" title="Houk"/>
<List.Item id="Inleshat" title="Inleshat"/>
<List.Detail>
<List.Detail.Metadata>
<List.Detail.Metadata.Value label={"Designation"}>Sentient</List.Detail.Metadata.Value>
@ -21,11 +21,10 @@ export default function Main(): ReactElement {
<List.Detail.Metadata.Value label={"Homeworld"}>Ezaraa</List.Detail.Metadata.Value>
<List.Detail.Metadata.Value label={"Diet"}>Carnivorous</List.Detail.Metadata.Value>
<List.Detail.Metadata.TagList label={"Appearances"}>
<List.Detail.Metadata.TagList.Item>The Screaming Citadel 1</List.Detail.Metadata.TagList.Item>
<List.Detail.Metadata.TagList.Item>Doctor Aphra (2016) 9</List.Detail.Metadata.TagList.Item>
<List.Detail.Metadata.TagList.Item>Doctor Aphra (2016) 10</List.Detail.Metadata.TagList.Item>
<List.Detail.Metadata.TagList.Item>Doctor Aphra (2016) 11</List.Detail.Metadata.TagList.Item>
<List.Detail.Metadata.TagList.Item>Doctor Aphra (2016) 12</List.Detail.Metadata.TagList.Item>
<List.Detail.Metadata.TagList.Item>Test 9</List.Detail.Metadata.TagList.Item>
<List.Detail.Metadata.TagList.Item>Test 10</List.Detail.Metadata.TagList.Item>
<List.Detail.Metadata.TagList.Item>Test 11</List.Detail.Metadata.TagList.Item>
<List.Detail.Metadata.TagList.Item>Test 12</List.Detail.Metadata.TagList.Item>
</List.Detail.Metadata.TagList>
</List.Detail.Metadata>
<List.Detail.Content>

View file

@ -1,9 +1,11 @@
import { ReactElement } from "react";
import { ReactElement, useState } from "react";
import { List } from "@project-gauntlet/api/components";
export default function Main(): ReactElement {
const [id, setId] = useState<string | null>(null);
return (
<List>
<List onItemFocusChange={setId} focusedItemId={id}>
<List.Item id="adarian" title="Adarian"/>
<List.Item id="aruzan" title="Aruzan"/>
<List.Item id="blutopian" title="Blutopian"/>
@ -14,6 +16,13 @@ export default function Main(): ReactElement {
<List.Item id="ezaraa" title="Ezaraa"/>
<List.Item id="houk" title="Houk"/>
<List.Item id="inleshat" title="Inleshat"/>
<List.Detail>
<List.Detail.Content>
<List.Detail.Content.Paragraph>
Focused: {id}
</List.Detail.Content.Paragraph>
</List.Detail.Content>
</List.Detail>
</List>
)
}

View file

@ -5,8 +5,7 @@
"esModuleInterop": true,
"target": "ES2022",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"types": ["@project-gauntlet/deno"]
"jsx": "react-jsx"
},
"lib": ["ES2020"]
}

View file

@ -1,32 +0,0 @@
# Gauntlet Theming
Gauntlet has extensive theming possibilities
There are 2 types of themes:
- Color only
- Everything: Color, paddings, borders, etc
Unfortunately due to the internally invasive nature of themes, it is perpetually unstable feature.
Themes are versioned and only one version is supported by application at the same time.
Meaning if there were some changes made in the release and theme version was incremented,
theme will stop working until it is updated.
This may change in the future
Current theme version:
- Color: `1`
- Everything: `1`
Theming is only applied to main window and doesn't affect settings
### Creating a custom theme
Gauntlet provides 2 CLI commands to generate sample. Sample is just a default theme that has been saved to file.
- `gauntlet generate-sample-color-theme`
- `gauntlet generate-sample-theme`
Running the command will create sample file, print location of that sample file
and will print location to which theme file will need to be saved to be detected by application
Currently, theme change is only applied after application restart
Any errors in theme parsing will be shown in application logs

View file

@ -1,17 +0,0 @@
@startuml
node "Frontend"
node "Server"
node "Deno worker 1" as Deno1
node "Deno worker 2" as Deno2
node "Deno worker 3" as Deno3
node "Deno worker 4" as Deno4
[Frontend] --> [Server]
[Server] --> [Deno1]
[Server] --> [Deno2]
[Server] --> [Deno3]
[Server] --> [Deno4]
@enduml

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View file

@ -1,31 +0,0 @@
@startuml
'https://www.planttext.com/
== Frontend: Start ==
Frontend -> Server: list of views and commands request
Server --> Frontend: list of views and commands response
== Frontend: Initial View Render ==
Frontend -> Server: open-view event
Server -> Frontend: render components request
Frontend --> Server: render components response
== Frontend: Command Execution ==
Frontend -> Server: execute command
== Frontend: View Update On Event ==
Frontend -> Server: button click, key press in input component, etc
Server -> Frontend: render components request
Frontend --> Server: render components response
== Settings ==
Settings -> Server: request
Server --> Settings: response
@enduml

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

View file

@ -0,0 +1 @@
An accessory with icon and optional tooltip

View file

@ -0,0 +1 @@
Icon of the accessory

View file

@ -0,0 +1 @@
A tooltip shown when the accessory is hovered.

View file

@ -0,0 +1 @@
An accessory with text and optional icon and tooltip

View file

@ -0,0 +1 @@
An icon that is displayed to the left side of the text

View file

@ -0,0 +1 @@
Text of the accessory

View file

@ -0,0 +1 @@
A tooltip shown when the accessory is hovered.

View file

@ -1 +1 @@
The identifier of an action. Referenced in [plugin manifest]($LINK$__PLUGIN_MANIFEST__) to assign shortcut to button in UI
The identifier of an action. Referenced in plugin manifest to assign shortcut to button in UI

View file

@ -1 +1 @@
Function that is called when the button in UI is clicked or the shortcut is pressed
Function that is called when action button is clicked or the shortcut is pressed (including primary and secondary shortcuts). ID parameter is an id of currently focused grid or list item. Returning `{ close: true }` object from the action will close the window

View file

@ -1 +1 @@
Checkbox is a type of form input that produces boolean value, either `true` or `false`
Checkbox is a type of form input that produces boolean value, either "true" or "false"

View file

@ -1,3 +1,3 @@
A container for a set of non-interactable components.
Used in a variety of places like [Detail]($COMPONENT$__DETAIL__), [Inline]($COMPONENT$__INLINE__) and [GridItem]($COMPONENT$__GRID_ITEM__).
Content is a container for a set of non-interactable components.
Used in a variety of places like <Detail/>, <Inline/> and <GridItem/>.
By utilizing the power of React the content can also be made dynamic

View file

@ -1 +0,0 @@
Date Picker is a type of form input that produces date value represented as a string in `YYYY-MM-DD` format

View file

@ -1 +0,0 @@
Text displayed in UI to the left of the date picker itself

View file

@ -1 +0,0 @@
Function that is called when the value of the data picker was changed

View file

@ -1 +0,0 @@
Value of the Date Picker. Can be used to implement controlled form

View file

@ -1 +1 @@
Detail is a root component that contains a possibly dynamic, non-interactable [Content]($COMPONENT$__CONTENT__) and an optional side panel
Detail is a root component that contains a possibly dynamic, non-interactable <Content/> component and an optional side panel

View file

@ -1,2 +1 @@
Allows to define an Action Panel for this view
Every root component has such property
Allows to define an Action Panel for this view. Every root component has such property

View file

@ -0,0 +1 @@
If "true" loading bar is shown above content

View file

@ -1,2 +1 @@
Allows to define an Action Panel for this view
Every root component has such property
Allows to define an Action Panel for this view. Every root component has such property

View file

@ -0,0 +1 @@
If "true" loading bar is shown above content

View file

@ -1,2 +1 @@
Allows to define an Action Panel for this view
Every root component has such property
Allows to define an Action Panel for this view. Every root component has such property

View file

@ -0,0 +1 @@
Sets item with specified id to be the one that is focused. `undefined` - uncontrolled, `null` - controlled and unset, other values - controlled and set

View file

@ -0,0 +1 @@
If "true" loading bar is shown above content

View file

@ -0,0 +1 @@
Function that is called when focused item changes. Argument is an ID of new focused item

View file

@ -1,2 +0,0 @@
Function that will be called when user selects an item on the grid.
The only parameter is an id of the selected grid item

View file

@ -0,0 +1 @@
Accessory displayed on the right bottom of the grid item

View file

@ -1 +1 @@
Identifier of the grid item. Used in the grid event functions
ID of the grid item. Used in Grid's onItemFocusChange event and Action's onAction event

View file

@ -1 +1 @@
Image data. Supported formats: `png`, `gif`, `jpg`, `webp`, `tiff`
Image data. Supported formats: "png", "gif', "jpg", "webp", "tiff"

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