modscape 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +208 -0
- package/README.md +76 -26
- package/package.json +1 -1
- package/src/export.js +27 -12
- package/src/index.js +1 -1
- package/src/templates/rules.md +3 -4
- package/visualizer/package.json +1 -1
- package/visualizer-dist/assets/index-5OYvnvnP.css +1 -0
- package/visualizer-dist/assets/index-CrpPTvCJ.js +40 -0
- package/visualizer-dist/index.html +2 -2
- package/visualizer-dist/assets/index-BJPtPm98.css +0 -1
- package/visualizer-dist/assets/index-CevUO3vN.js +0 -40
package/README.ja.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# <img src="./visualizer/public/favicon.svg" width="32" height="32" align="center" /> Modscape
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/modscape)
|
|
4
|
+
[](https://www.npmjs.com/package/modscape)
|
|
5
|
+
[](https://github.com/yujikawa/modscape/actions/workflows/deploy.yml)
|
|
6
|
+
[](https://github.com/yujikawa/modscape/actions/workflows/publish.yml)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
|
|
9
|
+
Modscapeは、YAMLベースのデータモデリング・ビジュアライザーです。データエンジニアやアーキテクトが、概念・論理・物理モデルのギャップを埋め、サンプルデータに基づいた「ストーリー」を共有しながら設計を進めることを支援します。
|
|
10
|
+
|
|
11
|
+
[ライブデモ](https://yujikawa.github.io/modscape/)
|
|
12
|
+
|
|
13
|
+
## 主な機能
|
|
14
|
+
|
|
15
|
+
- **YAMLファースト**: 単一のシンプルなYAMLファイルでデータモデル全体を定義。
|
|
16
|
+
- **統合サイドバー**: ナビゲーション、検索、YAML編集機能を備えた高機能サイドバー。
|
|
17
|
+
- **インタラクティブ・モデリング**:
|
|
18
|
+
- **ドラッグで接続**: カラムから別のテーブルへドラッグするだけでリレーションを作成。
|
|
19
|
+
- **プロパティ・エディタ**: テーブルやリレーションのメタデータをUIから直接編集。
|
|
20
|
+
- **インタラクティブ削除**: クリック一つでテーブルやリレーションを削除。
|
|
21
|
+
- **サンプルデータ「ストーリー」**: エンティティにサンプルデータを紐付け、データの目的を具体的に解説。
|
|
22
|
+
- **スマート・レイアウト**:
|
|
23
|
+
- **自動位置保存**: ドラッグ&ドロップで配置を調整。座標はYAMLに即座に反映。
|
|
24
|
+
- **適応型サイジング**: カラム数が多いテーブル(10行以上)は自動でリミットがかかり、スクロール表示に。
|
|
25
|
+
- **マルチファイル・サポート**: ディレクトリ内の複数のモデルをシームレスに切り替え。
|
|
26
|
+
- **ドキュメント・エクスポート**: Mermaid互換のMarkdownドキュメント(ER図、ドメインカタログを含む)を生成。
|
|
27
|
+
- **AIエージェント対応**: Gemini, Claude, Codex向けのプロンプトを用意。AIとの共同モデリングを支援。
|
|
28
|
+
|
|
29
|
+
## インストール
|
|
30
|
+
|
|
31
|
+
npm経由でグローバルにインストールします:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install -g modscape
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## はじめに
|
|
40
|
+
|
|
41
|
+
ワークフローに合わせて以下のいずれかの方法で開始してください。
|
|
42
|
+
|
|
43
|
+
### A: AI駆動のモデリング(推奨)
|
|
44
|
+
Gemini CLI, Claude Code, Cursor/CodexなどのAIアシスタントを使用する場合に最適です。
|
|
45
|
+
|
|
46
|
+
1. **初期化**: モデリングルールとAIエージェント用手順書を生成します。
|
|
47
|
+
```bash
|
|
48
|
+
modscape init
|
|
49
|
+
```
|
|
50
|
+
2. **起動**: モデルファイルを指定してビジュアライザーを起動します。
|
|
51
|
+
```bash
|
|
52
|
+
modscape dev model.yaml
|
|
53
|
+
```
|
|
54
|
+
3. **AIに指示**: `.modscape/rules.md` のルールに従って `model.yaml` にテーブルやカラムを追加するようAIに伝えてください。
|
|
55
|
+
|
|
56
|
+
### B: 手動モデリング
|
|
57
|
+
YAMLを直接編集して詳細なコントロールを行いたい場合に最適です。
|
|
58
|
+
|
|
59
|
+
1. **YAML作成**: `model.yaml` ファイルを作成します([モデルの定義](#モデルの定義-yaml)を参照)。
|
|
60
|
+
2. **起動**: ビジュアライザーを起動します。
|
|
61
|
+
```bash
|
|
62
|
+
modscape dev model.yaml
|
|
63
|
+
```
|
|
64
|
+
3. **サンプルを試す**: 付属のサンプルディレクトリを読み込むことも可能です:
|
|
65
|
+
```bash
|
|
66
|
+
modscape dev samples/
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## モデルの定義 (YAML)
|
|
72
|
+
|
|
73
|
+
Modscapeは、包括的でありながら人間が読みやすいYAMLスキーマを採用しています。この単一のファイルが、概念・論理・物理データモデルの「信頼できる唯一の情報源(SSOT)」として機能します。
|
|
74
|
+
|
|
75
|
+
### 完全なYAMLリファレンス
|
|
76
|
+
|
|
77
|
+
```yaml
|
|
78
|
+
# 1. Domains: 関連するテーブルをグループ化する視覚的なコンテナ
|
|
79
|
+
domains:
|
|
80
|
+
- id: sales_domain
|
|
81
|
+
name: 売上と注文
|
|
82
|
+
description: 主要な商取引
|
|
83
|
+
color: "rgba(59, 130, 246, 0.05)" # コンテナの背景色
|
|
84
|
+
tables: [orders, order_items] # 所属するテーブルIDのリスト
|
|
85
|
+
|
|
86
|
+
# 2. Tables: エンティティの定義
|
|
87
|
+
tables:
|
|
88
|
+
- id: orders
|
|
89
|
+
name: ORDERS
|
|
90
|
+
appearance:
|
|
91
|
+
type: fact # fact | dimension | hub | link | satellite
|
|
92
|
+
icon: 📦 # 絵文字や任意の文字
|
|
93
|
+
color: "#f87171" # このエンティティのカスタムテーマカラー
|
|
94
|
+
|
|
95
|
+
conceptual:
|
|
96
|
+
description: "顧客の購入記録"
|
|
97
|
+
tags: ["WHAT", "WHEN"] # BEAM* メソドロジーのタグ
|
|
98
|
+
|
|
99
|
+
physical:
|
|
100
|
+
name: T_ORDERS # データベース上の実際のテーブル名
|
|
101
|
+
schema: RAW_SALES # データベースのスキーマ名
|
|
102
|
+
|
|
103
|
+
columns:
|
|
104
|
+
- id: order_id
|
|
105
|
+
logical:
|
|
106
|
+
name: ORDER_ID
|
|
107
|
+
type: Integer
|
|
108
|
+
description: "注文のユニークな識別子"
|
|
109
|
+
isPrimaryKey: true
|
|
110
|
+
physical:
|
|
111
|
+
name: O_ID
|
|
112
|
+
type: NUMBER(38,0)
|
|
113
|
+
constraints: ["NOT NULL"]
|
|
114
|
+
|
|
115
|
+
- id: customer_id
|
|
116
|
+
logical:
|
|
117
|
+
name: CUSTOMER_ID
|
|
118
|
+
type: Integer
|
|
119
|
+
isForeignKey: true # 外部キーであることを示す
|
|
120
|
+
|
|
121
|
+
# 3. Sample Data: ストーリーを伝えるための現実的なデータ
|
|
122
|
+
# (2次元配列を直接記述。論理カラムの定義順にマッピングされます)
|
|
123
|
+
sampleData:
|
|
124
|
+
- [1001, 501]
|
|
125
|
+
- [1002, 502]
|
|
126
|
+
|
|
127
|
+
# 4. Relationships: テーブル間の接続
|
|
128
|
+
relationships:
|
|
129
|
+
- from: { table: orders, column: customer_id }
|
|
130
|
+
to: { table: customers, column: id }
|
|
131
|
+
type: many-to-one # one-to-one | one-to-many | many-to-many
|
|
132
|
+
|
|
133
|
+
# 5. Layout: 座標情報 (自動管理)
|
|
134
|
+
# 手動で記述する必要はありません。ドラッグするとModscapeが自動更新します。
|
|
135
|
+
layout:
|
|
136
|
+
orders: { x: 100, y: 100, width: 320, height: 400 }
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### スキーマの詳細解説
|
|
140
|
+
|
|
141
|
+
| セクション | フィールド | 説明 |
|
|
142
|
+
| :--- | :--- | :--- |
|
|
143
|
+
| **`domains`** | `color` | ビジュアライザー上でグループを囲むコンテナの背景色を定義します。 |
|
|
144
|
+
| **`appearance`**| `type` | 指定がない場合のヘッダーアイコンと色を決定します(標準: スター・スキーマまたはData Vault)。 |
|
|
145
|
+
| **`conceptual`**| `tags` | ビジネスコンテキストタグ。モデリングの粒度(WHO, WHAT, WHERE)の識別に役立ちます。 |
|
|
146
|
+
| **`physical`** | `name`/`schema` | 論理エンティティを実際のデータベースオブジェクトにマッピングします。 |
|
|
147
|
+
| **`columns`** | `isPrimaryKey` | 🔑 アイコンを表示し、テーブルの粒度を明示します。 |
|
|
148
|
+
| | `isForeignKey` | 🔩 アイコンを表示し、下流への接続を示します。 |
|
|
149
|
+
| **`sampleData`**| - | 2次元配列。論理カラムの定義順に値がマッピングされます。 |
|
|
150
|
+
| **`relationships`**| `type` | 接続線の矢印の形や視覚的なスタイルを制御します。 |
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 使い方
|
|
155
|
+
|
|
156
|
+
### 開発モード (Interactive Editor)
|
|
157
|
+
YAMLを編集し、エンティティを視覚的に配置するためのローカルセッションを開始します。
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# ディレクトリを指定して、中のすべてのモデルを管理
|
|
161
|
+
modscape dev models/
|
|
162
|
+
|
|
163
|
+
# または特定のファイルを指定
|
|
164
|
+
modscape dev my-model.yaml
|
|
165
|
+
```
|
|
166
|
+
- 自動的に `http://localhost:5173` が開きます。
|
|
167
|
+
- **永続化**: エンティティの配置変更はYAMLファイルに即座に保存されます。
|
|
168
|
+
- **セキュアルーティング**: モデルはスラッグ経由でアクセスされ、ローカルの絶対パスは隠蔽されます。
|
|
169
|
+
|
|
170
|
+
### ビルドモード (Static Site Generation)
|
|
171
|
+
ドキュメントを共有するための静的ウェブサイトを生成します(GitHub Pagesなどに最適)。
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
modscape build models/ -o dist-site
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### エクスポートモード (Static Documentation)
|
|
178
|
+
Mermaid図を含むMarkdownドキュメントを一括生成します。
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# 標準出力に書き出し
|
|
182
|
+
modscape export models/ecommerce.yaml
|
|
183
|
+
|
|
184
|
+
# ファイルに保存
|
|
185
|
+
modscape export models/ -o docs/
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## AIエージェントとの統合
|
|
189
|
+
|
|
190
|
+
ModscapeはAIアシスタントとの連携を前提に設計されています。`modscape init` を実行すると以下のファイルが生成されます:
|
|
191
|
+
|
|
192
|
+
- **`.modscape/rules.md`**: モデリング規約の「信頼できる唯一の情報源(SSOT)」。
|
|
193
|
+
- **エージェント向け手順書**: Gemini, Claude, Cursor/Codex向けの事前定義プロンプト。
|
|
194
|
+
|
|
195
|
+
AIに対して:*"Follow the rules in .modscape/rules.md to add a new billing domain to my model.yaml."* と指示してください。
|
|
196
|
+
|
|
197
|
+
## クレジット
|
|
198
|
+
|
|
199
|
+
Modscapeは以下の素晴らしいオープンソースプロジェクトによって支えられています:
|
|
200
|
+
|
|
201
|
+
- **[React Flow](https://reactflow.dev/)**: インタラクティブなグラフエンジン。
|
|
202
|
+
- **[Radix UI](https://www.radix-ui.com/)**: アクセシブルなUIプリミティブ。
|
|
203
|
+
- **[Lucide](https://lucide.dev/)**: 美しく一貫性のあるアイコン。
|
|
204
|
+
- **[shadcn/ui](https://ui.shadcn.com/)**: コンポーネントパターンとデザインインスピレーション。
|
|
205
|
+
- **[Express](https://expressjs.com/)**: ローカル開発環境のサーバー。
|
|
206
|
+
|
|
207
|
+
## ライセンス
|
|
208
|
+
MIT
|
package/README.md
CHANGED
|
@@ -14,8 +14,14 @@ Modscape is a YAML-driven data modeling visualizer. It helps data engineers and
|
|
|
14
14
|
|
|
15
15
|
- **YAML-First**: Define your entire data model in a single, simple YAML file.
|
|
16
16
|
- **Unified Sidebar**: A feature-rich sidebar for navigation, search, and YAML editing.
|
|
17
|
+
- **Interactive Modeling**:
|
|
18
|
+
- **Drag-to-Connect**: Create relationships by dragging from a column handle to another table.
|
|
19
|
+
- **Property Editor**: Edit table and relationship metadata directly in the UI.
|
|
20
|
+
- **Interactive Deletion**: Remove tables or relationships with a single click.
|
|
17
21
|
- **Sample Data "Stories"**: Attach sample data to entities to explain the data's purpose.
|
|
18
|
-
- **
|
|
22
|
+
- **Smart Layout**:
|
|
23
|
+
- **Auto-Positioning**: Arrange entities via drag-and-drop; positions are saved directly back to your YAML.
|
|
24
|
+
- **Adaptive Sizing**: Tables with many columns are automatically capped at 10 rows with scrolling for better canvas visibility.
|
|
19
25
|
- **Multi-file Support**: Manage multiple models in a single directory and switch between them seamlessly.
|
|
20
26
|
- **Documentation Export**: Generate Mermaid-compatible Markdown documentation including ER diagrams and domain catalogs.
|
|
21
27
|
- **AI-Agent Ready**: Scaffolding for Gemini, Claude, and Codex to help you model via AI.
|
|
@@ -65,40 +71,84 @@ Best for direct YAML editing and architectural control.
|
|
|
65
71
|
|
|
66
72
|
## Defining Your Model (YAML)
|
|
67
73
|
|
|
68
|
-
Modscape uses a human-readable YAML schema.
|
|
74
|
+
Modscape uses a comprehensive yet human-readable YAML schema. This single file acts as the **Single Source of Truth** for your conceptual, logical, and physical data models.
|
|
69
75
|
|
|
70
|
-
###
|
|
71
|
-
Here is the basic structure for your `model.yaml`:
|
|
76
|
+
### Complete YAML Reference
|
|
72
77
|
|
|
73
78
|
```yaml
|
|
79
|
+
# 1. Domains: Visual containers to group related tables
|
|
80
|
+
domains:
|
|
81
|
+
- id: sales_domain
|
|
82
|
+
name: Sales & Orders
|
|
83
|
+
description: Core commerce transactions
|
|
84
|
+
color: "rgba(59, 130, 246, 0.05)" # Container background
|
|
85
|
+
tables: [orders, order_items] # List of table IDs
|
|
86
|
+
|
|
87
|
+
# 2. Tables: Entity definitions
|
|
74
88
|
tables:
|
|
75
|
-
- id:
|
|
76
|
-
name:
|
|
89
|
+
- id: orders
|
|
90
|
+
name: ORDERS
|
|
77
91
|
appearance:
|
|
78
|
-
type:
|
|
79
|
-
icon:
|
|
92
|
+
type: fact # fact | dimension | hub | link | satellite
|
|
93
|
+
icon: 📦 # Any emoji or character
|
|
94
|
+
color: "#f87171" # Custom theme color for this entity
|
|
95
|
+
|
|
96
|
+
conceptual:
|
|
97
|
+
description: "Records of customer purchases"
|
|
98
|
+
tags: ["WHAT", "WHEN"] # BEAM* methodology tags
|
|
99
|
+
|
|
100
|
+
physical:
|
|
101
|
+
name: T_ORDERS # Actual table name in database
|
|
102
|
+
schema: RAW_SALES # Database schema name
|
|
103
|
+
|
|
80
104
|
columns:
|
|
81
|
-
- id:
|
|
82
|
-
logical:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
logical: { name: HK_CUSTOMER, type: Binary, isPrimaryKey: true }
|
|
105
|
+
- id: order_id
|
|
106
|
+
logical:
|
|
107
|
+
name: ORDER_ID
|
|
108
|
+
type: Integer
|
|
109
|
+
description: "Unique identifier for an order"
|
|
110
|
+
isPrimaryKey: true
|
|
111
|
+
physical:
|
|
112
|
+
name: O_ID
|
|
113
|
+
type: NUMBER(38,0)
|
|
114
|
+
constraints: ["NOT NULL"]
|
|
115
|
+
|
|
93
116
|
- id: customer_id
|
|
94
|
-
logical:
|
|
117
|
+
logical:
|
|
118
|
+
name: CUSTOMER_ID
|
|
119
|
+
type: Integer
|
|
120
|
+
isForeignKey: true
|
|
121
|
+
|
|
122
|
+
# 3. Sample Data: Realistic data for storytelling
|
|
123
|
+
# (Direct 2D array, mapped to logical columns by index)
|
|
124
|
+
sampleData:
|
|
125
|
+
- [1001, 501]
|
|
126
|
+
- [1002, 502]
|
|
127
|
+
|
|
128
|
+
# 4. Relationships: Connections between tables
|
|
129
|
+
relationships:
|
|
130
|
+
- from: { table: orders, column: customer_id }
|
|
131
|
+
to: { table: customers, column: id }
|
|
132
|
+
type: many-to-one # one-to-one | one-to-many | many-to-many
|
|
133
|
+
|
|
134
|
+
# 5. Layout: Visual coordinates (Auto-managed)
|
|
135
|
+
# You don't need to write this manually; Modscape updates it on drag.
|
|
136
|
+
layout:
|
|
137
|
+
orders: { x: 100, y: 100, width: 320, height: 400 }
|
|
95
138
|
```
|
|
96
139
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
140
|
+
### Schema Breakdown
|
|
141
|
+
|
|
142
|
+
| Section | Field | Description |
|
|
143
|
+
| :--- | :--- | :--- |
|
|
144
|
+
| **`domains`** | `color` | Defines the background of the grouping container in the visualizer. |
|
|
145
|
+
| **`appearance`**| `type` | Determines the header icon/color if not specified (Standard: Star Schema or Data Vault). |
|
|
146
|
+
| **`conceptual`**| `tags` | Business context tags. Great for identifying key modeling grains (WHO, WHAT, WHERE). |
|
|
147
|
+
| **`physical`** | `name`/`schema` | Maps the logical entity to your real database objects. |
|
|
148
|
+
| **`columns`** | `isPrimaryKey` | Adds a 🔑 icon and marks the grain of the table. |
|
|
149
|
+
| | `isForeignKey` | Adds a 🔩 icon to indicate a downstream connection. |
|
|
150
|
+
| **`sampleData`**| - | A direct 2D array of rows. Values are mapped to logical columns by index. |
|
|
151
|
+
| **`relationships`**| `type` | Controls the arrowheads and visual style of the connection lines. |
|
|
102
152
|
|
|
103
153
|
---
|
|
104
154
|
|
package/package.json
CHANGED
package/src/export.js
CHANGED
|
@@ -18,12 +18,26 @@ function normalizeSchema(data) {
|
|
|
18
18
|
layout: data.layout || {}
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
schema.tables = schema.tables.map(table =>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
schema.tables = schema.tables.map(table => {
|
|
22
|
+
let sampleData = table.sampleData;
|
|
23
|
+
|
|
24
|
+
// Migrate legacy format { columns: [...], rows: [[...]] } to new [[...]] format
|
|
25
|
+
if (sampleData && typeof sampleData === 'object' && !Array.isArray(sampleData)) {
|
|
26
|
+
if (Array.isArray(sampleData.rows)) {
|
|
27
|
+
sampleData = sampleData.rows;
|
|
28
|
+
} else {
|
|
29
|
+
sampleData = [];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
...table,
|
|
35
|
+
id: table.id || 'unknown',
|
|
36
|
+
name: table.name || table.id || 'Unnamed Table',
|
|
37
|
+
columns: Array.isArray(table.columns) ? table.columns : [],
|
|
38
|
+
sampleData: Array.isArray(sampleData) ? sampleData : []
|
|
39
|
+
};
|
|
40
|
+
});
|
|
27
41
|
|
|
28
42
|
return schema;
|
|
29
43
|
}
|
|
@@ -128,15 +142,16 @@ export function generateMarkdown(schema, modelName) {
|
|
|
128
142
|
}
|
|
129
143
|
|
|
130
144
|
// Sample Data
|
|
131
|
-
if (table.sampleData && table.sampleData.
|
|
145
|
+
if (table.sampleData && table.sampleData.length > 0) {
|
|
132
146
|
md += '#### Sample Data\n\n';
|
|
133
|
-
|
|
134
|
-
md += '| ' +
|
|
135
|
-
|
|
147
|
+
const headers = table.columns.map(c => c.logical?.name || c.id);
|
|
148
|
+
md += '| ' + headers.join(' | ') + ' |\n';
|
|
149
|
+
md += '| ' + headers.map(() => '---').join(' | ') + ' |\n';
|
|
150
|
+
table.sampleData.slice(0, 5).forEach(row => {
|
|
136
151
|
md += '| ' + row.join(' | ') + ' |\n';
|
|
137
152
|
});
|
|
138
|
-
if (table.sampleData.
|
|
139
|
-
md += `\n*(Showing 5 of ${table.sampleData.
|
|
153
|
+
if (table.sampleData.length > 5) {
|
|
154
|
+
md += `\n*(Showing 5 of ${table.sampleData.length} rows)*\n`;
|
|
140
155
|
}
|
|
141
156
|
md += '\n';
|
|
142
157
|
}
|
package/src/index.js
CHANGED
package/src/templates/rules.md
CHANGED
|
@@ -42,10 +42,9 @@ tables:
|
|
|
42
42
|
name: "physical_name"
|
|
43
43
|
type: "DB_TYPE"
|
|
44
44
|
constraints: ["NOT NULL", "UNIQUE"]
|
|
45
|
-
sampleData: # Optional
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
- ["value1", "value2"]
|
|
45
|
+
sampleData: # Optional: 2D array mapped to columns by index
|
|
46
|
+
- ["value1", "value2"] # Corresponds to first and second column
|
|
47
|
+
- ["value3", "value4"]
|
|
49
48
|
|
|
50
49
|
domains: # Optional: Group tables into visual containers
|
|
51
50
|
- id: domain_id
|
package/visualizer/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.static{position:static}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.-right-3\.5{right:-.875rem}.left-3{left:.75rem}.right-3{right:.75rem}.right-4{right:1rem}.top-0{top:0}.top-1\/2{top:50%}.top-4{top:1rem}.z-10{z-index:10}.z-50{z-index:50}.z-\[60\]{z-index:60}.mx-1{margin-left:.25rem;margin-right:.25rem}.my-1{margin-top:.25rem;margin-bottom:.25rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.flex{display:flex}.inline-flex{display:inline-flex}.\!table{display:table!important}.table{display:table}.grid{display:grid}.hidden{display:none}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-12{height:3rem}.h-2\.5{height:.625rem}.h-5{height:1.25rem}.h-7{height:1.75rem}.h-9{height:2.25rem}.h-\[28px\]{height:28px}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.min-h-0{min-height:0px}.w-1\.5{width:.375rem}.w-10{width:2.5rem}.w-2\.5{width:.625rem}.w-5{width:1.25rem}.w-7{width:1.75rem}.w-9{width:2.25rem}.w-\[1px\]{width:1px}.w-\[28px\]{width:28px}.w-\[400px\]{width:400px}.w-\[50px\]{width:50px}.w-full{width:100%}.w-screen{width:100vw}.min-w-\[140px\]{min-width:140px}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.caption-bottom{caption-side:bottom}.border-collapse{border-collapse:collapse}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.resize{resize:both}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-none{border-style:none}.border-blue-500\/20{border-color:#3b82f633}.border-blue-500\/30{border-color:#3b82f64d}.border-emerald-500\/30{border-color:#10b9814d}.border-red-500\/30{border-color:#ef44444d}.border-slate-700{--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity, 1))}.border-slate-700\/50{border-color:#33415580}.border-slate-800{--tw-border-opacity: 1;border-color:rgb(30 41 59 / var(--tw-border-opacity, 1))}.border-transparent{border-color:transparent}.border-white{--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity, 1))}.border-l-transparent{border-left-color:transparent}.border-t-transparent{border-top-color:transparent}.bg-\[\#ef4444\]{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-blue-500\/10{background-color:#3b82f61a}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-emerald-500\/10{background-color:#10b9811a}.bg-emerald-600{--tw-bg-opacity: 1;background-color:rgb(5 150 105 / var(--tw-bg-opacity, 1))}.bg-red-500\/10{background-color:#ef44441a}.bg-slate-700{--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity, 1))}.bg-slate-800{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity, 1))}.bg-slate-800\/50{background-color:#1e293b80}.bg-slate-900{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity, 1))}.bg-slate-900\/20{background-color:#0f172a33}.bg-slate-900\/50{background-color:#0f172a80}.bg-slate-900\/80{background-color:#0f172acc}.bg-slate-950{--tw-bg-opacity: 1;background-color:rgb(2 6 23 / var(--tw-bg-opacity, 1))}.bg-slate-950\/20{background-color:#02061733}.bg-transparent{background-color:transparent}.p-0{padding:0}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.p-\[1px\]{padding:1px}.px-0{padding-left:0;padding-right:0}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pl-9{padding-left:2.25rem}.pr-1{padding-right:.25rem}.pr-4{padding-right:1rem}.pr-8{padding-right:2rem}.pt-0{padding-top:0}.pt-1{padding-top:.25rem}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.text-left{text-align:left}.text-center{text-align:center}.align-middle{vertical-align:middle}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-base{font-size:1rem;line-height:1.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.leading-none{line-height:1}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-blue-300{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-emerald-400{--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-red-500\/50{color:#ef444480}.text-slate-100{--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity, 1))}.text-slate-200{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity, 1))}.text-slate-300{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.text-slate-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.text-slate-600{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity, 1))}.text-slate-700{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.underline-offset-4{text-underline-offset:4px}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-20{opacity:.2}.opacity-50{opacity:.5}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_4px_6px_rgba\(0\,0\,0\,0\.3\)\]{--tw-shadow: 0 4px 6px rgba(0,0,0,.3);--tw-shadow-colored: 0 4px 6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-inner{--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / .05);--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-black\/50{--tw-shadow-color: rgb(0 0 0 / .5);--tw-shadow: var(--tw-shadow-colored)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-md{--tw-backdrop-blur: blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}:root{--background: 222.2 84% 4.9%;--foreground: 210 40% 98%}*{border-color:#1e293b}body{margin:0;padding:0;background-color:#020617;color:#f1f5f9}#root{height:100vh;width:100vw}.react-flow,.react-flow__background{background-color:#020617}.react-flow__controls{background:#0f172a!important;border:1px solid #1e293b!important;border-radius:4px}.react-flow__controls-button{background:#0f172a!important;border-bottom:1px solid #1e293b!important;fill:#f1f5f9!important}.react-flow__controls-button:hover{background:#1e293b!important}.react-flow__controls-button svg{fill:#f1f5f9!important}.column-row:hover .column-handle{opacity:1!important;background-color:#3b82f6!important;width:8px!important;height:8px!important;border-radius:50%!important;border:2px solid #ffffff!important;transition:opacity .15s ease-in-out}.column-handle{transition:opacity .15s ease-in-out;pointer-events:auto}.hover\:border-slate-700\/50:hover{border-color:#33415580}.hover\:bg-blue-700:hover{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity, 1))}.hover\:bg-emerald-700:hover{--tw-bg-opacity: 1;background-color:rgb(4 120 87 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-800:hover{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-800\/30:hover{background-color:#1e293b4d}.hover\:bg-slate-800\/50:hover{background-color:#1e293b80}.hover\:text-red-400:hover{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.hover\:text-red-500:hover{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-50:hover{opacity:.5}.focus\:bg-slate-800:focus{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.focus\:ring-blue-500\/50:focus{--tw-ring-color: rgb(59 130 246 / .5)}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width: 2px}.active\:scale-95:active{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:scale-110{--tw-scale-x: 1.1;--tw-scale-y: 1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:text-slate-300{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:opacity-100{opacity:1}.data-\[state\=inactive\]\:hidden[data-state=inactive]{display:none}.data-\[state\=active\]\:bg-blue-600[data-state=active]{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.data-\[state\=active\]\:text-white[data-state=active]{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.data-\[state\=active\]\:shadow-sm[data-state=active]{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]){padding-right:0}.\[\&\>tr\]\:last\:border-b-0:last-child>tr{border-bottom-width:0px}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{width:1rem;height:1rem}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_tr\:last-child\]\:border-0 tr:last-child{border-width:0px}.\[\&_tr\]\:border-b tr{border-bottom-width:1px}.react-flow{direction:ltr}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1;cursor:grab}.react-flow__pane.selection{cursor:pointer}.react-flow__pane.dragging{cursor:grabbing}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow .react-flow__edges{pointer-events:none;overflow:visible}.react-flow__edge-path,.react-flow__connection-path{stroke:#b1b1b7;stroke-width:1;fill:none}.react-flow__edge{pointer-events:visibleStroke;cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge:focus .react-flow__edge-path,.react-flow__edge:focus-visible .react-flow__edge-path{stroke:#555}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge-textbg{fill:#fff}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__connectionline{z-index:1001}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:grab}.react-flow__node.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background:#1a192b;border:1px solid white;border-radius:100%}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:-4px;transform:translate(-50%)}.react-flow__handle-top{left:50%;top:-4px;transform:translate(-50%)}.react-flow__handle-left{top:50%;left:-4px;transform:translateY(-50%)}.react-flow__handle-right{right:-4px;top:50%;transform:translateY(-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.center{left:50%;transform:translate(-50%)}.react-flow__attribution{font-size:10px;background:#ffffff80;padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-default,.react-flow__node-input,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:3px;width:150px;font-size:12px;color:#222;text-align:center;border-width:1px;border-style:solid;border-color:#1a192b;background-color:#fff}.react-flow__node-default.selectable:hover,.react-flow__node-input.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:0 1px 4px 1px #00000014}.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:0 0 0 .5px #1a192b}.react-flow__node-group{background-color:#f0f0f040}.react-flow__nodesselection-rect,.react-flow__selection{background:#0059dc14;border:1px dotted rgba(0,89,220,.8)}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls{box-shadow:0 0 2px 1px #00000014}.react-flow__controls-button{border:none;background:#fefefe;border-bottom:1px solid #eee;box-sizing:content-box;display:flex;justify-content:center;align-items:center;width:16px;height:16px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;padding:5px}.react-flow__controls-button:hover{background:#f4f4f4}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__minimap{background-color:#fff}.react-flow__minimap svg{display:block}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:4px;height:4px;border:1px solid #fff;border-radius:1px;background-color:#3367d9;transform:translate(-50%,-50%)}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:#3367d9;border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}
|