@superblocksteam/vite-plugin-file-sync 2.0.93 → 2.0.94-next.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/dist/ai-service/agent/prompts/api-prompts.d.ts.map +1 -1
- package/dist/ai-service/agent/prompts/api-prompts.js +9 -1
- package/dist/ai-service/agent/prompts/api-prompts.js.map +1 -1
- package/dist/ai-service/agent/prompts/build-base-system-prompt.d.ts.map +1 -1
- package/dist/ai-service/agent/prompts/build-base-system-prompt.js +14 -1
- package/dist/ai-service/agent/prompts/build-base-system-prompt.js.map +1 -1
- package/dist/ai-service/agent/tools/apis/analysis.js +4 -4
- package/dist/ai-service/agent/tools/apis/analysis.js.map +1 -1
- package/dist/ai-service/agent/tools/apis/get-api-docs.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/apis/get-api-docs.js +3 -1
- package/dist/ai-service/agent/tools/apis/get-api-docs.js.map +1 -1
- package/dist/ai-service/agent/tools.d.ts.map +1 -1
- package/dist/ai-service/agent/tools.js +20 -6
- package/dist/ai-service/agent/tools.js.map +1 -1
- package/dist/ai-service/agent/tools2/access-control.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/access-control.js +5 -26
- package/dist/ai-service/agent/tools2/access-control.js.map +1 -1
- package/dist/ai-service/agent/tools2/tools/git-commit.d.ts +15 -0
- package/dist/ai-service/agent/tools2/tools/git-commit.d.ts.map +1 -0
- package/dist/ai-service/agent/tools2/tools/git-commit.js +105 -0
- package/dist/ai-service/agent/tools2/tools/git-commit.js.map +1 -0
- package/dist/ai-service/agent/tools2/tools/git-diff.d.ts +9 -0
- package/dist/ai-service/agent/tools2/tools/git-diff.d.ts.map +1 -0
- package/dist/ai-service/agent/tools2/tools/git-diff.js +23 -0
- package/dist/ai-service/agent/tools2/tools/git-diff.js.map +1 -0
- package/dist/ai-service/agent/tools2/tools/git-log.d.ts +12 -0
- package/dist/ai-service/agent/tools2/tools/git-log.d.ts.map +1 -0
- package/dist/ai-service/agent/tools2/tools/git-log.js +27 -0
- package/dist/ai-service/agent/tools2/tools/git-log.js.map +1 -0
- package/dist/ai-service/agent/tools2/tools/git-merge-abort.d.ts +7 -0
- package/dist/ai-service/agent/tools2/tools/git-merge-abort.d.ts.map +1 -0
- package/dist/ai-service/agent/tools2/tools/git-merge-abort.js +42 -0
- package/dist/ai-service/agent/tools2/tools/git-merge-abort.js.map +1 -0
- package/dist/ai-service/agent/tools2/tools/git-pull.d.ts +20 -0
- package/dist/ai-service/agent/tools2/tools/git-pull.d.ts.map +1 -0
- package/dist/ai-service/agent/tools2/tools/git-pull.js +80 -0
- package/dist/ai-service/agent/tools2/tools/git-pull.js.map +1 -0
- package/dist/ai-service/agent/tools2/tools/git-stage.d.ts +9 -0
- package/dist/ai-service/agent/tools2/tools/git-stage.d.ts.map +1 -0
- package/dist/ai-service/agent/tools2/tools/git-stage.js +34 -0
- package/dist/ai-service/agent/tools2/tools/git-stage.js.map +1 -0
- package/dist/ai-service/agent/tools2/tools/git-status.d.ts +9 -0
- package/dist/ai-service/agent/tools2/tools/git-status.d.ts.map +1 -0
- package/dist/ai-service/agent/tools2/tools/git-status.js +18 -0
- package/dist/ai-service/agent/tools2/tools/git-status.js.map +1 -0
- package/dist/ai-service/agent/tools2/tools/grep-metadata.d.ts +1 -1
- package/dist/ai-service/agent/tools2/tools/index.d.ts +7 -0
- package/dist/ai-service/agent/tools2/tools/index.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/tools/index.js +8 -0
- package/dist/ai-service/agent/tools2/tools/index.js.map +1 -1
- package/dist/ai-service/agent/tools2/types.d.ts +2 -3
- package/dist/ai-service/agent/tools2/types.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/types.js +1 -0
- package/dist/ai-service/agent/tools2/types.js.map +1 -1
- package/dist/ai-service/chat/chat-session-store.d.ts.map +1 -1
- package/dist/ai-service/chat/chat-session-store.js +2 -0
- package/dist/ai-service/chat/chat-session-store.js.map +1 -1
- package/dist/ai-service/features.d.ts +5 -0
- package/dist/ai-service/features.d.ts.map +1 -1
- package/dist/ai-service/features.js +5 -0
- package/dist/ai-service/features.js.map +1 -1
- package/dist/ai-service/index.d.ts +2 -1
- package/dist/ai-service/index.d.ts.map +1 -1
- package/dist/ai-service/index.js +135 -2
- package/dist/ai-service/index.js.map +1 -1
- package/dist/ai-service/prompts/summarize-api-usage.d.ts +18 -0
- package/dist/ai-service/prompts/summarize-api-usage.d.ts.map +1 -0
- package/dist/ai-service/prompts/summarize-api-usage.js +30 -0
- package/dist/ai-service/prompts/summarize-api-usage.js.map +1 -0
- package/dist/ai-service/skills/system/_registry.generated.d.ts.map +1 -1
- package/dist/ai-service/skills/system/_registry.generated.js +0 -1
- package/dist/ai-service/skills/system/_registry.generated.js.map +1 -1
- package/dist/ai-service/skills/system/superblocks-api/references/code-blocks.generated.d.ts +1 -1
- package/dist/ai-service/skills/system/superblocks-api/references/code-blocks.generated.d.ts.map +1 -1
- package/dist/ai-service/skills/system/superblocks-api/references/code-blocks.generated.js +51 -37
- package/dist/ai-service/skills/system/superblocks-api/references/code-blocks.generated.js.map +1 -1
- package/dist/ai-service/skills/system/superblocks-api/references/rest-apis.generated.d.ts +1 -1
- package/dist/ai-service/skills/system/superblocks-api/references/rest-apis.generated.d.ts.map +1 -1
- package/dist/ai-service/skills/system/superblocks-api/references/rest-apis.generated.js +100 -93
- package/dist/ai-service/skills/system/superblocks-api/references/rest-apis.generated.js.map +1 -1
- package/dist/ai-service/skills/system/superblocks-api/references/sql-databases.generated.d.ts +1 -1
- package/dist/ai-service/skills/system/superblocks-api/references/sql-databases.generated.d.ts.map +1 -1
- package/dist/ai-service/skills/system/superblocks-api/references/sql-databases.generated.js +72 -49
- package/dist/ai-service/skills/system/superblocks-api/references/sql-databases.generated.js.map +1 -1
- package/dist/ai-service/skills/system/superblocks-frontend/references/embedding.generated.d.ts +1 -1
- package/dist/ai-service/skills/system/superblocks-frontend/references/embedding.generated.d.ts.map +1 -1
- package/dist/ai-service/skills/system/superblocks-frontend/references/embedding.generated.js +7 -7
- package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.d.ts +1 -1
- package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.d.ts.map +1 -1
- package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.js +88 -28
- package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.js.map +1 -1
- package/dist/ai-service/state-machine/clark-fsm.d.ts.map +1 -1
- package/dist/ai-service/state-machine/clark-fsm.js +0 -2
- package/dist/ai-service/state-machine/clark-fsm.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/llm-generating.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/llm-generating.js +9 -0
- package/dist/ai-service/state-machine/handlers/llm-generating.js.map +1 -1
- package/dist/file-sync-vite-plugin.d.ts.map +1 -1
- package/dist/file-sync-vite-plugin.js +4 -0
- package/dist/file-sync-vite-plugin.js.map +1 -1
- package/dist/git-service/errors.d.ts +18 -0
- package/dist/git-service/errors.d.ts.map +1 -0
- package/dist/git-service/errors.js +27 -0
- package/dist/git-service/errors.js.map +1 -0
- package/dist/git-service/index.d.ts +30 -0
- package/dist/git-service/index.d.ts.map +1 -0
- package/dist/git-service/index.js +135 -0
- package/dist/git-service/index.js.map +1 -0
- package/dist/git-service/live-branch.d.ts +10 -0
- package/dist/git-service/live-branch.d.ts.map +1 -0
- package/dist/git-service/live-branch.js +63 -0
- package/dist/git-service/live-branch.js.map +1 -0
- package/dist/lock-service/index.d.ts +5 -1
- package/dist/lock-service/index.d.ts.map +1 -1
- package/dist/lock-service/index.js +83 -16
- package/dist/lock-service/index.js.map +1 -1
- package/dist/plugin-options.d.ts +3 -0
- package/dist/plugin-options.d.ts.map +1 -1
- package/dist/plugin-options.js.map +1 -1
- package/dist/socket-manager.d.ts +4 -1
- package/dist/socket-manager.d.ts.map +1 -1
- package/dist/socket-manager.js +378 -3
- package/dist/socket-manager.js.map +1 -1
- package/dist/sync-service/index.d.ts +12 -1
- package/dist/sync-service/index.d.ts.map +1 -1
- package/dist/sync-service/index.js +37 -6
- package/dist/sync-service/index.js.map +1 -1
- package/package.json +21 -16
|
@@ -34,8 +34,8 @@ import {
|
|
|
34
34
|
|
|
35
35
|
\`\`\`typescript
|
|
36
36
|
new PostgreSQL("get_users", "postgres-integration-id", {
|
|
37
|
-
statement: "SELECT * FROM users WHERE active = true LIMIT 100"
|
|
38
|
-
})
|
|
37
|
+
statement: "SELECT * FROM users WHERE active = true LIMIT 100",
|
|
38
|
+
});
|
|
39
39
|
\`\`\`
|
|
40
40
|
|
|
41
41
|
## Parameterized Queries
|
|
@@ -46,13 +46,13 @@ new PostgreSQL("get_users", "postgres-integration-id", {
|
|
|
46
46
|
|
|
47
47
|
Use \`parameters: "[var1, var2]"\` with the database-specific SQL placeholders:
|
|
48
48
|
|
|
49
|
-
| Database
|
|
50
|
-
|
|
51
|
-
| PostgreSQL, Redshift, CockroachDB
|
|
52
|
-
| MySQL, MariaDB, Snowflake, BigQuery, Athena | \`?\`, \`?\`, \`?\`
|
|
53
|
-
| MSSQL
|
|
54
|
-
| Databricks
|
|
55
|
-
| OracleDB
|
|
49
|
+
| Database | Placeholder Syntax | Example |
|
|
50
|
+
| ------------------------------------------- | ---------------------- | ------------------------------------------- |
|
|
51
|
+
| PostgreSQL, Redshift, CockroachDB | \`$1\`, \`$2\`, \`$3\` | \`WHERE id = $1 AND status = $2\` |
|
|
52
|
+
| MySQL, MariaDB, Snowflake, BigQuery, Athena | \`?\`, \`?\`, \`?\` | \`WHERE id = ? AND status = ?\` |
|
|
53
|
+
| MSSQL | \`@PARAM_1\`, \`@PARAM_2\` | \`WHERE id = @PARAM_1 AND status = @PARAM_2\` |
|
|
54
|
+
| Databricks | \`:PARAM_1\`, \`:PARAM_2\` | \`WHERE id = :PARAM_1 AND status = :PARAM_2\` |
|
|
55
|
+
| OracleDB | \`:1\`, \`:2\`, \`:3\` | \`WHERE id = :1 AND status = :2\` |
|
|
56
56
|
|
|
57
57
|
Only use binding functions for truly dynamic SQL elements like table/column names.
|
|
58
58
|
|
|
@@ -62,16 +62,16 @@ Only use binding functions for truly dynamic SQL elements like table/column name
|
|
|
62
62
|
|
|
63
63
|
\`\`\`typescript
|
|
64
64
|
// ❌ WRONG - Don't use \${} interpolation in parameters
|
|
65
|
-
parameters: "[\${build_query.output.userId}, \${searchTerm}]"
|
|
65
|
+
parameters: "[\${build_query.output.userId}, \${searchTerm}]";
|
|
66
66
|
|
|
67
|
-
// ❌ WRONG - Don't use binding functions for parameters
|
|
68
|
-
parameters: ({ build_query }) => JSON.stringify(build_query.output.params)
|
|
67
|
+
// ❌ WRONG - Don't use binding functions for parameters
|
|
68
|
+
parameters: ({ build_query }) => JSON.stringify(build_query.output.params);
|
|
69
69
|
|
|
70
70
|
// ✅ CORRECT - Write expressions directly (they get evaluated as JS)
|
|
71
|
-
parameters: "[build_query.output.userId, searchTerm, limit]"
|
|
71
|
+
parameters: "[build_query.output.userId, searchTerm, limit]";
|
|
72
72
|
|
|
73
73
|
// ✅ CORRECT - Reference block output array directly
|
|
74
|
-
parameters: "build_query.output.params"
|
|
74
|
+
parameters: "build_query.output.params";
|
|
75
75
|
\`\`\`
|
|
76
76
|
|
|
77
77
|
This is different from \`statement\`, where binding functions with \`\${}\` ARE used to interpolate values into SQL.
|
|
@@ -84,19 +84,20 @@ For simpler cases, string interpolation also works:
|
|
|
84
84
|
// ✅ CORRECT - String interpolation for WHERE clauses
|
|
85
85
|
new PostgreSQL("search", "postgres-id", {
|
|
86
86
|
statement: ({ searchTerm, limit }: { searchTerm: string; limit: number }) =>
|
|
87
|
-
\`SELECT * FROM users WHERE name ILIKE '%\${searchTerm}%' LIMIT \${limit}
|
|
88
|
-
})
|
|
87
|
+
\`SELECT * FROM users WHERE name ILIKE '%\${searchTerm}%' LIMIT \${limit}\`,
|
|
88
|
+
});
|
|
89
89
|
|
|
90
90
|
// ✅ CORRECT - For INSERT statements
|
|
91
91
|
new PostgreSQL("insert_user", "postgres-id", {
|
|
92
92
|
statement: ({ name, email }: { name: string; email: string }) =>
|
|
93
|
-
\`INSERT INTO users (name, email) VALUES ('\${name}', '\${email}') RETURNING
|
|
94
|
-
})
|
|
93
|
+
\`INSERT INTO users (name, email) VALUES ('\${name}', '\${email}') RETURNING *\`,
|
|
94
|
+
});
|
|
95
95
|
\`\`\`
|
|
96
96
|
|
|
97
97
|
### Dynamic SQL with Optional Filters
|
|
98
98
|
|
|
99
99
|
Use the \`parameters\` array with the "OR NULL" pattern for optional filters:
|
|
100
|
+
|
|
100
101
|
- \`WHERE (column = $1 OR $1::type IS NULL)\` makes the condition a no-op when parameter is null
|
|
101
102
|
- Cast parameters to their type when checking for NULL (PostgreSQL can't infer types from NULL values)
|
|
102
103
|
- Always pass all parameters, using null for unused filters
|
|
@@ -109,6 +110,7 @@ Use the \`parameters\` array with the "OR NULL" pattern for optional filters:
|
|
|
109
110
|
Each SQL block (PostgreSQL, Snowflake, MySQL, MicrosoftSql, Databricks) can execute **ONLY ONE SQL query**.
|
|
110
111
|
|
|
111
112
|
❌ **WRONG - Multiple queries in one block:**
|
|
113
|
+
|
|
112
114
|
\`\`\`sql
|
|
113
115
|
UPDATE users SET status = 'active';
|
|
114
116
|
DELETE FROM logs WHERE created < '2023-01-01';
|
|
@@ -116,13 +118,14 @@ INSERT INTO audit VALUES ('done');
|
|
|
116
118
|
\`\`\`
|
|
117
119
|
|
|
118
120
|
✅ **CORRECT - One query per block:**
|
|
121
|
+
|
|
119
122
|
\`\`\`typescript
|
|
120
|
-
new PostgreSQL("update_status", "postgres-id", {
|
|
121
|
-
statement: "UPDATE users SET status = 'active'"
|
|
123
|
+
(new PostgreSQL("update_status", "postgres-id", {
|
|
124
|
+
statement: "UPDATE users SET status = 'active'",
|
|
122
125
|
}),
|
|
123
|
-
new PostgreSQL("clean_logs", "postgres-id", {
|
|
124
|
-
|
|
125
|
-
})
|
|
126
|
+
new PostgreSQL("clean_logs", "postgres-id", {
|
|
127
|
+
statement: "DELETE FROM logs WHERE created < '2023-01-01'",
|
|
128
|
+
}));
|
|
126
129
|
\`\`\`
|
|
127
130
|
|
|
128
131
|
### 2. Use Meaningful Block Names
|
|
@@ -152,6 +155,7 @@ SELECT * FROM users
|
|
|
152
155
|
Always include a row limit clause to prevent runaway queries. Use 100 as the default unless user specifies otherwise.
|
|
153
156
|
|
|
154
157
|
Different SQL dialects have different syntax:
|
|
158
|
+
|
|
155
159
|
- **MySQL/PostgreSQL/SQLite**: \`LIMIT N\`
|
|
156
160
|
- **SQL Server**: \`SELECT TOP N\`
|
|
157
161
|
- **ANSI SQL (SQL Server, Oracle, DB2)**: \`OFFSET 0 ROWS FETCH NEXT N ROWS ONLY\`
|
|
@@ -202,13 +206,13 @@ SELECT * FROM users WHERE deleted_at IS NULL
|
|
|
202
206
|
// Array operations
|
|
203
207
|
new PostgreSQL("array_query", "pg-id", {
|
|
204
208
|
statement: ({ tags }: { tags: string[] }) =>
|
|
205
|
-
\`SELECT * FROM posts WHERE tags && ARRAY[\${tags.map(t => \`'\${t}'\`).join(
|
|
206
|
-
})
|
|
209
|
+
\`SELECT * FROM posts WHERE tags && ARRAY[\${tags.map((t) => \`'\${t}'\`).join(",")}]\`,
|
|
210
|
+
});
|
|
207
211
|
|
|
208
212
|
// JSON operations
|
|
209
213
|
new PostgreSQL("json_query", "pg-id", {
|
|
210
|
-
statement: "SELECT data->>'name' as name FROM json_data"
|
|
211
|
-
})
|
|
214
|
+
statement: "SELECT data->>'name' as name FROM json_data",
|
|
215
|
+
});
|
|
212
216
|
\`\`\`
|
|
213
217
|
|
|
214
218
|
- Use schema-qualified names: \`schema.table\` or \`"schema"."table"\`
|
|
@@ -224,8 +228,8 @@ new Snowflake("ranked_query", "snowflake-id", {
|
|
|
224
228
|
SELECT *, ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales DESC) as rank
|
|
225
229
|
FROM products
|
|
226
230
|
QUALIFY rank <= 3
|
|
227
|
-
|
|
228
|
-
})
|
|
231
|
+
\`,
|
|
232
|
+
});
|
|
229
233
|
\`\`\`
|
|
230
234
|
|
|
231
235
|
**Snowflake Two-Part Naming:** Snowflake uses \`schema.table\`
|
|
@@ -245,6 +249,7 @@ SELECT * FROM PRODUCTLINE -- ❌ Will fail if no default schema
|
|
|
245
249
|
**Databricks Three-Part Naming:** Databricks uses \`catalog.schema.table\`
|
|
246
250
|
|
|
247
251
|
When you see Databricks metadata like:
|
|
252
|
+
|
|
248
253
|
- \`uber.default.orders\` - Use the FULL path: \`uber.default.orders\`
|
|
249
254
|
- \`production.analytics.users\` - Use: \`production.analytics.users\`
|
|
250
255
|
|
|
@@ -262,8 +267,8 @@ SELECT * FROM orders -- ❌ Missing catalog and schema
|
|
|
262
267
|
\`\`\`typescript
|
|
263
268
|
// Use backticks for table names
|
|
264
269
|
new BigQuery("query", "bq-id", {
|
|
265
|
-
statement: "SELECT * FROM \\\`project.dataset.table\\\` LIMIT 100"
|
|
266
|
-
})
|
|
270
|
+
statement: "SELECT * FROM \\\`project.dataset.table\\\` LIMIT 100",
|
|
271
|
+
});
|
|
267
272
|
\`\`\`
|
|
268
273
|
|
|
269
274
|
## Common Patterns
|
|
@@ -273,22 +278,30 @@ new BigQuery("query", "bq-id", {
|
|
|
273
278
|
\`\`\`typescript
|
|
274
279
|
new PostgreSQL("paginated_fetch", "pg-id", {
|
|
275
280
|
statement: ({ page, pageSize }: { page: number; pageSize: number }) =>
|
|
276
|
-
\`SELECT * FROM orders ORDER BY created_at DESC LIMIT \${pageSize} OFFSET \${(page - 1) * pageSize}
|
|
277
|
-
})
|
|
281
|
+
\`SELECT * FROM orders ORDER BY created_at DESC LIMIT \${pageSize} OFFSET \${(page - 1) * pageSize}\`,
|
|
282
|
+
});
|
|
278
283
|
\`\`\`
|
|
279
284
|
|
|
280
285
|
### Search with Multiple Conditions
|
|
281
286
|
|
|
282
287
|
\`\`\`typescript
|
|
283
288
|
new PostgreSQL("advanced_search", "pg-id", {
|
|
284
|
-
statement: ({
|
|
289
|
+
statement: ({
|
|
290
|
+
name,
|
|
291
|
+
status,
|
|
292
|
+
minAmount,
|
|
293
|
+
}: {
|
|
294
|
+
name?: string;
|
|
295
|
+
status?: string;
|
|
296
|
+
minAmount?: number;
|
|
297
|
+
}) => {
|
|
285
298
|
const conditions = ["1=1"];
|
|
286
299
|
if (name) conditions.push(\`name ILIKE '%\${name}%'\`);
|
|
287
300
|
if (status) conditions.push(\`status = '\${status}'\`);
|
|
288
301
|
if (minAmount !== undefined) conditions.push(\`amount >= \${minAmount}\`);
|
|
289
302
|
return \`SELECT * FROM orders WHERE \${conditions.join(" AND ")} LIMIT 100\`;
|
|
290
|
-
}
|
|
291
|
-
})
|
|
303
|
+
},
|
|
304
|
+
});
|
|
292
305
|
\`\`\`
|
|
293
306
|
|
|
294
307
|
### Batch Insert
|
|
@@ -297,11 +310,11 @@ new PostgreSQL("advanced_search", "pg-id", {
|
|
|
297
310
|
new PostgreSQL("batch_insert", "pg-id", {
|
|
298
311
|
statement: ({ items }: { items: Array<{ name: string; value: number }> }) => {
|
|
299
312
|
const values = items
|
|
300
|
-
.map(item => \`('\${item.name}', \${item.value})\`)
|
|
313
|
+
.map((item) => \`('\${item.name}', \${item.value})\`)
|
|
301
314
|
.join(", ");
|
|
302
315
|
return \`INSERT INTO items (name, value) VALUES \${values} RETURNING *\`;
|
|
303
|
-
}
|
|
304
|
-
})
|
|
316
|
+
},
|
|
317
|
+
});
|
|
305
318
|
\`\`\`
|
|
306
319
|
|
|
307
320
|
### Upsert (Insert or Update)
|
|
@@ -309,17 +322,26 @@ new PostgreSQL("batch_insert", "pg-id", {
|
|
|
309
322
|
\`\`\`typescript
|
|
310
323
|
// PostgreSQL
|
|
311
324
|
new PostgreSQL("upsert_user", "pg-id", {
|
|
312
|
-
statement: ({
|
|
325
|
+
statement: ({
|
|
326
|
+
id,
|
|
327
|
+
name,
|
|
328
|
+
email,
|
|
329
|
+
}: {
|
|
330
|
+
id: string;
|
|
331
|
+
name: string;
|
|
332
|
+
email: string;
|
|
333
|
+
}) =>
|
|
313
334
|
\`INSERT INTO users (id, name, email)
|
|
314
335
|
VALUES ('\${id}', '\${name}', '\${email}')
|
|
315
336
|
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, email = EXCLUDED.email
|
|
316
|
-
RETURNING
|
|
317
|
-
})
|
|
337
|
+
RETURNING *\`,
|
|
338
|
+
});
|
|
318
339
|
\`\`\`
|
|
319
340
|
|
|
320
341
|
## Error Handling
|
|
321
342
|
|
|
322
343
|
SQL errors are returned in the block's output. Common errors:
|
|
344
|
+
|
|
323
345
|
- Syntax errors in SQL
|
|
324
346
|
- Table/column doesn't exist
|
|
325
347
|
- Constraint violations
|
|
@@ -331,19 +353,20 @@ For critical operations, wrap in TryCatch:
|
|
|
331
353
|
new TryCatch("safe_delete", {
|
|
332
354
|
try: [
|
|
333
355
|
new PostgreSQL("delete_record", "pg-id", {
|
|
334
|
-
statement: ({ id }: { id: string }) =>
|
|
335
|
-
|
|
356
|
+
statement: ({ id }: { id: string }) =>
|
|
357
|
+
\`DELETE FROM sensitive_data WHERE id = '\${id}'\`,
|
|
358
|
+
}),
|
|
336
359
|
],
|
|
337
360
|
catch: [
|
|
338
361
|
new JavaScript("log_error", {
|
|
339
362
|
fn: ({ deleteError }) => ({
|
|
340
363
|
success: false,
|
|
341
|
-
error: deleteError.value.message
|
|
342
|
-
})
|
|
343
|
-
})
|
|
364
|
+
error: deleteError.value.message,
|
|
365
|
+
}),
|
|
366
|
+
}),
|
|
344
367
|
],
|
|
345
|
-
variables: { error: "deleteError" }
|
|
346
|
-
})
|
|
368
|
+
variables: { error: "deleteError" },
|
|
369
|
+
});
|
|
347
370
|
\`\`\`
|
|
348
371
|
`;
|
|
349
372
|
//# sourceMappingURL=sql-databases.generated.js.map
|
package/dist/ai-service/skills/system/superblocks-api/references/sql-databases.generated.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql-databases.generated.js","sourceRoot":"","sources":["../../../../../../src/ai-service/skills/system/superblocks-api/references/sql-databases.generated.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,mDAAmD;AAEnD,MAAM,CAAC,MAAM,OAAO,GAAG
|
|
1
|
+
{"version":3,"file":"sql-databases.generated.js","sourceRoot":"","sources":["../../../../../../src/ai-service/skills/system/superblocks-api/references/sql-databases.generated.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,mDAAmD;AAEnD,MAAM,CAAC,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgXtB,CAAC"}
|
package/dist/ai-service/skills/system/superblocks-frontend/references/embedding.generated.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const content = "# Embedding Superblocks Applications\n\nThis reference covers embedding Superblocks applications in external websites or portals.\n\n## Embed Hooks\n\nAvailable hooks from `@superblocksteam/library` for embedded applications:\n\n```typescript\nimport {
|
|
1
|
+
export declare const content = "# Embedding Superblocks Applications\n\nThis reference covers embedding Superblocks applications in external websites or portals.\n\n## Embed Hooks\n\nAvailable hooks from `@superblocksteam/library` for embedded applications:\n\n```typescript\nimport {\n useEmbedProperties,\n useEmbedEvent,\n useEmitEmbedEvent,\n} from \"@superblocksteam/library\";\n```\n\n### useEmbedProperties\n\nRead properties passed from the embedder (parent application):\n\n```typescript\nconst properties = useEmbedProperties();\n\n// Access custom properties defined in the embed configuration\nconst { userId, theme, locale } = properties;\n```\n\n### useEmbedEvent\n\nListen to events emitted from the embedder:\n\n```typescript\nuseEmbedEvent(\"refreshData\", (payload) => {\n // Handle refresh request from parent\n refetchData();\n});\n\nuseEmbedEvent(\"userChanged\", (payload) => {\n // Handle user context change\n setCurrentUser(payload.user);\n});\n\nuseEmbedEvent(\"themeChanged\", (payload) => {\n // Handle theme updates\n setTheme(payload.theme);\n});\n```\n\n### useEmitEmbedEvent\n\nEmit events to the embedder (parent application):\n\n```typescript\nconst emitEvent = useEmitEmbedEvent();\n\n// Notify parent when form is submitted\nconst handleSubmit = async (data) => {\n await saveData(data);\n emitEvent(\"formSubmitted\", {\n timestamp: Date.now(),\n data,\n success: true,\n });\n};\n\n// Notify parent of navigation\nconst handleNavigate = (route) => {\n emitEvent(\"navigationChanged\", { route });\n navigate(route);\n};\n\n// Notify parent of errors\nconst handleError = (error) => {\n emitEvent(\"errorOccurred\", {\n message: error.message,\n code: error.code,\n });\n};\n```\n\n## Common Embedding Patterns\n\n### Bidirectional Communication\n\n```typescript\nfunction EmbeddedDashboard() {\n const properties = useEmbedProperties();\n const emitEvent = useEmitEmbedEvent();\n const [data, setData] = useState(null);\n\n // Listen for refresh commands from parent\n useEmbedEvent(\"refresh\", () => {\n fetchData();\n });\n\n // Notify parent when data changes\n const handleDataUpdate = async (newData) => {\n await saveData(newData);\n setData(newData);\n emitEvent(\"dataUpdated\", { data: newData });\n };\n\n return (\n <Dashboard\n userId={properties.userId}\n data={data}\n onUpdate={handleDataUpdate}\n />\n );\n}\n```\n\n### Synchronized State\n\n```typescript\nfunction EmbeddedForm() {\n const properties = useEmbedProperties();\n const emitEvent = useEmitEmbedEvent();\n const [formState, setFormState] = useState(properties.initialData || {});\n\n // Sync form state changes to parent\n useEffect(() => {\n emitEvent(\"formStateChanged\", { state: formState });\n }, [formState, emitEvent]);\n\n // Listen for external state updates\n useEmbedEvent(\"updateFormState\", (payload) => {\n setFormState(prev => ({ ...prev, ...payload }));\n });\n\n return <Form value={formState} onChange={setFormState} />;\n}\n```\n\n### Loading External Context\n\n```typescript\nfunction EmbeddedApp() {\n const properties = useEmbedProperties();\n const { customerId, permissions, branding } = properties;\n\n // Use embed properties to customize the app\n return (\n <ThemeProvider theme={branding}>\n <CustomerContext.Provider value={{ customerId, permissions }}>\n <AppContent />\n </CustomerContext.Provider>\n </ThemeProvider>\n );\n}\n```\n";
|
|
2
2
|
//# sourceMappingURL=embedding.generated.d.ts.map
|
package/dist/ai-service/skills/system/superblocks-frontend/references/embedding.generated.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedding.generated.d.ts","sourceRoot":"","sources":["../../../../../../src/ai-service/skills/system/superblocks-frontend/references/embedding.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,
|
|
1
|
+
{"version":3,"file":"embedding.generated.d.ts","sourceRoot":"","sources":["../../../../../../src/ai-service/skills/system/superblocks-frontend/references/embedding.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,q5GAuJnB,CAAC"}
|
package/dist/ai-service/skills/system/superblocks-frontend/references/embedding.generated.js
CHANGED
|
@@ -9,7 +9,7 @@ This reference covers embedding Superblocks applications in external websites or
|
|
|
9
9
|
Available hooks from \`@superblocksteam/library\` for embedded applications:
|
|
10
10
|
|
|
11
11
|
\`\`\`typescript
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
13
|
useEmbedProperties,
|
|
14
14
|
useEmbedEvent,
|
|
15
15
|
useEmitEmbedEvent,
|
|
@@ -58,10 +58,10 @@ const emitEvent = useEmitEmbedEvent();
|
|
|
58
58
|
// Notify parent when form is submitted
|
|
59
59
|
const handleSubmit = async (data) => {
|
|
60
60
|
await saveData(data);
|
|
61
|
-
emitEvent("formSubmitted", {
|
|
62
|
-
timestamp: Date.now(),
|
|
61
|
+
emitEvent("formSubmitted", {
|
|
62
|
+
timestamp: Date.now(),
|
|
63
63
|
data,
|
|
64
|
-
success: true
|
|
64
|
+
success: true,
|
|
65
65
|
});
|
|
66
66
|
};
|
|
67
67
|
|
|
@@ -73,9 +73,9 @@ const handleNavigate = (route) => {
|
|
|
73
73
|
|
|
74
74
|
// Notify parent of errors
|
|
75
75
|
const handleError = (error) => {
|
|
76
|
-
emitEvent("errorOccurred", {
|
|
76
|
+
emitEvent("errorOccurred", {
|
|
77
77
|
message: error.message,
|
|
78
|
-
code: error.code
|
|
78
|
+
code: error.code,
|
|
79
79
|
});
|
|
80
80
|
};
|
|
81
81
|
\`\`\`
|
|
@@ -103,7 +103,7 @@ function EmbeddedDashboard() {
|
|
|
103
103
|
};
|
|
104
104
|
|
|
105
105
|
return (
|
|
106
|
-
<Dashboard
|
|
106
|
+
<Dashboard
|
|
107
107
|
userId={properties.userId}
|
|
108
108
|
data={data}
|
|
109
109
|
onUpdate={handleDataUpdate}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const content = "---\nname: superblocks-frontend\ndescription: |\n Build frontend UI using React, Tailwind CSS, and Superblocks components. Essential for connecting Superblocks UIs with APIs.\n Use when creating pages, components, handling user interactions, or working with the design system.\nreadOnly: true\nmetadata:\n author: superblocks\n version: \"1.0\"\n---\n\n# Superblocks Frontend Development\n\nThis skill covers building frontend UI for Superblocks applications using React, Tailwind CSS v4, and the Superblocks component library.\n\n## Platform Overview\n\nThis is a React-based web application platform. Use standard React patterns:\n- `useState`, `useEffect`, `useCallback`, `useMemo`\n- Event handlers and controlled components\n- JSX with Tailwind CSS classes\n\n## Using APIs from Frontend\n\nYou **must** use the `useApi` hook for calling backend APIs:\n\n```typescript\nimport { useApi } from \"@superblocksteam/library\";\nimport { toast } from \"sonner\";\n\nexport default function useGetUsers({ email, name }: { email: string | undefined, name: string | undefined }) {\n const { run: runGetUsers } = useApi(\"GetUsers\"); // matches folder apis/GetUsers/api.yaml\n const [loading, setLoading] = useState(false);\n const [users, setUsers] = useState<User[]>([]);\n\n const getUsers = useCallback(async () => {\n setLoading(true);\n try {\n // ALWAYS include ALL inputs, even if they are empty\n const response = await runGetUsers({ email: email ?? null, name: name ?? null });\n setUsers(response?.users ?? []);\n } catch (error) {\n // API errors are thrown as strings, not Error objects\n console.error(error);\n toast.error(\"Error fetching users: \" + error);\n } finally {\n setLoading(false);\n }\n }, [email, name, runGetUsers]);\n\n return { getUsers, loading, users };\n}\n```\n\n### Critical API Rules\n\n1. **MUST call API by exact name**: Format is `<ApiName>` matching folder `apis/<ApiName>/api.yaml`\n2. **Always pass parameters**: `await api({ param1: value1, param2: value2 })`\n3. **Store response in React state**: Use `useState`, `useReducer`, etc.\n4. **Use async/await pattern**: APIs are async functions\n5. **Track loading and error states**: Always show visual feedback\n6. **ALWAYS check API interfaces**: NEVER guess the response structure\n7. **Include all parameters**: Even optional ones should be passed as `null`\n8. **ALWAYS use try/catch**: APIs throw string errors when they fail - wrap ALL API calls in try/catch blocks\n9. **Prevent HMR loops**: Guard `useEffect` with a ref:\n\n```tsx\nconst hasLoadedRef = useRef(false);\nuseEffect(() => {\n if (hasLoadedRef.current) return;\n hasLoadedRef.current = true;\n fetchData();\n}, []);\n```\n\n### File Handling\n\n**CRITICAL: Files must be wrapped in `{ files: [...] }` format:**\n\n```tsx\n// \u2705 CORRECT - Frontend: wrap files in { files: [...] }\nconst response = await runUploadApi({ userFile: { files: selectedFiles } });\n\n// \u274C WRONG - backend cannot process unwrapped files\nconst response = await runUploadApi({ userFile: selectedFiles });\n```\n\n## Platform Hooks\n\nAvailable hooks from `@superblocksteam/library`:\n\n```typescript\nimport {\n useSuperblocksUser,\n useSuperblocksGroups,\n useSuperblocksDataTags,\n} from \"@superblocksteam/library\";\n\n// Get current user info\nconst user = useSuperblocksUser();\n// user.name, user.email, user.id, user.groups, user.username, user.metadata\n\n// Get organization groups\nconst groups = useSuperblocksGroups();\n\n// Manage data tags\nconst { dataTags, setDataTag } = useSuperblocksDataTags();\n```\n\n**For embedded applications**, see the `references/embedding.md` file for `useEmbedProperties`, `useEmbedEvent`, and `useEmitEmbedEvent` hooks.\n\n## Application Architecture\n\n### App.tsx Layout Structure\n\n**For single-page applications:**\n- Put all content in `pages/<pageName>/index.tsx`\n- Use components to compose and build the page\n- Keep `App.tsx` minimal with just `<AppProvider>` and `<Outlet />`\n\n**For multi-page applications:**\n- Put shared navigation/layout in `App.tsx` (sidebars, headers, footers)\n- Always include `<Outlet />` for page content\n- Always include `<AppProvider />` wrapper\n- Individual pages focus on content, not layout\n\n```tsx\n// \u2705 CORRECT - Multi-page App.tsx layout\n<AppProvider>\n <div className=\"flex flex-row size-screen\">\n {/* Sidebar - persistent across all pages */}\n <div className=\"flex bg-sidebar border-r w-[250px]\">\n <Navigation />\n </div>\n\n {/* Main content area */}\n <div className=\"flex flex-1 h-full flex-col\">\n {/* Header - persistent across all pages */}\n <div className=\"flex bg-header border-b h-[60px]\">\n <Header />\n </div>\n\n {/* Page content area */}\n <div className=\"flex p-4 flex-1 overflow-auto\">\n <Outlet />\n </div>\n </div>\n </div>\n</AppProvider>\n```\n\n**PROTECTED: NEVER remove `<AppProvider>` or `<Outlet />` from App.tsx!**\n\n### Page Structure\n\n```tsx\n// \u2705 CORRECT - Page focuses on content only\n<div className=\"flex flex-col gap-4 size-screen overflow-auto\">\n <h1 className=\"text-3xl font-bold\">Dashboard Content</h1>\n <Card>\n {/* Page-specific content */}\n </Card>\n</div>\n```\n\n**IMPORTANT**: The first div on the page must have `overflow-auto` so the user's page can scroll.\n\n## Routing\n\nUse `react-router@7` in data mode. Standard patterns apply:\n- `useNavigate()` for programmatic navigation\n- `useParams()` for route parameters\n- `useSearchParams()` for query strings\n\n**If you add new pages or rename files, you MUST update the router.**\n\n## Design System (Tailwind CSS v4)\n\n**All design tokens are defined in `index.css`**. Apps come with a professional black-and-white theme by default.\n\n### Semantic Tokens Rule\n\n**ALWAYS use semantic tokens** \u2014 NEVER raw Tailwind utilities:\n\n```tsx\n// \u2705 CORRECT\n<Card className=\"bg-background text-foreground border border-border\" />\n\n// \u274C WRONG\n<Card className=\"bg-white text-black border-gray-200\" />\n```\n\n### When to Modify index.css\n\nOnly modify when:\n- User explicitly requests branding/theme changes\n- Replicating a specific brand look (e.g., Yelp, Instacart)\n- Feature requests (lists, filters, CRUD) do NOT require changes\n\n**Modification Rules:**\n- All colors must be in OKLCH format\n- Use semantic names (`--color-warning`, `--shadow-elevated`)\n- Do not remove or rename existing tokens\n- Limit color palette to 5 colors max\n- Avoid gradients unless explicitly requested\n- Minimal font sizes (3 max: body, section heading, main heading)\n\n### Component Variants\n\nUse or create variants instead of one-off styles:\n\n```tsx\n// \u274C WRONG - Hacky inline overrides\n<Button className=\"text-white border-white hover:bg-white\" />\n\n// \u2705 CORRECT - Use a variant\n<Button variant=\"secondary\" />\n```\n\n## Icons\n\nUse icons from Lucide React library:\n\n```tsx\nimport { Icon } from \"@/components/ui/icon\";\n<Icon icon=\"heart\" />\n\nimport { Button } from \"@/components/ui/button\";\n<Button><Icon icon=\"plus\" /> Add Item</Button>\n```\n\n**Always use icons rather than emojis unless explicitly requested.**\n\n## Custom Components\n\n**CRITICAL: Component composition is MANDATORY. DO NOT create monolithic pages.**\n\n### The Composition Rule\n\nWhen building ANY feature:\n1. **First**, identify reusable parts (list items, cards, forms, filters, headers)\n2. **Then**, create separate component files\n3. **Finally**, compose them together in the page\n\n### When to Create Components\n\n- **Rendering any list** - Extract to a component\n- **Building cards/complex UI** - Each card type is a component\n- **Creating forms** - Form sections are components\n- **Adding filters/headers** - These are components\n- **Page has >50 lines JSX** - Break it down\n\n### Component Boundary Rules\n\n**Inside a custom component:**\n- \u2705 Use React hooks (useState, useReducer, useEffect, etc.)\n- \u2705 Use local React state\n- \u2705 Use internal helper components\n- \u2705 Use other registered components\n- \u274C CANNOT call APIs - must pass data into the component\n\n### Example: Proper Component Structure\n\n```tsx\n// components/ProductCard/index.tsx - Extract to component\nimport { Card } from \"@/components/ui/card\";\nimport { Button } from \"@/components/ui/button\";\n\ntype ProductCardProps = {\n product: {\n id: string;\n name: string;\n image: string;\n };\n};\n\nexport default function ProductCard(props: ProductCardProps) {\n return (\n <Card>\n <img src={props.product.image} />\n <h3 className=\"text-lg font-semibold\">{props.product.name}</h3>\n <Button>Add</Button>\n </Card>\n );\n}\n\n// pages/Products/index.tsx - Clean composition\nimport ProductCard from \"@/components/ProductCard\";\n\nconst ProductsPage = () => {\n const [products, setProducts] = useState([]);\n\n return (\n <div className=\"grid grid-cols-3 gap-4\">\n {products.map(p => <ProductCard key={p.id} product={p} />)}\n </div>\n );\n};\n```\n\n## Visual Excellence\n\n**Prioritize visual excellence from the start:**\n\n1. **Design System Enhancement**: Start by enhancing `index.css` with app-specific colors that match the target aesthetic\n2. **Professional Layout Architecture**: Use sophisticated layouts with proper spacing, responsive design\n3. **Rich Interactive Components**: Leverage advanced components with proper variants and states\n4. **Visual Polish**: Add shadows, smooth transitions, skeleton loaders, hover effects, typography hierarchy\n5. **Real-World UI Patterns**: Create layouts that feel like professional applications\n\n**Make applications that look and feel like real, polished products - not basic wireframes.**\n\n### Loading States\n\n**Always represent loading with skeletons (shimmers), not spinners or empty screens.**\n\n### Efficient Loading Patterns\n\n1. **Default to Smart Loading**: Check for existing data before showing loading states\n2. **Loading State Hierarchy**:\n - Empty state \u2192 Full loading\n - Has data \u2192 Keep showing data during refetch\n - Error state \u2192 Show error with retry option\n3. **Debounce Rapid Requests**: Prevent multiple API calls in short succession\n4. **Use hooks to encapsulate API logic**: Avoid repeating code\n\n## Performance Rules\n\n### 1. ALWAYS Paginate Tables and Lists\n\n**NEVER render more than 50 rows without pagination.** Always add client-side pagination:\n\n```tsx\nfunction PaginatedTable({ data }: { data: any[] }) {\n const PAGE_SIZE = 20;\n const [page, setPage] = useState(0);\n const totalPages = Math.ceil(data.length / PAGE_SIZE);\n const pageData = useMemo(() => data.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE), [data, page]);\n\n useEffect(() => { setPage(0); }, [data.length]);\n\n return (<>\n <Table>{/* render pageData rows */}</Table>\n <span>Page {page + 1} of {totalPages} ({data.length} total rows)</span>\n <Button onClick={() => setPage((p) => Math.max(0, p - 1))} disabled={page === 0}>Previous</Button>\n <Button onClick={() => setPage((p) => Math.min(totalPages - 1, p + 1))} disabled={page >= totalPages - 1}>Next</Button>\n </>);\n}\n```\n\nFor 200+ rows, prefer **server-side pagination**; use cursor/keyset pagination for high offsets instead of `LIMIT`/`OFFSET`.\n\nFor 500+ item lists where pagination doesn't fit the UX (chat messages, infinite scroll feeds, large dropdowns), use **virtualization** (`react-virtuoso` or `@tanstack/react-virtual`) to render only the items visible in the viewport.\n\n### 2. ALWAYS Debounce Input-Driven API Calls\n\n**NEVER call an API directly from onChange.** Debounce search/filter inputs with a 300ms timer:\n\n```tsx\nfunction DebouncedSearch({ onSearch }: { onSearch: (query: string) => void }) {\n const [localValue, setLocalValue] = useState(\"\");\n const timerRef = useRef<ReturnType<typeof setTimeout>>();\n const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n setLocalValue(e.target.value);\n clearTimeout(timerRef.current);\n timerRef.current = setTimeout(() => onSearch(e.target.value), 300);\n }, [onSearch]);\n useEffect(() => () => clearTimeout(timerRef.current), []);\n\n return <Input value={localValue} onChange={handleChange} placeholder=\"Search...\" />;\n}\n```\n\n### 3. Memoize Expensive Renders\n\nUse memoization (`memo()`, `useMemo`, `useCallback`) when it prevents measurable re-renders or expensive recomputation:\n\n```tsx\nconst OrderRow = memo(function OrderRow({ order, onSelect }: { order: Order; onSelect: (id: string) => void }) {\n return <TableRow onClick={() => onSelect(order.id)}><TableCell>{order.id}</TableCell>...</TableRow>;\n});\n\nconst handleSelect = useCallback((id: string) => {\n setSelectedId(id);\n}, []);\n\nconst filtered = useMemo(\n () => orders.filter(o => o.status === status).sort((a, b) => b.total - a.total),\n [orders, status],\n);\n```\n\n### 4. ALWAYS Clean Up Side Effects\n\nClean up timers, event listeners, and subscriptions in `useEffect` return functions:\n\n```tsx\nuseEffect(() => {\n const handler = (e: KeyboardEvent) => { /* ... */ };\n window.addEventListener(\"keydown\", handler);\n return () => window.removeEventListener(\"keydown\", handler);\n}, []);\n```\n\n### 5. Cancel In-Flight API Requests on Unmount\n\nWhen debouncing or fetching data, use the `cancel()` function from `useApi` to cancel in-flight requests when the component unmounts or a newer request supersedes the previous one:\n\n```tsx\nconst { run, cancel } = useApi(\"SearchProducts\");\n\nuseEffect(() => {\n if (!query) return;\n run({ query }).then(res => setResults(res?.products ?? [])).catch(console.error);\n return () => { void cancel(); };\n}, [query, run, cancel]);\n```\n";
|
|
1
|
+
export declare const content = "---\nname: superblocks-frontend\ndescription: |\n Build frontend UI using React, Tailwind CSS, and Superblocks components. Essential for connecting Superblocks UIs with APIs.\n Use when creating pages, components, handling user interactions, or working with the design system.\nreadOnly: true\nmetadata:\n author: superblocks\n version: \"1.0\"\n---\n\n# Superblocks Frontend Development\n\nThis skill covers building frontend UI for Superblocks applications using React, Tailwind CSS v4, and the Superblocks component library.\n\n## Platform Overview\n\nThis is a React-based web application platform. Use standard React patterns:\n\n- `useState`, `useEffect`, `useCallback`, `useMemo`\n- Event handlers and controlled components\n- JSX with Tailwind CSS classes\n\n## Using APIs from Frontend\n\nYou **must** use the `useApi` hook for calling backend APIs:\n\n```typescript\nimport { useApi } from \"@superblocksteam/library\";\nimport { toast } from \"sonner\";\n\nexport default function useGetUsers({\n email,\n name,\n}: {\n email: string | undefined;\n name: string | undefined;\n}) {\n const { run: runGetUsers } = useApi(\"GetUsers\"); // matches folder apis/GetUsers/api.yaml\n const [loading, setLoading] = useState(false);\n const [users, setUsers] = useState<User[]>([]);\n\n const getUsers = useCallback(async () => {\n setLoading(true);\n try {\n // ALWAYS include ALL inputs, even if they are empty\n const response = await runGetUsers({\n email: email ?? null,\n name: name ?? null,\n });\n setUsers(response?.users ?? []);\n } catch (error) {\n // API errors are thrown as strings, not Error objects\n console.error(error);\n toast.error(\"Error fetching users: \" + error);\n } finally {\n setLoading(false);\n }\n }, [email, name, runGetUsers]);\n\n return { getUsers, loading, users };\n}\n```\n\n### Critical API Rules\n\n1. **MUST call API by exact name**: Format is `<ApiName>` matching folder `apis/<ApiName>/api.yaml`\n2. **Always pass parameters**: `await api({ param1: value1, param2: value2 })`\n3. **Store response in React state**: Use `useState`, `useReducer`, etc.\n4. **Use async/await pattern**: APIs are async functions\n5. **Track loading and error states**: Always show visual feedback\n6. **ALWAYS check API interfaces**: NEVER guess the response structure\n7. **Include all parameters**: Even optional ones should be passed as `null`\n8. **ALWAYS use try/catch**: APIs throw string errors when they fail - wrap ALL API calls in try/catch blocks\n9. **Prevent HMR loops**: Guard `useEffect` with a ref:\n\n```tsx\nconst hasLoadedRef = useRef(false);\nuseEffect(() => {\n if (hasLoadedRef.current) return;\n hasLoadedRef.current = true;\n fetchData();\n}, []);\n```\n\n### File Handling\n\n**CRITICAL: Files must be wrapped in `{ files: [...] }` format:**\n\n```tsx\n// \u2705 CORRECT - Frontend: wrap files in { files: [...] }\nconst response = await runUploadApi({ userFile: { files: selectedFiles } });\n\n// \u274C WRONG - backend cannot process unwrapped files\nconst response = await runUploadApi({ userFile: selectedFiles });\n```\n\n## Platform Hooks\n\nAvailable hooks from `@superblocksteam/library`:\n\n```typescript\nimport {\n useSuperblocksUser,\n useSuperblocksGroups,\n useSuperblocksDataTags,\n} from \"@superblocksteam/library\";\n\n// Get current user info\nconst user = useSuperblocksUser();\n// user.name, user.email, user.id, user.groups, user.username, user.metadata\n\n// Get organization groups\nconst groups = useSuperblocksGroups();\n\n// Manage data tags\nconst { dataTags, setDataTag } = useSuperblocksDataTags();\n```\n\n**For embedded applications**, see the `references/embedding.md` file for `useEmbedProperties`, `useEmbedEvent`, and `useEmitEmbedEvent` hooks.\n\n## Application Architecture\n\n### App.tsx Layout Structure\n\n**For single-page applications:**\n\n- Put all content in `pages/<pageName>/index.tsx`\n- Use components to compose and build the page\n- Keep `App.tsx` minimal with just `<AppProvider>` and `<Outlet />`\n\n**For multi-page applications:**\n\n- Put shared navigation/layout in `App.tsx` (sidebars, headers, footers)\n- Always include `<Outlet />` for page content\n- Always include `<AppProvider />` wrapper\n- Individual pages focus on content, not layout\n\n```tsx\n// \u2705 CORRECT - Multi-page App.tsx layout\n<AppProvider>\n <div className=\"flex flex-row size-screen\">\n {/* Sidebar - persistent across all pages */}\n <div className=\"flex bg-sidebar border-r w-[250px]\">\n <Navigation />\n </div>\n\n {/* Main content area */}\n <div className=\"flex flex-1 h-full flex-col\">\n {/* Header - persistent across all pages */}\n <div className=\"flex bg-header border-b h-[60px]\">\n <Header />\n </div>\n\n {/* Page content area */}\n <div className=\"flex p-4 flex-1 overflow-auto\">\n <Outlet />\n </div>\n </div>\n </div>\n</AppProvider>\n```\n\n**PROTECTED: NEVER remove `<AppProvider>` or `<Outlet />` from App.tsx!**\n\n### Page Structure\n\n```tsx\n// \u2705 CORRECT - Page focuses on content only\n<div className=\"flex flex-col gap-4 size-screen overflow-auto\">\n <h1 className=\"text-3xl font-bold\">Dashboard Content</h1>\n <Card>{/* Page-specific content */}</Card>\n</div>\n```\n\n**IMPORTANT**: The first div on the page must have `overflow-auto` so the user's page can scroll.\n\n## Routing\n\nUse `react-router@7` in data mode. Standard patterns apply:\n\n- `useNavigate()` for programmatic navigation\n- `useParams()` for route parameters\n- `useSearchParams()` for query strings\n\n**If you add new pages or rename files, you MUST update the router.**\n\n## Design System (Tailwind CSS v4)\n\n**All design tokens are defined in `index.css`**. Apps come with a professional black-and-white theme by default.\n\n### Semantic Tokens Rule\n\n**ALWAYS use semantic tokens** \u2014 NEVER raw Tailwind utilities:\n\n```tsx\n// \u2705 CORRECT\n<Card className=\"bg-background text-foreground border border-border\" />\n\n// \u274C WRONG\n<Card className=\"bg-white text-black border-gray-200\" />\n```\n\n### When to Modify index.css\n\nOnly modify when:\n\n- User explicitly requests branding/theme changes\n- Replicating a specific brand look (e.g., Yelp, Instacart)\n- Feature requests (lists, filters, CRUD) do NOT require changes\n\n**Modification Rules:**\n\n- All colors must be in OKLCH format\n- Use semantic names (`--color-warning`, `--shadow-elevated`)\n- Do not remove or rename existing tokens\n- Limit color palette to 5 colors max\n- Avoid gradients unless explicitly requested\n- Minimal font sizes (3 max: body, section heading, main heading)\n\n### Component Variants\n\nUse or create variants instead of one-off styles:\n\n```tsx\n// \u274C WRONG - Hacky inline overrides\n<Button className=\"text-white border-white hover:bg-white\" />\n\n// \u2705 CORRECT - Use a variant\n<Button variant=\"secondary\" />\n```\n\n## Icons\n\nUse icons from Lucide React library:\n\n```tsx\nimport { Icon } from \"@/components/ui/icon\";\n<Icon icon=\"heart\" />;\n\nimport { Button } from \"@/components/ui/button\";\n<Button>\n <Icon icon=\"plus\" /> Add Item\n</Button>;\n```\n\n**Always use icons rather than emojis unless explicitly requested.**\n\n## Custom Components\n\n**CRITICAL: Component composition is MANDATORY. DO NOT create monolithic pages.**\n\n### The Composition Rule\n\nWhen building ANY feature:\n\n1. **First**, identify reusable parts (list items, cards, forms, filters, headers)\n2. **Then**, create separate component files\n3. **Finally**, compose them together in the page\n\n### When to Create Components\n\n- **Rendering any list** - Extract to a component\n- **Building cards/complex UI** - Each card type is a component\n- **Creating forms** - Form sections are components\n- **Adding filters/headers** - These are components\n- **Page has >50 lines JSX** - Break it down\n\n### Component Boundary Rules\n\n**Inside a custom component:**\n\n- \u2705 Use React hooks (useState, useReducer, useEffect, etc.)\n- \u2705 Use local React state\n- \u2705 Use internal helper components\n- \u2705 Use other registered components\n- \u274C CANNOT call APIs - must pass data into the component\n\n### Example: Proper Component Structure\n\n```tsx\n// components/ProductCard/index.tsx - Extract to component\nimport { Card } from \"@/components/ui/card\";\nimport { Button } from \"@/components/ui/button\";\n\ntype ProductCardProps = {\n product: {\n id: string;\n name: string;\n image: string;\n };\n};\n\nexport default function ProductCard(props: ProductCardProps) {\n return (\n <Card>\n <img src={props.product.image} />\n <h3 className=\"text-lg font-semibold\">{props.product.name}</h3>\n <Button>Add</Button>\n </Card>\n );\n}\n\n// pages/Products/index.tsx - Clean composition\nimport ProductCard from \"@/components/ProductCard\";\n\nconst ProductsPage = () => {\n const [products, setProducts] = useState([]);\n\n return (\n <div className=\"grid grid-cols-3 gap-4\">\n {products.map((p) => (\n <ProductCard key={p.id} product={p} />\n ))}\n </div>\n );\n};\n```\n\n## Visual Excellence\n\n**Prioritize visual excellence from the start:**\n\n1. **Design System Enhancement**: Start by enhancing `index.css` with app-specific colors that match the target aesthetic\n2. **Professional Layout Architecture**: Use sophisticated layouts with proper spacing, responsive design\n3. **Rich Interactive Components**: Leverage advanced components with proper variants and states\n4. **Visual Polish**: Add shadows, smooth transitions, skeleton loaders, hover effects, typography hierarchy\n5. **Real-World UI Patterns**: Create layouts that feel like professional applications\n\n**Make applications that look and feel like real, polished products - not basic wireframes.**\n\n### Loading States\n\n**Always represent loading with skeletons (shimmers), not spinners or empty screens.**\n\n### Efficient Loading Patterns\n\n1. **Default to Smart Loading**: Check for existing data before showing loading states\n2. **Loading State Hierarchy**:\n - Empty state \u2192 Full loading\n - Has data \u2192 Keep showing data during refetch\n - Error state \u2192 Show error with retry option\n3. **Debounce Rapid Requests**: Prevent multiple API calls in short succession\n4. **Use hooks to encapsulate API logic**: Avoid repeating code\n\n## Performance Rules\n\n### 1. ALWAYS Paginate Tables and Lists\n\n**NEVER render more than 50 rows without pagination.** Always add client-side pagination:\n\n```tsx\nfunction PaginatedTable({ data }: { data: any[] }) {\n const PAGE_SIZE = 20;\n const [page, setPage] = useState(0);\n const totalPages = Math.ceil(data.length / PAGE_SIZE);\n const pageData = useMemo(\n () => data.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE),\n [data, page],\n );\n\n useEffect(() => {\n setPage(0);\n }, [data.length]);\n\n return (\n <>\n <Table>{/* render pageData rows */}</Table>\n <span>\n Page {page + 1} of {totalPages} ({data.length} total rows)\n </span>\n <Button\n onClick={() => setPage((p) => Math.max(0, p - 1))}\n disabled={page === 0}\n >\n Previous\n </Button>\n <Button\n onClick={() => setPage((p) => Math.min(totalPages - 1, p + 1))}\n disabled={page >= totalPages - 1}\n >\n Next\n </Button>\n </>\n );\n}\n```\n\nFor 200+ rows, prefer **server-side pagination**; use cursor/keyset pagination for high offsets instead of `LIMIT`/`OFFSET`.\n\nFor 500+ item lists where pagination doesn't fit the UX (chat messages, infinite scroll feeds, large dropdowns), use **virtualization** (`react-virtuoso` or `@tanstack/react-virtual`) to render only the items visible in the viewport.\n\n### 2. ALWAYS Debounce Input-Driven API Calls\n\n**NEVER call an API directly from onChange.** Debounce search/filter inputs with a 300ms timer:\n\n```tsx\nfunction DebouncedSearch({ onSearch }: { onSearch: (query: string) => void }) {\n const [localValue, setLocalValue] = useState(\"\");\n const timerRef = useRef<ReturnType<typeof setTimeout>>();\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n setLocalValue(e.target.value);\n clearTimeout(timerRef.current);\n timerRef.current = setTimeout(() => onSearch(e.target.value), 300);\n },\n [onSearch],\n );\n useEffect(() => () => clearTimeout(timerRef.current), []);\n\n return (\n <Input value={localValue} onChange={handleChange} placeholder=\"Search...\" />\n );\n}\n```\n\n### 3. Memoize Expensive Renders\n\nUse memoization (`memo()`, `useMemo`, `useCallback`) when it prevents measurable re-renders or expensive recomputation:\n\n```tsx\nconst OrderRow = memo(function OrderRow({\n order,\n onSelect,\n}: {\n order: Order;\n onSelect: (id: string) => void;\n}) {\n return (\n <TableRow onClick={() => onSelect(order.id)}>\n <TableCell>{order.id}</TableCell>...\n </TableRow>\n );\n});\n\nconst handleSelect = useCallback((id: string) => {\n setSelectedId(id);\n}, []);\n\nconst filtered = useMemo(\n () =>\n orders.filter((o) => o.status === status).sort((a, b) => b.total - a.total),\n [orders, status],\n);\n```\n\n### 4. ALWAYS Clean Up Side Effects\n\nClean up timers, event listeners, and subscriptions in `useEffect` return functions:\n\n```tsx\nuseEffect(() => {\n const handler = (e: KeyboardEvent) => {\n /* ... */\n };\n window.addEventListener(\"keydown\", handler);\n return () => window.removeEventListener(\"keydown\", handler);\n}, []);\n```\n\n### 5. Cancel In-Flight API Requests on Unmount\n\nWhen debouncing or fetching data, use the `cancel()` function from `useApi` to cancel in-flight requests when the component unmounts or a newer request supersedes the previous one:\n\n```tsx\nconst { run, cancel } = useApi(\"SearchProducts\");\n\nuseEffect(() => {\n if (!query) return;\n run({ query })\n .then((res) => setResults(res?.products ?? []))\n .catch(console.error);\n return () => {\n void cancel();\n };\n}, [query, run, cancel]);\n```\n";
|
|
2
2
|
//# sourceMappingURL=skill.generated.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill.generated.d.ts","sourceRoot":"","sources":["../../../../../src/ai-service/skills/system/superblocks-frontend/skill.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,
|
|
1
|
+
{"version":3,"file":"skill.generated.d.ts","sourceRoot":"","sources":["../../../../../src/ai-service/skills/system/superblocks-frontend/skill.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,66bA6dnB,CAAC"}
|