mirror of
https://github.com/project-gauntlet/gauntlet.git
synced 2025-12-23 10:35:53 +00:00
Compare commits
91 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 |
323 changed files with 16363 additions and 15297 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
|
||||
17
.github/workflows/format.yaml
vendored
17
.github/workflows/format.yaml
vendored
|
|
@ -1,12 +1,19 @@
|
|||
name: Format
|
||||
name: format
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
all:
|
||||
runs-on: ubuntu-latest
|
||||
rust:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: rustfmt
|
||||
- uses: actions/checkout@v4
|
||||
- name: Check format
|
||||
run: cargo +nightly fmt --all -- --check --verbose
|
||||
- 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
|
||||
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
|
|
@ -25,7 +25,7 @@ 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:
|
||||
|
|
|
|||
2
.github/workflows/setup-linux.yaml
vendored
2
.github/workflows/setup-linux.yaml
vendored
|
|
@ -14,7 +14,7 @@ on:
|
|||
|
||||
jobs:
|
||||
run-on-linux:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- run: sudo apt-get update
|
||||
|
|
|
|||
2
.github/workflows/setup-windows.yaml
vendored
2
.github/workflows/setup-windows.yaml
vendored
|
|
@ -14,7 +14,7 @@ on:
|
|||
|
||||
jobs:
|
||||
run-on-windows:
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2022
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
|
|||
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
|
||||
80
CHANGELOG.md
80
CHANGELOG.md
|
|
@ -3,12 +3,90 @@
|
|||
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
|
||||
|
|
|
|||
5955
Cargo.lock
generated
5955
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
50
Cargo.toml
50
Cargo.toml
|
|
@ -5,44 +5,39 @@ 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/plugin_runtime",
|
||||
"rust/manifest_schema",
|
||||
"rust/plugin_runtime",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[workspace.dependencies]
|
||||
# iced
|
||||
#iced = { version = "0.13.99", features = ["tiny-skia", "wgpu", "tokio", "lazy", "advanced", "image", "web-colors", "svg"] }
|
||||
iced = { git = "https://github.com/project-gauntlet/iced.git", branch = "gauntlet-0.13", default-features = false, features = ["tiny-skia", "wgpu", "tokio", "advanced", "image", "web-colors", "svg"] }
|
||||
#iced_aw = { version = "0.11.99", features = ["date_picker", "wrap", "number_input", "grid", "spinner"] }
|
||||
iced_aw = { git = "https://github.com/project-gauntlet/iced_aw.git", branch = "gauntlet-0.13", default-features = false, features = ["date_picker", "wrap", "number_input", "grid", "spinner"] }
|
||||
#iced_table = "0.13.99"
|
||||
iced_table = { git = "https://github.com/project-gauntlet/iced_table.git", branch = "gauntlet-0.13" }
|
||||
#iced_fonts = { version = "0.1.99", features = ["bootstrap"] }
|
||||
iced_fonts = { git = "https://github.com/project-gauntlet/iced_fonts.git", branch = "gauntlet-0.13", features = ["bootstrap"] }
|
||||
#iced_layershell = "0.13.99"
|
||||
iced_layershell = { git = "https://github.com/project-gauntlet/exwlshelleventloop.git", branch = "gauntlet-0.13" }
|
||||
#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-management-client = { path = "./rust/management_client" }
|
||||
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-plugin-runtime = { path = "./rust/plugin_runtime" }
|
||||
gauntlet-component-model = { path = "./rust/component_model" }
|
||||
gauntlet-scenario-runner = { path = "./rust/scenario_runner" }
|
||||
|
||||
|
|
@ -51,7 +46,7 @@ anyhow = { version = "1", features = ["backtrace"] }
|
|||
tracing = { version = "0.1" }
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
tokio = { version = "1.42" }
|
||||
tokio-util = "0.7"
|
||||
tokio-util = { version = "0.7" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0" }
|
||||
bincode = { version = "2.0.0-rc.3" }
|
||||
|
|
@ -70,13 +65,16 @@ 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]
|
||||
gauntlet-cli = { path = "rust/cli" }
|
||||
|
||||
[features]
|
||||
release = ["gauntlet-cli/release"]
|
||||
scenario_runner = ["gauntlet-cli/scenario_runner"]
|
||||
|
||||
[profile.release-size]
|
||||
inherits = "release"
|
||||
|
|
@ -84,6 +82,18 @@ opt-level = "s"
|
|||
lto = "thin"
|
||||
strip = true
|
||||
|
||||
[patch.crates-io]
|
||||
# NOTE https://github.com/ipetkov/crane/issues/336
|
||||
libffi-sys = { git = "https://github.com/tov/libffi-rs", rev = "d0704d634b6f3ffef5b6fc7e07fe965a1cff5c7b" }
|
||||
#[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" }
|
||||
|
|
|
|||
454
README.md
454
README.md
|
|
@ -7,404 +7,97 @@
|
|||
Web-first cross-platform application launcher with React-based plugins
|
||||
|
||||
> [!WARNING]
|
||||
> Launcher is being developed by single developer in their free time.
|
||||
> Changes may be few and far between.
|
||||
>
|
||||
> There will probably be breaking changes which will be documented in [changelog](CHANGELOG.md).
|
||||
> The project is no longer being developed.
|
||||
|
||||

|
||||
|
||||
## 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
|
||||
- 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
|
||||
- Built-in functionality is provided by bundled plugin
|
||||
- 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)
|
||||
- [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
|
||||
- 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)
|
||||
- Settings: open Gauntlet Settings
|
||||
- More to come, see [#15](https://github.com/project-gauntlet/gauntlet/issues/15)
|
||||
- [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
|
||||
- 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
|
||||
- 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
|
||||
- Currently, there is no fuzzy matching. Results are matched per word by substring
|
||||
- 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 or running commands, it is required to specify
|
||||
which operating systems it supports.
|
||||
- If plugin doesn't use filesystem, env variables 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
|
||||
|
||||
##### Official
|
||||
- <img src="https://cdn.jsdelivr.net/gh/simple-icons/simple-icons@develop/icons/linux.svg" width="18" height="18" /> Linux X11
|
||||
- Application plugin depends on `gtk-launch`
|
||||
- <img src="https://cdn.jsdelivr.net/gh/simple-icons/simple-icons@develop/icons/apple.svg" width="18" height="18" /> macOS M1
|
||||
|
||||
##### Best-effort
|
||||
- <img src="https://cdn.jsdelivr.net/gh/simple-icons/simple-icons@develop/icons/linux.svg" width="18" height="18" /> Linux Wayland
|
||||
- LayerShell support required
|
||||
- Application plugin depends on `gtk-launch`
|
||||
- <img src="https://img.icons8.com/windows/32/windows-11.png" width="18" height="18" /> Windows
|
||||
- <img src="https://cdn.jsdelivr.net/gh/simple-icons/simple-icons@develop/icons/apple.svg" width="18" height="18" /> macOS Intel
|
||||
|
||||
##### Planned features
|
||||
|
||||
- See [#13](https://github.com/project-gauntlet/gauntlet/issues/13)
|
||||
- See [#15](https://github.com/project-gauntlet/gauntlet/issues/15)
|
||||
- See [#16](https://github.com/project-gauntlet/gauntlet/issues/16)
|
||||
|
||||
##### Plugin APIs
|
||||
|
||||
- UI
|
||||
- Detail
|
||||
- Form
|
||||
- Action Panel
|
||||
- List
|
||||
- Grid
|
||||
- Inline
|
||||
- View directly under main search bar
|
||||
- Requires separate permission to be explicitly specified in manifest because it reads everything user enters in main search bar
|
||||
- Stack-based Navigation
|
||||
- Assets
|
||||
- Files placed into `assets` directory in root of plugin repository are accessible at plugin runtime using `assetData` function
|
||||
- Preferences
|
||||
- Preferences defined in plugin manifest can be set by user and are accessible at plugin runtime using `pluginPreferences` and `entrypointPreferences` functions
|
||||
- Clipboard
|
||||
- Accessible via `Clipboard` api
|
||||
- Requires separate permission to be explicitly specified in manifest
|
||||
- HUD
|
||||
- Shows small popup window with feedback information
|
||||
- Accessible via `showHud` function
|
||||
- React Helper 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
|
||||
|
||||
## 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
|
||||
|
||||
Download `.msi` at [Releases page](https://github.com/project-gauntlet/gauntlet/releases/latest) and open to install Gauntlet
|
||||
|
||||
Note: application doesn't have auto-update functionality, and has to be updated manually
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
#### Nix
|
||||
|
||||
The nix flake in this repository is community maintained. If you face a problem, please create an issue and hopefully somebody will work on it.
|
||||
|
||||
To install, you either know what to do, or you can read more [here](nix/README.md).
|
||||
|
||||
#### Other Linux Distributions
|
||||
|
||||
At the moment application is only available for Arch Linux and Nix. 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
|
||||
"""
|
||||
|
||||
[[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
|
||||
icon = 'icon.png' # optional, path to file inside assets dir
|
||||
type = 'view'
|
||||
description = 'Some entrypoint description'
|
||||
|
||||
[[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'
|
||||
|
||||
[[entrypoint]]
|
||||
id = 'entrypoint-generator'
|
||||
name = 'Entrypoint generator'
|
||||
path = 'src/entrypoint-generator.ts'
|
||||
type = 'entrypoint-generator'
|
||||
description = 'Some entrypoint description'
|
||||
|
||||
[[entrypoint]]
|
||||
id = 'inline-view'
|
||||
name = 'Inline view'
|
||||
path = 'src/inline-view.tsx'
|
||||
type = 'inline-view'
|
||||
description = 'Some entrypoint description'
|
||||
|
||||
[permissions]
|
||||
network = ["github.com", "example.com:8833"]
|
||||
clipboard = ["read", "write", "clear"]
|
||||
main_search_bar = ["read"]
|
||||
|
||||
# if specified requires supported_system to be specified as well
|
||||
environment = ["ENV_VAR_NAME"]
|
||||
|
||||
# if specified requires supported_system to be specified as well
|
||||
system = ["apiName"]
|
||||
|
||||
# if specified requires supported_system to be specified as well
|
||||
[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"]
|
||||
|
||||
# if specified requires supported_system to be specified as well
|
||||
[permissions.exec]
|
||||
command = ["ls"]
|
||||
executable = ["/usr/bin/ls"]
|
||||
|
||||
[[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
|
||||
|
||||
### 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 4 parts: server, frontend, plugin runtime and settings.
|
||||
Each plugin runs in separate plugin runtime in separate OS process. Each plugin is 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 or subprocess execution.
|
||||
Server saves plugins themselves and state of plugins into SQLite database.
|
||||
|
||||
Frontend is GUI module that uses [iced-rs](https://github.com/iced-rs/iced) as a GUI framework. It is run in the same process as a server.
|
||||
|
||||
Plugins can create UI using [React](https://github.com/facebook/react).
|
||||
Plugin Runtime implements custom React Reconciler (similar to React Native) which renders GUI components to frontend.
|
||||
Plugin Runtime listens on signals from frontend, so when user opens view defined by plugin, frontend sends an open-view request.
|
||||
Plugin Runtime 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 using a simple request-response approach.
|
||||
|
||||
Simplified communication:
|
||||

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

|
||||
|
||||
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 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](#arch-linux) and [Nix](#nix) so you can use them as examples.
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
- Rust
|
||||
|
|
@ -412,6 +105,8 @@ You will need:
|
|||
- 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
|
||||
npm ci
|
||||
|
|
@ -419,14 +114,35 @@ 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
|
||||
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
|
||||
|
||||
## Contributing
|
||||
|
|
@ -435,29 +151,5 @@ If you'd like to help build Gauntlet you can do it in more ways than just contri
|
|||
- Reporting a bug or UI/UX problem
|
||||
- Creating a plugin
|
||||
|
||||
If you are looking for things to do see pinned [issues](https://github.com/project-gauntlet/gauntlet/issues).
|
||||
|
||||
For simple problems feel free to open an issue or PR and tackle it yourself!
|
||||
|
||||
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.
|
||||
|
||||
All and any contributions are welcome.
|
||||
|
||||
## Versioning
|
||||
|
||||
### Application
|
||||
|
||||
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".
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
19
|
||||
21
|
||||
|
|
@ -196,7 +196,7 @@ export default async function Applications(context: GeneratorContext<object, Ent
|
|||
}),
|
||||
add,
|
||||
remove,
|
||||
{ exts: ["app"], maxDepth: 2 }
|
||||
{ exts: ["app"], maxDepth: 2, followSymlinks: true, }
|
||||
);
|
||||
}
|
||||
case "windows": {
|
||||
|
|
|
|||
|
|
@ -4,25 +4,30 @@ import {
|
|||
GeneratedEntrypointAction,
|
||||
} from "@project-gauntlet/api/helpers";
|
||||
import { linux_open_application } from "gauntlet:bridge/internal-linux";
|
||||
import React from "react";
|
||||
import { useState } from "react";
|
||||
import { Action, ActionPanel, List } from "@project-gauntlet/api/components";
|
||||
|
||||
export function ListOfWindows({ windows, focusWindow }: {
|
||||
export function ListOfWindows({ windows, focusWindow, focusSecond }: {
|
||||
windows: Record<string, OpenWindowData>,
|
||||
focusWindow: (windowId: string) => void
|
||||
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: string | undefined) => {
|
||||
onAction={id => {
|
||||
if (id) {
|
||||
focusAndSort(id, focusWindow)
|
||||
return { close: true }
|
||||
|
|
@ -31,6 +36,8 @@ export function ListOfWindows({ windows, focusWindow }: {
|
|||
/>
|
||||
</ActionPanel>
|
||||
}
|
||||
onItemFocusChange={setId}
|
||||
focusedItemId={id}
|
||||
>
|
||||
{
|
||||
sortedWindows.map(window => <List.Item key={window} id={window} title={windows[window]!!.title}/>)
|
||||
|
|
@ -45,7 +52,13 @@ export type OpenWindowData = {
|
|||
appId: string
|
||||
}
|
||||
|
||||
export const openWindows: Record<string, OpenWindowData> = {};
|
||||
|
||||
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,
|
||||
|
|
@ -65,7 +78,7 @@ export function applicationActions(
|
|||
}
|
||||
|
||||
const appWindows = Object.fromEntries(
|
||||
Object.entries(openWindows)
|
||||
Object.entries(openWindows())
|
||||
.filter(([_, windowData]) => windowData.appId == id)
|
||||
)
|
||||
|
||||
|
|
@ -103,7 +116,13 @@ export function applicationActions(
|
|||
{
|
||||
label: "Show windows",
|
||||
view: () => {
|
||||
return <ListOfWindows windows={appWindows} focusWindow={windowId => focusWindow(windowId)}/>
|
||||
return (
|
||||
<ListOfWindows
|
||||
windows={appWindows}
|
||||
focusWindow={windowId => focusWindow(windowId)}
|
||||
focusSecond={false}
|
||||
/>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -123,7 +142,7 @@ export function applicationAccessories(id: string, experimentalWindowTracking: b
|
|||
return []
|
||||
}
|
||||
|
||||
const appWindows = Object.entries(openWindows)
|
||||
const appWindows = Object.entries(openWindows())
|
||||
.filter(([_, windowData]) => windowData.appId == id)
|
||||
|
||||
if (appWindows.length == 0) {
|
||||
|
|
@ -147,7 +166,7 @@ export function addOpenWindow(
|
|||
add: (id: string, data: GeneratedEntrypoint) => void,
|
||||
) {
|
||||
if (generatedEntrypoint) {
|
||||
openWindows[windowId] = {
|
||||
openWindows()[windowId] = {
|
||||
id: windowId,
|
||||
appId: appId,
|
||||
title: windowTitle
|
||||
|
|
@ -172,11 +191,11 @@ export function deleteOpenWindow(
|
|||
get: (id: string) => GeneratedEntrypoint | undefined,
|
||||
add: (id: string, data: GeneratedEntrypoint) => void,
|
||||
) {
|
||||
const openWindow = openWindows[windowId];
|
||||
const openWindow = openWindows()[windowId];
|
||||
if (openWindow) {
|
||||
const generatedEntrypoint = get(openWindow.appId);
|
||||
|
||||
delete openWindows[windowId];
|
||||
delete openWindows()[windowId];
|
||||
|
||||
const knownWindows = readWindowOrder();
|
||||
const newKnownWindows = knownWindows.filter(id => id != windowId)
|
||||
|
|
|
|||
|
|
@ -10,11 +10,19 @@ export default function Windows(): ReactElement {
|
|||
case "linux": {
|
||||
if (wayland()) {
|
||||
return (
|
||||
<ListOfWindows windows={openWindows} focusWindow={(windowId) => focusWaylandWindow(windowId)}/>
|
||||
<ListOfWindows
|
||||
windows={openWindows()}
|
||||
focusWindow={(windowId) => focusWaylandWindow(windowId)}
|
||||
focusSecond={true}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<ListOfWindows windows={openWindows} focusWindow={(windowId) => focusX11Window(windowId)}/>
|
||||
<ListOfWindows
|
||||
windows={openWindows()}
|
||||
focusWindow={(windowId) => focusX11Window(windowId)}
|
||||
focusSecond={true}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
dev_data/.gitignore
vendored
Normal file
1
dev_data/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
scenarios
|
||||
|
|
@ -1,3 +1,11 @@
|
|||
|
||||
#[main_window]
|
||||
#close_on_unfocus = false
|
||||
#close_on_unfocus = false
|
||||
|
||||
[wayland]
|
||||
#main_window_surface = "xdg_shell"
|
||||
global_shortcuts_api = "legacy_x11_api"
|
||||
#global_shortcuts_api = "none"
|
||||
|
||||
[linux]
|
||||
native_hud = true
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ export default function ListView(): ReactElement {
|
|||
)
|
||||
}
|
||||
|
||||
function pushPrimaryAction(id: string | undefined, pushView: (component: ReactNode) => void) {
|
||||
function pushPrimaryAction(id: string | null, pushView: (component: ReactNode) => void) {
|
||||
switch (id) {
|
||||
case "UsePromiseTestBasic": {
|
||||
pushView(<UsePromiseTestBasic/>)
|
||||
|
|
@ -418,4 +418,4 @@ function printState(data: any, error: unknown, isLoading: boolean) {
|
|||
console.dir(data)
|
||||
console.dir(error)
|
||||
console.dir(isLoading)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ 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 onClick = (id: string | undefined) => {
|
||||
const onClick = (id: string | null) => {
|
||||
if (id == "print-env") {
|
||||
console.log(Environment.gauntletVersion);
|
||||
console.log(Environment.isDevelopment);
|
||||
|
|
@ -63,4 +63,4 @@ export default function ListView(): ReactElement {
|
|||
</List.Section>
|
||||
</List>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,43 @@
|
|||
import { ReactElement } from "react";
|
||||
import { Grid } from "@project-gauntlet/api/components";
|
||||
import { ReactElement, useState } from "react";
|
||||
import { Action, ActionPanel, Grid } from "@project-gauntlet/api/components";
|
||||
|
||||
export default function Main(): ReactElement {
|
||||
const content = (
|
||||
const [id, setId] = useState<string | null>(null);
|
||||
|
||||
const content = (id: string) => (
|
||||
<Grid.Item.Content>
|
||||
<Grid.Item.Content.Paragraph>
|
||||
Test
|
||||
{id}
|
||||
</Grid.Item.Content.Paragraph>
|
||||
</Grid.Item.Content>
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<Grid onItemFocusChange={(id: string | undefined) => console.log("onItemFocusChange", id)}>
|
||||
<Grid.Item id="adarian" title="Adarian">{content}</Grid.Item>
|
||||
<Grid.Item id="aruzan" title="Aruzan">{content}</Grid.Item>
|
||||
<Grid.Item id="blutopian" title="Blutopian">{content}</Grid.Item>
|
||||
<Grid.Item id="caphex" title="Caphex">{content}</Grid.Item>
|
||||
<Grid.Item id="condluran" title="Condluran">{content}</Grid.Item>
|
||||
<Grid.Item id="frozian" title="Frozian">{content}</Grid.Item>
|
||||
<Grid.Item id="evereni" title="Evereni">{content}</Grid.Item>
|
||||
<Grid.Item id="ezaraa" title="Ezaraa">{content}</Grid.Item>
|
||||
<Grid.Item id="houk" title="Houk">{content}</Grid.Item>
|
||||
<Grid.Item id="inleshat" title="Inleshat">{content}</Grid.Item>
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ import { ReactElement, useState } from "react";
|
|||
import { List } from "@project-gauntlet/api/components";
|
||||
|
||||
export default function Main(): ReactElement {
|
||||
const [id, setId] = useState<string | undefined>(undefined);
|
||||
const [id, setId] = useState<string | null>(null);
|
||||
|
||||
return (
|
||||
<List onItemFocusChange={setId}>
|
||||
<List onItemFocusChange={setId} focusedItemId={id}>
|
||||
<List.Item id="adarian" title="Adarian"/>
|
||||
<List.Item id="aruzan" title="Aruzan"/>
|
||||
<List.Item id="blutopian" title="Blutopian"/>
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
# Gauntlet Theming
|
||||
|
||||
Currently, in Gauntlet with themes it is possible to change (list is likely be extended with future updates):
|
||||
- Colors of text and background
|
||||
- Window border color, width and radius
|
||||
- Border radius of components in content
|
||||
|
||||
Theming is only affects main window and doesn't affect settings
|
||||
|
||||
Theme config file is in TOML format
|
||||
|
||||
Theme config file locations:
|
||||
- Windows: `C:\Users\Username\AppData\Roaming\Gauntlet\config\theme.toml`
|
||||
- Linux: `$XDG_CONFIG_HOME/gauntlet/theme.toml`
|
||||
- macOS: `$HOME/Library/Application Support/dev.project-gauntlet.gauntlet/theme.toml`
|
||||
|
||||
Currently, theme change is only applied after application restart
|
||||
|
||||
Any errors in theme parsing will be shown in application logs
|
||||
|
||||
See bundled themes for examples [here](./../bundled_themes)
|
||||
|
|
@ -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 +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
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/list/props/focusedItemId.md
Normal file
1
docs/js/components/list/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
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 520 KiB After Width: | Height: | Size: 678 KiB |
|
|
@ -56,6 +56,15 @@ type = 'view'
|
|||
description = ''
|
||||
# docs-code-segment:end
|
||||
|
||||
# docs-code-segment:start content-svg
|
||||
[[entrypoint]]
|
||||
id = 'content-svg'
|
||||
name = 'Content Svg'
|
||||
path = 'src/content-svg.tsx'
|
||||
type = 'view'
|
||||
description = ''
|
||||
# docs-code-segment:end
|
||||
|
||||
# docs-code-segment:start main
|
||||
[[entrypoint]]
|
||||
id = 'main'
|
||||
|
|
@ -120,4 +129,4 @@ description = ''
|
|||
# docs-code-segment:end
|
||||
|
||||
[permissions]
|
||||
network = ["static.wikia.nocookie.net"]
|
||||
network = ["static.wikia.nocookie.net", "upload.wikimedia.org"]
|
||||
|
|
|
|||
14
example_plugins/plugins/ui_detail/src/content-svg.tsx
Normal file
14
example_plugins/plugins/ui_detail/src/content-svg.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { ReactNode } from "react";
|
||||
import { Detail } from "@project-gauntlet/api/components";
|
||||
|
||||
const svgUrl = "https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg"
|
||||
|
||||
export default function ContentSvg(): ReactNode {
|
||||
return (
|
||||
<Detail>
|
||||
<Detail.Content>
|
||||
<Detail.Content.Svg source={{ url: svgUrl }}/>
|
||||
</Detail.Content>
|
||||
</Detail>
|
||||
)
|
||||
}
|
||||
|
|
@ -12,15 +12,6 @@ type = 'view'
|
|||
description = ''
|
||||
# docs-code-segment:end
|
||||
|
||||
# docs-code-segment:start date-picker
|
||||
[[entrypoint]]
|
||||
id = 'date-picker'
|
||||
name = 'Date Picker'
|
||||
path = 'src/date-picker.tsx'
|
||||
type = 'view'
|
||||
description = ''
|
||||
# docs-code-segment:end
|
||||
|
||||
# docs-code-segment:start main
|
||||
[[entrypoint]]
|
||||
id = 'main'
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
import { ReactElement } from 'react';
|
||||
import { Form } from "@project-gauntlet/api/components";
|
||||
|
||||
export default function DatePickerExample(): ReactElement {
|
||||
return (
|
||||
<Form>
|
||||
<Form.DatePicker
|
||||
label="Date"
|
||||
value="2024-03-22"
|
||||
onChange={value => {
|
||||
console.log(`value: ${value}`)
|
||||
}}
|
||||
/>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -30,13 +30,6 @@ export default function MainExample(): ReactElement {
|
|||
|
||||
<Form.Separator/>
|
||||
|
||||
<Form.DatePicker
|
||||
label="Date"
|
||||
value={"2024-03-22"}
|
||||
onChange={value => {
|
||||
console.log(`value: ${value}`)
|
||||
}}
|
||||
/>
|
||||
<Form.Checkbox
|
||||
title="I acknowledge the Galactic Code and agree to abide by its regulations."
|
||||
value={false}
|
||||
|
|
|
|||
|
|
@ -102,6 +102,15 @@ type = 'view'
|
|||
description = ''
|
||||
# docs-code-segment:end
|
||||
|
||||
# docs-code-segment:start content-svg
|
||||
[[entrypoint]]
|
||||
id = 'content-svg'
|
||||
name = 'Grid Content Svg'
|
||||
path = 'src/content-svg.tsx'
|
||||
type = 'view'
|
||||
description = ''
|
||||
# docs-code-segment:end
|
||||
|
||||
# docs-code-segment:start focus
|
||||
[[entrypoint]]
|
||||
id = 'focus'
|
||||
|
|
@ -112,4 +121,4 @@ description = ''
|
|||
# docs-code-segment:end
|
||||
|
||||
[permissions]
|
||||
network = ["static.wikia.nocookie.net"]
|
||||
network = ["static.wikia.nocookie.net", "upload.wikimedia.org"]
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
import { ReactElement } from "react";
|
||||
import { Grid } from "@project-gauntlet/api/components";
|
||||
|
||||
const url = "https://static.wikia.nocookie.net/star-wars-canon/images/b/b0/Tatooine_TPM.png/revision/latest/scale-to-width-down/150?cb=20151124205032";
|
||||
|
||||
export default function ContentImageExample(): ReactElement {
|
||||
return (
|
||||
<Grid>
|
||||
<Grid.Item id="tatooine" title="Tatooine">
|
||||
<Grid.Item.Content>
|
||||
<Grid.Item.Content.Image
|
||||
source={{ url: "https://static.wikia.nocookie.net/star-wars-canon/images/b/b0/Tatooine_TPM.png/revision/latest/scale-to-width-down/150?cb=20151124205032" }}/>
|
||||
<Grid.Item.Content.Image source={{ url: url }}/>
|
||||
</Grid.Item.Content>
|
||||
</Grid.Item>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
example_plugins/plugins/ui_grid/src/content-svg.tsx
Normal file
16
example_plugins/plugins/ui_grid/src/content-svg.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { ReactElement } from "react";
|
||||
import { Grid } from "@project-gauntlet/api/components";
|
||||
|
||||
const svgUrl = "https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg"
|
||||
|
||||
export default function ContentSvgExample(): ReactElement {
|
||||
return (
|
||||
<Grid>
|
||||
<Grid.Item id="svg">
|
||||
<Grid.Item.Content>
|
||||
<Grid.Item.Content.Svg source={{ url: svgUrl }}/>
|
||||
</Grid.Item.Content>
|
||||
</Grid.Item>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ import { ReactElement, useState } from "react";
|
|||
import { Grid } from "@project-gauntlet/api/components";
|
||||
|
||||
export default function FocusExample(): ReactElement {
|
||||
const [id, setId] = useState<string | undefined>(undefined);
|
||||
const [id, setId] = useState<string | null>(null);
|
||||
|
||||
const content = (text: string) => (
|
||||
<Grid.Item.Content>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ const items = [
|
|||
},
|
||||
{
|
||||
title: "Dantooine",
|
||||
image: "https://static.wikia.nocookie.net/starwars/images/a/a5/Dantooine_Resistance.jpg/revision/latest/scale-to-width-down/150?cb=20200120190043"
|
||||
image: "https://static.wikia.nocookie.net/starwars/images/9/9b/Dantooine-RebuildingTheResistance.jpg/revision/latest/scale-to-width-down/150?cb=20200120190043"
|
||||
},
|
||||
]
|
||||
|
||||
|
|
@ -44,4 +44,4 @@ export default function MainExample(): ReactElement {
|
|||
))}
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,18 +2,28 @@ import { ReactElement, useState } from "react";
|
|||
import { Action, ActionPanel, Grid } from "@project-gauntlet/api/components";
|
||||
|
||||
export default function SearchBarSetSearchTextExample(): ReactElement {
|
||||
const [searchText, setSearchText] = useState<string | undefined>("");
|
||||
const [searchText, setSearchText] = useState<string>("");
|
||||
|
||||
return (
|
||||
<Grid
|
||||
actions={
|
||||
<ActionPanel>
|
||||
<Action label="Set value" onAction={(id) => setSearchText(id)}/>
|
||||
<Action
|
||||
label="Set value"
|
||||
onAction={(id) => {
|
||||
// This will be the value in search bar when
|
||||
// enter is pressed while item is not focused
|
||||
const unfocused = "item-not-unfocused";
|
||||
|
||||
setSearchText(id || unfocused)
|
||||
}}
|
||||
/>
|
||||
</ActionPanel>
|
||||
}
|
||||
>
|
||||
<Grid.SearchBar value={searchText} onChange={setSearchText}/>
|
||||
<Grid.Item id="This will be the value in search bar">
|
||||
{/* This id will be the value in search bar when clicked or press enter when item is focused */}
|
||||
<Grid.Item id="item-focused">
|
||||
<Grid.Item.Content>
|
||||
<Grid.Item.Content.Paragraph>
|
||||
Click me!
|
||||
|
|
|
|||
2
example_plugins/plugins/ui_inline_svg/.gitignore
vendored
Normal file
2
example_plugins/plugins/ui_inline_svg/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
dist
|
||||
node_modules
|
||||
16
example_plugins/plugins/ui_inline_svg/gauntlet.toml
Normal file
16
example_plugins/plugins/ui_inline_svg/gauntlet.toml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
[gauntlet]
|
||||
name = 'Docs Inline Svg'
|
||||
description = ''
|
||||
|
||||
# docs-code-segment:start main
|
||||
[[entrypoint]]
|
||||
id = 'main'
|
||||
name = 'Main'
|
||||
path = 'src/main.tsx'
|
||||
type = 'inline-view'
|
||||
description = ''
|
||||
# docs-code-segment:end
|
||||
|
||||
[permissions]
|
||||
main_search_bar = ["read"]
|
||||
network = ["upload.wikimedia.org"]
|
||||
17
example_plugins/plugins/ui_inline_svg/package.json
Normal file
17
example_plugins/plugins/ui_inline_svg/package.json
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "@project-gauntlet/docs-inline-svg",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "gauntlet build",
|
||||
"dev": "gauntlet dev"
|
||||
},
|
||||
"dependencies": {
|
||||
"@project-gauntlet/api": "file:../../js/api"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/deno": "*",
|
||||
"@project-gauntlet/tools": "*",
|
||||
"typescript": "*"
|
||||
}
|
||||
}
|
||||
19
example_plugins/plugins/ui_inline_svg/src/main.tsx
Normal file
19
example_plugins/plugins/ui_inline_svg/src/main.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { ReactElement } from "react";
|
||||
import { Inline } from "@project-gauntlet/api/components";
|
||||
|
||||
const svgUrl = "https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg"
|
||||
|
||||
export default function Main({ text }: { text: string }): ReactElement | null {
|
||||
|
||||
if (text != "example") {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Inline>
|
||||
<Inline.Center>
|
||||
<Inline.Center.Svg source={{ url: svgUrl }}/>
|
||||
</Inline.Center>
|
||||
</Inline>
|
||||
)
|
||||
}
|
||||
11
example_plugins/plugins/ui_inline_svg/tsconfig.json
Normal file
11
example_plugins/plugins/ui_inline_svg/tsconfig.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"module": "ES2022",
|
||||
"esModuleInterop": true,
|
||||
"target": "ES2022",
|
||||
"moduleResolution": "bundler",
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"lib": ["ES2020"]
|
||||
}
|
||||
|
|
@ -120,6 +120,15 @@ type = 'view'
|
|||
description = ''
|
||||
# docs-code-segment:end
|
||||
|
||||
# docs-code-segment:start content-svg
|
||||
[[entrypoint]]
|
||||
id = 'content-svg'
|
||||
name = 'List Content Svg'
|
||||
path = 'src/content-svg.tsx'
|
||||
type = 'view'
|
||||
description = ''
|
||||
# docs-code-segment:end
|
||||
|
||||
# docs-code-segment:start metadata
|
||||
[[entrypoint]]
|
||||
id = 'metadata'
|
||||
|
|
@ -193,5 +202,4 @@ description = ''
|
|||
# docs-code-segment:end
|
||||
|
||||
[permissions]
|
||||
network = ["static.wikia.nocookie.net"]
|
||||
|
||||
network = ["static.wikia.nocookie.net", "upload.wikimedia.org"]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { ReactElement } from "react";
|
||||
import { List } from "@project-gauntlet/api/components";
|
||||
|
||||
const imgUrl = "https://static.wikia.nocookie.net/starwars/images/e/e4/Ezaraa.png/revision/latest/scale-to-width-down/200?cb=20170511082800"
|
||||
const imgUrl = "https://static.wikia.nocookie.net/starwars/images/9/9f/Ezaraa-TheScreamingCitadel1.png/revision/latest/scale-to-width-down/200?cb=20170511082800"
|
||||
|
||||
export default function ContentImageExample(): ReactElement {
|
||||
return (
|
||||
|
|
|
|||
17
example_plugins/plugins/ui_list/src/content-svg.tsx
Normal file
17
example_plugins/plugins/ui_list/src/content-svg.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { ReactElement } from "react";
|
||||
import { List } from "@project-gauntlet/api/components";
|
||||
|
||||
const svgUrl = "https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg"
|
||||
|
||||
export default function ContentSvgExample(): ReactElement {
|
||||
return (
|
||||
<List>
|
||||
<List.Item id="svg" title="Svg Example"/>
|
||||
<List.Detail>
|
||||
<List.Detail.Content>
|
||||
<List.Detail.Content.Svg source={{ url: svgUrl }}/>
|
||||
</List.Detail.Content>
|
||||
</List.Detail>
|
||||
</List>
|
||||
)
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ import { ReactElement, useState } from "react";
|
|||
import { List } from "@project-gauntlet/api/components";
|
||||
|
||||
export default function FocusExample(): ReactElement {
|
||||
const [id, setId] = useState<string | undefined>(undefined);
|
||||
const [id, setId] = useState<string | null>(null);
|
||||
|
||||
return (
|
||||
<List onItemFocusChange={setId}>
|
||||
|
|
|
|||
|
|
@ -2,18 +2,28 @@ import { ReactElement, useState } from "react";
|
|||
import { Action, ActionPanel, List } from "@project-gauntlet/api/components";
|
||||
|
||||
export default function SearchBarSetSearchTextExample(): ReactElement {
|
||||
const [searchText, setSearchText] = useState<string | undefined>("");
|
||||
const [searchText, setSearchText] = useState<string>("");
|
||||
|
||||
return (
|
||||
<List
|
||||
actions={
|
||||
<ActionPanel>
|
||||
<Action label="Set value" onAction={(id) => setSearchText(id)}/>
|
||||
<Action
|
||||
label="Set value"
|
||||
onAction={(id) => {
|
||||
// This will be the value in search bar when
|
||||
// enter is pressed while item is not focused
|
||||
const unfocused = "item-not-unfocused";
|
||||
|
||||
setSearchText(id || unfocused)
|
||||
}}
|
||||
/>
|
||||
</ActionPanel>
|
||||
}
|
||||
>
|
||||
<List.SearchBar value={searchText} onChange={setSearchText}/>
|
||||
<List.Item id="This will be the value in search bar" title="Click me!"/>
|
||||
{/* This id will be the value in search bar when clicked or press enter when item is focused */}
|
||||
<List.Item id="item-focused" title="Click me!"/>
|
||||
</List>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
"type": "ViewWithActionPanel"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
"type": "ViewWithActionPanel"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"type": "View"
|
||||
}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"type": "View"
|
||||
}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
3
example_plugins/scenarios/ui_grid/focus/default.json
Normal file
3
example_plugins/scenarios/ui_grid/focus/default.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"type": "View"
|
||||
}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"type": "RequestViewRender"
|
||||
}
|
||||
"type": "View"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"type": "Search",
|
||||
"type": "InlineView",
|
||||
"text": "example"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"type": "Search",
|
||||
"type": "InlineView",
|
||||
"text": "example"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"type": "Search",
|
||||
"type": "InlineView",
|
||||
"text": "example"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"type": "Search",
|
||||
"type": "InlineView",
|
||||
"text": "example"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"type": "Search",
|
||||
"type": "InlineView",
|
||||
"text": "1 + 2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"type": "Search",
|
||||
"type": "InlineView",
|
||||
"text": "example"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"type": "Search",
|
||||
"type": "InlineView",
|
||||
"text": "example"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
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