日記 EatからVtermへ出戻りした話とかeglot(rust)動かない問題とか

TL;DR 以前vtermのC-kまわりの挙動を追いかけていて、その流れでEatへ移行していた 今回もう一度vtermの素の設定を試したところ、C-kは特別な回避策なしで普通に動いた Eatは実運用してみると自分のワークフローと噛み合わない部分がいくつかあり、結局vtermに出戻りすることにした 副次的にEglotでRustのサーバーが起動しない問題も出ていたので、原因と対処もついでにメモしておく 経緯 もともとvtermのC-kバインドを追いかけていたらEatに移行することになった話(wasutech, 2026-07-04)でvtermからEatへ移行していた。この記事は逆に、そのEatからvtermへ戻した記録になる。 ちなみに、上の記事内でEat移行のきっかけとしてリンクされていたのが、Ki_chi氏のEmacsのターミナルエミュレーターをvtermからEatに移行しました(Ki_chi@Blog, 2026-01-29)。 削除した設定 Eatのuse-packageブロック一式を削除した。内容としては、straight経由でのcodebergリポジトリ指定、eat-semi-char-non-bound-keysの除外リスト編集、eat-reload呼び出しなど。 復元したvterm設定 元記事内で「移行前の設定」として紹介されていたvterm用のuse-packageブロックをベースに復元した。 (defun my/vterm-send-C-k () "Send C-k to vterm terminal." (interactive) (let ((inhibit-read-only t)) (vterm-send-key "k" nil nil t))) (use-package vterm :straight t :bind (:map vterm-mode-map ("C-h" . vterm--self-insert) ("C-k" . my/vterm-send-C-k)) :bind (("C-c v" . vterm)) :config (setq vterm-term-environment-variable "xterm-256color")) defunをuse-packageより前に置いているのは、Emacs Lispが上から評価される言語だから。先に定義しておかないと、use-packageの:bindが参照する段階で未定義エラーになる。 my/vterm-send-C-k自体は、元記事の著者本人も根本原因は特定できなかったと明記している回避策で、いわば対症療法である。 実際に設定を戻してみたところ、C-kは回避策なしの素の状態で普通に動いた。以前この問題を追いかけていた時点との違い(Emacsのバージョン、vtermのバージョン、あるいは単に環境差)は特定できていないので、なぜ動くようになったのかは正直わかっていない。ただ動いている以上、今のところ回避策は入れずに使っている。 別件: eglot(rust-analyzer)が起動しない vterm/eatの話とは別件だが、ちょうど同じタイミングでEglotのRustサーバー起動に失敗する問題が出た。 原因は単純で、rustcとrust-analyzerはrustupのコンポーネントとして別扱いになっており、公式toolchainをインストールしただけではrust-analyzerは入らない。個別にインストールする必要がある。 rustup component add rust-analyzer これで解決した。 出戻りマップ 項目 Eat Vterm 未Git管理ディレクトリを開いたとき ディレクトリを聞かれてモヤっとする 特に気にならない レイアウト 崩れることがしばしばあった 安定している 実装 Emacs Lispのみで完結(要確認) libvterm(C)に依存 C-k 今回は未検証 素の設定で動作した 所感 Eatは実装がEmacs Lisp単体で完結しているというのは魅力的で、パッケージとしての完成度も高いと思う。とはいえ、自分の使い方だと未Git管理のディレクトリを開こうとしたときにディレクトリを聞かれたり、レイアウトがよく崩れたりすることがしばしばあり、実際に使ってみるとかえって煩わしさの方が勝ってしまった。Emacsで完結するツールとしては素晴らしいパッケージだと思うが、結果的には私にはvtermの方が合っていたので、恥ずかしながら出戻りすることになった。とはいえ、また別のターミナルエミュレータを試すかもしれないので、結論は一旦保留にしておく。今はvtermを使う。 ...

August 26, 2026 · 1 min

Androidアプリ開発の極意 4,5章ユーザー体験と非同期処理

前の記事同様に Androidアプリ開発の極意 | 技術評論社の「ユーザーストレス軽減」まわりの実装Tipsと、並行処理の選択肢をざっくり整理してExpo周りと絡めたもの。細かい実装には踏み込まず、要点だけ。 はじめに アプリが固まる原因の大半は、メインスレッド(UIスレッド)を長時間ブロックしていること。非同期処理の設計は結局「ユーザーを待たせない」という一点のためにある。 二部構成で一部は本を読んだ感想+α、二部がExpoだとどうなるかって話。 基本的にAIで出して、それを読んで調べたりしたもの。 第1部:Androidの話 Android(Java/Kotlin)側だけで完結する話。UXまわりの実装Tipsと、非同期処理の手段がsynchronizedからVirtual Threadsまでどう変遷してきたかを、Expoの話を挟まずに整理する。 UXまわりの小さな判断 時間のかかる処理にはProgressBarで進捗を見せる SharedPreferencesは永続化のためディスクI/Oが発生する セルラー通信時は重い処理の前に一言断りを入れると親切 エラーはその場で表示しつつ、Crashlytics/ACRAのような開発者向けツールで後から追える形が理想 NDKは高速化と引き換えにデバッグの複雑さが増す。基本的には奥の手 非同期処理はなぜ増え続けているのか synchronizedは「同時に入らない」ことしか保証しない。順番までは保証しない。 Threadを直接生成した場合も同様で、生成順と実行順は一致しない。順番を保証したいならExecutors.newSingleThreadExecutor()のような直列化の仕組みを使う。 ExecutorServiceは、スレッドを直接管理せず、プールとキューで管理する仕組み。スレッド数の制御や再利用をアプリ側のロジックから切り離せる。 AsyncTaskはAPI 30で非推奨になった。ライフサイクル管理が弱く、Activity/Fragment破棄後もタスクが動き続けてリークやクラッシュを招くのが理由。 今の主流はKotlin Coroutines。スレッドをブロックせず中断できる軽量な並行処理で、排他制御が必要ならMutexを使う。UIに紐づく短い処理はCoroutines、画面を離れても続けるべき処理はWorkManagerと使い分ける。 JVM側ではJDK 21でVirtual Threads(JEP 444)が正式機能になった。I/Oバウンドな処理を大量にさばくのに向くが、CPUバウンドには向かない。ただしこれはJDK/Java SE側の機能で、Android Runtime(ART)は非対応。Androidアプリでそのまま使えるわけではない。 Akkaはアクター単位でメッセージパッシングする設計思想。2022年にライセンスがApache 2.0からBSL(Business Source License)に変わり、2024年10月以降はproduction利用にライセンスキーが必須になった。個人・OSS・startupは無料キー、商用は原則サブスクという体系。汎用ツールというより大規模分散システム向けの専門ツールという位置づけに変わっている。 比較表(Android) 手段 排他制御 順序保証 位置づけ synchronized あり なし 低レベルの排他制御プリミティブ Thread直接生成 なし なし 基本的には避ける ExecutorService 設計次第 直列化すれば可 CPUバウンド処理で有効 AsyncTask なし なし API 30で非推奨 Kotlin Coroutines Mutexで対応 設計次第 Android/Kotlinの主流 Virtual Threads 従来通り 設計次第 JVM側、I/Oバウンド向け(ART非対応) Akka アクターモデル 条件付き 大規模分散システム向け 「排他制御」と「順序保証」は別物。ここを混同すると設計は破綻する。AsyncTaskはAPI 30で非推奨、今はCoroutines(短命処理)とWorkManager(永続処理)を使い分けるのが主流。Virtual Threads(JDK 21)はJVM側の話でARTは非対応。 ...

August 18, 2026 · 1 min

Androidアプリ開発の極意 2,3章あたり ANR、UIスレッド

Androidアプリ開発の極意 | 技術評論社 この本読んでる。2017年に出版してて、内容には古いものがいくつかある。 そこはAIの使いどころ。とりあえず学習したことを軽くメモして、中身について古い場合は指摘を入れたり、今は何が主流なのかといったことを補完するような指示を出した。 サポート付きの読書っぽいことをしながら読み進めて、文章をまとめた。 概要 React Native/Expoでアプリを書いていると、「なぜかカクつくが、クラッシュログには何も残っていない」という現象に何度もぶつかる。 原因を追っていくと、大体はUIスレッドを塞いでいるか、メモリを食い潰しているかのどちらかという同じ根っこに行き着くことが多い気がする。 この記事では、Androidアプリ開発の極意 | 技術評論社の3章UIスレッド/ANRの基礎から、React NativeにおけるJS-ネイティブのスレッド分離、コルーチンとasync/awaitの正体、Android/iOS/RNの非同期処理対応、そして見落とされがちなメモリ管理(OOM・LMK・Javaのメモリリーク・useEffectのクリーンアップ)までを、実務で踏んだり踏みそうな地雷も交えて整理した。 なお、私は仕事でしばしReact Native Expoを利用しているため、なるべくそちらに沿った内容に寄せて本文を作成する。 それによって本の内容とは離れることがしばしあるが、そこは予め留意してほしい。 全体像 まずスレッドの分離構造をざっくり図にする。 [Android/ネイティブ] UIスレッド(メインスレッド) ── 描画・入力処理を担当 │ │ 5秒ブロック → ANRダイアログ ▼ ワーカースレッド/コルーチン(Dispatchers.IO等) ── 重い処理はここへ逃がす [React Native/Expo] JSスレッド ── ビジネスロジック・状態更新・レンダリング計算 │ │ (ブリッジ/JSI経由) ▼ ネイティブUIスレッド ── 実際の描画 │ (任意) Worklet用の別JSコンテキスト ── react-native-worklets-core / Reanimated ポイントは、Androidのネイティブ層には「ANR」という可視化された強制終了の仕組みがあるが、React NativeのJSスレッドが詰まっても同じ仕組みは働かないという非対称性にある。以下、順番に見ていく。 UIスレッド/ANRの基礎 Androidでは、UIスレッド(メインスレッド)が入力イベントに応答できない状態が一定時間続くと、システムが強制的にANR(Application Not Responding)ダイアログを出す。Android公式ドキュメントによれば、入力イベント(キー押下や画面タップなど)に5秒以内に応答しない場合にANRがトリガーされる。これはフォアグラウンドの入力ディスパッチに関する条件で、他にもブロードキャストレシーバーやサービスの応答遅延など複数のANR条件が存在する。 一方、React Native/Expoでは事情が異なる。RNのアーキテクチャはJSスレッドとネイティブUIスレッドが分離されている。JSスレッドが重い処理で詰まっても、ネイティブ側のUIスレッド自体がブロックされているわけではないため、Android OSが検知する「入力ディスパッチのタイムアウト」という意味でのANRダイアログは出にくい。ただし、体感としては「タップしても反応がない」「アニメーションがカクつく」という、ユーザーから見ればANRと区別のつかない現象が起きる。ANRダイアログが出ないぶん、むしろ発見が遅れて厄介とも言える。 補足: 「RNならANRが原理的に起きない」という理解は不正確。JSスレッドが完全にブロックされている間、そのJSスレッドに紐づくタッチイベント処理やRedux的な状態更新も止まるため、ユーザー体験としては同種の「固まり」が発生する。OS側の検知メカニズムが働かないだけで、問題自体は消えていない。 重い処理の逃がし方:InteractionManager(旧API)とWorkletsは別物 RN側で「重い処理をUI/JSスレッドから逃がす」ための手段として、従来は InteractionManager がよく使われていたが、2026年8月リリースのReact Native 0.87で InteractionManager は削除済みであり、公式は代替として requestIdleCallback を案内している。0.82〜0.86の時点では非推奨警告が出るようになっていたため、既存プロジェクトでこの警告を見た人も多いはずだ。以降は「過去にどう動いていたか」の説明として読んでほしい。 ...

August 16, 2026 · 2 min

自宅NW活動誌:固定IP撤去とdnsmasq移行で名前解決を建て直した話

TL;DR 電源断からの復旧で名前解決がおかしくなり、調べていったら「dnsmasqがhosts変更を未反映」「gw-a/gw-bがローカル固定IPとdns01のDNS管理で二重管理になっていた」の2つが絡んでいたという話。ついでに「自ホスト名pingが127.0.1.1を返す」件は仕様通りで実害なしと確認した。全部片付いたので、今回は「詰んだ」話ではなく「ハマったけど直した」話。 環境 セグメント: home.local(192.168.20.0/24) DNS/DHCP: dns01(dnsmasq) Keepalived構成のGW: gw-a, gw-b クライアント側ネットワーク管理: nmcli 発生した問題 電源断→起動後、あるノード(client01)から複数ノードにSSHできなくなった。調査を進める過程で、そもそもdns01自身の名前解決(dns01という名前そのもの)が引けなくなっていることが判明。/etc/hostsに手動でエントリを足しても、他ノードから引けない状態が続いた。 原因は1つではなく、「gw-a/gw-bがローカル固定IP設定+hosts記載を持っていたこと」と「dns01側のhostsエントリ消失」が絡み合っていた。 対応1:dnsmasqがhosts変更を反映していなかった 症状: /etc/hostsにエントリを書いても、他ノードからdnsmasq経由で引けない。 原因: dnsmasqは/etc/hostsを起動時に読み込むだけで、実行中のファイル変更を自動では拾わない。dhcp-hostsfileやdhcp-optsfileなどのDHCP関連設定ファイルは変更時にSIGHUPシグナルを送ることでdnsmasqに再読み込みさせられる仕様になっており、/etc/hostsについても同様に、SIGHUPを受け取るとキャッシュをクリアした上で再読み込みする挙動になっている。つまり「編集しただけ」では反映されず、シグナルを送る操作が別途必要というのがハマりどころだった。 対処: systemctl restart dnsmasq(reload = SIGHUP相当)で即解決。 学び: /etc/hostsをdnsmasq向けに編集したら、反射的に再起動する習慣をつける。 対応2:gw-a/gw-bを固定IP運用からdhcp-host予約運用に切り替え 目的: ローカル固定IP(dhcpcd.conf/nmcli static)とdns01のDNS管理が二重管理になっていたのを解消し、IP管理をdns01(dnsmasq)側に一元化する。 手順(実施順が重要) 1. dns01側を先に設定 /etc/dnsmasq.conf(またはdhcphostsファイル)に予約を追記する。 dhcp-host=<MACアドレス>,192.168.20.3 # gw-a dhcp-host=<MACアドレス>,192.168.20.4 # gw-b MACアドレスの大文字/小文字はdnsmasqでは区別されない(AA:BB:...でもaa:bb:...でも可)。区切り文字は:固定。設定後はsystemctl restart dnsmasq。 2. クライアント側(gw-a/gw-b)を動的取得に変更 nmcli管理の場合は以下の通り。 nmcli connection show # 対象接続名を確認 nmcli connection modify "<接続名>" ipv4.method auto nmcli connection modify "<接続名>" ipv4.addresses "" nmcli connection modify "<接続名>" ipv4.gateway "" nmcli connection modify "<接続名>" ipv4.dns "" nmcli connection up "<接続名>" 確実に反映させるため最終的にreboot。 ...

July 30, 2026 · 1 min

Terraform学習1:variable/outputが分離しているのは何のためか

Terraformを触り始めると、variableとoutputが真逆の役割を持っていることはすぐわかる。だが「なぜこの2つが分離しているのか」「なぜvalidationが型チェックと別枠なのか」まで理解しないと、秘匿情報をうっかりstateに残す事故につながる。今回は自分のTerraform構成(variables.tf, outputs.tf, versions.tfのbackend部分)を題材に、設計意図を整理する。 variableは「設定とロジックの分離」のための入口 variableは、値をリソース定義本体に直書きせず、外部から注入するための仕組みだ。値を変えるたびにリソース定義そのものを編集しなくて済むようにする、という目的がある。 値の入力元は主に4つある。 default(ブロック内で指定) terraform.tfvars 環境変数(TF_VAR_xxx) -varオプション(CLI実行時) defaultの有無で意味が変わる 意味 defaultあり 共通・変更頻度が低い値。省略可能(例: aws_region, project_name) defaultなし 環境固有・必須の値。指定しないとplan/apply時にエラー(例: budget_alert_email, github_org) defaultの有無は「省略可能かどうか」の表明であり、そのままその変数の性格(共通設定か、環境固有の必須値か)を表している。 validationは型チェックとは別レイヤーの検証 variableのtypeは形式のチェックしかしない。type = stringは「文字列であること」しか保証せず、「その文字列が正しい値かどうか」は見ない。 そこを埋めるのがvalidationブロックだ。値の中身・意味をチェックする。例えばSSMパラメータ名は/始まりである必要がある、EBSルートボリュームは8GiB以上必要、といった業務ルールをここに書く。 variable "image_id" { type = string description = "The ID of the machine image (AMI) to use for the server." validation { condition = length(var.image_id) > 4 && substr(var.image_id, 0, 4) == "ami-" error_message = "The image_id value must be a valid AMI ID, starting with \"ami-\"." } } validationはapply前、ローカルのplan段階でエラーを弾ける。無駄なapply実行を未然に防げるのが最大の利点で、クラウド側にリクエストが飛ぶ前に「その値はそもそもおかしい」と教えてくれる。 ...

July 25, 2026 · 2 min

go-ts-modeでgofmt(goimports)の設定を修正した

TL;DR Go開発をgo-ts-mode(tree-sitterベース)に寄せたら、問題が発生していた。 1つ目: gofmt(goimportsで代替)を実行した直後は正しく整列されているのに、TABキーを1回押すとインデントが縮む。 2つ目: before-save-hookにgofmt-before-saveを登録しても、保存時に一切フォーマットされない。しかもエラーは出ない。 両方とも「go-mode.el(tree-sitter以前のパッケージ)がgo-ts-modeの存在を想定していない」ことが根っこの原因だった。 環境 Emacs、Go開発はgo-ts-mode(tree-sitterベース)を使用 OS: Arch Linux gofmt-commandは"goimports"に上書き設定(gofmtの代わりにgoimportsを使う) 対象ファイル例: internal/api/server.go(構造体フィールドやjsonタグを縦に整列させるコードスタイル) 試したこと 症状1: 手動でTABキーを押すとインデントが縮む 手動でM-x gofmtを実行して整形した直後のファイルは正しく整列されているが、既存行にカーソルを置いてTABキーを押すと、その行のインデントが浅くなる。 思い出したがこれはかなり前から存在してる不備だ。 ちょうどよかったのでここで直してみよう。 切り分けは以下の手順で進めた。 M-x describe-variable RET indent-tabs-mode RET → 値はt。タブを使う設定として正常。 対象行の文字をC-u C-x =で確認 → タブ文字であることを確認(Char: TAB)。 タブの表示幅を疑い、行頭でタブ1個分右に移動してからC-x = → column=8。tab-widthも8(Emacsのデフォルト)。表示幅そのものは正常。 「短くなった行」と「正常に見える上の行」でM-m(back-to-indentation)からのC-x =を比較 → 両方column=16で一致。インデント幅そのものは揃っていた。 ここで「タブ幅の表示問題ではない」と判明。 真因は次の通り。gofmtは構造体フィールドやjsonタグを縦に整列させるために、構文的に必要な数以上のタブを意図的に挿入する(整列目的の余分なタブ)。一方go-ts-modeはtree-sitterによる構文解析で「その行の構文的必要インデント深さ」だけを計算し、TABキー押下時に行頭の空白をその計算値に置き換える。gofmtが足した「整列用の余分なタブ」は構文的には不要なので、go-ts-modeがそれを認識せず削ってしまう。 これはEmacs設定のバグではなく仕様通りの動作だ。go-ts-modeには「整列のための余分な空白」を認識する機能がそもそもない。 調査の途中で、副原因ももう1つ見つかった。go-ts-mode-indent-offsetが4に設定されていた。 (use-package treesit :straight (:type built-in) :config (setq treesit-font-lock-level 4) (setq go-ts-mode-indent-offset 4)) go-ts-mode-indent-offset = 4だと「1インデントレベル=4列」で計算される。一方tab-widthはEmacsデフォルトの8のまま。gofmtの出力は「1インデントレベル=タブ1個(実質8列相当)」が前提なので、この不一致でTABキー押下時の再インデント計算が4列刻みでずれる。 対処はgo-ts-mode-indent-offsetをtab-widthと同じ値に揃えるだけ。 ;; 変更前 (setq go-ts-mode-indent-offset 4) ;; 変更後(効果確認済み) (setq go-ts-mode-indent-offset 8) これで症状は解消した。 ...

July 18, 2026 · 2 min

PostgreSQLのCTEが現場で少ない理由を実務経験から考える

はじめに バッチ処理で大量のデータ変換を行う際、PostgreSQLのCTE(Common Table Expression、WITH句)を多用していた時期がありました。複雑な変換処理を段階的に分割できて、コードの見通しも良くなる便利な機能です。 しかし、実際の現場でCTEを使っているコードは意外と少ない。サブクエリや一時テーブルが使われているケースの方が圧倒的に多い印象です。 この記事では、実務でCTEを使って感じた強み・弱みと、「なぜ現場ではCTEが少ないのか」を考察します。特にPostgreSQL 12で大きく改善された最適化の仕組みについても解説します。 CTEの基本おさらい CTEはWITH句を使って一時的な結果セットを定義し、メインクエリから参照できる機能です。 WITH regional_sales AS ( SELECT region, SUM(amount) AS total_sales FROM orders GROUP BY region ) SELECT region, total_sales FROM regional_sales WHERE total_sales > 10000; サブクエリと似ていますが、名前をつけて再利用できる点が特徴です。変数のように扱えて、複雑なクエリを段階的に構築できます。 CTEの強みと弱み 強み 1. 可読性・保守性の向上 ネストしたサブクエリ地獄を回避できます。処理を論理的なステップに分割して、各ステップに名前をつけられるため、コードレビューやメンテナンスが格段に楽になります。 2. 再帰クエリが書ける WITH RECURSIVEを使えば、階層構造(組織図、カテゴリツリー)を扱えます。これはCTE独自の強みで、サブクエリでは実現できません。 3. 複数箇所から参照できる 同じCTEを複数回参照できます(ただし最適化の観点で注意が必要、後述)。サブクエリだと同じ処理を重複して書く必要があります。 4. デバッグしやすい 各CTEを個別に実行して中間結果を確認できます。サブクエリだと抜き出して実行するのが面倒です。 5. 変換処理の分離 SELECT句での複雑な計算を先にCTEで処理しておけます。WHERE句で使いたいけど計算が複雑な場合に便利です。 弱み 1. 親クエリのパラメータを参照できない サブクエリなら外側の列を参照できる(相関サブクエリ)のに対し、CTEは独立しているため参照できません。 -- サブクエリなら可能 SELECT * FROM orders o WHERE amount > (SELECT AVG(amount) FROM orders WHERE region = o.region); -- CTEでは不可能(外側のo.regionを参照できない) 2. 大量データ・長時間処理には不向き メモリ上に保持されるため、巨大データだと辛い。一時テーブルならインデックスを作成したり統計情報を活用できます。 実際、バッチ処理で数百万行のデータを扱う際、CTEよりも一時テーブルの方がパフォーマンスが良いケースが多かったです。 3. PostgreSQL 11以前は「最適化バリア」になる これが最大の問題でした。次のセクションで詳しく解説します。 ...

February 12, 2026 · 3 min

React NativeのTodoアプリで実装する相対時間ベースのプリセット機能

はじめに Todoアプリを使っていると、毎日・毎週繰り返す定型タスクの登録が面倒に感じることはありませんか? 「毎朝のルーチン」「週次ミーティングの準備タスク」など、同じタスクセットを何度も手入力するのは非効率です。この記事では、相対時間を使ったプリセット機能の実装方法を紹介します。 実装したアプリのソースコード: https://github.com/your-repo (適宜修正してください) 問題:絶対時間で期限を保存すると使い回せない 一般的なTodoアプリでプリセット機能を実装する場合、以下のような設計になりがちです: // ❌ よくある実装(絶対時間) interface PresetTask { text: string; dueDate: Date; // 2026-02-08 09:00:00 } この設計の問題点: プリセット作成時の日時が保存される 翌日読み込むと「昨日の9時」が期限になってしまう 毎回手動で期限を修正する必要がある 解決策:相対時間(dueHoursOffset)で管理する 代わりに、「今から何時間後」という相対的な時間で期限を管理します: // ✅ 相対時間ベースの設計 export interface PresetTask { id: string; text: string; priority?: Priority; dueHoursOffset?: number; // 現在時刻からの相対時間(時間単位) checklist?: string[]; } export interface Preset { id: string; name: string; tasks: PresetTask[]; createdAt: Date; } 公式ドキュメント: date-fns addHours: https://date-fns.org/v4.1.0/docs/addHours 実装の全体像 1. プリセット作成時の実装 プリセット編集画面では、期限を「現在時刻から何時間後」として入力します: // screens/PresetEditScreen.tsx const TaskInputRow = ({ item, index, onTaskTextChange, onDueHoursOffsetChange, // ... }: { item: PresetTask; index: number; onTaskTextChange: (index: number, text: string) => void; onDueHoursOffsetChange: (index: number, value: string) => void; // ... }) => { return ( <Card style={styles.taskCard}> <View style={styles.taskInputRow}> <TextInput label={`タスク ${index + 1}`} value={item.text} onChangeText={text => onTaskTextChange(index, text)} mode="outlined" style={styles.taskTextInput} autoComplete="off" autoCorrect={false} /> <TextInput label="期限(時間)" value={item.dueHoursOffset?.toString() || ''} onChangeText={value => { // 数字以外を除去 const filteredValue = value.replace(/[^0-9]/g, ''); onDueHoursOffsetChange(index, filteredValue); }} keyboardType="numeric" mode="outlined" style={styles.dueOffsetInput} /> </View> {/* ... */} </Card> ); }; ポイント: ...

February 8, 2026 · 4 min

Emacsのdotfilesをモジュール化してメンテナンス性を向上させた話

背景 約900行に肥大化したmypackage.elを整理し、機能ごとにファイル分割してメンテナンス性を向上させるリファクタリングを実施した。 課題 単一ファイルの肥大化: mypackage.elが900行超えで見通しが悪い 機密情報の混在: API keyがコード内に散在 使っていない設定: コメントアウトされた設定が残存 パッケージの把握困難: 何を使っているか不明瞭 新しいディレクトリ構成 dotfiles/emacs/ ├── init.el # エントリーポイント ├── early-init.el # 起動高速化 ├── core/ │ ├── env.el # 環境変数・基本設定 │ ├── custom.el # UI基本設定 │ ├── keymap.el # キーバインド │ └── util.el # ユーティリティ関数 ├── packages/ │ ├── manager.el # straight.el設定 │ ├── core.el # 基盤パッケージ │ ├── completion.el # 補完系 (Vertico, Corfu) │ ├── search.el # 検索系 (Consult, Embark) │ ├── git.el # Magit等 │ ├── lsp.el # Eglot等 │ ├── languages.el # 言語別設定 │ ├── ai.el # GPTel, Ollama │ ├── writing.el # Denote, Org, Markdown │ ├── ui.el # テーマ、アイコン │ └── optional.el # たまに使うもの ├── templates/ # Tempelテンプレート └── docs/ └── README.md 重要な学び: require vs load 問題: requireでパッケージが読み込まれない 当初、init.elで(require 'completion)のように読み込んでいたが、以下の問題が発生: ...

February 6, 2026 · 3 min

三竦(さんすくみ)要件定義書

1. 概要 1.1 ゲームコンセプト 「三竦(さんすくみ)」は、犬・猿・雉の三すくみ関係を利用した追跡型対戦ゲーム。プレイヤーは召喚獣を配置して相手を攻撃しつつ、敵の召喚獣から逃げ切る戦略性とアクション性を兼ね備えたリアルタイムバトルゲーム。 1.2 コアメカニクス 三すくみ関係: 犬 → 猿 → 雉 → 犬 追跡システム: 召喚獣は相手プレイヤーを自動追跡 相性バトル: 有利な召喚獣は相手を一方的に倒す 戦略的配置: 召喚位置とタイミングが勝敗を分ける 1.3 開発目標 シンプル: ルールが3分で理解できる 完成優先: 1ヶ月以内にプレイアブル版完成 Android専用: Expo使用、まずCPU対戦のみ 2. ゲーム仕様 2.1 基本ルール 勝利条件 HP制: 各プレイヤーHP 3 制限時間: 3分 勝敗判定: 相手のHPを0にした方が勝ち 3分経過時、HP多い方が勝ち 同点の場合は引き分け ゲームフロー graph TD A[ゲーム開始] --> B[3分タイマー開始] B --> C{ゲーム中} C --> D[プレイヤー移動] C --> E[召喚獣配置] C --> F[召喚獣追跡] F --> G{当たり判定} G -->|当たった| H[HP-1] G -->|外れた| C H --> I{HP=0?} I -->|Yes| J[ゲーム終了] I -->|No| C C --> K{3分経過?} K -->|Yes| J K -->|No| C J --> L[リザルト表示] 2.2 召喚獣仕様 三すくみ関係 graph LR A[犬] -->|勝つ| B[猿] B -->|勝つ| C[雉] C -->|勝つ| A パラメータ表 召喚獣 速度 寿命 クールダウン 特性 犬 🐕 速い 10秒 10秒 素早く追跡、短命 猿 🐒 中速 10秒 10秒 バランス型 雉 🐦 遅い 10秒 10秒 遅いが長持ち 共通ルール: ...

February 6, 2026 · 6 min