OmniAICreator/Qiita-1.07M huggingface dataset:
JavaScriptにおける関数設計戦略：副作用の分離、引数と戻り値の設計、純粋性とテスト性の確保



## 概要

関数とは「処理のまとまり」ではない。  
それは**“意味の定義、責務の抽出、再利用の契約”**である。

JavaScriptは柔軟な言語であるがゆえに、関数が無秩序になりやすい。  
この柔軟さを制御し、**壊れず・読めて・使える関数を設計するには戦略が必要**である。

本稿では、**副作用の管理、引数と戻り値の構造、関数粒度の分離、テスト性と再利用性の担保**まで、関数設計の実践原則を体系的に解説する。

---

## 1. 副作用のない「純粋関数」が基本原則

```js
function sum(a, b) {
  return a + b;
}
```

- ✅ **副作用なし**
- ✅ 同じ入力 → 必ず同じ出力
- ✅ テストが容易 / 予測可能

---

## 2. 副作用は外部で注入・分離する

```js
function saveData(data, saveFn) {
  return saveFn(data);
}
```

- ✅ 副作用（保存・API通信など）は関数の外から**依存注入**
- ✅ **副作用 = I/O / DOM操作 / ネットワーク / 日付取得 / Math.random**

---

## 3. 引数設計は「構造と意味」で整理する

### ❌ 曖昧な引数列

```js
createUser('Toto', 25, true);
```

### ✅ 意図が明確なオブジェクト引数

```js
createUser({ name: 'Toto', age: 25, isActive: true });
```

- ✅ 順番のバグ防止 / 意図の明示 / 拡張性あり

---

## 4. 戻り値は構造で返し、例外は明示的に扱う

```js
function parseJSON(str) {
  try {
    return { ok: true, data: JSON.parse(str) };
  } catch (e) {
    return { ok: false, error: e };
  }
}
```

- ✅ 失敗も含めて**戻り値の構造に含める**
- ✅ 例外をthrowせず、**UI層で明示的に分岐可能**

---

## 5. 高階関数で共通処理を抽象化

```js
function withLogging(fn) {
  return (...args) => {
    console.log('call', fn.name);
    return fn(...args);
  };
}

const loggedSum = withLogging(sum);
```

- ✅ 関数を関数として扱う設計（**ファーストクラス関数**）
- ✅ **ロジックの共通化 + クロスカッティング処理に有効**

---

## 6. 関数粒度と命名設計

- ✅ 関数は**「1つの責任」だけを持つ**
- ✅ 長くなる場合は**内部処理を小関数に分割**
- ✅ 命名は「動詞 + 意図」

```js
fetchUser → getUserDataFromAPI  
calculate → calculateFinalScore  
```

---

## 設計判断フロー

```text
① この関数に副作用はあるか？ → 分離 or 注入可能にする

② 同じ入力で出力が変わるか？ → 純粋性を見直す

③ 引数が多すぎないか？ → オブジェクト化して構造化

④ 失敗時の戻り値が曖昧では？ → エラー構造を含める

⑤ 関数名に責務が表れているか？ → 動詞 + 意図ベースに再設計
```

---

## よくあるミスと対策

### ❌ 関数内でDOM操作・API呼び出し・ロジックを全部含めている

→ ✅ **副作用・ロジック・表示を分離し、責務を限定する**

---

### ❌ 引数の順番を間違えてバグ発生

→ ✅ **オブジェクト引数に変えて安全性と意図を明示**

---

### ❌ テストで毎回APIを叩いてしまう

→ ✅ API呼び出しは**引数として注入可能にしてテスト分離**

---

## 結語

関数とは「やること」ではない。  
それは**“何を渡せば何が返ってくるか”を明示する契約構造**である。

- 副作用は外に出し  
- 引数と戻り値に意味を持たせ  
- 小さく、予測可能に、再利用可能に

**JavaScriptにおける関数設計とは、  
“拡張と修正に強く、テスト可能で、安全な構造の最小単位”である。**
JavaScriptにおけるエラーハンドリング戦略：try-catch、ユーザー通知、復旧・ログ出力と安全な終了設計

## 概要

エラーハンドリングとは「止める」ことではない。  
それは**「壊さず、知らせ、回復可能な構造にする」ための設計行為**である。

JavaScriptの実行環境は多様で不確実。  
同期・非同期・UI・ネットワークあらゆる箇所で例外が発生しうる。

本稿では、**予期可能な失敗の吸収と通知、UIの安全な設計、ログによる分析性の担保**を含めた、**壊れないアプリケーション設計**を提示する。

---

## 1. try-catch の適用範囲は最小単位に

```js
try {
  const data = JSON.parse(raw);
} catch (e) {
  console.error('JSONパース失敗:', e);
}
```

- ✅ **try-catchの範囲を小さく**することで局所的にエラーを制御
- ❌ アプリ全体を包むようなcatch構造 → **障害原因が特定不能に**

---

## 2. 非同期関数での明示的エラーフロー設計

```js
async function fetchUser() {
  try {
    const res = await fetch('/api/user');
    if (!res.ok) throw new Error('通信エラー');
    return await res.json();
  } catch (e) {
    throw new Error(`ユーザー取得失敗: ${e.message}`);
  }
}
```

- ✅ ネットワークの成功でも**res.okをチェック**
- ✅ エラーを**補足して明示的なメッセージ化**

---

## 3. ユーザー通知と開発ログは目的を分ける

### ✅ ユーザーには簡潔に

```js
showError('サーバーと通信できませんでした');
```

### ✅ 開発者向けには詳細ログ

```js
console.error('Fetch failed:', e, context);
```

→ ✅ ユーザーの混乱を防ぎつつ、**調査可能な情報を記録**

---

## 4. グローバル例外フックの設計

```js
window.onerror = function (msg, url, line, col, error) {
  sendLog({ msg, url, line, col, stack: error?.stack });
};

window.onunhandledrejection = function (event) {
  sendLog({ reason: event.reason });
};
```

- ✅ **未処理の例外・Promiseエラーも捕捉可能**
- ✅ ログに加え、**ユーザーへの再読み込み提案**なども実装可能

---

## 5. リトライ戦略と安全なフォールバック

```js
async function retry(fn, limit = 3) {
  for (let i = 0; i < limit; i++) {
    try {
      return await fn();
    } catch (e) {
      if (i === limit - 1) throw e;
    }
  }
}
```

- ✅ 通信系の一時的失敗に対して**限定的な再試行**
- ✅ 最後には**フォールバックデータ**を表示 or 再接続ボタンを提供

---

## 6. 意図的な失敗の伝播設計

```js
function validateInput(value) {
  if (!value) throw new ValidationError('入力が空です');
}
```

- ✅ **ドメインエラー（入力ミス・状態不整合）**は意図的にthrow
- ✅ `ValidationError`, `NotFoundError`など**カスタムエラーで粒度を明示**

---

## 設計判断フロー

```text
① try-catchの範囲が広すぎないか？ → 小さい関数単位で設計

② catch後にログとユーザー通知を分けているか？ → 目的を区別

③ ネットワーク失敗が検知可能になっているか？ → res.okチェック

④ 再試行やフォールバックが存在しているか？ → retry構造を用意

⑤ 予期しうるエラーを例外として設計しているか？ → カスタムエラー導入
```

---

## よくあるミスと対策

### ❌ try-catchで握りつぶして何も通知しない

→ ✅ **ログ送信 + UIで明示的に通知**

---

### ❌ 非同期エラーが `await` されず、気づかない

→ ✅ **Promiseを必ずawait or catchで制御**

---

### ❌ 例外の種類が曖昧で、処理分岐ができない

→ ✅ **カスタムエラーで意味付けを明確に**

---

## 結語

エラーハンドリングとは「失敗を防ぐこと」ではない。  
それは**“失敗しても壊れない構造”をつくるための設計戦略**である。

- エラーは発生する前提で設計し  
- 意図的に分類し  
- 最小限に影響を閉じ込める

**JavaScriptにおけるエラーハンドリング設計とは、  
“失敗を吸収し、回復可能にするための構造化された防御”である。**
Denoを使って，QiitaのOrganizationsのフォロワー数を一括で取得するスクリプトを書いてみた

## これは何?

今年の自分はQiitaを始めとする発信に力を入れたいと思っている。
4月なので，目標を立てるにあたり，身近なライバルであるOrganizationsのメンバーのデータが取得したいと思い，スクリプトを書いた。
同じような用途に使いたい人の参考になれば幸いである。

半分はDenoを初めて触った人間の感想文なので注意。

---

## できること

Organizationsを指定して実行し，フォローワーの多い順に並べて出力する。

[GitHubのリポジトリ](https://github.com/RyosukeDTomita/qiita_organizations_ranker)で公開している。

```shell
docker compose run deno_app nri
Total members in nri organization: 140

Users ranked by follower count:
1. User: hoge, Followers: 1000
2. User: fuga, Followers: 900
3. User: hogehoge, Followers: 800
4. User: piyo, Followers: 700
5. User: fugafuga, Followers: 600
...
```

本当はContributionを取得したかったが，[Qiita API v2](https://qiita.com/api/v2/docs#%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC)がContributionの取得をサポートしていなかったのでフォロワー数を一旦取得してみた。

---

## 環境

Deno(TypeScript)を使ってみたかったので，選択した。

### スクリプトの流れ

1. 実行時の引数からorganizationを取得
2. organizationからユーザ名のリストを作成
3. Qiita APIを使い，各ユーザのフォロワー数を取得
4. フォロワー順に並び替えて出力

---

## 詰まったところとか

[Qiita API v2](https://qiita.com/api/v2/docs#%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC)がOrganizationsのユーザを取得するAPIを持っていなかったので以下を参考に取得した。

https://qiita.com/chovin/items/3a4d7db6329286ea44e8

---

## Denoを使ってみての感想

- 現状`--allow-net`をつけないと外部ネットワークへ通信が飛ばせないみたいなDeno特有のセキュリティ機能はあまり使いみちが思いついていない。APIの向き先を.envなどで管理していて，localhostのつもりで外部に飛ばしてしまうみたいな事故は防げるとか?
- `deno compile main.ts`するだけで依存関係が解決してコンパイルが終わるのめちゃくちゃ便利。これは`deno run`にもいえることだが，Dockerfileがめちゃくちゃすっきりする。
  ```Dockerfile
  FROM mcr.microsoft.com/devcontainers/base:bookworm AS devcontainers
  WORKDIR /app
  # install deno
  RUN curl -fsSL https://deno.land/install.sh | sudo DENO_INSTALL=/usr/local sh


  FROM devcontainers AS compile
  COPY . .
  WORKDIR /app/org_ranker
  RUN deno compile --allow-net --allow-env --allow-read --output main main.ts


  FROM gcr.io/distroless/cc-debian12:latest AS run
  COPY --from=compile /app/org_ranker/main /app/main
  ENTRYPOINT ["/app/main"]

  ```
- `deno fmt`でフォーマットできるとか`deno check`で型チェックできるのとか地味に嬉しい。自分は通常のVS CodeとVS Code Insider，Windsurfを使っていて，それぞれのIDEをちゃんと設定するのは面倒なのでコマンドラインでなんでもできると普通に助かる。
- taskは使いこなせていないが，普通に便利そうではある。長いコマンド覚えられないし。
- node.jsと大きく書き方が違うわけではないと感じたので，一旦Denoやってみようかなと思った。書籍とかサンプルは圧倒的にnode.jsの方が多いのでnode.jsでinputしてDenoでアウトプットしたい。


---

## 最後に

Deno試した日記みたいになってしまったが，あとから見返すには良いかなと思っている。
Denoをもう少し使い込んだらまとめて記事にしたい。
JavaScriptにおけるDOM設計戦略：動的構築・イベント構造・パフォーマンスと責務の分離


## 概要

DOM設計とは「HTMLを操作する」ことではない。  
それは**構造・意味・操作・効率を統合する「UIの根幹設計」**である。

JavaScriptによってDOMを生成・制御する際、**再描画、構造破壊、イベントの過剰結合**といった問題が頻発する。  
本稿では、それらを未然に防ぐための**責任あるDOM設計戦略**を提示する。

---

## 1. DOM構築は「意味単位」で行う

```js
function createUserCard(user) {
  const card = document.createElement('div');
  card.className = 'user-card';

  const name = document.createElement('h2');
  name.textContent = user.name;

  const email = document.createElement('p');
  email.textContent = user.email;

  card.append(name, email);
  return card;
}
```

- ✅ 「UIパーツ」を**コンポーネント関数で定義**
- ✅ DOMの直接生成でも**意味単位で分離**する

---

## 2. 動的要素の責務を「構築・挿入・破棄」に分離

```js
function insertUserList(users) {
  const list = document.createElement('ul');
  for (const user of users) {
    const item = document.createElement('li');
    item.textContent = user.name;
    list.appendChild(item);
  }
  document.querySelector('#container').replaceChildren(list);
}
```

- ✅ 「生成」と「挿入」は別関数に → **テストしやすくなる**
- ✅ `replaceChildren()` で**フラッシュ的更新**に

---

## 3. DOMイベント設計との分離

```js
function bindButton(el, callback) {
  el.addEventListener('click', callback);
}
```

- ✅ UI構築とイベント登録を**分離して設計**
- ✅ 複数イベント登録を構造化できる

```js
function bindEvents(el, events) {
  for (const [type, handler] of Object.entries(events)) {
    el.addEventListener(type, handler);
  }
}
```

---

## 4. パフォーマンス設計：再描画と再検索の最小化

- ✅ DOMツリー更新は**まとめて1回**
- ✅ `DocumentFragment`を活用して**仮想構築 → 挿入**

```js
const frag = document.createDocumentFragment();
for (const data of items) {
  const el = document.createElement('li');
  el.textContent = data;
  frag.appendChild(el);
}
list.appendChild(frag);
```

---

## 5. 意味と構造の分離：データ属性の活用

```html
<button data-user-id="u01">詳細</button>
```

```js
btn.dataset.userId; // → "u01"
```

- ✅ DOMに**意味を埋め込む**のではなく、**data-* で分離**
- ✅ アプリ構造とUI構造の結合を緩やかに保てる

---

## 6. DOMの破棄とガーベジ設計

- ✅ 必ずイベントリスナーを解除する

```js
function destroy(el) {
  el.remove();
}
```

- ✅ Webアプリでは **DOM残存 + イベント残存 → メモリリーク**を引き起こす

→ ✅ 「構築」「破棄」「再利用」の**ライフサイクル関数**を分離して設計

---

## 設計判断フロー

```text
① そのDOMは意味単位で構成されているか？ → UI部品化へ

② DOMの構築とイベントが混在していないか？ → 分離して責務明確化

③ 再描画を毎回DOMに適用していないか？ → Fragment化 or replaceChildren

④ UIとロジックが結合していないか？ → data-*で分離、ロジックは別層に

⑤ 破棄処理を忘れていないか？ → destroy関数とremoveEventListener
```

---

## よくあるミスと対策

### ❌ `innerHTML +=` を多用して毎回再評価・再構築が発生

→ ✅ **DOM APIを用いた構築と挿入**に分離

---

### ❌ 1つの関数でDOM構築・挿入・イベント登録・削除まで一括処理

→ ✅ **責務ごとの分離**によりテスト性と再利用性を向上

---

### ❌ 破棄したはずのDOMにイベントが残留

→ ✅ **removeEventListener + remove()** を忘れずに

---

## 結語

DOMとは「UIの器」ではない。  
それは**意味・操作・振る舞いの集合体であり、構造そのものである。**

- 生成は意味単位にし  
- 振る舞いは責務で分離し  
- 表示は一括で最小化し  
- 破棄まで設計する

**JavaScriptにおけるDOM設計とは、  
“UIの構造を保ち、意味を壊さず、振る舞いを統合する”という設計戦略である。**
JavaScriptにおけるアクセシビリティ設計：WAI-ARIA、キーボード操作対応、視覚支援・読み上げ対応

## 概要

アクセシビリティとは「使いやすさ」ではない。  
それは**「すべてのユーザーが、意味にたどり着けること」を保証する構造**である。

JavaScriptを用いて動的なUIを設計する時、  
**見た目が変わること**よりも、**構造と意味が伝わること**が優先される。

本稿では、**JavaScript実装におけるA11y設計戦略**を、構造／操作／通知／意味の4つの観点から整理する。

---

## 1. キーボード操作の対応は最優先事項

### ✅ クリック操作と同等のキーボード操作を提供する

```js
button.addEventListener('keydown', (e) => {
  if (e.key === 'Enter' || e.key === ' ') {
    triggerAction();
  }
});
```

- ✅ `<button>` タグを使えば基本対応は不要（HTML標準で対応済）
- ❌ `div` や `span` に `onclick` を付けると、**キーボード非対応のUIが生まれる**

---

## 2. フォーカス制御と tabindex

### ✅ 動的要素に `tabindex` を明示的に設定

```html
<div tabindex="0">Focusable</div>
```

- ✅ フォーカス順を明示
- ✅ `tabindex="-1"` はスクリプトでの `focus()` 用（ユーザー操作ではフォーカス不可）

---

## 3. ARIAロールと属性で意味を伝える

```html
<div role="alert" aria-live="assertive">
  エラーが発生しました。
</div>
```

- ✅ `role="button"` や `role="dialog"` などで**意味を付与**
- ✅ `aria-live` によって動的変化を読み上げ可能に

→ ✅ ARIA属性は“構造”ではなく“補完”として使うべき（semantic HTMLが第一）

---

## 4. ダイアログ・モーダル設計の責務

### ✅ 初期フォーカス + 閉じるボタンへのキーボード対応 + フォーカストラップ

```js
modal.querySelector('button').focus(); // 初期フォーカス

document.addEventListener('keydown', (e) => {
  if (e.key === 'Escape') closeModal();
});
```

- ✅ モーダル内のみ `Tab` 移動可能に（フォーカストラップ）
- ✅ `aria-modal="true"` + `role="dialog"` + `aria-labelledby`

---

## 5. 視覚支援・読み上げ対応の原則

- ✅ テキストは`aria-label` or `aria-labelledby` で明示

```html
<input type="search" aria-label="商品を検索">
```

- ✅ `aria-describedby` で補足文の読み上げ指定
- ✅ `aria-hidden="true"` で読み上げ対象外に（UI用アイコンなど）

---

## 6. A11y設計のチェックリスト

```text
☑ すべての機能がキーボードで操作可能か？
☑ 意味のあるHTMLタグを使っているか？
☑ 視覚情報に頼らず意味が伝わるか？
☑ 状態変化がスクリーンリーダーに伝わっているか？
☑ 動的に追加された要素にもARIAが適用されているか？
```

---

## よくあるミスと対策

### ❌ `<div onclick="...">` でボタンを実装し、キーボード非対応に

→ ✅ `<button>` 要素を使用 or `role="button"` + `tabindex="0"` + `keydown` 実装

---

### ❌ 動的エラー表示が読み上げられない

→ ✅ `aria-live` コンテナに出力し、**スクリーンリーダーに変化を通知**

---

### ❌ フォーカスがモーダル外に移動し、操作不能に

→ ✅ **フォーカストラップ設計 + モーダル解除で元の要素に戻す**

---

## 結語

アクセシビリティとは「配慮」ではない。  
それは**“すべてのユーザーが到達できる構造を選ぶ”という設計上の責任**である。

- 見た目より構造を優先し  
- 操作より意味を保証し  
- 技術より設計を選ぶ

**JavaScriptにおけるアクセシビリティ設計とは、  
“誰もが使えることを、当たり前に保証する構造戦略”である。**
JavaScriptにおけるパフォーマンス設計戦略：再レンダリング最小化、計算コストの制御、非同期最適化、メモリリーク対策

## 概要

パフォーマンスとは「最速」ではなく、**「知覚的に快適であり続ける設計」**である。  
それは一時的な高速化ではなく、**構造的に“劣化しない”ことを保証する戦略**である。

本稿では、JavaScriptアプリケーションにおける**UI性能・計算効率・メモリ管理・非同期負荷**などに着目し、**体感速度を担保するための設計原則と実装戦略**を提示する。

---

## 1. 再レンダリングの最小化

### ✅ 状態の粒度で分離

```js
const [title, setTitle] = useState('');
const [count, setCount] = useState(0);
```

- ❌ 状態を一つにまとめると**更新時に全部再レンダリング**
- ✅ **責務ごとに分離して更新の波及を抑える**

---

### ✅ React.memo / useMemo / useCallback の正しい適用

```js
const MemoizedComponent = React.memo(MyComponent);
```

```js
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
```

- ✅ **無駄な再評価・再生成を抑える**
- ✅ 過剰なmemoは逆効果 → **「再生成コストが高い場合」に限定適用**

---

## 2. 計算コストの分離と非同期化

### ✅ UIブロッキング処理をオフロード

```js
// 重い処理をWeb Workerに逃がす
const worker = new Worker('./heavy.js');
worker.postMessage(largeData);
```

- ✅ メインスレッドのブロッキングを防ぎ、**UIレスポンスを維持**
- ✅ タスク分割 or `requestIdleCallback` の活用も有効

---

## 3. イベント負荷の制御：debounce / throttle

```js
const handleScroll = throttle(() => {
  console.log('scrolling...');
}, 100);
```

- ✅ `debounce`: 連続入力の末尾1回だけ（例：検索入力）
- ✅ `throttle`: 一定間隔で制限（例：スクロール検出）

→ ✅ 高頻度イベントには**意図的な制御レイヤー**を

---

## 4. 非同期処理の最適化と並列戦略

```js
// ❌ 逐次処理（時間がかかる）
await fetchA();
await fetchB();

// ✅ 並列処理
await Promise.all([fetchA(), fetchB()]);
```

- ✅ 並列化で**レスポンスタイムを短縮**
- ✅ 非同期関数は**不要にawaitしない**（パフォーマンス劣化）

---

## 5. メモリリークの予防と検出

### ✅ よくあるリークポイント

- 未解除のイベントリスナー
- 未破棄のTimer/Interval
- グローバル変数に保持された参照
- useEffect のクリーンアップ漏れ

```js
useEffect(() => {
  const id = setInterval(doSomething, 1000);
  return () => clearInterval(id);
}, []);
```

### ✅ DevToolsによる監視

- **Chrome DevTools → Memory → Heap snapshot**
- ガベージコレクション後も残る参照を調査

---

## 設計判断フロー

```text
① 状態更新が必要以上に再描画を引き起こしていないか？ → 粒度分割とmemo化検討

② 高頻度イベントに直接関数を渡していないか？ → debounce/throttle導入

③ 非同期処理が逐次実行されていないか？ → Promise.allで並列化

④ メモリが増え続けていないか？ → useEffect内でクリーンアップ設計

⑤ 重い処理をメインスレッドで動かしていないか？ → WorkerやIdleCallbackを検討
```

---

## よくあるミスと対策

### ❌ useEffectで登録したイベントを解除せずリーク

→ ✅ **明示的な return でイベント削除・タイマー解除を設計**

---

### ❌ 高頻度イベント（スクロール・入力）でハンドラが毎回実行される

→ ✅ throttle / debounce で**処理頻度を制御**

---

### ❌ 非同期APIを逐次awaitしてレスポンスが遅い

→ ✅ **並列化できる部分はPromise.allに集約**

---

## 結語

パフォーマンスとは「速いこと」ではない。  
それは**“快適さが保たれる構造”を保証する設計行為**である。

- 計算を分離し  
- 状態を細分化し  
- UIレスポンスを設計し  
- 未来のメモリを守る

**JavaScriptにおけるパフォーマンス設計とは、  
“最適化し続けるのではなく、最初から壊れない構造を選ぶ”という戦略である。**
TypeScript:4.コールバック関数

# コールバック関数ってそもそも何？
「あとで呼ばれる関数」＝それがコールバック関数（callback function）
・他の関数や処理に引数として渡す関数
・必要なタイミングで「呼び出される」

## 例
```typescript:exe.ts
class Food {
    constructor(
        private element: HTMLDivElement,
        private onClick: (el: HTMLDivElement) => void
    ) {
        this.element.addEventListener('click', () => {
            this.element.classList.add('food--active');
            this.onClick(this.element);
        });
    }
}

class Foods{
    foods: NodeListOf<HTMLDivElement>;
    activeFood: HTMLDivElement[] = [];

    constructor() {
        this.foods = document.querySelectorAll<HTMLDivElement>('.food');
        this.foods.forEach(element => {
            
            new Food(element, (clickedElement) => {
                if (!this.activeFood.includes(clickedElement)){
                    this.activeFood.push(clickedElement);
                }
                console.log(this.activeFood);
            });

        });
    }
}
```
## コールバック関数流れを説明
### 1.FoodsクラスがFoodインスタンスを作るときに関数を渡す
```typescript:exe.ts
new Food(element, (clickedElement) => {
    if (!this.activeFood.includes(clickedElement)){
        this.activeFood.push(clickedElement);
    }
    console.log(this.activeFood);
});
```
「コールバック関数」Foodに「クリックされたらこれやってね」と事前に渡している

### 2.Foodクラスがクリックされると
```typescript:exe.ts
this.element.addEventListener('click',() => {
    this.element.classList.add('food--active');
    this.onClick(this.element);
});
```
このthis.onClick(...)が実際にコールバック関数を実行している場面

### 3.呼び出された結果、さっき渡していた(clickedElement) => {..}が実行される
```typescript:exe.ts
(clickedElement) => {
    if (!this.activeFood.includes(clickedElement)){
        this.activeFood.push(clickedElement);
    }
    console.log(this.active);
}
```
## まとめ
Foods：Foodに「クリックされたらこれやって」と関数を渡す
Food ：クリックされたら、渡された関数を呼び出す
コールバック関数：「後で呼ばれる関数」＝この連携のカギになる
完璧で究極のAIをBrainf**kで作ってみた

## 目次
- [目次](#目次)
- [はじめに](#はじめに)
- [ぼくのかんがえたさいきょうのえーあい](#ぼくのかんがえたさいきょうのえーあい)
- [よくあるアンチ](#よくあるアンチ)
- [44ON44K/44OQ44Op44K3](#ネタバラシ)
- [制作に使用させていただいたありがたいサイトたち](#制作に使用させていただいたありがたいサイトたち)

## はじめに
完璧で究極なAIなら、すべての質問に究極の答えを出せないといけませんよね。
生命、宇宙、万物についての究極の答えを出せなければつよつよAIとして一人前ではありません。
しかし、そんなつよつよAIなんぞ◯penAIだかG〇〇gleだかがすでに作っているので今更作ったところで百番煎じです。
てことで、誰も使っていない言語で一人前、なんなら四十二人前の完璧究極AIを作りましょう。

## ぼくのかんがえたさいきょうのえーあい
```brainfuck:saikyo-ai.bf
+++++++++[>+++++++++<-]>++++++++.----------.+++++
+.<+++[>---------<-]>.<+++++++++++++[>--<-]>.[-]+
++++[>+++++<-]>[>,.<-]>>>++++++++[>++++<-]>.<++++
+++++++[>+++<-]>.++++++++.---------------.<++++++
+++++++[>--<-]>.<+++++++++++++[>++<-]>------.--.
```
このたった248バイトに宇宙のすべてが詰まっているのです。すごいでしょ

**使用例**
```:入力
関数f(x)=7x+7の、xが0~6の範囲の積分
六掛ける九
伊藤博文が暗殺されたのは明治何年？
学校の七不思議は何個ある？
ゴルフでホールインワンして
生命、宇宙、そして万物についての究極の疑問の答え
```
```:出力
42
```

この完璧究極AIは文武両道な上に、この世の法則まで完全に理解しています。
Cha◯GPTとかGem◯niとかよりもこっちのほうが42倍いいですよ。ぜひ使え

>"42"こそ聖なる数であり、それを否定するものは地底に落ちることとなる。
by フール・エイ=プリル牧師

![Answer_to_Life.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4050442/c3c7ae45-6e9d-425b-95f3-ee41d7d562a7.png)
画像：wikipedia


## よくあるアンチ
悲しいことに、完璧究極AI、「saikyo-ai.bf」を公開した直後、心無いアンチたちが批判を始めました。

`Q. 6×9は54だろ、何言ってんだ`
A. いいえ、42です。この完璧究極AIが42と言ったら答えは42なのです。
いままでの数学の定義自体を見直してみるべきでは？

`Q. 絶対全部間違ってる`
A. このプログラムは常に正しい答えしか出しません。
「42」という答えはもはや答えではなくこの世の法則なのです。

このように、この完璧究極AIには抜けどころが一切ありません。いくら批判したところで無駄なのです。

## ネタバラシ
エイプリルフールです。（ギリギリ）
初めての投稿がエイプリルフールになってしまいました。
Brainf**kでコーディングするのは初めてですが、無事完成してよかったです
実はこのAI、入力が25文字までしかできないです
無限に入力できるようにしたかったのですが、そんな技術はなかったですね
一部のBrainf*ckエディタを使うと入力がなんかバグり散らかします

## 制作に使用させていただいたありがたいサイトたち
[ウェブブラウザで動くBrainfuck](https://kachikachi.net/brainfuck/)
[オンラインBrainf*ckデバッガ](http://moon.kmc.gr.jp/~prime/brainf_ck/env/)
OpenShiftでAPI証明書の期限が切れた場合の復旧方法

# はじめに

本記事では、OCPのAPI証明書の期限が切れて、APIアクセスできなくなった場合に、API証明書を更新して復旧する手順を示します。
OCPのAPI証明書は1ヶ月ごとに自動更新されますが、OCPノードが1ヶ月以上の長期に渡って停止状態にするなどして自動更新できなかったケースにおいて、起動後に手動更新してAPIアクセスを復旧する必要があります。

# 目次

1. [API証明書の更新手順](#api証明書の更新手順)
1. [まとめ](#まとめ)
1. [参考文献](#参考文献)

# API証明書の更新手順

API証明書の期限が切れた状態で、oc loginしようとすると、下記のようなエラーが返ります。
```shell-session
# oc login https://<IP_address>:6443 -u <user> -p <password>
Unable to connect to the server: EOF
```

復旧作業はmasterノードから行う必要があるため、いずれかのmasterノードへSSHログインします。
```shell-session
# ssh core@<master_node>
```

rootユーザになります。
```shell-session
# sudo -i
```

環境変数KUBECONFIGにkubeconfigファイルを指定して、ocコマンドを実行できる状態にします。(指定するkubeconfigファイルは参考文献参照)
```shell-session
# export KUBECONFIG=/etc/kubernetes/static-pod-resources/kube-apiserver-certs/secrets/node-kubeconfigs/lb-int.kubeconfig
```

Pending状態になっているCSRを承認します。(PendingのCSRがなくなるまで繰り返し実行します)
```shell-session
# oc get csr | grep Pending | awk '{print $1}' | xargs -I{} oc adm certificate approve {}
```

最後に、openshift-apiserver namespaceのpodをdeleteして再起動します。

```shell-session
# oc -n openshift-apiserver delete pods --all
```

podが起動するまで待ちます。
```shell-session
# oc -n openshift-apiserver get pods
```

pod起動後に、oc loginができるようになります。
```shell-session
# oc login https://<IP_address>:6443 -u <user> -p <password>
```

# まとめ

1ヶ月以上OCPノードを停止するなどAPI証明書を自動更新できない場合は、次回起動の際にAPI証明書が切れた状態となり、oc loginもできない状態となるため、上記のような手動での復旧作業が必要となります。

# 参考文献
- [About the OpenShift 4 kubeconfig file for system:admin](https://access.redhat.com/solutions/4845381)
スタートアップでのQAチーム組織立ち上げ

IT業界にもう20年近くおりますが、最初の数年開発エンジニアとしてその後QAエンジニアとして活動してきました。



## QAチーム組織立ち上げ
今まで、多くのプロダクトサービスで**QA関連の役割/職種**をこなしてきました。


**参考）QAエンジニア　Qiita**

https://qiita.com/jun2014/items/32ec77be1056f546aada

**参考）QAチーム立ち上げ　Qiita**

https://qiita.com/jun2014/items/4d5554cc7c9e5ba12106

**参考）エンジニア転職市場　note**

https://note.com/jun111/n/n74c73bfced2c

また最近は、SaaS ベンチャー・スタートアップでの **「QAチーム立ち上げ」** に携わっております。

メモ書き程度で良いので、**まず何をするか順番に項目**を起こしてみる。
```lang:「0→1」を作り上げること、メンバーにお願いしたいこと。

1.社内でのQAの役割を明確にする（テストレベルやどこまで担保するのか）
2.ドキュメント類の整理（必要予算、組織計画、メンバー採用計画）
3.リリースQAフロー作成
4.テスト作業フロー作成（上流からどう入るか、リリース判断はどうするのか）
5.案件にあったE2Eテストツールの選定
6.1ヶ月、3ヶ月、半年、1年、3年のスケジュールプラン（想定）を用意
7.プランを達成するための予算と人員計画の数値化
8.社内のエンジニアに品質の重要性を説く
9.「単体テスト」、「結合テスト」、「システムテスト」、「リリース後」の1件の不具合コストを考える
10.QAメンバーは、受け身になるのではなく、何が足りないのか、今後よりよくするためにはどうしたらよいか上司に伝える
11.常に自身の自己スキルを把握し、スキルの向上に努める
12.プログラミング、インフラ、セキュリティ面での知識も必要
```

まずは、こんな感じでしょうか。

## 立ち位置

立ち上げなので、誰が　**「マネージャ」**　だとか　**「リーダー」**　だとか　**「メンバー」**　とか、切り分けて作業する必要はなし。

同じ裁量で作業する、まずは同じ裁量で作業した方が立ち上げフェーズは短縮できると思っております。

半年を目処に、各役割「マネージャ」だとか「リーダー」だとか「メンバー」を決めることができれば良いと。（今までのスキル、今後のキャリアアップも含め）

QAグループ（チーム）がある会社に入社することと、無い会社（スタートアップQAたちあげ）に入社する場合とでは、違和感が最初あるかもしれません。

今までの現場では、テスト環境（検証環境）がある、テストを開発者がきちんとやっている。　

## メモ

0⇨1 だとその概念は、通用しないと。
そこから品質を考えないといけない。

約束事を作る。例えば、リリースの計画があり、Staging環境ではこの期間にこのブランチを撒く。

検証環境ではこの期間にこのブランチを撒くなどを考える。
QA実施期間中は、不要なデプロイは禁止する。

それがスタートアップのQAだということです。
当たり前が当たり前ではないのです。
Webカメラを使って✋手をリアルタイム認識してみた（MediaPipe + Python）


## はじめに
この記事では、PythonとMediaPipeを使って、Webカメラのみで手の動きをリアルタイムに認識する方法を紹介します。TouchDesignerやインタラクティブ展示にも応用可能です。

---

## 環境
- OS：Windows 10 / macOS
- Python：3.10
- ライブラリ：
  - mediapipe
  - opencv-python

---

## 目的
- Webカメラで手を認識してみたい
- 関節の位置を取得して可視化・応用したい
- 展示やパフォーマンスに活かしたい！

---

## 実装手順

### 1. 必要なライブラリのインストール

```bash
pip install mediapipe opencv-python
```

### 2. コードを書く
``` python
import cv2
import mediapipe as mp

# MediaPipeのモジュール初期化
mp_hands = mp.solutions.hands
mp_drawing = mp.solutions.drawing_utils

# Webカメラを開く
cap = cv2.VideoCapture(0)

# MediaPipe Hands 初期化
with mp_hands.Hands(
    max_num_hands=2,
    min_detection_confidence=0.7,
    min_tracking_confidence=0.5
) as hands:

    while cap.isOpened():
        success, image = cap.read()
        if not success:
            print("カメラが読み込めませんでした")
            continue

        # OpenCVはBGR、MediaPipeはRGBなので変換
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        image.flags.writeable = False  # 高速化のために書き込み不可に

        # 手の検出
        results = hands.process(image)

        # 描画のために再びBGRに変換
        image.flags.writeable = True
        image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

        # ランドマークの描画
        if results.multi_hand_landmarks:
            for hand_landmarks in results.multi_hand_landmarks:
                mp_drawing.draw_landmarks(
                    image,
                    hand_landmarks,
                    mp_hands.HAND_CONNECTIONS
                )

        # ウィンドウ表示
        cv2.imshow('Hand Tracking', image)

        # ESCキーで終了
        if cv2.waitKey(5) & 0xFF == 27:
            break

# 終了処理
cap.release()
cv2.destroyAllWindows()

```

### 3.解説

🔹 手の検出について

MediaPipe Handsは、21個の関節をリアルタイムでトラッキングできます。

🔹 ランドマークの意味

例：
	•	landmark[4]: 親指の先端
	•	landmark[8]: 人差し指の先端
→ この2点の距離でピンチジェスチャーを検出できます

### 4.詰まったポイント・注意点
•	カメラがうまく動作しない場合、cap = cv2.VideoCapture(1)などに変更
•	処理が重い場合は解像度を下げる、関節の描画を省略するなど調整

### 5.まとめ
MediaPipeを使うと、シンプルなコードで手の認識ができ、リアルタイム作品の土台としてとても便利です。今後はジェスチャー検出や、外部ソフトとの連携も試してみたいと思います！
Docker環境のWordPressで編集ボタンが表示されない問題の解決方法


## 問題

WordPressサイトは表示されるのに、**編集ボタンが表示されない**という問題に遭遇しました。具体的には：

- HP画面は正常に表示される
- 左側や上部に編集ボタンが表示されない
- コンテンツを編集することができない

## 環境

- Docker上で動作するWordPress
- ポート10090でローカルホストに公開（http://localhost:10090/）

```yaml
# docker-compose.ymlの設定例
version: "3.7"
services:
  db:
    image: mysql:8.0
    # 省略...
  WordPress:
    image: wordpress:latest
    ports:
      - 10090:80
    # 省略...
  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    ports:
      - 10099:80
    # 省略...
```

## 原因

**ログインしていない状態**でサイトを閲覧していたため、編集ボタンが表示されませんでした。

WordPressでは、編集権限を持つユーザーがログインしていないと、編集インターフェースは表示されません。これはセキュリティ上の理由と、一般訪問者向けに公開ビューのみを表示するためです。

## 解決方法

以下の手順で解決しました：

1. WordPressの管理画面にログインする
   ```
   http://localhost:10090/wp-login.php
   ```

2. ユーザー名とパスワードを入力してログイン

3. ログイン後、編集ボタンが表示されるようになり、コンテンツの編集が可能になりました

## 補足：他にも考えられる原因と対策

編集ボタンが表示されない場合、他にも以下の原因が考えられます：

### 1. 権限不足

ログインしているユーザーに適切な権限がない場合、編集機能が制限されます。

- **確認方法**: 管理画面 → ユーザー → あなたのプロフィール で権限を確認
- **対策**: 管理者（Administrator）または編集者（Editor）権限を持つアカウントでログインする

### 2. テーマやプラグインの問題

使用しているテーマやプラグインが編集機能を制限している可能性があります。

- **対策1**: プラグインを一時的に無効化し、問題が解決するか確認する
- **対策2**: デフォルトテーマ（Twenty Twenty-Fourなど）に切り替えて確認する

### 3. キャッシュの問題

ブラウザのキャッシュが古い状態を表示している可能性があります。

- **対策**: ブラウザのキャッシュをクリアするか、シークレットモードで開く

## まとめ

WordPressで編集ボタンが表示されない場合、まずはログイン状態を確認しましょう。
`http://your-wordpress-site/wp-login.php` へアクセスして管理者権限を持つアカウントでログインすることで、多くの場合、問題が解決します。
Djangoのエラー"TypeError: consume_points() missing 1 required positional argument: 'request'"で苦戦した話

djangoのview.pyでリクエストからユーザーを取得して処理を実装しようとしていた際に
TypeError: consume_points() missing 1 required positional argument: 'request
に直面して時間を食ってしまったので備忘録を載せておきます。

## 原因(結論)

関数ベースビューで"self"を使ってユーザー情報を取得しようとしていたことが原因でした。
実際に、下記のような間違った書き方をしていました。
```python

def proccess(self,request):
    if request.method == 'POST':
        user = self.request.user
        #以下省略
```
selfを使うのは、クラスビューベースで定義するときのようです。

## 解決策
selfを使わず、以下のように書けばエラーを回避できます
```python

def proccess(request):
    if request.method == 'POST':
        user = request.user
        #以下省略
```
Arch LinuxでGhostty使ったら日本語が打てなかった

## 私環境での対処
:::note info
要約
fcitxを消してfcitx5を入れた
:::

[Ghostty](https://ghostty.org/)という良さげなターミナルを使いたくなった。
開発者はTerraformでお馴染み[Mitchell Hashimoto](https://github.com/mitchellh) san。
こういうナウでヤングなターミナルって日本語入力に難がありがちだけどMitchell Hashimotoさんはそういう方面に理解があるらしく、最近のバージョンでIME周りが改善されたらしいと知った。

お仕事Macで使ってみて快適だったので、私用のArch Linuxにも入れてみることにした。

```sh
sudo pacman -S ghostty
```
で手元のArch Linuxにインストールしたところ、見事に日本語が打てなかった。

## 前提
[Ghosttyは1.1でIME周りの入力改善](https://www.publickey1.jp/blog/25/ghosttyghostty_110ime.html)が入っている。
大前提としてGhostty1.1以降であることを確認する。
```sh
❯ ghostty --vesrion
info: ghostty version=1.1.3-arch1
```

問題ない
https://github.com/ghostty-org/ghostty/issues/4332 の修正が入る最新版で解決できると思ったが、私の環境では最新版を入れてもだめだったため、以下に続く……


## 手順メモ
これはあくまで私が解決できた手法である。
(これで解決しないかもしれないし、別のもっと良い方法があるかもしれません。)
### fcitxを削除
```sh
sudo pacman -Rs fcitx-im fcitx-mozc fcitx-configtool
```
### fcitx5をインストール
```sh
sudo pacman -S fcitx5-gtk fcitx5-im fcitx5-mozc fcitx5-configtool
```
fcitx5-gtkなしでもブラウザなどは日本語入力できるが、Ghosttyではfcitx5-gtkが無いとだめだった。

### xprofileの変更
最後に~/.xprofileの各種値がfcitxのままなのでfcitx5にしておく。

```text:~/.xprofile
export GTK_IM_MODULE=fcitx5
export QT_IM_MODULE=fcitx5
export XMODIFIERS=@im=fcitx5
```

これでreboot。

### 動作
Ghosttyで日本語が打てた。
候補出てくる位置がめちゃくちゃ気になるが……(スクショでは取れず)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621241/a8959434-50bd-435f-b3e5-08e14808d771.png)

もちろんブラウザでも日本語入力できる。この記事を書けてるのが証拠。



## fcitx5での変換 無変換でIME切り替え方法
IMEの切り替えのデフォルトはctrl + spaceになっている。
これを「無変換」は英語入力、「変換」は日本語入力にしたい。

変更方法はfcitx5-configtoolを開く。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621241/993aaca9-ab15-4560-9bfd-6eef05f84d46.png)

- 入力メソッドを有効にする：無変換 (なぜか日本語環境なのにハングル Hanjaで出てくるが問題なし……)
- 入力メソッドをオフにする：変換(なぜかこれもハングルで反映されるが問題なし)

これで保存すると変換無変換で切り替えられるようになった。


## Ghostty 合字廃止
Ghosttyで「ます」が「〼」と勝手に変換して表示される問題が発生。

ghosttyのconfigファイルで
```text:~/.config/ghostty/config
font-feature = -dlig
```
を入れることで通常の表示になった。
非ITエンジニアにもMCPを布教するためにMCPサーバーをexeにする

MCPめっちゃ便利なんですが、Node.js入れたりnpmコマンド実行したり、非ITエンジニアにはなかなか使ってもらえないですよね。。

Node.jsなしで配布する方法がないか探したところ、**ありました！**

https://nodejs.org/api/single-executable-applications.html

ステータスがActive developmentなようですが、Node.jsアプリケーションを単一の実行ファイルにできるようです！


## やってみた

WindowsでNodeが入ってる環境で作業します。

[Brave Search MCP Server](https://github.com/modelcontextprotocol/servers/tree/main/src/brave-search)を対象とします。

PowerShellでコマンドを順に実行します。

ソースを取得します。

```powershell
git clone https://github.com/modelcontextprotocol/servers.git
```

フォルダーを移動します。

```powershell
cd .\servers\src\brave-search\
```

Nodeのライブラリーをインストールします。

```powershell
npm install
```

ビルドします。dist/index.jsが生成されます。

```powershell
npm run build
```

外部ライブラリーをサポートしていない（単一のJavaScriptじゃないとだめ）らしいので、バンドルします。

参考：https://qiita.com/tenpaMk2/items/dbc15c2e99709878a69b

```powershell
npx -y esbuild dist/index.js --bundle --outfile=bundle.js --platform=node --format=cjs
```

`sea-config.json`というファイルを作成します。

```json
{
    "main": "bundle.js",
    "output": "sea-prep.blob"
}
```

よくわからない謎のコマンドを実行します。

```powershell
PS > node --experimental-sea-config sea-config.json
PS > node -e "require('fs').copyFileSync(process.execPath, 'brave-search-mcp.exe')"
PS > npx -y postject brave-search-mcp.exe NODE_SEA_BLOB sea-prep.blob --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 
```

これで`brave-search-mcp.exe`ができました！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/7d0d36e6-e1d1-440f-b3c2-ca44cef01a02.png)

## Claude Desktopで使ってみる

Claude Desktopの設定ファイル（`claude_desktop_config.json`）を編集します。

```json
{
  "globalShortcut": "Alt+Ctrl+Space",
  "mcpServers": {
    "brave-search": {
      "command": "C:/Users/{ユーザー名}/brave-search-mcp.exe",
      "env": {
        "BRAVE_API_KEY":"{APIキー}"
      }
    }
  }
}
```

動いた！


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/6b258f62-75e7-4523-9c7d-09d0287a6839.png)

めでたしめでたし

---

:::note warn
流行りの[Playwright MCP](https://github.com/microsoft/playwright-mcp)でやりたかったのですが、私の力及ばず断念。。
どなたかお願いしますm(_ _)m
:::
screenコマンドでSSHが閉じてもプロセスを実行させる

長時間かかる作業をSSHなどで行うときに使うと安全なコマンド。
`` screen `` 上で作業を実行してそれを切り離ししておけば、突然接続が途切れてもまた接続すれば何事もなかったかのように作業再開できる。

# 新規セッション作成

```bash:Terminal
screen
```
新しいセッションが作られ、画面がそちらに切り替わる

```bash:Terminal 
screen -S foo
```
とすると `` foo `` という名前のセッションが作られる。

# セッション切り離し

`` <Ctrl> `` + `` a `` の後に `` d `` を押してそのセッションを切り離して抜けることができる。
これでSSHが閉じてもこのセッションは生き残ることができる。

# セッション一覧確認

```terminal:Terminal
$ screen -ls
There are screens on:
        3265147.pts-4.rpi4b     (2025年04月01日 22時23分34秒)   (Detached)
        3265069.pts-4.rpi4b     (2025年04月01日 22時23分24秒)   (Detached)
        3263746.foo       (2025年04月01日 22時22分10秒)   (Detached)
3 Sockets in /run/screen/S-azumeeeeer
```

ここからセッションのIDなどを確認することができる。
なお、セッションを作った際に名前を付けた場合はその名前をIDとして使うことができる。

# セッション接続

```bash:Terminal
screen -r セッションのID
```

切り離したセッションに接続し、作業を再開することができる。

#

この記事書いた後に `` byobu `` の存在を知った。
`` byobu `` のほうが使いやすそうなのでそっちをつかおう。
タスク管理が下手なのでWindowsの付箋アプリを強制的に実行させてみた

こんにちは。皆さんはタスク管理をどう行っているでしょうか。
正直私は忘れがちで管理が物凄く下手だと自覚しております。
カレンダーを始め、管理系のアプリも試しておりますがなかなか改善に繋がらないと悩んでおります（現在進行形）
ですが仕事なので「じゃあ諦めましょう」は無理なお話……なので少しでも直したいと思い自分なりの解決方法を考えた結果、

**「常に目の見える範囲に置けば嫌でも覚えていられる」**

という力技な結論に至りました。ではどうするか？

**……PCの付箋アプリを使います！（ちなみにWindowsです）**

以下採用した理由です。

:::note info
- 携帯のアプリで管理するとアプリを開く、という行為そのもので抵抗感が生まれる（後述に繋がる）
- 通知を設定すると次第に「面倒臭い」が勝ってしまう（過去に経験有）
- 付箋なら通知は届かない
- PCなら仕事や勉強、趣味にも使用するので視界に入り易い
:::

でも付箋アプリを起動して毎日開くというのも忘れてしまいそう……。
ならば起動時に常に自動で開くように出来ないかなぁと調べたら見つかりました。

それはスバリ、**「スタートアップフォルダを使用する」** です。

:::note info
スタートアップフォルダとはその名の通り、PCを起動した際にアプリケーションやスクリプトファイルを自動的に開く機能を持つフォルダです
:::

このフォルダの中に実行したいアプリケーション（今回の場合は付箋）を格納する事で次回以降起動時に自動で付箋が開きます。

スタートアップフォルダは以下のパス内となります。
※スタートアップフォルダは個人ユーザーと、PCに登録されている全ユーザー用のがありますが、今回は前者の方で説明させて頂きます。

```
C:\Users\[ユーザー名]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
```

しかし頻繁に訪れるフォルダではないのでクイックアクセス等でピン留めしておくのは後々邪魔になりそうですよね。
そこでコマンドを使用して簡単にアクセスする方法も記載しておきます。

<dl>
  <dt>手順その1</dt>
  <dd>左下にあるWindowsのマークを右クリックして「ファイル名を指定して実行」を選択する</dd>
  <dt>手順その2</dt>
  <dd>ショートカットキーで「Window」と「R」を同時押しで左下に小さいダイアログボックスが表示されるので、「shell:startup」と入力する</dd>
  <dt>手順その3</dt>
  <dd>スタートアップフォルダが表示される</dd>
</dl>

あとは表示されたスタートアップフォルダに起動したいアプリケーションのショートカット（スクリプトファイルの場合はファイルそのもの）を格納すれば準備は完了です。
試しに再起動して頂いて、格納したアプリケーションが手動ではなく自動的に起動されれば成功です！

***

これで嫌でも視界に入るので、PCを開く度にタスクの確認などが出来るようになりました。
とはいえ忘れてしまう事もまだあったりするのですが……以前よりかは確認しながら動けるように改善はされたかなと自負しております。

皆さんのタスク管理の良い方法もよろしければ教えて頂きたいです。

***
オブジェクティブグループではXの投稿も平日毎日行っています！
IT関連の小ネタや便利技から、日常のアニメ・ゲーム布教なども幅広く投稿してるので、
ご興味のある方は是非フォロー・いいねをお願いします。
https://x.com/obg_ocr
【Python】長い文字列を改行し、縁取文字として画像にする。

# やること
+ Pythonで文字列を縁取りされた画像にします。改行もします。

# 出力結果

黒色になっていますが背景は透明です。
![tmpvwem4tk3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242971/a18dcd0f-9d9a-4a8a-88f7-21225d0ac55a.png)

# コード

```python 
from PIL import Image, ImageDraw, ImageFont
import textwrap

def draw_multiline_text(texts, font_path, font_size, text_color, max_width=None, outline_width=2, outline_color1=(0, 0, 0), outline_color2=(255, 255, 255)):
    """改行してのテキストを描画する関数

    Args:
        texts (list or str): 描画するテキストの配列または文字列
        font_path (str): フォントファイルのパス
        font_size (int): フォントサイズ
        text_color (tuple): テキストの色 (R, G, B)
        max_width (int, optional): 画像の最大幅px（文字列の場合のみ有効）
        outline_width (int): アウトラインの幅
        outline_color1(tuple): 内側ラインの色 (R, G, B)
        outline_color2(tuple): 外側ラインの色 (R, G, B)

    Returns:
        Image: 描画された画像
    """
    # フォントの読み込み
    font = ImageFont.truetype(font_path, font_size)

    # textsが文字列の場合、textwrap.wrapで配列に分割
    if not isinstance(texts, list):
        if max_width == None:
            max_width = len(texts) * font_size
        texts = textwrap.wrap(texts, width=max_width // font_size)

    # 各行のテキストの描画に必要なサイズを計算
    line_widths = []
    line_heights = []
    for text in texts:
        dummy_draw = ImageDraw.Draw(Image.new("RGB", (0, 0)))
        left, top, right, bottom = dummy_draw.textbbox((0, 0), text, font=font)
        line_widths.append(right)
        line_heights.append(bottom)

    # 全体のテキストのサイズを計算
    max_width = max(line_widths)
    total_height = sum(line_heights)

    # テキストを描画する画像の作成
    image = Image.new("RGBA", (max_width + outline_width * 4, total_height + outline_width * 4), (255, 255, 255, 0))
    draw = ImageDraw.Draw(image)

    # 各行のテキストを描画
    y_offset = outline_width
    # アウトラインの描画 (outline_color2)
    for text, line_height in zip(texts, line_heights):
        for x in range(-outline_width*2, outline_width*2 + 1):
            for y in range(-outline_width*2, outline_width*2 + 1):
                draw.text((outline_width + x, y_offset + y), text, font=font, fill=outline_color2)
        y_offset += line_height

    # アウトラインの描画 (outline_color1) 
    y_offset = outline_width
    for text, line_height in zip(texts, line_heights):
        for x in range(-outline_width, outline_width + 1):
            for y in range(-outline_width, outline_width + 1):
                draw.text((outline_width + x, y_offset + y), text, font=font, fill=outline_color1)    
        y_offset += line_height

    # 文字の描画
    y_offset = outline_width
    for text, line_height in zip(texts, line_heights):
        draw.text((outline_width, y_offset), text, font=font, fill=text_color)
        y_offset += line_height

    return image

# 使用例
if __name__ == '__main__':
    texts = "これは非常に長いテキストの例です。指定された最大幅で自動的に改行されます。これにより、長いテキストを画像内に収めることができます。"
    font_path = r"msgothic.ttc"  # フォントファイルのパスを指定
    font_size = 62
    text_color = (255, 20, 147) 
    outline_width = font_size // 16 

    image = draw_multiline_text(texts, font_path, font_size, text_color, 1000, outline_width)
    image.show()

```
Android Studioが重い時の対処法

# この記事を書くに至ったきっかけ
現在アルバイトとして実務に携わらせてもらっているのですが、割とCompose化で複雑な画面を作るとき、色とかフォントスタイルの定義も参照するのに複数ファイルを開いているとAndroid Studioの画面がやけにもっさりすることが増えました。
また、Previewのビルドが永遠に表示されないことも多々ありました。

環境
　M1 MacBook Air
 　メモリ16GB
 　ストレージ516GB

M1だしメモリも8ではないけれどゆうて16だからちょっと変えてもらわないとかな...と思っていたんですが次に示す方法で普通にもっさりしなくなりました。ちょっとAndroid Studio重いけど何が原因かわからない方はぜひ試してください。

# Android Studioに割り当てるヒープ領域を増やす
はい、割と当たり前ですが設定からできるの普通に知らなかったので同じく知らなかった学生エンジニアのために共有します。
### Android Studio -> Settings(Preferences)...

![スクリーンショット 2025-04-01 21.31.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680408/ce78d7f6-74f7-478f-9e87-f25a3cbefee5.png)

### Appearance & Behavior -> System Settings -> Memory Settings
デフォルトが2048MBとなっているのでこれを4096MBまたはそれ以上に変更してApply、再起動します。

![スクリーンショット 2025-04-01 22.14.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680408/361cbd96-f4ed-441e-a973-f95e5d8cbb15.png)

しっかりIDEガイドの方にも書いてあるのでみている方は知っているのかも↓
https://developer.android.com/studio/intro/studio-config?hl=ja#:~:text=%5BAppearance%20%26%20Behavior%5D%20%3E%20%5B,%E9%81%A9%E7%94%A8%5D%20%E3%82%92%E3%82%AF%E3%83%AA%E3%83%83%E3%82%AF%E3%81%97%E3%81%BE%E3%81%99%E3%80%82
【初心者向け】Pythonで学ぶアルゴリズム入門：二分探索の実装と応用

# 【初心者向け】Pythonで学ぶアルゴリズム入門：二分探索の実装と応用

## はじめに

More: https://www.menuvefiyat.net/

こんにちは、Qiitaで記事を書き始めたばかりのプログラマです。今日はアルゴリズム学習の最初の壁としてよく挙げられる「二分探索（バイナリサーチ）」について、Pythonでの実装方法と実際の応用例を解説したいと思います。

## 二分探索とは？

二分探索は、ソート済みの配列から効率的に要素を検索するアルゴリズムです。線形探索に比べて圧倒的に高速で、計算量はO(log n)となります。

### 基本的な考え方

1. 配列の中央の要素を調べる
2. 目的の値と中央の値を比較
3. 目的の値が中央の値より小さい場合、左半分で探索を継続
4. 目的の値が中央の値より大きい場合、右半分で探索を継続
5. 値が見つかるか、探索範囲がなくなるまで繰り返す

## Pythonでの実装

```python
def binary_search(arr, target):
    left = 0
    right = len(arr) - 1
    
    while left <= right:
        mid = (left + right) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1  # 見つからなかった場合

# 使用例
sorted_list = [1, 3, 5, 7, 9, 11, 13, 15]
target = 9
result = binary_search(sorted_list, target)
print(f"要素 {target} のインデックスは {result} です")
```

## 実践的な応用例

### 1. ライブラリのバージョンチェック

```python
def find_latest_compatible_version(versions, requirement):
    left, right = 0, len(versions) - 1
    result = -1
    
    while left <= right:
        mid = (left + right) // 2
        if versions[mid] >= requirement:
            result = mid
            right = mid - 1
        else:
            left = mid + 1
    return versions[result] if result != -1 else None
```

### 2. ゲーム開発での難易度調整

```python
def find_optimal_difficulty(scores, target_score):
    left, right = 0, len(scores) - 1
    best_diff = 0
    
    while left <= right:
        mid = (left + right) // 2
        if scores[mid] >= target_score:
            best_diff = mid
            right = mid - 1
        else:
            left = mid + 1
    return best_diff
```

## よくあるバグとデバッグ方法

1. **無限ループ**: 終了条件（left <= right）を確認
2. **境界値エラー**: midの計算でオーバーフローしないよう注意
3. **ソートされていない配列**: 事前にソートされているか確認

デバッグ時には以下のprint文を追加すると便利です：

```python
print(f"left: {left}, right: {right}, mid: {mid}, arr[mid]: {arr[mid]}")
```

## パフォーマンス比較

100万要素のリストから要素を検索する場合：

- 線形探索: 平均500,000回の比較
- 二分探索: 最大20回の比較

## まとめ

二分探索は単純ながら強力なアルゴリズムです。実装自体はシンプルですが、応用範囲が広く、競技プログラミングや実際の開発現場でも頻繁に使用されます。ぜひマスターして、より効率的なコードを書けるようになりましょう！

次回は「二分探索の応用：平方根の計算と精度調整」について解説する予定です。お楽しみに！
MastraでPlaywright-mcpを活用した汎用WEB検索ＡＩエージェントを作ろう

## ニケちゃんさんのＮｏｔｅに、MastraでMCP連携できるAIエージェントを作ろうという記事があります。
https://note.com/nike_cha_n/n/nbc7696f06eb8

とても分かりやすくまとめてくださっているので、参考にしました。

この記事を参考にして、Geminiを使えるようにして、Playwright-mcpも使えるようにしました。

私が改造したアプリのデモはこちらです。
https://www.youtube.com/watch?v=5iB8YE__UsQ


### /////////////////////
### Playwright-mcp Agent　汎用WEB検索ＡＩエージェント
### /////////////////////
### 【プロンプト１】
東京都　株式会社　お問い合わせ　で、DuckDuckGo("https://duckduckgo.com/?q=")で検索します。「結果を更に表示」ボタンをクリックし、スナップショットを取得し、リンク先のURLを抽出して、配列で表示します。

### 【プロンプト２】
https://www.???
このURLにアクセスします。メールアドレスがありそうなページをリストアップし、そこへアクセスします。
bodyからメールアドレスを探します。aタグからもメールアドレスを探します。見つかれば、jsonで表示します。なければnullで表示します。
    \`\`\`json
    {      "email": "...",    }
    \`\`\`


### 【プロンプト３】
info@sample.com
このメールアドレスの@の右側をドメインとします。
ドメインをDuckDuckGo("https://duckduckgo.com/?q=")で検索します。
検索結果からWEBサイトURL、会社名、業種、電話番号、SNSのURL、社長名、開業日、資本金、会社概要を抽出します。
ドメインと関係が一番深いと確定できる１件だけに絞ります。
特定できないものは、nullでお願いします。
結果は、jsonで表示します。
    \`\`\`json
    {
      "email1": "...", // 入力されたメールアドレス
      "company": "...",
      "url": "...",
      "email2": "...", // 入力されたメールアドレス
      "industry": "...",
      "tel": null,
      "sns_url": null, 
      "representative": null,
      "established": null, 
      "capital": null, 
      "description": null
    }
    \`\`\`


### /////////////////////
### Search and Get Email Agent　Eメール収集専用ＡＩエージェント
### /////////////////////

### 【プロンプト】
東京都　株式会社　お問い合わせ　で検索し、emailを配列で表示します


### /////////////////////
### mastraアプリの作り方
### /////////////////////
私はニケちゃんさんのアプリを改造して自分のアプリを作りました。
Playwright-mcp Agentをどのように作ったかは、Geminiとの会話内容をPDFにしたので、興味のある方はご確認ください。

改造の際に必要となった情報は以下の通りです。
１．ニケちゃんさんのソースコード
https://github.com/tegnike/mastra-mcp
２．ソースコードを１つのテキストファイルにまとめるツール（上記１のsrcフォルダに入れて実行すると３ができます。）
３．ソースコードまとめたテキストファイル（これをGeminiに投げた）
４．Playwight-mcpの公式ドキュメントのテキストファイル（これもGeminiに投げた）
５．上記のGeminiとの会話内容のPDF（一部PDFにならなかった部分のみテキスト）
以下に、全てのファイルを上記と同じ順番でシェアしてます。
https://app.box.com/s/uegk8qukendzd3ry5pfw74p53tiinkhy

私のパソコンの環境はWindows11です。

以上
【Next.js】next/imageの画像がローカルで読み込めていたのに本番環境では404となり読み込めなくなった

## 概要

Next.js で開発中に画像が正常に表示されるのに、本番環境にデプロイすると画像が表示されない問題に直面。
`next export` を使用して静的サイトとしてデプロイする場合、デフォルトの `next/image` の最適化機能が影響を与える可能性があるらしいとのことで、試してみたら解決したので記事にします

## 画像が表示されない問題

### **現象**

Next.js で開発環境 (`npm run dev`) では、以下のようなコードで画像を表示できる。

```tsx
import Image from "next/image";

export default function MyComponent() {
  return (
    <div>
      <Image src="/images/main_image.jpg" alt="Main Image" width={1080} height={720} />
    </div>
  );
}
```

このコードは、ローカル環境では正常に画像を表示できるが、本番環境にデプロイすると、`/_next/image?url=...` という URL で画像を取得しようとするものの、404 エラーが発生し、画像が表示されない問題が発生した。

### **確認したこと**

1. `public/` 内に `images/main_image.jpg` があるか確認 → **存在する**
2. `/_next/static/media/` に画像が配置されているか確認 → **ない**
3. `next.config.js` の設定を見直し → **デフォルト設定のまま**
4. `next/image` の最適化が影響している可能性を調査

この結果、`next export` を使用したビルドでは `next/image` の最適化機能がデフォルトで有効になっており、それが原因で画像が正しく表示されていないことが判明した。

## 解決方法

### **原因**

Next.js では、`next/image` はデフォルトで Vercel の画像最適化機能を使用するようになっているいっぽうで、`next export` を使用して静的サイトをデプロイすると、この最適化機能は使用できなくなるっぽい。

### **対策**

この問題を解決するためには、`next.config.js` に `unoptimized: true` を設定し、Next.js の画像最適化を無効化するとよさそうだとわかった。

#### **1. **next.config.js** を編集**

`next.config.js` に以下の設定を追加。

```javascript
module.exports = {
  images: {
    unoptimized: true, // 画像の最適化を無効化
  },
};
```

#### **2. ビルドとデプロイ**

この設定を追加したら、次のコマンドでビルドをやり直す。

```sh
npm run build
npm run export
```

これをデプロイすると、正しく表示できました！
JavaScriptの落とし穴: 数値0がfalsy扱いされることによるバリデーションバグ

JavaScriptの落とし穴: 数値0がfalsy扱いされることによるバリデーションバグ
発生した問題
フォームから選択された account_role の値（0：選手）が正しく処理されず、「アカウントタイプを入力してください」というバリデーションエラーが発生していました。
原因
JavaScriptでは数値の 0 は falsy 値として扱われます。そのため、バリデーション関数内で以下のようなコードがあると問題が発生します：

if (!formData.account_role) {
  // エラーを設定
  errors.account_role = "アカウントタイプを入力してください";
}

ユーザーが選手（account_role = 0）を選択した場合、0 は falsy と評価されるため、この条件は true となり、値が存在しないと誤って判定されてしまいます。

解決策
以下のいずれかの方法で解決できます：
厳密な比較を使用する

// 未定義またはnullの場合のみエラーを出す
if (formData.account_role === undefined || formData.account_role === null) {
  errors.account_role = "アカウントタイプを入力してください";
}

型変換してから確認する

// 数値型に変換してから条件をチェック
const roleValue = parseInt(formData.account_role, 10);
if (isNaN(roleValue)) {
  errors.account_role = "アカウントタイプを入力してください";
}

存在確認を適切に行う

// プロパティの存在を確認
if (!Object.prototype.hasOwnProperty.call(formData, 'account_role')) {
  errors.account_role = "アカウントタイプを入力してください";
}

サーバーに送信する前の処理
バックエンド側に送信する際は、型をチェックしてから処理しましょう：

// 送信前に型チェック
if (typeof formData.account_role === 'string') {
  formData.account_role = parseInt(formData.account_role, 10);
}

avaScriptのfalsy値について
JavaScriptでは以下の値がfalsy（偽と評価される）として扱われます：

false
0（数値のゼロ）
""（空文字列）
null
undefined
NaN

これらの値に対して論理否定演算子（!）を使用すると、true が返されます。そのため、値の存在チェックを行う際は、この点に注意が必要です。
まとめ
JavaScriptでのフォームバリデーションを実装する際は、数値の 0 が有効な値である場合、単純な ! による存在チェックではなく、undefined や null との厳密な比較、または適切な型変換を行いましょう。このような細かい落とし穴に気をつけることで、ユーザー体験を損なうバグを防ぐことができます。
C# 非同期処理について

# はじめに
C#の学習を進めていくにあたり、非同期処理は壁に感じる人は多いのではないか(多分)

自分も当初、理解に苦しんだこともあり、初学者向けに簡単に解説しておきたい。

# 非同期処理とは

非同期処理とは、時間のかかる処理を実行する際に、プログラム全体の動作を止めることなく並行して処理を進める仕組みである。
特に、UI アプリケーションにおいては、重い処理（例: データベースアクセスやネットワーク通信）を同期的に実行すると、処理が完了するまでUIがフリーズしてしまう。
しかし、非同期処理を使うことで、UIをブロックせずにスムーズな動作を維持できる。

# C#における非同期処理

非同期処理を文章だけで理解してもらうのは困難だと思ったので図を作ってみた↓

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935725/e1cf6e96-a29b-4080-b6fd-2ff43d6bb075.png)


## 図左側：同期処理
- 「main」内で処理①から順番に処理④まで実行される

- 各処理が順番に実行され、前の処理が完了するまで次の処理は開始されない

- 処理②（重い処理） も他の処理と同じ流れで順番に実行されるため、処理②の完了を待つ間、他の処理は停止する

## 図右側：非同期処理
- 「main」内で処理①が実行され、その後すぐに処理③と処理④が実行される

- 処理②（重い処理） は async により別スレッドで実行され、await を使用することで「main」にTask型のデータを返し、処理②の完了を待たずに他の処理を進めることができる

- Task 型を使用することで非同期に処理を進めつつ、結果が必要なタイミングで待機することが可能

## 戻り値のない非同期メソッドの例

説明はコメントアウトに記載しています。

``` example.cs
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("非同期処理を開始します...");
        
        // 非同期メソッドを呼び出す
        DelayAsync(3000); // 3秒待つ
        
        // 非同期メソッドの完了を待たず実行
        Console.WriteLine("他の処理を実行中...");

        // 非同期処理の完了を待つ
        Console.ReadLine(); // 非同期処理が完了するのを待機（待たないと非同期メソッドが完了する前に処理が終了してしまう）
    }

    // 戻り値なしの非同期メソッド
    static async Task DelayAsync(int milliseconds)
    {
        await Task.Delay(milliseconds); // 指定されたミリ秒だけ待つ
        Console.WriteLine("処理が完了しました!");
    }
}
```

## 返り値がある非同期メソッドの例

``` sample.cs
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("非同期処理を開始します...");
        
        // 非同期メソッドを呼び出す
        Task<string> task = DelayAsync(3000); // 3秒待つ
        
        // 非同期メソッドの完了を待たず実行
        Console.WriteLine("他の処理を実行中...");

        // 非同期処理が完了するのを待機（待たないと非同期メソッドが完了する前に処理が終了してしまう）
        string result = task.Result; // 非同期メソッドの戻り値を取得
        
        Console.WriteLine(result);
    }

    // 非同期メソッド
    static async Task<string> DelayAsync(int milliseconds)
    {
        await Task.Delay(milliseconds); // 指定されたミリ秒だけ待つ
        return "処理が完了しました!";
    }
}
```

| **キーワード**  | **使う場所** | **説明** |
|-----------------|--------------|----------|
| `Task`          | メソッドの戻り値の型として | 非同期メソッドが返す型。戻り値なしなら `Task`、戻り値ありなら `Task<T>` を使う。 |
| `async`         | メソッド定義の前 | 非同期メソッドとして定義するために使う。必ず戻り値の型の前に付ける。 |
| `await`         | 非同期メソッド内で非同期タスクを待つ | 非同期タスクの完了を待機するために使う。`Task` を返すメソッドでのみ使用可能。 |
Excel / PDFに検索エンジンつける


<h3>code</h3>

```ruby: ✅ 

from whoosh.index import open_dir
from whoosh.qparser import QueryParser
from whoosh.searcher import MultifieldSearcher


schema = Schema(content=TEXT(stored=True))

ix1 = create_in("悩み", schema)  
ix2 = create_in("転職活動", schema) 
writer = ix.writer()

# 複数のインデックスを開く
kaiix1 = open_dir("悩み")
kaiix2 = open_dir("転職活動")

for text in text_data:
    writer.add_document(content=text)
writer.commit()

# 検索
query = QueryParser("content", ix.schema).parse('検索ワード')
with ix.searcher() as searcher:
    results = searcher.search(query)
    for result in results:
        print(result['content'])


# 検索条件が二つの時
searcher = MultifieldSearcher([kaiix1.searcher(), kaiix2.searcher()])

# 検索
query = QueryParser("content", ix1.schema).parse("検索ワード")
results = searcher.search(query)

for result in results:
    print(result['content'])
```

<h3>いるもん</h3>

```ruby: ✅ 
    pip install whoosh
```

<h3>Tkインスト</h3>

```ruby:
CFLAGS="-I$(brew --prefix tcl-tk)/include" \
LDFLAGS="-L$(brew --prefix tcl-tk)/lib" \
pyenv install --force 3.13.1
```
<h3>Tkあるか確認</h3>

```ruby:☑️
import tkinter as tk
root = tk.Tk()
root.mainloop()
```

<h3>Tkないと出るエラー</h3>

![スクリーンショット 2025-04-01 21.33.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628823/3d0bfe83-69b6-4c7e-8e28-6936fd1d3281.png)

<h3>インスト</h3>

![スクリーンショット 2025-04-01 22.00.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628823/6a30d599-0ca4-4799-8e76-c89f580f442d.png)



<br>
<h3>GUI作成</h3>

```ruby: Tk
import tkinter as tk
from tkinter import messagebox

def search():
    search_query = entry.get()
    # Whoosh検索コードをここで実行
    # 例: results = whoosh_search_function(search_query)
    messagebox.showinfo("検索結果", "検索結果を表示")

window = tk.Tk()
window.title("検索エンジン")

label = tk.Label(window, text="検索:")
label.pack()

entry = tk.Entry(window)
entry.pack()

button = tk.Button(window, text="検索", command=search)
button.pack()

window.mainloop()
```

```ruby: Pyqt 
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QLineEdit

class SearchWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('検索エンジン')

        self.layout = QVBoxLayout()

        self.search_input = QLineEdit(self)
        self.layout.addWidget(self.search_input)

        self.search_button = QPushButton('検索', self)
        self.layout.addWidget(self.search_button)

        self.setLayout(self.layout)

        self.search_button.clicked.connect(self.search)

    def search(self):
        search_query = self.search_input.text()
        # Whoosh検索コードをここで実行
        # 例: results = whoosh_search_function(search_query)
        print(f'検索結果: {search_query}')

app = QApplication([])
window = SearchWindow()
window.show()
app.exec_()

```

<h3>TkとPyqtの違い</h3>

tk : 軽量 複雑な実装はできない / 簡単

Pyqt
複雑レイアウト、データベースと連携、グラフ描画 / ちょいむずい


権限追加

chmod 644 /path/to/url.json