@saltcorn/copilot 0.8.1 → 0.8.3

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.
@@ -23,6 +23,21 @@ const {
23
23
  getRelationPathsForPairs,
24
24
  } = require("../relation-paths");
25
25
 
26
+ const collectViewLinks = (segment, out = []) => {
27
+ if (!segment || typeof segment !== "object") return out;
28
+ if (Array.isArray(segment)) {
29
+ segment.forEach((s) => collectViewLinks(s, out));
30
+ return out;
31
+ }
32
+ if (segment.type === "view_link" && segment.view) out.push(segment);
33
+ if (segment.above) collectViewLinks(segment.above, out);
34
+ if (segment.besides) collectViewLinks(segment.besides, out);
35
+ if (segment.contents) collectViewLinks(segment.contents, out);
36
+ if (Array.isArray(segment.tabs))
37
+ segment.tabs.forEach((t) => collectViewLinks(t?.contents, out));
38
+ return out;
39
+ };
40
+
26
41
  const collectLayoutFieldNames = (segment, out = new Set()) => {
27
42
  if (!segment || typeof segment !== "object") return out;
28
43
  if (Array.isArray(segment)) {
@@ -129,7 +144,8 @@ class GenerateViewSkill {
129
144
  `(3) Write out the complete updated configuration JSON in full — every key from the existing config must be present, with only your targeted changes merged in.\n` +
130
145
  `(4) Call apply_view_config with that complete object. NEVER call apply_view_config before step (3) is finished. NEVER call it with only the name or a partial object — the configuration field is mandatory and must be the full merged result from step (3). Calling apply_view_config without a complete configuration is an error.\n\n` +
131
146
  `**Generating a new view that contains view_links or embedded views:**\n` +
132
- `Call get_relation_paths once with all source_table/target_view pairs you need before constructing the layout.\n\n` +
147
+ `If the task or prompt mentions a viewlink, a link to another view, or a button that opens another view from a list row, that view_link column is REQUIRED — do not omit it. ` +
148
+ `You MUST call get_relation_paths with all source_table/target_view pairs before constructing the layout. Never skip this step when view_links are needed.\n\n` +
133
149
  `**Embedded view segment format (for Show layouts):**\n` +
134
150
  ` { "type": "view", "view": "<viewName>", "name": "<viewName>", "relation": "<from get_relation_paths>" }\n` +
135
151
  `Do NOT use blank text segments as placeholders — always use a real view segment with a relation string from get_relation_paths.\n\n` +
@@ -161,11 +177,16 @@ class GenerateViewSkill {
161
177
  error: `View "${name}" already exists. Use get_view_config and apply_view_config to update it.`,
162
178
  };
163
179
  const tableRow = table ? Table.findOne({ name: table }) : null;
164
- const roleName = typeof min_role === "number" ? null : (min_role || "public");
180
+ const roleName =
181
+ typeof min_role === "number" ? null : min_role || "public";
165
182
  const resolvedRole =
166
183
  typeof min_role === "number"
167
184
  ? min_role
168
- : ((getState().roles || []).find((r) => r.role === roleName) || { id: 100 }).id;
185
+ : (
186
+ (getState().roles || []).find((r) => r.role === roleName) || {
187
+ id: 100,
188
+ }
189
+ ).id;
169
190
  await View.create({
170
191
  name,
171
192
  viewtemplate: viewpattern,
@@ -308,6 +329,21 @@ class GenerateViewSkill {
308
329
  });
309
330
  if (baseCfg?.columns) wfctx.columns = baseCfg.columns;
310
331
  }
332
+ if (viewpattern === "List" && wfctx.layout) {
333
+ // initial_config_all_fields never generates ViewLink columns — inject them from layout
334
+ const viewLinks = collectViewLinks(wfctx.layout);
335
+ const viewLinkColumns = viewLinks.map((seg) => ({
336
+ type: "ViewLink",
337
+ view: seg.view,
338
+ block: seg.block || false,
339
+ label: seg.view_label || "",
340
+ minRole: seg.minRole || 100,
341
+ ...(seg.relation ? { relation: seg.relation } : {}),
342
+ isFormula: seg.isFormula || {},
343
+ }));
344
+ if (viewLinkColumns.length > 0)
345
+ wfctx.columns = [...(wfctx.columns || []), ...viewLinkColumns];
346
+ }
311
347
  if (viewpattern === "Edit" && table) {
312
348
  const layoutFieldNames = collectLayoutFieldNames(wfctx.layout);
313
349
  const fields = table.fields || [];
@@ -331,7 +367,10 @@ class GenerateViewSkill {
331
367
  }
332
368
  }
333
369
  if (usersFkColumnsToAdd.length > 0)
334
- wfctx.columns = [...(wfctx.columns || []), ...usersFkColumnsToAdd];
370
+ wfctx.columns = [
371
+ ...(wfctx.columns || []),
372
+ ...usersFkColumnsToAdd,
373
+ ];
335
374
  if (Object.keys(fixed).length > 0) wfctx.fixed = fixed;
336
375
  wfctx.destination_type = "Back to referer";
337
376
  }
@@ -398,12 +437,15 @@ class GenerateViewSkill {
398
437
  for (const field of form.fields) {
399
438
  if (prefilledFields.has(field.name)) continue;
400
439
  //TODO showIf
440
+ const isShowif = field.name.endsWith("_showif");
401
441
  properties[field.name] = {
402
442
  description:
403
443
  field.copilot_description ||
404
- `${field.label}.${
405
- field.sublabel ? ` ${field.sublabel}` : ""
406
- }`,
444
+ (isShowif
445
+ ? `${field.label}. The correct default is an empty string — leave it blank to always show this element. Only provide a JavaScript expression if the task description explicitly states that this element should be conditionally hidden based on a URL state variable or the current user. Never invent field names or copy examples.`
446
+ : `${field.label}.${
447
+ field.sublabel ? ` ${field.sublabel}` : ""
448
+ }`),
407
449
  ...fieldProperties(field),
408
450
  };
409
451
  if (!properties[field.name].type) {
@@ -1,5 +1,11 @@
1
1
  const viewname = "Saltcorn AppConstructor (experimental)";
2
2
 
3
+ const TaskType = Object.freeze({
4
+ PLUGIN: "plugin",
5
+ DATA_MODEL: "data_model",
6
+ FEATURE: "feature",
7
+ });
8
+
3
9
  const tool_choice = (tool_name) => ({
4
10
  tool_choice: {
5
11
  type: "function",
@@ -9,4 +15,4 @@ const tool_choice = (tool_name) => ({
9
15
  },
10
16
  });
11
17
 
12
- module.exports = { viewname, tool_choice };
18
+ module.exports = { viewname, tool_choice, TaskType };