objectstack-ui について
objectstack-uiは、ObjectStack仕様に沿ったUI設計を支援するスキルです。ビュー、ナビゲーション、ダッシュボード、レポート、ページ、アクションを扱います。
主な機能
- 01リスト・フォームビューの設計ガイド
- 02マスターディテールフォームのパターン
- 03アプリナビゲーション構成
- 04ダッシュボードとレポートの設定
- 05ページとアクションの設計
活用シーン
- 01メタデータ駆動のCRM UI設計
- 02子レコードを含む親フォームの設定
- 03アプリナビゲーションとカスタムページの構成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ objectstack-ai/ objectstack --skill objectstack-uiインストール後、Claude Code で/objectstack-uiを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
evals/README.mdanalytics-inline-vs-dataset.jsonviews-apps-actions-pages.jsonreferences/_index.mdreact-blocks.mdrules/actions.mdlist-views.mddashboards.mdnavigation.mdpages.md詳細
UI デザイン — ObjectStack UI プロトコル
ObjectStack 仕様を使用してユーザーインターフェースを設計するための専門家向け指示です。このスキルでは、ビュー(リスト、フォーム、カンバン、カレンダー、…)、アプリのナビゲーション、ダッシュボード、レポート、アクションを扱います。
ビューの種類
リストビュー
| 種類 | 使用する場面 |
|---|---|
grid | 標準的なデータテーブル — ほとんどのオブジェクトでデフォルト |
kanban | 列を使ったビジュアルボード(ステータス主導のワークフロー) |
gallery | カードベースの石積みレイアウト(ビジュアルカタログ、連絡先) |
calendar | 日付ベースのスケジュール管理(イベント、タスク、予約) |
timeline | 時系列のアクティビティストリーム |
gantt | 依存関係を追跡するプロジェクト管理 |
map | location フィールドを持つ地理空間レコード |
chart | オブジェクト全体の集計を視覚化(ミニチャートビュー) |
tree | 自己参照型の階層構造(ツリーグリッド) |
フォームビュー
| 種類 | 使用する場面 |
|---|---|
simple | 1 ページのフォーム — フィールドが 15 個以下のオブジェクトに適しています |
tabbed | タブ付きセクション — 多くのフィールドグループを持つ複雑なオブジェクト向け |
wizard | ステップ形式のフロー — ガイド付きデータ入力(オンボーディング、申請) |
マスターディテールフォーム(親+子明細行)
ユーザーがレコードを子明細行と一緒に(請求書+明細行、プロジェクト+タスク)入力し、アトミックに保存できるようにする場合、カスタムページやフォーム設定が必要になることはほとんどありません。次の順に優先してください。
-
リレーションシップの
inlineEdit(デフォルト、UI 設定不要)。 データモデルで宣言します。親を参照する子のmaster_detailフィールドにinlineEditを設定してください。親の標準的な新規作成/編集フォーム(モーダル、ドロワー、全画面)には子が自動的に表示され、親と子が 1 回のアトミックな/api/v1/batchで保存されます。ビューのメタデータは不要です。true/'grid'/'form'の段階と、それぞれの値で選ばれる内容については、objectstack-data → Relationships → Inline Editing を参照してください。 -
フォームビューの
subforms(上書き/調整)。 派生した列や順序を上書きする場合、またはリレーションシップでインライン表示に指定されていない子を表示する場合にのみ、フォームビューに追加してください。form: { type: 'simple', sections: [{ group: 'invoice_header' }], // a declared fieldGroup subforms: [ { childObject: 'invoice_line', // relationshipField + columns are title: 'Line Items', // derived from the child object; addLabel: 'Add line' }, // set `columns` here only to override. ], }, -
object-master-detail-formページブロック(独自レイアウト)。 自由形式のレイアウトが必要な場合にのみページを使用してください。同じdetails: [{ childObject }]の省略記法を使えます。
リレーションシップの外部キーとグリッド列は、いずれの場合も子オブジェクトのメタデータから派生します。選択肢とルックアップも引き継がれます。親の summary フィールドでは、子の値をサーバー側で集計できます(objectstack-data を参照)。
明細行グリッドの動作(grid モード)。 編集可能なグリッドは、実際のスプレッドシート形式の明細エディター(QuickBooks / Stripe / NetSuite のパターン)です。計算列、カタログのタイプアヘッドによる自動入力、保存されるドラッグ並べ替え、リアルタイムの小計 → 税 → 合計の連鎖など、すべての動作はデータモデルから派生します。objectstack-data → Relationships → Inline Editing を参照してください。この側で設定するのは 3 つです。末尾には常にゴースト行が存在するため、「明細行を追加」ボタンは追加しないでください(ゴースト行に入力すると行が作成され、何も入力されていない行は保存されません)。autofill: false を指定すると、対象の列はタイプアヘッドによるコピーから除外されます。また、フォームの taxRateField で、合計の計算に使用する親フィールドを上書きできます。
読み取り側 — 詳細ページの関連リスト。 inlineEdit に対応するのは、親レコードの詳細ページにある関連リストです。関連リストを独自に定義する必要はありません。すべての子リレーションシップはデフォルトで関連リストとして表示されます(所有する master_detail の子が最初に表示されます)。リレーションシップで次のように調整できます。
relatedList: 'primary'— 重要なリレーションシップとして指定します。詳細ページでは独立したタブに昇格します(下記のレイアウトを参照)。これはレイアウトを切り替える指定ではなく、重要度を示す意図です(ADR-0085)。relatedList: false— 詳細ページから、不要な子の表示を抑制します。relatedListTitle/relatedListColumns— 派生したタイトル/列を上書きします(どちらも任意です。列は、指定がなければ子オブジェクトのhighlightFieldsから自動的に派生します)。objectstack-data → Relationships → Detail-page related lists を参照してください。
関連リストのレイアウト。 生成されるレコード詳細ページでは、relatedList: 'primary' の子はそれぞれ独立したタブになり、それ以外の関連リストはすべて 1 つの共通の Related タブに積み重ねて表示されます。そのため、子テーブルを第一級のタブに昇格するには、リレーションシップを 1 語変更するだけです。カスタムページは必要ありません。オブジェクトでは、画面ごとのレイアウトヒントを宣言しません。以前の detail.relatedLayout 切り替え設定とオブジェクトレベルの detail: {...} ブロックは削除済みです(ADR-0085)。relatedLayout: 'tabs' | 'stack' は、アプリレベルのデフォルト上書きとしてのみ残り、オブジェクトのキーとしては使えません。リレーションシップ層では表現できない配置(例:Open と Closed を分けたタブ、チャート/レポートのタブ、厳密なタブ順序)が必要な場合は、オブジェクトにカスタムレコードページを割り当て、record:related_list(またはインライン編集可能な line_items)ブロックで明示的にレイアウトしてください。
フォームのフィールド条件ルール
フィールドのライフサイクルに属する条件である visibleWhen、readonlyWhen、
requiredWhen は、フォームビューではなく データモデルのフィールド に宣言します。
ObjectUI フォームはこれらを使用します。セマンティクスとサーバーの動作については、
objectstack-data → Conditional Field Rules を参照してください。
UI 側では、インラインのマスター・詳細グリッドは、各子行に対して行ごとにこれらのルールを評価します。
requiredWhen を使用してください。conditionalRequired エイリアスはプロトコル 17 で
削除され、現在は解析エラーになります。複雑な CEL を記述する場合は
objectstack-formula を読み込んでください。
詳細ルール
- リスト、かんばん、ガントビュー —
defineView、data、列、フィルタリング、userFilters、ツールバー検索、並べ替え、かんばん、ガント。 - ナビゲーションと実行モード —
App.create、3 つの実行モード、レコードの表示形式。 - ダッシュボード、レポート、キューブ — ウィジェット、データセットのバインディング、フィルター、
compareTo、バケット化、options、ドリルダウン、レポート設定、キューブ。 - ページとドキュメント — ページタイプ、領域、コンポーネント、印刷ページ、html/react ソース階層、スタイル設定、パッケージドキュメント。
- アクション —
locations、表示設定、例、ctx、新しいタブ、パラメーター。
アプリのナビゲーション
App は、オブジェクト、ダッシュボード、レポート、カスタムページを構造化された
ナビゲーションツリーにまとめます。@objectstack/spec/ui の
App.create({...}) で構築し、defineStack({ apps: [...] }) に登録します。
⛔ type: 'app' のパッケージにつき App は 1 つだけです — ADR-0019
(app-as-consumer-unit)D3 に基づき、defineStack は読み込み時に 2 つ目の App を拒否します。
対象者を増やす場合は、2 つ目の App を作成せず、1 つの App 内 に
requiredPermissions で制御するグループを追加してください。
ナビゲーション項目の種類
| 種類 | プロパティ | 用途 |
|---|---|---|
group | label、icon、expanded、children[] | 折りたたみ可能な項目グループ |
object | objectName、viewName?、recordId?、filters?、label、icon | オブジェクトのリスト、名前付きビュー、レコードへのディープリンク、または基本データサーフェス上の filters スライスへのリンク |
dashboard | dashboardName、label、icon | ダッシュボードへのリンク |
report | reportName、label、icon | レポートへのリンク |
page | pageName、label、icon | カスタムページへのリンク |
url | url、label、icon | 外部 URL またはカスタム URL |
action | actionDef({ actionName, params? })、label、icon | ナビゲーションの代わりにアクションを実行 |
component | componentRef、params?、label、icon | 組み込みのプラットフォームコンポーネント。componentRef はコロンで連結したレジストリキー(metadata:resource)です |
doc | book? / doc?(いずれか必須)、label、icon | パッケージドキュメント。ドキュメントの対象者設定を継承 |
separator | — | 視覚的な区切り |
requiresObject/requiresService: オプションのシステムオブジェクトまたはカーネルサービスに依存する項目には、これらを使用してください。依存対象がない場合、ナビゲーション項目は自動的に非表示になります。条件付き UI をハードコードしないでください。
レポートの種類
| 種類 | 使用する場面 |
|---|---|
tabular | 列とフィルターを含むフラットなデータテーブル |
summary | 小計を含むグループ化データ(例:地域別の売上) |
matrix | クロス集計/ピボットテーブル(rows が縦方向 × columns が横方向) |
joined | 複数ブロックからなる分析サーフェス(複数のサブレポートを組み合わせる) |
chart レポートタイプはありません。レポートの視覚化には、埋め込みの
chart: 設定を使用します。
CRM UI ブループリント(メタデータ優先)
この CRM スタイルの構造を、UI を構築する際の標準リファレンスとして使用します。
| UI サーフェス | 一般的な場所 | 従うパターン |
|---|---|---|
| 複数ビューのオブジェクト UI | src/views/*.view.ts | デフォルトの list と form を定義し、シナリオ別に名前付きの listViews / formViews を定義します |
| 公開フォーム | src/views/*.view.ts(sharing: { enabled: true, allowAnonymous: true, publicLink: 'slug' } を含む formView) | GET/POST /api/v1/forms/:slug の Web-to-Lead |
| アプリナビゲーション | src/apps/*.app.ts | グループ化したナビゲーションツリー、viewName ショートカット、機能に応じた表示制御のための requiresObject を使用します |
| アナリティクスデータセット | src/datasets/*.dataset.ts | レポート対象にするオブジェクトごとに 1 つ定義します — ダッシュボードとレポートは名前で宣言済みデータセットにバインドされるため、データセットのないオブジェクトにはアナリティクスの表示手段がありません |
| ダッシュボード | src/dashboards/*.dashboard.ts | KPI、チャート、テーブルのウィジェットを、共通の dateRange と globalFilters と組み合わせます |
| レポート | src/reports/*.report.ts | そのデータセットから rows(ディメンション)と values(メジャー)を選択します。形式は表形式/サマリー/マトリックス/結合です |
| レコードページ | src/pages/*.page.ts | regions とコンポーネント(page:header、record:highlights、関連リスト、タブ)を組み合わせます |
| ユーザーアクション | src/actions/*.actions.ts | オーケストレーションには flow を、パラメーター化された一括変更には script を使用します |
このブループリントは、「メタデータアプリの UI 全体を構築する」タスクのデフォルトです。
日付マクロ — フィルタープレースホルダー
リストビュー、ダッシュボード、レポート、ページのフィルター値では、相対日付トークン({today}、{current_month_start}、{30_days_ago})と、セッショントークン {current_user_id} / {current_org_id} を使用できます。トークンの語彙、受け付ける 2 つの表記、2 つのリゾルバー、およびよく似た誤りの一覧は objectstack-query の rules/filters.md で管理されています — フィルタートークンを書く前に読み込んでください。
認識されないトークンはビルドエラーとなり、実行時にも例外が発生します。リテラルとして黙って扱われることはありません。
このパッケージが管理するルールは、トークンの語彙ではなく UI サーフェスに関する次の 2 つです。
- フィルタートークンは表示範囲であり、セキュリティではありません。サーフェスに何が表示されるかを決めるのはフィルタートークンであり、呼び出し元が何を読み取れるかを決めるのは RLS です。
{current_user_id}フィルターを削除するとビューの範囲は広がりますが、ユーザーが他のユーザーのデータにアクセスできるかどうかを左右する要素にしてはなりません。 AppContextSelectorの ID(例:{active_package})は、ナビゲーションのrecordId/paramsでのみ解決されます。フィルターの評価では、サイドバーのセレクター状態は使用されません。
アクション
アクションは、オブジェクトまたはビューに関連付けられた、ユーザーが起動する操作です。
defineStack({ actions: [...] }) の下に登録します。
アクションの種類
type | 目的 | 必須フィールド |
|---|---|---|
script | サーバー上でインラインの L2 フック本体(サンドボックス化された JS)を実行します | body(または target = 登録済み関数名) |
url | 内部ルートまたは外部 URL に移動します | target |
modal | target で指定されたページを開きます。body が実行されるのは script の場合のみです | target |
flow | 名前で画面フローまたは自動起動フローを起動します | target |
api | 登録済みの API エンドポイントを呼び出します | target |
form | 名前で FormView を開きます(/_console/forms/:name にルーティングされます) | target |
よくある落とし穴
-
グリッドビューに列を詰め込みすぎること。 通常、ユーザーがデフォルトで表示する列は 6~8 列までで十分です。補助的な列には
hiddenを使用してください。 -
主列に
link: trueを設定し忘れること。 最初の意味のある列(通常は名前/件名)を、レコード詳細への ナビゲーションリンクにしてください。 -
ウィジェットのグリッド配置を
positionに設定すること。 グリッド配置フィールドはlayout: { x, y, w, h }です。positionは ウィジェットのキーではなく、クローズドスキーマでは名前で拒否されます(プロトコル 17 より前は警告なく 削除されていました)。layoutは省略可能です。省略するとウィジェットは自動的に 配置されます(Studio デザイナーはこれを前提としています)。明示的なグリッド位置を指定したい 場合にのみ設定してください。
作業の検証
*.view.ts / *.action.ts / *.dashboard.ts を作成した後、完了を報告する前に
作成時の検証を実行してください。
os validate # CEL predicates (record.<field>) + widget bindings + schema
# or: os build # the same gates, plus emits dist/
実行時にはどちらも警告なしに問題が起きる、UI 固有の落とし穴を 2 つ検出します。
- アクション/フィールドの述語 — アクションの
visible/disabledやフィールドのvisibleWhenにフィールド参照のみを指定すると(record.doneではなくdone)、評価結果がnullとなり、すべてのレコードで コントロールが非表示になります(「ボタンが表示されない」落とし穴)。 - ダッシュボードのウィジェットバインディング — 宣言済みのデータセット/フィールドに解決できない
dataset/dimensions/valuesをウィジェットに指定すると、空のグラフが 表示されます(ADR-0021)。
os validate が成功するまで、ビュー/アクション/ダッシュボードの作業完了を報告しないでください。スキャフォールド済みのプロジェクトでは、検証コマンドは npm run validate です。
参考資料
Zod スキーマの全一覧(各項目に 1 行の説明を付記)については、references/_index.md を参照してください。各項目は
node_modules/@objectstack/spec/src/ 内の該当箇所を示しています。フィールドの正確な
構造は必ずソースを Read して確認し、プロパティ名の記憶に頼らないでください。