KHCの

KHC( https://twitter.com/we_can_panic )が140字を超えるツイートをするところ

3月の日記

2/29

やるぞやるぞやるそ

なんか最近仕事が忙しい!
趣味の時間が週末にしか取れない。たぶん周りの人間もそうだろう

今年の正月に大宰府菅原道真公に参ってきたので、今年は成長の年にしたいと思っている。
1月は「やるぞ」って思うことから。去年までは「やるぞ」って思っても「今やりたくないと思っているこの自分が真実の自分。それを押し殺した先で何が得られる…?」という考え方になっていたので、強制的に自分を押し殺せるメンタルを身に付けようとしていた。
そして実際成果は上がった。自分を押し殺せるメンタルを得た。

この境地に至って気づいたけど、大人(社会人)ってみんなこのメンタルを持ってるんだな。俺の周りの人間は「まあ、やるしかないですよね。。。」みたいな仕事に取り掛かるのがすごく早かったり、ダルくて一週間くらいかかりそうな面倒な仕事を一日でやってたりしている。どこかの段階でこうならざるを得ないんだろうか

2月はやりかけていたラテン語や哲学の本を消化した。
もともとDuolingoでラテン語をやっていたけど、おろそかにしていた格変化や動詞の活用の暗記などをやっつけている。-o, -inis, -ini, -inem, -ine、-us, -eris, -eri, -us, -ere。大学とかでラテン語の授業を取っていた人には敵わないけど、おそらく最初の半年くらいは無双できるんじゃないだろうか

3月は2月で学んだことをより身に付ける予定。
手始めに日記をラテン語で書いてみたりしようかな。。

日本語

今日は電車が止まった。花粉もひどかったし地震も起こってたらしい。
でも、どれも東京で見慣れた光景だ。こういうのもアーバンチックって言うんだろうか

英語

Today, the train has stopped. Pollen was terrible, and I heard that an earthquake has occurred.
But these are all familiar sights in Tokyo. Maybe also be called urban chick.

ラテン語

Hodie, Agmen prohibebat. pollina terribilia erant et audivi quod factus est terrae motus.
Sed haec omnia nota sunt in Tokyo. Forsitan et urbana vocentur

う~ん、見た目がかっこいい!

10月の日記

1

やるぞ

今日は昼まで寝てて、15時くらいに辛ラーメン(卵、挽き肉、ネギ入り)を食べて、19時くらいにローソンの焼肉弁当?を食べた

昨日遅くまでZoomで飲んでたから調子出なかったな〜

あすけんはやめたけどDuolingo(外国語学習アプリ)は続けてる。学んでるのはラテン語だから、「実利」感が少なくて続けるのに比較的抵抗がないのかも。これが英語とか中国語だったらどうしてもモチベーションが切れるときが来る気がする

でもこういうの、本体(自分)の意志に頼らずに出来るほうがいいんだろうな〜。これまで続け(ようとし)てきたあれこれは、自分の気持ちの問題で辞めるケースが圧倒的に多かった

というか逆にそれ以外の要素がないか。趣味って全部自分の選択だもんな

でも人生、自分の意志で全部決めたいという感情がある。自分で決めたことをやらずに寝転がってツイッターを見てるのもその時の自分の選択で、その意志は殺したくないと思う

こういうの、歳を経たら薄くなっていくんだろうか

2

朝食:ヨーグルト、コーヒー
昼食:カレーメシ
夕食:カルビクッパ

なんか昨日より調子が良い。一日前の日記に「なんだ?うだうだ書きやがって。。。」と思う

たぶんず~っと忙しい原因であるところの仕事が一区切りついたのがあるんだろうな。成果とかは上げられていないんだけど、報告ができて肩の荷が下りた感じがある

もうほんとプラトンの洞窟の比喩を地で行ってる自覚があるな。自分の心が書いた影に一喜一憂している

でも、ポストトゥルースの世界観ではそれが一つの真実でもあるんですよね。。。


カルビクッパ、ネギ入れて食ったら結構おいしかったです

3

今日は「。」を付けてみようかな。

お腹を壊しました。
カルビクッパ、そこまで辛くなかったのになー。

朝食:ヨーグルト、コーヒー
昼食:セブンのロコモコ
夕食:ステーキ

食べたものを見返すと、相当「独身」の食生活だと思う。 というか用意するのに時間がかかるものを敬遠して、なんとなく安くて美味しそうなものを選んでいるとこうなってくる気がする。

「健康に悪い」と言われてもピンときてないから直す気もそんなに起こらない。あすけんも「この栄養素が足りませんよ」と教えてくれるだけで、足りなかったらどうなるのか教えてくれないし。

良くないな……という気持ちはあるんですよ。規則正しく生きてないなって気持ちが。きっかけがないから実行に移せていないだけで。

どこかでこの溜まり溜まった気持ちが爆発してくれることを期待している。それが本格的に体を壊す前だとなお良い。

句点を付けると、なんかしゃちほこばった文章になる気がするね。

4

出社。つかれた~

なんか出社すると1.5倍くらい疲れる気がする。その代わり誘惑がないので仕事は1.5倍くらい早く進む。ついでに同僚に話しかけやすいので相談が気軽にとれる。ワークライフバランスは下がる

出社したほうが効率上がるよ~って言説、もともとは集まったほうが仕事が進むという意味だった(だろう)けど、最近は毎朝ジョギングすると健康になるよ~というフィジカル的な側面も持つようになってきた気がする

朝食:コーヒー
昼食:ヒレカツ定食、プリン
夕食:味噌ラーメン

味噌ラーメン、キャベツを軽く茹でたのを入れて食べたけど、肉野菜炒めに使うくらいの量を入れたらすごいかさになった。ラーメンとかの上に盛られてるキャベツって意外と大した量じゃないんだな

否、逆に肉野菜炒めがすごいのか(←化物語くらいの西尾維新の文体(暗号学園のいろは面白れ~(当然だけど呪術廻戦もよ!最近はハンタに似てるって言われなくなったよね)))

5, 6, 7

日記はリマインダとか設定してないから普通に忘れる、ということがある

たぶん・・
5日の夕食:ホッケの一夜干し
6日の夕食:味噌ラーメン
7日の夕食:天一の超こってりとチャーハン

魚焼きコンロ、先月知った炭を敷いてるけど、けっこう使い勝手がいい。水を敷いたりする後処理の時間がなくて、オーブンと同じ感じでコンロを使える

https://www.yodobashi.com/product/100000001003852498/


7日はブラックジャック展に行ってきた。手塚治虫先生、好きだ……

最後の回廊にブラックジャック全話の原画(1~2ページずつ)が展示されてて、最後の方は軽くくらっときていた。どのエピソードにもちゃんと見覚えがあって、全話読んでたんだな~、という気持ちになった。エピソードは「海賊の腕」が好きです

帰りに『アドルフに告ぐ』の舞台のパンフレットがあった。漫画版はけっこうストーリーものだった気がするけど、舞台にして映えるシーンとかあったかな……


天一の超こってり、野菜成分が多かった気がする

スープとしてめちゃくちゃ美味しいんだけど、正統派という感じではなかったな。「天一のスープって意外と飲みやすいんだ~」を強化したムキムキトランクスみたいな感じがする。一度はチャレンジして良いかも

チャーハンも食べたけど、思ったより醤油の味付けが濃い目で、これもノーマル天一のイメージとは違った感じがあった。あっさりベースの味なのかな?


その後マンガ熱が収まらなくて、呪術廻戦を一巻から読み返してた

呪術廻戦、単行本のタイミングで読んでるときには展開に驚かされてたけど、まとめて読み返すと「この先どうなるんだ!?」の感情が薄れて、その分キャラクターの個性に注目できる。宿儺って初期は意外と窮地だったんだな……

あと主人公チームの成長速度が早すぎる。京都校との対決から死滅回遊までひと月も経ってないんじゃないか?

↑姉妹校との対決が9月から10月で、死滅回遊が10/31の渋谷事変の直後だから、時系列として見るとやっぱり早いな。でも子供だし成長は早いか

8

Jガーデン、行こうと思ってたけど行きませんでした。。。

朝食:ヨーグルト、レーズン、コーヒー
昼食:チャーハン
夕食:鳥鍋と焼酎

今使ってる自作キーボードのファームウェアが古いとRemap(キー配置を変えるWebアプリ)から言われてるので、ファームウェアをバージョンアップしてビルドを試みてた。まだできてない

な~んか引数が違うってmakeファイルから怒られる。エラーメッセージを読むとそもそも使ってるキーボードがファームウェアのベースに登録されていない?みたいなんだけど、キーボード公式からファームウェア自体は出てるから、ビルド自体はできるんだろうな

普段は意識しないんだけど、こういう自分のできなさを突き付けられるとウゥ…という感じがしてくる。情報が少ないしそこまで得意分野でもないからまあできなくても仕方ないな、という気持ちはもちろんあるんだけど、最低限英語のドキュメントくらいは通読できるようになりたい。ここらへん周りにロールモデルがいないから成長も遅いんだろうな

9

ふたけっとに行ってきました。同時開催のショタケットと迷ったけど、こっちのほうが好きな作家さんが多かった

朝食:コーヒー
昼食:上野駅讃岐うどん屋さんの肉うどん 夕食:上野駅讃岐うどん屋さんの稲荷ずし

上野駅の店、よくメディアで取り上げられがちで何となく気になってた。讃岐うどんに関しては微妙だった。あれは完全にJRのホーム内にあるから味で売ってるわけではないのかな(失礼)

今度新しい同人イベントが開催されるらしい。気になる

www.penimarket.net

あ、自作キーボードのファームウェア直った!というかイチからクリーンインストールしたら動くようになった。ライブラリのバージョンの問題なのかな

なんやかんやあって AVR_CFLAGS="-Wno-array-bounds" みたいなオプションを付けないと動かない羽目になった。あとで昔の情報をアップデートしとくか

9月の日記

9/9

あすけんを始めたら日記の機能があったので、
「日記を書くのが健康にいいならはてなの日記を再開しようかな」と思って書き進めています

途中で止めたくないので、なるべく簡素な感じで書いていこうかな。少なくとも9月いっぱいまでは続けたいね

最初めっちゃ推敲しちゃう~
慣れなのかな


昼食:ほっともっとのミックスステーキ丼

お肉が食べたくて。。

ほっともっとのステーキ、肉がすごく柔らかくて驚いた。漬汁のおかげかな
ふつうのお弁当に入ってるお肉と比べても、月並みな表現だけど、真綿と羽毛くらいの差を感じる。ほっともっとの商品開発の人、ありがとう……

あと隣に入ってたウィンナーと鶏肉も炭火の香りがして美味しかったな~

高い肉と比べたらそりゃ負けちゃうんだけど、完成度的に600円は安く感じた。次はカットステーキ丼(700円くらい)食べてみようかな


夕食:ローソンのかつ丼

まだお肉が食べたくて。。

とんかつ、高級なイメージがあるけどかつ丼になった瞬間に庶民的になるな

お店で出てくる量を残さないように食べるのがキツいから外食はテイクアウトが多いんだけど、お店で出てくるカツがサクッとしたやつも食いたいね。。

9/10

コミティア読書会に行ってきました。。。

素晴らしかった。作者の人、ありがとう

頒布物、やはり現実のオブジェクトとして存在してるだけあって、ネットで見る作品よりも真に迫るものがある

ポポセブンさんの「Ghotiです」というイラスト集が刺さった。イラストというジャンル自体はそんなにピンときてなくて、Pixivでマンガを見てるときに出てきたらスキップしちゃうくらいの感じなのに、それでもこれは好きと言える「良さ」を感じるイラストだった。多分に主観が入ってるけど……
あとで作者の人調べてみよ

ほかの感想はスマホにメモってるから後で清書して送ろうかな

12時から16時くらいまでいたけど後半はもう脳に入ってこない感じがあった。美術館でもそうなっちゃう。もっとアート筋を付けたいね……


朝食:ヨーグルト、あんずのドライフルーツ、コーヒー

朝食、普段は食べないけどヨーグルトなら行けそう。もう完食してしまったので明日は食えませんが。。。


昼食:ガストのピザ、コーラ、コーヒー、ミルクティー

コミティア読書会会場の近くのガスト。ケンタの月見バーガーと迷った

近くの席の女の人が飲み物のグラスを倒しちゃって、店員さんに「めちゃくちゃこぼしてしまいました。。。」って言ってたの面白かった。絶対読書会の参加者だろうな


夕食:カレーそば

たんぱく質が足りなかったらしい

9/11

おなかいっぱいなのに


朝食:コーヒー

エスプレッソマシン買ってから一日に3, 4杯は飲んでると思う。会議中にカフェインが切れて猛烈に眠くなった


昼食:玉子丼

社食
うちの会社、全体的に関西風の味付けが多くて嬉しいぜ


夕食:冷やし中華

9/12

ツイッターのこの漫画、この解釈を見てほう…と思った

別に漫画の100%の解釈では全然ないんだ(ろう)けど、こういう目線は持ってないので単純に良いなと思うね…


朝食:コーヒーとマスカットジュース

昼食:バター塩ラーメン

夕食:焼肉弁当

無性に肉を食べたい時期(皆さんにもあると思います)と同じように、無性に野菜が食べたい時期(皆さんにはまだないと思います)があるんだけど、コンビニって野菜を肉みたいに売ってないね

いや、「肉と同じように野菜を食べたい」は言い過ぎか。焼肉弁当のように焼き野菜弁当が売ってたとしてもなんか寂しいって思ってしまいそうではある

仮に焼き野菜弁当を選んだとしても、付け合わせにホットスナックのチキンとか買ってそうだ

焼き野菜弁当と一緒に買うのがホットスナックのチキンなら、焼肉弁当と一緒に買うのはお総菜コーナーに2, 300円くらいで売ってるちっちゃいサラダだろうな。あれを買う人はなんとなく年上のイメージがあるけど

年齢で思い出したけど、近くの住宅街に「ゾーン30」っていう車の速度制限ゾーンがあって、通るたびに「アラサーの人たち、『俺達ももうすぐゾーン30だな』って言いそうだな~」と思う

その後に「この話を恥ずかしげもなくするようになるのが本当のゾーン30だな」と思う

9/13

「日記を書くのが健康にいいならはてなの日記を再開しようかな」と思って書き進めています

これ、効果実感できてません

あすけんの日記はたぶん「今日はこれとこれを食べた!明日はここに気を付けるぞ!」という振り返りを日記を通して行わせてるのであって、はてなの日記だとその効果が薄れちゃうのかも、ということは薄々思っている

まあ最初に一か月やるって言ったから続けるか……


朝食:-

気分でコーヒー飲まなかったら昼の眠気がやばかった。数日前と同じ過ちを繰り返している気がするな


昼食:肉うどん


夕食:辛ラーメン(+ウインナー、卵)

辛ラーメン、意外とあすけんの受けが良かった

9/14


朝食:コーヒー

昼食:キンメの一夜干し、とうふ、味噌汁

夕食:キンメの一夜干し、卵、ごはん、味噌汁

キンメは最近始めたコープの宅配で買った。魚、一人暮らし初めてからここ数年ずっと食べてこなかったけど、いいものですね……。肉と違って疲れてるときでも入るし、ちゃんと満足感がある。グリルの掃除が若干面倒かな

あと基本二人ぶんから売ってない気がする。これは肉もそうか

食べてるとき、炭の香りが欲しかったから魚焼きグリルで使える炭を探したら売ってた。ちょっと魚にレジャーを求めすぎか…?

https://www.yodobashi.com/product/100000001003852498/

明日は有給消化で午前休なので少し浮かれています。俺はなんでもできる……

9/15

午前休は前日の夜更かしとその分の睡眠時間に消えました

予定してたこと(部屋の片づけなど)はできなかったけど、体調がかなり良くなってたし、一概に「有給うまく使えなかった、トホホ……」とも言いづらい

生活習慣を修正するのが直近の課題だな……

理想を言えば21時までにご飯を食べてしまって、リングフィットアドベンチャーとかお風呂とかの時間を取りたいんだよな。最近仕事が長引くせいでご飯が遅くなってしまって達成できてない。週一、二くらいの頻度ではできてるから良しとしてもいいんだけど……

いや、どこかで遅めの終業時間を織り込んだ計画を立てようとは思っているんですよ。その瞬間その瞬間では目の前の娯楽の優先度が高いだけで。。。

今もそう。日記を書くのと過去一週間の終業時間を見て寝る前に必要なタスクを適切に配分した予定を立てる、という行為ではハードルの高さが違う。やる気が出ないというのは「やりたいな~」と思う自分とは別の場所にある感情だから。どんなに御者が優れていても馬に体力がなければ馬車は走らないんですよ!!


昼食:コーヒー、松屋牛めし

間食:ポテトチップス、シュークリーム、コーヒー

夕食:ほっともっとのブラックアンガスビーフの焼肉弁当

ほっともっとの焼肉弁当、個人的には脂身が少ないミックスグリル弁当の牛肉の方が好みだな。。。

9/16, 17, 18

3連休の間日記付けれてなかった!!!

ざっと、土曜日は掃除と買い物、日曜日はプログラム(Nim)、月曜日は横浜へツーリング、をしていた

バイク、去年の10月ごろから乗り始めたけど段々「バイク」に慣れてきている。具体的に言うと、200kg弱のエンジン付二輪車はこういう運動特性をしていて、それに対し自分はこういうアプローチができる、という理解の精度が段々と良くなっていく感じ

書いていて思ったけど、トータルで月100km(≒だいたい3時間ちょい)しか乗ってない中でこれを極めるのはだいぶ難しい感じがする。いまだにすり抜けのことを「事故のリスクと引き換えに劣悪な細い通路を恐る恐る走らされている。。。」と思っていて車と同じペースで走っているし、シティライドが主な用途だし

完全なイメージだけど、ダートとかを走ってこそのバイクだ、という感覚がある

最近の仮面ライダーももはや走んなくなってきてるな、ダート。機会があるなら一日中そこで乗り回していっぱい転んでその日の夜は泥のように眠りたい

3日前のこれ、どこかでやりたいな、という機運が高まってきている。これは良い傾向ですよ!!!

いや、どこかで遅めの終業時間を織り込んだ計画を立てようとは思っているんですよ。その瞬間その瞬間では目の前の娯楽の優先度が高いだけで。。。

9/19

今日ずっとノイキャンイヤホン付けてたけど明らかに集中できる頻度が上がった。あんまり仕事仕事したくはないけどそれとは別に「こりゃいいや」という気持ちもある。この日記だって書いてくにつれ文章の推敲速度が上がっていって仕事に貢献してしまっている。あ~~~~~~~~~~~~~~~~

夏休み、取ればよかったな。来年から気を付けよう


朝食:コーヒー

近くのショッピングモールで豆を売ってるので買った。おいしい。


昼食:トマトパスタ

麺が太めで美味しかった。調理するところを見るとパスタとソースを熱したフライパンで混ぜてた。家ではやらない過程だな


夕食:辛ラーメン

コープの配達、発注ミスで卵が20個届いてしまったので消費を意識している

9/20, 21, 22

おい!!!止まってるって!!!

原因はわかる。ここ一週間業務が過去一で忙しかったからだ。日記を書く体力がこの三日間残せなかった。来週からは比較的ラクになるからまだマシだけど

仕事、どうしても身が入らない。具体的に言うと人生における仕事の重要さ(30%くらい)と稼働時間(睡眠時間を除いて50%くらい)が見合ってない。金銭面を心配せずに生活できる今の位置に安住しまくってる気がする

まあ、働かなかったら働かなかったで生活のために必要なリソースの管理にそれ以上の時間を費やすことになるので、これが今できることの中で一番合理的ではあるんだよな。これ以上を求めるとある程度資本主義の外に出ないといけなくなる気がする

日記を書くのを休んでいると推敲能力が落ちることに気づいた。別に落ちても困らんと言えば困らんが。。。


この三日間で食べたものを思い出せる限り列挙してみる。9食思い出せれば満点だ

  • 9/22

    • トースト、コーヒー
    • ゆで卵、モンエナ
    • ほっともっとのカレー
  • 9/21

    • コーヒー、ヨーグルト
    • カツカレー
    • 中華丼の具を乗せたうどん
  • 9/20

う~ん、多分こう!

9/23

ずっとパソコン触ってた。だいぶ英気が回復した気がする

明日はなんかレジャーとか行きたいですね。。。


昼食:ヨーグルト、ドライフルーツ、コーヒー、トースト

こういうのを毎日食べることが理想だけど、どうもルーチンに組み込めない。大人(30代とか)になったらできるのかな


夕食:くまモン熊本ラーメン

これ、好きなのでわざわざAmazonで取り寄せてる

9/24

今日もずっとパソコン触ってた。来週はどっかに出かけたいね……


昼食:チャーハン

辛ラーメンの粉を卵に混ぜて食べた。卵2個使ったけどまだ味濃かったな


夕食:くまモン熊本ラーメン

これに辛ラーメンの麺合わせて食べた。おいしい。

最終週

25~30日は忙しくてつけてなかったので総括します

25~28の4日間はずっと出社していた。29日の金曜は夏休みの有給消化

↑これすごい社会人っぽい

休日以外は毎日出社してたことになるけど、2020年以前はこれがスタンダードだったのが信じられない。リモートワークで遠めのところに住んでるとはいえ片道一時間十五分だから、4日間で合計10時間電車に乗っているか歩いているかしていたことになる。アニメだったら1クール余裕で見てられる時間ですよ!

会社、行ったら行ったでみんなと話せて楽しいんですけどね。週一くらいで通うのがちょうどいいな。それだけ何かが衰えている気もするが……

在宅のスケジュールは、9時にパソコンを開き、20時にパソコンを閉じる。出社のときは、8時前に家を出て、20時に帰宅する。フレックスだから多少前後するけどこんな感じに落ち着いてる

8時から9時の間に何かを差し込める気がしたな……

行けると思いますか?

来週試してみようかな。ずっと触れていなかったリングフィットアドベンチャーでも再開するか。そのあとシャワーを浴びて、あと軽い朝食も準備できる。いま自分の中の何かが「机上!机上!」って叫んでる気がする

まあ日記もね、最終週は置いといて一か月近くは続けられたんだから何かしらのプランは実行できる気がする。ひとまず来週一週間は8時から9時までなんかやるようにするか

日記とあすけんはあまり効果が感じられないので継続するか迷うな~

あすけんは一週間の無料お試し期間で特に効果が感じられなかったので、とりあえず一か月の有料プラン(360円)を買って続けてるけど、その効果があったかと言われると迷う。そう、迷ってはいるんですよ。実行にこそ移せてないものの「野菜食わなきゃな~」という意識は膨らんだし、三食の記録を付ける行動もそこまで苦じゃないしね。あと一か月続けたらあすけんの感覚が身につくかもしれない、という感覚もある

ちょっと、一週間だけあすけんを使わずに生きてみるか。やりようによっては栄養素を意識するなんて行動はそんなに難しいことじゃない気がする

日記も、そうだな、あすけんと一緒にやめようかとも思ったけど、食事を意識するために一週間は続けてみるか…


~Post script~

食べてるとき、炭の香りが欲しかったから魚焼きグリルで使える炭を探したら売ってた。ちょっと魚にレジャーを求めすぎか…?

https://www.yodobashi.com/product/100000001003852498/

これ、買いました。肉に使ったけどそもそも魚焼きコンロの火力が弱くてうまく焼けなかった

今日の午後にでも魚で試してみるかな。。。

KeyBallのファームウェアをアップグレードするぞ

なんかめっちゃ苦労したので記録しておきます
Windows11だからかなー?

目次

1. QMKのインストール

↓の方のを参考にしました

note.com

シンボリックリンクだとうまく認識されなかったからディレクトリを直接移動した

2. カスタマイズ

エクストラキー対応

音量ボタンとかが使えるようになる

rules.mkに下記オプションを追加

EXTRAKEY_ENABLE = yes

それだけだとマイコンの容量が足りず入れられないので、代わりにいくつかのLEDオプションを無効化

config.h

#ifdef RGBLIGHT_ENABLE

#    define RGBLIGHT_EFFECT_RAINBOW_SWIRL
// #    define RGBLIGHT_EFFECT_RAINBOW_MOOD
// #    define RGBLIGHT_EFFECT_BREATHING
// #    define RGBLIGHT_EFFECT_SNAKE
// #    define RGBLIGHT_EFFECT_KNIGHT
#    define RGBLIGHT_EFFECT_CHRISTMAS
// #    define RGBLIGHT_EFFECT_STATIC_GRADIENT
// #    define RGBLIGHT_EFFECT_RGB_TEST
// #    define RGBLIGHT_EFFECT_ALTERNATING
// #    define RGBLIGHT_EFFECT_TWINKLE

#endif

レイヤーを増やす

config.hの下記の数字を書き換え

#define DYNAMIC_KEYMAP_LAYER_COUNT 7

これも↑と同じくLEDをいくつか無効化しないと入らない

なお、keymap.cにデフォルトの4レイヤー分のキーマップが記述してあるが、増やした分のレイヤーを追記する必要はない。Remapで編集可能

スクロールレイヤーを移動

keymap.clayer_state_set_user関数内で設定

デフォルトは3だが最後尾のレイヤーに移したかったので6に変更

layer_state_t layer_state_set_user(layer_state_t state) {
    // Auto enable scroll mode when the highest layer is 6
    keyball_set_scroll_mode(get_highest_layer(state) == 6);
    
    return state;
}

レイヤーのOLED表示

デフォルトのままだと小さくて見づらかったので大きくした

表示例

OLEDに写せる画像は、qmk/keyboards/keyball/lib配下のglcdfont.cに16進数で収められている

コード中で画像のインデックスを指定すると、その範囲内の画像が表示される形式
詳細は以下リンクが分かりやすい

qiita.com

レイヤー表示の素材となる白の四角を新たに追加。コード末尾の配列の一行を書き換える

const unsigned char font[] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  ...
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // <- ココ
};

keymap.cの中で画像のインデックスを指定する

今回は複数のイメージを写すので、↓のように配列をいくつか作る

/* Display Current Layer */

#ifdef OLED_ENABLE

#include "lib/oledkit/oledkit.h"

static const char PROGMEM qmk_logo[] = {
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94,
    0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4,
    0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0x00
};
static const char PROGMEM qmk_logo_connect[] = {
    0x80, 0x10, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x11,
    0xA0, 0x10, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0x11,
    0xC0, 0x10, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0x11, 0x00
};
static const char PROGMEM qmk_1[] = {
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0x00
};
static const char PROGMEM qmk_2[] = {
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF, 0x20, 0x20, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF, 0x00
};
static const char PROGMEM qmk_3[] = {
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0x00
};
static const char PROGMEM qmk_4[] = {
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0x20, 0x20, 0x20,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0x00
};
static const char PROGMEM qmk_5[] = {
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF, 0x20, 0x20, 0xDF, 0x00
};
static const char PROGMEM qmk_6[] = {
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF, 0x20, 0x20, 0xDF,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDF, 0xDF, 0xDF, 0xDF, 0x20, 0x20, 0xDF, 0x00
};

その下に処理を書く

void oledkit_render_info_user(void) {
    // デフォルトで押下したキー、動かしたボールの動きが表示される設定は無効化する
    // keyball_oled_render_keyinfo();
    // keyball_oled_render_ballinfo();

    switch (get_highest_layer(layer_state)) {
        case 0: // デフォルトレイヤーのときはPCとの疎通チェックのために特別な画像を表示
            oled_write_P(qmk_logo_connect, false);
            break;
        case 1:
            oled_write_P(qmk_1, false);
            break;
        case 2:
            oled_write_P(qmk_2, false);
            break;
        case 3:
            oled_write_P(qmk_3, false);
            break;
        case 4:
            oled_write_P(qmk_4, false);
            break;
        case 5:
            oled_write_P(qmk_5, false);
            break;
        case 6:
            oled_write_P(qmk_6, false);
            break;
        default:
            oled_write_P(qmk_logo, false);
            break;
    }
}

#endif

レイヤーロック機能

MOキーとかで遷移したレイヤーを固定する機能

下記リンクの方のを参考にしました

getreuer.info

keymap.cに下記を記載

/* Layer Lock */

enum custom_keycodes {
    KC_LAYER_LOCK = KEYBALL_SAFE_RANGE // 5DBB
};

bool process_record_user(uint16_t keycode, keyrecord_t *record) {
    if (!process_layer_lock(keycode, record, KC_LAYER_LOCK)) { return false; }
    return true;
}

これで、KC_LAYER_LOCKというキーを新たに追加できた

固定ロジックの実装は下記のソースで行っている。featuresディレクトリを自分のkeymap.cとかが置いてあるディレクトリの下に作成し、その下に↓の2ファイルを配置

qmk-keymap/features/layer_lock.h at main · getreuer/qmk-keymap · GitHub
qmk-keymap/features/layer_lock.c at main · getreuer/qmk-keymap · GitHub

配置イメージ

keymaps
├ default
└ khc
  ├ features
  │  ├ layer_lock.c
  │  └ layer_lock.h
  ├ config.h
  ├ keymap.c
  └ rules.mk

コードを紐づけるために、下記をrules.mkファイルに追記

SRC += features/layer_lock.c

また、keymap.cの頭に下記を追記

#include "features/layer_lock.h"

この状態でビルドしたファームウェアを入れたKeyBallをRemapに繋ぎ、5DBBを固定したいレイヤー上の任意のキーに配置すれば、固定キーが使えるようになる

固定した状態を解除するにはもう一回同じキーを押せばOK

マウスの微調整

大きく動かせばより早く、小さく動かせばより遅く移動するようになる

追記

CPIやスクロール感度はqmk/keyboards/keyball/lib/keyball/keyball.cで編集可能

俺がMermaidのチートシートを作る(クラス図)

Mermaid、いい感じのチートシートが見つからなかったので作ります

完成図

↑のコード

  ```mermaid
  classDiagram

  %% Original typeの宣言
  class Id { -int value }
  class Name { -string value }
  class Age { -int value }

  %% Enum型の宣言
  class UserType {
    Employee
    Customer
    Administrator
  }
  <<enumeration>> UserType
  %% <<>> の中は何を書いても通る

  %% クラス
  %% クラス内の変数等の書き方は自由(id: Id 等もOK)
  class User {
    +Id id
    +Name name
    +Age age
    -UserType type
    +regist()
    +greeting()
  }
  <<interface>> User

  class EmployeeUser {
    +Id id
    +Name name
    +Age age
    +regist()
    +greeting()
  }

  %% 関係性
  User <|-- EmployeeUser: extends
  User o-- Id
  User o-- Name
  User o-- Age
  User o-- UserType
  ```

記号

https://zenn.dev/tak_uchida/articles/da583cf960e854 より

Mermaidのルールではないが使うので掲載

記載 意味 サンプル
* Abstract foo*, foo()*
$ Static foo$, foo()$
+ Public +foo, +foo()
- Private -foo, -foo()
# Protected #foo, #foo()
~ Package/Internal ~foo

矢印

https://zenn.dev/tak_uchida/articles/da583cf960e854 より

意味はMermaidのルールではないが使うので記載

一覧

記載 意味
A <|-- B 継承
- Aが親クラス
A <|.. B 実現
- Aがabstract
A <-- B 関連
- AがBのクラス内変数
A <.. B 依存
- Aの変更がBに影響を与える
A *-- B 構成
- Aの構成にBが寄与(BはAの子)
A o-- B 集約
- Aの構成にBが寄与(BはAの子とは限らない)
A -- B リンク
- 何かしらの関連
A .. B リンク2
- 弱い関連

サンプル付き一覧

記載 意味 サンプル
<|-- 継承
- Aが親クラス
<|.. 実現
- Aがabstract
<-- 関連
- AがBのクラス内変数
<.. 依存
- Aの変更がBに影響を与える
*-- 構成
- Aの構成にBが寄与(BはAの子)
o-- 集約
- Aの構成にBが寄与(BはAの子とは限らない)
-- リンク
- 何かしらの関連
.. リンク2
- 弱い関連

PythonのこれってNimでどう書くの

個人的に使う表現ランキング。
Nimはスクリプト言語じゃないから本来の書き方から離れがちではあるけど。
やるぞっ。

1. CSVの読み込み(簡易)

Python

csv_data = [i.strip().split(",") for i in open("hoge.csv").readlines()]

Nim

import strutils, sequtils
var csv_data = readFile("hoge.csv").split("\n").mapIt(it.strip.split(","))

2. 転置

Python

data = [[1,2,3], [4,5,6]]
data2 = [[j[i] for j in data] for i in range(len(i))]

Nim

import sequtils

var
  data = [[1,2,3], [4,5,6]]
  data2 = block:
            var tmpdata: seq[seq[int]]
            for i in 0..<data.len:
              tmpdata.add(data.mapIt(it[i]))
            tmpdata

3. CSV形式→JSON形式

Python

list_data = [
  ["1", "2", "3", "4"], # <- csv header
  ["A", "B", "C", "D"],
  ["E", "F", "G", "H"]
]
dict_data = [{key:vals[i] for i, key in enumerate(list_data[0])} for vals in list_data[1:]]
"""
[
  {'1': 'A', '2': 'B', '3': 'C', '4': 'D'},
  {'1': 'E', '2': 'F', '3': 'G', '4': 'H'}
]
"""

Nim

import json, sequtils, tables

var
  list_data = [
    ["1", "2", "3", "4"], # <- csv header
    ["A", "B", "C", "D"],
    ["E", "F", "G", "H"]
  ]
  # 発想はPythonと同じで、list_dataのvalue部分をイテレートしてkey部分の要素と1:1でdictを作っている
  # seq[(string, string)]だと%でJsonに変換できなくて間にtoTableを嚙ましてある
  dict_data = %list_data[1..^1].mapIt(% zip(list_data[0], it).toTable)

  # さすがに分かりづらい。
  # 普通に書くならこうかも
  dict_data = block:
                var
                  keys = list_data[0]
                  tmpdata = %[]
                for vals in list_data[1..^1]:
                  tmpdata.add( % zip(keys, vals).toTable)
                tmpdata

4. Jsonの配列オブジェクトから任意の値を削除

Python

dict_data = [
  {"id": 1, "name": "Yulia"},
  {"id": 2, "name": "Raoh"},
  {"id": 3, "name": "Toki"},
]
# lambdaよりもこっちが書きやすいと思う
dict_data_2 = [i for i in dict_data if i["id"]!=2]

Nim

import json, sequtils

var
  dict_data = %* [
    {"id": 1, "name": "Yulia"},
    {"id": 2, "name": "Raoh"},
    {"id": 3, "name": "Toki"},
  ]
  dict_data_2 = %* dict_data.getElems.filterIt(it["id"].getInt!=2)

5. Jsonファイルの保存

Python

import json

dict_data = [
  {"id": 1, "name": "Yulia"},
  {"id": 2, "name": "Raoh"},
  {"id": 3, "name": "Toki"},
]

json.dump(dict_data, open("hoge.json", "w"), indent=2)

Nim

import json

dict_data = %* [
  {"id": 1, "name": "Yulia"},
  {"id": 2, "name": "Raoh"},
  {"id": 3, "name": "Toki"},
]

writeFile("hoge.json", dict_data.pretty)

6. CSVの読み込み(commaエスケープなど)

Python

import csv
csv_data = list(csv.reader(open("hoge.csv")))

Nim

import parsecsv, sequtils
var
  csv_data =  block:
                var
                  tmpdata: seq[seq[string]]
                  x: CsvParser
                defer: x.close()
                x.open("hoge.csv")
                while x.readRow():
                  tmpdata.add(x.row)
                tmpdata

Nimは(Pythonと張り合うために)シェル芸的な書き方が多かったけど実際のコーディングでは適宜blockや関数に落とし込んで実装することが多いかも。
blockが便利すぎる。手続き型みたいに文字空間を汚さないし、関数みたいに別の箇所を参照しなくて良いし。

読む難易度はPythonの内包表記と似たり寄ったりなはず(どちらかが読みにくいと感じたなら、それは他方との習熟度の違いの影響だと思う)

Karaxプラクティス集

https://github.com/karaxnim/karax

↑のライブラリを触っています。Nimで動的Webページが作成できるらしい。

基本

↓のHTMLでJSにトランスパイルされたNimコードを呼び出し。
Karaxは div[id="ROOT"] の要素を見つけ、そこから下に要素を生成していく。

<html>
  <head>
    <title>Sample page</title>
  </head>
</html>

<body id="body">
  <div id="ROOT">
    <script type="text/javascript" src="index.js"></script>
  </div>
</body>

Nimコードの例。↓をnim js index.nimなどしてindex.jsにトランスパイルし、HTMLに読み込ませる。

import strformat, sugar, colors
import karax / [kdom, vdom, karax, karaxdsl, vstyles]

type
  # Kanbanオブジェクト
  # Nim 2.0.0からtypeの宣言時にデフォルト値を設定できるようになった
  Kanban = ref object
    posx, posy: int
    headtxt = ""
    bodytxt = "write description here"
    headerColor = colAqua
    bodycolor = colAquamarine

# Kanbanの管理
var
  kanbanlist: seq[Kanban] = @[]

# event系の関数。引数はevent, nodeで固定。もしくは何も書かない。
proc addKanban(ev: Event, n: VNode) =
  var
    mev = (MouseEvent)ev
    num = kanbanlist.len+1
    title = case num:
      of 1: "1'st Kanban"
      of 2: "2'nd Kanban"
      of 3: "3'rd Kanban"
      else: fmt"{num}'th Kanban"

  kanbanlist.add(Kanban(
    posx: mev.pageX,
    posY: mev.pageY,
    headtxt: title
  ))

# Kanbanのデータに対応したHTMLコード生成。
proc makeKanban(k: Kanban): VNode =
  let
    kanbanStyle = fmt"position: absolute; top: {k.posy}px; left: {k.posx}px;"
    headerStyle = fmt"background-color: {k.headerColor}"
    bodyStyle = fmt"background-color: {k.bodycolor}; height: 3em"

  buildHtml tdiv(style=kanbanStyle.toCss, onclick = (ev: Event, n: VNode) => ev.stopPropagation):
    tdiv(class="kanban-header", style=headerStyle.toCss):
      input(placeholder=k.headtxt)
    tdiv(class="kanban-body", style=bodyStyle.toCss):
      textarea(placeholder=k.bodytxt, style="width: 100%; height: 100%".toCss)


# 全体の構造。これがアクションのたびに再計算されて動的なページを構成している
proc createDom(): VNode =
  buildHTML tdiv:
    # ヘッダ
    tdiv(class="header"):
      h1: text "KAN-BAN"

    # メイン。kanbanlistのぶんだけmakeKanbanして、HTMLオブジェクトを作成。
    tdiv(class="parette", onclick=addKanban, style="width: 600px; height: 400px; border: 1px solid black".toCss):
      for kanban in kanbanlist:
        tdiv(class="node"):
          kanban.makeKanban()

    button:
      text "refresh"
      proc onclick() =
        kanbanlist = @[]

    # フッタ
    tdiv(class="footer"):
      text "@we_can_panic"

# 任意のイベントが起こった際、createDom関数が仮想DOMをレンダリングし、差分があれば更新
setRenderer createDom

クリックしたところにKanbanが生成される。

チートシート

クリックイベント

2つの方法がある

一つは、関数を定義して任意の要素に渡す方法

# 引数は固定、もしくは何もつけない
proc greet(ev: Event, n: VNode) = echo "Hello world!"

proc main(): VNode =
  buildHtml tdiv:
    # ここで関数を渡す
    input(`type`="button", onclick=greet)


無名関数を渡すこともできる

import sugar # 無名関数の作成に必要

proc main(): VNode =
  buildHtml tdiv:
    # ここで関数を作成して渡す
    input(`type`="button", onclick=(ev: Event, n: VNode) => echo "Hello world!")


もう一つは、proc onclick()を任意の要素内で宣言する方法

proc main(): VNode =
  buildHtml tdiv:
    input(`type`="button"):
      # ここでevent名に合わせた関数を宣言
      # 関数の名前は固定なので、無名関数を渡すことはできない
      proc onclick(ev: Event, n: VNode) =
        echo "Hello world!"


DOM生成の都合上、ループで別々の関数を渡したいときにはコツが必要

var fruits = @["Apple", "Banana", "Pineapple"]

proc main(): VNode =
  buildHtml tdiv:
    for fruit in fruits:
      input(`type`="button"):
        proc onclick(ev: Event, n: VNode) =
          echo "Hello " & fruit & "!"

上記のように書いてしまうと、"Hello Pineapple!"を出力するボタンが3つ生成されるようになってしまう

この場合、inputの生成のたびに関数を生成する必要がある
ただしonclickの関数は引数が固定されているので、onclickの関数を生成する関数を作ってループの回数分実行してやる必要がある

var fruits = @["Apple", "Banana", "Pineapple"]

# onclickの関数を生成する関数
proc generateGreeter(word: string): proc (ev: Event, n: VNode) =
  greet(ev: Event, n: VNode) =
    echo "Hello " & word & "!"

proc main(): VNode =
  buildHtml tdiv:
    for fruit in fruits:
      # ループ分、関数を生成
      input(`type`="button" onclick=generateGreeter(word))

要素を動かす

const
  maxX = 400
  maxY = 600

var
  posX = 200
  posY = 300
  deltaX = 10
  deltaY = 10

# 0.1秒ごとに、posX, posYが更新され、ボールが動く
proc moveBall() =
  posX = posX + deltaX
  if posX > maxX:
    posX = 0
  posY = posY + deltaY
  if posY > maxY:
    posY = 0
  redraw()

discard setInterval(moveBall, 100)

proc main (): VNode =
  buildHtml tdiv:
    # 枠
    tdiv(style=fmt"width: {maxY}; height: {maxX}; border: 1px solid black;".toCss):
      # ボール
      tdiv(style=fmt"position: absolute; top: {posX}px; left: {posY}px; background-color: blue; width: 10px; height: 10px; border-radius: 50%".toCss)

setRenderer main

APIコール

基礎は以下

import std/jsfetch, asyncjs, httpcore
import karax / [kdom, vdom, karax, karaxdsl, vstyles]

proc get(url: string): Future[Response] {.async.} =
  result = await fetch(url.cstring)

proc post(url, body: string): Future[Response] {.async.} =
  let opt = newFetchOptions(
    metod = HttpPost,
    body = body
  )
  result = await fetch(url.cstring, opt)

関数の型が固定されている都合上、async関数はonclick下では使えないので、fetch部分は別の関数に切り分ける

var
  responseStatus: string
  responseBody: string

# fetch部分
proc displayGetResult(url: string) {.async discardable.} =
  let res = await get(url)
  responseStatus = $res.status
  responseBody = block:
    let txt = $(await res.text)
    try:
      parseJson(txt).pretty
    except:
      txt

proc displayPostResult(url, body: string) {.async discardable.} =
  let res = await post(url, body)
  responseStatus = $((await res).status)
  responseBody = block:
    let txt = $(await (await res).text)
    try:
      parseJson(txt).pretty
    except:
      txt

onclick下の処理では関数のキックのみを行う

proc main(): VNode =
  buildHtml tdiv:
    tdiv:
      input(id="url", style="width: 300px".toCss, value="https://sample.com/api/...")
    tdiv:
      textarea(id="body", style="width: 300px; height: 50px".toCss, value="{\n  \"\": \"\"\n}")
    tdiv:
      button:
        text "GET"
        proc onclick() =
          let url = $getElementById("url").value
          # キック
          displayGet(url)

      button:
        text "POST"
        proc onclick() =
          let
            url = $getElementById("url").value
            body = $getElementById("body").value
          displayPost(url, body)

    tdiv:
      text "status: "&responseStatus
      br()
      text "body: "&responseBody

setRenderer main