diff --git a/modules/core/url-preview/index.ts b/modules/core/url-preview/index.ts index d795d59..fe8d0a1 100644 --- a/modules/core/url-preview/index.ts +++ b/modules/core/url-preview/index.ts @@ -12,6 +12,7 @@ export async function close() { break; } catch(error) {} /* error will be already logged */ } + if(global_window) { global_window.close(); global_window = undefined; diff --git a/modules/renderer-manifest/index.ts b/modules/renderer-manifest/index.ts index 3b33758..716bde8 100644 --- a/modules/renderer-manifest/index.ts +++ b/modules/renderer-manifest/index.ts @@ -1,15 +1,14 @@ /* --------------- bootstrap --------------- */ import * as RequireProxy from "../renderer/RequireProxy"; import * as path from "path"; - -RequireProxy.initialize(path.join(__dirname, "backend-impl")); - /* --------------- entry point --------------- */ import * as loader from "tc-loader"; import {Stage} from "tc-loader"; import {Arguments, process_args} from "../shared/process-arguments"; import {remote} from "electron"; +RequireProxy.initialize(path.join(__dirname, "backend-impl")); + export function initialize(manifestTarget: string) { console.log("Initializing native client for manifest target %s", manifestTarget); @@ -42,12 +41,10 @@ export function initialize(manifestTarget: string) { loader.register_task(loader.Stage.JAVASCRIPT, { name: "teaclient jquery", function: async () => { - //const jquery = require("jquery"); - //console.error(jquery); - //window.$ = jquery; + window.$ = require("jquery"); - //window.jQuery = window.$; - //Object.assign(window.$, window.jsrender = require('jsrender')); + window.jQuery = window.$; + Object.assign(window.$, window.jsrender = require('jsrender')); }, priority: 80 }); diff --git a/native/ppt/CMakeLists.txt b/native/ppt/CMakeLists.txt index 05f6402..b040e05 100644 --- a/native/ppt/CMakeLists.txt +++ b/native/ppt/CMakeLists.txt @@ -2,7 +2,7 @@ set(MODULE_NAME "teaclient_ppt") set(SOURCE_FILES src/KeyboardHook.cpp) if (MSVC) - set(SOURCE_FILES ${SOURCE_FILES} src/Win32KeyboardHook.cpp src/Win32KeyboardHookLL.cpp src/Win32KeyboardRawInput.cpp) + set(SOURCE_FILES ${SOURCE_FILES} src/Win32KeyboardHook.cpp src/Win32KeyboardRawInput.cpp) else() set(SOURCE_FILES ${SOURCE_FILES} src/X11KeyboardHook.cpp) endif() diff --git a/native/ppt/binding.cc b/native/ppt/binding.cc index d9c2f4f..76afce8 100644 --- a/native/ppt/binding.cc +++ b/native/ppt/binding.cc @@ -13,8 +13,7 @@ using namespace std; #include "src/Win32KeyboardHook.h" #else #include "src/KeyboardHook.h" -#include "src/X11KeyboardHook.h" - + #include "src/X11KeyboardHook.h" #endif diff --git a/native/ppt/src/KeyboardHook.h b/native/ppt/src/KeyboardHook.h index 753148b..c02d043 100644 --- a/native/ppt/src/KeyboardHook.h +++ b/native/ppt/src/KeyboardHook.h @@ -6,16 +6,10 @@ enum struct KeyboardHookType { X11, - - RAW_INPUT, - SYSTEM_HOOK + RAW_INPUT }; class KeyboardHook { - #ifdef HOOK_WIN32_LL - friend LRESULT CALLBACK _keyboard_hook_callback(int, WPARAM, LPARAM); - friend LRESULT CALLBACK _mouse_hook_callback(int, WPARAM, LPARAM); - #endif public: typedef unsigned int KeyID; diff --git a/native/ppt/src/Win32KeyboardHook.h b/native/ppt/src/Win32KeyboardHook.h index ca50cb2..40cf66a 100644 --- a/native/ppt/src/Win32KeyboardHook.h +++ b/native/ppt/src/Win32KeyboardHook.h @@ -9,29 +9,6 @@ namespace hooks { extern std::string key_string_from_vk(DWORD code, bool extended); extern std::string key_string_from_sc(USHORT code); - class Win32SystemHook : public KeyboardHook { - public: - Win32SystemHook(); - - bool attach() override; - void detach() override; - - bool keytype_supported() const override { return true; } - private: - static LRESULT CALLBACK _keyboard_hook_callback(int, WPARAM, LPARAM); - static LRESULT CALLBACK _mouse_hook_callback(int, WPARAM, LPARAM); - - HHOOK keyboad_hook_id{nullptr}; - bool keyboard_hook_callback(int, WPARAM, LPARAM); - - HHOOK mouse_hook_id{nullptr}; - bool mouse_hook_callback(int, WPARAM, LPARAM); - - bool active{false}; - std::thread poll_thread; - void poll_events(); - }; - class Win32RawHook : public KeyboardHook { public: Win32RawHook(); @@ -39,12 +16,12 @@ namespace hooks { bool attach() override; void detach() override; - bool keytype_supported() const override { return true; } + [[nodiscard]] bool keytype_supported() const override { return true; } private: static LRESULT CALLBACK window_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp); - std::thread wthread; - void wloop(); + std::thread window_thread; + void window_loop(); enum struct WorkerStatus { STOPPED, @@ -63,6 +40,6 @@ namespace hooks { void set_wstatus(WorkerStatus); void handle_raw_input(RAWINPUT&); - HWND hwnd{0}; + HWND hwnd{nullptr}; }; } \ No newline at end of file diff --git a/native/ppt/src/Win32KeyboardHookLL.cpp b/native/ppt/src/Win32KeyboardHookLL.cpp deleted file mode 100644 index 6c6eeb4..0000000 --- a/native/ppt/src/Win32KeyboardHookLL.cpp +++ /dev/null @@ -1,301 +0,0 @@ -#include -#include -#include -#include -#include "./Win32KeyboardHook.h" - -using namespace std; - -namespace hooks { - void init_global_ed(); - void shutdown_global_ed(); - - typedef KBDLLHOOKSTRUCT KeyboardHookStruct; - thread_local Win32SystemHook* thread_hook{nullptr}; - - Win32SystemHook::Win32SystemHook() : KeyboardHook{KeyboardHookType::SYSTEM_HOOK} {} - - bool Win32SystemHook::attach() { - if(!KeyboardHook::attach()) - return false; - - init_global_ed(); - - this->active = true; - this->poll_thread = std::thread(std::bind(&Win32SystemHook::poll_events, this)); - return true; - } - - void Win32SystemHook::detach() { - this->active = false; - { - //TODO trigger no message! - } - if(this->poll_thread.joinable()) - this->poll_thread.join(); - - /* will flush all events */ - shutdown_global_ed(); - - KeyboardHook::detach(); - } - - - LRESULT Win32SystemHook::_mouse_hook_callback(int nCode, WPARAM event, LPARAM ptr_keyboard) { - assert(thread_hook); - auto consume = thread_hook->mouse_hook_callback(nCode, event, ptr_keyboard); - auto end = std::chrono::high_resolution_clock::now(); - if(consume) - return 1; - return CallNextHookEx(nullptr, nCode, event, ptr_keyboard); - } - - LRESULT Win32SystemHook::_keyboard_hook_callback(int nCode, WPARAM event, LPARAM ptr_keyboard) { - assert(thread_hook); - auto consume = thread_hook->keyboard_hook_callback(nCode, event, ptr_keyboard); - if(consume) - return 1; - return CallNextHookEx(nullptr, nCode, event, ptr_keyboard); - } - - void Win32SystemHook::poll_events() { - thread_hook = this; - - if(!SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS)) - std::cerr << "Failed to set priority class to realtime!" << std::endl; - - #if 0 - std::string msg_box{"Priority Class: "}; - msg_box += std::to_string((int) GetPriorityClass(GetCurrentProcess())); - msg_box += " Priority: "; - msg_box += std::to_string((int) GetThreadPriority(GetCurrentProcess())); - MessageBox(nullptr, msg_box.c_str(), "PPT-Thread", MB_OK); - #endif - #if 0 - int cur_priority = GetThreadPriority(GetCurrentThread()); - DWORD cur_priority_class = GetPriorityClass(GetCurrentProcess()); - std::cout << "P: " << cur_priority << " C: " << cur_priority_class << std::endl; - #endif - - this->keyboad_hook_id = SetWindowsHookEx(WH_KEYBOARD_LL, &Win32SystemHook::_keyboard_hook_callback, GetModuleHandle(nullptr), 0); - if(!this->keyboad_hook_id) { - cerr << "Failed to register keyboard hook" << endl; - return; - } - -#if 1 - this->mouse_hook_id = SetWindowsHookEx(WH_MOUSE_LL, &Win32SystemHook::_mouse_hook_callback, GetModuleHandle(nullptr), 0); - if(!this->keyboad_hook_id) { - UnhookWindowsHookEx(this->keyboad_hook_id); - cerr << "Failed to register mouse hook" << endl; - return; - } -#else - this->mouse_hook_id = 0; -#endif - - - MSG msg; - while(!GetMessage(&msg, nullptr, 0, 0) && this->active) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - if(this->mouse_hook_id > 0) - UnhookWindowsHookEx(this->mouse_hook_id); - UnhookWindowsHookEx(this->keyboad_hook_id); - thread_hook = nullptr; - } - - inline std::string key_code(KeyboardHookStruct* keyboard) { - return key_string_from_vk(keyboard->vkCode, (keyboard->flags & LLKHF_EXTENDED) > 0); - } - - using KeyType = KeyboardHook::KeyType; - - bool Win32SystemHook::keyboard_hook_callback(int nCode, WPARAM event, LPARAM ptr_keyboard) { - auto keyboard = (KeyboardHookStruct*) ptr_keyboard; - if(event == WM_KEYDOWN || event == WM_SYSKEYDOWN) { - auto& state = this->map_key[keyboard->vkCode]; - bool typed = state; - state = true; - - auto type = key_type_from_vk(keyboard->vkCode); - if(type != KeyType::KEY_NORMAL) - this->map_special[type] = true; - else - this->map_special[type] = keyboard->vkCode; - - if(!typed) - this->trigger_key_event(KeyEvent::PRESS, type == KeyType::KEY_NORMAL ? key_code(keyboard) : key_string_from_vk(this->map_special[KeyType::KEY_NORMAL], false)); - this->trigger_key_event(KeyEvent::TYPE, type == KeyType::KEY_NORMAL ? key_code(keyboard) : key_string_from_vk(this->map_special[KeyType::KEY_NORMAL], false)); - } else if(event == WM_KEYUP || event == WM_SYSKEYUP) { - auto& state = this->map_key[keyboard->vkCode]; - if(!state) return false; //Duplicate - state = false; - - auto type = key_type_from_vk(keyboard->vkCode); - if(type != KeyType::KEY_NORMAL) - this->map_special[type] = false; - else if(this->map_special[KeyType::KEY_NORMAL] == keyboard->vkCode) - this->map_special[KeyType::KEY_NORMAL] = 0; - - this->trigger_key_event(KeyEvent::RELEASE, type == KeyType::KEY_NORMAL ? key_code(keyboard) : key_string_from_vk(this->map_special[KeyType::KEY_NORMAL], false)); - } - - //Consume the event: return 1 - return false; - } - - typedef MSLLHOOKSTRUCT MouseLLHookStruct; - - struct MouseButtonEventEntry { - MouseButtonEventEntry* next; - KeyboardHook* hook; - - enum KeyboardHook::KeyEvent::type type; - std::string key; - }; - - inline MouseButtonEventEntry* allocate_mb_event() { - return new MouseButtonEventEntry{}; - } - - inline void delete_mb_event(MouseButtonEventEntry* event) { - delete event; - } - - struct MouseButtonEventDispatcher { - bool active{true}; - - std::thread dispatcher{}; - - CRITICAL_SECTION mutex; - CONDITION_VARIABLE cv_flushed; - CONDITION_VARIABLE cv_work; - - MouseButtonEventEntry* event_head{nullptr}; - MouseButtonEventEntry** event_tail{&event_head}; - }; - - MouseButtonEventDispatcher* global_event_dispatcher{}; - size_t global_ed_ref_count{0}; - - void init_global_ed() { - if(global_event_dispatcher) { - global_ed_ref_count++; - return; - } - - global_event_dispatcher = new MouseButtonEventDispatcher{}; - InitializeCriticalSection(&global_event_dispatcher->mutex); - InitializeConditionVariable(&global_event_dispatcher->cv_flushed); - InitializeConditionVariable(&global_event_dispatcher->cv_work); - - global_event_dispatcher->dispatcher = std::thread([]{ - auto ed = global_event_dispatcher; - - while(ed->active) { - MouseButtonEventEntry* entry{nullptr}; - { - EnterCriticalSection(&ed->mutex); - while(!global_event_dispatcher->event_head && ed->active) { - WakeAllConditionVariable(&ed->cv_flushed); - SleepConditionVariableCS(&ed->cv_work, &ed->mutex, INFINITE); - } - - entry = global_event_dispatcher->event_head; - global_event_dispatcher->event_head = nullptr; - global_event_dispatcher->event_tail = &global_event_dispatcher->event_head; - LeaveCriticalSection(&ed->mutex); - } - - while(entry) { - entry->hook->trigger_key_event(entry->type, std::string{entry->key}); - - auto next = entry->next; - delete_mb_event(entry); - entry = next; - } - } - }); - } - - void shutdown_global_ed() { - /* flush all events */ - EnterCriticalSection(&global_event_dispatcher->mutex); - WakeAllConditionVariable(&global_event_dispatcher->cv_work); - SleepConditionVariableCS(&global_event_dispatcher->cv_flushed, &global_event_dispatcher->mutex, INFINITE); - LeaveCriticalSection(&global_event_dispatcher->mutex); - - if(--global_ed_ref_count > 0) return; - - auto ed = std::exchange(global_event_dispatcher, nullptr); - ed->active = false; - WakeAllConditionVariable(&ed->cv_work); - - if(ed->dispatcher.joinable()) - ed->dispatcher.join(); - - DeleteCriticalSection(&ed->mutex); - delete ed; - } - - bool Win32SystemHook::mouse_hook_callback(int nCode, WPARAM event, LPARAM ptr_mouse) { - MouseButtonEventEntry* mb_event; - switch (event) { - case WM_LBUTTONDOWN: - mb_event = allocate_mb_event(); - mb_event->type = KeyEvent::PRESS; - mb_event->key = "MOUSE1"; - break; - case WM_LBUTTONUP: - mb_event = allocate_mb_event(); - mb_event->type = KeyEvent::RELEASE; - mb_event->key = "MOUSE1"; - break; - - case WM_RBUTTONDOWN: - mb_event = allocate_mb_event(); - mb_event->type = KeyEvent::PRESS; - mb_event->key = "MOUSE3"; - break; - case WM_RBUTTONUP: - mb_event = allocate_mb_event(); - mb_event->type = KeyEvent::RELEASE; - mb_event->key = "MOUSE3"; - break; - - case WM_XBUTTONDOWN: { - auto mouse = (MouseLLHookStruct*) ptr_mouse; - auto x_index = GET_XBUTTON_WPARAM(mouse->mouseData); - - mb_event = allocate_mb_event(); - mb_event->type = KeyEvent::PRESS; - mb_event->key = "MOUSEX" + std::to_string(x_index); - break; - } - case WM_XBUTTONUP: { - auto mouse = (MouseLLHookStruct*) ptr_mouse; - auto x_index = GET_XBUTTON_WPARAM(mouse->mouseData); - - mb_event = allocate_mb_event(); - mb_event->type = KeyEvent::RELEASE; - mb_event->key = "MOUSEX" + std::to_string(x_index); - break; - } - default: - return false; - } - - mb_event->next = nullptr; - mb_event->hook = thread_hook; - - EnterCriticalSection(&global_event_dispatcher->mutex); - *global_event_dispatcher->event_tail = mb_event; - global_event_dispatcher->event_tail = &mb_event->next; - WakeAllConditionVariable(&global_event_dispatcher->cv_work); - LeaveCriticalSection(&global_event_dispatcher->mutex); - return false; - } -} \ No newline at end of file diff --git a/native/ppt/src/Win32KeyboardRawInput.cpp b/native/ppt/src/Win32KeyboardRawInput.cpp index e2fd5c5..6713f00 100644 --- a/native/ppt/src/Win32KeyboardRawInput.cpp +++ b/native/ppt/src/Win32KeyboardRawInput.cpp @@ -15,7 +15,7 @@ namespace hooks { this->wactive = true; this->set_wstatus(WorkerStatus::INITIALIZING); - this->wthread = std::thread(std::bind(&Win32RawHook::wloop, this)); + this->window_thread = std::thread([this] { this->window_loop(); }); std::unique_lock ws_lock{this->wstatus_mutex}; this->wstatus_changed_cv.wait(ws_lock, [&]{ @@ -27,12 +27,14 @@ namespace hooks { void Win32RawHook::detach() { this->wactive = false; - { - //TODO trigger no message! + + if(this->hwnd) { + PostMessage(this->hwnd, WM_CLOSE, 0, 0); } - if(this->wthread.joinable()) - this->wthread.join(); + if(this->window_thread.joinable()) + this->window_thread.join(); + this->set_wstatus(WorkerStatus::STOPPED); KeyboardHook::detach(); @@ -46,7 +48,7 @@ namespace hooks { } #define WORKER_CLASS_NAME ("TeaClient - KeyHook worker") - void Win32RawHook::wloop() { + void Win32RawHook::window_loop() { this->set_wstatus(WorkerStatus::INITIALIZING); /* setup */ @@ -55,12 +57,12 @@ namespace hooks { WNDCLASS wc = {0}; wc.lpfnWndProc = window_proc; wc.cbWndExtra = sizeof(void*); - wc.hInstance = 0; + wc.hInstance = nullptr; wc.lpszClassName = WORKER_CLASS_NAME; RegisterClass(&wc); } - this->hwnd = CreateWindow(WORKER_CLASS_NAME, "TeaClient - KeyHook worker window", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, this); + this->hwnd = CreateWindow(WORKER_CLASS_NAME, "TeaClient - KeyHook worker window", 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, this); if(!this->hwnd) { this->worker_died_reason = "Failed to create window"; this->set_wstatus(WorkerStatus::DIED); @@ -70,12 +72,12 @@ namespace hooks { RAWINPUTDEVICE devices[2]; devices[0].usUsagePage = 0x01; //HID_USAGE_PAGE_GENERIC; devices[0].usUsage = 0x02; //HID_USAGE_GENERIC_MOUSE; - devices[0].dwFlags = RIDEV_NOLEGACY | RIDEV_INPUTSINK; + devices[0].dwFlags = (uint32_t) RIDEV_NOLEGACY | (uint32_t) RIDEV_INPUTSINK; devices[0].hwndTarget = hwnd; devices[1].usUsagePage = 0x01; //HID_USAGE_PAGE_GENERIC; devices[1].usUsage = 0x06; //HID_USAGE_GENERIC_KEYBOARD; - devices[1].dwFlags = RIDEV_NOLEGACY | RIDEV_INPUTSINK; + devices[1].dwFlags = (uint32_t) RIDEV_NOLEGACY | (uint32_t) RIDEV_INPUTSINK; devices[1].hwndTarget = hwnd; if(!RegisterRawInputDevices(devices, 2, sizeof *devices)) { @@ -86,18 +88,19 @@ namespace hooks { } this->set_wstatus(WorkerStatus::RUNNING); + BOOL ret; MSG msg; while (this->wactive) { ret = GetMessage(&msg, this->hwnd, 0, 0); if(ret == 0) break; + if (ret == -1) { this->worker_died_reason = "GetMessage() threw an error"; this->set_wstatus(WorkerStatus::DIED); goto cleanup; - } - else { + } else { TranslateMessage(&msg); DispatchMessage(&msg); } @@ -105,10 +108,8 @@ namespace hooks { this->set_wstatus(WorkerStatus::STOPPED); cleanup: - if(this->hwnd > 0) { - DestroyWindow(this->hwnd); - this->hwnd = 0; - } + if(auto window{std::exchange(this->hwnd, nullptr)}; window) + DestroyWindow(window); } LRESULT Win32RawHook::window_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { @@ -116,18 +117,17 @@ namespace hooks { switch (msg) { case WM_CREATE: { - CREATESTRUCT *s = reinterpret_cast(lp); + auto s = reinterpret_cast(lp); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) s->lpCreateParams); return 0; } case WM_CLOSE: - DestroyWindow(hwnd); - PostQuitMessage(0); + /* nothing to do here, the window will be cleaned up by the event dispatchers */ return 0; case WM_INPUT: { UINT target_size{0}; - GetRawInputData((HRAWINPUT) lp, RID_INPUT, NULL, &target_size, sizeof(RAWINPUTHEADER)); + GetRawInputData((HRAWINPUT) lp, RID_INPUT, nullptr, &target_size, sizeof(RAWINPUTHEADER)); if(target_size > sizeof(RAWINPUT)) { std::cerr << "Failed to retrieve input (Target size is longer than expected)" << std::endl; return 0; diff --git a/package-lock.json b/package-lock.json index 7ceea90..f87cdf1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -172,6 +172,11 @@ "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", "dev": true }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" + }, "@types/ejs": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-2.7.0.tgz", @@ -523,6 +528,11 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" + }, "async-each": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", @@ -1102,6 +1112,50 @@ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.3.0.tgz", "integrity": "sha512-Xs2Hf2nzrvJMFKimOR7YR0QwZ8fc0u98kdtwN1eNAZzNQgH3vK2pXzff6GJtKh7S5hoJ87ECiAiZFS2fb5Ii2w==" }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, "cliui": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", @@ -1679,6 +1733,26 @@ "extract-zip": "^1.0.3" } }, + "electron-context-menu": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/electron-context-menu/-/electron-context-menu-2.3.0.tgz", + "integrity": "sha512-XYsYkNY+jvX4C5o09qMuZoKL6e9frnQzBFehZSIiKp6zK0u3XYowJYDyK3vDKKZxYsOIGiE/Gbx40jERC03Ctw==", + "requires": { + "cli-truncate": "^2.0.0", + "electron-dl": "^3.0.0", + "electron-is-dev": "^1.0.1" + } + }, + "electron-dl": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/electron-dl/-/electron-dl-3.0.2.tgz", + "integrity": "sha512-pRgE9Jbhoo5z6Vk3qi+vIrfpMDlCp2oB1UeR96SMnsfz073jj0AZGQwp69EdIcEvlUlwBSGyJK8Jt6OB6JLn+g==", + "requires": { + "ext-name": "^5.0.0", + "pupa": "^2.0.1", + "unused-filename": "^2.1.0" + } + }, "electron-fetch": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/electron-fetch/-/electron-fetch-1.5.0.tgz", @@ -1770,6 +1844,11 @@ } } }, + "electron-is-dev": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-1.2.0.tgz", + "integrity": "sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw==" + }, "electron-osx-sign": { "version": "0.4.15", "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.15.tgz", @@ -2318,6 +2397,11 @@ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "optional": true }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2397,6 +2481,23 @@ } } }, + "ext-list": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", + "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", + "requires": { + "mime-db": "^1.28.0" + } + }, + "ext-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", + "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", + "requires": { + "ext-list": "^2.0.0", + "sort-keys-length": "^1.0.0" + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -3901,6 +4002,11 @@ "path-is-inside": "^1.0.1" } }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -4518,6 +4624,11 @@ } } }, + "modify-filename": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/modify-filename/-/modify-filename-1.1.0.tgz", + "integrity": "sha1-mi3sg4Bvuy2XXyK+7IWcoms5OqE=" + }, "moment": { "version": "2.26.0", "resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz", @@ -5313,6 +5424,14 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, + "pupa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz", + "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==", + "requires": { + "escape-goat": "^2.0.0" + } + }, "pure-uuid": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/pure-uuid/-/pure-uuid-1.6.0.tgz", @@ -5842,6 +5961,45 @@ "string-width": "^1.0.1" } }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + } + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -5970,6 +6128,22 @@ } } }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "sort-keys-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", + "integrity": "sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg=", + "requires": { + "sort-keys": "^1.0.0" + } + }, "source-map": { "version": "0.1.32", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.32.tgz", @@ -6668,6 +6842,22 @@ } } }, + "unused-filename": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unused-filename/-/unused-filename-2.1.0.tgz", + "integrity": "sha512-BMiNwJbuWmqCpAM1FqxCTD7lXF97AvfQC8Kr/DIeA6VtvhJaMDupZ82+inbjl5yVP44PcxOuCSxye1QMS0wZyg==", + "requires": { + "modify-filename": "^1.1.0", + "path-exists": "^4.0.0" + }, + "dependencies": { + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + } + } + }, "unzip-response": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", diff --git a/package.json b/package.json index 519e1d1..badaacf 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "aws4": "^1.10.0", "color.js": "^0.1.3", "electron": "8.0.0", + "electron-context-menu": "^2.3.0", "electron-installer-windows": "^1.1.0", "electron-rebuild": "^1.11.0", "electron-wix-msi": "^2.1.1",