gradle の dependency locking っていつから使えるの?
https://docs.gradle.org/4.8/release-notes.html 4.8から使えるっぽい。歴史がすごい。
https://docs.gradle.org/4.8/release-notes.html 4.8から使えるっぽい。歴史がすごい。
最近は keyball44 を利用しているわけだが、親指周りにキーがもっと多かったら便利なのかなぁと思い、YMGWorks さんの The endpoint を booth で購入し、試してみた。
トラックボールの位置は左手親指。トラックボールの位置の自由度が高いのは良いなぁ。一方で、ビルドガイドで「こうだったらこう。そうじゃなかったらこう」みたいな記述が多いので、注意して読まないといけないので集中力を要する。
左手親指においたらトラックボールの位置が結構遠くて、僕の手だとちょっとキツかった。 また、キーが多いとはいえ、多かったら多かったで、使うキーの種類をもっと増やしたいとはならないなぁ、と感じたので、keyball44 でちょうど良いのかも。と思った。
また、The endpoint のトラックボールモジュールは蓋がついていないため、傾けてるとすぐに外れて転がっていってしまうのがちょっと不便。トラックボールモジュールの追加部品をけぺおさんが作っているので、そういったものを使わないと、なかなか使いにくいかもしれず。
トラックボールモジュールに含まれているネジの種類が多くて、ネジの判別をするのが結構難しいので、マステの上に置いて数えながらやると良さそう。
ロータリーエンコーダーっていうのを初めて取り付けてみたが、自分的には用途がちょっと思い浮かばなかった。
などなど。
https://github.com/tokuhirom/keyball/commit/8ae41b881609df2692aa4daa899131a171976202
こんなパッチをファームウェアに当てた。 完全に自分用なので雑。
Special Key Code の 16 を押下している間だけ、スクロールモードに入ると同時に HORIZONTAL スクロールロックをかけるという感じ。 これにより、めっちゃ快適に横スクロールできるようになって最高。
まじおすすめです。
Discussions にも投稿してみた。 https://github.com/Yowkees/keyball/discussions/561
https://projectreactor.io/docs/netty/release/reference/index.html#faq.connection-closed
公式ドキュメントにトラブルシューティングの方法がまとまっている。
HTML の pre 要素を利用して表示させているときに、通常は長い行があっても折り返されない。 折り返したい場合は以下のようにする。
.wrap {
white-space: pre-wrap;
word-wrap: break-word;
}
./gradlew detekt
では一件も検出されないが、detektMain だと検出されまくるという時がある。
一般的な checkstyle などのプラグインではcheckstyleMain は checkstyle をメインソースセットに対してだけ実行するという意味なので、checkstyleMain と checkstyleTest を足したものにだいたい等しいのだが、detekt の場合は違う。
detektMain/detektTest の時は Type Resolution が有効になる という違いがある。
detekt の組み込みのルールの中にも、Type Resolution が有効じゃないと動かないテストも多いので注意が必要(NullableToStringCall など)。
./gradlew detekt
で CI していると./gradlew detektMain
では通らないコードができあがる。
rustmigemo という、migemo を rust で実現できる便利な crate がある。 これを利用しようとしたところ、入力文字列として "h" とか "s" を入れたときに、panic することがわかった。 これは、h とか s とかで始まる文字列として、surrogate pair が含まれているからだということが、panic したところに printf debug していたらわかった。
surrogate pair で検索するような文字を、migemo で探したいことはほぼないと思うので、実際にどのような文字が含まれているかを調べてみることにした。
python で以下のようなスクリプトをさらっと書いて、辞書ファイルをスキャンしてみる。
import codecs
import sys
def is_surrogate_pair(char):
"""
Checks if a character is in the surrogate-pair range.
"""
return 0x010000 <= ord(char) <= 0x10FFFF
infile = sys.argv[1]
with codecs.open(infile, 'r') as file:
for line in file:
has_surrogate_pair = False
for char in line:
if is_surrogate_pair(char):
print("U+%X" % ord(char))
line = line.replace(char, f'**{char}**')
has_surrogate_pair = True
if has_surrogate_pair:
print(line)
実行したところ、以下のような文字だということがわかった。
U+20B9F
しか 仕懸 仕掛 仕替 併 兪 叱 史家 呵 四角 始華 子夏 子華 市価 市花 師家 志嘉 志賀 歯科 死花 然
知客 確 確然 私家 糸価 紙価 紙花 緊 而 聢 芝河 詆 詞花 詞華 詩家 詩歌 賜暇 蹙 顰 飾
**𠮟**
U+20B9F
しっ 七 卓 叱 執 失 嫉 尻 悉 櫛 湿 漆 濕 疾 確 緊 質 **𠮟**
U+20B9F
しつ 七 仕付 仕詰 叱 喞 執 失 嫉 室 志都 悉 桎 湿 漆 濕 為付 為詰 瑟 疾 膝
蟋 貭 質 躾 隰 隲 **𠮟**
U+27631
ふき 不帰 不羈 不覊 不記 不諱 不軌 付記 吹 富喜 富貴 布岐 腐気 苳 葺 蕗 袘 附記 **𧘱**
人生で一度も使おうと思ったことがない漢字なので、これはまぁ検索出来なくてもいいだろうという気分になった。
サロゲートペアを含む文字を除外する処理を辞書作成スクリプトから除外すればいいな、と思ったが、、 yet-another-migemo-dict ではビルドツールが全部含まれてるわけじゃなくて手である程度ビルドする感じになっていたので、めんどくさいからスクリプトを書いて、再生成可能なようにした。
https://discuss.codemirror.net/t/codemirror-lang-markdown-with-codemirror-legacy-modes/7925
Codemirror6 では、一部のメジャーなプログラミング言語はサポートされているが、5時代にはサポートされていたのに6ではサポートされていないプログラミング言語も多い。 そういったプログラミング言語は @codemirror/lang-legacy-modes を使うことで、利用可能だ。 @codemirror/lang-legacy-modes のドキュメントを参照すれば、Codemirror6 でどのように使うかは書いてある。
しかしながら、Codemirror6 の lang-markdown に対してどのように使うかは書いてなかった。
そこで、フォーラムで質問したところ、@marjin 氏から即座にレスがついたので大変ありがたかった。
@codemirror/language-data
というパッケージを使うと、必要なプログラミング言語の文法を動的に読んでくれるらしく、lang-legacy-modes の中身もすぐに使えるようだ。
最高!
tauri 2.0.0 beta1 が出た。今後は大きい breaking changes はなさそうってことなので、移行してみる。 趣味で作ってるメモアプリを tauri 2.0.0 beta1 に移行した。
趣味だから、まぁ beta でもいいかな的なやつです。
npm install @tauri-apps/cli@next
npm run tauri migrate
ってやると、一通り自動マイグレーションされるので、それでだいたいいける。 が、微妙にぶっ壊れるので、新規で skelton 作って、それとの差分を見ながら調整していくみたいな作業はやっぱり必要。
[dependencies]
tauri-plugin-fs = "2.0.0-beta.0"
というふうに rust 側の Cargo.toml に依存追加する。
src-tauri/src/main.rs
に以下のように書いて、プラグインを有効化する必要がある。
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_fs::init())
}
JS の API で、renameFile が rename になったり、removeFile が remove になったりと、メソッド名もそこそこ変わっている。
各APIの dir 指定が baseDir
という名前に変わってる。rename の基底ディレクトリが newPathBaseDir と oldPathBaseDir に別れたの最高っぽい。
あとは、 credentails 周りが変わってる。 migration ツールが src-tauri/capabilities/migratred.json
に権限リストを移行させてくれるが、権限の名前が変わってるので、そのままだと起動しなくなる。
例えば以下のようなエラーになる。
Permission fs:allow-read-file not found, expected one of app:default, app:allow-app-hide, ...
appdata なら appdata に読み書きする権限が、個別で用意されるようになってるんで、ここのへんのフォルダだけ触る場合は以下のように書いたらよろしい。
{
"fs:allow-appdata-write-recursive",
"fs:allow-appdata-read-recursive"
}
Menu が rust ではなく JS 側でできるっぽいがドキュメントがまだなさそう。 情報が全く見当たらない。これはしばらく待ってから対応したいところ。
sqlite が組み込みになっている。便利。
わりと簡単に移行できる。
https://github.com/heapdive/heapdive
CI がコケてる事に気づいたので諸々修正した。
Zoom 等の会議があったときに、録音して文字起こしして要約するという一連のタスクがあるわけですが、これをMacのアプリとして実装してみました。
↓このへんからダウンロードできます。 https://github.com/tokuhirom/meetnote2/releases
Zoom 会議の場合には、録画機能などもあるわけだがそういうのを使えないケースもあるのかなと思いますので。。
今回も rust + tauri で作りました。この構成が、ネイティブアプリ作るときには制限が少なくて楽な気がします。Swift で作っても良いんだけど、Swift を覚えるのがめんどくさくて rust で済ませてしまっています。apple-sys を使えば mac の api を呼ぶことにほぼ不自由はないので、swift を覚えるモチベーションが薄いですね。
Mac の場合は screencapturekit という API を利用すると、アプリケーションから発せられる音をキャプチャすることが可能です。screencapturekit-rs というライブラリを使えば、これが簡単にできます。 が、、できます、といいたいところだが、出来なかったので、出来るようにした。パッチを送って取り込んでもらったのであった。wave ファイルとして保存できるので良い。
cpal というライブラリを使うとマイクの音を raw 形式で取得できるので、hound というライブラリで wave 形式で保存していく。
アプリケーションから発せられた音声が入っている音声ファイルとマイクから取得した音声ファイルを sox コマンドでミックスし、ffmpeg で mp3 に変換していく。
音声ファイルは圧縮率高めの mp3 として保存しておいて、後から聞き返せるようにしている。
本当は ffmpeg や sox 使ってるところは、rust のライブラリで処理できるといいのだが、適切なライブラリが見つかっていないのであった。
次に、mp3 ファイルを whisper.cpp を使うと文字起こしが出来るので、文字起こしする。一応、OpenAI の transcription api にも対応しているが、M1 Macbook Pro とかだと whisper.cpp でも十分な速度で文字起こしできるので、whisper.cpp を利用するのがオススメ。
文字起こししたファイルは webvtt 形式で出力される。これを OpenAI の API を使って要約するか、TF-IDF を利用した簡易要約を行う。TF-IDF の場合はローカルで処理が完結するので、重要な情報を扱う場合はこちらのほうがオススメ。本当は、もう少し賢い要約アルゴリズムが rust で利用できると嬉しいので、良い方法を知ってる人がいたら、教えて下さい。お願いします。何卒~~
録音の開始と終了を制御するのは面倒なので、開始と終了は、Zoom 会議の開始終了とか Teams 会議の開始終了とかのタイミングを自動で検知してやってほしい。
最初は Zoom アプリの CPU 使用率が一定以上高い状態が続いていたら録音するというようなロジックでやっていたのだが、これは誤検知が多いので、window のタイトルを使うことにした。
Window のタイトルが screencapturekit で取れるので、それが対象の window title だったら録音開始するというような感じ。もう少しスマートなやり方もありそうだが、思いついてないです。いい方法知ってる人いたら教えてください。
そんな感じで、外部コマンドとかにも依存しまくりであんまり他人に使わせる気があんまない構成にはなっているものの、割りと便利だと思う。
oninput を使うのが良さそう?
<script lang="ts">
import {emit} from "@tauri-apps/api/event";
import type {MessageRepository} from "./repository/MessageRepository";
import type {Message} from "./Message";
export let replyTo: Message | null;
export let messageRepository: MessageRepository;
let body="";
let inserted = true;
async function send() {
if (body.length > 0 && body.match(/\S/)) {
if (replyTo) {
await messageRepository.reply(replyTo, body);
} else {
await messageRepository.post(body);
}
await emit("sent_message");
}
body = "";
}
function handleKeydown(event: KeyboardEvent) {
if (event.shiftKey || event.ctrlKey || event.altKey || event.metaKey) {
return;
}
if (inserted) {
inserted = false; // ignore the insertFromComposition event.
return;
}
if (event.key === 'Enter') {
event.preventDefault();
send();
}
}
function handleInput(event: InputEvent) {
if (event.inputType === "insertFromComposition") {
inserted = true;
}
}
</script>
<div>
<form on:submit|preventDefault={send}>
<textarea bind:value={body} cols="40" rows="3" on:keyup={handleKeydown}
on:input={handleInput}
></textarea>
<button type="submit">Send</button>
</form>
</div>
https://crates.io/crates/apple-sys
Rust で Mac のアプリを作りたい場合、Mac OS の API を直接触りたくなることが多々ある。 いや、僕が書きたいアプリがそういうアプリばかりだというだけかもしれないというか、完全にそうなんだけど。
たとえば CoreFoundation であるとか ScreenCaptureKit とか CoreGraphics とかそういうのです。ScreenCaptureKit であれば ScreenCaptureKit-rs を使うとかそういう感じでいろんな crate を使うというのが一般的なのだが、最新の API に対応してなかったりとかAPIをラップした結果、逆に使いにくくなってるケースとかもある。
apple-sys は 、apple-bindgen で自動生成されるので最新のAPIがあるし、一通りのフレームワークを統一されたインターフェースで処理できるので結局これが一番便利かなぁ、という気分になる。
オススメです。
QMK で増井さんの Dynamic Macro を実装してみた のだが、Mac のレイヤーで普通にやりゃいいんじゃないかという話になったので、やってみた。
rust で実装できないかなぁと思って調べてみたところ rdev を使えば良さそうだということがわかった。が、rdev は Mac, Windows, Linux をサポートしていて抽象化レイヤーとなっていて、要件を満たせないということがわかった。
rdev を見ていると、CGEventTap というAPIがあることがわかって、これをベースにやれば良さそうだった。
CGEventTap を使うと、マウスとキーボードのイベントを取れる。CGEventCreateKeyboardEvent
, CGEventCreate
とかして CGEventPost
すればキーボード入力イベントがポストできる。ということがわかり、これを使えば Dynamic Macro を実装できるということがわかった。
当初、 DynamicMacro部分の実装を rust でゴリゴリ書いていたのだが、ふと、組み込み言語で実装できるようにしたら DynamicMacro 以外の用途にも使えるよなぁと思いついたので実装してみた。
当初は mruby を使おうと思ったのだが、最新の mruby でちゃんと動く mruby の rust binding が、探した感じなさそうだったので諦めた。
boajs という rust の JS 実装があったので、これを採用することに。JS は書けない人がいないという点が優れている。 基本的な機能は実装されてて問題なさそう。
KeyScriptenはシステムトレイで動作し、直感的な設定ウィンドウを通じてスクリプトの追加や設定の変更が簡単に行えます。また、アプリケーションはユーザーのキーボード入力を全て捕捉するため、Macのアクセシビリティ設定で特別な許可を与える必要があります。
↑こんな感じで設定できる。
コードは以下のような感じに書ける。シンプルでしょ。
(function () {
const id = "com.github.tokuhirom.samples";
let latest_flags = undefined;
registerPlugin(
id,
"My great script",
"my great script",
function (event, config) {
if (event.type === "flagsChanged") {
console.log(`[${id}] flagsChanged: ${event.flags}`);
latest_flags = event.flags;
} else if (event.type === "keyDown") {
console.log(`[${id}] keyDown: ${event.keycode}`);
if (config.hotkey.matches(latest_flags, event.keycode)) {
console.log("Handled hotkey");
return false;
}
}
return true; /* true means, CodeKeys should send the keycode to the application. */
},
[ /* configuration parameters */
{
"name": "hotkey",
"type": "hotkey",
"default": "S-M-C-t",
"description": "Key sequence for something.",
},
{
"name": "size",
"type": "integer",
"description": "Size of something.",
"default": "64"
}
]
)
})();
KeyScriptenは、Toshiyuki Masui氏の「ダイナミックマクロ」の実装をバンドルしています。ダイナミックマクロについては、何もしなくてもすぐに使えます。 実装はこのへん https://github.com/tokuhirom/KeyScripten/blob/main/keyscripten-core/js/dynamic-macro.js
さらに、JavaScriptを使用したカスタムスクリプトにより、ユーザーは自分だけの独自の機能やマクロを作成できます。 https://github.com/tokuhirom/KeyScripten/blob/main/HOW_TO_WRITE_SCRIPT.md スクリプトはこのマニュアルの通りに実装すれば動きます。
KeyScripten.appは、キーボードマクロシステムの概念を根本から再定義し、プログラミングスキルを活かして日常の作業を効率化したいMacユーザーにとって理想的なツールです。JavaScriptを駆使したこのアプリケーションは、キーボード操作のカスタマイズと自動化を新たなレベルに引き上げています。KeyScriptenの開発過程は、技術的な困難と創造性の融合を示し、RustとJavaScriptの可能性を探求する興味深い旅でした。
このアプリケーションの将来は、コミュニティの参加とフィードバックによって形作られます。KeyScripten.appは、キーボードマクロとプログラミングの世界を一つにする革新的な一歩です。
ポインティングデバイスとキーボードは一体化しているべき派なので、Keyball にずっと興味があった。 これまで2年ぐらいUltimateHackingKeyboardを使ってきたのだが、いい加減飽きてきたというところもある。飽きてくるとかそういう問題か?とも思うが。。ポインティングデバイスとしてポインティングスティック(Thinkpadの赤ポチのこと)を使っていたのだが、ときどきドリフトするからソフトウェアリセットが必要だったりする。また、あんまり使いやすくはなかった。トラックボールも小さくて、あんまり使いやすくはない。わりとケンジントンのトラックボールと併用しながら使わざるをえない感じなのであった。
というわけで、自作キーボードに手を出すことにした。
自宅にははんだ付けをする設備がないので、工具を買うところから。 工具セットの全部入りを買った。結論からいうと、ハンダゴテとピンセットと半田とハンダ吸い取り線はいいけど、それ以外はいらないかも。。逆作用ピンセット、フラックスとかは正直なくても大丈夫だった。あと、コテ台は使いやすいのだが、、使わないときに邪魔でしょうがない。上と下が分離できるタイプのほうが良かったかも。が、色々選んで買うとそれだけで疲れちゃうので、一括で買ったこと自体は後悔してないです。 https://shop.yushakobo.jp/products/a9900to
まず最初に、ほとんどはんだ付けがいらない Ergo68 を買ってみた。これはほとんどはんだ付けがいらないのだが、Pro Micro のはんだ付けに失敗して、一回休み、となった。
https://github.com/yushakobo/build-documents/tree/master/Ergo68 これを参考にやったのだが、どういう状況でやるのかよくわかってなくて、コンスルーだけProMicroにつけて机においてはんだ付けしたら死んだ。しかも、ProMicroに同梱されてる足をつけたりとかしてて、もうだめだった。 https://shop.yushakobo.jp/products/ergo68?_pos=1&_sid=01cd99f3d&_ss=r まぁ、そんなに高い部品じゃないのでもう一個買いました。
というわけで、本命のKeyball61を買った。Ergo68に比べると難易度は圧倒的に高い。
ダイオードが小さくてちょっと難しいけど、まぁどっち向きか見えないというほどではない。光の加減で見える。当方、視力は0.7ぐらい。ピンセットがないと作業無理なので、ピンセットは必須。
どのぐらいはんだ付けしたらいいのかがよく分からなくて、何個かはんだ付け失敗してて、難しかった。 github discussions で、どこを見直したらいいですか~?って質問させてもらって、なんかつけ直したりしてたら動くようになった。
組み立てて、何日か使ってみると、Keyballのボールはなかなかに大きくて、これなら他のポインティングデバイスはいらないなって感じ。 ボールを右につけたのだが、ボールを左手につけたほうが、右手を自由に使えるので便利棚、と思って、もう一台ほしくなった。
61を使ってみた結果、44までキーを減らしてもいけそうだなって気分になっていたので、44の乗り換えキットを買うことにした。
Keyball61からの乗り換え割がオトクだったので購入。数字キーがないので、複数キーを押さないと行けないのがちょっと大変だったけど、慣れた。
基本的にはKeyball61とほぼ一緒なので、ほぼノーミスで完成。
オートマウスレイヤーがほしくなったので、ファームウェアも少しいじることに。昔はオートマウスレイヤーの実装は自前でゴリゴリ書かざるを得なかったが、QMKが公式で対応していて、オートマウスレイヤーへの切り替え、実はそんなに難しくなさそうなのでやってみたらできた。マウスクリックするときに、一個ボタン押すだけで良くなったので、めっちゃ快適。オートマウスレイヤー、ちゃんと切り替えられるようにちゃんと作り込んでPull-request を出してみた。 https://github.com/Yowkees/keyball/pull/454
https://amzn.to/3HebFGz これを2個使ってる。安定してていい感じ。
BLEMicro使って無線化するのもいいかもしれない、、と思ったが、正直どこかに持って歩いたりする予定はないので、机の下を配線する方向で検討中。
Keyball44はかなりよくできていて、オススメ。
https://github.com/boa-dev/boa
boa は pure rust で書かれた JS Interperter である。組み込みがとにかく容易で依存もないので、プログラミング言語を rust に組み込む場合には、第一の選択肢になりうるのではないだろうか。
https://tauri.app/v1/guides/features/system-tray/
tauri で system tray app を作っていて、設定画面を作った。設定画面を閉じたらアプリが終了する怪現象が発生した。
SystemTray周りのドキュメントを読んだら、普通に解決策が書いてあった。
tauri::Builder::default()
.build(tauri::generate_context!())
.expect("error while building tauri application")
.run(|_app_handle, event| match event {
tauri::RunEvent::ExitRequested { api, .. } => {
api.prevent_exit();
}
_ => {}
});
brew install qmk/qmk/qmk
とすると、qmk がインストールできる。これは、めちゃくちゃ時間かかる
。数時間かかるので注意。
qmk のビルドは、ln してやる。make SKIP_GIT=true keyball/keyball44:tokuhirom
とかする。
qmk flash ...
とすると、書き込むことができる。キーボードのリセットボタンを押すと自動的に書き込みが開始される。最初は pro micro web writer で書いていたが、自分でファームウェア書くならCLIで書いた方がいい。
qmk console
とすると、コンソールをモニタリングできる。
rules.mk に CONSOLE_ENABLE = yes
に書く。
void keyboard_post_init_user(void) {
// Customise these values to desired behaviour
debug_enable=true;
debug_matrix=true;
//debug_keyboard=true;
//debug_mouse=true;
}
のように書いて、デバッグ機能を有効化する。
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
// コンソールが有効化されている場合、マトリックス上の位置とキー押下状態を出力します
#ifdef CONSOLE_ENABLE
uprintf("KL: kc: %u, col: %u, row: %u, pressed: %u\n", keycode, record->event.key.col, record->event.key.row, record->event.pressed);
#endif
return true;
}
のようにして、printf debug できる。
https://github.com/samhocevar-forks/qmk-firmware/blob/master/docs/ja/newbs_testing_debugging.md
https://github.com/tokuhirom/qmk-onemoretime
Keyboard のファームウェアとして、QMK が有名なわけだけど、その上で増井さんの Dynamic Macro を実装してみた。1ファイルのCライブラリとして実装してるので、自分のファームウェアに簡単に組み込めると思う。
DynamicMacro というのは、
Dynamic Macroの原理は非常に単純で、
「同じ編集操作を2回繰り返したあとで[CTRL]+[t]を押すと繰り返された操作が再実行される」
というものです。「二度あることは三度ある」と言うように、同じことが二度あればもう一度あるのは世の中でごく普通のことです。二度実行した操作をもう一度実行することもよくあることですので、この方法はたいへん効果的です。
というようなやつ。
なんかできそうなのでやってみただけです。 動いてみるとなんか楽しいです。
キーが押されたときに、リングバッファに押したキーを保存しておく。
任意のショートカットキーが押されたタイミングで、繰り返しコマンドになっている区間を検出して、処理する。
モディファイヤーキーの処理が難しい。キー送出している間はモディファイヤーキーを押してることにしちゃいけないので、状態を変えてもどしたりした。
メモリの要素をナイーブに舐めてるけど、ちゃんと memcmp とかにしたほうがエコかもしれない。が、ちょっとめんどくさいのでナイーブに実装した。
DynamicMacroという名前は QMK そのもので使われているから。Staticにハードコードされてない、キーボード側でマクロ記録開始!とかマクロ再生!!とかできるようなやつが DynamicMacro なのである。
もう一回実行するというところが本質かなと思うのでこういうネーミングとしてみた。
気軽に組み込めると思うので、お気軽に試してみてね。
https://github.com/svtlabs/screencapturekit-rs/pull/14
何故か panic! していたところがあったので、ちゃんと Result を返すように修正した。