mirror of
https://github.com/project-gauntlet/gauntlet.git
synced 2025-12-23 10:35:53 +00:00
Compare commits
494 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca066da5c9 | ||
|
|
ce76f8211c | ||
|
|
e62a89f45e | ||
|
|
1e8bd2eca9 | ||
|
|
de5166ccae | ||
|
|
1116190f7d | ||
|
|
3516d35a35 | ||
|
|
a577202ec9 | ||
|
|
b008fcd907 | ||
|
|
1a2368ffde | ||
|
|
24405c92bc | ||
|
|
204de4d88c | ||
|
|
4721a70111 | ||
|
|
204e4182c9 | ||
|
|
62a16d04e0 | ||
|
|
41f0fb460a | ||
|
|
e7c1f0343e | ||
|
|
32bf43438c | ||
|
|
0528b96fae | ||
|
|
39bf811b11 | ||
|
|
f82841aad4 | ||
|
|
35afc3b0cd | ||
|
|
dea6aee03a | ||
|
|
3245dcee96 | ||
|
|
f41ab9233b | ||
|
|
136b2c2ab6 | ||
|
|
b4340f4c10 | ||
|
|
72123db351 | ||
|
|
e1f2ca85ec | ||
|
|
242f5a2eea | ||
|
|
729b4f695f | ||
|
|
3cdd16cfb3 | ||
|
|
21f8ab6d09 | ||
|
|
b7b8413146 | ||
|
|
97fc6360ea | ||
|
|
e643dcc4f7 | ||
|
|
771e2f95d7 | ||
|
|
5c2db4d048 | ||
|
|
6bb5f40065 | ||
|
|
94c8984649 | ||
|
|
1c23514aac | ||
|
|
08c5bb2c49 | ||
|
|
a8287ede60 | ||
|
|
3a457450cb | ||
|
|
bced4daedd | ||
|
|
283285c317 | ||
|
|
70e815b795 | ||
|
|
9111dd074b | ||
|
|
d1e61d4d19 | ||
|
|
f529d061ff | ||
|
|
6645ce21ad | ||
|
|
a0bbad9fea | ||
|
|
f20537181e | ||
|
|
71e8adf44d | ||
|
|
f51a123411 | ||
|
|
0d7a9db4d9 | ||
|
|
2afecf3b80 | ||
|
|
29b1a4ab36 | ||
|
|
75fa1f3af5 | ||
|
|
e7f621018c | ||
|
|
3d1beaca15 | ||
|
|
8fd14b4dab | ||
|
|
e1787e7dc8 | ||
|
|
9021f0580e | ||
|
|
a91bf3066f | ||
|
|
f2fadf8b70 | ||
|
|
924fe69069 | ||
|
|
9fe9ccdb6b | ||
|
|
bce1969610 | ||
|
|
f7d7022000 | ||
|
|
e13ff1d688 | ||
|
|
04e0dfd44a | ||
|
|
547f502030 | ||
|
|
d5b4c71a61 | ||
|
|
41107d2043 | ||
|
|
fee4e9a18b | ||
|
|
747cb9435d | ||
|
|
901f8fdfa3 | ||
|
|
30963ffd73 | ||
|
|
1706840b8b | ||
|
|
7e46395152 | ||
|
|
5181a366e7 | ||
|
|
c0f61f444d | ||
|
|
b0fdaa6b9e | ||
|
|
a0275cce28 | ||
|
|
80bb33690b | ||
|
|
a8182f4b2e | ||
|
|
3599837033 | ||
|
|
2a80b51ff5 | ||
|
|
7a47cdb941 | ||
|
|
c9019ca772 | ||
|
|
3992897fcf | ||
|
|
624f37aaeb | ||
|
|
f0912c8e21 | ||
|
|
40fb6f2617 | ||
|
|
3064f7f297 | ||
|
|
5a52e1a9e5 | ||
|
|
2ddd9c8bf0 | ||
|
|
4ab582ddad | ||
|
|
4aec25397a | ||
|
|
80396abbad | ||
|
|
44d594c395 | ||
|
|
ba64162222 | ||
|
|
5b4f94cfc6 | ||
|
|
b6c0b780db | ||
|
|
f204969e63 | ||
|
|
59307623ca | ||
|
|
ebcee6a470 | ||
|
|
9abbc1909d | ||
|
|
c403a5a386 | ||
|
|
417ed8a13a | ||
|
|
affeccceb1 | ||
|
|
ace5a874e3 | ||
|
|
1a1c862973 | ||
|
|
87d6b1abb8 | ||
|
|
a72329aeb7 | ||
|
|
b8f07d5028 | ||
|
|
61eca05f42 | ||
|
|
7143ed2471 | ||
|
|
25a10d573c | ||
|
|
bdda05f579 | ||
|
|
4a99160427 | ||
|
|
8439f96430 | ||
|
|
6fafe23658 | ||
|
|
02f9d366fe | ||
|
|
d8b6010ad1 | ||
|
|
2bb69f8add | ||
|
|
a258bdf674 | ||
|
|
9f057c09dc | ||
|
|
ae0f833f15 | ||
|
|
aacf78a377 | ||
|
|
b47906fbad | ||
|
|
3d962f3405 | ||
|
|
6d37684fc8 | ||
|
|
c39ac08178 | ||
|
|
99f7ecec11 | ||
|
|
709f8337d4 | ||
|
|
d721bda736 | ||
|
|
90957f214a | ||
|
|
71284cbca8 | ||
|
|
6e04702903 | ||
|
|
8fc21934de | ||
|
|
2e2864e88a | ||
|
|
67c7234b41 | ||
|
|
345ab75143 | ||
|
|
c08e33c48c | ||
|
|
77d20bcd6c | ||
|
|
fc4df0efa1 | ||
|
|
d7d87d0829 | ||
|
|
6ccd091c8f | ||
|
|
74f6433bea | ||
|
|
053b0a1008 | ||
|
|
338540cdec | ||
|
|
4f715674c0 | ||
|
|
81c21e9ab6 | ||
|
|
704b466b20 | ||
|
|
93dbba1def | ||
|
|
9dfab9d537 | ||
|
|
fd481027e3 | ||
|
|
d4e88b97a5 | ||
|
|
705b42821c | ||
|
|
76609064df | ||
|
|
f96941d436 | ||
|
|
7e5d4aa8ea | ||
|
|
1aca32ae9c | ||
|
|
b5575725f0 | ||
|
|
b0f03daba7 | ||
|
|
e1216598a9 | ||
|
|
ecd21cf518 | ||
|
|
e2428ff759 | ||
|
|
900180dfcb | ||
|
|
1512e68237 | ||
|
|
f4d63a08ee | ||
|
|
8784598cd8 | ||
|
|
fe707d8419 | ||
|
|
b9f4d98408 | ||
|
|
a0b4dbe8e9 | ||
|
|
b7f178666f | ||
|
|
4d49952ea7 | ||
|
|
0e26ac0ba4 | ||
|
|
435f836eb2 | ||
|
|
e49e5a3911 | ||
|
|
dc8f8d96eb | ||
|
|
3b886b0998 | ||
|
|
c18a4050d0 | ||
|
|
f50a9ae51e | ||
|
|
047e0c34ca | ||
|
|
652af98544 | ||
|
|
0c6b0282c2 | ||
|
|
2d7af69372 | ||
|
|
ebc18be7c9 | ||
|
|
9f816c52ef | ||
|
|
b542633cb6 | ||
|
|
89c28bae6c | ||
|
|
7ce6844ad0 | ||
|
|
6be4c2aeae | ||
|
|
fcbeb20699 | ||
|
|
c8b3cfe09c | ||
|
|
8c017baedd | ||
|
|
5dade37cb4 | ||
|
|
ff1c1b8754 | ||
|
|
1b8ddf67b1 | ||
|
|
e36ff58a94 | ||
|
|
9a1ee39485 | ||
|
|
b949886314 | ||
|
|
7f185f2972 | ||
|
|
2d083074b1 | ||
|
|
478c72f69c | ||
|
|
d06d691a1a | ||
|
|
f9eeeb6ffd | ||
|
|
c3c0ac3cfe | ||
|
|
373909cc68 | ||
|
|
5e42d0d6bc | ||
|
|
792baa7e6a | ||
|
|
2c3a9f9dd3 | ||
|
|
c6761e80b2 | ||
|
|
19ff11a501 | ||
|
|
39046aabf9 | ||
|
|
1d9da7ec5b | ||
|
|
a285efc47c | ||
|
|
c0e68afc21 | ||
|
|
5f42835c1b | ||
|
|
c4e2b763c2 | ||
|
|
2a7e21c470 | ||
|
|
7bf36f9d8d | ||
|
|
60a9dccb1d | ||
|
|
b0d030dccf | ||
|
|
c64dfbfe65 | ||
|
|
d861b730ef | ||
|
|
6f7732ce38 | ||
|
|
4478f4a99e | ||
|
|
085b5ce7e3 | ||
|
|
8d66cac69f | ||
|
|
66bf1c861c | ||
|
|
7c440c5dcd | ||
|
|
bd268cbb01 | ||
|
|
d237d2b38a | ||
|
|
3201c721a5 | ||
|
|
e4df96c890 | ||
|
|
b4e255a92e | ||
|
|
47bf9f28e0 | ||
|
|
c492d26711 | ||
|
|
4cf3d3210a | ||
|
|
b6fe543df7 | ||
|
|
2ee1e645c9 | ||
|
|
565519fa30 | ||
|
|
bb1562a362 | ||
|
|
8ffe84c440 | ||
|
|
ea527a7ece | ||
|
|
b59762bd5b | ||
|
|
e7c8575322 | ||
|
|
a905795c36 | ||
|
|
4304d35d6a | ||
|
|
61b71c8858 | ||
|
|
d57d10d1a8 | ||
|
|
026bdf9b59 | ||
|
|
4611d06a8b | ||
|
|
a8666b1f57 | ||
|
|
d3ac37996a | ||
|
|
033f2c8d82 | ||
|
|
72ba3f03cb | ||
|
|
4d13725692 | ||
|
|
f9d6642900 | ||
|
|
8cf1fedf20 | ||
|
|
fd01fa2036 | ||
|
|
95b0d0d326 | ||
|
|
907e743607 | ||
|
|
796008ad39 | ||
|
|
e5117c6c45 | ||
|
|
d9b60d5e63 | ||
|
|
b2094be545 | ||
|
|
20fa211d05 | ||
|
|
63e1642823 | ||
|
|
6b0e3fa39b | ||
|
|
a5d69e8d94 | ||
|
|
27e60d4bc5 | ||
|
|
f0a18f0e22 | ||
|
|
f9e4f6660e | ||
|
|
532cd3edab | ||
|
|
34a5c5e168 | ||
|
|
2e062705c7 | ||
|
|
b406da1598 | ||
|
|
4f2add81cc | ||
|
|
434dd7a252 | ||
|
|
40e8d578ba | ||
|
|
a3532e9602 | ||
|
|
a84d3ee3da | ||
|
|
9767290ecb | ||
|
|
d4548c0cda | ||
|
|
873220ef89 | ||
|
|
f584f28a74 | ||
|
|
11af8eaff2 | ||
|
|
1c1deca67e | ||
|
|
9434c88f83 | ||
|
|
0b48acdf77 | ||
|
|
ba4fd06f37 | ||
|
|
7fb6ca4074 | ||
|
|
63a54d1d18 | ||
|
|
d01ab6bedc | ||
|
|
9604cb85f1 | ||
|
|
dd12028e15 | ||
|
|
45af858f13 | ||
|
|
c555579f2d | ||
|
|
dbc22aa81b | ||
|
|
ff05d7c673 | ||
|
|
00163031e0 | ||
|
|
95b0243d5b | ||
|
|
a00b78e719 | ||
|
|
8fb6b5becb | ||
|
|
f2caf8a60b | ||
|
|
68be9a1a2f | ||
|
|
ba497bf14d | ||
|
|
f15e7c61ce | ||
|
|
42f3bb4d7d | ||
|
|
5bb92eea8a | ||
|
|
c233b3ca00 | ||
|
|
b685361033 | ||
|
|
6209ff6272 | ||
|
|
bbfb17e4b1 | ||
|
|
a54a760cd4 | ||
|
|
536661e7d0 | ||
|
|
e32a024f1a | ||
|
|
c41adc3c41 | ||
|
|
c5faf7f812 | ||
|
|
84a598ddab | ||
|
|
a4451acae4 | ||
|
|
b751759189 | ||
|
|
0b67e67d0b | ||
|
|
337a8eaea4 | ||
|
|
49b52488f0 | ||
|
|
f45c797cdc | ||
|
|
62a2ffc2b7 | ||
|
|
37d8a218f7 | ||
|
|
364beb647b | ||
|
|
5b769c8e7d | ||
|
|
06ecad922d | ||
|
|
1f2fe341d6 | ||
|
|
17648299ef | ||
|
|
8150bfd03f | ||
|
|
82ef93a626 | ||
|
|
771d837eb0 | ||
|
|
27eccbebea | ||
|
|
5cd1c2759e | ||
|
|
ca0264fe1e | ||
|
|
7b41f1ac9b | ||
|
|
c8637733a7 | ||
|
|
2f24d73ef5 | ||
|
|
f742358b58 | ||
|
|
2cb6be718a | ||
|
|
c492783f0b | ||
|
|
950f8ebfa3 | ||
|
|
1285724719 | ||
|
|
5c8f13184b | ||
|
|
3bb5be6caf | ||
|
|
ef38ad4a5e | ||
|
|
5855990952 | ||
|
|
c340a69b5b | ||
|
|
72bc21716b | ||
|
|
6fe97d7afe | ||
|
|
3a095cfda6 | ||
|
|
84ccf5980a | ||
|
|
318895d241 | ||
|
|
220fb27eca | ||
|
|
bd76aa5099 | ||
|
|
8dae5cb16c | ||
|
|
ca609d58e3 | ||
|
|
1ca022ebd0 | ||
|
|
6a56604e89 | ||
|
|
201fea1446 | ||
|
|
058bcde9d2 | ||
|
|
57ea67d13e | ||
|
|
1bd4491429 | ||
|
|
504adac487 | ||
|
|
c9dd9173bb | ||
|
|
8c6f6b93e3 | ||
|
|
589c3748f2 | ||
|
|
e132fe10a5 | ||
|
|
579e29664d | ||
|
|
479594b385 | ||
|
|
db0a02f97e | ||
|
|
d3cc26cfd3 | ||
|
|
26695673a9 | ||
|
|
f68fcf1b6e | ||
|
|
6a70c4ecba | ||
|
|
47547083d4 | ||
|
|
8e0ec1d7b8 | ||
|
|
b415c90519 | ||
|
|
ec8511a5a4 | ||
|
|
750278dc48 | ||
|
|
508909cf7d | ||
|
|
90a1493256 | ||
|
|
8605293c81 | ||
|
|
f594b6310e | ||
|
|
f1191057d2 | ||
|
|
689461c456 | ||
|
|
7bd8a5f583 | ||
|
|
b9eea0d5ef | ||
|
|
97ff3e53b6 | ||
|
|
c3c58d3128 | ||
|
|
ed29af6583 | ||
|
|
cbcbcf8b94 | ||
|
|
f6e3748e78 | ||
|
|
6ea9748027 | ||
|
|
06cc7ad8c8 | ||
|
|
8d8ddd8f28 | ||
|
|
f1c7619fed | ||
|
|
d6d619b4ec | ||
|
|
8397b108e0 | ||
|
|
8bfd93ab57 | ||
|
|
a8391712cf | ||
|
|
f9aa94f13b | ||
|
|
978fafefcd | ||
|
|
1fb03fdcfd | ||
|
|
b9f5b32037 | ||
|
|
bb0ceead12 | ||
|
|
cf2aa7030d | ||
|
|
792568816e | ||
|
|
d94563512d | ||
|
|
5051312323 | ||
|
|
1f2fa6e3b2 | ||
|
|
0c01048d32 | ||
|
|
6781a65e18 | ||
|
|
07aeea6ebb | ||
|
|
f970c9b26c | ||
|
|
b0112a4e68 | ||
|
|
0a92457bd4 | ||
|
|
2cac027a26 | ||
|
|
763d7b0ac4 | ||
|
|
068cc0ddf0 | ||
|
|
e02e993949 | ||
|
|
39451890a8 | ||
|
|
68f4aadc6a | ||
|
|
f26baeaecb | ||
|
|
ff23ea4d76 | ||
|
|
43e7cf919d | ||
|
|
628523302e | ||
|
|
c021fbab7e | ||
|
|
a334ba2052 | ||
|
|
12634f3d7d | ||
|
|
72db547a99 | ||
|
|
f07795b76f | ||
|
|
947502e628 | ||
|
|
08931e2c80 | ||
|
|
71b110aa9e | ||
|
|
904a34a83d | ||
|
|
1e2877b057 | ||
|
|
e320145abe | ||
|
|
6f88f3f56a | ||
|
|
17ccf5b57c | ||
|
|
8793d678f6 | ||
|
|
f831cd4eb3 | ||
|
|
9346c6f394 | ||
|
|
1e181ea1a8 | ||
|
|
4b33f2af51 | ||
|
|
66cd004d32 | ||
|
|
f39b303471 | ||
|
|
00a2888b08 | ||
|
|
cdf46205d3 | ||
|
|
2bca56a1d1 | ||
|
|
2ce02919a0 | ||
|
|
34c3cf0da6 | ||
|
|
2bb6355f0c | ||
|
|
468013397d | ||
|
|
5420aafadf | ||
|
|
f697a40c4e | ||
|
|
b582d7ba7f | ||
|
|
1bb08e0f35 | ||
|
|
faa1c9e7b9 | ||
|
|
45015f8f8d | ||
|
|
079386cd80 | ||
|
|
007a6aa4d8 | ||
|
|
18dea0d56d | ||
|
|
2cf4816c69 | ||
|
|
ba86afac6d | ||
|
|
0caea4f456 | ||
|
|
ffd91f0b85 | ||
|
|
dd8e754a26 | ||
|
|
8c8ade8a89 | ||
|
|
29649aae51 | ||
|
|
ea9c795973 | ||
|
|
1fe349db47 | ||
|
|
a6e056c060 | ||
|
|
d350a3f05e | ||
|
|
60578e6391 | ||
|
|
9f38e3aa2d | ||
|
|
f9f373916f | ||
|
|
2154131b5c | ||
|
|
0eb1ba4c9b | ||
|
|
bff3bd8778 | ||
|
|
b86ae6c0d9 | ||
|
|
2f232aae02 | ||
|
|
adb6a48347 | ||
|
|
3c72104ea7 | ||
|
|
cdcda9fb75 |
731 changed files with 51884 additions and 24357 deletions
14
.editorconfig
Normal file
14
.editorconfig
Normal 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
1
.envrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
use flake
|
||||
5
.github/workflows/build.yaml
vendored
5
.github/workflows/build.yaml
vendored
|
|
@ -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
19
.github/workflows/format.yaml
vendored
Normal 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
11
.github/workflows/nix.yaml
vendored
Normal 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
|
||||
6
.github/workflows/release.yaml
vendored
6
.github/workflows/release.yaml
vendored
|
|
@ -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
|
||||
|
||||
|
|
|
|||
20
.github/workflows/setup-linux.yaml
vendored
20
.github/workflows/setup-linux.yaml
vendored
|
|
@ -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
|
||||
|
|
|
|||
20
.github/workflows/setup-macos.yaml
vendored
20
.github/workflows/setup-macos.yaml
vendored
|
|
@ -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
|
||||
|
|
|
|||
25
.github/workflows/setup-windows.yaml
vendored
25
.github/workflows/setup-windows.yaml
vendored
|
|
@ -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
27
.github/workflows/winget.yaml
vendored
Normal 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
3
.gitignore
vendored
|
|
@ -1,3 +1,6 @@
|
|||
.idea
|
||||
/target
|
||||
/node_modules
|
||||
/.direnv
|
||||
result
|
||||
.DS_Store
|
||||
|
|
|
|||
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -1,3 +0,0 @@
|
|||
[submodule "tools"]
|
||||
path = tools
|
||||
url = https://github.com/project-gauntlet/tools
|
||||
1
.npmrc
Normal file
1
.npmrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
@jsr:registry=https://npm.jsr.io
|
||||
526
CHANGELOG.md
526
CHANGELOG.md
|
|
@ -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
10437
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
91
Cargo.toml
91
Cargo.toml
|
|
@ -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
458
README.md
|
|
@ -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
|
||||

|
||||
|
||||
## 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`
|
||||
|
||||

|
||||
|
||||
### 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:
|
||||

|
||||
|
||||
Components:
|
||||

|
||||
|
||||
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.
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
8
|
||||
21
|
||||
|
|
@ -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/>
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
[gauntlet]
|
||||
name = 'Calculator'
|
||||
description = ''
|
||||
|
||||
[[entrypoint]]
|
||||
id = 'default'
|
||||
name = 'Default'
|
||||
path = 'src/default.tsx'
|
||||
type = 'inline-view'
|
||||
description = ''
|
||||
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
70
bundled_plugins/gauntlet/gauntlet.toml
Normal file
70
bundled_plugins/gauntlet/gauntlet.toml
Normal 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'
|
||||
19
bundled_plugins/gauntlet/package.json
Normal file
19
bundled_plugins/gauntlet/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
298
bundled_plugins/gauntlet/src/applications.tsx
Normal file
298
bundled_plugins/gauntlet/src/applications.tsx
Normal 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()
|
||||
}
|
||||
}
|
||||
56
bundled_plugins/gauntlet/src/calculator.tsx
Normal file
56
bundled_plugins/gauntlet/src/calculator.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
5
bundled_plugins/gauntlet/src/settings.tsx
Normal file
5
bundled_plugins/gauntlet/src/settings.tsx
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { open_settings } from "gauntlet:bridge/internal-all";
|
||||
|
||||
export default function Settings(): void {
|
||||
open_settings()
|
||||
}
|
||||
243
bundled_plugins/gauntlet/src/window/shared.tsx
Normal file
243
bundled_plugins/gauntlet/src/window/shared.tsx
Normal 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(","));
|
||||
}
|
||||
134
bundled_plugins/gauntlet/src/window/wayland.ts
Normal file
134
bundled_plugins/gauntlet/src/window/wayland.ts
Normal 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
296
bundled_plugins/gauntlet/src/window/x11.ts
Normal file
296
bundled_plugins/gauntlet/src/window/x11.ts
Normal 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
|
||||
}
|
||||
37
bundled_plugins/gauntlet/src/windows.tsx
Normal file
37
bundled_plugins/gauntlet/src/windows.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
13
bundled_plugins/gauntlet/tsconfig.json
Normal file
13
bundled_plugins/gauntlet/tsconfig.json
Normal 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"]
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
[gauntlet]
|
||||
name = 'Settings'
|
||||
description = ''
|
||||
|
||||
[[entrypoint]]
|
||||
id = 'default'
|
||||
name = 'Open Gauntlet Settings'
|
||||
path = 'src/default.tsx'
|
||||
type = 'command'
|
||||
description = ''
|
||||
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
23
bundled_themes/legacy.toml
Normal file
23
bundled_themes/legacy.toml
Normal 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
|
||||
23
bundled_themes/macos_dark.toml
Normal file
23
bundled_themes/macos_dark.toml
Normal 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
|
||||
23
bundled_themes/macos_light.toml
Normal file
23
bundled_themes/macos_light.toml
Normal 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
13
default.nix
Normal 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
1
dev_data/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
scenarios
|
||||
|
|
@ -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
|
||||
|
|
|
|||
4
dev_data/state/.gitignore
vendored
4
dev_data/state/.gitignore
vendored
|
|
@ -1 +1,3 @@
|
|||
logs
|
||||
logs
|
||||
local_storage
|
||||
window_position
|
||||
74
dev_plugin/assets/image.svg
Normal file
74
dev_plugin/assets/image.svg
Normal 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 |
|
|
@ -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"]
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
13
dev_plugin/src/detail-img-download-view.tsx
Normal file
13
dev_plugin/src/detail-img-download-view.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -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 }));
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
12
dev_plugin/src/empty-grid.tsx
Normal file
12
dev_plugin/src/empty-grid.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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
5
dev_plugin/src/empty.tsx
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { ReactElement } from "react";
|
||||
|
||||
export default function DetailView(): ReactElement {
|
||||
return <></>
|
||||
}
|
||||
89
dev_plugin/src/entrypoint-generator.tsx
Normal file
89
dev_plugin/src/entrypoint-generator.tsx
Normal 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"
|
||||
}
|
||||
],
|
||||
})
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
|
|
|
|||
421
dev_plugin/src/hooks-view.tsx
Normal file
421
dev_plugin/src/hooks-view.tsx
Normal 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)
|
||||
}
|
||||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
43
dev_plugin/src/test-grid-focus.tsx
Normal file
43
dev_plugin/src/test-grid-focus.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
@ -5,8 +5,7 @@
|
|||
"esModuleInterop": true,
|
||||
"target": "ES2022",
|
||||
"moduleResolution": "bundler",
|
||||
"jsx": "react-jsx",
|
||||
"types": ["@project-gauntlet/deno"]
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"lib": ["ES2020"]
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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 |
|
|
@ -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 |
1
docs/js/components/accessory_icon/description.md
Normal file
1
docs/js/components/accessory_icon/description.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
An accessory with icon and optional tooltip
|
||||
1
docs/js/components/accessory_icon/props/icon.md
Normal file
1
docs/js/components/accessory_icon/props/icon.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Icon of the accessory
|
||||
1
docs/js/components/accessory_icon/props/tooltip.md
Normal file
1
docs/js/components/accessory_icon/props/tooltip.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
A tooltip shown when the accessory is hovered.
|
||||
1
docs/js/components/accessory_text/description.md
Normal file
1
docs/js/components/accessory_text/description.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
An accessory with text and optional icon and tooltip
|
||||
1
docs/js/components/accessory_text/props/icon.md
Normal file
1
docs/js/components/accessory_text/props/icon.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
An icon that is displayed to the left side of the text
|
||||
1
docs/js/components/accessory_text/props/text.md
Normal file
1
docs/js/components/accessory_text/props/text.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Text of the accessory
|
||||
1
docs/js/components/accessory_text/props/tooltip.md
Normal file
1
docs/js/components/accessory_text/props/tooltip.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
A tooltip shown when the accessory is hovered.
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
Date Picker is a type of form input that produces date value represented as a string in `YYYY-MM-DD` format
|
||||
|
|
@ -1 +0,0 @@
|
|||
Text displayed in UI to the left of the date picker itself
|
||||
|
|
@ -1 +0,0 @@
|
|||
Function that is called when the value of the data picker was changed
|
||||
|
|
@ -1 +0,0 @@
|
|||
Value of the Date Picker. Can be used to implement controlled form
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
1
docs/js/components/detail/props/isLoading.md
Normal file
1
docs/js/components/detail/props/isLoading.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
If "true" loading bar is shown above content
|
||||
|
|
@ -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
|
||||
1
docs/js/components/form/props/isLoading.md
Normal file
1
docs/js/components/form/props/isLoading.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
If "true" loading bar is shown above content
|
||||
|
|
@ -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
|
||||
1
docs/js/components/grid/props/focusedItemId.md
Normal file
1
docs/js/components/grid/props/focusedItemId.md
Normal 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
|
||||
1
docs/js/components/grid/props/isLoading.md
Normal file
1
docs/js/components/grid/props/isLoading.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
If "true" loading bar is shown above content
|
||||
1
docs/js/components/grid/props/onItemFocusChange.md
Normal file
1
docs/js/components/grid/props/onItemFocusChange.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Function that is called when focused item changes. Argument is an ID of new focused item
|
||||
|
|
@ -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
|
||||
1
docs/js/components/grid_item/props/accessory.md
Normal file
1
docs/js/components/grid_item/props/accessory.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Accessory displayed on the right bottom of the grid item
|
||||
|
|
@ -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
|
||||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue