Loupedeck Live SでサラリーマンがPC作業を効率化し、更なる改善のためにプラグインを自作した話:Windows版

ClipboardImage 1 Main

はじめに

ずーっと、導入するか事あるごとに悩んでいた左手デバイス。
そんなある日、クラウドファンディングでLoupedeckのデバイスが登場するも、即断せずに悩んでいたのですが、、痺れをきらして支援してゲットしたのが今回紹介する「Loupedeck Live S」です。

こういう左手デバイスって、クリエイティブというか、動画編集とかイラストレーターが使うイメージがあって、そこら辺にいるサラリーマンがPCで作業するときに「果たして有用なのか?」と何度も自問自答してきたわけです。

何設定するか?って考えた時に真っ先に浮かぶのは・・・

Teamsのマイクのミュートの切り替え‼️

なんですが、それ以外に何かある?だと思うんですよ。ぶっちゃけ🤣
では、現時点で私がLoupedeck Live Sで設定した内容をご紹介します。

2023/02/05:使用しはじめて1ヶ月経過

今現在、使用しはじめて1ヶ月ほど経過していますが、今までキーボードのショートカットで実行していた操作もLoupedeckで操作するのが当たり前になってしまっています。
Excel系の操作のためのPluginを自作もしはじめて(ソースについては下の方で公開)、こちらの機能は日々追加しているとった感じです。

Loupedeck Live Sの設定内容

メインページ:真ん中の15個のボタン

ClipboardImage 1 Main 1

# 表示名 アクション アクション詳細 備考
全画面 ショートカット Windows + ↑ アプリのWindowを最大化
ID+P マクロ ID入力

Tabキー

パスワード入力

Enterキー
Web等でユーザIDと
パスワードを入力するやつ
RDP マクロ パスワード入力

Enterキー
特定の環境にリモートデスクトップ
する際のパスワード。リモート先が
ロックアウトされたときにパスワード
入力する際に重宝。
Teams退出 ショートカット Shift + Ctrl + H Teamsの会議を退出するショートカット。
ウィンドウにフォーカスが当たっていないと
使えないので微妙。
マイクOn/Off ショートカット Windows + Shift + A Teams会議のマイクのOn/Off❗️PowerToysの
「ビデオ会議のミュート」機能を利用。
ハッキリ言ってこのためにLoupedeckを
導入したと言っても過言ではないw
Capture ショートカット Windows + Shift + S Windows標準の画面キャプチャの呼び出し
Root P マクロ パスワード入力

Enterキー
特定の環境でパスワード入力するためのやつ
カレンダー アクションを実行 outlookcal: URIスキームでカレンダーアプリを実行
タスク アクションを実行 ms-to-do: URIスキームで標準のタスクアプリを実行。
タスクのアプリはMicrosoft Storeで
インストール可能。
計算機 アクションを実行 calculator: URIスキームで標準の計算機アプリを実行
未設定
Teams
Activity
独自プラグイン
(後述)
Teamsアプリのアクティビティを開く
Teams
Chat
独自プラグイン
(後述)
Teamsアプリのチャットを開く
Teams
Team
独自プラグイン
(後述)
Teamsアプリのチームを開く
Teams
Calendar
独自プラグイン
(後述)
Teamsアプリのカレンダーを開く

⑤ Teams会議のマイクOn/Offについての補足

ショートカット「Windows + Shift + A」で動作させるためには、Microsoftが公開しているPowerToysという便利ツールをインストールする必要があります。
他にも便利な機能が満載なので、Windowsを利用する際にはMustのツールだと個人的に思います。

今回のショートカットを利用するためには「ビデオ会議のミュート」という機能を有効化してください。

PowerToys Mute

アイコンについて

Teams関連のアイコンにはStream Deck用の下記のものでサイズが80pxのものを利用しています。それ以外はLoupedeckのMarketplaceで取得したものです。

左右のツマミとボタン

ClipboardImage 1 Main 2

# 操作 アクション 備考
ツマミ Windows + ←、Windows + → アプリのウィンドウを移動
ツマミ マウスホイールの水平移動
ボタン 未設定
ボタン ショートカットアクション
Alt + F4
アプリを閉じる
ボタン ショートカットアクション
Ctrl + Home
WebのページやらExcelやら何やらで
先頭行に移動するためのショートカットを
割り当てています。
ボタン Loupedeckのタッチページの移動

①、②のツマミの押し込みのボタン処理ですが、固くて押しづらいので現状何も割り当ててません。

よく使う操作

  • マルチモニター(3面)で作業をしているので、ツマミ①でアプリのWindowをモニター間を移動させて、タッチボタン①の全画面でWindowを最大化
    • ツマミの押し込みで全画面としても良かったのですが、どうにも押し込みづらく全画面はタッチボタンに割り当てました。おかげで操作感は良いです😁
    • この操作自体はWindowsキーと方向キー(←、→、↑)の組み合わせでできますが、両手を使う必要があります。Loupedeckを導入したことでマウスに置いてる手はそのままに同じ操作ができるのはいいですね。
  • タッチボタン⑤のTeams会議のマイクのミュートのOn/Offは、どう考えても便利です😆
  • 後述しますがTeamsの各機能を呼び出すボタンは、普段よく使います。
    • 例えば、もうす会議が始まるというときにTeamsのカレンダーを開く必要がありますが、これがボタン一つでできるようになったのはかなりストレスが減りました。

Loupedeckプラグインの自作:Teams用

独自の処理を組み込むために、Loupedeck用のプラグインを自作する流れを簡単に説明します。

どんなプラグインを作りたいか?

Microsoft Teamsのアクティビティ、チャット、チーム、カレンダーをボタン1つで即座に表示することができるようにしたかったというのが事の発端です。

Loupedeckのマクロで以下のような設定をしても似たような動きにすることはできるのですが、、、

  1. 【Windowsキー + 1】:タスクバー上で実行しているプログラムで例えばMicrosoft Teamsを一番左側にピン留めしていたとすると、このショートカットで当該アプリにフォーカスをあてることができます。
  2. 【遅延 1000ms】:1の処理でフォーカスが当たるまで待つ必要があります。
  3. 【Ctrl + 1】:Microsoft Teamsのウィンドウにフォーカスがある状態でこのショーカットでアクティビティに表示を切り替えることができます。

この設定だと動作が不安定で2回ボタンを押さないとまともに動かないのが気持ち悪く。。だったら、自分でプラグインを作ってしまえ❗️と思った次第です。

で、プラグインで以下のような動きのものを作ろうと調べ始めました。

  1. 起動しているアプリのウィンドウのタイトル名でアプリを特定し、そのアプリにフォーカスを当てる。タイトル名はプラグインのパラメータで設定可能とする。
  2. フォーカスが当たるまでSleep処理を入れる。
  3. フォーカスが当たったら、プラグインのパラメータで設定したショートカットを実行する。

尚、フォーカスを当てる際に、前面に出ていなかった場合には最前面に出すようにもします。

プラグインを作成する上でベースとしたソースコード

Loupedeck公式のGitHubのリポジトリの下記のサンプルのVisual Studioのソリューションファイルをベースとして作成しました。

VisualStudioのソリューションファイルをダウンロードするには下記のURLにアクセスし、

2023 01 09 GitHub Loupedeck

緑色の「Code」ボタンをクリックし、「Download Zip」をクリックすることでダウンロードできます。

DemoPluginフォルダ直下にC#のクラスファイルを追加してあげれば、処理を追加できます。最初は用意されている処理のクラスファイルを変更して試してみるのが良いかもしれません。

後は起動しているLoupedeckのアプリを落としてから、普通にビルドすればLoudedeckのプラグインのDLLがLoupedeckがロードする場所に配置されます。ビルド完了したら、再度Loupedeckのアプリを起動すれば作成したプラグインが設定できる状態になります。

この辺りの流れについてはwikiに記載されていますので、まずはコチラに目を通すのがよいと思います。

Looupedeckプラグインのソースコード

作成したソースコードを吊るしておきます。
といっても、ほとんどが指定したタイトルのウィンドウのアプリケーションにフォーカスを当てる処理となっていますが・・・

とりあえず、自分が使うために動けばいいという感じで書いているので、色々と目を瞑ってもらえれば😂
尚、アプリのウィンドウにフォーカスを当てる処理はWindowsに特化しているので、Windowsじゃないと動かないと思います。また、当方環境がWindows 10でしか確認はしていない点はご了承ください。

namespace Loupedeck.DemoPlugin
{
    using System;
    using System.Runtime.InteropServices;
    using System.Web.UI.WebControls;

    public class FocusAndShortcut : PluginDynamicCommand
    {
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        private static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool BringWindowToTop(IntPtr hWnd);

        [DllImport("user32.dll")]
        static extern IntPtr SetFocus(IntPtr hWnd);

        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SetWindowPos(IntPtr hWnd,
            int hWndInsertAfter, int x, int y, int cx, int cy, int uFlags);

        private const int SWP_NOSIZE = 0x0001;
        private const int SWP_NOMOVE = 0x0002;
        private const int SWP_NOZORDER = 0x0004;
        private const int SWP_SHOWWINDOW = 0x0040;
        private const int SWP_ASYNCWINDOWPOS = 0x4000;
        private const int HWND_TOP = 0;
        private const int HWND_BOTTOM = 1;
        private const int HWND_TOPMOST = -1;
        private const int HWND_NOTOPMOST = -2;

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);

        private const int SW_SHOWNORMAL = 1;
        private const int SW_SHOW = 5;
        private const int SW_RESTORE = 9;

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool IsIconic(IntPtr hWnd);

        [DllImport("user32.dll")]
        private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

        [DllImport("user32.dll")]
        public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

        [DllImport("kernel32.dll")]
        private static extern uint GetCurrentThreadId();

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);

        [DllImport("user32.dll", EntryPoint = "SystemParametersInfo",
            SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SystemParametersInfoGet(uint action, uint param, ref uint vparam, uint init);

        [DllImport("user32.dll", EntryPoint = "SystemParametersInfo",
            SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SystemParametersInfoSet(uint action, uint param, uint vparam, uint init);

        private const uint SPI_GETFOREGROUNDLOCKTIMEOUT = 0x2000;
        private const uint SPI_SETFOREGROUNDLOCKTIMEOUT = 0x2001;
        private const uint SPIF_UPDATEINIFILE = 0x01;
        private const uint SPIF_SENDCHANGE = 0x02;

        public FocusAndShortcut()
            : base(displayName: "FocusAndShortcut", description: "Focus specifit appliction", groupName: "Teams")
        {
            // Loupedeckで指定したアプリケーション名を取得
            this.MakeProfileAction("text;Enter Application Name:");
        }

        protected override void RunCommand(String actionParameter)
        {
            var nativeMethods = this.NativeApi.GetNativeMethods();
            var arrActionParameter = actionParameter.Split(',');

            if (!this.CheckFocus(arrActionParameter[0]))
            {
                // すべてのプロセスを列挙する
                foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses())
                {

                    // パラメータに指定した名前がメインウィンドウのタイトルに含まれているか調べる
                    if (0 <= p.MainWindowTitle.IndexOf(arrActionParameter[0]))
                    {
                        // ウィンドウをアクティブにする
                        ActiveWindow(p.MainWindowHandle);

                        break;
                    }
                }
            }

            while (!this.CheckFocus(arrActionParameter[0]))
            {
                // 遅延:フォーカスが当たるまでSleepを繰り返す
                System.Threading.Thread.Sleep(10);
            }

            // コマンド実行
            if (arrActionParameter[1].Equals("Ctrl+1"))
            {
                nativeMethods.SendKeyboardShortcut(VirtualKeyCode.Key1, ModifierKey.Control);
            }
            else if (arrActionParameter[1].Equals("Ctrl+2"))
            {
                nativeMethods.SendKeyboardShortcut(VirtualKeyCode.Key2, ModifierKey.Control);
            }
            else if (arrActionParameter[1].Equals("Ctrl+3"))
            {
                nativeMethods.SendKeyboardShortcut(VirtualKeyCode.Key3, ModifierKey.Control);
            }
            else if (arrActionParameter[1].Equals("Ctrl+4"))
            {
                nativeMethods.SendKeyboardShortcut(VirtualKeyCode.Key4, ModifierKey.Control);
            }
        }

        /// <summary>
        /// 指定したウィンドウタイトルのウィンドウにフォーカスが当たっているかを判定
        /// </summary>
        /// <param name="actionParameter"></param>
        /// <returns></returns>
        private Boolean CheckFocus(String actionParameter)
        {
            IntPtr hWnd = GetForegroundWindow();
            GetWindowThreadProcessId(hWnd, out var id);

            return 0 <= System.Diagnostics.Process.GetProcessById(id).MainWindowTitle.IndexOf(actionParameter);
        }

        /// <summary>
        /// 指定したウィンドウをアクティブに変更
        /// 
        /// 【参考】外部アプリケーションのウィンドウをアクティブにする .NET Tips
        /// https://dobon.net/vb/dotnet/process/appactivate.html
        /// </summary>
        /// <param name="hWnd">ウィンドウハンドル</param>
        public static void ActiveWindow(IntPtr hWnd)
        {
            if (hWnd == IntPtr.Zero)
            {
                return;
            }

            // ウィンドウが最小化されている場合は元に戻す
            if (IsIconic(hWnd))
            {
                ShowWindowAsync(hWnd, SW_RESTORE);
            }

            // AttachThreadInputの準備
            // フォアグラウドウィンドウのハンドルを取得
            IntPtr forehWnd = GetForegroundWindow();
            if (forehWnd == hWnd)
            {
                return;
            }

            // フォアグラウンドのスレッドIDを取得
            uint foreThread = GetWindowThreadProcessId(forehWnd, IntPtr.Zero);
            // 自分のスレッドIDを収得
            uint thisThread = GetCurrentThreadId();

            uint timeout = 200000;
            if (foreThread != thisThread)
            {
                // ForegroundLockTimeoutの現在の設定を取得
                //Visual Studio 2010, 2012起動後は、レジストリと違う値を返す
                SystemParametersInfoGet(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, ref timeout, 0);

                //ForegroundLockTimeoutの値を0にする
                //(SPIF_UPDATEINIFILE | SPIF_SENDCHANGE)を使いたいが、
                //  timeoutがレジストリと違う値だと戻せなくなるので使わない
                SystemParametersInfoSet(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, 0, 0);

                //入力処理機構にアタッチする
                AttachThreadInput(thisThread, foreThread, true);
            }

            // ウィンドウをフォアグラウンドにする処理
            SetForegroundWindow(hWnd);
            SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0,
                SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_ASYNCWINDOWPOS);
            BringWindowToTop(hWnd);
            ShowWindowAsync(hWnd, SW_SHOW);
            SetFocus(hWnd);

            if (foreThread != thisThread)
            {
                //ForegroundLockTimeoutの値を元に戻す
                //ここでも(SPIF_UPDATEINIFILE | SPIF_SENDCHANGE)は使わない
                SystemParametersInfoSet(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, timeout, 0);

                //デタッチ
                AttachThreadInput(thisThread, foreThread, false);
            }
        }
    }
}

実装する上でハマったこと

プラグインの中でショートカット「Ctrl + 2」をSendする際に、最初は以下のように記述してみたけど動きませんでした。

this.Plugin.ClientApplication.SendKeyboardShortcut(VirtualKeyCode.Key2, ModifierKey.Control);

何で動かないのか?まったくわからなくて諦めかけていたのですが、GitHubの下記の投稿で回避策がわかりました。

- Step03 Toggle Mute command is not working on Mac · Discussion #3 · Loupedeck/PluginSdk · GitHub

この投稿ではMacで「VirtualKeyCode.VolumeMute」が動作しないという内容でしたが、試しに下記のようにコードを書き換えて実行してみたところ無事に動いてくれました。

var nativeMethods = this.NativeApi.GetNativeMethods();
nativeMethods.SendKeyboardShortcut(VirtualKeyCode.Key2, ModifierKey.Control);

Loupedeckプラグインの画面での設定

自作したプラグインを設定する手順を簡単にまとめておきます。

Loupedeck Setting 01

まず作成したプラグインを表示するように設定する必要があります。
赤い矢印で示しているアイコンをクリックします。

Loupedeck Setting 02

今回、LoupdedeckのGithubのリポジトリから拝借したソリューションファイルベースで作成しているため、プラグインの名前は「Demo」になっています。
で、このプラグイン「Demo」の目玉のアイコンを表示の設定に切り替えます。
(自作したプラグインがそもそも出てきていないという場合は、Loupedeckのアプリを再起動してみてください。起動中にプラグインをビルドして書き出した場合には、読み込ませるためにLoupedeckアプリの起動のし直しが必要だからです)

Loupedeck Setting 03

実際に作成したプラグインのアクションの設定をしていきます。

  1. Demoプラグインのアイコンをクリック
  2. 今回作成したプラグインの「FocusAndShortcut」を選択すると、下に設定する画面が表示されます。
  3. アイコンと表示名は好きなものを指定。で、実行したいショートカット等を「Enter Application Name」に入力するのですが。上記のソースコードで実装しているものはフォーカスを当てるアプリケーションのウィンドウのタイトルとその後に実行するショートカットをカンマ区切りで指定するというものになっています。パラメータを追加できれば良かったのですが、そこまで調べるのが面倒だったので😂

Excelプラグインの自作

次は、Excelでの操作をLoupedeckのボタンに割り当てるにはどうすればいいか?と考えはじめました。

Excelをショートカットで操作することは、Altキーでリボンのメニューにフォーカスを当てて処理することが標準でできることなのですが、この方式だとLouopedeckからマクロで呼び出そうとしても、そもそもフォーカスが当たらなくてまともに動きません。

そのため、ExcelでのショートカットはVSTOにてExcel側で動作する独自のプラグインを作成し、そのプラグイン側でショートカットキーをフックしてExcelの処理を呼び出せるようにすることにしました。

Excelプラグインのソースコード

作成したExcelプラグインのソースコードも吊るしておきます。
ショートカットキーをフックするためのAPIとしては、以下のものを利用させてもらっています。

後は、やっていることは特定のショートカットが入力された際に、Excel上でやりたいことをExcel側のAPIを利用して記述しているだけです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Excel = Microsoft.Office.Interop.Excel;
using Office = Microsoft.Office.Core;
using Microsoft.Office.Tools.Excel;
using Gma.System.MouseKeyHook;
using System.Windows.Forms;
using Microsoft.Office.Core;
using System.Drawing;
using Microsoft.Office.Interop.Excel;

namespace ExcelAddIn1
{
    public partial class ThisAddIn
    {
        private IKeyboardMouseEvents m_Events;

        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {
            Unsubscribe();
            Subscribe(Hook.AppEvents());
        }

        private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
        {
            Unsubscribe();
        }

        private void Subscribe(IKeyboardMouseEvents events)
        {
            m_Events = events;
            m_Events.KeyDown += OnKeyDown;
        }

        private void Unsubscribe()
        {
            if (m_Events == null) return;
            m_Events.KeyDown -= OnKeyDown;

            m_Events.Dispose();
            m_Events = null;
        }

        /// <summary>
        /// キーダウン時のショートカット毎の処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnKeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyData == (Keys.A | Keys.Alt | Keys.Control))
            {
                // ショートカット「Alt+Ctrl+A」
                // 選択しているセルのフォントの色を「赤」に変更
                Excel.Areas areas = this.Application.Selection.Areas;

                foreach (Range item in areas)
                {
                    item.Font.Color = Color.Red;
                }
            }
            else if (e.KeyData == (Keys.B | Keys.Alt | Keys.Control))
            {
                // ショートカット「Alt+Ctrl+B」
                // 選択しているセルの背景色を「黄色」に変更
                Excel.Areas areas = this.Application.Selection.Areas;

                foreach (Range item in areas)
                {
                    item.Interior.Color = Color.Yellow;
                }
            }
            else if (e.KeyData == (Keys.C | Keys.Alt | Keys.Control))
            {
                // ショートカット「Alt+Ctrl+C」
                // 選択しているセルの背景色、フォントの色をクリア
                Excel.Areas areas = this.Application.Selection.Areas;

                foreach (Range item in areas)
                {
                    item.Font.ColorIndex = 1;
                    item.Interior.ColorIndex = 0;
                }
            }
            else if (e.KeyData == (Keys.D | Keys.Alt | Keys.Control))
            {
                // ショートカット「Alt+Ctrl+D」
                // シートのフォントを「MeiryoUI」に変更
                Excel.Worksheet currentSheet = this.Application.ActiveSheet;
                currentSheet.Cells.Font.Name = "Meiryo UI";
            }
            else if (e.KeyData == (Keys.E | Keys.Alt | Keys.Control))
            {
                // ショートカット「Alt+Ctrl+E」
                // すべての列の幅を「2」に変更
                Excel.Worksheet currentSheet = this.Application.ActiveSheet;
                currentSheet.Cells.ColumnWidth = 2;
            }
            else if (e.KeyData == (Keys.G | Keys.Alt | Keys.Control))
            {
                // ショートカット「Alt+Ctrl+G」
                // 選択しているセルの書式を「yyyy/mm/dd」に変更
                Excel.Areas areas = this.Application.Selection.Areas;

                foreach (Range item in areas)
                {
                    item.NumberFormatLocal = "yyyy/mm/dd";
                }
            }
        }

        #region VSTO で生成されたコード

        /// <summary>
        /// デザイナーのサポートに必要なメソッドです。
        /// コード エディターで変更しないでください。
        /// </summary>
        private void InternalStartup()
        {
            this.Startup += new System.EventHandler(ThisAddIn_Startup);
            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
        }

        #endregion
    }
}

ExcelプラグインのLoupedeckでの設定

ClipboardImage 2 Excel

Excelプラグインを利用したものはLoupedeckの2ページ目に設定しています。
設定自体は「ショートカット」で、実装したプラグイン側で指定したショートカットコマンドを設定するだけとなります。

まとめ

Loupedeck Live Sを購入したからにはしっかり使わないと❗️という思いから、自分がやりたかったことを実現するためにやった設定やプラグインの作成に関してまとめてみした。

所謂左デバイスというと動画配信、動画編集や画像編集といったクリエイティブ系での用途での情報はたくさんあるのですが、それ以外の通常用途(といっていいのか微妙ですが)での使い方に関する情報が少なかったこともあり、私なりの利用の仕方についてまとめてみました。

買ったはいいけど、やっぱり使わなかった・・・ではもったないと思いますので、何かしらお役に立てればと思います。

Loupedeckのプラグインを作成する上での情報ソース

私がLoupedeckのプラグインを作成する上で参照した情報ソースを以下にまとめておきます。今のところこれぐらいしかまともな物は見つけられませんでした。

情報ソース(GitHub)

GitHubで公開されているLoupedeckのプラグインのリポジトリで気になったもの

コメント

Wordpress Social Share Plugin powered by Ultimatelysocial