commit
152402b74e
2
.github/scripts/ubuntu/Dockerfile
vendored
2
.github/scripts/ubuntu/Dockerfile
vendored
|
@ -31,7 +31,7 @@ RUN set -eux; \
|
|||
cargo --version; \
|
||||
rustc --version;
|
||||
|
||||
RUN mkdir espanso && cargo install --force cargo-make --version 0.34.0
|
||||
RUN mkdir espanso && cargo install rust-script --version "0.7.0" && cargo install --force cargo-make --version 0.34.0
|
||||
|
||||
COPY . espanso
|
||||
|
||||
|
|
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
|
@ -33,8 +33,9 @@ jobs:
|
|||
cargo clippy -- -D warnings
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: "10.13"
|
||||
- name: Install cargo-make
|
||||
- name: Install rust-script and cargo-make
|
||||
run: |
|
||||
cargo install rust-script --version "0.7.0"
|
||||
cargo install --force cargo-make --version 0.34.0
|
||||
- name: Run test suite
|
||||
run: cargo make test-binary
|
||||
|
@ -58,8 +59,9 @@ jobs:
|
|||
run: |
|
||||
rustup component add clippy
|
||||
cargo clippy -p espanso --features wayland -- -D warnings
|
||||
- name: Install cargo-make
|
||||
- name: Install rust-script and cargo-make
|
||||
run: |
|
||||
cargo install rust-script --version "0.7.0"
|
||||
cargo install --force cargo-make --version 0.34.0
|
||||
- name: Run test suite
|
||||
run: cargo make test-binary --env NO_X11=true
|
||||
|
@ -72,8 +74,9 @@ jobs:
|
|||
- uses: actions/checkout@v2
|
||||
- name: Install target
|
||||
run: rustup update && rustup target add aarch64-apple-darwin
|
||||
- name: Install cargo-make
|
||||
- name: Install rust-script and cargo-make
|
||||
run: |
|
||||
cargo install rust-script --version "0.7.0"
|
||||
cargo install --force cargo-make --version 0.34.0
|
||||
- name: Build
|
||||
run: |
|
||||
|
|
9
.github/workflows/release.yml
vendored
9
.github/workflows/release.yml
vendored
|
@ -59,8 +59,9 @@ jobs:
|
|||
- name: Print target version
|
||||
run: |
|
||||
echo Using version ${{ needs.extract-version.outputs.espanso_version }}
|
||||
- name: Install cargo-make
|
||||
- name: Install rust-script and cargo-make
|
||||
run: |
|
||||
cargo install rust-script --version "0.7.0"
|
||||
cargo install --force cargo-make --version 0.34.0
|
||||
- name: Test
|
||||
run: cargo make test-binary --profile release
|
||||
|
@ -126,8 +127,9 @@ jobs:
|
|||
- name: Print target version
|
||||
run: |
|
||||
echo Using version ${{ needs.extract-version.outputs.espanso_version }}
|
||||
- name: Install cargo-make
|
||||
- name: Install rust-script and cargo-make
|
||||
run: |
|
||||
cargo install rust-script --version "0.7.0"
|
||||
cargo install --force cargo-make --version 0.34.0
|
||||
- name: Test
|
||||
run: cargo make test-binary --profile release
|
||||
|
@ -166,8 +168,9 @@ jobs:
|
|||
echo Using version ${{ needs.extract-version.outputs.espanso_version }}
|
||||
- name: Install rust target
|
||||
run: rustup update && rustup target add aarch64-apple-darwin
|
||||
- name: Install cargo-make
|
||||
- name: Install rust-script and cargo-make
|
||||
run: |
|
||||
cargo install rust-script --version "0.7.0"
|
||||
cargo install --force cargo-make --version 0.34.0
|
||||
- name: Build
|
||||
run: cargo make create-bundle --profile release --env BUILD_ARCH=aarch64-apple-darwin
|
||||
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -572,7 +572,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "espanso"
|
||||
version = "2.1.1-alpha"
|
||||
version = "2.1.2-alpha"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"caps",
|
||||
|
|
|
@ -49,18 +49,11 @@ pub trait Clipboard {
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Default)]
|
||||
pub struct ClipboardOperationOptions {
|
||||
pub use_xclip_backend: bool,
|
||||
}
|
||||
|
||||
impl Default for ClipboardOperationOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
use_xclip_backend: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct ClipboardOptions {
|
||||
// Wayland-only
|
||||
|
|
|
@ -135,7 +135,7 @@ impl Clipboard for WaylandFallbackClipboard {
|
|||
file.read_to_end(&mut data)?;
|
||||
|
||||
self.invoke_command_with_timeout(
|
||||
&mut Command::new("wl-copy").arg("--type").arg("image/png"),
|
||||
Command::new("wl-copy").arg("--type").arg("image/png"),
|
||||
&data,
|
||||
"wl-copy",
|
||||
)
|
||||
|
@ -148,7 +148,7 @@ impl Clipboard for WaylandFallbackClipboard {
|
|||
_: &ClipboardOperationOptions,
|
||||
) -> anyhow::Result<()> {
|
||||
self.invoke_command_with_timeout(
|
||||
&mut Command::new("wl-copy").arg("--type").arg("text/html"),
|
||||
Command::new("wl-copy").arg("--type").arg("text/html"),
|
||||
html.as_bytes(),
|
||||
"wl-copy",
|
||||
)
|
||||
|
|
|
@ -30,7 +30,7 @@ pub struct XClipClipboard {
|
|||
|
||||
impl XClipClipboard {
|
||||
pub fn new() -> Self {
|
||||
let command = Command::new("xclipz").arg("-h").output();
|
||||
let command = Command::new("xclip").arg("-h").output();
|
||||
let is_xclip_available = command
|
||||
.map(|output| output.status.success())
|
||||
.unwrap_or(false);
|
||||
|
|
|
@ -37,7 +37,7 @@ use thiserror::Error;
|
|||
|
||||
const STANDARD_INCLUDES: &[&str] = &["../match/**/[!_]*.yml"];
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub(crate) struct ResolvedConfig {
|
||||
parsed: ParsedConfig,
|
||||
|
||||
|
@ -52,20 +52,6 @@ pub(crate) struct ResolvedConfig {
|
|||
filter_exec: Option<Regex>,
|
||||
}
|
||||
|
||||
impl Default for ResolvedConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
parsed: Default::default(),
|
||||
source_path: None,
|
||||
id: 0,
|
||||
match_paths: Vec::new(),
|
||||
filter_title: None,
|
||||
filter_class: None,
|
||||
filter_exec: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config for ResolvedConfig {
|
||||
fn id(&self) -> i32 {
|
||||
self.id
|
||||
|
@ -202,13 +188,10 @@ impl Config for ResolvedConfig {
|
|||
Some("left_shift") => Some(ToggleKey::LeftShift),
|
||||
Some("left_meta") | Some("left_cmd") => Some(ToggleKey::LeftMeta),
|
||||
Some("off") => None,
|
||||
None => Some(ToggleKey::Alt),
|
||||
None => None,
|
||||
err => {
|
||||
error!(
|
||||
"invalid toggle_key specified {:?}, falling back to ALT",
|
||||
err
|
||||
);
|
||||
Some(ToggleKey::Alt)
|
||||
error!("invalid toggle_key specified {:?}", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -701,18 +701,15 @@ impl LegacyConfigSet {
|
|||
let mut sorted_triggers: Vec<String> = default
|
||||
.matches
|
||||
.iter()
|
||||
.flat_map(|t| triggers_for_match(t))
|
||||
.flat_map(triggers_for_match)
|
||||
.collect();
|
||||
sorted_triggers.sort();
|
||||
|
||||
let mut has_conflicts = Self::list_has_conflicts(&sorted_triggers);
|
||||
|
||||
for s in specific.iter() {
|
||||
let mut specific_triggers: Vec<String> = s
|
||||
.matches
|
||||
.iter()
|
||||
.flat_map(|t| triggers_for_match(t))
|
||||
.collect();
|
||||
let mut specific_triggers: Vec<String> =
|
||||
s.matches.iter().flat_map(triggers_for_match).collect();
|
||||
specific_triggers.sort();
|
||||
has_conflicts |= Self::list_has_conflicts(&specific_triggers);
|
||||
}
|
||||
|
|
|
@ -27,23 +27,13 @@ use super::{Match, Variable};
|
|||
pub(crate) mod loader;
|
||||
mod path;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub(crate) struct MatchGroup {
|
||||
pub imports: Vec<String>,
|
||||
pub global_vars: Vec<Variable>,
|
||||
pub matches: Vec<Match>,
|
||||
}
|
||||
|
||||
impl Default for MatchGroup {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
imports: Vec::new(),
|
||||
global_vars: Vec::new(),
|
||||
matches: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MatchGroup {
|
||||
// TODO: test
|
||||
pub fn load(group_path: &Path) -> Result<(Self, Option<NonFatalErrorSet>)> {
|
||||
|
|
|
@ -132,19 +132,11 @@ pub enum UpperCasingStyle {
|
|||
CapitalizeWords,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
|
||||
pub struct RegexCause {
|
||||
pub regex: String,
|
||||
}
|
||||
|
||||
impl Default for RegexCause {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
regex: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Effects
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, EnumAsInner)]
|
||||
|
@ -186,19 +178,11 @@ impl Default for TextEffect {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
|
||||
pub struct ImageEffect {
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
impl Default for ImageEffect {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
path: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Variable {
|
||||
pub id: StructId,
|
||||
|
|
|
@ -89,6 +89,7 @@ impl Default for InjectionOptions {
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Default)]
|
||||
pub struct InjectorCreationOptions {
|
||||
// Only relevant in X11 Linux systems, use the EVDEV backend instead of X11.
|
||||
pub use_evdev: bool,
|
||||
|
@ -128,18 +129,6 @@ pub trait KeyboardStateProvider {
|
|||
fn is_key_pressed(&self, code: u32) -> bool;
|
||||
}
|
||||
|
||||
impl Default for InjectorCreationOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
use_evdev: false,
|
||||
evdev_modifiers: None,
|
||||
evdev_max_modifier_combination_len: None,
|
||||
evdev_keyboard_rmlvo: None,
|
||||
keyboard_state_provider: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn get_injector(_options: InjectorCreationOptions) -> Result<Box<dyn Injector>> {
|
||||
info!("using Win32Injector");
|
||||
|
|
|
@ -40,19 +40,11 @@ impl<Id> RegexMatch<Id> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct RegexMatcherState {
|
||||
buffer: String,
|
||||
}
|
||||
|
||||
impl Default for RegexMatcherState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
buffer: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RegexMatcherOptions {
|
||||
pub max_buffer_size: usize,
|
||||
}
|
||||
|
|
|
@ -50,20 +50,12 @@ struct RollingMatcherStatePath<'a, Id> {
|
|||
events: Vec<(Event, IsWordSeparator)>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RollingMatcherOptions {
|
||||
pub char_word_separators: Vec<String>,
|
||||
pub key_word_separators: Vec<Key>,
|
||||
}
|
||||
|
||||
impl Default for RollingMatcherOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
char_word_separators: Vec::new(),
|
||||
key_word_separators: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RollingMatcher<Id> {
|
||||
char_word_separators: Vec<String>,
|
||||
key_word_separators: Vec<Key>,
|
||||
|
|
|
@ -72,22 +72,13 @@ impl<Id> RollingMatch<Id> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct StringMatchOptions {
|
||||
pub case_insensitive: bool,
|
||||
pub left_word: bool,
|
||||
pub right_word: bool,
|
||||
}
|
||||
|
||||
impl Default for StringMatchOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
case_insensitive: false,
|
||||
left_word: false,
|
||||
right_word: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -96,6 +96,7 @@ private:
|
|||
void Submit();
|
||||
void OnSubmitBtn(wxCommandEvent& event);
|
||||
void OnCharHook(wxKeyEvent& event);
|
||||
void OnListBoxEvent(wxCommandEvent& event);
|
||||
void UpdateHelpText();
|
||||
void HandleNormalFocus(wxFocusEvent& event);
|
||||
void HandleMultilineFocus(wxFocusEvent& event);
|
||||
|
@ -141,7 +142,6 @@ FormFrame::FormFrame(const wxString& title, const wxPoint& pos, const wxSize& si
|
|||
|
||||
Bind(wxEVT_BUTTON, &FormFrame::OnSubmitBtn, this, ID_Submit);
|
||||
Bind(wxEVT_CHAR_HOOK, &FormFrame::OnCharHook, this, wxID_ANY);
|
||||
// TODO: register ESC click handler: https://forums.wxwidgets.org/viewtopic.php?t=41926
|
||||
|
||||
this->SetClientSize(panel->GetBestSize());
|
||||
this->CentreOnScreen();
|
||||
|
@ -218,6 +218,11 @@ void FormFrame::AddComponent(wxPanel *parent, wxBoxSizer *sizer, FieldMetadata m
|
|||
}
|
||||
|
||||
((wxListBox*)choice)->Bind(wxEVT_SET_FOCUS, &FormFrame::HandleNormalFocus, this, wxID_ANY);
|
||||
// ListBoxes prevent the global CHAR_HOOK handler from handling the Return key
|
||||
// correctly, so we need to handle the double click event too (which is triggered
|
||||
// when the enter key is pressed).
|
||||
// See: https://github.com/federico-terzi/espanso/issues/857
|
||||
((wxListBox*)choice)->Bind(wxEVT_LISTBOX_DCLICK, &FormFrame::OnListBoxEvent, this, wxID_ANY);
|
||||
|
||||
// Create the field wrapper
|
||||
std::unique_ptr<FieldWrapper> field((FieldWrapper*) new ListFieldWrapper((wxListBox*) choice));
|
||||
|
@ -311,6 +316,10 @@ void FormFrame::OnCharHook(wxKeyEvent& event) {
|
|||
}
|
||||
}
|
||||
|
||||
void FormFrame::OnListBoxEvent(wxCommandEvent& event) {
|
||||
Submit();
|
||||
}
|
||||
|
||||
extern "C" void interop_show_form(FormMetadata * _metadata, void (*callback)(ValuePair *values, int size, void *data), void *data) {
|
||||
// Setup high DPI support on Windows
|
||||
#ifdef __WXMSW__
|
||||
|
|
132
espanso-render/src/extension/choice.rs
Normal file
132
espanso-render/src/extension/choice.rs
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2021 Federico Terzi
|
||||
*
|
||||
* espanso is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* espanso is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use anyhow::Result;
|
||||
use log::error;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{Extension, ExtensionOutput, ExtensionResult, Params, Value};
|
||||
|
||||
pub trait ChoiceSelector {
|
||||
fn show(&self, choices: &[Choice]) -> ChoiceSelectorResult;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Choice<'a> {
|
||||
pub label: &'a str,
|
||||
pub id: &'a str,
|
||||
}
|
||||
|
||||
pub enum ChoiceSelectorResult {
|
||||
Success(String),
|
||||
Aborted,
|
||||
Error(anyhow::Error),
|
||||
}
|
||||
|
||||
pub struct ChoiceExtension<'a> {
|
||||
selector: &'a dyn ChoiceSelector,
|
||||
}
|
||||
|
||||
#[allow(clippy::new_without_default)]
|
||||
impl<'a> ChoiceExtension<'a> {
|
||||
pub fn new(selector: &'a dyn ChoiceSelector) -> Self {
|
||||
Self { selector }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Extension for ChoiceExtension<'a> {
|
||||
fn name(&self) -> &str {
|
||||
"choice"
|
||||
}
|
||||
|
||||
fn calculate(
|
||||
&self,
|
||||
_: &crate::Context,
|
||||
_: &crate::Scope,
|
||||
params: &Params,
|
||||
) -> crate::ExtensionResult {
|
||||
let choices: Vec<Choice> = if let Some(Value::String(values)) = params.get("values") {
|
||||
values
|
||||
.lines()
|
||||
.filter_map(|line| {
|
||||
let trimmed_line = line.trim();
|
||||
if !trimmed_line.is_empty() {
|
||||
Some(trimmed_line)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(|line| Choice {
|
||||
label: line,
|
||||
id: line,
|
||||
})
|
||||
.collect()
|
||||
} else if let Some(Value::Array(values)) = params.get("values") {
|
||||
let choices: Result<Vec<Choice>> = values
|
||||
.iter()
|
||||
.map(|value| match value {
|
||||
Value::String(string) => Ok(Choice {
|
||||
id: string,
|
||||
label: string,
|
||||
}),
|
||||
Value::Object(fields) => Ok(Choice {
|
||||
id: fields
|
||||
.get("id")
|
||||
.and_then(|val| val.as_string())
|
||||
.ok_or(ChoiceError::InvalidObjectValue)?,
|
||||
label: fields
|
||||
.get("label")
|
||||
.and_then(|val| val.as_string())
|
||||
.ok_or(ChoiceError::InvalidObjectValue)?,
|
||||
}),
|
||||
_ => Err(ChoiceError::InvalidValueType.into()),
|
||||
})
|
||||
.collect();
|
||||
|
||||
match choices {
|
||||
Ok(choices) => choices,
|
||||
Err(err) => {
|
||||
return crate::ExtensionResult::Error(err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return crate::ExtensionResult::Error(ChoiceError::MissingValues.into());
|
||||
};
|
||||
|
||||
match self.selector.show(&choices) {
|
||||
ChoiceSelectorResult::Success(choice_id) => {
|
||||
ExtensionResult::Success(ExtensionOutput::Single(choice_id))
|
||||
}
|
||||
ChoiceSelectorResult::Aborted => ExtensionResult::Aborted,
|
||||
ChoiceSelectorResult::Error(error) => ExtensionResult::Error(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ChoiceError {
|
||||
#[error("missing values parameter")]
|
||||
MissingValues,
|
||||
|
||||
#[error("values contain object items, but they are missing either the 'id' or 'label' fields")]
|
||||
InvalidObjectValue,
|
||||
|
||||
#[error("values contain an invalid item type. items can only be strings or objects")]
|
||||
InvalidValueType,
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pub mod choice;
|
||||
pub mod clipboard;
|
||||
pub mod date;
|
||||
pub mod echo;
|
||||
|
|
|
@ -42,20 +42,12 @@ pub enum RenderResult {
|
|||
Error(anyhow::Error),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Context<'a> {
|
||||
pub global_vars: Vec<&'a Variable>,
|
||||
pub templates: Vec<&'a Template>,
|
||||
}
|
||||
|
||||
impl<'a> Default for Context<'a> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
global_vars: Vec::new(),
|
||||
templates: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct RenderOptions {
|
||||
pub casing_style: CasingStyle,
|
||||
|
@ -77,23 +69,13 @@ pub enum CasingStyle {
|
|||
Uppercase,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct Template {
|
||||
pub ids: Vec<String>,
|
||||
pub body: String,
|
||||
pub vars: Vec<Variable>,
|
||||
}
|
||||
|
||||
impl Default for Template {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ids: Vec::new(),
|
||||
body: "".to_string(),
|
||||
vars: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Variable {
|
||||
pub name: String,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "espanso"
|
||||
version = "2.1.1-alpha"
|
||||
version = "2.1.2-alpha"
|
||||
authors = ["Federico Terzi <federicoterzi96@gmail.com>"]
|
||||
license = "GPL-3.0"
|
||||
description = "Cross-platform Text Expander written in Rust"
|
||||
|
|
|
@ -74,6 +74,7 @@ pub enum LogMode {
|
|||
CleanAndAppend,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CliModuleArgs {
|
||||
pub config_store: Option<Box<dyn ConfigStore>>,
|
||||
pub match_store: Option<Box<dyn MatchStore>>,
|
||||
|
@ -84,20 +85,6 @@ pub struct CliModuleArgs {
|
|||
pub cli_args: Option<ArgMatches<'static>>,
|
||||
}
|
||||
|
||||
impl Default for CliModuleArgs {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
config_store: None,
|
||||
match_store: None,
|
||||
is_legacy_config: false,
|
||||
non_fatal_errors: Vec::new(),
|
||||
paths: None,
|
||||
paths_overrides: None,
|
||||
cli_args: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PathsOverrides {
|
||||
pub config: Option<PathBuf>,
|
||||
pub runtime: Option<PathBuf>,
|
||||
|
|
|
@ -81,6 +81,11 @@ impl<'a> ClipboardInjectorAdapter<'a> {
|
|||
custom_combination
|
||||
} else if cfg!(target_os = "macos") {
|
||||
vec![Key::Meta, Key::V]
|
||||
} else if cfg!(target_os = "linux") && cfg!(feature = "wayland") {
|
||||
// Because on Wayland we currently don't have app-specific configs (and therefore no patches)
|
||||
// we switch to the more supported SHIFT+INSERT combination
|
||||
// See: https://github.com/federico-terzi/espanso/issues/899
|
||||
vec![Key::Shift, Key::Insert]
|
||||
} else {
|
||||
vec![Key::Control, Key::V]
|
||||
};
|
||||
|
|
|
@ -58,11 +58,7 @@ fn convert_to_ui_menu_item(
|
|||
espanso_engine::event::ui::MenuItem::Sub(sub) => {
|
||||
espanso_ui::menu::MenuItem::Sub(espanso_ui::menu::SubMenuItem {
|
||||
label: sub.label.clone(),
|
||||
items: sub
|
||||
.items
|
||||
.iter()
|
||||
.map(|item| convert_to_ui_menu_item(item))
|
||||
.collect(),
|
||||
items: sub.items.iter().map(convert_to_ui_menu_item).collect(),
|
||||
})
|
||||
}
|
||||
espanso_engine::event::ui::MenuItem::Separator => espanso_ui::menu::MenuItem::Separator,
|
||||
|
|
|
@ -76,18 +76,11 @@ impl KeyStateStore {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct KeyState {
|
||||
keys: HashMap<u32, KeyStatus>,
|
||||
}
|
||||
|
||||
impl Default for KeyState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
keys: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct KeyStatus {
|
||||
pressed_at: Option<Instant>,
|
||||
}
|
||||
|
|
|
@ -49,7 +49,9 @@ use crate::{
|
|||
},
|
||||
multiplex::MultiplexAdapter,
|
||||
render::{
|
||||
extension::{clipboard::ClipboardAdapter, form::FormProviderAdapter},
|
||||
extension::{
|
||||
choice::ChoiceSelectorAdapter, clipboard::ClipboardAdapter, form::FormProviderAdapter,
|
||||
},
|
||||
RendererAdapter,
|
||||
},
|
||||
},
|
||||
|
@ -198,6 +200,9 @@ pub fn initialize_and_spawn(
|
|||
let shell_extension = espanso_render::extension::shell::ShellExtension::new(&paths.config);
|
||||
let form_adapter = FormProviderAdapter::new(&modulo_form_ui);
|
||||
let form_extension = espanso_render::extension::form::FormExtension::new(&form_adapter);
|
||||
let choice_adapter = ChoiceSelectorAdapter::new(&modulo_search_ui);
|
||||
let choice_extension =
|
||||
espanso_render::extension::choice::ChoiceExtension::new(&choice_adapter);
|
||||
let renderer = espanso_render::create(vec![
|
||||
&clipboard_extension,
|
||||
&date_extension,
|
||||
|
@ -207,6 +212,7 @@ pub fn initialize_and_spawn(
|
|||
&script_extension,
|
||||
&shell_extension,
|
||||
&form_extension,
|
||||
&choice_extension,
|
||||
]);
|
||||
let renderer_adapter = RendererAdapter::new(&match_cache, &config_manager, &renderer);
|
||||
let path_provider = PathProviderAdapter::new(&paths);
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2021 Federico Terzi
|
||||
*
|
||||
* espanso is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* espanso is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use espanso_render::extension::choice::{ChoiceSelector, ChoiceSelectorResult};
|
||||
|
||||
use crate::gui::{SearchItem, SearchUI};
|
||||
|
||||
pub struct ChoiceSelectorAdapter<'a> {
|
||||
search_ui: &'a dyn SearchUI,
|
||||
}
|
||||
|
||||
impl<'a> ChoiceSelectorAdapter<'a> {
|
||||
pub fn new(search_ui: &'a dyn SearchUI) -> Self {
|
||||
Self { search_ui }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ChoiceSelector for ChoiceSelectorAdapter<'a> {
|
||||
fn show(&self, choices: &[espanso_render::extension::choice::Choice]) -> ChoiceSelectorResult {
|
||||
let items = convert_items(choices);
|
||||
match self.search_ui.show(&items, None) {
|
||||
Ok(Some(choice)) => ChoiceSelectorResult::Success(choice),
|
||||
Ok(None) => ChoiceSelectorResult::Aborted,
|
||||
Err(err) => ChoiceSelectorResult::Error(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_items(choices: &[espanso_render::extension::choice::Choice]) -> Vec<SearchItem> {
|
||||
choices
|
||||
.iter()
|
||||
.map(|choice| SearchItem {
|
||||
id: choice.id.to_string(),
|
||||
label: choice.label.to_string(),
|
||||
tag: None,
|
||||
is_builtin: false,
|
||||
})
|
||||
.collect()
|
||||
}
|
|
@ -17,5 +17,6 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pub mod choice;
|
||||
pub mod clipboard;
|
||||
pub mod form;
|
||||
|
|
|
@ -31,6 +31,7 @@ pub fn patch_store(store: Box<dyn ConfigStore>) -> Box<dyn ConfigStore> {
|
|||
fn get_builtin_patches() -> Vec<PatchDefinition> {
|
||||
#[cfg(target_os = "windows")]
|
||||
return vec![
|
||||
patches::win::brave::patch(),
|
||||
patches::win::onenote_for_windows_10::patch(),
|
||||
patches::win::vscode_win::patch(),
|
||||
];
|
||||
|
|
41
espanso/src/patch/patches/win/brave.rs
Normal file
41
espanso/src/patch/patches/win/brave.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2021 Federico Terzi
|
||||
*
|
||||
* espanso is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* espanso is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::patch::patches::{PatchedConfig, Patches};
|
||||
use crate::patch::PatchDefinition;
|
||||
|
||||
pub fn patch() -> PatchDefinition {
|
||||
PatchDefinition {
|
||||
name: module_path!().split(':').last().unwrap_or("unknown"),
|
||||
is_enabled: || cfg!(target_os = "windows"),
|
||||
should_patch: |app| app.exec.unwrap_or_default().contains("brave.exe"),
|
||||
apply: |base, name| {
|
||||
Arc::new(PatchedConfig::patch(
|
||||
base,
|
||||
name,
|
||||
Patches {
|
||||
pre_paste_delay: Some(400),
|
||||
..Default::default()
|
||||
},
|
||||
))
|
||||
},
|
||||
}
|
||||
}
|
|
@ -17,5 +17,6 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pub mod brave;
|
||||
pub mod onenote_for_windows_10;
|
||||
pub mod vscode_win;
|
||||
|
|
254
packager.py
254
packager.py
|
@ -1,254 +0,0 @@
|
|||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import platform
|
||||
import hashlib
|
||||
import click
|
||||
import shutil
|
||||
import toml
|
||||
import hashlib
|
||||
import glob
|
||||
import urllib.request
|
||||
from dataclasses import dataclass
|
||||
|
||||
PACKAGER_TARGET_DIR = "target/packager"
|
||||
|
||||
@dataclass
|
||||
class PackageInfo:
|
||||
name: str
|
||||
version: str
|
||||
description: str
|
||||
publisher: str
|
||||
url: str
|
||||
modulo_version: str
|
||||
|
||||
@click.group()
|
||||
def cli():
|
||||
pass
|
||||
|
||||
@cli.command()
|
||||
@click.option('--skipcargo', default=False, is_flag=True, help="Skip cargo release build")
|
||||
def build(skipcargo):
|
||||
"""Build espanso distribution"""
|
||||
# Check operating system
|
||||
TARGET_OS = "macos"
|
||||
if platform.system() == "Windows":
|
||||
TARGET_OS = "windows"
|
||||
elif platform.system() == "Linux":
|
||||
TARGET_OS = "linux"
|
||||
|
||||
print("Detected OS:", TARGET_OS)
|
||||
|
||||
print("Loading info from Cargo.toml")
|
||||
cargo_info = toml.load("Cargo.toml")
|
||||
package_info = PackageInfo(cargo_info["package"]["name"],
|
||||
cargo_info["package"]["version"],
|
||||
cargo_info["package"]["description"],
|
||||
cargo_info["package"]["authors"][0],
|
||||
cargo_info["package"]["homepage"],
|
||||
cargo_info["modulo"]["version"])
|
||||
print(package_info)
|
||||
|
||||
if not skipcargo:
|
||||
print("Building release version...")
|
||||
subprocess.run(["cargo", "build", "--release"])
|
||||
else:
|
||||
print("Skipping build")
|
||||
|
||||
if TARGET_OS == "windows":
|
||||
build_windows(package_info)
|
||||
elif TARGET_OS == "macos":
|
||||
build_mac(package_info)
|
||||
|
||||
def calculate_sha256(file):
|
||||
with open(file, "rb") as f:
|
||||
b = f.read() # read entire file as bytes
|
||||
readable_hash = hashlib.sha256(b).hexdigest()
|
||||
return readable_hash
|
||||
|
||||
def build_windows(package_info):
|
||||
print("Starting packaging process for Windows...")
|
||||
|
||||
# Check Inno Setup
|
||||
try:
|
||||
subprocess.run(["iscc"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
except FileNotFoundError:
|
||||
raise Exception("Could not find Inno Setup compiler. Please install it from here: http://www.jrsoftware.org/isdl.php")
|
||||
|
||||
print("Clearing target dirs")
|
||||
|
||||
# Clearing previous build directory
|
||||
if os.path.isdir(PACKAGER_TARGET_DIR):
|
||||
print("Cleaning packager temp directory...")
|
||||
shutil.rmtree(PACKAGER_TARGET_DIR)
|
||||
|
||||
TARGET_DIR = os.path.join(PACKAGER_TARGET_DIR, "win")
|
||||
os.makedirs(TARGET_DIR, exist_ok=True)
|
||||
|
||||
modulo_url = "https://github.com/federico-terzi/modulo/releases/download/v{0}/modulo-win.exe".format(package_info.modulo_version)
|
||||
modulo_sha_url = "https://github.com/federico-terzi/modulo/releases/download/v{0}/modulo-win.exe.sha256.txt".format(package_info.modulo_version)
|
||||
print("Pulling modulo depencency from:", modulo_url)
|
||||
modulo_target_file = os.path.join(TARGET_DIR, "modulo.exe")
|
||||
urllib.request.urlretrieve(modulo_url, modulo_target_file)
|
||||
print("Pulling SHA signature from:", modulo_sha_url)
|
||||
modulo_sha_file = os.path.join(TARGET_DIR, "modulo.sha256")
|
||||
urllib.request.urlretrieve(modulo_sha_url, modulo_sha_file)
|
||||
print("Checking signatures...")
|
||||
expected_sha = None
|
||||
with open(modulo_sha_file, "r") as sha_f:
|
||||
expected_sha = sha_f.read()
|
||||
actual_sha = calculate_sha256(modulo_target_file)
|
||||
if actual_sha != expected_sha:
|
||||
raise Exception("Modulo SHA256 is not matching")
|
||||
|
||||
print("Gathering CRT DLLs...")
|
||||
msvc_dirs = glob.glob("C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\*\\VC\\Redist\\MSVC\\*")
|
||||
print("Found Redists: ", msvc_dirs)
|
||||
|
||||
print("Determining best redist...")
|
||||
|
||||
if len(msvc_dirs) == 0:
|
||||
raise Exception("Cannot find redistributable dlls")
|
||||
|
||||
msvc_dir = None
|
||||
|
||||
for curr_dir in msvc_dirs:
|
||||
dll_files = glob.glob(curr_dir + "\\x64\\*CRT\\*.dll")
|
||||
print("Found dlls", dll_files, "in", curr_dir)
|
||||
if any("vcruntime140_1.dll" in x.lower() for x in dll_files):
|
||||
msvc_dir = curr_dir
|
||||
break
|
||||
|
||||
if msvc_dir is None:
|
||||
raise Exception("Cannot find redist with VCRUNTIME140_1.dll")
|
||||
|
||||
print("Using: ", msvc_dir)
|
||||
|
||||
dll_files = glob.glob(msvc_dir + "\\x64\\*CRT\\*.dll")
|
||||
|
||||
print("Found DLLs:")
|
||||
include_list = []
|
||||
for dll in dll_files:
|
||||
print("Including: "+dll)
|
||||
include_list.append("Source: \""+dll+"\"; DestDir: \"{app}\"; Flags: ignoreversion")
|
||||
|
||||
print("Including modulo")
|
||||
include_list.append("Source: \""+os.path.abspath(modulo_target_file)+"\"; DestDir: \"{app}\"; Flags: ignoreversion")
|
||||
|
||||
include = "\r\n".join(include_list)
|
||||
|
||||
INSTALLER_NAME = f"espanso-win-installer"
|
||||
|
||||
# Inno setup
|
||||
shutil.copy("packager/win/modpath.iss", os.path.join(TARGET_DIR, "modpath.iss"))
|
||||
|
||||
print("Processing inno setup template")
|
||||
with open("packager/win/setupscript.iss", "r") as iss_script:
|
||||
content = iss_script.read()
|
||||
|
||||
# Replace variables
|
||||
content = content.replace("{{{app_name}}}", package_info.name)
|
||||
content = content.replace("{{{app_version}}}", package_info.version)
|
||||
content = content.replace("{{{app_publisher}}}", package_info.publisher)
|
||||
content = content.replace("{{{app_url}}}", package_info.url)
|
||||
content = content.replace("{{{app_license}}}", os.path.abspath("LICENSE"))
|
||||
content = content.replace("{{{app_icon}}}", os.path.abspath("packager/win/icon.ico"))
|
||||
content = content.replace("{{{executable_path}}}", os.path.abspath("target/release/espanso.exe"))
|
||||
content = content.replace("{{{output_dir}}}", os.path.abspath(TARGET_DIR))
|
||||
content = content.replace("{{{output_name}}}", INSTALLER_NAME)
|
||||
content = content.replace("{{{dll_include}}}", include)
|
||||
|
||||
with open(os.path.join(TARGET_DIR, "setupscript.iss"), "w") as output_script:
|
||||
output_script.write(content)
|
||||
|
||||
print("Compiling installer with Inno setup")
|
||||
subprocess.run(["iscc", os.path.abspath(os.path.join(TARGET_DIR, "setupscript.iss"))])
|
||||
|
||||
print("Calculating the SHA256")
|
||||
sha256_hash = hashlib.sha256()
|
||||
with open(os.path.abspath(os.path.join(TARGET_DIR, INSTALLER_NAME+".exe")),"rb") as f:
|
||||
# Read and update hash string value in blocks of 4K
|
||||
for byte_block in iter(lambda: f.read(4096),b""):
|
||||
sha256_hash.update(byte_block)
|
||||
|
||||
hash_file = os.path.abspath(os.path.join(TARGET_DIR, "espanso-win-installer-sha256.txt"))
|
||||
with open(hash_file, "w") as hf:
|
||||
hf.write(sha256_hash.hexdigest())
|
||||
|
||||
|
||||
def build_mac(package_info):
|
||||
print("Starting packaging process for MacOS...")
|
||||
|
||||
print("Clearing target dirs")
|
||||
|
||||
# Clearing previous build directory
|
||||
if os.path.isdir(PACKAGER_TARGET_DIR):
|
||||
print("Cleaning packager temp directory...")
|
||||
shutil.rmtree(PACKAGER_TARGET_DIR)
|
||||
|
||||
TARGET_DIR = os.path.join(PACKAGER_TARGET_DIR, "mac")
|
||||
os.makedirs(TARGET_DIR, exist_ok=True)
|
||||
|
||||
print("Compressing release to archive...")
|
||||
target_name = f"espanso-mac.tar.gz"
|
||||
archive_target = os.path.abspath(os.path.join(TARGET_DIR, target_name))
|
||||
subprocess.run(["tar",
|
||||
"-C", os.path.abspath("target/release"),
|
||||
"-cvf",
|
||||
archive_target,
|
||||
"espanso",
|
||||
])
|
||||
print(f"Created archive: {archive_target}")
|
||||
|
||||
print("Calculating the SHA256")
|
||||
sha256_hash = hashlib.sha256()
|
||||
with open(archive_target,"rb") as f:
|
||||
# Read and update hash string value in blocks of 4K
|
||||
for byte_block in iter(lambda: f.read(4096),b""):
|
||||
sha256_hash.update(byte_block)
|
||||
|
||||
hash_file = os.path.abspath(os.path.join(TARGET_DIR, "espanso-mac-sha256.txt"))
|
||||
with open(hash_file, "w") as hf:
|
||||
hf.write(sha256_hash.hexdigest())
|
||||
|
||||
modulo_sha_url = "https://github.com/federico-terzi/modulo/releases/download/v{0}/modulo-mac.sha256.txt".format(package_info.modulo_version)
|
||||
print("Pulling SHA signature from:", modulo_sha_url)
|
||||
modulo_sha_file = os.path.join(TARGET_DIR, "modulo.sha256")
|
||||
urllib.request.urlretrieve(modulo_sha_url, modulo_sha_file)
|
||||
modulo_sha = None
|
||||
with open(modulo_sha_file, "r") as sha_f:
|
||||
modulo_sha = sha_f.read()
|
||||
if modulo_sha is None:
|
||||
raise Exception("Cannot determine modulo SHA")
|
||||
|
||||
print("Processing Homebrew formula template")
|
||||
with open("packager/mac/espanso.rb", "r") as formula_template:
|
||||
content = formula_template.read()
|
||||
|
||||
# Replace variables
|
||||
content = content.replace("{{{app_desc}}}", package_info.description)
|
||||
content = content.replace("{{{app_url}}}", package_info.url)
|
||||
content = content.replace("{{{app_version}}}", package_info.version)
|
||||
content = content.replace("{{{modulo_version}}}", package_info.modulo_version)
|
||||
content = content.replace("{{{modulo_sha}}}", modulo_sha)
|
||||
|
||||
# Calculate hash
|
||||
with open(archive_target, "rb") as f:
|
||||
bytes = f.read()
|
||||
readable_hash = hashlib.sha256(bytes).hexdigest()
|
||||
content = content.replace("{{{release_hash}}}", readable_hash)
|
||||
|
||||
with open(os.path.join(TARGET_DIR, "espanso.rb"), "w") as output_script:
|
||||
output_script.write(content)
|
||||
|
||||
print("Done!")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("[[ espanso packager ]]")
|
||||
|
||||
# Check python version 3
|
||||
if sys.version_info[0] < 3:
|
||||
raise Exception("Must be using Python 3")
|
||||
|
||||
cli()
|
|
@ -8,7 +8,7 @@ BASE_DIR=$(pwd)
|
|||
|
||||
mkdir -p $TOOL_DIR
|
||||
|
||||
if ls $TOOL_DIR/*.AppImage 1> /dev/null 2>&1; then
|
||||
if ls $TOOL_DIR/linuxdeploy*.AppImage 1> /dev/null 2>&1; then
|
||||
echo "Skipping download of linuxdeploy"
|
||||
else
|
||||
echo "Downloading linuxdeploy tool"
|
||||
|
@ -16,12 +16,36 @@ else
|
|||
chmod +x $TOOL_DIR/linuxdeploy*.AppImage
|
||||
fi
|
||||
|
||||
if ls $TOOL_DIR/appimagetool*.AppImage 1> /dev/null 2>&1; then
|
||||
echo "Skipping download of appimagetool"
|
||||
else
|
||||
echo "Downloading appimagetool"
|
||||
wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -P "$TOOL_DIR"
|
||||
chmod +x $TOOL_DIR/appimagetool*.AppImage
|
||||
fi
|
||||
|
||||
rm -Rf "$TARGET_DIR"
|
||||
mkdir -p $OUTPUT_DIR
|
||||
mkdir -p $BUILD_DIR
|
||||
|
||||
echo Building AppImage into $OUTPUT_DIR
|
||||
pushd $OUTPUT_DIR
|
||||
$TOOL_DIR/linuxdeploy*.AppImage --appimage-extract-and-run -e "$BASE_DIR/$EXEC_PATH" -d "$BASE_DIR/espanso/src/res/linux/espanso.desktop" -i "$BASE_DIR/espanso/src/res/linux/icon.png" --appdir $BUILD_DIR --output appimage
|
||||
$TOOL_DIR/linuxdeploy*.AppImage --appimage-extract-and-run -e "$BASE_DIR/$EXEC_PATH" \
|
||||
-d "$BASE_DIR/espanso/src/res/linux/espanso.desktop" \
|
||||
-i "$BASE_DIR/espanso/src/res/linux/icon.png" \
|
||||
--appdir $BUILD_DIR \
|
||||
--output appimage
|
||||
chmod +x ./Espanso*.AppImage
|
||||
|
||||
# Apply a workaround to fix this issue: https://github.com/federico-terzi/espanso/issues/900
|
||||
# See: https://github.com/project-slippi/Ishiiruka/issues/323#issuecomment-977415376
|
||||
|
||||
echo "Applying patch for libgmodule"
|
||||
|
||||
./Espanso*.AppImage --appimage-extract
|
||||
rm -Rf ./Espanso*.AppImage
|
||||
rm -Rf squashfs-root/usr/lib/libgmodule*
|
||||
$TOOL_DIR/appimagetool*.AppImage --appimage-extract-and-run -v squashfs-root
|
||||
rm -Rf squashfs-root
|
||||
|
||||
popd
|
12
snap/hooks/remove
Executable file
12
snap/hooks/remove
Executable file
|
@ -0,0 +1,12 @@
|
|||
#!/bin/sh
|
||||
|
||||
echo "Stopping Espanso..."
|
||||
killall espanso
|
||||
|
||||
# Here I've also tried to unregister the Systemd service, but couldn't manage to.
|
||||
# The remove hook is run as Root, but the systemd service is registered as a user.
|
||||
# I couldn't find any (working) way to unregister a user systemd service as root.
|
||||
# I've also tried these solutions: https://unix.stackexchange.com/questions/552922/stop-systemd-user-services-as-root-user
|
||||
# but none seemed to work in this case.
|
||||
# If you manage to find a way to do so, feel free to open an issue so that
|
||||
# we can improve the hook! Thanks
|
|
@ -1,5 +1,5 @@
|
|||
name: espanso
|
||||
version: 2.1.1-alpha
|
||||
version: 2.1.2-alpha
|
||||
summary: A Cross-platform Text Expander written in Rust
|
||||
description: |
|
||||
espanso is a Cross-platform, Text Expander written in Rust.
|
||||
|
|
Loading…
Reference in New Issue
Block a user