Panda Noir

JavaScript の限界を究めるブログでした。最近はいろんな分野を幅広めに書いてます。

which-key.nvimで覚えづらいキーバインドにアイコンを設定 & グルーピングする

アイコンを付け、操作内容でグルーピングするようにした。個人的にはこれ入れてから結構コマンドを打ちやすくなった。

実際の画面

<c-w>

z

g

設定

return {
  {
    'folke/which-key.nvim',
    opts = {
      sort = {
        -- descの先頭についている[xxx]を元にソート
        function(item)
          local group = item.desc:match('^%[(.*)%]')
          return group and '0:' .. group or '1:'
        end,
        'local', 'order', 'group', 'alphanum', 'mod',
      },
      spec = {
        -- ウィンドウ関連のキーマップ
        { '<c-w>h', desc = '[focus] Go to the left window', icon = '󰜱', },
        { '<c-w>j', desc = '[focus] Go to the down window', icon = '󰜮', },
        { '<c-w>k', desc = '[focus] Go to the up window', icon = '󰜷', },
        { '<c-w>l', desc = '[focus] Go to the right window', icon = '󰜴', },
        { '<c-w>w', desc = '[focus] Switch windows', icon = '', },
        { '<c-w>H', desc = '[move] Move window to far left', icon = { icon = '', color = 'green' }, },
        { '<c-w>J', desc = '[move] Move window to far bottom', icon = { icon = '', color = 'green' }, },
        { '<c-w>K', desc = '[move] Move window to far top', icon = { icon = '', color = 'green' }, },
        { '<c-w>L', desc = '[move] Move window to far right', icon = { icon = '', color = 'green' }, },
        { '<c-w>s', desc = '[split] Split window', icon = { icon = '󰤻', color = 'yellow' }, },
        { '<c-w>v', desc = '[split] Split window vertically', icon = { icon = '󰤼', color = 'yellow' }, },
        { '<c-w>q', desc = '[close] Quit a window', icon = { icon = '󰅖', color = 'red' }, },
        { '<c-w>o', desc = '[close] Close all other windows', icon = { icon = '󰅗', color = 'red' }, },
        { '<c-w>+', desc = '[size-h] Increase height', icon = { icon = '󰡏', color = 'purple' }, },
        { '<c-w>-', desc = '[size-h] Decrease height', icon = { icon = '󰡍', color = 'purple' }, },
        { '<c-w>_', desc = '[size-h] Max out the height', icon = { icon = '', color = 'purple' }, },
        { '<c-w>>', desc = '[size-w] Increase width', icon = { icon = '󰡎', color = 'purple' }, },
        { '<c-w><', desc = '[size-w] Decrease width', icon = { icon = '󰡌', color = 'purple' }, },
        { '<c-w>|', desc = '[size-w] Max out the width', icon = { icon = '', color = 'purple' }, },
        { '<c-w>T', desc = 'Break out into a new tab', icon = '󰓩', },
        { '<c-w>x', desc = 'Swap current with next', icon = '󰓡', },
        { '<c-w>d', desc = 'Show diagnostics under the cursor', icon = '', },
        { '<c-w>=', desc = 'Equally high and wide', icon = '', },

        -- z系
        { 'za', desc = '[fold] Toggle fold under cursor', icon = { icon = '', color = 'yellow' } },
        { 'zA', desc = '[fold] Toggle all folds under cursor', icon = { icon = '', color = 'yellow' } },
        { 'zi', desc = '[fold] Toggle folding', icon = { icon = '', color = 'yellow' } },
        { 'zc', desc = '[fold] Close fold under cursor', icon = { icon = '󰘕', color = 'red' } },
        { 'zo', desc = '[fold] Open fold under cursor', icon = { icon = '󰘖', color = 'cyan' } },
        { 'zd', desc = '[fold] Delete fold under cursor', icon = { icon = '󰁮', color = 'gray' } },
        { 'zf', desc = '[fold] Create fold', icon = { icon = '󰐕', color = 'gray' } },
        { 'zC', desc = '[fold] Close all folds under cursor', icon = { icon = '󰘕', color = 'red' } },
        { 'zO', desc = '[fold] Open all folds under cursor', icon = { icon = '󰘖', color = 'cyan' } },
        { 'zD', desc = '[fold] Delete all folds under cursor', icon = { icon = '󰁮', color = 'gray' } },
        { 'zE', desc = '[fold] Delete all folds in file', icon = { icon = '󰁮', color = 'gray' } },
        { 'zR', desc = '[foldlevel] Open all folds (set foldlevel=max)', icon = { icon = '󰘖', color = 'cyan' } },
        { 'zM', desc = '[foldlevel] Close all folds (set foldlevel=0)', icon = { icon = '󰘕', color = 'red' } },
        { 'zr', desc = '[foldlevel] Fold less (foldlevel +1)', icon = { icon = '󰝡', color = 'cyan' } },
        { 'zm', desc = '[foldlevel] Fold more (foldlevel -1)', icon = { icon = '󰝠', color = 'red' } },
        { 'zx', desc = '[foldlevel] Reapply folds (reset to foldlevel)', icon = { icon = '󰑐', color = 'yellow' } },
        { 'zb', desc = '[cursor] Bottom this line', icon = { icon = '󰝓', color = 'purple' } },
        { 'zt', desc = '[cursor] Top this line', icon = { icon = '󰝕', color = 'purple' } },
        { 'zz', desc = '[cursor] Center this line', icon = { icon = '󰝔', color = 'purple' } },

        -- g系
        { 'ga', icon = '󰊄', group = '[case] converting text case' },
        { 'gd', icon = '󰅬', desc = '[lsp] go to definition' },
        { 'gg', icon = '󰘀', desc = 'First line' },
        { 'gO', icon = '󰅬', desc = '[lsp] Lists all symbols in current buffer in location-list' },
        { 'gp', icon = '󰅬', desc = '[lsp] peek definition' },
        { 'gr', icon = '󰅬', group = '[lsp] LSP' },
        { 'gt', icon = '󰅬', desc = '[lsp] go to type definition' },
        { 'gu', icon = '󰊄', desc = '[case] Lowercase' },
        { 'gU', icon = '󰊄', desc = '[case] Uppercase' },
        { 'gv', icon = '󰒉', desc = 'Last visual selection' },
      },
    },
  },
}

forEachの使い方1選

forEachって要らなくない?と思ってたんだけど使い道あるなと気づいたので紹介

nullableなリストに対してループしたい時に使える

nullableList?.forEach(item => {})

これはfor-ofで書こうと思うとちょっとダルい

if(nullableList != null)
  for (const item of nullableList) {}

// あるいは
for (const item of nullableList ?? []) {}

どっちも微妙だ。こういう時はforEachを使うと綺麗に書ける

デスク環境2026

去年のもの→ デスク環境2025 - Panda Noir

デスク全景

デスク全景

横のとこ

ディスプレイ上の小物

デスク周辺機器

  • デスク: FLEXISPOT E8B
  • キーボード: Moonlander
  • マイク: blue Yeti X
  • ウェブカメラ: Elgato Facecam
  • ディスプレイ: ASUS 4Kモニター PA329CV

機器関連は全く変わってない。 大体満足してる。でも、重量的に結構厳しくなってきているので、デスクの新調とモニターアーム導入はやりたくなってきている。今年中に結婚して引っ越しするぞという決意なので、そのタイミングでやりたい。

new: 巻取式充電ケーブル

巻取式充電ケーブル

2025年は巻き取り式の充電ケーブルをデスク下に設置した。すこぶる便利。デスクの上に充電ケーブルが散らばらず、ストレスフリー。

使ってるのは TORRAS FlexLine 67W。固定にはホームセンターで適当に買ってきた金具とゴム材を使ってる。金具をネジ止めして、ゴム材でずれないようにしている感じ。

ただ、これ買った直後に知った Anker Nano Charging Stationほぼ同じ値段でより多機能だった。これはデスク下に括り付ける運用できないけど、デスク上に設置するスペースあるならこっちのほうが良いと思う。

new: ディスプレイボード

ディスプレイボードに載せた小物

サンワダイレクト ディスプレイボードを買って、モニター上収納を増やした。細々とした小物を全部ここにまとめた。当初は文房具を置くつもりだったんだけど、いざ置くと結構不安定な感じがする+視界に入ってやかましかったのでやめた。

new: 電源タップ

有孔ボード裏の電源タップ

(これ一昨年からやってたかもしれないけど書いてなかったので紹介)

有孔ボードの裏にWindows機と電源タップを置いてある。これも結構便利。電源タップがここにあると、ちょっとだけ使いたい機器をサクッと接続できる。 扇風機の電源とかここから引いてた。最近は暖房機器を繋いだりしてる。

まとめ

2025年もそこまで劇的な変化はなく、細かいところがアップデートされた感じ。

今年は 窓なし部屋でも「朝」は作れる。カメラマン×エンジニアが教える『光のデスク設計術』 を読んで自分でもやってみたくなったので、照明周りを整えていきたい。

2025年を振り返る

去年の → 2024年を振り返る - Panda Noir

仕事、趣味、プライベートについて書くぞい。

仕事

  • LINEリニューアルチームに居たんだが、LINEリニューアルが仕切り直しとなったのでまた別のしごとをすることに。
  • 転職活動がつらかった
  • 新しい職場楽しい

記事は1月に書いた1本が300likeを超えた。あと、zennのトータルいいね数が2000を超えた。

関数の多重下請けをやめよう。単一責任の原則と関数の"責任"について

転職活動は、今年1月から本格的に選考を受け始めた。カジュアル面談は去年末から受けたりしてたけど。選考はかなりメンタルをやられて不眠出てきたり、生活リズムぼろぼろになってとても辛かった……最終的には内定がたくさん出て安心したけど、前半は1個も内定出なくてかなり絶望した。転職下手くそ人間。

新しい会社は結構すぐに馴染めてよかった。入社前に一回メンバーとオフラインで顔合わせする機会があったのも大きそう。仕事内容も楽しいしメンバーも自分に似た考え方の人が多いから働きやすい。頑張るぞい。

趣味

  • 前半はとにかく転職で手一杯になっててなにもできてなかった
  • 新しい趣味はそんなに増えてない。強いて言えば1枚刃の髭剃り

合気道をついに完全にやめた。1月から道場に行ってない。地元に戻ったし。

今年は「月1回初挑戦をしよう」という抱負を立てていて、それは達成できた。

  • 1月 ロボットペットの見学会に行く
  • 2月 ヘッドマッサージを受ける
  • 3月 クッキー作りに挑戦する
  • 4月 ラテアートしてみる
  • 5月 瓦割りをする
  • 6月 陶芸する
  • 7月 写経する
  • 8月 競馬場行ってみる
  • 9月 デジタルデトックスしてみる、バッティングセンター行く
  • 10月 垢擦りを受ける、1枚刃の剃刀で髭剃りをする
  • 11月 ヒップホップに入門する
  • 12月 ブレイクダンスに入門する

この中だと剃刀での髭剃りが普通に趣味になった。あとヒップホップを聴こうで聴いたアルバムはずっと聴いてる

プライベート

  • 地元にUターンした
    • 今年一番でかいトピック
    • 仕事部屋を借りたり車買ったり移住支援金を申請したりとか色々やった
  • 婚活
    • 戻って2週間で付き合い始めて1ヶ月で振られた
    • そのあとマッチングアプリで1ヶ月で8人と会ったけどダメだった
    • 12月から相談所を開始。そこそこ順調な滑り出しを見せているので、半年以内には結婚できそう

Uターン、思ってたより全然良かった。車があると新潟良いな。運転も結構すぐにできるようになったし。

婚活についてはあとで個別で記事を出す予定だけど、やってみるとマッチングアプリとはやっぱ性質が結構異なるなと感じる。一言で言えば、外部から付けられる制約が大きく異なってるので、ルールの異なる競技って感じ。

目標の振り返り

  • 外に出る機会を増やす、アウトドアの趣味を増やす
  • 婚活ある程度推し進める
  • 転職を真面目にやる
  • アウトプットを増やす

転職は成功した。婚活は相談所に入ったし佳境かな。あと半年を目処になんとかしたい。 外に出る機会はそこそこ作った気がする。月1回は初挑戦をしようってことで色々やってたし。アウトドアの趣味は増えてないが…アウトプットは記事はできたな。登壇は頑張りたい(新しい職場はそういうの結構活発だし一緒になんかしたい)

来年の目標

ラストマンシップをもつということ の中に出てきた「自分に関係ないことでも、「最後は自分がやるかも」と考える」という部分が全くできていなかったな、と反省したので頑張っていきたい。自分ができる範囲のことだけやるっていう姿勢ではダメだ。仕事の中でラストマンシップを持ちつつチャレンジしていく年にしたい。

useRefの初期値を1回だけ計算したい

useStateは初期値に関数を受け取ると、その関数を初期化時に呼び出してそれ以降は初期化処理を行いません。

const [todos, setTodos] = useState(createInitialTodos); // 初期化コストが高い

対してuseRefは関数を受け取って初期化するという機能がありません。

const todos = useRef(createInitialTodos); // ただの関数のrefという扱いになってしまう

かといって毎回初期値を生成するのは無駄が大きいです。これを回避する方法を思いついたので紹介です

解決策: 初期値をuseMemoで作る

const todos = useRef(useMemo(() => createInitialTodos(), [])); // createInitialTodosは1回のみ呼び出される

このパターンであれば「useMemoはパフォーマンス向上させるときにのみ使うべき」という原則とも合致しています。