@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.
Files changed (128) hide show
  1. package/dist/ai-service/agent/prompts/api-prompts.d.ts.map +1 -1
  2. package/dist/ai-service/agent/prompts/api-prompts.js +9 -1
  3. package/dist/ai-service/agent/prompts/api-prompts.js.map +1 -1
  4. package/dist/ai-service/agent/prompts/build-base-system-prompt.d.ts.map +1 -1
  5. package/dist/ai-service/agent/prompts/build-base-system-prompt.js +14 -1
  6. package/dist/ai-service/agent/prompts/build-base-system-prompt.js.map +1 -1
  7. package/dist/ai-service/agent/tools/apis/analysis.js +4 -4
  8. package/dist/ai-service/agent/tools/apis/analysis.js.map +1 -1
  9. package/dist/ai-service/agent/tools/apis/get-api-docs.d.ts.map +1 -1
  10. package/dist/ai-service/agent/tools/apis/get-api-docs.js +3 -1
  11. package/dist/ai-service/agent/tools/apis/get-api-docs.js.map +1 -1
  12. package/dist/ai-service/agent/tools.d.ts.map +1 -1
  13. package/dist/ai-service/agent/tools.js +20 -6
  14. package/dist/ai-service/agent/tools.js.map +1 -1
  15. package/dist/ai-service/agent/tools2/access-control.d.ts.map +1 -1
  16. package/dist/ai-service/agent/tools2/access-control.js +5 -26
  17. package/dist/ai-service/agent/tools2/access-control.js.map +1 -1
  18. package/dist/ai-service/agent/tools2/tools/git-commit.d.ts +15 -0
  19. package/dist/ai-service/agent/tools2/tools/git-commit.d.ts.map +1 -0
  20. package/dist/ai-service/agent/tools2/tools/git-commit.js +105 -0
  21. package/dist/ai-service/agent/tools2/tools/git-commit.js.map +1 -0
  22. package/dist/ai-service/agent/tools2/tools/git-diff.d.ts +9 -0
  23. package/dist/ai-service/agent/tools2/tools/git-diff.d.ts.map +1 -0
  24. package/dist/ai-service/agent/tools2/tools/git-diff.js +23 -0
  25. package/dist/ai-service/agent/tools2/tools/git-diff.js.map +1 -0
  26. package/dist/ai-service/agent/tools2/tools/git-log.d.ts +12 -0
  27. package/dist/ai-service/agent/tools2/tools/git-log.d.ts.map +1 -0
  28. package/dist/ai-service/agent/tools2/tools/git-log.js +27 -0
  29. package/dist/ai-service/agent/tools2/tools/git-log.js.map +1 -0
  30. package/dist/ai-service/agent/tools2/tools/git-merge-abort.d.ts +7 -0
  31. package/dist/ai-service/agent/tools2/tools/git-merge-abort.d.ts.map +1 -0
  32. package/dist/ai-service/agent/tools2/tools/git-merge-abort.js +42 -0
  33. package/dist/ai-service/agent/tools2/tools/git-merge-abort.js.map +1 -0
  34. package/dist/ai-service/agent/tools2/tools/git-pull.d.ts +20 -0
  35. package/dist/ai-service/agent/tools2/tools/git-pull.d.ts.map +1 -0
  36. package/dist/ai-service/agent/tools2/tools/git-pull.js +80 -0
  37. package/dist/ai-service/agent/tools2/tools/git-pull.js.map +1 -0
  38. package/dist/ai-service/agent/tools2/tools/git-stage.d.ts +9 -0
  39. package/dist/ai-service/agent/tools2/tools/git-stage.d.ts.map +1 -0
  40. package/dist/ai-service/agent/tools2/tools/git-stage.js +34 -0
  41. package/dist/ai-service/agent/tools2/tools/git-stage.js.map +1 -0
  42. package/dist/ai-service/agent/tools2/tools/git-status.d.ts +9 -0
  43. package/dist/ai-service/agent/tools2/tools/git-status.d.ts.map +1 -0
  44. package/dist/ai-service/agent/tools2/tools/git-status.js +18 -0
  45. package/dist/ai-service/agent/tools2/tools/git-status.js.map +1 -0
  46. package/dist/ai-service/agent/tools2/tools/grep-metadata.d.ts +1 -1
  47. package/dist/ai-service/agent/tools2/tools/index.d.ts +7 -0
  48. package/dist/ai-service/agent/tools2/tools/index.d.ts.map +1 -1
  49. package/dist/ai-service/agent/tools2/tools/index.js +8 -0
  50. package/dist/ai-service/agent/tools2/tools/index.js.map +1 -1
  51. package/dist/ai-service/agent/tools2/types.d.ts +2 -3
  52. package/dist/ai-service/agent/tools2/types.d.ts.map +1 -1
  53. package/dist/ai-service/agent/tools2/types.js +1 -0
  54. package/dist/ai-service/agent/tools2/types.js.map +1 -1
  55. package/dist/ai-service/chat/chat-session-store.d.ts.map +1 -1
  56. package/dist/ai-service/chat/chat-session-store.js +2 -0
  57. package/dist/ai-service/chat/chat-session-store.js.map +1 -1
  58. package/dist/ai-service/features.d.ts +5 -0
  59. package/dist/ai-service/features.d.ts.map +1 -1
  60. package/dist/ai-service/features.js +5 -0
  61. package/dist/ai-service/features.js.map +1 -1
  62. package/dist/ai-service/index.d.ts +2 -1
  63. package/dist/ai-service/index.d.ts.map +1 -1
  64. package/dist/ai-service/index.js +135 -2
  65. package/dist/ai-service/index.js.map +1 -1
  66. package/dist/ai-service/prompts/summarize-api-usage.d.ts +18 -0
  67. package/dist/ai-service/prompts/summarize-api-usage.d.ts.map +1 -0
  68. package/dist/ai-service/prompts/summarize-api-usage.js +30 -0
  69. package/dist/ai-service/prompts/summarize-api-usage.js.map +1 -0
  70. package/dist/ai-service/skills/system/_registry.generated.d.ts.map +1 -1
  71. package/dist/ai-service/skills/system/_registry.generated.js +0 -1
  72. package/dist/ai-service/skills/system/_registry.generated.js.map +1 -1
  73. package/dist/ai-service/skills/system/superblocks-api/references/code-blocks.generated.d.ts +1 -1
  74. package/dist/ai-service/skills/system/superblocks-api/references/code-blocks.generated.d.ts.map +1 -1
  75. package/dist/ai-service/skills/system/superblocks-api/references/code-blocks.generated.js +51 -37
  76. package/dist/ai-service/skills/system/superblocks-api/references/code-blocks.generated.js.map +1 -1
  77. package/dist/ai-service/skills/system/superblocks-api/references/rest-apis.generated.d.ts +1 -1
  78. package/dist/ai-service/skills/system/superblocks-api/references/rest-apis.generated.d.ts.map +1 -1
  79. package/dist/ai-service/skills/system/superblocks-api/references/rest-apis.generated.js +100 -93
  80. package/dist/ai-service/skills/system/superblocks-api/references/rest-apis.generated.js.map +1 -1
  81. package/dist/ai-service/skills/system/superblocks-api/references/sql-databases.generated.d.ts +1 -1
  82. package/dist/ai-service/skills/system/superblocks-api/references/sql-databases.generated.d.ts.map +1 -1
  83. package/dist/ai-service/skills/system/superblocks-api/references/sql-databases.generated.js +72 -49
  84. package/dist/ai-service/skills/system/superblocks-api/references/sql-databases.generated.js.map +1 -1
  85. package/dist/ai-service/skills/system/superblocks-frontend/references/embedding.generated.d.ts +1 -1
  86. package/dist/ai-service/skills/system/superblocks-frontend/references/embedding.generated.d.ts.map +1 -1
  87. package/dist/ai-service/skills/system/superblocks-frontend/references/embedding.generated.js +7 -7
  88. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.d.ts +1 -1
  89. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.d.ts.map +1 -1
  90. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.js +88 -28
  91. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.js.map +1 -1
  92. package/dist/ai-service/state-machine/clark-fsm.d.ts.map +1 -1
  93. package/dist/ai-service/state-machine/clark-fsm.js +0 -2
  94. package/dist/ai-service/state-machine/clark-fsm.js.map +1 -1
  95. package/dist/ai-service/state-machine/handlers/llm-generating.d.ts.map +1 -1
  96. package/dist/ai-service/state-machine/handlers/llm-generating.js +9 -0
  97. package/dist/ai-service/state-machine/handlers/llm-generating.js.map +1 -1
  98. package/dist/file-sync-vite-plugin.d.ts.map +1 -1
  99. package/dist/file-sync-vite-plugin.js +4 -0
  100. package/dist/file-sync-vite-plugin.js.map +1 -1
  101. package/dist/git-service/errors.d.ts +18 -0
  102. package/dist/git-service/errors.d.ts.map +1 -0
  103. package/dist/git-service/errors.js +27 -0
  104. package/dist/git-service/errors.js.map +1 -0
  105. package/dist/git-service/index.d.ts +30 -0
  106. package/dist/git-service/index.d.ts.map +1 -0
  107. package/dist/git-service/index.js +135 -0
  108. package/dist/git-service/index.js.map +1 -0
  109. package/dist/git-service/live-branch.d.ts +10 -0
  110. package/dist/git-service/live-branch.d.ts.map +1 -0
  111. package/dist/git-service/live-branch.js +63 -0
  112. package/dist/git-service/live-branch.js.map +1 -0
  113. package/dist/lock-service/index.d.ts +5 -1
  114. package/dist/lock-service/index.d.ts.map +1 -1
  115. package/dist/lock-service/index.js +83 -16
  116. package/dist/lock-service/index.js.map +1 -1
  117. package/dist/plugin-options.d.ts +3 -0
  118. package/dist/plugin-options.d.ts.map +1 -1
  119. package/dist/plugin-options.js.map +1 -1
  120. package/dist/socket-manager.d.ts +4 -1
  121. package/dist/socket-manager.d.ts.map +1 -1
  122. package/dist/socket-manager.js +378 -3
  123. package/dist/socket-manager.js.map +1 -1
  124. package/dist/sync-service/index.d.ts +12 -1
  125. package/dist/sync-service/index.d.ts.map +1 -1
  126. package/dist/sync-service/index.js +37 -6
  127. package/dist/sync-service/index.js.map +1 -1
  128. 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 | 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\` |
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
- statement: "DELETE FROM logs WHERE created < '2023-01-01'"
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: ({ name, status, minAmount }: { name?: string; status?: string; minAmount?: number }) => {
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: ({ id, name, email }: { id: string; name: string; email: string }) =>
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 }) => \`DELETE FROM sensitive_data WHERE id = '\${id}'\`
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
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyVtB,CAAC"}
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"}
@@ -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 { \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";
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
@@ -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,05GAuJnB,CAAC"}
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"}
@@ -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,slbAianB,CAAC"}
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"}