20250116 豆苗怖い

睡眠の質が悪い。9時頃起床し、朝食、労働(↓)。退勤、ジム(相撲観戦)、買い物、夕食、勉強会。料理、片付け。

相撲は熱海富士がすごかった。豊昇龍の肘が心配だ。大の里は間違いなく力は一級品で押し負ける相撲はめったに無いのだが、相手をしっかりと捕まえないまま前に出て返されている。まあ誰も大の里を相手に真っ向勝負では勝てないという点で贅沢な悩みですらあろうが、硬軟織り交ぜた戦いができないと強みは活かせないなと。

食事はいつもサラダを1品自作している。ネタ切れだったので料理上手な友人に訊いて「もやしと豆苗のニンニクごま油」を作ってみることにした。作るというほどのものではないが…。豆苗は初めて買ったが、切ったあと根の部分を水に浸けておくとまた生えてくるらしいのでやってみた。数時間経って見ると茎の切断面に透明な汁が分泌されていて、おそらく傷を修復しているのだろうが、急に植物の生き物らしさを感じて「俺が…斬ったのか…?」と怖くなってきた。

これは浸けはじめてすぐ。

20250116 日記

7:30起床。散歩、朝食、掃除、労働(普通)。休憩、ジム(相撲観戦)、退勤。

Dota2(負)、夕食、勉強会、歯磨き、ゴミ出し、睡眠薬。

朝散歩で寒い思いをすることで暖かい部屋への感謝を持って労働開始できる。が、敢えて寒い思いをするのはサウナerの冷水浴と同じなのかもしれない。帰宅して手を洗うときは、わざと水を流しながら隅々まで石鹸をつけることで温水に切り替わるのを待つ。クソくだらない資源の無駄だ。

執筆メモに「root」と書いてあるのだが意味がさっぱりわからない。マジで何?

次の勉強会題材は『暗号と認証 しくみと理論がしっかりわかる教科書』にした。友人に昔貰ったものだ。その友人の性格からしていかめしい本かと思ったら本当にしっかりわかりそうな親切な教科書だった。

個人開発(僕は勉強会と個人開発は内容を分けることにしている)では今はRustで簡単なwebスクレイピングをしている。Rustは型システムが充実しているしAIもRustが得意なのでスラスラ進む。しかしserdeで型なしjsonをパースするときに、jsonのフォーマットは同じなのに内容によってStringがnull扱いされて失敗することがあり、謎だ。

20250113 日記

昨夜cssの微調整にのめり込みすぎて朝に寝たので、14時頃起床。ゴミ捨て失敗。昼食、個人開発。

相撲のシーズンが来たので見ている。豊昇龍にはもともと技と状況判断力があり、最近は力も充実してきて良さそうだ。照ノ富士は勝ちはしたが力が感じられない。

友人がGeoGuesserを配信していたので見ていた。判断が早い。

そう言えば数日前に謎の光景を見た。ハトが生け垣に群がっている。見ると紫色の実がついていたのでそれを食べていたんだろうか。食事というほど落ち着いても見えないが…

今日は早めに寝よう。

リストのツールチップをgridで作る/ローカルLLM環境構築

リストのツールチップをgridで作る

リストの項目をクリックすることで詳細情報が展開されるタイプのUI(これツールチップって言うんか…?)。

  • その項目の近くに表示されてほしい
  • 詳細情報が表示されることでリストの配置が乱れてほしくない

という前提を置くと、position: absolteで配置するのがシンプルだろう。しかしabsoluteによる配置物はレスポンシブ化が難しい。詳細情報のwidthを固定すると画面の横幅が狭いとき小さくなってくれずにはみ出すし、逆に親であるリストに合わせて100%とするとリストの大きさを先に確定させる必要があり、リストの隣にも表示物があるので不都合だった。

いろいろ考慮した結果gridで詳細情報表示領域を確保してやるのがいい感じだった。まず4行のgridを作る(画像では5行あるがここでは関係ない目的で1つ多い。気にしないで)。詳細情報を表示する前の各項目は3行だけ利用する(grid-column: span 3;)。

■■■□
■■■□
■■■□
■■■□
■■■□

そしてある項目の詳細情報を表示するときは、HTMLでその項目の直後に1行n列(図ではn=3)の要素を置く(grid-column-start: 4; grid-row: span 3; 4行目に3列分使って要素を配置する)。


■■■□
■■■□
■■■▤ <選択
■■■▤
■■■▤

cssは本当に難しい。各要素の大きさがどこで決まっているのかわからない、ググったときに情報の質がピンキリ、プログラミングよりも1つの問題に対して様々な解決法があり良くない意味での創意工夫の余地が大きい。さらに個人開発で言うと終わりがない。いつまでも触れてしまう。僕はあまり考え過ぎたくなくてGoogle検索をひたすらパクるという方針でやっているのだが、Google検索のUIも少しずつ変化しているのでな…。

ローカルLLM環境構築

AI系の開発をやってみたいけどOpenAIに金払いたくないのでローカルマシンでLLMを動かすというのにチャレンジしていた。動かす事自体はそう難しくなくollama(LLM界のdocker cliみたいなやつ)を入れて、huggingface(LLM界のdockerhubみたいなやつ)からpullしてきてrunすれば済む。そこにwebuiをつけたりAPIアクセスしたりweb検索エンジンと連携させたり…みたいな細かい手間もあるが別にAIだから難しいということはない。

難しかったのは個人所有程度のマシンでLLMを動かしても速度も質も全然ダメというところだ。browser-useでURLを指定してこれを開けと指示するだけでもCPU100%で30分間かかってまだ終わらない。こういうのをやってみるとChatGPTがいかにものすごいか(LLMを実用レベルにするためにどれだけの半導体と電力をつぎ込んでいるのか)というのがわかる。

また今アツい分野であるだけに技術の進み方も速く、今日入れてみたパッケージが今日の更新で壊れてて3時間前にissueが立ってリアルタイムで解決法が共有されているという光景を目にした。不安定ではあるがダイナミックな活力がある(他分野から見るとwebフロントもこう見えるんだろうか、最近はさすがにすこし落ち着いていると思うが)。

20250111 予防接種/WordPressのテーマ

予防接種

急に思い立ってインフルエンザの予防接種を受けた。もっと早い方が良かったけど今からでも無意味ということはない。健康保険組合の提携医療機関なら少し安かったけどもうやってなかったので諦めて、いつも睡眠薬をもらう病院に併設された一般内科でやってもらった。

ほぼ流れ作業ではあるものの一応医療行為ではあるので問診票の記入が求められる。15年くらい前に注射の後迷走神経反射で倒れたことがある(注射が嫌いなだけ)ので、正直に書くとそのことを訊かれるのだが、15年前一度きりですと言うといつもじゃあ大丈夫ですねで終わる。実際大丈夫だしな。少し痛かったけど。必要な手順なのはわかってるけど刺してる状態で「痺れないですか」って聞いてくるの、ないないいいから早く終えてくれ!と思いませんか?

コロナの予防接種で感覚が狂うがインフルエンザの予防接種は発熱したりはしない。ただ激しい運動はダメとのことなので念の為ジムはなしにした。明日・明後日はジムは休館日なので3日連続でジムに行けないことになり少し残念だ。野郎ラーメンを食べて(これは僕は二郎のパチモンだと思ってたんですが、近所に最近できた店はちゃんと美味しくて認識を改めた)薬局で薬をもらって買い物して帰宅し、自宅でのんびりと掃除・洗濯・カブを加熱し、その後は個人開発をしていた。その内容は今日の別のブログ記事にまとめた。

スーパーで窓に貼る断熱シートが売っていた。ああいうのはどのくらい意味があるのだろう。シンプルな方法でエアコンの負担を軽減できるならそれに越したことは無いが、上下左右を囲まれたマンションの一室で断熱なんてそうそう気にする意味はないのかもしれない。

WordPressのテーマ

久々に技術記事を書いてみるとコードブロックが横幅的にまともに表示できておらず、前々から幅狭すぎね?と思っていたのでWordPressのテーマを変えてみることにした。しかしまあこれが難しく、まずWordPressの世界はググってもゴミと広告しか情報が出てこないという問題がある。そして僕が2カラム指向であるにもかかわらず公式テーマは時代の流れなのか1カラムばかりで、2カラムのテーマは2015年まで遡ることになった(おそらく僕がこのブログを開設した頃のものだ)。

このテーマはこのテーマでコードブロックプラグインと組み合わせるとコードの文字がバカ小さくなるという問題がある(あと日本語と英数字で太さが違いすぎ)が、もう気力が失せた。デザインは餅屋。僕は苦手だ。

追記

元の2017のやつに戻してカスタムcssで記事の幅広げました。あんまりこういう気持ちを込めた手作業はやりたくないんだけど仕方ない。

ReactでdetailsをoutSideClickで閉じるやつ

自作のアニメ視聴管理webサービス watch-duty-manager にこのようなメニューUIがある。ハンバーガーを押すとメニューが開く。これはHTMLのdetails/summaryタグで実現している。

こういうメニューはメニューの外側をクリックすると閉じるのが一般的だ。しかしdetailsタグは標準でそのような挙動をしない(まあメニューにだけ使われるものではないからね)。summary(ハンバーガー部分)をもう一度押すことで閉じることができる。外側クリックで閉じる方法を適当にググってみると

  • detailsが開いているときはcssでsummaryを画面全体を覆うように巨大化させる
  • JavaScriptのクリックイベントを全て拾ってなんとかする

という2つの方針が見つかった。1はなかなか賢いと思うものの、真面目に作り込もうと思うとなかなかバギーな部分もありそうで(特にレイヤー順とか)やめて2をやることにした。

みんな大好き react-use に useClickAway というカスタムフックがある。refで参照されたHTML要素の外側がクリックされたときに指定されたコールバックを実行する。実装としては愚直に2をやっているだけ。

// https://github.com/streamich/react-use/blob/ad33f76dfff7ddb041a9ef74b80656a94affaa80/src/useClickAway.ts#L16-L22

const handler = (event) => {
  const { current: el } = ref;
  el && !el.contains(event.target) && savedCallback.current(event);
};
for (const eventName of events) {
  on(document, eventName, handler);
}

これを使って

function Component() {
  const ref = useRef<HTMLDetailsElement>(null);
  useClickAway(ref, () => {
    if (ref.current && ref.current.open) {
      ref.current.removeAttribute("open");
    }
  })
  return (
    <details ref={ref}>
      <summary>summary</summary>
      details
    </details>
  )
}

てな感じでやれば一応動くものは作れる。作れるのだが、この作りだとdocument内のどこをクリックしても、画面内のこのコンポーネントの数だけ ref.current && ref.current.open の評価が走る。実際のユースケースでは100個くらいの表示はあり得る。まあ実際のところ100個あろうと大したコストではなく、使用感に影響を与える16msには届かないのだが、個人開発だからそういう現実的な損得はおいといて、もっと効率的な作りを追求することにした。

この作りの何が無駄かと言えば同時に開くdetailsは1個しかないのに画面内のdetails全てに対して同じ判定関数を与えていることだ。そこでデフォルトではなにもしない関数を渡しておいて、detailsが開いたときだけuseClickAwayに渡す関数を差し替えることにした。

const noOpCallback = () => {};

const useCloseDetailsOnClickAway = () => {
  const ref = useRef<HTMLDetailsElement>(null);
  const closeCallback = useCallback(() => {
    if (ref.current) {
      ref.current.removeAttribute("open");
    }
  }, [ref.current]);
  const [onClickAway, setOnClickAway] = useState({ f: noOpCallback });
  const onToggle = useCallback<React.ReactEventHandler<HTMLDetailsElement>>(
    (_) => {
      if (ref.current === null) {
        return;
      }
      if (ref.current.open) {
        setOnClickAway({ f: closeCallback });
      } else {
        setOnClickAway({ f: noOpCallback });
      }
    },
    [],
  );
  useClickAway(ref, onClickAway.f);
  return { ref, onToggle };
};

function Component() {
  const { ref, onToggle } = useCloseDetailsOnClickAway();
  return (
    <details ref={ref} onToggle={onToggle}>
      <summary>summary</summary>
      details
    </details>
  )
}

その結果できたカスタムフックがこれだ。detailsのtoggleイベントを監視して、開いたものだけuseClickAwayのコールバック関数をcloseCallbackに差し替えている。

ちなみに数字上の速度差はなかったです。たぶんReactやV8がなんとかしてるんでしょ。

 

20250110 おしぼり/タイミングを逃す

微妙な睡眠からの出勤。なかなか良い労働、退勤、ジム(有酸素のみ)。

金曜だしお腹が空いていたのでジム帰りにコンビニで肉まんとモンブランを買って、いつもの通り袋スプーン要らないって高速詠唱したら、「じゃあ(肉まん用の)お手拭きはもらってください」と言われてつけられてしまった。もらってくださいってなんだ、よくわからない。食事をする前に手を拭くべき人間だと思われたのだろうか?呆気に取られたのと「クレジットで」の詠唱とタイミングが被ってしまったので断れなかった。関西のおばちゃん的なサービス精神だったんだろうか。

遊戯王にはタイミングを逃すという概念がある。ざっくり言うと「Xした時Yできる」という任意の誘発効果は、チェーンの逆順効果解決等でXの直後に他の処理が入ってしまった場合、発動できないというルールだ。たぶんもとから知ってる人以外には意味不明な説明だろう。さらに面白いのは「Xした場合Yできる」という書き方だったり「Xした時Yする」という強制効果だったりする場合はタイミングを逃さず発動できる。そんなのオフィシャルに問い合わせないとカードの文章からは読み取れないだろ…

しかしまあ、この辺りの煩雑さも度重なる電子ゲーム化(カードゲームがコンピューターゲームになることをなんと言えばいいんだ?)によって否応なしにプログラム化され整理されたことだろう。遊戯王の膨大なカードプールを一通りゲームで使えるようにプログラムで記述するの、どれだけ大変だったんだろう。

これなんすよね

20250109 集合/絶叫

大学の後輩の卒論提出を祝って飲酒が催されているとのことで参加してきた。僕が着いたときは既に店を出て路上に移っており、通りすがりの知らない人(????)まで参加して盛り上がっていた。彼を祝い、他の後輩たちにHOW Y’ALL DOING?し、先輩に最近どすか仕事…してきた。短くも楽しい時間だったが、それはそれとしてとにかく寒い。二重ズボンとモコモコダウンで行くべきだった。普段は近い範囲で短時間しか外出しないので油断していた。

帰りの電車で、降りる人が席にスマホを落としていった。急いで拾って「お兄さんスマホ落としました」と絶叫したけどなかなか気づいてもらえない。さりとて僕は降りるわけにはいかない。ホームにいる人たちが一緒に叫んでくれて、なんとかスマホを渡すことができた。普段は大きい声を出さないけれど今日はちょうど出した帰りだったので良かった。

駅から自宅までの帰り道は風が強くなって一層寒く、とても辛かった。暖かい部屋に勝る幸せというのはそうそうない。

20250109 暗い

3時頃に中途覚醒し、再入眠まで60分を要した。その後の睡眠の質も悪く、11時に労働開始。内容は上々。

昼食は松屋の普通の牛丼(これは意外にも栄養バランスが良い、味噌汁抜くとなお良い)。

退勤、ジム、Dota2(敗)。勉強会は時間が合わずサボった。

ジムではNHKラジオニュースを聞いていたが、今日は気分が上がらず、どうして世界の裏側の悲しいニュースをどうにもできない俺が聞かなきゃいけないんだと嫌な気持ちになったのでやめた。

それでも気分が良くない日というのは自分からも嫌な情報を集めてしまいがちで、redditでは外国人たちが海賊版アニメサイトの閉鎖に対して「使いやすくて収録作品が多くて安いただ1つの配信プラットフォームに集めてくれればお金を払って見るのに…」みたいなくっだらねえ言い訳をしていて、作ってくれる人届けてくれる人への尊重がない、というよりは嘘でもあるかのように振る舞うという社会常識が存在しないんだなあとがっかりした。二度と日本のアニメーターの待遇の話するなよ。

架空のキャラクターに夢中になるということについて考えていた。これまでの人生で何度かそういう経験はある。キャラクターのstaticな属性そのものが好きというよりは、キャラクターが描かれていたり描かれていなかったりする様々な出来事に触れてどう反応していくのかという、ストーリーとの絡みまで含めて好きだ。一度夢中になると描かれていない生活の一部分まで想像して、他の人がどう考えているかについても一生懸命調べ回る。そして本編の供給が終了することを恐れる。しかし実際にはストーリーの最後の最後まで夢中であり続けることは少なく、まあそんなもんかという気持ちで終わりを迎えることが多い。大抵の面白いコンテンツは面白くなくなるまで続けられてその後終わるのでそうなるよね。

そろそろ寝るべき時間だが寝る気分にならない。困ったなあ。

20250107 七草粥

9時起床。散歩、朝食、労働(まあまあ)。

昼食、七草粥(概念)

退勤。ジムは昨日ガッツリやって若干関節に不穏を感じたので休み。Dota2(勝)、夕食、勉強会、個人開発(eslintのバージョン上げ)。

寝。