d.sunnyone.org
sunnyone.org

ページ

2026-03-25

PHPerKaigi 2026 に行ってきた

PHPerKaigi、ほぼフル参加したのはひさしぶり(初めて?)。セッションでない目標を持って行ったのもあって、かなり密度の濃い3日間になった。

 以下、印象に残ったセッションと、できごとを並べる。

セッション:「コントロールの三分法」で考える「コト」への向き合い方 by 大橋 佑太

 内容というよりも、この話をしている場が印象深かったので、自分にとって学びがあり聞いておいてよかった。あと、ask the speakerのところで、O2さんから聞いた話が興味深かった。ざっくり、コトに向き合える条件ってあるよね、という話。
 

余談:zoeさんの教育の話

zoeさんに教育についての考え方を聞いた。これもPHPerKaigiに出向いた理由のひとつだったので、聞けてよかった。この問いを受けて、なんと主張をツイートに整理いただいたので、こちらから読むことができる。

https://x.com/for__3/status/2035740177419891078 

 

セッション:「接続」—パフォーマンスチューニングの最後の一手 〜点と点を結ぶ、その一瞬のために〜 by 武田 憲太郎

 いろんなタイプのコネクションの話。私は、ネットワーク・サーバー屋さんだった時代があるので、馴染みある〜という内容で、接続を軸に整理されているのは、なかなか見ないので良いセッションだったと思う。アプリケーションコード、しかもPHPのようなインフラ側を見ることが少なめな言語(コネクションプールのあたりでこの話もされてる)を書くエンジニアが、インフラを垣間見るにはとても良い内容だったと思う。
 
後の飲み会でこのセッションの意図を聞けたのもよかった。 
 

 セッション:PHPでTLSのプロトコルを実装してみる by ひがき

TLS1.3を実装してみましたって話なんだけど、こんな普遍的なTLS(TLS1.3)がなんもわからん、知識時代遅れじゃないか、という危機感を覚えさせてくれたという意味で印象深いセッションだった。これのおかげで速攻でTLS1.3についてキャッチアップすることができて、ありがたいきっかけになった。
 

セッション:モジュラモノリス導入から4年間の総括:アーキテクチャと組織の相互作用について by 川島慧 

 お話の内容そのものはさておき、このお話にある種の感想を持つことができる、というのはかつて通った道が部分部分あるからだろうな、と思い、歴史に思いを馳せることができたという意味でよかった。飲み会でCARTAの人たちと議論できたのもよかったなあと思う。
 

余談:koriymさんのアドベントカレンダーの話

このアドベントカレンダーの記事はフィクションだって書いてあるけど、どのくらいフィクションなのか?というのを聞きたくて、現実のお二方に会えるタイミングで実際どんなもんなのかを聞いてみた。これもPHPerKaigiに行った理由のひとつ。結論としては、本当にフィクションだったのだけど、そのついでに出てきた話が面白くて、自分にとって価値のある話だった。こういうのも、現地ならではだよなと思った。

---
 
登壇者のみなさん、スタッフのみなさん、お疲れ様でした。

2026-03-16

Metal Git を gtk4 に対応させた

地味に自分で使っているGitクライアント Metal Git https://github.com/sunnyone/metal-git をgtk4 (gtk4-rs) に対応させた。gtk3はMacでは日本語が入力できず、いちいちテキストをコピペして使っていた(なんならそれが面倒でvibe codingしてgitcomiっていうやっつけネーミングなGitコミットUIをElectronで作ったのを使っていた)ので、ようやくまともにコミットができるようになった(!)

 

 

ListViewの移行 

見た目はほぼ変わらないというか、あまり変わらなくなるようにがんばった。ListView の作りが全く新しくなっていたので、ここの移行がしんどいなあと思ってやっていなかったのだけど、ようやく変えることができた。

gtk3まではCellRendererと言って、標準のテキストあたりの表示と、Gdk(≒X)で好きな表示が描けるカスタムレンダラを組み合わせて作る形だったのだけど、gtk4では新しいListViewはwidgetを入れられるようになった。この影響で左側のツリーをカスタムレンダラで描いていたmetal-gitは移行が荷が重かったのだけど、やっと移行できた。

なかなかいないと思うけど、gtk3→4をもしやる人がいたらお気をつけください。

 

2025-04-05

PostgreSQLの制約トリガー+ビューを使ってテーブル跨ぎで整合性を保つ

RDBMSを使うと、UNIQUE制約やCHECK制約、排他制約などを利用して、指定した条件に沿う形で、データの整合性を保つことができる。しかし、「同じ電話番号を複数の顧客が使ってよいのは、同じ家族に所属している場合のみ」というような、制約の前提に他テーブルを使う(テーブルを跨ぐ)ケースは、よくでてくるわりに実現しにくい。この記事では、そのような制約をアプリケーションではなくPostgreSQLによって実現する方法を紹介する。

 TL;DR

 テーブル跨ぎの整合性を保つための手段として、制約トリガー(CONSTRAINT TRIGGER)がある。手順の例は以下の通り。

  1. 不整合を検出するビューを用意する
  2. ビューに基づいて制約違反を判定する関数を定義する
  3. 制約トリガーを定義し、更新・挿入・削除時に整合性を検証する

前提(ユースケースの概要) 

今回は以下のような顧客と家族の情報を例にする。

  • 顧客は電話番号を持っている(こともある)
  • 顧客は家族に所属している(こともある)
  • 電話番号は原則として別の顧客と重複してはならない
    • 同じ家族に所属する場合のみ重複することができる
  • 電話番号も家族もあとから変更される 
ER図は以下の通り。 

手順

今回使うサンプルはすべて以下に置いてあるので、適宜動かせる。

 1. 顧客・家族テーブルとインデックスの定義(準備)

CREATE TABLE customer (
    id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
    name TEXT NOT NULL
);

CREATE TABLE customer_phone (
    customer_id UUID PRIMARY KEY REFERENCES customer(id),
    phone_number TEXT
);

CREATE INDEX customer_phone_phone_number_index ON customer_phone (phone_number);

CREATE TABLE family (
    id UUID DEFAULT gen_random_uuid() PRIMARY KEY
);

CREATE TABLE family_member (
    family_id UUID NOT NULL REFERENCES family(id),
    customer_id UUID NOT NULL REFERENCES customer(id),
    PRIMARY KEY (family_id, customer_id)
);

CREATE INDEX family_member_family_id_index ON family_member (family_id);
CREATE INDEX family_member_customer_id_index ON family_member (customer_id);

 2. 不整合検出用ビューの作成

不整合となる行を抽出できるビューを準備する。ここでは、同じ電話番号を持ちながら、異なる family_id に所属している顧客ペアを抽出する。

CREATE OR REPLACE VIEW customer_phone_number_conflicts AS
WITH base AS (
  SELECT
    c.id AS customer_id,
    cp.phone_number,
    fm.family_id
  FROM customer c
  LEFT JOIN customer_phone cp ON c.id = cp.customer_id
  LEFT JOIN family_member fm ON c.id = fm.customer_id
),
pairs AS (
  SELECT
    t1.customer_id AS t1_customer_id,
    t2.customer_id AS t2_customer_id,
    t1.phone_number AS t1_phone_number,
    t2.phone_number AS t2_phone_number,
    t1.family_id AS t1_family_id,
    t2.family_id AS t2_family_id
  FROM base t1
  JOIN base t2 ON t1.customer_id <> t2.customer_id
)
SELECT *
FROM pairs
WHERE t1_phone_number IS NOT NULL
  AND t1_phone_number = t2_phone_number
  AND (
    t1_family_id IS NULL
    OR t2_family_id IS NULL
    OR t1_family_id <> t2_family_id
  );

CTEは使わなくても良いが、重複検知のような場合には、以下の3ステップにするとわかりやすい。

  1. 不整合検知に必要な情報を集める
  2. 1.の行の組み合わせを生成する
  3. 2.から不整合になっている行を探す

ビューの作成自体も必須ではないが、ビューを作成すると以下のようなメリットがある。

  • 整合・不整合の状態を宣言的に記述できる
  • トリガーは複数になるが、1つのビューを作ることで整合状態の知識を分散させずに済む(条件を変えやすい)
    • 実際、この記事を書いている間にcustomerとcustomer_phoneを分割したのだけど、かなり変えやすかった
  • 不整合なデータの確認が容易
  • 検証に使われるクエリのチューニング(実行計画の確認など)がしやすい

 3. ビューを使って整合性をチェックする関数の定義

作成したビューを参照し、行が現れたらRAISE EXCEPTIONする関数を作る。ビューのSELECT以外はほぼしないことがポイント。

顧客の更新時(電話番号変更時)

CREATE OR REPLACE FUNCTION check_customer_phone_number_conflict_on_update()
RETURNS TRIGGER AS $$
BEGIN
  IF EXISTS (
    SELECT 1 FROM customer_phone_number_conflicts
    WHERE t1_customer_id = NEW.customer_id
  ) THEN
    RAISE EXCEPTION 'Phone number conflict: cannot update customer phone';
  END IF;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

家族構成の変更時

CREATE OR REPLACE FUNCTION check_customer_phone_number_conflict_on_family_change()
RETURNS TRIGGER AS $$
DECLARE
  target_id UUID;
BEGIN
  IF TG_OP = 'DELETE' THEN
    target_id := OLD.customer_id;
  ELSE
    target_id := NEW.customer_id;
  END IF;

  IF EXISTS (
    SELECT 1 FROM customer_phone_number_conflicts
    WHERE t1_customer_id = target_id
  ) THEN
    RAISE EXCEPTION 'Phone number conflict: cannot change family';
  END IF;

  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

 4. 関数を制約トリガーとして登録

関数を制約トリガー(CONSTRAINT TRIGGER)として登録する。 このとき、DEFERRABLE INITIALLY DEFERREDにすると、トランザクション終了時に検証できる。

なお、顧客側、家族側どちらの変更も監視しなければいけないことに注意

-- 顧客の電話番号変更時
CREATE CONSTRAINT TRIGGER check_customer_phone_number_conflict_trigger
AFTER INSERT OR UPDATE OF phone_number ON customer_phone
DEFERRABLE INITIALLY DEFERRED
FOR EACH ROW
EXECUTE FUNCTION check_customer_phone_number_conflict_on_update();

-- 家族構成の変更時
CREATE CONSTRAINT TRIGGER check_customer_phone_number_conflict_on_family_trigger
AFTER INSERT OR UPDATE OR DELETE ON family_member
DEFERRABLE INITIALLY DEFERRED
FOR EACH ROW
EXECUTE FUNCTION check_customer_phone_number_conflict_on_family_change();

 

検出例

パターン1:同じ家族内で同じ番号(OK)

INSERT INTO family (id) VALUES ('00000000-0000-0000-0000-000000000001');

INSERT INTO customer (id, name) VALUES
  ('00000000-0000-0000-0000-000000000011', 'Alice'),
  ('00000000-0000-0000-0000-000000000012', 'Bob');

INSERT INTO family_member (family_id, customer_id) VALUES
  ('00000000-0000-0000-0000-000000000001', '00000000-0000-0000-0000-000000000011'),
  ('00000000-0000-0000-0000-000000000001', '00000000-0000-0000-0000-000000000012');

INSERT INTO customer_phone (customer_id, phone_number) VALUES
  ('00000000-0000-0000-0000-000000000011', '000-0000-0000'),
  ('00000000-0000-0000-0000-000000000012', '000-0000-0000');

 

パターン2:異なる家族で同じ番号(NG)

INSERT INTO family (id) VALUES ('00000000-0000-0000-0000-000000000002');

INSERT INTO customer (id, name) VALUES ('00000000-0000-0000-0000-000000000013', 'Charlie');

INSERT INTO family_member (family_id, customer_id) VALUES
  ('00000000-0000-0000-0000-000000000002', '00000000-0000-0000-0000-000000000013');

INSERT INTO customer_phone (customer_id, phone_number) VALUES
  ('00000000-0000-0000-0000-000000000013', '000-0000-0000');

INSERT 0 1
INSERT 0 1
INSERT 0 1
ERROR:  Phone number conflict: cannot update customer phone
CONTEXT:  PL/pgSQL function check_customer_phone_number_conflict_on_update() line 7 at RAISE

パターン3:未所属同士の重複(NG)

INSERT INTO customer (id, name) VALUES
  ('00000000-0000-0000-0000-000000000014', 'Dana'),
  ('00000000-0000-0000-0000-000000000015', 'Eve');

INSERT INTO customer_phone (customer_id, phone_number) VALUES
  ('00000000-0000-0000-0000-000000000014', '000-1111-1111'),
  ('00000000-0000-0000-0000-000000000015', '000-1111-1111');

INSERT 0 2
ERROR:  Phone number conflict: cannot update customer phone
CONTEXT:  PL/pgSQL function check_customer_phone_number_conflict_on_update() line 7 at RAISE

パターン4:電話番号が NULL の場合(OK)

INSERT INTO customer (id, name) VALUES
  ('00000000-0000-0000-0000-000000000016', 'Frank'),
  ('00000000-0000-0000-0000-000000000017', 'Grace');

INSERT INTO customer_phone (customer_id, phone_number) VALUES
  ('00000000-0000-0000-0000-000000000016', NULL),
  ('00000000-0000-0000-0000-000000000017', NULL);

パターン5:family_member を DELETE → 残った側が未所属になって重複(NG)

INSERT INTO family (id) VALUES ('00000000-0000-0000-0000-000000000005');

INSERT INTO customer (id, name) VALUES
  ('00000000-0000-0000-0000-000000000051', 'Alice'),
  ('00000000-0000-0000-0000-000000000052', 'Bob');

INSERT INTO family_member (family_id, customer_id) VALUES
  ('00000000-0000-0000-0000-000000000005', '00000000-0000-0000-0000-000000000051'),
  ('00000000-0000-0000-0000-000000000005', '00000000-0000-0000-0000-000000000052');

INSERT INTO customer_phone (customer_id, phone_number) VALUES
  ('00000000-0000-0000-0000-000000000051', '000-0000-0000'),
  ('00000000-0000-0000-0000-000000000052', '000-0000-0000');

-- Bob を家族から外す → NG(未所属で重複状態になる)
DELETE FROM family_member
WHERE customer_id = '00000000-0000-0000-0000-000000000052';

INSERT 0 1
INSERT 0 2
INSERT 0 2
INSERT 0 2
ERROR:  Phone number conflict: cannot change family
CONTEXT:  PL/pgSQL function check_customer_phone_number_conflict_on_family_change() line 15 at RAISE

パターン6:phone_number の UPDATE による重複(NG)

INSERT INTO family (id) VALUES ('00000000-0000-0000-0000-000000000003');
INSERT INTO customer (id, name) VALUES
  ('00000000-0000-0000-0000-000000000018', 'Hank');
INSERT INTO customer_phone (customer_id, phone_number) VALUES
  ('00000000-0000-0000-0000-000000000018', '000-9999-9999');
INSERT INTO family_member (family_id, customer_id) VALUES
  ('00000000-0000-0000-0000-000000000003', '00000000-0000-0000-0000-000000000018');

UPDATE customer_phone
SET phone_number = '000-0000-0000'
WHERE customer_id = '00000000-0000-0000-0000-000000000018';

INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
ERROR:  Phone number conflict: cannot update customer phone
CONTEXT:  PL/pgSQL function check_customer_phone_number_conflict_on_update() line 7 at RAISE

パターン7:トランザクション内で家族と番号を同時変更(OK)

BEGIN;
INSERT INTO customer (id, name) VALUES
  ('00000000-0000-0000-0000-000000000019', 'Ian'),
  ('00000000-0000-0000-0000-000000000020', 'Jack');

INSERT INTO customer_phone (customer_id, phone_number) VALUES
  ('00000000-0000-0000-0000-000000000019', '000-3333-3333'),
  ('00000000-0000-0000-0000-000000000020', '000-3333-3333');

INSERT INTO family (id) VALUES ('00000000-0000-0000-0000-000000000004');
INSERT INTO family_member (family_id, customer_id) VALUES
  ('00000000-0000-0000-0000-000000000004', '00000000-0000-0000-0000-000000000019'),
  ('00000000-0000-0000-0000-000000000004', '00000000-0000-0000-0000-000000000020');

COMMIT; 

 

MySQLのときは?

 ビューは問題なく、pl/pgsqlのところは書き方を変えれば良さそうだが、CONSTRAINT TRIGGERがないので工夫が必要そう。
 

他手法との比較

  • UNIQUE制約、排他制約やCHECK制約
    • 原則として他テーブルを参照できない
  • 外部キー制約
    • 互換性を気にしなくてよく、すべて宣言的に収まるので、困りごとが起きないのであれば、こちらが望ましい
      • 制約のために関連がいびつになったり、条件が柔軟だったり、変わるものだったりすると厳しい
    • この方法は、SQLパズル の「パズル16 主任とアシスタント―参照整合性制約の正しい設定」を参照。

まとめ

本記事では、テーブル跨ぎで整合性を保つ例として、電話番号を共有して良いのは所属している家族同士のみというケースを用いて、制約トリガーの使い方を説明した。
 
同様の条件付き制約が必要な場面において、この手順が参考になれば幸いである。

2025-01-19

linariaからPanda CSSに移行した

うちのサイトは今のところNext.jsで書いてあるのだけど、スタイリングにはlinariaを使っていた。次の作業のためにPanda CSSに置き換えたので、その記録を残しておく。

 Panda CSSの特徴

移行時のポイントの前に、気にしておくべき造りの特徴としていくつかあげておく。

object の形でのスタイル指定 

linariaではstyled.div`` やcss``の指示でCSS文字列を記述できるが、Panda CSSではjs/tsのオブジェクトの形で指定する。最近ではよくある形。

const appItemsDivStyle = css({
    display: 'grid',
    gridTemplateColumns: '1fr',
    gap: '15px',

    md: {
        gridTemplateColumns: '1fr 1fr',
    }
});

基本的にはCSSの構造そのものを書くが、Chakraのような記法を受け付けているところもある。ブレイクポイントやデザイントークンのあたり。このへんにどう乗るかは移行時に気にかけておくと良いと思う。

クラスが細かく分割される(Atomic CSSスタイル)

linariaでは基本的にはスタイル指定の部分はまとまったままで、クラス名が生成されて適用される。このようなイメージ。

.sxatkf1 {
  padding-bottom:5px;
  border-bottom:1px solid #012356;
  margin-bottom:20px;
  line-height:1.8;
  font-size:20px;
  font-weight:700
}

classへの指定はこんなかんじ。

<h2 class="mocked-styled-0 sxatkf1">

一方で、Panda CSSではもっと細かく分解される。適当に切り出すとこんな感じ。

    .md\:fs_16px {
      font-size: 16px;
}

    .md\:fs_24px {
      font-size: 24px;
}

    .md\:mt_30px {
      margin-top: 30px;
}

分解されたスタイルは、クラス名のほうで合体される。

<h2 class="pb_5px bd-b_1px_solid_{colors.accent} mb_20px lh_1.8 fs_20px fw_bold md:fs_24px">

今回は問題にならなかったが、classがひとつであることに依存したような造りにもしなっているのであれば注意が必要。

カスケードレイヤー (@layer) を使っている

Cascade Layers  を参照。Panda CSSのみのときはたいして気にならないが、組み合わせるときに注意が必要。

バンドラの外でTypeScript ASTからスタイルを抽出する

zero-runtime CSS-in-JSのひとつのやりかたとして、linariaのようにwebpackなんかのバンドラの処理に手を入れる形でスタイル出力の変換をする形があるが、Panda CSSはts-morphを使ってバンドラの外でTypeScript ASTをparseする形でスタイルを抽出・出力する。

これはviteだのturbopackだのの話に巻き込まれにくいというメリットがあるが、linariaとは異なるタイプの制限があるので、知っておく必要がある。詳しくはマニュアルのDynamic stylingを参照。

eslint pluginによる静的検証がある

公式にeslint pluginがある(@pandacss/eslint-plugin)ので、TypeScriptの型チェックが及ばない部分、例えば動的な変数や変なトークン名なんかはeslint pluginによって検証できる。

zero-runtime らしい制約をlintでチェックしてくれるのは助かる。

移行時のポイント

上記を踏まえつつ、移行時に役に立ったり、ひっかかったりしたポイントを書く。

記述はLLMでだいたい移行できた

linariaからpandacssへの移行をお願いするだけで、だいたい書き直してくれた。後述のkeyframeを移したり、tokenに書き換えたりするところはできなかったので、手動で対応した。

ChatGPTならgpt4o-miniでだいたい行けたし、ollama + phi-4でも書き換えられた(めちゃ遅いけど)。

今回はstyled指定よりcss指定のほうがよく、diffの都合その場のほうがよかったりした(classNameのほうに移そうとしてくれたりしなかったりする)ので、そのあたりは指示した。

変数参照は基本できない→tokenを使う

はまったところとして、importした変数を参照できないので、tokenに置き換える必要がある。Design Tokensを参照。

これは、eslint pluginのno-dynamic-styling (recommendedに入っている) でわかるので、潰していけば良い。

lintでなんとかなったが、panda debugも知っておくと良いかも。

resetを持っている 

pandaは自身でresetを持っている。

もともとが雑だったので、今回はpandaのresetに乗るようにしたが、 preflight オプションでオフにできそうなので、このあたりも見ると良さそう。

素のstyleはlayerに気をつける

もともとaのようにグローバルにスタイルをつけていたのだけど、移行したらそっちが勝ちすぎたので、その部分に@layerの指示を入れた。今回はCascading Layersの記述の通り、@layer resetを指定。

keyframesの定義はconfigに

keyframesはその場に定義できないので、configに移した。configに書く設定はいくつかあるので気をつける。

eslint設定はカスタマイズが必要そう

基本的には@pandacss/recommendedで良いと思うのだけど、今回は以下を足した。

    "@pandacss/no-hardcoded-color": "off",
    "@pandacss/no-unsafe-token-fn-usage": "off"

no-hardcoded-colorは移植の都合。no-unsafe-token-fn-usageは'{sizes.foo}'ってわざわざ書かなくても'foo'でいいところはそうしましょう、みたいなオプションなのだけど、{sizes.foo} みたいに書くとno-invalid-token-pathsが機能しやすいので無効にして{}やtoken()を積極的に使っていくことにした。

記述の妥当性のほかにも、好みの指定もできて、prefer-longhand-propertiesなんかは役に立ちそう。no-margin-propertiesをCSS-in-JSが持っているのはちょっと面白い。

おわりに

思ったより考えてあるなって思った。ひっかかるポイントはあるけど、もうちょっと使ってみようと思う。

2024-12-18

Web入門時に読んだデザインの本まとめ(復刻版)

今回は、下書きがお蔵入りになっていた、デザインを勉強するときに読んだ本のまとめがでてきたので、微調整して公開する。2014年頃にGUIアプリからWebにスイッチした際に書いたもの。基礎的な内容をメインに勉強したので、今も概ね有用と考えている。

以下からは、基本的に本文は2014年のもの、注や指定があるのは2024年のもの。勉強し始めの感想と、10年後の感想をあわせて読むと、モチベーション的に学びやすいかなと思いこのような形にする。

Webサイトなんかを作っていて、どうもデザインする部分に苦手意識があるというか、作業を進める上でネックになっている感じがしたのだけど、 「別に勉強すればいいんじゃね?」ということに気づき、デザインの本を読んで勉強してみた。そのときに読んだ本たちを一挙紹介。

自分の目標は先の通り、Webサイト/アプリを作ることだけども「Webに関する本はWebの実現方法が主体で、 見た目のデザインそのもののセオリーとかは紙のデザインの本が強くない?」と本屋さんで本を探してて思ったので、 紙ベースの書籍やフライヤーなどを作るためのデザインの本を主に読んだ。そのため、 「デザイン」と広く言っているが、プロダクトデザインやインテリアデザインみたいな話は申し訳ないが入っていない。

この記事の読み方

本の情報のほかに、私の主観で以下の内容を書いている。

  • おすすめ度
  • ジャンル: レイアウト、色彩、タイポグラフィ
  • アプリケーション: なし

ジャンルは、「この手のデザインはレイアウト、色彩、文字に分類される気がする」と思ったので、勝手に分類している。 エディトリアルデザイン的には、装丁の部分、印刷や紙についての取り扱いも知識の一ジャンルとしてある気がするが、私がフォーカスしようと思っていない部分なので、ジャンルには入れていない。

アプリケーションは、そのアプリケーションの使い方を取り扱っている書籍について、記憶に残ったものを書くことにした。べったりツールを使っていく書籍もあるが、若干触れるだけというのもあるので、 その本の雰囲気を表す一つの方向性として見てもらえるといいと思う。

日常の資料作りをサポートする、あらゆる人向けのデザイン書籍

エディトリアルデザインの入門書には、本や1枚ものを作ることを目指す本があるのは当然として、 日常的に作るものを題材にした、そもそも本だとかを作ることを目指さない本もある。 とっつきやすいものとして、こういった内容だったものをまずは紹介。

伝わるデザインの基本 よい資料を作るためのレイアウトのルール

2024リンク: 伝わるデザインの基本 増補改訂3版 よい資料を作るためのレイアウトのルール / 技術評論社
  • おすすめ度: ★★★
  • ジャンル: レイアウト / 色彩 / タイポグラフィ
  • アプリケーション: Microsoft Office

資料を作るあらゆる方面の人におすすめの書籍

デザインそのもので仕事をしていくような人たちではなく、むしろ文書、プレゼン資料やポスターのような日常的な資料を書く人たちが、伝わりやすいデザインを実現するための本。ほぼ後述のノンデザイナーズ・デザインブックのような内容なのだが、書体や題材が現在の日本人にとって馴染みのある内容で、 非常に頭に入ってきやすい。デザイナー向けの書籍と異なり、 ツールやフォントもそこにあるものを使って実現しましょう、という形になっているので、すぐ試せる。 「センスないから読みやすい資料作るの苦手だわー」と思っている人にぜひ読んで欲しい。

基本的には伝わるデザイン|研究発表のユニバーサルデザインを書籍化したような内容であるが、紙の話であり、紙のほうが読み進めやすいので本がおすすめ。

ただし、デザイナー向け書籍と比べるとあっさりと記述されているので、興味を持ったらこの後紹介しているような 本を読んでみるといいと思う。

(2024注) 流行がありそうな領域なのに、2014年発売から今でも支持されてるのすごい。

ノンデザイナーズ・デザインブック

2024リンク: ノンデザイナーズ・デザインブック / マイナビ出版
  • おすすめ度: ★★☆ (2024注: 基本★★★だけど日常資料レベルなら★★)
  • ジャンル: タイポグラフィ / レイアウト
  • アプリケーション: なし

非デザイナー向けデザイン書籍の定番

非デザイナーがデザインの話を勉強すると言ったら必ず出てくるであろうこれ。気にしない人がやってしまいがちな例を直しながら、 どういったものが読みやすく伝わりやすいのか、日常的な書類などの身近な例で解説していく。

確かに「コントラスト」「反復」「整列」「近接」という話が、非デザイナーにも伝わるようよく整理されていて読みやすい。 しかし、欧文主体でしかも作例も古い感じなので、ちょっと入ってきにくい。昔読んで、最近勉強したタイミングで また読んだのだけど、欧文の書体選びとか、後から読んだからこそ参考になった(頭に入った)感がある。 なので、読んでおく価値はあると思うけども、時間の限られたサラリーマンでも必読、という感じではないかな、という感じ。

(2024注) 改訂されているようなので、読みやすくなっているかもしれない。一度見てみると良いかも。いろいろ作ってみたあとの2024年のほうが、この本の価値を感じている。

デザイナーの勉強の書籍(基礎系)

実例がまずあってうんぬんというよりは、色は色相/明度/彩度があって、みたいな各構成要素ごとに説明していくようなスタイルで 説明されている書籍をこちらに並べている。

難しさは、先のものと同じくらいのものもあるが、安くない書体を使うことが前提だったり、印刷会社での印刷が前提だったりといった部分が 多く占めるものに関してはこちらに分類している。

タイポグラフィの基本ルール プロに学ぶ、一生枯れない永久不滅テクニック

2024リンク: タイポグラフィの基本ルール-プロに学ぶ、一生枯れない永久不滅テクニック / SB Creative
  • おすすめ度: ★★★
  • ジャンル: タイポグラフィ
  • アプリケーション: なし

文字組について易しく、しかしきっちりと解説している一冊

基本的には文字の取り扱い(書体選び、アキの話などなど)について、他のタイポグラフィの書籍同様に解説している本。 タイポグラフィの知識が全くなくても読めるレベルの読みやすさでありつつも、詳しい例を載せながらきっちりと解説している。 例えば、アキは適切に、てな感じでさらっと流す書籍も見受けられるが、本書では複数の例で、色付けしたりしてわかり 易くなるように説明されている。専門用語にはきちんとふりがなが振ってあるのもうれしい(「平体」とか)。 タイポグラフィを名乗っているが、紙面レイアウトについても触れられている。そのロジックならそれは単にやり方の一つだよね、というのを思う部分もあるわけではないが、 実例も多く読みやすいので、おすすめの一冊である。

配色&カラーデザイン プロに学ぶ一生枯れない永久不滅テクニック

2024リンク: 配色&カラーデザイン ~プロに学ぶ、一生枯れない永久不滅テクニック~ / SB Creative
  • おすすめ度: ★★★
  • ジャンル: 色彩
  • アプリケーション: なし

色の基礎知識と配色の実例について読みやすく説明。PCCSは知っておくべき

色彩調和や配色手法などの基礎知識と、Webサイトを並べて配色について説明した事例集。似たようなタイトルついてるしタイポグラフィのやつとシリーズなのかな? 色彩検定ででてくる話が一通り書いてあるのだけど、読むためのものなので問題集より読みやすいし、まぁ実例がWebサイトなので色彩検定ででてくるファッションとかインテリア/エクステリアの話より馴染みやすい。

PCCS(Practical Color Co-ordinate System:色相とトーンという二軸での色の表現)の使い方を、この本で知ったおかげで色を考えやすくなった。もっと「ちゃんと」表現できるCMYKやHSL(HSB)の状態でも、ある程度色をシステマチックに決めるための方法論を自分の中で持っていれば要らないのだろうけど、そうでなければPCCSとその使い方を知ると便利。ざっくり色を考える方式としては、自分の中で不滅だと思う。

(2024注) 2024年の今でも色を選ぶときのやりかたはこの本の発想がベースになっていて、それを便利にするために作ったpcolorsは今でも使っている。不滅の看板も伊達ではなく、とても助かっている。

デザイン解体新書

2024リンク: デザイン解体新書 / ワークスコーポレーション
  • おすすめ度: ★★★
  • ジャンル: レイアウト / 色彩 / タイポグラフィ
  • アプリケーション: InDesign / Quark Xpress / Illustrator

前提知識が必要だが、実例が非常に多く、実践的で読み応えがある一冊

実際に著者の方がデザインされた本などを題材にデザインについて解説していく一冊。 ある程度の知識があることは前提としており、いきなり読むとつまづく。 例えば「二分アキってなんだっけ?」といったように用語の方面だとか、「こっちのツメはいいけど、こっちはダメといわれても、どう違うのこれ?」という感じで、見慣れてないと違いがわからない可能性すらある(最初そうだった)。 しかし、「ああ、これが紙の世界のスタイルシートなんだ」と思うレベルに要素要素に詳しく書かれた指示と、それに対して、ただきれいだからこうしたという話ではなく、 ちゃんと理由づけて解説されているのが素晴らしい。

2006年なので、若干古さを感じなくもないが、それは置いてでも価値がある。エディトリアルデザインに興味があれば読むべき一冊。 ここに書かれている内容も、やり方の一つではあるのだと思うが、ここまで徹底解説しようとすると、そうならざるを得ない。 すでにWebデザインだとか、デザインの仕事をしていたとしても、書籍の編集に携わったことがないのであれば読んで損しないと思う。

(2024注) いまでもめっちゃ推し。違う頭があることがわかる貴重な本。

キチンと身につけたい人のためのデザインの総復習

2024リンク: キチンと身につけたい人のための デザインの総復習。 / MdN BOOKS
  • おすすめ度: ★★☆
  • ジャンル: レイアウト / 色彩 / タイポグラフィ
  • アプリケーション: InDesign / Illustrator

タイトル通り復習という感じの書籍

レイアウト、色、文字、断裁等について全般的に通して説明した本。同じように全般的に説明している「伝わるデザインの基本 よい資料を作るためのレイアウトのルール」が日常的に作成する資料にフォーカスしたのに対して、こちらは出版物を対象にすることでもう少し踏み込んだ感じになっている。フォントを自由に選びにくいといった文字に対する制約がなくなったので、特に文字についてノンデザイナー向け書籍より詳しい(Wordで1文字ずつ詰め具合を調整しろって言われても辛いものがあるものね)。紙面が前提だし、デザイナーの立場でこういうことになったらこうしてしのぐ的な話もあって、ワークフローを意識した感じ。エディトリアルデザインの分野に入っていくときに最初のころに読むとか、部分部分でやってた人が弱点を見つけるみたいなことを目指したであろうという感じの一冊。

標準DTPデザイン講座

2024リンク: 標準DTPデザイン講座 基礎編 / 翔泳社
  • おすすめ度: ★☆☆
  • ジャンル: TODO
  • アプリケーション: なし

ワークフローの勉強にはよさそう

DTPとはなんぞ、という本。こうしたらよりよいデザインになります、というよりは、こうやって進めていきます、こういうのを使います、という感じの本。 デザインとしてはあまりよくない例があるように思うので、読むのであれば、細かいことは気にせず大枠こんなか、という読み方をすればいいと思う。「CTPってなんだっけ?」とか「校正のフローはこんな感じ」か、みたいな知識的な側面で読むと参考になるとは思う。 なお、2004年と古いので、しょっちゅう変わるものの話についてはあまり役に立たない。PCのスペックとか。

TYPOGRAPHY 2014/05

  • おすすめ度: ★★☆
  • ジャンル: タイポグラフィ
  • アプリケーション: なし

文字が大好きな人たちが書いてることがわかる雑誌

読むほど文字が好きな人たちが書いてるんだろうなってことが伝わってくる。文字について学ぼうと思っていたときに、特集「楽しく学ぶ文字入門」という表示を見かけ読んでみた。この特集はよく書かれており、本当はこれを万人におすすめに入れようかと思ったが実作業的な側面では 「タイポグラフィの基本ルール」のほうが読みやすく、例も多いのでやめた。とはいえ「フォントの見分け方」など、作業とは別の観点ではあるが、 よくでてきそうな疑問について書かれている。何より文字に対する愛が伝わってくるのがよい。買って損はない号だと思う。

(2024注) 雑誌のものを今取り上げなくていいとも思ったのだけど、文字が好きな人がいるっていうのは自分のなかで新鮮な学びだったので、そう思ったときのテキストを残した。ピンポイントでこの本じゃなくても、そういう想いのある本を読んでみると、新しい世界が見れるかも。

デザインのルール、レイアウトのセオリー。

デザインのルール、レイアウトのセオリー。 / MdN Books
  • おすすめ度: ★★☆
  • ジャンル: レイアウト / 色彩 / タイポグラフィ
  • アプリケーション: なし

レイアウトの観点のまとめ

「対称性」「統一感」「メタファー」など、挙げる観点に対して、実際の例や作った例を出して、その価値について解説していく本。 読み進めて難しくはないので、レイアウトが生み出す価値を知っておくという意味で、先に読んでおく側に分類してもよかったのだけど、 実例ベースでの解説であるし、どのような構成要素があるか知っていないと、価値が薄れてしまうかなという意味で、応用編に分類した。 作っているときに、この観点で考えたかな、という意味でチェックポイントとして良いのではないかと思った。

Design Rule Index[第2版]― デザイン、新・25+100の法則

(2024注) 結構いい本だったと思うのだけど、タイトルしか下書きになかったのでタイトルだけ紹介。

その他デザイン関連書籍ピックアップ

上述のものより、ジャンルを絞ってピンポイントで書いているような本を取り上げる。3つのジャンル分けは広すぎるものもあるので、特にしない。

たのしいロゴづくり -文字の形からの着想と展開

2024リンク: たのしいロゴづくり / BNN
  • おすすめ度: ★★☆

タイトルの通り。文字からどうやってロゴにしていくか、そのアイデア集のような感じ。

Photoshop+Illustrator Design Technique

2024リンク: Photoshop + Illustrator Design Technique / 翔泳社
  • おすすめ度: ★☆☆

作例がいくつも載っていて、どうやってそれを作っていくか、というのを説明した本。ツールの使い方のチュートリアルというよりは、 どういったテクニックを使っていくか、というお話。

たのしいインフォグラフィック入門

2024リンク: たのしいインフォグラフィック入門 / BNN
  • おすすめ度: ★★☆

図解するとはどういうことか、どうやって進めていけばいいか、という本。

プロになるためのWebデザイン入門講座 実践で役立つPhotoshop&Illustrator徹底ガイド

2024リンク: プロになるためのWebデザイン入門講座 実践で役立つPhotoshop&Illustrator徹底ガイド / 技術評論社
  • おすすめ度: ★☆☆

Webデザインの用途でPhotoshopの使い方を説明した本。悪くはないと思うけども、Webの「PhotoshopでWebデザインをはじめよう! 」のほうが読みやすいかなーといったところ。

おわりに

当然の話だけど、こうして読んできても、すぐに美しいデザインができるようになったりはしない。 やっぱり経験だな、というのが感想。でも逆に言うと、天才の粋に行かなければ、経験なのかなと思う。環境という話も大きいと思うけども。

読んできて全く役に立たなかったかというと、そんなことはなく、いい悪いとはどういうことか、というのが読む前と比べるとかなり見えるようになったと思う。 そういった意味では、方向性が少しは見えるようになったという意味で、読んできて間違いなく正解だった。 実用性という意味でも、デザイナーの意図が汲み取りやすくなったので、作りやすくなったと感じている。

なお、これを見て「それ読むならこれ読んで!」てな本があれば教えてもらえれば読もうと思うので、おすすめがあればぜひ教えてください。


2024年に読んで

「やっぱり経験だな」というのは、実践で学ぶことが多いという意味で、それはそうと思うのだけど、この本たちを読んでいたおかげで助けられたという場面はかなりあった。この10年の間に、作り運用してきたプロダクトたちの中には自分が表層まで手掛けてきたものもいくつかあるのだけど、使いやすいという評判も頂いている認識で、それはこのときの学習の影響が多分にあると思う。10年前に勉強しておけばよかったなと思うことはよくあるけど、これは10年前に勉強してよかったと思える類だなと、当時の10年後から振り返って思った。

(ブログのデザインがボロボロなことにも気づいてしまったのでなんとかしたい)

2024-11-25

exiftoolとpolarsでよく撮っている焦点距離を調べる

スマホを買い替えて以来、カメラで撮るよりスマホのほうが撮りやすいところあるなあと思っていたのだけど、いまさら焦点距離の微妙な違いが影響しているのではないかと気づいたので、カメラ購入以来控えてきたレンズ購入を検討することにした。キットレンズで行けるところまで行こうと思っていたがついにこのときが来てしまった。

カメラのレンズはSONY FE 28-60mm F4-5.6なので、28mmスタート。スマホはPixel 8 Proで1xが24mmらしい。先の困り具合から24mmは必要ということがわかる。しかし、24mmで十分かはわからない。単焦点で良いのか?ズームのほうが良いのか?そこで実際に撮っている写真からどのあたりをよく使うのか調べてみることにした。

ステップ1: exiftoolで写真ファイル群から焦点距離を収集する

写真のファイルに付与されたEXIF情報にレンズや焦点距離は記録されている。このEXIF情報を取得するツールは様々あるが、exiftoolを使うとコマンドラインで取得することができる。

コマンド例は以下のとおり。

$ exiftool -LensId -FocalLength -csv -ext jpg 20230103-浅草
SourceFile,LensID,FocalLength
20230103-浅草/DSC04803.JPG,Sony FE 28-60mm F4-5.6,35.0 mm
20230103-浅草/DSC04799.JPG,Sony FE 28-60mm F4-5.6,28.0 mm
20230103-浅草/DSC04818.JPG,Sony FE 28-60mm F4-5.6,28.0 mm
20230103-浅草/DSC04814.JPG,Sony FE 28-60mm F4-5.6,35.0 mm

exiftoolはファイルだけでなくディレクトリも引数に撮ることができ、-ext jpgとすると拡張子の絞り込みまでできる(ARWとどっちかで良いので)。さらに、-csvでCSV出力にできるし、-LensIdと-FocalLengthのようにフィールドを限定することもできる。

1ファイルを指定するとすべての情報を出すくらいの機能しかないと思っていて、シェルスクリプトでも書いて回すかと思っていたのだが、コマンド一発で済んでしまった。

ステップ2: polarsで集計する

sqliteに入れてもよかったんだけど、Polarsというものが使われているらしいのでせっかくなので試してみた。PandasみたいなノリだけどSQLも受け付けるらしい。

インストールはpip install --user polarsで入れた。

$ ipython3
Python 3.10.12 (main, Nov  6 2024, 20:22:13) [GCC 11.4.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.31.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import polars as pl

In [2]: pl.Config(tbl_rows=10000)
Out[2]: <polars.config.Config at 0x7e5fd1f90400>

In [3]: df = pl.read_csv('/tmp/summary.csv')

In [4]: df.sql("""
   ...: WITH s AS (SELECT LensID,FocalLength,count(*) AS count FROM self GROUP BY LensID,FocalLength ORDER BY FocalLength),
   ...: a AS (SELECT count(*) as total FROM self)
   ...: SELECT s.LensID, s.FocalLength, s.count::float * 100 / a.total AS percent FROM s CROSS JOIN a
   ...: """)
Out[4]: 
shape: (33, 3)
┌────────────────────────┬─────────────┬───────────┐
│ LensID                 ┆ FocalLength ┆ percent   │
│ ---                    ┆ ---         ┆ ---       │
│ str                    ┆ str         ┆ f64       │
╞════════════════════════╪═════════════╪═══════════╡
│ Sony FE 28-60mm F4-5.6 ┆ 28.0 mm     ┆ 59.369925 │
│ Sony FE 28-60mm F4-5.6 ┆ 29.0 mm     ┆ 3.150373  │
│ Sony FE 28-60mm F4-5.6 ┆ 30.0 mm     ┆ 2.143553  │
│ Sony FE 28-60mm F4-5.6 ┆ 31.0 mm     ┆ 2.273465  │
│ Sony FE 28-60mm F4-5.6 ┆ 32.0 mm     ┆ 2.208509  │
│ Sony FE 28-60mm F4-5.6 ┆ 33.0 mm     ┆ 1.52647   │
│ Sony FE 28-60mm F4-5.6 ┆ 34.0 mm     ┆ 1.721338  │
│ Sony FE 28-60mm F4-5.6 ┆ 35.0 mm     ┆ 2.53329   │
│ Sony FE 28-60mm F4-5.6 ┆ 36.0 mm     ┆ 2.143553  │
│ Sony FE 28-60mm F4-5.6 ┆ 37.0 mm     ┆ 1.85125   │
│ Sony FE 28-60mm F4-5.6 ┆ 38.0 mm     ┆ 2.208509  │
│ Sony FE 28-60mm F4-5.6 ┆ 39.0 mm     ┆ 1.753816  │
│ Sony FE 28-60mm F4-5.6 ┆ 40.0 mm     ┆ 1.623904  │
│ Sony FE 28-60mm F4-5.6 ┆ 41.0 mm     ┆ 0.779474  │
│ Sony FE 28-60mm F4-5.6 ┆ 42.0 mm     ┆ 0.974342  │
│ Sony FE 28-60mm F4-5.6 ┆ 43.0 mm     ┆ 0.779474  │
│ Sony FE 28-60mm F4-5.6 ┆ 44.0 mm     ┆ 0.876908  │
│ Sony FE 28-60mm F4-5.6 ┆ 45.0 mm     ┆ 0.84443   │
│ Sony FE 28-60mm F4-5.6 ┆ 46.0 mm     ┆ 0.552127  │
│ Sony FE 28-60mm F4-5.6 ┆ 47.0 mm     ┆ 0.389737  │
│ Sony FE 28-60mm F4-5.6 ┆ 48.0 mm     ┆ 0.129912  │
│ Sony FE 28-60mm F4-5.6 ┆ 49.0 mm     ┆ 0.584605  │
│ Sony FE 28-60mm F4-5.6 ┆ 50.0 mm     ┆ 0.422215  │
│ Sony FE 28-60mm F4-5.6 ┆ 51.0 mm     ┆ 0.032478  │
│ Sony FE 28-60mm F4-5.6 ┆ 52.0 mm     ┆ 0.324781  │
│ Sony FE 28-60mm F4-5.6 ┆ 53.0 mm     ┆ 0.16239   │
│ Sony FE 28-60mm F4-5.6 ┆ 54.0 mm     ┆ 0.519649  │
│ Sony FE 28-60mm F4-5.6 ┆ 55.0 mm     ┆ 0.16239   │
│ Sony FE 28-60mm F4-5.6 ┆ 56.0 mm     ┆ 0.032478  │
│ Sony FE 28-60mm F4-5.6 ┆ 57.0 mm     ┆ 0.16239   │
│ Sony FE 28-60mm F4-5.6 ┆ 58.0 mm     ┆ 0.129912  │
│ Sony FE 28-60mm F4-5.6 ┆ 59.0 mm     ┆ 0.584605  │
│ Sony FE 28-60mm F4-5.6 ┆ 60.0 mm     ┆ 7.047743  │
└────────────────────────┴─────────────┴───────────┘

サブクエリで怒られたのとFROMにカンマ区切りにしたら怒られたから書き換えたけど、それ以外は自然に書けた。CTEも使えるしすごい。

端以外はサマってみたいので、polarsのやりかたも使ってみる。

In [80]: s2 = s.filter(pl.col("FocalLength").is_in(["28.0 mm", "60.0 mm"]).not_())

In [81]: s3 = s2.select(pl.col("percent"), pl.col("FocalLength").str.extract(r"^(\d)", 1).alias("T"))

In [82]: s3.group_by("T").agg(pl.col("percent").sum())
Out[82]: 
shape: (4, 2)
┌─────┬───────────┐
│ T   ┆ percent   │
│ --- ┆ ---       │
│ str ┆ f64       │
╞═════╪═══════════╡
│ 3   ┆ 20.363754 │
│ 5   ┆ 2.53329   │
│ 2   ┆ 3.150373  │
│ 4   ┆ 7.534914  │
└─────┴───────────┘

filter()とかselect()にはpl.col("name")を使って何かする。これはpolars.Exprpolars.Expr.strを見ればいいっぽい。 

結果を見て

集計した結果以下がわかった。

  • 28mmが約60%。めちゃ使う。
  • 29mm〜40mmはそれぞれ1%以上ある。まあまあ使うらしい。
  • 41mm〜59mmは1%未満。あまり使わないみたい。
    • 30mm台をまとめて20%, 40mm台は7.5%
  • 60mmは約7%。
24mm単焦点でも困らないかもしれないが、40mmまであると便利そうというところか。この結果を見つつ検討したい。

2023-12-19

例でわかるTypeScript Utility Types

これはTypeScript Advent Calendar 202319日目の記事です。例とともにUtility Typesの紹介をします。

Utility Typesとは

Utility Typesは、型から型を生成するためのTypeScript組み込みの型です。ライブラリを書いているような方ならば普段から使っていると思いますが、Utility Typesが何かわからない方もPartial<Foo>やRecord<string, string>なんかは見たことがあるのではないでしょうか。

どんなものがあるか?というのはドキュメントを見てもらえれば良いので、 詳細はそちらに譲りますが、今日はどういうときに便利なのか?という視点でUtility Typesの主要なものを分類しつついくつか紹介します。

TypeScriptが提供する型: 作るもの

Record<Keys, Type>

Recordは、Keysをキーとするプロパティを持ち、そのプロパティの値の型はTypeであるという型を生成する型です。これは主にデータを格納するためのオブジェクトの型を定義するときに使います。

type FooRecord = Record<"prop1" | "prop2", string>;
// → { prop1: string, prop2: string }

上記のようなケースだとベタに定義したほうが良いのでありがたみはないですが、keyの集合がすでに定義されている場合は便利です。

interface BarData {
    prop1: string;
    prop2: number;
    prop3: Date;
}

type BarDataErrors = Record<keyof BarData, string>
//  → { prop1: string, prop2: string, prop3: string}
stringのように広い型を指定することもできます。 
type StringRecord = Record<string, string>;

もともとRecordは

type Record<K extends keyof any, T> = {
    [P in K]: T;
};

と単純なのでこれを書いてもいいのですが、意図を明示できる上に簡潔に記述することができます。

TypeScriptが提供する型: 主にプロパティを加工するためのもの

Pick<Type, Keys> / Omit<Type, Keys>

PickはTypeのプロパティをKeysに絞った型を作ります。OmitはTypeのプロパティからKeysを外した型を作ります。ホワイトリストとブラックリストですね。

これらの使い方は幅広いのですが、既存の関数のラッパーを作るときに便利です。

例えば、ButtonというReactコンポーネントがあり、propとしてsizeを取るとき、このsizeを“large”に固定したLargeButtonというコンポーネントを作りたいようなケースを考えます。このときにLargeButtonのpropsの型をButtonからsizeプロパティを外したものとして作ると、Buttonと同じだがsizeは渡せないということを明確にすることができます。

type LargeButtonProps = Omit<ButtonProps, "size">;

function LargeButton(props: LargeButtonProps) {
    return <Button {...props} size="large" />
}

Extract<Type, Union> / Exclude<UnionType, ExcludedMembers>

ExtractはTypeからUnionに代入可能なものを取り出した型を作り、ExcludeはUnionTypeからExcludedMembersに代入可能なものを外した型を作ります。

これは、discriminated unionとあわせて使います。例えば、type: “success”のときは正常、type: “error”のときはエラーであるResultオブジェクトを考えます。

interface ResultSuccess {
    type: "success";
    data: Record<string, string>;
}

interface ResultError {
    type: "error";
    errorMessage: string;
}

// 既存ライブラリが合わさっている型だけexportしていたり
export type Result = ResultSuccess | ResultError;

このような場合に、ExtractやExcludeを使うと限定した型を得ることができます。

type SuccessType = Extract<Result, {type: "success"}>
// ResultSuccess
type NonSuccessType = Exclude<Result, {type: "success"}>
// ResultError

Partial<Type> / Required<Type>

Partialはプロパティを任意にし、Requiredはプロパティを必須にします。

Partialは例えばプロパティを部分的に受け取ってオブジェクトを更新するような関数を作る場合に使えます。

function updateFoo(foo: Foo, fields: Partial<Foo>) {
    return {...foo, ...fields };
}

もともとすべてのプロパティがrequiredとは限らないので、Partialと比べるとRequiredを素で使うケースは少ないと思いますが、他との組み合わせ、例えばPickで明確にして使う、ということもできます。

interface Foo {
    name?: string;
    age?: number;
    description?: string;
}

type Bar = Required<Pick<Foo, "name" | "age">>;

NonNullable

NonNullableはTypeからnullとundefinedを外します。

type NullableType = string | null;
type NonNullType = NonNullable<NullableType>;
// string

まあこれはわかりやすいですよね。

TypeScriptが提供する型: 関数に関するもの

関数の型に対して何かをする型は、汎用的に関数を取ってそれをどうにかする関数には不可欠なものですが、具体的な関数がわかっている場合にも以下のような使い方をすることができます。

ReturnType

ReturnTypeは関数の型から戻り値の型を得ます。

ReturnTypeは、関数はわかっているが型にアクセスできない、または定義されていないようなとき、例えばfactory的な関数でその型の明示がないようなケースのときに、その値を中継するなどのことをするために型が欲しいときに便利です。

function createFoo() {
    return {
        a: "abc",
        b: 123
    };
}

type CreateFooReturn = ReturnType<typeof createFoo>;
// { a: string, b: number }

Parameters

Parametersは、関数の引数の型を得られます。ラッパー関数を作るような場合に、既存の関数の型をそのままに引数を足したいなどのときに便利です。

function foo(a: string|number) {
    return a;
}

type FooParameters = Parameters<typeof foo>;
function wrappedFoo(a: FooParameters[0], b: string) {
    other(b);
    return foo(a);
}

Awaited

asyncな関数だとreturn typeがPromiseになりますが、そのままでは扱いにくいです。 そんなときにAwaitedを使うと剥がすことができます。

interface Foo {
    a: string;
    b?: number;
}

export async function createFoo(): Promise<Foo> {
    return {
        a: "bar",
        b: 123
    };
}

///

type RequiredFoo = Required<Awaited<ReturnType<typeof createFoo>>>;
// {a: string, b: number}

自分で作る

Utility TypesもTypeScriptで定義された型でしかないので、このような型は自分でも作ることができます。

例えば、部分的に任意にするPartialPartiallyというものを作ると以下のようになります。

// すでに定義されているHoge
interface Hoge {
    name: string;
    description: string;
    startAt: Date;
    finishedAt: Date;
}

type PartialPartially<T, K extends keyof T> = Partial<Pick<T, K>> & Pick<T, keyof Omit<T, K>>;
// 終わってない(finishしていない)かもしれない型を作る
type HogeMayUnfinished = PartialPartial<Hoge, "finishedAt">;

このように、型の操作の仕方と、型の操作そのものを分離しておくと、あとから見たときの悩み度合いが減って良いです。

おわりに

使わないに越したこともないものもありますが、どうしようもないときは知ってるか知らないかで型の扱いの難易度が変わってきます。

Utility Typeを使って、型操作を楽にしていきましょう。

2023-03-27

PHPerKaigi 2023に参加してきました

YAPC::Kyoto 2023を見て、PHPの世界をまた見てみるのいいかもと思ってPHPerKaigi 2023に行ってきました。結果、今のPHPを知れたという意味でもよかったし、ああいう雰囲気の場所が戻ってきたんだというのをまた体感できたという意味でもよかったです。スタッフの皆様、参加者の皆様お疲れ様でした。

以下、セッションのメモです。

---

勉強になったセッション

勉強になったのはこの「PHPの配列の内部実装について学びたくなった。

PHP4のときにガラケー向けアプリケーションを書いていて、arrayの性能に悩まされたことがあり、あのarrayはなあと思っていたのだけど、今更ではあるもののPHP7で真の配列が導入されたことをここで知れたのでよかった。

印象に残ったセッション

印象に残ったのは「名付けできない画面を作ってはならない - 名前を付けるとは何か

設計の話をするセッションだと思っていたのだけど、もうちょっと広く見ていてなるほどなと思った。気持ちの面も結構大事な話だと思う。

2023-03-21

YAPC::Kyoto 2023に参加してきました

京都観光も兼ねて、YAPC::Kyoto 2023に参加してきました。面白かった。遠征でのカンファレンスの参加は人生初なので、そういう意味でも新鮮な体験だった。

印象に残ったセッション

みんな面白かったのだけど、印象に残ったのはmacopyさんのデプロイ今昔物語だった。


よくわからないホスティング環境を借りてPHP(確か4だったと思う)のプログラムを「ホームページ」に置いてみたりとか、読めないPerlを書いて自分で困ったりしていた時代を思い出して、原点に帰った気分になれてとてもよかった。

遠征での学び

日曜日にYAPCで、京都にせっかく行くのだから土曜日は観光しようと決めていて、土曜日に観光するなら朝から行けたほうがいいよねということで金曜日に行って泊まり、日曜日遅くなるかもしれないから月曜日に帰ろうということで、結果3泊4日になった。歩数計を見ると40km近く歩いていることになっているので、結構満喫したと思う。

旅程に合わせてホテルを変えたので、服は洗濯しちゃえば運ぶの楽じゃないか?と思ってチャレンジしてみたものの、旅から帰ってきたあとコインランドリーに行き来して洗濯するのは結構しんどいというのは学びだった。洗濯と乾燥が一緒ならいいのにな~

荷物まわりはuzullaさんがテクニックをまとめてくれているので、今度参考にして楽をしたい。

俺的!遠方カンファレンスの参加体験向上テク 2023最新版 - uzullaがブログ https://uzulla.hateblo.jp/entry/2023/03/21/132421


2022-12-31

自室作業環境再構築(2022)

もともとリモートワーク的に作業する環境はあったものの、机にガタが来たのをきっかけに見直しをして作り直したので、その環境のメモ。もう数ヶ月前なのでちょっと情報が古かったりするのだけど、部屋の環境を作るときの参考になれば。

家具

デスク: FlexiSpot E8 (+ 120cm 天板)

まずはデスクから。今までのデスクはキーボードの棚を引き出して使って、上にプリンタ棚があるいわゆるPCデスクという感じ。地震でキーボードの棚が引き出せなくなったことをきっかけにサイズを測ってみたら幅100cm/奥行き50cmしかなくてなるほどこれは狭いなと。

いっそデスクは新調しようということで調べてみると、高さが調整できたほうが良さそうだなというところには来て、電動昇降をありにするか迷っていたのだけど、使っている人にヒアリングしてみると、ずっとそこで仕事するなら切り替えになって良いという話を聞いたので、まあ机はケチるところではないし買ってみようかと思い電動昇降つきを選択。

機種としては、昇降デスクの定番らしいFlexiSpotのメインストリーム系であろうE8を選択。120cm天板/ウォールナット色をセットで追加。

実際に使ってみると、新しい体験で面白かった。スタンディングであまり作業してはないのだけど、細かい調整が気軽にできたり、配線なんかのときに持ち上げて作業できるのは嬉しい。どの程度活用されていくかは未知数だけど、らしい使い方を探していくことを含めて楽しそうだと思う。

ちょっとがっかりだったのがウォールナットの色がだいぶ赤っぽかったこと。現物を確認できなかったので、仕方がないけど、暗めの茶色にしたかったので、セットだと他の選択肢はなかった。結局電動ユニットやコントローラは木ねじで止めるので板はなんでもよく、こだわるならかなでものというところの天板を合わせるのがテンプレみたい。サンプルを注文できるみたいだし、選ぶ手間とコスト感が合えばそのほうがいいかも。ホームセンターで板を買ってきてDIYする人もいるらしい。

あとは幅について。100cmから120cmになってだいぶよくなったが、まだちょっと狭い。140cmを置けないことはなかったから140cmにしたほうが良かったかなと思いつつ、今の環境だと無理やり感出るからこれでもよかったかな、みたいな、サイズに関してはちょっとモヤっとする感じになってはいる。奥行きについては10cm伸びたのと、モニターアームをつけたこともあって結構いい感じにはなった。

PCワゴン: 30cm✕60cmで天板が昇降するやつ

PCは台車に乗せてデスクの下に置こうと思ったのだけど、120cmの幅だとだいぶ狭くなってしまう。仕方がないので横に置くことにしたのだけど、PCの上が棚になっていれば実質デスクを幅広く使えるのでは?ということで、天板を昇降できるPCワゴンをAmazonの森をかき分けて探してきた。結果座っている側の高さにあわせるといい感じにデスクを延長できている。

下に入れる場合でもPCワゴンいろいろあるみたいだから、フィットするのを探してみるのもいいかも? 

ケーブルトレー: クランプ式/ワイヤータイプの40cm✕2セットのもの

デスクが昇降するので、卓上↔床の配線はなるべく少なくしたい。となると、天板側になんとかしてケーブルを持ってくる必要がある。そのためにケーブルトレーというものがあって、形がいろいろあるのだけど、こだわりがないので、Amazonの森をかき分けて2個セットなのにセールで1.5kというものを見つけたので購入。

手軽そうだったのでクランプ式を選んだのだけど、どうせネジ止めしているので、ネジ式でも良いとは思う。ただ、モニターアームのクランプと干渉しない位置に設置する必要があるので、そのときに融通が効きやすいものを選択するのが良いとは思う。

椅子:REC-128AX

20年前くらいに買った手動昇降つきでメッシュ背面なオフィスチェア。いいやつがあれば置き換えるかもしれない。

長く使っているから座面の合皮がやぶれてはがれてしまったのだけど、合皮補修シートを使って直したらいい感じに戻った。

タイルカーペット: サンゲツ スタイルキットループ

昔から使ってたニトリの安いパズルマットと比べると圧倒的に良くて、ずれないし質感も良い。素直におすすめできる。

定価は40x40で800円/枚くらいだけど、楽天で500円/枚くらいのところがあった。サンプルも送ってもらえるので、試してみると良いかも。

デスクの寿命から派生した環境再構築のPCまわり編。デスクが変わったので、PCまわりもいろいろ不便だった部分を直した。

PC周り

モニタ: I-O DATA LCD-GCWQ341XDB

モニタは34インチ ウルトラワイドモニタ(UWQHD: 3440x1440)を選択。これは正解だった。

モニタは6年くらい前に買った27インチ4Kのモニタ(LG 27UD58P-B)と15インチ4Kのモバイルディスプレイを使っていたのだけど、27インチ 4K はdot by dotで使うと163ppiと、21インチ フルHDの105ppiと比べると圧倒的に細かくてスケーリングしないと使えない。

現実的に使おうとするとWQHD (2560x1440) 相当が109ppiなので、150%スケーリングしたいところなのだが、XOrg環境はまだ浮動少数スケーリングが得意じゃないので、200%でフォントサイズを調整して使っていた。

フルHD (1920x1080)x3の環境に慣れているので、なるべく広く取りたいのだけど、4Kも 200% にするとフルHDになってしまって、全く広くなくなっていた。

24インチ WQHD(2560x1440)を2つ並べるか?いや122ppiは細かすぎる、WUXGA (1920x1200)を2つしかないか?などと考えていたところ、34インチ UWQHD(3440x1440)の存在に気づき、選択した結果ちょうどいい感じになった。

湾曲でないほうがいいかなと思ったのだけど、使ってみてるとそんな困らない気もする。経験したので、今選ぶならまた違った選択肢になりそう。

サブモニタ: EVICIV 15.6インチ モバイルディスプレイ 4K

前に買ったやつを34インチモニタの上につけている。チャットアプリやターミナルを置いたりしているが、必要なときに見上げて使うのはちょっと辛いのでなんとかしたいところ。

モニターアーム(メイン用): エルゴトロンLX

悩むくらいならこれ選んでおけという感じで出てくるが、実際使ってみて納得。 強いし、調整もしやすい。あまり動かさないからこれでなくてもいいのではとも思うけど、わざわざ他を選ぶほどのことでもない印象。

モニターアーム(サブ用): Bracwiser PCモニターアーム + エルゴトロン クイックリリースブラケット

モバイルディスプレイ用のアーム。重いものではないから安いやつでいいものの、メインモニタの上に配置するから高さがあるものということで選んだ。ただ、モバイルディスプレイはヘッドレスなPCのメンテ用でもあるからモニタ側に取り外し可能にするブラケットをつけて外せるようにした。

スピーカー: EDIFIER R19U2

今まで使っていたスピーカーは置きにくくケーブルの取り回しが大変で、もうちょっと音がいいやつが欲しいなと思ったのでリーズナブルで便利そうなやつを買った。4000円くらいなので音はすごくいいわけではないけど、この値段なら納得できるし、見た目も悪くない。 Amazonを見たら一緒に比較検討してたJBL Pebblesが6264円なので、たいていはこっちでいいと思う。音質はこっちのほうが良い。ただ、スピーカー本体にボリュームノブがついてないので、その意味ではR19Uのほうが便利。

マイク: マランツ M4U

昔から使っているもの。マイク〜って形をしている。 3000円くらいのくせに、もろもろの付属なんかに比べるとずっと音質が良いのでおすすめ。

マイクスタンド: anctarc マイクスタンド

USB Type-C 充電器: Baseus PD 充電器 65W 6-IN-1 1250W 電源タップ

机の上にUSB PDの口とAC 100Vの口を出すために買った。サイズ感、配置の安定感なんかがよくてこれは正解。65Wで十分かどうかは注意だけど、足りないなら100Vの口から増やすのもあり。

USBハブ: ORICO USB3.0 ハブ 4ポート

これはハズレ。クランプで止めるのはいいけど、やっぱりバスパワーはつらそう。デバイスの認識がぱたぱたする。

工具

電動ドリル: makita DF033DZ

FlexiSpotは電動ドリルで木ねじを締めることを期待しているので、電動ドリルを買った。makitaの掃除機を持っていて、そこで10.8Vのバッテリーを使っているので、それに合わせてmakitaの10.8Vバッテリーを使う電動ドリルを買った。ご家庭にmakita色のものが増えるのは不思議な感じだけど、安心感がある。

makitaの工具的には14.4Vバッテリーが今の主力のようで、バッテリーの前提がなければそこから選ぶのが良いと思う。3kくらいの安いやつを選んでしまっても良い気もするけど。

今までの机を解体するときに便利かなと思って買った六角ドリルピットがほかでも使えてすごく便利だった。併せて買うと良いかも。

作業

デスクまわりほぼ一式作ったのでだいぶ疲れた。本当は既存環境も一応維持してBlue/Green的に構築しようと思ったのだけど、作業エリアのことを考えてなくて、結局復帰までPCまわりが使えなくなってしまった。

一気に注文したわけではなく、順番に頼んでいったのだけど、出来上がっていくのを見ながら軌道修正できたのでこれは正解だった。例えばPCワゴンなんか最初の設計にはなかった。建築にアジャイル要素は難しいよなと思っていたのだけど、活きる部分もあるなと思った。翌日到着するいまの通販インフラさまさまだけど。

参考

 設計にあたりリモートワークとスタンディングデスクや周辺事情 - tech.guitarrapc.cómには大いに参考になった。感謝。

 

2022-11-03

PulseAudioのデバイス接続時自動切替とデフォルト入出力の設定

Ubuntuを再起動すると入力デバイスが変わって音が拾えなくなる問題が起きていたのだけど、デバイス接続時にPulseAudioがデフォルトデバイスを自動切り替えする機能を切ればよくなりそうなので、無効にする方法をメモしておく(この環境では結果的にUSBオーディオデバイスがいくつもぶら下がっている形になっているので、認識順やつないでいる状態で順番が変わったりするのだと思う。バスパワーのハブにつながったカメラが怪しい)

PulseAudio 自動切り替えの無効化

自動切り替えを無効化するには、/etc/pulse/default.pa のmodule-switch-on-connectの記述をコメントアウトする。

### Use hot-plugged devices like Bluetooth or USB automatically (LP: #1702794)
##.ifexists module-switch-on-connect.so
##load-module module-switch-on-connect
##.endif 

デフォルト入出力デバイスの設定

これの前はデフォルトの設定を変える方法を試していたので、それも併せてメモしておく。

1. デバイスのnameを探す

汎用的には、pacmd list-sources / pacmd list-sinks でデバイスのname:を探す。
$ pacmd list-sources
(略)
  * index: 8
        name: <alsa_input.usb-C-Media_Electronics_Inc._MARANTZ_M4U_20190520-00.mono-fallback>
(略)

ここで出てきたname: を後で使う。アスタリスクは今デフォルトに選ばれているもの。

GNOMEの設定の「サウンド」で選択していれば、pactl get-default-source / get-default-sinkでも得られる。

$ pactl get-default-source
alsa_input.usb-C-Media_Electronics_Inc._MARANTZ_M4U_20190520-00.mono-fallback

2. 設定ファイルに記述する

/etc/pulse/default.pa.d/00-default-device.conf に以下のように記述する。
set-default-source alsa_input.usb-C-Media_Electronics_Inc._MARANTZ_M4U_20190520-00.mono-fallback
set-default-sink alsa_output.usb-Resonessence_Labs_Resonessence_Labs_HERUS-00.iec958-stereo
なお、pactlコマンドでも変えることができるので、シェルスクリプトに書くのも可。
$ pactl set-default-source alsa_input.usb-C-Media_Electronics_Inc._MARANTZ_M4U_20190520-00.mono-fallback

参考

安定のArchWiki。

PulseAudio/サンプル - ArchWiki

2021-12-17

React Transition Group と class の状態

 React Transition Group CSSTransition はtransitionのためのclassをelementに付与してくれるのだけど、使う機会があるたびにどういう状態があるのか思い出してる気がするので、図にしておくことにした。



2021-09-22

Linux/Windowsデュアルブート環境のインストール方針

自分のWindows PCはLinux (Ubuntu) をインストールしてデュアルブートにすることが多い。 端末のタイプによってインストールの仕方が異なり、このやりかたが参考になるかもしれないので書いておく。
  1. 空のディスクが複数ある場合→1台ずつWindows, Linuxとインストール
  2. 空のディスクが1つのみの場合→Windowsをインストール後空いたところにLinuxをインストール
  3. Windowsが入ったディスクがある場合→パーティションを縮小してLinuxをインストール

空のディスクが複数ある場合

複数のディスクがPCに搭載できる/したい場合、それぞれにインストールする。まずWindowsをひとつのディスクにインストールしてから、別のディスクにLinuxをインストールする。 一番無難なかたち。

この場合では、データの受け渡しをするためのNTFSパーティションをLinux側のディスクにちょろっと用意しておくと、Windows側のCドライブをマウントしなくてよくて便利だったりする (NTFSマウントはなんだかんだあることがあるので)

空のディスクが1つのみの場合

ディスクが1つのみの場合、Windowsインストール時に容量を指定して(例えば半分)パーティションを作成し、インストール後、 空いた領域にLinuxをインストールする。 手元にあるPCがこのパターンで、こんな構成。

ディスク /dev/nvme0n1: 931.53 GiB, 1000204886016 バイト, 1953525168 セクタ
Disk model: WDS100T1X0E-00AFY0                      
単位: セクタ (1 * 512 = 512 バイト)
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O サイズ (最小 / 推奨): 512 バイト / 512 バイト
ディスクラベルのタイプ: gpt
ディスク識別子: B1EA5819-908C-4E4F-8542-CCBD4314BXXX

デバイス        開始位置   最後から    セクタ サイズ タイプ
/dev/nvme0n1p1      2048     206847    204800   100M EFI システム
/dev/nvme0n1p2    206848     239615     32768    16M Microsoft 予約領域
/dev/nvme0n1p3    239616  818331110 818091495 390.1G Microsoft 基本データ
/dev/nvme0n1p4 818331648  819437567   1105920   540M Windows リカバリ環境
/dev/nvme0n1p5 819437568 1600688127 781250560 372.5G Linux ファイルシステム

この順番の理由は、Windowsが作る予約領域をきちんと作成したいというのと、ブートローダ(grub)を持つのはLinuxになるため。

Windowsが入ったディスクがある場合

ノートPCなど、最初からWindowsが入ったディスクの場合、まず回復ディスクを作成し、 その後Windowsのディスクの管理からボリュームを縮小、空のディスクが1つのときと同様に空いた領域にLinuxをインストールする。

2021-08-22

Ubuntu + SoftFM でFMステレオを受信する

RTL2832U + R820T2 のDVB-Tチューナを使うと、FMラジオが聞けるらしいということで買ってあったのだけど、久々にFMラジオが聴きたい機会がでてきたのでやってみた。 RTL-SDR についているrtl_fmというツールを使う話はよくあるのだけど、FMステレオ非対応なので、今回はSoftFMというツールを使ってみた。

インストール

依存パッケージをインストールする。
$ apt install build-essential cmake libasound2-dev librtlsdr-dev
/lib/udev/rules.d/60-librtlsdr0.rulesによると、デバイスを差し込むとplugdevというグループで使えるようになるようなので、 ユーザーをplugdevグループに追加する。
$ sudo usermod -aG plugdev foouser
SoftFMを https://github.com/jorisvr/SoftFM からgit cloneしてビルド
$ git clone https://github.com/jorisvr/SoftFM
$ cd SoftFM
$ mkdir build
$ cd build
$ cmake ..
$ make
$ sudo make install

使い方

softfm -f [freq]で受信できる。TOKYO FMならこんなかんじ。
$ softfm -f 80000000
softfm -f 79950000のほうが音質が良かったりするので試してみたほうがいいかも。-Wでファイルに吐けるが時間指定はなさそうなので、指定したければこんなかんじ。
$ timeout 60 softfm -f 80000000 -W tokyofm.wav

2021-07-04

Ryzen 5 5600X でPCを組んだ

速いメインPCが欲しくなったのでPCを組んだ。

構成は以下の通り。ハイエンドって言うほどじゃないと思うんだけど、こんな高いPCを組んだのは初めて。

M/BASRock B550M-ITX/ac
CPUAMD Ryzen 5 5600X (6core/12thread)
メモリCrucial CT2K16G4DF8832A (UDIMM DDR4-3200 16GBx2)
GPUELSA GeForce RTX 3070 S.A.C
SSDWD BLACK SN850 NVMe SSD 1TB (PCIe Gen4 NVMe SSD)
ケースCooler Master MasterBox NR200 Black
電源Cooler Master V750 SFX Gold MPY7501SFHAGVJP
ケースファンCooler Master MasterFan SF120M x2
CPUクーラーNoctua NH-U9S

sysbenchの出す値が昔と変わってしまったので比較できないのだけど、やっぱり速い。コンパイル速度はDeskMini A300のときの倍くらい。 CPUだけじゃなくて、SSDがPCIe Gen4になったというのも大きそうだけど。

 ワットモニター読みで、回したときに250Wくらい。省エネPCばかり見てるとびっくりする数字。

2021-05-11

<List.Item> スタイルのコンポーネントの定義

Reactでリストのコンポーネントを定義するとき、liの持たせ方だったりアイテムのネーミングだったりで作りにちょっと悩んだりするのだが、HogeList / HogeList.Itemの形にするとあまり考えなくてよくなるので便利。

 参考: React で <Stack.Item> みたいな作り方の使い所を考える

ShopifyのものはReact.VFCを使っていないので、使ったもののメモを残しておく。

定義するところ

使うところ

2021-04-14

css-loader の camelCase オプションの行方

微妙に違うオプション名が出てくることがあるので調べたらこんな感じらしい。 また調べなくて良いようにメモ。

バージョン名前備考
v4.0.0 modules.exportLocalsConvention README / Release
v3.0.0 localsConvention README / Release
(v2.1.1とv3.0.0の間) exportLocalsStyle README / commit ※1
v1.0.0 camelCase README / Release
それ以前 camelcase
※1: 「exportLocalsStyle」は気にしなくてもよいはずだが、v3.0.0のBREAKING CHANGESで唐突に「exportLocalsStyle option was remove in favor localsConvention option」と出てくるので「camelCase」とつながらない

2021-03-31

node/browser環境での重複確認用ハッシュ文字列生成メモ

emotionのdata-属性で使っているような値が欲しかった。 emotionは@emotion/hashとして持っているものの、内部用かなぁと思うのでmurmurhashを使ってみた。 TextEncoderを使っているので環境によりpolyfillが必要。
import murmurhash from "murmurhash";

function hashString(s) {
    return murmurhash.v3(s).toString(36);
}

console.log(hashString("hfogfffe")); // 6bmwh9
参考

2021-02-12

Loqui 0.7.0 リリース

Gtk3に対応したLoqui 0.7.0をリリースしました。 

 https://github.com/sunnyone/loqui/releases 

微妙に見た目が違うのですが、まずは動くのが大事だと思ったのでリリースしました。何かおかしかったら教えてください。 

レポジトリはLaunchPadからGitHubに移動しました。本当はActionsでビルドできるようにしようかなと思ったのですが、結構手間がかかりそうなのでとりあえずパス。 

gtk2から3の変更点はここに書いている通りで、deprecatedを許容すれば、基本的には機械的に変えていけばいいものばかりなんですが、CSSでスタイリングするようになったためなのか、サイズを変えてるような処理のところがちょっと形を変えないといけないかなという感じです。


2020-12-21

tmuxで複数の開発サーバーを一括で起動する

Next.jsを複数動かす必要があったり、SPAをwatchしつつバックエンドも起動しておく、みたいな開発環境の場合、ひとつひとつ立ち上げるのは手間なので、tmuxで一括起動するスクリプトを作った。バックエンドは左にでかく、フロント達は右に、みたいなのは考慮せず、縦に並べるだけ。