OmniAICreator/Qiita-1.07M huggingface dataset:
音声AIで切り拓く未来：リアルタイム応答システムの挑戦

### はじめに

生成AIの技術革新が進む中、音声インターフェースの活用が注目されています。特に、ハンズフリーでの情報取得は業務効率化に貢献すると考えられます。本記事では、社内AIツール開発の一環として取り組んだ「音声で質問し、ChatGPTとGeminiがリアルタイムで音声回答するシステム」の開発経緯や技術的課題、今後の展望について詳しく紹介します。

### 開発の背景

近年、生成AIの進化が著しく、ChatGPTやGeminiなどの技術が急速に普及しています。その中で、ハンズフリーでAIを活用し、効率的に情報を取得できる仕組みの必要性を感じました。

そこで開発したのが、**「音声で質問すると、ChatGPTとGeminiがリアルタイムで音声で回答してくれるシステム」** です。本システムにより、文字入力が苦手な方や作業中のユーザーが、より直感的にAIと対話できるようになります。

---

### アプリの概要

本システムでは、ユーザーが音声で質問を行うと、以下の流れで処理が進みます。

1. **リアルタイムで音声をストリーミング送信**
2. **ChatGPTとGeminiがそれぞれ回答を生成**
3. **生成された回答を音声化して返答**



#### 主な機能
- **音声入力** ：ユーザーが音声で質問可能
- **リアルタイム処理** ：ストリーム形式でデータを送信し、即座にAIが回答
- **音声合成** ：生成された回答を音声で提供
- **ChatGPT & Geminiの比較** ：異なるAIの回答を比較

これにより、手が塞がっている状況でもスムーズに情報を取得することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3966336/02fb5002-4317-ca53-4a30-5bf811f0dcaa.png)


---

### 技術スタック

#### バックエンド
本システムのバックエンドは、リアルタイム通信と音声処理を円滑に行うために以下の技術を採用しています。
- **Tornado** ：非同期処理を活用した軽量なWebサーバーフレームワーク。
- **Azure Cognitive Services Speech** ：音声認識および音声合成のためのMicrosoft Azureのサービス。
- **OpenAI API** ：ChatGPTを利用した自然言語処理。
- **Google Generative AI** ：Gemini APIを活用したテキスト生成機能。
- **Streamlit** ：簡単な管理画面を提供するためのフレームワーク。
- **JsonSchema、Psutil** ：データのバリデーションやシステムリソース管理を補助。
- **WebSocket** ：リアルタイム通信の実現に使用。

#### フロントエンド
- **React** ：軽量で動的なインターフェースを構築し、ユーザーが直感的に利用できる環境を提供。
- **WebSocket** ：リアルタイムデータの送受信を実現。

この技術構成により、音声入力から回答生成、音声合成までをスムーズに統合し、高速なレスポンスを実現しました。

---

### 開発で難しかったこと

今回の開発では、以下の点に課題がありました。
- **Callbackの適切な活用**
- **WebSocket経由で送信した音声がAzure Speech to Textで認識されない問題**
- **ディレクトリ構成の整理（可読性・スケーラビリティの向上）**

特に、リアルタイム音声処理の実装においては多くの試行錯誤が必要でした。しかし、先輩の清水さんのサポートを受けながら、WebSocketの適切な実装方法や非同期処理の設計について学び、最適な構成を実現しました。

---

### 開発を通じて得た知見

- **ChatGPTとGeminiの回答の違いが興味深い**
  - それぞれのAIが異なる視点で回答を生成し、比較することで有用な情報を得ることができました。
- **音声合成技術の精度向上**
  - 期待以上に自然な音声が生成され、実用性の高いシステムとして活用可能なレベルに到達しました。

---

### 今後の展望

現在の課題として、**レスポンス速度の遅延** が挙げられます。

今後の改善点として、
- **音声認識・生成の処理速度向上**
- **リアルタイム性のさらなる強化**

を検討しており、より快適なユーザー体験を目指します。

また、将来的には **多言語対応** や **業務向けカスタマイズ** などの機能拡張も視野に入れています。

---

### まとめ

本プロジェクトを通じて、
- **リアルタイム音声処理の実装**
- **AI音声合成の活用**
- **非同期処理の最適化**

といった技術的な課題に取り組み、多くの学びを得ることができました。

今後も改善を重ね、より使いやすく高度なシステムを構築していきます。

引き続き、より快適なAI体験を提供できるよう努めてまいります！
laravelのstancl/tenancyで詰まったところ

## assetのパスについて
なぜか通常のようにassetが使えなかった。
tenancyを入れると標準でassetがtenancy用になっているらしい。
`config/tenancy.php`ファイルの下記部分が影響しているとのこと。
```
 'asset_helper_tenancy' => true,
 から
  'asset_helper_tenancy' => false,
 に変更
```
---
## ルーティングについて
非テナント(central-domain,non tenant)のルーティング(web.php)をいくらいじっても動かなかった。
いつの間にか要らないものを設定していたらしい。
`kernel.php`の`middlewereGrop`のwebを確認
```
            // \App\Http\Middleware\InitializeTenancyByDomainWithCentralCheck::class,
            // \Stancl\Tenancy\Middleware\PreventAccessFromCentralDomains::class,
```

`RouteServiceProvider.php`にこれが書いてあるかも確認
```
    public function boot(): void
    {
    何か書いてあるものは消さない
        $this->routes(function () {
        何か書いてあるものは消さない
            $this->mapWebRoutes();<-追記
            $this->mapApiRoutes();<-追記
        });
    }
protected function mapWebRoutes()
{
    foreach ($this->centralDomains() as $domain) {
        Route::middleware('web')
            ->domain($domain)
            ->namespace($this->namespace)
            ->group(base_path('routes/web.php'));
    }
}

protected function mapApiRoutes()
{
    foreach ($this->centralDomains() as $domain) {
        Route::prefix('api')
            ->domain($domain)
            ->middleware('api')
            ->namespace($this->namespace)
            ->group(base_path('routes/api.php'));
    }
}

protected function centralDomains(): array
{
    return config('tenancy.central_domains', []);
}
```

## リクエスト データ (ヘッダーまたはクエリ パラメータ) に基づいてテナントを識別する方法
これがドキュメント等が全くない
ルーティングファイルに下記を設定しておけば出来てそう（確証はない）
```
use Stancl\Tenancy\Middleware\InitializeTenancyByRequestData;

Route::middleware([
    'api',
    InitializeTenancyByRequestData::class,<-これ
])->group(function () {
    Route::get('/', function () {
        // dd(\App\Models\User::all());
        return 'This is your multi-tenant application. The id of the current tenant is ' . tenant('id');
    });
});
```
`curl -H "X-Tenant: tenantsテーブルにある任意のid" http://自分のドメイン/api`
これでちゃんと返っては来ていた
getchar使ってみた

```getchar.cpp
#include <stdio.h>	

int main(void)
{
	int c;
	int count=0;

	while ((c = getchar()) != EOF) {

		c = getchar();
			count++;
	}

	printf("%d", count);
	
	return 0;
}
```

文字を入力
文字を数える
NextUIコンポーネントのProps拡張テクニック

# はじめに
NextUIのコンポーネントを使用するとき、独自のプロパティを追加したくなることがありませんか？🥺

そんなとき、各コンポーネントのProps型を拡張する方法が便利です。

今回はButtonコンポーネントを例に説明します。

# ❌ 私がやってしまった間違った実装

```ts
import { Button as NextUIButton } from "@nextui-org/react";

// 独自のプロパティだけを定義
type ButtonProps = {
  isProcessing?: boolean;
  children: React.ReactNode;
};

// これだとNextUIのプロパティが使えない！
export const CustomButton = ({
  isProcessing,
  children,
}: ButtonProps) => {
  return (
    <NextUIButton
      disabled={isProcessing}
    >
      {isProcessing ? "処理中..." : children}
    </NextUIButton>
  );
};
```

# ✅ 正しい実装例
```ts
import { Button as NextUIButton, ButtonProps as NextUIButtonProps } from "@nextui-org/react";

// コンポーネントのPropsを継承して独自のプロパティを追加
type CustomButtonProps = NextUIButtonProps & {
  isProcessing?: boolean;
};

export const CustomButton = ({
  isProcessing,
  children,
  ...props
}: CustomButtonProps) => {
  return (
    <NextUIButton
      {...props}
      disabled={isProcessing || props.disabled}
    >
      {isProcessing ? "処理中..." : children}
    </NextUIButton>
  );
};
```

# 使用例
```ts
// NextUIの標準プロパティが使える！
<CustomButton 
  color="primary"  // OK
  size="sm"        // OK
  variant="flat"   // OK
>
  クリック
</CustomButton>

// 独自プロパティも使える！
<CustomButton
  color="primary"
  isProcessing={true}
>
  送信
</CustomButton>
```

# なぜProps拡張が便利なのか？
1. NextUIが提供する標準のプロパティがそのまま使える
2. 独自のプロパティを型安全に追加できる

# 他のコンポーネントでも同じように使える
この手法は、他のNextUIコンポーネントでも同様に適用できます。

```ts
import { InputProps as NextUIInputProps, ModalProps as NextUIModalProps, CardProps as NextUICardProps } from "@nextui-org/react"

// Input
type CustomInputProps = NextUIInputProps & {
  customValidator?: (value: string) => boolean;
};

// Modal
type CustomModalProps = NextUIModalProps & {
  showCloseButton?: boolean;
};

// Card
type CustomCardProps = NextUICardProps & {
  isHighlighted?: boolean;
};
```

# まとめ
NextUIのコンポーネントのPropsを継承することで、既存の機能を維持しながら、簡単に独自の拡張ができます。

独自のPropsだけを定義してしまうと、NextUIの便利なプロパティが使えなくなってしまうので注意が必要であることを身をもって体感しました...😣
AWS SAPの事前対策備忘録

# AWS SAPの概要
レベル: プロフェッショナル
時間: 試験完了までに 180 分
コスト: 300 USD
フォーマット: 75 個の質問 (複数選択または複数応答のいずれか)。
配信方法: Pearson VUE テストセンターまたはオンラインでの監督付き試験
参考：https://aws.amazon.com/jp/certification/certified-solutions-architect-professional/

# 使用した書籍
・AWS認定資格試験テキスト＆問題集　AWS認定ソリューションアーキテクト - プロフェッショナル
[URL](https://www.amazon.co.jp/dp/4815617929?tag=nokonokonoko0-22&linkCode=ogi&th=1&psc=1)

・AWS認定ソリューションアーキテクト-プロフェッショナル ~試験特性から導き出した演習問題と詳細解説
[URL](https://www.amazon.co.jp/AWS%E8%AA%8D%E5%AE%9A%E3%82%BD%E3%83%AA%E3%83%A5%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%88-%E3%83%97%E3%83%AD%E3%83%95%E3%82%A7%E3%83%83%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%AB-%E8%A9%A6%E9%A8%93%E7%89%B9%E6%80%A7%E3%81%8B%E3%82%89%E5%B0%8E%E3%81%8D%E5%87%BA%E3%81%97%E3%81%9F%E6%BC%94%E7%BF%92%E5%95%8F%E9%A1%8C%E3%81%A8%E8%A9%B3%E7%B4%B0%E8%A7%A3%E8%AA%AC-%E5%B9%B3%E5%B1%B1-%E6%AF%85/dp/4865942483
)
※現在はSAP-C02なのでAWS認定資格試験テキスト＆問題集の方が現在の問題に沿った内容になっている

# 使用した問題集
・Udemy
[URL](https://www.udemy.com/)
SAPに関してはUdemyより問題集を購入してひたすら問題を解いておりました。

・CloudTech
[URL](https://kws-cloud-tech.com/)
SAPでは使用しませんでしたがSAP-C02にも対応しており、他の試験も学習できるので
他の試験も視野に入れておられる方はおすすめかと思います

# 試験時に意識した事
・新しく提供されたサービスで上記の問題集でよく見るものは実際の問題でもいくつか出題傾向にある
例)S3 Storage Lens、Transfer Acceleration、Compute Optimizer等
・何を目的にしてるかで回答が変わるので問題の最後の部分は特に確認しておく事
　→スピードなのかコストなのか可用性なのか等
・基本的に回答群から消去法で考える
　→問題を見て全く内容が理解できないときはとにかく回答群から消去する事で正解率の確率を上げる
 　ちゃんと回答群を見ると結構あり得ない内容があったりするのでそれを消去して二択まで持っていく事を意識する
・問題文が長くてよくわからないときはホワイトペーパーに構成を簡単に纏めながら読んでいく

# 最後に
個人的には問題を多く説く事で知識をつけて問題になれる事が合格への近道でした。
今は書籍も問題数も多くございますので参考にして受けていただければとおもいます。
fitbitの睡眠ログをPDFに出力するサービスを作った

# 概要
fitbitの睡眠ログをPDFにするサービスを作りました。

サービス: https://sleep-logs-pdf.onrender.com/
# 使用技術
| 領域 | 技術 |
|:-----------|------------:|
| フロントエンド      | Vue.js       | 
| バックエンド     | PHP(Laravel)      | 

# 使い方
出力したい年月を入力してsubmitします。
fitbitアカウントでログインすると、次のようなPDFが出力されます。
![Screenshot from 2025-01-30 07-52-27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3586658/d131d95d-c335-46d9-5838-ce7683c2a88a.png)

以上です。
【勉強会用】バッチファイルとシェルスクリプトについて

# 概要
本投稿は、勉強会で使用するためかつ、OUTPUT用の記事である。
以下題材を用いる。
・バッチファイル
・シェルスクリプト

## バッチファイル
### バッチファイルとは
　**Windows**の環境で実施することができるコマンドを羅列したテキストファイルのことを指す。
　拡張子は「*.bat」

### 操作と作成方法
##### ①テキストファイルの拡張子を変更する。
　テキストエディタ、メモ帳などを使用して、コマンドを記述し、拡張子を「.bat」に変更して、保存する。
##### ②実行中のコマンドを非表示とする。
　テキストの冒頭に「@echo off」と記述する。これは、それ以下に記述されるコマンドをすべて画面に表示させないコードである。この記述がなければ、処理を実行するたびにコマンドが画面に表示されることになる。
     
     ★設計メモ
     　「@」は「行内のコマンドを非表示にする」記述、そのため「echo off」だけでは、「echo off」だけが残
      ってしまう。
      確認が必要な処理はあえて、コマンドを消さない場合もある。
      (例)デバッグ時など。
##### ③環境変数を設定する。
　「setlocal」と記述し、それ以下に環境変数（OSが管理している変数）(※1)を記述する。

     ★設計メモ
     単に環境変数を設定するだけであれば、「set」でも可能
     「set」ではそこで定義した環境変数が、ほかのバッチファイルにも及ぶ恐れがある。

 >(※1)環境変数については、以下投稿を参照とする。
> https://qiita.com/jeyei/items/2c385e4e0488a5fa2591
##### ④コメントを記述する。　
　「rem」と記述した後ろに入力した文章はコメントとして扱われ、処理の実行には一切影響しない。
 　関数のように、意図関数、ヘッダー行を残すとメンテナンスがしやすい。
##### ⑤ファイル名を指定して実行する。
　作成したバッチファイルは、コマンドプロンプトからファイル名を指定するだけで実行可能。
 ※ただし、カレントディレクトリに存在するバッチファイルのみのため、パスの確認はしておくこと。
    
    ★設計メモ
    通常、バッチファイルをUIで管理者として実行、またはダブルクリックに

##### ⑥コマンドライン引数を指定または参照するよる実行を行うことが通説。
　コマンドライン引数を指定することで、バッチファイル実行時にプログラムに対して指定した値を渡すことが可能。
 >構文
 >■ファイル名.bat 引数１ 引数２ 引数３ ……

 ###  使用例
 #### ①ファイルの移動
 あらかじめ移動するパスが決まっているものをバッチファイルをたたくことで移動させることが可能。
 (例)ビルドで生成したbinファイルをしてパスに移動させる。

 #### ②プログラムの起動
 プログラムファイルを指定し、バッチファイルを叩くことでプログラムを起動することができる。
 (例)統合開発環境の起動、ビルド、バイナリファイルの移動をすべてバッチファイルで一括で行う。

 #### ③シェルスクリプトと連携
 Linux環境と連携し、ファイルの移動やシェルスクリプトの実行が可能。

 ### メリット/デメリット
 　バッチファイルにおける、メリット/デメリットの一例を紹介する。
 |メリット  |デメリット  |
|---|---|
|作業の自動化  |作成コスト  |
|人為的なミス削減  |動作確認  |
|作業時間外でも実行可能  |メンテナンス性  |
|ー  |CPUへの負荷  |

## シェルスクリプト
### シェルスクリプトとは
　**UNIX**環境で一連の手続きをファイルに書いておいて自動実行するもの。
 
     ★設計メモ
     ・UNIX・・・現存するOSの中で最も古いものの一つ。
     ・シェル・・・CLI（コマンドラインインターフェース）という、
                コンピュータを操作するためのインターフェースとなる黒い画面上で動くソフトウェアの一種
                ユーザーが入力したコマンドを受け取ってOSの中核「カーネル」へと送る、
                いわばユーザーとOSの橋渡しのような役割をするもので、
                コマンドと言われる命令文を使ってOSを操作する

### 操作と作成方法
##### ①テキストファイルの拡張子を変更する。
　テキストエディタ、メモ帳などを使用して、コマンドを記述し、拡張子を「.sh」などに変更して、保存する。
##### ②実行する処理系の指定
　shebangと呼ばれる、どのシェルによって実行されるかを宣言するための文字列を先頭行に記述する
    
    （例：「#!/bin/bash」）
 
##### ③実行権限付与
　作成したシェルスクリプトを実行する方法には、
- 実行権限を付与せず各種コマンドなどで実行する
- 実行権限を付与してコマンドとして直接実行する
の2つがある。
    
    >$ chmod +x test.sh または $ chmod 755 test.sh


##### ④コメントを記述する。　
　シェルスクリプトのコメントは ***#***

##### ⑥引数とオプション
　引数は実行時にプログラム内で参照され、シェルスクリプトの実行結果 (動作) に影響を与える。引数はパラメータと呼ばれることもある。
 
    command 引数
    command 引数1　引数2　…　引数n

　オプションは -f などのように - (ハイフン) とアルファベット1文字で表記され、主にコマンドの振る舞い方を指定する目的で使用されるものである。

また、オプションは -a -l といった複数指定を -al と一つにまとめて指定することもできる。引数に - が含まれる場合には「コマンド名　--　-引数」といったように -- を指定することで、コマンドは -- 以降の -引数 の部分をオプションではなく引数として解釈する。

 ###  使用例
 #### ①ファイルの移動
 ※バッチファイルと同じ内容のため、省略

 #### ②プログラムの起動
 ※バッチファイルと同じ内容のため、省略。

 #### ③バッチファイルと連携
 Windows環境と連携し、ファイルの移動やバッチファイルの実行が可能。

 ### メリット/デメリット
 　バッチファイルにおける、メリット/デメリットの一例を紹介する。
 |メリット  |デメリット  |
|---|---|
|作業の自動化  |処理速度が遅い  |
|人為的なミス削減  |動作確認  |
|作業時間外でも実行可能  |メンテナンス性  |

以上
Box Drive用のパスを取得するブックマークレットを作った

## はじめに
Boxを使った案件の場合、**[「Box Drive」](https://www.box.com/ja-jp/drive)** という公式に提供されているアプリケーションがとても便利です。  

ざっくり説明すると、エクスプローラーから直接Boxのファイルを参照したり開いたり編集したり同期したり出来る代物。  
いちいちダウンロード→アップロードをする必要もありませんし、バカ使いにくいExcel Onlineを使う必要もありません。  
（他の人と並行して編集する事はできませんが）

ただ、一つ欠点があります。  

それは、**「基本的にファイル連携時はBoxのURLで渡される」** ということです。

なので、URLからboxを開いてー上のところのファイルパスを確認してーそれに合わせてエクスプローラーを開いていってーという手間がかかります。  
めんどい。

必要な情報はブラウザの画面上にすべて出ているので、ブックマークレットで取得しちゃいましょう。

## 内容
[Boxで開いているフォルダをBoxDrive(Finder)で開きたい時に便利なブックマークレット](https://gist.github.com/t3yamoto/9b19180571e81fc7cccaea429be3349b)

散々偉そうなことを上で書きましたが、既に作ってくださっている方がいました。  
ただ、僕の環境だとうまく動かなかったりなんだりあったので、少し改修しました。

```javascript
javascript: {
    // 画面上に表示出来ているフォルダパスを取得して、全部合わせてクリップボードにコピー
	const getDisplayPathAndCopy = (path) => {
		path += [...document.querySelectorAll(".ItemListBreadcrumb-listItem")]
            .map((menuItem) => menuItem.innerText)
            .filter((innerText) => innerText !== "" && innerText !== "すべてのファイル")
            .join("/");
		navigator.clipboard.writeText(path);
		document.body.click(); // 省略されたフォルダパスを戻しておく
	};

	let path = "%USERPROFILE%/Box/";
	const dotButton = document.querySelectorAll(".ItemListBreadcrumb > button")[0];

    // 省略されているフォルダパスが存在する場合
	if (dotButton) {
		dotButton.click();
        // クリックした後に表示されるものが同一トランザクションだと取得出来ないので、setTimeoutで少しだけずらす
		setTimeout((path)=>{
			path += [...document.querySelectorAll("a[data-resin-target='openfolder'].menu-item")]
            .map((menuItem) => menuItem.innerText)
            .filter((innerText) => innerText !== "すべてのファイル")
            .join("/");

			if (!path.endsWith("/")) path += "/";

			getDisplayPathAndCopy(path);

		}, 100, path);
	} else {
		getDisplayPathAndCopy(path);
	}
}
```

これをminifyしたコイツ
```
javascript:{const getDisplayPathAndCopy=(path)=>{path+=[...document.querySelectorAll(".ItemListBreadcrumb-listItem")].map((menuItem)=>menuItem.innerText).filter((innerText)=>innerText!==""&&innerText!=="すべてのファイル").join("/");navigator.clipboard.writeText(path);document.body.click()};let path="%USERPROFILE%/Box/";const dotButton=document.querySelectorAll(".ItemListBreadcrumb > button")[0];if(dotButton){dotButton.click();setTimeout((path)=>{path+=[...document.querySelectorAll("a[data-resin-target='openfolder'].menu-item")].map((menuItem)=>menuItem.innerText).filter((innerText)=>innerText!=="すべてのファイル").join("/");if(!path.endsWith("/"))path+="/";getDisplayPathAndCopy(path)},100,path)}else{getDisplayPathAndCopy(path)}}
```
がURLとして設定されているブックマークを用意したら、後は呼び出すだけでクリップボードにフォルダパスがコピーされます。

## おわりに
あとはBox Driveから直接BoxのURLを取得出来るようになってくれれば文句なしなのですが、そっち側はどうしようもないので諦めます。  
ちょっと生きやすくなりました。
TypeScriptのプロジェクトにJestをインストールする

## はじめに

今回はnpmを利用したインストール方法をまとめてみます

## インストール
```
npm install --save-dev jest @types/jest ts-jest
```
※プロジェクト直下に`jest.config.js`が作成されていることを確認

## npm testコマンドを定義

```package.json
  "scripts": {
    "test": "jest"
  }
```
これで`npm test`でテストを実行できるようになります

## テスト
`__test__`ディレクトリを作成して適当なテストファイルを配置します
```sum.test.tsx
import {expect, test} from '@jest/globals'

function sum(a: number, b: number) {
  return a + b
}
test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3)
})

```

実行
```
npm test

 PASS  src/__test__/sum.test.tsx
  ✓ adds 1 + 2 to equal 3 (1 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.341 s
```

無事テストできてそうです

## おわりに

いかがだったでしょうか
今回はJestの導入方法をまとめてみました
【git】「LF would be replaced by CRLF in 〇〇」とエラーが出た時の解決方法

## 原因

gitはwindowsではチェックアウト時にLFをCRLFに変換していて、コミット時にCRLFからLFに変換しています。

このコミット時の変換でエラーが起きて、コミットができないです。
解決方法を２つごしょうかい

## 解決方法1 改行コードを変換する

コミット時に改行コードがLFのファイルがあるとエラーになるので、コミットするファイルすべて確認して、改行コードをLFからCRLFに変換してコミットすることで解決します。

改行コードはVScodeなどのエディタで変換すると楽です。

## 解決方法2 gitの設定を変更する

上記に記載したgitのチェックアウト時にLFをCRLFに変換して、コミット時にCRLFからLFに変換する設定は以下のコマンドで変更できます。

```
git config --global core.autocrlf false
```

この設定をするとLFとCRLFの変換が行われなくなります。
.tar.zstファイルをWindowsで展開する

LinuxのソースコードをWindowsで展開しようとしたら、拡張子が.tar.zstで展開できなかった時のメモ：

最新版の7-Zipをインストールしなおしたら展開できた。

※ 5年くらい前の7-Zipでは.zstは展開できないです...
paiza Bランク・スキルチェック過去問「名刺バインダー管理」をC言語で解いた

次に解いたのは、Bランク・スキルチェック過去問の「名刺バインダー管理」です。

https://paiza.jp/works/mondai/b_rank_skillcheck_archive/name_card

一発正答でいけましたが、なかなか法則をプログラムに落とし込めず、40分もかかってしまいました。

これは、シミュレーション問題ではなく、法則を見つけることで解くことができましたが、実際に名刺バインダーの配列を作って番号を配置することでも解ける気がしました。

ひとまず、悩みつつ書いたコードは以下の通りです。

```C
#include <stdio.h>
#include <math.h>
int main(void){
    char buf[100];
    int pocket_num, target_num;
    fgets(buf, sizeof(buf), stdin);
    sscanf(buf, "%d %d", &pocket_num, &target_num);
    
    //printf("%d %d", pocket_num, target_num);
    
    /*
        ポケットの数が3の場合の、裏表の対応
        1 <-> 6   => 足したら7 (= 3 * (2 * 1) + 1)
        2 <-> 5   => 7
        3 <-> 4   => 7
        4 <-> 3   => 7
        5 <-> 2   => 7
        6 <-> 1   => 7
        7 <-> 12  => 19 (= 3 * (2 * 3) + 1)
        ...
        10 <-> 9  => 19
        ...
        14 <-> 17 => 31 (= 3 * (2 * 5) + 1)
        
        つまり、解がxの場合、
        target_num + x = pocket_num * (2 * magic_num) + 1
        
        1ページ目は、右辺は7  -> magic_num = 1
        2ページ目は、右辺は7  -> magic_num = 1
        3ページ目は、右辺は19 -> magic_num = 3
        4ページ目は、右辺は19 -> magic_num = 3
        5ページ目は、右辺は31 -> magic_num = 5となる
    */
    
    // 何ページ目に配置されるかは、target_num / pocket_numの解の切り上げ
    int page = ceil((float)target_num / pocket_num);
    
    // 偶数ページ目のmagic_numは、そのページ数を-1する
    int magic_num = page;
    if(page % 2 == 0) {
        magic_num = page - 1;
    }
    //printf("magic [%d]", magic_number);
    
    // target_num + x = pocket_num * (2 * magic_num) + 1を
    // xについて解いた式で求める
    int x = pocket_num * (2 * magic_num) + 1 - target_num;
    
    printf("%d\n", x);
    
    return 0;
}
```

# 別解

また、以下のようにも考えられそうです。

ポケットの数が3のとき、

1 => 6 (= 3 * 2 + 1 - 1) 差は-5
2 => 5 (= 3 * 2 + 1 - 2) 差は-3
3 => 4 (= 3 * 2 + 1 - 3) 差は-1
4 => 3 (= 3 * 2 + 1 - 4) 差は1
5 => 2 (= 3 * 2 + 1 - 5) 差は3
6 => 1 (= 3 * 2 + 1 - 6) 差は5

という対応を考え、7以降は、

7 => 7 % (3 * 2) = 1
8 => 8 % (3 * 2) = 2
9 => 9 % (3 * 2) = 3
13 => 13 % (3 * 2) = 1
...

これで、target_numの該当する典型的な位置がわかれば、その数字から、上記の差を足すことで求められそうです。


# 終わりに

手元で例を書いていくことで、こういう変化があるなということはわかったのですが、それをプログラムへ落とし込むが難しかったです。

たぶん、もっと簡単に解く方法があるのだと思いますが、良い頭の体操にはなりましたのでよしとします。

以上、ここまで読んでいただきありがとうございました。
MUI AutoCompleteのカスタマイズについて

Autocompleteをカスタマイズする機会があり、少し詰まってしまったので
その時に使用したものをまとめてみました。


## 1. `renderInput`
Autocomplete のテキスト入力部分をカスタマイズする。
スタイルだけでなくイベントハンドリング（例：`onChange`、`onKeyDown`）も定義できる。

**実装例**
```tsx
renderInput={(params) => (
  <TextField
    {...params}
    onChange={(event) => setInputValue(event.target.value)}
    onKeyDown={handleKeyDown}
  />
)}
```

## 2. `renderOption`



  Autocompleteのドロップダウンリストの各オプションの表示方法をカスタマイズできる。

**実装例**
```tsx
  renderOption={(props, option) => (
    <Box component="li" {...props}>
      <Typography
        sx={{
          border: '1px solid #ccc',
          borderRadius: '4px',
          backgroundColor: 'lightBlue',
          padding: '2px',
          fontSize: '12px',
        }}
      >
        {option.value}
      </Typography>
    </Box>
  )}
```


## 3. `filterOptions`
 
  ユーザーが入力したテキストに対して、候補リストに表示するオプションをどのように絞り込むかのロジックを定義できる。  
  例えば、部分一致や大文字・小文字の区別。
  一致するものがない場合に新規オプションとして追加する処理などもここで実装した。

**実装例**

```tsx
filterOptions={(optionsList, params) => {
  const filtered = optionsList.filter((option) =>
    option.value.toLowerCase().includes(params.inputValue.toLowerCase()),
  )
  if (params.inputValue.trim() !== '' && filtered.length === 0) {
    const newOption: Option = {
      id: params.inputValue,
      value: `create: ${params.inputValue}`,
    }
    filtered.push(newOption)
  }
  return filtered
}}
```


## 4. `renderTags` 
複数選択を有効にしている時に、選択したオプションのタグ表示をカスタマイズできる。
タグにCloseIconを仕込み、クリック時に選択を解除する機能などもここで対応する。

**実装例**
```tsx
  renderTags={(value, getTagProps) =>
    value.map((option, index) => {
      const tagProps = getTagProps({ index })
      return (
        <Box
          {...tagProps}
          key={option.id}
          sx={{
            display: 'flex',
            backgroundColor: '#e3f2fd',
            color: '#0d47a1',
            borderRadius: '4px',
            padding: '2px 4px',
            alignItems: 'center',
            fontSize: '12px',
          }}
        >
          {option.value}
          <IconButton
            size="small"
            onClick={(event) => {
              event.stopPropagation()
              changeProperty(
                category,
                selectedValues.filter((v) => v !== option.value),
              )
            }}
            sx={{ padding: 0, marginLeft: '4px' }}
          >
            <CloseIcon sx={{ fontSize: 12, color: '#0d47a1' }} />
          </IconButton>
        </Box>
      )
    })
  }
```


## 5. `slotProps` 
  Autocomplete コンポーネント内部で利用されるサブコンポーネント(今回は候補リストを表示するためのポッパー）の表示位置や挙動（flip の無効化、オーバーフロー防止など）を制御する。

**実装例**
```tsx
slotProps={{
  popper: {
    placement: 'top-start',
    modifiers: [
      { name: 'flip', enabled: false },
      {
        name: 'preventOverflow',
        options: {
          altBoundary: true,
          tether: false,
        },
      },
    ],
  },
}}
```
---

参考: [MUI Autocomplete ドキュメント](https://mui.com/material-ui/react-autocomplete/?srsltid=AfmBOorieo1GAD6mqgZ9yz3LlkTPepQ6KYlUcFzIW1tOVLpNDzw3nFRM)
PowerVS IBM i 日記(63): Power9 標準メンテナンスサービスの終了. PowerVS S922には影響なし

# POWER9 L/E 標準メンテナンスサービスの終了

IBMより「[AD25-0134](https://www.ibm.com/docs/ja/announcements/services-withdrawal-discontinuance-service-select-power-systems-products-replacements-available)」で「サービス終了：一部の IBMに対するサービスの提供を終了 - 代替品あり」という発表が出ています。
ざっくりいえば Power9 L/E モデルの標準 H/W 保守サービスが **2026年1月31日** に終了というものです。その中には PowerVSでご利用いただいている S922 も含まれています。
上位機種である E980 への発表はありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184930/db3c4e5a-5c80-23cf-9e19-389b58818c3e.png)

えっ、PowerVS の S922 も使えなくなるの? 今、PowerVS S922 を使っているけど Power10 サーバーに移行が必要なの?

# PowerVS S922には影響なし

大丈夫です。**PowerVS の S922 には影響ありません。2028年10月の中旬までは提供/サポートされる予定**です。
今後、IBM Cloud 側で終了を決定した場合、**1 年以上前にアナウンス**するそうです。

正式な発表ではありませんが、IBMよりお客様と共有可能な情報として下記が提供されました。

> Q) How long will Power Virtual Server continue to provide and support Power9 based virtual system instances (VSIs)?
A) The EOS of Power9 VSIs in IBM Power Virtual Server has not yet been announced. The expectation is that IBM will continue to offer and support the Power9 based VSIs until **Mid-October 2028**. IBM will provide clients **a minimum of 1 year notice** for such an EOS event.

もちろん、Power10 の方がパフォーマンスがいいので、新規オーダーするのであれば Power10 S1022 をお勧めします。

# これを機会に PowerVS への移行も検討を

標準 H/W 保守サービスが **2026年1月31日**に終了するという事は、あと一年もありません。
オンプレで移行することも考えられますが、オーダー/搬入/基本セットアップなどリードタイムが必要なステップが発生します。
PowerVS ではポータルからポチポチするだけで、すぐに IBM i 環境が利用可能です。
これを機会に PowerVS への移行も、ぜひご検討ください 。

---
当日記のIndexは[こちら](https://qiita.com/6onoda/items/f146c78c0c1bbe905a42)です。

---
許可の無い転載を禁じます。
この記事は筆者の個人的な責任で無保証で提供しています。
当記事に関してIBMやビジネスパートナーに問い合わせることは、固くお断りします。
Windows11 24H2で共有フォルダが開けない

共有フォルダへアクセスする際、ユーザ認証を行わずにアクセスしようとすると、guestユーザでのアクセスになります。
しかし、Windows11 24H2環境ではguestユーザでのアクセスができなくなりました。

今のご時勢、セキュリティ的にguestユーザは使うなというのはごもっともなのですが、外に繋がらないイントラ環境だとちょっと困る事があります。

これはPowershell(管理者)で、以下のコマンドを実行する事で接続できるようになります。

```powershell
Set-SmbClientConfiguration -RequireSecuritySignature $false -Force
Set-SmbClientConfiguration -EnableInsecureGuestLogons $true -Force
```
ペアプログラミングで意識していること

ペアプログラミング（ペアプロ）を行う際に、個人的に意識していることを整理してみました。よりスムーズに、効果的なペアプロを行うためのポイントを紹介します。

## 1. 実装を指示する際は必ず意図を伝える

一人でコーディングする場合は、結論だけを実装すればよいですが、ペアプロでは相手がいます。そのため、必ず「なぜその実装を行うのか」という意図を伝えるようにします。

### **意図を伝えることで得られる効果**
- **共通認識の形成**: 複数人で作業する際、意図を共有することで意思の齟齬を防げる。
- **時間の節約**: 事前に意図を伝えることで、相手が推測する時間を削減できる。

### **具体例**
例えば、ユニットテスト（UT）でデータを作成する際に、一部の値を局所変数に格納しておく。そうすることで、期待値の確認時に「この変数はテストデータ作成時に設定した値をそのまま使っている」と明示できる。このような意図を事前に説明することで、ペアの相手もスムーズに理解できるようになります。

---

## 2. 既存のコードについて共通認識を持つ

ペアプロでは、周辺コードの責任範囲や役割をお互いに理解しておくことが重要です。認識がずれると、実装方針に食い違いが生じる可能性があります。

### **意識すべきポイント**
- **周辺コードの動作を確認する**: 何をしているのか、どの範囲までが責任範囲なのかを明確にする。
- **口に出して確認する**: 「このメソッドはAの処理をしているから、Bの責務は別のクラスにあるよね？」といったように、周辺コードについて理解したことを言葉にして確認する。

お互いの認識を合わせてから実装に入ることで、スムーズなコーディングが可能になります。

---

## 3. 相手が考えを話し始めたときは、途中で遮らずに合いの手を入れる

ペアプロは、単に一方が教えるだけでなく、より良い実装を共に考える場でもあります。そのため、相手の考えをしっかり聞き、意図を理解しながら議論を進めることが大切です。

### **意識すべきポイント**
- **相手の話が終わるまで遮らない**: 途中で反論すると、相手の考えの整理を妨げ、本来の話したい内容が伝わりにくくなる。
- **適度に相槌を打つ**: 無反応だと相手が不安になるため、「うん」「なるほど」といった簡単な相槌を入れることで、安心して話してもらえる。

### **なぜ相槌が重要か？**
話している側からすると、
- **的外れなことを言っていないか？**
- **相手にちゃんと伝わっているか？**
といった不安が生まれることがあります。そのため、適度に反応を示すことで、相手が安心して話せる環境を作ることが重要です。

---

## **まとめ**
ペアプロを効果的に行うためには、以下の3つのポイントを意識することが大切です。
1. **実装の意図を伝える**: 共通認識を作り、意思の齟齬や無駄な推測を防ぐ。
2. **既存のコードについて共通認識を持つ**: 責任範囲を明確にし、スムーズな実装につなげる。
3. **相手の話を最後まで聞く**: 遮らずに合いの手を入れることで、相手が安心して話せる環境を作る。

これらを意識することで、より円滑で効果的なペアプロが実現できるでしょう！
paiza Bランク・スキルチェック過去問「神経衰弱 C言語編」を解いた


昨日に引き続き、paizaのスキルチェック過去問を解きました。

https://paiza.jp/works/mondai/b_rank_skillcheck_archive/concentration

今回はこちらの「神経衰弱」です。

解いた感じ、書かれているとおりに実装して、シミュレーションすればよいだけですので簡単めでした。
（15分で一発正答でした）

入力が、h個の空白区切り1行のデータのため、そこを受けとるのがめんどくさかったです。

以下はコメントを付与した回答です。

```C
#include <stdio.h>
#include <string.h>
int main(void){
    char buf[100];
    
    // フィールドの縦幅、横幅、プレーヤー数を取得
    int h, w, n;
    fgets(buf, sizeof(buf), stdin);
    sscanf(buf, "%d %d %d", &h, &w, &n);
    
    // カードの配置を取得
    int field[13][13];
    for(int i = 0; i < h; i++) {
        char buf[13 * 3 + 1];
        int j = 0;
        fgets(buf, sizeof(buf), stdin);
        char *ptr = strtok(buf, " ");
        while(ptr != NULL) {
            sscanf(ptr, "%d", &field[i][j++]);
            ptr = strtok(NULL, " ");
        }
    }
    
    // ターン数を取得
    int l;
    fgets(buf, sizeof(buf), stdin);
    sscanf(buf, "%d", &l);
    
    // 各ターンの記録を取得
    // [0]: 1枚目の縦位置, [1]: 1枚目の横位置, [2]: 2枚目の縦位置, [3]: 3枚目の横位置
    int log[200][4];
    for(int i = 0; i < l; i++) {
        fgets(buf, sizeof(buf), stdin);
        sscanf(buf, "%d %d %d %d", &log[i][0], &log[i][1], &log[i][2], &log[i][3]);
    }
    
    // 入力を正常に受けとれたか確認
#if 0
    printf("%d %d %d %d\n", h, w, n, l);
    for(int i = 0; i < h; i++) {
        for(int j = 0; j < w; j++) {
            printf("%d ", field[i][j]);
        }
        printf("\n");
    }
    printf("%d\n", l);
    for(int i = 0; i < l; i++) {
        for(int j = 0; j < 4; j++) {
            printf("%d ", log[i][j]);
        }
        printf("\n");
    }
#endif

    // ここから解答
    
    int result[10] = {0}; // 最終的な出力結果（プレーヤーの取り分記録）
    int player_num = 0; // 手番のプレーヤー番号（0はじまり）
    int remain = h * w; // 残りカード数（一応実装したけど、いらんかったみたい）
    
    // 各ターンをシミュレートしていく
    for(int i = 0; i < l; i++) {
        int x = log[i][0] - 1;
        int y = log[i][1] - 1;
        int X = log[i][2] - 1;
        int Y = log[i][3] - 1;
        
        // カードの表記が一致していたら、そのプレーヤーの取り分へ2枚加算
        // 残りカード数から2枚減算
        if(field[x][y] == field[X][Y]) {
            result[player_num] += 2;
            remain -= 2;
        }
        // 一致していなかったら、次のプレーヤーの番へ
        else {
            player_num = (player_num + 1) % n;
        }
        
        // 残りカード数が0になったら、ゲーム終了
        if(remain <= 0) {
            break;
        }
    }
    
    // 最終的な出力
    for(int i = 0; i < n; i++) {
        printf("%d\n", result[i]);
    }
}
```

書かれている通りに実装しているだけです。

プレーヤーの順番をループさせなければならないところが大事だと思いました。

（プレーヤー番号は0はじまりで）
0 -> 1 -> 2 -> 3 -> 0 -> 1 -> 2 -> ...

となるので、番号が[4]になったら[0]へ戻すので、プレーヤー数の4で割った余りをとります。


# 終わりに

今回は、配列等の確保が入力の最大値までとなるように気をつかったのですが、もしかしたら思い違いで、境界値を入れるとランタイムエラーを起こすかもしれません。

それでは、引き続き他の問題を練習していきます。
【初心者】iframe内のリンクを親のURLで飛ばしたい！時の解決法【備忘録】

こんにちは。
今回は、現場にてとあるHTMLファイルの中にiframeタグを使用して別のHTMLファイルを読み込んだ際に狙い通りの挙動にならなかったので、そちらの対策についての備忘録です。

## 起きたこと
読み込んだ先のファイルの中にaタグで設定したリンクが存在し、
親側のファイル上でリンクを開くと外側のURLは変わらず、中身の内容だけが変わってしまった。
ここでは仮として

- index.html（親となるファイル）
- sub.html（iframeで読み込む先のファイル）
- other.html（読み込まれたsub.html内でaタグに設定するファイル）

以上の3つのファイルで図にしております。

```mermaid
flowchart TB
    subgraph index.html
        TAG["iframeタグで「sub.html」を読み込み"]-->iframe
    end
    subgraph iframe
        subgraph URL["sub.html"]
            link["aタグ（other.html）"]
        end
    end
```
ここでsub.html内にあるaタグ（other.html）を押下した際に、
index.htmlがother.htmlになるのが理想でしたが……

```mermaid
flowchart TB
    subgraph index.html
        TAG["iframeタグで「sub.html」を読み込み"]-->iframe
    end
    subgraph iframe
        subgraph URL["other.html"]
            contents["other.html内のコンテンツ"]
        end
    end
```
上記のようにiframe内でリンクが飛んでしまい、index.htmlには特に変化が起こりませんでした。
iframeタグの仕様上これは仕方ないことのようですね……iframeにはあまり触れてなかったので普通にindex.html側が切り替わると考えてしまっておりました。

## 解決策
ではどうすれば良いかというと難しくはありませんでした。
最初はJavaScriptを使ってリンクを取得して～など考えておりましたが、普通にHTMLのタグ内で完結しました。
aタグにtarget属性を追加し、値を「_parent（もしくは_top）」にすれば解決できます！

```HTML
<a href="リンク" target="_parent">
```

:::note info
「_parent」を指定する事で、iframeタグで読み込んだファイルから見て親に当たるファイル側でリンクを飛ばす事ができるようになります。
「_top」は親をすっ飛ばして最上位のファイルにてリンクを飛ばします（親と最上位が同じファイルであればどちらの値でも良いかと思います）
:::

「_blank」は別タブに飛ばす値として馴染み深いですが、上記の値は初めて知りました。

## まとめ

改めてMDNでaタグについて調べてみた所、

:::note info
target属性は「リンク先のURLを表示する場所」
:::

と解説されておりました。

https://developer.mozilla.org/ja/docs/Web/HTML/Element/a

そもそも属性の意味をきちんと理解していれば、「もしかしたら適した値があるかもしれない」という考えに至って下手に悩まされずに済んだかもしれなかったので、見慣れたタグや属性の意味を改めて学ぶのも良い機会なのではと思いました。

***
オブジェクティブグループではXの投稿も平日毎日行っています！
IT関連の小ネタや便利技から、日常のアニメ・ゲーム布教なども幅広く投稿してるので、
ご興味のある方は是非フォロー・いいねをお願いします。
https://x.com/obg_ocr
Agent.aiを使ってエンジニアのスキルレポートを出力するエージェントを作った話

### Agent.aiとは？

https://agent.ai/

- AIエージェントを構築する為のサービス
 - 各LLMなど外部サービスとの連携が容易
 - ノーコードではないが簡単なテンプレートが用意されていてエージェントが簡単に作れる
    - AI関連サービスとの連携が強いZapierがもう少しエンジニア寄りになったみたいなイメージ
    - マーケットプレイス風に見えるが今のところ売買の機能はない
    - 今のところLLMへのアクセスが無料 🙌  
- HubSpotのCTOがやってるみたい
    - それもあってHubSpotとの連携が充実してる
    - https://www.hubspot.jp/company/board-of-directors/dharmesh-shah
    - かっこいい、Xウィングに乗っていそう
- dev.toのハッカソン企画で知った
- ドメインの価値（幾らで買い取ったんだろう）

こんな感じの画面でエージェントを構築できます
![スクリーンショット 2025-02-07 9.53.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39655/a7e654fd-036b-5669-c2b2-d5eb80ce8852.png)


### 作ったエージェント
という事でAgent.aiの機能を使ってエンジニアのスキルをレポートにするエージェントを作りました。Agent.aiにアカウントがなくても利用可能です。

https://agent.ai/agent/gitHub-talent-matcher

#### できる事
GitHubのユーザーIDと採用基準を入力するとユーザーの活動履歴から主要スキルを特定し、採用コストを見積もり、最適なポジションを提案。

#### できない事
候補者のIDリスト自体は自前で用意する必要があります。GitHubのOrganization内にいるエンジニアの相互フォロワーを抽出する機能を付けたかったのですが、組織へのアクセス権をもつトークンをユーザーから受け取る必要がありセキュリティの観点から実装を見送りました。

また公開リポジトリの情報しか取得していないので、OSS活動等がないとユーザーの特徴を掴めません。

#### 使い方

##### 入力
1. 採用基準: 理想の候補者の条件を自由記述で入力（例: *「データベース最適化と問題解決能力に優れたバックエンドエンジニアを探しています。」*）。
2. GitHubのユーザーID: リストで複数人も入力可能

![スクリーンショット 2025-02-07 11.59.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39655/22017f62-8917-bbc7-75c7-2a6fd44fd24a.png)

##### 出力
- 主要スキル: 候補者の GitHub 活動から主要なスキルを3つ抽出
- 適した職種: 候補者の強みに基づいた推奨ポジション
- 採用コスト: 候補者の経験や貢献度に基づく推定月額コスト（USD）
- 適合スコア: 入力した採用基準に対する一致度（％）
- 候補者概要: 候補者の GitHub 活動を要約した簡潔なプロフィール

![スクリーンショット 2025-02-07 12.00.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39655/03b31fc6-fede-3d62-640e-dd52c5091b68.png)

私です、採用してください。

### ツールの利用シーン

#### 採用面接に駆り出されるエンジニア
「ちょっと採用面接に出てくれ」と言われた時に、候補者のGitHubのアカウントを入力する

#### 採用担当者
##### リファラル採用
1. GitHubのOrganization内にいる社内のエンジニアの相互フォロワーのIDを抽出
2. 自社エンジニアと繋がりのある候補者を特定
3. 「〇〇さん採用したいんだけど！」と繋がっている社内のエンジニアに声をかける

##### エージェント等から紹介された候補者の選定
githubのIDをリスト化して入力、候補者を絞り込む

#### OSS活動のモチベーションを上げたいエンジニア
憧れの著名エンジニアのIDと自分のIDを入力するとモチベーションが上がります。
採用担当者からの見られ方も意識してOSS活動に参加できるかもしれません。


### Agent.aiを使う上でのtips

#### セキュリティの制約
Agent.ai内に秘密情報を保存する様な機能がないため、外部APIとの連携を行う為のtokenなどを埋め込むのが不安です。自分はGitHubのデータを取得するために自前で外部にAPIを建てました。

#### Lambdaが使える
Agent.ai内でLambdaが実行可能です。入出力も設定できるので難しい処理はAgent.aiの機能で頑張るよりもLambdaに任せる方が簡単かもしれません。

![スクリーンショット 2025-02-07 12.28.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39655/0fe6b32c-0224-55bb-01ad-f2ebb00f9ae6.png)


#### 主要な LLM API が無料で利用可能
Agent.aiではOpenAIはじめAnthropic,Google,Meta,DeepSeekと主要なLLMのAPIを無料で使えます。いずれ有料になるのかな？

![スクリーンショット 2025-02-07 9.53.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39655/cc6985d6-474b-395c-26ea-8813cb87efd2.png)


#### Jinjaテンプレートとは 🤔
pythonでwebアプリを作っている人にはお馴染みなのか、Jinjaテンプレートフォーマットでinputを埋め込みなさいと記載されています。
とはいえ、Jinjaのドキュメントを読んで複雑な事をしようとすると対応されていないのか意図通り動きません。
例えば `{{user.name}}` や `{{user.skills[1]}}` の様な参照は可能です。

![スクリーンショット 2025-02-07 12.20.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39655/60d0f311-203a-902d-90c2-f2d672b7cff7.png)


### まとめ
なんの利害関係もないですが、日本語の記事が見当たらなかったのでサービスの紹介でした！
JavaScriptおすすめスライダー系プラグイン

Webサイト制作にあたりスライダー系の演出は必須といっても過言ではないです。
そこでおすすめのJavaScriptスライダープラグインをご紹介します。

<h2>Slick.js</h2>
カスタマイズ可能なカルーセル型スライダー。レスポンシブデザインに対応し、多彩なオプションを提供しています。<br>
<a href="https://dad-union.com/slick-js-multiple-image-slider">slick.jsを使った複数画像スライダープラグイン</a><br>
<a href="https://dad-union.com/slick-js-mp4-youtube-video-slider">slick.jsを活用してmp4およびYouTube動画を効果的にスライド表示する方法</a><br>
<a href="https://dad-union.com/slick-js-image-zoom-jquery">slick.jsを使って画像を徐々に拡大させながら切替表示する方法【jQuery】</a>

<h2>Swiper.js</h2>
レスポンシブでタッチ対応のスライダー。カルーセル、スライドショー、カード表示など様々なスタイルに対応しています。<br>
<a href="https://dad-union.com/swiper-js-responsive-image-slider">Swiper.jsを使ったレスポンシブ対応の複数画像スライダープラグイン</a>

<h2>Owl Carousel</h2>
レスポンシブでカスタマイズ可能なカルーセルスライダー。タッチ対応やアニメーションオプションなども豊富です。<br>
<a href="https://dad-union.com/owl-carousel2-responsive-slider-jquery">タッチ、スワイプ、マウスホイール、レスポンシブに対応したスライダーOwl Carousel 2【jQueryプラグイン】</a>

<h2>Glide.js</h2>
シンプルながらもカスタマイズ可能なスライダーライブラリ。レスポンシブデザインに対応しており、多くのトランジションオプションが利用できます。<br>
<a href="https://dad-union.com/glide-js-slider-setup">Glide.jsプラグインで魅力的なスライダー表示をする方法</a><br>
<br>
<br>
<a href="https://dad-union.com/javascript-slider-plugins-recommend"><b>続きはコチラ＞＞</b></a>
【ChatGPT】を東大生が課題で使う方法をPrompt化しました。


　どうも、カーブミラーです。

　今回は、東大生がChatGPTを課題で使う方法をPrompt化しました。

:::note info
本記事は、ChatGPT（Plus版）
で行なっております。
画像は✘✘✘。　あしからず。
:::
　ChatGPT等の動作状況を知らせる
　　OpenAIのステータスページ。
　　　[OpenAIステータス](https://status.openai.com/)
***
　こちらも載せますね。
　　[ChatGPTが使えない？ログインできない・反応しない原因やエラー時の対処法を解説](https://aismiley.co.jp/ai_news/chatgpt-error-resolution-explanation/)
***
　まずは、ニュースです。

　ChatGPTの
　　メモリ制限を
　　　25%増やした、そうです。

　Plus、Pro、Teamユーザーのみ。
　　EnterpriseとEduは、近日中に。
　　　[OpenAI公式ポスト](https://x.com/OpenAI/status/1887559196377223587?t=gP_kIE_r3fsYWIjjpLwScA&s=19)
***
　次に

　ChatGPTで
　　Canvasの共有が
　　　可能になりました。

　[OpenAI開発者ポスト](https://x.com/OpenAIDevs/status/1887604146515423390?t=936YutS3oyz2M6f3OuN3Hg&s=19)
>（DeepL翻訳）
>ChatGPTでCanvasの共有が可能になりました。 自分のCanvasを他の人と共有し、見たり、交流したり、編集したりして、自分だけのCanvasを作ることができます。 🧑‍🎨

　リンクを
　　なんらかの形（SNSとか）で
　　　相手に送ることで共有。
***
　次に

　OpenAI o3-miniの無料版と有料版、
　　およびo3-mini-highの有料版の
　　　思考の連鎖を更新したそうです。
　　　　[OpenAI公式ポスト](https://x.com/OpenAI/status/1887616278661112259?t=bBx-61nnSyzTPObmFBduMg&s=19)
***
　ではさっそく、【本題】です。

　今回は、
　　ChatGPTを
　　　東大生が課題で使う方法を
　　　　Prompt化しました。

　こちらの記事を
　　参考にしました。
　　　[東大生があえて｢ChatGPT｣を課題で使う納得の訳](https://toyokeizai.net/articles/-/856671?page=2)

　本来は、
　　人間側が
　　　東大生の真似をするのが
　　　　良い気がします。
　　　　　“脳みそを使う”という意味で。

　しかし、
　　時間の浪費を
　　　避けたいのは、
　　　　現代社会ではふつうです。

　そこで、
　　このPromptを作成しました。

　使ってみて、
　　結論よりも
　　　途中に、いいポイントが
　　　　出てきたと思います。

　こちらが、そのPromptです。
　　＊＊＊に問題を入力して、
　　　送信してください。
```Text:段階的に問題解決策を導出するPrompt
あなたは優秀な研究者兼アドバイザーです。以下の問題について、段階的に解決策を導き出してください。

### **問題**
{＊＊＊}

### **ステップ1: 問題の理解**
- この問題の背景や前提条件を整理してください。
- 解決するために考慮すべき重要な要素を列挙してください。

### **ステップ2: 初期アイデアの生成**
- この問題についての一般的な解決策やアプローチをいくつか提案してください。
- それぞれのアプローチの利点と欠点を簡潔に説明してください。

### **ステップ3: 深掘りと分析**
- 各アプローチをより詳細に分析し、最も適切な解決策を検討してください。
- 追加で必要な情報や調査すべきポイントを示してください。

### **ステップ4: 追加調査**
- 信頼できる情報源やデータを活用して、より具体的な解決策を考えてください。
- 最新の研究や実例があれば紹介してください。

### **ステップ5: 結論の整理**
- 最も適切な解決策を選択し、その理由を説明してください。
- 実行する際の具体的なステップを提案してください。

### **ステップ6: 検証と改善**
- 提案した解決策のリスクや課題を特定してください。
- 改善するための追加の工夫や代替案を示してください。

この問題解決プロセスを順番に進め、論理的かつ実用的な解決策を導き出してください。
```

　これで、
　　問題解決できるか、というと
　　　実際にやってみないと
　　　　わかりませんが、
　　　　　思わぬ助言が得られる
　　　　　　かと思います。

　みなさんの
　　作品制作の糧に
　　　なれば、幸いです。

　今回は、ここまで。
【勉強記録】勉強始めて6カ月たちました

# はじめに
こんにちは。アメリカ在住で独学でエンジニアを目指している者です。
ソフトウェアエンジニアになるために勉強を始めたのが2024年の8月1日ですが、期間的に約半年が経過しました
2カ月間大学に行っていたのでフルコミットできていたのは実質4カ月ですが、自分のやってきたことを振り返ろうと思います

## 6カ月間をさかのぼってみた
* 1カ月目
右も左もわからず、まずはHTMLとCSSを勉強しました。実は本を読むのがそこまで得意ではなかったので、progateをやった後youtubeで勉強していました。
アメリカに来たばかりというのもあり、デスクがなかったりモニターがなかったりなど環境はあまりよくなかったのを思い出します。
今はダブルスクリーンの日本から持ってきたいい性能のdesktopPCを使えているので問題なく作業できています
1日の平均勉強時間は5時間くらいですね。この時はちゃんと記録をつけていませんでした。
* 2カ月目
この時からJavaScript,SQL,Git,GitHub,Linuxの基礎中の基礎を学びました。JavaScriptもDOM操作は少しだけできて簡単なゲームが作れるくらいです。
Git,GitHub,Linuxをこの段階で軽くでもやり始めた理由はコマンドラインの操作に慣れておきたかったのがあります。
この時にこれらをやっていたおかげでQiitaもCLIを使っているし、GitHubも毎日使ってアウトプットがあればあげる体制をとることができました。
9月の末にRubyの基礎中の基礎である「ゼロからはじめるRuby」という教材を使用して勉強しました。
この頃も平均5時間くらいだったと思います。この時から勉強時間と内容をNotionに記録し始めました

8/1～9/25までの勉強累計時間を270時間として始めました。(どんな計算したかわからないけど、絶対もっと少ない気がする)

* 3カ月目
10月になり英語を学ぶために大学に行きはじめました。
お金を払って大学に行っていたので、この時の優先順位は　英語　＞　プログラミングでした。
なので、一日できても2時間ちょっとって感じだったので土日にも勉強するようにしていました。
この時は今みたいに充実して勉強できているというよりは、とりあえずコードを見る習慣をつける、プログラミングに触れるということを目標にしていました。
Qiita毎日投稿も実はこの時に、「例えどんなに忙しくても毎日ブログを書けば技術に触れる機会がある」と思い10/6に始めました。

* 4カ月目
3カ月目とほぼ同じでした。とても忙しいというわけではないですが、プログラミングの勉強は割と後回し的な感じだったので(本当はがっつり勉強したかった)
ただひたすらに「たのしいRuby」という教材でRubyの基礎を学んでいました。大学が終わる来る日のために。

* 5カ月目
12月の中旬に大学が終わりました。英語力についてはネイティブが早く話さなければギリ聞き取れて自分の伝えたいことが何となくいえるレベルには成長しました
大学が終わったのが金曜日でしたが、正直この2カ月ソフトウェアエンジニアの勉強をフルコミットできなかったことにかなり焦りがあったので、土日をほぼフルに使って今後の学習計画、生活リズムの作り方、忘れている技術等、優先順位付けなどを考えました。
フルコミットするには1日8時間くらい、かつ英語の勉強は一日1時間、筋トレも30分は最低やる計9.5時間勉強に当てるスケジュールを組みました。
3.4カ月は同じ技術だけ勉強していたので飽きと集中力の問題も加味して、一日のスケジュールを30分単位で分けて進め始めました。
この月からポモドーロ勉強法を採用し始めました。勉強時間が7時間とかになると集中力が落ちているのが自覚できますが、日中は集中力はほぼ落とさす進められたので自分にはかなり向いていたなと思いました
あと、こんなに平日フルコミットするのだから土日は、妻との予定を入れるようにしていました。たまに予定がない際は勉強していましたがｗ

勉強内容としてはRails、Web、Ruby演習等でした。Railsチュートリアルを進めていましたが、知識的に足りず理解できないということがなかったので(Bootsrapは除く)積み重ねて勉強できていたのだなと感心しました

* 6カ月目
この月が一番勉強時間と内容もかなり充実していました。土日抜いて170時間だったので一日8時間以上勉強していたらしいです。
Railsの勉強をしている中で知らないわけではないが、忘れている知識がかなりありJS、Linux、SQL、Gitあたりです。これらは平日の勉強時間に組み込み始めました
また英語力とポートフォリオとして英語のブログも1週間に1回程度書き始めたのもかなり大きな変化です。
この月はかなりいろいろ始めたので詳しくは1月の成果という記事をご覧ください


## 6カ月も勉強してるの？
私の友人は6カ月勉強して転職したようです。そこから見ると遅いなと思いますが、転職しただけがゴールではないかなと思っています
私の場合は海外在住ということもあり、フルリモートが確定しているので通常のカリキュラムではおそらく戦えないだろうなと思っています。
また、どうやら未経験エンジニア市場もかなり変わってきているみたいで技術が高い方が求められるようなので、例え就職が少し後ろになっても過度に焦らずしっかり技術をつけたうえでエンジニアになろうと思います。

実は2月も日本に一時帰国するのでほぼまるまる勉強できませんが、ブログ投稿は続けていきたいと思うのでよろしくお願いします。
Corepack の `Error: Cannot find matching keyid: ...` エラーに関して

## 事象
久しぶりに Corepack を使用している Docker コンテナを立ち上げると以下のようなエラーが発生した

```sh
 => ERROR [frontend 5/5] RUN pnpm config set store-dir /pnpm/store                                                                                 0.4s
------
 > [frontend 5/5] RUN pnpm config set store-dir /pnpm/store:
0.325 /usr/local/lib/node_modules/corepack/dist/lib/corepack.cjs:21535
0.325   if (key == null || signature == null) throw new Error(`Cannot find matching keyid: ${JSON.stringify({ signatures, keys })}`);
0.325                                               ^
0.325
0.325 Error: Cannot find matching keyid: {"signatures":[{"sig":"~","keyid":"SHA256:~"}],"keys":[{"expires":null,"keyid":"SHA256:~","keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","key":"~"}]}
0.325     at verifySignature (/usr/local/lib/node_modules/corepack/dist/lib/corepack.cjs:21535:47)
0.325     at fetchLatestStableVersion (/usr/local/lib/node_modules/corepack/dist/lib/corepack.cjs:21553:5)
0.325     at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
0.325     at async fetchLatestStableVersion2 (/usr/local/lib/node_modules/corepack/dist/lib/corepack.cjs:21672:14)
0.325     at async Engine.getDefaultVersion (/usr/local/lib/node_modules/corepack/dist/lib/corepack.cjs:22298:23)
0.325     at async Engine.executePackageManagerRequest (/usr/local/lib/node_modules/corepack/dist/lib/corepack.cjs:22396:47)
0.325     at async Object.runMain (/usr/local/lib/node_modules/corepack/dist/lib/corepack.cjs:23102:5)
0.325
0.325 Node.js v22.13.1
------
failed to solve: process "/bin/sh -c pnpm config set store-dir ${PNPM_HOME}/store" did not complete successfully: exit code: 1
```
## 解決方法
https://vercel.com/guides/corepack-errors-github-actions
> If your builds suddenly fail with messages like cannot find matching keyid: {"signatures":[...],"keys":[...]}, it usually means npm registry keys have been rotated, and your older Corepack can’t verify new versions of pnpm (like 9.15.4 or 10.1.0+).

npm のレジストリキーがローテーションしたことにより、Corepack が新しいバージョンの pnpm を検証できなくなったことが原因

> 1. If You’re on Node 18+ (or Newer)
> Upgrade to the latest Corepack (≥ 0.31.0):

今回 Node 22 を使用していたので、以下のように Corepack を有効化する前に最新バージョンへ更新してあげることで解決した

```dockerfile
RUN npm install -g corepack@latest
RUN corepack enable
RUN pnpm config set store-dir ${PNPM_HOME}/store
```

## その他参考
以下記事では Corepack の署名検証などに関しても解説されてました

https://zenn.dev/progfay/articles/corepack-verify-signature
LINEでもらった画像がPCのスマートフォン連携アプリで表示されない

## 起きたこと

スマホのGoogleフォトアプリで`コレクション`→`このデバイス上`→`LINE`を開くとお目当ての画像ファイルがある。これは人からLINEで送ってもらった画像を選択してダウンロードしたもの。
これがPCのスマートフォン連携アプリで表示できなかったことがあったのでメモ。

## 環境

スマホ：Android A202SO
PC:Windows11

## 試したこと

### カメラフォルダに移動（NG）
もらった画像を長押しして三点リーダーから`フォルダに移動`で`カメラ`フォルダに移動してみた。
これは、カメラ撮影したものがこのフォルダにあることがなんとなくわかっていたので、ここに移動すれば見れるのでは、と考えて。
結果NG。
Windows側でいくら最新の情報に更新してもダメ。

### Screenshotsフォルダに移動（OK）
上記と同様の手順で`Screenshots`フォルダに移動したら表示された。
Windows側でも意図的に最新の情報に更新（F5キー）したわけではなく、自動的に表示された。
【学習】PerlとAmon2で環境構築（恐る恐る）vol1

## はじめに
つい先日、PerlのAmon2フレームワークを使用して開発をしてみました。参考記事と一緒に私が実践したことをお伝えできればと思います。

## 参考記事
https://github.com/perl-entrance-org/Perl-Entrance-Textbook/blob/master/amon2/1.md

https://gihyo.jp/dev/serial/01/perl-hackers-hub/001802

https://zenn.dev/syy/scraps/3c0d26ca61f8eb



## 開発環境
- 言語: Perl
- フレームワーク: Amon2
- PCとか：Windows。VSCode


## セットアップ手順 
1. **ディレクトリ作成（ディレクトリ名は例）して移動**
    ```terminal
    $ mkdir -p ~/Projects/<ディレクトリ名>
    $ cd ~/Projects/<ディレクトリ名>
    ```
   <br>

2. **Perlインストール**
    ```terminal
    ・Perlがインストールされているか確認
    $ perl -v

    ・なければインストール
    Ubuntu:$ sudo apt-get install perl
    Mac:$ brew install perl

    ・「cpanm」のインストール確認
    $ cpanm -v
    
    ・Perlのパッケージ管理ツールである「cpanm」をインストール。
    ・Amon2 やその他の必要なライブラリを簡単にインストールできるようにする
    $ curl -L https://cpanmin.us | perl - --sudo App::cpanminus

    ```

    👇こちらはスキップしても大丈夫です。
    ```terminal
    ・「Task::Amon2」のインストール(Amon2 をインストール)
    ・(Amon2 を使うための「推奨パッケージセット」) 
    $ cpanm Task::Amon2

    ・Amon2 をプロジェクトに取り込む
    ・ここで<ディレクトリ>の下の階層？に<プロジェクト>が作成される
    $ amon2-setup.pl <取り込みたいプロジェクト名>
    ```
    ```terminal
    ※私は上記すっきぷしてもいいコマンドを実行しました。
    ※実行後下記のようなメッセージがターミナルに表示されます
    ※「carton install」してね。というコマンド
    ※その次のコマンドはサーバーを起動するためのコマンドです
    --------------------------------------------------------------

    Setup script was done! You are ready to run the skelton.

    You need to install the dependencies by:

    > carton install

    And then, run your application server:

    > carton exec perl -Ilib script/<プロジェクト名>-server

    -------------------------------------------------------------- 
    ```
    <br>


3. **Cartonをインストール**
    ・Amon2などの依存関係を解決するためのコマンド。待ち時間が長い💦
    ```terminal
    $ cpanm Carton
    ```
   <br>
    
4. **プロジェクト内の依存モジュール（Amon2など）をインストール**
    ・「carton」は、既にインストールされているパッケージについて自動的にスキップするので、「スキップしても大丈夫」のコマンドを仮に実行していたとしても問題ありません。
    ・`carton install` で依存モジュールをインストール
    ```terminal
    $ cd <プロジェクト名>
    $ carton install
    ```
   <br>
    
5. **サーバーを起動・終了する**
    - 下記コマンド実行後表示されたURLにアクセスすることで閲覧することが可能です
    ```terminal
    ・起動
    $ carton exec perl -Ilib script/<プロジェクト名>-server
    
    ・終了
    ctrl + cキー
    ```


## まとめ
Windowsのひとはすくない（個人的主観）ので今回の私の構築がそのまま当てはまるかといえば若干不安点はあります💦
ごさんこうになればさいわいで
エンジニア１年生～研修から実務編～

こんにちは。

私はエンジニア＆社会人になってから１年経過し、２年目に入りました。
僕自身、力及ばず周りに迷惑かけることも多くありましたが、いまは何とかやってますｗ

ただ、これからエンジニアになる人も多くいると思うので僕自身思ったことをポエムにして世に流そうと思います。

# インプット＆アウトプット

僕が入社してから一番心がけていたことは、一度学んだことをアウトプットして文書や誰かの見える場所に残しておこうとおもったことです。
学生自体にもノートはこまめに執っていましたが、社会人になってからは要点を簡潔にまとめ、より分かりやすく整理することを意識するようになりました
また、マークダウンの練習も兼ねて基本デジタル文書で残すようにしました。

「1度目のミスは知らなかったで済むが、何度も繰り返せば過失になる」と考え、同じミスを防ぐためのノートとしても活用しています。この習慣は今後も続けていきたいと思っています。

# 聞かぬは一生の損

経験豊富な歴戦のエンジニアさんたちはひよっこエンジニアにはとっても貴重な資料になります。~~動くエンジニア地引~~
今はエラーコードを検索すれば大抵のことは解決できる時代になりましたが、やはり今までの経験を積んできたエンジニアは憶測と経験から解決方法や最適解をぱっとだしてくれます。
また、自分だけでは解決できない状況に直面した際には聞いたほうが圧倒的に早い場合もあります。

自分の考えてることや聞きたいことをまとめて聞く力。それらをしっかりと培っていくのもエンジニアのパラメータであると実感しました。

# 心理的安全の確保

最初はこの言葉の意味がよくわからずに様々なエンジニアと携わってきましたが、実際に仕事してからはいかに重要か実感するようになりました。
話を聞くにしろ相手のことを考えてしまい、聞きにくかったり同じ仕事をしていてもやりにくいと感じてしまう感覚があったりします。
これらの解消するためには常日頃からコミュニケーションや連携をしていくことが大事だなーって思いました。
チャットだけだと硬すぎるためところどころ絵文字を加えたりなどの工夫も重要で、いかに自分を魅せられるか、相手のことを理解するか、ここら辺かなり意識しています。
また、自分の場合はかるーーーい１、２分の雑談的なことをしたり、季節ごとの話題を出したりなどをしていました。

# まとめ
これからエンジニアを目指す方の中には、右も左も分からず不安に感じる方もいるかもしれません。しかし、努力次第でどうにでもなります。
私自身、中途新卒という立場ながら、多くの人と関わり、お互いを尊重しながらさまざまなことに挑戦してきました。
同じ環境ではないかもしれませんが、周囲と積極的に関わり、聞く力を磨くことで、活躍できる場は広がっていくと思います。

これからエンジニアを目指す皆さん、一緒に頑張りましょう！
for分の繰り返し　練習

動作環境：windows13
ソフト：visual studio 2019

```test.cpp
#include <stdio.h>
int main(void) {
	int n,i;
	for (n = 0; n < 10; n++) {
		for (i = 0; i <= n; i++) {
			printf("*");
		}
		printf("\n");
	}
}
```

```:実行結果

*
**
***
****
*****
******
*******
********
*********
**********
```
[個人開発] WebサイトリニューアルにVercel v0を使ったら革新的すぎた件

## これは何？
寝不足になりながら個人で開発したWebサイトのリニューアルを生成AIを使って頑張った話を自己満でまとめた記事です。

## リニューアルしたサイト
こちらです。(PSO2NGSというオンラインMMORPGのチームサイトです)

https://funny-bunny.site/

私、実はこのゲームを旧PSO2の時代からもう10年近くやってまして
ゲーム内で所属しているチームを紹介してメンバーと活動内容を広めていく取り組みを(勝手に)やっています。

今回はこのサイトをいろいろな面でリニューアルしました。

## なんでリニューアルしたの？

まずはリニューアル前のサイトのスクショを見てみましょう。

![スクリーンショット 2025-02-07 095933.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/895851/0201240a-be95-b1e3-8ad7-b3990d8e4c56.png)

このサイトは、元はTumblrと呼ばれるブログとSNSを組み合わせたようなサービスがありそれを利用して公開していました。

https://www.tumblr.com/

Xのように画像やテキストを投稿して他のユーザーに見てもらうこともできるし
投稿したデータを利用してブログ形式でWebサイトを公開できるCMSのような機能もあります。
私の利用範囲では特に費用もかからずサイト公開出来ていました。

これだけ聞くととても便利なのですが

- Tumblrを利用している国内ユーザーがそれほどいない(どちらかというと海外ユーザーがゲームのスクリーンショット等を投稿して盛り上げていた印象)
- サイト制作という点では独自にカスタマイズしにくい
    - WordPressのテーマのようにWebサイトのテンプレートが用意されているが自由度が低い
    - カスタマイズも出来るが独自のJavascriptを組み込むといったことがほぼ出来ない(厳密に言えばサポートに連絡して承認もらえれば組み込める模様。連絡しても返答なかったけど)
- 上記理由からテンプレート以上のことは基本HTMLとCSSのみで実装するしかなかった

という感じだったので、もう少しモダンな技術を使って自分でカスタマイズできたらいいなあとずっと前から思っており
今回ようやく重い腰を上げてリニューアルに踏み切ったというわけです。

## どんな感じでリニューアルしたの？

まずTumblrは利用しない方針にしました。その上で

- もろもろの制約により複数ページに散らばっていた情報をシングルページに集約してデザインを統一したい
- レンタル or 仮想サーバーのサービスは使わない
    - サーバーの運用をする必要が出てくるため
- バックエンドのAPI等の複雑な処理は実装せずフロントエンドのみで完結させたい

などを考慮し、よくある構成のNext.jsとVercelを利用することにしました。

https://nextjs.org/

https://vercel.com/

## どうやってリニューアルしたの？

同じくVercelが開発している生成AI系サービスのv0を利用しました。

https://v0.dev/

本職がインフラエンジニアなのでインフラ込みのバックエンド領域には知見がありますが
フロントエンドの知見があまりなく0からNext.jsで開発していくのは時間的労力が高すぎるので
ありがたく生成AIの力を借りることにしました。

基本AIとプロンプトでやり取りしながら進めていきました。まじで便利すぎる。
実際の開発画面のスクリーンショットを貼っておきます。

![スクリーンショット 2025-02-07 10.55.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/895851/c718aeb0-b74a-d7aa-974f-3c80402e0a24.png)

こんな感じで生成したコードを実行したときの画面プレビューを閲覧しながら開発できるし

![スクリーンショット 2025-02-07 10.56.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/895851/0ffff840-0350-2092-ceb4-b354a01e59be.png)

生成したコードももちろん画面上から確認できます。
右上のデプロイというボタンを押すとそれだけでVercelに展開されてサイトを公開できます。

総じてユーザー体験が良すぎるなあと。

## v0の良いところ

- 複数ファイルの生成に対応している
    - Vercelが作っているサービスなので、Next.jsの仕様を理解してコンポーネントごとにファイルを分割して生成してくれる
    - 今回作ったサイトもコードとしてはNext.jsで書かれています
- 生成したコードと実際の動作をプレビュー画面でしながら開発できる
    - v0の画面から生成したコード自体を自分で編集することも可能
- 画像ベースでプロンプトから指示が出せる
    - 生成してもらった画面がこういう動きになってるんだけどもう少しなんとかならない？ってときにその部分のスクショと理想系の説明をすれば解釈して直してくれる
- 生成したコードをそのままVercelにデプロイして公開できる
    - デプロイボタンを押して待ってるだけ
- 意図しない挙動になった場合元のバージョンに復元することができる

## もう少しなところ
- 価格がちょっとお高めかも（月額$20、日本円で¥3000ちょっとくらい
    - フリープランもあるけどプロンプト入力の回数上限があるので本格的に使いたい場合は課金必須
- 指示を出していない部分を直してしまったり、数回前に編集した部分が巻き戻ることがある
    - これは生成AI系のサービスの宿命かもしれない

## まとめ

一旦ひと段落したのでゆっくり寝られます。
まだ上手く動いていない部分やサイト内で利用している画像が若干古かったりと修正箇所はあります。
ゆっくり直していきますか〜。
みなさんも是非v0を利用してみてください！