Feishu Portfolio Launch
このスキルの目的
一言で言うと:Feishu多次元テーブルのリンクを渡せば、ポートフォリオサイトを構築し、「Feishu一時リンクの有効期限切れ」を自動化します。
デフォルトの成果物:
index.htmlまたは静的フロントエンドページapi/videos.jsonapi/covers.jsonapi/portfolio.jsonrefresh.py.github/workflows/refresh.yml- アニメーション付きフロントエンドインタラクション層(CSS / JS / Canvas / Three.js 可)
組み込みテンプレートリソース:
目標結果:
- GitHub Pages にサイトをデプロイ
- Feishuメディアリンクは GitHub Actions で12時間ごとに自動更新
- ユーザーは自身のPCを常時起動させる必要なし
- ページは単なる静的レイアウトではなく、最低限のエントランス/スクロール/ホバー/ページ切り替えのアニメーションを備える
適用シーン
✅ 適している:
- AI動画/画像ポートフォリオ
- 写真、デザイン、クリエイティブサンプルライブラリ
- 顧客事例紹介サイト
- 「コンテンツがFeishu添付ファイルにあり、フロントエンドが表示のみを担当する」すべてのシーン
❌ 適していない:
- テキストブログ
- EC、決済、ログインシステム
- データベース書き込みとバックエンドビジネスロジックが必要なサイト
コア原則
Feishuの一時URLを永続的なリソースと見なさないこと。
Feishu添付ファイルの再生/ダウンロードURLには有効期限があります。正しいアプローチは、ユーザーにローカルPCで定期的なスクリプト実行をさせないことです。代わりに:
- Feishu多次元テーブルから最新のレコードを読み取る
- Feishu API を呼び出して添付ファイルの
file_tokenを一時URLに変換 api/*.jsonを生成- GitHub リポジトリにコミット
- GitHub Pages に自動再デプロイさせる
つまり:
Feishu Bitable -> refresh.py -> api/*.json -> GitHub Pages
実行フロー
Step 0:環境確認
ローカルに必要なツールが揃っているか確認:
for cmd in node python3 gh git; do
which "$cmd" >/dev/null 2>&1 && echo "OK $cmd" || echo "MISS $cmd"
done
lark-cli --version >/dev/null 2>&1 && echo "OK lark-cli" || echo "MISS lark-cli"
gh auth status >/dev/null 2>&1 && echo "OK GitHub auth" || echo "MISS GitHub auth"
不足しているツールがあれば、ユーザーにインストールを促す。
gh がログインしていなければ、先にGitHub認証を完了させる。
Step 1:Feishu Base と正しいデータシートを取得
ユーザーにFeishu多次元テーブルのリンクを依頼(例):
https://my.feishu.cn/base/AbCdEfGhIjk
URL から app_token を解析する。
その後、すべてのテーブルをリスト化:
lark-cli api GET "/open-apis/bitable/v1/apps/${APP_TOKEN}/tables"
最初のテーブルをデフォルトで選択しないこと。
多くのBaseには「空テンプレートテーブル」と「実際の作品がある素材テーブル」が混在している。レコードを読み取り、以下を検証する:
- 実際の添付フィールドがあるテーブルはどれか
- レコード数が正しいテーブルはどれか
- フィールド名がユーザーの表示内容であるテーブルはどれか
以下のフィールドを優先的に検索:
- 動画フィールド:
サンプル動画/動画/添付ファイル - カバーフィールド:
カバー/カバー画像/ポスター/画像 - タイトルフィールド:
内容/タイトル/作品名 - カテゴリフィールド:
タイプ/カテゴリ - 長さフィールド:
長さ - ツールフィールド:
AIツール/ツール - ソートフィールド:
順序
テーブルに テキスト / 単一選択 / 日付 / 添付ファイル のような空テンプレートフィールドしかない場合は、テーブルを切り替え、サイトを生成しない。
Step 2:Feishu権限を検証
GitHub Actions で自動更新を実行するには、Feishuアプリが最低限以下の操作をできる必要がある:
- 多次元テーブルレコードの読み取り
- 添付ファイルのメディアリンクの読み取り
実行計画では、これら4つのシークレットに明示的に依存する:
LARK_APP_IDLARK_APP_SECRETLARK_BASE_TOKENLARK_TABLE_ID
ユーザーのローカルですでに使用可能なFeishuアプリ設定があれば、それを再利用してもよい。それ以外の場合は、Feishuオープンプラットフォームで権限とキーを補足するようユーザーに明確に指示する。
Step 3:構造化された作品データをエクスポート
file_token のリストだけを抽出しない。
フロントエンドが直接消費できる構造化データを構築する。最低限以下を含む:
- title
- categories
- duration
- tools
- order
- video_token
- cover_token
推奨出力:
api/portfolio.json:完全な作品配列api/videos.json:video_token -> tmp_download_urlapi/covers.json:cover_token -> tmp_download_url
構造サンプルはリポジトリ内の assets/portfolio.sample.json を参照。
フロントエンドは静的HTML+JSのままでも、portfolio.json から完全にレンダリングしてもよい。
Step 4:フロントエンドページを生成
デフォルトでは静的ページを生成するが、以下を要求:
- レスポンシブ作品グリッド
- カテゴリフィルター
- カードクリックで再生
- 全画面拡大再生
- 同時に1つの動画のみ再生可能
- カバーはFeishuカバーフィールドを優先し、カバーがない場合はポスターなしにフォールバック可能
アニメーションはデフォルトで一緒に提供すること。「開けるだけ」の静的ページにしない:
- ファーストビューには少なくとも1組のエントランスアニメーション(例:タイトル、説明、統計数字が順にfade-up)
- ナビゲーションはスクロール後にblur/background/borderの変化を許可
- カードのホバーには少なくとも軽量なフィードバック(例:浮上、拡大、オーバーレイ、タイトル色変更)
- ページ切り替えやセクション切り替えにはトランジションが必要。唐突なフラッシュ切り替えは避ける
- About/連絡先/スキルモジュールなどの情報セクションには、スクロールリビールを優先的に追加
- ユーザーがより視覚的な表現を希望する場合、Canvas や Three.js の背景アニメーションを追加可能
実装の境界:
- ネイティブCSS transition / keyframes / IntersectionObserver を優先
- 継続的な動きのある背景が必要な場合のみ
requestAnimationFrameを使用 - Three.js を使用する場合、モバイルで空白、フリーズ、メインコンテンツの隠蔽が発生しないことを確認
- アニメーションは動画再生、フィルター、ポップアップ、モバイルスクロール、ファーストビューの可読性に影響を与えないこと
- デフォルトでフォールバックパスを提供し、スクリプトが失敗してもテキストとコンテンツは表示されること
データバインディングの原則:
- HTMLに期限切れURLをハードコードしない
- ページ実行時に
/api/videos.jsonと/api/covers.jsonを取得 - または、直接
/api/portfolio.jsonを取得してカードをレンダリング
ユーザーが既存のサイトを持っている場合、既存のビジュアルスタイルを優先して保持し、データソースと更新メカニズムのみを置き換える。
ユーザーが既存のサイトスタイルを参考にしたいと明確に述べた場合、以下のアニメーションタイプを優先的に再利用:
- hero コピーのエントランスアニメーション
- sticky filter/nav のスクロール状態遷移
- masonry カードのホバーエフェクト
- ページのフェードイン/アウト切り替え
- About ページの stagger reveal
- オプションの Three.js/Canvas 背景アニメーション
Step 5:リポジトリを作成しGitHub Pagesにデプロイ
標準フロー:
gh repo create <repo-name> --public --source=. --remote=origin --push
その後、Pagesのソースを確認:
- branch:
main - path:
/
確認:
gh api repos/<owner>/<repo>/pages
以下を確認必須:
- Pages が有効化されている
cnameが正常(カスタムドメインがある場合)- source が
mainとルートディレクトリを指している
Step 6:自動更新スクリプトを生成
更新スクリプトは以下のルートを採用:
app_id + app_secretでtenant_access_tokenを取得- Bitable レコードを読み取り
- レコードから動画とカバーの添付ファイルトークンを抽出
batch_get_tmp_download_urlをバッチ呼び出しapi/videos.json、api/covers.json、api/portfolio.jsonに書き込み
重要要件:
- レコードのページング読み取りをサポート
- バッチリクエストは5トークンずつ
- トークンリストをスクリプトにハードコードしない
- 実際のテーブルレコードから動的に生成することを優先
- 複数のフィールド名候補を許可
推奨ファイル名:
refresh.py
スキル組み込みテンプレートを直接コピーすることを優先:
cp ~/.claude/skills/feishu-portfolio-launch/assets/refresh.py ./refresh.py
その後、フィールド候補配列のみを修正し、具体的なトークンやURLをハードコードしない。
Step 7:GitHub Actions Workflow を生成
ローカルの launchd をデフォルトのソリューションとして使用しない。
デフォルトは GitHub Actions でなければならない。
ワークフローの要件:
workflow_dispatchをサポートscheduleをサポートし、デフォルトで12時間ごとに実行python refresh.pyを実行api/videos.json、api/covers.json、api/portfolio.jsonを自動コミット
標準シークレット:
LARK_APP_IDLARK_APP_SECRETLARK_BASE_TOKENLARK_TABLE_ID
標準ファイル:
.github/workflows/refresh.yml
スキル組み込みテンプレートを直接コピーすることを優先:
mkdir -p .github/workflows
cp ~/.claude/skills/feishu-portfolio-launch/assets/refresh.yml .github/workflows/refresh.yml
Step 8:Secrets をリポジトリに設定
現在の環境にこれらの値があり、ユーザーが設定を明確に依頼した場合、直接設定:
gh secret set LARK_APP_ID -R <owner>/<repo>
gh secret set LARK_APP_SECRET -R <owner>/<repo>
gh secret set LARK_BASE_TOKEN -R <owner>/<repo>
gh secret set LARK_TABLE_ID -R <owner>/<repo>
その後、確認:
gh secret list -R <owner>/<repo>
値が不足している場合は、ここで停止し、不足しているものをユーザーに明確に伝える。偽造しない。
Step 9:手動でトリガーして全体のチェーンを検証
設定完了後、すぐに手動でワークフローを1回実行:
gh workflow run refresh.yml -R <owner>/<repo>
gh run list -R <owner>/<repo> --limit 3
gh run watch <run-id> -R <owner>/<repo> --exit-status
さらにオンライン結果を確認:
python3 - <<'PY'
import json, urllib.request, time
url = f'https://<domain-or-pages-url>/api/videos.json?t={int(time.time())}'
with urllib.request.urlopen(url, timeout=20) as r:
data = json.load(r)
print(data.get('_refreshed_at'))
print(data.get('_count'))
PY
その後、1件の動画をサンプル確認:
- ステータスコードは
206または200であるべき content-typeはvideo/mp4または再生可能なメディアタイプであるべき
オンラインの api/videos.json がまだ古い時刻の場合:
- まずPagesのデプロイが完了しているか確認
- 次にワークフローが実際に新しいJSONをコミットしたか確認
- さらにPages source が正しいブランチとディレクトリを指しているか確認
Step 10:ドメインバインディング
ユーザーがカスタムドメインを希望する場合のみ、以下を実行:
CNAMEファイル- GitHub Pages カスタムドメイン設定
- DNS 設定ガイド
この部分は GitHub Pages の標準フローに従い、Feishu のロジックと結合する必要はない。
ユーザーへのデフォルト説明
「サイトは開けるが動画が再生できない」という状況に遭遇した場合、優先順位:
api/videos.jsonがまだ古いかどうか- 中のFeishu URLが有効期限切れかどうか
- 自動更新ワークフローが失敗したかどうか
- Pagesがまだ再デプロイされていないかどうか
デフォルトでプレイヤーやフロントエンドスタイルを疑わない。
ユーザーが「ページが死んでいる」「ポートフォリオのように息づく感覚が欲しい」と言った場合、デフォルトでアニメーション層を追加する。色やレイアウトだけを変更しない。
成功基準
このスキル完了後、少なくとも以下を満たすこと:
- サイトが公開されている
- GitHub Pages にアクセス可能
- 動画が再生可能
- リポジトリに自動更新ワークフローが存在する
- リポジトリに4つの
LARK_*secrets が存在する - 手動トリガーのワークフローが成功
- オンラインの
api/videos.jsonの_refreshed_atが新しい時刻
デフォルトでしないこと
- ローカルの
launchdをデフォルトで生成しない - ユーザーのMacの常時起動にデフォルトで依存しない
- すべての
file_tokenをハードコードしない - 最初のテーブルだけで続行しない
videos.jsonだけを生成し、カバーや構造化レコードを無視しない
参考実装
検証済みのアプローチが必要な場合、まずスキルリポジトリのテンプレートを直接再利用:
assets/refresh.pyassets/refresh.ymlassets/portfolio.sample.json
実際のプロジェクトでは、最終的にこれらのファイルが生成される:
refresh.py.github/workflows/refresh.ymlapi/videos.jsonapi/covers.jsonapi/portfolio.json
既存のリポジトリパターンを優先的に再利用し、その後ユーザーサイトのスタイルに合わせて適応する。